Merged with default branch. QtWebEngine

Mon, 28 Mar 2016 11:59:42 +0200

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Mon, 28 Mar 2016 11:59:42 +0200
branch
QtWebEngine
changeset 4895
3baaf8303a7f
parent 4886
b56735d36159 (diff)
parent 4892
64fc1deaeadb (current diff)
child 4900
32f940762654

Merged with default branch.

Documentation/Help/source.qch file | annotate | diff | comparison | revisions
Preferences/ConfigurationPages/NetworkPage.py file | annotate | diff | comparison | revisions
Preferences/__init__.py file | annotate | diff | comparison | revisions
WebBrowser/AdBlock/AdBlockIcon.py file | annotate | diff | comparison | revisions
WebBrowser/AdBlock/AdBlockTreeWidget.py file | annotate | diff | comparison | revisions
WebBrowser/CookieJar/CookieExceptionsModel.py file | annotate | diff | comparison | revisions
WebBrowser/CookieJar/CookieModel.py file | annotate | diff | comparison | revisions
WebBrowser/CookieJar/CookiesExceptionsDialog.py file | annotate | diff | comparison | revisions
WebBrowser/QtHelp/HelpDocsInstaller.py file | annotate | diff | comparison | revisions
install.py file | annotate | diff | comparison | revisions
--- a/.hgignore	Mon Mar 28 11:52:38 2016 +0200
+++ b/.hgignore	Mon Mar 28 11:59:42 2016 +0200
@@ -16,3 +16,4 @@
 glob:__pycache__
 glob:**.DS_Store
 glob:**.coverage
+glob:GPUCache
--- a/DebugClients/Python/DebugClientThreads.py	Mon Mar 28 11:52:38 2016 +0200
+++ b/DebugClients/Python/DebugClientThreads.py	Mon Mar 28 11:59:42 2016 +0200
@@ -200,4 +200,4 @@
 
 #
 # eflag: FileType = Python2
-# eflag: noqa = M601, M702
+# eflag: noqa = M601, M702, E402
--- a/DebugClients/Python3/DebugClientThreads.py	Mon Mar 28 11:52:38 2016 +0200
+++ b/DebugClients/Python3/DebugClientThreads.py	Mon Mar 28 11:59:42 2016 +0200
@@ -199,4 +199,4 @@
     debugClient.main()
 
 #
-# eflag: noqa = M702
+# eflag: noqa = M702, E402
--- a/E5Gui/E5ErrorMessage.py	Mon Mar 28 11:52:38 2016 +0200
+++ b/E5Gui/E5ErrorMessage.py	Mon Mar 28 11:59:42 2016 +0200
@@ -44,7 +44,9 @@
             "QFont::",
             "QCocoaMenu::removeMenuItem",
             "QCocoaMenu::insertNative",
-            ",type id:"
+            ",type id:",
+            "Remote debugging server started successfully",
+            "Uncaught SecurityError:",
         ]
     
     def __filterMessage(self, message):
--- a/Helpviewer/HelpBrowserWV.py	Mon Mar 28 11:52:38 2016 +0200
+++ b/Helpviewer/HelpBrowserWV.py	Mon Mar 28 11:59:42 2016 +0200
@@ -10,7 +10,7 @@
 
 from __future__ import unicode_literals
 try:
-    str = unicode
+    str = unicode       # __IGNORE_EXCEPTION__
 except NameError:
     pass
 
@@ -1617,8 +1617,8 @@
         
         searchUrl = QUrl(self.page().mainFrame().baseUrl().resolved(
             QUrl(formElement.attribute("action"))))
-        if searchUrl.scheme() != "http":
-            return
+##        if searchUrl.scheme() != "http":
+##            return
         
         if qVersion() >= "5.0.0":
             from PyQt5.QtCore import QUrlQuery
--- a/PluginManager/PluginManager.py	Mon Mar 28 11:52:38 2016 +0200
+++ b/PluginManager/PluginManager.py	Mon Mar 28 11:59:42 2016 +0200
@@ -144,7 +144,11 @@
             self.__networkManager.sslErrors.connect(self.__sslErrors)
         self.__replies = []
         
-        self.__ui.onlineStateChanged.connect(self.__onlineStateChanged)
+        try:
+            self.__ui.onlineStateChanged.connect(self.__onlineStateChanged)
+        except AttributeError:
+            # it was not called from eric
+            pass
     
     def finalizeSetup(self):
         """
--- a/Preferences/ConfigurationDialog.py	Mon Mar 28 11:52:38 2016 +0200
+++ b/Preferences/ConfigurationDialog.py	Mon Mar 28 11:59:42 2016 +0200
@@ -79,9 +79,10 @@
     HelpBrowserMode = 1
     TrayStarterMode = 2
     HexEditorMode = 3
+    WebBrowserMode = 4
     
     def __init__(self, parent=None, fromEric=True, displayMode=DefaultMode,
-                 expandedEntries=[]):
+                 expandedEntries=[], webEngine=False):
         """
         Constructor
         
@@ -89,21 +90,25 @@
         @keyparam fromEric flag indicating a dialog generation from within the
             eric6 ide (boolean)
         @keyparam displayMode mode of the configuration dialog
-            (DefaultMode, HelpBrowserMode, TrayStarterMode, HexEditorMode)
+            (DefaultMode, HelpBrowserMode, TrayStarterMode, HexEditorMode,
+             WebBrowserMode)
         @exception RuntimeError raised to indicate an invalid dialog mode
         @keyparam expandedEntries list of entries to be shown expanded
             (list of strings)
+        @keyparam webEngine flag indicating QtWebEngine is used (bool)
         """
         assert displayMode in (
             ConfigurationWidget.DefaultMode,
             ConfigurationWidget.HelpBrowserMode,
             ConfigurationWidget.TrayStarterMode,
             ConfigurationWidget.HexEditorMode,
+            ConfigurationWidget.WebBrowserMode,
         )
         
         super(ConfigurationWidget, self).__init__(parent)
         self.fromEric = fromEric
         self.displayMode = displayMode
+        self.__webEngine = webEngine
         
         self.__setupUi()
         
@@ -315,25 +320,49 @@
                 [self.tr("Viewmanager"), "preferences-viewmanager.png",
                  "ViewmanagerPage", "0interfacePage", None],
             }
-            try:
-                from PyQt5 import QtWebKit      # __IGNORE_WARNING__
+            if webEngine:
                 self.configItems.update({
-                    "helpAppearancePage":
+                    "0webBrowserPage":
+                    [self.tr("Web Browser"), "ericWeb.png",
+                     None, None, None],
+                    "webBrowserAppearancePage":
                     [self.tr("Appearance"), "preferences-styles.png",
-                     "HelpAppearancePage", "0helpPage", None],
+                     "WebBrowserAppearancePage", "0webBrowserPage", None],
+                    "webBrowserPage":
+                    [self.tr("eric6 Web Browser"), "ericWeb.png",
+                     "WebBrowserPage", "0webBrowserPage", None],
                     "helpFlashCookieManagerPage":
                     [self.tr("Flash Cookie Manager"),
                      "flashCookie16.png",
-                     "HelpFlashCookieManagerPage", "0helpPage", None],
+                     "HelpFlashCookieManagerPage", "0webBrowserPage", None],
                     "helpVirusTotalPage":
                     [self.tr("VirusTotal Interface"), "virustotal.png",
-                     "HelpVirusTotalPage", "0helpPage", None],
-                    "helpWebBrowserPage":
-                    [self.tr("eric6 Web Browser"), "ericWeb.png",
-                     "HelpWebBrowserPage", "0helpPage", None],
+                     "HelpVirusTotalPage", "0webBrowserPage", None],
                 })
-            except ImportError:
-                pass
+            else:
+                try:
+                    from PyQt5 import QtWebKit      # __IGNORE_WARNING__
+                    self.configItems.update({
+                        "0helpBrowserPage":
+                        [self.tr("Web Browser"), "ericWeb.png",
+                         None, None, None],
+                        "helpAppearancePage":
+                        [self.tr("Appearance"), "preferences-styles.png",
+                         "HelpAppearancePage", "0helpBrowserPage", None],
+                        "helpWebBrowserPage":
+                        [self.tr("eric6 Web Browser"), "ericWeb.png",
+                         "HelpWebBrowserPage", "0helpBrowserPage", None],
+                        "helpFlashCookieManagerPage":
+                        [self.tr("Flash Cookie Manager"),
+                         "flashCookie16.png",
+                         "HelpFlashCookieManagerPage", "0helpBrowserPage",
+                         None],
+                        "helpVirusTotalPage":
+                        [self.tr("VirusTotal Interface"), "virustotal.png",
+                         "HelpVirusTotalPage", "0helpBrowserPage", None],
+                    })
+                except ImportError:
+                    pass
             
             self.configItems.update(
                 e5App().getObject("PluginManager").getPluginConfigData())
@@ -359,34 +388,73 @@
                 [self.tr("Security"), "preferences-security.png",
                  "SecurityPage", None, None],
                 
-                "0helpPage":
-                [self.tr("Help"), "preferences-help.png",
-                 None, None, None],
                 "helpDocumentationPage":
                 [self.tr("Help Documentation"),
                  "preferences-helpdocumentation.png",
-                 "HelpDocumentationPage", "0helpPage", None],
+                 "HelpDocumentationPage", None, None],
             }
             try:
                 from PyQt5 import QtWebKit      # __IGNORE_WARNING__
                 self.configItems.update({
                     "helpAppearancePage":
                     [self.tr("Appearance"), "preferences-styles.png",
-                     "HelpAppearancePage", "0helpPage", None],
+                     "HelpAppearancePage", None, None],
                     "helpFlashCookieManagerPage":
                     [self.tr("Flash Cookie Manager"),
                      "flashCookie16.png",
-                     "HelpFlashCookieManagerPage", "0helpPage", None],
+                     "HelpFlashCookieManagerPage", None, None],
                     "helpVirusTotalPage":
                     [self.tr("VirusTotal Interface"), "virustotal.png",
-                     "HelpVirusTotalPage", "0helpPage", None],
+                     "HelpVirusTotalPage", None, None],
                     "helpWebBrowserPage":
                     [self.tr("eric6 Web Browser"), "ericWeb.png",
-                     "HelpWebBrowserPage", "0helpPage", None],
+                     "HelpWebBrowserPage", None, None],
                 })
             except ImportError:
                 pass
         
+        elif displayMode == ConfigurationWidget.WebBrowserMode:
+            self.configItems = {
+                # key : [display string, pixmap name, dialog module name or
+                #        page creation function, parent key,
+                #        reference to configuration page (must always be last)]
+                # The dialog module must have the module function 'create' to
+                # create the configuration page. This must have the method
+                # 'save' to save the settings.
+                "interfacePage":
+                [self.tr("Interface"), "preferences-interface.png",
+                 "HelpInterfacePage", None, None],
+                "networkPage":
+                [self.tr("Network"), "preferences-network.png",
+                 "NetworkPage", None, None],
+                "printerPage":
+                [self.tr("Printer"), "preferences-printer.png",
+                 "PrinterPage", None, None],
+                "securityPage":
+                [self.tr("Security"), "preferences-security.png",
+                 "SecurityPage", None, None],
+                
+                "helpDocumentationPage":
+                [self.tr("Help Documentation"),
+                 "preferences-helpdocumentation.png",
+                 "HelpDocumentationPage", None, None],
+                
+                "webBrowserAppearancePage":
+                [self.tr("Appearance"), "preferences-styles.png",
+                 "WebBrowserAppearancePage", None, None],
+                "webBrowserPage":
+                [self.tr("eric6 Web Browser"), "ericWeb.png",
+                 "WebBrowserPage", None, None],
+                
+                "helpFlashCookieManagerPage":
+                [self.tr("Flash Cookie Manager"),
+                 "flashCookie16.png",
+                 "HelpFlashCookieManagerPage", None, None],
+                "helpVirusTotalPage":
+                [self.tr("VirusTotal Interface"), "virustotal.png",
+                 "HelpVirusTotalPage", None, None],
+            }
+        
         elif displayMode == ConfigurationWidget.TrayStarterMode:
             self.configItems = {
                 # key : [display string, pixmap name, dialog module name or
@@ -446,7 +514,8 @@
         
         if displayMode in [ConfigurationWidget.HelpBrowserMode,
                            ConfigurationWidget.TrayStarterMode,
-                           ConfigurationWidget.HexEditorMode]:
+                           ConfigurationWidget.HexEditorMode,
+                           ConfigurationWidget.WebBrowserMode]:
             self.configListSearch.hide()
         
         if displayMode not in [ConfigurationWidget.TrayStarterMode,
@@ -837,6 +906,13 @@
         pageName = item.data(0, Qt.UserRole)
         if pageName not in self.__expandedEntries:
             self.__expandedEntries.append(pageName)
+    
+    def isUsingWebEngine(self):
+        """
+        Public method to get an indication, if QtWebEngine is being used.
+        """
+        return self.__webEngine or \
+            self.displayMode == ConfigurationWidget.WebBrowserMode
 
 
 class ConfigurationDialog(QDialog):
@@ -854,10 +930,11 @@
     HelpBrowserMode = ConfigurationWidget.HelpBrowserMode
     TrayStarterMode = ConfigurationWidget.TrayStarterMode
     HexEditorMode = ConfigurationWidget.HexEditorMode
+    WebBrowserMode = ConfigurationWidget.WebBrowserMode
     
     def __init__(self, parent=None, name=None, modal=False,
                  fromEric=True, displayMode=ConfigurationWidget.DefaultMode,
-                 expandedEntries=[]):
+                 expandedEntries=[], webEngine=False):
         """
         Constructor
         
@@ -867,9 +944,11 @@
         @keyparam fromEric flag indicating a dialog generation from within the
             eric6 ide (boolean)
         @keyparam displayMode mode of the configuration dialog
-            (DefaultMode, HelpBrowserMode, TrayStarterMode, HexEditorMode)
+            (DefaultMode, HelpBrowserMode, TrayStarterMode, HexEditorMode,
+             WebBrowserMode)
         @keyparam expandedEntries list of entries to be shown expanded
             (list of strings)
+        @keyparam webEngine flag indicating QtWebEngine is used (bool)
         """
         super(ConfigurationDialog, self).__init__(parent)
         if name:
@@ -883,7 +962,8 @@
         
         self.cw = ConfigurationWidget(self, fromEric=fromEric,
                                       displayMode=displayMode,
-                                      expandedEntries=expandedEntries)
+                                      expandedEntries=expandedEntries,
+                                      webEngine=webEngine)
         size = self.cw.size()
         self.layout.addWidget(self.cw)
         self.resize(size)
@@ -951,15 +1031,17 @@
     """
     Main window class for the standalone dialog.
     """
-    def __init__(self, parent=None):
+    def __init__(self, parent=None, webEngine=False):
         """
         Constructor
         
         @param parent reference to the parent widget (QWidget)
+        @keyparam webEngine flag indicating QtWebEngine is used (bool)
         """
         super(ConfigurationWindow, self).__init__(parent)
         
-        self.cw = ConfigurationWidget(self, fromEric=False)
+        self.cw = ConfigurationWidget(self, fromEric=False,
+                                      webEngine=webEngine)
         size = self.cw.size()
         self.setCentralWidget(self.cw)
         self.resize(size)
--- a/Preferences/ConfigurationPages/NetworkPage.py	Mon Mar 28 11:52:38 2016 +0200
+++ b/Preferences/ConfigurationPages/NetworkPage.py	Mon Mar 28 11:59:42 2016 +0200
@@ -25,14 +25,22 @@
     """
     Class implementing the Network configuration page.
     """
-    def __init__(self):
+    def __init__(self, configDialog):
         """
         Constructor
+        
+        @param configDialog reference to the configuration dialog
+            (ConfigurationDialog)
         """
         super(NetworkPage, self).__init__()
         self.setupUi(self)
         self.setObjectName("NetworkPage")
         
+        self.__configDlg = configDialog
+        self.__displayMode = None
+        self.__webEngine = False
+        self.__webKit = False
+        
         self.downloadDirPicker.setMode(E5PathPickerModes.DirectoryMode)
         
         self.ftpProxyTypeCombo.addItem(
@@ -61,14 +69,6 @@
         self.requestFilenameCheckBox.setChecked(
             Preferences.getUI("RequestDownloadFilename"))
         try:
-            policy = Preferences.getHelp("DownloadManagerRemovePolicy")
-            from Helpviewer.Download.DownloadManager import DownloadManager
-            if policy == DownloadManager.RemoveNever:
-                self.cleanupNeverButton.setChecked(True)
-            elif policy == DownloadManager.RemoveExit:
-                self.cleanupExitButton.setChecked(True)
-            else:
-                self.cleanupSuccessfulButton.setChecked(True)
         except ImportError:
             self.cleanupGroup.hide()
         
@@ -111,6 +111,48 @@
         self.exceptionsEdit.setText(
             ", ".join(Preferences.getUI("ProxyExceptions").split(",")))
     
+    def setMode(self, displayMode):
+        """
+        Public method to perform mode dependent setups.
+        
+        @param displayMode mode of the configuration dialog
+            (ConfigurationWidget.DefaultMode,
+             ConfigurationWidget.HelpBrowserMode,
+             ConfigurationWidget.WebBrowserMode)
+        """
+        from ..ConfigurationDialog import ConfigurationWidget
+        assert displayMode in (
+            ConfigurationWidget.DefaultMode,
+            ConfigurationWidget.HelpBrowserMode,
+            ConfigurationWidget.WebBrowserMode
+        )
+        
+        self.__displayMode = displayMode
+        if self.__displayMode == ConfigurationWidget.HelpBrowserMode or \
+                not self.__configDlg.isUsingWebEngine():
+            try:
+                policy = Preferences.getHelp("DownloadManagerRemovePolicy")
+                from Helpviewer.Download.DownloadManager import DownloadManager
+                if policy == DownloadManager.RemoveNever:
+                    self.cleanupNeverButton.setChecked(True)
+                elif policy == DownloadManager.RemoveExit:
+                    self.cleanupExitButton.setChecked(True)
+                else:
+                    self.cleanupSuccessfulButton.setChecked(True)
+                self.__webKit = True
+            except ImportError:
+                self.cleanupGroup.hide()
+        else:
+            policy = Preferences.getWebBrowser("DownloadManagerRemovePolicy")
+            from WebBrowser.Download.DownloadManager import DownloadManager
+            if policy == DownloadManager.RemoveNever:
+                self.cleanupNeverButton.setChecked(True)
+            elif policy == DownloadManager.RemoveExit:
+                self.cleanupExitButton.setChecked(True)
+            else:
+                self.cleanupSuccessfulButton.setChecked(True)
+            self.__webEngine = True
+    
     def save(self):
         """
         Public slot to save the Networj configuration.
@@ -121,7 +163,7 @@
         Preferences.setUI(
             "RequestDownloadFilename",
             self.requestFilenameCheckBox.isChecked())
-        try:
+        if self.__webKit:
             from Helpviewer.Download.DownloadManager import DownloadManager
             if self.cleanupNeverButton.isChecked():
                 policy = DownloadManager.RemoveNever
@@ -130,9 +172,15 @@
             else:
                 policy = DownloadManager.RemoveSuccessFullDownload
             Preferences.setHelp("DownloadManagerRemovePolicy", policy)
-        except ImportError:
-            # ignore it
-            pass
+        elif self.__webEngine:
+            from WebBrowser.Download.DownloadManager import DownloadManager
+            if self.cleanupNeverButton.isChecked():
+                policy = DownloadManager.RemoveNever
+            elif self.cleanupExitButton.isChecked():
+                policy = DownloadManager.RemoveExit
+            else:
+                policy = DownloadManager.RemoveSuccessFullDownload
+            Preferences.setWebBrowser("DownloadManagerRemovePolicy", policy)
         
         Preferences.setUI(
             "UseProxy",
@@ -222,5 +270,5 @@
     @param dlg reference to the configuration dialog
     @return reference to the instantiated page (ConfigurationPageBase)
     """
-    page = NetworkPage()
+    page = NetworkPage(dlg)
     return page
--- a/Preferences/ConfigurationPages/PrinterPage.py	Mon Mar 28 11:52:38 2016 +0200
+++ b/Preferences/ConfigurationPages/PrinterPage.py	Mon Mar 28 11:59:42 2016 +0200
@@ -52,6 +52,8 @@
             Preferences.getPrinter("TopMargin"))
         self.bottomMarginSpinBox.setValue(
             Preferences.getPrinter("BottomMargin"))
+        self.resolutionSpinBox.setValue(
+            Preferences.getPrinter("Resolution"))
         
     def save(self):
         """
@@ -84,6 +86,9 @@
         Preferences.setPrinter(
             "BottomMargin",
             self.bottomMarginSpinBox.value())
+        Preferences.setPrinter(
+            "Resolution",
+            self.resolutionSpinBox.value())
         
     @pyqtSlot()
     def on_printheaderFontButton_clicked(self):
--- a/Preferences/ConfigurationPages/PrinterPage.ui	Mon Mar 28 11:52:38 2016 +0200
+++ b/Preferences/ConfigurationPages/PrinterPage.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -1,56 +1,86 @@
-<ui version="4.0" >
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
  <class>PrinterPage</class>
- <widget class="QWidget" name="PrinterPage" >
-  <property name="geometry" >
+ <widget class="QWidget" name="PrinterPage">
+  <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>446</width>
+    <width>448</width>
     <height>568</height>
    </rect>
   </property>
-  <layout class="QVBoxLayout" >
+  <layout class="QVBoxLayout" name="verticalLayout">
    <item>
-    <widget class="QLabel" name="headerLabel" >
-     <property name="text" >
-      <string>&lt;b>Configure printer settings&lt;/b></string>
+    <widget class="QLabel" name="headerLabel">
+     <property name="text">
+      <string>&lt;b&gt;Configure printer settings&lt;/b&gt;</string>
      </property>
     </widget>
    </item>
    <item>
-    <widget class="Line" name="line7" >
-     <property name="frameShape" >
+    <widget class="Line" name="line7">
+     <property name="frameShape">
       <enum>QFrame::HLine</enum>
      </property>
-     <property name="frameShadow" >
+     <property name="frameShadow">
       <enum>QFrame::Sunken</enum>
      </property>
-     <property name="orientation" >
+     <property name="orientation">
       <enum>Qt::Horizontal</enum>
      </property>
     </widget>
    </item>
    <item>
-    <layout class="QGridLayout" >
-     <item row="1" column="1" colspan="2" >
-      <widget class="QFrame" name="frame_2" >
-       <property name="frameShape" >
+    <layout class="QGridLayout" name="gridLayout">
+     <item row="0" column="0">
+      <widget class="QLabel" name="TextLabel1">
+       <property name="text">
+        <string>Printername:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="1" colspan="3">
+      <widget class="QLineEdit" name="printerNameEdit"/>
+     </item>
+     <item row="1" column="0">
+      <widget class="QLabel" name="TextLabel2">
+       <property name="text">
+        <string>Colour Mode:</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignTop</set>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="1" colspan="3">
+      <widget class="QFrame" name="frame_2">
+       <property name="frameShape">
         <enum>QFrame::NoFrame</enum>
        </property>
-       <layout class="QVBoxLayout" >
-        <property name="margin" >
+       <layout class="QVBoxLayout">
+        <property name="leftMargin">
+         <number>0</number>
+        </property>
+        <property name="topMargin">
+         <number>0</number>
+        </property>
+        <property name="rightMargin">
+         <number>0</number>
+        </property>
+        <property name="bottomMargin">
          <number>0</number>
         </property>
         <item>
-         <widget class="QRadioButton" name="printerColorButton" >
-          <property name="text" >
+         <widget class="QRadioButton" name="printerColorButton">
+          <property name="text">
            <string>Colour</string>
           </property>
          </widget>
         </item>
         <item>
-         <widget class="QRadioButton" name="printerGrayscaleButton" >
-          <property name="text" >
+         <widget class="QRadioButton" name="printerGrayscaleButton">
+          <property name="text">
            <string>Gray Scale</string>
           </property>
          </widget>
@@ -58,28 +88,47 @@
        </layout>
       </widget>
      </item>
-     <item row="2" column="1" colspan="2" >
-      <widget class="QFrame" name="frame" >
-       <property name="frameShape" >
+     <item row="2" column="0">
+      <widget class="QLabel" name="TextLabel4">
+       <property name="text">
+        <string>Page Order:</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignTop</set>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="1" colspan="3">
+      <widget class="QFrame" name="frame">
+       <property name="frameShape">
         <enum>QFrame::NoFrame</enum>
        </property>
-       <layout class="QVBoxLayout" >
-        <property name="spacing" >
+       <layout class="QVBoxLayout">
+        <property name="spacing">
          <number>6</number>
         </property>
-        <property name="margin" >
+        <property name="leftMargin">
+         <number>0</number>
+        </property>
+        <property name="topMargin">
+         <number>0</number>
+        </property>
+        <property name="rightMargin">
+         <number>0</number>
+        </property>
+        <property name="bottomMargin">
          <number>0</number>
         </property>
         <item>
-         <widget class="QRadioButton" name="printFirstPageFirstButton" >
-          <property name="text" >
+         <widget class="QRadioButton" name="printFirstPageFirstButton">
+          <property name="text">
            <string>First Page First</string>
           </property>
          </widget>
         </item>
         <item>
-         <widget class="QRadioButton" name="printFirstPageLastButton" >
-          <property name="text" >
+         <widget class="QRadioButton" name="printFirstPageLastButton">
+          <property name="text">
            <string>Last Page First</string>
           </property>
          </widget>
@@ -87,88 +136,32 @@
        </layout>
       </widget>
      </item>
-     <item row="0" column="1" colspan="2" >
-      <widget class="QLineEdit" name="printerNameEdit" />
-     </item>
-     <item row="0" column="0" >
-      <widget class="QLabel" name="TextLabel1" >
-       <property name="text" >
-        <string>Printername:</string>
-       </property>
-      </widget>
-     </item>
-     <item row="3" column="0" >
-      <widget class="QLabel" name="TextLabel3" >
-       <property name="text" >
+     <item row="3" column="0">
+      <widget class="QLabel" name="TextLabel3">
+       <property name="text">
         <string>Magnification:</string>
        </property>
       </widget>
      </item>
-     <item row="4" column="1" colspan="2" >
-      <widget class="QLineEdit" name="printheaderFontSample" >
-       <property name="focusPolicy" >
-        <enum>Qt::NoFocus</enum>
-       </property>
-       <property name="text" >
-        <string>Header Font</string>
-       </property>
-       <property name="alignment" >
-        <set>Qt::AlignHCenter</set>
-       </property>
-       <property name="readOnly" >
-        <bool>true</bool>
-       </property>
-      </widget>
-     </item>
-     <item row="4" column="0" >
-      <widget class="QPushButton" name="printheaderFontButton" >
-       <property name="toolTip" >
-        <string>Press to select the font for the page headers</string>
-       </property>
-       <property name="text" >
-        <string>Header Font</string>
-       </property>
-      </widget>
-     </item>
-     <item row="3" column="1" >
-      <widget class="QSpinBox" name="printMagnificationSpinBox" >
-       <property name="minimum" >
+     <item row="3" column="1">
+      <widget class="QSpinBox" name="printMagnificationSpinBox">
+       <property name="minimum">
         <number>-10</number>
        </property>
-       <property name="maximum" >
+       <property name="maximum">
         <number>10</number>
        </property>
-       <property name="value" >
+       <property name="value">
         <number>-3</number>
        </property>
       </widget>
      </item>
-     <item row="1" column="0" >
-      <widget class="QLabel" name="TextLabel2" >
-       <property name="text" >
-        <string>Colour Mode:</string>
-       </property>
-       <property name="alignment" >
-        <set>Qt::AlignTop</set>
-       </property>
-      </widget>
-     </item>
-     <item row="2" column="0" >
-      <widget class="QLabel" name="TextLabel4" >
-       <property name="text" >
-        <string>Page Order:</string>
-       </property>
-       <property name="alignment" >
-        <set>Qt::AlignTop</set>
-       </property>
-      </widget>
-     </item>
-     <item row="3" column="2" >
+     <item row="3" column="2" colspan="2">
       <spacer>
-       <property name="orientation" >
+       <property name="orientation">
         <enum>Qt::Horizontal</enum>
        </property>
-       <property name="sizeHint" stdset="0" >
+       <property name="sizeHint" stdset="0">
         <size>
          <width>251</width>
          <height>20</height>
@@ -176,88 +169,153 @@
        </property>
       </spacer>
      </item>
+     <item row="4" column="0">
+      <widget class="QLabel" name="label">
+       <property name="text">
+        <string>Resolution:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="4" column="1" colspan="2">
+      <widget class="QSpinBox" name="resolutionSpinBox">
+       <property name="toolTip">
+        <string>Select the printer resolution </string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+       </property>
+       <property name="suffix">
+        <string> DPI</string>
+       </property>
+       <property name="maximum">
+        <number>6000</number>
+       </property>
+       <property name="singleStep">
+        <number>50</number>
+       </property>
+      </widget>
+     </item>
+     <item row="4" column="3">
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>208</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item row="5" column="0">
+      <widget class="QPushButton" name="printheaderFontButton">
+       <property name="toolTip">
+        <string>Press to select the font for the page headers</string>
+       </property>
+       <property name="text">
+        <string>Header Font</string>
+       </property>
+      </widget>
+     </item>
+     <item row="5" column="1" colspan="3">
+      <widget class="QLineEdit" name="printheaderFontSample">
+       <property name="focusPolicy">
+        <enum>Qt::NoFocus</enum>
+       </property>
+       <property name="text">
+        <string>Header Font</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignHCenter</set>
+       </property>
+       <property name="readOnly">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
     </layout>
    </item>
    <item>
-    <layout class="QHBoxLayout" >
+    <layout class="QHBoxLayout">
      <item>
-      <widget class="QGroupBox" name="groupBox" >
-       <property name="title" >
+      <widget class="QGroupBox" name="groupBox">
+       <property name="title">
         <string>Margins</string>
        </property>
-       <layout class="QGridLayout" >
-        <item row="0" column="1" >
-         <widget class="QDoubleSpinBox" name="topMarginSpinBox" >
-          <property name="toolTip" >
+       <layout class="QGridLayout">
+        <item row="0" column="1">
+         <widget class="QDoubleSpinBox" name="topMarginSpinBox">
+          <property name="toolTip">
            <string>Enter the top margin in cm.</string>
           </property>
-          <property name="suffix" >
+          <property name="suffix">
            <string> cm</string>
           </property>
-          <property name="decimals" >
+          <property name="decimals">
            <number>1</number>
           </property>
-          <property name="maximum" >
+          <property name="maximum">
            <double>9.900000000000000</double>
           </property>
-          <property name="singleStep" >
+          <property name="singleStep">
            <double>0.500000000000000</double>
           </property>
          </widget>
         </item>
-        <item row="1" column="0" >
-         <widget class="QDoubleSpinBox" name="leftMarginSpinBox" >
-          <property name="toolTip" >
+        <item row="1" column="0">
+         <widget class="QDoubleSpinBox" name="leftMarginSpinBox">
+          <property name="toolTip">
            <string>Enter the left margin in cm.</string>
           </property>
-          <property name="suffix" >
+          <property name="suffix">
            <string> cm</string>
           </property>
-          <property name="decimals" >
+          <property name="decimals">
            <number>1</number>
           </property>
-          <property name="maximum" >
+          <property name="maximum">
            <double>9.900000000000000</double>
           </property>
-          <property name="singleStep" >
+          <property name="singleStep">
            <double>0.500000000000000</double>
           </property>
          </widget>
         </item>
-        <item row="1" column="2" >
-         <widget class="QDoubleSpinBox" name="rightMarginSpinBox" >
-          <property name="toolTip" >
+        <item row="1" column="2">
+         <widget class="QDoubleSpinBox" name="rightMarginSpinBox">
+          <property name="toolTip">
            <string>Enter the right margin in cm.</string>
           </property>
-          <property name="suffix" >
+          <property name="suffix">
            <string> cm</string>
           </property>
-          <property name="decimals" >
+          <property name="decimals">
            <number>1</number>
           </property>
-          <property name="maximum" >
+          <property name="maximum">
            <double>9.900000000000000</double>
           </property>
-          <property name="singleStep" >
+          <property name="singleStep">
            <double>0.500000000000000</double>
           </property>
          </widget>
         </item>
-        <item row="2" column="1" >
-         <widget class="QDoubleSpinBox" name="bottomMarginSpinBox" >
-          <property name="toolTip" >
+        <item row="2" column="1">
+         <widget class="QDoubleSpinBox" name="bottomMarginSpinBox">
+          <property name="toolTip">
            <string>Enter the bottom margin in cm.</string>
           </property>
-          <property name="suffix" >
+          <property name="suffix">
            <string> cm</string>
           </property>
-          <property name="decimals" >
+          <property name="decimals">
            <number>1</number>
           </property>
-          <property name="maximum" >
+          <property name="maximum">
            <double>9.900000000000000</double>
           </property>
-          <property name="singleStep" >
+          <property name="singleStep">
            <double>0.500000000000000</double>
           </property>
          </widget>
@@ -267,10 +325,10 @@
      </item>
      <item>
       <spacer>
-       <property name="orientation" >
+       <property name="orientation">
         <enum>Qt::Horizontal</enum>
        </property>
-       <property name="sizeHint" stdset="0" >
+       <property name="sizeHint" stdset="0">
         <size>
          <width>40</width>
          <height>20</height>
@@ -282,10 +340,10 @@
    </item>
    <item>
     <spacer>
-     <property name="orientation" >
+     <property name="orientation">
       <enum>Qt::Vertical</enum>
      </property>
-     <property name="sizeHint" stdset="0" >
+     <property name="sizeHint" stdset="0">
       <size>
        <width>428</width>
        <height>61</height>
@@ -302,6 +360,7 @@
   <tabstop>printFirstPageFirstButton</tabstop>
   <tabstop>printFirstPageLastButton</tabstop>
   <tabstop>printMagnificationSpinBox</tabstop>
+  <tabstop>resolutionSpinBox</tabstop>
   <tabstop>printheaderFontButton</tabstop>
   <tabstop>topMarginSpinBox</tabstop>
   <tabstop>leftMarginSpinBox</tabstop>
--- a/Preferences/ConfigurationPages/SecurityPage.py	Mon Mar 28 11:52:38 2016 +0200
+++ b/Preferences/ConfigurationPages/SecurityPage.py	Mon Mar 28 11:59:42 2016 +0200
@@ -11,10 +11,6 @@
 
 from PyQt5.QtCore import pyqtSlot
 from PyQt5.QtWidgets import QDialog
-try:
-    from PyQt5.QtWebKit import QWebSettings
-except ImportError:
-    QWebSettings = None
 
 from .ConfigurationPageBase import ConfigurationPageBase
 from .Ui_SecurityPage import Ui_SecurityPage
@@ -38,6 +34,7 @@
         self.setObjectName("SecurityPage")
         
         self.__configDlg = configDialog
+        self.__displayMode = None
         
         # set initial values
         self.savePasswordsCheckBox.setChecked(
@@ -46,15 +43,41 @@
             Preferences.getUser("UseMasterPassword"))
         self.masterPasswordButton.setEnabled(
             Preferences.getUser("UseMasterPassword"))
-        if QWebSettings and hasattr(QWebSettings, "DnsPrefetchEnabled"):
-            self.dnsPrefetchCheckBox.setChecked(
-                Preferences.getHelp("DnsPrefetchEnabled"))
-        else:
-            self.dnsPrefetchCheckBox.setEnabled(False)
         
         self.__newPassword = ""
         self.__oldUseMasterPassword = Preferences.getUser("UseMasterPassword")
     
+    def setMode(self, displayMode):
+        """
+        Public method to perform mode dependent setups.
+        
+        @param displayMode mode of the configuration dialog
+            (ConfigurationWidget.DefaultMode,
+             ConfigurationWidget.HelpBrowserMode,
+             ConfigurationWidget.WebBrowserMode)
+        """
+        from ..ConfigurationDialog import ConfigurationWidget
+        assert displayMode in (
+            ConfigurationWidget.DefaultMode,
+            ConfigurationWidget.HelpBrowserMode,
+            ConfigurationWidget.WebBrowserMode
+        )
+        
+        self.__displayMode = displayMode
+        if self.__displayMode == ConfigurationWidget.HelpBrowserMode:
+            try:
+                from PyQt5.QtWebKit import QWebSettings
+                if QWebSettings and \
+                        hasattr(QWebSettings, "DnsPrefetchEnabled"):
+                    self.dnsPrefetchCheckBox.setChecked(
+                        Preferences.getHelp("DnsPrefetchEnabled"))
+            except ImportError:
+                self.dnsPrefetchCheckBox.setEnabled(False)
+        else:
+            if self.__configDlg.isUsingWebEngine():
+                self.dnsPrefetchCheckBox.setEnabled(False)
+                self.dnsGroup.hide()
+    
     def save(self):
         """
         Public slot to save the Help Viewers configuration.
--- a/Preferences/ConfigurationPages/SecurityPage.ui	Mon Mar 28 11:52:38 2016 +0200
+++ b/Preferences/ConfigurationPages/SecurityPage.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -91,7 +91,7 @@
     </widget>
    </item>
    <item>
-    <widget class="QGroupBox" name="groupBox_3">
+    <widget class="QGroupBox" name="dnsGroup">
      <property name="title">
       <string>DNS</string>
      </property>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Preferences/ConfigurationPages/WebBrowserAppearancePage.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,174 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2006 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the Web Browser configuration page.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtGui import QFont
+from PyQt5.QtWidgets import QFontDialog
+
+from E5Gui.E5PathPicker import E5PathPickerModes
+
+from .ConfigurationPageBase import ConfigurationPageBase
+from .Ui_WebBrowserAppearancePage import Ui_WebBrowserAppearancePage
+
+import Preferences
+
+try:
+    MonospacedFontsOption = QFontDialog.MonospacedFonts
+except AttributeError:
+    MonospacedFontsOption = QFontDialog.FontDialogOptions(0x10)
+
+
+class WebBrowserAppearancePage(ConfigurationPageBase,
+                               Ui_WebBrowserAppearancePage):
+    """
+    Class implementing the Web Browser Appearance page.
+    """
+    def __init__(self):
+        """
+        Constructor
+        """
+        super(WebBrowserAppearancePage, self).__init__()
+        self.setupUi(self)
+        self.setObjectName("WebBrowserAppearancePage")
+        
+        self.styleSheetPicker.setMode(E5PathPickerModes.OpenFileMode)
+        self.styleSheetPicker.setFilters(self.tr(
+            "Cascading Style Sheets (*.css);;All files (*)"))
+        
+        self.__displayMode = None
+        
+        # set initial values
+        defaultFontSize = Preferences.getWebBrowser("DefaultFontSize")
+        fixedFontSize = Preferences.getWebBrowser("DefaultFixedFontSize")
+        self.defaultSizeSpinBox.setValue(defaultFontSize)
+        self.fixedSizeSpinBox.setValue(fixedFontSize)
+        self.minSizeSpinBox.setValue(
+            Preferences.getWebBrowser("MinimumFontSize"))
+        self.minLogicalSizeSpinBox.setValue(
+            Preferences.getWebBrowser("MinimumLogicalFontSize"))
+        
+        self.standardFontCombo.setCurrentFont(
+            QFont(Preferences.getWebBrowser("StandardFontFamily"),
+                  defaultFontSize, QFont.Normal, False))
+        self.fixedFontCombo.setCurrentFont(
+            QFont(Preferences.getWebBrowser("FixedFontFamily"),
+                  fixedFontSize, QFont.Normal, False))
+        self.serifFontCombo.setCurrentFont(
+            QFont(Preferences.getWebBrowser("SerifFontFamily"),
+                  defaultFontSize, QFont.Normal, False))
+        self.sansSerifFontCombo.setCurrentFont(
+            QFont(Preferences.getWebBrowser("SansSerifFontFamily"),
+                  defaultFontSize, QFont.Normal, False))
+        self.cursiveFontCombo.setCurrentFont(
+            QFont(Preferences.getWebBrowser("CursiveFontFamily"),
+                  defaultFontSize, QFont.Normal, True))
+        self.fantasyFontCombo.setCurrentFont(
+            QFont(Preferences.getWebBrowser("FantasyFontFamily"),
+                  defaultFontSize, QFont.Normal, False))
+        
+        self.initColour("SaveUrlColor", self.secureURLsColourButton,
+                        Preferences.getWebBrowser)
+        
+        self.autoLoadImagesCheckBox.setChecked(
+            Preferences.getWebBrowser("AutoLoadImages"))
+        
+        self.styleSheetPicker.setText(
+            Preferences.getWebBrowser("UserStyleSheet"))
+        
+        self.tabsCloseButtonCheckBox.setChecked(
+            Preferences.getUI("SingleCloseButton"))
+        self.warnOnMultipleCloseCheckBox.setChecked(
+            Preferences.getWebBrowser("WarnOnMultipleClose"))
+    
+    def setMode(self, displayMode):
+        """
+        Public method to perform mode dependent setups.
+        
+        @param displayMode mode of the configuration dialog
+            (ConfigurationWidget.DefaultMode,
+             ConfigurationWidget.HelpBrowserMode,
+             ConfigurationWidget.TrayStarterMode)
+        """
+        from ..ConfigurationDialog import ConfigurationWidget
+        assert displayMode in (
+            ConfigurationWidget.DefaultMode,
+            ConfigurationWidget.WebBrowserMode,
+        )
+        
+        self.__displayMode = displayMode
+        if self.__displayMode != ConfigurationWidget.WebBrowserMode:
+            self.tabsGroupBox.hide()
+    
+    def save(self):
+        """
+        Public slot to save the Help Viewers configuration.
+        """
+        Preferences.setWebBrowser(
+            "StandardFontFamily",
+            self.standardFontCombo.currentFont().family())
+        Preferences.setWebBrowser(
+            "FixedFontFamily",
+            self.fixedFontCombo.currentFont().family())
+        Preferences.setWebBrowser(
+            "SerifFontFamily",
+            self.serifFontCombo.currentFont().family())
+        Preferences.setWebBrowser(
+            "SansSerifFontFamily",
+            self.sansSerifFontCombo.currentFont().family())
+        Preferences.setWebBrowser(
+            "CursiveFontFamily",
+            self.cursiveFontCombo.currentFont().family())
+        Preferences.setWebBrowser(
+            "FantasyFontFamily",
+            self.fantasyFontCombo.currentFont().family())
+        
+        Preferences.setWebBrowser(
+            "DefaultFontSize",
+            self.defaultSizeSpinBox.value())
+        Preferences.setWebBrowser(
+            "DefaultFixedFontSize",
+            self.fixedSizeSpinBox.value())
+        Preferences.setWebBrowser(
+            "MinimumFontSize",
+            self.minSizeSpinBox.value())
+        Preferences.setWebBrowser(
+            "MinimumLogicalFontSize",
+            self.minLogicalSizeSpinBox.value())
+        
+        Preferences.setWebBrowser(
+            "AutoLoadImages",
+            self.autoLoadImagesCheckBox.isChecked())
+        
+        Preferences.setWebBrowser(
+            "UserStyleSheet",
+            self.styleSheetPicker.text())
+        
+        self.saveColours(Preferences.setWebBrowser)
+        
+        from ..ConfigurationDialog import ConfigurationWidget
+        if self.__displayMode == ConfigurationWidget.WebBrowserMode:
+            Preferences.setUI(
+                "SingleCloseButton",
+                self.tabsCloseButtonCheckBox.isChecked())
+        
+        Preferences.setWebBrowser(
+            "WarnOnMultipleClose",
+            self.warnOnMultipleCloseCheckBox.isChecked())
+    
+
+def create(dlg):
+    """
+    Module function to create the configuration page.
+    
+    @param dlg reference to the configuration dialog
+    @return reference to the instantiated page (ConfigurationPageBase)
+    """
+    page = WebBrowserAppearancePage()
+    return page
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Preferences/ConfigurationPages/WebBrowserAppearancePage.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,410 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>WebBrowserAppearancePage</class>
+ <widget class="QWidget" name="WebBrowserAppearancePage">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>499</width>
+    <height>782</height>
+   </rect>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout_3">
+   <item>
+    <widget class="QLabel" name="headerLabel">
+     <property name="text">
+      <string>&lt;b&gt;Configure Web Browser appearance&lt;/b&gt;</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="Line" name="line17">
+     <property name="frameShape">
+      <enum>QFrame::HLine</enum>
+     </property>
+     <property name="frameShadow">
+      <enum>QFrame::Sunken</enum>
+     </property>
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="groupBox_4">
+     <property name="title">
+      <string>Fonts</string>
+     </property>
+     <layout class="QGridLayout" name="gridLayout_3">
+      <item row="0" column="0">
+       <widget class="QLabel" name="label_2">
+        <property name="text">
+         <string>Standard Font:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1">
+       <widget class="QFontComboBox" name="standardFontCombo">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="toolTip">
+         <string>Select the standard font</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="0">
+       <widget class="QLabel" name="label_3">
+        <property name="text">
+         <string>Fixed Width Font</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="1">
+       <widget class="QFontComboBox" name="fixedFontCombo">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="toolTip">
+         <string>Select the fixed width font</string>
+        </property>
+        <property name="fontFilters">
+         <set>QFontComboBox::MonospacedFonts</set>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="0">
+       <widget class="QLabel" name="label_4">
+        <property name="text">
+         <string>Serif Font:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="1">
+       <widget class="QFontComboBox" name="serifFontCombo">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="toolTip">
+         <string>Select the serif font</string>
+        </property>
+       </widget>
+      </item>
+      <item row="3" column="0">
+       <widget class="QLabel" name="label_5">
+        <property name="text">
+         <string>Sans Serif Font:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="3" column="1">
+       <widget class="QFontComboBox" name="sansSerifFontCombo">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="toolTip">
+         <string>Select the sans serif font</string>
+        </property>
+       </widget>
+      </item>
+      <item row="4" column="0">
+       <widget class="QLabel" name="label_6">
+        <property name="text">
+         <string>Cursive Font:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="4" column="1">
+       <widget class="QFontComboBox" name="cursiveFontCombo">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="toolTip">
+         <string>Select the cursive font</string>
+        </property>
+       </widget>
+      </item>
+      <item row="5" column="0">
+       <widget class="QLabel" name="label_7">
+        <property name="text">
+         <string>Fantasy Font:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="5" column="1">
+       <widget class="QFontComboBox" name="fantasyFontCombo">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="toolTip">
+         <string>Select the fantasy font</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="groupBox_6">
+     <property name="title">
+      <string>Font Sizes</string>
+     </property>
+     <layout class="QGridLayout" name="gridLayout_4">
+      <item row="0" column="0">
+       <widget class="QLabel" name="label_8">
+        <property name="text">
+         <string>Default Font Size:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1">
+       <widget class="QSpinBox" name="defaultSizeSpinBox">
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+        <property name="minimum">
+         <number>1</number>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="0">
+       <widget class="QLabel" name="label_9">
+        <property name="text">
+         <string>Fixed Font Size:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="1">
+       <widget class="QSpinBox" name="fixedSizeSpinBox">
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+        <property name="minimum">
+         <number>1</number>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="0">
+       <widget class="QLabel" name="label_10">
+        <property name="text">
+         <string>Minimum Font Size:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="1">
+       <widget class="QSpinBox" name="minSizeSpinBox">
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+        <property name="minimum">
+         <number>1</number>
+        </property>
+       </widget>
+      </item>
+      <item row="3" column="0">
+       <widget class="QLabel" name="label_11">
+        <property name="text">
+         <string>Minimum Logical Font Size:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="3" column="1">
+       <widget class="QSpinBox" name="minLogicalSizeSpinBox">
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+        <property name="minimum">
+         <number>1</number>
+        </property>
+       </widget>
+      </item>
+      <item row="3" column="2">
+       <spacer name="horizontalSpacer_2">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>230</width>
+          <height>20</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="groupBox_3">
+     <property name="title">
+      <string>Colours</string>
+     </property>
+     <layout class="QGridLayout" name="gridLayout_2">
+      <item row="0" column="0">
+       <widget class="QLabel" name="textLabel1_3">
+        <property name="text">
+         <string>Background colour of secure URLs:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1">
+       <widget class="QPushButton" name="secureURLsColourButton">
+        <property name="minimumSize">
+         <size>
+          <width>100</width>
+          <height>0</height>
+         </size>
+        </property>
+        <property name="toolTip">
+         <string>Select the background colour for secure URLs.</string>
+        </property>
+        <property name="text">
+         <string/>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="2">
+       <spacer name="horizontalSpacer">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>141</width>
+          <height>20</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="groupBox">
+     <property name="title">
+      <string>Images</string>
+     </property>
+     <layout class="QVBoxLayout" name="verticalLayout">
+      <item>
+       <widget class="QCheckBox" name="autoLoadImagesCheckBox">
+        <property name="toolTip">
+         <string>Select to load images</string>
+        </property>
+        <property name="text">
+         <string>Load images</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="groupBox_2">
+     <property name="title">
+      <string>Style Sheet</string>
+     </property>
+     <layout class="QHBoxLayout" name="horizontalLayout">
+      <item>
+       <widget class="QLabel" name="label">
+        <property name="text">
+         <string>User Style Sheet:</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="E5PathPicker" name="styleSheetPicker" native="true">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="focusPolicy">
+         <enum>Qt::StrongFocus</enum>
+        </property>
+        <property name="toolTip">
+         <string>Enter the file name of a user style sheet</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="tabsGroupBox">
+     <property name="title">
+      <string>Tabs</string>
+     </property>
+     <layout class="QVBoxLayout" name="verticalLayout_2">
+      <item>
+       <widget class="QCheckBox" name="tabsCloseButtonCheckBox">
+        <property name="text">
+         <string>Show only one close button instead of one for each tab</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QCheckBox" name="warnOnMultipleCloseCheckBox">
+        <property name="toolTip">
+         <string>Select to issue a warning, if multiple tabs are about to be closed</string>
+        </property>
+        <property name="text">
+         <string>Warn, if multiple tabs are about to be closed</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <spacer>
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>479</width>
+       <height>121</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>E5PathPicker</class>
+   <extends>QWidget</extends>
+   <header>E5Gui/E5PathPicker.h</header>
+   <container>1</container>
+  </customwidget>
+ </customwidgets>
+ <tabstops>
+  <tabstop>secureURLsColourButton</tabstop>
+  <tabstop>autoLoadImagesCheckBox</tabstop>
+  <tabstop>styleSheetPicker</tabstop>
+  <tabstop>tabsCloseButtonCheckBox</tabstop>
+  <tabstop>warnOnMultipleCloseCheckBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Preferences/ConfigurationPages/WebBrowserPage.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,313 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the  Web Browser configuration page.
+"""
+
+from PyQt5.QtCore import pyqtSlot, QLocale
+
+from .ConfigurationPageBase import ConfigurationPageBase
+from .Ui_WebBrowserPage import Ui_WebBrowserPage
+
+import Preferences
+import UI.PixmapCache
+
+
+class WebBrowserPage(ConfigurationPageBase, Ui_WebBrowserPage):
+    """
+    Class documentation goes here.
+    """
+    def __init__(self, configDialog):
+        """
+        Constructor
+        
+        @param configDialog reference to the configuration dialog
+            (ConfigurationDialog)
+        """
+        super(WebBrowserPage, self).__init__()
+        self.setupUi(self)
+        self.setObjectName("WebBrowserPage")
+        
+        self.__configDlg = configDialog
+        mw = configDialog.parent().parent()
+        if hasattr(mw, "helpWindow") and mw.helpWindow is not None:
+            # IDE
+            self.__browserWindow = mw.helpWindow
+        elif hasattr(mw, "currentBrowser"):
+            # Web Browser
+            self.__browserWindow = mw
+        else:
+            self.__browserWindow = None
+        self.setCurrentPageButton.setEnabled(self.__browserWindow is not None)
+        
+        defaultSchemes = ["file://", "http://", "https://"]
+        self.defaultSchemeCombo.addItems(defaultSchemes)
+        
+        # TODO: Click2Flash
+##        self.clickToFlashCheckBox.setIcon(
+##            UI.PixmapCache.getIcon("flashBlock.png"))
+        
+        # set initial values
+        self.singleHelpWindowCheckBox.setChecked(
+            Preferences.getWebBrowser("SingleWebBrowserWindow"))
+        self.saveGeometryCheckBox.setChecked(
+            Preferences.getWebBrowser("SaveGeometry"))
+        self.webSuggestionsCheckBox.setChecked(
+            Preferences.getWebBrowser("WebSearchSuggestions"))
+        self.showTabPreviews.setChecked(
+            Preferences.getWebBrowser("ShowPreview"))
+        self.errorPageCheckBox.setChecked(
+            Preferences.getWebBrowser("ErrorPageEnabled"))
+        self.scrollingCheckBox.setChecked(
+            Preferences.getWebBrowser("ScrollAnimatorEnabled"))
+        self.fullscreenCheckBox.setChecked(
+            Preferences.getWebBrowser("FullScreenSupportEnabled"))
+        
+        self.javaScriptGroup.setChecked(
+            Preferences.getWebBrowser("JavaScriptEnabled"))
+        self.jsOpenWindowsCheckBox.setChecked(
+            Preferences.getWebBrowser("JavaScriptCanOpenWindows"))
+        # TODO: Qt 5.6
+##        self.jsCloseWindowsCheckBox.setChecked(
+##            Preferences.getWebBrowser("JavaScriptCanCloseWindows"))
+        self.jsClipboardCheckBox.setChecked(
+            Preferences.getWebBrowser("JavaScriptCanAccessClipboard"))
+        self.pluginsCheckBox.setChecked(
+            Preferences.getWebBrowser("PluginsEnabled"))
+        # TODO: Click2Flash
+##        self.clickToFlashCheckBox.setChecked(
+##            Preferences.getWebBrowser("ClickToFlashEnabled"))
+        self.doNotTrackCheckBox.setChecked(
+            Preferences.getWebBrowser("DoNotTrack"))
+        self.sendRefererCheckBox.setChecked(
+            Preferences.getWebBrowser("SendReferer"))
+        
+        self.diskCacheCheckBox.setChecked(
+            Preferences.getWebBrowser("DiskCacheEnabled"))
+        self.cacheSizeSpinBox.setValue(
+            Preferences.getWebBrowser("DiskCacheSize"))
+        
+        self.startupCombo.setCurrentIndex(
+            Preferences.getWebBrowser("StartupBehavior"))
+        self.homePageEdit.setText(
+            Preferences.getWebBrowser("HomePage"))
+        
+        self.defaultSchemeCombo.setCurrentIndex(
+            self.defaultSchemeCombo.findText(
+                Preferences.getWebBrowser("DefaultScheme")))
+        
+        historyLimit = Preferences.getWebBrowser("HistoryLimit")
+        idx = 0
+        if historyLimit == 1:
+            idx = 0
+        elif historyLimit == 7:
+            idx = 1
+        elif historyLimit == 14:
+            idx = 2
+        elif historyLimit == 30:
+            idx = 3
+        elif historyLimit == 365:
+            idx = 4
+        elif historyLimit == -1:
+            idx = 5
+        elif historyLimit == -2:
+            idx = 6
+        else:
+            idx = 5
+        self.expireHistory.setCurrentIndex(idx)
+        
+        for language in range(2, QLocale.LastLanguage + 1):
+            countries = [l.country() for l in QLocale.matchingLocales(
+                language, QLocale.AnyScript, QLocale.AnyCountry)]
+            if len(countries) > 0:
+                self.languageCombo.addItem(
+                    QLocale.languageToString(language), language)
+        self.languageCombo.model().sort(0)
+        self.languageCombo.insertSeparator(0)
+        self.languageCombo.insertItem(0, QLocale.languageToString(0), 0)
+        index = self.languageCombo.findData(
+            Preferences.getWebBrowser("SearchLanguage"))
+        if index > -1:
+            self.languageCombo.setCurrentIndex(index)
+        
+        self.spatialCheckBox.setChecked(
+            Preferences.getWebBrowser("SpatialNavigationEnabled"))
+        self.linksInFocusChainCheckBox.setChecked(
+            Preferences.getWebBrowser("LinksIncludedInFocusChain"))
+        self.xssAuditingCheckBox.setChecked(
+            Preferences.getWebBrowser("XSSAuditingEnabled"))
+        
+        self.webInspectorGroup.setChecked(
+            Preferences.getWebBrowser("WebInspectorEnabled"))
+        self.webInspectorPortSpinBox.setValue(
+            Preferences.getWebBrowser("WebInspectorPort"))
+        
+        # TODO: Configuration: finish these with Qt 5.6
+        # Hide entries not yet supported
+        self.accessKeysCheckBox.hide()
+        self.jsCloseWindowsCheckBox.hide()
+    
+    def save(self):
+        """
+        Public slot to save the Help Viewers configuration.
+        """
+        Preferences.setWebBrowser(
+            "SingleWebBrowserWindow",
+            self.singleHelpWindowCheckBox.isChecked())
+        Preferences.setWebBrowser(
+            "SaveGeometry",
+            self.saveGeometryCheckBox.isChecked())
+        Preferences.setWebBrowser(
+            "WebSearchSuggestions",
+            self.webSuggestionsCheckBox.isChecked())
+        Preferences.setWebBrowser(
+            "ShowPreview",
+            self.showTabPreviews.isChecked())
+        Preferences.setWebBrowser(
+            "ErrorPageEnabled",
+            self.errorPageCheckBox.isChecked())
+        Preferences.setWebBrowser(
+            "ScrollAnimatorEnabled",
+            self.scrollingCheckBox.isChecked())
+        Preferences.setWebBrowser(
+            "FullScreenSupportEnabled",
+            self.fullscreenCheckBox.isChecked())
+        
+        Preferences.setWebBrowser(
+            "JavaScriptEnabled",
+            self.javaScriptGroup.isChecked())
+        Preferences.setWebBrowser(
+            "JavaScriptCanOpenWindows",
+            self.jsOpenWindowsCheckBox.isChecked())
+        # TODO: Qt 5.6
+##        Preferences.setWebBrowser(
+##            "JavaScriptCanCloseWindows",
+##            self.jsCloseWindowsCheckBox.isChecked())
+        Preferences.setWebBrowser(
+            "JavaScriptCanAccessClipboard",
+            self.jsClipboardCheckBox.isChecked())
+        Preferences.setWebBrowser(
+            "PluginsEnabled",
+            self.pluginsCheckBox.isChecked())
+        # TODO: Click2Flash
+##        Preferences.setWebBrowser(
+##            "ClickToFlashEnabled",
+##            self.clickToFlashCheckBox.isChecked())
+        Preferences.setWebBrowser(
+            "DoNotTrack",
+            self.doNotTrackCheckBox.isChecked())
+        Preferences.setWebBrowser(
+            "SendReferer",
+            self.sendRefererCheckBox.isChecked())
+        
+        Preferences.setWebBrowser(
+            "DiskCacheEnabled",
+            self.diskCacheCheckBox.isChecked())
+        Preferences.setWebBrowser(
+            "DiskCacheSize",
+            self.cacheSizeSpinBox.value())
+        
+        Preferences.setWebBrowser(
+            "StartupBehavior",
+            self.startupCombo.currentIndex())
+        Preferences.setWebBrowser(
+            "HomePage",
+            self.homePageEdit.text())
+        
+        Preferences.setWebBrowser(
+            "DefaultScheme",
+            self.defaultSchemeCombo.currentText())
+        
+        idx = self.expireHistory.currentIndex()
+        if idx == 0:
+            historyLimit = 1
+        elif idx == 1:
+            historyLimit = 7
+        elif idx == 2:
+            historyLimit = 14
+        elif idx == 3:
+            historyLimit = 30
+        elif idx == 4:
+            historyLimit = 365
+        elif idx == 5:
+            historyLimit = -1
+        elif idx == 6:
+            historyLimit = -2
+        Preferences.setWebBrowser("HistoryLimit", historyLimit)
+        
+        languageIndex = self.languageCombo.currentIndex()
+        if languageIndex > -1:
+            language = self.languageCombo.itemData(languageIndex)
+        else:
+            # fall back to system default
+            language = QLocale.system().language()
+        Preferences.setWebBrowser("SearchLanguage", language)
+        
+        Preferences.setWebBrowser(
+            "SpatialNavigationEnabled",
+            self.spatialCheckBox.isChecked())
+        Preferences.setWebBrowser(
+            "LinksIncludedInFocusChain",
+            self.linksInFocusChainCheckBox.isChecked())
+        Preferences.setWebBrowser(
+            "XSSAuditingEnabled",
+            self.xssAuditingCheckBox.isChecked())
+        
+        Preferences.setWebBrowser(
+            "WebInspectorEnabled",
+            self.webInspectorGroup.isChecked())
+        Preferences.setWebBrowser(
+            "WebInspectorPort",
+            self.webInspectorPortSpinBox.value())
+    
+    @pyqtSlot()
+    def on_setCurrentPageButton_clicked(self):
+        """
+        Private slot to set the current page as the home page.
+        """
+        url = self.__browserWindow.currentBrowser().url()
+        self.homePageEdit.setText(bytes(url.toEncoded()).decode())
+    
+    @pyqtSlot()
+    def on_defaultHomeButton_clicked(self):
+        """
+        Private slot to set the default home page.
+        """
+        self.homePageEdit.setText(Preferences.Prefs.helpDefaults["HomePage"])
+    
+    @pyqtSlot(int)
+    def on_startupCombo_currentIndexChanged(self, index):
+        """
+        Private slot to enable elements depending on the selected startup
+        entry.
+        
+        @param index index of the selected entry (integer)
+        """
+        enable = index == 0
+        self.homePageLabel.setEnabled(enable)
+        self.homePageEdit.setEnabled(enable)
+        self.defaultHomeButton.setEnabled(enable)
+        self.setCurrentPageButton.setEnabled(enable)
+    
+    @pyqtSlot()
+    def on_refererWhitelistButton_clicked(self):
+        """
+        Private slot to edit the referer whitelist.
+        """
+        from WebBrowser.Network.SendRefererWhitelistDialog import \
+            SendRefererWhitelistDialog
+        SendRefererWhitelistDialog(self).exec_()
+
+
+def create(dlg):
+    """
+    Module function to create the configuration page.
+    
+    @param dlg reference to the configuration dialog
+    @return reference to the instantiated page (ConfigurationPageBase)
+    """
+    page = WebBrowserPage(dlg)
+    return page
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Preferences/ConfigurationPages/WebBrowserPage.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,700 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>WebBrowserPage</class>
+ <widget class="QWidget" name="WebBrowserPage">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>616</width>
+    <height>1329</height>
+   </rect>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QLabel" name="headerLabel">
+     <property name="text">
+      <string>&lt;b&gt;Configure Web Browser&lt;/b&gt;</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="Line" name="line17">
+     <property name="frameShape">
+      <enum>QFrame::HLine</enum>
+     </property>
+     <property name="frameShadow">
+      <enum>QFrame::Sunken</enum>
+     </property>
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="groupBox_4">
+     <property name="title">
+      <string>General</string>
+     </property>
+     <layout class="QGridLayout" name="gridLayout_5">
+      <item row="0" column="0">
+       <widget class="QCheckBox" name="singleHelpWindowCheckBox">
+        <property name="toolTip">
+         <string>Select to use a single web browser window only</string>
+        </property>
+        <property name="text">
+         <string>Use single web browser window</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1">
+       <widget class="QCheckBox" name="webSuggestionsCheckBox">
+        <property name="toolTip">
+         <string>Select to enable suggestions for web searches</string>
+        </property>
+        <property name="text">
+         <string>Show suggestions for web searches</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="0">
+       <widget class="QCheckBox" name="saveGeometryCheckBox">
+        <property name="toolTip">
+         <string>Select to save the window size and position</string>
+        </property>
+        <property name="text">
+         <string>Save size and position upon exit</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="1">
+       <widget class="QCheckBox" name="showTabPreviews">
+        <property name="toolTip">
+         <string>Select to show a page preview when the mouse hovers over the tab</string>
+        </property>
+        <property name="text">
+         <string>Show preview when hovering tab</string>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="0">
+       <widget class="QCheckBox" name="errorPageCheckBox">
+        <property name="toolTip">
+         <string>Select to enable displaying the built-in Chromium error pages.</string>
+        </property>
+        <property name="text">
+         <string>Use built-in Chromium error page</string>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="1">
+       <widget class="QCheckBox" name="scrollingCheckBox">
+        <property name="toolTip">
+         <string>Select to activate animated scrolling</string>
+        </property>
+        <property name="text">
+         <string>Enable animated scrolling</string>
+        </property>
+       </widget>
+      </item>
+      <item row="3" column="0">
+       <widget class="QCheckBox" name="fullscreenCheckBox">
+        <property name="toolTip">
+         <string>Select to enable fullscreen support</string>
+        </property>
+        <property name="text">
+         <string>Enable Fullscreen Support</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+     <zorder>singleHelpWindowCheckBox</zorder>
+     <zorder>webSuggestionsCheckBox</zorder>
+     <zorder>saveGeometryCheckBox</zorder>
+     <zorder>showTabPreviews</zorder>
+     <zorder>errorPageCheckBox</zorder>
+     <zorder>scrollingCheckBox</zorder>
+     <zorder>startupGroupBox</zorder>
+     <zorder>fullscreenCheckBox</zorder>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="startupGroupBox">
+     <property name="title">
+      <string>Startup</string>
+     </property>
+     <layout class="QGridLayout" name="gridLayout_3">
+      <item row="0" column="0">
+       <widget class="QLabel" name="label">
+        <property name="text">
+         <string>On startup:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1" colspan="3">
+       <widget class="QComboBox" name="startupCombo">
+        <property name="toolTip">
+         <string>Select the startup behavior</string>
+        </property>
+        <item>
+         <property name="text">
+          <string>Show Home Page</string>
+         </property>
+        </item>
+        <item>
+         <property name="text">
+          <string>Show Speed Dial</string>
+         </property>
+        </item>
+        <item>
+         <property name="text">
+          <string>Show Empty Page</string>
+         </property>
+        </item>
+       </widget>
+      </item>
+      <item row="1" column="0">
+       <widget class="QLabel" name="homePageLabel">
+        <property name="text">
+         <string>Home Page:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="1" colspan="3">
+       <widget class="QLineEdit" name="homePageEdit">
+        <property name="toolTip">
+         <string>Enter the desired home page</string>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="1">
+       <widget class="QPushButton" name="setCurrentPageButton">
+        <property name="toolTip">
+         <string>Press to set the current page as the home page</string>
+        </property>
+        <property name="text">
+         <string>Set to current page</string>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="2">
+       <widget class="QPushButton" name="defaultHomeButton">
+        <property name="toolTip">
+         <string>Press to set the default home page</string>
+        </property>
+        <property name="text">
+         <string>Set to default home page</string>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="3">
+       <spacer name="horizontalSpacer_2">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>160</width>
+          <height>20</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="groupBox_2">
+     <property name="title">
+      <string>Scheme</string>
+     </property>
+     <layout class="QGridLayout" name="gridLayout_4">
+      <item row="0" column="0">
+       <widget class="QLabel" name="label_3">
+        <property name="text">
+         <string>Default Scheme:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1">
+       <widget class="QComboBox" name="defaultSchemeCombo">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="toolTip">
+         <string>Select the default scheme</string>
+        </property>
+        <property name="whatsThis">
+         <string>&lt;b&gt;Default Scheme&lt;/b&gt;&lt;p&gt;Select the default scheme. This scheme is prepended to URLs, that don't contain one.&lt;/p&gt;</string>
+        </property>
+        <property name="editable">
+         <bool>false</bool>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="privacyGroup">
+     <property name="title">
+      <string>Privacy</string>
+     </property>
+     <layout class="QVBoxLayout" name="verticalLayout_4">
+      <item>
+       <widget class="QGroupBox" name="javaScriptGroup">
+        <property name="toolTip">
+         <string>Select to enable JavaScript</string>
+        </property>
+        <property name="title">
+         <string>Enable JavaScript</string>
+        </property>
+        <property name="checkable">
+         <bool>true</bool>
+        </property>
+        <property name="checked">
+         <bool>false</bool>
+        </property>
+        <layout class="QGridLayout" name="gridLayout">
+         <item row="0" column="0">
+          <widget class="QCheckBox" name="jsOpenWindowsCheckBox">
+           <property name="toolTip">
+            <string>Select to allow JavaScript to open windows</string>
+           </property>
+           <property name="text">
+            <string>JavaScript can open windows</string>
+           </property>
+          </widget>
+         </item>
+         <item row="0" column="1">
+          <widget class="QCheckBox" name="jsCloseWindowsCheckBox">
+           <property name="toolTip">
+            <string>Select to allow JavaScript to close windows</string>
+           </property>
+           <property name="text">
+            <string>JavaScript can close windows</string>
+           </property>
+          </widget>
+         </item>
+         <item row="1" column="0">
+          <widget class="QCheckBox" name="jsClipboardCheckBox">
+           <property name="toolTip">
+            <string>Select to allow JavaScript to access the clipboard</string>
+           </property>
+           <property name="text">
+            <string>JavaScript can access clipboard</string>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </widget>
+      </item>
+      <item>
+       <widget class="QCheckBox" name="pluginsCheckBox">
+        <property name="toolTip">
+         <string>Select to enable plugins in web pages</string>
+        </property>
+        <property name="text">
+         <string>Enable Plug-ins</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="Line" name="line">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QCheckBox" name="doNotTrackCheckBox">
+        <property name="toolTip">
+         <string>Select to enabled the &quot;Do Not Track&quot; feature</string>
+        </property>
+        <property name="text">
+         <string>Tell web sites I do not want to be tracked</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <layout class="QHBoxLayout" name="horizontalLayout_4">
+        <item>
+         <widget class="QCheckBox" name="sendRefererCheckBox">
+          <property name="toolTip">
+           <string>Select to send referer headers to the server</string>
+          </property>
+          <property name="text">
+           <string>Send Referer header to servers</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <spacer name="horizontalSpacer_6">
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>40</width>
+            <height>20</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+        <item>
+         <widget class="QPushButton" name="refererWhitelistButton">
+          <property name="toolTip">
+           <string>Press to edit the list of whitelisted hosts</string>
+          </property>
+          <property name="text">
+           <string>Edit Referer Whitelist ...</string>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="groupBox_6">
+     <property name="title">
+      <string>Security</string>
+     </property>
+     <layout class="QVBoxLayout" name="verticalLayout_2">
+      <item>
+       <widget class="QCheckBox" name="xssAuditingCheckBox">
+        <property name="toolTip">
+         <string>Select to enable XSS auditing</string>
+        </property>
+        <property name="whatsThis">
+         <string>&lt;b&gt;Enable XSS Auditing&lt;/b&gt;
+&lt;p&gt;This selects whether load requests should be monitored for cross-site scripting attempts. Suspicious scripts will be blocked. These will be reported in the JavaScript console. Enabling this feature might have an impact on performance.&lt;/p&gt;</string>
+        </property>
+        <property name="text">
+         <string>Enable XSS Auditing</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="groupBox">
+     <property name="title">
+      <string>History</string>
+     </property>
+     <layout class="QHBoxLayout" name="horizontalLayout">
+      <item>
+       <widget class="QLabel" name="label_4">
+        <property name="text">
+         <string>Remove history items:</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QComboBox" name="expireHistory">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="toolTip">
+         <string>Select the period for expiration of history entries</string>
+        </property>
+        <item>
+         <property name="text">
+          <string>After one day</string>
+         </property>
+        </item>
+        <item>
+         <property name="text">
+          <string>After one week</string>
+         </property>
+        </item>
+        <item>
+         <property name="text">
+          <string>After two weeks</string>
+         </property>
+        </item>
+        <item>
+         <property name="text">
+          <string>After one month</string>
+         </property>
+        </item>
+        <item>
+         <property name="text">
+          <string>After one year</string>
+         </property>
+        </item>
+        <item>
+         <property name="text">
+          <string>Manually</string>
+         </property>
+        </item>
+        <item>
+         <property name="text">
+          <string>On application exit</string>
+         </property>
+        </item>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="cacheGroup">
+     <property name="title">
+      <string>Browser Cache</string>
+     </property>
+     <layout class="QGridLayout" name="gridLayout_2">
+      <item row="0" column="0" colspan="3">
+       <widget class="QCheckBox" name="diskCacheCheckBox">
+        <property name="text">
+         <string>Enable disk cache</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="0">
+       <widget class="QLabel" name="label_7">
+        <property name="text">
+         <string>Cache size:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="1">
+       <widget class="QSpinBox" name="cacheSizeSpinBox">
+        <property name="toolTip">
+         <string>Enter the maximum size of the disk cache</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+        <property name="suffix">
+         <string> MB</string>
+        </property>
+        <property name="minimum">
+         <number>1</number>
+        </property>
+        <property name="maximum">
+         <number>999</number>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="2">
+       <spacer name="horizontalSpacer">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>410</width>
+          <height>20</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="groupBox_5">
+     <property name="title">
+      <string>Web Search</string>
+     </property>
+     <layout class="QHBoxLayout" name="horizontalLayout_2">
+      <item>
+       <widget class="QLabel" name="label_5">
+        <property name="text">
+         <string>Language:</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QComboBox" name="languageCombo">
+        <property name="toolTip">
+         <string>Select the language to be used for web searches</string>
+        </property>
+        <property name="sizeAdjustPolicy">
+         <enum>QComboBox::AdjustToContents</enum>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <spacer name="horizontalSpacer_3">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>450</width>
+          <height>20</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="groupBox_3">
+     <property name="title">
+      <string>Navigation</string>
+     </property>
+     <layout class="QGridLayout" name="gridLayout_6">
+      <item row="0" column="0">
+       <widget class="QCheckBox" name="spatialCheckBox">
+        <property name="toolTip">
+         <string>Select to enable the spatial navigation feature</string>
+        </property>
+        <property name="whatsThis">
+         <string>&lt;b&gt;Enable Spatial Navigation&lt;/b&gt;
+&lt;p&gt;This enables or disables the Spatial Navigation feature, which consists in the ability to navigate between focusable elements in a Web page, such as hyperlinks and form controls, by using Left, Right, Up and Down arrow keys. For example, if a user presses the Right key, heuristics determine whether there is an element he might be trying to reach towards the right and which element he probably wants.&lt;/p&gt;</string>
+        </property>
+        <property name="text">
+         <string>Enable Spatial Navigation</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1">
+       <widget class="QCheckBox" name="linksInFocusChainCheckBox">
+        <property name="toolTip">
+         <string>Select to include links in focus chain</string>
+        </property>
+        <property name="whatsThis">
+         <string>&lt;b&gt;Include Links in Focus Chain&lt;/b&gt;
+&lt;p&gt;This selects whether hyperlinks should be included in the keyboard focus chain.&lt;/p&gt;</string>
+        </property>
+        <property name="text">
+         <string>Include Links in Focus Chain</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="0">
+       <widget class="QCheckBox" name="accessKeysCheckBox">
+        <property name="toolTip">
+         <string>Select to enable support for access keys</string>
+        </property>
+        <property name="text">
+         <string>Enable access keys</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="webInspectorGroup">
+     <property name="toolTip">
+      <string>Select to enable the Web Inspector tool</string>
+     </property>
+     <property name="title">
+      <string>Enable Web Development (Web Inspector)</string>
+     </property>
+     <property name="checkable">
+      <bool>true</bool>
+     </property>
+     <property name="checked">
+      <bool>false</bool>
+     </property>
+     <layout class="QGridLayout" name="gridLayout_8">
+      <item row="0" column="0">
+       <widget class="QLabel" name="label_2">
+        <property name="text">
+         <string>Web Inspector Port:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1">
+       <widget class="QSpinBox" name="webInspectorPortSpinBox">
+        <property name="toolTip">
+         <string>Enter the port to be used by the web inspector</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+        <property name="minimum">
+         <number>1025</number>
+        </property>
+        <property name="maximum">
+         <number>65535</number>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="2">
+       <spacer name="horizontalSpacer_4">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>372</width>
+          <height>20</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+      <item row="1" column="0" colspan="3">
+       <widget class="QLabel" name="label_6">
+        <property name="text">
+         <string>&lt;font color=&quot;red&quot;&gt;&lt;b&gt;Note:&lt;/b&gt; Web Inspector settings are activated after a restart of the application.&lt;/font&gt;</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <spacer>
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>479</width>
+       <height>121</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+  </layout>
+ </widget>
+ <tabstops>
+  <tabstop>singleHelpWindowCheckBox</tabstop>
+  <tabstop>webSuggestionsCheckBox</tabstop>
+  <tabstop>saveGeometryCheckBox</tabstop>
+  <tabstop>showTabPreviews</tabstop>
+  <tabstop>errorPageCheckBox</tabstop>
+  <tabstop>scrollingCheckBox</tabstop>
+  <tabstop>fullscreenCheckBox</tabstop>
+  <tabstop>startupCombo</tabstop>
+  <tabstop>homePageEdit</tabstop>
+  <tabstop>setCurrentPageButton</tabstop>
+  <tabstop>defaultHomeButton</tabstop>
+  <tabstop>defaultSchemeCombo</tabstop>
+  <tabstop>javaScriptGroup</tabstop>
+  <tabstop>jsOpenWindowsCheckBox</tabstop>
+  <tabstop>jsCloseWindowsCheckBox</tabstop>
+  <tabstop>jsClipboardCheckBox</tabstop>
+  <tabstop>pluginsCheckBox</tabstop>
+  <tabstop>doNotTrackCheckBox</tabstop>
+  <tabstop>sendRefererCheckBox</tabstop>
+  <tabstop>refererWhitelistButton</tabstop>
+  <tabstop>xssAuditingCheckBox</tabstop>
+  <tabstop>expireHistory</tabstop>
+  <tabstop>diskCacheCheckBox</tabstop>
+  <tabstop>cacheSizeSpinBox</tabstop>
+  <tabstop>languageCombo</tabstop>
+  <tabstop>spatialCheckBox</tabstop>
+  <tabstop>linksInFocusChainCheckBox</tabstop>
+  <tabstop>accessKeysCheckBox</tabstop>
+  <tabstop>webInspectorGroup</tabstop>
+  <tabstop>webInspectorPortSpinBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
--- a/Preferences/Shortcuts.py	Mon Mar 28 11:52:38 2016 +0200
+++ b/Preferences/Shortcuts.py	Mon Mar 28 11:59:42 2016 +0200
@@ -37,7 +37,8 @@
             act.setAlternateShortcut(QKeySequence(accel), removeEmpty=True)
 
 
-def readShortcuts(prefClass=Prefs, helpViewer=None, pluginName=None):
+def readShortcuts(prefClass=Prefs, helpViewer=None, pluginName=None,
+                  helpViewerCategory=""):
     """
     Module function to read the keyboard shortcuts for the defined QActions.
     
@@ -45,6 +46,7 @@
     @keyparam helpViewer reference to the help window object
     @keyparam pluginName name of the plugin for which to load shortcuts
         (string)
+    @keyparam helpViewerCategory name of the help viewer category (string)
     """
     if helpViewer is None and pluginName is None:
         for act in e5App().getObject("Project").getActions():
@@ -92,8 +94,10 @@
                     __readShortcut(act, category, prefClass)
     
     if helpViewer is not None:
+        if not helpViewerCategory:
+            helpViewerCategory = "HelpViewer"
         for act in helpViewer.getActions():
-            __readShortcut(act, "HelpViewer", prefClass)
+            __readShortcut(act, helpViewerCategory, prefClass)
     
     if pluginName is not None:
         try:
--- a/Preferences/__init__.py	Mon Mar 28 11:52:38 2016 +0200
+++ b/Preferences/__init__.py	Mon Mar 28 11:59:42 2016 +0200
@@ -37,6 +37,10 @@
     from PyQt5.QtWebKit import QWebSettings
 except ImportError:
     QWebSettings = None
+try:
+    from PyQt5.QtWebEngineWidgets import QWebEngineSettings
+except ImportError:
+    QWebEngineSettings = None
 from PyQt5.Qsci import QsciScintilla, QsciLexerPython
 
 from E5Gui import E5FileDialog
@@ -51,9 +55,12 @@
     ResourcesBrowserFlag, TranslationsBrowserFlag, InterfacesBrowserFlag, \
     OthersBrowserFlag, AllBrowsersFlag
 
-from Helpviewer.FlashCookieManager.FlashCookieUtilities import \
-    flashDataPathForOS
-
+try:
+    from Helpviewer.FlashCookieManager.FlashCookieUtilities import \
+        flashDataPathForOS
+except ImportError:
+    from WebBrowser.FlashCookieManager.FlashCookieUtilities import \
+        flashDataPathForOS
 
 class Prefs(object):
     """
@@ -705,6 +712,7 @@
         "RightMargin": 1.0,
         "TopMargin": 1.0,
         "BottomMargin": 1.0,
+        "Resolution": 150,      # printer resolution in DPI
     }
     
     # defaults for the project settings
@@ -835,7 +843,7 @@
         "StartupBehavior": 1,      # show speed dial
         "HomePage": "eric:home",
         "HistoryLimit": 30,
-        "DefaultScheme": "file://",
+        "DefaultScheme": "https://",
         "OfflineStorageDatabaseQuota": 50,     # 50 MB
         "UserAgent": "",
         "ShowPreview": True,
@@ -998,6 +1006,145 @@
         cls.webSettingsIntitialized = True
     
     webSettingsIntitialized = False
+    
+    # defaults for the web browser settings
+    webBrowserDefaults = {
+        "SingleWebBrowserWindow": True,
+        "SaveGeometry": True,
+        "WebBrowserState": QByteArray(),
+        "StartupBehavior": 1,      # show speed dial
+        "HomePage": "eric:home",
+        "WarnOnMultipleClose": True,
+        "DefaultScheme": "https://",
+        "UserStyleSheet": "",
+        "ZoomValuesDB": "{}",       # empty JSON dictionary
+        "HistoryLimit": 30,
+        "WebSearchSuggestions": True,
+        "WebSearchEngine": "DuckDuckGo",
+        "WebSearchKeywords": [],    # array of two tuples (keyword,
+                                    # search engine name)
+        "SearchLanguage": QLocale().language(),
+        "RssFeeds": [],
+        "ShowPreview": True,
+        "WebInspectorPort": 42024,
+        "WebInspectorEnabled": False,
+        "DiskCacheEnabled": True,
+        "DiskCacheSize": 50,        # 50 MB
+        "SslExceptionsDB": "{}",    # empty JSON dictionary
+        "DoNotTrack": False,
+        "SendReferer": True,
+        "SendRefererWhitelist": ["qt-apps.org", "kde-apps.org"],
+        "AcceptCookies": 2,         # CookieJar.AcceptOnlyFromSitesNavigatedTo
+        "KeepCookiesUntil": 0,      # CookieJar.KeepUntilExpire
+        "FilterTrackingCookies": True,
+        "SaveUrlColor": QColor(184, 248, 169),
+        # Grease Monkey
+        "GreaseMonkeyDisabledScripts": [],
+        # Downloads
+        "DownloadManagerRemovePolicy": 0,      # never delete downloads
+        "DownloadManagerSize": QSize(400, 300),
+        "DownloadManagerPosition": QPoint(),
+        "DownloadManagerDownloads": [],
+        # Sync
+        "SyncEnabled": False,
+        "SyncBookmarks": True,
+        "SyncHistory": True,
+        "SyncPasswords": False,
+        "SyncUserAgents": True,
+        "SyncSpeedDial": True,
+        "SyncEncryptData": False,
+        "SyncEncryptionKey": "",
+        "SyncEncryptionKeyLength": 32,      # 16, 24 or 32
+        "SyncEncryptPasswordsOnly": False,
+        "SyncType": 0,
+        "SyncFtpServer": "",
+        "SyncFtpUser": "",
+        "SyncFtpPassword": "",
+        "SyncFtpPath": "",
+        "SyncFtpPort": 21,
+        "SyncFtpIdleTimeout": 30,
+        "SyncDirectoryPath": "",
+        # AdBlock
+        "AdBlockEnabled": False,
+        "AdBlockSubscriptions": [],
+        "AdBlockUpdatePeriod": 1,
+        "AdBlockExceptions": [],
+        "AdBlockUseLimitedEasyList": True,
+        # Flash Cookie Manager: identical to helpDefaults
+        # PIM:                  identical to helpDefaults
+        # VirusTotal:           identical to helpDefaults
+    }
+    
+    @classmethod
+    def initWebEngineSettingsDefaults(cls):
+        """
+        Class method to initialize the web engine settings related defaults.
+        """
+        if QWebEngineSettings is None:
+            return
+        
+        webEngineSettings = QWebEngineSettings.globalSettings()
+        cls.webBrowserDefaults.update({
+            "StandardFontFamily": webEngineSettings.fontFamily(
+                QWebEngineSettings.StandardFont),
+            "FixedFontFamily": webEngineSettings.fontFamily(
+                QWebEngineSettings.FixedFont),
+            "SerifFontFamily": webEngineSettings.fontFamily(
+                QWebEngineSettings.StandardFont),
+            "SansSerifFontFamily": webEngineSettings.fontFamily(
+                QWebEngineSettings.SansSerifFont),
+            "CursiveFontFamily": webEngineSettings.fontFamily(
+                QWebEngineSettings.CursiveFont),
+            "FantasyFontFamily": webEngineSettings.fontFamily(
+                QWebEngineSettings.FantasyFont),
+            "DefaultFontSize": webEngineSettings.fontSize(
+                QWebEngineSettings.DefaultFontSize),
+            "DefaultFixedFontSize": webEngineSettings.fontSize(
+                QWebEngineSettings.DefaultFixedFontSize),
+            "MinimumFontSize": webEngineSettings.fontSize(
+                QWebEngineSettings.MinimumFontSize),
+            "MinimumLogicalFontSize": webEngineSettings.fontSize(
+                QWebEngineSettings.MinimumLogicalFontSize),
+            
+            "AutoLoadImages": webEngineSettings.testAttribute(
+                QWebEngineSettings.AutoLoadImages),
+            "JavaScriptEnabled": webEngineSettings.testAttribute(
+                QWebEngineSettings.JavascriptEnabled),
+            "JavaScriptCanOpenWindows": webEngineSettings.testAttribute(
+                QWebEngineSettings.JavascriptCanOpenWindows),
+##            "JavaScriptCanCloseWindows": webEngineSettings.testAttribute(
+##                QWebEngineSettings.JavascriptCanCloseWindows),
+            "JavaScriptCanAccessClipboard": webEngineSettings.testAttribute(
+                QWebEngineSettings.JavascriptCanAccessClipboard),
+            "PluginsEnabled": webEngineSettings.testAttribute(
+                QWebEngineSettings.PluginsEnabled),
+##            "OfflineStorageDatabaseEnabled":
+##            websettings.testAttribute(
+##                QWebSettings.OfflineStorageDatabaseEnabled),
+            "LocalStorageEnabled": webEngineSettings.testAttribute(
+                QWebEngineSettings.LocalStorageEnabled),
+            "DefaultTextEncoding": webEngineSettings.defaultTextEncoding(),
+            "SpatialNavigationEnabled": webEngineSettings.testAttribute(
+                QWebEngineSettings.SpatialNavigationEnabled),
+            "LinksIncludedInFocusChain": webEngineSettings.testAttribute(
+                QWebEngineSettings.LinksIncludedInFocusChain),
+            "LocalContentCanAccessRemoteUrls": webEngineSettings.testAttribute(
+                QWebEngineSettings.LocalContentCanAccessRemoteUrls),
+            "LocalContentCanAccessFileUrls": webEngineSettings.testAttribute(
+                QWebEngineSettings.LocalContentCanAccessFileUrls),
+            "XSSAuditingEnabled": webEngineSettings.testAttribute(
+                QWebEngineSettings.XSSAuditingEnabled),
+            "ScrollAnimatorEnabled": webEngineSettings.testAttribute(
+                QWebEngineSettings.ScrollAnimatorEnabled),
+            "ErrorPageEnabled": webEngineSettings.testAttribute(
+                QWebEngineSettings.ErrorPageEnabled),
+            "FullScreenSupportEnabled": webEngineSettings.testAttribute(
+                QWebEngineSettings.FullScreenSupportEnabled),
+        })
+        
+        cls.webEngineSettingsIntitialized = True
+    
+    webEngineSettingsIntitialized = False
 
     # defaults for system settings
     sysDefaults = {
@@ -1128,10 +1275,12 @@
     geometryDefaults = {
         "HelpViewerGeometry": QByteArray(),
         "HelpInspectorGeometry": QByteArray(),
+        "WebBrowserGeometry": QByteArray(),
         "IconEditorGeometry": QByteArray(),
         "HexEditorGeometry": QByteArray(),
         "MainGeometry": QByteArray(),
         "MainMaximized": False,
+        "WebInspectorGeometry": QByteArray(),
     }
 
     # if true, revert layouts to factory defaults
@@ -2141,7 +2290,7 @@
     if key in ["ColorMode", "FirstPageFirst"]:
         return toBool(prefClass.settings.value(
             "Printer/" + key, prefClass.printerDefaults[key]))
-    elif key in ["Magnification", "Orientation", "PageSize"]:
+    elif key in ["Magnification", "Orientation", "PageSize", "Resolution"]:
         return int(prefClass.settings.value(
             "Printer/" + key, prefClass.printerDefaults[key]))
     elif key in ["LeftMargin", "RightMargin", "TopMargin", "BottomMargin"]:
@@ -2543,6 +2692,208 @@
             "Help/" + key, pwConvert(value, encode=True))
     else:
         prefClass.settings.setValue("Help/" + key, value)
+
+
+def getWebBrowser(key, prefClass=Prefs):
+    """
+    Module function to retrieve the various web browser settings.
+    
+    @param key the key of the value to get
+    @param prefClass preferences class used as the storage area
+    @return the requested help setting
+    """
+    # the following entries are identical to the ones of the help viewer
+    # and are being redirected there
+    if key.startswith(("FlashCookie", "Pim", "VirusTotal")):
+        return getHelp(key, prefClass)
+    
+    # Web inspector stuff must come before initializing web engine settings
+    # because that starts the chromium web process
+    if key == "WebInspectorPort":
+        return int(prefClass.settings.value(
+            "WebBrowser/" + key, prefClass.webBrowserDefaults[key]))
+    elif key == "WebInspectorEnabled":
+        return toBool(prefClass.settings.value(
+            "WebBrowser/" + key, prefClass.webBrowserDefaults[key]))
+    
+    if not prefClass.webEngineSettingsIntitialized:
+        prefClass.initWebEngineSettingsDefaults()
+    
+    if key in ["StandardFont", "FixedFont"]:
+        f = QFont()
+        f.fromString(prefClass.settings.value(
+            "WebBrowser/" + key, prefClass.webBrowserDefaults[key]))
+        return f
+    elif key in ["SaveUrlColor"]:
+        col = prefClass.settings.value("WebBrowser/" + key)
+        if col is not None:
+            return QColor(col)
+        else:
+            return prefClass.webBrowserDefaults[key]
+    elif key in ["WebSearchKeywords"]:
+        # return a list of tuples of (keyword, engine name)
+        keywords = []
+        size = prefClass.settings.beginReadArray("WebBrowser/" + key)
+        for index in range(size):
+            prefClass.settings.setArrayIndex(index)
+            keyword = prefClass.settings.value("Keyword")
+            engineName = prefClass.settings.value("Engine")
+            keywords.append((keyword, engineName))
+        prefClass.settings.endArray()
+        return keywords
+    elif key in ["DownloadManagerDownloads"]:
+        # return a list of tuples of (URL, save location, done flag, page url)
+        downloads = []
+        length = prefClass.settings.beginReadArray("WebBrowser/" + key)
+        for index in range(length):
+            prefClass.settings.setArrayIndex(index)
+            url = prefClass.settings.value("URL")
+            location = prefClass.settings.value("Location")
+            done = toBool(prefClass.settings.value("Done"))
+            pageUrl = prefClass.settings.value("PageURL")
+            if pageUrl is None:
+                pageUrl = QUrl()
+            downloads.append((url, location, done, pageUrl))
+        prefClass.settings.endArray()
+        return downloads
+    elif key == "RssFeeds":
+        # return a list of tuples of (URL, title, icon)
+        feeds = []
+        length = prefClass.settings.beginReadArray("WebBrowser/" + key)
+        for index in range(length):
+            prefClass.settings.setArrayIndex(index)
+            url = prefClass.settings.value("URL")
+            title = prefClass.settings.value("Title")
+            icon = prefClass.settings.value("Icon")
+            feeds.append((url, title, icon))
+        prefClass.settings.endArray()
+        return feeds
+    elif key in ["SyncFtpPassword", "SyncEncryptionKey"]:
+        from Utilities.crypto import pwConvert
+        return pwConvert(prefClass.settings.value(
+            "WebBrowser/" + key, prefClass.helpDefaults[key]), encode=False)
+    # TODO: QtHelp
+##    elif key == "HelpViewerType":
+##        # special treatment to adjust for missing QtWebKit
+##        value = int(prefClass.settings.value(
+##            "WebBrowser/" + key, prefClass.helpDefaults[key]))
+##        if QWebSettings is None:
+##            value = prefClass.helpDefaults[key]
+##        return value
+##    elif key in ["StartupBehavior",
+##                 "OfflineStorageDatabaseQuota",
+##                 "OfflineWebApplicationCacheQuota", "CachePolicy",
+##                  ]:
+    elif key in ["StartupBehavior", "HistoryLimit",
+                 "DownloadManagerRemovePolicy","SyncType", "SyncFtpPort",
+                 "SyncFtpIdleTimeout", "SyncEncryptionKeyLength",
+                 "SearchLanguage", "WebInspectorPort",
+                 "DefaultFontSize", "DefaultFixedFontSize",
+                 "MinimumFontSize", "MinimumLogicalFontSize",
+                 "DiskCacheSize", "AcceptCookies", "KeepCookiesUntil",
+                 "AdBlockUpdatePeriod",
+                 ]:
+        return int(prefClass.settings.value(
+            "WebBrowser/" + key, prefClass.webBrowserDefaults[key]))
+##    elif key in ["PrintBackgrounds",
+##                 "JavaEnabled",
+##                 "JavaScriptCanCloseWindows",
+##                 "DnsPrefetchEnabled",
+##                 "OfflineStorageDatabaseEnabled",
+##                 "OfflineWebApplicationCacheEnabled", "LocalStorageEnabled",
+##                 "AccessKeysEnabled",
+##                 "SiteSpecificQuirksEnabled",
+##                 "ClickToFlashEnabled",
+##                 ]:
+    elif key in ["SingleWebBrowserWindow", "SaveGeometry", "JavaScriptEnabled",
+                 "JavaScriptCanOpenWindows", "JavaScriptCanAccessClipboard",
+                 "AutoLoadImages", "LocalStorageEnabled",
+                 "SpatialNavigationEnabled", "LinksIncludedInFocusChain",
+                 "LocalContentCanAccessRemoteUrls",
+                 "LocalContentCanAccessFileUrls", "XSSAuditingEnabled",
+                 "ScrollAnimatorEnabled", "ErrorPageEnabled",
+                 "WarnOnMultipleClose", "WebSearchSuggestions",
+                 "SyncEnabled", "SyncBookmarks", "SyncHistory",
+                 "SyncPasswords", "SyncUserAgents", "SyncSpeedDial",
+                 "SyncEncryptData", "SyncEncryptPasswordsOnly",
+                 "ShowPreview", "WebInspectorEnabled", "DiskCacheEnabled",
+                 "DoNotTrack", "SendReferer", "FilterTrackingCookies",
+                 "AdBlockEnabled", "AdBlockUseLimitedEasyList",
+                 "PluginsEnabled", "FullScreenSupportEnabled",
+                 ]:
+        return toBool(prefClass.settings.value(
+            "WebBrowser/" + key, prefClass.webBrowserDefaults[key]))
+##    elif key in ["ClickToFlashWhitelist",
+##                 "NoCacheHosts",
+    elif key in ["GreaseMonkeyDisabledScripts", "SendRefererWhitelist",
+                 "AdBlockSubscriptions", "AdBlockExceptions",
+                 ]:
+        return toList(prefClass.settings.value(
+            "WebBrowser/" + key, prefClass.helpDefaults[key]))
+    else:
+        return prefClass.settings.value("WebBrowser/" + key,
+                                        prefClass.webBrowserDefaults[key])
+    
+
+def setWebBrowser(key, value, prefClass=Prefs):
+    """
+    Module function to store the various web browser settings.
+    
+    @param key the key of the setting to be set
+    @param value the value to be set
+    @param prefClass preferences class used as the storage area
+    """
+    # the following entries are identical to the ones of the help viewer
+    # and are being redirected there
+    if key.startswith(("FlashCookie", "Pim", "VirusTotal")):
+        setHelp(key, value, prefClass)
+    
+    if key in ["StandardFont", "FixedFont"]:
+        prefClass.settings.setValue("WebBrowser/" + key, value.toString())
+    elif key == "SaveUrlColor":
+        prefClass.settings.setValue("WebBrowser/" + key, value.name())
+    elif key == "WebSearchKeywords":
+        # value is list of tuples of (keyword, engine name)
+        prefClass.settings.remove("WebBrowser/" + key)
+        prefClass.settings.beginWriteArray("WebBrowser/" + key, len(value))
+        index = 0
+        for v in value:
+            prefClass.settings.setArrayIndex(index)
+            prefClass.settings.setValue("Keyword", v[0])
+            prefClass.settings.setValue("Engine", v[1])
+            index += 1
+        prefClass.settings.endArray()
+    elif key == "DownloadManagerDownloads":
+        # value is list of tuples of (URL, save location, done flag, page url)
+        prefClass.settings.remove("Help/" + key)
+        prefClass.settings.beginWriteArray("WebBrowser/" + key, len(value))
+        index = 0
+        for v in value:
+            prefClass.settings.setArrayIndex(index)
+            prefClass.settings.setValue("URL", v[0])
+            prefClass.settings.setValue("Location", v[1])
+            prefClass.settings.setValue("Done", v[2])
+            prefClass.settings.setValue("PageURL", v[3])
+            index += 1
+        prefClass.settings.endArray()
+    elif key == "RssFeeds":
+        # value is list of tuples of (URL, title, icon)
+        prefClass.settings.remove("WebBrowser/" + key)
+        prefClass.settings.beginWriteArray("WebBrowser/" + key, len(value))
+        index = 0
+        for v in value:
+            prefClass.settings.setArrayIndex(index)
+            prefClass.settings.setValue("URL", v[0])
+            prefClass.settings.setValue("Title", v[1])
+            prefClass.settings.setValue("Icon", v[2])
+            index += 1
+        prefClass.settings.endArray()
+    elif key in ["SyncFtpPassword", "SyncEncryptionKey"]:
+        from Utilities.crypto import pwConvert
+        prefClass.settings.setValue(
+            "WebBrowser/" + key, pwConvert(value, encode=True))
+    else:
+        prefClass.settings.setValue("WebBrowser/" + key, value)
     
 
 def getSystem(key, prefClass=Prefs):
@@ -3198,6 +3549,16 @@
                 newPassword
             )
         )
+    for key in ["SyncFtpPassword", "SyncEncryptionKey"]:
+        prefClass.settings.setValue(
+            "WebBrowser/" + key,
+            pwRecode(
+                prefClass.settings.value("WebBrowser/" + key,
+                                         prefClass.webBrowserDefaults[key]),
+                oldPassword,
+                newPassword
+            )
+        )
 
 
 initPreferences()
--- a/UI/UserInterface.py	Mon Mar 28 11:52:38 2016 +0200
+++ b/UI/UserInterface.py	Mon Mar 28 11:59:42 2016 +0200
@@ -9,7 +9,7 @@
 
 from __future__ import unicode_literals
 try:
-    str = unicode
+    str = unicode       # __IGNORE_EXCEPTION__
 except NameError:
     pass
 
@@ -32,6 +32,14 @@
     WEBKIT_AVAILABLE = True
 except ImportError:
     WEBKIT_AVAILABLE = False
+if qVersion() < "5.6.0":
+    WEBENGINE_AVAILABLE = False
+else:
+    try:
+        from PyQt5 import QtWebEngineWidgets    # __IGNORE_WARNING__
+        WEBENGINE_AVAILABLE = True
+    except ImportError:
+        WEBENGINE_AVAILABLE = False
 
 from .Info import Version, BugAddress, Program, FeatureAddress
 from . import Config
@@ -468,10 +476,16 @@
         self.__initExternalToolsActions()
         
         # create a dummy help window for shortcuts handling
-        if WEBKIT_AVAILABLE:
+        if WEBENGINE_AVAILABLE:
+            from WebBrowser.WebBrowserWindow import WebBrowserWindow
+            self.dummyHelpViewer = \
+                WebBrowserWindow(None, '.', None, 'web_browser', True, True)
+        elif WEBKIT_AVAILABLE:
             from Helpviewer.HelpWindow import HelpWindow
             self.dummyHelpViewer = \
                 HelpWindow(None, '.', None, 'help viewer', True, True)
+        else:
+            self.dummyHelpViewer = None
         
         # register all relevant objects
         splash.showMessage(self.tr("Registering Objects..."))
@@ -486,7 +500,7 @@
         e5App().registerObject("TaskViewer", self.taskViewer)
         e5App().registerObject("TemplateViewer", self.templateViewer)
         e5App().registerObject("Shell", self.shell)
-        if WEBKIT_AVAILABLE:
+        if self.dummyHelpViewer is not None:
             e5App().registerObject("DummyHelpViewer", self.dummyHelpViewer)
         e5App().registerObject("PluginManager", self.pluginManager)
         e5App().registerObject("ToolbarManager", self.toolbarManager)
@@ -1596,7 +1610,7 @@
         self.whatsThisAct.triggered.connect(self.__whatsThis)
         self.actions.append(self.whatsThisAct)
 
-        if WEBKIT_AVAILABLE:
+        if WEBENGINE_AVAILABLE or WEBKIT_AVAILABLE:
             self.helpviewerAct = E5Action(
                 self.tr('Helpviewer'),
                 UI.PixmapCache.getIcon("help.png"),
@@ -1925,7 +1939,7 @@
         self.hexEditorAct.triggered.connect(self.__openHexEditor)
         self.actions.append(self.hexEditorAct)
 
-        if WEBKIT_AVAILABLE:
+        if WEBENGINE_AVAILABLE or WEBKIT_AVAILABLE:
             self.webBrowserAct = E5Action(
                 self.tr('eric6 Web Browser'),
                 UI.PixmapCache.getIcon("ericWeb.png"),
@@ -3002,12 +3016,16 @@
             .format(sip_version_str)
         versionText += """<tr><td><b>QScintilla</b></td><td>{0}</td></tr>"""\
             .format(QSCINTILLA_VERSION_STR)
-        try:
+        if WEBENGINE_AVAILABLE:
+            from WebBrowser.Tools import WebBrowserTools
+            chromeVersion = WebBrowserTools.getWebEngineVersions()[0]
+            versionText += \
+                """<tr><td><b>WebEngine</b></td><td>{0}</td></tr>"""\
+                .format(chromeVersion)
+        if WEBKIT_AVAILABLE:
             from PyQt5.QtWebKit import qWebKitVersion
             versionText += """<tr><td><b>WebKit</b></td><td>{0}</td></tr>"""\
                 .format(qWebKitVersion())
-        except ImportError:
-            pass
         versionText += """<tr><td><b>{0}</b></td><td>{1}</td></tr>"""\
             .format(Program, Version)
         versionText += self.tr("""</table>""")
@@ -5213,12 +5231,22 @@
             if not homeUrl.scheme():
                 home = QUrl.fromLocalFile(home).toString()
         
-        if WEBKIT_AVAILABLE:
-            if not (useSingle or Preferences.getHelp("SingleHelpWindow")) or \
-               self.helpWindow is None:
-                from Helpviewer.HelpWindow import HelpWindow
-                help = HelpWindow(home, '.', None, 'help viewer', True,
-                                  searchWord=searchWord)
+        if WEBENGINE_AVAILABLE or WEBKIT_AVAILABLE:
+            single = useSingle
+            if WEBENGINE_AVAILABLE:
+                single = single or \
+                    Preferences.getWebBrowser("SingleWebBrowserWindow")
+            elif WEBKIT_AVAILABLE:
+                single = single or Preferences.getHelp("SingleHelpWindow")
+            if not single or self.helpWindow is None:
+                if WEBENGINE_AVAILABLE:
+                    from WebBrowser.WebBrowserWindow import WebBrowserWindow
+                    help = WebBrowserWindow(home, '.', None, 'web_browser',
+                                            True, searchWord=searchWord)
+                elif WEBKIT_AVAILABLE:
+                    from Helpviewer.HelpWindow import HelpWindow
+                    help = HelpWindow(home, '.', None, 'help viewer', True,
+                                      searchWord=searchWord)
 
                 if QApplication.desktop().width() > 400 and \
                    QApplication.desktop().height() > 500:
@@ -5226,9 +5254,13 @@
                 else:
                     help.showMaximized()
                 
-                if useSingle or Preferences.getHelp("SingleHelpWindow"):
+                if single:
                     self.helpWindow = help
-                    self.helpWindow.helpClosed.connect(self.__helpClosed)
+                    try:
+                        self.helpWindow.webBrowserClosed.connect(
+                            self.__helpClosed)
+                    except AttributeError:
+                        self.helpWindow.helpClosed.connect(self.__helpClosed)
                     self.preferencesChanged.connect(
                         self.helpWindow.preferencesChanged)
                     self.masterPasswordChanged.connect(
@@ -5246,7 +5278,11 @@
         """
         Private slot to handle the helpClosed signal of the help window.
         """
-        if Preferences.getHelp("SingleHelpWindow"):
+        if WEBENGINE_AVAILABLE:
+            single = Preferences.getWebBrowser("SingleWebBrowserWindow")
+        elif WEBKIT_AVAILABLE:
+            single = Preferences.getHelp("SingleHelpWindow")
+        if single:
             self.preferencesChanged.disconnect(
                 self.helpWindow.preferencesChanged)
             self.masterPasswordChanged.disconnect(
@@ -5284,7 +5320,7 @@
             (boolean)
         @return reference to the help window instance (HelpWindow)
         """
-        if WEBKIT_AVAILABLE:
+        if WEBENGINE_AVAILABLE or WEBKIT_AVAILABLE:
             if self.helpWindow is None:
                 self.launchHelpViewer("", useSingle=True)
             self.helpWindow.raise_()
@@ -5304,6 +5340,7 @@
         dlg = ConfigurationDialog(
             self, 'Configuration',
             expandedEntries=self.__expandedConfigurationEntries,
+            webEngine=WEBENGINE_AVAILABLE,
         )
         dlg.preferencesChanged.connect(self.__preferencesChanged)
         dlg.masterPasswordChanged.connect(self.__masterPasswordChanged)
--- a/Utilities/__init__.py	Mon Mar 28 11:52:38 2016 +0200
+++ b/Utilities/__init__.py	Mon Mar 28 11:59:42 2016 +0200
@@ -1682,6 +1682,14 @@
         qVersion(), linesep, PYQT_VERSION_STR, linesep)
     info += "  sip {0}{1}  QScintilla {2}{3}".format(
         sip_version_str, linesep, QSCINTILLA_VERSION_STR, linesep)
+    if qVersion() >= "5.6.0":
+        try:
+            from PyQt5 import QtWebEngineWidgets    # __IGNORE_WARNING__
+            from WebBrowser.Tools import WebBrowserTools
+            chromeVersion = WebBrowserTools.getWebEngineVersions()[0]
+            info += "  WebEngine {0}{1}".format(chromeVersion, linesep)
+        except ImportError:
+            pass
     try:
         from PyQt5.QtWebKit import qWebKitVersion
         info += "  WebKit {0}{1}".format(qWebKitVersion(), linesep)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/AdBlock/AdBlockDialog.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,346 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the AdBlock configuration dialog.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSlot, Qt, QTimer, QCoreApplication
+from PyQt5.QtWidgets import QDialog, QMenu, QToolButton
+
+from E5Gui import E5MessageBox
+
+from .Ui_AdBlockDialog import Ui_AdBlockDialog
+
+import UI.PixmapCache
+import Preferences
+
+
+class AdBlockDialog(QDialog, Ui_AdBlockDialog):
+    """
+    Class implementing the AdBlock configuration dialog.
+    """
+    def __init__(self, manager, parent=None):
+        """
+        Constructor
+        
+        @param manager reference to the AdBlock manager (AdBlockManager)
+        @param parent reference to the parent object (QWidget)
+        """
+        super(AdBlockDialog, self).__init__(parent)
+        self.setupUi(self)
+        self.setWindowFlags(Qt.Window)
+        
+        self.__manager = manager
+        
+        self.iconLabel.setPixmap(UI.PixmapCache.getPixmap("adBlockPlus48.png"))
+        
+        self.updateSpinBox.setValue(
+            Preferences.getWebBrowser("AdBlockUpdatePeriod"))
+        
+        self.useLimitedEasyListCheckBox.setChecked(Preferences.getWebBrowser(
+            "AdBlockUseLimitedEasyList"))
+        
+        self.searchEdit.setInactiveText(self.tr("Search..."))
+        
+        self.adBlockGroup.setChecked(self.__manager.isEnabled())
+        self.__manager.requiredSubscriptionLoaded.connect(self.addSubscription)
+        
+        self.__currentTreeWidget = None
+        self.__currentSubscription = None
+        self.__loaded = False
+        
+        menu = QMenu(self)
+        menu.aboutToShow.connect(self.__aboutToShowActionMenu)
+        self.actionButton.setMenu(menu)
+        self.actionButton.setIcon(UI.PixmapCache.getIcon("adBlockAction.png"))
+        self.actionButton.setPopupMode(QToolButton.InstantPopup)
+        
+        self.__load()
+        
+        self.buttonBox.setFocus()
+    
+    def __loadSubscriptions(self):
+        """
+        Private slot to load the AdBlock subscription rules.
+        """
+        for index in range(self.subscriptionsTabWidget.count()):
+            tree = self.subscriptionsTabWidget.widget(index)
+            tree.refresh()
+    
+    def __load(self):
+        """
+        Private slot to populate the tab widget with subscriptions.
+        """
+        if self.__loaded or not self.adBlockGroup.isChecked():
+            return
+        
+        from .AdBlockTreeWidget import AdBlockTreeWidget
+        for subscription in self.__manager.subscriptions():
+            tree = AdBlockTreeWidget(subscription, self.subscriptionsTabWidget)
+            if subscription.isEnabled():
+                icon = UI.PixmapCache.getIcon("adBlockPlus.png")
+            else:
+                icon = UI.PixmapCache.getIcon("adBlockPlusDisabled.png")
+            self.subscriptionsTabWidget.addTab(
+                tree, icon, subscription.title())
+        
+        self.__loaded = True
+        QCoreApplication.processEvents()
+        
+        QTimer.singleShot(50, self.__loadSubscriptions)
+    
+    def addSubscription(self, subscription, refresh=True):
+        """
+        Public slot adding a subscription to the list.
+        
+        @param subscription reference to the subscription to be
+            added (AdBlockSubscription)
+        @param refresh flag indicating to refresh the tree (boolean)
+        """
+        from .AdBlockTreeWidget import AdBlockTreeWidget
+        tree = AdBlockTreeWidget(subscription, self.subscriptionsTabWidget)
+        index = self.subscriptionsTabWidget.insertTab(
+            self.subscriptionsTabWidget.count() - 1, tree,
+            subscription.title())
+        self.subscriptionsTabWidget.setCurrentIndex(index)
+        QCoreApplication.processEvents()
+        if refresh:
+            tree.refresh()
+        self.__setSubscriptionEnabled(subscription, True)
+    
+    def __aboutToShowActionMenu(self):
+        """
+        Private slot to show the actions menu.
+        """
+        subscriptionEditable = self.__currentSubscription and \
+            self.__currentSubscription.canEditRules()
+        subscriptionRemovable = self.__currentSubscription and \
+            self.__currentSubscription.canBeRemoved()
+        subscriptionEnabled = self.__currentSubscription and \
+            self.__currentSubscription.isEnabled()
+        
+        menu = self.actionButton.menu()
+        menu.clear()
+        
+        menu.addAction(self.tr("Add Rule"), self.__addCustomRule)\
+            .setEnabled(subscriptionEditable)
+        menu.addAction(self.tr("Remove Rule"), self.__removeCustomRule)\
+            .setEnabled(subscriptionEditable)
+        menu.addSeparator()
+        menu.addAction(
+            self.tr("Browse Subscriptions..."), self.__browseSubscriptions)
+        menu.addAction(
+            self.tr("Remove Subscription"), self.__removeSubscription)\
+            .setEnabled(subscriptionRemovable)
+        if self.__currentSubscription:
+            menu.addSeparator()
+            if subscriptionEnabled:
+                txt = self.tr("Disable Subscription")
+            else:
+                txt = self.tr("Enable Subscription")
+            menu.addAction(txt, self.__switchSubscriptionEnabled)
+        menu.addSeparator()
+        menu.addAction(
+            self.tr("Update Subscription"), self.__updateSubscription)\
+            .setEnabled(not subscriptionEditable)
+        menu.addAction(
+            self.tr("Update All Subscriptions"),
+            self.__updateAllSubscriptions)
+        menu.addSeparator()
+        menu.addAction(self.tr("Learn more about writing rules..."),
+                       self.__learnAboutWritingFilters)
+    
+    def addCustomRule(self, filter):
+        """
+        Public slot to add a custom AdBlock rule.
+        
+        @param filter filter to be added (string)
+        """
+        self.subscriptionsTabWidget.setCurrentIndex(
+            self.subscriptionsTabWidget.count() - 1)
+        self.__currentTreeWidget.addRule(filter)
+    
+    def __addCustomRule(self):
+        """
+        Private slot to add a custom AdBlock rule.
+        """
+        self.__currentTreeWidget.addRule()
+    
+    def __removeCustomRule(self):
+        """
+        Private slot to remove a custom AdBlock rule.
+        """
+        self.__currentTreeWidget.removeRule()
+    
+    def __updateSubscription(self):
+        """
+        Private slot to update the selected subscription.
+        """
+        self.__currentSubscription.updateNow()
+    
+    def __updateAllSubscriptions(self):
+        """
+        Private slot to update all subscriptions.
+        """
+        self.__manager.updateAllSubscriptions()
+    
+    def __browseSubscriptions(self):
+        """
+        Private slot to browse the list of available AdBlock subscriptions.
+        """
+        from WebBrowser.WebBrowserWindow import WebBrowserWindow
+        mw = WebBrowserWindow.mainWindow()
+        mw.newTab("http://adblockplus.org/en/subscriptions")
+        mw.raise_()
+    
+    def __learnAboutWritingFilters(self):
+        """
+        Private slot to show the web page about how to write filters.
+        """
+        from WebBrowser.WebBrowserWindow import WebBrowserWindow
+        mw = WebBrowserWindow.mainWindow()
+        mw.newTab("http://adblockplus.org/en/filters")
+        mw.raise_()
+    
+    def __removeSubscription(self):
+        """
+        Private slot to remove the selected subscription.
+        """
+        requiresTitles = []
+        requiresSubscriptions = \
+            self.__manager.getRequiresSubscriptions(self.__currentSubscription)
+        for subscription in requiresSubscriptions:
+            requiresTitles.append(subscription.title())
+        if requiresTitles:
+            message = self.tr(
+                "<p>Do you really want to remove subscription"
+                " <b>{0}</b> and all subscriptions requiring it?</p>"
+                "<ul><li>{1}</li></ul>").format(
+                self.__currentSubscription.title(),
+                "</li><li>".join(requiresTitles))
+        else:
+            message = self.tr(
+                "<p>Do you really want to remove subscription"
+                " <b>{0}</b>?</p>").format(self.__currentSubscription.title())
+        res = E5MessageBox.yesNo(
+            self,
+            self.tr("Remove Subscription"),
+            message)
+        
+        if res:
+            removeSubscription = self.__currentSubscription
+            removeTrees = [self.__currentTreeWidget]
+            for index in range(self.subscriptionsTabWidget.count()):
+                tree = self.subscriptionsTabWidget.widget(index)
+                if tree.subscription() in requiresSubscriptions:
+                    removeTrees.append(tree)
+            for tree in removeTrees:
+                self.subscriptionsTabWidget.removeTab(
+                    self.subscriptionsTabWidget.indexOf(tree))
+            self.__manager.removeSubscription(removeSubscription)
+    
+    def __switchSubscriptionEnabled(self):
+        """
+        Private slot to switch the enabled state of the selected subscription.
+        """
+        newState = not self.__currentSubscription.isEnabled()
+        self.__setSubscriptionEnabled(self.__currentSubscription, newState)
+    
+    def __setSubscriptionEnabled(self, subscription, enable):
+        """
+        Private slot to set the enabled state of a subscription.
+        
+        @param subscription subscription to set the state for
+            (AdBlockSubscription)
+        @param enable state to set to (boolean)
+        """
+        if enable:
+            # enable required one as well
+            sub = self.__manager.subscription(subscription.requiresLocation())
+            requiresSubscriptions = [] if sub is None else [sub]
+            icon = UI.PixmapCache.getIcon("adBlockPlus.png")
+        else:
+            # disable dependent ones as well
+            requiresSubscriptions = \
+                self.__manager.getRequiresSubscriptions(subscription)
+            icon = UI.PixmapCache.getIcon("adBlockPlusDisabled.png")
+        requiresSubscriptions.append(subscription)
+        for sub in requiresSubscriptions:
+            sub.setEnabled(enable)
+        
+        for index in range(self.subscriptionsTabWidget.count()):
+            tree = self.subscriptionsTabWidget.widget(index)
+            if tree.subscription() in requiresSubscriptions:
+                self.subscriptionsTabWidget.setTabIcon(
+                    self.subscriptionsTabWidget.indexOf(tree), icon)
+    
+    @pyqtSlot(int)
+    def on_updateSpinBox_valueChanged(self, value):
+        """
+        Private slot to handle changes of the update period.
+        
+        @param value update period (integer)
+        """
+        if value != Preferences.getWebBrowser("AdBlockUpdatePeriod"):
+            Preferences.setWebBrowser("AdBlockUpdatePeriod", value)
+            
+            from WebBrowser.WebBrowserWindow import WebBrowserWindow
+            manager = WebBrowserWindow.adBlockManager()
+            for subscription in manager.subscriptions():
+                subscription.checkForUpdate()
+    
+    @pyqtSlot(int)
+    def on_subscriptionsTabWidget_currentChanged(self, index):
+        """
+        Private slot handling the selection of another tab.
+        
+        @param index index of the new current tab (integer)
+        """
+        if index != -1:
+            self.__currentTreeWidget = \
+                self.subscriptionsTabWidget.widget(index)
+            self.__currentSubscription = \
+                self.__currentTreeWidget.subscription()
+            
+            isEasyList = \
+                self.__currentSubscription.url().toString().startswith(
+                    self.__manager.getDefaultSubscriptionUrl())
+            self.useLimitedEasyListCheckBox.setVisible(isEasyList)
+    
+    @pyqtSlot(str)
+    def on_searchEdit_textChanged(self, filter):
+        """
+        Private slot to set a new filter on the current widget.
+        
+        @param filter filter to be set (string)
+        """
+        if self.__currentTreeWidget and self.adBlockGroup.isChecked():
+            self.__currentTreeWidget.filterString(filter)
+    
+    @pyqtSlot(bool)
+    def on_adBlockGroup_toggled(self, state):
+        """
+        Private slot handling the enabling/disabling of AdBlock.
+        
+        @param state state of the toggle (boolean)
+        """
+        self.__manager.setEnabled(state)
+        
+        if state:
+            self.__load()
+    
+    @pyqtSlot(bool)
+    def on_useLimitedEasyListCheckBox_clicked(self, checked):
+        """
+        Private slot handling the selection of the limited EasyList.
+        
+        @param checked flag indicating the state of the check box
+        @type bool
+        """
+        self.__manager.setUseLimitedEasyList(
+            self.useLimitedEasyListCheckBox.isChecked())
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/AdBlock/AdBlockDialog.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,204 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>AdBlockDialog</class>
+ <widget class="QDialog" name="AdBlockDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>650</width>
+    <height>600</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>AdBlock Configuration</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout_2">
+   <item>
+    <widget class="QGroupBox" name="adBlockGroup">
+     <property name="title">
+      <string>Enable AdBlock</string>
+     </property>
+     <property name="checkable">
+      <bool>true</bool>
+     </property>
+     <layout class="QVBoxLayout" name="verticalLayout">
+      <item>
+       <layout class="QGridLayout" name="gridLayout">
+        <property name="horizontalSpacing">
+         <number>20</number>
+        </property>
+        <item row="0" column="0" rowspan="2">
+         <widget class="QLabel" name="iconLabel">
+          <property name="minimumSize">
+           <size>
+            <width>48</width>
+            <height>48</height>
+           </size>
+          </property>
+          <property name="text">
+           <string notr="true">Icon</string>
+          </property>
+         </widget>
+        </item>
+        <item row="0" column="1">
+         <spacer name="horizontalSpacer">
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>40</width>
+            <height>20</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+        <item row="1" column="1">
+         <widget class="E5ClearableLineEdit" name="searchEdit">
+          <property name="toolTip">
+           <string>Enter search term for subscriptions and rules</string>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+      <item>
+       <widget class="QTabWidget" name="subscriptionsTabWidget">
+        <property name="documentMode">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <layout class="QHBoxLayout" name="horizontalLayout">
+        <item>
+         <widget class="QToolButton" name="actionButton">
+          <property name="text">
+           <string>Actions</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <spacer name="horizontalSpacer_2">
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>40</width>
+            <height>20</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+        <item>
+         <widget class="QLabel" name="label">
+          <property name="text">
+           <string>Default Update Period (days):</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QSpinBox" name="updateSpinBox">
+          <property name="toolTip">
+           <string>Enter the update period (1 to 14 days)</string>
+          </property>
+          <property name="alignment">
+           <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+          </property>
+          <property name="suffix">
+           <string/>
+          </property>
+          <property name="minimum">
+           <number>1</number>
+          </property>
+          <property name="maximum">
+           <number>14</number>
+          </property>
+          <property name="value">
+           <number>7</number>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+      <item>
+       <widget class="QCheckBox" name="useLimitedEasyListCheckBox">
+        <property name="toolTip">
+         <string/>
+        </property>
+        <property name="text">
+         <string>Use only essential part of EasyList (for performance reasons)</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Close</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>E5ClearableLineEdit</class>
+   <extends>QLineEdit</extends>
+   <header>E5Gui/E5LineEdit.h</header>
+  </customwidget>
+ </customwidgets>
+ <tabstops>
+  <tabstop>adBlockGroup</tabstop>
+  <tabstop>searchEdit</tabstop>
+  <tabstop>subscriptionsTabWidget</tabstop>
+  <tabstop>actionButton</tabstop>
+  <tabstop>updateSpinBox</tabstop>
+  <tabstop>buttonBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>AdBlockDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>252</x>
+     <y>445</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>AdBlockDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>320</x>
+     <y>445</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/AdBlock/AdBlockExceptionsDialog.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,95 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog to configure the AdBlock exceptions.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSlot, Qt
+from PyQt5.QtWidgets import QDialog
+
+from .Ui_AdBlockExceptionsDialog import Ui_AdBlockExceptionsDialog
+
+import UI.PixmapCache
+
+
+class AdBlockExceptionsDialog(QDialog, Ui_AdBlockExceptionsDialog):
+    """
+    Class implementing a dialog to configure the AdBlock exceptions.
+    """
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(AdBlockExceptionsDialog, self).__init__(parent)
+        self.setupUi(self)
+        self.setWindowFlags(Qt.Window)
+        
+        self.iconLabel.setPixmap(
+            UI.PixmapCache.getPixmap("adBlockPlusGreen48.png"))
+        
+        self.hostEdit.setInactiveText(self.tr("Enter host to be added..."))
+        
+        self.buttonBox.setFocus()
+    
+    def load(self, hosts):
+        """
+        Public slot to load the list of excepted hosts.
+        
+        @param hosts list of excepted hosts
+        """
+        self.hostList.clear()
+        self.hostList.addItems(hosts)
+    
+    @pyqtSlot(str)
+    def on_hostEdit_textChanged(self, txt):
+        """
+        Private slot to handle changes of the host edit.
+        
+        @param txt text of the edit (string)
+        """
+        self.addButton.setEnabled(bool(txt))
+    
+    @pyqtSlot()
+    def on_addButton_clicked(self):
+        """
+        Private slot to handle a click of the add button.
+        """
+        self.hostList.addItem(self.hostEdit.text())
+        self.hostEdit.clear()
+    
+    @pyqtSlot()
+    def on_hostList_itemSelectionChanged(self):
+        """
+        Private slot handling a change of the number of selected items.
+        """
+        self.deleteButton.setEnabled(len(self.hostList.selectedItems()) > 0)
+    
+    @pyqtSlot()
+    def on_deleteButton_clicked(self):
+        """
+        Private slot handling a click of the delete button.
+        """
+        for itm in self.hostList.selectedItems():
+            row = self.hostList.row(itm)
+            removedItem = self.hostList.takeItem(row)
+            del removedItem
+    
+    def accept(self):
+        """
+        Public slot handling the acceptance of the dialog.
+        """
+        hosts = []
+        for row in range(self.hostList.count()):
+            hosts.append(self.hostList.item(row).text())
+        
+        from WebBrowser.WebBrowserWindow import WebBrowserWindow
+        WebBrowserWindow.adBlockManager().setExceptions(hosts)
+        
+        super(AdBlockExceptionsDialog, self).accept()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/AdBlock/AdBlockExceptionsDialog.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,167 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>AdBlockExceptionsDialog</class>
+ <widget class="QDialog" name="AdBlockExceptionsDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>550</width>
+    <height>450</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>AdBlock Exceptions</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QGridLayout" name="gridLayout">
+     <item row="0" column="0" rowspan="2">
+      <widget class="QLabel" name="iconLabel">
+       <property name="minimumSize">
+        <size>
+         <width>48</width>
+         <height>48</height>
+        </size>
+       </property>
+       <property name="text">
+        <string notr="true">Icon</string>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="1">
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>188</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item row="1" column="1">
+      <widget class="E5ClearableLineEdit" name="hostEdit">
+       <property name="toolTip">
+        <string>Enter a host to block AdBlock for</string>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="2">
+      <widget class="QPushButton" name="addButton">
+       <property name="enabled">
+        <bool>false</bool>
+       </property>
+       <property name="toolTip">
+        <string>Press to add the host</string>
+       </property>
+       <property name="text">
+        <string>&amp;Add</string>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="0" rowspan="2" colspan="2">
+      <widget class="QListWidget" name="hostList">
+       <property name="alternatingRowColors">
+        <bool>true</bool>
+       </property>
+       <property name="sortingEnabled">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="2">
+      <widget class="QPushButton" name="deleteButton">
+       <property name="enabled">
+        <bool>false</bool>
+       </property>
+       <property name="toolTip">
+        <string>Press to delete the selected hosts</string>
+       </property>
+       <property name="text">
+        <string>&amp;Delete</string>
+       </property>
+      </widget>
+     </item>
+     <item row="3" column="2">
+      <spacer name="verticalSpacer">
+       <property name="orientation">
+        <enum>Qt::Vertical</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>20</width>
+         <height>148</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>E5ClearableLineEdit</class>
+   <extends>QLineEdit</extends>
+   <header>E5Gui/E5LineEdit.h</header>
+  </customwidget>
+ </customwidgets>
+ <tabstops>
+  <tabstop>hostEdit</tabstop>
+  <tabstop>addButton</tabstop>
+  <tabstop>hostList</tabstop>
+  <tabstop>deleteButton</tabstop>
+  <tabstop>buttonBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>AdBlockExceptionsDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>AdBlockExceptionsDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/AdBlock/AdBlockIcon.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,188 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the AdBlock icon for the main window status bar.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import Qt
+from PyQt5.QtWidgets import QAction, QMenu
+
+from E5Gui.E5ClickableLabel import E5ClickableLabel
+
+import UI.PixmapCache
+
+
+class AdBlockIcon(E5ClickableLabel):
+    """
+    Class implementing the AdBlock icon for the main window status bar.
+    """
+    def __init__(self, parent):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (HelpWindow)
+        """
+        super(AdBlockIcon, self).__init__(parent)
+        
+        self.__mw = parent
+        self.__menuAction = None
+        self.__enabled = False
+        
+        self.setMaximumHeight(16)
+        self.setCursor(Qt.PointingHandCursor)
+        self.setToolTip(self.tr(
+            "AdBlock lets you block unwanted content on web pages."))
+        
+        self.clicked.connect(self.__showMenu)
+    
+    def setEnabled(self, enabled):
+        """
+        Public slot to set the enabled state.
+        
+        @param enabled enabled state (boolean)
+        """
+        self.__enabled = enabled
+        if enabled:
+            self.currentChanged()
+        else:
+            self.setPixmap(
+                UI.PixmapCache.getPixmap("adBlockPlusDisabled16.png"))
+    
+    def __createMenu(self, menu=None):
+        """
+        Private slot to create the context menu.
+        
+        @param menu parent menu (QMenu)
+        """
+        if menu is None:
+            menu = self.sender()
+            if menu is None:
+                return
+        
+        menu.clear()
+        
+        manager = self.__mw.adBlockManager()
+        
+        if manager.isEnabled():
+            menu.addAction(
+                UI.PixmapCache.getIcon("adBlockPlusDisabled.png"),
+                self.tr("Disable AdBlock"),
+                self.__enableAdBlock).setData(False)
+        else:
+            menu.addAction(
+                UI.PixmapCache.getIcon("adBlockPlus.png"),
+                self.tr("Enable AdBlock"),
+                self.__enableAdBlock).setData(True)
+        menu.addSeparator()
+        if manager.isEnabled() and self.__mw.currentBrowser().url().host():
+            if self.__isCurrentHostExcepted():
+                menu.addAction(
+                    UI.PixmapCache.getIcon("adBlockPlus.png"),
+                    self.tr("Remove AdBlock Exception"),
+                    self.__setException).setData(False)
+            else:
+                menu.addAction(
+                    UI.PixmapCache.getIcon("adBlockPlusGreen.png"),
+                    self.tr("Add AdBlock Exception"),
+                    self.__setException).setData(True)
+        menu.addAction(
+            UI.PixmapCache.getIcon("adBlockPlusGreen.png"),
+            self.tr("AdBlock Exceptions..."), manager.showExceptionsDialog)
+        menu.addSeparator()
+        menu.addAction(
+            UI.PixmapCache.getIcon("adBlockPlus.png"),
+            self.tr("AdBlock Configuration..."), manager.showDialog)
+    
+    def menuAction(self):
+        """
+        Public method to get a reference to the menu action.
+        
+        @return reference to the menu action (QAction)
+        """
+        if not self.__menuAction:
+            self.__menuAction = QAction(self.tr("AdBlock"), self)
+            self.__menuAction.setMenu(QMenu())
+            self.__menuAction.menu().aboutToShow.connect(self.__createMenu)
+        
+        if self.__enabled:
+            self.__menuAction.setIcon(
+                UI.PixmapCache.getIcon("adBlockPlus.png"))
+        else:
+            self.__menuAction.setIcon(
+                UI.PixmapCache.getIcon("adBlockPlusDisabled.png"))
+        
+        return self.__menuAction
+    
+    def __showMenu(self, pos):
+        """
+        Private slot to show the context menu.
+        
+        @param pos position the context menu should be shown (QPoint)
+        """
+        menu = QMenu()
+        self.__createMenu(menu)
+        menu.exec_(pos)
+    
+    def __enableAdBlock(self):
+        """
+        Private slot to enable or disable AdBlock.
+        """
+        act = self.sender()
+        if act is not None:
+            self.__mw.adBlockManager().setEnabled(act.data())
+    
+    def __isCurrentHostExcepted(self):
+        """
+        Private method to check, if the host of the current browser is
+        excepted.
+        
+        @return flag indicating an exception (boolean)
+        """
+        browser = self.__mw.currentBrowser()
+        if browser is None:
+            return False
+        
+        urlHost = browser.page().url().host()
+        
+        return urlHost and \
+            self.__mw.adBlockManager().isHostExcepted(urlHost)
+    
+    def currentChanged(self):
+        """
+        Public slot to handle a change of the current browser tab.
+        """
+        if self.__enabled:
+            if self.__isCurrentHostExcepted():
+                self.setPixmap(
+                    UI.PixmapCache.getPixmap("adBlockPlusGreen16.png"))
+            else:
+                self.setPixmap(UI.PixmapCache.getPixmap("adBlockPlus16.png"))
+    
+    def __setException(self):
+        """
+        Private slot to add or remove the current host from the list of
+        exceptions.
+        """
+        act = self.sender()
+        if act is not None:
+            urlHost = self.__mw.currentBrowser().url().host()
+            if act.data():
+                self.__mw.adBlockManager().addException(urlHost)
+            else:
+                self.__mw.adBlockManager().removeException(urlHost)
+            self.currentChanged()
+    
+    def sourceChanged(self, browser, url):
+        """
+        Public slot to handle URL changes.
+        
+        @param browser reference to the browser (HelpBrowser)
+        @param url new URL (QUrl)
+        """
+        if browser == self.__mw.currentBrowser():
+            self.currentChanged()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/AdBlock/AdBlockManager.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,609 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the AdBlock manager.
+"""
+
+from __future__ import unicode_literals
+
+import os
+
+from PyQt5.QtCore import pyqtSignal, QObject, QUrl, QUrlQuery, QFile, \
+    QByteArray
+from PyQt5.QtWebEngineCore import QWebEngineUrlRequestInfo
+
+from E5Gui import E5MessageBox
+
+from .AdBlockSubscription import AdBlockSubscription
+from .AdBlockUrlInterceptor import AdBlockUrlInterceptor
+
+from Utilities.AutoSaver import AutoSaver
+import Utilities
+import Preferences
+
+
+class AdBlockManager(QObject):
+    """
+    Class implementing the AdBlock manager.
+    
+    @signal rulesChanged() emitted after some rule has changed
+    """
+    rulesChanged = pyqtSignal()
+    requiredSubscriptionLoaded = pyqtSignal(AdBlockSubscription)
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object (QObject)
+        """
+        super(AdBlockManager, self).__init__(parent)
+        
+        self.__loaded = False
+        self.__subscriptionsLoaded = False
+        self.__enabled = False
+        self.__adBlockDialog = None
+        self.__adBlockExceptionsDialog = None
+        self.__adBlockNetwork = None
+        self.__adBlockPage = None
+        self.__subscriptions = []
+        self.__exceptedHosts = Preferences.getWebBrowser("AdBlockExceptions")
+        self.__saveTimer = AutoSaver(self, self.save)
+        self.__limitedEasyList = Preferences.getWebBrowser(
+            "AdBlockUseLimitedEasyList")
+        
+        self.__defaultSubscriptionUrlString = \
+            "abp:subscribe?location=" \
+            "https://easylist-downloads.adblockplus.org/easylist.txt&"\
+            "title=EasyList"
+        self.__customSubscriptionUrlString = \
+            bytes(self.__customSubscriptionUrl().toEncoded()).decode()
+        
+        self.rulesChanged.connect(self.__saveTimer.changeOccurred)
+        self.rulesChanged.connect(self.__rulesChanged)
+        
+        self.__interceptor = AdBlockUrlInterceptor(self)
+        
+        from WebBrowser.WebBrowserWindow import WebBrowserWindow
+        WebBrowserWindow.networkManager().installUrlInterceptor(
+            self.__interceptor)
+    
+    def __rulesChanged(self):
+        """
+        Private slot handling a change of the AdBlock rules.
+        """
+        from WebBrowser.WebBrowserWindow import WebBrowserWindow
+        WebBrowserWindow.mainWindow().reloadUserStyleSheet()
+    
+    def close(self):
+        """
+        Public method to close the open search engines manager.
+        """
+        self.__adBlockDialog and self.__adBlockDialog.close()
+        self.__adBlockExceptionsDialog and \
+            self.__adBlockExceptionsDialog.close()
+        
+        self.__saveTimer.saveIfNeccessary()
+    
+    def isEnabled(self):
+        """
+        Public method to check, if blocking ads is enabled.
+        
+        @return flag indicating the enabled state (boolean)
+        """
+        if not self.__loaded:
+            self.load()
+        
+        return self.__enabled
+    
+    def setEnabled(self, enabled):
+        """
+        Public slot to set the enabled state.
+        
+        @param enabled flag indicating the enabled state (boolean)
+        """
+        if self.isEnabled() == enabled:
+            return
+        
+        from WebBrowser.WebBrowserWindow import WebBrowserWindow
+        self.__enabled = enabled
+        for mainWindow in WebBrowserWindow.mainWindows():
+            mainWindow.adBlockIcon().setEnabled(enabled)
+        if enabled:
+            self.__loadSubscriptions()
+        self.rulesChanged.emit()
+    
+    def block(self, info):
+        """
+        Public method to check, if a request should be blocked.
+        
+        @param info request info aobject
+        @type QWebEngineUrlRequestInfo
+        @return flag indicating to block the request
+        @rtype bool
+        """
+        urlString = bytes(info.requestUrl().toEncoded()).decode().lower()
+        urlDomain = info.requestUrl().host().lower()
+        urlScheme = info.requestUrl().scheme().lower()
+        refererHost = info.firstPartyUrl().host().lower()
+        
+        if not self.isEnabled() or not self.__canRunOnScheme(urlScheme):
+            return False
+        
+        if self.isHostExcepted(urlDomain) or self.isHostExcepted(refererHost):
+            return False
+            
+        res = False
+        
+        for subscription in self.subscriptions():
+            if subscription.isEnabled():
+                if subscription.adBlockDisabledForUrl(info.requestUrl()):
+                    continue
+                
+                blockedRule = subscription.match(info, urlDomain, urlString)
+                if blockedRule:
+                    res = True
+                    if info.resourceType() == \
+                            QWebEngineUrlRequestInfo.ResourceTypeMainFrame:
+                        url = QUrl("eric:adblock")
+                        query = QUrlQuery()
+                        query.addQueryItem("rule", blockedRule.filter())
+                        query.addQueryItem(
+                            "subscription", blockedRule.subscription().title())
+                        url.setQuery(query)
+                        info.redirect(url)
+                        res = False
+                    else:
+                        info.block(True)
+                    break
+        
+        return res
+    
+    def __canRunOnScheme(self, scheme):
+        """
+        Private method to check, if AdBlock can be performed on the scheme.
+        
+        @param scheme scheme to check (string)
+        @return flag indicating, that AdBlock can be performed (boolean)
+        """
+        return scheme not in ["data", "eric", "qthelp", "qrc", "file", "abp"]
+    
+    def page(self):
+        """
+        Public method to get a reference to the page block object.
+        
+        @return reference to the page block object (AdBlockPage)
+        """
+        if self.__adBlockPage is None:
+            from .AdBlockPage import AdBlockPage
+            self.__adBlockPage = AdBlockPage(self)
+        return self.__adBlockPage
+    
+    def __customSubscriptionLocation(self):
+        """
+        Private method to generate the path for custom subscriptions.
+        
+        @return URL for custom subscriptions (QUrl)
+        """
+        dataDir = os.path.join(Utilities.getConfigDir(), "web_browser",
+                               "subscriptions")
+        if not os.path.exists(dataDir):
+            os.makedirs(dataDir)
+        fileName = os.path.join(dataDir, "adblock_subscription_custom")
+        return QUrl.fromLocalFile(fileName)
+    
+    def __customSubscriptionUrl(self):
+        """
+        Private method to generate the URL for custom subscriptions.
+        
+        @return URL for custom subscriptions (QUrl)
+        """
+        location = self.__customSubscriptionLocation()
+        encodedUrl = bytes(location.toEncoded()).decode()
+        url = QUrl("abp:subscribe?location={0}&title={1}".format(
+            encodedUrl, self.tr("Custom Rules")))
+        return url
+    
+    def customRules(self):
+        """
+        Public method to get a subscription for custom rules.
+        
+        @return subscription object for custom rules (AdBlockSubscription)
+        """
+        location = self.__customSubscriptionLocation()
+        for subscription in self.__subscriptions:
+            if subscription.location() == location:
+                return subscription
+        
+        url = self.__customSubscriptionUrl()
+        customAdBlockSubscription = AdBlockSubscription(url, True, self)
+        self.addSubscription(customAdBlockSubscription)
+        return customAdBlockSubscription
+    
+    def subscriptions(self):
+        """
+        Public method to get all subscriptions.
+        
+        @return list of subscriptions (list of AdBlockSubscription)
+        """
+        if not self.__loaded:
+            self.load()
+        
+        return self.__subscriptions[:]
+    
+    def subscription(self, location):
+        """
+        Public method to get a subscription based on its location.
+        
+        @param location location of the subscription to search for (string)
+        @return subscription or None (AdBlockSubscription)
+        """
+        if location != "":
+            for subscription in self.__subscriptions:
+                if subscription.location().toString() == location:
+                    return subscription
+        
+        return None
+    
+    def updateAllSubscriptions(self):
+        """
+        Public method to update all subscriptions.
+        """
+        for subscription in self.__subscriptions:
+            subscription.updateNow()
+    
+    def removeSubscription(self, subscription, emitSignal=True):
+        """
+        Public method to remove an AdBlock subscription.
+        
+        @param subscription AdBlock subscription to be removed
+            (AdBlockSubscription)
+        @param emitSignal flag indicating to send a signal (boolean)
+        """
+        if subscription is None:
+            return
+        
+        if subscription.url().toString().startswith(
+            (self.__defaultSubscriptionUrlString,
+             self.__customSubscriptionUrlString)):
+            return
+        
+        try:
+            self.__subscriptions.remove(subscription)
+            rulesFileName = subscription.rulesFileName()
+            QFile.remove(rulesFileName)
+            requiresSubscriptions = self.getRequiresSubscriptions(subscription)
+            for requiresSubscription in requiresSubscriptions:
+                self.removeSubscription(requiresSubscription, False)
+            if emitSignal:
+                self.rulesChanged.emit()
+        except ValueError:
+            pass
+    
+    def addSubscriptionFromUrl(self, url):
+        """
+        Public method to ad an AdBlock subscription given the abp URL:
+        
+        @param url URL to subscribe an AdBlock subscription
+        @type QUrl
+        @return flag indicating success
+        @rtype bool
+        """
+        if url.path() != "subscribe":
+            return False
+        
+        title = QUrl.fromPercentEncoding(
+            QByteArray(QUrlQuery(url).queryItemValue("title").encode()))
+        if not title:
+            return False
+        
+        res = E5MessageBox.yesNo(
+            None,
+            self.tr("Subscribe?"),
+            self.tr(
+                """<p>Subscribe to this AdBlock subscription?</p>"""
+                """<p>{0}</p>""").format(title))
+        if res:
+            from .AdBlockSubscription import AdBlockSubscription
+            from WebBrowser.WebBrowserWindow import WebBrowserWindow
+            
+            dlg = WebBrowserWindow.adBlockManager().showDialog()
+            subscription = AdBlockSubscription(
+                url, False,
+                WebBrowserWindow.adBlockManager())
+            WebBrowserWindow.adBlockManager().addSubscription(subscription)
+            dlg.addSubscription(subscription, False)
+            dlg.setFocus()
+            dlg.raise_()
+    
+    def addSubscription(self, subscription):
+        """
+        Public method to add an AdBlock subscription.
+        
+        @param subscription AdBlock subscription to be added
+            (AdBlockSubscription)
+        """
+        if subscription is None:
+            return
+        
+        self.__subscriptions.insert(-1, subscription)
+        
+        subscription.rulesChanged.connect(self.rulesChanged)
+        subscription.changed.connect(self.rulesChanged)
+        subscription.enabledChanged.connect(self.rulesChanged)
+        
+        self.rulesChanged.emit()
+    
+    def save(self):
+        """
+        Public method to save the AdBlock subscriptions.
+        """
+        if not self.__loaded:
+            return
+        
+        Preferences.setWebBrowser("AdBlockEnabled", self.__enabled)
+        if self.__subscriptionsLoaded:
+            subscriptions = []
+            requiresSubscriptions = []
+            # intermediate store for subscription requiring others
+            for subscription in self.__subscriptions:
+                if subscription is None:
+                    continue
+                urlString = bytes(subscription.url().toEncoded()).decode()
+                if "requiresLocation" in urlString:
+                    requiresSubscriptions.append(urlString)
+                else:
+                    subscriptions.append(urlString)
+                subscription.saveRules()
+            for subscription in requiresSubscriptions:
+                subscriptions.insert(-1, subscription)  # custom should be last
+            Preferences.setWebBrowser("AdBlockSubscriptions", subscriptions)
+    
+    def load(self):
+        """
+        Public method to load the AdBlock subscriptions.
+        """
+        if self.__loaded:
+            return
+        
+        self.__loaded = True
+        
+        self.__enabled = Preferences.getWebBrowser("AdBlockEnabled")
+        if self.__enabled:
+            self.__loadSubscriptions()
+    
+    def __loadSubscriptions(self):
+        """
+        Private method to load the set of subscriptions.
+        """
+        if self.__subscriptionsLoaded:
+            return
+        
+        subscriptions = Preferences.getWebBrowser("AdBlockSubscriptions")
+        if subscriptions:
+            for subscription in subscriptions:
+                if subscription.startswith(
+                        self.__defaultSubscriptionUrlString):
+                    break
+            else:
+                subscriptions.insert(0, self.__defaultSubscriptionUrlString)
+            for subscription in subscriptions:
+                if subscription.startswith(self.__customSubscriptionUrlString):
+                    break
+            else:
+                subscriptions.append(self.__customSubscriptionUrlString)
+        else:
+            subscriptions = [self.__defaultSubscriptionUrlString,
+                             self.__customSubscriptionUrlString]
+        for subscription in subscriptions:
+            url = QUrl.fromEncoded(subscription.encode("utf-8"))
+            adBlockSubscription = AdBlockSubscription(
+                url,
+                subscription.startswith(self.__customSubscriptionUrlString),
+                self,
+                subscription.startswith(self.__defaultSubscriptionUrlString))
+            adBlockSubscription.rulesChanged.connect(self.rulesChanged)
+            adBlockSubscription.changed.connect(self.rulesChanged)
+            adBlockSubscription.enabledChanged.connect(self.rulesChanged)
+            self.__subscriptions.append(adBlockSubscription)
+        
+        self.__subscriptionsLoaded = True
+    
+    def loadRequiredSubscription(self, location, title):
+        """
+        Public method to load a subscription required by another one.
+        
+        @param location location of the required subscription (string)
+        @param title title of the required subscription (string)
+        """
+        # Step 1: check, if the subscription is in the list of subscriptions
+        urlString = "abp:subscribe?location={0}&title={1}".format(
+            location, title)
+        for subscription in self.__subscriptions:
+            if subscription.url().toString().startswith(urlString):
+                # We found it!
+                return
+        
+        # Step 2: if it is not, get it
+        url = QUrl.fromEncoded(urlString.encode("utf-8"))
+        adBlockSubscription = AdBlockSubscription(url, False, self)
+        self.addSubscription(adBlockSubscription)
+        self.requiredSubscriptionLoaded.emit(adBlockSubscription)
+    
+    def getRequiresSubscriptions(self, subscription):
+        """
+        Public method to get a list of subscriptions, that require the given
+        one.
+        
+        @param subscription subscription to check for (AdBlockSubscription)
+        @return list of subscription requiring the given one (list of
+            AdBlockSubscription)
+        """
+        subscriptions = []
+        location = subscription.location().toString()
+        for subscription in self.__subscriptions:
+            if subscription.requiresLocation() == location:
+                subscriptions.append(subscription)
+        
+        return subscriptions
+    
+    def showDialog(self):
+        """
+        Public slot to show the AdBlock subscription management dialog.
+        
+        @return reference to the dialog (AdBlockDialog)
+        """
+        if self.__adBlockDialog is None:
+            from .AdBlockDialog import AdBlockDialog
+            self.__adBlockDialog = AdBlockDialog(self)
+        
+        self.__adBlockDialog.show()
+        return self.__adBlockDialog
+    
+    def elementHidingRules(self):
+        """
+        Public method to get the element hiding rules.
+        
+        @return element hiding rules (string)
+        """
+        if not self.isEnabled():
+            return ""
+        
+        rules = ""
+        
+        for subscription in self.__subscriptions:
+            rules += subscription.elementHidingRules()
+        
+        if rules:
+            # remove last ",
+            rules = rules[:-1]
+        
+        return rules
+    
+    def elementHidingRulesForDomain(self, url):
+        """
+        Public method to get the element hiding rules for a domain.
+        
+        @param url URL to get hiding rules for (QUrl)
+        @return element hiding rules (string)
+        """
+        if not self.isEnabled():
+            return ""
+        
+        rules = ""
+        
+        for subscription in self.__subscriptions:
+            if subscription.elemHideDisabledForUrl(url):
+                continue
+            
+            rules += subscription.elementHidingRulesForDomain(url.host())
+        
+        if rules:
+            # remove last ","
+            rules = rules[:-1]
+        
+        rules += "{display:none !important;}\n"
+        
+        return rules
+    
+    def exceptions(self):
+        """
+        Public method to get a list of excepted hosts.
+        
+        @return list of excepted hosts (list of string)
+        """
+        return self.__exceptedHosts
+    
+    def setExceptions(self, hosts):
+        """
+        Public method to set the list of excepted hosts.
+        
+        @param hosts list of excepted hosts (list of string)
+        """
+        self.__exceptedHosts = [host.lower() for host in hosts]
+        Preferences.setWebBrowser("AdBlockExceptions", self.__exceptedHosts)
+    
+    def addException(self, host):
+        """
+        Public method to add an exception.
+        
+        @param host to be excepted (string)
+        """
+        host = host.lower()
+        if host and host not in self.__exceptedHosts:
+            self.__exceptedHosts.append(host)
+            Preferences.setWebBrowser(
+                "AdBlockExceptions", self.__exceptedHosts)
+    
+    def removeException(self, host):
+        """
+        Public method to remove an exception.
+        
+        @param host to be removed from the list of exceptions (string)
+        """
+        host = host.lower()
+        if host in self.__exceptedHosts:
+            self.__exceptedHosts.remove(host)
+            Preferences.setWebBrowser(
+                "AdBlockExceptions", self.__exceptedHosts)
+    
+    def isHostExcepted(self, host):
+        """
+        Public slot to check, if a host is excepted.
+        
+        @param host host to check (string)
+        @return flag indicating an exception (boolean)
+        """
+        host = host.lower()
+        return host in self.__exceptedHosts
+    
+    def showExceptionsDialog(self):
+        """
+        Public method to show the AdBlock Exceptions dialog.
+        
+        @return reference to the exceptions dialog (AdBlockExceptionsDialog)
+        """
+        if self.__adBlockExceptionsDialog is None:
+            from .AdBlockExceptionsDialog import AdBlockExceptionsDialog
+            self.__adBlockExceptionsDialog = AdBlockExceptionsDialog()
+        
+        self.__adBlockExceptionsDialog.load(self.__exceptedHosts)
+        self.__adBlockExceptionsDialog.show()
+        return self.__adBlockExceptionsDialog
+    
+    def useLimitedEasyList(self):
+        """
+        Public method to test, if limited EasyList rules shall be used.
+        
+        @return flag indicating limited EasyList rules
+        @rtype bool
+        """
+        return self.__limitedEasyList
+    
+    def setUseLimitedEasyList(self, limited):
+        """
+        Public method to set the limited EasyList flag.
+        
+        @param limited flag indicating to use limited EasyList
+        @type bool
+        """
+        self.__limitedEasyList = limited
+        
+        for subscription in self.__subscriptions:
+            if subscription.url().toString().startswith(
+                    self.__defaultSubscriptionUrlString):
+                subscription.updateNow()
+        
+        Preferences.setWebBrowser("AdBlockUseLimitedEasyList", limited)
+    
+    def getDefaultSubscriptionUrl(self):
+        """
+        Public method to get the default subscription URL.
+        
+        @return default subscription URL
+        @rtype str
+        """
+        return self.__defaultSubscriptionUrlString
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/AdBlock/AdBlockPage.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,39 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a class to apply AdBlock rules to a web page.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import QObject
+
+from ..Tools import Scripts
+
+
+class AdBlockPage(QObject):
+    """
+    Class to apply AdBlock rules to a web page.
+    """
+    def hideBlockedPageEntries(self, page):
+        """
+        Public method to apply AdBlock rules to a web page.
+        
+        @param page reference to the web page (HelpWebPage)
+        """
+        if page is None:
+            return
+        
+        from WebBrowser.WebBrowserWindow import WebBrowserWindow
+        manager = WebBrowserWindow.adBlockManager()
+        if not manager.isEnabled():
+            return
+        
+        # apply domain specific element hiding rules
+        elementHiding = manager.elementHidingRulesForDomain(page.url())
+        if elementHiding:
+            script = Scripts.setCss(elementHiding)
+            page.runJavaScript(script)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/AdBlock/AdBlockRule.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,681 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the AdBlock rule class.
+"""
+
+from __future__ import unicode_literals
+
+import re
+
+from PyQt5.QtCore import Qt, QRegExp
+from PyQt5.QtWebEngineCore import QWebEngineUrlRequestInfo
+
+
+def toSecondLevelDomain(url):
+    """
+    Module function to get a second level domain from the given URL.
+    
+    @param url URL to extract domain from (QUrl)
+    @return name of second level domain (string)
+    """
+    topLevelDomain = url.topLevelDomain()
+    urlHost = url.host()
+    
+    if not topLevelDomain or not urlHost:
+        return ""
+    
+    domain = urlHost[:len(urlHost) - len(topLevelDomain)]
+    if domain.count(".") == 0:
+        return urlHost
+    
+    while domain.count(".") != 0:
+        domain = domain[domain.find(".") + 1:]
+    
+    return domain + topLevelDomain
+
+
+class AdBlockRule(object):
+    """
+    Class implementing the AdBlock rule.
+    """
+    def __init__(self, filter="", subscription=None):
+        """
+        Constructor
+        
+        @param filter filter string of the rule (string)
+        @param subscription reference to the subscription object
+            (AdBlockSubscription)
+        """
+        self.__subscription = subscription
+        
+        self.__regExp = QRegExp()
+        self.__options = []
+        self.__blockedDomains = []
+        self.__allowedDomains = []
+        
+        self.__enabled = True
+        self.__cssRule = False
+        self.__exception = False
+        self.__internalDisabled = False
+        self.__domainRestricted = False
+        self.__useRegExp = False
+        self.__useDomainMatch = False
+        self.__useEndsMatch = False
+        self.__thirdParty = False
+        self.__thirdPartyException = False
+        self.__object = False
+        self.__objectException = False
+        self.__subdocument = False
+        self.__subdocumentException = False
+        self.__xmlhttprequest = False
+        self.__xmlhttprequestException = False
+        self.__document = False
+        self.__elemhide = False
+        self.__caseSensitivity = Qt.CaseInsensitive
+        self.__image = False
+        self.__imageException = False
+        self.__script = False
+        self.__scriptException = False
+        self.__stylesheet = False
+        self.__stylesheetException = False
+        self.__objectSubrequest = False
+        self.__objectSubrequestException = False
+        self.__stringMatchRule = False
+        
+        self.setFilter(filter)
+    
+    def subscription(self):
+        """
+        Public method to get the subscription this rule belongs to.
+        
+        @return subscription of the rule (AdBlockSubscription)
+        """
+        return self.__subscription
+    
+    def filter(self):
+        """
+        Public method to get the rule filter string.
+        
+        @return rule filter string (string)
+        """
+        return self.__filter
+    
+    def setFilter(self, filter):
+        """
+        Public method to set the rule filter string.
+        
+        @param filter rule filter string (string)
+        """
+        self.__filter = filter
+        self.__parseFilter()
+    
+    def __parseFilter(self):
+        """
+        Private method to parse the filter pattern.
+        """
+        parsedLine = self.__filter
+        
+        # empty rule or just a comment
+        if not parsedLine.strip() or parsedLine.startswith(("!", "[Adblock")):
+            self.__enabled = False
+            return
+        
+        # CSS element hiding rule
+        if "##" in parsedLine or "#@#" in parsedLine:
+            self.__cssRule = True
+            pos = parsedLine.find("#")
+            
+            # domain restricted rule
+            if not parsedLine.startswith("##"):
+                domains = parsedLine[:pos]
+                self.__parseDomains(domains, ",")
+            
+            self.__exception = parsedLine[pos + 1] == "@"
+            
+            if self.__exception:
+                self.__cssSelector = parsedLine[pos + 3:]
+            else:
+                self.__cssSelector = parsedLine[pos + 2:]
+            # CSS rule cannot have more options -> stop parsing
+            return
+        
+        # Exception always starts with @@
+        if parsedLine.startswith("@@"):
+            self.__exception = True
+            parsedLine = parsedLine[2:]
+        
+        # Parse all options following '$' character
+        optionsIndex = parsedLine.find("$")
+        if optionsIndex >= 0:
+            options = parsedLine[optionsIndex + 1:].split(",")
+            
+            handledOptions = 0
+            for option in options:
+                if option.startswith("domain="):
+                    self.__parseDomains(option[7:], "|")
+                    handledOptions += 1
+                elif option == "match-case":
+                    self.__caseSensitivity = Qt.CaseSensitive
+                    handledOptions += 1
+                elif option.endswith("third-party"):
+                    self.__thirdParty = True
+                    self.__thirdPartyException = option.startswith("~")
+                    handledOptions += 1
+                elif option.endswith("object"):
+                    self.__object = True
+                    self.__objectException = option.startswith("~")
+                    handledOptions += 1
+                elif option.endswith("subdocument"):
+                    self.__subdocument = True
+                    self.__subdocumentException = option.startswith("~")
+                    handledOptions += 1
+                elif option.endswith("xmlhttprequest"):
+                    self.__xmlhttprequest = True
+                    self.__xmlhttprequestException = option.startswith("~")
+                    handledOptions += 1
+                elif option.endswith("image"):
+                    self.__image = True
+                    self.__imageException = option.startswith("~")
+                elif option.endswith("script"):
+                    self.__script = True
+                    self.__scriptException = option.startswith("~")
+                elif option.endswith("stylesheet"):
+                    self.__stylesheet = True
+                    self.__stylesheetException = option.startswith("~")
+                elif option.endswith("object-subrequest"):
+                    self.__objectSubrequest = True
+                    self.__objectSubrequestException = option.startswith("~")
+                elif option == "document" and self.__exception:
+                    self.__document = True
+                    handledOptions += 1
+                elif option == "elemhide" and self.__exception:
+                    self.__elemhide = True
+                    handledOptions += 1
+                elif option == "collapse":
+                    # Hiding placeholders of blocked elements
+                    handledOptions += 1
+            
+            # If we don't handle all options, it's safer to just disable
+            # this rule
+            if handledOptions != len(options):
+                self.__internalDisabled = True
+                return
+            
+            parsedLine = parsedLine[:optionsIndex]
+        
+        # Rule is classic regexp
+        if parsedLine.startswith("/") and parsedLine.endswith("/"):
+            parsedLine = parsedLine[1:-1]
+            self.__useRegExp = True
+            self.__regExp = QRegExp(parsedLine, self.__caseSensitivity,
+                                    QRegExp.RegExp)
+            return
+        
+        # Remove starting / ending wildcards
+        if parsedLine.startswith("*"):
+            parsedLine = parsedLine[1:]
+        if parsedLine.endswith("*"):
+            parsedLine = parsedLine[:-1]
+        
+        # Fast string matching for domain can be used
+        if parsedLine.startswith("||") and \
+           parsedLine.endswith("^") and \
+           QRegExp("[/:?=&\\*]").indexIn(parsedLine) == -1:
+            parsedLine = parsedLine[2:-1]
+            self.__useDomainMatch = True
+            self.__matchString = parsedLine
+            return
+        
+        # If rule contains '|' only at the end, string matching can be used
+        if parsedLine.endswith("|") and \
+           QRegExp("[\\^\\*]").indexIn(parsedLine) == -1 and \
+           parsedLine.count("|") == 1:
+            parsedLine = parsedLine[:-1]
+            self.__useEndsMatch = True
+            self.__matchString = parsedLine
+            return
+        
+        # If there is still a wildcard (*) or separator (^) or (|),
+        # the rule must be modified to comply with QRegExp.
+        if "*" in parsedLine or "^" in parsedLine or "|" in parsedLine:
+            pattern = self.__convertPatternToRegExp(parsedLine)
+            self.__useRegExp = True
+            self.__regExp = QRegExp(pattern, self.__caseSensitivity,
+                                    QRegExp.RegExp)
+            return
+        
+        # no regexp required
+        self.__useRegExp = False
+        self.__matchString = parsedLine
+        self.__stringMatchRule = True
+    
+    def __parseDomains(self, domains, separator):
+        """
+        Private method to parse a string with a domain list.
+        
+        @param domains list of domains (string)
+        @param separator separator character used by the list (string)
+        """
+        domainsList = domains.split(separator)
+        
+        for domain in domainsList:
+            if not domain:
+                continue
+            if domain.startswith("~"):
+                self.__blockedDomains.append(domain[1:])
+            else:
+                self.__allowedDomains.append(domain)
+        
+        self.__domainRestricted = \
+            bool(self.__blockedDomains) or bool(self.__allowedDomains)
+    
+    def networkMatch(self, request, domain, encodedUrl):
+        """
+        Public method to check the rule for a match.
+        
+        @param request reference to the network request
+        @type QWebEngineUrlRequestInfo
+        @param domain domain name
+        @type str
+        @param encodedUrl string encoded URL to be checked
+        @type str
+        @return flag indicating a match
+        @rtype bool
+        """
+        if self.__cssRule or not self.__enabled or self.__internalDisabled:
+            return False
+        
+        matched = self.__stringMatch(domain, encodedUrl)
+        
+        if matched:
+            # check domain restrictions
+            if self.__domainRestricted and \
+                    not self.matchDomain(request.firstPartyUrl().host()):
+                return False
+            
+            # check third-party restrictions
+            if self.__thirdParty and not self.matchThirdParty(request):
+                return False
+            
+            # check object restrictions
+            if self.__object and not self.matchObject(request):
+                return False
+            
+            # check subdocument restrictions
+            if self.__subdocument and not self.matchSubdocument(request):
+                return False
+            
+            # check xmlhttprequest restriction
+            if self.__xmlhttprequest and not self.matchXmlHttpRequest(request):
+                return False
+            
+            # check image restriction
+            if self.__image and not self.matchImage(request):
+                return False
+            
+            # check script restriction
+            if self.__script and not self.matchScript(request):
+                return False
+            
+            # check stylesheet restriction
+            if self.__stylesheet and not self.matchStyleSheet(request):
+                return False
+            
+            # check object-subrequest restriction
+            if self.__objectSubrequest and \
+                    not self.matchObjectSubrequest(request):
+                return False
+        
+        return matched
+    
+    def urlMatch(self, url):
+        """
+        Public method to check an URL against the rule.
+        
+        @param url URL to check (QUrl)
+        @return flag indicating a match (boolean)
+        """
+        if not self.__document and not self.__elemhide:
+            return False
+        
+        encodedUrl = bytes(url.toEncoded()).decode()
+        domain = url.host()
+        return self.__stringMatch(domain, encodedUrl)
+    
+    def __stringMatch(self, domain, encodedUrl):
+        """
+        Private method to match a domain string.
+        
+        @param domain domain to match
+        @type str
+        @param encodedUrl URL in encoded form
+        @type str
+        @return flag indicating a match
+        @rtype bool
+        """
+        if self.__cssRule or not self.__enabled or self.__internalDisabled:
+            return False
+        
+        matched = False
+        
+        if self.__useRegExp:
+            matched = self.__regExp.indexIn(encodedUrl) != -1
+        elif self.__useDomainMatch:
+            matched = domain.endswith(self.__matchString)
+        elif self.__useEndsMatch:
+            if self.__caseSensitivity == Qt.CaseInsensitive:
+                matched = encodedUrl.lower().endswith(
+                    self.__matchString.lower())
+            else:
+                matched = encodedUrl.endswith(self.__matchString)
+        else:
+            if self.__caseSensitivity == Qt.CaseInsensitive:
+                matched = self.__matchString.lower() in encodedUrl.lower()
+            else:
+                matched = self.__matchString in encodedUrl
+        
+        return matched
+    
+    def matchDomain(self, domain):
+        """
+        Public method to match a domain.
+        
+        @param domain domain name to check (string)
+        @return flag indicating a match (boolean)
+        """
+        if not self.__enabled:
+            return False
+        
+        if not self.__domainRestricted:
+            return True
+        
+        if len(self.__blockedDomains) == 0:
+            for dom in self.__allowedDomains:
+                if domain.endswith(dom):
+                    return True
+        elif len(self.__allowedDomains) == 0:
+            for dom in self.__blockedDomains:
+                if domain.endswith(dom):
+                    return False
+            return True
+        else:
+            for dom in self.__blockedDomains:
+                if domain.endswith(dom):
+                    return False
+            for dom in self.__allowedDomains:
+                if domain.endswith(dom):
+                    return True
+        
+        return False
+    
+    def matchThirdParty(self, req):
+        """
+        Public slot to match a third-party rule.
+        
+        @param req request object to check (QWebEngineUrlRequestInfo)
+        @return flag indicating a match (boolean)
+        """
+        # Third-party matching should be performed on second-level domains
+        firstPartyHost = toSecondLevelDomain(req.firstPartyUrl())
+        host = toSecondLevelDomain(req.requestUrl())
+        
+        match = firstPartyHost != host
+        
+        if self.__thirdPartyException:
+            return not match
+        else:
+            return match
+    
+    def matchObject(self, req):
+        """
+        Public slot to match an object rule.
+        
+        @param req request object to check (QWebEngineUrlRequestInfo)
+        @return flag indicating a match (boolean)
+        """
+        match = (
+            req.resourceType() == QWebEngineUrlRequestInfo.ResourceTypeObject)
+        
+        if self.__objectException:
+            return not match
+        else:
+            return match
+    
+    def matchSubdocument(self, req):
+        """
+        Public slot to match a sub-document rule.
+        
+        @param req request object to check (QWebEngineUrlRequestInfo)
+        @return flag indicating a match (boolean)
+        """
+        match = (
+            req.resourceType() ==
+            QWebEngineUrlRequestInfo.ResourceTypeSubFrame)
+        
+        if self.__subdocumentException:
+            return not match
+        else:
+            return match
+    
+    def matchXmlHttpRequest(self, req):
+        """
+        Public slot to match a XmlHttpRequest rule.
+        
+        @param req request object to check (QWebEngineUrlRequestInfo)
+        @return flag indicating a match (boolean)
+        """
+        match = (
+            req.resourceType() == QWebEngineUrlRequestInfo.ResourceTypeXhr)
+        
+        if self.__xmlhttprequestException:
+            return not match
+        else:
+            return match
+    
+    def matchImage(self, req):
+        """
+        Public slot to match an Image rule.
+        
+        @param req request object to check (QWebEngineUrlRequestInfo)
+        @return flag indicating a match (boolean)
+        """
+        match = (
+            req.resourceType() == QWebEngineUrlRequestInfo.ResourceTypeImage)
+        
+        if self.__imageException:
+            return not match
+        else:
+            return match
+    
+    def matchScript(self, req):
+        """
+        Public slot to match a Script rule.
+        
+        @param req request object to check (QWebEngineUrlRequestInfo)
+        @return flag indicating a match (boolean)
+        """
+        match = (
+            req.resourceType() == QWebEngineUrlRequestInfo.ResourceTypeScript)
+        
+        if self.__scriptException:
+            return not match
+        else:
+            return match
+    
+    def matchStyleSheet(self, req):
+        """
+        Public slot to match a StyleSheet rule.
+        
+        @param req request object to check (QWebEngineUrlRequestInfo)
+        @return flag indicating a match (boolean)
+        """
+        match = (
+            req.resourceType() ==
+            QWebEngineUrlRequestInfo.ResourceTypeStylesheet)
+        
+        if self.__stylesheetException:
+            return not match
+        else:
+            return match
+    
+    def matchObjectSubrequest(self, req):
+        """
+        Public slot to match an Object Subrequest rule.
+        
+        @param req request object to check (QWebEngineUrlRequestInfo)
+        @return flag indicating a match (boolean)
+        """
+        match = (
+            req.resourceType() ==
+            QWebEngineUrlRequestInfo.ResourceTypeSubResource)
+        
+        if self.__objectSubrequestException:
+            return not match
+        else:
+            return match
+    
+    def isException(self):
+        """
+        Public method to check, if the rule defines an exception.
+        
+        @return flag indicating an exception (boolean)
+        """
+        return self.__exception
+    
+    def setException(self, exception):
+        """
+        Public method to set the rule's exception flag.
+        
+        @param exception flag indicating an exception rule (boolean)
+        """
+        self.__exception = exception
+    
+    def isEnabled(self):
+        """
+        Public method to check, if the rule is enabled.
+        
+        @return flag indicating enabled state (boolean)
+        """
+        return self.__enabled
+    
+    def setEnabled(self, enabled):
+        """
+        Public method to set the rule's enabled state.
+        
+        @param enabled flag indicating the new enabled state (boolean)
+        """
+        self.__enabled = enabled
+        if not enabled:
+            self.__filter = "!" + self.__filter
+        else:
+            self.__filter = self.__filter[1:]
+    
+    def isCSSRule(self):
+        """
+        Public method to check, if the rule is a CSS rule.
+        
+        @return flag indicating a CSS rule (boolean)
+        """
+        return self.__cssRule
+    
+    def cssSelector(self):
+        """
+        Public method to get the CSS selector of the rule.
+        
+        @return CSS selector (string)
+        """
+        return self.__cssSelector
+    
+    def isDocument(self):
+        """
+        Public method to check, if this is a document rule.
+        
+        @return flag indicating a document rule (boolean)
+        """
+        return self.__document
+    
+    def isElementHiding(self):
+        """
+        Public method to check, if this is an element hiding rule.
+        
+        @return flag indicating an element hiding rule (boolean)
+        """
+        return self.__elemhide
+    
+    def isDomainRestricted(self):
+        """
+        Public method to check, if this rule is restricted by domain.
+        
+        @return flag indicating a domain restriction (boolean)
+        """
+        return self.__domainRestricted
+    
+    def isComment(self):
+        """
+        Public method to check, if this is a comment.
+        
+        @return flag indicating a comment (boolean)
+        """
+        return self.__filter.startswith("!")
+    
+    def isHeader(self):
+        """
+        Public method to check, if this is a header.
+        
+        @return flag indicating a header (boolean)
+        """
+        return self.__filter.startswith("[Adblock")
+    
+    def isSlow(self):
+        """
+        Public method to check, if this is a slow rule.
+        
+        @return flag indicating a slow rule (boolean)
+        """
+        return self.__useRegExp
+    
+    def isInternalDisabled(self):
+        """
+        Public method to check, if this rule was disabled internally.
+        
+        @return flag indicating an internally disabled rule (boolean)
+        """
+        return self.__internalDisabled
+    
+    def __convertPatternToRegExp(self, wildcardPattern):
+        """
+        Private method to convert a wildcard pattern to a regular expression.
+        
+        @param wildcardPattern string containing the wildcard pattern (string)
+        @return string containing a regular expression (string)
+        """
+        pattern = wildcardPattern
+        
+        # remove multiple wildcards
+        pattern = re.sub(r"\*+", "*", pattern)
+        # remove anchors following separator placeholder
+        pattern = re.sub(r"\^\|$", "^", pattern)
+        # remove leading wildcards
+        pattern = re.sub(r"^(\*)", "", pattern)
+        # remove trailing wildcards
+        pattern = re.sub(r"(\*)$", "", pattern)
+        # escape special symbols
+        pattern = re.sub(r"(\W)", r"\\\1", pattern)
+        # process extended anchor at expression start
+        pattern = re.sub(
+            r"^\\\|\\\|",
+            r"^[\w\-]+:\/+(?!\/)(?:[^\/]+\.)?", pattern)
+        # process separator placeholders
+        pattern = re.sub(r"\\\^", r"(?:[^\w\d\-.%]|$)", pattern)
+        # process anchor at expression start
+        pattern = re.sub(r"^\\\|", "^", pattern)
+        # process anchor at expression end
+        pattern = re.sub(r"\\\|$", "$", pattern)
+        # replace wildcards by .*
+        pattern = re.sub(r"\\\*", ".*", pattern)
+        
+        return pattern
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/AdBlock/AdBlockSubscription.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,718 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the AdBlock subscription class.
+"""
+
+from __future__ import unicode_literals
+
+import os
+import re
+import hashlib
+import base64
+
+from PyQt5.QtCore import pyqtSignal, Qt, QObject, QByteArray, QDateTime, \
+    QUrl, QUrlQuery, QCryptographicHash, QFile, QIODevice, QTextStream, \
+    QDate, QTime
+from PyQt5.QtNetwork import QNetworkReply
+
+from E5Gui import E5MessageBox
+
+import Utilities
+import Preferences
+
+
+class AdBlockSubscription(QObject):
+    """
+    Class implementing the AdBlock subscription.
+    
+    @signal changed() emitted after the subscription has changed
+    @signal rulesChanged() emitted after the subscription's rules have changed
+    @signal enabledChanged(bool) emitted after the enabled state was changed
+    """
+    changed = pyqtSignal()
+    rulesChanged = pyqtSignal()
+    enabledChanged = pyqtSignal(bool)
+    
+    def __init__(self, url, custom, parent=None, default=False):
+        """
+        Constructor
+        
+        @param url AdBlock URL for the subscription (QUrl)
+        @param custom flag indicating a custom subscription (boolean)
+        @param parent reference to the parent object (QObject)
+        @param default flag indicating a default subscription (boolean)
+        """
+        super(AdBlockSubscription, self).__init__(parent)
+        
+        self.__custom = custom
+        self.__url = url.toEncoded()
+        self.__enabled = False
+        self.__downloading = None
+        self.__defaultSubscription = default
+        
+        self.__title = ""
+        self.__location = QByteArray()
+        self.__lastUpdate = QDateTime()
+        self.__requiresLocation = ""
+        self.__requiresTitle = ""
+        
+        self.__updatePeriod = 0     # update period in hours, 0 = use default
+        self.__remoteModified = QDateTime()
+        
+        self.__rules = []   # list containing all AdBlock rules
+        
+        self.__networkExceptionRules = []
+        self.__networkBlockRules = []
+        self.__domainRestrictedCssRules = []
+        self.__elementHidingRules = ""
+        self.__documentRules = []
+        self.__elemhideRules = []
+        
+        self.__checksumRe = re.compile(
+            r"""^\s*!\s*checksum[\s\-:]+([\w\+\/=]+).*\n""",
+            re.IGNORECASE | re.MULTILINE)
+        self.__expiresRe = re.compile(
+            r"""(?:expires:|expires after)\s*(\d+)\s*(hour|h)?""",
+            re.IGNORECASE)
+        self.__remoteModifiedRe = re.compile(
+            r"""!\s*(?:Last modified|Updated):\s*(\d{1,2})\s*"""
+            r"""(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s*"""
+            r"""(\d{2,4})\s*((\d{1,2}):(\d{2}))?""",
+            re.IGNORECASE)
+        
+        self.__monthNameToNumber = {
+            "Jan": 1,
+            "Feb": 2,
+            "Mar": 3,
+            "Apr": 4,
+            "May": 5,
+            "Jun": 6,
+            "Jul": 7,
+            "Aug": 8,
+            "Sep": 9,
+            "Oct": 10,
+            "Nov": 11,
+            "Dec": 12
+        }
+        
+        self.__parseUrl(url)
+    
+    def __parseUrl(self, url):
+        """
+        Private method to parse the AdBlock URL for the subscription.
+        
+        @param url AdBlock URL for the subscription (QUrl)
+        """
+        if url.scheme() != "abp":
+            return
+        
+        if url.path() != "subscribe":
+            return
+        
+        urlQuery = QUrlQuery(url)
+        self.__title = QUrl.fromPercentEncoding(
+            QByteArray(urlQuery.queryItemValue("title").encode()))
+        self.__enabled = urlQuery.queryItemValue("enabled") != "false"
+        self.__location = QByteArray(QUrl.fromPercentEncoding(
+            QByteArray(urlQuery.queryItemValue("location").encode()))
+            .encode("utf-8"))
+        
+        # Check for required subscription
+        self.__requiresLocation = QUrl.fromPercentEncoding(
+            QByteArray(urlQuery.queryItemValue(
+                "requiresLocation").encode()))
+        self.__requiresTitle = QUrl.fromPercentEncoding(
+            QByteArray(urlQuery.queryItemValue("requiresTitle").encode()))
+        if self.__requiresLocation and self.__requiresTitle:
+            from WebBrowser.WebBrowserWindow import WebBrowserWindow
+            WebBrowserWindow.adBlockManager().loadRequiredSubscription(
+                self.__requiresLocation, self.__requiresTitle)
+        
+        lastUpdateString = urlQuery.queryItemValue("lastUpdate")
+        self.__lastUpdate = QDateTime.fromString(lastUpdateString,
+                                                 Qt.ISODate)
+        
+        self.__loadRules()
+    
+    def url(self):
+        """
+        Public method to generate the URL for this subscription.
+        
+        @return AdBlock URL for the subscription (QUrl)
+        """
+        url = QUrl()
+        url.setScheme("abp")
+        url.setPath("subscribe")
+        
+        queryItems = []
+        queryItems.append(("location", bytes(self.__location).decode()))
+        queryItems.append(("title", self.__title))
+        if self.__requiresLocation and self.__requiresTitle:
+            queryItems.append(("requiresLocation", self.__requiresLocation))
+            queryItems.append(("requiresTitle", self.__requiresTitle))
+        if not self.__enabled:
+            queryItems.append(("enabled", "false"))
+        if self.__lastUpdate.isValid():
+            queryItems.append(("lastUpdate",
+                               self.__lastUpdate.toString(Qt.ISODate)))
+        
+        query = QUrlQuery()
+        query.setQueryItems(queryItems)
+        url.setQuery(query)
+        return url
+    
+    def isEnabled(self):
+        """
+        Public method to check, if the subscription is enabled.
+        
+        @return flag indicating the enabled status (boolean)
+        """
+        return self.__enabled
+    
+    def setEnabled(self, enabled):
+        """
+        Public method to set the enabled status.
+        
+        @param enabled flag indicating the enabled status (boolean)
+        """
+        if self.__enabled == enabled:
+            return
+        
+        self.__enabled = enabled
+        self.enabledChanged.emit(enabled)
+    
+    def title(self):
+        """
+        Public method to get the subscription title.
+        
+        @return subscription title (string)
+        """
+        return self.__title
+    
+    def setTitle(self, title):
+        """
+        Public method to set the subscription title.
+        
+        @param title subscription title (string)
+        """
+        if self.__title == title:
+            return
+        
+        self.__title = title
+        self.changed.emit()
+    
+    def location(self):
+        """
+        Public method to get the subscription location.
+        
+        @return URL of the subscription location (QUrl)
+        """
+        return QUrl.fromEncoded(self.__location)
+    
+    def setLocation(self, url):
+        """
+        Public method to set the subscription location.
+        
+        @param url URL of the subscription location (QUrl)
+        """
+        if url == self.location():
+            return
+        
+        self.__location = url.toEncoded()
+        self.__lastUpdate = QDateTime()
+        self.changed.emit()
+    
+    def requiresLocation(self):
+        """
+        Public method to get the location of a required subscription.
+        
+        @return location of a required subscription (string)
+        """
+        return self.__requiresLocation
+    
+    def lastUpdate(self):
+        """
+        Public method to get the date and time of the last update.
+        
+        @return date and time of the last update (QDateTime)
+        """
+        return self.__lastUpdate
+    
+    def rulesFileName(self):
+        """
+        Public method to get the name of the rules file.
+        
+        @return name of the rules file (string)
+        """
+        if self.location().scheme() == "file":
+            return self.location().toLocalFile()
+        
+        if self.__location.isEmpty():
+            return ""
+        
+        sha1 = bytes(QCryptographicHash.hash(
+            self.__location, QCryptographicHash.Sha1).toHex()).decode()
+        dataDir = os.path.join(
+            Utilities.getConfigDir(), "web_browser", "subscriptions")
+        if not os.path.exists(dataDir):
+            os.makedirs(dataDir)
+        fileName = os.path.join(
+            dataDir, "adblock_subscription_{0}".format(sha1))
+        return fileName
+    
+    def __loadRules(self):
+        """
+        Private method to load the rules of the subscription.
+        """
+        fileName = self.rulesFileName()
+        f = QFile(fileName)
+        if f.exists():
+            if not f.open(QIODevice.ReadOnly):
+                E5MessageBox.warning(
+                    None,
+                    self.tr("Load subscription rules"),
+                    self.tr(
+                        """Unable to open AdBlock file '{0}' for reading.""")
+                    .format(fileName))
+            else:
+                textStream = QTextStream(f)
+                header = textStream.readLine(1024)
+                if not header.startswith("[Adblock"):
+                    E5MessageBox.warning(
+                        None,
+                        self.tr("Load subscription rules"),
+                        self.tr("""AdBlock file '{0}' does not start"""
+                                """ with [Adblock.""")
+                        .format(fileName))
+                    f.close()
+                    f.remove()
+                    self.__lastUpdate = QDateTime()
+                else:
+                    from .AdBlockRule import AdBlockRule
+                    
+                    self.__updatePeriod = 0
+                    self.__remoteModified = QDateTime()
+                    self.__rules = []
+                    self.__rules.append(AdBlockRule(header, self))
+                    while not textStream.atEnd():
+                        line = textStream.readLine()
+                        self.__rules.append(AdBlockRule(line, self))
+                        expires = self.__expiresRe.search(line)
+                        if expires:
+                            period, kind = expires.groups()
+                            if kind:
+                                # hours
+                                self.__updatePeriod = int(period)
+                            else:
+                                # days
+                                self.__updatePeriod = int(period) * 24
+                        remoteModified = self.__remoteModifiedRe.search(line)
+                        if remoteModified:
+                            day, month, year, time, hour, minute = \
+                                remoteModified.groups()
+                            self.__remoteModified.setDate(
+                                QDate(int(year),
+                                      self.__monthNameToNumber[month],
+                                      int(day))
+                            )
+                            if time:
+                                self.__remoteModified.setTime(
+                                    QTime(int(hour), int(minute)))
+                    self.__populateCache()
+                    self.changed.emit()
+        elif not fileName.endswith("_custom"):
+            self.__lastUpdate = QDateTime()
+        
+        self.checkForUpdate()
+    
+    def checkForUpdate(self):
+        """
+        Public method to check for an update.
+        """
+        if self.__updatePeriod:
+            updatePeriod = self.__updatePeriod
+        else:
+            updatePeriod = \
+                Preferences.getWebBrowser("AdBlockUpdatePeriod") * 24
+        if not self.__lastUpdate.isValid() or \
+           (self.__remoteModified.isValid() and
+            self.__remoteModified.addSecs(updatePeriod * 3600) <
+                QDateTime.currentDateTime()) or \
+           self.__lastUpdate.addSecs(updatePeriod * 3600) < \
+                QDateTime.currentDateTime():
+            self.updateNow()
+    
+    def updateNow(self):
+        """
+        Public method to update the subscription immediately.
+        """
+        if self.__downloading is not None:
+            return
+        
+        if not self.location().isValid():
+            return
+        
+        if self.location().scheme() == "file":
+            self.__lastUpdate = QDateTime.currentDateTime()
+            self.__loadRules()
+            return
+        
+        from WebBrowser.WebBrowserWindow import WebBrowserWindow
+        from WebBrowser.Network.FollowRedirectReply import FollowRedirectReply
+        self.__downloading = FollowRedirectReply(
+            self.location(),
+            WebBrowserWindow.networkManager())
+        self.__downloading.finished.connect(self.__rulesDownloaded)
+    
+    def __rulesDownloaded(self):
+        """
+        Private slot to deal with the downloaded rules.
+        """
+        reply = self.sender()
+        
+        response = reply.readAll()
+        reply.close()
+        self.__downloading = None
+        
+        if reply.error() != QNetworkReply.NoError:
+            if not self.__defaultSubscription:
+                # don't show error if we try to load the default
+                E5MessageBox.warning(
+                    None,
+                    self.tr("Downloading subscription rules"),
+                    self.tr(
+                        """<p>Subscription rules could not be"""
+                        """ downloaded.</p><p>Error: {0}</p>""")
+                    .format(reply.errorString()))
+            else:
+                # reset after first download attempt
+                self.__defaultSubscription = False
+            return
+        
+        if response.isEmpty():
+            E5MessageBox.warning(
+                None,
+                self.tr("Downloading subscription rules"),
+                self.tr("""Got empty subscription rules."""))
+            return
+        
+        fileName = self.rulesFileName()
+        QFile.remove(fileName)
+        f = QFile(fileName)
+        if not f.open(QIODevice.ReadWrite):
+            E5MessageBox.warning(
+                None,
+                self.tr("Downloading subscription rules"),
+                self.tr(
+                    """Unable to open AdBlock file '{0}' for writing.""")
+                .file(fileName))
+            return
+        
+        from WebBrowser.WebBrowserWindow import WebBrowserWindow
+        if WebBrowserWindow.adBlockManager().useLimitedEasyList() and \
+            self.url().toString().startswith(
+                WebBrowserWindow.adBlockManager().getDefaultSubscriptionUrl()):
+            limited = True
+            # ignore Third-party advertisers rules for performance
+            # whitelist rules at the end will be used
+            index = response.indexOf(
+                "!---------------------------"
+                "Third-party advertisers"
+                "---------------------------!")
+            part1 = response.left(index)
+            index = response.indexOf(
+                "!-----------------------"
+                "Whitelists to fix broken sites"
+                "------------------------!")
+            part2 = response.mid(index)
+            f.write(part1)
+            f.write(part2)
+        else:
+            limited = False
+            f.write(response)
+        f.close()
+        self.__lastUpdate = QDateTime.currentDateTime()
+        if limited or self.__validateCheckSum(fileName):
+            self.__loadRules()
+        else:
+            QFile.remove(fileName)
+        self.__downloading = None
+        reply.deleteLater()
+    
+    def __validateCheckSum(self, fileName):
+        """
+        Private method to check the subscription file's checksum.
+        
+        @param fileName name of the file containing the subscription (string)
+        @return flag indicating a valid file (boolean). A file is considered
+            valid, if the checksum is OK, the file does not contain a
+            checksum (i.e. cannot be checked) or we are using the limited
+            EasyList (because we fiddled with the original).
+        """
+        try:
+            f = open(fileName, "r", encoding="utf-8")
+            data = f.read()
+            f.close()
+        except (IOError, OSError):
+            return False
+        
+        match = re.search(self.__checksumRe, data)
+        if match:
+            expectedChecksum = match.group(1)
+        else:
+            # consider it as valid
+            return True
+        
+        # normalize the data
+        data = re.sub(r"\r", "", data)              # normalize eol
+        data = re.sub(r"\n+", "\n", data)           # remove empty lines
+        data = re.sub(self.__checksumRe, "", data)  # remove checksum line
+        
+        # calculate checksum
+        md5 = hashlib.md5()
+        md5.update(data.encode("utf-8"))
+        calculatedChecksum = base64.b64encode(md5.digest()).decode()\
+            .rstrip("=")
+        if calculatedChecksum == expectedChecksum:
+            return True
+        else:
+            res = E5MessageBox.yesNo(
+                None,
+                self.tr("Downloading subscription rules"),
+                self.tr(
+                    """<p>AdBlock subscription <b>{0}</b> has a wrong"""
+                    """ checksum.<br/>"""
+                    """Found: {1}<br/>"""
+                    """Calculated: {2}<br/>"""
+                    """Use it anyway?</p>""")
+                .format(self.__title, expectedChecksum,
+                        calculatedChecksum))
+            return res
+    
+    def saveRules(self):
+        """
+        Public method to save the subscription rules.
+        """
+        fileName = self.rulesFileName()
+        if not fileName:
+            return
+        
+        f = QFile(fileName)
+        if not f.open(QIODevice.ReadWrite | QIODevice.Truncate):
+            E5MessageBox.warning(
+                None,
+                self.tr("Saving subscription rules"),
+                self.tr(
+                    """Unable to open AdBlock file '{0}' for writing.""")
+                .format(fileName))
+            return
+        
+        textStream = QTextStream(f)
+        if not self.__rules or not self.__rules[0].isHeader():
+            textStream << "[Adblock Plus 1.1.1]\n"
+        for rule in self.__rules:
+            textStream << rule.filter() << "\n"
+    
+    def match(self, req, urlDomain, urlString):
+        """
+        Public method to check the subscription for a matching rule.
+        
+        @param req reference to the network request (QWebEngineUrlRequestInfo)
+        @param urlDomain domain of the URL (string)
+        @param urlString URL (string)
+        @return reference to the rule object or None (AdBlockRule)
+        """
+        for rule in self.__networkExceptionRules:
+            if rule.networkMatch(req, urlDomain, urlString):
+                return None
+        
+        for rule in self.__networkBlockRules:
+            if rule.networkMatch(req, urlDomain, urlString):
+                return rule
+        
+        return None
+    
+    def adBlockDisabledForUrl(self, url):
+        """
+        Public method to check, if AdBlock is disabled for the given URL.
+        
+        @param url URL to check (QUrl)
+        @return flag indicating disabled state (boolean)
+        """
+        for rule in self.__documentRules:
+            if rule.urlMatch(url):
+                return True
+        
+        return False
+    
+    def elemHideDisabledForUrl(self, url):
+        """
+        Public method to check, if element hiding is disabled for the given
+        URL.
+        
+        @param url URL to check (QUrl)
+        @return flag indicating disabled state (boolean)
+        """
+        if self.adBlockDisabledForUrl(url):
+            return True
+        
+        for rule in self.__elemhideRules:
+            if rule.urlMatch(url):
+                return True
+        
+        return False
+    
+    def elementHidingRules(self):
+        """
+        Public method to get the element hiding rules.
+        
+        @return element hiding rules (string)
+        """
+        return self.__elementHidingRules
+    
+    def elementHidingRulesForDomain(self, domain):
+        """
+        Public method to get the element hiding rules for the given domain.
+        
+        @param domain domain name (string)
+        @return element hiding rules (string)
+        """
+        rules = ""
+        
+        for rule in self.__domainRestrictedCssRules:
+            if rule.matchDomain(domain):
+                rules += rule.cssSelector() + ","
+        
+        return rules
+    
+    def rule(self, offset):
+        """
+        Public method to get a specific rule.
+        
+        @param offset offset of the rule (integer)
+        @return requested rule (AdBlockRule)
+        """
+        if offset >= len(self.__rules):
+            return None
+        
+        return self.__rules[offset]
+    
+    def allRules(self):
+        """
+        Public method to get the list of rules.
+        
+        @return list of rules (list of AdBlockRule)
+        """
+        return self.__rules[:]
+    
+    def addRule(self, rule):
+        """
+        Public method to add a rule.
+        
+        @param rule reference to the rule to add (AdBlockRule)
+        @return offset of the rule (integer)
+        """
+        self.__rules.append(rule)
+        self.__populateCache()
+        self.rulesChanged.emit()
+        
+        return len(self.__rules) - 1
+    
+    def removeRule(self, offset):
+        """
+        Public method to remove a rule given the offset.
+        
+        @param offset offset of the rule to remove (integer)
+        """
+        if offset < 0 or offset > len(self.__rules):
+            return
+        
+        del self.__rules[offset]
+        self.__populateCache()
+        self.rulesChanged.emit()
+    
+    def replaceRule(self, rule, offset):
+        """
+        Public method to replace a rule given the offset.
+        
+        @param rule reference to the rule to set (AdBlockRule)
+        @param offset offset of the rule to remove (integer)
+        @return requested rule (AdBlockRule)
+        """
+        if offset >= len(self.__rules):
+            return None
+        
+        self.__rules[offset] = rule
+        self.__populateCache()
+        self.rulesChanged.emit()
+        
+        return self.__rules[offset]
+    
+    def __populateCache(self):
+        """
+        Private method to populate the various rule caches.
+        """
+        self.__networkExceptionRules = []
+        self.__networkBlockRules = []
+        self.__domainRestrictedCssRules = []
+        self.__elementHidingRules = ""
+        self.__documentRules = []
+        self.__elemhideRules = []
+        
+        for rule in self.__rules:
+            if not rule.isEnabled():
+                continue
+            
+            if rule.isCSSRule():
+                if rule.isDomainRestricted():
+                    self.__domainRestrictedCssRules.append(rule)
+                else:
+                    self.__elementHidingRules += rule.cssSelector() + ","
+            elif rule.isDocument():
+                self.__documentRules.append(rule)
+            elif rule.isElementHiding():
+                self.__elemhideRules.append(rule)
+            elif rule.isException():
+                self.__networkExceptionRules.append(rule)
+            else:
+                self.__networkBlockRules.append(rule)
+    
+    def canEditRules(self):
+        """
+        Public method to check, if rules can be edited.
+        
+        @return flag indicating rules may be edited (boolean)
+        """
+        return self.__custom
+    
+    def canBeRemoved(self):
+        """
+        Public method to check, if the subscription can be removed.
+        
+        @return flag indicating removal is allowed (boolean)
+        """
+        return not self.__custom and not self.__defaultSubscription
+    
+    def setRuleEnabled(self, offset, enabled):
+        """
+        Public method to enable a specific rule.
+        
+        @param offset offset of the rule (integer)
+        @param enabled new enabled state (boolean)
+        @return reference to the changed rule (AdBlockRule)
+        """
+        if offset >= len(self.__rules):
+            return None
+        
+        rule = self.__rules[offset]
+        rule.setEnabled(enabled)
+        if rule.isCSSRule():
+            from WebBrowser.WebBrowserWindow import WebBrowserWindow
+            self.__populateCache()
+            WebBrowserWindow.mainWindow().reloadUserStyleSheet()
+        
+        return rule
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/AdBlock/AdBlockTreeWidget.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,262 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a tree widget for the AdBlock configuration dialog.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import Qt
+from PyQt5.QtGui import QFont, QColor
+from PyQt5.QtWidgets import QAbstractItemView, QTreeWidgetItem, QInputDialog, \
+    QLineEdit, QMenu, QApplication
+
+from E5Gui.E5TreeWidget import E5TreeWidget
+
+
+class AdBlockTreeWidget(E5TreeWidget):
+    """
+    Class implementing a tree widget for the AdBlock configuration dialog.
+    """
+    def __init__(self, subscription, parent=None):
+        """
+        Constructor
+        
+        @param subscription reference to the subscription (AdBlockSubscription)
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(AdBlockTreeWidget, self).__init__(parent)
+        
+        self.__subscription = subscription
+        self.__topItem = None
+        self.__ruleToBeSelected = ""
+        self.__itemChangingBlock = False
+        
+        self.setContextMenuPolicy(Qt.CustomContextMenu)
+        self.setDefaultItemShowMode(E5TreeWidget.ItemsExpanded)
+        self.setHeaderHidden(True)
+        self.setAlternatingRowColors(True)
+        
+        self.customContextMenuRequested.connect(self.__contextMenuRequested)
+        self.itemChanged.connect(self.__itemChanged)
+        self.__subscription.changed.connect(self.__subscriptionChanged)
+        self.__subscription.rulesChanged.connect(self.__subscriptionChanged)
+    
+    def subscription(self):
+        """
+        Public method to get a reference to the subscription.
+        
+        @return reference to the subscription (AdBlockSubscription)
+        """
+        return self.__subscription
+    
+    def showRule(self, rule):
+        """
+        Public method to highlight the given rule.
+        
+        @param rule AdBlock rule to be shown (AdBlockRule)
+        """
+        if rule:
+            self.__ruleToBeSelected = rule.filter()
+        if not self.__topItem:
+            return
+        if self.__ruleToBeSelected:
+            items = self.findItems(self.__ruleToBeSelected, Qt.MatchRecursive)
+            if items:
+                item = items[0]
+                self.setCurrentItem(item)
+                self.scrollToItem(item, QAbstractItemView.PositionAtCenter)
+            
+            self.__ruleToBeSelected = ""
+    
+    def refresh(self):
+        """
+        Public method to refresh the tree.
+        """
+        QApplication.setOverrideCursor(Qt.WaitCursor)
+        self.__itemChangingBlock = True
+        self.clear()
+        
+        boldFont = QFont()
+        boldFont.setBold(True)
+        
+        self.__topItem = QTreeWidgetItem(self)
+        self.__topItem.setText(0, self.__subscription.title())
+        self.__topItem.setFont(0, boldFont)
+        self.addTopLevelItem(self.__topItem)
+        
+        allRules = self.__subscription.allRules()
+        
+        index = 0
+        for rule in allRules:
+            item = QTreeWidgetItem(self.__topItem)
+            item.setText(0, rule.filter())
+            item.setData(0, Qt.UserRole, index)
+            if self.__subscription.canEditRules():
+                item.setFlags(item.flags() | Qt.ItemIsEditable)
+            self.__adjustItemFeatures(item, rule)
+            index += 1
+        
+        self.expandAll()
+        self.showRule(None)
+        self.__itemChangingBlock = False
+        QApplication.restoreOverrideCursor()
+        QApplication.processEvents()
+    
+    def addRule(self, filter=""):
+        """
+        Public slot to add a new rule.
+        
+        @param filter filter to be added (string)
+        """
+        if not self.__subscription.canEditRules():
+            return
+        
+        if not filter:
+            filter, ok = QInputDialog.getText(
+                self,
+                self.tr("Add Custom Rule"),
+                self.tr("Write your rule here:"),
+                QLineEdit.Normal)
+            if not ok or filter == "":
+                return
+        
+        from .AdBlockRule import AdBlockRule
+        rule = AdBlockRule(filter, self.__subscription)
+        self.__subscription.addRule(rule)
+    
+    def removeRule(self):
+        """
+        Public slot to remove the current rule.
+        """
+        item = self.currentItem()
+        if item is None or \
+           not self.__subscription.canEditRules() or \
+           item == self.__topItem:
+            return
+        
+        offset = item.data(0, Qt.UserRole)
+        self.__subscription.removeRule(offset)
+        self.deleteItem(item)
+    
+    def __contextMenuRequested(self, pos):
+        """
+        Private slot to show the context menu.
+        
+        @param pos position for the menu (QPoint)
+        """
+        if not self.__subscription.canEditRules():
+            return
+        
+        item = self.itemAt(pos)
+        if item is None:
+            return
+        
+        menu = QMenu()
+        menu.addAction(self.tr("Add Rule"), self.addRule)
+        menu.addSeparator()
+        act = menu.addAction(self.tr("Remove Rule"), self.removeRule)
+        if item.parent() is None:
+            act.setDisabled(True)
+        
+        menu.exec_(self.viewport().mapToGlobal(pos))
+    
+    def __itemChanged(self, itm):
+        """
+        Private slot to handle the change of an item.
+        
+        @param itm changed item (QTreeWidgetItem)
+        """
+        if itm is None or self.__itemChangingBlock:
+            return
+        
+        self.__itemChangingBlock = True
+        
+        offset = itm.data(0, Qt.UserRole)
+        oldRule = self.__subscription.rule(offset)
+        
+        if itm.checkState(0) == Qt.Unchecked and oldRule.isEnabled():
+            # Disable rule
+            rule = self.__subscription.setRuleEnabled(offset, False)
+            self.__adjustItemFeatures(itm, rule)
+        elif itm.checkState(0) == Qt.Checked and not oldRule.isEnabled():
+            # Enable rule
+            rule = self.__subscription.setRuleEnabled(offset, True)
+            self.__adjustItemFeatures(itm, rule)
+        elif self.__subscription.canEditRules():
+            from .AdBlockRule import AdBlockRule
+            # Custom rule has been changed
+            rule = self.__subscription.replaceRule(
+                AdBlockRule(itm.text(0), self.__subscription), offset)
+            self.__adjustItemFeatures(itm, rule)
+        
+        self.__itemChangingBlock = False
+    
+    def __copyFilter(self):
+        """
+        Private slot to copy the current filter to the clipboard.
+        """
+        item = self.currentItem()
+        if item is not None:
+            QApplication.clipboard().setText(item.text(0))
+    
+    def __subscriptionChanged(self):
+        """
+        Private slot handling a subscription change.
+        """
+        self.refresh()
+        
+        self.__itemChangingBlock = True
+        self.__topItem.setText(
+            0, self.tr("{0} (recently updated)").format(
+                self.__subscription.title()))
+        self.__itemChangingBlock = False
+    
+    def __adjustItemFeatures(self, itm, rule):
+        """
+        Private method to adjust an item.
+        
+        @param itm item to be adjusted (QTreeWidgetItem)
+        @param rule rule for the adjustment (AdBlockRule)
+        """
+        if not rule.isEnabled():
+            font = QFont()
+            font.setItalic(True)
+            itm.setForeground(0, QColor(Qt.gray))
+            
+            if not rule.isComment() and not rule.isHeader():
+                itm.setFlags(itm.flags() | Qt.ItemIsUserCheckable)
+                itm.setCheckState(0, Qt.Unchecked)
+                itm.setFont(0, font)
+            
+            return
+        
+        itm.setFlags(itm.flags() | Qt.ItemIsUserCheckable)
+        itm.setCheckState(0, Qt.Checked)
+        
+        if rule.isCSSRule():
+            itm.setForeground(0, QColor(Qt.darkBlue))
+            itm.setFont(0, QFont())
+        elif rule.isException():
+            itm.setForeground(0, QColor(Qt.darkGreen))
+            itm.setFont(0, QFont())
+        else:
+            itm.setForeground(0, QColor())
+            itm.setFont(0, QFont())
+    
+    def keyPressEvent(self, evt):
+        """
+        Protected method handling key presses.
+        
+        @param evt key press event (QKeyEvent)
+        """
+        if evt.key() == Qt.Key_C and \
+           evt.modifiers() & Qt.ControlModifier:
+            self.__copyFilter()
+        elif evt.key() == Qt.Key_Delete:
+            self.removeRule()
+        else:
+            super(AdBlockTreeWidget, self).keyPressEvent(evt)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/AdBlock/AdBlockUrlInterceptor.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,39 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing an URL interceptor base class.
+"""
+
+from __future__ import unicode_literals
+
+from ..Network.UrlInterceptor import UrlInterceptor
+
+class AdBlockUrlInterceptor(UrlInterceptor):
+    """
+    Class implementing an URL interceptor for AdBlock.
+    """
+    def __init__(self, manager, parent=None):
+        """
+        Constructor
+        
+        @param manager reference to the AdBlock manager
+        @type AdBlockManager
+        @param parent referemce to the parent object
+        @type QObject
+        """
+        super(AdBlockUrlInterceptor, self).__init__(parent)
+        
+        self.__manager = manager
+    
+    def interceptRequest(self, info):
+        """
+        Public method to intercept a request.
+        
+        @param info request info object
+        @type QWebEngineUrlRequestInfo
+        """
+        if self.__manager.block(info):
+            info.block(True)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/AdBlock/__init__.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Package implementing the advertisements blocker functionality.
+"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Bookmarks/AddBookmarkDialog.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,248 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog to add a bookmark or a bookmark folder.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import QModelIndex, QSortFilterProxyModel
+from PyQt5.QtWidgets import QDialog, QTreeView
+
+from .Ui_AddBookmarkDialog import Ui_AddBookmarkDialog
+
+
+class AddBookmarkProxyModel(QSortFilterProxyModel):
+    """
+    Class implementing a proxy model used by the AddBookmarkDialog dialog.
+    """
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object (QObject)
+        """
+        super(AddBookmarkProxyModel, self).__init__(parent)
+    
+    def columnCount(self, parent):
+        """
+        Public method to return the number of columns.
+        
+        @param parent index of the parent (QModelIndex)
+        @return number of columns (integer)
+        """
+        return min(1, QSortFilterProxyModel.columnCount(self, parent))
+    
+    def filterAcceptsRow(self, sourceRow, sourceParent):
+        """
+        Public method to determine, if the row is acceptable.
+        
+        @param sourceRow row number in the source model (integer)
+        @param sourceParent index of the source item (QModelIndex)
+        @return flag indicating acceptance (boolean)
+        """
+        idx = self.sourceModel().index(sourceRow, 0, sourceParent)
+        return self.sourceModel().hasChildren(idx)
+    
+    def filterAcceptsColumn(self, sourceColumn, sourceParent):
+        """
+        Public method to determine, if the column is acceptable.
+        
+        @param sourceColumn column number in the source model (integer)
+        @param sourceParent index of the source item (QModelIndex)
+        @return flag indicating acceptance (boolean)
+        """
+        return sourceColumn == 0
+    
+    def hasChildren(self, parent=QModelIndex()):
+        """
+        Public method to check, if a parent node has some children.
+        
+        @param parent index of the parent node (QModelIndex)
+        @return flag indicating the presence of children (boolean)
+        """
+        sindex = self.mapToSource(parent)
+        return self.sourceModel().hasChildren(sindex)
+
+
+class AddBookmarkDialog(QDialog, Ui_AddBookmarkDialog):
+    """
+    Class implementing a dialog to add a bookmark or a bookmark folder.
+    """
+    def __init__(self, parent=None, bookmarksManager=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (QWidget)
+        @param bookmarksManager reference to the bookmarks manager
+            object (BookmarksManager)
+        """
+        super(AddBookmarkDialog, self).__init__(parent)
+        self.setupUi(self)
+        
+        self.__bookmarksManager = bookmarksManager
+        self.__addedNode = None
+        self.__addFolder = False
+        
+        if self.__bookmarksManager is None:
+            import WebBrowser.WebBrowserWindow
+            self.__bookmarksManager = \
+                WebBrowser.WebBrowserWindow.WebBrowserWindow.bookmarksManager()
+        
+        self.__proxyModel = AddBookmarkProxyModel(self)
+        model = self.__bookmarksManager.bookmarksModel()
+        self.__proxyModel.setSourceModel(model)
+        
+        self.__treeView = QTreeView(self)
+        self.__treeView.setModel(self.__proxyModel)
+        self.__treeView.expandAll()
+        self.__treeView.header().setStretchLastSection(True)
+        self.__treeView.header().hide()
+        self.__treeView.setItemsExpandable(False)
+        self.__treeView.setRootIsDecorated(False)
+        self.__treeView.setIndentation(10)
+        self.__treeView.show()
+        
+        self.locationCombo.setModel(self.__proxyModel)
+        self.locationCombo.setView(self.__treeView)
+        
+        self.addressEdit.setInactiveText(self.tr("Url"))
+        self.nameEdit.setInactiveText(self.tr("Title"))
+        
+        self.resize(self.sizeHint())
+    
+    def setUrl(self, url):
+        """
+        Public slot to set the URL of the new bookmark.
+        
+        @param url URL of the bookmark (string)
+        """
+        self.addressEdit.setText(url)
+        self.resize(self.sizeHint())
+    
+    def url(self):
+        """
+        Public method to get the URL of the bookmark.
+        
+        @return URL of the bookmark (string)
+        """
+        return self.addressEdit.text()
+    
+    def setTitle(self, title):
+        """
+        Public method to set the title of the new bookmark.
+        
+        @param title title of the bookmark (string)
+        """
+        self.nameEdit.setText(title)
+    
+    def title(self):
+        """
+        Public method to get the title of the bookmark.
+        
+        @return title of the bookmark (string)
+        """
+        return self.nameEdit.text()
+    
+    def setDescription(self, description):
+        """
+        Public method to set the description of the new bookmark.
+        
+        @param description description of the bookamrk (string)
+        """
+        self.descriptionEdit.setPlainText(description)
+    
+    def description(self):
+        """
+        Public method to get the description of the bookmark.
+        
+        @return description of the bookamrk (string)
+        """
+        return self.descriptionEdit.toPlainText()
+    
+    def setCurrentIndex(self, idx):
+        """
+        Public method to set the current index.
+        
+        @param idx current index to be set (QModelIndex)
+        """
+        proxyIndex = self.__proxyModel.mapFromSource(idx)
+        self.__treeView.setCurrentIndex(proxyIndex)
+        self.locationCombo.setCurrentIndex(proxyIndex.row())
+    
+    def currentIndex(self):
+        """
+        Public method to get the current index.
+        
+        @return current index (QModelIndex)
+        """
+        idx = self.locationCombo.view().currentIndex()
+        idx = self.__proxyModel.mapToSource(idx)
+        return idx
+    
+    def setFolder(self, folder):
+        """
+        Public method to set the dialog to "Add Folder" mode.
+        
+        @param folder flag indicating "Add Folder" mode (boolean)
+        """
+        self.__addFolder = folder
+        
+        if folder:
+            self.setWindowTitle(self.tr("Add Folder"))
+            self.addressEdit.setVisible(False)
+        else:
+            self.setWindowTitle(self.tr("Add Bookmark"))
+            self.addressEdit.setVisible(True)
+        
+        self.resize(self.sizeHint())
+    
+    def isFolder(self):
+        """
+        Public method to test, if the dialog is in "Add Folder" mode.
+        
+        @return flag indicating "Add Folder" mode (boolean)
+        """
+        return self.__addFolder
+    
+    def addedNode(self):
+        """
+        Public method to get a reference to the added bookmark node.
+        
+        @return reference to the added bookmark node (BookmarkNode)
+        """
+        return self.__addedNode
+    
+    def accept(self):
+        """
+        Public slot handling the acceptance of the dialog.
+        """
+        if (not self.__addFolder and not self.addressEdit.text()) or \
+           not self.nameEdit.text():
+            super(AddBookmarkDialog, self).accept()
+            return
+        
+        from .BookmarkNode import BookmarkNode
+        
+        idx = self.currentIndex()
+        if not idx.isValid():
+            idx = self.__bookmarksManager.bookmarksModel().index(0, 0)
+        parent = self.__bookmarksManager.bookmarksModel().node(idx)
+        
+        if self.__addFolder:
+            type_ = BookmarkNode.Folder
+        else:
+            type_ = BookmarkNode.Bookmark
+        bookmark = BookmarkNode(type_)
+        bookmark.title = self.nameEdit.text()
+        if not self.__addFolder:
+            bookmark.url = self.addressEdit.text()
+        bookmark.desc = self.descriptionEdit.toPlainText()
+        
+        self.__bookmarksManager.addBookmark(parent, bookmark)
+        self.__addedNode = bookmark
+        
+        super(AddBookmarkDialog, self).accept()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Bookmarks/AddBookmarkDialog.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,148 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>AddBookmarkDialog</class>
+ <widget class="QDialog" name="AddBookmarkDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>500</width>
+    <height>230</height>
+   </rect>
+  </property>
+  <property name="minimumSize">
+   <size>
+    <width>500</width>
+    <height>0</height>
+   </size>
+  </property>
+  <property name="maximumSize">
+   <size>
+    <width>500</width>
+    <height>250</height>
+   </size>
+  </property>
+  <property name="windowTitle">
+   <string>Add Bookmark</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QGridLayout" name="gridLayout">
+   <item row="0" column="0">
+    <widget class="QLabel" name="label_2">
+     <property name="text">
+      <string>Name:</string>
+     </property>
+    </widget>
+   </item>
+   <item row="0" column="1">
+    <widget class="E5LineEdit" name="nameEdit">
+     <property name="toolTip">
+      <string>Enter the name</string>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="0">
+    <widget class="QLabel" name="label_3">
+     <property name="text">
+      <string>Address:</string>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="1">
+    <widget class="E5LineEdit" name="addressEdit">
+     <property name="toolTip">
+      <string>Enter the address</string>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="0">
+    <widget class="QLabel" name="label">
+     <property name="text">
+      <string>Description:</string>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="1">
+    <widget class="QPlainTextEdit" name="descriptionEdit">
+     <property name="toolTip">
+      <string>Enter a description</string>
+     </property>
+    </widget>
+   </item>
+   <item row="3" column="0">
+    <widget class="QLabel" name="label_4">
+     <property name="text">
+      <string>Folder:</string>
+     </property>
+    </widget>
+   </item>
+   <item row="3" column="1">
+    <widget class="QComboBox" name="locationCombo"/>
+   </item>
+   <item row="4" column="0" colspan="2">
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>E5LineEdit</class>
+   <extends>QLineEdit</extends>
+   <header>E5Gui/E5LineEdit.h</header>
+  </customwidget>
+ </customwidgets>
+ <tabstops>
+  <tabstop>nameEdit</tabstop>
+  <tabstop>addressEdit</tabstop>
+  <tabstop>descriptionEdit</tabstop>
+  <tabstop>locationCombo</tabstop>
+  <tabstop>buttonBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>AddBookmarkDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>AddBookmarkDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Bookmarks/BookmarkNode.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,111 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the bookmark node.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import QDateTime
+
+
+class BookmarkNode(object):
+    """
+    Class implementing the bookmark node type.
+    """
+    # possible bookmark node types
+    Root = 0
+    Folder = 1
+    Bookmark = 2
+    Separator = 3
+    
+    # possible timestamp types
+    TsAdded = 0
+    TsModified = 1
+    TsVisited = 2
+    
+    def __init__(self, type_=Root, parent=None):
+        """
+        Constructor
+        
+        @param type_ type of the bookmark node (BookmarkNode.Type)
+        @param parent reference to the parent node (BookmarkNode)
+        """
+        self.url = ""
+        self.title = ""
+        self.desc = ""
+        self.expanded = False
+        self.added = QDateTime()
+        self.modified = QDateTime()
+        self.visited = QDateTime()
+        
+        self._children = []
+        self._parent = parent
+        self._type = type_
+        
+        if parent is not None:
+            parent.add(self)
+    
+    def type(self):
+        """
+        Public method to get the bookmark's type.
+        
+        @return bookmark type (BookmarkNode.Type)
+        """
+        return self._type
+    
+    def setType(self, type_):
+        """
+        Public method to set the bookmark's type.
+        
+        @param type_ type of the bookmark node (BookmarkNode.Type)
+        """
+        self._type = type_
+    
+    def children(self):
+        """
+        Public method to get the list of child nodes.
+        
+        @return list of all child nodes (list of BookmarkNode)
+        """
+        return self._children[:]
+    
+    def parent(self):
+        """
+        Public method to get a reference to the parent node.
+        
+        @return reference to the parent node (BookmarkNode)
+        """
+        return self._parent
+    
+    def add(self, child, offset=-1):
+        """
+        Public method to add/insert a child node.
+        
+        @param child reference to the node to add (BookmarkNode)
+        @param offset position where to insert child (integer, -1 = append)
+        """
+        if child._type == BookmarkNode.Root:
+            return
+        
+        if child._parent is not None:
+            child._parent.remove(child)
+        
+        child._parent = self
+        if offset == -1:
+            self._children.append(child)
+        else:
+            self._children.insert(offset, child)
+    
+    def remove(self, child):
+        """
+        Public method to remove a child node.
+        
+        @param child reference to the child node (BookmarkNode)
+        """
+        child._parent = None
+        if child in self._children:
+            self._children.remove(child)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Bookmarks/BookmarkPropertiesDialog.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,68 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog to show and edit bookmark properties.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtWidgets import QDialog
+
+from .Ui_BookmarkPropertiesDialog import Ui_BookmarkPropertiesDialog
+
+
+class BookmarkPropertiesDialog(QDialog, Ui_BookmarkPropertiesDialog):
+    """
+    Class implementing a dialog to show and edit bookmark properties.
+    """
+    def __init__(self, node, parent=None):
+        """
+        Constructor
+        
+        @param node reference to the bookmark (BookmarkNode)
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(BookmarkPropertiesDialog, self).__init__(parent)
+        self.setupUi(self)
+        
+        from .BookmarkNode import BookmarkNode
+        self.__node = node
+        if self.__node.type() == BookmarkNode.Folder:
+            self.addressLabel.hide()
+            self.addressEdit.hide()
+        
+        self.nameEdit.setText(self.__node.title)
+        self.descriptionEdit.setPlainText(self.__node.desc)
+        self.addressEdit.setText(self.__node.url)
+    
+    def accept(self):
+        """
+        Public slot handling the acceptance of the dialog.
+        """
+        from .BookmarkNode import BookmarkNode
+        
+        if (self.__node.type() == BookmarkNode.Bookmark and
+            not self.addressEdit.text()) or \
+           not self.nameEdit.text():
+            super(BookmarkPropertiesDialog, self).accept()
+            return
+        
+        import WebBrowser.WebBrowserWindow
+        bookmarksManager = WebBrowser.WebBrowserWindow.WebBrowserWindow\
+            .bookmarksManager()
+        title = self.nameEdit.text()
+        if title != self.__node.title:
+            bookmarksManager.setTitle(self.__node, title)
+        if self.__node.type() == BookmarkNode.Bookmark:
+            url = self.addressEdit.text()
+            if url != self.__node.url:
+                bookmarksManager.setUrl(self.__node, url)
+        description = self.descriptionEdit.toPlainText()
+        if description != self.__node.desc:
+            self.__node.desc = description
+            bookmarksManager.setNodeChanged(self.__node)
+        
+        super(BookmarkPropertiesDialog, self).accept()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Bookmarks/BookmarkPropertiesDialog.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,137 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>BookmarkPropertiesDialog</class>
+ <widget class="QDialog" name="BookmarkPropertiesDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>500</width>
+    <height>221</height>
+   </rect>
+  </property>
+  <property name="minimumSize">
+   <size>
+    <width>500</width>
+    <height>0</height>
+   </size>
+  </property>
+  <property name="maximumSize">
+   <size>
+    <width>500</width>
+    <height>250</height>
+   </size>
+  </property>
+  <property name="windowTitle">
+   <string>Bookmark Properties</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QGridLayout" name="gridLayout">
+   <item row="0" column="0">
+    <widget class="QLabel" name="label_2">
+     <property name="text">
+      <string>Name:</string>
+     </property>
+    </widget>
+   </item>
+   <item row="0" column="1">
+    <widget class="E5LineEdit" name="nameEdit">
+     <property name="toolTip">
+      <string>Enter the name</string>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="0">
+    <widget class="QLabel" name="addressLabel">
+     <property name="text">
+      <string>Address:</string>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="1">
+    <widget class="E5LineEdit" name="addressEdit">
+     <property name="toolTip">
+      <string>Enter the address</string>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="0">
+    <widget class="QLabel" name="label">
+     <property name="text">
+      <string>Description:</string>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="1">
+    <widget class="QPlainTextEdit" name="descriptionEdit">
+     <property name="toolTip">
+      <string>Enter a description</string>
+     </property>
+    </widget>
+   </item>
+   <item row="3" column="0" colspan="2">
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>E5LineEdit</class>
+   <extends>QLineEdit</extends>
+   <header>E5Gui/E5LineEdit.h</header>
+  </customwidget>
+ </customwidgets>
+ <tabstops>
+  <tabstop>nameEdit</tabstop>
+  <tabstop>addressEdit</tabstop>
+  <tabstop>descriptionEdit</tabstop>
+  <tabstop>buttonBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>BookmarkPropertiesDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>BookmarkPropertiesDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Bookmarks/BookmarksDialog.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,266 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog to manage bookmarks.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSignal, Qt, QUrl, QModelIndex
+from PyQt5.QtGui import QFontMetrics, QCursor
+from PyQt5.QtWidgets import QDialog, QMenu, QApplication
+
+from E5Gui.E5TreeSortFilterProxyModel import E5TreeSortFilterProxyModel
+
+from .Ui_BookmarksDialog import Ui_BookmarksDialog
+
+
+class BookmarksDialog(QDialog, Ui_BookmarksDialog):
+    """
+    Class implementing a dialog to manage bookmarks.
+    
+    @signal openUrl(QUrl, str) emitted to open a URL in the current tab
+    @signal newUrl(QUrl, str) emitted to open a URL in a new tab
+    """
+    openUrl = pyqtSignal(QUrl, str)
+    newUrl = pyqtSignal(QUrl, str)
+    
+    def __init__(self, parent=None, manager=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (QWidget
+        @param manager reference to the bookmarks manager object
+            (BookmarksManager)
+        """
+        super(BookmarksDialog, self).__init__(parent)
+        self.setupUi(self)
+        self.setWindowFlags(Qt.Window)
+        
+        self.__bookmarksManager = manager
+        if self.__bookmarksManager is None:
+            import WebBrowser.WebBrowserWindow
+            self.__bookmarksManager = WebBrowser.WebBrowserWindow\
+                .WebBrowserWindow.bookmarksManager()
+        
+        self.__bookmarksModel = self.__bookmarksManager.bookmarksModel()
+        self.__proxyModel = E5TreeSortFilterProxyModel(self)
+        self.__proxyModel.setFilterKeyColumn(-1)
+        self.__proxyModel.setSourceModel(self.__bookmarksModel)
+        
+        self.searchEdit.textChanged.connect(
+            self.__proxyModel.setFilterFixedString)
+        
+        self.bookmarksTree.setModel(self.__proxyModel)
+        self.bookmarksTree.setExpanded(self.__proxyModel.index(0, 0), True)
+        fm = QFontMetrics(self.font())
+        header = fm.width("m") * 40
+        self.bookmarksTree.header().resizeSection(0, header)
+        self.bookmarksTree.header().setStretchLastSection(True)
+        self.bookmarksTree.setContextMenuPolicy(Qt.CustomContextMenu)
+        
+        self.bookmarksTree.activated.connect(self.__activated)
+        self.bookmarksTree.customContextMenuRequested.connect(
+            self.__customContextMenuRequested)
+        
+        self.removeButton.clicked.connect(
+            self.bookmarksTree.removeSelected)
+        self.addFolderButton.clicked.connect(self.__newFolder)
+        
+        self.__expandNodes(self.__bookmarksManager.bookmarks())
+    
+    def closeEvent(self, evt):
+        """
+        Protected method to handle the closing of the dialog.
+        
+        @param evt reference to the event object (QCloseEvent) (ignored)
+        """
+        self.__shutdown()
+    
+    def reject(self):
+        """
+        Public method called when the dialog is rejected.
+        """
+        self.__shutdown()
+        super(BookmarksDialog, self).reject()
+    
+    def __shutdown(self):
+        """
+        Private method to perform shutdown actions for the dialog.
+        """
+        if self.__saveExpandedNodes(self.bookmarksTree.rootIndex()):
+            self.__bookmarksManager.changeExpanded()
+    
+    def __saveExpandedNodes(self, parent):
+        """
+        Private method to save the child nodes of an expanded node.
+        
+        @param parent index of the parent node (QModelIndex)
+        @return flag indicating a change (boolean)
+        """
+        changed = False
+        for row in range(self.__proxyModel.rowCount(parent)):
+            child = self.__proxyModel.index(row, 0, parent)
+            sourceIndex = self.__proxyModel.mapToSource(child)
+            childNode = self.__bookmarksModel.node(sourceIndex)
+            wasExpanded = childNode.expanded
+            if self.bookmarksTree.isExpanded(child):
+                childNode.expanded = True
+                changed |= self.__saveExpandedNodes(child)
+            else:
+                childNode.expanded = False
+            changed |= (wasExpanded != childNode.expanded)
+        
+        return changed
+    
+    def __expandNodes(self, node):
+        """
+        Private method to expand all child nodes of a node.
+        
+        @param node reference to the bookmark node to expand (BookmarkNode)
+        """
+        for childNode in node.children():
+            if childNode.expanded:
+                idx = self.__bookmarksModel.nodeIndex(childNode)
+                idx = self.__proxyModel.mapFromSource(idx)
+                self.bookmarksTree.setExpanded(idx, True)
+                self.__expandNodes(childNode)
+    
+    def __customContextMenuRequested(self, pos):
+        """
+        Private slot to handle the context menu request for the bookmarks tree.
+        
+        @param pos position the context menu was requested (QPoint)
+        """
+        from .BookmarkNode import BookmarkNode
+        
+        menu = QMenu()
+        idx = self.bookmarksTree.indexAt(pos)
+        idx = idx.sibling(idx.row(), 0)
+        sourceIndex = self.__proxyModel.mapToSource(idx)
+        node = self.__bookmarksModel.node(sourceIndex)
+        if idx.isValid() and node.type() != BookmarkNode.Folder:
+            menu.addAction(
+                self.tr("&Open"), self.__openBookmarkInCurrentTab)
+            menu.addAction(
+                self.tr("Open in New &Tab"), self.__openBookmarkInNewTab)
+            menu.addSeparator()
+        act = menu.addAction(self.tr("Edit &Name"), self.__editName)
+        act.setEnabled(idx.flags() & Qt.ItemIsEditable)
+        if idx.isValid() and node.type() != BookmarkNode.Folder:
+            menu.addAction(self.tr("Edit &Address"), self.__editAddress)
+        menu.addSeparator()
+        act = menu.addAction(
+            self.tr("&Delete"), self.bookmarksTree.removeSelected)
+        act.setEnabled(idx.flags() & Qt.ItemIsDragEnabled)
+        menu.addSeparator()
+        act = menu.addAction(self.tr("&Properties..."), self.__edit)
+        act.setEnabled(idx.flags() & Qt.ItemIsEditable)
+        menu.exec_(QCursor.pos())
+    
+    def __activated(self, idx):
+        """
+        Private slot to handle the activation of an entry.
+        
+        @param idx reference to the entry index (QModelIndex)
+        """
+        self.__openBookmark(
+            QApplication.keyboardModifiers() & Qt.ControlModifier)
+        
+    def __openBookmarkInCurrentTab(self):
+        """
+        Private slot to open a bookmark in the current browser tab.
+        """
+        self.__openBookmark(False)
+    
+    def __openBookmarkInNewTab(self):
+        """
+        Private slot to open a bookmark in a new browser tab.
+        """
+        self.__openBookmark(True)
+    
+    def __openBookmark(self, newTab):
+        """
+        Private method to open a bookmark.
+        
+        @param newTab flag indicating to open the bookmark in a new tab
+            (boolean)
+        """
+        from .BookmarkNode import BookmarkNode
+        from .BookmarksModel import BookmarksModel
+        
+        idx = self.bookmarksTree.currentIndex()
+        sourceIndex = self.__proxyModel.mapToSource(idx)
+        node = self.__bookmarksModel.node(sourceIndex)
+        if not idx.parent().isValid() or \
+           node is None or \
+           node.type() == BookmarkNode.Folder:
+            return
+        if newTab:
+            self.newUrl.emit(
+                idx.sibling(idx.row(), 1).data(BookmarksModel.UrlRole),
+                idx.sibling(idx.row(), 0).data(Qt.DisplayRole))
+        else:
+            self.openUrl.emit(
+                idx.sibling(idx.row(), 1).data(BookmarksModel.UrlRole),
+                idx.sibling(idx.row(), 0).data(Qt.DisplayRole))
+    
+    def __editName(self):
+        """
+        Private slot to edit the name part of a bookmark.
+        """
+        idx = self.bookmarksTree.currentIndex()
+        idx = idx.sibling(idx.row(), 0)
+        self.bookmarksTree.edit(idx)
+    
+    def __editAddress(self):
+        """
+        Private slot to edit the address part of a bookmark.
+        """
+        idx = self.bookmarksTree.currentIndex()
+        idx = idx.sibling(idx.row(), 1)
+        self.bookmarksTree.edit(idx)
+    
+    def __edit(self):
+        """
+        Private slot to edit a bookmarks properties.
+        """
+        from .BookmarkPropertiesDialog import BookmarkPropertiesDialog
+        
+        idx = self.bookmarksTree.currentIndex()
+        sourceIndex = self.__proxyModel.mapToSource(idx)
+        node = self.__bookmarksModel.node(sourceIndex)
+        dlg = BookmarkPropertiesDialog(node)
+        dlg.exec_()
+    
+    def __newFolder(self):
+        """
+        Private slot to add a new bookmarks folder.
+        """
+        from .BookmarkNode import BookmarkNode
+        
+        currentIndex = self.bookmarksTree.currentIndex()
+        idx = QModelIndex(currentIndex)
+        sourceIndex = self.__proxyModel.mapToSource(idx)
+        sourceNode = self.__bookmarksModel.node(sourceIndex)
+        row = -1    # append new folder as the last item per default
+        
+        if sourceNode is not None and \
+           sourceNode.type() != BookmarkNode.Folder:
+            # If the selected item is not a folder, add a new folder to the
+            # parent folder, but directly below the selected item.
+            idx = idx.parent()
+            row = currentIndex.row() + 1
+        
+        if not idx.isValid():
+            # Select bookmarks menu as default.
+            idx = self.__proxyModel.index(1, 0)
+        
+        idx = self.__proxyModel.mapToSource(idx)
+        parent = self.__bookmarksModel.node(idx)
+        node = BookmarkNode(BookmarkNode.Folder)
+        node.title = self.tr("New Folder")
+        self.__bookmarksManager.addBookmark(parent, node, row)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Bookmarks/BookmarksDialog.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,179 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>BookmarksDialog</class>
+ <widget class="QDialog" name="BookmarksDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>750</width>
+    <height>450</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Manage Bookmarks</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_2">
+     <item>
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <layout class="QHBoxLayout" name="horizontalLayout">
+       <property name="spacing">
+        <number>0</number>
+       </property>
+       <item>
+        <widget class="E5ClearableLineEdit" name="searchEdit">
+         <property name="toolTip">
+          <string>Enter search term for bookmarks</string>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="E5TreeView" name="bookmarksTree">
+     <property name="dragDropMode">
+      <enum>QAbstractItemView::InternalMove</enum>
+     </property>
+     <property name="alternatingRowColors">
+      <bool>true</bool>
+     </property>
+     <property name="selectionMode">
+      <enum>QAbstractItemView::ExtendedSelection</enum>
+     </property>
+     <property name="textElideMode">
+      <enum>Qt::ElideMiddle</enum>
+     </property>
+     <property name="uniformRowHeights">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_3">
+     <item>
+      <widget class="QPushButton" name="removeButton">
+       <property name="toolTip">
+        <string>Press to delete the selected entries</string>
+       </property>
+       <property name="text">
+        <string>&amp;Delete</string>
+       </property>
+       <property name="autoDefault">
+        <bool>false</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="addFolderButton">
+       <property name="toolTip">
+        <string>Press to add a new bookmarks folder</string>
+       </property>
+       <property name="text">
+        <string>Add &amp;Folder</string>
+       </property>
+       <property name="autoDefault">
+        <bool>false</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="spacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Close</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>E5ClearableLineEdit</class>
+   <extends>QLineEdit</extends>
+   <header>E5Gui/E5LineEdit.h</header>
+  </customwidget>
+  <customwidget>
+   <class>E5TreeView</class>
+   <extends>QTreeView</extends>
+   <header>E5Gui/E5TreeView.h</header>
+  </customwidget>
+ </customwidgets>
+ <tabstops>
+  <tabstop>searchEdit</tabstop>
+  <tabstop>bookmarksTree</tabstop>
+  <tabstop>removeButton</tabstop>
+  <tabstop>addFolderButton</tabstop>
+  <tabstop>buttonBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>BookmarksDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>252</x>
+     <y>445</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>BookmarksDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>320</x>
+     <y>445</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Bookmarks/BookmarksImportDialog.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,155 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog for importing bookmarks from other sources.
+"""
+
+from __future__ import unicode_literals
+
+import os
+
+from PyQt5.QtCore import pyqtSlot, Qt, QSize
+from PyQt5.QtWidgets import QDialog, QListWidgetItem
+
+from E5Gui import E5MessageBox
+from E5Gui.E5PathPicker import E5PathPickerModes
+
+from .Ui_BookmarksImportDialog import Ui_BookmarksImportDialog
+
+from . import BookmarksImporters
+
+import Globals
+
+
+class BookmarksImportDialog(QDialog, Ui_BookmarksImportDialog):
+    """
+    Class implementing a dialog for importing bookmarks from other sources.
+    """
+    SourcesListIdRole = Qt.UserRole
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(BookmarksImportDialog, self).__init__(parent)
+        self.setupUi(self)
+        
+        self.filePicker.setMode(E5PathPickerModes.OpenFileMode)
+        
+        self.sourcesList.setIconSize(QSize(48, 48))
+        for icon, displayText, idText in BookmarksImporters.getImporters():
+            itm = QListWidgetItem(icon, displayText, self.sourcesList)
+            itm.setData(self.SourcesListIdRole, idText)
+        
+        self.__currentPage = 0
+        self.__selectedSource = ""
+        self.__topLevelBookmarkNode = None
+        self.__sourceFile = ""
+        self.__sourceDir = ""
+        
+        self.pagesWidget.setCurrentIndex(self.__currentPage)
+        self.__enableNextButton()
+    
+    def __enableNextButton(self):
+        """
+        Private slot to set the enabled state of the next button.
+        """
+        if self.__currentPage == 0:
+            self.nextButton.setEnabled(
+                len(self.sourcesList.selectedItems()) == 1)
+        elif self.__currentPage == 1:
+            self.nextButton.setEnabled(self.filePicker.text() != "")
+    
+    @pyqtSlot()
+    def on_sourcesList_itemSelectionChanged(self):
+        """
+        Private slot to handle changes of the selection of the import source.
+        """
+        self.__enableNextButton()
+    
+    @pyqtSlot(str)
+    def on_filePicker_textChanged(self, txt):
+        """
+        Private slot handling changes of the file to import bookmarks form.
+        
+        @param txt text of the line edit (string)
+        """
+        self.__enableNextButton()
+    
+    @pyqtSlot()
+    def on_nextButton_clicked(self):
+        """
+        Private slot to switch to the next page.
+        """
+        if self.sourcesList.currentItem() is None:
+            return
+        
+        if self.__currentPage == 0:
+            self.__selectedSource = self.sourcesList.currentItem().data(
+                self.SourcesListIdRole)
+            (pixmap, sourceName, self.__sourceFile, info, prompt,
+             self.__sourceDir) = BookmarksImporters.getImporterInfo(
+                self.__selectedSource)
+            
+            self.iconLabel.setPixmap(pixmap)
+            self.importingFromLabel.setText(
+                self.tr("<b>Importing from {0}</b>").format(sourceName))
+            self.fileLabel1.setText(info)
+            self.fileLabel2.setText(prompt)
+            self.standardDirLabel.setText(
+                "<i>{0}</i>".format(self.__sourceDir))
+            
+            self.nextButton.setText(self.tr("Finish"))
+            
+            self.__currentPage += 1
+            self.pagesWidget.setCurrentIndex(self.__currentPage)
+            self.__enableNextButton()
+            
+            if self.__selectedSource == "ie":
+                self.filePicker.setMode(E5PathPickerModes.DirectoryMode)
+            else:
+                self.filePicker.setMode(E5PathPickerModes.OpenFileMode)
+                if Globals.isMacPlatform():
+                    filter = "*{0}".format(
+                        os.path.splitext(self.__sourceFile)[1])
+                else:
+                    filter = self.__sourceFile
+                self.filePicker.setFilters(filter)
+            self.filePicker.setDefaultDirectory(self.__sourceDir)
+        
+        elif self.__currentPage == 1:
+            if self.filePicker.text() == "":
+                return
+            
+            importer = BookmarksImporters.getImporter(self.__selectedSource)
+            importer.setPath(self.filePicker.text())
+            if importer.open():
+                self.__topLevelBookmarkNode = importer.importedBookmarks()
+            if importer.error():
+                E5MessageBox.critical(
+                    self,
+                    self.tr("Error importing bookmarks"),
+                    importer.errorString())
+                return
+            
+            self.accept()
+    
+    @pyqtSlot()
+    def on_cancelButton_clicked(self):
+        """
+        Private slot documentation goes here.
+        """
+        self.reject()
+    
+    def getImportedBookmarks(self):
+        """
+        Public method to get the imported bookmarks.
+        
+        @return top level bookmark (BookmarkNode)
+        """
+        return self.__topLevelBookmarkNode
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Bookmarks/BookmarksImportDialog.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,211 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>BookmarksImportDialog</class>
+ <widget class="QDialog" name="BookmarksImportDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>550</width>
+    <height>354</height>
+   </rect>
+  </property>
+  <property name="minimumSize">
+   <size>
+    <width>550</width>
+    <height>350</height>
+   </size>
+  </property>
+  <property name="windowTitle">
+   <string>Import Bookmarks</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout_3">
+   <item>
+    <widget class="QStackedWidget" name="pagesWidget">
+     <property name="currentIndex">
+      <number>0</number>
+     </property>
+     <widget class="QWidget" name="sourcePage">
+      <layout class="QVBoxLayout" name="verticalLayout">
+       <property name="leftMargin">
+        <number>0</number>
+       </property>
+       <property name="topMargin">
+        <number>0</number>
+       </property>
+       <property name="rightMargin">
+        <number>0</number>
+       </property>
+       <property name="bottomMargin">
+        <number>0</number>
+       </property>
+       <item>
+        <widget class="QLabel" name="label_2">
+         <property name="text">
+          <string>Choose source from which you want to import bookmarks:</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QListWidget" name="sourcesList">
+         <property name="toolTip">
+          <string>Choose the source to import from</string>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </widget>
+     <widget class="QWidget" name="filePage">
+      <layout class="QVBoxLayout" name="verticalLayout_2">
+       <item>
+        <layout class="QHBoxLayout" name="horizontalLayout_2">
+         <item>
+          <widget class="QLabel" name="iconLabel">
+           <property name="minimumSize">
+            <size>
+             <width>48</width>
+             <height>48</height>
+            </size>
+           </property>
+           <property name="maximumSize">
+            <size>
+             <width>48</width>
+             <height>48</height>
+            </size>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QLabel" name="importingFromLabel"/>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <spacer name="verticalSpacer">
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>20</width>
+           <height>44</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+       <item>
+        <widget class="QLabel" name="fileLabel1">
+         <property name="wordWrap">
+          <bool>true</bool>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QLabel" name="standardDirLabel">
+         <property name="textInteractionFlags">
+          <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QLabel" name="fileLabel2"/>
+       </item>
+       <item>
+        <spacer name="verticalSpacer_2">
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>20</width>
+           <height>44</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+       <item>
+        <widget class="E5PathPicker" name="filePicker" native="true">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="focusPolicy">
+          <enum>Qt::StrongFocus</enum>
+         </property>
+         <property name="toolTip">
+          <string>Enter the name of the bookmarks file or directory</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <spacer name="verticalSpacer_3">
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>20</width>
+           <height>44</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+      </layout>
+     </widget>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QPushButton" name="nextButton">
+       <property name="text">
+        <string>Next &gt;</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="cancelButton">
+       <property name="text">
+        <string>Cancel</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>E5PathPicker</class>
+   <extends>QWidget</extends>
+   <header>E5Gui/E5PathPicker.h</header>
+   <container>1</container>
+  </customwidget>
+ </customwidgets>
+ <tabstops>
+  <tabstop>sourcesList</tabstop>
+  <tabstop>nextButton</tabstop>
+  <tabstop>cancelButton</tabstop>
+  <tabstop>filePicker</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Bookmarks/BookmarksImporters/BookmarksImporter.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,80 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a base class for the bookmarks importers.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import QObject
+
+
+class BookmarksImporter(QObject):
+    """
+    Class implementing the base class for the bookmarks importers.
+    """
+    def __init__(self, id="", parent=None):
+        """
+        Constructor
+        
+        @param id source ID (string)
+        @param parent reference to the parent object (QObject)
+        """
+        super(BookmarksImporter, self).__init__(parent)
+        
+        self._path = ""
+        self._file = ""
+        self._error = False
+        self._errorString = ""
+        self._id = id
+    
+    def setPath(self, path):
+        """
+        Public method to set the path of the bookmarks file or directory.
+        
+        @param path bookmarks file or directory (string)
+        @exception NotImplementedError raised to indicate this method must
+            be implemented by a subclass
+        """
+        raise NotImplementedError
+    
+    def open(self):
+        """
+        Public method to open the bookmarks file.
+        
+        It must return a flag indicating success (boolean).
+        
+        @exception NotImplementedError raised to indicate this method must
+            be implemented by a subclass
+        """
+        raise NotImplementedError
+    
+    def importedBookmarks(self):
+        """
+        Public method to get the imported bookmarks.
+        
+        It must return the imported bookmarks (BookmarkNode).
+        
+        @exception NotImplementedError raised to indicate this method must
+            be implemented by a subclass
+        """
+        raise NotImplementedError
+    
+    def error(self):
+        """
+        Public method to check for an error.
+        
+        @return flag indicating an error (boolean)
+        """
+        return self._error
+    
+    def errorString(self):
+        """
+        Public method to get the error description.
+        
+        @return error description (string)
+        """
+        return self._errorString
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Bookmarks/BookmarksImporters/ChromeImporter.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,188 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing an importer for Chrome bookmarks.
+"""
+
+from __future__ import unicode_literals
+
+import os
+import json
+
+from PyQt5.QtCore import QCoreApplication, QDate, Qt
+
+from .BookmarksImporter import BookmarksImporter
+
+import UI.PixmapCache
+import Globals
+
+
+def getImporterInfo(id):
+    """
+    Module function to get information for the given source id.
+    
+    @param id id of the browser ("chrome" or "chromium")
+    @return tuple with an icon (QPixmap), readable name (string), name of
+        the default bookmarks file (string), an info text (string),
+        a prompt (string) and the default directory of the bookmarks file
+        (string)
+    @exception ValueError raised to indicate an invalid browser ID
+    """
+    if id == "chrome":
+        if Globals.isWindowsPlatform():
+            standardDir = os.path.expandvars(
+                "%USERPROFILE%\\AppData\\Local\\Google\\Chrome\\"
+                "User Data\\Default")
+        elif Globals.isMacPlatform():
+            standardDir = os.path.expanduser(
+                "~/Library/Application Support/Google/Chrome/Default")
+        else:
+            standardDir = os.path.expanduser("~/.config/google-chrome/Default")
+        return (
+            UI.PixmapCache.getPixmap("chrome.png"),
+            "Google Chrome",
+            "Bookmarks",
+            QCoreApplication.translate(
+                "ChromeImporter",
+                """Google Chrome stores its bookmarks in the"""
+                """ <b>Bookmarks</b> text file. This file is usually"""
+                """ located in"""),
+            QCoreApplication.translate(
+                "ChromeImporter",
+                """Please choose the file to begin importing bookmarks."""),
+            standardDir,
+        )
+    elif id == "chromium":
+        if Globals.isWindowsPlatform():
+            standardDir = os.path.expandvars(
+                "%USERPROFILE%\\AppData\\Local\\Google\\Chrome\\"
+                "User Data\\Default")
+        else:
+            standardDir = os.path.expanduser("~/.config/chromium/Default")
+        return (
+            UI.PixmapCache.getPixmap("chromium.png"),
+            "Chromium",
+            "Bookmarks",
+            QCoreApplication.translate(
+                "ChromeImporter",
+                """Chromium stores its bookmarks in the <b>Bookmarks</b>"""
+                """ text file. This file is usually located in"""),
+            QCoreApplication.translate(
+                "ChromeImporter",
+                """Please choose the file to begin importing bookmarks."""),
+            standardDir,
+        )
+    else:
+        raise ValueError("Unsupported browser ID given ({0}).".format(id))
+
+
+class ChromeImporter(BookmarksImporter):
+    """
+    Class implementing the Chrome bookmarks importer.
+    """
+    def __init__(self, id="", parent=None):
+        """
+        Constructor
+        
+        @param id source ID (string)
+        @param parent reference to the parent object (QObject)
+        """
+        super(ChromeImporter, self).__init__(id, parent)
+        
+        self.__fileName = ""
+    
+    def setPath(self, path):
+        """
+        Public method to set the path of the bookmarks file or directory.
+        
+        @param path bookmarks file or directory (string)
+        """
+        self.__fileName = path
+    
+    def open(self):
+        """
+        Public method to open the bookmarks file.
+        
+        @return flag indicating success (boolean)
+        """
+        if not os.path.exists(self.__fileName):
+            self._error = True
+            self._errorString = self.tr(
+                "File '{0}' does not exist.").format(self.__fileName)
+            return False
+        return True
+    
+    def importedBookmarks(self):
+        """
+        Public method to get the imported bookmarks.
+        
+        @return imported bookmarks (BookmarkNode)
+        """
+        try:
+            f = open(self.__fileName, "r", encoding="utf-8")
+            contents = json.load(f)
+            f.close()
+        except IOError as err:
+            self._error = True
+            self._errorString = self.tr(
+                "File '{0}' cannot be read.\nReason: {1}")\
+                .format(self.__fileName, str(err))
+            return None
+        
+        from ..BookmarkNode import BookmarkNode
+        importRootNode = BookmarkNode(BookmarkNode.Folder)
+        if contents["version"] == 1:
+            self.__processRoots(contents["roots"], importRootNode)
+        
+        if self._id == "chrome":
+            importRootNode.title = self.tr("Google Chrome Import")
+        elif self._id == "chromium":
+            importRootNode.title = self.tr("Chromium Import")
+        else:
+            importRootNode.title = self.tr("Imported {0}")\
+                .format(QDate.currentDate().toString(Qt.SystemLocaleShortDate))
+        return importRootNode
+    
+    def __processRoots(self, data, rootNode):
+        """
+        Private method to process the bookmark roots.
+        
+        @param data dictionary with the bookmarks data (dict)
+        @param rootNode node to add the bookmarks to (BookmarkNode)
+        """
+        for node in data.values():
+            if node["type"] == "folder":
+                self.__generateFolderNode(node, rootNode)
+            elif node["type"] == "url":
+                self.__generateUrlNode(node, rootNode)
+    
+    def __generateFolderNode(self, data, rootNode):
+        """
+        Private method to process a bookmarks folder.
+        
+        @param data dictionary with the bookmarks data (dict)
+        @param rootNode node to add the bookmarks to (BookmarkNode)
+        """
+        from ..BookmarkNode import BookmarkNode
+        folder = BookmarkNode(BookmarkNode.Folder, rootNode)
+        folder.title = data["name"].replace("&", "&&")
+        for node in data["children"]:
+            if node["type"] == "folder":
+                self.__generateFolderNode(node, folder)
+            elif node["type"] == "url":
+                self.__generateUrlNode(node, folder)
+    
+    def __generateUrlNode(self, data, rootNode):
+        """
+        Private method to process a bookmarks node.
+        
+        @param data dictionary with the bookmarks data (dict)
+        @param rootNode node to add the bookmarks to (BookmarkNode)
+        """
+        from ..BookmarkNode import BookmarkNode
+        bookmark = BookmarkNode(BookmarkNode.Bookmark, rootNode)
+        bookmark.url = data["url"]
+        bookmark.title = data["name"].replace("&", "&&")
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Bookmarks/BookmarksImporters/FirefoxImporter.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,181 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing an importer for Firefox bookmarks.
+"""
+
+from __future__ import unicode_literals
+
+import os
+import sqlite3
+
+from PyQt5.QtCore import QCoreApplication, QDate, Qt, QUrl
+
+from .BookmarksImporter import BookmarksImporter
+
+import UI.PixmapCache
+import Globals
+
+
+def getImporterInfo(id):
+    """
+    Module function to get information for the given source id.
+    
+    @param id id of the browser ("chrome" or "chromium")
+    @return tuple with an icon (QPixmap), readable name (string), name of
+        the default bookmarks file (string), an info text (string),
+        a prompt (string) and the default directory of the bookmarks file
+        (string)
+    @exception ValueError raised to indicate an invalid browser ID
+    """
+    if id == "firefox":
+        if Globals.isWindowsPlatform():
+            standardDir = os.path.expandvars(
+                "%APPDATA%\\Mozilla\\Firefox\\Profiles")
+        elif Globals.isMacPlatform():
+            standardDir = os.path.expanduser(
+                "~/Library/Application Support/Firefox/Profiles")
+        else:
+            standardDir = os.path.expanduser("~/.mozilla/firefox")
+        return (
+            UI.PixmapCache.getPixmap("chrome.png"),
+            "Mozilla Firefox",
+            "places.sqlite",
+            QCoreApplication.translate(
+                "FirefoxImporter",
+                """Mozilla Firefox stores its bookmarks in the"""
+                """ <b>places.sqlite</b> SQLite database. This file is"""
+                """ usually located in"""),
+            QCoreApplication.translate(
+                "FirefoxImporter",
+                """Please choose the file to begin importing bookmarks."""),
+            standardDir,
+        )
+    else:
+        raise ValueError("Unsupported browser ID given ({0}).".format(id))
+
+
+class FirefoxImporter(BookmarksImporter):
+    """
+    Class implementing the Chrome bookmarks importer.
+    """
+    def __init__(self, id="", parent=None):
+        """
+        Constructor
+        
+        @param id source ID (string)
+        @param parent reference to the parent object (QObject)
+        """
+        super(FirefoxImporter, self).__init__(id, parent)
+        
+        self.__fileName = ""
+        self.__db = None
+    
+    def setPath(self, path):
+        """
+        Public method to set the path of the bookmarks file or directory.
+        
+        @param path bookmarks file or directory (string)
+        """
+        self.__fileName = path
+    
+    def open(self):
+        """
+        Public method to open the bookmarks file.
+        
+        @return flag indicating success (boolean)
+        """
+        if not os.path.exists(self.__fileName):
+            self._error = True
+            self._errorString = self.tr("File '{0}' does not exist.")\
+                .format(self.__fileName)
+            return False
+        
+        try:
+            self.__db = sqlite3.connect(self.__fileName)
+        except sqlite3.DatabaseError as err:
+            self._error = True
+            self._errorString = self.tr(
+                "Unable to open database.\nReason: {0}").format(str(err))
+            return False
+        
+        return True
+    
+    def importedBookmarks(self):
+        """
+        Public method to get the imported bookmarks.
+        
+        @return imported bookmarks (BookmarkNode)
+        """
+        from ..BookmarkNode import BookmarkNode
+        importRootNode = BookmarkNode(BookmarkNode.Root)
+        
+        # step 1: build the hierarchy of bookmark folders
+        folders = {}
+        
+        try:
+            cursor = self.__db.cursor()
+            cursor.execute(
+                "SELECT id, parent, title FROM moz_bookmarks "
+                "WHERE type = 2 and title !=''")
+            for row in cursor:
+                id_ = row[0]
+                parent = row[1]
+                title = row[2]
+                if parent in folders:
+                    folder = BookmarkNode(BookmarkNode.Folder, folders[parent])
+                else:
+                    folder = BookmarkNode(BookmarkNode.Folder, importRootNode)
+                folder.title = title.replace("&", "&&")
+                folders[id_] = folder
+        except sqlite3.DatabaseError as err:
+            self._error = True
+            self._errorString = self.tr(
+                "Unable to open database.\nReason: {0}").format(str(err))
+            return None
+        
+        try:
+            cursor = self.__db.cursor()
+            cursor.execute(
+                "SELECT parent, title, fk, position FROM moz_bookmarks"
+                " WHERE type = 1 and title != '' ORDER BY position")
+            for row in cursor:
+                parent = row[0]
+                title = row[1]
+                placesId = row[2]
+                
+                cursor2 = self.__db.cursor()
+                cursor2.execute(
+                    "SELECT url FROM moz_places WHERE id = {0}"
+                    .format(placesId))
+                row2 = cursor2.fetchone()
+                if row2:
+                    url = QUrl(row2[0])
+                    if not title or url.isEmpty() or \
+                            url.scheme() in ["place", "about"]:
+                        continue
+                    
+                    if parent in folders:
+                        bookmark = BookmarkNode(BookmarkNode.Bookmark,
+                                                folders[parent])
+                    else:
+                        bookmark = BookmarkNode(BookmarkNode.Bookmark,
+                                                importRootNode)
+                    bookmark.url = url.toString()
+                    bookmark.title = title.replace("&", "&&")
+        except sqlite3.DatabaseError as err:
+            self._error = True
+            self._errorString = self.tr(
+                "Unable to open database.\nReason: {0}").format(str(err))
+            return None
+        
+        importRootNode.setType(BookmarkNode.Folder)
+        if self._id == "firefox":
+            importRootNode.title = self.tr("Mozilla Firefox Import")
+        else:
+            importRootNode.title = self.tr("Imported {0}")\
+                .format(QDate.currentDate().toString(Qt.SystemLocaleShortDate))
+        return importRootNode
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Bookmarks/BookmarksImporters/HtmlImporter.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,108 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing an importer for HTML bookmark files.
+"""
+
+from __future__ import unicode_literals
+
+import os
+
+from PyQt5.QtCore import QCoreApplication, QDate, Qt
+
+from .BookmarksImporter import BookmarksImporter
+
+import UI.PixmapCache
+
+
+def getImporterInfo(id):
+    """
+    Module function to get information for the given HTML source id.
+    
+    @param id id of the browser ("chrome" or "chromium")
+    @return tuple with an icon (QPixmap), readable name (string), name of
+        the default bookmarks file (string), an info text (string),
+        a prompt (string) and the default directory of the bookmarks file
+        (string)
+    @exception ValueError raised to indicate an invalid browser ID
+    """
+    if id == "html":
+        return (
+            UI.PixmapCache.getPixmap("html.png"),
+            "HTML Netscape Bookmarks",
+            QCoreApplication.translate(
+                "HtmlImporter",
+                "HTML Netscape Bookmarks") + " (*.htm *.html)",
+            QCoreApplication.translate(
+                "HtmlImporter",
+                """You can import bookmarks from any browser that supports"""
+                """ HTML exporting. This file has usually the extension"""
+                """ .htm or .html."""),
+            QCoreApplication.translate(
+                "HtmlImporter",
+                """Please choose the file to begin importing bookmarks."""),
+            "",
+        )
+    else:
+        raise ValueError("Unsupported browser ID given ({0}).".format(id))
+
+
+class HtmlImporter(BookmarksImporter):
+    """
+    Class implementing the HTML bookmarks importer.
+    """
+    def __init__(self, id="", parent=None):
+        """
+        Constructor
+        
+        @param id source ID (string)
+        @param parent reference to the parent object (QObject)
+        """
+        super(HtmlImporter, self).__init__(id, parent)
+        
+        self.__fileName = ""
+        self.__inFile = None
+    
+    def setPath(self, path):
+        """
+        Public method to set the path of the bookmarks file or directory.
+        
+        @param path bookmarks file or directory (string)
+        """
+        self.__fileName = path
+    
+    def open(self):
+        """
+        Public method to open the bookmarks file.
+        
+        @return flag indicating success (boolean)
+        """
+        if not os.path.exists(self.__fileName):
+            self._error = True
+            self._errorString = self.tr("File '{0}' does not exist.")\
+                .format(self.__fileName)
+            return False
+        return True
+    
+    def importedBookmarks(self):
+        """
+        Public method to get the imported bookmarks.
+        
+        @return imported bookmarks (BookmarkNode)
+        """
+        from ..BookmarkNode import BookmarkNode
+        from ..NsHtmlReader import NsHtmlReader
+        
+        reader = NsHtmlReader()
+        importRootNode = reader.read(self.__fileName)
+        
+        importRootNode.setType(BookmarkNode.Folder)
+        if self._id == "html":
+            importRootNode.title = self.tr("HTML Import")
+        else:
+            importRootNode.title = self.tr("Imported {0}")\
+                .format(QDate.currentDate().toString(Qt.SystemLocaleShortDate))
+        return importRootNode
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Bookmarks/BookmarksImporters/IExplorerImporter.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,150 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing an importer for Internet Explorer bookmarks.
+"""
+
+from __future__ import unicode_literals
+
+import os
+
+from PyQt5.QtCore import QCoreApplication, QDate, Qt
+
+from .BookmarksImporter import BookmarksImporter
+
+import UI.PixmapCache
+import Globals
+
+
+def getImporterInfo(id):
+    """
+    Module function to get information for the given source id.
+    
+    @param id id of the browser ("chrome" or "chromium")
+    @return tuple with an icon (QPixmap), readable name (string), name of
+        the default bookmarks file (string), an info text (string),
+        a prompt (string) and the default directory of the bookmarks file
+        (string)
+    @exception ValueError raised to indicate an invalid browser ID
+    """
+    if id == "ie":
+        if Globals.isWindowsPlatform():
+            standardDir = os.path.expandvars(
+                "%USERPROFILE%\\Favorites")
+        else:
+            standardDir = ""
+        return (
+            UI.PixmapCache.getPixmap("internet_explorer.png"),
+            "Internet Explorer",
+            "",
+            QCoreApplication.translate(
+                "IExplorerImporter",
+                """Internet Explorer stores its bookmarks in the"""
+                """ <b>Favorites</b> folder This folder is usually"""
+                """ located in"""),
+            QCoreApplication.translate(
+                "IExplorerImporter",
+                """Please choose the folder to begin importing bookmarks."""),
+            standardDir,
+        )
+    else:
+        raise ValueError("Unsupported browser ID given ({0}).".format(id))
+
+
+class IExplorerImporter(BookmarksImporter):
+    """
+    Class implementing the Chrome bookmarks importer.
+    """
+    def __init__(self, id="", parent=None):
+        """
+        Constructor
+        
+        @param id source ID (string)
+        @param parent reference to the parent object (QObject)
+        """
+        super(IExplorerImporter, self).__init__(id, parent)
+        
+        self.__fileName = ""
+    
+    def setPath(self, path):
+        """
+        Public method to set the path of the bookmarks file or directory.
+        
+        @param path bookmarks file or directory (string)
+        """
+        self.__fileName = path
+    
+    def open(self):
+        """
+        Public method to open the bookmarks file.
+        
+        @return flag indicating success (boolean)
+        """
+        if not os.path.exists(self.__fileName):
+            self._error = True
+            self._errorString = self.tr("Folder '{0}' does not exist.")\
+                .format(self.__fileName)
+            return False
+        if not os.path.isdir(self.__fileName):
+            self._error = True
+            self._errorString = self.tr("'{0}' is not a folder.")\
+                .format(self.__fileName)
+        return True
+    
+    def importedBookmarks(self):
+        """
+        Public method to get the imported bookmarks.
+        
+        @return imported bookmarks (BookmarkNode)
+        """
+        from ..BookmarkNode import BookmarkNode
+        
+        folders = {}
+        
+        importRootNode = BookmarkNode(BookmarkNode.Folder)
+        folders[self.__fileName] = importRootNode
+        
+        for dir, subdirs, files in os.walk(self.__fileName):
+            for subdir in subdirs:
+                path = os.path.join(dir, subdir)
+                if dir in folders:
+                    folder = BookmarkNode(BookmarkNode.Folder, folders[dir])
+                else:
+                    folder = BookmarkNode(BookmarkNode.Folder, importRootNode)
+                folder.title = subdir.replace("&", "&&")
+                folders[path] = folder
+            
+            for file in files:
+                name, ext = os.path.splitext(file)
+                if ext.lower() == ".url":
+                    path = os.path.join(dir, file)
+                    try:
+                        f = open(path, "r")
+                        contents = f.read()
+                        f.close()
+                    except IOError:
+                        continue
+                    url = ""
+                    for line in contents.splitlines():
+                        if line.startswith("URL="):
+                            url = line.replace("URL=", "")
+                            break
+                    if url:
+                        if dir in folders:
+                            bookmark = BookmarkNode(BookmarkNode.Bookmark,
+                                                    folders[dir])
+                        else:
+                            bookmark = BookmarkNode(BookmarkNode.Bookmark,
+                                                    importRootNode)
+                        bookmark.url = url
+                        bookmark.title = name.replace("&", "&&")
+        
+        if self._id == "ie":
+            importRootNode.title = self.tr("Internet Explorer Import")
+        else:
+            importRootNode.title = self.tr("Imported {0}")\
+                .format(QDate.currentDate().toString(Qt.SystemLocaleShortDate))
+        return importRootNode
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Bookmarks/BookmarksImporters/OperaImporter.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,136 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing an importer for Opera bookmarks.
+"""
+
+from __future__ import unicode_literals
+
+import os
+
+from PyQt5.QtCore import QCoreApplication, QDate, Qt
+
+from .BookmarksImporter import BookmarksImporter
+
+import UI.PixmapCache
+import Globals
+
+
+def getImporterInfo(id):
+    """
+    Module function to get information for the given source id.
+    
+    @param id id of the browser ("chrome" or "chromium")
+    @return tuple with an icon (QPixmap), readable name (string), name of
+        the default bookmarks file (string), an info text (string),
+        a prompt (string) and the default directory of the bookmarks file
+        (string)
+    @exception ValueError raised to indicate an invalid browser ID
+    """
+    if id == "opera":
+        if Globals.isWindowsPlatform():
+            standardDir = os.path.expandvars("%APPDATA%\\Opera\\Opera")
+        elif Globals.isMacPlatform():
+            standardDir = os.path.expanduser(
+                "~/Library/Opera")
+        else:
+            standardDir = os.path.expanduser("~/.opera")
+        return (
+            UI.PixmapCache.getPixmap("opera.png"),
+            "Opera",
+            "bookmarks.adr",
+            QCoreApplication.translate(
+                "OperaImporter",
+                """Opera stores its bookmarks in the <b>bookmarks.adr</b> """
+                """text file. This file is usually located in"""),
+            QCoreApplication.translate(
+                "OperaImporter",
+                """Please choose the file to begin importing bookmarks."""),
+            standardDir,
+        )
+    else:
+        raise ValueError("Unsupported browser ID given ({0}).".format(id))
+
+
+class OperaImporter(BookmarksImporter):
+    """
+    Class implementing the Opera bookmarks importer.
+    """
+    def __init__(self, id="", parent=None):
+        """
+        Constructor
+        
+        @param id source ID (string)
+        @param parent reference to the parent object (QObject)
+        """
+        super(OperaImporter, self).__init__(id, parent)
+        
+        self.__fileName = ""
+    
+    def setPath(self, path):
+        """
+        Public method to set the path of the bookmarks file or directory.
+        
+        @param path bookmarks file or directory (string)
+        """
+        self.__fileName = path
+    
+    def open(self):
+        """
+        Public method to open the bookmarks file.
+        
+        @return flag indicating success (boolean)
+        """
+        if not os.path.exists(self.__fileName):
+            self._error = True
+            self._errorString = self.tr("File '{0}' does not exist.")\
+                .format(self.__fileName)
+            return False
+        return True
+    
+    def importedBookmarks(self):
+        """
+        Public method to get the imported bookmarks.
+        
+        @return imported bookmarks (BookmarkNode)
+        """
+        try:
+            f = open(self.__fileName, "r", encoding="utf-8")
+            contents = f.read()
+            f.close()
+        except IOError as err:
+            self._error = True
+            self._errorString = self.tr(
+                "File '{0}' cannot be read.\nReason: {1}")\
+                .format(self.__fileName, str(err))
+            return None
+        
+        folderStack = []
+        
+        from ..BookmarkNode import BookmarkNode
+        importRootNode = BookmarkNode(BookmarkNode.Folder)
+        folderStack.append(importRootNode)
+        
+        for line in contents.splitlines():
+            line = line.strip()
+            if line == "#FOLDER":
+                node = BookmarkNode(BookmarkNode.Folder, folderStack[-1])
+                folderStack.append(node)
+            elif line == "#URL":
+                node = BookmarkNode(BookmarkNode.Bookmark, folderStack[-1])
+            elif line == "-":
+                folderStack.pop()
+            elif line.startswith("NAME="):
+                node.title = line.replace("NAME=", "").replace("&", "&&")
+            elif line.startswith("URL="):
+                node.url = line.replace("URL=", "")
+        
+        if self._id == "opera":
+            importRootNode.title = self.tr("Opera Import")
+        else:
+            importRootNode.title = self.tr("Imported {0}")\
+                .format(QDate.currentDate().toString(Qt.SystemLocaleShortDate))
+        return importRootNode
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Bookmarks/BookmarksImporters/SafariImporter.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,146 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing an importer for Apple Safari bookmarks.
+"""
+
+from __future__ import unicode_literals
+
+import os
+
+from PyQt5.QtCore import QCoreApplication, QDate, Qt
+
+from .BookmarksImporter import BookmarksImporter
+
+import UI.PixmapCache
+import Globals
+
+from Utilities import binplistlib
+
+
+def getImporterInfo(id):
+    """
+    Module function to get information for the given source id.
+    
+    @param id id of the browser ("chrome" or "chromium")
+    @return tuple with an icon (QPixmap), readable name (string), name of
+        the default bookmarks file (string), an info text (string),
+        a prompt (string) and the default directory of the bookmarks file
+        (string)
+    @exception ValueError raised to indicate an invalid browser ID
+    """
+    if id == "safari":
+        if Globals.isWindowsPlatform():
+            standardDir = os.path.expandvars(
+                "%APPDATA%\\Apple Computer\\Safari")
+        elif Globals.isMacPlatform():
+            standardDir = os.path.expanduser("~/Library/Safari")
+        else:
+            standardDir = ""
+        return (
+            UI.PixmapCache.getPixmap("safari.png"),
+            "Apple Safari",
+            "Bookmarks.plist",
+            QCoreApplication.translate(
+                "SafariImporter",
+                """Apple Safari stores its bookmarks in the"""
+                """ <b>Bookmarks.plist</b> file. This file is usually"""
+                """ located in"""),
+            QCoreApplication.translate(
+                "SafariImporter",
+                """Please choose the file to begin importing bookmarks."""),
+            standardDir,
+        )
+    else:
+        raise ValueError("Unsupported browser ID given ({0}).".format(id))
+
+
+class SafariImporter(BookmarksImporter):
+    """
+    Class implementing the Apple Safari bookmarks importer.
+    """
+    def __init__(self, id="", parent=None):
+        """
+        Constructor
+        
+        @param id source ID (string)
+        @param parent reference to the parent object (QObject)
+        """
+        super(SafariImporter, self).__init__(id, parent)
+        
+        self.__fileName = ""
+    
+    def setPath(self, path):
+        """
+        Public method to set the path of the bookmarks file or directory.
+        
+        @param path bookmarks file or directory (string)
+        """
+        self.__fileName = path
+    
+    def open(self):
+        """
+        Public method to open the bookmarks file.
+        
+        @return flag indicating success (boolean)
+        """
+        if not os.path.exists(self.__fileName):
+            self._error = True
+            self._errorString = self.tr("File '{0}' does not exist.")\
+                .format(self.__fileName)
+            return False
+        return True
+    
+    def importedBookmarks(self):
+        """
+        Public method to get the imported bookmarks.
+        
+        @return imported bookmarks (BookmarkNode)
+        """
+        try:
+            bookmarksDict = binplistlib.readPlist(self.__fileName)
+        except binplistlib.InvalidPlistException as err:
+            self._error = True
+            self._errorString = self.tr(
+                "Bookmarks file cannot be read.\nReason: {0}".format(str(err)))
+            return None
+        
+        from ..BookmarkNode import BookmarkNode
+        importRootNode = BookmarkNode(BookmarkNode.Folder)
+        if bookmarksDict["WebBookmarkFileVersion"] == 1 and \
+           bookmarksDict["WebBookmarkType"] == "WebBookmarkTypeList":
+            self.__processChildren(bookmarksDict["Children"], importRootNode)
+        
+        if self._id == "safari":
+            importRootNode.title = self.tr("Apple Safari Import")
+        else:
+            importRootNode.title = self.tr("Imported {0}")\
+                .format(QDate.currentDate().toString(Qt.SystemLocaleShortDate))
+        return importRootNode
+    
+    def __processChildren(self, children, rootNode):
+        """
+        Private method to process the list of children.
+        
+        @param children list of child nodes to be processed (list of dict)
+        @param rootNode node to add the bookmarks to (BookmarkNode)
+        """
+        from ..BookmarkNode import BookmarkNode
+        for child in children:
+            if child["WebBookmarkType"] == "WebBookmarkTypeList":
+                folder = BookmarkNode(BookmarkNode.Folder, rootNode)
+                folder.title = child["Title"].replace("&", "&&")
+                if "Children" in child:
+                    self.__processChildren(child["Children"], folder)
+            elif child["WebBookmarkType"] == "WebBookmarkTypeLeaf":
+                url = child["URLString"]
+                if url.startswith(("place:", "about:")):
+                    continue
+                
+                bookmark = BookmarkNode(BookmarkNode.Bookmark, rootNode)
+                bookmark.url = url
+                bookmark.title = child["URIDictionary"]["title"]\
+                    .replace("&", "&&")
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Bookmarks/BookmarksImporters/XbelImporter.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,158 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing an importer for XBEL files.
+"""
+
+from __future__ import unicode_literals
+
+import os
+
+from PyQt5.QtCore import QCoreApplication, QXmlStreamReader, QDate, Qt
+
+from .BookmarksImporter import BookmarksImporter
+
+import UI.PixmapCache
+
+
+def getImporterInfo(id):
+    """
+    Module function to get information for the given XBEL source id.
+    
+    @param id id of the browser ("chrome" or "chromium")
+    @return tuple with an icon (QPixmap), readable name (string), name of
+        the default bookmarks file (string), an info text (string),
+        a prompt (string) and the default directory of the bookmarks file
+        (string)
+    @exception ValueError raised to indicate an invalid browser ID
+    """
+    if id == "e5browser":
+        from ..BookmarksManager import BookmarksManager
+        bookmarksFile = BookmarksManager.getFileName()
+        return (
+            UI.PixmapCache.getPixmap("ericWeb48.png"),
+            "eric6 Web Browser",
+            os.path.basename(bookmarksFile),
+            QCoreApplication.translate(
+                "XbelImporter",
+                """eric6 Web Browser stores its bookmarks in the"""
+                """ <b>{0}</b> XML file. This file is usually located in"""
+            ).format(os.path.basename(bookmarksFile)),
+            QCoreApplication.translate(
+                "XbelImporter",
+                """Please choose the file to begin importing bookmarks."""),
+            os.path.dirname(bookmarksFile),
+        )
+    elif id == "konqueror":
+        if os.path.exists(os.path.expanduser("~/.kde4")):
+            standardDir = os.path.expanduser("~/.kde4/share/apps/konqueror")
+        elif os.path.exists(os.path.expanduser("~/.kde")):
+            standardDir = os.path.expanduser("~/.kde/share/apps/konqueror")
+        else:
+            standardDir = ""
+        return (
+            UI.PixmapCache.getPixmap("konqueror.png"),
+            "Konqueror",
+            "bookmarks.xml",
+            QCoreApplication.translate(
+                "XbelImporter",
+                """Konqueror stores its bookmarks in the"""
+                """ <b>bookmarks.xml</b> XML file. This file is usually"""
+                """ located in"""),
+            QCoreApplication.translate(
+                "XbelImporter",
+                """Please choose the file to begin importing bookmarks."""),
+            standardDir,
+        )
+    elif id == "xbel":
+        return (
+            UI.PixmapCache.getPixmap("xbel.png"),
+            "XBEL Bookmarks",
+            QCoreApplication.translate(
+                "XbelImporter", "XBEL Bookmarks") + " (*.xbel *.xml)",
+            QCoreApplication.translate(
+                "XbelImporter",
+                """You can import bookmarks from any browser that supports"""
+                """ XBEL exporting. This file has usually the extension"""
+                """ .xbel or .xml."""),
+            QCoreApplication.translate(
+                "XbelImporter",
+                """Please choose the file to begin importing bookmarks."""),
+            "",
+        )
+    else:
+        raise ValueError("Unsupported browser ID given ({0}).".format(id))
+
+
+class XbelImporter(BookmarksImporter):
+    """
+    Class implementing the XBEL bookmarks importer.
+    """
+    def __init__(self, id="", parent=None):
+        """
+        Constructor
+        
+        @param id source ID (string)
+        @param parent reference to the parent object (QObject)
+        """
+        super(XbelImporter, self).__init__(id, parent)
+        
+        self.__fileName = ""
+    
+    def setPath(self, path):
+        """
+        Public method to set the path of the bookmarks file or directory.
+        
+        @param path bookmarks file or directory (string)
+        """
+        self.__fileName = path
+    
+    def open(self):
+        """
+        Public method to open the bookmarks file.
+        
+        @return flag indicating success (boolean)
+        """
+        if not os.path.exists(self.__fileName):
+            self._error = True
+            self._errorString = self.tr("File '{0}' does not exist.")\
+                .format(self.__fileName)
+            return False
+        return True
+    
+    def importedBookmarks(self):
+        """
+        Public method to get the imported bookmarks.
+        
+        @return imported bookmarks (BookmarkNode)
+        """
+        from ..XbelReader import XbelReader
+        
+        reader = XbelReader()
+        importRootNode = reader.read(self.__fileName)
+        
+        if reader.error() != QXmlStreamReader.NoError:
+            self._error = True
+            self._errorString = self.tr(
+                """Error when importing bookmarks on line {0},"""
+                """ column {1}:\n{2}""")\
+                .format(reader.lineNumber(),
+                        reader.columnNumber(),
+                        reader.errorString())
+            return None
+        
+        from ..BookmarkNode import BookmarkNode
+        importRootNode.setType(BookmarkNode.Folder)
+        if self._id == "e5browser":
+            importRootNode.title = self.tr("eric6 Web Browser Import")
+        elif self._id == "konqueror":
+            importRootNode.title = self.tr("Konqueror Import")
+        elif self._id == "xbel":
+            importRootNode.title = self.tr("XBEL Import")
+        else:
+            importRootNode.title = self.tr("Imported {0}")\
+                .format(QDate.currentDate().toString(Qt.SystemLocaleShortDate))
+        return importRootNode
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Bookmarks/BookmarksImporters/__init__.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,125 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Package implementing bookmarks importers for various sources.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import QCoreApplication
+
+import UI.PixmapCache
+import Globals
+
+
+def getImporters():
+    """
+    Module function to get a list of supported importers.
+    
+    @return list of tuples with an icon (QIcon), readable name (string) and
+        internal name (string)
+    """
+    importers = []
+    importers.append(
+        (UI.PixmapCache.getIcon("ericWeb48.png"), "eric6 Web Browser",
+         "e5browser"))
+    importers.append(
+        (UI.PixmapCache.getIcon("firefox.png"), "Mozilla Firefox", "firefox"))
+    importers.append(
+        (UI.PixmapCache.getIcon("chrome.png"), "Google Chrome", "chrome"))
+    if Globals.isLinuxPlatform():
+        importers.append(
+            (UI.PixmapCache.getIcon("chromium.png"), "Chromium", "chromium"))
+        importers.append(
+            (UI.PixmapCache.getIcon("konqueror.png"), "Konqueror",
+             "konqueror"))
+    importers.append(
+        (UI.PixmapCache.getIcon("opera.png"), "Opera", "opera"))
+    importers.append(
+        (UI.PixmapCache.getIcon("safari.png"), "Apple Safari", "safari"))
+    if Globals.isWindowsPlatform():
+        importers.append(
+            (UI.PixmapCache.getIcon("internet_explorer.png"),
+             "Internet Explorer", "ie"))
+    importers.append(
+        (UI.PixmapCache.getIcon("xbel.png"),
+         QCoreApplication.translate("BookmarksImporters", "XBEL File"),
+         "xbel"))
+    importers.append(
+        (UI.PixmapCache.getIcon("html.png"),
+         QCoreApplication.translate("BookmarksImporters", "HTML File"),
+         "html"))
+    return importers
+
+
+def getImporterInfo(id):
+    """
+    Module function to get information for the given source id.
+    
+    @param id source id to get info for (string)
+    @return tuple with an icon (QPixmap), readable name (string), name of
+        the default bookmarks file (string), an info text (string),
+        a prompt (string) and the default directory of the bookmarks
+        file (string)
+    @exception ValueError raised to indicate an unsupported importer
+    """
+    if id in ["e5browser", "xbel", "konqueror"]:
+        from . import XbelImporter
+        return XbelImporter.getImporterInfo(id)
+    elif id == "html":
+        from . import HtmlImporter
+        return HtmlImporter.getImporterInfo(id)
+    elif id in ["chrome", "chromium"]:
+        from . import ChromeImporter
+        return ChromeImporter.getImporterInfo(id)
+    elif id == "opera":
+        from . import OperaImporter
+        return OperaImporter.getImporterInfo(id)
+    elif id == "firefox":
+        from . import FirefoxImporter
+        return FirefoxImporter.getImporterInfo(id)
+    elif id == "ie":
+        from . import IExplorerImporter
+        return IExplorerImporter.getImporterInfo(id)
+    elif id == "safari":
+        from . import SafariImporter
+        return SafariImporter.getImporterInfo(id)
+    else:
+        raise ValueError("Invalid importer ID given ({0}).".format(id))
+
+
+def getImporter(id, parent=None):
+    """
+    Module function to get an importer for the given source id.
+    
+    @param id source id to get an importer for (string)
+    @param parent reference to the parent object (QObject)
+    @return bookmarks importer (BookmarksImporter)
+    @exception ValueError raised to indicate an unsupported importer
+    """
+    if id in ["e5browser", "xbel", "konqueror"]:
+        from . import XbelImporter
+        return XbelImporter.XbelImporter(id, parent)
+    elif id == "html":
+        from . import HtmlImporter
+        return HtmlImporter.HtmlImporter(id, parent)
+    elif id in ["chrome", "chromium"]:
+        from . import ChromeImporter
+        return ChromeImporter.ChromeImporter(id, parent)
+    elif id == "opera":
+        from . import OperaImporter
+        return OperaImporter.OperaImporter(id, parent)
+    elif id == "firefox":
+        from . import FirefoxImporter
+        return FirefoxImporter.FirefoxImporter(id, parent)
+    elif id == "ie":
+        from . import IExplorerImporter
+        return IExplorerImporter.IExplorerImporter(id, parent)
+    elif id == "safari":
+        from . import SafariImporter
+        return SafariImporter.SafariImporter(id, parent)
+    else:
+        raise ValueError("No importer for ID {0}.".format(id))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Bookmarks/BookmarksManager.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,615 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the bookmarks manager.
+"""
+
+from __future__ import unicode_literals
+
+import os
+
+from PyQt5.QtCore import pyqtSignal, Qt, QT_TRANSLATE_NOOP, QObject, QFile, \
+    QIODevice, QXmlStreamReader, QDate, QDateTime, QFileInfo, QUrl, \
+    QCoreApplication
+from PyQt5.QtWidgets import QUndoStack, QUndoCommand, QDialog
+
+from E5Gui import E5MessageBox, E5FileDialog
+
+from .BookmarkNode import BookmarkNode
+
+from Utilities.AutoSaver import AutoSaver
+import Utilities
+import Preferences
+
+BOOKMARKBAR = QT_TRANSLATE_NOOP("BookmarksManager", "Bookmarks Bar")
+BOOKMARKMENU = QT_TRANSLATE_NOOP("BookmarksManager", "Bookmarks Menu")
+
+StartRoot = 0
+StartMenu = 1
+StartToolBar = 2
+
+
+class BookmarksManager(QObject):
+    """
+    Class implementing the bookmarks manager.
+    
+    @signal entryAdded(BookmarkNode) emitted after a bookmark node has been
+        added
+    @signal entryRemoved(BookmarkNode, int, BookmarkNode) emitted after a
+        bookmark node has been removed
+    @signal entryChanged(BookmarkNode) emitted after a bookmark node has been
+        changed
+    @signal bookmarksSaved() emitted after the bookmarks were saved
+    @signal bookmarksReloaded() emitted after the bookmarks were reloaded
+    """
+    entryAdded = pyqtSignal(BookmarkNode)
+    entryRemoved = pyqtSignal(BookmarkNode, int, BookmarkNode)
+    entryChanged = pyqtSignal(BookmarkNode)
+    bookmarksSaved = pyqtSignal()
+    bookmarksReloaded = pyqtSignal()
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object (QObject)
+        """
+        super(BookmarksManager, self).__init__(parent)
+        
+        self.__saveTimer = AutoSaver(self, self.save)
+        self.entryAdded.connect(self.__saveTimer.changeOccurred)
+        self.entryRemoved.connect(self.__saveTimer.changeOccurred)
+        self.entryChanged.connect(self.__saveTimer.changeOccurred)
+        
+        self.__initialize()
+    
+    def __initialize(self):
+        """
+        Private method to initialize some data.
+        """
+        self.__loaded = False
+        self.__bookmarkRootNode = None
+        self.__toolbar = None
+        self.__menu = None
+        self.__bookmarksModel = None
+        self.__commands = QUndoStack()
+    
+    @classmethod
+    def getFileName(cls):
+        """
+        Class method to get the file name of the bookmark file.
+        
+        @return name of the bookmark file (string)
+        """
+        return os.path.join(Utilities.getConfigDir(), "web_browser",
+                            "bookmarks.xbel")
+    
+    def close(self):
+        """
+        Public method to close the bookmark manager.
+        """
+        self.__saveTimer.saveIfNeccessary()
+    
+    def undoRedoStack(self):
+        """
+        Public method to get a reference to the undo stack.
+        
+        @return reference to the undo stack (QUndoStack)
+        """
+        return self.__commands
+    
+    def changeExpanded(self):
+        """
+        Public method to handle a change of the expanded state.
+        """
+        self.__saveTimer.changeOccurred()
+    
+    def reload(self):
+        """
+        Public method used to initiate a reloading of the bookmarks.
+        """
+        self.__initialize()
+        self.load()
+        self.bookmarksReloaded.emit()
+    
+    def load(self):
+        """
+        Public method to load the bookmarks.
+        
+        @exception RuntimeError raised to indicate an error loading the
+            bookmarks
+        """
+        if self.__loaded:
+            return
+        
+        self.__loaded = True
+        
+        bookmarkFile = self.getFileName()
+        if not QFile.exists(bookmarkFile):
+            from . import DefaultBookmarks_rc       # __IGNORE_WARNING__
+            bookmarkFile = QFile(":/DefaultBookmarks.xbel")
+            bookmarkFile.open(QIODevice.ReadOnly)
+        
+        from .XbelReader import XbelReader
+        reader = XbelReader()
+        self.__bookmarkRootNode = reader.read(bookmarkFile)
+        if reader.error() != QXmlStreamReader.NoError:
+            E5MessageBox.warning(
+                None,
+                self.tr("Loading Bookmarks"),
+                self.tr(
+                    """Error when loading bookmarks on line {0},"""
+                    """ column {1}:\n {2}""")
+                .format(reader.lineNumber(),
+                        reader.columnNumber(),
+                        reader.errorString()))
+        
+        others = []
+        for index in range(
+                len(self.__bookmarkRootNode.children()) - 1, -1, -1):
+            node = self.__bookmarkRootNode.children()[index]
+            if node.type() == BookmarkNode.Folder:
+                if (node.title == self.tr("Toolbar Bookmarks") or
+                    node.title == BOOKMARKBAR) and \
+                   self.__toolbar is None:
+                    node.title = self.tr(BOOKMARKBAR)
+                    self.__toolbar = node
+                
+                if (node.title == self.tr("Menu") or
+                    node.title == BOOKMARKMENU) and \
+                   self.__menu is None:
+                    node.title = self.tr(BOOKMARKMENU)
+                    self.__menu = node
+            else:
+                others.append(node)
+            self.__bookmarkRootNode.remove(node)
+        
+        if len(self.__bookmarkRootNode.children()) > 0:
+            raise RuntimeError("Error loading bookmarks.")
+        
+        if self.__toolbar is None:
+            self.__toolbar = BookmarkNode(BookmarkNode.Folder,
+                                          self.__bookmarkRootNode)
+            self.__toolbar.title = self.tr(BOOKMARKBAR)
+        else:
+            self.__bookmarkRootNode.add(self.__toolbar)
+        
+        if self.__menu is None:
+            self.__menu = BookmarkNode(BookmarkNode.Folder,
+                                       self.__bookmarkRootNode)
+            self.__menu.title = self.tr(BOOKMARKMENU)
+        else:
+            self.__bookmarkRootNode.add(self.__menu)
+        
+        for node in others:
+            self.__menu.add(node)
+    
+    def save(self):
+        """
+        Public method to save the bookmarks.
+        """
+        if not self.__loaded:
+            return
+        
+        from .XbelWriter import XbelWriter
+        writer = XbelWriter()
+        bookmarkFile = self.getFileName()
+        
+        # save root folder titles in English (i.e. not localized)
+        self.__menu.title = BOOKMARKMENU
+        self.__toolbar.title = BOOKMARKBAR
+        if not writer.write(bookmarkFile, self.__bookmarkRootNode):
+            E5MessageBox.warning(
+                None,
+                self.tr("Saving Bookmarks"),
+                self.tr("""Error saving bookmarks to <b>{0}</b>.""")
+                .format(bookmarkFile))
+        
+        # restore localized titles
+        self.__menu.title = self.tr(BOOKMARKMENU)
+        self.__toolbar.title = self.tr(BOOKMARKBAR)
+        
+        self.bookmarksSaved.emit()
+    
+    def addBookmark(self, parent, node, row=-1):
+        """
+        Public method to add a bookmark.
+        
+        @param parent reference to the node to add to (BookmarkNode)
+        @param node reference to the node to add (BookmarkNode)
+        @param row row number (integer)
+        """
+        if not self.__loaded:
+            return
+        
+        self.setTimestamp(node, BookmarkNode.TsAdded,
+                          QDateTime.currentDateTime())
+        
+        command = InsertBookmarksCommand(self, parent, node, row)
+        self.__commands.push(command)
+    
+    def removeBookmark(self, node):
+        """
+        Public method to remove a bookmark.
+        
+        @param node reference to the node to be removed (BookmarkNode)
+        """
+        if not self.__loaded:
+            return
+        
+        parent = node.parent()
+        row = parent.children().index(node)
+        command = RemoveBookmarksCommand(self, parent, row)
+        self.__commands.push(command)
+    
+    def setTitle(self, node, newTitle):
+        """
+        Public method to set the title of a bookmark.
+        
+        @param node reference to the node to be changed (BookmarkNode)
+        @param newTitle title to be set (string)
+        """
+        if not self.__loaded:
+            return
+        
+        command = ChangeBookmarkCommand(self, node, newTitle, True)
+        self.__commands.push(command)
+    
+    def setUrl(self, node, newUrl):
+        """
+        Public method to set the URL of a bookmark.
+        
+        @param node reference to the node to be changed (BookmarkNode)
+        @param newUrl URL to be set (string)
+        """
+        if not self.__loaded:
+            return
+        
+        command = ChangeBookmarkCommand(self, node, newUrl, False)
+        self.__commands.push(command)
+    
+    def setNodeChanged(self, node):
+        """
+        Public method to signal changes of bookmarks other than title, URL
+        or timestamp.
+        
+        @param node reference to the bookmark (BookmarkNode)
+        """
+        self.__saveTimer.changeOccurred()
+    
+    def setTimestamp(self, node, timestampType, timestamp):
+        """
+        Public method to set the URL of a bookmark.
+        
+        @param node reference to the node to be changed (BookmarkNode)
+        @param timestampType type of the timestamp to set
+            (BookmarkNode.TsAdded, BookmarkNode.TsModified,
+            BookmarkNode.TsVisited)
+        @param timestamp timestamp to set (QDateTime)
+        """
+        if not self.__loaded:
+            return
+        
+        assert timestampType in [BookmarkNode.TsAdded,
+                                 BookmarkNode.TsModified,
+                                 BookmarkNode.TsVisited]
+        
+        if timestampType == BookmarkNode.TsAdded:
+            node.added = timestamp
+        elif timestampType == BookmarkNode.TsModified:
+            node.modified = timestamp
+        elif timestampType == BookmarkNode.TsVisited:
+            node.visited = timestamp
+        self.__saveTimer.changeOccurred()
+    
+    def bookmarks(self):
+        """
+        Public method to get a reference to the root bookmark node.
+        
+        @return reference to the root bookmark node (BookmarkNode)
+        """
+        if not self.__loaded:
+            self.load()
+        
+        return self.__bookmarkRootNode
+    
+    def menu(self):
+        """
+        Public method to get a reference to the bookmarks menu node.
+        
+        @return reference to the bookmarks menu node (BookmarkNode)
+        """
+        if not self.__loaded:
+            self.load()
+        
+        return self.__menu
+    
+    def toolbar(self):
+        """
+        Public method to get a reference to the bookmarks toolbar node.
+        
+        @return reference to the bookmarks toolbar node (BookmarkNode)
+        """
+        if not self.__loaded:
+            self.load()
+        
+        return self.__toolbar
+    
+    def bookmarksModel(self):
+        """
+        Public method to get a reference to the bookmarks model.
+        
+        @return reference to the bookmarks model (BookmarksModel)
+        """
+        if self.__bookmarksModel is None:
+            from .BookmarksModel import BookmarksModel
+            self.__bookmarksModel = BookmarksModel(self, self)
+        return self.__bookmarksModel
+    
+    def importBookmarks(self):
+        """
+        Public method to import bookmarks.
+        """
+        from .BookmarksImportDialog import BookmarksImportDialog
+        dlg = BookmarksImportDialog()
+        if dlg.exec_() == QDialog.Accepted:
+            importRootNode = dlg.getImportedBookmarks()
+            if importRootNode is not None:
+                self.addBookmark(self.menu(), importRootNode)
+    
+    def exportBookmarks(self):
+        """
+        Public method to export the bookmarks.
+        """
+        fileName, selectedFilter = E5FileDialog.getSaveFileNameAndFilter(
+            None,
+            self.tr("Export Bookmarks"),
+            "eric6_bookmarks.xbel",
+            self.tr("XBEL bookmarks (*.xbel);;"
+                    "XBEL bookmarks (*.xml);;"
+                    "HTML Bookmarks (*.html)"))
+        if not fileName:
+            return
+        
+        ext = QFileInfo(fileName).suffix()
+        if not ext:
+            ex = selectedFilter.split("(*")[1].split(")")[0]
+            if ex:
+                fileName += ex
+        
+        ext = QFileInfo(fileName).suffix()
+        if ext == "html":
+            from .NsHtmlWriter import NsHtmlWriter
+            writer = NsHtmlWriter()
+        else:
+            from .XbelWriter import XbelWriter
+            writer = XbelWriter()
+        if not writer.write(fileName, self.__bookmarkRootNode):
+            E5MessageBox.critical(
+                None,
+                self.tr("Exporting Bookmarks"),
+                self.tr("""Error exporting bookmarks to <b>{0}</b>.""")
+                .format(fileName))
+    
+    def iconChanged(self, url):
+        """
+        Public slot to update the icon image for an URL.
+        
+        @param url URL of the icon to update (QUrl or string)
+        """
+        if isinstance(url, QUrl):
+            url = url.toString()
+        nodes = self.bookmarksForUrl(url)
+        for node in nodes:
+            self.bookmarksModel().entryChanged(node)
+    
+    def bookmarkForUrl(self, url, start=StartRoot):
+        """
+        Public method to get a bookmark node for a given URL.
+        
+        @param url URL of the bookmark to search for (QUrl or string)
+        @keyparam start indicator for the start of the search
+            (StartRoot, StartMenu, StartToolBar)
+        @return bookmark node for the given url (BookmarkNode)
+        """
+        if start == StartMenu:
+            startNode = self.__menu
+        elif start == StartToolBar:
+            startNode = self.__toolbar
+        else:
+            startNode = self.__bookmarkRootNode
+        if startNode is None:
+            return None
+        
+        if isinstance(url, QUrl):
+            url = url.toString()
+        
+        return self.__searchBookmark(url, startNode)
+    
+    def __searchBookmark(self, url, startNode):
+        """
+        Private method get a bookmark node for a given URL.
+        
+        @param url URL of the bookmark to search for (string)
+        @param startNode reference to the node to start searching
+            (BookmarkNode)
+        @return bookmark node for the given url (BookmarkNode)
+        """
+        bm = None
+        for node in startNode.children():
+            if node.type() == BookmarkNode.Folder:
+                bm = self.__searchBookmark(url, node)
+            elif node.type() == BookmarkNode.Bookmark:
+                if node.url == url:
+                    bm = node
+            if bm is not None:
+                return bm
+        return None
+    
+    def bookmarksForUrl(self, url, start=StartRoot):
+        """
+        Public method to get a list of bookmark nodes for a given URL.
+        
+        @param url URL of the bookmarks to search for (QUrl or string)
+        @keyparam start indicator for the start of the search
+            (StartRoot, StartMenu, StartToolBar)
+        @return list of bookmark nodes for the given url (list of BookmarkNode)
+        """
+        if start == StartMenu:
+            startNode = self.__menu
+        elif start == StartToolBar:
+            startNode = self.__toolbar
+        else:
+            startNode = self.__bookmarkRootNode
+        if startNode is None:
+            return []
+        
+        if isinstance(url, QUrl):
+            url = url.toString()
+        
+        return self.__searchBookmarks(url, startNode)
+    
+    def __searchBookmarks(self, url, startNode):
+        """
+        Private method get a list of bookmark nodes for a given URL.
+        
+        @param url URL of the bookmarks to search for (string)
+        @param startNode reference to the node to start searching
+            (BookmarkNode)
+        @return list of bookmark nodes for the given url (list of BookmarkNode)
+        """
+        bm = []
+        for node in startNode.children():
+            if node.type() == BookmarkNode.Folder:
+                bm.extend(self.__searchBookmarks(url, node))
+            elif node.type() == BookmarkNode.Bookmark:
+                if node.url == url:
+                    bm.append(node)
+        return bm
+
+
+class RemoveBookmarksCommand(QUndoCommand):
+    """
+    Class implementing the Remove undo command.
+    """
+    def __init__(self, bookmarksManager, parent, row):
+        """
+        Constructor
+        
+        @param bookmarksManager reference to the bookmarks manager
+            (BookmarksManager)
+        @param parent reference to the parent node (BookmarkNode)
+        @param row row number of bookmark (integer)
+        """
+        super(RemoveBookmarksCommand, self).__init__(
+            QCoreApplication.translate("BookmarksManager", "Remove Bookmark"))
+        
+        self._row = row
+        self._bookmarksManager = bookmarksManager
+        try:
+            self._node = parent.children()[row]
+        except IndexError:
+            self._node = BookmarkNode()
+        self._parent = parent
+    
+    def undo(self):
+        """
+        Public slot to perform the undo action.
+        """
+        self._parent.add(self._node, self._row)
+        self._bookmarksManager.entryAdded.emit(self._node)
+    
+    def redo(self):
+        """
+        Public slot to perform the redo action.
+        """
+        self._parent.remove(self._node)
+        self._bookmarksManager.entryRemoved.emit(
+            self._parent, self._row, self._node)
+
+
+class InsertBookmarksCommand(RemoveBookmarksCommand):
+    """
+    Class implementing the Insert undo command.
+    """
+    def __init__(self, bookmarksManager, parent, node, row):
+        """
+        Constructor
+        
+        @param bookmarksManager reference to the bookmarks manager
+            (BookmarksManager)
+        @param parent reference to the parent node (BookmarkNode)
+        @param node reference to the node to be inserted (BookmarkNode)
+        @param row row number of bookmark (integer)
+        """
+        RemoveBookmarksCommand.__init__(self, bookmarksManager, parent, row)
+        self.setText(QCoreApplication.translate(
+            "BookmarksManager", "Insert Bookmark"))
+        self._node = node
+    
+    def undo(self):
+        """
+        Public slot to perform the undo action.
+        """
+        RemoveBookmarksCommand.redo(self)
+    
+    def redo(self):
+        """
+        Public slot to perform the redo action.
+        """
+        RemoveBookmarksCommand.undo(self)
+
+
+class ChangeBookmarkCommand(QUndoCommand):
+    """
+    Class implementing the Insert undo command.
+    """
+    def __init__(self, bookmarksManager, node, newValue, title):
+        """
+        Constructor
+        
+        @param bookmarksManager reference to the bookmarks manager
+            (BookmarksManager)
+        @param node reference to the node to be changed (BookmarkNode)
+        @param newValue new value to be set (string)
+        @param title flag indicating a change of the title (True) or
+            the URL (False) (boolean)
+        """
+        super(ChangeBookmarkCommand, self).__init__()
+        
+        self._bookmarksManager = bookmarksManager
+        self._title = title
+        self._newValue = newValue
+        self._node = node
+        
+        if self._title:
+            self._oldValue = self._node.title
+            self.setText(QCoreApplication.translate(
+                "BookmarksManager", "Name Change"))
+        else:
+            self._oldValue = self._node.url
+            self.setText(QCoreApplication.translate(
+                "BookmarksManager", "Address Change"))
+    
+    def undo(self):
+        """
+        Public slot to perform the undo action.
+        """
+        if self._title:
+            self._node.title = self._oldValue
+        else:
+            self._node.url = self._oldValue
+        self._bookmarksManager.entryChanged.emit(self._node)
+    
+    def redo(self):
+        """
+        Public slot to perform the redo action.
+        """
+        if self._title:
+            self._node.title = self._newValue
+        else:
+            self._node.url = self._newValue
+        self._bookmarksManager.entryChanged.emit(self._node)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Bookmarks/BookmarksMenu.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,333 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the bookmarks menu.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSignal, Qt, QUrl
+from PyQt5.QtGui import QCursor
+from PyQt5.QtWidgets import QMenu
+
+from E5Gui.E5ModelMenu import E5ModelMenu
+
+from .BookmarksModel import BookmarksModel
+from .BookmarkNode import BookmarkNode
+
+
+class BookmarksMenu(E5ModelMenu):
+    """
+    Class implementing the bookmarks menu base class.
+    
+    @signal openUrl(QUrl, str) emitted to open a URL with the given title in
+        the current tab
+    @signal newUrl(QUrl, str) emitted to open a URL with the given title in a
+        new tab
+    """
+    openUrl = pyqtSignal(QUrl, str)
+    newUrl = pyqtSignal(QUrl, str)
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (QWidget)
+        """
+        E5ModelMenu.__init__(self, parent)
+        
+        self.activated.connect(self.__activated)
+        self.setStatusBarTextRole(BookmarksModel.UrlStringRole)
+        self.setSeparatorRole(BookmarksModel.SeparatorRole)
+        
+        self.setContextMenuPolicy(Qt.CustomContextMenu)
+        self.customContextMenuRequested.connect(self.__contextMenuRequested)
+    
+    def createBaseMenu(self):
+        """
+        Public method to get the menu that is used to populate sub menu's.
+        
+        @return reference to the menu (BookmarksMenu)
+        """
+        menu = BookmarksMenu(self)
+        menu.openUrl.connect(self.openUrl)
+        menu.newUrl.connect(self.newUrl)
+        return menu
+    
+    def __activated(self, idx):
+        """
+        Private slot handling the activated signal.
+        
+        @param idx index of the activated item (QModelIndex)
+        """
+        if self._keyboardModifiers & Qt.ControlModifier:
+            self.newUrl.emit(
+                idx.data(BookmarksModel.UrlRole),
+                idx.data(Qt.DisplayRole))
+        else:
+            self.openUrl.emit(
+                idx.data(BookmarksModel.UrlRole),
+                idx.data(Qt.DisplayRole))
+        self.resetFlags()
+    
+    def postPopulated(self):
+        """
+        Public method to add any actions after the tree.
+        """
+        if self.isEmpty():
+            return
+        
+        parent = self.rootIndex()
+        
+        hasBookmarks = False
+        
+        for i in range(parent.model().rowCount(parent)):
+            child = parent.model().index(i, 0, parent)
+            
+            if child.data(BookmarksModel.TypeRole) == BookmarkNode.Bookmark:
+                hasBookmarks = True
+                break
+        
+        if not hasBookmarks:
+            return
+        
+        self.addSeparator()
+        act = self.addAction(self.tr("Open all in Tabs"))
+        act.triggered.connect(self.openAll)
+    
+    def openAll(self):
+        """
+        Public slot to open all the menu's items.
+        """
+        menu = self.sender().parent()
+        if menu is None:
+            return
+        
+        parent = menu.rootIndex()
+        if not parent.isValid():
+            return
+        
+        for i in range(parent.model().rowCount(parent)):
+            child = parent.model().index(i, 0, parent)
+            
+            if child.data(BookmarksModel.TypeRole) != BookmarkNode.Bookmark:
+                continue
+            
+            if i == 0:
+                self.openUrl.emit(
+                    child.data(BookmarksModel.UrlRole),
+                    child.data(Qt.DisplayRole))
+            else:
+                self.newUrl.emit(
+                    child.data(BookmarksModel.UrlRole),
+                    child.data(Qt.DisplayRole))
+    
+    def __contextMenuRequested(self, pos):
+        """
+        Private slot to handle the context menu request.
+        
+        @param pos position the context menu shall be shown (QPoint)
+        """
+        act = self.actionAt(pos)
+        
+        if act is not None and \
+                act.menu() is None and \
+                self.index(act).isValid():
+            menu = QMenu()
+            v = act.data()
+            
+            menu.addAction(
+                self.tr("Open"),
+                self.__openBookmark).setData(v)
+            menu.addAction(
+                self.tr("Open in New Tab\tCtrl+LMB"),
+                self.__openBookmarkInNewTab).setData(v)
+            menu.addAction(
+                self.tr("Open in New Window"),
+                self.__openBookmarkInNewWindow).setData(v)
+            menu.addAction(
+                self.tr("Open in New Private Window"),
+                self.__openBookmarkInPrivateWindow).setData(v)
+            menu.addSeparator()
+            
+            menu.addAction(
+                self.tr("Remove"),
+                self.__removeBookmark).setData(v)
+            menu.addSeparator()
+            
+            menu.addAction(
+                self.tr("Properties..."),
+                self.__edit).setData(v)
+            
+            execAct = menu.exec_(QCursor.pos())
+            if execAct is not None:
+                self.close()
+                parent = self.parent()
+                while parent is not None and isinstance(parent, QMenu):
+                    parent.close()
+                    parent = parent.parent()
+    
+    def __openBookmark(self):
+        """
+        Private slot to open a bookmark in the current browser tab.
+        """
+        idx = self.index(self.sender())
+        
+        self.openUrl.emit(
+            idx.data(BookmarksModel.UrlRole),
+            idx.data(Qt.DisplayRole))
+    
+    def __openBookmarkInNewTab(self):
+        """
+        Private slot to open a bookmark in a new browser tab.
+        """
+        idx = self.index(self.sender())
+        
+        self.newUrl.emit(
+            idx.data(BookmarksModel.UrlRole),
+            idx.data(Qt.DisplayRole))
+    
+    def __openBookmarkInNewWindow(self):
+        """
+        Private slot to open a bookmark in a new window.
+        """
+        idx = self.index(self.sender())
+        url = idx.data(BookmarksModel.UrlRole)
+        
+        from WebBrowser.WebBrowserWindow import WebBrowserWindow
+        WebBrowserWindow.mainWindow().newWindow(url)
+    
+    def __openBookmarkInPrivateWindow(self):
+        """
+        Private slot to open a bookmark in a new private window.
+        """
+        idx = self.index(self.sender())
+        url = idx.data(BookmarksModel.UrlRole)
+        
+        from WebBrowser.WebBrowserWindow import WebBrowserWindow
+        WebBrowserWindow.mainWindow().newPrivateWindow(url)
+    
+    def __removeBookmark(self):
+        """
+        Private slot to remove a bookmark.
+        """
+        idx = self.index(self.sender())
+        self.removeEntry(idx)
+    
+    def __edit(self):
+        """
+        Private slot to edit a bookmarks properties.
+        """
+        from .BookmarkPropertiesDialog import BookmarkPropertiesDialog
+        
+        idx = self.index(self.sender())
+        node = self.model().node(idx)
+        dlg = BookmarkPropertiesDialog(node)
+        dlg.exec_()
+
+##############################################################################
+
+
+class BookmarksMenuBarMenu(BookmarksMenu):
+    """
+    Class implementing a dynamically populated menu for bookmarks.
+    
+    @signal openUrl(QUrl, str) emitted to open a URL with the given title in
+        the current tab
+    """
+    openUrl = pyqtSignal(QUrl, str)
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (QWidget)
+        """
+        BookmarksMenu.__init__(self, parent)
+        
+        self.__bookmarksManager = None
+        self.__initialActions = []
+    
+    def prePopulated(self):
+        """
+        Public method to add any actions before the tree.
+       
+        @return flag indicating if any actions were added (boolean)
+        """
+        import WebBrowser.WebBrowserWindow
+        
+        self.__bookmarksManager = WebBrowser.WebBrowserWindow.WebBrowserWindow\
+            .bookmarksManager()
+        self.setModel(self.__bookmarksManager.bookmarksModel())
+        self.setRootIndex(self.__bookmarksManager.bookmarksModel()
+                          .nodeIndex(self.__bookmarksManager.menu()))
+        
+        # initial actions
+        for act in self.__initialActions:
+            if act == "--SEPARATOR--":
+                self.addSeparator()
+            else:
+                self.addAction(act)
+        if len(self.__initialActions) != 0:
+            self.addSeparator()
+        
+        self.createMenu(
+            self.__bookmarksManager.bookmarksModel()
+                .nodeIndex(self.__bookmarksManager.toolbar()),
+            1, self)
+        return True
+    
+    def postPopulated(self):
+        """
+        Public method to add any actions after the tree.
+        """
+        if self.isEmpty():
+            return
+        
+        parent = self.rootIndex()
+        
+        hasBookmarks = False
+        
+        for i in range(parent.model().rowCount(parent)):
+            child = parent.model().index(i, 0, parent)
+            
+            if child.data(BookmarksModel.TypeRole) == BookmarkNode.Bookmark:
+                hasBookmarks = True
+                break
+        
+        if not hasBookmarks:
+            return
+        
+        self.addSeparator()
+        act = self.addAction(self.tr("Default Home Page"))
+        act.setData("eric:home")
+        act.triggered.connect(self.__defaultBookmarkTriggered)
+        act = self.addAction(self.tr("Speed Dial"))
+        act.setData("eric:speeddial")
+        act.triggered.connect(self.__defaultBookmarkTriggered)
+        self.addSeparator()
+        act = self.addAction(self.tr("Open all in Tabs"))
+        act.triggered.connect(self.openAll)
+    
+    def setInitialActions(self, actions):
+        """
+        Public method to set the list of actions that should appear first in
+        the menu.
+        
+        @param actions list of initial actions (list of QAction)
+        """
+        self.__initialActions = actions[:]
+        for act in self.__initialActions:
+            self.addAction(act)
+    
+    def __defaultBookmarkTriggered(self):
+        """
+        Private slot handling the default bookmark menu entries.
+        """
+        act = self.sender()
+        urlStr = act.data()
+        if urlStr.startswith("eric:"):
+            self.openUrl.emit(QUrl(urlStr), "")
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Bookmarks/BookmarksModel.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,466 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the bookmark model class.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import Qt, QAbstractItemModel, QModelIndex, QUrl, \
+    QByteArray, QDataStream, QIODevice, QBuffer, QMimeData
+
+import UI.PixmapCache
+
+
+class BookmarksModel(QAbstractItemModel):
+    """
+    Class implementing the bookmark model.
+    """
+    TypeRole = Qt.UserRole + 1
+    UrlRole = Qt.UserRole + 2
+    UrlStringRole = Qt.UserRole + 3
+    SeparatorRole = Qt.UserRole + 4
+    
+    MIMETYPE = "application/bookmarks.xbel"
+    
+    def __init__(self, manager, parent=None):
+        """
+        Constructor
+        
+        @param manager reference to the bookmark manager object
+            (BookmarksManager)
+        @param parent reference to the parent object (QObject)
+        """
+        super(BookmarksModel, self).__init__(parent)
+        
+        self.__endMacro = False
+        self.__bookmarksManager = manager
+        
+        manager.entryAdded.connect(self.entryAdded)
+        manager.entryRemoved.connect(self.entryRemoved)
+        manager.entryChanged.connect(self.entryChanged)
+        
+        self.__headers = [
+            self.tr("Title"),
+            self.tr("Address"),
+        ]
+    
+    def bookmarksManager(self):
+        """
+        Public method to get a reference to the bookmarks manager.
+        
+        @return reference to the bookmarks manager object (BookmarksManager)
+        """
+        return self.__bookmarksManager
+    
+    def nodeIndex(self, node):
+        """
+        Public method to get a model index.
+        
+        @param node reference to the node to get the index for (BookmarkNode)
+        @return model index (QModelIndex)
+        """
+        parent = node.parent()
+        if parent is None:
+            return QModelIndex()
+        return self.createIndex(parent.children().index(node), 0, node)
+    
+    def entryAdded(self, node):
+        """
+        Public slot to add a bookmark node.
+        
+        @param node reference to the bookmark node to add (BookmarkNode)
+        """
+        if node is None or node.parent() is None:
+            return
+        
+        parent = node.parent()
+        row = parent.children().index(node)
+        # node was already added so remove before beginInsertRows is called
+        parent.remove(node)
+        self.beginInsertRows(self.nodeIndex(parent), row, row)
+        parent.add(node, row)
+        self.endInsertRows()
+    
+    def entryRemoved(self, parent, row, node):
+        """
+        Public slot to remove a bookmark node.
+        
+        @param parent reference to the parent bookmark node (BookmarkNode)
+        @param row row number of the node (integer)
+        @param node reference to the bookmark node to remove (BookmarkNode)
+        """
+        # node was already removed, re-add so beginRemoveRows works
+        parent.add(node, row)
+        self.beginRemoveRows(self.nodeIndex(parent), row, row)
+        parent.remove(node)
+        self.endRemoveRows()
+    
+    def entryChanged(self, node):
+        """
+        Public method to change a node.
+        
+        @param node reference to the bookmark node to change (BookmarkNode)
+        """
+        idx = self.nodeIndex(node)
+        self.dataChanged.emit(idx, idx)
+    
+    def removeRows(self, row, count, parent=QModelIndex()):
+        """
+        Public method to remove bookmarks from the model.
+        
+        @param row row of the first bookmark to remove (integer)
+        @param count number of bookmarks to remove (integer)
+        @param parent index of the parent bookmark node (QModelIndex)
+        @return flag indicating successful removal (boolean)
+        """
+        if row < 0 or count <= 0 or row + count > self.rowCount(parent):
+            return False
+        
+        bookmarkNode = self.node(parent)
+        children = bookmarkNode.children()[row:(row + count)]
+        for node in children:
+            if node == self.__bookmarksManager.menu() or \
+               node == self.__bookmarksManager.toolbar():
+                continue
+            self.__bookmarksManager.removeBookmark(node)
+        
+        if self.__endMacro:
+            self.__bookmarksManager.undoRedoStack().endMacro()
+            self.__endMacro = False
+        
+        return True
+    
+    def headerData(self, section, orientation, role=Qt.DisplayRole):
+        """
+        Public method to get the header data.
+        
+        @param section section number (integer)
+        @param orientation header orientation (Qt.Orientation)
+        @param role data role (integer)
+        @return header data
+        """
+        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
+            try:
+                return self.__headers[section]
+            except IndexError:
+                pass
+        return QAbstractItemModel.headerData(self, section, orientation, role)
+    
+    def data(self, index, role=Qt.DisplayRole):
+        """
+        Public method to get data from the model.
+        
+        @param index index of bookmark to get data for (QModelIndex)
+        @param role data role (integer)
+        @return bookmark data
+        """
+        if not index.isValid() or index.model() != self:
+            return None
+        
+        from .BookmarkNode import BookmarkNode
+        
+        bookmarkNode = self.node(index)
+        if role in [Qt.EditRole, Qt.DisplayRole]:
+            if bookmarkNode.type() == BookmarkNode.Separator:
+                if index.column() == 0:
+                    return 50 * '\xB7'
+                elif index.column() == 1:
+                    return ""
+            
+            if index.column() == 0:
+                return bookmarkNode.title
+            elif index.column() == 1:
+                return bookmarkNode.url
+        
+        elif role == self.UrlRole:
+            return QUrl(bookmarkNode.url)
+        
+        elif role == self.UrlStringRole:
+            return bookmarkNode.url
+        
+        elif role == self.TypeRole:
+            return bookmarkNode.type()
+        
+        elif role == self.SeparatorRole:
+            return bookmarkNode.type() == BookmarkNode.Separator
+        
+        elif role == Qt.DecorationRole:
+            if index.column() == 0:
+                if bookmarkNode.type() == BookmarkNode.Folder:
+                    return UI.PixmapCache.getIcon("dirOpen.png")
+                import WebBrowser.WebBrowserWindow
+                return WebBrowser.WebBrowserWindow.WebBrowserWindow.icon(
+                    QUrl(bookmarkNode.url))
+        
+        return None
+    
+    def columnCount(self, parent=QModelIndex()):
+        """
+        Public method to get the number of columns.
+        
+        @param parent index of parent (QModelIndex)
+        @return number of columns (integer)
+        """
+        if parent.column() > 0:
+            return 0
+        else:
+            return len(self.__headers)
+    
+    def rowCount(self, parent=QModelIndex()):
+        """
+        Public method to determine the number of rows.
+        
+        @param parent index of parent (QModelIndex)
+        @return number of rows (integer)
+        """
+        if parent.column() > 0:
+            return 0
+        
+        if not parent.isValid():
+            return len(self.__bookmarksManager.bookmarks().children())
+        
+        itm = parent.internalPointer()
+        return len(itm.children())
+    
+    def index(self, row, column, parent=QModelIndex()):
+        """
+        Public method to get a model index for a node cell.
+        
+        @param row row number (integer)
+        @param column column number (integer)
+        @param parent index of the parent (QModelIndex)
+        @return index (QModelIndex)
+        """
+        if row < 0 or column < 0 or \
+           row >= self.rowCount(parent) or column >= self.columnCount(parent):
+            return QModelIndex()
+        
+        parentNode = self.node(parent)
+        return self.createIndex(row, column, parentNode.children()[row])
+    
+    def parent(self, index=QModelIndex()):
+        """
+        Public method to get the index of the parent node.
+        
+        @param index index of the child node (QModelIndex)
+        @return index of the parent node (QModelIndex)
+        """
+        if not index.isValid():
+            return QModelIndex()
+        
+        itemNode = self.node(index)
+        if itemNode is None:
+            parentNode = None
+        else:
+            parentNode = itemNode.parent()
+        
+        if parentNode is None or \
+                parentNode == self.__bookmarksManager.bookmarks():
+            return QModelIndex()
+        
+        # get the parent's row
+        grandParentNode = parentNode.parent()
+        parentRow = grandParentNode.children().index(parentNode)
+        return self.createIndex(parentRow, 0, parentNode)
+    
+    def hasChildren(self, parent=QModelIndex()):
+        """
+        Public method to check, if a parent node has some children.
+        
+        @param parent index of the parent node (QModelIndex)
+        @return flag indicating the presence of children (boolean)
+        """
+        if not parent.isValid():
+            return True
+        
+        from .BookmarkNode import BookmarkNode
+        parentNode = self.node(parent)
+        return parentNode.type() == BookmarkNode.Folder
+    
+    def flags(self, index):
+        """
+        Public method to get flags for a node cell.
+        
+        @param index index of the node cell (QModelIndex)
+        @return flags (Qt.ItemFlags)
+        """
+        if not index.isValid():
+            return Qt.NoItemFlags
+        
+        node = self.node(index)
+        type_ = node.type()
+        flags = Qt.ItemIsSelectable | Qt.ItemIsEnabled
+        
+        if self.hasChildren(index):
+            flags |= Qt.ItemIsDropEnabled
+        
+        if node == self.__bookmarksManager.menu() or \
+           node == self.__bookmarksManager.toolbar():
+            return flags
+        
+        flags |= Qt.ItemIsDragEnabled
+        
+        from .BookmarkNode import BookmarkNode
+        if (index.column() == 0 and type_ != BookmarkNode.Separator) or \
+           (index.column() == 1 and type_ == BookmarkNode.Bookmark):
+            flags |= Qt.ItemIsEditable
+        
+        return flags
+    
+    def supportedDropActions(self):
+        """
+        Public method to report the supported drop actions.
+        
+        @return supported drop actions (Qt.DropAction)
+        """
+        return Qt.CopyAction | Qt.MoveAction
+    
+    def mimeTypes(self):
+        """
+        Public method to report the supported mime types.
+        
+        @return supported mime types (list of strings)
+        """
+        return [self.MIMETYPE, "text/uri-list"]
+    
+    def mimeData(self, indexes):
+        """
+        Public method to return the mime data.
+        
+        @param indexes list of indexes (QModelIndexList)
+        @return mime data (QMimeData)
+        """
+        from .XbelWriter import XbelWriter
+        
+        data = QByteArray()
+        stream = QDataStream(data, QIODevice.WriteOnly)
+        urls = []
+        
+        for index in indexes:
+            if index.column() != 0 or not index.isValid():
+                continue
+            
+            encodedData = QByteArray()
+            buffer = QBuffer(encodedData)
+            buffer.open(QIODevice.ReadWrite)
+            writer = XbelWriter()
+            parentNode = self.node(index)
+            writer.write(buffer, parentNode)
+            stream << encodedData
+            urls.append(index.data(self.UrlRole))
+        
+        mdata = QMimeData()
+        mdata.setData(self.MIMETYPE, data)
+        mdata.setUrls(urls)
+        return mdata
+    
+    def dropMimeData(self, data, action, row, column, parent):
+        """
+        Public method to accept the mime data of a drop action.
+        
+        @param data reference to the mime data (QMimeData)
+        @param action drop action requested (Qt.DropAction)
+        @param row row number (integer)
+        @param column column number (integer)
+        @param parent index of the parent node (QModelIndex)
+        @return flag indicating successful acceptance of the data (boolean)
+        """
+        if action == Qt.IgnoreAction:
+            return True
+        
+        if column > 0:
+            return False
+        
+        parentNode = self.node(parent)
+        
+        if not data.hasFormat(self.MIMETYPE):
+            if not data.hasUrls():
+                return False
+            
+            from .BookmarkNode import BookmarkNode
+            node = BookmarkNode(BookmarkNode.Bookmark, parentNode)
+            node.url = bytes(data.urls()[0].toEncoded()).decode()
+            
+            if data.hasText():
+                node.title = data.text()
+            else:
+                node.title = node.url
+            
+            self.__bookmarksManager.addBookmark(parentNode, node, row)
+            return True
+        
+        ba = data.data(self.MIMETYPE)
+        stream = QDataStream(ba, QIODevice.ReadOnly)
+        if stream.atEnd():
+            return False
+        
+        undoStack = self.__bookmarksManager.undoRedoStack()
+        undoStack.beginMacro("Move Bookmarks")
+        
+        from .XbelReader import XbelReader
+        while not stream.atEnd():
+            encodedData = QByteArray()
+            stream >> encodedData
+            buffer = QBuffer(encodedData)
+            buffer.open(QIODevice.ReadOnly)
+            
+            reader = XbelReader()
+            rootNode = reader.read(buffer)
+            for bookmarkNode in rootNode.children():
+                rootNode.remove(bookmarkNode)
+                row = max(0, row)
+                self.__bookmarksManager.addBookmark(
+                    parentNode, bookmarkNode, row)
+                self.__endMacro = True
+        
+        return True
+    
+    def setData(self, index, value, role=Qt.EditRole):
+        """
+        Public method to set the data of a node cell.
+        
+        @param index index of the node cell (QModelIndex)
+        @param value value to be set
+        @param role role of the data (integer)
+        @return flag indicating success (boolean)
+        """
+        if not index.isValid() or (self.flags(index) & Qt.ItemIsEditable) == 0:
+            return False
+        
+        item = self.node(index)
+        
+        if role in (Qt.EditRole, Qt.DisplayRole):
+            if index.column() == 0:
+                self.__bookmarksManager.setTitle(item, value)
+            elif index.column() == 1:
+                self.__bookmarksManager.setUrl(item, value)
+            else:
+                return False
+        
+        elif role == BookmarksModel.UrlRole:
+            self.__bookmarksManager.setUrl(item, value.toString())
+        
+        elif role == BookmarksModel.UrlStringRole:
+            self.__bookmarksManager.setUrl(item, value)
+        
+        else:
+            return False
+        
+        return True
+    
+    def node(self, index):
+        """
+        Public method to get a bookmark node given its index.
+        
+        @param index index of the node (QModelIndex)
+        @return bookmark node (BookmarkNode)
+        """
+        itemNode = index.internalPointer()
+        if itemNode is None:
+            return self.__bookmarksManager.bookmarks()
+        else:
+            return itemNode
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Bookmarks/BookmarksToolBar.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,240 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a tool bar showing bookmarks.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSignal, Qt, QUrl, QCoreApplication
+from PyQt5.QtGui import QCursor
+from PyQt5.QtWidgets import QMenu
+from PyQt5.QtWebEngineWidgets import QWebEnginePage
+
+from E5Gui.E5ModelToolBar import E5ModelToolBar
+
+from .BookmarksModel import BookmarksModel
+
+
+class BookmarksToolBar(E5ModelToolBar):
+    """
+    Class implementing a tool bar showing bookmarks.
+    
+    @signal openUrl(QUrl, str) emitted to open a URL in the current tab
+    @signal newUrl(QUrl, str) emitted to open a URL in a new tab
+    """
+    openUrl = pyqtSignal(QUrl, str)
+    newUrl = pyqtSignal(QUrl, str)
+    
+    def __init__(self, mainWindow, model, parent=None):
+        """
+        Constructor
+        
+        @param mainWindow reference to the main window (HelpWindow)
+        @param model reference to the bookmarks model (BookmarksModel)
+        @param parent reference to the parent widget (QWidget)
+        """
+        E5ModelToolBar.__init__(
+            self, QCoreApplication.translate("BookmarksToolBar", "Bookmarks"),
+            parent)
+        
+        self.__mw = mainWindow
+        self.__bookmarksModel = model
+        
+        self.__mw.bookmarksManager().bookmarksReloaded.connect(self.__rebuild)
+        
+        self.setModel(model)
+        self.setRootIndex(model.nodeIndex(
+            self.__mw.bookmarksManager().toolbar()))
+        
+        self.setContextMenuPolicy(Qt.CustomContextMenu)
+        self.customContextMenuRequested.connect(self.__contextMenuRequested)
+        self.activated.connect(self.__bookmarkActivated)
+        
+        self.setHidden(True)
+        self.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
+        
+        self._build()
+    
+    def __rebuild(self):
+        """
+        Private slot to rebuild the toolbar.
+        """
+        self.__bookmarksModel = \
+            self.__mw.bookmarksManager().bookmarksModel()
+        self.setModel(self.__bookmarksModel)
+        self.setRootIndex(self.__bookmarksModel.nodeIndex(
+            self.__mw.bookmarksManager().toolbar()))
+        self._build()
+    
+    def __contextMenuRequested(self, pos):
+        """
+        Private slot to handle the context menu request.
+        
+        @param pos position the context menu shall be shown (QPoint)
+        """
+        act = self.actionAt(pos)
+        menu = QMenu()
+        
+        if act is not None:
+            v = act.data()
+            
+            if act.menu() is None:
+                menu.addAction(
+                    self.tr("Open"),
+                    self.__openBookmark).setData(v)
+                menu.addAction(
+                    self.tr("Open in New Tab\tCtrl+LMB"),
+                    self.__openBookmarkInNewTab).setData(v)
+                menu.addAction(
+                    self.tr("Open in New Window"),
+                    self.__openBookmarkInNewWindow).setData(v)
+                menu.addAction(
+                    self.tr("Open in New Private Window"),
+                    self.__openBookmarkInPrivateWindow).setData(v)
+                menu.addSeparator()
+            
+            menu.addAction(
+                self.tr("Remove"),
+                self.__removeBookmark).setData(v)
+            menu.addSeparator()
+            
+            menu.addAction(
+                self.tr("Properties..."),
+                self.__edit).setData(v)
+            menu.addSeparator()
+        
+        menu.addAction(self.tr("Add Bookmark..."), self.__newBookmark)
+        menu.addAction(self.tr("Add Folder..."), self.__newFolder)
+        
+        menu.exec_(QCursor.pos())
+    
+    def __bookmarkActivated(self, idx):
+        """
+        Private slot handling the activation of a bookmark.
+        
+        @param idx index of the activated bookmark (QModelIndex)
+        """
+        assert idx.isValid()
+        
+        if self._mouseButton == Qt.XButton1:
+            self.__mw.currentBrowser().pageAction(QWebEnginePage.Back).trigger()
+        elif self._mouseButton == Qt.XButton2:
+            self.__mw.currentBrowser().pageAction(QWebEnginePage.Forward).trigger()
+        elif self._mouseButton == Qt.LeftButton:
+            if self._keyboardModifiers & Qt.ControlModifier:
+                self.newUrl.emit(
+                    idx.data(BookmarksModel.UrlRole),
+                    idx.data(Qt.DisplayRole))
+            else:
+                self.openUrl.emit(
+                    idx.data(BookmarksModel.UrlRole),
+                    idx.data(Qt.DisplayRole))
+    
+    def __openToolBarBookmark(self):
+        """
+        Private slot to open a bookmark in the current browser tab.
+        """
+        idx = self.index(self.sender())
+        
+        if self._keyboardModifiers & Qt.ControlModifier:
+            self.newUrl.emit(
+                idx.data(BookmarksModel.UrlRole),
+                idx.data(Qt.DisplayRole))
+        else:
+            self.openUrl.emit(
+                idx.data(BookmarksModel.UrlRole),
+                idx.data(Qt.DisplayRole))
+        self.resetFlags()
+    
+    def __openBookmark(self):
+        """
+        Private slot to open a bookmark in the current browser tab.
+        """
+        idx = self.index(self.sender())
+        
+        self.openUrl.emit(
+            idx.data(BookmarksModel.UrlRole),
+            idx.data(Qt.DisplayRole))
+    
+    def __openBookmarkInNewTab(self):
+        """
+        Private slot to open a bookmark in a new browser tab.
+        """
+        idx = self.index(self.sender())
+        
+        self.newUrl.emit(
+            idx.data(BookmarksModel.UrlRole),
+            idx.data(Qt.DisplayRole))
+    
+    def __openBookmarkInNewWindow(self):
+        """
+        Private slot to open a bookmark in a new window.
+        """
+        idx = self.index(self.sender())
+        url = idx.data(BookmarksModel.UrlRole)
+        
+        from WebBrowser.WebBrowserWindow import WebBrowserWindow
+        WebBrowserWindow.mainWindow().newWindow(url)
+    
+    def __openBookmarkInPrivateWindow(self):
+        """
+        Private slot to open a bookmark in a new private window.
+        """
+        idx = self.index(self.sender())
+        url = idx.data(BookmarksModel.UrlRole)
+        
+        from WebBrowser.WebBrowserWindow import WebBrowserWindow
+        WebBrowserWindow.mainWindow().newPrivateWindow(url)
+    
+    def __removeBookmark(self):
+        """
+        Private slot to remove a bookmark.
+        """
+        idx = self.index(self.sender())
+        
+        self.__bookmarksModel.removeRow(idx.row(), self.rootIndex())
+    
+    def __newBookmark(self):
+        """
+        Private slot to add a new bookmark.
+        """
+        from .AddBookmarkDialog import AddBookmarkDialog
+        dlg = AddBookmarkDialog()
+        dlg.setCurrentIndex(self.rootIndex())
+        dlg.exec_()
+    
+    def __newFolder(self):
+        """
+        Private slot to add a new bookmarks folder.
+        """
+        from .AddBookmarkDialog import AddBookmarkDialog
+        dlg = AddBookmarkDialog()
+        dlg.setCurrentIndex(self.rootIndex())
+        dlg.setFolder(True)
+        dlg.exec_()
+    
+    def _createMenu(self):
+        """
+        Protected method to create the menu for a tool bar action.
+        
+        @return menu for a tool bar action (E5ModelMenu)
+        """
+        from .BookmarksMenu import BookmarksMenu
+        menu = BookmarksMenu(self)
+        menu.openUrl.connect(self.openUrl)
+        menu.newUrl.connect(self.newUrl)
+        return menu
+    
+    def __edit(self):
+        """
+        Private slot to edit a bookmarks properties.
+        """
+        from .BookmarkPropertiesDialog import BookmarkPropertiesDialog
+        idx = self.index(self.sender())
+        node = self.__bookmarksModel.node(idx)
+        dlg = BookmarkPropertiesDialog(node)
+        dlg.exec_()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Bookmarks/DefaultBookmarks.qrc	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,6 @@
+<!DOCTYPE RCC>
+<RCC version="1.0">
+<qresource>
+  <file>DefaultBookmarks.xbel</file>
+</qresource>
+</RCC>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Bookmarks/DefaultBookmarks.xbel	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE xbel>
+<xbel version="1.0">
+    <folder folded="no">
+        <title>Bookmarks Bar</title>
+        <bookmark href="http://eric-ide.python-projects.org/">
+            <title>Eric Web Site</title>
+        </bookmark>
+        <bookmark href="http://www.riverbankcomputing.com/">
+            <title>PyQt Web Site</title>
+        </bookmark>
+        <folder folded="no">
+            <title>Qt Web Sites</title>
+            <bookmark href="http://www.qt.io//">
+                <title>Qt Web Site</title>
+            </bookmark>
+            <bookmark href="http://www.qt.io/developers/">
+                <title>Qt Developers</title>
+            </bookmark>
+            <bookmark href="http://doc.qt.io/">
+                <title>Qt Documentation</title>
+            </bookmark>
+            <bookmark href="http://blog.qt.io/">
+                <title>Qt Blog</title>
+            </bookmark>
+            <bookmark href="http://forum.qt.io/">
+                <title>Qt Forum</title>
+            </bookmark>
+            <bookmark href="http://planet.qt.io/">
+                <title>Planet Qt</title>
+            </bookmark>
+            <bookmark href="http://qtcentre.org/">
+                <title>Qt Centre</title>
+            </bookmark>
+            <bookmark href="http://qt-apps.org/">
+                <title>Qt-Apps.org</title>
+            </bookmark>
+        </folder>
+        <folder folded="no">
+            <title>Python Web Sites</title>
+            <bookmark href="http://www.python.org/">
+                <title>Python Language Website</title>
+            </bookmark>
+            <bookmark href="http://pypi.python.org/pypi">
+                <title>Python Package Index: PyPI</title>
+            </bookmark>
+        </folder>
+    </folder>
+    <folder folded="yes">
+        <title>Bookmarks Menu</title>
+        <bookmark href="http://eric-ide.python-projects.org/">
+            <title>Eric Web Site</title>
+        </bookmark>
+        <bookmark href="http://www.riverbankcomputing.com/">
+            <title>PyQt4 Web Site</title>
+        </bookmark>
+        <bookmark href="javascript:location.href='mailto:?SUBJECT=' + document.title + '&amp;BODY=' + escape(location.href);">
+            <title>Send Link</title>
+        </bookmark>
+    </folder>
+</xbel>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Bookmarks/DefaultBookmarks_rc.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,67 @@
+# -*- coding: utf-8 -*-
+
+# Resource object code
+#
+# Created by: The Resource Compiler for PyQt5 (Qt v5.4.1)
+#
+# WARNING! All changes made in this file will be lost!
+
+from PyQt5 import QtCore
+
+qt_resource_data = b"\
+\x00\x00\x01\xf1\
+\x00\
+\x00\x09\x00\x78\x9c\xdd\x96\x51\x6f\x9b\x30\x10\xc7\xdf\xfb\x29\
+\x3c\x1e\x9a\x4d\x15\xd0\x49\x7b\x98\x52\x48\x34\x92\x4c\xea\xd4\
+\xaa\x54\x69\x55\xf5\xd1\x98\x0b\x71\x01\xdb\x35\x26\x09\xdf\x7e\
+\x86\xb0\x96\xa4\x2c\xa4\x1d\x4f\xe3\xc5\xd8\x77\xbe\xdf\x9d\x8d\
+\xff\xc6\x19\x6f\xd2\x04\xad\x40\x66\x94\x33\xd7\xf8\x6a\x9d\x1b\
+\x08\x18\xe1\x21\x65\x91\x6b\xe4\x6a\x61\x7e\x37\xc6\xa3\x13\xe7\
+\xd3\xf4\x66\x72\xf7\xe8\xcf\xd0\x26\x80\x44\xf7\xcb\x66\x77\xda\
+\xe8\x04\xe9\xc7\x59\xf0\x24\x04\x89\xaa\x26\x74\x0d\xc6\x6b\x43\
+\x65\x54\x54\x25\x30\xf2\x38\x8f\x53\x2c\xe3\x0c\x79\x58\x3a\xf6\
+\x76\xf0\xd5\x29\xa8\xcd\x68\x29\x61\xe1\x1a\x4b\xa5\xc4\xd0\xb6\
+\x41\x52\x62\xd2\x10\x2c\x51\xa8\x25\x67\xa6\x90\xfc\x09\x88\xca\
+\x2c\x2e\x23\xbb\xc1\x68\x70\x66\x7a\x0a\x7a\x80\x00\xcd\xa9\x82\
+\xb7\x1c\xfb\x0f\xa8\x93\xbd\x5e\xaf\x2d\x49\x75\xb5\x01\x66\x31\
+\xe1\xa9\xc8\x95\x5e\x1e\x4b\xbf\xfd\x85\xec\x17\xb7\xea\x9d\xe4\
+\x43\xeb\xd6\x88\xdc\x88\x9b\xbd\x09\xdc\x51\xc2\xb3\xb2\x28\xb7\
+\xf7\x53\x6e\x0f\xde\x1e\xbb\x25\xf1\xa3\x98\x21\xac\x20\xe1\x42\
+\x7f\x2e\x87\xe9\xd3\x17\xbf\x3e\xf8\x21\x27\x35\xff\x30\x94\x93\
+\x3c\x05\xa6\xb0\xd2\xdf\x72\x1f\xdc\x20\xe1\xd1\x31\x60\x4f\xfb\
+\xf5\xc1\x5b\x70\x99\xa7\xc7\x00\x7f\x96\x8e\x7d\x10\x45\x82\x19\
+\xa8\x4e\xa4\x5f\xb9\xa1\x5b\xd5\x07\xf3\x59\x11\xbd\x49\x12\xda\
+\x0e\xfc\x6e\x99\x93\xca\xaf\x1f\xa6\x89\x85\x68\xd5\x98\x1d\xa4\
+\xf9\xa3\xf6\x3a\x1a\xea\xd8\xdb\x03\xff\x7e\x05\xf0\x2b\xfd\xfb\
+\xb8\x0a\x6c\xf5\xb3\xa3\xa4\x1a\x72\x85\x59\x94\xe3\x08\x4a\x5a\
+\xd6\x93\x2a\x88\x42\xd0\x66\x12\x65\xbf\x33\x11\x1f\x93\xb8\xcc\
+\xe3\x92\x85\xb0\x19\x22\xbf\xf0\x2f\x3f\xb8\xd4\x7b\xbd\xbd\x45\
+\x2f\x20\x3b\x74\x5f\x5d\x03\xcb\xff\xdb\x0b\xeb\xdb\xbf\xa1\x9f\
+\xf0\x0a\x67\x44\x52\xa1\x86\x09\x27\x95\x98\x5a\x95\x65\x90\x62\
+\x9a\x28\x3e\x1c\xcf\xef\xbd\x5f\xb3\xc9\x9d\x3b\x40\x67\x28\xac\
+\x45\xd7\xaa\x48\x7a\x60\x70\x8a\x53\x71\xe1\xdd\x4c\x1f\x2b\x3b\
+\x64\x04\x0b\xf8\xbc\x13\xe9\xcb\x45\x7b\xf2\x73\x60\x21\xba\xa2\
+\x2c\xee\xcc\xfb\x75\xf3\x1d\x7b\xfb\x23\xf3\x1b\xc5\xa5\x8d\x58\
+\
+"
+
+qt_resource_name = b"\
+\x00\x15\
+\x0c\xd3\x2e\x3c\
+\x00\x44\
+\x00\x65\x00\x66\x00\x61\x00\x75\x00\x6c\x00\x74\x00\x42\x00\x6f\x00\x6f\x00\x6b\x00\x6d\x00\x61\x00\x72\x00\x6b\x00\x73\x00\x2e\
+\x00\x78\x00\x62\x00\x65\x00\x6c\
+"
+
+qt_resource_struct = b"\
+\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
+\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\
+"
+
+def qInitResources():
+    QtCore.qRegisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data)
+
+def qCleanupResources():
+    QtCore.qUnregisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data)
+
+qInitResources()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Bookmarks/NsHtmlReader.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,133 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a class to read Netscape HTML bookmark files.
+"""
+
+from __future__ import unicode_literals
+try:
+    str = unicode
+except NameError:
+    pass
+
+from PyQt5.QtCore import QObject, QIODevice, QFile, QRegExp, Qt, QDateTime
+
+from .BookmarkNode import BookmarkNode
+
+import Utilities
+
+
+class NsHtmlReader(QObject):
+    """
+    Class implementing a reader object for Netscape HTML bookmark files.
+    """
+    indentSize = 4
+    
+    def __init__(self):
+        """
+        Constructor
+        """
+        super(NsHtmlReader, self).__init__()
+        
+        self.__folderRx = QRegExp("<DT><H3(.*)>(.*)</H3>", Qt.CaseInsensitive)
+        self.__folderRx.setMinimal(True)
+        
+        self.__endFolderRx = QRegExp("</DL>", Qt.CaseInsensitive)
+        
+        self.__bookmarkRx = QRegExp("<DT><A(.*)>(.*)</A>", Qt.CaseInsensitive)
+        self.__bookmarkRx.setMinimal(True)
+        
+        self.__descRx = QRegExp("<DD>(.*)", Qt.CaseInsensitive)
+        
+        self.__separatorRx = QRegExp("<HR>", Qt.CaseInsensitive)
+        
+        self.__urlRx = QRegExp('HREF="(.*)"', Qt.CaseInsensitive)
+        self.__urlRx.setMinimal(True)
+        
+        self.__addedRx = QRegExp('ADD_DATE="(\d*)"', Qt.CaseInsensitive)
+        self.__addedRx.setMinimal(True)
+        
+        self.__modifiedRx = QRegExp(
+            'LAST_MODIFIED="(\d*)"', Qt.CaseInsensitive)
+        self.__modifiedRx.setMinimal(True)
+        
+        self.__visitedRx = QRegExp('LAST_VISIT="(\d*)"', Qt.CaseInsensitive)
+        self.__visitedRx.setMinimal(True)
+        
+        self.__foldedRx = QRegExp("FOLDED", Qt.CaseInsensitive)
+    
+    def read(self, fileNameOrDevice):
+        """
+        Public method to read a Netscape HTML bookmark file.
+        
+        @param fileNameOrDevice name of the file to read (string)
+            or reference to the device to read (QIODevice)
+        @return reference to the root node (BookmarkNode)
+        """
+        if isinstance(fileNameOrDevice, QIODevice):
+            dev = fileNameOrDevice
+        else:
+            f = QFile(fileNameOrDevice)
+            if not f.exists():
+                return BookmarkNode(BookmarkNode.Root)
+            f.open(QFile.ReadOnly)
+            dev = f
+        
+        folders = []
+        lastNode = None
+        
+        root = BookmarkNode(BookmarkNode.Root)
+        folders.append(root)
+        
+        while not dev.atEnd():
+            line = str(dev.readLine(), encoding="utf-8").rstrip()
+            if self.__folderRx.indexIn(line) != -1:
+                # folder definition
+                arguments = self.__folderRx.cap(1)
+                name = self.__folderRx.cap(2)
+                node = BookmarkNode(BookmarkNode.Folder, folders[-1])
+                node.title = Utilities.html_udecode(name)
+                node.expanded = self.__foldedRx.indexIn(arguments) == -1
+                if self.__addedRx.indexIn(arguments) != -1:
+                    node.added = QDateTime.fromTime_t(
+                        int(self.__addedRx.cap(1)))
+                folders.append(node)
+                lastNode = node
+            
+            elif self.__endFolderRx.indexIn(line) != -1:
+                # end of folder definition
+                folders.pop()
+            
+            elif self.__bookmarkRx.indexIn(line) != -1:
+                # bookmark definition
+                arguments = self.__bookmarkRx.cap(1)
+                name = self.__bookmarkRx.cap(2)
+                node = BookmarkNode(BookmarkNode.Bookmark, folders[-1])
+                node.title = Utilities.html_udecode(name)
+                if self.__urlRx.indexIn(arguments) != -1:
+                    node.url = self.__urlRx.cap(1)
+                if self.__addedRx.indexIn(arguments) != -1:
+                    node.added = QDateTime.fromTime_t(
+                        int(self.__addedRx.cap(1)))
+                if self.__modifiedRx.indexIn(arguments) != -1:
+                    node.modified = QDateTime.fromTime_t(
+                        int(self.__modifiedRx.cap(1)))
+                if self.__visitedRx.indexIn(arguments) != -1:
+                    node.visited = QDateTime.fromTime_t(
+                        int(self.__visitedRx.cap(1)))
+                lastNode = node
+            
+            elif self.__descRx.indexIn(line) != -1:
+                # description
+                if lastNode:
+                    lastNode.desc = Utilities.html_udecode(
+                        self.__descRx.cap(1))
+            
+            elif self.__separatorRx.indexIn(line) != -1:
+                # separator definition
+                BookmarkNode(BookmarkNode.Separator, folders[-1])
+        
+        return root
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Bookmarks/NsHtmlWriter.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,166 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a class to write Netscape HTML bookmark files.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import QObject, QIODevice, QFile
+
+from .BookmarkNode import BookmarkNode
+
+import Utilities
+
+
+class NsHtmlWriter(QObject):
+    """
+    Class implementing a writer object to generate Netscape HTML bookmark
+    files.
+    """
+    indentSize = 4
+    
+    def __init__(self):
+        """
+        Constructor
+        """
+        super(NsHtmlWriter, self).__init__()
+    
+    def write(self, fileNameOrDevice, root):
+        """
+        Public method to write an Netscape HTML bookmark file.
+        
+        @param fileNameOrDevice name of the file to write (string)
+            or device to write to (QIODevice)
+        @param root root node of the bookmark tree (BookmarkNode)
+        @return flag indicating success (boolean)
+        """
+        if isinstance(fileNameOrDevice, QIODevice):
+            f = fileNameOrDevice
+        else:
+            f = QFile(fileNameOrDevice)
+            if root is None or not f.open(QFile.WriteOnly):
+                return False
+        
+        self.__dev = f
+        return self.__write(root)
+    
+    def __write(self, root):
+        """
+        Private method to write an Netscape HTML bookmark file.
+        
+        @param root root node of the bookmark tree (BookmarkNode)
+        @return flag indicating success (boolean)
+        """
+        self.__dev.write(
+            "<!DOCTYPE NETSCAPE-Bookmark-file-1>\n"
+            "<!-- This is an automatically generated file.\n"
+            "     It will be read and overwritten.\n"
+            "     DO NOT EDIT! -->\n"
+            "<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html;"
+            " charset=UTF-8\">\n"
+            "<TITLE>Bookmarks</TITLE>\n"
+            "<H1>Bookmarks</H1>\n"
+            "\n"
+            "<DL><p>\n")
+        if root.type() == BookmarkNode.Root:
+            for child in root.children():
+                self.__writeItem(child, self.indentSize)
+        else:
+            self.__writeItem(root, self.indentSize)
+        self.__dev.write("</DL><p>\n")
+        return True
+    
+    def __writeItem(self, node, indent):
+        """
+        Private method to write an entry for a node.
+        
+        @param node reference to the node to be written (BookmarkNode)
+        @param indent size of the indentation (integer)
+        """
+        if node.type() == BookmarkNode.Folder:
+            self.__writeFolder(node, indent)
+        elif node.type() == BookmarkNode.Bookmark:
+            self.__writeBookmark(node, indent)
+        elif node.type() == BookmarkNode.Separator:
+            self.__writeSeparator(indent)
+    
+    def __writeSeparator(self, indent):
+        """
+        Private method to write a separator.
+        
+        @param indent size of the indentation (integer)
+        """
+        self.__dev.write(" " * indent)
+        self.__dev.write("<HR>\n")
+    
+    def __writeBookmark(self, node, indent):
+        """
+        Private method to write a bookmark node.
+        
+        @param node reference to the node to be written (BookmarkNode)
+        @param indent size of the indentation (integer)
+        """
+        if node.added.isValid():
+            added = " ADD_DATE=\"{0}\"".format(node.added.toTime_t())
+        else:
+            added = ""
+        if node.modified.isValid():
+            modified = " LAST_MODIFIED=\"{0}\"".format(
+                node.modified.toTime_t())
+        else:
+            modified = ""
+        if node.visited.isValid():
+            visited = " LAST_VISIT=\"{0}\"".format(node.visited.toTime_t())
+        else:
+            visited = ""
+        
+        self.__dev.write(" " * indent)
+        self.__dev.write("<DT><A HREF=\"{0}\"{1}{2}{3}>{4}</A>\n".format(
+            node.url, added, modified, visited,
+            Utilities.html_uencode(node.title)
+        ))
+        
+        if node.desc:
+            self.__dev.write(" " * indent)
+            self.__dev.write("<DD>{0}\n".format(
+                Utilities.html_uencode("".join(node.desc.splitlines()))))
+    
+    def __writeFolder(self, node, indent):
+        """
+        Private method to write a bookmark node.
+        
+        @param node reference to the node to be written (BookmarkNode)
+        @param indent size of the indentation (integer)
+        """
+        if node.expanded:
+            folded = ""
+        else:
+            folded = " FOLDED"
+        
+        if node.added.isValid():
+            added = " ADD_DATE=\"{0}\"".format(node.added.toTime_t())
+        else:
+            added = ""
+        
+        self.__dev.write(" " * indent)
+        self.__dev.write("<DT><H3{0}{1}>{2}</H3>\n".format(
+            folded, added, Utilities.html_uencode(node.title)
+        ))
+        
+        if node.desc:
+            self.__dev.write(" " * indent)
+            self.__dev.write("<DD>{0}\n".format(
+                "".join(node.desc.splitlines())))
+        
+        self.__dev.write(" " * indent)
+        self.__dev.write("<DL><p>\n")
+        
+        for child in node.children():
+            self.__writeItem(child, indent + self.indentSize)
+        
+        self.__dev.write(" " * indent)
+        self.__dev.write("</DL><p>\n")
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Bookmarks/XbelReader.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,235 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a class to read XBEL bookmark files.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import QXmlStreamReader, QXmlStreamEntityResolver, \
+    QIODevice, QFile, QCoreApplication, QXmlStreamNamespaceDeclaration, \
+    QDateTime, Qt
+
+from .BookmarkNode import BookmarkNode
+
+
+class XmlEntityResolver(QXmlStreamEntityResolver):
+    """
+    Class implementing an XML entity resolver for bookmark files.
+    """
+    def resolveUndeclaredEntity(self, entity):
+        """
+        Public method to resolve undeclared entities.
+        
+        @param entity entity to be resolved (string)
+        @return resolved entity (string)
+        """
+        if entity == "nbsp":
+            return " "
+        return ""
+
+
+class XbelReader(QXmlStreamReader):
+    """
+    Class implementing a reader object for XBEL bookmark files.
+    """
+    def __init__(self):
+        """
+        Constructor
+        """
+        super(XbelReader, self).__init__()
+        
+        self.__resolver = XmlEntityResolver()
+        self.setEntityResolver(self.__resolver)
+    
+    def read(self, fileNameOrDevice):
+        """
+        Public method to read an XBEL bookmark file.
+        
+        @param fileNameOrDevice name of the file to read (string)
+            or reference to the device to read (QIODevice)
+        @return reference to the root node (BookmarkNode)
+        """
+        if isinstance(fileNameOrDevice, QIODevice):
+            self.setDevice(fileNameOrDevice)
+        else:
+            f = QFile(fileNameOrDevice)
+            if not f.exists():
+                return BookmarkNode(BookmarkNode.Root)
+            f.open(QFile.ReadOnly)
+            self.setDevice(f)
+        
+        root = BookmarkNode(BookmarkNode.Root)
+        while not self.atEnd():
+            self.readNext()
+            if self.isStartElement():
+                version = self.attributes().value("version")
+                if self.name() == "xbel" and \
+                   (not version or version == "1.0"):
+                    self.__readXBEL(root)
+                else:
+                    self.raiseError(QCoreApplication.translate(
+                        "XbelReader",
+                        "The file is not an XBEL version 1.0 file."))
+        
+        return root
+    
+    def __readXBEL(self, node):
+        """
+        Private method to read and parse the XBEL file.
+        
+        @param node reference to the node to attach to (BookmarkNode)
+        """
+        if not self.isStartElement() and self.name() != "xbel":
+            return
+        
+        while not self.atEnd():
+            self.readNext()
+            if self.isEndElement():
+                break
+            
+            if self.isStartElement():
+                if self.name() == "folder":
+                    self.__readFolder(node)
+                elif self.name() == "bookmark":
+                    self.__readBookmarkNode(node)
+                elif self.name() == "separator":
+                    self.__readSeparator(node)
+                else:
+                    self.__skipUnknownElement()
+    
+    def __readFolder(self, node):
+        """
+        Private method to read and parse a folder subtree.
+        
+        @param node reference to the node to attach to (BookmarkNode)
+        """
+        if not self.isStartElement() and self.name() != "folder":
+            return
+        
+        folder = BookmarkNode(BookmarkNode.Folder, node)
+        folder.expanded = self.attributes().value("folded") == "no"
+        folder.added = QDateTime.fromString(
+            self.attributes().value("added"), Qt.ISODate)
+        
+        while not self.atEnd():
+            self.readNext()
+            if self.isEndElement():
+                break
+            
+            if self.isStartElement():
+                if self.name() == "title":
+                    self.__readTitle(folder)
+                elif self.name() == "desc":
+                    self.__readDescription(folder)
+                elif self.name() == "folder":
+                    self.__readFolder(folder)
+                elif self.name() == "bookmark":
+                    self.__readBookmarkNode(folder)
+                elif self.name() == "separator":
+                    self.__readSeparator(folder)
+                elif self.name() == "info":
+                    self.__readInfo()
+                else:
+                    self.__skipUnknownElement()
+    
+    def __readTitle(self, node):
+        """
+        Private method to read the title element.
+        
+        @param node reference to the bookmark node title belongs to
+            (BookmarkNode)
+        """
+        if not self.isStartElement() and self.name() != "title":
+            return
+        
+        node.title = self.readElementText()
+    
+    def __readDescription(self, node):
+        """
+        Private method to read the desc element.
+        
+        @param node reference to the bookmark node desc belongs to
+            (BookmarkNode)
+        """
+        if not self.isStartElement() and self.name() != "desc":
+            return
+        
+        node.desc = self.readElementText()
+    
+    def __readSeparator(self, node):
+        """
+        Private method to read a separator element.
+        
+        @param node reference to the bookmark node the separator belongs to
+            (BookmarkNode)
+        """
+        sep = BookmarkNode(BookmarkNode.Separator, node)
+        sep.added = QDateTime.fromString(
+            self.attributes().value("added"), Qt.ISODate)
+        
+        # empty elements have a start and end element
+        while not self.atEnd():
+            self.readNext()
+            if self.isEndElement():
+                break
+            
+            if self.isStartElement():
+                if self.name() == "info":
+                    self.__readInfo()
+                else:
+                    self.__skipUnknownElement()
+    
+    def __readBookmarkNode(self, node):
+        """
+        Private method to read and parse a bookmark subtree.
+        
+        @param node reference to the node to attach to (BookmarkNode)
+        """
+        if not self.isStartElement() and self.name() != "bookmark":
+            return
+        
+        bookmark = BookmarkNode(BookmarkNode.Bookmark, node)
+        bookmark.url = self.attributes().value("href")
+        bookmark.added = QDateTime.fromString(
+            self.attributes().value("added"), Qt.ISODate)
+        bookmark.modified = QDateTime.fromString(
+            self.attributes().value("modified"), Qt.ISODate)
+        bookmark.visited = QDateTime.fromString(
+            self.attributes().value("visited"), Qt.ISODate)
+        
+        while not self.atEnd():
+            self.readNext()
+            if self.isEndElement():
+                break
+            
+            if self.isStartElement():
+                if self.name() == "title":
+                    self.__readTitle(bookmark)
+                elif self.name() == "desc":
+                    self.__readDescription(bookmark)
+                elif self.name() == "info":
+                    self.__readInfo()
+                else:
+                    self.__skipUnknownElement()
+        
+        if not bookmark.title:
+            bookmark.title = QCoreApplication.translate(
+                "XbelReader", "Unknown title")
+    
+    def __readInfo(self):
+        """
+        Private method to read and parse an info subtree.
+        """
+        self.addExtraNamespaceDeclaration(QXmlStreamNamespaceDeclaration(
+            "bookmark", "http://www.python.org"))
+        self.skipCurrentElement()
+    
+    def __skipUnknownElement(self):
+        """
+        Private method to skip over all unknown elements.
+        """
+        self.skipCurrentElement()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Bookmarks/XbelWriter.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,102 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a class to write XBEL bookmark files.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import QXmlStreamWriter, QIODevice, QFile, Qt
+
+from .BookmarkNode import BookmarkNode
+
+
+class XbelWriter(QXmlStreamWriter):
+    """
+    Class implementing a writer object to generate XBEL bookmark files.
+    """
+    def __init__(self):
+        """
+        Constructor
+        """
+        super(XbelWriter, self).__init__()
+        
+        self.setAutoFormatting(True)
+    
+    def write(self, fileNameOrDevice, root):
+        """
+        Public method to write an XBEL bookmark file.
+        
+        @param fileNameOrDevice name of the file to write (string)
+            or device to write to (QIODevice)
+        @param root root node of the bookmark tree (BookmarkNode)
+        @return flag indicating success (boolean)
+        """
+        if isinstance(fileNameOrDevice, QIODevice):
+            f = fileNameOrDevice
+        else:
+            f = QFile(fileNameOrDevice)
+            if root is None or not f.open(QFile.WriteOnly):
+                return False
+        
+        self.setDevice(f)
+        return self.__write(root)
+    
+    def __write(self, root):
+        """
+        Private method to write an XBEL bookmark file.
+        
+        @param root root node of the bookmark tree (BookmarkNode)
+        @return flag indicating success (boolean)
+        """
+        self.writeStartDocument()
+        self.writeDTD("<!DOCTYPE xbel>")
+        self.writeStartElement("xbel")
+        self.writeAttribute("version", "1.0")
+        if root.type() == BookmarkNode.Root:
+            for child in root.children():
+                self.__writeItem(child)
+        else:
+            self.__writeItem(root)
+        
+        self.writeEndDocument()
+        return True
+    
+    def __writeItem(self, node):
+        """
+        Private method to write an entry for a node.
+        
+        @param node reference to the node to be written (BookmarkNode)
+        """
+        if node.type() == BookmarkNode.Folder:
+            self.writeStartElement("folder")
+            if node.added.isValid():
+                self.writeAttribute("added", node.added.toString(Qt.ISODate))
+            self.writeAttribute("folded", node.expanded and "no" or "yes")
+            self.writeTextElement("title", node.title)
+            for child in node.children():
+                self.__writeItem(child)
+            self.writeEndElement()
+        elif node.type() == BookmarkNode.Bookmark:
+            self.writeStartElement("bookmark")
+            if node.url:
+                self.writeAttribute("href", node.url)
+            if node.added.isValid():
+                self.writeAttribute("added", node.added.toString(Qt.ISODate))
+            if node.modified.isValid():
+                self.writeAttribute(
+                    "modified", node.modified.toString(Qt.ISODate))
+            if node.visited.isValid():
+                self.writeAttribute(
+                    "visited", node.visited.toString(Qt.ISODate))
+            self.writeTextElement("title", node.title)
+            if node.desc:
+                self.writeTextElement("desc", node.desc)
+            self.writeEndElement()
+        elif node.type() == BookmarkNode.Separator:
+            self.writeEmptyElement("separator")
+            if node.added.isValid():
+                self.writeAttribute("added", node.added.toString(Qt.ISODate))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Bookmarks/__init__.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Package implementing the bookmarks system.
+"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/ClosedTabsManager.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,115 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a class to manage closed tabs.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSignal, QUrl, QObject
+
+
+class ClosedTab(object):
+    """
+    Class implementing a structure to store data about a closed tab.
+    """
+    def __init__(self, url=QUrl(), title="", position=-1):
+        """
+        Constructor
+        
+        @param url URL of the closed tab (QUrl)
+        @param title title of the closed tab (string)
+        @param position index of the closed tab (integer)
+        """
+        self.url = url
+        self.title = title
+        self.position = position
+    
+    def __eq__(self, other):
+        """
+        Special method implementing the equality operator.
+        
+        @param other reference to the object to compare against (ClosedTab)
+        @return flag indicating equality of the tabs (boolean)
+        """
+        return self.url == other.url and \
+            self.title == other.title and \
+            self.position == other.position
+
+
+class ClosedTabsManager(QObject):
+    """
+    Class implementing a manager for closed tabs.
+    
+    @signal closedTabAvailable(boolean) emitted to signal a change of
+        availability of closed tabs
+    """
+    closedTabAvailable = pyqtSignal(bool)
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object (QObject)
+        """
+        super(ClosedTabsManager, self).__init__()
+        
+        self.__closedTabs = []
+    
+    def recordBrowser(self, browser, position):
+        """
+        Public method to record the data of a browser about to be closed.
+        
+        @param browser reference to the browser to be closed (HelpBrowser)
+        @param position index of the tab to be closed (integer)
+        """
+        import WebBrowser.WebBrowserWindow
+        if WebBrowser.WebBrowserWindow.WebBrowserWindow.isPrivate():
+            return
+        
+        if browser.url().isEmpty():
+            return
+        
+        tab = ClosedTab(browser.url(), browser.title(), position)
+        self.__closedTabs.insert(0, tab)
+        self.closedTabAvailable.emit(True)
+    
+    def getClosedTabAt(self, index):
+        """
+        Public method to get the indexed closed tab.
+        
+        @param index index of the tab to return (integer)
+        @return requested tab (ClosedTab)
+        """
+        if len(self.__closedTabs) > 0 and len(self.__closedTabs) > index:
+            tab = self.__closedTabs.pop(index)
+        else:
+            tab = ClosedTab()
+        self.closedTabAvailable.emit(len(self.__closedTabs) > 0)
+        return tab
+    
+    def isClosedTabAvailable(self):
+        """
+        Public method to check for closed tabs.
+        
+        @return flag indicating the availability of closed tab data (boolean)
+        """
+        return len(self.__closedTabs) > 0
+    
+    def clearList(self):
+        """
+        Public method to clear the list of closed tabs.
+        """
+        self.__closedTabs = []
+        self.closedTabAvailable.emit(False)
+    
+    def allClosedTabs(self):
+        """
+        Public method to get a list of all closed tabs.
+        
+        @return list of closed tabs (list of ClosedTab)
+        """
+        return self.__closedTabs
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/CookieJar/CookieDetailsDialog.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,48 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog showing the cookie data.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import Qt
+from PyQt5.QtWidgets import QDialog
+
+from .Ui_CookieDetailsDialog import Ui_CookieDetailsDialog
+
+
+class CookieDetailsDialog(QDialog, Ui_CookieDetailsDialog):
+    """
+    Class implementing a dialog showing the cookie data.
+    """
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object (QWidget)
+        """
+        super(CookieDetailsDialog, self).__init__(parent)
+        self.setupUi(self)
+        self.setWindowFlags(Qt.Window)
+    
+    def setData(self, domain, name, path, secure, expires, value):
+        """
+        Public method to set the data to be shown.
+        
+        @param domain domain of the cookie (string)
+        @param name name of the cookie (string)
+        @param path path of the cookie (string)
+        @param secure flag indicating a secure cookie (boolean)
+        @param expires expiration time of the cookie (string)
+        @param value value of the cookie (string)
+        """
+        self.domainEdit.setText(domain)
+        self.nameEdit.setText(name)
+        self.pathEdit.setText(path)
+        self.secureCheckBox.setChecked(secure)
+        self.expirationEdit.setText(expires)
+        self.valueEdit.setPlainText(value)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/CookieJar/CookieDetailsDialog.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,167 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>CookieDetailsDialog</class>
+ <widget class="QDialog" name="CookieDetailsDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>400</width>
+    <height>300</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Cookie Details</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QFormLayout" name="formLayout">
+     <item row="0" column="0">
+      <widget class="QLabel" name="label">
+       <property name="text">
+        <string>Domain:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="1">
+      <widget class="QLineEdit" name="domainEdit">
+       <property name="readOnly">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="0">
+      <widget class="QLabel" name="label_2">
+       <property name="text">
+        <string>Name:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="1">
+      <widget class="QLineEdit" name="nameEdit">
+       <property name="readOnly">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="0">
+      <widget class="QLabel" name="label_3">
+       <property name="text">
+        <string>Path:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="1">
+      <widget class="QLineEdit" name="pathEdit">
+       <property name="readOnly">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+     <item row="3" column="0">
+      <widget class="QLabel" name="label_6">
+       <property name="text">
+        <string>Secure:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="3" column="1">
+      <widget class="QCheckBox" name="secureCheckBox">
+       <property name="text">
+        <string/>
+       </property>
+       <property name="checkable">
+        <bool>false</bool>
+       </property>
+      </widget>
+     </item>
+     <item row="4" column="0">
+      <widget class="QLabel" name="label_4">
+       <property name="text">
+        <string>Expires:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="4" column="1">
+      <widget class="QLineEdit" name="expirationEdit">
+       <property name="readOnly">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+     <item row="5" column="0">
+      <widget class="QLabel" name="label_5">
+       <property name="text">
+        <string>Contents:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="5" column="1">
+      <widget class="QPlainTextEdit" name="valueEdit">
+       <property name="readOnly">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Close</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <tabstops>
+  <tabstop>domainEdit</tabstop>
+  <tabstop>nameEdit</tabstop>
+  <tabstop>pathEdit</tabstop>
+  <tabstop>secureCheckBox</tabstop>
+  <tabstop>expirationEdit</tabstop>
+  <tabstop>valueEdit</tabstop>
+  <tabstop>buttonBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>CookieDetailsDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>CookieDetailsDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/CookieJar/CookieExceptionsModel.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,231 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the cookie exceptions model.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import Qt, QAbstractTableModel, QSize, QModelIndex
+from PyQt5.QtGui import QFont, QFontMetrics
+
+
+class CookieExceptionsModel(QAbstractTableModel):
+    """
+    Class implementing the cookie exceptions model.
+    """
+    def __init__(self, cookieJar, parent=None):
+        """
+        Constructor
+        
+        @param cookieJar reference to the cookie jar (CookieJar)
+        @param parent reference to the parent object (QObject)
+        """
+        super(CookieExceptionsModel, self).__init__(parent)
+        
+        self.__cookieJar = cookieJar
+        self.__allowedCookies = self.__cookieJar.allowedCookies()
+        self.__blockedCookies = self.__cookieJar.blockedCookies()
+        self.__sessionCookies = self.__cookieJar.allowForSessionCookies()
+        
+        self.__headers = [
+            self.tr("Website"),
+            self.tr("Status"),
+        ]
+    
+    def headerData(self, section, orientation, role):
+        """
+        Public method to get header data from the model.
+        
+        @param section section number (integer)
+        @param orientation orientation (Qt.Orientation)
+        @param role role of the data to retrieve (integer)
+        @return requested data
+        """
+        if role == Qt.SizeHintRole:
+            fm = QFontMetrics(QFont())
+            height = fm.height() + fm.height() // 3
+            width = \
+                fm.width(self.headerData(section, orientation, Qt.DisplayRole))
+            return QSize(width, height)
+        
+        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
+            try:
+                return self.__headers[section]
+            except IndexError:
+                return None
+        
+        return QAbstractTableModel.headerData(self, section, orientation, role)
+    
+    def data(self, index, role):
+        """
+        Public method to get data from the model.
+        
+        @param index index to get data for (QModelIndex)
+        @param role role of the data to retrieve (integer)
+        @return requested data
+        """
+        if index.row() < 0 or index.row() >= self.rowCount():
+            return None
+        
+        if role in (Qt.DisplayRole, Qt.EditRole):
+            row = index.row()
+            if row < len(self.__allowedCookies):
+                if index.column() == 0:
+                    return self.__allowedCookies[row]
+                elif index.column() == 1:
+                    return self.tr("Allow")
+                else:
+                    return None
+            
+            row -= len(self.__allowedCookies)
+            if row < len(self.__blockedCookies):
+                if index.column() == 0:
+                    return self.__blockedCookies[row]
+                elif index.column() == 1:
+                    return self.tr("Block")
+                else:
+                    return None
+            
+            row -= len(self.__blockedCookies)
+            if row < len(self.__sessionCookies):
+                if index.column() == 0:
+                    return self.__sessionCookies[row]
+                elif index.column() == 1:
+                    return self.tr("Allow For Session")
+                else:
+                    return None
+            
+            return None
+        
+        return None
+    
+    def columnCount(self, parent=QModelIndex()):
+        """
+        Public method to get the number of columns of the model.
+        
+        @param parent parent index (QModelIndex)
+        @return number of columns (integer)
+        """
+        if parent.isValid():
+            return 0
+        else:
+            return len(self.__headers)
+    
+    def rowCount(self, parent=QModelIndex()):
+        """
+        Public method to get the number of rows of the model.
+        
+        @param parent parent index (QModelIndex)
+        @return number of rows (integer)
+        """
+        if parent.isValid() or self.__cookieJar is None:
+            return 0
+        else:
+            return len(self.__allowedCookies) + \
+                len(self.__blockedCookies) + \
+                len(self.__sessionCookies)
+    
+    def removeRows(self, row, count, parent=QModelIndex()):
+        """
+        Public method to remove entries from the model.
+        
+        @param row start row (integer)
+        @param count number of rows to remove (integer)
+        @param parent parent index (QModelIndex)
+        @return flag indicating success (boolean)
+        """
+        if parent.isValid() or self.__cookieJar is None:
+            return False
+        
+        lastRow = row + count - 1
+        self.beginRemoveRows(parent, row, lastRow)
+        for i in range(lastRow, row - 1, -1):
+            rowToRemove = i
+            
+            if rowToRemove < len(self.__allowedCookies):
+                del self.__allowedCookies[rowToRemove]
+                continue
+            
+            rowToRemove -= len(self.__allowedCookies)
+            if rowToRemove < len(self.__blockedCookies):
+                del self.__blockedCookies[rowToRemove]
+                continue
+            
+            rowToRemove -= len(self.__blockedCookies)
+            if rowToRemove < len(self.__sessionCookies):
+                del self.__sessionCookies[rowToRemove]
+                continue
+        
+        self.__cookieJar.setAllowedCookies(self.__allowedCookies)
+        self.__cookieJar.setBlockedCookies(self.__blockedCookies)
+        self.__cookieJar.setAllowForSessionCookies(self.__sessionCookies)
+        self.endRemoveRows()
+        
+        return True
+    
+    def addRule(self, host, rule):
+        """
+        Public method to add an exception rule.
+        
+        @param host name of the host to add a rule for (string)
+        @param rule type of rule to add (CookieJar.Allow, CookieJar.Block or
+            CookieJar.AllowForSession)
+        """
+        if not host:
+            return
+        
+        from .CookieJar import CookieJar
+        
+        if rule == CookieJar.Allow:
+            self.__addHost(
+                host, self.__allowedCookies, self.__blockedCookies,
+                self.__sessionCookies)
+            return
+        elif rule == CookieJar.Block:
+            self.__addHost(
+                host, self.__blockedCookies, self.__allowedCookies,
+                self.__sessionCookies)
+            return
+        elif rule == CookieJar.AllowForSession:
+            self.__addHost(
+                host, self.__sessionCookies, self.__allowedCookies,
+                self.__blockedCookies)
+            return
+    
+    def __addHost(self, host, addList, removeList1, removeList2):
+        """
+        Private method to add a host to an exception list.
+        
+        @param host name of the host to add (string)
+        @param addList reference to the list to add it to (list of strings)
+        @param removeList1 reference to first list to remove it from
+            (list of strings)
+        @param removeList2 reference to second list to remove it from
+            (list of strings)
+        """
+        if host not in addList:
+            addList.append(host)
+            if host in removeList1:
+                removeList1.remove(host)
+            if host in removeList2:
+                removeList2.remove(host)
+        
+        # Avoid to have similar rules (with or without leading dot)
+        # e.g. python-projects.org and .python-projects.org
+        if host.startswith("."):
+            otherRule = host[1:]
+        else:
+            otherRule = '.' + host
+        if otherRule in addList:
+            addList.remove(otherRule)
+        if otherRule in removeList1:
+            removeList1.remove(otherRule)
+        if otherRule in removeList2:
+            removeList2.remove(otherRule)
+        
+        self.beginResetModel()
+        self.endResetModel()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/CookieJar/CookieJar.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,433 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a QNetworkCookieJar subclass with various accept policies.
+"""
+
+from __future__ import unicode_literals
+
+import os
+
+from PyQt5.QtCore import pyqtSignal, pyqtSlot, QSettings
+from PyQt5.QtNetwork import QNetworkCookieJar, QNetworkCookie
+
+from WebBrowser.WebBrowserWindow import WebBrowserWindow
+
+from Utilities.AutoSaver import AutoSaver
+import Utilities
+import Preferences
+
+
+class CookieJar(QNetworkCookieJar):
+    """
+    Class implementing a QNetworkCookieJar subclass with various accept
+    policies.
+    
+    @signal cookiesChanged() emitted after the cookies have been changed
+    """
+    cookiesChanged = pyqtSignal()
+    
+    AcceptAlways = 0
+    AcceptNever = 1
+    AcceptOnlyFromSitesNavigatedTo = 2
+    AcceptMax = 2
+
+    KeepUntilExpire = 0
+    KeepUntilExit = 1
+    KeepMax = 1
+    
+    Allow = 0
+    Block = 1
+    AllowForSession = 2
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object (QObject)
+        """
+        super(CookieJar, self).__init__(parent)
+        
+        self.__loaded = False
+        self.__acceptCookies = self.AcceptOnlyFromSitesNavigatedTo
+        self.__saveTimer = AutoSaver(self, self.__save)
+        
+        self.__cookiesFile = os.path.join(Utilities.getConfigDir(),
+                                          "web_browser", "cookies.ini")
+        
+        self.__store = WebBrowserWindow.webProfile().cookieStore()
+        self.__store.cookieAdded.connect(self.__cookieAdded)
+        self.__store.cookieRemoved.connect(self.__cookieRemoved)
+        
+        self.__load()
+        self.__store.loadAllCookies()
+    
+    def close(self):
+        """
+        Public slot to close the cookie jar.
+        """
+        if not self.__loaded:
+            self.__load()
+        
+        if self.__keepCookies == self.KeepUntilExit:
+            self.clear()
+        self.__saveTimer.saveIfNeccessary()
+    
+    def clear(self):
+        """
+        Public method to clear all cookies.
+        """
+        if not self.__loaded:
+            self.__load()
+        
+        self.setAllCookies([])
+        self.__store.deleteAllCookies()
+        self.cookiesChanged.emit()
+    
+    def removeCookies(self, cookies):
+        """
+        Public method to remove a list of cookies.
+        
+        @param cookies list of cookies to be removed
+        @type list of QNetworkCookie
+        """
+        wasBlocked = self.blockSignals(True)
+        for cookie in cookies:
+            self.__store.deleteCookie(cookie)
+        self.blockSignals(wasBlocked)
+        
+        self.cookiesChanged.emit()
+    
+    def __load(self):
+        """
+        Private method to load the cookies settings.
+        """
+        if self.__loaded:
+            return
+        
+        cookieSettings = QSettings(self.__cookiesFile, QSettings.IniFormat)
+        
+        # load exceptions
+        self.__exceptionsBlock = Preferences.toList(
+            cookieSettings.value("Exceptions/block"))
+        self.__exceptionsAllow = Preferences.toList(
+            cookieSettings.value("Exceptions/allow"))
+        self.__exceptionsAllowForSession = Preferences.toList(
+            cookieSettings.value("Exceptions/allowForSession"))
+        self.__exceptionsBlock.sort()
+        self.__exceptionsAllow.sort()
+        self.__exceptionsAllowForSession.sort()
+        
+        self.__acceptCookies = Preferences.getWebBrowser("AcceptCookies")
+        self.__keepCookies = Preferences.getWebBrowser("KeepCookiesUntil")
+        if self.__keepCookies == self.KeepUntilExit:
+            self.clear()
+        
+        self.__filterTrackingCookies = Preferences.toBool(
+            Preferences.getWebBrowser("FilterTrackingCookies"))
+        
+        self.__loaded = True
+        self.cookiesChanged.emit()
+    
+    def __save(self):
+        """
+        Private method to save the cookies settings.
+        """
+        if not self.__loaded:
+            return
+        
+        cookieSettings = QSettings(self.__cookiesFile, QSettings.IniFormat)
+        
+        cookieSettings.setValue("Exceptions/block", self.__exceptionsBlock)
+        cookieSettings.setValue("Exceptions/allow", self.__exceptionsAllow)
+        cookieSettings.setValue("Exceptions/allowForSession",
+                                self.__exceptionsAllowForSession)
+        
+        Preferences.setWebBrowser("AcceptCookies", self.__acceptCookies)
+        Preferences.setWebBrowser("KeepCookiesUntil", self.__keepCookies)
+        Preferences.setWebBrowser("FilterTrackingCookies",
+                                  self.__filterTrackingCookies)
+    
+    @pyqtSlot(QNetworkCookie)
+    def __cookieAdded(self, cookie):
+        """
+        Private slot handling the addition of a cookie.
+        
+        @param cookie cookie which was added
+        @type QNetworkCookie
+        """
+        if self.__rejectCookie(cookie, cookie.domain()):
+            self.__store.deleteCookie(cookie)
+            return
+        
+        self.insertCookie(cookie)
+        self.cookiesChanged.emit()
+    
+    @pyqtSlot(QNetworkCookie)
+    def __cookieRemoved(self, cookie):
+        """
+        Private slot handling the removal of a cookie.
+        
+        @param cookie cookie which was removed
+        @type QNetworkCookie
+        """
+        if self.deleteCookie(cookie):
+            self.cookiesChanged.emit()
+    
+    def __rejectCookie(self, cookie, cookieDomain):
+        """
+        Public method to test, if a cookie shall be rejected.
+        
+        @param cookie cookie to be tested
+        @type QNetworkCookie
+        @param cookieDomain domain of the cookie
+        @type str
+        @return flag indicating the cookie shall be rejected
+        @rtype bool
+        """
+        if not self.__loaded:
+            self.__load()
+        
+        eBlock = self.__isOnDomainList(self.__exceptionsBlock, cookieDomain)
+        eAllow = not eBlock and \
+            self.__isOnDomainList(self.__exceptionsAllow, cookieDomain)
+        eAllowSession = not eBlock and \
+            not eAllow and \
+            self.__isOnDomainList(
+                self.__exceptionsAllowForSession, cookieDomain)
+        
+        if self.__acceptCookies == self.AcceptNever:
+            if not eAllow and not eAllowSession:
+                return True
+        
+        if self.__acceptCookies == self.AcceptAlways:
+            if eBlock:
+                return True
+        
+        if self.__acceptCookies == self.AcceptOnlyFromSitesNavigatedTo:
+            url = WebBrowserWindow.mainWindow().getWindow().currentBrowser()\
+                .url()
+            if url.isValid():
+                host = url.host()
+            else:
+                host = ""
+            if not self.__matchDomain(cookieDomain, host):
+                return True
+        
+        if self.__filterTrackingCookies and cookie.name().startsWith(b"__utm"):
+            return True
+        
+        return False
+    
+    def acceptPolicy(self):
+        """
+        Public method to get the accept policy.
+        
+        @return current accept policy
+        """
+        if not self.__loaded:
+            self.__load()
+        
+        return self.__acceptCookies
+    
+    def setAcceptPolicy(self, policy):
+        """
+        Public method to set the accept policy.
+        
+        @param policy accept policy to be set
+        """
+        if not self.__loaded:
+            self.__load()
+        
+        if policy > self.AcceptMax:
+            return
+        if policy == self.__acceptCookies:
+            return
+        
+        self.__acceptCookies = policy
+        self.__saveTimer.changeOccurred()
+    
+    def keepPolicy(self):
+        """
+        Public method to get the keep policy.
+        
+        @return keep policy
+        """
+        if not self.__loaded:
+            self.__load()
+        
+        return self.__keepCookies
+    
+    def setKeepPolicy(self, policy):
+        """
+        Public method to set the keep policy.
+        
+        @param policy keep policy to be set
+        """
+        if not self.__loaded:
+            self.__load()
+        
+        if policy > self.KeepMax:
+            return
+        if policy == self.__keepCookies:
+            return
+        
+        self.__keepCookies = policy
+        self.__saveTimer.changeOccurred()
+    
+    def blockedCookies(self):
+        """
+        Public method to return the list of blocked domains.
+        
+        @return list of blocked domains (list of strings)
+        """
+        if not self.__loaded:
+            self.__load()
+        
+        return self.__exceptionsBlock
+    
+    def allowedCookies(self):
+        """
+        Public method to return the list of allowed domains.
+        
+        @return list of allowed domains (list of strings)
+        """
+        if not self.__loaded:
+            self.__load()
+        
+        return self.__exceptionsAllow
+    
+    def allowForSessionCookies(self):
+        """
+        Public method to return the list of allowed session cookie domains.
+        
+        @return list of allowed session cookie domains (list of strings)
+        """
+        if not self.__loaded:
+            self.__load()
+        
+        return self.__exceptionsAllowForSession
+    
+    def setBlockedCookies(self, list_):
+        """
+        Public method to set the list of blocked domains.
+        
+        @param list_ list of blocked domains (list of strings)
+        """
+        if not self.__loaded:
+            self.__load()
+        
+        self.__exceptionsBlock = list_[:]
+        self.__exceptionsBlock.sort()
+        self.__saveTimer.changeOccurred()
+    
+    def setAllowedCookies(self, list_):
+        """
+        Public method to set the list of allowed domains.
+        
+        @param list_ list of allowed domains (list of strings)
+        """
+        if not self.__loaded:
+            self.__load()
+        
+        self.__exceptionsAllow = list_[:]
+        self.__exceptionsAllow.sort()
+        self.__saveTimer.changeOccurred()
+    
+    def setAllowForSessionCookies(self, list_):
+        """
+        Public method to set the list of allowed session cookie domains.
+        
+        @param list_ list of allowed session cookie domains (list of strings)
+        """
+        if not self.__loaded:
+            self.__load()
+        
+        self.__exceptionsAllowForSession = list_[:]
+        self.__exceptionsAllowForSession.sort()
+        self.__saveTimer.changeOccurred()
+    
+    def filterTrackingCookies(self):
+        """
+        Public method to get the filter tracking cookies flag.
+        
+        @return filter tracking cookies flag (boolean)
+        """
+        return self.__filterTrackingCookies
+    
+    def setFilterTrackingCookies(self, filterTrackingCookies):
+        """
+        Public method to set the filter tracking cookies flag.
+        
+        @param filterTrackingCookies filter tracking cookies flag (boolean)
+        """
+        self.__filterTrackingCookies = filterTrackingCookies
+    
+    def __isOnDomainList(self, rules, domain):
+        """
+        Private method to check, if either the rule matches the domain exactly
+        or the domain ends with ".rule".
+        
+        @param rules list of rules (list of strings)
+        @param domain domain name to check (string)
+        @return flag indicating a match (boolean)
+        """
+        for rule in rules:
+            if rule.startswith("."):
+                if domain.endswith(rule):
+                    return True
+                
+                withoutDot = rule[1:]
+                if domain == withoutDot:
+                    return True
+            else:
+                domainEnding = domain[-(len(rule) + 1):]
+                if domainEnding and \
+                   domainEnding[0] == "." and \
+                   domain.endswith(rule):
+                    return True
+                
+                if rule == domain:
+                    return True
+        
+        return False
+    
+    def __matchDomain(self, cookieDomain, siteDomain):
+        """
+        Private method to check, if a URLs host matches a cookie domain
+        according to RFC 6265.
+        
+        @param cookieDomain domain of the cookie
+        @type str
+        @param siteDomain domain or host of an URL
+        @type str
+        @return flag indicating a match
+        @rtype bool
+        """
+        if cookieDomain.startswith("."):
+            cookieDomain = cookieDomain[1:]
+        if siteDomain.startswith("."):
+            siteDomain = siteDomain[1:]
+        
+        if cookieDomain == siteDomain:
+            return True
+        
+        if not siteDomain.endswith(cookieDomain):
+            return False
+        
+        index = siteDomain.find(cookieDomain)
+        return index > 0 and siteDomain[index - 1] == "."
+    
+    def cookies(self):
+        """
+        Public method to get the cookies of the cookie jar.
+        
+        @return list of all cookies (list of QNetworkCookie)
+        """
+        if not self.__loaded:
+            self.__load()
+        
+        return self.allCookies()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/CookieJar/CookieModel.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,146 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the cookie model.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import Qt, QAbstractTableModel, QSize, QModelIndex
+from PyQt5.QtGui import QFont, QFontMetrics
+
+
+class CookieModel(QAbstractTableModel):
+    """
+    Class implementing the cookie model.
+    """
+    def __init__(self, cookieJar, parent=None):
+        """
+        Constructor
+        
+        @param cookieJar reference to the cookie jar (CookieJar)
+        @param parent reference to the parent object (QObject)
+        """
+        super(CookieModel, self).__init__(parent)
+        
+        self.__headers = [
+            self.tr("Website"),
+            self.tr("Name"),
+            self.tr("Path"),
+            self.tr("Secure"),
+            self.tr("Expires"),
+        ]
+        self.__cookieJar = cookieJar
+        self.__cookieJar.cookiesChanged.connect(self.__cookiesChanged)
+    
+    def headerData(self, section, orientation, role):
+        """
+        Public method to get header data from the model.
+        
+        @param section section number (integer)
+        @param orientation orientation (Qt.Orientation)
+        @param role role of the data to retrieve (integer)
+        @return requested data
+        """
+        if role == Qt.SizeHintRole:
+            fm = QFontMetrics(QFont())
+            height = fm.height() + fm.height() // 3
+            width = \
+                fm.width(self.headerData(section, orientation, Qt.DisplayRole))
+            return QSize(width, height)
+        
+        if orientation == Qt.Horizontal:
+            if role == Qt.DisplayRole:
+                try:
+                    return self.__headers[section]
+                except IndexError:
+                    return None
+            
+            return None
+        
+        return QAbstractTableModel.headerData(self, section, orientation, role)
+    
+    def data(self, index, role):
+        """
+        Public method to get data from the model.
+        
+        @param index index to get data for (QModelIndex)
+        @param role role of the data to retrieve (integer)
+        @return requested data
+        """
+        lst = []
+        if self.__cookieJar is not None:
+            lst = self.__cookieJar.cookies()
+        if index.row() < 0 or index.row() >= len(lst):
+            return None
+        
+        if role in (Qt.DisplayRole, Qt.EditRole):
+            cookie = lst[index.row()]
+            col = index.column()
+            if col == 0:
+                return cookie.domain()
+            elif col == 1:
+                return bytes(cookie.name()).decode()
+            elif col == 2:
+                return cookie.path()
+            elif col == 3:
+                return cookie.isSecure()
+            elif col == 4:
+                return cookie.expirationDate()
+            elif col == 5:
+                return cookie.value()
+            else:
+                return None
+        
+        return None
+    
+    def columnCount(self, parent=QModelIndex()):
+        """
+        Public method to get the number of columns of the model.
+        
+        @param parent parent index (QModelIndex)
+        @return number of columns (integer)
+        """
+        if parent.isValid():
+            return 0
+        else:
+            return len(self.__headers)
+    
+    def rowCount(self, parent=QModelIndex()):
+        """
+        Public method to get the number of rows of the model.
+        
+        @param parent parent index (QModelIndex)
+        @return number of columns (integer)
+        """
+        if parent.isValid() or self.__cookieJar is None:
+            return 0
+        else:
+            return len(self.__cookieJar.cookies())
+    
+    def removeRows(self, row, count, parent=QModelIndex()):
+        """
+        Public method to remove entries from the model.
+        
+        @param row start row (integer)
+        @param count number of rows to remove (integer)
+        @param parent parent index (QModelIndex)
+        @return flag indicating success (boolean)
+        """
+        if parent.isValid() or self.__cookieJar is None:
+            return False
+        
+        lst = self.__cookieJar.cookies()[row:row + count]
+        self.__cookieJar.removeCookies(lst)
+        
+        return True
+    
+    def __cookiesChanged(self):
+        """
+        Private slot handling changes of the cookies list in the cookie jar.
+        """
+        self.beginResetModel()
+        self.endResetModel()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/CookieJar/CookiesConfigurationDialog.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,98 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the cookies configuration dialog.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSlot
+from PyQt5.QtWidgets import QDialog
+
+from .CookieJar import CookieJar
+
+from .Ui_CookiesConfigurationDialog import Ui_CookiesConfigurationDialog
+
+
+class CookiesConfigurationDialog(QDialog, Ui_CookiesConfigurationDialog):
+    """
+    Class implementing the cookies configuration dialog.
+    """
+    def __init__(self, parent):
+        """
+        Constructor
+        
+        @param parent reference to the parent object (QWidget)
+        """
+        super(CookiesConfigurationDialog, self).__init__(parent)
+        self.setupUi(self)
+        
+        self.__mw = parent
+        
+        jar = self.__mw.cookieJar()
+        acceptPolicy = jar.acceptPolicy()
+        if acceptPolicy == CookieJar.AcceptAlways:
+            self.acceptCombo.setCurrentIndex(0)
+        elif acceptPolicy == CookieJar.AcceptNever:
+            self.acceptCombo.setCurrentIndex(1)
+        elif acceptPolicy == CookieJar.AcceptOnlyFromSitesNavigatedTo:
+            self.acceptCombo.setCurrentIndex(2)
+        
+        keepPolicy = jar.keepPolicy()
+        if keepPolicy == CookieJar.KeepUntilExpire:
+            self.keepUntilCombo.setCurrentIndex(0)
+        elif keepPolicy == CookieJar.KeepUntilExit:
+            self.keepUntilCombo.setCurrentIndex(1)
+        
+        self.filterTrackingCookiesCheckbox.setChecked(
+            jar.filterTrackingCookies())
+        
+        msh = self.minimumSizeHint()
+        self.resize(max(self.width(), msh.width()), msh.height())
+    
+    def accept(self):
+        """
+        Public slot to accept the dialog.
+        """
+        acceptSelection = self.acceptCombo.currentIndex()
+        if acceptSelection == 0:
+            acceptPolicy = CookieJar.AcceptAlways
+        elif acceptSelection == 1:
+            acceptPolicy = CookieJar.AcceptNever
+        elif acceptSelection == 2:
+            acceptPolicy = CookieJar.AcceptOnlyFromSitesNavigatedTo
+        
+        keepSelection = self.keepUntilCombo.currentIndex()
+        if keepSelection == 0:
+            keepPolicy = CookieJar.KeepUntilExpire
+        elif keepSelection == 1:
+            keepPolicy = CookieJar.KeepUntilExit
+        
+        jar = self.__mw.cookieJar()
+        jar.setAcceptPolicy(acceptPolicy)
+        jar.setKeepPolicy(keepPolicy)
+        jar.setFilterTrackingCookies(
+            self.filterTrackingCookiesCheckbox.isChecked())
+        
+        super(CookiesConfigurationDialog, self).accept()
+    
+    @pyqtSlot()
+    def on_exceptionsButton_clicked(self):
+        """
+        Private slot to show the cookies exceptions dialog.
+        """
+        from .CookiesExceptionsDialog import CookiesExceptionsDialog
+        dlg = CookiesExceptionsDialog(self.__mw.cookieJar())
+        dlg.exec_()
+    
+    @pyqtSlot()
+    def on_cookiesButton_clicked(self):
+        """
+        Private slot to show the cookies dialog.
+        """
+        from .CookiesDialog import CookiesDialog
+        dlg = CookiesDialog(self.__mw.cookieJar())
+        dlg.exec_()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/CookieJar/CookiesConfigurationDialog.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,200 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>CookiesConfigurationDialog</class>
+ <widget class="QDialog" name="CookiesConfigurationDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>500</width>
+    <height>160</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Configure cookies</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QLabel" name="headerLabel">
+     <property name="text">
+      <string>&lt;b&gt;Configure cookies&lt;/b&gt;</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="Line" name="line17">
+     <property name="frameShape">
+      <enum>QFrame::HLine</enum>
+     </property>
+     <property name="frameShadow">
+      <enum>QFrame::Sunken</enum>
+     </property>
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <layout class="QGridLayout" name="gridLayout">
+     <item row="0" column="0">
+      <widget class="QLabel" name="label_2">
+       <property name="text">
+        <string>&amp;Accept Cookies:</string>
+       </property>
+       <property name="buddy">
+        <cstring>acceptCombo</cstring>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="1">
+      <widget class="QComboBox" name="acceptCombo">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="toolTip">
+        <string>Select the accept policy</string>
+       </property>
+       <item>
+        <property name="text">
+         <string>Always</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>Never</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>Only from sites you navigate to</string>
+        </property>
+       </item>
+      </widget>
+     </item>
+     <item row="0" column="2">
+      <widget class="QPushButton" name="exceptionsButton">
+       <property name="toolTip">
+        <string>Show a dialog to configure exceptions</string>
+       </property>
+       <property name="text">
+        <string>&amp;Exceptions...</string>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="0">
+      <widget class="QLabel" name="label">
+       <property name="text">
+        <string>&amp;Keep until:</string>
+       </property>
+       <property name="buddy">
+        <cstring>keepUntilCombo</cstring>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="1">
+      <widget class="QComboBox" name="keepUntilCombo">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="toolTip">
+        <string>Select the keep policy</string>
+       </property>
+       <item>
+        <property name="text">
+         <string>They expire</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>I exit the application</string>
+        </property>
+       </item>
+      </widget>
+     </item>
+     <item row="1" column="2">
+      <widget class="QPushButton" name="cookiesButton">
+       <property name="toolTip">
+        <string>Show a dialog listing all cookies</string>
+       </property>
+       <property name="text">
+        <string>&amp;Show Cookies...</string>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="1">
+      <widget class="QCheckBox" name="filterTrackingCookiesCheckbox">
+       <property name="toolTip">
+        <string>Select to filter tracking cookies</string>
+       </property>
+       <property name="text">
+        <string>&amp;Filter Tracking Cookies</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <tabstops>
+  <tabstop>acceptCombo</tabstop>
+  <tabstop>exceptionsButton</tabstop>
+  <tabstop>keepUntilCombo</tabstop>
+  <tabstop>cookiesButton</tabstop>
+  <tabstop>filterTrackingCookiesCheckbox</tabstop>
+  <tabstop>buttonBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>CookiesConfigurationDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>CookiesConfigurationDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/CookieJar/CookiesDialog.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,142 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog to show all cookies.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSlot, Qt, QDateTime, QByteArray, \
+    QSortFilterProxyModel
+from PyQt5.QtGui import QFont, QFontMetrics
+from PyQt5.QtWidgets import QDialog
+
+from .CookieModel import CookieModel
+
+from .Ui_CookiesDialog import Ui_CookiesDialog
+
+
+class CookiesDialog(QDialog, Ui_CookiesDialog):
+    """
+    Class implementing a dialog to show all cookies.
+    """
+    def __init__(self, cookieJar, parent=None):
+        """
+        Constructor
+        
+        @param cookieJar reference to the cookie jar (CookieJar)
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(CookiesDialog, self).__init__(parent)
+        self.setupUi(self)
+        
+        self.addButton.setEnabled(False)
+        
+        self.__cookieJar = cookieJar
+        
+        self.removeButton.clicked.connect(self.cookiesTable.removeSelected)
+        self.removeAllButton.clicked.connect(self.cookiesTable.removeAll)
+        
+        self.cookiesTable.verticalHeader().hide()
+        model = CookieModel(cookieJar, self)
+        self.__proxyModel = QSortFilterProxyModel(self)
+        self.__proxyModel.setSourceModel(model)
+        self.searchEdit.textChanged.connect(
+            self.__proxyModel.setFilterFixedString)
+        self.cookiesTable.setModel(self.__proxyModel)
+        self.cookiesTable.doubleClicked.connect(self.__showCookieDetails)
+        self.cookiesTable.selectionModel().selectionChanged.connect(
+            self.__tableSelectionChanged)
+        self.cookiesTable.model().modelReset.connect(self.__tableModelReset)
+        
+        fm = QFontMetrics(QFont())
+        height = fm.height() + fm.height() // 3
+        self.cookiesTable.verticalHeader().setDefaultSectionSize(height)
+        self.cookiesTable.verticalHeader().setMinimumSectionSize(-1)
+        for section in range(model.columnCount()):
+            header = self.cookiesTable.horizontalHeader()\
+                .sectionSizeHint(section)
+            if section == 0:
+                header = fm.width("averagebiglonghost.averagedomain.info")
+            elif section == 1:
+                header = fm.width("_session_id")
+            elif section == 4:
+                header = fm.width(
+                    QDateTime.currentDateTime().toString(Qt.LocalDate))
+            buffer = fm.width("mm")
+            header += buffer
+            self.cookiesTable.horizontalHeader().resizeSection(section, header)
+        self.cookiesTable.horizontalHeader().setStretchLastSection(True)
+        self.cookiesTable.model().sort(
+            self.cookiesTable.horizontalHeader().sortIndicatorSection(),
+            Qt.AscendingOrder)
+        
+        self.__detailsDialog = None
+    
+    def __showCookieDetails(self, index):
+        """
+        Private slot to show a dialog with the cookie details.
+        
+        @param index index of the entry to show (QModelIndex)
+        """
+        if not index.isValid():
+            return
+        
+        cookiesTable = self.sender()
+        if cookiesTable is None:
+            return
+        
+        model = cookiesTable.model()
+        row = index.row()
+        
+        domain = model.data(model.index(row, 0))
+        name = model.data(model.index(row, 1))
+        path = model.data(model.index(row, 2))
+        secure = model.data(model.index(row, 3))
+        expires = model.data(model.index(row, 4)).toString("yyyy-MM-dd hh:mm")
+        value = bytes(
+            QByteArray.fromPercentEncoding(
+                model.data(model.index(row, 5)))).decode()
+        
+        if self.__detailsDialog is None:
+            from .CookieDetailsDialog import CookieDetailsDialog
+            self.__detailsDialog = CookieDetailsDialog(self)
+        self.__detailsDialog.setData(domain, name, path, secure, expires,
+                                     value)
+        self.__detailsDialog.show()
+    
+    @pyqtSlot()
+    def on_addButton_clicked(self):
+        """
+        Private slot to add a new exception.
+        """
+        selection = self.cookiesTable.selectionModel().selectedRows()
+        if len(selection) == 0:
+            return
+        
+        from .CookiesExceptionsDialog import CookiesExceptionsDialog
+        
+        firstSelected = selection[0]
+        domainSelection = firstSelected.sibling(firstSelected.row(), 0)
+        domain = self.__proxyModel.data(domainSelection, Qt.DisplayRole)
+        dlg = CookiesExceptionsDialog(self.__cookieJar, self)
+        dlg.setDomainName(domain)
+        dlg.exec_()
+    
+    def __tableSelectionChanged(self, selected, deselected):
+        """
+        Private slot to handle a change of selected items.
+        
+        @param selected selected indexes (QItemSelection)
+        @param deselected deselected indexes (QItemSelection)
+        """
+        self.addButton.setEnabled(len(selected.indexes()) > 0)
+    
+    def __tableModelReset(self):
+        """
+        Private slot to handle a reset of the cookies table.
+        """
+        self.addButton.setEnabled(False)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/CookieJar/CookiesDialog.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,195 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>CookiesDialog</class>
+ <widget class="QDialog" name="CookiesDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>500</width>
+    <height>350</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Cookies</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QGridLayout" name="gridLayout">
+   <item row="0" column="0" colspan="4">
+    <layout class="QHBoxLayout" name="horizontalLayout_2">
+     <item>
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <layout class="QHBoxLayout" name="horizontalLayout">
+       <property name="spacing">
+        <number>0</number>
+       </property>
+       <item>
+        <widget class="E5ClearableLineEdit" name="searchEdit">
+         <property name="minimumSize">
+          <size>
+           <width>300</width>
+           <height>0</height>
+          </size>
+         </property>
+         <property name="toolTip">
+          <string>Enter search term for cookies</string>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+    </layout>
+   </item>
+   <item row="1" column="0" colspan="4">
+    <widget class="E5TableView" name="cookiesTable">
+     <property name="alternatingRowColors">
+      <bool>true</bool>
+     </property>
+     <property name="selectionBehavior">
+      <enum>QAbstractItemView::SelectRows</enum>
+     </property>
+     <property name="textElideMode">
+      <enum>Qt::ElideMiddle</enum>
+     </property>
+     <property name="showGrid">
+      <bool>false</bool>
+     </property>
+     <property name="sortingEnabled">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="0">
+    <widget class="QPushButton" name="removeButton">
+     <property name="toolTip">
+      <string>Press to remove the selected entries</string>
+     </property>
+     <property name="text">
+      <string>&amp;Remove</string>
+     </property>
+     <property name="autoDefault">
+      <bool>false</bool>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="1">
+    <widget class="QPushButton" name="removeAllButton">
+     <property name="toolTip">
+      <string>Press to remove all entries</string>
+     </property>
+     <property name="text">
+      <string>Remove &amp;All</string>
+     </property>
+     <property name="autoDefault">
+      <bool>false</bool>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="2">
+    <widget class="QPushButton" name="addButton">
+     <property name="toolTip">
+      <string>Press to open the cookies exceptions dialog to add a new rule</string>
+     </property>
+     <property name="text">
+      <string>Add R&amp;ule...</string>
+     </property>
+     <property name="autoDefault">
+      <bool>false</bool>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="3">
+    <spacer name="horizontalSpacer_2">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>208</width>
+       <height>20</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+   <item row="3" column="0" colspan="4">
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Close</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>E5ClearableLineEdit</class>
+   <extends>QLineEdit</extends>
+   <header>E5Gui/E5LineEdit.h</header>
+  </customwidget>
+  <customwidget>
+   <class>E5TableView</class>
+   <extends>QTableView</extends>
+   <header>E5Gui/E5TableView.h</header>
+  </customwidget>
+ </customwidgets>
+ <tabstops>
+  <tabstop>searchEdit</tabstop>
+  <tabstop>cookiesTable</tabstop>
+  <tabstop>removeButton</tabstop>
+  <tabstop>removeAllButton</tabstop>
+  <tabstop>addButton</tabstop>
+  <tabstop>buttonBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>CookiesDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>228</x>
+     <y>274</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>CookiesDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/CookieJar/CookiesExceptionsDialog.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,118 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog for the configuration of cookie exceptions.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSlot, QSortFilterProxyModel
+from PyQt5.QtGui import QFont, QFontMetrics
+from PyQt5.QtWidgets import QDialog, QCompleter
+
+from .CookieExceptionsModel import CookieExceptionsModel
+from .CookieModel import CookieModel
+
+from .Ui_CookiesExceptionsDialog import Ui_CookiesExceptionsDialog
+
+
+class CookiesExceptionsDialog(QDialog, Ui_CookiesExceptionsDialog):
+    """
+    Class implementing a dialog for the configuration of cookie exceptions.
+    """
+    def __init__(self, cookieJar, parent=None):
+        """
+        Constructor
+        
+        @param cookieJar reference to the cookie jar (CookieJar)
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(CookiesExceptionsDialog, self).__init__(parent)
+        self.setupUi(self)
+        
+        self.__cookieJar = cookieJar
+        
+        self.removeButton.clicked.connect(
+            self.exceptionsTable.removeSelected)
+        self.removeAllButton.clicked.connect(
+            self.exceptionsTable.removeAll)
+        
+        self.exceptionsTable.verticalHeader().hide()
+        self.__exceptionsModel = CookieExceptionsModel(cookieJar)
+        self.__proxyModel = QSortFilterProxyModel(self)
+        self.__proxyModel.setSourceModel(self.__exceptionsModel)
+        self.searchEdit.textChanged.connect(
+            self.__proxyModel.setFilterFixedString)
+        self.exceptionsTable.setModel(self.__proxyModel)
+        
+        cookieModel = CookieModel(cookieJar, self)
+        self.domainEdit.setCompleter(QCompleter(cookieModel, self.domainEdit))
+        
+        f = QFont()
+        f.setPointSize(10)
+        fm = QFontMetrics(f)
+        height = fm.height() + fm.height() // 3
+        self.exceptionsTable.verticalHeader().setDefaultSectionSize(height)
+        self.exceptionsTable.verticalHeader().setMinimumSectionSize(-1)
+        for section in range(self.__exceptionsModel.columnCount()):
+            header = self.exceptionsTable.horizontalHeader()\
+                .sectionSizeHint(section)
+            if section == 0:
+                header = fm.width("averagebiglonghost.averagedomain.info")
+            elif section == 1:
+                header = fm.width(self.tr("Allow For Session"))
+            buffer = fm.width("mm")
+            header += buffer
+            self.exceptionsTable.horizontalHeader()\
+                .resizeSection(section, header)
+    
+    def setDomainName(self, domain):
+        """
+        Public method to set the domain to be displayed.
+        
+        @param domain domain name to be displayed (string)
+        """
+        self.domainEdit.setText(domain)
+    
+    @pyqtSlot(str)
+    def on_domainEdit_textChanged(self, txt):
+        """
+        Private slot to handle a change of the domain edit text.
+        
+        @param txt current text of the edit (string)
+        """
+        enabled = txt != ""
+        self.blockButton.setEnabled(enabled)
+        self.allowButton.setEnabled(enabled)
+        self.allowForSessionButton.setEnabled(enabled)
+    
+    @pyqtSlot()
+    def on_blockButton_clicked(self):
+        """
+        Private slot to block cookies of a domain.
+        """
+        from .CookieJar import CookieJar
+        self.__exceptionsModel.addRule(self.domainEdit.text(), CookieJar.Block)
+        self.domainEdit.clear()
+    
+    @pyqtSlot()
+    def on_allowForSessionButton_clicked(self):
+        """
+        Private slot to allow cookies of a domain for the current session only.
+        """
+        from .CookieJar import CookieJar
+        self.__exceptionsModel.addRule(self.domainEdit.text(),
+                                       CookieJar.AllowForSession)
+        self.domainEdit.clear()
+    
+    @pyqtSlot()
+    def on_allowButton_clicked(self):
+        """
+        Private slot to allow cookies of a domain.
+        """
+        from .CookieJar import CookieJar
+        self.__exceptionsModel.addRule(self.domainEdit.text(), CookieJar.Allow)
+        self.domainEdit.clear()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/CookieJar/CookiesExceptionsDialog.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,292 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>CookiesExceptionsDialog</class>
+ <widget class="QDialog" name="CookiesExceptionsDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>500</width>
+    <height>450</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Cookie Exceptions</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QGroupBox" name="newExceptionGroupBox">
+     <property name="title">
+      <string>New Exception</string>
+     </property>
+     <layout class="QGridLayout">
+      <item row="0" column="0">
+       <layout class="QHBoxLayout" name="_2">
+        <item>
+         <widget class="QLabel" name="label">
+          <property name="text">
+           <string>&amp;Domain:</string>
+          </property>
+          <property name="buddy">
+           <cstring>domainEdit</cstring>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QLineEdit" name="domainEdit">
+          <property name="toolTip">
+           <string>Enter the domain name</string>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+      <item row="1" column="0">
+       <layout class="QHBoxLayout" name="_3">
+        <item>
+         <spacer>
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>81</width>
+            <height>25</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+        <item>
+         <widget class="QPushButton" name="blockButton">
+          <property name="enabled">
+           <bool>false</bool>
+          </property>
+          <property name="toolTip">
+           <string>Press to always reject cookies for the domain</string>
+          </property>
+          <property name="text">
+           <string>&amp;Block</string>
+          </property>
+          <property name="autoDefault">
+           <bool>false</bool>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QPushButton" name="allowForSessionButton">
+          <property name="enabled">
+           <bool>false</bool>
+          </property>
+          <property name="toolTip">
+           <string>Press to accept cookies for the domain for the current session</string>
+          </property>
+          <property name="text">
+           <string>Allow For &amp;Session</string>
+          </property>
+          <property name="autoDefault">
+           <bool>false</bool>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QPushButton" name="allowButton">
+          <property name="enabled">
+           <bool>false</bool>
+          </property>
+          <property name="toolTip">
+           <string>Press to always accept cookies for the domain</string>
+          </property>
+          <property name="text">
+           <string>Allo&amp;w</string>
+          </property>
+          <property name="autoDefault">
+           <bool>false</bool>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="exceptionsGroup">
+     <property name="title">
+      <string>Exceptions</string>
+     </property>
+     <layout class="QGridLayout" name="gridLayout">
+      <item row="0" column="0" colspan="3">
+       <layout class="QHBoxLayout" name="horizontalLayout_2">
+        <item>
+         <spacer name="horizontalSpacer">
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>40</width>
+            <height>20</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+        <item>
+         <layout class="QHBoxLayout" name="horizontalLayout">
+          <property name="spacing">
+           <number>0</number>
+          </property>
+          <item>
+           <widget class="E5ClearableLineEdit" name="searchEdit">
+            <property name="minimumSize">
+             <size>
+              <width>300</width>
+              <height>0</height>
+             </size>
+            </property>
+            <property name="toolTip">
+             <string>Enter search term for exceptions</string>
+            </property>
+           </widget>
+          </item>
+         </layout>
+        </item>
+       </layout>
+      </item>
+      <item row="1" column="0" colspan="3">
+       <widget class="E5TableView" name="exceptionsTable">
+        <property name="alternatingRowColors">
+         <bool>true</bool>
+        </property>
+        <property name="selectionBehavior">
+         <enum>QAbstractItemView::SelectRows</enum>
+        </property>
+        <property name="textElideMode">
+         <enum>Qt::ElideMiddle</enum>
+        </property>
+        <property name="showGrid">
+         <bool>false</bool>
+        </property>
+        <property name="sortingEnabled">
+         <bool>true</bool>
+        </property>
+        <attribute name="verticalHeaderVisible">
+         <bool>false</bool>
+        </attribute>
+       </widget>
+      </item>
+      <item row="2" column="0">
+       <widget class="QPushButton" name="removeButton">
+        <property name="toolTip">
+         <string>Press to remove the selected entries</string>
+        </property>
+        <property name="text">
+         <string>&amp;Remove</string>
+        </property>
+        <property name="autoDefault">
+         <bool>false</bool>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="1">
+       <widget class="QPushButton" name="removeAllButton">
+        <property name="toolTip">
+         <string>Press to remove all entries</string>
+        </property>
+        <property name="text">
+         <string>Remove &amp;All</string>
+        </property>
+        <property name="autoDefault">
+         <bool>false</bool>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="2">
+       <spacer name="horizontalSpacer_2">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>286</width>
+          <height>20</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Close</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>E5ClearableLineEdit</class>
+   <extends>QLineEdit</extends>
+   <header>E5Gui/E5LineEdit.h</header>
+  </customwidget>
+  <customwidget>
+   <class>E5TableView</class>
+   <extends>QTableView</extends>
+   <header>E5Gui/E5TableView.h</header>
+  </customwidget>
+ </customwidgets>
+ <tabstops>
+  <tabstop>domainEdit</tabstop>
+  <tabstop>blockButton</tabstop>
+  <tabstop>allowForSessionButton</tabstop>
+  <tabstop>allowButton</tabstop>
+  <tabstop>searchEdit</tabstop>
+  <tabstop>exceptionsTable</tabstop>
+  <tabstop>removeButton</tabstop>
+  <tabstop>removeAllButton</tabstop>
+  <tabstop>buttonBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>CookiesExceptionsDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>227</x>
+     <y>429</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>CookiesExceptionsDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>295</x>
+     <y>435</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/CookieJar/__init__.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Package implementing a cookie jar and related dialogs with models.
+"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Download/DownloadAskActionDialog.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,60 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2011 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog to ask for a download action.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtWidgets import QDialog
+
+from .Ui_DownloadAskActionDialog import Ui_DownloadAskActionDialog
+
+import Preferences
+
+
+class DownloadAskActionDialog(QDialog, Ui_DownloadAskActionDialog):
+    """
+    Class implementing a dialog to ask for a download action.
+    """
+    def __init__(self, fileName, mimeType, baseUrl, parent=None):
+        """
+        Constructor
+        
+        @param fileName file name (string)
+        @param mimeType mime type (string)
+        @param baseUrl URL (string)
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(DownloadAskActionDialog, self).__init__(parent)
+        self.setupUi(self)
+        
+        self.infoLabel.setText("<b>{0}</b>".format(fileName))
+        self.typeLabel.setText(mimeType)
+        self.siteLabel.setText(baseUrl)
+        
+        if not Preferences.getWebBrowser("VirusTotalEnabled") or \
+           Preferences.getWebBrowser("VirusTotalServiceKey") == "":
+            self.scanButton.setHidden(True)
+        
+        msh = self.minimumSizeHint()
+        self.resize(max(self.width(), msh.width()), msh.height())
+    
+    def getAction(self):
+        """
+        Public method to get the selected action.
+        
+        @return selected action ("save", "open", "scan" or "cancel")
+        """
+        if self.openButton.isChecked():
+            return "open"
+        elif self.scanButton.isChecked():
+            return "scan"
+        elif self.saveButton.isChecked():
+            return "save"
+        else:
+            # should not happen, but keep it safe
+            return "cancel"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Download/DownloadAskActionDialog.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,205 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>DownloadAskActionDialog</class>
+ <widget class="QDialog" name="DownloadAskActionDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>500</width>
+    <height>209</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>What to do?</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QLabel" name="label_3">
+     <property name="text">
+      <string>You are about to download this file:</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <layout class="QGridLayout" name="gridLayout_2">
+     <item row="0" column="0">
+      <widget class="QLabel" name="label_4">
+       <property name="text">
+        <string notr="true"/>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="1" colspan="2">
+      <widget class="QLabel" name="infoLabel">
+       <property name="text">
+        <string/>
+       </property>
+       <property name="wordWrap">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="1">
+      <widget class="QLabel" name="label_5">
+       <property name="text">
+        <string>Type:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="2">
+      <widget class="QLabel" name="typeLabel">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="text">
+        <string/>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="1">
+      <widget class="QLabel" name="label_6">
+       <property name="text">
+        <string>From:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="2">
+      <widget class="QLabel" name="siteLabel">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="text">
+        <string/>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <layout class="QGridLayout" name="gridLayout">
+     <item row="1" column="0">
+      <widget class="QLabel" name="label_2">
+       <property name="text">
+        <string notr="true"/>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="1">
+      <widget class="QRadioButton" name="openButton">
+       <property name="toolTip">
+        <string>Select to open the downloaded file</string>
+       </property>
+       <property name="text">
+        <string>&amp;Open File</string>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="1">
+      <widget class="QRadioButton" name="scanButton">
+       <property name="statusTip">
+        <string>Select to scan the file with VirusTotal</string>
+       </property>
+       <property name="text">
+        <string>Scan with &amp;VirusTotal</string>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="2" rowspan="2">
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item row="3" column="1">
+      <widget class="QRadioButton" name="saveButton">
+       <property name="toolTip">
+        <string>Select to save the file</string>
+       </property>
+       <property name="text">
+        <string>&amp;Save File</string>
+       </property>
+       <property name="checked">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="0" colspan="2">
+      <widget class="QLabel" name="label">
+       <property name="text">
+        <string>&lt;b&gt;What do you want to do?&lt;/b&gt;</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <tabstops>
+  <tabstop>openButton</tabstop>
+  <tabstop>scanButton</tabstop>
+  <tabstop>saveButton</tabstop>
+  <tabstop>buttonBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>DownloadAskActionDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>DownloadAskActionDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Download/DownloadItem.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,522 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2010 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a widget controlling a download.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt, QTime, QFileInfo, QUrl, \
+    PYQT_VERSION_STR
+from PyQt5.QtGui import QPalette, QDesktopServices
+from PyQt5.QtWidgets import QWidget, QStyle, QDialog
+from PyQt5.QtWebEngineWidgets import QWebEngineDownloadItem
+
+from E5Gui import E5FileDialog
+
+from .Ui_DownloadItem import Ui_DownloadItem
+
+from .DownloadUtilities import timeString, dataString
+from WebBrowser.WebBrowserWindow import WebBrowserWindow
+
+import UI.PixmapCache
+import Utilities.MimeTypes
+
+
+class DownloadItem(QWidget, Ui_DownloadItem):
+    """
+    Class implementing a widget controlling a download.
+    
+    @signal statusChanged() emitted upon a status change of a download
+    @signal downloadFinished() emitted when a download finished
+    @signal progress(int, int) emitted to signal the download progress
+    """
+    statusChanged = pyqtSignal()
+    downloadFinished = pyqtSignal()
+    progress = pyqtSignal(int, int)
+    
+    Downloading = 0
+    DownloadSuccessful = 1
+    DownloadCancelled = 2
+    
+    def __init__(self, downloadItem=None, parent=None):
+        """
+        Constructor
+        
+        @param downloadItem reference to the download object containing the
+        download data.
+        @keyparam parent reference to the parent widget (QWidget)
+        @type QWebEngineDownloadItem
+        """
+        super(DownloadItem, self).__init__(parent)
+        self.setupUi(self)
+        
+        p = self.infoLabel.palette()
+        p.setColor(QPalette.Text, Qt.darkGray)
+        self.infoLabel.setPalette(p)
+        
+        self.progressBar.setMaximum(0)
+        
+        self.stopButton.setIcon(UI.PixmapCache.getIcon("stopLoading.png"))
+        self.openButton.setIcon(UI.PixmapCache.getIcon("open.png"))
+        self.openButton.setEnabled(False)
+        self.openButton.setVisible(False)
+        
+        self.__state = DownloadItem.Downloading
+        
+        icon = self.style().standardIcon(QStyle.SP_FileIcon)
+        self.fileIcon.setPixmap(icon.pixmap(48, 48))
+        
+        self.__downloadItem = downloadItem
+        self.__pageUrl = \
+            WebBrowserWindow.mainWindow().getWindow().currentBrowser().url()
+        self.__bytesReceived = 0
+        self.__bytesTotal = -1
+        self.__downloadTime = QTime()
+        self.__fileName = ""
+        self.__originalFileName = ""
+        self.__finishedDownloading = False
+        self.__gettingFileName = False
+        self.__canceledFileSelect = False
+        self.__autoOpen = False
+        
+        self.__initialize()
+    
+    def __initialize(self):
+        """
+        Private method to initialize the widget.
+        """
+        if self.__downloadItem is None:
+            return
+        
+        self.__finishedDownloading = False
+        self.__bytesReceived = 0
+        self.__bytesTotal = -1
+        
+        # start timer for the download estimation
+        self.__downloadTime.start()
+        
+        # attach to the download item object
+        self.__url = self.__downloadItem.url()
+        self.__downloadItem.downloadProgress.connect(self.__downloadProgress)
+        self.__downloadItem.finished.connect(self.__finished)
+        
+        # reset info
+        self.infoLabel.clear()
+        self.progressBar.setValue(0)
+        self.__getFileName()
+        if not self.__fileName:
+            self.__downloadItem.cancel()
+        else:
+            self.__downloadItem.setPath(self.__fileName)
+            self.__downloadItem.accept()
+    
+    def __getFileName(self):
+        """
+        Private method to get the file name to save to from the user.
+        """
+        if self.__gettingFileName:
+            return
+        
+        downloadDirectory = WebBrowserWindow\
+            .downloadManager().downloadDirectory()
+        
+        if self.__fileName:
+            fileName = self.__fileName
+            originalFileName = self.__originalFileName
+            self.__toDownload = True
+            ask = False
+        else:
+            defaultFileName, originalFileName = \
+                self.__saveFileName(downloadDirectory)
+            fileName = defaultFileName
+            self.__originalFileName = originalFileName
+            ask = True
+        self.__autoOpen = False
+        from .DownloadAskActionDialog import DownloadAskActionDialog
+        url = self.__downloadItem.url()
+        mimetype = Utilities.MimeTypes.mimeType(originalFileName)
+        dlg = DownloadAskActionDialog(
+            QFileInfo(originalFileName).fileName(),
+            mimetype,
+            "{0}://{1}".format(url.scheme(), url.authority()),
+            self)
+        if dlg.exec_() == QDialog.Rejected or dlg.getAction() == "cancel":
+            self.progressBar.setVisible(False)
+            self.on_stopButton_clicked()
+            self.filenameLabel.setText(
+                self.tr("Download canceled: {0}").format(
+                    QFileInfo(defaultFileName).fileName()))
+            self.__canceledFileSelect = True
+            return
+        
+        if dlg.getAction() == "scan":
+            self.__mainWindow.requestVirusTotalScan(url)
+            
+            self.progressBar.setVisible(False)
+            self.on_stopButton_clicked()
+            self.filenameLabel.setText(
+                self.tr("VirusTotal scan scheduled: {0}").format(
+                    QFileInfo(defaultFileName).fileName()))
+            self.__canceledFileSelect = True
+            return
+        
+        self.__autoOpen = dlg.getAction() == "open"
+        if PYQT_VERSION_STR >= "5.0.0":
+            from PyQt5.QtCore import QStandardPaths
+            tempLocation = QStandardPaths.standardLocations(
+                QStandardPaths.TempLocation)[0]
+        else:
+            from PyQt5.QtGui import QDesktopServices
+            tempLocation = QDesktopServices.storageLocation(
+                QDesktopServices.TempLocation)
+        fileName = tempLocation + '/' + \
+            QFileInfo(fileName).completeBaseName()
+        
+        if ask and not self.__autoOpen:
+            self.__gettingFileName = True
+            fileName = E5FileDialog.getSaveFileName(
+                None,
+                self.tr("Save File"),
+                defaultFileName,
+                "")
+            self.__gettingFileName = False
+            if not fileName:
+                self.progressBar.setVisible(False)
+                self.on_stopButton_clicked()
+                self.filenameLabel.setText(
+                    self.tr("Download canceled: {0}")
+                        .format(QFileInfo(defaultFileName).fileName()))
+                self.__canceledFileSelect = True
+                return
+        
+        fileInfo = QFileInfo(fileName)
+        WebBrowserWindow.downloadManager()\
+            .setDownloadDirectory(fileInfo.absoluteDir().absolutePath())
+        self.filenameLabel.setText(fileInfo.fileName())
+        
+        self.__fileName = fileName
+        
+        # check file path for saving
+        saveDirPath = QFileInfo(self.__fileName).dir()
+        if not saveDirPath.exists():
+            if not saveDirPath.mkpath(saveDirPath.absolutePath()):
+                self.progressBar.setVisible(False)
+                self.on_stopButton_clicked()
+                self.infoLabel.setText(self.tr(
+                    "Download directory ({0}) couldn't be created.")
+                    .format(saveDirPath.absolutePath()))
+                return
+        
+        self.filenameLabel.setText(QFileInfo(self.__fileName).fileName())
+    
+    def __saveFileName(self, directory):
+        """
+        Private method to calculate a name for the file to download.
+        
+        @param directory name of the directory to store the file into (string)
+        @return proposed filename and original filename (string, string)
+        """
+        path = self.__downloadItem.path()
+        info = QFileInfo(path)
+        baseName = info.completeBaseName()
+        endName = info.suffix()
+        
+        origName = baseName
+        if endName:
+            origName += '.' + endName
+        
+        name = directory + baseName
+        if endName:
+            name += '.' + endName
+        return name, origName
+    
+    def __open(self):
+        """
+        Private slot to open the downloaded file.
+        """
+        info = QFileInfo(self.__fileName)
+        url = QUrl.fromLocalFile(info.absoluteFilePath())
+        QDesktopServices.openUrl(url)
+    
+    @pyqtSlot()
+    def on_stopButton_clicked(self):
+        """
+        Private slot to stop the download.
+        """
+        self.cancelDownload()
+    
+    def cancelDownload(self):
+        """
+        Public slot to stop the download.
+        """
+        self.setUpdatesEnabled(False)
+        self.stopButton.setEnabled(False)
+        self.stopButton.setVisible(False)
+        self.openButton.setEnabled(False)
+        self.openButton.setVisible(False)
+        self.setUpdatesEnabled(True)
+        self.__state = DownloadItem.DownloadCancelled
+        self.__downloadItem.cancel()
+        self.downloadFinished.emit()
+    
+    @pyqtSlot()
+    def on_openButton_clicked(self):
+        """
+        Private slot to open the downloaded file.
+        """
+        self.openFile()
+    
+    def openFile(self):
+        """
+        Public slot to open the downloaded file.
+        """
+        info = QFileInfo(self.__fileName)
+        url = QUrl.fromLocalFile(info.absoluteFilePath())
+        QDesktopServices.openUrl(url)
+    
+    def openFolder(self):
+        """
+        Public slot to open the folder containing the downloaded file.
+        """
+        info = QFileInfo(self.__fileName)
+        url = QUrl.fromLocalFile(info.absolutePath())
+        QDesktopServices.openUrl(url)
+    
+    def __downloadProgress(self, bytesReceived, bytesTotal):
+        """
+        Private method to show the download progress.
+        
+        @param bytesReceived number of bytes received (integer)
+        @param bytesTotal number of total bytes (integer)
+        """
+        self.__bytesReceived = bytesReceived
+        self.__bytesTotal = bytesTotal
+        currentValue = 0
+        totalValue = 0
+        if bytesTotal > 0:
+            currentValue = bytesReceived * 100 / bytesTotal
+            totalValue = 100
+        self.progressBar.setValue(currentValue)
+        self.progressBar.setMaximum(totalValue)
+        
+        self.progress.emit(currentValue, totalValue)
+        self.__updateInfoLabel()
+    
+    def bytesTotal(self):
+        """
+        Public method to get the total number of bytes of the download.
+        
+        @return total number of bytes (integer)
+        """
+        if self.__bytesTotal == -1:
+            self.__bytesTotal = self.__downloadItem.totalBytes()
+        return self.__bytesTotal
+    
+    def bytesReceived(self):
+        """
+        Public method to get the number of bytes received.
+        
+        @return number of bytes received (integer)
+        """
+        return self.__bytesReceived
+    
+    def remainingTime(self):
+        """
+        Public method to get an estimation for the remaining time.
+        
+        @return estimation for the remaining time (float)
+        """
+        if not self.downloading():
+            return -1.0
+        
+        if self.bytesTotal() == -1:
+            return -1.0
+        
+        cSpeed = self.currentSpeed()
+        if cSpeed != 0:
+            timeRemaining = (self.bytesTotal() - self.bytesReceived()) / cSpeed
+        else:
+            timeRemaining = 1
+        
+        # ETA should never be 0
+        if timeRemaining == 0:
+            timeRemaining = 1
+        
+        return timeRemaining
+    
+    def currentSpeed(self):
+        """
+        Public method to get an estimation for the download speed.
+        
+        @return estimation for the download speed (float)
+        """
+        if not self.downloading():
+            return -1.0
+        
+        return self.__bytesReceived * 1000.0 / self.__downloadTime.elapsed()
+    
+    def __updateInfoLabel(self):
+        """
+        Private method to update the info label.
+        """
+        bytesTotal = self.bytesTotal()
+        running = not self.downloadedSuccessfully()
+        
+        speed = self.currentSpeed()
+        timeRemaining = self.remainingTime()
+        
+        info = ""
+        if running:
+            remaining = ""
+            
+            if bytesTotal > 0:
+                remaining = timeString(timeRemaining)
+            
+            info = self.tr("{0} of {1} ({2}/sec)\n{3}")\
+                .format(
+                    dataString(self.__bytesReceived),
+                    bytesTotal == -1 and self.tr("?") or
+                    dataString(bytesTotal),
+                    dataString(int(speed)),
+                    remaining)
+        else:
+            if self.__bytesReceived == bytesTotal or bytesTotal == -1:
+                info = self.tr("{0} downloaded")\
+                    .format(dataString(self.__output.size()))
+            else:
+                info = self.tr("{0} of {1} - Stopped")\
+                    .format(dataString(self.__bytesReceived),
+                            dataString(bytesTotal))
+        self.infoLabel.setText(info)
+    
+    def downloading(self):
+        """
+        Public method to determine, if a download is in progress.
+        
+        @return flag indicating a download is in progress (boolean)
+        """
+        return self.__state == DownloadItem.Downloading
+    
+    def downloadedSuccessfully(self):
+        """
+        Public method to check for a successful download.
+        
+        @return flag indicating a successful download (boolean)
+        """
+        return self.__state == DownloadItem.DownloadSuccessful
+    
+    def downloadCanceled(self):
+        """
+        Public method to check, if the download was cancelled.
+        
+        @return flag indicating a canceled download (boolean)
+        """
+        return self.__state == DownloadItem.DownloadCancelled
+    
+    def __finished(self):
+        """
+        Private slot to handle the download finished.
+        """
+        self.__finishedDownloading = True
+        
+        noError = (self.__downloadItem.state() == 
+                   QWebEngineDownloadItem.DownloadCompleted)
+        
+        self.progressBar.setVisible(False)
+        self.stopButton.setEnabled(False)
+        self.stopButton.setVisible(False)
+        self.openButton.setEnabled(noError)
+        self.openButton.setVisible(noError)
+        self.__updateInfoLabel()
+        self.__state = DownloadItem.DownloadSuccessful
+        self.statusChanged.emit()
+        self.downloadFinished.emit()
+        
+        if self.__autoOpen:
+            self.__open()
+    
+    def canceledFileSelect(self):
+        """
+        Public method to check, if the user canceled the file selection.
+        
+        @return flag indicating cancellation (boolean)
+        """
+        return self.__canceledFileSelect
+    
+    def setIcon(self, icon):
+        """
+        Public method to set the download icon.
+        
+        @param icon reference to the icon to be set (QIcon)
+        """
+        self.fileIcon.setPixmap(icon.pixmap(48, 48))
+    
+    def fileName(self):
+        """
+        Public method to get the name of the output file.
+        
+        @return name of the output file (string)
+        """
+        return self.__fileName
+    
+    def absoluteFilePath(self):
+        """
+        Public method to get the absolute path of the output file.
+        
+        @return absolute path of the output file (string)
+        """
+        return QFileInfo(self.__fileName).absoluteFilePath()
+    
+    def getData(self):
+        """
+        Public method to get the relevant download data.
+        
+        @return tuple of URL, save location, flag and the
+            URL of the related web page (QUrl, string, boolean,QUrl)
+        """
+        return (self.__url, QFileInfo(self.__fileName).filePath(),
+                self.downloadedSuccessfully(), self.__pageUrl)
+    
+    def setData(self, data):
+        """
+        Public method to set the relevant download data.
+        
+        @param data tuple of URL, save location, flag and the
+            URL of the related web page (QUrl, string, boolean, QUrl)
+        """
+        self.__url = data[0]
+        self.__fileName = data[1]
+        self.__pageUrl = data[3]
+        
+        self.filenameLabel.setText(QFileInfo(self.__fileName).fileName())
+        self.infoLabel.setText(self.__fileName)
+        
+        self.stopButton.setEnabled(False)
+        self.stopButton.setVisible(False)
+        self.openButton.setEnabled(data[2])
+        self.openButton.setVisible(data[2])
+        if data[2]:
+            self.__state = DownloadItem.DownloadSuccessful
+        else:
+            self.__state = DownloadItem.DownloadCancelled
+        self.progressBar.setVisible(False)
+    
+    def getInfoData(self):
+        """
+        Public method to get the text of the info label.
+        
+        @return text of the info label (string)
+        """
+        return self.infoLabel.text()
+    
+    def getPageUrl(self):
+        """
+        Public method to get the URL of the download page.
+        
+        @return URL of the download page (QUrl)
+        """
+        return self.__pageUrl
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Download/DownloadItem.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>DownloadItem</class>
+ <widget class="QWidget" name="DownloadItem">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>400</width>
+    <height>87</height>
+   </rect>
+  </property>
+  <property name="sizePolicy">
+   <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
+    <horstretch>0</horstretch>
+    <verstretch>0</verstretch>
+   </sizepolicy>
+  </property>
+  <property name="windowTitle">
+   <string/>
+  </property>
+  <layout class="QHBoxLayout" name="horizontalLayout">
+   <item>
+    <widget class="QLabel" name="fileIcon">
+     <property name="text">
+      <string>Icon</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <layout class="QVBoxLayout" name="verticalLayout">
+     <item>
+      <widget class="QLabel" name="filenameLabel">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="text">
+        <string>Filename</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignCenter</set>
+       </property>
+       <property name="wordWrap">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QProgressBar" name="progressBar"/>
+     </item>
+     <item>
+      <widget class="QLabel" name="infoLabel">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="text">
+        <string notr="true">Info</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignCenter</set>
+       </property>
+       <property name="wordWrap">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QToolButton" name="stopButton">
+     <property name="toolTip">
+      <string>Press to cancel the download</string>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QToolButton" name="openButton">
+     <property name="toolTip">
+      <string>Press to open the downloaded file</string>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <tabstops>
+  <tabstop>stopButton</tabstop>
+  <tabstop>openButton</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Download/DownloadManager.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,482 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2010 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the download manager class.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSlot, Qt, QModelIndex, QFileInfo, QUrl
+from PyQt5.QtGui import QCursor, QKeySequence
+from PyQt5.QtWidgets import QDialog, QStyle, QFileIconProvider, QMenu, \
+    QApplication, QShortcut
+
+from E5Gui import E5MessageBox
+
+from .Ui_DownloadManager import Ui_DownloadManager
+
+from .DownloadModel import DownloadModel
+
+from WebBrowser.WebBrowserWindow import WebBrowserWindow
+
+from Utilities.AutoSaver import AutoSaver
+import UI.PixmapCache
+import Preferences
+
+
+class DownloadManager(QDialog, Ui_DownloadManager):
+    """
+    Class implementing the download manager.
+    """
+    RemoveNever = 0
+    RemoveExit = 1
+    RemoveSuccessFullDownload = 2
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(DownloadManager, self).__init__(parent)
+        self.setupUi(self)
+        self.setWindowFlags(Qt.Window)
+        
+        self.__saveTimer = AutoSaver(self, self.save)
+        
+        self.__model = DownloadModel(self)
+        self.__manager = WebBrowserWindow.networkManager()
+        
+        self.__iconProvider = None
+        self.__downloads = []
+        self.__downloadDirectory = ""
+        self.__loaded = False
+        
+        self.setDownloadDirectory(Preferences.getUI("DownloadPath"))
+        
+        self.downloadsView.setShowGrid(False)
+        self.downloadsView.verticalHeader().hide()
+        self.downloadsView.horizontalHeader().hide()
+        self.downloadsView.setAlternatingRowColors(True)
+        self.downloadsView.horizontalHeader().setStretchLastSection(True)
+        self.downloadsView.setModel(self.__model)
+        self.downloadsView.setContextMenuPolicy(Qt.CustomContextMenu)
+        self.downloadsView.customContextMenuRequested.connect(
+            self.__customContextMenuRequested)
+        
+        self.__clearShortcut = QShortcut(QKeySequence("Ctrl+L"), self)
+        self.__clearShortcut.activated.connect(self.on_cleanupButton_clicked)
+        
+        self.__load()
+    
+    def __customContextMenuRequested(self, pos):
+        """
+        Private slot to handle the context menu request for the bookmarks tree.
+        
+        @param pos position the context menu was requested (QPoint)
+        """
+        menu = QMenu()
+        
+        selectedRowsCount = len(
+            self.downloadsView.selectionModel().selectedRows())
+        
+        if selectedRowsCount == 1:
+            row = self.downloadsView.selectionModel().selectedRows()[0].row()
+            itm = self.__downloads[row]
+            if itm.downloadedSuccessfully():
+                menu.addAction(
+                    UI.PixmapCache.getIcon("open.png"),
+                    self.tr("Open"), self.__contextMenuOpen)
+            elif itm.downloading():
+                menu.addAction(
+                    UI.PixmapCache.getIcon("stopLoading.png"),
+                    self.tr("Cancel"), self.__contextMenuCancel)
+                menu.addSeparator()
+            menu.addAction(
+                self.tr("Open Containing Folder"),
+                    self.__contextMenuOpenFolder)
+            menu.addSeparator()
+            menu.addAction(
+                self.tr("Go to Download Page"),
+                self.__contextMenuGotoPage)
+            menu.addAction(
+                self.tr("Copy Download Link"),
+                self.__contextMenuCopyLink)
+            menu.addSeparator()
+        menu.addAction(self.tr("Select All"), self.__contextMenuSelectAll)
+        if selectedRowsCount > 1 or \
+           (selectedRowsCount == 1 and
+            not self.__downloads[
+                self.downloadsView.selectionModel().selectedRows()[0].row()]
+                .downloading()):
+            menu.addSeparator()
+            menu.addAction(
+                self.tr("Remove From List"),
+                self.__contextMenuRemoveSelected)
+        
+        menu.exec_(QCursor.pos())
+    
+    def shutdown(self):
+        """
+        Public method to stop the download manager.
+        """
+        self.__saveTimer.changeOccurred()
+        self.__saveTimer.saveIfNeccessary()
+        self.close()
+    
+    def activeDownloads(self):
+        """
+        Public method to get the number of active downloads.
+        
+        @return number of active downloads (integer)
+        """
+        count = 0
+        
+        for download in self.__downloads:
+            if download.downloading():
+                count += 1
+        return count
+    
+    def allowQuit(self):
+        """
+        Public method to check, if it is ok to quit.
+        
+        @return flag indicating allowance to quit (boolean)
+        """
+        if self.activeDownloads() > 0:
+            res = E5MessageBox.yesNo(
+                self,
+                self.tr(""),
+                self.tr("""There are %n downloads in progress.\n"""
+                        """Do you want to quit anyway?""", "",
+                        self.activeDownloads()),
+                icon=E5MessageBox.Warning)
+            if not res:
+                self.show()
+                return False
+        return True
+    
+    def download(self, downloadItem):
+        """
+        Public method to download a file.
+        
+        @param downloadItem reference to the download object containing the
+        download data.
+        @type QWebEngineDownloadItem
+        """
+        if downloadItem.url().isEmpty():
+            return
+        
+        from .DownloadItem import DownloadItem
+        itm = DownloadItem(downloadItem, parent=self)
+        self.__addItem(itm)
+        
+        if itm.canceledFileSelect():
+            return
+        
+        if not self.isVisible():
+            self.show()
+        
+        self.activateWindow()
+        self.raise_()
+    
+    def __addItem(self, itm):
+        """
+        Private method to add a download to the list of downloads.
+        
+        @param itm reference to the download item (DownloadItem)
+        """
+        itm.statusChanged.connect(self.__updateRow)
+        itm.downloadFinished.connect(self.__finished)
+        
+        row = len(self.__downloads)
+        self.__model.beginInsertRows(QModelIndex(), row, row)
+        self.__downloads.append(itm)
+        self.__model.endInsertRows()
+        
+        self.downloadsView.setIndexWidget(self.__model.index(row, 0), itm)
+        icon = self.style().standardIcon(QStyle.SP_FileIcon)
+        itm.setIcon(icon)
+        self.downloadsView.setRowHeight(row, itm.sizeHint().height() * 1.5)
+        # just in case the download finished before the constructor returned
+        self.__updateRow(itm)
+        self.changeOccurred()
+        self.__updateActiveItemCount()
+    
+    def __updateRow(self, itm=None):
+        """
+        Private slot to update a download item.
+        
+        @param itm reference to the download item (DownloadItem)
+        """
+        if itm is None:
+            itm = self.sender()
+        
+        if itm not in self.__downloads:
+            return
+        
+        row = self.__downloads.index(itm)
+        
+        if self.__iconProvider is None:
+            self.__iconProvider = QFileIconProvider()
+        
+        icon = self.__iconProvider.icon(QFileInfo(itm.fileName()))
+        if icon.isNull():
+            icon = self.style().standardIcon(QStyle.SP_FileIcon)
+        itm.setIcon(icon)
+        
+        oldHeight = self.downloadsView.rowHeight(row)
+        self.downloadsView.setRowHeight(
+            row,
+            max(oldHeight, itm.minimumSizeHint().height() * 1.5))
+        
+        remove = False
+        
+        if itm.downloadedSuccessfully() and \
+           self.removePolicy() == DownloadManager.RemoveSuccessFullDownload:
+            remove = True
+        
+        if remove:
+            self.__model.removeRow(row)
+        
+        self.cleanupButton.setEnabled(
+            (len(self.__downloads) - self.activeDownloads()) > 0)
+        
+        # record the change
+        self.changeOccurred()
+    
+    def removePolicy(self):
+        """
+        Public method to get the remove policy.
+        
+        @return remove policy (integer)
+        """
+        return Preferences.getWebBrowser("DownloadManagerRemovePolicy")
+    
+    def setRemovePolicy(self, policy):
+        """
+        Public method to set the remove policy.
+        
+        @param policy policy to be set
+            (DownloadManager.RemoveExit, DownloadManager.RemoveNever,
+             DownloadManager.RemoveSuccessFullDownload)
+        """
+        assert policy in (DownloadManager.RemoveExit,
+                          DownloadManager.RemoveNever,
+                          DownloadManager.RemoveSuccessFullDownload)
+        
+        if policy == self.removePolicy():
+            return
+        
+        Preferences.setWebBrowser("DownloadManagerRemovePolicy", self.policy)
+    
+    def save(self):
+        """
+        Public method to save the download settings.
+        """
+        if not self.__loaded:
+            return
+        
+        Preferences.setWebBrowser("DownloadManagerSize", self.size())
+        Preferences.setWebBrowser("DownloadManagerPosition", self.pos())
+        if self.removePolicy() == DownloadManager.RemoveExit:
+            return
+        
+        from WebBrowser.WebBrowserWindow import WebBrowserWindow
+        if WebBrowserWindow.isPrivate():
+            return
+        
+        downloads = []
+        for download in self.__downloads:
+            downloads.append(download.getData())
+        Preferences.setWebBrowser("DownloadManagerDownloads", downloads)
+    
+    def __load(self):
+        """
+        Private method to load the download settings.
+        """
+        if self.__loaded:
+            return
+        
+        size = Preferences.getWebBrowser("DownloadManagerSize")
+        if size.isValid():
+            self.resize(size)
+        pos = Preferences.getWebBrowser("DownloadManagerPosition")
+        self.move(pos)
+        
+        downloads = Preferences.getWebBrowser("DownloadManagerDownloads")
+        for download in downloads:
+            if not download[0].isEmpty() and \
+               download[1] != "":
+                from .DownloadItem import DownloadItem
+                itm = DownloadItem(parent=self)
+                itm.setData(download)
+                self.__addItem(itm)
+        self.cleanupButton.setEnabled(
+            (len(self.__downloads) - self.activeDownloads()) > 0)
+        
+        self.__loaded = True
+        self.__updateActiveItemCount()
+    
+    def cleanup(self):
+        """
+        Public slot to cleanup the downloads.
+        """
+        self.on_cleanupButton_clicked()
+    
+    @pyqtSlot()
+    def on_cleanupButton_clicked(self):
+        """
+        Private slot cleanup the downloads.
+        """
+        if len(self.__downloads) == 0:
+            return
+        
+        self.__model.removeRows(0, len(self.__downloads))
+        if len(self.__downloads) == 0 and \
+           self.__iconProvider is not None:
+            self.__iconProvider = None
+        
+        self.changeOccurred()
+        self.__updateActiveItemCount()
+    
+    def __updateItemCount(self):
+        """
+        Private method to update the count label.
+        """
+        count = len(self.__downloads)
+        self.countLabel.setText(self.tr("%n Download(s)", "", count))
+    
+    def __updateActiveItemCount(self):
+        """
+        Private method to update the window title.
+        """
+        count = self.activeDownloads()
+        if count > 0:
+            self.setWindowTitle(
+                self.tr("Downloading %n file(s)", "", count))
+        else:
+            self.setWindowTitle(self.tr("Downloads"))
+    
+    def __finished(self):
+        """
+        Private slot to handle a finished download.
+        """
+        self.__updateActiveItemCount()
+        if self.isVisible():
+            QApplication.alert(self)
+    
+    def setDownloadDirectory(self, directory):
+        """
+        Public method to set the current download directory.
+        
+        @param directory current download directory (string)
+        """
+        self.__downloadDirectory = directory
+        if self.__downloadDirectory != "":
+            self.__downloadDirectory += "/"
+    
+    def downloadDirectory(self):
+        """
+        Public method to get the current download directory.
+        
+        @return current download directory (string)
+        """
+        return self.__downloadDirectory
+    
+    def count(self):
+        """
+        Public method to get the number of downloads.
+        
+        @return number of downloads (integer)
+        """
+        return len(self.__downloads)
+    
+    def downloads(self):
+        """
+        Public method to get a reference to the downloads.
+        
+        @return reference to the downloads (list of DownloadItem)
+        """
+        return self.__downloads
+    
+    def changeOccurred(self):
+        """
+        Public method to signal a change.
+        """
+        self.__saveTimer.changeOccurred()
+        self.__updateItemCount()
+    
+    ###########################################################################
+    ## Context menu related methods below
+    ###########################################################################
+    
+    def __currentItem(self):
+        """
+        Private method to get a reference to the current item.
+        
+        @return reference to the current item (DownloadItem)
+        """
+        index = self.downloadsView.currentIndex()
+        if index and index.isValid():
+            row = index.row()
+            return self.__downloads[row]
+        
+        return None
+    
+    def __contextMenuOpen(self):
+        """
+        Private method to open the downloaded file.
+        """
+        itm = self.__currentItem()
+        if itm is not None:
+            itm.openFile()
+    
+    def __contextMenuOpenFolder(self):
+        """
+        Private method to open the folder containing the downloaded file.
+        """
+        itm = self.__currentItem()
+        if itm is not None:
+            itm.openFolder()
+    
+    def __contextMenuCancel(self):
+        """
+        Private method to cancel the current download.
+        """
+        itm = self.__currentItem()
+        if itm is not None:
+            itm.cancelDownload()
+    
+    def __contextMenuGotoPage(self):
+        """
+        Private method to open the download page.
+        """
+        itm = self.__currentItem()
+        if itm is not None:
+            url = itm.getPageUrl()
+            WebBrowserWindow.mainWindow().openUrl(url, "")
+    
+    def __contextMenuCopyLink(self):
+        """
+        Private method to copy the download link to the clipboard.
+        """
+        itm = self.__currentItem()
+        if itm is not None:
+            url = itm.getPageUrl().toDisplayString(QUrl.FullyDecoded)
+            QApplication.clipboard().setText(url)
+    
+    def __contextMenuSelectAll(self):
+        """
+        Private method to select all downloads.
+        """
+        self.downloadsView.selectAll()
+    
+    def __contextMenuRemoveSelected(self):
+        """
+        Private method to remove the selected downloads from the list.
+        """
+        self.downloadsView.removeSelected()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Download/DownloadManager.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>DownloadManager</class>
+ <widget class="QDialog" name="DownloadManager">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>400</width>
+    <height>300</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Downloads</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QGridLayout" name="gridLayout">
+   <item row="0" column="0" colspan="3">
+    <widget class="E5TableView" name="downloadsView"/>
+   </item>
+   <item row="1" column="0">
+    <layout class="QHBoxLayout" name="horizontalLayout_2">
+     <item>
+      <widget class="QPushButton" name="cleanupButton">
+       <property name="enabled">
+        <bool>false</bool>
+       </property>
+       <property name="toolTip">
+        <string>Press to clean up the list of downloads</string>
+       </property>
+       <property name="text">
+        <string>Clear List</string>
+       </property>
+       <property name="autoDefault">
+        <bool>false</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="horizontalSpacer_2">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+   <item row="1" column="1">
+    <widget class="QLabel" name="countLabel">
+     <property name="text">
+      <string>0 Items</string>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="2">
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QDialogButtonBox" name="buttonBox">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="standardButtons">
+        <set>QDialogButtonBox::Close</set>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>E5TableView</class>
+   <extends>QTableView</extends>
+   <header>E5Gui/E5TableView.h</header>
+  </customwidget>
+ </customwidgets>
+ <tabstops>
+  <tabstop>downloadsView</tabstop>
+  <tabstop>cleanupButton</tabstop>
+  <tabstop>buttonBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>DownloadManager</receiver>
+   <slot>close()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>330</x>
+     <y>274</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>294</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Download/DownloadModel.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,116 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2010 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the download model.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import Qt, QAbstractListModel, QModelIndex, QMimeData, QUrl
+
+
+class DownloadModel(QAbstractListModel):
+    """
+    Class implementing the download model.
+    """
+    def __init__(self, manager, parent=None):
+        """
+        Constructor
+        
+        @param manager reference to the download manager (DownloadManager)
+        @param parent reference to the parent object (QObject)
+        """
+        super(DownloadModel, self).__init__(parent)
+        
+        self.__manager = manager
+    
+    def data(self, index, role):
+        """
+        Public method to get data from the model.
+        
+        @param index index to get data for (QModelIndex)
+        @param role role of the data to retrieve (integer)
+        @return requested data
+        """
+        if index.row() < 0 or index.row() >= self.rowCount(index.parent()):
+            return None
+        
+        if role == Qt.ToolTipRole:
+            if self.__manager.downloads()[index.row()]\
+                    .downloadedSuccessfully():
+                return self.__manager.downloads()[index.row()].getInfoData()
+        
+        return None
+    
+    def rowCount(self, parent=QModelIndex()):
+        """
+        Public method to get the number of rows of the model.
+        
+        @param parent parent index (QModelIndex)
+        @return number of rows (integer)
+        """
+        if parent.isValid():
+            return 0
+        else:
+            return self.__manager.count()
+    
+    def removeRows(self, row, count, parent=QModelIndex()):
+        """
+        Public method to remove bookmarks from the model.
+        
+        @param row row of the first bookmark to remove (integer)
+        @param count number of bookmarks to remove (integer)
+        @param parent index of the parent bookmark node (QModelIndex)
+        @return flag indicating successful removal (boolean)
+        """
+        if parent.isValid():
+            return False
+        
+        if row < 0 or count <= 0 or row + count > self.rowCount(parent):
+            return False
+        
+        lastRow = row + count - 1
+        for i in range(lastRow, row - 1, -1):
+            if not self.__manager.downloads()[i].downloading():
+                self.beginRemoveRows(parent, i, i)
+                del self.__manager.downloads()[i]
+                self.endRemoveRows()
+        self.__manager.changeOccurred()
+        return True
+    
+    def flags(self, index):
+        """
+        Public method to get flags for an item.
+        
+        @param index index of the node cell (QModelIndex)
+        @return flags (Qt.ItemFlags)
+        """
+        if index.row() < 0 or index.row() >= self.rowCount(index.parent()):
+            return Qt.NoItemFlags
+        
+        defaultFlags = QAbstractListModel.flags(self, index)
+        
+        itm = self.__manager.downloads()[index.row()]
+        if itm.downloadedSuccessfully():
+            return defaultFlags | Qt.ItemIsDragEnabled
+        
+        return defaultFlags
+    
+    def mimeData(self, indexes):
+        """
+        Public method to return the mime data.
+        
+        @param indexes list of indexes (QModelIndexList)
+        @return mime data (QMimeData)
+        """
+        mimeData = QMimeData()
+        urls = []
+        for index in indexes:
+            if index.isValid():
+                itm = self.__manager.downloads()[index.row()]
+                urls.append(QUrl.fromLocalFile(itm.absoluteFilePath()))
+        mimeData.setUrls(urls)
+        return mimeData
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Download/DownloadUtilities.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,59 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2010 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing some utility functions for the Download package.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import QCoreApplication
+
+from Globals import translate
+
+
+def timeString(timeRemaining):
+    """
+    Module function to format the given time.
+    
+    @param timeRemaining time to be formatted (float)
+    @return time string (string)
+    """
+    if timeRemaining > 60:
+        minutes = int(timeRemaining / 60)
+        seconds = int(timeRemaining % 60)
+        remaining = translate(
+            "DownloadUtilities",
+            "%n:{0:02} minutes remaining", "",
+            minutes).format(seconds)
+    else:
+        seconds = int(timeRemaining)
+        remaining = translate(
+            "DownloadUtilities",
+            "%n seconds remaining", "", seconds)
+    
+    return remaining
+
+
+def dataString(size):
+    """
+    Module function to generate a formatted size string.
+    
+    @param size size to be formatted (integer)
+    @return formatted data string (string)
+    """
+    unit = ""
+    if size < 1024:
+        unit = QCoreApplication.translate("DownloadUtilities", "Bytes")
+    elif size < 1024 * 1024:
+        size /= 1024
+        unit = QCoreApplication.translate("DownloadUtilities", "KiB")
+    elif size < 1024 * 1024 * 1024:
+        size /= 1024 * 1024
+        unit = QCoreApplication.translate("DownloadUtilities", "MiB")
+    else:
+        size /= 1024 * 1024 * 1024
+        unit = QCoreApplication.translate("DownloadUtilities", "GiB")
+    return "{0:.1f} {1}".format(size, unit)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Download/__init__.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2010 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Package implementing all download related modules.
+"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/FeaturePermissions/FeaturePermissionBar.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,167 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2015 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the feature permission bar widget.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSlot, QUrl
+from PyQt5.QtWidgets import QLabel, QHBoxLayout, QPushButton
+from PyQt5.QtWebEngineWidgets import QWebEnginePage
+
+from E5Gui.E5AnimatedWidget import E5AnimatedWidget
+
+import UI.PixmapCache
+
+
+class FeaturePermissionBar(E5AnimatedWidget):
+    """
+    Class implementing the feature permission bar widget.
+    """
+    DefaultHeight = 30
+    
+    def __init__(self, page, origin, feature, manager):
+        """
+        Constructor
+        
+        @param page reference to the web page
+        @type QWebView
+        @param origin security origin requesting the feature
+        @type QUrl
+        @param feature requested feature
+        @type QWebPage.Feature
+        @param manager reference to the feature permissions manager
+        @type FeaturePermissionManager
+        """
+        super(FeaturePermissionBar, self).__init__(parent=page.view())
+        
+        self.__origin = QUrl(origin)
+        self.__feature = feature
+        self.__page = page
+        self.__manager = manager
+        
+        self.__permissionFeatureTexts = {
+            # TODO: Qt 5.6
+##            QWebEnginePage.Notifications:
+##                self.tr("{0} wants to use desktop notifications."),
+            QWebEnginePage.Geolocation:
+                self.tr("{0} wants to use your position."),
+            QWebEnginePage.MediaAudioCapture:
+                self.tr("{0} wants to use your microphone."),
+            QWebEnginePage.MediaVideoCapture:
+                self.tr("{0} wants to use your camera."),
+            QWebEnginePage.MediaAudioVideoCapture:
+                self.tr("{0} wants to use your microphone and camera."),
+        }
+        self.__permissionFeatureIconNames = {
+            # TODO: Qt 5.6
+##            QWebEnginePage.Notifications: "notification.png",
+            QWebEnginePage.Geolocation: "geolocation.png",
+            QWebEnginePage.MediaAudioCapture: "audiocapture.png",
+            QWebEnginePage.MediaVideoCapture: "camera.png",
+            QWebEnginePage.MediaAudioVideoCapture: "audio-video.png",
+        }
+        
+        self.setAutoFillBackground(True)
+        self.__layout = QHBoxLayout()
+        self.setLayout(self.__layout)
+        self.__layout.setContentsMargins(9, 0, 0, 0)
+        self.__iconLabel = QLabel(self)
+        self.__layout.addWidget(self.__iconLabel)
+        self.__messageLabel = QLabel(self)
+        self.__layout.addWidget(self.__messageLabel)
+        self.__layout.addStretch()
+        self.__rememberButton = QPushButton(self.tr("Remember"), self)
+        self.__rememberButton.setCheckable(True)
+        self.__allowButton = QPushButton(self.tr("Allow"), self)
+        self.__denyButton = QPushButton(self.tr("Deny"), self)
+        self.__discardButton = QPushButton(UI.PixmapCache.getIcon("close.png"),
+                                           "", self)
+        self.__allowButton.clicked.connect(self.__permissionGranted)
+        self.__denyButton.clicked.connect(self.__permissionDenied)
+        self.__discardButton.clicked.connect(self.__permissionUnknown)
+        self.__layout.addWidget(self.__rememberButton)
+        self.__layout.addWidget(self.__allowButton)
+        self.__layout.addWidget(self.__denyButton)
+        self.__layout.addWidget(self.__discardButton)
+        
+        try:
+            self.__iconLabel.setPixmap(UI.PixmapCache.getPixmap(
+                self.__permissionFeatureIconNames[self.__feature]))
+        except KeyError:
+            pass
+        
+        try:
+            self.__messageLabel.setText(
+                self.__permissionFeatureTexts[self.__feature].format(
+                    self.__origin.host()))
+        except KeyError:
+            self.__messageLabel.setText(
+                self.tr("{0} wants to use an unknown feature.").format(
+                    self.__origin.host()))
+        
+        self.__page.loadStarted.connect(self.hide)
+        
+        self.resize(self.__page.view().width(), self.height())
+        self.startAnimation()
+    
+    @pyqtSlot()
+    def hide(self):
+        """
+        Public slot to hide the animated widget.
+        """
+        self.__page.loadStarted.disconnect(self.hide)
+        super(FeaturePermissionBar, self).hide()
+    
+    def __permissionDenied(self):
+        """
+        Private slot handling the user pressing the deny button.
+        """
+        if self.__page is None or self.__manager is None:
+            return
+        
+        self.__page.setFeaturePermission(
+            self.__origin, self.__feature,
+            QWebEnginePage.PermissionDeniedByUser)
+        
+        if self.__rememberButton.isChecked():
+            self.__manager.rememberFeaturePermission(
+                self.__page.url().host(), self.__feature,
+                QWebEnginePage.PermissionDeniedByUser)
+        
+        self.hide()
+    
+    def __permissionGranted(self):
+        """
+        Private slot handling the user pressing the allow button.
+        """
+        if self.__page is None or self.__manager is None:
+            return
+        
+        self.__page.setFeaturePermission(
+            self.__origin, self.__feature,
+            QWebEnginePage.PermissionGrantedByUser)
+        
+        if self.__rememberButton.isChecked():
+            self.__manager.rememberFeaturePermission(
+                self.__page.url().host(), self.__feature,
+                QWebEnginePage.PermissionGrantedByUser)
+        
+        self.hide()
+    
+    def __permissionUnknown(self):
+        """
+        Private slot handling the user closing the dialog without.
+        """
+        if self.__page is None or self.__manager is None:
+            return
+        
+        self.__page.setFeaturePermission(
+            self.__origin, self.__feature,
+            QWebEnginePage.PermissionUnknown)
+        
+        self.hide()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/FeaturePermissions/FeaturePermissionManager.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,187 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2015 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the feature permission manager object.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import QObject
+from PyQt5.QtWidgets import QDialog
+from PyQt5.QtWebEngineWidgets import QWebEnginePage
+
+import Globals
+import Preferences
+
+
+class FeaturePermissionManager(QObject):
+    """
+    Class implementing the feature permission manager object.
+    """
+    SettingsKeyFormat = "WebBrowser/FeaturePermissions/{0}"
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object
+        @type QObject
+        """
+        super(FeaturePermissionManager, self).__init__(parent)
+        
+        self.__featurePermissions = {
+            # TODO: Qt 5.6
+##            QWebEnginePage.Notifications: {
+##                QWebEnginePage.PermissionGrantedByUser: [],
+##                QWebEnginePage.PermissionDeniedByUser: [],
+##            },
+            QWebEnginePage.Geolocation: {
+                QWebEnginePage.PermissionGrantedByUser: [],
+                QWebEnginePage.PermissionDeniedByUser: [],
+            },
+            QWebEnginePage.MediaAudioCapture: {
+                QWebEnginePage.PermissionGrantedByUser: [],
+                QWebEnginePage.PermissionDeniedByUser: [],
+            },
+            QWebEnginePage.MediaVideoCapture: {
+                QWebEnginePage.PermissionGrantedByUser: [],
+                QWebEnginePage.PermissionDeniedByUser: [],
+            },
+            QWebEnginePage.MediaAudioVideoCapture: {
+                QWebEnginePage.PermissionGrantedByUser: [],
+                QWebEnginePage.PermissionDeniedByUser: [],
+            },
+        }
+        self.__featurePermissionsKeys = {
+            # TODO: Qt 5.6
+##            (QWebEnginePage.Notifications,
+##             QWebEnginePage.PermissionGrantedByUser):
+##            "NotificationsGranted",
+##            (QWebEnginePage.Notifications,
+##             QWebEnginePage.PermissionDeniedByUser):
+##            "NotificationsDenied",
+            (QWebEnginePage.Geolocation,
+             QWebEnginePage.PermissionGrantedByUser):
+            "GeolocationGranted",
+            (QWebEnginePage.Geolocation,
+             QWebEnginePage.PermissionDeniedByUser):
+            "GeolocationDenied",
+            (QWebEnginePage.MediaAudioCapture,
+             QWebEnginePage.PermissionGrantedByUser):
+            "MediaAudioCaptureGranted",
+            (QWebEnginePage.MediaAudioCapture,
+             QWebEnginePage.PermissionDeniedByUser):
+            "MediaAudioCaptureDenied",
+            (QWebEnginePage.MediaVideoCapture,
+             QWebEnginePage.PermissionGrantedByUser):
+            "MediaVideoCaptureGranted",
+            (QWebEnginePage.MediaVideoCapture,
+             QWebEnginePage.PermissionDeniedByUser):
+            "MediaVideoCaptureDenied",
+            (QWebEnginePage.MediaAudioVideoCapture,
+             QWebEnginePage.PermissionGrantedByUser):
+            "MediaAudioVideoCaptureGranted",
+            (QWebEnginePage.MediaAudioVideoCapture,
+             QWebEnginePage.PermissionDeniedByUser):
+            "MediaAudioVideoCaptureDenied",
+        }
+        
+        self.__loaded = False
+
+    def requestFeaturePermission(self, page, origin, feature):
+        """
+        Public method to request a feature permission.
+        
+        @param page reference to the requesting web page
+        @type QWebEnginePage
+        @param origin security origin requesting the feature
+        @type QUrl
+        @param feature requested feature
+        @type QWebEnginePage.Feature
+        """
+        if origin is None or origin.isEmpty():
+            return
+        
+        if not self.__loaded:
+            self.__loadSettings()
+        
+        host = origin.host()
+        
+        if feature in self.__featurePermissions:
+            for permission in self.__featurePermissions[feature]:
+                if host in self.__featurePermissions[feature][permission]:
+                    page.setFeaturePermission(origin, feature, permission)
+                    return
+        
+        from .FeaturePermissionBar import FeaturePermissionBar
+        bar = FeaturePermissionBar(page, origin, feature, self)
+        bar.show()
+    
+    def rememberFeaturePermission(self, host, feature, permission):
+        """
+        Public method to remember a user decision for a feature permission.
+        
+        @param host host name to remember the decision for
+        @type str
+        @param feature feature to be remembered
+        @type QWebEnginePage.Feature
+        @param permission feature permission to be remembered
+        @type QWebEnginePage.PermissionPolicy
+        """
+        if feature in self.__featurePermissions:
+            if host not in self.__featurePermissions[feature][permission]:
+                self.__featurePermissions[feature][permission].append(host)
+                self.__saveSettings()
+    
+    def __loadSettings(self):
+        """
+        Private method to load the remembered feature permissions.
+        """
+        if self.__loaded:
+            # no reloading allowed
+            return
+        
+        for (feature, permission), key in \
+                self.__featurePermissionsKeys.items():
+            self.__featurePermissions[feature][permission] = \
+                Globals.toList(Preferences.Prefs.settings.value(
+                    FeaturePermissionManager.SettingsKeyFormat.format(key),
+                    []
+                ))
+        
+        self.__loaded = True
+    
+    def __saveSettings(self):
+        """
+        Private method to save the remembered feature permissions.
+        """
+        if not self.__loaded:
+            return
+        
+        import WebBrowser.WebBrowserWindow
+        if WebBrowser.WebBrowserWindow.WebBrowserWindow.isPrivate():
+            return
+        
+        for (feature, permission), key in \
+                self.__featurePermissionsKeys.items():
+            Preferences.Prefs.settings.setValue(
+                FeaturePermissionManager.SettingsKeyFormat.format(key),
+                self.__featurePermissions[feature][permission])
+    
+    def showFeaturePermissionsDialog(self):
+        """
+        Public method to show a dialog to manage the remembered feature
+        permissions.
+        """
+        if not self.__loaded:
+            self.__loadSettings()
+        
+        from .FeaturePermissionsDialog import FeaturePermissionsDialog
+        dlg = FeaturePermissionsDialog(self.__featurePermissions)
+        if dlg.exec_() == QDialog.Accepted:
+            newFeaturePermissions = dlg.getData()
+            self.__featurePermissions = newFeaturePermissions
+            self.__saveSettings()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/FeaturePermissions/FeaturePermissionsDialog.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,227 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2015 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the feature permission dialog.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSlot, Qt
+from PyQt5.QtWidgets import QDialog, QTreeWidgetItem, QTreeWidget, \
+    QAbstractItemView
+from PyQt5.QtWebEngineWidgets import QWebEnginePage
+
+import UI.PixmapCache
+
+from .Ui_FeaturePermissionsDialog import Ui_FeaturePermissionsDialog
+
+
+class FeaturePermissionsDialog(QDialog, Ui_FeaturePermissionsDialog):
+    """
+    Class implementing the feature permission dialog.
+    """
+    def __init__(self, featurePermissions, parent=None):
+        """
+        Constructor
+        
+        @param featurePermissions dictionary with remembered feature
+            permissions
+        @type dict of dict of list
+        @param parent reference to the parent widget
+        @type QWidget
+        """
+        super(FeaturePermissionsDialog, self).__init__(parent)
+        self.setupUi(self)
+        
+        # add the various lists
+        self.notifList = QTreeWidget()
+        self.notifList.setAlternatingRowColors(True)
+        self.notifList.setSelectionMode(QAbstractItemView.ExtendedSelection)
+        self.notifList.setRootIsDecorated(False)
+        self.notifList.setItemsExpandable(False)
+        self.notifList.setAllColumnsShowFocus(True)
+        self.notifList.setObjectName("notifList")
+        self.notifList.setSortingEnabled(True)
+        self.notifList.headerItem().setText(0, self.tr("Host"))
+        self.notifList.headerItem().setText(1, self.tr("Permission"))
+        self.tabWidget.addTab(
+            self.notifList, 
+            UI.PixmapCache.getIcon("notification.png"),
+            self.tr("Notifications"))
+        
+        self.geoList = QTreeWidget()
+        self.geoList.setAlternatingRowColors(True)
+        self.geoList.setSelectionMode(QAbstractItemView.ExtendedSelection)
+        self.geoList.setRootIsDecorated(False)
+        self.geoList.setItemsExpandable(False)
+        self.geoList.setAllColumnsShowFocus(True)
+        self.geoList.setObjectName("geoList")
+        self.geoList.setSortingEnabled(True)
+        self.geoList.headerItem().setText(0, self.tr("Host"))
+        self.geoList.headerItem().setText(1, self.tr("Permission"))
+        self.tabWidget.addTab(
+            self.geoList,
+            UI.PixmapCache.getIcon("geolocation.png"),
+            self.tr("Geolocation"))
+        
+        self.micList = QTreeWidget()
+        self.micList.setAlternatingRowColors(True)
+        self.micList.setSelectionMode(QAbstractItemView.ExtendedSelection)
+        self.micList.setRootIsDecorated(False)
+        self.micList.setItemsExpandable(False)
+        self.micList.setAllColumnsShowFocus(True)
+        self.micList.setObjectName("micList")
+        self.micList.setSortingEnabled(True)
+        self.micList.headerItem().setText(0, self.tr("Host"))
+        self.micList.headerItem().setText(1, self.tr("Permission"))
+        self.tabWidget.addTab(
+            self.micList,
+            UI.PixmapCache.getIcon("audiocapture.png"),
+            self.tr("Microphone"))
+        
+        self.camList = QTreeWidget()
+        self.camList.setAlternatingRowColors(True)
+        self.camList.setSelectionMode(QAbstractItemView.ExtendedSelection)
+        self.camList.setRootIsDecorated(False)
+        self.camList.setItemsExpandable(False)
+        self.camList.setAllColumnsShowFocus(True)
+        self.camList.setObjectName("camList")
+        self.camList.setSortingEnabled(True)
+        self.camList.headerItem().setText(0, self.tr("Host"))
+        self.camList.headerItem().setText(1, self.tr("Permission"))
+        self.tabWidget.addTab(
+            self.camList,
+            UI.PixmapCache.getIcon("camera.png"),
+            self.tr("Camera"))
+        
+        self.micCamList = QTreeWidget()
+        self.micCamList.setAlternatingRowColors(True)
+        self.micCamList.setSelectionMode(QAbstractItemView.ExtendedSelection)
+        self.micCamList.setRootIsDecorated(False)
+        self.micCamList.setItemsExpandable(False)
+        self.micCamList.setAllColumnsShowFocus(True)
+        self.micCamList.setObjectName("micCamList")
+        self.micCamList.setSortingEnabled(True)
+        self.micCamList.headerItem().setText(0, self.tr("Host"))
+        self.micCamList.headerItem().setText(1, self.tr("Permission"))
+        self.tabWidget.addTab(
+            self.micCamList,
+            UI.PixmapCache.getIcon("audio-video.png"),
+            self.tr("Microphone && Camera"))
+        
+        self.setTabOrder(self.tabWidget, self.notifList)
+        self.setTabOrder(self.notifList, self.geoList)
+        self.setTabOrder(self.geoList, self.micList)
+        self.setTabOrder(self.micList, self.camList)
+        self.setTabOrder(self.camList, self.micCamList)
+        self.setTabOrder(self.micCamList, self.removeButton)
+        self.setTabOrder(self.removeButton, self.removeAllButton)
+        
+        self.__permissionStrings = {
+            QWebEnginePage.PermissionGrantedByUser: self.tr("Allow"),
+            QWebEnginePage.PermissionDeniedByUser: self.tr("Deny"),
+        }
+        
+        self.__permissionsLists = {
+            # TODO: Qt 5.6
+##            QWebEnginePage.Notifications: self.notifList,
+            QWebEnginePage.Geolocation: self.geoList,
+            QWebEnginePage.MediaAudioCapture: self.micList,
+            QWebEnginePage.MediaVideoCapture: self.camList,
+            QWebEnginePage.MediaAudioVideoCapture: self.micCamList,
+        }
+        
+        for feature, permissionsList in self.__permissionsLists.items():
+            for permission in featurePermissions[feature]:
+                for host in featurePermissions[feature][permission]:
+                    itm = QTreeWidgetItem(
+                        permissionsList,
+                        [host, self.__permissionStrings[permission]])
+                    itm.setData(0, Qt.UserRole, permission)
+        
+        self.__previousCurrent = -1
+        self.tabWidget.currentChanged.connect(self.__currentTabChanged)
+        self.tabWidget.setCurrentIndex(0)
+    
+    @pyqtSlot(int)
+    def __currentTabChanged(self, index):
+        """
+        Private slot handling changes of the selected tab.
+        
+        @param index index of the current tab
+        @type int
+        """
+        if self.__previousCurrent >= 0:
+            previousList = self.tabWidget.widget(self.__previousCurrent)
+            previousList.itemSelectionChanged.disconnect(
+                self.__itemSelectionChanged)
+        
+        self.__updateButtons()
+        
+        currentList = self.tabWidget.currentWidget()
+        currentList.itemSelectionChanged.connect(self.__itemSelectionChanged)
+        self.__previousCurrent = index
+    
+    def __updateButtons(self):
+        """
+        Private method to update the buttons.
+        """
+        currentList = self.tabWidget.currentWidget()
+        self.removeAllButton.setEnabled(
+            currentList.topLevelItemCount() > 0)
+        self.removeButton.setEnabled(
+            len(currentList.selectedItems()) > 0)
+    
+    @pyqtSlot()
+    def __itemSelectionChanged(self):
+        """
+        Private slot handling changes in the current list of selected items.
+        """
+        self.__updateButtons()
+    
+    @pyqtSlot()
+    def on_removeButton_clicked(self):
+        """
+        Private slot to remove selected entries.
+        """
+        currentList = self.tabWidget.currentWidget()
+        for itm in currentList.selectedItems():
+            row = currentList.indexOfTopLevelItem(itm)
+            itm = currentList.takeTopLevelItem(row)
+            del itm
+        self.__updateButtons()
+    
+    @pyqtSlot()
+    def on_removeAllButton_clicked(self):
+        """
+        Private slot to remove all entries.
+        """
+        currentList = self.tabWidget.currentWidget()
+        while currentList.topLevelItemCount() > 0:
+            itm = currentList.takeTopLevelItem(0)      # __IGNORE_WARNING__
+            del itm
+        self.__updateGeoButtons()
+    
+    def getData(self):
+        """
+        Public method to retrieve the dialog contents.
+        
+        @return new feature permission settings
+        @rtype dict of dict of list
+        """
+        featurePermissions = {}
+        for feature, permissionsList in self.__permissionsLists.items():
+            featurePermissions[feature] = {
+                QWebEnginePage.PermissionGrantedByUser: [],
+                QWebEnginePage.PermissionDeniedByUser: [],
+            }
+            for row in range(permissionsList.topLevelItemCount()):
+                itm = permissionsList.topLevelItem(row)
+                host = itm.text(0)
+                permission = itm.data(0, Qt.UserRole)
+                featurePermissions[feature][permission].append(host)
+        
+        return featurePermissions
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/FeaturePermissions/FeaturePermissionsDialog.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,110 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>FeaturePermissionsDialog</class>
+ <widget class="QDialog" name="FeaturePermissionsDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>650</width>
+    <height>400</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>HTML5 Feature Permissions</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QGridLayout" name="gridLayout">
+   <item row="1" column="0" colspan="2">
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+   <item row="0" column="1">
+    <layout class="QVBoxLayout" name="verticalLayout_3">
+     <item>
+      <spacer name="verticalSpacer_2">
+       <property name="orientation">
+        <enum>Qt::Vertical</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>20</width>
+         <height>40</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QPushButton" name="removeButton">
+       <property name="text">
+        <string>&amp;Remove</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="removeAllButton">
+       <property name="text">
+        <string>Remove &amp;All</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item row="0" column="0">
+    <widget class="QTabWidget" name="tabWidget">
+     <property name="currentIndex">
+      <number>-1</number>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <tabstops>
+  <tabstop>tabWidget</tabstop>
+  <tabstop>removeButton</tabstop>
+  <tabstop>removeAllButton</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>FeaturePermissionsDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>227</x>
+     <y>381</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>FeaturePermissionsDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>295</x>
+     <y>387</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/FeaturePermissions/__init__.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,9 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2015 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Package implementing feature permission related widgets for the eric6
+web browser.
+"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Feeds/FeedEditDialog.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,83 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2011 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog to edit feed data.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSlot, QUrl
+from PyQt5.QtWidgets import QDialog, QDialogButtonBox
+
+from .Ui_FeedEditDialog import Ui_FeedEditDialog
+
+
+class FeedEditDialog(QDialog, Ui_FeedEditDialog):
+    """
+    Class implementing a dialog to edit feed data.
+    """
+    def __init__(self, urlString, title, parent=None):
+        """
+        Constructor
+        
+        @param urlString feed URL (string)
+        @param title feed title (string)
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(FeedEditDialog, self).__init__(parent)
+        self.setupUi(self)
+        
+        self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(False)
+        
+        self.titleEdit.setText(title)
+        self.urlEdit.setText(urlString)
+        
+        msh = self.minimumSizeHint()
+        self.resize(max(self.width(), msh.width()), msh.height())
+    
+    def __setOkButton(self):
+        """
+        Private slot to enable or disable the OK button.
+        """
+        enable = True
+        
+        enable = enable and bool(self.titleEdit.text())
+        
+        urlString = self.urlEdit.text()
+        enable = enable and bool(urlString)
+        if urlString:
+            url = QUrl(urlString)
+            enable = enable and bool(url.scheme())
+            enable = enable and bool(url.host())
+        
+        self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(enable)
+    
+    @pyqtSlot(str)
+    def on_titleEdit_textChanged(self, txt):
+        """
+        Private slot to handle changes of the feed title.
+        
+        @param txt new feed title (string)
+        """
+        self.__setOkButton()
+    
+    @pyqtSlot(str)
+    def on_urlEdit_textChanged(self, txt):
+        """
+        Private slot to handle changes of the feed URL.
+        
+        @param txt new feed URL (string)
+        """
+        self.__setOkButton()
+    
+    def getData(self):
+        """
+        Public method to get the entered feed data.
+        
+        @return tuple of two strings giving the feed URL and feed title
+            (string, string)
+        """
+        return (self.urlEdit.text(), self.titleEdit.text())
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Feeds/FeedEditDialog.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>FeedEditDialog</class>
+ <widget class="QDialog" name="FeedEditDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>475</width>
+    <height>114</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Edit Feed Data</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QGridLayout" name="gridLayout">
+     <item row="0" column="0" colspan="2">
+      <widget class="QLabel" name="label">
+       <property name="text">
+        <string>Fill title and URL of a feed:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="0">
+      <widget class="QLabel" name="label_2">
+       <property name="text">
+        <string>Feed title:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="1">
+      <widget class="QLineEdit" name="titleEdit">
+       <property name="toolTip">
+        <string>Enter the title of the feed</string>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="0">
+      <widget class="QLabel" name="label_3">
+       <property name="text">
+        <string>Feed URL:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="1">
+      <widget class="QLineEdit" name="urlEdit">
+       <property name="toolTip">
+        <string>Enter the URL of the feed</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <tabstops>
+  <tabstop>titleEdit</tabstop>
+  <tabstop>urlEdit</tabstop>
+  <tabstop>buttonBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>FeedEditDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>224</x>
+     <y>122</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>143</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>FeedEditDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>292</x>
+     <y>128</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>143</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Feeds/FeedsDialog.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,106 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2011 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog to add RSS feeds.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import QUrl, qVersion
+from PyQt5.QtWidgets import QDialog, QPushButton, QLabel
+
+from E5Gui import E5MessageBox
+
+from .Ui_FeedsDialog import Ui_FeedsDialog
+
+import UI.PixmapCache
+
+
+class FeedsDialog(QDialog, Ui_FeedsDialog):
+    """
+    Class implementing a dialog to add RSS feeds.
+    """
+    def __init__(self, availableFeeds, browser, parent=None):
+        """
+        Constructor
+        
+        @param availableFeeds list of available RSS feeds (list of tuple of
+            two strings)
+        @param browser reference to the browser widget (WebBrowserView)
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(FeedsDialog, self).__init__(parent)
+        self.setupUi(self)
+        
+        self.iconLabel.setPixmap(UI.PixmapCache.getPixmap("rss48.png"))
+        
+        self.__browser = browser
+        
+        self.__availableFeeds = availableFeeds[:]
+        for row in range(len(self.__availableFeeds)):
+            feed = self.__availableFeeds[row]
+            button = QPushButton(self)
+            button.setText(self.tr("Add"))
+            button.feed = feed
+            label = QLabel(self)
+            label.setText(feed[0])
+            self.feedsLayout.addWidget(label, row, 0)
+            self.feedsLayout.addWidget(button, row, 1)
+            button.clicked.connect(self.__addFeed)
+        
+        msh = self.minimumSizeHint()
+        self.resize(max(self.width(), msh.width()), msh.height())
+    
+    def __addFeed(self):
+        """
+        Private slot to add a RSS feed.
+        """
+        button = self.sender()
+        urlString = button.feed[1]
+        url = QUrl(urlString)
+        if url.isRelative():
+            url = self.__browser.url().resolved(url)
+            if qVersion() >= "5.0.0":
+                urlString = url.toDisplayString(QUrl.FullyDecoded)
+            else:
+                urlString = url.toString()
+##            if not urlString.startswith("/"):
+##                urlString = "/" + urlString
+##            urlString = self.__browser.url().host() + urlString
+##            tmpUrl = QUrl(urlString)
+##            if not tmpUrl.scheme():
+##                urlString = "http://" + urlString
+##            tmpUrl = QUrl(urlString)
+##            if not tmpUrl.scheme() or not tmpUrl.host():
+##                return
+        if not url.isValid():
+            return
+        
+        if button.feed[0]:
+            title = button.feed[0]
+        else:
+            title = self.__browser.url().host()
+        
+        from WebBrowser.WebBrowserWindow import WebBrowserWindow
+        feedsManager = WebBrowserWindow.feedsManager()
+        if feedsManager.addFeed(urlString, title, self.__browser.icon()):
+            if WebBrowserWindow.notificationsEnabled():
+                WebBrowserWindow.showNotification(
+                    UI.PixmapCache.getPixmap("rss48.png"),
+                    self.tr("Add RSS Feed"),
+                    self.tr("""The feed was added successfully."""))
+            else:
+                E5MessageBox.information(
+                    self,
+                    self.tr("Add RSS Feed"),
+                    self.tr("""The feed was added successfully."""))
+        else:
+            E5MessageBox.warning(
+                self,
+                self.tr("Add RSS Feed"),
+                self.tr("""The feed was already added before."""))
+            
+        self.close()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Feeds/FeedsDialog.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>FeedsDialog</class>
+ <widget class="QDialog" name="FeedsDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>352</width>
+    <height>94</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Add Feed</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <widget class="QLabel" name="iconLabel">
+       <property name="text">
+        <string notr="true"/>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLabel" name="label">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="text">
+        <string>Add Feeds from this site</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <layout class="QGridLayout" name="feedsLayout"/>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Close</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>FeedsDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>FeedsDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Feeds/FeedsManager.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,434 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2011 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a RSS feeds manager dialog.
+"""
+
+from __future__ import unicode_literals
+try:
+    str = unicode       # __IGNORE_EXCEPTION__
+except NameError:
+    pass
+
+from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt, QUrl, QXmlStreamReader
+from PyQt5.QtGui import QIcon, QCursor
+from PyQt5.QtWidgets import QDialog, QTreeWidgetItem, QMenu, QApplication
+from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply
+
+from E5Gui import E5MessageBox
+
+from .Ui_FeedsManager import Ui_FeedsManager
+
+import Preferences
+import UI.PixmapCache
+
+
+class FeedsManager(QDialog, Ui_FeedsManager):
+    """
+    Class implementing a RSS feeds manager dialog.
+    
+    @signal openUrl(QUrl, str) emitted to open a URL in the current tab
+    @signal newUrl(QUrl, str) emitted to open a URL in a new tab
+    """
+    openUrl = pyqtSignal(QUrl, str)
+    newUrl = pyqtSignal(QUrl, str)
+    
+    UrlStringRole = Qt.UserRole
+    ErrorDataRole = Qt.UserRole + 1
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(FeedsManager, self).__init__(parent)
+        self.setupUi(self)
+        self.setWindowFlags(Qt.Window)
+        
+        self.__wasShown = False
+        self.__loaded = False
+        self.__feeds = []
+        self.__replies = {}
+        # dict key is the id of the request object
+        # dict value is a tuple of request and tree item
+        
+        self.feedsTree.setContextMenuPolicy(Qt.CustomContextMenu)
+        self.feedsTree.customContextMenuRequested.connect(
+            self.__customContextMenuRequested)
+        self.feedsTree.itemActivated.connect(self.__itemActivated)
+    
+    def show(self):
+        """
+        Public slot to show the feeds manager dialog.
+        """
+        super(FeedsManager, self).show()
+        
+        if not self.__wasShown:
+            self.__enableButtons()
+            self.on_reloadAllButton_clicked()
+            self.__wasShown = True
+    
+    def addFeed(self, urlString, title, icon):
+        """
+        Public method to add a feed.
+        
+        @param urlString URL of the feed (string)
+        @param title title of the feed (string)
+        @param icon icon for the feed (QIcon)
+        @return flag indicating a successful addition of the feed (boolean)
+        """
+        if urlString == "":
+            return False
+        
+        if not self.__loaded:
+            self.__load()
+        
+        # step 1: check, if feed was already added
+        for feed in self.__feeds:
+            if feed[0] == urlString:
+                return False
+        
+        # step 2: add the feed
+        if icon.isNull():
+            icon = UI.PixmapCache.getIcon("rss16.png")
+        feed = (urlString, title, icon)
+        self.__feeds.append(feed)
+        self.__addFeedItem(feed)
+        self.__save()
+        
+        return True
+    
+    def __addFeedItem(self, feed):
+        """
+        Private slot to add a top level feed item.
+        
+        @param feed tuple containing feed info (URL, title, icon)
+            (string, string, QIcon)
+        """
+        itm = QTreeWidgetItem(self.feedsTree, [feed[1]])
+        itm.setIcon(0, feed[2])
+        itm.setData(0, FeedsManager.UrlStringRole, feed[0])
+    
+    def __load(self):
+        """
+        Private method to load the feeds data.
+        """
+        self.__feeds = Preferences.getWebBrowser("RssFeeds")
+        self.__loaded = True
+        
+        # populate the feeds tree top level with the feeds
+        self.feedsTree.clear()
+        for feed in self.__feeds:
+            self.__addFeedItem(feed)
+    
+    def __save(self):
+        """
+        Private method to store the feeds data.
+        """
+        if not self.__loaded:
+            self.__load()
+        
+        Preferences.setWebBrowser("RssFeeds", self.__feeds)
+    
+    @pyqtSlot()
+    def on_reloadAllButton_clicked(self):
+        """
+        Private slot to reload all feeds.
+        """
+        if not self.__loaded:
+            self.__load()
+        
+        for index in range(self.feedsTree.topLevelItemCount()):
+            itm = self.feedsTree.topLevelItem(index)
+            self.__reloadFeed(itm)
+    
+    @pyqtSlot()
+    def on_reloadButton_clicked(self):
+        """
+        Private slot to reload the selected feed.
+        """
+        itm = self.feedsTree.selectedItems()[0]
+        self.__reloadFeed(itm)
+    
+    @pyqtSlot()
+    def on_editButton_clicked(self):
+        """
+        Private slot to edit the selected feed.
+        """
+        itm = self.feedsTree.selectedItems()[0]
+        origTitle = itm.text(0)
+        origUrlString = itm.data(0, FeedsManager.UrlStringRole)
+        
+        feedToChange = None
+        for feed in self.__feeds:
+            if feed[0] == origUrlString:
+                feedToChange = feed
+                break
+        if feedToChange:
+            feedIndex = self.__feeds.index(feedToChange)
+            
+            from .FeedEditDialog import FeedEditDialog
+            dlg = FeedEditDialog(origUrlString, origTitle)
+            if dlg.exec_() == QDialog.Accepted:
+                urlString, title = dlg.getData()
+                for feed in self.__feeds:
+                    if feed[0] == urlString:
+                        E5MessageBox.critical(
+                            self,
+                            self.tr("Duplicate Feed URL"),
+                            self.tr(
+                                """A feed with the URL {0} exists already."""
+                                """ Aborting...""".format(urlString)))
+                        return
+                
+                self.__feeds[feedIndex] = (urlString, title, feedToChange[2])
+                self.__save()
+                
+                itm.setText(0, title)
+                itm.setData(0, FeedsManager.UrlStringRole, urlString)
+                self.__reloadFeed(itm)
+    
+    @pyqtSlot()
+    def on_deleteButton_clicked(self):
+        """
+        Private slot to delete the selected feed.
+        """
+        itm = self.feedsTree.selectedItems()[0]
+        title = itm.text(0)
+        res = E5MessageBox.yesNo(
+            self,
+            self.tr("Delete Feed"),
+            self.tr(
+                """<p>Do you really want to delete the feed"""
+                """ <b>{0}</b>?</p>""".format(title)))
+        if res:
+            urlString = itm.data(0, FeedsManager.UrlStringRole)
+            if urlString:
+                feedToDelete = None
+                for feed in self.__feeds:
+                    if feed[0] == urlString:
+                        feedToDelete = feed
+                        break
+                if feedToDelete:
+                    self.__feeds.remove(feedToDelete)
+                    self.__save()
+                
+                index = self.feedsTree.indexOfTopLevelItem(itm)
+                if index != -1:
+                    self.feedsTree.takeTopLevelItem(index)
+                    del itm
+    
+    @pyqtSlot()
+    def on_feedsTree_itemSelectionChanged(self):
+        """
+        Private slot to enable the various buttons depending on the selection.
+        """
+        self.__enableButtons()
+    
+    def __enableButtons(self):
+        """
+        Private slot to disable/enable various buttons.
+        """
+        selItems = self.feedsTree.selectedItems()
+        if len(selItems) == 1 and \
+           self.feedsTree.indexOfTopLevelItem(selItems[0]) != -1:
+            enable = True
+        else:
+            enable = False
+        
+        self.reloadButton.setEnabled(enable)
+        self.editButton.setEnabled(enable)
+        self.deleteButton.setEnabled(enable)
+    
+    def __reloadFeed(self, itm):
+        """
+        Private method to reload the given feed.
+        
+        @param itm feed item to be reloaded (QTreeWidgetItem)
+        """
+        urlString = itm.data(0, FeedsManager.UrlStringRole)
+        if urlString == "":
+            return
+        
+        for child in itm.takeChildren():
+            del child
+        
+        from WebBrowser.WebBrowserWindow import WebBrowserWindow
+        request = QNetworkRequest(QUrl(urlString))
+        reply = WebBrowserWindow.networkManager().get(request)
+        reply.finished.connect(self.__feedLoaded)
+        self.__replies[id(reply)] = (reply, itm)
+    
+    def __feedLoaded(self):
+        """
+        Private slot to extract the loaded feed data.
+        """
+        reply = self.sender()
+        if id(reply) not in self.__replies:
+            return
+        
+        topItem = self.__replies[id(reply)][1]
+        del self.__replies[id(reply)]
+        
+        if reply.error() == QNetworkReply.NoError:
+            linkString = ""
+            titleString = ""
+            
+            xml = QXmlStreamReader()
+            xmlData = reply.readAll()
+            xml.addData(xmlData)
+            
+            while not xml.atEnd():
+                xml.readNext()
+                if xml.isStartElement():
+                    if xml.name() == "item":
+                        linkString = xml.attributes().value("rss:about")
+                    elif xml.name() == "link":
+                        linkString = xml.attributes().value("href")
+                    currentTag = xml.name()
+                elif xml.isEndElement():
+                    if xml.name() in ["item", "entry"]:
+                        itm = QTreeWidgetItem(topItem)
+                        itm.setText(0, titleString)
+                        itm.setData(0, FeedsManager.UrlStringRole, linkString)
+                        itm.setIcon(0, UI.PixmapCache.getIcon("rss16.png"))
+                        
+                        linkString = ""
+                        titleString = ""
+                elif xml.isCharacters() and not xml.isWhitespace():
+                    if currentTag == "title":
+                        titleString = xml.text()
+                    elif currentTag == "link":
+                        linkString += xml.text()
+            
+            if topItem.childCount() == 0:
+                itm = QTreeWidgetItem(topItem)
+                itm.setText(0, self.tr("Error fetching feed"))
+                itm.setData(0, FeedsManager.UrlStringRole, "")
+                itm.setData(0, FeedsManager.ErrorDataRole,
+                            str(xmlData, encoding="utf-8"))
+            
+            topItem.setExpanded(True)
+        else:
+            linkString = ""
+            titleString = reply.errorString()
+            itm = QTreeWidgetItem(topItem)
+            itm.setText(0, titleString)
+            itm.setData(0, FeedsManager.UrlStringRole, linkString)
+            topItem.setExpanded(True)
+    
+    def __customContextMenuRequested(self, pos):
+        """
+        Private slot to handle the context menu request for the feeds tree.
+        
+        @param pos position the context menu was requested (QPoint)
+        """
+        itm = self.feedsTree.currentItem()
+        if itm is None:
+            return
+        
+        if self.feedsTree.indexOfTopLevelItem(itm) != -1:
+            return
+        
+        urlString = itm.data(0, FeedsManager.UrlStringRole)
+        if urlString:
+            menu = QMenu()
+            menu.addAction(
+                self.tr("&Open"), self.__openMessageInCurrentTab)
+            menu.addAction(
+                self.tr("Open in New &Tab"), self.__openMessageInNewTab)
+            menu.addSeparator()
+            menu.addAction(self.tr("&Copy URL to Clipboard"),
+                           self.__copyUrlToClipboard)
+            menu.exec_(QCursor.pos())
+        else:
+            errorString = itm.data(0, FeedsManager.ErrorDataRole)
+            if errorString:
+                menu = QMenu()
+                menu.addAction(
+                    self.tr("&Show error data"), self.__showError)
+                menu.exec_(QCursor.pos())
+    
+    def __itemActivated(self, itm, column):
+        """
+        Private slot to handle the activation of an item.
+        
+        @param itm reference to the activated item (QTreeWidgetItem)
+        @param column column of the activation (integer)
+        """
+        if self.feedsTree.indexOfTopLevelItem(itm) != -1:
+            return
+        
+        self.__openMessage(
+            QApplication.keyboardModifiers() &
+            Qt.ControlModifier == Qt.ControlModifier)
+        
+    def __openMessageInCurrentTab(self):
+        """
+        Private slot to open a feed message in the current browser tab.
+        """
+        self.__openMessage(False)
+    
+    def __openMessageInNewTab(self):
+        """
+        Private slot to open a feed message in a new browser tab.
+        """
+        self.__openMessage(True)
+    
+    def __openMessage(self, newTab):
+        """
+        Private method to open a feed message.
+        
+        @param newTab flag indicating to open the feed message in a new tab
+            (boolean)
+        """
+        itm = self.feedsTree.currentItem()
+        if itm is None:
+            return
+        
+        urlString = itm.data(0, FeedsManager.UrlStringRole)
+        if urlString:
+            title = itm.text(0)
+            
+            if newTab:
+                self.newUrl.emit(QUrl(urlString), title)
+            else:
+                self.openUrl.emit(QUrl(urlString), title)
+        else:
+            errorString = itm.data(0, FeedsManager.ErrorDataRole)
+            if errorString:
+                self.__showError()
+    
+    def __copyUrlToClipboard(self):
+        """
+        Private slot to copy the URL of the selected item to the clipboard.
+        """
+        itm = self.feedsTree.currentItem()
+        if itm is None:
+            return
+        
+        if self.feedsTree.indexOfTopLevelItem(itm) != -1:
+            return
+        
+        urlString = itm.data(0, FeedsManager.UrlStringRole)
+        if urlString:
+            QApplication.clipboard().setText(urlString)
+    
+    def __showError(self):
+        """
+        Private slot to show error info for a failed load operation.
+        """
+        itm = self.feedsTree.currentItem()
+        if itm is None:
+            return
+        
+        errorStr = itm.data(0, FeedsManager.ErrorDataRole)
+        if errorStr:
+            E5MessageBox.critical(
+                self,
+                self.tr("Error loading feed"),
+                "{0}".format(errorStr))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Feeds/FeedsManager.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,165 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>FeedsManager</class>
+ <widget class="QDialog" name="FeedsManager">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>750</width>
+    <height>500</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Feeds Manager</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QTreeWidget" name="feedsTree">
+     <property name="alternatingRowColors">
+      <bool>true</bool>
+     </property>
+     <property name="allColumnsShowFocus">
+      <bool>true</bool>
+     </property>
+     <property name="wordWrap">
+      <bool>true</bool>
+     </property>
+     <attribute name="headerVisible">
+      <bool>false</bool>
+     </attribute>
+     <column>
+      <property name="text">
+       <string>News</string>
+      </property>
+     </column>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <widget class="QPushButton" name="reloadAllButton">
+       <property name="toolTip">
+        <string>Press to reload all feeds</string>
+       </property>
+       <property name="text">
+        <string>Reload &amp;All</string>
+       </property>
+       <property name="autoDefault">
+        <bool>false</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="reloadButton">
+       <property name="toolTip">
+        <string>Press to reload the selected feed</string>
+       </property>
+       <property name="text">
+        <string>&amp;Reload</string>
+       </property>
+       <property name="autoDefault">
+        <bool>false</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="editButton">
+       <property name="toolTip">
+        <string>Press to edit the selected feed</string>
+       </property>
+       <property name="text">
+        <string>&amp;Edit Feed</string>
+       </property>
+       <property name="autoDefault">
+        <bool>false</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="deleteButton">
+       <property name="toolTip">
+        <string>Press to delete the selected feed</string>
+       </property>
+       <property name="text">
+        <string>&amp;Delete Feed</string>
+       </property>
+       <property name="autoDefault">
+        <bool>false</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Close</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <tabstops>
+  <tabstop>feedsTree</tabstop>
+  <tabstop>reloadAllButton</tabstop>
+  <tabstop>reloadButton</tabstop>
+  <tabstop>editButton</tabstop>
+  <tabstop>deleteButton</tabstop>
+  <tabstop>buttonBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>FeedsManager</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>FeedsManager</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Feeds/__init__.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2011 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Package implementing all RSS feed related modules.
+"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/FlashCookieManager/FlashCookie.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,40 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2015 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the Flash cookie class.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import QDateTime
+
+
+class FlashCookie(object):
+    """
+    Class implementing the Flash cookie.
+    """
+    def __init__(self):
+        """
+        Constructor
+        """
+        self.name = ""
+        self.origin = ""
+        self.size = 0
+        self.path = ""
+        self.contents = ""
+        self.lastModified = QDateTime()
+    
+    def __eq__(self, other):
+        """
+        Special method to compare to another Flash cookie.
+        
+        @param other reference to the other Flash cookie
+        @type FlashCookie
+        @return flag indicating equality of the two cookies
+        @rtype bool
+        """
+        return (self.name == other.name and
+                self.path == other.path)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/FlashCookieManager/FlashCookieManager.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,365 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2015 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the Flash cookie manager.
+"""
+
+from __future__ import unicode_literals
+
+try:
+    str = unicode       # __IGNORE_EXCEPTION__
+except NameError:
+    pass
+
+import shutil
+
+from PyQt5.QtCore import QObject, QTimer, QDir, QFileInfo, QFile
+
+from .FlashCookie import FlashCookie
+from .FlashCookieReader import FlashCookieReader, FlashCookieReaderError
+
+import WebBrowser.WebBrowserWindow
+
+import Preferences
+
+
+class FlashCookieManager(QObject):
+    """
+    Class implementing the Flash cookie manager object.
+    """
+    RefreshInterval = 60 * 1000
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object
+        @type QObject
+        """
+        super(FlashCookieManager, self).__init__(parent)
+        
+        self.__flashCookieManagerDialog = None
+        self.__flashCookies = []    # list of FlashCookie
+        self.__newCookiesList = []  # list of str
+##        self.__whitelist = []       # list of str
+##        self.__blacklist = []       # list of str
+        
+        self.__timer = QTimer(self)
+        self.__timer.setInterval(FlashCookieManager.RefreshInterval)
+        self.__timer.timeout.connect(self.__autoRefresh)
+        
+        # start the timer if needed
+        self.__startStopTimer()
+        
+        if Preferences.getWebBrowser("FlashCookiesDeleteOnStartExit"):
+            self.__loadFlashCookies()
+            self.__removeAllButWhitelisted()
+    
+    def shutdown(self):
+        """
+        Public method to perform shutdown actions.
+        """
+        if self.__flashCookieManagerDialog is not None:
+            self.__flashCookieManagerDialog.close()
+        
+        if Preferences.getWebBrowser("FlashCookiesDeleteOnStartExit"):
+            self.__removeAllButWhitelisted()
+    
+    def setFlashCookies(self, cookies):
+        """
+        Public method to set the list of cached Flash cookies.
+        
+        @param cookies list of Flash cookies to store
+        @type list of FlashCookie
+        """
+        self.__flashCookies = cookies[:]
+    
+    def flashCookies(self):
+        """
+        Public method to get the list of cached Flash cookies.
+        
+        @return list of Flash cookies
+        @rtype list of FlashCookie
+        """
+        if not self.__flashCookies:
+            self.__loadFlashCookies()
+        
+        return self.__flashCookies[:]
+    
+    def newCookiesList(self):
+        """
+        Public method to get the list of newly detected Flash cookies.
+        
+        @return list of newly detected Flash cookies
+        @rtype list of str
+        """
+        return self.__newCookiesList[:]
+    
+    def clearNewOrigins(self):
+        """
+        Public method to clear the list of newly detected Flash cookies.
+        """
+        self.__newCookiesList = []
+    
+    def clearCache(self):
+        """
+        Public method to clear the list of cached Flash cookies.
+        """
+        self.__flashCookies = []
+    
+    def __isBlacklisted(self, cookie):
+        """
+        Private method to check for a blacklisted cookie.
+        
+        @param cookie Flash cookie to be tested
+        @type FlashCookie
+        @return flag indicating a blacklisted cookie
+        @rtype bool
+        """
+        return cookie.origin in \
+            Preferences.getWebBrowser("FlashCookiesBlacklist")
+    
+    def __isWhitelisted(self, cookie):
+        """
+        Private method to check for a whitelisted cookie.
+        
+        @param cookie Flash cookie to be tested
+        @type FlashCookie
+        @return flag indicating a whitelisted cookie
+        @rtype bool
+        """
+        return cookie.origin in \
+            Preferences.getWebBrowser("FlashCookiesWhitelist")
+    
+    def __removeAllButWhitelisted(self):
+        """
+        Private method to remove all non-whitelisted cookies.
+        """
+        for cookie in self.__flashCookies[:]:
+            if not self.__isWhitelisted(cookie):
+                self.removeCookie(cookie)
+    
+    def removeAllCookies(self):
+        """
+        Public method to remove all flash cookies.
+        """
+        for cookie in self.__flashCookies[:]:
+            self.removeCookie(cookie)
+        self.clearNewOrigins()
+        self.clearCache()
+    
+    def __sharedObjectDirName(self):
+        """
+        Private slot to determine the path of the shared data objects.
+        
+        @return path of the shared data objects
+        @rtype str
+        """
+        if "macromedia" in self.flashPlayerDataPath().lower() or \
+                "/.gnash" not in self.flashPlayerDataPath().lower():
+            return "/#SharedObjects/"
+        else:
+            return "/SharedObjects/"
+    
+    def flashPlayerDataPath(self):
+        """
+        Public method to get the Flash Player data path.
+        
+        @return Flash Player data path
+        @rtype str
+        """
+        return Preferences.getWebBrowser("FlashCookiesDataPath")
+    
+    def preferencesChanged(self):
+        """
+        Public slot to handle a change of preferences.
+        """
+        self.__startStopTimer()
+    
+    def removeCookie(self, cookie):
+        """
+        Public method to remove a cookie of the list of cached cookies.
+        
+        @param cookie Flash cookie to be removed
+        @type FlashCookie
+        """
+        if cookie in self.__flashCookies:
+            self.__flashCookies.remove(cookie)
+            shutil.rmtree(cookie.path, True)
+    
+    def __autoRefresh(self):
+        """
+        Private slot to refresh the list of cookies.
+        """
+        if self.__flashCookieManagerDialog and \
+                self.__flashCookieManagerDialog.isVisible():
+            return
+        
+        oldFlashCookies = self.__flashCookies[:]
+        self.__loadFlashCookies()
+        newCookieList = []
+        
+        for cookie in self.__flashCookies[:]:
+            if self.__isBlacklisted(cookie):
+                self.removeCookie(cookie)
+                continue
+            
+            if self.__isWhitelisted(cookie):
+                continue
+            
+            newCookie = True
+            for oldCookie in oldFlashCookies:
+                if (oldCookie.path + oldCookie.name ==
+                        cookie.path + cookie.name):
+                    newCookie = False
+                    break
+            
+            if newCookie:
+                newCookieList.append(cookie.path + "/" + cookie.name)
+        
+        if newCookieList and Preferences.getWebBrowser("FlashCookieNotify"):
+            self.__newCookiesList.extend(newCookieList)
+            win = WebBrowser.WebBrowserWindow.WebBrowserWindow.mainWindow()
+            if win is None:
+                return
+            
+            view = win.currentBrowser()
+            if view is None:
+                return
+            
+            from .FlashCookieNotification import FlashCookieNotification
+            notification = FlashCookieNotification(
+                view, self, len(newCookieList))
+            notification.show()
+    
+    def showFlashCookieManagerDialog(self):
+        """
+        Public method to show the Flash cookies management dialog.
+        """
+        if self.__flashCookieManagerDialog is None:
+            from .FlashCookieManagerDialog import FlashCookieManagerDialog
+            self.__flashCookieManagerDialog = FlashCookieManagerDialog(self)
+        
+        self.__flashCookieManagerDialog.refreshView()
+        self.__flashCookieManagerDialog.showPage(0)
+        self.__flashCookieManagerDialog.show()
+        self.__flashCookieManagerDialog.raise_()
+    
+    def __startStopTimer(self):
+        """
+        Private slot to start or stop the auto refresh timer.
+        """
+        if Preferences.getWebBrowser("FlashCookieAutoRefresh"):
+            if not self.__timer.isActive():
+                if not bool(self.__flashCookies):
+                    self.__loadFlashCookies()
+                
+                self.__timer.start()
+        else:
+            self.__timer.stop()
+    
+    def __loadFlashCookies(self):
+        """
+        Private slot to load the Flash cookies to be cached.
+        """
+        self.__flashCookies = []
+        self.__loadFlashCookiesFromPath(self.flashPlayerDataPath())
+    
+    def __loadFlashCookiesFromPath(self, path):
+        """
+        Private slot to load the Flash cookies from a path.
+        
+        @param path Flash cookies path
+        @type str
+        """
+        if path.endswith("#AppContainer"):
+            # specific to IE and Windows
+            return
+        
+        path = path.replace("\\", "/")
+        solDir = QDir(path)
+        entryList = solDir.entryList()
+        for entry in entryList:
+            if entry == "." or entry == "..":
+                continue
+            entryInfo = QFileInfo(path + "/" + entry)
+            if entryInfo.isDir():
+                self.__loadFlashCookiesFromPath(entryInfo.filePath())
+            else:
+                self.__insertFlashCookie(entryInfo.filePath())
+    
+    def __insertFlashCookie(self, path):
+        """
+        Private method to insert a Flash cookie into the cache.
+        
+        @param path Flash cookies path
+        @type str
+        """
+        solFile = QFile(path)
+        if not solFile.open(QFile.ReadOnly):
+            return
+        
+        dataStr = ""
+        data = bytes(solFile.readAll())
+        if data:
+            try:
+                reader = FlashCookieReader()
+                reader.setBytes(data)
+                reader.parse()
+                dataStr = reader.toString()
+            except FlashCookieReaderError as err:
+                dataStr = err.msg
+        
+        solFileInfo = QFileInfo(solFile)
+        
+        cookie = FlashCookie()
+        cookie.contents = dataStr
+        cookie.name = solFileInfo.fileName()
+        cookie.path = solFileInfo.canonicalPath()
+        cookie.size = int(solFile.size())
+        cookie.lastModified = solFileInfo.lastModified()
+        cookie.origin = self.__extractOriginFrom(path)
+        
+        self.__flashCookies.append(cookie)
+    
+    def __extractOriginFrom(self, path):
+        """
+        Private method to extract the cookie origin given its file name.
+        
+        @param path file name of the cookie file
+        @type str
+        @return cookie origin
+        @rtype str
+        """
+        origin = path
+        if path.startswith(
+                self.flashPlayerDataPath() + self.__sharedObjectDirName()):
+            origin = origin.replace(
+                self.flashPlayerDataPath() + self.__sharedObjectDirName(), "")
+            if "/" in origin:
+                origin = origin.split("/", 1)[1]
+        elif path.startswith(
+            self.flashPlayerDataPath() +
+                "/macromedia.com/support/flashplayer/sys/"):
+            origin = origin.replace(
+                self.flashPlayerDataPath() +
+                "/macromedia.com/support/flashplayer/sys/", "")
+            if origin == "settings.sol":
+                return self.tr("!default")
+            elif origin.startswith("#"):
+                origin = origin[1:]
+        else:
+            origin = ""
+        
+        index = origin.find("/")
+        if index == -1:
+            return self.tr("!other")
+        
+        origin = origin[:index]
+        if origin in ["localhost", "local"]:
+            origin = "!localhost"
+        
+        return origin
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/FlashCookieManager/FlashCookieManagerDialog.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,425 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2015 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog to manage the flash cookies.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSlot, Qt, QPoint, QTimer
+from PyQt5.QtWidgets import QDialog, QTreeWidgetItem, QApplication, QMenu, \
+    QInputDialog, QLineEdit
+
+from E5Gui import E5MessageBox
+
+from .Ui_FlashCookieManagerDialog import Ui_FlashCookieManagerDialog
+
+import Preferences
+import UI.PixmapCache
+
+
+class FlashCookieManagerDialog(QDialog, Ui_FlashCookieManagerDialog):
+    """
+    Class implementing a dialog to manage the flash cookies.
+    """
+    def __init__(self, manager, parent=None):
+        """
+        Constructor
+        
+        @param manager reference to the Flash cookie manager object
+        @type FlashCookieManager
+        @param parent reference to the parent widget
+        @type QWidget
+        """
+        super(FlashCookieManagerDialog, self).__init__(parent)
+        self.setupUi(self)
+        self.setWindowFlags(Qt.Window)
+        
+        self.cookiesList.setContextMenuPolicy(Qt.CustomContextMenu)
+        self.cookiesList.customContextMenuRequested.connect(
+            self.__cookiesListContextMenuRequested)
+        
+        self.__manager = manager
+    
+    @pyqtSlot()
+    def on_whiteList_itemSelectionChanged(self):
+        """
+        Private slot handling the selection of items in the whitelist.
+        """
+        enable = len(self.whiteList.selectedItems()) > 0
+        self.removeWhiteButton.setEnabled(enable)
+    
+    @pyqtSlot()
+    def on_blackList_itemSelectionChanged(self):
+        """
+        Private slot handling the selection of items in the blacklist.
+        """
+        enable = len(self.blackList.selectedItems()) > 0
+        self.removeBlackButton.setEnabled(enable)
+    
+    @pyqtSlot()
+    def on_removeWhiteButton_clicked(self):
+        """
+        Private slot to remove a server from the whitelist.
+        """
+        for itm in self.whiteList.selectedItems():
+            row = self.whiteList.row(itm)
+            self.whiteList.takeItem(row)
+            del itm
+    
+    @pyqtSlot()
+    def on_addWhiteButton_clicked(self):
+        """
+        Private slot to add a server to the whitelist.
+        """
+        origin, ok = QInputDialog.getText(
+            self,
+            self.tr("Add to whitelist"),
+            self.tr("Origin:"),
+            QLineEdit.Normal)
+        if ok and bool(origin):
+            self.__addWhitelist(origin)
+    
+    def __addWhitelist(self, origin):
+        """
+        Private method to add a cookie origin to the whitelist.
+        
+        @param origin origin to be added to the list
+        @type str
+        """
+        if not origin:
+            return
+        
+        if len(self.blackList.findItems(origin, Qt.MatchFixedString)) > 0:
+            E5MessageBox.information(
+                self,
+                self.tr("Add to whitelist"),
+                self.tr("""The server '{0}' is already in the blacklist."""
+                        """ Please remove it first.""").format(origin))
+            return
+        
+        if len(self.whiteList.findItems(origin, Qt.MatchFixedString)) == 0:
+            self.whiteList.addItem(origin)
+    
+    @pyqtSlot()
+    def on_removeBlackButton_clicked(self):
+        """
+        Private slot to remove a server from the blacklist.
+        """
+        for itm in self.blackList.selectedItems():
+            row = self.blackList.row(itm)
+            self.blackList.takeItem(row)
+            del itm
+    
+    @pyqtSlot()
+    def on_addBlackButton_clicked(self):
+        """
+        Private slot to add a server to the blacklist.
+        """
+        origin, ok = QInputDialog.getText(
+            self,
+            self.tr("Add to blacklist"),
+            self.tr("Origin:"),
+            QLineEdit.Normal)
+        if ok and bool(origin):
+            self.__addBlacklist(origin)
+    
+    def __addBlacklist(self, origin):
+        """
+        Private method to add a cookie origin to the blacklist.
+        
+        @param origin origin to be added to the list
+        @type str
+        """
+        if not origin:
+            return
+        
+        if len(self.whiteList.findItems(origin, Qt.MatchFixedString)) > 0:
+            E5MessageBox.information(
+                self,
+                self.tr("Add to blacklist"),
+                self.tr("""The server '{0}' is already in the whitelist."""
+                        """ Please remove it first.""").format(origin))
+            return
+        
+        if len(self.blackList.findItems(origin, Qt.MatchFixedString)) == 0:
+            self.blackList.addItem(origin)
+    
+    @pyqtSlot(str)
+    def on_filterEdit_textChanged(self, filter):
+        """
+        Private slot to filter the cookies list.
+        
+        @param filter filter text
+        @type str
+        """
+        if not filter:
+            # show all in collapsed state
+            for index in range(self.cookiesList.topLevelItemCount()):
+                self.cookiesList.topLevelItem(index).setHidden(False)
+                self.cookiesList.topLevelItem(index).setExpanded(False)
+        else:
+            # show matching in expanded state
+            filter = filter.lower()
+            for index in range(self.cookiesList.topLevelItemCount()):
+                txt = "." + self.cookiesList.topLevelItem(index)\
+                    .text(0).lower()
+                self.cookiesList.topLevelItem(index).setHidden(
+                    filter not in txt)
+                self.cookiesList.topLevelItem(index).setExpanded(True)
+    
+    @pyqtSlot(QTreeWidgetItem, QTreeWidgetItem)
+    def on_cookiesList_currentItemChanged(self, current, previous):
+        """
+        Private slot handling a change of the current cookie item.
+        
+        @param current reference to the current item
+        @type QTreeWidgetItem
+        @param previous reference to the previous item
+        @type QTreeWidgetItem
+        """
+        if current is None:
+            self.removeButton.setEnabled(False)
+            return
+        
+        cookie = current.data(0, Qt.UserRole)
+        if cookie is None:
+            self.nameLabel.setText(self.tr("<no flash cookie selected>"))
+            self.sizeLabel.setText(self.tr("<no flash cookie selected>"))
+            self.originLabel.setText(self.tr("<no flash cookie selected>"))
+            self.modifiedLabel.setText(self.tr("<no flash cookie selected>"))
+            self.contentsEdit.clear()
+            self.pathEdit.clear()
+            self.removeButton.setText(self.tr("Remove Cookie Group"))
+        else:
+            suffix = ""
+            if cookie.path.startswith(
+                self.__manager.flashPlayerDataPath() +
+                    "/macromedia.com/support/flashplayer/sys"):
+                suffix = self.tr(" (settings)")
+            self.nameLabel.setText(
+                self.tr("{0}{1}", "name and suffix")
+                .format(cookie.name, suffix))
+            self.sizeLabel.setText(self.tr("{0} Byte").format(cookie.size))
+            self.originLabel.setText(cookie.origin)
+            self.modifiedLabel.setText(
+                cookie.lastModified.toString("yyyy-MM-dd hh:mm:ss"))
+            self.contentsEdit.setPlainText(cookie.contents)
+            self.pathEdit.setText(cookie.path)
+            self.removeButton.setText(self.tr("Remove Cookie"))
+        self.removeButton.setEnabled(True)
+    
+    @pyqtSlot(QPoint)
+    def __cookiesListContextMenuRequested(self, pos):
+        """
+        Private slot handling the cookies list context menu.
+        
+        @param pos position to show the menu at
+        @type QPoint
+        """
+        itm = self.cookiesList.itemAt(pos)
+        if itm is None:
+            return
+        
+        menu = QMenu()
+        addBlacklistAct = menu.addAction(self.tr("Add to blacklist"))
+        addWhitelistAct = menu.addAction(self.tr("Add to whitelist"))
+        
+        self.cookiesList.setCurrentItem(itm)
+        
+        activatedAction = menu.exec_(
+            self.cookiesList.viewport().mapToGlobal(pos))
+        if itm.childCount() == 0:
+            origin = itm.data(0, Qt.UserRole).origin
+        else:
+            origin = itm.text(0)
+        
+        if activatedAction == addBlacklistAct:
+            self.__addBlacklist(origin)
+        elif activatedAction == addWhitelistAct:
+            self.__addWhitelist(origin)
+    
+    @pyqtSlot()
+    def on_reloadButton_clicked(self):
+        """
+        Private slot handling a press of the reload button.
+        """
+        self.refreshView(True)
+    
+    @pyqtSlot()
+    def on_removeAllButton_clicked(self):
+        """
+        Private slot to remove all cookies.
+        """
+        ok = E5MessageBox.yesNo(
+            self,
+            self.tr("Remove All"),
+            self.tr("""Do you really want to delete all flash cookies on"""
+                    """ your computer?"""))
+        if ok:
+            cookies = self.__manager.flashCookies()
+            for cookie in cookies:
+                self.__manager.removeCookie(cookie)
+            
+            self.cookiesList.clear()
+            self.__manager.clearNewOrigins()
+            self.__manager.clearCache()
+    
+    @pyqtSlot()
+    def on_removeButton_clicked(self):
+        """
+        Private slot to remove one cookie or a cookie group.
+        """
+        itm = self.cookiesList.currentItem()
+        if itm is None:
+            return
+        
+        cookie = itm.data(0, Qt.UserRole)
+        if cookie is None:
+            # remove a whole cookie group
+            origin = itm.text(0)
+            cookieList = self.__manager.flashCookies()
+            for fcookie in cookieList:
+                if fcookie.origin == origin:
+                    self.__manager.removeCookie(fcookie)
+            
+            index = self.cookiesList.indexOfTopLevelItem(itm)
+            self.cookiesList.takeTopLevelItem(index)
+        else:
+            self.__manager.removeCookie(cookie)
+            parent = itm.parent()
+            index = parent.indexOfChild(itm)
+            parent.takeChild(index)
+            
+            if parent.childCount() == 0:
+                # remove origin item as well
+                index = self.cookiesList.indexOfTopLevelItem(parent)
+                self.cookiesList.takeTopLevelItem(index)
+                del parent
+        del itm
+    
+    def refreshView(self, forceReload=False):
+        """
+        Public method to refresh the dialog view.
+        
+        @param forceReload flag indicating to reload the cookies
+        @type bool
+        """
+        blocked = self.filterEdit.blockSignals(True)
+        self.filterEdit.clear()
+        self.contentsEdit.clear()
+        self.filterEdit.blockSignals(blocked)
+        
+        if forceReload:
+            self.__manager.clearCache()
+            self.__manager.clearNewOrigins()
+        
+        QTimer.singleShot(0, self.__refreshCookiesList)
+        QTimer.singleShot(0, self.__refreshFilterLists)
+    
+    def showPage(self, index):
+        """
+        Public method to display a given page.
+        
+        @param index index of the page to be shown
+        @type int
+        """
+        self.cookiesTabWidget.setCurrentIndex(index)
+    
+    @pyqtSlot()
+    def __refreshCookiesList(self):
+        """
+        Private slot to refresh the cookies list.
+        """
+        QApplication.setOverrideCursor(Qt.WaitCursor)
+        
+        cookies = self.__manager.flashCookies()
+        self.cookiesList.clear()
+        
+        counter = 0
+        originDict = {}
+        for cookie in cookies:
+            cookieOrigin = cookie.origin
+            if cookieOrigin.startswith("."):
+                cookieOrigin = cookieOrigin[1:]
+            
+            if cookieOrigin in originDict:
+                itm = QTreeWidgetItem(originDict[cookieOrigin])
+            else:
+                newParent = QTreeWidgetItem(self.cookiesList)
+                newParent.setText(0, cookieOrigin)
+                newParent.setIcon(0, UI.PixmapCache.getIcon("dirOpen.png"))
+                self.cookiesList.addTopLevelItem(newParent)
+                originDict[cookieOrigin] = newParent
+                
+                itm = QTreeWidgetItem(newParent)
+            
+            suffix = ""
+            if cookie.path.startswith(
+                self.__manager.flashPlayerDataPath() +
+                    "/macromedia.com/support/flashplayer/sys"):
+                suffix = self.tr(" (settings)")
+            
+            if cookie.path + "/" + cookie.name in \
+                    self.__manager.newCookiesList():
+                suffix += self.tr(" [new]")
+                font = itm.font(0)
+                font.setBold(True)
+                itm.setFont(font)
+                itm.parent().setExpanded(True)
+            
+            itm.setText(0, self.tr("{0}{1}", "name and suffix").format(
+                cookie.name, suffix))
+            itm.setData(0, Qt.UserRole, cookie)
+            
+            counter += 1
+            if counter > 100:
+                QApplication.processEvents()
+                counter = 0
+        
+        self.removeAllButton.setEnabled(
+            self.cookiesList.topLevelItemCount() > 0)
+        self.removeButton.setEnabled(False)
+        
+        QApplication.restoreOverrideCursor()
+    
+    @pyqtSlot()
+    def __refreshFilterLists(self):
+        """
+        Private slot to refresh the white and black lists.
+        """
+        self.whiteList.clear()
+        self.blackList.clear()
+        
+        self.whiteList.addItems(
+            Preferences.getWebBrowser("FlashCookiesWhitelist"))
+        self.blackList.addItems(
+            Preferences.getWebBrowser("FlashCookiesBlacklist"))
+        
+        self.on_whiteList_itemSelectionChanged()
+        self.on_blackList_itemSelectionChanged()
+    
+    def closeEvent(self, evt):
+        """
+        Protected method to handle the close event.
+        
+        @param evt reference to the close event
+        @type QCloseEvent
+        """
+        self.__manager.clearNewOrigins()
+        
+        whiteList = []
+        for row in range(self.whiteList.count()):
+            whiteList.append(self.whiteList.item(row).text())
+        
+        blackList = []
+        for row in range(self.blackList.count()):
+            blackList.append(self.blackList.item(row).text())
+        
+        Preferences.setWebBrowser("FlashCookiesWhitelist", whiteList)
+        Preferences.setWebBrowser("FlashCookiesBlacklist", blackList)
+        
+        evt.accept()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/FlashCookieManager/FlashCookieManagerDialog.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,478 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>FlashCookieManagerDialog</class>
+ <widget class="QDialog" name="FlashCookieManagerDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>550</width>
+    <height>500</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Flash Cookie Management</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QTabWidget" name="cookiesTabWidget">
+     <property name="currentIndex">
+      <number>0</number>
+     </property>
+     <widget class="QWidget" name="tab">
+      <attribute name="title">
+       <string>Stored Flash Cookies</string>
+      </attribute>
+      <layout class="QGridLayout" name="gridLayout_4">
+       <item row="0" column="0">
+        <layout class="QGridLayout" name="gridLayout">
+         <item row="0" column="0">
+          <widget class="QLabel" name="label_4">
+           <property name="text">
+            <string>Filter:</string>
+           </property>
+          </widget>
+         </item>
+         <item row="0" column="1">
+          <widget class="QLineEdit" name="filterEdit">
+           <property name="toolTip">
+            <string>Enter cookie filter string</string>
+           </property>
+          </widget>
+         </item>
+         <item row="1" column="0" colspan="2">
+          <widget class="QLabel" name="label_24">
+           <property name="text">
+            <string>Stored Flash Cookies:</string>
+           </property>
+           <property name="wordWrap">
+            <bool>true</bool>
+           </property>
+          </widget>
+         </item>
+         <item row="2" column="0" colspan="2">
+          <widget class="QTreeWidget" name="cookiesList">
+           <property name="alternatingRowColors">
+            <bool>true</bool>
+           </property>
+           <column>
+            <property name="text">
+             <string>Origin</string>
+            </property>
+           </column>
+          </widget>
+         </item>
+        </layout>
+       </item>
+       <item row="0" column="1">
+        <layout class="QGridLayout" name="gridLayout_3">
+         <item row="0" column="0">
+          <spacer name="horizontalSpacer">
+           <property name="orientation">
+            <enum>Qt::Horizontal</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>158</width>
+             <height>20</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
+         <item row="0" column="1">
+          <widget class="QPushButton" name="reloadButton">
+           <property name="toolTip">
+            <string>Press to reload Flash cookies from disk</string>
+           </property>
+           <property name="text">
+            <string>Reload</string>
+           </property>
+          </widget>
+         </item>
+         <item row="1" column="0" colspan="2">
+          <layout class="QGridLayout" name="gridLayout_2">
+           <item row="0" column="0">
+            <widget class="QLabel" name="label_6">
+             <property name="sizePolicy">
+              <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
+               <horstretch>0</horstretch>
+               <verstretch>0</verstretch>
+              </sizepolicy>
+             </property>
+             <property name="text">
+              <string>Name:</string>
+             </property>
+             <property name="alignment">
+              <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+             </property>
+            </widget>
+           </item>
+           <item row="0" column="1">
+            <widget class="QLabel" name="nameLabel">
+             <property name="text">
+              <string>&lt;no flash cookie selected&gt;</string>
+             </property>
+            </widget>
+           </item>
+           <item row="1" column="0">
+            <widget class="QLabel" name="labelSize_2">
+             <property name="text">
+              <string>Size:</string>
+             </property>
+            </widget>
+           </item>
+           <item row="1" column="1">
+            <widget class="QLabel" name="sizeLabel">
+             <property name="text">
+              <string>&lt;no flash cookie selected&gt;</string>
+             </property>
+            </widget>
+           </item>
+           <item row="2" column="0">
+            <widget class="QLabel" name="label_7">
+             <property name="text">
+              <string>Origin:</string>
+             </property>
+            </widget>
+           </item>
+           <item row="2" column="1">
+            <widget class="QLabel" name="originLabel">
+             <property name="text">
+              <string>&lt;no flash cookie selected&gt;</string>
+             </property>
+            </widget>
+           </item>
+           <item row="3" column="0">
+            <widget class="QLabel" name="label_22">
+             <property name="text">
+              <string>Last Modified:</string>
+             </property>
+            </widget>
+           </item>
+           <item row="3" column="1">
+            <widget class="QLabel" name="modifiedLabel">
+             <property name="text">
+              <string>&lt;no flash cookie selected&gt;</string>
+             </property>
+            </widget>
+           </item>
+          </layout>
+         </item>
+         <item row="2" column="0" colspan="2">
+          <widget class="QLabel" name="label">
+           <property name="text">
+            <string>Contents:</string>
+           </property>
+          </widget>
+         </item>
+         <item row="3" column="0" colspan="2">
+          <widget class="QPlainTextEdit" name="contentsEdit">
+           <property name="tabChangesFocus">
+            <bool>true</bool>
+           </property>
+           <property name="lineWrapMode">
+            <enum>QPlainTextEdit::NoWrap</enum>
+           </property>
+           <property name="readOnly">
+            <bool>true</bool>
+           </property>
+           <property name="textInteractionFlags">
+            <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </item>
+       <item row="1" column="0" colspan="2">
+        <layout class="QHBoxLayout" name="horizontalLayout_2">
+         <item>
+          <widget class="QLabel" name="label_2">
+           <property name="text">
+            <string>Path:</string>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QLineEdit" name="pathEdit">
+           <property name="readOnly">
+            <bool>true</bool>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </item>
+       <item row="2" column="0" colspan="2">
+        <layout class="QHBoxLayout" name="horizontalLayout">
+         <item>
+          <widget class="QPushButton" name="removeAllButton">
+           <property name="toolTip">
+            <string>Press to remove all flash cookies</string>
+           </property>
+           <property name="text">
+            <string>Remove All Cookies</string>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QPushButton" name="removeButton">
+           <property name="toolTip">
+            <string>Press to remove selected flash cookies</string>
+           </property>
+           <property name="text">
+            <string>Remove Cookie</string>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <spacer name="horizontalSpacer_2">
+           <property name="orientation">
+            <enum>Qt::Horizontal</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>40</width>
+             <height>20</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
+        </layout>
+       </item>
+      </layout>
+     </widget>
+     <widget class="QWidget" name="tab_2">
+      <attribute name="title">
+       <string>Flash Cookies Lists</string>
+      </attribute>
+      <layout class="QGridLayout" name="gridLayout_5">
+       <item row="0" column="0">
+        <widget class="QLabel" name="label_3">
+         <property name="text">
+          <string>&lt;b&gt;Flash cookie whitelist&lt;/b&gt;</string>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="1">
+        <widget class="QLabel" name="label_5">
+         <property name="text">
+          <string>&lt;b&gt;Flash cookie blacklist&lt;/b&gt;</string>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="0">
+        <widget class="QLabel" name="label_17">
+         <property name="text">
+          <string>Flash cookies from these origins will not be deleted automatically. (Also detection of them will not be notified to user.)</string>
+         </property>
+         <property name="wordWrap">
+          <bool>true</bool>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="1">
+        <widget class="QLabel" name="label_18">
+         <property name="text">
+          <string>Flash cookies from these origins will be deleted without any notification.</string>
+         </property>
+         <property name="wordWrap">
+          <bool>true</bool>
+         </property>
+        </widget>
+       </item>
+       <item row="2" column="0">
+        <widget class="QListWidget" name="whiteList">
+         <property name="alternatingRowColors">
+          <bool>true</bool>
+         </property>
+         <property name="selectionMode">
+          <enum>QAbstractItemView::ExtendedSelection</enum>
+         </property>
+         <property name="sortingEnabled">
+          <bool>true</bool>
+         </property>
+        </widget>
+       </item>
+       <item row="2" column="1">
+        <widget class="QListWidget" name="blackList">
+         <property name="alternatingRowColors">
+          <bool>true</bool>
+         </property>
+         <property name="selectionMode">
+          <enum>QAbstractItemView::ExtendedSelection</enum>
+         </property>
+         <property name="sortingEnabled">
+          <bool>true</bool>
+         </property>
+        </widget>
+       </item>
+       <item row="3" column="0">
+        <layout class="QHBoxLayout" name="horizontalLayout_3">
+         <item>
+          <spacer name="horizontalSpacer_3">
+           <property name="orientation">
+            <enum>Qt::Horizontal</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>40</width>
+             <height>20</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
+         <item>
+          <widget class="QPushButton" name="removeWhiteButton">
+           <property name="toolTip">
+            <string>Press to remove selected origins from the whitelist</string>
+           </property>
+           <property name="text">
+            <string>Remove</string>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QPushButton" name="addWhiteButton">
+           <property name="toolTip">
+            <string>Press to add an origin to the whitelist</string>
+           </property>
+           <property name="text">
+            <string>Add...</string>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <spacer name="horizontalSpacer_4">
+           <property name="orientation">
+            <enum>Qt::Horizontal</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>40</width>
+             <height>20</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
+        </layout>
+       </item>
+       <item row="3" column="1">
+        <layout class="QHBoxLayout" name="horizontalLayout_4">
+         <item>
+          <spacer name="horizontalSpacer_5">
+           <property name="orientation">
+            <enum>Qt::Horizontal</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>40</width>
+             <height>20</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
+         <item>
+          <widget class="QPushButton" name="removeBlackButton">
+           <property name="toolTip">
+            <string>Press to remove selected origins from the blacklist</string>
+           </property>
+           <property name="text">
+            <string>Remove</string>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QPushButton" name="addBlackButton">
+           <property name="toolTip">
+            <string>Press to add an origin to the blacklist</string>
+           </property>
+           <property name="text">
+            <string>Add...</string>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <spacer name="horizontalSpacer_6">
+           <property name="orientation">
+            <enum>Qt::Horizontal</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>40</width>
+             <height>20</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
+        </layout>
+       </item>
+      </layout>
+     </widget>
+    </widget>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Close</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <tabstops>
+  <tabstop>cookiesTabWidget</tabstop>
+  <tabstop>filterEdit</tabstop>
+  <tabstop>cookiesList</tabstop>
+  <tabstop>reloadButton</tabstop>
+  <tabstop>contentsEdit</tabstop>
+  <tabstop>pathEdit</tabstop>
+  <tabstop>removeAllButton</tabstop>
+  <tabstop>removeButton</tabstop>
+  <tabstop>whiteList</tabstop>
+  <tabstop>removeWhiteButton</tabstop>
+  <tabstop>addWhiteButton</tabstop>
+  <tabstop>blackList</tabstop>
+  <tabstop>removeBlackButton</tabstop>
+  <tabstop>addBlackButton</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>FlashCookieManagerDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>FlashCookieManagerDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/FlashCookieManager/FlashCookieNotification.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,66 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2015 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the feature permission bar widget.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtWidgets import QLabel, QHBoxLayout, QPushButton
+
+from E5Gui.E5AnimatedWidget import E5AnimatedWidget
+
+import UI.PixmapCache
+
+
+class FlashCookieNotification(E5AnimatedWidget):
+    """
+    Class implementing the feature permission bar widget.
+    """
+    DefaultHeight = 30
+    
+    def __init__(self, view, manager, noCookies):
+        """
+        Constructor
+        
+        @param view reference to the web view
+        @type WebBrowserView
+        @param manager reference to the Flash cookie manager object
+        @type FlashCookieManager
+        @param noCookies number of newly detected Flash cookies
+        @type int
+        """
+        super(FlashCookieNotification, self).__init__(parent=view)
+        
+        self.__manager = manager
+        
+        if noCookies == 1:
+            msg = self.tr("A new flash cookie was detected.")
+        else:
+            msg = self.tr("{0} new flash cookies were detected.")\
+                .format(noCookies)
+        self.setAutoFillBackground(True)
+        self.__layout = QHBoxLayout()
+        self.setLayout(self.__layout)
+        self.__layout.setContentsMargins(9, 0, 0, 0)
+        self.__iconLabel = QLabel(self)
+        self.__iconLabel.setPixmap(UI.PixmapCache.getPixmap("flashCookie.png"))
+        self.__layout.addWidget(self.__iconLabel)
+        self.__messageLabel = QLabel(msg, self)
+        self.__layout.addWidget(self.__messageLabel)
+        self.__viewButton = QPushButton(self.tr("View"), self)
+        self.__layout.addWidget(self.__viewButton)
+        self.__layout.addStretch()
+        self.__discardButton = QPushButton(UI.PixmapCache.getIcon("close.png"),
+                                           "", self)
+        self.__layout.addWidget(self.__discardButton)
+        
+        self.__viewButton.clicked.connect(manager.showFlashCookieManagerDialog)
+        self.__viewButton.clicked.connect(self.hide)
+        self.__discardButton.clicked.connect(self.hide)
+        
+        self.resize(view.width(), self.height())
+        self.startAnimation()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/FlashCookieManager/FlashCookieReader.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,474 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2015 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a class to read flash cookies.
+"""
+
+#
+# Note: The code is based on s2x.py
+#
+
+from __future__ import unicode_literals
+
+import struct
+import io
+
+from PyQt5.QtCore import QDateTime
+
+
+class FlashCookieReaderError(Exception):
+    """
+    Class containing data of a reader error.
+    """
+    def __init__(self, msg):
+        """
+        Constructor
+        
+        @param msg error message
+        @type str
+        """
+        self.msg = msg
+
+
+class FlashCookieReader(object):
+    """
+    Class implementing a reader for flash cookies (*.sol files).
+    """
+    Number = b'\x00'
+    Boolean = b'\x01'
+    String = b'\x02'
+    ObjObj = b'\x03'
+    Null = b'\x05'
+    Undef = b'\x06'
+    ObjArr = b'\x08'
+    ObjDate = b'\x0B'
+    ObjM = b'\x0D'
+    ObjXml = b'\x0F'
+    ObjCc = b'\x10'
+    
+    EpochCorrectionMsecs = 31 * 24 * 60 * 60 * 1000
+    # Flash Epoch starts at 1969-12-01
+    
+    def __init__(self):
+        """
+        Constructor
+        """
+        self.__result = {}
+        # dictionary with element name as key and tuple of
+        # type and value as value
+        self.__data = None
+        self.__parsed = False
+    
+    def setBytes(self, solData):
+        """
+        Public method to set the contents of a sol file to be parsed.
+        
+        @param solData contents of the file
+        @type bytes
+        """
+        self.__data = io.BytesIO(solData)
+    
+    def setFileName(self, solFilename):
+        """
+        Public method to set the name of a sol file to be parsed.
+        
+        @param solFilename name of the sol file
+        @type str
+        """
+        self.__data = open(solFilename, "rb")
+    
+    def setFile(self, solFile):
+        """
+        Public method to set an open sol file to be parsed.
+        
+        @param solFile sol file to be parsed
+        @type io.FileIO
+        """
+        self.__data = solFile
+    
+    def parse(self):
+        """
+        Public method to parse the sol file.
+        
+        @exception FlashCookieReaderError raised when encountering a parse
+            issue
+        """
+        if self.__data is None:
+            return
+        
+        self.__data.seek(0, 2)
+        lenSolData = self.__data.tell()
+        self.__data.seek(0)
+        self.__data.read(2)
+        sLenData = self.__data.read(4)
+        lenData, = struct.unpack(">L", sLenData)    # unsigned long, big-endian
+        if lenSolData != lenData + 6:
+            raise FlashCookieReaderError(
+                "Flash cookie data lengths don't match\n"
+                "  file length: {0}\n"
+                "  data length: {1}"
+                .format(lenSolData - 6, lenData))
+        sDataType = self.__data.read(4).decode("utf-8")             # 'TCSO'
+        if sDataType != "TCSO":
+            raise FlashCookieReaderError(
+                "Flash cookie type is not 'TCSO'; found '{0}'."
+                .format(sDataType))
+        self.__data.read(6)
+        lenSolName, = struct.unpack(">H", self.__data.read(2))
+        # unsigned short,  big-endian
+        solName = self.__data.read(lenSolName)
+        solName = solName.decode("utf-8", "replace")
+        self.__result["SolName"] = ("string", solName)
+        self.__data.read(4)
+        while self.__data.tell() < lenSolData:
+            lenVariableName, = struct.unpack(">H", self.__data.read(2))
+            # unsigned short,  big-endian
+            variableName = self.__data.read(lenVariableName)
+            variableName = variableName.decode("utf-8", "replace")
+            variableType = self.__data.read(1)
+            if len(variableType):
+                if variableType == self.Number:
+                    self.__parseNumber(variableName, self.__result)
+                elif variableType == self.Boolean:
+                    self.__parseBoolean(variableName, self.__result)
+                elif variableType == self.String:
+                    self.__parseString(variableName, self.__result)
+                elif variableType == self.ObjObj:
+                    self.__parseObject(variableName, self.__result)
+                elif variableType == self.ObjArr:
+                    self.__parseArray(variableName, self.__result)
+                elif variableType == self.ObjDate:
+                    self.__parseDate(variableName, self.__result)
+                elif variableType == self.ObjXml:
+                    self.__parseXml(variableName, self.__result)
+                elif variableType == self.ObjCc:
+                    self.__parseOcc(variableName, self.__result)
+                elif variableType == self.ObjM:
+                    self.__parseOjm(variableName, self.__result)
+                elif variableType == self.Null:
+                    self.__parseNull(variableName, self.__result)
+                elif variableType == self.Undef:
+                    self.__parseUndefined(variableName, self.__result)
+                else:
+                    raise FlashCookieReaderError(
+                        "Unexpected Data Type: " + hex(ord(variableType)))
+            self.__data.read(1)       # '\x00'
+        self.__data.close()
+        self.__parsed = True
+        
+    def __parseNumber(self, variableName, parent):
+        """
+        Private method to parse a number.
+        
+        @param variableName name of the variable to be parsed
+        @type str
+        @param parent reference to the dictionary to insert the result into
+        @type dict
+        """
+        b = self.__data.read(8)
+        if b == b"\x7F\xF0\x00\x00\x00\x00\x00\x00":
+            value = "Infinity"
+        elif b == b"\xFF\xF0\x00\x00\x00\x00\x00\x00":
+            value = "-Infinity"
+        elif b == b"\x7F\xF8\x00\x00\x00\x00\x00\x00":
+            value = "NaN"
+        else:
+            value, = struct.unpack(">d", b)    # double, big-endian
+            value = str(value)
+        parent[variableName] = ("number", value)
+    
+    def __parseBoolean(self, variableName, parent):
+        """
+        Private method to parse a boolean.
+        
+        @param variableName name of the variable to be parsed
+        @type str
+        @param parent reference to the dictionary to insert the result into
+        @type dict
+        """
+        b = self.__data.read(1)
+        if b == b"\x00":
+            value = "False"
+        elif b == b"\x01":
+            value = "True"
+        else:
+            # boolean value error; default to True
+            value = "True"
+        parent[variableName] = ("boolean", value)
+    
+    def __parseString(self, variableName, parent):
+        """
+        Private method to parse a string.
+        
+        @param variableName name of the variable to be parsed
+        @type str
+        @param parent reference to the dictionary to insert the result into
+        @type dict
+        """
+        lenStr, = struct.unpack(">H", self.__data.read(2))
+        # unsigned short, big-endian
+        b = self.__data.read(lenStr)
+        value = b.decode("utf-8", "replace")
+        parent[variableName] = ("string", value)
+    
+    def __parseDate(self, variableName, parent):
+        """
+        Private method to parse a date.
+        
+        @param variableName name of the variable to be parsed
+        @type str
+        @param parent reference to the dictionary to insert the result into
+        @type dict
+        """
+        msec, = struct.unpack(">d", self.__data.read(8))
+        # double, big-endian
+        # DateObject: Milliseconds Count From Dec. 1, 1969
+        msec -= self.EpochCorrectionMsecs   # correct for Unix epoch
+        minOffset, = struct.unpack(">h", self.__data.read(2))
+        # short, big-endian
+        offset = minOffset // 60    # offset in hours
+        # Timezone: UTC + Offset
+        value = QDateTime()
+        value.setMSecsSinceEpoch(msec)
+        value.setOffsetFromUtc(offset * 3600)
+        parent[variableName] = ("date",
+                                value.toString("yyyy-MM-dd HH:mm:ss t"))
+    
+    def __parseXml(self, variableName, parent):
+        """
+        Private method to parse XML.
+        
+        @param variableName name of the variable to be parsed
+        @type str
+        @param parent reference to the dictionary to insert the result into
+        @type dict
+        """
+        lenCData, = struct.unpack(">L", self.__data.read(4))
+        # unsigned long, big-endian
+        cData = self.__data.read(lenCData)
+        value = cData.decode("utf-8", "replace")
+        parent[variableName] = ("xml", value)
+    
+    def __parseOjm(self, variableName, parent):
+        """
+        Private method to parse an m_object.
+        
+        @param variableName name of the variable to be parsed
+        @type str
+        @param parent reference to the dictionary to insert the result into
+        @type dict
+        """
+        parent[variableName] = ("m_object", "")
+    
+    def __parseNull(self, variableName, parent):
+        """
+        Private method to parse a null object.
+        
+        @param variableName name of the variable to be parsed
+        @type str
+        @param parent reference to the dictionary to insert the result into
+        @type dict
+        """
+        parent[variableName] = ("null", "")
+    
+    def __parseUndefined(self, variableName, parent):
+        """
+        Private method to parse an undefined object.
+        
+        @param variableName name of the variable to be parsed
+        @type str
+        @param parent reference to the dictionary to insert the result into
+        @type dict
+        """
+        parent[variableName] = ("undefined", "")
+    
+    def __parseObject(self, variableName, parent):
+        """
+        Private method to parse an object.
+        
+        @param variableName name of the variable to be parsed
+        @type str
+        @param parent reference to the dictionary to insert the result into
+        @type dict
+        @exception FlashCookieReaderError raised when an issue with the cookie
+            file is found
+        """
+        value = {}
+        parent[variableName] = ("object", value)
+        
+        lenVariableName, = struct.unpack(">H", self.__data.read(2))
+        # unsigned short,  big-endian
+        while lenVariableName != 0:
+            variableName = self.__data.read(lenVariableName)
+            variableName = variableName.decode("utf-8", "replace")
+            variableType = self.__data.read(1)
+            if variableType == self.Number:
+                self.__parseNumber(variableName, value)
+            elif variableType == self.Boolean:
+                self.__parseBoolean(variableName, value)
+            elif variableType == self.String:
+                self.__parseString(variableName, value)
+            elif variableType == self.ObjObj:
+                self.__parseObject(variableName, value)
+            elif variableType == self.ObjArr:
+                self.__parseArray(variableName, value)
+            elif variableType == self.ObjDate:
+                self.__parseDate(variableName, value)
+            elif variableType == self.ObjXml:
+                self.__parseXml(variableName, value)
+            elif variableType == self.ObjCc:
+                self.__parseOcc(variableName, value)
+            elif variableType == self.ObjM:
+                self.__parseOjm(variableName, value)
+            elif variableType == self.Null:
+                self.__parseNull(variableName, value)
+            elif variableType == self.Undef:
+                self.__parseUndefined(variableName, value)
+            else:
+                raise FlashCookieReaderError(
+                    "Unexpected Data Type: " + hex(ord(variableType)))
+            lenVariableName, = struct.unpack(">H", self.__data.read(2))
+        self.__data.read(1)       # '\x09'
+    
+    def __parseArray(self, variableName, parent):
+        """
+        Private method to parse an array.
+        
+        @param variableName name of the variable to be parsed
+        @type str
+        @param parent reference to the dictionary to insert the result into
+        @type dict
+        @exception FlashCookieReaderError raised when an issue with the cookie
+            file is found
+        """
+        arrayLength, = struct.unpack(">L", self.__data.read(4))
+        # unsigned long, big-endian
+        
+        value = {}
+        parent[variableName] = ("array; length={0}".format(arrayLength), value)
+        
+        lenVariableName, = struct.unpack(">H", self.__data.read(2))
+        # unsigned short,  big-endian
+        while lenVariableName != 0:
+            variableName = self.__data.read(lenVariableName)
+            variableName = variableName.decode("utf-8", "replace")
+            variableType = self.__data.read(1)
+            if variableType == self.Number:
+                self.__parseNumber(variableName, value)
+            elif variableType == self.Boolean:
+                self.__parseBoolean(variableName, value)
+            elif variableType == self.String:
+                self.__parseString(variableName, value)
+            elif variableType == self.ObjObj:
+                self.__parseObject(variableName, value)
+            elif variableType == self.ObjArr:
+                self.__parseArray(variableName, value)
+            elif variableType == self.ObjDate:
+                self.__parseDate(variableName, value)
+            elif variableType == self.ObjXml:
+                self.__parseXml(variableName, value)
+            elif variableType == self.ObjCc:
+                self.__parseOcc(variableName, value)
+            elif variableType == self.ObjM:
+                self.__parseOjm(variableName, value)
+            elif variableType == self.Null:
+                self.__parseNull(variableName, value)
+            elif variableType == self.Undef:
+                self.__parseUndefined(variableName, value)
+            else:
+                raise FlashCookieReaderError(
+                    "Unexpected Data Type: " + hex(ord(variableType)))
+            lenVariableName, = struct.unpack(">H", self.__data.read(2))
+        self.__data.read(1)       # '\x09'
+    
+    def __parseOcc(self, variableName, parent):
+        """
+        Private method to parse a c_object.
+        
+        @param variableName name of the variable to be parsed
+        @type str
+        @param parent reference to the dictionary to insert the result into
+        @type dict
+        @exception FlashCookieReaderError raised when an issue with the cookie
+            file is found
+        """
+        lenCname = struct.unpack(">H", self.__data.read(2))
+        # unsigned short,  big-endian
+        cname = self.__data.read(lenCname)
+        cname = cname.decode("utf-8", "replace")
+        
+        value = {}
+        parent[variableName] = ("c_object; cname={0}".format(cname), value)
+        
+        lenVariableName, = struct.unpack(">H", self.__data.read(2))
+        # unsigned short,  big-endian
+        while lenVariableName != 0:
+            variableName = self.__data.read(lenVariableName)
+            variableName = variableName.decode("utf-8", "replace")
+            variableType = self.__data.read(1)
+            if variableType == self.Number:
+                self.__parseNumber(variableName, value)
+            elif variableType == self.Boolean:
+                self.__parseBoolean(variableName, value)
+            elif variableType == self.String:
+                self.__parseString(variableName, value)
+            elif variableType == self.ObjObj:
+                self.__parseObject(variableName, value)
+            elif variableType == self.ObjArr:
+                self.__parseArray(variableName, value)
+            elif variableType == self.ObjDate:
+                self.__parseDate(variableName, value)
+            elif variableType == self.ObjXml:
+                self.__parseXml(variableName, value)
+            elif variableType == self.ObjCc:
+                self.__parseOcc(variableName, value)
+            elif variableType == self.ObjM:
+                self.__parseOjm(variableName, value)
+            elif variableType == self.Null:
+                self.__parseNull(variableName, value)
+            elif variableType == self.Undef:
+                self.__parseUndefined(variableName, value)
+            else:
+                raise FlashCookieReaderError(
+                    "Unexpected Data Type: " + hex(ord(variableType)))
+            lenVariableName, = struct.unpack(">H", self.__data.read(2))
+        self.__data.read(1)       # '\x09'
+    
+    def toString(self, indent=0, parent=None):
+        """
+        Public method to convert the parsed cookie to a string representation.
+        
+        @param indent indentation level
+        @type int
+        @param parent reference to the dictionary to be converted
+        @type dict
+        @return string representation of the cookie
+        @rtype str
+        """
+        indentStr = "  " * indent
+        strArr = []
+        
+        if parent is None:
+            parent = self.__result
+        
+        if not parent:
+            return ""
+        
+        for variableName in sorted(parent.keys()):
+            variableType, value = parent[variableName]
+            if isinstance(value, dict):
+                resultStr = self.toString(indent + 1, value)
+                if resultStr:
+                    strArr.append("{0}{1}:\n{2}"
+                                  .format(indentStr, variableName, resultStr))
+                else:
+                    strArr.append("{0}{1}:"
+                                  .format(indentStr, variableName))
+            else:
+                strArr.append("{0}{1}: {2}"
+                              .format(indentStr, variableName, value))
+        
+        return "\n".join(strArr)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/FlashCookieManager/FlashCookieUtilities.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,56 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2015 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing some utility functions.
+"""
+
+from __future__ import unicode_literals
+
+import os
+
+from PyQt5.QtCore import QProcessEnvironment
+
+import Globals
+
+
+def flashDataPathForOS():
+    """
+    Function to determine the OS dependent path where Flash cookies
+    are stored.
+    
+    @return Flash data path
+    @rtype str
+    """
+    # On Microsoft Windows NT 5.x and 6.x, they are stored in:
+    # %APPDATA%\Macromedia\Flash Player\#SharedObjects\
+    # %APPDATA%\Macromedia\Flash Player\macromedia.com\support\flashplayer\sys\
+    # On Mac OS X, they are stored in:
+    # ~/Library/Preferences/Macromedia/Flash Player/#SharedObjects/
+    # ~/Library/Preferences/Macromedia/Flash Player/macromedia.com/support/⏎
+    #   flashplayer/sys/
+    # On Linux or Unix, they are stored in:
+    # ~/.macromedia/Flash_Player/#SharedObjects/
+    # ~/.macromedia/Flash_Player/macromedia.com/support/flashplayer/sys/
+    # For Linux and Unix systems, if the open-source Gnash plugin is being used
+    #  instead of the official Adobe Flash, they will instead be found at:
+    # ~/.gnash/SharedObjects/
+    
+    flashPath = ""
+    
+    if Globals.isWindowsPlatform():
+        appData = QProcessEnvironment.systemEnvironment().value("APPDATA")
+        appData = appData.replace("\\", "/")
+        flashPath = appData + "/Macromedia/Flash Player"
+    elif Globals.isMacPlatform():
+        flashPath = os.path.expanduser(
+            "~/Library/Preferences/Macromedia/Flash Player")
+    else:
+        if os.path.exists(os.path.expanduser("~/.macromedia")):
+            flashPath = os.path.expanduser("~/.macromedia/Flash_Player")
+        elif os.path.exists(os.path.expanduser("~/.gnash")):
+            flashPath = os.path.expanduser("~/.gnash")
+    
+    return flashPath
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/FlashCookieManager/__init__.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2015 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Package implementing the Flash cookie manager and associated objects.
+"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/GreaseMonkey/GreaseMonkeyAddScriptDialog.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,106 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+
+"""
+Module implementing a dialog for adding GreaseMonkey scripts..
+"""
+
+from __future__ import unicode_literals
+
+import os
+
+from PyQt5.QtCore import pyqtSlot, QDir, QFile
+from PyQt5.QtWidgets import QDialog
+
+from E5Gui import E5MessageBox
+
+from .Ui_GreaseMonkeyAddScriptDialog import Ui_GreaseMonkeyAddScriptDialog
+
+import UI.PixmapCache
+
+
+class GreaseMonkeyAddScriptDialog(QDialog, Ui_GreaseMonkeyAddScriptDialog):
+    """
+    Class implementing a dialog for adding GreaseMonkey scripts..
+    """
+    def __init__(self, manager, script, parent=None):
+        """
+        Constructor
+        
+        @param manager reference to the GreaseMonkey manager
+            (GreaseMonkeyManager)
+        @param script GreaseMonkey script to be added (GreaseMonkeyScript)
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(GreaseMonkeyAddScriptDialog, self).__init__(parent)
+        self.setupUi(self)
+        
+        self.iconLabel.setPixmap(
+            UI.PixmapCache.getPixmap("greaseMonkey48.png"))
+        
+        self.__manager = manager
+        self.__script = script
+        
+        runsAt = ""
+        doesNotRunAt = ""
+        
+        include = script.include()
+        exclude = script.exclude()
+        
+        if include:
+            runsAt = self.tr("<p>runs at:<br/><i>{0}</i></p>").format(
+                "<br/>".join(include))
+        
+        if exclude:
+            doesNotRunAt = self.tr(
+                "<p>does not run at:<br/><i>{0}</i></p>").format(
+                "<br/>".join(exclude))
+        
+        scriptInfoTxt = "<p><b>{0}</b> {1}<br/>{2}</p>{3}{4}".format(
+            script.name(), script.version(), script.description(), runsAt,
+            doesNotRunAt)
+        self.scriptInfo.setHtml(scriptInfoTxt)
+        
+        self.accepted.connect(self.__accepted)
+    
+    @pyqtSlot()
+    def on_showScriptSourceButton_clicked(self):
+        """
+        Private slot to show an editor window with the source code.
+        """
+        from WebBrowser.Tools import WebBrowserTools
+        
+        tmpFileName = WebBrowserTools.ensureUniqueFilename(
+            os.path.join(QDir.tempPath(), "tmp-userscript.js"))
+        if QFile.copy(self.__script.fileName(), tmpFileName):
+            from QScintilla.MiniEditor import MiniEditor
+            editor = MiniEditor(tmpFileName, "JavaScript", self)
+            editor.show()
+    
+    def __accepted(self):
+        """
+        Private slot handling the accepted signal.
+        """
+        if self.__manager.addScript(self.__script):
+            msg = self.tr(
+                "<p><b>{0}</b> installed successfully.</p>").format(
+                self.__script.name())
+            success = True
+        else:
+            msg = self.tr("<p>Cannot install script.</p>")
+            success = False
+        
+        from WebBrowser.WebBrowserWindow import WebBrowserWindow
+        if success and WebBrowserWindow.notificationsEnabled():
+            WebBrowserWindow.showNotification(
+                UI.PixmapCache.getPixmap("greaseMonkey48.png"),
+                self.tr("GreaseMonkey Script Installation"),
+                msg)
+        else:
+            E5MessageBox.information(
+                self,
+                self.tr("GreaseMonkey Script Installation"),
+                msg)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/GreaseMonkey/GreaseMonkeyAddScriptDialog.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>GreaseMonkeyAddScriptDialog</class>
+ <widget class="QDialog" name="GreaseMonkeyAddScriptDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>550</width>
+    <height>400</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>GreaseMonkey Script Installation</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QLabel" name="iconLabel">
+       <property name="minimumSize">
+        <size>
+         <width>48</width>
+         <height>48</height>
+        </size>
+       </property>
+       <property name="text">
+        <string notr="true">Icon</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLabel" name="label_2">
+       <property name="text">
+        <string>&lt;h2&gt;GreaseMonkey Script Installation&lt;/h2&gt;</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="horizontalSpacer_2">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QLabel" name="label_3">
+     <property name="text">
+      <string>You are about to install this userscript into GreaseMonkey:</string>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignCenter</set>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QTextBrowser" name="scriptInfo"/>
+   </item>
+   <item>
+    <widget class="QLabel" name="label_5">
+     <property name="text">
+      <string>&lt;b&gt;You should only install scripts from sources you trust!&lt;/b&gt;</string>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignCenter</set>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QLabel" name="label_4">
+     <property name="text">
+      <string>Are you sure you want to install it?</string>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignCenter</set>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_2">
+     <item>
+      <widget class="QPushButton" name="showScriptSourceButton">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="toolTip">
+        <string>Press to open an editor with the script's source</string>
+       </property>
+       <property name="text">
+        <string>Show source code of script</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QDialogButtonBox" name="buttonBox">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="standardButtons">
+        <set>QDialogButtonBox::No|QDialogButtonBox::Yes</set>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>GreaseMonkeyAddScriptDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>401</x>
+     <y>389</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>GreaseMonkeyAddScriptDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>439</x>
+     <y>389</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/GreaseMonkey/GreaseMonkeyConfiguration/GreaseMonkeyConfigurationDialog.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,175 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the GreaseMonkey scripts configuration dialog.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSlot, Qt, QUrl
+from PyQt5.QtGui import QDesktopServices
+from PyQt5.QtWidgets import QDialog, QListWidgetItem
+
+from E5Gui import E5MessageBox
+
+from .Ui_GreaseMonkeyConfigurationDialog import \
+    Ui_GreaseMonkeyConfigurationDialog
+
+import UI.PixmapCache
+
+
+class GreaseMonkeyConfigurationDialog(
+        QDialog, Ui_GreaseMonkeyConfigurationDialog):
+    """
+    Class implementing the GreaseMonkey scripts configuration dialog.
+    """
+    ScriptVersionRole = Qt.UserRole
+    ScriptDescriptionRole = Qt.UserRole + 1
+    ScriptRole = Qt.UserRole + 2
+    
+    def __init__(self, manager, parent=None):
+        """
+        Constructor
+        
+        @param manager reference to the manager object (GreaseMonkeyManager)
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(GreaseMonkeyConfigurationDialog, self).__init__(parent)
+        self.setupUi(self)
+        self.setWindowFlags(Qt.Window)
+        
+        self.iconLabel.setPixmap(
+            UI.PixmapCache.getPixmap("greaseMonkey48.png"))
+        
+        self.__manager = manager
+        
+        self.__loadScripts()
+        
+        self.scriptsList.removeItemRequested.connect(self.__removeItem)
+        self.scriptsList.itemChanged.connect(self.__itemChanged)
+    
+    @pyqtSlot()
+    def on_openDirectoryButton_clicked(self):
+        """
+        Private slot to open the GreaseMonkey scripts directory.
+        """
+        QDesktopServices.openUrl(
+            QUrl.fromLocalFile(self.__manager.scriptsDirectory()))
+    
+    @pyqtSlot(str)
+    def on_downloadLabel_linkActivated(self, link):
+        """
+        Private slot to open the greasyfork.org web site.
+        
+        @param link URL (string)
+        """
+        from WebBrowser.WebBrowserWindow import WebBrowserWindow
+        if not link or "userscript.org" in link:
+            # userscript.org is down, default to Greasy Fork.
+            link = "https://greasyfork.org/"
+        WebBrowserWindow.mainWindow().newTab(QUrl(link))
+        self.close()
+    
+    @pyqtSlot(QListWidgetItem)
+    def on_scriptsList_itemDoubleClicked(self, item):
+        """
+        Private slot to show information about the selected script.
+        
+        @param item reference to the double clicked item (QListWidgetItem)
+        """
+        script = self.__getScript(item)
+        if script is not None:
+            from .GreaseMonkeyConfigurationScriptInfoDialog import \
+                GreaseMonkeyConfigurationScriptInfoDialog
+            infoDlg = GreaseMonkeyConfigurationScriptInfoDialog(script, self)
+            infoDlg.exec_()
+    
+    def __loadScripts(self):
+        """
+        Private method to load all the available scripts.
+        """
+        for script in self.__manager.allScripts():
+            itm = QListWidgetItem(
+                UI.PixmapCache.getIcon("greaseMonkeyScript.png"),
+                script.name(), self.scriptsList)
+            itm.setData(
+                GreaseMonkeyConfigurationDialog.ScriptVersionRole,
+                script.version())
+            itm.setData(
+                GreaseMonkeyConfigurationDialog.ScriptDescriptionRole,
+                script.description())
+            itm.setFlags(itm.flags() | Qt.ItemIsUserCheckable)
+            if script.isEnabled():
+                itm.setCheckState(Qt.Checked)
+            else:
+                itm.setCheckState(Qt.Unchecked)
+            itm.setData(GreaseMonkeyConfigurationDialog.ScriptRole, script)
+            self.scriptsList.addItem(itm)
+        
+        self.scriptsList.sortItems()
+        
+        itemMoved = True
+        while itemMoved:
+            itemMoved = False
+            for row in range(self.scriptsList.count()):
+                topItem = self.scriptsList.item(row)
+                bottomItem = self.scriptsList.item(row + 1)
+                if topItem is None or bottomItem is None:
+                    continue
+                
+                if topItem.checkState() == Qt.Unchecked and \
+                   bottomItem.checkState == Qt.Checked:
+                    itm = self.scriptsList.takeItem(row + 1)
+                    self.scriptsList.insertItem(row, itm)
+                    itemMoved = True
+    
+    def __getScript(self, itm):
+        """
+        Private method to get the script for the given item.
+        
+        @param itm item to get script for (QListWidgetItem)
+        @return reference to the script object (GreaseMonkeyScript)
+        """
+        if itm is None:
+            return None
+        
+        script = itm.data(GreaseMonkeyConfigurationDialog.ScriptRole)
+        return script
+    
+    def __removeItem(self, itm):
+        """
+        Private slot to remove a script item.
+        
+        @param itm item to be removed (QListWidgetItem)
+        """
+        script = self.__getScript(itm)
+        if script is None:
+            return
+        
+        removeIt = E5MessageBox.yesNo(
+            self,
+            self.tr("Remove Script"),
+            self.tr(
+                """<p>Are you sure you want to remove <b>{0}</b>?</p>""")
+            .format(script.name()))
+        if removeIt and self.__manager.removeScript(script):
+            self.scriptsList.takeItem(self.scriptsList.row(itm))
+            del itm
+    
+    def __itemChanged(self, itm):
+        """
+        Private slot to handle changes of a script item.
+        
+        @param itm changed item (QListWidgetItem)
+        """
+        script = self.__getScript(itm)
+        if script is None:
+            return
+        
+        if itm.checkState() == Qt.Checked:
+            self.__manager.enableScript(script)
+        else:
+            self.__manager.disableScript(script)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/GreaseMonkey/GreaseMonkeyConfiguration/GreaseMonkeyConfigurationDialog.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,191 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>GreaseMonkeyConfigurationDialog</class>
+ <widget class="QDialog" name="GreaseMonkeyConfigurationDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>550</width>
+    <height>450</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>GreaseMonkey Scripts Configuration</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QLabel" name="iconLabel">
+       <property name="minimumSize">
+        <size>
+         <width>48</width>
+         <height>48</height>
+        </size>
+       </property>
+       <property name="text">
+        <string notr="true">Icon</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLabel" name="label_2">
+       <property name="text">
+        <string>&lt;h2&gt;GreaseMonkey Scripts&lt;/h2&gt;</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="horizontalSpacer_2">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QLabel" name="label_4">
+     <property name="text">
+      <string>Double clicking script will show additional information.</string>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignCenter</set>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="GreaseMonkeyConfigurationListWidget" name="scriptsList">
+     <property name="alternatingRowColors">
+      <bool>true</bool>
+     </property>
+     <property name="verticalScrollMode">
+      <enum>QAbstractItemView::ScrollPerPixel</enum>
+     </property>
+     <property name="uniformItemSizes">
+      <bool>true</bool>
+     </property>
+     <property name="selectionRectVisible">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QLabel" name="downloadLabel">
+     <property name="text">
+      <string>&lt;p&gt;Get more scripts from &lt;a href=&quot;https://greasyfork.org/&quot;&gt;greasyfork.org&lt;/a&gt; or via &lt;a href=&quot;http://wiki.greasespot.net/User_Script_Hosting&quot;&gt;Greasespot Wiki.&lt;/a&gt;&lt;/p&gt;</string>
+     </property>
+     <property name="textFormat">
+      <enum>Qt::RichText</enum>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignCenter</set>
+     </property>
+     <property name="textInteractionFlags">
+      <set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse</set>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_4">
+     <item>
+      <widget class="QPushButton" name="openDirectoryButton">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="toolTip">
+        <string>Press to open the scripts directory</string>
+       </property>
+       <property name="text">
+        <string>Open Scripts Directory</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QDialogButtonBox" name="buttonBox">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="standardButtons">
+        <set>QDialogButtonBox::Ok</set>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>GreaseMonkeyConfigurationListWidget</class>
+   <extends>QListWidget</extends>
+   <header>.GreaseMonkeyConfigurationListWidget.h</header>
+  </customwidget>
+ </customwidgets>
+ <tabstops>
+  <tabstop>scriptsList</tabstop>
+  <tabstop>openDirectoryButton</tabstop>
+  <tabstop>buttonBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>GreaseMonkeyConfigurationDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>GreaseMonkeyConfigurationDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/GreaseMonkey/GreaseMonkeyConfiguration/GreaseMonkeyConfigurationListDelegate.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,194 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a delegate for the special list widget for GreaseMonkey
+scripts.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import Qt, QSize, QRect, qVersion
+from PyQt5.QtGui import QFontMetrics, QPalette, QFont
+from PyQt5.QtWidgets import QStyle, QStyledItemDelegate, QApplication
+if qVersion() >= "5.0.0":
+    from PyQt5.QtWidgets import QStyleOptionViewItem
+else:
+    from PyQt5.QtWidgets import QStyleOptionViewItemV4 as QStyleOptionViewItem
+
+import UI.PixmapCache
+import Globals
+
+
+class GreaseMonkeyConfigurationListDelegate(QStyledItemDelegate):
+    """
+    Class implementing a delegate for the special list widget for GreaseMonkey
+    scripts.
+    """
+    IconSize = 32
+    RemoveIconSize = 16
+    CheckBoxSize = 18
+    MinPadding = 5
+    ItemWidth = 200
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object (QObject)
+        """
+        super(GreaseMonkeyConfigurationListDelegate, self).__init__(parent)
+        
+        self.__removePixmap = \
+            UI.PixmapCache.getIcon("greaseMonkeyTrash.png").pixmap(
+                GreaseMonkeyConfigurationListDelegate.RemoveIconSize)
+        self.__rowHeight = 0
+        self.__padding = 0
+    
+    def padding(self):
+        """
+        Public method to get the padding used.
+        
+        @return padding used (integer)
+        """
+        return self.__padding
+    
+    def paint(self, painter, option, index):
+        """
+        Public method to paint the specified list item.
+        
+        @param painter painter object to paint to (QPainter)
+        @param option style option used for painting (QStyleOptionViewItem)
+        @param index model index of the item (QModelIndex)
+        """
+        opt = QStyleOptionViewItem(option)
+        self.initStyleOption(opt, index)
+        
+        widget = opt.widget
+        style = widget.style() if widget is not None else QApplication.style()
+        height = opt.rect.height()
+        center = height // 2 + opt.rect.top()
+        
+        # Prepare title font
+        titleFont = QFont(opt.font)
+        titleFont.setBold(True)
+        titleFont.setPointSize(titleFont.pointSize() + 1)
+        
+        titleMetrics = QFontMetrics(titleFont)
+        if Globals.isWindowsPlatform():
+            colorRole = QPalette.Text
+        else:
+            colorRole = QPalette.HighlightedText \
+                if opt.state & QStyle.State_Selected else QPalette.Text
+        
+        leftPos = self.__padding
+        rightPos = opt.rect.right() - self.__padding - \
+            GreaseMonkeyConfigurationListDelegate.RemoveIconSize
+        
+        # Draw background
+        style.drawPrimitive(QStyle.PE_PanelItemViewItem, opt, painter, widget)
+        
+        # Draw checkbox
+        checkBoxYPos = center - \
+            GreaseMonkeyConfigurationListDelegate.CheckBoxSize // 2
+        opt2 = QStyleOptionViewItem(opt)
+        if opt2.checkState == Qt.Checked:
+            opt2.state |= QStyle.State_On
+        else:
+            opt2.state |= QStyle.State_Off
+        styleCheckBoxRect = style.subElementRect(
+            QStyle.SE_ViewItemCheckIndicator, opt2, widget)
+        opt2.rect = QRect(
+            leftPos, checkBoxYPos,
+            styleCheckBoxRect.width(), styleCheckBoxRect.height())
+        style.drawPrimitive(QStyle.PE_IndicatorViewItemCheck, opt2, painter,
+                            widget)
+        leftPos = opt2.rect.right() + self.__padding
+        
+        # Draw icon
+        iconYPos = center - GreaseMonkeyConfigurationListDelegate.IconSize // 2
+        iconRect = QRect(leftPos, iconYPos,
+                         GreaseMonkeyConfigurationListDelegate.IconSize,
+                         GreaseMonkeyConfigurationListDelegate.IconSize)
+        pixmap = index.data(Qt.DecorationRole).pixmap(
+            GreaseMonkeyConfigurationListDelegate.IconSize)
+        painter.drawPixmap(iconRect, pixmap)
+        leftPos = iconRect.right() + self.__padding
+        
+        # Draw script name
+        name = index.data(Qt.DisplayRole)
+        leftTitleEdge = leftPos + 2
+        rightTitleEdge = rightPos - self.__padding
+        leftPosForVersion = titleMetrics.width(name) + self.__padding
+        nameRect = QRect(leftTitleEdge, opt.rect.top() + self.__padding,
+                         rightTitleEdge - leftTitleEdge, titleMetrics.height())
+        painter.setFont(titleFont)
+        style.drawItemText(painter, nameRect, Qt.AlignLeft, opt.palette, True,
+                           name, colorRole)
+        
+        # Draw version
+        version = index.data(Qt.UserRole)
+        versionRect = QRect(
+            nameRect.x() + leftPosForVersion, nameRect.y(),
+            rightTitleEdge - leftTitleEdge, titleMetrics.height())
+        versionFont = titleFont
+        painter.setFont(versionFont)
+        style.drawItemText(painter, versionRect, Qt.AlignLeft, opt.palette,
+                           True, version, colorRole)
+        
+        # Draw description
+        infoYPos = nameRect.bottom() + opt.fontMetrics.leading()
+        infoRect = QRect(
+            nameRect.x(), infoYPos,
+            nameRect.width(), opt.fontMetrics.height())
+        info = opt.fontMetrics.elidedText(
+            index.data(Qt.UserRole + 1), Qt.ElideRight, infoRect.width())
+        painter.setFont(opt.font)
+        style.drawItemText(painter, infoRect, Qt.AlignLeft | Qt.TextSingleLine,
+                           opt.palette, True, info, colorRole)
+        
+        # Draw remove button
+        removeIconYPos = center - \
+            GreaseMonkeyConfigurationListDelegate.RemoveIconSize // 2
+        removeIconRect = QRect(
+            rightPos, removeIconYPos,
+            GreaseMonkeyConfigurationListDelegate.RemoveIconSize,
+            GreaseMonkeyConfigurationListDelegate.RemoveIconSize)
+        painter.drawPixmap(removeIconRect, self.__removePixmap)
+    
+    def sizeHint(self, option, index):
+        """
+        Public method to get a size hint for the specified list item.
+        
+        @param option style option used for painting (QStyleOptionViewItem)
+        @param index model index of the item (QModelIndex)
+        @return size hint (QSize)
+        """
+        if not self.__rowHeight:
+            opt = QStyleOptionViewItem(option)
+            self.initStyleOption(opt, index)
+            
+            widget = opt.widget
+            style = widget.style() if widget is not None \
+                else QApplication.style()
+            padding = style.pixelMetric(QStyle.PM_FocusFrameHMargin) + 1
+            
+            titleFont = opt.font
+            titleFont.setBold(True)
+            titleFont.setPointSize(titleFont.pointSize() + 1)
+            
+            self.__padding = padding \
+                if padding > GreaseMonkeyConfigurationListDelegate.MinPadding \
+                else GreaseMonkeyConfigurationListDelegate.MinPadding
+            
+            titleMetrics = QFontMetrics(titleFont)
+            
+            self.__rowHeight = 2 * self.__padding + \
+                opt.fontMetrics.leading() + \
+                opt.fontMetrics.height() + \
+                titleMetrics.height()
+        
+        return QSize(GreaseMonkeyConfigurationListDelegate.ItemWidth,
+                     self.__rowHeight)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/GreaseMonkey/GreaseMonkeyConfiguration/GreaseMonkeyConfigurationListWidget.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,81 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a special list widget for GreaseMonkey scripts.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSignal, QRect
+from PyQt5.QtWidgets import QListWidget, QListWidgetItem
+
+from .GreaseMonkeyConfigurationListDelegate import \
+    GreaseMonkeyConfigurationListDelegate
+
+
+class GreaseMonkeyConfigurationListWidget(QListWidget):
+    """
+    Class implementing a special list widget for GreaseMonkey scripts.
+    """
+    removeItemRequested = pyqtSignal(QListWidgetItem)
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(GreaseMonkeyConfigurationListWidget, self).__init__(parent)
+        
+        self.__delegate = GreaseMonkeyConfigurationListDelegate(self)
+        self.setItemDelegate(self.__delegate)
+    
+    def __containsRemoveIcon(self, pos):
+        """
+        Private method to check, if the given position is inside the remove
+        icon.
+        
+        @param pos position to check for (QPoint)
+        @return flag indicating success (boolean)
+        """
+        itm = self.itemAt(pos)
+        if itm is None:
+            return False
+        
+        rect = self.visualItemRect(itm)
+        iconSize = GreaseMonkeyConfigurationListDelegate.RemoveIconSize
+        removeIconXPos = rect.right() - self.__delegate.padding() - iconSize
+        center = rect.height() // 2 + rect.top()
+        removeIconYPos = center - iconSize // 2
+        
+        removeIconRect = QRect(removeIconXPos, removeIconYPos,
+                               iconSize, iconSize)
+        return removeIconRect.contains(pos)
+    
+    def mousePressEvent(self, evt):
+        """
+        Protected method handling presses of mouse buttons.
+        
+        @param evt mouse press event (QMouseEvent)
+        """
+        if self.__containsRemoveIcon(evt.pos()):
+            self.removeItemRequested.emit(self.itemAt(evt.pos()))
+            return
+        
+        super(GreaseMonkeyConfigurationListWidget, self).mousePressEvent(evt)
+    
+    def mouseDoubleClickEvent(self, evt):
+        """
+        Protected method handling mouse double click events.
+        
+        @param evt mouse press event (QMouseEvent)
+        """
+        if self.__containsRemoveIcon(evt.pos()):
+            self.removeItemRequested.emit(self.itemAt(evt.pos()))
+            return
+        
+        super(GreaseMonkeyConfigurationListWidget, self).mouseDoubleClickEvent(
+            evt)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/GreaseMonkey/GreaseMonkeyConfiguration/GreaseMonkeyConfigurationScriptInfoDialog.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,64 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog to show GreaseMonkey script information.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSlot
+from PyQt5.QtWidgets import QDialog
+
+from .Ui_GreaseMonkeyConfigurationScriptInfoDialog import \
+    Ui_GreaseMonkeyConfigurationScriptInfoDialog
+
+from ..GreaseMonkeyScript import GreaseMonkeyScript
+
+import UI.PixmapCache
+
+
+class GreaseMonkeyConfigurationScriptInfoDialog(
+        QDialog, Ui_GreaseMonkeyConfigurationScriptInfoDialog):
+    """
+    Class implementing a dialog to show GreaseMonkey script information.
+    """
+    def __init__(self, script, parent=None):
+        """
+        Constructor
+        
+        @param script reference to the script (GreaseMonkeyScript)
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(GreaseMonkeyConfigurationScriptInfoDialog, self).__init__(parent)
+        self.setupUi(self)
+        
+        self.iconLabel.setPixmap(
+            UI.PixmapCache.getPixmap("greaseMonkey48.png"))
+        
+        self.__scriptFileName = script.fileName()
+        
+        self.setWindowTitle(
+            self.tr("Script Details of {0}").format(script.name()))
+        
+        self.nameLabel.setText(script.fullName())
+        self.versionLabel.setText(script.version())
+        self.urlLabel.setText(script.downloadUrl().toString())
+        if script.startAt() == GreaseMonkeyScript.DocumentStart:
+            self.startAtLabel.setText("document-start")
+        else:
+            self.startAtLabel.setText("document-end")
+        self.descriptionBrowser.setHtml(script.description())
+        self.runsAtBrowser.setHtml("<br/>".join(script.include()))
+        self.doesNotRunAtBrowser.setHtml("<br/>".join(script.exclude()))
+    
+    @pyqtSlot()
+    def on_showScriptSourceButton_clicked(self):
+        """
+        Private slot to show an editor window with the script source code.
+        """
+        from QScintilla.MiniEditor import MiniEditor
+        editor = MiniEditor(self.__scriptFileName, "JavaScript", self)
+        editor.show()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/GreaseMonkey/GreaseMonkeyConfiguration/GreaseMonkeyConfigurationScriptInfoDialog.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,246 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>GreaseMonkeyConfigurationScriptInfoDialog</class>
+ <widget class="QDialog" name="GreaseMonkeyConfigurationScriptInfoDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>550</width>
+    <height>500</height>
+   </rect>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QLabel" name="iconLabel">
+       <property name="minimumSize">
+        <size>
+         <width>48</width>
+         <height>48</height>
+        </size>
+       </property>
+       <property name="text">
+        <string notr="true">Icon</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLabel" name="label_8">
+       <property name="text">
+        <string>&lt;h2&gt;GreaseMonkey Script Details&lt;/h2&gt;</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="horizontalSpacer_2">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <layout class="QGridLayout" name="gridLayout">
+     <item row="0" column="0">
+      <widget class="QLabel" name="label">
+       <property name="text">
+        <string>Name:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="1">
+      <widget class="QLabel" name="nameLabel">
+       <property name="text">
+        <string/>
+       </property>
+       <property name="textInteractionFlags">
+        <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="0">
+      <widget class="QLabel" name="label_2">
+       <property name="text">
+        <string>Version:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="1">
+      <widget class="QLabel" name="versionLabel">
+       <property name="text">
+        <string/>
+       </property>
+       <property name="textInteractionFlags">
+        <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="0">
+      <widget class="QLabel" name="label_3">
+       <property name="text">
+        <string>URL:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="1">
+      <widget class="QLabel" name="urlLabel">
+       <property name="text">
+        <string/>
+       </property>
+       <property name="textInteractionFlags">
+        <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
+       </property>
+      </widget>
+     </item>
+     <item row="3" column="0">
+      <widget class="QLabel" name="label_4">
+       <property name="text">
+        <string>Start at:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="3" column="1">
+      <widget class="QLabel" name="startAtLabel">
+       <property name="text">
+        <string/>
+       </property>
+       <property name="textInteractionFlags">
+        <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
+       </property>
+      </widget>
+     </item>
+     <item row="4" column="0">
+      <widget class="QLabel" name="label_5">
+       <property name="text">
+        <string>Description:</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+       </property>
+      </widget>
+     </item>
+     <item row="4" column="1">
+      <widget class="QTextBrowser" name="descriptionBrowser"/>
+     </item>
+     <item row="5" column="0">
+      <widget class="QLabel" name="label_6">
+       <property name="text">
+        <string>Runs at:</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+       </property>
+      </widget>
+     </item>
+     <item row="5" column="1">
+      <widget class="QTextBrowser" name="runsAtBrowser"/>
+     </item>
+     <item row="6" column="0">
+      <widget class="QLabel" name="label_7">
+       <property name="text">
+        <string>Does not run at:</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+       </property>
+      </widget>
+     </item>
+     <item row="6" column="1">
+      <widget class="QTextBrowser" name="doesNotRunAtBrowser"/>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_2">
+     <item>
+      <widget class="QPushButton" name="showScriptSourceButton">
+       <property name="toolTip">
+        <string>Press to open an editor with the script's source</string>
+       </property>
+       <property name="text">
+        <string>Show source code of script</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QDialogButtonBox" name="buttonBox">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="standardButtons">
+        <set>QDialogButtonBox::Close</set>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <tabstops>
+  <tabstop>descriptionBrowser</tabstop>
+  <tabstop>runsAtBrowser</tabstop>
+  <tabstop>doesNotRunAtBrowser</tabstop>
+  <tabstop>showScriptSourceButton</tabstop>
+  <tabstop>buttonBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>GreaseMonkeyConfigurationScriptInfoDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>402</x>
+     <y>484</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>GreaseMonkeyConfigurationScriptInfoDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>470</x>
+     <y>490</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/GreaseMonkey/GreaseMonkeyConfiguration/__init__.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Package implementing the GreaseMonkey configuration dialogs.
+"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/GreaseMonkey/GreaseMonkeyDownloader.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,181 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the downloader for GreaseMonkey scripts.
+"""
+
+from __future__ import unicode_literals
+
+import os
+
+from PyQt5.QtCore import pyqtSignal, QObject, QSettings, QRegExp, QUrl
+from PyQt5.QtWidgets import QDialog
+from PyQt5.QtNetwork import QNetworkReply
+
+from E5Gui import E5MessageBox
+
+from WebBrowser.WebBrowserWindow import WebBrowserWindow
+from WebBrowser.Network.FollowRedirectReply import FollowRedirectReply
+
+
+class GreaseMonkeyDownloader(QObject):
+    """
+    Class implementing the downloader for GreaseMonkey scripts.
+    """
+    finished = pyqtSignal()
+    
+    def __init__(self, url, manager):
+        """
+        Constructor
+        
+        @param url URL to download script from
+        @type QUrl
+        @param manager reference to the GreaseMonkey manager
+        @type GreaseMonkeyManager
+        """
+        super(GreaseMonkeyDownloader, self).__init__()
+        
+        self.__manager = manager
+        
+        self.__reply = FollowRedirectReply(
+            url, WebBrowserWindow.networkManager())
+        self.__reply.finished.connect(self.__scriptDownloaded)
+        
+        self.__fileName = ""
+        self.__requireUrls = []
+    
+    def __scriptDownloaded(self):
+        """
+        Private slot to handle the finished download of a script.
+        """
+        if self.sender() != self.__reply:
+            self.finished.emit()
+            return
+        
+        response = bytes(self.__reply.readAll()).decode()
+        
+        if self.__reply.error() == QNetworkReply.NoError and \
+           "// ==UserScript==" in response:
+            from WebBrowser.Tools import WebBrowserTools
+            filePath = os.path.join(
+                self.__manager.scriptsDirectory(),
+                WebBrowserTools.getFileNameFromUrl(self.__reply.url()))
+            self.__fileName = WebBrowserTools.ensureUniqueFilename(filePath)
+            
+            try:
+                f = open(self.__fileName, "w", encoding="utf-8")
+            except (IOError, OSError) as err:
+                E5MessageBox.critical(
+                    None,
+                    self.tr("GreaseMonkey Download"),
+                    self.tr(
+                        """<p>The file <b>{0}</b> could not be opened"""
+                        """ for writing.<br/>Reason: {1}</p>""").format(
+                        self.__fileName, str(err)))
+                self.finished.emit()
+                return
+            f.write(response)
+            f.close()
+            
+            settings = QSettings(
+                os.path.join(self.__manager.requireScriptsDirectory(),
+                             "requires.ini"),
+                QSettings.IniFormat)
+            settings.beginGroup("Files")
+            
+            rx = QRegExp("@require(.*)\\n")
+            rx.setMinimal(True)
+            rx.indexIn(response)
+            
+            for i in range(1, rx.captureCount() + 1):
+                url = rx.cap(i).strip()
+                if url and not settings.contains(url):
+                    self.__requireUrls.append(QUrl(url))
+        
+        self.__reply.deleteLater()
+        self.__reply = None
+        
+        self.__downloadRequires()
+    
+    def __requireDownloaded(self):
+        """
+        Private slot to handle the finished download of a required script.
+        """
+        if self.sender() != self.__reply:
+            self.finished.emit()
+            return
+        
+        response = bytes(self.__reply.readAll()).decode()
+        
+        if self.__reply.error() == QNetworkReply.NoError and response:
+            from WebBrowser.Tools import WebBrowserTools
+            filePath = os.path.join(self.__manager.requireScriptsDirectory(),
+                                    "require.js")
+            fileName = WebBrowserTools.ensureUniqueFilename(filePath, "{0}")
+            
+            try:
+                f = open(fileName, "w", encoding="utf-8")
+            except (IOError, OSError) as err:
+                E5MessageBox.critical(
+                    None,
+                    self.tr("GreaseMonkey Download"),
+                    self.tr(
+                        """<p>The file <b>{0}</b> could not be opened"""
+                        """ for writing.<br/>Reason: {1}</p>""").format(
+                        fileName, str(err)))
+                self.finished.emit()
+                return
+            f.write(response)
+            f.close()
+            
+            settings = QSettings(
+                os.path.join(self.__manager.requireScriptsDirectory(),
+                             "requires.ini"),
+                QSettings.IniFormat)
+            settings.beginGroup("Files")
+            settings.setValue(self.__reply.originalUrl().toString(), fileName)
+        
+        self.__reply.deleteLater()
+        self.__reply = None
+        
+        self.__downloadRequires()
+    
+    def __downloadRequires(self):
+        """
+        Private slot to initiate the download of required scripts.
+        """
+        if self.__requireUrls:
+            self.__reply = FollowRedirectReply(
+                self.__requireUrls.pop(0),
+                WebBrowserWindow.networkManager())
+            self.__reply.finished.connect(self.__requireDownloaded)
+        else:
+            from .GreaseMonkeyScript import GreaseMonkeyScript
+            deleteScript = True
+            script = GreaseMonkeyScript(self.__manager, self.__fileName)
+            
+            if script.isValid():
+                if not self.__manager.containsScript(script.fullName()):
+                    from .GreaseMonkeyAddScriptDialog import \
+                        GreaseMonkeyAddScriptDialog
+                    dlg = GreaseMonkeyAddScriptDialog(self.__manager, script)
+                    deleteScript = dlg.exec_() != QDialog.Accepted
+                else:
+                    E5MessageBox.information(
+                        None,
+                        self.tr("GreaseMonkey Download"),
+                        self.tr(
+                            """<p><b>{0}</b> is already installed.</p>""")
+                        .format(script.name()))
+            
+            if deleteScript:
+                try:
+                    os.remove(self.__fileName)
+                except (IOError, OSError):
+                    # ignore
+                    pass
+            
+            self.finished.emit()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/GreaseMonkey/GreaseMonkeyJavaScript.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,155 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module containing some JavaScript resources.
+"""
+
+from __future__ import unicode_literals
+
+bootstrap_js = """
+if(typeof GM_xmlhttpRequest === "undefined") {
+    GM_xmlhttpRequest = function(/* object */ details) {
+        details.method = details.method.toUpperCase() || "GET";
+
+        if(!details.url) {
+            throw("GM_xmlhttpRequest requires an URL.");
+        }
+
+        // build XMLHttpRequest object
+        var oXhr = new XMLHttpRequest;
+        // run it
+        if(oXhr) {
+            if("onreadystatechange" in details)
+                oXhr.onreadystatechange = function() {
+                    details.onreadystatechange(oXhr)
+                };
+            if("onload" in details)
+                oXhr.onload = function() { details.onload(oXhr) };
+            if("onerror" in details)
+                oXhr.onerror = function() { details.onerror(oXhr) };
+
+            oXhr.open(details.method, details.url, true);
+
+            if("headers" in details)
+                for(var header in details.headers)
+                    oXhr.setRequestHeader(header, details.headers[header]);
+
+            if("data" in details)
+                oXhr.send(details.data);
+            else
+                oXhr.send();
+        } else
+            throw ("This Browser is not supported, please upgrade.")
+    }
+}
+
+if(typeof GM_addStyle === "undefined") {
+    function GM_addStyle(/* String */ styles) {
+        var head = document.getElementsByTagName("head")[0];
+        if (head === undefined) {
+            document.onreadystatechange = function() {
+                if (document.readyState == "interactive") {
+                  var oStyle = document.createElement("style");
+                  oStyle.setAttribute("type", "text\/css");
+                  oStyle.appendChild(document.createTextNode(styles));
+                  document.getElementsByTagName("head")[0].appendChild(oStyle);
+                }
+            }
+        }
+        else {
+            var oStyle = document.createElement("style");
+            oStyle.setAttribute("type", "text\/css");
+            oStyle.appendChild(document.createTextNode(styles));
+            head.appendChild(oStyle);
+        }
+    }
+}
+
+if(typeof GM_log === "undefined") {
+    function GM_log(log) {
+        if(console)
+            console.log(log);
+    }
+}
+
+if(typeof GM_openInTab === "undefined") {
+    function GM_openInTab(url) {
+        window.open(url)
+    }
+}
+
+// Define unsafe window
+var unsafeWindow = window;
+window.wrappedJSObject = unsafeWindow;
+
+// GM_registerMenuCommand not supported
+if(typeof GM_registerMenuCommand === "undefined") {
+    function GM_registerMenuCommand(caption, commandFunc, accessKey) { }
+}
+
+// GM Resource not supported
+if(typeof GM_getResourceText === "undefined") {
+    function GM_getResourceText(resourceName) {
+        throw ("eric6 Web Browser: GM Resource is not supported!");
+    }
+}
+
+if(typeof GM_getResourceURL === "undefined") {
+    function GM_getResourceURL(resourceName) {
+        throw ("eric6 Web Browser: GM Resource is not supported!");
+    }
+}
+
+// GM Settings not supported
+if(typeof GM_getValue === "undefined") {
+    function GM_getValue(name, defaultValue) {
+        return defaultValue;
+    }
+}
+
+if(typeof GM_setValue === "undefined") {
+    function GM_setValue(name, value) { }
+}
+
+if(typeof GM_deleteValue === "undefined") {
+    function GM_deleteValue(name) { }
+}
+
+if(typeof GM_listValues === "undefined") {
+    function GM_listValues() {
+        return new Array("");
+    }
+}
+"""
+
+
+# {0} - unique script id
+values_js = """
+function GM_deleteValue(aKey) {{
+    localStorage.removeItem("{0}" + aKey);
+}}
+
+function GM_getValue(aKey, aDefault) {{
+    var val = localStorage.getItem("{0}" + aKey)
+    if (null === val && 'undefined' != typeof aDefault) return aDefault;
+    return val;
+}}
+
+function GM_listValues() {{
+    var values = [];
+    for (var i = 0; i < localStorage.length; i++) {{
+        var k = localStorage.key(i);
+        if (k.indexOf("{0}") === 0) {{
+            values.push(k.replace("{0}", ""));
+        }}
+    }}
+    return values;
+}}
+
+function GM_setValue(aKey, aVal) {{
+    localStorage.setItem("{0}" + aKey, aVal);
+}}
+"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/GreaseMonkey/GreaseMonkeyManager.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,307 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the manager for GreaseMonkey scripts.
+"""
+
+from __future__ import unicode_literals
+
+import os
+
+from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QObject, QTimer, QFile, QDir, \
+    QSettings, QMetaObject, QUrl, Q_ARG
+
+import Utilities
+import Preferences
+
+from WebBrowser.WebBrowserWindow import WebBrowserWindow
+from .GreaseMonkeyUrlInterceptor import GreaseMonkeyUrlInterceptor
+
+
+# TODO: GreaseMonkey: needs testing with Qt 5.6
+class GreaseMonkeyManager(QObject):
+    """
+    Class implementing the manager for GreaseMonkey scripts.
+    """
+    scriptsChanged = pyqtSignal()
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object (QObject)
+        """
+        super(GreaseMonkeyManager, self).__init__(parent)
+        
+        self.__disabledScripts = []
+        self.__scripts = []
+        self.__downloaders = []
+        
+        self.__interceptor = GreaseMonkeyUrlInterceptor(self)
+        WebBrowserWindow.networkManager().installUrlInterceptor(
+            GreaseMonkeyUrlInterceptor(self))
+        
+        QTimer.singleShot(0, self.__load)
+    
+    def __del__(self):
+        """
+        Special method called during object destruction.
+        """
+        WebBrowserWindow.networkManager().removeUrlInterceptor(
+            self.__interceptor)
+    
+    def showConfigurationDialog(self, parent=None):
+        """
+        Public method to show the configuration dialog.
+        
+        @param parent reference to the parent widget (QWidget)
+        """
+        from .GreaseMonkeyConfiguration.GreaseMonkeyConfigurationDialog \
+            import GreaseMonkeyConfigurationDialog
+        self.__configDiaolg = GreaseMonkeyConfigurationDialog(self, parent)
+        self.__configDiaolg.show()
+    
+    def downloadScript(self, url):
+        """
+        Public method to download a GreaseMonkey script.
+        
+        @param url URL to download script from
+        @type QUrl
+        """
+        QMetaObject.invokeMethod(
+            self, "doDownloadScript", Qt.QueuedConnection,
+            Q_ARG(QUrl, url))
+    
+    @pyqtSlot(QUrl)
+    def doDownloadScript(self, url):
+        from .GreaseMonkeyDownloader import GreaseMonkeyDownloader
+        downloader = GreaseMonkeyDownloader(url, self)
+        downloader.finished.connect(self.__downloaderFinished)
+        self.__downloaders.append(downloader)
+##void GM_Manager::downloadScript(const QUrl &url)
+##{
+##    QMetaObject::invokeMethod(this, "doDownloadScript", Qt::QueuedConnection, Q_ARG(QUrl, url));
+##}
+##void GM_Manager::doDownloadScript(const QUrl &url)
+##{
+##    new GM_Downloader(url, this);
+##}
+    
+    def __downloaderFinished(self):
+        """
+        Private slot to handle the completion of a script download.
+        """
+        downloader = self.sender()
+        if downloader is None or downloader not in self.__downloaders:
+            return
+        
+        self.__downloaders.remove(downloader)
+    
+    def scriptsDirectory(self):
+        """
+        Public method to get the path of the scripts directory.
+        
+        @return path of the scripts directory (string)
+        """
+        return os.path.join(
+            Utilities.getConfigDir(), "web_browser", "greasemonkey")
+    
+    def requireScriptsDirectory(self):
+        """
+        Public method to get the path of the scripts directory.
+        
+        @return path of the scripts directory (string)
+        """
+        return os.path.join(self.scriptsDirectory(), "requires")
+    
+    def requireScripts(self, urlList):
+        """
+        Public method to get the sources of all required scripts.
+        
+        @param urlList list of URLs (list of string)
+        @return sources of all required scripts (string)
+        """
+        requiresDir = QDir(self.requireScriptsDirectory())
+        if not requiresDir.exists() or len(urlList) == 0:
+            return ""
+        
+        script = ""
+        
+        settings = QSettings(
+            os.path.join(self.requireScriptsDirectory(), "requires.ini"),
+            QSettings.IniFormat)
+        settings.beginGroup("Files")
+        for url in urlList:
+            if settings.contains(url):
+                fileName = settings.value(url)
+                try:
+                    f = open(fileName, "r", encoding="utf-8")
+                    source = f.read()
+                    f.close()
+                except (IOError, OSError):
+                    source = ""
+                script += source.strip() + "\n"
+        
+        return script
+    
+    def saveConfiguration(self):
+        """
+        Public method to save the configuration.
+        """
+        Preferences.setWebBrowser("GreaseMonkeyDisabledScripts",
+                                  self.__disabledScripts)
+    
+    def allScripts(self):
+        """
+        Public method to get a list of all scripts.
+        
+        @return list of all scripts (list of GreaseMonkeyScript)
+        """
+        return self.__scripts[:]
+    
+    def containsScript(self, fullName):
+        """
+        Public method to check, if the given script exists.
+        
+        @param fullName full name of the script (string)
+        @return flag indicating the existence (boolean)
+        """
+        for script in self.__scripts:
+            if script.fullName() == fullName:
+                return True
+        
+        return False
+    
+    def enableScript(self, script):
+        """
+        Public method to enable the given script.
+        
+        @param script script to be enabled (GreaseMonkeyScript)
+        """
+        script.setEnabled(True)
+        fullName = script.fullName()
+        if fullName in self.__disabledScripts:
+            self.__disabledScripts.remove(fullName)
+        
+        collection = WebBrowserWindow.webProfile().scripts()
+        collection.insert(script.webScript())
+    
+    def disableScript(self, script):
+        """
+        Public method to disable the given script.
+        
+        @param script script to be disabled (GreaseMonkeyScript)
+        """
+        script.setEnabled(False)
+        fullName = script.fullName()
+        if fullName not in self.__disabledScripts:
+            self.__disabledScripts.append(fullName)
+        
+        collection = WebBrowserWindow.webProfile().scripts()
+        collection.remove(collection.findScript(fullName))
+    
+    def addScript(self, script):
+        """
+        Public method to add a script.
+        
+        @param script script to be added (GreaseMonkeyScript)
+        @return flag indicating success (boolean)
+        """
+        if not script or not script.isValid():
+            return False
+        
+        self.__scripts.append(script)
+        script.scriptChanged.connect(self.__scriptChanged)
+        
+        collection = WebBrowserWindow.webProfile().scripts()
+        collection.insert(script.webScript())
+        
+        self.scriptsChanged.emit()
+        return True
+    
+    def removeScript(self, script, removeFile=True):
+        """
+        Public method to remove a script.
+        
+        @param script script to be removed (GreaseMonkeyScript)
+        @param removeFile flag indicating to remove the script file as well
+            (bool)
+        @return flag indicating success (boolean)
+        """
+        if not script:
+            return False
+        
+        try:
+            self.__scripts.remove(script)
+        except ValueError:
+            pass
+        
+        fullName = script.fullName()
+        collection = WebBrowserWindow.webProfile().scripts()
+        collection.remove(collection.findScript(fullName))
+        
+        if fullName in self.__disabledScripts:
+            self.__disabledScripts.remove(fullName)
+        
+        if removeFile:
+            QFile.remove(script.fileName())
+            del script
+        
+        self.scriptsChanged.emit()
+        return True
+    
+    def canRunOnScheme(self, scheme):
+        """
+        Public method to check, if scripts can be run on a scheme.
+        
+        @param scheme scheme to check (string)
+        @return flag indicating, that scripts can be run (boolean)
+        """
+        return scheme in ["http", "https", "data", "ftp"]
+    
+    def __load(self):
+        """
+        Private slot to load the available scripts into the manager.
+        """
+        scriptsDir = QDir(self.scriptsDirectory())
+        if not scriptsDir.exists():
+            scriptsDir.mkpath(self.scriptsDirectory())
+        
+        if not scriptsDir.exists("requires"):
+            scriptsDir.mkdir("requires")
+        
+        self.__disabledScripts = \
+            Preferences.getWebBrowser("GreaseMonkeyDisabledScripts")
+        
+        from .GreaseMonkeyScript import GreaseMonkeyScript
+        for fileName in scriptsDir.entryList(["*.js"], QDir.Files):
+            absolutePath = scriptsDir.absoluteFilePath(fileName)
+            script = GreaseMonkeyScript(self, absolutePath)
+            
+            if not script.isValid():
+                del script
+                continue
+            
+            self.__scripts.append(script)
+            
+            if script.fullName() in self.__disabledScripts:
+                script.setEnabled(False)
+            else:
+                collection = WebBrowserWindow.webProfile().scripts()
+                collection.insert(script.webScript())
+    
+    def __scriptChanged(self):
+        """
+        Private slot handling a changed script.
+        """
+        script = self.sender()
+        if not script:
+            return
+        
+        fullName = script.fullName()
+        collection = WebBrowserWindow.webProfile().scripts()
+        collection.remove(collection.findScript(fullName))
+        collection.insert(script.webScript())
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/GreaseMonkey/GreaseMonkeyScript.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,394 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the GreaseMonkey script.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, QUrl, QRegExp, \
+    QByteArray,  QCryptographicHash
+from PyQt5.QtWebEngineWidgets import QWebEngineScript
+
+from .GreaseMonkeyJavaScript import bootstrap_js, values_js
+
+from ..Tools.DelayedFileWatcher import DelayedFileWatcher
+
+
+class GreaseMonkeyScript(QObject):
+    """
+    Class implementing the GreaseMonkey script.
+    """
+    DocumentStart = 0
+    DocumentEnd = 1
+    DocumentIdle = 2
+    
+    scriptChanged = pyqtSignal()
+    
+    def __init__(self, manager, path):
+        """
+        Constructor
+        
+        @param manager reference to the manager object (GreaseMonkeyManager)
+        @param path path of the Javascript file (string)
+        """
+        super(GreaseMonkeyScript, self).__init__(manager)
+        
+        self.__manager = manager
+        self.__fileWatcher = DelayedFileWatcher(parent=None)
+        
+        self.__name = ""
+        self.__namespace = "GreaseMonkeyNS"
+        self.__description = ""
+        self.__version = ""
+        
+        self.__include = []
+        self.__exclude = []
+        
+        self.__downloadUrl = QUrl()
+        self.__updateUrl = QUrl()
+        self.__startAt = GreaseMonkeyScript.DocumentEnd
+        
+        self.__script = ""
+        self.__fileName = path
+        self.__enabled = True
+        self.__valid = False
+        self.__noFrames = False
+        
+        self.__parseScript()
+        
+        self.__fileWatcher.delayedFileChanged.connect(
+            self.__watchedFileChanged)
+    
+    def isValid(self):
+        """
+        Public method to check the validity of the script.
+        
+        @return flag indicating a valid script (boolean)
+        """
+        return self.__valid
+    
+    def name(self):
+        """
+        Public method to get the name of the script.
+        
+        @return name of the script (string)
+        """
+        return self.__name
+    
+    def nameSpace(self):
+        """
+        Public method to get the name space of the script.
+        
+        @return name space of the script (string)
+        """
+        return self.__namespace
+    
+    def fullName(self):
+        """
+        Public method to get the full name of the script.
+        
+        @return full name of the script (string)
+        """
+        return "{0}/{1}".format(self.__namespace, self.__name)
+    
+    def description(self):
+        """
+        Public method to get the description of the script.
+        
+        @return description of the script (string)
+        """
+        return self.__description
+    
+    def version(self):
+        """
+        Public method to get the version of the script.
+        
+        @return version of the script (string)
+        """
+        return self.__version
+    
+    def downloadUrl(self):
+        """
+        Public method to get the download URL of the script.
+        
+        @return download URL of the script (QUrl)
+        """
+        return QUrl(self.__downloadUrl)
+    
+    def updateUrl(self):
+        """
+        Public method to get the update URL of the script.
+        
+        @return update URL of the script (QUrl)
+        """
+        return QUrl(self.__updateUrl)
+    
+    def startAt(self):
+        """
+        Public method to get the start point of the script.
+        
+        @return start point of the script (DocumentStart or DocumentEnd)
+        """
+        return self.__startAt
+    
+    def noFrames(self):
+        """
+        Public method to get the noFrames flag.
+        
+        @return flag indicating to not run on sub frames
+        @rtype bool
+        """
+        return self.__noFrames
+    
+    def isEnabled(self):
+        """
+        Public method to check, if the script is enabled.
+        
+        @return flag indicating an enabled state (boolean)
+        """
+        return self.__enabled and self.__valid
+    
+    def setEnabled(self, enable):
+        """
+        Public method to enable a script.
+        
+        @param enable flag indicating the new enabled state (boolean)
+        """
+        self.__enabled = enable
+    
+    def include(self):
+        """
+        Public method to get the list of included URLs.
+        
+        @return list of included URLs (list of strings)
+        """
+        return self.__include[:]
+    
+    def exclude(self):
+        """
+        Public method to get the list of excluded URLs.
+        
+        @return list of excluded URLs (list of strings)
+        """
+        return self.__exclude[:]
+    
+    def script(self):
+        """
+        Public method to get the Javascript source.
+        
+        @return Javascript source (string)
+        """
+        return self.__script
+    
+    def fileName(self):
+        """
+        Public method to get the path of the Javascript file.
+        
+        @return path path of the Javascript file (string)
+        """
+        return self.__fileName
+    
+    @pyqtSlot(str)
+    def __watchedFileChanged(self, fileName):
+        """
+        Private slot handling changes of the script file.
+        
+        @param fileName path of the script file
+        @type str
+        """
+        if self.__fileName == fileName:
+            self.__parseScript()
+            
+            self.__manager.removeScript(self, False)
+            self.__manager.addScript(self)
+            
+            self.scriptChanged.emit()
+    
+    def __parseScript(self):
+        """
+        Private method to parse the given script and populate the data
+        structure.
+        """
+        self.__name = ""
+        self.__namespace = "GreaseMonkeyNS"
+        self.__description = ""
+        self.__version = ""
+        
+        self.__include = []
+        self.__exclude = []
+        
+        self.__downloadUrl = QUrl()
+        self.__updateUrl = QUrl()
+        self.__startAt = GreaseMonkeyScript.DocumentEnd
+        
+        self.__script = ""
+        self.__enabled = True
+        self.__valid = False
+        self.__noFrames = False
+        
+        try:
+            f = open(self.__fileName, "r", encoding="utf-8")
+            fileData = f.read()
+            f.close()
+        except (IOError, OSError):
+            # silently ignore because it shouldn't happen
+            return
+        
+        if self.__fileName not in self.__fileWatcher.files():
+            self.__fileWatcher.addPath(self.__fileName)
+        
+        rx = QRegExp("// ==UserScript==(.*)// ==/UserScript==")
+        rx.indexIn(fileData)
+        metaDataBlock = rx.cap(1).strip()
+        
+        if metaDataBlock == "":
+            # invalid script file
+            return
+        
+        requireList = []
+        for line in metaDataBlock.splitlines():
+            if not line.strip():
+                continue
+            
+            if not line.startswith("// @"):
+                continue
+            
+            line = line[3:].replace("\t", " ")
+            index = line.find(" ")
+            if index < 0:
+                continue
+            
+            key = line[:index].strip()
+            value = line[index + 1:].strip()
+            
+            # Ignored values: @resource, @unwrap
+            
+            if not key or not value:
+                continue
+            
+            if key == "@name":
+                self.__name = value
+            
+            elif key == "@namespace":
+                self.__namespace = value
+            
+            elif key == "@description":
+                self.__description = value
+            
+            elif key == "@version":
+                self.__version = value
+            
+##            elif key == "@updateURL":
+##                self.__downloadUrl = QUrl(value)
+##            
+            elif key in ["@include", "@match"]:
+                self.__include.append(value)
+            
+            elif key in ["@exclude", "@exclude_match"]:
+                self.__exclude.append(value)
+            
+            elif key == "@require":
+                requireList.append(value)
+            
+            elif key == "@run-at":
+                if value == "document-end":
+                    self.__startAt = GreaseMonkeyScript.DocumentEnd
+                elif value == "document-start":
+                    self.__startAt = GreaseMonkeyScript.DocumentStart
+                elif value == "document-idle":
+                    self.__startAt = GreaseMonkeyScript.DocumentIdle
+            
+            elif key == "@downloadURL" and self.__downloadUrl.isEmpty():
+                self.__downloadUrl = QUrl(value)
+            
+            elif key == "@updateURL" and self.__updateUrl.isEmpty():
+                self.__updateUrl = QUrl(value)
+        
+        if not self.__include:
+            self.__include.append("*")
+        
+        nspace = bytes(QCryptographicHash.hash(
+            QByteArray(self.fullName().encode("utf-8")),
+            QCryptographicHash.Md4).toHex()).decode("ascii")
+        valuesScript = values_js.format(nspace)
+        runCheck = """
+            for (var value of {0}) {{
+                var re = new RegExp(value);
+                if (re.test(window.location.href)) {{
+                    return;
+                }}
+            }}
+            __eric_includes = false;
+            for (var value of {1}) {{
+                var re = new RegExp(value);
+                if (re.test(window.location.href)) {{
+                    __eric_includes = true;
+                    break;
+                }}
+            }}
+            if (!__eric_includes) {{
+                return;
+            }}
+            delete __eric_includes;""".format(
+                self.__toJavaScriptList(self.__exclude[:]),
+                self.__toJavaScriptList(self.__include[:])
+            )
+        runCheck = ""
+        self.__script = "(function(){{{0}\n{1}\n{2}\n{3}\n}})();".format(
+            runCheck, valuesScript,
+            self.__manager.requireScripts(requireList), fileData
+        )
+        self.__valid = True
+    
+    def webScript(self):
+        """
+        Public method to create a script object.
+        
+        @return prepared script object
+        @rtype QWebEngineScript
+        """
+        if self.startAt() == GreaseMonkeyScript.DocumentStart:
+            injectionPoint = QWebEngineScript.DocumentCreation
+        elif self.startAt() == GreaseMonkeyScript.DocumentEnd:
+            injectionPoint = QWebEngineScript.DocumentReady
+        elif self.startAt() == GreaseMonkeyScript.DocumentIdle:
+            injectionPoint = QWebEngineScript.Deferred
+        else:
+            raise ValueError("Wrong script start point.")
+        
+        script = QWebEngineScript()
+        script.setName(self.fullName())
+        script.setInjectionPoint(injectionPoint)
+        script.setWorldId(QWebEngineScript.MainWorld)
+        script.setRunsOnSubFrames(not self.__noFrames)
+        script.setSourceCode("{0}\n{1}".format(
+            bootstrap_js, self.__script
+        ))
+        return script
+    
+    def __toJavaScriptList(self, patterns):
+        """
+        Private method to convert a list of str to a string containing a valid
+        JavaScript list definition.
+        
+        @param patterns list of match patterns
+        @type list of str
+        @return JavaScript script containing the list
+        @rtype str
+        """
+        patternList = []
+        for pattern in patterns:
+            if pattern.startswith("/") and pattern.endswith("/") and \
+                    len(pattern) > 1:
+                pattern = pattern[1:-1]
+            else:
+                pattern = pattern.replace(".", "\\.").replace("*", ".*")
+            pattern = "'{0}'".format(pattern)
+            patternList.append(pattern)
+        
+        script = "[{0}]".format(",".join(patternList))
+        return script
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/GreaseMonkey/GreaseMonkeyUrlInterceptor.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,39 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a handler for GreaseMonkey related URLs.
+"""
+
+from __future__ import unicode_literals
+
+from ..Network.UrlInterceptor import UrlInterceptor
+
+
+class GreaseMonkeyUrlInterceptor(UrlInterceptor):
+    """
+    Class implementing a handler for GreaseMonkey related URLs.
+    """
+    def __init__(self, manager):
+        """
+        Constructor
+        
+        @param manager reference to the GreaseMonkey manager
+        @type GreaseMonkeyManager
+        """
+        super(GreaseMonkeyUrlInterceptor, self).__init__(manager)
+        
+        self.__manager = manager
+    
+    def interceptRequest(self, info):
+        """
+        Public method to handle a GreaseMonkey request.
+        
+        @param info request info object
+        @type QWebEngineUrlRequestInfo
+        """
+        if info.requestUrl().toString().endswith(".user.js"):
+            self.__manager.downloadScript(info.requestUrl())
+            info.block(True)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/GreaseMonkey/__init__.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,13 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Package implementing the GreaseMonkey support.
+"""
+
+#
+# The code in this package was inspired and ported in parts from QupZilla
+# Copyright (C) David Rosca <nowrep@gmail.com>
+#
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/History/HistoryCompleter.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,303 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a special completer for the history.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import Qt, QRegExp, QTimer, QSortFilterProxyModel
+from PyQt5.QtWidgets import QTableView, QAbstractItemView, QCompleter
+
+from .HistoryModel import HistoryModel
+from .HistoryFilterModel import HistoryFilterModel
+
+
+class HistoryCompletionView(QTableView):
+    """
+    Class implementing a special completer view for history based completions.
+    """
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(HistoryCompletionView, self).__init__(parent)
+        
+        self.horizontalHeader().hide()
+        self.verticalHeader().hide()
+        
+        self.setShowGrid(False)
+        
+        self.setSelectionBehavior(QAbstractItemView.SelectRows)
+        self.setSelectionMode(QAbstractItemView.SingleSelection)
+        self.setTextElideMode(Qt.ElideRight)
+        
+        metrics = self.fontMetrics()
+        self.verticalHeader().setDefaultSectionSize(metrics.height())
+    
+    def resizeEvent(self, evt):
+        """
+        Protected method handling resize events.
+        
+        @param evt reference to the resize event (QResizeEvent)
+        """
+        self.horizontalHeader().resizeSection(0, 0.65 * self.width())
+        self.horizontalHeader().setStretchLastSection(True)
+        
+        super(HistoryCompletionView, self).resizeEvent(evt)
+    
+    def sizeHintForRow(self, row):
+        """
+        Public method to give a size hint for rows.
+        
+        @param row row number (integer)
+        @return desired row height (integer)
+        """
+        metrics = self.fontMetrics()
+        return metrics.height()
+
+
+class HistoryCompletionModel(QSortFilterProxyModel):
+    """
+    Class implementing a special model for history based completions.
+    """
+    HistoryCompletionRole = HistoryFilterModel.MaxRole + 1
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object (QObject)
+        """
+        super(HistoryCompletionModel, self).__init__(parent)
+        
+        self.__searchString = ""
+        self.__searchMatcher = QRegExp(
+            "", Qt.CaseInsensitive, QRegExp.FixedString)
+        self.__wordMatcher = QRegExp("", Qt.CaseInsensitive)
+        self.__isValid = False
+        
+        self.setDynamicSortFilter(True)
+    
+    def data(self, index, role=Qt.DisplayRole):
+        """
+        Public method to get data from the model.
+        
+        @param index index of history entry to get data for (QModelIndex)
+        @param role data role (integer)
+        @return history entry data
+        """
+        # If the model is valid, tell QCompleter that everything we have
+        # filtered matches what the user typed; if not, nothing matches
+        if role == self.HistoryCompletionRole and index.isValid():
+            if self.isValid():
+                return "t"
+            else:
+                return "f"
+        
+        if role == Qt.DisplayRole:
+            if index.column() == 0:
+                role = HistoryModel.UrlStringRole
+            else:
+                role = HistoryModel.TitleRole
+        
+        return QSortFilterProxyModel.data(self, index, role)
+    
+    def searchString(self):
+        """
+        Public method to get the current search string.
+        
+        @return current search string (string)
+        """
+        return self.__searchString
+    
+    def setSearchString(self, string):
+        """
+        Public method to set the current search string.
+        
+        @param string new search string (string)
+        """
+        if string == self.__searchString:
+            return
+        
+        self.__searchString = string
+        self.__searchMatcher.setPattern(self.__searchString)
+        self.__wordMatcher.setPattern(
+            "\\b" + QRegExp.escape(self.__searchString))
+        self.invalidateFilter()
+    
+    def isValid(self):
+        """
+        Public method to check the model for validity.
+        
+        @return flag indicating a valid status (boolean)
+        """
+        return self.__isValid
+    
+    def setValid(self, valid):
+        """
+        Public method to set the model's validity.
+        
+        @param valid flag indicating the new valid status (boolean)
+        """
+        if valid == self.__isValid:
+            return
+        
+        self.__isValid = valid
+        
+        # tell the history completer that the model has changed
+        self.dataChanged.emit(self.index(0, 0), self.index(0,
+                              self.rowCount() - 1))
+    
+    def filterAcceptsRow(self, sourceRow, sourceParent):
+        """
+        Public method to determine, if the row is acceptable.
+        
+        @param sourceRow row number in the source model (integer)
+        @param sourceParent index of the source item (QModelIndex)
+        @return flag indicating acceptance (boolean)
+        """
+        # Do a case-insensitive substring match against both the url and title.
+        # It's already ensured, that the user doesn't accidentally use regexp
+        # metacharacters (s. setSearchString()).
+        idx = self.sourceModel().index(sourceRow, 0, sourceParent)
+        
+        url = self.sourceModel().data(idx, HistoryModel.UrlStringRole)
+        if self.__searchMatcher.indexIn(url) != -1:
+            return True
+        
+        title = self.sourceModel().data(idx, HistoryModel.TitleRole)
+        if self.__searchMatcher.indexIn(title) != -1:
+            return True
+        
+        return False
+    
+    def lessThan(self, left, right):
+        """
+        Public method used to sort the displayed items.
+        
+        It implements a special sorting function based on the history entry's
+        frequency giving a bonus to hits that match on a word boundary so that
+        e.g. "dot.python-projects.org" is a better result for typing "dot" than
+        "slashdot.org". However, it only looks for the string in the host name,
+        not the entire URL, since while it makes sense to e.g. give
+        "www.phoronix.com" a bonus for "ph", it does NOT make sense to give
+        "www.yadda.com/foo.php" the bonus.
+        
+        @param left index of left item (QModelIndex)
+        @param right index of right item (QModelIndex)
+        @return true, if left is less than right (boolean)
+        """
+        frequency_L = \
+            self.sourceModel().data(left, HistoryFilterModel.FrequencyRole)
+        url_L = self.sourceModel().data(left, HistoryModel.UrlRole).host()
+        title_L = self.sourceModel().data(left, HistoryModel.TitleRole)
+        
+        if self.__wordMatcher.indexIn(url_L) != -1 or \
+           self.__wordMatcher.indexIn(title_L) != -1:
+            frequency_L *= 2
+        
+        frequency_R = \
+            self.sourceModel().data(right, HistoryFilterModel.FrequencyRole)
+        url_R = self.sourceModel().data(right, HistoryModel.UrlRole).host()
+        title_R = self.sourceModel().data(right, HistoryModel.TitleRole)
+        
+        if self.__wordMatcher.indexIn(url_R) != -1 or \
+           self.__wordMatcher.indexIn(title_R) != -1:
+            frequency_R *= 2
+        
+        # Sort results in descending frequency-derived score.
+        return frequency_R < frequency_L
+
+
+class HistoryCompleter(QCompleter):
+    """
+    Class implementing a completer for the browser history.
+    """
+    def __init__(self, model, parent=None):
+        """
+        Constructor
+        
+        @param model reference to the model (QAbstractItemModel)
+        @param parent reference to the parent object (QObject)
+        """
+        super(HistoryCompleter, self).__init__(model, parent)
+        
+        self.setPopup(HistoryCompletionView())
+        
+        # Completion should be against the faked role.
+        self.setCompletionRole(HistoryCompletionModel.HistoryCompletionRole)
+        
+        # Since the completion role is faked, advantage of the sorted-model
+        # optimizations in QCompleter can be taken.
+        self.setCaseSensitivity(Qt.CaseSensitive)
+        self.setModelSorting(QCompleter.CaseSensitivelySortedModel)
+        
+        self.__searchString = ""
+        self.__filterTimer = QTimer(self)
+        self.__filterTimer.setSingleShot(True)
+        self.__filterTimer.timeout.connect(self.__updateFilter)
+    
+    def pathFromIndex(self, idx):
+        """
+        Public method to get a path for a given index.
+        
+        @param idx reference to the index (QModelIndex)
+        @return the actual URL from the history (string)
+        """
+        return self.model().data(idx, HistoryModel.UrlStringRole)
+    
+    def splitPath(self, path):
+        """
+        Public method to split the given path into strings, that are used to
+        match at each level in the model.
+        
+        @param path path to be split (string)
+        @return list of path elements (list of strings)
+        """
+        if path == self.__searchString:
+            return ["t"]
+        
+        # Queue an update to the search string. Wait a bit, so that if the user
+        # is quickly typing, the completer doesn't try to complete until they
+        # pause.
+        if self.__filterTimer.isActive():
+            self.__filterTimer.stop()
+        self.__filterTimer.start(150)
+        
+        # If the previous search results are not a superset of the current
+        # search results, tell the model that it is not valid yet.
+        if not path.startswith(self.__searchString):
+            self.model().setValid(False)
+        
+        self.__searchString = path
+        
+        # The actual filtering is done by the HistoryCompletionModel. Just
+        # return a short dummy here so that QCompleter thinks everything
+        # matched.
+        return ["t"]
+    
+    def __updateFilter(self):
+        """
+        Private slot to update the search string.
+        """
+        completionModel = self.model()
+        
+        # Tell the HistoryCompletionModel about the new search string.
+        completionModel.setSearchString(self.__searchString)
+        
+        # Sort the model.
+        completionModel.sort(0)
+        
+        # Mark it valid.
+        completionModel.setValid(True)
+        
+        # Now update the QCompleter widget, but only if the user is still
+        # typing a URL.
+        if self.widget() is not None and self.widget().hasFocus():
+            self.complete()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/History/HistoryDialog.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,148 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog to manage history.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSignal, Qt, QUrl
+from PyQt5.QtGui import QFontMetrics, QCursor
+from PyQt5.QtWidgets import QDialog, QMenu, QApplication
+
+from E5Gui.E5TreeSortFilterProxyModel import E5TreeSortFilterProxyModel
+
+from .HistoryModel import HistoryModel
+
+from .Ui_HistoryDialog import Ui_HistoryDialog
+
+
+class HistoryDialog(QDialog, Ui_HistoryDialog):
+    """
+    Class implementing a dialog to manage history.
+    
+    @signal openUrl(QUrl, str) emitted to open a URL in the current tab
+    @signal newUrl(QUrl, str) emitted to open a URL in a new tab
+    """
+    openUrl = pyqtSignal(QUrl, str)
+    newUrl = pyqtSignal(QUrl, str)
+    
+    def __init__(self, parent=None, manager=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (QWidget
+        @param manager reference to the history manager object (HistoryManager)
+        """
+        super(HistoryDialog, self).__init__(parent)
+        self.setupUi(self)
+        self.setWindowFlags(Qt.Window)
+        
+        self.__historyManager = manager
+        if self.__historyManager is None:
+            import WebBrowser.WebBrowserWindow
+            self.__historyManager = \
+                WebBrowser.WebBrowserWindow.WebBrowserWindow.historyManager()
+        
+        self.__model = self.__historyManager.historyTreeModel()
+        self.__proxyModel = E5TreeSortFilterProxyModel(self)
+        self.__proxyModel.setSortRole(HistoryModel.DateTimeRole)
+        self.__proxyModel.setFilterKeyColumn(-1)
+        self.__proxyModel.setSourceModel(self.__model)
+        self.historyTree.setModel(self.__proxyModel)
+        self.historyTree.expandAll()
+        fm = QFontMetrics(self.font())
+        header = fm.width("m") * 40
+        self.historyTree.header().resizeSection(0, header)
+        self.historyTree.header().setStretchLastSection(True)
+        self.historyTree.setContextMenuPolicy(Qt.CustomContextMenu)
+        
+        self.historyTree.activated.connect(self.__activated)
+        self.historyTree.customContextMenuRequested.connect(
+            self.__customContextMenuRequested)
+        
+        self.searchEdit.textChanged.connect(
+            self.__proxyModel.setFilterFixedString)
+        self.removeButton.clicked.connect(self.historyTree.removeSelected)
+        self.removeAllButton.clicked.connect(self.__historyManager.clear)
+        
+        self.__proxyModel.modelReset.connect(self.__modelReset)
+    
+    def __modelReset(self):
+        """
+        Private slot handling a reset of the tree view's model.
+        """
+        self.historyTree.expandAll()
+    
+    def __customContextMenuRequested(self, pos):
+        """
+        Private slot to handle the context menu request for the bookmarks tree.
+        
+        @param pos position the context menu was requested (QPoint)
+        """
+        menu = QMenu()
+        idx = self.historyTree.indexAt(pos)
+        idx = idx.sibling(idx.row(), 0)
+        if idx.isValid() and not self.historyTree.model().hasChildren(idx):
+            menu.addAction(
+                self.tr("&Open"), self.__openHistoryInCurrentTab)
+            menu.addAction(
+                self.tr("Open in New &Tab"), self.__openHistoryInNewTab)
+            menu.addSeparator()
+            menu.addAction(self.tr("&Copy"), self.__copyHistory)
+        menu.addAction(self.tr("&Remove"), self.historyTree.removeSelected)
+        menu.exec_(QCursor.pos())
+    
+    def __activated(self, idx):
+        """
+        Private slot to handle the activation of an entry.
+        
+        @param idx reference to the entry index (QModelIndex)
+        """
+        self.__openHistory(
+            QApplication.keyboardModifiers() & Qt.ControlModifier)
+        
+    def __openHistoryInCurrentTab(self):
+        """
+        Private slot to open a history entry in the current browser tab.
+        """
+        self.__openHistory(False)
+    
+    def __openHistoryInNewTab(self):
+        """
+        Private slot to open a history entry in a new browser tab.
+        """
+        self.__openHistory(True)
+    
+    def __openHistory(self, newTab):
+        """
+        Private method to open a history entry.
+        
+        @param newTab flag indicating to open the history entry in a new tab
+            (boolean)
+        """
+        idx = self.historyTree.currentIndex()
+        if newTab:
+            self.newUrl.emit(
+                idx.data(HistoryModel.UrlRole),
+                idx.data(HistoryModel.TitleRole))
+        else:
+            self.openUrl.emit(
+                idx.data(HistoryModel.UrlRole),
+                idx.data(HistoryModel.TitleRole))
+    
+    def __copyHistory(self):
+        """
+        Private slot to copy a history entry's URL to the clipboard.
+        """
+        idx = self.historyTree.currentIndex()
+        if not idx.parent().isValid():
+            return
+        
+        url = idx.data(HistoryModel.UrlStringRole)
+        
+        clipboard = QApplication.clipboard()
+        clipboard.setText(url)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/History/HistoryDialog.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,176 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>HistoryDialog</class>
+ <widget class="QDialog" name="HistoryDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>750</width>
+    <height>450</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Manage History</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_2">
+     <item>
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <layout class="QHBoxLayout" name="horizontalLayout">
+       <property name="spacing">
+        <number>0</number>
+       </property>
+       <item>
+        <widget class="E5ClearableLineEdit" name="searchEdit">
+         <property name="toolTip">
+          <string>Enter search term for history entries</string>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="E5TreeView" name="historyTree">
+     <property name="alternatingRowColors">
+      <bool>true</bool>
+     </property>
+     <property name="selectionMode">
+      <enum>QAbstractItemView::ExtendedSelection</enum>
+     </property>
+     <property name="textElideMode">
+      <enum>Qt::ElideMiddle</enum>
+     </property>
+     <property name="uniformRowHeights">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_3">
+     <item>
+      <widget class="QPushButton" name="removeButton">
+       <property name="toolTip">
+        <string>Press to remove the selected entries</string>
+       </property>
+       <property name="text">
+        <string>&amp;Remove</string>
+       </property>
+       <property name="autoDefault">
+        <bool>false</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="removeAllButton">
+       <property name="toolTip">
+        <string>Press to remove all entries</string>
+       </property>
+       <property name="text">
+        <string>Remove &amp;All</string>
+       </property>
+       <property name="autoDefault">
+        <bool>false</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="spacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Close</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>E5ClearableLineEdit</class>
+   <extends>QLineEdit</extends>
+   <header>E5Gui/E5LineEdit.h</header>
+  </customwidget>
+  <customwidget>
+   <class>E5TreeView</class>
+   <extends>QTreeView</extends>
+   <header>E5Gui/E5TreeView.h</header>
+  </customwidget>
+ </customwidgets>
+ <tabstops>
+  <tabstop>searchEdit</tabstop>
+  <tabstop>historyTree</tabstop>
+  <tabstop>removeButton</tabstop>
+  <tabstop>removeAllButton</tabstop>
+  <tabstop>buttonBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>HistoryDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>252</x>
+     <y>445</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>HistoryDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>320</x>
+     <y>445</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/History/HistoryFilterModel.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,375 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the history filter model.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import Qt, QDateTime, QModelIndex, QAbstractProxyModel
+
+from .HistoryModel import HistoryModel
+
+
+class HistoryData(object):
+    """
+    Class storing some history data.
+    """
+    def __init__(self, offset, frequency=0):
+        """
+        Constructor
+        
+        @param offset tail offset (integer)
+        @param frequency frequency (integer)
+        """
+        self.tailOffset = offset
+        self.frequency = frequency
+    
+    def __eq__(self, other):
+        """
+        Special method implementing equality.
+        
+        @param other reference to the object to check against (HistoryData)
+        @return flag indicating equality (boolean)
+        """
+        return self.tailOffset == other.tailOffset and \
+            (self.frequency == -1 or other.frequency == -1 or
+             self.frequency == other.frequency)
+    
+    def __lt__(self, other):
+        """
+        Special method determining less relation.
+        
+        Note: Like the actual history entries the index mapping is sorted in
+        reverse order by offset
+        
+        @param other reference to the history data object to compare against
+            (HistoryEntry)
+        @return flag indicating less (boolean)
+        """
+        return self.tailOffset > other.tailOffset
+
+
+class HistoryFilterModel(QAbstractProxyModel):
+    """
+    Class implementing the history filter model.
+    """
+    FrequencyRole = HistoryModel.MaxRole + 1
+    MaxRole = FrequencyRole
+    
+    def __init__(self, sourceModel, parent=None):
+        """
+        Constructor
+        
+        @param sourceModel reference to the source model (QAbstractItemModel)
+        @param parent reference to the parent object (QObject)
+        """
+        super(HistoryFilterModel, self).__init__(parent)
+        
+        self.__loaded = False
+        self.__filteredRows = []
+        self.__historyDict = {}
+        self.__scaleTime = QDateTime()
+        
+        self.setSourceModel(sourceModel)
+    
+    def historyContains(self, url):
+        """
+        Public method to check the history for an entry.
+        
+        @param url URL to check for (string)
+        @return flag indicating success (boolean)
+        """
+        self.__load()
+        return url in self.__historyDict
+    
+    def historyLocation(self, url):
+        """
+        Public method to get the row number of an entry in the source model.
+        
+        @param url URL to check for (tring)
+        @return row number in the source model (integer)
+        """
+        self.__load()
+        if url not in self.__historyDict:
+            return 0
+        
+        return self.sourceModel().rowCount() - self.__historyDict[url]
+    
+    def data(self, index, role=Qt.DisplayRole):
+        """
+        Public method to get data from the model.
+        
+        @param index index of history entry to get data for (QModelIndex)
+        @param role data role (integer)
+        @return history entry data
+        """
+        if role == self.FrequencyRole and index.isValid():
+            return self.__filteredRows[index.row()].frequency
+        
+        return QAbstractProxyModel.data(self, index, role)
+    
+    def setSourceModel(self, sourceModel):
+        """
+        Public method to set the source model.
+        
+        @param sourceModel reference to the source model (QAbstractItemModel)
+        """
+        if self.sourceModel() is not None:
+            self.sourceModel().modelReset.disconnect(self.__sourceReset)
+            self.sourceModel().dataChanged.disconnect(self.__sourceDataChanged)
+            self.sourceModel().rowsInserted.disconnect(
+                self.__sourceRowsInserted)
+            self.sourceModel().rowsRemoved.disconnect(self.__sourceRowsRemoved)
+        
+        super(HistoryFilterModel, self).setSourceModel(sourceModel)
+        
+        if self.sourceModel() is not None:
+            self.__loaded = False
+            self.sourceModel().modelReset.connect(self.__sourceReset)
+            self.sourceModel().dataChanged.connect(self.__sourceDataChanged)
+            self.sourceModel().rowsInserted.connect(self.__sourceRowsInserted)
+            self.sourceModel().rowsRemoved.connect(self.__sourceRowsRemoved)
+    
+    def __sourceDataChanged(self, topLeft, bottomRight):
+        """
+        Private slot to handle the change of data of the source model.
+        
+        @param topLeft index of top left data element (QModelIndex)
+        @param bottomRight index of bottom right data element (QModelIndex)
+        """
+        self.dataChanged.emit(
+            self.mapFromSource(topLeft), self.mapFromSource(bottomRight))
+    
+    def headerData(self, section, orientation, role=Qt.DisplayRole):
+        """
+        Public method to get the header data.
+        
+        @param section section number (integer)
+        @param orientation header orientation (Qt.Orientation)
+        @param role data role (integer)
+        @return header data
+        """
+        return self.sourceModel().headerData(section, orientation, role)
+    
+    def recalculateFrequencies(self):
+        """
+        Public method to recalculate the frequencies.
+        """
+        self.__sourceReset()
+    
+    def __sourceReset(self):
+        """
+        Private slot to handle a reset of the source model.
+        """
+        self.beginResetModel()
+        self.__loaded = False
+        self.endResetModel()
+    
+    def rowCount(self, parent=QModelIndex()):
+        """
+        Public method to determine the number of rows.
+        
+        @param parent index of parent (QModelIndex)
+        @return number of rows (integer)
+        """
+        self.__load()
+        if parent.isValid():
+            return 0
+        return len(self.__historyDict)
+    
+    def columnCount(self, parent=QModelIndex()):
+        """
+        Public method to get the number of columns.
+        
+        @param parent index of parent (QModelIndex)
+        @return number of columns (integer)
+        """
+        return self.sourceModel().columnCount(self.mapToSource(parent))
+    
+    def mapToSource(self, proxyIndex):
+        """
+        Public method to map an index to the source model index.
+        
+        @param proxyIndex reference to a proxy model index (QModelIndex)
+        @return source model index (QModelIndex)
+        """
+        self.__load()
+        sourceRow = self.sourceModel().rowCount() - proxyIndex.internalId()
+        return self.sourceModel().index(sourceRow, proxyIndex.column())
+    
+    def mapFromSource(self, sourceIndex):
+        """
+        Public method to map an index to the proxy model index.
+        
+        @param sourceIndex reference to a source model index (QModelIndex)
+        @return proxy model index (QModelIndex)
+        """
+        self.__load()
+        url = sourceIndex.data(HistoryModel.UrlStringRole)
+        if url not in self.__historyDict:
+            return QModelIndex()
+        
+        sourceOffset = self.sourceModel().rowCount() - sourceIndex.row()
+        
+        try:
+            row = self.__filteredRows.index(HistoryData(sourceOffset, -1))
+        except ValueError:
+            return QModelIndex()
+        
+        return self.createIndex(row, sourceIndex.column(), sourceOffset)
+    
+    def index(self, row, column, parent=QModelIndex()):
+        """
+        Public method to create an index.
+        
+        @param row row number for the index (integer)
+        @param column column number for the index (integer)
+        @param parent index of the parent item (QModelIndex)
+        @return requested index (QModelIndex)
+        """
+        self.__load()
+        if row < 0 or row >= self.rowCount(parent) or \
+           column < 0 or column >= self.columnCount(parent):
+            return QModelIndex()
+        
+        return self.createIndex(row, column,
+                                self.__filteredRows[row].tailOffset)
+
+    def parent(self, index):
+        """
+        Public method to get the parent index.
+        
+        @param index index of item to get parent (QModelIndex)
+        @return index of parent (QModelIndex)
+        """
+        return QModelIndex()
+    
+    def __load(self):
+        """
+        Private method to load the model data.
+        """
+        if self.__loaded:
+            return
+        
+        self.__filteredRows = []
+        self.__historyDict = {}
+        self.__scaleTime = QDateTime.currentDateTime()
+        
+        for sourceRow in range(self.sourceModel().rowCount()):
+            idx = self.sourceModel().index(sourceRow, 0)
+            url = idx.data(HistoryModel.UrlStringRole)
+            if url not in self.__historyDict:
+                sourceOffset = self.sourceModel().rowCount() - sourceRow
+                self.__filteredRows.append(
+                    HistoryData(sourceOffset, self.__frequencyScore(idx)))
+                self.__historyDict[url] = sourceOffset
+            else:
+                # the url is known already, so just update the frequency score
+                row = self.__filteredRows.index(
+                    HistoryData(self.__historyDict[url], -1))
+                self.__filteredRows[row].frequency += \
+                    self.__frequencyScore(idx)
+        
+        self.__loaded = True
+    
+    def __sourceRowsInserted(self, parent, start, end):
+        """
+        Private slot to handle the insertion of data in the source model.
+        
+        @param parent reference to the parent index (QModelIndex)
+        @param start start row (integer)
+        @param end end row (integer)
+        """
+        if start == end and start == 0:
+            if not self.__loaded:
+                return
+            
+            idx = self.sourceModel().index(start, 0, parent)
+            url = idx.data(HistoryModel.UrlStringRole)
+            currentFrequency = 0
+            if url in self.__historyDict:
+                row = self.__filteredRows.index(
+                    HistoryData(self.__historyDict[url], -1))
+                currentFrequency = self.__filteredRows[row].frequency
+                self.beginRemoveRows(QModelIndex(), row, row)
+                del self.__filteredRows[row]
+                del self.__historyDict[url]
+                self.endRemoveRows()
+            
+            self.beginInsertRows(QModelIndex(), 0, 0)
+            self.__filteredRows.insert(
+                0, HistoryData(
+                    self.sourceModel().rowCount(),
+                    self.__frequencyScore(idx) + currentFrequency))
+            self.__historyDict[url] = self.sourceModel().rowCount()
+            self.endInsertRows()
+    
+    def __sourceRowsRemoved(self, parent, start, end):
+        """
+        Private slot to handle the removal of data in the source model.
+        
+        @param parent reference to the parent index (QModelIndex)
+        @param start start row (integer)
+        @param end end row (integer)
+        """
+        self.__sourceReset()
+    
+    def removeRows(self, row, count, parent=QModelIndex()):
+        """
+        Public method to remove entries from the model.
+        
+        @param row row of the first entry to remove (integer)
+        @param count number of entries to remove (integer)
+        @param parent index of the parent entry (QModelIndex)
+        @return flag indicating successful removal (boolean)
+        """
+        if row < 0 or \
+           count <= 0 or \
+           row + count > self.rowCount(parent) or \
+           parent.isValid():
+            return False
+        
+        lastRow = row + count - 1
+        self.sourceModel().rowsRemoved.disconnect(self.__sourceRowsRemoved)
+        self.beginRemoveRows(parent, row, lastRow)
+        oldCount = self.rowCount()
+        start = self.sourceModel().rowCount() - \
+            self.__filteredRows[row].tailOffset
+        end = self.sourceModel().rowCount() - \
+            self.__filteredRows[lastRow].tailOffset
+        self.sourceModel().removeRows(start, end - start + 1)
+        self.endRemoveRows()
+        self.sourceModel().rowsRemoved.connect(self.__sourceRowsRemoved)
+        self.__loaded = False
+        if oldCount - count != self.rowCount():
+            self.beginResetModel()
+            self.endResetModel()
+        return True
+    
+    def __frequencyScore(self, sourceIndex):
+        """
+        Private method to calculate the frequency score.
+        
+        @param sourceIndex index of the source model (QModelIndex)
+        @return frequency score (integer)
+        """
+        loadTime = \
+            self.sourceModel().data(sourceIndex, HistoryModel.DateTimeRole)
+        days = loadTime.daysTo(self.__scaleTime)
+        
+        if days <= 1:
+            return 100
+        elif days < 8:      # within the last week
+            return 90
+        elif days < 15:     # within the last two weeks
+            return 70
+        elif days < 31:     # within the last month
+            return 50
+        elif days < 91:     # within the last 3 months
+            return 30
+        else:
+            return 10
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/History/HistoryManager.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,506 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the history manager.
+"""
+
+from __future__ import unicode_literals
+
+import os
+
+from PyQt5.QtCore import pyqtSignal, pyqtSlot, QFileInfo, QDateTime, QDate, \
+    QTime, QUrl, QTimer, QFile, QIODevice, QByteArray, QDataStream, \
+    QTemporaryFile, QObject
+
+from E5Gui import E5MessageBox
+
+from Utilities.AutoSaver import AutoSaver
+import Utilities
+import Preferences
+
+HISTORY_VERSION = 42
+
+
+class HistoryEntry(object):
+    """
+    Class implementing a history entry.
+    """
+    def __init__(self, url=None, dateTime=None, title=None):
+        """
+        Constructor
+        
+        @param url URL of the history entry (string)
+        @param dateTime date and time this entry was created (QDateTime)
+        @param title title string for the history entry (string)
+        """
+        self.url = url and url or ""
+        self.dateTime = dateTime and dateTime or QDateTime()
+        self.title = title and title or ""
+    
+    def __eq__(self, other):
+        """
+        Special method determining equality.
+        
+        @param other reference to the history entry to compare against
+            (HistoryEntry)
+        @return flag indicating equality (boolean)
+        """
+        return other.title == self.title and \
+            other.url == self.url and \
+            other.dateTime == self.dateTime
+    
+    def __lt__(self, other):
+        """
+        Special method determining less relation.
+        
+        Note: History is sorted in reverse order by date and time
+        
+        @param other reference to the history entry to compare against
+            (HistoryEntry)
+        @return flag indicating less (boolean)
+        """
+        return self.dateTime > other.dateTime
+    
+    def userTitle(self):
+        """
+        Public method to get the title of the history entry.
+        
+        @return title of the entry (string)
+        """
+        if not self.title:
+            page = QFileInfo(QUrl(self.url).path()).fileName()
+            if page:
+                return page
+            return self.url
+        return self.title
+
+
+# TODO: Enhancement: Only one entry per URL for latest visit and add no. of visits
+class HistoryManager(QObject):
+    """
+    Class implementing the history manager.
+    
+    @signal historyCleared() emitted after the history has been cleared
+    @signal historyReset() emitted after the history has been reset
+    @signal entryAdded(HistoryEntry) emitted after a history entry has been
+        added
+    @signal entryRemoved(HistoryEntry) emitted after a history entry has been
+        removed
+    @signal entryUpdated(int) emitted after a history entry has been updated
+    @signal historySaved() emitted after the history was saved
+    """
+    historyCleared = pyqtSignal()
+    historyReset = pyqtSignal()
+    entryAdded = pyqtSignal(HistoryEntry)
+    entryRemoved = pyqtSignal(HistoryEntry)
+    entryUpdated = pyqtSignal(int)
+    historySaved = pyqtSignal()
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object (QObject)
+        """
+        super(HistoryManager, self).__init__(parent)
+        
+        self.__saveTimer = AutoSaver(self, self.save)
+        self.__daysToExpire = Preferences.getWebBrowser("HistoryLimit")
+        self.__history = []
+        self.__lastSavedUrl = ""
+        
+        self.__expiredTimer = QTimer(self)
+        self.__expiredTimer.setSingleShot(True)
+        self.__expiredTimer.timeout.connect(self.__checkForExpired)
+        
+        self.__frequencyTimer = QTimer(self)
+        self.__frequencyTimer.setSingleShot(True)
+        self.__frequencyTimer.timeout.connect(self.__refreshFrequencies)
+        
+        self.entryAdded.connect(self.__saveTimer.changeOccurred)
+        self.entryRemoved.connect(self.__saveTimer.changeOccurred)
+        
+        self.__load()
+        
+        from .HistoryModel import HistoryModel
+        from .HistoryFilterModel import HistoryFilterModel
+        from .HistoryTreeModel import HistoryTreeModel
+        
+        self.__historyModel = HistoryModel(self, self)
+        self.__historyFilterModel = \
+            HistoryFilterModel(self.__historyModel, self)
+        self.__historyTreeModel = \
+            HistoryTreeModel(self.__historyFilterModel, self)
+        
+        self.__startFrequencyTimer()
+    
+    def close(self):
+        """
+        Public method to close the history manager.
+        """
+        # remove history items on application exit
+        if self.__daysToExpire == -2:
+            self.clear()
+        self.__saveTimer.saveIfNeccessary()
+    
+    def history(self):
+        """
+        Public method to return the history.
+        
+        @return reference to the list of history entries (list of HistoryEntry)
+        """
+        return self.__history[:]
+    
+    def setHistory(self, history, loadedAndSorted=False):
+        """
+        Public method to set a new history.
+        
+        @param history reference to the list of history entries to be set
+            (list of HistoryEntry)
+        @param loadedAndSorted flag indicating that the list is sorted
+            (boolean)
+        """
+        self.__history = history[:]
+        if not loadedAndSorted:
+            self.__history.sort()
+        
+        self.__checkForExpired()
+        
+        if loadedAndSorted:
+            try:
+                self.__lastSavedUrl = self.__history[0].url
+            except IndexError:
+                self.__lastSavedUrl = ""
+        else:
+            self.__lastSavedUrl = ""
+            self.__saveTimer.changeOccurred()
+        self.historyReset.emit()
+    
+    def addHistoryEntry(self, view):
+        """
+        Public method to add a history entry.
+        
+        @param view reference to the view to add an entry for
+        @type WebBrowserView
+        """
+        import WebBrowser.WebBrowserWindow
+        if WebBrowser.WebBrowserWindow.WebBrowserWindow.isPrivate():
+            return
+        
+        url = view.url()
+        title = view.title()
+        
+        if url.scheme() not in ["eric", "about", "data"]:
+            if url.password():
+                # don't save the password in the history
+                url.setPassword("")
+            if url.host():
+                url.setHost(url.host().lower())
+            itm = HistoryEntry(url.toString(),
+                               QDateTime.currentDateTime(),
+                               title)
+            self.__history.insert(0, itm)
+            self.entryAdded.emit(itm)
+            if len(self.__history) == 1:
+                self.__checkForExpired()
+    
+    def updateHistoryEntry(self, url, title):
+        """
+        Public method to update a history entry.
+        
+        @param url URL of the entry to update (string)
+        @param title title of the entry to update (string)
+        """
+        cleanurl = QUrl(url)
+        if cleanurl.scheme() not in ["eric", "about"]:
+            for index in range(len(self.__history)):
+                if url == self.__history[index].url:
+                    self.__history[index].title = title
+                    self.__saveTimer.changeOccurred()
+                    if not self.__lastSavedUrl:
+                        self.__lastSavedUrl = self.__history[index].url
+                    self.entryUpdated.emit(index)
+                    break
+    
+    def removeHistoryEntry(self, url, title=""):
+        """
+        Public method to remove a history entry.
+        
+        @param url URL of the entry to remove (QUrl)
+        @param title title of the entry to remove (string)
+        """
+        for index in range(len(self.__history)):
+            if url == QUrl(self.__history[index].url) and \
+               (not title or title == self.__history[index].title):
+                itm = self.__history[index]
+                self.__lastSavedUrl = ""
+                self.__history.remove(itm)
+                self.entryRemoved.emit(itm)
+                break
+    
+    def historyModel(self):
+        """
+        Public method to get a reference to the history model.
+        
+        @return reference to the history model (HistoryModel)
+        """
+        return self.__historyModel
+    
+    def historyFilterModel(self):
+        """
+        Public method to get a reference to the history filter model.
+        
+        @return reference to the history filter model (HistoryFilterModel)
+        """
+        return self.__historyFilterModel
+    
+    def historyTreeModel(self):
+        """
+        Public method to get a reference to the history tree model.
+        
+        @return reference to the history tree model (HistoryTreeModel)
+        """
+        return self.__historyTreeModel
+    
+    def __checkForExpired(self):
+        """
+        Private slot to check entries for expiration.
+        """
+        if self.__daysToExpire < 0 or len(self.__history) == 0:
+            return
+        
+        now = QDateTime.currentDateTime()
+        nextTimeout = 0
+        
+        while self.__history:
+            checkForExpired = QDateTime(self.__history[-1].dateTime)
+            checkForExpired.setDate(
+                checkForExpired.date().addDays(self.__daysToExpire))
+            if now.daysTo(checkForExpired) > 7:
+                nextTimeout = 7 * 86400
+            else:
+                nextTimeout = now.secsTo(checkForExpired)
+            if nextTimeout > 0:
+                break
+            
+            itm = self.__history.pop(-1)
+            self.__lastSavedUrl = ""
+            self.entryRemoved.emit(itm)
+        self.__saveTimer.saveIfNeccessary()
+        
+        if nextTimeout > 0:
+            self.__expiredTimer.start(nextTimeout * 1000)
+    
+    def daysToExpire(self):
+        """
+        Public method to get the days for entry expiration.
+        
+        @return days for entry expiration (integer)
+        """
+        return self.__daysToExpire
+    
+    def setDaysToExpire(self, limit):
+        """
+        Public method to set the days for entry expiration.
+        
+        @param limit days for entry expiration (integer)
+        """
+        if self.__daysToExpire == limit:
+            return
+        
+        self.__daysToExpire = limit
+        self.__checkForExpired()
+        self.__saveTimer.changeOccurred()
+    
+    def preferencesChanged(self):
+        """
+        Public method to indicate a change of preferences.
+        """
+        self.setDaysToExpire(Preferences.getWebBrowser("HistoryLimit"))
+    
+    @pyqtSlot()
+    def clear(self, period=0):
+        """
+        Public slot to clear the complete history.
+        
+        @param period history period in milliseconds to be cleared (integer)
+        """
+        if period == 0:
+            self.__history = []
+            self.historyReset.emit()
+        else:
+            breakMS = QDateTime.currentMSecsSinceEpoch() - period
+            while self.__history and \
+                (QDateTime(self.__history[0].dateTime).toMSecsSinceEpoch() >
+                 breakMS):
+                itm = self.__history.pop(0)
+                self.entryRemoved.emit(itm)
+        self.__lastSavedUrl = ""
+        self.__saveTimer.changeOccurred()
+        self.__saveTimer.saveIfNeccessary()
+        self.historyCleared.emit()
+    
+    def getFileName(self):
+        """
+        Public method to get the file name of the history file.
+        
+        @return name of the history file (string)
+        """
+        return os.path.join(Utilities.getConfigDir(), "web_browser", "history")
+    
+    def reload(self):
+        """
+        Public method to reload the history.
+        """
+        self.__load()
+    
+    def __load(self):
+        """
+        Private method to load the saved history entries from disk.
+        """
+        historyFile = QFile(self.getFileName())
+        if not historyFile.exists():
+            return
+        if not historyFile.open(QIODevice.ReadOnly):
+            E5MessageBox.warning(
+                None,
+                self.tr("Loading History"),
+                self.tr(
+                    """<p>Unable to open history file <b>{0}</b>.<br/>"""
+                    """Reason: {1}</p>""")
+                .format(historyFile.fileName, historyFile.errorString()))
+            return
+        
+        history = []
+        
+        # double check, that the history file is sorted as it is read
+        needToSort = False
+        lastInsertedItem = HistoryEntry()
+        data = QByteArray(historyFile.readAll())
+        stream = QDataStream(data, QIODevice.ReadOnly)
+        stream.setVersion(QDataStream.Qt_4_6)
+        while not stream.atEnd():
+            ver = stream.readUInt32()
+            if ver != HISTORY_VERSION:
+                continue
+            itm = HistoryEntry()
+            itm.url = Utilities.readStringFromStream(stream)
+            stream >> itm.dateTime
+            itm.title = Utilities.readStringFromStream(stream)
+            
+            if not itm.dateTime.isValid():
+                continue
+            
+            if itm == lastInsertedItem:
+                if not lastInsertedItem.title and len(history) > 0:
+                    history[0].title = itm.title
+                continue
+            
+            if not needToSort and history and lastInsertedItem < itm:
+                needToSort = True
+            
+            history.insert(0, itm)
+            lastInsertedItem = itm
+        historyFile.close()
+        
+        if needToSort:
+            history.sort()
+        
+        self.setHistory(history, True)
+        
+        # if the history had to be sorted, rewrite the history sorted
+        if needToSort:
+            self.__lastSavedUrl = ""
+            self.__saveTimer.changeOccurred()
+    
+    def save(self):
+        """
+        Public slot to save the history entries to disk.
+        """
+        historyFile = QFile(self.getFileName())
+        if not historyFile.exists():
+            self.__lastSavedUrl = ""
+        
+        saveAll = self.__lastSavedUrl == ""
+        first = len(self.__history) - 1
+        if not saveAll:
+            # find the first one to save
+            for index in range(len(self.__history)):
+                if self.__history[index].url == self.__lastSavedUrl:
+                    first = index - 1
+                    break
+        if first == len(self.__history) - 1:
+            saveAll = True
+        
+        if saveAll:
+            # use a temporary file when saving everything
+            f = QTemporaryFile()
+            f.setAutoRemove(False)
+            opened = f.open()
+        else:
+            f = historyFile
+            opened = f.open(QIODevice.Append)
+        
+        if not opened:
+            E5MessageBox.warning(
+                None,
+                self.tr("Saving History"),
+                self.tr(
+                    """<p>Unable to open history file <b>{0}</b>.<br/>"""
+                    """Reason: {1}</p>""")
+                .format(f.fileName(), f.errorString()))
+            return
+        
+        for index in range(first, -1, -1):
+            data = QByteArray()
+            stream = QDataStream(data, QIODevice.WriteOnly)
+            stream.setVersion(QDataStream.Qt_4_6)
+            itm = self.__history[index]
+            stream.writeUInt32(HISTORY_VERSION)
+            stream.writeString(itm.url.encode("utf-8"))
+            stream << itm.dateTime
+            stream.writeString(itm.title.encode('utf-8'))
+            f.write(data)
+        
+        f.close()
+        if saveAll:
+            if historyFile.exists() and not historyFile.remove():
+                E5MessageBox.warning(
+                    None,
+                    self.tr("Saving History"),
+                    self.tr(
+                        """<p>Error removing old history file <b>{0}</b>."""
+                        """<br/>Reason: {1}</p>""")
+                    .format(historyFile.fileName(),
+                            historyFile.errorString()))
+            if not f.copy(historyFile.fileName()):
+                E5MessageBox.warning(
+                    None,
+                    self.tr("Saving History"),
+                    self.tr(
+                        """<p>Error moving new history file over old one """
+                        """(<b>{0}</b>).<br/>Reason: {1}</p>""")
+                    .format(historyFile.fileName(), f.errorString()))
+        self.historySaved.emit()
+        try:
+            self.__lastSavedUrl = self.__history[0].url
+        except IndexError:
+            self.__lastSavedUrl = ""
+    
+    def __refreshFrequencies(self):
+        """
+        Private slot to recalculate the refresh frequencies.
+        """
+        self.__historyFilterModel.recalculateFrequencies()
+        self.__startFrequencyTimer()
+    
+    def __startFrequencyTimer(self):
+        """
+        Private method to start the timer to recalculate the frequencies.
+        """
+        tomorrow = QDateTime(QDate.currentDate().addDays(1), QTime(3, 0))
+        self.__frequencyTimer.start(
+            QDateTime.currentDateTime().secsTo(tomorrow) * 1000)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/History/HistoryMenu.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,476 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the history menu.
+"""
+
+from __future__ import unicode_literals
+
+import sys
+
+from PyQt5.QtCore import pyqtSignal, Qt, QMimeData, QUrl, QModelIndex, \
+    QSortFilterProxyModel, QAbstractProxyModel
+from PyQt5.QtWidgets import QMenu
+
+from E5Gui.E5ModelMenu import E5ModelMenu
+from E5Gui import E5MessageBox
+
+from .HistoryModel import HistoryModel
+
+import UI.PixmapCache
+
+
+class HistoryMenuModel(QAbstractProxyModel):
+    """
+    Class implementing a model for the history menu.
+    
+    It maps the first bunch of items of the source model to the root.
+    """
+    MOVEDROWS = 15
+    
+    def __init__(self, sourceModel, parent=None):
+        """
+        Constructor
+        
+        @param sourceModel reference to the source model (QAbstractItemModel)
+        @param parent reference to the parent object (QObject)
+        """
+        super(HistoryMenuModel, self).__init__(parent)
+        
+        self.__treeModel = sourceModel
+        
+        self.setSourceModel(sourceModel)
+    
+    def bumpedRows(self):
+        """
+        Public method to determine the number of rows moved to the root.
+        
+        @return number of rows moved to the root (integer)
+        """
+        first = self.__treeModel.index(0, 0)
+        if not first.isValid():
+            return 0
+        return min(self.__treeModel.rowCount(first), self.MOVEDROWS)
+    
+    def columnCount(self, parent=QModelIndex()):
+        """
+        Public method to get the number of columns.
+        
+        @param parent index of parent (QModelIndex)
+        @return number of columns (integer)
+        """
+        return self.__treeModel.columnCount(self.mapToSource(parent))
+    
+    def rowCount(self, parent=QModelIndex()):
+        """
+        Public method to determine the number of rows.
+        
+        @param parent index of parent (QModelIndex)
+        @return number of rows (integer)
+        """
+        if parent.column() > 0:
+            return 0
+        
+        if not parent.isValid():
+            folders = self.sourceModel().rowCount()
+            bumpedItems = self.bumpedRows()
+            if bumpedItems <= self.MOVEDROWS and \
+                    bumpedItems == self.sourceModel().rowCount(
+                        self.sourceModel().index(0, 0)):
+                folders -= 1
+            return bumpedItems + folders
+        
+        if parent.internalId() == sys.maxsize:
+            if parent.row() < self.bumpedRows():
+                return 0
+        
+        idx = self.mapToSource(parent)
+        defaultCount = self.sourceModel().rowCount(idx)
+        if idx == self.sourceModel().index(0, 0):
+            return defaultCount - self.bumpedRows()
+        
+        return defaultCount
+    
+    def mapFromSource(self, sourceIndex):
+        """
+        Public method to map an index to the proxy model index.
+        
+        @param sourceIndex reference to a source model index (QModelIndex)
+        @return proxy model index (QModelIndex)
+        """
+        sourceRow = self.__treeModel.mapToSource(sourceIndex).row()
+        return self.createIndex(
+            sourceIndex.row(), sourceIndex.column(), sourceRow)
+    
+    def mapToSource(self, proxyIndex):
+        """
+        Public method to map an index to the source model index.
+        
+        @param proxyIndex reference to a proxy model index (QModelIndex)
+        @return source model index (QModelIndex)
+        """
+        if not proxyIndex.isValid():
+            return QModelIndex()
+        
+        if proxyIndex.internalId() == sys.maxsize:
+            bumpedItems = self.bumpedRows()
+            if proxyIndex.row() < bumpedItems:
+                return self.__treeModel.index(
+                    proxyIndex.row(), proxyIndex.column(),
+                    self.__treeModel.index(0, 0))
+            if bumpedItems <= self.MOVEDROWS and \
+                    bumpedItems == self.sourceModel().rowCount(
+                        self.__treeModel.index(0, 0)):
+                bumpedItems -= 1
+            return self.__treeModel.index(proxyIndex.row() - bumpedItems,
+                                          proxyIndex.column())
+        
+        historyIndex = self.__treeModel.sourceModel()\
+            .index(proxyIndex.internalId(), proxyIndex.column())
+        treeIndex = self.__treeModel.mapFromSource(historyIndex)
+        return treeIndex
+    
+    def index(self, row, column, parent=QModelIndex()):
+        """
+        Public method to create an index.
+        
+        @param row row number for the index (integer)
+        @param column column number for the index (integer)
+        @param parent index of the parent item (QModelIndex)
+        @return requested index (QModelIndex)
+        """
+        if row < 0 or \
+           column < 0 or \
+           column >= self.columnCount(parent) or \
+           parent.column() > 0:
+            return QModelIndex()
+        
+        if not parent.isValid():
+            return self.createIndex(row, column, sys.maxsize)
+        
+        treeIndexParent = self.mapToSource(parent)
+        
+        bumpedItems = 0
+        if treeIndexParent == self.sourceModel().index(0, 0):
+            bumpedItems = self.bumpedRows()
+        treeIndex = self.__treeModel.index(
+            row + bumpedItems, column, treeIndexParent)
+        historyIndex = self.__treeModel.mapToSource(treeIndex)
+        historyRow = historyIndex.row()
+        if historyRow == -1:
+            historyRow = treeIndex.row()
+        return self.createIndex(row, column, historyRow)
+
+    def parent(self, index):
+        """
+        Public method to get the parent index.
+        
+        @param index index of item to get parent (QModelIndex)
+        @return index of parent (QModelIndex)
+        """
+        offset = index.internalId()
+        if offset == sys.maxsize or not index.isValid():
+            return QModelIndex()
+        
+        historyIndex = self.__treeModel.sourceModel().index(
+            index.internalId(), 0)
+        treeIndex = self.__treeModel.mapFromSource(historyIndex)
+        treeIndexParent = treeIndex.parent()
+        
+        sourceRow = self.sourceModel().mapToSource(treeIndexParent).row()
+        bumpedItems = self.bumpedRows()
+        if bumpedItems <= self.MOVEDROWS and \
+                bumpedItems == self.sourceModel().rowCount(
+                    self.sourceModel().index(0, 0)):
+            bumpedItems -= 1
+        
+        return self.createIndex(bumpedItems + treeIndexParent.row(),
+                                treeIndexParent.column(),
+                                sourceRow)
+    
+    def mimeData(self, indexes):
+        """
+        Public method to return the mime data.
+        
+        @param indexes list of indexes (QModelIndexList)
+        @return mime data (QMimeData)
+        """
+        urls = []
+        for index in indexes:
+            url = index.data(HistoryModel.UrlRole)
+            urls.append(url)
+        
+        mdata = QMimeData()
+        mdata.setUrls(urls)
+        return mdata
+
+
+class HistoryMostVisitedMenuModel(QSortFilterProxyModel):
+    """
+    Class implementing a model to show the most visited history entries.
+    """
+    def __init__(self, sourceModel, parent=None):
+        """
+        Constructor
+        
+        @param sourceModel reference to the source model (QAbstractItemModel)
+        @param parent reference to the parent object (QObject)
+        """
+        super(HistoryMostVisitedMenuModel, self).__init__(parent)
+        
+        self.setDynamicSortFilter(True)
+        self.setSourceModel(sourceModel)
+    
+    def lessThan(self, left, right):
+        """
+        Public method used to sort the displayed items.
+        
+        @param left index of left item (QModelIndex)
+        @param right index of right item (QModelIndex)
+        @return true, if left is less than right (boolean)
+        """
+        from .HistoryFilterModel import HistoryFilterModel
+        frequency_L = \
+            self.sourceModel().data(left, HistoryFilterModel.FrequencyRole)
+        dateTime_L = \
+            self.sourceModel().data(left, HistoryModel.DateTimeRole)
+        frequency_R = \
+            self.sourceModel().data(right, HistoryFilterModel.FrequencyRole)
+        dateTime_R = \
+            self.sourceModel().data(right, HistoryModel.DateTimeRole)
+        
+        # Sort results in descending frequency-derived score. If frequencies
+        # are equal, sort on most recently viewed
+        if frequency_R == frequency_L:
+            return dateTime_R < dateTime_L
+        
+        return frequency_R < frequency_L
+
+
+class HistoryMenu(E5ModelMenu):
+    """
+    Class implementing the history menu.
+    
+    @signal openUrl(QUrl, str) emitted to open a URL in the current tab
+    @signal newUrl(QUrl, str) emitted to open a URL in a new tab
+    """
+    openUrl = pyqtSignal(QUrl, str)
+    newUrl = pyqtSignal(QUrl, str)
+    
+    def __init__(self, parent=None, tabWidget=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (QWidget)
+        @param tabWidget reference to the tab widget managing the browser
+            tabs (HelpTabWidget
+        """
+        E5ModelMenu.__init__(self, parent)
+        
+        self.__tabWidget = tabWidget
+        
+        self.__historyManager = None
+        self.__historyMenuModel = None
+        self.__initialActions = []
+        self.__mostVisitedMenu = None
+        
+        self.__closedTabsMenu = QMenu(self.tr("Closed Tabs"))
+        self.__closedTabsMenu.aboutToShow.connect(
+            self.__aboutToShowClosedTabsMenu)
+        self.__tabWidget.closedTabsManager().closedTabAvailable.connect(
+            self.__closedTabAvailable)
+        
+        self.setMaxRows(7)
+        
+        self.activated.connect(self.__activated)
+        self.setStatusBarTextRole(HistoryModel.UrlStringRole)
+    
+    def __activated(self, idx):
+        """
+        Private slot handling the activated signal.
+        
+        @param idx index of the activated item (QModelIndex)
+        """
+        if self._keyboardModifiers & Qt.ControlModifier:
+            self.newUrl.emit(
+                idx.data(HistoryModel.UrlRole),
+                idx.data(HistoryModel.TitleRole))
+        else:
+            self.openUrl.emit(
+                idx.data(HistoryModel.UrlRole),
+                idx.data(HistoryModel.TitleRole))
+    
+    def prePopulated(self):
+        """
+        Public method to add any actions before the tree.
+       
+        @return flag indicating if any actions were added (boolean)
+        """
+        if self.__historyManager is None:
+            import WebBrowser.WebBrowserWindow
+            self.__historyManager = \
+                WebBrowser.WebBrowserWindow.WebBrowserWindow.historyManager()
+            self.__historyMenuModel = HistoryMenuModel(
+                self.__historyManager.historyTreeModel(), self)
+            self.setModel(self.__historyMenuModel)
+        
+        # initial actions
+        for act in self.__initialActions:
+            self.addAction(act)
+        if len(self.__initialActions) != 0:
+            self.addSeparator()
+        self.setFirstSeparator(self.__historyMenuModel.bumpedRows())
+        
+        return False
+    
+    def postPopulated(self):
+        """
+        Public method to add any actions after the tree.
+        """
+        if len(self.__historyManager.history()) > 0:
+            self.addSeparator()
+        
+        if self.__mostVisitedMenu is None:
+            self.__mostVisitedMenu = HistoryMostVisitedMenu(10, self)
+            self.__mostVisitedMenu.setTitle(self.tr("Most Visited"))
+            self.__mostVisitedMenu.openUrl.connect(self.openUrl)
+            self.__mostVisitedMenu.newUrl.connect(self.newUrl)
+        self.addMenu(self.__mostVisitedMenu)
+        act = self.addMenu(self.__closedTabsMenu)
+        act.setIcon(UI.PixmapCache.getIcon("trash.png"))
+        act.setEnabled(self.__tabWidget.canRestoreClosedTab())
+        self.addSeparator()
+        
+        act = self.addAction(UI.PixmapCache.getIcon("history.png"),
+                             self.tr("Show All History..."))
+        act.triggered.connect(self.__showHistoryDialog)
+        act = self.addAction(UI.PixmapCache.getIcon("historyClear.png"),
+                             self.tr("Clear History..."))
+        act.triggered.connect(self.__clearHistoryDialog)
+    
+    def setInitialActions(self, actions):
+        """
+        Public method to set the list of actions that should appear first in
+        the menu.
+        
+        @param actions list of initial actions (list of QAction)
+        """
+        self.__initialActions = actions[:]
+        for act in self.__initialActions:
+            self.addAction(act)
+    
+    def __showHistoryDialog(self):
+        """
+        Private slot to show the history dialog.
+        """
+        from .HistoryDialog import HistoryDialog
+        dlg = HistoryDialog(self)
+        dlg.newUrl.connect(self.newUrl)
+        dlg.openUrl.connect(self.openUrl)
+        dlg.show()
+    
+    def __clearHistoryDialog(self):
+        """
+        Private slot to clear the history.
+        """
+        if self.__historyManager is not None and E5MessageBox.yesNo(
+                self,
+                self.tr("Clear History"),
+                self.tr("""Do you want to clear the history?""")):
+            self.__historyManager.clear()
+            self.__tabWidget.clearClosedTabsList()
+    
+    def __aboutToShowClosedTabsMenu(self):
+        """
+        Private slot to populate the closed tabs menu.
+        """
+        fm = self.__closedTabsMenu.fontMetrics()
+        maxWidth = fm.width('m') * 40
+        
+        import WebBrowser.WebBrowserWindow
+        self.__closedTabsMenu.clear()
+        index = 0
+        for tab in self.__tabWidget.closedTabsManager().allClosedTabs():
+            title = fm.elidedText(tab.title, Qt.ElideRight, maxWidth)
+            self.__closedTabsMenu.addAction(
+                WebBrowser.WebBrowserWindow.WebBrowserWindow.icon(tab.url),
+                title,
+                self.__tabWidget.restoreClosedTab).setData(index)
+            index += 1
+        self.__closedTabsMenu.addSeparator()
+        self.__closedTabsMenu.addAction(
+            self.tr("Restore All Closed Tabs"),
+            self.__tabWidget.restoreAllClosedTabs)
+        self.__closedTabsMenu.addAction(
+            self.tr("Clear List"),
+            self.__tabWidget.clearClosedTabsList)
+    
+    def __closedTabAvailable(self, avail):
+        """
+        Private slot to handle changes of the availability of closed tabs.
+        
+        @param avail flag indicating the availability of closed tabs (boolean)
+        """
+        self.__closedTabsMenu.setEnabled(avail)
+
+
+class HistoryMostVisitedMenu(E5ModelMenu):
+    """
+    Class implementing the most visited history menu.
+    
+    @signal openUrl(QUrl, str) emitted to open a URL in the current tab
+    @signal newUrl(QUrl, str) emitted to open a URL in a new tab
+    """
+    openUrl = pyqtSignal(QUrl, str)
+    newUrl = pyqtSignal(QUrl, str)
+    
+    def __init__(self, count, parent=None):
+        """
+        Constructor
+        
+        @param count maximum number of entries to be shown (integer)
+        @param parent reference to the parent widget (QWidget)
+        """
+        E5ModelMenu.__init__(self, parent)
+        
+        self.__historyMenuModel = None
+        
+        self.setMaxRows(count + 1)
+        
+        self.activated.connect(self.__activated)
+        self.setStatusBarTextRole(HistoryModel.UrlStringRole)
+    
+    def __activated(self, idx):
+        """
+        Private slot handling the activated signal.
+        
+        @param idx index of the activated item (QModelIndex)
+        """
+        if self._keyboardModifiers & Qt.ControlModifier:
+            self.newUrl.emit(
+                idx.data(HistoryModel.UrlRole),
+                idx.data(HistoryModel.TitleRole))
+        else:
+            self.openUrl.emit(
+                idx.data(HistoryModel.UrlRole),
+                idx.data(HistoryModel.TitleRole))
+    
+    def prePopulated(self):
+        """
+        Public method to add any actions before the tree.
+       
+        @return flag indicating if any actions were added (boolean)
+        """
+        if self.__historyMenuModel is None:
+            import WebBrowser.WebBrowserWindow
+            historyManager = \
+                WebBrowser.WebBrowserWindow.WebBrowserWindow.historyManager()
+            self.__historyMenuModel = HistoryMostVisitedMenuModel(
+                historyManager.historyFilterModel(), self)
+            self.setModel(self.__historyMenuModel)
+        self.__historyMenuModel.sort(0)
+        
+        return False
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/History/HistoryModel.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,169 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the history model.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import Qt, QAbstractTableModel, QModelIndex, QUrl
+
+import WebBrowser.WebBrowserWindow
+
+
+class HistoryModel(QAbstractTableModel):
+    """
+    Class implementing the history model.
+    """
+    DateRole = Qt.UserRole + 1
+    DateTimeRole = Qt.UserRole + 2
+    UrlRole = Qt.UserRole + 3
+    UrlStringRole = Qt.UserRole + 4
+    TitleRole = Qt.UserRole + 5
+    MaxRole = TitleRole
+    
+    def __init__(self, historyManager, parent=None):
+        """
+        Constructor
+        
+        @param historyManager reference to the history manager object
+            (HistoryManager)
+        @param parent reference to the parent object (QObject)
+        """
+        super(HistoryModel, self).__init__(parent)
+        
+        self.__historyManager = historyManager
+        
+        self.__headers = [
+            self.tr("Title"),
+            self.tr("Address"),
+        ]
+        
+        self.__historyManager.historyReset.connect(self.historyReset)
+        self.__historyManager.entryRemoved.connect(self.historyReset)
+        self.__historyManager.entryAdded.connect(self.entryAdded)
+        self.__historyManager.entryUpdated.connect(self.entryUpdated)
+    
+    def historyReset(self):
+        """
+        Public slot to reset the model.
+        """
+        self.beginResetModel()
+        self.endResetModel()
+    
+    def entryAdded(self):
+        """
+        Public slot to handle the addition of a history entry.
+        """
+        self.beginInsertRows(QModelIndex(), 0, 0)
+        self.endInsertRows()
+    
+    def entryUpdated(self, row):
+        """
+        Public slot to handle the update of a history entry.
+        
+        @param row row number of the updated entry (integer)
+        """
+        idx = self.index(row, 0)
+        self.dataChanged.emit(idx, idx)
+    
+    def headerData(self, section, orientation, role=Qt.DisplayRole):
+        """
+        Public method to get the header data.
+        
+        @param section section number (integer)
+        @param orientation header orientation (Qt.Orientation)
+        @param role data role (integer)
+        @return header data
+        """
+        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
+            try:
+                return self.__headers[section]
+            except IndexError:
+                pass
+        return QAbstractTableModel.headerData(self, section, orientation, role)
+    
+    def data(self, index, role=Qt.DisplayRole):
+        """
+        Public method to get data from the model.
+        
+        @param index index of history entry to get data for (QModelIndex)
+        @param role data role (integer)
+        @return history entry data
+        """
+        lst = self.__historyManager.history()
+        if index.row() < 0 or index.row() > len(lst):
+            return None
+        
+        itm = lst[index.row()]
+        if role == self.DateTimeRole:
+            return itm.dateTime
+        elif role == self.DateRole:
+            return itm.dateTime.date()
+        elif role == self.UrlRole:
+            return QUrl(itm.url)
+        elif role == self.UrlStringRole:
+            return itm.url
+        elif role == self.TitleRole:
+            return itm.userTitle()
+        elif role in [Qt.DisplayRole, Qt.EditRole]:
+            if index.column() == 0:
+                return itm.userTitle()
+            elif index.column() == 1:
+                return itm.url
+        elif role == Qt.DecorationRole:
+            if index.column() == 0:
+                return WebBrowser.WebBrowserWindow.WebBrowserWindow.icon(
+                    QUrl(itm.url))
+        
+        return None
+    
+    def columnCount(self, parent=QModelIndex()):
+        """
+        Public method to get the number of columns.
+        
+        @param parent index of parent (QModelIndex)
+        @return number of columns (integer)
+        """
+        if parent.isValid():
+            return 0
+        else:
+            return len(self.__headers)
+    
+    def rowCount(self, parent=QModelIndex()):
+        """
+        Public method to determine the number of rows.
+        
+        @param parent index of parent (QModelIndex)
+        @return number of rows (integer)
+        """
+        if parent.isValid():
+            return 0
+        else:
+            return len(self.__historyManager.history())
+    
+    def removeRows(self, row, count, parent=QModelIndex()):
+        """
+        Public method to remove history entries from the model.
+        
+        @param row row of the first history entry to remove (integer)
+        @param count number of history entries to remove (integer)
+        @param parent index of the parent entry (QModelIndex)
+        @return flag indicating successful removal (boolean)
+        """
+        if parent.isValid():
+            return False
+        
+        lastRow = row + count - 1
+        self.beginRemoveRows(parent, row, lastRow)
+        lst = self.__historyManager.history()[:]
+        for index in range(lastRow, row - 1, -1):
+            del lst[index]
+        self.__historyManager.historyReset.disconnect(self.historyReset)
+        self.__historyManager.setHistory(lst)
+        self.__historyManager.historyReset.connect(self.historyReset)
+        self.endRemoveRows()
+        return True
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/History/HistoryTreeModel.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,377 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the history tree model.
+"""
+
+from __future__ import unicode_literals
+
+import bisect
+
+from PyQt5.QtCore import Qt, QModelIndex, QDate, QAbstractProxyModel
+
+from .HistoryModel import HistoryModel
+
+import UI.PixmapCache
+
+
+class HistoryTreeModel(QAbstractProxyModel):
+    """
+    Class implementing the history tree model.
+    """
+    def __init__(self, sourceModel, parent=None):
+        """
+        Constructor
+        
+        @param sourceModel reference to the source model (QAbstractItemModel)
+        @param parent reference to the parent object (QObject)
+        """
+        super(HistoryTreeModel, self).__init__(parent)
+        
+        self.__sourceRowCache = []
+        self.__removingDown = False
+        
+        self.setSourceModel(sourceModel)
+    
+    def headerData(self, section, orientation, role=Qt.DisplayRole):
+        """
+        Public method to get the header data.
+        
+        @param section section number (integer)
+        @param orientation header orientation (Qt.Orientation)
+        @param role data role (integer)
+        @return header data
+        """
+        return self.sourceModel().headerData(section, orientation, role)
+    
+    def data(self, index, role=Qt.DisplayRole):
+        """
+        Public method to get data from the model.
+        
+        @param index index of history entry to get data for (QModelIndex)
+        @param role data role (integer)
+        @return history entry data
+        """
+        if role in [Qt.DisplayRole, Qt.EditRole]:
+            start = index.internalId()
+            if start == 0:
+                offset = self.__sourceDateRow(index.row())
+                if index.column() == 0:
+                    idx = self.sourceModel().index(offset, 0)
+                    date = idx.data(HistoryModel.DateRole)
+                    if date == QDate.currentDate():
+                        return self.tr("Earlier Today")
+                    return date.toString("yyyy-MM-dd")
+                if index.column() == 1:
+                    return self.tr(
+                        "%n item(s)", "",
+                        self.rowCount(index.sibling(index.row(), 0)))
+        
+        elif role == Qt.DecorationRole:
+            if index.column() == 0 and not index.parent().isValid():
+                return UI.PixmapCache.getIcon("history.png")
+        
+        elif role == HistoryModel.DateRole:
+            if index.column() == 0 and index.internalId() == 0:
+                offset = self.__sourceDateRow(index.row())
+                idx = self.sourceModel().index(offset, 0)
+                return idx.data(HistoryModel.DateRole)
+        
+        return QAbstractProxyModel.data(self, index, role)
+    
+    def columnCount(self, parent=QModelIndex()):
+        """
+        Public method to get the number of columns.
+        
+        @param parent index of parent (QModelIndex)
+        @return number of columns (integer)
+        """
+        return self.sourceModel().columnCount(self.mapToSource(parent))
+    
+    def rowCount(self, parent=QModelIndex()):
+        """
+        Public method to determine the number of rows.
+        
+        @param parent index of parent (QModelIndex)
+        @return number of rows (integer)
+        """
+        if parent.internalId() != 0 or \
+           parent.column() > 0 or \
+           self.sourceModel() is None:
+            return 0
+        
+        # row count OF dates
+        if not parent.isValid():
+            if self.__sourceRowCache:
+                return len(self.__sourceRowCache)
+            
+            currentDate = QDate()
+            rows = 0
+            totalRows = self.sourceModel().rowCount()
+            
+            for row in range(totalRows):
+                rowDate = self.sourceModel().index(row, 0)\
+                    .data(HistoryModel.DateRole)
+                if rowDate != currentDate:
+                    self.__sourceRowCache.append(row)
+                    currentDate = rowDate
+                    rows += 1
+            return rows
+        
+        # row count FOR a date
+        start = self.__sourceDateRow(parent.row())
+        end = self.__sourceDateRow(parent.row() + 1)
+        return end - start
+    
+    def __sourceDateRow(self, row):
+        """
+        Private method to translate the top level date row into the offset
+        where that date starts.
+        
+        @param row row number of the date (integer)
+        @return offset where that date starts (integer)
+        """
+        if row <= 0:
+            return 0
+        
+        if len(self.__sourceRowCache) == 0:
+            self.rowCount(QModelIndex())
+        
+        if row >= len(self.__sourceRowCache):
+            if self.sourceModel() is None:
+                return 0
+            return self.sourceModel().rowCount()
+        
+        return self.__sourceRowCache[row]
+    
+    def mapToSource(self, proxyIndex):
+        """
+        Public method to map an index to the source model index.
+        
+        @param proxyIndex reference to a proxy model index (QModelIndex)
+        @return source model index (QModelIndex)
+        """
+        offset = proxyIndex.internalId()
+        if offset == 0:
+            return QModelIndex()
+        startDateRow = self.__sourceDateRow(offset - 1)
+        return self.sourceModel().index(
+            startDateRow + proxyIndex.row(), proxyIndex.column())
+    
+    def index(self, row, column, parent=QModelIndex()):
+        """
+        Public method to create an index.
+        
+        @param row row number for the index (integer)
+        @param column column number for the index (integer)
+        @param parent index of the parent item (QModelIndex)
+        @return requested index (QModelIndex)
+        """
+        if row < 0 or \
+           column < 0 or \
+           column >= self.columnCount(parent) or \
+           parent.column() > 0:
+            return QModelIndex()
+        
+        if not parent.isValid():
+            return self.createIndex(row, column, 0)
+        return self.createIndex(row, column, parent.row() + 1)
+
+    def parent(self, index):
+        """
+        Public method to get the parent index.
+        
+        @param index index of item to get parent (QModelIndex)
+        @return index of parent (QModelIndex)
+        """
+        offset = index.internalId()
+        if offset == 0 or not index.isValid():
+            return QModelIndex()
+        return self.createIndex(offset - 1, 0, 0)
+    
+    def hasChildren(self, parent=QModelIndex()):
+        """
+        Public method to check, if an entry has some children.
+        
+        @param parent index of the entry to check (QModelIndex)
+        @return flag indicating the presence of children (boolean)
+        """
+        grandparent = parent.parent()
+        if not grandparent.isValid():
+            return True
+        return False
+    
+    def flags(self, index):
+        """
+        Public method to get the item flags.
+        
+        @param index index of the item (QModelIndex)
+        @return flags (Qt.ItemFlags)
+        """
+        if not index.isValid():
+            return Qt.ItemFlags(Qt.NoItemFlags)
+        return Qt.ItemFlags(
+            Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
+    
+    def setSourceModel(self, sourceModel):
+        """
+        Public method to set the source model.
+        
+        @param sourceModel reference to the source model (QAbstractItemModel)
+        """
+        if self.sourceModel() is not None:
+            self.sourceModel().modelReset.disconnect(self.__sourceReset)
+            self.sourceModel().layoutChanged.disconnect(self.__sourceReset)
+            self.sourceModel().rowsInserted.disconnect(
+                self.__sourceRowsInserted)
+            self.sourceModel().rowsRemoved.disconnect(self.__sourceRowsRemoved)
+        
+        super(HistoryTreeModel, self).setSourceModel(sourceModel)
+        
+        if self.sourceModel() is not None:
+            self.__loaded = False
+            self.sourceModel().modelReset.connect(self.__sourceReset)
+            self.sourceModel().layoutChanged.connect(self.__sourceReset)
+            self.sourceModel().rowsInserted.connect(self.__sourceRowsInserted)
+            self.sourceModel().rowsRemoved.connect(self.__sourceRowsRemoved)
+        
+        self.beginResetModel()
+        self.endResetModel()
+    
+    def __sourceReset(self):
+        """
+        Private slot to handle a reset of the source model.
+        """
+        self.beginResetModel()
+        self.__sourceRowCache = []
+        self.endResetModel()
+    
+    def __sourceRowsInserted(self, parent, start, end):
+        """
+        Private slot to handle the insertion of data in the source model.
+        
+        @param parent reference to the parent index (QModelIndex)
+        @param start start row (integer)
+        @param end end row (integer)
+        """
+        if not parent.isValid():
+            if start != 0 or start != end:
+                self.beginResetModel()
+                self.__sourceRowCache = []
+                self.endResetModel()
+                return
+            
+            self.__sourceRowCache = []
+            treeIndex = self.mapFromSource(self.sourceModel().index(start, 0))
+            treeParent = treeIndex.parent()
+            if self.rowCount(treeParent) == 1:
+                self.beginInsertRows(QModelIndex(), 0, 0)
+                self.endInsertRows()
+            else:
+                self.beginInsertRows(treeParent, treeIndex.row(),
+                                     treeIndex.row())
+                self.endInsertRows()
+    
+    def mapFromSource(self, sourceIndex):
+        """
+        Public method to map an index to the proxy model index.
+        
+        @param sourceIndex reference to a source model index (QModelIndex)
+        @return proxy model index (QModelIndex)
+        """
+        if not sourceIndex.isValid():
+            return QModelIndex()
+        
+        if len(self.__sourceRowCache) == 0:
+            self.rowCount(QModelIndex())
+        
+        try:
+            row = self.__sourceRowCache.index(sourceIndex.row())
+        except ValueError:
+            row = bisect.bisect_left(self.__sourceRowCache, sourceIndex.row())
+        if row == len(self.__sourceRowCache) or \
+           self.__sourceRowCache[row] != sourceIndex.row():
+            row -= 1
+        dateRow = max(0, row)
+        row = sourceIndex.row() - self.__sourceRowCache[dateRow]
+        return self.createIndex(row, sourceIndex.column(), dateRow + 1)
+    
+    def removeRows(self, row, count, parent=QModelIndex()):
+        """
+        Public method to remove entries from the model.
+        
+        @param row row of the first entry to remove (integer)
+        @param count number of entries to remove (integer)
+        @param parent index of the parent entry (QModelIndex)
+        @return flag indicating successful removal (boolean)
+        """
+        if row < 0 or \
+           count <= 0 or \
+           row + count > self.rowCount(parent):
+            return False
+        
+        self.__removingDown = True
+        if parent.isValid() and self.rowCount(parent) == count - row:
+            self.beginRemoveRows(QModelIndex(), parent.row(), parent.row())
+        else:
+            self.beginRemoveRows(parent, row, row + count - 1)
+        if parent.isValid():
+            # removing pages
+            offset = self.__sourceDateRow(parent.row())
+            return self.sourceModel().removeRows(offset + row, count)
+        else:
+            # removing whole dates
+            for i in range(row + count - 1, row - 1, -1):
+                dateParent = self.index(i, 0)
+                offset = self.__sourceDateRow(dateParent.row())
+                if not self.sourceModel().removeRows(
+                        offset, self.rowCount(dateParent)):
+                    return False
+        return True
+    
+    def __sourceRowsRemoved(self, parent, start, end):
+        """
+        Private slot to handle the removal of data in the source model.
+        
+        @param parent reference to the parent index (QModelIndex)
+        @param start start row (integer)
+        @param end end row (integer)
+        """
+        if not self.__removingDown:
+            self.beginResetModel()
+            self.__sourceRowCache = []
+            self.endResetModel()
+            return
+        
+        if not parent.isValid():
+            if self.__sourceRowCache:
+                i = end
+                while i >= start:
+                    try:
+                        ind = self.__sourceRowCache.index(i)
+                    except ValueError:
+                        ind = bisect.bisect_left(self.__sourceRowCache, i)
+                    if ind == len(self.__sourceRowCache) or \
+                       self.__sourceRowCache[ind] != i:
+                        ind -= 1
+                    row = max(0, ind)
+                    offset = self.__sourceRowCache[row]
+                    dateParent = self.index(row, 0)
+                    # If we can remove all the rows in the date do that
+                    # and skip over them.
+                    rc = self.rowCount(dateParent)
+                    if i - rc + 1 == offset and start <= i - rc + 1:
+                        del self.__sourceRowCache[row]
+                        i -= rc + 1
+                    else:
+                        row += 1
+                        i -= 1
+                    for j in range(row, len(self.__sourceRowCache)):
+                        self.__sourceRowCache[j] -= 1
+            
+            if self.__removingDown:
+                self.endRemoveRows()
+                self.__removingDown = False
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/History/__init__.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Package implementing the history system.
+"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/JavaScript/ExternalJsObject.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,91 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the JavaScript external object being the endpoint of
+a web channel.
+"""
+
+#
+# This code was ported from QupZilla and modified.
+# Copyright (C) David Rosca <nowrep@gmail.com>
+#
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtProperty, QObject
+
+from WebBrowser.WebBrowserWindow import WebBrowserWindow
+
+from .StartPageJsObject import StartPageJsObject
+from .PasswordManagerJsObject import PasswordManagerJsObject
+
+
+class ExternalJsObject(QObject):
+    """
+    Class implementing the endpoint of our web channel.
+    """
+    def __init__(self, page):
+        """
+        Constructor
+        
+        @param page reference to the web page object
+        @type WebBrowserPage
+        """
+        super(ExternalJsObject, self).__init__(page)
+        
+        self.__page = page
+        
+        self.__startPage = None
+        self.__passwordManager = None
+    
+    def page(self):
+        """
+        Public method returning a reference to the web page object.
+        
+        @return reference to the web page object
+        @rtype WebBrowserPage
+        """
+        return self.__page
+    
+    @pyqtProperty(QObject, constant=True)
+    def passwordManager(self):
+        """
+        Public method to get a reference to the password manager JavaScript
+        object.
+        
+        @return reference to the password manager JavaScript object
+        @rtype StartPageJsObject
+        """
+        if self.__passwordManager is None:
+            self.__passwordManager = PasswordManagerJsObject(self)
+        
+        return self.__passwordManager
+    
+    @pyqtProperty(QObject, constant=True)
+    def speedDial(self):
+        """
+        Public method returning a reference to a speed dial object.
+        
+        @return reference to a speed dial object
+        @rtype SpeedDial
+        """
+        if self.__page.url().toString() != "eric:speeddial":
+            return None
+        
+        return WebBrowserWindow.speedDial()
+    
+    @pyqtProperty(QObject, constant=True)
+    def startPage(self):
+        """
+        Public method to get a reference to the start page JavaScript object.
+        
+        @return reference to the start page JavaScript object
+        @rtype StartPageJsObject
+        """
+        if self.__startPage is None:
+            self.__startPage = StartPageJsObject(self)
+        
+        return self.__startPage
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/JavaScript/PasswordManagerJsObject.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,46 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the Python side for calling the password manager.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSlot, QObject, QByteArray
+
+
+class PasswordManagerJsObject(QObject):
+    """
+    Class implementing the Python side for calling the password manager.
+    """
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object
+        @type ExternalJsObject
+        """
+        super(PasswordManagerJsObject, self).__init__(parent)
+        
+        self.__external = parent
+    
+    @pyqtSlot(str, str, str, QByteArray)
+    def formSubmitted(self, urlStr, userName, password, data):
+        """
+        Public slot passing form data to the password manager.
+        
+        @param urlStr form submission URL
+        @type str
+        @param userName name of the user
+        @type str
+        @param password user password
+        @type str
+        @param data data to be submitted
+        @type QByteArray
+        """
+        from WebBrowser.WebBrowserWindow import WebBrowserWindow
+        WebBrowserWindow.passwordManager().formSubmitted(
+            urlStr, userName, password, data, self.__external.page())
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/JavaScript/StartPageJsObject.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,51 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the Python side of the eric home page.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSlot, QObject
+
+
+class StartPageJsObject(QObject):
+    """
+    Class implementing the Python side of the eric home page.
+    """
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object
+        @type ExternalJsObject
+        """
+        super(StartPageJsObject, self).__init__(parent)
+        
+        self.__external = parent
+    
+    @pyqtSlot(result=str)
+    def providerString(self):
+        """
+        Public method to get a string for the search provider.
+        
+        @return string for the search provider (string)
+        """
+        return (self.tr("Search results provided by {0}")
+            .format(self.__external.page().view().mainWindow()
+            .openSearchManager().currentEngineName()))
+    
+    @pyqtSlot(str, result=str)
+    def searchUrl(self, searchStr):
+        """
+        Public method to get the search URL for the given search term.
+        
+        @param searchStr search term (string)
+        @return search URL (string)
+        """
+        return bytes(
+            self.__external.page().view().mainWindow().openSearchManager()
+            .currentEngine().searchUrl(searchStr).toEncoded()).decode()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/JavaScript/__init__.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Package implementing the external JavaScript objects.
+"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Network/EricSchemeHandler.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,246 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a scheme handler for the eric: scheme.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSignal, QByteArray, QBuffer, QIODevice, \
+    QTextStream, QUrlQuery
+from PyQt5.QtWidgets import qApp
+from PyQt5.QtWebEngineCore import QWebEngineUrlSchemeHandler
+
+from ..Tools.WebBrowserTools import readAllFileContents
+
+class EricSchemeHandler(QWebEngineUrlSchemeHandler):
+    """
+    Class implementing a scheme handler for the eric: scheme.
+    """
+    SupportedPages = [
+        "adblock",          # error page for URLs blocked by AdBlock
+        "home", "start", "startpage",       # eric home page
+        "speeddial",                        # eric speeddial
+    ]
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object
+        @type QObject
+        """
+        super(EricSchemeHandler, self).__init__(parent)
+        
+        self.__replies = []
+    
+    def requestStarted(self, job):
+        """
+        Public method handling the URL request.
+        
+        @param job URL request job
+        @type QWebEngineUrlRequestJob
+        """
+        if job.requestUrl().path() in self.SupportedPages:
+            reply = EricSchemeReply(job)
+            reply.closed.connect(self.__replyClosed)
+            self.__replies.append(reply)
+            job.reply(b"text/html", reply)
+        else:
+            job.reply(QByteArray(), QBuffer())
+            # job.fail(QWebEngineUrlRequestJob.UrlNotFound)
+    
+    def __replyClosed(self):
+        """
+        Private slot handling the closed signal of a reply.
+        """
+        object = self.sender()
+        if object and object in self.__replies:
+            self.__replies.remove(object)
+
+
+class EricSchemeReply(QIODevice):
+    """
+    Class implementing a reply for a requested eric: page.
+    
+    @signal closed emitted to signal that the web engine has read
+        the data
+    """
+    closed = pyqtSignal()
+    
+    _speedDialPage = ""
+    
+    def __init__(self, job, parent=None):
+        """
+        Constructor
+        
+        @param job reference to the URL request
+        @type QWebEngineUrlRequestJob
+        @param parent reference to the parent object
+        @type QObject
+        """
+        super(EricSchemeReply, self).__init__(parent)
+        
+        self.__loaded = False
+        self.__job = job
+        
+        self.__pageName = self.__job.requestUrl().path()
+        self.__buffer = QBuffer()
+        
+        self.open(QIODevice.ReadOnly)
+        self.__buffer.open(QIODevice.ReadWrite)
+        self.__loadPage()
+    
+    def __loadPage(self):
+        """
+        Private method to load the requested page.
+        """
+        if self.__loaded:
+            return
+        
+        stream = QTextStream(self.__buffer)
+        stream.setCodec("utf-8")
+        
+        if self.__pageName == "adblock":
+            stream << self.__adBlockPage()
+        elif self.__pageName in ["home", "start", "startpage"]:
+            stream << self.__startPage()
+        elif self.__pageName == "speeddial":
+            stream << self.__speedDialPage()
+        
+        stream.flush()
+        self.__buffer.reset()
+        self.__loaded = True
+    
+    def bytesAvailable(self):
+        """
+        Public method to get the number of available bytes.
+        
+        @return number of available bytes
+        @rtype int
+        """
+        return self.__buffer.bytesAvailable()
+    
+    def readData(self, maxlen):
+        """
+        Public method to retrieve data from the reply object.
+        
+        @param maxlen maximum number of bytes to read (integer)
+        @return string containing the data (bytes)
+        """
+        return self.__buffer.read(maxlen)
+    
+    def close(self):
+        """
+        Public method used to cloase the reply.
+        """
+        super(EricSchemeReply, self).close()
+        self.closed.emit()
+    
+    def __adBlockPage(self):
+        """
+        Private method to build the AdBlock page.
+        
+        @return built AdBlock page
+        @rtype str
+        """
+        query = QUrlQuery(self.__job.requestUrl())
+        rule = query.queryItemValue("rule")
+        subscription = query.queryItemValue("subscription")
+        title = self.tr("Content blocked by AdBlock Plus")
+        message = self.tr(
+            "Blocked by rule: <i>{0} ({1})</i>").format(rule, subscription)
+        
+        page = readAllFileContents(":/html/adblockPage.html")
+        page = page.replace(
+            "@FAVICON@", "qrc:icons/adBlockPlus16.png")
+        page = page.replace(
+            "@IMAGE@", "qrc:icons/adBlockPlus64.png")
+        page = page.replace("@TITLE@", title)
+        page = page.replace("@MESSAGE@", message)
+        
+        return page
+    
+    def __startPage(self):
+        """
+        Private method to build the Start page.
+        
+        @return built Start page
+        @rtype str
+        """
+        page = readAllFileContents(":/html/startPage.html")
+        page = page.replace("@FAVICON@", "qrc:icons/ericWeb16.png")
+        page = page.replace("@IMAGE@", "qrc:icons/ericWeb32.png")
+        page = page.replace("@TITLE@",
+                            self.tr("Welcome to eric6 Web Browser!"))
+        page = page.replace("@ERIC_LINK@", self.tr("About eric6"))
+        page = page.replace("@HEADER_TITLE@", self.tr("eric6 Web Browser"))
+        page = page.replace("@SUBMIT@", self.tr("Search!"))
+        if qApp.isLeftToRight():
+            ltr = "LTR"
+        else:
+            ltr = "RTL"
+        page = page.replace("@QT_LAYOUT_DIRECTION@", ltr)
+        
+        return page
+    
+    def __speedDialPage(self):
+        """
+        Private method to create the Speeddial page.
+        
+        @return prepared speeddial page (QByteArray)
+        """
+        if not self._speedDialPage:
+            page = readAllFileContents(":/html/speeddialPage.html")
+            page = (
+                page.replace("@FAVICON@", "qrc:icons/ericWeb16.png")
+                .replace("@IMG_PLUS@", "qrc:icons/plus.png")
+                .replace("@IMG_CLOSE@", "qrc:icons/close.png")
+                .replace("@IMG_EDIT@", "qrc:icons/edit.png")
+                .replace("@IMG_RELOAD@", "qrc:icons/reload.png")
+                .replace("@IMG_SETTINGS@", "qrc:icons/setting.png")
+                .replace("@LOADING-IMG@", "qrc:icons/loading.gif")
+                .replace("@BOX-BORDER@", "qrc:icons/box-border-small.png")
+                
+                .replace("@JQUERY@", "qrc:javascript/jquery.js")
+                .replace("@JQUERY-UI@", "qrc:javascript/jquery-ui.js")
+                
+                .replace("@SITE-TITLE@", self.tr("Speed Dial"))
+                .replace("@URL@", self.tr("URL"))
+                .replace("@TITLE@", self.tr("Title"))
+                .replace("@APPLY@", self.tr("Apply"))
+                .replace("@CLOSE@", self.tr("Close"))
+                .replace("@NEW-PAGE@", self.tr("New Page"))
+                .replace("@TITLE-EDIT@", self.tr("Edit"))
+                .replace("@TITLE-REMOVE@", self.tr("Remove"))
+                .replace("@TITLE-RELOAD@", self.tr("Reload"))
+                .replace("@TITLE-WARN@",
+                         self.tr("Are you sure to remove this speed dial?"))
+                .replace("@TITLE-WARN-REL@", 
+                         self.tr("Are you sure you want to reload all speed"
+                                 " dials?"))
+                .replace("@TITLE-FETCHTITLE@",
+                         self.tr("Load title from page"))
+                .replace("@SETTINGS-TITLE@",
+                         self.tr("Speed Dial Settings"))
+                .replace("@ADD-TITLE@", self.tr("Add New Page"))
+                .replace("@TXT_NRROWS@",
+                         self.tr("Maximum pages in a row:"))
+                .replace("@TXT_SDSIZE@", self.tr("Change size of pages:"))
+            )
+            
+            self._speedDialPage = page
+        
+        from WebBrowser.WebBrowserWindow import WebBrowserWindow
+        dial = WebBrowserWindow.speedDial()
+        page = (
+            self._speedDialPage
+            .replace("@INITIAL-SCRIPT@", dial.initialScript())
+            .replace("@ROW-PAGES@", str(dial.pagesInRow()))
+            .replace("@SD-SIZE@", str(dial.sdSize()))
+        )
+        
+        return page
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Network/FollowRedirectReply.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,114 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a network reply delegate allowing to check redirects.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSignal, QObject
+from PyQt5.QtNetwork import QNetworkRequest
+
+
+class FollowRedirectReply(QObject):
+    """
+    Class implementing a network reply delegate allowing to check redirects.
+    """
+    finished = pyqtSignal()
+    
+    def __init__(self, url, manager, maxRedirects=5):
+        """
+        Constructor
+        
+        @param url URL to get (QUrl)
+        @param manager reference to the network access manager
+            (QNetworkAccessManager)
+        @keyparam maxRedirects maximum allowed redirects (integer)
+        """
+        super(FollowRedirectReply, self).__init__()
+        
+        self.__manager = manager
+        self.__maxRedirects = maxRedirects
+        self.__redirectCount = 0
+        
+        self.__reply = self.__manager.get(QNetworkRequest(url))
+        self.__reply.finished.connect(self.__replyFinished)
+    
+    def reply(self):
+        """
+        Public method to get the reply object.
+        
+        @return reference to the reply object (QNetworkReply)
+        """
+        return self.__reply
+    
+    def originalUrl(self):
+        """
+        Public method to get the original URL.
+        
+        @return original URL (QUrl)
+        """
+        return self.__reply.request().url()
+    
+    def url(self):
+        """
+        Public method to get the final URL (after redirects).
+        
+        @return final URL (QUrl)
+        """
+        return self.__reply.url()
+    
+    def error(self):
+        """
+        Public method to get the error information.
+        
+        @return error code (QNetworkReply.NetworkError)
+        """
+        return self.__reply.error()
+    
+    def errorString(self):
+        """
+        Public method to get the error message.
+        
+        @return error message (string)
+        """
+        return self.__reply.errorString()
+    
+    def readAll(self):
+        """
+        Public method to read all received data.
+        
+        @return received raw data (QByteArray)
+        """
+        return self.__reply.readAll()
+    
+    def close(self):
+        """
+        Public method to close the data stream.
+        """
+        self.__reply.close()
+    
+    def __replyFinished(self):
+        """
+        Private slot handling the receipt of the requested data.
+        """
+        replyStatus = self.__reply.attribute(
+            QNetworkRequest.HttpStatusCodeAttribute)
+        if (replyStatus != 301 and replyStatus != 302) or \
+           self.__redirectCount == self.__maxRedirects:
+            self.finished.emit()
+            return
+        
+        self.__redirectCount += 1
+        
+        redirectUrl = self.__reply.attribute(
+            QNetworkRequest.RedirectionTargetAttribute)
+        self.__reply.close()
+        self.__reply.deleteLater()
+        self.__reply = None
+        
+        self.__reply = self.__manager.get(QNetworkRequest(redirectUrl))
+        self.__reply.finished.connect(self.__replyFinished)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Network/LoadRequest.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,133 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a page load request object.
+"""
+
+#
+# This code was ported from QupZilla.
+# Copyright (C) David Rosca <nowrep@gmail.com>
+#
+
+from __future__ import unicode_literals
+
+try:
+    from enum import Enum
+except ImportError:
+    from ThirdParty.enum import Enum
+
+from PyQt5.QtCore import QByteArray, QUrl
+
+
+class LoadRequestOperations(Enum):
+    """
+    Class implementing the load request operations.
+    """
+    GetOperation = 0
+    PostOperation = 1
+
+
+class LoadRequest(object):
+    """
+    Class implementing a page load request object.
+    """
+    def __init__(self, urlOrRequest=None,
+                 op=LoadRequestOperations.GetOperation, data=QByteArray()):
+        """
+        Constructor
+        
+        @param urlOrRequest URL or request object
+        @type QUrl or LoadRequest
+        @param op request operation
+        @type LoadRequestOperations
+        @param data request data
+        @type QByteArray
+        """
+        self.__url = QUrl()
+        self.__operation = op
+        self.__data = QByteArray(data)
+        
+        if isinstance(urlOrRequest, QUrl):
+            self.__url = QUrl(urlOrRequest)
+        elif isinstance(urlOrRequest, LoadRequest):
+            self.__url = urlOrRequest.url()
+            self.__operation = urlOrRequest.operation()
+            self.__data = urlOrRequest.data()
+    
+    def isEmpty(self):
+        """
+        Public method to test for an empty request.
+        
+        @return flag indicating an empty request
+        @rtype bool
+        """
+        return self.__url.isEmpty()
+    
+    def url(self):
+        """
+        Public method to get the request URL.
+        
+        @return request URL
+        @rtype QUrl
+        """
+        return QUrl(self.__url)
+    
+    def setUrl(self, url):
+        """
+        Public method to set the request URL.
+        
+        @param url request URL
+        @type QUrl
+        """
+        self.__url = QUrl(url)
+    
+    def urlString(self):
+        """
+        Public method to get the request URL as a string.
+        
+        @return request URL as a string
+        @rtype str
+        """
+        return QUrl.fromPercentEncoding(self.__url.toEncoded())
+    
+    def operation(self):
+        """
+        Public method to get the request operation.
+        
+        @return request operation
+        @rtype one of LoadRequest.GetOperation, LoadRequest.PostOperation
+        """
+        return self.__operation
+    
+    def setOperation(self, op):
+        """
+        Public method to set the request operation.
+        
+        @param op request operation
+        @type one of LoadRequest.GetOperation, LoadRequest.PostOperation
+        """
+        assert op in [LoadRequestOperations.GetOperation,
+                      LoadRequestOperations.PostOperation]
+        
+        self.__operation = op
+    
+    def data(self):
+        """
+        Public method to get the request data.
+        
+        @return request data
+        @rtype QByteArray
+        """
+        return QByteArray(self.__data)
+    
+    def setData(self, data):
+        """
+        Public method to set the request data
+        
+        @param data request data
+        @type QByteArray
+        """
+        self.__data = QByteArray(data)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Network/NetworkManager.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,303 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a network manager class.
+"""
+
+from __future__ import unicode_literals
+
+import json
+
+from PyQt5.QtCore import pyqtSignal, QByteArray
+from PyQt5.QtWidgets import QDialog
+from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkProxy
+
+from E5Gui import E5MessageBox
+
+from E5Network.E5NetworkProxyFactory import proxyAuthenticationRequired
+try:
+    from E5Network.E5SslErrorHandler import E5SslErrorHandler
+    SSL_AVAILABLE = True
+except ImportError:
+    SSL_AVAILABLE = False
+
+from WebBrowser.WebBrowserWindow import WebBrowserWindow
+from .NetworkUrlInterceptor import NetworkUrlInterceptor
+
+from Utilities.AutoSaver import AutoSaver
+import Preferences
+
+
+class NetworkManager(QNetworkAccessManager):
+    """
+    Class implementing a network manager.
+    
+    @signal changed() emitted to indicate a change
+    """
+    changed = pyqtSignal()
+    
+    def __init__(self, engine, parent=None):
+        """
+        Constructor
+        
+        @param engine reference to the help engine (QHelpEngine)
+        @param parent reference to the parent object (QObject)
+        """
+        super(NetworkManager, self).__init__(parent)
+        
+        if not WebBrowserWindow._fromEric:
+            from PyQt5.QtNetwork import QNetworkProxyFactory
+            from E5Network.E5NetworkProxyFactory import E5NetworkProxyFactory
+            
+            self.__proxyFactory = E5NetworkProxyFactory()
+            QNetworkProxyFactory.setApplicationProxyFactory(
+                self.__proxyFactory)
+        
+        self.languagesChanged()
+        
+        if SSL_AVAILABLE:
+            self.__sslErrorHandler = E5SslErrorHandler(self)
+            self.sslErrors.connect(self.__sslErrorHandler.sslErrorsReplySlot)
+        
+        self.__temporarilyIgnoredSslErrors = {}
+        self.__permanentlyIgnoredSslErrors = {}
+        # dictionaries of permanently and temporarily ignored SSL errors
+        
+        self.__loaded = False
+        self.__saveTimer = AutoSaver(self, self.__save)
+        
+        self.changed.connect(self.__saveTimer.changeOccurred)
+        self.proxyAuthenticationRequired.connect(proxyAuthenticationRequired)
+        self.authenticationRequired.connect(
+            lambda reply, auth: self.authentication(reply.url(), auth))
+        
+        from .EricSchemeHandler import EricSchemeHandler
+        self.__ericSchemeHandler = EricSchemeHandler()
+        WebBrowserWindow.webProfile().installUrlSchemeHandler(
+            QByteArray(b"eric"), self.__ericSchemeHandler)
+        
+        if engine:
+            from .QtHelpSchemeHandler import QtHelpSchemeHandler
+            self.__qtHelpSchemeHandler = QtHelpSchemeHandler(engine)
+            WebBrowserWindow.webProfile().installUrlSchemeHandler(
+                QByteArray(b"qthelp"), self.__qtHelpSchemeHandler)
+        
+        self.__interceptor = NetworkUrlInterceptor(self)
+        WebBrowserWindow.webProfile().setRequestInterceptor(self.__interceptor)
+        
+        WebBrowserWindow.cookieJar()
+    
+    def __save(self):
+        """
+        Private slot to save the permanent SSL error exceptions.
+        """
+        if not self.__loaded:
+            return
+        
+        from WebBrowser.WebBrowserWindow import WebBrowserWindow
+        if not WebBrowserWindow.isPrivate():
+            dbString = json.dumps(self.__permanentlyIgnoredSslErrors)
+            Preferences.setWebBrowser("SslExceptionsDB", dbString)
+    
+    def __load(self):
+        """
+        Private method to load the permanent SSL error exceptions.
+        """
+        if self.__loaded:
+            return
+        
+        dbString = Preferences.getWebBrowser("SslExceptionsDB")
+        if dbString:
+            try:
+                db = json.loads(dbString)
+                self.__permanentlyIgnoredSslErrors = db
+            except ValueError:
+                # ignore silently
+                pass
+        
+        self.__loaded = True
+    
+    def shutdown(self):
+        """
+        Public method to shut down the network manager.
+        """
+        self.__saveTimer.saveIfNeccessary()
+        self.__loaded = False
+        self.__temporarilyIgnoredSslErrors = {}
+        self.__permanentlyIgnoredSslErrors = {}
+    
+    def showSslErrorExceptionsDialog(self):
+        """
+        Public method to show the SSL error exceptions dialog.
+        """
+        self.__load()
+        
+        from .SslErrorExceptionsDialog import SslErrorExceptionsDialog
+        dlg = SslErrorExceptionsDialog(self.__permanentlyIgnoredSslErrors)
+        if dlg.exec_() == QDialog.Accepted:
+            self.__permanentlyIgnoredSslErrors = dlg.getSslErrorExceptions()
+            self.changed.emit()
+    
+    def clearSslExceptions(self):
+        """
+        Public method to clear the permanent SSL certificate error exceptions.
+        """
+        self.__load()
+        
+        self.__permanentlyIgnoredSslErrors = {}
+        self.changed.emit()
+        self.__saveTimer.saveIfNeccessary()
+    
+    def certificateError(self, error, view):
+        """
+        Public method to handle SSL certificate errors.
+        
+        @param error object containing the certificate error information
+        @type QWebEngineCertificateError
+        @param view reference to a view to be used as parent for the dialog
+        @type QWidget
+        @return flag indicating to ignore this error
+        @rtype bool
+        """
+        self.__load()
+        
+        host = error.url().host()
+        
+        if host in self.__temporarilyIgnoredSslErrors and \
+                error.error() in self.__temporarilyIgnoredSslErrors[host]:
+            return True
+        
+        if host in self.__permanentlyIgnoredSslErrors and \
+                error.error() in self.__permanentlyIgnoredSslErrors[host]:
+            return True
+        
+        title = self.tr("SSL Certificate Error")
+        msgBox = E5MessageBox.E5MessageBox(
+            E5MessageBox.Warning,
+            title,
+            self.tr("""<b>{0}</b>"""
+                    """<p>The page you are trying to access has errors"""
+                    """ in the SSL certificate.</p>"""
+                    """<ul><li>{1}</li></ul>"""
+                    """<p>Would you like to make an exception?</p>""")
+            .format(title, error.errorDescription()),
+            modal=True, parent=view)
+        permButton = msgBox.addButton(self.tr("&Permanent accept"),
+                                      E5MessageBox.AcceptRole)
+        tempButton = msgBox.addButton(self.tr("&Temporary accept"),
+                                      E5MessageBox.AcceptRole)
+        msgBox.addButton(self.tr("&Reject"), E5MessageBox.RejectRole)
+        msgBox.exec_()
+        if msgBox.clickedButton() == permButton:
+            if host not in self.__permanentlyIgnoredSslErrors:
+                self.__permanentlyIgnoredSslErrors[host] = []
+            self.__permanentlyIgnoredSslErrors[host].append(error.error())
+            self.changed.emit()
+            return True
+        elif msgBox.clickedButton() == tempButton:
+            if host not in self.__temporarilyIgnoredSslErrors:
+                self.__temporarilyIgnoredSslErrors[host] = []
+            self.__temporarilyIgnoredSslErrors[host].append(error.error())
+            return True
+        else:
+            return False
+    
+    def authentication(self, url, auth):
+        """
+        Public slot to handle an authentication request.
+        
+        @param url URL requesting authentication (QUrl)
+        @param auth reference to the authenticator object (QAuthenticator)
+        """
+        urlRoot = "{0}://{1}"\
+            .format(url.scheme(), url.authority())
+        realm = auth.realm()
+        if not realm and 'realm' in auth.options():
+            realm = auth.option("realm")
+        if realm:
+            info = self.tr("<b>Enter username and password for '{0}', "
+                           "realm '{1}'</b>").format(urlRoot, realm)
+        else:
+            info = self.tr("<b>Enter username and password for '{0}'</b>")\
+                .format(urlRoot)
+        
+        from UI.AuthenticationDialog import AuthenticationDialog
+        import WebBrowser.WebBrowserWindow
+        
+        dlg = AuthenticationDialog(info, auth.user(),
+                                   Preferences.getUser("SavePasswords"),
+                                   Preferences.getUser("SavePasswords"))
+        if Preferences.getUser("SavePasswords"):
+            username, password = \
+                WebBrowser.WebBrowserWindow.WebBrowserWindow.passwordManager()\
+                .getLogin(url, realm)
+            if username:
+                dlg.setData(username, password)
+        if dlg.exec_() == QDialog.Accepted:
+            username, password = dlg.getData()
+            auth.setUser(username)
+            auth.setPassword(password)
+            if Preferences.getUser("SavePasswords"):
+                WebBrowser.WebBrowserWindow.WebBrowserWindow.passwordManager()\
+                .setLogin(url, realm, username, password)
+    
+    def proxyAuthentication(self, requestUrl, auth, proxyHost):
+        """
+        Public slot to handle a proxy authentication request.
+        
+        @param requestUrl requested URL
+        @type QUrl
+        @param auth reference to the authenticator object
+        @type QAuthenticator
+        @param hostname name of the proxy host
+        @type str
+        """
+        proxy = QNetworkProxy.applicationProxy()
+        if proxy.user() and proxy.password():
+            auth.setUser(proxy.user())
+            auth.setPassword(proxy.password())
+            return
+        
+        proxyAuthenticationRequired(proxy, auth)
+    
+    def languagesChanged(self):
+        """
+        Public slot to (re-)load the list of accepted languages.
+        """
+        from WebBrowser.WebBrowserLanguagesDialog import \
+            WebBrowserLanguagesDialog
+        languages = Preferences.toList(
+            Preferences.Prefs.settings.value(
+                "WebBrowser/AcceptLanguages",
+                WebBrowserLanguagesDialog.defaultAcceptLanguages()))
+        self.__acceptLanguage = WebBrowserLanguagesDialog.httpString(languages)
+        
+        WebBrowserWindow.webProfile().setHttpAcceptLanguage(
+            self.__acceptLanguage)
+    
+    def installUrlInterceptor(self, interceptor):
+        """
+        Public method to install an URL interceptor.
+        
+        @param interceptor URL interceptor to be installed
+        @type UrlInterceptor
+        """
+        self.__interceptor.installUrlInterceptor(interceptor)
+    
+    def removeUrlInterceptor(self, interceptor):
+        """
+        Public method to remove an URL interceptor.
+        
+        @param interceptor URL interceptor to be removed
+        @type UrlInterceptor
+        """
+        self.__interceptor.removeUrlInterceptor(interceptor)
+    
+    def preferencesChanged(self):
+        """
+        Public slot to handle a change of preferences.
+        """
+        self.__interceptor.preferencesChanged()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Network/NetworkUrlInterceptor.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,86 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a class to handle URL requests before they get processed
+by QtWebEngine.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtWebEngineCore import QWebEngineUrlRequestInterceptor
+
+import Preferences
+
+
+class NetworkUrlInterceptor(QWebEngineUrlRequestInterceptor):
+    """
+    Class implementing an URL request handler.
+    """
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object
+        @type QObject
+        """
+        super(NetworkUrlInterceptor, self).__init__(parent)
+        
+        self.__interceptors = []
+        
+        self.__loadSettings()
+    
+    def interceptRequest(self, info):
+        """
+        Public method handling an URL request.
+        
+        @param info URL request information
+        @type QWebEngineUrlRequestInfo
+        """
+        # Do Not Track feature
+        if self.__doNotTrack:
+            info.setHttpHeader(b"DNT", b"1")
+            info.setHttpHeader(b"X-Do-Not-Track", b"1")
+        
+        # Send referer header?
+        if not self.__sendReferer and info.requestUrl().host() not in \
+                Preferences.getWebBrowser("SendRefererWhitelist"):
+            info.setHttpHeader(b"Referer", b"")
+        
+        for interceptor in self.__interceptors:
+            interceptor.interceptRequest(info)
+    
+    def installUrlInterceptor(self, interceptor):
+        """
+        Public method to install an URL interceptor.
+        
+        @param interceptor URL interceptor to be installed
+        @type UrlInterceptor
+        """
+        if interceptor not in self.__interceptors:
+            self.__interceptors.append(interceptor)
+    
+    def removeUrlInterceptor(self,  interceptor):
+        """
+        Public method to remove an URL interceptor.
+        
+        @param interceptor URL interceptor to be removed
+        @type UrlInterceptor
+        """
+        if interceptor in self.__interceptors:
+            self.__interceptors.remove(interceptor)
+    
+    def __loadSettings(self):
+        """
+        Private method to load the Network Manager settings.
+        """
+        self.__doNotTrack = Preferences.getWebBrowser("DoNotTrack")
+        self.__sendReferer = Preferences.getWebBrowser("SendReferer")
+    
+    def preferencesChanged(self):
+        """
+        Public slot to handle a change of preferences.
+        """
+        self.__loadSettings()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Network/QtHelpSchemeHandler.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,283 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a scheme access handler for QtHelp.
+"""
+
+from __future__ import unicode_literals
+
+import mimetypes
+import os
+
+from PyQt5.QtCore import pyqtSignal, QByteArray, QIODevice, QBuffer
+from PyQt5.QtWebEngineCore import QWebEngineUrlSchemeHandler, \
+    QWebEngineUrlRequestJob
+
+##from .SchemeAccessHandler import SchemeAccessHandler
+##
+##from .NetworkReply import NetworkReply
+##
+QtDocPath = "qthelp://org.qt-project."
+
+ExtensionMap = {
+    ".bmp": "image/bmp",
+    ".css": "text/css",
+    ".gif": "image/gif",
+    ".html": "text/html",
+    ".htm": "text/html",
+    ".ico": "image/x-icon",
+    ".jpeg": "image/jpeg",
+    ".jpg": "image/jpeg",
+    ".js": "application/x-javascript",
+    ".mng": "video/x-mng",
+    ".pbm": "image/x-portable-bitmap",
+    ".pgm": "image/x-portable-graymap",
+    ".pdf": "application/pdf",
+    ".png": "image/png",
+    ".ppm": "image/x-portable-pixmap",
+    ".rss": "application/rss+xml",
+    ".svg": "image/svg+xml",
+    ".svgz": "image/svg+xml",
+    ".text": "text/plain",
+    ".tif": "image/tiff",
+    ".tiff": "image/tiff",
+    ".txt": "text/plain",
+    ".xbm": "image/x-xbitmap",
+    ".xml": "text/xml",
+    ".xpm": "image/x-xpm",
+    ".xsl": "text/xsl",
+    ".xhtml": "application/xhtml+xml",
+    ".wml": "text/vnd.wap.wml",
+    ".wmlc": "application/vnd.wap.wmlc",
+}
+
+
+class QtHelpSchemeHandler(QWebEngineUrlSchemeHandler):
+    """
+    Class implementing a scheme handler for the qthelp: scheme.
+    """
+    def __init__(self, engine, parent=None):
+        """
+        Constructor
+        
+        @param engine reference to the help engine
+        @type QHelpEngine
+        @param parent reference to the parent object
+        @type QObject
+        """
+        super(QtHelpSchemeHandler, self).__init__(parent)
+        
+        self.__engine = engine
+        
+        self.__replies = []
+    
+    def requestStarted(self, job):
+        """
+        Public method handling the URL request.
+        
+        @param job URL request job
+        @type QWebEngineUrlRequestJob
+        """
+        if job.requestUrl().scheme() == "qthelp":
+            reply = QtHelpSchemeReply(job, self.__engine)
+            reply.closed.connect(self.__replyClosed)
+            self.__replies.append(reply)
+            job.reply(reply.mimeType(), reply)
+        else:
+            job.fail(QWebEngineUrlRequestJob.UrlInvalid)
+    
+    def __replyClosed(self):
+        """
+        Private slot handling the closed signal of a reply.
+        """
+        object = self.sender()
+        if object and object in self.__replies:
+            self.__replies.remove(object)
+
+
+class QtHelpSchemeReply(QIODevice):
+    """
+    Class implementing a reply for a requested qthelp: page.
+    
+    @signal closed emitted to signal that the web engine has read
+        the data
+    """
+    closed = pyqtSignal()
+    
+    def __init__(self, job, engine, parent=None):
+        """
+        Constructor
+        
+        @param job reference to the URL request
+        @type QWebEngineUrlRequestJob
+        @param engine reference to the help engine
+        @type QHelpEngine
+        @param parent reference to the parent object
+        @type QObject
+        """
+        super(QtHelpSchemeReply, self).__init__(parent)
+        
+        url = job.requestUrl()
+        strUrl = url.toString()
+        
+        self.__buffer = QBuffer()
+        
+        # For some reason the url to load maybe wrong (passed from web engine)
+        # though the css file and the references inside should work that way.
+        # One possible problem might be that the css is loaded at the same
+        # level as the html, thus a path inside the css like
+        # (../images/foo.png) might cd out of the virtual folder
+        if not engine.findFile(url).isValid():
+            if strUrl.startswith(QtDocPath):
+                newUrl = job.requestUrl()
+                if not newUrl.path().startswith("/qdoc/"):
+                    newUrl.setPath("/qdoc" + newUrl.path())
+                    url = newUrl
+                    strUrl = url.toString()
+        
+        self.__mimeType = mimetypes.guess_type(strUrl)[0]
+        if self.__mimeType is None:
+            # do our own (limited) guessing
+            self.__mimeType = self.__mimeFromUrl(url)
+        
+        if engine.findFile(url).isValid():
+            data = engine.fileData(url)
+        else:
+            data = QByteArray(self.tr(
+                """<html>"""
+                """<head><title>Error 404...</title></head>"""
+                """<body><div align="center"><br><br>"""
+                """<h1>The page could not be found</h1><br>"""
+                """<h3>'{0}'</h3></div></body>"""
+                """</html>""").format(strUrl)
+                .encode("utf-8"))
+        
+        self.__buffer.setData(data)
+        self.__buffer.open(QIODevice.ReadOnly)
+        self.open(QIODevice.ReadOnly)
+    
+    def bytesAvailable(self):
+        """
+        Public method to get the number of available bytes.
+        
+        @return number of available bytes
+        @rtype int
+        """
+        return self.__buffer.bytesAvailable()
+    
+    def readData(self, maxlen):
+        """
+        Public method to retrieve data from the reply object.
+        
+        @param maxlen maximum number of bytes to read (integer)
+        @return string containing the data (bytes)
+        """
+        return self.__buffer.read(maxlen)
+    
+    def close(self):
+        """
+        Public method used to cloase the reply.
+        """
+        super(QtHelpSchemeReply, self).close()
+        self.closed.emit()
+    
+    def __mimeFromUrl(self, url):
+        """
+        Private method to guess the mime type given an URL.
+        
+        @param url URL to guess the mime type from (QUrl)
+        @return mime type for the given URL (string)
+        """
+        path = url.path()
+        ext = os.path.splitext(path)[1].lower()
+        if ext in ExtensionMap:
+            return ExtensionMap[ext]
+        else:
+            return "application/octet-stream"
+    
+    def mimeType(self):
+        """
+        Public method to get the reply mime type.
+        
+        @return mime type of the reply
+        @rtype bytes
+        """
+        return self.__mimeType.encode("utf-8")
+##
+##
+##
+##
+##
+##class QtHelpAccessHandler(SchemeAccessHandler):
+##    """
+##    Class implementing a scheme access handler for QtHelp.
+##    """
+##    def __init__(self, engine, parent=None):
+##        """
+##        Constructor
+##        
+##        @param engine reference to the help engine (QHelpEngine)
+##        @param parent reference to the parent object (QObject)
+##        """
+##        SchemeAccessHandler.__init__(self, parent)
+##        
+##        self.__engine = engine
+##    
+##    def __mimeFromUrl(self, url):
+##        """
+##        Private method to guess the mime type given an URL.
+##        
+##        @param url URL to guess the mime type from (QUrl)
+##        @return mime type for the given URL (string)
+##        """
+##        path = url.path()
+##        ext = os.path.splitext(path)[1].lower()
+##        if ext in ExtensionMap:
+##            return ExtensionMap[ext]
+##        else:
+##            return "application/octet-stream"
+##    
+##    def createRequest(self, op, request, outgoingData=None):
+##        """
+##        Public method to create a request.
+##        
+##        @param op the operation to be performed
+##            (QNetworkAccessManager.Operation)
+##        @param request reference to the request object (QNetworkRequest)
+##        @param outgoingData reference to an IODevice containing data to be sent
+##            (QIODevice)
+##        @return reference to the created reply object (QNetworkReply)
+##        """
+##        url = request.url()
+##        strUrl = url.toString()
+##        
+##        # For some reason the url to load is already wrong (passed from webkit)
+##        # though the css file and the references inside should work that way.
+##        # One possible problem might be that the css is loaded at the same
+##        # level as the html, thus a path inside the css like
+##        # (../images/foo.png) might cd out of the virtual folder
+##        if not self.__engine.findFile(url).isValid():
+##            if strUrl.startswith(QtDocPath):
+##                newUrl = request.url()
+##                if not newUrl.path().startswith("/qdoc/"):
+##                    newUrl.setPath("qdoc" + newUrl.path())
+##                    url = newUrl
+##                    strUrl = url.toString()
+##        
+##        mimeType = mimetypes.guess_type(strUrl)[0]
+##        if mimeType is None:
+##            # do our own (limited) guessing
+##            mimeType = self.__mimeFromUrl(url)
+##        
+##        if self.__engine.findFile(url).isValid():
+##            data = self.__engine.fileData(url)
+##        else:
+##            data = QByteArray(self.tr(
+##                """<title>Error 404...</title>"""
+##                """<div align="center"><br><br>"""
+##                """<h1>The page could not be found</h1><br>"""
+##                """<h3>'{0}'</h3></div>""").format(strUrl).encode("utf-8"))
+##        return NetworkReply(request, data, mimeType, self.parent())
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Network/SendRefererWhitelistDialog.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,70 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog to manage the Send Referer whitelist.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSlot, Qt, QSortFilterProxyModel, QStringListModel
+from PyQt5.QtWidgets import QDialog, QInputDialog, QLineEdit
+
+from .Ui_SendRefererWhitelistDialog import Ui_SendRefererWhitelistDialog
+
+import Preferences
+
+
+class SendRefererWhitelistDialog(QDialog, Ui_SendRefererWhitelistDialog):
+    """
+    Class implementing a dialog to manage the Send Referer whitelist.
+    """
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(SendRefererWhitelistDialog, self).__init__(parent)
+        self.setupUi(self)
+        
+        self.__model = QStringListModel(
+            Preferences.getWebBrowser("SendRefererWhitelist"), self)
+        self.__model.sort(0)
+        self.__proxyModel = QSortFilterProxyModel(self)
+        self.__proxyModel.setFilterCaseSensitivity(Qt.CaseInsensitive)
+        self.__proxyModel.setSourceModel(self.__model)
+        self.whitelist.setModel(self.__proxyModel)
+        
+        self.searchEdit.textChanged.connect(
+            self.__proxyModel.setFilterFixedString)
+        
+        self.removeButton.clicked.connect(self.whitelist.removeSelected)
+        self.removeAllButton.clicked.connect(self.whitelist.removeAll)
+    
+    @pyqtSlot()
+    def on_addButton_clicked(self):
+        """
+        Private slot to add an entry to the whitelist.
+        """
+        host, ok = QInputDialog.getText(
+            self,
+            self.tr("Send Referer Whitelist"),
+            self.tr("Enter host name to add to the whitelist:"),
+            QLineEdit.Normal)
+        if ok and host != "" and host not in self.__model.stringList():
+            self.__model.insertRow(self.__model.rowCount())
+            self.__model.setData(
+                self.__model.index(self.__model.rowCount() - 1), host)
+            self.__model.sort(0)
+    
+    def accept(self):
+        """
+        Public method to accept the dialog data.
+        """
+        Preferences.setWebBrowser(
+            "SendRefererWhitelist", self.__model.stringList())
+        
+        super(SendRefererWhitelistDialog, self).accept()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Network/SendRefererWhitelistDialog.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,199 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>SendRefererWhitelistDialog</class>
+ <widget class="QDialog" name="SendRefererWhitelistDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>500</width>
+    <height>350</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Send Referer Whitelist</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QGridLayout" name="gridLayout_2">
+     <property name="horizontalSpacing">
+      <number>0</number>
+     </property>
+     <item row="0" column="1">
+      <widget class="E5ClearableLineEdit" name="searchEdit">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>300</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="toolTip">
+        <string>Enter search term for hosts</string>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="0">
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <layout class="QGridLayout" name="gridLayout">
+     <item row="4" column="1">
+      <spacer name="verticalSpacer">
+       <property name="orientation">
+        <enum>Qt::Vertical</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>20</width>
+         <height>40</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item row="0" column="1">
+      <widget class="QPushButton" name="addButton">
+       <property name="toolTip">
+        <string>Press to add site to the whitelist</string>
+       </property>
+       <property name="text">
+        <string>&amp;Add...</string>
+       </property>
+       <property name="autoDefault">
+        <bool>false</bool>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="1">
+      <widget class="Line" name="line">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="1">
+      <widget class="QPushButton" name="removeButton">
+       <property name="toolTip">
+        <string>Press to remove the selected entries</string>
+       </property>
+       <property name="text">
+        <string>&amp;Remove</string>
+       </property>
+       <property name="autoDefault">
+        <bool>false</bool>
+       </property>
+      </widget>
+     </item>
+     <item row="3" column="1">
+      <widget class="QPushButton" name="removeAllButton">
+       <property name="toolTip">
+        <string>Press to remove all entries</string>
+       </property>
+       <property name="text">
+        <string>R&amp;emove All</string>
+       </property>
+       <property name="autoDefault">
+        <bool>false</bool>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="0" rowspan="5">
+      <widget class="E5ListView" name="whitelist">
+       <property name="alternatingRowColors">
+        <bool>true</bool>
+       </property>
+       <property name="selectionMode">
+        <enum>QAbstractItemView::ExtendedSelection</enum>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>E5ClearableLineEdit</class>
+   <extends>QLineEdit</extends>
+   <header>E5Gui/E5LineEdit.h</header>
+  </customwidget>
+  <customwidget>
+   <class>E5ListView</class>
+   <extends>QListView</extends>
+   <header>E5Gui/E5ListView.h</header>
+  </customwidget>
+ </customwidgets>
+ <tabstops>
+  <tabstop>searchEdit</tabstop>
+  <tabstop>whitelist</tabstop>
+  <tabstop>addButton</tabstop>
+  <tabstop>removeButton</tabstop>
+  <tabstop>removeAllButton</tabstop>
+  <tabstop>buttonBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>SendRefererWhitelistDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>227</x>
+     <y>329</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>SendRefererWhitelistDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>295</x>
+     <y>335</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Network/SslErrorExceptionsDialog.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,175 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog to edit the SSL error exceptions.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSlot, Qt, QPoint
+from PyQt5.QtWidgets import QDialog, QTreeWidgetItem, QMenu
+from PyQt5.QtWebEngineWidgets import QWebEngineCertificateError
+
+from .Ui_SslErrorExceptionsDialog import Ui_SslErrorExceptionsDialog
+
+
+class SslErrorExceptionsDialog(QDialog, Ui_SslErrorExceptionsDialog):
+    """
+    Class implementing a dialog to edit the SSL error exceptions.
+    """
+    def __init__(self, errorsDict, parent=None):
+        """
+        Constructor
+        
+        @param errorsDict error exceptions
+        @type dict of list of int
+        @param parent reference to the parent widget
+        @type QWidget
+        """
+        super(SslErrorExceptionsDialog, self).__init__(parent)
+        self.setupUi(self)
+        
+        self.__errorDescriptions = {
+            QWebEngineCertificateError.SslPinnedKeyNotInCertificateChain:
+                self.tr("The certificate did not match the built-in public"
+                        " keys pinned for the host name."),
+            QWebEngineCertificateError.CertificateCommonNameInvalid:
+                self.tr("The certificate's common name did not match the"
+                        " host name."),
+            QWebEngineCertificateError.CertificateDateInvalid:
+                self.tr("The certificate is not valid at the current date"
+                        " and time."),
+            QWebEngineCertificateError.CertificateAuthorityInvalid:
+                self.tr("The certificate is not signed by a trusted"
+                        " authority."),
+            QWebEngineCertificateError.CertificateContainsErrors:
+                self.tr("The certificate contains errors."),
+            QWebEngineCertificateError.CertificateNoRevocationMechanism:
+                self.tr("The certificate has no mechanism for determining if"
+                        " it has been revoked."),
+            QWebEngineCertificateError.CertificateUnableToCheckRevocation:
+                self.tr("Revocation information for the certificate is"
+                        " not available."),
+            QWebEngineCertificateError.CertificateRevoked:
+                self.tr("The certificate has been revoked."),
+            QWebEngineCertificateError.CertificateInvalid:
+                self.tr("The certificate is invalid."),
+            QWebEngineCertificateError.CertificateWeakSignatureAlgorithm:
+                self.tr("The certificate is signed using a weak signature"
+                        " algorithm."),
+            QWebEngineCertificateError.CertificateNonUniqueName:
+                self.tr("The host name specified in the certificate is"
+                        " not unique."),
+            QWebEngineCertificateError.CertificateWeakKey:
+                self.tr("The certificate contains a weak key."),
+            QWebEngineCertificateError.CertificateNameConstraintViolation:
+                self.tr("The certificate claimed DNS names that are in"
+                        " violation of name constraints."),
+        }
+        
+        for host, errors in errorsDict.items():
+            itm = QTreeWidgetItem(self.errorsTree, [host])
+            self.errorsTree.setFirstItemColumnSpanned(itm, True)
+            for error in errors:
+                try:
+                    errorDesc = self.__errorDescriptions[error]
+                except KeyError:
+                    errorDesc = self.tr("No error description available.")
+                QTreeWidgetItem(itm, [str(error), errorDesc])
+        
+        self.errorsTree.expandAll()
+        for i in range(self.errorsTree.columnCount()):
+            self.errorsTree.resizeColumnToContents(i)
+        self.errorsTree.sortItems(0, Qt.AscendingOrder)
+        
+        self.__setRemoveButtons()
+    
+    def __setRemoveButtons(self):
+        """
+        Private method to set the state of the 'remove' buttons.
+        """
+        if self.errorsTree.topLevelItemCount() == 0:
+            self.removeButton.setEnabled(False)
+            self.removeAllButton.setEnabled(False)
+        else:
+            self.removeAllButton.setEnabled(True)
+            self.removeButton.setEnabled(
+                len(self.errorsTree.selectedItems()) > 0)
+    
+    @pyqtSlot(QPoint)
+    def on_errorsTree_customContextMenuRequested(self, pos):
+        """
+        Private slot to show the context menu.
+        
+        @param pos cursor position
+        @type QPoint
+        """
+        menu = QMenu()
+        menu.addAction(
+            self.tr("Remove Selected"),
+            self.on_removeButton_clicked).setEnabled(
+            self.errorsTree.topLevelItemCount() > 0 and
+            len(self.errorsTree.selectedItems()) > 0)
+        menu.addAction(
+            self.tr("Remove All"),
+            self.on_removeAllButton_clicked).setEnabled(
+            self.errorsTree.topLevelItemCount() > 0)
+        
+        menu.exec_(self.errorsTree.mapToGlobal(pos))
+    
+    @pyqtSlot()
+    def on_errorsTree_itemSelectionChanged(self):
+        """
+        Private slot handling the selection of entries.
+        """
+        self.__setRemoveButtons()
+    
+    @pyqtSlot()
+    def on_removeButton_clicked(self):
+        """
+        Private slot to remove the selected items.
+        """
+        for itm in self.errorsTree.selectedItems():
+            pitm = itm.parent()
+            if pitm:
+                pitm.removeChild(itm)
+            else:
+                index = self.errorsTree.indexOfTopLevelItem(itm)
+                self.errorsTree.takeTopLevelItem(index)
+            del itm
+        
+        # remove all hosts without an exception
+        for index in range(self.errorsTree.topLevelItemCount() - 1, -1, -1):
+            itm = self.errorsTree.topLevelItem(index)
+            if itm.childCount() == 0:
+                self.errorsTree.takeTopLevelItem(index)
+                del itm
+    
+    @pyqtSlot()
+    def on_removeAllButton_clicked(self):
+        """
+        Private slot to remove all entries.
+        """
+        self.errorsTree.clear()
+    
+    def getSslErrorExceptions(self):
+        """
+        Public method to retrieve the list of SSL error exceptions.
+        
+        @return error exceptions
+        @rtype dict of list of int
+        """
+        errors = {}
+        
+        for index in range(self.errorsTree.topLevelItemCount()):
+            itm = self.errorsTree.topLevelItem(index)
+            host = itm.text(0)
+            errors[host] = []
+            for cindex in range(itm.childCount()):
+                citm = itm.child(cindex)
+                errors[host].append(int(citm.text(0)))
+        
+        return errors
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Network/SslErrorExceptionsDialog.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,133 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>SslErrorExceptionsDialog</class>
+ <widget class="QDialog" name="SslErrorExceptionsDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>751</width>
+    <height>513</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>SSL Error Exceptions</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QGridLayout" name="gridLayout">
+     <item row="0" column="0" rowspan="3">
+      <widget class="QTreeWidget" name="errorsTree">
+       <property name="contextMenuPolicy">
+        <enum>Qt::CustomContextMenu</enum>
+       </property>
+       <property name="alternatingRowColors">
+        <bool>true</bool>
+       </property>
+       <property name="selectionMode">
+        <enum>QAbstractItemView::ExtendedSelection</enum>
+       </property>
+       <property name="sortingEnabled">
+        <bool>true</bool>
+       </property>
+       <property name="allColumnsShowFocus">
+        <bool>true</bool>
+       </property>
+       <column>
+        <property name="text">
+         <string>Code</string>
+        </property>
+       </column>
+       <column>
+        <property name="text">
+         <string>Error Description</string>
+        </property>
+       </column>
+      </widget>
+     </item>
+     <item row="0" column="1">
+      <widget class="QPushButton" name="removeButton">
+       <property name="toolTip">
+        <string>Press to remove the selected entries</string>
+       </property>
+       <property name="text">
+        <string>&amp;Remove</string>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="1">
+      <widget class="QPushButton" name="removeAllButton">
+       <property name="toolTip">
+        <string>Press to remove all entries</string>
+       </property>
+       <property name="text">
+        <string>Remove &amp;All</string>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="1">
+      <spacer name="verticalSpacer">
+       <property name="orientation">
+        <enum>Qt::Vertical</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>20</width>
+         <height>128</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>SslErrorExceptionsDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>388</x>
+     <y>385</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>399</x>
+     <y>319</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>SslErrorExceptionsDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>349</x>
+     <y>391</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>356</x>
+     <y>286</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Network/UrlInterceptor.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,34 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing an URL interceptor base class.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import QObject
+
+class UrlInterceptor(QObject):
+    """
+    Class implementing an URL interceptor base class.
+    """
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent referemce to the parent object
+        @type QObject
+        """
+        super(UrlInterceptor, self).__init__(parent)
+    
+    def interceptRequest(self, info):
+        """
+        Public method to intercept a request.
+        
+        @param info request info object
+        @type QWebEngineUrlRequestInfo
+        """
+        pass
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Network/__init__.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Package containing network related modules.
+"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/OpenSearch/DefaultSearchEngines/Amazoncom.xml	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
+    <ShortName>Amazon.com</ShortName>
+    <Description>Amazon.com Search</Description>
+    <Url method="get" type="text/html" template="http://www.amazon.com/exec/obidos/external-search/?field-keywords={searchTerms}"/>
+    <Image>http://www.amazon.com/favicon.ico</Image>
+</OpenSearchDescription>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/OpenSearch/DefaultSearchEngines/Bing.xml	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
+    <ShortName>Bing</ShortName>
+    <Description>Bing Web Search</Description>
+    <Url method="get" type="text/html" template="http://www.bing.com/search?cc={language}&amp;q={searchTerms}"/>
+    <Image>http://www.bing.com/s/wlflag.ico</Image>
+</OpenSearchDescription>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/OpenSearch/DefaultSearchEngines/DeEn_Beolingus.xml	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
+    <ShortName>De-En Beolingus</ShortName>
+    <Description>Beolingus: German-English Dictionary</Description>
+    <Url method="get" type="text/html" template="http://dict.tu-chemnitz.de/?query={searchTerms}"/>
+    <Url method="get" type="application/x-suggestions+json" template="http://dict.tu-chemnitz.de/sugg.php?json=1&amp;s={searchTerms}"/>
+    <Image>http://dict.tu-chemnitz.de/pics/beo-de.png</Image>
+</OpenSearchDescription>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/OpenSearch/DefaultSearchEngines/DefaultSearchEngines.qrc	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,21 @@
+<!DOCTYPE RCC>
+<RCC version="1.0">
+<qresource>
+  <file>Amazoncom.xml</file>
+  <file>Bing.xml</file>
+  <file>DeEn_Beolingus.xml</file>
+  <file>DuckDuckGo.xml</file>
+  <file>Facebook.xml</file>
+  <file>Google.xml</file>
+  <file>Google_Im_Feeling_Lucky.xml</file>
+  <file>LEO_DeuEng.xml</file>
+  <file>LinuxMagazin.xml</file>
+  <file>Reddit.xml</file>
+  <file>Wikia.xml</file>
+  <file>Wikia_en.xml</file>
+  <file>Wikipedia.xml</file>
+  <file>Wiktionary.xml</file>
+  <file>Yahoo.xml</file>
+  <file>YouTube.xml</file>
+</qresource>
+</RCC>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/OpenSearch/DefaultSearchEngines/DefaultSearchEngines_rc.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,728 @@
+# -*- coding: utf-8 -*-
+
+# Resource object code
+#
+# Created by: The Resource Compiler for PyQt5 (Qt v5.5.1)
+#
+# WARNING! All changes made in this file will be lost!
+
+from PyQt5 import QtCore
+
+qt_resource_data = b"\
+\x00\x00\x01\x79\
+\x3c\
+\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\
+\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x55\x54\x46\
+\x2d\x38\x22\x3f\x3e\x0a\x3c\x4f\x70\x65\x6e\x53\x65\x61\x72\x63\
+\x68\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\x20\x78\x6d\x6c\
+\x6e\x73\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x61\x39\x2e\x63\x6f\
+\x6d\x2f\x2d\x2f\x73\x70\x65\x63\x2f\x6f\x70\x65\x6e\x73\x65\x61\
+\x72\x63\x68\x2f\x31\x2e\x31\x2f\x22\x3e\x0a\x20\x20\x20\x20\x3c\
+\x53\x68\x6f\x72\x74\x4e\x61\x6d\x65\x3e\x42\x69\x6e\x67\x3c\x2f\
+\x53\x68\x6f\x72\x74\x4e\x61\x6d\x65\x3e\x0a\x20\x20\x20\x20\x3c\
+\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\x3e\x42\x69\x6e\x67\
+\x20\x57\x65\x62\x20\x53\x65\x61\x72\x63\x68\x3c\x2f\x44\x65\x73\
+\x63\x72\x69\x70\x74\x69\x6f\x6e\x3e\x0a\x20\x20\x20\x20\x3c\x55\
+\x72\x6c\x20\x6d\x65\x74\x68\x6f\x64\x3d\x22\x67\x65\x74\x22\x20\
+\x74\x79\x70\x65\x3d\x22\x74\x65\x78\x74\x2f\x68\x74\x6d\x6c\x22\
+\x20\x74\x65\x6d\x70\x6c\x61\x74\x65\x3d\x22\x68\x74\x74\x70\x3a\
+\x2f\x2f\x77\x77\x77\x2e\x62\x69\x6e\x67\x2e\x63\x6f\x6d\x2f\x73\
+\x65\x61\x72\x63\x68\x3f\x63\x63\x3d\x7b\x6c\x61\x6e\x67\x75\x61\
+\x67\x65\x7d\x26\x61\x6d\x70\x3b\x71\x3d\x7b\x73\x65\x61\x72\x63\
+\x68\x54\x65\x72\x6d\x73\x7d\x22\x2f\x3e\x0a\x20\x20\x20\x20\x3c\
+\x49\x6d\x61\x67\x65\x3e\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\
+\x2e\x62\x69\x6e\x67\x2e\x63\x6f\x6d\x2f\x73\x2f\x77\x6c\x66\x6c\
+\x61\x67\x2e\x69\x63\x6f\x3c\x2f\x49\x6d\x61\x67\x65\x3e\x0a\x3c\
+\x2f\x4f\x70\x65\x6e\x53\x65\x61\x72\x63\x68\x44\x65\x73\x63\x72\
+\x69\x70\x74\x69\x6f\x6e\x3e\x0a\
+\x00\x00\x02\x1e\
+\x3c\
+\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\
+\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x55\x54\x46\
+\x2d\x38\x22\x3f\x3e\x0a\x3c\x4f\x70\x65\x6e\x53\x65\x61\x72\x63\
+\x68\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\x20\x78\x6d\x6c\
+\x6e\x73\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x61\x39\x2e\x63\x6f\
+\x6d\x2f\x2d\x2f\x73\x70\x65\x63\x2f\x6f\x70\x65\x6e\x73\x65\x61\
+\x72\x63\x68\x2f\x31\x2e\x31\x2f\x22\x3e\x0a\x20\x20\x20\x20\x3c\
+\x53\x68\x6f\x72\x74\x4e\x61\x6d\x65\x3e\x44\x65\x2d\x45\x6e\x20\
+\x42\x65\x6f\x6c\x69\x6e\x67\x75\x73\x3c\x2f\x53\x68\x6f\x72\x74\
+\x4e\x61\x6d\x65\x3e\x0a\x20\x20\x20\x20\x3c\x44\x65\x73\x63\x72\
+\x69\x70\x74\x69\x6f\x6e\x3e\x42\x65\x6f\x6c\x69\x6e\x67\x75\x73\
+\x3a\x20\x47\x65\x72\x6d\x61\x6e\x2d\x45\x6e\x67\x6c\x69\x73\x68\
+\x20\x44\x69\x63\x74\x69\x6f\x6e\x61\x72\x79\x3c\x2f\x44\x65\x73\
+\x63\x72\x69\x70\x74\x69\x6f\x6e\x3e\x0a\x20\x20\x20\x20\x3c\x55\
+\x72\x6c\x20\x6d\x65\x74\x68\x6f\x64\x3d\x22\x67\x65\x74\x22\x20\
+\x74\x79\x70\x65\x3d\x22\x74\x65\x78\x74\x2f\x68\x74\x6d\x6c\x22\
+\x20\x74\x65\x6d\x70\x6c\x61\x74\x65\x3d\x22\x68\x74\x74\x70\x3a\
+\x2f\x2f\x64\x69\x63\x74\x2e\x74\x75\x2d\x63\x68\x65\x6d\x6e\x69\
+\x74\x7a\x2e\x64\x65\x2f\x3f\x71\x75\x65\x72\x79\x3d\x7b\x73\x65\
+\x61\x72\x63\x68\x54\x65\x72\x6d\x73\x7d\x22\x2f\x3e\x0a\x20\x20\
+\x20\x20\x3c\x55\x72\x6c\x20\x6d\x65\x74\x68\x6f\x64\x3d\x22\x67\
+\x65\x74\x22\x20\x74\x79\x70\x65\x3d\x22\x61\x70\x70\x6c\x69\x63\
+\x61\x74\x69\x6f\x6e\x2f\x78\x2d\x73\x75\x67\x67\x65\x73\x74\x69\
+\x6f\x6e\x73\x2b\x6a\x73\x6f\x6e\x22\x20\x74\x65\x6d\x70\x6c\x61\
+\x74\x65\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x64\x69\x63\x74\x2e\
+\x74\x75\x2d\x63\x68\x65\x6d\x6e\x69\x74\x7a\x2e\x64\x65\x2f\x73\
+\x75\x67\x67\x2e\x70\x68\x70\x3f\x6a\x73\x6f\x6e\x3d\x31\x26\x61\
+\x6d\x70\x3b\x73\x3d\x7b\x73\x65\x61\x72\x63\x68\x54\x65\x72\x6d\
+\x73\x7d\x22\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x49\x6d\x61\x67\x65\
+\x3e\x68\x74\x74\x70\x3a\x2f\x2f\x64\x69\x63\x74\x2e\x74\x75\x2d\
+\x63\x68\x65\x6d\x6e\x69\x74\x7a\x2e\x64\x65\x2f\x70\x69\x63\x73\
+\x2f\x62\x65\x6f\x2d\x64\x65\x2e\x70\x6e\x67\x3c\x2f\x49\x6d\x61\
+\x67\x65\x3e\x0a\x3c\x2f\x4f\x70\x65\x6e\x53\x65\x61\x72\x63\x68\
+\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\x3e\x0a\
+\x00\x00\x02\x64\
+\x3c\
+\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\
+\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x55\x54\x46\
+\x2d\x38\x22\x3f\x3e\x0a\x3c\x4f\x70\x65\x6e\x53\x65\x61\x72\x63\
+\x68\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\x20\x78\x6d\x6c\
+\x6e\x73\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x61\x39\x2e\x63\x6f\
+\x6d\x2f\x2d\x2f\x73\x70\x65\x63\x2f\x6f\x70\x65\x6e\x73\x65\x61\
+\x72\x63\x68\x2f\x31\x2e\x31\x2f\x22\x3e\x0a\x20\x20\x20\x20\x3c\
+\x53\x68\x6f\x72\x74\x4e\x61\x6d\x65\x3e\x47\x6f\x6f\x67\x6c\x65\
+\x20\x28\x49\x27\x6d\x20\x46\x65\x65\x6c\x69\x6e\x67\x20\x4c\x75\
+\x63\x6b\x79\x29\x3c\x2f\x53\x68\x6f\x72\x74\x4e\x61\x6d\x65\x3e\
+\x0a\x20\x20\x20\x20\x3c\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\
+\x6e\x3e\x47\x6f\x6f\x67\x6c\x65\x20\x57\x65\x62\x20\x53\x65\x61\
+\x72\x63\x68\x3c\x2f\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\
+\x3e\x0a\x20\x20\x20\x20\x3c\x55\x72\x6c\x20\x6d\x65\x74\x68\x6f\
+\x64\x3d\x22\x67\x65\x74\x22\x20\x74\x79\x70\x65\x3d\x22\x74\x65\
+\x78\x74\x2f\x68\x74\x6d\x6c\x22\x20\x74\x65\x6d\x70\x6c\x61\x74\
+\x65\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x67\x6f\
+\x6f\x67\x6c\x65\x2e\x63\x6f\x6d\x2f\x73\x65\x61\x72\x63\x68\x3f\
+\x62\x74\x6e\x49\x3d\x26\x61\x6d\x70\x3b\x68\x6c\x3d\x7b\x6c\x61\
+\x6e\x67\x75\x61\x67\x65\x7d\x26\x61\x6d\x70\x3b\x6c\x72\x3d\x6c\
+\x61\x6e\x67\x5f\x7b\x6c\x61\x6e\x67\x75\x61\x67\x65\x7d\x26\x61\
+\x6d\x70\x3b\x71\x3d\x7b\x73\x65\x61\x72\x63\x68\x54\x65\x72\x6d\
+\x73\x7d\x22\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x55\x72\x6c\x20\x6d\
+\x65\x74\x68\x6f\x64\x3d\x22\x67\x65\x74\x22\x20\x74\x79\x70\x65\
+\x3d\x22\x61\x70\x70\x6c\x69\x63\x61\x74\x69\x6f\x6e\x2f\x78\x2d\
+\x73\x75\x67\x67\x65\x73\x74\x69\x6f\x6e\x73\x2b\x6a\x73\x6f\x6e\
+\x22\x20\x74\x65\x6d\x70\x6c\x61\x74\x65\x3d\x22\x68\x74\x74\x70\
+\x3a\x2f\x2f\x73\x75\x67\x67\x65\x73\x74\x71\x75\x65\x72\x69\x65\
+\x73\x2e\x67\x6f\x6f\x67\x6c\x65\x2e\x63\x6f\x6d\x2f\x63\x6f\x6d\
+\x70\x6c\x65\x74\x65\x2f\x73\x65\x61\x72\x63\x68\x3f\x6f\x75\x74\
+\x70\x75\x74\x3d\x66\x69\x72\x65\x66\x6f\x78\x26\x61\x6d\x70\x3b\
+\x68\x6c\x3d\x7b\x6c\x61\x6e\x67\x75\x61\x67\x65\x7d\x26\x61\x6d\
+\x70\x3b\x71\x3d\x7b\x73\x65\x61\x72\x63\x68\x54\x65\x72\x6d\x73\
+\x7d\x22\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x49\x6d\x61\x67\x65\x3e\
+\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x67\x6f\x6f\x67\x6c\
+\x65\x2e\x63\x6f\x6d\x2f\x66\x61\x76\x69\x63\x6f\x6e\x2e\x69\x63\
+\x6f\x3c\x2f\x49\x6d\x61\x67\x65\x3e\x0a\x3c\x2f\x4f\x70\x65\x6e\
+\x53\x65\x61\x72\x63\x68\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\
+\x6e\x3e\x0a\
+\x00\x00\x02\x54\
+\x3c\
+\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\
+\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x55\x54\x46\
+\x2d\x38\x22\x3f\x3e\x0a\x3c\x4f\x70\x65\x6e\x53\x65\x61\x72\x63\
+\x68\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\x20\x78\x6d\x6c\
+\x6e\x73\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x61\x39\x2e\x63\x6f\
+\x6d\x2f\x2d\x2f\x73\x70\x65\x63\x2f\x6f\x70\x65\x6e\x73\x65\x61\
+\x72\x63\x68\x2f\x31\x2e\x31\x2f\x22\x3e\x0a\x20\x20\x20\x20\x3c\
+\x53\x68\x6f\x72\x74\x4e\x61\x6d\x65\x3e\x57\x69\x6b\x69\x61\x3c\
+\x2f\x53\x68\x6f\x72\x74\x4e\x61\x6d\x65\x3e\x0a\x20\x20\x20\x20\
+\x3c\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\x3e\x57\x69\x6b\
+\x69\x61\x20\x53\x69\x74\x65\x20\x53\x65\x61\x72\x63\x68\x3c\x2f\
+\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\x3e\x0a\x20\x20\x20\
+\x20\x3c\x55\x72\x6c\x20\x6d\x65\x74\x68\x6f\x64\x3d\x22\x67\x65\
+\x74\x22\x20\x74\x79\x70\x65\x3d\x22\x74\x65\x78\x74\x2f\x68\x74\
+\x6d\x6c\x22\x20\x74\x65\x6d\x70\x6c\x61\x74\x65\x3d\x22\x68\x74\
+\x74\x70\x3a\x2f\x2f\x7b\x63\x6f\x75\x6e\x74\x72\x79\x7d\x2e\x77\
+\x69\x6b\x69\x61\x2e\x63\x6f\x6d\x2f\x69\x6e\x64\x65\x78\x2e\x70\
+\x68\x70\x3f\x74\x69\x74\x6c\x65\x3d\x53\x70\x65\x63\x69\x61\x6c\
+\x3a\x53\x65\x61\x72\x63\x68\x26\x61\x6d\x70\x3b\x73\x65\x61\x72\
+\x63\x68\x3d\x7b\x73\x65\x61\x72\x63\x68\x54\x65\x72\x6d\x73\x7d\
+\x22\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x55\x72\x6c\x20\x6d\x65\x74\
+\x68\x6f\x64\x3d\x22\x67\x65\x74\x22\x20\x74\x79\x70\x65\x3d\x22\
+\x61\x70\x70\x6c\x69\x63\x61\x74\x69\x6f\x6e\x2f\x78\x2d\x73\x75\
+\x67\x67\x65\x73\x74\x69\x6f\x6e\x73\x2b\x6a\x73\x6f\x6e\x22\x20\
+\x74\x65\x6d\x70\x6c\x61\x74\x65\x3d\x22\x68\x74\x74\x70\x3a\x2f\
+\x2f\x7b\x63\x6f\x75\x6e\x74\x72\x79\x7d\x2e\x77\x69\x6b\x69\x61\
+\x2e\x63\x6f\x6d\x2f\x61\x70\x69\x2e\x70\x68\x70\x3f\x61\x63\x74\
+\x69\x6f\x6e\x3d\x6f\x70\x65\x6e\x73\x65\x61\x72\x63\x68\x26\x61\
+\x6d\x70\x3b\x73\x65\x61\x72\x63\x68\x3d\x7b\x73\x65\x61\x72\x63\
+\x68\x54\x65\x72\x6d\x73\x7d\x26\x61\x6d\x70\x3b\x6e\x61\x6d\x65\
+\x73\x70\x61\x63\x65\x3d\x30\x22\x2f\x3e\x0a\x20\x20\x20\x20\x3c\
+\x49\x6d\x61\x67\x65\x3e\x68\x74\x74\x70\x3a\x2f\x2f\x69\x6d\x61\
+\x67\x65\x73\x2e\x77\x69\x6b\x69\x61\x2e\x63\x6f\x6d\x2f\x77\x69\
+\x6b\x69\x61\x67\x6c\x6f\x62\x61\x6c\x2f\x69\x6d\x61\x67\x65\x73\
+\x2f\x36\x2f\x36\x34\x2f\x46\x61\x76\x69\x63\x6f\x6e\x2e\x69\x63\
+\x6f\x3c\x2f\x49\x6d\x61\x67\x65\x3e\x0a\x3c\x2f\x4f\x70\x65\x6e\
+\x53\x65\x61\x72\x63\x68\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\
+\x6e\x3e\x0a\
+\x00\x00\x02\x27\
+\x3c\
+\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\
+\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x55\x54\x46\
+\x2d\x38\x22\x3f\x3e\x0a\x3c\x4f\x70\x65\x6e\x53\x65\x61\x72\x63\
+\x68\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\x20\x78\x6d\x6c\
+\x6e\x73\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x61\x39\x2e\x63\x6f\
+\x6d\x2f\x2d\x2f\x73\x70\x65\x63\x2f\x6f\x70\x65\x6e\x73\x65\x61\
+\x72\x63\x68\x2f\x31\x2e\x31\x2f\x22\x3e\x0a\x20\x20\x20\x20\x3c\
+\x53\x68\x6f\x72\x74\x4e\x61\x6d\x65\x3e\x59\x61\x68\x6f\x6f\x21\
+\x3c\x2f\x53\x68\x6f\x72\x74\x4e\x61\x6d\x65\x3e\x0a\x20\x20\x20\
+\x20\x3c\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\x3e\x59\x61\
+\x68\x6f\x6f\x20\x57\x65\x62\x20\x53\x65\x61\x72\x63\x68\x3c\x2f\
+\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\x3e\x0a\x20\x20\x20\
+\x20\x3c\x55\x72\x6c\x20\x6d\x65\x74\x68\x6f\x64\x3d\x22\x67\x65\
+\x74\x22\x20\x74\x79\x70\x65\x3d\x22\x74\x65\x78\x74\x2f\x68\x74\
+\x6d\x6c\x22\x20\x74\x65\x6d\x70\x6c\x61\x74\x65\x3d\x22\x68\x74\
+\x74\x70\x3a\x2f\x2f\x73\x65\x61\x72\x63\x68\x2e\x79\x61\x68\x6f\
+\x6f\x2e\x63\x6f\x6d\x2f\x73\x65\x61\x72\x63\x68\x3f\x65\x69\x3d\
+\x75\x74\x66\x2d\x38\x26\x61\x6d\x70\x3b\x66\x72\x3d\x73\x66\x70\
+\x26\x61\x6d\x70\x3b\x69\x73\x63\x71\x72\x79\x3d\x26\x61\x6d\x70\
+\x3b\x70\x3d\x7b\x73\x65\x61\x72\x63\x68\x54\x65\x72\x6d\x73\x7d\
+\x22\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x55\x72\x6c\x20\x6d\x65\x74\
+\x68\x6f\x64\x3d\x22\x67\x65\x74\x22\x20\x74\x79\x70\x65\x3d\x22\
+\x61\x70\x70\x6c\x69\x63\x61\x74\x69\x6f\x6e\x2f\x78\x2d\x73\x75\
+\x67\x67\x65\x73\x74\x69\x6f\x6e\x73\x2b\x6a\x73\x6f\x6e\x22\x20\
+\x74\x65\x6d\x70\x6c\x61\x74\x65\x3d\x22\x68\x74\x74\x70\x3a\x2f\
+\x2f\x66\x66\x2e\x73\x65\x61\x72\x63\x68\x2e\x79\x61\x68\x6f\x6f\
+\x2e\x63\x6f\x6d\x2f\x67\x6f\x73\x73\x69\x70\x3f\x6f\x75\x74\x70\
+\x75\x74\x3d\x66\x78\x6a\x73\x6f\x6e\x26\x61\x6d\x70\x3b\x63\x6f\
+\x6d\x6d\x61\x6e\x64\x3d\x7b\x73\x65\x61\x72\x63\x68\x54\x65\x72\
+\x6d\x73\x7d\x22\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x49\x6d\x61\x67\
+\x65\x3e\x68\x74\x74\x70\x3a\x2f\x2f\x6d\x2e\x77\x77\x77\x2e\x79\
+\x61\x68\x6f\x6f\x2e\x63\x6f\x6d\x2f\x66\x61\x76\x69\x63\x6f\x6e\
+\x2e\x69\x63\x6f\x3c\x2f\x49\x6d\x61\x67\x65\x3e\x0a\x3c\x2f\x4f\
+\x70\x65\x6e\x53\x65\x61\x72\x63\x68\x44\x65\x73\x63\x72\x69\x70\
+\x74\x69\x6f\x6e\x3e\x0a\
+\x00\x00\x01\x6f\
+\x3c\
+\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\
+\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x55\x54\x46\
+\x2d\x38\x22\x3f\x3e\x0a\x3c\x4f\x70\x65\x6e\x53\x65\x61\x72\x63\
+\x68\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\x20\x78\x6d\x6c\
+\x6e\x73\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x61\x39\x2e\x63\x6f\
+\x6d\x2f\x2d\x2f\x73\x70\x65\x63\x2f\x6f\x70\x65\x6e\x73\x65\x61\
+\x72\x63\x68\x2f\x31\x2e\x31\x2f\x22\x3e\x0a\x20\x20\x20\x20\x3c\
+\x53\x68\x6f\x72\x74\x4e\x61\x6d\x65\x3e\x52\x65\x64\x64\x69\x74\
+\x3c\x2f\x53\x68\x6f\x72\x74\x4e\x61\x6d\x65\x3e\x0a\x20\x20\x20\
+\x20\x3c\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\x3e\x52\x65\
+\x64\x64\x69\x74\x20\x53\x69\x74\x65\x20\x53\x65\x61\x72\x63\x68\
+\x3c\x2f\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\x3e\x0a\x20\
+\x20\x20\x20\x3c\x55\x72\x6c\x20\x6d\x65\x74\x68\x6f\x64\x3d\x22\
+\x67\x65\x74\x22\x20\x74\x79\x70\x65\x3d\x22\x74\x65\x78\x74\x2f\
+\x68\x74\x6d\x6c\x22\x20\x74\x65\x6d\x70\x6c\x61\x74\x65\x3d\x22\
+\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x72\x65\x64\x64\x69\
+\x74\x2e\x63\x6f\x6d\x2f\x73\x65\x61\x72\x63\x68\x3f\x71\x3d\x7b\
+\x73\x65\x61\x72\x63\x68\x54\x65\x72\x6d\x73\x7d\x22\x2f\x3e\x0a\
+\x20\x20\x20\x20\x3c\x49\x6d\x61\x67\x65\x3e\x68\x74\x74\x70\x3a\
+\x2f\x2f\x77\x77\x77\x2e\x72\x65\x64\x64\x69\x74\x2e\x63\x6f\x6d\
+\x2f\x66\x61\x76\x69\x63\x6f\x6e\x2e\x69\x63\x6f\x3c\x2f\x49\x6d\
+\x61\x67\x65\x3e\x0a\x3c\x2f\x4f\x70\x65\x6e\x53\x65\x61\x72\x63\
+\x68\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\x3e\x0a\
+\x00\x00\x02\x7a\
+\x3c\
+\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\
+\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x55\x54\x46\
+\x2d\x38\x22\x3f\x3e\x0a\x3c\x4f\x70\x65\x6e\x53\x65\x61\x72\x63\
+\x68\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\x20\x78\x6d\x6c\
+\x6e\x73\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x61\x39\x2e\x63\x6f\
+\x6d\x2f\x2d\x2f\x73\x70\x65\x63\x2f\x6f\x70\x65\x6e\x73\x65\x61\
+\x72\x63\x68\x2f\x31\x2e\x31\x2f\x22\x3e\x0a\x20\x20\x20\x20\x3c\
+\x53\x68\x6f\x72\x74\x4e\x61\x6d\x65\x3e\x4c\x45\x4f\x20\x44\x65\
+\x75\x2d\x45\x6e\x67\x3c\x2f\x53\x68\x6f\x72\x74\x4e\x61\x6d\x65\
+\x3e\x0a\x20\x20\x20\x20\x3c\x44\x65\x73\x63\x72\x69\x70\x74\x69\
+\x6f\x6e\x3e\x44\x65\x75\x74\x73\x63\x68\x2d\x45\x6e\x67\x6c\x69\
+\x73\x63\x68\x20\x57\xc3\xb6\x72\x74\x65\x72\x62\x75\x63\x68\x20\
+\x76\x6f\x6e\x20\x4c\x45\x4f\x3c\x2f\x44\x65\x73\x63\x72\x69\x70\
+\x74\x69\x6f\x6e\x3e\x0a\x20\x20\x20\x20\x3c\x55\x72\x6c\x20\x6d\
+\x65\x74\x68\x6f\x64\x3d\x22\x67\x65\x74\x22\x20\x74\x79\x70\x65\
+\x3d\x22\x74\x65\x78\x74\x2f\x68\x74\x6d\x6c\x22\x20\x74\x65\x6d\
+\x70\x6c\x61\x74\x65\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x64\x69\
+\x63\x74\x2e\x6c\x65\x6f\x2e\x6f\x72\x67\x2f\x65\x6e\x64\x65\x3f\
+\x6c\x61\x6e\x67\x3d\x64\x65\x26\x61\x6d\x70\x3b\x73\x65\x61\x72\
+\x63\x68\x3d\x7b\x73\x65\x61\x72\x63\x68\x54\x65\x72\x6d\x73\x7d\
+\x22\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x55\x72\x6c\x20\x6d\x65\x74\
+\x68\x6f\x64\x3d\x22\x67\x65\x74\x22\x20\x74\x79\x70\x65\x3d\x22\
+\x61\x70\x70\x6c\x69\x63\x61\x74\x69\x6f\x6e\x2f\x78\x2d\x73\x75\
+\x67\x67\x65\x73\x74\x69\x6f\x6e\x73\x2b\x6a\x73\x6f\x6e\x22\x20\
+\x74\x65\x6d\x70\x6c\x61\x74\x65\x3d\x22\x68\x74\x74\x70\x3a\x2f\
+\x2f\x64\x69\x63\x74\x2e\x6c\x65\x6f\x2e\x6f\x72\x67\x2f\x64\x69\
+\x63\x74\x51\x75\x65\x72\x79\x2f\x6d\x2d\x71\x75\x65\x72\x79\x2f\
+\x63\x6f\x6e\x66\x2f\x65\x6e\x64\x65\x2f\x71\x75\x65\x72\x79\x2e\
+\x63\x6f\x6e\x66\x2f\x73\x74\x72\x6c\x69\x73\x74\x2e\x6a\x73\x6f\
+\x6e\x3f\x71\x3d\x7b\x73\x65\x61\x72\x63\x68\x54\x65\x72\x6d\x73\
+\x7d\x26\x61\x6d\x70\x3b\x73\x6f\x72\x74\x3d\x50\x4c\x61\x26\x61\
+\x6d\x70\x3b\x73\x68\x6f\x72\x74\x51\x75\x65\x72\x79\x26\x61\x6d\
+\x70\x3b\x6e\x6f\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\x26\
+\x61\x6d\x70\x3b\x6e\x6f\x51\x75\x65\x72\x79\x55\x52\x4c\x73\x22\
+\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x49\x6d\x61\x67\x65\x3e\x68\x74\
+\x74\x70\x3a\x2f\x2f\x64\x69\x63\x74\x2e\x6c\x65\x6f\x2e\x6f\x72\
+\x67\x2f\x69\x6d\x67\x2f\x66\x61\x76\x69\x63\x6f\x6e\x73\x2f\x65\
+\x6e\x64\x65\x2e\x69\x63\x6f\x3c\x2f\x49\x6d\x61\x67\x65\x3e\x0a\
+\x3c\x2f\x4f\x70\x65\x6e\x53\x65\x61\x72\x63\x68\x44\x65\x73\x63\
+\x72\x69\x70\x74\x69\x6f\x6e\x3e\x0a\
+\x00\x00\x01\x85\
+\x3c\
+\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\
+\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x55\x54\x46\
+\x2d\x38\x22\x3f\x3e\x0a\x3c\x4f\x70\x65\x6e\x53\x65\x61\x72\x63\
+\x68\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\x20\x78\x6d\x6c\
+\x6e\x73\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x61\x39\x2e\x63\x6f\
+\x6d\x2f\x2d\x2f\x73\x70\x65\x63\x2f\x6f\x70\x65\x6e\x73\x65\x61\
+\x72\x63\x68\x2f\x31\x2e\x31\x2f\x22\x3e\x0a\x20\x20\x20\x20\x3c\
+\x53\x68\x6f\x72\x74\x4e\x61\x6d\x65\x3e\x59\x6f\x75\x54\x75\x62\
+\x65\x3c\x2f\x53\x68\x6f\x72\x74\x4e\x61\x6d\x65\x3e\x0a\x20\x20\
+\x20\x20\x3c\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\x3e\x59\
+\x6f\x75\x54\x75\x62\x65\x3c\x2f\x44\x65\x73\x63\x72\x69\x70\x74\
+\x69\x6f\x6e\x3e\x0a\x20\x20\x20\x20\x3c\x55\x72\x6c\x20\x6d\x65\
+\x74\x68\x6f\x64\x3d\x22\x67\x65\x74\x22\x20\x74\x79\x70\x65\x3d\
+\x22\x74\x65\x78\x74\x2f\x68\x74\x6d\x6c\x22\x20\x74\x65\x6d\x70\
+\x6c\x61\x74\x65\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\
+\x2e\x79\x6f\x75\x74\x75\x62\x65\x2e\x63\x6f\x6d\x2f\x72\x65\x73\
+\x75\x6c\x74\x73\x3f\x73\x65\x61\x72\x63\x68\x5f\x71\x75\x65\x72\
+\x79\x3d\x7b\x73\x65\x61\x72\x63\x68\x54\x65\x72\x6d\x73\x7d\x26\
+\x61\x6d\x70\x3b\x73\x65\x61\x72\x63\x68\x3d\x53\x65\x61\x72\x63\
+\x68\x22\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x49\x6d\x61\x67\x65\x3e\
+\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x79\x6f\x75\x74\x75\
+\x62\x65\x2e\x63\x6f\x6d\x2f\x66\x61\x76\x69\x63\x6f\x6e\x2e\x69\
+\x63\x6f\x3c\x2f\x49\x6d\x61\x67\x65\x3e\x0a\x3c\x2f\x4f\x70\x65\
+\x6e\x53\x65\x61\x72\x63\x68\x44\x65\x73\x63\x72\x69\x70\x74\x69\
+\x6f\x6e\x3e\x0a\
+\x00\x00\x06\xfe\
+\x3c\
+\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\
+\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x55\x54\x46\
+\x2d\x38\x22\x3f\x3e\x0a\x3c\x4f\x70\x65\x6e\x53\x65\x61\x72\x63\
+\x68\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\x20\x78\x6d\x6c\
+\x6e\x73\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x61\x39\x2e\x63\x6f\
+\x6d\x2f\x2d\x2f\x73\x70\x65\x63\x2f\x6f\x70\x65\x6e\x73\x65\x61\
+\x72\x63\x68\x2f\x31\x2e\x31\x2f\x22\x3e\x0a\x20\x20\x20\x20\x3c\
+\x53\x68\x6f\x72\x74\x4e\x61\x6d\x65\x3e\x44\x75\x63\x6b\x44\x75\
+\x63\x6b\x47\x6f\x3c\x2f\x53\x68\x6f\x72\x74\x4e\x61\x6d\x65\x3e\
+\x0a\x20\x20\x20\x20\x3c\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\
+\x6e\x3e\x53\x65\x61\x72\x63\x68\x20\x44\x75\x63\x6b\x44\x75\x63\
+\x6b\x47\x6f\x3c\x2f\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\
+\x3e\x0a\x20\x20\x20\x20\x3c\x55\x72\x6c\x20\x6d\x65\x74\x68\x6f\
+\x64\x3d\x22\x67\x65\x74\x22\x20\x74\x79\x70\x65\x3d\x22\x74\x65\
+\x78\x74\x2f\x68\x74\x6d\x6c\x22\x20\x74\x65\x6d\x70\x6c\x61\x74\
+\x65\x3d\x22\x68\x74\x74\x70\x73\x3a\x2f\x2f\x64\x75\x63\x6b\x64\
+\x75\x63\x6b\x67\x6f\x2e\x63\x6f\x6d\x2f\x3f\x71\x3d\x7b\x73\x65\
+\x61\x72\x63\x68\x54\x65\x72\x6d\x73\x7d\x22\x2f\x3e\x0a\x20\x20\
+\x20\x20\x3c\x55\x72\x6c\x20\x6d\x65\x74\x68\x6f\x64\x3d\x22\x67\
+\x65\x74\x22\x20\x74\x79\x70\x65\x3d\x22\x61\x70\x70\x6c\x69\x63\
+\x61\x74\x69\x6f\x6e\x2f\x78\x2d\x73\x75\x67\x67\x65\x73\x74\x69\
+\x6f\x6e\x73\x2b\x6a\x73\x6f\x6e\x22\x20\x74\x65\x6d\x70\x6c\x61\
+\x74\x65\x3d\x22\x68\x74\x74\x70\x73\x3a\x2f\x2f\x61\x63\x2e\x64\
+\x75\x63\x6b\x64\x75\x63\x6b\x67\x6f\x2e\x63\x6f\x6d\x2f\x61\x63\
+\x2f\x3f\x71\x3d\x7b\x73\x65\x61\x72\x63\x68\x54\x65\x72\x6d\x73\
+\x7d\x26\x61\x6d\x70\x3b\x74\x79\x70\x65\x3d\x6c\x69\x73\x74\x22\
+\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x49\x6d\x61\x67\x65\x3e\x64\x61\
+\x74\x61\x3a\x69\x6d\x61\x67\x65\x2f\x78\x2d\x69\x63\x6f\x6e\x3b\
+\x62\x61\x73\x65\x36\x34\x2c\x69\x56\x42\x4f\x52\x77\x30\x4b\x47\
+\x67\x6f\x41\x41\x41\x41\x4e\x53\x55\x68\x45\x55\x67\x41\x41\x41\
+\x42\x41\x41\x41\x41\x41\x51\x43\x41\x4d\x41\x41\x41\x41\x6f\x4c\
+\x51\x39\x54\x41\x41\x41\x41\x42\x47\x64\x42\x54\x55\x45\x41\x41\
+\x4c\x47\x50\x43\x2f\x78\x68\x42\x51\x41\x41\x41\x43\x42\x6a\x53\
+\x46\x4a\x4e\x41\x41\x42\x36\x4a\x67\x41\x41\x67\x49\x51\x41\x41\
+\x50\x6f\x41\x41\x41\x43\x41\x36\x41\x41\x41\x64\x54\x41\x41\x41\
+\x4f\x70\x67\x41\x41\x41\x36\x6d\x41\x41\x41\x46\x33\x43\x63\x75\
+\x6c\x45\x38\x41\x41\x41\x42\x38\x6c\x42\x4d\x56\x45\x55\x41\x41\
+\x41\x44\x6b\x52\x51\x7a\x6a\x50\x77\x50\x6a\x51\x51\x58\x6b\x52\
+\x51\x33\x69\x50\x77\x54\x69\x51\x51\x58\x67\x50\x51\x50\x65\x51\
+\x67\x72\x63\x4f\x77\x50\x56\x4e\x67\x44\x56\x4e\x51\x44\x57\x4f\
+\x67\x62\x54\x4d\x77\x44\x52\x4d\x67\x44\x51\x4d\x77\x44\x53\x4d\
+\x77\x44\x52\x4e\x77\x54\x51\x4c\x67\x44\x52\x4a\x67\x44\x53\x4a\
+\x77\x44\x53\x4c\x67\x44\x53\x4e\x77\x54\x6a\x4f\x67\x44\x69\x4f\
+\x41\x44\x6a\x4f\x51\x44\x6b\x50\x41\x44\x68\x51\x41\x58\x7a\x73\
+\x35\x76\x2b\x2f\x66\x76\x2f\x2f\x2f\x2f\x30\x76\x4b\x62\x69\x52\
+\x51\x76\x67\x50\x51\x48\x70\x64\x55\x72\x38\x35\x4e\x7a\x75\x6b\
+\x6e\x50\x64\x4b\x67\x44\x63\x49\x77\x44\x6e\x5a\x7a\x6a\x32\x77\
+\x37\x48\x71\x65\x55\x2f\x67\x50\x51\x4c\x73\x69\x6d\x62\x2f\x2b\
+\x50\x66\x74\x6a\x57\x6e\x39\x37\x4f\x62\x70\x62\x30\x4c\x64\x4a\
+\x51\x44\x65\x4c\x51\x44\x74\x6a\x6d\x76\x73\x69\x32\x6a\x67\x53\
+\x42\x44\x6e\x62\x55\x4c\x67\x4f\x51\x44\x2f\x33\x39\x48\x67\x4c\
+\x51\x44\x65\x4d\x67\x44\x70\x65\x46\x4c\x67\x53\x42\x48\x30\x76\
+\x36\x37\x30\x75\x71\x62\x61\x4a\x51\x44\x32\x71\x49\x6d\x57\x76\
+\x50\x2f\x47\x31\x4f\x62\x35\x2b\x2f\x33\x75\x2f\x2f\x2b\x66\x76\
+\x76\x58\x79\x70\x34\x37\x64\x4d\x77\x44\x61\x4c\x77\x44\x30\x75\
+\x36\x76\x30\x76\x36\x2f\x61\x4e\x51\x44\x69\x58\x69\x2f\x61\x4b\
+\x51\x44\x33\x71\x6f\x7a\x55\x37\x2f\x38\x67\x53\x59\x32\x76\x76\
+\x74\x67\x30\x5a\x4b\x2f\x4f\x71\x4c\x44\x61\x4b\x51\x48\x59\x4b\
+\x67\x4c\x67\x57\x54\x66\x61\x4e\x41\x44\x5a\x4d\x67\x44\x5a\x4d\
+\x41\x44\x5a\x4c\x41\x44\x7a\x71\x70\x44\x37\x2f\x2f\x2b\x78\x77\
+\x64\x7a\x2f\x2f\x39\x48\x2f\x35\x42\x6e\x2f\x37\x42\x6e\x2f\x2f\
+\x41\x44\x6f\x66\x41\x44\x59\x4d\x41\x44\x59\x4d\x51\x44\x5a\x4f\
+\x67\x50\x58\x4c\x67\x44\x69\x5a\x44\x6a\x2f\x2f\x39\x37\x2f\x30\
+\x41\x44\x33\x74\x51\x44\x76\x6c\x67\x48\x5a\x4f\x67\x62\x58\x4c\
+\x41\x54\x58\x4d\x41\x44\x57\x4d\x67\x44\x66\x58\x6a\x4c\x56\x4c\
+\x51\x44\x2f\x2f\x2f\x7a\x2b\x30\x41\x44\x2f\x33\x52\x6e\x2f\x79\
+\x52\x6e\x77\x6e\x51\x44\x63\x56\x6a\x62\x56\x4d\x51\x44\x79\x76\
+\x36\x37\x77\x75\x4b\x54\x53\x4a\x77\x44\x52\x48\x51\x44\x2b\x38\
+\x4f\x2f\x74\x67\x33\x2f\x69\x51\x51\x44\x77\x68\x41\x48\x6e\x61\
+\x77\x48\x57\x4d\x41\x44\x76\x74\x4b\x66\x79\x76\x61\x37\x58\x51\
+\x78\x48\x67\x61\x30\x62\x51\x47\x51\x44\x32\x76\x62\x48\x2f\x75\
+\x38\x4c\x58\x49\x51\x43\x6d\x50\x51\x7a\x6a\x61\x30\x37\x58\x51\
+\x78\x4c\x6c\x69\x47\x6e\x39\x39\x66\x50\x6b\x63\x56\x48\x76\x68\
+\x6e\x47\x5a\x35\x56\x67\x75\x76\x55\x55\x35\x77\x6b\x74\x42\x77\
+\x43\x63\x41\x67\x78\x7a\x79\x64\x56\x76\x2f\x38\x2f\x58\x6d\x69\
+\x47\x6e\x67\x64\x6c\x4c\x2b\x79\x73\x69\x33\x2b\x49\x38\x4c\x74\
+\x43\x45\x38\x30\x56\x36\x50\x33\x59\x6d\x58\x34\x73\x44\x6c\x65\
+\x6c\x6a\x53\x4e\x51\x4c\x7a\x72\x36\x44\x37\x73\x4b\x50\x58\x4e\
+\x51\x54\x53\x49\x77\x41\x45\x41\x62\x4d\x72\x41\x41\x41\x41\x46\
+\x33\x52\x53\x54\x6c\x4d\x41\x52\x71\x53\x6b\x52\x76\x50\x7a\x38\
+\x30\x50\x54\x70\x4b\x52\x47\x33\x66\x50\x65\x33\x68\x69\x6f\x39\
+\x2f\x65\x6f\x47\x50\x35\x30\x6a\x4e\x73\x41\x41\x41\x41\x42\x59\
+\x6b\x74\x48\x52\x42\x35\x79\x43\x69\x41\x72\x41\x41\x41\x41\x79\
+\x45\x6c\x45\x51\x56\x51\x59\x47\x51\x58\x42\x76\x55\x71\x43\x59\
+\x52\x69\x41\x34\x66\x75\x32\x56\x39\x54\x6e\x2b\x55\x51\x64\x64\
+\x49\x33\x61\x43\x70\x78\x61\x4f\x6f\x55\x36\x69\x55\x34\x67\x63\
+\x71\x71\x70\x6f\x59\x62\x41\x4c\x58\x42\x75\x43\x75\x6f\x59\x6d\
+\x74\x74\x61\x6d\x71\x4a\x44\x69\x45\x6f\x68\x34\x59\x50\x2b\x4d\
+\x4f\x69\x36\x42\x4e\x43\x68\x2b\x75\x59\x4b\x45\x47\x69\x4f\x56\
+\x4e\x43\x58\x58\x78\x41\x32\x58\x44\x56\x56\x2f\x55\x79\x66\x4b\
+\x62\x52\x43\x58\x54\x4c\x51\x57\x41\x78\x62\x50\x32\x76\x74\x38\
+\x55\x65\x2f\x75\x59\x44\x76\x66\x69\x6d\x39\x31\x36\x31\x35\x73\
+\x62\x32\x75\x6d\x36\x72\x71\x74\x72\x72\x2f\x4e\x46\x62\x31\x63\
+\x55\x66\x31\x59\x62\x64\x30\x36\x61\x72\x65\x55\x36\x6c\x53\x6c\
+\x59\x70\x4b\x37\x39\x6a\x7a\x4b\x31\x53\x79\x4a\x4f\x6b\x66\x68\
+\x4f\x6c\x38\x4a\x47\x45\x63\x71\x56\x35\x7a\x6f\x4b\x72\x54\x52\
+\x71\x4f\x36\x79\x55\x7a\x49\x7a\x4e\x75\x34\x36\x69\x6a\x64\x4d\
+\x31\x56\x56\x39\x62\x68\x75\x55\x4a\x2f\x6e\x5a\x55\x52\x45\x78\
+\x4c\x52\x7a\x55\x69\x50\x51\x6d\x33\x6b\x4b\x58\x48\x69\x34\x42\
+\x41\x45\x47\x4f\x6d\x4f\x69\x37\x38\x41\x2f\x4c\x31\x51\x6f\x55\
+\x2f\x56\x48\x6f\x54\x73\x41\x41\x41\x41\x6c\x64\x45\x56\x59\x64\
+\x47\x52\x68\x64\x47\x55\x36\x59\x33\x4a\x6c\x59\x58\x52\x6c\x41\
+\x44\x49\x77\x4d\x54\x51\x74\x4d\x44\x45\x74\x4d\x54\x6c\x55\x4d\
+\x6a\x41\x36\x4d\x44\x45\x36\x4d\x54\x45\x74\x4d\x44\x55\x36\x4d\
+\x44\x41\x75\x45\x54\x36\x63\x41\x41\x41\x41\x4a\x58\x52\x46\x57\
+\x48\x52\x6b\x59\x58\x52\x6c\x4f\x6d\x31\x76\x5a\x47\x6c\x6d\x65\
+\x51\x41\x79\x4d\x44\x45\x30\x4c\x54\x41\x78\x4c\x54\x45\x35\x56\
+\x44\x49\x77\x4f\x6a\x41\x78\x4f\x6a\x45\x78\x4c\x54\x41\x31\x4f\
+\x6a\x41\x77\x58\x30\x79\x47\x49\x41\x41\x41\x41\x41\x42\x4a\x52\
+\x55\x35\x45\x72\x6b\x4a\x67\x67\x67\x3d\x3d\x3c\x2f\x49\x6d\x61\
+\x67\x65\x3e\x0a\x3c\x2f\x4f\x70\x65\x6e\x53\x65\x61\x72\x63\x68\
+\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\x3e\x0a\
+\x00\x00\x01\x7e\
+\x3c\
+\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\
+\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x55\x54\x46\
+\x2d\x38\x22\x3f\x3e\x0a\x3c\x4f\x70\x65\x6e\x53\x65\x61\x72\x63\
+\x68\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\x20\x78\x6d\x6c\
+\x6e\x73\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x61\x39\x2e\x63\x6f\
+\x6d\x2f\x2d\x2f\x73\x70\x65\x63\x2f\x6f\x70\x65\x6e\x73\x65\x61\
+\x72\x63\x68\x2f\x31\x2e\x31\x2f\x22\x3e\x0a\x20\x20\x20\x20\x3c\
+\x53\x68\x6f\x72\x74\x4e\x61\x6d\x65\x3e\x46\x61\x63\x65\x62\x6f\
+\x6f\x6b\x3c\x2f\x53\x68\x6f\x72\x74\x4e\x61\x6d\x65\x3e\x0a\x20\
+\x20\x20\x20\x3c\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\x3e\
+\x53\x65\x61\x72\x63\x68\x20\x46\x61\x63\x65\x62\x6f\x6f\x6b\x3c\
+\x2f\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\x3e\x0a\x20\x20\
+\x20\x20\x3c\x55\x72\x6c\x20\x6d\x65\x74\x68\x6f\x64\x3d\x22\x67\
+\x65\x74\x22\x20\x74\x79\x70\x65\x3d\x22\x74\x65\x78\x74\x2f\x68\
+\x74\x6d\x6c\x22\x20\x74\x65\x6d\x70\x6c\x61\x74\x65\x3d\x22\x68\
+\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x66\x61\x63\x65\x62\x6f\
+\x6f\x6b\x2e\x63\x6f\x6d\x2f\x73\x65\x61\x72\x63\x68\x2f\x3f\x73\
+\x72\x63\x3d\x6f\x73\x26\x61\x6d\x70\x3b\x71\x3d\x7b\x73\x65\x61\
+\x72\x63\x68\x54\x65\x72\x6d\x73\x7d\x22\x2f\x3e\x0a\x20\x20\x20\
+\x20\x3c\x49\x6d\x61\x67\x65\x3e\x68\x74\x74\x70\x3a\x2f\x2f\x77\
+\x77\x77\x2e\x66\x61\x63\x65\x62\x6f\x6f\x6b\x2e\x63\x6f\x6d\x2f\
+\x66\x61\x76\x69\x63\x6f\x6e\x2e\x69\x63\x6f\x3c\x2f\x49\x6d\x61\
+\x67\x65\x3e\x0a\x3c\x2f\x4f\x70\x65\x6e\x53\x65\x61\x72\x63\x68\
+\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\x3e\x0a\
+\x00\x00\x01\xc9\
+\x3c\
+\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\
+\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x55\x54\x46\
+\x2d\x38\x22\x3f\x3e\x0a\x3c\x4f\x70\x65\x6e\x53\x65\x61\x72\x63\
+\x68\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\x20\x78\x6d\x6c\
+\x6e\x73\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x61\x39\x2e\x63\x6f\
+\x6d\x2f\x2d\x2f\x73\x70\x65\x63\x2f\x6f\x70\x65\x6e\x73\x65\x61\
+\x72\x63\x68\x2f\x31\x2e\x31\x2f\x22\x3e\x0a\x20\x20\x20\x20\x3c\
+\x53\x68\x6f\x72\x74\x4e\x61\x6d\x65\x3e\x4c\x69\x6e\x75\x78\x2d\
+\x4d\x61\x67\x61\x7a\x69\x6e\x3c\x2f\x53\x68\x6f\x72\x74\x4e\x61\
+\x6d\x65\x3e\x0a\x20\x20\x20\x20\x3c\x44\x65\x73\x63\x72\x69\x70\
+\x74\x69\x6f\x6e\x3e\x53\x75\x63\x68\x65\x20\x61\x75\x66\x20\x77\
+\x77\x77\x2e\x6c\x69\x6e\x75\x78\x2d\x6d\x61\x67\x61\x7a\x69\x6e\
+\x2e\x64\x65\x3c\x2f\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\
+\x3e\x0a\x20\x20\x20\x20\x3c\x55\x72\x6c\x20\x6d\x65\x74\x68\x6f\
+\x64\x3d\x22\x67\x65\x74\x22\x20\x74\x79\x70\x65\x3d\x22\x74\x65\
+\x78\x74\x2f\x68\x74\x6d\x6c\x22\x20\x74\x65\x6d\x70\x6c\x61\x74\
+\x65\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x6c\x69\
+\x6e\x75\x78\x2d\x6d\x61\x67\x61\x7a\x69\x6e\x2e\x64\x65\x2f\x63\
+\x6f\x6e\x74\x65\x6e\x74\x2f\x73\x65\x61\x72\x63\x68\x3f\x53\x65\
+\x61\x72\x63\x68\x54\x65\x78\x74\x3d\x7b\x73\x65\x61\x72\x63\x68\
+\x54\x65\x72\x6d\x73\x7d\x22\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x49\
+\x6d\x61\x67\x65\x3e\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\
+\x6c\x69\x6e\x75\x78\x2d\x6d\x61\x67\x61\x7a\x69\x6e\x2e\x64\x65\
+\x2f\x65\x78\x74\x65\x6e\x73\x69\x6f\x6e\x2f\x6c\x6e\x6d\x2f\x64\
+\x65\x73\x69\x67\x6e\x2f\x6c\x69\x6e\x75\x78\x5f\x6d\x61\x67\x61\
+\x7a\x69\x6e\x2f\x69\x6d\x61\x67\x65\x73\x2f\x66\x61\x76\x69\x63\
+\x6f\x6e\x2e\x69\x63\x6f\x3c\x2f\x49\x6d\x61\x67\x65\x3e\x0a\x3c\
+\x2f\x4f\x70\x65\x6e\x53\x65\x61\x72\x63\x68\x44\x65\x73\x63\x72\
+\x69\x70\x74\x69\x6f\x6e\x3e\x0a\
+\x00\x00\x01\x95\
+\x3c\
+\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\
+\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x55\x54\x46\
+\x2d\x38\x22\x3f\x3e\x0a\x3c\x4f\x70\x65\x6e\x53\x65\x61\x72\x63\
+\x68\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\x20\x78\x6d\x6c\
+\x6e\x73\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x61\x39\x2e\x63\x6f\
+\x6d\x2f\x2d\x2f\x73\x70\x65\x63\x2f\x6f\x70\x65\x6e\x73\x65\x61\
+\x72\x63\x68\x2f\x31\x2e\x31\x2f\x22\x3e\x0a\x20\x20\x20\x20\x3c\
+\x53\x68\x6f\x72\x74\x4e\x61\x6d\x65\x3e\x41\x6d\x61\x7a\x6f\x6e\
+\x2e\x63\x6f\x6d\x3c\x2f\x53\x68\x6f\x72\x74\x4e\x61\x6d\x65\x3e\
+\x0a\x20\x20\x20\x20\x3c\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\
+\x6e\x3e\x41\x6d\x61\x7a\x6f\x6e\x2e\x63\x6f\x6d\x20\x53\x65\x61\
+\x72\x63\x68\x3c\x2f\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\
+\x3e\x0a\x20\x20\x20\x20\x3c\x55\x72\x6c\x20\x6d\x65\x74\x68\x6f\
+\x64\x3d\x22\x67\x65\x74\x22\x20\x74\x79\x70\x65\x3d\x22\x74\x65\
+\x78\x74\x2f\x68\x74\x6d\x6c\x22\x20\x74\x65\x6d\x70\x6c\x61\x74\
+\x65\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x61\x6d\
+\x61\x7a\x6f\x6e\x2e\x63\x6f\x6d\x2f\x65\x78\x65\x63\x2f\x6f\x62\
+\x69\x64\x6f\x73\x2f\x65\x78\x74\x65\x72\x6e\x61\x6c\x2d\x73\x65\
+\x61\x72\x63\x68\x2f\x3f\x66\x69\x65\x6c\x64\x2d\x6b\x65\x79\x77\
+\x6f\x72\x64\x73\x3d\x7b\x73\x65\x61\x72\x63\x68\x54\x65\x72\x6d\
+\x73\x7d\x22\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x49\x6d\x61\x67\x65\
+\x3e\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x61\x6d\x61\x7a\
+\x6f\x6e\x2e\x63\x6f\x6d\x2f\x66\x61\x76\x69\x63\x6f\x6e\x2e\x69\
+\x63\x6f\x3c\x2f\x49\x6d\x61\x67\x65\x3e\x0a\x3c\x2f\x4f\x70\x65\
+\x6e\x53\x65\x61\x72\x63\x68\x44\x65\x73\x63\x72\x69\x70\x74\x69\
+\x6f\x6e\x3e\x0a\
+\x00\x00\x02\x46\
+\x3c\
+\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\
+\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x55\x54\x46\
+\x2d\x38\x22\x3f\x3e\x0a\x3c\x4f\x70\x65\x6e\x53\x65\x61\x72\x63\
+\x68\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\x20\x78\x6d\x6c\
+\x6e\x73\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x61\x39\x2e\x63\x6f\
+\x6d\x2f\x2d\x2f\x73\x70\x65\x63\x2f\x6f\x70\x65\x6e\x73\x65\x61\
+\x72\x63\x68\x2f\x31\x2e\x31\x2f\x22\x3e\x0a\x20\x20\x20\x20\x3c\
+\x53\x68\x6f\x72\x74\x4e\x61\x6d\x65\x3e\x47\x6f\x6f\x67\x6c\x65\
+\x3c\x2f\x53\x68\x6f\x72\x74\x4e\x61\x6d\x65\x3e\x0a\x20\x20\x20\
+\x20\x3c\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\x3e\x47\x6f\
+\x6f\x67\x6c\x65\x20\x57\x65\x62\x20\x53\x65\x61\x72\x63\x68\x3c\
+\x2f\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\x3e\x0a\x20\x20\
+\x20\x20\x3c\x55\x72\x6c\x20\x6d\x65\x74\x68\x6f\x64\x3d\x22\x67\
+\x65\x74\x22\x20\x74\x79\x70\x65\x3d\x22\x74\x65\x78\x74\x2f\x68\
+\x74\x6d\x6c\x22\x20\x74\x65\x6d\x70\x6c\x61\x74\x65\x3d\x22\x68\
+\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x67\x6f\x6f\x67\x6c\x65\
+\x2e\x63\x6f\x6d\x2f\x73\x65\x61\x72\x63\x68\x3f\x68\x6c\x3d\x7b\
+\x6c\x61\x6e\x67\x75\x61\x67\x65\x7d\x26\x61\x6d\x70\x3b\x6c\x72\
+\x3d\x6c\x61\x6e\x67\x5f\x7b\x6c\x61\x6e\x67\x75\x61\x67\x65\x7d\
+\x26\x61\x6d\x70\x3b\x71\x3d\x7b\x73\x65\x61\x72\x63\x68\x54\x65\
+\x72\x6d\x73\x7d\x22\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x55\x72\x6c\
+\x20\x6d\x65\x74\x68\x6f\x64\x3d\x22\x67\x65\x74\x22\x20\x74\x79\
+\x70\x65\x3d\x22\x61\x70\x70\x6c\x69\x63\x61\x74\x69\x6f\x6e\x2f\
+\x78\x2d\x73\x75\x67\x67\x65\x73\x74\x69\x6f\x6e\x73\x2b\x6a\x73\
+\x6f\x6e\x22\x20\x74\x65\x6d\x70\x6c\x61\x74\x65\x3d\x22\x68\x74\
+\x74\x70\x3a\x2f\x2f\x73\x75\x67\x67\x65\x73\x74\x71\x75\x65\x72\
+\x69\x65\x73\x2e\x67\x6f\x6f\x67\x6c\x65\x2e\x63\x6f\x6d\x2f\x63\
+\x6f\x6d\x70\x6c\x65\x74\x65\x2f\x73\x65\x61\x72\x63\x68\x3f\x6f\
+\x75\x74\x70\x75\x74\x3d\x66\x69\x72\x65\x66\x6f\x78\x26\x61\x6d\
+\x70\x3b\x68\x6c\x3d\x7b\x6c\x61\x6e\x67\x75\x61\x67\x65\x7d\x26\
+\x61\x6d\x70\x3b\x71\x3d\x7b\x73\x65\x61\x72\x63\x68\x54\x65\x72\
+\x6d\x73\x7d\x22\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x49\x6d\x61\x67\
+\x65\x3e\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x67\x6f\x6f\
+\x67\x6c\x65\x2e\x63\x6f\x6d\x2f\x66\x61\x76\x69\x63\x6f\x6e\x2e\
+\x69\x63\x6f\x3c\x2f\x49\x6d\x61\x67\x65\x3e\x0a\x3c\x2f\x4f\x70\
+\x65\x6e\x53\x65\x61\x72\x63\x68\x44\x65\x73\x63\x72\x69\x70\x74\
+\x69\x6f\x6e\x3e\x0a\
+\x00\x00\x02\x46\
+\x3c\
+\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\
+\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x55\x54\x46\
+\x2d\x38\x22\x3f\x3e\x0a\x3c\x4f\x70\x65\x6e\x53\x65\x61\x72\x63\
+\x68\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\x20\x78\x6d\x6c\
+\x6e\x73\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x61\x39\x2e\x63\x6f\
+\x6d\x2f\x2d\x2f\x73\x70\x65\x63\x2f\x6f\x70\x65\x6e\x73\x65\x61\
+\x72\x63\x68\x2f\x31\x2e\x31\x2f\x22\x3e\x0a\x20\x20\x20\x20\x3c\
+\x53\x68\x6f\x72\x74\x4e\x61\x6d\x65\x3e\x57\x69\x6b\x69\x61\x20\
+\x28\x65\x6e\x29\x3c\x2f\x53\x68\x6f\x72\x74\x4e\x61\x6d\x65\x3e\
+\x0a\x20\x20\x20\x20\x3c\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\
+\x6e\x3e\x57\x69\x6b\x69\x61\x20\x28\x65\x6e\x29\x3c\x2f\x44\x65\
+\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\x3e\x0a\x20\x20\x20\x20\x3c\
+\x55\x72\x6c\x20\x6d\x65\x74\x68\x6f\x64\x3d\x22\x67\x65\x74\x22\
+\x20\x74\x79\x70\x65\x3d\x22\x74\x65\x78\x74\x2f\x68\x74\x6d\x6c\
+\x22\x20\x74\x65\x6d\x70\x6c\x61\x74\x65\x3d\x22\x68\x74\x74\x70\
+\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x69\x6b\x69\x61\x2e\x63\x6f\x6d\
+\x2f\x69\x6e\x64\x65\x78\x2e\x70\x68\x70\x3f\x74\x69\x74\x6c\x65\
+\x3d\x53\x70\x65\x63\x69\x61\x6c\x3a\x53\x65\x61\x72\x63\x68\x26\
+\x61\x6d\x70\x3b\x73\x65\x61\x72\x63\x68\x3d\x7b\x73\x65\x61\x72\
+\x63\x68\x54\x65\x72\x6d\x73\x7d\x22\x2f\x3e\x0a\x20\x20\x20\x20\
+\x3c\x55\x72\x6c\x20\x6d\x65\x74\x68\x6f\x64\x3d\x22\x67\x65\x74\
+\x22\x20\x74\x79\x70\x65\x3d\x22\x61\x70\x70\x6c\x69\x63\x61\x74\
+\x69\x6f\x6e\x2f\x78\x2d\x73\x75\x67\x67\x65\x73\x74\x69\x6f\x6e\
+\x73\x2b\x6a\x73\x6f\x6e\x22\x20\x74\x65\x6d\x70\x6c\x61\x74\x65\
+\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x69\x6b\
+\x69\x61\x2e\x63\x6f\x6d\x2f\x61\x70\x69\x2e\x70\x68\x70\x3f\x61\
+\x63\x74\x69\x6f\x6e\x3d\x6f\x70\x65\x6e\x73\x65\x61\x72\x63\x68\
+\x26\x61\x6d\x70\x3b\x73\x65\x61\x72\x63\x68\x3d\x7b\x73\x65\x61\
+\x72\x63\x68\x54\x65\x72\x6d\x73\x7d\x26\x61\x6d\x70\x3b\x6e\x61\
+\x6d\x65\x73\x70\x61\x63\x65\x3d\x31\x22\x2f\x3e\x0a\x20\x20\x20\
+\x20\x3c\x49\x6d\x61\x67\x65\x3e\x68\x74\x74\x70\x3a\x2f\x2f\x69\
+\x6d\x61\x67\x65\x73\x2e\x77\x69\x6b\x69\x61\x2e\x63\x6f\x6d\x2f\
+\x77\x69\x6b\x69\x61\x67\x6c\x6f\x62\x61\x6c\x2f\x69\x6d\x61\x67\
+\x65\x73\x2f\x36\x2f\x36\x34\x2f\x46\x61\x76\x69\x63\x6f\x6e\x2e\
+\x69\x63\x6f\x3c\x2f\x49\x6d\x61\x67\x65\x3e\x0a\x3c\x2f\x4f\x70\
+\x65\x6e\x53\x65\x61\x72\x63\x68\x44\x65\x73\x63\x72\x69\x70\x74\
+\x69\x6f\x6e\x3e\x0a\
+\x00\x00\x01\x9b\
+\x3c\
+\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\
+\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x55\x54\x46\
+\x2d\x38\x22\x3f\x3e\x0a\x3c\x4f\x70\x65\x6e\x53\x65\x61\x72\x63\
+\x68\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\x20\x78\x6d\x6c\
+\x6e\x73\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x61\x39\x2e\x63\x6f\
+\x6d\x2f\x2d\x2f\x73\x70\x65\x63\x2f\x6f\x70\x65\x6e\x73\x65\x61\
+\x72\x63\x68\x2f\x31\x2e\x31\x2f\x22\x3e\x0a\x20\x20\x20\x20\x3c\
+\x53\x68\x6f\x72\x74\x4e\x61\x6d\x65\x3e\x57\x69\x6b\x74\x69\x6f\
+\x6e\x61\x72\x79\x3c\x2f\x53\x68\x6f\x72\x74\x4e\x61\x6d\x65\x3e\
+\x0a\x20\x20\x20\x20\x3c\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\
+\x6e\x3e\x57\x69\x6b\x74\x69\x6f\x6e\x61\x72\x79\x3c\x2f\x44\x65\
+\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\x3e\x0a\x20\x20\x20\x20\x3c\
+\x55\x72\x6c\x20\x6d\x65\x74\x68\x6f\x64\x3d\x22\x67\x65\x74\x22\
+\x20\x74\x79\x70\x65\x3d\x22\x74\x65\x78\x74\x2f\x68\x74\x6d\x6c\
+\x22\x20\x74\x65\x6d\x70\x6c\x61\x74\x65\x3d\x22\x68\x74\x74\x70\
+\x3a\x2f\x2f\x7b\x63\x6f\x75\x6e\x74\x72\x79\x7d\x2e\x77\x69\x6b\
+\x74\x69\x6f\x6e\x61\x72\x79\x2e\x6f\x72\x67\x2f\x77\x2f\x69\x6e\
+\x64\x65\x78\x2e\x70\x68\x70\x3f\x74\x69\x74\x6c\x65\x3d\x53\x70\
+\x65\x63\x69\x61\x6c\x3a\x53\x65\x61\x72\x63\x68\x26\x61\x6d\x70\
+\x3b\x73\x65\x61\x72\x63\x68\x3d\x7b\x73\x65\x61\x72\x63\x68\x54\
+\x65\x72\x6d\x73\x7d\x22\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x49\x6d\
+\x61\x67\x65\x3e\x68\x74\x74\x70\x3a\x2f\x2f\x65\x6e\x2e\x77\x69\
+\x6b\x74\x69\x6f\x6e\x61\x72\x79\x2e\x6f\x72\x67\x2f\x66\x61\x76\
+\x69\x63\x6f\x6e\x2e\x69\x63\x6f\x3c\x2f\x49\x6d\x61\x67\x65\x3e\
+\x0a\x3c\x2f\x4f\x70\x65\x6e\x53\x65\x61\x72\x63\x68\x44\x65\x73\
+\x63\x72\x69\x70\x74\x69\x6f\x6e\x3e\x0a\
+\x00\x00\x02\x5b\
+\x3c\
+\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\
+\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x55\x54\x46\
+\x2d\x38\x22\x3f\x3e\x0a\x3c\x4f\x70\x65\x6e\x53\x65\x61\x72\x63\
+\x68\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\x20\x78\x6d\x6c\
+\x6e\x73\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x61\x39\x2e\x63\x6f\
+\x6d\x2f\x2d\x2f\x73\x70\x65\x63\x2f\x6f\x70\x65\x6e\x73\x65\x61\
+\x72\x63\x68\x2f\x31\x2e\x31\x2f\x22\x3e\x0a\x20\x20\x20\x20\x3c\
+\x53\x68\x6f\x72\x74\x4e\x61\x6d\x65\x3e\x57\x69\x6b\x69\x70\x65\
+\x64\x69\x61\x3c\x2f\x53\x68\x6f\x72\x74\x4e\x61\x6d\x65\x3e\x0a\
+\x20\x20\x20\x20\x3c\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\
+\x3e\x46\x75\x6c\x6c\x20\x74\x65\x78\x74\x20\x73\x65\x61\x72\x63\
+\x68\x20\x69\x6e\x20\x57\x69\x6b\x69\x70\x65\x64\x69\x61\x3c\x2f\
+\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\x3e\x0a\x20\x20\x20\
+\x20\x3c\x55\x72\x6c\x20\x6d\x65\x74\x68\x6f\x64\x3d\x22\x67\x65\
+\x74\x22\x20\x74\x79\x70\x65\x3d\x22\x74\x65\x78\x74\x2f\x68\x74\
+\x6d\x6c\x22\x20\x74\x65\x6d\x70\x6c\x61\x74\x65\x3d\x22\x68\x74\
+\x74\x70\x3a\x2f\x2f\x7b\x63\x6f\x75\x6e\x74\x72\x79\x7d\x2e\x77\
+\x69\x6b\x69\x70\x65\x64\x69\x61\x2e\x6f\x72\x67\x2f\x77\x69\x6b\
+\x69\x2f\x53\x70\x65\x63\x69\x61\x6c\x3a\x53\x65\x61\x72\x63\x68\
+\x3f\x73\x65\x61\x72\x63\x68\x3d\x7b\x73\x65\x61\x72\x63\x68\x54\
+\x65\x72\x6d\x73\x7d\x26\x61\x6d\x70\x3b\x66\x75\x6c\x6c\x74\x65\
+\x78\x74\x3d\x53\x65\x61\x72\x63\x68\x22\x2f\x3e\x0a\x20\x20\x20\
+\x20\x3c\x55\x72\x6c\x20\x6d\x65\x74\x68\x6f\x64\x3d\x22\x67\x65\
+\x74\x22\x20\x74\x79\x70\x65\x3d\x22\x61\x70\x70\x6c\x69\x63\x61\
+\x74\x69\x6f\x6e\x2f\x78\x2d\x73\x75\x67\x67\x65\x73\x74\x69\x6f\
+\x6e\x73\x2b\x6a\x73\x6f\x6e\x22\x20\x74\x65\x6d\x70\x6c\x61\x74\
+\x65\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x7b\x63\x6f\x75\x6e\x74\
+\x72\x79\x7d\x2e\x77\x69\x6b\x69\x70\x65\x64\x69\x61\x2e\x6f\x72\
+\x67\x2f\x77\x2f\x61\x70\x69\x2e\x70\x68\x70\x3f\x61\x63\x74\x69\
+\x6f\x6e\x3d\x6f\x70\x65\x6e\x73\x65\x61\x72\x63\x68\x26\x61\x6d\
+\x70\x3b\x73\x65\x61\x72\x63\x68\x3d\x7b\x73\x65\x61\x72\x63\x68\
+\x54\x65\x72\x6d\x73\x7d\x26\x61\x6d\x70\x3b\x6e\x61\x6d\x65\x73\
+\x70\x61\x63\x65\x3d\x30\x22\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x49\
+\x6d\x61\x67\x65\x3e\x68\x74\x74\x70\x3a\x2f\x2f\x65\x6e\x2e\x77\
+\x69\x6b\x69\x70\x65\x64\x69\x61\x2e\x6f\x72\x67\x2f\x66\x61\x76\
+\x69\x63\x6f\x6e\x2e\x69\x63\x6f\x3c\x2f\x49\x6d\x61\x67\x65\x3e\
+\x0a\x3c\x2f\x4f\x70\x65\x6e\x53\x65\x61\x72\x63\x68\x44\x65\x73\
+\x63\x72\x69\x70\x74\x69\x6f\x6e\x3e\x0a\
+"
+
+qt_resource_name = b"\
+\x00\x08\
+\x00\x4a\x56\x1c\
+\x00\x42\
+\x00\x69\x00\x6e\x00\x67\x00\x2e\x00\x78\x00\x6d\x00\x6c\
+\x00\x12\
+\x0a\xf9\x0f\x7c\
+\x00\x44\
+\x00\x65\x00\x45\x00\x6e\x00\x5f\x00\x42\x00\x65\x00\x6f\x00\x6c\x00\x69\x00\x6e\x00\x67\x00\x75\x00\x73\x00\x2e\x00\x78\x00\x6d\
+\x00\x6c\
+\x00\x1b\
+\x0d\x52\x43\x5c\
+\x00\x47\
+\x00\x6f\x00\x6f\x00\x67\x00\x6c\x00\x65\x00\x5f\x00\x49\x00\x6d\x00\x5f\x00\x46\x00\x65\x00\x65\x00\x6c\x00\x69\x00\x6e\x00\x67\
+\x00\x5f\x00\x4c\x00\x75\x00\x63\x00\x6b\x00\x79\x00\x2e\x00\x78\x00\x6d\x00\x6c\
+\x00\x09\
+\x01\xf4\xe3\x3c\
+\x00\x57\
+\x00\x69\x00\x6b\x00\x69\x00\x61\x00\x2e\x00\x78\x00\x6d\x00\x6c\
+\x00\x09\
+\x0f\x62\xe1\xdc\
+\x00\x59\
+\x00\x61\x00\x68\x00\x6f\x00\x6f\x00\x2e\x00\x78\x00\x6d\x00\x6c\
+\x00\x0a\
+\x0b\x0c\x48\x7c\
+\x00\x52\
+\x00\x65\x00\x64\x00\x64\x00\x69\x00\x74\x00\x2e\x00\x78\x00\x6d\x00\x6c\
+\x00\x0e\
+\x00\xf1\x12\x1c\
+\x00\x4c\
+\x00\x45\x00\x4f\x00\x5f\x00\x44\x00\x65\x00\x75\x00\x45\x00\x6e\x00\x67\x00\x2e\x00\x78\x00\x6d\x00\x6c\
+\x00\x0b\
+\x0b\x48\x8a\x5c\
+\x00\x59\
+\x00\x6f\x00\x75\x00\x54\x00\x75\x00\x62\x00\x65\x00\x2e\x00\x78\x00\x6d\x00\x6c\
+\x00\x0e\
+\x09\x21\x3a\xfc\
+\x00\x44\
+\x00\x75\x00\x63\x00\x6b\x00\x44\x00\x75\x00\x63\x00\x6b\x00\x47\x00\x6f\x00\x2e\x00\x78\x00\x6d\x00\x6c\
+\x00\x0c\
+\x0f\xd5\x68\x1c\
+\x00\x46\
+\x00\x61\x00\x63\x00\x65\x00\x62\x00\x6f\x00\x6f\x00\x6b\x00\x2e\x00\x78\x00\x6d\x00\x6c\
+\x00\x10\
+\x09\x73\x65\x7c\
+\x00\x4c\
+\x00\x69\x00\x6e\x00\x75\x00\x78\x00\x4d\x00\x61\x00\x67\x00\x61\x00\x7a\x00\x69\x00\x6e\x00\x2e\x00\x78\x00\x6d\x00\x6c\
+\x00\x0d\
+\x0a\x2e\x72\x9c\
+\x00\x41\
+\x00\x6d\x00\x61\x00\x7a\x00\x6f\x00\x6e\x00\x63\x00\x6f\x00\x6d\x00\x2e\x00\x78\x00\x6d\x00\x6c\
+\x00\x0a\
+\x0e\x31\x93\x9c\
+\x00\x47\
+\x00\x6f\x00\x6f\x00\x67\x00\x6c\x00\x65\x00\x2e\x00\x78\x00\x6d\x00\x6c\
+\x00\x0c\
+\x0e\x81\x61\xdc\
+\x00\x57\
+\x00\x69\x00\x6b\x00\x69\x00\x61\x00\x5f\x00\x65\x00\x6e\x00\x2e\x00\x78\x00\x6d\x00\x6c\
+\x00\x0e\
+\x08\xce\x7c\x3c\
+\x00\x57\
+\x00\x69\x00\x6b\x00\x74\x00\x69\x00\x6f\x00\x6e\x00\x61\x00\x72\x00\x79\x00\x2e\x00\x78\x00\x6d\x00\x6c\
+\x00\x0d\
+\x06\xf8\x53\x3c\
+\x00\x57\
+\x00\x69\x00\x6b\x00\x69\x00\x70\x00\x65\x00\x64\x00\x69\x00\x61\x00\x2e\x00\x78\x00\x6d\x00\x6c\
+"
+
+qt_resource_struct = b"\
+\x00\x00\x00\x00\x00\x02\x00\x00\x00\x10\x00\x00\x00\x01\
+\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
+\x00\x00\x00\xc6\x00\x00\x00\x00\x00\x01\x00\x00\x0b\xfd\
+\x00\x00\x00\x7c\x00\x00\x00\x00\x00\x01\x00\x00\x06\x07\
+\x00\x00\x01\xe4\x00\x00\x00\x00\x00\x01\x00\x00\x22\x21\
+\x00\x00\x01\xc2\x00\x00\x00\x00\x00\x01\x00\x00\x20\x82\
+\x00\x00\x01\x04\x00\x00\x00\x00\x00\x01\x00\x00\x10\x04\
+\x00\x00\x01\x44\x00\x00\x00\x00\x00\x01\x00\x00\x18\x88\
+\x00\x00\x01\x6a\x00\x00\x00\x00\x00\x01\x00\x00\x1a\x55\
+\x00\x00\x00\x16\x00\x00\x00\x00\x00\x01\x00\x00\x01\x7d\
+\x00\x00\x00\xac\x00\x00\x00\x00\x00\x01\x00\x00\x0a\x8a\
+\x00\x00\x00\xe8\x00\x00\x00\x00\x00\x01\x00\x00\x0e\x7b\
+\x00\x00\x00\x40\x00\x00\x00\x00\x00\x01\x00\x00\x03\x9f\
+\x00\x00\x01\x8a\x00\x00\x00\x00\x00\x01\x00\x00\x1b\xee\
+\x00\x00\x01\xa4\x00\x00\x00\x00\x00\x01\x00\x00\x1e\x38\
+\x00\x00\x00\x94\x00\x00\x00\x00\x00\x01\x00\x00\x08\x5f\
+\x00\x00\x01\x26\x00\x00\x00\x00\x00\x01\x00\x00\x17\x06\
+"
+
+def qInitResources():
+    QtCore.qRegisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data)
+
+def qCleanupResources():
+    QtCore.qUnregisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data)
+
+qInitResources()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/OpenSearch/DefaultSearchEngines/DuckDuckGo.xml	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
+    <ShortName>DuckDuckGo</ShortName>
+    <Description>Search DuckDuckGo</Description>
+    <Url method="get" type="text/html" template="https://duckduckgo.com/?q={searchTerms}"/>
+    <Url method="get" type="application/x-suggestions+json" template="https://ac.duckduckgo.com/ac/?q={searchTerms}&amp;type=list"/>
+    <Image>data:image/x-icon;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAB8lBMVEUAAADkRQzjPwPjQQXkRQ3iPwTiQQXgPQPeQgrcOwPVNgDVNQDWOgbTMwDRMgDQMwDSMwDRNwTQLgDRJgDSJwDSLgDSNwTjOgDiOADjOQDkPADhQAXzs5v+/fv////0vKbiRQvgPQHpdUr85NzuknPdKgDcIwDnZzj2w7HqeU/gPQLsimb/+PftjWn97Obpb0LdJQDeLQDtjmvsi2jgSBDnbULgOQD/39HgLQDeMgDpeFLgSBH0v670uqbaJQD2qImWvP/G1Ob5+/3u//+fvvXyp47dMwDaLwD0u6v0v6/aNQDiXi/aKQD3qozU7/8gSY2vvtg0ZK/OqLDaKQHYKgLgWTfaNADZMgDZMADZLADzqpD7//+xwdz//9H/5Bn/7Bn//ADofADYMADYMQDZOgPXLgDiZDj//97/0AD3tQDvlgHZOgbXLATXMADWMgDfXjLVLQD///z+0AD/3Rn/yRnwnQDcVjbVMQDyv67wuKTSJwDRHQD+8O/tg3/iQQDwhAHnawHWMADvtKfyva7XQxHga0bQGQD2vbH/u8LXIQCmPQzja07XQxLliGn99fPkcVHvhnGZ5VguvUU5wktBwCcAgxzydVv/8/XmiGngdlL+ysi3+I8LtCE80V6P3YmX4sDleljSNQLzr6D7sKPXNQTSIwAEAbMrAAAAF3RSTlMARqSkRvPz80PTpKRG3fPe3hio9/eoGP50jNsAAAABYktHRB5yCiArAAAAyElEQVQYGQXBvUqCYRiA4fu2V9Tn+UQddI3aCpxaOoU6iU4gcqqpoYbALXBuCuoYmttamqJDiEoh4YP+MOi6BNCh+uYKEGiOVNCXXxA2XDVV/UyfKbRCXTLQWAxbP2vt8Ue/uYDvfim91615sb2um6rqtrr/NFb1cUf1Ybd06areU6lSlYpK79jzK1SyJOkfhOl8JGEcqV5zoKrTRqO6yUzIzNu46ijdM1VV9bhuUJ/nZURExLRzUiPQm3kKXHi4BAEGOmOi78A/L1QoU/VHoTsAAAAldEVYdGRhdGU6Y3JlYXRlADIwMTQtMDEtMTlUMjA6MDE6MTEtMDU6MDAuET6cAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE0LTAxLTE5VDIwOjAxOjExLTA1OjAwX0yGIAAAAABJRU5ErkJggg==</Image>
+</OpenSearchDescription>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/OpenSearch/DefaultSearchEngines/Facebook.xml	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
+    <ShortName>Facebook</ShortName>
+    <Description>Search Facebook</Description>
+    <Url method="get" type="text/html" template="http://www.facebook.com/search/?src=os&amp;q={searchTerms}"/>
+    <Image>http://www.facebook.com/favicon.ico</Image>
+</OpenSearchDescription>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/OpenSearch/DefaultSearchEngines/Google.xml	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
+    <ShortName>Google</ShortName>
+    <Description>Google Web Search</Description>
+    <Url method="get" type="text/html" template="http://www.google.com/search?hl={language}&amp;lr=lang_{language}&amp;q={searchTerms}"/>
+    <Url method="get" type="application/x-suggestions+json" template="http://suggestqueries.google.com/complete/search?output=firefox&amp;hl={language}&amp;q={searchTerms}"/>
+    <Image>http://www.google.com/favicon.ico</Image>
+</OpenSearchDescription>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/OpenSearch/DefaultSearchEngines/Google_Im_Feeling_Lucky.xml	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
+    <ShortName>Google (I'm Feeling Lucky)</ShortName>
+    <Description>Google Web Search</Description>
+    <Url method="get" type="text/html" template="http://www.google.com/search?btnI=&amp;hl={language}&amp;lr=lang_{language}&amp;q={searchTerms}"/>
+    <Url method="get" type="application/x-suggestions+json" template="http://suggestqueries.google.com/complete/search?output=firefox&amp;hl={language}&amp;q={searchTerms}"/>
+    <Image>http://www.google.com/favicon.ico</Image>
+</OpenSearchDescription>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/OpenSearch/DefaultSearchEngines/LEO_DeuEng.xml	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
+    <ShortName>LEO Deu-Eng</ShortName>
+    <Description>Deutsch-Englisch Wörterbuch von LEO</Description>
+    <Url method="get" type="text/html" template="http://dict.leo.org/ende?lang=de&amp;search={searchTerms}"/>
+    <Url method="get" type="application/x-suggestions+json" template="http://dict.leo.org/dictQuery/m-query/conf/ende/query.conf/strlist.json?q={searchTerms}&amp;sort=PLa&amp;shortQuery&amp;noDescription&amp;noQueryURLs"/>
+    <Image>http://dict.leo.org/img/favicons/ende.ico</Image>
+</OpenSearchDescription>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/OpenSearch/DefaultSearchEngines/LinuxMagazin.xml	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
+    <ShortName>Linux-Magazin</ShortName>
+    <Description>Suche auf www.linux-magazin.de</Description>
+    <Url method="get" type="text/html" template="http://www.linux-magazin.de/content/search?SearchText={searchTerms}"/>
+    <Image>http://www.linux-magazin.de/extension/lnm/design/linux_magazin/images/favicon.ico</Image>
+</OpenSearchDescription>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/OpenSearch/DefaultSearchEngines/Reddit.xml	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
+    <ShortName>Reddit</ShortName>
+    <Description>Reddit Site Search</Description>
+    <Url method="get" type="text/html" template="http://www.reddit.com/search?q={searchTerms}"/>
+    <Image>http://www.reddit.com/favicon.ico</Image>
+</OpenSearchDescription>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/OpenSearch/DefaultSearchEngines/Wikia.xml	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
+    <ShortName>Wikia</ShortName>
+    <Description>Wikia Site Search</Description>
+    <Url method="get" type="text/html" template="http://{country}.wikia.com/index.php?title=Special:Search&amp;search={searchTerms}"/>
+    <Url method="get" type="application/x-suggestions+json" template="http://{country}.wikia.com/api.php?action=opensearch&amp;search={searchTerms}&amp;namespace=0"/>
+    <Image>http://images.wikia.com/wikiaglobal/images/6/64/Favicon.ico</Image>
+</OpenSearchDescription>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/OpenSearch/DefaultSearchEngines/Wikia_en.xml	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
+    <ShortName>Wikia (en)</ShortName>
+    <Description>Wikia (en)</Description>
+    <Url method="get" type="text/html" template="http://www.wikia.com/index.php?title=Special:Search&amp;search={searchTerms}"/>
+    <Url method="get" type="application/x-suggestions+json" template="http://www.wikia.com/api.php?action=opensearch&amp;search={searchTerms}&amp;namespace=1"/>
+    <Image>http://images.wikia.com/wikiaglobal/images/6/64/Favicon.ico</Image>
+</OpenSearchDescription>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/OpenSearch/DefaultSearchEngines/Wikipedia.xml	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
+    <ShortName>Wikipedia</ShortName>
+    <Description>Full text search in Wikipedia</Description>
+    <Url method="get" type="text/html" template="http://{country}.wikipedia.org/wiki/Special:Search?search={searchTerms}&amp;fulltext=Search"/>
+    <Url method="get" type="application/x-suggestions+json" template="http://{country}.wikipedia.org/w/api.php?action=opensearch&amp;search={searchTerms}&amp;namespace=0"/>
+    <Image>http://en.wikipedia.org/favicon.ico</Image>
+</OpenSearchDescription>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/OpenSearch/DefaultSearchEngines/Wiktionary.xml	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
+    <ShortName>Wiktionary</ShortName>
+    <Description>Wiktionary</Description>
+    <Url method="get" type="text/html" template="http://{country}.wiktionary.org/w/index.php?title=Special:Search&amp;search={searchTerms}"/>
+    <Image>http://en.wiktionary.org/favicon.ico</Image>
+</OpenSearchDescription>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/OpenSearch/DefaultSearchEngines/Yahoo.xml	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
+    <ShortName>Yahoo!</ShortName>
+    <Description>Yahoo Web Search</Description>
+    <Url method="get" type="text/html" template="http://search.yahoo.com/search?ei=utf-8&amp;fr=sfp&amp;iscqry=&amp;p={searchTerms}"/>
+    <Url method="get" type="application/x-suggestions+json" template="http://ff.search.yahoo.com/gossip?output=fxjson&amp;command={searchTerms}"/>
+    <Image>http://m.www.yahoo.com/favicon.ico</Image>
+</OpenSearchDescription>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/OpenSearch/DefaultSearchEngines/YouTube.xml	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
+    <ShortName>YouTube</ShortName>
+    <Description>YouTube</Description>
+    <Url method="get" type="text/html" template="http://www.youtube.com/results?search_query={searchTerms}&amp;search=Search"/>
+    <Image>http://www.youtube.com/favicon.ico</Image>
+</OpenSearchDescription>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/OpenSearch/DefaultSearchEngines/__init__.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2013 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Package conatining the default search engine definitions.
+"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/OpenSearch/OpenSearchDialog.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,121 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog for the configuration of search engines.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtWidgets import QDialog
+from PyQt5.QtCore import pyqtSlot
+
+from E5Gui import E5MessageBox, E5FileDialog
+
+from .OpenSearchEngineModel import OpenSearchEngineModel
+
+from .Ui_OpenSearchDialog import Ui_OpenSearchDialog
+
+
+class OpenSearchDialog(QDialog, Ui_OpenSearchDialog):
+    """
+    Class implementing a dialog for the configuration of search engines.
+    """
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object (QWidget)
+        """
+        super(OpenSearchDialog, self).__init__(parent)
+        self.setupUi(self)
+        
+        self.setModal(True)
+        
+        self.__mw = parent
+        
+        self.__model = \
+            OpenSearchEngineModel(self.__mw.openSearchManager(), self)
+        self.enginesTable.setModel(self.__model)
+        self.enginesTable.horizontalHeader().resizeSection(0, 200)
+        self.enginesTable.horizontalHeader().setStretchLastSection(True)
+        self.enginesTable.verticalHeader().hide()
+        self.enginesTable.verticalHeader().setDefaultSectionSize(
+            1.2 * self.fontMetrics().height())
+        
+        self.enginesTable.selectionModel().selectionChanged.connect(
+            self.__selectionChanged)
+        self.editButton.setEnabled(False)
+    
+    @pyqtSlot()
+    def on_addButton_clicked(self):
+        """
+        Private slot to add a new search engine.
+        """
+        fileNames = E5FileDialog.getOpenFileNames(
+            self,
+            self.tr("Add search engine"),
+            "",
+            self.tr("OpenSearch (*.xml);;All Files (*)"))
+        
+        osm = self.__mw.openSearchManager()
+        for fileName in fileNames:
+            if not osm.addEngine(fileName):
+                E5MessageBox.critical(
+                    self,
+                    self.tr("Add search engine"),
+                    self.tr(
+                        """{0} is not a valid OpenSearch 1.1 description or"""
+                        """ is already on your list.""").format(fileName))
+    
+    @pyqtSlot()
+    def on_deleteButton_clicked(self):
+        """
+        Private slot to delete the selected search engines.
+        """
+        if self.enginesTable.model().rowCount() == 1:
+            E5MessageBox.critical(
+                self,
+                self.tr("Delete selected engines"),
+                self.tr("""You must have at least one search engine."""))
+        
+        self.enginesTable.removeSelected()
+    
+    @pyqtSlot()
+    def on_restoreButton_clicked(self):
+        """
+        Private slot to restore the default search engines.
+        """
+        self.__mw.openSearchManager().restoreDefaults()
+    
+    @pyqtSlot()
+    def on_editButton_clicked(self):
+        """
+        Private slot to edit the data of the current search engine.
+        """
+        from .OpenSearchEditDialog import OpenSearchEditDialog
+        
+        rows = self.enginesTable.selectionModel().selectedRows()
+        if len(rows) == 0:
+            row = self.enginesTable.selectionModel().currentIndex().row()
+        else:
+            row = rows[0].row()
+        
+        osm = self.__mw.openSearchManager()
+        engineName = osm.allEnginesNames()[row]
+        engine = osm.engine(engineName)
+        dlg = OpenSearchEditDialog(engine, self)
+        if dlg.exec_() == QDialog.Accepted:
+            osm.enginesChanged()
+    
+    def __selectionChanged(self, selected, deselected):
+        """
+        Private slot to handle a change of the selection.
+        
+        @param selected item selection of selected items (QItemSelection)
+        @param deselected item selection of deselected items (QItemSelection)
+        """
+        self.editButton.setEnabled(
+            len(self.enginesTable.selectionModel().selectedRows()) <= 1)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/OpenSearch/OpenSearchDialog.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,164 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>OpenSearchDialog</class>
+ <widget class="QDialog" name="OpenSearchDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>650</width>
+    <height>350</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Open Search Engines Configuration</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QGridLayout" name="gridLayout">
+     <item row="0" column="0" rowspan="5">
+      <widget class="E5TableView" name="enginesTable">
+       <property name="alternatingRowColors">
+        <bool>true</bool>
+       </property>
+       <property name="selectionBehavior">
+        <enum>QAbstractItemView::SelectRows</enum>
+       </property>
+       <property name="showGrid">
+        <bool>false</bool>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="1">
+      <widget class="QPushButton" name="addButton">
+       <property name="toolTip">
+        <string>Press to add a new search engine from file</string>
+       </property>
+       <property name="text">
+        <string>&amp;Add...</string>
+       </property>
+       <property name="autoDefault">
+        <bool>false</bool>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="1">
+      <widget class="QPushButton" name="deleteButton">
+       <property name="toolTip">
+        <string>Press to delete the selected engines</string>
+       </property>
+       <property name="text">
+        <string>&amp;Delete</string>
+       </property>
+       <property name="autoDefault">
+        <bool>false</bool>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="1">
+      <widget class="QPushButton" name="editButton">
+       <property name="toolTip">
+        <string>Press to edit the data of the current engine</string>
+       </property>
+       <property name="text">
+        <string>Edit...</string>
+       </property>
+       <property name="autoDefault">
+        <bool>false</bool>
+       </property>
+      </widget>
+     </item>
+     <item row="3" column="1">
+      <widget class="QPushButton" name="restoreButton">
+       <property name="toolTip">
+        <string>Press to restore the default engines</string>
+       </property>
+       <property name="text">
+        <string>&amp;Restore Defaults</string>
+       </property>
+       <property name="autoDefault">
+        <bool>false</bool>
+       </property>
+      </widget>
+     </item>
+     <item row="4" column="1">
+      <spacer name="verticalSpacer">
+       <property name="orientation">
+        <enum>Qt::Vertical</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>20</width>
+         <height>38</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Close</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>E5TableView</class>
+   <extends>QTableView</extends>
+   <header>E5Gui/E5TableView.h</header>
+  </customwidget>
+ </customwidgets>
+ <tabstops>
+  <tabstop>enginesTable</tabstop>
+  <tabstop>addButton</tabstop>
+  <tabstop>deleteButton</tabstop>
+  <tabstop>editButton</tabstop>
+  <tabstop>restoreButton</tabstop>
+  <tabstop>buttonBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>OpenSearchDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>OpenSearchDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/OpenSearch/OpenSearchEditDialog.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,52 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog to edit the data of a search engine.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtWidgets import QDialog
+
+from .Ui_OpenSearchEditDialog import Ui_OpenSearchEditDialog
+
+
+class OpenSearchEditDialog(QDialog, Ui_OpenSearchEditDialog):
+    """
+    Class implementing a dialog to edit the data of a search engine.
+    """
+    def __init__(self, engine, parent=None):
+        """
+        Constructor
+        
+        @param engine reference to the search engine (OpenSearchEngine)
+        @param parent reference to the parent object (QWidget)
+        """
+        super(OpenSearchEditDialog, self).__init__(parent)
+        self.setupUi(self)
+        
+        self.__engine = engine
+        
+        self.nameEdit.setText(engine.name())
+        self.descriptionEdit.setText(engine.description())
+        self.imageEdit.setText(engine.imageUrl())
+        self.searchEdit.setText(engine.searchUrlTemplate())
+        self.suggestionsEdit.setText(engine.suggestionsUrlTemplate())
+        
+        msh = self.minimumSizeHint()
+        self.resize(max(self.width(), msh.width()), msh.height())
+    
+    def accept(self):
+        """
+        Public slot to accept the data entered.
+        """
+        self.__engine.setName(self.nameEdit.text())
+        self.__engine.setDescription(self.descriptionEdit.text())
+        self.__engine.setImageUrlAndLoad(self.imageEdit.text())
+        self.__engine.setSearchUrlTemplate(self.searchEdit.text())
+        self.__engine.setSuggestionsUrlTemplate(self.suggestionsEdit.text())
+        
+        super(OpenSearchEditDialog, self).accept()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/OpenSearch/OpenSearchEditDialog.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>OpenSearchEditDialog</class>
+ <widget class="QDialog" name="OpenSearchEditDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>690</width>
+    <height>218</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Edit search engine data</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QGridLayout" name="gridLayout">
+     <item row="0" column="0">
+      <widget class="QLabel" name="label_2">
+       <property name="text">
+        <string>&amp;Name:</string>
+       </property>
+       <property name="buddy">
+        <cstring>nameEdit</cstring>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="1">
+      <widget class="QLineEdit" name="nameEdit">
+       <property name="focusPolicy">
+        <enum>Qt::NoFocus</enum>
+       </property>
+       <property name="toolTip">
+        <string>Shows the name of the search engine</string>
+       </property>
+       <property name="readOnly">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="0">
+      <widget class="QLabel" name="label_3">
+       <property name="text">
+        <string>&amp;Description:</string>
+       </property>
+       <property name="buddy">
+        <cstring>descriptionEdit</cstring>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="1">
+      <widget class="QLineEdit" name="descriptionEdit">
+       <property name="toolTip">
+        <string>Enter a description</string>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="0">
+      <widget class="QLabel" name="label_5">
+       <property name="text">
+        <string>&amp;Image URL:</string>
+       </property>
+       <property name="buddy">
+        <cstring>imageEdit</cstring>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="1">
+      <widget class="QLineEdit" name="imageEdit">
+       <property name="toolTip">
+        <string>Enter the URL of the image</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QLabel" name="label_4">
+     <property name="text">
+      <string>&amp;Search URL Template:</string>
+     </property>
+     <property name="buddy">
+      <cstring>searchEdit</cstring>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QLineEdit" name="searchEdit">
+     <property name="toolTip">
+      <string>Enter the template of the search URL</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QLabel" name="label_6">
+     <property name="text">
+      <string>Su&amp;ggestions URL Template:</string>
+     </property>
+     <property name="buddy">
+      <cstring>suggestionsEdit</cstring>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QLineEdit" name="suggestionsEdit">
+     <property name="toolTip">
+      <string>Enter the template of the suggestions URL</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <tabstops>
+  <tabstop>nameEdit</tabstop>
+  <tabstop>descriptionEdit</tabstop>
+  <tabstop>imageEdit</tabstop>
+  <tabstop>searchEdit</tabstop>
+  <tabstop>suggestionsEdit</tabstop>
+  <tabstop>buttonBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>OpenSearchEditDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>222</x>
+     <y>232</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>246</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>OpenSearchEditDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>290</x>
+     <y>238</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>246</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/OpenSearch/OpenSearchEngine.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,538 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the open search engine.
+"""
+
+from __future__ import unicode_literals
+
+import re
+import json
+
+from PyQt5.QtCore import pyqtSignal, pyqtSlot, QLocale, QUrl, QByteArray, \
+    QBuffer, QIODevice, QObject, qVersion
+from PyQt5.QtGui import QImage
+from PyQt5.QtNetwork import QNetworkRequest, QNetworkAccessManager, \
+    QNetworkReply
+
+from UI.Info import Program
+
+import Preferences
+import Utilities
+
+
+class OpenSearchEngine(QObject):
+    """
+    Class implementing the open search engine.
+    
+    @signal imageChanged() emitted after the icon has been changed
+    @signal suggestions(list of strings) emitted after the suggestions have
+            been received
+    """
+    imageChanged = pyqtSignal()
+    suggestions = pyqtSignal(list)
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object (QObject)
+        """
+        super(OpenSearchEngine, self).__init__(parent)
+        
+        self.__suggestionsReply = None
+        self.__networkAccessManager = None
+        self._name = ""
+        self._description = ""
+        self._searchUrlTemplate = ""
+        self._suggestionsUrlTemplate = ""
+        self._searchParameters = []            # list of two tuples
+        self._suggestionsParameters = []       # list of two tuples
+        self._imageUrl = ""
+        self.__image = QImage()
+        self.__iconMoved = False
+        self.__searchMethod = "get"
+        self.__suggestionsMethod = "get"
+        self.__requestMethods = {
+            "get": QNetworkAccessManager.GetOperation,
+            "post": QNetworkAccessManager.PostOperation,
+        }
+        
+        self.__replies = []
+    
+    @classmethod
+    def parseTemplate(cls, searchTerm, searchTemplate):
+        """
+        Class method to parse a search template.
+        
+        @param searchTerm term to search for (string)
+        @param searchTemplate template to be parsed (string)
+        @return parsed template (string)
+        """
+        locale = QLocale(Preferences.getWebBrowser("SearchLanguage"))
+        language = locale.name().replace("_", "-")
+        country = locale.name().split("_")[0].lower()
+        
+        result = searchTemplate
+        result = result.replace("{count}", "20")
+        result = result.replace("{startIndex}", "0")
+        result = result.replace("{startPage}", "0")
+        result = result.replace("{language}", language)
+        result = result.replace("{country}", country)
+        result = result.replace("{inputEncoding}", "UTF-8")
+        result = result.replace("{outputEncoding}", "UTF-8")
+        result = result.replace(
+            "{searchTerms}",
+            bytes(QUrl.toPercentEncoding(searchTerm)).decode())
+        result = re.sub(r"""\{([^\}]*:|)source\??\}""", Program, result)
+
+        return result
+    
+    @pyqtSlot(result=str)
+    def name(self):
+        """
+        Public method to get the name of the engine.
+        
+        @return name of the engine (string)
+        """
+        return self._name
+    
+    def setName(self, name):
+        """
+        Public method to set the engine name.
+        
+        @param name name of the engine (string)
+        """
+        self._name = name
+    
+    def description(self):
+        """
+        Public method to get the description of the engine.
+        
+        @return description of the engine (string)
+        """
+        return self._description
+    
+    def setDescription(self, description):
+        """
+        Public method to set the engine description.
+        
+        @param description description of the engine (string)
+        """
+        self._description = description
+    
+    def searchUrlTemplate(self):
+        """
+        Public method to get the search URL template of the engine.
+        
+        @return search URL template of the engine (string)
+        """
+        return self._searchUrlTemplate
+    
+    def setSearchUrlTemplate(self, searchUrlTemplate):
+        """
+        Public method to set the engine search URL template.
+        
+        The URL template is processed according to the specification:
+        <a
+          href="http://www.opensearch.org/Specifications/OpenSearch/1.1#OpenSearch_URL_template_syntax">
+        http://www.opensearch.org/Specifications/OpenSearch/1.1#OpenSearch_URL_template_syntax</a>
+
+        A list of template parameters currently supported and what they are
+        replaced with:
+        <table>
+        <tr><td><b>Parameter</b></td><td><b>Value</b></td></tr>
+        <tr><td>{count}</td><td>20</td></tr>
+        <tr><td>{startIndex}</td><td>0</td></tr>
+        <tr><td>{startPage}</td><td>0</td></tr>
+        <tr><td>{language}</td>
+          <td>the default language code (RFC 3066)</td></tr>
+        <tr><td>{country}</td>
+          <td>the default country code (first part of language)</td></tr>
+        <tr><td>{inputEncoding}</td><td>UTF-8</td></tr>
+        <tr><td>{outputEncoding}</td><td>UTF-8</td></tr>
+        <tr><td>{searchTerms}</td><td>the string supplied by the user</td></tr>
+        <tr><td>{*:source}</td>
+          <td>application name, QCoreApplication::applicationName()</td></tr>
+        </table>
+        
+        @param searchUrlTemplate search URL template of the engine (string)
+        """
+        self._searchUrlTemplate = searchUrlTemplate
+    
+    def searchUrl(self, searchTerm):
+        """
+        Public method to get a URL ready for searching.
+        
+        @param searchTerm term to search for (string)
+        @return URL (QUrl)
+        """
+        if not self._searchUrlTemplate:
+            return QUrl()
+        
+        ret = QUrl.fromEncoded(
+            self.parseTemplate(searchTerm, self._searchUrlTemplate)
+            .encode("utf-8"))
+        
+        if self.__searchMethod != "post":
+            if qVersion() >= "5.0.0":
+                from PyQt5.QtCore import QUrlQuery
+                urlQuery = QUrlQuery(ret)
+                for parameter in self._searchParameters:
+                    urlQuery.addQueryItem(
+                        parameter[0],
+                        self.parseTemplate(searchTerm, parameter[1]))
+                ret.setQuery(urlQuery)
+            else:
+                for parameter in self._searchParameters:
+                    ret.addQueryItem(
+                        parameter[0],
+                        self.parseTemplate(searchTerm, parameter[1]))
+        
+        return ret
+    
+    def providesSuggestions(self):
+        """
+        Public method to check, if the engine provides suggestions.
+        
+        @return flag indicating suggestions are provided (boolean)
+        """
+        return self._suggestionsUrlTemplate != ""
+    
+    def suggestionsUrlTemplate(self):
+        """
+        Public method to get the search URL template of the engine.
+        
+        @return search URL template of the engine (string)
+        """
+        return self._suggestionsUrlTemplate
+    
+    def setSuggestionsUrlTemplate(self, suggestionsUrlTemplate):
+        """
+        Public method to set the engine suggestions URL template.
+        
+        @param suggestionsUrlTemplate suggestions URL template of the
+            engine (string)
+        """
+        self._suggestionsUrlTemplate = suggestionsUrlTemplate
+    
+    def suggestionsUrl(self, searchTerm):
+        """
+        Public method to get a URL ready for suggestions.
+        
+        @param searchTerm term to search for (string)
+        @return URL (QUrl)
+        """
+        if not self._suggestionsUrlTemplate:
+            return QUrl()
+        
+        ret = QUrl.fromEncoded(QByteArray(self.parseTemplate(
+            searchTerm, self._suggestionsUrlTemplate).encode("utf-8")))
+        
+        if self.__searchMethod != "post":
+            if qVersion() >= "5.0.0":
+                from PyQt5.QtCore import QUrlQuery
+                urlQuery = QUrlQuery(ret)
+                for parameter in self._suggestionsParameters:
+                    urlQuery.addQueryItem(
+                        parameter[0],
+                        self.parseTemplate(searchTerm, parameter[1]))
+                ret.setQuery(urlQuery)
+            else:
+                for parameter in self._suggestionsParameters:
+                    ret.addQueryItem(
+                        parameter[0],
+                        self.parseTemplate(searchTerm, parameter[1]))
+        
+        return ret
+    
+    def searchParameters(self):
+        """
+        Public method to get the search parameters of the engine.
+        
+        @return search parameters of the engine (list of two tuples)
+        """
+        return self._searchParameters[:]
+    
+    def setSearchParameters(self, searchParameters):
+        """
+        Public method to set the engine search parameters.
+        
+        @param searchParameters search parameters of the engine
+            (list of two tuples)
+        """
+        self._searchParameters = searchParameters[:]
+    
+    def suggestionsParameters(self):
+        """
+        Public method to get the suggestions parameters of the engine.
+        
+        @return suggestions parameters of the engine (list of two tuples)
+        """
+        return self._suggestionsParameters[:]
+    
+    def setSuggestionsParameters(self, suggestionsParameters):
+        """
+        Public method to set the engine suggestions parameters.
+        
+        @param suggestionsParameters suggestions parameters of the
+            engine (list of two tuples)
+        """
+        self._suggestionsParameters = suggestionsParameters[:]
+    
+    def searchMethod(self):
+        """
+        Public method to get the HTTP request method used to perform search
+        requests.
+        
+        @return HTTP request method (string)
+        """
+        return self.__searchMethod
+    
+    def setSearchMethod(self, method):
+        """
+        Public method to set the HTTP request method used to perform search
+        requests.
+        
+        @param method HTTP request method (string)
+        """
+        requestMethod = method.lower()
+        if requestMethod not in self.__requestMethods:
+            return
+        
+        self.__searchMethod = requestMethod
+    
+    def suggestionsMethod(self):
+        """
+        Public method to get the HTTP request method used to perform
+        suggestions requests.
+        
+        @return HTTP request method (string)
+        """
+        return self.__suggestionsMethod
+    
+    def setSuggestionsMethod(self, method):
+        """
+        Public method to set the HTTP request method used to perform
+        suggestions requests.
+        
+        @param method HTTP request method (string)
+        """
+        requestMethod = method.lower()
+        if requestMethod not in self.__requestMethods:
+            return
+        
+        self.__suggestionsMethod = requestMethod
+    
+    def imageUrl(self):
+        """
+        Public method to get the image URL of the engine.
+        
+        @return image URL of the engine (string)
+        """
+        return self._imageUrl
+    
+    def setImageUrl(self, imageUrl):
+        """
+        Public method to set the engine image URL.
+        
+        @param imageUrl image URL of the engine (string)
+        """
+        self._imageUrl = imageUrl
+    
+    def setImageUrlAndLoad(self, imageUrl):
+        """
+        Public method to set the engine image URL.
+        
+        @param imageUrl image URL of the engine (string)
+        """
+        self.setImageUrl(imageUrl)
+        self.__iconMoved = False
+        self.loadImage()
+    
+    def loadImage(self):
+        """
+        Public method to load the image of the engine.
+        """
+        if self.__networkAccessManager is None or not self._imageUrl:
+            return
+        
+        reply = self.__networkAccessManager.get(
+            QNetworkRequest(QUrl.fromEncoded(self._imageUrl.encode("utf-8"))))
+        reply.finished.connect(self.__imageObtained)
+        self.__replies.append(reply)
+    
+    def __imageObtained(self):
+        """
+        Private slot to receive the image of the engine.
+        """
+        reply = self.sender()
+        if reply is None:
+            return
+        
+        response = reply.readAll()
+        
+        reply.close()
+        if reply in self.__replies:
+            self.__replies.remove(reply)
+        reply.deleteLater()
+        
+        if response.isEmpty():
+            return
+        
+        if response.startsWith(b"<html>") or response.startsWith(b"HTML"):
+            self.__iconMoved = True
+            self.__image = QImage()
+        else:
+            self.__image.loadFromData(response)
+        self.imageChanged.emit()
+    
+    def image(self):
+        """
+        Public method to get the image of the engine.
+        
+        @return image of the engine (QImage)
+        """
+        if not self.__iconMoved and self.__image.isNull():
+            self.loadImage()
+        
+        return self.__image
+    
+    def setImage(self, image):
+        """
+        Public method to set the image of the engine.
+        
+        @param image image to be set (QImage)
+        """
+        if not self._imageUrl:
+            imageBuffer = QBuffer()
+            imageBuffer.open(QIODevice.ReadWrite)
+            if image.save(imageBuffer, "PNG"):
+                self._imageUrl = "data:image/png;base64,{0}".format(
+                    bytes(imageBuffer.buffer().toBase64()).decode())
+        
+        self.__image = QImage(image)
+        self.imageChanged.emit()
+    
+    def isValid(self):
+        """
+        Public method to check, if the engine is valid.
+        
+        @return flag indicating validity (boolean)
+        """
+        return self._name and self._searchUrlTemplate
+    
+    def __eq__(self, other):
+        """
+        Special method implementing the == operator.
+        
+        @param other reference to an open search engine (OpenSearchEngine)
+        @return flag indicating equality (boolean)
+        """
+        if not isinstance(other, OpenSearchEngine):
+            return NotImplemented
+        
+        return self._name == other._name and \
+            self._description == other._description and \
+            self._imageUrl == other._imageUrl and \
+            self._searchUrlTemplate == other._searchUrlTemplate and \
+            self._suggestionsUrlTemplate == other._suggestionsUrlTemplate and \
+            self._searchParameters == other._searchParameters and \
+            self._suggestionsParameters == other._suggestionsParameters
+    
+    def __lt__(self, other):
+        """
+        Special method implementing the < operator.
+        
+        @param other reference to an open search engine (OpenSearchEngine)
+        @return flag indicating less than (boolean)
+        """
+        if not isinstance(other, OpenSearchEngine):
+            return NotImplemented
+        
+        return self._name < other._name
+    
+    def requestSuggestions(self, searchTerm):
+        """
+        Public method to request suggestions.
+        
+        @param searchTerm term to get suggestions for (string)
+        """
+        if not searchTerm or not self.providesSuggestions():
+            return
+        
+        if self.__networkAccessManager is None:
+            return
+        
+        if self.__suggestionsReply is not None:
+            self.__suggestionsReply.finished.disconnect(
+                self.__suggestionsObtained)
+            self.__suggestionsReply.abort()
+            self.__suggestionsReply.deleteLater()
+            self.__suggestionsReply = None
+        
+        if self.__suggestionsMethod not in self.__requestMethods:
+            # ignore
+            return
+        
+        if self.__suggestionsMethod == "get":
+            self.__suggestionsReply = self.networkAccessManager().get(
+                QNetworkRequest(self.suggestionsUrl(searchTerm)))
+        else:
+            parameters = []
+            for parameter in self._suggestionsParameters:
+                parameters.append(parameter[0] + "=" + parameter[1])
+            data = "&".join(parameters)
+            self.__suggestionsReply = self.networkAccessManager().post(
+                QNetworkRequest(self.suggestionsUrl(searchTerm)), data)
+        self.__suggestionsReply.finished.connect(
+            self.__suggestionsObtained)
+    
+    def __suggestionsObtained(self):
+        """
+        Private slot to receive the suggestions.
+        """
+        if self.__suggestionsReply.error() == QNetworkReply.NoError:
+            buffer = bytes(self.__suggestionsReply.readAll())
+            response = Utilities.decodeBytes(buffer)
+            response = response.strip()
+            
+            self.__suggestionsReply.close()
+            self.__suggestionsReply.deleteLater()
+            self.__suggestionsReply = None
+            
+            if len(response) == 0:
+                return
+            
+            try:
+                result = json.loads(response)
+            except ValueError:
+                return
+            
+            try:
+                suggestions = result[1]
+            except IndexError:
+                return
+            
+            self.suggestions.emit(suggestions)
+    
+    def networkAccessManager(self):
+        """
+        Public method to get a reference to the network access manager object.
+        
+        @return reference to the network access manager object
+            (QNetworkAccessManager)
+        """
+        return self.__networkAccessManager
+    
+    def setNetworkAccessManager(self, networkAccessManager):
+        """
+        Public method to set the reference to the network access manager.
+        
+        @param networkAccessManager reference to the network access manager
+            object (QNetworkAccessManager)
+        """
+        self.__networkAccessManager = networkAccessManager
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/OpenSearch/OpenSearchEngineAction.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,53 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a QAction subclass for open search.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import QUrl
+from PyQt5.QtGui import QPixmap, QIcon
+from PyQt5.QtWidgets import QAction
+
+
+class OpenSearchEngineAction(QAction):
+    """
+    Class implementing a QAction subclass for open search.
+    """
+    def __init__(self, engine, parent=None):
+        """
+        Constructor
+        
+        @param engine reference to the open search engine object
+            (OpenSearchEngine)
+        @param parent reference to the parent object (QObject)
+        """
+        super(OpenSearchEngineAction, self).__init__(parent)
+        
+        self.__engine = engine
+        if self.__engine.networkAccessManager() is None:
+            import WebBrowser.WebBrowserWindow
+            self.__engine.setNetworkAccessManager(
+                WebBrowser.WebBrowserWindow.WebBrowserWindow.networkManager())
+        
+        self.setText(engine.name())
+        self.__imageChanged()
+        
+        engine.imageChanged.connect(self.__imageChanged)
+    
+    def __imageChanged(self):
+        """
+        Private slot handling a change of the associated image.
+        """
+        image = self.__engine.image()
+        if image.isNull():
+            import WebBrowser.WebBrowserWindow
+            self.setIcon(
+                WebBrowser.WebBrowserWindow.WebBrowserWindow.icon(
+                    QUrl(self.__engine.imageUrl())))
+        else:
+            self.setIcon(QIcon(QPixmap.fromImage(image)))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/OpenSearch/OpenSearchEngineModel.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,201 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a model for search engines.
+"""
+
+from __future__ import unicode_literals
+
+import re
+
+from PyQt5.QtCore import Qt, QUrl, QAbstractTableModel, QModelIndex
+from PyQt5.QtGui import QPixmap, QIcon
+
+
+class OpenSearchEngineModel(QAbstractTableModel):
+    """
+    Class implementing a model for search engines.
+    """
+    def __init__(self, manager, parent=None):
+        """
+        Constructor
+        
+        @param manager reference to the search engine manager
+            (OpenSearchManager)
+        @param parent reference to the parent object (QObject)
+        """
+        super(OpenSearchEngineModel, self).__init__(parent)
+        
+        self.__manager = manager
+        manager.changed.connect(self.__enginesChanged)
+        
+        self.__headers = [
+            self.tr("Name"),
+            self.tr("Keywords"),
+        ]
+    
+    def removeRows(self, row, count, parent=QModelIndex()):
+        """
+        Public method to remove entries from the model.
+        
+        @param row start row (integer)
+        @param count number of rows to remove (integer)
+        @param parent parent index (QModelIndex)
+        @return flag indicating success (boolean)
+        """
+        if parent.isValid():
+            return False
+        
+        if count <= 0:
+            return False
+        
+        if self.rowCount() <= 1:
+            return False
+        
+        lastRow = row + count - 1
+        
+        self.beginRemoveRows(parent, row, lastRow)
+        
+        nameList = self.__manager.allEnginesNames()
+        for index in range(row, lastRow + 1):
+            self.__manager.removeEngine(nameList[index])
+        
+        # removeEngine emits changed()
+        #self.endRemoveRows()
+        
+        return True
+    
+    def rowCount(self, parent=QModelIndex()):
+        """
+        Public method to get the number of rows of the model.
+        
+        @param parent parent index (QModelIndex)
+        @return number of rows (integer)
+        """
+        if parent.isValid():
+            return 0
+        else:
+            return self.__manager.enginesCount()
+    
+    def columnCount(self, parent=QModelIndex()):
+        """
+        Public method to get the number of columns of the model.
+        
+        @param parent parent index (QModelIndex)
+        @return number of columns (integer)
+        """
+        return 2
+    
+    def flags(self, index):
+        """
+        Public method to get flags for a model cell.
+        
+        @param index index of the model cell (QModelIndex)
+        @return flags (Qt.ItemFlags)
+        """
+        if index.column() == 1:
+            return Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsEditable
+        else:
+            return Qt.ItemIsEnabled | Qt.ItemIsSelectable
+    
+    def data(self, index, role):
+        """
+        Public method to get data from the model.
+        
+        @param index index to get data for (QModelIndex)
+        @param role role of the data to retrieve (integer)
+        @return requested data
+        """
+        if index.row() >= self.__manager.enginesCount() or index.row() < 0:
+            return None
+        
+        engine = self.__manager.engine(
+            self.__manager.allEnginesNames()[index.row()])
+        
+        if engine is None:
+            return None
+        
+        if index.column() == 0:
+            if role == Qt.DisplayRole:
+                return engine.name()
+                
+            elif role == Qt.DecorationRole:
+                image = engine.image()
+                if image.isNull():
+                    from WebBrowser.WebBrowserWindow import WebBrowserWindow
+                    icon = WebBrowserWindow.icon(QUrl(engine.imageUrl()))
+                else:
+                    icon = QIcon(QPixmap.fromImage(image))
+                return icon
+                
+            elif role == Qt.ToolTipRole:
+                description = self.tr("<strong>Description:</strong> {0}")\
+                    .format(engine.description())
+                if engine.providesSuggestions():
+                    description += "<br/>"
+                    description += self.tr(
+                        "<strong>Provides contextual suggestions</strong>")
+                
+                return description
+        elif index.column() == 1:
+            if role in [Qt.EditRole, Qt.DisplayRole]:
+                return ",".join(self.__manager.keywordsForEngine(engine))
+            elif role == Qt.ToolTipRole:
+                return self.tr(
+                    "Comma-separated list of keywords that may"
+                    " be entered in the location bar followed by search terms"
+                    " to search with this engine")
+        
+        return None
+    
+    def setData(self, index, value, role=Qt.EditRole):
+        """
+        Public method to set the data of a model cell.
+        
+        @param index index of the model cell (QModelIndex)
+        @param value value to be set
+        @param role role of the data (integer)
+        @return flag indicating success (boolean)
+        """
+        if not index.isValid() or index.column() != 1:
+            return False
+        
+        if index.row() >= self.rowCount() or index.row() < 0:
+            return False
+        
+        if role != Qt.EditRole:
+            return False
+        
+        engineName = self.__manager.allEnginesNames()[index.row()]
+        keywords = re.split("[ ,]+", value)
+        self.__manager.setKeywordsForEngine(
+            self.__manager.engine(engineName), keywords)
+        
+        return True
+    
+    def headerData(self, section, orientation, role=Qt.DisplayRole):
+        """
+        Public method to get the header data.
+        
+        @param section section number (integer)
+        @param orientation header orientation (Qt.Orientation)
+        @param role data role (integer)
+        @return header data
+        """
+        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
+            try:
+                return self.__headers[section]
+            except IndexError:
+                pass
+        
+        return None
+    
+    def __enginesChanged(self):
+        """
+        Private slot handling a change of the registered engines.
+        """
+        self.beginResetModel()
+        self.endResetModel()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/OpenSearch/OpenSearchManager.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,598 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a manager for open search engines.
+"""
+
+from __future__ import unicode_literals
+
+import os
+
+from PyQt5.QtCore import pyqtSignal, QObject, QUrl, QFile, QDir, QIODevice, \
+    QUrlQuery
+from PyQt5.QtWidgets import QLineEdit, QInputDialog
+from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply
+
+from E5Gui.E5Application import e5App
+from E5Gui import E5MessageBox
+
+from Utilities.AutoSaver import AutoSaver
+import Utilities
+import Preferences
+
+
+class OpenSearchManager(QObject):
+    """
+    Class implementing a manager for open search engines.
+    
+    @signal changed() emitted to indicate a change
+    @signal currentEngineChanged() emitted to indicate a change of
+            the current search engine
+    """
+    changed = pyqtSignal()
+    currentEngineChanged = pyqtSignal()
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object (QObject)
+        """
+        if parent is None:
+            parent = e5App()
+        super(OpenSearchManager, self).__init__(parent)
+        
+        self.__replies = []
+        self.__engines = {}
+        self.__keywords = {}
+        self.__current = ""
+        self.__loading = False
+        self.__saveTimer = AutoSaver(self, self.save)
+        
+        self.changed.connect(self.__saveTimer.changeOccurred)
+        
+        self.load()
+    
+    def close(self):
+        """
+        Public method to close the open search engines manager.
+        """
+        self.__saveTimer.saveIfNeccessary()
+    
+    def currentEngineName(self):
+        """
+        Public method to get the name of the current search engine.
+        
+        @return name of the current search engine (string)
+        """
+        return self.__current
+    
+    def setCurrentEngineName(self, name):
+        """
+        Public method to set the current engine by name.
+        
+        @param name name of the new current engine (string)
+        """
+        if name not in self.__engines:
+            return
+        
+        self.__current = name
+        self.currentEngineChanged.emit()
+        self.changed.emit()
+    
+    def currentEngine(self):
+        """
+        Public method to get a reference to the current engine.
+        
+        @return reference to the current engine (OpenSearchEngine)
+        """
+        if not self.__current or self.__current not in self.__engines:
+            return None
+        
+        return self.__engines[self.__current]
+    
+    def setCurrentEngine(self, engine):
+        """
+        Public method to set the current engine.
+        
+        @param engine reference to the new current engine (OpenSearchEngine)
+        """
+        if engine is None:
+            return
+        
+        for engineName in self.__engines:
+            if self.__engines[engineName] == engine:
+                self.setCurrentEngineName(engineName)
+                break
+    
+    def engine(self, name):
+        """
+        Public method to get a reference to the named engine.
+        
+        @param name name of the engine (string)
+        @return reference to the engine (OpenSearchEngine)
+        """
+        if name not in self.__engines:
+            return None
+        
+        return self.__engines[name]
+    
+    def engineExists(self, name):
+        """
+        Public method to check, if an engine exists.
+        
+        @param name name of the engine (string)
+        @return flag indicating an existing engine (boolean)
+        """
+        return name in self.__engines
+    
+    def allEnginesNames(self):
+        """
+        Public method to get a list of all engine names.
+        
+        @return sorted list of all engine names (list of strings)
+        """
+        return sorted(self.__engines.keys())
+    
+    def enginesCount(self):
+        """
+        Public method to get the number of available engines.
+        
+        @return number of engines (integer)
+        """
+        return len(self.__engines)
+    
+    def addEngine(self, engine):
+        """
+        Public method to add a new search engine.
+        
+        @param engine URL of the engine definition file (QUrl) or
+            name of a file containing the engine definition (string)
+            or reference to an engine object (OpenSearchEngine)
+        @return flag indicating success (boolean)
+        """
+        from .OpenSearchEngine import OpenSearchEngine
+        if isinstance(engine, QUrl):
+            return self.__addEngineByUrl(engine)
+        elif isinstance(engine, OpenSearchEngine):
+            return self.__addEngineByEngine(engine)
+        else:
+            return self.__addEngineByFile(engine)
+    
+    def __addEngineByUrl(self, url):
+        """
+        Private method to add a new search engine given its URL.
+        
+        @param url URL of the engine definition file (QUrl)
+        @return flag indicating success (boolean)
+        """
+        if not url.isValid():
+            return
+        
+        from WebBrowser.WebBrowserWindow import WebBrowserWindow
+
+        reply = WebBrowserWindow.networkManager().get(QNetworkRequest(url))
+        reply.finished.connect(self.__engineFromUrlAvailable)
+        reply.setParent(self)
+        self.__replies.append(reply)
+        
+        return True
+    
+    def __addEngineByFile(self, filename):
+        """
+        Private method to add a new search engine given a filename.
+        
+        @param filename name of a file containing the engine definition
+            (string)
+        @return flag indicating success (boolean)
+        """
+        file_ = QFile(filename)
+        if not file_.open(QIODevice.ReadOnly):
+            return False
+        
+        from .OpenSearchReader import OpenSearchReader
+        reader = OpenSearchReader()
+        engine = reader.read(file_)
+        
+        if not self.__addEngineByEngine(engine):
+            return False
+        
+        return True
+    
+    def __addEngineByEngine(self, engine):
+        """
+        Private method to add a new search engine given a reference to an
+        engine.
+        
+        @param engine reference to an engine object (OpenSearchEngine)
+        @return flag indicating success (boolean)
+        """
+        if engine is None:
+            return False
+        
+        if not engine.isValid():
+            return False
+        
+        if engine.name() in self.__engines:
+            return False
+        
+        engine.setParent(self)
+        self.__engines[engine.name()] = engine
+        
+        self.changed.emit()
+        
+        return True
+    
+    def addEngineFromForm(self, res, view):
+        """
+        Private method to add a new search engine from a form.
+        
+        @param res result of the JavaScript run on by
+            WebBrowserView.__addSearchEngine()
+        @type dict or None
+        @param view reference to the web browser view
+        @type WebBrowserView
+        """
+        if not res:
+            return
+        
+        method = res["method"]
+        isPost = method == "post"
+        actionUrl = QUrl(res["action"])
+        inputName = res["inputName"]
+        
+        if method != "get":
+            E5MessageBox.warning(
+                self,
+                self.tr("Method not supported"),
+                self.tr(
+                    """{0} method is not supported.""").format(method.upper()))
+            return
+        
+        if actionUrl.isRelative():
+            actionUrl = view.url().resolved(actionUrl)
+        
+        searchUrlQuery = QUrlQuery(actionUrl)
+        searchUrlQuery.addQueryItem(inputName, "{searchTerms}")
+        
+        inputFields = res["inputs"]
+        for inputField in inputFields:
+            name = inputField[0]
+            value = inputField[1]
+            
+            if not name or name == inputName or not value:
+                continue
+            
+            searchUrlQuery.addQueryItem(name, value)
+        
+        engineName, ok = QInputDialog.getText(
+            view,
+            self.tr("Engine name"),
+            self.tr("Enter a name for the engine"),
+            QLineEdit.Normal)
+        if not ok:
+            return
+        
+        actionUrl.setQuery(searchUrlQuery)
+        
+        from .OpenSearchEngine import OpenSearchEngine
+        engine = OpenSearchEngine()
+        engine.setName(engineName)
+        engine.setDescription(engineName)
+        engine.setSearchUrlTemplate(actionUrl.toDisplayString(QUrl.FullyDecoded))
+        engine.setImage(view.icon().pixmap(16, 16).toImage())
+        
+        self.__addEngineByEngine(engine)
+    
+    def removeEngine(self, name):
+        """
+        Public method to remove an engine.
+        
+        @param name name of the engine (string)
+        """
+        if len(self.__engines) <= 1:
+            return
+        
+        if name not in self.__engines:
+            return
+        
+        engine = self.__engines[name]
+        for keyword in [k for k in self.__keywords
+                        if self.__keywords[k] == engine]:
+            del self.__keywords[keyword]
+        del self.__engines[name]
+        
+        file_ = QDir(self.enginesDirectory()).filePath(
+            self.generateEngineFileName(name))
+        QFile.remove(file_)
+        
+        if name == self.__current:
+            self.setCurrentEngineName(list(self.__engines.keys())[0])
+        
+        self.changed.emit()
+    
+    def generateEngineFileName(self, engineName):
+        """
+        Public method to generate a valid engine file name.
+        
+        @param engineName name of the engine (string)
+        @return valid engine file name (string)
+        """
+        fileName = ""
+        
+        # strip special characters
+        for c in engineName:
+            if c.isspace():
+                fileName += '_'
+                continue
+            
+            if c.isalnum():
+                fileName += c
+        
+        fileName += ".xml"
+        
+        return fileName
+    
+    def saveDirectory(self, dirName):
+        """
+        Public method to save the search engine definitions to files.
+        
+        @param dirName name of the directory to write the files to (string)
+        """
+        dir = QDir()
+        if not dir.mkpath(dirName):
+            return
+        dir.setPath(dirName)
+        
+        from .OpenSearchWriter import OpenSearchWriter
+        writer = OpenSearchWriter()
+        
+        for engine in list(self.__engines.values()):
+            name = self.generateEngineFileName(engine.name())
+            fileName = dir.filePath(name)
+            
+            file = QFile(fileName)
+            if not file.open(QIODevice.WriteOnly):
+                continue
+            
+            writer.write(file, engine)
+    
+    def save(self):
+        """
+        Public method to save the search engines configuration.
+        """
+        if self.__loading:
+            return
+        
+        self.saveDirectory(self.enginesDirectory())
+        
+        Preferences.setWebBrowser("WebSearchEngine", self.__current)
+        keywords = []
+        for k in self.__keywords:
+            if self.__keywords[k]:
+                keywords.append((k, self.__keywords[k].name()))
+        Preferences.setWebBrowser("WebSearchKeywords", keywords)
+    
+    def loadDirectory(self, dirName):
+        """
+        Public method to load the search engine definitions from files.
+        
+        @param dirName name of the directory to load the files from (string)
+        @return flag indicating success (boolean)
+        """
+        if not QFile.exists(dirName):
+            return False
+        
+        success = False
+        
+        dir = QDir(dirName)
+        for name in dir.entryList(["*.xml"]):
+            fileName = dir.filePath(name)
+            if self.__addEngineByFile(fileName):
+                success = True
+        
+        return success
+    
+    def load(self):
+        """
+        Public method to load the search engines configuration.
+        """
+        self.__loading = True
+        self.__current = Preferences.getWebBrowser("WebSearchEngine")
+        keywords = Preferences.getWebBrowser("WebSearchKeywords")
+        
+        if not self.loadDirectory(self.enginesDirectory()):
+            self.restoreDefaults()
+        
+        for keyword, engineName in keywords:
+            self.__keywords[keyword] = self.engine(engineName)
+        
+        if self.__current not in self.__engines and \
+           len(self.__engines) > 0:
+            self.__current = list(self.__engines.keys())[0]
+        
+        self.__loading = False
+        self.currentEngineChanged.emit()
+    
+    def restoreDefaults(self):
+        """
+        Public method to restore the default search engines.
+        """
+        from .OpenSearchReader import OpenSearchReader
+        from .DefaultSearchEngines import DefaultSearchEngines_rc   # __IGNORE_WARNING__
+        
+        defaultEngineFiles = ["Amazoncom.xml", "Bing.xml",
+                              "DeEn_Beolingus.xml", "DuckDuckGo.xml",
+                              "Facebook.xml", "Google.xml",
+                              "Google_Im_Feeling_Lucky.xml", "LEO_DeuEng.xml",
+                              "LinuxMagazin.xml", "Reddit.xml", "Wikia.xml",
+                              "Wikia_en.xml", "Wikipedia.xml",
+                              "Wiktionary.xml", "Yahoo.xml", "YouTube.xml", ]
+        # Keep this list in sync with the contents of the resource file.
+
+        reader = OpenSearchReader()
+        for engineFileName in defaultEngineFiles:
+            engineFile = QFile(":/" + engineFileName)
+            if not engineFile.open(QIODevice.ReadOnly):
+                continue
+            engine = reader.read(engineFile)
+            self.__addEngineByEngine(engine)
+    
+    def enginesDirectory(self):
+        """
+        Public method to determine the directory containing the search engine
+        descriptions.
+        
+        @return directory name (string)
+        """
+        return os.path.join(
+            Utilities.getConfigDir(), "web_browser", "searchengines")
+    
+    def __confirmAddition(self, engine):
+        """
+        Private method to confirm the addition of a new search engine.
+        
+        @param engine reference to the engine to be added (OpenSearchEngine)
+        @return flag indicating the engine shall be added (boolean)
+        """
+        if engine is None or not engine.isValid():
+            return False
+        
+        host = QUrl(engine.searchUrlTemplate()).host()
+        
+        res = E5MessageBox.yesNo(
+            None,
+            "",
+            self.tr(
+                """<p>Do you want to add the following engine to your"""
+                """ list of search engines?<br/><br/>Name: {0}<br/>"""
+                """Searches on: {1}</p>""").format(engine.name(), host))
+        return res
+    
+    def __engineFromUrlAvailable(self):
+        """
+        Private slot to add a search engine from the net.
+        """
+        reply = self.sender()
+        if reply is None:
+            return
+        
+        if reply.error() != QNetworkReply.NoError:
+            reply.close()
+            if reply in self.__replies:
+                self.__replies.remove(reply)
+            return
+        
+        from .OpenSearchReader import OpenSearchReader
+        reader = OpenSearchReader()
+        engine = reader.read(reply)
+        
+        reply.close()
+        if reply in self.__replies:
+            self.__replies.remove(reply)
+        
+        if not engine.isValid():
+            return
+        
+        if self.engineExists(engine.name()):
+            return
+        
+        if not self.__confirmAddition(engine):
+            return
+        
+        if not self.__addEngineByEngine(engine):
+            return
+    
+    def convertKeywordSearchToUrl(self, keywordSearch):
+        """
+        Public method to get the search URL for a keyword search.
+        
+        @param keywordSearch search string for keyword search (string)
+        @return search URL (QUrl)
+        """
+        try:
+            keyword, term = keywordSearch.split(" ", 1)
+        except ValueError:
+            return QUrl()
+        
+        if not term:
+            return QUrl()
+        
+        engine = self.engineForKeyword(keyword)
+        if engine:
+            return engine.searchUrl(term)
+        
+        return QUrl()
+    
+    def engineForKeyword(self, keyword):
+        """
+        Public method to get the engine for a keyword.
+        
+        @param keyword keyword to get engine for (string)
+        @return reference to the search engine object (OpenSearchEngine)
+        """
+        if keyword and keyword in self.__keywords:
+            return self.__keywords[keyword]
+        
+        return None
+    
+    def setEngineForKeyword(self, keyword, engine):
+        """
+        Public method to set the engine for a keyword.
+        
+        @param keyword keyword to get engine for (string)
+        @param engine reference to the search engine object (OpenSearchEngine)
+            or None to remove the keyword
+        """
+        if not keyword:
+            return
+        
+        if engine is None:
+            try:
+                del self.__keywords[keyword]
+            except KeyError:
+                pass
+        else:
+            self.__keywords[keyword] = engine
+        
+        self.changed.emit()
+    
+    def keywordsForEngine(self, engine):
+        """
+        Public method to get the keywords for a given engine.
+        
+        @param engine reference to the search engine object (OpenSearchEngine)
+        @return list of keywords (list of strings)
+        """
+        return [k for k in self.__keywords if self.__keywords[k] == engine]
+    
+    def setKeywordsForEngine(self, engine, keywords):
+        """
+        Public method to set the keywords for an engine.
+        
+        @param engine reference to the search engine object (OpenSearchEngine)
+        @param keywords list of keywords (list of strings)
+        """
+        if engine is None:
+            return
+        
+        for keyword in self.keywordsForEngine(engine):
+            del self.__keywords[keyword]
+        
+        for keyword in keywords:
+            if not keyword:
+                continue
+            
+            self.__keywords[keyword] = engine
+        
+        self.changed.emit()
+    
+    def enginesChanged(self):
+        """
+        Public slot to tell the search engine manager, that something has
+        changed.
+        """
+        self.changed.emit()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/OpenSearch/OpenSearchReader.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,123 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a reader for open search engine descriptions.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import QXmlStreamReader, QIODevice, QCoreApplication
+
+
+class OpenSearchReader(QXmlStreamReader):
+    """
+    Class implementing a reader for open search engine descriptions.
+    """
+    def read(self, device):
+        """
+        Public method to read the description.
+        
+        @param device device to read the description from (QIODevice)
+        @return search engine object (OpenSearchEngine)
+        """
+        self.clear()
+        
+        if not device.isOpen():
+            device.open(QIODevice.ReadOnly)
+        
+        self.setDevice(device)
+        return self.__read()
+    
+    def __read(self):
+        """
+        Private method to read and parse the description.
+        
+        @return search engine object (OpenSearchEngine)
+        """
+        from .OpenSearchEngine import OpenSearchEngine
+        engine = OpenSearchEngine()
+        
+        while not self.isStartElement() and not self.atEnd():
+            self.readNext()
+        
+        if self.name() != "OpenSearchDescription" or \
+           self.namespaceUri() != "http://a9.com/-/spec/opensearch/1.1/":
+            self.raiseError(QCoreApplication.translate(
+                "OpenSearchReader",
+                "The file is not an OpenSearch 1.1 file."))
+            return engine
+        
+        while not self.atEnd():
+            self.readNext()
+            
+            if not self.isStartElement():
+                continue
+            
+            if self.name() == "ShortName":
+                engine.setName(self.readElementText())
+                
+            elif self.name() == "Description":
+                engine.setDescription(self.readElementText())
+                
+            elif self.name() == "Url":
+                type_ = self.attributes().value("type")
+                url = self.attributes().value("template")
+                method = self.attributes().value("method")
+                
+                if type_ == "application/x-suggestions+json" and \
+                   engine.suggestionsUrlTemplate():
+                    continue
+                
+                if (not type_ or
+                    type_ == "text/html" or
+                    type_ == "application/xhtml+xml") and \
+                   engine.suggestionsUrlTemplate():
+                    continue
+                
+                if not url:
+                    continue
+                
+                parameters = []
+                
+                self.readNext()
+                
+                while not (self.isEndElement() and self.name() == "Url"):
+                    if not self.isStartElement() or \
+                       (self.name() != "Param" and self.name() != "Parameter"):
+                        self.readNext()
+                        continue
+                    
+                    key = self.attributes().value("name")
+                    value = self.attributes().value("value")
+                    
+                    if key and value:
+                        parameters.append((key, value))
+                    
+                    while not self.isEndElement():
+                        self.readNext()
+                
+                if type_ == "application/x-suggestions+json":
+                    engine.setSuggestionsUrlTemplate(url)
+                    engine.setSuggestionsParameters(parameters)
+                    engine.setSuggestionsMethod(method)
+                elif not type_ or \
+                    type_ == "text/html" or \
+                        type_ == "application/xhtml+xml":
+                    engine.setSearchUrlTemplate(url)
+                    engine.setSearchParameters(parameters)
+                    engine.setSearchMethod(method)
+                
+            elif self.name() == "Image":
+                engine.setImageUrl(self.readElementText())
+            
+            if engine.name() and \
+               engine.description() and \
+               engine.suggestionsUrlTemplate() and \
+               engine.searchUrlTemplate() and \
+               engine.imageUrl():
+                break
+        
+        return engine
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/OpenSearch/OpenSearchWriter.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,100 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a writer for open search engine descriptions.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import QXmlStreamWriter, QIODevice
+
+
+class OpenSearchWriter(QXmlStreamWriter):
+    """
+    Class implementing a writer for open search engine descriptions.
+    """
+    def __init__(self):
+        """
+        Constructor
+        """
+        super(OpenSearchWriter, self).__init__()
+        
+        self.setAutoFormatting(True)
+    
+    def write(self, device, engine):
+        """
+        Public method to write the description of an engine.
+        
+        @param device reference to the device to write to (QIODevice)
+        @param engine reference to the engine (OpenSearchEngine)
+        @return flag indicating success (boolean)
+        """
+        if engine is None:
+            return False
+        
+        if not device.isOpen():
+            if not device.open(QIODevice.WriteOnly):
+                return False
+        
+        self.setDevice(device)
+        self.__write(engine)
+        return True
+    
+    def __write(self, engine):
+        """
+        Private method to write the description of an engine.
+        
+        @param engine reference to the engine (OpenSearchEngine)
+        """
+        self.writeStartDocument()
+        self.writeStartElement("OpenSearchDescription")
+        self.writeDefaultNamespace("http://a9.com/-/spec/opensearch/1.1/")
+        
+        if engine.name():
+            self.writeTextElement("ShortName", engine.name())
+        
+        if engine.description():
+            self.writeTextElement("Description", engine.description())
+        
+        if engine.searchUrlTemplate():
+            self.writeStartElement("Url")
+            self.writeAttribute("method", engine.searchMethod())
+            self.writeAttribute("type", "text/html")
+            self.writeAttribute("template", engine.searchUrlTemplate())
+            
+            if len(engine.searchParameters()) > 0:
+                self.writeNamespace(
+                    "http://a9.com/-/spec/opensearch/extensions/"
+                    "parameters/1.0/", "p")
+                for parameter in engine.searchParameters():
+                    self.writeStartElement("p:Parameter")
+                    self.writeAttribute("name", parameter[0])
+                    self.writeAttribute("value", parameter[1])
+            
+            self.writeEndElement()
+        
+        if engine.suggestionsUrlTemplate():
+            self.writeStartElement("Url")
+            self.writeAttribute("method", engine.suggestionsMethod())
+            self.writeAttribute("type", "application/x-suggestions+json")
+            self.writeAttribute("template", engine.suggestionsUrlTemplate())
+            
+            if len(engine.suggestionsParameters()) > 0:
+                self.writeNamespace(
+                    "http://a9.com/-/spec/opensearch/extensions/"
+                    "parameters/1.0/", "p")
+                for parameter in engine.suggestionsParameters():
+                    self.writeStartElement("p:Parameter")
+                    self.writeAttribute("name", parameter[0])
+                    self.writeAttribute("value", parameter[1])
+            
+            self.writeEndElement()
+        
+        if engine.imageUrl():
+            self.writeTextElement("Image", engine.imageUrl())
+        
+        self.writeEndElement()
+        self.writeEndDocument()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/OpenSearch/__init__.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Package implementing the opensearch search engine interfaces.
+"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/PageScreenDialog.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,136 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog to save a screenshot of a web page.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSlot, Qt, QFile, QFileInfo, QSize
+from PyQt5.QtGui import QImage, QPainter, QPixmap
+from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QAbstractButton
+
+from E5Gui import E5FileDialog, E5MessageBox
+
+from .Ui_PageScreenDialog import Ui_PageScreenDialog
+
+
+class PageScreenDialog(QDialog, Ui_PageScreenDialog):
+    """
+    Class documentation goes here.
+    """
+    def __init__(self, view, visibleOnly=False, parent=None):
+        """
+        Constructor
+        
+        @param view reference to the web view containing the page to be saved
+            (WebBrowserView)
+        @param visibleOnly flag indicating to just save the visible part
+            of the page (boolean)
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(PageScreenDialog, self).__init__(parent)
+        self.setupUi(self)
+        self.setWindowFlags(Qt.Window)
+        
+        self.__view = view
+        self.__createPixmap(visibleOnly)
+        self.pageScreenLabel.setPixmap(self.__pagePixmap)
+    
+    def __createPixmap(self, visibleOnly):
+        """
+        Private slot to create a pixmap of the associated view's page.
+        
+        @param visibleOnly flag indicating to just save the visible part
+            of the page (boolean)
+        """
+        res = self.__view.page().execJavaScript(
+            "(function() {"
+            "var res = {"
+            "    width: 0,"
+            "    height: 0,"
+            "};"
+            "res.width = document.body.scrollWidth;"
+            "res.height = document.body.scrollHeight;"
+            "return res;"
+            "})()"
+        )
+        if res is not None:
+            if visibleOnly:
+                image = QImage(QSize(res["width"], self.__view.height()),
+                               QImage.Format_ARGB32)
+                painter = QPainter(image)
+                self.__view.render(painter)
+                painter.end()
+            else:
+                # TODO: once QWebEngineView supports this
+                image = QImage(QSize(res["width"], self.__view.height()),
+                               QImage.Format_ARGB32)
+                painter = QPainter(image)
+                self.__view.render(painter)
+                painter.end()
+            
+            self.__pagePixmap = QPixmap.fromImage(image)
+    
+    def __savePageScreen(self):
+        """
+        Private slot to save the page screen.
+        
+        @return flag indicating success (boolean)
+        """
+        fileName = E5FileDialog.getSaveFileName(
+            self,
+            self.tr("Save Page Screen"),
+            self.tr("screen.png"),
+            self.tr("Portable Network Graphics File (*.png)"),
+            E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite))
+        if not fileName:
+            return False
+        
+        if QFileInfo(fileName).exists():
+            res = E5MessageBox.yesNo(
+                self,
+                self.tr("Save Page Screen"),
+                self.tr("<p>The file <b>{0}</b> already exists."
+                        " Overwrite it?</p>").format(fileName),
+                icon=E5MessageBox.Warning)
+            if not res:
+                return False
+        
+        file = QFile(fileName)
+        if not file.open(QFile.WriteOnly):
+            E5MessageBox.warning(
+                self,
+                self.tr("Save Page Screen"),
+                self.tr("Cannot write file '{0}:\n{1}.")
+                .format(fileName, file.errorString()))
+            return False
+        
+        res = self.__pagePixmap.save(file)
+        file.close()
+        
+        if not res:
+            E5MessageBox.warning(
+                self,
+                self.tr("Save Page Screen"),
+                self.tr("Cannot write file '{0}:\n{1}.")
+                .format(fileName, file.errorString()))
+            return False
+        
+        return True
+    
+    @pyqtSlot(QAbstractButton)
+    def on_buttonBox_clicked(self, button):
+        """
+        Private slot to handle clicks of the dialog buttons.
+        
+        @param button button that was clicked (QAbstractButton)
+        """
+        if button == self.buttonBox.button(QDialogButtonBox.Cancel):
+            self.reject()
+        elif button == self.buttonBox.button(QDialogButtonBox.Save):
+            if self.__savePageScreen():
+                self.accept()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/PageScreenDialog.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>PageScreenDialog</class>
+ <widget class="QDialog" name="PageScreenDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>500</width>
+    <height>450</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Page Screen</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QScrollArea" name="scrollArea">
+     <property name="frameShape">
+      <enum>QFrame::NoFrame</enum>
+     </property>
+     <property name="frameShadow">
+      <enum>QFrame::Plain</enum>
+     </property>
+     <property name="lineWidth">
+      <number>0</number>
+     </property>
+     <property name="widgetResizable">
+      <bool>true</bool>
+     </property>
+     <widget class="QWidget" name="scrollAreaWidgetContents">
+      <property name="geometry">
+       <rect>
+        <x>0</x>
+        <y>0</y>
+        <width>482</width>
+        <height>403</height>
+       </rect>
+      </property>
+      <layout class="QHBoxLayout" name="horizontalLayout">
+       <property name="margin">
+        <number>0</number>
+       </property>
+       <item>
+        <widget class="QLabel" name="pageScreenLabel">
+         <property name="alignment">
+          <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </widget>
+    </widget>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Save</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <tabstops>
+  <tabstop>scrollArea</tabstop>
+  <tabstop>buttonBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Passwords/LoginForm.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,34 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a data structure for login forms.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import QUrl
+
+
+class LoginForm(object):
+    """
+    Class implementing a data structure for login forms.
+    """
+    def __init__(self):
+        """
+        Constructor
+        """
+        self.url = QUrl()
+        self.name = ""
+        self.postData = ""
+    
+    def isValid(self):
+        """
+        Public method to test for validity.
+        
+        @return flag indicating a valid form (boolean)
+        """
+        return not self.url.isEmpty() and \
+            bool(self.postData)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Passwords/PasswordManager.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,415 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the password manager.
+"""
+
+from __future__ import unicode_literals
+
+import os
+
+from PyQt5.QtCore import pyqtSignal, QObject, QByteArray, QUrl, \
+    QCoreApplication, QXmlStreamReader
+from PyQt5.QtWidgets import QApplication
+from PyQt5.QtWebEngineWidgets import QWebEngineScript
+
+from E5Gui import E5MessageBox
+from E5Gui.E5ProgressDialog import E5ProgressDialog
+
+from Utilities.AutoSaver import AutoSaver
+import Utilities
+import Utilities.crypto
+import Preferences
+
+import WebBrowser.WebBrowserWindow
+from ..Tools import Scripts
+
+
+class PasswordManager(QObject):
+    """
+    Class implementing the password manager.
+    
+    @signal changed() emitted to indicate a change
+    @signal passwordsSaved() emitted after the passwords were saved
+    """
+    changed = pyqtSignal()
+    passwordsSaved = pyqtSignal()
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object (QObject)
+        """
+        super(PasswordManager, self).__init__(parent)
+        
+        # setup userscript to monitor forms
+        script = QWebEngineScript()
+        script.setName("_eric_passwordmonitor")
+        script.setInjectionPoint(QWebEngineScript.DocumentReady)
+        script.setWorldId(QWebEngineScript.MainWorld)
+        script.setRunsOnSubFrames(True)
+        script.setSourceCode(Scripts.setupFormObserver())
+        profile = WebBrowser.WebBrowserWindow.WebBrowserWindow.webProfile()
+        profile.scripts().insert(script)
+        
+        self.__logins = {}
+        self.__loginForms = {}
+        self.__never = []
+        self.__loaded = False
+        self.__saveTimer = AutoSaver(self, self.save)
+        
+        self.changed.connect(self.__saveTimer.changeOccurred)
+    
+    def clear(self):
+        """
+        Public slot to clear the saved passwords.
+        """
+        if not self.__loaded:
+            self.__load()
+        
+        self.__logins = {}
+        self.__loginForms = {}
+        self.__never = []
+        self.__saveTimer.changeOccurred()
+        self.__saveTimer.saveIfNeccessary()
+        
+        self.changed.emit()
+    
+    def getLogin(self, url, realm):
+        """
+        Public method to get the login credentials.
+        
+        @param url URL to get the credentials for (QUrl)
+        @param realm realm to get the credentials for (string)
+        @return tuple containing the user name (string) and password (string)
+        """
+        if not self.__loaded:
+            self.__load()
+        
+        key = self.__createKey(url, realm)
+        try:
+            return self.__logins[key][0], Utilities.crypto.pwConvert(
+                self.__logins[key][1], encode=False)
+        except KeyError:
+            return "", ""
+    
+    def setLogin(self, url, realm, username, password):
+        """
+        Public method to set the login credentials.
+        
+        @param url URL to set the credentials for (QUrl)
+        @param realm realm to set the credentials for (string)
+        @param username username for the login (string)
+        @param password password for the login (string)
+        """
+        if not self.__loaded:
+            self.__load()
+        
+        key = self.__createKey(url, realm)
+        self.__logins[key] = (
+            username,
+            Utilities.crypto.pwConvert(password, encode=True)
+        )
+        self.changed.emit()
+    
+    def __createKey(self, url, realm):
+        """
+        Private method to create the key string for the login credentials.
+        
+        @param url URL to get the credentials for (QUrl)
+        @param realm realm to get the credentials for (string)
+        @return key string (string)
+        """
+        authority = url.authority()
+        if authority.startswith("@"):
+            authority = authority[1:]
+        if realm:
+            key = "{0}://{1} ({2})".format(
+                url.scheme(), authority, realm)
+        else:
+            key = "{0}://{1}".format(url.scheme(), authority)
+        return key
+    
+    def getFileName(self):
+        """
+        Public method to get the file name of the passwords file.
+        
+        @return name of the passwords file (string)
+        """
+        return os.path.join(Utilities.getConfigDir(),
+                            "web_browser", "logins.xml")
+    
+    def save(self):
+        """
+        Public slot to save the login entries to disk.
+        """
+        if not self.__loaded:
+            return
+        
+        from WebBrowser.WebBrowserWindow import WebBrowserWindow
+        if not WebBrowserWindow.isPrivate():
+            from .PasswordWriter import PasswordWriter
+            loginFile = self.getFileName()
+            writer = PasswordWriter()
+            if not writer.write(
+                    loginFile, self.__logins, self.__loginForms, self.__never):
+                E5MessageBox.critical(
+                    None,
+                    self.tr("Saving login data"),
+                    self.tr(
+                        """<p>Login data could not be saved to"""
+                        """ <b>{0}</b></p>"""
+                    ).format(loginFile))
+            else:
+                self.passwordsSaved.emit()
+    
+    def __load(self):
+        """
+        Private method to load the saved login credentials.
+        """
+        if self.__loaded:
+            return
+        
+        loginFile = self.getFileName()
+        if os.path.exists(loginFile):
+            from .PasswordReader import PasswordReader
+            reader = PasswordReader()
+            self.__logins, self.__loginForms, self.__never = \
+                reader.read(loginFile)
+            if reader.error() != QXmlStreamReader.NoError:
+                E5MessageBox.warning(
+                    None,
+                    self.tr("Loading login data"),
+                    self.tr("""Error when loading login data on"""
+                            """ line {0}, column {1}:\n{2}""")
+                    .format(reader.lineNumber(),
+                            reader.columnNumber(),
+                            reader.errorString()))
+        
+        self.__loaded = True
+    
+    def reload(self):
+        """
+        Public method to reload the login data.
+        """
+        if not self.__loaded:
+            return
+        
+        self.__loaded = False
+        self.__load()
+    
+    def close(self):
+        """
+        Public method to close the passwords manager.
+        """
+        self.__saveTimer.saveIfNeccessary()
+    
+    def removePassword(self, site):
+        """
+        Public method to remove a password entry.
+        
+        @param site web site name (string)
+        """
+        if site in self.__logins:
+            del self.__logins[site]
+            if site in self.__loginForms:
+                del self.__loginForms[site]
+            self.changed.emit()
+    
+    def allSiteNames(self):
+        """
+        Public method to get a list of all site names.
+        
+        @return sorted list of all site names (list of strings)
+        """
+        if not self.__loaded:
+            self.__load()
+        
+        return sorted(self.__logins.keys())
+    
+    def sitesCount(self):
+        """
+        Public method to get the number of available sites.
+        
+        @return number of sites (integer)
+        """
+        if not self.__loaded:
+            self.__load()
+        
+        return len(self.__logins)
+    
+    def siteInfo(self, site):
+        """
+        Public method to get a reference to the named site.
+        
+        @param site web site name (string)
+        @return tuple containing the user name (string) and password (string)
+        """
+        if not self.__loaded:
+            self.__load()
+        
+        if site not in self.__logins:
+            return None
+        
+        return self.__logins[site][0], Utilities.crypto.pwConvert(
+            self.__logins[site][1], encode=False)
+    
+    def formSubmitted(self, urlStr, userName, password, data, page):
+        """
+        Public method to record login data.
+        
+        @param urlStr form submission URL
+        @type str
+        @param userName name of the user
+        @type str
+        @param password user password
+        @type str
+        @param data data to be submitted
+        @type QByteArray
+        @param page reference to the calling page
+        @type QWebEnginePage
+        """
+        # shall passwords be saved?
+        if not Preferences.getUser("SavePasswords"):
+            return
+        
+        if WebBrowser.WebBrowserWindow.WebBrowserWindow.isPrivate():
+            return
+        
+        if not self.__loaded:
+            self.__load()
+        
+        if urlStr in self.__never:
+            return
+        
+        if userName and password:
+            url = QUrl(urlStr)
+            url = self.__stripUrl(url)
+            key = self.__createKey(url, "")
+            if key not in self.__loginForms:
+                mb = E5MessageBox.E5MessageBox(
+                    E5MessageBox.Question,
+                    self.tr("Save password"),
+                    self.tr(
+                        """<b>Would you like to save this password?</b><br/>"""
+                        """To review passwords you have saved and remove"""
+                        """ them, use the password management dialog of the"""
+                        """ Settings menu."""),
+                    modal=True, parent=page.view())
+                neverButton = mb.addButton(
+                    self.tr("Never for this site"),
+                    E5MessageBox.DestructiveRole)
+                noButton = mb.addButton(
+                    self.tr("Not now"), E5MessageBox.RejectRole)
+                mb.addButton(E5MessageBox.Yes)
+                mb.exec_()
+                if mb.clickedButton() == neverButton:
+                    self.__never.append(url.toString())
+                    return
+                elif mb.clickedButton() == noButton:
+                    return
+        
+            self.__logins[key] = \
+                (userName,
+                 Utilities.crypto.pwConvert(password, encode=True))
+            from .LoginForm import LoginForm
+            form = LoginForm()
+            form.url = url
+            form.name = userName
+            form.postData = Utilities.crypto.pwConvert(
+                bytes(data).decode("utf-8"), encode=True)
+            self.__loginForms[key] = form
+            self.changed.emit()
+    
+    def __stripUrl(self, url):
+        """
+        Private method to strip off all unneeded parts of a URL.
+        
+        @param url URL to be stripped (QUrl)
+        @return stripped URL (QUrl)
+        """
+        cleanUrl = QUrl(url)
+        cleanUrl.setQuery("")
+        cleanUrl.setUserInfo("")
+        
+        authority = cleanUrl.authority()
+        if authority.startswith("@"):
+            authority = authority[1:]
+        cleanUrl = QUrl("{0}://{1}{2}".format(
+            cleanUrl.scheme(), authority, cleanUrl.path()))
+        cleanUrl.setFragment("")
+        return cleanUrl
+    
+    def completePage(self, page):
+        """
+        Public slot to complete login forms with saved data.
+        
+        @param page reference to the web page (WebBrowserPage)
+        """
+        if page is None:
+            return
+        
+        if not self.__loaded:
+            self.__load()
+        
+        url = page.url()
+        url = self.__stripUrl(url)
+        key = self.__createKey(url, "")
+        if key not in self.__loginForms or \
+           key not in self.__logins:
+            return
+        
+        form = self.__loginForms[key]
+        if form.url != url:
+            return
+        
+        postData = QByteArray(Utilities.crypto.pwConvert(
+                form.postData, encode=False).encode("utf-8"))
+        script = Scripts.completeFormData(postData)
+        page.runJavaScript(script)
+    
+    def masterPasswordChanged(self, oldPassword, newPassword):
+        """
+        Public slot to handle the change of the master password.
+        
+        @param oldPassword current master password (string)
+        @param newPassword new master password (string)
+        """
+        if not self.__loaded:
+            self.__load()
+        
+        progress = E5ProgressDialog(
+            self.tr("Re-encoding saved passwords..."),
+            None, 0, len(self.__logins) + len(self.__loginForms),
+            self.tr("%v/%m Passwords"),
+            QApplication.activeModalWidget())
+        progress.setMinimumDuration(0)
+        progress.setWindowTitle(self.tr("Passwords"))
+        count = 0
+        
+        # step 1: do the logins
+        for key in self.__logins:
+            progress.setValue(count)
+            QCoreApplication.processEvents()
+            username, hash = self.__logins[key]
+            hash = Utilities.crypto.pwRecode(hash, oldPassword, newPassword)
+            self.__logins[key] = (username, hash)
+            count += 1
+        
+        # step 2: do the login forms
+        for key in self.__loginForms:
+            progress.setValue(count)
+            QCoreApplication.processEvents()
+            postData = self.__loginForms[key].postData
+            postData = Utilities.crypto.pwRecode(
+                postData, oldPassword, newPassword)
+            self.__loginForms[key].postData = postData
+            count += 1
+        
+        progress.setValue(len(self.__logins) + len(self.__loginForms))
+        QCoreApplication.processEvents()
+        self.changed.emit()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Passwords/PasswordModel.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,156 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a model for password management.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import Qt, QModelIndex, QAbstractTableModel
+
+
+class PasswordModel(QAbstractTableModel):
+    """
+    Class implementing a model for password management.
+    """
+    def __init__(self, manager, parent=None):
+        """
+        Constructor
+        
+        @param manager reference to the password manager (PasswordManager)
+        @param parent reference to the parent object (QObject)
+        """
+        super(PasswordModel, self).__init__(parent)
+        
+        self.__manager = manager
+        manager.changed.connect(self.__passwordsChanged)
+        
+        self.__headers = [
+            self.tr("Website"),
+            self.tr("Username"),
+            self.tr("Password")
+        ]
+        
+        self.__showPasswords = False
+    
+    def setShowPasswords(self, on):
+        """
+        Public methods to show passwords.
+        
+        @param on flag indicating if passwords shall be shown (boolean)
+        """
+        self.__showPasswords = on
+        self.beginResetModel()
+        self.endResetModel()
+    
+    def showPasswords(self):
+        """
+        Public method to indicate, if passwords shall be shown.
+        
+        @return flag indicating if passwords shall be shown (boolean)
+        """
+        return self.__showPasswords
+    
+    def __passwordsChanged(self):
+        """
+        Private slot handling a change of the registered passwords.
+        """
+        self.beginResetModel()
+        self.endResetModel()
+    
+    def removeRows(self, row, count, parent=QModelIndex()):
+        """
+        Public method to remove entries from the model.
+        
+        @param row start row (integer)
+        @param count number of rows to remove (integer)
+        @param parent parent index (QModelIndex)
+        @return flag indicating success (boolean)
+        """
+        if parent.isValid():
+            return False
+        
+        if count <= 0:
+            return False
+        
+        lastRow = row + count - 1
+        
+        self.beginRemoveRows(parent, row, lastRow)
+        
+        siteList = self.__manager.allSiteNames()
+        for index in range(row, lastRow + 1):
+            self.__manager.removePassword(siteList[index])
+        
+        # removeEngine emits changed()
+        #self.endRemoveRows()
+        
+        return True
+    
+    def rowCount(self, parent=QModelIndex()):
+        """
+        Public method to get the number of rows of the model.
+        
+        @param parent parent index (QModelIndex)
+        @return number of rows (integer)
+        """
+        if parent.isValid():
+            return 0
+        else:
+            return self.__manager.sitesCount()
+    
+    def columnCount(self, parent=QModelIndex()):
+        """
+        Public method to get the number of columns of the model.
+        
+        @param parent parent index (QModelIndex)
+        @return number of columns (integer)
+        """
+        if self.__showPasswords:
+            return 3
+        else:
+            return 2
+    
+    def data(self, index, role):
+        """
+        Public method to get data from the model.
+        
+        @param index index to get data for (QModelIndex)
+        @param role role of the data to retrieve (integer)
+        @return requested data
+        """
+        if index.row() >= self.__manager.sitesCount() or index.row() < 0:
+            return None
+        
+        site = self.__manager.allSiteNames()[index.row()]
+        siteInfo = self.__manager.siteInfo(site)
+        
+        if siteInfo is None:
+            return None
+        
+        if role == Qt.DisplayRole:
+            if index.column() == 0:
+                return site
+            elif index.column() in [1, 2]:
+                return siteInfo[index.column() - 1]
+        
+        return None
+    
+    def headerData(self, section, orientation, role=Qt.DisplayRole):
+        """
+        Public method to get the header data.
+        
+        @param section section number (integer)
+        @param orientation header orientation (Qt.Orientation)
+        @param role data role (integer)
+        @return header data
+        """
+        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
+            try:
+                return self.__headers[section]
+            except IndexError:
+                pass
+        
+        return None
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Passwords/PasswordReader.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,176 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a class to read login data files.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import QXmlStreamReader, QIODevice, QFile, \
+    QCoreApplication, QUrl, QByteArray
+
+
+class PasswordReader(QXmlStreamReader):
+    """
+    Class implementing a reader object for login data files.
+    """
+    def __init__(self):
+        """
+        Constructor
+        """
+        super(PasswordReader, self).__init__()
+    
+    def read(self, fileNameOrDevice):
+        """
+        Public method to read a login data file.
+        
+        @param fileNameOrDevice name of the file to read (string)
+            or reference to the device to read (QIODevice)
+        @return tuple containing the logins, forms and never URLs
+        """
+        self.__logins = {}
+        self.__loginForms = {}
+        self.__never = []
+        
+        if isinstance(fileNameOrDevice, QIODevice):
+            self.setDevice(fileNameOrDevice)
+        else:
+            f = QFile(fileNameOrDevice)
+            if not f.exists():
+                return self.__logins, self.__loginForms, self.__never
+            f.open(QFile.ReadOnly)
+            self.setDevice(f)
+        
+        while not self.atEnd():
+            self.readNext()
+            if self.isStartElement():
+                version = self.attributes().value("version")
+                if self.name() == "Password" and \
+                   (not version or version == "2.0"):
+                    self.__readPasswords()
+                else:
+                    self.raiseError(QCoreApplication.translate(
+                        "PasswordReader",
+                        "The file is not a Passwords version 2.0 file."))
+        
+        return self.__logins, self.__loginForms, self.__never
+    
+    def __readPasswords(self):
+        """
+        Private method to read and parse the login data file.
+        """
+        if not self.isStartElement() and self.name() != "Password":
+            return
+        
+        while not self.atEnd():
+            self.readNext()
+            if self.isEndElement():
+                break
+            
+            if self.isStartElement():
+                if self.name() == "Logins":
+                    self.__readLogins()
+                elif self.name() == "Forms":
+                    self.__readForms()
+                elif self.name() == "Nevers":
+                    self.__readNevers()
+                else:
+                    self.__skipUnknownElement()
+    
+    def __readLogins(self):
+        """
+        Private method to read the login information.
+        """
+        if not self.isStartElement() and self.name() != "Logins":
+            return
+        
+        while not self.atEnd():
+            self.readNext()
+            if self.isEndElement():
+                if self.name() == "Login":
+                    continue
+                else:
+                    break
+            
+            if self.isStartElement():
+                if self.name() == "Login":
+                    attributes = self.attributes()
+                    key = attributes.value("key")
+                    user = attributes.value("user")
+                    password = attributes.value("password")
+                    self.__logins[key] = (user, password)
+                else:
+                    self.__skipUnknownElement()
+    
+    def __readForms(self):
+        """
+        Private method to read the forms information.
+        """
+        if not self.isStartElement() and self.name() != "Forms":
+            return
+        
+        while not self.atEnd():
+            self.readNext()
+            if self.isStartElement():
+                if self.name() == "Form":
+                    from .LoginForm import LoginForm
+                    attributes = self.attributes()
+                    key = attributes.value("key")
+                    form = LoginForm()
+                    form.url = QUrl(attributes.value("url"))
+                    form.name = attributes.value("name")
+                    
+                elif self.name() == "PostData":
+                    form.postData = self.readElementText()
+                else:
+                    self.__skipUnknownElement()
+            
+            if self.isEndElement():
+                if self.name() == "Form":
+                    self.__loginForms[key] = form
+                    continue
+                elif self.name() == "PostData":
+                    continue
+                elif self.name() in ["Elements", "Element"]:
+                    continue
+                else:
+                    break
+    
+    def __readNevers(self):
+        """
+        Private method to read the never URLs.
+        """
+        if not self.isStartElement() and self.name() != "Nevers":
+            return
+        
+        while not self.atEnd():
+            self.readNext()
+            if self.isEndElement():
+                if self.name() == "Never":
+                    continue
+                else:
+                    break
+            
+            if self.isStartElement():
+                if self.name() == "Never":
+                    self.__never.append(self.attributes().value("url"))
+                else:
+                    self.__skipUnknownElement()
+    
+    def __skipUnknownElement(self):
+        """
+        Private method to skip over all unknown elements.
+        """
+        if not self.isStartElement():
+            return
+        
+        while not self.atEnd():
+            self.readNext()
+            if self.isEndElement():
+                break
+            
+            if self.isStartElement():
+                self.__skipUnknownElement()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Passwords/PasswordWriter.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,112 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a class to write login data files.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import QXmlStreamWriter, QIODevice, QFile
+
+
+class PasswordWriter(QXmlStreamWriter):
+    """
+    Class implementing a writer object to generate login data files.
+    """
+    def __init__(self):
+        """
+        Constructor
+        """
+        super(PasswordWriter, self).__init__()
+        
+        self.setAutoFormatting(True)
+    
+    def write(self, fileNameOrDevice, logins, forms, nevers):
+        """
+        Public method to write an login data file.
+        
+        @param fileNameOrDevice name of the file to write (string)
+            or device to write to (QIODevice)
+        @param logins dictionary with login data (user name, password)
+        @param forms list of forms data (list of LoginForm)
+        @param nevers list of URLs to never store data for (list of strings)
+        @return flag indicating success (boolean)
+        """
+        if isinstance(fileNameOrDevice, QIODevice):
+            f = fileNameOrDevice
+        else:
+            f = QFile(fileNameOrDevice)
+            if not f.open(QFile.WriteOnly):
+                return False
+        
+        self.setDevice(f)
+        return self.__write(logins, forms, nevers)
+    
+    def __write(self, logins, forms, nevers):
+        """
+        Private method to write an login data file.
+        
+        @param logins dictionary with login data (user name, password)
+        @param forms list of forms data (list of LoginForm)
+        @param nevers list of URLs to never store data for (list of strings)
+        @return flag indicating success (boolean)
+        """
+        self.writeStartDocument()
+        self.writeDTD("<!DOCTYPE passwords>")
+        self.writeStartElement("Password")
+        self.writeAttribute("version", "2.0")
+        
+        if logins:
+            self.__writeLogins(logins)
+        if forms:
+            self.__writeForms(forms)
+        if nevers:
+            self.__writeNevers(nevers)
+        
+        self.writeEndDocument()
+        return True
+    
+    def __writeLogins(self, logins):
+        """
+        Private method to write the login data.
+        
+        @param logins dictionary with login data (user name, password)
+        """
+        self.writeStartElement("Logins")
+        for key, login in logins.items():
+            self.writeEmptyElement("Login")
+            self.writeAttribute("key", key)
+            self.writeAttribute("user", login[0])
+            self.writeAttribute("password", login[1])
+        self.writeEndElement()
+    
+    def __writeForms(self, forms):
+        """
+        Private method to write forms data.
+        
+        @param forms list of forms data (list of LoginForm)
+        """
+        self.writeStartElement("Forms")
+        for key, form in forms.items():
+            self.writeStartElement("Form")
+            self.writeAttribute("key", key)
+            self.writeAttribute("url", form.url.toString())
+            self.writeAttribute("name", str(form.name))
+            self.writeTextElement("PostData", form.postData)
+            self.writeEndElement()
+        self.writeEndElement()
+    
+    def __writeNevers(self, nevers):
+        """
+        Private method to write the URLs never to store login data for.
+        
+        @param nevers list of URLs to never store data for (list of strings)
+        """
+        self.writeStartElement("Nevers")
+        for never in nevers:
+            self.writeEmptyElement("Never")
+            self.writeAttribute("url", never)
+        self.writeEndElement()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Passwords/PasswordsDialog.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,98 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog to show all saved logins.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSlot, QSortFilterProxyModel
+from PyQt5.QtGui import QFont, QFontMetrics
+from PyQt5.QtWidgets import QDialog
+
+from E5Gui import E5MessageBox
+
+from .Ui_PasswordsDialog import Ui_PasswordsDialog
+
+
+class PasswordsDialog(QDialog, Ui_PasswordsDialog):
+    """
+    Class implementing a dialog to show all saved logins.
+    """
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(PasswordsDialog, self).__init__(parent)
+        self.setupUi(self)
+        
+        self.__showPasswordsText = self.tr("Show Passwords")
+        self.__hidePasswordsText = self.tr("Hide Passwords")
+        self.passwordsButton.setText(self.__showPasswordsText)
+        
+        self.removeButton.clicked.connect(
+            self.passwordsTable.removeSelected)
+        self.removeAllButton.clicked.connect(self.passwordsTable.removeAll)
+        
+        import WebBrowser.WebBrowserWindow
+        from .PasswordModel import PasswordModel
+        
+        self.passwordsTable.verticalHeader().hide()
+        self.__passwordModel = PasswordModel(
+            WebBrowser.WebBrowserWindow.WebBrowserWindow.passwordManager(),
+            self)
+        self.__proxyModel = QSortFilterProxyModel(self)
+        self.__proxyModel.setSourceModel(self.__passwordModel)
+        self.searchEdit.textChanged.connect(
+            self.__proxyModel.setFilterFixedString)
+        self.passwordsTable.setModel(self.__proxyModel)
+        
+        fm = QFontMetrics(QFont())
+        height = fm.height() + fm.height() // 3
+        self.passwordsTable.verticalHeader().setDefaultSectionSize(height)
+        self.passwordsTable.verticalHeader().setMinimumSectionSize(-1)
+        
+        self.__calculateHeaderSizes()
+    
+    def __calculateHeaderSizes(self):
+        """
+        Private method to calculate the section sizes of the horizontal header.
+        """
+        fm = QFontMetrics(QFont())
+        for section in range(self.__passwordModel.columnCount()):
+            header = self.passwordsTable.horizontalHeader()\
+                .sectionSizeHint(section)
+            if section == 0:
+                header = fm.width("averagebiglongsitename")
+            elif section == 1:
+                header = fm.width("averagelongusername")
+            elif section == 2:
+                header = fm.width("averagelongpassword")
+            buffer = fm.width("mm")
+            header += buffer
+            self.passwordsTable.horizontalHeader()\
+                .resizeSection(section, header)
+        self.passwordsTable.horizontalHeader().setStretchLastSection(True)
+    
+    @pyqtSlot()
+    def on_passwordsButton_clicked(self):
+        """
+        Private slot to switch the password display mode.
+        """
+        if self.__passwordModel.showPasswords():
+            self.__passwordModel.setShowPasswords(False)
+            self.passwordsButton.setText(self.__showPasswordsText)
+        else:
+            res = E5MessageBox.yesNo(
+                self,
+                self.tr("Saved Passwords"),
+                self.tr("""Do you really want to show passwords?"""))
+            if res:
+                self.__passwordModel.setShowPasswords(True)
+                self.passwordsButton.setText(self.__hidePasswordsText)
+        self.__calculateHeaderSizes()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Passwords/PasswordsDialog.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,202 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>PasswordsDialog</class>
+ <widget class="QDialog" name="PasswordsDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>500</width>
+    <height>350</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Saved Passwords</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_2">
+     <item>
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <layout class="QHBoxLayout" name="horizontalLayout">
+       <property name="spacing">
+        <number>0</number>
+       </property>
+       <item>
+        <widget class="E5ClearableLineEdit" name="searchEdit">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="minimumSize">
+          <size>
+           <width>300</width>
+           <height>0</height>
+          </size>
+         </property>
+         <property name="toolTip">
+          <string>Enter search term</string>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="E5TableView" name="passwordsTable">
+     <property name="alternatingRowColors">
+      <bool>true</bool>
+     </property>
+     <property name="selectionBehavior">
+      <enum>QAbstractItemView::SelectRows</enum>
+     </property>
+     <property name="textElideMode">
+      <enum>Qt::ElideMiddle</enum>
+     </property>
+     <property name="showGrid">
+      <bool>false</bool>
+     </property>
+     <property name="sortingEnabled">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_3">
+     <item>
+      <widget class="QPushButton" name="removeButton">
+       <property name="toolTip">
+        <string>Press to remove the selected entries</string>
+       </property>
+       <property name="text">
+        <string>&amp;Remove</string>
+       </property>
+       <property name="autoDefault">
+        <bool>false</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="removeAllButton">
+       <property name="toolTip">
+        <string>Press to remove all entries</string>
+       </property>
+       <property name="text">
+        <string>Remove &amp;All</string>
+       </property>
+       <property name="autoDefault">
+        <bool>false</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="horizontalSpacer_2">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>208</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QPushButton" name="passwordsButton">
+       <property name="toolTip">
+        <string>Press to toggle the display of passwords</string>
+       </property>
+       <property name="text">
+        <string/>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Close</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>E5ClearableLineEdit</class>
+   <extends>QLineEdit</extends>
+   <header>E5Gui/E5LineEdit.h</header>
+  </customwidget>
+  <customwidget>
+   <class>E5TableView</class>
+   <extends>QTableView</extends>
+   <header>E5Gui/E5TableView.h</header>
+  </customwidget>
+ </customwidgets>
+ <tabstops>
+  <tabstop>searchEdit</tabstop>
+  <tabstop>passwordsTable</tabstop>
+  <tabstop>removeButton</tabstop>
+  <tabstop>removeAllButton</tabstop>
+  <tabstop>passwordsButton</tabstop>
+  <tabstop>buttonBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>PasswordsDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>237</x>
+     <y>340</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>PasswordsDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>325</x>
+     <y>340</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Passwords/__init__.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Package implementing the password management interface.
+"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/PersonalInformationManager/PersonalDataDialog.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,71 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog to enter personal data.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtWidgets import QDialog
+
+from .Ui_PersonalDataDialog import Ui_PersonalDataDialog
+
+import UI.PixmapCache
+import Preferences
+
+
+class PersonalDataDialog(QDialog, Ui_PersonalDataDialog):
+    """
+    Class implementing a dialog to enter personal data.
+    """
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(PersonalDataDialog, self).__init__(parent)
+        self.setupUi(self)
+        
+        self.iconLabel.setPixmap(UI.PixmapCache.getPixmap("pim48.png"))
+        
+        self.firstnameEdit.setText(Preferences.getWebBrowser("PimFirstName"))
+        self.lastnameEdit.setText(Preferences.getWebBrowser("PimLastName"))
+        self.fullnameEdit.setText(Preferences.getWebBrowser("PimFullName"))
+        self.emailEdit.setText(Preferences.getWebBrowser("PimEmail"))
+        self.phoneEdit.setText(Preferences.getWebBrowser("PimPhone"))
+        self.mobileEdit.setText(Preferences.getWebBrowser("PimMobile"))
+        self.addressEdit.setText(Preferences.getWebBrowser("PimAddress"))
+        self.cityEdit.setText(Preferences.getWebBrowser("PimCity"))
+        self.zipEdit.setText(Preferences.getWebBrowser("PimZip"))
+        self.stateEdit.setText(Preferences.getWebBrowser("PimState"))
+        self.countryEdit.setText(Preferences.getWebBrowser("PimCountry"))
+        self.homepageEdit.setText(Preferences.getWebBrowser("PimHomePage"))
+        self.special1Edit.setText(Preferences.getWebBrowser("PimSpecial1"))
+        self.special2Edit.setText(Preferences.getWebBrowser("PimSpecial2"))
+        self.special3Edit.setText(Preferences.getWebBrowser("PimSpecial3"))
+        self.special4Edit.setText(Preferences.getWebBrowser("PimSpecial4"))
+    
+    def storeData(self):
+        """
+        Public method to store the entered personal information.
+        """
+        Preferences.setWebBrowser("PimFirstName", self.firstnameEdit.text())
+        Preferences.setWebBrowser("PimLastName", self.lastnameEdit.text())
+        Preferences.setWebBrowser("PimFullName", self.fullnameEdit.text())
+        Preferences.setWebBrowser("PimEmail", self.emailEdit.text())
+        Preferences.setWebBrowser("PimPhone", self.phoneEdit.text())
+        Preferences.setWebBrowser("PimMobile", self.mobileEdit.text())
+        Preferences.setWebBrowser("PimAddress", self.addressEdit.text())
+        Preferences.setWebBrowser("PimCity", self.cityEdit.text())
+        Preferences.setWebBrowser("PimZip", self.zipEdit.text())
+        Preferences.setWebBrowser("PimState", self.stateEdit.text())
+        Preferences.setWebBrowser("PimCountry", self.countryEdit.text())
+        Preferences.setWebBrowser("PimHomePage", self.homepageEdit.text())
+        Preferences.setWebBrowser("PimSpecial1", self.special1Edit.text())
+        Preferences.setWebBrowser("PimSpecial2", self.special2Edit.text())
+        Preferences.setWebBrowser("PimSpecial3", self.special3Edit.text())
+        Preferences.setWebBrowser("PimSpecial4", self.special4Edit.text())
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/PersonalInformationManager/PersonalDataDialog.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,384 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>PersonalDataDialog</class>
+ <widget class="QDialog" name="PersonalDataDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>600</width>
+    <height>400</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Personal Information</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <spacer name="horizontalSpacer_2">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QLabel" name="iconLabel">
+       <property name="minimumSize">
+        <size>
+         <width>48</width>
+         <height>48</height>
+        </size>
+       </property>
+       <property name="text">
+        <string notr="true">Icon</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLabel" name="label">
+       <property name="text">
+        <string>&lt;h2&gt;Personal Information&lt;/h2&gt;</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QLabel" name="label_01">
+     <property name="text">
+      <string>Your personal information that will be used on webpages.</string>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignCenter</set>
+     </property>
+     <property name="wordWrap">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <layout class="QGridLayout" name="gridLayout">
+     <item row="0" column="0">
+      <widget class="QLabel" name="label_02">
+       <property name="text">
+        <string>First Name:</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="1">
+      <widget class="QLineEdit" name="firstnameEdit"/>
+     </item>
+     <item row="0" column="2">
+      <widget class="QLabel" name="label_08">
+       <property name="text">
+        <string>ZIP Code:</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="3">
+      <widget class="QLineEdit" name="zipEdit"/>
+     </item>
+     <item row="1" column="0">
+      <widget class="QLabel" name="label_03">
+       <property name="text">
+        <string>Last Name:</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="1">
+      <widget class="QLineEdit" name="lastnameEdit"/>
+     </item>
+     <item row="1" column="2">
+      <widget class="QLabel" name="label_09">
+       <property name="text">
+        <string>State/Region:</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="3">
+      <widget class="QLineEdit" name="stateEdit"/>
+     </item>
+     <item row="2" column="0">
+      <widget class="QLabel" name="label_18">
+       <property name="text">
+        <string>Full Name:</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="1">
+      <widget class="QLineEdit" name="fullnameEdit"/>
+     </item>
+     <item row="2" column="2">
+      <widget class="QLabel" name="label_10">
+       <property name="text">
+        <string>Country:</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="3">
+      <widget class="QLineEdit" name="countryEdit"/>
+     </item>
+     <item row="3" column="0">
+      <widget class="QLabel" name="label_12">
+       <property name="text">
+        <string>E-mail:</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+       </property>
+      </widget>
+     </item>
+     <item row="3" column="1">
+      <widget class="QLineEdit" name="emailEdit"/>
+     </item>
+     <item row="3" column="2">
+      <widget class="QLabel" name="label_11">
+       <property name="text">
+        <string>Home Page:</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+       </property>
+      </widget>
+     </item>
+     <item row="3" column="3">
+      <widget class="QLineEdit" name="homepageEdit"/>
+     </item>
+     <item row="4" column="0">
+      <widget class="QLabel" name="label_04">
+       <property name="text">
+        <string>Phone:</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+       </property>
+      </widget>
+     </item>
+     <item row="4" column="1">
+      <widget class="QLineEdit" name="phoneEdit"/>
+     </item>
+     <item row="4" column="2">
+      <widget class="QLabel" name="label_13">
+       <property name="text">
+        <string>Custom 1:</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+       </property>
+      </widget>
+     </item>
+     <item row="4" column="3">
+      <widget class="QLineEdit" name="special1Edit"/>
+     </item>
+     <item row="5" column="0">
+      <widget class="QLabel" name="label_05">
+       <property name="text">
+        <string>Mobile Phone:</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+       </property>
+      </widget>
+     </item>
+     <item row="5" column="1">
+      <widget class="QLineEdit" name="mobileEdit"/>
+     </item>
+     <item row="5" column="2">
+      <widget class="QLabel" name="label_14">
+       <property name="text">
+        <string>Custom 2:</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+       </property>
+      </widget>
+     </item>
+     <item row="5" column="3">
+      <widget class="QLineEdit" name="special2Edit"/>
+     </item>
+     <item row="6" column="0">
+      <widget class="QLabel" name="label_06">
+       <property name="text">
+        <string>Address:</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+       </property>
+      </widget>
+     </item>
+     <item row="6" column="1">
+      <widget class="QLineEdit" name="addressEdit"/>
+     </item>
+     <item row="6" column="2">
+      <widget class="QLabel" name="label_15">
+       <property name="text">
+        <string>Custom 3:</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+       </property>
+      </widget>
+     </item>
+     <item row="6" column="3">
+      <widget class="QLineEdit" name="special3Edit"/>
+     </item>
+     <item row="7" column="0">
+      <widget class="QLabel" name="label_07">
+       <property name="text">
+        <string>City:</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+       </property>
+      </widget>
+     </item>
+     <item row="7" column="1">
+      <widget class="QLineEdit" name="cityEdit"/>
+     </item>
+     <item row="7" column="2">
+      <widget class="QLabel" name="label_17">
+       <property name="text">
+        <string>Custom 4:</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+       </property>
+      </widget>
+     </item>
+     <item row="7" column="3">
+      <widget class="QLineEdit" name="special4Edit"/>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QLabel" name="label_16">
+     <property name="text">
+      <string>&lt;b&gt;Note:&lt;/b&gt; Press Ctrl+ENTER to autofill form fields for which personal entries were found.</string>
+     </property>
+     <property name="wordWrap">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <spacer name="verticalSpacer">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>31</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <tabstops>
+  <tabstop>firstnameEdit</tabstop>
+  <tabstop>lastnameEdit</tabstop>
+  <tabstop>fullnameEdit</tabstop>
+  <tabstop>emailEdit</tabstop>
+  <tabstop>phoneEdit</tabstop>
+  <tabstop>mobileEdit</tabstop>
+  <tabstop>addressEdit</tabstop>
+  <tabstop>cityEdit</tabstop>
+  <tabstop>zipEdit</tabstop>
+  <tabstop>stateEdit</tabstop>
+  <tabstop>countryEdit</tabstop>
+  <tabstop>homepageEdit</tabstop>
+  <tabstop>special1Edit</tabstop>
+  <tabstop>special2Edit</tabstop>
+  <tabstop>special3Edit</tabstop>
+  <tabstop>special4Edit</tabstop>
+  <tabstop>buttonBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>PersonalDataDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>PersonalDataDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/PersonalInformationManager/PersonalInformationManager.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,281 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a personal information manager used to complete form
+fields.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import Qt, QObject, QPoint
+from PyQt5.QtWidgets import QDialog, QMenu
+
+import Preferences
+import UI.PixmapCache
+
+
+class PersonalInformationManager(QObject):
+    """
+    Class implementing the personal information manager used to complete form
+    fields.
+    """
+    FullName = 0
+    LastName = 1
+    FirstName = 2
+    Email = 3
+    Mobile = 4
+    Phone = 5
+    Address = 6
+    City = 7
+    Zip = 8
+    State = 9
+    Country = 10
+    HomePage = 11
+    Special1 = 12
+    Special2 = 13
+    Special3 = 14
+    Special4 = 15
+    Max = 16
+    Invalid = 256
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object (QObject)
+        """
+        super(PersonalInformationManager, self).__init__(parent)
+        
+        self.__loaded = False
+        self.__allInfo = {}
+        self.__infoMatches = {}
+        self.__translations = {}
+        
+        self.__view = None
+        self.__clickedPos = QPoint()
+    
+    def __loadSettings(self):
+        """
+        Private method to load the settings.
+        """
+        self.__allInfo[self.FullName] = \
+            Preferences.getWebBrowser("PimFullName")
+        self.__allInfo[self.LastName] = \
+            Preferences.getWebBrowser("PimLastName")
+        self.__allInfo[self.FirstName] = \
+            Preferences.getWebBrowser("PimFirstName")
+        self.__allInfo[self.Email] = Preferences.getWebBrowser("PimEmail")
+        self.__allInfo[self.Mobile] = Preferences.getWebBrowser("PimMobile")
+        self.__allInfo[self.Phone] = Preferences.getWebBrowser("PimPhone")
+        self.__allInfo[self.Address] = Preferences.getWebBrowser("PimAddress")
+        self.__allInfo[self.City] = Preferences.getWebBrowser("PimCity")
+        self.__allInfo[self.Zip] = Preferences.getWebBrowser("PimZip")
+        self.__allInfo[self.State] = Preferences.getWebBrowser("PimState")
+        self.__allInfo[self.Country] = Preferences.getWebBrowser("PimCountry")
+        self.__allInfo[self.HomePage] = \
+            Preferences.getWebBrowser("PimHomePage")
+        self.__allInfo[self.Special1] = \
+            Preferences.getWebBrowser("PimSpecial1")
+        self.__allInfo[self.Special2] = \
+            Preferences.getWebBrowser("PimSpecial2")
+        self.__allInfo[self.Special3] = \
+            Preferences.getWebBrowser("PimSpecial3")
+        self.__allInfo[self.Special4] = \
+            Preferences.getWebBrowser("PimSpecial4")
+        
+        self.__translations[self.FullName] = self.tr("Full Name")
+        self.__translations[self.LastName] = self.tr("Last Name")
+        self.__translations[self.FirstName] = self.tr("First Name")
+        self.__translations[self.Email] = self.tr("E-mail")
+        self.__translations[self.Mobile] = self.tr("Mobile")
+        self.__translations[self.Phone] = self.tr("Phone")
+        self.__translations[self.Address] = self.tr("Address")
+        self.__translations[self.City] = self.tr("City")
+        self.__translations[self.Zip] = self.tr("ZIP Code")
+        self.__translations[self.State] = self.tr("State/Region")
+        self.__translations[self.Country] = self.tr("Country")
+        self.__translations[self.HomePage] = self.tr("Home Page")
+        self.__translations[self.Special1] = self.tr("Custom 1")
+        self.__translations[self.Special2] = self.tr("Custom 2")
+        self.__translations[self.Special3] = self.tr("Custom 3")
+        self.__translations[self.Special4] = self.tr("Custom 4")
+        
+        self.__infoMatches[self.FullName] = ["fullname", "realname"]
+        self.__infoMatches[self.LastName] = ["lastname", "surname"]
+        self.__infoMatches[self.FirstName] = ["firstname", "name"]
+        self.__infoMatches[self.Email] = ["email", "e-mail", "mail"]
+        self.__infoMatches[self.Mobile] = ["mobile", "mobilephone"]
+        self.__infoMatches[self.Phone] = ["phone", "telephone"]
+        self.__infoMatches[self.Address] = ["address"]
+        self.__infoMatches[self.City] = ["city"]
+        self.__infoMatches[self.Zip] = ["zip"]
+        self.__infoMatches[self.State] = ["state", "region"]
+        self.__infoMatches[self.Country] = ["country"]
+        self.__infoMatches[self.HomePage] = ["homepage", "www"]
+        
+        self.__loaded = True
+    
+    def showConfigurationDialog(self):
+        """
+        Public method to show the configuration dialog.
+        """
+        from .PersonalDataDialog import PersonalDataDialog
+        dlg = PersonalDataDialog()
+        if dlg.exec_() == QDialog.Accepted:
+            dlg.storeData()
+            self.__loadSettings()
+    
+    def createSubMenu(self, menu, view, hitTestResult):
+        """
+        Public method to create the personal information sub-menu.
+        
+        @param menu reference to the main menu (QMenu)
+        @param view reference to the view (HelpBrowser)
+        @param hitTestResult reference to the hit test result
+            (WebHitTestResult)
+        """
+        self.__view = view
+        self.__clickedPos = hitTestResult.pos()
+        
+        if not hitTestResult.isContentEditable():
+            return
+        
+        if not self.__loaded:
+            self.__loadSettings()
+        
+        submenu = QMenu(self.tr("Insert Personal Information"), menu)
+        submenu.setIcon(UI.PixmapCache.getIcon("pim.png"))
+        
+        for key, info in sorted(self.__allInfo.items()):
+            if info:
+                act = submenu.addAction(
+                    self.__translations[key], self.__insertData)
+                act.setData(info)
+        
+        submenu.addSeparator()
+        submenu.addAction(self.tr("Edit Personal Information"),
+                          self.showConfigurationDialog)
+        
+        menu.addMenu(submenu)
+        menu.addSeparator()
+    
+    def __insertData(self):
+        """
+        Private slot to insert the selected personal information.
+        """
+        if self.__view is None or self.__clickedPos.isNull():
+            return
+        
+        act = self.sender()
+        if act is None:
+            return
+        
+        info = act.data()
+        info = info.replace('"', '\\"')
+        
+        source = """
+            var e = document.elementFromPoint({0}, {1});
+            if (e) {{
+                var v = e.value.substring(0, e.selectionStart);
+                v += "{2}" + e.value.substring(e.selectionEnd);
+                e.value = v;
+            }}""".format(self.__clickedPos.x(), self.__clickedPos.y(), info)
+        self.__view.page().runJavaScript(source)
+    
+    def viewKeyPressEvent(self, view, evt):
+        """
+        Protected method to handle key press events we are interested in.
+        
+        @param view reference to the view (HelpBrowser)
+        @param evt reference to the key event (QKeyEvent)
+        @return flag indicating handling of the event (boolean)
+        """
+        if view is None:
+            return False
+        
+        isEnter = evt.key() in [Qt.Key_Return, Qt.Key_Enter]
+        isControlModifier = evt.modifiers() & Qt.ControlModifier
+        if not isEnter or not isControlModifier:
+            return False
+        
+        if not self.__loaded:
+            self.__loadSettings()
+        
+        source = """
+            var inputs = document.getElementsByTagName('input');
+            var table = {0};
+            for (var i = 0; i < inputs.length; ++i) {{
+                var input = inputs[i];
+                if (input.type != 'text' || input.name == '')
+                    continue;
+                for (var key in table) {{
+                    if (!table.hasOwnProperty(key))
+                        continue;
+                    if (key == input.name || input.name.indexOf(key) != -1) {{
+                        input.value = table[key];
+                        break;
+                    }}
+                }}
+            }}""".format(self.__matchingJsTable())
+        view.page().runJavaScript(source)
+        
+        return True
+    
+    def connectPage(self, page):
+        """
+        Public method to allow the personal information manager to connect to
+        the page.
+        
+        @param page reference to the web page (HelpWebPage)
+        """
+        page.loadFinished.connect(self.__pageLoadFinished)
+    
+    def __pageLoadFinished(self, ok):
+        """
+        Private slot to handle the completion of a page load.
+        
+        @param ok flag indicating a successful load (boolean)
+        """
+        page = self.sender()
+        if page is None or not ok:
+            return
+        
+        if not self.__loaded:
+            self.__loadSettings()
+        
+        source = """
+            var inputs = document.getElementsByTagName('input');
+            var table = {0};
+            for (var i = 0; i < inputs.length; ++i) {{
+                var input = inputs[i];
+                if (input.type != 'text' || input.name == '')
+                    continue;
+                for (var key in table) {{
+                    if (!table.hasOwnProperty(key) || table[key] == '')
+                        continue;
+                    if (key == input.name || input.name.indexOf(key) != -1) {{
+                        input.style['-webkit-appearance'] = 'none';
+                        input.style['-webkit-box-shadow'] = 
+                            'inset 0 0 2px 1px #000EEE';
+                        break;
+                    }}
+                }}
+            }}""".format(self.__matchingJsTable())
+        page.runJavaScript(source)
+    
+    def __matchingJsTable(self):
+        """
+        Private method to create the common part of the JavaScript sources.
+        
+        @return JavaScript source
+        @rtype str
+        """
+        values = []
+        for key, names in self.__infoMatches.items():
+            for name in names:
+                value = self.__allInfo[key].replace('"', '\\"')
+                values.append('"{0}":"{1}"'.format(name, value))
+        return "{{ {0} }}".format(",".join(values))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/PersonalInformationManager/__init__.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,9 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Package implementing the personal information manager for the completion of
+forms.
+"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/QtHelp/HelpDocsInstaller.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,245 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a thread class populating and updating the QtHelp
+documentation database.
+"""
+
+from __future__ import unicode_literals
+
+import os
+
+from PyQt5.QtCore import pyqtSignal, qVersion, QThread, Qt, QMutex, \
+    QDateTime, QDir, QLibraryInfo, QFileInfo
+from PyQt5.QtHelp import QHelpEngineCore
+
+from eric6config import getConfig
+
+
+class HelpDocsInstaller(QThread):
+    """
+    Class implementing the worker thread populating and updating the QtHelp
+    documentation database.
+    
+    @signal errorMessage(str) emitted, if an error occurred during
+        the installation of the documentation
+    @signal docsInstalled(bool) emitted after the installation has finished
+    """
+    errorMessage = pyqtSignal(str)
+    docsInstalled = pyqtSignal(bool)
+    
+    def __init__(self, collection):
+        """
+        Constructor
+        
+        @param collection full pathname of the collection file (string)
+        """
+        super(HelpDocsInstaller, self).__init__()
+        
+        self.__abort = False
+        self.__collection = collection
+        self.__mutex = QMutex()
+    
+    def stop(self):
+        """
+        Public slot to stop the installation procedure.
+        """
+        if not self.isRunning():
+            return
+        
+        self.__mutex.lock()
+        self.__abort = True
+        self.__mutex.unlock()
+        self.wait()
+    
+    def installDocs(self):
+        """
+        Public method to start the installation procedure.
+        """
+        self.start(QThread.LowPriority)
+    
+    def run(self):
+        """
+        Public method executed by the thread.
+        """
+        engine = QHelpEngineCore(self.__collection)
+        engine.setupData()
+        changes = False
+        
+        qt4Docs = ["designer", "linguist", "qt"]
+        qt5Docs = [
+            "activeqt", "qdoc", "qmake", "qt3d", "qt3drenderer",
+            "qtandroidextras", "qtassistant", "qtbluetooth", "qtcanvas3d",
+            "qtconcurrent", "qtcore", "qtdbus", "qtdesigner", "qtdoc",
+            "qtenginio", "qtenginiooverview", "qtenginoqml",
+            "qtgraphicaleffects", "qtgui", "qthelp", "qtimageformats",
+            "qtlabscontrols", "qtlinguist", "qtlocation", "qtmaxextras",
+            "qtmultimedia", "qtmultimediawidgets", "qtnetwork", "qtnfc",
+            "qtopengl", "qtplatformheaders", "qtpositioning", "qtprintsupport",
+            "qtqml", "qtquick", "qtquickcontrols", "qtquickdialogs",
+            "qtquickextras", "qtquicklayouts", "qtscript", "qtscripttools",
+            "qtsensors", "qtserialbus", "qtserialport", "qtsql", "qtsvg",
+            "qttestlib", "qtuitools", "qtwebchannel", "qtwebengine",
+            "qtwebenginewidgets", "qtwebkit", "qtwebkitexamples",
+            "qtwebsockets", "qtwebview", "qtwidgets", "qtwinextras",
+            "qtx11extras", "qtxml", "qtxmlpatterns"]
+        for qtDocs, version in [(qt4Docs, 4), (qt5Docs, 5)]:
+            for doc in qtDocs:
+                changes |= self.__installQtDoc(doc, version, engine)
+                self.__mutex.lock()
+                if self.__abort:
+                    engine = None
+                    self.__mutex.unlock()
+                    return
+                self.__mutex.unlock()
+        
+        changes |= self.__installEric6Doc(engine)
+        engine = None
+        del engine
+        self.docsInstalled.emit(changes)
+    
+    def __installQtDoc(self, name, version, engine):
+        """
+        Private method to install/update a Qt help document.
+        
+        @param name name of the Qt help document (string)
+        @param version Qt version of the help documens (integer)
+        @param engine reference to the help engine (QHelpEngineCore)
+        @return flag indicating success (boolean)
+        """
+        versionKey = "qt_version_{0}@@{1}".format(version, name)
+        info = engine.customValue(versionKey, "")
+        lst = info.split('|')
+        
+        dt = QDateTime()
+        if len(lst) and lst[0]:
+            dt = QDateTime.fromString(lst[0], Qt.ISODate)
+        
+        qchFile = ""
+        if len(lst) == 2:
+            qchFile = lst[1]
+        
+        if version == 4:
+            docsPath = QDir(
+                QLibraryInfo.location(QLibraryInfo.DocumentationPath) +
+                QDir.separator() + "qch")
+        elif version == 5:
+            docsPath = QLibraryInfo.location(QLibraryInfo.DocumentationPath)
+            if not os.path.isdir(docsPath) or \
+                    len(QDir(docsPath).entryList(["*.qch"])) == 0:
+                # Qt installer is a bit buggy; it's missing a symbolic link
+                docsPathList = QDir.fromNativeSeparators(docsPath).split("/")
+                docsPath = os.sep.join(
+                    docsPathList[:-3] +
+                    ["Docs", "Qt-{0}".format(qVersion()[:3])])
+            docsPath = QDir(docsPath)
+        else:
+            # unsupported Qt version
+            return False
+        
+        files = docsPath.entryList(["*.qch"])
+        if not files:
+            engine.setCustomValue(
+                versionKey,
+                QDateTime().toString(Qt.ISODate) + '|')
+            return False
+        
+        for f in files:
+            if f.startswith(name + "."):
+                fi = QFileInfo(docsPath.absolutePath() + QDir.separator() + f)
+                namespace = QHelpEngineCore.namespaceName(
+                    fi.absoluteFilePath())
+                if not namespace:
+                    continue
+                
+                if dt.isValid() and \
+                   namespace in engine.registeredDocumentations() and \
+                   fi.lastModified().toString(Qt.ISODate) == \
+                    dt.toString(Qt.ISODate) and \
+                   qchFile == fi.absoluteFilePath():
+                    return False
+                
+                if namespace in engine.registeredDocumentations():
+                    engine.unregisterDocumentation(namespace)
+                
+                if not engine.registerDocumentation(fi.absoluteFilePath()):
+                    self.errorMessage.emit(
+                        self.tr(
+                            """<p>The file <b>{0}</b> could not be"""
+                            """ registered. <br/>Reason: {1}</p>""")
+                        .format(fi.absoluteFilePath, engine.error())
+                    )
+                    return False
+                
+                engine.setCustomValue(
+                    versionKey,
+                    fi.lastModified().toString(Qt.ISODate) + '|' +
+                    fi.absoluteFilePath())
+                return True
+        
+        return False
+    
+    def __installEric6Doc(self, engine):
+        """
+        Private method to install/update the eric6 help documentation.
+        
+        @param engine reference to the help engine (QHelpEngineCore)
+        @return flag indicating success (boolean)
+        """
+        versionKey = "eric6_ide"
+        info = engine.customValue(versionKey, "")
+        lst = info.split('|')
+        
+        dt = QDateTime()
+        if len(lst) and lst[0]:
+            dt = QDateTime.fromString(lst[0], Qt.ISODate)
+        
+        qchFile = ""
+        if len(lst) == 2:
+            qchFile = lst[1]
+        
+        docsPath = QDir(getConfig("ericDocDir") + QDir.separator() + "Help")
+        
+        files = docsPath.entryList(["*.qch"])
+        if not files:
+            engine.setCustomValue(
+                versionKey, QDateTime().toString(Qt.ISODate) + '|')
+            return False
+        
+        for f in files:
+            if f == "source.qch":
+                fi = QFileInfo(docsPath.absolutePath() + QDir.separator() + f)
+                namespace = QHelpEngineCore.namespaceName(
+                    fi.absoluteFilePath())
+                if not namespace:
+                    continue
+                
+                if dt.isValid() and \
+                   namespace in engine.registeredDocumentations() and \
+                   fi.lastModified().toString(Qt.ISODate) == \
+                    dt.toString(Qt.ISODate) and \
+                   qchFile == fi.absoluteFilePath():
+                    return False
+                
+                if namespace in engine.registeredDocumentations():
+                    engine.unregisterDocumentation(namespace)
+                
+                if not engine.registerDocumentation(fi.absoluteFilePath()):
+                    self.errorMessage.emit(
+                        self.tr(
+                            """<p>The file <b>{0}</b> could not be"""
+                            """ registered. <br/>Reason: {1}</p>""")
+                        .format(fi.absoluteFilePath, engine.error())
+                    )
+                    return False
+                
+                engine.setCustomValue(
+                    versionKey,
+                    fi.lastModified().toString(Qt.ISODate) + '|' +
+                    fi.absoluteFilePath())
+                return True
+        
+        return False
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/QtHelp/HelpIndexWidget.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,181 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a window for showing the QtHelp index.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSignal, Qt, QUrl, QEvent
+from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QLineEdit, QMenu, \
+    QDialog
+
+
+class HelpIndexWidget(QWidget):
+    """
+    Class implementing a window for showing the QtHelp index.
+    
+    @signal linkActivated(QUrl) emitted when an index entry is activated
+    @signal linksActivated(links, keyword) emitted when an index entry
+        referencing multiple targets is activated
+    @signal escapePressed() emitted when the ESC key was pressed
+    """
+    linkActivated = pyqtSignal(QUrl)
+    linksActivated = pyqtSignal(dict, str)
+    escapePressed = pyqtSignal()
+    
+    def __init__(self, engine, mainWindow, parent=None):
+        """
+        Constructor
+        
+        @param engine reference to the help engine (QHelpEngine)
+        @param mainWindow reference to the main window object (QMainWindow)
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(HelpIndexWidget, self).__init__(parent)
+        
+        self.__engine = engine
+        self.__mw = mainWindow
+        
+        self.__searchEdit = None
+        self.__index = None
+        
+        self.__layout = QVBoxLayout(self)
+        label = QLabel(self.tr("&Look for:"))
+        self.__layout.addWidget(label)
+        
+        self.__searchEdit = QLineEdit()
+        label.setBuddy(self.__searchEdit)
+        self.__searchEdit.textChanged.connect(self.__filterIndices)
+        self.__searchEdit.installEventFilter(self)
+        self.__layout.addWidget(self.__searchEdit)
+        
+        self.__index = self.__engine.indexWidget()
+        self.__index.installEventFilter(self)
+        self.__engine.indexModel().indexCreationStarted.connect(
+            self.__disableSearchEdit)
+        self.__engine.indexModel().indexCreated.connect(
+            self.__enableSearchEdit)
+        self.__index.activated.connect(self.__activated)
+        self.__searchEdit.returnPressed.connect(
+            self.__index.activateCurrentItem)
+        self.__layout.addWidget(self.__index)
+        
+        self.__index.viewport().installEventFilter(self)
+    
+    def __activated(self, idx):
+        """
+        Private slot to handle the activation of a keyword entry.
+        
+        @param idx index of the activated entry (QModelIndex)
+        """
+        model = self.__index.model()
+        if model is not None:
+            keyword = model.data(idx, Qt.DisplayRole)
+            links = model.linksForKeyword(keyword)
+            if len(links) == 1:
+                self.linkActivated.emit(QUrl(links[list(links.keys())[0]]))
+            else:
+                self.linksActivated.emit(links, keyword)
+    
+    def __filterIndices(self, filter):
+        """
+        Private slot to filter the indices according to the given filter.
+        
+        @param filter filter to be used (string)
+        """
+        if '*' in filter:
+            self.__index.filterIndices(filter, filter)
+        else:
+            self.__index.filterIndices(filter)
+    
+    def __enableSearchEdit(self):
+        """
+        Private slot to enable the search edit.
+        """
+        self.__searchEdit.setEnabled(True)
+        self.__filterIndices(self.__searchEdit.text())
+    
+    def __disableSearchEdit(self):
+        """
+        Private slot to enable the search edit.
+        """
+        self.__searchEdit.setEnabled(False)
+    
+    def focusInEvent(self, evt):
+        """
+        Protected method handling focus in events.
+        
+        @param evt reference to the focus event object (QFocusEvent)
+        """
+        if evt.reason() != Qt.MouseFocusReason:
+            self.__searchEdit.selectAll()
+            self.__searchEdit.setFocus()
+    
+    def eventFilter(self, watched, event):
+        """
+        Public method called to filter the event queue.
+        
+        @param watched the QObject being watched (QObject)
+        @param event the event that occurred (QEvent)
+        @return flag indicating whether the event was handled (boolean)
+        """
+        if self.__searchEdit and watched == self.__searchEdit and \
+           event.type() == QEvent.KeyPress:
+            idx = self.__index.currentIndex()
+            if event.key() == Qt.Key_Up:
+                idx = self.__index.model().index(
+                    idx.row() - 1, idx.column(), idx.parent())
+                if idx.isValid():
+                    self.__index.setCurrentIndex(idx)
+            elif event.key() == Qt.Key_Down:
+                idx = self.__index.model().index(
+                    idx.row() + 1, idx.column(), idx.parent())
+                if idx.isValid():
+                    self.__index.setCurrentIndex(idx)
+            elif event.key() == Qt.Key_Escape:
+                self.escapePressed.emit()
+        elif self.__index and watched == self.__index and \
+                event.type() == QEvent.ContextMenu:
+            idx = self.__index.indexAt(event.pos())
+            if idx.isValid():
+                menu = QMenu()
+                curTab = menu.addAction(self.tr("Open Link"))
+                newTab = menu.addAction(self.tr("Open Link in New Tab"))
+                menu.move(self.__index.mapToGlobal(event.pos()))
+                
+                act = menu.exec_()
+                if act == curTab:
+                    self.__activated(idx)
+                elif act == newTab:
+                    model = self.__index.model()
+                    if model is not None:
+                        keyword = model.data(idx, Qt.DisplayRole)
+                        links = model.linksForKeyword(keyword)
+                        if len(links) == 1:
+                            self.__mw.newTab(list(links.values())[0])
+                        elif len(links) > 1:
+                            from .HelpTopicDialog import HelpTopicDialog
+                            dlg = HelpTopicDialog(self, keyword, links)
+                            if dlg.exec_() == QDialog.Accepted:
+                                self.__mw.newTab(dlg.link())
+        elif self.__index and watched == self.__index.viewport() and \
+                event.type() == QEvent.MouseButtonRelease:
+            idx = self.__index.indexAt(event.pos())
+            if idx.isValid() and event.button() == Qt.MidButton:
+                model = self.__index.model()
+                if model is not None:
+                    keyword = model.data(idx, Qt.DisplayRole)
+                    links = model.linksForKeyword(keyword)
+                    if len(links) == 1:
+                        self.__mw.newTab(list(links.values())[0])
+                    elif len(links) > 1:
+                        from .HelpTopicDialog import HelpTopicDialog
+                        dlg = HelpTopicDialog(self, keyword, links)
+                        if dlg.exec_() == QDialog.Accepted:
+                            self.__mw.newTab(dlg.link())
+        
+        return QWidget.eventFilter(self, watched, event)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/QtHelp/HelpSearchWidget.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,139 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a window for showing the QtHelp index.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSignal, Qt, QEvent, QUrl
+from PyQt5.QtWidgets import QWidget, QVBoxLayout, QTextBrowser, QApplication, \
+    QMenu
+
+
+class HelpSearchWidget(QWidget):
+    """
+    Class implementing a window for showing the QtHelp index.
+    
+    @signal linkActivated(QUrl) emitted when a search result entry is activated
+    @signal escapePressed() emitted when the ESC key was pressed
+    """
+    linkActivated = pyqtSignal(QUrl)
+    escapePressed = pyqtSignal()
+    
+    def __init__(self, engine, mainWindow, parent=None):
+        """
+        Constructor
+        
+        @param engine reference to the help search engine (QHelpSearchEngine)
+        @param mainWindow reference to the main window object (QMainWindow)
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(HelpSearchWidget, self).__init__(parent)
+        
+        self.__engine = engine
+        self.__mw = mainWindow
+        
+        self.__layout = QVBoxLayout(self)
+        
+        self.__result = self.__engine.resultWidget()
+        self.__query = self.__engine.queryWidget()
+        
+        self.__layout.addWidget(self.__query)
+        self.__layout.addWidget(self.__result)
+        
+        self.setFocusProxy(self.__query)
+        
+        self.__query.search.connect(self.__search)
+        self.__result.requestShowLink.connect(self.linkActivated)
+        
+        self.__engine.searchingStarted.connect(self.__searchingStarted)
+        self.__engine.searchingFinished.connect(self.__searchingFinished)
+        
+        self.__browser = self.__result.findChildren(QTextBrowser)[0]
+        if self.__browser:
+            self.__browser.viewport().installEventFilter(self)
+    
+    def __search(self):
+        """
+        Private slot to perform a search of the database.
+        """
+        query = self.__query.query()
+        self.__engine.search(query)
+    
+    def __searchingStarted(self):
+        """
+        Private slot to handle the start of a search.
+        """
+        QApplication.setOverrideCursor(Qt.WaitCursor)
+    
+    def __searchingFinished(self, hits):
+        """
+        Private slot to handle the end of the search.
+        
+        @param hits number of hits (integer) (unused)
+        """
+        QApplication.restoreOverrideCursor()
+    
+    def eventFilter(self, watched, event):
+        """
+        Public method called to filter the event queue.
+        
+        @param watched the QObject being watched (QObject)
+        @param event the event that occurred (QEvent)
+        @return flag indicating whether the event was handled (boolean)
+        """
+        if self.__browser and watched == self.__browser.viewport() and \
+           event.type() == QEvent.MouseButtonRelease:
+            link = self.__result.linkAt(event.pos())
+            if not link.isEmpty() and link.isValid():
+                ctrl = event.modifiers() & Qt.ControlModifier
+                if (event.button() == Qt.LeftButton and ctrl) or \
+                   event.button() == Qt.MidButton:
+                    self.__mw.newTab(link)
+        
+        return QWidget.eventFilter(self, watched, event)
+    
+    def keyPressEvent(self, evt):
+        """
+        Protected method handling key press events.
+        
+        @param evt reference to the key press event (QKeyEvent)
+        """
+        if evt.key() == Qt.Key_Escape:
+            self.escapePressed.emit()
+        else:
+            evt.ignore()
+    
+    def contextMenuEvent(self, evt):
+        """
+        Protected method handling context menu events.
+        
+        @param evt reference to the context menu event (QContextMenuEvent)
+        """
+        point = evt.globalPos()
+        
+        if self.__browser:
+            point = self.__browser.mapFromGlobal(point)
+            if not self.__browser.rect().contains(point, True):
+                return
+            link = QUrl(self.__browser.anchorAt(point))
+        else:
+            point = self.__result.mapFromGlobal(point)
+            link = self.__result.linkAt(point)
+        
+        if link.isEmpty() or not link.isValid():
+            return
+        
+        menu = QMenu()
+        curTab = menu.addAction(self.tr("Open Link"))
+        newTab = menu.addAction(self.tr("Open Link in New Tab"))
+        menu.move(evt.globalPos())
+        act = menu.exec_()
+        if act == curTab:
+            self.linkActivated.emit(link)
+        elif act == newTab:
+            self.__mw.newTab(link)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/QtHelp/HelpTocWidget.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,169 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a window for showing the QtHelp TOC.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSignal, Qt, QEvent, QUrl
+from PyQt5.QtWidgets import QWidget, QVBoxLayout, QMenu
+
+
+class HelpTocWidget(QWidget):
+    """
+    Class implementing a window for showing the QtHelp TOC.
+    
+    @signal linkActivated(QUrl) emitted when a TOC entry is activated
+    @signal escapePressed() emitted when the ESC key was pressed
+    """
+    linkActivated = pyqtSignal(QUrl)
+    escapePressed = pyqtSignal()
+    
+    def __init__(self, engine, mainWindow, parent=None):
+        """
+        Constructor
+        
+        @param engine reference to the help engine (QHelpEngine)
+        @param mainWindow reference to the main window object (QMainWindow)
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(HelpTocWidget, self).__init__(parent)
+        
+        self.__engine = engine
+        self.__mw = mainWindow
+        self.__expandDepth = -2
+        
+        self.__tocWidget = self.__engine.contentWidget()
+        self.__tocWidget.viewport().installEventFilter(self)
+        self.__tocWidget.setContextMenuPolicy(Qt.CustomContextMenu)
+        self.__tocWidget.setSortingEnabled(True)
+        
+        self.__layout = QVBoxLayout(self)
+        self.__layout.addWidget(self.__tocWidget)
+        
+        self.__tocWidget.customContextMenuRequested.connect(
+            self.__showContextMenu)
+        self.__tocWidget.linkActivated.connect(self.linkActivated)
+        
+        model = self.__tocWidget.model()
+        model.contentsCreated.connect(self.__contentsCreated)
+    
+    def __contentsCreated(self):
+        """
+        Private slot to be run after the contents was generated.
+        """
+        self.__tocWidget.sortByColumn(0, Qt.AscendingOrder)
+        self.__expandTOC()
+    
+    def __expandTOC(self):
+        """
+        Private slot to expand the table of contents.
+        """
+        if self.__expandDepth > -2:
+            self.expandToDepth(self.__expandDepth)
+            self.__expandDepth = -2
+    
+    def expandToDepth(self, depth):
+        """
+        Public slot to expand the table of contents to a specific depth.
+        
+        @param depth depth to expand to (integer)
+        """
+        self.__expandDepth = depth
+        if depth == -1:
+            self.__tocWidget.expandAll()
+        else:
+            self.__tocWidget.expandToDepth(depth)
+    
+    def focusInEvent(self, evt):
+        """
+        Protected method handling focus in events.
+        
+        @param evt reference to the focus event object (QFocusEvent)
+        """
+        if evt.reason() != Qt.MouseFocusReason:
+            self.__tocWidget.setFocus()
+    
+    def keyPressEvent(self, evt):
+        """
+        Protected method handling key press events.
+        
+        @param evt reference to the key press event (QKeyEvent)
+        """
+        if evt.key() == Qt.Key_Escape:
+            self.escapePressed.emit()
+    
+    def eventFilter(self, watched, event):
+        """
+        Public method called to filter the event queue.
+        
+        @param watched the QObject being watched (QObject)
+        @param event the event that occurred (QEvent)
+        @return flag indicating whether the event was handled (boolean)
+        """
+        if self.__tocWidget and watched == self.__tocWidget.viewport() and \
+           event.type() == QEvent.MouseButtonRelease:
+            if self.__tocWidget.indexAt(event.pos()).isValid() and \
+               event.button() == Qt.LeftButton:
+                self.itemClicked(self.__tocWidget.currentIndex())
+            elif self.__tocWidget.indexAt(event.pos()).isValid() and \
+                    event.button() == Qt.MidButton:
+                model = self.__tocWidget.model()
+                itm = model.contentItemAt(self.__tocWidget.currentIndex())
+                self.__mw.newTab(itm.url())
+        
+        return QWidget.eventFilter(self, watched, event)
+    
+    def itemClicked(self, index):
+        """
+        Public slot handling a click of a TOC entry.
+        
+        @param index index of the TOC clicked (QModelIndex)
+        """
+        if not index.isValid():
+            return
+        
+        model = self.__tocWidget.model()
+        itm = model.contentItemAt(index)
+        if itm:
+            self.linkActivated.emit(itm.url())
+    
+    def syncToContent(self, url):
+        """
+        Public method to sync the TOC to the displayed page.
+        
+        @param url URL of the displayed page (QUrl)
+        @return flag indicating a successful synchronization (boolean)
+        """
+        idx = self.__tocWidget.indexOf(url)
+        if not idx.isValid():
+            return False
+        self.__tocWidget.setCurrentIndex(idx)
+        return True
+    
+    def __showContextMenu(self, pos):
+        """
+        Private slot showing the context menu.
+        
+        @param pos position to show the menu at (QPoint)
+        """
+        if not self.__tocWidget.indexAt(pos).isValid():
+            return
+        
+        menu = QMenu()
+        curTab = menu.addAction(self.tr("Open Link"))
+        newTab = menu.addAction(self.tr("Open Link in New Tab"))
+        menu.move(self.__tocWidget.mapToGlobal(pos))
+        
+        model = self.__tocWidget.model()
+        itm = model.contentItemAt(self.__tocWidget.currentIndex())
+        
+        act = menu.exec_()
+        if act == curTab:
+            self.linkActivated.emit(itm.url())
+        elif act == newTab:
+            self.__mw.newTab(itm.url())
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/QtHelp/HelpTopicDialog.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,60 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog to select a help topic to display.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtWidgets import QDialog
+from PyQt5.QtCore import QUrl
+
+from .Ui_HelpTopicDialog import Ui_HelpTopicDialog
+
+
+class HelpTopicDialog(QDialog, Ui_HelpTopicDialog):
+    """
+    Class implementing a dialog to select a help topic to display.
+    """
+    def __init__(self, parent, keyword, links):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (QWidget)
+        @param keyword keyword for the link set (string)
+        @param links dictionary with help topic as key (string) and
+            URL as value (QUrl)
+        """
+        super(HelpTopicDialog, self).__init__(parent)
+        self.setupUi(self)
+        
+        self.label.setText(self.tr("Choose a &topic for <b>{0}</b>:")
+                           .format(keyword))
+        
+        self.__links = links
+        for topic in sorted(self.__links):
+            self.topicsList.addItem(topic)
+        if self.topicsList.count() > 0:
+            self.topicsList.setCurrentRow(0)
+        self.topicsList.setFocus()
+        
+        self.topicsList.itemActivated.connect(self.accept)
+    
+    def link(self):
+        """
+        Public method to the link of the selected topic.
+        
+        @return URL of the selected topic (QUrl)
+        """
+        itm = self.topicsList.currentItem()
+        if itm is None:
+            return QUrl()
+        
+        topic = itm.text()
+        if topic == "" or topic not in self.__links:
+            return QUrl()
+        
+        return self.__links[topic]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/QtHelp/HelpTopicDialog.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,80 @@
+<ui version="4.0" >
+ <class>HelpTopicDialog</class>
+ <widget class="QDialog" name="HelpTopicDialog" >
+  <property name="geometry" >
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>500</width>
+    <height>300</height>
+   </rect>
+  </property>
+  <property name="windowTitle" >
+   <string>Select Help Topic</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout" >
+   <item>
+    <widget class="QLabel" name="label" >
+     <property name="text" >
+      <string>&amp;Topics:</string>
+     </property>
+     <property name="buddy" >
+      <cstring>topicsList</cstring>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QListWidget" name="topicsList" />
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox" >
+     <property name="orientation" >
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons" >
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <tabstops>
+  <tabstop>topicsList</tabstop>
+  <tabstop>buttonBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>HelpTopicDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel" >
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel" >
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>HelpTopicDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel" >
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel" >
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/QtHelp/QtHelpDocumentationDialog.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,155 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog to manage the QtHelp documentation database.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSlot, Qt, QItemSelectionModel
+from PyQt5.QtWidgets import QDialog
+from PyQt5.QtHelp import QHelpEngineCore
+
+from E5Gui import E5MessageBox, E5FileDialog
+
+from .Ui_QtHelpDocumentationDialog import Ui_QtHelpDocumentationDialog
+
+
+class QtHelpDocumentationDialog(QDialog, Ui_QtHelpDocumentationDialog):
+    """
+    Class implementing a dialog to manage the QtHelp documentation database.
+    """
+    def __init__(self, engine, parent):
+        """
+        Constructor
+        
+        @param engine reference to the help engine (QHelpEngine)
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(QtHelpDocumentationDialog, self).__init__(parent)
+        self.setupUi(self)
+        
+        self.removeButton.setEnabled(False)
+        
+        self.__engine = engine
+        self.__mw = parent
+        
+        docs = self.__engine.registeredDocumentations()
+        self.documentsList.addItems(docs)
+        
+        self.__registeredDocs = []
+        self.__unregisteredDocs = []
+        self.__tabsToClose = []
+    
+    @pyqtSlot()
+    def on_documentsList_itemSelectionChanged(self):
+        """
+        Private slot handling a change of the documents selection.
+        """
+        self.removeButton.setEnabled(
+            len(self.documentsList.selectedItems()) != 0)
+    
+    @pyqtSlot()
+    def on_addButton_clicked(self):
+        """
+        Private slot to add documents to the help database.
+        """
+        fileNames = E5FileDialog.getOpenFileNames(
+            self,
+            self.tr("Add Documentation"),
+            "",
+            self.tr("Qt Compressed Help Files (*.qch)"))
+        if not fileNames:
+            return
+        
+        for fileName in fileNames:
+            ns = QHelpEngineCore.namespaceName(fileName)
+            if not ns:
+                E5MessageBox.warning(
+                    self,
+                    self.tr("Add Documentation"),
+                    self.tr(
+                        """The file <b>{0}</b> is not a valid"""
+                        """ Qt Help File.""").format(fileName)
+                )
+                continue
+            
+            if len(self.documentsList.findItems(ns, Qt.MatchFixedString)):
+                E5MessageBox.warning(
+                    self,
+                    self.tr("Add Documentation"),
+                    self.tr(
+                        """The namespace <b>{0}</b> is already registered.""")
+                    .format(ns)
+                )
+                continue
+            
+            self.__engine.registerDocumentation(fileName)
+            self.documentsList.addItem(ns)
+            self.__registeredDocs.append(ns)
+            if ns in self.__unregisteredDocs:
+                self.__unregisteredDocs.remove(ns)
+
+    @pyqtSlot()
+    def on_removeButton_clicked(self):
+        """
+        Private slot to remove a document from the help database.
+        """
+        res = E5MessageBox.yesNo(
+            self,
+            self.tr("Remove Documentation"),
+            self.tr(
+                """Do you really want to remove the selected documentation """
+                """sets from the database?"""))
+        if not res:
+            return
+        
+        openedDocs = self.__mw.getSourceFileList()
+        
+        items = self.documentsList.selectedItems()
+        for item in items:
+            ns = item.text()
+            if ns in list(openedDocs.values()):
+                res = E5MessageBox.yesNo(
+                    self,
+                    self.tr("Remove Documentation"),
+                    self.tr(
+                        """Some documents currently opened reference the """
+                        """documentation you are attempting to remove. """
+                        """Removing the documentation will close those """
+                        """documents. Remove anyway?"""),
+                    icon=E5MessageBox.Warning)
+                if not res:
+                    return
+            self.__unregisteredDocs.append(ns)
+            for id in openedDocs:
+                if openedDocs[id] == ns and id not in self.__tabsToClose:
+                    self.__tabsToClose.append(id)
+            itm = self.documentsList.takeItem(self.documentsList.row(item))
+            del itm
+            
+            self.__engine.unregisterDocumentation(ns)
+        
+        if self.documentsList.count():
+            self.documentsList.setCurrentRow(
+                0, QItemSelectionModel.ClearAndSelect)
+    
+    def hasChanges(self):
+        """
+        Public slot to test the dialog for changes.
+        
+        @return flag indicating presence of changes
+        """
+        return len(self.__registeredDocs) > 0 or \
+            len(self.__unregisteredDocs) > 0
+    
+    def getTabsToClose(self):
+        """
+        Public method to get the list of tabs to close.
+        
+        @return list of tab ids to be closed (list of integers)
+        """
+        return self.__tabsToClose
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/QtHelp/QtHelpDocumentationDialog.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,130 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>QtHelpDocumentationDialog</class>
+ <widget class="QDialog" name="QtHelpDocumentationDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>425</width>
+    <height>391</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Manage QtHelp Documentation Database</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QLabel" name="label">
+     <property name="text">
+      <string>Registered Documents</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <layout class="QGridLayout" name="gridLayout">
+     <item row="0" column="0" rowspan="3">
+      <widget class="QListWidget" name="documentsList">
+       <property name="alternatingRowColors">
+        <bool>true</bool>
+       </property>
+       <property name="selectionMode">
+        <enum>QAbstractItemView::ExtendedSelection</enum>
+       </property>
+       <property name="sortingEnabled">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="1">
+      <widget class="QPushButton" name="addButton">
+       <property name="toolTip">
+        <string>Press to select QtHelp documents to add to the database</string>
+       </property>
+       <property name="text">
+        <string>Add...</string>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="1">
+      <widget class="QPushButton" name="removeButton">
+       <property name="toolTip">
+        <string>Press to remove the selected documents from the database</string>
+       </property>
+       <property name="text">
+        <string>Remove</string>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="1">
+      <spacer name="verticalSpacer">
+       <property name="orientation">
+        <enum>Qt::Vertical</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>20</width>
+         <height>98</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Close</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <tabstops>
+  <tabstop>documentsList</tabstop>
+  <tabstop>addButton</tabstop>
+  <tabstop>removeButton</tabstop>
+  <tabstop>buttonBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>QtHelpDocumentationDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>QtHelpDocumentationDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/QtHelp/QtHelpFiltersDialog.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,273 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog to manage the QtHelp filters.
+"""
+
+from __future__ import unicode_literals
+
+import sqlite3
+
+from PyQt5.QtCore import pyqtSlot, Qt, QItemSelectionModel
+from PyQt5.QtWidgets import QDialog, QTreeWidgetItem, QListWidgetItem, \
+    QInputDialog, QLineEdit
+from PyQt5.QtHelp import QHelpEngineCore
+
+from E5Gui import E5MessageBox
+
+from .Ui_QtHelpFiltersDialog import Ui_QtHelpFiltersDialog
+
+
+class QtHelpFiltersDialog(QDialog, Ui_QtHelpFiltersDialog):
+    """
+    Class implementing a dialog to manage the QtHelp filters.
+    """
+    def __init__(self, engine, parent=None):
+        """
+        Constructor
+        
+        @param engine reference to the help engine (QHelpEngine)
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(QtHelpFiltersDialog, self).__init__(parent)
+        self.setupUi(self)
+        
+        self.removeButton.setEnabled(False)
+        self.removeAttributeButton.setEnabled(False)
+        
+        self.__engine = engine
+        
+        self.filtersList.clear()
+        self.attributesList.clear()
+        
+        help = QHelpEngineCore(self.__engine.collectionFile())
+        help.setupData()
+        
+        self.__removedFilters = []
+        self.__filterMap = {}
+        self.__filterMapBackup = {}
+        self.__removedAttributes = []
+        
+        for filter in help.customFilters():
+            atts = help.filterAttributes(filter)
+            self.__filterMapBackup[filter] = atts
+            if filter not in self.__filterMap:
+                self.__filterMap[filter] = atts
+        
+        self.filtersList.addItems(sorted(self.__filterMap.keys()))
+        for attr in help.filterAttributes():
+            QTreeWidgetItem(self.attributesList, [attr])
+        self.attributesList.sortItems(0, Qt.AscendingOrder)
+        
+        if self.__filterMap:
+            self.filtersList.setCurrentRow(0)
+    
+    @pyqtSlot(QListWidgetItem, QListWidgetItem)
+    def on_filtersList_currentItemChanged(self, current, previous):
+        """
+        Private slot to update the attributes depending on the current filter.
+        
+        @param current reference to the current item (QListWidgetitem)
+        @param previous reference to the previous current item
+            (QListWidgetItem)
+        """
+        checkedList = []
+        if current is not None:
+            checkedList = self.__filterMap[current.text()]
+        for index in range(0, self.attributesList.topLevelItemCount()):
+            itm = self.attributesList.topLevelItem(index)
+            if itm.text(0) in checkedList:
+                itm.setCheckState(0, Qt.Checked)
+            else:
+                itm.setCheckState(0, Qt.Unchecked)
+    
+    @pyqtSlot()
+    def on_filtersList_itemSelectionChanged(self):
+        """
+        Private slot handling a change of selected filters.
+        """
+        self.removeButton.setEnabled(
+            len(self.filtersList.selectedItems()) > 0)
+    
+    @pyqtSlot(QTreeWidgetItem, int)
+    def on_attributesList_itemChanged(self, item, column):
+        """
+        Private slot to handle a change of an attribute.
+        
+        @param item reference to the changed item (QTreeWidgetItem)
+        @param column column containing the change (integer)
+        """
+        if self.filtersList.currentItem() is None:
+            return
+        
+        filter = self.filtersList.currentItem().text()
+        if filter not in self.__filterMap:
+            return
+        
+        newAtts = []
+        for index in range(0, self.attributesList.topLevelItemCount()):
+            itm = self.attributesList.topLevelItem(index)
+            if itm.checkState(0) == Qt.Checked:
+                newAtts.append(itm.text(0))
+        self.__filterMap[filter] = newAtts
+    
+    @pyqtSlot()
+    def on_attributesList_itemSelectionChanged(self):
+        """
+        Private slot handling the selection of attributes.
+        """
+        self.removeAttributeButton.setEnabled(
+            len(self.attributesList.selectedItems()) != 0)
+    
+    @pyqtSlot()
+    def on_addButton_clicked(self):
+        """
+        Private slot to add a new filter.
+        """
+        filter, ok = QInputDialog.getText(
+            None,
+            self.tr("Add Filter"),
+            self.tr("Filter name:"),
+            QLineEdit.Normal)
+        if not filter:
+            return
+        
+        if filter not in self.__filterMap:
+            self.__filterMap[filter] = []
+            self.filtersList.addItem(filter)
+        
+        itm = self.filtersList.findItems(filter, Qt.MatchCaseSensitive)[0]
+        self.filtersList.setCurrentItem(itm)
+    
+    @pyqtSlot()
+    def on_removeButton_clicked(self):
+        """
+        Private slot to remove the selected filters.
+        """
+        ok = E5MessageBox.yesNo(
+            self,
+            self.tr("Remove Filters"),
+            self.tr(
+                """Do you really want to remove the selected filters """
+                """from the database?"""))
+        if not ok:
+            return
+        
+        items = self.filtersList.selectedItems()
+        for item in items:
+            itm = self.filtersList.takeItem(self.filtersList.row(item))
+            if itm is None:
+                continue
+            
+            del self.__filterMap[itm.text()]
+            self.__removedFilters.append(itm.text())
+            del itm
+        
+        if self.filtersList.count():
+            self.filtersList.setCurrentRow(
+                0, QItemSelectionModel.ClearAndSelect)
+    
+    @pyqtSlot()
+    def on_removeAttributeButton_clicked(self):
+        """
+        Private slot to remove the selected filter attributes.
+        """
+        ok = E5MessageBox.yesNo(
+            self,
+            self.tr("Remove Attributes"),
+            self.tr(
+                """Do you really want to remove the selected attributes """
+                """from the database?"""))
+        if not ok:
+            return
+        
+        items = self.attributesList.selectedItems()
+        for item in items:
+            itm = self.attributesList.takeTopLevelItem(
+                self.attributesList.indexOfTopLevelItem(item))
+            if itm is None:
+                continue
+            
+            attr = itm.text(0)
+            self.__removedAttributes.append(attr)
+            for filter in self.__filterMap:
+                if attr in self.__filterMap[filter]:
+                    self.__filterMap[filter].remove(attr)
+            
+            del itm
+    
+    @pyqtSlot()
+    def on_unusedAttributesButton_clicked(self):
+        """
+        Private slot to select all unused attributes.
+        """
+        # step 1: determine all used attributes
+        attributes = set()
+        for filter in self.__filterMap:
+            attributes |= set(self.__filterMap[filter])
+        
+        # step 2: select all unused attribute items
+        self.attributesList.clearSelection()
+        for row in range(self.attributesList.topLevelItemCount()):
+            itm = self.attributesList.topLevelItem(row)
+            if itm.text(0) not in attributes:
+                itm.setSelected(True)
+    
+    def __removeAttributes(self):
+        """
+        Private method to remove attributes from the Qt Help database.
+        """
+        try:
+            self.__db = sqlite3.connect(self.__engine.collectionFile())
+        except sqlite3.DatabaseError:
+            pass        # ignore database errors
+        
+        for attr in self.__removedAttributes:
+            self.__db.execute(
+                "DELETE FROM FilterAttributeTable WHERE Name = '{0}'"
+                .format(attr))
+        self.__db.commit()
+        self.__db.close()
+    
+    @pyqtSlot()
+    def on_buttonBox_accepted(self):
+        """
+        Private slot to update the database, if the dialog is accepted.
+        """
+        filtersChanged = False
+        if len(self.__filterMapBackup) != len(self.__filterMap):
+            filtersChanged = True
+        else:
+            for filter in self.__filterMapBackup:
+                if filter not in self.__filterMap:
+                    filtersChanged = True
+                else:
+                    oldFilterAtts = self.__filterMapBackup[filter]
+                    newFilterAtts = self.__filterMap[filter]
+                    if len(oldFilterAtts) != len(newFilterAtts):
+                        filtersChanged = True
+                    else:
+                        for attr in oldFilterAtts:
+                            if attr not in newFilterAtts:
+                                filtersChanged = True
+                                break
+                
+                if filtersChanged:
+                    break
+        
+        if filtersChanged:
+            for filter in self.__removedFilters:
+                self.__engine.removeCustomFilter(filter)
+            for filter in self.__filterMap:
+                self.__engine.addCustomFilter(filter, self.__filterMap[filter])
+        
+        if self.__removedAttributes:
+            self.__removeAttributes()
+        
+        if filtersChanged or self.__removedAttributes:
+            self.__engine.setupData()
+        
+        self.accept()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/QtHelp/QtHelpFiltersDialog.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,155 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>QtHelpFiltersDialog</class>
+ <widget class="QDialog" name="QtHelpFiltersDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>570</width>
+    <height>391</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Manage QtHelp Filters</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QGridLayout" name="gridLayout">
+     <item row="0" column="0" colspan="2">
+      <widget class="QLabel" name="label">
+       <property name="text">
+        <string>Filters:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="2" colspan="2">
+      <widget class="QLabel" name="label_2">
+       <property name="text">
+        <string>Attributes:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="0" colspan="2">
+      <widget class="QListWidget" name="filtersList">
+       <property name="alternatingRowColors">
+        <bool>true</bool>
+       </property>
+       <property name="selectionMode">
+        <enum>QAbstractItemView::ExtendedSelection</enum>
+       </property>
+       <property name="sortingEnabled">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="2" colspan="2">
+      <widget class="QTreeWidget" name="attributesList">
+       <property name="alternatingRowColors">
+        <bool>true</bool>
+       </property>
+       <property name="selectionMode">
+        <enum>QAbstractItemView::ExtendedSelection</enum>
+       </property>
+       <property name="rootIsDecorated">
+        <bool>false</bool>
+       </property>
+       <property name="sortingEnabled">
+        <bool>true</bool>
+       </property>
+       <property name="headerHidden">
+        <bool>true</bool>
+       </property>
+       <column>
+        <property name="text">
+         <string>1</string>
+        </property>
+       </column>
+      </widget>
+     </item>
+     <item row="2" column="0">
+      <widget class="QPushButton" name="addButton">
+       <property name="toolTip">
+        <string>Press to add a new filter</string>
+       </property>
+       <property name="text">
+        <string>Add Filter ...</string>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="1">
+      <widget class="QPushButton" name="removeButton">
+       <property name="toolTip">
+        <string>Press to remove the selected filters</string>
+       </property>
+       <property name="text">
+        <string>Remove Filters</string>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="2">
+      <widget class="QPushButton" name="removeAttributeButton">
+       <property name="toolTip">
+        <string>Press to remove the selected attributes</string>
+       </property>
+       <property name="text">
+        <string>Remove Attributes</string>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="3">
+      <widget class="QPushButton" name="unusedAttributesButton">
+       <property name="statusTip">
+        <string>Press to select all unused attributes</string>
+       </property>
+       <property name="text">
+        <string>Select Unused</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <tabstops>
+  <tabstop>filtersList</tabstop>
+  <tabstop>addButton</tabstop>
+  <tabstop>removeButton</tabstop>
+  <tabstop>attributesList</tabstop>
+  <tabstop>removeAttributeButton</tabstop>
+  <tabstop>unusedAttributesButton</tabstop>
+  <tabstop>buttonBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>QtHelpFiltersDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>320</x>
+     <y>386</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/QtHelp/__init__.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Package containing the interface to QtHelp.
+"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/SearchWidget.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,224 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the search bar for the web browser.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSlot, Qt
+from PyQt5.QtGui import QPalette, QBrush, QColor
+from PyQt5.QtWidgets import QWidget
+
+from .Ui_SearchWidget import Ui_SearchWidget
+
+import UI.PixmapCache
+
+
+class SearchWidget(QWidget, Ui_SearchWidget):
+    """
+    Class implementing the search bar for the web browser.
+    """
+    def __init__(self, mainWindow, parent=None):
+        """
+        Constructor
+        
+        @param mainWindow reference to the main window (QMainWindow)
+        @param parent parent widget of this dialog (QWidget)
+        """
+        super(SearchWidget, self).__init__(parent)
+        self.setupUi(self)
+        
+        self.__mainWindow = mainWindow
+        
+        self.closeButton.setIcon(UI.PixmapCache.getIcon("close.png"))
+        self.findPrevButton.setIcon(UI.PixmapCache.getIcon("1leftarrow.png"))
+        self.findNextButton.setIcon(UI.PixmapCache.getIcon("1rightarrow.png"))
+        
+        self.__defaultBaseColor = \
+            self.findtextCombo.lineEdit().palette().color(QPalette.Base)
+        self.__defaultTextColor = \
+            self.findtextCombo.lineEdit().palette().color(QPalette.Text)
+        
+        self.__findHistory = []
+        self.__havefound = False
+        self.__findBackwards = False
+        
+        self.findtextCombo.setCompleter(None)
+        self.findtextCombo.lineEdit().returnPressed.connect(
+            self.__findByReturnPressed)
+        self.findtextCombo.lineEdit().textEdited.connect(
+            self.__searchTextEdited)
+
+    def on_findtextCombo_editTextChanged(self, txt):
+        """
+        Private slot to enable/disable the find buttons.
+        
+        @param txt text of the combobox (string)
+        """
+        self.findPrevButton.setEnabled(txt != "")
+        self.findNextButton.setEnabled(txt != "")
+    
+    def __searchTextEdited(self, txt):
+        """
+        Private slot to perform an incremental search.
+        
+        @param txt current text of the search combos line edit (string)
+            (unused)
+        """
+        self.__findNextPrev()
+    
+    def __findNextPrev(self):
+        """
+        Private slot to find the next occurrence of text.
+        """
+        self.infoLabel.clear()
+        self.__setFindtextComboBackground(False)
+        
+        if not self.findtextCombo.currentText():
+            return
+        
+        self.__mainWindow.currentBrowser().findNextPrev(
+            self.findtextCombo.currentText(),
+            self.caseCheckBox.isChecked(),
+            self.__findBackwards,
+            self.__findNextPrevCallback)
+    
+    def __findNextPrevCallback(self, found):
+        """
+        Private method to process the result of the last search.
+        
+        @param found flag indicating if the last search succeeded
+        @type bool
+        """
+        if not found:
+            self.infoLabel.setText(self.tr("Expression was not found."))
+            self.__setFindtextComboBackground(True)
+    
+    @pyqtSlot()
+    def on_findNextButton_clicked(self):
+        """
+        Private slot to find the next occurrence.
+        """
+        txt = self.findtextCombo.currentText()
+        
+        # This moves any previous occurrence of this statement to the head
+        # of the list and updates the combobox
+        if txt in self.__findHistory:
+            self.__findHistory.remove(txt)
+        self.__findHistory.insert(0, txt)
+        self.findtextCombo.clear()
+        self.findtextCombo.addItems(self.__findHistory)
+        
+        self.__findBackwards = False
+        self.__findNextPrev()
+    
+    def findNext(self):
+        """
+        Public slot to find the next occurrence.
+        """
+        if not self.__havefound or not self.findtextCombo.currentText():
+            self.showFind()
+            return
+        
+        self.on_findNextButton_clicked()
+
+    @pyqtSlot()
+    def on_findPrevButton_clicked(self):
+        """
+        Private slot to find the previous occurrence.
+        """
+        txt = self.findtextCombo.currentText()
+        
+        # This moves any previous occurrence of this statement to the head
+        # of the list and updates the combobox
+        if txt in self.__findHistory:
+            self.__findHistory.remove(txt)
+        self.__findHistory.insert(0, txt)
+        self.findtextCombo.clear()
+        self.findtextCombo.addItems(self.__findHistory)
+        
+        self.__findBackwards = True
+        self.__findNextPrev()
+    
+    def findPrevious(self):
+        """
+        Public slot to find the previous occurrence.
+        """
+        if not self.__havefound or not self.findtextCombo.currentText():
+            self.showFind()
+            return
+        
+        self.on_findPrevButton_clicked()
+    
+    def __findByReturnPressed(self):
+        """
+        Private slot to handle the returnPressed signal of the findtext
+        combobox.
+        """
+        if self.__findBackwards:
+            self.on_findPrevButton_clicked()
+        else:
+            self.on_findNextButton_clicked()
+
+    def showFind(self):
+        """
+        Public method to display this dialog.
+        """
+        self.__havefound = True
+        self.__findBackwards = False
+        
+        self.findtextCombo.clear()
+        self.findtextCombo.addItems(self.__findHistory)
+        self.findtextCombo.setEditText('')
+        self.findtextCombo.setFocus()
+        
+        self.caseCheckBox.setChecked(False)
+        
+        if self.__mainWindow.currentBrowser().hasSelection():
+            self.findtextCombo.setEditText(
+                self.__mainWindow.currentBrowser().selectedText())
+        
+        self.__setFindtextComboBackground(False)
+        self.show()
+
+    @pyqtSlot()
+    def on_closeButton_clicked(self):
+        """
+        Private slot to close the widget.
+        """
+        self.close()
+    
+    def keyPressEvent(self, event):
+        """
+        Protected slot to handle key press events.
+        
+        @param event reference to the key press event (QKeyEvent)
+        """
+        if event.key() == Qt.Key_Escape:
+            cb = self.__mainWindow.currentBrowser()
+            if cb:
+                cb.setFocus(Qt.ActiveWindowFocusReason)
+            event.accept()
+            self.close()
+    
+    def __setFindtextComboBackground(self, error):
+        """
+        Private slot to change the findtext combo background to indicate
+        errors.
+        
+        @param error flag indicating an error condition (boolean)
+        """
+        le = self.findtextCombo.lineEdit()
+        p = le.palette()
+        if error:
+            p.setBrush(QPalette.Base, QBrush(QColor("#FF6666")))
+            p.setBrush(QPalette.Text, QBrush(QColor("#000000")))
+        else:
+            p.setBrush(QPalette.Base, self.__defaultBaseColor)
+            p.setBrush(QPalette.Text, self.__defaultTextColor)
+        le.setPalette(p)
+        le.update()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/SearchWidget.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>SearchWidget</class>
+ <widget class="QWidget" name="SearchWidget">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>747</width>
+    <height>26</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Find</string>
+  </property>
+  <layout class="QHBoxLayout" name="horizontalLayout">
+   <property name="leftMargin">
+    <number>0</number>
+   </property>
+   <property name="topMargin">
+    <number>0</number>
+   </property>
+   <property name="rightMargin">
+    <number>0</number>
+   </property>
+   <property name="bottomMargin">
+    <number>0</number>
+   </property>
+   <item>
+    <widget class="QToolButton" name="closeButton">
+     <property name="toolTip">
+      <string>Press to close the window</string>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QLabel" name="label">
+     <property name="text">
+      <string>Find:</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QComboBox" name="findtextCombo">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="editable">
+      <bool>true</bool>
+     </property>
+     <property name="insertPolicy">
+      <enum>QComboBox::InsertAtTop</enum>
+     </property>
+     <property name="duplicatesEnabled">
+      <bool>false</bool>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QToolButton" name="findPrevButton">
+     <property name="toolTip">
+      <string>Press to find the previous occurrence</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QToolButton" name="findNextButton">
+     <property name="toolTip">
+      <string>Press to find the next occurrence</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QCheckBox" name="caseCheckBox">
+     <property name="text">
+      <string>Match case</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="Line" name="infoLine">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QLabel" name="infoLabel">
+     <property name="minimumSize">
+      <size>
+       <width>200</width>
+       <height>0</height>
+      </size>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <tabstops>
+  <tabstop>findtextCombo</tabstop>
+  <tabstop>caseCheckBox</tabstop>
+  <tabstop>findNextButton</tabstop>
+  <tabstop>findPrevButton</tabstop>
+  <tabstop>closeButton</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/SiteInfo/SiteInfoDialog.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,286 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2011 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog to show some information about a site.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSlot, QUrl, Qt
+from PyQt5.QtGui import QPixmap, QImage
+from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply
+from PyQt5.QtWidgets import QDialog, QTreeWidgetItem, QGraphicsScene, QMenu, \
+    QApplication, QGraphicsPixmapItem
+
+from E5Gui import E5MessageBox, E5FileDialog
+
+from .Ui_SiteInfoDialog import Ui_SiteInfoDialog
+
+from ..Tools import Scripts, WebBrowserTools
+
+import UI.PixmapCache
+
+
+class SiteInfoDialog(QDialog, Ui_SiteInfoDialog):
+    """
+    Class implementing a dialog to show some information about a site.
+    """
+    okStyle = "QLabel { color : white; background-color : green; }"
+    nokStyle = "QLabel { color : white; background-color : red; }"
+    
+    def __init__(self, browser, parent=None):
+        """
+        Constructor
+        
+        @param browser reference to the browser window (HelpBrowser)
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(SiteInfoDialog, self).__init__(parent)
+        self.setupUi(self)
+        self.setWindowFlags(Qt.Window)
+        
+        # put icons
+        self.tabWidget.setTabIcon(
+            0, UI.PixmapCache.getIcon("siteinfo-general.png"))
+        self.tabWidget.setTabIcon(
+            1, UI.PixmapCache.getIcon("siteinfo-media.png"))
+        
+        self.__imageReply = None
+        
+        self.__baseUrl = browser.url()
+        title = browser.title()
+        
+        # populate General tab
+        self.heading.setText("<b>{0}</b>".format(title))
+        self.siteAddressLabel.setText(self.__baseUrl.toString())
+        if self.__baseUrl.scheme() in ["https"]:
+            self.securityLabel.setStyleSheet(SiteInfoDialog.okStyle)
+            self.securityLabel.setText('<b>Connection is encrypted.</b>')
+        else:
+            self.securityLabel.setStyleSheet(SiteInfoDialog.nokStyle)
+            self.securityLabel.setText('<b>Connection is not encrypted.</b>')
+        browser.page().runJavaScript(
+            "document.charset",
+            lambda res: self.encodingLabel.setText(res))
+        
+        # populate Meta tags
+        browser.page().runJavaScript(Scripts.getAllMetaAttributes(),
+                                     self.__processMetaAttributes)
+        
+        # populate Media tab
+        browser.page().runJavaScript(Scripts.getAllImages(), 
+                                     self.__processImageTags)
+        
+    def __processImageTags(self, res):
+        """
+        Private method to process the image tags.
+        
+        @param res result of the JavaScript script
+        @type list of dict
+        """
+        for img in res:
+            src = img["src"]
+            alt = img["alt"]
+            if not alt:
+                if src.find("/") == -1:
+                    alt = src
+                else:
+                    pos = src.rfind("/")
+                    alt = src[pos + 1:]
+            
+            if not src or not alt:
+                continue
+            
+            QTreeWidgetItem(self.imagesTree, [alt, src])
+        
+        for col in range(self.imagesTree.columnCount()):
+            self.imagesTree.resizeColumnToContents(col)
+        if self.imagesTree.columnWidth(0) > 300:
+            self.imagesTree.setColumnWidth(0, 300)
+        self.imagesTree.setCurrentItem(self.imagesTree.topLevelItem(0))
+        self.imagesTree.setContextMenuPolicy(Qt.CustomContextMenu)
+        self.imagesTree.customContextMenuRequested.connect(
+            self.__imagesTreeContextMenuRequested)
+    
+    def __processMetaAttributes(self, res):
+        """
+        Private method to process the meta attributes.
+        
+        @param res result of the JavaScript script
+        @type list of dict
+        """
+        for meta in res:
+            content = meta["content"]
+            name = meta["name"]
+            if not name:
+                name = meta["httpequiv"]
+            
+            if not name or not content:
+                continue
+            
+            if meta["charset"]:
+                self.encodingLabel.setText(meta["charset"])
+            if "charset=" in content:
+                self.encodingLabel.setText(
+                    content[content.index("charset=") + 8:])
+            
+            QTreeWidgetItem(self.tagsTree, [name, content])
+        for col in range(self.tagsTree.columnCount()):
+            self.tagsTree.resizeColumnToContents(col)
+    
+    @pyqtSlot(QTreeWidgetItem, QTreeWidgetItem)
+    def on_imagesTree_currentItemChanged(self, current, previous):
+        """
+        Private slot to show a preview of the selected image.
+        
+        @param current current image entry (QTreeWidgetItem)
+        @param previous old current entry (QTreeWidgetItem)
+        """
+        if current is None:
+            return
+        
+        imageUrl = QUrl(current.text(1))
+        if imageUrl.isRelative():
+            imageUrl = self.__baseUrl.resolved(imageUrl)
+        
+        pixmap = QPixmap()
+        loading = False
+        
+        if imageUrl.scheme() == "data":
+            encodedUrl = current.text(1).encode("utf-8")
+            imageData = encodedUrl[encodedUrl.find(",") + 1:]
+            pixmap = WebBrowserTools.pixmapFromByteArray(imageData)
+        elif imageUrl.scheme() == "file":
+            pixmap = QPixmap(imageUrl.toLocalFile())
+        elif imageUrl.scheme() == "qrc":
+            pixmap = QPixmap(imageUrl.toString()[3:])
+        else:
+            if self.__imageReply is not None:
+                self.__imageReply.deleteLater()
+                self.__imageReply = None
+            
+            from WebBrowser.WebBrowserWindow import WebBrowserWindow
+            self.__imageReply = WebBrowserWindow.networkManager().get(
+                QNetworkRequest(imageUrl))
+            self.__imageReply.finished.connect(self.__imageReplyFinished)
+            loading = True
+            self.__showLoadingText()
+        
+        if not loading:
+            self.__showPixmap(pixmap)
+    
+    @pyqtSlot()
+    def __imageReplyFinished(self):
+        """
+        Private slot handling the loading of an image.
+        """
+        if self.__imageReply.error() != QNetworkReply.NoError:
+            return
+        
+        data = self.__imageReply.readAll()
+        self.__showPixmap(QPixmap.fromImage(QImage.fromData(data)))
+    
+    def __showPixmap(self, pixmap):
+        """
+        Private method to show a pixmap in the preview pane.
+        
+        @param pixmap pixmap to be shown
+        @type QPixmap
+        """
+        scene = QGraphicsScene(self.imagePreview)
+        if pixmap.isNull():
+            scene.addText(self.tr("Preview not available."))
+        else:
+            scene.addPixmap(pixmap)
+        self.imagePreview.setScene(scene)
+    
+    def __showLoadingText(self):
+        """
+        Private method to show some text while loading an image.
+        """
+        scene = QGraphicsScene(self.imagePreview)
+        scene.addText(self.tr("Loading..."))
+        self.imagePreview.setScene(scene)
+    
+    def __imagesTreeContextMenuRequested(self, pos):
+        """
+        Private slot to show a context menu for the images list.
+        
+        @param pos position for the menu (QPoint)
+        """
+        itm = self.imagesTree.itemAt(pos)
+        if itm is None:
+            return
+        
+        menu = QMenu()
+        menu.addAction(
+            self.tr("Copy Image Location to Clipboard"),
+            self.__copyAction).setData(itm.text(1))
+        menu.addAction(
+            self.tr("Copy Image Name to Clipboard"),
+            self.__copyAction).setData(itm.text(0))
+        menu.addSeparator()
+        menu.addAction(
+            self.tr("Save Image"),
+            self.__saveImage).setData(self.imagesTree.indexOfTopLevelItem(itm))
+        menu.exec_(self.imagesTree.viewport().mapToGlobal(pos))
+    
+    def __copyAction(self):
+        """
+        Private slot to copy the image URL or the image name to the clipboard.
+        """
+        act = self.sender()
+        QApplication.clipboard().setText(act.data())
+    
+    def __saveImage(self):
+        """
+        Private slot to save the selected image to disk.
+        """
+        act = self.sender()
+        index = act.data()
+        itm = self.imagesTree.topLevelItem(index)
+        if itm is None:
+            return
+        
+        if not self.imagePreview.scene() or \
+                len(self.imagePreview.scene().items()) == 0:
+            return
+        
+        pixmapItem = self.imagePreview.scene().items()[0]
+        if not isinstance(pixmapItem, QGraphicsPixmapItem):
+            return
+        
+        if pixmapItem.pixmap().isNull():
+            E5MessageBox.warning(
+                self,
+                self.tr("Save Image"),
+                self.tr(
+                    """<p>This preview is not available.</p>"""))
+            return
+        
+        imageFileName = WebBrowserTools.getFileNameFromUrl(QUrl(itm.text(1)))
+        index = imageFileName.rfind(".")
+        if index != -1:
+            imageFileName = imageFileName[:index] + ".png"
+        
+        filename = E5FileDialog.getSaveFileName(
+            self,
+            self.tr("Save Image"),
+            imageFileName,
+            self.tr("All Files (*)"),
+            E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite))
+        
+        if not filename:
+            return
+        
+        if not pixmapItem.pixmap().save(filename, "PNG"):
+            E5MessageBox.critical(
+                self,
+                self.tr("Save Image"),
+                self.tr(
+                    """<p>Cannot write to file <b>{0}</b>.</p>""")
+                .format(filename))
+            return
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/SiteInfo/SiteInfoDialog.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,262 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>SiteInfoDialog</class>
+ <widget class="QDialog" name="SiteInfoDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>700</width>
+    <height>550</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Site Information</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QLabel" name="heading">
+     <property name="text">
+      <string notr="true"/>
+     </property>
+     <property name="wordWrap">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QTabWidget" name="tabWidget">
+     <property name="currentIndex">
+      <number>0</number>
+     </property>
+     <widget class="QWidget" name="generalTab">
+      <attribute name="title">
+       <string>General</string>
+      </attribute>
+      <layout class="QVBoxLayout" name="verticalLayout_2">
+       <item>
+        <layout class="QGridLayout" name="gridLayout">
+         <item row="0" column="0">
+          <widget class="QLabel" name="label">
+           <property name="text">
+            <string>Site Address:</string>
+           </property>
+          </widget>
+         </item>
+         <item row="0" column="1">
+          <widget class="QLabel" name="siteAddressLabel">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+          </widget>
+         </item>
+         <item row="1" column="0">
+          <widget class="QLabel" name="label_2">
+           <property name="text">
+            <string>Encoding:</string>
+           </property>
+          </widget>
+         </item>
+         <item row="1" column="1">
+          <widget class="QLabel" name="encodingLabel">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <widget class="QLabel" name="label_9">
+         <property name="text">
+          <string>Meta tags of site:</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QTreeWidget" name="tagsTree">
+         <property name="alternatingRowColors">
+          <bool>true</bool>
+         </property>
+         <property name="rootIsDecorated">
+          <bool>false</bool>
+         </property>
+         <property name="itemsExpandable">
+          <bool>false</bool>
+         </property>
+         <property name="wordWrap">
+          <bool>false</bool>
+         </property>
+         <column>
+          <property name="text">
+           <string>Tag</string>
+          </property>
+         </column>
+         <column>
+          <property name="text">
+           <string>Value</string>
+          </property>
+         </column>
+        </widget>
+       </item>
+       <item>
+        <layout class="QGridLayout" name="gridLayout_2">
+         <item row="0" column="0" colspan="4">
+          <widget class="QLabel" name="label_4">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="text">
+            <string>&lt;b&gt;Security information&lt;/b&gt;</string>
+           </property>
+          </widget>
+         </item>
+         <item row="1" column="0">
+          <spacer name="horizontalSpacer">
+           <property name="orientation">
+            <enum>Qt::Horizontal</enum>
+           </property>
+           <property name="sizeType">
+            <enum>QSizePolicy::Fixed</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>20</width>
+             <height>20</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
+         <item row="1" column="1">
+          <widget class="QLabel" name="securityLabel">
+           <property name="text">
+            <string/>
+           </property>
+          </widget>
+         </item>
+         <item row="1" column="2">
+          <spacer name="horizontalSpacer_2">
+           <property name="orientation">
+            <enum>Qt::Horizontal</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>40</width>
+             <height>20</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
+        </layout>
+       </item>
+      </layout>
+     </widget>
+     <widget class="QWidget" name="mediaTab">
+      <attribute name="title">
+       <string>Media</string>
+      </attribute>
+      <layout class="QVBoxLayout" name="verticalLayout_4">
+       <item>
+        <widget class="QTreeWidget" name="imagesTree">
+         <property name="alternatingRowColors">
+          <bool>true</bool>
+         </property>
+         <property name="rootIsDecorated">
+          <bool>false</bool>
+         </property>
+         <property name="itemsExpandable">
+          <bool>false</bool>
+         </property>
+         <column>
+          <property name="text">
+           <string>Image</string>
+          </property>
+         </column>
+         <column>
+          <property name="text">
+           <string>Image Address</string>
+          </property>
+         </column>
+        </widget>
+       </item>
+       <item>
+        <widget class="QLabel" name="label_5">
+         <property name="text">
+          <string>&lt;b&gt;Preview&lt;/b&gt;</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QGraphicsView" name="imagePreview"/>
+       </item>
+      </layout>
+     </widget>
+    </widget>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Close</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <tabstops>
+  <tabstop>tabWidget</tabstop>
+  <tabstop>tagsTree</tabstop>
+  <tabstop>imagesTree</tabstop>
+  <tabstop>imagePreview</tabstop>
+  <tabstop>buttonBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>SiteInfoDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>SiteInfoDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/SiteInfo/__init__.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2011 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Package implementing the site info widgets.
+"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/SpeedDial/Page.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,47 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a structure to hold the data for a speed dial page.
+"""
+
+
+from __future__ import unicode_literals
+
+
+class Page(object):
+    """
+    Class to hold the data for a speed dial page.
+    """
+    def __init__(self, url="", title="", broken=False):
+        """
+        Constructor
+        
+        @param url URL of the page (string)
+        @param title title of the page (string)
+        @param broken flag indicating a broken connection (boolean)
+        """
+        self.url = url
+        self.title = title
+        self.broken = broken
+    
+    def __eq__(self, other):
+        """
+        Special method implementing the equality operator.
+        
+        @param other reference to the other page object (Page)
+        @return flag indicating equality (boolean)
+        """
+        return self.title == other.title and \
+            self.url == other.url
+    
+    def isValid(self):
+        """
+        Public method to check the validity.
+        
+        @return flag indicating a valid object
+        @rtype bool
+        """
+        return bool(self.url)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/SpeedDial/PageThumbnailer.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,137 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing an object to create a thumbnail image of a web site.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, QSize, Qt, QUrl, \
+    QTimer
+from PyQt5.QtGui import QPixmap
+from PyQt5.QtQuickWidgets import QQuickWidget
+
+
+class PageThumbnailer(QObject):
+    """
+    Class implementing a thumbnail creator for web sites.
+    
+    @signal thumbnailCreated(QPixmap) emitted after the thumbnail has been
+        created
+    """
+    thumbnailCreated = pyqtSignal(QPixmap)
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object (QObject)
+        """
+        super(PageThumbnailer, self).__init__(parent)
+        
+        self.__size = QSize(231, 130)
+##        self.__size = QSize(450, 253)
+        self.__loadTitle = False
+        self.__title = ""
+        self.__url = QUrl()
+        
+        self.__view = QQuickWidget()
+        self.__view.setAttribute(Qt.WA_DontShowOnScreen)
+        self.__view.setSource(QUrl("qrc:qml/thumbnailer.qml"))
+        self.__view.rootContext().setContextProperty("thumbnailer", self)
+        self.__view.show()
+    
+    def setSize(self, size):
+        """
+        Public method to set the size of the image.
+        
+        @param size size of the image (QSize)
+        """
+        if size.isValid():
+            self.__size = QSize(size)
+    
+    def setUrl(self, url):
+        """
+        Public method to set the URL of the site to be thumbnailed.
+        
+        @param url URL of the web site (QUrl)
+        """
+        if url.isValid():
+            self.__url = QUrl(url)
+    
+    def url(self):
+        """
+        Public method to get the URL of the thumbnail.
+        
+        @return URL of the thumbnail (QUrl)
+        """
+        return QUrl(self.__url)
+    
+    def loadTitle(self):
+        """
+        Public method to check, if the title is loaded from the web site.
+        
+        @return flag indicating, that the title is loaded (boolean)
+        """
+        return self.__loadTitle
+    
+    def setLoadTitle(self, load):
+        """
+        Public method to set a flag indicating to load the title from
+        the web site.
+        
+        @param load flag indicating to load the title (boolean)
+        """
+        self.__loadTitle = load
+    
+    def title(self):
+        """
+        Public method to get the title of the thumbnail.
+        
+        @return title of the thumbnail (string)
+        """
+        if self.__title:
+            title = self.__title
+        else:
+            title = self.__url.host()
+        if not title:
+            title = self.__url.toString()
+        return title
+    
+    def start(self):
+        """
+        Public method to start the thumbnailing action.
+        """
+        if self.__view.rootObject():
+            self.__view.rootObject().setProperty("url", self.__url)
+        else:
+            QTimer.singleShot(0, lambda: self.thumbnailCreated.emit(QPixmap()))
+    
+    @pyqtSlot(bool)
+    def createThumbnail(self, status):
+        """
+        Private slot creating the thumbnail of the web site.
+        
+        @param status flag indicating a successful load of the web site
+            (boolean)
+        """
+        if not status:
+            self.thumbnailCreated.emit(QPixmap())
+            return
+        
+        QTimer.singleShot(1000, self.__grabThumbnail)
+    
+    def __grabThumbnail(self):
+        """
+        Private slot to grab the thumbnail image from the view.
+        """
+        self.__title = self.__view.rootObject().property("title")
+        pixmap = QPixmap.fromImage(
+            self.__view.grabFramebuffer().scaled(
+                self.__size, Qt.KeepAspectRatioByExpanding,
+##                self.__size, Qt.IgnoreAspectRatio,
+                Qt.SmoothTransformation))
+        self.thumbnailCreated.emit(pixmap)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/SpeedDial/SpeedDial.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,430 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the speed dial.
+"""
+
+from __future__ import unicode_literals
+try:
+    str = unicode
+except NameError:
+    pass
+
+import os
+
+from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, QCryptographicHash, \
+    QByteArray, QUrl, qWarning
+from PyQt5.QtGui import QPixmap
+
+from E5Gui import E5MessageBox
+
+from ..Tools.WebBrowserTools import pixmapToDataUrl
+
+from Utilities.AutoSaver import AutoSaver
+import Utilities
+
+
+class SpeedDial(QObject):
+    """
+    Class implementing the speed dial.
+    
+    @signal pagesChanged() emitted after the list of pages changed
+    @signal thumbnailLoaded(url, src) emitted after a thumbnail was loaded
+    @signal titleLoaded(url, title) emitted after a title was loaded
+    @signal speedDialSaved() emitted after the speed dial data was saved
+    """
+    pagesChanged = pyqtSignal()
+    thumbnailLoaded = pyqtSignal(str, str)
+    pageTitleLoaded = pyqtSignal(str, str)
+    speedDialSaved = pyqtSignal()
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object (QObject)
+        """
+        super(SpeedDial, self).__init__(parent)
+        
+        self.__regenerateScript = True
+        
+        self.__webPages = []
+        
+        self.__initialScript = ""
+        self.__thumbnailsDirectory = ""
+        
+        self.__thumbnailers = []
+        
+        self.__initialize()
+        
+        self.__saveTimer = AutoSaver(self, self.save)
+        self.pagesChanged.connect(self.__saveTimer.changeOccurred)
+    
+    def addPage(self, url, title):
+        """
+        Public method to add a page for the given data.
+        
+        @param url URL of the page (QUrl)
+        @param title title of the page (string)
+        """
+        if url.isEmpty():
+            return
+        
+        from .Page import Page
+        page = Page(
+            self.__escapeUrl(url.toString()),
+            self.__escapeTitle(title))
+        self.__webPages.append(page)
+        self.__regenerateScript = True
+        
+        self.pagesChanged.emit()
+    
+    def removePage(self, url):
+        """
+        Public method to remove a page.
+        
+        @param url URL of the page (QUrl)
+        """
+        page = self.pageForUrl(url)
+        if not page.isValid():
+            return
+        
+        self.removeImageForUrl(page.url)
+        self.__webPages.remove(page)
+        self.__regenerateScript = True
+        
+        self.pagesChanged.emit()
+    
+    def __imageFileName(self, url):
+        """
+        Private method to generate the image file name for a URL.
+        
+        @param url URL to generate the file name from (string)
+        @return name of the image file (string)
+        """
+        return os.path.join(
+            self.__thumbnailsDirectory,
+            str(QCryptographicHash.hash(QByteArray(url.encode("utf-8")),
+                QCryptographicHash.Md5).toHex(), encoding="utf-8") + ".png")
+    
+    def initialScript(self):
+        """
+        Public method to get the 'initial' JavaScript script.
+        
+        @return initial JavaScript script (string)
+        """
+        if self.__regenerateScript:
+            self.__regenerateScript = False
+            self.__initialScript = ""
+            
+            for page in self.__webPages:
+                if page.broken:
+                    imgSource = "qrc:icons/brokenPage.png"
+                else:
+                    imgSource = self.__imageFileName(page.url)
+                    if not os.path.exists(imgSource):
+                        self.loadThumbnail(page.url)
+                        imgSource = "qrc:icons/loading.gif"
+                        
+                        if not page.url:
+                            imgSource = ""
+                    else:
+                        imgSource = \
+                            pixmapToDataUrl(QPixmap(imgSource)).toString()
+                
+                self.__initialScript += \
+                    "addBox('{0}', '{1}', '{2}');\n".format(
+                        page.url, Utilities.html_uencode(page.title),
+                        imgSource)
+        
+        return self.__initialScript
+    
+    def getFileName(self):
+        """
+        Public method to get the file name of the user agents file.
+        
+        @return name of the user agents file (string)
+        """
+        return os.path.join(
+            Utilities.getConfigDir(), "web_browser", "speedDial.xml")
+    
+    def __initialize(self):
+        """
+        Private method to initialize the speed dial.
+        """
+        self.__thumbnailsDirectory = os.path.join(
+            Utilities.getConfigDir(), "web_browser", "thumbnails")
+        # Create directory if it does not exist yet
+        if not os.path.exists(self.__thumbnailsDirectory):
+            os.makedirs(self.__thumbnailsDirectory)
+        
+        self.__load()
+    
+    def reload(self):
+        """
+        Public method to reload the speed dial data.
+        """
+        self.__load()
+    
+    def __load(self):
+        """
+        Private method to load the speed dial configuration.
+        """
+        allPages, pagesPerRow, speedDialSize = [], 0, 0
+        
+        speedDialFile = self.getFileName()
+        if os.path.exists(speedDialFile):
+            from .SpeedDialReader import SpeedDialReader
+            reader = SpeedDialReader()
+            allPages, pagesPerRow, speedDialSize = reader.read(speedDialFile)
+        
+        self.__pagesPerRow = pagesPerRow if pagesPerRow else 4
+        self.__speedDialSize = speedDialSize if speedDialSize else 231
+        
+        if allPages:
+            self.__webPages = allPages
+            self.pagesChanged.emit()
+        else:
+            allPages = \
+                'url:"http://eric-ide.python-projects.org/"|'\
+                'title:"Eric Web Site";'\
+                'url:"http://www.riverbankcomputing.com/"|'\
+                'title:"PyQt Web Site";'\
+                'url:"http://www.qt.io/"|title:"Qt Web Site";'\
+                'url:"http://blog.qt.digia.com/"|title:"Qt Blog";'\
+                'url:"http://www.python.org"|title:"Python Language Website";'\
+                'url:"http://www.google.com"|title:"Google";'
+            self.changed(allPages)
+    
+    def save(self):
+        """
+        Public method to save the speed dial configuration.
+        """
+        from .SpeedDialWriter import SpeedDialWriter
+        speedDialFile = self.getFileName()
+        writer = SpeedDialWriter()
+        if not writer.write(speedDialFile, self.__webPages,
+                            self.__pagesPerRow, self.__speedDialSize):
+            E5MessageBox.critical(
+                None,
+                self.tr("Saving Speed Dial data"),
+                self.tr(
+                    """<p>Speed Dial data could not be saved to"""
+                    """ <b>{0}</b></p>""").format(speedDialFile))
+        else:
+            self.speedDialSaved.emit()
+    
+    def close(self):
+        """
+        Public method to close the user agents manager.
+        """
+        self.__saveTimer.saveIfNeccessary()
+    
+    def pageForUrl(self, url):
+        """
+        Public method to get the page for the given URL.
+        
+        @param url URL to be searched for (QUrl)
+        @return page for the URL (Page)
+        """
+        urlString = url.toString()
+        if urlString.endswith("/"):
+            urlString = urlString[:-1]
+        
+        for page in self.__webPages:
+            if page.url == urlString:
+                return page
+        
+        from .Page import Page
+        return Page()
+    
+    def urlForShortcut(self, key):
+        """
+        Public method to get the URL for the given shortcut key.
+        
+        @param key shortcut key (integer)
+        @return URL for the key (QUrl)
+        """
+        if key < 0 or len(self.__webPages) <= key:
+            return QUrl()
+        
+        return QUrl.fromEncoded(self.__webPages[key].url.encode("utf-8"))
+    
+    @pyqtSlot(str)
+    def changed(self, allPages):
+        """
+        Public slot to react on changed pages.
+        
+        @param allPages string giving all pages (string)
+        """
+        if not allPages:
+            return
+        
+        entries = allPages.split('";')
+        self.__webPages = []
+        
+        from .Page import Page
+        for entry in entries:
+            if not entry:
+                continue
+            
+            tmp = entry.split('"|')
+            if len(tmp) == 2:
+                broken = False
+            elif len(tmp) == 3:
+                broken = "brokenPage" in tmp[2][5:]
+            else:
+                continue
+            
+            url = tmp[0][5:]
+            if url.endswith("/"):
+                url = url[:-1]
+            title = tmp[1][7:]
+            page = Page(url, title, broken)
+            self.__webPages.append(page)
+        
+        self.pagesChanged.emit()
+    
+    @pyqtSlot(str, bool)
+    def loadThumbnail(self, url, loadTitle):
+        """
+        Public slot to load a thumbnail of the given URL.
+        
+        @param url URL of the thumbnail (string)
+        @param loadTitle flag indicating to get the title for the thumbnail
+            from the site (boolean)
+        """
+        if not url:
+            return
+        
+        from .PageThumbnailer import PageThumbnailer
+        thumbnailer = PageThumbnailer(self)
+        thumbnailer.setUrl(QUrl.fromEncoded(url.encode("utf-8")))
+        thumbnailer.setLoadTitle(loadTitle)
+        thumbnailer.thumbnailCreated.connect(self.__thumbnailCreated)
+        self.__thumbnailers.append(thumbnailer)
+        
+        thumbnailer.start()
+
+    @pyqtSlot(str)
+    def removeImageForUrl(self, url):
+        """
+        Public slot to remove the image for a URL.
+        
+        @param url URL to remove the image for (string)
+        """
+        fileName = self.__imageFileName(url)
+        if os.path.exists(fileName):
+            os.remove(fileName)
+
+    @pyqtSlot(str, result=str)
+    def urlFromUserInput(self, url):
+        """
+        Public slot to get the URL from user input.
+        
+        @param url URL entered by the user (string)
+        @return sanitized URL (string)
+        """
+        return QUrl.fromUserInput(url).toString()
+    
+    @pyqtSlot(int)
+    def setPagesInRow(self, count):
+        """
+        Public slot to set the number of pages per row.
+        
+        @param count number of pages per row (integer)
+        """
+        self.__pagesPerRow = count
+        self.__saveTimer.changeOccurred()
+
+    def pagesInRow(self):
+        """
+        Public method to get the number of dials per row.
+        
+        @return number of dials per row (integer)
+        """
+        return self.__pagesPerRow
+    
+    @pyqtSlot(int)
+    def setSdSize(self, size):
+        """
+        Public slot to set the size of the speed dial.
+        
+        @param size size of the speed dial (integer)
+        """
+        self.__speedDialSize = size
+        self.__saveTimer.changeOccurred()
+    
+    def sdSize(self):
+        """
+        Public method to get the speed dial size.
+        
+        @return speed dial size (integer)
+        """
+        return self.__speedDialSize
+    
+    def __thumbnailCreated(self, image):
+        """
+        Private slot to handle the creation of a thumbnail image.
+        
+        @param image thumbnail image (QPixmap)
+        """
+        from .PageThumbnailer import PageThumbnailer
+        thumbnailer = self.sender()
+        if not isinstance(thumbnailer, PageThumbnailer) or \
+           thumbnailer not in self.__thumbnailers:
+            return
+        
+        loadTitle = thumbnailer.loadTitle()
+        title = thumbnailer.title()
+        url = thumbnailer.url().toString()
+        fileName = self.__imageFileName(url)
+        
+        if image.isNull():
+            fileName = "qrc:icons/brokenPage.png"
+            title = self.tr("Unable to load")
+            loadTitle = True
+            page = self.pageForUrl(thumbnailer.url())
+            page.broken = True
+        else:
+            if not image.save(fileName, "PNG"):
+                qWarning(
+                    "SpeedDial.__thumbnailCreated: Cannot save thumbnail"
+                    " to {0}".format(fileName))
+        
+        self.__regenerateScript = True
+        thumbnailer.deleteLater()
+        self.__thumbnailers.remove(thumbnailer)
+        
+        if loadTitle:
+            self.pageTitleLoaded.emit(url, title)
+        
+        self.thumbnailLoaded.emit(
+            url, pixmapToDataUrl(QPixmap(fileName)).toString())
+    
+    def __escapeTitle(self, title):
+        """
+        Private method to escape a title string.
+        
+        @param title title string to be escaped
+        @type str
+        @return escaped title string
+        @rtype str
+        """
+        title = title.replace('"', "&quot;").replace("'", "&apos;")
+        return title
+    
+    def __escapeUrl(self, url):
+        """
+        Private method to escape an URL string.
+        
+        @param url URL to be escaped
+        @type str
+        @return escaped URL string
+        @rtype str
+        """
+        url = url.replace('"', "").replace("'", "")
+        return url
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/SpeedDial/SpeedDialReader.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,118 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+
+"""
+Module implementing a class to read speed dial data files.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import QXmlStreamReader, QIODevice, QFile, QCoreApplication
+
+
+class SpeedDialReader(QXmlStreamReader):
+    """
+    Class implementing a reader object for speed dial data files.
+    """
+    def __init__(self):
+        """
+        Constructor
+        """
+        super(SpeedDialReader, self).__init__()
+    
+    def read(self, fileNameOrDevice):
+        """
+        Public method to read a user agent file.
+        
+        @param fileNameOrDevice name of the file to read (string)
+            or reference to the device to read (QIODevice)
+        @return list of speed dial pages (list of Page), number of pages per
+            row (integer) and size of the speed dial pages (integer)
+        """
+        self.__pages = []
+        self.__pagesPerRow = 0
+        self.__sdSize = 0
+        
+        if isinstance(fileNameOrDevice, QIODevice):
+            self.setDevice(fileNameOrDevice)
+        else:
+            f = QFile(fileNameOrDevice)
+            if not f.exists():
+                return self.__pages, self.__pagesPerRow, self.__sdSize
+            opened = f.open(QFile.ReadOnly)
+            if not opened:
+                self.raiseError(QCoreApplication.translate(
+                    "SpeedDialReader",
+                    "The file {0} could not be opened. Error: {1}").format(
+                    fileNameOrDevice, f.errorString()))
+                return self.__pages, self.__pagesPerRow, self.__sdSize
+            self.setDevice(f)
+        
+        while not self.atEnd():
+            self.readNext()
+            if self.isStartElement():
+                version = self.attributes().value("version")
+                if self.name() == "SpeedDial" and \
+                   (not version or version == "1.0"):
+                    self.__readSpeedDial()
+                else:
+                    self.raiseError(QCoreApplication.translate(
+                        "SpeedDialReader",
+                        "The file is not a SpeedDial version 1.0 file."))
+        
+        return self.__pages, self.__pagesPerRow, self.__sdSize
+    
+    def __readSpeedDial(self):
+        """
+        Private method to read the speed dial data.
+        """
+        if not self.isStartElement() and self.name() != "SpeedDial":
+            return
+        
+        while not self.atEnd():
+            self.readNext()
+            if self.isEndElement():
+                if self.name() in ["Pages", "Page"]:
+                    continue
+                else:
+                    break
+            
+            if self.isStartElement():
+                if self.name() == "Pages":
+                    attributes = self.attributes()
+                    pagesPerRow = attributes.value("row")
+                    if pagesPerRow.isdigit():
+                        self.__pagesPerRow = int(pagesPerRow)
+                    sdSize = attributes.value("size")
+                    if sdSize.isdigit():
+                        self.__sdSize = int(sdSize)
+                elif self.name() == "Page":
+                    attributes = self.attributes()
+                    url = attributes.value("url")
+                    title = attributes.value("title")
+                    if url:
+                        if not title:
+                            title = url
+                        from .Page import Page
+                        page = Page(url, title)
+                        self.__pages.append(page)
+                else:
+                    self.__skipUnknownElement()
+    
+    def __skipUnknownElement(self):
+        """
+        Private method to skip over all unknown elements.
+        """
+        if not self.isStartElement():
+            return
+        
+        while not self.atEnd():
+            self.readNext()
+            if self.isEndElement():
+                break
+            
+            if self.isStartElement():
+                self.__skipUnknownElement()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/SpeedDial/SpeedDialWriter.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,72 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a class to write speed dial data files.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import QXmlStreamWriter, QIODevice, QFile
+
+
+class SpeedDialWriter(QXmlStreamWriter):
+    """
+    Class implementing a writer object to generate speed dial data files.
+    """
+    def __init__(self):
+        """
+        Constructor
+        """
+        super(SpeedDialWriter, self).__init__()
+        
+        self.setAutoFormatting(True)
+    
+    def write(self, fileNameOrDevice, pages, pagesPerRow, speedDialSize):
+        """
+        Public method to write a speed dial data file.
+        
+        @param fileNameOrDevice name of the file to write (string)
+            or device to write to (QIODevice)
+        @param pages list of speed dial pages (list of Page)
+        @param pagesPerRow number of pages per row (integer)
+        @param speedDialSize size of the speed dial pages (integer)
+        @return flag indicating success (boolean)
+        """
+        if isinstance(fileNameOrDevice, QIODevice):
+            f = fileNameOrDevice
+        else:
+            f = QFile(fileNameOrDevice)
+            if not f.open(QFile.WriteOnly):
+                return False
+        
+        self.setDevice(f)
+        return self.__write(pages, pagesPerRow, speedDialSize)
+    
+    def __write(self, pages, pagesPerRow, speedDialSize):
+        """
+        Private method to write a speed dial file.
+        
+        @param pages list of speed dial pages (list of Page)
+        @param pagesPerRow number of pages per row (integer)
+        @param speedDialSize size of the speed dial pages (integer)
+        @return flag indicating success (boolean)
+        """
+        self.writeStartDocument()
+        self.writeDTD("<!DOCTYPE speeddial>")
+        self.writeStartElement("SpeedDial")
+        self.writeAttribute("version", "1.0")
+        
+        self.writeStartElement("Pages")
+        self.writeAttribute("row", str(pagesPerRow))
+        self.writeAttribute("size", str(speedDialSize))
+        
+        for page in pages:
+            self.writeEmptyElement("Page")
+            self.writeAttribute("url", page.url)
+            self.writeAttribute("title", page.title)
+        
+        self.writeEndDocument()
+        return True
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/SpeedDial/__init__.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Package implementing the speed dial functionality.
+"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Sync/DirectorySyncHandler.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,279 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a synchronization handler using a shared directory.
+"""
+
+from __future__ import unicode_literals
+
+import os
+
+from PyQt5.QtCore import pyqtSignal, QByteArray, QFileInfo, QCoreApplication
+
+from .SyncHandler import SyncHandler
+
+from WebBrowser.WebBrowserWindow import WebBrowserWindow
+
+import Preferences
+
+
+class DirectorySyncHandler(SyncHandler):
+    """
+    Class implementing a synchronization handler using a shared directory.
+    
+    @signal syncStatus(type_, message) emitted to indicate the synchronization
+        status (string one of "bookmarks", "history", "passwords",
+        "useragents" or "speeddial", string)
+    @signal syncError(message) emitted for a general error with the error
+        message (string)
+    @signal syncMessage(message) emitted to send a message about
+        synchronization (string)
+    @signal syncFinished(type_, done, download) emitted after a
+        synchronization has finished (string one of "bookmarks", "history",
+        "passwords", "useragents" or "speeddial", boolean, boolean)
+    """
+    syncStatus = pyqtSignal(str, str)
+    syncError = pyqtSignal(str)
+    syncMessage = pyqtSignal(str)
+    syncFinished = pyqtSignal(str, bool, bool)
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object (QObject)
+        """
+        super(DirectorySyncHandler, self).__init__(parent)
+        self.__forceUpload = False
+        
+        self.__remoteFilesFound = []
+    
+    def initialLoadAndCheck(self, forceUpload):
+        """
+        Public method to do the initial check.
+        
+        @keyparam forceUpload flag indicating a forced upload of the files
+            (boolean)
+        """
+        if not Preferences.getWebBrowser("SyncEnabled"):
+            return
+        
+        self.__forceUpload = forceUpload
+        
+        self.__remoteFilesFound = []
+        
+        # check the existence of the shared directory; create it, if it is
+        # not there
+        if not os.path.exists(Preferences.getWebBrowser("SyncDirectoryPath")):
+            try:
+                os.makedirs(Preferences.getWebBrowser("SyncDirectoryPath"))
+            except OSError as err:
+                self.syncError.emit(
+                    self.tr("Error creating the shared directory.\n{0}")
+                    .format(str(err)))
+                return
+        
+        self.__initialSync()
+    
+    def __downloadFile(self, type_, fileName, timestamp):
+        """
+        Private method to downlaod the given file.
+        
+        @param type_ type of the synchronization event (string one
+            of "bookmarks", "history", "passwords", "useragents" or
+            "speeddial")
+        @param fileName name of the file to be downloaded (string)
+        @param timestamp time stamp in seconds of the file to be downloaded
+            (integer)
+        """
+        self.syncStatus.emit(type_, self._messages[type_]["RemoteExists"])
+        try:
+            f = open(os.path.join(
+                    Preferences.getWebBrowser("SyncDirectoryPath"),
+                    self._remoteFiles[type_]), "rb")
+            data = f.read()
+            f.close()
+        except IOError as err:
+            self.syncStatus.emit(
+                type_,
+                self.tr("Cannot read remote file.\n{0}").format(str(err)))
+            self.syncFinished.emit(type_, False, True)
+            return
+        
+        QCoreApplication.processEvents()
+        ok, error = self.writeFile(QByteArray(data), fileName, type_,
+                                   timestamp)
+        if not ok:
+            self.syncStatus.emit(type_, error)
+        self.syncFinished.emit(type_, ok, True)
+    
+    def __uploadFile(self, type_, fileName):
+        """
+        Private method to upload the given file.
+        
+        @param type_ type of the synchronization event (string one
+            of "bookmarks", "history", "passwords", "useragents" or
+            "speeddial")
+        @param fileName name of the file to be uploaded (string)
+        """
+        QCoreApplication.processEvents()
+        data = self.readFile(fileName, type_)
+        if data.isEmpty():
+            self.syncStatus.emit(type_, self._messages[type_]["LocalMissing"])
+            self.syncFinished.emit(type_, False, False)
+            return
+        else:
+            try:
+                f = open(os.path.join(
+                    Preferences.getWebBrowser("SyncDirectoryPath"),
+                    self._remoteFiles[type_]), "wb")
+                f.write(bytes(data))
+                f.close()
+            except IOError as err:
+                self.syncStatus.emit(
+                    type_,
+                    self.tr("Cannot write remote file.\n{0}").format(
+                        str(err)))
+                self.syncFinished.emit(type_, False, False)
+                return
+            
+        self.syncFinished.emit(type_, True, False)
+    
+    def __initialSyncFile(self, type_, fileName):
+        """
+        Private method to do the initial synchronization of the given file.
+        
+        @param type_ type of the synchronization event (string one
+            of "bookmarks", "history", "passwords", "useragents" or
+            "speeddial")
+        @param fileName name of the file to be synchronized (string)
+        """
+        if not self.__forceUpload and \
+                os.path.exists(os.path.join(
+                    Preferences.getWebBrowser("SyncDirectoryPath"),
+                    self._remoteFiles[type_])) and \
+                QFileInfo(fileName).lastModified() <= QFileInfo(
+                    os.path.join(
+                        Preferences.getWebBrowser("SyncDirectoryPath"),
+                        self._remoteFiles[type_])).lastModified():
+            self.__downloadFile(
+                type_, fileName,
+                QFileInfo(os.path.join(
+                    Preferences.getWebBrowser("SyncDirectoryPath"),
+                    self._remoteFiles[type_])).lastModified().toTime_t())
+        else:
+            if not os.path.exists(os.path.join(
+                    Preferences.getWebBrowser("SyncDirectoryPath"),
+                    self._remoteFiles[type_])):
+                self.syncStatus.emit(
+                    type_, self._messages[type_]["RemoteMissing"])
+            else:
+                self.syncStatus.emit(
+                    type_, self._messages[type_]["LocalNewer"])
+            self.__uploadFile(type_, fileName)
+    
+    def __initialSync(self):
+        """
+        Private slot to do the initial synchronization.
+        """
+        QCoreApplication.processEvents()
+        # Bookmarks
+        if Preferences.getWebBrowser("SyncBookmarks"):
+            self.__initialSyncFile(
+                "bookmarks",
+                WebBrowserWindow.bookmarksManager().getFileName())
+        
+        QCoreApplication.processEvents()
+        # History
+        if Preferences.getWebBrowser("SyncHistory"):
+            self.__initialSyncFile(
+                "history",
+                WebBrowserWindow.historyManager().getFileName())
+        
+        QCoreApplication.processEvents()
+        # Passwords
+        if Preferences.getWebBrowser("SyncPasswords"):
+            self.__initialSyncFile(
+                "passwords",
+                WebBrowserWindow.passwordManager().getFileName())
+        
+        # TODO: UserAgents
+##        QCoreApplication.processEvents()
+##        # User Agent Settings
+##        if Preferences.getWebBrowser("SyncUserAgents"):
+##            self.__initialSyncFile(
+##                "useragents",
+##                WebBrowserWindow.userAgentsManager().getFileName())
+        
+        QCoreApplication.processEvents()
+        # Speed Dial Settings
+        if Preferences.getWebBrowser("SyncSpeedDial"):
+            self.__initialSyncFile(
+                "speeddial",
+                WebBrowserWindow.speedDial().getFileName())
+        
+        self.__forceUpload = False
+        self.syncMessage.emit(self.tr("Synchronization finished"))
+    
+    def __syncFile(self, type_, fileName):
+        """
+        Private method to synchronize the given file.
+        
+        @param type_ type of the synchronization event (string one
+            of "bookmarks", "history", "passwords", "useragents" or
+            "speeddial")
+        @param fileName name of the file to be synchronized (string)
+        """
+        self.syncStatus.emit(type_, self._messages[type_]["Uploading"])
+        self.__uploadFile(type_, fileName)
+    
+    def syncBookmarks(self):
+        """
+        Public method to synchronize the bookmarks.
+        """
+        self.__syncFile(
+            "bookmarks",
+            WebBrowserWindow.bookmarksManager().getFileName())
+    
+    def syncHistory(self):
+        """
+        Public method to synchronize the history.
+        """
+        self.__syncFile(
+            "history",
+            WebBrowserWindow.historyManager().getFileName())
+    
+    def syncPasswords(self):
+        """
+        Public method to synchronize the passwords.
+        """
+        self.__syncFile(
+            "passwords",
+            WebBrowserWindow.passwordManager().getFileName())
+    
+    # TODO: UserAgents
+    def syncUserAgents(self):
+        """
+        Public method to synchronize the user agents.
+        """
+##        self.__syncFile(
+##            "useragents",
+##            WebBrowserWindow.userAgentsManager().getFileName())
+    
+    def syncSpeedDial(self):
+        """
+        Public method to synchronize the speed dial data.
+        """
+        self.__syncFile(
+            "speeddial",
+            WebBrowserWindow.speedDial().getFileName())
+    
+    def shutdown(self):
+        """
+        Public method to shut down the handler.
+        """
+        # nothing to do
+        return
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Sync/FtpSyncHandler.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,412 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a synchronization handler using FTP.
+"""
+
+from __future__ import unicode_literals
+
+import ftplib
+import io
+
+from PyQt5.QtCore import pyqtSignal, QTimer, QFileInfo, QCoreApplication, \
+    QByteArray
+
+from E5Network.E5Ftp import E5Ftp, E5FtpProxyType, E5FtpProxyError
+
+from .SyncHandler import SyncHandler
+
+from WebBrowser.WebBrowserWindow import WebBrowserWindow
+
+import Preferences
+
+from Utilities.FtpUtilities import FtpDirLineParser, FtpDirLineParserError
+
+
+class FtpSyncHandler(SyncHandler):
+    """
+    Class implementing a synchronization handler using FTP.
+    
+    @signal syncStatus(type_, message) emitted to indicate the synchronization
+        status (string one of "bookmarks", "history", "passwords",
+        "useragents" or "speeddial", string)
+    @signal syncError(message) emitted for a general error with the error
+        message (string)
+    @signal syncMessage(message) emitted to send a message about
+        synchronization (string)
+    @signal syncFinished(type_, done, download) emitted after a
+        synchronization has finished (string one of "bookmarks", "history",
+        "passwords", "useragents" or "speeddial", boolean, boolean)
+    """
+    syncStatus = pyqtSignal(str, str)
+    syncError = pyqtSignal(str)
+    syncMessage = pyqtSignal(str)
+    syncFinished = pyqtSignal(str, bool, bool)
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object (QObject)
+        """
+        super(FtpSyncHandler, self).__init__(parent)
+        
+        self.__state = "idle"
+        self.__forceUpload = False
+        self.__connected = False
+        
+        self.__remoteFilesFound = {}
+    
+    def initialLoadAndCheck(self, forceUpload):
+        """
+        Public method to do the initial check.
+        
+        @keyparam forceUpload flag indicating a forced upload of the files
+            (boolean)
+        """
+        if not Preferences.getWebBrowser("SyncEnabled"):
+            return
+        
+        self.__state = "initializing"
+        self.__forceUpload = forceUpload
+        
+        self.__dirLineParser = FtpDirLineParser()
+        self.__remoteFilesFound = {}
+        
+        self.__idleTimer = QTimer(self)
+        self.__idleTimer.setInterval(
+            Preferences.getWebBrowser("SyncFtpIdleTimeout") * 1000)
+        self.__idleTimer.timeout.connect(self.__idleTimeout)
+        
+        self.__ftp = E5Ftp()
+        
+        # do proxy setup
+        if not Preferences.getUI("UseProxy"):
+            proxyType = E5FtpProxyType.NoProxy
+        else:
+            proxyType = Preferences.getUI("ProxyType/Ftp")
+        if proxyType != E5FtpProxyType.NoProxy:
+            self.__ftp.setProxy(
+                proxyType,
+                Preferences.getUI("ProxyHost/Ftp"),
+                Preferences.getUI("ProxyPort/Ftp"))
+            if proxyType != E5FtpProxyType.NonAuthorizing:
+                self.__ftp.setProxyAuthentication(
+                    Preferences.getUI("ProxyUser/Ftp"),
+                    Preferences.getUI("ProxyPassword/Ftp"),
+                    Preferences.getUI("ProxyAccount/Ftp"))
+        
+        QTimer.singleShot(0, self.__doFtpCommands)
+    
+    def __doFtpCommands(self):
+        """
+        Private slot executing the sequence of FTP commands.
+        """
+        try:
+            ok = self.__connectAndLogin()
+            if ok:
+                self.__changeToStore()
+                self.__ftp.retrlines("LIST", self.__dirListCallback)
+                self.__initialSync()
+                self.__state = "idle"
+                self.__idleTimer.start()
+        except (ftplib.all_errors + (E5FtpProxyError,)) as err:
+            self.syncError.emit(str(err))
+    
+    def __connectAndLogin(self):
+        """
+        Private method to connect to the FTP server and log in.
+        
+        @return flag indicating a successful log in (boolean)
+        """
+        self.__ftp.connect(
+            Preferences.getWebBrowser("SyncFtpServer"),
+            Preferences.getWebBrowser("SyncFtpPort"),
+            timeout=5)
+        self.__ftp.login(
+            Preferences.getWebBrowser("SyncFtpUser"),
+            Preferences.getWebBrowser("SyncFtpPassword"))
+        self.__connected = True
+        return True
+    
+    def __changeToStore(self):
+        """
+        Private slot to change to the storage directory.
+        
+        This action will create the storage path on the server, if it
+        does not exist. Upon return, the current directory of the server
+        is the sync directory.
+        """
+        storePathList = \
+            Preferences.getWebBrowser("SyncFtpPath")\
+            .replace("\\", "/").split("/")
+        if storePathList[0] == "":
+            storePathList.pop(0)
+        while storePathList:
+            path = storePathList[0]
+            try:
+                self.__ftp.cwd(path)
+            except ftplib.error_perm as err:
+                code = err.args[0].strip()[:3]
+                if code == "550":
+                    # path does not exist, create it
+                    self.__ftp.mkd(path)
+                    self.__ftp.cwd(path)
+                else:
+                    raise
+            storePathList.pop(0)
+    
+    def __dirListCallback(self, line):
+        """
+        Private slot handling the receipt of directory listing lines.
+        
+        @param line the received line of the directory listing (string)
+        """
+        try:
+            urlInfo = self.__dirLineParser.parseLine(line)
+        except FtpDirLineParserError:
+            # silently ignore parser errors
+            urlInfo = None
+        
+        if urlInfo and urlInfo.isValid() and urlInfo.isFile():
+            if urlInfo.name() in self._remoteFiles.values():
+                self.__remoteFilesFound[urlInfo.name()] = \
+                    urlInfo.lastModified()
+        
+        QCoreApplication.processEvents()
+    
+    def __downloadFile(self, type_, fileName, timestamp):
+        """
+        Private method to downlaod the given file.
+        
+        @param type_ type of the synchronization event (string one
+            of "bookmarks", "history", "passwords", "useragents" or
+            "speeddial")
+        @param fileName name of the file to be downloaded (string)
+        @param timestamp time stamp in seconds of the file to be downloaded
+            (integer)
+        """
+        self.syncStatus.emit(type_, self._messages[type_]["RemoteExists"])
+        buffer = io.BytesIO()
+        try:
+            self.__ftp.retrbinary(
+                "RETR {0}".format(self._remoteFiles[type_]),
+                lambda x: self.__downloadFileCallback(buffer, x))
+            ok, error = self.writeFile(
+                QByteArray(buffer.getvalue()), fileName, type_, timestamp)
+            if not ok:
+                self.syncStatus.emit(type_, error)
+            self.syncFinished.emit(type_, ok, True)
+        except ftplib.all_errors as err:
+            self.syncStatus.emit(type_, str(err))
+            self.syncFinished.emit(type_, False, True)
+    
+    def __downloadFileCallback(self, buffer, data):
+        """
+        Private method receiving the downloaded data.
+        
+        @param buffer reference to the buffer (io.BytesIO)
+        @param data byte string to store in the buffer (bytes)
+        @return number of bytes written to the buffer (integer)
+        """
+        res = buffer.write(data)
+        QCoreApplication.processEvents()
+        return res
+    
+    def __uploadFile(self, type_, fileName):
+        """
+        Private method to upload the given file.
+        
+        @param type_ type of the synchronization event (string one
+            of "bookmarks", "history", "passwords", "useragents" or
+            "speeddial")
+        @param fileName name of the file to be uploaded (string)
+        @return flag indicating success (boolean)
+        """
+        res = False
+        data = self.readFile(fileName, type_)
+        if data.isEmpty():
+            self.syncStatus.emit(type_, self._messages[type_]["LocalMissing"])
+            self.syncFinished.emit(type_, False, False)
+        else:
+            buffer = io.BytesIO(data.data())
+            try:
+                self.__ftp.storbinary(
+                    "STOR {0}".format(self._remoteFiles[type_]),
+                    buffer,
+                    callback=lambda x: QCoreApplication.processEvents())
+                self.syncFinished.emit(type_, True, False)
+                res = True
+            except ftplib.all_errors as err:
+                self.syncStatus.emit(type_, str(err))
+                self.syncFinished.emit(type_, False, False)
+        return res
+    
+    def __initialSyncFile(self, type_, fileName):
+        """
+        Private method to do the initial synchronization of the given file.
+        
+        @param type_ type of the synchronization event (string one
+            of "bookmarks", "history", "passwords", "useragents" or
+            "speeddial")
+        @param fileName name of the file to be synchronized (string)
+        """
+        if not self.__forceUpload and \
+           self._remoteFiles[type_] in self.__remoteFilesFound:
+            if QFileInfo(fileName).lastModified() < \
+               self.__remoteFilesFound[self._remoteFiles[type_]]:
+                self.__downloadFile(
+                    type_, fileName,
+                    self.__remoteFilesFound[self._remoteFiles[type_]]
+                        .toTime_t())
+            else:
+                self.syncStatus.emit(
+                    type_, self.tr("No synchronization required."))
+                self.syncFinished.emit(type_, True, True)
+        else:
+            if self._remoteFiles[type_] not in self.__remoteFilesFound:
+                self.syncStatus.emit(
+                    type_, self._messages[type_]["RemoteMissing"])
+            else:
+                self.syncStatus.emit(
+                    type_, self._messages[type_]["LocalNewer"])
+            self.__uploadFile(type_, fileName)
+    
+    def __initialSync(self):
+        """
+        Private slot to do the initial synchronization.
+        """
+        # Bookmarks
+        if Preferences.getWebBrowser("SyncBookmarks"):
+            self.__initialSyncFile(
+                "bookmarks",
+                WebBrowserWindow.bookmarksManager().getFileName())
+        
+        # History
+        if Preferences.getWebBrowser("SyncHistory"):
+            self.__initialSyncFile(
+                "history",
+                WebBrowserWindow.historyManager().getFileName())
+        
+        # Passwords
+        if Preferences.getWebBrowser("SyncPasswords"):
+            self.__initialSyncFile(
+                "passwords",
+                WebBrowserWindow.passwordManager().getFileName())
+        
+        # TODO: UserAgents
+        # User Agent Settings
+##        if Preferences.getWebBrowser("SyncUserAgents"):
+##            self.__initialSyncFile(
+##                "useragents",
+##                WebBrowserWindow.userAgentsManager().getFileName())
+        
+        # Speed Dial Settings
+        if Preferences.getWebBrowser("SyncSpeedDial"):
+            self.__initialSyncFile(
+                "speeddial",
+                WebBrowserWindow.speedDial().getFileName())
+        
+        self.__forceUpload = False
+    
+    def __syncFile(self, type_, fileName):
+        """
+        Private method to synchronize the given file.
+        
+        @param type_ type of the synchronization event (string one
+            of "bookmarks", "history", "passwords", "useragents" or
+            "speeddial")
+        @param fileName name of the file to be synchronized (string)
+        """
+        if self.__state == "initializing":
+            return
+        
+        # use idle timeout to check, if we are still connected
+        if self.__connected:
+            self.__idleTimeout()
+        if not self.__connected or self.__ftp.sock is None:
+            ok = self.__connectAndLogin()
+            if not ok:
+                self.syncStatus.emit(
+                    type_, self.tr("Cannot log in to FTP host."))
+                return
+        
+        # upload the changed file
+        self.__state = "uploading"
+        self.syncStatus.emit(type_, self._messages[type_]["Uploading"])
+        if self.__uploadFile(type_, fileName):
+            self.syncStatus.emit(
+                type_, self.tr("Synchronization finished."))
+        self.__state = "idle"
+    
+    def syncBookmarks(self):
+        """
+        Public method to synchronize the bookmarks.
+        """
+        self.__syncFile(
+            "bookmarks",
+            WebBrowserWindow.bookmarksManager().getFileName())
+    
+    def syncHistory(self):
+        """
+        Public method to synchronize the history.
+        """
+        self.__syncFile(
+            "history",
+            WebBrowserWindow.historyManager().getFileName())
+    
+    def syncPasswords(self):
+        """
+        Public method to synchronize the passwords.
+        """
+        self.__syncFile(
+            "passwords",
+            WebBrowserWindow.passwordManager().getFileName())
+    
+    # TODO: UserAgents
+    def syncUserAgents(self):
+        """
+        Public method to synchronize the user agents.
+        """
+##        self.__syncFile(
+##            "useragents",
+##            WebBrowserWindow.userAgentsManager().getFileName())
+    
+    def syncSpeedDial(self):
+        """
+        Public method to synchronize the speed dial data.
+        """
+        self.__syncFile(
+            "speeddial",
+            WebBrowserWindow.speedDial().getFileName())
+    
+    def shutdown(self):
+        """
+        Public method to shut down the handler.
+        """
+        if self.__idleTimer.isActive():
+            self.__idleTimer.stop()
+        
+        try:
+            if self.__connected:
+                self.__ftp.quit()
+        except ftplib.all_errors:
+            pass    # ignore FTP errors because we are shutting down anyway
+        self.__connected = False
+    
+    def __idleTimeout(self):
+        """
+        Private slot to prevent a disconnect from the server.
+        """
+        if self.__state == "idle" and self.__connected:
+            try:
+                self.__ftp.voidcmd("NOOP")
+            except ftplib.Error as err:
+                code = err.args[0].strip()[:3]
+                if code == "421":
+                    self.__connected = False
+            except IOError:
+                self.__connected = False
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Sync/SyncAssistantDialog.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,56 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a wizard dialog to enter the synchronization data.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtWidgets import QWizard
+
+import UI.PixmapCache
+import Globals
+
+
+class SyncAssistantDialog(QWizard):
+    """
+    Class implementing a wizard dialog to enter the synchronization data.
+    """
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(SyncAssistantDialog, self).__init__(parent)
+        
+        from . import SyncGlobals
+
+        from .SyncDataPage import SyncDataPage
+        from .SyncEncryptionPage import SyncEncryptionPage
+        from .SyncHostTypePage import SyncHostTypePage
+        from .SyncFtpSettingsPage import SyncFtpSettingsPage
+        from .SyncDirectorySettingsPage import SyncDirectorySettingsPage
+        from .SyncCheckPage import SyncCheckPage
+
+        self.setPage(SyncGlobals.PageData, SyncDataPage(self))
+        self.setPage(SyncGlobals.PageEncryption, SyncEncryptionPage(self))
+        self.setPage(SyncGlobals.PageType, SyncHostTypePage(self))
+        self.setPage(SyncGlobals.PageFTPSettings, SyncFtpSettingsPage(self))
+        self.setPage(SyncGlobals.PageDirectorySettings,
+                     SyncDirectorySettingsPage(self))
+        self.setPage(SyncGlobals.PageCheck, SyncCheckPage(self))
+        
+        self.setPixmap(QWizard.LogoPixmap,
+                       UI.PixmapCache.getPixmap("ericWeb48.png"))
+        self.setPixmap(QWizard.WatermarkPixmap,
+                       UI.PixmapCache.getPixmap("eric256.png"))
+        self.setPixmap(QWizard.BackgroundPixmap,
+                       UI.PixmapCache.getPixmap("eric256.png"))
+        
+        self.setMinimumSize(650, 450)
+        if Globals.isWindowsPlatform():
+            self.setWizardStyle(QWizard.ModernStyle)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Sync/SyncCheckPage.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,211 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the synchronization status wizard page.
+"""
+
+from __future__ import unicode_literals
+
+import os
+
+from PyQt5.QtCore import QByteArray, QTimer
+from PyQt5.QtGui import QMovie
+from PyQt5.QtWidgets import QWizardPage
+
+from . import SyncGlobals
+
+from .Ui_SyncCheckPage import Ui_SyncCheckPage
+
+import Preferences
+import UI.PixmapCache
+
+from eric6config import getConfig
+
+
+class SyncCheckPage(QWizardPage, Ui_SyncCheckPage):
+    """
+    Class implementing the synchronization status wizard page.
+    """
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(SyncCheckPage, self).__init__(parent)
+        self.setupUi(self)
+    
+    def initializePage(self):
+        """
+        Public method to initialize the page.
+        """
+        self.syncErrorLabel.hide()
+        
+        forceUpload = self.field("ReencryptData")
+        
+        from WebBrowser.WebBrowserWindow import WebBrowserWindow
+        syncMgr = WebBrowserWindow.syncManager()
+        syncMgr.syncError.connect(self.__syncError)
+        syncMgr.syncStatus.connect(self.__updateMessages)
+        syncMgr.syncFinished.connect(self.__updateLabels)
+        
+        if Preferences.getWebBrowser("SyncType") == SyncGlobals.SyncTypeFtp:
+            self.handlerLabel.setText(self.tr("FTP"))
+            self.infoLabel.setText(self.tr("Host:"))
+            self.infoDataLabel.setText(
+                Preferences.getWebBrowser("SyncFtpServer"))
+        elif Preferences.getWebBrowser("SyncType") == \
+                SyncGlobals.SyncTypeDirectory:
+            self.handlerLabel.setText(self.tr("Shared Directory"))
+            self.infoLabel.setText(self.tr("Directory:"))
+            self.infoDataLabel.setText(
+                Preferences.getWebBrowser("SyncDirectoryPath"))
+        else:
+            self.handlerLabel.setText(self.tr("No Synchronization"))
+            self.hostLabel.setText("")
+        
+        self.bookmarkMsgLabel.setText("")
+        self.historyMsgLabel.setText("")
+        self.passwordsMsgLabel.setText("")
+        self.userAgentsMsgLabel.setText("")
+        self.speedDialMsgLabel.setText("")
+        
+        if not syncMgr.syncEnabled():
+            self.bookmarkLabel.setPixmap(
+                UI.PixmapCache.getPixmap("syncNo.png"))
+            self.historyLabel.setPixmap(UI.PixmapCache.getPixmap("syncNo.png"))
+            self.passwordsLabel.setPixmap(
+                UI.PixmapCache.getPixmap("syncNo.png"))
+            self.userAgentsLabel.setPixmap(
+                UI.PixmapCache.getPixmap("syncNo.png"))
+            self.speedDialLabel.setPixmap(
+                UI.PixmapCache.getPixmap("syncNo.png"))
+            return
+        
+        animationFile = os.path.join(getConfig("ericPixDir"), "loading.gif")
+        
+        # bookmarks
+        if Preferences.getWebBrowser("SyncBookmarks"):
+            self.__makeAnimatedLabel(animationFile, self.bookmarkLabel)
+        else:
+            self.bookmarkLabel.setPixmap(
+                UI.PixmapCache.getPixmap("syncNo.png"))
+        
+        # history
+        if Preferences.getWebBrowser("SyncHistory"):
+            self.__makeAnimatedLabel(animationFile, self.historyLabel)
+        else:
+            self.historyLabel.setPixmap(UI.PixmapCache.getPixmap("syncNo.png"))
+        
+        # Passwords
+        if Preferences.getWebBrowser("SyncPasswords"):
+            self.__makeAnimatedLabel(animationFile, self.passwordsLabel)
+        else:
+            self.passwordsLabel.setPixmap(
+                UI.PixmapCache.getPixmap("syncNo.png"))
+        
+        # user agent settings
+        if Preferences.getWebBrowser("SyncUserAgents"):
+            self.__makeAnimatedLabel(animationFile, self.userAgentsLabel)
+        else:
+            self.userAgentsLabel.setPixmap(
+                UI.PixmapCache.getPixmap("syncNo.png"))
+        
+        # speed dial settings
+        if Preferences.getWebBrowser("SyncSpeedDial"):
+            self.__makeAnimatedLabel(animationFile, self.speedDialLabel)
+        else:
+            self.speedDialLabel.setPixmap(
+                UI.PixmapCache.getPixmap("syncNo.png"))
+        
+        QTimer.singleShot(
+            0, lambda: syncMgr.loadSettings(forceUpload=forceUpload))
+    
+    def __makeAnimatedLabel(self, fileName, label):
+        """
+        Private slot to create an animated label.
+        
+        @param fileName name of the file containing the animation (string)
+        @param label reference to the label to be animated (QLabel)
+        """
+        movie = QMovie(fileName, QByteArray(), label)
+        movie.setSpeed(100)
+        label.setMovie(movie)
+        movie.start()
+    
+    def __updateMessages(self, type_, msg):
+        """
+        Private slot to update the synchronization status info.
+        
+        @param type_ type of synchronization data (string)
+        @param msg synchronization message (string)
+        """
+        if type_ == "bookmarks":
+            self.bookmarkMsgLabel.setText(msg)
+        elif type_ == "history":
+            self.historyMsgLabel.setText(msg)
+        elif type_ == "passwords":
+            self.passwordsMsgLabel.setText(msg)
+        elif type_ == "useragents":
+            self.userAgentsMsgLabel.setText(msg)
+        elif type_ == "speeddial":
+            self.speedDialMsgLabel.setText(msg)
+    
+    def __updateLabels(self, type_, status, download):
+        """
+        Private slot to handle a finished synchronization event.
+        
+        @param type_ type of the synchronization event (string one
+            of "bookmarks", "history", "passwords", "useragents" or
+            "speeddial")
+        @param status flag indicating success (boolean)
+        @param download flag indicating a download of a file (boolean)
+        """
+        if type_ == "bookmarks":
+            if status:
+                self.bookmarkLabel.setPixmap(
+                    UI.PixmapCache.getPixmap("syncCompleted.png"))
+            else:
+                self.bookmarkLabel.setPixmap(
+                    UI.PixmapCache.getPixmap("syncFailed.png"))
+        elif type_ == "history":
+            if status:
+                self.historyLabel.setPixmap(
+                    UI.PixmapCache.getPixmap("syncCompleted.png"))
+            else:
+                self.historyLabel.setPixmap(
+                    UI.PixmapCache.getPixmap("syncFailed.png"))
+        elif type_ == "passwords":
+            if status:
+                self.passwordsLabel.setPixmap(
+                    UI.PixmapCache.getPixmap("syncCompleted.png"))
+            else:
+                self.passwordsLabel.setPixmap(
+                    UI.PixmapCache.getPixmap("syncFailed.png"))
+        elif type_ == "useragents":
+            if status:
+                self.userAgentsLabel.setPixmap(
+                    UI.PixmapCache.getPixmap("syncCompleted.png"))
+            else:
+                self.userAgentsLabel.setPixmap(
+                    UI.PixmapCache.getPixmap("syncFailed.png"))
+        elif type_ == "speeddial":
+            if status:
+                self.speedDialLabel.setPixmap(
+                    UI.PixmapCache.getPixmap("syncCompleted.png"))
+            else:
+                self.speedDialLabel.setPixmap(
+                    UI.PixmapCache.getPixmap("syncFailed.png"))
+    
+    def __syncError(self, message):
+        """
+        Private slot to handle general synchronization issues.
+        
+        @param message error message (string)
+        """
+        self.syncErrorLabel.show()
+        self.syncErrorLabel.setText(self.tr(
+            '<font color="#FF0000"><b>Error:</b> {0}</font>').format(message))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Sync/SyncCheckPage.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,217 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>SyncCheckPage</class>
+ <widget class="QWizardPage" name="SyncCheckPage">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>650</width>
+    <height>400</height>
+   </rect>
+  </property>
+  <property name="title">
+   <string>Synchronization status</string>
+  </property>
+  <property name="subTitle">
+   <string>This page shows the status of the current synchronization process.</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QGroupBox" name="groupBox">
+     <property name="title">
+      <string>Synchronization Data</string>
+     </property>
+     <layout class="QGridLayout" name="gridLayout">
+      <item row="0" column="0">
+       <widget class="QLabel" name="label">
+        <property name="text">
+         <string>Sync Handler:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1">
+       <widget class="QLabel" name="handlerLabel">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="text">
+         <string notr="true">handler</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="0">
+       <widget class="QLabel" name="infoLabel">
+        <property name="text">
+         <string notr="true">Host:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="1">
+       <widget class="QLabel" name="infoDataLabel">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="text">
+         <string notr="true">host</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="groupBox_2">
+     <property name="title">
+      <string>Synchronization Status</string>
+     </property>
+     <layout class="QGridLayout" name="gridLayout_2">
+      <item row="0" column="0">
+       <widget class="QLabel" name="label_3">
+        <property name="text">
+         <string>Bookmarks:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1">
+       <widget class="QLabel" name="bookmarkLabel"/>
+      </item>
+      <item row="0" column="2" colspan="2">
+       <widget class="QLabel" name="bookmarkMsgLabel">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="wordWrap">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="0">
+       <widget class="QLabel" name="label_4">
+        <property name="text">
+         <string>History:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="1">
+       <widget class="QLabel" name="historyLabel"/>
+      </item>
+      <item row="1" column="2" colspan="2">
+       <widget class="QLabel" name="historyMsgLabel">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="wordWrap">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="0">
+       <widget class="QLabel" name="label_5">
+        <property name="text">
+         <string>Passwords:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="1">
+       <widget class="QLabel" name="passwordsLabel"/>
+      </item>
+      <item row="2" column="2" colspan="2">
+       <widget class="QLabel" name="passwordsMsgLabel">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="wordWrap">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item row="3" column="0">
+       <widget class="QLabel" name="label_6">
+        <property name="text">
+         <string>User Agent Settings:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="3" column="1">
+       <widget class="QLabel" name="userAgentsLabel"/>
+      </item>
+      <item row="3" column="2" colspan="2">
+       <widget class="QLabel" name="userAgentsMsgLabel">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="wordWrap">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item row="4" column="0">
+       <widget class="QLabel" name="label_7">
+        <property name="text">
+         <string>Speed Dial Settings:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="4" column="1" colspan="2">
+       <widget class="QLabel" name="speedDialLabel"/>
+      </item>
+      <item row="4" column="3">
+       <widget class="QLabel" name="speedDialMsgLabel">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="wordWrap">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item row="5" column="0" colspan="4">
+       <widget class="QLabel" name="syncErrorLabel">
+        <property name="wordWrap">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <spacer name="verticalSpacer">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>81</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Sync/SyncDataPage.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,71 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the synchronization data wizard page.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtWidgets import QWizardPage
+
+from .Ui_SyncDataPage import Ui_SyncDataPage
+
+import Preferences
+
+
+class SyncDataPage(QWizardPage, Ui_SyncDataPage):
+    """
+    Class implementing the synchronization data wizard page.
+    """
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(SyncDataPage, self).__init__(parent)
+        self.setupUi(self)
+        
+        self.bookmarksCheckBox.setChecked(
+            Preferences.getWebBrowser("SyncBookmarks"))
+        self.historyCheckBox.setChecked(
+            Preferences.getWebBrowser("SyncHistory"))
+        self.passwordsCheckBox.setChecked(
+            Preferences.getWebBrowser("SyncPasswords"))
+        self.userAgentsCheckBox.setChecked(
+            Preferences.getWebBrowser("SyncUserAgents"))
+        self.speedDialCheckBox.setChecked(
+            Preferences.getWebBrowser("SyncSpeedDial"))
+        
+        self.activeCheckBox.setChecked(
+            Preferences.getWebBrowser("SyncEnabled"))
+    
+    def nextId(self):
+        """
+        Public method returning the ID of the next wizard page.
+        
+        @return next wizard page ID (integer)
+        """
+        # save the settings
+        Preferences.setWebBrowser(
+            "SyncEnabled", self.activeCheckBox.isChecked())
+        
+        Preferences.setWebBrowser(
+            "SyncBookmarks", self.bookmarksCheckBox.isChecked())
+        Preferences.setWebBrowser(
+            "SyncHistory", self.historyCheckBox.isChecked())
+        Preferences.setWebBrowser(
+            "SyncPasswords", self.passwordsCheckBox.isChecked())
+        Preferences.setWebBrowser(
+            "SyncUserAgents", self.userAgentsCheckBox.isChecked())
+        Preferences.setWebBrowser(
+            "SyncSpeedDial", self.speedDialCheckBox.isChecked())
+        
+        from . import SyncGlobals
+        if self.activeCheckBox.isChecked():
+            return SyncGlobals.PageEncryption
+        else:
+            return SyncGlobals.PageCheck
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Sync/SyncDataPage.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>SyncDataPage</class>
+ <widget class="QWizardPage" name="SyncDataPage">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>650</width>
+    <height>400</height>
+   </rect>
+  </property>
+  <property name="title">
+   <string>Basic synchronization settings</string>
+  </property>
+  <property name="subTitle">
+   <string>Please select, if synchronization should be enabled and which data should be synchronized.</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout_2">
+   <item>
+    <widget class="QCheckBox" name="activeCheckBox">
+     <property name="toolTip">
+      <string>Select to activate data synchronization</string>
+     </property>
+     <property name="text">
+      <string>Activate synchronization</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="syncDataBox">
+     <property name="enabled">
+      <bool>false</bool>
+     </property>
+     <property name="title">
+      <string>Data to be synchronized</string>
+     </property>
+     <layout class="QVBoxLayout" name="verticalLayout">
+      <item>
+       <widget class="QCheckBox" name="bookmarksCheckBox">
+        <property name="toolTip">
+         <string>Select to synchronize bookmarks</string>
+        </property>
+        <property name="text">
+         <string>Bookmarks</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QCheckBox" name="historyCheckBox">
+        <property name="toolTip">
+         <string>Select to synchronize history</string>
+        </property>
+        <property name="text">
+         <string>History</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QCheckBox" name="passwordsCheckBox">
+        <property name="toolTip">
+         <string>Select to synchronize passwords</string>
+        </property>
+        <property name="text">
+         <string>Passwords</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QCheckBox" name="userAgentsCheckBox">
+        <property name="toolTip">
+         <string>Select to synchronize user agent settings</string>
+        </property>
+        <property name="text">
+         <string>User Agent Settings</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QCheckBox" name="speedDialCheckBox">
+        <property name="toolTip">
+         <string>Select to synchronize the speed dial data</string>
+        </property>
+        <property name="text">
+         <string>Speed Dial Settings</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <spacer name="verticalSpacer">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>150</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>activeCheckBox</sender>
+   <signal>toggled(bool)</signal>
+   <receiver>syncDataBox</receiver>
+   <slot>setEnabled(bool)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>63</x>
+     <y>15</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>63</x>
+     <y>42</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Sync/SyncDirectorySettingsPage.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,58 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the synchronization shared directory settings wizard page.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtWidgets import QWizardPage
+
+from E5Gui.E5PathPicker import E5PathPickerModes
+
+from .Ui_SyncDirectorySettingsPage import Ui_SyncDirectorySettingsPage
+
+import Preferences
+
+
+class SyncDirectorySettingsPage(QWizardPage, Ui_SyncDirectorySettingsPage):
+    """
+    Class implementing the shared directory host settings wizard page.
+    """
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(SyncDirectorySettingsPage, self).__init__(parent)
+        self.setupUi(self)
+        
+        self.directoryPicker.setMode(E5PathPickerModes.DirectoryMode)
+        self.directoryPicker.setText(
+            Preferences.getWebBrowser("SyncDirectoryPath"))
+        
+        self.directoryPicker.textChanged.connect(self.completeChanged)
+    
+    def nextId(self):
+        """
+        Public method returning the ID of the next wizard page.
+        
+        @return next wizard page ID (integer)
+        """
+        # save the settings
+        Preferences.setWebBrowser("SyncDirectoryPath", self.directoryPicker.text())
+        
+        from . import SyncGlobals
+        return SyncGlobals.PageCheck
+    
+    def isComplete(self):
+        """
+        Public method to check the completeness of the page.
+        
+        @return flag indicating completeness (boolean)
+        """
+        return self.directoryPicker.text() != ""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Sync/SyncDirectorySettingsPage.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>SyncDirectorySettingsPage</class>
+ <widget class="QWizardPage" name="SyncDirectorySettingsPage">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>650</width>
+    <height>400</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string/>
+  </property>
+  <property name="title">
+   <string>Synchronize to a shared directory</string>
+  </property>
+  <property name="subTitle">
+   <string>Please enter the data for synchronization via a shared directory. All fields must be filled.</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QGroupBox" name="groupBox">
+     <property name="title">
+      <string>Shared Directory Settings</string>
+     </property>
+     <layout class="QHBoxLayout" name="horizontalLayout">
+      <item>
+       <widget class="QLabel" name="label">
+        <property name="text">
+         <string>Directory Name:</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="E5PathPicker" name="directoryPicker" native="true">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="focusPolicy">
+         <enum>Qt::StrongFocus</enum>
+        </property>
+        <property name="toolTip">
+         <string>Enter the full path of the shared directory</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <spacer name="verticalSpacer">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>317</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>E5PathPicker</class>
+   <extends>QWidget</extends>
+   <header>E5Gui/E5PathPicker.h</header>
+   <container>1</container>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Sync/SyncEncryptionPage.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,147 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing encryption settings wizard page.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSlot
+from PyQt5.QtWidgets import QWizardPage
+
+from .Ui_SyncEncryptionPage import Ui_SyncEncryptionPage
+
+import Preferences
+
+
+class SyncEncryptionPage(QWizardPage, Ui_SyncEncryptionPage):
+    """
+    Class implementing encryption settings wizard page.
+    """
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(SyncEncryptionPage, self).__init__(parent)
+        self.setupUi(self)
+        
+        self.keySizeComboBox.addItem(self.tr("128 Bits"), 16)
+        self.keySizeComboBox.addItem(self.tr("192 Bits"), 24)
+        self.keySizeComboBox.addItem(self.tr("256 Bits"), 32)
+        
+        self.registerField("ReencryptData", self.reencryptCheckBox)
+        
+        self.encryptionGroupBox.setChecked(
+            Preferences.getWebBrowser("SyncEncryptData"))
+        self.encryptionKeyEdit.setText(
+            Preferences.getWebBrowser("SyncEncryptionKey"))
+        self.encryptionKeyAgainEdit.setEnabled(False)
+        self.keySizeComboBox.setCurrentIndex(self.keySizeComboBox.findData(
+            Preferences.getWebBrowser("SyncEncryptionKeyLength")))
+        self.loginsOnlyCheckBox.setChecked(
+            Preferences.getWebBrowser("SyncEncryptPasswordsOnly"))
+    
+    def nextId(self):
+        """
+        Public method returning the ID of the next wizard page.
+        
+        @return next wizard page ID (integer)
+        """
+        Preferences.setWebBrowser(
+            "SyncEncryptData", self.encryptionGroupBox.isChecked())
+        Preferences.setWebBrowser(
+            "SyncEncryptionKey", self.encryptionKeyEdit.text())
+        Preferences.setWebBrowser(
+            "SyncEncryptionKeyLength", self.keySizeComboBox.itemData(
+                self.keySizeComboBox.currentIndex()))
+        Preferences.setWebBrowser(
+            "SyncEncryptPasswordsOnly", self.loginsOnlyCheckBox.isChecked())
+        
+        from . import SyncGlobals
+        return SyncGlobals.PageType
+    
+    def isComplete(self):
+        """
+        Public method to check the completeness of the page.
+        
+        @return flag indicating completeness (boolean)
+        """
+        if self.encryptionGroupBox.isChecked():
+            if self.encryptionKeyEdit.text() == "":
+                complete = False
+            else:
+                if self.reencryptCheckBox.isChecked():
+                    complete = (self.encryptionKeyEdit.text() ==
+                                self.encryptionKeyAgainEdit.text())
+                else:
+                    complete = True
+        else:
+            complete = True
+        
+        return complete
+    
+    def __updateUI(self):
+        """
+        Private slot to update the variable parts of the UI.
+        """
+        error = ""
+        
+        if self.encryptionGroupBox.isChecked():
+            self.encryptionKeyAgainEdit.setEnabled(
+                self.reencryptCheckBox.isChecked())
+            
+            if self.encryptionKeyEdit.text() == "":
+                error = error or self.tr(
+                    "Encryption key must not be empty.")
+            
+            if self.encryptionKeyEdit.text() != "" and \
+                    self.reencryptCheckBox.isChecked() and \
+                    (self.encryptionKeyEdit.text() !=
+                     self.encryptionKeyAgainEdit.text()):
+                error = error or self.tr(
+                    "Repeated encryption key is wrong.")
+        
+        self.errorLabel.setText(error)
+        self.completeChanged.emit()
+    
+    @pyqtSlot(str)
+    def on_encryptionKeyEdit_textChanged(self, txt):
+        """
+        Private slot to handle changes of the encryption key.
+        
+        @param txt content of the edit widget (string)
+        """
+        self.passwordMeter.checkPasswordStrength(txt)
+        self.__updateUI()
+    
+    @pyqtSlot(str)
+    def on_encryptionKeyAgainEdit_textChanged(self, txt):
+        """
+        Private slot to handle changes of the encryption key repetition.
+        
+        @param txt content of the edit widget (string)
+        """
+        self.__updateUI()
+    
+    @pyqtSlot(bool)
+    def on_encryptionGroupBox_toggled(self, on):
+        """
+        Private slot to handle changes of the encryption selection.
+        
+        @param on state of the group box (boolean)
+        """
+        self.__updateUI()
+    
+    @pyqtSlot(bool)
+    def on_reencryptCheckBox_toggled(self, on):
+        """
+        Private slot to handle changes of the re-encryption selection.
+        
+        @param on state of the check box (boolean)
+        """
+        self.__updateUI()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Sync/SyncEncryptionPage.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,179 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>SyncEncryptionPage</class>
+ <widget class="QWizardPage" name="SyncEncryptionPage">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>650</width>
+    <height>400</height>
+   </rect>
+  </property>
+  <property name="title">
+   <string>Encryption Settings</string>
+  </property>
+  <property name="subTitle">
+   <string>Please select, if the synchronized data should be encrypted and enter the encryption key</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QGroupBox" name="encryptionGroupBox">
+     <property name="toolTip">
+      <string>Select to encrypt the synchronzed data</string>
+     </property>
+     <property name="title">
+      <string>Encrypt Data</string>
+     </property>
+     <property name="checkable">
+      <bool>true</bool>
+     </property>
+     <layout class="QGridLayout" name="gridLayout">
+      <item row="0" column="0" colspan="2">
+       <widget class="QLabel" name="label_3">
+        <property name="text">
+         <string>&lt;p&gt;The encryption key will be used to encrypt and decrypt the synchronizde data. If the data should be re-encrypted, the respective selection should be done. The key must only be repeated, if a re-encryption is requested.&lt;br/&gt;&lt;br/&gt;&lt;b&gt;Note: If you forget the encryption key, the encrypted data cannot be recovered!&lt;/b&gt;&lt;br/&gt;&lt;/p&gt;</string>
+        </property>
+        <property name="wordWrap">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="0" colspan="2">
+       <widget class="QCheckBox" name="reencryptCheckBox">
+        <property name="toolTip">
+         <string>Select to re-encrypt the synchronized data</string>
+        </property>
+        <property name="text">
+         <string>Re-encrypt synchronized data</string>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="0">
+       <widget class="QLabel" name="label">
+        <property name="text">
+         <string>Encryption Key:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="1">
+       <widget class="QLineEdit" name="encryptionKeyEdit">
+        <property name="toolTip">
+         <string>Enter the encryption key</string>
+        </property>
+        <property name="echoMode">
+         <enum>QLineEdit::Password</enum>
+        </property>
+       </widget>
+      </item>
+      <item row="3" column="0">
+       <widget class="QLabel" name="label_2">
+        <property name="text">
+         <string>Encryption Key (again):</string>
+        </property>
+       </widget>
+      </item>
+      <item row="3" column="1">
+       <widget class="QLineEdit" name="encryptionKeyAgainEdit">
+        <property name="toolTip">
+         <string>Repeat the encryption key</string>
+        </property>
+        <property name="echoMode">
+         <enum>QLineEdit::Password</enum>
+        </property>
+       </widget>
+      </item>
+      <item row="4" column="0" colspan="2">
+       <widget class="E5PasswordMeter" name="passwordMeter">
+        <property name="toolTip">
+         <string>Shows an indication for the encryption key strength</string>
+        </property>
+       </widget>
+      </item>
+      <item row="5" column="0" colspan="2">
+       <layout class="QHBoxLayout" name="horizontalLayout">
+        <item>
+         <widget class="QLabel" name="label_4">
+          <property name="text">
+           <string>Size of generated encryption key:</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QComboBox" name="keySizeComboBox">
+          <property name="toolTip">
+           <string>Select the size of the generated encryption key</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <spacer name="horizontalSpacer">
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>40</width>
+            <height>20</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+       </layout>
+      </item>
+      <item row="6" column="0" colspan="2">
+       <widget class="QLabel" name="errorLabel">
+        <property name="styleSheet">
+         <string notr="true">color : red;</string>
+        </property>
+        <property name="wordWrap">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item row="7" column="0" colspan="2">
+       <widget class="QCheckBox" name="loginsOnlyCheckBox">
+        <property name="toolTip">
+         <string>Select to encrypt only the passwords</string>
+        </property>
+        <property name="text">
+         <string>Encrypt Passwords Only</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <spacer name="verticalSpacer">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>191</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>E5PasswordMeter</class>
+   <extends>QProgressBar</extends>
+   <header>E5Gui/E5PasswordMeter</header>
+  </customwidget>
+ </customwidgets>
+ <tabstops>
+  <tabstop>encryptionGroupBox</tabstop>
+  <tabstop>reencryptCheckBox</tabstop>
+  <tabstop>encryptionKeyEdit</tabstop>
+  <tabstop>encryptionKeyAgainEdit</tabstop>
+  <tabstop>keySizeComboBox</tabstop>
+  <tabstop>loginsOnlyCheckBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Sync/SyncFtpSettingsPage.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,71 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the synchronization FTP host settings wizard page.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtWidgets import QWizardPage
+
+from .Ui_SyncFtpSettingsPage import Ui_SyncFtpSettingsPage
+
+import Preferences
+
+
+class SyncFtpSettingsPage(QWizardPage, Ui_SyncFtpSettingsPage):
+    """
+    Class implementing the synchronization FTP host settings wizard page.
+    """
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(SyncFtpSettingsPage, self).__init__(parent)
+        self.setupUi(self)
+        
+        self.serverEdit.setText(Preferences.getWebBrowser("SyncFtpServer"))
+        self.userNameEdit.setText(Preferences.getWebBrowser("SyncFtpUser"))
+        self.passwordEdit.setText(Preferences.getWebBrowser("SyncFtpPassword"))
+        self.pathEdit.setText(Preferences.getWebBrowser("SyncFtpPath"))
+        self.portSpinBox.setValue(Preferences.getWebBrowser("SyncFtpPort"))
+        self.idleSpinBox.setValue(
+            Preferences.getWebBrowser("SyncFtpIdleTimeout"))
+        
+        self.serverEdit.textChanged.connect(self.completeChanged)
+        self.userNameEdit.textChanged.connect(self.completeChanged)
+        self.passwordEdit.textChanged.connect(self.completeChanged)
+        self.pathEdit.textChanged.connect(self.completeChanged)
+    
+    def nextId(self):
+        """
+        Public method returning the ID of the next wizard page.
+        
+        @return next wizard page ID (integer)
+        """
+        # save the settings
+        Preferences.setWebBrowser("SyncFtpServer", self.serverEdit.text())
+        Preferences.setWebBrowser("SyncFtpUser", self.userNameEdit.text())
+        Preferences.setWebBrowser("SyncFtpPassword", self.passwordEdit.text())
+        Preferences.setWebBrowser("SyncFtpPath", self.pathEdit.text())
+        Preferences.setWebBrowser("SyncFtpPort", self.portSpinBox.value())
+        Preferences.setWebBrowser("SyncFtpIdleTimeout", self.idleSpinBox.value())
+        
+        from . import SyncGlobals
+        return SyncGlobals.PageCheck
+    
+    def isComplete(self):
+        """
+        Public method to check the completeness of the page.
+        
+        @return flag indicating completeness (boolean)
+        """
+        return self.serverEdit.text() != "" and \
+            self.userNameEdit.text() != "" and \
+            self.passwordEdit.text() != "" and \
+            self.pathEdit.text() != ""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Sync/SyncFtpSettingsPage.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,183 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>SyncFtpSettingsPage</class>
+ <widget class="QWizardPage" name="SyncFtpSettingsPage">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>650</width>
+    <height>400</height>
+   </rect>
+  </property>
+  <property name="title">
+   <string>Synchronize to an FTP host</string>
+  </property>
+  <property name="subTitle">
+   <string>Please enter the data for synchronization via FTP. All fields must be filled.</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QGroupBox" name="groupBox">
+     <property name="title">
+      <string>Remote FTP Host Settings</string>
+     </property>
+     <layout class="QGridLayout" name="gridLayout">
+      <item row="0" column="0">
+       <widget class="QLabel" name="label">
+        <property name="text">
+         <string>Server:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1" colspan="2">
+       <widget class="QLineEdit" name="serverEdit">
+        <property name="toolTip">
+         <string>Enter the FTP server name</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="0">
+       <widget class="QLabel" name="label_2">
+        <property name="text">
+         <string>User Name:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="1" colspan="2">
+       <widget class="QLineEdit" name="userNameEdit">
+        <property name="toolTip">
+         <string>Enter the user name</string>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="0">
+       <widget class="QLabel" name="label_3">
+        <property name="text">
+         <string>Password:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="1" colspan="2">
+       <widget class="QLineEdit" name="passwordEdit">
+        <property name="toolTip">
+         <string>Enter the password</string>
+        </property>
+        <property name="echoMode">
+         <enum>QLineEdit::Password</enum>
+        </property>
+       </widget>
+      </item>
+      <item row="3" column="0">
+       <widget class="QLabel" name="label_4">
+        <property name="text">
+         <string>Path:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="3" column="1" colspan="2">
+       <widget class="QLineEdit" name="pathEdit">
+        <property name="toolTip">
+         <string>Enter the remote path</string>
+        </property>
+       </widget>
+      </item>
+      <item row="4" column="0">
+       <widget class="QLabel" name="label_5">
+        <property name="text">
+         <string>Port:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="4" column="1">
+       <widget class="QSpinBox" name="portSpinBox">
+        <property name="toolTip">
+         <string>Enter the remote port</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+        <property name="minimum">
+         <number>1</number>
+        </property>
+        <property name="maximum">
+         <number>65635</number>
+        </property>
+        <property name="value">
+         <number>21</number>
+        </property>
+       </widget>
+      </item>
+      <item row="4" column="2">
+       <spacer name="horizontalSpacer">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>218</width>
+          <height>20</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+      <item row="5" column="0">
+       <widget class="QLabel" name="label_6">
+        <property name="text">
+         <string>Idle Timeout:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="5" column="1">
+       <widget class="QSpinBox" name="idleSpinBox">
+        <property name="toolTip">
+         <string>Enter the idle timeout interval to prevent a server disconnect</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+        <property name="suffix">
+         <string> s</string>
+        </property>
+        <property name="minimum">
+         <number>10</number>
+        </property>
+        <property name="maximum">
+         <number>3600</number>
+        </property>
+       </widget>
+      </item>
+      <item row="5" column="2">
+       <spacer name="horizontalSpacer_2">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>419</width>
+          <height>20</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <spacer name="verticalSpacer">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>101</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Sync/SyncGlobals.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing some global definitions.
+"""
+
+# Page IDs for the sync wizard
+PageData = 0
+PageEncryption = 1
+PageType = 2
+PageFTPSettings = 3
+PageDirectorySettings = 4
+PageCheck = 5
+
+# Sync types
+SyncTypeNone = -1
+SyncTypeFtp = 0
+SyncTypeDirectory = 1
+
+#
+# eflag: noqa = M702
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Sync/SyncHandler.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,279 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module containing a base class for synchronization handlers.
+"""
+
+from __future__ import unicode_literals
+
+import os
+
+from PyQt5.QtCore import QObject, pyqtSignal, QByteArray
+
+import Preferences
+
+from Utilities.crypto import dataEncrypt, dataDecrypt
+
+
+class SyncHandler(QObject):
+    """
+    Base class for synchronization handlers.
+    
+    @signal syncStatus(type_, message) emitted to indicate the synchronization
+        status (string one of "bookmarks", "history", "passwords",
+        "useragents" or "speeddial", string)
+    @signal syncError(message) emitted for a general error with the error
+        message (string)
+    @signal syncMessage(message) emitted to send a message about
+        synchronization (string)
+    @signal syncFinished(type_, done, download) emitted after a
+        synchronization has finished (string one of "bookmarks", "history",
+        "passwords", "useragents" or "speeddial", boolean, boolean)
+    """
+    syncStatus = pyqtSignal(str, str)
+    syncError = pyqtSignal(str)
+    syncMessage = pyqtSignal(str)
+    syncFinished = pyqtSignal(str, bool, bool)
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object (QObject)
+        """
+        super(SyncHandler, self).__init__(parent)
+        
+        self._firstTimeSynced = False
+        
+        self._remoteFiles = {
+            "bookmarks": "Bookmarks",
+            "history": "History",
+            "passwords": "Logins",
+            "useragents": "UserAgentSettings",
+            "speeddial": "SpeedDial",
+        }
+        
+        self._messages = {
+            "bookmarks": {
+                "RemoteExists": self.tr(
+                    "Remote bookmarks file exists! Syncing local copy..."),
+                "RemoteMissing": self.tr(
+                    "Remote bookmarks file does NOT exist. Exporting"
+                    " local copy..."),
+                "LocalNewer": self.tr(
+                    "Local bookmarks file is NEWER. Exporting local copy..."),
+                "LocalMissing": self.tr(
+                    "Local bookmarks file does NOT exist. Skipping"
+                    " synchronization!"),
+                "Uploading": self.tr("Uploading local bookmarks file..."),
+            },
+            "history": {
+                "RemoteExists": self.tr(
+                    "Remote history file exists! Syncing local copy..."),
+                "RemoteMissing": self.tr(
+                    "Remote history file does NOT exist. Exporting"
+                    " local copy..."),
+                "LocalNewer": self.tr(
+                    "Local history file is NEWER. Exporting local copy..."),
+                "LocalMissing": self.tr(
+                    "Local history file does NOT exist. Skipping"
+                    " synchronization!"),
+                "Uploading": self.tr("Uploading local history file..."),
+            },
+            "passwords": {
+                "RemoteExists": self.tr(
+                    "Remote logins file exists! Syncing local copy..."),
+                "RemoteMissing": self.tr(
+                    "Remote logins file does NOT exist. Exporting"
+                    " local copy..."),
+                "LocalNewer": self.tr(
+                    "Local logins file is NEWER. Exporting local copy..."),
+                "LocalMissing": self.tr(
+                    "Local logins file does NOT exist. Skipping"
+                    " synchronization!"),
+                "Uploading": self.tr("Uploading local logins file..."),
+            },
+            "useragents": {
+                "RemoteExists": self.tr(
+                    "Remote user agent settings file exists! Syncing local"
+                    " copy..."),
+                "RemoteMissing": self.tr(
+                    "Remote user agent settings file does NOT exist."
+                    " Exporting local copy..."),
+                "LocalNewer": self.tr(
+                    "Local user agent settings file is NEWER. Exporting"
+                    " local copy..."),
+                "LocalMissing": self.tr(
+                    "Local user agent settings file does NOT exist."
+                    " Skipping synchronization!"),
+                "Uploading": self.tr(
+                    "Uploading local user agent settings file..."),
+            },
+            "speeddial": {
+                "RemoteExists": self.tr(
+                    "Remote speed dial settings file exists! Syncing local"
+                    " copy..."),
+                "RemoteMissing": self.tr(
+                    "Remote speed dial settings file does NOT exist."
+                    " Exporting local copy..."),
+                "LocalNewer": self.tr(
+                    "Local speed dial settings file is NEWER. Exporting"
+                    " local copy..."),
+                "LocalMissing": self.tr(
+                    "Local speed dial settings file does NOT exist."
+                    " Skipping synchronization!"),
+                "Uploading": self.tr(
+                    "Uploading local speed dial settings file..."),
+            },
+        }
+    
+    def syncBookmarks(self):
+        """
+        Public method to synchronize the bookmarks.
+        
+        @exception NotImplementedError raised to indicate that this method
+            must be implemented by subclasses
+        """
+        raise NotImplementedError
+    
+    def syncHistory(self):
+        """
+        Public method to synchronize the history.
+        
+        @exception NotImplementedError raised to indicate that this method
+            must be implemented by subclasses
+        """
+        raise NotImplementedError
+    
+    def syncPasswords(self):
+        """
+        Public method to synchronize the passwords.
+        
+        @exception NotImplementedError raised to indicate that this method
+            must be implemented by subclasses
+        """
+        raise NotImplementedError
+    
+    def syncUserAgents(self):
+        """
+        Public method to synchronize the user agents.
+        
+        @exception NotImplementedError raised to indicate that this method
+            must be implemented by subclasses
+        """
+        raise NotImplementedError
+    
+    def syncSpeedDial(self):
+        """
+        Public method to synchronize the speed dial data.
+        
+        @exception NotImplementedError raised to indicate that this method
+            must be implemented by subclasses
+        """
+        raise NotImplementedError
+    
+    def initialLoadAndCheck(self, forceUpload):
+        """
+        Public method to do the initial check.
+        
+        @keyparam forceUpload flag indicating a forced upload of the files
+            (boolean)
+        @exception NotImplementedError raised to indicate that this method
+            must be implemented by subclasses
+        """
+        raise NotImplementedError
+    
+    def shutdown(self):
+        """
+        Public method to shut down the handler.
+        
+        @exception NotImplementedError raised to indicate that this method
+            must be implemented by subclasses
+        """
+        raise NotImplementedError
+    
+    def readFile(self, fileName, type_):
+        """
+        Public method to read a file.
+        
+        If encrypted synchronization is enabled, the data will be encrypted
+        using the relevant encryption key.
+        
+        @param fileName name of the file to be read (string)
+        @param type_ type of the synchronization event (string one
+            of "bookmarks", "history", "passwords", "useragents" or
+            "speeddial")
+        @return data of the file, optionally encrypted (QByteArray)
+        """
+        if os.path.exists(fileName):
+            try:
+                inputFile = open(fileName, "rb")
+                data = inputFile.read()
+                inputFile.close()
+            except IOError:
+                return QByteArray()
+            
+            if Preferences.getWebBrowser("SyncEncryptData") and \
+               (not Preferences.getWebBrowser("SyncEncryptPasswordsOnly") or
+                (Preferences.getWebBrowser("SyncEncryptPasswordsOnly") and
+                 type_ == "passwords")):
+                key = Preferences.getWebBrowser("SyncEncryptionKey")
+                if not key:
+                    return QByteArray()
+                
+                data, ok = dataEncrypt(
+                    data, key,
+                    keyLength=Preferences.getWebBrowser(
+                        "SyncEncryptionKeyLength"),
+                    hashIterations=100)
+                if not ok:
+                    return QByteArray()
+            
+            return QByteArray(data)
+        
+        return QByteArray()
+    
+    def writeFile(self, data, fileName, type_, timestamp=0):
+        """
+        Public method to write the data to a file.
+        
+        If encrypted synchronization is enabled, the data will be decrypted
+        using the relevant encryption key.
+        
+        @param data data to be written and optionally decrypted (QByteArray)
+        @param fileName name of the file the data is to be written to (string)
+        @param type_ type of the synchronization event (string one
+            of "bookmarks", "history", "passwords", "useragents" or
+            "speeddial")
+        @param timestamp timestamp to be given to the file (int)
+        @return tuple giving a success flag and an error string (boolean,
+            string)
+        """
+        data = bytes(data)
+        
+        if Preferences.getWebBrowser("SyncEncryptData") and \
+                (not Preferences.getWebBrowser("SyncEncryptPasswordsOnly") or
+                 (Preferences.getWebBrowser("SyncEncryptPasswordsOnly") and
+                  type_ == "passwords")):
+            key = Preferences.getWebBrowser("SyncEncryptionKey")
+            if not key:
+                return False, self.tr("Invalid encryption key given.")
+            
+            data, ok = dataDecrypt(
+                data, key,
+                keyLength=Preferences.getWebBrowser("SyncEncryptionKeyLength"))
+            if not ok:
+                return False, self.tr("Data cannot be decrypted.")
+        
+        try:
+            outputFile = open(fileName, "wb")
+            outputFile.write(data)
+            outputFile.close()
+            if timestamp > 0:
+                os.utime(fileName, (timestamp, timestamp))
+            return True, ""
+        except IOError as error:
+            return False, str(error)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Sync/SyncHostTypePage.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,58 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the synchronization host type wizard page.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtWidgets import QWizardPage
+
+from . import SyncGlobals
+
+from .Ui_SyncHostTypePage import Ui_SyncHostTypePage
+
+import Preferences
+
+
+class SyncHostTypePage(QWizardPage, Ui_SyncHostTypePage):
+    """
+    Class implementing the synchronization host type wizard page.
+    """
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(SyncHostTypePage, self).__init__(parent)
+        self.setupUi(self)
+        
+        if Preferences.getWebBrowser("SyncType") == SyncGlobals.SyncTypeFtp:
+            self.ftpRadioButton.setChecked(True)
+        elif Preferences.getWebBrowser("SyncType") == \
+                SyncGlobals.SyncTypeDirectory:
+            self.directoryRadioButton.setChecked(True)
+        else:
+            self.noneRadioButton.setChecked(True)
+    
+    def nextId(self):
+        """
+        Public method returning the ID of the next wizard page.
+        
+        @return next wizard page ID (integer)
+        """
+        # save the settings
+        if self.ftpRadioButton.isChecked():
+            Preferences.setWebBrowser("SyncType", SyncGlobals.SyncTypeFtp)
+            return SyncGlobals.PageFTPSettings
+        elif self.directoryRadioButton.isChecked():
+            Preferences.setWebBrowser(
+                "SyncType", SyncGlobals.SyncTypeDirectory)
+            return SyncGlobals.PageDirectorySettings
+        else:
+            Preferences.setWebBrowser("SyncType", SyncGlobals.SyncTypeNone)
+            return SyncGlobals.PageCheck
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Sync/SyncHostTypePage.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>SyncHostTypePage</class>
+ <widget class="QWizardPage" name="SyncHostTypePage">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>650</width>
+    <height>400</height>
+   </rect>
+  </property>
+  <property name="title">
+   <string>Host Type Selection</string>
+  </property>
+  <property name="subTitle">
+   <string>Please select the type of the host to be used for synchronization.</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout_2">
+   <item>
+    <widget class="QGroupBox" name="groupBox">
+     <property name="title">
+      <string>Synchronization Host Type</string>
+     </property>
+     <layout class="QVBoxLayout" name="verticalLayout">
+      <item>
+       <widget class="QRadioButton" name="ftpRadioButton">
+        <property name="toolTip">
+         <string>Select to use a FTP host</string>
+        </property>
+        <property name="text">
+         <string>FTP</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QRadioButton" name="directoryRadioButton">
+        <property name="toolTip">
+         <string>Select to use a shared directory</string>
+        </property>
+        <property name="text">
+         <string>Shared Directory</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QRadioButton" name="noneRadioButton">
+        <property name="toolTip">
+         <string>Select to use no particular host type</string>
+        </property>
+        <property name="text">
+         <string>None</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <spacer name="verticalSpacer">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>191</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+  </layout>
+ </widget>
+ <tabstops>
+  <tabstop>ftpRadioButton</tabstop>
+  <tabstop>directoryRadioButton</tabstop>
+  <tabstop>noneRadioButton</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Sync/SyncManager.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,278 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the synchronization manager class.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import QObject, pyqtSignal
+
+import Preferences
+
+from WebBrowser.WebBrowserWindow import WebBrowserWindow
+
+
+class SyncManager(QObject):
+    """
+    Class implementing the synchronization manager.
+    
+    @signal syncError(message) emitted for a general error with the error
+        message (string)
+    @signal syncMessage(message) emitted to give status info about the sync
+        process (string)
+    @signal syncStatus(type_, message) emitted to indicate the synchronization
+        status (string one of "bookmarks", "history", "passwords",
+        "useragents" or "speeddial", string)
+    @signal syncFinished(type_, done, download) emitted after a
+        synchronization has finished (string one of "bookmarks", "history",
+        "passwords", "useragents" or "speeddial", boolean, boolean)
+    """
+    syncError = pyqtSignal(str)
+    syncMessage = pyqtSignal(str)
+    syncStatus = pyqtSignal(str, str)
+    syncFinished = pyqtSignal(str, bool, bool)
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object (QObject)
+        """
+        super(SyncManager, self).__init__(parent)
+        
+        self.__handler = None
+    
+    def handler(self):
+        """
+        Public method to get a reference to the sync handler object.
+        
+        @return reference to the sync handler object (SyncHandler)
+        """
+        return self.__handler
+    
+    def showSyncDialog(self):
+        """
+        Public method to show the synchronization dialog.
+        """
+        from .SyncAssistantDialog import SyncAssistantDialog
+        dlg = SyncAssistantDialog()
+        dlg.exec_()
+    
+    def loadSettings(self, forceUpload=False):
+        """
+        Public method to load the settings.
+        
+        @keyparam forceUpload flag indicating a forced upload of the files
+            (boolean)
+        """
+        if self.__handler is not None:
+            self.__handler.syncError.disconnect(self.__syncError)
+            self.__handler.syncFinished.disconnect(self.__syncFinished)
+            self.__handler.syncStatus.disconnect(self.__syncStatus)
+            self.__handler.syncMessage.disconnect(self.syncMessage)
+            self.__handler.shutdown()
+        
+        if self.syncEnabled():
+            from . import SyncGlobals
+            if Preferences.getWebBrowser("SyncType") == SyncGlobals.SyncTypeFtp:
+                from .FtpSyncHandler import FtpSyncHandler
+                self.__handler = FtpSyncHandler(self)
+            elif Preferences.getWebBrowser("SyncType") == \
+                    SyncGlobals.SyncTypeDirectory:
+                from .DirectorySyncHandler import DirectorySyncHandler
+                self.__handler = DirectorySyncHandler(self)
+            self.__handler.syncError.connect(self.__syncError)
+            self.__handler.syncFinished.connect(self.__syncFinished)
+            self.__handler.syncStatus.connect(self.__syncStatus)
+            self.__handler.syncMessage.connect(self.syncMessage)
+            
+            self.__handler.initialLoadAndCheck(forceUpload=forceUpload)
+            
+            # connect sync manager to bookmarks manager
+            if Preferences.getWebBrowser("SyncBookmarks"):
+                WebBrowserWindow.bookmarksManager()\
+                    .bookmarksSaved.connect(self.__syncBookmarks)
+            else:
+                try:
+                    WebBrowserWindow.bookmarksManager()\
+                        .bookmarksSaved.disconnect(self.__syncBookmarks)
+                except TypeError:
+                    pass
+            
+            # connect sync manager to history manager
+            if Preferences.getWebBrowser("SyncHistory"):
+                WebBrowserWindow.historyManager().historySaved\
+                    .connect(self.__syncHistory)
+            else:
+                try:
+                    WebBrowserWindow.historyManager()\
+                        .historySaved.disconnect(self.__syncHistory)
+                except TypeError:
+                    pass
+            
+            # connect sync manager to passwords manager
+            if Preferences.getWebBrowser("SyncPasswords"):
+                WebBrowserWindow.passwordManager()\
+                    .passwordsSaved.connect(self.__syncPasswords)
+            else:
+                try:
+                    WebBrowserWindow.passwordManager()\
+                        .passwordsSaved.disconnect(self.__syncPasswords)
+                except TypeError:
+                    pass
+            
+            # TODO: UserAgents
+            # connect sync manager to user agent manager
+##            if Preferences.getWebBrowser("SyncUserAgents"):
+##                WebBrowserWindow.userAgentsManager()\
+##                    .userAgentSettingsSaved.connect(self.__syncUserAgents)
+##            else:
+##                try:
+##                    WebBrowserWindow.userAgentsManager()\
+##                        .userAgentSettingsSaved.disconnect(
+##                            self.__syncUserAgents)
+##                except TypeError:
+##                    pass
+            
+            # connect sync manager to speed dial
+            if Preferences.getWebBrowser("SyncSpeedDial"):
+                WebBrowserWindow.speedDial()\
+                    .speedDialSaved.connect(self.__syncSpeedDial)
+            else:
+                try:
+                    WebBrowserWindow.speedDial()\
+                        .speedDialSaved.disconnect(self.__syncSpeedDial)
+                except TypeError:
+                    pass
+        else:
+            self.__handler = None
+            
+            try:
+                WebBrowserWindow.bookmarksManager()\
+                    .bookmarksSaved.disconnect(self.__syncBookmarks)
+            except TypeError:
+                pass
+            try:
+                WebBrowserWindow.historyManager().historySaved\
+                    .disconnect(self.__syncHistory)
+            except TypeError:
+                pass
+            try:
+                WebBrowserWindow.passwordManager()\
+                    .passwordsSaved.disconnect(self.__syncPasswords)
+            except TypeError:
+                pass
+            # TODO: UserAgents
+##            try:
+##                WebBrowserWindow.userAgentsManager()\
+##                    .userAgentSettingsSaved.disconnect(self.__syncUserAgents)
+##            except TypeError:
+##                pass
+            try:
+                WebBrowserWindow.speedDial()\
+                    .speedDialSaved.disconnect(self.__syncSpeedDial)
+            except TypeError:
+                pass
+    
+    def syncEnabled(self):
+        """
+        Public method to check, if synchronization is enabled.
+        
+        @return flag indicating enabled synchronization
+        """
+        from . import SyncGlobals
+        return Preferences.getWebBrowser("SyncEnabled") and \
+            Preferences.getWebBrowser("SyncType") != SyncGlobals.SyncTypeNone
+    
+    def __syncBookmarks(self):
+        """
+        Private slot to synchronize the bookmarks.
+        """
+        if self.__handler is not None:
+            self.__handler.syncBookmarks()
+    
+    def __syncHistory(self):
+        """
+        Private slot to synchronize the history.
+        """
+        if self.__handler is not None:
+            self.__handler.syncHistory()
+    
+    def __syncPasswords(self):
+        """
+        Private slot to synchronize the passwords.
+        """
+        if self.__handler is not None:
+            self.__handler.syncPasswords()
+    
+    def __syncUserAgents(self):
+        """
+        Private slot to synchronize the user agent settings.
+        """
+        if self.__handler is not None:
+            self.__handler.syncUserAgents()
+    
+    def __syncSpeedDial(self):
+        """
+        Private slot to synchronize the speed dial settings.
+        """
+        if self.__handler is not None:
+            self.__handler.syncSpeedDial()
+    
+    def __syncError(self, message):
+        """
+        Private slot to handle general synchronization issues.
+        
+        @param message error message (string)
+        """
+        self.syncError.emit(message)
+    
+    def __syncFinished(self, type_, status, download):
+        """
+        Private slot to handle a finished synchronization event.
+        
+        @param type_ type of the synchronization event (string one
+            of "bookmarks", "history", "passwords", "useragents" or
+            "speeddial")
+        @param status flag indicating success (boolean)
+        @param download flag indicating a download of a file (boolean)
+        """
+        if status and download:
+            if type_ == "bookmarks":
+                WebBrowserWindow.bookmarksManager().reload()
+            elif type_ == "history":
+                WebBrowserWindow.historyManager().reload()
+            elif type_ == "passwords":
+                WebBrowserWindow.passwordManager().reload()
+            # TODO: UserAgents
+##            elif type_ == "useragents":
+##                WebBrowserWindow.userAgentsManager().reload()
+            elif type_ == "speeddial":
+                WebBrowserWindow.speedDial().reload()
+        self.syncFinished.emit(type_, status, download)
+    
+    def __syncStatus(self, type_, message):
+        """
+        Private slot to handle a status update of a synchronization event.
+        
+        @param type_ type of the synchronization event (string one
+            of "bookmarks", "history", "passwords", "useragents" or
+            "speeddial")
+        @param message status message for the event (string)
+        """
+        self.syncMessage.emit(message)
+        self.syncStatus.emit(type_, message)
+    
+    def close(self):
+        """
+        Public slot to shut down the synchronization manager.
+        """
+        if not self.syncEnabled():
+            return
+        
+        if self.__handler is not None:
+            self.__handler.shutdown()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Sync/__init__.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Package implementing capabilities to sync some configuration data.
+"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Tools/DelayedFileWatcher.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,80 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a file system watcher with a delay.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSignal, pyqtSlot, QFileSystemWatcher, QTimer
+
+
+class DelayedFileWatcher(QFileSystemWatcher):
+    """
+    Class implementing a file system watcher with a delay.
+    
+    @signal delayedDirectoryChanged(path) emitted to indicate a changed
+        directory
+    @signal delayedFileChanged(path) emitted to indicate a changed file
+    """
+    delayedDirectoryChanged = pyqtSignal(str)
+    delayedFileChanged = pyqtSignal(str)
+    
+    def __init__(self, paths=None, parent=None):
+        """
+        Constructor
+        
+        @param paths list of paths to be watched
+        @type list of str
+        @param parent reference to the parent object
+        @type QObject
+        """
+        if paths:
+            super(DelayedFileWatcher, self).__init__(paths, parent)
+        else:
+            super(DelayedFileWatcher, self).__init__(parent)
+        
+        self.__dirQueue = []
+        self.__fileQueue = []
+        
+        self.directoryChanged.connect(self.__directoryChanged)
+        self.fileChanged.connect(self.__fileChanged)
+    
+    @pyqtSlot(str)
+    def __directoryChanged(self, path):
+        """
+        Private slot handling a changed directory.
+        
+        @param path name of the changed directory
+        @type str
+        """
+        self.__dirQueue.append(path)
+        QTimer.singleShot(500, self.__dequeueDirectory)
+    
+    @pyqtSlot(str)
+    def __fileChanged(self, path):
+        """
+        Private slot handling a changed file.
+        
+        @param path name of the changed file
+        @type str
+        """
+        self.__fileQueue.append(path)
+        QTimer.singleShot(500, self.__dequeueFile)
+    
+    @pyqtSlot()
+    def __dequeueDirectory(self):
+        """
+        Private slot to signal a directory change.
+        """
+        self.delayedDirectoryChanged.emit(self.__dirQueue.pop(0))
+    
+    @pyqtSlot()
+    def __dequeueFile(self):
+        """
+        Private slot to signal a file change.
+        """
+        self.delayedFileChanged.emit(self.__fileQueue.pop(0))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Tools/Scripts.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,390 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module containing function to generate JavaScript code.
+"""
+
+#
+# This code was ported from QupZilla.
+# Copyright (C) David Rosca <nowrep@gmail.com>
+#
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import QUrlQuery, QUrl
+
+from .WebBrowserTools import readAllFileContents
+
+
+def setupWebChannel():
+    """
+    Function generating  a script to setup the web channel.
+    
+    @return script to setup the web channel
+    @rtype str
+    """
+    source = """
+        (function() {{
+            {0}
+            
+            function registerExternal(e) {{
+                window.external = e;
+                if (window.external) {{
+                    var event = document.createEvent('Event');
+                    event.initEvent('_eric_external_created', true, true);
+                    document.dispatchEvent(event);
+                }}
+            }}
+            
+            if (self !== top) {{
+                if (top.external)
+                    registerExternal(top.external);
+                else
+                    top.document.addEventListener(
+                        '_eric_external_created', function() {{
+                            registerExternal(top.external);
+                    }});
+                return;
+            }}
+
+            new QWebChannel(qt.webChannelTransport, function(channel) {{
+               registerExternal(channel.objects.eric_object);
+            }});
+
+        }})()"""
+    
+    return source.format(readAllFileContents(":/javascript/qwebchannel.js"))
+
+
+def setStyleSheet(css):
+    """
+    Function generating a script to set a user style sheet.
+    
+    @param css style sheet to be applied
+    @type str
+    @return script to set a user style sheet
+    @rtype str
+    """
+    source = """
+        (function() {{
+            var css = document.createElement('style');
+            css.setAttribute('type', 'text/css');
+            css.appendChild(document.createTextNode('{0}'));
+            document.getElementsByTagName('head')[0].appendChild(css);
+        }})()"""
+    
+    style = css.replace("'", "\\'").replace("\n", "\\n")
+    return source.format(style)
+
+
+def getFormData(pos):
+    """
+    Function generating a script to extract data for a form element.
+    
+    @param pos position to extract data at
+    @type QPoint
+    @return script to extract form data
+    @rtype str
+    """
+    source = """
+        (function() {{
+            var e = document.elementFromPoint({0}, {1});
+            if (!e || e.tagName != 'INPUT')
+                return;
+            var fe = e.parentElement;
+            while (fe) {{
+                if (fe.tagName == 'FORM')
+                    break;
+                fe = fe.parentElement;
+            }}
+            if (!fe)
+                return;
+            var res = {{
+                method: fe.method.toLowerCase(),
+                action: fe.action,
+                inputName: e.name,
+                inputs: [],
+            }};
+            for (var i = 0; i < fe.length; ++i) {{
+                var input = fe.elements[i];
+                res.inputs.push([input.name, input.value]);
+            }}
+            return res;
+        }})()"""
+    return source.format(pos.x(), pos.y())
+
+
+def getAllImages():
+    """
+    Function generating a script to extract all image tags of a web page.
+    
+    @return script to extract image tags
+    @rtype str
+    """
+    source = """
+        (function() {
+            var out = [];
+            var imgs = document.getElementsByTagName('img');
+            for (var i = 0; i < imgs.length; ++i) {
+                var e = imgs[i];
+                out.push({
+                    src: e.src,
+                    alt: e.alt
+                });
+            }
+            return out;
+        })()"""
+    return source
+
+
+def getAllMetaAttributes():
+    """
+    Function generating a script to extract all meta attributes of a web page.
+    
+    @return script to extract meta attributes
+    @rtype str
+    """
+    source = """
+        (function() {
+            var out = [];
+            var meta = document.getElementsByTagName('meta');
+            for (var i = 0; i < meta.length; ++i) {
+                var e = meta[i];
+                out.push({
+                    name: e.getAttribute('name'),
+                    content: e.getAttribute('content'),
+                    httpequiv: e.getAttribute('http-equiv'),
+                    charset: e.getAttribute('charset')
+                });
+            }
+            return out;
+        })()"""
+    return source
+
+
+def getOpenSearchLinks():
+    """
+    Function generating a script to extract all open search links.
+    
+    @return script to extract all open serach links
+    @rtype str
+    """
+    source = """
+        (function() {
+            var out = [];
+            var links = document.getElementsByTagName('link');
+            for (var i = 0; i < links.length; ++i) {
+                var e = links[i];
+                if (e.type == 'application/opensearchdescription+xml') {
+                    out.push({
+                        url: e.getAttribute('href'),
+                        title: e.getAttribute('title')
+                    });
+                }
+            }
+            return out;
+        })()"""
+    return source
+
+
+def sendPostData(url, data):
+    """
+    Function generating a script to send Post data.
+    
+    @param url URL to send the data to
+    @type QUrl
+    @param data data to be sent
+    @type QByteArray
+    @return script to send Post data
+    @rtype str
+    """
+    source = """
+        (function() {{
+            var form = document.createElement('form');
+            form.setAttribute('method', 'POST');
+            form.setAttribute('action', '{0}');
+            var val;
+            {1}
+            form.submit();
+        }})()"""
+    
+    valueSource = """
+        val = document.createElement('input');
+        val.setAttribute('type', 'hidden');
+        val.setAttribute('name', '{0}');
+        val.setAttribute('value', '{1}');
+        form.appendChild(val);"""
+    
+    values = ""
+    query = QUrlQuery(data)
+    for name, value in query.queryItems(QUrl.FullyDecoded):
+        value = value.replace("'", "\\'")
+        name = name.replace("'", "\\'")
+        values += valueSource.format(name, value)
+    
+    return source.format(url.toString(), values)
+
+
+def setupFormObserver():
+    """
+    Function generating a script to monitor a web form for user entries.
+    
+    @return script to monitor a web page
+    @rtype str
+    """
+    source = """
+        (function() {
+            function findUsername(inputs) {
+                for (var i = 0; i < inputs.length; ++i)
+                    if (inputs[i].type == 'text' && inputs[i].value.length &&
+                        inputs[i].name.indexOf('user') != -1)
+                        return inputs[i].value;
+                for (var i = 0; i < inputs.length; ++i)
+                    if (inputs[i].type == 'text' && inputs[i].value.length &&
+                        inputs[i].name.indexOf('name') != -1)
+                        return inputs[i].value;
+                for (var i = 0; i < inputs.length; ++i)
+                    if (inputs[i].type == 'text' && inputs[i].value.length)
+                        return inputs[i].value;
+                for (var i = 0; i < inputs.length; ++i)
+                    if (inputs[i].type == 'email' && inputs[i].value.length)
+                        return inputs[i].value;
+                return '';
+            }
+            
+            function registerForm(form) {
+                form.addEventListener('submit', function() {
+                    var form = this;
+                    var data = '';
+                    var password = '';
+                    var inputs = form.getElementsByTagName('input');
+                    for (var i = 0; i < inputs.length; ++i) {
+                        var input = inputs[i];
+                        var type = input.type.toLowerCase();
+                        if (type != 'text' && type != 'password' &&
+                            type != 'email')
+                            continue;
+                        if (!password && type == 'password')
+                            password = input.value;
+                        data += encodeURIComponent(input.name);
+                        data += '=';
+                        data += encodeURIComponent(input.value);
+                        data += '&';
+                    }
+                    if (!password)
+                        return;
+                    data = data.substring(0, data.length - 1);
+                    var url = window.location.href;
+                    var username = findUsername(inputs);
+                    external.passwordManager.formSubmitted(
+                        url, username, password, data);
+                }, true);
+            }
+            
+            for (var i = 0; i < document.forms.length; ++i)
+                registerForm(document.forms[i]);
+            
+            var observer = new MutationObserver(function(mutations) {
+                for (var i = 0; i < mutations.length; ++i)
+                    for (var j = 0; j < mutations[i].addedNodes.length; ++j)
+                        if (mutations[i].addedNodes[j].tagName == 'form')
+                            registerForm(mutations[i].addedNodes[j]);
+            });
+            observer.observe(document.documentElement, { childList: true });
+            
+        })()"""
+    return source
+
+
+def completeFormData(data):
+    """
+    Function generating a script to fill in form data.
+    
+    @param data data to be filled into the form
+    @type QByteArray
+    @return script to fill a form
+    @rtype str
+    """
+    source = """
+        (function() {{
+            var data = '{0}'.split('&');
+            var inputs = document.getElementsByTagName('input');
+            
+            for (var i = 0; i < data.length; ++i) {{
+                var pair = data[i].split('=');
+                if (pair.length != 2)
+                    continue;
+                var key = decodeURIComponent(pair[0]);
+                var val = decodeURIComponent(pair[1]);
+                for (var j = 0; j < inputs.length; ++j) {{
+                    var input = inputs[j];
+                    var type = input.type.toLowerCase();
+                    if (type != 'text' && type != 'password' &&
+                        type != 'email')
+                        continue;
+                    if (input.name == key)
+                        input.value = val;
+                }}
+            }}
+            
+        }})()"""
+    
+    data = bytes(data).decode("utf-8")
+    data = data.replace("'", "\\'")
+    return source.format(data)
+
+
+def setCss(css):
+    """
+    Function generating a script to set a given CSS style sheet.
+    
+    @param css style sheet
+    @type str
+    @return script to set the style sheet
+    @rtype str
+    """
+    source = """
+        (function() {{
+            var css = document.createElement('style');
+            css.setAttribute('type', 'text/css');
+            css.appendChild(document.createTextNode('{0}'));
+            document.getElementsByTagName('head')[0].appendChild(css);
+            }})()"""
+    style = css.replace("'", "\\'").replace("\n", "\\n")
+    return source.format(style)
+
+###########################################################################
+## scripts below are specific for eric
+###########################################################################
+
+
+def getFeedLinks():
+    """
+    Function generating a script to extract all RSS and Atom feed links.
+    
+    @return script to extract all RSS and Atom feed links
+    @rtype str
+    """
+    source = """
+        (function() {
+            var out = [];
+            var links = document.getElementsByTagName('link');
+            for (var i = 0; i < links.length; ++i) {
+                var e = links[i];
+                if ((e.rel == 'alternate') &&
+                    ((e.type == 'application/atom+xml') ||
+                     (e.type == 'application/rss+xml')
+                    )
+                   ) {
+                    out.push({
+                        url: e.getAttribute('href'),
+                        title: e.getAttribute('title')
+                    });
+                }
+            }
+            return out;
+        })()"""
+    return source
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Tools/WebBrowserTools.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,213 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing tool functions for the web browser.
+"""
+
+from __future__ import unicode_literals
+try:
+    str = unicode       # __IGNORE_EXCEPTION__
+except NameError:
+    pass
+
+import os
+import re
+
+from PyQt5.QtCore import QFile, QByteArray, QUrl, QCoreApplication, QBuffer, \
+    QIODevice
+from PyQt5.QtGui import QPixmap
+
+
+def readAllFileContents(filename):
+    """
+    Function to read the string contents of the given file.
+    
+    @param filename name of the file
+    @type str
+    @return contents of the file
+    @rtype str
+    """
+    return str(readAllFileByteContents(filename), encoding="utf-8")
+
+
+def readAllFileByteContents(filename):
+    """
+    Function to read the bytes contents of the given file.
+    
+    @param filename name of the file
+    @type str
+    @return contents of the file
+    @rtype str
+    """
+    dataFile = QFile(filename)
+    if filename and dataFile.open(QFile.ReadOnly):
+        contents = dataFile.readAll()
+        dataFile.close()
+        return contents
+    
+    return QByteArray()
+
+
+def containsSpace(string):
+    """
+    Function to check, if a string contains whitespace characters.
+    
+    @param string string to be checked
+    @type str
+    @return flag indicating the presence of at least one whitespace character
+    @rtype bool
+    """
+    for ch in string:
+        if ch.isspace():
+            return True
+    
+    return False
+
+
+def ensureUniqueFilename(name, appendFormat="({0})"):
+    """
+    Module function to generate an unique file name based on a pattern.
+    
+    @param name desired file name (string)
+    @param appendFormat format pattern to be used to make the unique name
+        (string)
+    @return unique file name
+    """
+    if not os.path.exists(name):
+        return name
+    
+    tmpFileName = name
+    i = 1
+    while os.path.exists(tmpFileName):
+        tmpFileName = name
+        index = tmpFileName.rfind(".")
+        
+        appendString = appendFormat.format(i)
+        if index == -1:
+            tmpFileName += appendString
+        else:
+            tmpFileName = tmpFileName[:index] + appendString + \
+                tmpFileName[index:]
+        i += 1
+    
+    return tmpFileName
+
+
+def getFileNameFromUrl(url):
+    """
+    Module function to generate a file name based on the given URL.
+    
+    @param url URL (QUrl)
+    @return file name (string)
+    """
+    fileName = url.toString(QUrl.RemoveFragment | QUrl.RemoveQuery |
+                            QUrl.RemoveScheme | QUrl.RemovePort)
+    if fileName.find("/") != -1:
+        pos = fileName.rfind("/")
+        fileName = fileName[pos:]
+        fileName = fileName.replace("/", "")
+    
+    fileName = filterCharsFromFilename(fileName)
+    
+    if not fileName:
+        fileName = filterCharsFromFilename(url.host().replace(".", "_"))
+    
+    return fileName
+
+
+def filterCharsFromFilename(name):
+    """
+    Module function to filter illegal characters.
+    
+    @param name name to be sanitized (string)
+    @return sanitized name (string)
+    """
+    return name\
+        .replace("/", "_")\
+        .replace("\\", "")\
+        .replace(":", "")\
+        .replace("*", "")\
+        .replace("?", "")\
+        .replace('"', "")\
+        .replace("<", "")\
+        .replace(">", "")\
+        .replace("|", "")
+
+
+def pixmapFromByteArray(data):
+    """
+    Module function to convert a byte array to a pixmap.
+    
+    @param data data for the pixmap
+    @type bytes or QByteArray
+    @return extracted pixmap
+    @rtype QPixmap
+    """
+    pixmap = QPixmap()
+    barray = QByteArray.fromBase64(data)
+    pixmap.loadFromData(barray)
+    
+    return pixmap
+
+
+def pixmapToByteArray(pixmap):
+    """
+    Module function to convert a pixmap to a byte array containing the pixmap
+    as a PNG encoded as base64.
+    
+    @param pixmap pixmap to be converted
+    @type QPixmap
+    @return byte array containing the pixmap
+    @rtype QByteArray
+    """
+    byteArray = QByteArray()
+    buffer = QBuffer(byteArray)
+    buffer.open(QIODevice.WriteOnly)
+    if pixmap.save(buffer, "PNG"):
+        return buffer.buffer().toBase64()
+    
+    return QByteArray()
+
+
+def pixmapToDataUrl(pixmap):
+    """
+    Module function to convert a pixmap to a data: URL.
+    
+    @param pixmap pixmap to be converted
+    @type QPixmap
+    @return data: URL
+    @rtype QUrl
+    """
+    data = bytes(pixmapToByteArray(pixmap)).decode()
+    if data:
+        return QUrl("data:image/png;base64," + data)
+    else:
+        return QUrl()
+
+
+def getWebEngineVersions():
+    """
+    Module function to extract the web engine version from the default user
+    agent string.
+    
+    @return tuple containing the Chrome version and the QtWebEngine version
+    @rtype tuple of str
+    """
+    from WebBrowser.WebBrowserWindow import WebBrowserWindow
+    useragent = WebBrowserWindow.webProfile().defaultUserAgent
+    match = re.search(r"""Chrome/([\d.]+)""", useragent)
+    if match:
+        chromeVersion = match.group(1)
+    else:
+        chromeVersion = QCoreApplication.translate(
+            "WebBrowserTools", "<unknown>")
+    match = re.search(r"""QtWebEngine/([\d.]+)""", useragent)
+    if match:
+        webengineVersion = match.group(1)
+    else:
+        webengineVersion = QCoreApplication.translate(
+            "WebBrowserTools", "<unknown>")
+    return (chromeVersion, webengineVersion)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Tools/WebHitTestResult.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,255 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing an object for testing certain aspects of a web page.
+"""
+
+#
+# This code was ported from QupZilla.
+# Copyright (C) David Rosca <nowrep@gmail.com>
+#
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import QPoint, QRect, QUrl
+
+class WebHitTestResult(object):
+    """
+    Class implementing an object for testing certain aspects of a web page.
+    """
+    def __init__(self, page, pos):
+        """
+        Constructor
+        
+        @param page reference to the web page
+        @type WebBrowserPage
+        @param pos position to be tested
+        @type QPoint
+        """
+        self.__isNull = True
+        self.__isContentEditable = False
+        self.__isContentSelected = False
+        self.__isMediaPaused = False
+        self.__isMediaMuted = False
+        self.__pos = QPoint(pos)
+        self.__alternateText = ""
+        self.__boundingRect = QRect()
+        self.__imageUrl = QUrl()
+        self.__linkTitle = ""
+        self.__linkUrl = QUrl()
+        self.__mediaUrl = QUrl()
+        self.__tagName = ""
+    
+        script = """
+            (function() {{
+                var e = document.elementFromPoint({0}, {1});
+                if (!e)
+                    return;
+                function isMediaElement(e) {{
+                    return e.tagName == 'AUDIO' || e.tagName == 'VIDEO';
+                }}
+                function isEditableElement(e) {{
+                    if (e.isContentEditable)
+                        return true;
+                    if (e.tagName == 'INPUT' || e.tagName == 'TEXTAREA')
+                        return e.getAttribute('readonly') != 'readonly';
+                    return false;
+                }}
+                function isSelected(e) {{
+                    var selection = window.getSelection();
+                    if (selection.type != 'Range')
+                        return false;
+                    return window.getSelection().containsNode(e, true);
+                }}
+                var res = {{
+                    alternateText: e.getAttribute('alt'),
+                    boundingRect: '',
+                    imageUrl: '',
+                    contentEditable: isEditableElement(e),
+                    contentSelected: isSelected(e),
+                    linkTitle: '',
+                    linkUrl: '',
+                    mediaUrl: '',
+                    mediaPaused: false,
+                    mediaMuted: false,
+                    tagName: e.tagName.toLowerCase()
+                }};
+                var r = e.getBoundingClientRect();
+                res.boundingRect = [r.top, r.left, r.width, r.height];
+                if (e.tagName == 'IMG')
+                    res.imageUrl = e.getAttribute('src');
+                if (e.tagName == 'A') {{
+                    res.linkTitle = e.text;
+                    res.linkUrl = e.getAttribute('href');
+                }}
+                while (e) {{
+                    if (res.linkTitle == '' && e.tagName == 'A')
+                        res.linkTitle = e.text;
+                    if (res.linkUrl == '' && e.tagName == 'A')
+                        res.linkUrl = e.getAttribute('href');
+                    if (res.mediaUrl == '' && isMediaElement(e)) {{
+                        res.mediaUrl = e.currentSrc;
+                        res.mediaPaused = e.paused;
+                        res.mediaMuted = e.muted;
+                    }}
+                    e = e.parentElement;
+                }}
+                return res;
+            }})()
+        """.format(pos.x(), pos.y())
+        self.__populate(page.url(), page.execJavaScript(script))
+    
+    def alternateText(self):
+        """
+        Public method to get the alternate text.
+        
+        @return alternate text
+        @rtype str
+        """
+        return self.__alternateText
+    
+    def boundingRect(self):
+        """
+        Public method to get the bounding rectangle.
+        
+        @return bounding rectangle
+        @rtype QRect
+        """
+        return QRect(self.__boundingRect)
+    
+    def imageUrl(self):
+        """
+        Public method to get the URL of an image.
+        
+        @return image URL
+        @rtype QUrl
+        """
+        return self.__imageUrl
+    
+    def isContentEditable(self):
+        """
+        Public method to check for editable content.
+        
+        @return flag indicating editable content
+        @rtype bool
+        """
+        return self.__isContentEditable
+    
+    def isContentSelected(self):
+        """
+        Public method to check for selected content.
+        
+        @return flag indicating selected content
+        @rtype bool
+        """
+        return self.__isContentSelected
+    
+    def isNull(self):
+        """
+        Public method to test, if the hit test is empty.
+        
+        @return flag indicating an empty object
+        @rtype bool
+        """
+        return self.__isNull
+    
+    def linkTitle(self):
+        """
+        Public method to get the title for a link element.
+        
+        @return title for a link element
+        @rtype str
+        """
+        return self.__linkTitle
+    
+    def linkUrl(self):
+        """
+        Public method to get the URL for a link element.
+        
+        @return URL for a link element
+        @rtype QUrl
+        """
+        return self.__linkUrl
+    
+    def mediaUrl(self):
+        """
+        Public method to get the URL for a media element.
+        
+        @return URL for a media element
+        @rtype QUrl
+        """
+        return self.__mediaUrl
+    
+    def mediaPaused(self):
+        """
+        Public method to check, if a media element is paused.
+        
+        @return flag indicating a paused media element
+        @rtype bool
+        """
+        return self.__isMediaPaused
+    
+    def mediaMuted(self):
+        """
+        Public method to check, if a media element is muted.
+        
+        @return flag indicating a muted media element
+        @rtype bool
+        """
+        return self.__isMediaMuted
+    
+    def pos(self):
+        """
+        Public method to get the position of the hit test.
+        
+        @return position of hit test
+        @rtype QPoint
+        """
+        return QPoint(self.__pos)
+    
+    def tagName(self):
+        """
+        Public method to get the name of the tested tag.
+        
+        @return name of the tested tag
+        @rtype str
+        """
+        return self.__tagName
+    
+    def __populate(self, url, res):
+        """
+        Private method to populate the object.
+        
+        @param url URL of the tested page
+        @type QUrl
+        @param res dictionary with result data from JavaScript
+        @type dict
+        """
+        if not res:
+            return
+        
+        self.__alternateText = res["alternateText"]
+        self.__imageUrl = QUrl(res["imageUrl"])
+        self.__isContentEditable = res["contentEditable"]
+        self.__isContentSelected = res["contentSelected"]
+        self.__linkTitle = res["linkTitle"]
+        self.__linkUrl = QUrl(res["linkUrl"])
+        self.__mediaUrl = QUrl(res["mediaUrl"])
+        self.__isMediaPaused = res["mediaPaused"]
+        self.__isMediaMuted = res["mediaMuted"]
+        self.__tagName = res["tagName"]
+        
+        rect = res["boundingRect"]
+        if len(rect) == 4:
+            self.__boundingRect = QRect(int(rect[0]), int(rect[1]),
+                                        int(rect[2]), int(rect[3]))
+        
+        if not self.__imageUrl.isEmpty():
+            self.__imageUrl = url.resolved(self.__imageUrl)
+        if not self.__linkUrl.isEmpty():
+            self.__linkUrl = url.resolved(self.__linkUrl)
+        if not self.__mediaUrl.isEmpty():
+            self.__mediaUrl = url.resolved(self.__mediaUrl)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Tools/WebIconDialog.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,100 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog to manage the Favicons.
+"""
+
+from PyQt5.QtCore import pyqtSlot, Qt, QPoint
+from PyQt5.QtWidgets import QDialog, QListWidgetItem, QMenu
+
+from .Ui_WebIconDialog import Ui_WebIconDialog
+
+
+class WebIconDialog(QDialog, Ui_WebIconDialog):
+    """
+    Class implementing a dialog to manage the Favicons.
+    """
+    def __init__(self, iconsDB, parent=None):
+        """
+        Constructor
+        
+        @param iconsDB icons database
+        @type dict
+        @param parent reference to the parent widget
+        @type QWidget
+        """
+        super(WebIconDialog, self).__init__(parent)
+        self.setupUi(self)
+        
+        for url, icon in iconsDB.items():
+            QListWidgetItem(icon, url, self.iconsList)
+        self.iconsList.sortItems(Qt.AscendingOrder)
+        
+        self.__setRemoveButtons()
+    
+    def __setRemoveButtons(self):
+        """
+        Private method to set the state of the 'remove' buttons.
+        """
+        self.removeAllButton.setEnabled(self.iconsList.count() > 0)
+        self.removeButton.setEnabled(len(self.iconsList.selectedItems()) > 0)
+    
+    @pyqtSlot(QPoint)
+    def on_iconsList_customContextMenuRequested(self, pos):
+        """
+        Private slot to show the context menu.
+        
+        @param pos cursor position
+        @type QPoint
+        """
+        menu = QMenu()
+        menu.addAction(
+            self.tr("Remove Selected"),
+            self.on_removeButton_clicked).setEnabled(
+            len(self.iconsList.selectedItems()) > 0)
+        menu.addAction(
+            self.tr("Remove All"),
+            self.on_removeAllButton_clicked).setEnabled(
+            self.iconsList.count() > 0)
+        
+        menu.exec_(self.iconsList.mapToGlobal(pos))
+    
+    @pyqtSlot()
+    def on_iconsList_itemSelectionChanged(self):
+        """
+        Private slot handling the selection of entries.
+        """
+        self.__setRemoveButtons()
+    
+    @pyqtSlot()
+    def on_removeButton_clicked(self):
+        """
+        Private slot to remove the selected items.
+        """
+        for itm in self.iconsList.selectedItems():
+            row = self.iconsList.row(itm)
+            self.iconsList.takeItem(row)
+            del itm
+    
+    @pyqtSlot()
+    def on_removeAllButton_clicked(self):
+        """
+        Private slot to remove all entries.
+        """
+        self.iconsList.clear()
+    
+    def getUrls(self):
+        """
+        Public method to get the list of URLs.
+        
+        @return list of URLs
+        @rtype list of str
+        """
+        urls = []
+        for row in range(self.iconsList.count()):
+            urls.append(self.iconsList.item(row).text())
+        
+        return urls
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Tools/WebIconDialog.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>WebIconDialog</class>
+ <widget class="QDialog" name="WebIconDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>550</width>
+    <height>600</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Favicons</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QGridLayout" name="gridLayout">
+     <item row="0" column="0" rowspan="3">
+      <widget class="QListWidget" name="iconsList">
+       <property name="contextMenuPolicy">
+        <enum>Qt::CustomContextMenu</enum>
+       </property>
+       <property name="alternatingRowColors">
+        <bool>true</bool>
+       </property>
+       <property name="selectionMode">
+        <enum>QAbstractItemView::ExtendedSelection</enum>
+       </property>
+       <property name="iconSize">
+        <size>
+         <width>22</width>
+         <height>22</height>
+        </size>
+       </property>
+       <property name="sortingEnabled">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="1">
+      <widget class="QPushButton" name="removeButton">
+       <property name="toolTip">
+        <string>Press to remove the selected entries</string>
+       </property>
+       <property name="text">
+        <string>&amp;Remove</string>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="1">
+      <widget class="QPushButton" name="removeAllButton">
+       <property name="toolTip">
+        <string>Press to remove all entries</string>
+       </property>
+       <property name="text">
+        <string>Remove &amp;All</string>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="1">
+      <spacer name="verticalSpacer">
+       <property name="orientation">
+        <enum>Qt::Vertical</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>20</width>
+         <height>40</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>WebIconDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>228</x>
+     <y>581</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>WebIconDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>296</x>
+     <y>587</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Tools/WebIconLoader.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,55 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing an object to load web site icons.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject
+from PyQt5.QtGui import QIcon, QPixmap, QImage
+
+from ..Network.FollowRedirectReply import FollowRedirectReply
+
+import WebBrowser.WebBrowserWindow
+
+
+class WebIconLoader(QObject):
+    """
+    Class implementing a loader for web site icons.
+    
+    @signal iconLoaded(icon) emitted when the con has been loaded
+    """
+    iconLoaded = pyqtSignal(QIcon)
+    
+    def __init__(self, url, parent=None):
+        """
+        Constructor
+        
+        @param url URL to fetch the icon from
+        @type QUrl
+        @param parent reference to the parent object
+        @type QObject
+        """
+        super(WebIconLoader, self).__init__(parent)
+        
+        networkManager = \
+            WebBrowser.WebBrowserWindow.WebBrowserWindow.networkManager()
+        self.__reply = FollowRedirectReply(url, networkManager)
+        self.__reply.finished.connect(self.__finished)
+    
+    @pyqtSlot()
+    def __finished(self):
+        """
+        Private slot handling the downloaded icon.
+        """
+        # ignore any errors and emit an empty icon in this case
+        data = self.__reply.readAll()
+        icon = QIcon(QPixmap.fromImage(QImage.fromData(data)))
+        self.iconLoaded.emit(icon)
+        
+        self.__reply.deleteLater()
+        self.__reply = None
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Tools/WebIconProvider.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,239 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module containing a web site icon storage object.
+"""
+
+from __future__ import unicode_literals
+
+import json
+import os
+
+from PyQt5.QtCore import pyqtSignal, QObject, QByteArray, QBuffer, QIODevice, \
+    QUrl
+from PyQt5.QtGui import QIcon, QPixmap, QImage
+from PyQt5.QtWidgets import QDialog
+
+from Utilities.AutoSaver import AutoSaver
+
+import UI.PixmapCache
+
+
+class WebIconProvider(QObject):
+    """
+    Class implementing a web site icon storage.
+    """
+    changed = pyqtSignal()
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object (QObject)
+        """
+        super(WebIconProvider, self).__init__(parent)
+        
+        self.__encoding = "iso-8859-1"
+        self.__iconsFileName = "web_site_icons.json"
+        self.__iconDatabasePath = ""    # saving of icons disabled
+        
+        self.__iconsDB = {}
+        self.__loaded = False
+        
+        self.__saveTimer = AutoSaver(self, self.save)
+        
+        self.changed.connect(self.__saveTimer.changeOccurred)
+    
+    def setIconDatabasePath(self, path):
+        """
+        Public method to set the path for the web site icons store.
+        
+        @param path path to store the icons file to
+        @type str
+        """
+        if path != self.__iconDatabasePath:
+            self.close()
+        
+        self.__iconDatabasePath = path
+    
+    def iconDatabasePath(self):
+        """
+        Public method o get the path for the web site icons store.
+        
+        @return path to store the icons file to
+        @rtype str
+        """
+        return self.__iconDatabasePath
+    
+    def close(self):
+        """
+        Public method to close the web icon provider.
+        """
+        self.__saveTimer.saveIfNeccessary()
+        self.__loaded = False
+        self.__iconsDB = {}
+    
+    def load(self):
+        """
+        Public method to load the bookmarks.
+        """
+        if self.__loaded:
+            return
+        
+        if self.__iconDatabasePath:
+            filename = os.path.join(self.__iconDatabasePath,
+                                    self.__iconsFileName)
+            try:
+                f = open(filename, "r")
+                db = json.load(f)
+                f.close()
+            except (IOError, OSError):
+                # ignore silentyl
+                db = {}
+            
+            self.__iconsDB = {}
+            for url, data in db.items():
+                self.__iconsDB[url] = QIcon(QPixmap.fromImage(QImage.fromData(
+                    QByteArray(data.encode(self.__encoding)))))
+        
+        self.__loaded = True
+    
+    def save(self):
+        """
+        Public method to save the zoom values.
+        """
+        if not self.__loaded:
+            return
+        
+        from WebBrowser.WebBrowserWindow import WebBrowserWindow
+        if not WebBrowserWindow.isPrivate() and bool(self.__iconDatabasePath):
+            db = {}
+            for url, icon in self.__iconsDB.items():
+                ba = QByteArray()
+                buffer = QBuffer(ba)
+                buffer.open(QIODevice.WriteOnly)
+                icon.pixmap(32).toImage().save(buffer, "PNG")
+                db[url] = bytes(buffer.data()).decode(self.__encoding)
+            
+            filename = os.path.join(self.__iconDatabasePath,
+                                    self.__iconsFileName)
+            try:
+                f = open(filename, "w")
+                json.dump(db, f)
+                f.close()
+            except (IOError, OSError):
+                # ignore silentyl
+                pass
+    
+    def saveIcon(self, view):
+        """
+        Public method to save a web site icon.
+        
+        @param view reference to the view object
+        @type WebBrowserView
+        """
+        scheme = view.url().scheme()
+        if scheme in ["eric", "about", "qthelp", "file", "abp", "ftp"]:
+            return
+        
+        self.load()
+        
+        if view.mainWindow().isPrivate():
+            return
+        
+        urlStr = self.__urlToString(view.url())
+        self.__iconsDB[urlStr] = view.icon()
+        
+        self.changed.emit()
+    
+    def __urlToString(self, url):
+        """
+        Private method to convert an URL to a string.
+        
+        @param url URL to be converted
+        @type QUrl
+        @return string representation of the URL
+        @rtype str
+        """
+        return url.toString(QUrl.PrettyDecoded | QUrl.RemoveUserInfo |
+                            QUrl.RemoveFragment)
+    
+    def iconForUrl(self, url):
+        """
+        Public method to get an icon for an URL.
+        
+        @param url URL to get icon for
+        @type QUrl
+        @return icon for the URL
+        @rtype QIcon
+        """
+        scheme = url.scheme()
+        if scheme in ["eric", "about"]:
+            return UI.PixmapCache.getIcon("ericWeb.png")
+        elif scheme == "qthelp":
+            return UI.PixmapCache.getIcon("qthelp.png")
+        elif scheme == "file":
+            return UI.PixmapCache.getIcon("fileMisc.png")
+        elif scheme == "abp":
+            return UI.PixmapCache.getIcon("adBlockPlus.png")
+        elif scheme == "ftp":
+            return UI.PixmapCache.getIcon("network-server.png")
+        
+        self.load()
+        
+        urlStr = self.__urlToString(url)
+        if urlStr in self.__iconsDB:
+            return self.__iconsDB[urlStr]
+        elif scheme == "https":
+            return UI.PixmapCache.getIcon("securityHigh32.png")
+        else:
+            return UI.PixmapCache.getIcon("defaultIcon.png")
+    
+    def clear(self):
+        """
+        Public method to clear the icons cache.
+        """
+        self.load()
+        self.__iconsDB = {}
+        self.changed.emit()
+        self.__saveTimer.saveIfNeccessary()
+    
+    def showWebIconDialog(self):
+        """
+        Public method to show a dialog to manage the Favicons.
+        """
+        self.load()
+        
+        from .WebIconDialog import WebIconDialog
+        dlg = WebIconDialog(self.__iconsDB)
+        if dlg.exec_() == QDialog.Accepted:
+            changed = False
+            urls = dlg.getUrls()
+            for url in list(self.__iconsDB.keys())[:]:
+                if url not in urls:
+                    del self.__iconsDB[url]
+                    changed = True
+            if changed:
+                self.changed.emit()
+
+
+__WebIconProvider = None
+
+
+def instance():
+    """
+    Global function to get a reference to the web icon provider and create it,
+    if it hasn't been yet.
+    
+    @return reference to the web icon provider object
+    @rtype WebIconProvider
+    """
+    global __WebIconProvider
+    
+    if __WebIconProvider is None:
+        __WebIconProvider = WebIconProvider()
+    
+    return __WebIconProvider
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Tools/__init__.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Package containing tools for the QtWebEngine based browser.
+"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/UrlBar/BookmarkActionSelectionDialog.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,88 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog to select the action to be performed on the
+bookmark.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSlot
+from PyQt5.QtWidgets import QDialog
+
+from .Ui_BookmarkActionSelectionDialog import Ui_BookmarkActionSelectionDialog
+
+import UI.PixmapCache
+
+
+class BookmarkActionSelectionDialog(QDialog, Ui_BookmarkActionSelectionDialog):
+    """
+    Class implementing a dialog to select the action to be performed on
+    the bookmark.
+    """
+    Undefined = -1
+    AddBookmark = 0
+    EditBookmark = 1
+    AddSpeeddial = 2
+    RemoveSpeeddial = 3
+    
+    def __init__(self, url, parent=None):
+        """
+        Constructor
+        
+        @param url URL to be worked on (QUrl)
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(BookmarkActionSelectionDialog, self).__init__(parent)
+        self.setupUi(self)
+        
+        self.__action = self.Undefined
+        
+        self.icon.setPixmap(UI.PixmapCache.getPixmap("bookmark32.png"))
+        
+        from WebBrowser.WebBrowserWindow import WebBrowserWindow
+        
+        if WebBrowserWindow.bookmarksManager().bookmarkForUrl(url) is None:
+            self.__bmAction = self.AddBookmark
+            self.bookmarkPushButton.setText(self.tr("Add Bookmark"))
+        else:
+            self.__bmAction = self.EditBookmark
+            self.bookmarkPushButton.setText(self.tr("Edit Bookmark"))
+        
+        if WebBrowserWindow.speedDial().pageForUrl(url).url:
+            self.__sdAction = self.RemoveSpeeddial
+            self.speeddialPushButton.setText(
+                self.tr("Remove from Speed Dial"))
+        else:
+            self.__sdAction = self.AddSpeeddial
+            self.speeddialPushButton.setText(self.tr("Add to Speed Dial"))
+        
+        msh = self.minimumSizeHint()
+        self.resize(max(self.width(), msh.width()), msh.height())
+    
+    @pyqtSlot()
+    def on_bookmarkPushButton_clicked(self):
+        """
+        Private slot handling selection of a bookmark action.
+        """
+        self.__action = self.__bmAction
+        self.accept()
+    
+    @pyqtSlot()
+    def on_speeddialPushButton_clicked(self):
+        """
+        Private slot handling selection of a speed dial action.
+        """
+        self.__action = self.__sdAction
+        self.accept()
+    
+    def getAction(self):
+        """
+        Public method to get the selected action.
+        
+        @return reference to the associated action
+        """
+        return self.__action
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/UrlBar/BookmarkActionSelectionDialog.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>BookmarkActionSelectionDialog</class>
+ <widget class="QDialog" name="BookmarkActionSelectionDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>291</width>
+    <height>153</height>
+   </rect>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QLabel" name="icon">
+     <property name="alignment">
+      <set>Qt::AlignCenter</set>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QLabel" name="label_2">
+     <property name="text">
+      <string>&lt;b&gt;Add/Edit Bookmark&lt;/b&gt;</string>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignCenter</set>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QPushButton" name="bookmarkPushButton"/>
+   </item>
+   <item>
+    <widget class="QPushButton" name="speeddialPushButton"/>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/UrlBar/BookmarkInfoDialog.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,71 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2010 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog to show some bookmark info.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSlot
+from PyQt5.QtGui import QFont
+from PyQt5.QtWidgets import QDialog
+
+from .Ui_BookmarkInfoDialog import Ui_BookmarkInfoDialog
+
+import UI.PixmapCache
+
+
+class BookmarkInfoDialog(QDialog, Ui_BookmarkInfoDialog):
+    """
+    Class implementing a dialog to show some bookmark info.
+    """
+    def __init__(self, bookmark, parent=None):
+        """
+        Constructor
+        
+        @param bookmark reference to the bookmark to be shown (Bookmark)
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(BookmarkInfoDialog, self).__init__(parent)
+        self.setupUi(self)
+        
+        self.__bookmark = bookmark
+        
+        self.icon.setPixmap(UI.PixmapCache.getPixmap("bookmark32.png"))
+        
+        font = QFont()
+        font.setPointSize(font.pointSize() + 2)
+        self.title.setFont(font)
+        
+        if bookmark is None:
+            self.titleEdit.setEnabled(False)
+        else:
+            self.titleEdit.setText(bookmark.title)
+            self.titleEdit.setFocus()
+        
+        msh = self.minimumSizeHint()
+        self.resize(max(self.width(), msh.width()), msh.height())
+    
+    @pyqtSlot()
+    def on_removeButton_clicked(self):
+        """
+        Private slot to remove the current bookmark.
+        """
+        import WebBrowser.WebBrowserWindow
+        WebBrowser.WebBrowserWindow.WebBrowserWindow.bookmarksManager()\
+            .removeBookmark(self.__bookmark)
+        self.close()
+    
+    def accept(self):
+        """
+        Public slot handling the acceptance of the dialog.
+        """
+        if self.__bookmark is not None and \
+           self.titleEdit.text() != self.__bookmark.title:
+            import WebBrowser.WebBrowserWindow
+            WebBrowser.WebBrowserWindow.WebBrowserWindow.bookmarksManager()\
+                .setTitle(self.__bookmark, self.titleEdit.text())
+        self.close()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/UrlBar/BookmarkInfoDialog.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,132 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>BookmarkInfoDialog</class>
+ <widget class="QDialog" name="BookmarkInfoDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>350</width>
+    <height>135</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Edit Bookmark</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QGridLayout" name="gridLayout">
+   <item row="0" column="1">
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <property name="spacing">
+      <number>10</number>
+     </property>
+     <item>
+      <widget class="QLabel" name="icon">
+       <property name="text">
+        <string/>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <layout class="QVBoxLayout" name="verticalLayout">
+       <item>
+        <widget class="QLabel" name="title">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="text">
+          <string>Edit this Bookmark</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignCenter</set>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QPushButton" name="removeButton">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="toolTip">
+          <string>Press to remove this bookmark</string>
+         </property>
+         <property name="text">
+          <string>Remove this Bookmark</string>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+    </layout>
+   </item>
+   <item row="1" column="0">
+    <widget class="QLabel" name="label">
+     <property name="text">
+      <string>Title:</string>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="1">
+    <widget class="QLineEdit" name="titleEdit"/>
+   </item>
+   <item row="2" column="0" colspan="2">
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <tabstops>
+  <tabstop>removeButton</tabstop>
+  <tabstop>titleEdit</tabstop>
+  <tabstop>buttonBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>BookmarkInfoDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>227</x>
+     <y>114</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>134</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>BookmarkInfoDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>295</x>
+     <y>120</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>134</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/UrlBar/FavIconLabel.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,99 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2010 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the label to show the web site icon.
+"""
+
+from __future__ import unicode_literals
+try:
+    str = unicode
+except NameError:
+    pass
+
+from PyQt5.QtCore import Qt, QPoint, QMimeData
+from PyQt5.QtGui import QDrag, QPixmap
+from PyQt5.QtWidgets import QLabel, QApplication
+
+
+class FavIconLabel(QLabel):
+    """
+    Class implementing the label to show the web site icon.
+    """
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(FavIconLabel, self).__init__(parent)
+        
+        self.__browser = None
+        self.__dragStartPos = QPoint()
+        
+        self.setFocusPolicy(Qt.NoFocus)
+        self.setCursor(Qt.ArrowCursor)
+        self.setMinimumSize(16, 16)
+        self.resize(16, 16)
+        
+        self.__browserIconChanged()
+    
+    def __browserIconChanged(self):
+        """
+        Private slot to set the icon.
+        """
+        if self.__browser:
+            self.setPixmap(
+                self.__browser.icon().pixmap(16, 16))
+    
+    def __clearIcon(self):
+        """
+        Private slot to clear the icon.
+        """
+        self.setPixmap(QPixmap())
+    
+    def setBrowser(self, browser):
+        """
+        Public method to set the browser connection.
+        
+        @param browser reference to the browser widegt (HelpBrowser)
+        """
+        self.__browser = browser
+        self.__browser.loadFinished.connect(self.__browserIconChanged)
+        self.__browser.iconChanged.connect(self.__browserIconChanged)
+        self.__browser.loadStarted.connect(self.__clearIcon)
+    
+    def mousePressEvent(self, evt):
+        """
+        Protected method to handle mouse press events.
+        
+        @param evt reference to the mouse event (QMouseEvent)
+        """
+        if evt.button() == Qt.LeftButton:
+            self.__dragStartPos = evt.pos()
+        super(FavIconLabel, self).mousePressEvent(evt)
+    
+    def mouseMoveEvent(self, evt):
+        """
+        Protected method to handle mouse move events.
+        
+        @param evt reference to the mouse event (QMouseEvent)
+        """
+        if evt.button() == Qt.LeftButton and \
+           (evt.pos() - self.__dragStartPos).manhattanLength() > \
+                QApplication.startDragDistance() and \
+           self.__browser is not None:
+            drag = QDrag(self)
+            mimeData = QMimeData()
+            title = self.__browser.title()
+            if title == "":
+                title = str(self.__browser.url().toEncoded(), encoding="utf-8")
+            mimeData.setText(title)
+            mimeData.setUrls([self.__browser.url()])
+            p = self.pixmap()
+            if p:
+                drag.setPixmap(p)
+            drag.setMimeData(mimeData)
+            drag.exec_()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/UrlBar/StackedUrlBar.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,70 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2010 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a widget to stack url bars.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtWidgets import QStackedWidget, QSizePolicy
+
+
+class StackedUrlBar(QStackedWidget):
+    """
+    Class implementing a widget to stack URL bars.
+    """
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(StackedUrlBar, self).__init__(parent)
+        
+        sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
+        sizePolicy.setHorizontalStretch(6)
+        sizePolicy.setVerticalStretch(0)
+        self.setSizePolicy(sizePolicy)
+        self.setMinimumSize(200, 22)
+    
+    def currentUrlBar(self):
+        """
+        Public method to get a reference to the current URL bar.
+        
+        @return reference to the current URL bar (UrlBar)
+        """
+        return self.urlBar(self.currentIndex())
+    
+    def urlBar(self, index):
+        """
+        Public method to get a reference to the URL bar for a given index.
+        
+        @param index index of the url bar (integer)
+        @return reference to the URL bar for the given index (UrlBar)
+        """
+        return self.widget(index)
+    
+    def moveBar(self, from_, to_):
+        """
+        Public slot to move an URL bar.
+        
+        @param from_ index of URL bar to be moved (integer)
+        @param to_ index to move the URL bar to (integer)
+        """
+        fromBar = self.widget(from_)
+        self.removeWidget(fromBar)
+        self.insertWidget(to_, fromBar)
+    
+    def urlBars(self):
+        """
+        Public method to get a list of references to all URL bars.
+        
+        @return list of references to URL bars (list of UrlBar)
+        """
+        urlBars = []
+        for index in range(self.count()):
+            urlBars.append(self.widget(index))
+        return urlBars
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/UrlBar/UrlBar.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,441 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2010 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the URL bar widget.
+"""
+
+from __future__ import unicode_literals
+try:
+    str = unicode       # __IGNORE_EXCEPTION__
+except NameError:
+    pass
+
+from PyQt5.QtCore import pyqtSlot, Qt, QPointF, QUrl, QDateTime, QTimer, \
+    qVersion
+from PyQt5.QtGui import QColor, QPalette, QLinearGradient, QIcon
+from PyQt5.QtWidgets import QDialog, QApplication
+try:
+    from PyQt5.QtNetwork import QSslCertificate     # __IGNORE_EXCEPTION__
+except ImportError:
+    QSslCertificate = None      # __IGNORE_WARNING__
+from PyQt5.QtWebEngineWidgets import QWebEnginePage
+
+from E5Gui.E5LineEdit import E5LineEdit
+from E5Gui.E5LineEditButton import E5LineEditButton
+
+from WebBrowser.WebBrowserWindow import WebBrowserWindow
+
+from .FavIconLabel import FavIconLabel
+##from .SslLabel import SslLabel
+##
+import UI.PixmapCache
+import Preferences
+import Utilities
+
+
+class UrlBar(E5LineEdit):
+    """
+    Class implementing a line edit for entering URLs.
+    """
+    def __init__(self, mainWindow, parent=None):
+        """
+        Constructor
+        
+        @param mainWindow reference to the main window (HelpWindow)
+        @param parent reference to the parent widget (HelpBrowser)
+        """
+        E5LineEdit.__init__(self, parent)
+        self.setInactiveText(self.tr("Enter the URL here."))
+        self.setWhatsThis(self.tr("Enter the URL here."))
+        
+        self.__mw = mainWindow
+        self.__browser = None
+        self.__privateMode = WebBrowserWindow.isPrivate()
+        
+        self.__bmActiveIcon = UI.PixmapCache.getIcon("bookmark16.png")
+        self.__bmInactiveIcon = QIcon(
+            self.__bmActiveIcon.pixmap(16, 16, QIcon.Disabled))
+        
+        self.__favicon = FavIconLabel(self)
+        self.addWidget(self.__favicon, E5LineEdit.LeftSide)
+        
+        # TODO: SSL
+##        self.__sslLabel = SslLabel(self)
+##        self.addWidget(self.__sslLabel, E5LineEdit.LeftSide)
+##        self.__sslLabel.setVisible(False)
+        
+        self.__rssButton = E5LineEditButton(self)
+        self.__rssButton.setIcon(UI.PixmapCache.getIcon("rss16.png"))
+        self.addWidget(self.__rssButton, E5LineEdit.RightSide)
+        self.__rssButton.setVisible(False)
+        
+        self.__bookmarkButton = E5LineEditButton(self)
+        self.addWidget(self.__bookmarkButton, E5LineEdit.RightSide)
+        self.__bookmarkButton.setVisible(False)
+        
+        self.__clearButton = E5LineEditButton(self)
+        self.__clearButton.setIcon(UI.PixmapCache.getIcon("clearLeft.png"))
+        self.addWidget(self.__clearButton, E5LineEdit.RightSide)
+        self.__clearButton.setVisible(False)
+        
+        self.__bookmarkButton.clicked.connect(self.__showBookmarkInfo)
+        self.__rssButton.clicked.connect(self.__rssClicked)
+        self.__clearButton.clicked.connect(self.clear)
+        self.textChanged.connect(self.__textChanged)
+        
+        self.__mw.bookmarksManager().entryChanged.connect(
+            self.__bookmarkChanged)
+        self.__mw.bookmarksManager().entryAdded.connect(
+            self.__bookmarkChanged)
+        self.__mw.bookmarksManager().entryRemoved.connect(
+            self.__bookmarkChanged)
+        self.__mw.speedDial().pagesChanged.connect(
+            self.__bookmarkChanged)
+    
+    def setBrowser(self, browser):
+        """
+        Public method to set the browser connection.
+        
+        @param browser reference to the browser widget (WebBrowserView)
+        """
+        self.__browser = browser
+        self.__favicon.setBrowser(browser)
+        
+        self.__browser.urlChanged.connect(self.__browserUrlChanged)
+        self.__browser.loadProgress.connect(self.update)
+        self.__browser.loadFinished.connect(self.__loadFinished)
+        self.__browser.loadStarted.connect(self.__loadStarted)
+        
+        # TODO: SSL
+##        self.__sslLabel.clicked.connect(self.__browser.page().showSslInfo)
+    
+    def browser(self):
+        """
+        Public method to get the associated browser.
+       
+        @return reference to the associated browser (HelpBrowser)
+        """
+        return self.__browser
+    
+    def __browserUrlChanged(self, url):
+        """
+        Private slot to handle a URL change of the associated browser.
+        
+        @param url new URL of the browser (QUrl)
+        """
+        strUrl = url.toString()
+        if strUrl in ["eric:speeddial", "eric:home",
+                      "about:blank", "about:config"]:
+            strUrl = ""
+        
+        if self.text() != strUrl:
+            self.setText(strUrl)
+        self.setCursorPosition(0)
+    
+    def __loadStarted(self):
+        """
+        Private slot to perform actions before the page is loaded.
+        """
+        pass
+        # TODO: SSL
+##        self.__sslLabel.setVisible(False)
+        self.__bookmarkButton.setVisible(False)
+        self.__rssButton.setVisible(False)
+    
+    def __checkBookmark(self):
+        """
+        Private slot to check the current URL for the bookmarked state.
+        """
+        manager = self.__mw.bookmarksManager()
+        if manager.bookmarkForUrl(self.__browser.url()) is not None:
+            self.__bookmarkButton.setIcon(self.__bmActiveIcon)
+            bookmarks = manager.bookmarksForUrl(self.__browser.url())
+            from WebBrowser.Bookmarks.BookmarkNode import BookmarkNode
+            for bookmark in bookmarks:
+                manager.setTimestamp(bookmark, BookmarkNode.TsVisited,
+                                     QDateTime.currentDateTime())
+        elif self.__mw.speedDial()\
+                .pageForUrl(self.__browser.url()).url != "":
+            self.__bookmarkButton.setIcon(self.__bmActiveIcon)
+        else:
+            self.__bookmarkButton.setIcon(self.__bmInactiveIcon)
+    
+    def __loadFinished(self, ok):
+        """
+        Private slot to set some data after the page was loaded.
+        
+        @param ok flag indicating a successful load (boolean)
+        """
+##        try:
+        if self.__browser.url().scheme() in ["eric", "about"]:
+            self.__bookmarkButton.setVisible(False)
+        else:
+            self.__checkBookmark()
+            self.__bookmarkButton.setVisible(True)
+        
+        if ok:
+            QTimer.singleShot(0, self.__setRssButton)
+        
+        # TODO: SSL certificate stuff (if possible)
+##        if ok and \
+##           self.__browser.url().scheme() == "https" and \
+##           QSslCertificate is not None:
+##            sslInfo = self.__browser.page().getSslCertificate()
+##            if sslInfo is not None:
+##                org = Utilities.decodeString(", ".join(
+##                    sslInfo.subjectInfo(QSslCertificate.Organization)))
+##                if org == "":
+##                    cn = Utilities.decodeString(", ".join(
+##                        sslInfo.subjectInfo(
+##                            QSslCertificate.CommonName)))
+##                    if cn != "":
+##                        org = cn.split(".", 1)[1]
+##                    if org == "":
+##                        org = self.tr("Unknown")
+##                self.__sslLabel.setText(" {0} ".format(org))
+##                self.__sslLabel.setVisible(True)
+##                valid = not sslInfo.isBlacklisted()
+##                if valid:
+##                    config = self.__browser.page().getSslConfiguration()
+##                    if config is None or config.sessionCipher().isNull():
+##                        valid = False
+##                self.__sslLabel.setValidity(valid)
+##                return
+##        
+##        self.__sslLabel.setVisible(False)
+##        except RuntimeError:
+##            pass
+    
+    def __textChanged(self, txt):
+        """
+        Private slot to handle changes of the text.
+        
+        @param txt current text (string)
+        """
+        self.__clearButton.setVisible(txt != "")
+    
+    def preferencesChanged(self):
+        """
+        Public slot to handle a change of preferences.
+        """
+        self.update()
+    
+    def __showBookmarkInfo(self):
+        """
+        Private slot to show a dialog with some bookmark info.
+        """
+        from .BookmarkActionSelectionDialog import \
+            BookmarkActionSelectionDialog
+        url = self.__browser.url()
+        dlg = BookmarkActionSelectionDialog(url)
+        if dlg.exec_() == QDialog.Accepted:
+            action = dlg.getAction()
+            if action == BookmarkActionSelectionDialog.AddBookmark:
+                self.__browser.addBookmark()
+            elif action == BookmarkActionSelectionDialog.EditBookmark:
+                bookmark = self.__mw.bookmarksManager()\
+                    .bookmarkForUrl(url)
+                from .BookmarkInfoDialog import BookmarkInfoDialog
+                dlg = BookmarkInfoDialog(bookmark, self.__browser)
+                dlg.exec_()
+            elif action == BookmarkActionSelectionDialog.AddSpeeddial:
+                self.__mw.speedDial().addPage(
+                    url, self.__browser.title())
+            elif action == BookmarkActionSelectionDialog.RemoveSpeeddial:
+                self.__mw.speedDial().removePage(url)
+    
+    @pyqtSlot()
+    def __bookmarkChanged(self):
+        """
+        Private slot to handle bookmark or speed dial changes.
+        """
+        self.__checkBookmark()
+    
+    def paintEvent(self, evt):
+        """
+        Protected method handling a paint event.
+        
+        @param evt reference to the paint event (QPaintEvent)
+        """
+        if self.__privateMode:
+            backgroundColor = QColor(220, 220, 220)     # light gray
+            foregroundColor = Qt.black
+        else:
+            backgroundColor = QApplication.palette().color(QPalette.Base)
+            foregroundColor = QApplication.palette().color(QPalette.Text)
+        
+        if self.__browser is not None:
+            p = self.palette()
+            progress = self.__browser.progress()
+            if progress == 0 or progress == 100:
+                if self.__browser.url().scheme() == "https":
+##                    if QSslCertificate is not None:
+##                        if self.__browser.page().hasValidSslInfo():
+##                            backgroundColor = Preferences.getWebBrowser(
+##                                "SaveUrlColor")
+##                    else:
+                    backgroundColor = Preferences.getWebBrowser(
+                        "SaveUrlColor")
+                p.setBrush(QPalette.Base, backgroundColor)
+                p.setBrush(QPalette.Text, foregroundColor)
+            else:
+                if self.__browser.url().scheme() == "https":
+##                    if QSslCertificate is not None:
+##                        if self.__browser.page().hasValidSslInfo():
+##                            backgroundColor = Preferences.getWebBrowser(
+##                                "SaveUrlColor")
+##                    else:
+                    backgroundColor = Preferences.getWebBrowser(
+                        "SaveUrlColor")
+                highlight = QApplication.palette().color(QPalette.Highlight)
+                r = (highlight.red() + 2 * backgroundColor.red()) // 3
+                g = (highlight.green() + 2 * backgroundColor.green()) // 3
+                b = (highlight.blue() + 2 * backgroundColor.blue()) // 3
+                
+                loadingColor = QColor(r, g, b)
+                if abs(loadingColor.lightness() -
+                        backgroundColor.lightness()) < 20:
+                    # special handling for special color schemes (e.g Gaia)
+                    r = (2 * highlight.red() + backgroundColor.red()) // 3
+                    g = (2 * highlight.green() + backgroundColor.green()) // 3
+                    b = (2 * highlight.blue() + backgroundColor.blue()) // 3
+                    loadingColor = QColor(r, g, b)
+                
+                gradient = QLinearGradient(
+                    QPointF(0, 0), QPointF(self.width(), 0))
+                gradient.setColorAt(0, loadingColor)
+                gradient.setColorAt(progress / 100.0 - 0.000001, loadingColor)
+                gradient.setColorAt(progress / 100.0, backgroundColor)
+                p.setBrush(QPalette.Base, gradient)
+            
+            self.setPalette(p)
+        
+        E5LineEdit.paintEvent(self, evt)
+    
+    def focusOutEvent(self, evt):
+        """
+        Protected method to handle focus out event.
+        
+        @param evt reference to the focus event (QFocusEvent)
+        """
+        if self.text() == "" and self.__browser is not None:
+            self.__browserUrlChanged(self.__browser.url())
+        E5LineEdit.focusOutEvent(self, evt)
+    
+    def mousePressEvent(self, evt):
+        """
+        Protected method called by a mouse press event.
+        
+        @param evt reference to the mouse event (QMouseEvent)
+        """
+        if evt.button() == Qt.XButton1:
+            self.__mw.currentBrowser().triggerPageAction(
+                QWebEnginePage.Back)
+        elif evt.button() == Qt.XButton2:
+            self.__mw.currentBrowser().triggerPageAction(
+                QWebEnginePage.Forward)
+        else:
+            super(UrlBar, self).mousePressEvent(evt)
+    
+    def mouseDoubleClickEvent(self, evt):
+        """
+        Protected method to handle mouse double click events.
+        
+        @param evt reference to the mouse event (QMouseEvent)
+        """
+        if evt.button() == Qt.LeftButton:
+            self.selectAll()
+        else:
+            E5LineEdit.mouseDoubleClickEvent(self, evt)
+    
+    def keyPressEvent(self, evt):
+        """
+        Protected method to handle key presses.
+        
+        @param evt reference to the key press event (QKeyEvent)
+        """
+        if evt.key() == Qt.Key_Escape:
+            if self.__browser is not None:
+                self.setText(
+                    str(self.__browser.url().toEncoded(), encoding="utf-8"))
+                self.selectAll()
+            completer = self.completer()
+            if completer:
+                completer.popup().hide()
+            return
+        
+        currentText = self.text().strip()
+        if evt.key() in [Qt.Key_Enter, Qt.Key_Return] and \
+           not currentText.lower().startswith("http://"):
+            append = ""
+            if evt.modifiers() == Qt.KeyboardModifiers(Qt.ControlModifier):
+                append = ".com"
+            elif evt.modifiers() == Qt.KeyboardModifiers(
+                    Qt.ControlModifier | Qt.ShiftModifier):
+                append = ".org"
+            elif evt.modifiers() == Qt.KeyboardModifiers(Qt.ShiftModifier):
+                append = ".net"
+            
+            if append != "":
+                url = QUrl("http://www." + currentText)
+                host = url.host()
+                if not host.lower().endswith(append):
+                    host += append
+                    url.setHost(host)
+                    self.setText(url.toString())
+        
+        E5LineEdit.keyPressEvent(self, evt)
+    
+    def dragEnterEvent(self, evt):
+        """
+        Protected method to handle drag enter events.
+        
+        @param evt reference to the drag enter event (QDragEnterEvent)
+        """
+        mimeData = evt.mimeData()
+        if mimeData.hasUrls() or mimeData.hasText():
+            evt.acceptProposedAction()
+        
+        E5LineEdit.dragEnterEvent(self, evt)
+    
+    def dropEvent(self, evt):
+        """
+        Protected method to handle drop events.
+        
+        @param evt reference to the drop event (QDropEvent)
+        """
+        mimeData = evt.mimeData()
+        
+        url = QUrl()
+        if mimeData.hasUrls():
+            url = mimeData.urls()[0]
+        elif mimeData.hasText():
+            url = QUrl.fromEncoded(mimeData.text().encode("utf-8"),
+                                   QUrl.TolerantMode)
+        
+        if url.isEmpty() or not url.isValid():
+            E5LineEdit.dropEvent(self, evt)
+            return
+        
+        self.setText(str(url.toEncoded(), encoding="utf-8"))
+        self.selectAll()
+        
+        evt.acceptProposedAction()
+    
+    def __setRssButton(self):
+        """
+        Private slot to show the RSS button.
+        """
+        self.__rssButton.setVisible(self.__browser.checkRSS())
+    
+    def __rssClicked(self):
+        """
+        Private slot to handle clicking the RSS icon.
+        """
+        from WebBrowser.Feeds.FeedsDialog import FeedsDialog
+        feeds = self.__browser.getRSS()
+        dlg = FeedsDialog(feeds, self.__browser)
+        dlg.exec_()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/UrlBar/__init__.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2010 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Package implementing the URL bar widget.
+"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/VirusTotal/VirusTotalApi.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,416 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2011 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the <a href="http://www.virustotal.com">VirusTotal</a>
+API class.
+"""
+
+from __future__ import unicode_literals
+try:
+    str = unicode       # __IGNORE_EXCEPTION__
+except NameError:
+    pass
+
+import json
+
+from PyQt5.QtCore import QObject, QUrl, QByteArray, pyqtSignal, qVersion
+from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply
+
+from E5Gui import E5MessageBox
+
+import Preferences
+
+
+class VirusTotalAPI(QObject):
+    """
+    Class implementing the <a href="http://www.virustotal.com">VirusTotal</a>
+    API.
+    
+    @signal checkServiceKeyFinished(bool, str) emitted after the service key
+        check has been performed. It gives a flag indicating validity
+        (boolean) and an error message in case of a network error (string).
+    @signal submitUrlError(str) emitted with the error string, if the URL scan
+        submission returned an error.
+    @signal urlScanReport(str) emitted with the URL of the URL scan report page
+    @signal fileScanReport(str) emitted with the URL of the file scan report
+        page
+    """
+    checkServiceKeyFinished = pyqtSignal(bool, str)
+    submitUrlError = pyqtSignal(str)
+    urlScanReport = pyqtSignal(str)
+    fileScanReport = pyqtSignal(str)
+    
+    TestServiceKeyScanID = \
+        "4feed2c2e352f105f6188efd1d5a558f24aee6971bdf96d5fdb19c197d6d3fad"
+    
+    ServiceResult_ItemQueued = -2
+    ServiceResult_ItemNotPresent = 0
+    ServiceResult_ItemPresent = 1
+    
+    # HTTP Status Codes
+    ServiceCode_InvalidKey = 202
+    ServiceCode_RateLimitExceeded = 204
+    ServiceCode_InvalidPrivilege = 403
+    
+    GetFileReportPattern = "{0}://www.virustotal.com/vtapi/v2/file/report"
+    ScanUrlPattern = "{0}://www.virustotal.com/vtapi/v2/url/scan"
+    GetUrlReportPattern = "{0}://www.virustotal.com/vtapi/v2/url/report"
+    GetIpAddressReportPattern = \
+        "{0}://www.virustotal.com/vtapi/v2/ip-address/report"
+    GetDomainReportPattern = "{0}://www.virustotal.com/vtapi/v2/domain/report"
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object (QObject)
+        """
+        super(VirusTotalAPI, self).__init__(parent)
+        
+        self.__replies = []
+        
+        self.__loadSettings()
+        
+        self.__lastIP = ""
+        self.__lastDomain = ""
+        self.__ipReportDlg = None
+        self.__domainReportDlg = None
+    
+    def __loadSettings(self):
+        """
+        Private method to load the settings.
+        """
+        if Preferences.getWebBrowser("VirusTotalSecure"):
+            protocol = "https"
+        else:
+            protocol = "http"
+        self.GetFileReportUrl = self.GetFileReportPattern.format(protocol)
+        self.ScanUrlUrl = self.ScanUrlPattern.format(protocol)
+        self.GetUrlReportUrl = self.GetUrlReportPattern.format(protocol)
+        self.GetIpAddressReportUrl = self.GetIpAddressReportPattern.format(
+            protocol)
+        self.GetDomainReportUrl = self.GetDomainReportPattern.format(protocol)
+        
+        self.errorMessages = {
+            204: self.tr("Request limit has been reached."),
+            0: self.tr("Requested item is not present."),
+            -2: self.tr("Requested item is still queued."),
+        }
+    
+    def preferencesChanged(self):
+        """
+        Public slot to handle a change of preferences.
+        """
+        self.__loadSettings()
+    
+    def checkServiceKeyValidity(self, key, protocol=""):
+        """
+        Public method to check the validity of the given service key.
+        
+        @param key service key (string)
+        @param protocol protocol used to access VirusTotal (string)
+        """
+        if protocol == "":
+            urlStr = self.GetFileReportUrl
+        else:
+            urlStr = self.GetFileReportPattern.format(protocol)
+        request = QNetworkRequest(QUrl(urlStr))
+        request.setHeader(QNetworkRequest.ContentTypeHeader,
+                          "application/x-www-form-urlencoded")
+        params = QByteArray("apikey={0}&resource={1}".format(
+            key, self.TestServiceKeyScanID).encode("utf-8"))
+        
+        import WebBrowser.WebBrowserWindow
+        nam = WebBrowser.WebBrowserWindow.WebBrowserWindow\
+            .networkManager()
+        reply = nam.post(request, params)
+        reply.finished.connect(self.__checkServiceKeyValidityFinished)
+        self.__replies.append(reply)
+    
+    def __checkServiceKeyValidityFinished(self):
+        """
+        Private slot to determine the result of the service key validity check.
+        """
+        res = False
+        msg = ""
+        
+        reply = self.sender()
+        if reply.error() == QNetworkReply.NoError:
+            res = True
+        elif reply.error() == self.ServiceCode_InvalidKey:
+            res = False
+        else:
+            msg = reply.errorString()
+        self.__replies.remove(reply)
+        reply.deleteLater()
+        
+        self.checkServiceKeyFinished.emit(res, msg)
+    
+    def submitUrl(self, url):
+        """
+        Public method to submit an URL to be scanned.
+        
+        @param url url to be scanned (QUrl)
+        """
+        request = QNetworkRequest(QUrl(self.ScanUrlUrl))
+        request.setHeader(QNetworkRequest.ContentTypeHeader,
+                          "application/x-www-form-urlencoded")
+        params = QByteArray("apikey={0}&url=".format(
+            Preferences.getWebBrowser("VirusTotalServiceKey")).encode("utf-8"))\
+            .append(QUrl.toPercentEncoding(url.toString()))
+        
+        import WebBrowser.WebBrowserWindow
+        nam = WebBrowser.WebBrowserWindow.WebBrowserWindow\
+            .networkManager()
+        reply = nam.post(request, params)
+        reply.finished.connect(self.__submitUrlFinished)
+        self.__replies.append(reply)
+    
+    def __submitUrlFinished(self):
+        """
+        Private slot to determine the result of the URL scan submission.
+        """
+        reply = self.sender()
+        if reply.error() == QNetworkReply.NoError:
+            result = json.loads(str(reply.readAll(), "utf-8"))
+            if result["response_code"] == self.ServiceResult_ItemPresent:
+                self.urlScanReport.emit(result["permalink"])
+                self.__getUrlScanReportUrl(result["scan_id"])
+            else:
+                if result["response_code"] in self.errorMessages:
+                    msg = self.errorMessages[result["response_code"]]
+                else:
+                    msg = result["verbose_msg"]
+                self.submitUrlError.emit(msg)
+        elif reply.error() == self.ServiceCode_RateLimitExceeded:
+            self.submitUrlError.emit(
+                self.errorMessages[result[self.ServiceCode_RateLimitExceeded]])
+        else:
+            self.submitUrlError.emit(reply.errorString())
+        self.__replies.remove(reply)
+        reply.deleteLater()
+    
+    def __getUrlScanReportUrl(self, scanId):
+        """
+        Private method to get the report URL for a URL scan.
+        
+        @param scanId ID of the scan to get the report URL for (string)
+        """
+        request = QNetworkRequest(QUrl(self.GetUrlReportUrl))
+        request.setHeader(QNetworkRequest.ContentTypeHeader,
+                          "application/x-www-form-urlencoded")
+        params = QByteArray("apikey={0}&resource={1}".format(
+            Preferences.getWebBrowser("VirusTotalServiceKey"), scanId)
+            .encode("utf-8"))
+        
+        import WebBrowser.WebBrowserWindow
+        nam = WebBrowser.WebBrowserWindow.WebBrowserWindow\
+            .networkManager()
+        reply = nam.post(request, params)
+        reply.finished.connect(self.__getUrlScanReportUrlFinished)
+        self.__replies.append(reply)
+    
+    def __getUrlScanReportUrlFinished(self):
+        """
+        Private slot to determine the result of the URL scan report URL
+        request.
+        """
+        reply = self.sender()
+        if reply.error() == QNetworkReply.NoError:
+            result = json.loads(str(reply.readAll(), "utf-8"))
+            if "filescan_id" in result and result["filescan_id"] is not None:
+                self.__getFileScanReportUrl(result["filescan_id"])
+        self.__replies.remove(reply)
+        reply.deleteLater()
+    
+    def __getFileScanReportUrl(self, scanId):
+        """
+        Private method to get the report URL for a file scan.
+        
+        @param scanId ID of the scan to get the report URL for (string)
+        """
+        request = QNetworkRequest(QUrl(self.GetFileReportUrl))
+        request.setHeader(QNetworkRequest.ContentTypeHeader,
+                          "application/x-www-form-urlencoded")
+        params = QByteArray("apikey={0}&resource={1}".format(
+            Preferences.getWebBrowser("VirusTotalServiceKey"), scanId)
+            .encode("utf-8"))
+        
+        import WebBrowser.WebBrowserWindow
+        nam = WebBrowser.WebBrowserWindow.WebBrowserWindow\
+            .networkManager()
+        reply = nam.post(request, params)
+        reply.finished.connect(self.__getFileScanReportUrlFinished)
+        self.__replies.append(reply)
+    
+    def __getFileScanReportUrlFinished(self):
+        """
+        Private slot to determine the result of the file scan report URL
+        request.
+        """
+        reply = self.sender()
+        if reply.error() == QNetworkReply.NoError:
+            result = json.loads(str(reply.readAll(), "utf-8"))
+            self.fileScanReport.emit(result["permalink"])
+        self.__replies.remove(reply)
+        reply.deleteLater()
+    
+    def getIpAddressReport(self, ipAddress):
+        """
+        Public method to retrieve a report for an IP address.
+        
+        @param ipAddress valid IPv4 address in dotted quad notation
+        @type str
+        """
+        self.__lastIP = ipAddress
+        
+        queryItems = [
+            ("apikey", Preferences.getWebBrowser("VirusTotalServiceKey")),
+            ("ip", ipAddress),
+        ]
+        url = QUrl(self.GetIpAddressReportUrl)
+        if qVersion() >= "5.0.0":
+            from PyQt5.QtCore import QUrlQuery
+            query = QUrlQuery()
+            query.setQueryItems(queryItems)
+            url.setQuery(query)
+        else:
+            url.setQueryItems(queryItems)
+        request = QNetworkRequest(url)
+        
+        import WebBrowser.WebBrowserWindow
+        nam = WebBrowser.WebBrowserWindow.WebBrowserWindow\
+            .networkManager()
+        reply = nam.get(request)
+        reply.finished.connect(self.__getIpAddressReportFinished)
+        self.__replies.append(reply)
+    
+    def __getIpAddressReportFinished(self):
+        """
+        Private slot to process the IP address report data.
+        """
+        reply = self.sender()
+        if reply.error() == QNetworkReply.NoError:
+            result = json.loads(str(reply.readAll(), "utf-8"))
+            if result["response_code"] == 0:
+                E5MessageBox.information(
+                    None,
+                    self.tr("VirusTotal IP Address Report"),
+                    self.tr("""VirusTotal does not have any information for"""
+                            """ the given IP address."""))
+            elif result["response_code"] == -1:
+                E5MessageBox.information(
+                    None,
+                    self.tr("VirusTotal IP Address Report"),
+                    self.tr("""The submitted IP address is invalid."""))
+            else:
+                owner = result["as_owner"]
+                resolutions = result["resolutions"]
+                try:
+                    urls = result["detected_urls"]
+                except KeyError:
+                    urls = []
+                
+                from .VirusTotalIpReportDialog import VirusTotalIpReportDialog
+                self.__ipReportDlg = VirusTotalIpReportDialog(
+                    self.__lastIP, owner, resolutions, urls)
+                self.__ipReportDlg.show()
+        self.__replies.remove(reply)
+        reply.deleteLater()
+    
+    def getDomainReport(self, domain):
+        """
+        Public method to retrieve a report for a domain.
+        
+        @param domain domain name
+        @type str
+        """
+        self.__lastDomain = domain
+        
+        queryItems = [
+            ("apikey", Preferences.getWebBrowser("VirusTotalServiceKey")),
+            ("domain", domain),
+        ]
+        url = QUrl(self.GetDomainReportUrl)
+        if qVersion() >= "5.0.0":
+            from PyQt5.QtCore import QUrlQuery
+            query = QUrlQuery()
+            query.setQueryItems(queryItems)
+            url.setQuery(query)
+        else:
+            url.setQueryItems(queryItems)
+        request = QNetworkRequest(url)
+        
+        import WebBrowser.WebBrowserWindow
+        nam = WebBrowser.WebBrowserWindow.WebBrowserWindow\
+            .networkManager()
+        reply = nam.get(request)
+        reply.finished.connect(self.__getDomainReportFinished)
+        self.__replies.append(reply)
+    
+    def __getDomainReportFinished(self):
+        """
+        Private slot to process the IP address report data.
+        """
+        reply = self.sender()
+        if reply.error() == QNetworkReply.NoError:
+            result = json.loads(str(reply.readAll(), "utf-8"))
+            if result["response_code"] == 0:
+                E5MessageBox.information(
+                    None,
+                    self.tr("VirusTotal Domain Report"),
+                    self.tr("""VirusTotal does not have any information for"""
+                            """ the given domain."""))
+            elif result["response_code"] == -1:
+                E5MessageBox.information(
+                    None,
+                    self.tr("VirusTotal Domain Report"),
+                    self.tr("""The submitted domain address is invalid."""))
+            else:
+                resolutions = result["resolutions"]
+                try:
+                    urls = result["detected_urls"]
+                except KeyError:
+                    urls = []
+                try:
+                    subdomains = result["subdomains"]
+                except KeyError:
+                    subdomains = []
+                try:
+                    bdCategory = result["BitDefender category"]
+                except KeyError:
+                    bdCategory = self.tr("not available")
+                try:
+                    tmCategory = result["TrendMicro category"]
+                except KeyError:
+                    tmCategory = self.tr("not available")
+                try:
+                    wtsCategory = result["Websense ThreatSeeker category"]
+                except KeyError:
+                    wtsCategory = self.tr("not available")
+                try:
+                    whois = result["whois"]
+                except KeyError:
+                    whois = ""
+                
+                from .VirusTotalDomainReportDialog import \
+                    VirusTotalDomainReportDialog
+                self.__domainReportDlg = VirusTotalDomainReportDialog(
+                    self.__lastDomain, resolutions, urls, subdomains,
+                    bdCategory, tmCategory, wtsCategory, whois)
+                self.__domainReportDlg.show()
+        self.__replies.remove(reply)
+        reply.deleteLater()
+    
+    def close(self):
+        """
+        Public slot to close the API.
+        """
+        for reply in self.__replies:
+            reply.abort()
+        
+        self.__ipReportDlg and self.__ipReportDlg.close()
+        self.__domainReportDlg and self.__domainReportDlg.close()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/VirusTotal/VirusTotalDomainReportDialog.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,103 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2015 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog to show the VirusTotal domain report.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSlot, Qt
+from PyQt5.QtWidgets import QDialog, QTreeWidgetItem
+
+from .Ui_VirusTotalDomainReportDialog import Ui_VirusTotalDomainReportDialog
+
+import UI.PixmapCache
+
+
+class VirusTotalDomainReportDialog(QDialog, Ui_VirusTotalDomainReportDialog):
+    """
+    Class implementing a dialog to show the VirusTotal domain report.
+    """
+    def __init__(self, domain, resolutions, urls, subdomains,
+                 bdCategory, tmCategory, wtsCategory, whois, parent=None):
+        """
+        Constructor
+        
+        @param domain domain name
+        @type str
+        @param resolutions list of resolved host names
+        @type list of dict
+        @param urls list of detected URLs
+        @type list of dict
+        @param subdomains list of subdomains
+        @type list of str
+        @param bdCategory BitDefender categorization
+        @type str
+        @param tmCategory TrendMicro categorization
+        @type str
+        @param wtsCategory Websense ThreatSeeker categorization
+        @type str
+        @param whois whois information
+        @type str
+        @param parent reference to the parent widget
+        @type QWidget
+        """
+        super(VirusTotalDomainReportDialog, self).__init__(parent)
+        self.setupUi(self)
+        self.setWindowFlags(Qt.Window)
+        
+        self.headerLabel.setText(
+            self.tr("<b>Report for domain {0}</b>").format(domain))
+        self.headerPixmap.setPixmap(
+            UI.PixmapCache.getPixmap("virustotal.png"))
+        
+        for resolution in resolutions:
+            QTreeWidgetItem(
+                self.resolutionsList,
+                [resolution["ip_address"],
+                 resolution["last_resolved"].split()[0]]
+            )
+        self.resolutionsList.resizeColumnToContents(0)
+        self.resolutionsList.resizeColumnToContents(1)
+        self.resolutionsList.sortByColumn(0, Qt.AscendingOrder)
+        
+        if not urls:
+            self.detectedUrlsGroup.setVisible(False)
+        for url in urls:
+            QTreeWidgetItem(
+                self.urlsList,
+                [url["url"],
+                 self.tr("{0}/{1}", "positives / total").format(
+                    url["positives"], url["total"]),
+                 url["scan_date"].split()[0]]
+            )
+        self.urlsList.resizeColumnToContents(0)
+        self.urlsList.resizeColumnToContents(1)
+        self.urlsList.resizeColumnToContents(2)
+        self.urlsList.sortByColumn(0, Qt.AscendingOrder)
+        
+        if not subdomains:
+            self.subdomainsGroup.setVisible(False)
+        else:
+            self.subdomainsList.addItems(subdomains)
+            self.subdomainsList.sortItems()
+        
+        self.bdLabel.setText(bdCategory)
+        self.tmLabel.setText(tmCategory)
+        self.wtsLabel.setText(wtsCategory)
+        
+        self.__whois = whois
+        self.__whoisDomain = domain
+        self.whoisButton.setEnabled(bool(whois))
+    
+    @pyqtSlot()
+    def on_whoisButton_clicked(self):
+        """
+        Private slot to show the whois information.
+        """
+        from .VirusTotalWhoisDialog import VirusTotalWhoisDialog
+        dlg = VirusTotalWhoisDialog(self.__whoisDomain, self.__whois)
+        dlg.exec_()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/VirusTotal/VirusTotalDomainReportDialog.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,304 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>VirusTotalDomainReportDialog</class>
+ <widget class="QDialog" name="VirusTotalDomainReportDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>900</width>
+    <height>700</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Domain Report</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout_4">
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_4">
+     <item>
+      <widget class="QLabel" name="headerPixmap"/>
+     </item>
+     <item>
+      <widget class="QLabel" name="headerLabel">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="Line" name="line9_3">
+     <property name="frameShape">
+      <enum>QFrame::HLine</enum>
+     </property>
+     <property name="frameShadow">
+      <enum>QFrame::Sunken</enum>
+     </property>
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_3">
+     <item>
+      <widget class="QGroupBox" name="groupBox">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="title">
+        <string>Categorizations</string>
+       </property>
+       <layout class="QHBoxLayout" name="horizontalLayout">
+        <item>
+         <layout class="QGridLayout" name="gridLayout">
+          <item row="0" column="0">
+           <widget class="QLabel" name="label">
+            <property name="text">
+             <string notr="true">BitDefender:</string>
+            </property>
+           </widget>
+          </item>
+          <item row="0" column="1">
+           <widget class="QLabel" name="bdLabel">
+            <property name="text">
+             <string/>
+            </property>
+           </widget>
+          </item>
+          <item row="1" column="0">
+           <widget class="QLabel" name="label_2">
+            <property name="text">
+             <string notr="true">TrendMicro:</string>
+            </property>
+           </widget>
+          </item>
+          <item row="1" column="1">
+           <widget class="QLabel" name="tmLabel">
+            <property name="text">
+             <string/>
+            </property>
+           </widget>
+          </item>
+          <item row="2" column="0">
+           <widget class="QLabel" name="label_3">
+            <property name="text">
+             <string notr="true">Websense ThreatSeeker:</string>
+            </property>
+           </widget>
+          </item>
+          <item row="2" column="1">
+           <widget class="QLabel" name="wtsLabel">
+            <property name="text">
+             <string/>
+            </property>
+           </widget>
+          </item>
+         </layout>
+        </item>
+        <item>
+         <spacer name="horizontalSpacer">
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>690</width>
+            <height>20</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+       </layout>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="whoisButton">
+       <property name="text">
+        <string>Whois</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_2">
+     <item>
+      <widget class="QGroupBox" name="resolutionsGroup">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+         <horstretch>0</horstretch>
+         <verstretch>4</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="title">
+        <string>Resolutions</string>
+       </property>
+       <layout class="QVBoxLayout" name="verticalLayout_2">
+        <item>
+         <widget class="QTreeWidget" name="resolutionsList">
+          <property name="alternatingRowColors">
+           <bool>true</bool>
+          </property>
+          <property name="rootIsDecorated">
+           <bool>false</bool>
+          </property>
+          <property name="sortingEnabled">
+           <bool>true</bool>
+          </property>
+          <property name="allColumnsShowFocus">
+           <bool>true</bool>
+          </property>
+          <column>
+           <property name="text">
+            <string>IP-Address</string>
+           </property>
+          </column>
+          <column>
+           <property name="text">
+            <string>Resolved Date</string>
+           </property>
+          </column>
+         </widget>
+        </item>
+       </layout>
+      </widget>
+     </item>
+     <item>
+      <widget class="QGroupBox" name="subdomainsGroup">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+         <horstretch>0</horstretch>
+         <verstretch>4</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="title">
+        <string>Subdomains</string>
+       </property>
+       <layout class="QVBoxLayout" name="verticalLayout">
+        <item>
+         <widget class="QListWidget" name="subdomainsList">
+          <property name="alternatingRowColors">
+           <bool>true</bool>
+          </property>
+          <property name="sortingEnabled">
+           <bool>true</bool>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="detectedUrlsGroup">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>0</horstretch>
+       <verstretch>2</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="title">
+      <string>Detected URLs</string>
+     </property>
+     <layout class="QVBoxLayout" name="verticalLayout_3">
+      <item>
+       <widget class="QTreeWidget" name="urlsList">
+        <property name="alternatingRowColors">
+         <bool>true</bool>
+        </property>
+        <property name="rootIsDecorated">
+         <bool>false</bool>
+        </property>
+        <property name="sortingEnabled">
+         <bool>true</bool>
+        </property>
+        <property name="allColumnsShowFocus">
+         <bool>true</bool>
+        </property>
+        <column>
+         <property name="text">
+          <string>URL</string>
+         </property>
+        </column>
+        <column>
+         <property name="text">
+          <string>Scan Result</string>
+         </property>
+        </column>
+        <column>
+         <property name="text">
+          <string>Scan Date</string>
+         </property>
+        </column>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Close</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <tabstops>
+  <tabstop>whoisButton</tabstop>
+  <tabstop>resolutionsList</tabstop>
+  <tabstop>subdomainsList</tabstop>
+  <tabstop>urlsList</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>VirusTotalDomainReportDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>VirusTotalDomainReportDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/VirusTotal/VirusTotalIpReportDialog.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,72 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2015 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog to show the VirusTotal IP address report.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import Qt
+from PyQt5.QtWidgets import QDialog, QTreeWidgetItem
+
+from .Ui_VirusTotalIpReportDialog import Ui_VirusTotalIpReportDialog
+
+import UI.PixmapCache
+
+
+class VirusTotalIpReportDialog(QDialog, Ui_VirusTotalIpReportDialog):
+    """
+    Class implementing a dialog to show the VirusTotal IP address report.
+    """
+    def __init__(self, ip, owner, resolutions, urls, parent=None):
+        """
+        Constructor
+        
+        @param ip IP address
+        @type str
+        @param owner owner of the IP address
+        @type str
+        @param resolutions list of resolved host names
+        @type list of dict
+        @param urls list of detected URLs
+        @type list of dict
+        @param parent reference to the parent widget
+        @type QWidget
+        """
+        super(VirusTotalIpReportDialog, self).__init__(parent)
+        self.setupUi(self)
+        self.setWindowFlags(Qt.Window)
+        
+        self.headerLabel.setText(
+            self.tr("<b>Report for IP {0}</b>").format(ip))
+        self.headerPixmap.setPixmap(
+            UI.PixmapCache.getPixmap("virustotal.png"))
+        self.ownerLabel.setText(owner)
+        
+        for resolution in resolutions:
+            QTreeWidgetItem(
+                self.resolutionsList,
+                [resolution["hostname"],
+                 resolution["last_resolved"].split()[0]]
+            )
+        self.resolutionsList.resizeColumnToContents(0)
+        self.resolutionsList.resizeColumnToContents(1)
+        self.resolutionsList.sortByColumn(0, Qt.AscendingOrder)
+        
+        if not urls:
+            self.detectedUrlsGroup.setVisible(False)
+        for url in urls:
+            QTreeWidgetItem(
+                self.urlsList,
+                [url["url"],
+                 self.tr("{0}/{1}", "positives / total").format(
+                    url["positives"], url["total"]),
+                 url["scan_date"].split()[0]]
+            )
+        self.urlsList.resizeColumnToContents(0)
+        self.urlsList.resizeColumnToContents(1)
+        self.urlsList.resizeColumnToContents(2)
+        self.urlsList.sortByColumn(0, Qt.AscendingOrder)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/VirusTotal/VirusTotalIpReportDialog.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,208 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>VirusTotalIpReportDialog</class>
+ <widget class="QDialog" name="VirusTotalIpReportDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>800</width>
+    <height>600</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>IP Address Report</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_4">
+     <item>
+      <widget class="QLabel" name="headerPixmap"/>
+     </item>
+     <item>
+      <widget class="QLabel" name="headerLabel">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="Line" name="line9_3">
+     <property name="frameShape">
+      <enum>QFrame::HLine</enum>
+     </property>
+     <property name="frameShadow">
+      <enum>QFrame::Sunken</enum>
+     </property>
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <widget class="QLabel" name="label">
+       <property name="text">
+        <string>Owner:</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLabel" name="ownerLabel">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="text">
+        <string/>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="resolutionsGroup">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>0</horstretch>
+       <verstretch>4</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="title">
+      <string>Resolutions</string>
+     </property>
+     <layout class="QVBoxLayout" name="verticalLayout_2">
+      <item>
+       <widget class="QTreeWidget" name="resolutionsList">
+        <property name="alternatingRowColors">
+         <bool>true</bool>
+        </property>
+        <property name="rootIsDecorated">
+         <bool>false</bool>
+        </property>
+        <property name="sortingEnabled">
+         <bool>true</bool>
+        </property>
+        <property name="allColumnsShowFocus">
+         <bool>true</bool>
+        </property>
+        <column>
+         <property name="text">
+          <string>Hostname</string>
+         </property>
+        </column>
+        <column>
+         <property name="text">
+          <string>Resolved Date</string>
+         </property>
+        </column>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="detectedUrlsGroup">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>0</horstretch>
+       <verstretch>2</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="title">
+      <string>Detected URLs</string>
+     </property>
+     <layout class="QVBoxLayout" name="verticalLayout_3">
+      <item>
+       <widget class="QTreeWidget" name="urlsList">
+        <property name="alternatingRowColors">
+         <bool>true</bool>
+        </property>
+        <property name="rootIsDecorated">
+         <bool>false</bool>
+        </property>
+        <property name="sortingEnabled">
+         <bool>true</bool>
+        </property>
+        <property name="allColumnsShowFocus">
+         <bool>true</bool>
+        </property>
+        <column>
+         <property name="text">
+          <string>URL</string>
+         </property>
+        </column>
+        <column>
+         <property name="text">
+          <string>Scan Result</string>
+         </property>
+        </column>
+        <column>
+         <property name="text">
+          <string>Scan Date</string>
+         </property>
+        </column>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Close</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>VirusTotalIpReportDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>VirusTotalIpReportDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/VirusTotal/VirusTotalWhoisDialog.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,41 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2015 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog to show the 'whois' information.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtWidgets import QDialog
+
+from .Ui_VirusTotalWhoisDialog import Ui_VirusTotalWhoisDialog
+
+import UI.PixmapCache
+
+
+class VirusTotalWhoisDialog(QDialog, Ui_VirusTotalWhoisDialog):
+    """
+    Class implementing a dialog to show the 'whois' information.
+    """
+    def __init__(self, domain, whois, parent=None):
+        """
+        Constructor
+        
+        @param domain domain name
+        @type str
+        @param whois whois information
+        @type str
+        @param parent reference to the parent widget
+        @type QWidget
+        """
+        super(VirusTotalWhoisDialog, self).__init__(parent)
+        self.setupUi(self)
+        
+        self.headerLabel.setText(
+            self.tr("<b>Whois information for domain {0}</b>").format(domain))
+        self.headerPixmap.setPixmap(
+            UI.PixmapCache.getPixmap("virustotal.png"))
+        self.whoisEdit.setPlainText(whois)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/VirusTotal/VirusTotalWhoisDialog.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,110 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>VirusTotalWhoisDialog</class>
+ <widget class="QDialog" name="VirusTotalWhoisDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>500</width>
+    <height>400</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Whois Information</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_4">
+     <item>
+      <widget class="QLabel" name="headerPixmap"/>
+     </item>
+     <item>
+      <widget class="QLabel" name="headerLabel">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="Line" name="line9_3">
+     <property name="frameShape">
+      <enum>QFrame::HLine</enum>
+     </property>
+     <property name="frameShadow">
+      <enum>QFrame::Sunken</enum>
+     </property>
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QPlainTextEdit" name="whoisEdit">
+     <property name="tabChangesFocus">
+      <bool>true</bool>
+     </property>
+     <property name="lineWrapMode">
+      <enum>QPlainTextEdit::NoWrap</enum>
+     </property>
+     <property name="readOnly">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Close</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>VirusTotalWhoisDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>VirusTotalWhoisDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/VirusTotal/__init__.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2015 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Package containing the VirusTotal interface.
+"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/WebBrowserClearPrivateDataDialog.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,73 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog to select which private data to clear.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtWidgets import QDialog
+
+from .Ui_WebBrowserClearPrivateDataDialog import \
+    Ui_WebBrowserClearPrivateDataDialog
+
+
+class WebBrowserClearPrivateDataDialog(QDialog,
+                                       Ui_WebBrowserClearPrivateDataDialog):
+    """
+    Class implementing a dialog to select which private data to clear.
+    """
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(WebBrowserClearPrivateDataDialog, self).__init__(parent)
+        self.setupUi(self)
+        
+        msh = self.minimumSizeHint()
+        self.resize(max(self.width(), msh.width()), msh.height())
+    
+    def getData(self):
+        """
+        Public method to get the data from the dialog.
+        
+        @return tuple with flags indicating which data to clear
+            (browsing history, search history, favicons, disk cache, cookies,
+            passwords, web databases, downloads, flash, zoom values, SSL
+            certificate error exceptions) and the selected history period in
+            milliseconds (tuple of booleans and integer)
+        """
+        index = self.historyCombo.currentIndex()
+        if index == 0:
+            # last hour
+            historyPeriod = 60 * 60 * 1000
+        elif index == 1:
+            # last day
+            historyPeriod = 24 * 60 * 60 * 1000
+        elif index == 2:
+            # last week
+            historyPeriod = 7 * 24 * 60 * 60 * 1000
+        elif index == 3:
+            # last four weeks
+            historyPeriod = 4 * 7 * 24 * 60 * 60 * 1000
+        elif index == 4:
+            # clear all
+            historyPeriod = 0
+        
+        return (self.historyCheckBox.isChecked(),
+                self.searchCheckBox.isChecked(),
+                self.iconsCheckBox.isChecked(),
+                self.cacheCheckBox.isChecked(),
+                self.cookiesCheckBox.isChecked(),
+                self.passwordsCheckBox.isChecked(),
+                self.databasesCheckBox.isChecked(),
+                self.downloadsCheckBox.isChecked(),
+                self.flashCookiesCheckBox.isChecked(),
+                self.zoomCheckBox.isChecked(),
+                self.sslExceptionsCheckBox.isChecked(),
+                historyPeriod)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/WebBrowserClearPrivateDataDialog.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,293 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>WebBrowserClearPrivateDataDialog</class>
+ <widget class="QDialog" name="WebBrowserClearPrivateDataDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>305</width>
+    <height>380</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Clear Private Data</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QCheckBox" name="historyCheckBox">
+     <property name="toolTip">
+      <string>Select to clear the browsing history</string>
+     </property>
+     <property name="text">
+      <string>&amp;Browsing History</string>
+     </property>
+     <property name="checked">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QComboBox" name="historyCombo">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+         <horstretch>1</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="toolTip">
+        <string>Select the history period to be deleted</string>
+       </property>
+       <item>
+        <property name="text">
+         <string>Last Hour</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>Last Day</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>Last Week</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>Last 4 Weeks</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>Whole Period</string>
+        </property>
+       </item>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QCheckBox" name="searchCheckBox">
+     <property name="toolTip">
+      <string>Select to clear the search history</string>
+     </property>
+     <property name="text">
+      <string>&amp;Search History</string>
+     </property>
+     <property name="checked">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QCheckBox" name="downloadsCheckBox">
+     <property name="toolTip">
+      <string>Select to clear the download history</string>
+     </property>
+     <property name="text">
+      <string>Download &amp;History</string>
+     </property>
+     <property name="checked">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QCheckBox" name="cookiesCheckBox">
+     <property name="toolTip">
+      <string>Select to clear the cookies</string>
+     </property>
+     <property name="text">
+      <string>&amp;Cookies</string>
+     </property>
+     <property name="checked">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QCheckBox" name="cacheCheckBox">
+     <property name="toolTip">
+      <string>Select to clear the disk cache</string>
+     </property>
+     <property name="text">
+      <string>Cached &amp;Web Pages</string>
+     </property>
+     <property name="checked">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QCheckBox" name="iconsCheckBox">
+     <property name="toolTip">
+      <string>Select to clear the website icons</string>
+     </property>
+     <property name="text">
+      <string>Website &amp;Icons</string>
+     </property>
+     <property name="checked">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QCheckBox" name="passwordsCheckBox">
+     <property name="toolTip">
+      <string>Select to clear the saved passwords</string>
+     </property>
+     <property name="text">
+      <string>Saved &amp;Passwords</string>
+     </property>
+     <property name="checked">
+      <bool>false</bool>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QCheckBox" name="databasesCheckBox">
+     <property name="toolTip">
+      <string>Select to delete all web databases</string>
+     </property>
+     <property name="text">
+      <string>Web &amp;Databases</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QCheckBox" name="zoomCheckBox">
+     <property name="toolTip">
+      <string>Select to delete all remembered zoom settings</string>
+     </property>
+     <property name="text">
+      <string>&amp;Zoom Settings</string>
+     </property>
+     <property name="checked">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QCheckBox" name="sslExceptionsCheckBox">
+     <property name="text">
+      <string>SSL Certificate Error Exceptions</string>
+     </property>
+     <property name="checked">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="Line" name="line">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QCheckBox" name="flashCookiesCheckBox">
+     <property name="toolTip">
+      <string>Select to clear cookies set by the Adobe Flash Player</string>
+     </property>
+     <property name="text">
+      <string>Cookies from Adobe &amp;Flash Player</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <tabstops>
+  <tabstop>historyCheckBox</tabstop>
+  <tabstop>historyCombo</tabstop>
+  <tabstop>searchCheckBox</tabstop>
+  <tabstop>downloadsCheckBox</tabstop>
+  <tabstop>cookiesCheckBox</tabstop>
+  <tabstop>cacheCheckBox</tabstop>
+  <tabstop>iconsCheckBox</tabstop>
+  <tabstop>passwordsCheckBox</tabstop>
+  <tabstop>databasesCheckBox</tabstop>
+  <tabstop>zoomCheckBox</tabstop>
+  <tabstop>sslExceptionsCheckBox</tabstop>
+  <tabstop>flashCookiesCheckBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>WebBrowserClearPrivateDataDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>267</x>
+     <y>342</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>252</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>WebBrowserClearPrivateDataDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>294</x>
+     <y>342</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>252</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>historyCheckBox</sender>
+   <signal>toggled(bool)</signal>
+   <receiver>historyCombo</receiver>
+   <slot>setEnabled(bool)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>65</x>
+     <y>19</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>83</x>
+     <y>45</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/WebBrowserJavaScriptConsole.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,109 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a JavaScript console widget.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import Qt
+from PyQt5.QtGui import QTextCursor
+from PyQt5.QtWidgets import QTextEdit, QMenu
+from PyQt5.QtWebEngineWidgets import QWebEnginePage
+
+
+class WebBrowserJavaScriptConsole(QTextEdit):
+    """
+    Class implementing a JavaScript console widget.
+    """
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(WebBrowserJavaScriptConsole, self).__init__(parent)
+        self.setAcceptRichText(False)
+        self.setLineWrapMode(QTextEdit.NoWrap)
+        self.setReadOnly(True)
+        
+        # create the context menu
+        self.__menu = QMenu(self)
+        self.__menu.addAction(self.tr('Clear'), self.clear)
+        self.__menu.addAction(self.tr('Copy'), self.copy)
+        self.__menu.addSeparator()
+        self.__menu.addAction(self.tr('Select All'), self.selectAll)
+        
+        self.setContextMenuPolicy(Qt.CustomContextMenu)
+        self.customContextMenuRequested.connect(self.__handleShowContextMenu)
+        
+        self.__levelStrings = {
+            QWebEnginePage.InfoMessageLevel: self.tr("Info"),
+            QWebEnginePage.WarningMessageLevel: self.tr("Warning"),
+            QWebEnginePage.ErrorMessageLevel: self.tr("Error"),
+        }
+    
+    def __handleShowContextMenu(self, coord):
+        """
+        Private slot to show the context menu.
+        
+        @param coord the position of the mouse pointer (QPoint)
+        """
+        coord = self.mapToGlobal(coord)
+        self.__menu.popup(coord)
+    
+    def __appendText(self, txt):
+        """
+        Private method to append text to the end.
+        
+        @param txt text to insert (string)
+        """
+        tc = self.textCursor()
+        tc.movePosition(QTextCursor.End)
+        self.setTextCursor(tc)
+        self.insertPlainText(txt)
+        self.ensureCursorVisible()
+    
+    def keyPressEvent(self, evt):
+        """
+        Protected method handling key press events.
+        
+        @param evt key press event (QKeyEvent)
+        """
+        if evt.modifiers() == Qt.ControlModifier:
+            if evt.key() == Qt.Key_C:
+                self.copy()
+                evt.accept()
+                return
+            elif evt.key() == Qt.Key_A:
+                self.selectAll()
+                evt.accept()
+                return
+    
+    def javaScriptConsoleMessage(self, level, message, lineNumber,  sourceId):
+        """
+        Public method to show a console message.
+        
+        @param level severity
+        @type QWebEnginePage.JavaScriptConsoleMessageLevel
+        @param message message to be shown
+        @type str
+        @param lineNumber line number of an error
+        @type int
+        @param sourceId source URL causing the error
+        @type str
+        """
+        txt = self.tr("[{0}] {1}").format(
+            self.__levelStrings[level], message)
+        self.__appendText(txt)
+        
+        if lineNumber:
+            self.__appendText(self.tr(" at line {0}\n").format(lineNumber))
+        else:
+            self.__appendText("\n")
+        
+        if sourceId:
+            self.__appendText(self.tr("URL: {0}\n").format(sourceId))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/WebBrowserLanguagesDialog.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,187 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog to configure the preferred languages.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSlot, QLocale, QStringListModel
+from PyQt5.QtWidgets import QDialog
+
+from .Ui_WebBrowserLanguagesDialog import Ui_WebBrowserLanguagesDialog
+
+import Preferences
+
+
+class WebBrowserLanguagesDialog(QDialog, Ui_WebBrowserLanguagesDialog):
+    """
+    Class implementing a dialog to configure the preferred languages.
+    """
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(WebBrowserLanguagesDialog, self).__init__(parent)
+        self.setupUi(self)
+        
+        self.__model = QStringListModel()
+        self.languagesList.setModel(self.__model)
+        self.languagesList.selectionModel().currentChanged.connect(
+            self.__currentChanged)
+        
+        languages = Preferences.toList(Preferences.Prefs.settings.value(
+            "WebBrowser/AcceptLanguages", self.defaultAcceptLanguages()))
+        self.__model.setStringList(languages)
+        
+        allLanguages = []
+        for index in range(QLocale.C + 1, QLocale.LastLanguage + 1):
+            allLanguages += self.expand(QLocale.Language(index))
+        self.__allLanguagesModel = QStringListModel()
+        self.__allLanguagesModel.setStringList(allLanguages)
+        self.addCombo.setModel(self.__allLanguagesModel)
+    
+    def __currentChanged(self, current, previous):
+        """
+        Private slot to handle a change of the current selection.
+        
+        @param current index of the currently selected item (QModelIndex)
+        @param previous index of the previously selected item (QModelIndex)
+        """
+        self.removeButton.setEnabled(current.isValid())
+        row = current.row()
+        self.upButton.setEnabled(row > 0)
+        self.downButton.setEnabled(
+            row != -1 and row < self.__model.rowCount() - 1)
+
+    @pyqtSlot()
+    def on_upButton_clicked(self):
+        """
+        Private slot to move a language up.
+        """
+        currentRow = self.languagesList.currentIndex().row()
+        data = self.languagesList.currentIndex().data()
+        self.__model.removeRow(currentRow)
+        self.__model.insertRow(currentRow - 1)
+        self.__model.setData(self.__model.index(currentRow - 1), data)
+        self.languagesList.setCurrentIndex(self.__model.index(currentRow - 1))
+    
+    @pyqtSlot()
+    def on_downButton_clicked(self):
+        """
+        Private slot to move a language down.
+        """
+        currentRow = self.languagesList.currentIndex().row()
+        data = self.languagesList.currentIndex().data()
+        self.__model.removeRow(currentRow)
+        self.__model.insertRow(currentRow + 1)
+        self.__model.setData(self.__model.index(currentRow + 1), data)
+        self.languagesList.setCurrentIndex(self.__model.index(currentRow + 1))
+    
+    @pyqtSlot()
+    def on_removeButton_clicked(self):
+        """
+        Private slot to remove a language from the list of acceptable
+        languages.
+        """
+        currentRow = self.languagesList.currentIndex().row()
+        self.__model.removeRow(currentRow)
+    
+    @pyqtSlot()
+    def on_addButton_clicked(self):
+        """
+        Private slot to add a language to the list of acceptable languages.
+        """
+        language = self.addCombo.currentText()
+        if language in self.__model.stringList():
+            return
+        
+        self.__model.insertRow(self.__model.rowCount())
+        self.__model.setData(self.__model.index(self.__model.rowCount() - 1),
+                             language)
+        self.languagesList.setCurrentIndex(
+            self.__model.index(self.__model.rowCount() - 1))
+    
+    def accept(self):
+        """
+        Public method to accept the data entered.
+        """
+        result = self.__model.stringList()
+        if result == self.defaultAcceptLanguages():
+            Preferences.Prefs.settings.remove("WebBrowser/AcceptLanguages")
+        else:
+            Preferences.Prefs.settings.setValue(
+                "WebBrowser/AcceptLanguages", result)
+        super(WebBrowserLanguagesDialog, self).accept()
+    
+    @classmethod
+    def httpString(cls, languages):
+        """
+        Class method to convert a list of acceptable languages into a
+        byte array.
+       
+        The byte array can be sent along with the Accept-Language http header
+        (see RFC 2616).
+        
+        @param languages list of acceptable languages (list of strings)
+        @return converted list (QByteArray)
+        """
+        processed = []
+        qvalue = 1.0
+        for language in languages:
+            leftBracket = language.find('[')
+            rightBracket = language.find(']')
+            tag = language[leftBracket + 1:rightBracket]
+            if not processed:
+                processed.append(tag)
+            else:
+                processed.append("{0};q={1:.1f}".format(tag, qvalue))
+            if qvalue > 0.1:
+                qvalue -= 0.1
+        
+        return ", ".join(processed)
+    
+    @classmethod
+    def defaultAcceptLanguages(cls):
+        """
+        Class method to get the list of default accept languages.
+        
+        @return list of acceptable languages (list of strings)
+        """
+        language = QLocale.system().name()
+        if not language:
+            return []
+        else:
+            return cls.expand(QLocale(language).language())
+    
+    @classmethod
+    def expand(cls, language):
+        """
+        Class method to expand a language enum to a readable languages
+        list.
+        
+        @param language language number (QLocale.Language)
+        @return list of expanded language names (list of strings)
+        """
+        allLanguages = []
+        countries = [l.country() for l in QLocale.matchingLocales(
+            language, QLocale.AnyScript, QLocale.AnyCountry)]
+        languageString = "{0} [{1}]"\
+            .format(QLocale.languageToString(language),
+                    QLocale(language).name().split('_')[0])
+        allLanguages.append(languageString)
+        for country in countries:
+            languageString = "{0}/{1} [{2}]"\
+                .format(QLocale.languageToString(language),
+                        QLocale.countryToString(country),
+                        '-'.join(QLocale(language, country).name()
+                                 .split('_')).lower())
+            if languageString not in allLanguages:
+                allLanguages.append(languageString)
+        
+        return allLanguages
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/WebBrowserLanguagesDialog.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>WebBrowserLanguagesDialog</class>
+ <widget class="QDialog" name="WebBrowserLanguagesDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>400</width>
+    <height>300</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Languages</string>
+  </property>
+  <layout class="QGridLayout" name="gridLayout">
+   <item row="0" column="0">
+    <widget class="QLabel" name="label">
+     <property name="text">
+      <string>Languages in order of preference:</string>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="0" rowspan="4">
+    <widget class="QListView" name="languagesList"/>
+   </item>
+   <item row="1" column="1">
+    <widget class="QPushButton" name="upButton">
+     <property name="text">
+      <string>&amp;Up</string>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="1">
+    <widget class="QPushButton" name="downButton">
+     <property name="text">
+      <string>&amp;Down</string>
+     </property>
+    </widget>
+   </item>
+   <item row="3" column="1">
+    <widget class="QPushButton" name="removeButton">
+     <property name="text">
+      <string>&amp;Remove</string>
+     </property>
+    </widget>
+   </item>
+   <item row="4" column="1">
+    <spacer name="verticalSpacer">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>77</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+   <item row="5" column="0">
+    <widget class="QComboBox" name="addCombo"/>
+   </item>
+   <item row="5" column="1">
+    <widget class="QPushButton" name="addButton">
+     <property name="text">
+      <string>&amp;Add</string>
+     </property>
+    </widget>
+   </item>
+   <item row="6" column="0" colspan="2">
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <tabstops>
+  <tabstop>languagesList</tabstop>
+  <tabstop>upButton</tabstop>
+  <tabstop>downButton</tabstop>
+  <tabstop>removeButton</tabstop>
+  <tabstop>addCombo</tabstop>
+  <tabstop>addButton</tabstop>
+  <tabstop>buttonBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>WebBrowserLanguagesDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>WebBrowserLanguagesDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/WebBrowserPage.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,656 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2008 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+
+"""
+Module implementing the helpbrowser using QWebView.
+"""
+
+from __future__ import unicode_literals
+try:
+    str = unicode       # __IGNORE_EXCEPTION__
+except NameError:
+    pass
+
+from PyQt5.QtCore import pyqtSlot, pyqtSignal, QObject, QT_TRANSLATE_NOOP, \
+    QUrl, QBuffer, QIODevice, QFileInfo, Qt, QTimer, QEvent, \
+    QRect, QFile, QPoint, QByteArray, QEventLoop, qVersion
+from PyQt5.QtGui import QDesktopServices, QClipboard, QMouseEvent, QColor, \
+    QPalette
+from PyQt5.QtWidgets import qApp, QStyle, QMenu, QApplication, QInputDialog, \
+    QLineEdit, QLabel, QToolTip, QFrame, QDialog
+from PyQt5.QtPrintSupport import QPrinter, QPrintDialog
+from PyQt5.QtWebEngineWidgets import QWebEnginePage
+from PyQt5.QtWebChannel import QWebChannel
+from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest
+import sip
+
+from E5Gui import E5MessageBox, E5FileDialog
+
+import WebBrowser
+from WebBrowser.WebBrowserWindow import WebBrowserWindow
+
+from .JavaScript.ExternalJsObject import ExternalJsObject
+
+from .Tools.WebHitTestResult import WebHitTestResult
+from .Tools import Scripts
+
+import Preferences
+import UI.PixmapCache
+import Globals
+
+try:
+    from PyQt5.QtNetwork import QSslCertificate
+    SSL_AVAILABLE = True
+except ImportError:
+    SSL_AVAILABLE = False
+
+
+class WebBrowserPage(QWebEnginePage):
+    """
+    Class implementing an enhanced web page.
+    """
+##    _webPluginFactory = None
+##    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent parent widget of this window (QWidget)
+        """
+        super(WebBrowserPage, self).__init__(
+            WebBrowserWindow.webProfile(), parent)
+        
+        self.__setupWebChannel()
+        
+##        self.setPluginFactory(self.webPluginFactory())
+##        
+##        self.__lastRequest = None
+##        self.__lastRequestType = QWebPage.NavigationTypeOther
+##        
+##        from .Network.NetworkAccessManagerProxy import \
+##            NetworkAccessManagerProxy
+##        self.__proxy = NetworkAccessManagerProxy(self)
+##        self.__proxy.setWebPage(self)
+##        self.__proxy.setPrimaryNetworkAccessManager(
+##            WebBrowserWindow.networkManager())
+##        self.setNetworkAccessManager(self.__proxy)
+        
+        self.__sslConfiguration = None
+##        self.__proxy.finished.connect(self.__managerFinished)
+##        
+##        self.__adBlockedEntries = []
+##        self.loadStarted.connect(self.__loadStarted)
+        self.featurePermissionRequested.connect(
+            self.__featurePermissionRequested)
+        
+        self.authenticationRequired.connect(
+            WebBrowserWindow.networkManager().authentication)
+        
+        self.proxyAuthenticationRequired.connect(
+            WebBrowserWindow.networkManager().proxyAuthentication)
+        
+        self.fullScreenRequested.connect(self.__fullScreenRequested)
+    
+    def acceptNavigationRequest(self, url, type_, isMainFrame):
+        """
+        Public method to determine, if a request may be accepted.
+        
+        @param url URL to navigate to
+        @type QUrl
+        @param type_ type of the navigation request
+        @type QWebEnginePage.NavigationType
+        @param isMainFrame flag indicating, that the request originated from
+            the main frame
+        @type bool
+        @return flag indicating acceptance
+        @rtype bool
+        """
+##        self.__lastRequest = request
+##        if self.__lastRequest.url() != request.url() or \
+##           type_ != QWebPage.NavigationTypeOther:
+##            self.__lastRequestType = type_
+        
+        # TODO: Qt 5.6: move to handleUnknownProtocol
+        scheme = url.scheme()
+        if scheme == "mailto":
+            QDesktopServices.openUrl(url)
+            return False
+        
+        # AdBlock
+        if url.scheme() == "abp":
+            if WebBrowserWindow.adBlockManager().addSubscriptionFromUrl(url):
+                return False
+        
+##        
+##        if type_ == QWebPage.NavigationTypeFormResubmitted:
+##            res = E5MessageBox.yesNo(
+##                self.view(),
+##                self.tr("Resending POST request"),
+##                self.tr(
+##                    """In order to display the site, the request along with"""
+##                    """ all the data must be sent once again, which may lead"""
+##                    """ to some unexpected behaviour of the site e.g. the"""
+##                    """ same action might be performed once again. Do you"""
+##                    """ want to continue anyway?"""),
+##                icon=E5MessageBox.Warning)
+##            if not res:
+##                return False
+        
+        return QWebEnginePage.acceptNavigationRequest(self, url, type_,
+                                                      isMainFrame)
+##    
+##    def populateNetworkRequest(self, request):
+##        """
+##        Public method to add data to a network request.
+##        
+##        @param request reference to the network request object
+##            (QNetworkRequest)
+##        """
+##        try:
+##            request.setAttribute(QNetworkRequest.User + 100, self)
+##            if self.__lastRequest.url() == request.url():
+##                request.setAttribute(QNetworkRequest.User + 101,
+##                                     self.__lastRequestType)
+##                if self.__lastRequestType == \
+##                        QWebPage.NavigationTypeLinkClicked:
+##                    request.setRawHeader(b"X-Eric6-UserLoadAction",
+##                                         QByteArray(b"1"))
+##        except TypeError:
+##            pass
+##    
+##    def pageAttributeId(self):
+##        """
+##        Public method to get the attribute id of the page attribute.
+##        
+##        @return attribute id of the page attribute (integer)
+##        """
+##        return QNetworkRequest.User + 100
+##    
+##    def supportsExtension(self, extension):
+##        """
+##        Public method to check the support for an extension.
+##        
+##        @param extension extension to test for (QWebPage.Extension)
+##        @return flag indicating the support of extension (boolean)
+##        """
+##        try:
+##            if extension in [QWebPage.ErrorPageExtension,
+##                             QWebPage.ChooseMultipleFilesExtension]:
+##                return True
+##        except AttributeError:
+##            pass
+##        
+##        return QWebPage.supportsExtension(self, extension)
+##    
+##    def extension(self, extension, option, output):
+##        """
+##        Public method to implement a specific extension.
+##        
+##        @param extension extension to be executed (QWebPage.Extension)
+##        @param option provides input to the extension
+##            (QWebPage.ExtensionOption)
+##        @param output stores the output results (QWebPage.ExtensionReturn)
+##        @return flag indicating a successful call of the extension (boolean)
+##        """
+##        if extension == QWebPage.ChooseMultipleFilesExtension:
+##            info = sip.cast(option,
+##                            QWebPage.ChooseMultipleFilesExtensionOption)
+##            files = sip.cast(output,
+##                             QWebPage.ChooseMultipleFilesExtensionReturn)
+##            if info is None or files is None:
+##                return super(HelpWebPage, self).extension(
+##                    extension, option, output)
+##            
+##            suggestedFileName = ""
+##            if info.suggestedFileNames:
+##                suggestedFileName = info.suggestedFileNames[0]
+##            
+##            files.fileNames = E5FileDialog.getOpenFileNames(
+##                None,
+##                self.tr("Select files to upload..."),
+##                suggestedFileName)
+##            return True
+##        
+##        if extension == QWebPage.ErrorPageExtension:
+##            info = sip.cast(option, QWebPage.ErrorPageExtensionOption)
+##            
+##            errorPage = sip.cast(output, QWebPage.ErrorPageExtensionReturn)
+##            urlString = bytes(info.url.toEncoded()).decode()
+##            errorPage.baseUrl = info.url
+##            if info.domain == QWebPage.QtNetwork and \
+##               info.error == QNetworkReply.ProtocolUnknownError:
+##                url = QUrl(info.url)
+##                res = E5MessageBox.yesNo(
+##                    None,
+##                    self.tr("Protocol Error"),
+##                    self.tr("""Open external application for {0}-link?\n"""
+##                            """URL: {1}""").format(
+##                        url.scheme(), url.toString(
+##                            QUrl.PrettyDecoded | QUrl.RemovePassword)),
+##                    yesDefault=True)
+##                
+##                if res:
+##                    QDesktopServices.openUrl(url)
+##                return True
+##            elif info.domain == QWebPage.QtNetwork and \
+##                info.error == QNetworkReply.ContentAccessDenied and \
+##                    info.errorString.startswith("AdBlockRule:"):
+##                if info.frame != info.frame.page().mainFrame():
+##                    # content in <iframe>
+##                    docElement = info.frame.page().mainFrame()\
+##                        .documentElement()
+##                    for element in docElement.findAll("iframe"):
+##                        src = element.attribute("src")
+##                        if src in info.url.toString():
+##                            element.setAttribute("style", "display:none;")
+##                    return False
+##                else:
+##                    # the whole page is blocked
+##                    rule = info.errorString.replace("AdBlockRule:", "")
+##                    title = self.tr("Content blocked by AdBlock Plus")
+##                    message = self.tr(
+##                        "Blocked by rule: <i>{0}</i>").format(rule)
+##                    
+##                    htmlFile = QFile(":/html/adblockPage.html")
+##                    htmlFile.open(QFile.ReadOnly)
+##                    html = htmlFile.readAll()
+##                    html = html.replace(
+##                        "@FAVICON@", "qrc:icons/adBlockPlus16.png")
+##                    html = html.replace(
+##                        "@IMAGE@", "qrc:icons/adBlockPlus64.png")
+##                    html = html.replace("@TITLE@", title.encode("utf8"))
+##                    html = html.replace("@MESSAGE@", message.encode("utf8"))
+##                    errorPage.content = html
+##                    return True
+##            
+##            if info.domain == QWebPage.QtNetwork and \
+##               info.error == QNetworkReply.OperationCanceledError and \
+##               info.errorString == "eric6:No Error":
+##                return False
+##            
+##            if info.domain == QWebPage.WebKit and info.error == 203:
+##                # "Loading is handled by the media engine"
+##                return False
+##            
+##            title = self.tr("Error loading page: {0}").format(urlString)
+##            htmlFile = QFile(":/html/notFoundPage.html")
+##            htmlFile.open(QFile.ReadOnly)
+##            html = htmlFile.readAll()
+##            pixmap = qApp.style()\
+##                .standardIcon(QStyle.SP_MessageBoxWarning).pixmap(48, 48)
+##            imageBuffer = QBuffer()
+##            imageBuffer.open(QIODevice.ReadWrite)
+##            if pixmap.save(imageBuffer, "PNG"):
+##                html = html.replace("@IMAGE@", imageBuffer.buffer().toBase64())
+##            pixmap = qApp.style()\
+##                .standardIcon(QStyle.SP_MessageBoxWarning).pixmap(16, 16)
+##            imageBuffer = QBuffer()
+##            imageBuffer.open(QIODevice.ReadWrite)
+##            if pixmap.save(imageBuffer, "PNG"):
+##                html = html.replace(
+##                    "@FAVICON@", imageBuffer.buffer().toBase64())
+##            html = html.replace("@TITLE@", title.encode("utf8"))
+##            html = html.replace("@H1@", info.errorString.encode("utf8"))
+##            html = html.replace(
+##                "@H2@", self.tr("When connecting to: {0}.")
+##                .format(urlString).encode("utf8"))
+##            html = html.replace(
+##                "@LI-1@",
+##                self.tr("Check the address for errors such as "
+##                        "<b>ww</b>.example.org instead of "
+##                        "<b>www</b>.example.org").encode("utf8"))
+##            html = html.replace(
+##                "@LI-2@",
+##                self.tr(
+##                    "If the address is correct, try checking the network "
+##                    "connection.").encode("utf8"))
+##            html = html.replace(
+##                "@LI-3@",
+##                self.tr(
+##                    "If your computer or network is protected by a firewall "
+##                    "or proxy, make sure that the browser is permitted to "
+##                    "access the network.").encode("utf8"))
+##            html = html.replace(
+##                "@LI-4@",
+##                self.tr("If your cache policy is set to offline browsing,"
+##                        "only pages in the local cache are available.")
+##                .encode("utf8"))
+##            html = html.replace(
+##                "@BUTTON@", self.tr("Try Again").encode("utf8"))
+##            errorPage.content = html
+##            return True
+##        
+##        return QWebPage.extension(self, extension, option, output)
+##    
+##    def __loadStarted(self):
+##        """
+##        Private slot to handle the loadStarted signal.
+##        """
+##        self.__adBlockedEntries = []
+##    
+##    def addAdBlockRule(self, rule, url):
+##        """
+##        Public slot to add an AdBlock rule to the page.
+##        
+##        @param rule AdBlock rule to add (AdBlockRule)
+##        @param url URL that matched the rule (QUrl)
+##        """
+##        from .AdBlock.AdBlockPage import AdBlockedPageEntry
+##        entry = AdBlockedPageEntry(rule, url)
+##        if entry not in self.__adBlockedEntries:
+##            self.__adBlockedEntries.append(entry)
+##    
+##    def getAdBlockedPageEntries(self):
+##        """
+##        Public method to get the list of AdBlock page entries.
+##        
+##        @return list of AdBlock page entries (list of AdBlockedPageEntry)
+##        """
+##        return self.__adBlockedEntries
+    
+    # TODO: User Agent Manager
+##    def userAgent(self, resolveEmpty=False):
+##        """
+##        Public method to get the global user agent setting.
+##        
+##        @param resolveEmpty flag indicating to resolve an empty
+##            user agent (boolean)
+##        @return user agent string (string)
+##        """
+##        agent = Preferences.getWebBrowser("UserAgent")
+##        if agent == "" and resolveEmpty:
+##            agent = self.userAgentForUrl(QUrl())
+##        return agent
+##    
+##    def setUserAgent(self, agent):
+##        """
+##        Public method to set the global user agent string.
+##        
+##        @param agent new current user agent string (string)
+##        """
+##        Preferences.setWebBrowser("UserAgent", agent)
+##    
+##    def userAgentForUrl(self, url):
+##        """
+##        Public method to determine the user agent for the given URL.
+##        
+##        @param url URL to determine user agent for (QUrl)
+##        @return user agent string (string)
+##        """
+##        agent = WebBrowserWindow.userAgentsManager().userAgentForUrl(url)
+##        if agent == "":
+##            # no agent string specified for the given host -> use global one
+##            agent = Preferences.getWebBrowser("UserAgent")
+##            if agent == "":
+##                # no global agent string specified -> use default one
+##                agent = QWebPage.userAgentForUrl(self, url)
+##        return agent
+##    
+    # TODO: SSL
+##    def __managerFinished(self, reply):
+##        """
+##        Private slot to handle a finished reply.
+##        
+##        This slot is used to get SSL related information for a reply.
+##        
+##        @param reply reference to the finished reply (QNetworkReply)
+##        """
+##        try:
+##            frame = reply.request().originatingObject()
+##        except AttributeError:
+##            frame = None
+##        
+##        mainFrameRequest = frame == self.mainFrame()
+##        
+##        if mainFrameRequest and \
+##           self.__sslConfiguration is not None and \
+##           reply.url() == self.mainFrame().url():
+##            self.__sslConfiguration = None
+##        
+##        if reply.error() == QNetworkReply.NoError and \
+##           mainFrameRequest and \
+##           self.__sslConfiguration is None and \
+##           reply.url().scheme().lower() == "https" and \
+##           reply.url() == self.mainFrame().url():
+##            self.__sslConfiguration = reply.sslConfiguration()
+##            self.__sslConfiguration.url = QUrl(reply.url())
+##        
+##        if reply.error() == QNetworkReply.NoError and \
+##           mainFrameRequest and \
+##           reply.url() == self.mainFrame().url():
+##            modified = reply.header(QNetworkRequest.LastModifiedHeader)
+##            if modified and modified.isValid():
+##                manager = WebBrowserWindow.bookmarksManager()
+##                from .Bookmarks.BookmarkNode import BookmarkNode
+##                for bookmark in manager.bookmarksForUrl(reply.url()):
+##                    manager.setTimestamp(bookmark, BookmarkNode.TsModified,
+##                                         modified)
+    
+##    def getSslCertificate(self):
+##        """
+##        Public method to get a reference to the SSL certificate.
+##        
+##        @return amended SSL certificate (QSslCertificate)
+##        """
+##        if self.__sslConfiguration is None:
+##            return None
+##        
+##        sslInfo = self.__sslConfiguration.peerCertificate()
+##        sslInfo.url = QUrl(self.__sslConfiguration.url)
+##        return sslInfo
+##    
+##    def getSslCertificateChain(self):
+##        """
+##        Public method to get a reference to the SSL certificate chain.
+##        
+##        @return SSL certificate chain (list of QSslCertificate)
+##        """
+##        if self.__sslConfiguration is None:
+##            return []
+##        
+##        chain = self.__sslConfiguration.peerCertificateChain()
+##        return chain
+##    
+##    def getSslConfiguration(self):
+##        """
+##        Public method to return a reference to the current SSL configuration.
+##        
+##        @return reference to the SSL configuration in use (QSslConfiguration)
+##        """
+##        return self.__sslConfiguration
+##    
+##    def showSslInfo(self, pos):
+##        """
+##        Public slot to show some SSL information for the loaded page.
+##        
+##        @param pos position to show the info at (QPoint)
+##        """
+##        if SSL_AVAILABLE and self.__sslConfiguration is not None:
+##            from E5Network.E5SslInfoWidget import E5SslInfoWidget
+##            widget = E5SslInfoWidget(
+##                self.mainFrame().url(), self.__sslConfiguration, self.view())
+##            widget.showAt(pos)
+##        else:
+##            E5MessageBox.warning(
+##                self.view(),
+##                self.tr("SSL Info"),
+##                self.tr("""This site does not contain SSL information."""))
+##    
+    def hasValidSslInfo(self):
+        """
+        Public method to check, if the page has a valid SSL certificate.
+        
+        @return flag indicating a valid SSL certificate (boolean)
+        """
+        if self.__sslConfiguration is None:
+            return False
+        
+        certList = self.__sslConfiguration.peerCertificateChain()
+        if not certList:
+            return False
+        
+        certificateDict = Globals.toDict(
+            Preferences.Prefs.settings.value("Ssl/CaCertificatesDict"))
+        for server in certificateDict:
+            localCAList = QSslCertificate.fromData(certificateDict[server])
+            for cert in certList:
+                if cert in localCAList:
+                    return True
+        
+        for cert in certList:
+            if cert.isBlacklisted():
+                return False
+        
+        return True
+    
+##    @classmethod
+##    def webPluginFactory(cls):
+##        """
+##        Class method to get a reference to the web plug-in factory
+##        instance.
+##        
+##        @return reference to the web plug-in factory instance (WebPluginFactory
+##        """
+##        if cls._webPluginFactory is None:
+##            from .WebPlugins.WebPluginFactory import WebPluginFactory
+##            cls._webPluginFactory = WebPluginFactory()
+##        
+##        return cls._webPluginFactory
+##    
+##    def event(self, evt):
+##        """
+##        Public method implementing the event handler.
+##        
+##        @param evt reference to the event (QEvent)
+##        @return flag indicating that the event was handled (boolean)
+##        """
+##        if evt.type() == QEvent.Leave:
+##            # Fake a mouse move event just outside of the widget to trigger
+##            # the WebKit event handler's mouseMoved function. This implements
+##            # the interesting mouse-out behavior like invalidating scrollbars.
+##            fakeEvent = QMouseEvent(QEvent.MouseMove, QPoint(0, -1),
+##                                    Qt.NoButton, Qt.NoButton, Qt.NoModifier)
+##            return super(HelpWebPage, self).event(fakeEvent)
+##        
+##        return super(HelpWebPage, self).event(evt)
+    
+    def __featurePermissionRequested(self, url, feature):
+        """
+        Private slot handling a feature permission request.
+        
+        @param url url requesting the feature
+        @type QUrl
+        @param feature requested feature
+        @type QWebEnginePage.Feature
+        """
+        manager = WebBrowserWindow.featurePermissionManager()
+        manager.requestFeaturePermission(self, url, feature)
+    
+    def execJavaScript(self, script):
+        """
+        Public method to execute a JavaScript function synchroneously.
+        
+        @param script JavaScript script source to be executed
+        @type str
+        @return result of the script
+        @rtype depending upon script result
+        """
+        loop = QEventLoop()
+        resultDict = {"res": None}
+        QTimer.singleShot(500, loop.quit);
+        
+        def resultCallback(res, resDict=resultDict):
+            if loop and loop.isRunning():
+                resDict["res"] = res
+                loop.quit()
+        
+        self.runJavaScript(script, resultCallback)
+        
+        loop.exec_()
+        return resultDict["res"]
+    
+    def scroll(self, x, y):
+        """
+        Public method to scroll by the given amount of pixels.
+        
+        @param x horizontal scroll value
+        @type int
+        @param y vertical scroll value
+        @type int
+        """
+        self.runJavaScript(
+            "window.scrollTo(window.scrollX + {0}, window.scrollY + {1})"
+            .format(x, y)
+        )
+    
+    def hitTestContent(self, pos):
+        """
+        Public method to test the content at a specified position.
+        
+        @param pos position to execute the test at
+        @type QPoint
+        @return test result object
+        @rtype WebHitTestResult
+        """
+        return WebHitTestResult(self, pos)
+    
+    def __setupWebChannel(self):
+        """
+        Private method to setup a web channel to our external object.
+        """
+        oldChannel = self.webChannel()
+        newChannel = QWebChannel(self)
+        newChannel.registerObject("eric_object", ExternalJsObject(self))
+        self.setWebChannel(newChannel)
+        
+        if oldChannel:
+            del oldChannel.registeredObjects["eric_object"]
+            del oldChannel
+    
+    def certificateError(self, error):
+        """
+        Public method to handle SSL certificate errors.
+        
+        @param error object containing the certificate error information
+        @type QWebEngineCertificateError
+        @return flag indicating to ignore this error
+        @rtype bool
+        """
+        return WebBrowserWindow.networkManager().certificateError(
+            error, self.view())
+    
+    def __fullScreenRequested(self, request):
+        """
+        Private slot handling a full screen request.
+        """
+        self.view().requestFullScreen(request.toggleOn())
+        
+        accepted = request.toggleOn() == self.view().isFullScreen()
+        
+        if accepted:
+            request.accept()
+        else:
+            request.reject()
+    
+    ##############################################
+    ## Methods below deal with JavaScript messages
+    ##############################################
+    
+    def javaScriptConsoleMessage(self, level, message, lineNumber,  sourceId):
+        """
+        Public method to show a console message.
+        
+        @param level severity
+        @type QWebEnginePage.JavaScriptConsoleMessageLevel
+        @param message message to be shown
+        @type str
+        @param lineNumber line number of an error
+        @type int
+        @param sourceId source URL causing the error
+        @type str
+        """
+        self.view().mainWindow().javascriptConsole().javaScriptConsoleMessage(
+            level, message, lineNumber,  sourceId)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/WebBrowserSnap.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,48 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2010 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing functions to generate page previews.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import Qt
+from PyQt5.QtGui import QPixmap, QPainter
+
+
+def renderTabPreview(view, w, h):
+    """
+    Public function to render a pixmap of a page.
+    
+    @param view reference to the view to be previewed (QWebEngineView)
+    @param w width of the preview pixmap (integer)
+    @param h height of the preview pixmap (integer)
+    @return preview pixmap (QPixmap)
+    """
+    pageImage = __render(view, view.width(), view.height())
+    return pageImage.scaled(
+        w, h, Qt.IgnoreAspectRatio, Qt.SmoothTransformation)
+
+
+def __render(view, w, h):
+    """
+    Private function to render a pixmap of given size for a web page.
+    
+    @param view reference to the view to be previewed (QWebEngineView)
+    @param w width of the pixmap (integer)
+    @param h height of the pixmap (integer)
+    @return rendered pixmap (QPixmap)
+    """
+    # create the page image
+    pageImage = QPixmap(w, h)
+    pageImage.fill(Qt.transparent)
+    
+    # render it
+    p = QPainter(pageImage)
+    view.render(p)
+    p.end()
+    
+    return pageImage
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/WebBrowserTabBar.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,165 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2010 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a specialized tab bar for the web browser.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import Qt, QPoint, QTimer, QEvent
+from PyQt5.QtWidgets import QFrame, QLabel
+
+from E5Gui.E5TabWidget import E5WheelTabBar
+from E5Gui.E5PassivePopup import E5PassivePopup
+
+import Preferences
+
+
+class WebBrowserTabBar(E5WheelTabBar):
+    """
+    Class implementing the tab bar of the web browser.
+    """
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (WebBrowserTabWidget)
+        """
+        super(WebBrowserTabBar, self).__init__(parent)
+        
+        self.__tabWidget = parent
+        
+        self.__previewPopup = None
+        self.__currentTabPreviewIndex = -1
+        
+        self.setMouseTracking(True)
+    
+    def __showTabPreview(self):
+        """
+        Private slot to show the tab preview.
+        """
+        indexedBrowser = self.__tabWidget.browserAt(
+            self.__currentTabPreviewIndex)
+        currentBrowser = self.__tabWidget.currentBrowser()
+        
+        if indexedBrowser is None or currentBrowser is None:
+            return
+        
+        # no previews during load
+        if indexedBrowser.progress() != 0:
+            return
+        
+        w = self.tabSizeHint(self.__currentTabPreviewIndex).width()
+        h = int(w * currentBrowser.height() / currentBrowser.width())
+        
+        self.__previewPopup = E5PassivePopup(self)
+        self.__previewPopup.setFrameShape(QFrame.StyledPanel)
+        self.__previewPopup.setFrameShadow(QFrame.Plain)
+        self.__previewPopup.setFixedSize(w, h)
+        
+        from .WebBrowserSnap import renderTabPreview
+        label = QLabel()
+        label.setPixmap(renderTabPreview(indexedBrowser, w, h))
+        
+        self.__previewPopup.setView(label)
+        self.__previewPopup.layout().setAlignment(Qt.AlignTop)
+        self.__previewPopup.layout().setContentsMargins(0, 0, 0, 0)
+        
+        tr = self.tabRect(self.__currentTabPreviewIndex)
+        pos = QPoint(tr.x(), tr.y() + tr.height())
+        
+        self.__previewPopup.show(self.mapToGlobal(pos))
+    
+    def mouseMoveEvent(self, evt):
+        """
+        Protected method to handle mouse move events.
+        
+        @param evt reference to the mouse move event (QMouseEvent)
+        """
+        if self.count() == 1:
+            return
+        
+        super(WebBrowserTabBar, self).mouseMoveEvent(evt)
+        
+        if Preferences.getWebBrowser("ShowPreview"):
+            # Find the tab under the mouse
+            i = 0
+            tabIndex = -1
+            while i < self.count() and tabIndex == -1:
+                if self.tabRect(i).contains(evt.pos()):
+                    tabIndex = i
+                i += 1
+            
+            # If found and not the current tab then show tab preview
+            if tabIndex != -1 and \
+               tabIndex != self.currentIndex() and \
+               self.__currentTabPreviewIndex != tabIndex and \
+               evt.buttons() == Qt.NoButton:
+                self.__currentTabPreviewIndex = tabIndex
+                QTimer.singleShot(200, self.__showTabPreview)
+            
+            # If current tab or not found then hide previous tab preview
+            if tabIndex == self.currentIndex() or \
+               tabIndex == -1:
+                if self.__previewPopup is not None:
+                    self.__previewPopup.hide()
+                self.__currentTabPreviewIndex = -1
+    
+    def leaveEvent(self, evt):
+        """
+        Protected method to handle leave events.
+        
+        @param evt reference to the leave event (QEvent)
+        """
+        if Preferences.getWebBrowser("ShowPreview"):
+            # If leave tabwidget then hide previous tab preview
+            if self.__previewPopup is not None:
+                self.__previewPopup.hide()
+            self.__currentTabPreviewIndex = -1
+        
+        super(WebBrowserTabBar, self).leaveEvent(evt)
+    
+    def mousePressEvent(self, evt):
+        """
+        Protected method to handle mouse press events.
+        
+        @param evt reference to the mouse press event (QMouseEvent)
+        """
+        if Preferences.getWebBrowser("ShowPreview"):
+            if self.__previewPopup is not None:
+                self.__previewPopup.hide()
+            self.__currentTabPreviewIndex = -1
+        
+        super(WebBrowserTabBar, self).mousePressEvent(evt)
+    
+    def event(self, evt):
+        """
+        Public method to handle event.
+        
+        This event handler just handles the tooltip event and passes the
+        handling of all others to the superclass.
+        
+        @param evt reference to the event to be handled (QEvent)
+        @return flag indicating, if the event was handled (boolean)
+        """
+        if evt.type() == QEvent.ToolTip and \
+           Preferences.getWebBrowser("ShowPreview"):
+            # suppress tool tips if we are showing previews
+            evt.setAccepted(True)
+            return True
+        
+        return super(WebBrowserTabBar, self).event(evt)
+    
+    def tabRemoved(self, index):
+        """
+        Public slot to handle the removal of a tab.
+        
+        @param index index of the removed tab (integer)
+        """
+        if Preferences.getWebBrowser("ShowPreview"):
+            if self.__previewPopup is not None:
+                self.__previewPopup.hide()
+            self.__currentTabPreviewIndex = -1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/WebBrowserTabWidget.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,984 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2010 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the central widget showing the web pages.
+"""
+
+from __future__ import unicode_literals
+
+import os
+
+from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QUrl
+from PyQt5.QtGui import QIcon
+from PyQt5.QtWidgets import QWidget, QHBoxLayout, QMenu, QToolButton, QDialog
+from PyQt5.QtPrintSupport import QPrinter, QPrintDialog
+##from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest
+
+from E5Gui.E5TabWidget import E5TabWidget
+from E5Gui import E5MessageBox
+from E5Gui.E5Application import e5App
+
+from .WebBrowserView import WebBrowserView
+from .Tools import WebBrowserTools
+from . import WebInspector
+
+import UI.PixmapCache
+
+import Utilities
+import Preferences
+import Globals
+
+from eric6config import getConfig
+
+
+class WebBrowserTabWidget(E5TabWidget):
+    """
+    Class implementing the central widget showing the web pages.
+    
+    @signal sourceChanged(WebBrowserView, QUrl) emitted after the URL of a
+        browser has changed
+    @signal titleChanged(WebBrowserView, str) emitted after the title of a
+        browser has changed
+    @signal showMessage(str) emitted to show a message in the main window
+        status bar
+    @signal browserClosed(QWidget) emitted after a browser was closed
+    @signal browserZoomValueChanged(int) emitted to signal a change of the
+        current browser's zoom level
+    """
+    sourceChanged = pyqtSignal(WebBrowserView, QUrl)
+    titleChanged = pyqtSignal(WebBrowserView, str)
+    showMessage = pyqtSignal(str)
+    browserClosed = pyqtSignal(QWidget)
+    browserZoomValueChanged = pyqtSignal(int)
+    
+    def __init__(self, parent):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(WebBrowserTabWidget, self).__init__(parent, dnd=True)
+        
+        from .WebBrowserTabBar import WebBrowserTabBar
+        self.__tabBar = WebBrowserTabBar(self)
+        self.setCustomTabBar(True, self.__tabBar)
+        
+        self.__mainWindow = parent
+        
+        self.setUsesScrollButtons(True)
+        self.setDocumentMode(True)
+        self.setElideMode(Qt.ElideNone)
+        
+        from .ClosedTabsManager import ClosedTabsManager
+        self.__closedTabsManager = ClosedTabsManager(self)
+        self.__closedTabsManager.closedTabAvailable.connect(
+            self.__closedTabAvailable)
+        
+        from .UrlBar.StackedUrlBar import StackedUrlBar
+        self.__stackedUrlBar = StackedUrlBar(self)
+        self.__tabBar.tabMoved.connect(self.__stackedUrlBar.moveBar)
+        
+        self.__tabContextMenuIndex = -1
+        self.currentChanged[int].connect(self.__currentChanged)
+        self.setTabContextMenuPolicy(Qt.CustomContextMenu)
+        self.customTabContextMenuRequested.connect(self.__showContextMenu)
+        
+        self.__rightCornerWidget = QWidget(self)
+        self.__rightCornerWidgetLayout = QHBoxLayout(self.__rightCornerWidget)
+        self.__rightCornerWidgetLayout.setContentsMargins(0, 0, 0, 0)
+        self.__rightCornerWidgetLayout.setSpacing(0)
+        
+        self.__navigationMenu = QMenu(self)
+        self.__navigationMenu.aboutToShow.connect(self.__showNavigationMenu)
+        self.__navigationMenu.triggered.connect(self.__navigationMenuTriggered)
+        
+        self.__navigationButton = QToolButton(self)
+        self.__navigationButton.setIcon(
+            UI.PixmapCache.getIcon("1downarrow.png"))
+        self.__navigationButton.setToolTip(
+            self.tr("Show a navigation menu"))
+        self.__navigationButton.setPopupMode(QToolButton.InstantPopup)
+        self.__navigationButton.setMenu(self.__navigationMenu)
+        self.__navigationButton.setEnabled(False)
+        self.__rightCornerWidgetLayout.addWidget(self.__navigationButton)
+        
+        self.__closedTabsMenu = QMenu(self)
+        self.__closedTabsMenu.aboutToShow.connect(
+            self.__aboutToShowClosedTabsMenu)
+        
+        self.__closedTabsButton = QToolButton(self)
+        self.__closedTabsButton.setIcon(UI.PixmapCache.getIcon("trash.png"))
+        self.__closedTabsButton.setToolTip(
+            self.tr("Show a navigation menu for closed tabs"))
+        self.__closedTabsButton.setPopupMode(QToolButton.InstantPopup)
+        self.__closedTabsButton.setMenu(self.__closedTabsMenu)
+        self.__closedTabsButton.setEnabled(False)
+        self.__rightCornerWidgetLayout.addWidget(self.__closedTabsButton)
+        
+        self.__closeButton = QToolButton(self)
+        self.__closeButton.setIcon(UI.PixmapCache.getIcon("close.png"))
+        self.__closeButton.setToolTip(
+            self.tr("Close the current web browser"))
+        self.__closeButton.setEnabled(False)
+        self.__closeButton.clicked.connect(self.closeBrowser)
+        self.__rightCornerWidgetLayout.addWidget(self.__closeButton)
+        if Preferences.getUI("SingleCloseButton") or \
+           not hasattr(self, 'setTabsClosable'):
+            self.__closeButton.show()
+        else:
+            self.setTabsClosable(True)
+            self.tabCloseRequested.connect(self.closeBrowserAt)
+            self.__closeButton.hide()
+        
+        self.setCornerWidget(self.__rightCornerWidget, Qt.TopRightCorner)
+        
+        self.__newTabButton = QToolButton(self)
+        self.__newTabButton.setIcon(UI.PixmapCache.getIcon("plus.png"))
+        self.__newTabButton.setToolTip(
+            self.tr("Open a new web browser tab"))
+        self.setCornerWidget(self.__newTabButton, Qt.TopLeftCorner)
+        self.__newTabButton.clicked.connect(self.__newBrowser)
+        
+        self.__initTabContextMenu()
+        
+        self.__historyCompleter = None
+    
+    def __initTabContextMenu(self):
+        """
+        Private method to create the tab context menu.
+        """
+        self.__tabContextMenu = QMenu(self)
+        self.tabContextNewAct = self.__tabContextMenu.addAction(
+            UI.PixmapCache.getIcon("tabNew.png"),
+            self.tr('New Tab'), self.newBrowser)
+        self.__tabContextMenu.addSeparator()
+        self.leftMenuAct = self.__tabContextMenu.addAction(
+            UI.PixmapCache.getIcon("1leftarrow.png"),
+            self.tr('Move Left'), self.__tabContextMenuMoveLeft)
+        self.rightMenuAct = self.__tabContextMenu.addAction(
+            UI.PixmapCache.getIcon("1rightarrow.png"),
+            self.tr('Move Right'), self.__tabContextMenuMoveRight)
+        self.__tabContextMenu.addSeparator()
+        self.tabContextCloneAct = self.__tabContextMenu.addAction(
+            self.tr("Duplicate Page"), self.__tabContextMenuClone)
+        self.__tabContextMenu.addSeparator()
+        self.tabContextCloseAct = self.__tabContextMenu.addAction(
+            UI.PixmapCache.getIcon("tabClose.png"),
+            self.tr('Close'), self.__tabContextMenuClose)
+        self.tabContextCloseOthersAct = self.__tabContextMenu.addAction(
+            UI.PixmapCache.getIcon("tabCloseOther.png"),
+            self.tr("Close Others"), self.__tabContextMenuCloseOthers)
+        self.__tabContextMenu.addAction(
+            self.tr('Close All'), self.closeAllBrowsers)
+        self.__tabContextMenu.addSeparator()
+        self.__tabContextMenu.addAction(
+            UI.PixmapCache.getIcon("printPreview.png"),
+            self.tr('Print Preview'), self.__tabContextMenuPrintPreview)
+        self.__tabContextMenu.addAction(
+            UI.PixmapCache.getIcon("print.png"),
+            self.tr('Print'), self.__tabContextMenuPrint)
+        if Globals.isLinuxPlatform():
+            self.__tabContextMenu.addAction(
+                UI.PixmapCache.getIcon("printPdf.png"),
+                self.tr('Print as PDF'), self.__tabContextMenuPrintPdf)
+        self.__tabContextMenu.addSeparator()
+        self.__tabContextMenu.addAction(
+            UI.PixmapCache.getIcon("reload.png"),
+            self.tr('Reload All'), self.reloadAllBrowsers)
+        self.__tabContextMenu.addSeparator()
+        self.__tabContextMenu.addAction(
+            UI.PixmapCache.getIcon("addBookmark.png"),
+            self.tr('Bookmark All Tabs'), self.__mainWindow.bookmarkAll)
+        
+        self.__tabBackContextMenu = QMenu(self)
+        self.__tabBackContextMenu.addAction(
+            self.tr('Close All'), self.closeAllBrowsers)
+        self.__tabBackContextMenu.addAction(
+            UI.PixmapCache.getIcon("reload.png"),
+            self.tr('Reload All'), self.reloadAllBrowsers)
+        self.__tabBackContextMenu.addAction(
+            UI.PixmapCache.getIcon("addBookmark.png"),
+            self.tr('Bookmark All Tabs'), self.__mainWindow.bookmarkAll)
+        self.__tabBackContextMenu.addSeparator()
+        self.__restoreClosedTabAct = self.__tabBackContextMenu.addAction(
+            UI.PixmapCache.getIcon("trash.png"),
+            self.tr('Restore Closed Tab'), self.restoreClosedTab)
+        self.__restoreClosedTabAct.setEnabled(False)
+        self.__restoreClosedTabAct.setData(0)
+    
+    def __showContextMenu(self, coord, index):
+        """
+        Private slot to show the tab context menu.
+        
+        @param coord the position of the mouse pointer (QPoint)
+        @param index index of the tab the menu is requested for (integer)
+        """
+        coord = self.mapToGlobal(coord)
+        if index == -1:
+            self.__tabBackContextMenu.popup(coord)
+        else:
+            self.__tabContextMenuIndex = index
+            self.leftMenuAct.setEnabled(index > 0)
+            self.rightMenuAct.setEnabled(index < self.count() - 1)
+            
+            self.tabContextCloseOthersAct.setEnabled(self.count() > 1)
+            
+            self.__tabContextMenu.popup(coord)
+    
+    def __tabContextMenuMoveLeft(self):
+        """
+        Private method to move a tab one position to the left.
+        """
+        self.moveTab(self.__tabContextMenuIndex,
+                     self.__tabContextMenuIndex - 1)
+    
+    def __tabContextMenuMoveRight(self):
+        """
+        Private method to move a tab one position to the right.
+        """
+        self.moveTab(self.__tabContextMenuIndex,
+                     self.__tabContextMenuIndex + 1)
+    
+    def __tabContextMenuClone(self):
+        """
+        Private method to clone the selected tab.
+        """
+        idx = self.__tabContextMenuIndex
+        if idx < 0:
+            idx = self.currentIndex()
+        if idx < 0 or idx > self.count():
+            return
+        
+##        req = QNetworkRequest(self.widget(idx).url())
+##        req.setRawHeader(b"X-Eric6-UserLoadAction", b"1")
+##        self.newBrowser(None, (req, QNetworkAccessManager.GetOperation, b""))
+        url = self.widget(idx).url()
+        self.newBrowser(url)
+    
+    def __tabContextMenuClose(self):
+        """
+        Private method to close the selected tab.
+        """
+        self.closeBrowserAt(self.__tabContextMenuIndex)
+    
+    def __tabContextMenuCloseOthers(self):
+        """
+        Private slot to close all other tabs.
+        """
+        index = self.__tabContextMenuIndex
+        for i in list(range(self.count() - 1, index, -1)) + \
+                list(range(index - 1, -1, -1)):
+            self.closeBrowserAt(i)
+    
+    def __tabContextMenuPrint(self):
+        """
+        Private method to print the selected tab.
+        """
+        browser = self.widget(self.__tabContextMenuIndex)
+        self.printBrowser(browser)
+    
+    def __tabContextMenuPrintPdf(self):
+        """
+        Private method to print the selected tab as PDF.
+        """
+        browser = self.widget(self.__tabContextMenuIndex)
+        self.printBrowserPdf(browser)
+    
+    def __tabContextMenuPrintPreview(self):
+        """
+        Private method to show a print preview of the selected tab.
+        """
+        browser = self.widget(self.__tabContextMenuIndex)
+        self.printPreviewBrowser(browser)
+    
+    @pyqtSlot()
+    def __newBrowser(self):
+        """
+        Private slot to open a new browser tab.
+        """
+        self.newBrowser()
+    
+    # TODO: get rid of requestData
+    def newBrowser(self, link=None, requestData=None, position=-1):
+        """
+        Public method to create a new web browser tab.
+        
+        @param link link to be shown (string or QUrl)
+        @param requestData page load request data (LoadRequest)
+        @keyparam position position to create the new tab at or -1 to add it
+            to the end (integer)
+        """
+        if link is None:
+            linkName = ""
+        elif isinstance(link, QUrl):
+            linkName = link.toString()
+        else:
+            linkName = link
+        
+        from .UrlBar.UrlBar import UrlBar
+        urlbar = UrlBar(self.__mainWindow, self)
+        if self.__historyCompleter is None:
+            import WebBrowser.WebBrowserWindow
+            from .History.HistoryCompleter import HistoryCompletionModel, \
+                HistoryCompleter
+            self.__historyCompletionModel = HistoryCompletionModel(self)
+            self.__historyCompletionModel.setSourceModel(
+                WebBrowser.WebBrowserWindow.WebBrowserWindow.historyManager()
+                .historyFilterModel())
+            self.__historyCompleter = HistoryCompleter(
+                self.__historyCompletionModel, self)
+            self.__historyCompleter.activated[str].connect(self.__pathSelected)
+        urlbar.setCompleter(self.__historyCompleter)
+        urlbar.returnPressed.connect(self.__lineEditReturnPressed)
+        if position == -1:
+            self.__stackedUrlBar.addWidget(urlbar)
+        else:
+            self.__stackedUrlBar.insertWidget(position, urlbar)
+        
+        browser = WebBrowserView(self.__mainWindow, self)
+        urlbar.setBrowser(browser)
+        
+        browser.sourceChanged.connect(self.__sourceChanged)
+        browser.titleChanged.connect(self.__titleChanged)
+        browser.highlighted.connect(self.showMessage)
+        browser.backwardAvailable.connect(
+            self.__mainWindow.setBackwardAvailable)
+        browser.forwardAvailable.connect(self.__mainWindow.setForwardAvailable)
+        browser.loadStarted.connect(self.__loadStarted)
+        browser.loadFinished.connect(self.__loadFinished)
+        browser.iconChanged.connect(self.__iconChanged)
+        browser.search.connect(self.newBrowser)
+        browser.page().windowCloseRequested.connect(
+            self.__windowCloseRequested)
+        browser.zoomValueChanged.connect(self.browserZoomValueChanged)
+        
+        if position == -1:
+            index = self.addTab(browser, self.tr("..."))
+        else:
+            index = self.insertTab(position, browser, self.tr("..."))
+        self.setCurrentIndex(index)
+        
+        self.__mainWindow.closeAct.setEnabled(True)
+        self.__mainWindow.closeAllAct.setEnabled(True)
+        self.__closeButton.setEnabled(True)
+        self.__navigationButton.setEnabled(True)
+        
+        if not linkName and not requestData:
+            if Preferences.getWebBrowser("StartupBehavior") == 0:
+                linkName = Preferences.getWebBrowser("HomePage")
+            elif Preferences.getWebBrowser("StartupBehavior") == 1:
+                linkName = "eric:speeddial"
+        
+        if linkName:
+            browser.setSource(QUrl(linkName))
+            if not browser.documentTitle():
+                self.setTabText(index, self.__elide(linkName, Qt.ElideMiddle))
+                self.setTabToolTip(index, linkName)
+            else:
+                self.setTabText(
+                    index,
+                    self.__elide(browser.documentTitle().replace("&", "&&")))
+                self.setTabToolTip(index, browser.documentTitle())
+##        elif requestData:
+##            browser.load(requestData)
+    
+    def newBrowserAfter(self, browser, link=None, requestData=None):
+        """
+        Public method to create a new web browser tab after a given one.
+        
+        @param browser reference to the browser to add after (WebBrowserView)
+        @param link link to be shown (string or QUrl)
+        @param requestData page load request data (LoadRequest)
+        """
+        if browser:
+            position = self.indexOf(browser) + 1
+        else:
+            position = -1
+        self.newBrowser(link, requestData, position)
+    
+    def __showNavigationMenu(self):
+        """
+        Private slot to show the navigation button menu.
+        """
+        self.__navigationMenu.clear()
+        for index in range(self.count()):
+            act = self.__navigationMenu.addAction(
+                self.tabIcon(index), self.tabText(index))
+            act.setData(index)
+    
+    def __navigationMenuTriggered(self, act):
+        """
+        Private slot called to handle the navigation button menu selection.
+        
+        @param act reference to the selected action (QAction)
+        """
+        index = act.data()
+        if index is not None:
+            self.setCurrentIndex(index)
+    
+    def __windowCloseRequested(self):
+        """
+        Private slot to handle the windowCloseRequested signal of a browser.
+        """
+        page = self.sender()
+        if page is None:
+            return
+        
+        browser = page.view()
+        if browser is None:
+            return
+        
+        index = self.indexOf(browser)
+        self.closeBrowserAt(index)
+    
+    def reloadAllBrowsers(self):
+        """
+        Public slot to reload all browsers.
+        """
+        for index in range(self.count()):
+            browser = self.widget(index)
+            browser and browser.reload()
+    
+    @pyqtSlot()
+    def closeBrowser(self):
+        """
+        Public slot called to handle the close action.
+        """
+        self.closeBrowserAt(self.currentIndex())
+    
+    def closeAllBrowsers(self, shutdown=False):
+        """
+        Public slot called to handle the close all action.
+        
+        @param shutdown flag indicating a shutdown action
+        @type bool
+        """
+        for index in range(self.count() - 1, -1, -1):
+            self.closeBrowserAt(index, shutdown=shutdown)
+    
+    def closeBrowserAt(self, index, shutdown=False):
+        """
+        Public slot to close a browser based on its index.
+        
+        @param index index of browser to close
+        @type int
+        @param shutdown flag indicating a shutdown action
+        @type bool
+        """
+        browser = self.widget(index)
+        if browser is None:
+            return
+        
+##        if browser.isModified():
+##            ok = E5MessageBox.yesNo(
+##                self,
+##                self.tr("Do you really want to close this page?"),
+##                self.tr("""You have modified this page and when closing it"""
+##                        """ you would lose the modification.\nDo you really"""
+##                        """ want to close this page?"""))
+##            if not ok:
+##                return
+##        
+        urlbar = self.__stackedUrlBar.widget(index)
+        self.__stackedUrlBar.removeWidget(urlbar)
+        urlbar.deleteLater()
+        del urlbar
+        
+        self.__closedTabsManager.recordBrowser(browser, index)
+        
+        browser.closeWebInspector()
+        WebInspector.unregisterView(browser)
+        self.removeTab(index)
+        self.browserClosed.emit(browser)
+        browser.deleteLater()
+        del browser
+        
+        if self.count() == 0 and not shutdown:
+            self.newBrowser()
+        else:
+            self.currentChanged[int].emit(self.currentIndex())
+    
+    def currentBrowser(self):
+        """
+        Public method to get a reference to the current browser.
+        
+        @return reference to the current browser (WebBrowserView)
+        """
+        return self.currentWidget()
+    
+    def browserAt(self, index):
+        """
+        Public method to get a reference to the browser with the given index.
+        
+        @param index index of the browser to get (integer)
+        @return reference to the indexed browser (WebBrowserView)
+        """
+        return self.widget(index)
+    
+    def browsers(self):
+        """
+        Public method to get a list of references to all browsers.
+        
+        @return list of references to browsers (list of WebBrowserView)
+        """
+        li = []
+        for index in range(self.count()):
+            li.append(self.widget(index))
+        return li
+    
+    @pyqtSlot()
+    def printBrowser(self, browser=None):
+        """
+        Public slot called to print the displayed page.
+        
+        @param browser reference to the browser to be printed (WebBrowserView)
+        """
+        if browser is None:
+            browser = self.currentBrowser()
+        
+        printer = QPrinter(mode=QPrinter.HighResolution)
+        if Preferences.getPrinter("ColorMode"):
+            printer.setColorMode(QPrinter.Color)
+        else:
+            printer.setColorMode(QPrinter.GrayScale)
+        if Preferences.getPrinter("FirstPageFirst"):
+            printer.setPageOrder(QPrinter.FirstPageFirst)
+        else:
+            printer.setPageOrder(QPrinter.LastPageFirst)
+        printer.setPageMargins(
+            Preferences.getPrinter("LeftMargin") * 10,
+            Preferences.getPrinter("TopMargin") * 10,
+            Preferences.getPrinter("RightMargin") * 10,
+            Preferences.getPrinter("BottomMargin") * 10,
+            QPrinter.Millimeter
+        )
+        printerName = Preferences.getPrinter("PrinterName")
+        if printerName:
+            printer.setPrinterName(printerName)
+        printer.setResolution(Preferences.getPrinter("Resolution"))
+        
+        printDialog = QPrintDialog(printer, self)
+        if printDialog.exec_() == QDialog.Accepted:
+            browser.render(printer)
+    
+    @pyqtSlot()
+    def printBrowserPdf(self, browser=None):
+        """
+        Public slot called to print the displayed page to PDF.
+        
+        @param browser reference to the browser to be printed (HelpBrowser)
+        """
+        if browser is None:
+            browser = self.currentBrowser()
+        
+        printer = QPrinter(mode=QPrinter.HighResolution)
+        if Preferences.getPrinter("ColorMode"):
+            printer.setColorMode(QPrinter.Color)
+        else:
+            printer.setColorMode(QPrinter.GrayScale)
+        printerName = Preferences.getPrinter("PrinterName")
+        if printerName:
+            printer.setPrinterName(printerName)
+        printer.setOutputFormat(QPrinter.PdfFormat)
+        name = WebBrowserTools.getFileNameFromUrl(browser.url())
+        if name:
+            name = name.rsplit('.', 1)[0]
+            name += '.pdf'
+            printer.setOutputFileName(name)
+        printer.setResolution(Preferences.getPrinter("Resolution"))
+        
+        printDialog = QPrintDialog(printer, self)
+        if printDialog.exec_() == QDialog.Accepted:
+            browser.render(printer)
+    
+    @pyqtSlot()
+    def printPreviewBrowser(self, browser=None):
+        """
+        Public slot called to show a print preview of the displayed file.
+        
+        @param browser reference to the browser to be printed (HelpBrowserWV)
+        """
+        from PyQt5.QtPrintSupport import QPrintPreviewDialog
+        
+        if browser is None:
+            browser = self.currentBrowser()
+        
+        printer = QPrinter(mode=QPrinter.HighResolution)
+        if Preferences.getPrinter("ColorMode"):
+            printer.setColorMode(QPrinter.Color)
+        else:
+            printer.setColorMode(QPrinter.GrayScale)
+        if Preferences.getPrinter("FirstPageFirst"):
+            printer.setPageOrder(QPrinter.FirstPageFirst)
+        else:
+            printer.setPageOrder(QPrinter.LastPageFirst)
+        printer.setPageMargins(
+            Preferences.getPrinter("LeftMargin") * 10,
+            Preferences.getPrinter("TopMargin") * 10,
+            Preferences.getPrinter("RightMargin") * 10,
+            Preferences.getPrinter("BottomMargin") * 10,
+            QPrinter.Millimeter
+        )
+        printerName = Preferences.getPrinter("PrinterName")
+        if printerName:
+            printer.setPrinterName(printerName)
+        printer.setResolution(Preferences.getPrinter("Resolution"))
+        
+        preview = QPrintPreviewDialog(printer, self)
+        preview.paintRequested.connect(lambda p: browser.render(p))
+        preview.exec_()
+    
+    def __sourceChanged(self, url):
+        """
+        Private slot to handle a change of a browsers source.
+        
+        @param url URL of the new site (QUrl)
+        """
+        browser = self.sender()
+        
+        if browser is not None:
+            self.sourceChanged.emit(browser, url)
+    
+    def __titleChanged(self, title):
+        """
+        Private slot to handle a change of a browsers title.
+        
+        @param title new title (string)
+        """
+        browser = self.sender()
+        
+        if browser is not None and isinstance(browser, QWidget):
+            index = self.indexOf(browser)
+            if title == "":
+                title = browser.url().toString()
+            
+            self.setTabText(index, self.__elide(title.replace("&", "&&")))
+            self.setTabToolTip(index, title)
+        
+            self.titleChanged.emit(browser, title)
+    
+    def __elide(self, txt, mode=Qt.ElideRight, length=40):
+        """
+        Private method to elide some text.
+        
+        @param txt text to be elided (string)
+        @keyparam mode elide mode (Qt.TextElideMode)
+        @keyparam length amount of characters to be used (integer)
+        @return the elided text (string)
+        """
+        if mode == Qt.ElideNone or len(txt) < length:
+            return txt
+        elif mode == Qt.ElideLeft:
+            return "...{0}".format(txt[-length:])
+        elif mode == Qt.ElideMiddle:
+            return "{0}...{1}".format(txt[:length // 2], txt[-(length // 2):])
+        elif mode == Qt.ElideRight:
+            return "{0}...".format(txt[:length])
+        else:
+            # just in case
+            return txt
+    
+    def preferencesChanged(self):
+        """
+        Public slot to handle a change of preferences.
+        """
+        for browser in self.browsers():
+            browser.preferencesChanged()
+        
+        for urlbar in self.__stackedUrlBar.urlBars():
+            urlbar.preferencesChanged()
+        
+        if Preferences.getUI("SingleCloseButton"):
+            self.setTabsClosable(False)
+            try:
+                self.tabCloseRequested.disconnect(self.closeBrowserAt)
+            except TypeError:
+                pass
+            self.__closeButton.show()
+        else:
+            self.setTabsClosable(True)
+            self.tabCloseRequested.connect(self.closeBrowserAt)
+            self.__closeButton.hide()
+    
+    def __loadStarted(self):
+        """
+        Private method to handle the loadStarted signal.
+        """
+        browser = self.sender()
+        
+        if browser is not None:
+            index = self.indexOf(browser)
+            anim = self.animationLabel(
+                index, os.path.join(getConfig("ericPixDir"), "loading.gif"),
+                100)
+            if not anim:
+                loading = QIcon(os.path.join(getConfig("ericPixDir"),
+                                "loading.gif"))
+                self.setTabIcon(index, loading)
+            else:
+                self.setTabIcon(index, QIcon())
+            self.setTabText(index, self.tr("Loading..."))
+            self.setTabToolTip(index, self.tr("Loading..."))
+            self.showMessage.emit(self.tr("Loading..."))
+            
+            self.__mainWindow.setLoadingActions(True)
+    
+    def __loadFinished(self, ok):
+        """
+        Private method to handle the loadFinished signal.
+        
+        @param ok flag indicating the result (boolean)
+        """
+        browser = self.sender()
+        if not isinstance(browser, WebBrowserView):
+            return
+        
+        if browser is not None:
+            import WebBrowser.WebBrowserWindow
+            index = self.indexOf(browser)
+            self.resetAnimation(index)
+            self.setTabIcon(
+                index, WebBrowser.WebBrowserWindow.WebBrowserWindow.icon(
+                    browser.url()))
+            if ok:
+                self.showMessage.emit(self.tr("Finished loading"))
+            else:
+                self.showMessage.emit(self.tr("Failed to load"))
+            
+            self.__mainWindow.setLoadingActions(False)
+    
+    def __iconChanged(self):
+        """
+        Private slot to handle a change of the web site icon.
+        """
+        browser = self.sender()
+        
+        if browser is not None and isinstance(browser, QWidget):
+            self.setTabIcon(
+                self.indexOf(browser),
+                browser.icon())
+            self.__mainWindow.bookmarksManager().iconChanged(browser.url())
+    
+    def getSourceFileList(self):
+        """
+        Public method to get a list of all opened Qt help files.
+        
+        @return dictionary with tab id as key and host/namespace as value
+        """
+        sourceList = {}
+        for i in range(self.count()):
+            browser = self.widget(i)
+            if browser is not None and \
+               browser.source().isValid():
+                sourceList[i] = browser.source().host()
+        
+        return sourceList
+    
+    def shallShutDown(self):
+        """
+        Public method to check, if the application should be shut down.
+        
+        @return flag indicating a shut down (boolean)
+        """
+        if self.count() > 1 and Preferences.getWebBrowser(
+                "WarnOnMultipleClose"):
+            mb = E5MessageBox.E5MessageBox(
+                E5MessageBox.Information,
+                self.tr("Are you sure you want to close the window?"),
+                self.tr("""Are you sure you want to close the window?\n"""
+                        """You have %n tab(s) open.""", "", self.count()),
+                modal=True,
+                parent=self)
+            if self.__mainWindow.fromEric:
+                quitButton = mb.addButton(
+                    self.tr("&Close"), E5MessageBox.AcceptRole)
+                quitButton.setIcon(UI.PixmapCache.getIcon("close.png"))
+            else:
+                quitButton = mb.addButton(
+                    self.tr("&Quit"), E5MessageBox.AcceptRole)
+                quitButton.setIcon(UI.PixmapCache.getIcon("exit.png"))
+            closeTabButton = mb.addButton(
+                self.tr("C&lose Current Tab"), E5MessageBox.AcceptRole)
+            closeTabButton.setIcon(UI.PixmapCache.getIcon("tabClose.png"))
+            mb.addButton(E5MessageBox.Cancel)
+            mb.exec_()
+            if mb.clickedButton() == quitButton:
+                return True
+            else:
+                if mb.clickedButton() == closeTabButton:
+                    self.closeBrowser()
+                return False
+        
+        return True
+    
+    def stackedUrlBar(self):
+        """
+        Public method to get a reference to the stacked url bar.
+        
+        @return reference to the stacked url bar (StackedUrlBar)
+        """
+        return self.__stackedUrlBar
+    
+    def currentUrlBar(self):
+        """
+        Public method to get a reference to the current url bar.
+        
+        @return reference to the current url bar (UrlBar)
+        """
+        return self.__stackedUrlBar.currentWidget()
+    
+    def __lineEditReturnPressed(self):
+        """
+        Private slot to handle the entering of an URL.
+        """
+        edit = self.sender()
+        url = self.__guessUrlFromPath(edit.text())
+##        request = QNetworkRequest(url)
+##        request.setRawHeader(b"X-Eric6-UserLoadAction", b"1")
+        if e5App().keyboardModifiers() == Qt.AltModifier:
+            self.newBrowser(url)
+##            self.newBrowser(
+##                None, (request, QNetworkAccessManager.GetOperation, b""))
+        else:
+            self.currentBrowser().setSource(url)
+##            self.currentBrowser().setSource(
+##                None, (request, QNetworkAccessManager.GetOperation, b""))
+            self.currentBrowser().setFocus()
+    
+    def __pathSelected(self, path):
+        """
+        Private slot called when a URL is selected from the completer.
+        
+        @param path path to be shown (string)
+        """
+        url = self.__guessUrlFromPath(path)
+        self.currentBrowser().setSource(url)
+    
+    def __guessUrlFromPath(self, path):
+        """
+        Private method to guess an URL given a path string.
+        
+        @param path path string to guess an URL for (string)
+        @return guessed URL (QUrl)
+        """
+        manager = self.__mainWindow.openSearchManager()
+        path = Utilities.fromNativeSeparators(path)
+        url = manager.convertKeywordSearchToUrl(path)
+        if url.isValid():
+            return url
+        
+        try:
+            url = QUrl.fromUserInput(path)
+        except AttributeError:
+            url = QUrl(path)
+        
+        if url.scheme() == "about" and \
+           url.path() == "home":
+            url = QUrl("eric:home")
+        
+        # TODO: extend this logic to about:config (open config dialog)
+        
+        if url.scheme() in ["s", "search"]:
+            url = manager.currentEngine().searchUrl(url.path().strip())
+        
+        if url.scheme() != "" and \
+           (url.host() != "" or url.path() != ""):
+            return url
+        
+        urlString = Preferences.getWebBrowser("DefaultScheme") + path.strip()
+        url = QUrl.fromEncoded(urlString.encode("utf-8"), QUrl.TolerantMode)
+        
+        return url
+    
+    def __currentChanged(self, index):
+        """
+        Private slot to handle an index change.
+        
+        @param index new index (integer)
+        """
+        self.__stackedUrlBar.setCurrentIndex(index)
+        
+        browser = self.browserAt(index)
+        if browser is not None:
+            if browser.url() == "" and browser.hasFocus():
+                self.__stackedUrlBar.currentWidget.setFocus()
+            elif browser.url() != "":
+                browser.setFocus()
+    
+    def restoreClosedTab(self):
+        """
+        Public slot to restore the most recently closed tab.
+        """
+        if not self.canRestoreClosedTab():
+            return
+        
+        act = self.sender()
+        tab = self.__closedTabsManager.getClosedTabAt(act.data())
+        
+        self.newBrowser(tab.url.toString(), position=tab.position)
+    
+    def canRestoreClosedTab(self):
+        """
+        Public method to check, if closed tabs can be restored.
+        
+        @return flag indicating that closed tabs can be restored (boolean)
+        """
+        return self.__closedTabsManager.isClosedTabAvailable()
+    
+    def restoreAllClosedTabs(self):
+        """
+        Public slot to restore all closed tabs.
+        """
+        if not self.canRestoreClosedTab():
+            return
+        
+        for tab in self.__closedTabsManager.allClosedTabs():
+            self.newBrowser(tab.url.toString(), position=tab.position)
+        self.__closedTabsManager.clearList()
+    
+    def clearClosedTabsList(self):
+        """
+        Public slot to clear the list of closed tabs.
+        """
+        self.__closedTabsManager.clearList()
+    
+    def __aboutToShowClosedTabsMenu(self):
+        """
+        Private slot to populate the closed tabs menu.
+        """
+        fm = self.__closedTabsMenu.fontMetrics()
+        maxWidth = fm.width('m') * 40
+        
+        self.__closedTabsMenu.clear()
+        index = 0
+        for tab in self.__closedTabsManager.allClosedTabs():
+            title = fm.elidedText(tab.title, Qt.ElideRight, maxWidth)
+            self.__closedTabsMenu.addAction(
+                self.__mainWindow.icon(tab.url), title,
+                self.restoreClosedTab).setData(index)
+            index += 1
+        self.__closedTabsMenu.addSeparator()
+        self.__closedTabsMenu.addAction(
+            self.tr("Restore All Closed Tabs"), self.restoreAllClosedTabs)
+        self.__closedTabsMenu.addAction(
+            self.tr("Clear List"), self.clearClosedTabsList)
+    
+    def closedTabsManager(self):
+        """
+        Public slot to get a reference to the closed tabs manager.
+        
+        @return reference to the closed tabs manager (ClosedTabsManager)
+        """
+        return self.__closedTabsManager
+    
+    def __closedTabAvailable(self, avail):
+        """
+        Private slot to handle changes of the availability of closed tabs.
+        
+        @param avail flag indicating the availability of closed tabs (boolean)
+        """
+        self.__closedTabsButton.setEnabled(avail)
+        self.__restoreClosedTabAct.setEnabled(avail)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/WebBrowserView.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,2012 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2008 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+
+"""
+Module implementing the web browser using QWebEngineView.
+"""
+
+from __future__ import unicode_literals
+try:
+    str = unicode           # __IGNORE_EXCEPTION__
+except NameError:
+    pass
+
+from PyQt5.QtCore import pyqtSlot, pyqtSignal, QObject, QT_TRANSLATE_NOOP, \
+    QUrl, QBuffer, QIODevice, QFileInfo, Qt, QTimer, QEvent, \
+    QRect, QFile, QPoint, QByteArray, qVersion
+from PyQt5.QtGui import QDesktopServices, QClipboard, QMouseEvent, QColor, \
+    QPalette, QIcon, QContextMenuEvent
+from PyQt5.QtWidgets import qApp, QStyle, QMenu, QApplication, QInputDialog, \
+    QLineEdit, QLabel, QToolTip, QFrame, QDialog
+from PyQt5.QtPrintSupport import QPrinter, QPrintDialog
+from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest, QHostInfo
+from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEnginePage
+
+from E5Gui import E5MessageBox, E5FileDialog
+
+import WebBrowser
+from .WebBrowserPage import WebBrowserPage
+
+from .Tools.WebIconLoader import WebIconLoader
+from .Tools import WebBrowserTools, Scripts
+
+from .Network.LoadRequest import LoadRequest, LoadRequestOperations
+
+from . import WebInspector
+
+import Preferences
+import UI.PixmapCache
+import Globals
+
+try:
+    from PyQt5.QtNetwork import QSslCertificate
+    SSL_AVAILABLE = True
+except ImportError:
+    SSL_AVAILABLE = False
+
+
+class WebBrowserView(QWebEngineView):
+    """
+    Class implementing the web browser view widget.
+    
+    @signal sourceChanged(QUrl) emitted after the current URL has changed
+    @signal forwardAvailable(bool) emitted after the current URL has changed
+    @signal backwardAvailable(bool) emitted after the current URL has changed
+    @signal highlighted(str) emitted, when the mouse hovers over a link
+    @signal search(QUrl) emitted, when a search is requested
+    @signal zoomValueChanged(int) emitted to signal a change of the zoom value
+    @signal iconChanged() emitted to signal a changed web site icon
+    """
+    sourceChanged = pyqtSignal(QUrl)
+    forwardAvailable = pyqtSignal(bool)
+    backwardAvailable = pyqtSignal(bool)
+    highlighted = pyqtSignal(str)
+    search = pyqtSignal(QUrl)
+    zoomValueChanged = pyqtSignal(int)
+    iconChanged = pyqtSignal()
+    
+    ZoomLevels = [
+        30, 40, 50, 67, 80, 90,
+        100,
+        110, 120, 133, 150, 170, 200, 220, 233, 250, 270, 285, 300,
+    ]
+    ZoomLevelDefault = 100
+    
+    def __init__(self, mainWindow, parent=None, name=""):
+        """
+        Constructor
+        
+        @param mainWindow reference to the main window (WebBrowserWindow)
+        @param parent parent widget of this window (QWidget)
+        @param name name of this window (string)
+        """
+        super(WebBrowserView, self).__init__(parent)
+        self.setObjectName(name)
+        
+        self.__rwhvqt = None
+        self.installEventFilter(self)
+        
+        from WebBrowser.WebBrowserWindow import WebBrowserWindow
+        self.__speedDial = WebBrowserWindow.speedDial()
+        
+        self.__page = WebBrowserPage(self)
+        self.setPage(self.__page)
+        
+        self.__mw = mainWindow
+        self.__ctrlPressed = False
+        self.__isLoading = False
+        self.__progress = 0
+        self.__siteIconLoader = None
+        self.__siteIcon = QIcon()
+        self.__menu = QMenu(self)
+        self.__clickedPos = QPoint()
+        self.__firstLoad = False
+        
+        self.__currentZoom = 100
+        self.__zoomLevels = WebBrowserView.ZoomLevels[:]
+        
+##        self.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks)
+##        self.linkClicked.connect(self.setSource)
+##        
+        self.iconUrlChanged.connect(self.__iconUrlChanged)
+        self.urlChanged.connect(self.__urlChanged)
+##        self.statusBarMessage.connect(self.__statusBarMessage)
+        self.page().linkHovered.connect(self.__linkHovered)
+        
+        self.loadStarted.connect(self.__loadStarted)
+        self.loadProgress.connect(self.__loadProgress)
+        self.loadFinished.connect(self.__loadFinished)
+        
+##        self.page().setForwardUnsupportedContent(True)
+##        self.page().unsupportedContent.connect(self.__unsupportedContent)
+        
+##        self.page().databaseQuotaExceeded.connect(self.__databaseQuotaExceeded)
+        
+        self.__mw.openSearchManager().currentEngineChanged.connect(
+            self.__currentEngineChanged)
+        
+        self.setAcceptDrops(True)
+        
+        # TODO: Access Keys
+##        self.__enableAccessKeys = Preferences.getWebBrowser("AccessKeysEnabled")
+##        self.__accessKeysPressed = False
+##        self.__accessKeyLabels = []
+##        self.__accessKeyNodes = {}
+##        
+##        self.page().loadStarted.connect(self.__hideAccessKeys)
+##        self.page().scrollRequested.connect(self.__hideAccessKeys)
+        
+        self.__rss = []
+        
+        self.__clickedFrame = None
+        
+        self.__mw.personalInformationManager().connectPage(self.page())
+        
+        self.__inspector = None
+        WebInspector.registerView(self)
+        
+        self.grabGesture(Qt.PinchGesture)
+    
+    def __currentEngineChanged(self):
+        """
+        Private slot to track a change of the current search engine.
+        """
+        if self.url().toString() == "eric:home":
+            self.reload()
+    
+    def mainWindow(self):
+        """
+        Public method to get a reference to the main window.
+        
+        @return reference to the main window
+        @rtype WebBrowserWindow
+        """
+        return self.__mw
+    
+    def load(self, url):
+        """
+        Public method to load a web site.
+        
+        @param url URL to be loaded
+        @type QUrl or LoadRequest
+        """
+##        if isinstance(urlOrRequest, QUrl):
+        super(WebBrowserView, self).load(url)
+        
+        if not self.__firstLoad:
+            self.__firstLoad = True
+            WebInspector.pushView(self)
+##        elif isinstance(urlOrRequest, LoadRequest):
+##            reqUrl = urlOrRequest.url()
+##            if reqUrl.isEmpty():
+##                return
+##            
+##            if reqUrl.scheme() == "javascript":
+##                script = reqUrl.toString()[11:]
+##                # check if the javascript script is percent encode
+##                # i.e. it contains '%' characters
+##                if '%' in script:
+##                    script = QUrl.fromPercentEncoding(
+##                        QByteArray(script.encode("utf-8")))
+##                self.page().runJavaScript(script)
+##                return
+##            
+##            if self.__isUrlValid(reqUrl):
+##                self.loadRequest(urlOrRequest)
+##                return
+##            
+##            # ensure proper loading of hosts without a '.'
+##            if not reqUrl.isEmpty() and \
+##               reqUrl.scheme() and \
+##               not WebBrowserTools.containsSpace(reqUrl.path()) and \
+##               '.' not in reqUrl.path():
+##                u = QUrl("http://" + reqUrl.path())
+##                if u.isValid():
+##                    info = QHostInfo.fromName(u.path())
+##                    if info.error() == QHostInfo.NoError:
+##                        req = LoadRequest(urlOrRequest)
+##                        req.setUrl(u)
+##                        self.loadRequest(req)
+##                        return
+##    
+##    def loadRequest(self, req):
+##        """
+##        Public method to load a page via a load request object.
+##        
+##        @param req loaf request object
+##        @type LoadRequest
+##        """
+##        if req.Operation == LoadRequestOperations.GetOperation:
+##            self.load(req.url())
+##        else:
+##            self.page().runJavaScript(
+##                Scripts.sendPostData(req.url(), req.data()))
+    
+    # TODO: eliminate requestData, add param to get rid of __ctrlPressed
+    def setSource(self, name, requestData=None):
+        """
+        Public method used to set the source to be displayed.
+        
+        @param name filename to be shown (QUrl)
+        @param requestData tuple containing the request data (QNetworkRequest,
+            QNetworkAccessManager.Operation, QByteArray)
+        """
+##        if (name is None or not name.isValid()) and requestData is None:
+        if name is None or not name.isValid():
+            return
+        
+##        if name is None and requestData is not None:
+##            name = requestData[0].url()
+##        
+        if self.__ctrlPressed:
+            # open in a new window
+            self.__mw.newTab(name)
+            self.__ctrlPressed = False
+            return
+        
+        if not name.scheme():
+            name.setUrl(Preferences.getWebBrowser("DefaultScheme") +
+                        name.toString())
+        
+        if len(name.scheme()) == 1 or \
+           name.scheme() == "file":
+            # name is a local file
+            if name.scheme() and len(name.scheme()) == 1:
+                # it is a local path on win os
+                name = QUrl.fromLocalFile(name.toString())
+            
+            if not QFileInfo(name.toLocalFile()).exists():
+                E5MessageBox.critical(
+                    self,
+                    self.tr("eric6 Web Browser"),
+                    self.tr(
+                        """<p>The file <b>{0}</b> does not exist.</p>""")
+                    .format(name.toLocalFile()))
+                return
+
+            if name.toLocalFile().endswith(".pdf") or \
+               name.toLocalFile().endswith(".PDF") or \
+               name.toLocalFile().endswith(".chm") or \
+               name.toLocalFile().endswith(".CHM"):
+                started = QDesktopServices.openUrl(name)
+                if not started:
+                    E5MessageBox.critical(
+                        self,
+                        self.tr("eric6 Web Browser"),
+                        self.tr(
+                            """<p>Could not start a viewer"""
+                            """ for file <b>{0}</b>.</p>""")
+                        .format(name.path()))
+                return
+        elif name.scheme() in ["mailto"]:
+            started = QDesktopServices.openUrl(name)
+            if not started:
+                E5MessageBox.critical(
+                    self,
+                    self.tr("eric6 Web Browser"),
+                    self.tr(
+                        """<p>Could not start an application"""
+                        """ for URL <b>{0}</b>.</p>""")
+                    .format(name.toString()))
+            return
+        else:
+            if name.toString().endswith(".pdf") or \
+               name.toString().endswith(".PDF") or \
+               name.toString().endswith(".chm") or \
+               name.toString().endswith(".CHM"):
+                started = QDesktopServices.openUrl(name)
+                if not started:
+                    E5MessageBox.critical(
+                        self,
+                        self.tr("eric6 Web Browser"),
+                        self.tr(
+                            """<p>Could not start a viewer"""
+                            """ for file <b>{0}</b>.</p>""")
+                        .format(name.path()))
+                return
+        
+        if requestData is not None:
+            self.load(*requestData)
+        else:
+            self.load(name)
+
+    def source(self):
+        """
+        Public method to return the URL of the loaded page.
+        
+        @return URL loaded in the help browser (QUrl)
+        """
+        return self.url()
+    
+    def documentTitle(self):
+        """
+        Public method to return the title of the loaded page.
+        
+        @return title (string)
+        """
+        return self.title()
+    
+    def backward(self):
+        """
+        Public slot to move backwards in history.
+        """
+        self.triggerPageAction(QWebEnginePage.Back)
+        self.__urlChanged(self.history().currentItem().url())
+    
+    def forward(self):
+        """
+        Public slot to move forward in history.
+        """
+        self.triggerPageAction(QWebEnginePage.Forward)
+        self.__urlChanged(self.history().currentItem().url())
+    
+    def home(self):
+        """
+        Public slot to move to the first page loaded.
+        """
+        homeUrl = QUrl(Preferences.getWebBrowser("HomePage"))
+        self.setSource(homeUrl)
+        self.__urlChanged(self.history().currentItem().url())
+    
+    def reload(self):
+        """
+        Public slot to reload the current page.
+        """
+        self.triggerPageAction(QWebEnginePage.Reload)
+    
+    def reloadBypassingCache(self):
+        """
+        Public slot to reload the current page bypassing the cache.
+        """
+        self.triggerPageAction(QWebEnginePage.ReloadAndBypassCache)
+    
+    def copy(self):
+        """
+        Public slot to copy the selected text.
+        """
+        self.triggerPageAction(QWebEnginePage.Copy)
+    
+    def cut(self):
+        """
+        Public slot to cut the selected text.
+        """
+        self.triggerPageAction(QWebEnginePage.Cut)
+    
+    def paste(self):
+        """
+        Public slot to paste text from the clipboard.
+        """
+        self.triggerPageAction(QWebEnginePage.Paste)
+    
+    def undo(self):
+        """
+        Public slot to undo the last edit action.
+        """
+        self.triggerPageAction(QWebEnginePage.Undo)
+    
+    def redo(self):
+        """
+        Public slot to redo the last edit action.
+        """
+        self.triggerPageAction(QWebEnginePage.Redo)
+    
+    def selectAll(self):
+        """
+        Public slot to select all text.
+        """
+        self.triggerPageAction(QWebEnginePage.SelectAll)
+    
+    def isForwardAvailable(self):
+        """
+        Public method to determine, if a forward move in history is possible.
+        
+        @return flag indicating move forward is possible (boolean)
+        """
+        return self.history().canGoForward()
+    
+    def isBackwardAvailable(self):
+        """
+        Public method to determine, if a backwards move in history is possible.
+        
+        @return flag indicating move backwards is possible (boolean)
+        """
+        return self.history().canGoBack()
+    
+    def __levelForZoom(self, zoom):
+        """
+        Private method determining the zoom level index given a zoom factor.
+        
+        @param zoom zoom factor (integer)
+        @return index of zoom factor (integer)
+        """
+        try:
+            index = self.__zoomLevels.index(zoom)
+        except ValueError:
+            for index in range(len(self.__zoomLevels)):
+                if zoom <= self.__zoomLevels[index]:
+                    break
+        return index
+    
+    def setZoomValue(self, value, saveValue=True):
+        """
+        Public method to set the zoom value.
+        
+        @param value zoom value (integer)
+        @keyparam saveValue flag indicating to save the zoom value with the
+            zoom manager
+        @type bool
+        """
+        if value != self.__currentZoom:
+            self.setZoomFactor(value / 100.0)
+            self.__currentZoom = value
+            if saveValue and not self.__mw.isPrivate():
+                from .ZoomManager import ZoomManager
+                ZoomManager.instance().setZoomValue(self.url(), value)
+            self.zoomValueChanged.emit(value)
+    
+    def zoomValue(self):
+        """
+        Public method to get the current zoom value.
+        
+        @return zoom value (integer)
+        """
+        val = self.zoomFactor() * 100
+        return int(val)
+    
+    def zoomIn(self):
+        """
+        Public slot to zoom into the page.
+        """
+        index = self.__levelForZoom(self.__currentZoom)
+        if index < len(self.__zoomLevels) - 1:
+            self.setZoomValue(self.__zoomLevels[index + 1])
+    
+    def zoomOut(self):
+        """
+        Public slot to zoom out of the page.
+        """
+        index = self.__levelForZoom(self.__currentZoom)
+        if index > 0:
+            self.setZoomValue(self.__zoomLevels[index - 1])
+    
+    def zoomReset(self):
+        """
+        Public method to reset the zoom factor.
+        """
+        index = self.__levelForZoom(WebBrowserView.ZoomLevelDefault)
+        self.setZoomValue(self.__zoomLevels[index])
+    
+    def hasSelection(self):
+        """
+        Public method to determine, if there is some text selected.
+        
+        @return flag indicating text has been selected (boolean)
+        """
+        return self.selectedText() != ""
+    
+    def findNextPrev(self, txt, case, backwards, callback):
+        """
+        Public slot to find the next occurrence of a text.
+        
+        @param txt text to search for (string)
+        @param case flag indicating a case sensitive search (boolean)
+        @param backwards flag indicating a backwards search (boolean)
+        @param callback reference to a function with a bool parameter
+        @type function(bool) or None
+        """
+        findFlags = QWebEnginePage.FindFlags()
+        if case:
+            findFlags |= QWebEnginePage.FindCaseSensitively
+        if backwards:
+            findFlags |= QWebEnginePage.FindBackward
+        
+        if callback is None:
+            self.findText(txt, findFlags)
+        else:
+            self.findText(txt, findFlags, callback)
+    
+    def contextMenuEvent(self, evt):
+        """
+        Public method called to create a context menu.
+        
+        This method is overridden from QWebEngineView.
+        
+        @param evt reference to the context menu event object
+            (QContextMenuEvent)
+        """
+        pos = evt.pos()
+        reason = evt.reason()
+        QTimer.singleShot(
+            0,
+            lambda: self._contextMenuEvent(QContextMenuEvent(reason, pos)))
+        # needs to be done this way because contextMenuEvent is blocking
+        # the main loop
+    
+    def _contextMenuEvent(self, evt):
+        """
+        Protected method called to create a context menu.
+        
+        This method is overridden from QWebEngineView.
+        
+        @param evt reference to the context menu event object
+            (QContextMenuEvent)
+        """
+        self.__menu.clear()
+        
+        hitTest = self.page().hitTestContent(evt.pos())
+        
+        self.__createContextMenu(self.__menu, hitTest)
+        
+        if not hitTest.isContentEditable() and not hitTest.isContentSelected():
+            self.__menu.addSeparator()
+            self.__menu.addAction(self.__mw.adBlockIcon().menuAction())
+        
+        if Preferences.getWebBrowser("WebInspectorEnabled"):
+            self.__menu.addSeparator()
+            self.__menu.addAction(
+                UI.PixmapCache.getIcon("webInspector.png"),
+                self.tr("Inspect Element..."), self.__webInspector)
+        
+        if not self.__menu.isEmpty():
+            pos = evt.globalPos()
+            self.__menu.popup(QPoint(pos.x(), pos.y() + 1))
+    
+    def __createContextMenu(self, menu, hitTest):
+        """
+        Private method to populate the context menu.
+        
+        @param menu reference to the menu to be populated
+        @type QMenu
+        @param hitTest reference to the hit test object
+        @type WebHitTestResult
+        """
+        if not hitTest.linkUrl().isEmpty() and \
+                hitTest.linkUrl().scheme() != "javascript":
+            self.__createLinkContextMenu(menu, hitTest)
+        
+        if not hitTest.imageUrl().isEmpty():
+            self.__createImageContextMenu(menu, hitTest)
+        
+        if not hitTest.mediaUrl().isEmpty():
+            self.__createMediaContextMenu(menu, hitTest)
+        
+        if hitTest.isContentEditable():
+            menu.addAction(self.__mw.undoAct)
+            menu.addAction(self.__mw.redoAct)
+            menu.addSeparator()
+            menu.addAction(self.__mw.cutAct)
+            menu.addAction(self.__mw.copyAct)
+            menu.addAction(self.__mw.pasteAct)
+            menu.addSeparator()
+            self.__mw.personalInformationManager().createSubMenu(
+                menu, self, hitTest)
+            
+            if hitTest.tagName() == "input":
+                menu.addSeparator()
+                act = menu.addAction("")
+                act.setVisible(False)
+                self.__checkForForm(act, hitTest.pos())
+        
+        if self.selectedText():
+            self.__createSelectedTextContextMenu(menu, hitTest)
+        
+        if self.__menu.isEmpty():
+            self.__createPageContextMenu(menu)
+    
+    def __createLinkContextMenu(self, menu, hitTest):
+        """
+        Private method to populate the context menu for URLs.
+        
+        @param menu reference to the menu to be populated
+        @type QMenu
+        @param hitTest reference to the hit test object
+        @type WebHitTestResult
+        """
+        if not menu.isEmpty():
+            menu.addSeparator()
+        
+        menu.addAction(
+            UI.PixmapCache.getIcon("openNewTab.png"),
+            self.tr("Open Link in New Tab\tCtrl+LMB"),
+            self.__openLinkInNewTab).setData(hitTest.linkUrl())
+        menu.addAction(
+            UI.PixmapCache.getIcon("newWindow.png"),
+            self.tr("Open Link in New Window"),
+            self.__openLinkInNewWindow).setData(hitTest.linkUrl())
+        menu.addAction(
+            UI.PixmapCache.getIcon("privateMode.png"),
+            self.tr("Open Link in New Private Window"),
+            self.__openLinkInNewPrivateWindow).setData(hitTest.linkUrl())
+        menu.addSeparator()
+        menu.addAction(
+            UI.PixmapCache.getIcon("download.png"),
+            self.tr("Save Lin&k"), self.__downloadLink)
+        menu.addAction(
+            UI.PixmapCache.getIcon("bookmark22.png"),
+            self.tr("Bookmark this Link"), self.__bookmarkLink)\
+            .setData(hitTest.linkUrl())
+        menu.addSeparator()
+        menu.addAction(
+            UI.PixmapCache.getIcon("editCopy.png"),
+            self.tr("Copy Link to Clipboard"), self.__copyLink)\
+            .setData(hitTest.linkUrl())
+        menu.addAction(
+            UI.PixmapCache.getIcon("mailSend.png"),
+            self.tr("Send Link"),
+            self.__sendLink).setData(hitTest.linkUrl())
+        if Preferences.getWebBrowser("VirusTotalEnabled") and \
+           Preferences.getWebBrowser("VirusTotalServiceKey") != "":
+            menu.addAction(
+                UI.PixmapCache.getIcon("virustotal.png"),
+                self.tr("Scan Link with VirusTotal"),
+                self.__virusTotal).setData(hitTest.linkUrl())
+        
+    def __createImageContextMenu(self, menu, hitTest):
+        """
+        Private method to populate the context menu for images.
+        
+        @param menu reference to the menu to be populated
+        @type QMenu
+        @param hitTest reference to the hit test object
+        @type WebHitTestResult
+        """
+        if not menu.isEmpty():
+            menu.addSeparator()
+        
+        menu.addAction(
+            UI.PixmapCache.getIcon("openNewTab.png"),
+            self.tr("Open Image in New Tab"),
+            self.__openLinkInNewTab).setData(hitTest.imageUrl())
+        menu.addSeparator()
+        menu.addAction(
+            UI.PixmapCache.getIcon("download.png"),
+            self.tr("Save Image"), self.__downloadImage)
+        menu.addAction(
+            self.tr("Copy Image to Clipboard"), self.__copyImage)
+        menu.addAction(
+            UI.PixmapCache.getIcon("editCopy.png"),
+            self.tr("Copy Image Location to Clipboard"),
+            self.__copyLink).setData(hitTest.imageUrl())
+        menu.addAction(
+            UI.PixmapCache.getIcon("mailSend.png"),
+            self.tr("Send Image Link"),
+            self.__sendLink).setData(hitTest.imageUrl())
+        menu.addSeparator()
+        menu.addAction(
+            UI.PixmapCache.getIcon("adBlockPlus.png"),
+            self.tr("Block Image"), self.__blockImage)\
+            .setData(hitTest.imageUrl().toString())
+        if Preferences.getWebBrowser("VirusTotalEnabled") and \
+           Preferences.getWebBrowser("VirusTotalServiceKey") != "":
+            menu.addAction(
+                UI.PixmapCache.getIcon("virustotal.png"),
+                self.tr("Scan Image with VirusTotal"),
+                self.__virusTotal).setData(hitTest.imageUrl())
+    
+    def __createMediaContextMenu(self, menu, hitTest):
+        """
+        Private method to populate the context menu for media elements.
+        
+        @param menu reference to the menu to be populated
+        @type QMenu
+        @param hitTest reference to the hit test object
+        @type WebHitTestResult
+        """
+        self.__clickedPos = hitTest.pos()
+        
+        if not menu.isEmpty():
+            menu.addSeparator()
+        
+        if hitTest.mediaPaused():
+            menu.addAction(
+                UI.PixmapCache.getIcon("mediaPlaybackStart.png"),
+                self.tr("Play"), self.__pauseMedia)
+        else:
+            menu.addAction(
+                UI.PixmapCache.getIcon("mediaPlaybackPause.png"),
+                self.tr("Pause"), self.__pauseMedia)
+        if hitTest.mediaMuted():
+            menu.addAction(
+                UI.PixmapCache.getIcon("audioVolumeHigh.png"),
+                self.tr("Unmute"), self.__muteMedia)
+        else:
+            menu.addAction(
+                UI.PixmapCache.getIcon("audioVolumeMuted.png"),
+                self.tr("Mute"), self.__muteMedia)
+        menu.addSeparator()
+        menu.addAction(
+            UI.PixmapCache.getIcon("editCopy.png"),
+            self.tr("Copy Media Address to Clipboard"),
+            self.__copyLink).setData(hitTest.mediaUrl())
+        menu.addAction(
+            UI.PixmapCache.getIcon("mailSend.png"),
+            self.tr("Send Media Address"), self.__sendLink)\
+            .setData(hitTest.mediaUrl())
+        menu.addAction(
+            UI.PixmapCache.getIcon("download.png"),
+            self.tr("Save Media"), self.__downloadMedia)
+    
+    def __createSelectedTextContextMenu(self, menu, hitTest):
+        """
+        Private method to populate the context menu for selected text.
+        
+        @param menu reference to the menu to be populated
+        @type QMenu
+        @param hitTest reference to the hit test object
+        @type WebHitTestResult
+        """
+        if not menu.isEmpty():
+            menu.addSeparator()
+        
+        menu.addAction(self.__mw.copyAct)
+        menu.addSeparator()
+        menu.addAction(
+            UI.PixmapCache.getIcon("mailSend.png"),
+            self.tr("Send Text"),
+            self.__sendLink).setData(self.selectedText())
+        
+        engineName = self.__mw.openSearchManager().currentEngineName()
+        if engineName:
+            menu.addAction(self.tr("Search with '{0}'").format(engineName), 
+                           self.__searchDefaultRequested)
+        
+        from .OpenSearch.OpenSearchEngineAction import \
+            OpenSearchEngineAction
+        
+        self.__searchMenu = menu.addMenu(self.tr("Search with..."))
+        engineNames = self.__mw.openSearchManager().allEnginesNames()
+        for engineName in engineNames:
+            engine = self.__mw.openSearchManager().engine(engineName)
+            act = OpenSearchEngineAction(engine, self.__searchMenu)
+            act.setData(engineName)
+            self.__searchMenu.addAction(act)
+        self.__searchMenu.triggered.connect(self.__searchRequested)
+        
+        menu.addSeparator()
+        
+        from .WebBrowserLanguagesDialog import WebBrowserLanguagesDialog
+        languages = Preferences.toList(
+            Preferences.Prefs.settings.value(
+                "WebBrowser/AcceptLanguages",
+                WebBrowserLanguagesDialog.defaultAcceptLanguages()))
+        if languages:
+            language = languages[0]
+            langCode = language.split("[")[1][:2]
+            googleTranslatorUrl = QUrl(
+                "http://translate.google.com/#auto|{0}|{1}".format(
+                    langCode, self.selectedText()))
+            menu.addAction(
+                UI.PixmapCache.getIcon("translate.png"),
+                self.tr("Google Translate"), self.__openLinkInNewTab)\
+                .setData(googleTranslatorUrl)
+            wiktionaryUrl = QUrl(
+                "http://{0}.wiktionary.org/wiki/Special:Search?search={1}"
+                .format(langCode, self.selectedText()))
+            menu.addAction(
+                UI.PixmapCache.getIcon("wikipedia.png"),
+                self.tr("Dictionary"), self.__openLinkInNewTab)\
+                .setData(wiktionaryUrl)
+            menu.addSeparator()
+        
+        guessedUrl = QUrl.fromUserInput(self.selectedText().strip())
+        if self.__isUrlValid(guessedUrl):
+            menu.addAction(
+                self.tr("Go to web address"),
+                self.__openLinkInNewTab).setData(guessedUrl)
+    
+    def __createPageContextMenu(self, menu):
+        """
+        Private method to populate the basic context menu.
+        
+        @param menu reference to the menu to be populated
+        @type QMenu
+        """
+        
+        menu.addAction(self.__mw.newTabAct)
+        menu.addAction(self.__mw.newAct)
+        menu.addSeparator()
+        # TODO: Save
+##        menu.addAction(self.__mw.saveAsAct)
+##        menu.addSeparator()
+        
+        if self.url().toString() == "eric:speeddial":
+            # special menu for the spedd dial page
+            menu.addAction(self.__mw.backAct)
+            menu.addAction(self.__mw.forwardAct)
+            menu.addSeparator()
+            menu.addAction(
+                UI.PixmapCache.getIcon("plus.png"),
+                self.tr("Add New Page"), self.__addSpeedDial)
+            menu.addAction(
+                UI.PixmapCache.getIcon("preferences-general.png"),
+                self.tr("Configure Speed Dial"), self.__configureSpeedDial)
+            menu.addSeparator()
+            menu.addAction(
+                UI.PixmapCache.getIcon("reload.png"),
+                self.tr("Reload All Dials"), self.__reloadAllSpeedDials)
+            return
+        
+        menu.addAction(
+            UI.PixmapCache.getIcon("bookmark22.png"),
+            self.tr("Bookmark this Page"), self.addBookmark)
+        menu.addAction(
+            UI.PixmapCache.getIcon("editCopy.png"),
+            self.tr("Copy Page Link"), self.__copyLink).setData(self.url())
+        menu.addAction(
+            UI.PixmapCache.getIcon("mailSend.png"),
+            self.tr("Send Page Link"), self.__sendLink).setData(self.url())
+        menu.addSeparator()
+        
+        # TODO: User Agent
+##        from .UserAgent.UserAgentMenu import UserAgentMenu
+##        self.__userAgentMenu = UserAgentMenu(self.tr("User Agent"),
+##                                             url=self.url())
+##        menu.addMenu(self.__userAgentMenu)
+##        menu.addSeparator()
+        menu.addAction(self.__mw.backAct)
+        menu.addAction(self.__mw.forwardAct)
+        menu.addAction(self.__mw.homeAct)
+        menu.addAction(self.__mw.reloadAct)
+        menu.addAction(self.__mw.stopAct)
+        menu.addSeparator()
+        menu.addAction(self.__mw.zoomInAct)
+        menu.addAction(self.__mw.zoomResetAct)
+        menu.addAction(self.__mw.zoomOutAct)
+        menu.addSeparator()
+        menu.addAction(self.__mw.selectAllAct)
+        menu.addSeparator()
+        menu.addAction(self.__mw.findAct)
+        menu.addSeparator()
+        menu.addAction(self.__mw.pageSourceAct)
+        menu.addSeparator()
+        menu.addAction(self.__mw.siteInfoAct)
+        if self.url().scheme() in ["http", "https"]:
+            menu.addSeparator()
+            
+            w3url = QUrl.fromEncoded(
+                b"http://validator.w3.org/check?uri=" +
+                QUrl.toPercentEncoding(bytes(self.url().toEncoded()).decode()))
+            menu.addAction(
+                UI.PixmapCache.getIcon("w3.png"),
+                self.tr("Validate Page"), self.__openLinkInNewTab)\
+                .setData(w3url)
+            
+            from .WebBrowserLanguagesDialog import WebBrowserLanguagesDialog
+            languages = Preferences.toList(
+                Preferences.Prefs.settings.value(
+                    "WebBrowser/AcceptLanguages",
+                    WebBrowserLanguagesDialog.defaultAcceptLanguages()))
+            if languages:
+                language = languages[0]
+                langCode = language.split("[")[1][:2]
+                googleTranslatorUrl = QUrl.fromEncoded(
+                    b"http://translate.google.com/translate?sl=auto&tl=" +
+                    langCode.encode() +
+                    b"&u=" +
+                    QUrl.toPercentEncoding(
+                        bytes(self.url().toEncoded()).decode()))
+                menu.addAction(
+                    UI.PixmapCache.getIcon("translate.png"),
+                    self.tr("Google Translate"), self.__openLinkInNewTab)\
+                    .setData(googleTranslatorUrl)
+        
+    def __checkForForm(self, act, pos):
+        """
+        Private method to check the given position for an open search form.
+        
+        @param act reference to the action to be populated upon success
+        @type QAction
+        @param pos position to be tested
+        @type QPoint
+        """
+        self.__clickedPos = pos
+        
+        from .Tools import Scripts
+        script = Scripts.getFormData(pos)
+        self.page().runJavaScript(
+            script, lambda res: self.__checkForFormCallback(res, act))
+    
+    def __checkForFormCallback(self, res, act):
+        """
+        Private method handling the __checkForForm result.
+        
+        @param res result dictionary generated by JavaScript
+        @type dict
+        @param act reference to the action to be populated upon success
+        @type QAction
+        """
+        if act is None or not bool(res):
+            return
+        
+        url = QUrl(res["action"])
+        method = res["method"]
+        
+        if not url.isEmpty() and method in ["get", "post"]:
+            act.setVisible(True)
+            act.setText(self.tr("Add to web search toolbar"))
+            act.triggered.connect(self.__addSearchEngine)
+    
+    def __isUrlValid(self, url):
+        """
+        Private method to check a URL for validity.
+        
+        @param url URL to be checked (QUrl)
+        @return flag indicating a valid URL (boolean)
+        """
+        return url.isValid() and \
+            bool(url.host()) and \
+            bool(url.scheme()) and \
+            "." in url.host()
+    
+    def __openLinkInNewTab(self):
+        """
+        Private method called by the context menu to open a link in a new
+        tab.
+        """
+        act = self.sender()
+        url = act.data()
+        if url.isEmpty():
+            return
+        
+        # TODO: check, if this can be done simpler
+        self.__ctrlPressed = True
+        self.setSource(url)
+        self.__ctrlPressed = False
+    
+    def __openLinkInNewWindow(self):
+        """
+        Private slot called by the context menu to open a link in a new
+        window.
+        """
+        act = self.sender()
+        url = act.data()
+        if url.isEmpty():
+            return
+        
+        self.__mw.newWindow(url)
+    
+    def __openLinkInNewPrivateWindow(self):
+        """
+        Private slot called by the context menu to open a link in a new
+        private window.
+        """
+        act = self.sender()
+        url = act.data()
+        if url.isEmpty():
+            return
+        
+        self.__mw.newPrivateWindow(url)
+    
+    def __bookmarkLink(self):
+        """
+        Private slot to bookmark a link via the context menu.
+        """
+        act = self.sender()
+        url = act.data()
+        if url.isEmpty():
+            return
+        
+        from .Bookmarks.AddBookmarkDialog import AddBookmarkDialog
+        dlg = AddBookmarkDialog()
+        dlg.setUrl(bytes(url.toEncoded()).decode())
+        dlg.exec_()
+    
+    def __sendLink(self):
+        """
+        Private slot to send a link via email.
+        """
+        act = self.sender()
+        data = act.data()
+        if isinstance(data, QUrl) and data.isEmpty():
+            return
+        
+        if isinstance(data, QUrl):
+            data = data.toString()
+        QDesktopServices.openUrl(QUrl("mailto:?body=" + data))
+    
+    def __copyLink(self):
+        """
+        Private slot to copy a link to the clipboard.
+        """
+        act = self.sender()
+        data = act.data()
+        if isinstance(data, QUrl) and data.isEmpty():
+            return
+        
+        if isinstance(data, QUrl):
+            data = data.toString()
+        QApplication.clipboard().setText(data)
+    
+    def __downloadLink(self):
+        """
+        Private slot to download a link and save it to disk.
+        """
+        self.triggerPageAction(QWebEnginePage.DownloadLinkToDisk)
+    
+    def __downloadImage(self):
+        """
+        Private slot to download an image and save it to disk.
+        """
+        self.triggerPageAction(QWebEnginePage.DownloadImageToDisk)
+    
+    def __copyImage(self):
+        """
+        Private slot to copy an image to the clipboard.
+        """
+        self.triggerPageAction(QWebEnginePage.CopyImageToClipboard)
+    
+    def __blockImage(self):
+        """
+        Private slot to add a block rule for an image URL.
+        """
+        from WebBrowser.WebBrowserWindow import WebBrowserWindow
+        act = self.sender()
+        url = act.data()
+        dlg = WebBrowserWindow.adBlockManager().showDialog()
+        dlg.addCustomRule(url)
+    
+    def __downloadMedia(self):
+        """
+        Private slot to download a media and save it to disk.
+        """
+        self.triggerPageAction(QWebEnginePage.DownloadMediaToDisk)
+    
+    def __pauseMedia(self):
+        """
+        Private slot to pause or play the selected media.
+        """
+        self.triggerPageAction(QWebEnginePage.ToggleMediaPlayPause)
+    
+    def __muteMedia(self):
+        """
+        Private slot to (un)mute the selected media.
+        """
+        self.triggerPageAction(QWebEnginePage.ToggleMediaMute)
+    
+    def __virusTotal(self):
+        """
+        Private slot to scan the selected URL with VirusTotal.
+        """
+        act = self.sender()
+        url = act.data()
+        self.__mw.requestVirusTotalScan(url)
+    
+    def __searchDefaultRequested(self):
+        """
+        Private slot to search for some text with the current search engine.
+        """
+        searchText = self.selectedText()
+        
+        if not searchText:
+            return
+        
+        engine = self.__mw.openSearchManager().currentEngine()
+        if engine:
+            self.search.emit(engine.searchUrl(searchText))
+    
+    def __searchRequested(self, act):
+        """
+        Private slot to search for some text with a selected search engine.
+        
+        @param act reference to the action that triggered this slot (QAction)
+        """
+        searchText = self.selectedText()
+        
+        if not searchText:
+            return
+        
+        engineName = act.data()
+        if engineName:
+            engine = self.__mw.openSearchManager().engine(engineName)
+        else:
+            engine = self.__mw.openSearchManager().currentEngine()
+        if engine:
+            self.search.emit(engine.searchUrl(searchText))
+    
+    def __addSearchEngine(self):
+        """
+        Private slot to add a new search engine.
+        """
+        from .Tools import Scripts
+        script = Scripts.getFormData(self.__clickedPos)
+        self.page().runJavaScript(
+            script,
+            lambda res: self.__mw.openSearchManager().addEngineFromForm(
+                res, self))
+    
+    def __webInspector(self):
+        """
+        Private slot to show the web inspector window.
+        """
+        if self.__inspector is None:
+            from .WebInspector import WebInspector
+            self.__inspector = WebInspector()
+            self.__inspector.setView(self, True)
+            self.__inspector.show()
+        else:
+            self.closeWebInspector()
+    
+    def closeWebInspector(self):
+        """
+        Public slot to close the web inspector.
+        """
+        if self.__inspector is not None:
+            if self.__inspector.isVisible():
+                self.__inspector.hide()
+            WebInspector.unregisterView(self.__inspector)
+            self.__inspector.deleteLater()
+            self.__inspector = None
+    
+    def addBookmark(self):
+        """
+        Public slot to bookmark the current page.
+        """
+        from .Tools import Scripts
+        script = Scripts.getAllMetaAttributes()
+        self.page().runJavaScript(
+            script, self.__addBookmarkCallback)
+    
+    def __addBookmarkCallback(self, res):
+        """
+        Private callback method of __addBookmark().
+        
+        @param url URL for the bookmark
+        @type str
+        @param title title for the bookmark
+        @type str
+        @param res result of the JavaScript
+        @type list
+        """
+        description = ""
+        for meta in res:
+            if meta["name"] == "description":
+                description = meta["content"]
+        
+        from .Bookmarks.AddBookmarkDialog import AddBookmarkDialog
+        dlg = AddBookmarkDialog()
+        dlg.setUrl(bytes(self.url().toEncoded()).decode())
+        dlg.setTitle(self.title())
+        dlg.setDescription(description)
+        dlg.exec_()
+    
+    def dragEnterEvent(self, evt):
+        """
+        Protected method called by a drag enter event.
+        
+        @param evt reference to the drag enter event (QDragEnterEvent)
+        """
+        evt.acceptProposedAction()
+    
+    def dragMoveEvent(self, evt):
+        """
+        Protected method called by a drag move event.
+        
+        @param evt reference to the drag move event (QDragMoveEvent)
+        """
+        evt.ignore()
+        if evt.source() != self:
+            if len(evt.mimeData().urls()) > 0:
+                evt.acceptProposedAction()
+            else:
+                url = QUrl(evt.mimeData().text())
+                if url.isValid():
+                    evt.acceptProposedAction()
+        
+        if not evt.isAccepted():
+            super(WebBrowserView, self).dragMoveEvent(evt)
+    
+    def dropEvent(self, evt):
+        """
+        Protected method called by a drop event.
+        
+        @param evt reference to the drop event (QDropEvent)
+        """
+        super(WebBrowserView, self).dropEvent(evt)
+        if not evt.isAccepted() and \
+           evt.source() != self and \
+           evt.possibleActions() & Qt.CopyAction:
+            url = QUrl()
+            if len(evt.mimeData().urls()) > 0:
+                url = evt.mimeData().urls()[0]
+            if not url.isValid():
+                url = QUrl(evt.mimeData().text())
+            if url.isValid():
+                self.setSource(url)
+                evt.acceptProposedAction()
+    
+    def _mousePressEvent(self, evt):
+        """
+        Protected method called by a mouse press event.
+        
+        @param evt reference to the mouse event (QMouseEvent)
+        """
+        self.__mw.setEventMouseButtons(evt.buttons())
+        self.__mw.setEventKeyboardModifiers(evt.modifiers())
+        
+        if evt.button() == Qt.XButton1:
+            self.pageAction(QWebEnginePage.Back).trigger()
+        elif evt.button() == Qt.XButton2:
+            self.pageAction(QWebEnginePage.Forward).trigger()
+        else:
+            super(WebBrowserView, self).mousePressEvent(evt)
+    
+    def _mouseReleaseEvent(self, evt):
+        """
+        Protected method called by a mouse release event.
+        
+        @param evt reference to the mouse event (QMouseEvent)
+        """
+        accepted = evt.isAccepted()
+        self.__page.event(evt)
+        if not evt.isAccepted() and \
+           self.__mw.eventMouseButtons() & Qt.MidButton:
+            url = QUrl(QApplication.clipboard().text(QClipboard.Selection))
+            if not url.isEmpty() and \
+               url.isValid() and \
+               url.scheme() != "":
+                self.__mw.setEventMouseButtons(Qt.NoButton)
+                self.__mw.setEventKeyboardModifiers(Qt.NoModifier)
+                self.setSource(url)
+        evt.setAccepted(accepted)
+    
+    def _wheelEvent(self, evt):
+        """
+        Protected method to handle wheel events.
+        
+        @param evt reference to the wheel event (QWheelEvent)
+        """
+        delta = evt.angleDelta().y()
+        if evt.modifiers() & Qt.ControlModifier:
+            if delta < 0:
+                self.zoomOut()
+            else:
+                self.zoomIn()
+            evt.accept()
+            return
+        
+        if evt.modifiers() & Qt.ShiftModifier:
+            if delta < 0:
+                self.backward()
+            else:
+                self.forward()
+            evt.accept()
+            return
+        
+        super(WebBrowserView, self).wheelEvent(evt)
+    
+    def _keyPressEvent(self, evt):
+        """
+        Protected method called by a key press.
+        
+        @param evt reference to the key event (QKeyEvent)
+        """
+        if self.__mw.personalInformationManager().viewKeyPressEvent(self, evt):
+            evt.accept()
+            return
+        
+        if evt.key() == Qt.Key_Escape:
+            if self.isFullScreen():
+                self.triggerPageAction(QWebEnginePage.ExitFullScreen)
+                evt.accept()
+                return
+        
+        # TODO: Access Keys
+##        if self.__enableAccessKeys:
+##            self.__accessKeysPressed = (
+##                evt.modifiers() == Qt.ControlModifier and
+##                evt.key() == Qt.Key_Control)
+##            if not self.__accessKeysPressed:
+##                if self.__checkForAccessKey(evt):
+##                    self.__hideAccessKeys()
+##                    evt.accept()
+##                    return
+##                self.__hideAccessKeys()
+##            else:
+##                QTimer.singleShot(300, self.__accessKeyShortcut)
+        
+        self.__ctrlPressed = (evt.key() == Qt.Key_Control)
+        super(WebBrowserView, self).keyPressEvent(evt)
+    
+    def _keyReleaseEvent(self, evt):
+        """
+        Protected method called by a key release.
+        
+        @param evt reference to the key event (QKeyEvent)
+        """
+        # TODO: Access Keys
+##        if self.__enableAccessKeys:
+##            self.__accessKeysPressed = evt.key() == Qt.Key_Control
+        
+        self.__ctrlPressed = False
+        super(WebBrowserView, self).keyReleaseEvent(evt)
+    
+    def focusOutEvent(self, evt):
+        """
+        Protected method called by a focus out event.
+        
+        @param evt reference to the focus event (QFocusEvent)
+        """
+        # TODO: Access Keys
+##        if self.__accessKeysPressed:
+##            self.__hideAccessKeys()
+##            self.__accessKeysPressed = False
+        
+        super(WebBrowserView, self).focusOutEvent(evt)
+    
+    # TODO: Gestures: Obsoleted by eventFilter() (?)
+    def event(self, evt):
+        """
+        Public method handling events.
+        
+        @param evt reference to the event (QEvent)
+        @return flag indicating, if the event was handled (boolean)
+        """
+        if evt.type() == QEvent.Gesture:
+            self._gestureEvent(evt)
+            return True
+        
+        return super(WebBrowserView, self).event(evt)
+    
+    def _gestureEvent(self, evt):
+        """
+        Protected method handling gesture events.
+        
+        @param evt reference to the gesture event (QGestureEvent
+        """
+        pinch = evt.gesture(Qt.PinchGesture)
+        if pinch:
+            if pinch.state() == Qt.GestureStarted:
+                pinch.setScaleFactor(self.__currentZoom / 100.0)
+            else:
+                scaleFactor = pinch.scaleFactor()
+                self.setZoomValue(int(scaleFactor * 100))
+            evt.accept()
+    
+    def eventFilter(self, obj, evt):
+        """
+        Public method to process event for other objects.
+        
+        @param obj reference to object to process events for
+        @type QObject
+        @param evt reference to event to be processed
+        @type QEvent
+        @return flag indicating that the event should be filtered out
+        @rtype bool
+        """
+        # find the render widget receiving events for the web page
+        if obj is self and evt.type() == QEvent.ChildAdded:
+            child = evt.child()
+            if child and child.inherits(
+                    "QtWebEngineCore::RenderWidgetHostViewQtDelegateWidget"):
+                self.__rwhvqt = child
+                self.grabGesture(Qt.PinchGesture)
+                self.__rwhvqt.installEventFilter(self)
+        
+        # forward events to WebBrowserView
+        if obj is self.__rwhvqt:
+            wasAccepted = evt.isAccepted()
+            evt.setAccepted(False)
+            if evt.type() == QEvent.KeyPress:
+                self._keyPressEvent(evt)
+            elif evt.type() == QEvent.KeyRelease:
+                self._keyReleaseEvent(evt)
+            elif evt.type() == QEvent.MouseButtonPress:
+                self._mousePressEvent(evt)
+            elif evt.type() == QEvent.MouseButtonRelease:
+                self._mouseReleaseEvent(evt)
+            elif evt.type() == QEvent.Wheel:
+                self._wheelEvent(evt)
+            elif evt.type() == QEvent.Gesture:
+                self._gestureEvent(evt)
+            ret = evt.isAccepted()
+            evt.setAccepted(wasAccepted)
+            return ret
+        
+        # block already handled events
+        if obj is self:
+            if evt.type() in [QEvent.KeyPress, QEvent.KeyRelease,
+                              QEvent.MouseButtonPress,
+                              QEvent.MouseButtonRelease,
+                              QEvent.Wheel, QEvent.Gesture]:
+                return True
+        
+        return super(WebBrowserView, self).eventFilter(obj, evt)
+    
+    def clearHistory(self):
+        """
+        Public slot to clear the history.
+        """
+        self.history().clear()
+        self.__urlChanged(self.history().currentItem().url())
+    
+    ###########################################################################
+    ## Signal converters below
+    ###########################################################################
+    
+    def __urlChanged(self, url):
+        """
+        Private slot to handle the urlChanged signal.
+        
+        @param url the new url (QUrl)
+        """
+        self.sourceChanged.emit(url)
+        
+        self.forwardAvailable.emit(self.isForwardAvailable())
+        self.backwardAvailable.emit(self.isBackwardAvailable())
+    
+    def __iconUrlChanged(self, url):
+        """
+        Private slot to handle the iconUrlChanged signal.
+        
+        @param url URL to get web site icon from
+        @type QUrl
+        """
+        self.__siteIcon = QIcon()
+        if self.__siteIconLoader is not None:
+            self.__siteIconLoader.deleteLater()
+        self.__siteIconLoader = WebIconLoader(url, self)
+        self.__siteIconLoader.iconLoaded.connect(self.__iconLoaded)
+    
+    def __iconLoaded(self, icon):
+        """
+        Private slot handling the loaded web site icon.
+        
+        @param icon web site icon
+        @type QIcon
+        """
+        self.__siteIcon = icon
+        
+        from .Tools import WebIconProvider
+        WebIconProvider.instance().saveIcon(self)
+        
+        self.iconChanged.emit()
+    
+    def icon(self):
+        """
+        Public method to get the web site icon.
+        
+        @return web site icon
+        @rtype QIcon
+        """
+        if not self.__siteIcon.isNull():
+            return QIcon(self.__siteIcon)
+        
+        from .Tools import WebIconProvider
+        return WebIconProvider.instance().iconForUrl(self.url())
+    
+##    def __statusBarMessage(self, text):
+##        """
+##        Private slot to handle the statusBarMessage signal.
+##        
+##        @param text text to be shown in the status bar (string)
+##        """
+##        self.__mw.statusBar().showMessage(text)
+##    
+    def __linkHovered(self, link):
+        """
+        Private slot to handle the linkHovered signal.
+        
+        @param link the URL of the link (string)
+        """
+        self.highlighted.emit(link)
+    
+    ###########################################################################
+    ## Signal handlers below
+    ###########################################################################
+    
+    def __loadStarted(self):
+        """
+        Private method to handle the loadStarted signal.
+        """
+        self.__isLoading = True
+        self.__progress = 0
+    
+    def __loadProgress(self, progress):
+        """
+        Private method to handle the loadProgress signal.
+        
+        @param progress progress value (integer)
+        """
+        self.__progress = progress
+    
+    def __loadFinished(self, ok):
+        """
+        Private method to handle the loadFinished signal.
+        
+        @param ok flag indicating the result (boolean)
+        """
+        self.__isLoading = False
+        self.__progress = 0
+        
+        # TODO: ClickToFlash (?)
+##        if Preferences.getWebBrowser("ClickToFlashEnabled"):
+##            # this is a hack to make the ClickToFlash button appear
+##            self.zoomIn()
+##            self.zoomOut()
+        
+        from .ZoomManager import ZoomManager
+        zoomValue = ZoomManager.instance().zoomValue(self.url())
+        self.setZoomValue(zoomValue)
+        
+        if ok:
+            self.__mw.historyManager().addHistoryEntry(self)
+            self.__mw.adBlockManager().page().hideBlockedPageEntries(
+                self.page())
+            self.__mw.passwordManager().completePage(self.page())
+    
+    def isLoading(self):
+        """
+        Public method to get the loading state.
+        
+        @return flag indicating the loading state (boolean)
+        """
+        return self.__isLoading
+    
+    def progress(self):
+        """
+        Public method to get the load progress.
+        
+        @return load progress (integer)
+        """
+        return self.__progress
+    
+##    def saveAs(self):
+##        """
+##        Public method to save the current page to a file.
+##        """
+##        url = self.url()
+##        if url.isEmpty():
+##            return
+##        
+##        self.__mw.downloadManager().download(url, True, mainWindow=self.__mw)
+    
+##    def __unsupportedContent(self, reply, requestFilename=None,
+##                             download=False):
+##        """
+##        Private slot to handle the unsupportedContent signal.
+##        
+##        @param reply reference to the reply object (QNetworkReply)
+##        @keyparam requestFilename indicating to ask for a filename
+##            (boolean or None). If it is None, the behavior is determined
+##            by a configuration option.
+##        @keyparam download flag indicating a download operation (boolean)
+##        """
+##        if reply is None:
+##            return
+##        
+##        replyUrl = reply.url()
+##        
+##        if replyUrl.scheme() == "abp":
+##            return
+##        
+##        if reply.error() == QNetworkReply.NoError:
+##            if reply.header(QNetworkRequest.ContentTypeHeader):
+##                self.__mw.downloadManager().handleUnsupportedContent(
+##                    reply, webPage=self.page(), mainWindow=self.__mw)
+##                return
+##        
+##        replyUrl = reply.url()
+##        if replyUrl.isEmpty():
+##            return
+##        
+##        notFoundFrame = self.page().mainFrame()
+##        if notFoundFrame is None:
+##            return
+##        
+##        if reply.header(QNetworkRequest.ContentTypeHeader):
+##            data = reply.readAll()
+##            if contentSniff(data):
+##                notFoundFrame.setHtml(str(data, encoding="utf-8"), replyUrl)
+##                return
+##        
+##        urlString = bytes(replyUrl.toEncoded()).decode()
+##        title = self.tr("Error loading page: {0}").format(urlString)
+##        htmlFile = QFile(":/html/notFoundPage.html")
+##        htmlFile.open(QFile.ReadOnly)
+##        html = htmlFile.readAll()
+##        pixmap = qApp.style()\
+##            .standardIcon(QStyle.SP_MessageBoxWarning).pixmap(48, 48)
+##        imageBuffer = QBuffer()
+##        imageBuffer.open(QIODevice.ReadWrite)
+##        if pixmap.save(imageBuffer, "PNG"):
+##            html = html.replace("@IMAGE@", imageBuffer.buffer().toBase64())
+##        pixmap = qApp.style()\
+##            .standardIcon(QStyle.SP_MessageBoxWarning).pixmap(16, 16)
+##        imageBuffer = QBuffer()
+##        imageBuffer.open(QIODevice.ReadWrite)
+##        if pixmap.save(imageBuffer, "PNG"):
+##            html = html.replace("@FAVICON@", imageBuffer.buffer().toBase64())
+##        html = html.replace("@TITLE@", title.encode("utf8"))
+##        html = html.replace("@H1@", reply.errorString().encode("utf8"))
+##        html = html.replace(
+##            "@H2@", self.tr("When connecting to: {0}.")
+##            .format(urlString).encode("utf8"))
+##        html = html.replace(
+##            "@LI-1@",
+##            self.tr("Check the address for errors such as "
+##                    "<b>ww</b>.example.org instead of "
+##                    "<b>www</b>.example.org").encode("utf8"))
+##        html = html.replace(
+##            "@LI-2@",
+##            self.tr("If the address is correct, try checking the network "
+##                    "connection.").encode("utf8"))
+##        html = html.replace(
+##            "@LI-3@",
+##            self.tr(
+##                "If your computer or network is protected by a firewall "
+##                "or proxy, make sure that the browser is permitted to "
+##                "access the network.").encode("utf8"))
+##        html = html.replace(
+##            "@LI-4@",
+##            self.tr("If your cache policy is set to offline browsing,"
+##                    "only pages in the local cache are available.")
+##            .encode("utf8"))
+##        html = html.replace(
+##            "@BUTTON@", self.tr("Try Again").encode("utf8"))
+##        notFoundFrame.setHtml(bytes(html).decode("utf8"), replyUrl)
+##        self.__mw.historyManager().removeHistoryEntry(replyUrl, self.title())
+##        self.loadFinished.emit(False)
+##    
+    
+##    def __databaseQuotaExceeded(self, frame, databaseName):
+##        """
+##        Private slot to handle the case, where the database quota is exceeded.
+##        
+##        @param frame reference to the frame (QWebFrame)
+##        @param databaseName name of the web database (string)
+##        """
+##        securityOrigin = frame.securityOrigin()
+##        if securityOrigin.databaseQuota() > 0 and \
+##           securityOrigin.databaseUsage() == 0:
+##            # cope with a strange behavior of Qt 4.6, if a database is
+##            # accessed for the first time
+##            return
+##        
+##        res = E5MessageBox.yesNo(
+##            self,
+##            self.tr("Web Database Quota"),
+##            self.tr(
+##                """<p>The database quota of <strong>{0}</strong> has"""
+##                """ been exceeded while accessing database <strong>{1}"""
+##                """</strong>.</p><p>Shall it be changed?</p>""")
+##            .format(self.__dataString(securityOrigin.databaseQuota()),
+##                    databaseName),
+##            yesDefault=True)
+##        if res:
+##            newQuota, ok = QInputDialog.getInt(
+##                self,
+##                self.tr("New Web Database Quota"),
+##                self.tr(
+##                    "Enter the new quota in MB (current = {0}, used = {1}; "
+##                    "step size = 5 MB):"
+##                    .format(
+##                        self.__dataString(securityOrigin.databaseQuota()),
+##                        self.__dataString(securityOrigin.databaseUsage()))),
+##                securityOrigin.databaseQuota() // (1024 * 1024),
+##                0, 2147483647, 5)
+##            if ok:
+##                securityOrigin.setDatabaseQuota(newQuota * 1024 * 1024)
+##    
+##    def __dataString(self, size):
+##        """
+##        Private method to generate a formatted data string.
+##        
+##        @param size size to be formatted (integer)
+##        @return formatted data string (string)
+##        """
+##        unit = ""
+##        if size < 1024:
+##            unit = self.tr("bytes")
+##        elif size < 1024 * 1024:
+##            size /= 1024
+##            unit = self.tr("kB")
+##        else:
+##            size /= 1024 * 1024
+##            unit = self.tr("MB")
+##        return "{0:.1f} {1}".format(size, unit)
+    
+    ###########################################################################
+    ## Access key related methods below
+    ###########################################################################
+    
+    # TODO: Access Keys
+##    def __accessKeyShortcut(self):
+##        """
+##        Private slot to switch the display of access keys.
+##        """
+##        if not self.hasFocus() or \
+##           not self.__accessKeysPressed or \
+##           not self.__enableAccessKeys:
+##            return
+##        
+##        if self.__accessKeyLabels:
+##            self.__hideAccessKeys()
+##        else:
+##            self.__showAccessKeys()
+##        
+##        self.__accessKeysPressed = False
+##    
+##    def __checkForAccessKey(self, evt):
+##        """
+##        Private method to check the existence of an access key and activate the
+##        corresponding link.
+##        
+##        @param evt reference to the key event (QKeyEvent)
+##        @return flag indicating, if the event was handled (boolean)
+##        """
+##        if not self.__accessKeyLabels:
+##            return False
+##        
+##        text = evt.text()
+##        if not text:
+##            return False
+##        
+##        key = text[0].upper()
+##        handled = False
+##        if key in self.__accessKeyNodes:
+##            element = self.__accessKeyNodes[key]
+##            p = element.geometry().center()
+##            frame = element.webFrame()
+##            p -= frame.scrollPosition()
+##            frame = frame.parentFrame()
+##            while frame and frame != self.page().mainFrame():
+##                p -= frame.scrollPosition()
+##                frame = frame.parentFrame()
+##            pevent = QMouseEvent(
+##                QEvent.MouseButtonPress, p, Qt.LeftButton,
+##                Qt.MouseButtons(Qt.NoButton),
+##                Qt.KeyboardModifiers(Qt.NoModifier))
+##            qApp.sendEvent(self, pevent)
+##            revent = QMouseEvent(
+##                QEvent.MouseButtonRelease, p, Qt.LeftButton,
+##                Qt.MouseButtons(Qt.NoButton),
+##                Qt.KeyboardModifiers(Qt.NoModifier))
+##            qApp.sendEvent(self, revent)
+##            handled = True
+##        
+##        return handled
+##    
+##    def __hideAccessKeys(self):
+##        """
+##        Private slot to hide the access key labels.
+##        """
+##        if self.__accessKeyLabels:
+##            for label in self.__accessKeyLabels:
+##                label.hide()
+##                label.deleteLater()
+##            self.__accessKeyLabels = []
+##            self.__accessKeyNodes = {}
+##            self.update()
+##    
+##    def __showAccessKeys(self):
+##        """
+##        Private method to show the access key labels.
+##        """
+##        supportedElements = [
+##            "input", "a", "area", "button", "label", "legend", "textarea",
+##        ]
+##        unusedKeys = "A B C D E F G H I J K L M N O P Q R S T U V W X Y Z" \
+##            " 0 1 2 3 4 5 6 7 8 9".split()
+##        
+##        viewport = QRect(self.__page.mainFrame().scrollPosition(),
+##                         self.__page.viewportSize())
+##        # Priority first goes to elements with accesskey attributes
+##        alreadyLabeled = []
+##        for elementType in supportedElements:
+##            result = self.page().mainFrame().findAllElements(elementType)\
+##                .toList()
+##            for element in result:
+##                geometry = element.geometry()
+##                if geometry.size().isEmpty() or \
+##                   not viewport.contains(geometry.topLeft()):
+##                    continue
+##                
+##                accessKeyAttribute = element.attribute("accesskey").upper()
+##                if not accessKeyAttribute:
+##                    continue
+##                
+##                accessKey = ""
+##                i = 0
+##                while i < len(accessKeyAttribute):
+##                    if accessKeyAttribute[i] in unusedKeys:
+##                        accessKey = accessKeyAttribute[i]
+##                        break
+##                    i += 2
+##                if accessKey == "":
+##                    continue
+##                unusedKeys.remove(accessKey)
+##                self.__makeAccessLabel(accessKey, element)
+##                alreadyLabeled.append(element)
+##        
+##        # Pick an access key first from the letters in the text and then
+##        # from the list of unused access keys
+##        for elementType in supportedElements:
+##            result = self.page().mainFrame().findAllElements(elementType)\
+##                .toList()
+##            for element in result:
+##                geometry = element.geometry()
+##                if not unusedKeys or \
+##                   element in alreadyLabeled or \
+##                   geometry.size().isEmpty() or \
+##                   not viewport.contains(geometry.topLeft()):
+##                    continue
+##                
+##                accessKey = ""
+##                text = element.toPlainText().upper()
+##                for c in text:
+##                    if c in unusedKeys:
+##                        accessKey = c
+##                        break
+##                if accessKey == "":
+##                    accessKey = unusedKeys[0]
+##                unusedKeys.remove(accessKey)
+##                self.__makeAccessLabel(accessKey, element)
+##    
+##    def __makeAccessLabel(self, accessKey, element):
+##        """
+##        Private method to generate the access label for an element.
+##        
+##        @param accessKey access key to generate the label for (str)
+##        @param element reference to the web element to create the label for
+##            (QWebElement)
+##        """
+##        label = QLabel(self)
+##        label.setText("<qt><b>{0}</b></qt>".format(accessKey))
+##        
+##        p = QToolTip.palette()
+##        color = QColor(Qt.yellow).lighter(150)
+##        color.setAlpha(175)
+##        p.setColor(QPalette.Window, color)
+##        label.setPalette(p)
+##        label.setAutoFillBackground(True)
+##        label.setFrameStyle(QFrame.Box | QFrame.Plain)
+##        point = element.geometry().center()
+##        point -= self.__page.mainFrame().scrollPosition()
+##        label.move(point)
+##        label.show()
+##        point.setX(point.x() - label.width() // 2)
+##        label.move(point)
+##        self.__accessKeyLabels.append(label)
+##        self.__accessKeyNodes[accessKey] = element
+    
+    ###########################################################################
+    ## Miscellaneous methods below
+    ###########################################################################
+    
+    def createWindow(self, windowType):
+        """
+        Public method called, when a new window should be created.
+        
+        @param windowType type of the requested window
+            (QWebEnginePage.WebWindowType)
+        @return reference to the created browser window (WebBrowserView)
+        """
+        self.__mw.newTab(addNextTo=self)
+        return self.__mw.currentBrowser()
+    
+    def preferencesChanged(self):
+        """
+        Public method to indicate a change of the settings.
+        """
+        # TODO: Access Keys
+##        self.__enableAccessKeys = Preferences.getWebBrowser("AccessKeysEnabled")
+##        if not self.__enableAccessKeys:
+##            self.__hideAccessKeys()
+        
+        self.reload()
+    
+    ###########################################################################
+    ## RSS related methods below
+    ###########################################################################
+    
+    def checkRSS(self):
+        """
+        Public method to check, if the loaded page contains feed links.
+        
+        @return flag indicating the existence of feed links (boolean)
+        """
+        self.__rss = []
+        
+        script = Scripts.getFeedLinks()
+        feeds = self.page().execJavaScript(script)
+        
+        if feeds is not None:
+            for feed in feeds:
+                if feed["url"] and feed["title"]:
+                    self.__rss.append((feed["title"], feed["url"]))
+        
+        return len(self.__rss) > 0
+    
+    def getRSS(self):
+        """
+        Public method to get the extracted RSS feeds.
+        
+        @return list of RSS feeds (list of tuples of two strings)
+        """
+        return self.__rss
+    
+    def hasRSS(self):
+        """
+        Public method to check, if the loaded page has RSS links.
+        
+        @return flag indicating the presence of RSS links (boolean)
+        """
+        return len(self.__rss) > 0
+    
+    ###########################################################################
+    ## Full Screen handling below
+    ###########################################################################
+    
+    def isFullScreen(self):
+        """
+        Public method to check, if full screen mode is active.
+        
+        @return flag indicating full screen mode
+        @rtype bool
+        """
+        return self.__mw.isFullScreen()
+    
+    def requestFullScreen(self, enable):
+        """
+        Public method to request full screen mode.
+        
+        @param enable flag indicating full screen mode on or off
+        @type bool
+        """
+        if enable:
+            self.__mw.enterHtmlFullScreen()
+        else:
+            self.__mw.showNormal()
+    
+    ###########################################################################
+    ## Speed Dial slots below
+    ###########################################################################
+    
+    def __addSpeedDial(self):
+        """
+        Private slot to add a new speed dial.
+        """
+        self.__page.runJavaScript("addSpeedDial();")
+    
+    def __configureSpeedDial(self):
+        """
+        Private slot to configure the speed dial.
+        """
+        self.page().runJavaScript("configureSpeedDial();")
+    
+    def __reloadAllSpeedDials(self):
+        """
+        Private slot to reload all speed dials.
+        """
+        self.page().runJavaScript("reloadAll();")
+
+##def contentSniff(data):
+##    """
+##    Module function to do some content sniffing to check, if the data is HTML.
+##    
+##    @param data data block to sniff at (string)
+##    @return flag indicating HTML content (boolean)
+##    """
+##    if data.contains("<!doctype") or \
+##       data.contains("<script") or \
+##       data.contains("<html") or \
+##       data.contains("<!--") or \
+##       data.contains("<head") or \
+##       data.contains("<iframe") or \
+##       data.contains("<h1") or \
+##       data.contains("<div") or \
+##       data.contains("<font") or \
+##       data.contains("<table") or \
+##       data.contains("<a") or \
+##       data.contains("<style") or \
+##       data.contains("<title") or \
+##       data.contains("<b") or \
+##       data.contains("<body") or \
+##       data.contains("<br") or \
+##       data.contains("<p"):
+##        return True
+##    
+##    return False
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/WebBrowserWebSearchWidget.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,403 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a web search widget for the web browser.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSignal, QUrl, QModelIndex, QTimer, Qt
+from PyQt5.QtGui import QStandardItem, QStandardItemModel, QFont, QIcon, \
+    QPixmap
+from PyQt5.QtWidgets import QMenu, QCompleter
+from PyQt5.QtWebEngineWidgets import QWebEnginePage
+
+import UI.PixmapCache
+
+import Preferences
+
+from E5Gui.E5LineEdit import E5ClearableLineEdit
+
+
+class WebBrowserWebSearchWidget(E5ClearableLineEdit):
+    """
+    Class implementing a web search widget for the web browser.
+    
+    @signal search(QUrl) emitted when the search should be done
+    """
+    search = pyqtSignal(QUrl)
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(WebBrowserWebSearchWidget, self).__init__(parent)
+        
+        from E5Gui.E5LineEdit import E5LineEdit
+        from E5Gui.E5LineEditButton import E5LineEditButton
+        from .OpenSearch.OpenSearchManager import OpenSearchManager
+
+        self.__mw = parent
+        
+        self.__openSearchManager = OpenSearchManager(self)
+        self.__openSearchManager.currentEngineChanged.connect(
+            self.__currentEngineChanged)
+        self.__currentEngine = ""
+        
+        self.__enginesMenu = QMenu(self)
+        
+        self.__engineButton = E5LineEditButton(self)
+        self.__engineButton.setMenu(self.__enginesMenu)
+        self.addWidget(self.__engineButton, E5LineEdit.LeftSide)
+        
+        self.__searchButton = E5LineEditButton(self)
+        self.__searchButton.setIcon(UI.PixmapCache.getIcon("webSearch.png"))
+        self.addWidget(self.__searchButton, E5LineEdit.LeftSide)
+        
+        self.__model = QStandardItemModel(self)
+        self.__completer = QCompleter()
+        self.__completer.setModel(self.__model)
+        self.__completer.setCompletionMode(
+            QCompleter.UnfilteredPopupCompletion)
+        self.__completer.setWidget(self)
+        
+        self.__searchButton.clicked.connect(self.__searchButtonClicked)
+        self.textEdited.connect(self.__textEdited)
+        self.returnPressed.connect(self.__searchNow)
+        self.__completer.activated[QModelIndex].connect(
+            self.__completerActivated)
+        self.__completer.highlighted[QModelIndex].connect(
+            self.__completerHighlighted)
+        self.__enginesMenu.aboutToShow.connect(self.__showEnginesMenu)
+        
+        self.__suggestionsItem = None
+        self.__suggestions = []
+        self.__suggestTimer = None
+        self.__suggestionsEnabled = Preferences.getWebBrowser(
+            "WebSearchSuggestions")
+        
+        self.__recentSearchesItem = None
+        self.__recentSearches = []
+        self.__maxSavedSearches = 10
+        
+        self.__engine = None
+        self.__loadSearches()
+        self.__setupCompleterMenu()
+        self.__currentEngineChanged()
+    
+    def __searchNow(self):
+        """
+        Private slot to perform the web search.
+        """
+        searchText = self.text()
+        if not searchText:
+            return
+        
+        import WebBrowser.WebBrowserWindow
+        if WebBrowser.WebBrowserWindow.WebBrowserWindow.isPrivate():
+            return
+        
+        if searchText in self.__recentSearches:
+            self.__recentSearches.remove(searchText)
+        self.__recentSearches.insert(0, searchText)
+        if len(self.__recentSearches) > self.__maxSavedSearches:
+            self.__recentSearches = \
+                self.__recentSearches[:self.__maxSavedSearches]
+        self.__setupCompleterMenu()
+        
+        self.__mw.currentBrowser().setFocus()
+        self.__mw.currentBrowser().load(
+            self.__openSearchManager.currentEngine().searchUrl(searchText))
+    
+    def __setupCompleterMenu(self):
+        """
+        Private method to create the completer menu.
+        """
+        if not self.__suggestions or \
+           (self.__model.rowCount() > 0 and
+                self.__model.item(0) != self.__suggestionsItem):
+            self.__model.clear()
+            self.__suggestionsItem = None
+        else:
+            self.__model.removeRows(1, self.__model.rowCount() - 1)
+        
+        boldFont = QFont()
+        boldFont.setBold(True)
+        
+        if self.__suggestions:
+            if self.__model.rowCount() == 0:
+                if not self.__suggestionsItem:
+                    self.__suggestionsItem = QStandardItem(
+                        self.tr("Suggestions"))
+                    self.__suggestionsItem.setFont(boldFont)
+                self.__model.appendRow(self.__suggestionsItem)
+            
+            for suggestion in self.__suggestions:
+                self.__model.appendRow(QStandardItem(suggestion))
+        
+        if not self.__recentSearches:
+            self.__recentSearchesItem = QStandardItem(
+                self.tr("No Recent Searches"))
+            self.__recentSearchesItem.setFont(boldFont)
+            self.__model.appendRow(self.__recentSearchesItem)
+        else:
+            self.__recentSearchesItem = QStandardItem(
+                self.tr("Recent Searches"))
+            self.__recentSearchesItem.setFont(boldFont)
+            self.__model.appendRow(self.__recentSearchesItem)
+            for recentSearch in self.__recentSearches:
+                self.__model.appendRow(QStandardItem(recentSearch))
+        
+        view = self.__completer.popup()
+        view.setFixedHeight(view.sizeHintForRow(0) * self.__model.rowCount() +
+                            view.frameWidth() * 2)
+        
+        self.__searchButton.setEnabled(
+            bool(self.__recentSearches or self.__suggestions))
+    
+    def __completerActivated(self, index):
+        """
+        Private slot handling the selection of an entry from the completer.
+        
+        @param index index of the item (QModelIndex)
+        """
+        if self.__suggestionsItem and \
+           self.__suggestionsItem.index().row() == index.row():
+            return
+        
+        if self.__recentSearchesItem and \
+           self.__recentSearchesItem.index().row() == index.row():
+            return
+        
+        self.__searchNow()
+    
+    def __completerHighlighted(self, index):
+        """
+        Private slot handling the highlighting of an entry of the completer.
+        
+        @param index index of the item (QModelIndex)
+        @return flah indicating a successful highlighting (boolean)
+        """
+        if self.__suggestionsItem and \
+           self.__suggestionsItem.index().row() == index.row():
+            return False
+        
+        if self.__recentSearchesItem and \
+           self.__recentSearchesItem.index().row() == index.row():
+            return False
+        
+        self.setText(index.data())
+        return True
+    
+    def __textEdited(self, txt):
+        """
+        Private slot to handle changes of the search text.
+        
+        @param txt search text (string)
+        """
+        if self.__suggestionsEnabled:
+            if self.__suggestTimer is None:
+                self.__suggestTimer = QTimer(self)
+                self.__suggestTimer.setSingleShot(True)
+                self.__suggestTimer.setInterval(200)
+                self.__suggestTimer.timeout.connect(self.__getSuggestions)
+            self.__suggestTimer.start()
+        else:
+            self.__completer.setCompletionPrefix(txt)
+            self.__completer.complete()
+    
+    def __getSuggestions(self):
+        """
+        Private slot to get search suggestions from the configured search
+        engine.
+        """
+        searchText = self.text()
+        if searchText:
+            self.__openSearchManager.currentEngine()\
+                .requestSuggestions(searchText)
+    
+    def __newSuggestions(self, suggestions):
+        """
+        Private slot to receive a new list of suggestions.
+        
+        @param suggestions list of suggestions (list of strings)
+        """
+        self.__suggestions = suggestions
+        self.__setupCompleterMenu()
+        self.__completer.complete()
+    
+    def __showEnginesMenu(self):
+        """
+        Private slot to handle the display of the engines menu.
+        """
+        self.__enginesMenu.clear()
+        
+        from .OpenSearch.OpenSearchEngineAction import OpenSearchEngineAction
+        engineNames = self.__openSearchManager.allEnginesNames()
+        for engineName in engineNames:
+            engine = self.__openSearchManager.engine(engineName)
+            action = OpenSearchEngineAction(engine, self.__enginesMenu)
+            action.setData(engineName)
+            action.triggered.connect(self.__changeCurrentEngine)
+            self.__enginesMenu.addAction(action)
+            
+            if self.__openSearchManager.currentEngineName() == engineName:
+                action.setCheckable(True)
+                action.setChecked(True)
+        
+        cb = self.__mw.currentBrowser()
+        from .Tools import Scripts
+        script = Scripts.getOpenSearchLinks()
+        cb.page().runJavaScript(script, self.__showEnginesMenuCallback)
+    
+    def __showEnginesMenuCallback(self, res):
+        """
+        Private method handling the open search links callback.
+        
+        @param res result of the JavaScript
+        @type list of dict
+        """
+        cb = self.__mw.currentBrowser()
+        if res:
+            self.__enginesMenu.addSeparator()
+            for entry in res:
+                url = cb.url().resolved(QUrl(entry["url"]))
+                title = entry["title"]
+                if url.isEmpty():
+                    continue
+                if not title:
+                    title = cb.title()
+                
+                action = self.__enginesMenu.addAction(
+                    self.tr("Add '{0}'").format(title),
+                    self.__addEngineFromUrl)
+                action.setData(url)
+                action.setIcon(cb.icon())
+        
+        self.__enginesMenu.addSeparator()
+        self.__enginesMenu.addAction(self.__mw.searchEnginesAction())
+        
+        if self.__recentSearches:
+            self.__enginesMenu.addAction(self.tr("Clear Recent Searches"),
+                                         self.clear)
+    
+    def __changeCurrentEngine(self):
+        """
+        Private slot to handle the selection of a search engine.
+        """
+        action = self.sender()
+        if action is not None:
+            name = action.data()
+            self.__openSearchManager.setCurrentEngineName(name)
+    
+    def __addEngineFromUrl(self):
+        """
+        Private slot to add a search engine given its URL.
+        """
+        action = self.sender()
+        if action is not None:
+            url = action.data()
+            if not isinstance(url, QUrl):
+                return
+            
+            self.__openSearchManager.addEngine(url)
+    
+    def __searchButtonClicked(self):
+        """
+        Private slot to show the search menu via the search button.
+        """
+        self.__setupCompleterMenu()
+        self.__completer.complete()
+    
+    def clear(self):
+        """
+        Public method to clear all private data.
+        """
+        self.__recentSearches = []
+        self.__setupCompleterMenu()
+        super(WebBrowserWebSearchWidget, self).clear()
+        self.clearFocus()
+    
+    def preferencesChanged(self):
+        """
+        Public method to handle the change of preferences.
+        """
+        self.__suggestionsEnabled = Preferences.getWebBrowser(
+            "WebSearchSuggestions")
+        if not self.__suggestionsEnabled:
+            self.__suggestions = []
+            self.__setupCompleterMenu()
+    
+    def saveSearches(self):
+        """
+        Public method to save the recently performed web searches.
+        """
+        Preferences.Prefs.settings.setValue(
+            'WebBrowser/WebSearches', self.__recentSearches)
+    
+    def __loadSearches(self):
+        """
+        Private method to load the recently performed web searches.
+        """
+        searches = Preferences.Prefs.settings.value('WebBrowser/WebSearches')
+        if searches is not None:
+            self.__recentSearches = searches
+    
+    def openSearchManager(self):
+        """
+        Public method to get a reference to the opensearch manager object.
+        
+        @return reference to the opensearch manager object (OpenSearchManager)
+        """
+        return self.__openSearchManager
+    
+    def __currentEngineChanged(self):
+        """
+        Private slot to track a change of the current search engine.
+        """
+        if self.__openSearchManager.engineExists(self.__currentEngine):
+            oldEngine = self.__openSearchManager.engine(self.__currentEngine)
+            oldEngine.imageChanged.disconnect(self.__engineImageChanged)
+            if self.__suggestionsEnabled:
+                oldEngine.suggestions.disconnect(self.__newSuggestions)
+        
+        newEngine = self.__openSearchManager.currentEngine()
+        if newEngine.networkAccessManager() is None:
+            newEngine.setNetworkAccessManager(self.__mw.networkManager())
+        newEngine.imageChanged.connect(self.__engineImageChanged)
+        if self.__suggestionsEnabled:
+            newEngine.suggestions.connect(self.__newSuggestions)
+        
+        self.setInactiveText(self.__openSearchManager.currentEngineName())
+        self.__currentEngine = self.__openSearchManager.currentEngineName()
+        self.__engineButton.setIcon(QIcon(QPixmap.fromImage(
+            self.__openSearchManager.currentEngine().image())))
+        self.__suggestions = []
+        self.__setupCompleterMenu()
+    
+    def __engineImageChanged(self):
+        """
+        Private slot to handle a change of the current search engine icon.
+        """
+        self.__engineButton.setIcon(QIcon(QPixmap.fromImage(
+            self.__openSearchManager.currentEngine().image())))
+    
+    def mousePressEvent(self, evt):
+        """
+        Protected method called by a mouse press event.
+        
+        @param evt reference to the mouse event (QMouseEvent)
+        """
+        if evt.button() == Qt.XButton1:
+            self.__mw.currentBrowser().triggerPageAction(
+                QWebEnginePage.Back)
+        elif evt.button() == Qt.XButton2:
+            self.__mw.currentBrowser().triggerPageAction(
+                QWebEnginePage.Forward)
+        else:
+            super(WebBrowserWebSearchWidget, self).mousePressEvent(evt)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/WebBrowserWindow.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,4169 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2002 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the web browser main window.
+"""
+
+from __future__ import unicode_literals
+try:
+    str = unicode           # __IGNORE_EXCEPTION__
+except NameError:
+    pass
+
+import os
+import shutil
+import sys
+
+from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt, QByteArray, QSize, QTimer, \
+    QUrl, QThread, QTextCodec, QProcess, QEvent
+from PyQt5.QtGui import QDesktopServices, QKeySequence, QFont, QFontMetrics, \
+    QIcon
+from PyQt5.QtWidgets import QWidget, QVBoxLayout, QSizePolicy, QDockWidget, \
+    QComboBox, QLabel, QSplitter, QMenu, QToolButton, QLineEdit, \
+    QApplication, QWhatsThis, QDialog, QHBoxLayout, QProgressBar, QAction, \
+    QInputDialog
+##from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest
+from PyQt5.QtWebEngineWidgets import QWebEngineSettings, QWebEnginePage, \
+    QWebEngineProfile, QWebEngineScript
+try:
+    from PyQt5.QtHelp import QHelpEngine, QHelpEngineCore, QHelpSearchQuery
+    QTHELP_AVAILABLE = True
+except ImportError:
+    QTHELP_AVAILABLE = False
+
+from E5Gui.E5Action import E5Action
+from E5Gui import E5MessageBox, E5FileDialog, E5ErrorMessage
+from E5Gui.E5MainWindow import E5MainWindow
+from E5Gui.E5Application import e5App
+from E5Gui.E5ZoomWidget import E5ZoomWidget
+
+from E5Network.E5NetworkIcon import E5NetworkIcon
+
+import Preferences
+from Preferences import Shortcuts
+
+import Utilities
+import Globals
+
+import UI.PixmapCache
+import UI.Config
+from UI.Info import Version
+
+from .data import icons_rc          # __IGNORE_WARNING__
+from .data import html_rc           # __IGNORE_WARNING__
+from .data import javascript_rc     # __IGNORE_WARNING__
+from .data import qml_rc            # __IGNORE_WARNING__
+
+
+from .Tools import Scripts, WebBrowserTools, WebIconProvider
+
+from .ZoomManager import ZoomManager
+
+from eric6config import getConfig
+
+
+class WebBrowserWindow(E5MainWindow):
+    """
+    Class implementing the web browser main window.
+    
+    @signal webBrowserClosed() emitted after the window was requested to close
+    @signal zoomTextOnlyChanged(bool) emitted after the zoom text only setting
+        was changed
+    """
+    webBrowserClosed = pyqtSignal()
+    
+    BrowserWindows = []
+
+##    maxMenuFilePathLen = 75
+##    
+    _fromEric = False
+    UseQtHelp = QTHELP_AVAILABLE
+    _isPrivate = False
+    
+    _webProfile = None
+    _networkManager = None
+    _cookieJar = None
+    _helpEngine = None
+    _bookmarksManager = None
+    _historyManager = None
+    _passwordManager = None
+    _adblockManager = None
+    _downloadManager = None
+    _feedsManager = None
+##    _userAgentsManager = None
+    _syncManager = None
+    _speedDial = None
+    _personalInformationManager = None
+    _greaseMonkeyManager = None
+    _notification = None
+    _featurePermissionManager = None
+    _flashCookieManager = None
+    
+    def __init__(self, home, path, parent, name, fromEric=False,
+                 initShortcutsOnly=False, searchWord=None,
+                 private=False, qthelp=False, settingsDir=""):
+        """
+        Constructor
+        
+        @param home the URL to be shown (string)
+        @param path the path of the working dir (usually '.') (string)
+        @param parent parent widget of this window (QWidget)
+        @param name name of this window (string)
+        @param fromEric flag indicating whether it was called from within
+            eric6 (boolean)
+        @keyparam initShortcutsOnly flag indicating to just initialize the
+            keyboard shortcuts (boolean)
+        @keyparam searchWord word to search for (string)
+        @keyparam private flag indicating a private browsing window (bool)
+        @keyparam qthelp flag indicating to enable the QtHelp support (bool)
+        @keyparam settingsDir directory to be used for the settings files (str)
+        """
+        super(WebBrowserWindow, self).__init__(parent)
+        self.setObjectName(name)
+        self.setWindowTitle(self.tr("eric6 Web Browser"))
+        
+        self.__settingsDir = settingsDir
+        self.__fromEric = fromEric
+        WebBrowserWindow._fromEric = fromEric
+        self.__initShortcutsOnly = initShortcutsOnly
+        self.setWindowIcon(UI.PixmapCache.getIcon("ericWeb.png"))
+
+        self.__mHistory = []
+        self.__lastConfigurationPageName = ""
+        
+        WebBrowserWindow._isPrivate = private
+        
+        self.__eventMouseButtons = Qt.NoButton
+        self.__eventKeyboardModifiers = Qt.NoModifier
+        
+        if self.__initShortcutsOnly:
+            self.__initActions()
+        else:
+            if Preferences.getWebBrowser("WebInspectorEnabled"):
+                os.putenv(
+                    "QTWEBENGINE_REMOTE_DEBUGGING",
+                    str(Preferences.getWebBrowser("WebInspectorPort")))
+            
+            WebBrowserWindow.setUseQtHelp(
+                self.__fromEric or qthelp or bool(searchWord))
+            
+            self.webProfile(private)
+            self.networkManager()
+            
+            self.__htmlFullScreen = False
+            self.__windowStates = 0
+            
+            from .SearchWidget import SearchWidget
+            from .QtHelp.HelpTocWidget import HelpTocWidget
+            from .QtHelp.HelpIndexWidget import HelpIndexWidget
+            from .QtHelp.HelpSearchWidget import HelpSearchWidget
+            from .WebBrowserView import WebBrowserView
+            from .WebBrowserTabWidget import WebBrowserTabWidget
+            from .AdBlock.AdBlockIcon import AdBlockIcon
+            from .VirusTotal.VirusTotalApi import VirusTotalAPI
+            
+            if not self.__fromEric:
+                self.setStyle(Preferences.getUI("Style"),
+                              Preferences.getUI("StyleSheet"))
+                
+                # TODO: Check if this is needed with QtWebEngine
+                # initialize some SSL stuff
+##                from E5Network.E5SslUtilities import initSSL
+##                initSSL()
+            
+            if WebBrowserWindow.useQtHelp:
+                self.__helpEngine = QHelpEngine(
+                    os.path.join(Utilities.getConfigDir(),
+                                 "web_browser", "eric6help.qhc"),
+                    self)
+                self.__removeOldDocumentation()
+                self.__helpEngine.warning.connect(self.__warning)
+            else:
+                self.__helpEngine = None
+            self.__helpInstaller = None
+            
+            self.__zoomWidget = E5ZoomWidget(
+                UI.PixmapCache.getPixmap("zoomOut.png"),
+                UI.PixmapCache.getPixmap("zoomIn.png"),
+                UI.PixmapCache.getPixmap("zoomReset.png"), self)
+            self.statusBar().addPermanentWidget(self.__zoomWidget)
+            self.__zoomWidget.setMapping(
+                WebBrowserView.ZoomLevels, WebBrowserView.ZoomLevelDefault)
+            self.__zoomWidget.valueChanged.connect(self.__zoomValueChanged)
+            
+            self.__tabWidget = WebBrowserTabWidget(self)
+            self.__tabWidget.currentChanged[int].connect(self.__currentChanged)
+            self.__tabWidget.titleChanged.connect(self.__titleChanged)
+            self.__tabWidget.showMessage.connect(self.statusBar().showMessage)
+            self.__tabWidget.browserZoomValueChanged.connect(
+                self.__zoomWidget.setValue)
+            
+            self.__searchWidget = SearchWidget(self, self)
+            centralWidget = QWidget()
+            layout = QVBoxLayout()
+            layout.setContentsMargins(1, 1, 1, 1)
+            layout.addWidget(self.__tabWidget)
+            layout.addWidget(self.__searchWidget)
+            self.__tabWidget.setSizePolicy(
+                QSizePolicy.Preferred, QSizePolicy.Expanding)
+            centralWidget.setLayout(layout)
+            self.setCentralWidget(centralWidget)
+            self.__searchWidget.hide()
+            
+            if WebBrowserWindow.useQtHelp:
+                # TODO: QtHelp: place the widgets in a tab widget
+                # setup the TOC widget
+                self.__tocWindow = HelpTocWidget(self.__helpEngine, self)
+                self.__tocDock = QDockWidget(self.tr("Contents"), self)
+                self.__tocDock.setObjectName("TocWindow")
+                self.__tocDock.setWidget(self.__tocWindow)
+                self.addDockWidget(Qt.LeftDockWidgetArea, self.__tocDock)
+                
+                # setup the index widget
+                self.__indexWindow = HelpIndexWidget(self.__helpEngine, self)
+                self.__indexDock = QDockWidget(self.tr("Index"), self)
+                self.__indexDock.setObjectName("IndexWindow")
+                self.__indexDock.setWidget(self.__indexWindow)
+                self.addDockWidget(Qt.LeftDockWidgetArea, self.__indexDock)
+                
+                # setup the search widget
+                self.__searchWord = searchWord
+                self.__indexing = False
+                self.__indexingProgress = None
+                self.__searchEngine = self.__helpEngine.searchEngine()
+                self.__searchEngine.indexingStarted.connect(
+                    self.__indexingStarted)
+                self.__searchEngine.indexingFinished.connect(
+                    self.__indexingFinished)
+                self.__searchWindow = HelpSearchWidget(
+                    self.__searchEngine, self)
+                self.__searchDock = QDockWidget(self.tr("Search"), self)
+                self.__searchDock.setObjectName("SearchWindow")
+                self.__searchDock.setWidget(self.__searchWindow)
+                self.addDockWidget(Qt.LeftDockWidgetArea, self.__searchDock)
+            
+            # JavaScript Console window
+            from .WebBrowserJavaScriptConsole import \
+                WebBrowserJavaScriptConsole
+            self.__javascriptConsole = WebBrowserJavaScriptConsole(self)
+            self.__javascriptConsoleDock = QDockWidget(
+                self.tr("JavaScript Console"))
+            self.__javascriptConsoleDock.setObjectName("JavascriptConsole")
+            self.__javascriptConsoleDock.setAllowedAreas(
+                Qt.BottomDockWidgetArea | Qt.TopDockWidgetArea)
+            self.__javascriptConsoleDock.setWidget(self.__javascriptConsole)
+            self.addDockWidget(Qt.BottomDockWidgetArea,
+                               self.__javascriptConsoleDock)
+            
+            if Preferences.getWebBrowser("SaveGeometry"):
+                g = Preferences.getGeometry("WebBrowserGeometry")
+            else:
+                g = QByteArray()
+            if g.isEmpty():
+                s = QSize(800, 800)
+                self.resize(s)
+            else:
+                self.restoreGeometry(g)
+            
+            WebBrowserWindow.BrowserWindows.append(self)
+            
+            self.__setIconDatabasePath()
+            self.__initWebEngineSettings()
+            
+            # initialize some of our class objects
+            self.passwordManager()
+            self.historyManager()
+            self.greaseMonkeyManager()
+            
+            self.__initActions()
+            self.__initMenus()
+            self.__initToolbars()
+            
+            syncMgr = self.syncManager()
+            syncMgr.syncMessage.connect(self.statusBar().showMessage)
+            syncMgr.syncError.connect(self.statusBar().showMessage)
+            
+            self.__tabWidget.newBrowser(home)
+            self.__tabWidget.currentBrowser().setFocus()
+            
+            self.__adBlockIcon = AdBlockIcon(self)
+            self.statusBar().addPermanentWidget(self.__adBlockIcon)
+            self.__adBlockIcon.setEnabled(
+                Preferences.getWebBrowser("AdBlockEnabled"))
+            self.__tabWidget.currentChanged[int].connect(
+                self.__adBlockIcon.currentChanged)
+            self.__tabWidget.sourceChanged.connect(
+                self.__adBlockIcon.sourceChanged)
+            
+            self.networkIcon = E5NetworkIcon(self)
+            self.statusBar().addPermanentWidget(self.networkIcon)
+            
+            QDesktopServices.setUrlHandler("http", self.__linkActivated)
+            QDesktopServices.setUrlHandler("https", self.__linkActivated)
+            
+            # setup connections
+            self.__activating = False
+            if WebBrowserWindow.useQtHelp:
+                # TOC window
+                self.__tocWindow.linkActivated.connect(self.__linkActivated)
+                self.__tocWindow.escapePressed.connect(
+                    self.__activateCurrentBrowser)
+                # index window
+                self.__indexWindow.linkActivated.connect(self.__linkActivated)
+                self.__indexWindow.linksActivated.connect(
+                    self.__linksActivated)
+                self.__indexWindow.escapePressed.connect(
+                    self.__activateCurrentBrowser)
+                # search window
+                self.__searchWindow.linkActivated.connect(
+                    self.__linkActivated)
+                self.__searchWindow.escapePressed.connect(
+                    self.__activateCurrentBrowser)
+            
+            state = Preferences.getWebBrowser("WebBrowserState")
+            self.restoreState(state)
+            
+            self.__initHelpDb()
+            
+            self.__virusTotal = VirusTotalAPI(self)
+            self.__virusTotal.submitUrlError.connect(
+                self.__virusTotalSubmitUrlError)
+            self.__virusTotal.urlScanReport.connect(
+                self.__virusTotalUrlScanReport)
+            self.__virusTotal.fileScanReport.connect(
+                self.__virusTotalFileScanReport)
+            
+            self.__shutdownCalled = False
+            
+            self.flashCookieManager()
+            
+            if WebBrowserWindow.useQtHelp:
+                QTimer.singleShot(0, self.__lookForNewDocumentation)
+                if self.__searchWord is not None:
+                    QTimer.singleShot(0, self.__searchForWord)
+            
+            self.__lastActiveWindow = None
+            e5App().focusChanged[QWidget, QWidget].connect(
+                self.__appFocusChanged)
+            
+            QTimer.singleShot(0, syncMgr.loadSettings)
+    
+    def __del__(self):
+        """
+        Special method called during object destruction.
+        
+        Note: This empty variant seems to get rid of the Qt message
+        'Warning: QBasicTimer::start: QBasicTimer can only be used with
+        threads started with QThread'
+        """
+        pass
+    
+    def fromEric(self):
+        """
+        Public method to check, if the web browser was called from within the
+        eric IDE.
+        
+        @return flag indicating that the browserw as opened from within eric
+        @rtype bool
+        """
+        return self.__fromEric
+    
+    def __setIconDatabasePath(self, enable=True):
+        """
+        Private method to set the favicons path.
+        
+        @param enable flag indicating to enabled icon storage (boolean)
+        """
+        if enable:
+            iconDatabasePath = os.path.join(Utilities.getConfigDir(),
+                                            "web_browser", "favicons")
+            if not os.path.exists(iconDatabasePath):
+                os.makedirs(iconDatabasePath)
+        else:
+            iconDatabasePath = ""   # setting an empty path disables it
+        
+        WebIconProvider.instance().setIconDatabasePath(iconDatabasePath)
+        
+    def __initWebEngineSettings(self):
+        """
+        Private method to set the global web settings.
+        """
+        settings = QWebEngineSettings.globalSettings()
+        
+        settings.setFontFamily(
+            QWebEngineSettings.StandardFont,
+            Preferences.getWebBrowser("StandardFontFamily"))
+        settings.setFontFamily(
+            QWebEngineSettings.FixedFont,
+            Preferences.getWebBrowser("FixedFontFamily"))
+        settings.setFontFamily(
+            QWebEngineSettings.SerifFont,
+            Preferences.getWebBrowser("SerifFontFamily"))
+        settings.setFontFamily(
+            QWebEngineSettings.SansSerifFont,
+            Preferences.getWebBrowser("SansSerifFontFamily"))
+        settings.setFontFamily(
+            QWebEngineSettings.CursiveFont,
+            Preferences.getWebBrowser("CursiveFontFamily"))
+        settings.setFontFamily(
+            QWebEngineSettings.FantasyFont,
+            Preferences.getWebBrowser("FantasyFontFamily"))
+        
+        settings.setFontSize(
+            QWebEngineSettings.DefaultFontSize,
+            Preferences.getWebBrowser("DefaultFontSize"))
+        settings.setFontSize(
+            QWebEngineSettings.DefaultFixedFontSize,
+            Preferences.getWebBrowser("DefaultFixedFontSize"))
+        settings.setFontSize(
+            QWebEngineSettings.MinimumFontSize,
+            Preferences.getWebBrowser("MinimumFontSize"))
+        settings.setFontSize(
+            QWebEngineSettings.MinimumLogicalFontSize,
+            Preferences.getWebBrowser("MinimumLogicalFontSize"))
+        
+        styleSheet = Preferences.getWebBrowser("UserStyleSheet")
+        self.__setUserStyleSheet(styleSheet)
+        
+        settings.setAttribute(
+            QWebEngineSettings.AutoLoadImages,
+            Preferences.getWebBrowser("AutoLoadImages"))
+        settings.setAttribute(
+            QWebEngineSettings.JavascriptEnabled,
+            Preferences.getWebBrowser("JavaScriptEnabled"))
+        settings.setAttribute(
+            QWebEngineSettings.JavascriptCanOpenWindows,
+            Preferences.getWebBrowser("JavaScriptCanOpenWindows"))
+        settings.setAttribute(
+            QWebEngineSettings.JavascriptCanAccessClipboard,
+            Preferences.getWebBrowser("JavaScriptCanAccessClipboard"))
+        settings.setAttribute(
+            QWebEngineSettings.PluginsEnabled,
+            Preferences.getWebBrowser("PluginsEnabled"))
+        
+        if self.isPrivate():
+            settings.setAttribute(
+                QWebEngineSettings.LocalStorageEnabled, False)
+        else:
+            settings.setAttribute(
+                QWebEngineSettings.LocalStorageEnabled,
+                Preferences.getWebBrowser("LocalStorageEnabled"))
+        settings.setDefaultTextEncoding(
+            Preferences.getWebBrowser("DefaultTextEncoding"))
+        
+        settings.setAttribute(
+            QWebEngineSettings.SpatialNavigationEnabled,
+            Preferences.getWebBrowser("SpatialNavigationEnabled"))
+        settings.setAttribute(
+            QWebEngineSettings.LinksIncludedInFocusChain,
+            Preferences.getWebBrowser("LinksIncludedInFocusChain"))
+        settings.setAttribute(
+            QWebEngineSettings.LocalContentCanAccessRemoteUrls,
+            Preferences.getWebBrowser("LocalContentCanAccessRemoteUrls"))
+        settings.setAttribute(
+            QWebEngineSettings.LocalContentCanAccessFileUrls,
+            Preferences.getWebBrowser("LocalContentCanAccessFileUrls"))
+        settings.setAttribute(
+            QWebEngineSettings.XSSAuditingEnabled,
+            Preferences.getWebBrowser("XSSAuditingEnabled"))
+        settings.setAttribute(
+            QWebEngineSettings.ScrollAnimatorEnabled,
+            Preferences.getWebBrowser("ScrollAnimatorEnabled"))
+        settings.setAttribute(
+            QWebEngineSettings.ErrorPageEnabled,
+            Preferences.getWebBrowser("ErrorPageEnabled"))
+        settings.setAttribute(
+            QWebEngineSettings.FullScreenSupportEnabled,
+            Preferences.getWebBrowser("FullScreenSupportEnabled"))
+    
+    def __initActions(self):
+        """
+        Private method to define the user interface actions.
+        """
+        # list of all actions
+        self.__actions = []
+        
+        self.newTabAct = E5Action(
+            self.tr('New Tab'),
+            UI.PixmapCache.getIcon("tabNew.png"),
+            self.tr('&New Tab'),
+            QKeySequence(self.tr("Ctrl+T", "File|New Tab")),
+            0, self, 'webbrowser_file_new_tab')
+        self.newTabAct.setStatusTip(self.tr('Open a new web browser tab'))
+        self.newTabAct.setWhatsThis(self.tr(
+            """<b>New Tab</b>"""
+            """<p>This opens a new web browser tab.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.newTabAct.triggered.connect(self.newTab)
+        self.__actions.append(self.newTabAct)
+        
+        self.newAct = E5Action(
+            self.tr('New Window'),
+            UI.PixmapCache.getIcon("newWindow.png"),
+            self.tr('New &Window'),
+            QKeySequence(self.tr("Ctrl+N", "File|New Window")),
+            0, self, 'webbrowser_file_new_window')
+        self.newAct.setStatusTip(self.tr('Open a new web browser window'))
+        self.newAct.setWhatsThis(self.tr(
+            """<b>New Window</b>"""
+            """<p>This opens a new web browser window in the current"""
+            """ privacy mode.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.newAct.triggered.connect(self.newWindow)
+        self.__actions.append(self.newAct)
+        
+        self.newPrivateAct = E5Action(
+            self.tr('New Private Window'),
+            UI.PixmapCache.getIcon("privateMode.png"),
+            self.tr('New &Private Window'),
+            QKeySequence(self.tr("Ctrl+Shift+P", "File|New Private Window")),
+            0, self, 'webbrowser_file_new_private_window')
+        self.newPrivateAct.setStatusTip(self.tr(
+            'Open a new private web browser window'))
+        self.newPrivateAct.setWhatsThis(self.tr(
+            """<b>New Private Window</b>"""
+            """<p>This opens a new private web browser window by starting"""
+            """ a new web browser instance in private mode.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.newPrivateAct.triggered.connect(self.newPrivateWindow)
+        self.__actions.append(self.newPrivateAct)
+        
+        self.openAct = E5Action(
+            self.tr('Open File'),
+            UI.PixmapCache.getIcon("open.png"),
+            self.tr('&Open File'),
+            QKeySequence(self.tr("Ctrl+O", "File|Open")),
+            0, self, 'webbrowser_file_open')
+        self.openAct.setStatusTip(self.tr('Open a file for display'))
+        self.openAct.setWhatsThis(self.tr(
+            """<b>Open File</b>"""
+            """<p>This opens a new file for display."""
+            """ It pops up a file selection dialog.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.openAct.triggered.connect(self.__openFile)
+        self.__actions.append(self.openAct)
+        
+        self.openTabAct = E5Action(
+            self.tr('Open File in New Tab'),
+            UI.PixmapCache.getIcon("openNewTab.png"),
+            self.tr('Open File in New &Tab'),
+            QKeySequence(self.tr("Shift+Ctrl+O", "File|Open in new tab")),
+            0, self, 'webbrowser_file_open_tab')
+        self.openTabAct.setStatusTip(
+            self.tr('Open a file for display in a new tab'))
+        self.openTabAct.setWhatsThis(self.tr(
+            """<b>Open File in New Tab</b>"""
+            """<p>This opens a new file for display in a new tab."""
+            """ It pops up a file selection dialog.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.openTabAct.triggered.connect(self.__openFileNewTab)
+        self.__actions.append(self.openTabAct)
+        # TODO: Save
+##        
+##        self.saveAsAct = E5Action(
+##            self.tr('Save As'),
+##            UI.PixmapCache.getIcon("fileSaveAs.png"),
+##            self.tr('&Save As...'),
+##            QKeySequence(self.tr("Shift+Ctrl+S", "File|Save As")),
+##            0, self, 'webbrowser_file_save_as')
+##        self.saveAsAct.setStatusTip(
+##            self.tr('Save the current page to disk'))
+##        self.saveAsAct.setWhatsThis(self.tr(
+##            """<b>Save As...</b>"""
+##            """<p>Saves the current page to disk.</p>"""
+##        ))
+##        if not self.__initShortcutsOnly:
+##            self.saveAsAct.triggered.connect(self.__savePageAs)
+##        self.__actions.append(self.saveAsAct)
+##        
+        self.savePageScreenAct = E5Action(
+            self.tr('Save Page Screen'),
+            UI.PixmapCache.getIcon("fileSavePixmap.png"),
+            self.tr('Save Page Screen...'),
+            0, 0, self, 'webbrowser_file_save_page_screen')
+        self.savePageScreenAct.setStatusTip(
+            self.tr('Save the current page as a screen shot'))
+        self.savePageScreenAct.setWhatsThis(self.tr(
+            """<b>Save Page Screen...</b>"""
+            """<p>Saves the current page as a screen shot.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.savePageScreenAct.triggered.connect(self.__savePageScreen)
+        self.__actions.append(self.savePageScreenAct)
+        
+        self.saveVisiblePageScreenAct = E5Action(
+            self.tr('Save Visible Page Screen'),
+            UI.PixmapCache.getIcon("fileSaveVisiblePixmap.png"),
+            self.tr('Save Visible Page Screen...'),
+            0, 0, self, 'webbrowser_file_save_visible_page_screen')
+        self.saveVisiblePageScreenAct.setStatusTip(
+            self.tr('Save the visible part of the current page as a'
+                    ' screen shot'))
+        self.saveVisiblePageScreenAct.setWhatsThis(self.tr(
+            """<b>Save Visible Page Screen...</b>"""
+            """<p>Saves the visible part of the current page as a"""
+            """ screen shot.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.saveVisiblePageScreenAct.triggered.connect(
+                self.__saveVisiblePageScreen)
+        self.__actions.append(self.saveVisiblePageScreenAct)
+        
+        bookmarksManager = self.bookmarksManager()
+        self.importBookmarksAct = E5Action(
+            self.tr('Import Bookmarks'),
+            self.tr('&Import Bookmarks...'),
+            0, 0, self, 'webbrowser_file_import_bookmarks')
+        self.importBookmarksAct.setStatusTip(
+            self.tr('Import bookmarks from other browsers'))
+        self.importBookmarksAct.setWhatsThis(self.tr(
+            """<b>Import Bookmarks</b>"""
+            """<p>Import bookmarks from other browsers.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.importBookmarksAct.triggered.connect(
+                bookmarksManager.importBookmarks)
+        self.__actions.append(self.importBookmarksAct)
+        
+        self.exportBookmarksAct = E5Action(
+            self.tr('Export Bookmarks'),
+            self.tr('&Export Bookmarks...'),
+            0, 0, self, 'webbrowser_file_export_bookmarks')
+        self.exportBookmarksAct.setStatusTip(
+            self.tr('Export the bookmarks into a file'))
+        self.exportBookmarksAct.setWhatsThis(self.tr(
+            """<b>Export Bookmarks</b>"""
+            """<p>Export the bookmarks into a file.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.exportBookmarksAct.triggered.connect(
+                bookmarksManager.exportBookmarks)
+        self.__actions.append(self.exportBookmarksAct)
+        
+        self.printAct = E5Action(
+            self.tr('Print'),
+            UI.PixmapCache.getIcon("print.png"),
+            self.tr('&Print'),
+            QKeySequence(self.tr("Ctrl+P", "File|Print")),
+            0, self, 'webbrowser_file_print')
+        self.printAct.setStatusTip(self.tr('Print the displayed help'))
+        self.printAct.setWhatsThis(self.tr(
+            """<b>Print</b>"""
+            """<p>Print the displayed help text.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.printAct.triggered.connect(self.__tabWidget.printBrowser)
+        self.__actions.append(self.printAct)
+        
+        if Globals.isLinuxPlatform():
+            self.printPdfAct = E5Action(
+                self.tr('Print as PDF'),
+                UI.PixmapCache.getIcon("printPdf.png"),
+                self.tr('Print as PDF'),
+                0, 0, self, 'webbrowser_file_print_pdf')
+            self.printPdfAct.setStatusTip(self.tr(
+                'Print the displayed help as PDF'))
+            self.printPdfAct.setWhatsThis(self.tr(
+                """<b>Print as PDF</b>"""
+                """<p>Print the displayed help text as a PDF file.</p>"""
+            ))
+            if not self.__initShortcutsOnly:
+                self.printPdfAct.triggered.connect(
+                    self.__tabWidget.printBrowserPdf)
+            self.__actions.append(self.printPdfAct)
+        else:
+            self.printPdfAct = None
+        
+        self.printPreviewAct = E5Action(
+            self.tr('Print Preview'),
+            UI.PixmapCache.getIcon("printPreview.png"),
+            self.tr('Print Preview'),
+            0, 0, self, 'webbrowser_file_print_preview')
+        self.printPreviewAct.setStatusTip(self.tr(
+            'Print preview of the displayed help'))
+        self.printPreviewAct.setWhatsThis(self.tr(
+            """<b>Print Preview</b>"""
+            """<p>Print preview of the displayed help text.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.printPreviewAct.triggered.connect(
+                self.__tabWidget.printPreviewBrowser)
+        self.__actions.append(self.printPreviewAct)
+        
+        self.closeAct = E5Action(
+            self.tr('Close'),
+            UI.PixmapCache.getIcon("close.png"),
+            self.tr('&Close'),
+            QKeySequence(self.tr("Ctrl+W", "File|Close")),
+            0, self, 'webbrowser_file_close')
+        self.closeAct.setStatusTip(self.tr(
+            'Close the current help window'))
+        self.closeAct.setWhatsThis(self.tr(
+            """<b>Close</b>"""
+            """<p>Closes the current web browser window.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.closeAct.triggered.connect(self.__tabWidget.closeBrowser)
+        self.__actions.append(self.closeAct)
+        
+        self.closeAllAct = E5Action(
+            self.tr('Close All'),
+            self.tr('Close &All'),
+            0, 0, self, 'webbrowser_file_close_all')
+        self.closeAllAct.setStatusTip(self.tr('Close all help windows'))
+        self.closeAllAct.setWhatsThis(self.tr(
+            """<b>Close All</b>"""
+            """<p>Closes all web browser windows except the first one.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.closeAllAct.triggered.connect(
+                self.__tabWidget.closeAllBrowsers)
+        self.__actions.append(self.closeAllAct)
+        
+        self.exitAct = E5Action(
+            self.tr('Quit'),
+            UI.PixmapCache.getIcon("exit.png"),
+            self.tr('&Quit'),
+            QKeySequence(self.tr("Ctrl+Q", "File|Quit")),
+            0, self, 'webbrowser_file_quit')
+        self.exitAct.setStatusTip(self.tr('Quit the eric6 Web Browser'))
+        self.exitAct.setWhatsThis(self.tr(
+            """<b>Quit</b>"""
+            """<p>Quit the eric6 Web Browser.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            if self.__fromEric:
+                self.exitAct.triggered.connect(self.close)
+            else:
+                self.exitAct.triggered.connect(self.__closeAllWindows)
+        self.__actions.append(self.exitAct)
+        
+        self.backAct = E5Action(
+            self.tr('Backward'),
+            UI.PixmapCache.getIcon("back.png"),
+            self.tr('&Backward'),
+            QKeySequence(self.tr("Alt+Left", "Go|Backward")),
+            0, self, 'webbrowser_go_backward')
+        self.backAct.setStatusTip(self.tr('Move one screen backward'))
+        self.backAct.setWhatsThis(self.tr(
+            """<b>Backward</b>"""
+            """<p>Moves one screen backward. If none is"""
+            """ available, this action is disabled.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.backAct.triggered.connect(self.__backward)
+        self.__actions.append(self.backAct)
+        
+        self.forwardAct = E5Action(
+            self.tr('Forward'),
+            UI.PixmapCache.getIcon("forward.png"),
+            self.tr('&Forward'),
+            QKeySequence(self.tr("Alt+Right", "Go|Forward")),
+            0, self, 'webbrowser_go_foreward')
+        self.forwardAct.setStatusTip(self.tr(
+            'Move one screen forward'))
+        self.forwardAct.setWhatsThis(self.tr(
+            """<b>Forward</b>"""
+            """<p>Moves one screen forward. If none is"""
+            """ available, this action is disabled.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.forwardAct.triggered.connect(self.__forward)
+        self.__actions.append(self.forwardAct)
+        
+        self.homeAct = E5Action(
+            self.tr('Home'),
+            UI.PixmapCache.getIcon("home.png"),
+            self.tr('&Home'),
+            QKeySequence(self.tr("Ctrl+Home", "Go|Home")),
+            0, self, 'webbrowser_go_home')
+        self.homeAct.setStatusTip(self.tr(
+            'Move to the initial help screen'))
+        self.homeAct.setWhatsThis(self.tr(
+            """<b>Home</b>"""
+            """<p>Moves to the initial screen.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.homeAct.triggered.connect(self.__home)
+        self.__actions.append(self.homeAct)
+        
+        self.reloadAct = E5Action(
+            self.tr('Reload'),
+            UI.PixmapCache.getIcon("reload.png"),
+            self.tr('&Reload'),
+            QKeySequence(self.tr("Ctrl+R", "Go|Reload")),
+            QKeySequence(self.tr("F5", "Go|Reload")),
+            self, 'webbrowser_go_reload')
+        self.reloadAct.setStatusTip(self.tr(
+            'Reload the current screen'))
+        self.reloadAct.setWhatsThis(self.tr(
+            """<b>Reload</b>"""
+            """<p>Reloads the current screen.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.reloadAct.triggered.connect(self.__reload)
+        self.__actions.append(self.reloadAct)
+        
+        self.stopAct = E5Action(
+            self.tr('Stop'),
+            UI.PixmapCache.getIcon("stopLoading.png"),
+            self.tr('&Stop'),
+            QKeySequence(self.tr("Ctrl+.", "Go|Stop")),
+            QKeySequence(self.tr("Esc", "Go|Stop")),
+            self, 'webbrowser_go_stop')
+        self.stopAct.setStatusTip(self.tr('Stop loading'))
+        self.stopAct.setWhatsThis(self.tr(
+            """<b>Stop</b>"""
+            """<p>Stops loading of the current tab.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.stopAct.triggered.connect(self.__stopLoading)
+        self.__actions.append(self.stopAct)
+        
+        self.copyAct = E5Action(
+            self.tr('Copy'),
+            UI.PixmapCache.getIcon("editCopy.png"),
+            self.tr('&Copy'),
+            QKeySequence(self.tr("Ctrl+C", "Edit|Copy")),
+            0, self, 'webbrowser_edit_copy')
+        self.copyAct.setStatusTip(self.tr('Copy the selected text'))
+        self.copyAct.setWhatsThis(self.tr(
+            """<b>Copy</b>"""
+            """<p>Copy the selected text to the clipboard.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.copyAct.triggered.connect(self.__copy)
+        self.__actions.append(self.copyAct)
+        
+        self.cutAct = E5Action(
+            self.tr('Cut'),
+            UI.PixmapCache.getIcon("editCut.png"),
+            self.tr('Cu&t'),
+            QKeySequence(self.tr("Ctrl+X", "Edit|Cut")),
+            0, self, 'webbrowser_edit_cut')
+        self.cutAct.setStatusTip(self.tr('Cut the selected text'))
+        self.cutAct.setWhatsThis(self.tr(
+            """<b>Cut</b>"""
+            """<p>Cut the selected text to the clipboard.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.cutAct.triggered.connect(self.__cut)
+        self.__actions.append(self.cutAct)
+        
+        self.pasteAct = E5Action(
+            self.tr('Paste'),
+            UI.PixmapCache.getIcon("editPaste.png"),
+            self.tr('&Paste'),
+            QKeySequence(self.tr("Ctrl+V", "Edit|Paste")),
+            0, self, 'webbrowser_edit_paste')
+        self.pasteAct.setStatusTip(self.tr('Paste text from the clipboard'))
+        self.pasteAct.setWhatsThis(self.tr(
+            """<b>Paste</b>"""
+            """<p>Paste some text from the clipboard.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.pasteAct.triggered.connect(self.__paste)
+        self.__actions.append(self.pasteAct)
+        
+        self.undoAct = E5Action(
+            self.tr('Undo'),
+            UI.PixmapCache.getIcon("editUndo.png"),
+            self.tr('&Undo'),
+            QKeySequence(self.tr("Ctrl+Z", "Edit|Undo")),
+            0, self, 'webbrowser_edit_undo')
+        self.undoAct.setStatusTip(self.tr('Undo the last edit action'))
+        self.undoAct.setWhatsThis(self.tr(
+            """<b>Undo</b>"""
+            """<p>Undo the last edit action.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.undoAct.triggered.connect(self.__undo)
+        self.__actions.append(self.undoAct)
+        
+        self.redoAct = E5Action(
+            self.tr('Redo'),
+            UI.PixmapCache.getIcon("editRedo.png"),
+            self.tr('&Redo'),
+            QKeySequence(self.tr("Ctrl+Shift+Z", "Edit|Redo")),
+            0, self, 'webbrowser_edit_redo')
+        self.redoAct.setStatusTip(self.tr('Redo the last edit action'))
+        self.redoAct.setWhatsThis(self.tr(
+            """<b>Redo</b>"""
+            """<p>Redo the last edit action.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.redoAct.triggered.connect(self.__redo)
+        self.__actions.append(self.redoAct)
+        
+        self.selectAllAct = E5Action(
+            self.tr('Select All'),
+            UI.PixmapCache.getIcon("editSelectAll.png"),
+            self.tr('&Select All'),
+            QKeySequence(self.tr("Ctrl+A", "Edit|Select All")),
+            0, self, 'webbrowser_edit_select_all')
+        self.selectAllAct.setStatusTip(self.tr('Select all text'))
+        self.selectAllAct.setWhatsThis(self.tr(
+            """<b>Select All</b>"""
+            """<p>Select all text of the current browser.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.selectAllAct.triggered.connect(self.__selectAll)
+        self.__actions.append(self.selectAllAct)
+        
+        self.findAct = E5Action(
+            self.tr('Find...'),
+            UI.PixmapCache.getIcon("find.png"),
+            self.tr('&Find...'),
+            QKeySequence(self.tr("Ctrl+F", "Edit|Find")),
+            0, self, 'webbrowser_edit_find')
+        self.findAct.setStatusTip(self.tr('Find text in page'))
+        self.findAct.setWhatsThis(self.tr(
+            """<b>Find</b>"""
+            """<p>Find text in the current page.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.findAct.triggered.connect(self.__find)
+        self.__actions.append(self.findAct)
+        
+        self.findNextAct = E5Action(
+            self.tr('Find next'),
+            UI.PixmapCache.getIcon("findNext.png"),
+            self.tr('Find &next'),
+            QKeySequence(self.tr("F3", "Edit|Find next")),
+            0, self, 'webbrowser_edit_find_next')
+        self.findNextAct.setStatusTip(self.tr(
+            'Find next occurrence of text in page'))
+        self.findNextAct.setWhatsThis(self.tr(
+            """<b>Find next</b>"""
+            """<p>Find the next occurrence of text in the current page.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.findNextAct.triggered.connect(self.__searchWidget.findNext)
+        self.__actions.append(self.findNextAct)
+        
+        self.findPrevAct = E5Action(
+            self.tr('Find previous'),
+            UI.PixmapCache.getIcon("findPrev.png"),
+            self.tr('Find &previous'),
+            QKeySequence(self.tr("Shift+F3", "Edit|Find previous")),
+            0, self, 'webbrowser_edit_find_previous')
+        self.findPrevAct.setStatusTip(
+            self.tr('Find previous occurrence of text in page'))
+        self.findPrevAct.setWhatsThis(self.tr(
+            """<b>Find previous</b>"""
+            """<p>Find the previous occurrence of text in the current"""
+            """ page.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.findPrevAct.triggered.connect(
+                self.__searchWidget.findPrevious)
+        self.__actions.append(self.findPrevAct)
+        
+        self.bookmarksManageAct = E5Action(
+            self.tr('Manage Bookmarks'),
+            self.tr('&Manage Bookmarks...'),
+            QKeySequence(self.tr("Ctrl+Shift+B", "Help|Manage bookmarks")),
+            0, self, 'webbrowser_bookmarks_manage')
+        self.bookmarksManageAct.setStatusTip(self.tr(
+            'Open a dialog to manage the bookmarks.'))
+        self.bookmarksManageAct.setWhatsThis(self.tr(
+            """<b>Manage Bookmarks...</b>"""
+            """<p>Open a dialog to manage the bookmarks.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.bookmarksManageAct.triggered.connect(
+                self.__showBookmarksDialog)
+        self.__actions.append(self.bookmarksManageAct)
+        
+        self.bookmarksAddAct = E5Action(
+            self.tr('Add Bookmark'),
+            UI.PixmapCache.getIcon("addBookmark.png"),
+            self.tr('Add &Bookmark...'),
+            QKeySequence(self.tr("Ctrl+D", "Help|Add bookmark")),
+            0, self, 'webbrowser_bookmark_add')
+        self.bookmarksAddAct.setIconVisibleInMenu(False)
+        self.bookmarksAddAct.setStatusTip(self.tr(
+            'Open a dialog to add a bookmark.'))
+        self.bookmarksAddAct.setWhatsThis(self.tr(
+            """<b>Add Bookmark</b>"""
+            """<p>Open a dialog to add the current URL as a bookmark.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.bookmarksAddAct.triggered.connect(self.__addBookmark)
+        self.__actions.append(self.bookmarksAddAct)
+        
+        self.bookmarksAddFolderAct = E5Action(
+            self.tr('Add Folder'),
+            self.tr('Add &Folder...'),
+            0, 0, self, 'webbrowser_bookmark_show_all')
+        self.bookmarksAddFolderAct.setStatusTip(self.tr(
+            'Open a dialog to add a new bookmarks folder.'))
+        self.bookmarksAddFolderAct.setWhatsThis(self.tr(
+            """<b>Add Folder...</b>"""
+            """<p>Open a dialog to add a new bookmarks folder.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.bookmarksAddFolderAct.triggered.connect(
+                self.__addBookmarkFolder)
+        self.__actions.append(self.bookmarksAddFolderAct)
+        
+        self.bookmarksAllTabsAct = E5Action(
+            self.tr('Bookmark All Tabs'),
+            self.tr('Bookmark All Tabs...'),
+            0, 0, self, 'webbrowser_bookmark_all_tabs')
+        self.bookmarksAllTabsAct.setStatusTip(self.tr(
+            'Bookmark all open tabs.'))
+        self.bookmarksAllTabsAct.setWhatsThis(self.tr(
+            """<b>Bookmark All Tabs...</b>"""
+            """<p>Open a dialog to add a new bookmarks folder for"""
+            """ all open tabs.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.bookmarksAllTabsAct.triggered.connect(self.bookmarkAll)
+        self.__actions.append(self.bookmarksAllTabsAct)
+        
+        self.whatsThisAct = E5Action(
+            self.tr('What\'s This?'),
+            UI.PixmapCache.getIcon("whatsThis.png"),
+            self.tr('&What\'s This?'),
+            QKeySequence(self.tr("Shift+F1", "Help|What's This?'")),
+            0, self, 'webbrowser_help_whats_this')
+        self.whatsThisAct.setStatusTip(self.tr('Context sensitive help'))
+        self.whatsThisAct.setWhatsThis(self.tr(
+            """<b>Display context sensitive help</b>"""
+            """<p>In What's This? mode, the mouse cursor shows an arrow"""
+            """ with a question mark, and you can click on the interface"""
+            """ elements to get a short description of what they do and how"""
+            """ to use them. In dialogs, this feature can be accessed using"""
+            """ the context help button in the titlebar.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.whatsThisAct.triggered.connect(self.__whatsThis)
+        self.__actions.append(self.whatsThisAct)
+        
+        self.aboutAct = E5Action(
+            self.tr('About'),
+            self.tr('&About'),
+            0, 0, self, 'webbrowser_help_about')
+        self.aboutAct.setStatusTip(self.tr(
+            'Display information about this software'))
+        self.aboutAct.setWhatsThis(self.tr(
+            """<b>About</b>"""
+            """<p>Display some information about this software.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.aboutAct.triggered.connect(self.__about)
+        self.__actions.append(self.aboutAct)
+        
+        self.aboutQtAct = E5Action(
+            self.tr('About Qt'),
+            self.tr('About &Qt'),
+            0, 0, self, 'webbrowser_help_about_qt')
+        self.aboutQtAct.setStatusTip(
+            self.tr('Display information about the Qt toolkit'))
+        self.aboutQtAct.setWhatsThis(self.tr(
+            """<b>About Qt</b>"""
+            """<p>Display some information about the Qt toolkit.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.aboutQtAct.triggered.connect(self.__aboutQt)
+        self.__actions.append(self.aboutQtAct)
+        
+        self.zoomInAct = E5Action(
+            self.tr('Zoom in'),
+            UI.PixmapCache.getIcon("zoomIn.png"),
+            self.tr('Zoom &in'),
+            QKeySequence(self.tr("Ctrl++", "View|Zoom in")),
+            QKeySequence(self.tr("Zoom In", "View|Zoom in")),
+            self, 'webbrowser_view_zoom_in')
+        self.zoomInAct.setStatusTip(self.tr('Zoom in on the web page'))
+        self.zoomInAct.setWhatsThis(self.tr(
+            """<b>Zoom in</b>"""
+            """<p>Zoom in on the web page."""
+            """ This makes the web page bigger.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.zoomInAct.triggered.connect(self.__zoomIn)
+        self.__actions.append(self.zoomInAct)
+        
+        self.zoomOutAct = E5Action(
+            self.tr('Zoom out'),
+            UI.PixmapCache.getIcon("zoomOut.png"),
+            self.tr('Zoom &out'),
+            QKeySequence(self.tr("Ctrl+-", "View|Zoom out")),
+            QKeySequence(self.tr("Zoom Out", "View|Zoom out")),
+            self, 'webbrowser_view_zoom_out')
+        self.zoomOutAct.setStatusTip(self.tr('Zoom out on the web page'))
+        self.zoomOutAct.setWhatsThis(self.tr(
+            """<b>Zoom out</b>"""
+            """<p>Zoom out on the web page."""
+            """ This makes the web page smaller.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.zoomOutAct.triggered.connect(self.__zoomOut)
+        self.__actions.append(self.zoomOutAct)
+        
+        self.zoomResetAct = E5Action(
+            self.tr('Zoom reset'),
+            UI.PixmapCache.getIcon("zoomReset.png"),
+            self.tr('Zoom &reset'),
+            QKeySequence(self.tr("Ctrl+0", "View|Zoom reset")),
+            0, self, 'webbrowser_view_zoom_reset')
+        self.zoomResetAct.setStatusTip(self.tr(
+            'Reset the zoom of the web page'))
+        self.zoomResetAct.setWhatsThis(self.tr(
+            """<b>Zoom reset</b>"""
+            """<p>Reset the zoom of the web page. """
+            """This sets the zoom factor to 100%.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.zoomResetAct.triggered.connect(self.__zoomReset)
+        self.__actions.append(self.zoomResetAct)
+        
+        self.pageSourceAct = E5Action(
+            self.tr('Show page source'),
+            self.tr('Show page source'),
+            QKeySequence(self.tr('Ctrl+U')), 0,
+            self, 'webbrowser_show_page_source')
+        self.pageSourceAct.setStatusTip(self.tr(
+            'Show the page source in an editor'))
+        self.pageSourceAct.setWhatsThis(self.tr(
+            """<b>Show page source</b>"""
+            """<p>Show the page source in an editor.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.pageSourceAct.triggered.connect(self.__showPageSource)
+        self.__actions.append(self.pageSourceAct)
+        self.addAction(self.pageSourceAct)
+        
+        self.fullScreenAct = E5Action(
+            self.tr('Full Screen'),
+            UI.PixmapCache.getIcon("windowFullscreen.png"),
+            self.tr('&Full Screen'),
+            QKeySequence(self.tr('F11')), 0,
+            self, 'webbrowser_view_full_scree')
+        if not self.__initShortcutsOnly:
+            self.fullScreenAct.triggered.connect(self.__viewFullScreen)
+        self.__actions.append(self.fullScreenAct)
+        self.addAction(self.fullScreenAct)
+        
+        self.nextTabAct = E5Action(
+            self.tr('Show next tab'),
+            self.tr('Show next tab'),
+            QKeySequence(self.tr('Ctrl+Alt+Tab')), 0,
+            self, 'webbrowser_view_next_tab')
+        if not self.__initShortcutsOnly:
+            self.nextTabAct.triggered.connect(self.__nextTab)
+        self.__actions.append(self.nextTabAct)
+        self.addAction(self.nextTabAct)
+        
+        self.prevTabAct = E5Action(
+            self.tr('Show previous tab'),
+            self.tr('Show previous tab'),
+            QKeySequence(self.tr('Shift+Ctrl+Alt+Tab')), 0,
+            self, 'webbrowser_view_previous_tab')
+        if not self.__initShortcutsOnly:
+            self.prevTabAct.triggered.connect(self.__prevTab)
+        self.__actions.append(self.prevTabAct)
+        self.addAction(self.prevTabAct)
+        
+        self.switchTabAct = E5Action(
+            self.tr('Switch between tabs'),
+            self.tr('Switch between tabs'),
+            QKeySequence(self.tr('Ctrl+1')), 0,
+            self, 'webbrowser_switch_tabs')
+        if not self.__initShortcutsOnly:
+            self.switchTabAct.triggered.connect(self.__switchTab)
+        self.__actions.append(self.switchTabAct)
+        self.addAction(self.switchTabAct)
+        
+        self.prefAct = E5Action(
+            self.tr('Preferences'),
+            UI.PixmapCache.getIcon("configure.png"),
+            self.tr('&Preferences...'), 0, 0, self, 'webbrowser_preferences')
+        self.prefAct.setStatusTip(self.tr(
+            'Set the prefered configuration'))
+        self.prefAct.setWhatsThis(self.tr(
+            """<b>Preferences</b>"""
+            """<p>Set the configuration items of the application"""
+            """ with your prefered values.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.prefAct.triggered.connect(self.__showPreferences)
+        self.__actions.append(self.prefAct)
+        
+        self.acceptedLanguagesAct = E5Action(
+            self.tr('Languages'),
+            UI.PixmapCache.getIcon("flag.png"),
+            self.tr('&Languages...'), 0, 0,
+            self, 'webbrowser_accepted_languages')
+        self.acceptedLanguagesAct.setStatusTip(self.tr(
+            'Configure the accepted languages for web pages'))
+        self.acceptedLanguagesAct.setWhatsThis(self.tr(
+            """<b>Languages</b>"""
+            """<p>Configure the accepted languages for web pages.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.acceptedLanguagesAct.triggered.connect(
+                self.__showAcceptedLanguages)
+        self.__actions.append(self.acceptedLanguagesAct)
+        
+        self.cookiesAct = E5Action(
+            self.tr('Cookies'),
+            UI.PixmapCache.getIcon("cookie.png"),
+            self.tr('C&ookies...'), 0, 0, self, 'webbrowser_cookies')
+        self.cookiesAct.setStatusTip(self.tr(
+            'Configure cookies handling'))
+        self.cookiesAct.setWhatsThis(self.tr(
+            """<b>Cookies</b>"""
+            """<p>Configure cookies handling.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.cookiesAct.triggered.connect(
+                self.__showCookiesConfiguration)
+        self.__actions.append(self.cookiesAct)
+        
+        self.flashCookiesAct = E5Action(
+            self.tr('Flash Cookies'),
+            UI.PixmapCache.getIcon("flashCookie.png"),
+            self.tr('&Flash Cookies...'), 0, 0, self,
+            'webbrowser_flash_cookies')
+        self.flashCookiesAct.setStatusTip(self.tr(
+            'Manage flash cookies'))
+        self.flashCookiesAct.setWhatsThis(self.tr(
+            """<b>Flash Cookies</b>"""
+            """<p>Show a dialog to manage the flash cookies.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.flashCookiesAct.triggered.connect(
+                self.__showFlashCookiesManagement)
+        self.__actions.append(self.flashCookiesAct)
+        
+        # TODO: Offline Storage
+##        self.offlineStorageAct = E5Action(
+##            self.tr('Offline Storage'),
+##            UI.PixmapCache.getIcon("preferences-html5.png"),
+##            self.tr('Offline &Storage...'), 0, 0,
+##            self, 'webbrowser_offline_storage')
+##        self.offlineStorageAct.setStatusTip(self.tr(
+##            'Configure offline storage'))
+##        self.offlineStorageAct.setWhatsThis(self.tr(
+##            """<b>Offline Storage</b>"""
+##            """<p>Opens a dialog to configure offline storage.</p>"""
+##        ))
+##        if not self.__initShortcutsOnly:
+##            self.offlineStorageAct.triggered.connect(
+##                self.__showOfflineStorageConfiguration)
+##        self.__actions.append(self.offlineStorageAct)
+        
+        self.personalDataAct = E5Action(
+            self.tr('Personal Information'),
+            UI.PixmapCache.getIcon("pim.png"),
+            self.tr('Personal Information...'),
+            0, 0,
+            self, 'webbrowser_personal_information')
+        self.personalDataAct.setStatusTip(self.tr(
+            'Configure personal information for completing form fields'))
+        self.personalDataAct.setWhatsThis(self.tr(
+            """<b>Personal Information...</b>"""
+            """<p>Opens a dialog to configure the personal information"""
+            """ used for completing form fields.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.personalDataAct.triggered.connect(
+                self.__showPersonalInformationDialog)
+        self.__actions.append(self.personalDataAct)
+        
+        self.greaseMonkeyAct = E5Action(
+            self.tr('GreaseMonkey Scripts'),
+            UI.PixmapCache.getIcon("greaseMonkey.png"),
+            self.tr('GreaseMonkey Scripts...'),
+            0, 0,
+            self, 'webbrowser_greasemonkey')
+        self.greaseMonkeyAct.setStatusTip(self.tr(
+            'Configure the GreaseMonkey Scripts'))
+        self.greaseMonkeyAct.setWhatsThis(self.tr(
+            """<b>GreaseMonkey Scripts...</b>"""
+            """<p>Opens a dialog to configure the available GreaseMonkey"""
+            """ Scripts.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.greaseMonkeyAct.triggered.connect(
+                self.__showGreaseMonkeyConfigDialog)
+        self.__actions.append(self.greaseMonkeyAct)
+        
+        self.editMessageFilterAct = E5Action(
+            self.tr('Edit Message Filters'),
+            UI.PixmapCache.getIcon("warning.png"),
+            self.tr('Edit Message Filters...'), 0, 0, self,
+            'webbrowser_manage_message_filters')
+        self.editMessageFilterAct.setStatusTip(self.tr(
+            'Edit the message filters used to suppress unwanted messages'))
+        self.editMessageFilterAct.setWhatsThis(self.tr(
+            """<b>Edit Message Filters</b>"""
+            """<p>Opens a dialog to edit the message filters used to"""
+            """ suppress unwanted messages been shown in an error"""
+            """ window.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.editMessageFilterAct.triggered.connect(
+                E5ErrorMessage.editMessageFilters)
+        self.__actions.append(self.editMessageFilterAct)
+        
+        self.featurePermissionAct = E5Action(
+            self.tr('Edit HTML5 Feature Permissions'),
+            UI.PixmapCache.getIcon("featurePermission.png"),
+            self.tr('Edit HTML5 Feature Permissions...'), 0, 0, self,
+            'webbrowser_edit_feature_permissions')
+        self.featurePermissionAct.setStatusTip(self.tr(
+            'Edit the remembered HTML5 feature permissions'))
+        self.featurePermissionAct.setWhatsThis(self.tr(
+            """<b>Edit HTML5 Feature Permissions</b>"""
+            """<p>Opens a dialog to edit the remembered HTML5"""
+            """ feature permissions.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.featurePermissionAct.triggered.connect(
+                self.__showFeaturePermissionDialog)
+        self.__actions.append(self.featurePermissionAct)
+        
+        if WebBrowserWindow.useQtHelp or self.__initShortcutsOnly:
+            self.syncTocAct = E5Action(
+                self.tr('Sync with Table of Contents'),
+                UI.PixmapCache.getIcon("syncToc.png"),
+                self.tr('Sync with Table of Contents'),
+                0, 0, self, 'webbrowser_sync_toc')
+            self.syncTocAct.setStatusTip(self.tr(
+                'Synchronizes the table of contents with current page'))
+            self.syncTocAct.setWhatsThis(self.tr(
+                """<b>Sync with Table of Contents</b>"""
+                """<p>Synchronizes the table of contents with current"""
+                """ page.</p>"""
+            ))
+            if not self.__initShortcutsOnly:
+                self.syncTocAct.triggered.connect(self.__syncTOC)
+            self.__actions.append(self.syncTocAct)
+            
+            self.showTocAct = E5Action(
+                self.tr('Table of Contents'),
+                self.tr('Table of Contents'),
+                0, 0, self, 'webbrowser_show_toc')
+            self.showTocAct.setStatusTip(self.tr(
+                'Shows the table of contents window'))
+            self.showTocAct.setWhatsThis(self.tr(
+                """<b>Table of Contents</b>"""
+                """<p>Shows the table of contents window.</p>"""
+            ))
+            if not self.__initShortcutsOnly:
+                self.showTocAct.triggered.connect(self.__showTocWindow)
+            self.__actions.append(self.showTocAct)
+            
+            self.showIndexAct = E5Action(
+                self.tr('Index'),
+                self.tr('Index'),
+                0, 0, self, 'webbrowser_show_index')
+            self.showIndexAct.setStatusTip(self.tr(
+                'Shows the index window'))
+            self.showIndexAct.setWhatsThis(self.tr(
+                """<b>Index</b>"""
+                """<p>Shows the index window.</p>"""
+            ))
+            if not self.__initShortcutsOnly:
+                self.showIndexAct.triggered.connect(self.__showIndexWindow)
+            self.__actions.append(self.showIndexAct)
+            
+            self.showSearchAct = E5Action(
+                self.tr('Search'),
+                self.tr('Search'),
+                0, 0, self, 'webbrowser_show_search')
+            self.showSearchAct.setStatusTip(self.tr(
+                'Shows the search window'))
+            self.showSearchAct.setWhatsThis(self.tr(
+                """<b>Search</b>"""
+                """<p>Shows the search window.</p>"""
+            ))
+            if not self.__initShortcutsOnly:
+                self.showSearchAct.triggered.connect(
+                    self.__showSearchWindow)
+            self.__actions.append(self.showSearchAct)
+            
+            self.manageQtHelpDocsAct = E5Action(
+                self.tr('Manage QtHelp Documents'),
+                self.tr('Manage QtHelp &Documents'),
+                0, 0, self, 'webbrowser_qthelp_documents')
+            self.manageQtHelpDocsAct.setStatusTip(self.tr(
+                'Shows a dialog to manage the QtHelp documentation set'))
+            self.manageQtHelpDocsAct.setWhatsThis(self.tr(
+                """<b>Manage QtHelp Documents</b>"""
+                """<p>Shows a dialog to manage the QtHelp documentation"""
+                """ set.</p>"""
+            ))
+            if not self.__initShortcutsOnly:
+                self.manageQtHelpDocsAct.triggered.connect(
+                    self.__manageQtHelpDocumentation)
+            self.__actions.append(self.manageQtHelpDocsAct)
+            
+            self.manageQtHelpFiltersAct = E5Action(
+                self.tr('Manage QtHelp Filters'),
+                self.tr('Manage QtHelp &Filters'),
+                0, 0, self, 'webbrowser_qthelp_filters')
+            self.manageQtHelpFiltersAct.setStatusTip(self.tr(
+                'Shows a dialog to manage the QtHelp filters'))
+            self.manageQtHelpFiltersAct.setWhatsThis(self.tr(
+                """<b>Manage QtHelp Filters</b>"""
+                """<p>Shows a dialog to manage the QtHelp filters.</p>"""
+            ))
+            if not self.__initShortcutsOnly:
+                self.manageQtHelpFiltersAct.triggered.connect(
+                    self.__manageQtHelpFilters)
+            self.__actions.append(self.manageQtHelpFiltersAct)
+            
+            self.reindexDocumentationAct = E5Action(
+                self.tr('Reindex Documentation'),
+                self.tr('&Reindex Documentation'),
+                0, 0, self, 'webbrowser_qthelp_reindex')
+            self.reindexDocumentationAct.setStatusTip(self.tr(
+                'Reindexes the documentation set'))
+            self.reindexDocumentationAct.setWhatsThis(self.tr(
+                """<b>Reindex Documentation</b>"""
+                """<p>Reindexes the documentation set.</p>"""
+            ))
+            if not self.__initShortcutsOnly:
+                self.reindexDocumentationAct.triggered.connect(
+                    self.__searchEngine.reindexDocumentation)
+            self.__actions.append(self.reindexDocumentationAct)
+        
+        self.clearPrivateDataAct = E5Action(
+            self.tr('Clear private data'),
+            self.tr('&Clear private data'),
+            0, 0,
+            self, 'webbrowser_clear_private_data')
+        self.clearPrivateDataAct.setStatusTip(self.tr(
+            'Clear private data'))
+        self.clearPrivateDataAct.setWhatsThis(self.tr(
+            """<b>Clear private data</b>"""
+            """<p>Clears the private data like browsing history, search"""
+            """ history or the favicons database.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.clearPrivateDataAct.triggered.connect(
+                self.__clearPrivateData)
+        self.__actions.append(self.clearPrivateDataAct)
+        
+        self.clearIconsAct = E5Action(
+            self.tr('Clear icons database'),
+            self.tr('Clear &icons database'),
+            0, 0,
+            self, 'webbrowser_clear_icons_db')
+        self.clearIconsAct.setStatusTip(self.tr(
+            'Clear the database of favicons'))
+        self.clearIconsAct.setWhatsThis(self.tr(
+            """<b>Clear icons database</b>"""
+            """<p>Clears the database of favicons of previously visited"""
+            """ URLs.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.clearIconsAct.triggered.connect(self.__clearIconsDatabase)
+        self.__actions.append(self.clearIconsAct)
+        
+        self.manageIconsAct = E5Action(
+            self.tr('Manage saved Favicons'),
+            UI.PixmapCache.getIcon("icons.png"),
+            self.tr('Manage saved Favicons'),
+            0, 0,
+            self, 'webbrowser_manage_icons_db')
+        self.manageIconsAct.setStatusTip(self.tr(
+            'Show a dialog to manage the saved favicons'))
+        self.manageIconsAct.setWhatsThis(self.tr(
+            """<b>Manage saved Favicons</b>"""
+            """<p>This shows a dialog to manage the saved favicons of"""
+            """ previously visited URLs.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.manageIconsAct.triggered.connect(self.__showWebIconsDialog)
+        self.__actions.append(self.manageIconsAct)
+        
+        self.searchEnginesAct = E5Action(
+            self.tr('Configure Search Engines'),
+            self.tr('Configure Search &Engines...'),
+            0, 0,
+            self, 'webbrowser_search_engines')
+        self.searchEnginesAct.setStatusTip(self.tr(
+            'Configure the available search engines'))
+        self.searchEnginesAct.setWhatsThis(self.tr(
+            """<b>Configure Search Engines...</b>"""
+            """<p>Opens a dialog to configure the available search"""
+            """ engines.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.searchEnginesAct.triggered.connect(
+                self.__showEnginesConfigurationDialog)
+        self.__actions.append(self.searchEnginesAct)
+        
+        self.passwordsAct = E5Action(
+            self.tr('Manage Saved Passwords'),
+            UI.PixmapCache.getIcon("passwords.png"),
+            self.tr('Manage Saved Passwords...'),
+            0, 0,
+            self, 'webbrowser_manage_passwords')
+        self.passwordsAct.setStatusTip(self.tr(
+            'Manage the saved passwords'))
+        self.passwordsAct.setWhatsThis(self.tr(
+            """<b>Manage Saved Passwords...</b>"""
+            """<p>Opens a dialog to manage the saved passwords.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.passwordsAct.triggered.connect(self.__showPasswordsDialog)
+        self.__actions.append(self.passwordsAct)
+        
+        self.adblockAct = E5Action(
+            self.tr('Ad Block'),
+            UI.PixmapCache.getIcon("adBlockPlus.png"),
+            self.tr('&Ad Block...'),
+            0, 0,
+            self, 'webbrowser_adblock')
+        self.adblockAct.setStatusTip(self.tr(
+            'Configure AdBlock subscriptions and rules'))
+        self.adblockAct.setWhatsThis(self.tr(
+            """<b>Ad Block...</b>"""
+            """<p>Opens a dialog to configure AdBlock subscriptions and"""
+            """ rules.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.adblockAct.triggered.connect(self.__showAdBlockDialog)
+        self.__actions.append(self.adblockAct)
+        
+        # TODO: Click2Flash (?)
+##        self.flashblockAct = E5Action(
+##            self.tr('ClickToFlash'),
+##            UI.PixmapCache.getIcon("flashBlock.png"),
+##            self.tr('&ClickToFlash...'),
+##            0, 0,
+##            self, 'webbrowser_flashblock')
+##        self.flashblockAct.setStatusTip(self.tr(
+##            'Configure ClickToFlash whitelist'))
+##        self.flashblockAct.setWhatsThis(self.tr(
+##            """<b>ClickToFlash...</b>"""
+##            """<p>Opens a dialog to configure the ClickToFlash"""
+##            """ whitelist.</p>"""
+##        ))
+##        if not self.__initShortcutsOnly:
+##            self.flashblockAct.triggered.connect(
+##                self.__showClickToFlashDialog)
+##        self.__actions.append(self.flashblockAct)
+        
+        # TODO: Certificates
+##        
+##        from .Network.NetworkManager import SSL_AVAILABLE
+##        if SSL_AVAILABLE:
+##            self.certificatesAct = E5Action(
+##                self.tr('Manage SSL Certificates'),
+##                UI.PixmapCache.getIcon("certificates.png"),
+##                self.tr('Manage SSL Certificates...'),
+##                0, 0,
+##                self, 'webbrowser_manage_certificates')
+##            self.certificatesAct.setStatusTip(self.tr(
+##                'Manage the saved SSL certificates'))
+##            self.certificatesAct.setWhatsThis(self.tr(
+##                """<b>Manage SSL Certificates...</b>"""
+##                """<p>Opens a dialog to manage the saved SSL"""
+##                """ certificates.</p>"""
+##            ))
+##            if not self.__initShortcutsOnly:
+##                self.certificatesAct.triggered.connect(
+##                    self.__showCertificatesDialog)
+##            self.__actions.append(self.certificatesAct)
+        
+        self.certificateErrorsAct = E5Action(
+            self.tr('Manage SSL Certificate Errors'),
+            UI.PixmapCache.getIcon("certificates.png"),
+            self.tr('Manage SSL Certificate Errors...'),
+            0, 0,
+            self, 'webbrowser_manage_certificate_errors')
+        self.certificateErrorsAct.setStatusTip(self.tr(
+            'Manage the accepted SSL certificate Errors'))
+        self.certificateErrorsAct.setWhatsThis(self.tr(
+            """<b>Manage SSL Certificate Errors...</b>"""
+            """<p>Opens a dialog to manage the accepted SSL"""
+            """ certificate errors.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.certificateErrorsAct.triggered.connect(
+                self.__showCertificateErrorsDialog)
+        self.__actions.append(self.certificateErrorsAct)
+        
+        # TODO: Network Monitor (?)
+##        self.toolsMonitorAct = E5Action(
+##            self.tr('Network Monitor'),
+##            self.tr('&Network Monitor...'),
+##            0, 0,
+##            self, 'webbrowser_tools_network_monitor')
+##        self.toolsMonitorAct.setStatusTip(self.tr(
+##            'Show the network monitor dialog'))
+##        self.toolsMonitorAct.setWhatsThis(self.tr(
+##            """<b>Network Monitor...</b>"""
+##            """<p>Shows the network monitor dialog.</p>"""
+##        ))
+##        if not self.__initShortcutsOnly:
+##            self.toolsMonitorAct.triggered.connect(
+##                self.__showNetworkMonitor)
+##        self.__actions.append(self.toolsMonitorAct)
+        
+        self.showDownloadManagerAct = E5Action(
+            self.tr('Downloads'),
+            self.tr('Downloads'),
+            0, 0, self, 'webbrowser_show_downloads')
+        self.showDownloadManagerAct.setStatusTip(self.tr(
+            'Shows the downloads window'))
+        self.showDownloadManagerAct.setWhatsThis(self.tr(
+            """<b>Downloads</b>"""
+            """<p>Shows the downloads window.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.showDownloadManagerAct.triggered.connect(
+                self.__showDownloadsWindow)
+        self.__actions.append(self.showDownloadManagerAct)
+        
+        self.feedsManagerAct = E5Action(
+            self.tr('RSS Feeds Dialog'),
+            UI.PixmapCache.getIcon("rss22.png"),
+            self.tr('&RSS Feeds Dialog...'),
+            QKeySequence(self.tr("Ctrl+Shift+F", "Help|RSS Feeds Dialog")),
+            0, self, 'webbrowser_rss_feeds')
+        self.feedsManagerAct.setStatusTip(self.tr(
+            'Open a dialog showing the configured RSS feeds.'))
+        self.feedsManagerAct.setWhatsThis(self.tr(
+            """<b>RSS Feeds Dialog...</b>"""
+            """<p>Open a dialog to show the configured RSS feeds."""
+            """ It can be used to mange the feeds and to show their"""
+            """ contents.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.feedsManagerAct.triggered.connect(self.__showFeedsManager)
+        self.__actions.append(self.feedsManagerAct)
+        
+        self.siteInfoAct = E5Action(
+            self.tr('Siteinfo Dialog'),
+            UI.PixmapCache.getIcon("helpAbout.png"),
+            self.tr('&Siteinfo Dialog...'),
+            QKeySequence(self.tr("Ctrl+Shift+I", "Help|Siteinfo Dialog")),
+            0, self, 'webbrowser_siteinfo')
+        self.siteInfoAct.setStatusTip(self.tr(
+            'Open a dialog showing some information about the current site.'))
+        self.siteInfoAct.setWhatsThis(self.tr(
+            """<b>Siteinfo Dialog...</b>"""
+            """<p>Opens a dialog showing some information about the current"""
+            """ site.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.siteInfoAct.triggered.connect(self.__showSiteinfoDialog)
+        self.__actions.append(self.siteInfoAct)
+        
+        # TODO: User Agents
+##        self.userAgentManagerAct = E5Action(
+##            self.tr('Manage User Agent Settings'),
+##            self.tr('Manage &User Agent Settings'),
+##            0, 0, self, 'webbrowser_user_agent_settings')
+##        self.userAgentManagerAct.setStatusTip(self.tr(
+##            'Shows a dialog to manage the User Agent settings'))
+##        self.userAgentManagerAct.setWhatsThis(self.tr(
+##            """<b>Manage User Agent Settings</b>"""
+##            """<p>Shows a dialog to manage the User Agent settings.</p>"""
+##        ))
+##        if not self.__initShortcutsOnly:
+##            self.userAgentManagerAct.triggered.connect(
+##                self.__showUserAgentsDialog)
+##        self.__actions.append(self.userAgentManagerAct)
+        
+        self.synchronizationAct = E5Action(
+            self.tr('Synchronize data'),
+            UI.PixmapCache.getIcon("sync.png"),
+            self.tr('&Synchronize Data...'),
+            0, 0, self, 'webbrowser_synchronize_data')
+        self.synchronizationAct.setStatusTip(self.tr(
+            'Shows a dialog to synchronize data via the network'))
+        self.synchronizationAct.setWhatsThis(self.tr(
+            """<b>Synchronize Data...</b>"""
+            """<p>This shows a dialog to synchronize data via the"""
+            """ network.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.synchronizationAct.triggered.connect(
+                self.__showSyncDialog)
+        self.__actions.append(self.synchronizationAct)
+        
+        self.zoomValuesAct = E5Action(
+            self.tr('Manage Saved Zoom Values'),
+            UI.PixmapCache.getIcon("zoomReset.png"),
+            self.tr('Manage Saved Zoom Values...'),
+            0, 0,
+            self, 'webbrowser_manage_zoom_values')
+        self.zoomValuesAct.setStatusTip(self.tr(
+            'Manage the saved zoom values'))
+        self.zoomValuesAct.setWhatsThis(self.tr(
+            """<b>Manage Saved Zoom Values...</b>"""
+            """<p>Opens a dialog to manage the saved zoom values.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.zoomValuesAct.triggered.connect(self.__showZoomValuesDialog)
+        self.__actions.append(self.zoomValuesAct)
+        
+        self.showJavaScriptConsoleAct = E5Action(
+            self.tr('JavaScript Console'),
+            self.tr('JavaScript Console'),
+            0, 0, self, 'webbrowser_show_javascript_console')
+        self.showJavaScriptConsoleAct.setStatusTip(self.tr(
+            'Toggle the JavaScript console window'))
+        self.showJavaScriptConsoleAct.setWhatsThis(self.tr(
+            """<b>JavaScript Console</b>"""
+            """<p>This toggles the JavaScript console window.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.showJavaScriptConsoleAct.triggered.connect(
+                self.__toggleJavaScriptConsole)
+        self.__actions.append(self.showJavaScriptConsoleAct)
+        
+        self.backAct.setEnabled(False)
+        self.forwardAct.setEnabled(False)
+        
+        # now read the keyboard shortcuts for the actions
+        Shortcuts.readShortcuts(
+            helpViewer=self, helpViewerCategory="webBrowser")
+    
+    def getActions(self):
+        """
+        Public method to get a list of all actions.
+        
+        @return list of all actions (list of E5Action)
+        """
+        return self.__actions[:]
+        
+    def __initMenus(self):
+        """
+        Private method to create the menus.
+        """
+        mb = self.menuBar()
+        
+        menu = mb.addMenu(self.tr('&File'))
+        menu.setTearOffEnabled(True)
+        menu.addAction(self.newTabAct)
+        menu.addAction(self.newAct)
+        menu.addAction(self.newPrivateAct)
+        menu.addAction(self.openAct)
+        menu.addAction(self.openTabAct)
+        menu.addSeparator()
+##        menu.addAction(self.saveAsAct)
+        menu.addAction(self.savePageScreenAct)
+        menu.addAction(self.saveVisiblePageScreenAct)
+        menu.addSeparator()
+        menu.addAction(self.printPreviewAct)
+        menu.addAction(self.printAct)
+        if self.printPdfAct:
+            menu.addAction(self.printPdfAct)
+        menu.addSeparator()
+        menu.addAction(self.closeAct)
+        menu.addAction(self.closeAllAct)
+        menu.addSeparator()
+        menu.addAction(self.exitAct)
+        
+        menu = mb.addMenu(self.tr('&Edit'))
+        menu.setTearOffEnabled(True)
+        menu.addAction(self.undoAct)
+        menu.addAction(self.redoAct)
+        menu.addSeparator()
+        menu.addAction(self.copyAct)
+        menu.addAction(self.cutAct)
+        menu.addAction(self.pasteAct)
+        menu.addSeparator()
+        menu.addAction(self.selectAllAct)
+        menu.addSeparator()
+        menu.addAction(self.findAct)
+        menu.addAction(self.findNextAct)
+        menu.addAction(self.findPrevAct)
+        
+        menu = mb.addMenu(self.tr('&View'))
+        menu.setTearOffEnabled(True)
+        menu.addAction(self.zoomInAct)
+        menu.addAction(self.zoomResetAct)
+        menu.addAction(self.zoomOutAct)
+        menu.addSeparator()
+        menu.addAction(self.pageSourceAct)
+        menu.addAction(self.fullScreenAct)
+        self.__textEncodingMenu = menu.addMenu(
+            self.tr("Text Encoding"))
+        self.__textEncodingMenu.aboutToShow.connect(
+            self.__aboutToShowTextEncodingMenu)
+        self.__textEncodingMenu.triggered.connect(self.__setTextEncoding)
+        
+        menu = mb.addMenu(self.tr('&Go'))
+        menu.setTearOffEnabled(True)
+        menu.addAction(self.backAct)
+        menu.addAction(self.forwardAct)
+        menu.addAction(self.homeAct)
+        menu.addSeparator()
+        menu.addAction(self.stopAct)
+        menu.addAction(self.reloadAct)
+        if WebBrowserWindow.useQtHelp:
+            menu.addSeparator()
+            menu.addAction(self.syncTocAct)
+        
+        from .History.HistoryMenu import HistoryMenu
+        self.historyMenu = HistoryMenu(self, self.__tabWidget)
+        self.historyMenu.setTearOffEnabled(True)
+        self.historyMenu.setTitle(self.tr('H&istory'))
+        self.historyMenu.openUrl.connect(self.openUrl)
+        self.historyMenu.newUrl.connect(self.openUrlNewTab)
+        mb.addMenu(self.historyMenu)
+        
+        from .Bookmarks.BookmarksMenu import BookmarksMenuBarMenu
+        self.bookmarksMenu = BookmarksMenuBarMenu(self)
+        self.bookmarksMenu.setTearOffEnabled(True)
+        self.bookmarksMenu.setTitle(self.tr('&Bookmarks'))
+        self.bookmarksMenu.openUrl.connect(self.openUrl)
+        self.bookmarksMenu.newUrl.connect(self.openUrlNewTab)
+        mb.addMenu(self.bookmarksMenu)
+        
+        bookmarksActions = []
+        bookmarksActions.append(self.bookmarksManageAct)
+        bookmarksActions.append(self.bookmarksAddAct)
+        bookmarksActions.append(self.bookmarksAllTabsAct)
+        bookmarksActions.append(self.bookmarksAddFolderAct)
+        bookmarksActions.append("--SEPARATOR--")
+        bookmarksActions.append(self.importBookmarksAct)
+        bookmarksActions.append(self.exportBookmarksAct)
+        self.bookmarksMenu.setInitialActions(bookmarksActions)
+        
+        menu = mb.addMenu(self.tr('&Settings'))
+        menu.setTearOffEnabled(True)
+        menu.addAction(self.prefAct)
+        menu.addAction(self.acceptedLanguagesAct)
+        menu.addAction(self.cookiesAct)
+        menu.addAction(self.flashCookiesAct)
+##        menu.addAction(self.offlineStorageAct)
+        menu.addAction(self.personalDataAct)
+        menu.addAction(self.greaseMonkeyAct)
+        menu.addAction(self.featurePermissionAct)
+        menu.addSeparator()
+        menu.addAction(self.editMessageFilterAct)
+        menu.addSeparator()
+        menu.addAction(self.searchEnginesAct)
+        menu.addSeparator()
+        menu.addAction(self.passwordsAct)
+        # TODO: Certificates
+##        from .Network.NetworkManager import SSL_AVAILABLE
+##        if SSL_AVAILABLE:
+##            menu.addAction(self.certificatesAct)
+        menu.addAction(self.certificateErrorsAct)
+        menu.addSeparator()
+        menu.addAction(self.zoomValuesAct)
+        menu.addAction(self.manageIconsAct)
+        menu.addSeparator()
+        menu.addAction(self.adblockAct)
+##        menu.addAction(self.flashblockAct)
+##        menu.addSeparator()
+        self.__settingsMenu = menu
+        self.__settingsMenu.aboutToShow.connect(
+            self.__aboutToShowSettingsMenu)
+        
+##        from .UserAgent.UserAgentMenu import UserAgentMenu
+##        self.__userAgentMenu = UserAgentMenu(self.tr("Global User Agent"))
+##        menu.addMenu(self.__userAgentMenu)
+##        menu.addAction(self.userAgentManagerAct)
+##        menu.addSeparator()
+        
+        if WebBrowserWindow.useQtHelp:
+            menu.addAction(self.manageQtHelpDocsAct)
+            menu.addAction(self.manageQtHelpFiltersAct)
+            menu.addAction(self.reindexDocumentationAct)
+            menu.addSeparator()
+        menu.addAction(self.clearPrivateDataAct)
+        menu.addAction(self.clearIconsAct)
+        
+        menu = mb.addMenu(self.tr("&Tools"))
+        menu.setTearOffEnabled(True)
+        menu.addAction(self.feedsManagerAct)
+        menu.addAction(self.siteInfoAct)
+        menu.addSeparator()
+        menu.addAction(self.synchronizationAct)
+        # TODO: Network Monitor
+##        menu.addSeparator()
+##        menu.addAction(self.toolsMonitorAct)
+        
+        menu = mb.addMenu(self.tr("&Window"))
+        menu.setTearOffEnabled(True)
+        menu.addAction(self.showDownloadManagerAct)
+        menu.addAction(self.showJavaScriptConsoleAct)
+        if WebBrowserWindow.useQtHelp:
+            menu.addSeparator()
+            menu.addAction(self.showTocAct)
+            menu.addAction(self.showIndexAct)
+            menu.addAction(self.showSearchAct)
+        
+        mb.addSeparator()
+        
+        menu = mb.addMenu(self.tr('&Help'))
+        menu.setTearOffEnabled(True)
+        menu.addAction(self.aboutAct)
+        menu.addAction(self.aboutQtAct)
+        menu.addSeparator()
+        menu.addAction(self.whatsThisAct)
+    
+    def __initToolbars(self):
+        """
+        Private method to create the toolbars.
+        """
+        filetb = self.addToolBar(self.tr("File"))
+        filetb.setObjectName("FileToolBar")
+        filetb.setIconSize(UI.Config.ToolBarIconSize)
+        filetb.addAction(self.newTabAct)
+        filetb.addAction(self.newAct)
+        filetb.addAction(self.newPrivateAct)
+        filetb.addAction(self.openAct)
+        filetb.addAction(self.openTabAct)
+        filetb.addSeparator()
+##        filetb.addAction(self.saveAsAct)
+        filetb.addAction(self.savePageScreenAct)
+        filetb.addSeparator()
+        filetb.addAction(self.printPreviewAct)
+        filetb.addAction(self.printAct)
+        if self.printPdfAct:
+            filetb.addAction(self.printPdfAct)
+        filetb.addSeparator()
+        filetb.addAction(self.closeAct)
+        filetb.addAction(self.exitAct)
+        
+        self.savePageScreenMenu = QMenu(self)
+        self.savePageScreenMenu.addAction(self.savePageScreenAct)
+        self.savePageScreenMenu.addAction(self.saveVisiblePageScreenAct)
+        savePageScreenButton = filetb.widgetForAction(self.savePageScreenAct)
+        savePageScreenButton.setMenu(self.savePageScreenMenu)
+        savePageScreenButton.setPopupMode(QToolButton.MenuButtonPopup)
+        
+        edittb = self.addToolBar(self.tr("Edit"))
+        edittb.setObjectName("EditToolBar")
+        edittb.setIconSize(UI.Config.ToolBarIconSize)
+        edittb.addAction(self.undoAct)
+        edittb.addAction(self.redoAct)
+        edittb.addSeparator()
+        edittb.addAction(self.copyAct)
+        edittb.addAction(self.cutAct)
+        edittb.addAction(self.pasteAct)
+        edittb.addSeparator()
+        edittb.addAction(self.selectAllAct)
+        
+        viewtb = self.addToolBar(self.tr("View"))
+        viewtb.setObjectName("ViewToolBar")
+        viewtb.setIconSize(UI.Config.ToolBarIconSize)
+        viewtb.addAction(self.zoomInAct)
+        viewtb.addAction(self.zoomResetAct)
+        viewtb.addAction(self.zoomOutAct)
+        viewtb.addSeparator()
+        viewtb.addAction(self.fullScreenAct)
+        
+        findtb = self.addToolBar(self.tr("Find"))
+        findtb.setObjectName("FindToolBar")
+        findtb.setIconSize(UI.Config.ToolBarIconSize)
+        findtb.addAction(self.findAct)
+        findtb.addAction(self.findNextAct)
+        findtb.addAction(self.findPrevAct)
+        
+        if WebBrowserWindow.useQtHelp:
+            filtertb = self.addToolBar(self.tr("Filter"))
+            filtertb.setObjectName("FilterToolBar")
+            self.filterCombo = QComboBox()
+            self.filterCombo.setMinimumWidth(
+                QFontMetrics(QFont()).width("ComboBoxWithEnoughWidth"))
+            filtertb.addWidget(QLabel(self.tr("Filtered by: ")))
+            filtertb.addWidget(self.filterCombo)
+            self.__helpEngine.setupFinished.connect(self.__setupFilterCombo)
+            self.filterCombo.activated[str].connect(
+                self.__filterQtHelpDocumentation)
+            self.__setupFilterCombo()
+        
+        settingstb = self.addToolBar(self.tr("Settings"))
+        settingstb.setObjectName("SettingsToolBar")
+        settingstb.setIconSize(UI.Config.ToolBarIconSize)
+        settingstb.addAction(self.prefAct)
+        settingstb.addAction(self.acceptedLanguagesAct)
+        settingstb.addAction(self.cookiesAct)
+        settingstb.addAction(self.flashCookiesAct)
+##        settingstb.addAction(self.offlineStorageAct)
+        settingstb.addAction(self.personalDataAct)
+        settingstb.addAction(self.greaseMonkeyAct)
+        settingstb.addAction(self.featurePermissionAct)
+        
+        toolstb = self.addToolBar(self.tr("Tools"))
+        toolstb.setObjectName("ToolsToolBar")
+        toolstb.setIconSize(UI.Config.ToolBarIconSize)
+        toolstb.addAction(self.feedsManagerAct)
+        toolstb.addAction(self.siteInfoAct)
+        toolstb.addSeparator()
+        toolstb.addAction(self.synchronizationAct)
+        
+        helptb = self.addToolBar(self.tr("Help"))
+        helptb.setObjectName("HelpToolBar")
+        helptb.setIconSize(UI.Config.ToolBarIconSize)
+        helptb.addAction(self.whatsThisAct)
+        
+        self.addToolBarBreak()
+        
+        gotb = self.addToolBar(self.tr("Go"))
+        gotb.setObjectName("GoToolBar")
+        gotb.setIconSize(UI.Config.ToolBarIconSize)
+        gotb.addAction(self.backAct)
+        gotb.addAction(self.forwardAct)
+        gotb.addAction(self.reloadAct)
+        gotb.addAction(self.stopAct)
+        gotb.addAction(self.homeAct)
+        gotb.addSeparator()
+        
+        self.__navigationSplitter = QSplitter(gotb)
+        self.__navigationSplitter.addWidget(self.__tabWidget.stackedUrlBar())
+        
+        from .WebBrowserWebSearchWidget import WebBrowserWebSearchWidget
+        self.searchEdit = WebBrowserWebSearchWidget(self)
+        sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
+        sizePolicy.setHorizontalStretch(2)
+        sizePolicy.setVerticalStretch(0)
+        self.searchEdit.setSizePolicy(sizePolicy)
+        self.searchEdit.search.connect(self.__linkActivated)
+        self.__navigationSplitter.addWidget(self.searchEdit)
+        gotb.addWidget(self.__navigationSplitter)
+        
+        self.__navigationSplitter.setSizePolicy(
+            QSizePolicy.Expanding, QSizePolicy.Maximum)
+        self.__navigationSplitter.setCollapsible(0, False)
+        
+        self.backMenu = QMenu(self)
+        self.backMenu.aboutToShow.connect(self.__showBackMenu)
+        self.backMenu.triggered.connect(self.__navigationMenuActionTriggered)
+        backButton = gotb.widgetForAction(self.backAct)
+        backButton.setMenu(self.backMenu)
+        backButton.setPopupMode(QToolButton.MenuButtonPopup)
+        
+        self.forwardMenu = QMenu(self)
+        self.forwardMenu.aboutToShow.connect(self.__showForwardMenu)
+        self.forwardMenu.triggered.connect(
+            self.__navigationMenuActionTriggered)
+        forwardButton = gotb.widgetForAction(self.forwardAct)
+        forwardButton.setMenu(self.forwardMenu)
+        forwardButton.setPopupMode(QToolButton.MenuButtonPopup)
+        
+        from .Bookmarks.BookmarksToolBar import BookmarksToolBar
+        bookmarksModel = self.bookmarksManager().bookmarksModel()
+        self.bookmarksToolBar = BookmarksToolBar(self, bookmarksModel, self)
+        self.bookmarksToolBar.setObjectName("BookmarksToolBar")
+        self.bookmarksToolBar.setIconSize(UI.Config.ToolBarIconSize)
+        self.bookmarksToolBar.openUrl.connect(self.openUrl)
+        self.bookmarksToolBar.newUrl.connect(self.openUrlNewTab)
+        self.addToolBarBreak()
+        self.addToolBar(self.bookmarksToolBar)
+        
+        self.addToolBarBreak()
+        vttb = self.addToolBar(self.tr("VirusTotal"))
+        vttb.setObjectName("VirusTotalToolBar")
+        vttb.setIconSize(UI.Config.ToolBarIconSize)
+        vttb.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
+        self.virustotalScanCurrentAct = vttb.addAction(
+            UI.PixmapCache.getIcon("virustotal.png"),
+            self.tr("Scan current site"),
+            self.__virusTotalScanCurrentSite)
+        self.virustotalIpReportAct = vttb.addAction(
+            UI.PixmapCache.getIcon("virustotal.png"),
+            self.tr("IP Address Report"),
+            self.__virusTotalIpAddressReport)
+        self.virustotalDomainReportAct = vttb.addAction(
+            UI.PixmapCache.getIcon("virustotal.png"),
+            self.tr("Domain Report"),
+            self.__virusTotalDomainReport)
+        if not Preferences.getWebBrowser("VirusTotalEnabled") or \
+           Preferences.getWebBrowser("VirusTotalServiceKey") == "":
+            self.virustotalScanCurrentAct.setEnabled(False)
+            self.virustotalIpReportAct.setEnabled(False)
+            self.virustotalDomainReportAct.setEnabled(False)
+        
+    def __nextTab(self):
+        """
+        Private slot used to show the next tab.
+        """
+        fwidget = QApplication.focusWidget()
+        while fwidget and not hasattr(fwidget, 'nextTab'):
+            fwidget = fwidget.parent()
+        if fwidget:
+            fwidget.nextTab()
+        
+    def __prevTab(self):
+        """
+        Private slot used to show the previous tab.
+        """
+        fwidget = QApplication.focusWidget()
+        while fwidget and not hasattr(fwidget, 'prevTab'):
+            fwidget = fwidget.parent()
+        if fwidget:
+            fwidget.prevTab()
+        
+    def __switchTab(self):
+        """
+        Private slot used to switch between the current and the previous
+        current tab.
+        """
+        fwidget = QApplication.focusWidget()
+        while fwidget and not hasattr(fwidget, 'switchTab'):
+            fwidget = fwidget.parent()
+        if fwidget:
+            fwidget.switchTab()
+        
+    def __whatsThis(self):
+        """
+        Private slot called in to enter Whats This mode.
+        """
+        QWhatsThis.enterWhatsThisMode()
+        
+    def __titleChanged(self, browser, title):
+        """
+        Private slot called to handle a change of a browser's title.
+        
+        @param browser reference to the browser (WebBrowserView)
+        @param title new title (string)
+        """
+        self.historyManager().updateHistoryEntry(
+            browser.url().toString(), title)
+    
+    @pyqtSlot()
+    def newTab(self, link=None, requestData=None, addNextTo=None):
+        """
+        Public slot called to open a new web browser tab.
+        
+        @param link file to be displayed in the new window (string or QUrl)
+        @param requestData page load request data (LoadRequest)
+        @param addNextTo reference to the browser to open the tab after
+            (HelpBrowser)
+        """
+        if addNextTo:
+            self.__tabWidget.newBrowserAfter(addNextTo, link, requestData)
+        else:
+            self.__tabWidget.newBrowser(link, requestData)
+    
+    @pyqtSlot()
+    def newWindow(self, link=None):
+        """
+        Public slot called to open a new web browser window.
+        
+        @param link URL to be displayed in the new window
+        @type str or QUrl
+        """
+        if link is None:
+            linkName = ""
+        elif isinstance(link, QUrl):
+            linkName = link.toString()
+        else:
+            linkName = link
+        h = WebBrowserWindow(linkName, ".", self.parent(), "webbrowser",
+                             self.__fromEric, private=self.isPrivate())
+        h.show()
+    
+    @pyqtSlot()
+    def newPrivateWindow(self, link=None):
+        """
+        Public slot called to open a new private web browser window.
+        
+        @param link URL to be displayed in the new window
+        @type str or QUrl
+        """
+        if link is None:
+            linkName = ""
+        elif isinstance(link, QUrl):
+            linkName = link.toString()
+        else:
+            linkName = link
+        
+        applPath = os.path.join(getConfig("ericDir"), "eric6_browser.py")
+        args = []
+        args.append(applPath)
+        args.append("--config={0}".format(Utilities.getConfigDir()))
+        if self.__settingsDir:
+            args.append("--settings={0}".format(self.__settingsDir))
+        args.append("--private")
+        if linkName:
+            args.append(linkName)
+        
+        if not os.path.isfile(applPath) or \
+                not QProcess.startDetached(sys.executable, args):
+            E5MessageBox.critical(
+                self,
+                self.tr('New Private Window'),
+                self.tr(
+                    '<p>Could not start the process.<br>'
+                    'Ensure that it is available as <b>{0}</b>.</p>'
+                ).format(applPath),
+                self.tr('OK'))
+    
+    def __openFile(self):
+        """
+        Private slot called to open a file.
+        """
+        fn = E5FileDialog.getOpenFileName(
+            self,
+            self.tr("Open File"),
+            "",
+            self.tr("Help Files (*.html *.htm);;"
+                    "PDF Files (*.pdf);;"
+                    "CHM Files (*.chm);;"
+                    "All Files (*)"
+                    ))
+        if fn:
+            if Utilities.isWindowsPlatform():
+                url = "file:///" + Utilities.fromNativeSeparators(fn)
+            else:
+                url = "file://" + fn
+            self.currentBrowser().setSource(QUrl(url))
+        
+    def __openFileNewTab(self):
+        """
+        Private slot called to open a file in a new tab.
+        """
+        fn = E5FileDialog.getOpenFileName(
+            self,
+            self.tr("Open File"),
+            "",
+            self.tr("Help Files (*.html *.htm);;"
+                    "PDF Files (*.pdf);;"
+                    "CHM Files (*.chm);;"
+                    "All Files (*)"
+                    ))
+        if fn:
+            if Utilities.isWindowsPlatform():
+                url = "file:///" + Utilities.fromNativeSeparators(fn)
+            else:
+                url = "file://" + fn
+            self.newTab(url)
+        
+##    def __savePageAs(self):
+##        """
+##        Private slot to save the current page.
+##        """
+##        browser = self.currentBrowser()
+##        if browser is not None:
+##            browser.saveAs()
+##    
+    @pyqtSlot()
+    def __savePageScreen(self, visibleOnly=False):
+        """
+        Private slot to save the current page as a screen shot.
+        
+        @param visibleOnly flag indicating to just save the visible part
+            of the page (boolean)
+        """
+        from .PageScreenDialog import PageScreenDialog
+        self.__pageScreen = PageScreenDialog(
+            self.currentBrowser(), visibleOnly=visibleOnly)
+        self.__pageScreen.show()
+        
+    @pyqtSlot()
+    def __saveVisiblePageScreen(self):
+        """
+        Private slot to save the visible part of the current page as a screen
+        shot.
+        """
+        self.__savePageScreen(visibleOnly=True)
+        
+    def __about(self):
+        """
+        Private slot to show the about information.
+        """
+        chromeVersion, webengineVersion = \
+            WebBrowserTools.getWebEngineVersions()
+        E5MessageBox.about(
+            self,
+            self.tr("eric6 Web Browser"),
+            self.tr(
+                """<b>eric6 Web Browser - {0}</b>"""
+                """<p>The eric6 Web Browser is a combined help file and HTML"""
+                """ browser. It is part of the eric6 development"""
+                """ toolset.</p>"""
+                """<p>It is based on QtWebEngine {1} and Chrome {2}.</p>"""
+            ).format(Version, webengineVersion, chromeVersion))
+        
+    def __aboutQt(self):
+        """
+        Private slot to show info about Qt.
+        """
+        E5MessageBox.aboutQt(self, self.tr("eric6 Web Browser"))
+
+    def setBackwardAvailable(self, b):
+        """
+        Public slot called when backward references are available.
+        
+        @param b flag indicating availability of the backwards action (boolean)
+        """
+        self.backAct.setEnabled(b)
+        
+    def setForwardAvailable(self, b):
+        """
+        Public slot called when forward references are available.
+        
+        @param b flag indicating the availability of the forwards action
+            (boolean)
+        """
+        self.forwardAct.setEnabled(b)
+        
+    def setLoadingActions(self, b):
+        """
+        Public slot to set the loading dependent actions.
+        
+        @param b flag indicating the loading state to consider (boolean)
+        """
+        self.reloadAct.setEnabled(not b)
+        self.stopAct.setEnabled(b)
+        
+    def __addBookmark(self):
+        """
+        Private slot called to add the displayed file to the bookmarks.
+        """
+        view = self.currentBrowser()
+        view.addBookmark()
+        urlStr = bytes(view.url().toEncoded()).decode()
+        title = view.title()
+        
+        script = Scripts.getAllMetaAttributes()
+        view.page().runJavaScript(
+            script,
+            lambda res: self.__addBookmarkCallback(urlStr, title, res))
+    
+    def __addBookmarkCallback(self, url, title, res):
+        """
+        Private callback method of __addBookmark().
+        
+        @param url URL for the bookmark
+        @type str
+        @param title title for the bookmark
+        @type str
+        @param res result of the JavaScript
+        @type list
+        """
+        description = ""
+        for meta in res:
+            if meta["name"] == "description":
+                description = meta["content"]
+        
+        from .Bookmarks.AddBookmarkDialog import AddBookmarkDialog
+        dlg = AddBookmarkDialog()
+        dlg.setUrl(url)
+        dlg.setTitle(title)
+        dlg.setDescription(description)
+        menu = self.bookmarksManager().menu()
+        idx = self.bookmarksManager().bookmarksModel().nodeIndex(menu)
+        dlg.setCurrentIndex(idx)
+        dlg.exec_()
+        
+    def __addBookmarkFolder(self):
+        """
+        Private slot to add a new bookmarks folder.
+        """
+        from .Bookmarks.AddBookmarkDialog import AddBookmarkDialog
+        dlg = AddBookmarkDialog()
+        menu = self.bookmarksManager().menu()
+        idx = self.bookmarksManager().bookmarksModel().nodeIndex(menu)
+        dlg.setCurrentIndex(idx)
+        dlg.setFolder(True)
+        dlg.exec_()
+        
+    def __showBookmarksDialog(self):
+        """
+        Private slot to show the bookmarks dialog.
+        """
+        from .Bookmarks.BookmarksDialog import BookmarksDialog
+        self.__bookmarksDialog = BookmarksDialog(self)
+        self.__bookmarksDialog.openUrl.connect(self.openUrl)
+        self.__bookmarksDialog.newUrl.connect(self.openUrlNewTab)
+        self.__bookmarksDialog.show()
+        
+    def bookmarkAll(self):
+        """
+        Public slot to bookmark all open tabs.
+        """
+        from .Bookmarks.AddBookmarkDialog import AddBookmarkDialog
+        dlg = AddBookmarkDialog()
+        dlg.setFolder(True)
+        dlg.setTitle(self.tr("Saved Tabs"))
+        dlg.exec_()
+        
+        folder = dlg.addedNode()
+        if folder is None:
+            return
+        
+        for view in self.__tabWidget.browsers():
+            urlStr = bytes(view.url().toEncoded()).decode()
+            title = view.title()
+            
+            script = Scripts.getAllMetaAttributes()
+            view.page().runJavaScript(
+                script,
+                lambda res: self.__bookmarkAllCallback(folder, urlStr,
+                                                       title, res))
+    
+    def __bookmarkAllCallback(self, folder, url, title, res):
+        """
+        Private callback method of __addBookmark().
+        
+        @param folder reference to the bookmarks folder
+        @type BookmarkNode
+        @param url URL for the bookmark
+        @type str
+        @param title title for the bookmark
+        @type str
+        @param res result of the JavaScript
+        @type list
+        """
+        description = ""
+        for meta in res:
+            if meta["name"] == "description":
+                description = meta["content"]
+        
+        from .Bookmarks.BookmarkNode import BookmarkNode
+        bookmark = BookmarkNode(BookmarkNode.Bookmark)
+        bookmark.url = url
+        bookmark.title = title
+        bookmark.desc = description
+        
+        self.bookmarksManager().addBookmark(folder, bookmark)
+        
+    def __find(self):
+        """
+        Private slot to handle the find action.
+        
+        It opens the search dialog in order to perform the various
+        search actions and to collect the various search info.
+        """
+        self.__searchWidget.showFind()
+        
+    def __closeAllWindows(self):
+        """
+        Private slot to close all windows.
+        """
+        for browser in WebBrowserWindow.BrowserWindows:
+            if browser != self:
+                browser.close()
+        self.close()
+        
+    def closeEvent(self, e):
+        """
+        Protected event handler for the close event.
+        
+        @param e the close event (QCloseEvent)
+            <br />This event is simply accepted after the history has been
+            saved and all window references have been deleted.
+        """
+        if not self.__shutdownCalled:
+            res = self.shutdown()
+            
+            if res:
+                e.accept()
+                self.webBrowserClosed.emit()
+            else:
+                e.ignore()
+        else:
+            e.accept()
+    
+    def shutdown(self):
+        """
+        Public method to shut down the web browser.
+        
+        @return flag indicating successful shutdown (boolean)
+        """
+        if not self.__tabWidget.shallShutDown():
+            return False
+        
+        if not self.downloadManager().allowQuit():
+            return False
+        
+        self.downloadManager().shutdown()
+        
+        # TODO: Network Monitor
+##        self.__closeNetworkMonitor()
+##        
+        self.cookieJar().close()
+        
+        self.bookmarksToolBar.setModel(None)
+        self.bookmarksManager().close()
+        
+        self.historyManager().close()
+        
+        self.passwordManager().close()
+        
+        self.adBlockManager().close()
+        
+        # TODO: UserAgents
+##        self.userAgentsManager().close()
+##        
+        self.speedDial().close()
+        
+        self.syncManager().close()
+        
+        ZoomManager.instance().close()
+        
+        WebIconProvider.instance().close()
+        
+        self.__virusTotal.close()
+        
+        self.flashCookieManager().shutdown()
+        
+        self.searchEdit.openSearchManager().close()
+        
+        if WebBrowserWindow.useQtHelp:
+            self.__searchEngine.cancelIndexing()
+            self.__searchEngine.cancelSearching()
+            
+            if self.__helpInstaller:
+                self.__helpInstaller.stop()
+        
+        self.searchEdit.saveSearches()
+        
+        self.__tabWidget.closeAllBrowsers(shutdown=True)
+        
+        state = self.saveState()
+        Preferences.setWebBrowser("WebBrowserState", state)
+
+        if Preferences.getWebBrowser("SaveGeometry"):
+            if not self.isFullScreen():
+                Preferences.setGeometry("WebBrowserGeometry",
+                                        self.saveGeometry())
+        else:
+            Preferences.setGeometry("WebBrowserGeometry", QByteArray())
+        
+        try:
+            if self.__fromEric or len(WebBrowserWindow.BrowserWindows) > 1:
+                del WebBrowserWindow.BrowserWindows[
+                    WebBrowserWindow.BrowserWindows.index(self)]
+        except ValueError:
+            pass
+        
+        self.networkManager().shutdown()
+        
+        if not self.__fromEric:
+            Preferences.syncPreferences()
+        
+        self.__shutdownCalled = True
+        return True
+
+    def __backward(self):
+        """
+        Private slot called to handle the backward action.
+        """
+        self.currentBrowser().backward()
+    
+    def __forward(self):
+        """
+        Private slot called to handle the forward action.
+        """
+        self.currentBrowser().forward()
+    
+    def __home(self):
+        """
+        Private slot called to handle the home action.
+        """
+        self.currentBrowser().home()
+    
+    def __reload(self):
+        """
+        Private slot called to handle the reload action.
+        """
+##        self.currentBrowser().reloadBypassingCache()
+        self.currentBrowser().reload()
+    
+    def __stopLoading(self):
+        """
+        Private slot called to handle loading of the current page.
+        """
+        self.currentBrowser().stop()
+    
+    def __zoomValueChanged(self, value):
+        """
+        Private slot to handle value changes of the zoom widget.
+        
+        @param value zoom value (integer)
+        """
+        self.currentBrowser().setZoomValue(value)
+    
+    def __zoomIn(self):
+        """
+        Private slot called to handle the zoom in action.
+        """
+        self.currentBrowser().zoomIn()
+        self.__zoomWidget.setValue(self.currentBrowser().zoomValue())
+    
+    def __zoomOut(self):
+        """
+        Private slot called to handle the zoom out action.
+        """
+        self.currentBrowser().zoomOut()
+        self.__zoomWidget.setValue(self.currentBrowser().zoomValue())
+    
+    def __zoomReset(self):
+        """
+        Private slot called to handle the zoom reset action.
+        """
+        self.currentBrowser().zoomReset()
+        self.__zoomWidget.setValue(self.currentBrowser().zoomValue())
+    
+    def __viewFullScreen(self):
+        """
+        Private slot called to toggle fullscreen mode.
+        """
+        if self.__htmlFullScreen:
+            self.currentBrowser().triggerPageAction(
+                QWebEnginePage.ExitFullScreen)
+            return
+        
+        if self.isFullScreen():
+            # switch back to normal
+            self.showNormal()
+            self.menuBar().show()
+            self.fullScreenAct.setIcon(
+                UI.PixmapCache.getIcon("windowFullscreen.png"))
+            self.fullScreenAct.setIconText(self.tr('Full Screen'))
+        else:
+            # switch to full screen
+            self.showFullScreen()
+            self.menuBar().hide()
+            self.fullScreenAct.setIcon(
+                UI.PixmapCache.getIcon("windowRestore.png"))
+            self.fullScreenAct.setIconText(self.tr('Restore Window'))
+    
+    def enterHtmlFullScreen(self):
+        """
+        Public method to switch to full screen initiated by the
+        HTML page.
+        """
+        self.showFullScreen()
+        self.__htmlFullScreen = True
+    
+    def __copy(self):
+        """
+        Private slot called to handle the copy action.
+        """
+        self.currentBrowser().copy()
+    
+    def __cut(self):
+        """
+        Private slot called to handle the cut action.
+        """
+        self.currentBrowser().cut()
+    
+    def __paste(self):
+        """
+        Private slot called to handle the paste action.
+        """
+        self.currentBrowser().paste()
+    
+    def __undo(self):
+        """
+        Private slot to handle the undo action.
+        """
+        self.currentBrowser().undo()
+    
+    def __redo(self):
+        """
+        Private slot to handle the redo action.
+        """
+        self.currentBrowser().redo()
+    
+    def __selectAll(self):
+        """
+        Private slot to handle the select all action.
+        """
+        self.currentBrowser().selectAll()
+    
+    @classmethod
+    def isPrivate(cls):
+        """
+        Public method to check the private browsing mode.
+        
+        @return flag indicating private browsing mode
+        @rtype bool
+        """
+        return cls._isPrivate
+    
+    def currentBrowser(self):
+        """
+        Public method to get a reference to the current web browser.
+        
+        @return reference to the current help browser (WebBrowserView)
+        """
+        return self.__tabWidget.currentBrowser()
+    
+    def browserAt(self, index):
+        """
+        Public method to get a reference to the web browser with the given
+        index.
+        
+        @param index index of the browser to get (integer)
+        @return reference to the indexed web browser (WebBrowserView)
+        """
+        return self.__tabWidget.browserAt(index)
+    
+    def browsers(self):
+        """
+        Public method to get a list of references to all web browsers.
+        
+        @return list of references to web browsers (list of WebBrowserView)
+        """
+        return self.__tabWidget.browsers()
+    
+    def __currentChanged(self, index):
+        """
+        Private slot to handle the currentChanged signal.
+        
+        @param index index of the current tab (integer)
+        """
+        if index > -1:
+            cb = self.currentBrowser()
+            if cb is not None:
+                self.setForwardAvailable(cb.isForwardAvailable())
+                self.setBackwardAvailable(cb.isBackwardAvailable())
+                self.setLoadingActions(cb.isLoading())
+                
+                # set value of zoom widget
+                self.__zoomWidget.setValue(cb.zoomValue())
+    
+    def __showPreferences(self):
+        """
+        Private slot to set the preferences.
+        """
+        from Preferences.ConfigurationDialog import ConfigurationDialog
+        dlg = ConfigurationDialog(
+            self, 'Configuration', True, fromEric=self.__fromEric,
+            displayMode=ConfigurationDialog.WebBrowserMode)
+        dlg.preferencesChanged.connect(self.preferencesChanged)
+        dlg.masterPasswordChanged.connect(self.masterPasswordChanged)
+        dlg.show()
+        if self.__lastConfigurationPageName:
+            dlg.showConfigurationPageByName(self.__lastConfigurationPageName)
+        else:
+            dlg.showConfigurationPageByName("empty")
+        dlg.exec_()
+        QApplication.processEvents()
+        if dlg.result() == QDialog.Accepted:
+            dlg.setPreferences()
+            Preferences.syncPreferences()
+            self.preferencesChanged()
+        self.__lastConfigurationPageName = dlg.getConfigurationPageName()
+    
+    def preferencesChanged(self):
+        """
+        Public slot to handle a change of preferences.
+        """
+        if not self.__fromEric:
+            self.setStyle(Preferences.getUI("Style"),
+                          Preferences.getUI("StyleSheet"))
+        
+        self.__initWebEngineSettings()
+        
+        self.networkManager().preferencesChanged()
+        
+        self.historyManager().preferencesChanged()
+        
+        self.__tabWidget.preferencesChanged()
+        
+        self.searchEdit.preferencesChanged()
+        
+        if not self.isPrivate():
+            profile = self.webProfile()
+            if Preferences.getWebBrowser("DiskCacheEnabled"):
+                profile.setHttpCacheType(QWebEngineProfile.DiskHttpCache)
+                profile.setHttpCacheMaximumSize(
+                    Preferences.getWebBrowser("DiskCacheSize") * 1024 * 1024)
+            else:
+                profile.setHttpCacheType(QWebEngineProfile.MemoryHttpCache)
+                profile.setHttpCacheMaximumSize(0)
+            
+        self.__virusTotal.preferencesChanged()
+        if not Preferences.getWebBrowser("VirusTotalEnabled") or \
+           Preferences.getWebBrowser("VirusTotalServiceKey") == "":
+            self.virustotalScanCurrentAct.setEnabled(False)
+            self.virustotalIpReportAct.setEnabled(False)
+            self.virustotalDomainReportAct.setEnabled(False)
+        else:
+            self.virustotalScanCurrentAct.setEnabled(True)
+            self.virustotalIpReportAct.setEnabled(True)
+            self.virustotalDomainReportAct.setEnabled(True)
+    
+    def masterPasswordChanged(self, oldPassword, newPassword):
+        """
+        Public slot to handle the change of the master password.
+        
+        @param oldPassword current master password (string)
+        @param newPassword new master password (string)
+        """
+        from Preferences.ConfigurationDialog import ConfigurationDialog
+        self.passwordManager().masterPasswordChanged(oldPassword, newPassword)
+        if self.__fromEric and isinstance(self.sender(), ConfigurationDialog):
+            # we were called from our local configuration dialog
+            Preferences.convertPasswords(oldPassword, newPassword)
+            Utilities.crypto.changeRememberedMaster(newPassword)
+    
+    def __showAcceptedLanguages(self):
+        """
+        Private slot to configure the accepted languages for web pages.
+        """
+        from .WebBrowserLanguagesDialog import WebBrowserLanguagesDialog
+        dlg = WebBrowserLanguagesDialog(self)
+        dlg.exec_()
+        self.networkManager().languagesChanged()
+    
+    def __showCookiesConfiguration(self):
+        """
+        Private slot to configure the cookies handling.
+        """
+        from .CookieJar.CookiesConfigurationDialog import \
+            CookiesConfigurationDialog
+        dlg = CookiesConfigurationDialog(self)
+        dlg.exec_()
+    
+    def __showFlashCookiesManagement(self):
+        """
+        Private slot to show the flash cookies management dialog.
+        """
+        self.flashCookieManager().showFlashCookieManagerDialog()
+    
+##    def __showOfflineStorageConfiguration(self):
+##        """
+##        Private slot to configure the offline storage.
+##        """
+##        from .OfflineStorage.OfflineStorageConfigDialog import \
+##            OfflineStorageConfigDialog
+##        dlg = OfflineStorageConfigDialog(self)
+##        if dlg.exec_() == QDialog.Accepted:
+##            dlg.storeData()
+##            self.__initWebEngineSettings()
+    
+    @classmethod
+    def setUseQtHelp(cls, use):
+        """
+        Class method to set the QtHelp usage.
+        
+        @param use flag indicating usage (boolean)
+        """
+        if use:
+            cls.useQtHelp = use and QTHELP_AVAILABLE
+        else:
+            cls.useQtHelp = False
+    
+    @classmethod
+    def helpEngine(cls):
+        """
+        Class method to get a reference to the help engine.
+        
+        @return reference to the help engine (QHelpEngine)
+        """
+        if cls.useQtHelp:
+            if cls._helpEngine is None:
+                cls._helpEngine = \
+                    QHelpEngine(os.path.join(Utilities.getConfigDir(),
+                                             "web_browser", "eric6help.qhc"))
+            return cls._helpEngine
+        else:
+            return None
+        
+    @classmethod
+    def networkManager(cls):
+        """
+        Class method to get a reference to the network manager object.
+        
+        @return reference to the network access manager (NetworkManager)
+        """
+        if cls._networkManager is None:
+            from .Network.NetworkManager import NetworkManager
+            cls._networkManager = NetworkManager(cls.helpEngine())
+        
+        return cls._networkManager
+    
+    @classmethod
+    def cookieJar(cls):
+        """
+        Class method to get a reference to the cookie jar.
+        
+        @return reference to the cookie jar (CookieJar)
+        """
+        if cls._cookieJar is None:
+            from .CookieJar.CookieJar import CookieJar
+            cls._cookieJar = CookieJar()
+        
+        return cls._cookieJar
+        
+    def __clearIconsDatabase(self):
+        """
+        Private slot to clear the favicons databse.
+        """
+        WebIconProvider.instance().clear()
+    
+    def __showWebIconsDialog(self):
+        """
+        Private slot to show a dialog to manage the favicons database.
+        """
+        WebIconProvider.instance().showWebIconDialog()
+        
+    @pyqtSlot(QUrl)
+    def __linkActivated(self, url):
+        """
+        Private slot to handle the selection of a link.
+        
+        @param url URL to be shown (QUrl)
+        """
+        if not self.__activating:
+            self.__activating = True
+            self.currentBrowser().setUrl(url)
+            self.__activating = False
+        
+    def __linksActivated(self, links, keyword):
+        """
+        Private slot to select a topic to be shown.
+        
+        @param links dictionary with help topic as key (string) and
+            URL as value (QUrl)
+        @param keyword keyword for the link set (string)
+        """
+        if not self.__activating:
+            from .QtHelp.HelpTopicDialog import HelpTopicDialog
+            self.__activating = True
+            dlg = HelpTopicDialog(self, keyword, links)
+            if dlg.exec_() == QDialog.Accepted:
+                self.currentBrowser().setSource(dlg.link())
+            self.__activating = False
+    
+    def __activateCurrentBrowser(self):
+        """
+        Private slot to activate the current browser.
+        """
+        self.currentBrowser().setFocus()
+        
+    def __syncTOC(self):
+        """
+        Private slot to synchronize the TOC with the currently shown page.
+        """
+        if WebBrowserWindow.UseQtHelp:
+            QApplication.setOverrideCursor(Qt.WaitCursor)
+            url = self.currentBrowser().source()
+            self.__showTocWindow()
+            if not self.__tocWindow.syncToContent(url):
+                self.statusBar().showMessage(
+                    self.tr("Could not find an associated content."), 5000)
+            QApplication.restoreOverrideCursor()
+        
+    def __showTocWindow(self):
+        """
+        Private method to show the table of contents window.
+        """
+        if WebBrowserWindow.useQtHelp:
+            self.__activateDock(self.__tocWindow)
+        
+##    def __hideTocWindow(self):
+##        """
+##        Private method to hide the table of contents window.
+##        """
+##        if WebBrowserWindow.UseQtHelp:
+##            self.__tocDock.hide()
+##        
+    def __showIndexWindow(self):
+        """
+        Private method to show the index window.
+        """
+        if WebBrowserWindow.useQtHelp:
+            self.__activateDock(self.__indexWindow)
+        
+##    def __hideIndexWindow(self):
+##        """
+##        Private method to hide the index window.
+##        """
+##        if WebBrowserWindow.UseQtHelp:
+##            self.__indexDock.hide()
+##        
+    def __showSearchWindow(self):
+        """
+        Private method to show the search window.
+        """
+        if WebBrowserWindow.useQtHelp:
+            self.__activateDock(self.__searchWindow)
+        
+##    def __hideSearchWindow(self):
+##        """
+##        Private method to hide the search window.
+##        """
+##        if WebBrowserWindow.UseQtHelp:
+##            self.__searchDock.hide()
+##        
+    def __activateDock(self, widget):
+        """
+        Private method to activate the dock widget of the given widget.
+        
+        @param widget reference to the widget to be activated (QWidget)
+        """
+        widget.parent().show()
+        widget.parent().raise_()
+        widget.setFocus()
+        
+    def __setupFilterCombo(self):
+        """
+        Private slot to setup the filter combo box.
+        """
+        if WebBrowserWindow.useQtHelp:
+            curFilter = self.filterCombo.currentText()
+            if not curFilter:
+                curFilter = self.__helpEngine.currentFilter()
+            self.filterCombo.clear()
+            self.filterCombo.addItems(self.__helpEngine.customFilters())
+            idx = self.filterCombo.findText(curFilter)
+            if idx < 0:
+                idx = 0
+            self.filterCombo.setCurrentIndex(idx)
+        
+    def __filterQtHelpDocumentation(self, customFilter):
+        """
+        Private slot to filter the QtHelp documentation.
+        
+        @param customFilter name of filter to be applied (string)
+        """
+        if self.__helpEngine:
+            self.__helpEngine.setCurrentFilter(customFilter)
+        
+    def __manageQtHelpDocumentation(self):
+        """
+        Private slot to manage the QtHelp documentation database.
+        """
+        if WebBrowserWindow.useQtHelp:
+            from .QtHelp.QtHelpDocumentationDialog import \
+                QtHelpDocumentationDialog
+            dlg = QtHelpDocumentationDialog(self.__helpEngine, self)
+            dlg.exec_()
+            if dlg.hasChanges():
+                for i in sorted(dlg.getTabsToClose(), reverse=True):
+                    self.__tabWidget.closeBrowserAt(i)
+                self.__helpEngine.setupData()
+        
+    def getSourceFileList(self):
+        """
+        Public method to get a list of all opened source files.
+        
+        @return dictionary with tab id as key and host/namespace as value
+        """
+        return self.__tabWidget.getSourceFileList()
+    
+    def __manageQtHelpFilters(self):
+        """
+        Private slot to manage the QtHelp filters.
+        """
+        if WebBrowserWindow.useQtHelp:
+            from .QtHelp.QtHelpFiltersDialog import QtHelpFiltersDialog
+            dlg = QtHelpFiltersDialog(self.__helpEngine, self)
+            dlg.exec_()
+        
+    def __indexingStarted(self):
+        """
+        Private slot to handle the start of the indexing process.
+        """
+        if WebBrowserWindow.useQtHelp:
+            self.__indexing = True
+            if self.__indexingProgress is None:
+                self.__indexingProgress = QWidget()
+                layout = QHBoxLayout(self.__indexingProgress)
+                layout.setContentsMargins(0, 0, 0, 0)
+                sizePolicy = QSizePolicy(QSizePolicy.Preferred,
+                                         QSizePolicy.Maximum)
+                
+                label = QLabel(self.tr("Updating search index"))
+                label.setSizePolicy(sizePolicy)
+                layout.addWidget(label)
+                
+                progressBar = QProgressBar()
+                progressBar.setRange(0, 0)
+                progressBar.setTextVisible(False)
+                progressBar.setFixedHeight(16)
+                progressBar.setSizePolicy(sizePolicy)
+                layout.addWidget(progressBar)
+                
+                self.statusBar().insertPermanentWidget(
+                    0, self.__indexingProgress)
+        
+    def __indexingFinished(self):
+        """
+        Private slot to handle the start of the indexing process.
+        """
+        if WebBrowserWindow.useQtHelp:
+            self.statusBar().removeWidget(self.__indexingProgress)
+            self.__indexingProgress = None
+            self.__indexing = False
+            if self.__searchWord is not None:
+                self.__searchForWord()
+        
+    def __searchForWord(self):
+        """
+        Private slot to search for a word.
+        """
+        if WebBrowserWindow.useQtHelp and not self.__indexing and \
+                self.__searchWord is not None:
+            self.__searchDock.show()
+            self.__searchDock.raise_()
+            query = QHelpSearchQuery(QHelpSearchQuery.DEFAULT,
+                                     [self.__searchWord])
+            self.__searchEngine.search([query])
+            self.__searchWord = None
+        
+    def search(self, word):
+        """
+        Public method to search for a word.
+        
+        @param word word to search for (string)
+        """
+        if WebBrowserWindow.useQtHelp:
+            self.__searchWord = word
+            self.__searchForWord()
+        
+    def __removeOldDocumentation(self):
+        """
+        Private slot to remove non-existing documentation from the help engine.
+        """
+        for namespace in self.__helpEngine.registeredDocumentations():
+            docFile = self.__helpEngine.documentationFileName(namespace)
+            if not os.path.exists(docFile):
+                self.__helpEngine.unregisterDocumentation(namespace)
+        
+    def __lookForNewDocumentation(self):
+        """
+        Private slot to look for new documentation to be loaded into the
+        help database.
+        """
+        if WebBrowserWindow.useQtHelp:
+            from .QtHelp.HelpDocsInstaller import HelpDocsInstaller
+            self.__helpInstaller = HelpDocsInstaller(
+                self.__helpEngine.collectionFile())
+            self.__helpInstaller.errorMessage.connect(
+                self.__showInstallationError)
+            self.__helpInstaller.docsInstalled.connect(self.__docsInstalled)
+            
+            self.statusBar().showMessage(
+                self.tr("Looking for Documentation..."))
+            self.__helpInstaller.installDocs()
+        
+    def __showInstallationError(self, message):
+        """
+        Private slot to show installation errors.
+        
+        @param message message to be shown (string)
+        """
+        E5MessageBox.warning(
+            self,
+            self.tr("eric6 Web Browser"),
+            message)
+        
+    def __docsInstalled(self, installed):
+        """
+        Private slot handling the end of documentation installation.
+        
+        @param installed flag indicating that documents were installed
+            (boolean)
+        """
+        if WebBrowserWindow.useQtHelp:
+            if installed:
+                self.__helpEngine.setupData()
+            self.statusBar().clearMessage()
+        
+    def __initHelpDb(self):
+        """
+        Private slot to initialize the documentation database.
+        """
+        if WebBrowserWindow.useQtHelp:
+            if not self.__helpEngine.setupData():
+                return
+            
+            unfiltered = self.tr("Unfiltered")
+            if unfiltered not in self.__helpEngine.customFilters():
+                hc = QHelpEngineCore(self.__helpEngine.collectionFile())
+                hc.setupData()
+                hc.addCustomFilter(unfiltered, [])
+                hc = None
+                del hc
+                
+                self.__helpEngine.blockSignals(True)
+                self.__helpEngine.setCurrentFilter(unfiltered)
+                self.__helpEngine.blockSignals(False)
+                self.__helpEngine.setupData()
+        
+    def __warning(self, msg):
+        """
+        Private slot handling warnings from the help engine.
+        
+        @param msg message sent by the help  engine (string)
+        """
+        E5MessageBox.warning(
+            self,
+            self.tr("Help Engine"), msg)
+        
+    def __aboutToShowSettingsMenu(self):
+        """
+        Private slot to show the Settings menu.
+        """
+        self.editMessageFilterAct.setEnabled(
+            E5ErrorMessage.messageHandlerInstalled())
+        
+    def __showBackMenu(self):
+        """
+        Private slot showing the backwards navigation menu.
+        """
+        self.backMenu.clear()
+        history = self.currentBrowser().history()
+        historyCount = history.count()
+        backItems = history.backItems(historyCount)
+        for index in range(len(backItems) - 1, -1, -1):
+            item = backItems[index]
+            act = QAction(self)
+            act.setData(-1 * (index + 1))
+            icon = WebBrowserWindow.icon(item.url())
+            act.setIcon(icon)
+            act.setText(item.title())
+            self.backMenu.addAction(act)
+        
+    def __showForwardMenu(self):
+        """
+        Private slot showing the forwards navigation menu.
+        """
+        self.forwardMenu.clear()
+        history = self.currentBrowser().history()
+        historyCount = history.count()
+        forwardItems = history.forwardItems(historyCount)
+        for index in range(len(forwardItems)):
+            item = forwardItems[index]
+            act = QAction(self)
+            act.setData(index + 1)
+            icon = WebBrowserWindow.icon(item.url())
+            act.setIcon(icon)
+            act.setText(item.title())
+            self.forwardMenu.addAction(act)
+        
+    def __navigationMenuActionTriggered(self, act):
+        """
+        Private slot to go to the selected page.
+        
+        @param act reference to the action selected in the navigation menu
+            (QAction)
+        """
+        offset = act.data()
+        history = self.currentBrowser().history()
+        historyCount = history.count()
+        if offset < 0:
+            # go back
+            history.goToItem(history.backItems(historyCount)[-1 * offset - 1])
+        else:
+            # go forward
+            history.goToItem(history.forwardItems(historyCount)[offset - 1])
+        
+    def __clearPrivateData(self):
+        """
+        Private slot to clear the private data.
+        """
+        from .WebBrowserClearPrivateDataDialog import \
+            WebBrowserClearPrivateDataDialog
+        dlg = WebBrowserClearPrivateDataDialog(self)
+        if dlg.exec_() == QDialog.Accepted:
+            # browsing history, search history, favicons, disk cache, cookies,
+            # passwords, web databases, downloads, Flash cookies
+            (history, searches, favicons, cache, cookies,
+             passwords, databases, downloads, flashCookies, zoomValues,
+             sslExceptions, historyPeriod) = dlg.getData()
+            if history:
+                self.historyManager().clear(historyPeriod)
+                self.__tabWidget.clearClosedTabsList()
+                self.webProfile().clearAllVisitedLinks()
+            if searches:
+                self.searchEdit.clear()
+            if downloads:
+                self.downloadManager().cleanup()
+                self.downloadManager().hide()
+            if favicons:
+                self.__clearIconsDatabase()
+            if cache:
+                cachePath = self.webProfile().cachePath()
+                if cachePath:
+                    shutil.rmtree(cachePath)
+            if cookies:
+                self.cookieJar().clear()
+                self.webProfile().cookieStore().deleteAllCookies()
+            if passwords:
+                self.passwordManager().clear()
+            if flashCookies:
+                self.flashCookieManager().removeAllCookies()
+            if zoomValues:
+                ZoomManager.instance().clear()
+            if sslExceptions:
+                self.networkManager().clearSslExceptions()
+        
+    def __showEnginesConfigurationDialog(self):
+        """
+        Private slot to show the search engines configuration dialog.
+        """
+        from .OpenSearch.OpenSearchDialog import OpenSearchDialog
+        
+        dlg = OpenSearchDialog(self)
+        dlg.exec_()
+        
+    def searchEnginesAction(self):
+        """
+        Public method to get a reference to the search engines configuration
+        action.
+        
+        @return reference to the search engines configuration action (QAction)
+        """
+        return self.searchEnginesAct
+        
+    def __showPasswordsDialog(self):
+        """
+        Private slot to show the passwords management dialog.
+        """
+        from .Passwords.PasswordsDialog import PasswordsDialog
+        
+        dlg = PasswordsDialog(self)
+        dlg.exec_()
+    
+    # TODO: Certificates
+##    def __showCertificatesDialog(self):
+##        """
+##        Private slot to show the certificates management dialog.
+##        """
+##        from E5Network.E5SslCertificatesDialog import E5SslCertificatesDialog
+##        
+##        dlg = E5SslCertificatesDialog(self)
+##        dlg.exec_()
+##        
+    def __showCertificateErrorsDialog(self):
+        """
+        Private slot to show the certificate errors management dialog.
+        """
+        self.networkManager().showSslErrorExceptionsDialog()
+    
+    def __showAdBlockDialog(self):
+        """
+        Private slot to show the AdBlock configuration dialog.
+        """
+        self.adBlockManager().showDialog()
+        
+    # TODO: Click2Flash
+##    def __showClickToFlashDialog(self):
+##        """
+##        Private slot to open the ClickToFlash whitelist configuration dialog.
+##        """
+##        from .HelpBrowserWV import HelpWebPage
+##        HelpWebPage.webPluginFactory().plugin("ClickToFlash").configure()
+##        
+    def __showPersonalInformationDialog(self):
+        """
+        Private slot to show the Personal Information configuration dialog.
+        """
+        self.personalInformationManager().showConfigurationDialog()
+        
+    def __showGreaseMonkeyConfigDialog(self):
+        """
+        Private slot to show the GreaseMonkey scripts configuration dialog.
+        """
+        self.greaseMonkeyManager().showConfigurationDialog()
+        
+    def __showFeaturePermissionDialog(self):
+        """
+        Private slot to show the feature permission dialog.
+        """
+        self.featurePermissionManager().showFeaturePermissionsDialog()
+        
+    def __showZoomValuesDialog(self):
+        """
+        Private slot to show the zoom values management dialog.
+        """
+        from .ZoomManager.ZoomValuesDialog import ZoomValuesDialog
+        
+        dlg = ZoomValuesDialog(self)
+        dlg.exec_()
+    
+    # TODO: Network Monitor
+##    def __showNetworkMonitor(self):
+##        """
+##        Private slot to show the network monitor dialog.
+##        """
+##        from E5Network.E5NetworkMonitor import E5NetworkMonitor
+##        monitor = E5NetworkMonitor.instance(self.networkManager())
+##        monitor.show()
+##        
+    def __showDownloadsWindow(self):
+        """
+        Private slot to show the downloads dialog.
+        """
+        self.downloadManager().show()
+        
+    # TODO: Network Monitor
+##    def __closeNetworkMonitor(self):
+##        """
+##        Private slot to close the network monitor dialog.
+##        """
+##        from E5Network.E5NetworkMonitor import E5NetworkMonitor
+##        E5NetworkMonitor.closeMonitor()
+##        
+    def __showPageSource(self):
+        """
+        Private slot to show the source of the current page in an editor.
+        """
+        self.currentBrowser().page().toHtml(self.__showPageSourceCallback)
+        
+    def __showPageSourceCallback(self, src):
+        """
+        Private method to show the source of the current page in an editor.
+        
+        @param src source of the web page
+        @type str
+        """
+        from QScintilla.MiniEditor import MiniEditor
+        editor = MiniEditor(parent=self)
+        editor.setText(src, "Html")
+        editor.setLanguage("dummy.html")
+        editor.show()
+    
+    def __toggleJavaScriptConsole(self):
+        """
+        Private slot to toggle the JavaScript console.
+        """
+        if self.__javascriptConsoleDock.isVisible():
+            self.__javascriptConsoleDock.hide()
+        else:
+            self.__javascriptConsoleDock.show()
+    
+    def javascriptConsole(self):
+        """
+        Public method to get a reference to the JavaScript console widget.
+        
+        @return reference to the JavaScript console
+        @rtype WebBrowserJavaScriptConsole
+        """
+        return self.__javascriptConsole
+    
+    @classmethod
+    def icon(cls, url):
+        """
+        Class method to get the icon for an URL.
+        
+        @param url URL to get icon for (QUrl)
+        @return icon for the URL (QIcon)
+        """
+        return WebIconProvider.instance().iconForUrl(url)
+
+    @classmethod
+    def bookmarksManager(cls):
+        """
+        Class method to get a reference to the bookmarks manager.
+        
+        @return reference to the bookmarks manager (BookmarksManager)
+        """
+        if cls._bookmarksManager is None:
+            from .Bookmarks.BookmarksManager import BookmarksManager
+            cls._bookmarksManager = BookmarksManager()
+        
+        return cls._bookmarksManager
+    
+    def openUrl(self, url, title):
+        """
+        Public slot to load a URL in the current tab.
+        
+        @param url URL to be opened (QUrl)
+        @param title title of the bookmark (string)
+        """
+        self.__linkActivated(url)
+    
+    def openUrlNewTab(self, url, title):
+        """
+        Public slot to load a URL in a new tab.
+        
+        @param url URL to be opened (QUrl)
+        @param title title of the bookmark (string)
+        """
+        self.newTab(url)
+    
+    @classmethod
+    def historyManager(cls):
+        """
+        Class method to get a reference to the history manager.
+        
+        @return reference to the history manager (HistoryManager)
+        """
+        if cls._historyManager is None:
+            from .History.HistoryManager import HistoryManager
+            cls._historyManager = HistoryManager()
+        
+        return cls._historyManager
+        
+    @classmethod
+    def passwordManager(cls):
+        """
+        Class method to get a reference to the password manager.
+        
+        @return reference to the password manager (PasswordManager)
+        """
+        if cls._passwordManager is None:
+            from .Passwords.PasswordManager import PasswordManager
+            cls._passwordManager = PasswordManager()
+        
+        return cls._passwordManager
+    
+    @classmethod
+    def adBlockManager(cls):
+        """
+        Class method to get a reference to the AdBlock manager.
+        
+        @return reference to the AdBlock manager (AdBlockManager)
+        """
+        if cls._adblockManager is None:
+            from .AdBlock.AdBlockManager import AdBlockManager
+            cls._adblockManager = AdBlockManager()
+        
+        return cls._adblockManager
+    
+    def adBlockIcon(self):
+        """
+        Public method to get a reference to the AdBlock icon.
+        
+        @return reference to the AdBlock icon (AdBlockIcon)
+        """
+        return self.__adBlockIcon
+    
+    @classmethod
+    def downloadManager(cls):
+        """
+        Class method to get a reference to the download manager.
+        
+        @return reference to the download manager (DownloadManager)
+        """
+        if cls._downloadManager is None:
+            from .Download.DownloadManager import DownloadManager
+            cls._downloadManager = DownloadManager()
+        
+        return cls._downloadManager
+        
+    @classmethod
+    def personalInformationManager(cls):
+        """
+        Class method to get a reference to the personal information manager.
+        
+        @return reference to the personal information manager
+            (PersonalInformationManager)
+        """
+        if cls._personalInformationManager is None:
+            from .PersonalInformationManager.PersonalInformationManager \
+                import PersonalInformationManager
+            cls._personalInformationManager = PersonalInformationManager()
+        
+        return cls._personalInformationManager
+        
+    @classmethod
+    def greaseMonkeyManager(cls):
+        """
+        Class method to get a reference to the GreaseMonkey manager.
+        
+        @return reference to the GreaseMonkey manager (GreaseMonkeyManager)
+        """
+        if cls._greaseMonkeyManager is None:
+            from .GreaseMonkey.GreaseMonkeyManager import GreaseMonkeyManager
+            cls._greaseMonkeyManager = GreaseMonkeyManager()
+        
+        return cls._greaseMonkeyManager
+        
+    @classmethod
+    def featurePermissionManager(cls):
+        """
+        Class method to get a reference to the feature permission manager.
+        
+        @return reference to the feature permission manager
+        @rtype FeaturePermissionManager
+        """
+        if cls._featurePermissionManager is None:
+            from .FeaturePermissions.FeaturePermissionManager import \
+                FeaturePermissionManager
+            cls._featurePermissionManager = FeaturePermissionManager()
+        
+        return cls._featurePermissionManager
+        
+    @classmethod
+    def flashCookieManager(cls):
+        """
+        Class method to get a reference to the flash cookies manager.
+        
+        @return reference to the flash cookies manager
+        @rtype FlashCookieManager
+        """
+        if cls._flashCookieManager is None:
+            from .FlashCookieManager.FlashCookieManager import \
+                FlashCookieManager
+            cls._flashCookieManager = FlashCookieManager()
+        
+        return cls._flashCookieManager
+        
+    @classmethod
+    def mainWindow(cls):
+        """
+        Class method to get a reference to the main window.
+        
+        @return reference to the main window (WebBrowserWindow)
+        """
+        if cls.BrowserWindows:
+            return cls.BrowserWindows[0]
+        else:
+            return None
+    
+    @classmethod
+    def mainWindows(cls):
+        """
+        Class method to get references to all main windows.
+        
+        @return references to all main window (list of WebBrowserWindow)
+        """
+        return cls.BrowserWindows
+    
+    def __appFocusChanged(self, old, now):
+        """
+        Private slot to handle a change of the focus.
+        
+        @param old reference to the widget, that lost focus (QWidget or None)
+        @param now reference to the widget having the focus (QWidget or None)
+        """
+        if isinstance(now, WebBrowserWindow):
+            self.__lastActiveWindow = now
+    
+    def getWindow(self):
+        """
+        Public method to get a reference to the most recent active
+        web browser window.
+        
+        @return reference to most recent web browser window
+        @rtype WebBrowserWindow
+        """
+        if self.__lastActiveWindow:
+            return self.__lastActiveWindow
+        
+        return self.mainWindow()
+    
+    def openSearchManager(self):
+        """
+        Public method to get a reference to the opensearch manager object.
+        
+        @return reference to the opensearch manager object (OpenSearchManager)
+        """
+        return self.searchEdit.openSearchManager()
+    
+    def __aboutToShowTextEncodingMenu(self):
+        """
+        Private slot to populate the text encoding menu.
+        """
+        self.__textEncodingMenu.clear()
+        
+        codecs = []
+        for codec in QTextCodec.availableCodecs():
+            codecs.append(str(codec, encoding="utf-8").lower())
+        codecs.sort()
+        
+        defaultTextEncoding = \
+            QWebEngineSettings.globalSettings().defaultTextEncoding().lower()
+        if defaultTextEncoding in codecs:
+            currentCodec = defaultTextEncoding
+        else:
+            currentCodec = ""
+        
+        isDefaultEncodingUsed = True
+        isoMenu = QMenu(self.tr("ISO"), self.__textEncodingMenu)
+        winMenu = QMenu(self.tr("Windows"), self.__textEncodingMenu)
+        isciiMenu = QMenu(self.tr("ISCII"), self.__textEncodingMenu)
+        uniMenu = QMenu(self.tr("Unicode"), self.__textEncodingMenu)
+        otherMenu = QMenu(self.tr("Other"), self.__textEncodingMenu)
+        ibmMenu = QMenu(self.tr("IBM"), self.__textEncodingMenu)
+        
+        for codec in codecs:
+            if codec.startswith(("iso", "latin", "csisolatin")):
+                act = isoMenu.addAction(codec)
+            elif codec.startswith(("windows", "cp1")):
+                act = winMenu.addAction(codec)
+            elif codec.startswith("iscii"):
+                act = isciiMenu.addAction(codec)
+            elif codec.startswith("utf"):
+                act = uniMenu.addAction(codec)
+            elif codec.startswith(("ibm", "csibm", "cp")):
+                act = ibmMenu.addAction(codec)
+            else:
+                act = otherMenu.addAction(codec)
+            
+            act.setData(codec)
+            act.setCheckable(True)
+            if currentCodec == codec:
+                act.setChecked(True)
+                isDefaultEncodingUsed = False
+        
+        act = self.__textEncodingMenu.addAction(
+            self.tr("Default Encoding"))
+        act.setData("")
+        act.setCheckable(True)
+        act.setChecked(isDefaultEncodingUsed)
+        self.__textEncodingMenu.addMenu(uniMenu)
+        self.__textEncodingMenu.addMenu(isoMenu)
+        self.__textEncodingMenu.addMenu(winMenu)
+        self.__textEncodingMenu.addMenu(ibmMenu)
+        self.__textEncodingMenu.addMenu(isciiMenu)
+        self.__textEncodingMenu.addMenu(otherMenu)
+    
+    def __setTextEncoding(self, act):
+        """
+        Private slot to set the selected text encoding as the default for
+        this session.
+        
+        @param act reference to the selected action (QAction)
+        """
+        codec = act.data()
+        if codec == "":
+            QWebEngineSettings.globalSettings().setDefaultTextEncoding("")
+        else:
+            QWebEngineSettings.globalSettings().setDefaultTextEncoding(codec)
+    
+    def eventMouseButtons(self):
+        """
+        Public method to get the last recorded mouse buttons.
+        
+        @return mouse buttons (Qt.MouseButtons)
+        """
+        return self.__eventMouseButtons
+    
+    def eventKeyboardModifiers(self):
+        """
+        Public method to get the last recorded keyboard modifiers.
+        
+        @return keyboard modifiers (Qt.KeyboardModifiers)
+        """
+        return self.__eventKeyboardModifiers
+    
+    def setEventMouseButtons(self, buttons):
+        """
+        Public method to record mouse buttons.
+        
+        @param buttons mouse buttons to record (Qt.MouseButtons)
+        """
+        self.__eventMouseButtons = buttons
+    
+    def setEventKeyboardModifiers(self, modifiers):
+        """
+        Public method to record keyboard modifiers.
+        
+        @param modifiers keyboard modifiers to record (Qt.KeyboardModifiers)
+        """
+        self.__eventKeyboardModifiers = modifiers
+    
+    def mousePressEvent(self, evt):
+        """
+        Protected method called by a mouse press event.
+        
+        @param evt reference to the mouse event (QMouseEvent)
+        """
+        if evt.button() == Qt.XButton1:
+            self.currentBrowser().triggerPageAction(QWebEnginePage.Back)
+        elif evt.button() == Qt.XButton2:
+            self.currentBrowser().triggerPageAction(QWebEnginePage.Forward)
+        else:
+            super(WebBrowserWindow, self).mousePressEvent(evt)
+    
+    @classmethod
+    def feedsManager(cls):
+        """
+        Class method to get a reference to the RSS feeds manager.
+        
+        @return reference to the RSS feeds manager (FeedsManager)
+        """
+        if cls._feedsManager is None:
+            from .Feeds.FeedsManager import FeedsManager
+            cls._feedsManager = FeedsManager()
+        
+        return cls._feedsManager
+    
+    def __showFeedsManager(self):
+        """
+        Private slot to show the feeds manager dialog.
+        """
+        feedsManager = self.feedsManager()
+        feedsManager.openUrl.connect(self.openUrl)
+        feedsManager.newUrl.connect(self.openUrlNewTab)
+        feedsManager.rejected.connect(self.__feedsManagerClosed)
+        feedsManager.show()
+    
+    def __feedsManagerClosed(self):
+        """
+        Private slot to handle closing the feeds manager dialog.
+        """
+        feedsManager = self.sender()
+        feedsManager.openUrl.disconnect(self.openUrl)
+        feedsManager.newUrl.disconnect(self.openUrlNewTab)
+        feedsManager.rejected.disconnect(self.__feedsManagerClosed)
+    
+    def __showSiteinfoDialog(self):
+        """
+        Private slot to show the site info dialog.
+        """
+        from .SiteInfo.SiteInfoDialog import SiteInfoDialog
+        self.__siteinfoDialog = SiteInfoDialog(self.currentBrowser(), self)
+        self.__siteinfoDialog.show()
+
+    # TODO: User Agents
+##    @classmethod
+##    def userAgentsManager(cls):
+##        """
+##        Class method to get a reference to the user agents manager.
+##        
+##        @return reference to the user agents manager (UserAgentManager)
+##        """
+##        if cls._userAgentsManager is None:
+##            from .UserAgent.UserAgentManager import UserAgentManager
+##            cls._userAgentsManager = UserAgentManager()
+##        
+##        return cls._userAgentsManager
+##    
+##    def __showUserAgentsDialog(self):
+##        """
+##        Private slot to show the user agents management dialog.
+##        """
+##        from .UserAgent.UserAgentsDialog import UserAgentsDialog
+##        
+##        dlg = UserAgentsDialog(self)
+##        dlg.exec_()
+##    
+    @classmethod
+    def syncManager(cls):
+        """
+        Class method to get a reference to the data synchronization manager.
+        
+        @return reference to the data synchronization manager (SyncManager)
+        """
+        if cls._syncManager is None:
+            from .Sync.SyncManager import SyncManager
+            cls._syncManager = SyncManager()
+        
+        return cls._syncManager
+    
+    def __showSyncDialog(self):
+        """
+        Private slot to show the synchronization dialog.
+        """
+        self.syncManager().showSyncDialog()
+    
+    @classmethod
+    def speedDial(cls):
+        """
+        Class methdo to get a reference to the speed dial.
+        
+        @return reference to the speed dial (SpeedDial)
+        """
+        if cls._speedDial is None:
+            from .SpeedDial.SpeedDial import SpeedDial
+            cls._speedDial = SpeedDial()
+        
+        return cls._speedDial
+    
+    def keyPressEvent(self, evt):
+        """
+        Protected method to handle key presses.
+        
+        @param evt reference to the key press event (QKeyEvent)
+        """
+        number = -1
+        key = evt.key()
+        
+        if key == Qt.Key_1:
+            number = 1
+        elif key == Qt.Key_2:
+            number = 2
+        elif key == Qt.Key_3:
+            number = 3
+        elif key == Qt.Key_4:
+            number = 4
+        elif key == Qt.Key_5:
+            number = 5
+        elif key == Qt.Key_6:
+            number = 6
+        elif key == Qt.Key_7:
+            number = 7
+        elif key == Qt.Key_8:
+            number = 8
+        elif key == Qt.Key_9:
+            number = 9
+        elif key == Qt.Key_0:
+            number = 10
+        
+        if number != -1:
+            if evt.modifiers() == Qt.KeyboardModifiers(Qt.AltModifier):
+                if number == 10:
+                    number = self.__tabWidget.count()
+                self.__tabWidget.setCurrentIndex(number - 1)
+                return
+            
+            if evt.modifiers() == Qt.KeyboardModifiers(Qt.MetaModifier):
+                url = self.speedDial().urlForShortcut(number - 1)
+                if url.isValid():
+                    self.__linkActivated(url)
+                    return
+        
+        super(WebBrowserWindow, self).keyPressEvent(evt)
+    
+    def event(self, evt):
+        """
+        Public method handling events.
+        
+        @param evt reference to the event
+        @type QEvent
+        @return flag indicating a handled event
+        @rtype bool
+        """
+        if evt.type() == QEvent.WindowStateChange:
+            if not bool(evt.oldState() & Qt.WindowFullScreen) and \
+               bool(self.windowState() & Qt.WindowFullScreen):
+                # enter full screen mode
+                self.__windowStates = evt.oldState()
+            elif bool(evt.oldState() & Qt.WindowFullScreen) and \
+                 not bool(self.windowState() & Qt.WindowFullScreen):
+                # leave full screen mode
+                self.setWindowState(self.__windowStates)
+                self.__htmlFullScreen = False
+        
+        return super(WebBrowserWindow, self).event(evt)
+    
+    ###########################################################################
+    ## Interface to VirusTotal below                                         ##
+    ###########################################################################
+    
+    def __virusTotalScanCurrentSite(self):
+        """
+        Private slot to ask VirusTotal for a scan of the URL of the current
+        browser.
+        """
+        cb = self.currentBrowser()
+        if cb is not None:
+            url = cb.url()
+            if url.scheme() in ["http", "https", "ftp"]:
+                self.requestVirusTotalScan(url)
+    
+    def requestVirusTotalScan(self, url):
+        """
+        Public method to submit a request to scan an URL by VirusTotal.
+        
+        @param url URL to be scanned (QUrl)
+        """
+        self.__virusTotal.submitUrl(url)
+    
+    def __virusTotalSubmitUrlError(self, msg):
+        """
+        Private slot to handle an URL scan submission error.
+        
+        @param msg error message (str)
+        """
+        E5MessageBox.critical(
+            self,
+            self.tr("VirusTotal Scan"),
+            self.tr("""<p>The VirusTotal scan could not be"""
+                    """ scheduled.<p>\n<p>Reason: {0}</p>""").format(msg))
+    
+    def __virusTotalUrlScanReport(self, url):
+        """
+        Private slot to initiate the display of the URL scan report page.
+        
+        @param url URL of the URL scan report page (string)
+        """
+        self.newTab(url)
+    
+    def __virusTotalFileScanReport(self, url):
+        """
+        Private slot to initiate the display of the file scan report page.
+        
+        @param url URL of the file scan report page (string)
+        """
+        self.newTab(url)
+    
+    def __virusTotalIpAddressReport(self):
+        """
+        Private slot to retrieve an IP address report.
+        """
+        ip, ok = QInputDialog.getText(
+            self,
+            self.tr("IP Address Report"),
+            self.tr("Enter a valid IPv4 address in dotted quad notation:"),
+            QLineEdit.Normal)
+        if ok and ip:
+            if ip.count(".") == 3:
+                self.__virusTotal.getIpAddressReport(ip)
+            else:
+                E5MessageBox.information(
+                    self,
+                    self.tr("IP Address Report"),
+                    self.tr("""The given IP address is not in dotted quad"""
+                            """ notation."""))
+    
+    def __virusTotalDomainReport(self):
+        """
+        Private slot to retrieve a domain report.
+        """
+        domain, ok = QInputDialog.getText(
+            self,
+            self.tr("Domain Report"),
+            self.tr("Enter a valid domain name:"),
+            QLineEdit.Normal)
+        if ok and domain:
+            self.__virusTotal.getDomainReport(domain)
+    
+    ###########################################################################
+    ## Style sheet handling below                                            ##
+    ###########################################################################
+    
+    def reloadUserStyleSheet(self):
+        """
+        Public method to reload the user style sheet.
+        """
+        styleSheet = Preferences.getWebBrowser("UserStyleSheet")
+        self.__setUserStyleSheet(styleSheet)
+    
+    def __setUserStyleSheet(self, styleSheetFile):
+        """
+        Private method to set a user style sheet.
+        
+        @param styleSheetFile name of the user style sheet file (string)
+        """
+        userStyle = \
+            self.adBlockManager().elementHidingRules().replace('"', '\\"')
+        
+        userStyle += WebBrowserTools.readAllFileContents(styleSheetFile)\
+            .replace("\n", "")
+        name = "_eric_userstylesheet"
+        
+        oldScript = self.webProfile().scripts().findScript(name)
+        if not oldScript.isNull():
+            self.webProfile().scripts().remove(oldScript)
+        
+        if userStyle:
+            script = QWebEngineScript()
+            script.setName(name)
+            script.setInjectionPoint(QWebEngineScript.DocumentCreation)
+            script.setWorldId(QWebEngineScript.ApplicationWorld)
+            script.setRunsOnSubFrames(True)
+            script.setSourceCode(Scripts.setStyleSheet(userStyle))
+            self.webProfile().scripts().insert(script)
+    
+    ##########################################
+    ## Support for desktop notifications below
+    ##########################################
+    
+    @classmethod
+    def showNotification(cls, icon, heading, text):
+        """
+        Class method to show a desktop notification.
+        
+        @param icon icon to be shown in the notification (QPixmap)
+        @param heading heading of the notification (string)
+        @param text text of the notification (string)
+        """
+        if cls._fromEric:
+            e5App().getObject("UserInterface").showNotification(
+                icon, heading, text)
+        else:
+            if Preferences.getUI("NotificationsEnabled"):
+                if cls._notification is None:
+                    from UI.NotificationWidget import NotificationWidget
+                    cls._notification = NotificationWidget()
+                cls._notification.setPixmap(icon)
+                cls._notification.setHeading(heading)
+                cls._notification.setText(text)
+                cls._notification.setTimeout(
+                    Preferences.getUI("NotificationTimeout"))
+                cls._notification.move(
+                    Preferences.getUI("NotificationPosition"))
+                cls._notification.show()
+    
+    @classmethod
+    def notificationsEnabled(cls):
+        """
+        Class method to check, if notifications are enabled.
+        
+        @return flag indicating, if notifications are enabled (boolean)
+        """
+        if cls._fromEric:
+            return e5App().getObject("UserInterface").notificationsEnabled()
+        else:
+            return Preferences.getUI("NotificationsEnabled")
+    
+    ###################################
+    ## Support for download files below
+    ###################################
+    
+    @classmethod
+    def downloadRequested(self, download):
+        """
+        Class method to handle a download request.
+        
+        @param download reference to the download data
+        @type QWebEngineDownloadItem
+        """
+        self.downloadManager().download(download)
+    
+    ########################################
+    ## Support for web engine profiles below
+    ########################################
+    
+    @classmethod
+    def webProfile(cls, private=False):
+        """
+        Class method handling the web engine profile.
+        
+        @param private flag indicating the privacy mode
+        @type bool
+        @return reference to the web profile object
+        @rtype QWebEngineProfile
+        """
+        if cls._webProfile is None:
+            if private:
+                cls._webProfile = QWebEngineProfile()
+            else:
+                cls._webProfile = QWebEngineProfile.defaultProfile()
+            cls._webProfile.downloadRequested.connect(
+                cls.downloadRequested)
+            
+            # add the default user agent string
+            userAgent = cls._webProfile.httpUserAgent()
+            cls._webProfile.defaultUserAgent = userAgent
+            
+            if not private:
+                if Preferences.getWebBrowser("DiskCacheEnabled"):
+                    cls._webProfile.setHttpCacheType(
+                        QWebEngineProfile.DiskHttpCache)
+                    cls._webProfile.setHttpCacheMaximumSize(
+                        Preferences.getWebBrowser("DiskCacheSize")
+                        * 1024 * 1024)
+                    cls._webProfile.setCachePath(os.path.join(
+                        Utilities.getConfigDir(), "web_browser"))
+                else:
+                    cls._webProfile.setHttpCacheType(
+                        QWebEngineProfile.MemoryHttpCache)
+                    cls._webProfile.setHttpCacheMaximumSize(0)
+                cls._webProfile.setPersistentStoragePath(os.path.join(
+                    Utilities.getConfigDir(), "web_browser",
+                    "persistentstorage"))
+                cls._webProfile.setPersistentCookiesPolicy(
+                    QWebEngineProfile.AllowPersistentCookies)
+            
+            # Setup QWebChannel user script
+            script = QWebEngineScript()
+            script.setName("_eric_webchannel")
+            script.setInjectionPoint(QWebEngineScript.DocumentCreation)
+            script.setWorldId(QWebEngineScript.MainWorld)
+            script.setRunsOnSubFrames(True)
+            script.setSourceCode(Scripts.setupWebChannel())
+            cls._webProfile.scripts().insert(script)
+        
+        return cls._webProfile
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/WebInspector.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,183 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2015 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a QWebEngineView to load the web inspector in.
+"""
+
+from __future__ import unicode_literals
+try:
+    str = unicode           # __IGNORE_EXCEPTION__
+except NameError:
+    pass
+
+import json
+
+from PyQt5.QtCore import QSize, QUrl
+from PyQt5.QtNetwork import QNetworkRequest
+from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEnginePage
+
+import Preferences
+
+_VIEWS = []
+
+
+class WebInspector(QWebEngineView):
+    """
+    Class implementing a QWebEngineView to load the web inspector in.
+    """
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget
+        @type QWidget
+        """
+        super(WebInspector, self).__init__(parent)
+        
+        self.__view = None
+        self.__inspectElement = False
+        
+        self.__reloadGeometry()
+        
+        registerView(self)
+        
+        self.page().windowCloseRequested.connect(self.close)
+        self.page().loadFinished.connect(self.__loadFinished)
+    
+    def closeEvent(self, evt):
+        """
+        Protected method to save the geometry when closed.
+        
+        @param evt event object
+        @type QCloseEvent
+        """
+        Preferences.setGeometry("WebInspectorGeometry", self.saveGeometry())
+        super(WebInspector, self).closeEvent(evt)
+
+    def __reloadGeometry(self):
+        """
+        Private method to restore the geometry.
+        """
+        geom = Preferences.getGeometry("WebInspectorGeometry")
+        if geom.isEmpty():
+            s = QSize(600, 600)
+            self.resize(s)
+        else:
+            self.restoreGeometry(geom)
+    
+    def setView(self, view, inspectElement=False):
+        """
+        Public method to connect a view to this inspector.
+        
+        @param view reference to the view object
+        @type WebBrowserView
+        @param inspectElement flag indicating to start a web inspection
+        @type bool
+        """
+        self.__view = view
+        if not self.isEnabled():
+            return
+        
+        self.__inspectElement = inspectElement
+        
+        port = Preferences.getWebBrowser("WebInspectorPort")
+        inspectorUrl = QUrl("http://localhost:{0}".format(port))
+        
+        from WebBrowser.WebBrowserWindow import WebBrowserWindow
+        self.__reply = WebBrowserWindow.networkManager().get(
+            QNetworkRequest(inspectorUrl.resolved(QUrl("json/list"))))
+        self.__reply.finished.connect(self.__inspectorReplyFinished)
+    
+    def __inspectorReplyFinished(self):
+        """
+        Private slot handling the reply.
+        """
+        result = str(self.__reply.readAll(), encoding="utf8")
+        clients = json.loads(result)
+        
+        self.__reply.deleteLater()
+        self.__replay = None
+        
+        pageUrl = QUrl()
+        try:
+            index = _VIEWS.index(self.__view)
+        except ValueError:
+            index = -1
+        if len(clients) > index:
+            port = Preferences.getWebBrowser("WebInspectorPort")
+            inspectorUrl = QUrl("http://localhost:{0}".format(port))
+            
+            client = clients[index]
+            pageUrl = inspectorUrl.resolved(
+                QUrl(client["devtoolsFrontendUrl"]))
+        self.load(pageUrl)
+        pushView(self)
+        self.show()
+    
+    def inspectElement(self):
+        """
+        Public method to inspect an element.
+        """
+        self.__inspectElement = True
+    
+    def isEnabled(self):
+        """
+        Public method to check, if the web inspector is enabled.
+        
+        @return flag indicating the enabled state
+        @rtype bool
+        """
+        return Preferences.getWebBrowser("WebInspectorEnabled")
+    
+    def __loadFinished(self):
+        """
+        Private slot handling the finished signal.
+        """
+        if self.__inspectElement:
+            self.__view.triggerPageAction(QWebEnginePage.InspectElement)
+            self.__inspectElement = False
+
+
+def registerView(view):
+    """
+    Function to register a view.
+    
+    @param view reference to the view
+    @type WebBrowserView
+    """
+    if _VIEWS is None:
+        return
+    
+    _VIEWS.insert(0, view)
+
+
+def unregisterView(view):
+    """
+    Function to unregister a view.
+    
+    @param view reference to the view
+    @type WebBrowserView
+    """
+    if _VIEWS is None:
+        return
+    
+    if view in _VIEWS:
+        _VIEWS.remove(view)
+
+
+def pushView(view):
+    """
+    Function to push a view to the front of the list.
+    
+    @param view reference to the view
+    @type WebBrowserView
+    """
+    if _VIEWS is None:
+        return
+    
+    if view in _VIEWS:
+        _VIEWS.remove(view)
+    _VIEWS.insert(0, view)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/ZoomManager/ZoomManager.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,230 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2015 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a manager for site specific zoom level settings.
+"""
+
+from __future__ import unicode_literals
+
+import json
+
+from PyQt5.QtCore import pyqtSignal, QObject
+
+from Utilities.AutoSaver import AutoSaver
+import Preferences
+
+
+class ZoomManager(QObject):
+    """
+    Class implementing a manager for site specific zoom level settings.
+    """
+    changed = pyqtSignal()
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object (QObject)
+        """
+        super(ZoomManager, self).__init__(parent)
+        
+        self.__zoomDB = {}
+        
+        self.__saveTimer = AutoSaver(self, self.save)
+        
+        self.changed.connect(self.__saveTimer.changeOccurred)
+        
+        self.__loaded = False
+    
+    def close(self):
+        """
+        Public method to close the zoom manager.
+        """
+        self.__saveTimer.saveIfNeccessary()
+    
+    def load(self):
+        """
+        Public method to load the bookmarks.
+        """
+        if self.__loaded:
+            return
+        
+        dbString = Preferences.getWebBrowser("ZoomValuesDB")
+        if dbString:
+            try:
+                db = json.loads(dbString)
+                self.__zoomDB = db
+            except ValueError:
+                # ignore silently
+                pass
+        
+        self.__loaded = True
+    
+    def save(self):
+        """
+        Public method to save the zoom values.
+        """
+        if not self.__loaded:
+            return
+        
+        from WebBrowser.WebBrowserWindow import WebBrowserWindow
+        if not WebBrowserWindow.isPrivate():
+            dbString = json.dumps(self.__zoomDB)
+            Preferences.setWebBrowser("ZoomValuesDB", dbString)
+    
+    def __keyFromUrl(self, url):
+        """
+        Private method to generate a DB key for an URL.
+        
+        @param url URL to generate a key for
+        @type QUrl
+        @return key for the given URL
+        @rtype str
+        """
+        if url.isEmpty():
+            key = ""
+        else:
+            scheme = url.scheme()
+            host = url.host()
+            if host:
+                key = host
+            elif scheme == "file":
+                path = url.path()
+                key = path.rsplit("/", 1)[0]
+            else:
+                key = ""
+        
+        return key
+    
+    def setZoomValue(self, url, zoomValue):
+        """
+        Public method to record the zoom value for the given URL.
+        
+        Note: Only zoom values not equal 100% are recorded.
+        
+        @param url URL of the page to remember the zoom value for
+        @type QUrl
+        @param zoomValue zoom value for the URL
+        @type int
+        """
+        self.load()
+        
+        key = self.__keyFromUrl(url)
+        if not key:
+            return
+        
+        if ((zoomValue == 100 and key not in self.__zoomDB) or
+                (key in self.__zoomDB and self.__zoomDB[key] == zoomValue)):
+            return
+        
+        if zoomValue == 100:
+            del self.__zoomDB[key]
+        else:
+            self.__zoomDB[key] = zoomValue
+        
+        self.changed.emit()
+    
+    def zoomValue(self, url):
+        """
+        Public method to get the zoom value for an URL.
+        
+        @param url URL of the page to get the zoom value for
+        @type QUrl
+        @return zoomValue zoom value for the URL
+        @rtype int
+        """
+        self.load()
+        
+        key = self.__keyFromUrl(url)
+        if not key:
+            zoom = 100
+        
+        if key in self.__zoomDB:
+            zoom = self.__zoomDB[key]
+        else:
+            # default zoom value (i.e. no zoom)
+            zoom = 100
+        
+        return zoom
+    
+    def clear(self):
+        """
+        Public method to clear the saved zoom values.
+        """
+        self.__zoomDB = {}
+        self.__loaded = True
+        
+        self.changed.emit()
+    
+    def removeZoomValue(self, site):
+        """
+        Public method to remove a zoom value entry.
+        
+        @param site web site name
+        @type str
+        """
+        self.load()
+        
+        if site in self.__zoomDB:
+            del self.__zoomDB[site]
+            self.changed.emit()
+    
+    def allSiteNames(self):
+        """
+        Public method to get a list of all site names.
+        
+        @return sorted list of all site names
+        @rtype list of str
+        """
+        self.load()
+        
+        return sorted(self.__zoomDB.keys())
+    
+    def sitesCount(self):
+        """
+        Public method to get the number of available sites.
+        
+        @return number of sites
+        @rtype int
+        """
+        self.load()
+        
+        return len(self.__zoomDB)
+    
+    def siteInfo(self, site):
+        """
+        Public method to get the zoom value for the site.
+        
+        @param site web site name
+        @type str
+        @return zoom value for the site
+        @rtype int
+        """
+        self.load()
+        
+        if site not in self.__zoomDB:
+            return None
+        
+        return self.__zoomDB[site]
+
+
+__ZoomManager = None
+
+
+def instance():
+    """
+    Global function to get a reference to the zoom manager and create it, if
+    it hasn't been yet.
+    
+    @return reference to the zoom manager object
+    @rtype ZoomManager
+    """
+    global __ZoomManager
+    
+    if __ZoomManager is None:
+        __ZoomManager = ZoomManager()
+    
+    return __ZoomManager
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/ZoomManager/ZoomValuesDialog.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,71 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2015 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog to show all saved zoom values.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import QSortFilterProxyModel
+from PyQt5.QtGui import QFont, QFontMetrics
+from PyQt5.QtWidgets import QDialog
+
+from .Ui_ZoomValuesDialog import Ui_ZoomValuesDialog
+
+
+class ZoomValuesDialog(QDialog, Ui_ZoomValuesDialog):
+    """
+    Class implementing a dialog to show all saved zoom values.
+    """
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(ZoomValuesDialog, self).__init__(parent)
+        self.setupUi(self)
+        
+        self.removeButton.clicked.connect(
+            self.zoomValuesTable.removeSelected)
+        self.removeAllButton.clicked.connect(self.zoomValuesTable.removeAll)
+        
+        from . import ZoomManager
+        from .ZoomValuesModel import ZoomValuesModel
+        
+        self.zoomValuesTable.verticalHeader().hide()
+        self.__zoomValuesModel = ZoomValuesModel(
+            ZoomManager.instance(), self)
+        self.__proxyModel = QSortFilterProxyModel(self)
+        self.__proxyModel.setSourceModel(self.__zoomValuesModel)
+        self.searchEdit.textChanged.connect(
+            self.__proxyModel.setFilterFixedString)
+        self.zoomValuesTable.setModel(self.__proxyModel)
+        
+        fm = QFontMetrics(QFont())
+        height = fm.height() + fm.height() // 3
+        self.zoomValuesTable.verticalHeader().setDefaultSectionSize(height)
+        self.zoomValuesTable.verticalHeader().setMinimumSectionSize(-1)
+        
+        self.__calculateHeaderSizes()
+    
+    def __calculateHeaderSizes(self):
+        """
+        Private method to calculate the section sizes of the horizontal header.
+        """
+        fm = QFontMetrics(QFont())
+        for section in range(self.__zoomValuesModel.columnCount()):
+            header = self.zoomValuesTable.horizontalHeader()\
+                .sectionSizeHint(section)
+            if section == 0:
+                header = fm.width("extraveryveryverylongsitename")
+            elif section == 1:
+                header = fm.width("averagelongzoomvalue")
+            buffer = fm.width("mm")
+            header += buffer
+            self.zoomValuesTable.horizontalHeader()\
+                .resizeSection(section, header)
+        self.zoomValuesTable.horizontalHeader().setStretchLastSection(True)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/ZoomManager/ZoomValuesDialog.ui	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,191 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ZoomValuesDialog</class>
+ <widget class="QDialog" name="ZoomValuesDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>500</width>
+    <height>350</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Saved Zoom Values</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_2">
+     <item>
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <layout class="QHBoxLayout" name="horizontalLayout">
+       <property name="spacing">
+        <number>0</number>
+       </property>
+       <item>
+        <widget class="E5ClearableLineEdit" name="searchEdit">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="minimumSize">
+          <size>
+           <width>300</width>
+           <height>0</height>
+          </size>
+         </property>
+         <property name="toolTip">
+          <string>Enter search term</string>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="E5TableView" name="zoomValuesTable">
+     <property name="alternatingRowColors">
+      <bool>true</bool>
+     </property>
+     <property name="selectionBehavior">
+      <enum>QAbstractItemView::SelectRows</enum>
+     </property>
+     <property name="textElideMode">
+      <enum>Qt::ElideMiddle</enum>
+     </property>
+     <property name="showGrid">
+      <bool>false</bool>
+     </property>
+     <property name="sortingEnabled">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_3">
+     <item>
+      <widget class="QPushButton" name="removeButton">
+       <property name="toolTip">
+        <string>Press to remove the selected entries</string>
+       </property>
+       <property name="text">
+        <string>&amp;Remove</string>
+       </property>
+       <property name="autoDefault">
+        <bool>false</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="removeAllButton">
+       <property name="toolTip">
+        <string>Press to remove all entries</string>
+       </property>
+       <property name="text">
+        <string>Remove &amp;All</string>
+       </property>
+       <property name="autoDefault">
+        <bool>false</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="horizontalSpacer_2">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>208</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Close</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>E5ClearableLineEdit</class>
+   <extends>QLineEdit</extends>
+   <header>E5Gui/E5LineEdit.h</header>
+  </customwidget>
+  <customwidget>
+   <class>E5TableView</class>
+   <extends>QTableView</extends>
+   <header>E5Gui/E5TableView.h</header>
+  </customwidget>
+ </customwidgets>
+ <tabstops>
+  <tabstop>searchEdit</tabstop>
+  <tabstop>zoomValuesTable</tabstop>
+  <tabstop>removeButton</tabstop>
+  <tabstop>removeAllButton</tabstop>
+  <tabstop>buttonBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>ZoomValuesDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>237</x>
+     <y>340</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>ZoomValuesDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>325</x>
+     <y>340</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/ZoomManager/ZoomValuesModel.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,129 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2015 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a model for zoom values management.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import Qt, QModelIndex, QAbstractTableModel
+
+
+class ZoomValuesModel(QAbstractTableModel):
+    """
+    Class implementing a model for zoom values management.
+    """
+    def __init__(self, manager, parent=None):
+        """
+        Constructor
+        
+        @param manager reference to the zoom values manager (ZoomManager)
+        @param parent reference to the parent object (QObject)
+        """
+        super(ZoomValuesModel, self).__init__(parent)
+        
+        self.__manager = manager
+        manager.changed.connect(self.__zoomValuesChanged)
+        
+        self.__headers = [
+            self.tr("Website"),
+            self.tr("Zoom Value [%]"),
+        ]
+    
+    def __zoomValuesChanged(self):
+        """
+        Private slot handling a change of the registered zoom values.
+        """
+        self.beginResetModel()
+        self.endResetModel()
+    
+    def removeRows(self, row, count, parent=QModelIndex()):
+        """
+        Public method to remove entries from the model.
+        
+        @param row start row (integer)
+        @param count number of rows to remove (integer)
+        @param parent parent index (QModelIndex)
+        @return flag indicating success (boolean)
+        """
+        if parent.isValid():
+            return False
+        
+        if count <= 0:
+            return False
+        
+        lastRow = row + count - 1
+        
+        self.beginRemoveRows(parent, row, lastRow)
+        
+        siteList = self.__manager.allSiteNames()
+        for index in range(row, lastRow + 1):
+            self.__manager.removeZoomValue(siteList[index])
+        
+        return True
+    
+    def rowCount(self, parent=QModelIndex()):
+        """
+        Public method to get the number of rows of the model.
+        
+        @param parent parent index (QModelIndex)
+        @return number of rows (integer)
+        """
+        if parent.isValid():
+            return 0
+        else:
+            return self.__manager.sitesCount()
+    
+    def columnCount(self, parent=QModelIndex()):
+        """
+        Public method to get the number of columns of the model.
+        
+        @param parent parent index (QModelIndex)
+        @return number of columns (integer)
+        """
+        return len(self.__headers)
+    
+    def data(self, index, role):
+        """
+        Public method to get data from the model.
+        
+        @param index index to get data for (QModelIndex)
+        @param role role of the data to retrieve (integer)
+        @return requested data
+        """
+        if index.row() >= self.__manager.sitesCount() or index.row() < 0:
+            return None
+        
+        site = self.__manager.allSiteNames()[index.row()]
+        siteInfo = self.__manager.siteInfo(site)
+        
+        if siteInfo is None:
+            return None
+        
+        if role == Qt.DisplayRole:
+            if index.column() == 0:
+                return site
+            elif index.column() == 1:
+                return siteInfo
+        
+        return None
+    
+    def headerData(self, section, orientation, role=Qt.DisplayRole):
+        """
+        Public method to get the header data.
+        
+        @param section section number (integer)
+        @param orientation header orientation (Qt.Orientation)
+        @param role data role (integer)
+        @return header data
+        """
+        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
+            try:
+                return self.__headers[section]
+            except IndexError:
+                pass
+        
+        return None
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/ZoomManager/__init__.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2015 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Package implementing a manager for site specific zoom level settings.
+"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/__init__.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,15 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2002 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Package implementing a little web browser.
+
+The web browser is a HTML browser for the display of HTML help files like the
+Qt Online Documentation and for browsing the internet. It may be used as a
+standalone version as well by using the eric6_browser.py script found in the
+main eric6 installation directory.
+
+This package requires at least Qt 5.6.0.
+"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/data/html.qrc	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,8 @@
+<!DOCTYPE RCC>
+<RCC version="1.0">
+<qresource>
+  <file>html/adblockPage.html</file>
+  <file>html/startPage.html</file>
+  <file>html/speeddialPage.html</file>
+</qresource>
+</RCC>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/data/html/adblockPage.html	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"> 
+<meta http-equiv="content-type" content="text/html; charset=utf-8">
+<title>@TITLE@</title>
+<link rel="icon" href="@FAVICON@" type="image/x-icon" />
+<style>
+body {
+  padding: 3em 0em;
+  background: -webkit-gradient(linear, left top, left bottom, from(#85784A), to(#FDFDFD), color-stop(0.5, #FDFDFD));
+  background-repeat: repeat-x;
+}
+#box {
+  background: white;
+  border: 1px solid #85784A;
+  max-width: 600px;
+  height: 50%;
+  padding: 40px;
+  padding-bottom: 10px;
+  margin: auto;
+  border-radius: 0.8em;
+  text-align: center;
+  vertical-align: middle;
+  margin: auto;
+}
+h1 {
+  font-size: 130%;
+  font-weight: bold;
+  border-bottom: 1px solid #85784A;
+  margin-bottom: 0px;
+}
+</style>
+</head>
+<body>
+  <div id="box">
+    <img src="@IMAGE@" width="64" height="64"/>
+    <h1>AdBlock Plus</h1>
+    <p>@MESSAGE@</p>
+  </div>
+</body>
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/data/html/speeddialPage.html	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,636 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=utf-8">
+<title>@SITE-TITLE@</title>
+<link rel="icon" href="@FAVICON@" type="image/x-icon" />
+<style type="text/css" media="screen">
+body {
+    background: -webkit-gradient(linear, left top, left bottom, from(#85784A), to(#FDFDFD), color-stop(0.5, #FDFDFD));
+    background-repeat: repeat-x;
+    font: 13px/22px "Helvetica Neue", Helvetica, Arial, sans-serif;
+    color: #525c66;
+}
+body * {
+    -webkit-user-select: none;
+    font-size: 100%;
+    line-height: 1.6;
+    margin: 0px;
+}
+.add {
+    position: absolute;
+    right:10px;top:10px;
+    width: 24px;
+    height: 24px;
+    background: url(@IMG_PLUS@);
+    cursor: pointer;
+}
+
+
+#quickdial {
+    margin: auto;
+    text-align: center;
+    font-weight: bold;
+}
+#quickdial div.entry {
+    position: relative;
+    float: left;
+    border-width: 10px;
+    -webkit-border-image: url(@BOX-BORDER@) 10;
+    margin: 5px;
+}
+#quickdial img {
+    display: block;
+    margin: auto;
+}
+#quickdial a {
+    position: absolute;
+    left: 0px;
+    top: 0px;
+    width: 100%;
+    height: 87%;
+}
+
+
+div.entry:hover .edit,
+div.entry:hover .close,
+div.entry:hover .reload {
+    display: inline;
+}
+span.boxTitle {
+    width:100%;
+    max-height: 20px;
+    position: absolute;
+    top: 88%;
+    left: 0px;
+    text-align: center;
+    overflow:hidden;
+}
+span.close {
+    width: 14px;
+    height: 14px;
+    position: absolute;
+    left: 92%;
+    top: 90%;
+    background: url(@IMG_CLOSE@) no-repeat;
+    background-position: center;
+    border: 1px solid transparent;
+    display: none;
+}
+span.close:hover {
+    border-color: grey;
+    border-radius: 3px;
+}
+span.edit {
+    width: 14px;
+    height: 14px;
+    position: absolute;
+    left: 0px;
+    top: 90%;
+    background: url(@IMG_EDIT@) no-repeat;
+    background-position: center;
+    border: 1px solid transparent;
+    display: none;
+}
+span.edit:hover {
+    border-color: grey;
+    border-radius: 3px;
+}
+span.reload {
+    width: 16px;
+    height: 16px;
+    position: absolute;
+    left: 92%;
+    top: 0px;
+    background: url(@IMG_RELOAD@) no-repeat;
+    background-position: center;
+    border: 1px solid transparent;
+    display: none;
+}
+span.reload:hover {
+    border-color: grey;
+    border-radius: 4px;
+}
+
+
+#overlay-edit {
+    width: 380px;
+    max-height: 265px;
+    margin-left: auto;
+    margin-right: auto;
+    margin-top: 100px;
+    border-width: 20px;
+    -webkit-border-image: url(@BOX-BORDER@) 25;
+}
+#overlay-edit img {
+    display: block;
+    margin-left: auto;
+    margin-right: auto;
+}
+#overlay-edit .buttonbox input {
+    margin-right: 3px;
+    margin-left: 3px;
+}
+
+
+.formTable {
+    width: 350px;
+    margin-left: auto;
+    margin-right: auto;
+    margin-top: 15px;
+}
+.formTable input[type="text"] {
+    width: 100%;
+    -webkit-user-select: auto;
+}
+
+
+.sett {
+    position: absolute;
+    right:36px;
+    top:10px;
+    width: 24px;
+    height: 24px;
+    background: url(@IMG_SETTINGS@);
+    cursor: pointer;
+}
+#settingsBox {
+    position: absolute;
+    right: 58px;
+    top: 25px;
+    min-width: 250px;
+    width: auto;
+    height: auto;
+    background: #EDECE6;
+    margin: 5px;
+    border-radius: 15px;
+    padding: 8px 15px;
+    border: 1px solid transparent;
+    opacity: 1;
+    z-index: 200;
+}
+#settingsBox .content {
+    float: right;
+    margin-left: 115px;
+}
+#settingsBox p label {
+    margin: 2px;
+    padding: 1px;
+    text-align: center;
+}
+#settingsBox .togop {
+    margin-bottom: 1px;
+    padding-bottom: 2px;
+}
+#settingsBox .button {
+    margin: 2px;
+    padding: 1px;
+    text-align:center;
+    width: 98%;
+}
+#settingsBox .rowsel {
+    margin: 2px;
+    padding: 3px 0;
+    border-bottom: 1px solid #888;
+}
+#settingsBox .rowsel input {
+    text-align: center;
+    width: 80%;
+    height: 12px;
+    margin: 0px;
+    padding-bottom: 0;
+}
+#settingsBox .rowsel span {
+    font-weight: bold;
+    text-align: center;
+    margin: 2px;
+    margin-right: 7px;
+    display: inline-block;
+    width: 25px;
+}
+
+.buttonbox {
+    margin-top: 5px;
+    margin-bottom: -5px;
+    text-align: right;
+}
+</style>
+
+<script type="text/javascript" src="@JQUERY@"></script>
+<script type="text/javascript" src="@JQUERY-UI@"></script>
+<script type="text/javascript">
+    var LOADING_IMAGE = '@LOADING-IMG@';
+    var URL = '@URL@';
+    var TITLE = '@TITLE@';
+    var EDIT = '@APPLY@';
+    var NEW_PAGE = '@NEW-PAGE@';
+    var TITLE_EDIT = '@TITLE-EDIT@';
+    var TITLE_REMOVE = '@TITLE-REMOVE@';
+    var TITLE_RELOAD = '@TITLE-RELOAD@';
+    var TITLE_FETCHTITLE = '@TITLE-FETCHTITLE@';
+    var MAX_PAGES_ROW = @ROW-PAGES@;
+    var DIAL_WIDTH = @SD-SIZE@;
+
+    var editingId = -1;
+    var ignoreNextChanged = false;
+
+    function escapeTitle(title) {
+        title = title.replace(/"/g, '&quot;');
+        title = title.replace(/'/g, '&apos;');
+        return title;
+    }
+
+    function unescapeTitle(title) {
+        title = title.replace(/&quot;/g, '"');
+        title = title.replace(/&apos;/g, '\'');
+        return title;
+    }
+
+    function escapeUrl(url) {
+        url = url.replace(/"/g, '');
+        url = url.replace(/'/g, '');
+        return url;
+    }
+
+    function onRemoveClick(box) {
+        removeBox($(box).index());
+    }
+
+    function onEditKeyPress(e) {
+        if (e.keyCode == 13) {
+            boxEdited();
+            return false;
+        }
+        else if (e.keyCode == 27) {
+            $('#fadeOverlay').click();
+            return false;
+        }
+        return true;
+    }
+
+    function onFetchTitleClick(checkbox) {
+        var displayStyle;
+        checkbox.checked ? displayStyle = 'hidden' : displayStyle = 'visible';
+        $('#titleLine').css({'visibility' : displayStyle });
+    }
+
+    function hideEditBox() {
+        $('#fadeOverlay').fadeOut("slow", function() {$("#fadeOverlay").remove();});
+    }
+
+    function emitChanged(pages)
+    {
+        ignoreNextChanged = true;
+        external.speedDial.changed(pages);
+    }
+
+    function addSpeedDial()
+    {
+        onEditClick(addBox('', NEW_PAGE, ''));
+        alignPage();
+    }
+    
+    function onEditClick(box) {
+        editingId = $(box).index();
+        var boxUrl = $(box).children('a').first().attr('href');
+        var boxTitle = escapeTitle($(box).children('span').first().text());
+        if (boxUrl === '')
+            boxUrl = 'http://';
+
+        $('body').append('<div id="fadeOverlay" style="opacity:0.9;display:none;position:fixed;left:0;' +
+                         'top:0;width:100%;height:100%;z-index:9999;background:#85784A;">' +
+                         '<div id="overlay-edit" onkeypress="return onEditKeyPress(event)">' +
+                         '<img src="' + $(box).children('img').first().attr('src') + '"> ' +
+                         '<table class="formTable"><tr><td>' + URL + ': </td><td>' +
+                         '<input type="text" id="formUrl" value="' + boxUrl + '"></td></tr>' +
+                         '<tr id="titleLine"><td>' + TITLE + ': </td><td>' +
+                         '<input type="text" id="formTitle" value="' + boxTitle + '"></td></tr>' +
+                         '<tr><td></td><td><input type="checkbox" id="fetchTitle" onclick="onFetchTitleClick(this)">' +
+                         '<label for="fetchTitle">  ' + TITLE_FETCHTITLE + ' </label></td></tr>' +
+                         '</table><p class="buttonbox">' +
+                         '<input type="button" value=" @CLOSE@ " onClick="hideEditBox();">' +
+                         '<input type="button" value="   ' + EDIT + '   " onClick="boxEdited()"></p>' +
+                         '</div></div>');
+
+        $('#fadeOverlay').css({'filter' : 'alpha(opacity=90)'}).fadeIn();
+        $('#fadeOverlay').click(function() {hideEditBox()});
+        $('#overlay-edit').click(function(event) { event.stopPropagation(); });
+        $('#formUrl').focus();
+    }
+
+    function onReloadClick(box) {
+        var url = $(box).children('a').first().attr('href');
+        var img = $(box).children('img').first();
+
+        if (url === '')
+            return;
+
+        $(img).attr('src', LOADING_IMAGE);
+        external.speedDial.loadThumbnail(url, false);
+    }
+
+    function boxEdited() {
+        if (editingId == -1)
+            return;
+
+        external.speedDial.urlFromUserInput($('#formUrl').attr("value"), function(newUrl) {
+            var box = document.getElementById('quickdial').getElementsByTagName('div')[editingId];
+            var a = box.getElementsByTagName('a')[0];
+            var originalUrl = a.getAttribute('href');
+            setBoxUrl(editingId, newUrl);
+            setBoxTitle(editingId, $('#formTitle').attr("value"));
+            var changedUrl = a.getAttribute('href');
+            var fetchTitleChecked = document.getElementById('fetchTitle').checked;
+            
+            var pages = allPages()
+            
+            if (fetchTitleChecked || (originalUrl != changedUrl && changedUrl !== '') ) {
+                var img = box.getElementsByTagName('img')[0];
+                img.setAttribute('src', LOADING_IMAGE);
+
+                $('#fadeOverlay').fadeOut("slow", function() {
+                    $("#fadeOverlay").remove();
+                });
+                    external.speedDial.loadThumbnail(a.getAttribute('href'), fetchTitleChecked);
+                    external.speedDial.removeImageForUrl(a.getAttribute('href'));
+            } else {
+                hideEditBox();
+            }
+            emitChanged(pages);
+        });
+    }
+
+    function allPages() {
+        var urls = $('a[class="boxUrl"]');
+        var titles = $('span[class="boxTitle"]');
+        var value = "";
+        $('div.entry').each(function(i) {
+            var url = $(this).children('a').first().attr('href');
+            var title = $(this).children('span[class="boxTitle"]').first().text();
+            var img = $(this).children('img').first().attr('src');
+            value += 'url:"' + escapeUrl(url) + '"|title:"' + escapeTitle(title) + '"|img:"' + escapeUrl(img) + '";';
+        });
+
+        return value;
+    }
+
+    function addBox(url, title, img_source) {
+        var div = document.createElement('div');
+        div.setAttribute('class', 'entry');
+        var img = document.createElement('img');
+        img.setAttribute('src', img_source);
+        var a = document.createElement('a');
+        a.setAttribute('href', url);
+        a.setAttribute('class', 'boxUrl');
+        var span1 = document.createElement('span');
+        span1.setAttribute('class', 'boxTitle');
+        span1.innerText = unescapeTitle(title);
+        var span2 = document.createElement('span');
+        span2.setAttribute('class', 'edit');
+        span2.setAttribute('onClick', 'onEditClick(parentNode)');
+        span2.setAttribute('title', TITLE_EDIT);
+        var span3 = document.createElement('span');
+        span3.setAttribute('class', 'close');
+        span3.setAttribute('onClick', 'onRemoveClick(parentNode)');
+        span3.setAttribute('title', TITLE_REMOVE);
+        var span4 = document.createElement('span');
+        span4.setAttribute('class', 'reload');
+        span4.setAttribute('onClick', 'onReloadClick(parentNode)');
+        span4.setAttribute('title', TITLE_RELOAD);
+
+        div.appendChild(img);
+        div.appendChild(img);
+        div.appendChild(a);
+        div.appendChild(span1);
+        div.appendChild(span2);
+        div.appendChild(span3);
+        div.appendChild(span4);
+
+        document.getElementById("quickdial").appendChild(div);
+
+        if (img_source == LOADING_IMAGE) {
+            external.speedDial.loadThumbnail(url, false);
+        }
+
+        return div;
+    }
+
+    function setBoxImage(id, img_source) {
+        var box = document.getElementById('quickdial').getElementsByTagName('div')[id];
+        if (box === undefined)
+            return;
+
+        var img = box.getElementsByTagName('img')[0];
+        img.setAttribute('src', img_source + '?' + new Date());
+    }
+
+    function setTitleToUrl(url, title) {
+        var changed = false;
+        var boxes = document.getElementById('quickdial').getElementsByTagName('div');
+        for (i = 0; i < boxes.length; ++i) {
+            var box = boxes[i];
+
+            if (box === undefined)
+                continue;
+
+            var boxUrl = box.getElementsByTagName('a')[0].getAttribute('href');
+            if (url != boxUrl)
+                continue;
+
+            var span = box.getElementsByTagName('span')[0];
+            if (span.innerText != title) {
+                changed = true;
+                span.innerText = title;
+            }
+        }
+
+        emitChanged(allPages());
+    }
+
+    function setImageToUrl(url, img_source) {
+        var aElement = $('a[href="' + url + '"]');
+        $(aElement).each(function() {
+            var box = $(this).parent();
+            var imgElement = $(box).children("img").first();
+            if ($(imgElement).size() == 0)
+                return;
+
+            $(imgElement).attr('src', img_source);
+        });
+    }
+
+    function setBoxUrl(id, url) {
+        var box = document.getElementById('quickdial').getElementsByTagName('div')[id];
+        if (box === undefined)
+            return;
+
+        var a = box.getElementsByTagName('a')[0];
+        a.setAttribute('href', url);
+    }
+
+    function setBoxTitle(id, title) {
+        var box = document.getElementById('quickdial').getElementsByTagName('div')[id];
+        if (box === undefined)
+            return;
+
+        var span = box.getElementsByTagName('span')[0];
+        span.innerText = title;
+    }
+
+    function removeBox(id) {
+        if (confirm("@TITLE-WARN@"))
+        var box = document.getElementById('quickdial').getElementsByTagName('div')[id];
+        if (box === undefined)
+            return;
+
+        var url = box.getElementsByTagName('a')[0].getAttribute('href');
+        document.getElementById("quickdial").removeChild(box);
+        alignPage();
+
+        external.speedDial.removeImageForUrl(url);
+        emitChanged(allPages());
+    }
+
+    function alignPage() {
+        $('head').append('<style>#quickdial img[src*=".png"]{height:auto;width:'+DIAL_WIDTH+'px}</style>');
+        $('#quickdial div.entry').css({'width' : DIAL_WIDTH + 'px',
+                                       'height' : Math.round(DIAL_WIDTH / 1.54) + 'px'});
+
+        var width = $(window).width();
+        var height = $(window).height();
+        var boxWidth = Math.floor(DIAL_WIDTH + 30);
+        var boxHeight = Math.floor(Math.round(DIAL_WIDTH / 1.54) + 40);
+
+        var maxBoxes = Math.floor(width / boxWidth);
+        if (maxBoxes > MAX_PAGES_ROW) maxBoxes = MAX_PAGES_ROW;
+        if (maxBoxes < 1) maxBoxes = 1;
+
+        var maxwidth = maxBoxes * boxWidth;
+        $("#quickdial").css('width', maxwidth + 'px');
+
+        var boxesCount = $("#quickdial").children("div").size();
+        var rows = Math.ceil(boxesCount / maxBoxes);
+        var margintop = (height - rows * boxHeight) / 2;
+
+        if (margintop < 0) margintop = 0;
+
+        $("#quickdial").css('margin-top', margintop + 'px');
+    }
+
+
+    function saveSettings() {
+        MAX_PAGES_ROW = $('#PgInRow').val();
+        DIAL_WIDTH = parseInt($('#SdSize').val());
+
+        external.speedDial.setPagesInRow(MAX_PAGES_ROW);
+        external.speedDial.setSdSize(DIAL_WIDTH);
+
+        alignPage();
+    }
+
+
+    function sdSizeToggle() {
+        var check = document.getElementById('SdSizeToggle');
+        var SdSize = document.getElementById('SdSize');
+        var SdSizeSl = document.getElementById('sliderValueSd');
+        
+        SdSize.disabled = (check.checked ? false : true);
+        SdSize.value = (check.checked ? SdSize.value : 231);
+        SdSizeSl.innerHTML = (check.checked ? DIAL_WIDTH : 231);
+    }
+
+    function configureSpeedDial()
+    {
+        // Load settings
+        $('#PgInRow').val(MAX_PAGES_ROW);
+        $('#sliderValuePg').html(MAX_PAGES_ROW);
+        $('#SdSize').val(DIAL_WIDTH);
+        $('#SdSizeToggle').prop('checked', DIAL_WIDTH != 231);
+        $('#sliderValueSd').html(DIAL_WIDTH);
+        $('#SdSizeToggle').is(':checked') ? $('#SdSize').removeAttr('disabled') : $('#SdSize').attr('disabled', 'disabled');
+
+        // Show dialog
+        $('#fadeOverlay2').css({'filter' : 'alpha(opacity=100)'}).fadeIn();
+        $('#fadeOverlay2').click(function() { $(this).fadeOut('slow'); });
+        $('#settingsBox').click(function(event) { event.stopPropagation(); });
+    }
+    
+    function reloadAll() {
+        if (confirm("@TITLE-WARN-REL@"))
+            $('div.entry').each(function(i) {
+                onReloadClick($(this));
+            });
+    }
+
+</script>
+</head>
+
+<body>
+    <div id="quickdial"></div>
+    <a onClick="configureSpeedDial();" title="@SETTINGS-TITLE@" class="sett"></a>
+    <a onClick="addSpeedDial();" title="@ADD-TITLE@" class="add"></a>
+
+    <script type="text/javascript">
+    function init()
+    {
+        @INITIAL-SCRIPT@
+
+        external.speedDial.pagesChanged.connect(function() {
+            if (ignoreNextChanged) {
+                ignoreNextChanged = false;
+                return;
+            }
+            window.location.reload();
+        });
+
+        external.speedDial.thumbnailLoaded.connect(setImageToUrl);
+        external.speedDial.pageTitleLoaded.connect(setTitleToUrl);
+
+        alignPage();
+        $(window).resize(function() { alignPage(); });
+        $("div").disableSelection();
+        $("#quickdial").sortable({
+            revert: true,
+            cursor: 'move',
+            containment: 'document',
+            opacity: 0.8,
+            distance: 40,
+            update: function(event, ui) {
+                emitChanged(allPages());
+            }
+        });
+    }
+        
+    // Initialize
+    if (window.external) {
+        init();
+    } else {
+        document.addEventListener('_eric_external_created', init);
+    }
+    </script>
+    <div id="fadeOverlay2" style="opacity:0.9;display:none;position:fixed;left:0;top:0;width:100%;height:100%;z-index:100;background:#85784A;">
+      <div id="settingsBox">
+        <div class="togop">
+          <label for="PgInRow">@TXT_NRROWS@</label>
+        </div>
+        <div class="rowsel">
+          <span id="sliderValuePg">@ROW-PAGES@</span>
+          <input id="PgInRow" type="range" min="2" max="8" value="@ROW-PAGES@" step="1" onchange="$('#sliderValuePg').html(this.value);" />
+        </div>
+        <div class="togop">
+          <input type="checkbox" name="sdsizet" id="SdSizeToggle" onchange="sdSizeToggle()" />&nbsp;<label for="SdSizeToggle">@TXT_SDSIZE@</label>
+        </div>
+        <div class="rowsel">
+          <span id="sliderValueSd">@SD-SIZE@</span>
+          <input id="SdSize" type="range" min="145" max="360" value="@SD-SIZE@" step="1" onchange="$('#sliderValueSd').html(this.value);" />
+        </div>
+        <div class="content">
+          <p class="buttonbox">
+            <input type="button" value=" @CLOSE@ " onClick="$('#fadeOverlay2').fadeOut('slow');" />
+            <input type="button" value="   @APPLY@   " onClick="saveSettings();$('#fadeOverlay2').fadeOut('slow');"/>
+          </p>
+        </div>
+      </div>
+    </div>
+</body>
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/data/html/startPage.html	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,138 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"> 
+<meta http-equiv="content-type" content="text/html; charset=utf-8">
+<title>@TITLE@</title>
+<link rel="icon" href="@FAVICON@" type="image/x-icon" />
+<style>
+* {
+    margin: 0;
+    padding: 0;
+    font-family: "DejaVu Sans";
+}
+
+body {
+    background: -webkit-gradient(linear, left top, left bottom, from(#85784A), to(#FDFDFD), color-stop(0.5, #FDFDFD));
+    background-repeat: repeat-x;
+    margin-top: 200px;
+}
+
+#header, #search, #footer {
+    width: 600px;
+    margin: 10px auto;
+}
+
+#header, #search {
+    border-radius: 0.8em;
+    padding: 25px;
+}
+
+#header {
+    background: -webkit-gradient(linear, left top, left bottom, from(#D57E3E), to(#D57E3E), color-stop(0.5, #FFBA89));
+    height: 25px;
+}
+
+#header h1 {
+    display: inline;
+    font-size: 1.7em;
+    font-weight: bold;
+}
+
+#header img {
+    display: inline;
+    float: right;
+    margin-top: -5px;
+}
+
+#search {
+    background: -webkit-gradient(linear, left top, right top, from(#85784A), to(#85784A), color-stop(0.5, #C8C2AE));
+    height: 50px;
+    color: #000;
+    text-align: center;
+    padding-top: 40px !important;
+}
+
+#search fieldset {
+    border: 0;
+}
+
+#search input[type=text] {
+    width: 65%;
+}
+
+#search input[type=submit] {
+    width: 25%;
+}
+
+#footer {
+    text-align: center;
+    color: #999;
+}
+
+#footer a {
+    color: #555;
+    text-decoration: none;
+}
+
+#footer a:hover {
+    text-decoration: underline;
+}
+    </style>
+    <script type="text/javascript">
+        function update()
+        {
+            external.startPage.providerString(function(provider) {
+                document.getElementById('footer').innerHTML = 
+                    provider
+                    + ' | <a href="http://eric-ide.python-projects.org/">'
+                    + '@ERIC_LINK@</a>';
+                document.getElementById('lineEdit').placeholder = provider;
+            });
+
+            // Try to change the direction of the page:
+
+            var newDir = '@QT_LAYOUT_DIRECTION@';
+            newDir = newDir.toLowerCase();
+            if ((newDir != 'ltr') && (newDir != 'rtl'))
+                newDir = 'ltr';
+            document.getElementsByTagName('body')[0].setAttribute(
+                'dir', newDir);
+        }
+
+        function formSubmitted()
+        {
+            var string = lineEdit.value;
+
+            if (string.length == 0)
+                return;
+
+            external.startPage.searchUrl(string, function(url) {
+                window.location.href = url;
+            });
+        }
+        
+        // Initialize
+        if (window.external) {
+            update();
+        } else {
+            document.addEventListener('_eric_external_created', update);
+        }
+    </script>
+</head>
+<body onload="document.forms[0].lineEdit.select();">
+    <div id="header">
+        <h1>@HEADER_TITLE@</h1>
+        <img src="@IMAGE@" width="32" height="32"/>
+    </div>
+    <div id="search">
+        <form action="javascript:formSubmitted();">
+            <fieldset>
+                <input id="lineEdit" name="lineEdit" type="text" />
+                <input type="submit" value="@SUBMIT@"/>
+            </fieldset>
+        </form>
+    </div>
+    <div id="footer"></div>
+</body>
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/data/html_rc.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,609 @@
+# -*- coding: utf-8 -*-
+
+# Resource object code
+#
+# Created by: The Resource Compiler for PyQt5 (Qt v5.6.0)
+#
+# WARNING! All changes made in this file will be lost!
+
+from PyQt5 import QtCore
+
+qt_resource_data = b"\
+\x00\x00\x12\x96\
+\x00\
+\x00\x4a\x63\x78\x9c\xcd\x3c\x6b\x77\xdb\x36\x96\xdf\xf3\x2b\x18\
+\xba\x29\xa9\x46\x6f\xdb\xa9\x63\x59\x1e\x39\xb6\xd2\x68\xd7\xb1\
+\xbd\x96\xd2\xb4\x9b\xcd\xf1\xa1\x49\x58\xe2\x86\x22\x39\x24\xe5\
+\xc7\xa4\xfe\xef\x7b\x2f\x00\x92\x00\xf8\x90\x9c\x64\xe7\x54\x73\
+\x26\x96\x08\xdc\x8b\xfb\x7e\x00\x60\x0f\x9e\x9f\x9c\x1f\xcf\xfe\
+\xbc\x18\x6b\x8b\x64\xe9\x1d\x3e\x3b\x48\xff\x10\xcb\x81\x3f\x4b\
+\x92\x58\x30\x92\x84\x2d\xf2\xcf\x95\x7b\x3b\xd4\xed\xc0\x4f\x88\
+\x9f\xb4\x92\x87\x90\xe8\x1a\xff\x35\xd4\x13\x72\x9f\x74\x10\x74\
+\xa0\xd9\x0b\x2b\x8a\x49\x32\x5c\x25\x37\xad\x3d\x1d\x70\x24\x6e\
+\xe2\x91\xc3\xd1\x74\x32\x1b\xb7\x66\x93\xd9\xe9\x78\x74\xd0\x61\
+\xcf\x9e\x1d\x78\xae\xff\x45\x8b\x88\x37\xd4\x5d\xc0\xa5\x6b\x8b\
+\x88\xdc\x0c\xf5\xd1\xdb\xa3\xdf\x27\xc7\xe7\x67\x23\x5d\xc3\x75\
+\x60\x70\x69\xcd\x49\xe7\xbe\xc5\x26\x75\x00\x30\x4e\x1e\x3c\xc2\
+\x47\xe9\xe2\x76\x1c\xeb\xda\x92\x38\xae\x35\xd4\x63\x3b\x22\xc4\
+\x87\xb5\xaf\x03\xe7\x41\xfb\xfa\x4c\x83\xcf\xb5\x65\x7f\x99\x47\
+\xc1\xca\x77\xf6\xb5\xd6\x1d\xb9\xfe\xe2\x26\xad\x79\x64\x39\x2e\
+\xd0\x6f\x02\x19\xc4\x8a\x9a\x9a\x47\x6e\x12\x2d\x09\x42\xfe\xed\
+\x3a\x48\x92\x60\xd9\xd4\x6e\xa2\x60\x69\x6e\xed\xed\xfe\xba\xb7\
+\x73\xd4\x68\xc2\x04\x73\xeb\xed\x09\xfe\x0f\x7e\xd8\x81\x17\x44\
+\xad\x18\x80\xcc\x6e\x7b\xb7\xa9\xa5\x23\x8d\x81\xb2\x6c\x2b\x22\
+\x21\xb1\x92\x7d\x8d\xfd\x6d\xdd\xb3\x09\x37\x20\xc2\x7d\xad\xb7\
+\x1d\xde\x77\xfa\xfd\xf0\x5e\xd3\xdf\x11\xef\x96\x24\xae\x6d\x69\
+\x67\x64\x45\xf4\xa6\x96\x3d\x68\x6a\x47\x91\x6b\x79\x4d\x2d\xb6\
+\xfc\xb8\x15\x93\xc8\xbd\x61\x38\x28\x0d\xfb\xda\xd6\x6e\x7f\xd7\
+\x7e\xf5\x6a\xf0\xec\x91\x31\xfe\x0b\x67\x3d\x65\x77\x05\x20\x00\
+\xe6\x11\x1b\x56\xf4\x03\x9f\xe4\x14\xb4\x62\xf7\x5f\x04\xc8\xe8\
+\x76\x5f\xb0\x87\x28\x91\xd6\x82\xb8\xf3\x05\x52\xd7\x7e\xc5\x9e\
+\x2e\xad\x68\xee\xfa\xfb\x5a\x37\xbc\xc7\x55\xda\x96\xe3\xf0\x35\
+\xc2\x20\x76\x13\x37\x80\x31\xeb\x3a\x0e\xbc\x55\xc2\x91\x47\x14\
+\x43\x0f\x01\x40\x44\xec\x0b\x1d\xb8\x73\x9d\x64\xb1\xaf\xf5\x77\
+\xd2\x07\xe9\x62\xf9\x13\x51\x65\xab\xc8\x33\x47\x93\xf7\xbf\x5d\
+\x5d\x9c\x7e\x98\x8e\xb8\x70\xed\x55\x14\x23\xe3\x61\xe0\x82\x1d\
+\x46\x48\xd2\xb3\x67\x5b\x60\xa8\xf6\x17\xb0\x03\x8f\x93\x96\x12\
+\x6d\xad\x92\x80\xc1\xa1\xc1\xb4\x2c\xcf\x9d\xc3\x53\x9b\x30\xd0\
+\x4c\x12\x77\x9c\x8e\xeb\xc0\x73\x10\xa3\x80\xcf\x71\x6f\xdb\x30\
+\x3d\x7a\x28\x30\x0d\x26\x6c\x25\xee\x6d\x2a\x51\x2f\x40\x45\xa3\
+\x0d\x71\x4e\x82\xc8\x01\xd9\x73\x9e\x73\x21\xa4\x9a\xe1\xe3\xd4\
+\xcc\x39\xaf\x6f\xce\xff\x68\xbd\x39\xbf\x3c\x19\x5f\x8e\x1a\x00\
+\x21\xcb\x7f\x97\xc9\x5f\x20\xcd\x5d\xce\x39\x51\x8e\x1b\x87\x9e\
+\xf5\x00\x0c\x78\x81\xfd\x65\x50\x22\x02\x09\xd0\x5a\xa3\x40\x64\
+\x82\x2b\x9c\x8a\x0e\x94\xa8\xa9\x4a\xcc\xcd\x26\x55\xe2\xde\xaf\
+\x2f\x98\x36\x32\x91\xed\x2f\x82\x5b\x12\x69\x6d\xf0\xd0\xa4\x59\
+\x7c\x6c\x7b\x41\x4c\x4a\x9e\x83\x60\x03\xcb\x51\x59\x73\x7d\xb4\
+\x4f\x5c\x21\x0e\x2d\xbf\x7d\x1d\xdc\xcf\x30\xa0\xf0\x69\x8c\xaa\
+\x9c\xa8\xa5\x75\x9f\x99\x72\x3f\xa3\xbd\x8a\x61\xca\xe1\xde\xde\
+\x8b\x72\xf6\x2b\x2c\x07\x89\x05\xad\xdf\xed\x2f\x5c\xc7\x21\x7e\
+\x46\x19\x65\x4b\x22\x4b\xeb\x15\x2c\x3e\x7f\x52\xaf\x84\xd7\xfd\
+\x17\x02\x89\xaf\x53\xf6\x4a\xdd\xe4\xf8\xf4\x7c\x3a\x06\xcb\xf1\
+\x03\x1e\x77\x0a\xe1\x28\x5f\x4b\x64\x84\x59\x22\xd0\x04\xa1\x08\
+\x28\x70\x1d\x2d\x89\x20\xda\x84\x56\x04\x93\x06\xb2\x16\x58\x08\
+\x11\x39\xe5\x5a\xfb\x2a\x1a\x3d\x0f\x4e\xf3\x88\x3c\x48\xce\x80\
+\xb1\x77\x15\xef\x6b\xdb\xcc\x96\x29\x12\x34\x8e\x1f\x25\x2d\xd9\
+\x64\xeb\xa5\x35\x3e\x99\xcc\xfe\x9d\xc2\x42\x3e\xbf\x57\x56\x92\
+\x67\xa4\xd2\x7a\x55\x90\xd6\xab\x6f\xb1\xad\x6e\x6d\x08\xbe\x1c\
+\x9f\x9e\x1f\x9d\xfc\x3b\xe5\xc5\x78\xfd\x16\x89\xed\x30\x89\x41\
+\x5a\x40\x58\xc0\xdc\x2a\xda\xd8\xf6\x5e\xc6\xaf\x14\x2a\x5e\xed\
+\xe6\x8f\x31\x7e\xb6\x98\xa8\xf2\x3c\xc2\x1f\xb3\x1c\x57\x7c\x4e\
+\x45\x09\x61\x28\x13\xa6\x94\x05\xfa\x4f\xce\x02\xfd\x5d\x1a\xbb\
+\x25\x46\x36\x89\xfb\x1b\xd1\xad\x22\x6e\x5f\xaf\xa0\xf8\xf1\x21\
+\xb4\x42\xb4\x0d\x57\x89\x94\x4c\x53\xd0\xed\x52\x01\x6d\xa7\x32\
+\x6f\xdf\x04\xd1\x72\x66\x5d\x2b\x91\x59\xdb\xde\xed\xfe\x08\xc9\
+\xf2\x2c\x28\xac\x42\x29\xfd\x94\x57\x85\xfa\x67\xc5\x3f\xb2\x9c\
+\x50\x5a\x13\xa5\x92\x00\xca\xa1\x82\x4d\x36\x2a\x6d\xb6\x5f\x89\
+\x71\xe6\xfb\xeb\x9b\xe9\x78\x36\x9b\x9c\xfd\x56\x57\xe3\x6c\x21\
+\x71\xae\x3f\x8f\xdf\x80\x72\x36\xa1\x51\xdb\xdd\x93\x82\x61\x3f\
+\x37\x6c\x10\x66\x4a\xe9\xae\x4a\x7b\x2e\xf6\x94\xf6\xfc\x89\x48\
+\xfb\xd6\xf8\x64\x7c\x3c\x56\x4a\xc4\x5d\xc5\xea\x53\x87\xec\x65\
+\x03\x21\x54\x8f\xc0\x05\x24\x5b\x08\x07\x3d\x65\x7e\x6d\x90\x08\
+\x42\xcb\x76\x13\x30\xf5\x1e\xfb\xfd\xaf\x96\xeb\x3b\xe4\x1e\x7d\
+\xaa\x5b\x10\x50\x9b\x37\x29\x5c\x52\xbc\x36\xa3\x82\x29\x31\xc1\
+\x5e\x6a\x55\x12\x8e\x50\xf3\xac\x6b\xa2\x16\x94\xfd\x02\x27\xbd\
+\xba\x3a\x41\xa5\x2b\x09\xe6\x41\x28\xbb\x15\x6b\x38\x04\x3c\x1c\
+\x73\x36\xd0\x2f\x21\x8e\x7b\xea\x37\x11\x27\x46\x67\xae\xf5\xd7\
+\x7b\x2f\x8a\x4b\x44\xc1\x5d\xbc\x01\xff\xe0\xf9\x5a\x57\x52\xbb\
+\xc0\x11\xd7\xe6\xd6\xde\xde\x5e\xe5\x02\x62\xa8\xa9\xaa\xb5\x38\
+\x9d\x7b\x6a\xcd\xd9\xeb\xcb\x41\x45\x48\x63\xaa\x18\x4b\xac\x84\
+\x13\x80\xe9\x26\xb5\x94\x62\x37\x50\x47\x56\x41\x2a\x72\x0c\xfb\
+\x35\x7d\xac\x14\xb1\x2d\x21\x5c\x67\x9e\xc8\x03\xa8\x10\x83\x25\
+\x33\xa1\x4e\xac\x26\xa7\x94\xb7\xd6\x6e\x99\x11\x72\x7b\x7f\x7c\
+\x76\xd0\xa1\xad\xf3\xe1\x33\xe8\xa1\xed\xc8\x0d\x13\xb1\x89\xfe\
+\x5f\xeb\xd6\x62\x4f\x75\x2d\x8e\x6c\xe8\xc4\xff\xe3\xbf\x3e\x8c\
+\x2f\xff\x1c\xe9\x87\x00\x47\x07\x0e\x9f\x02\xd7\xfa\x30\x79\x02\
+\xe8\x21\xa5\xfa\xd6\x8a\x34\xac\x2f\x20\x08\x5e\x4d\xde\x1f\xfd\
+\x36\xd6\x86\x9a\x31\xe2\x4f\x5a\x10\x22\x47\xc6\x20\x9b\xf8\xe1\
+\xf2\x94\x0e\xc3\x5f\xf1\x31\xdd\x66\xa0\x03\x6c\xc3\x41\x18\xc2\
+\x52\x8f\x8e\x1c\x5d\x5c\x9c\xfe\x29\x8e\x9c\x8d\x3f\x5e\x5d\xa4\
+\xeb\xc1\x8f\x16\xfe\x28\x60\xbd\xca\x10\xd0\x9f\x2d\x5a\x3a\x16\
+\x26\x5d\x8e\xdf\x9f\xff\x2e\x50\xd0\x62\x0f\xca\x26\x22\x67\xd2\
+\x44\x5a\x5c\x15\x26\xbe\x1d\xcf\x8e\xdf\x29\x7c\xb5\xf2\x87\x22\
+\xc0\xfb\xa3\x3f\x28\x23\xd3\xab\xcb\xf3\x8f\x30\x7b\x04\x7f\x28\
+\x33\xd3\x51\x3e\xe9\x64\x72\x74\x7a\xf5\x71\x72\x32\x7b\x87\x33\
+\xa6\x27\xad\xe9\xe4\xbf\xc7\x30\x9e\x4d\xc0\x2a\x00\xbc\x63\xe2\
+\xc0\x78\xab\x97\x03\x82\x39\x05\x11\x39\x03\xdd\x1d\x2f\x2c\x7f\
+\x4e\x70\xfc\xc6\xf2\x62\xc2\x61\x6f\x56\xbe\x8d\xa9\x48\x23\xb1\
+\x6d\x85\x84\x36\x66\x26\xdd\xef\x69\x70\x2b\xa6\xb6\x49\xfb\xb5\
+\x21\xfb\x0b\xf5\x1d\xb8\x84\x4d\xcc\x8e\xde\x99\x37\x35\xe3\xe7\
+\x7f\xae\x82\x64\x60\xf0\x0c\x58\x33\xdd\x60\xd3\x2d\x48\x7f\xd2\
+\xf4\x88\x24\xab\xc8\x67\xb3\xd9\xd3\x47\x85\xb8\x95\xff\x2d\xe4\
+\x31\xc2\xe8\xa2\xfa\x06\xe4\x31\xc2\xe8\xf4\xff\x31\x9e\x46\x1f\
+\xa3\xee\x03\x54\x05\x50\x19\x88\x94\xc1\x4f\x58\x08\xfe\x55\x85\
+\x26\xe2\x2f\x99\x64\x14\x26\x71\x22\x60\x56\x39\x09\x81\x7f\x49\
+\x96\x50\x13\x1e\x7b\xae\xfd\xc5\x84\x20\x24\x92\x11\xd1\x21\x88\
+\x9c\xe6\x4f\x74\xa8\x4d\x33\xb0\x99\x6e\x7b\x15\x71\x8d\xc1\x9c\
+\xfe\x93\x3c\x5c\x44\x24\x8e\x4d\x49\xd6\xee\x8d\x66\x92\xf6\x17\
+\xf2\x70\x1c\x38\x20\xc4\xa1\xd6\xdb\x16\x87\xf1\x03\x2b\x20\x3c\
+\x71\x4c\x81\x7e\x81\x07\x6e\x7f\xe9\xd3\xc7\xec\x1b\x81\xe7\xc5\
+\x05\xfa\xbf\xaa\x0b\xfc\x64\x1a\x5b\x37\x96\x43\xce\x59\x09\x6c\
+\x34\xa0\x9f\x45\xae\x9f\xb8\x5c\xaa\xd7\x68\x55\xa1\xd6\xc0\x7f\
+\x4b\x12\x7b\x41\xad\x8e\xc9\xd5\x5e\x10\xfb\x8b\x22\x5c\x74\x33\
+\x9e\x26\xa6\x18\xac\xf3\xa5\xd2\xd9\x6d\xfa\x05\x7c\xef\x1f\xd2\
+\x44\x0c\x0d\x6c\x07\xc2\xd0\xf6\x0b\x23\xb7\x6e\xec\x42\x95\x6c\
+\xe4\xe8\x90\x6d\x6a\x84\xa7\x90\x89\x90\x69\xd0\xcd\x57\x36\xcf\
+\xf5\xa0\xc2\x2a\x60\x79\xac\xd0\x2f\x2c\x4a\x50\x43\x68\x10\x22\
+\x27\x45\xb9\xd2\x5f\xab\xc4\xd4\x63\x2f\xb8\xd3\x9b\x19\x0a\x04\
+\xfb\xc9\xd4\xc5\xd9\x7a\xa3\xcd\xcc\x0c\xd4\x50\xb5\x30\x59\xba\
+\x69\x1c\x32\x43\xe8\x9b\xe2\x06\x1d\x17\xac\xab\x24\x5c\xe5\xfa\
+\xa1\x36\x72\x0f\x39\xdc\xb7\xbc\x76\x1c\x12\xe2\x9c\xb8\xf0\xcd\
+\x96\x30\x96\xaf\x0c\x25\xc5\x34\x05\x30\xd5\x55\x99\xbd\x33\x0d\
+\xc3\x44\x14\x8b\x61\x34\xb3\x14\x43\x3d\x51\xb0\x2d\x9a\xa4\x2f\
+\x60\x31\x33\x5b\x0c\xff\x2d\xf3\xa1\x52\x6f\x14\x63\xb5\xec\x8f\
+\x03\xc9\xa8\x60\xe4\x03\x0d\x0d\x7c\x92\xbd\x70\x3d\x07\xea\x6a\
+\xd3\xb0\x50\x35\x6e\x14\x27\x66\xa3\x6d\x25\x49\x64\x1a\xb8\x15\
+\x6f\x14\xe1\x67\x3c\xd4\x89\xe1\xb3\x80\x0d\xab\x28\x01\x21\x26\
+\x7a\x53\xe4\x17\x3d\x32\xa5\x05\xfc\x11\x84\xa1\xba\x3b\x23\xd3\
+\xc0\xa3\x87\xfd\x4e\xc7\xe0\xb9\x85\x9b\x14\xee\x6e\x03\x7a\x2b\
+\x0c\x89\xef\x98\xc6\x81\xe3\xde\x6a\xae\x33\xd4\x45\xdb\xd1\x68\
+\x99\x33\xd4\xd3\x5e\xa1\xdb\x7e\x3d\x48\x4b\x2f\xba\xb7\x90\xb5\
+\x4c\x37\xee\x3d\x71\x06\xb4\xfc\xef\x0e\x0c\xed\xa5\x44\x8a\xf4\
+\x31\xb0\xf0\xea\x0e\x84\xfd\x45\x5e\x7b\xd2\xef\x69\x17\xf2\x1a\
+\x3e\x03\xa1\x3f\xe2\x07\x07\x03\xfd\xb0\x1e\x79\xc6\x86\xd8\x87\
+\xeb\xa0\x76\x88\x5c\x21\x86\xcd\xa1\xce\xe3\x8b\x1a\x4d\x6f\xa1\
+\x0c\x6d\xac\x45\x8f\x5b\x05\xb4\x38\x83\x79\x45\x03\x80\xd1\x82\
+\x09\xc0\x6c\xa3\x01\x93\x0d\xfd\x50\x5b\x83\x3c\xa1\xfd\xb7\xed\
+\x59\x48\x66\xd6\x90\x43\xed\x97\x44\xf0\x7f\x07\x69\xa3\xc5\x1a\
+\x20\xdb\xd7\x0e\x3a\xf0\x84\x3f\xad\xa5\x98\x76\x03\x42\x3b\xcf\
+\xb4\x0c\xd8\xc1\x3c\x74\xb0\x48\x6f\x45\x18\x3b\xdc\x62\x28\xa9\
+\x0c\x7b\x07\x16\x5e\x47\x73\x44\xf1\x65\x01\x50\xcf\x28\x65\x75\
+\xd6\x0f\xa2\x95\xfa\x88\x4a\x2d\x73\xa3\xa7\xd2\x4b\x09\xc9\x28\
+\x92\xd6\x4c\x33\x03\x5f\x37\xcb\x32\x68\x40\x34\x97\x81\x5d\x15\
+\x92\x4f\xb2\x70\xe3\xf5\x86\xc3\x7a\x5f\x60\x45\xc2\x7b\xa8\x69\
+\x99\xa8\xc4\xea\x14\x78\x02\xa1\x51\x98\xcd\x59\xeb\x50\xfb\x39\
+\x3c\x08\x53\x13\xca\x3a\x9f\xf5\x66\x2d\x08\x81\x41\x65\xb2\xd6\
+\x46\x6c\x1b\x5c\x43\x21\x1c\x33\x21\x48\x99\x6a\xbd\x4f\xd6\x61\
+\x67\x02\xa0\xfd\x00\x32\xad\x89\xcb\x08\x15\x0b\x6a\x38\x5c\x2b\
+\x00\x70\xfe\x43\xf6\x2f\x06\xdd\x9a\x04\xca\x72\xf4\x8d\xeb\x41\
+\xd6\xc2\xfc\x6c\x58\x5e\xb8\xb0\x4c\x1e\xe7\x86\xaf\xbb\x0d\xe3\
+\x91\xa5\xd9\x89\x2f\xc6\xff\xaa\x12\x47\x4c\xc0\x92\x70\x1e\x15\
+\x60\x31\x2c\x15\xa1\x59\x10\xd2\xbe\x6a\xf4\x4b\x1b\x0f\x44\x2f\
+\x22\x20\x6a\x6e\x31\xe4\x03\x4d\xc5\xc7\xfd\x18\xa3\x4e\x60\xaf\
+\x62\xb3\xb2\x74\xbc\xa4\xbb\xbf\xa5\x89\x0f\x93\xd2\xea\x7b\x32\
+\x1a\x06\xc5\x12\x60\x29\x1a\x0a\xea\xc0\xc4\xb5\xaa\xc8\x5a\x2c\
+\x38\x4b\xba\x03\x34\x62\x2c\x6d\xca\x8d\x6d\xa3\xb6\x06\x41\x9e\
+\x67\x8b\xd5\xf2\xda\xb7\x5c\xda\x05\x34\x59\xdd\x59\x21\x26\xc1\
+\xe0\xd4\xda\x3a\x2f\x0e\xb0\x93\x5b\x43\x74\x09\x25\xb0\xf6\xdb\
+\x28\x58\x7e\x88\x49\x34\x41\x77\x30\x65\xed\x51\xfe\x74\xea\x14\
+\x7a\x43\x28\xe8\x7c\x72\xf7\x41\xee\x5d\x52\xa1\xe3\x86\xc6\x50\
+\x73\x40\xeb\x4b\xb4\x95\x39\x49\xc6\x1e\xc1\xaf\x6f\x1e\x26\x90\
+\xcf\xb3\xc3\x49\x40\x9e\x8f\xc5\x6f\x1e\x66\xd6\xfc\xcc\x5a\x12\
+\xd3\x00\x37\x31\x1a\x9f\x32\xc6\x3e\x0f\x0a\x4b\x58\xb0\x00\xd6\
+\xc9\xe5\xf0\x60\x1b\x9f\xba\x25\x50\x41\xe4\xce\x5d\x60\x9e\x55\
+\x1f\x16\x42\x1f\x01\x77\x2e\x78\x3e\x29\xda\x0f\x7e\x62\x82\xce\
+\x82\x7d\x5a\x46\x4d\x53\xe3\x9c\x97\xcd\x64\x05\x93\x30\x37\x95\
+\x25\x1d\x50\xa5\xd9\x28\x92\xc8\x4b\xd3\xcd\x29\x44\xa0\x3c\x6c\
+\x1f\xf3\xbe\xa1\x46\xfc\xf9\x64\xf4\x72\x36\x5f\x46\x59\xc0\x4f\
+\xeb\x64\xa4\xc7\xf3\xb0\x8a\x8d\xcd\x46\xf5\x7c\x34\xc9\x22\x3d\
+\x7f\xfd\xa5\x99\xa2\xf4\x9f\x0f\x45\x4e\x7f\xfe\x59\xfc\xf5\x9c\
+\x39\x9f\xa6\x9a\x56\x4a\x0d\xf3\xe9\x6a\xed\x53\xe7\x2e\xe8\x9f\
+\xd2\xb6\x9c\xe3\xce\xbf\x20\xd1\x72\xb7\x2d\x00\x3e\xad\xcf\x29\
+\x4d\x04\x35\xcd\x4f\x61\xfe\x63\xc9\x33\xfc\xac\x0d\x23\xe5\x06\
+\xd3\x2c\x1a\xc8\xe6\x0b\x30\x2a\x27\x78\x68\xf5\x36\x88\xd0\x13\
+\xca\x17\x51\x30\x3e\xb2\xde\xbc\x28\x0b\x39\x41\xcb\x30\xd2\xaf\
+\x62\xeb\x27\xb4\xe4\x55\x4d\x5b\x66\xa0\xc5\x24\x12\xd3\x44\x60\
+\x58\x9f\xd2\x12\x84\xfa\xb5\xfe\x59\x4d\x19\xb4\x5e\xe4\x93\xb1\
+\xd1\x11\xe6\xb3\xca\xa8\x00\x41\xbd\x19\x00\x74\x5d\xca\x80\xd9\
+\x65\x07\x30\x17\x62\xd9\x8b\x3c\x99\xba\x65\x51\x33\xcd\x73\xb4\
+\x68\x7b\x52\xa2\x93\x28\x2f\xc5\x51\xc5\x87\xd2\xc2\x15\x51\xa6\
+\xf9\x53\x45\x58\xd9\x4e\xa8\x28\x50\x32\x2f\xc1\x9f\x81\xbb\x7d\
+\x5a\x1c\x2b\x3b\x5f\x58\x22\xff\x45\x09\x17\x87\xa5\x6d\x3b\x3a\
+\x05\x16\x54\xe1\x31\xfb\xd2\xc1\x81\x21\x5b\x86\xba\x5f\x43\xa9\
+\xa8\xec\xf2\xd1\x10\x69\xfa\xa5\xcb\x35\x91\xe5\xab\x38\x58\x45\
+\x36\x29\xee\xd9\xdc\x8a\x81\xd5\x8e\x88\x95\x10\x1e\x80\x78\xd2\
+\xca\x09\x41\xed\xcb\xb1\x86\xca\x1f\xa2\x8d\xc1\x8d\xa2\xac\x52\
+\xa9\x42\x4e\xe5\x2d\x34\xd8\x15\x81\x4c\xa0\x5d\xc6\x6e\xd5\xe0\
+\xb6\x44\xcc\x96\x82\x97\xda\x5a\x13\x8d\xb3\x66\x52\xc6\x19\xf3\
+\x29\x95\x35\xb4\xbe\x5e\x0d\x01\x6c\x3b\x21\x87\xa1\xf3\x6b\x96\
+\xe0\xc9\x4b\x05\x70\x7d\x9f\x44\x33\x30\x64\xdc\x1b\x2d\xd9\xff\
+\x2d\x12\xd5\x7f\x22\x51\xfd\x4a\x8d\xd2\x9a\xb9\x7e\x32\xef\x1d\
+\x70\xba\xb8\xd5\xc3\x0e\x3e\xcf\x02\x87\x34\xd6\x61\xa0\x6c\x00\
+\x7c\x7e\x50\x51\xc2\xd3\xf6\x13\x79\xda\xae\xe2\x89\xde\xf8\x59\
+\x37\x5b\x62\x4a\xdc\x4f\xae\x61\x4b\xc5\x21\xb3\xc5\x4e\x52\x4a\
+\x18\xdb\x79\x22\x63\x3b\x55\x8c\xb1\xdb\x26\xeb\xa6\x2b\x9c\xe5\
+\x2d\x4a\x0d\x67\x2a\x0e\x95\x33\xac\x33\xc4\xf8\x84\x31\x82\xed\
+\x72\x1d\x63\x64\xa5\x11\x6d\xf0\x8d\xa3\x56\xcd\x18\xf5\x8f\x35\
+\xe3\xfd\x35\xe3\xdb\x6b\xc6\x77\x24\xc6\x2a\x8a\x4f\x3d\xab\xfd\
+\xf5\x86\x84\x01\x30\xaa\x7d\x58\x1e\xcb\xb0\xb1\x91\x4b\x34\x25\
+\x7d\x3e\xbd\xbd\xc2\xcf\x63\x21\x51\x00\x15\xe5\x69\x82\x15\xf7\
+\xb4\x08\x32\x5d\xa7\x2e\x47\xfc\xa0\xde\xc7\x15\x9b\x1e\xbe\x9d\
+\x4a\xbb\xd2\x95\xef\x90\x1b\xd7\x87\x0a\xae\xbe\xcf\xfb\xb6\x42\
+\x79\x7d\x5e\xc1\x84\xfb\x0f\xcc\xc3\xd0\x04\x69\x27\xe0\x83\x95\
+\x67\x42\x80\x87\x46\xdf\x59\xc0\x93\x3d\xcf\xb0\xaa\xc4\x6c\xf5\
+\x98\x51\x11\x27\xad\xc5\xbe\x57\xa0\x39\x56\x68\xc6\xc0\xb6\x00\
+\x65\x77\xa0\xb9\xda\x01\x5b\xa1\xed\x11\x7f\x9e\x2c\x06\xda\xcb\
+\x97\xa5\xb5\x19\xd3\x2a\x9d\xfa\xc9\xfd\xac\x34\x08\x1b\xa8\x07\
+\x3f\x78\x4f\xc5\xf5\x57\x44\x81\x96\xf6\xed\xd7\xb5\xb4\x1b\xf4\
+\x83\xe9\x16\xc6\xf3\x21\x47\xfb\x24\x4a\xe8\xfd\x88\x3a\x3a\x58\
+\xa4\x2d\x74\x57\xb8\x2a\xbd\xca\x97\x27\xe1\xe7\xc3\xa2\xbe\x33\
+\x0a\x2a\xce\x6a\xd2\x8f\x82\x6a\x28\x1e\xa5\xa6\x9f\xc7\x32\x47\
+\x16\x9b\x86\xbc\x19\xa8\xb6\x50\xea\xd2\x82\x85\x56\x3b\xb6\xc5\
+\x85\x91\x76\x11\xec\x55\x06\xf4\x84\x15\xdf\x99\x96\xfa\x82\x9f\
+\xcc\x14\x40\x2d\xfe\xab\xed\x2b\x2d\xb3\x59\x86\xa9\x28\xc7\x45\
+\x32\xe4\x5d\x2d\x1d\x46\x75\x61\x57\x4b\xd5\x0f\xdd\xaf\xca\x88\
+\xc2\x37\x02\x80\x16\xb0\xd9\x6e\xd1\x42\x0a\xe1\x84\xb1\x24\xc2\
+\x8b\xdb\x5e\xa5\x65\x67\x55\x8f\x96\x6f\xa9\x60\x24\x55\x8e\xbf\
+\xff\x6e\x21\xf4\x69\xfb\x4c\x6b\x8b\xe7\x72\x59\xb0\x22\x15\xa5\
+\x51\x1a\x20\xff\x4e\xf2\xf8\x96\xf8\x50\xe7\xcc\xaa\x40\xf2\x7b\
+\x07\xae\xa3\x6e\x74\x42\xd8\x02\xdb\x5e\x9a\x3a\xbf\x1d\xf3\xf1\
+\xe8\xf2\x6c\xa4\x37\x1a\x7f\x5b\x61\xad\x7e\x44\x4c\xdf\xa8\x94\
+\x62\x62\x63\xa5\x14\x06\x85\x8a\xa3\xe7\x3c\x4c\x6e\xb2\xcf\x23\
+\xf7\x7c\x4f\x8a\xac\xc2\xaa\xf2\x6d\x01\x7c\xef\x4c\x3c\xda\x65\
+\x97\xd5\xe4\x17\x5d\x3e\x41\x50\xf9\x65\xa8\xb7\x43\x7f\xae\x7f\
+\xfe\xca\x8f\x60\xe9\xd5\x54\x76\x32\x6b\xbc\xcc\x2f\x37\xbd\x34\
+\xc2\xfb\xc7\xf4\xce\x9b\x1c\x7f\x8d\xb2\x37\x7b\xb2\x03\x16\x8a\
+\x0a\xcf\x57\x84\x8b\x52\x10\xc4\xc3\x7b\xa3\x59\xba\x33\x56\xf2\
+\x31\x18\x69\x88\xe4\xbd\x95\x2c\xda\xf4\x38\xd8\x14\xf0\x75\xb4\
+\x5e\x7b\x77\xa7\xc1\xf1\x4a\x3b\x13\x68\x1e\x94\x04\x1a\xc7\xef\
+\x5c\xdf\x09\xee\x1a\x6d\xfa\x44\x3d\xd4\x67\xab\x48\xf3\xd8\xa3\
+\x92\xd3\xff\x8f\x1c\x25\xa5\xe7\xc6\x0b\x82\xc8\x94\xf8\xdb\xee\
+\x16\x61\xde\xa5\xf8\x05\xa0\x75\xfc\xec\x74\x55\x66\x96\xd6\xfd\
+\x1b\x5e\xab\x09\x78\x18\x8b\x9d\x8c\x34\xe5\x8e\x40\x06\x74\x28\
+\x5f\x6a\x6b\x48\xe8\xc4\x91\x0a\xf8\x03\xad\x27\xc1\xf4\x8a\xd4\
+\xa5\xd2\xce\x66\xfd\x92\x51\x25\x5a\x8d\xbe\x25\xfa\x15\xda\x0a\
+\x37\x95\x66\x8e\x84\xe9\x53\x95\x00\xad\x0e\x8f\x41\x66\x4c\x57\
+\x0a\xa2\x2c\x49\x83\x29\xea\x69\xf6\x95\x75\x81\x57\x54\x53\xf1\
+\xd9\x04\xba\x16\x01\x63\x27\xa3\x5b\x01\x62\xb7\x43\x93\x20\x04\
+\x48\x93\x9b\x4a\x8b\xa1\xfa\x25\xd7\x6e\x03\x10\xf4\x95\xfe\x2a\
+\x87\x3c\x80\x12\x40\x42\xd4\x95\x4e\xb9\x4a\x44\x92\x5f\x54\xa5\
+\x72\x49\x21\x33\xc1\x20\x20\xde\xb3\xc7\xbf\x79\xb2\xb3\x6e\xc9\
+\x94\xdf\xc9\x95\xe2\x82\x7a\xa1\x11\x7d\xf7\x62\x3e\xf1\x2f\x83\
+\x3b\xf0\xd7\x5b\xbc\x7f\x93\x33\x2d\x5d\x6c\x0c\xf1\x5d\xd4\x89\
+\xcf\x8e\xac\xa6\xce\x14\xa4\x9a\x42\xac\x09\x77\x90\x79\x69\x08\
+\xa3\xab\x98\xb2\xf5\xd5\x9e\xdb\x01\x20\x5b\x48\xf0\x0c\x71\xad\
+\x92\xbb\x3e\xaa\x1c\x28\xf8\x2c\x98\xcf\x3d\x52\xd8\xa8\xa6\x07\
+\x32\x75\x19\x6c\x2a\x40\xab\x1b\x6d\x6c\x6c\x3d\x74\x39\xdc\xd4\
+\xab\x83\x8c\x3d\xd7\x21\xd1\xef\xb8\x95\x3a\x95\x36\x4f\xb2\x2f\
+\x0c\x4b\xdb\x71\x63\x3c\xec\xc7\xe2\x9e\xdd\x7e\x13\x2e\xb3\xd1\
+\x0e\x0f\xe2\x25\x56\xfd\x02\x0a\x0e\x99\xee\xa3\x17\xc0\xa4\xf1\
+\x7d\xad\xbf\xdd\x2b\x00\x4f\x3d\x56\x62\xbc\x9b\xbd\x3f\x2d\x43\
+\x21\x98\x8d\x88\x40\x4d\x5c\xb4\xc6\x98\xaf\x22\x52\x7d\xf7\xab\
+\xd3\xd1\x4e\xf1\xcd\xac\xf4\x7a\xb9\x94\x72\x64\xb3\xad\x32\x2b\
+\x9c\x29\xc8\xf3\x02\xf7\xd2\xf1\xed\xea\x5a\x00\xc9\xbc\x25\xdb\
+\x2b\xce\x4a\xcd\xa3\x1d\x46\x41\x68\x1a\x5c\x0c\xe0\xab\x82\x18\
+\xa0\x3f\x93\x05\xa9\x50\x85\x5a\x66\x54\x6d\xba\x98\x0b\x91\x61\
+\x3f\x5d\xab\x01\x42\x97\xc8\x66\x05\xc6\x11\x6d\x1b\x52\x1b\x81\
+\x59\xfb\xf2\x2c\x4b\x1e\x6f\x6a\xc2\x5c\xc1\xcb\x40\x09\xd3\x45\
+\x70\xa7\x61\x50\x0a\xe6\x12\x51\xc2\x21\x5b\x7f\xfd\x7d\x8a\x5e\
+\x77\xc3\x0b\x15\xfd\xd2\x1b\x15\x59\xcf\x96\x9e\x07\x1a\x78\x1e\
+\x68\x94\x5c\x87\x10\xde\x46\xf8\x9e\xdb\x15\x65\x37\x07\xd9\x86\
+\xe6\x91\xe7\x15\xee\x06\x94\x95\xcc\x78\x05\x5d\x2a\x9b\x39\x85\
+\x4f\x38\xae\xc2\x8f\xbc\x29\xca\xc5\xa0\x1e\xe8\xe5\x5e\x26\xbc\
+\x24\xd0\x61\xff\xf1\x81\x67\x07\x78\xcf\x8f\xbd\x14\x90\xdd\x8d\
+\xcb\x33\x0d\xbf\x2d\xc3\x86\xad\xfc\xe6\x4d\x99\x87\x0e\x74\xd6\
+\x53\x0c\xf5\x51\xfa\x7e\x15\xff\x4f\x11\xe8\xe9\x85\x23\x14\x3f\
+\xe2\xb4\x8a\x18\xe5\x9b\x9e\x02\xae\xa3\x93\x13\x15\x0d\xcc\xe5\
+\x58\x18\x9a\x0d\x5e\x78\xc8\xd4\xe4\xfa\x6e\x52\x88\x26\xa3\xc9\
+\xd9\x64\x06\xfe\xd5\x9a\x1e\x5f\x4e\x2e\x66\xa3\xda\xa4\x45\x8f\
+\x41\x79\x11\x8e\xef\x3d\xf9\xc4\x4e\xaa\x37\x16\xe8\x26\xaa\x7a\
+\x3d\xb6\x4c\x95\x35\x57\xfe\xd5\xa9\x69\x9f\x23\x69\x59\xfa\xc5\
+\x6a\xd4\xb6\x17\xd8\xd4\x76\xf9\xbb\x9d\xa2\x63\x3d\xae\x49\xcd\
+\x49\xba\x6d\x8b\x61\x56\x60\x54\xda\xae\xa9\x4f\xd2\x28\x28\xda\
+\x55\x17\x51\xe4\x7b\x92\xb5\x59\x1b\x3f\x79\xc1\x1d\x11\x5a\xb0\
+\x49\x9e\x2f\x82\x28\xde\xce\x8b\x3c\x1e\xb9\xa6\xf4\xf5\x43\xe6\
+\xc7\x95\x65\x55\x1c\x44\xf4\x8e\x9c\x29\xab\x27\x82\x80\x10\x25\
+\x2c\x61\xca\x8d\x49\xfa\xe2\xa0\x81\x51\x55\x69\x5a\x70\x83\xcf\
+\x72\x7d\x4c\xde\x30\x21\x4d\xe9\xca\xa4\xec\x2d\xbb\x6e\x7b\x4f\
+\x1e\x01\xba\x13\xcb\xb7\xc9\x3e\xd4\xf9\xf2\xc8\x2a\x74\xac\x04\
+\x9e\xcb\x81\xab\xa9\xad\x4a\x43\x44\x6d\xcb\x98\x59\x43\xc9\x86\
+\x51\xfa\x84\x7e\x81\x50\x3f\x01\xcf\x01\x31\x81\x0a\xe8\x13\x34\
+\x6c\x6e\x66\xa9\xee\xa5\xc0\x47\xfd\x8c\xe3\x52\xaf\x21\x64\xf5\
+\x0d\x78\xf2\x18\x89\x3f\x05\x66\x09\x14\x0e\xa6\x71\x45\x22\xd7\
+\xbe\x4a\x31\x5e\xb1\x23\x26\x4c\x41\x88\x50\xa2\x2d\x0f\x66\xf4\
+\x57\xc9\xfd\xe4\xfe\xb7\x5e\x50\xde\xe8\x0a\x32\xfc\x28\xbf\x81\
+\xcc\xd9\xcc\x28\x12\x92\x4e\x36\xc8\x87\x79\x40\xa3\x6f\x29\x0a\
+\x63\x30\x2a\xdc\x07\xe5\xe5\x8c\x7e\x38\x9a\xfd\x31\xbb\x3a\xbb\
+\x84\xaa\x64\x3a\x4a\x6f\x7f\xe6\xf8\xf2\x48\xad\xa2\x67\xaf\xdd\
+\xc9\xf8\xe9\x1e\x12\xa5\x4e\x2c\x81\x60\x8d\xfc\x3d\x26\x10\x30\
+\x4c\x92\xa0\xd8\x4d\x4d\x04\x4b\x89\xe2\x51\x37\x42\x03\xd3\xf1\
+\x55\xd7\xa1\x0e\x62\x87\x3e\x69\xa8\xef\x65\xb7\x38\x05\xa4\xa8\
+\x12\x12\x0e\xf5\x1e\xbd\x33\x4b\xed\x72\xa8\x57\x96\x62\x98\xcf\
+\x58\xc5\x89\x29\xa1\xb3\x11\xb7\x25\xc2\x2c\xbf\xc3\xeb\x5b\x4b\
+\xf8\x1d\x3b\x18\x55\xf8\x55\x62\xb1\x96\x12\x09\x94\xbb\x05\xa4\
+\xe4\x67\xff\x3a\x0e\x07\xa2\x9a\x24\x58\xa6\xab\xe9\x09\x7d\xe1\
+\xeb\xff\x43\x57\x53\x48\x81\xd9\x1b\x65\xb5\x9a\x62\x74\x95\x29\
+\xaa\xb7\xb3\xcb\x55\xb5\xfd\xaa\x9b\x2b\x2b\xc5\xba\x89\xaa\xf2\
+\xfa\xf4\x5b\x54\xc5\xdf\x1a\x96\xb9\x2d\xbb\x9e\x2c\x05\xab\xa7\
+\xde\x46\x2e\xa9\x21\xd5\x42\x51\x22\x78\xed\x1a\x50\x30\xb0\x17\
+\x1b\x35\xe9\x3a\xb2\xdc\x5a\x0f\x36\x59\x56\x5a\x15\x6f\x30\x97\
+\x4b\x4d\xac\xc2\xd8\xd7\x83\x0e\xab\xdb\x0e\x3a\xec\xbf\x25\xf5\
+\x7f\x9e\xbc\x80\x7b\
+\x00\x00\x03\x7c\
+\x3c\
+\x21\x44\x4f\x43\x54\x59\x50\x45\x20\x68\x74\x6d\x6c\x3e\x0a\x3c\
+\x68\x74\x6d\x6c\x3e\x0a\x3c\x68\x65\x61\x64\x3e\x0a\x3c\x6d\x65\
+\x74\x61\x20\x63\x68\x61\x72\x73\x65\x74\x3d\x22\x75\x74\x66\x2d\
+\x38\x22\x3e\x20\x0a\x3c\x6d\x65\x74\x61\x20\x68\x74\x74\x70\x2d\
+\x65\x71\x75\x69\x76\x3d\x22\x63\x6f\x6e\x74\x65\x6e\x74\x2d\x74\
+\x79\x70\x65\x22\x20\x63\x6f\x6e\x74\x65\x6e\x74\x3d\x22\x74\x65\
+\x78\x74\x2f\x68\x74\x6d\x6c\x3b\x20\x63\x68\x61\x72\x73\x65\x74\
+\x3d\x75\x74\x66\x2d\x38\x22\x3e\x0a\x3c\x74\x69\x74\x6c\x65\x3e\
+\x40\x54\x49\x54\x4c\x45\x40\x3c\x2f\x74\x69\x74\x6c\x65\x3e\x0a\
+\x3c\x6c\x69\x6e\x6b\x20\x72\x65\x6c\x3d\x22\x69\x63\x6f\x6e\x22\
+\x20\x68\x72\x65\x66\x3d\x22\x40\x46\x41\x56\x49\x43\x4f\x4e\x40\
+\x22\x20\x74\x79\x70\x65\x3d\x22\x69\x6d\x61\x67\x65\x2f\x78\x2d\
+\x69\x63\x6f\x6e\x22\x20\x2f\x3e\x0a\x3c\x73\x74\x79\x6c\x65\x3e\
+\x0a\x62\x6f\x64\x79\x20\x7b\x0a\x20\x20\x70\x61\x64\x64\x69\x6e\
+\x67\x3a\x20\x33\x65\x6d\x20\x30\x65\x6d\x3b\x0a\x20\x20\x62\x61\
+\x63\x6b\x67\x72\x6f\x75\x6e\x64\x3a\x20\x2d\x77\x65\x62\x6b\x69\
+\x74\x2d\x67\x72\x61\x64\x69\x65\x6e\x74\x28\x6c\x69\x6e\x65\x61\
+\x72\x2c\x20\x6c\x65\x66\x74\x20\x74\x6f\x70\x2c\x20\x6c\x65\x66\
+\x74\x20\x62\x6f\x74\x74\x6f\x6d\x2c\x20\x66\x72\x6f\x6d\x28\x23\
+\x38\x35\x37\x38\x34\x41\x29\x2c\x20\x74\x6f\x28\x23\x46\x44\x46\
+\x44\x46\x44\x29\x2c\x20\x63\x6f\x6c\x6f\x72\x2d\x73\x74\x6f\x70\
+\x28\x30\x2e\x35\x2c\x20\x23\x46\x44\x46\x44\x46\x44\x29\x29\x3b\
+\x0a\x20\x20\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\x2d\x72\x65\
+\x70\x65\x61\x74\x3a\x20\x72\x65\x70\x65\x61\x74\x2d\x78\x3b\x0a\
+\x7d\x0a\x23\x62\x6f\x78\x20\x7b\x0a\x20\x20\x62\x61\x63\x6b\x67\
+\x72\x6f\x75\x6e\x64\x3a\x20\x77\x68\x69\x74\x65\x3b\x0a\x20\x20\
+\x62\x6f\x72\x64\x65\x72\x3a\x20\x31\x70\x78\x20\x73\x6f\x6c\x69\
+\x64\x20\x23\x38\x35\x37\x38\x34\x41\x3b\x0a\x20\x20\x6d\x61\x78\
+\x2d\x77\x69\x64\x74\x68\x3a\x20\x36\x30\x30\x70\x78\x3b\x0a\x20\
+\x20\x68\x65\x69\x67\x68\x74\x3a\x20\x35\x30\x25\x3b\x0a\x20\x20\
+\x70\x61\x64\x64\x69\x6e\x67\x3a\x20\x34\x30\x70\x78\x3b\x0a\x20\
+\x20\x70\x61\x64\x64\x69\x6e\x67\x2d\x62\x6f\x74\x74\x6f\x6d\x3a\
+\x20\x31\x30\x70\x78\x3b\x0a\x20\x20\x6d\x61\x72\x67\x69\x6e\x3a\
+\x20\x61\x75\x74\x6f\x3b\x0a\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\
+\x72\x61\x64\x69\x75\x73\x3a\x20\x30\x2e\x38\x65\x6d\x3b\x0a\x20\
+\x20\x74\x65\x78\x74\x2d\x61\x6c\x69\x67\x6e\x3a\x20\x63\x65\x6e\
+\x74\x65\x72\x3b\x0a\x20\x20\x76\x65\x72\x74\x69\x63\x61\x6c\x2d\
+\x61\x6c\x69\x67\x6e\x3a\x20\x6d\x69\x64\x64\x6c\x65\x3b\x0a\x20\
+\x20\x6d\x61\x72\x67\x69\x6e\x3a\x20\x61\x75\x74\x6f\x3b\x0a\x7d\
+\x0a\x68\x31\x20\x7b\x0a\x20\x20\x66\x6f\x6e\x74\x2d\x73\x69\x7a\
+\x65\x3a\x20\x31\x33\x30\x25\x3b\x0a\x20\x20\x66\x6f\x6e\x74\x2d\
+\x77\x65\x69\x67\x68\x74\x3a\x20\x62\x6f\x6c\x64\x3b\x0a\x20\x20\
+\x62\x6f\x72\x64\x65\x72\x2d\x62\x6f\x74\x74\x6f\x6d\x3a\x20\x31\
+\x70\x78\x20\x73\x6f\x6c\x69\x64\x20\x23\x38\x35\x37\x38\x34\x41\
+\x3b\x0a\x20\x20\x6d\x61\x72\x67\x69\x6e\x2d\x62\x6f\x74\x74\x6f\
+\x6d\x3a\x20\x30\x70\x78\x3b\x0a\x7d\x0a\x3c\x2f\x73\x74\x79\x6c\
+\x65\x3e\x0a\x3c\x2f\x68\x65\x61\x64\x3e\x0a\x3c\x62\x6f\x64\x79\
+\x3e\x0a\x20\x20\x3c\x64\x69\x76\x20\x69\x64\x3d\x22\x62\x6f\x78\
+\x22\x3e\x0a\x20\x20\x20\x20\x3c\x69\x6d\x67\x20\x73\x72\x63\x3d\
+\x22\x40\x49\x4d\x41\x47\x45\x40\x22\x20\x77\x69\x64\x74\x68\x3d\
+\x22\x36\x34\x22\x20\x68\x65\x69\x67\x68\x74\x3d\x22\x36\x34\x22\
+\x2f\x3e\x0a\x20\x20\x20\x20\x3c\x68\x31\x3e\x41\x64\x42\x6c\x6f\
+\x63\x6b\x20\x50\x6c\x75\x73\x3c\x2f\x68\x31\x3e\x0a\x20\x20\x20\
+\x20\x3c\x70\x3e\x40\x4d\x45\x53\x53\x41\x47\x45\x40\x3c\x2f\x70\
+\x3e\x0a\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x3c\x2f\x62\x6f\x64\
+\x79\x3e\x0a\x3c\x2f\x68\x74\x6d\x6c\x3e\x0a\
+\x00\x00\x0c\x87\
+\x3c\
+\x21\x44\x4f\x43\x54\x59\x50\x45\x20\x68\x74\x6d\x6c\x3e\x0a\x3c\
+\x68\x74\x6d\x6c\x3e\x0a\x3c\x68\x65\x61\x64\x3e\x0a\x3c\x6d\x65\
+\x74\x61\x20\x63\x68\x61\x72\x73\x65\x74\x3d\x22\x75\x74\x66\x2d\
+\x38\x22\x3e\x20\x0a\x3c\x6d\x65\x74\x61\x20\x68\x74\x74\x70\x2d\
+\x65\x71\x75\x69\x76\x3d\x22\x63\x6f\x6e\x74\x65\x6e\x74\x2d\x74\
+\x79\x70\x65\x22\x20\x63\x6f\x6e\x74\x65\x6e\x74\x3d\x22\x74\x65\
+\x78\x74\x2f\x68\x74\x6d\x6c\x3b\x20\x63\x68\x61\x72\x73\x65\x74\
+\x3d\x75\x74\x66\x2d\x38\x22\x3e\x0a\x3c\x74\x69\x74\x6c\x65\x3e\
+\x40\x54\x49\x54\x4c\x45\x40\x3c\x2f\x74\x69\x74\x6c\x65\x3e\x0a\
+\x3c\x6c\x69\x6e\x6b\x20\x72\x65\x6c\x3d\x22\x69\x63\x6f\x6e\x22\
+\x20\x68\x72\x65\x66\x3d\x22\x40\x46\x41\x56\x49\x43\x4f\x4e\x40\
+\x22\x20\x74\x79\x70\x65\x3d\x22\x69\x6d\x61\x67\x65\x2f\x78\x2d\
+\x69\x63\x6f\x6e\x22\x20\x2f\x3e\x0a\x3c\x73\x74\x79\x6c\x65\x3e\
+\x0a\x2a\x20\x7b\x0a\x20\x20\x20\x20\x6d\x61\x72\x67\x69\x6e\x3a\
+\x20\x30\x3b\x0a\x20\x20\x20\x20\x70\x61\x64\x64\x69\x6e\x67\x3a\
+\x20\x30\x3b\x0a\x20\x20\x20\x20\x66\x6f\x6e\x74\x2d\x66\x61\x6d\
+\x69\x6c\x79\x3a\x20\x22\x44\x65\x6a\x61\x56\x75\x20\x53\x61\x6e\
+\x73\x22\x3b\x0a\x7d\x0a\x0a\x62\x6f\x64\x79\x20\x7b\x0a\x20\x20\
+\x20\x20\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\x3a\x20\x2d\x77\
+\x65\x62\x6b\x69\x74\x2d\x67\x72\x61\x64\x69\x65\x6e\x74\x28\x6c\
+\x69\x6e\x65\x61\x72\x2c\x20\x6c\x65\x66\x74\x20\x74\x6f\x70\x2c\
+\x20\x6c\x65\x66\x74\x20\x62\x6f\x74\x74\x6f\x6d\x2c\x20\x66\x72\
+\x6f\x6d\x28\x23\x38\x35\x37\x38\x34\x41\x29\x2c\x20\x74\x6f\x28\
+\x23\x46\x44\x46\x44\x46\x44\x29\x2c\x20\x63\x6f\x6c\x6f\x72\x2d\
+\x73\x74\x6f\x70\x28\x30\x2e\x35\x2c\x20\x23\x46\x44\x46\x44\x46\
+\x44\x29\x29\x3b\x0a\x20\x20\x20\x20\x62\x61\x63\x6b\x67\x72\x6f\
+\x75\x6e\x64\x2d\x72\x65\x70\x65\x61\x74\x3a\x20\x72\x65\x70\x65\
+\x61\x74\x2d\x78\x3b\x0a\x20\x20\x20\x20\x6d\x61\x72\x67\x69\x6e\
+\x2d\x74\x6f\x70\x3a\x20\x32\x30\x30\x70\x78\x3b\x0a\x7d\x0a\x0a\
+\x23\x68\x65\x61\x64\x65\x72\x2c\x20\x23\x73\x65\x61\x72\x63\x68\
+\x2c\x20\x23\x66\x6f\x6f\x74\x65\x72\x20\x7b\x0a\x20\x20\x20\x20\
+\x77\x69\x64\x74\x68\x3a\x20\x36\x30\x30\x70\x78\x3b\x0a\x20\x20\
+\x20\x20\x6d\x61\x72\x67\x69\x6e\x3a\x20\x31\x30\x70\x78\x20\x61\
+\x75\x74\x6f\x3b\x0a\x7d\x0a\x0a\x23\x68\x65\x61\x64\x65\x72\x2c\
+\x20\x23\x73\x65\x61\x72\x63\x68\x20\x7b\x0a\x20\x20\x20\x20\x62\
+\x6f\x72\x64\x65\x72\x2d\x72\x61\x64\x69\x75\x73\x3a\x20\x30\x2e\
+\x38\x65\x6d\x3b\x0a\x20\x20\x20\x20\x70\x61\x64\x64\x69\x6e\x67\
+\x3a\x20\x32\x35\x70\x78\x3b\x0a\x7d\x0a\x0a\x23\x68\x65\x61\x64\
+\x65\x72\x20\x7b\x0a\x20\x20\x20\x20\x62\x61\x63\x6b\x67\x72\x6f\
+\x75\x6e\x64\x3a\x20\x2d\x77\x65\x62\x6b\x69\x74\x2d\x67\x72\x61\
+\x64\x69\x65\x6e\x74\x28\x6c\x69\x6e\x65\x61\x72\x2c\x20\x6c\x65\
+\x66\x74\x20\x74\x6f\x70\x2c\x20\x6c\x65\x66\x74\x20\x62\x6f\x74\
+\x74\x6f\x6d\x2c\x20\x66\x72\x6f\x6d\x28\x23\x44\x35\x37\x45\x33\
+\x45\x29\x2c\x20\x74\x6f\x28\x23\x44\x35\x37\x45\x33\x45\x29\x2c\
+\x20\x63\x6f\x6c\x6f\x72\x2d\x73\x74\x6f\x70\x28\x30\x2e\x35\x2c\
+\x20\x23\x46\x46\x42\x41\x38\x39\x29\x29\x3b\x0a\x20\x20\x20\x20\
+\x68\x65\x69\x67\x68\x74\x3a\x20\x32\x35\x70\x78\x3b\x0a\x7d\x0a\
+\x0a\x23\x68\x65\x61\x64\x65\x72\x20\x68\x31\x20\x7b\x0a\x20\x20\
+\x20\x20\x64\x69\x73\x70\x6c\x61\x79\x3a\x20\x69\x6e\x6c\x69\x6e\
+\x65\x3b\x0a\x20\x20\x20\x20\x66\x6f\x6e\x74\x2d\x73\x69\x7a\x65\
+\x3a\x20\x31\x2e\x37\x65\x6d\x3b\x0a\x20\x20\x20\x20\x66\x6f\x6e\
+\x74\x2d\x77\x65\x69\x67\x68\x74\x3a\x20\x62\x6f\x6c\x64\x3b\x0a\
+\x7d\x0a\x0a\x23\x68\x65\x61\x64\x65\x72\x20\x69\x6d\x67\x20\x7b\
+\x0a\x20\x20\x20\x20\x64\x69\x73\x70\x6c\x61\x79\x3a\x20\x69\x6e\
+\x6c\x69\x6e\x65\x3b\x0a\x20\x20\x20\x20\x66\x6c\x6f\x61\x74\x3a\
+\x20\x72\x69\x67\x68\x74\x3b\x0a\x20\x20\x20\x20\x6d\x61\x72\x67\
+\x69\x6e\x2d\x74\x6f\x70\x3a\x20\x2d\x35\x70\x78\x3b\x0a\x7d\x0a\
+\x0a\x23\x73\x65\x61\x72\x63\x68\x20\x7b\x0a\x20\x20\x20\x20\x62\
+\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\x3a\x20\x2d\x77\x65\x62\x6b\
+\x69\x74\x2d\x67\x72\x61\x64\x69\x65\x6e\x74\x28\x6c\x69\x6e\x65\
+\x61\x72\x2c\x20\x6c\x65\x66\x74\x20\x74\x6f\x70\x2c\x20\x72\x69\
+\x67\x68\x74\x20\x74\x6f\x70\x2c\x20\x66\x72\x6f\x6d\x28\x23\x38\
+\x35\x37\x38\x34\x41\x29\x2c\x20\x74\x6f\x28\x23\x38\x35\x37\x38\
+\x34\x41\x29\x2c\x20\x63\x6f\x6c\x6f\x72\x2d\x73\x74\x6f\x70\x28\
+\x30\x2e\x35\x2c\x20\x23\x43\x38\x43\x32\x41\x45\x29\x29\x3b\x0a\
+\x20\x20\x20\x20\x68\x65\x69\x67\x68\x74\x3a\x20\x35\x30\x70\x78\
+\x3b\x0a\x20\x20\x20\x20\x63\x6f\x6c\x6f\x72\x3a\x20\x23\x30\x30\
+\x30\x3b\x0a\x20\x20\x20\x20\x74\x65\x78\x74\x2d\x61\x6c\x69\x67\
+\x6e\x3a\x20\x63\x65\x6e\x74\x65\x72\x3b\x0a\x20\x20\x20\x20\x70\
+\x61\x64\x64\x69\x6e\x67\x2d\x74\x6f\x70\x3a\x20\x34\x30\x70\x78\
+\x20\x21\x69\x6d\x70\x6f\x72\x74\x61\x6e\x74\x3b\x0a\x7d\x0a\x0a\
+\x23\x73\x65\x61\x72\x63\x68\x20\x66\x69\x65\x6c\x64\x73\x65\x74\
+\x20\x7b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x3a\x20\x30\
+\x3b\x0a\x7d\x0a\x0a\x23\x73\x65\x61\x72\x63\x68\x20\x69\x6e\x70\
+\x75\x74\x5b\x74\x79\x70\x65\x3d\x74\x65\x78\x74\x5d\x20\x7b\x0a\
+\x20\x20\x20\x20\x77\x69\x64\x74\x68\x3a\x20\x36\x35\x25\x3b\x0a\
+\x7d\x0a\x0a\x23\x73\x65\x61\x72\x63\x68\x20\x69\x6e\x70\x75\x74\
+\x5b\x74\x79\x70\x65\x3d\x73\x75\x62\x6d\x69\x74\x5d\x20\x7b\x0a\
+\x20\x20\x20\x20\x77\x69\x64\x74\x68\x3a\x20\x32\x35\x25\x3b\x0a\
+\x7d\x0a\x0a\x23\x66\x6f\x6f\x74\x65\x72\x20\x7b\x0a\x20\x20\x20\
+\x20\x74\x65\x78\x74\x2d\x61\x6c\x69\x67\x6e\x3a\x20\x63\x65\x6e\
+\x74\x65\x72\x3b\x0a\x20\x20\x20\x20\x63\x6f\x6c\x6f\x72\x3a\x20\
+\x23\x39\x39\x39\x3b\x0a\x7d\x0a\x0a\x23\x66\x6f\x6f\x74\x65\x72\
+\x20\x61\x20\x7b\x0a\x20\x20\x20\x20\x63\x6f\x6c\x6f\x72\x3a\x20\
+\x23\x35\x35\x35\x3b\x0a\x20\x20\x20\x20\x74\x65\x78\x74\x2d\x64\
+\x65\x63\x6f\x72\x61\x74\x69\x6f\x6e\x3a\x20\x6e\x6f\x6e\x65\x3b\
+\x0a\x7d\x0a\x0a\x23\x66\x6f\x6f\x74\x65\x72\x20\x61\x3a\x68\x6f\
+\x76\x65\x72\x20\x7b\x0a\x20\x20\x20\x20\x74\x65\x78\x74\x2d\x64\
+\x65\x63\x6f\x72\x61\x74\x69\x6f\x6e\x3a\x20\x75\x6e\x64\x65\x72\
+\x6c\x69\x6e\x65\x3b\x0a\x7d\x0a\x20\x20\x20\x20\x3c\x2f\x73\x74\
+\x79\x6c\x65\x3e\x0a\x20\x20\x20\x20\x3c\x73\x63\x72\x69\x70\x74\
+\x20\x74\x79\x70\x65\x3d\x22\x74\x65\x78\x74\x2f\x6a\x61\x76\x61\
+\x73\x63\x72\x69\x70\x74\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\
+\x20\x66\x75\x6e\x63\x74\x69\x6f\x6e\x20\x75\x70\x64\x61\x74\x65\
+\x28\x29\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x7b\x0a\x20\x20\x20\
+\x20\x20\x20\x20\x20\x20\x20\x20\x20\x65\x78\x74\x65\x72\x6e\x61\
+\x6c\x2e\x73\x74\x61\x72\x74\x50\x61\x67\x65\x2e\x70\x72\x6f\x76\
+\x69\x64\x65\x72\x53\x74\x72\x69\x6e\x67\x28\x66\x75\x6e\x63\x74\
+\x69\x6f\x6e\x28\x70\x72\x6f\x76\x69\x64\x65\x72\x29\x20\x7b\x0a\
+\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\
+\x64\x6f\x63\x75\x6d\x65\x6e\x74\x2e\x67\x65\x74\x45\x6c\x65\x6d\
+\x65\x6e\x74\x42\x79\x49\x64\x28\x27\x66\x6f\x6f\x74\x65\x72\x27\
+\x29\x2e\x69\x6e\x6e\x65\x72\x48\x54\x4d\x4c\x20\x3d\x20\x0a\x20\
+\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\
+\x20\x20\x20\x70\x72\x6f\x76\x69\x64\x65\x72\x0a\x20\x20\x20\x20\
+\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\
+\x2b\x20\x27\x20\x7c\x20\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x68\
+\x74\x74\x70\x3a\x2f\x2f\x65\x72\x69\x63\x2d\x69\x64\x65\x2e\x70\
+\x79\x74\x68\x6f\x6e\x2d\x70\x72\x6f\x6a\x65\x63\x74\x73\x2e\x6f\
+\x72\x67\x2f\x22\x3e\x27\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\
+\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x2b\x20\x27\x40\x45\
+\x52\x49\x43\x5f\x4c\x49\x4e\x4b\x40\x3c\x2f\x61\x3e\x27\x3b\x0a\
+\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\
+\x64\x6f\x63\x75\x6d\x65\x6e\x74\x2e\x67\x65\x74\x45\x6c\x65\x6d\
+\x65\x6e\x74\x42\x79\x49\x64\x28\x27\x6c\x69\x6e\x65\x45\x64\x69\
+\x74\x27\x29\x2e\x70\x6c\x61\x63\x65\x68\x6f\x6c\x64\x65\x72\x20\
+\x3d\x20\x70\x72\x6f\x76\x69\x64\x65\x72\x3b\x0a\x20\x20\x20\x20\
+\x20\x20\x20\x20\x20\x20\x20\x20\x7d\x29\x3b\x0a\x0a\x20\x20\x20\
+\x20\x20\x20\x20\x20\x20\x20\x20\x20\x2f\x2f\x20\x54\x72\x79\x20\
+\x74\x6f\x20\x63\x68\x61\x6e\x67\x65\x20\x74\x68\x65\x20\x64\x69\
+\x72\x65\x63\x74\x69\x6f\x6e\x20\x6f\x66\x20\x74\x68\x65\x20\x70\
+\x61\x67\x65\x3a\x0a\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\
+\x20\x20\x76\x61\x72\x20\x6e\x65\x77\x44\x69\x72\x20\x3d\x20\x27\
+\x40\x51\x54\x5f\x4c\x41\x59\x4f\x55\x54\x5f\x44\x49\x52\x45\x43\
+\x54\x49\x4f\x4e\x40\x27\x3b\x0a\x20\x20\x20\x20\x20\x20\x20\x20\
+\x20\x20\x20\x20\x6e\x65\x77\x44\x69\x72\x20\x3d\x20\x6e\x65\x77\
+\x44\x69\x72\x2e\x74\x6f\x4c\x6f\x77\x65\x72\x43\x61\x73\x65\x28\
+\x29\x3b\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\
+\x66\x20\x28\x28\x6e\x65\x77\x44\x69\x72\x20\x21\x3d\x20\x27\x6c\
+\x74\x72\x27\x29\x20\x26\x26\x20\x28\x6e\x65\x77\x44\x69\x72\x20\
+\x21\x3d\x20\x27\x72\x74\x6c\x27\x29\x29\x0a\x20\x20\x20\x20\x20\
+\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x6e\x65\x77\x44\x69\
+\x72\x20\x3d\x20\x27\x6c\x74\x72\x27\x3b\x0a\x20\x20\x20\x20\x20\
+\x20\x20\x20\x20\x20\x20\x20\x64\x6f\x63\x75\x6d\x65\x6e\x74\x2e\
+\x67\x65\x74\x45\x6c\x65\x6d\x65\x6e\x74\x73\x42\x79\x54\x61\x67\
+\x4e\x61\x6d\x65\x28\x27\x62\x6f\x64\x79\x27\x29\x5b\x30\x5d\x2e\
+\x73\x65\x74\x41\x74\x74\x72\x69\x62\x75\x74\x65\x28\x0a\x20\x20\
+\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x27\x64\
+\x69\x72\x27\x2c\x20\x6e\x65\x77\x44\x69\x72\x29\x3b\x0a\x20\x20\
+\x20\x20\x20\x20\x20\x20\x7d\x0a\x0a\x20\x20\x20\x20\x20\x20\x20\
+\x20\x66\x75\x6e\x63\x74\x69\x6f\x6e\x20\x66\x6f\x72\x6d\x53\x75\
+\x62\x6d\x69\x74\x74\x65\x64\x28\x29\x0a\x20\x20\x20\x20\x20\x20\
+\x20\x20\x7b\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\
+\x76\x61\x72\x20\x73\x74\x72\x69\x6e\x67\x20\x3d\x20\x6c\x69\x6e\
+\x65\x45\x64\x69\x74\x2e\x76\x61\x6c\x75\x65\x3b\x0a\x0a\x20\x20\
+\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x66\x20\x28\x73\x74\
+\x72\x69\x6e\x67\x2e\x6c\x65\x6e\x67\x74\x68\x20\x3d\x3d\x20\x30\
+\x29\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\
+\x20\x20\x72\x65\x74\x75\x72\x6e\x3b\x0a\x0a\x20\x20\x20\x20\x20\
+\x20\x20\x20\x20\x20\x20\x20\x65\x78\x74\x65\x72\x6e\x61\x6c\x2e\
+\x73\x74\x61\x72\x74\x50\x61\x67\x65\x2e\x73\x65\x61\x72\x63\x68\
+\x55\x72\x6c\x28\x73\x74\x72\x69\x6e\x67\x2c\x20\x66\x75\x6e\x63\
+\x74\x69\x6f\x6e\x28\x75\x72\x6c\x29\x20\x7b\x0a\x20\x20\x20\x20\
+\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x77\x69\x6e\x64\
+\x6f\x77\x2e\x6c\x6f\x63\x61\x74\x69\x6f\x6e\x2e\x68\x72\x65\x66\
+\x20\x3d\x20\x75\x72\x6c\x3b\x0a\x20\x20\x20\x20\x20\x20\x20\x20\
+\x20\x20\x20\x20\x7d\x29\x3b\x0a\x20\x20\x20\x20\x20\x20\x20\x20\
+\x7d\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x0a\x20\x20\x20\x20\x20\
+\x20\x20\x20\x2f\x2f\x20\x49\x6e\x69\x74\x69\x61\x6c\x69\x7a\x65\
+\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x69\x66\x20\x28\x77\x69\x6e\
+\x64\x6f\x77\x2e\x65\x78\x74\x65\x72\x6e\x61\x6c\x29\x20\x7b\x0a\
+\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x75\x70\x64\x61\
+\x74\x65\x28\x29\x3b\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x7d\x20\
+\x65\x6c\x73\x65\x20\x7b\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\
+\x20\x20\x20\x64\x6f\x63\x75\x6d\x65\x6e\x74\x2e\x61\x64\x64\x45\
+\x76\x65\x6e\x74\x4c\x69\x73\x74\x65\x6e\x65\x72\x28\x27\x5f\x65\
+\x72\x69\x63\x5f\x65\x78\x74\x65\x72\x6e\x61\x6c\x5f\x63\x72\x65\
+\x61\x74\x65\x64\x27\x2c\x20\x75\x70\x64\x61\x74\x65\x29\x3b\x0a\
+\x20\x20\x20\x20\x20\x20\x20\x20\x7d\x0a\x20\x20\x20\x20\x3c\x2f\
+\x73\x63\x72\x69\x70\x74\x3e\x0a\x3c\x2f\x68\x65\x61\x64\x3e\x0a\
+\x3c\x62\x6f\x64\x79\x20\x6f\x6e\x6c\x6f\x61\x64\x3d\x22\x64\x6f\
+\x63\x75\x6d\x65\x6e\x74\x2e\x66\x6f\x72\x6d\x73\x5b\x30\x5d\x2e\
+\x6c\x69\x6e\x65\x45\x64\x69\x74\x2e\x73\x65\x6c\x65\x63\x74\x28\
+\x29\x3b\x22\x3e\x0a\x20\x20\x20\x20\x3c\x64\x69\x76\x20\x69\x64\
+\x3d\x22\x68\x65\x61\x64\x65\x72\x22\x3e\x0a\x20\x20\x20\x20\x20\
+\x20\x20\x20\x3c\x68\x31\x3e\x40\x48\x45\x41\x44\x45\x52\x5f\x54\
+\x49\x54\x4c\x45\x40\x3c\x2f\x68\x31\x3e\x0a\x20\x20\x20\x20\x20\
+\x20\x20\x20\x3c\x69\x6d\x67\x20\x73\x72\x63\x3d\x22\x40\x49\x4d\
+\x41\x47\x45\x40\x22\x20\x77\x69\x64\x74\x68\x3d\x22\x33\x32\x22\
+\x20\x68\x65\x69\x67\x68\x74\x3d\x22\x33\x32\x22\x2f\x3e\x0a\x20\
+\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x3c\x64\
+\x69\x76\x20\x69\x64\x3d\x22\x73\x65\x61\x72\x63\x68\x22\x3e\x0a\
+\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x66\x6f\x72\x6d\x20\x61\x63\
+\x74\x69\x6f\x6e\x3d\x22\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\
+\x3a\x66\x6f\x72\x6d\x53\x75\x62\x6d\x69\x74\x74\x65\x64\x28\x29\
+\x3b\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\
+\x3c\x66\x69\x65\x6c\x64\x73\x65\x74\x3e\x0a\x20\x20\x20\x20\x20\
+\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x69\x6e\x70\x75\
+\x74\x20\x69\x64\x3d\x22\x6c\x69\x6e\x65\x45\x64\x69\x74\x22\x20\
+\x6e\x61\x6d\x65\x3d\x22\x6c\x69\x6e\x65\x45\x64\x69\x74\x22\x20\
+\x74\x79\x70\x65\x3d\x22\x74\x65\x78\x74\x22\x20\x2f\x3e\x0a\x20\
+\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\
+\x69\x6e\x70\x75\x74\x20\x74\x79\x70\x65\x3d\x22\x73\x75\x62\x6d\
+\x69\x74\x22\x20\x76\x61\x6c\x75\x65\x3d\x22\x40\x53\x55\x42\x4d\
+\x49\x54\x40\x22\x2f\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\
+\x20\x20\x20\x3c\x2f\x66\x69\x65\x6c\x64\x73\x65\x74\x3e\x0a\x20\
+\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x66\x6f\x72\x6d\x3e\x0a\x20\
+\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x3c\x64\
+\x69\x76\x20\x69\x64\x3d\x22\x66\x6f\x6f\x74\x65\x72\x22\x3e\x3c\
+\x2f\x64\x69\x76\x3e\x0a\x3c\x2f\x62\x6f\x64\x79\x3e\x0a\x3c\x2f\
+\x68\x74\x6d\x6c\x3e\x0a\
+"
+
+qt_resource_name = b"\
+\x00\x04\
+\x00\x06\xfb\x3c\
+\x00\x68\
+\x00\x74\x00\x6d\x00\x6c\
+\x00\x12\
+\x06\x3e\x39\x5c\
+\x00\x73\
+\x00\x70\x00\x65\x00\x65\x00\x64\x00\x64\x00\x69\x00\x61\x00\x6c\x00\x50\x00\x61\x00\x67\x00\x65\x00\x2e\x00\x68\x00\x74\x00\x6d\
+\x00\x6c\
+\x00\x10\
+\x0b\xe9\x1d\x9c\
+\x00\x61\
+\x00\x64\x00\x62\x00\x6c\x00\x6f\x00\x63\x00\x6b\x00\x50\x00\x61\x00\x67\x00\x65\x00\x2e\x00\x68\x00\x74\x00\x6d\x00\x6c\
+\x00\x0e\
+\x08\x97\xc9\x7c\
+\x00\x73\
+\x00\x74\x00\x61\x00\x72\x00\x74\x00\x50\x00\x61\x00\x67\x00\x65\x00\x2e\x00\x68\x00\x74\x00\x6d\x00\x6c\
+"
+
+qt_resource_struct = b"\
+\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
+\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x02\
+\x00\x00\x00\x0e\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\
+\x00\x00\x00\x5e\x00\x00\x00\x00\x00\x01\x00\x00\x16\x1a\
+\x00\x00\x00\x38\x00\x00\x00\x00\x00\x01\x00\x00\x12\x9a\
+"
+
+def qInitResources():
+    QtCore.qRegisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data)
+
+def qCleanupResources():
+    QtCore.qUnregisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data)
+
+qInitResources()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/data/icons.qrc	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,17 @@
+<!DOCTYPE RCC>
+<RCC version="1.0">
+<qresource>
+  <file>icons/adBlockPlus16.png</file>
+  <file>icons/adBlockPlus64.png</file>
+  <file>icons/ericWeb16.png</file>
+  <file>icons/ericWeb32.png</file>
+  <file>icons/box-border-small.png</file>
+  <file>icons/brokenPage.png</file>
+  <file>icons/close.png</file>
+  <file>icons/edit.png</file>
+  <file>icons/loading.gif</file>
+  <file>icons/plus.png</file>
+  <file>icons/reload.png</file>
+  <file>icons/setting.png</file>
+</qresource>
+</RCC>
Binary file WebBrowser/data/icons/adBlockPlus16.png has changed
Binary file WebBrowser/data/icons/adBlockPlus64.png has changed
Binary file WebBrowser/data/icons/box-border-small.png has changed
Binary file WebBrowser/data/icons/brokenPage.png has changed
Binary file WebBrowser/data/icons/close.png has changed
Binary file WebBrowser/data/icons/edit.png has changed
Binary file WebBrowser/data/icons/ericWeb16.png has changed
Binary file WebBrowser/data/icons/ericWeb32.png has changed
Binary file WebBrowser/data/icons/loading.gif has changed
Binary file WebBrowser/data/icons/plus.png has changed
Binary file WebBrowser/data/icons/reload.png has changed
Binary file WebBrowser/data/icons/setting.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/data/icons_rc.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,2034 @@
+# -*- coding: utf-8 -*-
+
+# Resource object code
+#
+# Created by: The Resource Compiler for PyQt5 (Qt v5.6.0)
+#
+# WARNING! All changes made in this file will be lost!
+
+from PyQt5 import QtCore
+
+qt_resource_data = b"\
+\x00\x00\x0c\x69\
+\x89\
+\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
+\x00\x00\x20\x00\x00\x00\x20\x08\x06\x00\x00\x00\x73\x7a\x7a\xf4\
+\x00\x00\x00\x06\x62\x4b\x47\x44\x00\xff\x00\xff\x00\xff\xa0\xbd\
+\xa7\x93\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x2e\x23\x00\x00\
+\x2e\x23\x01\x78\xa5\x3f\x76\x00\x00\x00\x07\x74\x49\x4d\x45\x07\
+\xde\x03\x0c\x12\x05\x12\x78\xfa\xe0\x84\x00\x00\x0b\xf6\x49\x44\
+\x41\x54\x58\xc3\x9d\xd7\x69\x90\x5c\xd5\x75\xc0\xf1\xff\x7d\xef\
+\x75\xbf\x5e\xa6\x7b\xf6\xa5\x47\xcc\x22\x89\x19\x49\xa3\x05\x09\
+\x89\x45\x80\x89\x01\xe1\x24\x88\xb0\x83\x65\x4a\x04\x41\x22\x30\
+\xd8\xae\x80\x29\x8c\x01\x83\x08\x81\xc4\xd8\x89\x31\x01\x1b\x03\
+\xe5\x38\x18\x0a\x01\x02\x04\x48\x48\x02\x8d\x24\x04\x48\x62\x98\
+\xd1\x3e\x1b\x33\x9a\x9e\x7d\xed\xe9\x6d\xe6\x75\xbf\x5e\xde\x7b\
+\x37\x1f\x08\x95\x38\x89\xcb\x84\xfb\xf5\xde\x3a\xe7\x57\xe7\xd4\
+\xbd\x75\x8f\xe0\x6b\xac\x4d\x0f\x2d\xa5\x76\xe1\x49\x4e\x1c\xbc\
+\x91\xfa\xc6\x1a\x7a\x3a\x5b\xc5\xb3\xcf\xed\x95\xff\xf3\x9c\x94\
+\x52\x00\xee\x9e\xfd\x5b\xf4\x4f\xc3\x9f\xb8\xae\x1b\x7a\x2a\xee\
+\x7f\x44\x38\x5f\xee\x9f\xd3\xd8\x84\xf8\x3a\x80\xf6\xc9\x66\xf6\
+\x6e\x69\x51\x57\x5e\x7e\x15\xe7\xd7\x2f\xb6\xbf\x4c\xf6\xdb\xb7\
+\x1e\xac\xde\xd5\xbc\xf7\xbc\xb1\xd1\xd8\x19\x65\x41\xf3\x74\x33\
+\x6f\x56\xba\x82\x35\xa5\x8d\x73\x8b\x0a\xaa\xfc\x51\xaf\xab\x24\
+\x7b\x2a\x40\xf5\xd6\x8d\x45\xb7\x3e\xb5\x69\xe7\xeb\xf2\xd1\x5f\
+\x6f\x73\xbe\x12\xa0\xf5\xf0\x0b\x9c\xb5\x72\x23\xef\x3e\x7b\x37\
+\x57\xdc\xf1\x24\x00\xef\x6f\x7b\x3c\x60\xc8\x9a\x79\x71\xd3\x5c\
+\x39\x15\x99\xf8\x96\xdb\x23\x56\xf9\x5c\x95\xf5\xc5\xa5\x73\xd4\
+\xda\xe5\x75\x0c\x4d\x4d\x49\xd5\x6b\x39\x5d\xc7\x5f\x45\xe4\xdf\
+\x91\x65\xe2\x5c\x8c\xa1\x3c\xed\xc7\x5a\xb5\xd8\xa8\xff\x27\xdb\
+\x0f\x8c\x3e\xfe\x97\x97\x56\x09\xed\x4f\x25\x7f\xf5\x99\xbb\x39\
+\x6b\xe5\x46\x9e\x7e\x78\xbd\x72\xc5\x1d\x4f\x3a\x72\xf8\x2d\xdf\
+\x0b\xfb\x0f\xdf\xd3\xdc\x62\xdc\x92\x2f\x1a\xac\x4e\xd6\x09\xbd\
+\x62\xd1\x14\x8b\xab\xeb\xa8\x58\x70\x36\x1d\xf1\x23\x52\xcc\xa6\
+\xf9\xf0\x95\x87\xc5\xca\x9a\x90\x9a\xf1\x16\x51\x10\xf5\xd0\x4e\
+\x07\x8d\x91\x69\x2a\x64\x9a\xa4\x66\x5f\x01\x3c\x9e\x68\x9f\x40\
+\xfd\x63\x89\xff\xe5\x67\xff\x8a\x35\xfe\x09\x75\x73\x42\xec\xf8\
+\xb8\x9d\x1d\x1f\x1e\xc7\x8e\x1c\x5c\xbf\xb3\xa3\x7f\xf3\x9e\x7d\
+\x9d\xd7\x9d\x79\xc6\x77\x02\x3b\xb7\xec\xd1\x56\xcc\xd1\x88\xe7\
+\xc6\x45\x7e\xdc\xcf\xf0\x47\xbf\x67\xb2\x3b\x2c\x3c\x89\x29\xd1\
+\xd9\x6b\xf0\xad\x6f\xfc\x15\x71\xb3\x0f\x23\x61\x20\x47\x92\x4c\
+\x77\x65\x49\xc4\x35\xbc\xba\xda\x7e\x2c\x9c\x7b\xb9\x61\x39\xe2\
+\x8f\x02\x8a\x6b\x06\xd9\xfe\xfe\x28\x3b\x3e\x6e\x67\x72\xf8\xb5\
+\xd0\x3d\xb7\xdf\xf6\xa2\xd3\xd4\xf4\x80\x6b\xce\x92\xd2\xd6\x3d\
+\xcd\xb2\xad\xb5\x4d\x1d\x1d\xee\x16\xc7\x3e\x3b\x22\x2e\xb8\xfa\
+\x9b\x68\x5a\x8a\x0b\xaf\x6a\x61\x66\x50\xa7\xf8\xea\x12\x1a\x8b\
+\x1b\xb1\x74\x49\xb4\xf5\x1d\x06\x0e\xc6\xf1\x44\x6d\x3e\xea\xd3\
+\x30\x4c\x89\xcb\xe7\xdb\xda\x35\x98\xde\x7d\xf1\x92\xda\xff\xbb\
+\x05\x5b\x76\x3d\x44\x77\xcf\x29\xf1\x1a\x9d\xf2\x8e\xf5\x2b\xe6\
+\xdd\x79\xff\xb3\x3b\x1a\x2e\xf8\xf6\x02\xcb\x56\x9d\x89\xc9\x76\
+\xc5\x8c\x27\x44\x2a\x9d\xc0\x4a\x5b\xb8\x8a\x21\x54\x73\x08\x99\
+\xf7\xa3\x16\xf9\xc8\x27\xf2\xec\xde\x32\x4c\x49\xbc\x8b\x89\xee\
+\x18\x75\x39\x95\xfe\xa1\x2c\xd2\xed\x62\xd1\x0a\x0d\x99\x53\x29\
+\x2f\x0e\x66\x21\xc2\x9c\x79\xa5\xfc\x2f\xc0\xfb\xfb\x7e\xc1\x9f\
+\x5f\xf4\x43\x00\xf9\xc8\x73\x1b\xe6\xa5\x44\xd9\xae\x0b\x37\xfc\
+\xa0\x21\x26\x5d\xb2\xe7\xcd\x37\x15\x4e\x66\x09\xce\xab\x66\xea\
+\x40\x17\x59\xd3\xe1\xcc\x4b\x16\xd1\x7b\xdc\x43\x6f\x74\x12\xff\
+\xaa\x85\xb8\x16\x98\xa4\xdf\x8d\xd2\xb4\xa0\x92\xc8\x64\x94\x53\
+\x8e\x8f\xb2\x15\x3a\xb9\xe4\x14\xa6\x61\x4b\x69\xe7\x05\x41\x33\
+\x0c\xe0\xd5\x90\x7f\x00\xd8\x75\xe8\x31\x56\x2e\x0c\x01\x88\xad\
+\xbf\xba\xad\xb8\xdd\xa3\xbe\xa1\x2e\xbd\x78\x7e\xf3\x6f\x9a\xb1\
+\xdb\x77\x8b\x3b\xef\xbb\x9a\x77\xd2\x0b\x29\x58\xae\xe0\xf7\x29\
+\x14\x04\x73\x2c\x38\xdf\xc3\xd2\xe5\x59\xa2\xaf\x64\xe9\x7b\x77\
+\x12\xe1\xd5\x29\xcd\xcc\xf2\xf6\xef\xc3\x84\x96\x95\x60\x99\x51\
+\x88\x66\xf1\xe6\x05\x16\xc2\xd1\x85\x6b\x36\x6d\xa6\x9b\xd7\x9f\
+\x13\xd4\x56\xaf\xff\xae\xa5\xfc\x77\xc0\x87\x67\x64\x28\x2b\xfe\
+\x0e\x52\x1e\xf1\x0c\xcf\x69\x7a\x31\xd6\x5f\xb5\x22\xb1\xe5\x03\
+\x71\xd3\xba\x6a\x6e\x7e\x6e\x35\x29\xdf\x11\x26\xe3\x2d\x5c\x74\
+\xd9\x02\x54\xe9\x27\xd2\x6f\x90\xf5\x14\x32\x10\x49\x11\x3a\xdd\
+\x47\xdb\xd6\x51\xa6\xf7\x45\x39\x70\x60\x94\xb3\x6e\x29\xc5\x65\
+\x0c\x91\xed\xcd\x92\x8d\x48\x84\x25\x65\x36\x9d\x57\x35\x32\xcf\
+\xff\x76\x6b\x62\xf8\xd2\x1b\xd6\xda\xdf\x5c\x79\x1b\x7f\x00\x58\
+\xd9\x69\x08\x80\xef\x3d\xfd\xf4\x63\xa9\x33\x4b\x2e\xff\x9b\x4d\
+\x77\x48\xad\x41\x13\x2d\xf4\x71\x64\xcf\x00\xff\xfe\x8b\x66\x0a\
+\xbd\x69\xe2\x6d\xc7\x29\xaf\x4e\xb2\x6a\xcd\x62\xd4\xe1\x71\xd2\
+\xfd\x0e\xda\xac\x45\xa4\x67\x96\xf0\x64\x84\x92\x46\x1f\x7d\xcd\
+\xc3\xe4\xc6\x4d\x8a\x55\x0b\xb7\x96\x93\x66\xca\x11\x5e\xd5\xf9\
+\x20\xe4\x3d\xed\x51\x80\x9b\xef\xd9\x2c\x85\x10\xff\x05\x78\xf3\
+\xf9\xb5\x44\x56\x3e\x29\xde\x69\x7e\xf0\xa2\x40\x49\xc1\x0f\x2a\
+\x6b\xca\x9d\x18\x1d\xa2\xf3\xbd\x4f\x68\x7f\xe2\x55\xc6\xfb\x7a\
+\xc8\xcf\x58\xc8\x78\x86\xf0\xbe\x23\x0c\x75\x24\x09\x06\x26\x69\
+\xdb\x3e\xcd\xe8\x87\x49\x4e\x1c\x1c\x46\x0b\xa4\x69\x5a\x39\x4b\
+\x4d\x60\x9a\xd6\x0f\x0c\xcc\x8c\x64\x26\x2d\xe4\xac\xe5\x16\xaa\
+\x87\xed\x4d\x73\x8a\xd6\xfd\x6c\xdb\x40\xea\x87\x57\x96\xb1\xeb\
+\xb5\xfb\x91\x52\x7e\xf1\x14\x4b\x29\x69\xfc\xe9\x06\x7a\xef\x7f\
+\x91\x47\xde\xbb\x6f\xef\x8c\x2c\xbe\x68\xf2\x78\x0b\x7a\x75\x00\
+\x7d\x7a\x14\x19\x8b\x12\x19\x48\x90\x30\x24\x73\x1a\x5d\xd4\x94\
+\x2b\x7c\xb2\x3b\x42\x55\xa9\x1b\x05\x87\x7d\x9f\x65\xb9\xe1\xde\
+\x4b\x18\x3f\xba\x8c\xf2\x82\x2a\x7c\x7e\xc1\x74\x24\x4b\x6f\x77\
+\x1f\x9a\xd9\x42\xb1\x7f\x78\xf7\xa6\x0d\x63\x57\xae\xd8\x80\x09\
+\xf0\xc2\x83\x6b\xd8\xf8\x78\x33\x00\x62\xac\xeb\x7a\xaa\x9f\xd9\
+\x02\xbf\x82\x17\xdb\x36\xad\x0d\x5b\xfe\xed\xd1\xf1\x01\x99\xee\
+\x3e\x24\x12\x1d\xe0\xf8\x6c\x96\xaf\xae\xc0\xb6\x0c\x5a\xde\x1b\
+\x64\x51\x7d\x31\x66\x7c\x94\x63\x47\x25\x45\x67\x78\xa8\x2c\xbc\
+\x86\x50\xf1\x25\x2c\x6a\x6a\xa0\xbf\x3f\xca\x74\xcc\x60\x74\x34\
+\x81\x23\x05\x1e\xbf\x8e\xe3\x38\xa0\x68\x13\xdd\xed\x43\x6f\x77\
+\xb4\xdc\xf3\xfd\xda\x85\xbf\x53\x86\xba\x6f\xc9\xc3\x8d\xc0\x2b\
+\x5f\x54\xa0\x79\xbc\x47\xb9\xa4\xaa\x41\xfe\xe3\x89\x3d\x3b\x7a\
+\x0f\xff\xf3\xa5\x25\x89\xa8\x9a\xb4\x7d\x2c\xac\x9d\xe0\xd8\xdb\
+\x06\x99\x1c\x14\xf8\x54\x06\x07\xe2\xd8\x45\x6e\x16\x5f\xb2\x84\
+\x91\x6d\x93\x2c\x3b\xff\x61\x6a\x6a\xeb\x59\x75\xde\x3c\x0e\x1f\
+\x1d\xe5\x54\x7f\x94\x83\x9f\x0d\x53\x3d\xb7\x9c\xb2\xd2\x02\xc6\
+\xc2\x63\xb8\x55\x55\x56\xd5\x97\x4b\x3b\x6f\x29\x47\x0e\xf5\x9d\
+\x6a\xdb\xb3\xfd\x1c\xdb\xd9\x19\xfb\xb2\xf5\xaa\x74\x76\xf3\x8d\
+\xc7\x3e\x57\xd6\x2c\x34\x2e\x38\xd0\x71\xfc\x1f\x26\x3e\xfd\x50\
+\x58\x33\x51\x31\x71\x62\x9c\xc8\x60\x1c\x43\xd1\x39\xd4\x92\xa4\
+\xf0\xdc\x6a\xea\xce\xaa\x62\x3c\x61\x12\x8a\x4b\xa4\xeb\x7b\x2c\
+\x5b\xb5\x84\xf1\x69\x83\xb9\xa7\x57\x22\x5c\x6e\x4c\x07\xbc\x01\
+\x0f\xe1\xfe\x08\xab\xcf\x9b\x8f\x2a\x1c\xdc\xba\x5b\x0c\x8d\xc4\
+\x44\xd6\xb4\xe4\x92\x15\xb5\xa5\xf1\x19\xcf\x35\x33\xd1\xba\x57\
+\x42\x8d\xeb\x72\x33\x91\xdd\x52\x8d\x35\xcd\x13\x7b\xee\xfe\x3b\
+\x67\xf9\xc6\xeb\x9f\xb7\x0b\xaa\x4e\x0f\xef\x7f\x4f\x24\xa6\x66\
+\x28\x52\xb3\x8c\x25\xdd\x04\xfd\x82\xb9\xc5\x92\x44\x77\x8c\xf1\
+\xce\x29\x42\xc2\x21\x6b\xaf\x63\xcd\xda\x8b\xe9\x0a\x4f\xb3\x78\
+\x49\x2d\xe5\x15\x41\x3c\xba\xc6\x6b\xdb\x4e\x32\x93\xb5\xc9\xda\
+\x0e\x86\x99\xe7\xed\x0f\x7a\x88\xc4\xd3\x8c\x4d\x18\x08\x4d\x15\
+\x81\x42\x9f\x2c\x2c\xf6\x95\xa6\x66\xad\x86\xc1\x8e\xfb\x5f\xfd\
+\xfe\x4f\x76\xa2\x7e\xf6\xc6\x7e\xa4\x94\x0b\xba\xc6\x4e\x3e\xd9\
+\xde\xb2\x9f\x74\x64\x90\xe8\xe7\x11\x66\x92\x50\xe6\x36\xc9\xc4\
+\xb2\x18\x69\x1b\x49\x1e\xb7\x22\x49\xc7\xbd\xcc\x5b\x72\x33\x8a\
+\xee\x45\x75\xeb\x5c\x75\xf9\x62\xa6\x12\x19\x62\x86\xc5\xe4\x6c\
+\x8e\xba\xfa\x72\x4c\x4b\x22\x2d\x8b\xa8\x61\xb1\xb4\xb1\x8c\xb1\
+\x98\x41\xda\xcc\x63\x66\x2c\x71\xe4\xd0\x29\x67\x6e\x7d\x59\x93\
+\xaa\x9d\xbd\xff\x83\xb7\xfe\x76\x40\x01\xd8\x4b\xeb\xb5\x9d\x13\
+\x27\xa8\x3a\xa3\x51\xca\x60\x29\x96\x57\xc5\xe5\x86\xf1\x88\x20\
+\x9d\x92\x90\xb3\xd0\x73\x92\x12\x04\x85\x15\x6b\x31\x6d\x1f\x6e\
+\x8f\xce\x95\x97\x35\x31\x38\x6e\xa0\x6a\x2a\x52\x53\x58\xb6\xb8\
+\x9a\x92\x12\x3f\x2e\xbf\x4e\x6b\x7f\x9c\xe2\x32\x1f\x6d\x7d\x71\
+\x54\x8f\x8e\xd4\x54\x14\xdd\x45\xc3\x8a\x79\x8a\xbb\xbc\xc8\x2a\
+\x2e\x2b\xb8\x17\x40\x91\xb2\x4b\x3b\x78\x60\xe8\xda\x79\xf3\x57\
+\xdb\x89\xf0\x5e\x11\xd2\x62\xa0\x7a\x30\xd2\x12\x2b\xab\x60\xa3\
+\x60\xce\x4a\x62\xb3\x79\xf4\xc2\x4a\xfa\x23\x73\x51\x5d\x2a\xbd\
+\xc3\x09\xa2\x33\x59\x84\xaa\x90\xb6\x24\x8e\x94\xd4\xcd\x09\x72\
+\x6a\x24\x4e\x5b\x6f\x04\x4f\xc0\x4b\x4a\x0a\xdc\x01\x1d\xe1\x71\
+\x63\xab\x0a\x86\x69\x51\x16\x2a\x22\x58\x12\x74\xf4\x02\xef\x0a\
+\x9f\xff\xf6\xa0\x02\xb5\xee\x50\xb9\xed\x3b\xb1\xef\x0d\xc2\xc7\
+\xc7\xe8\x09\x27\xf0\x3a\x79\x3c\x1e\x70\x34\x87\x78\x22\x47\x36\
+\xe3\x60\x49\x08\x0f\xfb\x51\x5d\x5e\x3e\x6a\x1b\xe2\x70\x77\x84\
+\xb8\x91\xc3\xe5\x52\xc1\x91\x54\x96\xfa\xe8\x1c\x88\xe2\xd6\x15\
+\x2e\x5a\x75\x1a\x81\x22\x1f\x8e\xc7\x85\xe2\x75\xa3\x78\x34\x54\
+\x8f\x0b\x97\x57\x23\x14\x0a\x52\x56\xec\xd3\x3c\x01\x6f\x95\xcb\
+\x5b\x58\xa3\xdd\xfb\xd2\xbd\x7a\x64\x4a\x0f\x56\x35\x14\xc8\x92\
+\xae\x31\x26\x27\xa2\x08\xc7\x21\xe3\x08\x90\x0e\x45\x3e\x15\x1c\
+\x07\x97\x90\xa4\x33\x2e\xf4\x72\x1f\xb1\x94\x85\x91\xc8\x10\x9b\
+\xcd\x62\x0c\xc6\x69\xeb\x9d\xa4\xaa\xa2\x10\x29\x25\x15\xa5\x05\
+\x58\x52\xd2\x3e\x99\xc6\x23\x21\x9f\xcd\x83\x10\x38\x36\x28\xba\
+\x1b\xd5\xe5\x22\x3e\x6d\x08\x45\x75\x09\x29\x9d\x02\xe5\xd1\x9b\
+\x1e\xcb\x5f\xb6\xfe\xaf\x33\x9f\x6f\x7b\x97\x70\x67\x04\x47\x75\
+\x23\x4d\x9b\x60\x26\x87\xae\x0b\xbc\xaa\x83\xb0\x41\x45\x52\x5a\
+\xee\x23\x1a\x37\x51\x5d\x0a\x1e\x8f\x8b\xcd\xbb\x7b\xf0\xfb\x5d\
+\x2c\x6f\xac\xc2\x01\x3a\x22\x26\x6f\x75\x44\x18\x4d\xe5\x99\xb6\
+\x00\xaf\x0b\x45\x77\x23\x3c\x6e\xd0\x14\x14\x45\x92\xfb\xcf\x1b\
+\x12\xf0\x29\xd8\x12\x5b\xf1\x89\x12\xe3\xe0\x4b\xef\x1e\x4d\x4e\
+\x98\x4e\x62\x22\x67\x9b\xa3\xb3\xd2\x98\x91\x8e\x21\x1d\x47\x98\
+\x8e\x4c\x1b\xc2\xd1\x14\xe1\x48\x5c\x96\x65\xcf\xd8\x39\x33\x4d\
+\x26\xeb\x90\x73\x1c\xea\x43\x01\xee\x7a\xae\x85\x68\x2a\xc3\xe1\
+\x51\x83\x13\xd3\x26\x8e\xdb\xcd\xa7\x63\x69\xbc\x01\x0f\x8a\x47\
+\x47\x78\x34\x50\x24\x9a\x47\xc5\xb6\x6c\x8e\x1e\x1b\x64\x2c\x3c\
+\xe1\xc4\xa6\xe2\x59\x4d\x53\x93\x2a\xc0\x8f\x36\xde\xd0\x57\xa0\
+\x67\xce\x12\x96\x93\xd3\x35\x8f\xad\x8b\x6c\x4e\x08\xd5\x0c\xea\
+\x5a\x3a\x18\xd0\x92\x5e\xaf\x16\x2b\x0a\x16\x0e\x4b\xd2\x71\x83\
+\xe5\xe5\x9a\x37\x88\xab\xc0\xc3\xe1\x91\x59\x36\xac\x5d\xc8\x96\
+\xa3\x11\x4e\xab\x2e\xe0\xd2\xa5\xd5\x14\x06\x74\x72\x8a\x4a\x56\
+\x7e\x51\x35\xc7\x01\xcb\x16\x14\x79\x34\x1c\x33\xcf\xd4\x68\x0c\
+\x3b\x95\xb2\x72\xc9\xcc\xc4\x48\x4f\xf3\xa3\xda\x1d\xdf\xbd\x8e\
+\x6b\x6f\xb8\xf3\xf0\xe6\xcd\x3f\xbe\xcb\x91\xc1\x15\xf9\xac\xed\
+\xc9\x18\x49\x19\x9d\x1c\x15\xaa\xc4\x2a\xf0\xea\xf9\xbc\x9d\x9b\
+\x01\x91\x12\xb6\x11\x48\x84\xc7\x7e\xda\x9f\x2a\xa9\x2a\x2a\xf6\
+\x71\x7a\x28\x40\xef\xb8\x41\x59\xb1\x07\xa1\x69\x7c\xa3\xa1\x94\
+\x78\xd6\xe1\xbc\xac\xc3\xa9\x58\x9a\xf7\x8e\x8f\x93\xcf\xda\x54\
+\x97\xf8\x10\xb3\xb3\x84\x13\x29\xb0\x1d\x7c\x2e\xb7\x2b\x93\x4c\
+\xed\x82\x4f\x2c\xed\xd9\xdf\xbc\x41\x5f\xdf\x47\x62\xfe\xfc\x0b\
+\x0f\x00\x07\xfe\xd4\x37\xfd\xc6\x35\x0b\x9a\xa6\x66\x72\x3f\x32\
+\x53\x59\x99\x4d\x98\x22\x35\x92\x64\xe9\xd2\x10\x4d\xa1\x20\x42\
+\x51\x40\x48\x6c\x45\x50\x51\xa8\xb3\x61\x75\x0d\x89\x64\x86\x13\
+\x5d\xa3\xb4\x9e\x4a\xa3\x3a\xe0\x38\x8e\xc4\xc9\x8b\xb1\xd1\xe8\
+\xcf\x01\xb4\xd7\x5f\xdf\xc4\xfc\xf9\x17\xca\x97\x5f\x7e\x80\xc1\
+\xa1\x09\x91\x33\x2d\xf2\x66\x86\xf8\xf4\x08\xd2\xb2\x28\xf0\xea\
+\xe4\x6d\x0b\x97\x92\x93\xc6\x4c\xa3\xd6\x54\x35\xf5\xb4\xd9\x97\
+\x58\xd7\x66\x14\x86\x74\x45\x6a\x32\x97\x17\x9d\x43\x49\xdc\x85\
+\x7e\x16\xa5\x73\x94\x78\xdd\x08\x55\xa5\x48\x57\x48\x9b\x79\x66\
+\x67\x14\x8e\x9c\x8a\xe1\xb1\x2c\xf2\x8e\xb4\x15\x29\xd5\x89\xa1\
+\xd8\xc3\xe9\xe8\x2f\xfb\xe6\x2e\xfd\x27\xf1\x95\x47\xb3\x7b\xbf\
+\xfd\x67\xfc\xfc\xb5\xfd\x00\x1c\xde\xff\xf6\x4d\xcf\x6f\x9e\xfc\
+\xfb\x8f\xa3\x05\x73\xa9\x08\x3a\xd2\xef\x51\x28\xf2\x71\xf5\xb9\
+\xb5\x5c\xbf\xac\x0a\x97\x22\xc8\x58\x36\x3d\xe3\x06\xad\xdd\x13\
+\xec\xf8\x38\x8c\x9a\xb3\x9d\xfc\x54\x54\x31\x7a\xc7\x5e\x71\x3a\
+\x1e\xba\x29\x5b\x79\x1f\x89\xc9\x27\x1c\xf5\xab\x02\x0e\x76\x0c\
+\xf2\xe0\xad\xeb\x90\x43\x23\xd4\x3d\xfa\xbb\x93\x37\xcf\x4f\xfd\
+\xdb\xce\x9d\xfd\xae\xa0\xdf\x73\x7e\x7d\x43\xa5\x55\x5d\x1e\xb0\
+\x6b\x4b\xbd\xca\xa2\x32\x1f\x39\xdb\x11\x03\xb1\x0c\x53\xf1\x94\
+\x9c\x49\xe7\xad\xf0\xb8\x61\x8b\xc9\x98\x66\x86\x27\x9e\x1a\x39\
+\xf9\xe0\xed\x29\x90\x99\xd4\x01\x59\x39\xff\x99\xaf\x37\x9c\x26\
+\x22\x9d\x62\xfb\xb6\x21\x65\xfd\xad\x7f\x61\x9f\xd6\xf8\xe3\xb3\
+\x5d\xe5\x55\x4f\xba\x6a\x2a\x16\xaf\x3a\xbb\xae\xf0\xbc\xa5\x21\
+\xe2\x19\x8b\xf8\x4c\x86\x74\x32\x43\xb8\x77\x22\x3d\xde\x3d\x72\
+\x58\xc4\x13\xf7\x9f\xf8\xf4\x81\x03\x25\xa1\x47\xd4\xd8\xf8\x23\
+\xf6\x97\xb1\xfe\xdf\x00\x29\x25\x42\x5c\x03\x6c\xe5\x97\x2f\x49\
+\xee\xba\xe9\x8b\x10\x85\x95\x77\xd7\xaa\xe5\xe5\xf5\x45\x35\x15\
+\x42\x78\x74\xcc\xd9\x2c\xd9\xa9\x29\x29\x63\xc9\xa1\xd8\xc8\x13\
+\x03\x00\x6b\xee\x3c\x46\xf3\xaf\x97\xb3\xf4\xd2\x13\x9c\xdc\xbd\
+\x0c\x80\xff\x00\x1c\x7c\x06\xd9\x9b\xd6\x5b\xe7\x00\x00\x00\x00\
+\x49\x45\x4e\x44\xae\x42\x60\x82\
+\x00\x00\x03\xb1\
+\x89\
+\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
+\x00\x00\x18\x00\x00\x00\x18\x08\x06\x00\x00\x00\xe0\x77\x3d\xf8\
+\x00\x00\x00\x01\x73\x52\x47\x42\x00\xae\xce\x1c\xe9\x00\x00\x00\
+\x09\x70\x48\x59\x73\x00\x00\x1b\xaf\x00\x00\x1b\xaf\x01\x5e\x1a\
+\x91\x1c\x00\x00\x03\x56\x49\x44\x41\x54\x48\xc7\xdd\x95\x4d\x68\
+\x5c\x55\x14\xc7\x7f\xf7\xe3\xbd\x97\xe9\x34\x69\x5a\x93\x60\xdb\
+\x80\x60\xc1\x12\x9a\x20\x0d\x49\x88\x50\xca\x43\xbb\x70\x93\x85\
+\x62\x41\x2b\xea\x42\xb0\xb8\xb2\x20\x08\x6e\x64\xd0\x55\x4b\x2d\
+\x48\x37\x22\x48\x10\x8c\x20\xba\x09\x45\xa4\x12\xe2\x98\x8a\x29\
+\x1a\xa4\x92\x6a\x6d\x4a\x9b\xb4\x19\x9d\x98\xc4\xb1\xd3\x99\x79\
+\xf3\xde\x9b\xf7\xee\x75\x51\x2c\xe4\x63\x31\xc1\x6e\xf4\xec\xee\
+\xc7\x39\xbf\x7b\xee\xb9\xff\x73\xe1\xbf\x6e\xa2\x99\x4d\xfd\x9f\
+\x3e\xd4\xff\xdc\x23\x2f\x7d\x38\xbe\xf0\xc9\xa3\x8e\x94\xc1\xc1\
+\xae\xc1\xc9\xa1\xae\x43\x6f\x3c\xbb\xef\xd5\x5f\xb7\x04\xc8\xe7\
+\xf3\x76\xb3\x4d\x1f\x94\x4f\xf3\xee\x91\xf7\x99\x2d\xcd\x90\xda\
+\x98\x30\x89\x28\xdd\x5e\x65\xdf\x9d\x83\x9b\x06\xf5\x7d\xff\x5e\
+\x5c\xbd\x7e\x31\x9f\xcf\x6f\x70\xb8\xb6\x67\x8e\x6f\x0a\x13\x74\
+\x74\x08\x04\x29\x5e\xb5\x8d\xc9\xf9\x2f\x58\x9c\x2d\x6f\x16\x7c\
+\xcd\x58\x6f\x76\x82\x5c\x2e\xb7\x26\xb3\x87\x3f\xda\xf5\x43\x6c\
+\xa2\x81\xdf\x6a\xab\x08\x04\xd9\x44\x33\x2f\xaf\xce\x8c\xe5\x26\
+\x06\xd7\xf9\x6d\xb8\x01\xdd\x4c\x0d\x52\x63\x48\xad\x21\x6c\xc4\
+\x08\xa0\xc5\x1a\x8c\x69\xae\xc8\x4d\x01\x4c\x6a\x30\x58\x6a\x71\
+\x08\x08\xda\x74\x4a\x6a\xec\xfd\x03\x24\xc6\x24\xc6\x1a\xea\xc9\
+\xdd\x0c\x8c\x34\x24\xa9\x49\xb6\x04\x38\x3b\x73\xb6\xb7\x20\x0b\
+\x9c\xeb\x3c\x07\xa7\xf9\xde\xd3\x5a\x68\x29\xd1\x52\x26\x89\xb1\
+\x7d\x49\x9a\x12\x36\x42\xa4\x10\xc4\x6e\x8c\xc5\xf6\x0d\x7e\xde\
+\x3d\xed\x69\xad\x1d\xad\xd0\x12\xbb\xcc\x4f\xd4\xf6\xec\x67\xa2\
+\xf8\x59\xcf\x91\xdd\x47\xaf\xac\x01\xcc\xfd\x39\xf7\xde\xa9\xc7\
+\x4f\x51\x11\x65\x8e\xc6\x4f\x0f\x4a\x29\x11\xc2\x82\x00\x29\x04\
+\x59\x37\x63\x83\x30\x10\x00\x9e\xf2\xec\xf1\xde\xd7\xb3\x4a\xa8\
+\x61\x29\x24\x5a\x2a\xb6\x39\x59\xb6\xeb\xed\x34\x08\xf8\xea\xd6\
+\xd8\x3b\xc0\x33\x6b\x00\xa3\x97\x46\x87\x0f\x74\xf4\x70\xa3\x72\
+\x85\x62\x58\x44\x6a\x85\x52\xa0\x94\x44\x0b\xc9\x5c\x60\x44\xc3\
+\x84\x48\x01\xd3\x2b\x93\x42\x0b\x05\x48\x5a\x9d\x76\x8c\x51\xfc\
+\xb2\x74\x8d\x1b\xc5\x45\x5e\x1c\x1a\x61\x29\x28\x3c\xb9\xe1\x8a\
+\x82\x28\xb0\x97\x57\x7e\x26\x30\x65\x6a\x69\x1d\x65\x25\xca\x48\
+\x1c\xa3\x70\xd4\xdd\x6c\x94\x54\x78\xca\xa3\x45\xb5\xa0\x71\xa9\
+\xc4\x55\xbe\xfb\xe3\x22\xf3\xa5\x5b\x54\xc2\x90\xac\x69\xa7\xd6\
+\xa8\xd2\x30\xe9\xc6\x1a\x58\x63\xc7\xa7\x16\x2e\x1c\xeb\xe9\xec\
+\xc1\x53\x6d\x68\xa9\xf0\x94\x8b\x87\x8b\x46\xd1\xea\x64\xec\xc5\
+\xe5\xf3\xc2\xca\x98\xa1\xce\x27\x6c\x10\xd7\x85\x12\x9a\xbe\x07\
+\x0e\xd0\xb7\xab\x17\x8b\xc5\x95\x8a\x42\x78\x95\xc4\xa4\xe3\x9b\
+\x01\x4e\x64\xa2\xcc\xb1\xa9\xcb\x53\x14\xdd\xe2\x0c\x02\x90\x20\
+\x24\x89\xab\x54\xdf\x53\xfb\x47\xb2\x5a\x5b\x32\x6e\x96\xdb\xf5\
+\xbf\xc4\x97\x0b\xe7\x6b\x9e\x23\x67\x5d\xa5\xb4\xab\x14\xae\xd6\
+\xec\x8c\x3a\x06\xba\xdb\xbb\x89\x68\x9c\xd8\xf8\x4c\xdf\x66\xe5\
+\x64\xfe\x24\xf9\x7c\x9e\xdc\x9b\xb9\x7b\x0a\xb5\x80\x39\x63\xa7\
+\x83\x24\x18\xd6\x8e\xc4\xd3\x92\xba\x09\x48\x4c\x32\x7b\xf3\x85\
+\xca\x63\x6b\x95\xfc\xb2\xf5\x7d\x1f\xdf\xf7\x57\xfe\x99\x93\x4d\
+\x29\xd9\xa2\xab\x71\x15\x8b\x45\x29\x41\x64\x42\x52\xd3\x9c\x86\
+\x9a\x53\xb2\x35\x54\xa2\x0a\xa9\x13\x92\x71\x15\x51\x5c\x25\x69\
+\xb2\x57\x34\x05\xc0\xc0\x6a\xbd\x8c\x67\x22\xea\x8d\x06\x36\x89\
+\x48\xed\xbf\x00\xac\xef\x8a\x1f\xcb\x51\x5c\x62\x16\x4b\x15\x56\
+\x74\x8d\xbd\xdb\xba\xd9\x5b\xef\x1e\x78\x2d\xf7\xbc\xdd\x32\x60\
+\x7d\x3f\x07\x98\x5b\xfe\x1a\xe9\x2d\xb2\x50\x2d\x61\x52\x4d\x97\
+\xde\x8d\xd3\xea\xe0\xf7\xfb\xf7\xe7\xcb\x1c\x19\x7b\xf0\xd0\x1d\
+\xd4\x99\x0b\x4b\xbf\x0f\xb4\x7b\x3b\xea\x87\x77\x1e\xfe\xf6\xd2\
+\xf5\x1f\x5f\x59\x78\xab\x70\x93\xff\xbd\xfd\x0d\xf6\xbd\x75\x1d\
+\x3b\xea\x7e\x89\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
+\
+\x00\x00\x03\x30\
+\x89\
+\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
+\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\
+\x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\
+\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0e\x4d\x00\x00\x0e\x9c\
+\x01\xde\xf6\x9c\x57\x00\x00\x02\xd2\x49\x44\x41\x54\x38\x8d\x6d\
+\x93\x4d\x68\x5c\x55\x14\x80\xbf\x73\xdf\x9b\xe9\xc4\x21\xcc\x4c\
+\x4c\xc6\x2a\x22\xb6\xa9\x93\x44\x0c\xa3\x62\xc1\x2c\x14\x9d\xd2\
+\x12\x35\x85\x52\x5a\x11\x6c\xa5\x1b\x71\x57\xe8\xce\xc5\x74\x37\
+\x82\x2e\xa5\x4b\x05\xcb\x20\xc6\x45\xdd\x48\x4a\xf0\x27\xf1\x07\
+\x69\xa4\x86\xc2\x98\xd0\xc4\x50\x14\x1a\x4d\x9b\x57\xf3\x63\xe0\
+\xbd\xe4\xdd\xf7\xee\xbd\x2e\x06\xa7\xb1\x7a\x56\xe7\x1c\x38\x1f\
+\x87\xc3\x77\x84\x7b\x62\x69\x69\xc9\x69\xad\xef\x6d\x03\x90\xcd\
+\x66\xa9\x54\x2a\xb2\xbb\xe7\xef\x2e\x5a\xad\x96\xdb\xba\xd0\xc0\
+\x9f\xbe\x8c\x12\xc1\xcb\x08\xce\x82\xb5\x0e\xa7\x1c\x5b\xcf\x8f\
+\xd1\x6a\xb5\x5c\xb5\x5a\xed\x40\x3a\xc9\xcc\xcc\x8c\x53\xcd\x0b\
+\xdc\xf7\xed\x57\x78\x22\x74\xed\xf5\x31\xeb\x16\x44\xf0\xef\x57\
+\x6c\xdf\x4a\x30\xc6\x12\xd5\x0e\x63\x4e\x9f\x65\x64\x64\x44\x3a\
+\x80\xe9\xe9\x69\xe7\x35\x3f\xa0\x77\x76\x8a\x6c\xc6\xc3\xf7\x05\
+\x3f\xeb\xa1\x3c\x01\x0b\x56\x1c\x26\xb5\x24\x3b\x16\x9d\x1a\xd6\
+\x0e\x1e\x22\x3d\xf5\x26\xb5\x5a\x4d\x64\x62\x62\xc2\x75\x7d\xfa\
+\x11\xe5\xab\xdf\xb3\x99\x11\x36\x8d\x63\xef\x9e\x0c\x7b\xba\x3d\
+\x82\x6d\x8b\x00\xbd\x5d\x42\x12\x59\x56\xa2\x04\x8b\xa3\x94\x3a\
+\xf4\xb3\x2f\x12\xbf\x76\x06\x15\x04\x01\x3d\x3f\x7c\x83\x38\x21\
+\x1e\x3d\xc6\xd8\x42\xc0\xce\xcb\x27\x59\x13\xc7\xa1\xd9\xdf\x79\
+\xe1\xc7\xdf\x28\xbc\x7b\x91\x2b\x5b\x09\x47\x17\x02\x8e\xce\xdf\
+\x22\x7f\xfe\x3d\xf4\x77\x5f\xb3\xba\xba\x8a\x0a\xc3\x10\x25\xa0\
+\xb2\x42\xe1\xf1\xa7\xd8\xd9\xdc\xa0\xfb\x89\x2a\x6e\xbb\x7d\x9e\
+\xcf\xde\x38\xc6\xc3\x07\x9f\x23\xdf\xdf\x0f\xc0\xf8\xe9\x93\x3c\
+\xfd\xfa\x19\x36\x4a\xbd\x84\x61\xd8\x06\x58\x0b\x6b\x91\xa1\x6f\
+\xb8\x4a\xeb\xd2\x38\xbd\x83\xc3\xfc\xa9\x0c\x69\x9a\xf2\xcc\x5b\
+\xe7\xb8\x73\x63\x91\x70\xf9\x0f\xd2\x34\x25\xd7\x53\x20\x35\x06\
+\x67\xcc\x5d\x80\x01\xee\x28\x45\x71\x7f\x85\x6b\x1f\x5f\xa4\x7b\
+\x5f\x3f\xb7\xb7\x1c\x71\x1c\xb3\x7c\x6d\x96\x5c\xdf\x83\xa8\x62\
+\x81\x38\x8e\x39\xf2\xce\xfb\xfc\xd4\xfc\x90\xe2\xe6\x3a\x51\x14\
+\xe1\x87\x61\x48\x92\x18\xa2\x47\x2b\x58\xa5\x38\xde\xbc\x84\x01\
+\x72\xfb\x0f\xa0\xb5\xe6\xe7\x4f\x9a\x0c\xbe\x7a\x0a\x3f\x5f\x44\
+\x6b\xcd\xf8\x93\xfb\x18\x44\xe8\x43\xda\x1b\x44\x51\x84\x4e\x0c\
+\xde\xc0\x10\x2b\x73\x2d\xa6\xce\xbf\xcd\xed\xeb\xf3\x94\x2a\x43\
+\x68\xad\x39\xf1\xf9\x14\x37\xaf\x5e\x61\xe3\xfa\x3c\x5a\x6b\x4a\
+\x0a\xf2\x16\x92\x24\x25\x8a\x22\xa4\x5e\xaf\xbb\x07\xa6\x26\xc9\
+\x2d\x2d\xe0\x7b\x8a\x03\x39\x9f\x65\xe7\x40\x14\x4a\xb5\x45\xe9\
+\x51\x0a\x27\x8e\x20\x4a\x29\x88\xa3\xa8\x60\xee\x91\x01\x56\x6a\
+\xa3\x6d\x91\xea\xf5\xba\x2b\x7f\x39\x49\x75\xe5\x17\x24\x01\x25\
+\x42\xae\xe0\x63\x22\x07\x80\x97\x17\xe2\xbf\x0c\xd6\x39\x5c\xc6\
+\xd1\x7a\x68\x80\xd5\xc3\x2f\xd1\x68\x34\xa4\xa3\x72\xbd\x5e\x77\
+\xe5\x2f\x2e\x33\xf8\xeb\x02\x9e\x08\xa2\xa4\x6d\xa2\x03\xac\xc3\
+\xe2\x48\x8d\x63\xb1\x7f\x88\xe0\xc8\x2b\x34\x1a\x8d\xbb\x2a\xef\
+\x86\x94\x26\x27\x78\x6c\x71\x0e\x2f\xeb\xe1\x2c\xe0\x40\x7c\x30\
+\xb1\xe1\xc6\xd0\x30\xeb\xa3\x63\x9d\xe1\xff\x00\xfe\x81\x04\x41\
+\xf0\xbf\xef\x5c\x2e\x97\xff\x35\x0c\xf0\x37\x5c\xfc\x53\xd5\x3a\
+\x96\xb0\xec\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
+\x00\x00\x04\x2c\
+\x89\
+\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
+\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\
+\x00\x00\x00\x06\x62\x4b\x47\x44\x00\xff\x00\xff\x00\xff\xa0\xbd\
+\xa7\x93\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x2e\x23\x00\x00\
+\x2e\x23\x01\x78\xa5\x3f\x76\x00\x00\x00\x07\x74\x49\x4d\x45\x07\
+\xde\x03\x0c\x12\x06\x08\xae\xb5\x4a\x3d\x00\x00\x03\xb9\x49\x44\
+\x41\x54\x38\xcb\x5d\xcb\x5b\x4c\x5b\x75\x00\x07\xe0\xdf\xff\x9c\
+\xd3\x9e\x76\x6d\x69\xc1\x52\x84\xca\x40\x2e\x16\xc7\x9d\x31\x67\
+\x9c\x66\x03\x27\x73\x2f\x8a\x71\x2c\x12\x12\x12\x1f\xa6\x89\x2c\
+\x73\xc9\x36\x37\x13\x6f\x0f\x5e\xa2\xcc\x88\x0f\xb8\x87\x3d\x78\
+\x99\x09\x09\x4b\x34\xd1\x38\x86\x3a\xb9\x3a\x04\x06\x1d\x84\x6b\
+\xa1\x2d\xd0\x0b\xa5\xed\xa1\x17\x7a\x4a\xdb\x73\xf3\xc9\x17\xbf\
+\xf7\x8f\xe0\x7f\xea\x00\xac\xf6\x78\xb0\x7f\xb1\x10\x00\xd0\x07\
+\x50\xbf\x9d\x69\x36\x15\x55\xd5\x3f\x22\xe6\xab\xcd\xac\xbc\x64\
+\xcd\x65\x33\x82\x73\x79\x67\xa2\xa7\x7b\x36\xc8\xfc\x17\xaf\xff\
+\x74\x15\xee\x73\x9f\xe3\xd9\xf0\x3e\xd5\x4e\xb4\xf2\xbb\x57\x3a\
+\xcb\xac\x95\xcd\xaf\x2f\x14\x72\xf5\xad\x79\xe5\x15\xe1\x4c\xf2\
+\xf1\xd8\xf4\x10\x76\x84\x2d\x18\x92\x31\xec\x39\x82\xc3\x25\x96\
+\x83\x2d\x04\x00\x2e\x5f\xac\xc7\xf5\x1e\x3b\x00\xe0\x30\xa0\x39\
+\xd6\xf5\xd2\x25\x5e\x6f\x7c\x7f\x6e\x88\x66\x6d\x2f\x04\x51\x7b\
+\x54\xc0\xae\x5f\x40\x69\x41\x03\xbc\x53\x03\xd8\x18\xf7\x80\x4f\
+\x88\x23\xb6\x23\xc5\x2d\x14\x00\x68\xee\xd8\x71\xfe\xe7\x5e\xb4\
+\xb5\xe1\xc0\xe9\xdb\xbd\xb7\x72\x5a\xda\x3f\x76\xb8\xfd\x78\x30\
+\xf5\x9d\x1c\xe7\xb6\x51\x7a\x92\x81\x62\x52\x63\xda\x31\x89\xb9\
+\xbf\xfd\xf0\x2b\x40\xda\xc4\xc4\x3e\xb9\xb1\x92\xa1\x15\x1f\x8f\
+\xc1\x6c\x99\x4a\xda\x1a\xe9\x12\x53\xd5\x37\x74\x9e\xa5\xa3\xb1\
+\x22\x28\x91\x1c\x95\x8a\x11\x52\xe4\xe9\x4e\x03\x16\x07\x78\x84\
+\x97\x05\x78\xe7\x37\x40\xcb\xe9\x8c\x56\x94\x69\xa3\x4a\xee\x9e\
+\x73\x8a\x76\x82\xa1\x2f\x80\xa6\x77\xf0\xf5\xc4\xa7\x6f\xe9\x8a\
+\x6d\xbd\xae\xe1\x11\x29\x15\x72\xd0\xc9\x1d\x0e\x12\x05\x04\xdc\
+\x5e\x48\xfb\x09\xe8\xf2\x53\x70\xfc\xa9\xc8\x8f\xb2\x22\x65\xc8\
+\xa2\x6f\x5c\x7b\xbe\xbc\xab\xf1\xa3\x15\x85\x00\xc0\xbc\x32\x9a\
+\xfd\xfd\xef\x77\xdc\x81\x99\x87\x9a\x02\x36\xc2\xae\x8e\xfa\x90\
+\x65\x12\x11\xe6\x62\x30\x9b\x4f\x23\xbb\xf0\x14\x84\x3d\x23\x7c\
+\xbe\x10\x88\xb2\xb5\xb5\xe6\x2e\x6b\x5d\x9e\x79\xd3\xde\xda\x71\
+\x93\xd0\x00\xf0\x58\x53\xcb\xb5\x8d\x91\x6f\x4f\x1d\x88\x6d\x52\
+\xf6\xb1\x20\xd1\x54\x18\x91\x10\xd3\x38\x56\xdd\x83\x27\x6a\xda\
+\x50\x5d\x57\x8b\xd1\x87\x01\x94\x54\x14\x2b\x12\x2c\xc6\xd2\x43\
+\xe6\x8e\xe8\x5e\xcd\xf4\xc4\x1f\x17\x5c\x74\xdf\xe2\x6d\x53\x44\
+\xc5\xf6\x4c\xf5\xf5\x9b\xa3\x7e\x9e\x98\x73\x68\x44\x96\x7d\x28\
+\x50\xbf\x08\xe4\x34\xe1\xe5\xd6\x2a\xdc\x9b\x74\x81\x97\x15\x4c\
+\xce\x7b\xc9\x92\x33\x44\x68\x8d\x5a\xa5\xa5\x94\x13\x14\x9e\xfa\
+\x81\xa2\x8c\xf9\x15\x54\xc2\x63\x63\x58\x0d\x38\x5f\x1a\x9b\x0b\
+\x31\x68\x24\x82\x38\x7d\x1c\x95\x35\x79\x90\x28\x1a\x87\x8f\x94\
+\x40\x9d\x63\xc4\x1e\xab\x86\xd6\x92\x85\xcd\x9d\x38\x61\xb2\xb3\
+\x8a\xac\xe5\xb9\x8d\x54\x38\xb0\x5e\xe4\x78\x30\x02\x29\x1e\x05\
+\x51\x53\x90\x64\x05\xb1\x3d\x3d\x26\x96\x44\xc5\xbe\x16\x86\x27\
+\x18\xc7\x36\xc7\x43\xaf\x67\x90\x7b\xd0\x0c\xc5\xa8\x03\xa3\xd3\
+\x92\x86\xa3\x25\xe0\x79\xb1\x99\xe1\x9c\xc1\x48\x68\x66\x05\x11\
+\x2f\x0f\x0d\x14\x68\x55\x04\x1a\x35\x03\x5a\x21\xc4\xee\xe4\x94\
+\x8c\xd1\x43\x12\x94\x0a\x9b\xbc\x80\x24\xcb\x82\xe8\xb5\x60\x29\
+\x82\xd5\xb5\x20\x64\x31\x93\x62\xaa\xa4\xe8\xfd\x1d\x83\xe5\x33\
+\x29\x8f\xb7\xa6\x12\x9c\x64\xd0\xe8\x32\x3a\x2d\x1f\xdd\xd8\x4d\
+\xbc\x1d\x88\xa4\x35\x26\x5e\x42\x43\xa5\x05\x87\x74\x5a\x0c\xaf\
+\x85\x11\x57\x13\x45\xf0\xa4\xc9\xd2\xec\x3a\xb4\xa9\xf4\x10\x01\
+\x80\xfb\xeb\x23\x54\x55\x7e\x21\x9d\xda\xf5\xca\xf7\x06\x26\xa9\
+\xd7\x7e\xb9\x22\x9c\x4c\x5c\xfd\xd1\xa9\xb7\x75\xe8\x9f\xb4\x2a\
+\x6f\x9c\xad\x23\x65\x16\x13\xb8\xfd\x0c\xee\x8e\xaf\x63\x6c\x70\
+\x11\x8c\x2f\x3c\x4b\x85\x62\xc7\x19\xcf\xea\x2d\x7c\xf9\xd5\x7b\
+\x68\xef\x1f\x93\x18\x0a\xd0\xab\x21\xb5\x7b\x40\x14\xe5\xf2\x85\
+\x13\x67\x7e\x4d\x46\xf7\xd3\xe7\xc6\x1d\x1c\x5e\x6d\xb0\x60\x76\
+\x4b\xc4\x6e\x28\x01\x46\x10\x97\x52\xf1\x54\xa7\x7f\xed\xc3\x04\
+\x01\x00\x87\xeb\x2e\xb8\x40\x1c\xdb\xae\x05\xc4\xc2\x22\x3c\x3e\
+\x2b\x3e\xe8\xee\x02\x00\x3c\xf7\xca\xcd\x4a\xc5\x64\x38\x5b\x5d\
+\x5b\x58\xe0\xf2\x44\x64\xce\xbf\xfb\x17\xef\x0e\x0d\x2e\xff\x73\
+\x29\xfa\xcc\xf9\x7e\xfc\x0b\x0b\xb9\xbe\x45\x31\x2c\x8e\xcb\x00\
+\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
+\x00\x00\x20\x26\
+\x89\
+\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
+\x00\x00\x40\x00\x00\x00\x40\x08\x06\x00\x00\x00\xaa\x69\x71\xde\
+\x00\x00\x00\x01\x73\x52\x47\x42\x00\xae\xce\x1c\xe9\x00\x00\x00\
+\x06\x62\x4b\x47\x44\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\
+\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\
+\x00\x9a\x9c\x18\x00\x00\x00\x07\x74\x49\x4d\x45\x07\xdc\x02\x15\
+\x0e\x23\x2f\xab\xc9\xe5\x0b\x00\x00\x00\x1d\x69\x54\x58\x74\x43\
+\x6f\x6d\x6d\x65\x6e\x74\x00\x00\x00\x00\x00\x43\x72\x65\x61\x74\
+\x65\x64\x20\x77\x69\x74\x68\x20\x47\x49\x4d\x50\x64\x2e\x65\x07\
+\x00\x00\x1f\x7d\x49\x44\x41\x54\x78\xda\xcd\x9b\x69\x90\x5d\xd7\
+\x71\xdf\x7f\x7d\xee\xfa\x96\xd9\x07\xc4\x00\x33\x18\x02\x03\xae\
+\x22\x45\x53\x0b\x25\xd2\x91\x44\x53\x8a\x2d\x89\x91\x17\xad\xa4\
+\x2c\x5b\x52\x12\xc9\xf1\x56\x72\xca\xa9\xe4\x4b\x2a\x1f\x52\x89\
+\xbf\x38\x49\x39\x72\xe2\x4d\x92\x63\x4b\xb1\x55\x12\x4d\x49\xb4\
+\xc5\x45\x24\x41\x10\xa0\x44\x09\x80\x28\x42\x22\x41\x12\xfb\x3a\
+\x33\x00\x66\x06\xb3\xbf\xed\xde\x7b\x4e\xe7\xc3\xb9\x6f\x30\x00\
+\x41\xed\x76\xe5\x56\xdd\x79\x6f\xee\xf6\x4e\xf7\xe9\xfe\x77\xf7\
+\xff\xf4\x85\x1f\x73\x7b\xf1\xc5\x17\xf9\xff\x65\xfb\x49\xc6\x12\
+\xfe\x34\x06\x70\xe0\xc0\x81\xde\x9e\x9e\x9e\x9f\x37\xc6\xbc\x4e\
+\x44\x7a\x01\xfb\x8f\x24\x6b\xa0\xaa\xcb\xce\xb9\xef\xac\xac\xac\
+\x3c\x7e\xf3\xcd\x37\x2f\xff\xa4\x0f\x94\x1f\xe7\xa6\x3d\x7b\xf6\
+\x70\xfb\xed\xb7\x03\xf0\xf5\xaf\x7f\xbd\x32\x3a\x3a\x7a\x6f\xad\
+\x56\xfb\x70\x14\x45\x37\x1b\x63\x22\x40\x45\x04\x91\x2b\x3f\xfe\
+\xf2\xe3\xaa\xfa\x8a\xbf\xa5\xaa\xeb\xcf\x8b\x73\x2e\xcf\xf3\xfc\
+\x40\xa3\xd1\xf8\xdc\xd4\xd4\xd4\x17\xde\xfc\xe6\x37\xb7\x2e\x1f\
+\xd3\x3f\xea\xf6\xe0\x83\x0f\xae\x7d\x7f\xe0\x81\x07\x92\xe3\xc7\
+\x8f\x7f\xe8\xdc\xb9\x73\xdf\x5c\x5c\x5c\x6c\xaf\x36\x1a\xda\x6c\
+\xb5\xb4\xd5\x6e\x6b\xbb\xd3\xd1\x4e\xb9\x67\x59\xe6\xf7\x3c\xd7\
+\x2c\xcf\x35\xcf\x73\x2d\x8a\x42\xf3\xa2\xf0\x9f\xeb\x8e\x67\x79\
+\xbe\x76\x7d\xa7\xd3\xd1\x76\xbb\xad\xad\x56\x4b\x5b\xad\x96\x36\
+\x5b\x2d\x5d\x5d\x5d\xd5\x85\x85\x85\xf6\xb9\x73\xe7\xbe\x79\xfc\
+\xf8\xf1\x0f\x3d\xf0\xc0\x03\xc9\x95\xc6\xf6\x8f\xb2\xed\xd9\xb3\
+\x67\xed\xfb\xfd\xf7\xdf\x9f\x1e\x3e\x7c\xf8\xdd\xd3\xd3\xd3\x7b\
+\x16\x16\x16\x8a\x46\xa3\xa1\xed\x2c\xd3\x8e\xb5\x2e\x73\x4e\x73\
+\xa7\x5a\xa8\xdf\x6d\xb9\xbb\x72\xbf\xd2\xe6\xd6\xed\xdd\xeb\x0b\
+\x55\xcd\x55\x35\x73\x4e\x33\xe7\xb4\x63\xad\x6b\x67\x99\x36\x1a\
+\x0d\x5d\x58\x58\x28\xa6\xa7\xa7\xf7\x1c\x3e\x7c\xf8\xdd\xf7\xdf\
+\x7f\x7f\x7a\xa5\x31\xfe\x54\x5d\x60\xff\xfe\xfd\xbc\xe6\x35\xaf\
+\x01\x60\xe7\xce\x9d\xb5\xcd\x9b\x37\xbf\xa5\x5e\xaf\xff\xa7\x4a\
+\xb5\x7a\x47\x9a\x24\x88\x88\xae\x9c\x3c\xc1\xe2\xb1\xe3\x92\x35\
+\x56\x41\x04\xb8\xd4\x0d\x8c\xc8\xda\x2f\x0a\x82\x5e\xb4\x73\x7f\
+\x5c\x41\xfd\x1f\x7f\x4e\xb5\xbc\x46\x41\x95\xb8\x56\xa3\x6f\xfb\
+\x76\xed\xdd\xba\x0d\x55\x95\x76\xa7\x43\xab\xd9\xfc\xd6\xea\xea\
+\xea\x7f\x39\x7b\xf6\xec\x53\x77\xdd\x75\x57\xe3\xf2\xb1\xfe\x54\
+\x14\xf0\xdd\xef\x7e\x97\x5b\x6f\xbd\xb5\xeb\xf3\xf5\x91\x91\x91\
+\xb7\x56\xab\xd5\xff\x90\x56\x2a\xff\x2c\x89\x63\x44\x55\x57\xcf\
+\x9c\x91\x43\x9f\xfe\x33\x0e\xff\xcd\xe7\x58\x99\x5d\xea\xca\xb3\
+\xf6\xd9\xfd\x31\x53\xfe\xaf\x57\x18\x88\x00\xee\xb2\xeb\xd7\x3f\
+\xa3\x77\x43\x1f\xd7\xfe\xda\x87\xb9\xfe\xe3\xbf\x45\x6d\xcb\x16\
+\x45\x44\xda\xed\x36\xed\x76\xfb\xe9\x56\xab\xf5\x87\xe7\xcf\x9f\
+\xdf\xf9\xa6\x37\xbd\x69\xf5\xf2\x31\xff\xd8\x0a\x78\xee\xb9\xe7\
+\x00\xb8\xe5\x96\x5b\xba\xc2\xf7\x8c\x8c\x8c\xdc\x5d\xad\x56\x7f\
+\x2f\x4d\xd3\x3b\x92\x24\x51\x50\x56\x4f\x9d\x92\x83\x9f\xfe\x14\
+\x8b\x0f\xfd\x3d\x3d\x4b\x33\xf4\xd7\x13\x50\xc1\xb5\x1d\x61\xcd\
+\x80\x03\x2d\x20\x88\x04\x13\x09\xae\x50\x34\x57\x4c\xc5\x5b\x84\
+\x6d\x28\x26\x11\x24\x14\x5c\xee\x70\x05\x98\x00\x54\xa0\x58\x75\
+\x04\x55\x03\xa2\x2c\xae\x76\x58\xe9\xbb\x8a\xfe\xbb\x7f\x89\xeb\
+\x3f\xf6\x1b\xd4\xc7\xb7\xaa\x0a\x74\x3a\x1d\xe9\x74\x3a\xdf\x6a\
+\x36\x9b\x9f\x3c\x77\xee\xdc\xc3\x6f\x7e\xf3\x9b\x57\xae\x34\xfe\
+\x9f\x28\x0c\x3e\xf9\xe4\x93\x7d\x23\x23\x23\xbf\x52\xad\x56\xff\
+\x4d\x9a\xa6\x77\xc4\x71\x8c\x3a\xc7\xea\x99\xd3\x1c\xfa\xf4\x9f\
+\xb3\xf2\xe8\x57\xe9\x59\x9c\xa5\x1e\x45\x68\x0e\xa2\x10\x24\x01\
+\x61\x2a\xa0\x82\x6d\x39\xd4\x2a\xd6\x2a\x88\x60\x52\x83\xa9\x88\
+\x9f\xdd\x4c\xd1\x42\xa1\xf0\xf3\x6d\x22\x21\x48\x05\x54\x71\x99\
+\x57\x1e\x02\xf5\x20\x86\xc5\x39\x56\x1e\xff\x2a\x07\x51\xae\xff\
+\xd8\x6f\x52\x1f\x1f\x27\x8e\x63\x44\xe4\x0e\x11\x61\x64\x64\x24\
+\x7d\xf2\xc9\x27\x1f\xb8\xeb\xae\xbb\x96\x7e\x22\x17\x58\x6f\x42\
+\x4f\x3d\xf5\x54\xff\xe8\xe8\xe8\xfb\x2a\x95\xca\x47\x93\x24\xb9\
+\x2d\x8a\xe3\x58\x40\x9b\x53\x53\xf2\xe2\xff\xfa\x24\xab\x3b\x1e\
+\xa6\x67\x71\x81\x6a\x28\x84\x89\xc1\x84\x02\x4e\xd0\x8e\x12\xd6\
+\x0d\x58\x50\xab\x98\x48\x30\xa1\x17\xc8\x15\x8a\x49\xbd\xe1\xdb\
+\xa6\xc3\x24\x06\x13\x94\x16\x60\x15\x29\xa7\xa6\x68\x94\x96\x62\
+\x14\x97\x2b\x79\xe6\x68\x5a\x65\xa5\x67\x80\xfa\xdb\xee\xe6\x55\
+\x9f\xf8\x04\xd5\xd1\x31\x75\xaa\x92\x67\x59\x96\x65\xd9\xb7\x5b\
+\xad\xd6\x5f\x4f\x4d\x4d\xdd\xff\x96\xb7\xbc\x65\xf1\x07\xb9\x83\
+\xb9\xd2\xc1\xbd\x7b\xf7\xae\xdd\xb0\x7b\xf7\xee\xbe\x4d\x9b\x36\
+\xbd\x2f\x49\x92\x8f\xc4\x71\xfc\xda\x30\x0c\x63\x13\x04\xb4\x67\
+\xce\xf1\xfc\x27\xff\x88\x95\x47\x1e\xa2\x6f\x79\x81\x9a\x11\xa2\
+\xc0\x10\xc6\x86\xb0\x12\x78\x45\x58\x10\x27\x88\x13\x0c\x42\x18\
+\x1b\xa2\x6a\xf7\x9c\xc1\x38\x83\x71\x10\x60\x88\x12\x43\x54\xf3\
+\x0a\x0c\xf0\xe7\xc5\x09\xc6\xf9\xfb\xc2\x34\xf0\xf7\x07\x86\x2a\
+\x42\xef\xca\x02\x2b\x8f\x3e\xc4\x81\x4f\xfe\x11\xed\xf3\xe7\x08\
+\xc2\x80\x28\x8a\xe2\x38\x8e\x5f\x9b\x24\xc9\x47\x36\x6d\xda\xf4\
+\xbe\xdd\xbb\x77\xf7\x01\xdc\x7a\xeb\xad\xec\xdd\xbb\xf7\x87\xb3\
+\x80\xf5\x09\xc5\x13\x4f\x3c\xd1\x3b\x36\x36\xf6\x9e\x5a\xad\xf6\
+\xb1\x24\x49\x5e\x1b\x45\x51\x25\xae\x56\x59\x3a\x76\x4c\x5f\xf8\
+\xe3\xff\x29\xab\x0f\xfd\x03\xfd\xed\x15\xfa\x7a\x22\x0c\x7e\xd6\
+\x25\x10\x82\x48\xbc\xdf\xe7\x4a\x54\x37\xa8\x13\xef\xf3\x81\xf7\
+\x73\xb5\x0a\x0e\x4c\xd5\xc3\x9b\x6d\x80\x09\x81\x40\xd0\x42\x51\
+\xa7\x48\xe4\xcf\x15\x2b\x8a\x24\xa0\x06\x5c\xae\xa8\x55\x34\x00\
+\xe7\x1c\xcb\xab\x39\x8b\x69\x2f\xb5\x77\xfe\x22\x37\xfd\xde\xbf\
+\xd5\xde\xed\xdb\x25\x6b\x36\x29\xf2\xbc\xd5\xe9\x74\x9e\x6d\x34\
+\x1a\x9f\x99\x9c\x9c\xfc\xf2\xdb\xde\xf6\xb6\xe5\x57\x4a\x96\xcc\
+\xe5\x49\x4e\xf7\x82\xa7\x9f\x7e\xba\x3e\x3a\x3a\x7a\x77\xb5\x5a\
+\xfd\x8d\x24\x49\x6e\x8b\xa2\xa8\x12\x55\xab\xcc\x1f\x3c\xa8\x2f\
+\xfd\xe9\x9f\xc8\xfc\xfd\x5f\x64\xa0\xb9\x44\x5f\x35\x22\x4c\x02\
+\x82\xc4\x60\x8c\x60\x1c\x48\xe1\xf7\x30\x31\x84\xa9\x21\xaa\x08\
+\x41\x28\x88\x03\x32\x10\x2b\x04\x89\x10\x56\x84\x30\xed\x9e\x13\
+\x24\x53\xc4\x79\xb0\x8c\xaa\x42\x58\x09\x30\xdd\x73\x16\xc4\x82\
+\x09\x84\xa8\x62\x88\xd2\x80\xde\x34\x62\xa0\xb9\xc4\xfc\x97\xbf\
+\xc0\x4b\x7f\xf6\x27\x32\x7f\xf0\xa0\x46\x95\x0a\x61\x14\x55\x92\
+\x24\xb9\xad\x5a\xad\xfe\xc6\xe8\xe8\xe8\xdd\x4f\x3f\xfd\x74\x1d\
+\xe0\xf6\xdb\x6f\x7f\x59\xb2\xb4\xa6\x80\x1d\x3b\x76\xf0\xae\x77\
+\xbd\xab\x3b\xf3\xb5\xc1\xc1\xc1\xb7\x56\x2a\x95\x4f\x24\x49\x72\
+\x47\x18\x86\xb1\x09\x43\x96\x8e\x1e\xd5\x43\x9f\xfe\x94\x9c\xfb\
+\xec\xa7\xd9\x12\x5b\xfa\x7b\x52\x0c\x5e\x30\xb1\x42\x60\x84\x20\
+\x36\x04\xa9\x21\x48\x0c\x62\x05\xca\x3d\x10\xf1\xca\xa8\x1b\xc2\
+\x54\x10\xdb\xdd\x0d\x81\x7a\xd3\x8f\xea\x86\x30\x11\x8c\x0a\xe4\
+\xfe\xb9\xc6\x79\x25\x45\x15\xe3\x5d\x47\x04\x0a\x4a\xb7\x32\xf4\
+\xf7\x24\x6c\x49\x95\x73\x9f\xfd\x34\x47\x3e\xfd\x29\x59\x3a\x76\
+\x4c\x4d\x10\x10\x86\x61\x9c\x24\xc9\x1d\x95\x4a\xe5\x13\x83\x83\
+\x83\x6f\x7d\xe2\x89\x27\x6a\x00\xef\x7a\xd7\xbb\xd8\xb1\x63\xc7\
+\x45\x17\xd8\xbd\x7b\x37\x00\x77\xde\x79\x27\x00\xf7\xdd\x77\x5f\
+\x7a\xe3\x8d\x37\xbe\xb5\x56\xab\xfd\xc7\x4a\xa5\xf2\xb3\x69\x9a\
+\x02\x68\x7b\x76\x96\xe7\xff\xfb\xff\x90\xa5\xaf\x7c\x81\x2d\xa9\
+\xa3\xb7\x9e\xe0\x1c\xb8\x86\x62\x62\x81\x42\x08\x13\x21\xac\x1b\
+\x4c\x22\x60\xa1\x98\x71\x04\x35\x0f\x82\x12\x42\xd8\x13\x10\x54\
+\x05\xd7\x56\x8a\x79\x87\x24\x94\xb1\x4e\x09\x07\x0c\x92\x0a\xb6\
+\xa9\x14\xcb\x16\xb5\x20\x06\x6c\x1b\x82\x61\x41\x02\xc5\x76\x94\
+\x62\xc5\x61\x33\x07\x81\x0f\xa7\xa6\x06\xc6\x08\x4b\xab\x1d\xce\
+\xb4\x84\xbe\x77\xdf\xc3\xab\x7f\xff\xdf\x69\xb2\x61\x18\x10\xe9\
+\x74\x3a\xb4\x5a\xad\x6f\x36\x1a\x8d\x3f\x78\xe9\xa5\x97\x76\x7e\
+\xe0\x03\x1f\x68\x97\xd8\xc6\x9d\x77\xde\x79\x29\x06\x7c\xe6\x33\
+\x9f\x49\xde\xf0\x86\x37\xdc\x5d\xab\xd5\xfe\x7d\xa5\x52\xf1\x71\
+\x5e\x95\xd6\xb9\x73\xf2\xd2\xff\xfe\x24\xcd\x47\x1e\x62\xb0\xb9\
+\x44\x5f\x6f\xea\xfd\x59\x3d\xa2\x87\x35\x3f\xdb\x02\x98\xd8\x5b\
+\x01\x16\xdc\xaa\x12\x0d\x1a\xb0\x3e\x22\x98\x54\xbc\x72\x3a\x8a\
+\x6b\x83\xe9\xf3\x3f\x6f\x97\x1c\xa6\x66\x90\x08\x5c\x47\xd1\xcc\
+\x63\x80\x33\x4a\x7e\xc1\x11\xf4\x79\x7c\xb0\x6d\x87\xcb\x14\x35\
+\x8a\x0a\xd8\x86\x2b\xf1\x41\xb0\x99\x63\x69\xb9\xcd\x7c\xb5\x8f\
+\xea\x3b\xfe\x05\x37\xfc\xee\x27\xa8\x6c\x1c\x51\x44\xe8\x74\x3a\
+\xd2\x6e\xb7\xbf\xd5\x68\x34\xfe\xdb\xbe\x7d\xfb\x1e\xfe\xd8\xc7\
+\x3e\xd6\xe9\x82\x7d\xd0\x15\xfe\xf3\x9f\xff\x7c\xed\x96\x5b\x6e\
+\x79\x6f\xad\x56\xfb\xfd\x34\x4d\x6f\x4f\xd2\x04\x51\xa5\x71\xe6\
+\x0c\x87\xff\xe2\x4f\xa5\xfd\xe4\xa3\x0c\x76\x96\xa9\x47\x31\x64\
+\x52\x9a\xbe\x10\x24\x86\xb0\x62\x08\x22\x83\x58\xaf\x10\x29\x40\
+\x72\x21\xac\x05\x04\x35\xe3\x7d\xdc\xfa\xe3\x74\x4a\x77\xa9\x1b\
+\x82\x9a\xf8\x90\x59\x78\xa5\xd0\xf1\x85\xb4\x89\xbb\xe7\xfc\x73\
+\x34\xf7\x80\xaa\x39\x98\x50\x08\x6a\x06\x91\x12\x4c\x73\x0f\x9c\
+\xb6\x05\x49\x12\x12\x49\x87\xd5\xe9\x29\x96\x66\xe7\xe9\xb9\xe6\
+\x5a\xe2\xde\x5e\x82\x20\x10\x60\x0b\xb0\x75\x78\x78\xb8\x71\xe7\
+\x9d\x77\x1e\xfb\xd2\x97\xbe\x94\x7f\xfc\xe3\x1f\xf7\x0a\xd8\xb5\
+\x6b\x57\xff\xf8\xf8\xf8\xfb\x6b\xb5\xda\x6f\x56\x2a\x95\x37\x24\
+\x69\x2a\xa2\xaa\xab\xa7\x4f\xc9\xd1\xbf\xfc\x8c\x34\x1f\x7f\x84\
+\xbe\xd5\x45\x6a\x12\x10\x88\x4f\x52\xc2\xd4\x20\x81\xf7\xd3\xc0\
+\x78\x7f\x36\xf8\xe3\x41\x45\x08\x02\x41\xb2\xae\x80\xa5\xcf\xa6\
+\x86\xb0\xba\xee\x98\xc1\x03\x63\x2e\x84\x55\x7f\x9f\x98\x12\x2c\
+\xd5\x2b\xcd\xb5\x14\x53\xf7\x96\x23\x94\xd1\xc5\x51\x46\x19\x90\
+\x58\x30\xb1\xf7\x24\x51\x21\x44\x30\xad\x16\xab\xa7\x27\x59\x5e\
+\x5e\x91\xda\xb6\x09\x89\xfb\xfb\x35\x8c\x22\x11\x91\xcd\xc6\x98\
+\xd1\x81\x81\x81\xce\xbd\xf7\xde\x7b\xe2\x4d\x6f\x7a\x53\xdb\x00\
+\xd4\x6a\xb5\xb7\xa7\x69\xfa\x91\x24\x49\x5e\x1b\xc5\xb1\x41\x55\
+\x57\x4f\x9f\x96\x63\x7f\xf5\x97\x34\xbf\xf6\x10\x3d\x0b\xf3\xd4\
+\xc4\xc7\x64\x29\xe3\x79\x10\x1b\x82\xa8\x1c\xac\x05\x32\x45\x80\
+\x20\x16\x6f\x11\x15\x03\xb9\x9f\x6d\x29\x04\x03\x84\xa9\x10\xf6\
+\xfa\x88\x21\x19\x98\x42\x90\x5c\x30\x85\x10\xa4\xa6\x3c\xb7\x2e\
+\x22\x14\x3e\xaa\x84\x69\x69\x65\xa9\xf1\x4a\xe8\x00\x85\x07\xc8\
+\xa0\x74\xb9\x30\xf1\x2e\x69\x0a\xf1\x79\xc2\xd2\x02\xad\xc7\x1e\
+\xe6\xf8\x67\xff\x92\xc6\xe9\xd3\x82\x53\x8d\xe2\xd8\x24\x49\xf2\
+\xda\x34\x4d\x3f\x52\xab\xd5\xde\xde\x4d\x85\x0d\xf0\xfa\x28\x8a\
+\x6e\x8a\xa2\x28\x0e\xc2\x90\xe5\x63\xc7\x38\xfe\x57\xff\x87\xd5\
+\x47\x1e\xa4\x6f\x71\x9e\x5a\x1a\x12\xc6\x81\x0f\x71\x5a\x9a\x24\
+\x3e\xdd\x35\x81\x1f\x84\x84\x7e\x76\xc8\x40\xb5\x74\x8f\x14\x82\
+\xe4\xe2\x71\xc9\xbc\xcf\x4b\xee\x05\x36\x55\x01\x2d\x5d\xa6\x34\
+\x7f\x29\x84\x20\x02\x62\x5f\x2d\x9a\x96\x81\x36\xa8\x51\xc8\x7c\
+\x2e\x61\x22\xc1\xa1\x60\x9d\x77\x0d\x29\x9f\x6f\xf0\x96\x47\x40\
+\xcd\x29\x6e\xfe\x02\xcb\x0f\x3d\xc8\x51\x07\x13\x1f\xfd\x57\xf4\
+\x4c\x6c\x27\x8a\xa2\xb8\x28\x8a\x9b\x80\xd7\x03\x7f\x17\x02\x71\
+\x51\x14\x7d\x22\x12\x19\x63\x40\x44\xdb\x27\x4f\xca\xfc\xdf\x7d\
+\x91\xe1\xa2\x45\x3d\x8e\x09\x23\x1f\x8f\x8d\x08\xb4\x0a\x74\x39\
+\x43\xe3\xd2\x4c\x43\x90\x24\xc2\x44\x31\x14\xa1\x9f\x71\x01\x71\
+\xbe\xc0\x09\x2a\x5e\x68\x2d\x1c\x34\xdb\xd0\x74\x5e\x71\xc9\xc5\
+\xf2\x58\x0a\x41\x0b\x01\xe3\x43\x1b\x49\x80\xd4\x0c\x8a\xa2\x2b\
+\xde\xc7\xc5\x78\x1c\x30\x36\x83\xb8\x40\x2d\x68\xcb\xa2\x4e\x71\
+\x01\xd8\x86\x85\x48\x90\x54\x10\x15\x02\x31\xd4\xa3\x00\x5d\x9c\
+\xe3\xc2\xdf\x7d\x91\xd6\xcf\xdd\x25\x3d\xd7\x5c\xa3\xc6\x18\x11\
+\x91\xa8\x28\x8a\x3e\x20\x0e\x81\x38\xcb\x32\x51\xf5\xc4\x93\x82\
+\x84\xed\x26\xfd\x0b\xf3\xf4\x8e\x0c\x60\xd4\x9b\x96\xcf\xe7\x0b\
+\xc2\xc1\x61\xd2\x89\x09\xa2\x81\x3a\xb8\xb2\xc4\x35\x19\x6e\x7a\
+\x1a\x37\x77\x01\x43\x41\x90\x78\xd3\x27\x2f\x77\xeb\x08\x7a\x87\
+\x08\xb6\x6c\xc1\x0c\xf4\x41\xe1\x6f\x34\xf5\xb2\x34\x9e\x2f\x50\
+\xcd\x20\x6b\xe1\x66\xe7\x71\xf3\x0b\x68\x2b\x47\x82\x32\x1d\xae\
+\x7b\x73\x93\xb4\x8a\x6c\xbc\x0e\x33\xba\x09\x57\x14\xc4\xab\x0e\
+\x35\x40\x00\x45\xcb\xe2\x6c\x81\xda\x36\xf9\xe2\x22\x3a\x3d\x8b\
+\x2e\x2e\xd0\x5b\x8d\x29\xce\xcf\x13\xb6\x5b\x94\x46\x8b\xaa\x6a\
+\x96\x65\x02\x44\x21\x10\xe4\x79\x1e\x38\xe7\xa4\xcb\xbf\xc5\x81\
+\xa1\xaf\x16\x78\x40\x52\xef\x6f\xea\x14\xbb\xda\xa6\xfe\xd6\xdb\
+\xd9\xf4\x5f\xff\x80\x78\xf3\x96\xb5\xba\xbd\x38\x7f\x96\xe5\x4f\
+\xfd\x39\xd9\xce\xbf\xc7\xb4\x67\x30\x95\x08\xb1\x06\xb7\x02\x18\
+\x41\xdb\x16\xee\xb8\x85\xe4\x77\x7e\x9f\xf8\x96\x9f\x59\x47\x84\
+\xf8\x64\x5c\x5b\x0d\x74\xf9\x02\x6e\x6e\x0a\xfb\xec\x33\xe4\x4f\
+\xee\xc2\x1e\x3e\x88\x5b\x6d\xf9\x7a\x22\x35\x38\x2d\x30\x9b\x46\
+\xa8\x7e\xe8\xb7\xa9\xbd\xf3\xfd\x5c\x89\x45\xb4\xab\xab\xe4\x67\
+\xa7\x69\xbf\x74\x80\x95\xa7\x76\xb1\xfa\xf4\x4e\xb2\xb9\x19\xfa\
+\xeb\x01\x49\x68\xd6\xf8\x45\xe7\x9c\xe4\x79\x1e\x00\x61\x57\x01\
+\x62\xad\x5d\x77\x81\xa2\x0e\xcf\xd9\xa8\xf7\xb9\x30\x16\x6c\x54\
+\x25\xdc\xb6\x95\x78\xf3\x96\x4b\x0a\x09\x73\xd5\x46\x82\x6d\x5b\
+\xa1\xa7\x17\x6d\xcc\xf8\x4a\xb0\x4c\x5b\x83\x41\xa1\xdd\x50\x5a\
+\x81\x12\xa9\x12\x5f\x46\x92\xa0\x20\x95\x1a\xa6\x52\x23\xd8\x38\
+\x4e\x78\xd3\x1d\x84\x3f\x77\x37\xd9\xfd\x7f\x41\xbe\xeb\x61\xf4\
+\x78\x03\x71\xe0\x54\xc8\x33\x25\x2a\xf4\x15\xcb\xd8\xb0\x5e\x27\
+\xbc\xf6\x3a\x2a\xd7\x5e\x47\xcf\x5b\x7f\x81\xe5\x1d\x0f\x73\xfe\
+\xcf\xff\x98\x7c\xe1\x19\x9c\x73\x6b\xf2\x59\x6b\xc9\xf3\x5c\x80\
+\xc0\x5c\x89\x95\x55\xa7\x68\x07\x5c\xc7\x97\xad\x18\x50\x57\x10\
+\x5d\x37\x41\x72\xc3\x75\x2f\xe7\xaa\xc5\x10\x6d\x1b\xc7\x0c\x6f\
+\xc4\xb5\x42\xdc\xac\x43\x97\x94\xa0\xcf\x63\x84\x8b\xa1\x48\x15\
+\x17\xac\x11\x5c\xa8\xb5\x74\x1a\xab\x74\x9a\xab\x14\xad\x16\xea\
+\xdc\x9a\x60\xc1\xe6\xad\x84\xef\xb8\x07\x19\xdb\x82\xe9\x0d\xb0\
+\xf3\x8a\x9d\x71\xe4\x8b\x0e\xdb\xd1\x4b\x18\x63\xe7\x1c\xce\x59\
+\x5c\x79\xff\x7a\x65\xf4\xbd\xfd\x5d\xf4\xbe\xfd\x97\x09\x7a\x36\
+\x78\x17\x5e\xc7\x30\x77\x3f\xc3\x4b\x14\x50\x5e\x20\x02\x12\x81\
+\x89\x7d\xfc\x56\x0b\x2e\xcb\x49\xb6\x6c\x27\xdc\x76\xed\x95\xcb\
+\xca\x8d\x63\x98\xf1\xab\xe1\xe4\x0b\x04\xd9\xaa\x0f\x57\x6d\xa0\
+\xe2\x93\x1f\xb2\x92\xef\x2a\x85\x6c\x2e\x2d\x70\xe0\xef\xef\xc7\
+\x39\x4b\xda\x3b\xc0\xc6\x57\xdd\xcc\xf0\xc4\x35\xc4\x95\x2a\x12\
+\x04\x04\x9b\xae\xc6\x8d\xbf\x0a\xf7\x9d\x93\x04\xbd\x0e\x75\x86\
+\xa2\x66\x20\x94\xf5\x9c\x39\xf3\xa7\x4f\x32\x7f\xfa\x14\xce\x39\
+\xea\xc3\x1b\xe8\xdf\x3c\x4a\xb5\x7f\x00\x63\x0c\x26\xad\x30\xf0\
+\xcb\xef\x66\xe1\xe1\x87\x20\x0a\xd7\x94\xbc\x5e\x11\x21\x50\x6a\
+\xd1\xa1\x5a\x2a\xc3\xf8\x8c\x4b\x82\x92\xd4\xb4\x8a\xd3\x80\x78\
+\x6c\x2b\xe9\xd8\x38\x00\xce\x5a\xb2\x56\x8b\xb8\x52\xc1\x04\x01\
+\xe1\xc6\x51\xc2\xf1\x71\x8a\x81\x3a\x2c\xad\x22\x2a\xb0\x02\xb4\
+\x15\x6d\xe3\x51\x7e\x9d\xa1\xe5\x8b\x0b\x5c\xf8\xca\xe7\x20\xcf\
+\x28\xf2\x80\xe5\xdb\x6f\xc3\xdc\xf3\x51\x46\x5e\xfd\x7a\x7f\x41\
+\x94\x90\x0f\x6e\x01\x35\x44\x69\xc9\x2b\x24\xc6\x87\xdb\x35\x4b\
+\x75\x4c\x7f\xeb\x29\x0e\x7f\xf9\xf3\x34\xe6\xe6\x49\x37\x6f\x63\
+\xeb\x3f\x7f\x07\xd7\xbf\xf3\x9d\xf4\x8f\x6c\x46\x44\xa8\x4c\x5c\
+\x43\xb4\x6d\x2b\x54\x52\xaf\x00\xbd\x68\x39\x6b\x0a\x58\xf3\xff\
+\x2e\x07\x6b\x3d\x45\xe5\x72\xc5\x00\x06\x87\xd9\x34\x42\xb2\x75\
+\x1b\x51\x5f\x3f\x00\x59\xb3\xc9\xd9\x03\xcf\x33\x34\x31\x41\x7d\
+\xc3\x55\x44\x3d\x75\xc2\x4d\xe3\x14\xb5\x21\xf4\xdc\x39\x1c\xea\
+\x79\x81\x58\x7c\x9c\x0f\x29\x99\x62\xaf\x87\x48\x0b\xb6\x15\x33\
+\x24\x45\x46\x73\x36\x23\x7b\x21\xc6\x4e\x4f\x52\xdc\xf4\x3a\x42\
+\xe3\x73\x80\x4e\x1c\x11\x05\xa0\x2d\x87\x16\x0e\xd7\xe3\xc0\xae\
+\x77\x57\x25\x9a\x9b\xa3\xff\xe8\x11\xa2\xc9\xb3\x74\x4e\x9c\xe2\
+\x82\x38\xe6\xc6\xc7\xe8\x1f\xd9\xbc\xce\xa5\xc6\xd0\xb4\xba\x26\
+\x63\x17\x07\x5e\xa6\x80\xf5\x2c\xb5\xb3\x0a\xae\x54\x88\x58\xc2\
+\xed\xdb\x09\xb7\x8c\x23\x71\xe4\x15\xb0\xba\xc2\xe4\x53\x4f\x42\
+\x91\x53\xe9\xeb\x27\x4a\x53\xcc\xc6\x51\x18\x1e\xc3\x1d\x3a\x84\
+\xb1\x0e\x8d\xd5\xc7\x65\x2b\x10\xc0\x3a\x92\x9b\x00\x65\xb4\x2a\
+\xd4\x9c\x21\x6f\xa5\xac\xf6\x0e\x10\x26\xd5\x35\x23\x51\x55\x5a\
+\x8b\xe7\x09\x70\xb8\x0e\x3e\xee\x77\x4a\xee\xb0\x0b\xa2\x0a\x3d\
+\x61\xc4\xd5\xd5\x1a\x59\xb5\xce\xaa\x16\x64\xe7\x4f\x53\x4c\x9d\
+\x59\x7b\x86\x88\xe0\x7a\xfa\x21\x8a\xbc\x60\xe5\xf1\x4b\x14\xd0\
+\x45\xc8\xf5\x3c\x91\x04\xa5\x0b\x38\xc1\x59\x88\xc6\x27\x08\x47\
+\x46\x51\x13\xe0\x54\xc9\x97\x16\x59\xd9\xfb\x14\x0b\x3d\x3d\x8c\
+\xdc\xf2\x33\x84\x69\x8a\x8c\x8c\x22\xdb\x26\xd0\xef\x7d\x1d\x69\
+\xb4\xd0\x1c\x5c\x53\x71\x6d\xc5\x15\x0e\x67\x1d\x0a\x38\x55\x88\
+\x12\x64\xe2\x67\xb0\x6a\x91\xb1\x0a\xb5\x5b\xdf\x88\xd9\x3a\x81\
+\x31\x82\x05\x5a\x17\xe6\x68\x1f\x3c\x44\x3d\x2b\x90\x44\x20\x14\
+\x24\x16\x54\xbc\x1a\x7d\xa4\x72\x88\x58\xc2\xc8\x4b\xd2\x53\x80\
+\x86\x29\xf5\x4a\xa5\xbc\xc6\x21\x41\x40\x3b\xeb\x90\xd8\xe2\xe2\
+\x04\x5c\xee\x02\x97\xad\xbf\x79\x0c\x08\x40\x4a\x53\xd4\xb0\x4a\
+\x3c\xba\x95\x70\x70\x08\x44\xc8\xda\x6d\x9a\x67\x27\x89\x8e\xbd\
+\x48\x3e\x36\x81\x6d\xb6\x70\x7d\xfd\x04\x57\x6d\x24\xd8\xba\x15\
+\xdb\x57\x41\x8a\x16\x7a\x41\xb1\x40\xd1\xf6\x4a\x50\xa7\x9e\xf7\
+\xb7\x05\x66\xe8\x2a\xa2\x8f\xfd\x67\x54\x14\x89\xab\x04\xbd\x7d\
+\x48\xad\x8e\x5a\xcb\xca\xec\x0c\xd3\x5f\xdf\x05\x07\x8f\x20\xb9\
+\x85\xbe\xb2\x68\x4a\x14\x0d\xfd\x33\x9c\x3a\x54\x1d\x84\x01\x54\
+\x62\x4c\x5f\x42\xa0\x75\xa2\x89\x9b\xa8\x4e\x5c\x8b\x2d\x31\xc2\
+\x04\x01\xad\xf3\xd3\x04\xad\x66\xb9\x18\xa3\x2f\x07\xc1\xae\x39\
+\xac\x73\x2d\x9c\x53\x9c\x2a\x62\x95\x60\xeb\x16\xc2\xb1\x51\x24\
+\x4d\x51\xa0\x39\x7f\x81\xd9\xfd\xcf\x30\x94\x77\x60\xf2\x0c\x8d\
+\xd9\xf3\x04\x83\x83\x04\x49\x42\xb0\x61\x33\xb2\x61\x0b\x6e\x66\
+\x1e\x50\x82\x01\xc1\x34\x58\xe3\xf1\x1c\xe0\xac\x83\x30\x46\xae\
+\xbe\xc6\x27\x42\xea\xf1\x41\x8c\xa1\xbd\xba\xca\xf4\xb3\x7b\x39\
+\xf5\xe0\xff\xe5\x3a\xbd\x80\x29\x7c\x14\xc2\xe9\x1a\x9b\xec\x95\
+\xe8\xad\x36\x1c\xd9\x0c\x37\xdd\x8a\x1b\x6f\x52\x7f\xcd\xcf\x52\
+\xb9\xeb\xe7\x89\xb7\x4d\xe0\xb2\x0c\x55\xa5\x68\xb7\x29\x26\x27\
+\xa1\xd9\xf2\x18\x54\x0a\x7e\x65\x17\xd0\x8b\x20\x68\xdb\x8a\x69\
+\x3b\x50\x25\xda\xba\x0d\xd9\xb4\x19\x17\x45\x38\xa0\x7d\xe1\x02\
+\x17\xf6\x7c\x9b\xcd\x0d\xa1\x68\x2e\xb0\x78\xe2\x08\xc9\xe8\x18\
+\x95\x38\xc1\xf6\x5f\x45\x67\xf8\x7a\xcc\xdc\xb3\xa4\x83\xa1\x1f\
+\xac\x51\x48\x1c\x4e\x1c\xb6\x34\xdd\x35\xb0\x59\xf3\x39\x1f\x59\
+\x82\xb4\xc2\xf8\xeb\xdf\xc8\x55\xed\x0f\x23\x5f\xf8\x33\x68\xcf\
+\x52\xcc\x5a\xb2\x8e\x23\x37\x8e\xb8\x65\x4b\x25\xfa\xc5\x82\x9e\
+\x9f\x7b\x27\xbc\xe5\x17\x3c\x28\x18\x03\x81\x41\xad\xc3\x8a\xa0\
+\x45\xc1\x91\x5d\x4f\xd2\x38\x72\x9c\x81\xac\xc0\x18\xb3\x86\x77\
+\x97\xb8\xc0\xe5\x18\x20\xe2\xab\x3b\x89\x05\xd4\x10\x8f\x6d\x25\
+\xda\x30\x82\x44\x31\xce\x29\xc5\xfc\x2c\xee\xe0\x77\x08\x22\x47\
+\xfb\xdc\x0c\xed\x63\x47\xb0\xaf\xb9\x0d\xed\x1b\x20\xd8\xb8\x91\
+\xe8\xd5\x37\x52\x3c\xea\x17\x43\x24\x16\x6c\xc7\x2b\xd4\x59\xb7\
+\x06\x60\x9d\x95\x65\x26\x9f\xfd\x36\xea\x2c\x51\xa5\x4e\xff\xf8\
+\x38\xd5\xa1\x61\xc2\x38\x26\xe8\x1d\xa0\xf2\xc6\xb7\xd2\x39\x7d\
+\x92\xe2\xc4\x17\xa0\xa7\x8d\xa9\x0b\x52\xf7\x96\xa4\xeb\x73\x97\
+\x20\x44\xa2\xe8\xb2\xfc\x52\xb1\x79\xce\xd2\xd4\x24\xa7\xbf\xfa\
+\x65\xc2\xe9\xd3\x04\x5d\x8e\xed\x95\x30\xe0\x12\x96\x5c\x00\xe3\
+\x57\x70\x64\x68\x98\x68\xcb\x38\xa6\x56\x03\x11\x3a\x8d\x06\xf9\
+\xf2\x12\x7d\x7d\xbd\x44\x1b\x37\x12\x65\x06\x1a\x6d\x5c\x27\xc3\
+\xda\x02\x53\xeb\x21\xbe\x7a\x2b\xf9\x55\x43\xd8\xe3\x17\x10\xe3\
+\x28\x5a\xea\xa9\x2c\xab\xde\x77\x81\xe6\xcc\x0c\x2f\x7c\xf2\x0f\
+\x21\xcf\x50\x0c\xf5\x1b\xaf\x63\xfc\x17\xee\x66\xec\xb6\x37\x51\
+\x19\x18\x42\xeb\xfd\xd8\x57\xbd\x91\x9c\xfb\x89\xa2\x32\x22\x45\
+\x0a\x81\x4f\xd5\xbb\x02\xd8\xa2\x83\x2d\x0a\xbf\x8a\x64\x1d\x45\
+\x96\xd1\x59\x5d\xe1\xc2\xb1\x63\x9c\x7a\xf4\x61\xec\xd3\xbb\xa9\
+\xb5\x57\x09\x4c\x77\x61\x56\x5e\x9e\x09\x5e\xb4\x80\x75\x28\x59\
+\x28\xce\x7a\x1f\x0b\xb6\x6c\x81\x24\xc6\x15\x05\x36\xcf\x48\xc7\
+\xc6\xd9\xfc\xaf\x7f\x97\x38\x8a\x89\x73\x07\xa3\x63\x98\x4a\x15\
+\x9b\xe7\xbe\x2c\xee\x1f\xc6\x4e\xdc\x84\x9c\xd8\x45\x18\x19\x5f\
+\x4c\x19\x8f\x29\xea\xbc\x1b\x98\xbc\xcd\xe8\x85\x63\xc4\x79\x46\
+\xb6\x6c\x69\xce\x4f\xb3\xda\xd3\x4b\x63\x6c\x82\xb4\x7f\x08\x15\
+\x41\xae\x9e\xc0\xd6\x6b\x48\x63\x99\x22\xf7\x69\xb0\x2b\xdc\xda\
+\x78\x9d\x73\x9c\x3f\x74\x88\xb9\x43\x07\xc9\x5b\x2d\x6c\x9e\x93\
+\xaf\x2c\xd1\x3a\x7f\x8e\xd6\x91\xa3\xd8\x17\x0f\xb0\xa1\xb5\x42\
+\x54\xb8\x4b\x00\xee\xfb\x24\x42\xeb\xf2\x80\x02\xac\x1a\xe2\x91\
+\x51\xc2\xcd\xa3\x10\x46\xd8\xa2\xc0\x88\x50\xdd\xb2\x15\x19\xbb\
+\x1a\x05\x12\xca\x04\x27\x08\x70\x85\xf7\x4b\x57\xef\x87\x9b\x6e\
+\xc3\xee\x7c\x82\x30\x0d\xc1\x81\x0b\x40\x71\x6b\xb3\x17\x18\xd8\
+\x36\x54\xa7\x52\x74\xc8\x8d\x63\x31\xcb\xe9\xcc\xce\xe2\x96\x97\
+\x70\x28\x2a\x42\xd0\xdf\x4f\xde\xd7\x0f\x0b\xe7\xb0\x1d\x87\x6d\
+\x2b\x36\x2f\x05\x50\xc5\x15\x05\x8b\xcf\x7d\x97\xe9\x7f\xf8\x32\
+\xed\xb9\x79\x04\x07\x59\x0b\x5d\x5a\x22\x5e\x5c\x66\xd0\x2a\xf5\
+\x28\xf4\x39\x84\xbb\x58\x80\xfe\xc0\x3c\x40\x7d\x11\x08\x95\x0a\
+\xd1\x96\x71\xc2\x81\x41\xd4\x18\x70\x0e\x09\x43\x4c\x9c\xbc\xac\
+\x1c\x53\xeb\x50\x67\xb1\x45\x8e\xd4\xea\x44\x37\xdc\x4c\x3b\x48\
+\x08\xda\x4a\xd1\x76\xe5\xcc\x95\xc5\x8b\xfa\xff\xf3\x95\x8c\xb0\
+\xc8\x71\x6d\x4b\xb5\x56\x23\x19\x1e\x24\xa9\x56\xb1\x85\x2d\x81\
+\x52\x68\x4b\x40\x50\xae\xab\xab\x28\xaa\xae\x4c\xdd\xbd\x35\x85\
+\xf3\xb3\xf4\x9f\x3c\x44\x7e\x76\x06\x11\x43\x10\x08\x91\x18\xe2\
+\x20\xc0\x04\x1e\x17\xf2\x2b\xb4\xdd\x7c\xff\x3c\x40\x14\x8d\x04\
+\x19\x1e\x24\x9a\x98\x40\x92\xc4\x87\x94\x4e\x87\x4e\xa3\x41\xd1\
+\x69\xaf\xa5\xb5\xdd\x94\x2c\x8c\x63\xa2\x6a\x95\x30\x4e\x90\x28\
+\x26\xda\xb8\x89\xc6\xa6\xab\x29\xa6\x8f\x53\x34\x2c\xb6\xe3\x13\
+\x21\xd7\x15\x40\x05\x1b\x0d\x92\x4b\x86\xf4\x0b\xe6\xc6\xed\x04\
+\xaf\x7d\x1d\x66\xc3\x46\x5c\x91\xfb\x59\x6a\xb7\x69\xcd\x5c\xa0\
+\x12\x58\xbf\x86\x10\x2b\x1a\x5c\x54\xa2\xaa\xa3\x16\x1b\x92\x9e\
+\x14\xd7\xaa\x50\xb4\xf0\x8a\x2e\x93\x2d\x4f\x9f\xfb\xda\x06\xb9\
+\x72\xdf\xd1\x95\xf3\x00\xa7\x38\x0c\x3a\x38\x4c\xb0\x6d\x3b\x1a\
+\x04\x88\x31\xcc\x1d\x3f\xc6\x81\xaf\x7c\x89\x33\xdf\xde\x43\x10\
+\x45\x65\xd7\x12\x60\x2c\xc3\x37\xdc\xc4\x0d\xef\xf8\x45\x46\x6f\
+\x7d\x0d\x61\x10\xe2\x92\x94\xe2\xf5\xb7\x11\xee\x38\xe9\x31\x40\
+\xf4\x62\xd1\x55\x14\x04\x83\x43\xa4\xbf\xf5\x7b\x04\xce\x41\x5a\
+\xc7\x8c\x8f\x23\xc3\x1b\x21\xad\xe0\xac\xc5\xda\x82\xe5\xa9\xd3\
+\x98\x95\x65\x30\x0e\xeb\xc0\xe6\x65\x1e\xe0\x1c\xea\x9c\x27\x69\
+\x0a\xbf\xe4\x6e\x33\xc5\xa9\x40\xd0\x5d\x49\x2e\x6b\x1a\x51\xac\
+\x5b\x17\x71\xbb\xe4\xc9\xf7\x77\x01\xc5\x3a\x43\x30\xb8\x91\x74\
+\xfc\x6a\x08\x02\xd4\x18\x96\x8f\x1d\x41\x9f\xfe\x06\x63\x07\x5e\
+\x24\x4a\x22\x44\xfc\xb2\x95\x09\x1c\x79\x7b\x85\xec\xba\xeb\xc9\
+\xae\xbb\x1e\x93\xa6\x90\x26\xc4\x6f\xbc\x03\xbb\xf3\x7e\x9c\x71\
+\x68\x54\x9a\xb0\x75\xa8\x75\x50\xad\x11\xfe\xec\x5d\x17\xa3\x4f\
+\x18\xfa\x38\x0e\x20\x86\xce\xf2\x32\x87\xbe\xf4\x79\x36\x6b\x93\
+\xce\xbc\xc5\x15\x96\x3c\xb5\xd8\xce\xc5\x38\xae\xce\xe1\xda\x8e\
+\x7c\xd9\xe1\x56\x1d\x92\x1a\x24\x2e\x59\x6a\x05\x9b\xf9\x88\x63\
+\xf3\x2e\xea\xcb\x95\x5d\xe0\x65\xc5\x90\x31\xc8\x60\x1f\xf1\xab\
+\x6e\x24\xac\x54\x10\x63\xc8\x0b\x8b\x9e\x3a\x4d\xcf\xd9\x29\x7a\
+\x13\x25\xaa\x94\xa1\xa9\x80\x30\x14\x16\xcf\x4e\xc1\xd4\x24\xb6\
+\xd5\xf2\x1d\x40\x71\x42\xe5\xda\xeb\x59\xee\x1d\xc0\x2e\x2c\x63\
+\x33\x8b\x77\xeb\x92\x08\x15\x83\x24\xe9\x25\x21\x49\xfd\x09\x16\
+\xcf\x4e\x73\x7a\xc7\x43\x98\x3d\xbb\x30\x2b\x6d\xcf\x1d\x5a\x45\
+\x52\xf5\x33\x2c\xdd\xe7\x08\x9a\x80\x54\xbc\x7b\x38\xa7\x90\x7b\
+\xf3\x77\xae\x24\x72\xe4\xe5\xed\x38\x3f\x44\x31\x24\xb8\x34\xa5\
+\x55\xad\x32\x73\xf2\x24\x26\x8c\x68\x2c\xcc\xb3\x7a\xf0\x30\x49\
+\xab\x45\x1c\x19\x8c\x94\x99\x65\x01\xd2\x86\xb8\xdd\x24\x3f\x7a\
+\x8c\xb9\x17\x0f\xb0\xda\x69\x21\x4e\x61\x65\x85\x4e\xcf\x26\x8a\
+\xce\x24\xed\xc5\x06\x8b\xd3\x53\xe4\x27\x8e\xe1\xda\xed\x75\xf8\
+\xe1\xf3\xfa\xac\xb9\x42\x67\x71\x81\xa5\x93\x27\x59\xdc\xbf\x1f\
+\x79\xe1\x79\xc6\x16\xe6\x30\x56\x3d\x00\x2b\x58\x2d\x58\x99\x9f\
+\xc5\x1e\x3d\x8a\xeb\xb4\x21\x2f\xe8\x2c\x2c\x80\x2d\x20\x04\xd7\
+\x71\xd8\xac\xeb\x6e\xde\xfc\xdd\xba\xe8\xe6\x09\xce\x2b\x58\x00\
+\x5d\x98\x28\x19\x91\x70\x60\x40\x64\xe3\x26\x8e\x7c\xf9\x4b\xb4\
+\x1f\x7f\x1c\x4c\x80\x74\x9a\x0c\x9c\x99\x64\x28\x2f\x70\xa1\xff\
+\x91\xf2\x79\x88\x40\x12\x05\x2c\xbc\xf0\x1c\x53\x4b\x0b\x74\x7a\
+\x7b\x11\x20\xca\x2d\xa3\x33\xa7\x49\x43\xa1\x79\xfa\x04\x67\xff\
+\xf6\xaf\x29\x1e\xec\x87\x52\xfb\xdd\x2e\x10\xcd\x14\x8a\x0e\xd2\
+\x69\x22\xf3\xf3\xc4\x73\xb3\x0c\xda\x0e\x3d\x71\x0c\x45\xd7\x94\
+\x85\xf6\xec\x3c\xb3\x0f\xfd\x03\x2b\xcf\x7c\x1b\xb5\x16\x93\x2b\
+\x23\x33\x53\xf4\x2c\x37\x90\x42\x71\xca\x25\xd5\xa2\x07\x42\xa8\
+\xde\x7c\x23\xe1\xe0\x20\x65\x25\x24\xeb\xfa\xb5\xbc\x02\xf2\x3c\
+\x6f\x34\x9b\xcd\x22\xcb\x32\xc2\x38\x96\x74\xeb\x36\xbd\xea\x97\
+\x7e\x59\x2e\x7c\xee\xaf\x59\x7d\x7a\x0f\x0a\x54\x42\x88\xd2\x18\
+\x09\x23\x0a\x27\x18\xd5\x72\x61\x04\x34\x12\x82\x28\x20\xba\x30\
+\x43\x38\x79\x86\x5c\x9d\x27\x22\x82\x80\x70\xb8\x4a\x14\x85\x54\
+\x96\x2e\xd0\x3a\x3b\x8d\x5a\xbb\x56\x6e\xfb\x16\x19\xd0\xa6\x12\
+\x8a\xef\x30\xa9\x06\x21\xbd\x69\x48\xad\xc7\x87\xda\xa2\xed\x28\
+\xda\xe5\xd2\x57\xd6\x24\x98\x79\x1e\xf3\xbd\x67\xfc\xac\xb6\x15\
+\x4d\x13\x5c\x12\x43\xee\x85\x27\xf4\x5c\x86\x53\x87\x4a\x40\x65\
+\xfb\x35\x6c\xb8\xe7\x5e\xd2\x6d\x13\x8a\x73\x92\x65\x19\xcd\x66\
+\xb3\xc8\xf3\xbc\xd1\x55\x80\xce\xcc\xcc\xbc\x34\x39\x39\x79\x32\
+\x4d\xd3\x9e\x8d\x41\x10\x56\x47\x46\xd8\xf4\xde\xf7\x91\x0a\xcc\
+\xdd\x77\x1f\xed\x93\xc7\x11\x57\x78\x9a\x1c\xc5\xaa\x2f\x35\x05\
+\xf1\x09\x46\xee\x4d\xa1\x1e\x84\xf4\xf4\x44\x7e\xcd\x4f\x7d\x4d\
+\x61\xc4\x40\xa6\xf4\x07\x01\x83\x3d\x35\x24\xe8\x22\xb2\x10\x94\
+\x3d\x9e\x36\x2a\x01\xca\x74\x13\x16\xf5\xb3\x2e\x42\xd1\x2d\xd2\
+\x14\x12\x23\x6c\xae\xa4\x8c\x06\x09\xaa\x50\xc4\xd6\x27\x6d\x65\
+\x5f\xa1\xba\x6e\x06\xeb\xd0\x20\x22\xdd\x3a\xc1\xf0\xfb\x3e\x40\
+\xff\xaf\xbc\x07\xed\x1f\xa0\xd9\x6a\x32\x3b\x3b\x5b\x4c\x4e\x4e\
+\x9e\x9c\x99\x99\x79\x09\x9f\x9f\x91\x2c\x2d\x2d\x35\x92\x24\x89\
+\xa3\x28\xda\x98\x26\xc9\x50\xa5\x52\x91\xa0\x56\xd3\xca\xb6\x09\
+\x31\x81\xa1\x3d\x39\x45\xb1\xb4\x84\x2b\xac\xaf\x0f\x42\x5f\x57\
+\x28\xfe\x07\xd5\xe1\x5b\x5b\x04\x24\x12\x4c\x59\x9b\x14\x6d\x8f\
+\xc2\x9a\x79\xbe\x91\x08\x24\x36\x28\x50\xb4\x7c\x32\x53\xe4\x3e\
+\xbb\x33\x69\x49\x78\xa8\xc3\xe5\x8a\xcd\x1d\xd6\x2a\x2e\x73\xbe\
+\x49\x2a\x2a\xc1\xab\xbc\xde\x5a\x1f\x02\x25\x04\x82\x2e\xcd\xa5\
+\xd8\xc2\x41\x10\x92\x5e\xbd\x9d\x0d\xf7\xdc\x43\xff\x7b\xde\x83\
+\xeb\xeb\xd3\x4e\xa7\x23\x67\xcf\x9e\xd5\x43\x87\x0e\x1d\x79\xe6\
+\x99\x67\x1e\x7c\xec\xb1\xc7\x1e\x9b\x9d\x9d\x9d\x0f\x80\x60\x76\
+\x76\x36\x3f\x72\xe4\xc8\x5c\xa5\x52\x21\x8a\xa2\x91\x38\x8e\x07\
+\x7b\x7a\x7b\xc5\x54\x2a\x5a\x9d\xb8\x06\x11\x95\xec\xec\x34\xf9\
+\xc2\x22\x58\x77\x91\xdb\x2b\x57\x6a\x25\x10\x14\xf1\x40\xda\xf5\
+\x41\xeb\x97\xb3\x25\x64\xcd\x27\x11\x0f\x33\x2e\x2f\x41\x28\xea\
+\xc6\x64\x5d\x2b\xd5\xbb\x35\x88\x1a\x01\x51\x6c\xc1\x9a\x65\xd8\
+\xdc\xb7\xda\x69\xd9\x57\xe1\x4d\x1d\xd4\x09\xb6\xcc\x0f\x30\x01\
+\x95\x6d\x13\x0c\xbf\xff\xfd\x0c\xbe\xf7\xfd\xea\x7a\xfb\xc8\xad\
+\x95\xd3\xa7\x4e\x71\xe0\xc0\x81\x63\xdf\xfc\xe6\x37\x1f\xb8\xef\
+\xbe\xfb\x1e\x3a\x7d\xfa\xf4\x19\xa0\x15\x74\xd7\x67\x5a\xad\x56\
+\x7e\xf4\xe8\xd1\xf3\x49\x92\xb4\xe2\x38\xde\x14\xc7\xf1\x50\x5f\
+\x7f\x3f\x52\x49\xa9\xdd\x70\xa3\x18\x55\x3a\x93\xa7\xc9\xe6\x2e\
+\xac\x09\xae\x56\x3d\x6d\x66\xc0\x18\xf5\x1d\x23\xb9\x4f\x42\x5c\
+\xee\xdb\xe2\x24\x34\xe0\xb3\x68\x5c\xae\x14\x1d\x3f\x53\x12\x97\
+\x4d\x51\x52\x0a\x97\x95\x25\x73\xb9\x0e\x21\x21\x68\xd9\x03\x50\
+\x74\x1c\x45\xae\xb8\x52\x19\x84\x5d\xc1\xfd\x33\xbb\x33\xef\xd4\
+\x51\xd9\x3e\xc1\x86\x0f\x7c\x90\xc1\x0f\x7e\x08\x5b\xaf\x53\x58\
+\xcb\x89\xe3\xc7\xe5\xb9\xe7\x9e\x3b\xf2\x8d\x6f\x7c\xe3\x8b\x5f\
+\xf9\xca\x57\xbe\xb6\xb4\xb4\x34\x89\xe7\xac\x9b\xdd\x06\x09\x07\
+\xd8\x2c\xcb\xf2\x53\xa7\x4e\xcd\x85\x61\xb8\x9c\xa6\xe9\x58\x1c\
+\xc7\x43\xbd\xfd\xfd\x22\x49\xa2\x3d\x37\xbf\x1a\x51\xa4\x79\xf4\
+\x18\xf9\xfc\x05\x4c\x14\x96\x83\xd7\xb5\x19\x52\xe7\x85\x92\xd0\
+\xcf\x9e\x2b\xd4\x77\x7f\xba\x8b\x33\x29\x51\x49\x4b\x59\x20\xf4\
+\x94\x9b\xed\xf8\xf5\x3d\xc2\x12\xa8\x9d\xef\x1d\x76\x40\xd1\xb4\
+\xde\x52\x02\x59\x8b\xef\x5d\x58\x28\x0a\x8f\xf2\x2a\x8a\xed\x74\
+\x48\xc7\xaf\x66\xe4\xd7\x3f\xca\xf0\xaf\xff\xba\x16\x69\x4a\x6e\
+\xad\x74\x85\x7f\xea\xa9\xa7\xfe\xe6\x91\x47\x1e\xd9\xb9\xb2\xb2\
+\x32\x09\x2c\x02\x2d\xc0\x76\x2d\xc0\x89\x88\x05\x6c\xa7\xd3\xc9\
+\xce\x9f\x3f\xbf\x14\xc7\xf1\x6a\x9a\xa6\x9b\x2a\x69\x3a\x54\xad\
+\xd5\x24\xa8\x56\xa5\x76\xdd\x75\x1a\x55\x2a\xd2\x38\x74\x90\x6c\
+\x61\xbe\x64\x5f\x14\xe7\x04\x6b\x3d\xe5\x2c\xeb\x92\x0f\x97\x97\
+\xa0\x54\xb6\xbe\x11\x78\x41\xbd\x39\x7b\x8e\xc0\xe6\x1e\xb4\x24\
+\xf4\x1c\x84\x73\x4a\x51\x5c\xea\xeb\x5d\xc5\x38\xf5\xee\x51\xe4\
+\xba\x66\xf2\x8a\x62\xdb\x1d\xe2\xd1\x51\xc6\x7e\xf3\xb7\x19\xba\
+\xf7\x83\x6a\x6b\x35\x69\xb7\x5a\x32\x79\xe6\x0c\xcf\x3f\xff\xfc\
+\xe1\xa7\x9f\x7e\xfa\x0b\x3b\x76\xec\xd8\xbd\xb8\xb8\x78\x1a\x58\
+\x14\x91\x06\x90\xe3\xf9\x95\x8b\x15\x40\xa9\x04\xd7\x6a\xb5\x3a\
+\x33\x33\x33\x0b\x95\x4a\x25\x8b\xa2\x68\x43\x25\x4d\xfb\xd2\x38\
+\x0e\xe2\xa1\x21\xa9\x6c\xbd\x5a\xa3\x9e\x1e\x69\x1e\x3d\x4a\xbe\
+\xb0\xe0\x99\xf3\xb2\x5f\x68\xad\xde\xee\x22\xb2\x2b\xc1\xf2\x22\
+\xc3\xee\xf9\x46\xeb\xcf\xa9\x78\xcc\x70\x76\x5d\xfc\xb6\x25\x06\
+\x94\x4f\xb2\xa5\x85\x75\x85\x2f\xd7\x36\x7c\xc1\xe3\x7c\x5a\x9d\
+\x8e\x5f\xcd\x96\xdf\xfa\x1d\x06\xde\xfb\x5e\x65\x68\x48\x56\x17\
+\x17\x99\x9a\x9a\xca\x9f\x7f\xfe\xf9\x23\x7b\xf7\xee\xfd\xf2\xe3\
+\x8f\x3f\xfe\xc4\xdc\xdc\xdc\xa9\xae\xf0\xaa\x9a\x77\xd3\xb0\xe0\
+\xb2\x2c\x71\x4d\x09\x8d\x46\x23\x9b\x99\x99\xb9\x50\xa9\x54\x5c\
+\x1c\xc7\x43\x95\x4a\xa5\x2f\x0a\x82\x20\x1d\x1a\x96\xea\xf6\x6b\
+\x34\x88\x22\x69\x9d\x3e\x43\xbe\xbc\xe4\xeb\x70\xfc\xb4\xbb\x6e\
+\x58\x2a\x0f\xad\x37\x79\xe7\x74\x4d\x40\xa4\x34\x79\xf1\xdc\x83\
+\xb3\x9e\x2c\xed\x0a\x4f\xe8\xc3\xa2\x2d\x13\x1c\x57\x86\x38\xd5\
+\x12\x14\x71\x38\x0c\x95\xad\xdb\xd9\xfc\xd1\x7f\xc9\xd0\x3d\x1f\
+\x54\x06\x87\x64\xf9\xc2\x1c\x67\xcf\x9e\xcd\x0e\x1c\x38\x70\x74\
+\xdf\xbe\x7d\x0f\x3e\xf6\xd8\x63\x8f\x9e\x3f\x7f\xfe\xf4\x95\x84\
+\xbf\x92\x02\xba\x4a\x70\x7e\xb5\x79\xb5\x33\x37\x37\x37\x97\x24\
+\x89\x89\xe3\x78\x30\x4d\xd3\xbe\x24\x0c\x83\xa8\xb7\x57\x7a\x6e\
+\x7c\x95\xaa\x75\xd2\x99\x9e\x26\x5f\x5a\x42\x0b\xeb\x05\x2f\x53\
+\x4f\x5f\x72\x7a\x25\x38\x57\xe6\xfb\xee\x22\x25\xdd\x6d\x8b\x77\
+\x0e\x4f\x94\x96\xd7\xbb\x2e\x75\x8e\x77\x07\x6b\x75\xcd\x9a\xdc\
+\xc5\xe5\x6d\x34\x88\xa8\x6c\x9b\x60\xe4\xde\x0f\xb2\xf1\xc3\x1f\
+\x51\x5b\xab\xc9\xca\xc2\x02\x53\x53\x53\xd9\x0b\x2f\xbc\x70\x74\
+\xef\xde\xbd\x5f\xdb\xb1\x63\xc7\x23\xd3\xd3\xd3\xa7\x80\x05\x11\
+\x69\x5e\x2e\xfc\x2b\x29\xa0\x9b\xa5\x5b\xc0\x2e\x2f\x2f\xb7\x66\
+\x66\x66\xe6\xa2\x28\x92\x28\x8a\x86\xd2\x34\x1d\xa8\x56\xab\xc6\
+\x24\x09\xf5\x1b\x6f\x14\xa3\x8e\xf6\xe4\x14\xd9\xe2\x92\x6f\x50\
+\x30\xbe\x27\x00\x29\x73\x83\x12\xb8\x5c\xb9\xe6\xe8\x43\xa6\x9f\
+\x59\xd5\x75\x16\x51\xce\xb8\x67\x7a\xdc\xda\x71\x67\x3d\x76\x78\
+\x32\x44\xb1\xd6\xc7\xf9\xca\xc4\x04\x9b\xee\xb9\x97\x0d\xbf\xf6\
+\x6b\xd8\x34\xa5\xd5\x6c\xca\x99\x33\x67\x8a\x03\x07\x0e\x1c\xd9\
+\xb3\x67\xcf\x23\x3b\x76\xec\x78\x78\x6a\x6a\xea\x78\x09\x78\x0d\
+\xdf\xe5\xf0\xf2\xb6\x82\xef\xa7\x00\xd7\x7d\x7f\x61\x65\x65\xa5\
+\x31\x37\x37\x37\x17\x86\xa1\x0b\x82\x60\x43\x9a\xa6\xc3\x7d\x7d\
+\x7d\x62\x92\x44\xeb\xd7\x5e\x87\xc1\x48\x6b\x72\x92\xce\xc2\xfc\
+\x1a\xf3\xeb\x5c\x59\xfa\xae\x95\x18\xac\x2d\xbe\x6a\xd7\x15\xd6\
+\x7a\x11\xbc\xc7\x3b\x55\x6c\xd7\x55\xba\xee\xd4\xcd\xe9\x9d\x17\
+\x5e\x4d\x40\x75\xfb\xb5\x6c\xbe\xf7\x57\xd9\x70\xcf\x3d\xea\x6a\
+\x35\xf2\xa2\x90\x13\x27\x4e\xf0\xbd\xef\x7d\xef\xf0\xde\xbd\x7b\
+\xbf\xba\x73\xe7\xce\x47\xa6\xa7\xa7\x8f\x77\x43\x5d\x17\xf0\xae\
+\xf8\x1a\xda\x0f\x68\xa7\xef\x2a\xc1\xae\xac\xac\xb4\x2e\x5c\xb8\
+\x30\x63\x8c\xe9\xa8\xea\xc6\x30\x0c\x87\x87\x86\x86\x90\x34\xa5\
+\xb6\x7d\xbb\x04\x41\x40\x7b\x6a\x8a\xf6\xcc\x9c\x4f\x56\xba\xb4\
+\x9a\xf1\x4d\x53\x97\x08\xee\xca\xc6\xa6\xf2\xb5\x99\xae\x95\x58\
+\xab\x17\x5d\x48\x64\xcd\x0d\x9c\xfa\x64\xc9\x39\xa8\x5f\x77\x3d\
+\xa3\xbf\xfa\x21\x36\xbc\xef\x7d\x68\x6f\x2f\x85\xb5\x1c\x3c\x78\
+\x50\x9e\x7d\xf6\xd9\x43\xfb\xf6\xed\xbb\x7f\xf7\xee\xdd\x5f\x9b\
+\x9e\x9e\x3e\x09\x2c\x95\xc2\x67\xaf\x24\xfc\x0f\xa3\x80\xcb\x95\
+\xd0\x39\x7f\xfe\xfc\x9c\x31\x66\x25\x08\x82\xd1\x20\x08\x86\x07\
+\x07\x07\x25\xa8\xd5\xb4\xb6\x7d\xbb\x04\x61\x48\xeb\xc4\x71\x1a\
+\x8b\x8b\xd8\xf2\xfd\x87\xc2\x2a\x85\xf3\xbb\x75\x17\xbf\x17\xd6\
+\xef\xd6\x2a\x79\x79\x6e\xfd\xf9\xbc\x3c\x5f\xa8\x5f\x10\xb6\x40\
+\xef\xb6\xad\x6c\xf9\xc8\x47\xd9\xf0\x81\xf7\xa3\x7d\xfd\x9a\xe5\
+\xb9\xbc\xf8\xe2\x8b\xb2\x7f\xff\xfe\xc3\x7b\xf6\xec\xf9\xdb\x5d\
+\xbb\x76\x3d\xd1\x05\xbc\x32\xce\xe7\xdf\x4f\xf8\x1f\xf6\x8d\x11\
+\x05\x0a\x11\x69\xa9\x2a\x73\x73\x73\x6e\xf7\xee\xdd\x3b\xad\xb5\
+\xd6\x39\xf7\x11\xe0\xba\xeb\x6f\xb8\x41\xaa\x03\x03\x3a\xfc\x9e\
+\xf7\x30\x74\xfb\xed\xe2\x96\x97\xaf\xdc\x8f\x2f\xeb\x8f\xe8\x2b\
+\x74\xee\xeb\x25\x2f\x0d\x5d\xb2\x64\xd9\xdb\x8b\x6c\xde\xac\x32\
+\x30\x40\xab\xd9\x92\x43\x07\x0f\xf2\xec\xb3\xcf\x1e\xde\xb3\x67\
+\xcf\x67\xbf\xf1\x8d\x6f\xec\x9e\x9f\x9f\x3f\x0d\x2c\x97\x63\x2d\
+\x7e\x90\xf0\x3f\xca\x2b\x33\x4e\x55\x73\x11\xdf\x4c\x36\x3f\x3f\
+\x7f\x6a\xe7\xce\x9d\x4f\x1a\x63\x22\x11\xf9\xd5\x30\x0c\xaf\xd9\
+\xba\x6d\x9b\xe9\xdd\xb8\x91\x78\x7c\x5c\x83\x20\x90\x4b\x49\xd3\
+\xf5\x0b\x37\x3f\x68\x4c\xf2\xf2\x37\xad\x4a\xaa\xda\x5a\xab\x59\
+\xa7\x23\xcb\x8b\x8b\x9c\x3c\x71\xc2\xed\xdf\xbf\xff\xe8\xbe\x7d\
+\xfb\x3e\xbf\x73\xe7\xce\x27\x57\x57\x57\xcf\xfc\xa8\xc2\xff\xa8\
+\xaf\xce\xaa\xaa\xae\x59\xc2\xea\xea\xea\xe4\xe3\x8f\x3f\xfe\x68\
+\x10\x04\x49\x18\x86\xef\x06\xae\x19\xdb\xb2\x25\x4c\xd3\x6e\x91\
+\xaf\xfc\x54\x37\xaf\x50\x69\xb7\xdb\x4c\x9e\x39\x53\xec\xdf\xbf\
+\xff\xe8\x33\xcf\x3c\xf3\x95\xc7\x1f\x7f\xfc\xd1\x46\xa3\x31\xf5\
+\xe3\x08\xff\xe3\xbc\x3b\xac\xa5\x25\xa0\xaa\xd2\x68\x34\xd8\xb5\
+\x6b\xd7\x83\xc6\x18\x03\xbc\x73\x69\x69\x69\x6b\x4f\x4f\x4f\xa8\
+\xfa\xd3\x96\x7e\x6d\xcd\x52\x56\x56\x56\x8a\xe3\xc7\x8f\x9f\xdc\
+\xb7\x6f\xdf\x23\xbb\x76\xed\x7a\xf0\x32\xe1\xf3\x7f\x92\x77\x87\
+\xcb\x2d\x06\x52\xa0\x77\x6c\x6c\x6c\xeb\xeb\x5e\xf7\xba\x3b\xfb\
+\xfb\xfb\x5f\x15\x04\x41\xdd\x39\xa7\x3f\xe1\xb3\xaf\xa8\x7c\x63\
+\x8c\x58\x6b\x57\x17\x17\x17\x5f\xfc\xce\x77\xbe\xb3\x7b\x72\x72\
+\xf2\x24\xb0\x8c\x6f\xc7\xca\xfe\xc9\x5e\x9e\x5e\x77\x6f\x58\xf6\
+\x81\xd5\x81\x1e\xa0\x56\x2a\x26\x58\xf7\x2e\xe4\x4f\x45\xf8\x75\
+\xc9\x59\x56\x26\x36\x2b\xc0\x6a\x89\xf6\x3f\x92\xd9\xaf\xdf\xfe\
+\x1f\xba\xc9\xa0\xf1\xd3\x43\x85\x2f\x00\x00\x00\x00\x49\x45\x4e\
+\x44\xae\x42\x60\x82\
+\x00\x00\x02\x73\
+\x89\
+\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
+\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\
+\x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\
+\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0d\xd7\x00\x00\x0d\xd7\
+\x01\x42\x28\x9b\x78\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\
+\x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\
+\x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x01\xf0\x49\x44\
+\x41\x54\x38\x8d\xa5\x92\x4f\x6b\x1a\x61\x10\xc6\x7f\xae\x8a\x55\
+\x8a\xed\xcd\x20\x14\xb2\x87\x88\x87\x42\x4d\x3c\x49\x3d\xec\x25\
+\x64\x03\x7b\xc9\xc1\xdc\x3c\xf5\xb0\xb7\xc6\x9b\x5f\x20\x1f\x40\
+\xea\xb9\x9f\xc0\xe0\xa9\x37\x21\x14\x12\x59\x58\xd8\x84\x42\xa1\
+\xe0\x69\x89\x60\x4c\x2f\x49\xb5\xa8\xc4\x3f\xd3\x43\xde\xb5\x26\
+\x48\x28\x74\xe0\x85\x79\x67\x9e\x79\x98\x99\x67\x42\x22\xc2\xff\
+\x58\xe4\x99\x5c\x1a\x78\xa9\xfc\xdf\x40\x6f\x2d\x4a\x44\x56\xdf\
+\x2b\x11\xd1\x45\x24\x6d\x59\xd6\x01\x70\x03\xdc\x58\x96\x75\x20\
+\x22\x69\x11\xd9\x54\x98\x65\xcd\xa3\x62\xdb\xb6\x77\x43\xa1\x90\
+\x97\x4a\xa5\x8e\x63\xb1\xd8\x09\x70\x09\x5c\xc6\x62\xb1\x93\x54\
+\x2a\x75\x1c\x0e\x87\x1d\xdb\xb6\x77\x57\x49\x42\x2b\x3b\xd8\x8c\
+\x46\xa3\x9f\x67\xb3\x59\x52\xb5\x3e\x01\xc6\x2a\x17\x07\x5e\x00\
+\xa3\x48\x24\x72\x37\x9d\x4e\x3f\x00\x3e\x80\xb6\x32\xcd\x7d\xb9\
+\x5c\xfe\x02\x0c\x81\x6b\xe0\x56\x91\x4c\x94\x7f\x0d\xfc\x52\x98\
+\xfb\xa7\x4b\x4c\x97\x4a\xa5\x9d\x56\xab\xb5\xa5\x0a\x48\x26\x93\
+\x77\x95\x4a\xe5\x1b\x40\xad\x56\x7b\x37\x18\x0c\x5e\x03\x34\x9b\
+\xcd\xad\xe1\x70\xb8\xd3\x68\x34\x00\x7a\xc1\xfc\x19\xe0\x02\x68\
+\x01\x4d\xa0\x59\xad\x56\x8f\x44\x64\x5b\x44\xb6\xab\xd5\xea\x51\
+\x10\x57\x98\x0b\x11\xc9\x88\xc8\x5f\x19\x35\x4d\xfb\xb1\x58\x2c\
+\x42\xc1\xbf\xdd\x6e\x0f\x80\xfe\x8a\x3f\x51\xa9\x89\xa6\x69\x3f\
+\x97\x75\x81\xce\xae\xeb\x9e\x19\x86\xd1\x09\xe6\x76\x1c\x67\x5f\
+\xd7\xf5\x43\x5d\xd7\x0f\x1d\xc7\xd9\x0f\xe2\x86\x61\x74\x5c\xd7\
+\x3d\x53\xb7\xb1\x24\xe8\xe5\xf3\xf9\xd3\x6c\x36\x7b\x15\x00\xe7\
+\xf3\xf9\xc0\xf7\xfd\x3d\xdf\xf7\xf7\xe6\xf3\x79\xd0\xc1\x24\x9b\
+\xcd\x5e\xe5\xf3\xf9\x53\xd4\x61\x3d\x92\x31\x91\x48\x7c\x1c\x8f\
+\x03\xe5\xd6\x5b\x3c\x1e\x67\x34\x1a\x7d\x62\x8d\x8c\xb7\x9e\xe7\
+\xb9\xb9\x5c\xae\x5f\xaf\xd7\x3b\xa6\x69\x76\x79\xb8\x83\xb1\x69\
+\x9a\xdd\x7a\xbd\xde\xc9\xe5\x72\x7d\xcf\xf3\x5c\x1e\x64\xe5\x69\
+\x07\x00\x1b\xc0\x7b\xa0\xeb\x38\x4e\xba\x58\x2c\xbe\x05\x38\x3f\
+\x3f\xff\x5e\x28\x14\x7a\xc0\x1b\xa0\x1d\x2c\x77\x1d\xc1\xaa\x65\
+\x00\x43\xf9\x5f\x81\xce\x3a\xd0\x73\x04\xff\x64\x7f\x00\x19\x1e\
+\x09\x5d\x0e\xd9\xff\x6d\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\
+\x60\x82\
+\x00\x00\x02\x4a\
+\x89\
+\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
+\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\
+\x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\
+\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0d\xd7\x00\x00\x0d\xd7\
+\x01\x42\x28\x9b\x78\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\
+\x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\
+\x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x01\xc7\x49\x44\
+\x41\x54\x38\x8d\xa5\x93\xcd\x6a\x1a\x61\x14\x86\x1f\x33\x0e\x05\
+\x4d\x47\xda\x88\x64\x53\x14\x83\x43\x76\xc6\x95\x4c\x5d\xb9\x33\
+\x05\x17\xdd\x64\x99\x45\x2e\x20\xbd\x82\x76\x51\x7a\x03\xbd\x82\
+\x76\x93\x95\x0c\x82\x8b\xe2\x76\x32\xe8\x6a\x04\x85\x04\xa2\xd0\
+\xa2\x14\x84\x80\xc5\x9f\x5a\xb5\xf0\xa5\xa7\x1b\x2d\x93\xa1\x29\
+\x14\x0f\x9c\xcd\x39\xe7\x79\x39\x3f\xdf\x17\x12\x11\xb6\xb1\x9d\
+\xad\xe8\x07\x04\x12\x40\x1a\x88\xfa\x62\x51\xe0\x60\x9d\xfb\xa7\
+\xc0\x7e\x3e\x9f\x3f\xd6\x75\xfd\xa3\x6d\xdb\xe6\x1a\x8c\xda\xb6\
+\x6d\xea\xba\xfe\x21\x9f\xcf\x1f\x03\xfb\xf7\x08\x11\xd9\x78\xc2\
+\xb2\xac\x53\xa0\x0b\x5c\x69\x9a\x76\x59\xad\x56\x8f\xaa\xd5\xea\
+\x91\xa6\x69\x97\xc0\x15\xd0\xb5\x2c\xeb\x54\x44\x12\x1b\x2e\xe4\
+\x5b\x62\x3a\x95\x4a\xbd\x1f\x0c\x06\x4f\x00\x01\x1e\x85\xc3\xe1\
+\x39\x80\x52\x6a\x17\xf8\x09\x84\x92\xc9\xe4\xb8\xdf\xef\xbf\x02\
+\xbe\x04\x47\xb8\xf5\x3c\xef\x22\x93\xc9\x5c\x03\x0b\x60\xac\x94\
+\xd2\x94\x52\x1a\x30\x06\x16\x99\x4c\xe6\xda\xf3\xbc\x0b\xe0\x76\
+\x03\x85\x02\x67\x34\x46\xa3\xd1\x8b\x42\xa1\xf0\xbc\xd7\xeb\x3d\
+\xf3\x27\x4c\xd3\xfc\xda\x68\x34\x9a\xf1\x78\xfc\x13\x30\x7b\x68\
+\x89\x77\xad\x56\xab\xb7\x5c\x2e\xf7\xd6\x5d\xfc\xf1\xd5\x6a\xf5\
+\xb4\xdd\x6e\x77\x81\x3b\x3f\xe0\xef\x20\x5a\xaf\xd7\x0f\xca\xe5\
+\xf2\x6b\xa5\x94\x02\xe6\x01\xf1\x5d\x5d\xd7\xb5\x5a\xad\xf6\xae\
+\x54\x2a\x7d\x06\x7e\x04\x05\xd2\xb1\x58\xec\xcd\x6c\x36\xfb\x05\
+\x7c\x07\xc8\x66\xb3\xdf\x00\x3a\x9d\xce\xde\xba\xe6\xb1\x61\x18\
+\x3b\xd3\xe9\xf4\xed\xdf\x96\x38\xaf\x54\x2a\xcd\x48\x24\x32\x04\
+\x16\xb9\x5c\x6e\xe8\x38\xce\x8d\xe3\x38\x37\xb9\x5c\x6e\x08\x2c\
+\x22\x91\xc8\xb0\x52\xa9\x34\xef\x75\xe7\x7b\x07\x88\xc8\xa1\xeb\
+\xba\x67\xc5\x62\xf1\x7c\x32\x99\x9c\x88\x88\x21\x22\xc6\x64\x32\
+\x39\x29\x16\x8b\xe7\xae\xeb\x9e\x89\xc8\xa1\x9f\x09\x0a\x20\x22\
+\xa6\x88\xbc\x5c\xc3\x9b\x98\xb1\x8e\x99\xc1\xfa\xe0\x19\xff\xdb\
+\xb6\xfe\x8d\xbf\x01\x7c\x52\x08\x38\x8e\xd7\x1d\xc8\x00\x00\x00\
+\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
+\x00\x00\x07\x84\
+\x47\
+\x49\x46\x38\x39\x61\x36\x00\x37\x00\xf3\x00\x00\xff\xff\xff\x00\
+\x00\x00\x78\x78\x78\x1c\x1c\x1c\x0e\x0e\x0e\xd8\xd8\xd8\x54\x54\
+\x54\xdc\xdc\xdc\xc4\xc4\xc4\x48\x48\x48\x8a\x8a\x8a\x00\x00\x00\
+\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x21\xff\x0b\x4e\
+\x45\x54\x53\x43\x41\x50\x45\x32\x2e\x30\x03\x01\x00\x00\x00\x21\
+\xfe\x1a\x43\x72\x65\x61\x74\x65\x64\x20\x77\x69\x74\x68\x20\x61\
+\x6a\x61\x78\x6c\x6f\x61\x64\x2e\x69\x6e\x66\x6f\x00\x21\xf9\x04\
+\x09\x0a\x00\x00\x00\x2c\x00\x00\x00\x00\x36\x00\x37\x00\x00\x04\
+\xcc\x10\xc8\x49\xab\xbd\x38\xeb\xcd\xbb\xff\x60\x28\x8e\x64\x69\
+\x9e\x25\x92\x10\x44\x82\xa0\xa7\xc2\xce\x84\x02\x93\x08\x4d\xbf\
+\x77\xb8\xea\xac\x44\x2f\x04\xa4\x0d\x41\xc5\xd9\xf1\xf3\x03\x0a\
+\x97\x9d\x5c\x91\x77\x2c\x18\x58\x86\x02\x46\xa6\xb3\x2d\x0b\x03\
+\xda\x40\x7b\x51\x05\xa9\x16\xc1\x60\x20\x18\x5d\x75\x06\x93\x80\
+\xd6\x26\x16\x4d\xe1\xd9\x40\x94\xc4\x8b\x45\x6f\x34\x71\x25\x73\
+\x33\x75\x20\x07\x79\x2c\x03\x07\x27\x6a\x6c\x24\x07\x6f\x06\x8d\
+\x50\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x1c\x8f\x87\x96\xa1\x1a\
+\x85\x2c\xa2\x47\xa6\x04\xa8\x14\x8a\x04\x7b\x96\xae\xb0\x17\xb2\
+\x97\xb5\x18\xaa\xac\x3d\xb9\xa5\x6b\xba\x43\xa4\x9f\xc2\xc3\xc4\
+\xc5\xc6\xc7\xc8\x9b\xc1\x8e\xbe\x24\xbc\x72\x74\x23\xb7\x25\xd3\
+\x20\xd5\xd2\x7f\x22\xcf\x84\xd1\x23\xcb\x72\xcd\xc9\x9e\xdf\xc5\
+\xdb\xc5\xd7\xc3\xe8\xc2\xe6\xe5\xe1\xe2\xef\x3d\x11\x00\x21\xf9\
+\x04\x09\x0a\x00\x00\x00\x2c\x00\x00\x00\x00\x36\x00\x37\x00\x00\
+\x04\xce\x10\xc8\x49\xab\xbd\x38\xeb\xcd\xbb\xff\x60\x28\x8e\x64\
+\x69\x9e\x68\xaa\x16\x06\x41\x18\x85\x5a\x16\x83\xeb\x0e\xb1\x2c\
+\xb6\xb6\x6b\xe8\xa2\x5e\x0f\x18\x12\xda\x88\x20\x5e\xef\x87\xf4\
+\x1c\x6a\xb6\xc1\xe1\x82\x48\xb8\x12\x88\xa6\xe4\xc0\x33\x4c\x2d\
+\x0a\xa3\x62\x24\x18\x0c\x04\x26\x84\xd1\x95\x05\x09\x7a\x68\x92\
+\x75\x9d\x08\x41\x6f\xa5\xf5\x11\x74\x27\x0c\xf2\x7a\x04\x21\x6f\
+\x36\x71\x23\x73\x46\x75\x83\x66\x86\x23\x6a\x6b\x6d\x5a\x1a\x61\
+\x42\x63\x92\x1c\x55\x57\x91\x97\x9c\x9d\x9e\x9f\xa0\xa1\x6e\x8c\
+\x97\x65\x67\x1a\x84\x2e\x8d\x40\xa9\x04\xab\x14\x7d\x7f\x4d\xb1\
+\x19\xb4\xb3\x3d\xb2\x17\xad\xaf\x32\xbb\xa8\xa4\x92\xa6\xbc\xa2\
+\xc4\xc5\xc6\xc7\xc8\xc9\xca\x27\xc2\x29\xcd\x64\x70\xcc\xd1\x22\
+\xb6\x25\xd5\x7c\xb8\x27\xd7\x1f\xbe\x26\xdd\x22\xcf\xcc\xc0\xcb\
+\x9e\xe1\xc7\xdf\xc6\xdb\xc4\xea\xa2\xe8\xc6\xe6\xe4\xf1\x27\x11\
+\x00\x21\xf9\x04\x09\x0a\x00\x00\x00\x2c\x00\x00\x00\x00\x36\x00\
+\x37\x00\x00\x04\xce\x10\xc8\x49\xab\xbd\x38\xeb\xcd\xbb\xff\x60\
+\x28\x8e\x64\x69\x9e\x68\xaa\xae\xac\x30\x0c\x02\x5b\x0a\x44\x4d\
+\xc4\xb2\x38\xd8\xc4\x90\xeb\x3c\xdf\x0f\x44\xb3\xe1\x86\x1f\x17\
+\x0c\x99\x2b\x18\x6a\x86\x02\x49\x79\x24\x15\x76\xb6\x81\x34\x54\
+\xac\x55\x45\x4f\x1e\xc1\x00\xcc\x9a\xc4\xb6\x72\x4d\x48\x42\xd7\
+\x44\xdd\x9b\x29\xcc\x23\xc3\x5f\x5f\xd1\x01\xbb\x3e\x30\x3b\x07\
+\x61\x06\x7e\x7f\x85\x86\x87\x88\x89\x8a\x12\x08\x09\x35\x09\x08\
+\x8b\x15\x0a\x68\x0a\x19\x54\x43\x08\x6e\x04\x91\x16\x71\x79\x29\
+\x8e\x6e\x09\x17\x7c\x3d\x3f\x9b\x6f\x16\xa6\x6c\x2c\xa9\x04\x17\
+\x9f\x3f\xa2\x68\xa4\xb1\x78\x99\x9b\x9d\x92\x94\x62\x96\x92\x13\
+\x8d\x8f\xbb\xc0\xc5\xc6\xc7\xc8\xc9\x13\x98\x28\xcc\x70\x3c\xa0\
+\x44\xd0\x23\xac\x27\xd5\x6a\xa7\x26\xd7\x5c\xd3\x26\xb2\x23\xce\
+\xde\xb8\xca\x88\xe1\xc5\xdf\xc6\xdb\xc5\xea\xc0\xe8\xc6\xe6\xe4\
+\xf1\x26\x11\x00\x21\xf9\x04\x09\x0a\x00\x00\x00\x2c\x00\x00\x00\
+\x00\x36\x00\x37\x00\x00\x04\xcd\x10\xc8\x49\xab\xbd\x38\xeb\xcd\
+\xbb\xff\x60\x28\x8e\x64\x69\x9e\x68\xaa\xae\xac\x30\x0c\x02\x5b\
+\x0a\x44\x4d\xc4\xb2\x38\xd8\xc4\x90\xeb\x3c\xdf\x0f\x44\xb3\xe1\
+\x86\x1f\x17\x0c\xc9\x34\x29\x8f\xce\x17\x94\xc8\x9b\x8a\x8a\x35\
+\x6b\x67\x67\x13\x96\xb8\x35\x2f\x08\xdc\x3b\x91\xc5\xc9\xea\x09\
+\x7b\x23\x3d\x53\xef\xa6\x7c\x4e\xaf\xdb\xef\xf8\xbc\xaa\x60\xa8\
+\x19\x0a\x1a\x71\x3f\x05\x67\x80\x17\x6c\x5a\x29\x7d\x3c\x04\x06\
+\x18\x67\x43\x8c\x36\x8f\x41\x91\x92\x04\x18\x88\x43\x8b\x3c\x8e\
+\x99\x52\x48\x07\x67\x07\x7a\x13\x07\x8b\x06\xa4\xa5\xab\xac\xad\
+\xae\x27\x08\x09\x35\x09\x08\xac\x0a\x92\x0a\x23\x82\x23\x08\x97\
+\x04\xb5\x54\x46\x25\xb2\x97\x09\x21\x90\x24\xbe\x35\xc7\x95\xc9\
+\xca\x21\x9a\x24\xc4\x92\xc6\xd0\xa0\x25\xbd\x97\xc0\x79\xb7\x8c\
+\xb9\x48\xbb\x15\xb1\xb3\xdb\x3f\xd1\xab\xc8\xe8\xcd\xab\xe7\xec\
+\xd7\xaf\xf0\x2b\x11\x00\x21\xf9\x04\x09\x0a\x00\x00\x00\x2c\x00\
+\x00\x00\x00\x36\x00\x37\x00\x00\x04\xcb\x10\xc8\x49\xab\xbd\x38\
+\xeb\xcd\xbb\xff\x60\x28\x8e\x64\x69\x9e\x68\xaa\xae\xac\x30\x0c\
+\x02\x5b\x0a\x44\x4d\xc4\xb2\x38\xd8\xc4\x90\xeb\x3c\xdf\x0f\x44\
+\xb3\xe1\x86\x1f\x17\x0c\xc9\x34\x29\x8f\xce\x17\x94\xc8\x9b\x8a\
+\x8a\x35\x6b\x67\x67\x13\x96\xb8\x35\x2f\x08\xdc\x3b\x91\xc5\xc9\
+\xea\x09\x7b\x23\x3d\x53\xef\xa6\x7c\x4e\xaf\xdb\xef\xf8\x3c\x3e\
+\x8e\xe4\x5f\xd8\x5a\x2a\x80\x19\x67\x48\x85\x18\x87\x3f\x89\x16\
+\x83\x43\x8d\x18\x7e\x3f\x91\x7a\x94\x95\x96\x97\x98\x99\x4c\x05\
+\x06\x35\x06\x05\x6e\x52\x27\x05\x67\xa0\x21\x8f\x23\x9d\x3c\x04\
+\x06\x40\x5d\x26\xab\x36\xae\x61\xb0\xb1\x04\x57\x6a\x25\xaa\x3c\
+\xad\x57\xa2\x19\x08\x09\x35\x09\x08\x17\x07\x67\x07\x43\x0a\xb1\
+\x0a\xc6\xaa\x06\xc9\x3f\x08\xb6\x04\xc5\x79\xc2\xb6\x09\x7a\xd4\
+\x35\xdb\xdc\x7a\xd8\xb1\xda\x79\xd3\xb6\xd6\x79\xcb\xab\xcd\x95\
+\xc1\xc3\xe7\x22\x11\x00\x21\xf9\x04\x09\x0a\x00\x00\x00\x2c\x00\
+\x00\x00\x00\x36\x00\x37\x00\x00\x04\xcc\x10\xc8\x49\xab\xbd\x38\
+\xeb\xcd\xbb\xff\x60\x28\x8e\x64\x69\x9e\x68\xaa\xae\xac\x30\x0c\
+\x02\x5b\x0a\x44\x4d\xc4\xb2\x38\xd8\xc4\x90\xeb\x3c\xdf\x0f\x44\
+\xb3\xe1\x86\x1f\x17\x0c\xc9\x34\x29\x8f\xce\x17\x94\xc8\x9b\x8a\
+\x8a\x35\x6b\x67\x67\x13\x96\xb8\x35\x2f\x08\xdc\x3b\x91\xc5\xc9\
+\xea\x09\x7b\x23\x3d\x53\xef\xa6\x7c\x4e\xaf\xdb\xef\xf8\x3c\x3e\
+\x8e\xe4\x5f\xd8\x5a\x2a\x80\x19\x67\x48\x85\x18\x87\x3f\x89\x16\
+\x83\x43\x8d\x18\x7e\x3f\x91\x7a\x94\x95\x96\x97\x98\x00\x08\x09\
+\x35\x09\x08\x96\x0a\x3c\x35\x0a\x94\x08\xa2\x36\x9f\x33\x52\x22\
+\x9c\xa7\x04\x09\x33\x6a\x20\xae\x36\x5f\x41\x21\xb4\x35\xb6\x5d\
+\x21\xad\xa7\xb0\x6e\xb2\x1f\xa6\xae\xa9\x6e\xab\x22\xa1\xa2\xa4\
+\x17\x05\x06\x35\x06\x05\x48\x9b\x9d\xc6\x15\x05\x67\xd2\x94\xcf\
+\xa2\x06\x95\xb4\xdf\xae\x95\xdc\x3c\xde\x94\x07\x67\x07\x96\x07\
+\xdc\x06\xea\x99\x28\x11\x00\x21\xf9\x04\x09\x0a\x00\x00\x00\x2c\
+\x00\x00\x00\x00\x36\x00\x37\x00\x00\x04\xcc\x10\xc8\x49\xab\xbd\
+\x38\xeb\xcd\xbb\xff\x60\x28\x8e\x64\x69\x9e\x68\xaa\xae\xac\x30\
+\x0c\x02\x5b\x0a\x44\x4d\xc4\xb2\x38\xd8\xc4\x90\xeb\x3c\xdf\x0f\
+\x44\xb3\xe1\x86\x1f\x17\x0c\xc9\x34\x29\x8f\xce\x17\x94\xc8\x9b\
+\x8a\x8a\x35\x6b\x67\x67\x13\x96\xb8\x35\x2f\x08\xdc\x3b\x91\xc5\
+\xc9\xea\x09\x7b\x23\x3d\x53\xef\xa6\x7c\x4e\xaf\xdb\xef\x22\x44\
+\xa2\x96\x40\xe0\x2b\x0a\x3c\x35\x0a\x7f\x12\x08\x82\x36\x7e\x3f\
+\x71\x16\x7b\x88\x04\x09\x8b\x6a\x16\x8f\x36\x3f\x67\x17\x95\x35\
+\x97\x41\x17\x8e\x88\x91\x39\x6c\x5a\x87\x8f\x8a\xa2\x52\x19\x81\
+\x82\x84\x85\x86\x8e\x7d\xae\xb2\xb3\xb4\xb5\x4c\x05\x06\x35\x06\
+\x05\xb3\x05\x67\xbc\x51\x4b\x22\xb9\x82\x06\x4e\x93\x20\x95\x26\
+\x98\x21\xca\x5f\x9d\x21\xc4\x3c\xc6\x33\xc8\x1f\x07\x67\x07\x6b\
+\xa9\x23\x07\xc4\x06\xda\xb6\x1d\x8c\xb2\xa3\xb4\xcc\xb2\xe8\xae\
+\xe6\xb4\xe4\xe2\xef\x27\x11\x00\x21\xf9\x04\x09\x0a\x00\x00\x00\
+\x2c\x00\x00\x00\x00\x36\x00\x37\x00\x00\x04\xcc\x10\xc8\x49\xab\
+\xbd\x38\xeb\xcd\xbb\xff\x60\x28\x8e\x64\x69\x9e\x68\xaa\xae\xac\
+\x30\x0c\x02\x5b\x0a\x44\x4d\xc4\xb2\x38\xd8\xc4\x90\xeb\x3c\xdf\
+\x0f\x44\xb3\xe1\x7e\x88\x44\x2d\x81\xd0\xb8\x60\x43\x80\x82\x57\
+\x53\x44\x39\x08\xaa\xad\x39\x7b\x1d\x3f\x4a\x2d\x21\x31\xe3\x7d\
+\x3b\x62\x5b\x69\x67\x13\x7e\xd2\xb5\x75\x30\x14\xd6\x92\x49\xc5\
+\xda\x19\x9b\xe6\xe2\xbd\x23\x53\x54\x56\x57\x58\x61\x4c\x85\x89\
+\x8a\x8b\x8c\x8d\x57\x05\x06\x35\x06\x05\x8e\x16\x05\x6c\x35\x03\
+\x94\x43\x4f\x7b\x13\x91\x54\x06\x9c\x66\x18\x69\x43\x98\x3d\xa5\
+\x62\xa7\x73\x17\xa0\x3c\xa2\x3f\x79\x37\x18\x07\xa8\x03\x07\x51\
+\x9d\x1a\x07\xa0\x06\xb9\x95\xc1\xc2\xc3\xc4\xc5\xc6\xc7\x1d\xbb\
+\x28\xca\x22\xb3\x9e\x44\xa4\x40\x6d\x27\xb7\x23\xd5\x26\xd7\x21\
+\xce\x27\xdb\x23\xcc\x26\xdf\xc8\x89\xe1\xc2\xdd\xc4\xd9\xc3\xe8\
+\xe5\xd1\xc5\xe4\xe2\xef\x25\x11\x00\x3b\x00\x00\x00\x00\x00\x00\
+\x00\x00\x00\
+\x00\x00\x03\x16\
+\x89\
+\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
+\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\
+\x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\
+\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0d\xd7\x00\x00\x0d\xd7\
+\x01\x42\x28\x9b\x78\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\
+\x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\
+\x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x02\x93\x49\x44\
+\x41\x54\x38\x8d\x95\x91\xbd\x4f\x1b\x41\x10\xc5\x67\xe1\x96\xbb\
+\xf3\x25\xd8\x46\xe1\x82\x20\x91\x2c\x45\x38\x42\x51\x94\x02\x19\
+\x21\x04\x1d\xa2\xa3\x80\xa3\x30\x05\x25\x12\x15\x32\x14\x44\x48\
+\xfc\x05\x54\x96\x28\x29\x4c\x45\x0f\xae\xa0\xc0\x20\x0a\x7a\xa0\
+\x20\x46\x48\x24\x0e\x8e\xb1\x8d\xe3\x23\xb7\xc7\xde\xde\xee\x5e\
+\x8a\xc4\x16\x1f\x49\xc1\x2b\x67\xde\xfc\xf4\x66\x06\x65\xb3\xd9\
+\x81\x20\x08\x52\x08\xa1\x38\x3c\x43\x41\x10\xe4\x11\x42\x69\xc5\
+\xf7\xfd\x85\x91\x91\x91\x01\xc3\x30\xd4\xe7\x00\x08\x21\xd1\xc3\
+\xc3\xc3\x05\x45\x4a\x19\x07\x80\x6a\xbd\x5e\x77\x9f\x03\xc0\x18\
+\x87\xa4\x94\x71\x85\x52\x0a\x94\xd2\x27\xc3\xb9\x5c\x2e\xba\xb7\
+\xb7\xf7\x2a\x16\x8b\xb9\xc9\x64\xb2\x64\x18\x86\xb8\xdf\x17\x42\
+\xb8\x94\xd2\x76\x85\x73\x8e\x82\x20\x68\x36\x6a\xb5\x9a\x92\x4a\
+\xa5\x3e\x5e\x5c\x5c\x84\x55\x55\x85\x83\x83\x03\xd8\xd8\xd8\x78\
+\x37\x3f\x3f\xff\x65\x6a\x6a\xea\xfa\x3e\x84\x73\x8e\x14\xc6\x18\
+\xe2\x9c\x37\x8b\x2b\x2b\x2b\x1f\x6e\x6f\x6f\xdb\x97\x96\x96\x2a\
+\x63\x63\x63\x2e\x00\xc0\xc9\xc9\x49\x5b\x22\x91\x70\xe6\xe6\xe6\
+\x3e\x25\x93\xc9\x6f\xc3\xc3\xc3\x3f\x01\x00\x18\x63\xa8\x85\x73\
+\x8e\xa4\x94\x20\xa5\x84\xb3\xb3\x33\xa3\x5c\x2e\x47\xa7\xa7\xa7\
+\x6b\x96\x65\x5d\xa9\xaa\x5a\x50\x55\xf5\x6a\x68\x68\xe8\x47\x26\
+\x93\xe9\xa8\x56\xab\xd1\xf5\xf5\xf5\xf7\x9e\xe7\x21\x29\x25\x70\
+\xce\x51\x0b\x63\x0c\x49\x29\x61\x7b\x7b\xbb\x6b\x6b\x6b\xeb\xad\
+\x69\x9a\x80\x31\xf6\x8e\x8e\x8e\x24\x63\x8c\x33\xc6\xd8\xf1\xf1\
+\x31\xdf\xdd\xdd\x8d\x99\xa6\x09\x9a\xa6\x69\xe9\x74\x3a\x2e\xa5\
+\x6c\x26\x00\x29\x25\x10\x42\xc2\xf9\x7c\xbe\x4b\xd7\x75\xd8\xd9\
+\xd9\x31\x4d\xd3\x0c\x1a\xc9\x84\x10\x6d\xcb\xcb\xcb\x15\x5d\xd7\
+\x61\x76\x76\xf6\x7a\x7c\x7c\xdc\xfd\x9b\x00\x94\xc6\x0a\x33\x33\
+\x33\x25\x00\x78\x71\x7e\x7e\xfe\x72\x71\x71\xb1\x18\x8d\x46\x5d\
+\x29\x25\x00\x00\xf4\xf6\xf6\x3a\x99\x4c\x46\xd5\x75\x1d\xfa\xfa\
+\xfa\xca\xba\xae\x13\xc6\xd8\x9f\x23\x36\x00\xae\xeb\xda\x96\x65\
+\x95\x4e\x4f\x4f\xef\x22\x91\x48\x79\x6d\x6d\x2d\xde\xdf\xdf\x5f\
+\xc1\x18\xcb\x5c\x2e\xf7\xc6\x71\x9c\xc8\xc4\xc4\x44\x05\x63\x5c\
+\xa7\x94\x8a\xe6\x17\xee\x1f\x11\x63\x5c\x1e\x1c\x1c\xbc\xcb\x66\
+\xb3\x86\x6d\xdb\x1d\x9b\x9b\x9b\xaf\x01\x00\x4c\xd3\x04\xcb\xb2\
+\x2a\x89\x44\xa2\xe4\x38\x8e\x78\xf0\x46\xdf\xf7\x51\x23\xaa\xe7\
+\x79\xbe\xe7\x79\xb5\xd1\xd1\x51\x6d\x72\x72\xf2\xeb\xcd\xcd\x0d\
+\xb6\x6d\x5b\xe9\xe9\xe9\xf1\x11\x42\xbf\x6c\xdb\xae\x35\xbc\x00\
+\x00\xbe\xef\x3f\x04\x34\x44\x08\xa1\x84\x90\xef\x9a\xa6\x69\x9d\
+\x9d\x9d\xd8\xb6\xed\x3b\x21\x04\x87\x47\xf2\x7d\x1f\x29\x00\x70\
+\x59\x28\x14\xc2\xdd\xdd\xdd\xec\xb1\x81\x10\x42\x01\x80\x3e\xae\
+\x03\x00\x14\x8b\xc5\x36\x00\xb8\x54\x42\xa1\xd0\xea\xfe\xfe\xfe\
+\x67\x21\x44\xec\x5f\xc6\xff\xa9\xb5\xb5\xf5\x32\x1c\x0e\xaf\xfe\
+\x06\x47\x78\x84\x8a\xac\x27\x36\x0e\x00\x00\x00\x00\x49\x45\x4e\
+\x44\xae\x42\x60\x82\
+\x00\x00\x0b\x2d\
+\x89\
+\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
+\x00\x00\xb0\x00\x00\x00\x7f\x08\x06\x00\x00\x00\x4f\x09\x48\x3f\
+\x00\x00\x00\x01\x73\x52\x47\x42\x00\xae\xce\x1c\xe9\x00\x00\x00\
+\x06\x62\x4b\x47\x44\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\
+\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\
+\x00\x9a\x9c\x18\x00\x00\x00\x07\x74\x49\x4d\x45\x07\xdc\x02\x18\
+\x13\x26\x10\x86\x4c\xd4\x8d\x00\x00\x0a\xad\x49\x44\x41\x54\x78\
+\xda\xed\x9d\x7d\x8c\x1d\x55\x19\xc6\x9f\xf7\x9d\x33\xf7\x63\xb7\
+\x1f\x6e\xb7\xdd\xb5\x95\xc6\xd6\xc0\xd6\xd6\x6a\xa2\x25\x01\x12\
+\x04\x34\x90\x28\x84\x10\x44\x4c\xd0\x04\x63\x24\x8a\x46\x08\x06\
+\x48\x34\x86\xc6\xe0\x3f\xc6\x36\x01\xb5\x2a\xa8\x21\x36\x18\x35\
+\x40\x88\x04\x0d\x92\x6a\x88\x46\x0b\x28\x51\x12\x15\x0a\xc5\x02\
+\x02\x02\x05\x04\xba\xed\xde\xbd\xf3\x71\x5e\xff\x98\x99\x7b\xe7\
+\xde\xdd\x6d\x31\xa1\xdd\x1e\xf2\xfc\x9a\xec\xbd\xf7\xcc\x99\x33\
+\x67\xde\xf3\xce\xdb\xe7\x9c\x39\x73\x46\xb0\xf9\x62\x4c\x24\xfb\
+\x44\x44\x60\x66\x80\x88\x08\x00\x03\x04\x66\x02\xa0\x9f\x56\xfd\
+\x06\xaa\x4f\xcc\xf7\xbb\xc8\x8d\xaa\x3c\xc0\xac\x97\x6e\xc5\x06\
+\xf4\x8e\x37\x1f\xe5\xfe\xa8\xf6\xaf\xbe\xd7\xb7\xcf\x53\xe6\x82\
+\xcc\x57\xc6\x82\x79\x01\x41\x59\x66\x45\x59\x0f\xe9\xfd\xb4\x81\
+\x73\xb5\x21\x23\xf4\xce\x1b\xd6\x2b\x4b\x16\x38\x9c\x1d\xa1\xfc\
+\x61\x9b\x0e\xec\x53\x1d\x7f\xc8\xce\xc3\x75\x9b\xb7\xdc\x5a\x79\
+\x83\xb6\x34\xc0\x8e\xd0\x1e\xc3\xed\x32\x4f\x59\x73\xea\x5c\xb5\
+\xf9\xe0\xf1\xac\x57\xa7\xa2\x5c\xab\x9f\xe2\xc0\xa7\x88\x95\x7e\
+\x69\xb5\xa6\x32\x37\x99\x3e\x29\x28\x8c\x20\x65\x21\x52\x5a\x45\
+\x00\x20\x95\x86\x66\x22\x6a\x88\xd4\x8b\xa8\x87\x8a\x8a\x89\xb7\
+\x5a\xed\x04\x52\x54\x4a\x6a\xc7\x95\xfe\x4f\xe9\xa7\x19\x0c\xfd\
+\x4b\x44\x06\x2f\x01\xab\xf6\x2e\x1a\xbe\xdf\xea\x65\xde\x5a\x9e\
+\x7e\x99\xd5\xc9\xd7\x8e\x5b\x6b\xea\x7e\x8a\x00\x62\xb5\xbc\xf5\
+\x63\x1e\xce\xc5\x16\xb8\x5c\x87\xeb\x62\xf3\xe7\x9f\x73\x2e\x75\
+\x3b\x49\xbf\x61\x07\x6c\x66\x47\xb0\x47\xb9\x7d\x4e\xba\x2c\x74\
+\x89\xc8\x3c\xdb\x0f\x93\xae\x35\x1b\x0d\xe5\x33\x18\x44\x8b\x8b\
+\xa6\x7e\xec\xb9\x6d\x36\x84\xce\x3d\xaf\xd2\x1d\xca\xcd\x66\x5e\
+\xc4\x14\xde\x14\xe6\x15\xde\x47\xe6\x7d\x6c\x89\x97\xca\xd3\x45\
+\xac\x76\xb1\x9b\x99\x89\x2b\x9d\x5e\xfa\xad\x5c\x34\x73\x2a\x0d\
+\x4d\xc5\xb9\x04\x91\xfb\xed\xce\x6f\xbf\xf3\xd1\xdd\x3f\xba\xb6\
+\x3d\x32\x3a\xd2\x6c\xb6\xdb\x52\xbf\xb4\x08\x79\x93\x30\x33\x9b\
+\xed\x76\x3a\x67\x9d\xf7\xe5\x1f\x3e\xfd\x9f\xfd\xcf\x9f\x7d\xe9\
+\x95\x4f\xc7\x12\x65\xb1\xcf\xb2\x06\x52\x6f\x43\xf1\x1c\x22\x26\
+\x13\x53\x5b\xb4\x8c\xb8\x10\x11\x4d\x10\x6b\x57\xe2\x28\x83\xc6\
+\x3b\xbe\xf0\xbe\xef\x2d\x5b\x36\xb6\x42\x55\xe9\xb0\xe4\x98\xe3\
+\xbd\xb7\x0f\x9e\x7b\xe5\x8e\xf5\x67\x7e\xfc\x4f\x0e\x79\xda\xb4\
+\x34\x8f\x91\x7a\x00\xbe\x94\xb4\xa5\x03\x03\x02\x11\x4d\xcc\x69\
+\xa6\xb1\xdb\x76\xdd\xd5\x93\xa3\x87\x1e\xdc\xde\x6a\xb6\xda\x34\
+\x23\x59\x6c\x66\xbb\xb3\x9d\x99\xd1\x53\xae\xb9\xfa\x1b\xdb\x5f\
+\x8c\x2d\xcb\x1a\x56\x38\x31\x44\x4c\xa5\x40\x53\xc4\x9a\x69\xec\
+\xb6\x6f\xbd\x66\xf5\xb2\xce\x43\x37\xd2\x79\xc9\xf1\x42\xab\xd9\
+\x6a\x2f\xed\x3c\x74\xe3\xf6\xad\xd7\xae\xce\x24\x76\xa9\x34\xb4\
+\x54\xd5\xe2\x00\x88\x99\x49\xaa\xce\x75\xd0\x6c\x8d\x4c\xdf\xbf\
+\x2d\x6e\xb5\xe3\x81\x50\x6e\xd2\x98\x9e\x7e\x6d\xb2\xd9\x1e\xcb\
+\x0c\x12\x19\x8c\x92\x82\xbc\xe9\x08\xc4\x04\x96\x9f\xb0\x6e\xf3\
+\xe3\xfb\x9f\xfd\xfb\xb8\x8a\x25\xd5\xb6\x38\x8e\xe3\xd1\xe9\xfb\
+\xb7\x75\xa4\x79\x19\x80\xd9\x18\x69\x2a\x80\x38\x03\x24\x97\x58\
+\x33\x44\xf1\xf7\x2f\xdf\xf4\x9d\x56\xab\xdd\xaa\xf7\x47\x73\x2f\
+\x23\x99\xd7\xb1\xa5\xcb\x27\x56\x9f\x73\xee\xa5\x2b\x68\x66\x72\
+\xb4\xd9\xfb\xc4\x63\xcb\x93\x5c\x0f\x39\xf5\xaf\x46\x6a\x33\x02\
+\x78\x00\x68\xb5\xda\xad\x7f\xed\xba\xf5\xb4\x77\x9f\x7d\xc9\x1f\
+\x33\x8b\x72\x27\x99\x39\x98\x49\xa2\xce\x25\x1a\x37\x96\x2f\x1f\
+\x1b\xab\x17\x94\x7b\x19\x49\x32\xff\x8e\xf3\x2f\xbc\x6c\x3d\xcd\
+\x4a\x8e\x15\x27\x9d\xb8\x61\xdd\x49\x27\x6e\xc0\x63\xfb\x9e\xb9\
+\xef\xa9\x7f\xde\x3b\xe2\xd4\x0e\x56\xdb\x1e\xd8\x75\xd3\x17\x13\
+\x8d\xff\xdc\x40\x96\xc5\x96\x7b\xcd\x24\x8e\x52\x38\x77\xfb\x0d\
+\x5b\xd7\xd5\x47\x1b\xbc\x49\x23\xf3\x3a\x76\xfe\x85\x9f\xa3\xf3\
+\x92\x45\x61\xc3\xbb\xd6\x7e\x68\x6c\xf5\xe6\xd4\x9b\x34\xaa\x34\
+\x55\x95\xdb\x6f\xd8\xba\x2e\x81\x73\xa9\x45\x91\xe6\xa2\x9a\x8b\
+\xc6\xfb\x1f\xb9\xed\xaa\x01\xe1\xbc\x64\x22\x1b\x1d\x5d\x3a\x49\
+\x33\x92\x45\x1d\x81\x38\xf0\xdc\xe6\x89\x13\xde\xfb\x4a\x3d\x6d\
+\x6a\xfd\xda\xc9\x1c\x1a\xe7\x1a\xa9\x7a\x88\x66\x88\xa2\xa9\x4d\
+\xa7\xed\xab\x67\x3a\xf0\xda\x4b\x93\x67\x9d\x7d\xf1\x4a\x9a\x90\
+\x2c\x26\x67\x7c\xf8\xa2\x55\xcf\x3e\xf5\x8f\xa9\x7a\xda\xfe\x27\
+\x77\x9f\x92\x69\x14\x79\xa8\xaa\x41\x15\xb0\xe8\xe5\x17\xf6\x6e\
+\xa9\x67\x32\x20\xa2\xf9\xc8\xf1\x80\x41\x06\x7c\xf1\xe5\x17\xf6\
+\x6e\x11\x20\x32\x88\x2a\x20\x0a\x68\x24\x73\x76\x02\x87\xca\xc8\
+\x71\xe2\xc0\x83\xc3\xb6\xe5\x34\xa9\xc8\x00\xd5\xb2\xcb\xa6\x34\
+\x13\x09\xcc\xad\x15\x10\xa8\x07\x94\x93\x73\x48\x88\xba\xc2\x60\
+\x45\x04\x7e\x23\xd3\x64\x09\x39\x9e\x28\x42\xae\x40\x21\xe5\x3f\
+\x42\x42\x0a\xc0\x56\x4c\xfd\x55\x30\xfa\x92\x80\xd1\xaa\x57\x47\
+\x48\xb0\x0e\x4c\x48\xd0\x0e\xcc\x4e\x1c\x09\xaf\x17\x57\x8f\xc0\
+\xd4\x10\x24\xb8\x5e\x5c\xf1\x47\x8b\x47\xbf\x09\x09\x56\x42\x08\
+\x07\x22\x48\x78\x0a\xa2\x37\x0e\x4c\x05\x41\x42\x54\x10\xbd\xf5\
+\x24\x84\x9d\x38\x12\xb2\x84\x30\xce\x3b\x23\x81\x6b\x60\x42\x02\
+\x14\x11\x00\x00\x57\xfb\x4e\x48\x68\xbd\xb8\xea\x4e\x1c\x3d\x98\
+\x04\x18\x80\xcd\x2a\x07\xa6\x8c\x20\x21\x06\x60\xe1\x5c\x08\x12\
+\x68\x00\xee\x0d\xa3\x31\x00\x93\x80\x61\x04\x26\x41\x0b\x61\x65\
+\x1f\x8e\x04\xae\x81\xe9\xbd\x24\xc4\xd8\xdb\x93\x10\x14\xc0\x24\
+\xc0\x08\xdc\x77\x60\xa3\x86\x20\x21\x6b\x60\x01\x97\x85\x20\xa1\
+\xc6\x61\x3e\x52\x44\xc2\x8c\xbd\xe5\x2b\xbb\x78\x2b\x99\x84\x19\
+\x7b\xcb\xf7\xcd\x15\x13\xda\x29\x21\x48\x88\x11\x18\x06\x35\x18\
+\x25\x04\x09\x57\x03\x73\x55\x29\x12\xa8\xef\x02\x00\x6f\x64\x90\
+\xb0\xd1\xe2\x95\xb3\x34\x04\x09\x52\x04\x83\x8b\xfb\x91\xa0\x35\
+\x84\x82\x9d\x38\x12\xa8\xff\x5a\xff\x4e\x1c\xed\x41\x42\x93\x10\
+\x80\x54\xb3\xd1\x18\x81\x09\x3b\x71\x84\x1c\x4b\x05\x51\x5f\x9d\
+\x92\x01\x98\x04\xa7\x20\x7a\xcf\xc4\x71\x36\x25\x09\xb4\x13\xd7\
+\x7f\xa4\x88\x1e\x4c\x02\xec\xc4\xf5\x1f\xab\xa7\x08\x26\x61\x7a\
+\x30\x97\x57\x25\x41\x6b\x08\x4e\x68\x27\x61\x47\x61\x2e\x6c\x42\
+\xc2\x8c\xbf\x52\x88\x08\xbe\x23\x83\x04\x2d\x22\xf8\x58\x3d\x09\
+\x53\x3c\xf4\xd7\x46\xa3\x00\x26\x61\x4a\x88\x72\x18\x8d\x11\x98\
+\x84\xd8\x7d\x63\x04\x26\x6f\x01\x0f\xe6\x30\x1a\x09\xb6\x07\xd7\
+\x9b\x0f\x4c\x48\x88\x11\x58\x7a\x2f\x3a\xa4\x0f\x93\x00\x23\x30\
+\x80\x62\x5d\x08\x42\x82\xd6\xc0\x84\x84\x0a\x3b\x71\x24\x6c\x09\
+\xc1\x95\x79\x08\x25\x04\x21\xc7\x3a\x00\x0b\x1d\x98\xbc\x15\x34\
+\x30\x47\x21\x48\xb8\x2a\xc2\x4a\x0d\x4c\x19\x4c\x82\xec\xc7\x09\
+\x1f\x29\x22\x81\x46\x5f\xeb\xdd\x4a\xe6\xca\x3c\x24\xcc\x4e\x1c\
+\x27\xb4\x93\xe0\x45\x04\x47\x21\x48\xd0\xf0\xa1\x4e\x12\xac\x06\
+\xee\x3b\x30\x35\x30\x09\x50\x03\x17\x8f\xd5\x0b\x23\x30\x09\x35\
+\x02\x0b\x1c\xa3\x2f\x09\x5c\x03\xd3\x83\x49\xb0\x71\x98\xc3\x68\
+\x24\x54\x11\x5c\xfc\xe1\x30\x1a\x09\x35\xf8\xa2\xb6\x3e\x30\x21\
+\x21\x47\x60\xaa\x08\x12\x5c\x04\xae\xad\x0f\xcc\x7e\x1c\x09\x2e\
+\x00\x4b\x6d\x7d\x60\x42\xc2\x0c\xc0\x5c\x1f\x98\x04\x2e\x84\x29\
+\x21\x48\xc0\x9d\x38\x4a\x08\x12\xac\x86\xe8\x4d\x68\x27\x24\xcc\
+\x08\xcc\xb5\xd1\x48\xe8\x0a\x82\xaf\x9a\x25\x81\x2a\x88\xfa\x33\
+\x71\xf4\x60\x12\x66\x14\xe6\x2b\x06\x48\xd0\x1a\x82\x9d\x38\x12\
+\xac\x86\x28\xbc\x98\x0e\x4c\x42\xf6\x60\x3a\x30\x09\x55\x42\xf4\
+\xe6\x42\x18\x87\xd1\x48\x80\x01\xb8\x17\x81\x85\x2b\xf3\x90\x00\
+\x03\x30\x67\xa3\x91\xf0\x03\x30\x1d\x98\x04\x2d\x81\xe9\xc0\x24\
+\xe8\x38\x4c\x07\x26\x01\x4b\x08\xe9\x8d\x03\xb3\x17\x47\x42\xd3\
+\x10\x85\x17\x6b\xff\x17\x21\x61\xf9\x6f\x79\x27\x8e\xd1\x97\x04\
+\x2a\x21\x00\xa8\x51\x42\x90\x10\x23\xf0\xe0\x8b\x0e\x29\x21\x48\
+\x60\x11\xb8\xfc\xe4\x13\x19\x24\x60\x0d\x61\xd4\xc0\x24\x94\x0e\
+\xdb\xdc\x54\x2b\x23\xb0\x61\xfe\xf7\x7d\xd3\xb3\xc9\xf1\xa6\x18\
+\x00\x00\xde\x7b\x03\x60\x6a\xe6\x55\x01\x33\x83\x9d\x71\xde\x15\
+\x77\xd4\x33\x29\x90\xd3\x6e\xe4\x38\x89\xc0\x03\xbe\x38\xdb\xed\
+\xa6\x22\x30\x11\x81\x0a\xcc\x2b\x2c\xbf\x73\xe7\xd6\x73\x06\x1c\
+\x58\x6d\xf6\x77\xbf\xf9\xe9\x4b\x34\x1f\x59\x4c\xee\xfc\xf9\xb6\
+\xe9\x83\xaf\x3f\xbf\xa4\x9e\xb6\xf1\xd4\x4f\xef\x14\xb3\x5c\x60\
+\x5e\x15\xde\x04\x96\x47\x6b\x3e\x72\x73\x3d\x93\x53\x1c\x9a\xed\
+\x26\xfb\x69\x42\xb2\x98\x34\x47\xc6\xff\xbd\x62\xfc\xed\x4f\xd7\
+\xd3\xfe\xf6\xe8\x13\xaf\xc2\x7c\xae\x30\x53\x35\xf3\x6a\x3e\xbf\
+\xfc\xfa\xef\x3e\x57\x6a\x8b\x22\x02\x8b\x25\x1b\x37\x9f\xfe\xc0\
+\x3d\x77\xef\xdc\x4b\x33\x92\xc5\xe0\x9e\xbb\x77\xee\xcd\xbb\x07\
+\xba\x2a\x96\xd4\xf5\xef\xe5\xd7\xef\x78\x2e\x82\xe5\xe2\x73\xef\
+\x14\xde\x3b\xe4\x59\x26\x3e\x7d\xe5\xd5\xff\x4e\xaf\x1a\x5f\xb9\
+\xac\xca\xbc\x6f\xcf\xef\xd7\x6e\xd8\x74\xe6\x1f\x7e\x7d\xd7\x2d\
+\x49\xd6\x3d\xb0\xf6\x82\x4f\x5c\xb5\x8c\x66\x25\x47\x9b\xbb\x6e\
+\xbb\xf1\x80\x6b\x2e\x7b\x26\xef\x1e\xe8\x8e\x8c\x8e\xbe\x5c\xdf\
+\xb6\xfe\x03\x97\xdc\x22\xb2\x3b\x75\x96\x67\x11\xbc\x97\x89\xa9\
+\x2d\xed\xae\xb6\x1a\x33\x68\x8e\xa4\x1a\xbf\xed\x27\x57\xbe\xff\
+\x9b\x23\xed\x76\x63\xa0\xd7\x67\xd2\xc8\x3c\x46\xbd\x97\x96\x07\
+\x22\xf0\xce\x07\x39\x4a\xa3\x0d\x2a\xc8\x55\x6c\xd6\x29\x0e\xd5\
+\x23\x2f\x00\x9c\x7e\xee\x15\x77\xac\x39\xe3\x92\xdd\xb1\xa5\xaf\
+\x8d\xa0\x3b\xd3\xb4\x6e\xe2\x44\xc4\x9c\x4f\x33\xa7\x9a\x64\x16\
+\x75\xee\xdb\xb7\xe2\x5b\xe7\x4c\xbd\xfe\x95\x46\xdc\x70\x75\x39\
+\xd1\x88\x90\x20\xe2\xc8\x1a\x59\x1c\x92\x24\xc9\xbe\xba\xed\xc7\
+\x7b\x04\x79\x27\x46\x9a\xc4\x96\x65\x02\x58\xb4\x64\xe5\x1a\x15\
+\x31\x81\xa8\x18\x14\x7f\x7d\xfc\xa9\x74\x74\x7c\xe3\xc3\x6b\x97\
+\x4e\x9f\x1a\xc7\x71\x44\xd3\x91\xc5\xe6\xf4\x8f\x7e\xe9\x8e\x9b\
+\x7f\xf5\xc8\x2f\x6f\xbd\x7b\xd7\x8b\xb1\xe5\x87\x9a\xc8\x92\x18\
+\x69\x06\x33\x2f\x13\x53\x5b\x62\x00\x9a\x8b\x73\x99\x38\xd7\x41\
+\xa3\x9d\xaa\x5b\x62\x88\x96\xec\xf8\xec\x49\x5f\x5b\x39\x3e\xbe\
+\x44\x79\xaf\x99\x2c\x02\xde\x0c\xab\x37\x5e\xb4\xf3\xcc\xcf\x5c\
+\xbb\x47\x2c\x3f\x18\x23\x3f\xd8\xb6\x6e\x27\xb6\x2c\x53\xcb\x32\
+\x00\x5e\x26\x36\x9c\xec\x60\xa6\x00\xd4\x8b\x8b\x52\x89\xe2\x0e\
+\x1a\xcd\x5c\xdc\x48\x2e\x51\x1b\x86\xf6\x75\x9f\xff\xe4\xaa\x55\
+\xe9\x5f\x2e\xbb\xe0\x53\xd7\xdd\xfb\xe0\xae\x9b\x3e\x46\xd3\x92\
+\xa3\xc5\xc1\x99\x99\x64\xcd\x7b\x2e\xfe\xc5\x03\x0f\x3f\xf2\xfa\
+\xd7\x7f\xf0\xb3\x17\x21\xd6\x89\x7c\xd6\x89\xe0\x67\xda\x98\xed\
+\x3a\x9f\xa7\x91\xf8\x1c\x66\x1e\x80\x97\xc9\x0d\x27\x47\x06\xa8\
+\x00\x62\x66\xa5\x13\x3b\x97\x41\x1a\x99\xc4\x8d\x0c\x51\xd3\x8b\
+\x34\x0c\xda\x80\x21\x86\x48\x04\xb3\xa8\x5c\xa0\x15\x80\xf5\x9f\
+\xcb\xaf\x22\x75\xf1\xbc\x47\x99\x86\xe2\x46\x60\xf5\x09\xab\xe5\
+\x1b\xfa\xdd\x4b\xab\xa5\x57\x65\x49\x6d\x5b\xbd\xcc\xfa\x7e\xc3\
+\xff\x51\xfc\xbf\xf9\x31\x5c\xd7\xc3\xec\x3b\x8c\x2c\x50\xce\x61\
+\xbb\x2c\xf3\x94\x61\x43\xe5\xd9\x50\x61\x87\xab\x33\xde\xe0\x36\
+\x1b\xb6\x79\xcd\xc6\x0b\xb5\xc3\x7c\x75\x96\x5a\x59\xc3\x65\x1e\
+\xa9\x9c\x5e\xdd\xc4\x6a\x75\xcc\x61\x96\x43\x90\x8a\xe5\x89\x02\
+\x49\x64\x79\xb7\x81\x2c\x89\xe0\x13\xe7\xd3\x2c\x42\x9e\x8b\x88\
+\x07\x60\x66\xe6\x9d\x01\x26\x80\xb7\x62\x5e\x84\x57\xe4\x68\x5a\
+\x6e\x0e\xea\x33\xf8\x34\x83\x26\x99\x45\xce\x24\x72\x06\x44\x30\
+\x89\x00\xa8\x59\xb5\xa0\x44\x61\x75\x11\x81\xf9\xbe\x1f\xf7\x26\
+\x57\xd8\xe0\xb9\x0e\x7e\x2f\xf6\x17\xab\xce\x65\xc8\xa6\x65\x9e\
+\xaa\xec\x5e\xb9\x95\x2d\x7c\xbf\x71\xcc\xaa\x72\x6d\xc0\xeb\x44\
+\x00\xf3\x55\x19\xe5\x67\xb5\xbf\xd5\x8e\x35\xec\x37\x56\x6b\x9f\
+\x5a\xb9\xc5\x6e\x83\xe7\x8d\xb2\x4e\x3d\xdf\xab\x39\x5f\x6f\xbf\
+\x7a\xda\xbc\xde\x55\xb4\x44\x71\x8e\x06\x29\xcd\x2b\x43\x4e\x5b\
+\xd9\x01\x03\x36\x2c\x6b\x36\x5f\x1d\x50\x3f\xcf\xfe\xd5\x31\xc7\
+\xf7\x06\xfc\x7b\xb0\xce\x03\xb6\xaf\xd5\xb9\xb7\x4f\xd9\x0e\x73\
+\xeb\x76\xb8\x7a\x94\xf6\xab\xea\x6d\x30\x11\x78\x33\x9f\xab\x21\
+\x57\x58\xe6\x90\x67\x11\xf2\xd4\xf9\x2c\x77\xe2\x73\x08\xbc\x41\
+\xbc\x15\x05\x78\x11\xb1\xff\x01\xed\xeb\x89\x21\x3a\xe3\x55\x01\
+\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
+\x00\x00\x20\xb1\
+\x89\
+\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
+\x00\x00\xe7\x00\x00\x00\x82\x08\x06\x00\x00\x00\xbd\xcd\xdf\xc9\
+\x00\x00\x00\x01\x73\x52\x47\x42\x00\xae\xce\x1c\xe9\x00\x00\x00\
+\x06\x62\x4b\x47\x44\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\
+\x00\x00\x09\x70\x48\x59\x73\x00\x00\xdd\x75\x00\x00\xdd\x75\x01\
+\xac\x87\xc3\x83\x00\x00\x00\x07\x74\x49\x4d\x45\x07\xdc\x02\x18\
+\x12\x22\x32\x36\x82\x3a\x5a\x00\x00\x20\x00\x49\x44\x41\x54\x78\
+\xda\xed\x5d\x69\x73\xdb\xd8\xb1\x3d\x20\x01\x02\x04\xc1\x55\x8b\
+\x25\x79\xec\xf1\x54\x3e\xa4\xf2\xff\x7f\x4a\xaa\x52\xa9\x54\xc5\
+\x9b\x6c\xd9\x96\x44\x71\x01\xb1\x72\x7b\x1f\x26\xe7\xa6\x79\x05\
+\x90\xd4\x1b\xcf\x8c\x63\x77\x57\xb9\xb4\x70\x15\x8d\x73\xbb\xfb\
+\xf4\xe9\x6e\x67\xbb\xdd\x6e\xa1\xa6\xa6\xf6\xcd\x59\x43\x3f\x02\
+\x35\x35\x05\xa7\x9a\x9a\x9a\x82\x53\x4d\x4d\xc1\xa9\xa6\xa6\xa6\
+\xe0\x54\x53\x53\x70\xaa\xa9\xa9\x29\x38\xd5\xd4\xd4\x14\x9c\x6a\
+\x6a\x0a\x4e\x35\x35\x35\x05\xa7\x9a\x9a\x82\x53\x4d\x4d\x4d\xc1\
+\xa9\xa6\xa6\xa6\xe0\x54\x53\x53\x70\xaa\xa9\xa9\x29\x38\xd5\xd4\
+\x14\x9c\x6a\x6a\x6a\x0a\x4e\x35\xb5\x1f\xd1\x5c\xfd\x08\xbe\x5d\
+\xdb\x6e\xb7\xd8\x6e\xb7\x58\xaf\xd7\x58\xaf\xd7\x28\xcb\x12\x49\
+\x92\x60\xbb\xdd\x62\x38\x1c\xa2\xdd\x6e\x1f\xfd\x5c\x59\x96\xe1\
+\xe1\xe1\x01\xeb\xf5\x1a\xbe\xef\xc3\xf3\x3c\xb4\x5a\x2d\xb4\x5a\
+\x2d\x34\x9b\x4d\x38\x8e\x03\xc7\x71\x00\xc0\x7c\x55\x53\x70\xaa\
+\x09\x30\x6e\x36\x1b\xac\xd7\x6b\x14\x45\x81\x2c\xcb\xcc\xbf\xa2\
+\x28\xcc\x6d\x41\x10\xa0\xd3\xe9\x3c\x09\x9c\xab\xd5\x0a\xf3\xf9\
+\x1c\x59\x96\xa1\xd1\x68\xc0\x71\x1c\x34\x1a\x0d\xb4\x5a\x2d\xf8\
+\xbe\x8f\x20\x08\xd0\x6e\xb7\xd1\x6a\xb5\xe0\x79\x9e\x01\xac\x9a\
+\x82\xf3\x87\x07\xe5\x7a\xbd\x46\x96\x65\x58\x2c\x16\x48\xd3\x14\
+\x79\x9e\x63\xb5\x5a\xc1\x71\x1c\xb8\xae\x8b\x76\xbb\x8d\x46\xe3\
+\xd7\x2c\xa4\xd9\x6c\x9a\xef\x8f\xce\x5f\x1a\x0d\xb4\xdb\x6d\xb8\
+\xae\x8b\xcd\x66\x63\x0e\x82\x34\x4d\x71\x7f\x7f\x8f\xe5\x72\x89\
+\x66\xb3\x89\x30\x0c\xd1\xeb\xf5\x10\x45\x11\xa2\x28\x42\x18\x86\
+\x68\x36\x9b\xfa\x9f\xf4\x27\x98\xa3\x03\xbe\xfe\x5c\x2b\x8a\x02\
+\x0f\x0f\x0f\xb8\xbf\xbf\x47\x9e\xe7\x70\x1c\x07\xbe\xef\x23\x0c\
+\x43\xb4\x5a\x2d\x03\xc2\xcd\x66\x83\xd5\x6a\x85\xd5\x6a\x85\xcd\
+\x66\x83\xf3\xf3\x73\x0c\x87\xc3\xa3\x5f\x67\x3a\x9d\xe2\xf3\xe7\
+\xcf\xd8\x6e\xb7\x68\x34\x1a\x3b\x00\xdf\x6e\xb7\x58\x2e\x97\x28\
+\xcb\x12\x45\x51\x18\x2f\xcd\x43\x61\x34\x1a\xe1\xf4\xf4\x14\x61\
+\x18\xaa\x37\x55\xcf\xf9\xfd\x5b\x9e\xe7\xb8\xb9\xb9\xc1\x97\x2f\
+\x5f\xd0\x68\x34\x10\x86\x21\x4e\x4f\x4f\xd1\xe9\x74\xcc\xed\x59\
+\x96\xa1\x2c\x4b\xac\xd7\x6b\xe3\xbd\xca\xb2\xc4\x6a\xb5\x42\xbf\
+\xdf\x7f\xf2\x21\x10\xc7\x31\x5c\xd7\x85\xe3\x38\x28\x8a\x02\x65\
+\x59\x62\xbb\xdd\xc2\x71\x1c\x13\xc6\x36\x1a\x0d\x74\xbb\x5d\x00\
+\xc0\x72\xb9\xc4\x6a\xb5\xc2\x87\x0f\x1f\xf0\xf1\xe3\x47\x8c\x46\
+\x23\x5c\x5d\x5d\x21\x8a\xa2\x27\x7b\x6e\x35\x05\xe7\x37\x1f\xbe\
+\x16\x45\x81\xbb\xbb\x3b\x7c\xfe\xfc\x19\x8e\xe3\x60\x34\x1a\xed\
+\xe4\x8f\x79\x9e\x63\x36\x9b\x61\xb9\x5c\x62\xb3\xd9\x60\xb3\xd9\
+\x98\xc7\x93\x14\xa2\xf7\x7c\x8a\xf1\xb9\x56\xab\x15\xca\xb2\xc4\
+\x72\xb9\x7c\x14\x56\x3b\x8e\x83\xed\x76\x8b\x3c\xcf\xe1\x79\x9e\
+\x01\x6d\x10\x04\x28\x8a\x02\x5f\xbe\x7c\xc1\x74\x3a\xc5\xd9\xd9\
+\x19\x9e\x3d\x7b\x86\x30\x0c\x15\xa4\x0a\xce\xef\x23\x7c\x9d\x4c\
+\x26\x18\x8f\xc7\xc8\xf3\x1c\x41\x10\xc0\x75\x5d\xc3\x98\x12\x34\
+\x79\x9e\xef\x00\x8f\x80\x59\x2e\x97\x58\xaf\xd7\x60\x16\xf2\xd4\
+\x6c\x64\xbb\xdd\xa2\x2c\x4b\x38\x8e\x83\xf5\x7a\x8d\x46\xa3\x61\
+\xd8\x60\x19\xde\x92\xb5\x2d\xcb\x12\x00\x0c\x39\xd4\xe9\x74\xb0\
+\x5c\x2e\xb1\x58\x2c\xf0\xf6\xed\x5b\xcc\xe7\x73\x3c\x7b\xf6\x0c\
+\xc3\xe1\x10\xbe\xef\x2b\x48\x15\x9c\xff\x7b\xb6\xd9\x6c\x30\x9b\
+\xcd\x70\x77\x77\x87\x24\x49\xe0\x38\x8e\x21\x66\x58\x22\x99\xcf\
+\xe7\x28\x8a\xc2\x00\x6e\xbd\x5e\x1b\xd2\x46\x32\xb8\x04\x4f\x51\
+\x14\xc8\xf3\xfc\x49\xef\x63\xb9\x5c\x62\xb9\x5c\x1a\x8f\x28\x89\
+\xa2\x2a\xb0\x37\x1a\x0d\xe3\x69\xd3\x34\x35\xac\xee\x70\x38\x44\
+\x9e\xe7\x78\x78\x78\xc0\x74\x3a\xc5\xf9\xf9\x39\x2e\x2f\x2f\xd1\
+\xef\xf7\x95\x38\x52\x70\xfe\xef\x84\xb0\xeb\xf5\x1a\xe3\xf1\x18\
+\x9f\x3f\x7f\x46\x59\x96\x08\xc3\x10\x9e\xe7\x19\x00\x7a\x9e\x87\
+\xcd\x66\x63\x88\x9f\x2c\xcb\x4c\x38\x4b\x50\x12\xa0\x12\x64\x49\
+\x92\x3c\x19\x9c\x69\x9a\x22\x4d\x53\xf4\xfb\x7d\xe3\x8d\xe5\x57\
+\x59\xe7\xb4\xbd\xf6\x7a\xbd\x46\x9e\xe7\xa6\x8c\xe3\xfb\x3e\x5c\
+\xd7\x45\x51\x14\xf8\xf4\xe9\x13\xee\xef\xef\xf1\xea\xd5\x2b\x3c\
+\x7f\xfe\x5c\x4b\x30\x0a\xce\x6f\xdf\x96\xcb\x25\x6e\x6f\x6f\x71\
+\x7d\x7d\x8d\x56\xab\x85\xd1\x68\x64\x2e\xf4\x66\xb3\x89\x66\xb3\
+\x69\xbc\x91\xeb\xba\x3b\x80\x23\x28\x09\x72\x29\x46\xe0\x7d\x9a\
+\xcd\x26\x36\x9b\xcd\x51\xe1\xe4\x76\xbb\x35\xc2\x03\x86\xd4\xbe\
+\xef\x63\xbb\xdd\x62\xb5\x5a\x3d\x7a\x2d\x09\x56\x19\xfa\x36\x1a\
+\x8d\x1d\x0f\x1e\x04\x01\x5a\xad\x16\x92\x24\xc1\x3f\xfe\xf1\x0f\
+\x4c\xa7\x53\xfc\xf5\xaf\x7f\x85\xef\xfb\x0a\x50\x05\xe7\xb7\xe9\
+\x31\xc9\xc4\xde\xde\xde\x22\x8a\x22\x74\x3a\x1d\x73\x51\xb7\xdb\
+\xed\x9d\x9a\xa5\xcc\x29\xcb\xb2\xc4\x66\xb3\xd9\x01\x85\xe3\x38\
+\x06\xac\x0c\x77\xa3\x28\x32\xe0\x3a\xc6\x48\xf6\xf8\xbe\x8f\x66\
+\xb3\x69\xc2\x6a\xe6\x96\x0c\xa9\x99\x87\x12\x58\xcc\x7d\x9b\xcd\
+\xa6\x01\x37\x19\x5c\xcf\xf3\xcc\x7d\xa3\x28\x82\xeb\xba\xb8\xb9\
+\xb9\x41\x1c\xc7\xf8\xdb\xdf\xfe\x86\xd1\x68\xa4\x00\xfd\x8d\xa6\
+\x59\xfc\x57\xb6\x34\x4d\xf1\xf6\xed\x5b\xdc\xdf\xdf\x63\x30\x18\
+\x20\x0c\x43\x03\xb8\x76\xbb\x6d\x88\x20\xe9\xf1\xd6\xeb\xb5\x21\
+\x7c\x28\xaf\x0b\x82\xc0\x28\x79\x9a\xcd\x26\xd6\xeb\x35\x92\x24\
+\x41\xb3\xd9\x84\xeb\xba\xc8\xf3\x7c\x87\x71\xdd\x67\xab\xd5\x0a\
+\x45\x51\x20\x08\x02\xf4\xfb\x7d\x03\xac\xed\x76\x6b\x08\x1f\x82\
+\x8e\xaf\x49\xb0\x4a\x2f\xca\xfa\x28\x81\x4d\xf0\x6d\xb7\x5b\x04\
+\x41\x80\xd1\x68\x84\x3c\xcf\xf1\xf7\xbf\xff\x1d\x37\x37\x37\xd0\
+\x12\xba\x82\xf3\x9b\xb1\xd9\x6c\x86\x7f\xff\xfb\xdf\x48\xd3\x14\
+\xa3\xd1\xc8\xb0\xb0\xeb\xf5\xda\x80\x90\x3f\x4b\xaf\xe8\x38\x0e\
+\xba\xdd\x2e\x3a\x9d\x8e\xd1\xbc\xd2\xbb\xd1\x83\x6d\x36\x1b\x03\
+\x2a\x2a\x88\xf8\xbc\xc7\x90\x52\x7c\x1d\x3b\x9f\x24\x40\xb7\xdb\
+\x2d\xe2\x38\x36\x61\xb3\xf4\xa2\x9b\xcd\xc6\x94\x70\xaa\x08\x24\
+\x3e\x97\xeb\xba\xe8\x76\xbb\xd8\x6c\x36\xf8\xe7\x3f\xff\x89\x37\
+\x6f\xde\x3c\xb9\xe4\xa3\xa6\xe0\xfc\xea\x36\x99\x4c\xf0\xee\xdd\
+\x3b\x6c\xb7\x5b\x0c\x06\x03\x34\x1a\x0d\x93\xcf\xb1\x8c\x41\x56\
+\x56\x7a\x27\xfe\x6b\xb5\x5a\x88\xa2\x68\x87\x24\xf2\x7d\x7f\x87\
+\xa9\x6d\xb5\x5a\x46\x7e\x47\x4f\x7b\x6c\xa8\xcd\xd7\x5f\x2c\x16\
+\x06\x7c\xf4\x7e\xae\xeb\xa2\xd3\xe9\x18\x8f\xcc\x30\x98\xef\x91\
+\x00\x97\x64\x95\xfc\xdb\xf8\x7c\x00\xe0\xba\x2e\xa2\x28\x82\xe3\
+\x38\x78\xfd\xfa\x35\xde\xbc\x79\x73\xf4\xfb\x54\xd3\x9c\xf3\xab\
+\xdb\x74\x3a\xc5\xbb\x77\xef\xb0\x5e\xaf\xd1\xeb\xf5\x4c\x7e\x28\
+\x3d\x0c\xc9\x1b\x7a\x24\xde\x26\x43\x43\xc7\x71\x8c\x17\x93\x1e\
+\x16\x80\xf1\xa8\x0c\x89\xa9\xf0\x39\x96\x9c\x4a\xd3\x14\x49\x92\
+\x18\x35\x92\x04\x1e\x43\x58\x2a\x83\xe4\x7b\x94\x9e\x52\x8a\x1f\
+\xf8\xf7\xf1\x3d\xf3\x7d\x33\x47\x8d\xa2\x08\x71\x1c\xe3\xfd\xfb\
+\xf7\x68\xb5\x5a\xb8\xba\xba\xd2\x52\x8b\x82\xf3\x8f\xb5\xf9\x7c\
+\x8e\xb7\x6f\xdf\x62\xb5\x5a\x61\x30\x18\xec\x5c\xb8\xf4\x7a\xfc\
+\xde\xd6\xb3\xda\xde\xd3\x75\x5d\x84\x61\x88\x38\x8e\xf1\xf0\xf0\
+\x60\x84\xef\x00\x76\x44\x01\xfd\x7e\x7f\x87\xd1\x3d\x26\xe7\xa4\
+\xf0\x20\xcf\x73\x53\x52\x91\x22\x78\xd7\x75\x4d\x79\xa4\xea\x60\
+\xa1\xa7\xa4\xe6\xd6\x75\x5d\xf3\x3b\x7a\x5f\x59\x9a\xa1\x70\x61\
+\xb1\x58\xe0\xf5\xeb\xd7\x68\x34\x1a\xb8\xbc\xbc\x54\xb1\x82\x82\
+\xf3\x8f\xb1\x2c\xcb\x70\x73\x73\x83\xb2\x2c\xd1\xeb\xf5\xd0\x6c\
+\x36\xb1\x5a\xad\x0c\x40\x79\xe1\xcb\x3e\x49\xd6\x01\x25\x28\x65\
+\xf8\xe9\xba\x2e\x86\xc3\x21\xc2\x30\x44\x9a\xa6\x86\x4d\x65\x98\
+\x9c\x24\x09\xba\xdd\x2e\x7a\xbd\x1e\x82\x20\x38\xea\x7d\xb2\xec\
+\x61\x83\xba\xd5\x6a\xed\x90\x3d\x0c\x5d\xed\x5c\x95\x2d\x6c\x14\
+\x3f\xac\x56\x2b\x43\x0c\x51\xd9\xd4\x6c\x36\x2b\x05\x0e\x61\x18\
+\x22\x49\x12\xbc\x7e\xfd\x1a\x9e\xe7\xe1\xec\xec\x4c\x59\x5c\x05\
+\xe7\xef\x6b\xab\xd5\x0a\xb7\xb7\xb7\x98\x4c\x26\xe8\x76\xbb\xf0\
+\x7d\xdf\x84\x7d\x36\x30\xf9\xbd\xef\xfb\x06\x10\xb6\x0c\x4f\xaa\
+\x81\xb6\xdb\xad\x09\x0d\x09\x8c\xb2\x2c\x77\x44\x0a\x4f\xc9\x39\
+\xe9\xe9\xf8\x18\xcf\xf3\x0c\xfb\xcb\xf7\xca\x3a\x2b\xe5\x84\x41\
+\x10\x60\xb3\xd9\x20\x8e\x63\x03\x54\x09\x3a\x5b\xf3\x9b\x65\x99\
+\xa9\xdf\xf2\x36\x82\x3e\x08\x02\xc3\x62\xb7\xdb\x6d\x13\x3e\xab\
+\x29\x38\xbf\xba\x6d\x36\x1b\x3c\x3c\x3c\xe0\xe6\xe6\xc6\x5c\xc8\
+\x04\x9c\x24\x4a\x08\xcc\xf5\x7a\x6d\x42\x56\x19\x4a\xd6\x85\xa5\
+\x92\x21\x65\xfb\x56\x92\x24\x28\xcb\x12\xdd\x6e\x17\x41\x10\xa0\
+\x2c\xcb\xa3\x73\xce\x34\x4d\xf1\xf0\xf0\x60\x42\x56\xe6\x87\x0c\
+\x31\xd3\x34\xc5\x62\xb1\x30\x87\x03\x35\xb4\xfc\x7b\x64\xa8\xee\
+\x79\x1e\x5c\xd7\x7d\x54\x6a\xa1\x40\x5e\x8a\xf5\x29\xf4\xa7\xe7\
+\x9e\x4c\x26\xb8\xbe\xbe\xc6\x5f\xfe\xf2\x17\xf8\xbe\xaf\x17\x92\
+\x82\xf3\xeb\x5b\x9a\xa6\x78\xff\xfe\x3d\x1c\xc7\x31\xcd\xc8\xac\
+\x13\x4a\xd0\x49\x2f\x4a\x50\xc9\xdb\xa5\x07\x95\x79\xa8\xe3\x38\
+\x58\xad\x56\x46\x93\x2b\x0f\x85\xb2\x2c\x4d\x17\xcb\xb1\xe1\x21\
+\x4b\x38\x9e\xe7\xa1\xdb\xed\x1a\xd0\x50\x1c\x21\xdb\xd2\x78\x5f\
+\x19\x66\x4b\xf1\x01\xa3\x03\xfe\x6d\x9c\xa6\xe0\x79\x1e\x92\x24\
+\x41\x14\x45\xa6\x8b\x25\x4d\x53\x2c\x97\x4b\x14\x45\x81\x5e\xaf\
+\x07\xcf\xf3\xf0\xf1\xe3\x47\x74\xbb\x5d\x3c\x7f\xfe\x5c\xf3\xcf\
+\x03\xa6\x9f\xce\x13\x6d\xb9\x5c\xe2\xe6\xe6\xc6\x5c\xdc\x64\x36\
+\x29\x20\x90\x0a\x1f\xfe\x4c\xaf\xc9\x90\xcf\x0e\x69\xe5\xc8\x90\
+\x20\x08\x90\xe7\x39\xfe\xf5\xaf\x7f\xe1\xcd\x9b\x37\x26\xcf\x5b\
+\x2e\x97\x26\xd7\x2c\x8a\x02\x37\x37\x37\xa6\x2c\x72\x4c\x08\x4e\
+\xf5\x11\x5f\x87\x5e\xd2\x66\x67\x65\x7e\x2c\xa3\x02\x19\xc6\xb2\
+\x09\x5b\x32\xcb\x3c\x38\x66\xb3\x19\x92\x24\x81\xeb\xba\xe8\xf5\
+\x7a\xf0\x7d\xdf\xc8\x0e\x59\xf7\x7d\xf3\xe6\x8d\xf1\xcc\x6a\x0a\
+\xce\xaf\x62\xdb\xed\x16\xf3\xf9\x1c\xb7\xb7\xb7\x46\x57\x4a\x00\
+\x4a\xcf\x47\xa0\xd2\x4b\xb2\x86\x28\x73\x44\x82\x98\x0a\x1d\xea\
+\x56\x39\x19\xa1\x2c\x4b\xa3\xbb\x75\x5d\x17\x41\x10\xa0\xdb\xed\
+\x9a\xfe\x4f\x32\xa8\x4f\xfa\xcf\xfe\x4f\x29\x87\xa4\x54\x51\x14\
+\x26\x0f\x95\xa2\x03\x82\xaf\xdf\xef\x1b\x41\xbe\x24\xb6\x78\xff\
+\x20\x08\x8c\x92\x49\x7e\x06\x04\x6a\x59\x96\x08\x82\xc0\x4c\x6c\
+\xa0\x90\x62\x36\x9b\xe1\xcd\x9b\x37\x47\x2b\x9c\x14\x9c\x6a\x07\
+\xad\x2c\x4b\x5c\x5f\x5f\x9b\x0b\x53\x12\x38\x04\x9b\x14\x0d\x10\
+\x8c\x24\x81\x24\xf1\xc3\x9a\x25\xa5\x7b\x0c\x1b\x99\xbf\x11\xb0\
+\x0c\x73\x49\x26\x91\x01\x3d\x3b\x3b\x43\x18\x86\x47\xbd\xef\x30\
+\x0c\x4d\x37\x8a\x04\x27\x23\x01\xa9\xbb\xa5\xd7\x24\x69\x94\x65\
+\x99\x51\xfe\x30\x4f\x94\xe1\x38\xb5\xc2\x54\x0f\xc9\x88\x81\xe1\
+\x32\xfb\x56\xe5\x81\xf4\xe9\xd3\x27\x8c\xc7\x63\xf5\x9e\x0a\xce\
+\xaf\xe3\x35\xd9\xc3\x48\x90\xf0\x82\x24\x31\x23\x81\x29\x45\xe3\
+\xb2\xb6\xc9\x6e\x14\xd9\x40\x2d\x43\xc6\xe5\x72\x89\xd3\xd3\x53\
+\x5c\x5d\x5d\x99\x03\x40\x76\x8e\xf0\x62\xef\xf5\x7a\xe6\x79\x0e\
+\x19\x5f\x93\xe1\xad\xec\x32\xe1\x24\x3f\xe6\x9b\xb4\x3c\xcf\x91\
+\xe7\x39\xc2\x30\x34\x79\x2a\x95\x43\x92\x8d\x8d\xe3\x78\x27\x64\
+\x96\xde\x33\x49\x12\xdc\xdd\xdd\x21\x8e\xe3\x47\xe4\xd0\x76\xbb\
+\xc5\xfb\xf7\xef\x51\x14\x85\x5e\x5c\x0a\xce\xdf\x66\x45\x51\xe0\
+\xc3\x87\x0f\xf0\x3c\x6f\x87\x95\x94\xa1\xac\xac\x5d\x52\x82\x77\
+\x76\x76\x66\x66\xee\x10\xa8\x12\xc4\x36\x19\x44\x90\x0f\x87\x43\
+\x5c\x5d\x5d\x21\x0c\x43\x23\x84\x97\x9e\x4d\xd6\x4b\x0f\x31\xcb\
+\xf4\x9e\xbe\xef\x9b\xfc\x95\xa0\xa7\x28\x9e\xb5\x4b\xe9\xa9\xe9\
+\x2d\xa9\x30\x92\xa3\x4c\x38\xb2\x93\x7f\x0b\xa3\x03\xcf\xf3\xcc\
+\x81\x25\x6b\xa1\x2c\xd5\x70\x34\x27\xd9\xdb\xbb\xbb\x3b\xf5\x9e\
+\x0a\xce\xdf\xe6\x35\xef\xef\xef\x91\xa6\xa9\x01\x89\x04\x98\x0c\
+\xe5\x64\x3e\x49\x31\xbb\x94\xad\xc9\x7c\xd4\x26\x85\x64\xe8\xcb\
+\x51\x96\x14\xc1\x77\xbb\x5d\x23\x84\x97\xcf\x75\x48\x58\x4e\x10\
+\xb2\x55\xcc\x71\x1c\x23\xe5\xe3\x57\x29\xa2\x67\xbe\xc9\x99\x46\
+\x59\x96\x21\x4d\xd3\x1d\x26\x99\xe1\xb6\x7c\x6d\x86\xb1\xcc\x85\
+\x93\x24\xc1\x6c\x36\x33\xef\x81\x07\x42\x9e\xe7\x88\xe3\xd8\x8c\
+\x36\xb9\xbe\xbe\x3e\xba\x24\xf4\xa3\x99\x96\x52\x8e\xb0\x3c\xcf\
+\xf1\xf9\xf3\x67\xa3\x6f\xb5\x07\x6c\xd1\x8b\x4a\x52\x64\x34\x1a\
+\x61\x34\x1a\x19\x21\x3b\xc1\x54\xe5\x25\xe8\x09\x49\xda\xc8\xd0\
+\xb8\x6e\x1a\x7b\x95\xf7\xad\xf3\x9c\x64\x8c\x7b\xbd\x1e\x80\x5f\
+\xbb\x67\x38\x86\xd3\xee\x54\xf1\x7d\xdf\x10\x4e\x9c\x9e\x50\x35\
+\x35\x41\xb2\xbc\x14\x34\x78\x9e\x87\x30\x0c\x0d\x08\xe5\xdf\x21\
+\x3b\x70\x28\xa8\x68\xb7\xdb\x88\xe3\x18\x37\x37\x37\xf8\xe5\x97\
+\x5f\xf4\x42\x53\x70\x3e\xdd\xc6\xe3\x31\xca\xb2\x34\xa1\x9b\x0c\
+\x69\x19\xea\x11\x94\x65\x59\x62\x30\x18\x98\x96\x31\x59\x13\xb4\
+\xc3\x58\xe6\x83\xf2\xf1\xbc\x2f\x19\xd1\xe1\x70\x68\x84\x03\xd2\
+\xc3\xca\x36\x2e\xde\x56\x65\x92\x70\xa2\xba\x88\xb9\x21\x1f\x27\
+\x5b\xca\x08\x2c\xca\xf5\xaa\x6a\xa9\xf6\xc4\x04\x7b\x6d\x04\x19\
+\x68\x5b\x90\x21\x5b\xcb\xf8\xfb\x66\xb3\x89\xf1\x78\x6c\xa6\xf9\
+\xa9\x29\x38\x8f\xb6\xe5\x72\x89\xe9\x74\x6a\x88\x18\x3b\x94\x24\
+\x88\x80\x5f\xeb\x89\x6c\xbf\xe2\xa4\x02\x09\x4a\x29\xe5\x93\xe1\
+\x21\x2f\xd4\xd5\x6a\x65\xc2\x3f\xe6\xa6\x1c\xf9\xc1\xe7\x61\x3e\
+\x37\x9f\xcf\xe1\xfb\x3e\x46\xa3\xd1\xde\xb5\x0c\x14\x33\x8c\xc7\
+\x63\xa3\xee\x61\x1e\xc9\x32\x8d\x24\x7a\x28\x1e\xa8\x9a\x29\x24\
+\x73\x63\x7b\x5a\x03\x6b\xa2\x2c\x2f\xc9\x83\x8b\x1e\x96\x07\x03\
+\xef\xc7\xcf\x22\x4d\x53\xcc\x66\x33\x05\xa7\x82\xf3\x69\x96\x24\
+\x89\x21\x31\xe8\x65\x24\x73\xca\x8b\x8f\x17\x64\xa7\xd3\x31\xd3\
+\x0f\xe4\x7d\x6d\x3d\x2a\x81\x49\xd0\xda\x22\x00\xf9\x7b\x1b\xdc\
+\x00\x0c\x29\x75\xa8\x56\x58\x96\x25\xe2\x38\x36\x93\x10\x28\x9c\
+\x60\x68\x2b\x47\x64\x4a\xb0\xc9\xf7\x60\x13\x56\x55\xef\x93\x4a\
+\x22\x19\xf2\xb2\x24\x44\xdd\x71\x9e\xe7\x8f\xfe\x1e\x86\xb9\x8b\
+\xc5\xc2\x4c\x07\x54\x53\x70\x1e\x45\x04\x71\x77\x89\x14\xac\xdb\
+\x3d\x9a\xf4\x9e\xdd\x6e\x77\xc7\x23\x54\x99\x3d\x3c\xcb\x26\x77\
+\xea\xc0\x20\x5f\x97\x61\xa3\x1c\xa7\x79\x88\xad\x6d\x34\x1a\x46\
+\x9f\x6b\xdf\x6e\x77\x92\x54\x81\x54\xbe\x77\x5b\x7e\xc8\x70\x9e\
+\x25\xa6\x46\xa3\x61\xc0\x4a\x52\x8b\xf5\x54\x3b\xff\x64\x9b\x1a\
+\x0f\x10\x05\xa7\x82\xf3\xe8\x90\x76\xb1\x58\xec\x80\x50\x5e\xd4\
+\x0c\xdd\x48\x84\x64\x59\x86\x24\x49\x8c\xb2\xa6\xae\x6b\x84\x17\
+\xb5\x14\x30\xd8\xed\x65\x36\xf9\x23\xe7\xfa\xb0\xb4\x41\xcf\x74\
+\xc8\xa4\xe0\xa0\x2c\xcb\x47\x5e\x5b\x02\xb3\xea\x71\xf6\xfb\x96\
+\xdf\xcb\xbf\xc5\xce\x7f\xa9\xb9\x6d\x34\x1a\x26\xa4\xb6\x0f\x20\
+\x3e\x36\x8e\x63\x24\x49\x82\x4e\xa7\xa3\x2d\x65\x0a\xce\xe3\x58\
+\xda\xd9\x6c\x56\x3b\x99\x8e\xbf\x6f\xb5\x5a\x28\x8a\x02\x8b\xc5\
+\xa2\x76\x2c\xa4\xad\xa9\xb5\xc3\x5e\x09\x06\xa9\x0e\xe2\x01\x40\
+\xc1\x03\x27\xc7\x13\x9c\x27\x27\x27\x7b\x17\x1a\xb1\x96\x29\x01\
+\x59\xf5\x9a\x87\xc0\x68\x13\x41\x55\x51\x86\x0c\x93\x57\xab\xd5\
+\xce\x24\x78\x99\xc3\x32\xd2\x90\x51\x04\xeb\xa6\x75\xcf\xff\x23\
+\x9a\xd6\x39\xf7\x84\xb4\x24\x47\xd8\x60\x6c\x83\x8a\x53\xe7\x36\
+\x9b\x8d\x59\xa3\xb0\x5c\x2e\x1f\xad\x54\xa8\x63\x3b\xe9\xad\x6c\
+\xe0\xd0\xbb\x30\x9f\xe5\x18\x10\xb2\x9c\x52\x08\x70\x28\xe7\xa4\
+\xc8\xc0\x5e\xf1\x50\xf5\x4f\x1e\x16\xd2\xb3\xca\xef\xab\x56\x37\
+\xc8\x03\x2b\x8e\x63\x64\x59\x86\x4e\xa7\xf3\xa8\x77\xd5\xce\xb7\
+\x59\x42\xf2\x3c\xcf\xec\x0f\x55\xbd\xad\x82\xf3\xa0\xb1\xce\xc7\
+\x10\x72\x36\x9b\x99\xbc\x90\xe4\x4f\x14\x45\x3b\x3d\x97\xc7\xd4\
+\x1e\xed\x3e\x4e\xc9\xda\x56\x79\x2f\xbe\x17\x86\xd8\xcc\xed\x8e\
+\x95\xef\xc9\xfa\xab\x3c\x54\xec\x49\x0c\x12\x78\x75\x1e\xb6\xee\
+\xbe\x76\x6e\x2c\x95\x42\x55\x73\x92\x6c\x80\x93\xe1\x1d\x8f\xc7\
+\x0a\x4e\x05\xe7\x61\xa3\xe8\x9b\x5a\xd4\x24\x49\x90\x24\x89\x99\
+\xc7\xc3\x56\x28\x76\x76\xc8\x30\xd4\xf6\x40\xc7\x90\x43\x12\x9c\
+\x9c\xc7\x23\x81\x49\xa5\x0e\x43\xd4\x56\xab\x65\x46\x69\x1e\x0b\
+\xce\xaa\x39\xb4\x76\xfe\x67\x7b\x4c\x09\xa8\xaa\xb1\x98\xf6\xdf\
+\x49\x4f\xbf\xef\xb0\xb1\xc5\x0c\x14\xc6\xb3\x3d\x4e\xe5\x7c\x0a\
+\xce\xa3\x3c\x27\xdb\xb2\x9a\xcd\x26\xe6\xf3\x39\xa6\xd3\xa9\xb9\
+\xa8\xb3\x2c\x33\xa1\x23\x2f\x44\x2e\x2e\x4a\xd3\x74\x2f\x41\x23\
+\x01\xc2\xb2\x03\xc3\x57\xa9\x0a\xda\x6c\x36\xe6\x90\x90\x35\x43\
+\xb2\xb6\x87\x1a\x96\x7d\xdf\x37\xf3\x8d\x6c\x60\xd9\xef\xa9\x6e\
+\xa9\x91\x0d\xc4\xba\xbf\xc7\x0e\x5b\x6d\xe2\xa8\xaa\x74\xc4\xe7\
+\x25\xa3\x4b\xc9\x9f\x9a\x12\x42\x7b\x73\x35\xb9\x5c\x56\xf6\x69\
+\xb2\x57\x91\x6a\x97\xd5\x6a\xb5\x33\x11\x81\x42\xf2\x63\x58\x54\
+\x79\xf1\x4a\xe5\x8c\xdc\x40\x2d\xa7\x0e\x48\x80\x1e\x22\x4e\x78\
+\x1f\x4e\x4e\x60\x08\x5e\xa5\x65\xb5\x01\x54\x57\x4a\xa9\xf2\x86\
+\xb6\xf7\x94\xf9\xad\x24\xc1\xc8\x2e\xcb\x75\x86\x72\x76\xee\x7a\
+\xbd\x36\x4d\xd8\x4a\x0a\xa9\xe7\xdc\xcb\xd4\x12\x60\x32\xbf\xea\
+\x74\x3a\xc8\xb2\x0c\xd3\xe9\xd4\xf4\x60\xb2\x6f\x11\xf8\x6f\xf9\
+\xe0\x18\x0f\x53\xf7\x7b\x09\x5a\xe6\xb3\x92\xc1\xb5\xef\x53\x47\
+\x3e\x49\x72\x89\xe5\x1e\x12\x35\x75\x5e\x53\x7a\xbf\x3a\x16\xd7\
+\x06\xb4\x0c\x97\x1f\x5d\x60\x22\x1c\xe6\xca\x06\xf9\x38\xbe\x3f\
+\x1e\x38\x59\x96\xe9\x10\x6a\xf5\x9c\xfb\x8d\xde\x4f\x86\x97\x9c\
+\x86\xc7\xee\x0b\x82\x52\xce\x75\xe5\x3e\x4b\xca\xf0\x6c\x6f\xc4\
+\x10\xd9\x06\x83\x9d\xa7\xf2\x79\x78\xb1\xd6\x01\x84\x39\x65\x55\
+\x78\x2b\x6b\xa7\x92\xa8\xa9\x12\xef\x1f\x6a\x3f\xb3\x3d\xaa\xfd\
+\xd5\x5c\x50\x96\x52\x48\x7a\x4d\x8e\xf3\xb4\x85\x0f\xf2\x73\xe1\
+\x61\xa7\x62\x04\xf5\x9c\x7b\x3d\x8e\xbc\xb8\x39\x2f\x87\xc0\x64\
+\xb8\x49\x80\x3e\x3c\x3c\x98\x1c\xcf\x6e\xb3\xb2\x2f\xf2\x38\x8e\
+\x71\x77\x77\x67\x40\xcc\x92\x08\xc3\x3b\xea\x79\x29\x80\xa8\x1a\
+\xa5\x29\x3d\x4f\x9d\xe7\xdc\x77\xdb\x21\x6f\x7e\x6c\x1e\x6a\x1b\
+\x35\xb6\xb2\x36\xdc\x68\x34\x10\x45\x91\x61\xb7\xed\xd7\x90\xde\
+\x9d\xe0\x54\x53\xcf\xb9\x97\xad\x95\xa2\x76\x8e\xed\x90\xe4\x8f\
+\xbc\xa8\x09\x52\xde\x7f\xb1\x58\x20\x49\x12\xf4\x7a\xbd\x9d\xf0\
+\x6d\x3e\x9f\xe3\xd3\xa7\x4f\x46\x53\x2a\x45\xed\x1c\xfc\x4c\xe1\
+\x80\xcd\x82\x56\x85\x98\x87\xc0\x69\x33\xb0\x72\xf6\x90\x54\x17\
+\xd9\x7a\x57\xbb\x9b\xa4\xca\x6b\x56\xb1\xd3\x8c\x0a\x6c\x51\x3c\
+\x95\x3f\x9c\x7c\x20\xef\x43\xcf\x4f\xcf\xa9\x61\xad\x7a\xce\x83\
+\x9e\x93\xe1\x24\x65\x67\x72\xfd\x00\xef\x67\x13\x46\x7c\x1c\x97\
+\x06\xd9\x17\x32\x37\x79\x35\x9b\xcd\x9d\x39\x43\x64\x7f\xf9\x18\
+\x59\x4e\x39\xc4\x2a\x1f\xca\x39\x25\xc8\xed\x1d\x2c\x75\xa1\x6b\
+\x9d\x17\xb5\xc1\xba\x6f\xa2\x3d\xc3\x5c\x4e\x61\xa0\x7c\xf0\x98\
+\x83\x51\x4d\xc1\x59\x7b\xc1\xd3\xd8\x29\x41\xcf\x59\x15\xaa\xd2\
+\xb3\x31\x4c\xed\x74\x3a\xa6\x55\x4b\x12\x1e\x65\x59\x1a\xd9\x9d\
+\x9d\x97\xc9\x0b\x9b\xad\x5c\xf2\xf7\x24\x56\xec\x7f\xc7\x12\x42\
+\xf4\xbc\xb2\xf5\xcc\x2e\x7f\xc8\x7d\xa0\x92\xc8\xa9\x02\xac\xed\
+\x55\x6d\xf2\xc7\x06\x3b\xbb\x52\x18\xbe\x57\x1d\x0a\x76\x08\xaf\
+\x61\xad\x5a\x6d\xbe\x45\x0f\xc6\xb0\xd5\x5e\xc3\x2e\x2f\x22\x0a\
+\xdd\xd7\xeb\xb5\x19\x07\x42\xa6\x55\x7a\x2d\xb9\xb6\xc0\x96\xca\
+\x51\x06\xc8\x70\x53\x86\x88\xd2\x53\xc9\x10\x77\x5f\x07\x8c\x0d\
+\x44\x99\xcf\xd5\xb1\xb1\x55\xed\x63\xc7\x10\x45\xb2\x66\x6b\x77\
+\xdb\x70\xed\x03\x87\xa2\xd9\x1e\x5a\x86\xc7\x36\xc0\x15\x9c\x6a\
+\xb5\x17\x1e\xbd\x19\x2f\x6a\x39\x64\xab\x4a\x33\x4a\x00\xf8\xbe\
+\xbf\x33\x4a\x52\x5e\xf8\xf2\xf1\x36\xc1\x43\x35\x92\x6c\x4b\xa3\
+\x4a\x88\xd2\x36\x5b\x73\x7b\xc8\xd3\xc8\xc9\x09\x75\x13\x19\x6c\
+\xb0\xd4\x79\x4c\xfb\xf0\xb2\xef\x6b\x87\xd1\x7c\xed\x34\x4d\x77\
+\x08\x22\x02\x51\xfe\x9d\x55\xde\x58\xc1\xa9\x56\x09\x4a\x7a\x37\
+\x0a\xd9\xa5\xaa\xc7\xf6\x1a\xf2\xf4\xe7\xcc\x1e\x0e\xb9\xe2\xe0\
+\x65\x0e\x89\x96\x1d\x2d\x14\x1e\xc8\xd5\x7a\xcc\x6f\x79\xa1\x53\
+\x80\x2f\xf3\x53\xbe\x8f\x3c\xcf\xd1\xef\xf7\xcd\x7c\x20\x69\x45\
+\x51\x98\xd9\x3e\x75\x35\xd2\x43\x2c\x6e\xd5\x4e\x17\xbb\x94\x22\
+\x0f\x80\xe5\x72\xb9\x33\xa7\x57\x7e\x2e\x1c\x93\x52\xf5\xfa\xb2\
+\x24\xa4\x7b\x3c\x35\xe7\xac\xff\x50\x44\xff\xa1\xbd\x42\x41\x6e\
+\x7d\xae\x0a\xbf\xe4\x60\xae\xf5\x7a\x8d\x77\xef\xde\xe1\xf6\xf6\
+\xd6\x6c\x19\xeb\xf5\x7a\xa6\x86\x19\x86\x21\x4e\x4f\x4f\xcd\x26\
+\x68\x76\x90\xb0\x71\x9b\x3b\x4a\xe4\x38\x4b\xf9\x1a\x14\xe5\xd7\
+\x89\xc5\x39\xce\x84\xe4\x14\x43\x73\x1b\x80\xf6\x41\x73\xac\x4c\
+\xaf\x2a\x57\x97\xb5\x61\xa9\x37\xb6\x9f\x43\xe6\xb8\x72\x9a\x44\
+\xdd\xe7\xaa\x9e\x53\xcd\x18\x67\xae\xca\x69\x03\x76\x2f\xa7\x94\
+\x9d\x49\x4f\x52\x14\x85\x99\x00\x90\xa6\x29\xde\xbd\x7b\x07\x00\
+\x38\x39\x39\xc1\xb3\x67\xcf\xd0\xe9\x74\x00\xc0\x8c\xbb\xb4\x77\
+\x9e\x70\xbe\xab\xef\xfb\xa6\x18\xcf\x0b\xd9\xbe\x80\x0f\xb1\xb5\
+\x55\xeb\x22\x6c\x8f\x58\xb5\x2b\xa5\xaa\xf1\xbb\xaa\x4b\x65\x1f\
+\x58\x29\xd5\xab\x7a\x2d\x46\x27\x24\x88\xe8\x79\x25\x19\xa6\xe0\
+\x54\xab\xbd\xb0\x82\x20\x30\xa5\x0d\x9b\x40\x91\x13\xe7\x18\x86\
+\x71\x17\x88\x5c\x28\xdb\x6c\x36\x91\xa6\x29\xae\xaf\xaf\xb1\x5a\
+\xad\xd0\xe9\x74\x30\x18\x0c\x76\xf6\x95\xc8\x89\xe9\x72\xda\xc1\
+\xfd\xfd\xbd\x21\x85\xd8\x26\x06\xfc\x3a\xda\x92\x13\x0d\xf6\xe5\
+\x85\x36\xa9\x53\x05\xbc\xaa\x3c\xb8\xee\x7e\x55\xb3\x8e\xe4\x6b\
+\x48\xed\xaf\x24\xa4\x24\x28\x6d\xed\x30\x41\xca\x43\x84\xc2\x7f\
+\x35\x05\x67\x6d\xf8\xc6\x29\x75\xfb\x42\x2c\x79\x61\xcb\xfd\x26\
+\xae\xeb\x62\x36\x9b\xe1\xea\xea\x0a\xc0\x7f\x05\x09\xd7\xd7\xd7\
+\x68\xb5\x5a\x18\x0c\x06\xb8\xb8\xb8\x80\xef\xfb\x28\xcb\x72\x27\
+\x64\x95\x21\x1e\xd9\x5d\xe6\xab\x72\xd4\x24\x4b\x31\xfb\x06\x4b\
+\xdb\x0d\xd1\x55\xb9\x63\x1d\xe0\xf6\x09\xe0\xab\x9e\xc7\xfe\x4c\
+\xe4\x40\x33\xf9\x19\x92\xbd\xdd\x6c\x36\xe8\xf7\xfb\xe6\xf9\x79\
+\xd8\xa8\xe7\x54\x70\x1e\x34\x9e\xe0\x14\x03\xc8\x39\xab\xf6\x54\
+\x3d\x39\x15\x8f\x3f\x33\xa4\x0d\x82\xc0\x14\xdf\x39\xfa\x92\xcc\
+\xef\xe5\xe5\x25\x3a\x9d\x8e\xa9\x8b\xce\xe7\x73\xb3\xe0\x96\x80\
+\xa5\x17\x61\xaf\x63\xd5\x04\x83\x7d\xa5\x94\xaa\x7c\x51\x1e\x2a\
+\x75\xa1\xaa\x0d\xd0\x7d\x26\x0f\x01\xb6\xb8\x71\x75\xa0\x0d\x70\
+\xd6\x7a\xdb\xed\xf6\x0e\xf3\x9d\xe7\xf9\x51\x07\xa2\x82\x53\xcd\
+\x90\x41\x04\x29\x49\x17\x7a\x84\x7d\xf9\x16\xf3\xc0\xf9\x7c\x8e\
+\x28\x8a\x0c\xf8\xe8\x61\xb7\xdb\xad\x59\xf0\x73\x7a\x7a\x8a\xd1\
+\x68\x84\x30\x0c\x91\xe7\x39\xc6\xe3\xb1\x01\x68\xd5\x54\xbc\x2a\
+\x52\xa6\x4a\xfc\x7e\xcc\x04\xc0\x7d\xb7\x57\x81\xb2\xae\xf6\x69\
+\xd7\x7f\xd9\x36\xc7\xcd\x66\x00\x4c\x14\x90\x24\x89\x99\x2c\x4f\
+\x63\x03\x39\xc1\xa9\x61\xad\x82\xf3\x60\xce\xc9\x0b\x85\x2b\x15\
+\xaa\x64\x6f\x12\x14\xb2\x4c\xc2\xf0\x74\x32\x99\x20\x8a\x22\xd3\
+\x4f\x29\x81\x96\xa6\x29\x3e\x7d\xfa\x84\xf5\x7a\x8d\x8b\x8b\x0b\
+\x2c\x16\x0b\x43\x0e\x55\xcd\xe7\xb1\x43\x48\xfe\x6c\xeb\x80\x79\
+\xff\xaa\x6e\x96\xba\x69\x07\xfb\x88\x9f\x3a\x32\xc9\x06\x31\x47\
+\x63\xb2\xa6\x39\x9f\xcf\xcd\xc0\xeb\x24\x49\x4c\xfe\xce\x92\x12\
+\x07\x82\xc9\x7c\x7d\xdf\x80\x6c\x05\xa7\x9a\x01\x50\xbb\xdd\xc6\
+\x72\xb9\x34\x40\xa5\x68\xfb\x18\x89\x99\x9c\xce\xce\x31\x26\xb6\
+\x16\x97\x75\x4a\x0e\xae\xf6\x3c\x0f\xfd\x7e\xdf\x34\x73\x4b\x70\
+\x32\x4c\x94\xb5\x42\xdb\x73\xda\x21\xad\xac\x29\xda\x25\x93\x7d\
+\x93\xf4\x6c\x95\x4f\x15\x70\xed\xc7\xb1\x96\xca\xcf\x88\x6a\x27\
+\xe6\xc9\x5c\xed\xc0\xee\x93\xcd\x66\x83\xc5\x62\x61\x36\x64\xaf\
+\x56\x2b\x74\xbb\x5d\x05\xa7\x82\xf3\x38\x52\x88\xdb\xa4\x3f\x7c\
+\xf8\x60\x18\x53\x7a\xa4\xa7\x18\x37\x3c\x7b\x9e\xf7\x68\x97\xa5\
+\xe7\x79\x98\x4c\x26\x68\x36\x9b\x18\x0e\x87\x08\x82\xc0\xac\x75\
+\xa0\x07\x67\x93\x34\x3d\x24\x73\x50\x02\x95\x42\x06\xdb\xa3\xd7\
+\x0d\xd4\xaa\x0a\x5d\xed\x7c\xf3\x18\xe9\x9e\x14\xb7\x53\x1d\x25\
+\xe5\x8a\x5c\xef\x60\x13\x67\x6c\x8b\xe3\xf6\x33\x3e\x2e\x0c\x43\
+\x05\xa7\x82\xf3\x38\xf3\x7d\x1f\x61\x18\x1a\x72\x87\x1e\xac\xae\
+\xe7\xd0\xd6\xbd\xca\x61\xce\xad\x56\x0b\xbe\xef\x3f\xda\x46\xc6\
+\x52\xc2\x64\x32\x41\x51\x14\x88\xa2\x08\x61\x18\x1a\xe0\x85\x61\
+\x88\xe1\x70\x08\xcf\xf3\xcc\xf0\x2b\xd9\xd1\xb2\xd9\x6c\xd0\x6e\
+\xb7\x1f\x91\x28\x5c\xf9\xc7\x03\xc1\xee\x16\xb1\x81\x78\x88\xf8\
+\xa9\x23\x89\x98\x2b\xf2\xe0\xa8\x63\x85\xa5\x57\x66\x64\xc0\x03\
+\xa8\x28\x0a\xb3\x3d\x5b\x9b\xac\x15\x9c\x47\x33\xb6\x51\x14\xc1\
+\xf7\x7d\xa4\x69\x6a\xd4\x3b\x94\xa7\xc9\x12\x88\xcd\x82\xda\x39\
+\x21\x57\x0d\x74\x3a\x9d\x1d\x21\xb8\xbc\x78\x19\x06\x52\x3d\x33\
+\x1a\x8d\x0c\xa9\xc2\x7a\x6a\x55\x79\xa4\xce\xc3\xb5\xdb\x6d\x9c\
+\x9d\x9d\xed\x0c\x1c\x3b\x04\x9e\xaa\x9f\xab\x48\x21\xb9\xa5\x9b\
+\xec\xf2\x21\x52\x89\xe1\xad\xdc\x3f\x4a\xa5\x53\x18\x86\x66\x52\
+\xbe\xda\x7f\x0e\x32\xfd\x08\xf6\x93\x42\xec\xe0\x67\x49\x45\xe6\
+\x83\x14\xb7\xdb\x82\x6f\x7a\x08\x12\x24\xcd\x66\x73\xa7\x5d\x8c\
+\xcb\x84\x64\x4e\x28\x73\x45\xb2\x9d\x9c\x84\x20\x77\x77\xca\x95\
+\x81\xfb\x0e\x05\x86\xb5\xbe\xef\x63\x30\x18\x20\x0c\xc3\x47\xa3\
+\x2e\xab\xc0\x58\x37\xe3\xc8\x06\x1c\xc9\x1b\x19\xba\x1e\x03\x68\
+\xe9\x41\xe5\x5e\x99\x7e\xbf\x8f\x28\x8a\xf4\xa2\x53\x70\x1e\x9f\
+\x77\xb6\xdb\xed\x47\x03\x9c\xe5\xbc\x5a\x00\x66\xe5\x9f\x2c\x5f\
+\x50\x47\xcb\x90\x93\x3a\xd9\xf9\x7c\x6e\x4a\x06\x87\xba\x2f\xec\
+\xe5\x46\xf6\x7b\x93\xb5\x45\x9b\x10\x92\x60\xa0\xf0\x81\xab\x1b\
+\x38\x29\xb0\x4e\x5b\xbb\x4f\x43\x4b\x60\x92\x20\x23\x01\xb4\x0f\
+\x90\x55\x53\xe4\xc3\x30\x34\x8c\xad\xe3\x38\x18\x8d\x46\x08\x82\
+\x40\x2f\x3a\x0d\x6b\x9f\xf0\x01\xb9\x2e\xfa\xfd\x3e\x3a\x9d\x8e\
+\x99\x0b\x24\x3d\x13\x73\xad\x56\xab\xb5\x13\xe2\x51\x90\x4e\x19\
+\xa0\xec\xdf\xe4\xfd\x39\x95\xa0\xaa\xb4\xc1\x3d\x96\xd2\xc3\xda\
+\xb9\xe2\x3e\xcf\x69\xe7\x92\x7c\x7f\x24\xb4\xd2\x34\x45\x1c\xc7\
+\x3b\xd3\x1d\xea\xba\x50\xe4\x73\x72\x80\xd7\xbe\x89\x0a\x55\xef\
+\xc9\x16\x72\xb4\xdb\x6d\x33\x8e\x65\x30\x18\xa0\xdf\xef\x6b\x7d\
+\x53\x3d\xe7\xd3\xc1\xd9\xed\x76\xd1\xef\xf7\x77\x4e\x76\xe9\x59\
+\xd8\xe5\xcf\x46\x69\x8a\xde\xd9\x43\xc9\xae\x12\xd7\x75\x4d\x28\
+\x48\x6f\x6a\xb3\xa8\x04\xe6\x60\x30\xc0\xf9\xf9\xb9\x79\x9e\x3a\
+\xb6\x75\x5f\x8e\x58\xd5\xcc\x2c\xc1\x51\xe7\xa9\xea\xf6\x73\xba\
+\xae\x6b\x86\x77\x91\x75\xae\x6b\xd4\xb6\x3d\xbc\xfc\x5d\xab\xd5\
+\xda\xf1\x9a\x67\x67\x67\xa6\x19\x40\x4d\x3d\xe7\x93\x8c\xf3\x56\
+\xa9\xde\xb1\x1b\x85\xe5\x44\x02\x59\x0b\xa5\xc7\x64\xa3\x34\xd7\
+\x05\xb2\x47\x74\x38\x1c\xc2\x71\x1c\x24\x49\xb2\xa3\x27\x6d\xb7\
+\xdb\x66\x6d\xfd\xbe\xb1\x98\x76\x97\xcc\x3e\x60\x56\x49\xf7\xe4\
+\xd0\xea\x43\x53\xde\xc9\x36\xf3\xb0\xa9\x62\x7b\x0f\x79\x50\x7e\
+\x6e\x61\x18\x9a\xb6\xb9\x6e\xb7\x8b\x93\x93\x13\x65\x69\xd5\x73\
+\xfe\xb6\xd0\x76\x30\x18\x98\xdc\x52\xd6\x0e\xeb\x26\x02\xa4\x69\
+\x8a\xc5\x62\xf1\x08\xa8\x04\xf3\x62\xb1\x40\xb7\xdb\x35\x35\x4c\
+\xbb\xbe\x58\x95\xab\x55\xe5\x87\xc7\x84\x95\xf6\x74\x3d\x86\xb3\
+\xac\xa1\x52\xd3\x5a\x35\x05\x81\x39\x26\x81\x59\xf5\x9c\x75\xa0\
+\xb6\x6f\x0f\x82\x00\xbe\xef\x1b\x25\xd4\xd9\xd9\x19\xba\xdd\xae\
+\xb2\xb4\xea\x39\xff\xff\xc6\xc6\xe8\x87\x87\x87\xbd\x13\xe4\xec\
+\x3a\x22\x19\xde\xa2\x28\x4c\x01\x9e\x4c\x30\x19\x5c\xf6\x75\xda\
+\x13\xe6\xab\x40\x6f\x7f\x3d\x54\xa3\xac\xfa\x3d\x73\x50\x92\x42\
+\x14\x02\x50\x18\xc0\xbc\x54\x8e\x45\x61\x68\x2e\x95\x4e\xf6\xf3\
+\xef\x7b\x2f\x32\x9c\xe6\xc6\xb4\x7e\xbf\x8f\xb3\xb3\xb3\x47\x13\
+\xe8\xd5\x14\x9c\x4f\xf6\x9e\xc3\xe1\x10\xc3\xe1\x10\x59\x96\x3d\
+\xea\xde\x97\xfd\x89\x55\xbb\x42\xe4\x18\x0f\xe9\x1d\x29\x04\x27\
+\x58\x39\x20\x4c\x8e\x2c\xa9\x2a\xd3\xc8\xe7\xae\x62\x74\xf9\x9c\
+\xd2\x93\xdb\x9b\xa8\x65\x1f\x2a\x65\x75\x72\xfb\x34\xff\x3e\xf6\
+\x8f\xd2\x0e\x85\xc1\xf6\xe1\xc1\x43\x8a\xb9\xe6\x7c\x3e\x37\xb9\
+\xe6\x70\x38\x54\xaf\xa9\x61\xed\x6f\xb7\x4e\xa7\x83\x8b\x8b\x0b\
+\x93\x33\xd9\x53\xf4\xea\x58\xd3\x2a\x72\x45\xee\xca\x64\xe8\xcb\
+\xa9\x7d\xd3\xe9\x14\xb7\xb7\xb7\xc8\xb2\x6c\xe7\x7e\x76\xd8\x2c\
+\x67\x16\xd5\x6d\xce\xb6\xcb\x2c\x55\xbd\x9b\xf6\x6d\x52\xf4\xbf\
+\xef\xef\xd9\xc7\x16\xdb\xb7\xcb\x3d\x9c\x69\x9a\x62\x30\x18\xe0\
+\xea\xea\x4a\xbd\xa6\x82\xf3\x2b\x7d\x58\xff\x51\xed\x9c\x9d\x9d\
+\x19\x85\x50\x95\x30\xc0\xbe\xf0\xab\x58\x4c\xfb\xe2\x9d\xcf\xe7\
+\x66\x76\x90\xeb\xba\x98\x4c\x26\x98\x4c\x26\xe6\x75\x8f\x25\x5f\
+\xf6\xe5\xc1\x55\x87\x88\x5d\xa2\xe1\x2e\x15\xee\x84\x61\x1f\x69\
+\x1d\x19\x55\xb7\x8c\xd7\x36\x2e\x78\x8a\xe3\x18\xbe\xef\xe3\xf9\
+\xf3\xe7\x18\x0c\x06\x3a\x69\x4f\xc1\xf9\x75\x99\xdb\xab\xab\x2b\
+\x9c\x9c\x9c\x18\x62\x85\xde\x46\x0a\xd0\xeb\x72\xc5\xaa\xdb\xe5\
+\x5e\x4f\x00\x66\x9f\xe6\x64\x32\xc1\x74\x3a\xad\x95\xec\x49\x82\
+\x67\x9f\x47\xab\x6a\xd0\xae\x3a\x78\x48\x0a\xb1\x31\x3c\xcb\x32\
+\x13\xd2\x56\x1d\x38\xb2\xc4\x53\xe7\x85\xf9\xdc\x2c\x9d\x94\x65\
+\x89\xf3\xf3\x73\x5c\x5d\x5d\xe9\xc4\x03\xcd\x39\xbf\xae\x39\x8e\
+\x83\xc1\x60\x80\x17\x2f\x5e\x98\xe9\x76\x52\x0f\x4b\x90\xda\x8b\
+\x90\x0e\x8d\xfe\xa0\xcd\xe7\x73\x74\x3a\x1d\x44\x51\x84\x34\x4d\
+\x31\x99\x4c\x4c\xad\xd5\x26\x9a\x78\x71\x4b\x31\x3c\x7f\x96\x1a\
+\x60\x8a\x25\x6c\x01\x85\xdd\x29\x22\x77\x92\x66\x59\x86\x38\x8e\
+\x4d\xae\x59\xe7\x79\xab\x84\xf4\x04\xa4\x3c\x88\xd8\x40\x3e\x1c\
+\x0e\xf1\xf2\xe5\x4b\xad\x6b\x2a\x38\x7f\x3f\x72\xe8\xfc\xfc\x1c\
+\x59\x96\x99\x7a\x9d\xcc\xeb\xe8\x29\xaa\xda\xcb\xe4\x8a\x84\xaa\
+\x1c\x90\x63\x3e\x38\xbe\x24\xcf\x73\xb3\xc1\x8c\xb2\x3b\x5b\x30\
+\x5f\x35\x43\xc8\x9e\xaf\x6b\xf7\x7f\xd2\x5b\xf3\x3e\xec\x3b\x65\
+\xb8\x4e\x85\x53\x95\x57\xb6\x3d\x64\x5d\xfb\x19\x8d\x65\x9b\x4e\
+\xa7\x83\x17\x2f\x5e\x60\x34\x1a\x69\x38\xab\x61\xed\xef\x1b\xde\
+\x5e\x5e\x5e\xe2\xf2\xf2\xd2\x10\x39\x72\x22\x1f\x4b\x12\x72\xf0\
+\xd7\xa1\x95\x7a\x04\x8d\xdc\xf0\x1c\x45\x11\x36\x9b\x0d\xee\xef\
+\xef\x31\x9b\xcd\x76\x46\x63\xd6\xf5\x66\x56\x2d\x18\x92\x60\xa0\
+\xa4\x90\x60\xa7\x4e\x58\x7a\xd9\xaa\xc9\xf0\x75\xde\xd3\x0e\x8f\
+\xe5\x4a\x05\x1e\x46\xad\x56\x0b\x97\x97\x97\x78\xf6\xec\x99\x0a\
+\x0e\xd4\x73\xfe\xfe\x16\x45\x11\x2e\x2f\x2f\xb1\x5c\x2e\x71\x77\
+\x77\x67\xd8\x55\x5e\xa4\x1c\xf9\x48\x40\xd8\xeb\xf3\xaa\x84\x0c\
+\x72\x0f\x4b\x1c\xc7\x08\x82\x00\xed\x76\x7b\x67\x31\xef\x7a\xbd\
+\x36\x6c\x6a\x9d\x00\xc0\x5e\x8b\x60\x77\xa3\x48\x26\x57\x82\x89\
+\x9e\x9b\xe1\xed\xbe\xd5\x0c\x55\x23\x4c\x6c\xb6\x98\xaa\xa8\x8b\
+\x8b\x0b\xbc\x78\xf1\x42\xc3\x59\x05\xe7\x1f\xc7\xde\xf6\xfb\x7d\
+\x3c\x7f\xfe\x1c\x9b\xcd\x06\xe3\xf1\xd8\x00\x54\x5e\xf4\x76\x28\
+\x6b\x7b\x22\x39\xfd\x5c\x82\x80\xe4\x4c\xa3\xd1\xc0\x70\x38\x34\
+\xe3\x31\xd9\xa8\x6c\xef\xd7\xac\x1b\x91\x29\x27\xab\xcb\x55\x0f\
+\x12\x50\x3c\x48\x38\xc6\x53\x4e\x34\xd8\x37\xd8\x4b\x7e\x6f\xb3\
+\xb6\x8c\x1c\x2e\x2e\x2e\xf0\xea\xd5\x2b\x93\x37\xab\x29\x38\xff\
+\x10\xe3\x78\x11\x5e\x90\xd3\xe9\xd4\xe4\xa2\xcc\x4f\xab\x98\xd6\
+\xaa\x19\x40\x72\x4d\x81\x2c\xcc\x73\x4d\x03\xf5\xad\xfb\xf2\x3d\
+\xf9\x7b\x12\x46\xb2\x83\xc6\x16\x34\xb0\xfe\xc8\x81\x63\x0c\x41\
+\xa9\x05\xae\x0a\x93\x09\xf8\x7d\xa0\x64\x33\xf9\xc5\xc5\x05\x7e\
+\xfe\xf9\x67\x6d\xa4\x56\x70\xfe\x39\xec\xad\xeb\xba\x38\x39\x39\
+\x31\x60\x9d\x4c\x26\xc8\xb2\xcc\x5c\xdc\x12\x6c\x75\xc3\xb6\xaa\
+\x04\x0a\xd2\xdb\x25\x49\x82\xa2\x28\x10\x04\xc1\x8e\x16\xd6\x9e\
+\x97\x6b\xb3\xaf\x92\x99\xb5\x5f\x87\x92\x3d\x39\xd1\xa1\xd7\xeb\
+\x21\xcf\x73\xcc\x66\xb3\x47\xcb\x82\xf7\xd5\x72\xe5\x7b\x27\x7b\
+\x7d\x71\x71\x81\x5f\x7e\xf9\x05\xa3\xd1\x48\xdb\xc1\x14\x9c\x7f\
+\x1e\x40\x9b\xcd\xa6\x61\x21\x1d\xc7\xd9\x19\x0d\x42\xaf\x65\x87\
+\xaf\xb6\x07\xab\x1b\x21\xc2\x16\xad\x3c\xcf\x91\xa6\x29\x56\xab\
+\x95\x11\x90\x57\xf5\x61\xca\x19\x47\x75\xf5\x4f\x2e\x48\xaa\xea\
+\x15\xad\xda\xa2\xb6\x4f\xd8\x20\xbd\x32\x95\x53\x57\x57\x57\xf8\
+\xf9\xe7\x9f\x31\x1c\x0e\x15\x98\x0a\xce\x6f\xc7\x83\xba\xae\x8b\
+\x0f\x1f\x3e\x00\x80\x59\xa7\x20\xc3\x57\x7a\xb7\x2a\x50\xca\xfc\
+\xd0\x66\x48\xc9\xb2\x72\x5a\xba\x24\x8f\xe4\xce\x16\x3b\xbc\xb5\
+\xf5\xbd\x6c\x72\xae\xcb\x1b\xe5\x50\xeb\x2a\xd1\x83\x2d\x40\xa0\
+\x77\x5e\xad\x56\x68\xb5\x5a\xf8\xe9\xa7\x9f\xf0\xe2\xc5\x0b\x23\
+\xa6\x50\x53\x70\x7e\x33\x1e\x74\x30\x18\x98\x89\x01\x77\x77\x77\
+\xa6\x09\x5b\xb2\xab\xf4\x34\x54\x15\x55\x85\xb8\x64\x7c\x59\x8a\
+\xe1\x98\x4c\xe6\x9d\x12\x20\xbc\x9f\x0d\x38\x49\xec\xb0\xc4\xc3\
+\x7c\xb2\xca\x5b\xf3\x76\x3b\x37\xb5\x45\xf3\xb6\x7e\x97\x4b\x9a\
+\x5e\xbe\x7c\x89\xab\xab\x2b\x33\x27\x49\x4d\xc1\xf9\xcd\xb1\xb8\
+\xdd\x6e\x17\xaf\x5e\xbd\x82\xef\xfb\xf8\xf4\xe9\x93\x99\x82\xce\
+\xdb\xb9\xde\x8f\xa4\x8d\xf4\x66\x54\xf7\xb0\xad\x4b\x82\xcf\xde\
+\xd6\x25\x41\xc5\xe9\xf4\x24\xa2\x38\x89\x81\x0d\xe2\x04\x68\x1d\
+\x39\x25\x01\x5c\xb7\xbc\xc8\x16\xd4\x13\xc4\x27\x27\x27\x78\xf1\
+\xe2\x05\xce\xce\xce\x4c\xdd\x57\x4d\xc1\xf9\xcd\x7a\xd1\x20\x08\
+\xf0\xf2\xe5\x4b\x74\xbb\x5d\x5c\x5f\x5f\xe3\xe1\xe1\xc1\x4c\x3e\
+\xa7\xb0\x1c\xf8\xaf\x28\x9c\x6b\x0a\xa8\xd9\x9d\xcf\xe7\x28\xcb\
+\xd2\x4c\x82\x67\x8e\x59\x35\xfb\x96\xc0\x02\x60\xfa\x25\x65\x19\
+\xa7\xaa\xbb\xc5\x06\x1e\x1f\xcb\x43\xc2\x16\x1c\xd8\x1d\x30\xec\
+\x0b\x7d\xf6\xec\x19\x7e\xfa\xe9\x27\xf4\x7a\x3d\xf3\xfe\xd5\x14\
+\x9c\xff\x33\x79\x68\x18\x86\xb8\xbb\xbb\xc3\xed\xed\xad\x21\x8b\
+\x64\xc9\x43\xea\x60\xe9\xcd\xe8\x2d\x5b\xad\xd6\x4e\x9e\xc9\x71\
+\x94\x9e\xe7\xc1\xf3\x3c\xac\xd7\x6b\x43\xee\x70\x2a\x1e\x73\x5d\
+\x12\x3c\x72\xbd\xbd\x5c\xf6\x2b\xf3\x48\xae\x86\xb0\xf3\x49\x09\
+\x48\xd9\xfb\x19\x04\x01\x9e\x3f\x7f\x6e\xd6\x19\xda\x64\x97\xda\
+\x6f\xbc\x7e\xb6\x87\x96\x7e\xa8\x7d\x15\x23\x61\x12\xc7\x31\xc6\
+\xe3\x31\xc6\xe3\xb1\x91\xe3\xc9\xc9\xef\x12\x50\xb2\x34\x42\xad\
+\x2d\x67\xe1\xf2\xf7\x9c\x01\xb4\x5c\x2e\x11\x45\x11\xfa\xfd\x3e\
+\xe2\x38\x46\x1c\xc7\x3b\x84\x93\x7c\x4e\x09\x50\x02\x8e\xe5\x13\
+\xb6\x87\xd9\x4d\xde\xbc\x1f\x87\x7c\x0d\x87\x43\x5c\x5c\x5c\xa8\
+\xb7\x54\x70\x7e\x5f\x20\x2d\xcb\x12\x71\x1c\x63\x3a\x9d\x62\x3c\
+\x1e\x63\x32\x99\x98\xfd\x28\x52\x90\x6e\x0b\x12\x38\x31\xbe\xd3\
+\xe9\x98\xe5\xb9\x92\xf8\x89\xa2\x08\xdd\x6e\x17\x8b\xc5\xc2\x80\
+\x53\x0a\x1c\xa4\xa7\x26\x39\xc5\x59\xba\x9c\x42\x2f\x15\x4b\x04\
+\x3d\x47\x7c\x72\x2a\xfb\xe9\xe9\x29\x06\x83\x81\xf1\xf6\x6a\x0a\
+\xce\xef\xca\x48\xd6\x24\x49\x82\xd9\x6c\x86\xf1\x78\x8c\xe9\x74\
+\x6a\x88\x1d\x99\x4f\x4a\xaf\xc7\x29\x05\xdc\x1b\xca\x11\x9b\x5c\
+\x1d\x11\x45\x11\x66\xb3\x19\xbe\x7c\xf9\x62\xbc\xa0\xad\x48\xa2\
+\x17\x67\xce\x2b\x57\xbd\xf3\x7d\xf1\x36\xee\x8b\x19\x0c\x06\x18\
+\x8d\x46\x66\xc0\xb6\x32\xb1\x0a\xce\xef\xd6\x7b\x12\x08\x72\x08\
+\x75\x92\x24\x98\xcf\xe7\x88\xe3\xd8\xd4\x1a\x65\x4d\x51\xd6\x40\
+\xe5\x73\x31\x07\x3c\x3f\x3f\xc7\xc9\xc9\x09\x1e\x1e\x1e\xf0\xf1\
+\xe3\xc7\x47\x3b\x3b\xeb\x86\x47\x4b\x21\x01\xc1\x1a\x04\x81\x09\
+\x93\x39\x54\x5b\x41\xa9\x84\xd0\x0f\x05\x4c\x86\x9c\xec\x32\x69\
+\xb7\xdb\x18\x0c\x06\xc8\xb2\xcc\xfc\x2b\x8a\x62\x67\x2a\x5e\xd5\
+\x58\x90\x24\x49\xcc\xf3\x4f\x26\x13\x24\x49\xf2\x48\x91\x44\x0f\
+\x5c\x35\xf8\xb9\xd5\x6a\x99\x36\xb2\x76\xbb\x6d\xbe\x72\x75\xa1\
+\xdd\x55\xa3\xa6\xe0\xfc\x6e\x01\x6a\x97\x27\x24\x29\xc4\x92\x0a\
+\xc7\x48\x32\xcc\x64\xfe\xc7\x55\x08\x12\xac\xac\x4d\xf2\x76\x32\
+\xb4\xf4\xb8\xfc\x2a\xc3\x63\xce\x0b\xe2\x42\x26\xd6\x56\x29\xa0\
+\x90\x80\x94\xde\x5b\x4d\xc1\xf9\xfd\xe7\x14\xc2\x93\xf1\xab\xad\
+\x6d\x25\x88\x82\x20\xd8\x01\x24\xbf\x97\x8a\x1d\xdf\xf7\xcd\xf0\
+\xea\x4e\xa7\x53\xd9\x3f\x4a\x86\x97\xa4\x10\x47\x70\x32\x7f\xad\
+\xdb\x64\x66\x1f\x2e\xea\x3d\x15\x9c\xdf\x25\x20\x25\x30\xe5\xef\
+\x25\x58\xab\xca\x1d\xf2\xab\xed\x81\x01\x18\x90\xf9\xbe\xbf\x33\
+\xc9\xa0\x6a\xc0\x97\xf4\xa2\xf2\xeb\xa1\x7f\x0a\x4a\x05\xe7\x77\
+\x6f\xb6\xa7\xb4\x5b\xbb\x6c\x45\x4f\x55\xd3\xb3\x3d\x7f\xc8\x1e\
+\x0d\x52\xa5\xd9\xad\x92\xee\x55\xd5\x42\x25\xf9\xa4\xa0\x54\x70\
+\xfe\x50\xde\x53\x76\xa1\x48\xb0\xd6\xad\x5c\xa8\x6b\xdb\xb2\x43\
+\xcd\x2a\x26\xb6\x8a\xdd\xad\xdb\xbf\x62\x7b\x75\xbb\xab\x45\xed\
+\x0f\xbc\x4e\xb4\x94\xf2\x6d\x91\x44\x75\x5b\xab\xeb\x06\x44\x57\
+\x79\xc2\x7d\x80\xde\xf7\xb8\x3a\xb0\xab\x29\x38\xd5\x9e\x00\xe4\
+\x63\x40\x54\x05\xcc\x7d\x80\x54\x53\x70\xaa\xa9\xa9\x1d\xc3\x4b\
+\xe8\x47\xa0\xa6\xa6\xe0\x54\x53\x53\x53\x70\xaa\xa9\x29\x38\xd5\
+\xd4\xd4\x14\x9c\x6a\x6a\x0a\x4e\x35\x35\x35\x05\xa7\x9a\x9a\x9a\
+\x82\x53\x4d\x4d\xc1\xa9\xa6\xa6\xa6\xe0\x54\x53\x53\x70\xaa\xf2\
+\xa6\xcf\x7c\x00\x00\x00\x25\x49\x44\x41\x54\xa9\xa9\x29\x38\xd5\
+\xd4\xd4\x14\x9c\x6a\x6a\x0a\x4e\x35\x35\x35\x05\xa7\x9a\x9a\x82\
+\x53\x4d\x4d\x4d\xc1\xa9\xa6\xf6\x23\xda\xff\x01\xb7\xbe\xbf\x34\
+\x7c\xc5\x7a\x2d\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
+\
+\x00\x00\x04\x7f\
+\x89\
+\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
+\x00\x00\x18\x00\x00\x00\x18\x08\x06\x00\x00\x00\xe0\x77\x3d\xf8\
+\x00\x00\x00\x01\x73\x52\x47\x42\x00\xae\xce\x1c\xe9\x00\x00\x00\
+\x06\x62\x4b\x47\x44\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\
+\x00\x00\x09\x70\x48\x59\x73\x00\x00\x1b\xaf\x00\x00\x1b\xaf\x01\
+\x5e\x1a\x91\x1c\x00\x00\x00\x07\x74\x49\x4d\x45\x07\xdc\x01\x0a\
+\x17\x02\x30\xf6\x73\x58\xfb\x00\x00\x03\xff\x49\x44\x41\x54\x48\
+\xc7\xbd\x95\x6b\x4c\x9b\x75\x14\xc6\x9f\xb7\xd7\xc1\xe8\xe8\xca\
+\x66\x3b\x4a\xc9\x80\x7a\x03\x5a\xca\x8c\x81\x99\x60\x5e\x37\x2e\
+\x92\x2c\xd9\x07\x2c\x4a\xe4\x36\x32\x34\xc6\x84\x0c\xe2\x40\x12\
+\x63\x5f\xb2\xa8\x89\x41\xe2\x52\xd6\x80\x51\xc1\x38\xa5\xc8\x75\
+\x1b\x42\x3b\x88\x62\x59\x02\x6c\x0c\x07\x04\x4a\xe2\x56\x20\x8b\
+\x5c\x02\x65\xa9\xb6\x89\xbc\x57\xbf\xa0\x21\x0a\x8c\x6e\xd1\xf3\
+\xf9\x7f\xce\xef\xfc\x9f\x73\x9e\x1c\xe0\x3f\x0e\xe2\x51\xf3\x2a\
+\x2a\x2a\x4c\x99\x99\x99\x65\xa1\xa1\xa1\xa9\x1c\xcf\xc9\xfc\x7e\
+\xdf\xdd\x55\xef\x5a\x6b\x6d\x4f\xdd\xe5\x99\xb6\x19\xfa\xb1\x9a\
+\xb2\x58\xde\x2b\x59\x5c\x5a\xa4\x39\x8e\x15\x7c\x3e\x9f\x30\x37\
+\x3f\x27\xdc\x1e\x1f\x13\x3a\x3a\xdb\xf8\x8a\x77\xce\x59\x01\x48\
+\xfe\x7a\x2c\xd9\x9a\x39\x38\x38\x28\x3c\xac\x7a\x4d\x4d\x8d\x3e\
+\xf5\xf8\x0b\xf5\x52\xa9\x54\xca\x32\x1c\xc4\x12\x31\x5a\xed\x76\
+\xbe\xa3\xa3\xc3\x73\xe7\xce\x44\x0f\xc3\x30\x76\x00\xc2\xb6\x80\
+\x4d\xc8\xae\x00\x85\x62\xff\xe9\xf0\x70\x45\x08\xc7\x71\x60\x58\
+\x1a\x04\x41\xa0\xb7\xcf\xf1\xf5\xad\x5b\x63\xd5\x14\x45\x2d\x92\
+\x24\x79\x8e\x24\x49\x62\x47\x00\x00\x50\x14\xb5\xd3\x6c\xc4\x45\
+\x45\xf9\x76\x8f\x67\x1e\x31\x31\x7a\x30\x0c\x03\x91\x58\x8c\x23\
+\x1a\x8d\x1b\xc0\xf2\x76\x09\xa2\x20\xf5\x0f\x79\x36\x3e\xe1\x79\
+\xb7\x7b\x06\x2c\xcb\x80\x65\x59\xb0\x0c\x8b\xc2\xc2\x82\xf4\xf8\
+\xf8\x78\xe9\xd6\xc6\xaa\xaa\xaa\xc2\x83\x06\xa4\xa5\xa5\x3d\xa5\
+\xd7\xc7\x45\x4b\xa5\x32\xb8\x67\xdc\x20\x08\x02\x2c\xcb\x20\x56\
+\x1f\x97\x5e\x5c\x5c\xd0\x5b\x56\x56\x16\xbd\xbc\x7c\xff\xb9\xeb\
+\xd7\x9d\x5f\x28\x0e\x84\x8d\x05\x0b\x20\x48\xf2\xc5\x53\x5a\xad\
+\x96\xd0\x6a\xb5\xe8\xec\xea\xc6\x47\x1f\xd7\x82\x65\x59\xec\x93\
+\xcb\x11\xa5\x8b\x3e\x19\xba\x3f\x64\x56\x1f\xf7\xcc\x58\x64\x64\
+\x64\xc9\x90\x6b\xe8\x41\xb0\x00\x79\xa2\xc1\x98\x73\xf7\xde\x1c\
+\x3a\xbb\x7b\x60\x3a\x76\x0c\x3d\x0e\x07\x26\xa7\xa6\x00\x01\xd0\
+\xe9\x74\x08\x0b\x0b\x0b\x91\xed\x93\x11\x23\xa3\xa3\x6c\x7f\xff\
+\x40\x4b\x30\x00\x82\xa2\xde\x37\x27\x9b\x4c\x46\x43\x42\x02\x7e\
+\x5d\x5a\x82\x58\x24\x42\x6c\x4c\x0c\x5a\xdb\xdb\xc1\xf3\x3c\x94\
+\x4a\x25\x08\x82\xc0\xac\x7b\x56\xa8\xae\xac\x76\xf2\x3c\xdf\xb2\
+\xe3\x16\xfd\xb3\xb8\xcd\x66\xcd\x4b\x49\x39\xde\xb8\xb2\xb2\x02\
+\x83\xc1\x80\xec\xac\x0c\xf4\x3a\x1c\x38\x1a\xa5\x85\x29\xc9\x08\
+\x99\x5c\x86\xd5\xd5\x15\xac\x7b\xd7\x99\xd7\x2d\xf9\xdf\x33\x0c\
+\x53\x0d\x60\x05\xdb\x78\x40\xa0\x28\xea\x6f\x93\x98\xcd\x66\x71\
+\x73\xf3\x97\x96\xcb\xdf\x7e\xc3\x65\x65\x65\x09\x29\xa9\x29\x82\
+\xcb\xe5\x12\x3c\x9e\x7b\xc2\x8f\x83\x3f\x70\x79\x79\xaf\xfd\xf6\
+\x12\x49\xce\x91\x24\xe9\x12\x8b\xc5\x56\x00\x85\x9b\x66\xdd\xdd\
+\x07\x00\x50\x52\x52\xa2\x38\x91\x71\xa2\x69\x7a\x6a\x3a\xc7\xe9\
+\x74\x22\x42\x15\x01\x9d\x2e\x1a\x6a\x8d\x9a\xbf\x74\xc9\x76\xd3\
+\x6a\xb5\x5e\xa3\x69\xfa\x36\x80\x39\x00\x6b\x00\xfc\x14\x45\x6d\
+\x6c\xa6\x0b\xbb\x02\xca\xcb\xcb\xf5\xa6\x64\x43\x47\x5b\xeb\x77\
+\xc6\xf1\xf1\x9f\xa1\x52\xa9\xc0\xb2\x2c\xce\x14\x17\x09\x36\x9b\
+\xad\xeb\xe2\xa7\x17\x2b\x01\x2c\x00\xe0\x1e\xa6\xef\xbf\x00\x7e\
+\xbf\x2f\xfb\xc9\xa7\xe3\x46\x6c\xb6\x86\x88\xf9\xf9\x05\x48\xa4\
+\x12\xd0\x34\x8d\x97\xb3\xb3\x30\x39\x39\xe5\xda\x2c\xee\xd9\xeb\
+\xea\x6d\x05\x48\xaf\x5e\xed\xae\x3e\xac\x3e\x7c\xad\xa1\xe1\x33\
+\xc9\xda\xaa\x17\x22\x91\x08\x1c\xcb\x21\x29\x39\x09\xaa\x83\xaa\
+\x5f\x4a\x4b\xdf\x38\xbf\x29\x09\x82\x02\xe8\xf5\x7a\x79\xd1\x99\
+\xfc\x7a\xb5\x5a\x73\x76\x7a\x7a\x1a\x07\x14\x0a\xd0\x1b\x34\x02\
+\x81\x00\xa2\xa2\xa2\x90\x71\x32\xdd\x5b\x79\xbe\xaa\x12\xc0\xf8\
+\x56\x7d\xf7\x12\x22\x00\x78\xf3\xad\xd2\x2a\x8e\xe3\x72\x9b\x9a\
+\xbf\x7a\xd7\xbb\xfe\xe0\x15\xf3\xab\xe6\xfb\x4a\xa5\x12\x5a\xad\
+\x16\xc5\xc5\x85\x1b\x9f\xd4\xd6\x7d\xe0\xf5\x7a\xfb\xf6\xa2\xf9\
+\xb6\x3f\x38\x14\x71\xc8\x7c\xa5\xeb\xca\xcd\xe1\x1b\xc3\x8d\xc3\
+\x37\x86\x7d\x26\x43\x82\xc6\x68\x34\xd4\x27\x1a\x12\x79\x7b\x8b\
+\xfd\xf3\xdc\xdc\xdc\x3a\x00\x75\x8f\x72\x9d\x24\x00\x20\x97\xcb\
+\x7e\x57\x1f\xd1\xa8\x01\xa8\x01\xf8\xff\x60\x99\x27\x92\x93\x93\
+\x04\x47\x9f\xa3\xdb\x62\xa9\x79\xfb\xb1\x6f\x72\x63\xa3\xad\x20\
+\xfc\x60\x78\xd3\xe8\xc8\xe8\x08\x41\x10\x0b\x31\xb1\x47\x73\x7e\
+\x1a\x1c\xea\x6f\x6f\xeb\x2c\x0b\x76\xa8\x3b\x1d\x7d\xd1\x85\x0f\
+\x2f\x14\x31\x0c\x7d\x36\x10\x08\x10\x13\x13\x93\x3d\x03\xce\x81\
+\x66\x00\x4b\xc1\x0e\xf5\x7f\x8f\x3f\x01\x2e\x51\xa3\xa9\xc7\xde\
+\x69\x7b\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
+"
+
+qt_resource_name = b"\
+\x00\x05\
+\x00\x6f\xa6\x53\
+\x00\x69\
+\x00\x63\x00\x6f\x00\x6e\x00\x73\
+\x00\x0d\
+\x0c\x7a\x4d\x87\
+\x00\x65\
+\x00\x72\x00\x69\x00\x63\x00\x57\x00\x65\x00\x62\x00\x33\x00\x32\x00\x2e\x00\x70\x00\x6e\x00\x67\
+\x00\x08\
+\x03\xc6\x59\xa7\
+\x00\x70\
+\x00\x6c\x00\x75\x00\x73\x00\x2e\x00\x70\x00\x6e\x00\x67\
+\x00\x11\
+\x0c\x97\x94\x07\
+\x00\x61\
+\x00\x64\x00\x42\x00\x6c\x00\x6f\x00\x63\x00\x6b\x00\x50\x00\x6c\x00\x75\x00\x73\x00\x31\x00\x36\x00\x2e\x00\x70\x00\x6e\x00\x67\
+\
+\x00\x0d\
+\x0c\x56\x4d\x87\
+\x00\x65\
+\x00\x72\x00\x69\x00\x63\x00\x57\x00\x65\x00\x62\x00\x31\x00\x36\x00\x2e\x00\x70\x00\x6e\x00\x67\
+\x00\x11\
+\x0c\x49\x94\x07\
+\x00\x61\
+\x00\x64\x00\x42\x00\x6c\x00\x6f\x00\x63\x00\x6b\x00\x50\x00\x6c\x00\x75\x00\x73\x00\x36\x00\x34\x00\x2e\x00\x70\x00\x6e\x00\x67\
+\
+\x00\x08\
+\x0b\x07\x5a\x27\
+\x00\x65\
+\x00\x64\x00\x69\x00\x74\x00\x2e\x00\x70\x00\x6e\x00\x67\
+\x00\x09\
+\x06\x98\x83\x27\
+\x00\x63\
+\x00\x6c\x00\x6f\x00\x73\x00\x65\x00\x2e\x00\x70\x00\x6e\x00\x67\
+\x00\x0b\
+\x00\xb0\xe2\x96\
+\x00\x6c\
+\x00\x6f\x00\x61\x00\x64\x00\x69\x00\x6e\x00\x67\x00\x2e\x00\x67\x00\x69\x00\x66\
+\x00\x0a\
+\x05\x78\x4f\x27\
+\x00\x72\
+\x00\x65\x00\x6c\x00\x6f\x00\x61\x00\x64\x00\x2e\x00\x70\x00\x6e\x00\x67\
+\x00\x14\
+\x0b\x6a\x55\xa7\
+\x00\x62\
+\x00\x6f\x00\x78\x00\x2d\x00\x62\x00\x6f\x00\x72\x00\x64\x00\x65\x00\x72\x00\x2d\x00\x73\x00\x6d\x00\x61\x00\x6c\x00\x6c\x00\x2e\
+\x00\x70\x00\x6e\x00\x67\
+\x00\x0e\
+\x05\x17\xcb\xe7\
+\x00\x62\
+\x00\x72\x00\x6f\x00\x6b\x00\x65\x00\x6e\x00\x50\x00\x61\x00\x67\x00\x65\x00\x2e\x00\x70\x00\x6e\x00\x67\
+\x00\x0b\
+\x00\xbd\xc0\x27\
+\x00\x73\
+\x00\x65\x00\x74\x00\x74\x00\x69\x00\x6e\x00\x67\x00\x2e\x00\x70\x00\x6e\x00\x67\
+"
+
+qt_resource_struct = b"\
+\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
+\x00\x00\x00\x00\x00\x02\x00\x00\x00\x0c\x00\x00\x00\x02\
+\x00\x00\x00\xe4\x00\x00\x00\x00\x00\x01\x00\x00\x3c\x75\
+\x00\x00\x01\x6a\x00\x00\x00\x00\x00\x01\x00\x00\x72\xfd\
+\x00\x00\x00\x30\x00\x00\x00\x00\x00\x01\x00\x00\x0c\x6d\
+\x00\x00\x01\x48\x00\x00\x00\x00\x00\x01\x00\x00\x52\x48\
+\x00\x00\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x43\xfd\
+\x00\x00\x00\xcc\x00\x00\x00\x00\x00\x01\x00\x00\x3a\x27\
+\x00\x00\x00\xb6\x00\x00\x00\x00\x00\x01\x00\x00\x37\xb0\
+\x00\x00\x01\x1a\x00\x00\x00\x00\x00\x01\x00\x00\x47\x17\
+\x00\x00\x00\x8e\x00\x00\x00\x00\x00\x01\x00\x00\x17\x86\
+\x00\x00\x00\x6e\x00\x00\x00\x00\x00\x01\x00\x00\x13\x56\
+\x00\x00\x00\x10\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
+\x00\x00\x00\x46\x00\x00\x00\x00\x00\x01\x00\x00\x10\x22\
+"
+
+def qInitResources():
+    QtCore.qRegisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data)
+
+def qCleanupResources():
+    QtCore.qUnregisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data)
+
+qInitResources()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/data/javascript.qrc	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,8 @@
+<!DOCTYPE RCC>
+<RCC version="1.0">
+<qresource>
+  <file>javascript/jquery-ui.js</file>
+  <file>javascript/jquery.js</file>
+  <file>javascript/qwebchannel.js</file>
+</qresource>
+</RCC>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/data/javascript/jquery-ui.js	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,111 @@
+/*!
+ * jQuery UI 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI
+ */
+(function(c,j){function k(a,b){var d=a.nodeName.toLowerCase();if("area"===d){b=a.parentNode;d=b.name;if(!a.href||!d||b.nodeName.toLowerCase()!=="map")return false;a=c("img[usemap=#"+d+"]")[0];return!!a&&l(a)}return(/input|select|textarea|button|object/.test(d)?!a.disabled:"a"==d?a.href||b:b)&&l(a)}function l(a){return!c(a).parents().andSelf().filter(function(){return c.curCSS(this,"visibility")==="hidden"||c.expr.filters.hidden(this)}).length}c.ui=c.ui||{};if(!c.ui.version){c.extend(c.ui,{version:"1.8.16",
+keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,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,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91}});c.fn.extend({propAttr:c.fn.prop||c.fn.attr,_focus:c.fn.focus,focus:function(a,b){return typeof a==="number"?this.each(function(){var d=
+this;setTimeout(function(){c(d).focus();b&&b.call(d)},a)}):this._focus.apply(this,arguments)},scrollParent:function(){var a;a=c.browser.msie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?this.parents().filter(function(){return/(relative|absolute|fixed)/.test(c.curCSS(this,"position",1))&&/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0):this.parents().filter(function(){return/(auto|scroll)/.test(c.curCSS(this,
+"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0);return/fixed/.test(this.css("position"))||!a.length?c(document):a},zIndex:function(a){if(a!==j)return this.css("zIndex",a);if(this.length){a=c(this[0]);for(var b;a.length&&a[0]!==document;){b=a.css("position");if(b==="absolute"||b==="relative"||b==="fixed"){b=parseInt(a.css("zIndex"),10);if(!isNaN(b)&&b!==0)return b}a=a.parent()}}return 0},disableSelection:function(){return this.bind((c.support.selectstart?"selectstart":
+"mousedown")+".ui-disableSelection",function(a){a.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}});c.each(["Width","Height"],function(a,b){function d(f,g,m,n){c.each(e,function(){g-=parseFloat(c.curCSS(f,"padding"+this,true))||0;if(m)g-=parseFloat(c.curCSS(f,"border"+this+"Width",true))||0;if(n)g-=parseFloat(c.curCSS(f,"margin"+this,true))||0});return g}var e=b==="Width"?["Left","Right"]:["Top","Bottom"],h=b.toLowerCase(),i={innerWidth:c.fn.innerWidth,innerHeight:c.fn.innerHeight,
+outerWidth:c.fn.outerWidth,outerHeight:c.fn.outerHeight};c.fn["inner"+b]=function(f){if(f===j)return i["inner"+b].call(this);return this.each(function(){c(this).css(h,d(this,f)+"px")})};c.fn["outer"+b]=function(f,g){if(typeof f!=="number")return i["outer"+b].call(this,f);return this.each(function(){c(this).css(h,d(this,f,true,g)+"px")})}});c.extend(c.expr[":"],{data:function(a,b,d){return!!c.data(a,d[3])},focusable:function(a){return k(a,!isNaN(c.attr(a,"tabindex")))},tabbable:function(a){var b=c.attr(a,
+"tabindex"),d=isNaN(b);return(d||b>=0)&&k(a,!d)}});c(function(){var a=document.body,b=a.appendChild(b=document.createElement("div"));c.extend(b.style,{minHeight:"100px",height:"auto",padding:0,borderWidth:0});c.support.minHeight=b.offsetHeight===100;c.support.selectstart="onselectstart"in b;a.removeChild(b).style.display="none"});c.extend(c.ui,{plugin:{add:function(a,b,d){a=c.ui[a].prototype;for(var e in d){a.plugins[e]=a.plugins[e]||[];a.plugins[e].push([b,d[e]])}},call:function(a,b,d){if((b=a.plugins[b])&&
+a.element[0].parentNode)for(var e=0;e<b.length;e++)a.options[b[e][0]]&&b[e][1].apply(a.element,d)}},contains:function(a,b){return document.compareDocumentPosition?a.compareDocumentPosition(b)&16:a!==b&&a.contains(b)},hasScroll:function(a,b){if(c(a).css("overflow")==="hidden")return false;b=b&&b==="left"?"scrollLeft":"scrollTop";var d=false;if(a[b]>0)return true;a[b]=1;d=a[b]>0;a[b]=0;return d},isOverAxis:function(a,b,d){return a>b&&a<b+d},isOver:function(a,b,d,e,h,i){return c.ui.isOverAxis(a,d,h)&&
+c.ui.isOverAxis(b,e,i)}})}})(jQuery);
+;/*!
+ * jQuery UI Widget 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Widget
+ */
+(function(b,j){if(b.cleanData){var k=b.cleanData;b.cleanData=function(a){for(var c=0,d;(d=a[c])!=null;c++)try{b(d).triggerHandler("remove")}catch(e){}k(a)}}else{var l=b.fn.remove;b.fn.remove=function(a,c){return this.each(function(){if(!c)if(!a||b.filter(a,[this]).length)b("*",this).add([this]).each(function(){try{b(this).triggerHandler("remove")}catch(d){}});return l.call(b(this),a,c)})}}b.widget=function(a,c,d){var e=a.split(".")[0],f;a=a.split(".")[1];f=e+"-"+a;if(!d){d=c;c=b.Widget}b.expr[":"][f]=
+function(h){return!!b.data(h,a)};b[e]=b[e]||{};b[e][a]=function(h,g){arguments.length&&this._createWidget(h,g)};c=new c;c.options=b.extend(true,{},c.options);b[e][a].prototype=b.extend(true,c,{namespace:e,widgetName:a,widgetEventPrefix:b[e][a].prototype.widgetEventPrefix||a,widgetBaseClass:f},d);b.widget.bridge(a,b[e][a])};b.widget.bridge=function(a,c){b.fn[a]=function(d){var e=typeof d==="string",f=Array.prototype.slice.call(arguments,1),h=this;d=!e&&f.length?b.extend.apply(null,[true,d].concat(f)):
+d;if(e&&d.charAt(0)==="_")return h;e?this.each(function(){var g=b.data(this,a),i=g&&b.isFunction(g[d])?g[d].apply(g,f):g;if(i!==g&&i!==j){h=i;return false}}):this.each(function(){var g=b.data(this,a);g?g.option(d||{})._init():b.data(this,a,new c(d,this))});return h}};b.Widget=function(a,c){arguments.length&&this._createWidget(a,c)};b.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",options:{disabled:false},_createWidget:function(a,c){b.data(c,this.widgetName,this);this.element=b(c);this.options=
+b.extend(true,{},this.options,this._getCreateOptions(),a);var d=this;this.element.bind("remove."+this.widgetName,function(){d.destroy()});this._create();this._trigger("create");this._init()},_getCreateOptions:function(){return b.metadata&&b.metadata.get(this.element[0])[this.widgetName]},_create:function(){},_init:function(){},destroy:function(){this.element.unbind("."+this.widgetName).removeData(this.widgetName);this.widget().unbind("."+this.widgetName).removeAttr("aria-disabled").removeClass(this.widgetBaseClass+
+"-disabled ui-state-disabled")},widget:function(){return this.element},option:function(a,c){var d=a;if(arguments.length===0)return b.extend({},this.options);if(typeof a==="string"){if(c===j)return this.options[a];d={};d[a]=c}this._setOptions(d);return this},_setOptions:function(a){var c=this;b.each(a,function(d,e){c._setOption(d,e)});return this},_setOption:function(a,c){this.options[a]=c;if(a==="disabled")this.widget()[c?"addClass":"removeClass"](this.widgetBaseClass+"-disabled ui-state-disabled").attr("aria-disabled",
+c);return this},enable:function(){return this._setOption("disabled",false)},disable:function(){return this._setOption("disabled",true)},_trigger:function(a,c,d){var e=this.options[a];c=b.Event(c);c.type=(a===this.widgetEventPrefix?a:this.widgetEventPrefix+a).toLowerCase();d=d||{};if(c.originalEvent){a=b.event.props.length;for(var f;a;){f=b.event.props[--a];c[f]=c.originalEvent[f]}}this.element.trigger(c,d);return!(b.isFunction(e)&&e.call(this.element[0],c,d)===false||c.isDefaultPrevented())}}})(jQuery);
+;/*!
+ * jQuery UI Mouse 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Mouse
+ *
+ * Depends:
+ *	jquery.ui.widget.js
+ */
+(function(b){var d=false;b(document).mouseup(function(){d=false});b.widget("ui.mouse",{options:{cancel:":input,option",distance:1,delay:0},_mouseInit:function(){var a=this;this.element.bind("mousedown."+this.widgetName,function(c){return a._mouseDown(c)}).bind("click."+this.widgetName,function(c){if(true===b.data(c.target,a.widgetName+".preventClickEvent")){b.removeData(c.target,a.widgetName+".preventClickEvent");c.stopImmediatePropagation();return false}});this.started=false},_mouseDestroy:function(){this.element.unbind("."+
+this.widgetName)},_mouseDown:function(a){if(!d){this._mouseStarted&&this._mouseUp(a);this._mouseDownEvent=a;var c=this,f=a.which==1,g=typeof this.options.cancel=="string"&&a.target.nodeName?b(a.target).closest(this.options.cancel).length:false;if(!f||g||!this._mouseCapture(a))return true;this.mouseDelayMet=!this.options.delay;if(!this.mouseDelayMet)this._mouseDelayTimer=setTimeout(function(){c.mouseDelayMet=true},this.options.delay);if(this._mouseDistanceMet(a)&&this._mouseDelayMet(a)){this._mouseStarted=
+this._mouseStart(a)!==false;if(!this._mouseStarted){a.preventDefault();return true}}true===b.data(a.target,this.widgetName+".preventClickEvent")&&b.removeData(a.target,this.widgetName+".preventClickEvent");this._mouseMoveDelegate=function(e){return c._mouseMove(e)};this._mouseUpDelegate=function(e){return c._mouseUp(e)};b(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate);a.preventDefault();return d=true}},_mouseMove:function(a){if(b.browser.msie&&
+!(document.documentMode>=9)&&!a.button)return this._mouseUp(a);if(this._mouseStarted){this._mouseDrag(a);return a.preventDefault()}if(this._mouseDistanceMet(a)&&this._mouseDelayMet(a))(this._mouseStarted=this._mouseStart(this._mouseDownEvent,a)!==false)?this._mouseDrag(a):this._mouseUp(a);return!this._mouseStarted},_mouseUp:function(a){b(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate);if(this._mouseStarted){this._mouseStarted=
+false;a.target==this._mouseDownEvent.target&&b.data(a.target,this.widgetName+".preventClickEvent",true);this._mouseStop(a)}return false},_mouseDistanceMet:function(a){return Math.max(Math.abs(this._mouseDownEvent.pageX-a.pageX),Math.abs(this._mouseDownEvent.pageY-a.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return true}})})(jQuery);
+;/*
+ * jQuery UI Sortable 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Sortables
+ *
+ * Depends:
+ *	jquery.ui.core.js
+ *	jquery.ui.mouse.js
+ *	jquery.ui.widget.js
+ */
+(function(d){d.widget("ui.sortable",d.ui.mouse,{widgetEventPrefix:"sort",options:{appendTo:"parent",axis:false,connectWith:false,containment:false,cursor:"auto",cursorAt:false,dropOnEmpty:true,forcePlaceholderSize:false,forceHelperSize:false,grid:false,handle:false,helper:"original",items:"> *",opacity:false,placeholder:false,revert:false,scroll:true,scrollSensitivity:20,scrollSpeed:20,scope:"default",tolerance:"intersect",zIndex:1E3},_create:function(){var a=this.options;this.containerCache={};this.element.addClass("ui-sortable");
+this.refresh();this.floating=this.items.length?a.axis==="x"||/left|right/.test(this.items[0].item.css("float"))||/inline|table-cell/.test(this.items[0].item.css("display")):false;this.offset=this.element.offset();this._mouseInit()},destroy:function(){this.element.removeClass("ui-sortable ui-sortable-disabled").removeData("sortable").unbind(".sortable");this._mouseDestroy();for(var a=this.items.length-1;a>=0;a--)this.items[a].item.removeData("sortable-item");return this},_setOption:function(a,b){if(a===
+"disabled"){this.options[a]=b;this.widget()[b?"addClass":"removeClass"]("ui-sortable-disabled")}else d.Widget.prototype._setOption.apply(this,arguments)},_mouseCapture:function(a,b){if(this.reverting)return false;if(this.options.disabled||this.options.type=="static")return false;this._refreshItems(a);var c=null,e=this;d(a.target).parents().each(function(){if(d.data(this,"sortable-item")==e){c=d(this);return false}});if(d.data(a.target,"sortable-item")==e)c=d(a.target);if(!c)return false;if(this.options.handle&&
+!b){var f=false;d(this.options.handle,c).find("*").andSelf().each(function(){if(this==a.target)f=true});if(!f)return false}this.currentItem=c;this._removeCurrentsFromItems();return true},_mouseStart:function(a,b,c){b=this.options;var e=this;this.currentContainer=this;this.refreshPositions();this.helper=this._createHelper(a);this._cacheHelperProportions();this._cacheMargins();this.scrollParent=this.helper.scrollParent();this.offset=this.currentItem.offset();this.offset={top:this.offset.top-this.margins.top,
+left:this.offset.left-this.margins.left};this.helper.css("position","absolute");this.cssPosition=this.helper.css("position");d.extend(this.offset,{click:{left:a.pageX-this.offset.left,top:a.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()});this.originalPosition=this._generatePosition(a);this.originalPageX=a.pageX;this.originalPageY=a.pageY;b.cursorAt&&this._adjustOffsetFromHelper(b.cursorAt);this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]};
+this.helper[0]!=this.currentItem[0]&&this.currentItem.hide();this._createPlaceholder();b.containment&&this._setContainment();if(b.cursor){if(d("body").css("cursor"))this._storedCursor=d("body").css("cursor");d("body").css("cursor",b.cursor)}if(b.opacity){if(this.helper.css("opacity"))this._storedOpacity=this.helper.css("opacity");this.helper.css("opacity",b.opacity)}if(b.zIndex){if(this.helper.css("zIndex"))this._storedZIndex=this.helper.css("zIndex");this.helper.css("zIndex",b.zIndex)}if(this.scrollParent[0]!=
+document&&this.scrollParent[0].tagName!="HTML")this.overflowOffset=this.scrollParent.offset();this._trigger("start",a,this._uiHash());this._preserveHelperProportions||this._cacheHelperProportions();if(!c)for(c=this.containers.length-1;c>=0;c--)this.containers[c]._trigger("activate",a,e._uiHash(this));if(d.ui.ddmanager)d.ui.ddmanager.current=this;d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this,a);this.dragging=true;this.helper.addClass("ui-sortable-helper");this._mouseDrag(a);
+return true},_mouseDrag:function(a){this.position=this._generatePosition(a);this.positionAbs=this._convertPositionTo("absolute");if(!this.lastPositionAbs)this.lastPositionAbs=this.positionAbs;if(this.options.scroll){var b=this.options,c=false;if(this.scrollParent[0]!=document&&this.scrollParent[0].tagName!="HTML"){if(this.overflowOffset.top+this.scrollParent[0].offsetHeight-a.pageY<b.scrollSensitivity)this.scrollParent[0].scrollTop=c=this.scrollParent[0].scrollTop+b.scrollSpeed;else if(a.pageY-this.overflowOffset.top<
+b.scrollSensitivity)this.scrollParent[0].scrollTop=c=this.scrollParent[0].scrollTop-b.scrollSpeed;if(this.overflowOffset.left+this.scrollParent[0].offsetWidth-a.pageX<b.scrollSensitivity)this.scrollParent[0].scrollLeft=c=this.scrollParent[0].scrollLeft+b.scrollSpeed;else if(a.pageX-this.overflowOffset.left<b.scrollSensitivity)this.scrollParent[0].scrollLeft=c=this.scrollParent[0].scrollLeft-b.scrollSpeed}else{if(a.pageY-d(document).scrollTop()<b.scrollSensitivity)c=d(document).scrollTop(d(document).scrollTop()-
+b.scrollSpeed);else if(d(window).height()-(a.pageY-d(document).scrollTop())<b.scrollSensitivity)c=d(document).scrollTop(d(document).scrollTop()+b.scrollSpeed);if(a.pageX-d(document).scrollLeft()<b.scrollSensitivity)c=d(document).scrollLeft(d(document).scrollLeft()-b.scrollSpeed);else if(d(window).width()-(a.pageX-d(document).scrollLeft())<b.scrollSensitivity)c=d(document).scrollLeft(d(document).scrollLeft()+b.scrollSpeed)}c!==false&&d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this,
+a)}this.positionAbs=this._convertPositionTo("absolute");if(!this.options.axis||this.options.axis!="y")this.helper[0].style.left=this.position.left+"px";if(!this.options.axis||this.options.axis!="x")this.helper[0].style.top=this.position.top+"px";for(b=this.items.length-1;b>=0;b--){c=this.items[b];var e=c.item[0],f=this._intersectsWithPointer(c);if(f)if(e!=this.currentItem[0]&&this.placeholder[f==1?"next":"prev"]()[0]!=e&&!d.ui.contains(this.placeholder[0],e)&&(this.options.type=="semi-dynamic"?!d.ui.contains(this.element[0],
+e):true)){this.direction=f==1?"down":"up";if(this.options.tolerance=="pointer"||this._intersectsWithSides(c))this._rearrange(a,c);else break;this._trigger("change",a,this._uiHash());break}}this._contactContainers(a);d.ui.ddmanager&&d.ui.ddmanager.drag(this,a);this._trigger("sort",a,this._uiHash());this.lastPositionAbs=this.positionAbs;return false},_mouseStop:function(a,b){if(a){d.ui.ddmanager&&!this.options.dropBehaviour&&d.ui.ddmanager.drop(this,a);if(this.options.revert){var c=this;b=c.placeholder.offset();
+c.reverting=true;d(this.helper).animate({left:b.left-this.offset.parent.left-c.margins.left+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollLeft),top:b.top-this.offset.parent.top-c.margins.top+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollTop)},parseInt(this.options.revert,10)||500,function(){c._clear(a)})}else this._clear(a,b);return false}},cancel:function(){var a=this;if(this.dragging){this._mouseUp({target:null});this.options.helper=="original"?this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"):
+this.currentItem.show();for(var b=this.containers.length-1;b>=0;b--){this.containers[b]._trigger("deactivate",null,a._uiHash(this));if(this.containers[b].containerCache.over){this.containers[b]._trigger("out",null,a._uiHash(this));this.containers[b].containerCache.over=0}}}if(this.placeholder){this.placeholder[0].parentNode&&this.placeholder[0].parentNode.removeChild(this.placeholder[0]);this.options.helper!="original"&&this.helper&&this.helper[0].parentNode&&this.helper.remove();d.extend(this,{helper:null,
+dragging:false,reverting:false,_noFinalSort:null});this.domPosition.prev?d(this.domPosition.prev).after(this.currentItem):d(this.domPosition.parent).prepend(this.currentItem)}return this},serialize:function(a){var b=this._getItemsAsjQuery(a&&a.connected),c=[];a=a||{};d(b).each(function(){var e=(d(a.item||this).attr(a.attribute||"id")||"").match(a.expression||/(.+)[-=_](.+)/);if(e)c.push((a.key||e[1]+"[]")+"="+(a.key&&a.expression?e[1]:e[2]))});!c.length&&a.key&&c.push(a.key+"=");return c.join("&")},
+toArray:function(a){var b=this._getItemsAsjQuery(a&&a.connected),c=[];a=a||{};b.each(function(){c.push(d(a.item||this).attr(a.attribute||"id")||"")});return c},_intersectsWith:function(a){var b=this.positionAbs.left,c=b+this.helperProportions.width,e=this.positionAbs.top,f=e+this.helperProportions.height,g=a.left,h=g+a.width,i=a.top,k=i+a.height,j=this.offset.click.top,l=this.offset.click.left;j=e+j>i&&e+j<k&&b+l>g&&b+l<h;return this.options.tolerance=="pointer"||this.options.forcePointerForContainers||
+this.options.tolerance!="pointer"&&this.helperProportions[this.floating?"width":"height"]>a[this.floating?"width":"height"]?j:g<b+this.helperProportions.width/2&&c-this.helperProportions.width/2<h&&i<e+this.helperProportions.height/2&&f-this.helperProportions.height/2<k},_intersectsWithPointer:function(a){var b=d.ui.isOverAxis(this.positionAbs.top+this.offset.click.top,a.top,a.height);a=d.ui.isOverAxis(this.positionAbs.left+this.offset.click.left,a.left,a.width);b=b&&a;a=this._getDragVerticalDirection();
+var c=this._getDragHorizontalDirection();if(!b)return false;return this.floating?c&&c=="right"||a=="down"?2:1:a&&(a=="down"?2:1)},_intersectsWithSides:function(a){var b=d.ui.isOverAxis(this.positionAbs.top+this.offset.click.top,a.top+a.height/2,a.height);a=d.ui.isOverAxis(this.positionAbs.left+this.offset.click.left,a.left+a.width/2,a.width);var c=this._getDragVerticalDirection(),e=this._getDragHorizontalDirection();return this.floating&&e?e=="right"&&a||e=="left"&&!a:c&&(c=="down"&&b||c=="up"&&!b)},
+_getDragVerticalDirection:function(){var a=this.positionAbs.top-this.lastPositionAbs.top;return a!=0&&(a>0?"down":"up")},_getDragHorizontalDirection:function(){var a=this.positionAbs.left-this.lastPositionAbs.left;return a!=0&&(a>0?"right":"left")},refresh:function(a){this._refreshItems(a);this.refreshPositions();return this},_connectWith:function(){var a=this.options;return a.connectWith.constructor==String?[a.connectWith]:a.connectWith},_getItemsAsjQuery:function(a){var b=[],c=[],e=this._connectWith();
+if(e&&a)for(a=e.length-1;a>=0;a--)for(var f=d(e[a]),g=f.length-1;g>=0;g--){var h=d.data(f[g],"sortable");if(h&&h!=this&&!h.options.disabled)c.push([d.isFunction(h.options.items)?h.options.items.call(h.element):d(h.options.items,h.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),h])}c.push([d.isFunction(this.options.items)?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):d(this.options.items,this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),
+this]);for(a=c.length-1;a>=0;a--)c[a][0].each(function(){b.push(this)});return d(b)},_removeCurrentsFromItems:function(){for(var a=this.currentItem.find(":data(sortable-item)"),b=0;b<this.items.length;b++)for(var c=0;c<a.length;c++)a[c]==this.items[b].item[0]&&this.items.splice(b,1)},_refreshItems:function(a){this.items=[];this.containers=[this];var b=this.items,c=[[d.isFunction(this.options.items)?this.options.items.call(this.element[0],a,{item:this.currentItem}):d(this.options.items,this.element),
+this]],e=this._connectWith();if(e)for(var f=e.length-1;f>=0;f--)for(var g=d(e[f]),h=g.length-1;h>=0;h--){var i=d.data(g[h],"sortable");if(i&&i!=this&&!i.options.disabled){c.push([d.isFunction(i.options.items)?i.options.items.call(i.element[0],a,{item:this.currentItem}):d(i.options.items,i.element),i]);this.containers.push(i)}}for(f=c.length-1;f>=0;f--){a=c[f][1];e=c[f][0];h=0;for(g=e.length;h<g;h++){i=d(e[h]);i.data("sortable-item",a);b.push({item:i,instance:a,width:0,height:0,left:0,top:0})}}},refreshPositions:function(a){if(this.offsetParent&&
+this.helper)this.offset.parent=this._getParentOffset();for(var b=this.items.length-1;b>=0;b--){var c=this.items[b];if(!(c.instance!=this.currentContainer&&this.currentContainer&&c.item[0]!=this.currentItem[0])){var e=this.options.toleranceElement?d(this.options.toleranceElement,c.item):c.item;if(!a){c.width=e.outerWidth();c.height=e.outerHeight()}e=e.offset();c.left=e.left;c.top=e.top}}if(this.options.custom&&this.options.custom.refreshContainers)this.options.custom.refreshContainers.call(this);else for(b=
+this.containers.length-1;b>=0;b--){e=this.containers[b].element.offset();this.containers[b].containerCache.left=e.left;this.containers[b].containerCache.top=e.top;this.containers[b].containerCache.width=this.containers[b].element.outerWidth();this.containers[b].containerCache.height=this.containers[b].element.outerHeight()}return this},_createPlaceholder:function(a){var b=a||this,c=b.options;if(!c.placeholder||c.placeholder.constructor==String){var e=c.placeholder;c.placeholder={element:function(){var f=
+d(document.createElement(b.currentItem[0].nodeName)).addClass(e||b.currentItem[0].className+" ui-sortable-placeholder").removeClass("ui-sortable-helper")[0];if(!e)f.style.visibility="hidden";return f},update:function(f,g){if(!(e&&!c.forcePlaceholderSize)){g.height()||g.height(b.currentItem.innerHeight()-parseInt(b.currentItem.css("paddingTop")||0,10)-parseInt(b.currentItem.css("paddingBottom")||0,10));g.width()||g.width(b.currentItem.innerWidth()-parseInt(b.currentItem.css("paddingLeft")||0,10)-parseInt(b.currentItem.css("paddingRight")||
+0,10))}}}}b.placeholder=d(c.placeholder.element.call(b.element,b.currentItem));b.currentItem.after(b.placeholder);c.placeholder.update(b,b.placeholder)},_contactContainers:function(a){for(var b=null,c=null,e=this.containers.length-1;e>=0;e--)if(!d.ui.contains(this.currentItem[0],this.containers[e].element[0]))if(this._intersectsWith(this.containers[e].containerCache)){if(!(b&&d.ui.contains(this.containers[e].element[0],b.element[0]))){b=this.containers[e];c=e}}else if(this.containers[e].containerCache.over){this.containers[e]._trigger("out",
+a,this._uiHash(this));this.containers[e].containerCache.over=0}if(b)if(this.containers.length===1){this.containers[c]._trigger("over",a,this._uiHash(this));this.containers[c].containerCache.over=1}else if(this.currentContainer!=this.containers[c]){b=1E4;e=null;for(var f=this.positionAbs[this.containers[c].floating?"left":"top"],g=this.items.length-1;g>=0;g--)if(d.ui.contains(this.containers[c].element[0],this.items[g].item[0])){var h=this.items[g][this.containers[c].floating?"left":"top"];if(Math.abs(h-
+f)<b){b=Math.abs(h-f);e=this.items[g]}}if(e||this.options.dropOnEmpty){this.currentContainer=this.containers[c];e?this._rearrange(a,e,null,true):this._rearrange(a,null,this.containers[c].element,true);this._trigger("change",a,this._uiHash());this.containers[c]._trigger("change",a,this._uiHash(this));this.options.placeholder.update(this.currentContainer,this.placeholder);this.containers[c]._trigger("over",a,this._uiHash(this));this.containers[c].containerCache.over=1}}},_createHelper:function(a){var b=
+this.options;a=d.isFunction(b.helper)?d(b.helper.apply(this.element[0],[a,this.currentItem])):b.helper=="clone"?this.currentItem.clone():this.currentItem;a.parents("body").length||d(b.appendTo!="parent"?b.appendTo:this.currentItem[0].parentNode)[0].appendChild(a[0]);if(a[0]==this.currentItem[0])this._storedCSS={width:this.currentItem[0].style.width,height:this.currentItem[0].style.height,position:this.currentItem.css("position"),top:this.currentItem.css("top"),left:this.currentItem.css("left")};if(a[0].style.width==
+""||b.forceHelperSize)a.width(this.currentItem.width());if(a[0].style.height==""||b.forceHelperSize)a.height(this.currentItem.height());return a},_adjustOffsetFromHelper:function(a){if(typeof a=="string")a=a.split(" ");if(d.isArray(a))a={left:+a[0],top:+a[1]||0};if("left"in a)this.offset.click.left=a.left+this.margins.left;if("right"in a)this.offset.click.left=this.helperProportions.width-a.right+this.margins.left;if("top"in a)this.offset.click.top=a.top+this.margins.top;if("bottom"in a)this.offset.click.top=
+this.helperProportions.height-a.bottom+this.margins.top},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var a=this.offsetParent.offset();if(this.cssPosition=="absolute"&&this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0])){a.left+=this.scrollParent.scrollLeft();a.top+=this.scrollParent.scrollTop()}if(this.offsetParent[0]==document.body||this.offsetParent[0].tagName&&this.offsetParent[0].tagName.toLowerCase()=="html"&&d.browser.msie)a=
+{top:0,left:0};return{top:a.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:a.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if(this.cssPosition=="relative"){var a=this.currentItem.position();return{top:a.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:a.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}else return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.currentItem.css("marginLeft"),
+10)||0,top:parseInt(this.currentItem.css("marginTop"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var a=this.options;if(a.containment=="parent")a.containment=this.helper[0].parentNode;if(a.containment=="document"||a.containment=="window")this.containment=[0-this.offset.relative.left-this.offset.parent.left,0-this.offset.relative.top-this.offset.parent.top,d(a.containment=="document"?
+document:window).width()-this.helperProportions.width-this.margins.left,(d(a.containment=="document"?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top];if(!/^(document|window|parent)$/.test(a.containment)){var b=d(a.containment)[0];a=d(a.containment).offset();var c=d(b).css("overflow")!="hidden";this.containment=[a.left+(parseInt(d(b).css("borderLeftWidth"),10)||0)+(parseInt(d(b).css("paddingLeft"),10)||0)-this.margins.left,a.top+(parseInt(d(b).css("borderTopWidth"),
+10)||0)+(parseInt(d(b).css("paddingTop"),10)||0)-this.margins.top,a.left+(c?Math.max(b.scrollWidth,b.offsetWidth):b.offsetWidth)-(parseInt(d(b).css("borderLeftWidth"),10)||0)-(parseInt(d(b).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left,a.top+(c?Math.max(b.scrollHeight,b.offsetHeight):b.offsetHeight)-(parseInt(d(b).css("borderTopWidth"),10)||0)-(parseInt(d(b).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top]}},_convertPositionTo:function(a,b){if(!b)b=
+this.position;a=a=="absolute"?1:-1;var c=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,e=/(html|body)/i.test(c[0].tagName);return{top:b.top+this.offset.relative.top*a+this.offset.parent.top*a-(d.browser.safari&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():e?0:c.scrollTop())*a),left:b.left+this.offset.relative.left*a+this.offset.parent.left*a-(d.browser.safari&&
+this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():e?0:c.scrollLeft())*a)}},_generatePosition:function(a){var b=this.options,c=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,e=/(html|body)/i.test(c[0].tagName);if(this.cssPosition=="relative"&&!(this.scrollParent[0]!=document&&this.scrollParent[0]!=this.offsetParent[0]))this.offset.relative=this._getRelativeOffset();
+var f=a.pageX,g=a.pageY;if(this.originalPosition){if(this.containment){if(a.pageX-this.offset.click.left<this.containment[0])f=this.containment[0]+this.offset.click.left;if(a.pageY-this.offset.click.top<this.containment[1])g=this.containment[1]+this.offset.click.top;if(a.pageX-this.offset.click.left>this.containment[2])f=this.containment[2]+this.offset.click.left;if(a.pageY-this.offset.click.top>this.containment[3])g=this.containment[3]+this.offset.click.top}if(b.grid){g=this.originalPageY+Math.round((g-
+this.originalPageY)/b.grid[1])*b.grid[1];g=this.containment?!(g-this.offset.click.top<this.containment[1]||g-this.offset.click.top>this.containment[3])?g:!(g-this.offset.click.top<this.containment[1])?g-b.grid[1]:g+b.grid[1]:g;f=this.originalPageX+Math.round((f-this.originalPageX)/b.grid[0])*b.grid[0];f=this.containment?!(f-this.offset.click.left<this.containment[0]||f-this.offset.click.left>this.containment[2])?f:!(f-this.offset.click.left<this.containment[0])?f-b.grid[0]:f+b.grid[0]:f}}return{top:g-
+this.offset.click.top-this.offset.relative.top-this.offset.parent.top+(d.browser.safari&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollTop():e?0:c.scrollTop()),left:f-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+(d.browser.safari&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():e?0:c.scrollLeft())}},_rearrange:function(a,b,c,e){c?c[0].appendChild(this.placeholder[0]):b.item[0].parentNode.insertBefore(this.placeholder[0],
+this.direction=="down"?b.item[0]:b.item[0].nextSibling);this.counter=this.counter?++this.counter:1;var f=this,g=this.counter;window.setTimeout(function(){g==f.counter&&f.refreshPositions(!e)},0)},_clear:function(a,b){this.reverting=false;var c=[];!this._noFinalSort&&this.currentItem.parent().length&&this.placeholder.before(this.currentItem);this._noFinalSort=null;if(this.helper[0]==this.currentItem[0]){for(var e in this._storedCSS)if(this._storedCSS[e]=="auto"||this._storedCSS[e]=="static")this._storedCSS[e]=
+"";this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper")}else this.currentItem.show();this.fromOutside&&!b&&c.push(function(f){this._trigger("receive",f,this._uiHash(this.fromOutside))});if((this.fromOutside||this.domPosition.prev!=this.currentItem.prev().not(".ui-sortable-helper")[0]||this.domPosition.parent!=this.currentItem.parent()[0])&&!b)c.push(function(f){this._trigger("update",f,this._uiHash())});if(!d.ui.contains(this.element[0],this.currentItem[0])){b||c.push(function(f){this._trigger("remove",
+f,this._uiHash())});for(e=this.containers.length-1;e>=0;e--)if(d.ui.contains(this.containers[e].element[0],this.currentItem[0])&&!b){c.push(function(f){return function(g){f._trigger("receive",g,this._uiHash(this))}}.call(this,this.containers[e]));c.push(function(f){return function(g){f._trigger("update",g,this._uiHash(this))}}.call(this,this.containers[e]))}}for(e=this.containers.length-1;e>=0;e--){b||c.push(function(f){return function(g){f._trigger("deactivate",g,this._uiHash(this))}}.call(this,
+this.containers[e]));if(this.containers[e].containerCache.over){c.push(function(f){return function(g){f._trigger("out",g,this._uiHash(this))}}.call(this,this.containers[e]));this.containers[e].containerCache.over=0}}this._storedCursor&&d("body").css("cursor",this._storedCursor);this._storedOpacity&&this.helper.css("opacity",this._storedOpacity);if(this._storedZIndex)this.helper.css("zIndex",this._storedZIndex=="auto"?"":this._storedZIndex);this.dragging=false;if(this.cancelHelperRemoval){if(!b){this._trigger("beforeStop",
+a,this._uiHash());for(e=0;e<c.length;e++)c[e].call(this,a);this._trigger("stop",a,this._uiHash())}return false}b||this._trigger("beforeStop",a,this._uiHash());this.placeholder[0].parentNode.removeChild(this.placeholder[0]);this.helper[0]!=this.currentItem[0]&&this.helper.remove();this.helper=null;if(!b){for(e=0;e<c.length;e++)c[e].call(this,a);this._trigger("stop",a,this._uiHash())}this.fromOutside=false;return true},_trigger:function(){d.Widget.prototype._trigger.apply(this,arguments)===false&&this.cancel()},
+_uiHash:function(a){var b=a||this;return{helper:b.helper,placeholder:b.placeholder||d([]),position:b.position,originalPosition:b.originalPosition,offset:b.positionAbs,item:b.currentItem,sender:a?a.element:null}}});d.extend(d.ui.sortable,{version:"1.8.16"})})(jQuery);
+;
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/data/javascript/jquery.js	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,4 @@
+/*! jQuery v1.7.1 jquery.com | jquery.org/license */
+(function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cv(a){if(!ck[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){cl||(cl=c.createElement("iframe"),cl.frameBorder=cl.width=cl.height=0),b.appendChild(cl);if(!cm||!cl.createElement)cm=(cl.contentWindow||cl.contentDocument).document,cm.write((c.compatMode==="CSS1Compat"?"<!doctype html>":"")+"<html><body>"),cm.close();d=cm.createElement(a),cm.body.appendChild(d),e=f.css(d,"display"),b.removeChild(cl)}ck[a]=e}return ck[a]}function cu(a,b){var c={};f.each(cq.concat.apply([],cq.slice(0,b)),function(){c[this]=a});return c}function ct(){cr=b}function cs(){setTimeout(ct,0);return cr=f.now()}function cj(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ci(){try{return new a.XMLHttpRequest}catch(b){}}function cc(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g<i;g++){if(g===1)for(h in a.converters)typeof h=="string"&&(e[h.toLowerCase()]=a.converters[h]);l=k,k=d[g];if(k==="*")k=l;else if(l!=="*"&&l!==k){m=l+" "+k,n=e[m]||e["* "+k];if(!n){p=b;for(o in e){j=o.split(" ");if(j[0]===l||j[0]==="*"){p=e[j[1]+" "+k];if(p){o=e[o],o===!0?n=p:p===!0&&(n=o);break}}}}!n&&!p&&f.error("No conversion from "+m.replace(" "," to ")),n!==!0&&(c=n?n(c):p(o(c)))}}return c}function cb(a,c,d){var e=a.contents,f=a.dataTypes,g=a.responseFields,h,i,j,k;for(i in g)i in d&&(c[g[i]]=d[i]);while(f[0]==="*")f.shift(),h===b&&(h=a.mimeType||c.getResponseHeader("content-type"));if(h)for(i in e)if(e[i]&&e[i].test(h)){f.unshift(i);break}if(f[0]in d)j=f[0];else{for(i in d){if(!f[0]||a.converters[i+" "+f[0]]){j=i;break}k||(k=i)}j=j||k}if(j){j!==f[0]&&f.unshift(j);return d[j]}}function ca(a,b,c,d){if(f.isArray(b))f.each(b,function(b,e){c||bE.test(a)?d(a,e):ca(a+"["+(typeof e=="object"||f.isArray(e)?b:"")+"]",e,c,d)});else if(!c&&b!=null&&typeof b=="object")for(var e in b)ca(a+"["+e+"]",b[e],c,d);else d(a,b)}function b_(a,c){var d,e,g=f.ajaxSettings.flatOptions||{};for(d in c)c[d]!==b&&((g[d]?a:e||(e={}))[d]=c[d]);e&&f.extend(!0,a,e)}function b$(a,c,d,e,f,g){f=f||c.dataTypes[0],g=g||{},g[f]=!0;var h=a[f],i=0,j=h?h.length:0,k=a===bT,l;for(;i<j&&(k||!l);i++)l=h[i](c,d,e),typeof l=="string"&&(!k||g[l]?l=b:(c.dataTypes.unshift(l),l=b$(a,c,d,e,l,g)));(k||!l)&&!g["*"]&&(l=b$(a,c,d,e,"*",g));return l}function bZ(a){return function(b,c){typeof b!="string"&&(c=b,b="*");if(f.isFunction(c)){var d=b.toLowerCase().split(bP),e=0,g=d.length,h,i,j;for(;e<g;e++)h=d[e],j=/^\+/.test(h),j&&(h=h.substr(1)||"*"),i=a[h]=a[h]||[],i[j?"unshift":"push"](c)}}}function bC(a,b,c){var d=b==="width"?a.offsetWidth:a.offsetHeight,e=b==="width"?bx:by,g=0,h=e.length;if(d>0){if(c!=="border")for(;g<h;g++)c||(d-=parseFloat(f.css(a,"padding"+e[g]))||0),c==="margin"?d+=parseFloat(f.css(a,c+e[g]))||0:d-=parseFloat(f.css(a,"border"+e[g]+"Width"))||0;return d+"px"}d=bz(a,b,b);if(d<0||d==null)d=a.style[b]||0;d=parseFloat(d)||0;if(c)for(;g<h;g++)d+=parseFloat(f.css(a,"padding"+e[g]))||0,c!=="padding"&&(d+=parseFloat(f.css(a,"border"+e[g]+"Width"))||0),c==="margin"&&(d+=parseFloat(f.css(a,c+e[g]))||0);return d+"px"}function bp(a,b){b.src?f.ajax({url:b.src,async:!1,dataType:"script"}):f.globalEval((b.text||b.textContent||b.innerHTML||"").replace(bf,"/*$0*/")),b.parentNode&&b.parentNode.removeChild(b)}function bo(a){var b=c.createElement("div");bh.appendChild(b),b.innerHTML=a.outerHTML;return b.firstChild}function bn(a){var b=(a.nodeName||"").toLowerCase();b==="input"?bm(a):b!=="script"&&typeof a.getElementsByTagName!="undefined"&&f.grep(a.getElementsByTagName("input"),bm)}function bm(a){if(a.type==="checkbox"||a.type==="radio")a.defaultChecked=a.checked}function bl(a){return typeof a.getElementsByTagName!="undefined"?a.getElementsByTagName("*"):typeof a.querySelectorAll!="undefined"?a.querySelectorAll("*"):[]}function bk(a,b){var c;if(b.nodeType===1){b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase();if(c==="object")b.outerHTML=a.outerHTML;else if(c!=="input"||a.type!=="checkbox"&&a.type!=="radio"){if(c==="option")b.selected=a.defaultSelected;else if(c==="input"||c==="textarea")b.defaultValue=a.defaultValue}else a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value);b.removeAttribute(f.expando)}}function bj(a,b){if(b.nodeType===1&&!!f.hasData(a)){var c,d,e,g=f._data(a),h=f._data(b,g),i=g.events;if(i){delete h.handle,h.events={};for(c in i)for(d=0,e=i[c].length;d<e;d++)f.event.add(b,c+(i[c][d].namespace?".":"")+i[c][d].namespace,i[c][d],i[c][d].data)}h.data&&(h.data=f.extend({},h.data))}}function bi(a,b){return f.nodeName(a,"table")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function U(a){var b=V.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}function T(a,b,c){b=b||0;if(f.isFunction(b))return f.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return f.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=f.grep(a,function(a){return a.nodeType===1});if(O.test(b))return f.filter(b,d,!c);b=f.filter(b,d)}return f.grep(a,function(a,d){return f.inArray(a,b)>=0===c})}function S(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function K(){return!0}function J(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?parseFloat(d):j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c<d;c++)b[a[c]]=!0;return b}var c=a.document,d=a.navigator,e=a.location,f=function(){function J(){if(!e.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(J,1);return}e.ready()}}var e=function(a,b){return new e.fn.init(a,b,h)},f=a.jQuery,g=a.$,h,i=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z]|[0-9])/ig,w=/^-ms-/,x=function(a,b){return(b+"").toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=m.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7.1",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.add(a);return this},eq:function(a){a=+a;return a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j<k;j++)if((a=arguments[j])!=null)for(c in a){d=i[c],f=a[c];if(i===f)continue;l&&f&&(e.isPlainObject(f)||(g=e.isArray(f)))?(g?(g=!1,h=d&&e.isArray(d)?d:[]):h=d&&e.isPlainObject(d)?d:{},i[c]=e.extend(l,h,f)):f!==b&&(i[c]=f)}return i},e.extend({noConflict:function(b){a.$===e&&(a.$=g),b&&a.jQuery===e&&(a.jQuery=f);return e},isReady:!1,readyWait:1,holdReady:function(a){a?e.readyWait++:e.ready(!0)},ready:function(a){if(a===!0&&!--e.readyWait||a!==!0&&!e.isReady){if(!c.body)return setTimeout(e.ready,1);e.isReady=!0;if(a!==!0&&--e.readyWait>0)return;A.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").off("ready")}},bindReady:function(){if(!A){A=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a&&typeof a=="object"&&"setInterval"in a},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||D.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw new Error(a)},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,"ms-").replace(v,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g<h;)if(c.apply(a[g++],d)===!1)break}else if(i){for(f in a)if(c.call(a[f],f,a[f])===!1)break}else for(;g<h;)if(c.call(a[g],g,a[g++])===!1)break;return a},trim:G?function(a){return a==null?"":G.call(a)}:function(a){return a==null?"":(a+"").replace(k,"").replace(l,"")},makeArray:function(a,b){var c=b||[];if(a!=null){var d=e.type(a);a.length==null||d==="string"||d==="function"||d==="regexp"||e.isWindow(a)?E.call(c,a):e.merge(c,a)}return c},inArray:function(a,b,c){var d;if(b){if(H)return H.call(b,a,c);d=b.length,c=c?c<0?Math.max(0,d+c):c:0;for(;c<d;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,c){var d=a.length,e=0;if(typeof c.length=="number")for(var f=c.length;e<f;e++)a[d++]=c[e];else while(c[e]!==b)a[d++]=c[e++];a.length=d;return a},grep:function(a,b,c){var d=[],e;c=!!c;for(var f=0,g=a.length;f<g;f++)e=!!b(a[f],f),c!==e&&d.push(a[f]);return d},map:function(a,c,d){var f,g,h=[],i=0,j=a.length,k=a instanceof e||j!==b&&typeof j=="number"&&(j>0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i<j;i++)f=c(a[i],i,d),f!=null&&(h[h.length]=f);else for(g in a)f=c(a[g],g,d),f!=null&&(h[h.length]=f);return h.concat.apply([],h)},guid:1,proxy:function(a,c){if(typeof c=="string"){var d=a[c];c=a,a=d}if(!e.isFunction(a))return b;var f=F.call(arguments,2),g=function(){return a.apply(c,f.concat(F.call(arguments)))};g.guid=a.guid=a.guid||g.guid||e.guid++;return g},access:function(a,c,d,f,g,h){var i=a.length;if(typeof c=="object"){for(var j in c)e.access(a,j,c[j],f,g,d);return a}if(d!==b){f=!h&&f&&e.isFunction(d);for(var k=0;k<i;k++)g(a[k],c,f?d.call(a[k],k,g(a[k],c)):d,h);return a}return i?g(a[0],c):b},now:function(){return(new Date).getTime()},uaMatch:function(a){a=a.toLowerCase();var b=r.exec(a)||s.exec(a)||t.exec(a)||a.indexOf("compatible")<0&&u.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},sub:function(){function a(b,c){return new a.fn.init(b,c)}e.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function(d,f){f&&f instanceof e&&!(f instanceof a)&&(f=a(f));return e.fn.init.call(this,d,f,b)},a.fn.init.prototype=a.fn;var b=a(c);return a},browser:{}}),e.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(a,b){I["[object "+b+"]"]=b.toLowerCase()}),z=e.uaMatch(y),z.browser&&(e.browser[z.browser]=!0,e.browser.version=z.version),e.browser.webkit&&(e.browser.safari=!0),j.test(" ")&&(k=/^[\s\xA0]+/,l=/[\s\xA0]+$/),h=e(c),c.addEventListener?B=function(){c.removeEventListener("DOMContentLoaded",B,!1),e.ready()}:c.attachEvent&&(B=function(){c.readyState==="complete"&&(c.detachEvent("onreadystatechange",B),e.ready())});return e}(),g={};f.Callbacks=function(a){a=a?g[a]||h(a):{};var c=[],d=[],e,i,j,k,l,m=function(b){var d,e,g,h,i;for(d=0,e=b.length;d<e;d++)g=b[d],h=f.type(g),h==="array"?m(g):h==="function"&&(!a.unique||!o.has(g))&&c.push(g)},n=function(b,f){f=f||[],e=!a.memory||[b,f],i=!0,l=j||0,j=0,k=c.length;for(;c&&l<k;l++)if(c[l].apply(b,f)===!1&&a.stopOnFalse){e=!0;break}i=!1,c&&(a.once?e===!0?o.disable():c=[]:d&&d.length&&(e=d.shift(),o.fireWith(e[0],e[1])))},o={add:function(){if(c){var a=c.length;m(arguments),i?k=c.length:e&&e!==!0&&(j=a,n(e[0],e[1]))}return this},remove:function(){if(c){var b=arguments,d=0,e=b.length;for(;d<e;d++)for(var f=0;f<c.length;f++)if(b[d]===c[f]){i&&f<=k&&(k--,f<=l&&l--),c.splice(f--,1);if(a.unique)break}}return this},has:function(a){if(c){var b=0,d=c.length;for(;b<d;b++)if(a===c[b])return!0}return!1},empty:function(){c=[];return this},disable:function(){c=d=e=b;return this},disabled:function(){return!c},lock:function(){d=b,(!e||e===!0)&&o.disable();return this},locked:function(){return!d},fireWith:function(b,c){d&&(i?a.once||d.push([b,c]):(!a.once||!e)&&n(b,c));return this},fire:function(){o.fireWith(this,arguments);return this},fired:function(){return!!e}};return o};var i=[].slice;f.extend({Deferred:function(a){var b=f.Callbacks("once memory"),c=f.Callbacks("once memory"),d=f.Callbacks("memory"),e="pending",g={resolve:b,reject:c,notify:d},h={done:b.add,fail:c.add,progress:d.add,state:function(){return e},isResolved:b.fired,isRejected:c.fired,then:function(a,b,c){i.done(a).fail(b).progress(c);return this},always:function(){i.done.apply(i,arguments).fail.apply(i,arguments);return this},pipe:function(a,b,c){return f.Deferred(function(d){f.each({done:[a,"resolve"],fail:[b,"reject"],progress:[c,"notify"]},function(a,b){var c=b[0],e=b[1],g;f.isFunction(c)?i[a](function(){g=c.apply(this,arguments),g&&f.isFunction(g.promise)?g.promise().then(d.resolve,d.reject,d.notify):d[e+"With"](this===i?d:this,[g])}):i[a](d[e])})}).promise()},promise:function(a){if(a==null)a=h;else for(var b in h)a[b]=h[b];return a}},i=h.promise({}),j;for(j in g)i[j]=g[j].fire,i[j+"With"]=g[j].fireWith;i.done(function(){e="resolved"},c.disable,d.lock).fail(function(){e="rejected"},b.disable,d.lock),a&&a.call(i,i);return i},when:function(a){function m(a){return function(b){e[a]=arguments.length>1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c<d;c++)b[c]&&b[c].promise&&f.isFunction(b[c].promise)?b[c].promise().then(l(c),j.reject,m(c)):--g;g||j.resolveWith(j,b)}else j!==a&&j.resolveWith(j,d?[a]:[]);return k}}),f.support=function(){var b,d,e,g,h,i,j,k,l,m,n,o,p,q=c.createElement("div"),r=c.documentElement;q.setAttribute("className","t"),q.innerHTML="   <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>",d=q.getElementsByTagName("*"),e=q.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=q.getElementsByTagName("input")[0],b={leadingWhitespace:q.firstChild.nodeType===3,tbody:!q.getElementsByTagName("tbody").length,htmlSerialize:!!q.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:q.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav></:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0},i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete q.test}catch(s){b.deleteExpando=!1}!q.addEventListener&&q.attachEvent&&q.fireEvent&&(q.attachEvent("onclick",function(){b.noCloneEvent=!1}),q.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),q.appendChild(i),k=c.createDocumentFragment(),k.appendChild(q.lastChild),b.checkClone=k.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,k.removeChild(i),k.appendChild(q),q.innerHTML="",a.getComputedStyle&&(j=c.createElement("div"),j.style.width="0",j.style.marginRight="0",q.style.width="2px",q.appendChild(j),b.reliableMarginRight=(parseInt((a.getComputedStyle(j,null)||{marginRight:0}).marginRight,10)||0)===0);if(q.attachEvent)for(o in{submit:1,change:1,focusin:1})n="on"+o,p=n in q,p||(q.setAttribute(n,"return;"),p=typeof q[n]=="function"),b[o+"Bubbles"]=p;k.removeChild(q),k=g=h=j=q=i=null,f(function(){var a,d,e,g,h,i,j,k,m,n,o,r=c.getElementsByTagName("body")[0];!r||(j=1,k="position:absolute;top:0;left:0;width:1px;height:1px;margin:0;",m="visibility:hidden;border:0;",n="style='"+k+"border:5px solid #000;padding:0;'",o="<div "+n+"><div></div></div>"+"<table "+n+" cellpadding='0' cellspacing='0'>"+"<tr><td></td></tr></table>",a=c.createElement("div"),a.style.cssText=m+"width:0;height:0;position:static;top:0;margin-top:"+j+"px",r.insertBefore(a,r.firstChild),q=c.createElement("div"),a.appendChild(q),q.innerHTML="<table><tr><td style='padding:0;border:0;display:none'></td><td>t</td></tr></table>",l=q.getElementsByTagName("td"),p=l[0].offsetHeight===0,l[0].style.display="",l[1].style.display="none",b.reliableHiddenOffsets=p&&l[0].offsetHeight===0,q.innerHTML="",q.style.width=q.style.paddingLeft="1px",f.boxModel=b.boxModel=q.offsetWidth===2,typeof q.style.zoom!="undefined"&&(q.style.display="inline",q.style.zoom=1,b.inlineBlockNeedsLayout=q.offsetWidth===2,q.style.display="",q.innerHTML="<div style='width:4px;'></div>",b.shrinkWrapBlocks=q.offsetWidth!==2),q.style.cssText=k+m,q.innerHTML=o,d=q.firstChild,e=d.firstChild,h=d.nextSibling.firstChild.firstChild,i={doesNotAddBorder:e.offsetTop!==5,doesAddBorderForTableAndCells:h.offsetTop===5},e.style.position="fixed",e.style.top="20px",i.fixedPosition=e.offsetTop===20||e.offsetTop===15,e.style.position=e.style.top="",d.style.overflow="hidden",d.style.position="relative",i.subtractsBorderForOverflowNotVisible=e.offsetTop===-5,i.doesNotIncludeMarginInBodyOffset=r.offsetTop!==j,r.removeChild(a),q=a=null,f.extend(b,i))});return b}();var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[j]:a[j]&&j,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[j]=n=++f.uuid:n=j),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[h]:h;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)||(b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,g=b.length;e<g;e++)delete d[b[e]];if(!(c?m:f.isEmptyObject)(d))return}}if(!c){delete j[k].data;if(!m(j[k]))return}f.support.deleteExpando||!j.setInterval?delete j[k]:j[k]=null,i&&(f.support.deleteExpando?delete a[h]:a.removeAttribute?a.removeAttribute(h):a[h]=null)}},_data:function(a,b,c){return f.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=f.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),f.fn.extend({data:function(a,c){var d,e,g,h=null;if(typeof a=="undefined"){if(this.length){h=f.data(this[0]);if(this[0].nodeType===1&&!f._data(this[0],"parsedAttrs")){e=this[0].attributes;for(var i=0,j=e.length;i<j;i++)g=e[i].name,g.indexOf("data-")===0&&(g=f.camelCase(g.substring(5)),l(this[0],g,h[g]));f._data(this[0],"parsedAttrs",!0)}}return h}if(typeof a=="object")return this.each(function(){f.data(this,a)});d=a.split("."),d[1]=d[1]?"."+d[1]:"";if(c===b){h=this.triggerHandler("getData"+d[1]+"!",[d[0]]),h===b&&this.length&&(h=f.data(this[0],a),h=l(this[0],a,h));return h===b&&d[1]?this.data(d[0]):h}return this.each(function(){var b=f(this),e=[d[0],c];b.triggerHandler("setData"+d[1]+"!",e),f.data(this,a,c),b.triggerHandler("changeData"+d[1]+"!",e)})},removeData:function(a){return this.each(function(){f.removeData(this,a)})}}),f.extend({_mark:function(a,b){a&&(b=(b||"fx")+"mark",f._data(a,b,(f._data(a,b)||0)+1))},_unmark:function(a,b,c){a!==!0&&(c=b,b=a,a=!1);if(b){c=c||"fx";var d=c+"mark",e=a?0:(f._data(b,d)||1)-1;e?f._data(b,d,e):(f.removeData(b,d,!0),n(b,c,"mark"))}},queue:function(a,b,c){var d;if(a){b=(b||"fx")+"queue",d=f._data(a,b),c&&(!d||f.isArray(c)?d=f._data(a,b,f.makeArray(c)):d.push(c));return d||[]}},dequeue:function(a,b){b=b||"fx";var c=f.queue(a,b),d=c.shift(),e={};d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),f._data(a,b+".run",e),d.call(a,function(){f.dequeue(a,b)},e)),c.length||(f.removeData(a,b+"queue "+b+".run",!0),n(a,b,"queue"))}}),f.fn.extend({queue:function(a,c){typeof a!="string"&&(c=a,a="fx");if(c===b)return f.queue(this[0],a);return this.each(function(){var b=f.queue(this,a,c);a==="fx"&&b[0]!=="inprogress"&&f.dequeue(this,a)})},dequeue:function(a){return this.each(function(){f.dequeue(this,a)})},delay:function(a,b){a=f.fx?f.fx.speeds[a]||a:a,b=b||"fx";return this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,c){function m(){--h||d.resolveWith(e,[e])}typeof a!="string"&&(c=a,a=b),a=a||"fx";var d=f.Deferred(),e=this,g=e.length,h=1,i=a+"defer",j=a+"queue",k=a+"mark",l;while(g--)if(l=f.data(e[g],i,b,!0)||(f.data(e[g],j,b,!0)||f.data(e[g],k,b,!0))&&f.data(e[g],i,f.Callbacks("once memory"),!0))h++,l.add(m);m();return d.promise()}});var o=/[\n\t\r]/g,p=/\s+/,q=/\r/g,r=/^(?:button|input)$/i,s=/^(?:button|input|object|select|textarea)$/i,t=/^a(?:rea)?$/i,u=/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,v=f.support.getSetAttribute,w,x,y;f.fn.extend({attr:function(a,b){return f.access(this,a,b,!0,f.attr)},removeAttr:function(a){return this.each(function(){f.removeAttr(this,a)})},prop:function(a,b){return f.access(this,a,b,!0,f.prop)},removeProp:function(a){a=f.propFix[a]||a;return this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,g,h,i;if(f.isFunction(a))return this.each(function(b){f(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(p);for(c=0,d=this.length;c<d;c++){e=this[c];if(e.nodeType===1)if(!e.className&&b.length===1)e.className=a;else{g=" "+e.className+" ";for(h=0,i=b.length;h<i;h++)~g.indexOf(" "+b[h]+" ")||(g+=b[h]+" ");e.className=f.trim(g)}}}return this},removeClass:function(a){var c,d,e,g,h,i,j;if(f.isFunction(a))return this.each(function(b){f(this).removeClass(a.call(this,b,this.className))});if(a&&typeof a=="string"||a===b){c=(a||"").split(p);for(d=0,e=this.length;d<e;d++){g=this[d];if(g.nodeType===1&&g.className)if(a){h=(" "+g.className+" ").replace(o," ");for(i=0,j=c.length;i<j;i++)h=h.replace(" "+c[i]+" "," ");g.className=f.trim(h)}else g.className=""}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";if(f.isFunction(a))return this.each(function(c){f(this).toggleClass(a.call(this,c,this.className,b),b)});return this.each(function(){if(c==="string"){var e,g=0,h=f(this),i=b,j=a.split(p);while(e=j[g++])i=d?i:!h.hasClass(e),h[i?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&f._data(this,"__className__",this.className),this.className=this.className||a===!1?"":f._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ",c=0,d=this.length;for(;c<d;c++)if(this[c].nodeType===1&&(" "+this[c].className+" ").replace(o," ").indexOf(b)>-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];{if(!!arguments.length){e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.nodeName.toLowerCase()]||f.valHooks[this.type];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}if(g){c=f.valHooks[g.nodeName.toLowerCase()]||f.valHooks[g.type];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}}}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c<d;c++){e=i[c];if(e.selected&&(f.support.optDisabled?!e.disabled:e.getAttribute("disabled")===null)&&(!e.parentNode.disabled||!f.nodeName(e.parentNode,"optgroup"))){b=f(e).val();if(j)return b;h.push(b)}}if(j&&!h.length&&i.length)return f(i[g]).val();return h},set:function(a,b){var c=f.makeArray(b);f(a).find("option").each(function(){this.selected=f.inArray(f(this).val(),c)>=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!!a&&j!==3&&j!==8&&j!==2){if(e&&c in f.attrFn)return f(a)[c](d);if(typeof a.getAttribute=="undefined")return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g}},removeAttr:function(a,b){var c,d,e,g,h=0;if(b&&a.nodeType===1){d=b.toLowerCase().split(p),g=d.length;for(;h<g;h++)e=d[h],e&&(c=f.propFix[e]||e,f.attr(a,e,""),a.removeAttribute(v?e:c),u.test(e)&&c in a&&(a[c]=!1))}},attrHooks:{type:{set:function(a,b){if(r.test(a.nodeName)&&a.parentNode)f.error("type property can't be changed");else if(!f.support.radioValue&&b==="radio"&&f.nodeName(a,"input")){var c=a.value;a.setAttribute("type",b),c&&(a.value=c);return b}}},value:{get:function(a,b){if(w&&f.nodeName(a,"button"))return w.get(a,b);return b in a?a.value:null},set:function(a,b,c){if(w&&f.nodeName(a,"button"))return w.set(a,b,c);a.value=b}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(a,c,d){var e,g,h,i=a.nodeType;if(!!a&&i!==3&&i!==8&&i!==2){h=i!==1||!f.isXMLDoc(a),h&&(c=f.propFix[c]||c,g=f.propHooks[c]);return d!==b?g&&"set"in g&&(e=g.set(a,d,c))!==b?e:a[c]=d:g&&"get"in g&&(e=g.get(a,c))!==null?e:a[c]}},propHooks:{tabIndex:{get:function(a){var c=a.getAttributeNode("tabindex");return c&&c.specified?parseInt(c.value,10):s.test(a.nodeName)||t.test(a.nodeName)&&a.href?0:b}}}}),f.attrHooks.tabindex=f.propHooks.tabIndex,x={get:function(a,c){var d,e=f.prop(a,c);return e===!0||typeof e!="boolean"&&(d=a.getAttributeNode(c))&&d.nodeValue!==!1?c.toLowerCase():b},set:function(a,b,c){var d;b===!1?f.removeAttr(a,c):(d=f.propFix[c]||c,d in a&&(a[d]=!0),a.setAttribute(c,c.toLowerCase()));return c}},v||(y={name:!0,id:!0},w=f.valHooks.button={get:function(a,c){var d;d=a.getAttributeNode(c);return d&&(y[c]?d.nodeValue!=="":d.specified)?d.nodeValue:b},set:function(a,b,d){var e=a.getAttributeNode(d);e||(e=c.createAttribute(d),a.setAttributeNode(e));return e.nodeValue=b+""}},f.attrHooks.tabindex.set=w.set,f.each(["width","height"],function(a,b){f.attrHooks[b]=f.extend(f.attrHooks[b],{set:function(a,c){if(c===""){a.setAttribute(b,"auto");return c}}})}),f.attrHooks.contenteditable={get:w.get,set:function(a,b,c){b===""&&(b="false"),w.set(a,b,c)}}),f.support.hrefNormalized||f.each(["href","src","width","height"],function(a,c){f.attrHooks[c]=f.extend(f.attrHooks[c],{get:function(a){var d=a.getAttribute(c,2);return d===null?b:d}})}),f.support.style||(f.attrHooks.style={get:function(a){return a.style.cssText.toLowerCase()||b},set:function(a,b){return a.style.cssText=""+b}}),f.support.optSelected||(f.propHooks.selected=f.extend(f.propHooks.selected,{get:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex);return null}})),f.support.enctype||(f.propFix.enctype="encoding"),f.support.checkOn||f.each(["radio","checkbox"],function(){f.valHooks[this]={get:function(a){return a.getAttribute("value")===null?"on":a.value}}}),f.each(["radio","checkbox"],function(){f.valHooks[this]=f.extend(f.valHooks[this],{set:function(a,b){if(f.isArray(b))return a.checked=f.inArray(f(a).val(),b)>=0}})});var z=/^(?:textarea|input|select)$/i,A=/^([^\.]*)?(?:\.(.+))?$/,B=/\bhover(\.\S+)?\b/,C=/^key/,D=/^(?:mouse|contextmenu)|click/,E=/^(?:focusinfocus|focusoutblur)$/,F=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,G=function(a){var b=F.exec(a);b&&(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},H=function(a,b){var c=a.attributes||{};return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||(c.id||{}).value===b[2])&&(!b[3]||b[3].test((c["class"]||{}).value))},I=function(a){return f.event.special.hover?a:a.replace(B,"mouseenter$1 mouseleave$1")};
+f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=f.trim(I(c)).split(" ");for(k=0;k<c.length;k++){l=A.exec(c[k])||[],m=l[1],n=(l[2]||"").split(".").sort(),s=f.event.special[m]||{},m=(g?s.delegateType:s.bindType)||m,s=f.event.special[m]||{},o=f.extend({type:m,origType:l[1],data:e,handler:d,guid:d.guid,selector:g,quick:G(g),namespace:n.join(".")},p),r=j[m];if(!r){r=j[m]=[],r.delegateCount=0;if(!s.setup||s.setup.call(a,e,n,i)===!1)a.addEventListener?a.addEventListener(m,i,!1):a.attachEvent&&a.attachEvent("on"+m,i)}s.add&&(s.add.call(a,o),o.handler.guid||(o.handler.guid=d.guid)),g?r.splice(r.delegateCount++,0,o):r.push(o),f.event.global[m]=!0}a=null}},global:{},remove:function(a,b,c,d,e){var g=f.hasData(a)&&f._data(a),h,i,j,k,l,m,n,o,p,q,r,s;if(!!g&&!!(o=g.events)){b=f.trim(I(b||"")).split(" ");for(h=0;h<b.length;h++){i=A.exec(b[h])||[],j=k=i[1],l=i[2];if(!j){for(j in o)f.event.remove(a,j+b[h],c,d,!0);continue}p=f.event.special[j]||{},j=(d?p.delegateType:p.bindType)||j,r=o[j]||[],m=r.length,l=l?new RegExp("(^|\\.)"+l.split(".").sort().join("\\.(?:.*\\.)?")+"(\\.|$)"):null;for(n=0;n<r.length;n++)s=r[n],(e||k===s.origType)&&(!c||c.guid===s.guid)&&(!l||l.test(s.namespace))&&(!d||d===s.selector||d==="**"&&s.selector)&&(r.splice(n--,1),s.selector&&r.delegateCount--,p.remove&&p.remove.call(a,s));r.length===0&&m!==r.length&&((!p.teardown||p.teardown.call(a,l)===!1)&&f.removeEvent(a,j,g.handle),delete o[j])}f.isEmptyObject(o)&&(q=g.handle,q&&(q.elem=null),f.removeData(a,["events","handle"],!0))}},customEvent:{getData:!0,setData:!0,changeData:!0},trigger:function(c,d,e,g){if(!e||e.nodeType!==3&&e.nodeType!==8){var h=c.type||c,i=[],j,k,l,m,n,o,p,q,r,s;if(E.test(h+f.event.triggered))return;h.indexOf("!")>=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"";if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,m=E.test(s+h)?e:e.parentNode,n=null;for(;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;l<r.length&&!c.isPropagationStopped();l++)m=r[l][0],c.type=r[l][1],q=(f._data(m,"events")||{})[c.type]&&f._data(m,"handle"),q&&q.apply(m,d),q=o&&m[o],q&&f.acceptData(m)&&q.apply(m,d)===!1&&c.preventDefault();c.type=h,!g&&!c.isDefaultPrevented()&&(!p._default||p._default.apply(e.ownerDocument,d)===!1)&&(h!=="click"||!f.nodeName(e,"a"))&&f.acceptData(e)&&o&&e[h]&&(h!=="focus"&&h!=="blur"||c.target.offsetWidth!==0)&&!f.isWindow(e)&&(n=e[o],n&&(e[o]=null),f.event.triggered=h,e[h](),f.event.triggered=b,n&&(e[o]=n));return c.result}},dispatch:function(c){c=f.event.fix(c||a.event);var d=(f._data(this,"events")||{})[c.type]||[],e=d.delegateCount,g=[].slice.call(arguments,0),h=!c.exclusive&&!c.namespace,i=[],j,k,l,m,n,o,p,q,r,s,t;g[0]=c,c.delegateTarget=this;if(e&&!c.target.disabled&&(!c.button||c.type!=="click")){m=f(this),m.context=this.ownerDocument||this;for(l=c.target;l!=this;l=l.parentNode||this){o={},q=[],m[0]=l;for(j=0;j<e;j++)r=d[j],s=r.selector,o[s]===b&&(o[s]=r.quick?H(l,r.quick):m.is(s)),o[s]&&q.push(r);q.length&&i.push({elem:l,matches:q})}}d.length>e&&i.push({elem:this,matches:d.slice(e)});for(j=0;j<i.length&&!c.isPropagationStopped();j++){p=i[j],c.currentTarget=p.elem;for(k=0;k<p.matches.length&&!c.isImmediatePropagationStopped();k++){r=p.matches[k];if(h||!c.namespace&&!r.namespace||c.namespace_re&&c.namespace_re.test(r.namespace))c.data=r.data,c.handleObj=r,n=((f.event.special[r.origType]||{}).handle||r.handler).apply(p.elem,g),n!==b&&(c.result=n,n===!1&&(c.preventDefault(),c.stopPropagation()))}}return c.result},props:"attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){a.which==null&&(a.which=b.charCode!=null?b.charCode:b.keyCode);return a}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,d){var e,f,g,h=d.button,i=d.fromElement;a.pageX==null&&d.clientX!=null&&(e=a.target.ownerDocument||c,f=e.documentElement,g=e.body,a.pageX=d.clientX+(f&&f.scrollLeft||g&&g.scrollLeft||0)-(f&&f.clientLeft||g&&g.clientLeft||0),a.pageY=d.clientY+(f&&f.scrollTop||g&&g.scrollTop||0)-(f&&f.clientTop||g&&g.clientTop||0)),!a.relatedTarget&&i&&(a.relatedTarget=i===a.target?d.toElement:i),!a.which&&h!==b&&(a.which=h&1?1:h&2?3:h&4?2:0);return a}},fix:function(a){if(a[f.expando])return a;var d,e,g=a,h=f.event.fixHooks[a.type]||{},i=h.props?this.props.concat(h.props):this.props;a=f.Event(g);for(d=i.length;d;)e=i[--d],a[e]=g[e];a.target||(a.target=g.srcElement||c),a.target.nodeType===3&&(a.target=a.target.parentNode),a.metaKey===b&&(a.metaKey=a.ctrlKey);return h.filter?h.filter(a,g):a},special:{ready:{setup:f.bindReady},load:{noBubble:!0},focus:{delegateType:"focusin"},blur:{delegateType:"focusout"},beforeunload:{setup:function(a,b,c){f.isWindow(this)&&(this.onbeforeunload=c)},teardown:function(a,b){this.onbeforeunload===b&&(this.onbeforeunload=null)}}},simulate:function(a,b,c,d){var e=f.extend(new f.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?f.event.trigger(e,null,b):f.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},f.event.handle=f.event.dispatch,f.removeEvent=c.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){a.detachEvent&&a.detachEvent("on"+b,c)},f.Event=function(a,b){if(!(this instanceof f.Event))return new f.Event(a,b);a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||a.returnValue===!1||a.getPreventDefault&&a.getPreventDefault()?K:J):this.type=a,b&&f.extend(this,b),this.timeStamp=a&&a.timeStamp||f.now(),this[f.expando]=!0},f.Event.prototype={preventDefault:function(){this.isDefaultPrevented=K;var a=this.originalEvent;!a||(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){this.isPropagationStopped=K;var a=this.originalEvent;!a||(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=K,this.stopPropagation()},isDefaultPrevented:J,isPropagationStopped:J,isImmediatePropagationStopped:J},f.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){f.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c=this,d=a.relatedTarget,e=a.handleObj,g=e.selector,h;if(!d||d!==c&&!f.contains(c,d))a.type=e.origType,h=e.handler.apply(this,arguments),a.type=b;return h}}}),f.support.submitBubbles||(f.event.special.submit={setup:function(){if(f.nodeName(this,"form"))return!1;f.event.add(this,"click._submit keypress._submit",function(a){var c=a.target,d=f.nodeName(c,"input")||f.nodeName(c,"button")?c.form:b;d&&!d._submit_attached&&(f.event.add(d,"submit._submit",function(a){this.parentNode&&!a.isTrigger&&f.event.simulate("submit",this.parentNode,a,!0)}),d._submit_attached=!0)})},teardown:function(){if(f.nodeName(this,"form"))return!1;f.event.remove(this,"._submit")}}),f.support.changeBubbles||(f.event.special.change={setup:function(){if(z.test(this.nodeName)){if(this.type==="checkbox"||this.type==="radio")f.event.add(this,"propertychange._change",function(a){a.originalEvent.propertyName==="checked"&&(this._just_changed=!0)}),f.event.add(this,"click._change",function(a){this._just_changed&&!a.isTrigger&&(this._just_changed=!1,f.event.simulate("change",this,a,!0))});return!1}f.event.add(this,"beforeactivate._change",function(a){var b=a.target;z.test(b.nodeName)&&!b._change_attached&&(f.event.add(b,"change._change",function(a){this.parentNode&&!a.isSimulated&&!a.isTrigger&&f.event.simulate("change",this.parentNode,a,!0)}),b._change_attached=!0)})},handle:function(a){var b=a.target;if(this!==b||a.isSimulated||a.isTrigger||b.type!=="radio"&&b.type!=="checkbox")return a.handleObj.handler.apply(this,arguments)},teardown:function(){f.event.remove(this,"._change");return z.test(this.nodeName)}}),f.support.focusinBubbles||f.each({focus:"focusin",blur:"focusout"},function(a,b){var d=0,e=function(a){f.event.simulate(b,a.target,f.event.fix(a),!0)};f.event.special[b]={setup:function(){d++===0&&c.addEventListener(a,e,!0)},teardown:function(){--d===0&&c.removeEventListener(a,e,!0)}}}),f.fn.extend({on:function(a,c,d,e,g){var h,i;if(typeof a=="object"){typeof c!="string"&&(d=c,c=b);for(i in a)this.on(i,c,d,a[i],g);return this}d==null&&e==null?(e=c,d=c=b):e==null&&(typeof c=="string"?(e=d,d=b):(e=d,d=c,c=b));if(e===!1)e=J;else if(!e)return this;g===1&&(h=e,e=function(a){f().off(a);return h.apply(this,arguments)},e.guid=h.guid||(h.guid=f.guid++));return this.each(function(){f.event.add(this,a,e,d,c)})},one:function(a,b,c,d){return this.on.call(this,a,b,c,d,1)},off:function(a,c,d){if(a&&a.preventDefault&&a.handleObj){var e=a.handleObj;f(a.delegateTarget).off(e.namespace?e.type+"."+e.namespace:e.type,e.selector,e.handler);return this}if(typeof a=="object"){for(var g in a)this.off(g,c,a[g]);return this}if(c===!1||typeof c=="function")d=c,c=b;d===!1&&(d=J);return this.each(function(){f.event.remove(this,a,d,c)})},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},live:function(a,b,c){f(this.context).on(a,this.selector,b,c);return this},die:function(a,b){f(this.context).off(a,this.selector||"**",b);return this},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return arguments.length==1?this.off(a,"**"):this.off(b,a,c)},trigger:function(a,b){return this.each(function(){f.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0])return f.event.trigger(a,b,this[0],!0)},toggle:function(a){var b=arguments,c=a.guid||f.guid++,d=0,e=function(c){var e=(f._data(this,"lastToggle"+a.guid)||0)%d;f._data(this,"lastToggle"+a.guid,e+1),c.preventDefault();return b[e].apply(this,arguments)||!1};e.guid=c;while(d<b.length)b[d++].guid=c;return this.click(e)},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),f.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){f.fn[b]=function(a,c){c==null&&(c=a,a=null);return arguments.length>0?this.on(b,null,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),C.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),D.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h<i;h++){var j=e[h];if(j){var k=!1;j=j[a];while(j){if(j[d]===c){k=e[j.sizset];break}if(j.nodeType===1){g||(j[d]=c,j.sizset=h);if(typeof b!="string"){if(j===b){k=!0;break}}else if(m.filter(b,[j]).length>0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h<i;h++){var j=e[h];if(j){var k=!1;j=j[a];while(j){if(j[d]===c){k=e[j.sizset];break}j.nodeType===1&&!g&&(j[d]=c,j.sizset=h);if(j.nodeName.toLowerCase()===b){k=j;break}j=j[a]}e[h]=k}}}var a=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b<a.length;b++)a[b]===a[b-1]&&a.splice(b--,1)}return a},m.matches=function(a,b){return m(a,null,null,b)},m.matchesSelector=function(a,b){return m(b,null,null,[a]).length>0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e<f;e++){h=o.order[e];if(g=o.leftMatch[h].exec(a)){i=g[1],g.splice(1,1);if(i.substr(i.length-1)!=="\\"){g[1]=(g[1]||"").replace(j,""),d=o.find[h](g,b,c);if(d!=null){a=a.replace(o.match[h],"");break}}}}d||(d=typeof b.getElementsByTagName!="undefined"?b.getElementsByTagName("*"):[]);return{set:d,expr:a}},m.filter=function(a,c,d,e){var f,g,h,i,j,k,l,n,p,q=a,r=[],s=c,t=c&&c[0]&&m.isXML(c[0]);while(a&&c.length){for(h in o.filter)if((f=o.leftMatch[h].exec(a))!=null&&f[2]){k=o.filter[h],l=f[1],g=!1,f.splice(1,1);if(l.substr(l.length-1)==="\\")continue;s===r&&(r=[]);if(o.preFilter[h]){f=o.preFilter[h](f,s,d,r,e,t);if(!f)g=i=!0;else if(f===!0)continue}if(f)for(n=0;(j=s[n])!=null;n++)j&&(i=k(j,f,n,s),p=e^i,d&&i!=null?p?g=!0:s[n]=!1:p&&(r.push(j),g=!0));if(i!==b){d||(s=r),a=a.replace(o.match[h],"");if(!g)return[];break}}if(a===q)if(g==null)m.error(a);else break;q=a}return s},m.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)};var n=m.getText=function(a){var b,c,d=a.nodeType,e="";if(d){if(d===1||d===9){if(typeof a.textContent=="string")return a.textContent;if(typeof a.innerText=="string")return a.innerText.replace(k,"");for(a=a.firstChild;a;a=a.nextSibling)e+=n(a)}else if(d===3||d===4)return a.nodeValue}else for(b=0;c=a[b];b++)c.nodeType!==8&&(e+=n(c));return e},o=m.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(a){return a.getAttribute("href")},type:function(a){return a.getAttribute("type")}},relative:{"+":function(a,b){var c=typeof b=="string",d=c&&!l.test(b),e=c&&!d;d&&(b=b.toLowerCase());for(var f=0,g=a.length,h;f<g;f++)if(h=a[f]){while((h=h.previousSibling)&&h.nodeType!==1);a[f]=e||h&&h.nodeName.toLowerCase()===b?h||!1:h===b}e&&m.filter(b,a,!0)},">":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e<f;e++){c=a[e];if(c){var g=c.parentNode;a[e]=g.nodeName.toLowerCase()===b?g:!1}}}else{for(;e<f;e++)c=a[e],c&&(a[e]=d?c.parentNode:c.parentNode===b);d&&m.filter(b,a,!0)}},"":function(a,b,c){var d,f=e++,g=x;typeof b=="string"&&!l.test(b)&&(b=b.toLowerCase(),d=b,g=w),g("parentNode",b,f,a,d,c)},"~":function(a,b,c){var d,f=e++,g=x;typeof b=="string"&&!l.test(b)&&(b=b.toLowerCase(),d=b,g=w),g("previousSibling",b,f,a,d,c)}},find:{ID:function(a,b,c){if(typeof b.getElementById!="undefined"&&!c){var d=b.getElementById(a[1]);return d&&d.parentNode?[d]:[]}},NAME:function(a,b){if(typeof b.getElementsByName!="undefined"){var c=[],d=b.getElementsByName(a[1]);for(var e=0,f=d.length;e<f;e++)d[e].getAttribute("name")===a[1]&&c.push(d[e]);return c.length===0?null:c}},TAG:function(a,b){if(typeof b.getElementsByTagName!="undefined")return b.getElementsByTagName(a[1])}},preFilter:{CLASS:function(a,b,c,d,e,f){a=" "+a[1].replace(j,"")+" ";if(f)return a;for(var g=0,h;(h=b[g])!=null;g++)h&&(e^(h.className&&(" "+h.className+" ").replace(/[\t\n\r]/g," ").indexOf(a)>=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return b<c[3]-0},gt:function(a,b,c){return b>c[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h<i;h++)if(g[h]===a)return!1;return!0}m.error(e)},CHILD:function(a,b){var c,e,f,g,h,i,j,k=b[1],l=a;switch(k){case"only":case"first":while(l=l.previousSibling)if(l.nodeType===1)return!1;if(k==="first")return!0;l=a;case"last":while(l=l.nextSibling)if(l.nodeType===1)return!1;return!0;case"nth":c=b[2],e=b[3];if(c===1&&e===0)return!0;f=b[0],g=a.parentNode;if(g&&(g[d]!==f||!a.nodeIndex)){i=0;for(l=g.firstChild;l;l=l.nextSibling)l.nodeType===1&&(l.nodeIndex=++i);g[d]=f}j=a.nodeIndex-e;return c===0?j===0:j%c===0&&j/c>=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c<e;c++)d.push(a[c]);else for(;a[c];c++)d.push(a[c]);return d}}var u,v;c.documentElement.compareDocumentPosition?u=function(a,b){if(a===b){h=!0;return 0}if(!a.compareDocumentPosition||!b.compareDocumentPosition)return a.compareDocumentPosition?-1:1;return a.compareDocumentPosition(b)&4?-1:1}:(u=function(a,b){if(a===b){h=!0;return 0}if(a.sourceIndex&&b.sourceIndex)return a.sourceIndex-b.sourceIndex;var c,d,e=[],f=[],g=a.parentNode,i=b.parentNode,j=g;if(g===i)return v(a,b);if(!g)return-1;if(!i)return 1;while(j)e.unshift(j),j=j.parentNode;j=i;while(j)f.unshift(j),j=j.parentNode;c=e.length,d=f.length;for(var k=0;k<c&&k<d;k++)if(e[k]!==f[k])return v(e[k],f[k]);return k===c?v(a,f[k],-1):v(e[k],b,1)},v=function(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}),function(){var a=c.createElement("div"),d="script"+(new Date).getTime(),e=c.documentElement;a.innerHTML="<a name='"+d+"'/>",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="<a href='#'></a>",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="<p class='TEST'></p>";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="<div class='test e'></div><div class='test'></div>";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h<i;h++)m(a,g[h],e,c);return m.filter(f,e)};m.attr=f.attr,m.selectors.attrMap={},f.find=m,f.expr=m.selectors,f.expr[":"]=f.expr.filters,f.unique=m.uniqueSort,f.text=m.getText,f.isXMLDoc=m.isXML,f.contains=m.contains}();var L=/Until$/,M=/^(?:parents|prevUntil|prevAll)/,N=/,/,O=/^.[^:#\[\.,]*$/,P=Array.prototype.slice,Q=f.expr.match.POS,R={children:!0,contents:!0,next:!0,prev:!0};f.fn.extend({find:function(a){var b=this,c,d;if(typeof a!="string")return f(a).filter(function(){for(c=0,d=b.length;c<d;c++)if(f.contains(b[c],this))return!0});var e=this.pushStack("","find",a),g,h,i;for(c=0,d=this.length;c<d;c++){g=e.length,f.find(a,this[c],e);if(c>0)for(h=g;h<e.length;h++)for(i=0;i<g;i++)if(e[i]===e[h]){e.splice(h--,1);break}}return e},has:function(a){var b=f(a);return this.filter(function(){for(var a=0,c=b.length;a<c;a++)if(f.contains(this,b[a]))return!0})},not:function(a){return this.pushStack(T(this,a,!1),"not",a)},filter:function(a){return this.pushStack(T(this,a,!0),"filter",a)},is:function(a){return!!a&&(typeof a=="string"?Q.test(a)?f(a,this.context).index(this[0])>=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d<a.length;d++)f(g).is(a[d])&&c.push({selector:a[d],elem:g,level:h});g=g.parentNode,h++}return c}var i=Q.test(a)||typeof a!="string"?f(a,b||this.context):0;for(d=0,e=this.length;d<e;d++){g=this[d];while(g){if(i?i.index(g)>-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(S(c[0])||S(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling(a.parentNode.firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c);L.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!R[a]?f.unique(e):e,(this.length>1||N.test(d))&&M.test(a)&&(e=e.reverse());return this.pushStack(e,a,P.call(arguments).join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var V="abbr|article|aside|audio|canvas|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/<tbody/i,_=/<|&#?\w+;/,ba=/<(?:script|style)/i,bb=/<(?:script|object|embed|option|style)/i,bc=new RegExp("<(?:"+V+")","i"),bd=/checked\s*(?:[^=]|=\s*.checked.)/i,be=/\/(java|ecma)script/i,bf=/^\s*<!(?:\[CDATA\[|\-\-)/,bg={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]},bh=U(c);bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div<div>","</div>"]),f.fn.extend({text:function(a){if(f.isFunction(a))return this.each(function(b){var c=f(this);c.text(a.call(this,b,c.text()))});if(typeof a!="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return f.text(this)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=f.isFunction(a);return this.each(function(c){f(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f.clean(arguments);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f.clean(arguments));return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function()
+{for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!bg[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1></$2>");try{for(var c=0,d=this.length;c<d;c++)this[c].nodeType===1&&(f.cleanData(this[c].getElementsByTagName("*")),this[c].innerHTML=a)}catch(e){this.empty().append(a)}}else f.isFunction(a)?this.each(function(b){var c=f(this);c.html(a.call(this,b,c.html()))}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&this[0].parentNode){if(f.isFunction(a))return this.each(function(b){var c=f(this),d=c.html();c.replaceWith(a.call(this,b,d))});typeof a!="string"&&(a=f(a).detach());return this.each(function(){var b=this.nextSibling,c=this.parentNode;f(this).remove(),b?f(b).before(a):f(c).append(a)})}return this.length?this.pushStack(f(f.isFunction(a)?a():a),"replaceWith",a):this},detach:function(a){return this.remove(a,!0)},domManip:function(a,c,d){var e,g,h,i,j=a[0],k=[];if(!f.support.checkClone&&arguments.length===3&&typeof j=="string"&&bd.test(j))return this.each(function(){f(this).domManip(a,c,d,!0)});if(f.isFunction(j))return this.each(function(e){var g=f(this);a[0]=j.call(this,e,c?g.html():b),g.domManip(a,c,d)});if(this[0]){i=j&&j.parentNode,f.support.parentNode&&i&&i.nodeType===11&&i.childNodes.length===this.length?e={fragment:i}:e=f.buildFragment(a,this,k),h=e.fragment,h.childNodes.length===1?g=h=h.firstChild:g=h.firstChild;if(g){c=c&&f.nodeName(g,"tr");for(var l=0,m=this.length,n=m-1;l<m;l++)d.call(c?bi(this[l],g):this[l],e.cacheable||m>1&&l<n?f.clone(h,!0,!0):h)}k.length&&f.each(k,bp)}return this}}),f.buildFragment=function(a,b,d){var e,g,h,i,j=a[0];b&&b[0]&&(i=b[0].ownerDocument||b[0]),i.createDocumentFragment||(i=c),a.length===1&&typeof j=="string"&&j.length<512&&i===c&&j.charAt(0)==="<"&&!bb.test(j)&&(f.support.checkClone||!bd.test(j))&&(f.support.html5Clone||!bc.test(j))&&(g=!0,h=f.fragments[j],h&&h!==1&&(e=h)),e||(e=i.createDocumentFragment(),f.clean(a,i,e,d)),g&&(f.fragments[j]=h?e:1);return{fragment:e,cacheable:g}},f.fragments={},f.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){f.fn[a]=function(c){var d=[],e=f(c),g=this.length===1&&this[0].parentNode;if(g&&g.nodeType===11&&g.childNodes.length===1&&e.length===1){e[b](this[0]);return this}for(var h=0,i=e.length;h<i;h++){var j=(h>0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d,e,g,h=f.support.html5Clone||!bc.test("<"+a.nodeName)?a.cloneNode(!0):bo(a);if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bk(a,h),d=bl(a),e=bl(h);for(g=0;d[g];++g)e[g]&&bk(d[g],e[g])}if(b){bj(a,h);if(c){d=bl(a),e=bl(h);for(g=0;d[g];++g)bj(d[g],e[g])}}d=e=null;return h},clean:function(a,b,d,e){var g;b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var h=[],i;for(var j=0,k;(k=a[j])!=null;j++){typeof k=="number"&&(k+="");if(!k)continue;if(typeof k=="string")if(!_.test(k))k=b.createTextNode(k);else{k=k.replace(Y,"<$1></$2>");var l=(Z.exec(k)||["",""])[1].toLowerCase(),m=bg[l]||bg._default,n=m[0],o=b.createElement("div");b===c?bh.appendChild(o):U(b).appendChild(o),o.innerHTML=m[1]+k+m[2];while(n--)o=o.lastChild;if(!f.support.tbody){var p=$.test(k),q=l==="table"&&!p?o.firstChild&&o.firstChild.childNodes:m[1]==="<table>"&&!p?o.childNodes:[];for(i=q.length-1;i>=0;--i)f.nodeName(q[i],"tbody")&&!q[i].childNodes.length&&q[i].parentNode.removeChild(q[i])}!f.support.leadingWhitespace&&X.test(k)&&o.insertBefore(b.createTextNode(X.exec(k)[0]),o.firstChild),k=o.childNodes}var r;if(!f.support.appendChecked)if(k[0]&&typeof (r=k.length)=="number")for(i=0;i<r;i++)bn(k[i]);else bn(k);k.nodeType?h.push(k):h=f.merge(h,k)}if(d){g=function(a){return!a.type||be.test(a.type)};for(j=0;h[j];j++)if(e&&f.nodeName(h[j],"script")&&(!h[j].type||h[j].type.toLowerCase()==="text/javascript"))e.push(h[j].parentNode?h[j].parentNode.removeChild(h[j]):h[j]);else{if(h[j].nodeType===1){var s=f.grep(h[j].getElementsByTagName("script"),g);h.splice.apply(h,[j+1,0].concat(s))}d.appendChild(h[j])}}return h},cleanData:function(a){var b,c,d=f.cache,e=f.event.special,g=f.support.deleteExpando;for(var h=0,i;(i=a[h])!=null;h++){if(i.nodeName&&f.noData[i.nodeName.toLowerCase()])continue;c=i[f.expando];if(c){b=d[c];if(b&&b.events){for(var j in b.events)e[j]?f.event.remove(i,j):f.removeEvent(i,j,b.handle);b.handle&&(b.handle.elem=null)}g?delete i[f.expando]:i.removeAttribute&&i.removeAttribute(f.expando),delete d[c]}}}});var bq=/alpha\([^)]*\)/i,br=/opacity=([^)]*)/,bs=/([A-Z]|^ms)/g,bt=/^-?\d+(?:px)?$/i,bu=/^-?\d/,bv=/^([\-+])=([\-+.\de]+)/,bw={position:"absolute",visibility:"hidden",display:"block"},bx=["Left","Right"],by=["Top","Bottom"],bz,bA,bB;f.fn.css=function(a,c){if(arguments.length===2&&c===b)return this;return f.access(this,a,c,!0,function(a,c,d){return d!==b?f.style(a,c,d):f.css(a,c)})},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bz(a,"opacity","opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get"in k&&(g=k.get(a,!1,e))!==b)return g;return j[c]}h=typeof d,h==="string"&&(g=bv.exec(d))&&(d=+(g[1]+1)*+g[2]+parseFloat(f.css(a,c)),h="number");if(d==null||h==="number"&&isNaN(d))return;h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set"in k)||(d=k.set(a,d))!==b)try{j[c]=d}catch(l){}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get"in g&&(e=g.get(a,!0,d))!==b)return e;if(bz)return bz(a,c)},swap:function(a,b,c){var d={};for(var e in b)d[e]=a.style[e],a.style[e]=b[e];c.call(a);for(e in b)a.style[e]=d[e]}}),f.curCSS=f.css,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){var e;if(c){if(a.offsetWidth!==0)return bC(a,b,d);f.swap(a,bw,function(){e=bC(a,b,d)});return e}},set:function(a,b){if(!bt.test(b))return b;b=parseFloat(b);if(b>=0)return b+"px"}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return br.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bq,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bq.test(g)?g.replace(bq,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){var c;f.swap(a,{display:"inline-block"},function(){b?c=bz(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(bA=function(a,b){var c,d,e;b=b.replace(bs,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b)));return c}),c.documentElement.currentStyle&&(bB=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f===null&&g&&(e=g[b])&&(f=e),!bt.test(f)&&bu.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f||0,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),bz=bA||bB,f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)});var bD=/%20/g,bE=/\[\]$/,bF=/\r?\n/g,bG=/#.*$/,bH=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bI=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bJ=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bK=/^(?:GET|HEAD)$/,bL=/^\/\//,bM=/\?/,bN=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,bO=/^(?:select|textarea)/i,bP=/\s+/,bQ=/([?&])_=[^&]*/,bR=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bS=f.fn.load,bT={},bU={},bV,bW,bX=["*/"]+["*"];try{bV=e.href}catch(bY){bV=c.createElement("a"),bV.href="",bV=bV.href}bW=bR.exec(bV.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bS)return bS.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("<div>").append(c.replace(bN,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bO.test(this.nodeName)||bI.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bF,"\r\n")}}):{name:b.name,value:c.replace(bF,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.on(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?b_(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),b_(a,b);return a},ajaxSettings:{url:bV,isLocal:bJ.test(bW[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bX},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bZ(bT),ajaxTransport:bZ(bU),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?cb(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=cc(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bH.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bG,"").replace(bL,bW[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bP),d.crossDomain==null&&(r=bR.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bW[1]&&r[2]==bW[2]&&(r[3]||(r[1]==="http:"?80:443))==(bW[3]||(bW[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),b$(bT,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bK.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bM.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bQ,"$1_="+x);d.url=y+(y===d.url?(bM.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bX+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=b$(bU,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){if(s<2)w(-1,z);else throw z}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)ca(g,a[g],c,e);return d.join("&").replace(bD,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cd=f.now(),ce=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cd++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=b.contentType==="application/x-www-form-urlencoded"&&typeof b.data=="string";if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(ce.test(b.url)||e&&ce.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(ce,l),b.url===j&&(e&&(k=k.replace(ce,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var cf=a.ActiveXObject?function(){for(var a in ch)ch[a](0,1)}:!1,cg=0,ch;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ci()||cj()}:ci,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,cf&&delete ch[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n),m.text=h.responseText;try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cg,cf&&(ch||(ch={},f(a).unload(cf)),ch[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var ck={},cl,cm,cn=/^(?:toggle|show|hide)$/,co=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,cp,cq=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cr;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(cu("show",3),a,b,c);for(var g=0,h=this.length;g<h;g++)d=this[g],d.style&&(e=d.style.display,!f._data(d,"olddisplay")&&e==="none"&&(e=d.style.display=""),e===""&&f.css(d,"display")==="none"&&f._data(d,"olddisplay",cv(d.nodeName)));for(g=0;g<h;g++){d=this[g];if(d.style){e=d.style.display;if(e===""||e==="none")d.style.display=f._data(d,"olddisplay")||""}}return this},hide:function(a,b,c){if(a||a===0)return this.animate(cu("hide",3),a,b,c);var d,e,g=0,h=this.length;for(;g<h;g++)d=this[g],d.style&&(e=f.css(d,"display"),e!=="none"&&!f._data(d,"olddisplay")&&f._data(d,"olddisplay",e));for(g=0;g<h;g++)this[g].style&&(this[g].style.display="none");return this},_toggle:f.fn.toggle,toggle:function(a,b,c){var d=typeof a=="boolean";f.isFunction(a)&&f.isFunction(b)?this._toggle.apply(this,arguments):a==null||d?this.each(function(){var b=d?a:f(this).is(":hidden");f(this)[b?"show":"hide"]()}):this.animate(cu("toggle",3),a,b,c);return this},fadeTo:function(a,b,c,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){function g(){e.queue===!1&&f._mark(this);var b=f.extend({},e),c=this.nodeType===1,d=c&&f(this).is(":hidden"),g,h,i,j,k,l,m,n,o;b.animatedProperties={};for(i in a){g=f.camelCase(i),i!==g&&(a[g]=a[i],delete a[i]),h=a[g],f.isArray(h)?(b.animatedProperties[g]=h[1],h=a[g]=h[0]):b.animatedProperties[g]=b.specialEasing&&b.specialEasing[g]||b.easing||"swing";if(h==="hide"&&d||h==="show"&&!d)return b.complete.call(this);c&&(g==="height"||g==="width")&&(b.overflow=[this.style.overflow,this.style.overflowX,this.style.overflowY],f.css(this,"display")==="inline"&&f.css(this,"float")==="none"&&(!f.support.inlineBlockNeedsLayout||cv(this.nodeName)==="inline"?this.style.display="inline-block":this.style.zoom=1))}b.overflow!=null&&(this.style.overflow="hidden");for(i in a)j=new f.fx(this,b,i),h=a[i],cn.test(h)?(o=f._data(this,"toggle"+i)||(h==="toggle"?d?"show":"hide":0),o?(f._data(this,"toggle"+i,o==="show"?"hide":"show"),j[o]()):j[h]()):(k=co.exec(h),l=j.cur(),k?(m=parseFloat(k[2]),n=k[3]||(f.cssNumber[i]?"":"px"),n!=="px"&&(f.style(this,i,(m||1)+n),l=(m||1)/j.cur()*l,f.style(this,i,l+n)),k[1]&&(m=(k[1]==="-="?-1:1)*m+l),j.custom(l,m,n)):j.custom(l,h,""));return!0}var e=f.speed(b,c,d);if(f.isEmptyObject(a))return this.each(e.complete,[!1]);a=f.extend({},a);return e.queue===!1?this.each(g):this.queue(e.queue,g)},stop:function(a,c,d){typeof a!="string"&&(d=c,c=a,a=b),c&&a!==!1&&this.queue(a||"fx",[]);return this.each(function(){function h(a,b,c){var e=b[c];f.removeData(a,c,!0),e.stop(d)}var b,c=!1,e=f.timers,g=f._data(this);d||f._unmark(!0,this);if(a==null)for(b in g)g[b]&&g[b].stop&&b.indexOf(".run")===b.length-4&&h(this,g,b);else g[b=a+".run"]&&g[b].stop&&h(this,g,b);for(b=e.length;b--;)e[b].elem===this&&(a==null||e[b].queue===a)&&(d?e[b](!0):e[b].saveState(),c=!0,e.splice(b,1));(!d||!c)&&f.dequeue(this,a)})}}),f.each({slideDown:cu("show",1),slideUp:cu("hide",1),slideToggle:cu("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){f.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),f.extend({speed:function(a,b,c){var d=a&&typeof a=="object"?f.extend({},a):{complete:c||!c&&b||f.isFunction(a)&&a,duration:a,easing:c&&b||b&&!f.isFunction(b)&&b};d.duration=f.fx.off?0:typeof d.duration=="number"?d.duration:d.duration in f.fx.speeds?f.fx.speeds[d.duration]:f.fx.speeds._default;if(d.queue==null||d.queue===!0)d.queue="fx";d.old=d.complete,d.complete=function(a){f.isFunction(d.old)&&d.old.call(this),d.queue?f.dequeue(this,d.queue):a!==!1&&f._unmark(this)};return d},easing:{linear:function(a,b,c,d){return c+d*a},swing:function(a,b,c,d){return(-Math.cos(a*Math.PI)/2+.5)*d+c}},timers:[],fx:function(a,b,c){this.options=b,this.elem=a,this.prop=c,b.orig=b.orig||{}}}),f.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this),(f.fx.step[this.prop]||f.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a,b=f.css(this.elem,this.prop);return isNaN(a=parseFloat(b))?!b||b==="auto"?0:b:a},custom:function(a,c,d){function h(a){return e.step(a)}var e=this,g=f.fx;this.startTime=cr||cs(),this.end=c,this.now=this.start=a,this.pos=this.state=0,this.unit=d||this.unit||(f.cssNumber[this.prop]?"":"px"),h.queue=this.options.queue,h.elem=this.elem,h.saveState=function(){e.options.hide&&f._data(e.elem,"fxshow"+e.prop)===b&&f._data(e.elem,"fxshow"+e.prop,e.start)},h()&&f.timers.push(h)&&!cp&&(cp=setInterval(g.tick,g.interval))},show:function(){var a=f._data(this.elem,"fxshow"+this.prop);this.options.orig[this.prop]=a||f.style(this.elem,this.prop),this.options.show=!0,a!==b?this.custom(this.cur(),a):this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur()),f(this.elem).show()},hide:function(){this.options.orig[this.prop]=f._data(this.elem,"fxshow"+this.prop)||f.style(this.elem,this.prop),this.options.hide=!0,this.custom(this.cur(),0)},step:function(a){var b,c,d,e=cr||cs(),g=!0,h=this.elem,i=this.options;if(a||e>=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c<b.length;c++)a=b[c],!a()&&b[c]===a&&b.splice(c--,1);b.length||f.fx.stop()},interval:13,stop:function(){clearInterval(cp),cp=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){f.style(a.elem,"opacity",a.now)},_default:function(a){a.elem.style&&a.elem.style[a.prop]!=null?a.elem.style[a.prop]=a.now+a.unit:a.elem[a.prop]=a.now}}}),f.each(["width","height"],function(a,b){f.fx.step[b]=function(a){f.style(a.elem,b,Math.max(0,a.now)+a.unit)}}),f.expr&&f.expr.filters&&(f.expr.filters.animated=function(a){return f.grep(f.timers,function(b){return a===b.elem}).length});var cw=/^t(?:able|d|h)$/i,cx=/^(?:body|html)$/i;"getBoundingClientRect"in c.documentElement?f.fn.offset=function(a){var b=this[0],c;if(a)return this.each(function(b){f.offset.setOffset(this,a,b)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return f.offset.bodyOffset(b);try{c=b.getBoundingClientRect()}catch(d){}var e=b.ownerDocument,g=e.documentElement;if(!c||!f.contains(g,b))return c?{top:c.top,left:c.left}:{top:0,left:0};var h=e.body,i=cy(e),j=g.clientTop||h.clientTop||0,k=g.clientLeft||h.clientLeft||0,l=i.pageYOffset||f.support.boxModel&&g.scrollTop||h.scrollTop,m=i.pageXOffset||f.support.boxModel&&g.scrollLeft||h.scrollLeft,n=c.top+l-j,o=c.left+m-k;return{top:n,left:o}}:f.fn.offset=function(a){var b=this[0];if(a)return this.each(function(b){f.offset.setOffset(this,a,b)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return f.offset.bodyOffset(b);var c,d=b.offsetParent,e=b,g=b.ownerDocument,h=g.documentElement,i=g.body,j=g.defaultView,k=j?j.getComputedStyle(b,null):b.currentStyle,l=b.offsetTop,m=b.offsetLeft;while((b=b.parentNode)&&b!==i&&b!==h){if(f.support.fixedPosition&&k.position==="fixed")break;c=j?j.getComputedStyle(b,null):b.currentStyle,l-=b.scrollTop,m-=b.scrollLeft,b===d&&(l+=b.offsetTop,m+=b.offsetLeft,f.support.doesNotAddBorder&&(!f.support.doesAddBorderForTableAndCells||!cw.test(b.nodeName))&&(l+=parseFloat(c.borderTopWidth)||0,m+=parseFloat(c.borderLeftWidth)||0),e=d,d=b.offsetParent),f.support.subtractsBorderForOverflowNotVisible&&c.overflow!=="visible"&&(l+=parseFloat(c.borderTopWidth)||0,m+=parseFloat(c.borderLeftWidth)||0),k=c}if(k.position==="relative"||k.position==="static")l+=i.offsetTop,m+=i.offsetLeft;f.support.fixedPosition&&k.position==="fixed"&&(l+=Math.max(h.scrollTop,i.scrollTop),m+=Math.max(h.scrollLeft,i.scrollLeft));return{top:l,left:m}},f.offset={bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;f.support.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(f.css(a,"marginTop"))||0,c+=parseFloat(f.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var d=f.css(a,"position");d==="static"&&(a.style.position="relative");var e=f(a),g=e.offset(),h=f.css(a,"top"),i=f.css(a,"left"),j=(d==="absolute"||d==="fixed")&&f.inArray("auto",[h,i])>-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each(["Left","Top"],function(a,c){var d="scroll"+c;f.fn[d]=function(c){var e,g;if(c===b){e=this[0];if(!e)return null;g=cy(e);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:f.support.boxModel&&g.document.documentElement[d]||g.document.body[d]:e[d]}return this.each(function(){g=cy(this),g?g.scrollTo(a?f(g).scrollLeft():c,a?c:f(g).scrollTop()):this[d]=c})}}),f.each(["Height","Width"],function(a,c){var d=c.toLowerCase();f.fn["inner"+c]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,d,"padding")):this[d]():null},f.fn["outer"+c]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,d,a?"margin":"border")):this[d]():null},f.fn[d]=function(a){var e=this[0];if(!e)return a==null?null:this;if(f.isFunction(a))return this.each(function(b){var c=f(this);c[d](a.call(this,b,c[d]()))});if(f.isWindow(e)){var g=e.document.documentElement["client"+c],h=e.document.body;return e.document.compatMode==="CSS1Compat"&&g||h&&h["client"+c]||g}if(e.nodeType===9)return Math.max(e.documentElement["client"+c],e.body["scroll"+c],e.documentElement["scroll"+c],e.body["offset"+c],e.documentElement["offset"+c]);if(a===b){var i=f.css(e,d),j=parseFloat(i);return f.isNumeric(j)?j:i}return this.css(d,typeof a=="string"?a:a+"px")}}),a.jQuery=a.$=f,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return f})})(window);
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/data/javascript/qwebchannel.js	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,413 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Copyright (C) 2014 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff <milian.wolff@kdab.com>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtWebChannel module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+"use strict";
+
+var QWebChannelMessageTypes = {
+    signal: 1,
+    propertyUpdate: 2,
+    init: 3,
+    idle: 4,
+    debug: 5,
+    invokeMethod: 6,
+    connectToSignal: 7,
+    disconnectFromSignal: 8,
+    setProperty: 9,
+    response: 10,
+};
+
+var QWebChannel = function(transport, initCallback)
+{
+    if (typeof transport !== "object" || typeof transport.send !== "function") {
+        console.error("The QWebChannel expects a transport object with a send function and onmessage callback property." +
+                      " Given is: transport: " + typeof(transport) + ", transport.send: " + typeof(transport.send));
+        return;
+    }
+
+    var channel = this;
+    this.transport = transport;
+
+    this.send = function(data)
+    {
+        if (typeof(data) !== "string") {
+            data = JSON.stringify(data);
+        }
+        channel.transport.send(data);
+    }
+
+    this.transport.onmessage = function(message)
+    {
+        var data = message.data;
+        if (typeof data === "string") {
+            data = JSON.parse(data);
+        }
+        switch (data.type) {
+            case QWebChannelMessageTypes.signal:
+                channel.handleSignal(data);
+                break;
+            case QWebChannelMessageTypes.response:
+                channel.handleResponse(data);
+                break;
+            case QWebChannelMessageTypes.propertyUpdate:
+                channel.handlePropertyUpdate(data);
+                break;
+            default:
+                console.error("invalid message received:", message.data);
+                break;
+        }
+    }
+
+    this.execCallbacks = {};
+    this.execId = 0;
+    this.exec = function(data, callback)
+    {
+        if (!callback) {
+            // if no callback is given, send directly
+            channel.send(data);
+            return;
+        }
+        if (channel.execId === Number.MAX_VALUE) {
+            // wrap
+            channel.execId = Number.MIN_VALUE;
+        }
+        if (data.hasOwnProperty("id")) {
+            console.error("Cannot exec message with property id: " + JSON.stringify(data));
+            return;
+        }
+        data.id = channel.execId++;
+        channel.execCallbacks[data.id] = callback;
+        channel.send(data);
+    };
+
+    this.objects = {};
+
+    this.handleSignal = function(message)
+    {
+        var object = channel.objects[message.object];
+        if (object) {
+            object.signalEmitted(message.signal, message.args);
+        } else {
+            console.warn("Unhandled signal: " + message.object + "::" + message.signal);
+        }
+    }
+
+    this.handleResponse = function(message)
+    {
+        if (!message.hasOwnProperty("id")) {
+            console.error("Invalid response message received: ", JSON.stringify(message));
+            return;
+        }
+        channel.execCallbacks[message.id](message.data);
+        delete channel.execCallbacks[message.id];
+    }
+
+    this.handlePropertyUpdate = function(message)
+    {
+        for (var i in message.data) {
+            var data = message.data[i];
+            var object = channel.objects[data.object];
+            if (object) {
+                object.propertyUpdate(data.signals, data.properties);
+            } else {
+                console.warn("Unhandled property update: " + data.object + "::" + data.signal);
+            }
+        }
+        channel.exec({type: QWebChannelMessageTypes.idle});
+    }
+
+    this.debug = function(message)
+    {
+        channel.send({type: QWebChannelMessageTypes.debug, data: message});
+    };
+
+    channel.exec({type: QWebChannelMessageTypes.init}, function(data) {
+        for (var objectName in data) {
+            var object = new QObject(objectName, data[objectName], channel);
+        }
+        // now unwrap properties, which might reference other registered objects
+        for (var objectName in channel.objects) {
+            channel.objects[objectName].unwrapProperties();
+        }
+        if (initCallback) {
+            initCallback(channel);
+        }
+        channel.exec({type: QWebChannelMessageTypes.idle});
+    });
+};
+
+function QObject(name, data, webChannel)
+{
+    this.__id__ = name;
+    webChannel.objects[name] = this;
+
+    // List of callbacks that get invoked upon signal emission
+    this.__objectSignals__ = {};
+
+    // Cache of all properties, updated when a notify signal is emitted
+    this.__propertyCache__ = {};
+
+    var object = this;
+
+    // ----------------------------------------------------------------------
+
+    this.unwrapQObject = function(response)
+    {
+        if (response instanceof Array) {
+            // support list of objects
+            var ret = new Array(response.length);
+            for (var i = 0; i < response.length; ++i) {
+                ret[i] = object.unwrapQObject(response[i]);
+            }
+            return ret;
+        }
+        if (!response
+            || !response["__QObject*__"]
+            || response.id === undefined) {
+            return response;
+        }
+
+        var objectId = response.id;
+        if (webChannel.objects[objectId])
+            return webChannel.objects[objectId];
+
+        if (!response.data) {
+            console.error("Cannot unwrap unknown QObject " + objectId + " without data.");
+            return;
+        }
+
+        var qObject = new QObject( objectId, response.data, webChannel );
+        qObject.destroyed.connect(function() {
+            if (webChannel.objects[objectId] === qObject) {
+                delete webChannel.objects[objectId];
+                // reset the now deleted QObject to an empty {} object
+                // just assigning {} though would not have the desired effect, but the
+                // below also ensures all external references will see the empty map
+                // NOTE: this detour is necessary to workaround QTBUG-40021
+                var propertyNames = [];
+                for (var propertyName in qObject) {
+                    propertyNames.push(propertyName);
+                }
+                for (var idx in propertyNames) {
+                    delete qObject[propertyNames[idx]];
+                }
+            }
+        });
+        // here we are already initialized, and thus must directly unwrap the properties
+        qObject.unwrapProperties();
+        return qObject;
+    }
+
+    this.unwrapProperties = function()
+    {
+        for (var propertyIdx in object.__propertyCache__) {
+            object.__propertyCache__[propertyIdx] = object.unwrapQObject(object.__propertyCache__[propertyIdx]);
+        }
+    }
+
+    function addSignal(signalData, isPropertyNotifySignal)
+    {
+        var signalName = signalData[0];
+        var signalIndex = signalData[1];
+        object[signalName] = {
+            connect: function(callback) {
+                if (typeof(callback) !== "function") {
+                    console.error("Bad callback given to connect to signal " + signalName);
+                    return;
+                }
+
+                object.__objectSignals__[signalIndex] = object.__objectSignals__[signalIndex] || [];
+                object.__objectSignals__[signalIndex].push(callback);
+
+                if (!isPropertyNotifySignal && signalName !== "destroyed") {
+                    // only required for "pure" signals, handled separately for properties in propertyUpdate
+                    // also note that we always get notified about the destroyed signal
+                    webChannel.exec({
+                        type: QWebChannelMessageTypes.connectToSignal,
+                        object: object.__id__,
+                        signal: signalIndex
+                    });
+                }
+            },
+            disconnect: function(callback) {
+                if (typeof(callback) !== "function") {
+                    console.error("Bad callback given to disconnect from signal " + signalName);
+                    return;
+                }
+                object.__objectSignals__[signalIndex] = object.__objectSignals__[signalIndex] || [];
+                var idx = object.__objectSignals__[signalIndex].indexOf(callback);
+                if (idx === -1) {
+                    console.error("Cannot find connection of signal " + signalName + " to " + callback.name);
+                    return;
+                }
+                object.__objectSignals__[signalIndex].splice(idx, 1);
+                if (!isPropertyNotifySignal && object.__objectSignals__[signalIndex].length === 0) {
+                    // only required for "pure" signals, handled separately for properties in propertyUpdate
+                    webChannel.exec({
+                        type: QWebChannelMessageTypes.disconnectFromSignal,
+                        object: object.__id__,
+                        signal: signalIndex
+                    });
+                }
+            }
+        };
+    }
+
+    /**
+     * Invokes all callbacks for the given signalname. Also works for property notify callbacks.
+     */
+    function invokeSignalCallbacks(signalName, signalArgs)
+    {
+        var connections = object.__objectSignals__[signalName];
+        if (connections) {
+            connections.forEach(function(callback) {
+                callback.apply(callback, signalArgs);
+            });
+        }
+    }
+
+    this.propertyUpdate = function(signals, propertyMap)
+    {
+        // update property cache
+        for (var propertyIndex in propertyMap) {
+            var propertyValue = propertyMap[propertyIndex];
+            object.__propertyCache__[propertyIndex] = propertyValue;
+        }
+
+        for (var signalName in signals) {
+            // Invoke all callbacks, as signalEmitted() does not. This ensures the
+            // property cache is updated before the callbacks are invoked.
+            invokeSignalCallbacks(signalName, signals[signalName]);
+        }
+    }
+
+    this.signalEmitted = function(signalName, signalArgs)
+    {
+        invokeSignalCallbacks(signalName, signalArgs);
+    }
+
+    function addMethod(methodData)
+    {
+        var methodName = methodData[0];
+        var methodIdx = methodData[1];
+        object[methodName] = function() {
+            var args = [];
+            var callback;
+            for (var i = 0; i < arguments.length; ++i) {
+                if (typeof arguments[i] === "function")
+                    callback = arguments[i];
+                else
+                    args.push(arguments[i]);
+            }
+
+            webChannel.exec({
+                "type": QWebChannelMessageTypes.invokeMethod,
+                "object": object.__id__,
+                "method": methodIdx,
+                "args": args
+            }, function(response) {
+                if (response !== undefined) {
+                    var result = object.unwrapQObject(response);
+                    if (callback) {
+                        (callback)(result);
+                    }
+                }
+            });
+        };
+    }
+
+    function bindGetterSetter(propertyInfo)
+    {
+        var propertyIndex = propertyInfo[0];
+        var propertyName = propertyInfo[1];
+        var notifySignalData = propertyInfo[2];
+        // initialize property cache with current value
+        // NOTE: if this is an object, it is not directly unwrapped as it might
+        // reference other QObject that we do not know yet
+        object.__propertyCache__[propertyIndex] = propertyInfo[3];
+
+        if (notifySignalData) {
+            if (notifySignalData[0] === 1) {
+                // signal name is optimized away, reconstruct the actual name
+                notifySignalData[0] = propertyName + "Changed";
+            }
+            addSignal(notifySignalData, true);
+        }
+
+        Object.defineProperty(object, propertyName, {
+            configurable: true,
+            get: function () {
+                var propertyValue = object.__propertyCache__[propertyIndex];
+                if (propertyValue === undefined) {
+                    // This shouldn't happen
+                    console.warn("Undefined value in property cache for property \"" + propertyName + "\" in object " + object.__id__);
+                }
+
+                return propertyValue;
+            },
+            set: function(value) {
+                if (value === undefined) {
+                    console.warn("Property setter for " + propertyName + " called with undefined value!");
+                    return;
+                }
+                object.__propertyCache__[propertyIndex] = value;
+                webChannel.exec({
+                    "type": QWebChannelMessageTypes.setProperty,
+                    "object": object.__id__,
+                    "property": propertyIndex,
+                    "value": value
+                });
+            }
+        });
+
+    }
+
+    // ----------------------------------------------------------------------
+
+    data.methods.forEach(addMethod);
+
+    data.properties.forEach(bindGetterSetter);
+
+    data.signals.forEach(function(signal) { addSignal(signal, false); });
+
+    for (var name in data.enums) {
+        object[name] = data.enums[name];
+    }
+}
+
+//required for use with nodejs
+if (typeof module === 'object') {
+    module.exports = {
+        QWebChannel: QWebChannel
+    };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/data/javascript_rc.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,6740 @@
+# -*- coding: utf-8 -*-
+
+# Resource object code
+#
+# Created by: The Resource Compiler for PyQt5 (Qt v5.5.1)
+#
+# WARNING! All changes made in this file will be lost!
+
+from PyQt5 import QtCore
+
+qt_resource_data = b"\
+\x00\x00\x0e\xfa\
+\x00\
+\x00\x39\xf5\x78\x9c\xcd\x1b\xed\x6e\xdb\x46\xf2\xbf\x9f\x62\x23\
+\x14\xad\x9c\xc8\xb4\x9d\xb4\xf7\x21\x5f\x0e\xe7\x38\x8e\xe1\xab\
+\xe3\x38\x17\xa7\x2d\xe0\x1a\xc6\x4a\x5c\x49\xac\x29\x92\x5d\x92\
+\x56\x74\x89\xde\xe6\xde\xe4\x5e\xec\x66\xf6\x7b\xc9\xa5\xa4\x14\
+\xb9\xa2\x44\x10\x85\xe4\xec\xcc\xec\x7c\xcf\x2c\xb3\xff\xf8\x0b\
+\x5e\x3b\xe2\x0f\x39\xc9\x8b\x25\x4f\xa6\xb3\x8a\xf4\x4f\x76\xc9\
+\xd3\x83\xc3\xef\xc8\xf5\x8c\x91\xb7\x15\xbc\x99\x17\x34\x5b\x92\
+\x8b\x2a\x8e\x82\x90\xdf\x92\xef\x53\xca\xff\xfb\x9f\xf4\x21\xa6\
+\x29\xcb\x4a\xf2\x92\x56\xf4\x3e\xcf\xca\x3a\xad\xc8\xf1\x8b\x01\
+\xa1\xe4\xfb\x97\xc7\x2f\xc8\x19\xcf\xeb\x82\x8c\x25\xba\x01\x49\
+\xb2\x49\xfe\x8f\xfb\x98\x8e\x22\x78\x04\x40\x75\x35\xcb\x39\x79\
+\x9d\xa4\x09\xcd\xc8\x8f\x79\x3a\x99\x90\xbf\xcd\xc5\x5d\xb4\xc0\
+\x3b\x03\xfb\x77\xc9\x45\x56\xd1\x71\x35\x24\xb3\xaa\x2a\x86\xfb\
+\xfb\x8b\xc5\x22\xfa\xb5\x8a\x92\x7c\x3f\x4d\xc6\xc0\x44\x92\x4d\
+\xf7\xd5\xd6\xae\x67\x49\x49\x26\x49\xca\x08\xfc\x16\x94\x57\x24\
+\x9f\x90\x4a\x6c\xee\x47\x36\x3a\x99\xd1\x2c\x63\x29\x99\xe7\x71\
+\x0d\x20\xe6\x15\xb9\xce\xf3\xf4\x3e\xa9\x22\x85\xe5\xab\xb7\xd7\
+\x77\x2f\x4e\xcf\xce\x2f\xef\x2e\xce\x4f\x4e\x2f\xdf\x9d\x0e\x2f\
+\xce\xae\x2e\x9e\x1e\x7e\x25\xb9\x99\xcf\x19\x1f\x27\x34\x25\x17\
+\x82\x3c\x23\xef\x4b\x3a\x65\xf8\x4e\x3d\x60\x25\x99\xe5\x69\x0c\
+\x7c\x91\x07\x9a\x26\x31\x0a\x42\xaf\x01\x6a\x92\x6b\x00\x9a\xd3\
+\x25\xa9\x61\x7d\x65\xb9\xce\x10\x0d\x1d\x8f\x73\x1e\xd3\x6c\xcc\
+\xc8\x22\xa9\x66\x82\x4b\x07\x85\x5a\x4f\xe8\x94\x33\x36\x67\x59\
+\x45\x0a\x9e\x3f\x24\x31\x8b\x0d\x38\x62\x79\x97\x4f\xaa\x05\xe5\
+\xb0\x4f\x0e\x22\x4f\x2b\xc6\x33\x5a\x25\x0f\x2c\x15\x0a\x09\x12\
+\x01\x98\x79\x09\xa4\x40\xde\x49\x06\xe8\x14\x3b\x64\xc1\x93\xaa\
+\x62\x99\x43\x71\xc4\xaa\x05\x83\x27\xcb\xbc\x26\x34\x8b\x1b\x06\
+\x14\x91\x57\xa0\x5f\xa3\x1d\x89\x57\xa0\xca\x50\x18\x59\x9c\x54\
+\x09\x18\x0d\x01\x51\xb5\x95\x2a\x80\xf7\x2c\x94\x44\x36\xa9\x39\
+\x70\xc8\x11\x09\x5a\x13\x9f\x53\x7c\xa9\xc4\xc7\x24\xcf\xe3\x8a\
+\xe0\x1b\x42\xab\x36\x56\x05\xb0\x57\x97\x5a\xcb\x67\x97\xef\xc9\
+\x05\x2b\x4b\xc6\xc9\x19\xcb\x18\x07\xc9\x5e\xd5\x23\x60\xba\xad\
+\xd7\x63\x5f\x7a\x56\x5f\xa8\xc1\x11\x43\x2e\x62\x52\x67\x31\xa0\
+\xb2\x62\x54\xe6\x65\xc9\x08\xa2\x61\x4a\x0f\x8c\x97\xb8\x9d\xa7\
+\xd1\x21\xa8\xcb\xdc\x3e\x23\x14\xcc\x18\x41\xcb\x19\x50\x18\x2d\
+\x05\xc6\x57\xa0\x04\x4f\xc1\xaf\x72\xa0\x2d\xe5\x81\x02\xa6\x45\
+\xc1\x28\x47\xb9\x83\x9a\x71\x81\x60\x55\x99\x72\x84\xa6\xfc\xf0\
+\xf4\x10\x21\x85\xc9\xba\x8f\x9f\xc1\x8a\x71\x5a\xc7\x42\xf5\x62\
+\x69\x41\xc7\xf7\x74\x8a\xb8\xc4\x76\xd4\xbe\x23\x72\x95\x32\x0a\
+\x7c\x73\xf6\x90\xb0\x85\xb6\xb8\x49\x9e\xa6\xf9\x42\x12\xb6\x2a\
+\xaa\x72\x02\x7b\xac\x39\x6b\x88\xa3\x43\x16\x88\x88\xb3\x5f\xeb\
+\x84\x0b\x4b\x2b\xc1\x3a\xd3\x14\x85\x3c\x67\x2a\x04\x94\x4a\xb1\
+\xd3\xac\x8e\x72\x3e\xdd\xd7\xfe\xb4\x9f\x4e\x8b\x34\x9a\x55\xf3\
+\x54\x6f\xce\xb1\x82\x16\x30\x38\xe8\x9e\xb7\x72\x0f\x84\x2f\x56\
+\x6b\x03\x39\x2e\xc1\xf4\xcb\x82\x09\xa7\x63\x1f\xc6\xac\xc0\xfd\
+\x0c\x9a\xc1\x72\x0a\x56\x51\x0a\x47\x18\x33\x8e\x8e\x43\x68\x2c\
+\x4d\x97\xa6\x62\x33\x18\x40\xc1\x8a\x61\x19\x4a\x4c\xdc\x11\x54\
+\x5b\xcc\xca\x31\x4f\x46\x52\xd8\xcd\x08\x0c\xea\x20\xa7\x9a\x26\
+\xa2\xd1\x36\x71\x18\x1d\x0e\x5a\x6a\x92\x1a\x86\x35\x77\xa7\x3f\
+\x9d\x9c\x5e\x5d\x9f\xbf\xb9\x8c\xaa\x0f\x95\x7c\x2d\x42\x21\xaa\
+\x91\xb9\x01\xee\xf4\xf2\xa5\x0e\x6f\x5f\xc9\xc7\x5f\xee\xda\xdf\
+\xd9\xe9\xa1\x67\x96\x15\x4f\xc6\x55\xef\x68\x67\xe7\x81\x72\xf2\
+\xd6\x86\xe0\xd7\x60\x01\xc0\xcf\xf5\xb2\x00\xd1\x3d\x27\x1f\x77\
+\x08\x5c\x65\x32\x05\x91\x0d\xc9\xe1\x40\xdc\x42\x50\x2b\x40\xa0\
+\xcb\xf7\x05\xd8\x36\x1b\x92\xa7\xf2\x71\x92\x25\x60\x07\xcf\xd4\
+\x4d\x9c\xc2\x9b\x6f\xe5\x4d\xcc\x46\xf5\x74\x48\xbe\xd3\x70\x0f\
+\xf9\x3d\x7b\xcd\x20\xd5\xc4\x43\xf2\x27\xf9\x10\xa2\x40\xc6\xc6\
+\xd5\x75\xfe\x4e\xd1\xfa\xb3\x5a\x9a\x94\xea\xd5\x2b\x9e\xcf\xf5\
+\xcb\xbf\xc8\x97\x25\xab\xae\x14\x2f\x43\xf2\x57\xf9\x8c\xb3\xb2\
+\x80\xd8\x04\xc4\x0f\x0f\x06\x3b\xab\xf6\x0e\x61\x57\x93\x3a\x1b\
+\xa3\xf6\xfa\x15\xa7\x19\x80\xf3\x6a\x20\xb8\x3f\xa1\x69\x3a\x02\
+\x85\xec\xee\xc8\x7d\x27\x13\xd2\xaf\x40\x12\xe8\x61\x1a\x92\x3c\
+\x7a\xfe\x9c\xf4\xf2\xd1\x2f\xc0\x52\x8f\x7c\xfa\x44\x9a\x00\x51\
+\xc9\xc0\xd5\x05\x94\xa6\xd3\xdb\x55\x82\x54\x3b\x2d\x73\xf0\x54\
+\xc6\x79\xce\xfb\x3d\x61\x5e\x0e\x77\xec\x03\xd8\x35\xda\xa1\x43\
+\x52\x52\x93\xd9\x00\x0c\x1f\xf1\x6b\xd4\x22\xae\xe4\xd9\x5c\xaa\
+\x8d\x8c\xd5\x0e\x8c\x8e\xa2\x1e\x79\x62\x48\xfb\x57\x8f\x9c\x81\
+\x83\x64\x90\x90\x87\x96\xd6\x10\x1e\x3f\x51\x7b\xb2\xe2\xd9\x85\
+\x67\xbd\x41\x63\x8f\x61\x50\xf1\x6a\x77\xf7\xc8\x10\xe5\xac\xaa\
+\x79\x26\xef\x57\x3b\xe2\x07\x35\x32\x36\xda\x40\x3f\x90\xaf\xf1\
+\x5f\x91\xdd\xf6\x73\x4b\xf0\x68\xc7\x02\x88\xfd\x3b\x4a\x04\x23\
+\xa4\xbb\xe2\xb5\x15\xb2\xd5\x9c\x7c\x2d\xf5\x81\x56\x9f\x4d\x3d\
+\x6d\x08\x1b\x03\x08\x40\xf8\xcf\x77\xe0\x9b\x12\x24\x99\x2c\xe5\
+\x3a\xbb\x8d\x95\x55\xa0\xe4\x3c\xf2\xb7\xec\xc2\xaf\x76\x02\xdb\
+\x89\xac\x96\x1c\xe6\xd5\xa3\x26\xff\x28\x21\xc5\x96\x82\x88\xf0\
+\xf6\x28\xb0\x41\x05\xb7\xe5\xfe\xa0\xf4\x2a\x59\xf7\xde\x4a\xb0\
+\xb1\xf1\x8c\x08\x80\x08\xf1\x37\x71\x8d\x31\xb9\x74\xc4\x8b\x48\
+\xc5\x89\x96\xbd\x69\x89\xc1\x0f\x84\x05\xe9\xc4\x4d\x1e\xf4\x35\
+\xe2\x8c\xde\x1f\x6d\x4f\xd3\xf8\xfb\x06\xaa\xff\x52\x70\x5f\x8a\
+\x6e\x23\x08\x6e\xa0\x7e\xe5\x41\x7f\x06\x0f\x31\x9b\x50\x28\xe1\
+\x03\xf8\xfd\x30\x02\x41\x55\x14\xb4\xda\xc4\x38\x1b\x33\xf0\xee\
+\x78\x08\x6e\xeb\x5a\xd0\x66\xaa\xab\x96\x09\xb3\x0f\x6c\xac\x43\
+\xa3\x48\x0a\xab\x23\xff\xe5\x39\xfa\xe3\x41\xe3\x61\xd3\x45\x07\
+\x26\x38\x85\x9c\xf5\x91\x79\xd9\x30\xb8\xfd\x7d\x7c\x9f\xe5\x36\
+\xb4\x41\xd6\xc4\xcc\x0e\xf9\x5e\x44\x82\x18\xea\x91\x71\x95\x2e\
+\x7d\xd5\x29\xe9\x37\x3d\x53\x5f\x6e\x50\xb2\x9b\xd6\xcc\xe8\xd5\
+\x7a\x6f\xe0\x5b\x97\xf5\x7c\xc4\x78\xf4\xfa\xf8\xa7\xbb\x1f\x8e\
+\x2f\xde\x9f\x06\xd8\x5c\x70\x5a\x04\x99\x30\x22\xd2\x48\xa0\x7f\
+\x11\x48\xba\xe8\x0b\xef\x9b\xd1\xf2\xcd\x22\xd3\x86\x03\x1a\x8e\
+\x7b\xbb\x2d\x6f\xf4\x8d\xe0\x04\xe8\xe5\x15\x11\xd2\xd7\x86\x20\
+\x92\x86\x36\x56\x48\xcb\x32\x6a\x87\x62\xdd\xd6\x22\x12\xec\x25\
+\xb8\x21\x7f\x87\x4f\x9e\x58\x68\xf7\x8d\xb1\x9d\x1b\xb5\xf2\x16\
+\x97\xaa\x87\xed\x25\xad\x68\xea\x06\x7f\x99\x0a\xb5\x15\xda\xe7\
+\x6e\x68\xd9\x32\xbe\xaa\xac\x6a\x77\xa1\x70\xdf\x68\x7f\x91\xf7\
+\xb7\x7e\xd0\x95\x0f\x9b\x8a\x90\x4f\x55\x00\x3c\x9d\x63\x53\x16\
+\x6b\xda\xea\xa9\xf5\x43\xca\xa7\xa5\x1b\x7d\x09\x4b\x21\xd0\x84\
+\x35\x0b\x6d\x44\xd6\xef\xbd\xcf\xe4\xfe\x62\x53\x8a\xa1\x12\x7d\
+\x3e\x31\x45\x0f\x87\xee\x73\x09\xdb\x8a\xf3\xab\x96\xd8\x74\x6c\
+\xdc\x42\x70\xc2\x57\x35\x81\xdf\x60\xa2\xe7\x2a\x4e\xe9\xb0\xdd\
+\x0e\x58\x58\x68\x34\xcc\x53\xf3\xb2\xb5\x85\x86\xad\x4f\xb3\x0d\
+\x06\xd8\xef\x88\x89\x31\x4b\x59\xc5\x36\xaf\x6f\xe7\xf9\x50\x9c\
+\xdf\x42\x9e\xd0\x8d\x91\x3e\x5a\x63\x82\x2d\x81\xc7\x55\x43\x90\
+\x1d\x25\xc1\x4d\x72\x7b\xd4\x82\xeb\x34\x6d\xe1\x80\x2d\xbb\x5e\
+\x6f\xdb\x8e\x7d\x17\xed\x34\xa6\xac\xac\x1c\xc8\xb0\xa0\x20\x12\
+\x56\x36\x94\x15\xb4\x72\xd7\x40\x9a\x96\x6e\x62\x56\xad\xda\x0c\
+\x34\x6d\x87\x7f\x6b\xef\x0e\x1b\x4d\xa2\x1b\x6c\xa3\xff\x11\x6b\
+\x9c\x61\x67\x92\xc7\x2e\x66\x15\x28\xea\x44\x43\xb3\x85\x76\xbd\
+\x90\xb6\x81\x96\xc0\x29\xa5\x38\xd4\x1a\x5e\x35\x42\xe0\x67\xf1\
+\x0e\x0d\xcd\x6a\xd0\x28\x93\x43\x96\x27\xc5\x79\x49\xe7\x38\xe5\
+\x22\x5d\xa6\x67\x4c\x2a\x63\x0b\xf2\xf6\x8d\xb8\xeb\xdb\xa5\x92\
+\xf1\x1b\xfb\xe0\x76\xa0\xb9\x0d\x16\x9a\x90\x2f\xb3\x7c\x41\xea\
+\x0c\xb3\x26\xb1\x56\x33\x20\x8b\x59\x02\x15\xe8\x5c\x0c\x37\x39\
+\x9b\x30\xce\x70\x12\x96\xe3\x8c\x09\xee\xa7\x49\x59\xc1\xa3\x58\
+\xf1\x53\x6e\xda\x4e\xc3\x01\x5a\xd1\xa9\xe1\x1f\x0e\xff\x91\xe4\
+\xed\xca\xb0\xd6\x0f\x6e\x04\xfd\xc6\xeb\x1d\x1b\x14\xdc\x77\xfd\
+\x75\x12\xf9\xcd\x76\x09\xbf\x68\x1f\xa6\x23\xd4\xca\xc9\x8c\x5a\
+\x40\xa8\x06\x8f\xee\x6d\x85\x1d\xdf\xdd\x25\xf1\xdd\x1d\x2a\x15\
+\x60\x25\x3a\x0b\x69\x84\x82\x2f\x6f\x4d\xa3\xb6\xa3\xd4\x77\x01\
+\x9a\xc0\xd9\xd3\xd8\x54\x86\xd5\x8c\x56\x64\xca\x2a\xd5\xe1\xc7\
+\xe0\xb9\xc0\x8e\x74\x4c\xc2\xe6\x49\x89\x63\x12\x97\xb8\x24\x20\
+\x93\x76\x29\xf8\x30\x49\x1d\x08\x9c\xd0\xf1\x4c\xcc\x82\x81\x80\
+\x67\x21\x32\x20\xc4\x60\x29\x38\xf9\x04\x3b\xaa\x20\x49\x68\x3a\
+\x50\x1d\x32\x99\x7f\x5d\x4a\x3a\x98\x08\x9c\x3e\x25\xcf\xb8\xfd\
+\x2d\xee\x7d\x91\xcb\x89\x1b\xd2\xa4\x94\x82\xdc\xf8\xa1\xd3\x61\
+\x28\xdd\x9a\x54\x99\x64\x65\x85\x63\x61\x10\xc9\x31\xe7\x74\x19\
+\xa8\x41\xcb\xba\x10\xad\x73\xaa\x94\xd3\xf4\x12\xbd\x61\x48\x9d\
+\xca\x95\x05\x26\x43\x23\x4a\x59\x36\xad\x66\x8d\x30\xea\x24\x29\
+\xac\xf3\xe1\xe7\x6f\xa4\xb1\xe2\x88\x3c\x79\x92\x84\x32\x07\x50\
+\x82\x14\x05\xeb\x54\x0a\xf1\x44\x60\xe8\x02\x48\x67\xe8\x56\x58\
+\x20\xd5\xe3\x4f\x97\x0f\x3e\xd2\xa8\xbc\x75\x9f\x3e\x11\xf3\xe2\
+\xa6\x77\x77\xa7\xe8\x3e\xbe\xbb\xeb\xdd\x36\x01\xcd\x8e\x12\x59\
+\xf2\xe3\xd8\x78\x82\xd3\xf6\xe6\xb6\x0c\x33\x12\xde\xe5\x28\x50\
+\x60\x8a\xca\xdf\xc1\xed\x97\x93\x01\x77\xd3\xcb\x6e\x77\x43\x54\
+\xd7\x2d\x38\xda\x09\x8b\x24\x58\x4f\x84\x7b\x07\x15\x90\xeb\xec\
+\x1e\xc2\xb3\x89\x25\x22\xf7\x9a\xed\x40\xe2\x15\x6d\x45\x5e\x57\
+\x32\xf7\xf6\x36\x16\x66\x9e\x5c\x7e\x7d\x13\xca\x25\x06\xff\x80\
+\x78\x7c\xbb\xc1\x8b\x38\x84\x14\x16\xc8\x9b\x50\x28\xe6\x4b\x16\
+\x47\x6a\x50\xd8\x37\x5e\xd5\x8a\xc5\x1b\x04\x2e\xd4\xae\xf0\x86\
+\x6c\x59\x55\x87\xeb\x55\xd0\x5c\x04\x5e\x09\xfb\x01\x7f\xc3\x71\
+\x30\xe6\x3c\x89\x25\x36\xb2\xad\x72\x42\x33\x08\x5a\x05\xd4\x3a\
+\x1f\x57\x4a\x0c\x21\x34\xbf\xd4\xe0\xd4\xb4\xc4\x40\x87\xa3\x7c\
+\x80\x45\x1d\x4c\x67\x64\x91\xd7\x69\x8c\x71\x90\xcc\xe8\x83\x9c\
+\xe6\x83\x54\x12\x4c\x93\x6c\x32\x01\x6c\x03\x32\xaa\x05\x03\x21\
+\xb4\x23\x96\x02\x57\x10\x81\xf5\x71\x40\x29\x22\x2e\xfb\x20\x8e\
+\x56\x52\x9b\x84\xd5\xc4\x1f\xcf\x86\x90\x84\xe4\x78\xde\x68\x7a\
+\x15\xd2\xcb\x37\xd7\xa7\x43\x39\xe2\x8e\x59\x95\xd7\x1c\x03\x33\
+\x68\x07\xb3\x18\x5f\xe2\x9e\x17\x39\xbf\xa7\x1c\x0f\x48\xc8\xdb\
+\xeb\x17\xef\xcf\xf6\xbe\x3d\x38\x78\x7a\xd8\xc2\x85\x06\xa3\xa3\
+\x37\xe6\x65\x6c\xfd\x6e\x02\x62\x36\x31\xca\x05\xc6\x0a\x60\x8d\
+\x3e\xf1\xf2\x70\x47\x45\x5d\xce\xfa\xee\xa3\xc0\xbc\x64\xd5\x4d\
+\x3b\x89\x3f\x20\x49\x0f\x67\x17\x61\x65\x4c\x8a\xbd\x1b\x6f\xcd\
+\x0d\x20\xba\x0d\x6c\x72\xd5\x11\x22\x57\x0e\x9b\x20\x7c\x28\x95\
+\xd0\x4a\xc5\x59\x06\x4d\x39\xa3\xf1\x52\xd4\x21\x09\xb4\x5e\xff\
+\x66\xe0\x60\x38\x34\xae\x66\x75\x49\xe6\x68\x52\x7a\x82\xa2\x9d\
+\x5f\x1c\x2f\x99\x7c\xdb\x72\xb8\x75\x75\x91\x0a\x52\x0a\xb4\x5d\
+\x38\x37\xd7\xba\x39\xb0\xb3\x35\xd2\xa2\x39\x97\xd2\x55\x89\xa4\
+\x95\xd4\x3b\x7a\xf2\x16\xdc\x8d\x83\xaf\x33\x31\x6d\xb5\xb8\xab\
+\xb5\xb6\xc3\xf9\x38\x56\xd3\x4e\x59\x9d\xbc\x14\xf1\x2c\x29\x75\
+\x9b\x78\x29\x4a\x17\x09\x12\x1a\x51\xc8\x55\xc2\x92\x9f\x13\x8b\
+\xe2\xe6\xc0\xb1\x0d\x0b\x77\x0e\x09\xeb\x83\x0f\x78\xe8\x00\xca\
+\x2d\xdd\x58\x9c\xb7\xe6\x80\x47\x5f\x2a\x82\x0e\xad\x52\xba\xc6\
+\x72\x78\x39\x43\x76\x0b\xd6\x7d\xf0\xd1\x20\xe4\x26\x9f\x17\x34\
+\xb6\xf3\x3d\x31\xdc\xc3\x08\xa1\xb8\xc1\x7f\xaa\xe2\x0e\xd3\x90\
+\xe5\x3f\xe0\x9b\xd6\x06\x43\xce\xd3\xd5\xd6\xb6\x2a\xd1\x1b\x47\
+\xa0\x8e\x89\x6c\x80\x83\x1a\x22\x14\x99\xb6\x5a\x2c\x23\x8f\x11\
+\xe3\x51\x9b\x57\x91\xd1\xc3\xa6\x43\xbe\xfe\xda\x35\x15\xa1\x02\
+\x93\x16\x3b\x75\x00\x81\x22\xcf\xc0\xeb\xd5\x99\x6e\x2c\x1c\xae\
+\x57\x40\x02\xe8\x11\xd3\xd4\x9b\xb9\x13\x2b\x28\x87\xaa\x1b\xe0\
+\x11\xcc\x86\x07\x37\xde\xc9\xb1\x40\x17\x31\x91\x61\x20\x49\x31\
+\xd9\x28\x60\x7c\x4a\x17\x74\x59\x8a\x96\x41\x14\xf1\x09\x10\xa2\
+\xa3\x5c\xa6\x2a\x62\x76\xa0\xb8\x09\xe2\x75\x72\xb1\x6c\x9c\x82\
+\x50\x78\xad\xef\xa8\x1a\xe7\x8f\x83\x4e\x34\x52\x89\x43\xab\x55\
+\xec\xa0\xba\xc1\xf5\xbc\xce\xd1\x75\x10\x76\xb5\x31\xd1\xac\x7c\
+\x22\xf6\x60\xf4\x0f\xe2\xad\x96\x21\x32\xe1\xf9\xfc\x0b\xb9\x6c\
+\xf3\xc9\xef\xe2\xb1\x3a\x95\x6f\x89\x26\x4a\xf0\xe7\xcd\xc4\x75\
+\xdf\x90\xf4\x05\x4a\x10\xf8\xde\xe1\x96\x82\x56\x35\x39\x34\x21\
+\xb1\x0e\x86\x98\x55\xa0\xaf\x0b\x0a\x57\x14\xe6\xa0\x09\x7c\xaa\
+\x59\x89\xb2\xdf\x4d\xea\x51\x59\xe0\x67\x1b\xb8\xcd\x01\x39\xec\
+\x90\xc1\x9a\x08\xb6\x1d\x11\xd9\x6e\x0a\x39\x1e\xfc\x21\x22\xdb\
+\x97\x8a\x40\xa1\x2f\x1d\xfe\x88\x61\xc8\x96\x3c\x5e\x79\xb7\xff\
+\xf8\xb1\x7c\xf3\x98\x9c\x8b\xf1\x8f\xec\x22\xec\x74\x08\x85\x8b\
+\x61\x5d\x06\x0c\xc9\x08\x1a\x67\x44\x8e\x31\x31\x60\x3b\x50\xba\
+\x1a\x58\xea\xc1\x8e\x41\x11\x29\x02\xfb\x7e\x91\x25\xa7\x4d\x52\
+\x60\x66\x58\xdf\xb7\x6e\x31\x50\xc4\x8e\xf1\xd8\x25\x50\x64\x59\
+\xcf\x2a\x37\x7b\xbc\xa8\x9b\xfc\x16\xde\x59\x1f\xe8\xb1\xf5\xab\
+\x08\xb6\x76\x0a\x35\x64\x7f\xab\x60\x6d\xdc\x97\x16\x45\xba\x34\
+\xa0\xde\x56\x1a\xf3\x92\xb5\xe7\x3c\x45\xe7\xb9\x84\xf1\x07\x0d\
+\xf2\x9a\x16\x4d\x29\x81\x3f\xc9\x99\x9b\xd5\xcd\x18\xcb\xe1\x35\
+\xa5\xba\x28\x45\x1d\x07\x42\xac\x81\xa9\xb2\x7e\xfd\x03\x4d\x6b\
+\x64\xcb\x01\xbf\xf1\x70\x35\xa2\xf4\xe6\xf2\x5c\x27\x02\x8f\x42\
+\x70\x24\x61\x98\x77\x22\x69\xa2\x2d\xb4\xa5\x52\x90\x85\xb4\x6f\
+\xdf\xbc\x07\xf8\x41\xa0\x7f\xfc\xb7\x4b\xe2\x1c\xbc\x00\xac\x38\
+\x92\x9f\xbf\xea\xee\xba\xd9\x87\x03\x4a\x5f\xae\xd8\x2b\xeb\x21\
+\xe7\x88\x01\x7f\xea\x53\x4a\xe3\x4b\xd8\xd8\xa9\x21\x6b\xd4\x98\
+\x36\x6f\xe7\x0b\xa5\x6b\xce\x6b\x2d\xc7\xdb\x53\xdb\x70\x36\x39\
+\xd8\x67\x39\xa7\x17\x50\xdc\x26\x4a\x7e\x2c\xd6\x9f\x8b\x9f\x97\
+\x81\xef\x7d\x50\x7d\xf2\xad\x6a\x96\x2c\x68\xab\x59\x92\xaf\xce\
+\x45\x7e\x77\xc0\x02\xad\x92\xc5\x78\xeb\x35\xaa\x01\x43\xc6\x33\
+\xdd\xc0\x64\x42\xc4\x97\xd6\x49\xb7\x67\x75\xce\x4c\x15\x90\xd4\
+\xe2\xd3\xca\x4d\x43\x55\xe7\xeb\x1f\xb3\x46\x4c\x59\xfd\x72\x2e\
+\x5c\x63\xe8\xca\xed\xb9\xb7\xb6\x1d\xf7\xf1\xbc\x2e\x88\x01\xf7\
+\x2a\xfb\x15\x17\x41\x6b\x7e\xeb\xdd\x6e\xce\x92\x3d\xdc\x50\x6f\
+\xdd\x79\x96\xfd\x6c\xb0\x9d\xe8\xf4\x17\x79\x1b\x33\x62\x4f\x2a\
+\xb5\x37\xb4\x76\x10\x00\xc2\x2d\x02\x08\xfe\x34\x8a\xf0\xc0\xcc\
+\xbe\x43\x41\x66\x6e\xff\x68\xdd\x30\x59\x5f\x72\x2a\x2f\x3e\xd7\
+\xdf\x30\x2b\xef\xa8\xe6\x44\x2a\x5a\x93\x52\xf4\x65\x81\xfa\x92\
+\x5e\x07\xbe\x76\x15\xd8\x28\x04\xdc\xb0\x11\xf6\xdc\x11\x14\xae\
+\x67\x0c\x02\x07\x7f\x27\xfe\xee\xdb\xe8\x3c\xc9\x43\x2e\xec\xa7\
+\x8f\xe7\xc4\x85\x6f\xf9\xb1\x37\xe8\x6b\xc0\x1e\x36\x60\x33\xa7\
+\xd6\x7c\x29\x0f\xd1\x3d\xf8\xa7\x0e\x3c\x7e\x71\x64\x86\x65\xcd\
+\xc8\x2c\x3e\xa7\x19\xd7\x9c\xe3\xb7\xf6\x0f\x98\x53\xdc\x75\x72\
+\xea\x99\xa8\x0f\xb2\xe1\x0f\xd5\xe3\xaa\x01\x49\x2a\x31\x02\xcd\
+\x5b\xf3\xb6\x02\xbb\xde\x12\xdf\x8b\x43\x4f\x17\x5f\xf3\xfc\xd3\
+\x8c\x8c\x55\x03\x1d\x8b\x86\x9a\xe0\xac\x9e\x2c\x59\xd5\x88\x5f\
+\x9f\x93\x1e\x85\x14\x9e\x35\x4f\x10\x9a\x62\x0b\x0d\xd4\x9b\x30\
+\xa0\x27\x11\x87\x82\x4d\x0e\x1e\x51\xc9\x82\x3f\x13\x69\xb6\x24\
+\x79\x51\x25\x73\x9c\x4a\x12\xba\xa0\x4b\x9c\xfd\x63\x13\x54\xf1\
+\x7a\x2c\xa7\x00\x74\x5c\xd5\x0a\xbc\x85\x2d\x48\xda\xb7\x0b\x68\
+\x89\x30\x98\x4c\x59\xdc\x5b\x77\xbe\x64\x07\x75\x4d\x9c\xf8\xd5\
+\x6b\xcd\x76\x83\x95\x83\x39\x81\x40\xcf\x36\x9f\xc1\x68\x85\xbb\
+\x7c\x0c\xda\x85\xe1\x24\x99\xd6\x9c\x8e\xf0\x33\x69\xa4\xe0\x07\
+\xa1\x29\x73\xda\x7a\xd2\xca\x38\x78\x85\xca\xa7\x2d\xf5\x1e\x6e\
+\xcd\x1a\xc8\xb6\x09\x5a\xa0\x4e\x51\xd9\x94\x33\x3c\x81\xc8\xbe\
+\xc1\x03\x08\xb0\xe7\x6c\x6d\x73\xab\x3f\xf4\x50\xb8\xa5\x17\xb9\
+\xb5\xa2\x72\x35\xaf\x17\xf8\xb9\x87\x5d\x6d\x53\xb5\x3f\xf7\xec\
+\x40\xd8\x39\xa3\x52\xc1\x3f\xd8\xcb\xb4\x1e\xa9\x91\x75\x47\x9d\
+\x28\x16\xf9\xba\x29\x5d\xdd\xf4\x05\xf7\x5d\x29\xe0\x61\x7b\x51\
+\xfa\xd2\xd1\xc6\x84\xc4\x20\x70\xca\x06\x36\x20\x00\x91\xcd\xf5\
+\x7f\x2f\xaa\x7d\x91\x3e\x6a\x1e\xcb\xf9\x3b\xfe\xac\xbe\x7f\x63\
+\x1c\x79\x68\x8b\x0d\xaf\xed\x9a\xe3\x4d\xa9\xdf\xf9\xce\x3f\xdc\
+\xe2\x6e\x9d\xfd\x05\xb0\x66\x1e\xc0\xbd\x7d\x74\x80\x8b\xad\x01\
+\xac\x1f\xed\x8d\xd0\xba\x3f\x39\xd2\x53\xdc\xd5\xff\xe5\x83\x02\
+\x71\xf2\x2a\x6b\x18\xdb\x5c\x9a\x5a\x59\xd3\x6e\x7c\x94\x65\x00\
+\x9b\xa9\xd9\x83\x57\x3d\x42\xbb\x65\x55\x1f\x59\x91\x8f\xad\x93\
+\x0d\xa8\x8b\x60\x05\xc4\x49\xbb\x6d\x53\xe1\x66\xce\xa7\x45\x11\
+\xcb\xea\xb9\xd7\x54\xa9\x5a\x5b\x7f\x64\x62\x81\xe4\x23\x5d\x59\
+\x80\x10\xf7\xf7\xbd\x81\x0e\xfe\x3f\x16\x61\xf7\x59\x1e\xb3\x5f\
+\xca\x1d\xa7\x28\x56\xff\x8f\x10\x1d\xef\x1b\x89\xff\x1b\x4d\x52\
+\xbe\x02\x6b\xc4\xef\x24\x4a\xef\x04\xc4\x31\x3f\xcf\x16\x25\x07\
+\x47\xc0\xc2\xff\x00\x9d\xa1\x00\xc6\
+\x00\x00\x24\x34\
+\x00\
+\x00\x86\x68\x78\x9c\xd5\x7d\xfb\x73\xdb\x38\xd2\xe0\xcf\xa7\xbf\
+\x42\xe6\x5d\xb9\xc4\x88\x92\xa5\x64\x76\x76\x86\x0a\xa3\xf2\xd8\
+\x9e\x8d\xeb\xcb\xeb\x62\x67\x67\xe7\x54\xba\x14\x1f\x90\xc4\x44\
+\x16\xf5\x89\x54\x12\xaf\xa4\xff\xfd\xba\xd1\x00\x08\x90\xa0\x1f\
+\xd9\xec\x55\x6d\xd5\xd4\x44\xc4\xb3\xd1\x68\xf4\x0b\xdd\xf0\xc9\
+\x93\xa3\x56\xfb\x49\xfb\xd3\xff\xde\xb2\xcd\x6d\xfb\xc3\x65\x7b\
+\xd8\xff\xa5\x3f\xfc\x19\xca\xb0\xf8\x2c\x5b\xdf\x6e\xd2\xf9\xa2\
+\x68\x3f\x1d\x0c\x87\x5e\xfb\xf4\xc3\xf5\xcb\xb7\xef\xaf\xfa\xc5\
+\xb7\xa2\xdd\x59\x14\xc5\xda\x3f\x39\xf9\xf4\xdf\xd8\x75\x9b\xf6\
+\xe3\xec\xe6\x24\x8c\xb2\x6d\xe1\x62\xd7\xf3\x6d\xb8\x6c\x2f\xd3\
+\x98\xad\x72\x96\xb4\xb7\xab\x84\x6d\xda\xc5\x82\xb5\x5f\x5f\x5e\
+\xb7\xb3\x4d\xfb\x6f\xef\x5e\xb5\xff\xce\x36\x79\x9a\xad\xda\x4f\
+\x65\xbb\xbc\x8f\x3d\x8d\x71\xfb\xd9\x66\x7e\x22\xaa\x05\x50\xa2\
+\x3e\xc9\xe2\xbc\x2f\x1a\xe1\xd4\x1f\x2e\xa1\xf2\xa4\xd5\x99\x6d\
+\x57\x71\x01\xc3\x76\x62\xef\x93\xbb\x93\x5f\xed\xcf\x9d\xd0\x8b\
+\xdc\xdd\x97\x70\xd3\x4e\x82\xb0\xbf\xca\x12\xf6\x26\xbc\x61\xfd\
+\x22\x7b\x95\x7d\x65\x9b\xb3\x30\x67\x1d\x77\x94\xce\x3a\x4e\xb8\
+\x61\xa1\x13\x04\x41\xe2\xee\x22\x68\xb9\x86\xef\x55\xf1\x06\xda\
+\x8f\x92\x20\xea\xaf\xa0\x13\x36\x3b\x0a\xfb\x8b\x0d\x9b\xed\xf7\
+\x47\xc9\x7e\x1f\x35\x8c\x77\x14\x04\xce\x4d\xb8\x76\xdc\x0d\x2b\
+\xb6\x9b\x55\x7b\x16\x2e\x73\x36\x0a\x83\xb8\xe3\xa4\x37\xf3\xc9\
+\x36\x67\x50\x1b\xfc\x4f\xa7\x9b\x74\x9d\xa9\xe3\x4e\x06\xd3\x11\
+\xb5\x3c\x3a\x0a\x8f\x8f\x97\x9d\xd0\x3d\xd0\x77\xe7\x24\x5d\xad\
+\xb7\xc5\x3e\x67\x4b\x16\x17\xfb\x82\x7d\x2b\x10\xcc\x7d\xb4\x2d\
+\x8a\x6c\xb5\xcf\xa2\x4f\x50\x7c\xd2\x2f\x58\x5e\x74\x12\x77\x0c\
+\xc0\x25\x69\x1e\x46\x4b\x96\xf8\x0e\x2e\x26\x19\x4b\x70\x23\x3f\
+\x72\xc5\xd0\x0a\x37\xf8\xb5\x13\x13\xc7\xf0\x5b\xac\x39\xef\xb8\
+\xfd\x70\x95\x5c\xb1\xe5\x0c\x7e\xcd\xd2\x65\xc1\x36\x25\x7a\x65\
+\x8f\x76\xdc\x8f\xb7\x9b\xb3\xab\xab\x4e\xb1\x48\x73\xcf\xf9\x92\
+\xe6\x69\x94\x2e\xd3\xe2\xd6\x71\x01\x8b\xce\x22\x4d\x12\xb6\x72\
+\xf6\xfb\xb8\xcf\xbe\xad\x37\x62\x9c\xbc\x4f\xe5\xbc\x93\x7b\x70\
+\xfb\x4b\xb6\x9a\x17\x8b\x43\xdc\xdf\xa6\x01\xfe\x6f\xbf\xdf\x1d\
+\x38\x9e\xf1\xa3\xff\x85\x48\xc5\xdd\xe1\x20\x05\x5b\x25\x1d\x2c\
+\xf6\x76\xa2\xdc\x77\x88\x6c\x1d\xaf\xf5\x99\xdd\x9e\xc1\x5e\xf8\
+\xbb\xd3\x57\xd7\xfe\xf0\x17\xef\xb7\xd3\xb3\xff\xba\x7a\x77\x7a\
+\x76\xe1\xff\xe2\x9d\x9d\xbe\xbb\xfa\xf8\xea\xed\xd9\x7f\xf9\x4f\
+\x07\xde\xd9\xdb\xd7\xaf\x4f\xa1\xc5\x2f\xf4\xeb\xcd\xb9\xff\xeb\
+\x50\xfe\xfc\xf8\xea\xe2\xf7\x6b\xfd\xfb\xfd\xe5\xdf\x5e\x42\xc1\
+\x33\x28\x78\x73\xfd\xfe\xed\x2b\x7f\xf8\x57\xef\xfc\xe2\xd5\xc5\
+\xf5\x85\xff\xd3\xcf\xde\xf9\xdb\x3f\xde\xf8\x3f\x0d\xbc\x0b\x18\
+\xe4\xd9\x5f\xe0\x9f\xeb\x8b\xf7\xfe\xf0\x99\x77\x71\x05\x33\x5e\
+\xf8\x4f\xff\xea\xbd\x7c\xfb\xfa\xc2\x7f\xf6\xb3\x77\xf9\xe6\xea\
+\xe2\xfd\xb5\xff\xd3\x5f\x3c\x3e\xc3\xb3\xbf\x7a\xaf\x2f\xde\x7c\
+\xc0\x81\xdf\x7c\x78\xfd\xee\xf4\xfc\xe3\xe9\xf9\xb9\x3f\x1c\xfc\
+\x55\x7e\x9e\x5f\x9c\x5d\xbe\x3e\x85\xe9\x86\x03\x55\x74\xf9\xf7\
+\xcb\xf3\x0b\x28\x19\xca\x12\x31\xdf\xe0\x17\x59\xf0\xfa\xc3\xab\
+\xeb\xcb\x77\xaf\xfe\x84\xb2\x9f\x65\xd9\xd5\x87\xdf\xae\xdf\x9f\
+\x9e\x01\x4e\x06\xbf\x7a\xef\x4e\xff\x76\xf1\x91\x43\xfd\xec\x27\
+\xfa\xf8\xf0\xce\x7f\xf6\xcc\x7b\x77\xf1\xfe\xf2\x2d\x00\xf0\xeb\
+\xc0\xa3\x15\x3f\xfb\xd5\xbb\x7a\x79\x09\x90\x0e\x7f\xf6\x08\x8b\
+\xcf\x9e\x7a\xd7\xa7\xbf\xf9\xbf\x7a\xd8\xe3\x17\xef\x8f\xcb\x37\
+\x30\xce\x15\xe0\xea\x70\x70\x47\x71\x7f\xb6\x92\x1b\xb4\x5b\x6f\
+\xb2\xf5\x69\x51\x6c\x7c\x5e\x8a\x5f\x48\x03\xf0\x33\x84\x42\xef\
+\xe3\x2c\x8b\xb7\x39\xd5\xf1\x9f\x1e\x15\x28\xfa\xe2\xc7\x55\x90\
+\x58\x71\xbb\x66\xd9\xac\x1d\x22\x3d\xad\xb6\x37\x11\xdb\x38\x63\
+\x24\x9c\x3e\x0b\xe3\x85\x4e\x92\x74\xbc\x5b\x58\x37\xca\x59\x71\
+\x9d\xde\x30\x60\x48\x7a\x8b\x18\xce\x07\x4d\x08\xc7\x3d\x3a\x3e\
+\x8e\xfa\x71\xb8\x5c\x42\xe1\xc1\x83\x23\xe1\xfa\x7c\x58\x02\xae\
+\x1f\xae\xd7\xcb\x5b\x22\xeb\x70\x33\xdf\xde\xe0\x99\x80\x76\x79\
+\xbc\xc9\x96\xcb\x77\xfc\x8c\xf8\x95\xc9\x43\x3c\xdd\xfd\x68\x93\
+\x7d\xcd\xd9\xa6\x7f\x93\xa7\xec\xf8\xf8\xa4\x93\x17\x61\x91\xc6\
+\xfb\x0d\x5b\xc2\xbf\x5f\x98\x2b\x4e\x2a\x9f\x2b\xce\xf3\x8e\xb3\
+\xce\xf2\x14\x47\x71\x5c\x77\xbf\x07\x2e\x9a\x67\xcb\x6d\xc1\xee\
+\x6a\x46\xeb\x2f\x0f\x6a\xd3\xf1\x3c\xe9\xc8\x59\xf7\x72\xd8\xfd\
+\x2c\xfd\xc6\x12\x09\x44\xe5\xf8\xaa\x29\xbc\xa1\xeb\x22\xec\xe1\
+\xb6\xc8\xf6\xb4\xe4\x86\x2e\x19\x9c\xc2\xd9\x32\xfb\x8a\x5d\xba\
+\x0d\x75\xbd\xdb\x3b\x6b\xbf\xf1\xe9\x80\x0d\xb0\xff\xee\x0c\xc4\
+\x26\x3c\x64\x6d\xf7\x02\xd7\xfa\xf1\xd0\x09\x16\x7d\xc2\xb1\x78\
+\xf7\x4e\x02\x1f\x26\xbe\x36\x06\xb2\x03\x92\x42\x0a\x72\xfd\xf0\
+\xe0\xfd\xf3\x12\x04\xe2\x37\x8d\xd8\xdd\x1d\xf0\xba\x10\xe4\xc5\
+\x27\x29\x2b\xca\x21\xa9\xb1\x03\xf4\x89\x0c\x91\x97\xd3\xa8\xee\
+\x0e\x45\x09\x16\x80\xe4\x70\x47\xb3\x6c\xd3\x41\x1a\x8c\x46\x72\
+\xda\xe3\xe3\x10\x6a\x60\x54\x39\xf9\x88\x64\x5a\x05\x52\x1c\x36\
+\xc2\xb3\x25\x29\x04\xb8\x35\xff\x96\xa4\x23\xbf\xf9\x92\x1d\x1c\
+\x03\x36\x27\x67\x97\xab\xa2\x13\x1a\x30\xba\xde\x70\xc0\x87\x3b\
+\x4a\xf3\x37\xe1\x9b\x0e\xca\x9a\x08\xe6\x1f\xc8\x55\x45\x87\x50\
+\xc9\xd4\x8e\x7b\x10\xe2\xad\x3d\x38\x78\x42\x60\x5d\x71\x09\x87\
+\x3c\xbd\x2e\x69\xf8\xd2\xa3\x14\x98\x0b\xec\x72\xbe\x5d\xaf\xb3\
+\x4d\xd1\x27\x91\x08\x27\x6c\x53\x8c\x1d\xed\xc3\xf1\x5b\xce\x4d\
+\x06\x22\x36\xc9\xbe\xc2\x1a\xbb\x0e\x08\x8c\x5e\x75\x12\xc7\xd3\
+\xb7\x00\x00\xdb\xb0\x2f\x00\xd9\x39\x9b\x85\xdb\x25\x02\x08\xc7\
+\x9d\xad\x1e\x04\xd7\x76\xc5\x21\xb3\x4f\xe3\x12\x77\xe4\xec\x6a\
+\xe2\xfc\x91\x26\xc5\xc2\xf1\x9c\x97\x0c\x75\x2b\x67\xea\x99\x4c\
+\x4f\xc9\xe5\xa4\x33\xf3\xe6\xde\x8d\x47\x72\x0f\xfb\x32\x4f\x9b\
+\x7d\xde\xa3\x6d\xf8\x7d\x99\x85\x1a\xdd\xcf\xe0\x10\x87\x49\x92\
+\xae\xe6\x4e\x97\x13\x72\xb1\xd9\x32\xa4\xc6\x01\x6e\xcc\x8d\xdb\
+\xdc\x2d\xca\x36\xa0\xa6\x51\xaf\xae\x84\xd2\xe8\xbd\xba\xa3\xf7\
+\x0d\xf0\xc8\x74\x55\x9d\xf3\x20\xcf\x4b\x7b\x7e\x40\xe2\x64\x01\
+\xa7\x24\x1a\x7c\x3c\x71\x5e\xb1\x59\x01\xa8\x78\x4f\x98\xf0\x27\
+\xce\x75\xb6\x86\xef\xdf\x32\x50\x6c\x6e\x00\x35\x0b\xd0\xb9\x0c\
+\x9d\xca\x4b\x83\x5d\xba\x5a\xb1\x0d\x1f\x82\x04\x48\xf9\xed\xf1\
+\x9f\x84\x58\xad\x8e\x0a\xbc\x16\x08\x03\xa3\x63\xf9\xed\xf1\x9f\
+\x7a\x47\xad\xe0\xc0\x25\xdb\xc4\xe1\x63\x39\xdd\x68\x1a\xa8\x6d\
+\x98\xf1\x93\x3b\x0b\xb4\x93\x9b\x6a\x0d\x49\xba\x70\x25\x67\xa4\
+\x53\x4b\x55\x70\xd1\x31\x76\xf9\x61\x5a\x78\x09\x71\xa0\x19\x50\
+\xed\x1a\x0e\x15\x50\xa1\x00\x80\xc3\x54\x01\xc0\x9b\x73\x10\x84\
+\x8c\x9c\x1d\x95\x32\x52\x03\x48\x75\x2c\x01\x82\xe1\xbf\x03\x24\
+\xbe\xb3\x30\xa5\x82\x8c\x08\x5b\x2a\x65\xa8\xe2\x4d\x1c\x1f\x36\
+\x6e\x97\x84\x45\x68\x88\x73\x2f\x51\x5a\x26\x68\x75\x58\x0d\xa5\
+\xc9\xe4\xd9\x14\x0e\x19\x17\xb8\x78\x66\x0c\x9e\x28\xc0\x43\xdd\
+\x5d\x30\x94\x98\x6b\x0f\xf0\xed\x14\x21\x9e\x37\xe4\x39\xc0\x9a\
+\x3d\xf8\x8a\x6a\xdd\x39\x33\x0c\x54\x97\x96\xd6\xc7\x4b\x02\xc9\
+\xa1\x04\x12\x3a\xa8\xc7\xbf\x00\x4e\x75\x7c\xcc\xe7\x4b\x68\x69\
+\x55\xed\x22\x54\xbc\xb4\x1f\x65\xc9\xad\x87\xec\x14\xf4\x04\x58\
+\xfd\xd9\x22\x5d\x26\xc0\x47\x55\x7d\x0c\x1a\x7a\xc1\x2e\x96\x0c\
+\xbf\x3a\x4e\x92\x7e\x01\x58\x4b\x64\x45\xfd\xbc\xb8\x5d\x32\x6f\
+\x77\x93\xae\x04\xdd\x39\xc3\xc1\x00\xd0\xea\x2d\xc4\x27\x8a\x37\
+\xc7\x13\xa7\xd9\x1f\x78\x74\x40\x89\x7e\x07\x1c\xf1\x92\x0b\xaa\
+\x31\xe0\xb8\x64\xb3\x19\xe8\x3e\xe2\x33\x08\x60\xcc\x91\x95\x5d\
+\x06\x4e\xb6\xd2\x19\x66\xba\xe2\xa2\x63\xc3\x6e\x40\xf2\x89\xd5\
+\xb8\x04\x24\x9a\x13\xeb\x65\x78\x0b\xa4\x95\xad\x98\x63\xee\x39\
+\x2a\xe2\xeb\xe5\x16\x0e\xbf\xbf\x03\x50\x6b\x7b\x1e\x72\x85\x7e\
+\x12\x4e\x51\x09\x2c\x32\x24\x54\x25\xab\x58\x1b\x66\x4d\x38\xeb\
+\xe5\x23\xe4\x13\x36\x0d\xf4\x8f\xfd\x7e\x32\x1d\xe9\x05\xfd\xf5\
+\x36\x07\x26\x0a\x23\xc3\x07\x90\xce\xc1\x43\x8a\xae\x4d\x0a\x27\
+\xa2\x13\x69\x23\x45\x53\xd8\xd8\x56\xd8\x67\xb4\x1d\x20\x16\x35\
+\xcb\xce\x55\xe0\x04\x83\x11\x7b\x1e\x09\xf1\x39\x62\xdd\xae\x1b\
+\xf6\xb3\x35\x0e\x0c\x43\xc0\x84\xd0\x6f\x0a\x22\x0d\x7f\x0d\xa7\
+\x42\x41\x54\x83\x7a\x09\x07\x27\x5b\x15\x21\x4c\x69\x57\x65\x4b\
+\xea\xc8\x6e\x10\x80\x73\xf1\xfd\x4e\x48\x63\x30\xd4\x1a\x6a\x50\
+\x98\x0e\x7f\xf6\x51\x49\x00\xbd\x15\x9b\xd1\x3c\x50\x7e\xf0\x16\
+\x61\x7e\xc5\xd5\xa0\xca\xac\x80\x06\x6e\xd1\x71\x21\xad\x54\x21\
+\xdd\x30\x33\x6d\xd3\x08\xc7\xe6\x1c\x7a\x89\x8c\x19\x24\x2a\x1f\
+\x95\x73\x69\x5f\x7c\x20\x87\x1e\x91\xa6\x4d\x9d\x50\x75\x01\x04\
+\xbf\x50\x52\x1e\x79\xc4\x08\x8b\x82\x21\x18\xcd\x54\x47\xdf\x03\
+\xc9\x72\x92\x83\x97\xe6\x6f\x01\xa0\xd3\x6f\x69\xde\xc0\x26\xda\
+\xe1\x0b\x5c\xe9\xf3\xa8\xab\x5a\x57\x5a\x7a\xcc\x03\x96\xaf\x99\
+\xa2\x60\x2b\x96\xc3\x22\x7b\xf1\x16\xb8\xef\xd5\x8a\x08\x3a\xa6\
+\x78\xc0\xe1\xbf\x0e\x39\x3f\xdc\x51\x6b\x74\x52\xf5\x86\xc0\x51\
+\x9b\xb3\xe2\x3f\xd9\x29\x72\x42\x4b\xa8\xf8\x46\x22\xf4\x8d\xa0\
+\xd6\xd7\x8f\x97\x2c\x5c\x9d\x03\x2f\x26\xf6\xf6\x39\xd0\x8a\x46\
+\xda\xef\x40\x67\xac\xf2\xb8\xc4\xc1\xc0\x4b\x46\x1d\xdc\xe2\x78\
+\xea\x1e\x05\xab\xed\x72\x39\x8a\xe1\xd0\x14\x9b\xdb\x5d\x84\x96\
+\x56\x01\x98\x9a\x83\x18\x0d\x57\xc9\x12\x54\x78\x87\x98\x0b\x08\
+\x8e\x38\x2c\x50\x9f\x71\x77\x87\xcf\xe8\x8b\x38\x30\x20\x24\x0e\
+\xc1\x12\x20\x00\xe9\x4b\x0d\x47\xda\x6f\x0d\x02\x2f\x36\x75\xaf\
+\xaa\xe8\xe2\x7e\x03\x97\x7b\x69\xd0\x39\x23\xec\x87\xd0\x9b\x60\
+\xeb\xa9\x74\x36\xb8\x51\xc7\x79\x02\x0a\x0e\x97\x72\xc0\xb8\x3a\
+\xb2\xba\x3a\x1c\xad\x86\xda\xdd\xb3\x20\xa0\xdd\x43\xa9\xf2\x2c\
+\x49\xda\x8a\xbe\x1e\xc2\x8d\x24\x17\xf5\xbf\xf2\x4d\x31\x56\x84\
+\x64\x4f\x2c\x28\xec\x03\xaf\x4d\x41\x62\xf4\xb9\x4b\xc8\x9b\x8d\
+\x42\xb3\x6c\x38\x1d\xcd\x02\xd6\x75\x7a\x4e\x37\xe4\xca\x36\x74\
+\x4d\x82\x78\x14\x03\xea\x68\xbb\x61\x0a\x25\x89\x27\xb3\x69\xd0\
+\x52\x33\x2d\x4a\x19\x1c\x91\x0c\x5e\xa0\xe1\x3b\x42\x96\x16\x44\
+\x9c\xe1\xee\xe8\x0b\x18\x76\x09\xe0\x02\xb5\x0c\x65\x04\x2b\xf3\
+\x82\xac\x65\x12\x74\x34\x33\x6f\x09\xea\x4a\xb0\x62\x5f\xdb\x00\
+\x92\xe4\x9e\x41\x24\x05\x06\x57\x20\x76\xc0\x27\x65\x95\x2b\xa7\
+\x2b\xe5\x43\xa5\x75\xec\xed\xd0\xef\x96\xaf\xc3\x98\xf9\xcc\x23\
+\xec\xa1\xbb\xcd\x0f\xc5\xc7\x05\xaa\xed\xef\x36\x0c\x2c\x14\xbf\
+\x36\x5a\xbf\xd6\x66\xbf\x97\x1d\x7f\x03\xa5\xf2\x6c\x19\xe6\xc0\
+\x83\xc0\x06\x01\x50\x44\x63\xb0\xe1\xf1\x5f\x64\x33\x34\x1c\xe2\
+\xc8\xac\xab\x10\x24\x92\xaa\x81\x33\xb5\xa1\x42\x35\x4b\x90\xad\
+\xe6\x40\x40\xa0\x99\x7b\xb3\xe0\x74\xb3\x09\x6f\x35\x20\x73\x3c\
+\xd1\x44\x30\x0a\xd1\x60\x77\x82\x06\xcc\xdd\x19\x49\x70\xc4\x8e\
+\x8f\x67\xd2\xa0\x94\x08\x12\x32\x08\x4f\x1e\x90\x37\x22\x2b\x99\
+\xa2\x68\x00\x72\x04\xdd\xd4\xf5\x5b\x09\x52\x08\xf4\x4c\xfa\xf1\
+\x22\xdc\x9c\x16\x60\xc1\x22\x1c\x1f\x15\xe3\x07\x31\xd7\xec\x4c\
+\x99\x07\x82\x4a\xc8\x0b\x82\xda\xf7\x1c\x9d\x26\x69\xfe\xbb\x6c\
+\x38\x9f\x24\x53\x77\x8c\xff\x17\xb0\xcc\x41\xb3\xf4\xe7\x38\x6d\
+\x0a\xb2\x0a\x9a\xa7\xdc\xae\xdd\x2d\x82\x74\xa4\x0b\x9b\x83\x74\
+\xb7\x3c\x64\xe2\xd1\x7c\x3c\x17\x04\x83\x9a\xda\x0e\xac\xf1\x8f\
+\xe9\x0a\x4e\x84\xeb\x1b\x0d\x3d\x4e\x77\x9d\x84\x8e\xb5\x5b\x9e\
+\xc5\xc5\x01\x37\xf0\x8f\xfa\xc1\x7b\x20\x5d\xf3\xc3\xab\x46\xd0\
+\x48\x75\xa7\x91\xa3\x43\xbf\x1d\x0b\x55\x3a\x8e\x27\xe8\xdd\xdf\
+\x29\xe7\x2c\xe1\xc1\x33\x66\xf2\xab\x64\xc5\x57\x17\xf3\x05\xf5\
+\xcb\xb9\x68\x81\x23\x42\x20\xe9\x1f\x41\xd4\x89\x45\x89\x3c\x75\
+\xad\xda\xb1\xd3\xab\xe9\xe3\x23\x8c\x78\xc6\x01\x78\x4b\xc5\x60\
+\x64\x01\xc2\x49\xc2\x73\xda\xd3\x27\x21\x03\x5c\xf0\xbd\x3e\x99\
+\x7b\x3a\x58\xda\x3e\x26\xfd\x84\x01\xbd\x67\xb7\x68\x48\x8f\x74\
+\x9c\x76\xe4\xa7\x60\xa7\x1d\x87\xca\x1d\x59\x4e\x5b\x0b\x98\xa9\
+\xc2\x66\x31\xbf\xa3\xfe\x0d\x2b\x42\xc4\x12\x52\xa6\xfc\xdd\xc7\
+\x4d\xd3\x01\x47\x1f\xc9\xa4\x02\xed\x54\xe1\x5e\x1f\x17\x0a\x71\
+\x7e\xb3\x48\x2c\x45\x2f\x34\xd0\xa2\xec\xff\x1a\x4a\x5c\x21\xc0\
+\xce\x25\x95\xea\x75\x23\xad\xa0\xe3\x3e\x60\x14\x74\xa9\xe2\x25\
+\x45\x1a\x4a\x37\x43\xe2\xc8\x3a\xce\xc8\xf4\x29\x14\x77\xeb\xb6\
+\x1c\xd5\xbc\xbd\x4d\x7b\xe8\x91\x64\xda\x00\x07\x41\xb1\x4d\xde\
+\x0d\xb1\xca\x83\xa0\xe1\x0a\x8d\x8a\x5b\x15\xae\x08\x56\xce\x52\
+\xa0\x3b\x7f\x94\x6f\xd8\x24\x43\xf2\x6b\x69\x3e\x5e\xc1\x24\x49\
+\x7d\x0d\xaa\x4e\x31\xa9\x8f\x87\x53\x60\x8a\x20\xac\x12\xe4\xba\
+\xf1\x81\x08\x07\x2c\x1f\x49\xc5\x89\x61\xdf\xc2\xa6\x96\x75\x35\
+\x33\x31\x26\x42\x8f\x88\x17\x85\x25\x15\x83\x8a\x89\xde\x97\xb2\
+\x2f\x2f\x39\x34\x0e\x5d\xc1\x4c\x05\x60\x10\xd2\x88\x23\x5c\x63\
+\x89\x7b\x83\x02\x26\xf1\xd8\x01\x5d\x84\x6f\x1a\x48\x6f\x6d\x5f\
+\x9d\xa9\x7d\x67\xef\xde\x58\x32\x7e\x2b\xf4\xe2\xb5\xe2\xca\x02\
+\xc8\xc9\xd5\xb4\xfb\xda\xea\x4b\xb8\x3d\xce\xbc\x5c\xe5\xb9\x7b\
+\x5c\x67\xee\x20\x02\xc4\x09\x06\xe0\xdb\x55\xa1\xea\x7e\xa3\x86\
+\xc3\x99\x2a\x32\xba\xb8\xcf\xf9\x2f\x47\xa7\x86\x19\x8d\xe9\x8e\
+\x43\xdf\x5e\xd1\x05\x93\xc8\xbc\xf9\x4b\x82\x44\x5e\x39\x81\x62\
+\x02\x40\xa5\xab\x70\xc9\x7b\xa0\x01\x0b\x74\x81\x3f\xf9\x2d\x86\
+\xa4\x6c\x65\xc3\x82\x7e\x36\x02\x9d\xd8\x6c\x34\xe9\xf5\x10\x5e\
+\x54\xbe\x2a\xe3\x41\xd1\xe1\x60\xb0\x0e\xc9\x04\x71\xe1\xf2\x02\
+\xb0\x63\xc8\x58\x06\x46\x0c\x2b\x5d\x37\x1a\x4b\xe3\xd8\x02\x04\
+\xf0\xcd\xc0\x0b\x96\x34\x17\xde\xcb\x77\xe4\xcb\x64\x49\x07\xc4\
+\xe0\x3d\x76\xce\x6b\x74\x94\xfe\x47\x9b\x39\x7c\x05\xa2\xd5\x39\
+\x43\x7f\x4c\xee\xc3\xef\xff\x21\x5a\x01\x8c\x42\x77\xfb\x94\x57\
+\x6d\x21\xc9\xbf\x84\x05\x5c\x7a\xea\xfb\xdc\x7f\xbc\x5d\xeb\xba\
+\x89\x68\x76\x28\x35\xc5\x8e\x03\x83\xf3\x96\x8e\xb7\x53\x22\x3e\
+\x0e\x57\x31\x5b\xfa\x8e\xcf\x2f\x6e\x05\xdb\x74\xf0\xac\x14\x58\
+\xe3\x0f\x41\xa0\x2c\xc3\x5b\x7f\x00\x67\x80\x77\xbe\xac\x48\x1c\
+\x72\x36\x35\x89\x5f\xe5\xd9\xbe\x4b\x02\x97\x56\x52\xd8\xa7\x49\
+\xce\xa1\x47\x07\x8d\x10\x31\x4c\x0c\xe8\xfd\x7c\xcf\x10\xc8\x9b\
+\xe1\xb0\x02\x8d\x49\x55\xa4\x5f\x00\x97\x67\x85\x17\x6a\x5d\xba\
+\x8e\x74\x9e\x9f\xe1\x98\x9c\xd2\x1d\x17\xd5\x17\x4d\xf8\x3d\xa2\
+\x27\xba\xac\x8a\x6c\x7d\x79\x73\xc3\x92\x14\x58\xda\x3b\x38\x54\
+\xe1\x3c\x24\xe4\x54\xd5\x48\xc2\x10\xf7\x62\x31\xb9\x41\x02\xad\
+\xe7\x0f\x17\xdb\xad\xaa\xc4\x55\x63\x00\xd6\xaa\x77\x35\x68\x73\
+\x11\x7b\xe3\x4d\xae\x68\x6e\xa9\x39\xf2\xb2\x0f\x6b\x68\x3b\xd2\
+\x0a\x70\x1c\xbe\x3c\x10\x95\xa5\xd0\x01\x53\x00\xd0\xb1\x48\x63\
+\x90\x95\x43\x6f\x2e\xad\x05\x9d\xf5\xf5\x89\x9a\x4a\xc9\x88\xfe\
+\x1f\x42\xa5\x8a\x3c\x18\x47\x1d\x59\xe6\x82\xd9\x9e\xe5\xea\x5e\
+\xca\x1c\x44\x5a\xbf\xbe\xf2\xdd\x1c\xcd\xf6\xfb\xf9\x7e\x7f\xa4\
+\x41\x7a\x16\xae\x01\xc1\x60\xfd\xb8\x86\x4b\x87\xb7\x10\x68\x05\
+\xe2\x7d\x0d\x9a\xf4\x91\x31\x05\xa7\x69\x3e\x66\xbd\xa9\xab\x63\
+\x02\xcb\xf0\x76\x76\x13\x34\x5c\xd3\x56\xa6\xc1\xe9\x4d\xa5\x81\
+\xe6\x2a\xaf\xc4\xc4\xc0\xe2\x7c\xbd\x46\x8d\xdd\x35\xb6\x43\x8e\
+\x85\x8b\xb2\x6c\x1d\xdd\x1a\xeb\x65\xd0\xf0\x28\x28\x5d\x5c\x47\
+\xf5\x3e\xb6\x2b\xa3\x91\x86\x30\xe0\xf3\xc6\xc9\x91\x1b\x54\xd5\
+\xe5\xed\x47\x00\xb5\x59\xed\xf0\x3c\xae\xb3\x4e\x77\xaf\x71\x08\
+\x20\x78\x38\x3e\x9a\xd1\xca\x34\xb7\x59\xd9\x0e\x8a\x0f\x23\x83\
+\x88\x1f\xd2\x13\x48\x1d\xfb\xe9\xbc\x53\xe3\x53\x0d\x96\x42\x03\
+\x84\x46\xd7\xed\xfa\xee\x8e\x25\x78\xee\xa8\x79\x2f\x12\xa2\x1f\
+\x79\x9e\x71\xb6\xea\x79\x8e\x2a\x97\xf6\xad\x23\xb5\x96\xbe\xfc\
+\xf1\x1a\x8e\xda\x8b\xe0\x57\xd8\x98\xa3\xb0\x4f\xa1\x37\x86\x62\
+\xaa\x1f\x7b\x93\x2c\x15\xbd\xe8\xf4\xb8\x09\xe7\xd8\x52\xb1\xe8\
+\xda\xe5\xe3\x77\x91\xb6\x65\xda\xa0\x46\xd9\x36\xa6\xe4\x95\xe4\
+\x2e\xa2\x0b\x0c\x38\xfd\xda\x12\x85\xa6\x52\x9f\x4f\xe2\xf9\xc3\
+\xda\xc0\xb2\x4e\x1d\x92\xed\x7e\x17\x7d\x18\x9d\x1f\x43\x21\xf7\
+\x6f\x8a\x62\x06\x22\x54\x4b\x9c\xb9\x20\xb0\x61\x4c\x54\xe2\x39\
+\x7d\xfc\xf1\x26\x0d\x78\x64\xcc\x9d\xad\xcb\x90\xaf\xb6\x29\xc6\
+\xca\xcd\xb7\xdd\x90\xbd\x0e\x8b\x45\xff\x26\xfc\xd6\xe1\x3f\xc2\
+\x28\xb7\xee\x70\x1f\x24\x28\xfb\x47\x2f\xa4\x7f\x5d\xef\xfe\xc6\
+\x7f\x8a\xc6\x7f\xba\xee\x0b\x43\x19\xef\x4b\x45\xa6\x94\xb3\x44\
+\x82\x4d\x56\x80\xd1\x48\x76\xe2\xd8\xae\x58\xde\x8a\xe6\x6c\xe5\
+\x88\x22\x5b\xb9\x90\x57\xb6\xc9\xf9\xc1\x77\x2b\xea\xaf\xa9\xfd\
+\x5e\x65\x9b\x02\xcd\x92\xff\x68\x05\x58\x2e\x22\xbf\x53\x09\x8e\
+\xb3\x0d\x23\x15\x58\x2b\xe4\x28\xac\x95\x36\xe9\xcb\xe8\x6c\xd6\
+\x55\xdf\x5c\x4c\x0c\xca\xad\x1a\xcc\xdb\x59\x1c\x60\xd8\x50\x73\
+\x82\xd1\xad\xe9\x75\xe6\x3b\x74\x11\xe7\x78\x21\xbf\x0a\x42\xb2\
+\xc7\x4b\xb4\x15\x8b\x8b\x3f\x52\xa9\xa8\xc8\x6b\xb5\x1b\x1e\x74\
+\x45\x25\xdb\x0d\x0c\x29\x6f\x49\xe9\xeb\x54\x56\x26\xa0\x2f\xbe\
+\x5d\x5d\xdc\xac\x8b\x5b\x9f\xbb\xbe\xc0\x5a\x8b\xd9\xbb\x65\x18\
+\xb3\x45\xb6\x84\x0d\xb9\x4a\xff\xc9\x44\x5b\x5e\xf5\x92\x2d\xd7\
+\x46\xe9\x7c\x93\x0a\x07\x9d\xb7\xe0\xde\x7e\xf9\xc1\x1b\xfa\x8e\
+\x34\xea\x1c\x2f\x2d\xd8\x4d\xee\x3b\x2f\xda\x4f\x70\x79\x61\x9c\
+\xc2\x9c\xd4\x76\x5d\xce\x27\x4a\x90\x0f\x6c\x24\x90\x74\x9f\x46\
+\xf0\xd1\xef\x2b\xd8\xeb\xb4\x48\xbf\xe0\x10\x4f\x07\xb2\x70\xcd\
+\x58\x42\x9f\xd9\x9a\xf9\x4e\x42\x12\x02\x58\x48\xb6\x64\x1b\x6e\
+\x4e\x38\xe9\x0a\xa3\x2a\x01\x65\x8e\x0c\x2e\x1a\x5e\x3c\xb3\x7a\
+\xb7\x4a\xc3\x42\x1e\x66\x62\x42\x02\xc1\x68\x11\xc7\x0b\x86\x8e\
+\x15\x43\x51\x96\x4e\x09\xdc\xf3\x9e\xda\x73\x38\x4e\xbc\x15\x6c\
+\xf1\x86\xe5\x0b\xe9\xd7\x9b\x61\x74\x08\x28\xaa\x34\x0d\xc7\x8f\
+\x74\x5c\x87\x7d\xdc\x66\xf4\x80\x7c\x73\xf6\xfb\x13\xbc\x66\xdc\
+\xf3\x63\xa6\x87\x52\xf1\x1e\x78\x47\x8b\x3f\xe8\xf2\x92\x0f\x49\
+\xf1\x71\xe9\x6a\x09\x70\xee\x39\x04\x3d\x50\x69\x97\xf7\x74\x15\
+\x97\xd7\xd0\x59\x28\xbd\xb4\x78\x7e\x4d\x1e\x18\x8b\xa4\xb2\x8e\
+\xc1\x96\x2f\x85\x2b\xf2\x3e\x07\xa0\xee\x82\xd3\x71\xd4\xd6\x7e\
+\xd7\x5d\x76\x5c\xb5\x73\x4a\x7c\x96\x06\x89\x86\x63\x43\xd4\x0b\
+\x8f\xaa\x72\x3f\x84\x75\x24\xf7\x86\xa3\xf0\x45\x30\x18\x85\xbd\
+\x9e\xab\x21\x25\x14\x48\xb1\xcd\xdc\xc3\x1a\xe7\x41\x6e\x2d\xba\
+\x56\x46\xaf\x4b\x4b\xf3\x62\xd5\x5c\x5d\x91\xe9\xda\x9c\x44\x77\
+\x38\xb6\x1c\x3b\x8e\xf8\xad\x60\x3b\xa9\xf9\xdc\x35\x9f\x52\x53\
+\x90\x66\x83\x5c\x90\xd0\x0b\xa2\xc5\xb3\x08\x64\x6a\xde\x81\xcb\
+\x6a\x4d\xd4\x71\x78\xf6\x7b\xa3\x98\x3b\x9f\xd0\x22\xc3\xd8\xce\
+\xca\x35\x3a\x6d\x98\x38\x14\x97\x88\xfc\x8e\xf0\xa8\xc7\xfc\x92\
+\xd4\x63\xe2\x52\x47\xb3\xd9\xca\x78\x47\xcb\x75\x66\xa2\xdd\x71\
+\x54\xf7\x2c\x08\xd0\x45\x19\x24\x66\x98\x91\xb2\x8d\xcb\xde\x4a\
+\x3f\xb1\x8d\x80\x03\x28\x60\x28\xf2\xfa\x6e\xb4\x10\x53\x44\x4d\
+\x59\x78\x50\x66\xc2\x56\x4a\x6c\xed\xbc\x18\xe3\x38\x91\xb0\x9f\
+\x38\x7a\x58\xb9\x65\xad\xd8\x3b\x08\x14\x2c\x33\x52\xde\x09\xa6\
+\x99\x01\x13\xb9\xd1\x80\xf3\x23\xe6\x10\xcd\x41\xac\x50\xcf\xc9\
+\x8b\x6a\xf2\xdf\x37\xd9\x0d\xed\x82\x69\x9e\xd9\xd5\x10\x8c\x2c\
+\xc0\x8b\x17\x93\x47\x96\x0e\xc9\x91\x3e\xeb\x99\xe4\x9a\x5a\x95\
+\xd8\x77\x19\xb8\x91\x4b\x7e\x42\x92\x23\xd0\x2f\x42\x48\xea\x94\
+\x7e\x82\x18\x79\x2f\x15\xa2\xc7\x03\xb6\x49\x1f\x80\xaa\x5f\xf3\
+\x40\x3b\x55\xa8\x87\x23\x07\xda\x3c\x46\x85\x6c\xac\x33\x3d\x0d\
+\x6f\x15\xc6\x27\x5a\xed\x50\xdb\xd2\x0a\xfa\xf0\xdd\x23\x6d\x8e\
+\x40\xc0\x02\xaf\x85\x2c\xdc\x68\x86\x05\x66\x3b\x2c\x39\xe8\x48\
+\xa8\x84\xa0\x7a\x65\xe8\xa9\x00\x01\xea\x25\xfe\x82\xe6\x7e\xee\
+\x28\x51\xf7\x5b\x25\x00\xde\x8e\xfb\xb5\xfc\x1d\x87\x4c\xa8\xbb\
+\xbd\x2a\x84\x1e\xae\x4e\xa8\xb7\xbd\xca\x2a\x0f\x1e\x9d\x46\x61\
+\xf4\x00\x19\x12\x1a\xdf\x0a\x34\x79\x32\x32\xb6\x6c\xf0\x5e\x94\
+\xc8\x26\xd2\x2b\x25\xd5\x04\x73\x35\xd0\x03\x68\x06\xfd\x5a\x32\
+\xba\x27\xac\xb6\x47\xa0\x03\x01\x7c\xbd\xea\x4f\x51\xf5\x27\x06\
+\x69\x08\xd5\x47\xda\x84\x61\xf2\x69\x9b\x0b\x58\x91\xf4\x05\x91\
+\x95\x0d\xc5\x54\x49\x76\xa3\xa0\xda\xa1\x85\xe2\xd7\xe8\x02\x4b\
+\x3b\x3c\x0a\x41\x47\x88\xd1\x42\xd0\x17\xb4\x39\x08\x65\x80\xb6\
+\x8a\x47\x1e\x57\x9b\x43\xa1\x80\x52\x1f\x63\x91\x26\xe5\x95\x20\
+\x1d\x0c\x4d\x53\xc3\x40\xfd\xbe\xa6\xfe\xc9\x65\xc2\xea\xce\xca\
+\x52\xca\xde\x91\x6b\x24\xa6\xd9\x71\x30\x46\xcf\x11\xe1\x4f\x54\
+\x03\x5a\x80\xe8\x5e\x80\x2e\x9c\x9c\xf1\xc2\xa0\xa1\xe9\xc8\x5e\
+\xee\xa9\x69\x0e\x7c\x4e\xa1\xf4\x95\x92\x45\x27\x56\x51\x59\x99\
+\xf7\x2d\x95\xd6\x69\x5b\x35\xaf\x1f\x17\x59\xe5\x95\x33\xd2\xfc\
+\xa4\xf1\xd9\xa7\x97\x91\xd9\xc6\xec\xff\x87\x17\xd6\x27\x97\x8d\
+\xeb\x73\xcb\x20\x74\x35\x99\xf2\x4e\xe8\x6c\x86\xef\x79\x4b\x9a\
+\xf9\x62\xa3\x2a\x0d\x80\xaf\xcf\xd1\x2a\x3e\x0a\x9c\x97\xd7\xaf\
+\x5f\x89\xfb\x2f\x19\x9b\xf6\x56\x63\x50\x7a\xc7\xaa\x6a\xa6\x2e\
+\x8e\x29\x76\xd1\x0b\x85\xcd\xbf\x4d\x5f\x86\xa8\x87\xca\x76\x40\
+\xc0\x39\xdb\x7c\xa9\xf3\x54\x21\xcd\x9b\x39\x2e\x89\x40\xd4\xb2\
+\xe2\xc0\x54\x90\x35\x35\x2b\x46\x35\x2b\x96\x6a\x56\xd9\x62\x12\
+\x4f\x35\x18\x43\x10\x2c\x5f\xf0\x7a\x1b\xc0\x64\x0a\x46\x2e\xae\
+\x85\x7c\x06\xc3\x29\x49\x6e\xc2\x15\x9c\xe8\x8d\x6b\x7e\xca\x93\
+\x22\x34\x06\xa3\xee\xf8\x18\xa3\x78\x00\xec\xdf\xd8\x22\xfc\x92\
+\x66\xdb\x0d\x06\x7a\x18\xbd\x01\x01\x78\x44\x09\xaf\xb9\x0a\xa7\
+\x20\x06\x00\x66\xf6\x9c\xab\xe9\xca\xbb\x2b\xf6\xdc\xaa\xed\xf7\
+\xa8\xb2\xa2\x90\x0a\x37\x56\xcb\x22\x58\x4d\x33\x3e\x14\x3a\xe2\
+\xfa\x81\x9c\x50\xb6\x3b\x8d\x72\x29\x33\xb3\x15\x6a\x6c\xb2\xe5\
+\x75\xd6\xd1\xe5\x86\xf2\xd1\x02\xe0\xaa\x0d\x74\x76\x6d\x85\x41\
+\x75\x8a\x9a\x7e\x23\xd2\x4a\x44\x14\xb1\x5e\xe5\xc5\x81\xa9\x12\
+\xd5\x8e\xc0\x23\x4f\x80\x3a\xb8\xe6\x29\x40\x49\xd4\xb5\x0e\xa0\
+\x87\xf8\x4a\x17\xcd\xf3\xa8\x5f\xb3\x21\x5d\x6b\x6f\x15\xc6\x19\
+\xc4\xf5\x83\x66\x34\xe8\xaa\x31\xd1\x04\x1d\x71\x75\x1c\x75\x7f\
+\x43\x6a\xd6\x60\x7e\xde\xfa\x37\x80\xd2\x33\x41\x69\xc0\x18\x0a\
+\xf6\xbb\x50\xc6\x63\xa7\xa5\x07\xec\xb1\x18\xc3\x28\xd8\xbb\xe1\
+\xc4\x16\x77\xe2\xec\x1f\x56\x9c\x21\xd4\xff\x1e\x60\x4c\xac\x51\
+\x94\xa5\xb6\x81\x89\xe6\x92\x55\x98\xee\xb8\x56\x58\xd0\x40\xb0\
+\xb5\x6e\x18\xa3\xd7\x32\xa6\x76\x15\x22\x92\xce\x57\xb0\x03\xb2\
+\xaf\x6e\x9f\xc2\xdc\xa1\xe9\x7d\xf0\xfc\x10\x80\xba\x15\x78\xb4\
+\x3d\xa9\xf7\x40\xdc\x3d\x02\x0f\xbc\x79\xd3\x28\xbd\x7b\x11\xf1\
+\x15\xa9\xb2\xc4\x43\x33\x40\x3f\x08\xa2\x0a\x2a\x0e\xb1\x74\xfc\
+\x57\xa5\xc7\x77\xca\x97\x56\xe8\x1e\xfe\x35\x16\x2e\x99\x30\xfa\
+\x8a\x2a\x76\x37\x16\x01\xe7\xbc\x15\x8a\x83\x52\x36\x45\xa6\x02\
+\x9e\x25\x93\xb9\x13\x53\xc0\x7c\x95\xc7\x8c\xff\xad\x61\x7c\xe0\
+\x6f\x95\xe1\x91\x4b\xf3\xd1\x51\x57\x88\xac\xde\x18\x4c\x2c\x19\
+\x45\xa0\x26\xec\x62\xad\x7e\x12\x4d\x85\x59\x19\xf3\x02\x1e\xec\
+\x2b\xf0\xa4\xfc\x79\x39\xfa\x40\xdf\x65\xfc\x1b\xa3\x5e\x30\xdb\
+\x08\x43\x9b\xd9\x5d\xca\xb5\xe6\x77\x9c\xcc\x82\x60\x38\x76\x56\
+\x60\x26\x39\xbe\x83\xfa\xbc\x33\xe5\xda\xfa\x51\x00\xdb\x7d\x94\
+\x90\x57\x58\xa4\x12\xd4\x3a\x03\x44\x18\x7b\x62\x4a\x46\xe9\xf9\
+\x60\x37\x69\x2f\xb9\x5d\x85\x37\x69\xec\x8c\x6d\x23\x69\x61\x2a\
+\x2d\xe6\xfa\x94\x14\x46\x4a\x40\x92\x6e\x28\x37\x2e\x20\xf8\x78\
+\x96\x9e\xef\x6c\xd7\x4e\x4d\x10\x2b\x2f\x27\xcc\xb9\x26\x44\x38\
+\x52\x7b\x33\xd1\x74\x05\x76\x44\x0e\x48\x72\xa5\x13\x20\xdc\x40\
+\x47\x1e\xc5\x1b\x8b\x73\x17\x41\xe1\xe7\x5a\xf0\xe1\x02\x5b\xd9\
+\x94\x48\xde\x5c\x84\xeb\x7c\xe4\x8b\x8b\x4b\x93\x9f\x3b\x75\xaa\
+\x07\xa6\x72\x3c\x50\xc5\x32\x95\x2e\x4d\x75\xcd\xee\xd0\x5c\xef\
+\xd5\x57\x6c\xd7\x44\xe6\xfd\x88\x72\xd5\xa1\xc7\xbe\x72\xac\x0d\
+\x0c\xdf\x7d\xc2\xb1\x56\x2d\xa1\xba\x39\xe4\x43\x33\x83\xe8\x80\
+\x9c\x35\x1a\x2a\x35\xf7\x56\x5c\xba\xdc\x48\xe5\x4c\x74\x7b\x05\
+\xdd\x42\xe9\x0d\x46\x87\x92\xe1\x1e\x69\x8e\x04\x61\x9a\x93\xb1\
+\x49\xe5\xb1\xe1\x5d\xe8\xea\x0e\x00\x25\x09\x03\x33\x5b\x6b\x3c\
+\xf0\x6d\xad\x34\xde\xe8\x72\xaf\x40\x54\x7a\x3a\xcc\x89\xb1\x38\
+\xd6\xbd\x1f\x3f\x60\x5a\x10\x4e\x2e\xf7\x37\x50\x42\xad\x05\xbb\
+\x98\x54\xbb\xdf\xff\x65\x30\xf0\x8c\x08\x89\x8f\x98\x9c\x81\xbe\
+\xa3\x83\x70\x94\x0a\x2a\xa5\x52\x2f\xaa\xba\x02\x3d\x11\x93\x64\
+\x0f\x32\x92\x1b\x2b\x6d\x02\xe3\x5a\xf4\xc3\xba\xb3\x23\x77\x9c\
+\x8f\xee\xcb\x83\x19\x65\x2c\x1d\x5b\x41\x79\x27\x32\xae\xd9\xf8\
+\xb1\x8c\x4c\x95\x86\xf7\xd5\x95\xdb\xe8\x33\x57\x96\x86\xdf\xaa\
+\x0d\x94\x2f\xb2\xaf\x9a\xfb\x3b\x6a\x36\xce\x4a\xae\x5b\xb5\xce\
+\x22\xdd\x3a\x4b\x58\x69\x9f\x71\xe7\x6c\x68\x31\xd1\x2c\x23\x98\
+\x17\x26\x5c\xa7\xbb\x67\xa6\x6c\x5b\x34\x4d\xf1\xb0\xf1\x83\xc1\
+\xe1\xa0\x0c\x6f\xed\x8c\x49\xcb\xca\xe0\xdc\x5a\x82\x9b\x45\x2c\
+\x18\xf5\x46\xd2\x9f\xa5\xa9\x75\xbf\x8f\xb4\xfd\x16\x13\x50\x85\
+\xf1\x61\x05\x44\x58\x99\x34\x6b\xa7\xe2\xc5\xf3\x76\xe2\x8a\x8d\
+\x63\xaa\x25\x09\xd2\xb8\x40\x2b\xbf\x3f\xae\xb2\xdf\x11\x04\xbc\
+\xff\x34\x88\x53\xf3\x6e\x71\x37\xd6\x58\xac\xac\x5a\x0e\x6c\x67\
+\x86\xb2\xb5\x4a\x68\xae\x6f\xeb\xc0\x57\xe2\x72\xad\x47\x79\x1d\
+\xf5\x4e\x07\xe3\x02\x25\x67\x9b\x34\x5c\xf2\x3b\xc5\x5a\x5e\xaa\
+\xb4\x7e\x79\xbf\xfc\x34\xa7\xfb\xe8\x4e\x28\x72\xfb\xf0\xfa\x13\
+\x74\x33\xb0\x35\x31\x01\x32\x08\x79\x74\x2a\xcf\xc8\xb4\xa5\x4b\
+\xb0\xa0\x83\xee\x7b\xd4\x22\x48\x34\x8a\xc0\xdf\x90\xff\x93\x46\
+\xf8\xec\xc3\xde\x49\x13\x07\x18\x89\xe3\xb8\xc0\xc2\x30\x5b\x29\
+\xe4\x59\x42\x2c\xc7\x9b\xe8\xfd\xfe\xa4\xd3\xef\xba\x93\x5e\xf0\
+\x71\x8a\x3f\x4e\x38\xd9\x33\x37\xa6\x84\x4b\x68\xfb\x99\xdd\xee\
+\xf7\x6c\x32\x9c\x76\x9d\xc9\x14\x33\xea\x03\xa7\x4b\xc5\x08\x72\
+\x39\xd2\x18\xdb\xf8\x6c\xf2\x74\xca\xf3\x2f\x8e\xe2\xf2\x35\x02\
+\x6a\x2c\x86\xe4\x5f\x38\x8a\x62\x54\x71\xff\x13\xc8\xf7\x8e\x73\
+\x8c\x01\xe9\xad\x22\xe3\x29\x32\x3f\x08\x75\x51\x3d\xc1\x99\xc0\
+\x78\x0c\xde\xca\xb8\xef\x98\x27\x09\xe8\xca\x47\x13\x9c\x9a\xcc\
+\x26\xbf\x73\x1c\x44\x5d\xed\x14\x68\x5e\x27\x32\x04\xc4\xe5\x90\
+\xd1\x11\x1d\xed\x98\xf3\xd5\xd0\x8f\x2c\x29\x6f\x1e\x84\x34\xc5\
+\x22\x98\x77\x43\x31\x5a\x8a\x57\x29\xd0\xfd\x73\x90\x42\x99\x68\
+\xf9\x29\xd0\xa5\x1b\x85\x81\x62\xa3\xa5\xa5\x1c\x47\x1c\x7d\x82\
+\xc9\x3f\xbd\x48\x8f\x8f\xe1\x9f\xe7\x9f\x8f\x8f\xa3\xee\xf2\xc5\
+\x9c\xff\xf3\x7c\x61\xa4\x91\x3f\x40\x6b\x93\x4d\xe8\x4a\x9e\xea\
+\x7e\xcf\x36\xa5\x5e\xb5\xdf\xb7\xec\x63\x1d\x95\x63\x19\x9c\x44\
+\xc3\x05\xa5\x78\xc8\x0b\xe8\xb1\xc3\x91\x00\x9a\xe5\x42\x3c\xb6\
+\xf0\x22\xbc\xaf\xc5\xf8\x93\x3f\x7f\x7e\xf7\x16\x9d\x3c\x05\x2a\
+\xee\xdd\xdd\xe2\x39\x10\x7c\xfa\xfc\x9e\x2d\xc3\x81\x66\x4d\x03\
+\xc9\x26\xcf\x3f\xd7\x88\x4d\x60\xcd\x42\x73\x49\x25\xd5\xd5\x46\
+\x4a\x5d\xfb\xe6\x87\xe2\xff\x34\xaf\x0b\x47\xe7\xde\xc1\x4a\x7f\
+\x4b\x8d\x64\xbc\x50\xfe\xc3\x11\xe2\x52\x6e\x31\x3e\x90\xa3\x8e\
+\x2f\xfa\x07\xff\x8e\xdc\x3c\x0e\x97\xe7\xd2\x20\x40\x45\xb1\xd4\
+\x26\x55\xbb\x97\x20\x6b\xfe\x89\x14\x62\xb4\x44\x83\x2e\x32\xef\
+\x29\x75\x62\x54\x9b\x1c\xc3\x7e\xe1\x83\x26\x7c\x87\xf7\xfb\x10\
+\x13\x30\xd0\xe2\x18\x3f\xf5\x87\x3e\xf0\x8d\x8e\x51\xe2\xd6\xf0\
+\xcd\x2d\x8b\x7f\x03\xb6\xd5\x89\x3c\x79\xfa\xa3\x11\x2f\x19\x00\
+\x1f\x59\x6c\x81\x05\xaf\x16\xfc\x4b\x06\x74\x37\xea\x6d\x78\x06\
+\xf6\x30\x66\x0a\xcf\xb0\xdb\x20\x30\x64\x2e\x39\x46\x4d\xfa\xb0\
+\x0f\x98\xd1\x43\xa8\x06\xf6\xb1\xdf\xe3\x17\x58\x7d\xe8\x69\x40\
+\xa6\xdf\x08\x56\x43\x08\x4b\x05\xd7\x3d\x9b\xed\x84\x15\x2a\xcc\
+\xf2\x28\x18\xe0\x76\xbf\x18\xe8\x36\xa7\xc8\x34\x6b\x58\xec\x03\
+\xa6\x2e\x2d\x95\xea\xdc\x9c\x7b\x5a\x26\x27\x14\xf9\x84\x1b\x98\
+\x5f\x5c\x1a\xd7\xdd\xe5\xb5\x30\x02\x5e\x6a\xb9\x63\x36\x43\x37\
+\x8c\x08\xaa\x3b\xa3\x7f\x54\x00\xaa\xd6\x05\x7f\xe7\x60\xa2\xc5\
+\xa0\xab\x07\xc1\x15\x8f\x3a\x1f\x4f\x8c\x26\x53\xdf\xf8\x24\x0c\
+\x1a\x02\xd9\x72\x5e\x26\x53\x2e\x95\x15\x85\x69\x03\xe0\xb9\xa7\
+\x54\xd5\x90\xdf\xba\x84\x01\xb3\x84\xb3\xa8\xa4\x9b\x20\xe9\x30\
+\x4c\xcb\x05\xa1\x37\x2b\xdb\xcd\xb1\xdd\x1c\x55\x7e\x6c\xb4\x08\
+\x44\xe4\xc3\x6c\x32\x9f\x7a\x5a\x94\x0d\xf2\x0d\x60\xcf\x0b\xf2\
+\xa1\x00\xed\x2d\x6a\xd1\x1e\x52\xf1\x99\x24\x7a\x22\x4e\xd9\x8e\
+\x7b\x70\xdc\x71\xa5\x80\x72\x74\x16\xd2\xf3\x81\x3a\x64\xa5\x85\
+\x57\x56\xf6\x57\x59\x41\x0f\x06\xd5\xec\x1e\x4b\x95\xa6\x91\x3b\
+\xae\xb7\x98\xba\x07\x2b\x84\x86\xe0\x14\x40\xd6\xcb\xea\xb9\x44\
+\x64\x97\xa8\x04\x16\xbd\x0b\x0f\x70\xab\xdd\xfc\x1e\x94\x82\x6c\
+\xae\x4f\x1f\xf4\x5f\x5a\x22\xd7\x01\xc4\xbb\x56\xf8\x86\x48\x9d\
+\x18\x62\x20\x00\xb4\x2f\xaa\x9a\x5d\x44\x88\xe1\xfa\x5c\xa9\xb2\
+\x25\xfc\xa5\x8c\xa6\xd0\x10\xfd\x90\x54\x42\xab\x74\xeb\x93\x62\
+\x58\x7c\x4e\x55\x46\x14\x8d\x0b\x10\x47\x68\x6f\x3e\xaf\x39\x00\
+\x47\x51\xb7\xeb\x6a\x6f\x26\x8c\xe2\xe7\xf2\x85\x2e\xfe\x5a\x02\
+\xbe\x9e\x10\x98\x7e\x41\xe9\x0f\x14\x5a\x0e\x8d\x86\x4f\x00\xc4\
+\xac\x13\x79\x43\x5a\x47\xc9\x16\xea\x5c\x83\xf7\x40\xfd\xb7\x62\
+\x58\x06\xf4\xc8\xc1\x48\x53\x50\x69\xdb\xe0\x58\xfe\x18\x42\x42\
+\x6f\x5f\xe8\xed\xbe\x9f\x66\xc4\xc6\x37\x31\x09\x6e\x95\x94\x6c\
+\x40\x63\x12\x33\xa4\x8b\x99\xc6\x24\xe6\x9c\x49\xcc\xa6\x98\x44\
+\x3f\x2f\xdb\x2d\xb0\xdd\x42\x32\x89\x54\x32\x89\xf9\x64\x51\x63\
+\x12\x29\xcf\x5e\x17\x4c\x22\xad\x33\x89\x9d\xf5\x0c\xa6\x55\xbc\
+\xa5\x36\xa4\xa5\x0f\xc6\x58\xa5\xbb\x97\x96\xb8\x4a\xa7\x35\xdf\
+\x01\x41\x84\xcf\x9b\x20\x1e\x66\xfa\xc9\x51\x18\xc2\x47\x79\x00\
+\x31\xf8\x94\x04\xa3\x5f\x83\xe9\x68\x81\x95\xd0\x65\xae\x90\x3a\
+\x5a\x3c\x9f\x8f\x16\x40\xa2\xbb\x94\xa3\x12\xb8\xce\x28\x25\x6c\
+\x55\x62\xc8\xd0\x37\x28\x8e\x1d\x2d\x24\xf5\xd2\x95\xc8\x75\xe3\
+\x6f\x2d\xe0\x93\x45\xf2\x61\xa3\x81\xc7\x1d\x7c\x03\xee\x6c\x1b\
+\xe0\x93\x18\x4a\xfe\x29\x81\x56\x4d\xd0\xa8\x79\xd0\x8e\x8f\xf5\
+\x68\x13\xb7\xee\xad\x2b\x95\x18\x33\x78\xa7\xea\x3a\x6a\xf4\xd5\
+\x6b\xda\x92\xf2\xd7\xa3\xc2\xd9\x89\xfb\x72\x6d\xa6\x13\x5e\x59\
+\x30\x66\x8c\x8b\x56\xac\x1c\xfd\x56\xef\xbd\x6b\xcb\x7b\x2d\xad\
+\x1f\xf1\x94\xd4\x38\x69\xf0\x8f\x8b\x7a\x8f\x26\x71\x7d\xfa\x97\
+\x5e\xab\x45\x4a\xe5\xbb\x00\x7b\x5b\xbe\x7a\xd6\xc1\x3c\x3b\xda\
+\x14\x59\xfe\x52\x5c\xca\x1d\x18\x96\x48\x94\xc5\x74\xaf\x42\xd7\
+\x2b\x98\x79\x9b\xad\x03\x7e\x17\x52\xba\xa3\x54\x9a\xd9\x36\x2f\
+\xb2\x1b\x81\x01\xb3\x50\x6a\x2d\xa5\xa5\xe7\x3e\xa8\x95\xfe\x8a\
+\x1a\xf7\x74\xd2\x4d\x4b\xeb\x01\x9e\x3f\x56\x75\x0f\x22\x73\xb5\
+\x87\xfb\xde\xe9\x7d\xd3\x97\x7f\x7f\x6b\x85\x9e\x07\xb4\xa5\x5d\
+\xb9\x0b\x4a\x7d\xbb\xee\x1f\x4f\x6c\xe7\x7d\x03\xaa\x7d\xae\xa8\
+\x8d\xd5\xe8\x2b\x8b\x12\x17\x92\x1d\x8f\x8e\x0c\xa5\x44\xd2\x53\
+\xbd\x9a\x00\xc7\x5c\x64\xfd\x16\xc0\xa2\x50\x4a\x6a\x37\x1a\x8e\
+\x8c\xaf\x60\x27\x80\xae\xea\xb0\xb3\xa0\x55\xde\x6e\x56\x9e\x5a\
+\x8b\x2a\xc7\x4a\xe5\x48\xba\x6e\x19\xd8\xc2\xf0\x65\xa0\x4a\xbb\
+\x18\x6b\x28\x63\xa7\xdd\xa8\x97\xdc\xef\xb4\x46\x5e\x8a\xf8\x00\
+\x31\x25\x2e\x0d\xcb\x47\x91\xd5\xbb\x5b\xca\x39\x7f\xf0\xb6\xeb\
+\xc4\x88\xd1\x97\x4f\xfa\x1d\xa1\x32\x0c\x58\xb5\xa5\x2e\x00\xab\
+\x98\xab\x0b\xf4\xfd\x5e\xfd\x36\xd6\xa4\x3f\x7e\xd8\x71\x7b\xea\
+\x8e\x21\xaa\x79\xe6\xe5\x93\x91\xf8\xb6\x17\x3e\xdb\x88\x57\x0e\
+\x0f\x69\x2f\xde\x69\x94\x5d\xdc\xd1\x5c\x5e\x66\x23\x4c\xf4\xd3\
+\x02\x92\xa0\xe6\x87\xcc\xc0\x9f\x1e\x7b\x0c\x48\xf4\x94\x24\xf4\
+\x68\x11\x48\x20\x5c\x0e\x91\x41\x53\xf8\x68\x9d\x4e\x9a\xf2\x5c\
+\xd0\x63\x4d\x4a\x21\x36\xa6\x70\xdd\x91\x39\x25\x39\x8d\x8d\x81\
+\x5d\x93\x76\xfb\xb4\xaf\xa0\xaf\x99\xad\xc8\x38\x33\x6f\x12\x8d\
+\x53\x56\xca\x27\xae\x93\x1b\x21\xe3\x56\x76\xc7\x90\xdd\x31\x60\
+\x77\x3c\x31\xb9\x7e\x0f\x6b\x92\xb9\x57\x65\x0d\x6c\xaa\xbf\x91\
+\xe2\xaa\x2c\x39\xd3\x05\x52\xbb\x02\x61\x55\xd6\xe3\x0a\xaa\x8d\
+\xc4\x15\x62\x05\x88\x86\x09\xbd\xc8\x98\x5d\x05\x5e\x1b\xed\x47\
+\x71\xc0\xe8\x91\xb0\xb6\xe5\x32\xa6\x06\x49\xc3\x65\x0c\xab\x5d\
+\xc6\xb4\x2a\xb7\xaf\x0d\xb7\x31\xf6\x09\x82\x01\x0f\xbf\x74\xeb\
+\x10\x95\x8f\x9f\x0c\xeb\x50\x18\xa1\x81\x38\x4e\xed\x0e\xb8\x01\
+\x8a\xd8\x0e\xc5\xb0\x82\x97\x8a\xd2\x71\x54\x43\x67\x3c\x45\x24\
+\x0f\x2f\x7e\x02\xdd\x8f\x3f\xd9\x56\x6a\xd3\x55\xc7\xc6\xc4\x02\
+\x43\xe9\x39\x5d\xd2\xb3\x80\x20\xe7\x9c\xa9\x67\x49\xf6\xd1\x0d\
+\x72\x19\xe6\xd8\x48\x14\xb1\x41\x14\x9a\xd6\x35\x57\xd6\x90\x2b\
+\xcd\x7a\xa3\xf6\xe1\x30\x22\x57\x56\x49\x8f\x8b\x5e\x6b\xe6\x3e\
+\x8f\x10\x15\x5a\xd9\x0c\x34\x0c\x73\x78\xae\xe1\xb0\x8a\xeb\x5a\
+\x4b\x2f\x93\x1b\x6c\xcb\x07\x30\xa1\x92\x0f\x6d\x19\x81\x09\x8c\
+\x0c\x6f\x1e\x1b\xe1\xd7\xab\xa9\xb2\x11\x55\x46\x16\xe9\x03\xe2\
+\x19\xee\xa4\xc5\x86\x6e\x3a\x35\xca\xf5\x5b\xd8\x9c\x15\x0b\x34\
+\x92\xc1\x23\xff\xfd\xc7\xe1\xa0\xf4\x18\x8a\xeb\xb5\xa8\x30\xc6\
+\xfd\x02\xf7\xb7\x6a\xe6\x5b\x24\x6d\x8a\x71\xa2\x7e\x6b\x79\x46\
+\x3a\x99\x4e\x04\xa8\x1a\x87\x05\x2a\xf5\xa3\xf2\x4a\x3c\x5e\xe2\
+\x8b\xa9\x96\xfb\x70\x2c\xef\xb8\x35\x83\x6f\x14\xaa\x4c\x20\x19\
+\x7a\x4e\x87\x69\xbf\x47\x68\x64\xd6\x24\xde\x84\x50\xda\xe4\xb8\
+\x2c\xac\x0d\x56\x79\xe6\x14\x3f\xf5\xc7\x6a\x43\x7e\xb5\xcb\x9f\
+\xef\x1c\x48\x0f\x44\xc5\x22\xa1\x8d\x50\x57\xf6\xfc\xb5\x33\x30\
+\xe5\x6c\x13\x91\xaa\x43\x97\x4e\xc2\xd0\x6b\x6e\x26\xee\xa1\x24\
+\xab\xa9\xe7\x15\x54\x12\x3d\x3c\x95\x8b\x52\x6b\x84\x47\xdb\xf5\
+\xca\x2c\x94\x5a\x03\xe1\x62\x95\x0b\xd5\x01\xc5\x1c\x36\x87\xbf\
+\x11\x69\xe6\x80\xba\xc2\x65\x5e\x93\x9f\x52\xbd\x71\x2b\xa3\x09\
+\xad\x3b\x68\x1a\x4d\xa8\x67\xb5\xe1\xa4\x0a\x57\xbe\x03\x00\xc4\
+\x6b\x4f\xe0\xa8\x59\xc4\xea\xc5\x2c\xf5\x60\x96\xf6\x50\x64\xdb\
+\x11\xa1\xe5\x69\xce\xaf\x51\xf1\x6d\x80\x30\xa0\xc8\x9a\x6e\xc8\
+\x59\x2c\x60\x14\x7e\x0d\xa7\xf8\x4c\x37\xff\xd3\x27\x1c\x51\x29\
+\xc0\x60\x58\xd3\xe5\xcd\x82\xb8\x5e\xa4\x7b\x07\x3d\xfa\x86\xf7\
+\x26\x8f\xf6\x5d\xdd\x35\x7b\xbd\x76\x61\xd6\x0b\xfb\x7c\x80\x86\
+\xc1\x71\x93\x1b\x86\x46\x63\x2b\x2c\x2f\x5a\xb4\xe8\x1c\xde\x33\
+\x22\x15\xf5\x8e\xce\xad\x06\xb8\x16\x32\xbe\x9a\x86\xa8\x0d\x4f\
+\x2e\x6f\xdd\xb9\x50\x4b\x0e\xd5\x1d\x16\x46\xba\x85\x5e\xd1\xa1\
+\x8b\x19\xe9\x96\xd7\x6a\x4a\x0b\x55\x49\x77\x2d\x2f\x4a\x7b\xb0\
+\xdf\x1e\x68\xae\x07\xa2\x5b\x84\x6f\xa5\xb5\x57\x9b\x9e\x44\xae\
+\xd8\x74\x4b\x4e\x86\x1e\x53\x3a\xa2\x2d\x68\x6c\xc5\x63\x70\x0f\
+\x36\x47\x4e\x3d\x60\x4a\x0a\xdb\x4a\xbc\x94\x08\x9b\x97\xae\x05\
+\x7b\xad\xf9\xae\x17\xbe\x73\x5c\xdc\x60\x7c\x4a\x62\x3c\xf0\x01\
+\x47\xa1\xc5\xd3\xdb\xa4\x2b\xea\x20\xce\xdf\x8e\xb2\xc2\x78\x68\
+\x57\x25\x26\x4b\xdf\x17\xce\x57\xe8\x39\x6e\x58\x18\xbd\x66\xef\
+\x52\x9c\xd6\x40\xf0\x22\x81\xb6\x07\x8e\x82\x48\xac\x0c\x73\x20\
+\xfa\x32\x13\xcb\x74\x0a\xb3\xd3\x84\xfa\xa3\x0d\xc6\x65\x8f\x91\
+\xb2\x25\x33\x2d\xdc\xda\xa2\x7b\x15\x70\xf5\x14\x20\xe2\xb3\x02\
+\xb8\x7a\x90\xbd\xbe\xcf\x3a\x06\xee\x1a\x92\x38\xf3\xbd\x63\x12\
+\x85\x09\xfd\x5f\x03\x59\x6d\x1e\x0a\x7c\x2d\x35\xb2\x76\x0c\xc5\
+\xa1\x15\xec\xcf\x84\xa7\x26\x2e\xa8\x31\xd9\x9d\x5e\x8b\x60\xe3\
+\xcc\xf2\x41\xfd\xae\x35\x2c\x29\xb8\x6a\xf9\x45\x35\x08\x6b\xdc\
+\xc7\x90\xb4\x92\x69\x68\x9e\x20\x43\xc4\xea\xf5\xca\xb1\x43\xf9\
+\xdb\x5a\x8a\xdc\x3d\xf7\x80\x3c\x20\x5e\xcb\xb3\x0b\x94\x8a\xe1\
+\x9a\xe5\xda\x94\xa6\x82\x61\x1b\x42\x1e\x6c\xbc\x75\xaf\x54\x51\
+\xf8\xbb\x63\xe4\x4e\xf1\xaa\xc9\xc0\x08\xbe\x94\x14\x7d\x67\x3c\
+\xa8\xd7\xd0\xa7\x39\x92\xd3\x4b\x9a\x81\x1d\xab\x24\x36\xbf\x1a\
+\xa4\x7f\xa7\x10\xab\x09\x2f\xaf\x73\xd7\x2c\xb5\x49\x4a\x8f\x8e\
+\xc1\x10\xf5\x50\x3d\x3a\x12\xb4\xcb\x8d\xd0\x08\xd1\x55\x95\x58\
+\xe4\x97\x3a\xf9\xbf\xca\x73\xb6\xa7\x89\xf7\x22\xae\xed\x7f\x89\
+\x97\x14\x0c\x88\x5d\x15\xec\x50\x29\x47\x3f\x57\x58\x2b\x2d\x65\
+\x16\xf9\xcf\x79\xcc\x5a\xe5\xfd\xf9\xa3\xd2\x0d\x56\xdf\xfc\x1a\
+\xe3\x2c\x47\x68\xe4\x95\xd6\xd6\x86\x07\x49\xb6\xb4\x6c\x51\x95\
+\xdd\x57\xe7\xd3\x38\x7c\xeb\x01\xf3\xe9\x1c\xc0\xad\xe7\x4b\xcb\
+\xe5\xc5\x63\xf5\x6a\x8f\x4c\xc7\xa0\xbf\x44\x22\xff\x82\x03\xff\
+\x42\x5b\x42\xff\xec\x3d\x0a\x2d\xd6\xd6\xa6\x9f\xcc\x04\xf4\xe1\
+\x74\x2d\x90\x66\x59\x85\xf8\x63\x2b\xe6\x1f\xa2\x28\xd7\x21\x29\
+\xf7\x41\xf8\x7e\xc0\x3a\xa4\x0b\xf2\xbe\x85\x34\x1d\x89\x03\xf9\
+\xe3\xcc\xac\x94\x7a\x10\xfd\x51\xe4\x4a\xb3\x51\xca\x4f\x8c\x2d\
+\xd4\x95\xb0\xf1\xd0\xef\x0d\xf5\x18\x9b\x46\x65\xed\xe8\xde\xbc\
+\xc1\xef\x56\xd7\xc6\xb5\x62\xbf\xd6\xdb\x63\xc1\x49\x07\xf5\xa2\
+\x3d\x32\x17\xf7\x24\x15\x7f\x3a\x4b\x53\xa3\x0c\xd5\x20\xaa\x85\
+\x31\xe9\xec\xf5\x49\xd8\xb5\x33\xd8\x27\x61\xaf\x53\xaa\x5d\x79\
+\x38\x0b\x37\xa9\xbc\x19\x33\x30\x43\x7f\x57\x6a\x3c\xf0\x6d\xfa\
+\x8c\xa8\xec\xdd\xa9\x70\xf8\x0c\x7a\xc7\x46\xce\xd8\x93\x50\xa8\
+\x21\x51\x3d\x58\xca\x90\x28\x76\xf8\xa9\xc6\xb6\x80\xd6\x8f\x5e\
+\x01\xa9\x37\xc6\x12\x44\xba\xd7\x93\x50\x68\x81\x66\x8a\x6c\x53\
+\xa8\x69\x99\x9f\xfa\x9f\x4f\x7e\xf7\x28\xb7\x0f\x59\x84\xbd\xde\
+\x0e\xb7\x8d\x3a\xca\x0b\xe4\xea\xe3\x0e\x14\xa1\x38\x93\xcf\x33\
+\xf0\xc0\x5b\x7a\x8d\x41\x19\x38\x95\x67\x1f\x34\x6d\x5d\x93\x95\
+\x65\x2e\xa6\xf9\x3e\x45\x69\x33\x3f\xaf\xf6\x41\x68\x67\x81\xa5\
+\xb4\x21\x1a\xb0\xcc\x73\xfc\xd3\x32\x05\x66\xeb\xd6\xc6\x1a\x4e\
+\xdd\x79\x7d\x86\xa1\x6d\x06\x61\x64\xdf\xbd\x86\x17\xb5\xb1\x9e\
+\x5a\xd7\xf0\xf4\xbb\xd7\x50\x9f\xe1\x99\x75\x0d\xcf\x1a\xd6\x40\
+\xcf\x28\xe0\x8b\x5f\xee\x4e\x74\x33\x1e\xdb\xe8\x72\x21\xb7\xc9\
+\xb6\xf8\xb7\xe7\xe6\xbd\x56\xbd\x85\x7b\x42\xfd\x11\x79\x4f\xd4\
+\xcf\x51\x1d\x86\xf1\x11\x0c\xf0\xe0\x8d\xd8\xef\x1b\x1a\x5b\x57\
+\x3c\x9e\xfb\x8f\x1a\x1d\x3a\xf4\x14\xac\xfe\xbc\xab\xfd\x1e\xcd\
+\xea\x68\xf8\x87\x81\x06\x11\xb6\x6c\x34\x50\x58\x18\x94\x58\x18\
+\x4c\x47\xf5\xbd\x06\x2c\xcc\x1e\x41\xf1\xfb\x7d\x53\x6b\x2b\x6d\
+\x8d\x67\xfe\xe3\xc6\x87\x1e\x3d\x05\xae\x3f\xeb\x6a\xbf\xe5\xdf\
+\x28\xe4\x82\x50\x6d\x7d\x05\xb9\x8f\x35\x3d\xba\x8f\x94\x8b\x3f\
+\x50\x2c\x92\x4c\x6c\x42\xce\xe3\xed\xae\xff\x0f\x4b\x69\x96\x8f\
+\x07\x1e\x07\x27\x6e\x4b\x2a\x2f\x38\xf1\x87\xfb\xc7\x71\xd5\xe7\
+\x4d\x1a\x9c\x99\xdd\x04\x2a\x42\x5a\xf3\x96\x63\x8c\x0f\xe8\x84\
+\xbf\xb1\x59\xb6\x61\xd6\xcc\x58\xa2\x85\x32\x93\x55\x46\x90\xab\
+\xd1\xb4\x71\x31\xff\xf6\x2a\x8d\x96\x18\xf6\x20\x6d\x9e\x2d\xde\
+\xaf\x06\xfa\xc7\xb8\xdb\xd5\x3f\x7d\xd2\x26\xe9\xf8\x78\x73\xa3\
+\xe9\x88\x2c\xb7\xbe\xfd\xfd\xe4\x79\x10\xcc\x64\x4b\xfc\xab\x31\
+\xb5\x70\xe1\x23\x7c\xde\x7a\xc0\xef\xa2\x31\x5f\xb0\xa2\xef\xf2\
+\x89\xca\x5c\x4d\x0a\xac\x27\xc5\x76\x32\x1d\x89\x27\x5f\xb5\xc4\
+\x2b\xcb\x9b\x3e\xf2\x5d\x20\xf3\x6f\xaa\xe8\xf7\x46\x91\x86\x59\
+\xfd\xa6\x7d\x54\x1b\x9e\x6e\x29\xcd\xd7\x6d\x1a\x6f\x2c\x76\xc6\
+\xdf\x96\xa3\xb1\xca\x94\x43\x75\xb9\xad\x8a\xf0\x0f\x1f\x05\xf4\
+\x88\xa4\xcc\x2a\xae\xd4\xc9\x27\xde\x2c\x95\x2d\x47\x5a\xb0\xff\
+\x72\xaa\xa3\x96\xc3\x69\x49\x77\xe4\xe5\xb3\x4d\x76\xf3\x76\x5b\
+\xe4\x29\x26\xd2\x1d\x45\x2a\x93\x4a\xff\x6b\x94\x34\xb1\xba\x50\
+\x03\xda\x64\xa8\x2f\xe1\x5f\x6e\xe4\x35\xda\x9d\x9a\x3e\x1e\x4f\
+\xd5\xc2\x3f\x9a\x57\xad\x10\x38\xa9\x66\xcd\xd5\xe3\xd7\xc4\x5b\
+\x51\x77\x44\xfc\x72\x46\x5e\x1f\x8d\x13\x8a\x6d\xbc\xf2\x65\x29\
+\x97\xa7\x07\xdc\xbf\x5a\xba\x8a\xac\x2d\x56\x2e\xee\x9e\x1c\x75\
+\x1b\x35\xb9\xbb\x88\x47\x32\xdd\x8b\x66\xfe\x67\xc0\xbc\x96\x6d\
+\x66\x24\xc8\x07\x46\x6f\x3c\x26\x6e\xc2\x06\x2e\xc7\xd3\xce\x02\
+\xaf\x8c\x36\x52\x7f\xb5\x09\xce\x89\x8d\x4c\xe6\xb6\xab\xd7\xc3\
+\x41\xfb\xb3\xa1\x75\xa0\xf8\x5f\xb5\x7c\xec\x8c\x72\xab\xbe\x6f\
+\x42\x8a\x6d\x7d\x08\x52\x1b\xf6\xef\x1e\xe8\xf4\x9c\xe0\xfb\x21\
+\xac\x06\x22\x12\x4e\x1e\x11\xa8\xf2\x78\x00\x79\xf4\xca\x77\x6e\
+\xd6\xc3\xa0\xc2\x5c\x63\x83\x8b\xf1\x17\xcf\xc0\x34\xb4\x3f\x89\
+\x56\x6f\x2a\x19\xb9\xf1\xe0\x99\x99\x00\x6c\x3e\x6b\x66\x69\xae\
+\xbd\xd9\xad\xbf\x5c\xe6\xd6\x06\x91\xef\x93\xd5\xdb\x4a\xe6\x3e\
+\x76\x1c\xdf\x32\x52\xe5\x25\x2c\xf3\x61\x27\xca\x98\x27\xef\xfd\
+\x7b\x3c\xe2\xe1\x52\x3a\x83\xaa\x1c\x80\xa4\x19\x3e\xc0\x50\x8f\
+\x2a\x72\x25\x0f\xc0\xbf\x2e\x2a\xc3\xb1\xf9\x5f\x17\x8d\x39\xf2\
+\xd5\x76\x59\xde\x88\xe0\x03\xd6\xc6\x33\x1f\x09\x8f\xa4\xe0\xb2\
+\x42\xd3\x10\x05\xf2\xaf\x26\x83\x3f\xe8\x95\xbf\x6a\xa2\xb7\x56\
+\xa8\x84\x3a\x22\xf3\x47\xa3\xa7\x2a\xc5\x02\x33\x37\x90\xde\x28\
+\xab\xfd\xed\x21\x7c\x27\xa3\xfe\xd6\xac\x68\x66\x7f\x69\x36\x50\
+\x2f\xe7\x68\x04\xd3\xe1\x99\x6c\x04\x4d\x73\xfc\xac\xf4\xb0\x89\
+\x6c\x77\x19\x33\x62\x3c\x1a\x1d\x99\x21\xb5\x49\x67\x32\x75\xcb\
+\xa0\x89\x48\xb9\x22\xbd\xaa\xbf\x01\xdd\xad\x95\x22\x8f\x94\x77\
+\xad\xd7\x69\x24\x72\x7b\x8c\xd0\x46\x2f\x67\xf8\x62\xb9\x1f\x8e\
+\xd5\x5f\xb6\xa5\xc4\x7a\x7c\x4d\x56\x65\xea\x73\x49\x25\x45\xbc\
+\xb7\xfb\x42\xef\x99\xfb\x0e\xbd\xa1\xee\x54\x9e\x5c\xff\x7f\x2f\
+\x2e\x80\x20\
+\x00\x01\x6e\xac\
+\x2f\
+\x2a\x21\x20\x6a\x51\x75\x65\x72\x79\x20\x76\x31\x2e\x37\x2e\x31\
+\x20\x6a\x71\x75\x65\x72\x79\x2e\x63\x6f\x6d\x20\x7c\x20\x6a\x71\
+\x75\x65\x72\x79\x2e\x6f\x72\x67\x2f\x6c\x69\x63\x65\x6e\x73\x65\
+\x20\x2a\x2f\x0a\x28\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\
+\x62\x29\x7b\x66\x75\x6e\x63\x74\x69\x6f\x6e\x20\x63\x79\x28\x61\
+\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x66\x2e\x69\x73\x57\x69\x6e\
+\x64\x6f\x77\x28\x61\x29\x3f\x61\x3a\x61\x2e\x6e\x6f\x64\x65\x54\
+\x79\x70\x65\x3d\x3d\x3d\x39\x3f\x61\x2e\x64\x65\x66\x61\x75\x6c\
+\x74\x56\x69\x65\x77\x7c\x7c\x61\x2e\x70\x61\x72\x65\x6e\x74\x57\
+\x69\x6e\x64\x6f\x77\x3a\x21\x31\x7d\x66\x75\x6e\x63\x74\x69\x6f\
+\x6e\x20\x63\x76\x28\x61\x29\x7b\x69\x66\x28\x21\x63\x6b\x5b\x61\
+\x5d\x29\x7b\x76\x61\x72\x20\x62\x3d\x63\x2e\x62\x6f\x64\x79\x2c\
+\x64\x3d\x66\x28\x22\x3c\x22\x2b\x61\x2b\x22\x3e\x22\x29\x2e\x61\
+\x70\x70\x65\x6e\x64\x54\x6f\x28\x62\x29\x2c\x65\x3d\x64\x2e\x63\
+\x73\x73\x28\x22\x64\x69\x73\x70\x6c\x61\x79\x22\x29\x3b\x64\x2e\
+\x72\x65\x6d\x6f\x76\x65\x28\x29\x3b\x69\x66\x28\x65\x3d\x3d\x3d\
+\x22\x6e\x6f\x6e\x65\x22\x7c\x7c\x65\x3d\x3d\x3d\x22\x22\x29\x7b\
+\x63\x6c\x7c\x7c\x28\x63\x6c\x3d\x63\x2e\x63\x72\x65\x61\x74\x65\
+\x45\x6c\x65\x6d\x65\x6e\x74\x28\x22\x69\x66\x72\x61\x6d\x65\x22\
+\x29\x2c\x63\x6c\x2e\x66\x72\x61\x6d\x65\x42\x6f\x72\x64\x65\x72\
+\x3d\x63\x6c\x2e\x77\x69\x64\x74\x68\x3d\x63\x6c\x2e\x68\x65\x69\
+\x67\x68\x74\x3d\x30\x29\x2c\x62\x2e\x61\x70\x70\x65\x6e\x64\x43\
+\x68\x69\x6c\x64\x28\x63\x6c\x29\x3b\x69\x66\x28\x21\x63\x6d\x7c\
+\x7c\x21\x63\x6c\x2e\x63\x72\x65\x61\x74\x65\x45\x6c\x65\x6d\x65\
+\x6e\x74\x29\x63\x6d\x3d\x28\x63\x6c\x2e\x63\x6f\x6e\x74\x65\x6e\
+\x74\x57\x69\x6e\x64\x6f\x77\x7c\x7c\x63\x6c\x2e\x63\x6f\x6e\x74\
+\x65\x6e\x74\x44\x6f\x63\x75\x6d\x65\x6e\x74\x29\x2e\x64\x6f\x63\
+\x75\x6d\x65\x6e\x74\x2c\x63\x6d\x2e\x77\x72\x69\x74\x65\x28\x28\
+\x63\x2e\x63\x6f\x6d\x70\x61\x74\x4d\x6f\x64\x65\x3d\x3d\x3d\x22\
+\x43\x53\x53\x31\x43\x6f\x6d\x70\x61\x74\x22\x3f\x22\x3c\x21\x64\
+\x6f\x63\x74\x79\x70\x65\x20\x68\x74\x6d\x6c\x3e\x22\x3a\x22\x22\
+\x29\x2b\x22\x3c\x68\x74\x6d\x6c\x3e\x3c\x62\x6f\x64\x79\x3e\x22\
+\x29\x2c\x63\x6d\x2e\x63\x6c\x6f\x73\x65\x28\x29\x3b\x64\x3d\x63\
+\x6d\x2e\x63\x72\x65\x61\x74\x65\x45\x6c\x65\x6d\x65\x6e\x74\x28\
+\x61\x29\x2c\x63\x6d\x2e\x62\x6f\x64\x79\x2e\x61\x70\x70\x65\x6e\
+\x64\x43\x68\x69\x6c\x64\x28\x64\x29\x2c\x65\x3d\x66\x2e\x63\x73\
+\x73\x28\x64\x2c\x22\x64\x69\x73\x70\x6c\x61\x79\x22\x29\x2c\x62\
+\x2e\x72\x65\x6d\x6f\x76\x65\x43\x68\x69\x6c\x64\x28\x63\x6c\x29\
+\x7d\x63\x6b\x5b\x61\x5d\x3d\x65\x7d\x72\x65\x74\x75\x72\x6e\x20\
+\x63\x6b\x5b\x61\x5d\x7d\x66\x75\x6e\x63\x74\x69\x6f\x6e\x20\x63\
+\x75\x28\x61\x2c\x62\x29\x7b\x76\x61\x72\x20\x63\x3d\x7b\x7d\x3b\
+\x66\x2e\x65\x61\x63\x68\x28\x63\x71\x2e\x63\x6f\x6e\x63\x61\x74\
+\x2e\x61\x70\x70\x6c\x79\x28\x5b\x5d\x2c\x63\x71\x2e\x73\x6c\x69\
+\x63\x65\x28\x30\x2c\x62\x29\x29\x2c\x66\x75\x6e\x63\x74\x69\x6f\
+\x6e\x28\x29\x7b\x63\x5b\x74\x68\x69\x73\x5d\x3d\x61\x7d\x29\x3b\
+\x72\x65\x74\x75\x72\x6e\x20\x63\x7d\x66\x75\x6e\x63\x74\x69\x6f\
+\x6e\x20\x63\x74\x28\x29\x7b\x63\x72\x3d\x62\x7d\x66\x75\x6e\x63\
+\x74\x69\x6f\x6e\x20\x63\x73\x28\x29\x7b\x73\x65\x74\x54\x69\x6d\
+\x65\x6f\x75\x74\x28\x63\x74\x2c\x30\x29\x3b\x72\x65\x74\x75\x72\
+\x6e\x20\x63\x72\x3d\x66\x2e\x6e\x6f\x77\x28\x29\x7d\x66\x75\x6e\
+\x63\x74\x69\x6f\x6e\x20\x63\x6a\x28\x29\x7b\x74\x72\x79\x7b\x72\
+\x65\x74\x75\x72\x6e\x20\x6e\x65\x77\x20\x61\x2e\x41\x63\x74\x69\
+\x76\x65\x58\x4f\x62\x6a\x65\x63\x74\x28\x22\x4d\x69\x63\x72\x6f\
+\x73\x6f\x66\x74\x2e\x58\x4d\x4c\x48\x54\x54\x50\x22\x29\x7d\x63\
+\x61\x74\x63\x68\x28\x62\x29\x7b\x7d\x7d\x66\x75\x6e\x63\x74\x69\
+\x6f\x6e\x20\x63\x69\x28\x29\x7b\x74\x72\x79\x7b\x72\x65\x74\x75\
+\x72\x6e\x20\x6e\x65\x77\x20\x61\x2e\x58\x4d\x4c\x48\x74\x74\x70\
+\x52\x65\x71\x75\x65\x73\x74\x7d\x63\x61\x74\x63\x68\x28\x62\x29\
+\x7b\x7d\x7d\x66\x75\x6e\x63\x74\x69\x6f\x6e\x20\x63\x63\x28\x61\
+\x2c\x63\x29\x7b\x61\x2e\x64\x61\x74\x61\x46\x69\x6c\x74\x65\x72\
+\x26\x26\x28\x63\x3d\x61\x2e\x64\x61\x74\x61\x46\x69\x6c\x74\x65\
+\x72\x28\x63\x2c\x61\x2e\x64\x61\x74\x61\x54\x79\x70\x65\x29\x29\
+\x3b\x76\x61\x72\x20\x64\x3d\x61\x2e\x64\x61\x74\x61\x54\x79\x70\
+\x65\x73\x2c\x65\x3d\x7b\x7d\x2c\x67\x2c\x68\x2c\x69\x3d\x64\x2e\
+\x6c\x65\x6e\x67\x74\x68\x2c\x6a\x2c\x6b\x3d\x64\x5b\x30\x5d\x2c\
+\x6c\x2c\x6d\x2c\x6e\x2c\x6f\x2c\x70\x3b\x66\x6f\x72\x28\x67\x3d\
+\x31\x3b\x67\x3c\x69\x3b\x67\x2b\x2b\x29\x7b\x69\x66\x28\x67\x3d\
+\x3d\x3d\x31\x29\x66\x6f\x72\x28\x68\x20\x69\x6e\x20\x61\x2e\x63\
+\x6f\x6e\x76\x65\x72\x74\x65\x72\x73\x29\x74\x79\x70\x65\x6f\x66\
+\x20\x68\x3d\x3d\x22\x73\x74\x72\x69\x6e\x67\x22\x26\x26\x28\x65\
+\x5b\x68\x2e\x74\x6f\x4c\x6f\x77\x65\x72\x43\x61\x73\x65\x28\x29\
+\x5d\x3d\x61\x2e\x63\x6f\x6e\x76\x65\x72\x74\x65\x72\x73\x5b\x68\
+\x5d\x29\x3b\x6c\x3d\x6b\x2c\x6b\x3d\x64\x5b\x67\x5d\x3b\x69\x66\
+\x28\x6b\x3d\x3d\x3d\x22\x2a\x22\x29\x6b\x3d\x6c\x3b\x65\x6c\x73\
+\x65\x20\x69\x66\x28\x6c\x21\x3d\x3d\x22\x2a\x22\x26\x26\x6c\x21\
+\x3d\x3d\x6b\x29\x7b\x6d\x3d\x6c\x2b\x22\x20\x22\x2b\x6b\x2c\x6e\
+\x3d\x65\x5b\x6d\x5d\x7c\x7c\x65\x5b\x22\x2a\x20\x22\x2b\x6b\x5d\
+\x3b\x69\x66\x28\x21\x6e\x29\x7b\x70\x3d\x62\x3b\x66\x6f\x72\x28\
+\x6f\x20\x69\x6e\x20\x65\x29\x7b\x6a\x3d\x6f\x2e\x73\x70\x6c\x69\
+\x74\x28\x22\x20\x22\x29\x3b\x69\x66\x28\x6a\x5b\x30\x5d\x3d\x3d\
+\x3d\x6c\x7c\x7c\x6a\x5b\x30\x5d\x3d\x3d\x3d\x22\x2a\x22\x29\x7b\
+\x70\x3d\x65\x5b\x6a\x5b\x31\x5d\x2b\x22\x20\x22\x2b\x6b\x5d\x3b\
+\x69\x66\x28\x70\x29\x7b\x6f\x3d\x65\x5b\x6f\x5d\x2c\x6f\x3d\x3d\
+\x3d\x21\x30\x3f\x6e\x3d\x70\x3a\x70\x3d\x3d\x3d\x21\x30\x26\x26\
+\x28\x6e\x3d\x6f\x29\x3b\x62\x72\x65\x61\x6b\x7d\x7d\x7d\x7d\x21\
+\x6e\x26\x26\x21\x70\x26\x26\x66\x2e\x65\x72\x72\x6f\x72\x28\x22\
+\x4e\x6f\x20\x63\x6f\x6e\x76\x65\x72\x73\x69\x6f\x6e\x20\x66\x72\
+\x6f\x6d\x20\x22\x2b\x6d\x2e\x72\x65\x70\x6c\x61\x63\x65\x28\x22\
+\x20\x22\x2c\x22\x20\x74\x6f\x20\x22\x29\x29\x2c\x6e\x21\x3d\x3d\
+\x21\x30\x26\x26\x28\x63\x3d\x6e\x3f\x6e\x28\x63\x29\x3a\x70\x28\
+\x6f\x28\x63\x29\x29\x29\x7d\x7d\x72\x65\x74\x75\x72\x6e\x20\x63\
+\x7d\x66\x75\x6e\x63\x74\x69\x6f\x6e\x20\x63\x62\x28\x61\x2c\x63\
+\x2c\x64\x29\x7b\x76\x61\x72\x20\x65\x3d\x61\x2e\x63\x6f\x6e\x74\
+\x65\x6e\x74\x73\x2c\x66\x3d\x61\x2e\x64\x61\x74\x61\x54\x79\x70\
+\x65\x73\x2c\x67\x3d\x61\x2e\x72\x65\x73\x70\x6f\x6e\x73\x65\x46\
+\x69\x65\x6c\x64\x73\x2c\x68\x2c\x69\x2c\x6a\x2c\x6b\x3b\x66\x6f\
+\x72\x28\x69\x20\x69\x6e\x20\x67\x29\x69\x20\x69\x6e\x20\x64\x26\
+\x26\x28\x63\x5b\x67\x5b\x69\x5d\x5d\x3d\x64\x5b\x69\x5d\x29\x3b\
+\x77\x68\x69\x6c\x65\x28\x66\x5b\x30\x5d\x3d\x3d\x3d\x22\x2a\x22\
+\x29\x66\x2e\x73\x68\x69\x66\x74\x28\x29\x2c\x68\x3d\x3d\x3d\x62\
+\x26\x26\x28\x68\x3d\x61\x2e\x6d\x69\x6d\x65\x54\x79\x70\x65\x7c\
+\x7c\x63\x2e\x67\x65\x74\x52\x65\x73\x70\x6f\x6e\x73\x65\x48\x65\
+\x61\x64\x65\x72\x28\x22\x63\x6f\x6e\x74\x65\x6e\x74\x2d\x74\x79\
+\x70\x65\x22\x29\x29\x3b\x69\x66\x28\x68\x29\x66\x6f\x72\x28\x69\
+\x20\x69\x6e\x20\x65\x29\x69\x66\x28\x65\x5b\x69\x5d\x26\x26\x65\
+\x5b\x69\x5d\x2e\x74\x65\x73\x74\x28\x68\x29\x29\x7b\x66\x2e\x75\
+\x6e\x73\x68\x69\x66\x74\x28\x69\x29\x3b\x62\x72\x65\x61\x6b\x7d\
+\x69\x66\x28\x66\x5b\x30\x5d\x69\x6e\x20\x64\x29\x6a\x3d\x66\x5b\
+\x30\x5d\x3b\x65\x6c\x73\x65\x7b\x66\x6f\x72\x28\x69\x20\x69\x6e\
+\x20\x64\x29\x7b\x69\x66\x28\x21\x66\x5b\x30\x5d\x7c\x7c\x61\x2e\
+\x63\x6f\x6e\x76\x65\x72\x74\x65\x72\x73\x5b\x69\x2b\x22\x20\x22\
+\x2b\x66\x5b\x30\x5d\x5d\x29\x7b\x6a\x3d\x69\x3b\x62\x72\x65\x61\
+\x6b\x7d\x6b\x7c\x7c\x28\x6b\x3d\x69\x29\x7d\x6a\x3d\x6a\x7c\x7c\
+\x6b\x7d\x69\x66\x28\x6a\x29\x7b\x6a\x21\x3d\x3d\x66\x5b\x30\x5d\
+\x26\x26\x66\x2e\x75\x6e\x73\x68\x69\x66\x74\x28\x6a\x29\x3b\x72\
+\x65\x74\x75\x72\x6e\x20\x64\x5b\x6a\x5d\x7d\x7d\x66\x75\x6e\x63\
+\x74\x69\x6f\x6e\x20\x63\x61\x28\x61\x2c\x62\x2c\x63\x2c\x64\x29\
+\x7b\x69\x66\x28\x66\x2e\x69\x73\x41\x72\x72\x61\x79\x28\x62\x29\
+\x29\x66\x2e\x65\x61\x63\x68\x28\x62\x2c\x66\x75\x6e\x63\x74\x69\
+\x6f\x6e\x28\x62\x2c\x65\x29\x7b\x63\x7c\x7c\x62\x45\x2e\x74\x65\
+\x73\x74\x28\x61\x29\x3f\x64\x28\x61\x2c\x65\x29\x3a\x63\x61\x28\
+\x61\x2b\x22\x5b\x22\x2b\x28\x74\x79\x70\x65\x6f\x66\x20\x65\x3d\
+\x3d\x22\x6f\x62\x6a\x65\x63\x74\x22\x7c\x7c\x66\x2e\x69\x73\x41\
+\x72\x72\x61\x79\x28\x65\x29\x3f\x62\x3a\x22\x22\x29\x2b\x22\x5d\
+\x22\x2c\x65\x2c\x63\x2c\x64\x29\x7d\x29\x3b\x65\x6c\x73\x65\x20\
+\x69\x66\x28\x21\x63\x26\x26\x62\x21\x3d\x6e\x75\x6c\x6c\x26\x26\
+\x74\x79\x70\x65\x6f\x66\x20\x62\x3d\x3d\x22\x6f\x62\x6a\x65\x63\
+\x74\x22\x29\x66\x6f\x72\x28\x76\x61\x72\x20\x65\x20\x69\x6e\x20\
+\x62\x29\x63\x61\x28\x61\x2b\x22\x5b\x22\x2b\x65\x2b\x22\x5d\x22\
+\x2c\x62\x5b\x65\x5d\x2c\x63\x2c\x64\x29\x3b\x65\x6c\x73\x65\x20\
+\x64\x28\x61\x2c\x62\x29\x7d\x66\x75\x6e\x63\x74\x69\x6f\x6e\x20\
+\x62\x5f\x28\x61\x2c\x63\x29\x7b\x76\x61\x72\x20\x64\x2c\x65\x2c\
+\x67\x3d\x66\x2e\x61\x6a\x61\x78\x53\x65\x74\x74\x69\x6e\x67\x73\
+\x2e\x66\x6c\x61\x74\x4f\x70\x74\x69\x6f\x6e\x73\x7c\x7c\x7b\x7d\
+\x3b\x66\x6f\x72\x28\x64\x20\x69\x6e\x20\x63\x29\x63\x5b\x64\x5d\
+\x21\x3d\x3d\x62\x26\x26\x28\x28\x67\x5b\x64\x5d\x3f\x61\x3a\x65\
+\x7c\x7c\x28\x65\x3d\x7b\x7d\x29\x29\x5b\x64\x5d\x3d\x63\x5b\x64\
+\x5d\x29\x3b\x65\x26\x26\x66\x2e\x65\x78\x74\x65\x6e\x64\x28\x21\
+\x30\x2c\x61\x2c\x65\x29\x7d\x66\x75\x6e\x63\x74\x69\x6f\x6e\x20\
+\x62\x24\x28\x61\x2c\x63\x2c\x64\x2c\x65\x2c\x66\x2c\x67\x29\x7b\
+\x66\x3d\x66\x7c\x7c\x63\x2e\x64\x61\x74\x61\x54\x79\x70\x65\x73\
+\x5b\x30\x5d\x2c\x67\x3d\x67\x7c\x7c\x7b\x7d\x2c\x67\x5b\x66\x5d\
+\x3d\x21\x30\x3b\x76\x61\x72\x20\x68\x3d\x61\x5b\x66\x5d\x2c\x69\
+\x3d\x30\x2c\x6a\x3d\x68\x3f\x68\x2e\x6c\x65\x6e\x67\x74\x68\x3a\
+\x30\x2c\x6b\x3d\x61\x3d\x3d\x3d\x62\x54\x2c\x6c\x3b\x66\x6f\x72\
+\x28\x3b\x69\x3c\x6a\x26\x26\x28\x6b\x7c\x7c\x21\x6c\x29\x3b\x69\
+\x2b\x2b\x29\x6c\x3d\x68\x5b\x69\x5d\x28\x63\x2c\x64\x2c\x65\x29\
+\x2c\x74\x79\x70\x65\x6f\x66\x20\x6c\x3d\x3d\x22\x73\x74\x72\x69\
+\x6e\x67\x22\x26\x26\x28\x21\x6b\x7c\x7c\x67\x5b\x6c\x5d\x3f\x6c\
+\x3d\x62\x3a\x28\x63\x2e\x64\x61\x74\x61\x54\x79\x70\x65\x73\x2e\
+\x75\x6e\x73\x68\x69\x66\x74\x28\x6c\x29\x2c\x6c\x3d\x62\x24\x28\
+\x61\x2c\x63\x2c\x64\x2c\x65\x2c\x6c\x2c\x67\x29\x29\x29\x3b\x28\
+\x6b\x7c\x7c\x21\x6c\x29\x26\x26\x21\x67\x5b\x22\x2a\x22\x5d\x26\
+\x26\x28\x6c\x3d\x62\x24\x28\x61\x2c\x63\x2c\x64\x2c\x65\x2c\x22\
+\x2a\x22\x2c\x67\x29\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x6c\x7d\
+\x66\x75\x6e\x63\x74\x69\x6f\x6e\x20\x62\x5a\x28\x61\x29\x7b\x72\
+\x65\x74\x75\x72\x6e\x20\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x62\
+\x2c\x63\x29\x7b\x74\x79\x70\x65\x6f\x66\x20\x62\x21\x3d\x22\x73\
+\x74\x72\x69\x6e\x67\x22\x26\x26\x28\x63\x3d\x62\x2c\x62\x3d\x22\
+\x2a\x22\x29\x3b\x69\x66\x28\x66\x2e\x69\x73\x46\x75\x6e\x63\x74\
+\x69\x6f\x6e\x28\x63\x29\x29\x7b\x76\x61\x72\x20\x64\x3d\x62\x2e\
+\x74\x6f\x4c\x6f\x77\x65\x72\x43\x61\x73\x65\x28\x29\x2e\x73\x70\
+\x6c\x69\x74\x28\x62\x50\x29\x2c\x65\x3d\x30\x2c\x67\x3d\x64\x2e\
+\x6c\x65\x6e\x67\x74\x68\x2c\x68\x2c\x69\x2c\x6a\x3b\x66\x6f\x72\
+\x28\x3b\x65\x3c\x67\x3b\x65\x2b\x2b\x29\x68\x3d\x64\x5b\x65\x5d\
+\x2c\x6a\x3d\x2f\x5e\x5c\x2b\x2f\x2e\x74\x65\x73\x74\x28\x68\x29\
+\x2c\x6a\x26\x26\x28\x68\x3d\x68\x2e\x73\x75\x62\x73\x74\x72\x28\
+\x31\x29\x7c\x7c\x22\x2a\x22\x29\x2c\x69\x3d\x61\x5b\x68\x5d\x3d\
+\x61\x5b\x68\x5d\x7c\x7c\x5b\x5d\x2c\x69\x5b\x6a\x3f\x22\x75\x6e\
+\x73\x68\x69\x66\x74\x22\x3a\x22\x70\x75\x73\x68\x22\x5d\x28\x63\
+\x29\x7d\x7d\x7d\x66\x75\x6e\x63\x74\x69\x6f\x6e\x20\x62\x43\x28\
+\x61\x2c\x62\x2c\x63\x29\x7b\x76\x61\x72\x20\x64\x3d\x62\x3d\x3d\
+\x3d\x22\x77\x69\x64\x74\x68\x22\x3f\x61\x2e\x6f\x66\x66\x73\x65\
+\x74\x57\x69\x64\x74\x68\x3a\x61\x2e\x6f\x66\x66\x73\x65\x74\x48\
+\x65\x69\x67\x68\x74\x2c\x65\x3d\x62\x3d\x3d\x3d\x22\x77\x69\x64\
+\x74\x68\x22\x3f\x62\x78\x3a\x62\x79\x2c\x67\x3d\x30\x2c\x68\x3d\
+\x65\x2e\x6c\x65\x6e\x67\x74\x68\x3b\x69\x66\x28\x64\x3e\x30\x29\
+\x7b\x69\x66\x28\x63\x21\x3d\x3d\x22\x62\x6f\x72\x64\x65\x72\x22\
+\x29\x66\x6f\x72\x28\x3b\x67\x3c\x68\x3b\x67\x2b\x2b\x29\x63\x7c\
+\x7c\x28\x64\x2d\x3d\x70\x61\x72\x73\x65\x46\x6c\x6f\x61\x74\x28\
+\x66\x2e\x63\x73\x73\x28\x61\x2c\x22\x70\x61\x64\x64\x69\x6e\x67\
+\x22\x2b\x65\x5b\x67\x5d\x29\x29\x7c\x7c\x30\x29\x2c\x63\x3d\x3d\
+\x3d\x22\x6d\x61\x72\x67\x69\x6e\x22\x3f\x64\x2b\x3d\x70\x61\x72\
+\x73\x65\x46\x6c\x6f\x61\x74\x28\x66\x2e\x63\x73\x73\x28\x61\x2c\
+\x63\x2b\x65\x5b\x67\x5d\x29\x29\x7c\x7c\x30\x3a\x64\x2d\x3d\x70\
+\x61\x72\x73\x65\x46\x6c\x6f\x61\x74\x28\x66\x2e\x63\x73\x73\x28\
+\x61\x2c\x22\x62\x6f\x72\x64\x65\x72\x22\x2b\x65\x5b\x67\x5d\x2b\
+\x22\x57\x69\x64\x74\x68\x22\x29\x29\x7c\x7c\x30\x3b\x72\x65\x74\
+\x75\x72\x6e\x20\x64\x2b\x22\x70\x78\x22\x7d\x64\x3d\x62\x7a\x28\
+\x61\x2c\x62\x2c\x62\x29\x3b\x69\x66\x28\x64\x3c\x30\x7c\x7c\x64\
+\x3d\x3d\x6e\x75\x6c\x6c\x29\x64\x3d\x61\x2e\x73\x74\x79\x6c\x65\
+\x5b\x62\x5d\x7c\x7c\x30\x3b\x64\x3d\x70\x61\x72\x73\x65\x46\x6c\
+\x6f\x61\x74\x28\x64\x29\x7c\x7c\x30\x3b\x69\x66\x28\x63\x29\x66\
+\x6f\x72\x28\x3b\x67\x3c\x68\x3b\x67\x2b\x2b\x29\x64\x2b\x3d\x70\
+\x61\x72\x73\x65\x46\x6c\x6f\x61\x74\x28\x66\x2e\x63\x73\x73\x28\
+\x61\x2c\x22\x70\x61\x64\x64\x69\x6e\x67\x22\x2b\x65\x5b\x67\x5d\
+\x29\x29\x7c\x7c\x30\x2c\x63\x21\x3d\x3d\x22\x70\x61\x64\x64\x69\
+\x6e\x67\x22\x26\x26\x28\x64\x2b\x3d\x70\x61\x72\x73\x65\x46\x6c\
+\x6f\x61\x74\x28\x66\x2e\x63\x73\x73\x28\x61\x2c\x22\x62\x6f\x72\
+\x64\x65\x72\x22\x2b\x65\x5b\x67\x5d\x2b\x22\x57\x69\x64\x74\x68\
+\x22\x29\x29\x7c\x7c\x30\x29\x2c\x63\x3d\x3d\x3d\x22\x6d\x61\x72\
+\x67\x69\x6e\x22\x26\x26\x28\x64\x2b\x3d\x70\x61\x72\x73\x65\x46\
+\x6c\x6f\x61\x74\x28\x66\x2e\x63\x73\x73\x28\x61\x2c\x63\x2b\x65\
+\x5b\x67\x5d\x29\x29\x7c\x7c\x30\x29\x3b\x72\x65\x74\x75\x72\x6e\
+\x20\x64\x2b\x22\x70\x78\x22\x7d\x66\x75\x6e\x63\x74\x69\x6f\x6e\
+\x20\x62\x70\x28\x61\x2c\x62\x29\x7b\x62\x2e\x73\x72\x63\x3f\x66\
+\x2e\x61\x6a\x61\x78\x28\x7b\x75\x72\x6c\x3a\x62\x2e\x73\x72\x63\
+\x2c\x61\x73\x79\x6e\x63\x3a\x21\x31\x2c\x64\x61\x74\x61\x54\x79\
+\x70\x65\x3a\x22\x73\x63\x72\x69\x70\x74\x22\x7d\x29\x3a\x66\x2e\
+\x67\x6c\x6f\x62\x61\x6c\x45\x76\x61\x6c\x28\x28\x62\x2e\x74\x65\
+\x78\x74\x7c\x7c\x62\x2e\x74\x65\x78\x74\x43\x6f\x6e\x74\x65\x6e\
+\x74\x7c\x7c\x62\x2e\x69\x6e\x6e\x65\x72\x48\x54\x4d\x4c\x7c\x7c\
+\x22\x22\x29\x2e\x72\x65\x70\x6c\x61\x63\x65\x28\x62\x66\x2c\x22\
+\x2f\x2a\x24\x30\x2a\x2f\x22\x29\x29\x2c\x62\x2e\x70\x61\x72\x65\
+\x6e\x74\x4e\x6f\x64\x65\x26\x26\x62\x2e\x70\x61\x72\x65\x6e\x74\
+\x4e\x6f\x64\x65\x2e\x72\x65\x6d\x6f\x76\x65\x43\x68\x69\x6c\x64\
+\x28\x62\x29\x7d\x66\x75\x6e\x63\x74\x69\x6f\x6e\x20\x62\x6f\x28\
+\x61\x29\x7b\x76\x61\x72\x20\x62\x3d\x63\x2e\x63\x72\x65\x61\x74\
+\x65\x45\x6c\x65\x6d\x65\x6e\x74\x28\x22\x64\x69\x76\x22\x29\x3b\
+\x62\x68\x2e\x61\x70\x70\x65\x6e\x64\x43\x68\x69\x6c\x64\x28\x62\
+\x29\x2c\x62\x2e\x69\x6e\x6e\x65\x72\x48\x54\x4d\x4c\x3d\x61\x2e\
+\x6f\x75\x74\x65\x72\x48\x54\x4d\x4c\x3b\x72\x65\x74\x75\x72\x6e\
+\x20\x62\x2e\x66\x69\x72\x73\x74\x43\x68\x69\x6c\x64\x7d\x66\x75\
+\x6e\x63\x74\x69\x6f\x6e\x20\x62\x6e\x28\x61\x29\x7b\x76\x61\x72\
+\x20\x62\x3d\x28\x61\x2e\x6e\x6f\x64\x65\x4e\x61\x6d\x65\x7c\x7c\
+\x22\x22\x29\x2e\x74\x6f\x4c\x6f\x77\x65\x72\x43\x61\x73\x65\x28\
+\x29\x3b\x62\x3d\x3d\x3d\x22\x69\x6e\x70\x75\x74\x22\x3f\x62\x6d\
+\x28\x61\x29\x3a\x62\x21\x3d\x3d\x22\x73\x63\x72\x69\x70\x74\x22\
+\x26\x26\x74\x79\x70\x65\x6f\x66\x20\x61\x2e\x67\x65\x74\x45\x6c\
+\x65\x6d\x65\x6e\x74\x73\x42\x79\x54\x61\x67\x4e\x61\x6d\x65\x21\
+\x3d\x22\x75\x6e\x64\x65\x66\x69\x6e\x65\x64\x22\x26\x26\x66\x2e\
+\x67\x72\x65\x70\x28\x61\x2e\x67\x65\x74\x45\x6c\x65\x6d\x65\x6e\
+\x74\x73\x42\x79\x54\x61\x67\x4e\x61\x6d\x65\x28\x22\x69\x6e\x70\
+\x75\x74\x22\x29\x2c\x62\x6d\x29\x7d\x66\x75\x6e\x63\x74\x69\x6f\
+\x6e\x20\x62\x6d\x28\x61\x29\x7b\x69\x66\x28\x61\x2e\x74\x79\x70\
+\x65\x3d\x3d\x3d\x22\x63\x68\x65\x63\x6b\x62\x6f\x78\x22\x7c\x7c\
+\x61\x2e\x74\x79\x70\x65\x3d\x3d\x3d\x22\x72\x61\x64\x69\x6f\x22\
+\x29\x61\x2e\x64\x65\x66\x61\x75\x6c\x74\x43\x68\x65\x63\x6b\x65\
+\x64\x3d\x61\x2e\x63\x68\x65\x63\x6b\x65\x64\x7d\x66\x75\x6e\x63\
+\x74\x69\x6f\x6e\x20\x62\x6c\x28\x61\x29\x7b\x72\x65\x74\x75\x72\
+\x6e\x20\x74\x79\x70\x65\x6f\x66\x20\x61\x2e\x67\x65\x74\x45\x6c\
+\x65\x6d\x65\x6e\x74\x73\x42\x79\x54\x61\x67\x4e\x61\x6d\x65\x21\
+\x3d\x22\x75\x6e\x64\x65\x66\x69\x6e\x65\x64\x22\x3f\x61\x2e\x67\
+\x65\x74\x45\x6c\x65\x6d\x65\x6e\x74\x73\x42\x79\x54\x61\x67\x4e\
+\x61\x6d\x65\x28\x22\x2a\x22\x29\x3a\x74\x79\x70\x65\x6f\x66\x20\
+\x61\x2e\x71\x75\x65\x72\x79\x53\x65\x6c\x65\x63\x74\x6f\x72\x41\
+\x6c\x6c\x21\x3d\x22\x75\x6e\x64\x65\x66\x69\x6e\x65\x64\x22\x3f\
+\x61\x2e\x71\x75\x65\x72\x79\x53\x65\x6c\x65\x63\x74\x6f\x72\x41\
+\x6c\x6c\x28\x22\x2a\x22\x29\x3a\x5b\x5d\x7d\x66\x75\x6e\x63\x74\
+\x69\x6f\x6e\x20\x62\x6b\x28\x61\x2c\x62\x29\x7b\x76\x61\x72\x20\
+\x63\x3b\x69\x66\x28\x62\x2e\x6e\x6f\x64\x65\x54\x79\x70\x65\x3d\
+\x3d\x3d\x31\x29\x7b\x62\x2e\x63\x6c\x65\x61\x72\x41\x74\x74\x72\
+\x69\x62\x75\x74\x65\x73\x26\x26\x62\x2e\x63\x6c\x65\x61\x72\x41\
+\x74\x74\x72\x69\x62\x75\x74\x65\x73\x28\x29\x2c\x62\x2e\x6d\x65\
+\x72\x67\x65\x41\x74\x74\x72\x69\x62\x75\x74\x65\x73\x26\x26\x62\
+\x2e\x6d\x65\x72\x67\x65\x41\x74\x74\x72\x69\x62\x75\x74\x65\x73\
+\x28\x61\x29\x2c\x63\x3d\x62\x2e\x6e\x6f\x64\x65\x4e\x61\x6d\x65\
+\x2e\x74\x6f\x4c\x6f\x77\x65\x72\x43\x61\x73\x65\x28\x29\x3b\x69\
+\x66\x28\x63\x3d\x3d\x3d\x22\x6f\x62\x6a\x65\x63\x74\x22\x29\x62\
+\x2e\x6f\x75\x74\x65\x72\x48\x54\x4d\x4c\x3d\x61\x2e\x6f\x75\x74\
+\x65\x72\x48\x54\x4d\x4c\x3b\x65\x6c\x73\x65\x20\x69\x66\x28\x63\
+\x21\x3d\x3d\x22\x69\x6e\x70\x75\x74\x22\x7c\x7c\x61\x2e\x74\x79\
+\x70\x65\x21\x3d\x3d\x22\x63\x68\x65\x63\x6b\x62\x6f\x78\x22\x26\
+\x26\x61\x2e\x74\x79\x70\x65\x21\x3d\x3d\x22\x72\x61\x64\x69\x6f\
+\x22\x29\x7b\x69\x66\x28\x63\x3d\x3d\x3d\x22\x6f\x70\x74\x69\x6f\
+\x6e\x22\x29\x62\x2e\x73\x65\x6c\x65\x63\x74\x65\x64\x3d\x61\x2e\
+\x64\x65\x66\x61\x75\x6c\x74\x53\x65\x6c\x65\x63\x74\x65\x64\x3b\
+\x65\x6c\x73\x65\x20\x69\x66\x28\x63\x3d\x3d\x3d\x22\x69\x6e\x70\
+\x75\x74\x22\x7c\x7c\x63\x3d\x3d\x3d\x22\x74\x65\x78\x74\x61\x72\
+\x65\x61\x22\x29\x62\x2e\x64\x65\x66\x61\x75\x6c\x74\x56\x61\x6c\
+\x75\x65\x3d\x61\x2e\x64\x65\x66\x61\x75\x6c\x74\x56\x61\x6c\x75\
+\x65\x7d\x65\x6c\x73\x65\x20\x61\x2e\x63\x68\x65\x63\x6b\x65\x64\
+\x26\x26\x28\x62\x2e\x64\x65\x66\x61\x75\x6c\x74\x43\x68\x65\x63\
+\x6b\x65\x64\x3d\x62\x2e\x63\x68\x65\x63\x6b\x65\x64\x3d\x61\x2e\
+\x63\x68\x65\x63\x6b\x65\x64\x29\x2c\x62\x2e\x76\x61\x6c\x75\x65\
+\x21\x3d\x3d\x61\x2e\x76\x61\x6c\x75\x65\x26\x26\x28\x62\x2e\x76\
+\x61\x6c\x75\x65\x3d\x61\x2e\x76\x61\x6c\x75\x65\x29\x3b\x62\x2e\
+\x72\x65\x6d\x6f\x76\x65\x41\x74\x74\x72\x69\x62\x75\x74\x65\x28\
+\x66\x2e\x65\x78\x70\x61\x6e\x64\x6f\x29\x7d\x7d\x66\x75\x6e\x63\
+\x74\x69\x6f\x6e\x20\x62\x6a\x28\x61\x2c\x62\x29\x7b\x69\x66\x28\
+\x62\x2e\x6e\x6f\x64\x65\x54\x79\x70\x65\x3d\x3d\x3d\x31\x26\x26\
+\x21\x21\x66\x2e\x68\x61\x73\x44\x61\x74\x61\x28\x61\x29\x29\x7b\
+\x76\x61\x72\x20\x63\x2c\x64\x2c\x65\x2c\x67\x3d\x66\x2e\x5f\x64\
+\x61\x74\x61\x28\x61\x29\x2c\x68\x3d\x66\x2e\x5f\x64\x61\x74\x61\
+\x28\x62\x2c\x67\x29\x2c\x69\x3d\x67\x2e\x65\x76\x65\x6e\x74\x73\
+\x3b\x69\x66\x28\x69\x29\x7b\x64\x65\x6c\x65\x74\x65\x20\x68\x2e\
+\x68\x61\x6e\x64\x6c\x65\x2c\x68\x2e\x65\x76\x65\x6e\x74\x73\x3d\
+\x7b\x7d\x3b\x66\x6f\x72\x28\x63\x20\x69\x6e\x20\x69\x29\x66\x6f\
+\x72\x28\x64\x3d\x30\x2c\x65\x3d\x69\x5b\x63\x5d\x2e\x6c\x65\x6e\
+\x67\x74\x68\x3b\x64\x3c\x65\x3b\x64\x2b\x2b\x29\x66\x2e\x65\x76\
+\x65\x6e\x74\x2e\x61\x64\x64\x28\x62\x2c\x63\x2b\x28\x69\x5b\x63\
+\x5d\x5b\x64\x5d\x2e\x6e\x61\x6d\x65\x73\x70\x61\x63\x65\x3f\x22\
+\x2e\x22\x3a\x22\x22\x29\x2b\x69\x5b\x63\x5d\x5b\x64\x5d\x2e\x6e\
+\x61\x6d\x65\x73\x70\x61\x63\x65\x2c\x69\x5b\x63\x5d\x5b\x64\x5d\
+\x2c\x69\x5b\x63\x5d\x5b\x64\x5d\x2e\x64\x61\x74\x61\x29\x7d\x68\
+\x2e\x64\x61\x74\x61\x26\x26\x28\x68\x2e\x64\x61\x74\x61\x3d\x66\
+\x2e\x65\x78\x74\x65\x6e\x64\x28\x7b\x7d\x2c\x68\x2e\x64\x61\x74\
+\x61\x29\x29\x7d\x7d\x66\x75\x6e\x63\x74\x69\x6f\x6e\x20\x62\x69\
+\x28\x61\x2c\x62\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x66\x2e\x6e\
+\x6f\x64\x65\x4e\x61\x6d\x65\x28\x61\x2c\x22\x74\x61\x62\x6c\x65\
+\x22\x29\x3f\x61\x2e\x67\x65\x74\x45\x6c\x65\x6d\x65\x6e\x74\x73\
+\x42\x79\x54\x61\x67\x4e\x61\x6d\x65\x28\x22\x74\x62\x6f\x64\x79\
+\x22\x29\x5b\x30\x5d\x7c\x7c\x61\x2e\x61\x70\x70\x65\x6e\x64\x43\
+\x68\x69\x6c\x64\x28\x61\x2e\x6f\x77\x6e\x65\x72\x44\x6f\x63\x75\
+\x6d\x65\x6e\x74\x2e\x63\x72\x65\x61\x74\x65\x45\x6c\x65\x6d\x65\
+\x6e\x74\x28\x22\x74\x62\x6f\x64\x79\x22\x29\x29\x3a\x61\x7d\x66\
+\x75\x6e\x63\x74\x69\x6f\x6e\x20\x55\x28\x61\x29\x7b\x76\x61\x72\
+\x20\x62\x3d\x56\x2e\x73\x70\x6c\x69\x74\x28\x22\x7c\x22\x29\x2c\
+\x63\x3d\x61\x2e\x63\x72\x65\x61\x74\x65\x44\x6f\x63\x75\x6d\x65\
+\x6e\x74\x46\x72\x61\x67\x6d\x65\x6e\x74\x28\x29\x3b\x69\x66\x28\
+\x63\x2e\x63\x72\x65\x61\x74\x65\x45\x6c\x65\x6d\x65\x6e\x74\x29\
+\x77\x68\x69\x6c\x65\x28\x62\x2e\x6c\x65\x6e\x67\x74\x68\x29\x63\
+\x2e\x63\x72\x65\x61\x74\x65\x45\x6c\x65\x6d\x65\x6e\x74\x28\x62\
+\x2e\x70\x6f\x70\x28\x29\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x63\
+\x7d\x66\x75\x6e\x63\x74\x69\x6f\x6e\x20\x54\x28\x61\x2c\x62\x2c\
+\x63\x29\x7b\x62\x3d\x62\x7c\x7c\x30\x3b\x69\x66\x28\x66\x2e\x69\
+\x73\x46\x75\x6e\x63\x74\x69\x6f\x6e\x28\x62\x29\x29\x72\x65\x74\
+\x75\x72\x6e\x20\x66\x2e\x67\x72\x65\x70\x28\x61\x2c\x66\x75\x6e\
+\x63\x74\x69\x6f\x6e\x28\x61\x2c\x64\x29\x7b\x76\x61\x72\x20\x65\
+\x3d\x21\x21\x62\x2e\x63\x61\x6c\x6c\x28\x61\x2c\x64\x2c\x61\x29\
+\x3b\x72\x65\x74\x75\x72\x6e\x20\x65\x3d\x3d\x3d\x63\x7d\x29\x3b\
+\x69\x66\x28\x62\x2e\x6e\x6f\x64\x65\x54\x79\x70\x65\x29\x72\x65\
+\x74\x75\x72\x6e\x20\x66\x2e\x67\x72\x65\x70\x28\x61\x2c\x66\x75\
+\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x64\x29\x7b\x72\x65\x74\x75\
+\x72\x6e\x20\x61\x3d\x3d\x3d\x62\x3d\x3d\x3d\x63\x7d\x29\x3b\x69\
+\x66\x28\x74\x79\x70\x65\x6f\x66\x20\x62\x3d\x3d\x22\x73\x74\x72\
+\x69\x6e\x67\x22\x29\x7b\x76\x61\x72\x20\x64\x3d\x66\x2e\x67\x72\
+\x65\x70\x28\x61\x2c\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\
+\x7b\x72\x65\x74\x75\x72\x6e\x20\x61\x2e\x6e\x6f\x64\x65\x54\x79\
+\x70\x65\x3d\x3d\x3d\x31\x7d\x29\x3b\x69\x66\x28\x4f\x2e\x74\x65\
+\x73\x74\x28\x62\x29\x29\x72\x65\x74\x75\x72\x6e\x20\x66\x2e\x66\
+\x69\x6c\x74\x65\x72\x28\x62\x2c\x64\x2c\x21\x63\x29\x3b\x62\x3d\
+\x66\x2e\x66\x69\x6c\x74\x65\x72\x28\x62\x2c\x64\x29\x7d\x72\x65\
+\x74\x75\x72\x6e\x20\x66\x2e\x67\x72\x65\x70\x28\x61\x2c\x66\x75\
+\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x64\x29\x7b\x72\x65\x74\x75\
+\x72\x6e\x20\x66\x2e\x69\x6e\x41\x72\x72\x61\x79\x28\x61\x2c\x62\
+\x29\x3e\x3d\x30\x3d\x3d\x3d\x63\x7d\x29\x7d\x66\x75\x6e\x63\x74\
+\x69\x6f\x6e\x20\x53\x28\x61\x29\x7b\x72\x65\x74\x75\x72\x6e\x21\
+\x61\x7c\x7c\x21\x61\x2e\x70\x61\x72\x65\x6e\x74\x4e\x6f\x64\x65\
+\x7c\x7c\x61\x2e\x70\x61\x72\x65\x6e\x74\x4e\x6f\x64\x65\x2e\x6e\
+\x6f\x64\x65\x54\x79\x70\x65\x3d\x3d\x3d\x31\x31\x7d\x66\x75\x6e\
+\x63\x74\x69\x6f\x6e\x20\x4b\x28\x29\x7b\x72\x65\x74\x75\x72\x6e\
+\x21\x30\x7d\x66\x75\x6e\x63\x74\x69\x6f\x6e\x20\x4a\x28\x29\x7b\
+\x72\x65\x74\x75\x72\x6e\x21\x31\x7d\x66\x75\x6e\x63\x74\x69\x6f\
+\x6e\x20\x6e\x28\x61\x2c\x62\x2c\x63\x29\x7b\x76\x61\x72\x20\x64\
+\x3d\x62\x2b\x22\x64\x65\x66\x65\x72\x22\x2c\x65\x3d\x62\x2b\x22\
+\x71\x75\x65\x75\x65\x22\x2c\x67\x3d\x62\x2b\x22\x6d\x61\x72\x6b\
+\x22\x2c\x68\x3d\x66\x2e\x5f\x64\x61\x74\x61\x28\x61\x2c\x64\x29\
+\x3b\x68\x26\x26\x28\x63\x3d\x3d\x3d\x22\x71\x75\x65\x75\x65\x22\
+\x7c\x7c\x21\x66\x2e\x5f\x64\x61\x74\x61\x28\x61\x2c\x65\x29\x29\
+\x26\x26\x28\x63\x3d\x3d\x3d\x22\x6d\x61\x72\x6b\x22\x7c\x7c\x21\
+\x66\x2e\x5f\x64\x61\x74\x61\x28\x61\x2c\x67\x29\x29\x26\x26\x73\
+\x65\x74\x54\x69\x6d\x65\x6f\x75\x74\x28\x66\x75\x6e\x63\x74\x69\
+\x6f\x6e\x28\x29\x7b\x21\x66\x2e\x5f\x64\x61\x74\x61\x28\x61\x2c\
+\x65\x29\x26\x26\x21\x66\x2e\x5f\x64\x61\x74\x61\x28\x61\x2c\x67\
+\x29\x26\x26\x28\x66\x2e\x72\x65\x6d\x6f\x76\x65\x44\x61\x74\x61\
+\x28\x61\x2c\x64\x2c\x21\x30\x29\x2c\x68\x2e\x66\x69\x72\x65\x28\
+\x29\x29\x7d\x2c\x30\x29\x7d\x66\x75\x6e\x63\x74\x69\x6f\x6e\x20\
+\x6d\x28\x61\x29\x7b\x66\x6f\x72\x28\x76\x61\x72\x20\x62\x20\x69\
+\x6e\x20\x61\x29\x7b\x69\x66\x28\x62\x3d\x3d\x3d\x22\x64\x61\x74\
+\x61\x22\x26\x26\x66\x2e\x69\x73\x45\x6d\x70\x74\x79\x4f\x62\x6a\
+\x65\x63\x74\x28\x61\x5b\x62\x5d\x29\x29\x63\x6f\x6e\x74\x69\x6e\
+\x75\x65\x3b\x69\x66\x28\x62\x21\x3d\x3d\x22\x74\x6f\x4a\x53\x4f\
+\x4e\x22\x29\x72\x65\x74\x75\x72\x6e\x21\x31\x7d\x72\x65\x74\x75\
+\x72\x6e\x21\x30\x7d\x66\x75\x6e\x63\x74\x69\x6f\x6e\x20\x6c\x28\
+\x61\x2c\x63\x2c\x64\x29\x7b\x69\x66\x28\x64\x3d\x3d\x3d\x62\x26\
+\x26\x61\x2e\x6e\x6f\x64\x65\x54\x79\x70\x65\x3d\x3d\x3d\x31\x29\
+\x7b\x76\x61\x72\x20\x65\x3d\x22\x64\x61\x74\x61\x2d\x22\x2b\x63\
+\x2e\x72\x65\x70\x6c\x61\x63\x65\x28\x6b\x2c\x22\x2d\x24\x31\x22\
+\x29\x2e\x74\x6f\x4c\x6f\x77\x65\x72\x43\x61\x73\x65\x28\x29\x3b\
+\x64\x3d\x61\x2e\x67\x65\x74\x41\x74\x74\x72\x69\x62\x75\x74\x65\
+\x28\x65\x29\x3b\x69\x66\x28\x74\x79\x70\x65\x6f\x66\x20\x64\x3d\
+\x3d\x22\x73\x74\x72\x69\x6e\x67\x22\x29\x7b\x74\x72\x79\x7b\x64\
+\x3d\x64\x3d\x3d\x3d\x22\x74\x72\x75\x65\x22\x3f\x21\x30\x3a\x64\
+\x3d\x3d\x3d\x22\x66\x61\x6c\x73\x65\x22\x3f\x21\x31\x3a\x64\x3d\
+\x3d\x3d\x22\x6e\x75\x6c\x6c\x22\x3f\x6e\x75\x6c\x6c\x3a\x66\x2e\
+\x69\x73\x4e\x75\x6d\x65\x72\x69\x63\x28\x64\x29\x3f\x70\x61\x72\
+\x73\x65\x46\x6c\x6f\x61\x74\x28\x64\x29\x3a\x6a\x2e\x74\x65\x73\
+\x74\x28\x64\x29\x3f\x66\x2e\x70\x61\x72\x73\x65\x4a\x53\x4f\x4e\
+\x28\x64\x29\x3a\x64\x7d\x63\x61\x74\x63\x68\x28\x67\x29\x7b\x7d\
+\x66\x2e\x64\x61\x74\x61\x28\x61\x2c\x63\x2c\x64\x29\x7d\x65\x6c\
+\x73\x65\x20\x64\x3d\x62\x7d\x72\x65\x74\x75\x72\x6e\x20\x64\x7d\
+\x66\x75\x6e\x63\x74\x69\x6f\x6e\x20\x68\x28\x61\x29\x7b\x76\x61\
+\x72\x20\x62\x3d\x67\x5b\x61\x5d\x3d\x7b\x7d\x2c\x63\x2c\x64\x3b\
+\x61\x3d\x61\x2e\x73\x70\x6c\x69\x74\x28\x2f\x5c\x73\x2b\x2f\x29\
+\x3b\x66\x6f\x72\x28\x63\x3d\x30\x2c\x64\x3d\x61\x2e\x6c\x65\x6e\
+\x67\x74\x68\x3b\x63\x3c\x64\x3b\x63\x2b\x2b\x29\x62\x5b\x61\x5b\
+\x63\x5d\x5d\x3d\x21\x30\x3b\x72\x65\x74\x75\x72\x6e\x20\x62\x7d\
+\x76\x61\x72\x20\x63\x3d\x61\x2e\x64\x6f\x63\x75\x6d\x65\x6e\x74\
+\x2c\x64\x3d\x61\x2e\x6e\x61\x76\x69\x67\x61\x74\x6f\x72\x2c\x65\
+\x3d\x61\x2e\x6c\x6f\x63\x61\x74\x69\x6f\x6e\x2c\x66\x3d\x66\x75\
+\x6e\x63\x74\x69\x6f\x6e\x28\x29\x7b\x66\x75\x6e\x63\x74\x69\x6f\
+\x6e\x20\x4a\x28\x29\x7b\x69\x66\x28\x21\x65\x2e\x69\x73\x52\x65\
+\x61\x64\x79\x29\x7b\x74\x72\x79\x7b\x63\x2e\x64\x6f\x63\x75\x6d\
+\x65\x6e\x74\x45\x6c\x65\x6d\x65\x6e\x74\x2e\x64\x6f\x53\x63\x72\
+\x6f\x6c\x6c\x28\x22\x6c\x65\x66\x74\x22\x29\x7d\x63\x61\x74\x63\
+\x68\x28\x61\x29\x7b\x73\x65\x74\x54\x69\x6d\x65\x6f\x75\x74\x28\
+\x4a\x2c\x31\x29\x3b\x72\x65\x74\x75\x72\x6e\x7d\x65\x2e\x72\x65\
+\x61\x64\x79\x28\x29\x7d\x7d\x76\x61\x72\x20\x65\x3d\x66\x75\x6e\
+\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x29\x7b\x72\x65\x74\x75\x72\
+\x6e\x20\x6e\x65\x77\x20\x65\x2e\x66\x6e\x2e\x69\x6e\x69\x74\x28\
+\x61\x2c\x62\x2c\x68\x29\x7d\x2c\x66\x3d\x61\x2e\x6a\x51\x75\x65\
+\x72\x79\x2c\x67\x3d\x61\x2e\x24\x2c\x68\x2c\x69\x3d\x2f\x5e\x28\
+\x3f\x3a\x5b\x5e\x23\x3c\x5d\x2a\x28\x3c\x5b\x5c\x77\x5c\x57\x5d\
+\x2b\x3e\x29\x5b\x5e\x3e\x5d\x2a\x24\x7c\x23\x28\x5b\x5c\x77\x5c\
+\x2d\x5d\x2a\x29\x24\x29\x2f\x2c\x6a\x3d\x2f\x5c\x53\x2f\x2c\x6b\
+\x3d\x2f\x5e\x5c\x73\x2b\x2f\x2c\x6c\x3d\x2f\x5c\x73\x2b\x24\x2f\
+\x2c\x6d\x3d\x2f\x5e\x3c\x28\x5c\x77\x2b\x29\x5c\x73\x2a\x5c\x2f\
+\x3f\x3e\x28\x3f\x3a\x3c\x5c\x2f\x5c\x31\x3e\x29\x3f\x24\x2f\x2c\
+\x6e\x3d\x2f\x5e\x5b\x5c\x5d\x2c\x3a\x7b\x7d\x5c\x73\x5d\x2a\x24\
+\x2f\x2c\x6f\x3d\x2f\x5c\x5c\x28\x3f\x3a\x5b\x22\x5c\x5c\x5c\x2f\
+\x62\x66\x6e\x72\x74\x5d\x7c\x75\x5b\x30\x2d\x39\x61\x2d\x66\x41\
+\x2d\x46\x5d\x7b\x34\x7d\x29\x2f\x67\x2c\x70\x3d\x2f\x22\x5b\x5e\
+\x22\x5c\x5c\x5c\x6e\x5c\x72\x5d\x2a\x22\x7c\x74\x72\x75\x65\x7c\
+\x66\x61\x6c\x73\x65\x7c\x6e\x75\x6c\x6c\x7c\x2d\x3f\x5c\x64\x2b\
+\x28\x3f\x3a\x5c\x2e\x5c\x64\x2a\x29\x3f\x28\x3f\x3a\x5b\x65\x45\
+\x5d\x5b\x2b\x5c\x2d\x5d\x3f\x5c\x64\x2b\x29\x3f\x2f\x67\x2c\x71\
+\x3d\x2f\x28\x3f\x3a\x5e\x7c\x3a\x7c\x2c\x29\x28\x3f\x3a\x5c\x73\
+\x2a\x5c\x5b\x29\x2b\x2f\x67\x2c\x72\x3d\x2f\x28\x77\x65\x62\x6b\
+\x69\x74\x29\x5b\x20\x5c\x2f\x5d\x28\x5b\x5c\x77\x2e\x5d\x2b\x29\
+\x2f\x2c\x73\x3d\x2f\x28\x6f\x70\x65\x72\x61\x29\x28\x3f\x3a\x2e\
+\x2a\x76\x65\x72\x73\x69\x6f\x6e\x29\x3f\x5b\x20\x5c\x2f\x5d\x28\
+\x5b\x5c\x77\x2e\x5d\x2b\x29\x2f\x2c\x74\x3d\x2f\x28\x6d\x73\x69\
+\x65\x29\x20\x28\x5b\x5c\x77\x2e\x5d\x2b\x29\x2f\x2c\x75\x3d\x2f\
+\x28\x6d\x6f\x7a\x69\x6c\x6c\x61\x29\x28\x3f\x3a\x2e\x2a\x3f\x20\
+\x72\x76\x3a\x28\x5b\x5c\x77\x2e\x5d\x2b\x29\x29\x3f\x2f\x2c\x76\
+\x3d\x2f\x2d\x28\x5b\x61\x2d\x7a\x5d\x7c\x5b\x30\x2d\x39\x5d\x29\
+\x2f\x69\x67\x2c\x77\x3d\x2f\x5e\x2d\x6d\x73\x2d\x2f\x2c\x78\x3d\
+\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x29\x7b\x72\x65\
+\x74\x75\x72\x6e\x28\x62\x2b\x22\x22\x29\x2e\x74\x6f\x55\x70\x70\
+\x65\x72\x43\x61\x73\x65\x28\x29\x7d\x2c\x79\x3d\x64\x2e\x75\x73\
+\x65\x72\x41\x67\x65\x6e\x74\x2c\x7a\x2c\x41\x2c\x42\x2c\x43\x3d\
+\x4f\x62\x6a\x65\x63\x74\x2e\x70\x72\x6f\x74\x6f\x74\x79\x70\x65\
+\x2e\x74\x6f\x53\x74\x72\x69\x6e\x67\x2c\x44\x3d\x4f\x62\x6a\x65\
+\x63\x74\x2e\x70\x72\x6f\x74\x6f\x74\x79\x70\x65\x2e\x68\x61\x73\
+\x4f\x77\x6e\x50\x72\x6f\x70\x65\x72\x74\x79\x2c\x45\x3d\x41\x72\
+\x72\x61\x79\x2e\x70\x72\x6f\x74\x6f\x74\x79\x70\x65\x2e\x70\x75\
+\x73\x68\x2c\x46\x3d\x41\x72\x72\x61\x79\x2e\x70\x72\x6f\x74\x6f\
+\x74\x79\x70\x65\x2e\x73\x6c\x69\x63\x65\x2c\x47\x3d\x53\x74\x72\
+\x69\x6e\x67\x2e\x70\x72\x6f\x74\x6f\x74\x79\x70\x65\x2e\x74\x72\
+\x69\x6d\x2c\x48\x3d\x41\x72\x72\x61\x79\x2e\x70\x72\x6f\x74\x6f\
+\x74\x79\x70\x65\x2e\x69\x6e\x64\x65\x78\x4f\x66\x2c\x49\x3d\x7b\
+\x7d\x3b\x65\x2e\x66\x6e\x3d\x65\x2e\x70\x72\x6f\x74\x6f\x74\x79\
+\x70\x65\x3d\x7b\x63\x6f\x6e\x73\x74\x72\x75\x63\x74\x6f\x72\x3a\
+\x65\x2c\x69\x6e\x69\x74\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\
+\x61\x2c\x64\x2c\x66\x29\x7b\x76\x61\x72\x20\x67\x2c\x68\x2c\x6a\
+\x2c\x6b\x3b\x69\x66\x28\x21\x61\x29\x72\x65\x74\x75\x72\x6e\x20\
+\x74\x68\x69\x73\x3b\x69\x66\x28\x61\x2e\x6e\x6f\x64\x65\x54\x79\
+\x70\x65\x29\x7b\x74\x68\x69\x73\x2e\x63\x6f\x6e\x74\x65\x78\x74\
+\x3d\x74\x68\x69\x73\x5b\x30\x5d\x3d\x61\x2c\x74\x68\x69\x73\x2e\
+\x6c\x65\x6e\x67\x74\x68\x3d\x31\x3b\x72\x65\x74\x75\x72\x6e\x20\
+\x74\x68\x69\x73\x7d\x69\x66\x28\x61\x3d\x3d\x3d\x22\x62\x6f\x64\
+\x79\x22\x26\x26\x21\x64\x26\x26\x63\x2e\x62\x6f\x64\x79\x29\x7b\
+\x74\x68\x69\x73\x2e\x63\x6f\x6e\x74\x65\x78\x74\x3d\x63\x2c\x74\
+\x68\x69\x73\x5b\x30\x5d\x3d\x63\x2e\x62\x6f\x64\x79\x2c\x74\x68\
+\x69\x73\x2e\x73\x65\x6c\x65\x63\x74\x6f\x72\x3d\x61\x2c\x74\x68\
+\x69\x73\x2e\x6c\x65\x6e\x67\x74\x68\x3d\x31\x3b\x72\x65\x74\x75\
+\x72\x6e\x20\x74\x68\x69\x73\x7d\x69\x66\x28\x74\x79\x70\x65\x6f\
+\x66\x20\x61\x3d\x3d\x22\x73\x74\x72\x69\x6e\x67\x22\x29\x7b\x61\
+\x2e\x63\x68\x61\x72\x41\x74\x28\x30\x29\x21\x3d\x3d\x22\x3c\x22\
+\x7c\x7c\x61\x2e\x63\x68\x61\x72\x41\x74\x28\x61\x2e\x6c\x65\x6e\
+\x67\x74\x68\x2d\x31\x29\x21\x3d\x3d\x22\x3e\x22\x7c\x7c\x61\x2e\
+\x6c\x65\x6e\x67\x74\x68\x3c\x33\x3f\x67\x3d\x69\x2e\x65\x78\x65\
+\x63\x28\x61\x29\x3a\x67\x3d\x5b\x6e\x75\x6c\x6c\x2c\x61\x2c\x6e\
+\x75\x6c\x6c\x5d\x3b\x69\x66\x28\x67\x26\x26\x28\x67\x5b\x31\x5d\
+\x7c\x7c\x21\x64\x29\x29\x7b\x69\x66\x28\x67\x5b\x31\x5d\x29\x7b\
+\x64\x3d\x64\x20\x69\x6e\x73\x74\x61\x6e\x63\x65\x6f\x66\x20\x65\
+\x3f\x64\x5b\x30\x5d\x3a\x64\x2c\x6b\x3d\x64\x3f\x64\x2e\x6f\x77\
+\x6e\x65\x72\x44\x6f\x63\x75\x6d\x65\x6e\x74\x7c\x7c\x64\x3a\x63\
+\x2c\x6a\x3d\x6d\x2e\x65\x78\x65\x63\x28\x61\x29\x2c\x6a\x3f\x65\
+\x2e\x69\x73\x50\x6c\x61\x69\x6e\x4f\x62\x6a\x65\x63\x74\x28\x64\
+\x29\x3f\x28\x61\x3d\x5b\x63\x2e\x63\x72\x65\x61\x74\x65\x45\x6c\
+\x65\x6d\x65\x6e\x74\x28\x6a\x5b\x31\x5d\x29\x5d\x2c\x65\x2e\x66\
+\x6e\x2e\x61\x74\x74\x72\x2e\x63\x61\x6c\x6c\x28\x61\x2c\x64\x2c\
+\x21\x30\x29\x29\x3a\x61\x3d\x5b\x6b\x2e\x63\x72\x65\x61\x74\x65\
+\x45\x6c\x65\x6d\x65\x6e\x74\x28\x6a\x5b\x31\x5d\x29\x5d\x3a\x28\
+\x6a\x3d\x65\x2e\x62\x75\x69\x6c\x64\x46\x72\x61\x67\x6d\x65\x6e\
+\x74\x28\x5b\x67\x5b\x31\x5d\x5d\x2c\x5b\x6b\x5d\x29\x2c\x61\x3d\
+\x28\x6a\x2e\x63\x61\x63\x68\x65\x61\x62\x6c\x65\x3f\x65\x2e\x63\
+\x6c\x6f\x6e\x65\x28\x6a\x2e\x66\x72\x61\x67\x6d\x65\x6e\x74\x29\
+\x3a\x6a\x2e\x66\x72\x61\x67\x6d\x65\x6e\x74\x29\x2e\x63\x68\x69\
+\x6c\x64\x4e\x6f\x64\x65\x73\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\
+\x65\x2e\x6d\x65\x72\x67\x65\x28\x74\x68\x69\x73\x2c\x61\x29\x7d\
+\x68\x3d\x63\x2e\x67\x65\x74\x45\x6c\x65\x6d\x65\x6e\x74\x42\x79\
+\x49\x64\x28\x67\x5b\x32\x5d\x29\x3b\x69\x66\x28\x68\x26\x26\x68\
+\x2e\x70\x61\x72\x65\x6e\x74\x4e\x6f\x64\x65\x29\x7b\x69\x66\x28\
+\x68\x2e\x69\x64\x21\x3d\x3d\x67\x5b\x32\x5d\x29\x72\x65\x74\x75\
+\x72\x6e\x20\x66\x2e\x66\x69\x6e\x64\x28\x61\x29\x3b\x74\x68\x69\
+\x73\x2e\x6c\x65\x6e\x67\x74\x68\x3d\x31\x2c\x74\x68\x69\x73\x5b\
+\x30\x5d\x3d\x68\x7d\x74\x68\x69\x73\x2e\x63\x6f\x6e\x74\x65\x78\
+\x74\x3d\x63\x2c\x74\x68\x69\x73\x2e\x73\x65\x6c\x65\x63\x74\x6f\
+\x72\x3d\x61\x3b\x72\x65\x74\x75\x72\x6e\x20\x74\x68\x69\x73\x7d\
+\x72\x65\x74\x75\x72\x6e\x21\x64\x7c\x7c\x64\x2e\x6a\x71\x75\x65\
+\x72\x79\x3f\x28\x64\x7c\x7c\x66\x29\x2e\x66\x69\x6e\x64\x28\x61\
+\x29\x3a\x74\x68\x69\x73\x2e\x63\x6f\x6e\x73\x74\x72\x75\x63\x74\
+\x6f\x72\x28\x64\x29\x2e\x66\x69\x6e\x64\x28\x61\x29\x7d\x69\x66\
+\x28\x65\x2e\x69\x73\x46\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\
+\x29\x72\x65\x74\x75\x72\x6e\x20\x66\x2e\x72\x65\x61\x64\x79\x28\
+\x61\x29\x3b\x61\x2e\x73\x65\x6c\x65\x63\x74\x6f\x72\x21\x3d\x3d\
+\x62\x26\x26\x28\x74\x68\x69\x73\x2e\x73\x65\x6c\x65\x63\x74\x6f\
+\x72\x3d\x61\x2e\x73\x65\x6c\x65\x63\x74\x6f\x72\x2c\x74\x68\x69\
+\x73\x2e\x63\x6f\x6e\x74\x65\x78\x74\x3d\x61\x2e\x63\x6f\x6e\x74\
+\x65\x78\x74\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x65\x2e\x6d\x61\
+\x6b\x65\x41\x72\x72\x61\x79\x28\x61\x2c\x74\x68\x69\x73\x29\x7d\
+\x2c\x73\x65\x6c\x65\x63\x74\x6f\x72\x3a\x22\x22\x2c\x6a\x71\x75\
+\x65\x72\x79\x3a\x22\x31\x2e\x37\x2e\x31\x22\x2c\x6c\x65\x6e\x67\
+\x74\x68\x3a\x30\x2c\x73\x69\x7a\x65\x3a\x66\x75\x6e\x63\x74\x69\
+\x6f\x6e\x28\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x74\x68\x69\x73\
+\x2e\x6c\x65\x6e\x67\x74\x68\x7d\x2c\x74\x6f\x41\x72\x72\x61\x79\
+\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x29\x7b\x72\x65\x74\x75\
+\x72\x6e\x20\x46\x2e\x63\x61\x6c\x6c\x28\x74\x68\x69\x73\x2c\x30\
+\x29\x7d\x2c\x67\x65\x74\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\
+\x61\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x61\x3d\x3d\x6e\x75\x6c\
+\x6c\x3f\x74\x68\x69\x73\x2e\x74\x6f\x41\x72\x72\x61\x79\x28\x29\
+\x3a\x61\x3c\x30\x3f\x74\x68\x69\x73\x5b\x74\x68\x69\x73\x2e\x6c\
+\x65\x6e\x67\x74\x68\x2b\x61\x5d\x3a\x74\x68\x69\x73\x5b\x61\x5d\
+\x7d\x2c\x70\x75\x73\x68\x53\x74\x61\x63\x6b\x3a\x66\x75\x6e\x63\
+\x74\x69\x6f\x6e\x28\x61\x2c\x62\x2c\x63\x29\x7b\x76\x61\x72\x20\
+\x64\x3d\x74\x68\x69\x73\x2e\x63\x6f\x6e\x73\x74\x72\x75\x63\x74\
+\x6f\x72\x28\x29\x3b\x65\x2e\x69\x73\x41\x72\x72\x61\x79\x28\x61\
+\x29\x3f\x45\x2e\x61\x70\x70\x6c\x79\x28\x64\x2c\x61\x29\x3a\x65\
+\x2e\x6d\x65\x72\x67\x65\x28\x64\x2c\x61\x29\x2c\x64\x2e\x70\x72\
+\x65\x76\x4f\x62\x6a\x65\x63\x74\x3d\x74\x68\x69\x73\x2c\x64\x2e\
+\x63\x6f\x6e\x74\x65\x78\x74\x3d\x74\x68\x69\x73\x2e\x63\x6f\x6e\
+\x74\x65\x78\x74\x2c\x62\x3d\x3d\x3d\x22\x66\x69\x6e\x64\x22\x3f\
+\x64\x2e\x73\x65\x6c\x65\x63\x74\x6f\x72\x3d\x74\x68\x69\x73\x2e\
+\x73\x65\x6c\x65\x63\x74\x6f\x72\x2b\x28\x74\x68\x69\x73\x2e\x73\
+\x65\x6c\x65\x63\x74\x6f\x72\x3f\x22\x20\x22\x3a\x22\x22\x29\x2b\
+\x63\x3a\x62\x26\x26\x28\x64\x2e\x73\x65\x6c\x65\x63\x74\x6f\x72\
+\x3d\x74\x68\x69\x73\x2e\x73\x65\x6c\x65\x63\x74\x6f\x72\x2b\x22\
+\x2e\x22\x2b\x62\x2b\x22\x28\x22\x2b\x63\x2b\x22\x29\x22\x29\x3b\
+\x72\x65\x74\x75\x72\x6e\x20\x64\x7d\x2c\x65\x61\x63\x68\x3a\x66\
+\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x29\x7b\x72\x65\x74\
+\x75\x72\x6e\x20\x65\x2e\x65\x61\x63\x68\x28\x74\x68\x69\x73\x2c\
+\x61\x2c\x62\x29\x7d\x2c\x72\x65\x61\x64\x79\x3a\x66\x75\x6e\x63\
+\x74\x69\x6f\x6e\x28\x61\x29\x7b\x65\x2e\x62\x69\x6e\x64\x52\x65\
+\x61\x64\x79\x28\x29\x2c\x41\x2e\x61\x64\x64\x28\x61\x29\x3b\x72\
+\x65\x74\x75\x72\x6e\x20\x74\x68\x69\x73\x7d\x2c\x65\x71\x3a\x66\
+\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x61\x3d\x2b\x61\x3b\
+\x72\x65\x74\x75\x72\x6e\x20\x61\x3d\x3d\x3d\x2d\x31\x3f\x74\x68\
+\x69\x73\x2e\x73\x6c\x69\x63\x65\x28\x61\x29\x3a\x74\x68\x69\x73\
+\x2e\x73\x6c\x69\x63\x65\x28\x61\x2c\x61\x2b\x31\x29\x7d\x2c\x66\
+\x69\x72\x73\x74\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x29\x7b\
+\x72\x65\x74\x75\x72\x6e\x20\x74\x68\x69\x73\x2e\x65\x71\x28\x30\
+\x29\x7d\x2c\x6c\x61\x73\x74\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\
+\x28\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x74\x68\x69\x73\x2e\x65\
+\x71\x28\x2d\x31\x29\x7d\x2c\x73\x6c\x69\x63\x65\x3a\x66\x75\x6e\
+\x63\x74\x69\x6f\x6e\x28\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x74\
+\x68\x69\x73\x2e\x70\x75\x73\x68\x53\x74\x61\x63\x6b\x28\x46\x2e\
+\x61\x70\x70\x6c\x79\x28\x74\x68\x69\x73\x2c\x61\x72\x67\x75\x6d\
+\x65\x6e\x74\x73\x29\x2c\x22\x73\x6c\x69\x63\x65\x22\x2c\x46\x2e\
+\x63\x61\x6c\x6c\x28\x61\x72\x67\x75\x6d\x65\x6e\x74\x73\x29\x2e\
+\x6a\x6f\x69\x6e\x28\x22\x2c\x22\x29\x29\x7d\x2c\x6d\x61\x70\x3a\
+\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x72\x65\x74\x75\
+\x72\x6e\x20\x74\x68\x69\x73\x2e\x70\x75\x73\x68\x53\x74\x61\x63\
+\x6b\x28\x65\x2e\x6d\x61\x70\x28\x74\x68\x69\x73\x2c\x66\x75\x6e\
+\x63\x74\x69\x6f\x6e\x28\x62\x2c\x63\x29\x7b\x72\x65\x74\x75\x72\
+\x6e\x20\x61\x2e\x63\x61\x6c\x6c\x28\x62\x2c\x63\x2c\x62\x29\x7d\
+\x29\x29\x7d\x2c\x65\x6e\x64\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\
+\x28\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x74\x68\x69\x73\x2e\x70\
+\x72\x65\x76\x4f\x62\x6a\x65\x63\x74\x7c\x7c\x74\x68\x69\x73\x2e\
+\x63\x6f\x6e\x73\x74\x72\x75\x63\x74\x6f\x72\x28\x6e\x75\x6c\x6c\
+\x29\x7d\x2c\x70\x75\x73\x68\x3a\x45\x2c\x73\x6f\x72\x74\x3a\x5b\
+\x5d\x2e\x73\x6f\x72\x74\x2c\x73\x70\x6c\x69\x63\x65\x3a\x5b\x5d\
+\x2e\x73\x70\x6c\x69\x63\x65\x7d\x2c\x65\x2e\x66\x6e\x2e\x69\x6e\
+\x69\x74\x2e\x70\x72\x6f\x74\x6f\x74\x79\x70\x65\x3d\x65\x2e\x66\
+\x6e\x2c\x65\x2e\x65\x78\x74\x65\x6e\x64\x3d\x65\x2e\x66\x6e\x2e\
+\x65\x78\x74\x65\x6e\x64\x3d\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\
+\x29\x7b\x76\x61\x72\x20\x61\x2c\x63\x2c\x64\x2c\x66\x2c\x67\x2c\
+\x68\x2c\x69\x3d\x61\x72\x67\x75\x6d\x65\x6e\x74\x73\x5b\x30\x5d\
+\x7c\x7c\x7b\x7d\x2c\x6a\x3d\x31\x2c\x6b\x3d\x61\x72\x67\x75\x6d\
+\x65\x6e\x74\x73\x2e\x6c\x65\x6e\x67\x74\x68\x2c\x6c\x3d\x21\x31\
+\x3b\x74\x79\x70\x65\x6f\x66\x20\x69\x3d\x3d\x22\x62\x6f\x6f\x6c\
+\x65\x61\x6e\x22\x26\x26\x28\x6c\x3d\x69\x2c\x69\x3d\x61\x72\x67\
+\x75\x6d\x65\x6e\x74\x73\x5b\x31\x5d\x7c\x7c\x7b\x7d\x2c\x6a\x3d\
+\x32\x29\x2c\x74\x79\x70\x65\x6f\x66\x20\x69\x21\x3d\x22\x6f\x62\
+\x6a\x65\x63\x74\x22\x26\x26\x21\x65\x2e\x69\x73\x46\x75\x6e\x63\
+\x74\x69\x6f\x6e\x28\x69\x29\x26\x26\x28\x69\x3d\x7b\x7d\x29\x2c\
+\x6b\x3d\x3d\x3d\x6a\x26\x26\x28\x69\x3d\x74\x68\x69\x73\x2c\x2d\
+\x2d\x6a\x29\x3b\x66\x6f\x72\x28\x3b\x6a\x3c\x6b\x3b\x6a\x2b\x2b\
+\x29\x69\x66\x28\x28\x61\x3d\x61\x72\x67\x75\x6d\x65\x6e\x74\x73\
+\x5b\x6a\x5d\x29\x21\x3d\x6e\x75\x6c\x6c\x29\x66\x6f\x72\x28\x63\
+\x20\x69\x6e\x20\x61\x29\x7b\x64\x3d\x69\x5b\x63\x5d\x2c\x66\x3d\
+\x61\x5b\x63\x5d\x3b\x69\x66\x28\x69\x3d\x3d\x3d\x66\x29\x63\x6f\
+\x6e\x74\x69\x6e\x75\x65\x3b\x6c\x26\x26\x66\x26\x26\x28\x65\x2e\
+\x69\x73\x50\x6c\x61\x69\x6e\x4f\x62\x6a\x65\x63\x74\x28\x66\x29\
+\x7c\x7c\x28\x67\x3d\x65\x2e\x69\x73\x41\x72\x72\x61\x79\x28\x66\
+\x29\x29\x29\x3f\x28\x67\x3f\x28\x67\x3d\x21\x31\x2c\x68\x3d\x64\
+\x26\x26\x65\x2e\x69\x73\x41\x72\x72\x61\x79\x28\x64\x29\x3f\x64\
+\x3a\x5b\x5d\x29\x3a\x68\x3d\x64\x26\x26\x65\x2e\x69\x73\x50\x6c\
+\x61\x69\x6e\x4f\x62\x6a\x65\x63\x74\x28\x64\x29\x3f\x64\x3a\x7b\
+\x7d\x2c\x69\x5b\x63\x5d\x3d\x65\x2e\x65\x78\x74\x65\x6e\x64\x28\
+\x6c\x2c\x68\x2c\x66\x29\x29\x3a\x66\x21\x3d\x3d\x62\x26\x26\x28\
+\x69\x5b\x63\x5d\x3d\x66\x29\x7d\x72\x65\x74\x75\x72\x6e\x20\x69\
+\x7d\x2c\x65\x2e\x65\x78\x74\x65\x6e\x64\x28\x7b\x6e\x6f\x43\x6f\
+\x6e\x66\x6c\x69\x63\x74\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\
+\x62\x29\x7b\x61\x2e\x24\x3d\x3d\x3d\x65\x26\x26\x28\x61\x2e\x24\
+\x3d\x67\x29\x2c\x62\x26\x26\x61\x2e\x6a\x51\x75\x65\x72\x79\x3d\
+\x3d\x3d\x65\x26\x26\x28\x61\x2e\x6a\x51\x75\x65\x72\x79\x3d\x66\
+\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x65\x7d\x2c\x69\x73\x52\x65\
+\x61\x64\x79\x3a\x21\x31\x2c\x72\x65\x61\x64\x79\x57\x61\x69\x74\
+\x3a\x31\x2c\x68\x6f\x6c\x64\x52\x65\x61\x64\x79\x3a\x66\x75\x6e\
+\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x61\x3f\x65\x2e\x72\x65\x61\
+\x64\x79\x57\x61\x69\x74\x2b\x2b\x3a\x65\x2e\x72\x65\x61\x64\x79\
+\x28\x21\x30\x29\x7d\x2c\x72\x65\x61\x64\x79\x3a\x66\x75\x6e\x63\
+\x74\x69\x6f\x6e\x28\x61\x29\x7b\x69\x66\x28\x61\x3d\x3d\x3d\x21\
+\x30\x26\x26\x21\x2d\x2d\x65\x2e\x72\x65\x61\x64\x79\x57\x61\x69\
+\x74\x7c\x7c\x61\x21\x3d\x3d\x21\x30\x26\x26\x21\x65\x2e\x69\x73\
+\x52\x65\x61\x64\x79\x29\x7b\x69\x66\x28\x21\x63\x2e\x62\x6f\x64\
+\x79\x29\x72\x65\x74\x75\x72\x6e\x20\x73\x65\x74\x54\x69\x6d\x65\
+\x6f\x75\x74\x28\x65\x2e\x72\x65\x61\x64\x79\x2c\x31\x29\x3b\x65\
+\x2e\x69\x73\x52\x65\x61\x64\x79\x3d\x21\x30\x3b\x69\x66\x28\x61\
+\x21\x3d\x3d\x21\x30\x26\x26\x2d\x2d\x65\x2e\x72\x65\x61\x64\x79\
+\x57\x61\x69\x74\x3e\x30\x29\x72\x65\x74\x75\x72\x6e\x3b\x41\x2e\
+\x66\x69\x72\x65\x57\x69\x74\x68\x28\x63\x2c\x5b\x65\x5d\x29\x2c\
+\x65\x2e\x66\x6e\x2e\x74\x72\x69\x67\x67\x65\x72\x26\x26\x65\x28\
+\x63\x29\x2e\x74\x72\x69\x67\x67\x65\x72\x28\x22\x72\x65\x61\x64\
+\x79\x22\x29\x2e\x6f\x66\x66\x28\x22\x72\x65\x61\x64\x79\x22\x29\
+\x7d\x7d\x2c\x62\x69\x6e\x64\x52\x65\x61\x64\x79\x3a\x66\x75\x6e\
+\x63\x74\x69\x6f\x6e\x28\x29\x7b\x69\x66\x28\x21\x41\x29\x7b\x41\
+\x3d\x65\x2e\x43\x61\x6c\x6c\x62\x61\x63\x6b\x73\x28\x22\x6f\x6e\
+\x63\x65\x20\x6d\x65\x6d\x6f\x72\x79\x22\x29\x3b\x69\x66\x28\x63\
+\x2e\x72\x65\x61\x64\x79\x53\x74\x61\x74\x65\x3d\x3d\x3d\x22\x63\
+\x6f\x6d\x70\x6c\x65\x74\x65\x22\x29\x72\x65\x74\x75\x72\x6e\x20\
+\x73\x65\x74\x54\x69\x6d\x65\x6f\x75\x74\x28\x65\x2e\x72\x65\x61\
+\x64\x79\x2c\x31\x29\x3b\x69\x66\x28\x63\x2e\x61\x64\x64\x45\x76\
+\x65\x6e\x74\x4c\x69\x73\x74\x65\x6e\x65\x72\x29\x63\x2e\x61\x64\
+\x64\x45\x76\x65\x6e\x74\x4c\x69\x73\x74\x65\x6e\x65\x72\x28\x22\
+\x44\x4f\x4d\x43\x6f\x6e\x74\x65\x6e\x74\x4c\x6f\x61\x64\x65\x64\
+\x22\x2c\x42\x2c\x21\x31\x29\x2c\x61\x2e\x61\x64\x64\x45\x76\x65\
+\x6e\x74\x4c\x69\x73\x74\x65\x6e\x65\x72\x28\x22\x6c\x6f\x61\x64\
+\x22\x2c\x65\x2e\x72\x65\x61\x64\x79\x2c\x21\x31\x29\x3b\x65\x6c\
+\x73\x65\x20\x69\x66\x28\x63\x2e\x61\x74\x74\x61\x63\x68\x45\x76\
+\x65\x6e\x74\x29\x7b\x63\x2e\x61\x74\x74\x61\x63\x68\x45\x76\x65\
+\x6e\x74\x28\x22\x6f\x6e\x72\x65\x61\x64\x79\x73\x74\x61\x74\x65\
+\x63\x68\x61\x6e\x67\x65\x22\x2c\x42\x29\x2c\x61\x2e\x61\x74\x74\
+\x61\x63\x68\x45\x76\x65\x6e\x74\x28\x22\x6f\x6e\x6c\x6f\x61\x64\
+\x22\x2c\x65\x2e\x72\x65\x61\x64\x79\x29\x3b\x76\x61\x72\x20\x62\
+\x3d\x21\x31\x3b\x74\x72\x79\x7b\x62\x3d\x61\x2e\x66\x72\x61\x6d\
+\x65\x45\x6c\x65\x6d\x65\x6e\x74\x3d\x3d\x6e\x75\x6c\x6c\x7d\x63\
+\x61\x74\x63\x68\x28\x64\x29\x7b\x7d\x63\x2e\x64\x6f\x63\x75\x6d\
+\x65\x6e\x74\x45\x6c\x65\x6d\x65\x6e\x74\x2e\x64\x6f\x53\x63\x72\
+\x6f\x6c\x6c\x26\x26\x62\x26\x26\x4a\x28\x29\x7d\x7d\x7d\x2c\x69\
+\x73\x46\x75\x6e\x63\x74\x69\x6f\x6e\x3a\x66\x75\x6e\x63\x74\x69\
+\x6f\x6e\x28\x61\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x65\x2e\x74\
+\x79\x70\x65\x28\x61\x29\x3d\x3d\x3d\x22\x66\x75\x6e\x63\x74\x69\
+\x6f\x6e\x22\x7d\x2c\x69\x73\x41\x72\x72\x61\x79\x3a\x41\x72\x72\
+\x61\x79\x2e\x69\x73\x41\x72\x72\x61\x79\x7c\x7c\x66\x75\x6e\x63\
+\x74\x69\x6f\x6e\x28\x61\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x65\
+\x2e\x74\x79\x70\x65\x28\x61\x29\x3d\x3d\x3d\x22\x61\x72\x72\x61\
+\x79\x22\x7d\x2c\x69\x73\x57\x69\x6e\x64\x6f\x77\x3a\x66\x75\x6e\
+\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\
+\x61\x26\x26\x74\x79\x70\x65\x6f\x66\x20\x61\x3d\x3d\x22\x6f\x62\
+\x6a\x65\x63\x74\x22\x26\x26\x22\x73\x65\x74\x49\x6e\x74\x65\x72\
+\x76\x61\x6c\x22\x69\x6e\x20\x61\x7d\x2c\x69\x73\x4e\x75\x6d\x65\
+\x72\x69\x63\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\
+\x72\x65\x74\x75\x72\x6e\x21\x69\x73\x4e\x61\x4e\x28\x70\x61\x72\
+\x73\x65\x46\x6c\x6f\x61\x74\x28\x61\x29\x29\x26\x26\x69\x73\x46\
+\x69\x6e\x69\x74\x65\x28\x61\x29\x7d\x2c\x74\x79\x70\x65\x3a\x66\
+\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x72\x65\x74\x75\x72\
+\x6e\x20\x61\x3d\x3d\x6e\x75\x6c\x6c\x3f\x53\x74\x72\x69\x6e\x67\
+\x28\x61\x29\x3a\x49\x5b\x43\x2e\x63\x61\x6c\x6c\x28\x61\x29\x5d\
+\x7c\x7c\x22\x6f\x62\x6a\x65\x63\x74\x22\x7d\x2c\x69\x73\x50\x6c\
+\x61\x69\x6e\x4f\x62\x6a\x65\x63\x74\x3a\x66\x75\x6e\x63\x74\x69\
+\x6f\x6e\x28\x61\x29\x7b\x69\x66\x28\x21\x61\x7c\x7c\x65\x2e\x74\
+\x79\x70\x65\x28\x61\x29\x21\x3d\x3d\x22\x6f\x62\x6a\x65\x63\x74\
+\x22\x7c\x7c\x61\x2e\x6e\x6f\x64\x65\x54\x79\x70\x65\x7c\x7c\x65\
+\x2e\x69\x73\x57\x69\x6e\x64\x6f\x77\x28\x61\x29\x29\x72\x65\x74\
+\x75\x72\x6e\x21\x31\x3b\x74\x72\x79\x7b\x69\x66\x28\x61\x2e\x63\
+\x6f\x6e\x73\x74\x72\x75\x63\x74\x6f\x72\x26\x26\x21\x44\x2e\x63\
+\x61\x6c\x6c\x28\x61\x2c\x22\x63\x6f\x6e\x73\x74\x72\x75\x63\x74\
+\x6f\x72\x22\x29\x26\x26\x21\x44\x2e\x63\x61\x6c\x6c\x28\x61\x2e\
+\x63\x6f\x6e\x73\x74\x72\x75\x63\x74\x6f\x72\x2e\x70\x72\x6f\x74\
+\x6f\x74\x79\x70\x65\x2c\x22\x69\x73\x50\x72\x6f\x74\x6f\x74\x79\
+\x70\x65\x4f\x66\x22\x29\x29\x72\x65\x74\x75\x72\x6e\x21\x31\x7d\
+\x63\x61\x74\x63\x68\x28\x63\x29\x7b\x72\x65\x74\x75\x72\x6e\x21\
+\x31\x7d\x76\x61\x72\x20\x64\x3b\x66\x6f\x72\x28\x64\x20\x69\x6e\
+\x20\x61\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x64\x3d\x3d\x3d\x62\
+\x7c\x7c\x44\x2e\x63\x61\x6c\x6c\x28\x61\x2c\x64\x29\x7d\x2c\x69\
+\x73\x45\x6d\x70\x74\x79\x4f\x62\x6a\x65\x63\x74\x3a\x66\x75\x6e\
+\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x66\x6f\x72\x28\x76\x61\x72\
+\x20\x62\x20\x69\x6e\x20\x61\x29\x72\x65\x74\x75\x72\x6e\x21\x31\
+\x3b\x72\x65\x74\x75\x72\x6e\x21\x30\x7d\x2c\x65\x72\x72\x6f\x72\
+\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x74\x68\x72\
+\x6f\x77\x20\x6e\x65\x77\x20\x45\x72\x72\x6f\x72\x28\x61\x29\x7d\
+\x2c\x70\x61\x72\x73\x65\x4a\x53\x4f\x4e\x3a\x66\x75\x6e\x63\x74\
+\x69\x6f\x6e\x28\x62\x29\x7b\x69\x66\x28\x74\x79\x70\x65\x6f\x66\
+\x20\x62\x21\x3d\x22\x73\x74\x72\x69\x6e\x67\x22\x7c\x7c\x21\x62\
+\x29\x72\x65\x74\x75\x72\x6e\x20\x6e\x75\x6c\x6c\x3b\x62\x3d\x65\
+\x2e\x74\x72\x69\x6d\x28\x62\x29\x3b\x69\x66\x28\x61\x2e\x4a\x53\
+\x4f\x4e\x26\x26\x61\x2e\x4a\x53\x4f\x4e\x2e\x70\x61\x72\x73\x65\
+\x29\x72\x65\x74\x75\x72\x6e\x20\x61\x2e\x4a\x53\x4f\x4e\x2e\x70\
+\x61\x72\x73\x65\x28\x62\x29\x3b\x69\x66\x28\x6e\x2e\x74\x65\x73\
+\x74\x28\x62\x2e\x72\x65\x70\x6c\x61\x63\x65\x28\x6f\x2c\x22\x40\
+\x22\x29\x2e\x72\x65\x70\x6c\x61\x63\x65\x28\x70\x2c\x22\x5d\x22\
+\x29\x2e\x72\x65\x70\x6c\x61\x63\x65\x28\x71\x2c\x22\x22\x29\x29\
+\x29\x72\x65\x74\x75\x72\x6e\x28\x6e\x65\x77\x20\x46\x75\x6e\x63\
+\x74\x69\x6f\x6e\x28\x22\x72\x65\x74\x75\x72\x6e\x20\x22\x2b\x62\
+\x29\x29\x28\x29\x3b\x65\x2e\x65\x72\x72\x6f\x72\x28\x22\x49\x6e\
+\x76\x61\x6c\x69\x64\x20\x4a\x53\x4f\x4e\x3a\x20\x22\x2b\x62\x29\
+\x7d\x2c\x70\x61\x72\x73\x65\x58\x4d\x4c\x3a\x66\x75\x6e\x63\x74\
+\x69\x6f\x6e\x28\x63\x29\x7b\x76\x61\x72\x20\x64\x2c\x66\x3b\x74\
+\x72\x79\x7b\x61\x2e\x44\x4f\x4d\x50\x61\x72\x73\x65\x72\x3f\x28\
+\x66\x3d\x6e\x65\x77\x20\x44\x4f\x4d\x50\x61\x72\x73\x65\x72\x2c\
+\x64\x3d\x66\x2e\x70\x61\x72\x73\x65\x46\x72\x6f\x6d\x53\x74\x72\
+\x69\x6e\x67\x28\x63\x2c\x22\x74\x65\x78\x74\x2f\x78\x6d\x6c\x22\
+\x29\x29\x3a\x28\x64\x3d\x6e\x65\x77\x20\x41\x63\x74\x69\x76\x65\
+\x58\x4f\x62\x6a\x65\x63\x74\x28\x22\x4d\x69\x63\x72\x6f\x73\x6f\
+\x66\x74\x2e\x58\x4d\x4c\x44\x4f\x4d\x22\x29\x2c\x64\x2e\x61\x73\
+\x79\x6e\x63\x3d\x22\x66\x61\x6c\x73\x65\x22\x2c\x64\x2e\x6c\x6f\
+\x61\x64\x58\x4d\x4c\x28\x63\x29\x29\x7d\x63\x61\x74\x63\x68\x28\
+\x67\x29\x7b\x64\x3d\x62\x7d\x28\x21\x64\x7c\x7c\x21\x64\x2e\x64\
+\x6f\x63\x75\x6d\x65\x6e\x74\x45\x6c\x65\x6d\x65\x6e\x74\x7c\x7c\
+\x64\x2e\x67\x65\x74\x45\x6c\x65\x6d\x65\x6e\x74\x73\x42\x79\x54\
+\x61\x67\x4e\x61\x6d\x65\x28\x22\x70\x61\x72\x73\x65\x72\x65\x72\
+\x72\x6f\x72\x22\x29\x2e\x6c\x65\x6e\x67\x74\x68\x29\x26\x26\x65\
+\x2e\x65\x72\x72\x6f\x72\x28\x22\x49\x6e\x76\x61\x6c\x69\x64\x20\
+\x58\x4d\x4c\x3a\x20\x22\x2b\x63\x29\x3b\x72\x65\x74\x75\x72\x6e\
+\x20\x64\x7d\x2c\x6e\x6f\x6f\x70\x3a\x66\x75\x6e\x63\x74\x69\x6f\
+\x6e\x28\x29\x7b\x7d\x2c\x67\x6c\x6f\x62\x61\x6c\x45\x76\x61\x6c\
+\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x62\x29\x7b\x62\x26\x26\
+\x6a\x2e\x74\x65\x73\x74\x28\x62\x29\x26\x26\x28\x61\x2e\x65\x78\
+\x65\x63\x53\x63\x72\x69\x70\x74\x7c\x7c\x66\x75\x6e\x63\x74\x69\
+\x6f\x6e\x28\x62\x29\x7b\x61\x2e\x65\x76\x61\x6c\x2e\x63\x61\x6c\
+\x6c\x28\x61\x2c\x62\x29\x7d\x29\x28\x62\x29\x7d\x2c\x63\x61\x6d\
+\x65\x6c\x43\x61\x73\x65\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\
+\x61\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x61\x2e\x72\x65\x70\x6c\
+\x61\x63\x65\x28\x77\x2c\x22\x6d\x73\x2d\x22\x29\x2e\x72\x65\x70\
+\x6c\x61\x63\x65\x28\x76\x2c\x78\x29\x7d\x2c\x6e\x6f\x64\x65\x4e\
+\x61\x6d\x65\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\
+\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x61\x2e\x6e\x6f\x64\x65\x4e\
+\x61\x6d\x65\x26\x26\x61\x2e\x6e\x6f\x64\x65\x4e\x61\x6d\x65\x2e\
+\x74\x6f\x55\x70\x70\x65\x72\x43\x61\x73\x65\x28\x29\x3d\x3d\x3d\
+\x62\x2e\x74\x6f\x55\x70\x70\x65\x72\x43\x61\x73\x65\x28\x29\x7d\
+\x2c\x65\x61\x63\x68\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\
+\x2c\x63\x2c\x64\x29\x7b\x76\x61\x72\x20\x66\x2c\x67\x3d\x30\x2c\
+\x68\x3d\x61\x2e\x6c\x65\x6e\x67\x74\x68\x2c\x69\x3d\x68\x3d\x3d\
+\x3d\x62\x7c\x7c\x65\x2e\x69\x73\x46\x75\x6e\x63\x74\x69\x6f\x6e\
+\x28\x61\x29\x3b\x69\x66\x28\x64\x29\x7b\x69\x66\x28\x69\x29\x7b\
+\x66\x6f\x72\x28\x66\x20\x69\x6e\x20\x61\x29\x69\x66\x28\x63\x2e\
+\x61\x70\x70\x6c\x79\x28\x61\x5b\x66\x5d\x2c\x64\x29\x3d\x3d\x3d\
+\x21\x31\x29\x62\x72\x65\x61\x6b\x7d\x65\x6c\x73\x65\x20\x66\x6f\
+\x72\x28\x3b\x67\x3c\x68\x3b\x29\x69\x66\x28\x63\x2e\x61\x70\x70\
+\x6c\x79\x28\x61\x5b\x67\x2b\x2b\x5d\x2c\x64\x29\x3d\x3d\x3d\x21\
+\x31\x29\x62\x72\x65\x61\x6b\x7d\x65\x6c\x73\x65\x20\x69\x66\x28\
+\x69\x29\x7b\x66\x6f\x72\x28\x66\x20\x69\x6e\x20\x61\x29\x69\x66\
+\x28\x63\x2e\x63\x61\x6c\x6c\x28\x61\x5b\x66\x5d\x2c\x66\x2c\x61\
+\x5b\x66\x5d\x29\x3d\x3d\x3d\x21\x31\x29\x62\x72\x65\x61\x6b\x7d\
+\x65\x6c\x73\x65\x20\x66\x6f\x72\x28\x3b\x67\x3c\x68\x3b\x29\x69\
+\x66\x28\x63\x2e\x63\x61\x6c\x6c\x28\x61\x5b\x67\x5d\x2c\x67\x2c\
+\x61\x5b\x67\x2b\x2b\x5d\x29\x3d\x3d\x3d\x21\x31\x29\x62\x72\x65\
+\x61\x6b\x3b\x72\x65\x74\x75\x72\x6e\x20\x61\x7d\x2c\x74\x72\x69\
+\x6d\x3a\x47\x3f\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\
+\x72\x65\x74\x75\x72\x6e\x20\x61\x3d\x3d\x6e\x75\x6c\x6c\x3f\x22\
+\x22\x3a\x47\x2e\x63\x61\x6c\x6c\x28\x61\x29\x7d\x3a\x66\x75\x6e\
+\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\
+\x61\x3d\x3d\x6e\x75\x6c\x6c\x3f\x22\x22\x3a\x28\x61\x2b\x22\x22\
+\x29\x2e\x72\x65\x70\x6c\x61\x63\x65\x28\x6b\x2c\x22\x22\x29\x2e\
+\x72\x65\x70\x6c\x61\x63\x65\x28\x6c\x2c\x22\x22\x29\x7d\x2c\x6d\
+\x61\x6b\x65\x41\x72\x72\x61\x79\x3a\x66\x75\x6e\x63\x74\x69\x6f\
+\x6e\x28\x61\x2c\x62\x29\x7b\x76\x61\x72\x20\x63\x3d\x62\x7c\x7c\
+\x5b\x5d\x3b\x69\x66\x28\x61\x21\x3d\x6e\x75\x6c\x6c\x29\x7b\x76\
+\x61\x72\x20\x64\x3d\x65\x2e\x74\x79\x70\x65\x28\x61\x29\x3b\x61\
+\x2e\x6c\x65\x6e\x67\x74\x68\x3d\x3d\x6e\x75\x6c\x6c\x7c\x7c\x64\
+\x3d\x3d\x3d\x22\x73\x74\x72\x69\x6e\x67\x22\x7c\x7c\x64\x3d\x3d\
+\x3d\x22\x66\x75\x6e\x63\x74\x69\x6f\x6e\x22\x7c\x7c\x64\x3d\x3d\
+\x3d\x22\x72\x65\x67\x65\x78\x70\x22\x7c\x7c\x65\x2e\x69\x73\x57\
+\x69\x6e\x64\x6f\x77\x28\x61\x29\x3f\x45\x2e\x63\x61\x6c\x6c\x28\
+\x63\x2c\x61\x29\x3a\x65\x2e\x6d\x65\x72\x67\x65\x28\x63\x2c\x61\
+\x29\x7d\x72\x65\x74\x75\x72\x6e\x20\x63\x7d\x2c\x69\x6e\x41\x72\
+\x72\x61\x79\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\
+\x2c\x63\x29\x7b\x76\x61\x72\x20\x64\x3b\x69\x66\x28\x62\x29\x7b\
+\x69\x66\x28\x48\x29\x72\x65\x74\x75\x72\x6e\x20\x48\x2e\x63\x61\
+\x6c\x6c\x28\x62\x2c\x61\x2c\x63\x29\x3b\x64\x3d\x62\x2e\x6c\x65\
+\x6e\x67\x74\x68\x2c\x63\x3d\x63\x3f\x63\x3c\x30\x3f\x4d\x61\x74\
+\x68\x2e\x6d\x61\x78\x28\x30\x2c\x64\x2b\x63\x29\x3a\x63\x3a\x30\
+\x3b\x66\x6f\x72\x28\x3b\x63\x3c\x64\x3b\x63\x2b\x2b\x29\x69\x66\
+\x28\x63\x20\x69\x6e\x20\x62\x26\x26\x62\x5b\x63\x5d\x3d\x3d\x3d\
+\x61\x29\x72\x65\x74\x75\x72\x6e\x20\x63\x7d\x72\x65\x74\x75\x72\
+\x6e\x2d\x31\x7d\x2c\x6d\x65\x72\x67\x65\x3a\x66\x75\x6e\x63\x74\
+\x69\x6f\x6e\x28\x61\x2c\x63\x29\x7b\x76\x61\x72\x20\x64\x3d\x61\
+\x2e\x6c\x65\x6e\x67\x74\x68\x2c\x65\x3d\x30\x3b\x69\x66\x28\x74\
+\x79\x70\x65\x6f\x66\x20\x63\x2e\x6c\x65\x6e\x67\x74\x68\x3d\x3d\
+\x22\x6e\x75\x6d\x62\x65\x72\x22\x29\x66\x6f\x72\x28\x76\x61\x72\
+\x20\x66\x3d\x63\x2e\x6c\x65\x6e\x67\x74\x68\x3b\x65\x3c\x66\x3b\
+\x65\x2b\x2b\x29\x61\x5b\x64\x2b\x2b\x5d\x3d\x63\x5b\x65\x5d\x3b\
+\x65\x6c\x73\x65\x20\x77\x68\x69\x6c\x65\x28\x63\x5b\x65\x5d\x21\
+\x3d\x3d\x62\x29\x61\x5b\x64\x2b\x2b\x5d\x3d\x63\x5b\x65\x2b\x2b\
+\x5d\x3b\x61\x2e\x6c\x65\x6e\x67\x74\x68\x3d\x64\x3b\x72\x65\x74\
+\x75\x72\x6e\x20\x61\x7d\x2c\x67\x72\x65\x70\x3a\x66\x75\x6e\x63\
+\x74\x69\x6f\x6e\x28\x61\x2c\x62\x2c\x63\x29\x7b\x76\x61\x72\x20\
+\x64\x3d\x5b\x5d\x2c\x65\x3b\x63\x3d\x21\x21\x63\x3b\x66\x6f\x72\
+\x28\x76\x61\x72\x20\x66\x3d\x30\x2c\x67\x3d\x61\x2e\x6c\x65\x6e\
+\x67\x74\x68\x3b\x66\x3c\x67\x3b\x66\x2b\x2b\x29\x65\x3d\x21\x21\
+\x62\x28\x61\x5b\x66\x5d\x2c\x66\x29\x2c\x63\x21\x3d\x3d\x65\x26\
+\x26\x64\x2e\x70\x75\x73\x68\x28\x61\x5b\x66\x5d\x29\x3b\x72\x65\
+\x74\x75\x72\x6e\x20\x64\x7d\x2c\x6d\x61\x70\x3a\x66\x75\x6e\x63\
+\x74\x69\x6f\x6e\x28\x61\x2c\x63\x2c\x64\x29\x7b\x76\x61\x72\x20\
+\x66\x2c\x67\x2c\x68\x3d\x5b\x5d\x2c\x69\x3d\x30\x2c\x6a\x3d\x61\
+\x2e\x6c\x65\x6e\x67\x74\x68\x2c\x6b\x3d\x61\x20\x69\x6e\x73\x74\
+\x61\x6e\x63\x65\x6f\x66\x20\x65\x7c\x7c\x6a\x21\x3d\x3d\x62\x26\
+\x26\x74\x79\x70\x65\x6f\x66\x20\x6a\x3d\x3d\x22\x6e\x75\x6d\x62\
+\x65\x72\x22\x26\x26\x28\x6a\x3e\x30\x26\x26\x61\x5b\x30\x5d\x26\
+\x26\x61\x5b\x6a\x2d\x31\x5d\x7c\x7c\x6a\x3d\x3d\x3d\x30\x7c\x7c\
+\x65\x2e\x69\x73\x41\x72\x72\x61\x79\x28\x61\x29\x29\x3b\x69\x66\
+\x28\x6b\x29\x66\x6f\x72\x28\x3b\x69\x3c\x6a\x3b\x69\x2b\x2b\x29\
+\x66\x3d\x63\x28\x61\x5b\x69\x5d\x2c\x69\x2c\x64\x29\x2c\x66\x21\
+\x3d\x6e\x75\x6c\x6c\x26\x26\x28\x68\x5b\x68\x2e\x6c\x65\x6e\x67\
+\x74\x68\x5d\x3d\x66\x29\x3b\x65\x6c\x73\x65\x20\x66\x6f\x72\x28\
+\x67\x20\x69\x6e\x20\x61\x29\x66\x3d\x63\x28\x61\x5b\x67\x5d\x2c\
+\x67\x2c\x64\x29\x2c\x66\x21\x3d\x6e\x75\x6c\x6c\x26\x26\x28\x68\
+\x5b\x68\x2e\x6c\x65\x6e\x67\x74\x68\x5d\x3d\x66\x29\x3b\x72\x65\
+\x74\x75\x72\x6e\x20\x68\x2e\x63\x6f\x6e\x63\x61\x74\x2e\x61\x70\
+\x70\x6c\x79\x28\x5b\x5d\x2c\x68\x29\x7d\x2c\x67\x75\x69\x64\x3a\
+\x31\x2c\x70\x72\x6f\x78\x79\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\
+\x28\x61\x2c\x63\x29\x7b\x69\x66\x28\x74\x79\x70\x65\x6f\x66\x20\
+\x63\x3d\x3d\x22\x73\x74\x72\x69\x6e\x67\x22\x29\x7b\x76\x61\x72\
+\x20\x64\x3d\x61\x5b\x63\x5d\x3b\x63\x3d\x61\x2c\x61\x3d\x64\x7d\
+\x69\x66\x28\x21\x65\x2e\x69\x73\x46\x75\x6e\x63\x74\x69\x6f\x6e\
+\x28\x61\x29\x29\x72\x65\x74\x75\x72\x6e\x20\x62\x3b\x76\x61\x72\
+\x20\x66\x3d\x46\x2e\x63\x61\x6c\x6c\x28\x61\x72\x67\x75\x6d\x65\
+\x6e\x74\x73\x2c\x32\x29\x2c\x67\x3d\x66\x75\x6e\x63\x74\x69\x6f\
+\x6e\x28\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x61\x2e\x61\x70\x70\
+\x6c\x79\x28\x63\x2c\x66\x2e\x63\x6f\x6e\x63\x61\x74\x28\x46\x2e\
+\x63\x61\x6c\x6c\x28\x61\x72\x67\x75\x6d\x65\x6e\x74\x73\x29\x29\
+\x29\x7d\x3b\x67\x2e\x67\x75\x69\x64\x3d\x61\x2e\x67\x75\x69\x64\
+\x3d\x61\x2e\x67\x75\x69\x64\x7c\x7c\x67\x2e\x67\x75\x69\x64\x7c\
+\x7c\x65\x2e\x67\x75\x69\x64\x2b\x2b\x3b\x72\x65\x74\x75\x72\x6e\
+\x20\x67\x7d\x2c\x61\x63\x63\x65\x73\x73\x3a\x66\x75\x6e\x63\x74\
+\x69\x6f\x6e\x28\x61\x2c\x63\x2c\x64\x2c\x66\x2c\x67\x2c\x68\x29\
+\x7b\x76\x61\x72\x20\x69\x3d\x61\x2e\x6c\x65\x6e\x67\x74\x68\x3b\
+\x69\x66\x28\x74\x79\x70\x65\x6f\x66\x20\x63\x3d\x3d\x22\x6f\x62\
+\x6a\x65\x63\x74\x22\x29\x7b\x66\x6f\x72\x28\x76\x61\x72\x20\x6a\
+\x20\x69\x6e\x20\x63\x29\x65\x2e\x61\x63\x63\x65\x73\x73\x28\x61\
+\x2c\x6a\x2c\x63\x5b\x6a\x5d\x2c\x66\x2c\x67\x2c\x64\x29\x3b\x72\
+\x65\x74\x75\x72\x6e\x20\x61\x7d\x69\x66\x28\x64\x21\x3d\x3d\x62\
+\x29\x7b\x66\x3d\x21\x68\x26\x26\x66\x26\x26\x65\x2e\x69\x73\x46\
+\x75\x6e\x63\x74\x69\x6f\x6e\x28\x64\x29\x3b\x66\x6f\x72\x28\x76\
+\x61\x72\x20\x6b\x3d\x30\x3b\x6b\x3c\x69\x3b\x6b\x2b\x2b\x29\x67\
+\x28\x61\x5b\x6b\x5d\x2c\x63\x2c\x66\x3f\x64\x2e\x63\x61\x6c\x6c\
+\x28\x61\x5b\x6b\x5d\x2c\x6b\x2c\x67\x28\x61\x5b\x6b\x5d\x2c\x63\
+\x29\x29\x3a\x64\x2c\x68\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x61\
+\x7d\x72\x65\x74\x75\x72\x6e\x20\x69\x3f\x67\x28\x61\x5b\x30\x5d\
+\x2c\x63\x29\x3a\x62\x7d\x2c\x6e\x6f\x77\x3a\x66\x75\x6e\x63\x74\
+\x69\x6f\x6e\x28\x29\x7b\x72\x65\x74\x75\x72\x6e\x28\x6e\x65\x77\
+\x20\x44\x61\x74\x65\x29\x2e\x67\x65\x74\x54\x69\x6d\x65\x28\x29\
+\x7d\x2c\x75\x61\x4d\x61\x74\x63\x68\x3a\x66\x75\x6e\x63\x74\x69\
+\x6f\x6e\x28\x61\x29\x7b\x61\x3d\x61\x2e\x74\x6f\x4c\x6f\x77\x65\
+\x72\x43\x61\x73\x65\x28\x29\x3b\x76\x61\x72\x20\x62\x3d\x72\x2e\
+\x65\x78\x65\x63\x28\x61\x29\x7c\x7c\x73\x2e\x65\x78\x65\x63\x28\
+\x61\x29\x7c\x7c\x74\x2e\x65\x78\x65\x63\x28\x61\x29\x7c\x7c\x61\
+\x2e\x69\x6e\x64\x65\x78\x4f\x66\x28\x22\x63\x6f\x6d\x70\x61\x74\
+\x69\x62\x6c\x65\x22\x29\x3c\x30\x26\x26\x75\x2e\x65\x78\x65\x63\
+\x28\x61\x29\x7c\x7c\x5b\x5d\x3b\x72\x65\x74\x75\x72\x6e\x7b\x62\
+\x72\x6f\x77\x73\x65\x72\x3a\x62\x5b\x31\x5d\x7c\x7c\x22\x22\x2c\
+\x76\x65\x72\x73\x69\x6f\x6e\x3a\x62\x5b\x32\x5d\x7c\x7c\x22\x30\
+\x22\x7d\x7d\x2c\x73\x75\x62\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\
+\x28\x29\x7b\x66\x75\x6e\x63\x74\x69\x6f\x6e\x20\x61\x28\x62\x2c\
+\x63\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x6e\x65\x77\x20\x61\x2e\
+\x66\x6e\x2e\x69\x6e\x69\x74\x28\x62\x2c\x63\x29\x7d\x65\x2e\x65\
+\x78\x74\x65\x6e\x64\x28\x21\x30\x2c\x61\x2c\x74\x68\x69\x73\x29\
+\x2c\x61\x2e\x73\x75\x70\x65\x72\x63\x6c\x61\x73\x73\x3d\x74\x68\
+\x69\x73\x2c\x61\x2e\x66\x6e\x3d\x61\x2e\x70\x72\x6f\x74\x6f\x74\
+\x79\x70\x65\x3d\x74\x68\x69\x73\x28\x29\x2c\x61\x2e\x66\x6e\x2e\
+\x63\x6f\x6e\x73\x74\x72\x75\x63\x74\x6f\x72\x3d\x61\x2c\x61\x2e\
+\x73\x75\x62\x3d\x74\x68\x69\x73\x2e\x73\x75\x62\x2c\x61\x2e\x66\
+\x6e\x2e\x69\x6e\x69\x74\x3d\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\
+\x64\x2c\x66\x29\x7b\x66\x26\x26\x66\x20\x69\x6e\x73\x74\x61\x6e\
+\x63\x65\x6f\x66\x20\x65\x26\x26\x21\x28\x66\x20\x69\x6e\x73\x74\
+\x61\x6e\x63\x65\x6f\x66\x20\x61\x29\x26\x26\x28\x66\x3d\x61\x28\
+\x66\x29\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x65\x2e\x66\x6e\x2e\
+\x69\x6e\x69\x74\x2e\x63\x61\x6c\x6c\x28\x74\x68\x69\x73\x2c\x64\
+\x2c\x66\x2c\x62\x29\x7d\x2c\x61\x2e\x66\x6e\x2e\x69\x6e\x69\x74\
+\x2e\x70\x72\x6f\x74\x6f\x74\x79\x70\x65\x3d\x61\x2e\x66\x6e\x3b\
+\x76\x61\x72\x20\x62\x3d\x61\x28\x63\x29\x3b\x72\x65\x74\x75\x72\
+\x6e\x20\x61\x7d\x2c\x62\x72\x6f\x77\x73\x65\x72\x3a\x7b\x7d\x7d\
+\x29\x2c\x65\x2e\x65\x61\x63\x68\x28\x22\x42\x6f\x6f\x6c\x65\x61\
+\x6e\x20\x4e\x75\x6d\x62\x65\x72\x20\x53\x74\x72\x69\x6e\x67\x20\
+\x46\x75\x6e\x63\x74\x69\x6f\x6e\x20\x41\x72\x72\x61\x79\x20\x44\
+\x61\x74\x65\x20\x52\x65\x67\x45\x78\x70\x20\x4f\x62\x6a\x65\x63\
+\x74\x22\x2e\x73\x70\x6c\x69\x74\x28\x22\x20\x22\x29\x2c\x66\x75\
+\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x29\x7b\x49\x5b\x22\x5b\
+\x6f\x62\x6a\x65\x63\x74\x20\x22\x2b\x62\x2b\x22\x5d\x22\x5d\x3d\
+\x62\x2e\x74\x6f\x4c\x6f\x77\x65\x72\x43\x61\x73\x65\x28\x29\x7d\
+\x29\x2c\x7a\x3d\x65\x2e\x75\x61\x4d\x61\x74\x63\x68\x28\x79\x29\
+\x2c\x7a\x2e\x62\x72\x6f\x77\x73\x65\x72\x26\x26\x28\x65\x2e\x62\
+\x72\x6f\x77\x73\x65\x72\x5b\x7a\x2e\x62\x72\x6f\x77\x73\x65\x72\
+\x5d\x3d\x21\x30\x2c\x65\x2e\x62\x72\x6f\x77\x73\x65\x72\x2e\x76\
+\x65\x72\x73\x69\x6f\x6e\x3d\x7a\x2e\x76\x65\x72\x73\x69\x6f\x6e\
+\x29\x2c\x65\x2e\x62\x72\x6f\x77\x73\x65\x72\x2e\x77\x65\x62\x6b\
+\x69\x74\x26\x26\x28\x65\x2e\x62\x72\x6f\x77\x73\x65\x72\x2e\x73\
+\x61\x66\x61\x72\x69\x3d\x21\x30\x29\x2c\x6a\x2e\x74\x65\x73\x74\
+\x28\x22\xc2\xa0\x22\x29\x26\x26\x28\x6b\x3d\x2f\x5e\x5b\x5c\x73\
+\x5c\x78\x41\x30\x5d\x2b\x2f\x2c\x6c\x3d\x2f\x5b\x5c\x73\x5c\x78\
+\x41\x30\x5d\x2b\x24\x2f\x29\x2c\x68\x3d\x65\x28\x63\x29\x2c\x63\
+\x2e\x61\x64\x64\x45\x76\x65\x6e\x74\x4c\x69\x73\x74\x65\x6e\x65\
+\x72\x3f\x42\x3d\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x29\x7b\x63\
+\x2e\x72\x65\x6d\x6f\x76\x65\x45\x76\x65\x6e\x74\x4c\x69\x73\x74\
+\x65\x6e\x65\x72\x28\x22\x44\x4f\x4d\x43\x6f\x6e\x74\x65\x6e\x74\
+\x4c\x6f\x61\x64\x65\x64\x22\x2c\x42\x2c\x21\x31\x29\x2c\x65\x2e\
+\x72\x65\x61\x64\x79\x28\x29\x7d\x3a\x63\x2e\x61\x74\x74\x61\x63\
+\x68\x45\x76\x65\x6e\x74\x26\x26\x28\x42\x3d\x66\x75\x6e\x63\x74\
+\x69\x6f\x6e\x28\x29\x7b\x63\x2e\x72\x65\x61\x64\x79\x53\x74\x61\
+\x74\x65\x3d\x3d\x3d\x22\x63\x6f\x6d\x70\x6c\x65\x74\x65\x22\x26\
+\x26\x28\x63\x2e\x64\x65\x74\x61\x63\x68\x45\x76\x65\x6e\x74\x28\
+\x22\x6f\x6e\x72\x65\x61\x64\x79\x73\x74\x61\x74\x65\x63\x68\x61\
+\x6e\x67\x65\x22\x2c\x42\x29\x2c\x65\x2e\x72\x65\x61\x64\x79\x28\
+\x29\x29\x7d\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x65\x7d\x28\x29\
+\x2c\x67\x3d\x7b\x7d\x3b\x66\x2e\x43\x61\x6c\x6c\x62\x61\x63\x6b\
+\x73\x3d\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x61\x3d\
+\x61\x3f\x67\x5b\x61\x5d\x7c\x7c\x68\x28\x61\x29\x3a\x7b\x7d\x3b\
+\x76\x61\x72\x20\x63\x3d\x5b\x5d\x2c\x64\x3d\x5b\x5d\x2c\x65\x2c\
+\x69\x2c\x6a\x2c\x6b\x2c\x6c\x2c\x6d\x3d\x66\x75\x6e\x63\x74\x69\
+\x6f\x6e\x28\x62\x29\x7b\x76\x61\x72\x20\x64\x2c\x65\x2c\x67\x2c\
+\x68\x2c\x69\x3b\x66\x6f\x72\x28\x64\x3d\x30\x2c\x65\x3d\x62\x2e\
+\x6c\x65\x6e\x67\x74\x68\x3b\x64\x3c\x65\x3b\x64\x2b\x2b\x29\x67\
+\x3d\x62\x5b\x64\x5d\x2c\x68\x3d\x66\x2e\x74\x79\x70\x65\x28\x67\
+\x29\x2c\x68\x3d\x3d\x3d\x22\x61\x72\x72\x61\x79\x22\x3f\x6d\x28\
+\x67\x29\x3a\x68\x3d\x3d\x3d\x22\x66\x75\x6e\x63\x74\x69\x6f\x6e\
+\x22\x26\x26\x28\x21\x61\x2e\x75\x6e\x69\x71\x75\x65\x7c\x7c\x21\
+\x6f\x2e\x68\x61\x73\x28\x67\x29\x29\x26\x26\x63\x2e\x70\x75\x73\
+\x68\x28\x67\x29\x7d\x2c\x6e\x3d\x66\x75\x6e\x63\x74\x69\x6f\x6e\
+\x28\x62\x2c\x66\x29\x7b\x66\x3d\x66\x7c\x7c\x5b\x5d\x2c\x65\x3d\
+\x21\x61\x2e\x6d\x65\x6d\x6f\x72\x79\x7c\x7c\x5b\x62\x2c\x66\x5d\
+\x2c\x69\x3d\x21\x30\x2c\x6c\x3d\x6a\x7c\x7c\x30\x2c\x6a\x3d\x30\
+\x2c\x6b\x3d\x63\x2e\x6c\x65\x6e\x67\x74\x68\x3b\x66\x6f\x72\x28\
+\x3b\x63\x26\x26\x6c\x3c\x6b\x3b\x6c\x2b\x2b\x29\x69\x66\x28\x63\
+\x5b\x6c\x5d\x2e\x61\x70\x70\x6c\x79\x28\x62\x2c\x66\x29\x3d\x3d\
+\x3d\x21\x31\x26\x26\x61\x2e\x73\x74\x6f\x70\x4f\x6e\x46\x61\x6c\
+\x73\x65\x29\x7b\x65\x3d\x21\x30\x3b\x62\x72\x65\x61\x6b\x7d\x69\
+\x3d\x21\x31\x2c\x63\x26\x26\x28\x61\x2e\x6f\x6e\x63\x65\x3f\x65\
+\x3d\x3d\x3d\x21\x30\x3f\x6f\x2e\x64\x69\x73\x61\x62\x6c\x65\x28\
+\x29\x3a\x63\x3d\x5b\x5d\x3a\x64\x26\x26\x64\x2e\x6c\x65\x6e\x67\
+\x74\x68\x26\x26\x28\x65\x3d\x64\x2e\x73\x68\x69\x66\x74\x28\x29\
+\x2c\x6f\x2e\x66\x69\x72\x65\x57\x69\x74\x68\x28\x65\x5b\x30\x5d\
+\x2c\x65\x5b\x31\x5d\x29\x29\x29\x7d\x2c\x6f\x3d\x7b\x61\x64\x64\
+\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x29\x7b\x69\x66\x28\x63\
+\x29\x7b\x76\x61\x72\x20\x61\x3d\x63\x2e\x6c\x65\x6e\x67\x74\x68\
+\x3b\x6d\x28\x61\x72\x67\x75\x6d\x65\x6e\x74\x73\x29\x2c\x69\x3f\
+\x6b\x3d\x63\x2e\x6c\x65\x6e\x67\x74\x68\x3a\x65\x26\x26\x65\x21\
+\x3d\x3d\x21\x30\x26\x26\x28\x6a\x3d\x61\x2c\x6e\x28\x65\x5b\x30\
+\x5d\x2c\x65\x5b\x31\x5d\x29\x29\x7d\x72\x65\x74\x75\x72\x6e\x20\
+\x74\x68\x69\x73\x7d\x2c\x72\x65\x6d\x6f\x76\x65\x3a\x66\x75\x6e\
+\x63\x74\x69\x6f\x6e\x28\x29\x7b\x69\x66\x28\x63\x29\x7b\x76\x61\
+\x72\x20\x62\x3d\x61\x72\x67\x75\x6d\x65\x6e\x74\x73\x2c\x64\x3d\
+\x30\x2c\x65\x3d\x62\x2e\x6c\x65\x6e\x67\x74\x68\x3b\x66\x6f\x72\
+\x28\x3b\x64\x3c\x65\x3b\x64\x2b\x2b\x29\x66\x6f\x72\x28\x76\x61\
+\x72\x20\x66\x3d\x30\x3b\x66\x3c\x63\x2e\x6c\x65\x6e\x67\x74\x68\
+\x3b\x66\x2b\x2b\x29\x69\x66\x28\x62\x5b\x64\x5d\x3d\x3d\x3d\x63\
+\x5b\x66\x5d\x29\x7b\x69\x26\x26\x66\x3c\x3d\x6b\x26\x26\x28\x6b\
+\x2d\x2d\x2c\x66\x3c\x3d\x6c\x26\x26\x6c\x2d\x2d\x29\x2c\x63\x2e\
+\x73\x70\x6c\x69\x63\x65\x28\x66\x2d\x2d\x2c\x31\x29\x3b\x69\x66\
+\x28\x61\x2e\x75\x6e\x69\x71\x75\x65\x29\x62\x72\x65\x61\x6b\x7d\
+\x7d\x72\x65\x74\x75\x72\x6e\x20\x74\x68\x69\x73\x7d\x2c\x68\x61\
+\x73\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x69\x66\
+\x28\x63\x29\x7b\x76\x61\x72\x20\x62\x3d\x30\x2c\x64\x3d\x63\x2e\
+\x6c\x65\x6e\x67\x74\x68\x3b\x66\x6f\x72\x28\x3b\x62\x3c\x64\x3b\
+\x62\x2b\x2b\x29\x69\x66\x28\x61\x3d\x3d\x3d\x63\x5b\x62\x5d\x29\
+\x72\x65\x74\x75\x72\x6e\x21\x30\x7d\x72\x65\x74\x75\x72\x6e\x21\
+\x31\x7d\x2c\x65\x6d\x70\x74\x79\x3a\x66\x75\x6e\x63\x74\x69\x6f\
+\x6e\x28\x29\x7b\x63\x3d\x5b\x5d\x3b\x72\x65\x74\x75\x72\x6e\x20\
+\x74\x68\x69\x73\x7d\x2c\x64\x69\x73\x61\x62\x6c\x65\x3a\x66\x75\
+\x6e\x63\x74\x69\x6f\x6e\x28\x29\x7b\x63\x3d\x64\x3d\x65\x3d\x62\
+\x3b\x72\x65\x74\x75\x72\x6e\x20\x74\x68\x69\x73\x7d\x2c\x64\x69\
+\x73\x61\x62\x6c\x65\x64\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\
+\x29\x7b\x72\x65\x74\x75\x72\x6e\x21\x63\x7d\x2c\x6c\x6f\x63\x6b\
+\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x29\x7b\x64\x3d\x62\x2c\
+\x28\x21\x65\x7c\x7c\x65\x3d\x3d\x3d\x21\x30\x29\x26\x26\x6f\x2e\
+\x64\x69\x73\x61\x62\x6c\x65\x28\x29\x3b\x72\x65\x74\x75\x72\x6e\
+\x20\x74\x68\x69\x73\x7d\x2c\x6c\x6f\x63\x6b\x65\x64\x3a\x66\x75\
+\x6e\x63\x74\x69\x6f\x6e\x28\x29\x7b\x72\x65\x74\x75\x72\x6e\x21\
+\x64\x7d\x2c\x66\x69\x72\x65\x57\x69\x74\x68\x3a\x66\x75\x6e\x63\
+\x74\x69\x6f\x6e\x28\x62\x2c\x63\x29\x7b\x64\x26\x26\x28\x69\x3f\
+\x61\x2e\x6f\x6e\x63\x65\x7c\x7c\x64\x2e\x70\x75\x73\x68\x28\x5b\
+\x62\x2c\x63\x5d\x29\x3a\x28\x21\x61\x2e\x6f\x6e\x63\x65\x7c\x7c\
+\x21\x65\x29\x26\x26\x6e\x28\x62\x2c\x63\x29\x29\x3b\x72\x65\x74\
+\x75\x72\x6e\x20\x74\x68\x69\x73\x7d\x2c\x66\x69\x72\x65\x3a\x66\
+\x75\x6e\x63\x74\x69\x6f\x6e\x28\x29\x7b\x6f\x2e\x66\x69\x72\x65\
+\x57\x69\x74\x68\x28\x74\x68\x69\x73\x2c\x61\x72\x67\x75\x6d\x65\
+\x6e\x74\x73\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x74\x68\x69\x73\
+\x7d\x2c\x66\x69\x72\x65\x64\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\
+\x28\x29\x7b\x72\x65\x74\x75\x72\x6e\x21\x21\x65\x7d\x7d\x3b\x72\
+\x65\x74\x75\x72\x6e\x20\x6f\x7d\x3b\x76\x61\x72\x20\x69\x3d\x5b\
+\x5d\x2e\x73\x6c\x69\x63\x65\x3b\x66\x2e\x65\x78\x74\x65\x6e\x64\
+\x28\x7b\x44\x65\x66\x65\x72\x72\x65\x64\x3a\x66\x75\x6e\x63\x74\
+\x69\x6f\x6e\x28\x61\x29\x7b\x76\x61\x72\x20\x62\x3d\x66\x2e\x43\
+\x61\x6c\x6c\x62\x61\x63\x6b\x73\x28\x22\x6f\x6e\x63\x65\x20\x6d\
+\x65\x6d\x6f\x72\x79\x22\x29\x2c\x63\x3d\x66\x2e\x43\x61\x6c\x6c\
+\x62\x61\x63\x6b\x73\x28\x22\x6f\x6e\x63\x65\x20\x6d\x65\x6d\x6f\
+\x72\x79\x22\x29\x2c\x64\x3d\x66\x2e\x43\x61\x6c\x6c\x62\x61\x63\
+\x6b\x73\x28\x22\x6d\x65\x6d\x6f\x72\x79\x22\x29\x2c\x65\x3d\x22\
+\x70\x65\x6e\x64\x69\x6e\x67\x22\x2c\x67\x3d\x7b\x72\x65\x73\x6f\
+\x6c\x76\x65\x3a\x62\x2c\x72\x65\x6a\x65\x63\x74\x3a\x63\x2c\x6e\
+\x6f\x74\x69\x66\x79\x3a\x64\x7d\x2c\x68\x3d\x7b\x64\x6f\x6e\x65\
+\x3a\x62\x2e\x61\x64\x64\x2c\x66\x61\x69\x6c\x3a\x63\x2e\x61\x64\
+\x64\x2c\x70\x72\x6f\x67\x72\x65\x73\x73\x3a\x64\x2e\x61\x64\x64\
+\x2c\x73\x74\x61\x74\x65\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\
+\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x65\x7d\x2c\x69\x73\x52\x65\
+\x73\x6f\x6c\x76\x65\x64\x3a\x62\x2e\x66\x69\x72\x65\x64\x2c\x69\
+\x73\x52\x65\x6a\x65\x63\x74\x65\x64\x3a\x63\x2e\x66\x69\x72\x65\
+\x64\x2c\x74\x68\x65\x6e\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\
+\x61\x2c\x62\x2c\x63\x29\x7b\x69\x2e\x64\x6f\x6e\x65\x28\x61\x29\
+\x2e\x66\x61\x69\x6c\x28\x62\x29\x2e\x70\x72\x6f\x67\x72\x65\x73\
+\x73\x28\x63\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x74\x68\x69\x73\
+\x7d\x2c\x61\x6c\x77\x61\x79\x73\x3a\x66\x75\x6e\x63\x74\x69\x6f\
+\x6e\x28\x29\x7b\x69\x2e\x64\x6f\x6e\x65\x2e\x61\x70\x70\x6c\x79\
+\x28\x69\x2c\x61\x72\x67\x75\x6d\x65\x6e\x74\x73\x29\x2e\x66\x61\
+\x69\x6c\x2e\x61\x70\x70\x6c\x79\x28\x69\x2c\x61\x72\x67\x75\x6d\
+\x65\x6e\x74\x73\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x74\x68\x69\
+\x73\x7d\x2c\x70\x69\x70\x65\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\
+\x28\x61\x2c\x62\x2c\x63\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x66\
+\x2e\x44\x65\x66\x65\x72\x72\x65\x64\x28\x66\x75\x6e\x63\x74\x69\
+\x6f\x6e\x28\x64\x29\x7b\x66\x2e\x65\x61\x63\x68\x28\x7b\x64\x6f\
+\x6e\x65\x3a\x5b\x61\x2c\x22\x72\x65\x73\x6f\x6c\x76\x65\x22\x5d\
+\x2c\x66\x61\x69\x6c\x3a\x5b\x62\x2c\x22\x72\x65\x6a\x65\x63\x74\
+\x22\x5d\x2c\x70\x72\x6f\x67\x72\x65\x73\x73\x3a\x5b\x63\x2c\x22\
+\x6e\x6f\x74\x69\x66\x79\x22\x5d\x7d\x2c\x66\x75\x6e\x63\x74\x69\
+\x6f\x6e\x28\x61\x2c\x62\x29\x7b\x76\x61\x72\x20\x63\x3d\x62\x5b\
+\x30\x5d\x2c\x65\x3d\x62\x5b\x31\x5d\x2c\x67\x3b\x66\x2e\x69\x73\
+\x46\x75\x6e\x63\x74\x69\x6f\x6e\x28\x63\x29\x3f\x69\x5b\x61\x5d\
+\x28\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x29\x7b\x67\x3d\x63\x2e\
+\x61\x70\x70\x6c\x79\x28\x74\x68\x69\x73\x2c\x61\x72\x67\x75\x6d\
+\x65\x6e\x74\x73\x29\x2c\x67\x26\x26\x66\x2e\x69\x73\x46\x75\x6e\
+\x63\x74\x69\x6f\x6e\x28\x67\x2e\x70\x72\x6f\x6d\x69\x73\x65\x29\
+\x3f\x67\x2e\x70\x72\x6f\x6d\x69\x73\x65\x28\x29\x2e\x74\x68\x65\
+\x6e\x28\x64\x2e\x72\x65\x73\x6f\x6c\x76\x65\x2c\x64\x2e\x72\x65\
+\x6a\x65\x63\x74\x2c\x64\x2e\x6e\x6f\x74\x69\x66\x79\x29\x3a\x64\
+\x5b\x65\x2b\x22\x57\x69\x74\x68\x22\x5d\x28\x74\x68\x69\x73\x3d\
+\x3d\x3d\x69\x3f\x64\x3a\x74\x68\x69\x73\x2c\x5b\x67\x5d\x29\x7d\
+\x29\x3a\x69\x5b\x61\x5d\x28\x64\x5b\x65\x5d\x29\x7d\x29\x7d\x29\
+\x2e\x70\x72\x6f\x6d\x69\x73\x65\x28\x29\x7d\x2c\x70\x72\x6f\x6d\
+\x69\x73\x65\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\
+\x69\x66\x28\x61\x3d\x3d\x6e\x75\x6c\x6c\x29\x61\x3d\x68\x3b\x65\
+\x6c\x73\x65\x20\x66\x6f\x72\x28\x76\x61\x72\x20\x62\x20\x69\x6e\
+\x20\x68\x29\x61\x5b\x62\x5d\x3d\x68\x5b\x62\x5d\x3b\x72\x65\x74\
+\x75\x72\x6e\x20\x61\x7d\x7d\x2c\x69\x3d\x68\x2e\x70\x72\x6f\x6d\
+\x69\x73\x65\x28\x7b\x7d\x29\x2c\x6a\x3b\x66\x6f\x72\x28\x6a\x20\
+\x69\x6e\x20\x67\x29\x69\x5b\x6a\x5d\x3d\x67\x5b\x6a\x5d\x2e\x66\
+\x69\x72\x65\x2c\x69\x5b\x6a\x2b\x22\x57\x69\x74\x68\x22\x5d\x3d\
+\x67\x5b\x6a\x5d\x2e\x66\x69\x72\x65\x57\x69\x74\x68\x3b\x69\x2e\
+\x64\x6f\x6e\x65\x28\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x29\x7b\
+\x65\x3d\x22\x72\x65\x73\x6f\x6c\x76\x65\x64\x22\x7d\x2c\x63\x2e\
+\x64\x69\x73\x61\x62\x6c\x65\x2c\x64\x2e\x6c\x6f\x63\x6b\x29\x2e\
+\x66\x61\x69\x6c\x28\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x29\x7b\
+\x65\x3d\x22\x72\x65\x6a\x65\x63\x74\x65\x64\x22\x7d\x2c\x62\x2e\
+\x64\x69\x73\x61\x62\x6c\x65\x2c\x64\x2e\x6c\x6f\x63\x6b\x29\x2c\
+\x61\x26\x26\x61\x2e\x63\x61\x6c\x6c\x28\x69\x2c\x69\x29\x3b\x72\
+\x65\x74\x75\x72\x6e\x20\x69\x7d\x2c\x77\x68\x65\x6e\x3a\x66\x75\
+\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x66\x75\x6e\x63\x74\x69\
+\x6f\x6e\x20\x6d\x28\x61\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x66\
+\x75\x6e\x63\x74\x69\x6f\x6e\x28\x62\x29\x7b\x65\x5b\x61\x5d\x3d\
+\x61\x72\x67\x75\x6d\x65\x6e\x74\x73\x2e\x6c\x65\x6e\x67\x74\x68\
+\x3e\x31\x3f\x69\x2e\x63\x61\x6c\x6c\x28\x61\x72\x67\x75\x6d\x65\
+\x6e\x74\x73\x2c\x30\x29\x3a\x62\x2c\x6a\x2e\x6e\x6f\x74\x69\x66\
+\x79\x57\x69\x74\x68\x28\x6b\x2c\x65\x29\x7d\x7d\x66\x75\x6e\x63\
+\x74\x69\x6f\x6e\x20\x6c\x28\x61\x29\x7b\x72\x65\x74\x75\x72\x6e\
+\x20\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x63\x29\x7b\x62\x5b\x61\
+\x5d\x3d\x61\x72\x67\x75\x6d\x65\x6e\x74\x73\x2e\x6c\x65\x6e\x67\
+\x74\x68\x3e\x31\x3f\x69\x2e\x63\x61\x6c\x6c\x28\x61\x72\x67\x75\
+\x6d\x65\x6e\x74\x73\x2c\x30\x29\x3a\x63\x2c\x2d\x2d\x67\x7c\x7c\
+\x6a\x2e\x72\x65\x73\x6f\x6c\x76\x65\x57\x69\x74\x68\x28\x6a\x2c\
+\x62\x29\x7d\x7d\x76\x61\x72\x20\x62\x3d\x69\x2e\x63\x61\x6c\x6c\
+\x28\x61\x72\x67\x75\x6d\x65\x6e\x74\x73\x2c\x30\x29\x2c\x63\x3d\
+\x30\x2c\x64\x3d\x62\x2e\x6c\x65\x6e\x67\x74\x68\x2c\x65\x3d\x41\
+\x72\x72\x61\x79\x28\x64\x29\x2c\x67\x3d\x64\x2c\x68\x3d\x64\x2c\
+\x6a\x3d\x64\x3c\x3d\x31\x26\x26\x61\x26\x26\x66\x2e\x69\x73\x46\
+\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2e\x70\x72\x6f\x6d\x69\x73\
+\x65\x29\x3f\x61\x3a\x66\x2e\x44\x65\x66\x65\x72\x72\x65\x64\x28\
+\x29\x2c\x6b\x3d\x6a\x2e\x70\x72\x6f\x6d\x69\x73\x65\x28\x29\x3b\
+\x69\x66\x28\x64\x3e\x31\x29\x7b\x66\x6f\x72\x28\x3b\x63\x3c\x64\
+\x3b\x63\x2b\x2b\x29\x62\x5b\x63\x5d\x26\x26\x62\x5b\x63\x5d\x2e\
+\x70\x72\x6f\x6d\x69\x73\x65\x26\x26\x66\x2e\x69\x73\x46\x75\x6e\
+\x63\x74\x69\x6f\x6e\x28\x62\x5b\x63\x5d\x2e\x70\x72\x6f\x6d\x69\
+\x73\x65\x29\x3f\x62\x5b\x63\x5d\x2e\x70\x72\x6f\x6d\x69\x73\x65\
+\x28\x29\x2e\x74\x68\x65\x6e\x28\x6c\x28\x63\x29\x2c\x6a\x2e\x72\
+\x65\x6a\x65\x63\x74\x2c\x6d\x28\x63\x29\x29\x3a\x2d\x2d\x67\x3b\
+\x67\x7c\x7c\x6a\x2e\x72\x65\x73\x6f\x6c\x76\x65\x57\x69\x74\x68\
+\x28\x6a\x2c\x62\x29\x7d\x65\x6c\x73\x65\x20\x6a\x21\x3d\x3d\x61\
+\x26\x26\x6a\x2e\x72\x65\x73\x6f\x6c\x76\x65\x57\x69\x74\x68\x28\
+\x6a\x2c\x64\x3f\x5b\x61\x5d\x3a\x5b\x5d\x29\x3b\x72\x65\x74\x75\
+\x72\x6e\x20\x6b\x7d\x7d\x29\x2c\x66\x2e\x73\x75\x70\x70\x6f\x72\
+\x74\x3d\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x29\x7b\x76\x61\x72\
+\x20\x62\x2c\x64\x2c\x65\x2c\x67\x2c\x68\x2c\x69\x2c\x6a\x2c\x6b\
+\x2c\x6c\x2c\x6d\x2c\x6e\x2c\x6f\x2c\x70\x2c\x71\x3d\x63\x2e\x63\
+\x72\x65\x61\x74\x65\x45\x6c\x65\x6d\x65\x6e\x74\x28\x22\x64\x69\
+\x76\x22\x29\x2c\x72\x3d\x63\x2e\x64\x6f\x63\x75\x6d\x65\x6e\x74\
+\x45\x6c\x65\x6d\x65\x6e\x74\x3b\x71\x2e\x73\x65\x74\x41\x74\x74\
+\x72\x69\x62\x75\x74\x65\x28\x22\x63\x6c\x61\x73\x73\x4e\x61\x6d\
+\x65\x22\x2c\x22\x74\x22\x29\x2c\x71\x2e\x69\x6e\x6e\x65\x72\x48\
+\x54\x4d\x4c\x3d\x22\x20\x20\x20\x3c\x6c\x69\x6e\x6b\x2f\x3e\x3c\
+\x74\x61\x62\x6c\x65\x3e\x3c\x2f\x74\x61\x62\x6c\x65\x3e\x3c\x61\
+\x20\x68\x72\x65\x66\x3d\x27\x2f\x61\x27\x20\x73\x74\x79\x6c\x65\
+\x3d\x27\x74\x6f\x70\x3a\x31\x70\x78\x3b\x66\x6c\x6f\x61\x74\x3a\
+\x6c\x65\x66\x74\x3b\x6f\x70\x61\x63\x69\x74\x79\x3a\x2e\x35\x35\
+\x3b\x27\x3e\x61\x3c\x2f\x61\x3e\x3c\x69\x6e\x70\x75\x74\x20\x74\
+\x79\x70\x65\x3d\x27\x63\x68\x65\x63\x6b\x62\x6f\x78\x27\x2f\x3e\
+\x22\x2c\x64\x3d\x71\x2e\x67\x65\x74\x45\x6c\x65\x6d\x65\x6e\x74\
+\x73\x42\x79\x54\x61\x67\x4e\x61\x6d\x65\x28\x22\x2a\x22\x29\x2c\
+\x65\x3d\x71\x2e\x67\x65\x74\x45\x6c\x65\x6d\x65\x6e\x74\x73\x42\
+\x79\x54\x61\x67\x4e\x61\x6d\x65\x28\x22\x61\x22\x29\x5b\x30\x5d\
+\x3b\x69\x66\x28\x21\x64\x7c\x7c\x21\x64\x2e\x6c\x65\x6e\x67\x74\
+\x68\x7c\x7c\x21\x65\x29\x72\x65\x74\x75\x72\x6e\x7b\x7d\x3b\x67\
+\x3d\x63\x2e\x63\x72\x65\x61\x74\x65\x45\x6c\x65\x6d\x65\x6e\x74\
+\x28\x22\x73\x65\x6c\x65\x63\x74\x22\x29\x2c\x68\x3d\x67\x2e\x61\
+\x70\x70\x65\x6e\x64\x43\x68\x69\x6c\x64\x28\x63\x2e\x63\x72\x65\
+\x61\x74\x65\x45\x6c\x65\x6d\x65\x6e\x74\x28\x22\x6f\x70\x74\x69\
+\x6f\x6e\x22\x29\x29\x2c\x69\x3d\x71\x2e\x67\x65\x74\x45\x6c\x65\
+\x6d\x65\x6e\x74\x73\x42\x79\x54\x61\x67\x4e\x61\x6d\x65\x28\x22\
+\x69\x6e\x70\x75\x74\x22\x29\x5b\x30\x5d\x2c\x62\x3d\x7b\x6c\x65\
+\x61\x64\x69\x6e\x67\x57\x68\x69\x74\x65\x73\x70\x61\x63\x65\x3a\
+\x71\x2e\x66\x69\x72\x73\x74\x43\x68\x69\x6c\x64\x2e\x6e\x6f\x64\
+\x65\x54\x79\x70\x65\x3d\x3d\x3d\x33\x2c\x74\x62\x6f\x64\x79\x3a\
+\x21\x71\x2e\x67\x65\x74\x45\x6c\x65\x6d\x65\x6e\x74\x73\x42\x79\
+\x54\x61\x67\x4e\x61\x6d\x65\x28\x22\x74\x62\x6f\x64\x79\x22\x29\
+\x2e\x6c\x65\x6e\x67\x74\x68\x2c\x68\x74\x6d\x6c\x53\x65\x72\x69\
+\x61\x6c\x69\x7a\x65\x3a\x21\x21\x71\x2e\x67\x65\x74\x45\x6c\x65\
+\x6d\x65\x6e\x74\x73\x42\x79\x54\x61\x67\x4e\x61\x6d\x65\x28\x22\
+\x6c\x69\x6e\x6b\x22\x29\x2e\x6c\x65\x6e\x67\x74\x68\x2c\x73\x74\
+\x79\x6c\x65\x3a\x2f\x74\x6f\x70\x2f\x2e\x74\x65\x73\x74\x28\x65\
+\x2e\x67\x65\x74\x41\x74\x74\x72\x69\x62\x75\x74\x65\x28\x22\x73\
+\x74\x79\x6c\x65\x22\x29\x29\x2c\x68\x72\x65\x66\x4e\x6f\x72\x6d\
+\x61\x6c\x69\x7a\x65\x64\x3a\x65\x2e\x67\x65\x74\x41\x74\x74\x72\
+\x69\x62\x75\x74\x65\x28\x22\x68\x72\x65\x66\x22\x29\x3d\x3d\x3d\
+\x22\x2f\x61\x22\x2c\x6f\x70\x61\x63\x69\x74\x79\x3a\x2f\x5e\x30\
+\x2e\x35\x35\x2f\x2e\x74\x65\x73\x74\x28\x65\x2e\x73\x74\x79\x6c\
+\x65\x2e\x6f\x70\x61\x63\x69\x74\x79\x29\x2c\x63\x73\x73\x46\x6c\
+\x6f\x61\x74\x3a\x21\x21\x65\x2e\x73\x74\x79\x6c\x65\x2e\x63\x73\
+\x73\x46\x6c\x6f\x61\x74\x2c\x63\x68\x65\x63\x6b\x4f\x6e\x3a\x69\
+\x2e\x76\x61\x6c\x75\x65\x3d\x3d\x3d\x22\x6f\x6e\x22\x2c\x6f\x70\
+\x74\x53\x65\x6c\x65\x63\x74\x65\x64\x3a\x68\x2e\x73\x65\x6c\x65\
+\x63\x74\x65\x64\x2c\x67\x65\x74\x53\x65\x74\x41\x74\x74\x72\x69\
+\x62\x75\x74\x65\x3a\x71\x2e\x63\x6c\x61\x73\x73\x4e\x61\x6d\x65\
+\x21\x3d\x3d\x22\x74\x22\x2c\x65\x6e\x63\x74\x79\x70\x65\x3a\x21\
+\x21\x63\x2e\x63\x72\x65\x61\x74\x65\x45\x6c\x65\x6d\x65\x6e\x74\
+\x28\x22\x66\x6f\x72\x6d\x22\x29\x2e\x65\x6e\x63\x74\x79\x70\x65\
+\x2c\x68\x74\x6d\x6c\x35\x43\x6c\x6f\x6e\x65\x3a\x63\x2e\x63\x72\
+\x65\x61\x74\x65\x45\x6c\x65\x6d\x65\x6e\x74\x28\x22\x6e\x61\x76\
+\x22\x29\x2e\x63\x6c\x6f\x6e\x65\x4e\x6f\x64\x65\x28\x21\x30\x29\
+\x2e\x6f\x75\x74\x65\x72\x48\x54\x4d\x4c\x21\x3d\x3d\x22\x3c\x3a\
+\x6e\x61\x76\x3e\x3c\x2f\x3a\x6e\x61\x76\x3e\x22\x2c\x73\x75\x62\
+\x6d\x69\x74\x42\x75\x62\x62\x6c\x65\x73\x3a\x21\x30\x2c\x63\x68\
+\x61\x6e\x67\x65\x42\x75\x62\x62\x6c\x65\x73\x3a\x21\x30\x2c\x66\
+\x6f\x63\x75\x73\x69\x6e\x42\x75\x62\x62\x6c\x65\x73\x3a\x21\x31\
+\x2c\x64\x65\x6c\x65\x74\x65\x45\x78\x70\x61\x6e\x64\x6f\x3a\x21\
+\x30\x2c\x6e\x6f\x43\x6c\x6f\x6e\x65\x45\x76\x65\x6e\x74\x3a\x21\
+\x30\x2c\x69\x6e\x6c\x69\x6e\x65\x42\x6c\x6f\x63\x6b\x4e\x65\x65\
+\x64\x73\x4c\x61\x79\x6f\x75\x74\x3a\x21\x31\x2c\x73\x68\x72\x69\
+\x6e\x6b\x57\x72\x61\x70\x42\x6c\x6f\x63\x6b\x73\x3a\x21\x31\x2c\
+\x72\x65\x6c\x69\x61\x62\x6c\x65\x4d\x61\x72\x67\x69\x6e\x52\x69\
+\x67\x68\x74\x3a\x21\x30\x7d\x2c\x69\x2e\x63\x68\x65\x63\x6b\x65\
+\x64\x3d\x21\x30\x2c\x62\x2e\x6e\x6f\x43\x6c\x6f\x6e\x65\x43\x68\
+\x65\x63\x6b\x65\x64\x3d\x69\x2e\x63\x6c\x6f\x6e\x65\x4e\x6f\x64\
+\x65\x28\x21\x30\x29\x2e\x63\x68\x65\x63\x6b\x65\x64\x2c\x67\x2e\
+\x64\x69\x73\x61\x62\x6c\x65\x64\x3d\x21\x30\x2c\x62\x2e\x6f\x70\
+\x74\x44\x69\x73\x61\x62\x6c\x65\x64\x3d\x21\x68\x2e\x64\x69\x73\
+\x61\x62\x6c\x65\x64\x3b\x74\x72\x79\x7b\x64\x65\x6c\x65\x74\x65\
+\x20\x71\x2e\x74\x65\x73\x74\x7d\x63\x61\x74\x63\x68\x28\x73\x29\
+\x7b\x62\x2e\x64\x65\x6c\x65\x74\x65\x45\x78\x70\x61\x6e\x64\x6f\
+\x3d\x21\x31\x7d\x21\x71\x2e\x61\x64\x64\x45\x76\x65\x6e\x74\x4c\
+\x69\x73\x74\x65\x6e\x65\x72\x26\x26\x71\x2e\x61\x74\x74\x61\x63\
+\x68\x45\x76\x65\x6e\x74\x26\x26\x71\x2e\x66\x69\x72\x65\x45\x76\
+\x65\x6e\x74\x26\x26\x28\x71\x2e\x61\x74\x74\x61\x63\x68\x45\x76\
+\x65\x6e\x74\x28\x22\x6f\x6e\x63\x6c\x69\x63\x6b\x22\x2c\x66\x75\
+\x6e\x63\x74\x69\x6f\x6e\x28\x29\x7b\x62\x2e\x6e\x6f\x43\x6c\x6f\
+\x6e\x65\x45\x76\x65\x6e\x74\x3d\x21\x31\x7d\x29\x2c\x71\x2e\x63\
+\x6c\x6f\x6e\x65\x4e\x6f\x64\x65\x28\x21\x30\x29\x2e\x66\x69\x72\
+\x65\x45\x76\x65\x6e\x74\x28\x22\x6f\x6e\x63\x6c\x69\x63\x6b\x22\
+\x29\x29\x2c\x69\x3d\x63\x2e\x63\x72\x65\x61\x74\x65\x45\x6c\x65\
+\x6d\x65\x6e\x74\x28\x22\x69\x6e\x70\x75\x74\x22\x29\x2c\x69\x2e\
+\x76\x61\x6c\x75\x65\x3d\x22\x74\x22\x2c\x69\x2e\x73\x65\x74\x41\
+\x74\x74\x72\x69\x62\x75\x74\x65\x28\x22\x74\x79\x70\x65\x22\x2c\
+\x22\x72\x61\x64\x69\x6f\x22\x29\x2c\x62\x2e\x72\x61\x64\x69\x6f\
+\x56\x61\x6c\x75\x65\x3d\x69\x2e\x76\x61\x6c\x75\x65\x3d\x3d\x3d\
+\x22\x74\x22\x2c\x69\x2e\x73\x65\x74\x41\x74\x74\x72\x69\x62\x75\
+\x74\x65\x28\x22\x63\x68\x65\x63\x6b\x65\x64\x22\x2c\x22\x63\x68\
+\x65\x63\x6b\x65\x64\x22\x29\x2c\x71\x2e\x61\x70\x70\x65\x6e\x64\
+\x43\x68\x69\x6c\x64\x28\x69\x29\x2c\x6b\x3d\x63\x2e\x63\x72\x65\
+\x61\x74\x65\x44\x6f\x63\x75\x6d\x65\x6e\x74\x46\x72\x61\x67\x6d\
+\x65\x6e\x74\x28\x29\x2c\x6b\x2e\x61\x70\x70\x65\x6e\x64\x43\x68\
+\x69\x6c\x64\x28\x71\x2e\x6c\x61\x73\x74\x43\x68\x69\x6c\x64\x29\
+\x2c\x62\x2e\x63\x68\x65\x63\x6b\x43\x6c\x6f\x6e\x65\x3d\x6b\x2e\
+\x63\x6c\x6f\x6e\x65\x4e\x6f\x64\x65\x28\x21\x30\x29\x2e\x63\x6c\
+\x6f\x6e\x65\x4e\x6f\x64\x65\x28\x21\x30\x29\x2e\x6c\x61\x73\x74\
+\x43\x68\x69\x6c\x64\x2e\x63\x68\x65\x63\x6b\x65\x64\x2c\x62\x2e\
+\x61\x70\x70\x65\x6e\x64\x43\x68\x65\x63\x6b\x65\x64\x3d\x69\x2e\
+\x63\x68\x65\x63\x6b\x65\x64\x2c\x6b\x2e\x72\x65\x6d\x6f\x76\x65\
+\x43\x68\x69\x6c\x64\x28\x69\x29\x2c\x6b\x2e\x61\x70\x70\x65\x6e\
+\x64\x43\x68\x69\x6c\x64\x28\x71\x29\x2c\x71\x2e\x69\x6e\x6e\x65\
+\x72\x48\x54\x4d\x4c\x3d\x22\x22\x2c\x61\x2e\x67\x65\x74\x43\x6f\
+\x6d\x70\x75\x74\x65\x64\x53\x74\x79\x6c\x65\x26\x26\x28\x6a\x3d\
+\x63\x2e\x63\x72\x65\x61\x74\x65\x45\x6c\x65\x6d\x65\x6e\x74\x28\
+\x22\x64\x69\x76\x22\x29\x2c\x6a\x2e\x73\x74\x79\x6c\x65\x2e\x77\
+\x69\x64\x74\x68\x3d\x22\x30\x22\x2c\x6a\x2e\x73\x74\x79\x6c\x65\
+\x2e\x6d\x61\x72\x67\x69\x6e\x52\x69\x67\x68\x74\x3d\x22\x30\x22\
+\x2c\x71\x2e\x73\x74\x79\x6c\x65\x2e\x77\x69\x64\x74\x68\x3d\x22\
+\x32\x70\x78\x22\x2c\x71\x2e\x61\x70\x70\x65\x6e\x64\x43\x68\x69\
+\x6c\x64\x28\x6a\x29\x2c\x62\x2e\x72\x65\x6c\x69\x61\x62\x6c\x65\
+\x4d\x61\x72\x67\x69\x6e\x52\x69\x67\x68\x74\x3d\x28\x70\x61\x72\
+\x73\x65\x49\x6e\x74\x28\x28\x61\x2e\x67\x65\x74\x43\x6f\x6d\x70\
+\x75\x74\x65\x64\x53\x74\x79\x6c\x65\x28\x6a\x2c\x6e\x75\x6c\x6c\
+\x29\x7c\x7c\x7b\x6d\x61\x72\x67\x69\x6e\x52\x69\x67\x68\x74\x3a\
+\x30\x7d\x29\x2e\x6d\x61\x72\x67\x69\x6e\x52\x69\x67\x68\x74\x2c\
+\x31\x30\x29\x7c\x7c\x30\x29\x3d\x3d\x3d\x30\x29\x3b\x69\x66\x28\
+\x71\x2e\x61\x74\x74\x61\x63\x68\x45\x76\x65\x6e\x74\x29\x66\x6f\
+\x72\x28\x6f\x20\x69\x6e\x7b\x73\x75\x62\x6d\x69\x74\x3a\x31\x2c\
+\x63\x68\x61\x6e\x67\x65\x3a\x31\x2c\x66\x6f\x63\x75\x73\x69\x6e\
+\x3a\x31\x7d\x29\x6e\x3d\x22\x6f\x6e\x22\x2b\x6f\x2c\x70\x3d\x6e\
+\x20\x69\x6e\x20\x71\x2c\x70\x7c\x7c\x28\x71\x2e\x73\x65\x74\x41\
+\x74\x74\x72\x69\x62\x75\x74\x65\x28\x6e\x2c\x22\x72\x65\x74\x75\
+\x72\x6e\x3b\x22\x29\x2c\x70\x3d\x74\x79\x70\x65\x6f\x66\x20\x71\
+\x5b\x6e\x5d\x3d\x3d\x22\x66\x75\x6e\x63\x74\x69\x6f\x6e\x22\x29\
+\x2c\x62\x5b\x6f\x2b\x22\x42\x75\x62\x62\x6c\x65\x73\x22\x5d\x3d\
+\x70\x3b\x6b\x2e\x72\x65\x6d\x6f\x76\x65\x43\x68\x69\x6c\x64\x28\
+\x71\x29\x2c\x6b\x3d\x67\x3d\x68\x3d\x6a\x3d\x71\x3d\x69\x3d\x6e\
+\x75\x6c\x6c\x2c\x66\x28\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x29\
+\x7b\x76\x61\x72\x20\x61\x2c\x64\x2c\x65\x2c\x67\x2c\x68\x2c\x69\
+\x2c\x6a\x2c\x6b\x2c\x6d\x2c\x6e\x2c\x6f\x2c\x72\x3d\x63\x2e\x67\
+\x65\x74\x45\x6c\x65\x6d\x65\x6e\x74\x73\x42\x79\x54\x61\x67\x4e\
+\x61\x6d\x65\x28\x22\x62\x6f\x64\x79\x22\x29\x5b\x30\x5d\x3b\x21\
+\x72\x7c\x7c\x28\x6a\x3d\x31\x2c\x6b\x3d\x22\x70\x6f\x73\x69\x74\
+\x69\x6f\x6e\x3a\x61\x62\x73\x6f\x6c\x75\x74\x65\x3b\x74\x6f\x70\
+\x3a\x30\x3b\x6c\x65\x66\x74\x3a\x30\x3b\x77\x69\x64\x74\x68\x3a\
+\x31\x70\x78\x3b\x68\x65\x69\x67\x68\x74\x3a\x31\x70\x78\x3b\x6d\
+\x61\x72\x67\x69\x6e\x3a\x30\x3b\x22\x2c\x6d\x3d\x22\x76\x69\x73\
+\x69\x62\x69\x6c\x69\x74\x79\x3a\x68\x69\x64\x64\x65\x6e\x3b\x62\
+\x6f\x72\x64\x65\x72\x3a\x30\x3b\x22\x2c\x6e\x3d\x22\x73\x74\x79\
+\x6c\x65\x3d\x27\x22\x2b\x6b\x2b\x22\x62\x6f\x72\x64\x65\x72\x3a\
+\x35\x70\x78\x20\x73\x6f\x6c\x69\x64\x20\x23\x30\x30\x30\x3b\x70\
+\x61\x64\x64\x69\x6e\x67\x3a\x30\x3b\x27\x22\x2c\x6f\x3d\x22\x3c\
+\x64\x69\x76\x20\x22\x2b\x6e\x2b\x22\x3e\x3c\x64\x69\x76\x3e\x3c\
+\x2f\x64\x69\x76\x3e\x3c\x2f\x64\x69\x76\x3e\x22\x2b\x22\x3c\x74\
+\x61\x62\x6c\x65\x20\x22\x2b\x6e\x2b\x22\x20\x63\x65\x6c\x6c\x70\
+\x61\x64\x64\x69\x6e\x67\x3d\x27\x30\x27\x20\x63\x65\x6c\x6c\x73\
+\x70\x61\x63\x69\x6e\x67\x3d\x27\x30\x27\x3e\x22\x2b\x22\x3c\x74\
+\x72\x3e\x3c\x74\x64\x3e\x3c\x2f\x74\x64\x3e\x3c\x2f\x74\x72\x3e\
+\x3c\x2f\x74\x61\x62\x6c\x65\x3e\x22\x2c\x61\x3d\x63\x2e\x63\x72\
+\x65\x61\x74\x65\x45\x6c\x65\x6d\x65\x6e\x74\x28\x22\x64\x69\x76\
+\x22\x29\x2c\x61\x2e\x73\x74\x79\x6c\x65\x2e\x63\x73\x73\x54\x65\
+\x78\x74\x3d\x6d\x2b\x22\x77\x69\x64\x74\x68\x3a\x30\x3b\x68\x65\
+\x69\x67\x68\x74\x3a\x30\x3b\x70\x6f\x73\x69\x74\x69\x6f\x6e\x3a\
+\x73\x74\x61\x74\x69\x63\x3b\x74\x6f\x70\x3a\x30\x3b\x6d\x61\x72\
+\x67\x69\x6e\x2d\x74\x6f\x70\x3a\x22\x2b\x6a\x2b\x22\x70\x78\x22\
+\x2c\x72\x2e\x69\x6e\x73\x65\x72\x74\x42\x65\x66\x6f\x72\x65\x28\
+\x61\x2c\x72\x2e\x66\x69\x72\x73\x74\x43\x68\x69\x6c\x64\x29\x2c\
+\x71\x3d\x63\x2e\x63\x72\x65\x61\x74\x65\x45\x6c\x65\x6d\x65\x6e\
+\x74\x28\x22\x64\x69\x76\x22\x29\x2c\x61\x2e\x61\x70\x70\x65\x6e\
+\x64\x43\x68\x69\x6c\x64\x28\x71\x29\x2c\x71\x2e\x69\x6e\x6e\x65\
+\x72\x48\x54\x4d\x4c\x3d\x22\x3c\x74\x61\x62\x6c\x65\x3e\x3c\x74\
+\x72\x3e\x3c\x74\x64\x20\x73\x74\x79\x6c\x65\x3d\x27\x70\x61\x64\
+\x64\x69\x6e\x67\x3a\x30\x3b\x62\x6f\x72\x64\x65\x72\x3a\x30\x3b\
+\x64\x69\x73\x70\x6c\x61\x79\x3a\x6e\x6f\x6e\x65\x27\x3e\x3c\x2f\
+\x74\x64\x3e\x3c\x74\x64\x3e\x74\x3c\x2f\x74\x64\x3e\x3c\x2f\x74\
+\x72\x3e\x3c\x2f\x74\x61\x62\x6c\x65\x3e\x22\x2c\x6c\x3d\x71\x2e\
+\x67\x65\x74\x45\x6c\x65\x6d\x65\x6e\x74\x73\x42\x79\x54\x61\x67\
+\x4e\x61\x6d\x65\x28\x22\x74\x64\x22\x29\x2c\x70\x3d\x6c\x5b\x30\
+\x5d\x2e\x6f\x66\x66\x73\x65\x74\x48\x65\x69\x67\x68\x74\x3d\x3d\
+\x3d\x30\x2c\x6c\x5b\x30\x5d\x2e\x73\x74\x79\x6c\x65\x2e\x64\x69\
+\x73\x70\x6c\x61\x79\x3d\x22\x22\x2c\x6c\x5b\x31\x5d\x2e\x73\x74\
+\x79\x6c\x65\x2e\x64\x69\x73\x70\x6c\x61\x79\x3d\x22\x6e\x6f\x6e\
+\x65\x22\x2c\x62\x2e\x72\x65\x6c\x69\x61\x62\x6c\x65\x48\x69\x64\
+\x64\x65\x6e\x4f\x66\x66\x73\x65\x74\x73\x3d\x70\x26\x26\x6c\x5b\
+\x30\x5d\x2e\x6f\x66\x66\x73\x65\x74\x48\x65\x69\x67\x68\x74\x3d\
+\x3d\x3d\x30\x2c\x71\x2e\x69\x6e\x6e\x65\x72\x48\x54\x4d\x4c\x3d\
+\x22\x22\x2c\x71\x2e\x73\x74\x79\x6c\x65\x2e\x77\x69\x64\x74\x68\
+\x3d\x71\x2e\x73\x74\x79\x6c\x65\x2e\x70\x61\x64\x64\x69\x6e\x67\
+\x4c\x65\x66\x74\x3d\x22\x31\x70\x78\x22\x2c\x66\x2e\x62\x6f\x78\
+\x4d\x6f\x64\x65\x6c\x3d\x62\x2e\x62\x6f\x78\x4d\x6f\x64\x65\x6c\
+\x3d\x71\x2e\x6f\x66\x66\x73\x65\x74\x57\x69\x64\x74\x68\x3d\x3d\
+\x3d\x32\x2c\x74\x79\x70\x65\x6f\x66\x20\x71\x2e\x73\x74\x79\x6c\
+\x65\x2e\x7a\x6f\x6f\x6d\x21\x3d\x22\x75\x6e\x64\x65\x66\x69\x6e\
+\x65\x64\x22\x26\x26\x28\x71\x2e\x73\x74\x79\x6c\x65\x2e\x64\x69\
+\x73\x70\x6c\x61\x79\x3d\x22\x69\x6e\x6c\x69\x6e\x65\x22\x2c\x71\
+\x2e\x73\x74\x79\x6c\x65\x2e\x7a\x6f\x6f\x6d\x3d\x31\x2c\x62\x2e\
+\x69\x6e\x6c\x69\x6e\x65\x42\x6c\x6f\x63\x6b\x4e\x65\x65\x64\x73\
+\x4c\x61\x79\x6f\x75\x74\x3d\x71\x2e\x6f\x66\x66\x73\x65\x74\x57\
+\x69\x64\x74\x68\x3d\x3d\x3d\x32\x2c\x71\x2e\x73\x74\x79\x6c\x65\
+\x2e\x64\x69\x73\x70\x6c\x61\x79\x3d\x22\x22\x2c\x71\x2e\x69\x6e\
+\x6e\x65\x72\x48\x54\x4d\x4c\x3d\x22\x3c\x64\x69\x76\x20\x73\x74\
+\x79\x6c\x65\x3d\x27\x77\x69\x64\x74\x68\x3a\x34\x70\x78\x3b\x27\
+\x3e\x3c\x2f\x64\x69\x76\x3e\x22\x2c\x62\x2e\x73\x68\x72\x69\x6e\
+\x6b\x57\x72\x61\x70\x42\x6c\x6f\x63\x6b\x73\x3d\x71\x2e\x6f\x66\
+\x66\x73\x65\x74\x57\x69\x64\x74\x68\x21\x3d\x3d\x32\x29\x2c\x71\
+\x2e\x73\x74\x79\x6c\x65\x2e\x63\x73\x73\x54\x65\x78\x74\x3d\x6b\
+\x2b\x6d\x2c\x71\x2e\x69\x6e\x6e\x65\x72\x48\x54\x4d\x4c\x3d\x6f\
+\x2c\x64\x3d\x71\x2e\x66\x69\x72\x73\x74\x43\x68\x69\x6c\x64\x2c\
+\x65\x3d\x64\x2e\x66\x69\x72\x73\x74\x43\x68\x69\x6c\x64\x2c\x68\
+\x3d\x64\x2e\x6e\x65\x78\x74\x53\x69\x62\x6c\x69\x6e\x67\x2e\x66\
+\x69\x72\x73\x74\x43\x68\x69\x6c\x64\x2e\x66\x69\x72\x73\x74\x43\
+\x68\x69\x6c\x64\x2c\x69\x3d\x7b\x64\x6f\x65\x73\x4e\x6f\x74\x41\
+\x64\x64\x42\x6f\x72\x64\x65\x72\x3a\x65\x2e\x6f\x66\x66\x73\x65\
+\x74\x54\x6f\x70\x21\x3d\x3d\x35\x2c\x64\x6f\x65\x73\x41\x64\x64\
+\x42\x6f\x72\x64\x65\x72\x46\x6f\x72\x54\x61\x62\x6c\x65\x41\x6e\
+\x64\x43\x65\x6c\x6c\x73\x3a\x68\x2e\x6f\x66\x66\x73\x65\x74\x54\
+\x6f\x70\x3d\x3d\x3d\x35\x7d\x2c\x65\x2e\x73\x74\x79\x6c\x65\x2e\
+\x70\x6f\x73\x69\x74\x69\x6f\x6e\x3d\x22\x66\x69\x78\x65\x64\x22\
+\x2c\x65\x2e\x73\x74\x79\x6c\x65\x2e\x74\x6f\x70\x3d\x22\x32\x30\
+\x70\x78\x22\x2c\x69\x2e\x66\x69\x78\x65\x64\x50\x6f\x73\x69\x74\
+\x69\x6f\x6e\x3d\x65\x2e\x6f\x66\x66\x73\x65\x74\x54\x6f\x70\x3d\
+\x3d\x3d\x32\x30\x7c\x7c\x65\x2e\x6f\x66\x66\x73\x65\x74\x54\x6f\
+\x70\x3d\x3d\x3d\x31\x35\x2c\x65\x2e\x73\x74\x79\x6c\x65\x2e\x70\
+\x6f\x73\x69\x74\x69\x6f\x6e\x3d\x65\x2e\x73\x74\x79\x6c\x65\x2e\
+\x74\x6f\x70\x3d\x22\x22\x2c\x64\x2e\x73\x74\x79\x6c\x65\x2e\x6f\
+\x76\x65\x72\x66\x6c\x6f\x77\x3d\x22\x68\x69\x64\x64\x65\x6e\x22\
+\x2c\x64\x2e\x73\x74\x79\x6c\x65\x2e\x70\x6f\x73\x69\x74\x69\x6f\
+\x6e\x3d\x22\x72\x65\x6c\x61\x74\x69\x76\x65\x22\x2c\x69\x2e\x73\
+\x75\x62\x74\x72\x61\x63\x74\x73\x42\x6f\x72\x64\x65\x72\x46\x6f\
+\x72\x4f\x76\x65\x72\x66\x6c\x6f\x77\x4e\x6f\x74\x56\x69\x73\x69\
+\x62\x6c\x65\x3d\x65\x2e\x6f\x66\x66\x73\x65\x74\x54\x6f\x70\x3d\
+\x3d\x3d\x2d\x35\x2c\x69\x2e\x64\x6f\x65\x73\x4e\x6f\x74\x49\x6e\
+\x63\x6c\x75\x64\x65\x4d\x61\x72\x67\x69\x6e\x49\x6e\x42\x6f\x64\
+\x79\x4f\x66\x66\x73\x65\x74\x3d\x72\x2e\x6f\x66\x66\x73\x65\x74\
+\x54\x6f\x70\x21\x3d\x3d\x6a\x2c\x72\x2e\x72\x65\x6d\x6f\x76\x65\
+\x43\x68\x69\x6c\x64\x28\x61\x29\x2c\x71\x3d\x61\x3d\x6e\x75\x6c\
+\x6c\x2c\x66\x2e\x65\x78\x74\x65\x6e\x64\x28\x62\x2c\x69\x29\x29\
+\x7d\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x62\x7d\x28\x29\x3b\x76\
+\x61\x72\x20\x6a\x3d\x2f\x5e\x28\x3f\x3a\x5c\x7b\x2e\x2a\x5c\x7d\
+\x7c\x5c\x5b\x2e\x2a\x5c\x5d\x29\x24\x2f\x2c\x6b\x3d\x2f\x28\x5b\
+\x41\x2d\x5a\x5d\x29\x2f\x67\x3b\x66\x2e\x65\x78\x74\x65\x6e\x64\
+\x28\x7b\x63\x61\x63\x68\x65\x3a\x7b\x7d\x2c\x75\x75\x69\x64\x3a\
+\x30\x2c\x65\x78\x70\x61\x6e\x64\x6f\x3a\x22\x6a\x51\x75\x65\x72\
+\x79\x22\x2b\x28\x66\x2e\x66\x6e\x2e\x6a\x71\x75\x65\x72\x79\x2b\
+\x4d\x61\x74\x68\x2e\x72\x61\x6e\x64\x6f\x6d\x28\x29\x29\x2e\x72\
+\x65\x70\x6c\x61\x63\x65\x28\x2f\x5c\x44\x2f\x67\x2c\x22\x22\x29\
+\x2c\x6e\x6f\x44\x61\x74\x61\x3a\x7b\x65\x6d\x62\x65\x64\x3a\x21\
+\x30\x2c\x6f\x62\x6a\x65\x63\x74\x3a\x22\x63\x6c\x73\x69\x64\x3a\
+\x44\x32\x37\x43\x44\x42\x36\x45\x2d\x41\x45\x36\x44\x2d\x31\x31\
+\x63\x66\x2d\x39\x36\x42\x38\x2d\x34\x34\x34\x35\x35\x33\x35\x34\
+\x30\x30\x30\x30\x22\x2c\x61\x70\x70\x6c\x65\x74\x3a\x21\x30\x7d\
+\x2c\x68\x61\x73\x44\x61\x74\x61\x3a\x66\x75\x6e\x63\x74\x69\x6f\
+\x6e\x28\x61\x29\x7b\x61\x3d\x61\x2e\x6e\x6f\x64\x65\x54\x79\x70\
+\x65\x3f\x66\x2e\x63\x61\x63\x68\x65\x5b\x61\x5b\x66\x2e\x65\x78\
+\x70\x61\x6e\x64\x6f\x5d\x5d\x3a\x61\x5b\x66\x2e\x65\x78\x70\x61\
+\x6e\x64\x6f\x5d\x3b\x72\x65\x74\x75\x72\x6e\x21\x21\x61\x26\x26\
+\x21\x6d\x28\x61\x29\x7d\x2c\x64\x61\x74\x61\x3a\x66\x75\x6e\x63\
+\x74\x69\x6f\x6e\x28\x61\x2c\x63\x2c\x64\x2c\x65\x29\x7b\x69\x66\
+\x28\x21\x21\x66\x2e\x61\x63\x63\x65\x70\x74\x44\x61\x74\x61\x28\
+\x61\x29\x29\x7b\x76\x61\x72\x20\x67\x2c\x68\x2c\x69\x2c\x6a\x3d\
+\x66\x2e\x65\x78\x70\x61\x6e\x64\x6f\x2c\x6b\x3d\x74\x79\x70\x65\
+\x6f\x66\x20\x63\x3d\x3d\x22\x73\x74\x72\x69\x6e\x67\x22\x2c\x6c\
+\x3d\x61\x2e\x6e\x6f\x64\x65\x54\x79\x70\x65\x2c\x6d\x3d\x6c\x3f\
+\x66\x2e\x63\x61\x63\x68\x65\x3a\x61\x2c\x6e\x3d\x6c\x3f\x61\x5b\
+\x6a\x5d\x3a\x61\x5b\x6a\x5d\x26\x26\x6a\x2c\x6f\x3d\x63\x3d\x3d\
+\x3d\x22\x65\x76\x65\x6e\x74\x73\x22\x3b\x69\x66\x28\x28\x21\x6e\
+\x7c\x7c\x21\x6d\x5b\x6e\x5d\x7c\x7c\x21\x6f\x26\x26\x21\x65\x26\
+\x26\x21\x6d\x5b\x6e\x5d\x2e\x64\x61\x74\x61\x29\x26\x26\x6b\x26\
+\x26\x64\x3d\x3d\x3d\x62\x29\x72\x65\x74\x75\x72\x6e\x3b\x6e\x7c\
+\x7c\x28\x6c\x3f\x61\x5b\x6a\x5d\x3d\x6e\x3d\x2b\x2b\x66\x2e\x75\
+\x75\x69\x64\x3a\x6e\x3d\x6a\x29\x2c\x6d\x5b\x6e\x5d\x7c\x7c\x28\
+\x6d\x5b\x6e\x5d\x3d\x7b\x7d\x2c\x6c\x7c\x7c\x28\x6d\x5b\x6e\x5d\
+\x2e\x74\x6f\x4a\x53\x4f\x4e\x3d\x66\x2e\x6e\x6f\x6f\x70\x29\x29\
+\x3b\x69\x66\x28\x74\x79\x70\x65\x6f\x66\x20\x63\x3d\x3d\x22\x6f\
+\x62\x6a\x65\x63\x74\x22\x7c\x7c\x74\x79\x70\x65\x6f\x66\x20\x63\
+\x3d\x3d\x22\x66\x75\x6e\x63\x74\x69\x6f\x6e\x22\x29\x65\x3f\x6d\
+\x5b\x6e\x5d\x3d\x66\x2e\x65\x78\x74\x65\x6e\x64\x28\x6d\x5b\x6e\
+\x5d\x2c\x63\x29\x3a\x6d\x5b\x6e\x5d\x2e\x64\x61\x74\x61\x3d\x66\
+\x2e\x65\x78\x74\x65\x6e\x64\x28\x6d\x5b\x6e\x5d\x2e\x64\x61\x74\
+\x61\x2c\x63\x29\x3b\x67\x3d\x68\x3d\x6d\x5b\x6e\x5d\x2c\x65\x7c\
+\x7c\x28\x68\x2e\x64\x61\x74\x61\x7c\x7c\x28\x68\x2e\x64\x61\x74\
+\x61\x3d\x7b\x7d\x29\x2c\x68\x3d\x68\x2e\x64\x61\x74\x61\x29\x2c\
+\x64\x21\x3d\x3d\x62\x26\x26\x28\x68\x5b\x66\x2e\x63\x61\x6d\x65\
+\x6c\x43\x61\x73\x65\x28\x63\x29\x5d\x3d\x64\x29\x3b\x69\x66\x28\
+\x6f\x26\x26\x21\x68\x5b\x63\x5d\x29\x72\x65\x74\x75\x72\x6e\x20\
+\x67\x2e\x65\x76\x65\x6e\x74\x73\x3b\x6b\x3f\x28\x69\x3d\x68\x5b\
+\x63\x5d\x2c\x69\x3d\x3d\x6e\x75\x6c\x6c\x26\x26\x28\x69\x3d\x68\
+\x5b\x66\x2e\x63\x61\x6d\x65\x6c\x43\x61\x73\x65\x28\x63\x29\x5d\
+\x29\x29\x3a\x69\x3d\x68\x3b\x72\x65\x74\x75\x72\x6e\x20\x69\x7d\
+\x7d\x2c\x72\x65\x6d\x6f\x76\x65\x44\x61\x74\x61\x3a\x66\x75\x6e\
+\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x2c\x63\x29\x7b\x69\x66\x28\
+\x21\x21\x66\x2e\x61\x63\x63\x65\x70\x74\x44\x61\x74\x61\x28\x61\
+\x29\x29\x7b\x76\x61\x72\x20\x64\x2c\x65\x2c\x67\x2c\x68\x3d\x66\
+\x2e\x65\x78\x70\x61\x6e\x64\x6f\x2c\x69\x3d\x61\x2e\x6e\x6f\x64\
+\x65\x54\x79\x70\x65\x2c\x6a\x3d\x69\x3f\x66\x2e\x63\x61\x63\x68\
+\x65\x3a\x61\x2c\x6b\x3d\x69\x3f\x61\x5b\x68\x5d\x3a\x68\x3b\x69\
+\x66\x28\x21\x6a\x5b\x6b\x5d\x29\x72\x65\x74\x75\x72\x6e\x3b\x69\
+\x66\x28\x62\x29\x7b\x64\x3d\x63\x3f\x6a\x5b\x6b\x5d\x3a\x6a\x5b\
+\x6b\x5d\x2e\x64\x61\x74\x61\x3b\x69\x66\x28\x64\x29\x7b\x66\x2e\
+\x69\x73\x41\x72\x72\x61\x79\x28\x62\x29\x7c\x7c\x28\x62\x20\x69\
+\x6e\x20\x64\x3f\x62\x3d\x5b\x62\x5d\x3a\x28\x62\x3d\x66\x2e\x63\
+\x61\x6d\x65\x6c\x43\x61\x73\x65\x28\x62\x29\x2c\x62\x20\x69\x6e\
+\x20\x64\x3f\x62\x3d\x5b\x62\x5d\x3a\x62\x3d\x62\x2e\x73\x70\x6c\
+\x69\x74\x28\x22\x20\x22\x29\x29\x29\x3b\x66\x6f\x72\x28\x65\x3d\
+\x30\x2c\x67\x3d\x62\x2e\x6c\x65\x6e\x67\x74\x68\x3b\x65\x3c\x67\
+\x3b\x65\x2b\x2b\x29\x64\x65\x6c\x65\x74\x65\x20\x64\x5b\x62\x5b\
+\x65\x5d\x5d\x3b\x69\x66\x28\x21\x28\x63\x3f\x6d\x3a\x66\x2e\x69\
+\x73\x45\x6d\x70\x74\x79\x4f\x62\x6a\x65\x63\x74\x29\x28\x64\x29\
+\x29\x72\x65\x74\x75\x72\x6e\x7d\x7d\x69\x66\x28\x21\x63\x29\x7b\
+\x64\x65\x6c\x65\x74\x65\x20\x6a\x5b\x6b\x5d\x2e\x64\x61\x74\x61\
+\x3b\x69\x66\x28\x21\x6d\x28\x6a\x5b\x6b\x5d\x29\x29\x72\x65\x74\
+\x75\x72\x6e\x7d\x66\x2e\x73\x75\x70\x70\x6f\x72\x74\x2e\x64\x65\
+\x6c\x65\x74\x65\x45\x78\x70\x61\x6e\x64\x6f\x7c\x7c\x21\x6a\x2e\
+\x73\x65\x74\x49\x6e\x74\x65\x72\x76\x61\x6c\x3f\x64\x65\x6c\x65\
+\x74\x65\x20\x6a\x5b\x6b\x5d\x3a\x6a\x5b\x6b\x5d\x3d\x6e\x75\x6c\
+\x6c\x2c\x69\x26\x26\x28\x66\x2e\x73\x75\x70\x70\x6f\x72\x74\x2e\
+\x64\x65\x6c\x65\x74\x65\x45\x78\x70\x61\x6e\x64\x6f\x3f\x64\x65\
+\x6c\x65\x74\x65\x20\x61\x5b\x68\x5d\x3a\x61\x2e\x72\x65\x6d\x6f\
+\x76\x65\x41\x74\x74\x72\x69\x62\x75\x74\x65\x3f\x61\x2e\x72\x65\
+\x6d\x6f\x76\x65\x41\x74\x74\x72\x69\x62\x75\x74\x65\x28\x68\x29\
+\x3a\x61\x5b\x68\x5d\x3d\x6e\x75\x6c\x6c\x29\x7d\x7d\x2c\x5f\x64\
+\x61\x74\x61\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\
+\x2c\x63\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x66\x2e\x64\x61\x74\
+\x61\x28\x61\x2c\x62\x2c\x63\x2c\x21\x30\x29\x7d\x2c\x61\x63\x63\
+\x65\x70\x74\x44\x61\x74\x61\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\
+\x28\x61\x29\x7b\x69\x66\x28\x61\x2e\x6e\x6f\x64\x65\x4e\x61\x6d\
+\x65\x29\x7b\x76\x61\x72\x20\x62\x3d\x66\x2e\x6e\x6f\x44\x61\x74\
+\x61\x5b\x61\x2e\x6e\x6f\x64\x65\x4e\x61\x6d\x65\x2e\x74\x6f\x4c\
+\x6f\x77\x65\x72\x43\x61\x73\x65\x28\x29\x5d\x3b\x69\x66\x28\x62\
+\x29\x72\x65\x74\x75\x72\x6e\x20\x62\x21\x3d\x3d\x21\x30\x26\x26\
+\x61\x2e\x67\x65\x74\x41\x74\x74\x72\x69\x62\x75\x74\x65\x28\x22\
+\x63\x6c\x61\x73\x73\x69\x64\x22\x29\x3d\x3d\x3d\x62\x7d\x72\x65\
+\x74\x75\x72\x6e\x21\x30\x7d\x7d\x29\x2c\x66\x2e\x66\x6e\x2e\x65\
+\x78\x74\x65\x6e\x64\x28\x7b\x64\x61\x74\x61\x3a\x66\x75\x6e\x63\
+\x74\x69\x6f\x6e\x28\x61\x2c\x63\x29\x7b\x76\x61\x72\x20\x64\x2c\
+\x65\x2c\x67\x2c\x68\x3d\x6e\x75\x6c\x6c\x3b\x69\x66\x28\x74\x79\
+\x70\x65\x6f\x66\x20\x61\x3d\x3d\x22\x75\x6e\x64\x65\x66\x69\x6e\
+\x65\x64\x22\x29\x7b\x69\x66\x28\x74\x68\x69\x73\x2e\x6c\x65\x6e\
+\x67\x74\x68\x29\x7b\x68\x3d\x66\x2e\x64\x61\x74\x61\x28\x74\x68\
+\x69\x73\x5b\x30\x5d\x29\x3b\x69\x66\x28\x74\x68\x69\x73\x5b\x30\
+\x5d\x2e\x6e\x6f\x64\x65\x54\x79\x70\x65\x3d\x3d\x3d\x31\x26\x26\
+\x21\x66\x2e\x5f\x64\x61\x74\x61\x28\x74\x68\x69\x73\x5b\x30\x5d\
+\x2c\x22\x70\x61\x72\x73\x65\x64\x41\x74\x74\x72\x73\x22\x29\x29\
+\x7b\x65\x3d\x74\x68\x69\x73\x5b\x30\x5d\x2e\x61\x74\x74\x72\x69\
+\x62\x75\x74\x65\x73\x3b\x66\x6f\x72\x28\x76\x61\x72\x20\x69\x3d\
+\x30\x2c\x6a\x3d\x65\x2e\x6c\x65\x6e\x67\x74\x68\x3b\x69\x3c\x6a\
+\x3b\x69\x2b\x2b\x29\x67\x3d\x65\x5b\x69\x5d\x2e\x6e\x61\x6d\x65\
+\x2c\x67\x2e\x69\x6e\x64\x65\x78\x4f\x66\x28\x22\x64\x61\x74\x61\
+\x2d\x22\x29\x3d\x3d\x3d\x30\x26\x26\x28\x67\x3d\x66\x2e\x63\x61\
+\x6d\x65\x6c\x43\x61\x73\x65\x28\x67\x2e\x73\x75\x62\x73\x74\x72\
+\x69\x6e\x67\x28\x35\x29\x29\x2c\x6c\x28\x74\x68\x69\x73\x5b\x30\
+\x5d\x2c\x67\x2c\x68\x5b\x67\x5d\x29\x29\x3b\x66\x2e\x5f\x64\x61\
+\x74\x61\x28\x74\x68\x69\x73\x5b\x30\x5d\x2c\x22\x70\x61\x72\x73\
+\x65\x64\x41\x74\x74\x72\x73\x22\x2c\x21\x30\x29\x7d\x7d\x72\x65\
+\x74\x75\x72\x6e\x20\x68\x7d\x69\x66\x28\x74\x79\x70\x65\x6f\x66\
+\x20\x61\x3d\x3d\x22\x6f\x62\x6a\x65\x63\x74\x22\x29\x72\x65\x74\
+\x75\x72\x6e\x20\x74\x68\x69\x73\x2e\x65\x61\x63\x68\x28\x66\x75\
+\x6e\x63\x74\x69\x6f\x6e\x28\x29\x7b\x66\x2e\x64\x61\x74\x61\x28\
+\x74\x68\x69\x73\x2c\x61\x29\x7d\x29\x3b\x64\x3d\x61\x2e\x73\x70\
+\x6c\x69\x74\x28\x22\x2e\x22\x29\x2c\x64\x5b\x31\x5d\x3d\x64\x5b\
+\x31\x5d\x3f\x22\x2e\x22\x2b\x64\x5b\x31\x5d\x3a\x22\x22\x3b\x69\
+\x66\x28\x63\x3d\x3d\x3d\x62\x29\x7b\x68\x3d\x74\x68\x69\x73\x2e\
+\x74\x72\x69\x67\x67\x65\x72\x48\x61\x6e\x64\x6c\x65\x72\x28\x22\
+\x67\x65\x74\x44\x61\x74\x61\x22\x2b\x64\x5b\x31\x5d\x2b\x22\x21\
+\x22\x2c\x5b\x64\x5b\x30\x5d\x5d\x29\x2c\x68\x3d\x3d\x3d\x62\x26\
+\x26\x74\x68\x69\x73\x2e\x6c\x65\x6e\x67\x74\x68\x26\x26\x28\x68\
+\x3d\x66\x2e\x64\x61\x74\x61\x28\x74\x68\x69\x73\x5b\x30\x5d\x2c\
+\x61\x29\x2c\x68\x3d\x6c\x28\x74\x68\x69\x73\x5b\x30\x5d\x2c\x61\
+\x2c\x68\x29\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x68\x3d\x3d\x3d\
+\x62\x26\x26\x64\x5b\x31\x5d\x3f\x74\x68\x69\x73\x2e\x64\x61\x74\
+\x61\x28\x64\x5b\x30\x5d\x29\x3a\x68\x7d\x72\x65\x74\x75\x72\x6e\
+\x20\x74\x68\x69\x73\x2e\x65\x61\x63\x68\x28\x66\x75\x6e\x63\x74\
+\x69\x6f\x6e\x28\x29\x7b\x76\x61\x72\x20\x62\x3d\x66\x28\x74\x68\
+\x69\x73\x29\x2c\x65\x3d\x5b\x64\x5b\x30\x5d\x2c\x63\x5d\x3b\x62\
+\x2e\x74\x72\x69\x67\x67\x65\x72\x48\x61\x6e\x64\x6c\x65\x72\x28\
+\x22\x73\x65\x74\x44\x61\x74\x61\x22\x2b\x64\x5b\x31\x5d\x2b\x22\
+\x21\x22\x2c\x65\x29\x2c\x66\x2e\x64\x61\x74\x61\x28\x74\x68\x69\
+\x73\x2c\x61\x2c\x63\x29\x2c\x62\x2e\x74\x72\x69\x67\x67\x65\x72\
+\x48\x61\x6e\x64\x6c\x65\x72\x28\x22\x63\x68\x61\x6e\x67\x65\x44\
+\x61\x74\x61\x22\x2b\x64\x5b\x31\x5d\x2b\x22\x21\x22\x2c\x65\x29\
+\x7d\x29\x7d\x2c\x72\x65\x6d\x6f\x76\x65\x44\x61\x74\x61\x3a\x66\
+\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x72\x65\x74\x75\x72\
+\x6e\x20\x74\x68\x69\x73\x2e\x65\x61\x63\x68\x28\x66\x75\x6e\x63\
+\x74\x69\x6f\x6e\x28\x29\x7b\x66\x2e\x72\x65\x6d\x6f\x76\x65\x44\
+\x61\x74\x61\x28\x74\x68\x69\x73\x2c\x61\x29\x7d\x29\x7d\x7d\x29\
+\x2c\x66\x2e\x65\x78\x74\x65\x6e\x64\x28\x7b\x5f\x6d\x61\x72\x6b\
+\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x29\x7b\x61\
+\x26\x26\x28\x62\x3d\x28\x62\x7c\x7c\x22\x66\x78\x22\x29\x2b\x22\
+\x6d\x61\x72\x6b\x22\x2c\x66\x2e\x5f\x64\x61\x74\x61\x28\x61\x2c\
+\x62\x2c\x28\x66\x2e\x5f\x64\x61\x74\x61\x28\x61\x2c\x62\x29\x7c\
+\x7c\x30\x29\x2b\x31\x29\x29\x7d\x2c\x5f\x75\x6e\x6d\x61\x72\x6b\
+\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x2c\x63\x29\
+\x7b\x61\x21\x3d\x3d\x21\x30\x26\x26\x28\x63\x3d\x62\x2c\x62\x3d\
+\x61\x2c\x61\x3d\x21\x31\x29\x3b\x69\x66\x28\x62\x29\x7b\x63\x3d\
+\x63\x7c\x7c\x22\x66\x78\x22\x3b\x76\x61\x72\x20\x64\x3d\x63\x2b\
+\x22\x6d\x61\x72\x6b\x22\x2c\x65\x3d\x61\x3f\x30\x3a\x28\x66\x2e\
+\x5f\x64\x61\x74\x61\x28\x62\x2c\x64\x29\x7c\x7c\x31\x29\x2d\x31\
+\x3b\x65\x3f\x66\x2e\x5f\x64\x61\x74\x61\x28\x62\x2c\x64\x2c\x65\
+\x29\x3a\x28\x66\x2e\x72\x65\x6d\x6f\x76\x65\x44\x61\x74\x61\x28\
+\x62\x2c\x64\x2c\x21\x30\x29\x2c\x6e\x28\x62\x2c\x63\x2c\x22\x6d\
+\x61\x72\x6b\x22\x29\x29\x7d\x7d\x2c\x71\x75\x65\x75\x65\x3a\x66\
+\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x2c\x63\x29\x7b\x76\
+\x61\x72\x20\x64\x3b\x69\x66\x28\x61\x29\x7b\x62\x3d\x28\x62\x7c\
+\x7c\x22\x66\x78\x22\x29\x2b\x22\x71\x75\x65\x75\x65\x22\x2c\x64\
+\x3d\x66\x2e\x5f\x64\x61\x74\x61\x28\x61\x2c\x62\x29\x2c\x63\x26\
+\x26\x28\x21\x64\x7c\x7c\x66\x2e\x69\x73\x41\x72\x72\x61\x79\x28\
+\x63\x29\x3f\x64\x3d\x66\x2e\x5f\x64\x61\x74\x61\x28\x61\x2c\x62\
+\x2c\x66\x2e\x6d\x61\x6b\x65\x41\x72\x72\x61\x79\x28\x63\x29\x29\
+\x3a\x64\x2e\x70\x75\x73\x68\x28\x63\x29\x29\x3b\x72\x65\x74\x75\
+\x72\x6e\x20\x64\x7c\x7c\x5b\x5d\x7d\x7d\x2c\x64\x65\x71\x75\x65\
+\x75\x65\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x29\
+\x7b\x62\x3d\x62\x7c\x7c\x22\x66\x78\x22\x3b\x76\x61\x72\x20\x63\
+\x3d\x66\x2e\x71\x75\x65\x75\x65\x28\x61\x2c\x62\x29\x2c\x64\x3d\
+\x63\x2e\x73\x68\x69\x66\x74\x28\x29\x2c\x65\x3d\x7b\x7d\x3b\x64\
+\x3d\x3d\x3d\x22\x69\x6e\x70\x72\x6f\x67\x72\x65\x73\x73\x22\x26\
+\x26\x28\x64\x3d\x63\x2e\x73\x68\x69\x66\x74\x28\x29\x29\x2c\x64\
+\x26\x26\x28\x62\x3d\x3d\x3d\x22\x66\x78\x22\x26\x26\x63\x2e\x75\
+\x6e\x73\x68\x69\x66\x74\x28\x22\x69\x6e\x70\x72\x6f\x67\x72\x65\
+\x73\x73\x22\x29\x2c\x66\x2e\x5f\x64\x61\x74\x61\x28\x61\x2c\x62\
+\x2b\x22\x2e\x72\x75\x6e\x22\x2c\x65\x29\x2c\x64\x2e\x63\x61\x6c\
+\x6c\x28\x61\x2c\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x29\x7b\x66\
+\x2e\x64\x65\x71\x75\x65\x75\x65\x28\x61\x2c\x62\x29\x7d\x2c\x65\
+\x29\x29\x2c\x63\x2e\x6c\x65\x6e\x67\x74\x68\x7c\x7c\x28\x66\x2e\
+\x72\x65\x6d\x6f\x76\x65\x44\x61\x74\x61\x28\x61\x2c\x62\x2b\x22\
+\x71\x75\x65\x75\x65\x20\x22\x2b\x62\x2b\x22\x2e\x72\x75\x6e\x22\
+\x2c\x21\x30\x29\x2c\x6e\x28\x61\x2c\x62\x2c\x22\x71\x75\x65\x75\
+\x65\x22\x29\x29\x7d\x7d\x29\x2c\x66\x2e\x66\x6e\x2e\x65\x78\x74\
+\x65\x6e\x64\x28\x7b\x71\x75\x65\x75\x65\x3a\x66\x75\x6e\x63\x74\
+\x69\x6f\x6e\x28\x61\x2c\x63\x29\x7b\x74\x79\x70\x65\x6f\x66\x20\
+\x61\x21\x3d\x22\x73\x74\x72\x69\x6e\x67\x22\x26\x26\x28\x63\x3d\
+\x61\x2c\x61\x3d\x22\x66\x78\x22\x29\x3b\x69\x66\x28\x63\x3d\x3d\
+\x3d\x62\x29\x72\x65\x74\x75\x72\x6e\x20\x66\x2e\x71\x75\x65\x75\
+\x65\x28\x74\x68\x69\x73\x5b\x30\x5d\x2c\x61\x29\x3b\x72\x65\x74\
+\x75\x72\x6e\x20\x74\x68\x69\x73\x2e\x65\x61\x63\x68\x28\x66\x75\
+\x6e\x63\x74\x69\x6f\x6e\x28\x29\x7b\x76\x61\x72\x20\x62\x3d\x66\
+\x2e\x71\x75\x65\x75\x65\x28\x74\x68\x69\x73\x2c\x61\x2c\x63\x29\
+\x3b\x61\x3d\x3d\x3d\x22\x66\x78\x22\x26\x26\x62\x5b\x30\x5d\x21\
+\x3d\x3d\x22\x69\x6e\x70\x72\x6f\x67\x72\x65\x73\x73\x22\x26\x26\
+\x66\x2e\x64\x65\x71\x75\x65\x75\x65\x28\x74\x68\x69\x73\x2c\x61\
+\x29\x7d\x29\x7d\x2c\x64\x65\x71\x75\x65\x75\x65\x3a\x66\x75\x6e\
+\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\
+\x74\x68\x69\x73\x2e\x65\x61\x63\x68\x28\x66\x75\x6e\x63\x74\x69\
+\x6f\x6e\x28\x29\x7b\x66\x2e\x64\x65\x71\x75\x65\x75\x65\x28\x74\
+\x68\x69\x73\x2c\x61\x29\x7d\x29\x7d\x2c\x64\x65\x6c\x61\x79\x3a\
+\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x29\x7b\x61\x3d\
+\x66\x2e\x66\x78\x3f\x66\x2e\x66\x78\x2e\x73\x70\x65\x65\x64\x73\
+\x5b\x61\x5d\x7c\x7c\x61\x3a\x61\x2c\x62\x3d\x62\x7c\x7c\x22\x66\
+\x78\x22\x3b\x72\x65\x74\x75\x72\x6e\x20\x74\x68\x69\x73\x2e\x71\
+\x75\x65\x75\x65\x28\x62\x2c\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\
+\x62\x2c\x63\x29\x7b\x76\x61\x72\x20\x64\x3d\x73\x65\x74\x54\x69\
+\x6d\x65\x6f\x75\x74\x28\x62\x2c\x61\x29\x3b\x63\x2e\x73\x74\x6f\
+\x70\x3d\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x29\x7b\x63\x6c\x65\
+\x61\x72\x54\x69\x6d\x65\x6f\x75\x74\x28\x64\x29\x7d\x7d\x29\x7d\
+\x2c\x63\x6c\x65\x61\x72\x51\x75\x65\x75\x65\x3a\x66\x75\x6e\x63\
+\x74\x69\x6f\x6e\x28\x61\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x74\
+\x68\x69\x73\x2e\x71\x75\x65\x75\x65\x28\x61\x7c\x7c\x22\x66\x78\
+\x22\x2c\x5b\x5d\x29\x7d\x2c\x70\x72\x6f\x6d\x69\x73\x65\x3a\x66\
+\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x63\x29\x7b\x66\x75\x6e\
+\x63\x74\x69\x6f\x6e\x20\x6d\x28\x29\x7b\x2d\x2d\x68\x7c\x7c\x64\
+\x2e\x72\x65\x73\x6f\x6c\x76\x65\x57\x69\x74\x68\x28\x65\x2c\x5b\
+\x65\x5d\x29\x7d\x74\x79\x70\x65\x6f\x66\x20\x61\x21\x3d\x22\x73\
+\x74\x72\x69\x6e\x67\x22\x26\x26\x28\x63\x3d\x61\x2c\x61\x3d\x62\
+\x29\x2c\x61\x3d\x61\x7c\x7c\x22\x66\x78\x22\x3b\x76\x61\x72\x20\
+\x64\x3d\x66\x2e\x44\x65\x66\x65\x72\x72\x65\x64\x28\x29\x2c\x65\
+\x3d\x74\x68\x69\x73\x2c\x67\x3d\x65\x2e\x6c\x65\x6e\x67\x74\x68\
+\x2c\x68\x3d\x31\x2c\x69\x3d\x61\x2b\x22\x64\x65\x66\x65\x72\x22\
+\x2c\x6a\x3d\x61\x2b\x22\x71\x75\x65\x75\x65\x22\x2c\x6b\x3d\x61\
+\x2b\x22\x6d\x61\x72\x6b\x22\x2c\x6c\x3b\x77\x68\x69\x6c\x65\x28\
+\x67\x2d\x2d\x29\x69\x66\x28\x6c\x3d\x66\x2e\x64\x61\x74\x61\x28\
+\x65\x5b\x67\x5d\x2c\x69\x2c\x62\x2c\x21\x30\x29\x7c\x7c\x28\x66\
+\x2e\x64\x61\x74\x61\x28\x65\x5b\x67\x5d\x2c\x6a\x2c\x62\x2c\x21\
+\x30\x29\x7c\x7c\x66\x2e\x64\x61\x74\x61\x28\x65\x5b\x67\x5d\x2c\
+\x6b\x2c\x62\x2c\x21\x30\x29\x29\x26\x26\x66\x2e\x64\x61\x74\x61\
+\x28\x65\x5b\x67\x5d\x2c\x69\x2c\x66\x2e\x43\x61\x6c\x6c\x62\x61\
+\x63\x6b\x73\x28\x22\x6f\x6e\x63\x65\x20\x6d\x65\x6d\x6f\x72\x79\
+\x22\x29\x2c\x21\x30\x29\x29\x68\x2b\x2b\x2c\x6c\x2e\x61\x64\x64\
+\x28\x6d\x29\x3b\x6d\x28\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x64\
+\x2e\x70\x72\x6f\x6d\x69\x73\x65\x28\x29\x7d\x7d\x29\x3b\x76\x61\
+\x72\x20\x6f\x3d\x2f\x5b\x5c\x6e\x5c\x74\x5c\x72\x5d\x2f\x67\x2c\
+\x70\x3d\x2f\x5c\x73\x2b\x2f\x2c\x71\x3d\x2f\x5c\x72\x2f\x67\x2c\
+\x72\x3d\x2f\x5e\x28\x3f\x3a\x62\x75\x74\x74\x6f\x6e\x7c\x69\x6e\
+\x70\x75\x74\x29\x24\x2f\x69\x2c\x73\x3d\x2f\x5e\x28\x3f\x3a\x62\
+\x75\x74\x74\x6f\x6e\x7c\x69\x6e\x70\x75\x74\x7c\x6f\x62\x6a\x65\
+\x63\x74\x7c\x73\x65\x6c\x65\x63\x74\x7c\x74\x65\x78\x74\x61\x72\
+\x65\x61\x29\x24\x2f\x69\x2c\x74\x3d\x2f\x5e\x61\x28\x3f\x3a\x72\
+\x65\x61\x29\x3f\x24\x2f\x69\x2c\x75\x3d\x2f\x5e\x28\x3f\x3a\x61\
+\x75\x74\x6f\x66\x6f\x63\x75\x73\x7c\x61\x75\x74\x6f\x70\x6c\x61\
+\x79\x7c\x61\x73\x79\x6e\x63\x7c\x63\x68\x65\x63\x6b\x65\x64\x7c\
+\x63\x6f\x6e\x74\x72\x6f\x6c\x73\x7c\x64\x65\x66\x65\x72\x7c\x64\
+\x69\x73\x61\x62\x6c\x65\x64\x7c\x68\x69\x64\x64\x65\x6e\x7c\x6c\
+\x6f\x6f\x70\x7c\x6d\x75\x6c\x74\x69\x70\x6c\x65\x7c\x6f\x70\x65\
+\x6e\x7c\x72\x65\x61\x64\x6f\x6e\x6c\x79\x7c\x72\x65\x71\x75\x69\
+\x72\x65\x64\x7c\x73\x63\x6f\x70\x65\x64\x7c\x73\x65\x6c\x65\x63\
+\x74\x65\x64\x29\x24\x2f\x69\x2c\x76\x3d\x66\x2e\x73\x75\x70\x70\
+\x6f\x72\x74\x2e\x67\x65\x74\x53\x65\x74\x41\x74\x74\x72\x69\x62\
+\x75\x74\x65\x2c\x77\x2c\x78\x2c\x79\x3b\x66\x2e\x66\x6e\x2e\x65\
+\x78\x74\x65\x6e\x64\x28\x7b\x61\x74\x74\x72\x3a\x66\x75\x6e\x63\
+\x74\x69\x6f\x6e\x28\x61\x2c\x62\x29\x7b\x72\x65\x74\x75\x72\x6e\
+\x20\x66\x2e\x61\x63\x63\x65\x73\x73\x28\x74\x68\x69\x73\x2c\x61\
+\x2c\x62\x2c\x21\x30\x2c\x66\x2e\x61\x74\x74\x72\x29\x7d\x2c\x72\
+\x65\x6d\x6f\x76\x65\x41\x74\x74\x72\x3a\x66\x75\x6e\x63\x74\x69\
+\x6f\x6e\x28\x61\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x74\x68\x69\
+\x73\x2e\x65\x61\x63\x68\x28\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\
+\x29\x7b\x66\x2e\x72\x65\x6d\x6f\x76\x65\x41\x74\x74\x72\x28\x74\
+\x68\x69\x73\x2c\x61\x29\x7d\x29\x7d\x2c\x70\x72\x6f\x70\x3a\x66\
+\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x29\x7b\x72\x65\x74\
+\x75\x72\x6e\x20\x66\x2e\x61\x63\x63\x65\x73\x73\x28\x74\x68\x69\
+\x73\x2c\x61\x2c\x62\x2c\x21\x30\x2c\x66\x2e\x70\x72\x6f\x70\x29\
+\x7d\x2c\x72\x65\x6d\x6f\x76\x65\x50\x72\x6f\x70\x3a\x66\x75\x6e\
+\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x61\x3d\x66\x2e\x70\x72\x6f\
+\x70\x46\x69\x78\x5b\x61\x5d\x7c\x7c\x61\x3b\x72\x65\x74\x75\x72\
+\x6e\x20\x74\x68\x69\x73\x2e\x65\x61\x63\x68\x28\x66\x75\x6e\x63\
+\x74\x69\x6f\x6e\x28\x29\x7b\x74\x72\x79\x7b\x74\x68\x69\x73\x5b\
+\x61\x5d\x3d\x62\x2c\x64\x65\x6c\x65\x74\x65\x20\x74\x68\x69\x73\
+\x5b\x61\x5d\x7d\x63\x61\x74\x63\x68\x28\x63\x29\x7b\x7d\x7d\x29\
+\x7d\x2c\x61\x64\x64\x43\x6c\x61\x73\x73\x3a\x66\x75\x6e\x63\x74\
+\x69\x6f\x6e\x28\x61\x29\x7b\x76\x61\x72\x20\x62\x2c\x63\x2c\x64\
+\x2c\x65\x2c\x67\x2c\x68\x2c\x69\x3b\x69\x66\x28\x66\x2e\x69\x73\
+\x46\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x29\x72\x65\x74\x75\
+\x72\x6e\x20\x74\x68\x69\x73\x2e\x65\x61\x63\x68\x28\x66\x75\x6e\
+\x63\x74\x69\x6f\x6e\x28\x62\x29\x7b\x66\x28\x74\x68\x69\x73\x29\
+\x2e\x61\x64\x64\x43\x6c\x61\x73\x73\x28\x61\x2e\x63\x61\x6c\x6c\
+\x28\x74\x68\x69\x73\x2c\x62\x2c\x74\x68\x69\x73\x2e\x63\x6c\x61\
+\x73\x73\x4e\x61\x6d\x65\x29\x29\x7d\x29\x3b\x69\x66\x28\x61\x26\
+\x26\x74\x79\x70\x65\x6f\x66\x20\x61\x3d\x3d\x22\x73\x74\x72\x69\
+\x6e\x67\x22\x29\x7b\x62\x3d\x61\x2e\x73\x70\x6c\x69\x74\x28\x70\
+\x29\x3b\x66\x6f\x72\x28\x63\x3d\x30\x2c\x64\x3d\x74\x68\x69\x73\
+\x2e\x6c\x65\x6e\x67\x74\x68\x3b\x63\x3c\x64\x3b\x63\x2b\x2b\x29\
+\x7b\x65\x3d\x74\x68\x69\x73\x5b\x63\x5d\x3b\x69\x66\x28\x65\x2e\
+\x6e\x6f\x64\x65\x54\x79\x70\x65\x3d\x3d\x3d\x31\x29\x69\x66\x28\
+\x21\x65\x2e\x63\x6c\x61\x73\x73\x4e\x61\x6d\x65\x26\x26\x62\x2e\
+\x6c\x65\x6e\x67\x74\x68\x3d\x3d\x3d\x31\x29\x65\x2e\x63\x6c\x61\
+\x73\x73\x4e\x61\x6d\x65\x3d\x61\x3b\x65\x6c\x73\x65\x7b\x67\x3d\
+\x22\x20\x22\x2b\x65\x2e\x63\x6c\x61\x73\x73\x4e\x61\x6d\x65\x2b\
+\x22\x20\x22\x3b\x66\x6f\x72\x28\x68\x3d\x30\x2c\x69\x3d\x62\x2e\
+\x6c\x65\x6e\x67\x74\x68\x3b\x68\x3c\x69\x3b\x68\x2b\x2b\x29\x7e\
+\x67\x2e\x69\x6e\x64\x65\x78\x4f\x66\x28\x22\x20\x22\x2b\x62\x5b\
+\x68\x5d\x2b\x22\x20\x22\x29\x7c\x7c\x28\x67\x2b\x3d\x62\x5b\x68\
+\x5d\x2b\x22\x20\x22\x29\x3b\x65\x2e\x63\x6c\x61\x73\x73\x4e\x61\
+\x6d\x65\x3d\x66\x2e\x74\x72\x69\x6d\x28\x67\x29\x7d\x7d\x7d\x72\
+\x65\x74\x75\x72\x6e\x20\x74\x68\x69\x73\x7d\x2c\x72\x65\x6d\x6f\
+\x76\x65\x43\x6c\x61\x73\x73\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\
+\x28\x61\x29\x7b\x76\x61\x72\x20\x63\x2c\x64\x2c\x65\x2c\x67\x2c\
+\x68\x2c\x69\x2c\x6a\x3b\x69\x66\x28\x66\x2e\x69\x73\x46\x75\x6e\
+\x63\x74\x69\x6f\x6e\x28\x61\x29\x29\x72\x65\x74\x75\x72\x6e\x20\
+\x74\x68\x69\x73\x2e\x65\x61\x63\x68\x28\x66\x75\x6e\x63\x74\x69\
+\x6f\x6e\x28\x62\x29\x7b\x66\x28\x74\x68\x69\x73\x29\x2e\x72\x65\
+\x6d\x6f\x76\x65\x43\x6c\x61\x73\x73\x28\x61\x2e\x63\x61\x6c\x6c\
+\x28\x74\x68\x69\x73\x2c\x62\x2c\x74\x68\x69\x73\x2e\x63\x6c\x61\
+\x73\x73\x4e\x61\x6d\x65\x29\x29\x7d\x29\x3b\x69\x66\x28\x61\x26\
+\x26\x74\x79\x70\x65\x6f\x66\x20\x61\x3d\x3d\x22\x73\x74\x72\x69\
+\x6e\x67\x22\x7c\x7c\x61\x3d\x3d\x3d\x62\x29\x7b\x63\x3d\x28\x61\
+\x7c\x7c\x22\x22\x29\x2e\x73\x70\x6c\x69\x74\x28\x70\x29\x3b\x66\
+\x6f\x72\x28\x64\x3d\x30\x2c\x65\x3d\x74\x68\x69\x73\x2e\x6c\x65\
+\x6e\x67\x74\x68\x3b\x64\x3c\x65\x3b\x64\x2b\x2b\x29\x7b\x67\x3d\
+\x74\x68\x69\x73\x5b\x64\x5d\x3b\x69\x66\x28\x67\x2e\x6e\x6f\x64\
+\x65\x54\x79\x70\x65\x3d\x3d\x3d\x31\x26\x26\x67\x2e\x63\x6c\x61\
+\x73\x73\x4e\x61\x6d\x65\x29\x69\x66\x28\x61\x29\x7b\x68\x3d\x28\
+\x22\x20\x22\x2b\x67\x2e\x63\x6c\x61\x73\x73\x4e\x61\x6d\x65\x2b\
+\x22\x20\x22\x29\x2e\x72\x65\x70\x6c\x61\x63\x65\x28\x6f\x2c\x22\
+\x20\x22\x29\x3b\x66\x6f\x72\x28\x69\x3d\x30\x2c\x6a\x3d\x63\x2e\
+\x6c\x65\x6e\x67\x74\x68\x3b\x69\x3c\x6a\x3b\x69\x2b\x2b\x29\x68\
+\x3d\x68\x2e\x72\x65\x70\x6c\x61\x63\x65\x28\x22\x20\x22\x2b\x63\
+\x5b\x69\x5d\x2b\x22\x20\x22\x2c\x22\x20\x22\x29\x3b\x67\x2e\x63\
+\x6c\x61\x73\x73\x4e\x61\x6d\x65\x3d\x66\x2e\x74\x72\x69\x6d\x28\
+\x68\x29\x7d\x65\x6c\x73\x65\x20\x67\x2e\x63\x6c\x61\x73\x73\x4e\
+\x61\x6d\x65\x3d\x22\x22\x7d\x7d\x72\x65\x74\x75\x72\x6e\x20\x74\
+\x68\x69\x73\x7d\x2c\x74\x6f\x67\x67\x6c\x65\x43\x6c\x61\x73\x73\
+\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x29\x7b\x76\
+\x61\x72\x20\x63\x3d\x74\x79\x70\x65\x6f\x66\x20\x61\x2c\x64\x3d\
+\x74\x79\x70\x65\x6f\x66\x20\x62\x3d\x3d\x22\x62\x6f\x6f\x6c\x65\
+\x61\x6e\x22\x3b\x69\x66\x28\x66\x2e\x69\x73\x46\x75\x6e\x63\x74\
+\x69\x6f\x6e\x28\x61\x29\x29\x72\x65\x74\x75\x72\x6e\x20\x74\x68\
+\x69\x73\x2e\x65\x61\x63\x68\x28\x66\x75\x6e\x63\x74\x69\x6f\x6e\
+\x28\x63\x29\x7b\x66\x28\x74\x68\x69\x73\x29\x2e\x74\x6f\x67\x67\
+\x6c\x65\x43\x6c\x61\x73\x73\x28\x61\x2e\x63\x61\x6c\x6c\x28\x74\
+\x68\x69\x73\x2c\x63\x2c\x74\x68\x69\x73\x2e\x63\x6c\x61\x73\x73\
+\x4e\x61\x6d\x65\x2c\x62\x29\x2c\x62\x29\x7d\x29\x3b\x72\x65\x74\
+\x75\x72\x6e\x20\x74\x68\x69\x73\x2e\x65\x61\x63\x68\x28\x66\x75\
+\x6e\x63\x74\x69\x6f\x6e\x28\x29\x7b\x69\x66\x28\x63\x3d\x3d\x3d\
+\x22\x73\x74\x72\x69\x6e\x67\x22\x29\x7b\x76\x61\x72\x20\x65\x2c\
+\x67\x3d\x30\x2c\x68\x3d\x66\x28\x74\x68\x69\x73\x29\x2c\x69\x3d\
+\x62\x2c\x6a\x3d\x61\x2e\x73\x70\x6c\x69\x74\x28\x70\x29\x3b\x77\
+\x68\x69\x6c\x65\x28\x65\x3d\x6a\x5b\x67\x2b\x2b\x5d\x29\x69\x3d\
+\x64\x3f\x69\x3a\x21\x68\x2e\x68\x61\x73\x43\x6c\x61\x73\x73\x28\
+\x65\x29\x2c\x68\x5b\x69\x3f\x22\x61\x64\x64\x43\x6c\x61\x73\x73\
+\x22\x3a\x22\x72\x65\x6d\x6f\x76\x65\x43\x6c\x61\x73\x73\x22\x5d\
+\x28\x65\x29\x7d\x65\x6c\x73\x65\x20\x69\x66\x28\x63\x3d\x3d\x3d\
+\x22\x75\x6e\x64\x65\x66\x69\x6e\x65\x64\x22\x7c\x7c\x63\x3d\x3d\
+\x3d\x22\x62\x6f\x6f\x6c\x65\x61\x6e\x22\x29\x74\x68\x69\x73\x2e\
+\x63\x6c\x61\x73\x73\x4e\x61\x6d\x65\x26\x26\x66\x2e\x5f\x64\x61\
+\x74\x61\x28\x74\x68\x69\x73\x2c\x22\x5f\x5f\x63\x6c\x61\x73\x73\
+\x4e\x61\x6d\x65\x5f\x5f\x22\x2c\x74\x68\x69\x73\x2e\x63\x6c\x61\
+\x73\x73\x4e\x61\x6d\x65\x29\x2c\x74\x68\x69\x73\x2e\x63\x6c\x61\
+\x73\x73\x4e\x61\x6d\x65\x3d\x74\x68\x69\x73\x2e\x63\x6c\x61\x73\
+\x73\x4e\x61\x6d\x65\x7c\x7c\x61\x3d\x3d\x3d\x21\x31\x3f\x22\x22\
+\x3a\x66\x2e\x5f\x64\x61\x74\x61\x28\x74\x68\x69\x73\x2c\x22\x5f\
+\x5f\x63\x6c\x61\x73\x73\x4e\x61\x6d\x65\x5f\x5f\x22\x29\x7c\x7c\
+\x22\x22\x7d\x29\x7d\x2c\x68\x61\x73\x43\x6c\x61\x73\x73\x3a\x66\
+\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x76\x61\x72\x20\x62\
+\x3d\x22\x20\x22\x2b\x61\x2b\x22\x20\x22\x2c\x63\x3d\x30\x2c\x64\
+\x3d\x74\x68\x69\x73\x2e\x6c\x65\x6e\x67\x74\x68\x3b\x66\x6f\x72\
+\x28\x3b\x63\x3c\x64\x3b\x63\x2b\x2b\x29\x69\x66\x28\x74\x68\x69\
+\x73\x5b\x63\x5d\x2e\x6e\x6f\x64\x65\x54\x79\x70\x65\x3d\x3d\x3d\
+\x31\x26\x26\x28\x22\x20\x22\x2b\x74\x68\x69\x73\x5b\x63\x5d\x2e\
+\x63\x6c\x61\x73\x73\x4e\x61\x6d\x65\x2b\x22\x20\x22\x29\x2e\x72\
+\x65\x70\x6c\x61\x63\x65\x28\x6f\x2c\x22\x20\x22\x29\x2e\x69\x6e\
+\x64\x65\x78\x4f\x66\x28\x62\x29\x3e\x2d\x31\x29\x72\x65\x74\x75\
+\x72\x6e\x21\x30\x3b\x72\x65\x74\x75\x72\x6e\x21\x31\x7d\x2c\x76\
+\x61\x6c\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x76\
+\x61\x72\x20\x63\x2c\x64\x2c\x65\x2c\x67\x3d\x74\x68\x69\x73\x5b\
+\x30\x5d\x3b\x7b\x69\x66\x28\x21\x21\x61\x72\x67\x75\x6d\x65\x6e\
+\x74\x73\x2e\x6c\x65\x6e\x67\x74\x68\x29\x7b\x65\x3d\x66\x2e\x69\
+\x73\x46\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x3b\x72\x65\x74\
+\x75\x72\x6e\x20\x74\x68\x69\x73\x2e\x65\x61\x63\x68\x28\x66\x75\
+\x6e\x63\x74\x69\x6f\x6e\x28\x64\x29\x7b\x76\x61\x72\x20\x67\x3d\
+\x66\x28\x74\x68\x69\x73\x29\x2c\x68\x3b\x69\x66\x28\x74\x68\x69\
+\x73\x2e\x6e\x6f\x64\x65\x54\x79\x70\x65\x3d\x3d\x3d\x31\x29\x7b\
+\x65\x3f\x68\x3d\x61\x2e\x63\x61\x6c\x6c\x28\x74\x68\x69\x73\x2c\
+\x64\x2c\x67\x2e\x76\x61\x6c\x28\x29\x29\x3a\x68\x3d\x61\x2c\x68\
+\x3d\x3d\x6e\x75\x6c\x6c\x3f\x68\x3d\x22\x22\x3a\x74\x79\x70\x65\
+\x6f\x66\x20\x68\x3d\x3d\x22\x6e\x75\x6d\x62\x65\x72\x22\x3f\x68\
+\x2b\x3d\x22\x22\x3a\x66\x2e\x69\x73\x41\x72\x72\x61\x79\x28\x68\
+\x29\x26\x26\x28\x68\x3d\x66\x2e\x6d\x61\x70\x28\x68\x2c\x66\x75\
+\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x72\x65\x74\x75\x72\x6e\
+\x20\x61\x3d\x3d\x6e\x75\x6c\x6c\x3f\x22\x22\x3a\x61\x2b\x22\x22\
+\x7d\x29\x29\x2c\x63\x3d\x66\x2e\x76\x61\x6c\x48\x6f\x6f\x6b\x73\
+\x5b\x74\x68\x69\x73\x2e\x6e\x6f\x64\x65\x4e\x61\x6d\x65\x2e\x74\
+\x6f\x4c\x6f\x77\x65\x72\x43\x61\x73\x65\x28\x29\x5d\x7c\x7c\x66\
+\x2e\x76\x61\x6c\x48\x6f\x6f\x6b\x73\x5b\x74\x68\x69\x73\x2e\x74\
+\x79\x70\x65\x5d\x3b\x69\x66\x28\x21\x63\x7c\x7c\x21\x28\x22\x73\
+\x65\x74\x22\x69\x6e\x20\x63\x29\x7c\x7c\x63\x2e\x73\x65\x74\x28\
+\x74\x68\x69\x73\x2c\x68\x2c\x22\x76\x61\x6c\x75\x65\x22\x29\x3d\
+\x3d\x3d\x62\x29\x74\x68\x69\x73\x2e\x76\x61\x6c\x75\x65\x3d\x68\
+\x7d\x7d\x29\x7d\x69\x66\x28\x67\x29\x7b\x63\x3d\x66\x2e\x76\x61\
+\x6c\x48\x6f\x6f\x6b\x73\x5b\x67\x2e\x6e\x6f\x64\x65\x4e\x61\x6d\
+\x65\x2e\x74\x6f\x4c\x6f\x77\x65\x72\x43\x61\x73\x65\x28\x29\x5d\
+\x7c\x7c\x66\x2e\x76\x61\x6c\x48\x6f\x6f\x6b\x73\x5b\x67\x2e\x74\
+\x79\x70\x65\x5d\x3b\x69\x66\x28\x63\x26\x26\x22\x67\x65\x74\x22\
+\x69\x6e\x20\x63\x26\x26\x28\x64\x3d\x63\x2e\x67\x65\x74\x28\x67\
+\x2c\x22\x76\x61\x6c\x75\x65\x22\x29\x29\x21\x3d\x3d\x62\x29\x72\
+\x65\x74\x75\x72\x6e\x20\x64\x3b\x64\x3d\x67\x2e\x76\x61\x6c\x75\
+\x65\x3b\x72\x65\x74\x75\x72\x6e\x20\x74\x79\x70\x65\x6f\x66\x20\
+\x64\x3d\x3d\x22\x73\x74\x72\x69\x6e\x67\x22\x3f\x64\x2e\x72\x65\
+\x70\x6c\x61\x63\x65\x28\x71\x2c\x22\x22\x29\x3a\x64\x3d\x3d\x6e\
+\x75\x6c\x6c\x3f\x22\x22\x3a\x64\x7d\x7d\x7d\x7d\x29\x2c\x66\x2e\
+\x65\x78\x74\x65\x6e\x64\x28\x7b\x76\x61\x6c\x48\x6f\x6f\x6b\x73\
+\x3a\x7b\x6f\x70\x74\x69\x6f\x6e\x3a\x7b\x67\x65\x74\x3a\x66\x75\
+\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x76\x61\x72\x20\x62\x3d\
+\x61\x2e\x61\x74\x74\x72\x69\x62\x75\x74\x65\x73\x2e\x76\x61\x6c\
+\x75\x65\x3b\x72\x65\x74\x75\x72\x6e\x21\x62\x7c\x7c\x62\x2e\x73\
+\x70\x65\x63\x69\x66\x69\x65\x64\x3f\x61\x2e\x76\x61\x6c\x75\x65\
+\x3a\x61\x2e\x74\x65\x78\x74\x7d\x7d\x2c\x73\x65\x6c\x65\x63\x74\
+\x3a\x7b\x67\x65\x74\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\
+\x29\x7b\x76\x61\x72\x20\x62\x2c\x63\x2c\x64\x2c\x65\x2c\x67\x3d\
+\x61\x2e\x73\x65\x6c\x65\x63\x74\x65\x64\x49\x6e\x64\x65\x78\x2c\
+\x68\x3d\x5b\x5d\x2c\x69\x3d\x61\x2e\x6f\x70\x74\x69\x6f\x6e\x73\
+\x2c\x6a\x3d\x61\x2e\x74\x79\x70\x65\x3d\x3d\x3d\x22\x73\x65\x6c\
+\x65\x63\x74\x2d\x6f\x6e\x65\x22\x3b\x69\x66\x28\x67\x3c\x30\x29\
+\x72\x65\x74\x75\x72\x6e\x20\x6e\x75\x6c\x6c\x3b\x63\x3d\x6a\x3f\
+\x67\x3a\x30\x2c\x64\x3d\x6a\x3f\x67\x2b\x31\x3a\x69\x2e\x6c\x65\
+\x6e\x67\x74\x68\x3b\x66\x6f\x72\x28\x3b\x63\x3c\x64\x3b\x63\x2b\
+\x2b\x29\x7b\x65\x3d\x69\x5b\x63\x5d\x3b\x69\x66\x28\x65\x2e\x73\
+\x65\x6c\x65\x63\x74\x65\x64\x26\x26\x28\x66\x2e\x73\x75\x70\x70\
+\x6f\x72\x74\x2e\x6f\x70\x74\x44\x69\x73\x61\x62\x6c\x65\x64\x3f\
+\x21\x65\x2e\x64\x69\x73\x61\x62\x6c\x65\x64\x3a\x65\x2e\x67\x65\
+\x74\x41\x74\x74\x72\x69\x62\x75\x74\x65\x28\x22\x64\x69\x73\x61\
+\x62\x6c\x65\x64\x22\x29\x3d\x3d\x3d\x6e\x75\x6c\x6c\x29\x26\x26\
+\x28\x21\x65\x2e\x70\x61\x72\x65\x6e\x74\x4e\x6f\x64\x65\x2e\x64\
+\x69\x73\x61\x62\x6c\x65\x64\x7c\x7c\x21\x66\x2e\x6e\x6f\x64\x65\
+\x4e\x61\x6d\x65\x28\x65\x2e\x70\x61\x72\x65\x6e\x74\x4e\x6f\x64\
+\x65\x2c\x22\x6f\x70\x74\x67\x72\x6f\x75\x70\x22\x29\x29\x29\x7b\
+\x62\x3d\x66\x28\x65\x29\x2e\x76\x61\x6c\x28\x29\x3b\x69\x66\x28\
+\x6a\x29\x72\x65\x74\x75\x72\x6e\x20\x62\x3b\x68\x2e\x70\x75\x73\
+\x68\x28\x62\x29\x7d\x7d\x69\x66\x28\x6a\x26\x26\x21\x68\x2e\x6c\
+\x65\x6e\x67\x74\x68\x26\x26\x69\x2e\x6c\x65\x6e\x67\x74\x68\x29\
+\x72\x65\x74\x75\x72\x6e\x20\x66\x28\x69\x5b\x67\x5d\x29\x2e\x76\
+\x61\x6c\x28\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x68\x7d\x2c\x73\
+\x65\x74\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x29\
+\x7b\x76\x61\x72\x20\x63\x3d\x66\x2e\x6d\x61\x6b\x65\x41\x72\x72\
+\x61\x79\x28\x62\x29\x3b\x66\x28\x61\x29\x2e\x66\x69\x6e\x64\x28\
+\x22\x6f\x70\x74\x69\x6f\x6e\x22\x29\x2e\x65\x61\x63\x68\x28\x66\
+\x75\x6e\x63\x74\x69\x6f\x6e\x28\x29\x7b\x74\x68\x69\x73\x2e\x73\
+\x65\x6c\x65\x63\x74\x65\x64\x3d\x66\x2e\x69\x6e\x41\x72\x72\x61\
+\x79\x28\x66\x28\x74\x68\x69\x73\x29\x2e\x76\x61\x6c\x28\x29\x2c\
+\x63\x29\x3e\x3d\x30\x7d\x29\x2c\x63\x2e\x6c\x65\x6e\x67\x74\x68\
+\x7c\x7c\x28\x61\x2e\x73\x65\x6c\x65\x63\x74\x65\x64\x49\x6e\x64\
+\x65\x78\x3d\x2d\x31\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x63\x7d\
+\x7d\x7d\x2c\x61\x74\x74\x72\x46\x6e\x3a\x7b\x76\x61\x6c\x3a\x21\
+\x30\x2c\x63\x73\x73\x3a\x21\x30\x2c\x68\x74\x6d\x6c\x3a\x21\x30\
+\x2c\x74\x65\x78\x74\x3a\x21\x30\x2c\x64\x61\x74\x61\x3a\x21\x30\
+\x2c\x77\x69\x64\x74\x68\x3a\x21\x30\x2c\x68\x65\x69\x67\x68\x74\
+\x3a\x21\x30\x2c\x6f\x66\x66\x73\x65\x74\x3a\x21\x30\x7d\x2c\x61\
+\x74\x74\x72\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x63\
+\x2c\x64\x2c\x65\x29\x7b\x76\x61\x72\x20\x67\x2c\x68\x2c\x69\x2c\
+\x6a\x3d\x61\x2e\x6e\x6f\x64\x65\x54\x79\x70\x65\x3b\x69\x66\x28\
+\x21\x21\x61\x26\x26\x6a\x21\x3d\x3d\x33\x26\x26\x6a\x21\x3d\x3d\
+\x38\x26\x26\x6a\x21\x3d\x3d\x32\x29\x7b\x69\x66\x28\x65\x26\x26\
+\x63\x20\x69\x6e\x20\x66\x2e\x61\x74\x74\x72\x46\x6e\x29\x72\x65\
+\x74\x75\x72\x6e\x20\x66\x28\x61\x29\x5b\x63\x5d\x28\x64\x29\x3b\
+\x69\x66\x28\x74\x79\x70\x65\x6f\x66\x20\x61\x2e\x67\x65\x74\x41\
+\x74\x74\x72\x69\x62\x75\x74\x65\x3d\x3d\x22\x75\x6e\x64\x65\x66\
+\x69\x6e\x65\x64\x22\x29\x72\x65\x74\x75\x72\x6e\x20\x66\x2e\x70\
+\x72\x6f\x70\x28\x61\x2c\x63\x2c\x64\x29\x3b\x69\x3d\x6a\x21\x3d\
+\x3d\x31\x7c\x7c\x21\x66\x2e\x69\x73\x58\x4d\x4c\x44\x6f\x63\x28\
+\x61\x29\x2c\x69\x26\x26\x28\x63\x3d\x63\x2e\x74\x6f\x4c\x6f\x77\
+\x65\x72\x43\x61\x73\x65\x28\x29\x2c\x68\x3d\x66\x2e\x61\x74\x74\
+\x72\x48\x6f\x6f\x6b\x73\x5b\x63\x5d\x7c\x7c\x28\x75\x2e\x74\x65\
+\x73\x74\x28\x63\x29\x3f\x78\x3a\x77\x29\x29\x3b\x69\x66\x28\x64\
+\x21\x3d\x3d\x62\x29\x7b\x69\x66\x28\x64\x3d\x3d\x3d\x6e\x75\x6c\
+\x6c\x29\x7b\x66\x2e\x72\x65\x6d\x6f\x76\x65\x41\x74\x74\x72\x28\
+\x61\x2c\x63\x29\x3b\x72\x65\x74\x75\x72\x6e\x7d\x69\x66\x28\x68\
+\x26\x26\x22\x73\x65\x74\x22\x69\x6e\x20\x68\x26\x26\x69\x26\x26\
+\x28\x67\x3d\x68\x2e\x73\x65\x74\x28\x61\x2c\x64\x2c\x63\x29\x29\
+\x21\x3d\x3d\x62\x29\x72\x65\x74\x75\x72\x6e\x20\x67\x3b\x61\x2e\
+\x73\x65\x74\x41\x74\x74\x72\x69\x62\x75\x74\x65\x28\x63\x2c\x22\
+\x22\x2b\x64\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x64\x7d\x69\x66\
+\x28\x68\x26\x26\x22\x67\x65\x74\x22\x69\x6e\x20\x68\x26\x26\x69\
+\x26\x26\x28\x67\x3d\x68\x2e\x67\x65\x74\x28\x61\x2c\x63\x29\x29\
+\x21\x3d\x3d\x6e\x75\x6c\x6c\x29\x72\x65\x74\x75\x72\x6e\x20\x67\
+\x3b\x67\x3d\x61\x2e\x67\x65\x74\x41\x74\x74\x72\x69\x62\x75\x74\
+\x65\x28\x63\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x67\x3d\x3d\x3d\
+\x6e\x75\x6c\x6c\x3f\x62\x3a\x67\x7d\x7d\x2c\x72\x65\x6d\x6f\x76\
+\x65\x41\x74\x74\x72\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\
+\x2c\x62\x29\x7b\x76\x61\x72\x20\x63\x2c\x64\x2c\x65\x2c\x67\x2c\
+\x68\x3d\x30\x3b\x69\x66\x28\x62\x26\x26\x61\x2e\x6e\x6f\x64\x65\
+\x54\x79\x70\x65\x3d\x3d\x3d\x31\x29\x7b\x64\x3d\x62\x2e\x74\x6f\
+\x4c\x6f\x77\x65\x72\x43\x61\x73\x65\x28\x29\x2e\x73\x70\x6c\x69\
+\x74\x28\x70\x29\x2c\x67\x3d\x64\x2e\x6c\x65\x6e\x67\x74\x68\x3b\
+\x66\x6f\x72\x28\x3b\x68\x3c\x67\x3b\x68\x2b\x2b\x29\x65\x3d\x64\
+\x5b\x68\x5d\x2c\x65\x26\x26\x28\x63\x3d\x66\x2e\x70\x72\x6f\x70\
+\x46\x69\x78\x5b\x65\x5d\x7c\x7c\x65\x2c\x66\x2e\x61\x74\x74\x72\
+\x28\x61\x2c\x65\x2c\x22\x22\x29\x2c\x61\x2e\x72\x65\x6d\x6f\x76\
+\x65\x41\x74\x74\x72\x69\x62\x75\x74\x65\x28\x76\x3f\x65\x3a\x63\
+\x29\x2c\x75\x2e\x74\x65\x73\x74\x28\x65\x29\x26\x26\x63\x20\x69\
+\x6e\x20\x61\x26\x26\x28\x61\x5b\x63\x5d\x3d\x21\x31\x29\x29\x7d\
+\x7d\x2c\x61\x74\x74\x72\x48\x6f\x6f\x6b\x73\x3a\x7b\x74\x79\x70\
+\x65\x3a\x7b\x73\x65\x74\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\
+\x61\x2c\x62\x29\x7b\x69\x66\x28\x72\x2e\x74\x65\x73\x74\x28\x61\
+\x2e\x6e\x6f\x64\x65\x4e\x61\x6d\x65\x29\x26\x26\x61\x2e\x70\x61\
+\x72\x65\x6e\x74\x4e\x6f\x64\x65\x29\x66\x2e\x65\x72\x72\x6f\x72\
+\x28\x22\x74\x79\x70\x65\x20\x70\x72\x6f\x70\x65\x72\x74\x79\x20\
+\x63\x61\x6e\x27\x74\x20\x62\x65\x20\x63\x68\x61\x6e\x67\x65\x64\
+\x22\x29\x3b\x65\x6c\x73\x65\x20\x69\x66\x28\x21\x66\x2e\x73\x75\
+\x70\x70\x6f\x72\x74\x2e\x72\x61\x64\x69\x6f\x56\x61\x6c\x75\x65\
+\x26\x26\x62\x3d\x3d\x3d\x22\x72\x61\x64\x69\x6f\x22\x26\x26\x66\
+\x2e\x6e\x6f\x64\x65\x4e\x61\x6d\x65\x28\x61\x2c\x22\x69\x6e\x70\
+\x75\x74\x22\x29\x29\x7b\x76\x61\x72\x20\x63\x3d\x61\x2e\x76\x61\
+\x6c\x75\x65\x3b\x61\x2e\x73\x65\x74\x41\x74\x74\x72\x69\x62\x75\
+\x74\x65\x28\x22\x74\x79\x70\x65\x22\x2c\x62\x29\x2c\x63\x26\x26\
+\x28\x61\x2e\x76\x61\x6c\x75\x65\x3d\x63\x29\x3b\x72\x65\x74\x75\
+\x72\x6e\x20\x62\x7d\x7d\x7d\x2c\x76\x61\x6c\x75\x65\x3a\x7b\x67\
+\x65\x74\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x29\
+\x7b\x69\x66\x28\x77\x26\x26\x66\x2e\x6e\x6f\x64\x65\x4e\x61\x6d\
+\x65\x28\x61\x2c\x22\x62\x75\x74\x74\x6f\x6e\x22\x29\x29\x72\x65\
+\x74\x75\x72\x6e\x20\x77\x2e\x67\x65\x74\x28\x61\x2c\x62\x29\x3b\
+\x72\x65\x74\x75\x72\x6e\x20\x62\x20\x69\x6e\x20\x61\x3f\x61\x2e\
+\x76\x61\x6c\x75\x65\x3a\x6e\x75\x6c\x6c\x7d\x2c\x73\x65\x74\x3a\
+\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x2c\x63\x29\x7b\
+\x69\x66\x28\x77\x26\x26\x66\x2e\x6e\x6f\x64\x65\x4e\x61\x6d\x65\
+\x28\x61\x2c\x22\x62\x75\x74\x74\x6f\x6e\x22\x29\x29\x72\x65\x74\
+\x75\x72\x6e\x20\x77\x2e\x73\x65\x74\x28\x61\x2c\x62\x2c\x63\x29\
+\x3b\x61\x2e\x76\x61\x6c\x75\x65\x3d\x62\x7d\x7d\x7d\x2c\x70\x72\
+\x6f\x70\x46\x69\x78\x3a\x7b\x74\x61\x62\x69\x6e\x64\x65\x78\x3a\
+\x22\x74\x61\x62\x49\x6e\x64\x65\x78\x22\x2c\x72\x65\x61\x64\x6f\
+\x6e\x6c\x79\x3a\x22\x72\x65\x61\x64\x4f\x6e\x6c\x79\x22\x2c\x22\
+\x66\x6f\x72\x22\x3a\x22\x68\x74\x6d\x6c\x46\x6f\x72\x22\x2c\x22\
+\x63\x6c\x61\x73\x73\x22\x3a\x22\x63\x6c\x61\x73\x73\x4e\x61\x6d\
+\x65\x22\x2c\x6d\x61\x78\x6c\x65\x6e\x67\x74\x68\x3a\x22\x6d\x61\
+\x78\x4c\x65\x6e\x67\x74\x68\x22\x2c\x63\x65\x6c\x6c\x73\x70\x61\
+\x63\x69\x6e\x67\x3a\x22\x63\x65\x6c\x6c\x53\x70\x61\x63\x69\x6e\
+\x67\x22\x2c\x63\x65\x6c\x6c\x70\x61\x64\x64\x69\x6e\x67\x3a\x22\
+\x63\x65\x6c\x6c\x50\x61\x64\x64\x69\x6e\x67\x22\x2c\x72\x6f\x77\
+\x73\x70\x61\x6e\x3a\x22\x72\x6f\x77\x53\x70\x61\x6e\x22\x2c\x63\
+\x6f\x6c\x73\x70\x61\x6e\x3a\x22\x63\x6f\x6c\x53\x70\x61\x6e\x22\
+\x2c\x75\x73\x65\x6d\x61\x70\x3a\x22\x75\x73\x65\x4d\x61\x70\x22\
+\x2c\x66\x72\x61\x6d\x65\x62\x6f\x72\x64\x65\x72\x3a\x22\x66\x72\
+\x61\x6d\x65\x42\x6f\x72\x64\x65\x72\x22\x2c\x63\x6f\x6e\x74\x65\
+\x6e\x74\x65\x64\x69\x74\x61\x62\x6c\x65\x3a\x22\x63\x6f\x6e\x74\
+\x65\x6e\x74\x45\x64\x69\x74\x61\x62\x6c\x65\x22\x7d\x2c\x70\x72\
+\x6f\x70\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x63\x2c\
+\x64\x29\x7b\x76\x61\x72\x20\x65\x2c\x67\x2c\x68\x2c\x69\x3d\x61\
+\x2e\x6e\x6f\x64\x65\x54\x79\x70\x65\x3b\x69\x66\x28\x21\x21\x61\
+\x26\x26\x69\x21\x3d\x3d\x33\x26\x26\x69\x21\x3d\x3d\x38\x26\x26\
+\x69\x21\x3d\x3d\x32\x29\x7b\x68\x3d\x69\x21\x3d\x3d\x31\x7c\x7c\
+\x21\x66\x2e\x69\x73\x58\x4d\x4c\x44\x6f\x63\x28\x61\x29\x2c\x68\
+\x26\x26\x28\x63\x3d\x66\x2e\x70\x72\x6f\x70\x46\x69\x78\x5b\x63\
+\x5d\x7c\x7c\x63\x2c\x67\x3d\x66\x2e\x70\x72\x6f\x70\x48\x6f\x6f\
+\x6b\x73\x5b\x63\x5d\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x64\x21\
+\x3d\x3d\x62\x3f\x67\x26\x26\x22\x73\x65\x74\x22\x69\x6e\x20\x67\
+\x26\x26\x28\x65\x3d\x67\x2e\x73\x65\x74\x28\x61\x2c\x64\x2c\x63\
+\x29\x29\x21\x3d\x3d\x62\x3f\x65\x3a\x61\x5b\x63\x5d\x3d\x64\x3a\
+\x67\x26\x26\x22\x67\x65\x74\x22\x69\x6e\x20\x67\x26\x26\x28\x65\
+\x3d\x67\x2e\x67\x65\x74\x28\x61\x2c\x63\x29\x29\x21\x3d\x3d\x6e\
+\x75\x6c\x6c\x3f\x65\x3a\x61\x5b\x63\x5d\x7d\x7d\x2c\x70\x72\x6f\
+\x70\x48\x6f\x6f\x6b\x73\x3a\x7b\x74\x61\x62\x49\x6e\x64\x65\x78\
+\x3a\x7b\x67\x65\x74\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\
+\x29\x7b\x76\x61\x72\x20\x63\x3d\x61\x2e\x67\x65\x74\x41\x74\x74\
+\x72\x69\x62\x75\x74\x65\x4e\x6f\x64\x65\x28\x22\x74\x61\x62\x69\
+\x6e\x64\x65\x78\x22\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x63\x26\
+\x26\x63\x2e\x73\x70\x65\x63\x69\x66\x69\x65\x64\x3f\x70\x61\x72\
+\x73\x65\x49\x6e\x74\x28\x63\x2e\x76\x61\x6c\x75\x65\x2c\x31\x30\
+\x29\x3a\x73\x2e\x74\x65\x73\x74\x28\x61\x2e\x6e\x6f\x64\x65\x4e\
+\x61\x6d\x65\x29\x7c\x7c\x74\x2e\x74\x65\x73\x74\x28\x61\x2e\x6e\
+\x6f\x64\x65\x4e\x61\x6d\x65\x29\x26\x26\x61\x2e\x68\x72\x65\x66\
+\x3f\x30\x3a\x62\x7d\x7d\x7d\x7d\x29\x2c\x66\x2e\x61\x74\x74\x72\
+\x48\x6f\x6f\x6b\x73\x2e\x74\x61\x62\x69\x6e\x64\x65\x78\x3d\x66\
+\x2e\x70\x72\x6f\x70\x48\x6f\x6f\x6b\x73\x2e\x74\x61\x62\x49\x6e\
+\x64\x65\x78\x2c\x78\x3d\x7b\x67\x65\x74\x3a\x66\x75\x6e\x63\x74\
+\x69\x6f\x6e\x28\x61\x2c\x63\x29\x7b\x76\x61\x72\x20\x64\x2c\x65\
+\x3d\x66\x2e\x70\x72\x6f\x70\x28\x61\x2c\x63\x29\x3b\x72\x65\x74\
+\x75\x72\x6e\x20\x65\x3d\x3d\x3d\x21\x30\x7c\x7c\x74\x79\x70\x65\
+\x6f\x66\x20\x65\x21\x3d\x22\x62\x6f\x6f\x6c\x65\x61\x6e\x22\x26\
+\x26\x28\x64\x3d\x61\x2e\x67\x65\x74\x41\x74\x74\x72\x69\x62\x75\
+\x74\x65\x4e\x6f\x64\x65\x28\x63\x29\x29\x26\x26\x64\x2e\x6e\x6f\
+\x64\x65\x56\x61\x6c\x75\x65\x21\x3d\x3d\x21\x31\x3f\x63\x2e\x74\
+\x6f\x4c\x6f\x77\x65\x72\x43\x61\x73\x65\x28\x29\x3a\x62\x7d\x2c\
+\x73\x65\x74\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\
+\x2c\x63\x29\x7b\x76\x61\x72\x20\x64\x3b\x62\x3d\x3d\x3d\x21\x31\
+\x3f\x66\x2e\x72\x65\x6d\x6f\x76\x65\x41\x74\x74\x72\x28\x61\x2c\
+\x63\x29\x3a\x28\x64\x3d\x66\x2e\x70\x72\x6f\x70\x46\x69\x78\x5b\
+\x63\x5d\x7c\x7c\x63\x2c\x64\x20\x69\x6e\x20\x61\x26\x26\x28\x61\
+\x5b\x64\x5d\x3d\x21\x30\x29\x2c\x61\x2e\x73\x65\x74\x41\x74\x74\
+\x72\x69\x62\x75\x74\x65\x28\x63\x2c\x63\x2e\x74\x6f\x4c\x6f\x77\
+\x65\x72\x43\x61\x73\x65\x28\x29\x29\x29\x3b\x72\x65\x74\x75\x72\
+\x6e\x20\x63\x7d\x7d\x2c\x76\x7c\x7c\x28\x79\x3d\x7b\x6e\x61\x6d\
+\x65\x3a\x21\x30\x2c\x69\x64\x3a\x21\x30\x7d\x2c\x77\x3d\x66\x2e\
+\x76\x61\x6c\x48\x6f\x6f\x6b\x73\x2e\x62\x75\x74\x74\x6f\x6e\x3d\
+\x7b\x67\x65\x74\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\
+\x63\x29\x7b\x76\x61\x72\x20\x64\x3b\x64\x3d\x61\x2e\x67\x65\x74\
+\x41\x74\x74\x72\x69\x62\x75\x74\x65\x4e\x6f\x64\x65\x28\x63\x29\
+\x3b\x72\x65\x74\x75\x72\x6e\x20\x64\x26\x26\x28\x79\x5b\x63\x5d\
+\x3f\x64\x2e\x6e\x6f\x64\x65\x56\x61\x6c\x75\x65\x21\x3d\x3d\x22\
+\x22\x3a\x64\x2e\x73\x70\x65\x63\x69\x66\x69\x65\x64\x29\x3f\x64\
+\x2e\x6e\x6f\x64\x65\x56\x61\x6c\x75\x65\x3a\x62\x7d\x2c\x73\x65\
+\x74\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x2c\x64\
+\x29\x7b\x76\x61\x72\x20\x65\x3d\x61\x2e\x67\x65\x74\x41\x74\x74\
+\x72\x69\x62\x75\x74\x65\x4e\x6f\x64\x65\x28\x64\x29\x3b\x65\x7c\
+\x7c\x28\x65\x3d\x63\x2e\x63\x72\x65\x61\x74\x65\x41\x74\x74\x72\
+\x69\x62\x75\x74\x65\x28\x64\x29\x2c\x61\x2e\x73\x65\x74\x41\x74\
+\x74\x72\x69\x62\x75\x74\x65\x4e\x6f\x64\x65\x28\x65\x29\x29\x3b\
+\x72\x65\x74\x75\x72\x6e\x20\x65\x2e\x6e\x6f\x64\x65\x56\x61\x6c\
+\x75\x65\x3d\x62\x2b\x22\x22\x7d\x7d\x2c\x66\x2e\x61\x74\x74\x72\
+\x48\x6f\x6f\x6b\x73\x2e\x74\x61\x62\x69\x6e\x64\x65\x78\x2e\x73\
+\x65\x74\x3d\x77\x2e\x73\x65\x74\x2c\x66\x2e\x65\x61\x63\x68\x28\
+\x5b\x22\x77\x69\x64\x74\x68\x22\x2c\x22\x68\x65\x69\x67\x68\x74\
+\x22\x5d\x2c\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x29\
+\x7b\x66\x2e\x61\x74\x74\x72\x48\x6f\x6f\x6b\x73\x5b\x62\x5d\x3d\
+\x66\x2e\x65\x78\x74\x65\x6e\x64\x28\x66\x2e\x61\x74\x74\x72\x48\
+\x6f\x6f\x6b\x73\x5b\x62\x5d\x2c\x7b\x73\x65\x74\x3a\x66\x75\x6e\
+\x63\x74\x69\x6f\x6e\x28\x61\x2c\x63\x29\x7b\x69\x66\x28\x63\x3d\
+\x3d\x3d\x22\x22\x29\x7b\x61\x2e\x73\x65\x74\x41\x74\x74\x72\x69\
+\x62\x75\x74\x65\x28\x62\x2c\x22\x61\x75\x74\x6f\x22\x29\x3b\x72\
+\x65\x74\x75\x72\x6e\x20\x63\x7d\x7d\x7d\x29\x7d\x29\x2c\x66\x2e\
+\x61\x74\x74\x72\x48\x6f\x6f\x6b\x73\x2e\x63\x6f\x6e\x74\x65\x6e\
+\x74\x65\x64\x69\x74\x61\x62\x6c\x65\x3d\x7b\x67\x65\x74\x3a\x77\
+\x2e\x67\x65\x74\x2c\x73\x65\x74\x3a\x66\x75\x6e\x63\x74\x69\x6f\
+\x6e\x28\x61\x2c\x62\x2c\x63\x29\x7b\x62\x3d\x3d\x3d\x22\x22\x26\
+\x26\x28\x62\x3d\x22\x66\x61\x6c\x73\x65\x22\x29\x2c\x77\x2e\x73\
+\x65\x74\x28\x61\x2c\x62\x2c\x63\x29\x7d\x7d\x29\x2c\x66\x2e\x73\
+\x75\x70\x70\x6f\x72\x74\x2e\x68\x72\x65\x66\x4e\x6f\x72\x6d\x61\
+\x6c\x69\x7a\x65\x64\x7c\x7c\x66\x2e\x65\x61\x63\x68\x28\x5b\x22\
+\x68\x72\x65\x66\x22\x2c\x22\x73\x72\x63\x22\x2c\x22\x77\x69\x64\
+\x74\x68\x22\x2c\x22\x68\x65\x69\x67\x68\x74\x22\x5d\x2c\x66\x75\
+\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x63\x29\x7b\x66\x2e\x61\x74\
+\x74\x72\x48\x6f\x6f\x6b\x73\x5b\x63\x5d\x3d\x66\x2e\x65\x78\x74\
+\x65\x6e\x64\x28\x66\x2e\x61\x74\x74\x72\x48\x6f\x6f\x6b\x73\x5b\
+\x63\x5d\x2c\x7b\x67\x65\x74\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\
+\x28\x61\x29\x7b\x76\x61\x72\x20\x64\x3d\x61\x2e\x67\x65\x74\x41\
+\x74\x74\x72\x69\x62\x75\x74\x65\x28\x63\x2c\x32\x29\x3b\x72\x65\
+\x74\x75\x72\x6e\x20\x64\x3d\x3d\x3d\x6e\x75\x6c\x6c\x3f\x62\x3a\
+\x64\x7d\x7d\x29\x7d\x29\x2c\x66\x2e\x73\x75\x70\x70\x6f\x72\x74\
+\x2e\x73\x74\x79\x6c\x65\x7c\x7c\x28\x66\x2e\x61\x74\x74\x72\x48\
+\x6f\x6f\x6b\x73\x2e\x73\x74\x79\x6c\x65\x3d\x7b\x67\x65\x74\x3a\
+\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x72\x65\x74\x75\
+\x72\x6e\x20\x61\x2e\x73\x74\x79\x6c\x65\x2e\x63\x73\x73\x54\x65\
+\x78\x74\x2e\x74\x6f\x4c\x6f\x77\x65\x72\x43\x61\x73\x65\x28\x29\
+\x7c\x7c\x62\x7d\x2c\x73\x65\x74\x3a\x66\x75\x6e\x63\x74\x69\x6f\
+\x6e\x28\x61\x2c\x62\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x61\x2e\
+\x73\x74\x79\x6c\x65\x2e\x63\x73\x73\x54\x65\x78\x74\x3d\x22\x22\
+\x2b\x62\x7d\x7d\x29\x2c\x66\x2e\x73\x75\x70\x70\x6f\x72\x74\x2e\
+\x6f\x70\x74\x53\x65\x6c\x65\x63\x74\x65\x64\x7c\x7c\x28\x66\x2e\
+\x70\x72\x6f\x70\x48\x6f\x6f\x6b\x73\x2e\x73\x65\x6c\x65\x63\x74\
+\x65\x64\x3d\x66\x2e\x65\x78\x74\x65\x6e\x64\x28\x66\x2e\x70\x72\
+\x6f\x70\x48\x6f\x6f\x6b\x73\x2e\x73\x65\x6c\x65\x63\x74\x65\x64\
+\x2c\x7b\x67\x65\x74\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\
+\x29\x7b\x76\x61\x72\x20\x62\x3d\x61\x2e\x70\x61\x72\x65\x6e\x74\
+\x4e\x6f\x64\x65\x3b\x62\x26\x26\x28\x62\x2e\x73\x65\x6c\x65\x63\
+\x74\x65\x64\x49\x6e\x64\x65\x78\x2c\x62\x2e\x70\x61\x72\x65\x6e\
+\x74\x4e\x6f\x64\x65\x26\x26\x62\x2e\x70\x61\x72\x65\x6e\x74\x4e\
+\x6f\x64\x65\x2e\x73\x65\x6c\x65\x63\x74\x65\x64\x49\x6e\x64\x65\
+\x78\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x6e\x75\x6c\x6c\x7d\x7d\
+\x29\x29\x2c\x66\x2e\x73\x75\x70\x70\x6f\x72\x74\x2e\x65\x6e\x63\
+\x74\x79\x70\x65\x7c\x7c\x28\x66\x2e\x70\x72\x6f\x70\x46\x69\x78\
+\x2e\x65\x6e\x63\x74\x79\x70\x65\x3d\x22\x65\x6e\x63\x6f\x64\x69\
+\x6e\x67\x22\x29\x2c\x66\x2e\x73\x75\x70\x70\x6f\x72\x74\x2e\x63\
+\x68\x65\x63\x6b\x4f\x6e\x7c\x7c\x66\x2e\x65\x61\x63\x68\x28\x5b\
+\x22\x72\x61\x64\x69\x6f\x22\x2c\x22\x63\x68\x65\x63\x6b\x62\x6f\
+\x78\x22\x5d\x2c\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x29\x7b\x66\
+\x2e\x76\x61\x6c\x48\x6f\x6f\x6b\x73\x5b\x74\x68\x69\x73\x5d\x3d\
+\x7b\x67\x65\x74\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\
+\x7b\x72\x65\x74\x75\x72\x6e\x20\x61\x2e\x67\x65\x74\x41\x74\x74\
+\x72\x69\x62\x75\x74\x65\x28\x22\x76\x61\x6c\x75\x65\x22\x29\x3d\
+\x3d\x3d\x6e\x75\x6c\x6c\x3f\x22\x6f\x6e\x22\x3a\x61\x2e\x76\x61\
+\x6c\x75\x65\x7d\x7d\x7d\x29\x2c\x66\x2e\x65\x61\x63\x68\x28\x5b\
+\x22\x72\x61\x64\x69\x6f\x22\x2c\x22\x63\x68\x65\x63\x6b\x62\x6f\
+\x78\x22\x5d\x2c\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x29\x7b\x66\
+\x2e\x76\x61\x6c\x48\x6f\x6f\x6b\x73\x5b\x74\x68\x69\x73\x5d\x3d\
+\x66\x2e\x65\x78\x74\x65\x6e\x64\x28\x66\x2e\x76\x61\x6c\x48\x6f\
+\x6f\x6b\x73\x5b\x74\x68\x69\x73\x5d\x2c\x7b\x73\x65\x74\x3a\x66\
+\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x29\x7b\x69\x66\x28\
+\x66\x2e\x69\x73\x41\x72\x72\x61\x79\x28\x62\x29\x29\x72\x65\x74\
+\x75\x72\x6e\x20\x61\x2e\x63\x68\x65\x63\x6b\x65\x64\x3d\x66\x2e\
+\x69\x6e\x41\x72\x72\x61\x79\x28\x66\x28\x61\x29\x2e\x76\x61\x6c\
+\x28\x29\x2c\x62\x29\x3e\x3d\x30\x7d\x7d\x29\x7d\x29\x3b\x76\x61\
+\x72\x20\x7a\x3d\x2f\x5e\x28\x3f\x3a\x74\x65\x78\x74\x61\x72\x65\
+\x61\x7c\x69\x6e\x70\x75\x74\x7c\x73\x65\x6c\x65\x63\x74\x29\x24\
+\x2f\x69\x2c\x41\x3d\x2f\x5e\x28\x5b\x5e\x5c\x2e\x5d\x2a\x29\x3f\
+\x28\x3f\x3a\x5c\x2e\x28\x2e\x2b\x29\x29\x3f\x24\x2f\x2c\x42\x3d\
+\x2f\x5c\x62\x68\x6f\x76\x65\x72\x28\x5c\x2e\x5c\x53\x2b\x29\x3f\
+\x5c\x62\x2f\x2c\x43\x3d\x2f\x5e\x6b\x65\x79\x2f\x2c\x44\x3d\x2f\
+\x5e\x28\x3f\x3a\x6d\x6f\x75\x73\x65\x7c\x63\x6f\x6e\x74\x65\x78\
+\x74\x6d\x65\x6e\x75\x29\x7c\x63\x6c\x69\x63\x6b\x2f\x2c\x45\x3d\
+\x2f\x5e\x28\x3f\x3a\x66\x6f\x63\x75\x73\x69\x6e\x66\x6f\x63\x75\
+\x73\x7c\x66\x6f\x63\x75\x73\x6f\x75\x74\x62\x6c\x75\x72\x29\x24\
+\x2f\x2c\x46\x3d\x2f\x5e\x28\x5c\x77\x2a\x29\x28\x3f\x3a\x23\x28\
+\x5b\x5c\x77\x5c\x2d\x5d\x2b\x29\x29\x3f\x28\x3f\x3a\x5c\x2e\x28\
+\x5b\x5c\x77\x5c\x2d\x5d\x2b\x29\x29\x3f\x24\x2f\x2c\x47\x3d\x66\
+\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x76\x61\x72\x20\x62\
+\x3d\x46\x2e\x65\x78\x65\x63\x28\x61\x29\x3b\x62\x26\x26\x28\x62\
+\x5b\x31\x5d\x3d\x28\x62\x5b\x31\x5d\x7c\x7c\x22\x22\x29\x2e\x74\
+\x6f\x4c\x6f\x77\x65\x72\x43\x61\x73\x65\x28\x29\x2c\x62\x5b\x33\
+\x5d\x3d\x62\x5b\x33\x5d\x26\x26\x6e\x65\x77\x20\x52\x65\x67\x45\
+\x78\x70\x28\x22\x28\x3f\x3a\x5e\x7c\x5c\x5c\x73\x29\x22\x2b\x62\
+\x5b\x33\x5d\x2b\x22\x28\x3f\x3a\x5c\x5c\x73\x7c\x24\x29\x22\x29\
+\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x62\x7d\x2c\x48\x3d\x66\x75\
+\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x29\x7b\x76\x61\x72\x20\
+\x63\x3d\x61\x2e\x61\x74\x74\x72\x69\x62\x75\x74\x65\x73\x7c\x7c\
+\x7b\x7d\x3b\x72\x65\x74\x75\x72\x6e\x28\x21\x62\x5b\x31\x5d\x7c\
+\x7c\x61\x2e\x6e\x6f\x64\x65\x4e\x61\x6d\x65\x2e\x74\x6f\x4c\x6f\
+\x77\x65\x72\x43\x61\x73\x65\x28\x29\x3d\x3d\x3d\x62\x5b\x31\x5d\
+\x29\x26\x26\x28\x21\x62\x5b\x32\x5d\x7c\x7c\x28\x63\x2e\x69\x64\
+\x7c\x7c\x7b\x7d\x29\x2e\x76\x61\x6c\x75\x65\x3d\x3d\x3d\x62\x5b\
+\x32\x5d\x29\x26\x26\x28\x21\x62\x5b\x33\x5d\x7c\x7c\x62\x5b\x33\
+\x5d\x2e\x74\x65\x73\x74\x28\x28\x63\x5b\x22\x63\x6c\x61\x73\x73\
+\x22\x5d\x7c\x7c\x7b\x7d\x29\x2e\x76\x61\x6c\x75\x65\x29\x29\x7d\
+\x2c\x49\x3d\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x72\
+\x65\x74\x75\x72\x6e\x20\x66\x2e\x65\x76\x65\x6e\x74\x2e\x73\x70\
+\x65\x63\x69\x61\x6c\x2e\x68\x6f\x76\x65\x72\x3f\x61\x3a\x61\x2e\
+\x72\x65\x70\x6c\x61\x63\x65\x28\x42\x2c\x22\x6d\x6f\x75\x73\x65\
+\x65\x6e\x74\x65\x72\x24\x31\x20\x6d\x6f\x75\x73\x65\x6c\x65\x61\
+\x76\x65\x24\x31\x22\x29\x7d\x3b\x0a\x66\x2e\x65\x76\x65\x6e\x74\
+\x3d\x7b\x61\x64\x64\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\
+\x2c\x63\x2c\x64\x2c\x65\x2c\x67\x29\x7b\x76\x61\x72\x20\x68\x2c\
+\x69\x2c\x6a\x2c\x6b\x2c\x6c\x2c\x6d\x2c\x6e\x2c\x6f\x2c\x70\x2c\
+\x71\x2c\x72\x2c\x73\x3b\x69\x66\x28\x21\x28\x61\x2e\x6e\x6f\x64\
+\x65\x54\x79\x70\x65\x3d\x3d\x3d\x33\x7c\x7c\x61\x2e\x6e\x6f\x64\
+\x65\x54\x79\x70\x65\x3d\x3d\x3d\x38\x7c\x7c\x21\x63\x7c\x7c\x21\
+\x64\x7c\x7c\x21\x28\x68\x3d\x66\x2e\x5f\x64\x61\x74\x61\x28\x61\
+\x29\x29\x29\x29\x7b\x64\x2e\x68\x61\x6e\x64\x6c\x65\x72\x26\x26\
+\x28\x70\x3d\x64\x2c\x64\x3d\x70\x2e\x68\x61\x6e\x64\x6c\x65\x72\
+\x29\x2c\x64\x2e\x67\x75\x69\x64\x7c\x7c\x28\x64\x2e\x67\x75\x69\
+\x64\x3d\x66\x2e\x67\x75\x69\x64\x2b\x2b\x29\x2c\x6a\x3d\x68\x2e\
+\x65\x76\x65\x6e\x74\x73\x2c\x6a\x7c\x7c\x28\x68\x2e\x65\x76\x65\
+\x6e\x74\x73\x3d\x6a\x3d\x7b\x7d\x29\x2c\x69\x3d\x68\x2e\x68\x61\
+\x6e\x64\x6c\x65\x2c\x69\x7c\x7c\x28\x68\x2e\x68\x61\x6e\x64\x6c\
+\x65\x3d\x69\x3d\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\
+\x72\x65\x74\x75\x72\x6e\x20\x74\x79\x70\x65\x6f\x66\x20\x66\x21\
+\x3d\x22\x75\x6e\x64\x65\x66\x69\x6e\x65\x64\x22\x26\x26\x28\x21\
+\x61\x7c\x7c\x66\x2e\x65\x76\x65\x6e\x74\x2e\x74\x72\x69\x67\x67\
+\x65\x72\x65\x64\x21\x3d\x3d\x61\x2e\x74\x79\x70\x65\x29\x3f\x66\
+\x2e\x65\x76\x65\x6e\x74\x2e\x64\x69\x73\x70\x61\x74\x63\x68\x2e\
+\x61\x70\x70\x6c\x79\x28\x69\x2e\x65\x6c\x65\x6d\x2c\x61\x72\x67\
+\x75\x6d\x65\x6e\x74\x73\x29\x3a\x62\x7d\x2c\x69\x2e\x65\x6c\x65\
+\x6d\x3d\x61\x29\x2c\x63\x3d\x66\x2e\x74\x72\x69\x6d\x28\x49\x28\
+\x63\x29\x29\x2e\x73\x70\x6c\x69\x74\x28\x22\x20\x22\x29\x3b\x66\
+\x6f\x72\x28\x6b\x3d\x30\x3b\x6b\x3c\x63\x2e\x6c\x65\x6e\x67\x74\
+\x68\x3b\x6b\x2b\x2b\x29\x7b\x6c\x3d\x41\x2e\x65\x78\x65\x63\x28\
+\x63\x5b\x6b\x5d\x29\x7c\x7c\x5b\x5d\x2c\x6d\x3d\x6c\x5b\x31\x5d\
+\x2c\x6e\x3d\x28\x6c\x5b\x32\x5d\x7c\x7c\x22\x22\x29\x2e\x73\x70\
+\x6c\x69\x74\x28\x22\x2e\x22\x29\x2e\x73\x6f\x72\x74\x28\x29\x2c\
+\x73\x3d\x66\x2e\x65\x76\x65\x6e\x74\x2e\x73\x70\x65\x63\x69\x61\
+\x6c\x5b\x6d\x5d\x7c\x7c\x7b\x7d\x2c\x6d\x3d\x28\x67\x3f\x73\x2e\
+\x64\x65\x6c\x65\x67\x61\x74\x65\x54\x79\x70\x65\x3a\x73\x2e\x62\
+\x69\x6e\x64\x54\x79\x70\x65\x29\x7c\x7c\x6d\x2c\x73\x3d\x66\x2e\
+\x65\x76\x65\x6e\x74\x2e\x73\x70\x65\x63\x69\x61\x6c\x5b\x6d\x5d\
+\x7c\x7c\x7b\x7d\x2c\x6f\x3d\x66\x2e\x65\x78\x74\x65\x6e\x64\x28\
+\x7b\x74\x79\x70\x65\x3a\x6d\x2c\x6f\x72\x69\x67\x54\x79\x70\x65\
+\x3a\x6c\x5b\x31\x5d\x2c\x64\x61\x74\x61\x3a\x65\x2c\x68\x61\x6e\
+\x64\x6c\x65\x72\x3a\x64\x2c\x67\x75\x69\x64\x3a\x64\x2e\x67\x75\
+\x69\x64\x2c\x73\x65\x6c\x65\x63\x74\x6f\x72\x3a\x67\x2c\x71\x75\
+\x69\x63\x6b\x3a\x47\x28\x67\x29\x2c\x6e\x61\x6d\x65\x73\x70\x61\
+\x63\x65\x3a\x6e\x2e\x6a\x6f\x69\x6e\x28\x22\x2e\x22\x29\x7d\x2c\
+\x70\x29\x2c\x72\x3d\x6a\x5b\x6d\x5d\x3b\x69\x66\x28\x21\x72\x29\
+\x7b\x72\x3d\x6a\x5b\x6d\x5d\x3d\x5b\x5d\x2c\x72\x2e\x64\x65\x6c\
+\x65\x67\x61\x74\x65\x43\x6f\x75\x6e\x74\x3d\x30\x3b\x69\x66\x28\
+\x21\x73\x2e\x73\x65\x74\x75\x70\x7c\x7c\x73\x2e\x73\x65\x74\x75\
+\x70\x2e\x63\x61\x6c\x6c\x28\x61\x2c\x65\x2c\x6e\x2c\x69\x29\x3d\
+\x3d\x3d\x21\x31\x29\x61\x2e\x61\x64\x64\x45\x76\x65\x6e\x74\x4c\
+\x69\x73\x74\x65\x6e\x65\x72\x3f\x61\x2e\x61\x64\x64\x45\x76\x65\
+\x6e\x74\x4c\x69\x73\x74\x65\x6e\x65\x72\x28\x6d\x2c\x69\x2c\x21\
+\x31\x29\x3a\x61\x2e\x61\x74\x74\x61\x63\x68\x45\x76\x65\x6e\x74\
+\x26\x26\x61\x2e\x61\x74\x74\x61\x63\x68\x45\x76\x65\x6e\x74\x28\
+\x22\x6f\x6e\x22\x2b\x6d\x2c\x69\x29\x7d\x73\x2e\x61\x64\x64\x26\
+\x26\x28\x73\x2e\x61\x64\x64\x2e\x63\x61\x6c\x6c\x28\x61\x2c\x6f\
+\x29\x2c\x6f\x2e\x68\x61\x6e\x64\x6c\x65\x72\x2e\x67\x75\x69\x64\
+\x7c\x7c\x28\x6f\x2e\x68\x61\x6e\x64\x6c\x65\x72\x2e\x67\x75\x69\
+\x64\x3d\x64\x2e\x67\x75\x69\x64\x29\x29\x2c\x67\x3f\x72\x2e\x73\
+\x70\x6c\x69\x63\x65\x28\x72\x2e\x64\x65\x6c\x65\x67\x61\x74\x65\
+\x43\x6f\x75\x6e\x74\x2b\x2b\x2c\x30\x2c\x6f\x29\x3a\x72\x2e\x70\
+\x75\x73\x68\x28\x6f\x29\x2c\x66\x2e\x65\x76\x65\x6e\x74\x2e\x67\
+\x6c\x6f\x62\x61\x6c\x5b\x6d\x5d\x3d\x21\x30\x7d\x61\x3d\x6e\x75\
+\x6c\x6c\x7d\x7d\x2c\x67\x6c\x6f\x62\x61\x6c\x3a\x7b\x7d\x2c\x72\
+\x65\x6d\x6f\x76\x65\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\
+\x2c\x62\x2c\x63\x2c\x64\x2c\x65\x29\x7b\x76\x61\x72\x20\x67\x3d\
+\x66\x2e\x68\x61\x73\x44\x61\x74\x61\x28\x61\x29\x26\x26\x66\x2e\
+\x5f\x64\x61\x74\x61\x28\x61\x29\x2c\x68\x2c\x69\x2c\x6a\x2c\x6b\
+\x2c\x6c\x2c\x6d\x2c\x6e\x2c\x6f\x2c\x70\x2c\x71\x2c\x72\x2c\x73\
+\x3b\x69\x66\x28\x21\x21\x67\x26\x26\x21\x21\x28\x6f\x3d\x67\x2e\
+\x65\x76\x65\x6e\x74\x73\x29\x29\x7b\x62\x3d\x66\x2e\x74\x72\x69\
+\x6d\x28\x49\x28\x62\x7c\x7c\x22\x22\x29\x29\x2e\x73\x70\x6c\x69\
+\x74\x28\x22\x20\x22\x29\x3b\x66\x6f\x72\x28\x68\x3d\x30\x3b\x68\
+\x3c\x62\x2e\x6c\x65\x6e\x67\x74\x68\x3b\x68\x2b\x2b\x29\x7b\x69\
+\x3d\x41\x2e\x65\x78\x65\x63\x28\x62\x5b\x68\x5d\x29\x7c\x7c\x5b\
+\x5d\x2c\x6a\x3d\x6b\x3d\x69\x5b\x31\x5d\x2c\x6c\x3d\x69\x5b\x32\
+\x5d\x3b\x69\x66\x28\x21\x6a\x29\x7b\x66\x6f\x72\x28\x6a\x20\x69\
+\x6e\x20\x6f\x29\x66\x2e\x65\x76\x65\x6e\x74\x2e\x72\x65\x6d\x6f\
+\x76\x65\x28\x61\x2c\x6a\x2b\x62\x5b\x68\x5d\x2c\x63\x2c\x64\x2c\
+\x21\x30\x29\x3b\x63\x6f\x6e\x74\x69\x6e\x75\x65\x7d\x70\x3d\x66\
+\x2e\x65\x76\x65\x6e\x74\x2e\x73\x70\x65\x63\x69\x61\x6c\x5b\x6a\
+\x5d\x7c\x7c\x7b\x7d\x2c\x6a\x3d\x28\x64\x3f\x70\x2e\x64\x65\x6c\
+\x65\x67\x61\x74\x65\x54\x79\x70\x65\x3a\x70\x2e\x62\x69\x6e\x64\
+\x54\x79\x70\x65\x29\x7c\x7c\x6a\x2c\x72\x3d\x6f\x5b\x6a\x5d\x7c\
+\x7c\x5b\x5d\x2c\x6d\x3d\x72\x2e\x6c\x65\x6e\x67\x74\x68\x2c\x6c\
+\x3d\x6c\x3f\x6e\x65\x77\x20\x52\x65\x67\x45\x78\x70\x28\x22\x28\
+\x5e\x7c\x5c\x5c\x2e\x29\x22\x2b\x6c\x2e\x73\x70\x6c\x69\x74\x28\
+\x22\x2e\x22\x29\x2e\x73\x6f\x72\x74\x28\x29\x2e\x6a\x6f\x69\x6e\
+\x28\x22\x5c\x5c\x2e\x28\x3f\x3a\x2e\x2a\x5c\x5c\x2e\x29\x3f\x22\
+\x29\x2b\x22\x28\x5c\x5c\x2e\x7c\x24\x29\x22\x29\x3a\x6e\x75\x6c\
+\x6c\x3b\x66\x6f\x72\x28\x6e\x3d\x30\x3b\x6e\x3c\x72\x2e\x6c\x65\
+\x6e\x67\x74\x68\x3b\x6e\x2b\x2b\x29\x73\x3d\x72\x5b\x6e\x5d\x2c\
+\x28\x65\x7c\x7c\x6b\x3d\x3d\x3d\x73\x2e\x6f\x72\x69\x67\x54\x79\
+\x70\x65\x29\x26\x26\x28\x21\x63\x7c\x7c\x63\x2e\x67\x75\x69\x64\
+\x3d\x3d\x3d\x73\x2e\x67\x75\x69\x64\x29\x26\x26\x28\x21\x6c\x7c\
+\x7c\x6c\x2e\x74\x65\x73\x74\x28\x73\x2e\x6e\x61\x6d\x65\x73\x70\
+\x61\x63\x65\x29\x29\x26\x26\x28\x21\x64\x7c\x7c\x64\x3d\x3d\x3d\
+\x73\x2e\x73\x65\x6c\x65\x63\x74\x6f\x72\x7c\x7c\x64\x3d\x3d\x3d\
+\x22\x2a\x2a\x22\x26\x26\x73\x2e\x73\x65\x6c\x65\x63\x74\x6f\x72\
+\x29\x26\x26\x28\x72\x2e\x73\x70\x6c\x69\x63\x65\x28\x6e\x2d\x2d\
+\x2c\x31\x29\x2c\x73\x2e\x73\x65\x6c\x65\x63\x74\x6f\x72\x26\x26\
+\x72\x2e\x64\x65\x6c\x65\x67\x61\x74\x65\x43\x6f\x75\x6e\x74\x2d\
+\x2d\x2c\x70\x2e\x72\x65\x6d\x6f\x76\x65\x26\x26\x70\x2e\x72\x65\
+\x6d\x6f\x76\x65\x2e\x63\x61\x6c\x6c\x28\x61\x2c\x73\x29\x29\x3b\
+\x72\x2e\x6c\x65\x6e\x67\x74\x68\x3d\x3d\x3d\x30\x26\x26\x6d\x21\
+\x3d\x3d\x72\x2e\x6c\x65\x6e\x67\x74\x68\x26\x26\x28\x28\x21\x70\
+\x2e\x74\x65\x61\x72\x64\x6f\x77\x6e\x7c\x7c\x70\x2e\x74\x65\x61\
+\x72\x64\x6f\x77\x6e\x2e\x63\x61\x6c\x6c\x28\x61\x2c\x6c\x29\x3d\
+\x3d\x3d\x21\x31\x29\x26\x26\x66\x2e\x72\x65\x6d\x6f\x76\x65\x45\
+\x76\x65\x6e\x74\x28\x61\x2c\x6a\x2c\x67\x2e\x68\x61\x6e\x64\x6c\
+\x65\x29\x2c\x64\x65\x6c\x65\x74\x65\x20\x6f\x5b\x6a\x5d\x29\x7d\
+\x66\x2e\x69\x73\x45\x6d\x70\x74\x79\x4f\x62\x6a\x65\x63\x74\x28\
+\x6f\x29\x26\x26\x28\x71\x3d\x67\x2e\x68\x61\x6e\x64\x6c\x65\x2c\
+\x71\x26\x26\x28\x71\x2e\x65\x6c\x65\x6d\x3d\x6e\x75\x6c\x6c\x29\
+\x2c\x66\x2e\x72\x65\x6d\x6f\x76\x65\x44\x61\x74\x61\x28\x61\x2c\
+\x5b\x22\x65\x76\x65\x6e\x74\x73\x22\x2c\x22\x68\x61\x6e\x64\x6c\
+\x65\x22\x5d\x2c\x21\x30\x29\x29\x7d\x7d\x2c\x63\x75\x73\x74\x6f\
+\x6d\x45\x76\x65\x6e\x74\x3a\x7b\x67\x65\x74\x44\x61\x74\x61\x3a\
+\x21\x30\x2c\x73\x65\x74\x44\x61\x74\x61\x3a\x21\x30\x2c\x63\x68\
+\x61\x6e\x67\x65\x44\x61\x74\x61\x3a\x21\x30\x7d\x2c\x74\x72\x69\
+\x67\x67\x65\x72\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x63\x2c\
+\x64\x2c\x65\x2c\x67\x29\x7b\x69\x66\x28\x21\x65\x7c\x7c\x65\x2e\
+\x6e\x6f\x64\x65\x54\x79\x70\x65\x21\x3d\x3d\x33\x26\x26\x65\x2e\
+\x6e\x6f\x64\x65\x54\x79\x70\x65\x21\x3d\x3d\x38\x29\x7b\x76\x61\
+\x72\x20\x68\x3d\x63\x2e\x74\x79\x70\x65\x7c\x7c\x63\x2c\x69\x3d\
+\x5b\x5d\x2c\x6a\x2c\x6b\x2c\x6c\x2c\x6d\x2c\x6e\x2c\x6f\x2c\x70\
+\x2c\x71\x2c\x72\x2c\x73\x3b\x69\x66\x28\x45\x2e\x74\x65\x73\x74\
+\x28\x68\x2b\x66\x2e\x65\x76\x65\x6e\x74\x2e\x74\x72\x69\x67\x67\
+\x65\x72\x65\x64\x29\x29\x72\x65\x74\x75\x72\x6e\x3b\x68\x2e\x69\
+\x6e\x64\x65\x78\x4f\x66\x28\x22\x21\x22\x29\x3e\x3d\x30\x26\x26\
+\x28\x68\x3d\x68\x2e\x73\x6c\x69\x63\x65\x28\x30\x2c\x2d\x31\x29\
+\x2c\x6b\x3d\x21\x30\x29\x2c\x68\x2e\x69\x6e\x64\x65\x78\x4f\x66\
+\x28\x22\x2e\x22\x29\x3e\x3d\x30\x26\x26\x28\x69\x3d\x68\x2e\x73\
+\x70\x6c\x69\x74\x28\x22\x2e\x22\x29\x2c\x68\x3d\x69\x2e\x73\x68\
+\x69\x66\x74\x28\x29\x2c\x69\x2e\x73\x6f\x72\x74\x28\x29\x29\x3b\
+\x69\x66\x28\x28\x21\x65\x7c\x7c\x66\x2e\x65\x76\x65\x6e\x74\x2e\
+\x63\x75\x73\x74\x6f\x6d\x45\x76\x65\x6e\x74\x5b\x68\x5d\x29\x26\
+\x26\x21\x66\x2e\x65\x76\x65\x6e\x74\x2e\x67\x6c\x6f\x62\x61\x6c\
+\x5b\x68\x5d\x29\x72\x65\x74\x75\x72\x6e\x3b\x63\x3d\x74\x79\x70\
+\x65\x6f\x66\x20\x63\x3d\x3d\x22\x6f\x62\x6a\x65\x63\x74\x22\x3f\
+\x63\x5b\x66\x2e\x65\x78\x70\x61\x6e\x64\x6f\x5d\x3f\x63\x3a\x6e\
+\x65\x77\x20\x66\x2e\x45\x76\x65\x6e\x74\x28\x68\x2c\x63\x29\x3a\
+\x6e\x65\x77\x20\x66\x2e\x45\x76\x65\x6e\x74\x28\x68\x29\x2c\x63\
+\x2e\x74\x79\x70\x65\x3d\x68\x2c\x63\x2e\x69\x73\x54\x72\x69\x67\
+\x67\x65\x72\x3d\x21\x30\x2c\x63\x2e\x65\x78\x63\x6c\x75\x73\x69\
+\x76\x65\x3d\x6b\x2c\x63\x2e\x6e\x61\x6d\x65\x73\x70\x61\x63\x65\
+\x3d\x69\x2e\x6a\x6f\x69\x6e\x28\x22\x2e\x22\x29\x2c\x63\x2e\x6e\
+\x61\x6d\x65\x73\x70\x61\x63\x65\x5f\x72\x65\x3d\x63\x2e\x6e\x61\
+\x6d\x65\x73\x70\x61\x63\x65\x3f\x6e\x65\x77\x20\x52\x65\x67\x45\
+\x78\x70\x28\x22\x28\x5e\x7c\x5c\x5c\x2e\x29\x22\x2b\x69\x2e\x6a\
+\x6f\x69\x6e\x28\x22\x5c\x5c\x2e\x28\x3f\x3a\x2e\x2a\x5c\x5c\x2e\
+\x29\x3f\x22\x29\x2b\x22\x28\x5c\x5c\x2e\x7c\x24\x29\x22\x29\x3a\
+\x6e\x75\x6c\x6c\x2c\x6f\x3d\x68\x2e\x69\x6e\x64\x65\x78\x4f\x66\
+\x28\x22\x3a\x22\x29\x3c\x30\x3f\x22\x6f\x6e\x22\x2b\x68\x3a\x22\
+\x22\x3b\x69\x66\x28\x21\x65\x29\x7b\x6a\x3d\x66\x2e\x63\x61\x63\
+\x68\x65\x3b\x66\x6f\x72\x28\x6c\x20\x69\x6e\x20\x6a\x29\x6a\x5b\
+\x6c\x5d\x2e\x65\x76\x65\x6e\x74\x73\x26\x26\x6a\x5b\x6c\x5d\x2e\
+\x65\x76\x65\x6e\x74\x73\x5b\x68\x5d\x26\x26\x66\x2e\x65\x76\x65\
+\x6e\x74\x2e\x74\x72\x69\x67\x67\x65\x72\x28\x63\x2c\x64\x2c\x6a\
+\x5b\x6c\x5d\x2e\x68\x61\x6e\x64\x6c\x65\x2e\x65\x6c\x65\x6d\x2c\
+\x21\x30\x29\x3b\x72\x65\x74\x75\x72\x6e\x7d\x63\x2e\x72\x65\x73\
+\x75\x6c\x74\x3d\x62\x2c\x63\x2e\x74\x61\x72\x67\x65\x74\x7c\x7c\
+\x28\x63\x2e\x74\x61\x72\x67\x65\x74\x3d\x65\x29\x2c\x64\x3d\x64\
+\x21\x3d\x6e\x75\x6c\x6c\x3f\x66\x2e\x6d\x61\x6b\x65\x41\x72\x72\
+\x61\x79\x28\x64\x29\x3a\x5b\x5d\x2c\x64\x2e\x75\x6e\x73\x68\x69\
+\x66\x74\x28\x63\x29\x2c\x70\x3d\x66\x2e\x65\x76\x65\x6e\x74\x2e\
+\x73\x70\x65\x63\x69\x61\x6c\x5b\x68\x5d\x7c\x7c\x7b\x7d\x3b\x69\
+\x66\x28\x70\x2e\x74\x72\x69\x67\x67\x65\x72\x26\x26\x70\x2e\x74\
+\x72\x69\x67\x67\x65\x72\x2e\x61\x70\x70\x6c\x79\x28\x65\x2c\x64\
+\x29\x3d\x3d\x3d\x21\x31\x29\x72\x65\x74\x75\x72\x6e\x3b\x72\x3d\
+\x5b\x5b\x65\x2c\x70\x2e\x62\x69\x6e\x64\x54\x79\x70\x65\x7c\x7c\
+\x68\x5d\x5d\x3b\x69\x66\x28\x21\x67\x26\x26\x21\x70\x2e\x6e\x6f\
+\x42\x75\x62\x62\x6c\x65\x26\x26\x21\x66\x2e\x69\x73\x57\x69\x6e\
+\x64\x6f\x77\x28\x65\x29\x29\x7b\x73\x3d\x70\x2e\x64\x65\x6c\x65\
+\x67\x61\x74\x65\x54\x79\x70\x65\x7c\x7c\x68\x2c\x6d\x3d\x45\x2e\
+\x74\x65\x73\x74\x28\x73\x2b\x68\x29\x3f\x65\x3a\x65\x2e\x70\x61\
+\x72\x65\x6e\x74\x4e\x6f\x64\x65\x2c\x6e\x3d\x6e\x75\x6c\x6c\x3b\
+\x66\x6f\x72\x28\x3b\x6d\x3b\x6d\x3d\x6d\x2e\x70\x61\x72\x65\x6e\
+\x74\x4e\x6f\x64\x65\x29\x72\x2e\x70\x75\x73\x68\x28\x5b\x6d\x2c\
+\x73\x5d\x29\x2c\x6e\x3d\x6d\x3b\x6e\x26\x26\x6e\x3d\x3d\x3d\x65\
+\x2e\x6f\x77\x6e\x65\x72\x44\x6f\x63\x75\x6d\x65\x6e\x74\x26\x26\
+\x72\x2e\x70\x75\x73\x68\x28\x5b\x6e\x2e\x64\x65\x66\x61\x75\x6c\
+\x74\x56\x69\x65\x77\x7c\x7c\x6e\x2e\x70\x61\x72\x65\x6e\x74\x57\
+\x69\x6e\x64\x6f\x77\x7c\x7c\x61\x2c\x73\x5d\x29\x7d\x66\x6f\x72\
+\x28\x6c\x3d\x30\x3b\x6c\x3c\x72\x2e\x6c\x65\x6e\x67\x74\x68\x26\
+\x26\x21\x63\x2e\x69\x73\x50\x72\x6f\x70\x61\x67\x61\x74\x69\x6f\
+\x6e\x53\x74\x6f\x70\x70\x65\x64\x28\x29\x3b\x6c\x2b\x2b\x29\x6d\
+\x3d\x72\x5b\x6c\x5d\x5b\x30\x5d\x2c\x63\x2e\x74\x79\x70\x65\x3d\
+\x72\x5b\x6c\x5d\x5b\x31\x5d\x2c\x71\x3d\x28\x66\x2e\x5f\x64\x61\
+\x74\x61\x28\x6d\x2c\x22\x65\x76\x65\x6e\x74\x73\x22\x29\x7c\x7c\
+\x7b\x7d\x29\x5b\x63\x2e\x74\x79\x70\x65\x5d\x26\x26\x66\x2e\x5f\
+\x64\x61\x74\x61\x28\x6d\x2c\x22\x68\x61\x6e\x64\x6c\x65\x22\x29\
+\x2c\x71\x26\x26\x71\x2e\x61\x70\x70\x6c\x79\x28\x6d\x2c\x64\x29\
+\x2c\x71\x3d\x6f\x26\x26\x6d\x5b\x6f\x5d\x2c\x71\x26\x26\x66\x2e\
+\x61\x63\x63\x65\x70\x74\x44\x61\x74\x61\x28\x6d\x29\x26\x26\x71\
+\x2e\x61\x70\x70\x6c\x79\x28\x6d\x2c\x64\x29\x3d\x3d\x3d\x21\x31\
+\x26\x26\x63\x2e\x70\x72\x65\x76\x65\x6e\x74\x44\x65\x66\x61\x75\
+\x6c\x74\x28\x29\x3b\x63\x2e\x74\x79\x70\x65\x3d\x68\x2c\x21\x67\
+\x26\x26\x21\x63\x2e\x69\x73\x44\x65\x66\x61\x75\x6c\x74\x50\x72\
+\x65\x76\x65\x6e\x74\x65\x64\x28\x29\x26\x26\x28\x21\x70\x2e\x5f\
+\x64\x65\x66\x61\x75\x6c\x74\x7c\x7c\x70\x2e\x5f\x64\x65\x66\x61\
+\x75\x6c\x74\x2e\x61\x70\x70\x6c\x79\x28\x65\x2e\x6f\x77\x6e\x65\
+\x72\x44\x6f\x63\x75\x6d\x65\x6e\x74\x2c\x64\x29\x3d\x3d\x3d\x21\
+\x31\x29\x26\x26\x28\x68\x21\x3d\x3d\x22\x63\x6c\x69\x63\x6b\x22\
+\x7c\x7c\x21\x66\x2e\x6e\x6f\x64\x65\x4e\x61\x6d\x65\x28\x65\x2c\
+\x22\x61\x22\x29\x29\x26\x26\x66\x2e\x61\x63\x63\x65\x70\x74\x44\
+\x61\x74\x61\x28\x65\x29\x26\x26\x6f\x26\x26\x65\x5b\x68\x5d\x26\
+\x26\x28\x68\x21\x3d\x3d\x22\x66\x6f\x63\x75\x73\x22\x26\x26\x68\
+\x21\x3d\x3d\x22\x62\x6c\x75\x72\x22\x7c\x7c\x63\x2e\x74\x61\x72\
+\x67\x65\x74\x2e\x6f\x66\x66\x73\x65\x74\x57\x69\x64\x74\x68\x21\
+\x3d\x3d\x30\x29\x26\x26\x21\x66\x2e\x69\x73\x57\x69\x6e\x64\x6f\
+\x77\x28\x65\x29\x26\x26\x28\x6e\x3d\x65\x5b\x6f\x5d\x2c\x6e\x26\
+\x26\x28\x65\x5b\x6f\x5d\x3d\x6e\x75\x6c\x6c\x29\x2c\x66\x2e\x65\
+\x76\x65\x6e\x74\x2e\x74\x72\x69\x67\x67\x65\x72\x65\x64\x3d\x68\
+\x2c\x65\x5b\x68\x5d\x28\x29\x2c\x66\x2e\x65\x76\x65\x6e\x74\x2e\
+\x74\x72\x69\x67\x67\x65\x72\x65\x64\x3d\x62\x2c\x6e\x26\x26\x28\
+\x65\x5b\x6f\x5d\x3d\x6e\x29\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\
+\x63\x2e\x72\x65\x73\x75\x6c\x74\x7d\x7d\x2c\x64\x69\x73\x70\x61\
+\x74\x63\x68\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x63\x29\x7b\
+\x63\x3d\x66\x2e\x65\x76\x65\x6e\x74\x2e\x66\x69\x78\x28\x63\x7c\
+\x7c\x61\x2e\x65\x76\x65\x6e\x74\x29\x3b\x76\x61\x72\x20\x64\x3d\
+\x28\x66\x2e\x5f\x64\x61\x74\x61\x28\x74\x68\x69\x73\x2c\x22\x65\
+\x76\x65\x6e\x74\x73\x22\x29\x7c\x7c\x7b\x7d\x29\x5b\x63\x2e\x74\
+\x79\x70\x65\x5d\x7c\x7c\x5b\x5d\x2c\x65\x3d\x64\x2e\x64\x65\x6c\
+\x65\x67\x61\x74\x65\x43\x6f\x75\x6e\x74\x2c\x67\x3d\x5b\x5d\x2e\
+\x73\x6c\x69\x63\x65\x2e\x63\x61\x6c\x6c\x28\x61\x72\x67\x75\x6d\
+\x65\x6e\x74\x73\x2c\x30\x29\x2c\x68\x3d\x21\x63\x2e\x65\x78\x63\
+\x6c\x75\x73\x69\x76\x65\x26\x26\x21\x63\x2e\x6e\x61\x6d\x65\x73\
+\x70\x61\x63\x65\x2c\x69\x3d\x5b\x5d\x2c\x6a\x2c\x6b\x2c\x6c\x2c\
+\x6d\x2c\x6e\x2c\x6f\x2c\x70\x2c\x71\x2c\x72\x2c\x73\x2c\x74\x3b\
+\x67\x5b\x30\x5d\x3d\x63\x2c\x63\x2e\x64\x65\x6c\x65\x67\x61\x74\
+\x65\x54\x61\x72\x67\x65\x74\x3d\x74\x68\x69\x73\x3b\x69\x66\x28\
+\x65\x26\x26\x21\x63\x2e\x74\x61\x72\x67\x65\x74\x2e\x64\x69\x73\
+\x61\x62\x6c\x65\x64\x26\x26\x28\x21\x63\x2e\x62\x75\x74\x74\x6f\
+\x6e\x7c\x7c\x63\x2e\x74\x79\x70\x65\x21\x3d\x3d\x22\x63\x6c\x69\
+\x63\x6b\x22\x29\x29\x7b\x6d\x3d\x66\x28\x74\x68\x69\x73\x29\x2c\
+\x6d\x2e\x63\x6f\x6e\x74\x65\x78\x74\x3d\x74\x68\x69\x73\x2e\x6f\
+\x77\x6e\x65\x72\x44\x6f\x63\x75\x6d\x65\x6e\x74\x7c\x7c\x74\x68\
+\x69\x73\x3b\x66\x6f\x72\x28\x6c\x3d\x63\x2e\x74\x61\x72\x67\x65\
+\x74\x3b\x6c\x21\x3d\x74\x68\x69\x73\x3b\x6c\x3d\x6c\x2e\x70\x61\
+\x72\x65\x6e\x74\x4e\x6f\x64\x65\x7c\x7c\x74\x68\x69\x73\x29\x7b\
+\x6f\x3d\x7b\x7d\x2c\x71\x3d\x5b\x5d\x2c\x6d\x5b\x30\x5d\x3d\x6c\
+\x3b\x66\x6f\x72\x28\x6a\x3d\x30\x3b\x6a\x3c\x65\x3b\x6a\x2b\x2b\
+\x29\x72\x3d\x64\x5b\x6a\x5d\x2c\x73\x3d\x72\x2e\x73\x65\x6c\x65\
+\x63\x74\x6f\x72\x2c\x6f\x5b\x73\x5d\x3d\x3d\x3d\x62\x26\x26\x28\
+\x6f\x5b\x73\x5d\x3d\x72\x2e\x71\x75\x69\x63\x6b\x3f\x48\x28\x6c\
+\x2c\x72\x2e\x71\x75\x69\x63\x6b\x29\x3a\x6d\x2e\x69\x73\x28\x73\
+\x29\x29\x2c\x6f\x5b\x73\x5d\x26\x26\x71\x2e\x70\x75\x73\x68\x28\
+\x72\x29\x3b\x71\x2e\x6c\x65\x6e\x67\x74\x68\x26\x26\x69\x2e\x70\
+\x75\x73\x68\x28\x7b\x65\x6c\x65\x6d\x3a\x6c\x2c\x6d\x61\x74\x63\
+\x68\x65\x73\x3a\x71\x7d\x29\x7d\x7d\x64\x2e\x6c\x65\x6e\x67\x74\
+\x68\x3e\x65\x26\x26\x69\x2e\x70\x75\x73\x68\x28\x7b\x65\x6c\x65\
+\x6d\x3a\x74\x68\x69\x73\x2c\x6d\x61\x74\x63\x68\x65\x73\x3a\x64\
+\x2e\x73\x6c\x69\x63\x65\x28\x65\x29\x7d\x29\x3b\x66\x6f\x72\x28\
+\x6a\x3d\x30\x3b\x6a\x3c\x69\x2e\x6c\x65\x6e\x67\x74\x68\x26\x26\
+\x21\x63\x2e\x69\x73\x50\x72\x6f\x70\x61\x67\x61\x74\x69\x6f\x6e\
+\x53\x74\x6f\x70\x70\x65\x64\x28\x29\x3b\x6a\x2b\x2b\x29\x7b\x70\
+\x3d\x69\x5b\x6a\x5d\x2c\x63\x2e\x63\x75\x72\x72\x65\x6e\x74\x54\
+\x61\x72\x67\x65\x74\x3d\x70\x2e\x65\x6c\x65\x6d\x3b\x66\x6f\x72\
+\x28\x6b\x3d\x30\x3b\x6b\x3c\x70\x2e\x6d\x61\x74\x63\x68\x65\x73\
+\x2e\x6c\x65\x6e\x67\x74\x68\x26\x26\x21\x63\x2e\x69\x73\x49\x6d\
+\x6d\x65\x64\x69\x61\x74\x65\x50\x72\x6f\x70\x61\x67\x61\x74\x69\
+\x6f\x6e\x53\x74\x6f\x70\x70\x65\x64\x28\x29\x3b\x6b\x2b\x2b\x29\
+\x7b\x72\x3d\x70\x2e\x6d\x61\x74\x63\x68\x65\x73\x5b\x6b\x5d\x3b\
+\x69\x66\x28\x68\x7c\x7c\x21\x63\x2e\x6e\x61\x6d\x65\x73\x70\x61\
+\x63\x65\x26\x26\x21\x72\x2e\x6e\x61\x6d\x65\x73\x70\x61\x63\x65\
+\x7c\x7c\x63\x2e\x6e\x61\x6d\x65\x73\x70\x61\x63\x65\x5f\x72\x65\
+\x26\x26\x63\x2e\x6e\x61\x6d\x65\x73\x70\x61\x63\x65\x5f\x72\x65\
+\x2e\x74\x65\x73\x74\x28\x72\x2e\x6e\x61\x6d\x65\x73\x70\x61\x63\
+\x65\x29\x29\x63\x2e\x64\x61\x74\x61\x3d\x72\x2e\x64\x61\x74\x61\
+\x2c\x63\x2e\x68\x61\x6e\x64\x6c\x65\x4f\x62\x6a\x3d\x72\x2c\x6e\
+\x3d\x28\x28\x66\x2e\x65\x76\x65\x6e\x74\x2e\x73\x70\x65\x63\x69\
+\x61\x6c\x5b\x72\x2e\x6f\x72\x69\x67\x54\x79\x70\x65\x5d\x7c\x7c\
+\x7b\x7d\x29\x2e\x68\x61\x6e\x64\x6c\x65\x7c\x7c\x72\x2e\x68\x61\
+\x6e\x64\x6c\x65\x72\x29\x2e\x61\x70\x70\x6c\x79\x28\x70\x2e\x65\
+\x6c\x65\x6d\x2c\x67\x29\x2c\x6e\x21\x3d\x3d\x62\x26\x26\x28\x63\
+\x2e\x72\x65\x73\x75\x6c\x74\x3d\x6e\x2c\x6e\x3d\x3d\x3d\x21\x31\
+\x26\x26\x28\x63\x2e\x70\x72\x65\x76\x65\x6e\x74\x44\x65\x66\x61\
+\x75\x6c\x74\x28\x29\x2c\x63\x2e\x73\x74\x6f\x70\x50\x72\x6f\x70\
+\x61\x67\x61\x74\x69\x6f\x6e\x28\x29\x29\x29\x7d\x7d\x72\x65\x74\
+\x75\x72\x6e\x20\x63\x2e\x72\x65\x73\x75\x6c\x74\x7d\x2c\x70\x72\
+\x6f\x70\x73\x3a\x22\x61\x74\x74\x72\x43\x68\x61\x6e\x67\x65\x20\
+\x61\x74\x74\x72\x4e\x61\x6d\x65\x20\x72\x65\x6c\x61\x74\x65\x64\
+\x4e\x6f\x64\x65\x20\x73\x72\x63\x45\x6c\x65\x6d\x65\x6e\x74\x20\
+\x61\x6c\x74\x4b\x65\x79\x20\x62\x75\x62\x62\x6c\x65\x73\x20\x63\
+\x61\x6e\x63\x65\x6c\x61\x62\x6c\x65\x20\x63\x74\x72\x6c\x4b\x65\
+\x79\x20\x63\x75\x72\x72\x65\x6e\x74\x54\x61\x72\x67\x65\x74\x20\
+\x65\x76\x65\x6e\x74\x50\x68\x61\x73\x65\x20\x6d\x65\x74\x61\x4b\
+\x65\x79\x20\x72\x65\x6c\x61\x74\x65\x64\x54\x61\x72\x67\x65\x74\
+\x20\x73\x68\x69\x66\x74\x4b\x65\x79\x20\x74\x61\x72\x67\x65\x74\
+\x20\x74\x69\x6d\x65\x53\x74\x61\x6d\x70\x20\x76\x69\x65\x77\x20\
+\x77\x68\x69\x63\x68\x22\x2e\x73\x70\x6c\x69\x74\x28\x22\x20\x22\
+\x29\x2c\x66\x69\x78\x48\x6f\x6f\x6b\x73\x3a\x7b\x7d\x2c\x6b\x65\
+\x79\x48\x6f\x6f\x6b\x73\x3a\x7b\x70\x72\x6f\x70\x73\x3a\x22\x63\
+\x68\x61\x72\x20\x63\x68\x61\x72\x43\x6f\x64\x65\x20\x6b\x65\x79\
+\x20\x6b\x65\x79\x43\x6f\x64\x65\x22\x2e\x73\x70\x6c\x69\x74\x28\
+\x22\x20\x22\x29\x2c\x66\x69\x6c\x74\x65\x72\x3a\x66\x75\x6e\x63\
+\x74\x69\x6f\x6e\x28\x61\x2c\x62\x29\x7b\x61\x2e\x77\x68\x69\x63\
+\x68\x3d\x3d\x6e\x75\x6c\x6c\x26\x26\x28\x61\x2e\x77\x68\x69\x63\
+\x68\x3d\x62\x2e\x63\x68\x61\x72\x43\x6f\x64\x65\x21\x3d\x6e\x75\
+\x6c\x6c\x3f\x62\x2e\x63\x68\x61\x72\x43\x6f\x64\x65\x3a\x62\x2e\
+\x6b\x65\x79\x43\x6f\x64\x65\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\
+\x61\x7d\x7d\x2c\x6d\x6f\x75\x73\x65\x48\x6f\x6f\x6b\x73\x3a\x7b\
+\x70\x72\x6f\x70\x73\x3a\x22\x62\x75\x74\x74\x6f\x6e\x20\x62\x75\
+\x74\x74\x6f\x6e\x73\x20\x63\x6c\x69\x65\x6e\x74\x58\x20\x63\x6c\
+\x69\x65\x6e\x74\x59\x20\x66\x72\x6f\x6d\x45\x6c\x65\x6d\x65\x6e\
+\x74\x20\x6f\x66\x66\x73\x65\x74\x58\x20\x6f\x66\x66\x73\x65\x74\
+\x59\x20\x70\x61\x67\x65\x58\x20\x70\x61\x67\x65\x59\x20\x73\x63\
+\x72\x65\x65\x6e\x58\x20\x73\x63\x72\x65\x65\x6e\x59\x20\x74\x6f\
+\x45\x6c\x65\x6d\x65\x6e\x74\x22\x2e\x73\x70\x6c\x69\x74\x28\x22\
+\x20\x22\x29\x2c\x66\x69\x6c\x74\x65\x72\x3a\x66\x75\x6e\x63\x74\
+\x69\x6f\x6e\x28\x61\x2c\x64\x29\x7b\x76\x61\x72\x20\x65\x2c\x66\
+\x2c\x67\x2c\x68\x3d\x64\x2e\x62\x75\x74\x74\x6f\x6e\x2c\x69\x3d\
+\x64\x2e\x66\x72\x6f\x6d\x45\x6c\x65\x6d\x65\x6e\x74\x3b\x61\x2e\
+\x70\x61\x67\x65\x58\x3d\x3d\x6e\x75\x6c\x6c\x26\x26\x64\x2e\x63\
+\x6c\x69\x65\x6e\x74\x58\x21\x3d\x6e\x75\x6c\x6c\x26\x26\x28\x65\
+\x3d\x61\x2e\x74\x61\x72\x67\x65\x74\x2e\x6f\x77\x6e\x65\x72\x44\
+\x6f\x63\x75\x6d\x65\x6e\x74\x7c\x7c\x63\x2c\x66\x3d\x65\x2e\x64\
+\x6f\x63\x75\x6d\x65\x6e\x74\x45\x6c\x65\x6d\x65\x6e\x74\x2c\x67\
+\x3d\x65\x2e\x62\x6f\x64\x79\x2c\x61\x2e\x70\x61\x67\x65\x58\x3d\
+\x64\x2e\x63\x6c\x69\x65\x6e\x74\x58\x2b\x28\x66\x26\x26\x66\x2e\
+\x73\x63\x72\x6f\x6c\x6c\x4c\x65\x66\x74\x7c\x7c\x67\x26\x26\x67\
+\x2e\x73\x63\x72\x6f\x6c\x6c\x4c\x65\x66\x74\x7c\x7c\x30\x29\x2d\
+\x28\x66\x26\x26\x66\x2e\x63\x6c\x69\x65\x6e\x74\x4c\x65\x66\x74\
+\x7c\x7c\x67\x26\x26\x67\x2e\x63\x6c\x69\x65\x6e\x74\x4c\x65\x66\
+\x74\x7c\x7c\x30\x29\x2c\x61\x2e\x70\x61\x67\x65\x59\x3d\x64\x2e\
+\x63\x6c\x69\x65\x6e\x74\x59\x2b\x28\x66\x26\x26\x66\x2e\x73\x63\
+\x72\x6f\x6c\x6c\x54\x6f\x70\x7c\x7c\x67\x26\x26\x67\x2e\x73\x63\
+\x72\x6f\x6c\x6c\x54\x6f\x70\x7c\x7c\x30\x29\x2d\x28\x66\x26\x26\
+\x66\x2e\x63\x6c\x69\x65\x6e\x74\x54\x6f\x70\x7c\x7c\x67\x26\x26\
+\x67\x2e\x63\x6c\x69\x65\x6e\x74\x54\x6f\x70\x7c\x7c\x30\x29\x29\
+\x2c\x21\x61\x2e\x72\x65\x6c\x61\x74\x65\x64\x54\x61\x72\x67\x65\
+\x74\x26\x26\x69\x26\x26\x28\x61\x2e\x72\x65\x6c\x61\x74\x65\x64\
+\x54\x61\x72\x67\x65\x74\x3d\x69\x3d\x3d\x3d\x61\x2e\x74\x61\x72\
+\x67\x65\x74\x3f\x64\x2e\x74\x6f\x45\x6c\x65\x6d\x65\x6e\x74\x3a\
+\x69\x29\x2c\x21\x61\x2e\x77\x68\x69\x63\x68\x26\x26\x68\x21\x3d\
+\x3d\x62\x26\x26\x28\x61\x2e\x77\x68\x69\x63\x68\x3d\x68\x26\x31\
+\x3f\x31\x3a\x68\x26\x32\x3f\x33\x3a\x68\x26\x34\x3f\x32\x3a\x30\
+\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x61\x7d\x7d\x2c\x66\x69\x78\
+\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x69\x66\x28\
+\x61\x5b\x66\x2e\x65\x78\x70\x61\x6e\x64\x6f\x5d\x29\x72\x65\x74\
+\x75\x72\x6e\x20\x61\x3b\x76\x61\x72\x20\x64\x2c\x65\x2c\x67\x3d\
+\x61\x2c\x68\x3d\x66\x2e\x65\x76\x65\x6e\x74\x2e\x66\x69\x78\x48\
+\x6f\x6f\x6b\x73\x5b\x61\x2e\x74\x79\x70\x65\x5d\x7c\x7c\x7b\x7d\
+\x2c\x69\x3d\x68\x2e\x70\x72\x6f\x70\x73\x3f\x74\x68\x69\x73\x2e\
+\x70\x72\x6f\x70\x73\x2e\x63\x6f\x6e\x63\x61\x74\x28\x68\x2e\x70\
+\x72\x6f\x70\x73\x29\x3a\x74\x68\x69\x73\x2e\x70\x72\x6f\x70\x73\
+\x3b\x61\x3d\x66\x2e\x45\x76\x65\x6e\x74\x28\x67\x29\x3b\x66\x6f\
+\x72\x28\x64\x3d\x69\x2e\x6c\x65\x6e\x67\x74\x68\x3b\x64\x3b\x29\
+\x65\x3d\x69\x5b\x2d\x2d\x64\x5d\x2c\x61\x5b\x65\x5d\x3d\x67\x5b\
+\x65\x5d\x3b\x61\x2e\x74\x61\x72\x67\x65\x74\x7c\x7c\x28\x61\x2e\
+\x74\x61\x72\x67\x65\x74\x3d\x67\x2e\x73\x72\x63\x45\x6c\x65\x6d\
+\x65\x6e\x74\x7c\x7c\x63\x29\x2c\x61\x2e\x74\x61\x72\x67\x65\x74\
+\x2e\x6e\x6f\x64\x65\x54\x79\x70\x65\x3d\x3d\x3d\x33\x26\x26\x28\
+\x61\x2e\x74\x61\x72\x67\x65\x74\x3d\x61\x2e\x74\x61\x72\x67\x65\
+\x74\x2e\x70\x61\x72\x65\x6e\x74\x4e\x6f\x64\x65\x29\x2c\x61\x2e\
+\x6d\x65\x74\x61\x4b\x65\x79\x3d\x3d\x3d\x62\x26\x26\x28\x61\x2e\
+\x6d\x65\x74\x61\x4b\x65\x79\x3d\x61\x2e\x63\x74\x72\x6c\x4b\x65\
+\x79\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x68\x2e\x66\x69\x6c\x74\
+\x65\x72\x3f\x68\x2e\x66\x69\x6c\x74\x65\x72\x28\x61\x2c\x67\x29\
+\x3a\x61\x7d\x2c\x73\x70\x65\x63\x69\x61\x6c\x3a\x7b\x72\x65\x61\
+\x64\x79\x3a\x7b\x73\x65\x74\x75\x70\x3a\x66\x2e\x62\x69\x6e\x64\
+\x52\x65\x61\x64\x79\x7d\x2c\x6c\x6f\x61\x64\x3a\x7b\x6e\x6f\x42\
+\x75\x62\x62\x6c\x65\x3a\x21\x30\x7d\x2c\x66\x6f\x63\x75\x73\x3a\
+\x7b\x64\x65\x6c\x65\x67\x61\x74\x65\x54\x79\x70\x65\x3a\x22\x66\
+\x6f\x63\x75\x73\x69\x6e\x22\x7d\x2c\x62\x6c\x75\x72\x3a\x7b\x64\
+\x65\x6c\x65\x67\x61\x74\x65\x54\x79\x70\x65\x3a\x22\x66\x6f\x63\
+\x75\x73\x6f\x75\x74\x22\x7d\x2c\x62\x65\x66\x6f\x72\x65\x75\x6e\
+\x6c\x6f\x61\x64\x3a\x7b\x73\x65\x74\x75\x70\x3a\x66\x75\x6e\x63\
+\x74\x69\x6f\x6e\x28\x61\x2c\x62\x2c\x63\x29\x7b\x66\x2e\x69\x73\
+\x57\x69\x6e\x64\x6f\x77\x28\x74\x68\x69\x73\x29\x26\x26\x28\x74\
+\x68\x69\x73\x2e\x6f\x6e\x62\x65\x66\x6f\x72\x65\x75\x6e\x6c\x6f\
+\x61\x64\x3d\x63\x29\x7d\x2c\x74\x65\x61\x72\x64\x6f\x77\x6e\x3a\
+\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x29\x7b\x74\x68\
+\x69\x73\x2e\x6f\x6e\x62\x65\x66\x6f\x72\x65\x75\x6e\x6c\x6f\x61\
+\x64\x3d\x3d\x3d\x62\x26\x26\x28\x74\x68\x69\x73\x2e\x6f\x6e\x62\
+\x65\x66\x6f\x72\x65\x75\x6e\x6c\x6f\x61\x64\x3d\x6e\x75\x6c\x6c\
+\x29\x7d\x7d\x7d\x2c\x73\x69\x6d\x75\x6c\x61\x74\x65\x3a\x66\x75\
+\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x2c\x63\x2c\x64\x29\x7b\
+\x76\x61\x72\x20\x65\x3d\x66\x2e\x65\x78\x74\x65\x6e\x64\x28\x6e\
+\x65\x77\x20\x66\x2e\x45\x76\x65\x6e\x74\x2c\x63\x2c\x7b\x74\x79\
+\x70\x65\x3a\x61\x2c\x69\x73\x53\x69\x6d\x75\x6c\x61\x74\x65\x64\
+\x3a\x21\x30\x2c\x6f\x72\x69\x67\x69\x6e\x61\x6c\x45\x76\x65\x6e\
+\x74\x3a\x7b\x7d\x7d\x29\x3b\x64\x3f\x66\x2e\x65\x76\x65\x6e\x74\
+\x2e\x74\x72\x69\x67\x67\x65\x72\x28\x65\x2c\x6e\x75\x6c\x6c\x2c\
+\x62\x29\x3a\x66\x2e\x65\x76\x65\x6e\x74\x2e\x64\x69\x73\x70\x61\
+\x74\x63\x68\x2e\x63\x61\x6c\x6c\x28\x62\x2c\x65\x29\x2c\x65\x2e\
+\x69\x73\x44\x65\x66\x61\x75\x6c\x74\x50\x72\x65\x76\x65\x6e\x74\
+\x65\x64\x28\x29\x26\x26\x63\x2e\x70\x72\x65\x76\x65\x6e\x74\x44\
+\x65\x66\x61\x75\x6c\x74\x28\x29\x7d\x7d\x2c\x66\x2e\x65\x76\x65\
+\x6e\x74\x2e\x68\x61\x6e\x64\x6c\x65\x3d\x66\x2e\x65\x76\x65\x6e\
+\x74\x2e\x64\x69\x73\x70\x61\x74\x63\x68\x2c\x66\x2e\x72\x65\x6d\
+\x6f\x76\x65\x45\x76\x65\x6e\x74\x3d\x63\x2e\x72\x65\x6d\x6f\x76\
+\x65\x45\x76\x65\x6e\x74\x4c\x69\x73\x74\x65\x6e\x65\x72\x3f\x66\
+\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x2c\x63\x29\x7b\x61\
+\x2e\x72\x65\x6d\x6f\x76\x65\x45\x76\x65\x6e\x74\x4c\x69\x73\x74\
+\x65\x6e\x65\x72\x26\x26\x61\x2e\x72\x65\x6d\x6f\x76\x65\x45\x76\
+\x65\x6e\x74\x4c\x69\x73\x74\x65\x6e\x65\x72\x28\x62\x2c\x63\x2c\
+\x21\x31\x29\x7d\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\
+\x62\x2c\x63\x29\x7b\x61\x2e\x64\x65\x74\x61\x63\x68\x45\x76\x65\
+\x6e\x74\x26\x26\x61\x2e\x64\x65\x74\x61\x63\x68\x45\x76\x65\x6e\
+\x74\x28\x22\x6f\x6e\x22\x2b\x62\x2c\x63\x29\x7d\x2c\x66\x2e\x45\
+\x76\x65\x6e\x74\x3d\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\
+\x62\x29\x7b\x69\x66\x28\x21\x28\x74\x68\x69\x73\x20\x69\x6e\x73\
+\x74\x61\x6e\x63\x65\x6f\x66\x20\x66\x2e\x45\x76\x65\x6e\x74\x29\
+\x29\x72\x65\x74\x75\x72\x6e\x20\x6e\x65\x77\x20\x66\x2e\x45\x76\
+\x65\x6e\x74\x28\x61\x2c\x62\x29\x3b\x61\x26\x26\x61\x2e\x74\x79\
+\x70\x65\x3f\x28\x74\x68\x69\x73\x2e\x6f\x72\x69\x67\x69\x6e\x61\
+\x6c\x45\x76\x65\x6e\x74\x3d\x61\x2c\x74\x68\x69\x73\x2e\x74\x79\
+\x70\x65\x3d\x61\x2e\x74\x79\x70\x65\x2c\x74\x68\x69\x73\x2e\x69\
+\x73\x44\x65\x66\x61\x75\x6c\x74\x50\x72\x65\x76\x65\x6e\x74\x65\
+\x64\x3d\x61\x2e\x64\x65\x66\x61\x75\x6c\x74\x50\x72\x65\x76\x65\
+\x6e\x74\x65\x64\x7c\x7c\x61\x2e\x72\x65\x74\x75\x72\x6e\x56\x61\
+\x6c\x75\x65\x3d\x3d\x3d\x21\x31\x7c\x7c\x61\x2e\x67\x65\x74\x50\
+\x72\x65\x76\x65\x6e\x74\x44\x65\x66\x61\x75\x6c\x74\x26\x26\x61\
+\x2e\x67\x65\x74\x50\x72\x65\x76\x65\x6e\x74\x44\x65\x66\x61\x75\
+\x6c\x74\x28\x29\x3f\x4b\x3a\x4a\x29\x3a\x74\x68\x69\x73\x2e\x74\
+\x79\x70\x65\x3d\x61\x2c\x62\x26\x26\x66\x2e\x65\x78\x74\x65\x6e\
+\x64\x28\x74\x68\x69\x73\x2c\x62\x29\x2c\x74\x68\x69\x73\x2e\x74\
+\x69\x6d\x65\x53\x74\x61\x6d\x70\x3d\x61\x26\x26\x61\x2e\x74\x69\
+\x6d\x65\x53\x74\x61\x6d\x70\x7c\x7c\x66\x2e\x6e\x6f\x77\x28\x29\
+\x2c\x74\x68\x69\x73\x5b\x66\x2e\x65\x78\x70\x61\x6e\x64\x6f\x5d\
+\x3d\x21\x30\x7d\x2c\x66\x2e\x45\x76\x65\x6e\x74\x2e\x70\x72\x6f\
+\x74\x6f\x74\x79\x70\x65\x3d\x7b\x70\x72\x65\x76\x65\x6e\x74\x44\
+\x65\x66\x61\x75\x6c\x74\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\
+\x29\x7b\x74\x68\x69\x73\x2e\x69\x73\x44\x65\x66\x61\x75\x6c\x74\
+\x50\x72\x65\x76\x65\x6e\x74\x65\x64\x3d\x4b\x3b\x76\x61\x72\x20\
+\x61\x3d\x74\x68\x69\x73\x2e\x6f\x72\x69\x67\x69\x6e\x61\x6c\x45\
+\x76\x65\x6e\x74\x3b\x21\x61\x7c\x7c\x28\x61\x2e\x70\x72\x65\x76\
+\x65\x6e\x74\x44\x65\x66\x61\x75\x6c\x74\x3f\x61\x2e\x70\x72\x65\
+\x76\x65\x6e\x74\x44\x65\x66\x61\x75\x6c\x74\x28\x29\x3a\x61\x2e\
+\x72\x65\x74\x75\x72\x6e\x56\x61\x6c\x75\x65\x3d\x21\x31\x29\x7d\
+\x2c\x73\x74\x6f\x70\x50\x72\x6f\x70\x61\x67\x61\x74\x69\x6f\x6e\
+\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x29\x7b\x74\x68\x69\x73\
+\x2e\x69\x73\x50\x72\x6f\x70\x61\x67\x61\x74\x69\x6f\x6e\x53\x74\
+\x6f\x70\x70\x65\x64\x3d\x4b\x3b\x76\x61\x72\x20\x61\x3d\x74\x68\
+\x69\x73\x2e\x6f\x72\x69\x67\x69\x6e\x61\x6c\x45\x76\x65\x6e\x74\
+\x3b\x21\x61\x7c\x7c\x28\x61\x2e\x73\x74\x6f\x70\x50\x72\x6f\x70\
+\x61\x67\x61\x74\x69\x6f\x6e\x26\x26\x61\x2e\x73\x74\x6f\x70\x50\
+\x72\x6f\x70\x61\x67\x61\x74\x69\x6f\x6e\x28\x29\x2c\x61\x2e\x63\
+\x61\x6e\x63\x65\x6c\x42\x75\x62\x62\x6c\x65\x3d\x21\x30\x29\x7d\
+\x2c\x73\x74\x6f\x70\x49\x6d\x6d\x65\x64\x69\x61\x74\x65\x50\x72\
+\x6f\x70\x61\x67\x61\x74\x69\x6f\x6e\x3a\x66\x75\x6e\x63\x74\x69\
+\x6f\x6e\x28\x29\x7b\x74\x68\x69\x73\x2e\x69\x73\x49\x6d\x6d\x65\
+\x64\x69\x61\x74\x65\x50\x72\x6f\x70\x61\x67\x61\x74\x69\x6f\x6e\
+\x53\x74\x6f\x70\x70\x65\x64\x3d\x4b\x2c\x74\x68\x69\x73\x2e\x73\
+\x74\x6f\x70\x50\x72\x6f\x70\x61\x67\x61\x74\x69\x6f\x6e\x28\x29\
+\x7d\x2c\x69\x73\x44\x65\x66\x61\x75\x6c\x74\x50\x72\x65\x76\x65\
+\x6e\x74\x65\x64\x3a\x4a\x2c\x69\x73\x50\x72\x6f\x70\x61\x67\x61\
+\x74\x69\x6f\x6e\x53\x74\x6f\x70\x70\x65\x64\x3a\x4a\x2c\x69\x73\
+\x49\x6d\x6d\x65\x64\x69\x61\x74\x65\x50\x72\x6f\x70\x61\x67\x61\
+\x74\x69\x6f\x6e\x53\x74\x6f\x70\x70\x65\x64\x3a\x4a\x7d\x2c\x66\
+\x2e\x65\x61\x63\x68\x28\x7b\x6d\x6f\x75\x73\x65\x65\x6e\x74\x65\
+\x72\x3a\x22\x6d\x6f\x75\x73\x65\x6f\x76\x65\x72\x22\x2c\x6d\x6f\
+\x75\x73\x65\x6c\x65\x61\x76\x65\x3a\x22\x6d\x6f\x75\x73\x65\x6f\
+\x75\x74\x22\x7d\x2c\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\
+\x62\x29\x7b\x66\x2e\x65\x76\x65\x6e\x74\x2e\x73\x70\x65\x63\x69\
+\x61\x6c\x5b\x61\x5d\x3d\x7b\x64\x65\x6c\x65\x67\x61\x74\x65\x54\
+\x79\x70\x65\x3a\x62\x2c\x62\x69\x6e\x64\x54\x79\x70\x65\x3a\x62\
+\x2c\x68\x61\x6e\x64\x6c\x65\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\
+\x28\x61\x29\x7b\x76\x61\x72\x20\x63\x3d\x74\x68\x69\x73\x2c\x64\
+\x3d\x61\x2e\x72\x65\x6c\x61\x74\x65\x64\x54\x61\x72\x67\x65\x74\
+\x2c\x65\x3d\x61\x2e\x68\x61\x6e\x64\x6c\x65\x4f\x62\x6a\x2c\x67\
+\x3d\x65\x2e\x73\x65\x6c\x65\x63\x74\x6f\x72\x2c\x68\x3b\x69\x66\
+\x28\x21\x64\x7c\x7c\x64\x21\x3d\x3d\x63\x26\x26\x21\x66\x2e\x63\
+\x6f\x6e\x74\x61\x69\x6e\x73\x28\x63\x2c\x64\x29\x29\x61\x2e\x74\
+\x79\x70\x65\x3d\x65\x2e\x6f\x72\x69\x67\x54\x79\x70\x65\x2c\x68\
+\x3d\x65\x2e\x68\x61\x6e\x64\x6c\x65\x72\x2e\x61\x70\x70\x6c\x79\
+\x28\x74\x68\x69\x73\x2c\x61\x72\x67\x75\x6d\x65\x6e\x74\x73\x29\
+\x2c\x61\x2e\x74\x79\x70\x65\x3d\x62\x3b\x72\x65\x74\x75\x72\x6e\
+\x20\x68\x7d\x7d\x7d\x29\x2c\x66\x2e\x73\x75\x70\x70\x6f\x72\x74\
+\x2e\x73\x75\x62\x6d\x69\x74\x42\x75\x62\x62\x6c\x65\x73\x7c\x7c\
+\x28\x66\x2e\x65\x76\x65\x6e\x74\x2e\x73\x70\x65\x63\x69\x61\x6c\
+\x2e\x73\x75\x62\x6d\x69\x74\x3d\x7b\x73\x65\x74\x75\x70\x3a\x66\
+\x75\x6e\x63\x74\x69\x6f\x6e\x28\x29\x7b\x69\x66\x28\x66\x2e\x6e\
+\x6f\x64\x65\x4e\x61\x6d\x65\x28\x74\x68\x69\x73\x2c\x22\x66\x6f\
+\x72\x6d\x22\x29\x29\x72\x65\x74\x75\x72\x6e\x21\x31\x3b\x66\x2e\
+\x65\x76\x65\x6e\x74\x2e\x61\x64\x64\x28\x74\x68\x69\x73\x2c\x22\
+\x63\x6c\x69\x63\x6b\x2e\x5f\x73\x75\x62\x6d\x69\x74\x20\x6b\x65\
+\x79\x70\x72\x65\x73\x73\x2e\x5f\x73\x75\x62\x6d\x69\x74\x22\x2c\
+\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x76\x61\x72\x20\
+\x63\x3d\x61\x2e\x74\x61\x72\x67\x65\x74\x2c\x64\x3d\x66\x2e\x6e\
+\x6f\x64\x65\x4e\x61\x6d\x65\x28\x63\x2c\x22\x69\x6e\x70\x75\x74\
+\x22\x29\x7c\x7c\x66\x2e\x6e\x6f\x64\x65\x4e\x61\x6d\x65\x28\x63\
+\x2c\x22\x62\x75\x74\x74\x6f\x6e\x22\x29\x3f\x63\x2e\x66\x6f\x72\
+\x6d\x3a\x62\x3b\x64\x26\x26\x21\x64\x2e\x5f\x73\x75\x62\x6d\x69\
+\x74\x5f\x61\x74\x74\x61\x63\x68\x65\x64\x26\x26\x28\x66\x2e\x65\
+\x76\x65\x6e\x74\x2e\x61\x64\x64\x28\x64\x2c\x22\x73\x75\x62\x6d\
+\x69\x74\x2e\x5f\x73\x75\x62\x6d\x69\x74\x22\x2c\x66\x75\x6e\x63\
+\x74\x69\x6f\x6e\x28\x61\x29\x7b\x74\x68\x69\x73\x2e\x70\x61\x72\
+\x65\x6e\x74\x4e\x6f\x64\x65\x26\x26\x21\x61\x2e\x69\x73\x54\x72\
+\x69\x67\x67\x65\x72\x26\x26\x66\x2e\x65\x76\x65\x6e\x74\x2e\x73\
+\x69\x6d\x75\x6c\x61\x74\x65\x28\x22\x73\x75\x62\x6d\x69\x74\x22\
+\x2c\x74\x68\x69\x73\x2e\x70\x61\x72\x65\x6e\x74\x4e\x6f\x64\x65\
+\x2c\x61\x2c\x21\x30\x29\x7d\x29\x2c\x64\x2e\x5f\x73\x75\x62\x6d\
+\x69\x74\x5f\x61\x74\x74\x61\x63\x68\x65\x64\x3d\x21\x30\x29\x7d\
+\x29\x7d\x2c\x74\x65\x61\x72\x64\x6f\x77\x6e\x3a\x66\x75\x6e\x63\
+\x74\x69\x6f\x6e\x28\x29\x7b\x69\x66\x28\x66\x2e\x6e\x6f\x64\x65\
+\x4e\x61\x6d\x65\x28\x74\x68\x69\x73\x2c\x22\x66\x6f\x72\x6d\x22\
+\x29\x29\x72\x65\x74\x75\x72\x6e\x21\x31\x3b\x66\x2e\x65\x76\x65\
+\x6e\x74\x2e\x72\x65\x6d\x6f\x76\x65\x28\x74\x68\x69\x73\x2c\x22\
+\x2e\x5f\x73\x75\x62\x6d\x69\x74\x22\x29\x7d\x7d\x29\x2c\x66\x2e\
+\x73\x75\x70\x70\x6f\x72\x74\x2e\x63\x68\x61\x6e\x67\x65\x42\x75\
+\x62\x62\x6c\x65\x73\x7c\x7c\x28\x66\x2e\x65\x76\x65\x6e\x74\x2e\
+\x73\x70\x65\x63\x69\x61\x6c\x2e\x63\x68\x61\x6e\x67\x65\x3d\x7b\
+\x73\x65\x74\x75\x70\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x29\
+\x7b\x69\x66\x28\x7a\x2e\x74\x65\x73\x74\x28\x74\x68\x69\x73\x2e\
+\x6e\x6f\x64\x65\x4e\x61\x6d\x65\x29\x29\x7b\x69\x66\x28\x74\x68\
+\x69\x73\x2e\x74\x79\x70\x65\x3d\x3d\x3d\x22\x63\x68\x65\x63\x6b\
+\x62\x6f\x78\x22\x7c\x7c\x74\x68\x69\x73\x2e\x74\x79\x70\x65\x3d\
+\x3d\x3d\x22\x72\x61\x64\x69\x6f\x22\x29\x66\x2e\x65\x76\x65\x6e\
+\x74\x2e\x61\x64\x64\x28\x74\x68\x69\x73\x2c\x22\x70\x72\x6f\x70\
+\x65\x72\x74\x79\x63\x68\x61\x6e\x67\x65\x2e\x5f\x63\x68\x61\x6e\
+\x67\x65\x22\x2c\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\
+\x61\x2e\x6f\x72\x69\x67\x69\x6e\x61\x6c\x45\x76\x65\x6e\x74\x2e\
+\x70\x72\x6f\x70\x65\x72\x74\x79\x4e\x61\x6d\x65\x3d\x3d\x3d\x22\
+\x63\x68\x65\x63\x6b\x65\x64\x22\x26\x26\x28\x74\x68\x69\x73\x2e\
+\x5f\x6a\x75\x73\x74\x5f\x63\x68\x61\x6e\x67\x65\x64\x3d\x21\x30\
+\x29\x7d\x29\x2c\x66\x2e\x65\x76\x65\x6e\x74\x2e\x61\x64\x64\x28\
+\x74\x68\x69\x73\x2c\x22\x63\x6c\x69\x63\x6b\x2e\x5f\x63\x68\x61\
+\x6e\x67\x65\x22\x2c\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\
+\x7b\x74\x68\x69\x73\x2e\x5f\x6a\x75\x73\x74\x5f\x63\x68\x61\x6e\
+\x67\x65\x64\x26\x26\x21\x61\x2e\x69\x73\x54\x72\x69\x67\x67\x65\
+\x72\x26\x26\x28\x74\x68\x69\x73\x2e\x5f\x6a\x75\x73\x74\x5f\x63\
+\x68\x61\x6e\x67\x65\x64\x3d\x21\x31\x2c\x66\x2e\x65\x76\x65\x6e\
+\x74\x2e\x73\x69\x6d\x75\x6c\x61\x74\x65\x28\x22\x63\x68\x61\x6e\
+\x67\x65\x22\x2c\x74\x68\x69\x73\x2c\x61\x2c\x21\x30\x29\x29\x7d\
+\x29\x3b\x72\x65\x74\x75\x72\x6e\x21\x31\x7d\x66\x2e\x65\x76\x65\
+\x6e\x74\x2e\x61\x64\x64\x28\x74\x68\x69\x73\x2c\x22\x62\x65\x66\
+\x6f\x72\x65\x61\x63\x74\x69\x76\x61\x74\x65\x2e\x5f\x63\x68\x61\
+\x6e\x67\x65\x22\x2c\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\
+\x7b\x76\x61\x72\x20\x62\x3d\x61\x2e\x74\x61\x72\x67\x65\x74\x3b\
+\x7a\x2e\x74\x65\x73\x74\x28\x62\x2e\x6e\x6f\x64\x65\x4e\x61\x6d\
+\x65\x29\x26\x26\x21\x62\x2e\x5f\x63\x68\x61\x6e\x67\x65\x5f\x61\
+\x74\x74\x61\x63\x68\x65\x64\x26\x26\x28\x66\x2e\x65\x76\x65\x6e\
+\x74\x2e\x61\x64\x64\x28\x62\x2c\x22\x63\x68\x61\x6e\x67\x65\x2e\
+\x5f\x63\x68\x61\x6e\x67\x65\x22\x2c\x66\x75\x6e\x63\x74\x69\x6f\
+\x6e\x28\x61\x29\x7b\x74\x68\x69\x73\x2e\x70\x61\x72\x65\x6e\x74\
+\x4e\x6f\x64\x65\x26\x26\x21\x61\x2e\x69\x73\x53\x69\x6d\x75\x6c\
+\x61\x74\x65\x64\x26\x26\x21\x61\x2e\x69\x73\x54\x72\x69\x67\x67\
+\x65\x72\x26\x26\x66\x2e\x65\x76\x65\x6e\x74\x2e\x73\x69\x6d\x75\
+\x6c\x61\x74\x65\x28\x22\x63\x68\x61\x6e\x67\x65\x22\x2c\x74\x68\
+\x69\x73\x2e\x70\x61\x72\x65\x6e\x74\x4e\x6f\x64\x65\x2c\x61\x2c\
+\x21\x30\x29\x7d\x29\x2c\x62\x2e\x5f\x63\x68\x61\x6e\x67\x65\x5f\
+\x61\x74\x74\x61\x63\x68\x65\x64\x3d\x21\x30\x29\x7d\x29\x7d\x2c\
+\x68\x61\x6e\x64\x6c\x65\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\
+\x61\x29\x7b\x76\x61\x72\x20\x62\x3d\x61\x2e\x74\x61\x72\x67\x65\
+\x74\x3b\x69\x66\x28\x74\x68\x69\x73\x21\x3d\x3d\x62\x7c\x7c\x61\
+\x2e\x69\x73\x53\x69\x6d\x75\x6c\x61\x74\x65\x64\x7c\x7c\x61\x2e\
+\x69\x73\x54\x72\x69\x67\x67\x65\x72\x7c\x7c\x62\x2e\x74\x79\x70\
+\x65\x21\x3d\x3d\x22\x72\x61\x64\x69\x6f\x22\x26\x26\x62\x2e\x74\
+\x79\x70\x65\x21\x3d\x3d\x22\x63\x68\x65\x63\x6b\x62\x6f\x78\x22\
+\x29\x72\x65\x74\x75\x72\x6e\x20\x61\x2e\x68\x61\x6e\x64\x6c\x65\
+\x4f\x62\x6a\x2e\x68\x61\x6e\x64\x6c\x65\x72\x2e\x61\x70\x70\x6c\
+\x79\x28\x74\x68\x69\x73\x2c\x61\x72\x67\x75\x6d\x65\x6e\x74\x73\
+\x29\x7d\x2c\x74\x65\x61\x72\x64\x6f\x77\x6e\x3a\x66\x75\x6e\x63\
+\x74\x69\x6f\x6e\x28\x29\x7b\x66\x2e\x65\x76\x65\x6e\x74\x2e\x72\
+\x65\x6d\x6f\x76\x65\x28\x74\x68\x69\x73\x2c\x22\x2e\x5f\x63\x68\
+\x61\x6e\x67\x65\x22\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x7a\x2e\
+\x74\x65\x73\x74\x28\x74\x68\x69\x73\x2e\x6e\x6f\x64\x65\x4e\x61\
+\x6d\x65\x29\x7d\x7d\x29\x2c\x66\x2e\x73\x75\x70\x70\x6f\x72\x74\
+\x2e\x66\x6f\x63\x75\x73\x69\x6e\x42\x75\x62\x62\x6c\x65\x73\x7c\
+\x7c\x66\x2e\x65\x61\x63\x68\x28\x7b\x66\x6f\x63\x75\x73\x3a\x22\
+\x66\x6f\x63\x75\x73\x69\x6e\x22\x2c\x62\x6c\x75\x72\x3a\x22\x66\
+\x6f\x63\x75\x73\x6f\x75\x74\x22\x7d\x2c\x66\x75\x6e\x63\x74\x69\
+\x6f\x6e\x28\x61\x2c\x62\x29\x7b\x76\x61\x72\x20\x64\x3d\x30\x2c\
+\x65\x3d\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x66\x2e\
+\x65\x76\x65\x6e\x74\x2e\x73\x69\x6d\x75\x6c\x61\x74\x65\x28\x62\
+\x2c\x61\x2e\x74\x61\x72\x67\x65\x74\x2c\x66\x2e\x65\x76\x65\x6e\
+\x74\x2e\x66\x69\x78\x28\x61\x29\x2c\x21\x30\x29\x7d\x3b\x66\x2e\
+\x65\x76\x65\x6e\x74\x2e\x73\x70\x65\x63\x69\x61\x6c\x5b\x62\x5d\
+\x3d\x7b\x73\x65\x74\x75\x70\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\
+\x28\x29\x7b\x64\x2b\x2b\x3d\x3d\x3d\x30\x26\x26\x63\x2e\x61\x64\
+\x64\x45\x76\x65\x6e\x74\x4c\x69\x73\x74\x65\x6e\x65\x72\x28\x61\
+\x2c\x65\x2c\x21\x30\x29\x7d\x2c\x74\x65\x61\x72\x64\x6f\x77\x6e\
+\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x29\x7b\x2d\x2d\x64\x3d\
+\x3d\x3d\x30\x26\x26\x63\x2e\x72\x65\x6d\x6f\x76\x65\x45\x76\x65\
+\x6e\x74\x4c\x69\x73\x74\x65\x6e\x65\x72\x28\x61\x2c\x65\x2c\x21\
+\x30\x29\x7d\x7d\x7d\x29\x2c\x66\x2e\x66\x6e\x2e\x65\x78\x74\x65\
+\x6e\x64\x28\x7b\x6f\x6e\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\
+\x61\x2c\x63\x2c\x64\x2c\x65\x2c\x67\x29\x7b\x76\x61\x72\x20\x68\
+\x2c\x69\x3b\x69\x66\x28\x74\x79\x70\x65\x6f\x66\x20\x61\x3d\x3d\
+\x22\x6f\x62\x6a\x65\x63\x74\x22\x29\x7b\x74\x79\x70\x65\x6f\x66\
+\x20\x63\x21\x3d\x22\x73\x74\x72\x69\x6e\x67\x22\x26\x26\x28\x64\
+\x3d\x63\x2c\x63\x3d\x62\x29\x3b\x66\x6f\x72\x28\x69\x20\x69\x6e\
+\x20\x61\x29\x74\x68\x69\x73\x2e\x6f\x6e\x28\x69\x2c\x63\x2c\x64\
+\x2c\x61\x5b\x69\x5d\x2c\x67\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\
+\x74\x68\x69\x73\x7d\x64\x3d\x3d\x6e\x75\x6c\x6c\x26\x26\x65\x3d\
+\x3d\x6e\x75\x6c\x6c\x3f\x28\x65\x3d\x63\x2c\x64\x3d\x63\x3d\x62\
+\x29\x3a\x65\x3d\x3d\x6e\x75\x6c\x6c\x26\x26\x28\x74\x79\x70\x65\
+\x6f\x66\x20\x63\x3d\x3d\x22\x73\x74\x72\x69\x6e\x67\x22\x3f\x28\
+\x65\x3d\x64\x2c\x64\x3d\x62\x29\x3a\x28\x65\x3d\x64\x2c\x64\x3d\
+\x63\x2c\x63\x3d\x62\x29\x29\x3b\x69\x66\x28\x65\x3d\x3d\x3d\x21\
+\x31\x29\x65\x3d\x4a\x3b\x65\x6c\x73\x65\x20\x69\x66\x28\x21\x65\
+\x29\x72\x65\x74\x75\x72\x6e\x20\x74\x68\x69\x73\x3b\x67\x3d\x3d\
+\x3d\x31\x26\x26\x28\x68\x3d\x65\x2c\x65\x3d\x66\x75\x6e\x63\x74\
+\x69\x6f\x6e\x28\x61\x29\x7b\x66\x28\x29\x2e\x6f\x66\x66\x28\x61\
+\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x68\x2e\x61\x70\x70\x6c\x79\
+\x28\x74\x68\x69\x73\x2c\x61\x72\x67\x75\x6d\x65\x6e\x74\x73\x29\
+\x7d\x2c\x65\x2e\x67\x75\x69\x64\x3d\x68\x2e\x67\x75\x69\x64\x7c\
+\x7c\x28\x68\x2e\x67\x75\x69\x64\x3d\x66\x2e\x67\x75\x69\x64\x2b\
+\x2b\x29\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x74\x68\x69\x73\x2e\
+\x65\x61\x63\x68\x28\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x29\x7b\
+\x66\x2e\x65\x76\x65\x6e\x74\x2e\x61\x64\x64\x28\x74\x68\x69\x73\
+\x2c\x61\x2c\x65\x2c\x64\x2c\x63\x29\x7d\x29\x7d\x2c\x6f\x6e\x65\
+\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x2c\x63\x2c\
+\x64\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x74\x68\x69\x73\x2e\x6f\
+\x6e\x2e\x63\x61\x6c\x6c\x28\x74\x68\x69\x73\x2c\x61\x2c\x62\x2c\
+\x63\x2c\x64\x2c\x31\x29\x7d\x2c\x6f\x66\x66\x3a\x66\x75\x6e\x63\
+\x74\x69\x6f\x6e\x28\x61\x2c\x63\x2c\x64\x29\x7b\x69\x66\x28\x61\
+\x26\x26\x61\x2e\x70\x72\x65\x76\x65\x6e\x74\x44\x65\x66\x61\x75\
+\x6c\x74\x26\x26\x61\x2e\x68\x61\x6e\x64\x6c\x65\x4f\x62\x6a\x29\
+\x7b\x76\x61\x72\x20\x65\x3d\x61\x2e\x68\x61\x6e\x64\x6c\x65\x4f\
+\x62\x6a\x3b\x66\x28\x61\x2e\x64\x65\x6c\x65\x67\x61\x74\x65\x54\
+\x61\x72\x67\x65\x74\x29\x2e\x6f\x66\x66\x28\x65\x2e\x6e\x61\x6d\
+\x65\x73\x70\x61\x63\x65\x3f\x65\x2e\x74\x79\x70\x65\x2b\x22\x2e\
+\x22\x2b\x65\x2e\x6e\x61\x6d\x65\x73\x70\x61\x63\x65\x3a\x65\x2e\
+\x74\x79\x70\x65\x2c\x65\x2e\x73\x65\x6c\x65\x63\x74\x6f\x72\x2c\
+\x65\x2e\x68\x61\x6e\x64\x6c\x65\x72\x29\x3b\x72\x65\x74\x75\x72\
+\x6e\x20\x74\x68\x69\x73\x7d\x69\x66\x28\x74\x79\x70\x65\x6f\x66\
+\x20\x61\x3d\x3d\x22\x6f\x62\x6a\x65\x63\x74\x22\x29\x7b\x66\x6f\
+\x72\x28\x76\x61\x72\x20\x67\x20\x69\x6e\x20\x61\x29\x74\x68\x69\
+\x73\x2e\x6f\x66\x66\x28\x67\x2c\x63\x2c\x61\x5b\x67\x5d\x29\x3b\
+\x72\x65\x74\x75\x72\x6e\x20\x74\x68\x69\x73\x7d\x69\x66\x28\x63\
+\x3d\x3d\x3d\x21\x31\x7c\x7c\x74\x79\x70\x65\x6f\x66\x20\x63\x3d\
+\x3d\x22\x66\x75\x6e\x63\x74\x69\x6f\x6e\x22\x29\x64\x3d\x63\x2c\
+\x63\x3d\x62\x3b\x64\x3d\x3d\x3d\x21\x31\x26\x26\x28\x64\x3d\x4a\
+\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x74\x68\x69\x73\x2e\x65\x61\
+\x63\x68\x28\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x29\x7b\x66\x2e\
+\x65\x76\x65\x6e\x74\x2e\x72\x65\x6d\x6f\x76\x65\x28\x74\x68\x69\
+\x73\x2c\x61\x2c\x64\x2c\x63\x29\x7d\x29\x7d\x2c\x62\x69\x6e\x64\
+\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x2c\x63\x29\
+\x7b\x72\x65\x74\x75\x72\x6e\x20\x74\x68\x69\x73\x2e\x6f\x6e\x28\
+\x61\x2c\x6e\x75\x6c\x6c\x2c\x62\x2c\x63\x29\x7d\x2c\x75\x6e\x62\
+\x69\x6e\x64\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\
+\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x74\x68\x69\x73\x2e\x6f\x66\
+\x66\x28\x61\x2c\x6e\x75\x6c\x6c\x2c\x62\x29\x7d\x2c\x6c\x69\x76\
+\x65\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x2c\x63\
+\x29\x7b\x66\x28\x74\x68\x69\x73\x2e\x63\x6f\x6e\x74\x65\x78\x74\
+\x29\x2e\x6f\x6e\x28\x61\x2c\x74\x68\x69\x73\x2e\x73\x65\x6c\x65\
+\x63\x74\x6f\x72\x2c\x62\x2c\x63\x29\x3b\x72\x65\x74\x75\x72\x6e\
+\x20\x74\x68\x69\x73\x7d\x2c\x64\x69\x65\x3a\x66\x75\x6e\x63\x74\
+\x69\x6f\x6e\x28\x61\x2c\x62\x29\x7b\x66\x28\x74\x68\x69\x73\x2e\
+\x63\x6f\x6e\x74\x65\x78\x74\x29\x2e\x6f\x66\x66\x28\x61\x2c\x74\
+\x68\x69\x73\x2e\x73\x65\x6c\x65\x63\x74\x6f\x72\x7c\x7c\x22\x2a\
+\x2a\x22\x2c\x62\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x74\x68\x69\
+\x73\x7d\x2c\x64\x65\x6c\x65\x67\x61\x74\x65\x3a\x66\x75\x6e\x63\
+\x74\x69\x6f\x6e\x28\x61\x2c\x62\x2c\x63\x2c\x64\x29\x7b\x72\x65\
+\x74\x75\x72\x6e\x20\x74\x68\x69\x73\x2e\x6f\x6e\x28\x62\x2c\x61\
+\x2c\x63\x2c\x64\x29\x7d\x2c\x75\x6e\x64\x65\x6c\x65\x67\x61\x74\
+\x65\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x2c\x63\
+\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x61\x72\x67\x75\x6d\x65\x6e\
+\x74\x73\x2e\x6c\x65\x6e\x67\x74\x68\x3d\x3d\x31\x3f\x74\x68\x69\
+\x73\x2e\x6f\x66\x66\x28\x61\x2c\x22\x2a\x2a\x22\x29\x3a\x74\x68\
+\x69\x73\x2e\x6f\x66\x66\x28\x62\x2c\x61\x2c\x63\x29\x7d\x2c\x74\
+\x72\x69\x67\x67\x65\x72\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\
+\x61\x2c\x62\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x74\x68\x69\x73\
+\x2e\x65\x61\x63\x68\x28\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x29\
+\x7b\x66\x2e\x65\x76\x65\x6e\x74\x2e\x74\x72\x69\x67\x67\x65\x72\
+\x28\x61\x2c\x62\x2c\x74\x68\x69\x73\x29\x7d\x29\x7d\x2c\x74\x72\
+\x69\x67\x67\x65\x72\x48\x61\x6e\x64\x6c\x65\x72\x3a\x66\x75\x6e\
+\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x29\x7b\x69\x66\x28\x74\x68\
+\x69\x73\x5b\x30\x5d\x29\x72\x65\x74\x75\x72\x6e\x20\x66\x2e\x65\
+\x76\x65\x6e\x74\x2e\x74\x72\x69\x67\x67\x65\x72\x28\x61\x2c\x62\
+\x2c\x74\x68\x69\x73\x5b\x30\x5d\x2c\x21\x30\x29\x7d\x2c\x74\x6f\
+\x67\x67\x6c\x65\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\
+\x7b\x76\x61\x72\x20\x62\x3d\x61\x72\x67\x75\x6d\x65\x6e\x74\x73\
+\x2c\x63\x3d\x61\x2e\x67\x75\x69\x64\x7c\x7c\x66\x2e\x67\x75\x69\
+\x64\x2b\x2b\x2c\x64\x3d\x30\x2c\x65\x3d\x66\x75\x6e\x63\x74\x69\
+\x6f\x6e\x28\x63\x29\x7b\x76\x61\x72\x20\x65\x3d\x28\x66\x2e\x5f\
+\x64\x61\x74\x61\x28\x74\x68\x69\x73\x2c\x22\x6c\x61\x73\x74\x54\
+\x6f\x67\x67\x6c\x65\x22\x2b\x61\x2e\x67\x75\x69\x64\x29\x7c\x7c\
+\x30\x29\x25\x64\x3b\x66\x2e\x5f\x64\x61\x74\x61\x28\x74\x68\x69\
+\x73\x2c\x22\x6c\x61\x73\x74\x54\x6f\x67\x67\x6c\x65\x22\x2b\x61\
+\x2e\x67\x75\x69\x64\x2c\x65\x2b\x31\x29\x2c\x63\x2e\x70\x72\x65\
+\x76\x65\x6e\x74\x44\x65\x66\x61\x75\x6c\x74\x28\x29\x3b\x72\x65\
+\x74\x75\x72\x6e\x20\x62\x5b\x65\x5d\x2e\x61\x70\x70\x6c\x79\x28\
+\x74\x68\x69\x73\x2c\x61\x72\x67\x75\x6d\x65\x6e\x74\x73\x29\x7c\
+\x7c\x21\x31\x7d\x3b\x65\x2e\x67\x75\x69\x64\x3d\x63\x3b\x77\x68\
+\x69\x6c\x65\x28\x64\x3c\x62\x2e\x6c\x65\x6e\x67\x74\x68\x29\x62\
+\x5b\x64\x2b\x2b\x5d\x2e\x67\x75\x69\x64\x3d\x63\x3b\x72\x65\x74\
+\x75\x72\x6e\x20\x74\x68\x69\x73\x2e\x63\x6c\x69\x63\x6b\x28\x65\
+\x29\x7d\x2c\x68\x6f\x76\x65\x72\x3a\x66\x75\x6e\x63\x74\x69\x6f\
+\x6e\x28\x61\x2c\x62\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x74\x68\
+\x69\x73\x2e\x6d\x6f\x75\x73\x65\x65\x6e\x74\x65\x72\x28\x61\x29\
+\x2e\x6d\x6f\x75\x73\x65\x6c\x65\x61\x76\x65\x28\x62\x7c\x7c\x61\
+\x29\x7d\x7d\x29\x2c\x66\x2e\x65\x61\x63\x68\x28\x22\x62\x6c\x75\
+\x72\x20\x66\x6f\x63\x75\x73\x20\x66\x6f\x63\x75\x73\x69\x6e\x20\
+\x66\x6f\x63\x75\x73\x6f\x75\x74\x20\x6c\x6f\x61\x64\x20\x72\x65\
+\x73\x69\x7a\x65\x20\x73\x63\x72\x6f\x6c\x6c\x20\x75\x6e\x6c\x6f\
+\x61\x64\x20\x63\x6c\x69\x63\x6b\x20\x64\x62\x6c\x63\x6c\x69\x63\
+\x6b\x20\x6d\x6f\x75\x73\x65\x64\x6f\x77\x6e\x20\x6d\x6f\x75\x73\
+\x65\x75\x70\x20\x6d\x6f\x75\x73\x65\x6d\x6f\x76\x65\x20\x6d\x6f\
+\x75\x73\x65\x6f\x76\x65\x72\x20\x6d\x6f\x75\x73\x65\x6f\x75\x74\
+\x20\x6d\x6f\x75\x73\x65\x65\x6e\x74\x65\x72\x20\x6d\x6f\x75\x73\
+\x65\x6c\x65\x61\x76\x65\x20\x63\x68\x61\x6e\x67\x65\x20\x73\x65\
+\x6c\x65\x63\x74\x20\x73\x75\x62\x6d\x69\x74\x20\x6b\x65\x79\x64\
+\x6f\x77\x6e\x20\x6b\x65\x79\x70\x72\x65\x73\x73\x20\x6b\x65\x79\
+\x75\x70\x20\x65\x72\x72\x6f\x72\x20\x63\x6f\x6e\x74\x65\x78\x74\
+\x6d\x65\x6e\x75\x22\x2e\x73\x70\x6c\x69\x74\x28\x22\x20\x22\x29\
+\x2c\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x29\x7b\x66\
+\x2e\x66\x6e\x5b\x62\x5d\x3d\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\
+\x61\x2c\x63\x29\x7b\x63\x3d\x3d\x6e\x75\x6c\x6c\x26\x26\x28\x63\
+\x3d\x61\x2c\x61\x3d\x6e\x75\x6c\x6c\x29\x3b\x72\x65\x74\x75\x72\
+\x6e\x20\x61\x72\x67\x75\x6d\x65\x6e\x74\x73\x2e\x6c\x65\x6e\x67\
+\x74\x68\x3e\x30\x3f\x74\x68\x69\x73\x2e\x6f\x6e\x28\x62\x2c\x6e\
+\x75\x6c\x6c\x2c\x61\x2c\x63\x29\x3a\x74\x68\x69\x73\x2e\x74\x72\
+\x69\x67\x67\x65\x72\x28\x62\x29\x7d\x2c\x66\x2e\x61\x74\x74\x72\
+\x46\x6e\x26\x26\x28\x66\x2e\x61\x74\x74\x72\x46\x6e\x5b\x62\x5d\
+\x3d\x21\x30\x29\x2c\x43\x2e\x74\x65\x73\x74\x28\x62\x29\x26\x26\
+\x28\x66\x2e\x65\x76\x65\x6e\x74\x2e\x66\x69\x78\x48\x6f\x6f\x6b\
+\x73\x5b\x62\x5d\x3d\x66\x2e\x65\x76\x65\x6e\x74\x2e\x6b\x65\x79\
+\x48\x6f\x6f\x6b\x73\x29\x2c\x44\x2e\x74\x65\x73\x74\x28\x62\x29\
+\x26\x26\x28\x66\x2e\x65\x76\x65\x6e\x74\x2e\x66\x69\x78\x48\x6f\
+\x6f\x6b\x73\x5b\x62\x5d\x3d\x66\x2e\x65\x76\x65\x6e\x74\x2e\x6d\
+\x6f\x75\x73\x65\x48\x6f\x6f\x6b\x73\x29\x7d\x29\x2c\x66\x75\x6e\
+\x63\x74\x69\x6f\x6e\x28\x29\x7b\x66\x75\x6e\x63\x74\x69\x6f\x6e\
+\x20\x78\x28\x61\x2c\x62\x2c\x63\x2c\x65\x2c\x66\x2c\x67\x29\x7b\
+\x66\x6f\x72\x28\x76\x61\x72\x20\x68\x3d\x30\x2c\x69\x3d\x65\x2e\
+\x6c\x65\x6e\x67\x74\x68\x3b\x68\x3c\x69\x3b\x68\x2b\x2b\x29\x7b\
+\x76\x61\x72\x20\x6a\x3d\x65\x5b\x68\x5d\x3b\x69\x66\x28\x6a\x29\
+\x7b\x76\x61\x72\x20\x6b\x3d\x21\x31\x3b\x6a\x3d\x6a\x5b\x61\x5d\
+\x3b\x77\x68\x69\x6c\x65\x28\x6a\x29\x7b\x69\x66\x28\x6a\x5b\x64\
+\x5d\x3d\x3d\x3d\x63\x29\x7b\x6b\x3d\x65\x5b\x6a\x2e\x73\x69\x7a\
+\x73\x65\x74\x5d\x3b\x62\x72\x65\x61\x6b\x7d\x69\x66\x28\x6a\x2e\
+\x6e\x6f\x64\x65\x54\x79\x70\x65\x3d\x3d\x3d\x31\x29\x7b\x67\x7c\
+\x7c\x28\x6a\x5b\x64\x5d\x3d\x63\x2c\x6a\x2e\x73\x69\x7a\x73\x65\
+\x74\x3d\x68\x29\x3b\x69\x66\x28\x74\x79\x70\x65\x6f\x66\x20\x62\
+\x21\x3d\x22\x73\x74\x72\x69\x6e\x67\x22\x29\x7b\x69\x66\x28\x6a\
+\x3d\x3d\x3d\x62\x29\x7b\x6b\x3d\x21\x30\x3b\x62\x72\x65\x61\x6b\
+\x7d\x7d\x65\x6c\x73\x65\x20\x69\x66\x28\x6d\x2e\x66\x69\x6c\x74\
+\x65\x72\x28\x62\x2c\x5b\x6a\x5d\x29\x2e\x6c\x65\x6e\x67\x74\x68\
+\x3e\x30\x29\x7b\x6b\x3d\x6a\x3b\x62\x72\x65\x61\x6b\x7d\x7d\x6a\
+\x3d\x6a\x5b\x61\x5d\x7d\x65\x5b\x68\x5d\x3d\x6b\x7d\x7d\x7d\x66\
+\x75\x6e\x63\x74\x69\x6f\x6e\x20\x77\x28\x61\x2c\x62\x2c\x63\x2c\
+\x65\x2c\x66\x2c\x67\x29\x7b\x66\x6f\x72\x28\x76\x61\x72\x20\x68\
+\x3d\x30\x2c\x69\x3d\x65\x2e\x6c\x65\x6e\x67\x74\x68\x3b\x68\x3c\
+\x69\x3b\x68\x2b\x2b\x29\x7b\x76\x61\x72\x20\x6a\x3d\x65\x5b\x68\
+\x5d\x3b\x69\x66\x28\x6a\x29\x7b\x76\x61\x72\x20\x6b\x3d\x21\x31\
+\x3b\x6a\x3d\x6a\x5b\x61\x5d\x3b\x77\x68\x69\x6c\x65\x28\x6a\x29\
+\x7b\x69\x66\x28\x6a\x5b\x64\x5d\x3d\x3d\x3d\x63\x29\x7b\x6b\x3d\
+\x65\x5b\x6a\x2e\x73\x69\x7a\x73\x65\x74\x5d\x3b\x62\x72\x65\x61\
+\x6b\x7d\x6a\x2e\x6e\x6f\x64\x65\x54\x79\x70\x65\x3d\x3d\x3d\x31\
+\x26\x26\x21\x67\x26\x26\x28\x6a\x5b\x64\x5d\x3d\x63\x2c\x6a\x2e\
+\x73\x69\x7a\x73\x65\x74\x3d\x68\x29\x3b\x69\x66\x28\x6a\x2e\x6e\
+\x6f\x64\x65\x4e\x61\x6d\x65\x2e\x74\x6f\x4c\x6f\x77\x65\x72\x43\
+\x61\x73\x65\x28\x29\x3d\x3d\x3d\x62\x29\x7b\x6b\x3d\x6a\x3b\x62\
+\x72\x65\x61\x6b\x7d\x6a\x3d\x6a\x5b\x61\x5d\x7d\x65\x5b\x68\x5d\
+\x3d\x6b\x7d\x7d\x7d\x76\x61\x72\x20\x61\x3d\x2f\x28\x28\x3f\x3a\
+\x5c\x28\x28\x3f\x3a\x5c\x28\x5b\x5e\x28\x29\x5d\x2b\x5c\x29\x7c\
+\x5b\x5e\x28\x29\x5d\x2b\x29\x2b\x5c\x29\x7c\x5c\x5b\x28\x3f\x3a\
+\x5c\x5b\x5b\x5e\x5c\x5b\x5c\x5d\x5d\x2a\x5c\x5d\x7c\x5b\x27\x22\
+\x5d\x5b\x5e\x27\x22\x5d\x2a\x5b\x27\x22\x5d\x7c\x5b\x5e\x5c\x5b\
+\x5c\x5d\x27\x22\x5d\x2b\x29\x2b\x5c\x5d\x7c\x5c\x5c\x2e\x7c\x5b\
+\x5e\x20\x3e\x2b\x7e\x2c\x28\x5c\x5b\x5c\x5c\x5d\x2b\x29\x2b\x7c\
+\x5b\x3e\x2b\x7e\x5d\x29\x28\x5c\x73\x2a\x2c\x5c\x73\x2a\x29\x3f\
+\x28\x28\x3f\x3a\x2e\x7c\x5c\x72\x7c\x5c\x6e\x29\x2a\x29\x2f\x67\
+\x2c\x64\x3d\x22\x73\x69\x7a\x63\x61\x63\x68\x65\x22\x2b\x28\x4d\
+\x61\x74\x68\x2e\x72\x61\x6e\x64\x6f\x6d\x28\x29\x2b\x22\x22\x29\
+\x2e\x72\x65\x70\x6c\x61\x63\x65\x28\x22\x2e\x22\x2c\x22\x22\x29\
+\x2c\x65\x3d\x30\x2c\x67\x3d\x4f\x62\x6a\x65\x63\x74\x2e\x70\x72\
+\x6f\x74\x6f\x74\x79\x70\x65\x2e\x74\x6f\x53\x74\x72\x69\x6e\x67\
+\x2c\x68\x3d\x21\x31\x2c\x69\x3d\x21\x30\x2c\x6a\x3d\x2f\x5c\x5c\
+\x2f\x67\x2c\x6b\x3d\x2f\x5c\x72\x5c\x6e\x2f\x67\x2c\x6c\x3d\x2f\
+\x5c\x57\x2f\x3b\x5b\x30\x2c\x30\x5d\x2e\x73\x6f\x72\x74\x28\x66\
+\x75\x6e\x63\x74\x69\x6f\x6e\x28\x29\x7b\x69\x3d\x21\x31\x3b\x72\
+\x65\x74\x75\x72\x6e\x20\x30\x7d\x29\x3b\x76\x61\x72\x20\x6d\x3d\
+\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x62\x2c\x64\x2c\x65\x2c\x66\
+\x29\x7b\x65\x3d\x65\x7c\x7c\x5b\x5d\x2c\x64\x3d\x64\x7c\x7c\x63\
+\x3b\x76\x61\x72\x20\x68\x3d\x64\x3b\x69\x66\x28\x64\x2e\x6e\x6f\
+\x64\x65\x54\x79\x70\x65\x21\x3d\x3d\x31\x26\x26\x64\x2e\x6e\x6f\
+\x64\x65\x54\x79\x70\x65\x21\x3d\x3d\x39\x29\x72\x65\x74\x75\x72\
+\x6e\x5b\x5d\x3b\x69\x66\x28\x21\x62\x7c\x7c\x74\x79\x70\x65\x6f\
+\x66\x20\x62\x21\x3d\x22\x73\x74\x72\x69\x6e\x67\x22\x29\x72\x65\
+\x74\x75\x72\x6e\x20\x65\x3b\x76\x61\x72\x20\x69\x2c\x6a\x2c\x6b\
+\x2c\x6c\x2c\x6e\x2c\x71\x2c\x72\x2c\x74\x2c\x75\x3d\x21\x30\x2c\
+\x76\x3d\x6d\x2e\x69\x73\x58\x4d\x4c\x28\x64\x29\x2c\x77\x3d\x5b\
+\x5d\x2c\x78\x3d\x62\x3b\x64\x6f\x7b\x61\x2e\x65\x78\x65\x63\x28\
+\x22\x22\x29\x2c\x69\x3d\x61\x2e\x65\x78\x65\x63\x28\x78\x29\x3b\
+\x69\x66\x28\x69\x29\x7b\x78\x3d\x69\x5b\x33\x5d\x2c\x77\x2e\x70\
+\x75\x73\x68\x28\x69\x5b\x31\x5d\x29\x3b\x69\x66\x28\x69\x5b\x32\
+\x5d\x29\x7b\x6c\x3d\x69\x5b\x33\x5d\x3b\x62\x72\x65\x61\x6b\x7d\
+\x7d\x7d\x77\x68\x69\x6c\x65\x28\x69\x29\x3b\x69\x66\x28\x77\x2e\
+\x6c\x65\x6e\x67\x74\x68\x3e\x31\x26\x26\x70\x2e\x65\x78\x65\x63\
+\x28\x62\x29\x29\x69\x66\x28\x77\x2e\x6c\x65\x6e\x67\x74\x68\x3d\
+\x3d\x3d\x32\x26\x26\x6f\x2e\x72\x65\x6c\x61\x74\x69\x76\x65\x5b\
+\x77\x5b\x30\x5d\x5d\x29\x6a\x3d\x79\x28\x77\x5b\x30\x5d\x2b\x77\
+\x5b\x31\x5d\x2c\x64\x2c\x66\x29\x3b\x65\x6c\x73\x65\x7b\x6a\x3d\
+\x6f\x2e\x72\x65\x6c\x61\x74\x69\x76\x65\x5b\x77\x5b\x30\x5d\x5d\
+\x3f\x5b\x64\x5d\x3a\x6d\x28\x77\x2e\x73\x68\x69\x66\x74\x28\x29\
+\x2c\x64\x29\x3b\x77\x68\x69\x6c\x65\x28\x77\x2e\x6c\x65\x6e\x67\
+\x74\x68\x29\x62\x3d\x77\x2e\x73\x68\x69\x66\x74\x28\x29\x2c\x6f\
+\x2e\x72\x65\x6c\x61\x74\x69\x76\x65\x5b\x62\x5d\x26\x26\x28\x62\
+\x2b\x3d\x77\x2e\x73\x68\x69\x66\x74\x28\x29\x29\x2c\x6a\x3d\x79\
+\x28\x62\x2c\x6a\x2c\x66\x29\x7d\x65\x6c\x73\x65\x7b\x21\x66\x26\
+\x26\x77\x2e\x6c\x65\x6e\x67\x74\x68\x3e\x31\x26\x26\x64\x2e\x6e\
+\x6f\x64\x65\x54\x79\x70\x65\x3d\x3d\x3d\x39\x26\x26\x21\x76\x26\
+\x26\x6f\x2e\x6d\x61\x74\x63\x68\x2e\x49\x44\x2e\x74\x65\x73\x74\
+\x28\x77\x5b\x30\x5d\x29\x26\x26\x21\x6f\x2e\x6d\x61\x74\x63\x68\
+\x2e\x49\x44\x2e\x74\x65\x73\x74\x28\x77\x5b\x77\x2e\x6c\x65\x6e\
+\x67\x74\x68\x2d\x31\x5d\x29\x26\x26\x28\x6e\x3d\x6d\x2e\x66\x69\
+\x6e\x64\x28\x77\x2e\x73\x68\x69\x66\x74\x28\x29\x2c\x64\x2c\x76\
+\x29\x2c\x64\x3d\x6e\x2e\x65\x78\x70\x72\x3f\x6d\x2e\x66\x69\x6c\
+\x74\x65\x72\x28\x6e\x2e\x65\x78\x70\x72\x2c\x6e\x2e\x73\x65\x74\
+\x29\x5b\x30\x5d\x3a\x6e\x2e\x73\x65\x74\x5b\x30\x5d\x29\x3b\x69\
+\x66\x28\x64\x29\x7b\x6e\x3d\x66\x3f\x7b\x65\x78\x70\x72\x3a\x77\
+\x2e\x70\x6f\x70\x28\x29\x2c\x73\x65\x74\x3a\x73\x28\x66\x29\x7d\
+\x3a\x6d\x2e\x66\x69\x6e\x64\x28\x77\x2e\x70\x6f\x70\x28\x29\x2c\
+\x77\x2e\x6c\x65\x6e\x67\x74\x68\x3d\x3d\x3d\x31\x26\x26\x28\x77\
+\x5b\x30\x5d\x3d\x3d\x3d\x22\x7e\x22\x7c\x7c\x77\x5b\x30\x5d\x3d\
+\x3d\x3d\x22\x2b\x22\x29\x26\x26\x64\x2e\x70\x61\x72\x65\x6e\x74\
+\x4e\x6f\x64\x65\x3f\x64\x2e\x70\x61\x72\x65\x6e\x74\x4e\x6f\x64\
+\x65\x3a\x64\x2c\x76\x29\x2c\x6a\x3d\x6e\x2e\x65\x78\x70\x72\x3f\
+\x6d\x2e\x66\x69\x6c\x74\x65\x72\x28\x6e\x2e\x65\x78\x70\x72\x2c\
+\x6e\x2e\x73\x65\x74\x29\x3a\x6e\x2e\x73\x65\x74\x2c\x77\x2e\x6c\
+\x65\x6e\x67\x74\x68\x3e\x30\x3f\x6b\x3d\x73\x28\x6a\x29\x3a\x75\
+\x3d\x21\x31\x3b\x77\x68\x69\x6c\x65\x28\x77\x2e\x6c\x65\x6e\x67\
+\x74\x68\x29\x71\x3d\x77\x2e\x70\x6f\x70\x28\x29\x2c\x72\x3d\x71\
+\x2c\x6f\x2e\x72\x65\x6c\x61\x74\x69\x76\x65\x5b\x71\x5d\x3f\x72\
+\x3d\x77\x2e\x70\x6f\x70\x28\x29\x3a\x71\x3d\x22\x22\x2c\x72\x3d\
+\x3d\x6e\x75\x6c\x6c\x26\x26\x28\x72\x3d\x64\x29\x2c\x6f\x2e\x72\
+\x65\x6c\x61\x74\x69\x76\x65\x5b\x71\x5d\x28\x6b\x2c\x72\x2c\x76\
+\x29\x7d\x65\x6c\x73\x65\x20\x6b\x3d\x77\x3d\x5b\x5d\x7d\x6b\x7c\
+\x7c\x28\x6b\x3d\x6a\x29\x2c\x6b\x7c\x7c\x6d\x2e\x65\x72\x72\x6f\
+\x72\x28\x71\x7c\x7c\x62\x29\x3b\x69\x66\x28\x67\x2e\x63\x61\x6c\
+\x6c\x28\x6b\x29\x3d\x3d\x3d\x22\x5b\x6f\x62\x6a\x65\x63\x74\x20\
+\x41\x72\x72\x61\x79\x5d\x22\x29\x69\x66\x28\x21\x75\x29\x65\x2e\
+\x70\x75\x73\x68\x2e\x61\x70\x70\x6c\x79\x28\x65\x2c\x6b\x29\x3b\
+\x65\x6c\x73\x65\x20\x69\x66\x28\x64\x26\x26\x64\x2e\x6e\x6f\x64\
+\x65\x54\x79\x70\x65\x3d\x3d\x3d\x31\x29\x66\x6f\x72\x28\x74\x3d\
+\x30\x3b\x6b\x5b\x74\x5d\x21\x3d\x6e\x75\x6c\x6c\x3b\x74\x2b\x2b\
+\x29\x6b\x5b\x74\x5d\x26\x26\x28\x6b\x5b\x74\x5d\x3d\x3d\x3d\x21\
+\x30\x7c\x7c\x6b\x5b\x74\x5d\x2e\x6e\x6f\x64\x65\x54\x79\x70\x65\
+\x3d\x3d\x3d\x31\x26\x26\x6d\x2e\x63\x6f\x6e\x74\x61\x69\x6e\x73\
+\x28\x64\x2c\x6b\x5b\x74\x5d\x29\x29\x26\x26\x65\x2e\x70\x75\x73\
+\x68\x28\x6a\x5b\x74\x5d\x29\x3b\x65\x6c\x73\x65\x20\x66\x6f\x72\
+\x28\x74\x3d\x30\x3b\x6b\x5b\x74\x5d\x21\x3d\x6e\x75\x6c\x6c\x3b\
+\x74\x2b\x2b\x29\x6b\x5b\x74\x5d\x26\x26\x6b\x5b\x74\x5d\x2e\x6e\
+\x6f\x64\x65\x54\x79\x70\x65\x3d\x3d\x3d\x31\x26\x26\x65\x2e\x70\
+\x75\x73\x68\x28\x6a\x5b\x74\x5d\x29\x3b\x65\x6c\x73\x65\x20\x73\
+\x28\x6b\x2c\x65\x29\x3b\x6c\x26\x26\x28\x6d\x28\x6c\x2c\x68\x2c\
+\x65\x2c\x66\x29\x2c\x6d\x2e\x75\x6e\x69\x71\x75\x65\x53\x6f\x72\
+\x74\x28\x65\x29\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x65\x7d\x3b\
+\x6d\x2e\x75\x6e\x69\x71\x75\x65\x53\x6f\x72\x74\x3d\x66\x75\x6e\
+\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x69\x66\x28\x75\x29\x7b\x68\
+\x3d\x69\x2c\x61\x2e\x73\x6f\x72\x74\x28\x75\x29\x3b\x69\x66\x28\
+\x68\x29\x66\x6f\x72\x28\x76\x61\x72\x20\x62\x3d\x31\x3b\x62\x3c\
+\x61\x2e\x6c\x65\x6e\x67\x74\x68\x3b\x62\x2b\x2b\x29\x61\x5b\x62\
+\x5d\x3d\x3d\x3d\x61\x5b\x62\x2d\x31\x5d\x26\x26\x61\x2e\x73\x70\
+\x6c\x69\x63\x65\x28\x62\x2d\x2d\x2c\x31\x29\x7d\x72\x65\x74\x75\
+\x72\x6e\x20\x61\x7d\x2c\x6d\x2e\x6d\x61\x74\x63\x68\x65\x73\x3d\
+\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x29\x7b\x72\x65\
+\x74\x75\x72\x6e\x20\x6d\x28\x61\x2c\x6e\x75\x6c\x6c\x2c\x6e\x75\
+\x6c\x6c\x2c\x62\x29\x7d\x2c\x6d\x2e\x6d\x61\x74\x63\x68\x65\x73\
+\x53\x65\x6c\x65\x63\x74\x6f\x72\x3d\x66\x75\x6e\x63\x74\x69\x6f\
+\x6e\x28\x61\x2c\x62\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x6d\x28\
+\x62\x2c\x6e\x75\x6c\x6c\x2c\x6e\x75\x6c\x6c\x2c\x5b\x61\x5d\x29\
+\x2e\x6c\x65\x6e\x67\x74\x68\x3e\x30\x7d\x2c\x6d\x2e\x66\x69\x6e\
+\x64\x3d\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x2c\x63\
+\x29\x7b\x76\x61\x72\x20\x64\x2c\x65\x2c\x66\x2c\x67\x2c\x68\x2c\
+\x69\x3b\x69\x66\x28\x21\x61\x29\x72\x65\x74\x75\x72\x6e\x5b\x5d\
+\x3b\x66\x6f\x72\x28\x65\x3d\x30\x2c\x66\x3d\x6f\x2e\x6f\x72\x64\
+\x65\x72\x2e\x6c\x65\x6e\x67\x74\x68\x3b\x65\x3c\x66\x3b\x65\x2b\
+\x2b\x29\x7b\x68\x3d\x6f\x2e\x6f\x72\x64\x65\x72\x5b\x65\x5d\x3b\
+\x69\x66\x28\x67\x3d\x6f\x2e\x6c\x65\x66\x74\x4d\x61\x74\x63\x68\
+\x5b\x68\x5d\x2e\x65\x78\x65\x63\x28\x61\x29\x29\x7b\x69\x3d\x67\
+\x5b\x31\x5d\x2c\x67\x2e\x73\x70\x6c\x69\x63\x65\x28\x31\x2c\x31\
+\x29\x3b\x69\x66\x28\x69\x2e\x73\x75\x62\x73\x74\x72\x28\x69\x2e\
+\x6c\x65\x6e\x67\x74\x68\x2d\x31\x29\x21\x3d\x3d\x22\x5c\x5c\x22\
+\x29\x7b\x67\x5b\x31\x5d\x3d\x28\x67\x5b\x31\x5d\x7c\x7c\x22\x22\
+\x29\x2e\x72\x65\x70\x6c\x61\x63\x65\x28\x6a\x2c\x22\x22\x29\x2c\
+\x64\x3d\x6f\x2e\x66\x69\x6e\x64\x5b\x68\x5d\x28\x67\x2c\x62\x2c\
+\x63\x29\x3b\x69\x66\x28\x64\x21\x3d\x6e\x75\x6c\x6c\x29\x7b\x61\
+\x3d\x61\x2e\x72\x65\x70\x6c\x61\x63\x65\x28\x6f\x2e\x6d\x61\x74\
+\x63\x68\x5b\x68\x5d\x2c\x22\x22\x29\x3b\x62\x72\x65\x61\x6b\x7d\
+\x7d\x7d\x7d\x64\x7c\x7c\x28\x64\x3d\x74\x79\x70\x65\x6f\x66\x20\
+\x62\x2e\x67\x65\x74\x45\x6c\x65\x6d\x65\x6e\x74\x73\x42\x79\x54\
+\x61\x67\x4e\x61\x6d\x65\x21\x3d\x22\x75\x6e\x64\x65\x66\x69\x6e\
+\x65\x64\x22\x3f\x62\x2e\x67\x65\x74\x45\x6c\x65\x6d\x65\x6e\x74\
+\x73\x42\x79\x54\x61\x67\x4e\x61\x6d\x65\x28\x22\x2a\x22\x29\x3a\
+\x5b\x5d\x29\x3b\x72\x65\x74\x75\x72\x6e\x7b\x73\x65\x74\x3a\x64\
+\x2c\x65\x78\x70\x72\x3a\x61\x7d\x7d\x2c\x6d\x2e\x66\x69\x6c\x74\
+\x65\x72\x3d\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x63\x2c\
+\x64\x2c\x65\x29\x7b\x76\x61\x72\x20\x66\x2c\x67\x2c\x68\x2c\x69\
+\x2c\x6a\x2c\x6b\x2c\x6c\x2c\x6e\x2c\x70\x2c\x71\x3d\x61\x2c\x72\
+\x3d\x5b\x5d\x2c\x73\x3d\x63\x2c\x74\x3d\x63\x26\x26\x63\x5b\x30\
+\x5d\x26\x26\x6d\x2e\x69\x73\x58\x4d\x4c\x28\x63\x5b\x30\x5d\x29\
+\x3b\x77\x68\x69\x6c\x65\x28\x61\x26\x26\x63\x2e\x6c\x65\x6e\x67\
+\x74\x68\x29\x7b\x66\x6f\x72\x28\x68\x20\x69\x6e\x20\x6f\x2e\x66\
+\x69\x6c\x74\x65\x72\x29\x69\x66\x28\x28\x66\x3d\x6f\x2e\x6c\x65\
+\x66\x74\x4d\x61\x74\x63\x68\x5b\x68\x5d\x2e\x65\x78\x65\x63\x28\
+\x61\x29\x29\x21\x3d\x6e\x75\x6c\x6c\x26\x26\x66\x5b\x32\x5d\x29\
+\x7b\x6b\x3d\x6f\x2e\x66\x69\x6c\x74\x65\x72\x5b\x68\x5d\x2c\x6c\
+\x3d\x66\x5b\x31\x5d\x2c\x67\x3d\x21\x31\x2c\x66\x2e\x73\x70\x6c\
+\x69\x63\x65\x28\x31\x2c\x31\x29\x3b\x69\x66\x28\x6c\x2e\x73\x75\
+\x62\x73\x74\x72\x28\x6c\x2e\x6c\x65\x6e\x67\x74\x68\x2d\x31\x29\
+\x3d\x3d\x3d\x22\x5c\x5c\x22\x29\x63\x6f\x6e\x74\x69\x6e\x75\x65\
+\x3b\x73\x3d\x3d\x3d\x72\x26\x26\x28\x72\x3d\x5b\x5d\x29\x3b\x69\
+\x66\x28\x6f\x2e\x70\x72\x65\x46\x69\x6c\x74\x65\x72\x5b\x68\x5d\
+\x29\x7b\x66\x3d\x6f\x2e\x70\x72\x65\x46\x69\x6c\x74\x65\x72\x5b\
+\x68\x5d\x28\x66\x2c\x73\x2c\x64\x2c\x72\x2c\x65\x2c\x74\x29\x3b\
+\x69\x66\x28\x21\x66\x29\x67\x3d\x69\x3d\x21\x30\x3b\x65\x6c\x73\
+\x65\x20\x69\x66\x28\x66\x3d\x3d\x3d\x21\x30\x29\x63\x6f\x6e\x74\
+\x69\x6e\x75\x65\x7d\x69\x66\x28\x66\x29\x66\x6f\x72\x28\x6e\x3d\
+\x30\x3b\x28\x6a\x3d\x73\x5b\x6e\x5d\x29\x21\x3d\x6e\x75\x6c\x6c\
+\x3b\x6e\x2b\x2b\x29\x6a\x26\x26\x28\x69\x3d\x6b\x28\x6a\x2c\x66\
+\x2c\x6e\x2c\x73\x29\x2c\x70\x3d\x65\x5e\x69\x2c\x64\x26\x26\x69\
+\x21\x3d\x6e\x75\x6c\x6c\x3f\x70\x3f\x67\x3d\x21\x30\x3a\x73\x5b\
+\x6e\x5d\x3d\x21\x31\x3a\x70\x26\x26\x28\x72\x2e\x70\x75\x73\x68\
+\x28\x6a\x29\x2c\x67\x3d\x21\x30\x29\x29\x3b\x69\x66\x28\x69\x21\
+\x3d\x3d\x62\x29\x7b\x64\x7c\x7c\x28\x73\x3d\x72\x29\x2c\x61\x3d\
+\x61\x2e\x72\x65\x70\x6c\x61\x63\x65\x28\x6f\x2e\x6d\x61\x74\x63\
+\x68\x5b\x68\x5d\x2c\x22\x22\x29\x3b\x69\x66\x28\x21\x67\x29\x72\
+\x65\x74\x75\x72\x6e\x5b\x5d\x3b\x62\x72\x65\x61\x6b\x7d\x7d\x69\
+\x66\x28\x61\x3d\x3d\x3d\x71\x29\x69\x66\x28\x67\x3d\x3d\x6e\x75\
+\x6c\x6c\x29\x6d\x2e\x65\x72\x72\x6f\x72\x28\x61\x29\x3b\x65\x6c\
+\x73\x65\x20\x62\x72\x65\x61\x6b\x3b\x71\x3d\x61\x7d\x72\x65\x74\
+\x75\x72\x6e\x20\x73\x7d\x2c\x6d\x2e\x65\x72\x72\x6f\x72\x3d\x66\
+\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x74\x68\x72\x6f\x77\
+\x20\x6e\x65\x77\x20\x45\x72\x72\x6f\x72\x28\x22\x53\x79\x6e\x74\
+\x61\x78\x20\x65\x72\x72\x6f\x72\x2c\x20\x75\x6e\x72\x65\x63\x6f\
+\x67\x6e\x69\x7a\x65\x64\x20\x65\x78\x70\x72\x65\x73\x73\x69\x6f\
+\x6e\x3a\x20\x22\x2b\x61\x29\x7d\x3b\x76\x61\x72\x20\x6e\x3d\x6d\
+\x2e\x67\x65\x74\x54\x65\x78\x74\x3d\x66\x75\x6e\x63\x74\x69\x6f\
+\x6e\x28\x61\x29\x7b\x76\x61\x72\x20\x62\x2c\x63\x2c\x64\x3d\x61\
+\x2e\x6e\x6f\x64\x65\x54\x79\x70\x65\x2c\x65\x3d\x22\x22\x3b\x69\
+\x66\x28\x64\x29\x7b\x69\x66\x28\x64\x3d\x3d\x3d\x31\x7c\x7c\x64\
+\x3d\x3d\x3d\x39\x29\x7b\x69\x66\x28\x74\x79\x70\x65\x6f\x66\x20\
+\x61\x2e\x74\x65\x78\x74\x43\x6f\x6e\x74\x65\x6e\x74\x3d\x3d\x22\
+\x73\x74\x72\x69\x6e\x67\x22\x29\x72\x65\x74\x75\x72\x6e\x20\x61\
+\x2e\x74\x65\x78\x74\x43\x6f\x6e\x74\x65\x6e\x74\x3b\x69\x66\x28\
+\x74\x79\x70\x65\x6f\x66\x20\x61\x2e\x69\x6e\x6e\x65\x72\x54\x65\
+\x78\x74\x3d\x3d\x22\x73\x74\x72\x69\x6e\x67\x22\x29\x72\x65\x74\
+\x75\x72\x6e\x20\x61\x2e\x69\x6e\x6e\x65\x72\x54\x65\x78\x74\x2e\
+\x72\x65\x70\x6c\x61\x63\x65\x28\x6b\x2c\x22\x22\x29\x3b\x66\x6f\
+\x72\x28\x61\x3d\x61\x2e\x66\x69\x72\x73\x74\x43\x68\x69\x6c\x64\
+\x3b\x61\x3b\x61\x3d\x61\x2e\x6e\x65\x78\x74\x53\x69\x62\x6c\x69\
+\x6e\x67\x29\x65\x2b\x3d\x6e\x28\x61\x29\x7d\x65\x6c\x73\x65\x20\
+\x69\x66\x28\x64\x3d\x3d\x3d\x33\x7c\x7c\x64\x3d\x3d\x3d\x34\x29\
+\x72\x65\x74\x75\x72\x6e\x20\x61\x2e\x6e\x6f\x64\x65\x56\x61\x6c\
+\x75\x65\x7d\x65\x6c\x73\x65\x20\x66\x6f\x72\x28\x62\x3d\x30\x3b\
+\x63\x3d\x61\x5b\x62\x5d\x3b\x62\x2b\x2b\x29\x63\x2e\x6e\x6f\x64\
+\x65\x54\x79\x70\x65\x21\x3d\x3d\x38\x26\x26\x28\x65\x2b\x3d\x6e\
+\x28\x63\x29\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x65\x7d\x2c\x6f\
+\x3d\x6d\x2e\x73\x65\x6c\x65\x63\x74\x6f\x72\x73\x3d\x7b\x6f\x72\
+\x64\x65\x72\x3a\x5b\x22\x49\x44\x22\x2c\x22\x4e\x41\x4d\x45\x22\
+\x2c\x22\x54\x41\x47\x22\x5d\x2c\x6d\x61\x74\x63\x68\x3a\x7b\x49\
+\x44\x3a\x2f\x23\x28\x28\x3f\x3a\x5b\x5c\x77\x5c\x75\x30\x30\x63\
+\x30\x2d\x5c\x75\x46\x46\x46\x46\x5c\x2d\x5d\x7c\x5c\x5c\x2e\x29\
+\x2b\x29\x2f\x2c\x43\x4c\x41\x53\x53\x3a\x2f\x5c\x2e\x28\x28\x3f\
+\x3a\x5b\x5c\x77\x5c\x75\x30\x30\x63\x30\x2d\x5c\x75\x46\x46\x46\
+\x46\x5c\x2d\x5d\x7c\x5c\x5c\x2e\x29\x2b\x29\x2f\x2c\x4e\x41\x4d\
+\x45\x3a\x2f\x5c\x5b\x6e\x61\x6d\x65\x3d\x5b\x27\x22\x5d\x2a\x28\
+\x28\x3f\x3a\x5b\x5c\x77\x5c\x75\x30\x30\x63\x30\x2d\x5c\x75\x46\
+\x46\x46\x46\x5c\x2d\x5d\x7c\x5c\x5c\x2e\x29\x2b\x29\x5b\x27\x22\
+\x5d\x2a\x5c\x5d\x2f\x2c\x41\x54\x54\x52\x3a\x2f\x5c\x5b\x5c\x73\
+\x2a\x28\x28\x3f\x3a\x5b\x5c\x77\x5c\x75\x30\x30\x63\x30\x2d\x5c\
+\x75\x46\x46\x46\x46\x5c\x2d\x5d\x7c\x5c\x5c\x2e\x29\x2b\x29\x5c\
+\x73\x2a\x28\x3f\x3a\x28\x5c\x53\x3f\x3d\x29\x5c\x73\x2a\x28\x3f\
+\x3a\x28\x5b\x27\x22\x5d\x29\x28\x2e\x2a\x3f\x29\x5c\x33\x7c\x28\
+\x23\x3f\x28\x3f\x3a\x5b\x5c\x77\x5c\x75\x30\x30\x63\x30\x2d\x5c\
+\x75\x46\x46\x46\x46\x5c\x2d\x5d\x7c\x5c\x5c\x2e\x29\x2a\x29\x7c\
+\x29\x7c\x29\x5c\x73\x2a\x5c\x5d\x2f\x2c\x54\x41\x47\x3a\x2f\x5e\
+\x28\x28\x3f\x3a\x5b\x5c\x77\x5c\x75\x30\x30\x63\x30\x2d\x5c\x75\
+\x46\x46\x46\x46\x5c\x2a\x5c\x2d\x5d\x7c\x5c\x5c\x2e\x29\x2b\x29\
+\x2f\x2c\x43\x48\x49\x4c\x44\x3a\x2f\x3a\x28\x6f\x6e\x6c\x79\x7c\
+\x6e\x74\x68\x7c\x6c\x61\x73\x74\x7c\x66\x69\x72\x73\x74\x29\x2d\
+\x63\x68\x69\x6c\x64\x28\x3f\x3a\x5c\x28\x5c\x73\x2a\x28\x65\x76\
+\x65\x6e\x7c\x6f\x64\x64\x7c\x28\x3f\x3a\x5b\x2b\x5c\x2d\x5d\x3f\
+\x5c\x64\x2b\x7c\x28\x3f\x3a\x5b\x2b\x5c\x2d\x5d\x3f\x5c\x64\x2a\
+\x29\x3f\x6e\x5c\x73\x2a\x28\x3f\x3a\x5b\x2b\x5c\x2d\x5d\x5c\x73\
+\x2a\x5c\x64\x2b\x29\x3f\x29\x29\x5c\x73\x2a\x5c\x29\x29\x3f\x2f\
+\x2c\x50\x4f\x53\x3a\x2f\x3a\x28\x6e\x74\x68\x7c\x65\x71\x7c\x67\
+\x74\x7c\x6c\x74\x7c\x66\x69\x72\x73\x74\x7c\x6c\x61\x73\x74\x7c\
+\x65\x76\x65\x6e\x7c\x6f\x64\x64\x29\x28\x3f\x3a\x5c\x28\x28\x5c\
+\x64\x2a\x29\x5c\x29\x29\x3f\x28\x3f\x3d\x5b\x5e\x5c\x2d\x5d\x7c\
+\x24\x29\x2f\x2c\x50\x53\x45\x55\x44\x4f\x3a\x2f\x3a\x28\x28\x3f\
+\x3a\x5b\x5c\x77\x5c\x75\x30\x30\x63\x30\x2d\x5c\x75\x46\x46\x46\
+\x46\x5c\x2d\x5d\x7c\x5c\x5c\x2e\x29\x2b\x29\x28\x3f\x3a\x5c\x28\
+\x28\x5b\x27\x22\x5d\x3f\x29\x28\x28\x3f\x3a\x5c\x28\x5b\x5e\x5c\
+\x29\x5d\x2b\x5c\x29\x7c\x5b\x5e\x5c\x28\x5c\x29\x5d\x2a\x29\x2b\
+\x29\x5c\x32\x5c\x29\x29\x3f\x2f\x7d\x2c\x6c\x65\x66\x74\x4d\x61\
+\x74\x63\x68\x3a\x7b\x7d\x2c\x61\x74\x74\x72\x4d\x61\x70\x3a\x7b\
+\x22\x63\x6c\x61\x73\x73\x22\x3a\x22\x63\x6c\x61\x73\x73\x4e\x61\
+\x6d\x65\x22\x2c\x22\x66\x6f\x72\x22\x3a\x22\x68\x74\x6d\x6c\x46\
+\x6f\x72\x22\x7d\x2c\x61\x74\x74\x72\x48\x61\x6e\x64\x6c\x65\x3a\
+\x7b\x68\x72\x65\x66\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\
+\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x61\x2e\x67\x65\x74\x41\x74\
+\x74\x72\x69\x62\x75\x74\x65\x28\x22\x68\x72\x65\x66\x22\x29\x7d\
+\x2c\x74\x79\x70\x65\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\
+\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x61\x2e\x67\x65\x74\x41\x74\
+\x74\x72\x69\x62\x75\x74\x65\x28\x22\x74\x79\x70\x65\x22\x29\x7d\
+\x7d\x2c\x72\x65\x6c\x61\x74\x69\x76\x65\x3a\x7b\x22\x2b\x22\x3a\
+\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x29\x7b\x76\x61\
+\x72\x20\x63\x3d\x74\x79\x70\x65\x6f\x66\x20\x62\x3d\x3d\x22\x73\
+\x74\x72\x69\x6e\x67\x22\x2c\x64\x3d\x63\x26\x26\x21\x6c\x2e\x74\
+\x65\x73\x74\x28\x62\x29\x2c\x65\x3d\x63\x26\x26\x21\x64\x3b\x64\
+\x26\x26\x28\x62\x3d\x62\x2e\x74\x6f\x4c\x6f\x77\x65\x72\x43\x61\
+\x73\x65\x28\x29\x29\x3b\x66\x6f\x72\x28\x76\x61\x72\x20\x66\x3d\
+\x30\x2c\x67\x3d\x61\x2e\x6c\x65\x6e\x67\x74\x68\x2c\x68\x3b\x66\
+\x3c\x67\x3b\x66\x2b\x2b\x29\x69\x66\x28\x68\x3d\x61\x5b\x66\x5d\
+\x29\x7b\x77\x68\x69\x6c\x65\x28\x28\x68\x3d\x68\x2e\x70\x72\x65\
+\x76\x69\x6f\x75\x73\x53\x69\x62\x6c\x69\x6e\x67\x29\x26\x26\x68\
+\x2e\x6e\x6f\x64\x65\x54\x79\x70\x65\x21\x3d\x3d\x31\x29\x3b\x61\
+\x5b\x66\x5d\x3d\x65\x7c\x7c\x68\x26\x26\x68\x2e\x6e\x6f\x64\x65\
+\x4e\x61\x6d\x65\x2e\x74\x6f\x4c\x6f\x77\x65\x72\x43\x61\x73\x65\
+\x28\x29\x3d\x3d\x3d\x62\x3f\x68\x7c\x7c\x21\x31\x3a\x68\x3d\x3d\
+\x3d\x62\x7d\x65\x26\x26\x6d\x2e\x66\x69\x6c\x74\x65\x72\x28\x62\
+\x2c\x61\x2c\x21\x30\x29\x7d\x2c\x22\x3e\x22\x3a\x66\x75\x6e\x63\
+\x74\x69\x6f\x6e\x28\x61\x2c\x62\x29\x7b\x76\x61\x72\x20\x63\x2c\
+\x64\x3d\x74\x79\x70\x65\x6f\x66\x20\x62\x3d\x3d\x22\x73\x74\x72\
+\x69\x6e\x67\x22\x2c\x65\x3d\x30\x2c\x66\x3d\x61\x2e\x6c\x65\x6e\
+\x67\x74\x68\x3b\x69\x66\x28\x64\x26\x26\x21\x6c\x2e\x74\x65\x73\
+\x74\x28\x62\x29\x29\x7b\x62\x3d\x62\x2e\x74\x6f\x4c\x6f\x77\x65\
+\x72\x43\x61\x73\x65\x28\x29\x3b\x66\x6f\x72\x28\x3b\x65\x3c\x66\
+\x3b\x65\x2b\x2b\x29\x7b\x63\x3d\x61\x5b\x65\x5d\x3b\x69\x66\x28\
+\x63\x29\x7b\x76\x61\x72\x20\x67\x3d\x63\x2e\x70\x61\x72\x65\x6e\
+\x74\x4e\x6f\x64\x65\x3b\x61\x5b\x65\x5d\x3d\x67\x2e\x6e\x6f\x64\
+\x65\x4e\x61\x6d\x65\x2e\x74\x6f\x4c\x6f\x77\x65\x72\x43\x61\x73\
+\x65\x28\x29\x3d\x3d\x3d\x62\x3f\x67\x3a\x21\x31\x7d\x7d\x7d\x65\
+\x6c\x73\x65\x7b\x66\x6f\x72\x28\x3b\x65\x3c\x66\x3b\x65\x2b\x2b\
+\x29\x63\x3d\x61\x5b\x65\x5d\x2c\x63\x26\x26\x28\x61\x5b\x65\x5d\
+\x3d\x64\x3f\x63\x2e\x70\x61\x72\x65\x6e\x74\x4e\x6f\x64\x65\x3a\
+\x63\x2e\x70\x61\x72\x65\x6e\x74\x4e\x6f\x64\x65\x3d\x3d\x3d\x62\
+\x29\x3b\x64\x26\x26\x6d\x2e\x66\x69\x6c\x74\x65\x72\x28\x62\x2c\
+\x61\x2c\x21\x30\x29\x7d\x7d\x2c\x22\x22\x3a\x66\x75\x6e\x63\x74\
+\x69\x6f\x6e\x28\x61\x2c\x62\x2c\x63\x29\x7b\x76\x61\x72\x20\x64\
+\x2c\x66\x3d\x65\x2b\x2b\x2c\x67\x3d\x78\x3b\x74\x79\x70\x65\x6f\
+\x66\x20\x62\x3d\x3d\x22\x73\x74\x72\x69\x6e\x67\x22\x26\x26\x21\
+\x6c\x2e\x74\x65\x73\x74\x28\x62\x29\x26\x26\x28\x62\x3d\x62\x2e\
+\x74\x6f\x4c\x6f\x77\x65\x72\x43\x61\x73\x65\x28\x29\x2c\x64\x3d\
+\x62\x2c\x67\x3d\x77\x29\x2c\x67\x28\x22\x70\x61\x72\x65\x6e\x74\
+\x4e\x6f\x64\x65\x22\x2c\x62\x2c\x66\x2c\x61\x2c\x64\x2c\x63\x29\
+\x7d\x2c\x22\x7e\x22\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\
+\x2c\x62\x2c\x63\x29\x7b\x76\x61\x72\x20\x64\x2c\x66\x3d\x65\x2b\
+\x2b\x2c\x67\x3d\x78\x3b\x74\x79\x70\x65\x6f\x66\x20\x62\x3d\x3d\
+\x22\x73\x74\x72\x69\x6e\x67\x22\x26\x26\x21\x6c\x2e\x74\x65\x73\
+\x74\x28\x62\x29\x26\x26\x28\x62\x3d\x62\x2e\x74\x6f\x4c\x6f\x77\
+\x65\x72\x43\x61\x73\x65\x28\x29\x2c\x64\x3d\x62\x2c\x67\x3d\x77\
+\x29\x2c\x67\x28\x22\x70\x72\x65\x76\x69\x6f\x75\x73\x53\x69\x62\
+\x6c\x69\x6e\x67\x22\x2c\x62\x2c\x66\x2c\x61\x2c\x64\x2c\x63\x29\
+\x7d\x7d\x2c\x66\x69\x6e\x64\x3a\x7b\x49\x44\x3a\x66\x75\x6e\x63\
+\x74\x69\x6f\x6e\x28\x61\x2c\x62\x2c\x63\x29\x7b\x69\x66\x28\x74\
+\x79\x70\x65\x6f\x66\x20\x62\x2e\x67\x65\x74\x45\x6c\x65\x6d\x65\
+\x6e\x74\x42\x79\x49\x64\x21\x3d\x22\x75\x6e\x64\x65\x66\x69\x6e\
+\x65\x64\x22\x26\x26\x21\x63\x29\x7b\x76\x61\x72\x20\x64\x3d\x62\
+\x2e\x67\x65\x74\x45\x6c\x65\x6d\x65\x6e\x74\x42\x79\x49\x64\x28\
+\x61\x5b\x31\x5d\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x64\x26\x26\
+\x64\x2e\x70\x61\x72\x65\x6e\x74\x4e\x6f\x64\x65\x3f\x5b\x64\x5d\
+\x3a\x5b\x5d\x7d\x7d\x2c\x4e\x41\x4d\x45\x3a\x66\x75\x6e\x63\x74\
+\x69\x6f\x6e\x28\x61\x2c\x62\x29\x7b\x69\x66\x28\x74\x79\x70\x65\
+\x6f\x66\x20\x62\x2e\x67\x65\x74\x45\x6c\x65\x6d\x65\x6e\x74\x73\
+\x42\x79\x4e\x61\x6d\x65\x21\x3d\x22\x75\x6e\x64\x65\x66\x69\x6e\
+\x65\x64\x22\x29\x7b\x76\x61\x72\x20\x63\x3d\x5b\x5d\x2c\x64\x3d\
+\x62\x2e\x67\x65\x74\x45\x6c\x65\x6d\x65\x6e\x74\x73\x42\x79\x4e\
+\x61\x6d\x65\x28\x61\x5b\x31\x5d\x29\x3b\x66\x6f\x72\x28\x76\x61\
+\x72\x20\x65\x3d\x30\x2c\x66\x3d\x64\x2e\x6c\x65\x6e\x67\x74\x68\
+\x3b\x65\x3c\x66\x3b\x65\x2b\x2b\x29\x64\x5b\x65\x5d\x2e\x67\x65\
+\x74\x41\x74\x74\x72\x69\x62\x75\x74\x65\x28\x22\x6e\x61\x6d\x65\
+\x22\x29\x3d\x3d\x3d\x61\x5b\x31\x5d\x26\x26\x63\x2e\x70\x75\x73\
+\x68\x28\x64\x5b\x65\x5d\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x63\
+\x2e\x6c\x65\x6e\x67\x74\x68\x3d\x3d\x3d\x30\x3f\x6e\x75\x6c\x6c\
+\x3a\x63\x7d\x7d\x2c\x54\x41\x47\x3a\x66\x75\x6e\x63\x74\x69\x6f\
+\x6e\x28\x61\x2c\x62\x29\x7b\x69\x66\x28\x74\x79\x70\x65\x6f\x66\
+\x20\x62\x2e\x67\x65\x74\x45\x6c\x65\x6d\x65\x6e\x74\x73\x42\x79\
+\x54\x61\x67\x4e\x61\x6d\x65\x21\x3d\x22\x75\x6e\x64\x65\x66\x69\
+\x6e\x65\x64\x22\x29\x72\x65\x74\x75\x72\x6e\x20\x62\x2e\x67\x65\
+\x74\x45\x6c\x65\x6d\x65\x6e\x74\x73\x42\x79\x54\x61\x67\x4e\x61\
+\x6d\x65\x28\x61\x5b\x31\x5d\x29\x7d\x7d\x2c\x70\x72\x65\x46\x69\
+\x6c\x74\x65\x72\x3a\x7b\x43\x4c\x41\x53\x53\x3a\x66\x75\x6e\x63\
+\x74\x69\x6f\x6e\x28\x61\x2c\x62\x2c\x63\x2c\x64\x2c\x65\x2c\x66\
+\x29\x7b\x61\x3d\x22\x20\x22\x2b\x61\x5b\x31\x5d\x2e\x72\x65\x70\
+\x6c\x61\x63\x65\x28\x6a\x2c\x22\x22\x29\x2b\x22\x20\x22\x3b\x69\
+\x66\x28\x66\x29\x72\x65\x74\x75\x72\x6e\x20\x61\x3b\x66\x6f\x72\
+\x28\x76\x61\x72\x20\x67\x3d\x30\x2c\x68\x3b\x28\x68\x3d\x62\x5b\
+\x67\x5d\x29\x21\x3d\x6e\x75\x6c\x6c\x3b\x67\x2b\x2b\x29\x68\x26\
+\x26\x28\x65\x5e\x28\x68\x2e\x63\x6c\x61\x73\x73\x4e\x61\x6d\x65\
+\x26\x26\x28\x22\x20\x22\x2b\x68\x2e\x63\x6c\x61\x73\x73\x4e\x61\
+\x6d\x65\x2b\x22\x20\x22\x29\x2e\x72\x65\x70\x6c\x61\x63\x65\x28\
+\x2f\x5b\x5c\x74\x5c\x6e\x5c\x72\x5d\x2f\x67\x2c\x22\x20\x22\x29\
+\x2e\x69\x6e\x64\x65\x78\x4f\x66\x28\x61\x29\x3e\x3d\x30\x29\x3f\
+\x63\x7c\x7c\x64\x2e\x70\x75\x73\x68\x28\x68\x29\x3a\x63\x26\x26\
+\x28\x62\x5b\x67\x5d\x3d\x21\x31\x29\x29\x3b\x72\x65\x74\x75\x72\
+\x6e\x21\x31\x7d\x2c\x49\x44\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\
+\x28\x61\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x61\x5b\x31\x5d\x2e\
+\x72\x65\x70\x6c\x61\x63\x65\x28\x6a\x2c\x22\x22\x29\x7d\x2c\x54\
+\x41\x47\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x29\
+\x7b\x72\x65\x74\x75\x72\x6e\x20\x61\x5b\x31\x5d\x2e\x72\x65\x70\
+\x6c\x61\x63\x65\x28\x6a\x2c\x22\x22\x29\x2e\x74\x6f\x4c\x6f\x77\
+\x65\x72\x43\x61\x73\x65\x28\x29\x7d\x2c\x43\x48\x49\x4c\x44\x3a\
+\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x69\x66\x28\x61\
+\x5b\x31\x5d\x3d\x3d\x3d\x22\x6e\x74\x68\x22\x29\x7b\x61\x5b\x32\
+\x5d\x7c\x7c\x6d\x2e\x65\x72\x72\x6f\x72\x28\x61\x5b\x30\x5d\x29\
+\x2c\x61\x5b\x32\x5d\x3d\x61\x5b\x32\x5d\x2e\x72\x65\x70\x6c\x61\
+\x63\x65\x28\x2f\x5e\x5c\x2b\x7c\x5c\x73\x2a\x2f\x67\x2c\x22\x22\
+\x29\x3b\x76\x61\x72\x20\x62\x3d\x2f\x28\x2d\x3f\x29\x28\x5c\x64\
+\x2a\x29\x28\x3f\x3a\x6e\x28\x5b\x2b\x5c\x2d\x5d\x3f\x5c\x64\x2a\
+\x29\x29\x3f\x2f\x2e\x65\x78\x65\x63\x28\x61\x5b\x32\x5d\x3d\x3d\
+\x3d\x22\x65\x76\x65\x6e\x22\x26\x26\x22\x32\x6e\x22\x7c\x7c\x61\
+\x5b\x32\x5d\x3d\x3d\x3d\x22\x6f\x64\x64\x22\x26\x26\x22\x32\x6e\
+\x2b\x31\x22\x7c\x7c\x21\x2f\x5c\x44\x2f\x2e\x74\x65\x73\x74\x28\
+\x61\x5b\x32\x5d\x29\x26\x26\x22\x30\x6e\x2b\x22\x2b\x61\x5b\x32\
+\x5d\x7c\x7c\x61\x5b\x32\x5d\x29\x3b\x61\x5b\x32\x5d\x3d\x62\x5b\
+\x31\x5d\x2b\x28\x62\x5b\x32\x5d\x7c\x7c\x31\x29\x2d\x30\x2c\x61\
+\x5b\x33\x5d\x3d\x62\x5b\x33\x5d\x2d\x30\x7d\x65\x6c\x73\x65\x20\
+\x61\x5b\x32\x5d\x26\x26\x6d\x2e\x65\x72\x72\x6f\x72\x28\x61\x5b\
+\x30\x5d\x29\x3b\x61\x5b\x30\x5d\x3d\x65\x2b\x2b\x3b\x72\x65\x74\
+\x75\x72\x6e\x20\x61\x7d\x2c\x41\x54\x54\x52\x3a\x66\x75\x6e\x63\
+\x74\x69\x6f\x6e\x28\x61\x2c\x62\x2c\x63\x2c\x64\x2c\x65\x2c\x66\
+\x29\x7b\x76\x61\x72\x20\x67\x3d\x61\x5b\x31\x5d\x3d\x61\x5b\x31\
+\x5d\x2e\x72\x65\x70\x6c\x61\x63\x65\x28\x6a\x2c\x22\x22\x29\x3b\
+\x21\x66\x26\x26\x6f\x2e\x61\x74\x74\x72\x4d\x61\x70\x5b\x67\x5d\
+\x26\x26\x28\x61\x5b\x31\x5d\x3d\x6f\x2e\x61\x74\x74\x72\x4d\x61\
+\x70\x5b\x67\x5d\x29\x2c\x61\x5b\x34\x5d\x3d\x28\x61\x5b\x34\x5d\
+\x7c\x7c\x61\x5b\x35\x5d\x7c\x7c\x22\x22\x29\x2e\x72\x65\x70\x6c\
+\x61\x63\x65\x28\x6a\x2c\x22\x22\x29\x2c\x61\x5b\x32\x5d\x3d\x3d\
+\x3d\x22\x7e\x3d\x22\x26\x26\x28\x61\x5b\x34\x5d\x3d\x22\x20\x22\
+\x2b\x61\x5b\x34\x5d\x2b\x22\x20\x22\x29\x3b\x72\x65\x74\x75\x72\
+\x6e\x20\x61\x7d\x2c\x50\x53\x45\x55\x44\x4f\x3a\x66\x75\x6e\x63\
+\x74\x69\x6f\x6e\x28\x62\x2c\x63\x2c\x64\x2c\x65\x2c\x66\x29\x7b\
+\x69\x66\x28\x62\x5b\x31\x5d\x3d\x3d\x3d\x22\x6e\x6f\x74\x22\x29\
+\x69\x66\x28\x28\x61\x2e\x65\x78\x65\x63\x28\x62\x5b\x33\x5d\x29\
+\x7c\x7c\x22\x22\x29\x2e\x6c\x65\x6e\x67\x74\x68\x3e\x31\x7c\x7c\
+\x2f\x5e\x5c\x77\x2f\x2e\x74\x65\x73\x74\x28\x62\x5b\x33\x5d\x29\
+\x29\x62\x5b\x33\x5d\x3d\x6d\x28\x62\x5b\x33\x5d\x2c\x6e\x75\x6c\
+\x6c\x2c\x6e\x75\x6c\x6c\x2c\x63\x29\x3b\x65\x6c\x73\x65\x7b\x76\
+\x61\x72\x20\x67\x3d\x6d\x2e\x66\x69\x6c\x74\x65\x72\x28\x62\x5b\
+\x33\x5d\x2c\x63\x2c\x64\x2c\x21\x30\x5e\x66\x29\x3b\x64\x7c\x7c\
+\x65\x2e\x70\x75\x73\x68\x2e\x61\x70\x70\x6c\x79\x28\x65\x2c\x67\
+\x29\x3b\x72\x65\x74\x75\x72\x6e\x21\x31\x7d\x65\x6c\x73\x65\x20\
+\x69\x66\x28\x6f\x2e\x6d\x61\x74\x63\x68\x2e\x50\x4f\x53\x2e\x74\
+\x65\x73\x74\x28\x62\x5b\x30\x5d\x29\x7c\x7c\x6f\x2e\x6d\x61\x74\
+\x63\x68\x2e\x43\x48\x49\x4c\x44\x2e\x74\x65\x73\x74\x28\x62\x5b\
+\x30\x5d\x29\x29\x72\x65\x74\x75\x72\x6e\x21\x30\x3b\x72\x65\x74\
+\x75\x72\x6e\x20\x62\x7d\x2c\x50\x4f\x53\x3a\x66\x75\x6e\x63\x74\
+\x69\x6f\x6e\x28\x61\x29\x7b\x61\x2e\x75\x6e\x73\x68\x69\x66\x74\
+\x28\x21\x30\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x61\x7d\x7d\x2c\
+\x66\x69\x6c\x74\x65\x72\x73\x3a\x7b\x65\x6e\x61\x62\x6c\x65\x64\
+\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x72\x65\x74\
+\x75\x72\x6e\x20\x61\x2e\x64\x69\x73\x61\x62\x6c\x65\x64\x3d\x3d\
+\x3d\x21\x31\x26\x26\x61\x2e\x74\x79\x70\x65\x21\x3d\x3d\x22\x68\
+\x69\x64\x64\x65\x6e\x22\x7d\x2c\x64\x69\x73\x61\x62\x6c\x65\x64\
+\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x72\x65\x74\
+\x75\x72\x6e\x20\x61\x2e\x64\x69\x73\x61\x62\x6c\x65\x64\x3d\x3d\
+\x3d\x21\x30\x7d\x2c\x63\x68\x65\x63\x6b\x65\x64\x3a\x66\x75\x6e\
+\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\
+\x61\x2e\x63\x68\x65\x63\x6b\x65\x64\x3d\x3d\x3d\x21\x30\x7d\x2c\
+\x73\x65\x6c\x65\x63\x74\x65\x64\x3a\x66\x75\x6e\x63\x74\x69\x6f\
+\x6e\x28\x61\x29\x7b\x61\x2e\x70\x61\x72\x65\x6e\x74\x4e\x6f\x64\
+\x65\x26\x26\x61\x2e\x70\x61\x72\x65\x6e\x74\x4e\x6f\x64\x65\x2e\
+\x73\x65\x6c\x65\x63\x74\x65\x64\x49\x6e\x64\x65\x78\x3b\x72\x65\
+\x74\x75\x72\x6e\x20\x61\x2e\x73\x65\x6c\x65\x63\x74\x65\x64\x3d\
+\x3d\x3d\x21\x30\x7d\x2c\x70\x61\x72\x65\x6e\x74\x3a\x66\x75\x6e\
+\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x72\x65\x74\x75\x72\x6e\x21\
+\x21\x61\x2e\x66\x69\x72\x73\x74\x43\x68\x69\x6c\x64\x7d\x2c\x65\
+\x6d\x70\x74\x79\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\
+\x7b\x72\x65\x74\x75\x72\x6e\x21\x61\x2e\x66\x69\x72\x73\x74\x43\
+\x68\x69\x6c\x64\x7d\x2c\x68\x61\x73\x3a\x66\x75\x6e\x63\x74\x69\
+\x6f\x6e\x28\x61\x2c\x62\x2c\x63\x29\x7b\x72\x65\x74\x75\x72\x6e\
+\x21\x21\x6d\x28\x63\x5b\x33\x5d\x2c\x61\x29\x2e\x6c\x65\x6e\x67\
+\x74\x68\x7d\x2c\x68\x65\x61\x64\x65\x72\x3a\x66\x75\x6e\x63\x74\
+\x69\x6f\x6e\x28\x61\x29\x7b\x72\x65\x74\x75\x72\x6e\x2f\x68\x5c\
+\x64\x2f\x69\x2e\x74\x65\x73\x74\x28\x61\x2e\x6e\x6f\x64\x65\x4e\
+\x61\x6d\x65\x29\x7d\x2c\x74\x65\x78\x74\x3a\x66\x75\x6e\x63\x74\
+\x69\x6f\x6e\x28\x61\x29\x7b\x76\x61\x72\x20\x62\x3d\x61\x2e\x67\
+\x65\x74\x41\x74\x74\x72\x69\x62\x75\x74\x65\x28\x22\x74\x79\x70\
+\x65\x22\x29\x2c\x63\x3d\x61\x2e\x74\x79\x70\x65\x3b\x72\x65\x74\
+\x75\x72\x6e\x20\x61\x2e\x6e\x6f\x64\x65\x4e\x61\x6d\x65\x2e\x74\
+\x6f\x4c\x6f\x77\x65\x72\x43\x61\x73\x65\x28\x29\x3d\x3d\x3d\x22\
+\x69\x6e\x70\x75\x74\x22\x26\x26\x22\x74\x65\x78\x74\x22\x3d\x3d\
+\x3d\x63\x26\x26\x28\x62\x3d\x3d\x3d\x63\x7c\x7c\x62\x3d\x3d\x3d\
+\x6e\x75\x6c\x6c\x29\x7d\x2c\x72\x61\x64\x69\x6f\x3a\x66\x75\x6e\
+\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\
+\x61\x2e\x6e\x6f\x64\x65\x4e\x61\x6d\x65\x2e\x74\x6f\x4c\x6f\x77\
+\x65\x72\x43\x61\x73\x65\x28\x29\x3d\x3d\x3d\x22\x69\x6e\x70\x75\
+\x74\x22\x26\x26\x22\x72\x61\x64\x69\x6f\x22\x3d\x3d\x3d\x61\x2e\
+\x74\x79\x70\x65\x7d\x2c\x63\x68\x65\x63\x6b\x62\x6f\x78\x3a\x66\
+\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x72\x65\x74\x75\x72\
+\x6e\x20\x61\x2e\x6e\x6f\x64\x65\x4e\x61\x6d\x65\x2e\x74\x6f\x4c\
+\x6f\x77\x65\x72\x43\x61\x73\x65\x28\x29\x3d\x3d\x3d\x22\x69\x6e\
+\x70\x75\x74\x22\x26\x26\x22\x63\x68\x65\x63\x6b\x62\x6f\x78\x22\
+\x3d\x3d\x3d\x61\x2e\x74\x79\x70\x65\x7d\x2c\x66\x69\x6c\x65\x3a\
+\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x72\x65\x74\x75\
+\x72\x6e\x20\x61\x2e\x6e\x6f\x64\x65\x4e\x61\x6d\x65\x2e\x74\x6f\
+\x4c\x6f\x77\x65\x72\x43\x61\x73\x65\x28\x29\x3d\x3d\x3d\x22\x69\
+\x6e\x70\x75\x74\x22\x26\x26\x22\x66\x69\x6c\x65\x22\x3d\x3d\x3d\
+\x61\x2e\x74\x79\x70\x65\x7d\x2c\x70\x61\x73\x73\x77\x6f\x72\x64\
+\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x72\x65\x74\
+\x75\x72\x6e\x20\x61\x2e\x6e\x6f\x64\x65\x4e\x61\x6d\x65\x2e\x74\
+\x6f\x4c\x6f\x77\x65\x72\x43\x61\x73\x65\x28\x29\x3d\x3d\x3d\x22\
+\x69\x6e\x70\x75\x74\x22\x26\x26\x22\x70\x61\x73\x73\x77\x6f\x72\
+\x64\x22\x3d\x3d\x3d\x61\x2e\x74\x79\x70\x65\x7d\x2c\x73\x75\x62\
+\x6d\x69\x74\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\
+\x76\x61\x72\x20\x62\x3d\x61\x2e\x6e\x6f\x64\x65\x4e\x61\x6d\x65\
+\x2e\x74\x6f\x4c\x6f\x77\x65\x72\x43\x61\x73\x65\x28\x29\x3b\x72\
+\x65\x74\x75\x72\x6e\x28\x62\x3d\x3d\x3d\x22\x69\x6e\x70\x75\x74\
+\x22\x7c\x7c\x62\x3d\x3d\x3d\x22\x62\x75\x74\x74\x6f\x6e\x22\x29\
+\x26\x26\x22\x73\x75\x62\x6d\x69\x74\x22\x3d\x3d\x3d\x61\x2e\x74\
+\x79\x70\x65\x7d\x2c\x69\x6d\x61\x67\x65\x3a\x66\x75\x6e\x63\x74\
+\x69\x6f\x6e\x28\x61\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x61\x2e\
+\x6e\x6f\x64\x65\x4e\x61\x6d\x65\x2e\x74\x6f\x4c\x6f\x77\x65\x72\
+\x43\x61\x73\x65\x28\x29\x3d\x3d\x3d\x22\x69\x6e\x70\x75\x74\x22\
+\x26\x26\x22\x69\x6d\x61\x67\x65\x22\x3d\x3d\x3d\x61\x2e\x74\x79\
+\x70\x65\x7d\x2c\x72\x65\x73\x65\x74\x3a\x66\x75\x6e\x63\x74\x69\
+\x6f\x6e\x28\x61\x29\x7b\x76\x61\x72\x20\x62\x3d\x61\x2e\x6e\x6f\
+\x64\x65\x4e\x61\x6d\x65\x2e\x74\x6f\x4c\x6f\x77\x65\x72\x43\x61\
+\x73\x65\x28\x29\x3b\x72\x65\x74\x75\x72\x6e\x28\x62\x3d\x3d\x3d\
+\x22\x69\x6e\x70\x75\x74\x22\x7c\x7c\x62\x3d\x3d\x3d\x22\x62\x75\
+\x74\x74\x6f\x6e\x22\x29\x26\x26\x22\x72\x65\x73\x65\x74\x22\x3d\
+\x3d\x3d\x61\x2e\x74\x79\x70\x65\x7d\x2c\x62\x75\x74\x74\x6f\x6e\
+\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x76\x61\x72\
+\x20\x62\x3d\x61\x2e\x6e\x6f\x64\x65\x4e\x61\x6d\x65\x2e\x74\x6f\
+\x4c\x6f\x77\x65\x72\x43\x61\x73\x65\x28\x29\x3b\x72\x65\x74\x75\
+\x72\x6e\x20\x62\x3d\x3d\x3d\x22\x69\x6e\x70\x75\x74\x22\x26\x26\
+\x22\x62\x75\x74\x74\x6f\x6e\x22\x3d\x3d\x3d\x61\x2e\x74\x79\x70\
+\x65\x7c\x7c\x62\x3d\x3d\x3d\x22\x62\x75\x74\x74\x6f\x6e\x22\x7d\
+\x2c\x69\x6e\x70\x75\x74\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\
+\x61\x29\x7b\x72\x65\x74\x75\x72\x6e\x2f\x69\x6e\x70\x75\x74\x7c\
+\x73\x65\x6c\x65\x63\x74\x7c\x74\x65\x78\x74\x61\x72\x65\x61\x7c\
+\x62\x75\x74\x74\x6f\x6e\x2f\x69\x2e\x74\x65\x73\x74\x28\x61\x2e\
+\x6e\x6f\x64\x65\x4e\x61\x6d\x65\x29\x7d\x2c\x66\x6f\x63\x75\x73\
+\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x72\x65\x74\
+\x75\x72\x6e\x20\x61\x3d\x3d\x3d\x61\x2e\x6f\x77\x6e\x65\x72\x44\
+\x6f\x63\x75\x6d\x65\x6e\x74\x2e\x61\x63\x74\x69\x76\x65\x45\x6c\
+\x65\x6d\x65\x6e\x74\x7d\x7d\x2c\x73\x65\x74\x46\x69\x6c\x74\x65\
+\x72\x73\x3a\x7b\x66\x69\x72\x73\x74\x3a\x66\x75\x6e\x63\x74\x69\
+\x6f\x6e\x28\x61\x2c\x62\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x62\
+\x3d\x3d\x3d\x30\x7d\x2c\x6c\x61\x73\x74\x3a\x66\x75\x6e\x63\x74\
+\x69\x6f\x6e\x28\x61\x2c\x62\x2c\x63\x2c\x64\x29\x7b\x72\x65\x74\
+\x75\x72\x6e\x20\x62\x3d\x3d\x3d\x64\x2e\x6c\x65\x6e\x67\x74\x68\
+\x2d\x31\x7d\x2c\x65\x76\x65\x6e\x3a\x66\x75\x6e\x63\x74\x69\x6f\
+\x6e\x28\x61\x2c\x62\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x62\x25\
+\x32\x3d\x3d\x3d\x30\x7d\x2c\x6f\x64\x64\x3a\x66\x75\x6e\x63\x74\
+\x69\x6f\x6e\x28\x61\x2c\x62\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\
+\x62\x25\x32\x3d\x3d\x3d\x31\x7d\x2c\x6c\x74\x3a\x66\x75\x6e\x63\
+\x74\x69\x6f\x6e\x28\x61\x2c\x62\x2c\x63\x29\x7b\x72\x65\x74\x75\
+\x72\x6e\x20\x62\x3c\x63\x5b\x33\x5d\x2d\x30\x7d\x2c\x67\x74\x3a\
+\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x2c\x63\x29\x7b\
+\x72\x65\x74\x75\x72\x6e\x20\x62\x3e\x63\x5b\x33\x5d\x2d\x30\x7d\
+\x2c\x6e\x74\x68\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\
+\x62\x2c\x63\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x63\x5b\x33\x5d\
+\x2d\x30\x3d\x3d\x3d\x62\x7d\x2c\x65\x71\x3a\x66\x75\x6e\x63\x74\
+\x69\x6f\x6e\x28\x61\x2c\x62\x2c\x63\x29\x7b\x72\x65\x74\x75\x72\
+\x6e\x20\x63\x5b\x33\x5d\x2d\x30\x3d\x3d\x3d\x62\x7d\x7d\x2c\x66\
+\x69\x6c\x74\x65\x72\x3a\x7b\x50\x53\x45\x55\x44\x4f\x3a\x66\x75\
+\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x2c\x63\x2c\x64\x29\x7b\
+\x76\x61\x72\x20\x65\x3d\x62\x5b\x31\x5d\x2c\x66\x3d\x6f\x2e\x66\
+\x69\x6c\x74\x65\x72\x73\x5b\x65\x5d\x3b\x69\x66\x28\x66\x29\x72\
+\x65\x74\x75\x72\x6e\x20\x66\x28\x61\x2c\x63\x2c\x62\x2c\x64\x29\
+\x3b\x69\x66\x28\x65\x3d\x3d\x3d\x22\x63\x6f\x6e\x74\x61\x69\x6e\
+\x73\x22\x29\x72\x65\x74\x75\x72\x6e\x28\x61\x2e\x74\x65\x78\x74\
+\x43\x6f\x6e\x74\x65\x6e\x74\x7c\x7c\x61\x2e\x69\x6e\x6e\x65\x72\
+\x54\x65\x78\x74\x7c\x7c\x6e\x28\x5b\x61\x5d\x29\x7c\x7c\x22\x22\
+\x29\x2e\x69\x6e\x64\x65\x78\x4f\x66\x28\x62\x5b\x33\x5d\x29\x3e\
+\x3d\x30\x3b\x69\x66\x28\x65\x3d\x3d\x3d\x22\x6e\x6f\x74\x22\x29\
+\x7b\x76\x61\x72\x20\x67\x3d\x62\x5b\x33\x5d\x3b\x66\x6f\x72\x28\
+\x76\x61\x72\x20\x68\x3d\x30\x2c\x69\x3d\x67\x2e\x6c\x65\x6e\x67\
+\x74\x68\x3b\x68\x3c\x69\x3b\x68\x2b\x2b\x29\x69\x66\x28\x67\x5b\
+\x68\x5d\x3d\x3d\x3d\x61\x29\x72\x65\x74\x75\x72\x6e\x21\x31\x3b\
+\x72\x65\x74\x75\x72\x6e\x21\x30\x7d\x6d\x2e\x65\x72\x72\x6f\x72\
+\x28\x65\x29\x7d\x2c\x43\x48\x49\x4c\x44\x3a\x66\x75\x6e\x63\x74\
+\x69\x6f\x6e\x28\x61\x2c\x62\x29\x7b\x76\x61\x72\x20\x63\x2c\x65\
+\x2c\x66\x2c\x67\x2c\x68\x2c\x69\x2c\x6a\x2c\x6b\x3d\x62\x5b\x31\
+\x5d\x2c\x6c\x3d\x61\x3b\x73\x77\x69\x74\x63\x68\x28\x6b\x29\x7b\
+\x63\x61\x73\x65\x22\x6f\x6e\x6c\x79\x22\x3a\x63\x61\x73\x65\x22\
+\x66\x69\x72\x73\x74\x22\x3a\x77\x68\x69\x6c\x65\x28\x6c\x3d\x6c\
+\x2e\x70\x72\x65\x76\x69\x6f\x75\x73\x53\x69\x62\x6c\x69\x6e\x67\
+\x29\x69\x66\x28\x6c\x2e\x6e\x6f\x64\x65\x54\x79\x70\x65\x3d\x3d\
+\x3d\x31\x29\x72\x65\x74\x75\x72\x6e\x21\x31\x3b\x69\x66\x28\x6b\
+\x3d\x3d\x3d\x22\x66\x69\x72\x73\x74\x22\x29\x72\x65\x74\x75\x72\
+\x6e\x21\x30\x3b\x6c\x3d\x61\x3b\x63\x61\x73\x65\x22\x6c\x61\x73\
+\x74\x22\x3a\x77\x68\x69\x6c\x65\x28\x6c\x3d\x6c\x2e\x6e\x65\x78\
+\x74\x53\x69\x62\x6c\x69\x6e\x67\x29\x69\x66\x28\x6c\x2e\x6e\x6f\
+\x64\x65\x54\x79\x70\x65\x3d\x3d\x3d\x31\x29\x72\x65\x74\x75\x72\
+\x6e\x21\x31\x3b\x72\x65\x74\x75\x72\x6e\x21\x30\x3b\x63\x61\x73\
+\x65\x22\x6e\x74\x68\x22\x3a\x63\x3d\x62\x5b\x32\x5d\x2c\x65\x3d\
+\x62\x5b\x33\x5d\x3b\x69\x66\x28\x63\x3d\x3d\x3d\x31\x26\x26\x65\
+\x3d\x3d\x3d\x30\x29\x72\x65\x74\x75\x72\x6e\x21\x30\x3b\x66\x3d\
+\x62\x5b\x30\x5d\x2c\x67\x3d\x61\x2e\x70\x61\x72\x65\x6e\x74\x4e\
+\x6f\x64\x65\x3b\x69\x66\x28\x67\x26\x26\x28\x67\x5b\x64\x5d\x21\
+\x3d\x3d\x66\x7c\x7c\x21\x61\x2e\x6e\x6f\x64\x65\x49\x6e\x64\x65\
+\x78\x29\x29\x7b\x69\x3d\x30\x3b\x66\x6f\x72\x28\x6c\x3d\x67\x2e\
+\x66\x69\x72\x73\x74\x43\x68\x69\x6c\x64\x3b\x6c\x3b\x6c\x3d\x6c\
+\x2e\x6e\x65\x78\x74\x53\x69\x62\x6c\x69\x6e\x67\x29\x6c\x2e\x6e\
+\x6f\x64\x65\x54\x79\x70\x65\x3d\x3d\x3d\x31\x26\x26\x28\x6c\x2e\
+\x6e\x6f\x64\x65\x49\x6e\x64\x65\x78\x3d\x2b\x2b\x69\x29\x3b\x67\
+\x5b\x64\x5d\x3d\x66\x7d\x6a\x3d\x61\x2e\x6e\x6f\x64\x65\x49\x6e\
+\x64\x65\x78\x2d\x65\x3b\x72\x65\x74\x75\x72\x6e\x20\x63\x3d\x3d\
+\x3d\x30\x3f\x6a\x3d\x3d\x3d\x30\x3a\x6a\x25\x63\x3d\x3d\x3d\x30\
+\x26\x26\x6a\x2f\x63\x3e\x3d\x30\x7d\x7d\x2c\x49\x44\x3a\x66\x75\
+\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x29\x7b\x72\x65\x74\x75\
+\x72\x6e\x20\x61\x2e\x6e\x6f\x64\x65\x54\x79\x70\x65\x3d\x3d\x3d\
+\x31\x26\x26\x61\x2e\x67\x65\x74\x41\x74\x74\x72\x69\x62\x75\x74\
+\x65\x28\x22\x69\x64\x22\x29\x3d\x3d\x3d\x62\x7d\x2c\x54\x41\x47\
+\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x29\x7b\x72\
+\x65\x74\x75\x72\x6e\x20\x62\x3d\x3d\x3d\x22\x2a\x22\x26\x26\x61\
+\x2e\x6e\x6f\x64\x65\x54\x79\x70\x65\x3d\x3d\x3d\x31\x7c\x7c\x21\
+\x21\x61\x2e\x6e\x6f\x64\x65\x4e\x61\x6d\x65\x26\x26\x61\x2e\x6e\
+\x6f\x64\x65\x4e\x61\x6d\x65\x2e\x74\x6f\x4c\x6f\x77\x65\x72\x43\
+\x61\x73\x65\x28\x29\x3d\x3d\x3d\x62\x7d\x2c\x43\x4c\x41\x53\x53\
+\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x29\x7b\x72\
+\x65\x74\x75\x72\x6e\x28\x22\x20\x22\x2b\x28\x61\x2e\x63\x6c\x61\
+\x73\x73\x4e\x61\x6d\x65\x7c\x7c\x61\x2e\x67\x65\x74\x41\x74\x74\
+\x72\x69\x62\x75\x74\x65\x28\x22\x63\x6c\x61\x73\x73\x22\x29\x29\
+\x2b\x22\x20\x22\x29\x2e\x69\x6e\x64\x65\x78\x4f\x66\x28\x62\x29\
+\x3e\x2d\x31\x7d\x2c\x41\x54\x54\x52\x3a\x66\x75\x6e\x63\x74\x69\
+\x6f\x6e\x28\x61\x2c\x62\x29\x7b\x76\x61\x72\x20\x63\x3d\x62\x5b\
+\x31\x5d\x2c\x64\x3d\x6d\x2e\x61\x74\x74\x72\x3f\x6d\x2e\x61\x74\
+\x74\x72\x28\x61\x2c\x63\x29\x3a\x6f\x2e\x61\x74\x74\x72\x48\x61\
+\x6e\x64\x6c\x65\x5b\x63\x5d\x3f\x6f\x2e\x61\x74\x74\x72\x48\x61\
+\x6e\x64\x6c\x65\x5b\x63\x5d\x28\x61\x29\x3a\x61\x5b\x63\x5d\x21\
+\x3d\x6e\x75\x6c\x6c\x3f\x61\x5b\x63\x5d\x3a\x61\x2e\x67\x65\x74\
+\x41\x74\x74\x72\x69\x62\x75\x74\x65\x28\x63\x29\x2c\x65\x3d\x64\
+\x2b\x22\x22\x2c\x66\x3d\x62\x5b\x32\x5d\x2c\x67\x3d\x62\x5b\x34\
+\x5d\x3b\x72\x65\x74\x75\x72\x6e\x20\x64\x3d\x3d\x6e\x75\x6c\x6c\
+\x3f\x66\x3d\x3d\x3d\x22\x21\x3d\x22\x3a\x21\x66\x26\x26\x6d\x2e\
+\x61\x74\x74\x72\x3f\x64\x21\x3d\x6e\x75\x6c\x6c\x3a\x66\x3d\x3d\
+\x3d\x22\x3d\x22\x3f\x65\x3d\x3d\x3d\x67\x3a\x66\x3d\x3d\x3d\x22\
+\x2a\x3d\x22\x3f\x65\x2e\x69\x6e\x64\x65\x78\x4f\x66\x28\x67\x29\
+\x3e\x3d\x30\x3a\x66\x3d\x3d\x3d\x22\x7e\x3d\x22\x3f\x28\x22\x20\
+\x22\x2b\x65\x2b\x22\x20\x22\x29\x2e\x69\x6e\x64\x65\x78\x4f\x66\
+\x28\x67\x29\x3e\x3d\x30\x3a\x67\x3f\x66\x3d\x3d\x3d\x22\x21\x3d\
+\x22\x3f\x65\x21\x3d\x3d\x67\x3a\x66\x3d\x3d\x3d\x22\x5e\x3d\x22\
+\x3f\x65\x2e\x69\x6e\x64\x65\x78\x4f\x66\x28\x67\x29\x3d\x3d\x3d\
+\x30\x3a\x66\x3d\x3d\x3d\x22\x24\x3d\x22\x3f\x65\x2e\x73\x75\x62\
+\x73\x74\x72\x28\x65\x2e\x6c\x65\x6e\x67\x74\x68\x2d\x67\x2e\x6c\
+\x65\x6e\x67\x74\x68\x29\x3d\x3d\x3d\x67\x3a\x66\x3d\x3d\x3d\x22\
+\x7c\x3d\x22\x3f\x65\x3d\x3d\x3d\x67\x7c\x7c\x65\x2e\x73\x75\x62\
+\x73\x74\x72\x28\x30\x2c\x67\x2e\x6c\x65\x6e\x67\x74\x68\x2b\x31\
+\x29\x3d\x3d\x3d\x67\x2b\x22\x2d\x22\x3a\x21\x31\x3a\x65\x26\x26\
+\x64\x21\x3d\x3d\x21\x31\x7d\x2c\x50\x4f\x53\x3a\x66\x75\x6e\x63\
+\x74\x69\x6f\x6e\x28\x61\x2c\x62\x2c\x63\x2c\x64\x29\x7b\x76\x61\
+\x72\x20\x65\x3d\x62\x5b\x32\x5d\x2c\x66\x3d\x6f\x2e\x73\x65\x74\
+\x46\x69\x6c\x74\x65\x72\x73\x5b\x65\x5d\x3b\x69\x66\x28\x66\x29\
+\x72\x65\x74\x75\x72\x6e\x20\x66\x28\x61\x2c\x63\x2c\x62\x2c\x64\
+\x29\x7d\x7d\x7d\x2c\x70\x3d\x6f\x2e\x6d\x61\x74\x63\x68\x2e\x50\
+\x4f\x53\x2c\x71\x3d\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\
+\x62\x29\x7b\x72\x65\x74\x75\x72\x6e\x22\x5c\x5c\x22\x2b\x28\x62\
+\x2d\x30\x2b\x31\x29\x7d\x3b\x66\x6f\x72\x28\x76\x61\x72\x20\x72\
+\x20\x69\x6e\x20\x6f\x2e\x6d\x61\x74\x63\x68\x29\x6f\x2e\x6d\x61\
+\x74\x63\x68\x5b\x72\x5d\x3d\x6e\x65\x77\x20\x52\x65\x67\x45\x78\
+\x70\x28\x6f\x2e\x6d\x61\x74\x63\x68\x5b\x72\x5d\x2e\x73\x6f\x75\
+\x72\x63\x65\x2b\x2f\x28\x3f\x21\x5b\x5e\x5c\x5b\x5d\x2a\x5c\x5d\
+\x29\x28\x3f\x21\x5b\x5e\x5c\x28\x5d\x2a\x5c\x29\x29\x2f\x2e\x73\
+\x6f\x75\x72\x63\x65\x29\x2c\x6f\x2e\x6c\x65\x66\x74\x4d\x61\x74\
+\x63\x68\x5b\x72\x5d\x3d\x6e\x65\x77\x20\x52\x65\x67\x45\x78\x70\
+\x28\x2f\x28\x5e\x28\x3f\x3a\x2e\x7c\x5c\x72\x7c\x5c\x6e\x29\x2a\
+\x3f\x29\x2f\x2e\x73\x6f\x75\x72\x63\x65\x2b\x6f\x2e\x6d\x61\x74\
+\x63\x68\x5b\x72\x5d\x2e\x73\x6f\x75\x72\x63\x65\x2e\x72\x65\x70\
+\x6c\x61\x63\x65\x28\x2f\x5c\x5c\x28\x5c\x64\x2b\x29\x2f\x67\x2c\
+\x71\x29\x29\x3b\x76\x61\x72\x20\x73\x3d\x66\x75\x6e\x63\x74\x69\
+\x6f\x6e\x28\x61\x2c\x62\x29\x7b\x61\x3d\x41\x72\x72\x61\x79\x2e\
+\x70\x72\x6f\x74\x6f\x74\x79\x70\x65\x2e\x73\x6c\x69\x63\x65\x2e\
+\x63\x61\x6c\x6c\x28\x61\x2c\x30\x29\x3b\x69\x66\x28\x62\x29\x7b\
+\x62\x2e\x70\x75\x73\x68\x2e\x61\x70\x70\x6c\x79\x28\x62\x2c\x61\
+\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x62\x7d\x72\x65\x74\x75\x72\
+\x6e\x20\x61\x7d\x3b\x74\x72\x79\x7b\x41\x72\x72\x61\x79\x2e\x70\
+\x72\x6f\x74\x6f\x74\x79\x70\x65\x2e\x73\x6c\x69\x63\x65\x2e\x63\
+\x61\x6c\x6c\x28\x63\x2e\x64\x6f\x63\x75\x6d\x65\x6e\x74\x45\x6c\
+\x65\x6d\x65\x6e\x74\x2e\x63\x68\x69\x6c\x64\x4e\x6f\x64\x65\x73\
+\x2c\x30\x29\x5b\x30\x5d\x2e\x6e\x6f\x64\x65\x54\x79\x70\x65\x7d\
+\x63\x61\x74\x63\x68\x28\x74\x29\x7b\x73\x3d\x66\x75\x6e\x63\x74\
+\x69\x6f\x6e\x28\x61\x2c\x62\x29\x7b\x76\x61\x72\x20\x63\x3d\x30\
+\x2c\x64\x3d\x62\x7c\x7c\x5b\x5d\x3b\x69\x66\x28\x67\x2e\x63\x61\
+\x6c\x6c\x28\x61\x29\x3d\x3d\x3d\x22\x5b\x6f\x62\x6a\x65\x63\x74\
+\x20\x41\x72\x72\x61\x79\x5d\x22\x29\x41\x72\x72\x61\x79\x2e\x70\
+\x72\x6f\x74\x6f\x74\x79\x70\x65\x2e\x70\x75\x73\x68\x2e\x61\x70\
+\x70\x6c\x79\x28\x64\x2c\x61\x29\x3b\x65\x6c\x73\x65\x20\x69\x66\
+\x28\x74\x79\x70\x65\x6f\x66\x20\x61\x2e\x6c\x65\x6e\x67\x74\x68\
+\x3d\x3d\x22\x6e\x75\x6d\x62\x65\x72\x22\x29\x66\x6f\x72\x28\x76\
+\x61\x72\x20\x65\x3d\x61\x2e\x6c\x65\x6e\x67\x74\x68\x3b\x63\x3c\
+\x65\x3b\x63\x2b\x2b\x29\x64\x2e\x70\x75\x73\x68\x28\x61\x5b\x63\
+\x5d\x29\x3b\x65\x6c\x73\x65\x20\x66\x6f\x72\x28\x3b\x61\x5b\x63\
+\x5d\x3b\x63\x2b\x2b\x29\x64\x2e\x70\x75\x73\x68\x28\x61\x5b\x63\
+\x5d\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x64\x7d\x7d\x76\x61\x72\
+\x20\x75\x2c\x76\x3b\x63\x2e\x64\x6f\x63\x75\x6d\x65\x6e\x74\x45\
+\x6c\x65\x6d\x65\x6e\x74\x2e\x63\x6f\x6d\x70\x61\x72\x65\x44\x6f\
+\x63\x75\x6d\x65\x6e\x74\x50\x6f\x73\x69\x74\x69\x6f\x6e\x3f\x75\
+\x3d\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x29\x7b\x69\
+\x66\x28\x61\x3d\x3d\x3d\x62\x29\x7b\x68\x3d\x21\x30\x3b\x72\x65\
+\x74\x75\x72\x6e\x20\x30\x7d\x69\x66\x28\x21\x61\x2e\x63\x6f\x6d\
+\x70\x61\x72\x65\x44\x6f\x63\x75\x6d\x65\x6e\x74\x50\x6f\x73\x69\
+\x74\x69\x6f\x6e\x7c\x7c\x21\x62\x2e\x63\x6f\x6d\x70\x61\x72\x65\
+\x44\x6f\x63\x75\x6d\x65\x6e\x74\x50\x6f\x73\x69\x74\x69\x6f\x6e\
+\x29\x72\x65\x74\x75\x72\x6e\x20\x61\x2e\x63\x6f\x6d\x70\x61\x72\
+\x65\x44\x6f\x63\x75\x6d\x65\x6e\x74\x50\x6f\x73\x69\x74\x69\x6f\
+\x6e\x3f\x2d\x31\x3a\x31\x3b\x72\x65\x74\x75\x72\x6e\x20\x61\x2e\
+\x63\x6f\x6d\x70\x61\x72\x65\x44\x6f\x63\x75\x6d\x65\x6e\x74\x50\
+\x6f\x73\x69\x74\x69\x6f\x6e\x28\x62\x29\x26\x34\x3f\x2d\x31\x3a\
+\x31\x7d\x3a\x28\x75\x3d\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\
+\x2c\x62\x29\x7b\x69\x66\x28\x61\x3d\x3d\x3d\x62\x29\x7b\x68\x3d\
+\x21\x30\x3b\x72\x65\x74\x75\x72\x6e\x20\x30\x7d\x69\x66\x28\x61\
+\x2e\x73\x6f\x75\x72\x63\x65\x49\x6e\x64\x65\x78\x26\x26\x62\x2e\
+\x73\x6f\x75\x72\x63\x65\x49\x6e\x64\x65\x78\x29\x72\x65\x74\x75\
+\x72\x6e\x20\x61\x2e\x73\x6f\x75\x72\x63\x65\x49\x6e\x64\x65\x78\
+\x2d\x62\x2e\x73\x6f\x75\x72\x63\x65\x49\x6e\x64\x65\x78\x3b\x76\
+\x61\x72\x20\x63\x2c\x64\x2c\x65\x3d\x5b\x5d\x2c\x66\x3d\x5b\x5d\
+\x2c\x67\x3d\x61\x2e\x70\x61\x72\x65\x6e\x74\x4e\x6f\x64\x65\x2c\
+\x69\x3d\x62\x2e\x70\x61\x72\x65\x6e\x74\x4e\x6f\x64\x65\x2c\x6a\
+\x3d\x67\x3b\x69\x66\x28\x67\x3d\x3d\x3d\x69\x29\x72\x65\x74\x75\
+\x72\x6e\x20\x76\x28\x61\x2c\x62\x29\x3b\x69\x66\x28\x21\x67\x29\
+\x72\x65\x74\x75\x72\x6e\x2d\x31\x3b\x69\x66\x28\x21\x69\x29\x72\
+\x65\x74\x75\x72\x6e\x20\x31\x3b\x77\x68\x69\x6c\x65\x28\x6a\x29\
+\x65\x2e\x75\x6e\x73\x68\x69\x66\x74\x28\x6a\x29\x2c\x6a\x3d\x6a\
+\x2e\x70\x61\x72\x65\x6e\x74\x4e\x6f\x64\x65\x3b\x6a\x3d\x69\x3b\
+\x77\x68\x69\x6c\x65\x28\x6a\x29\x66\x2e\x75\x6e\x73\x68\x69\x66\
+\x74\x28\x6a\x29\x2c\x6a\x3d\x6a\x2e\x70\x61\x72\x65\x6e\x74\x4e\
+\x6f\x64\x65\x3b\x63\x3d\x65\x2e\x6c\x65\x6e\x67\x74\x68\x2c\x64\
+\x3d\x66\x2e\x6c\x65\x6e\x67\x74\x68\x3b\x66\x6f\x72\x28\x76\x61\
+\x72\x20\x6b\x3d\x30\x3b\x6b\x3c\x63\x26\x26\x6b\x3c\x64\x3b\x6b\
+\x2b\x2b\x29\x69\x66\x28\x65\x5b\x6b\x5d\x21\x3d\x3d\x66\x5b\x6b\
+\x5d\x29\x72\x65\x74\x75\x72\x6e\x20\x76\x28\x65\x5b\x6b\x5d\x2c\
+\x66\x5b\x6b\x5d\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x6b\x3d\x3d\
+\x3d\x63\x3f\x76\x28\x61\x2c\x66\x5b\x6b\x5d\x2c\x2d\x31\x29\x3a\
+\x76\x28\x65\x5b\x6b\x5d\x2c\x62\x2c\x31\x29\x7d\x2c\x76\x3d\x66\
+\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x2c\x63\x29\x7b\x69\
+\x66\x28\x61\x3d\x3d\x3d\x62\x29\x72\x65\x74\x75\x72\x6e\x20\x63\
+\x3b\x76\x61\x72\x20\x64\x3d\x61\x2e\x6e\x65\x78\x74\x53\x69\x62\
+\x6c\x69\x6e\x67\x3b\x77\x68\x69\x6c\x65\x28\x64\x29\x7b\x69\x66\
+\x28\x64\x3d\x3d\x3d\x62\x29\x72\x65\x74\x75\x72\x6e\x2d\x31\x3b\
+\x64\x3d\x64\x2e\x6e\x65\x78\x74\x53\x69\x62\x6c\x69\x6e\x67\x7d\
+\x72\x65\x74\x75\x72\x6e\x20\x31\x7d\x29\x2c\x66\x75\x6e\x63\x74\
+\x69\x6f\x6e\x28\x29\x7b\x76\x61\x72\x20\x61\x3d\x63\x2e\x63\x72\
+\x65\x61\x74\x65\x45\x6c\x65\x6d\x65\x6e\x74\x28\x22\x64\x69\x76\
+\x22\x29\x2c\x64\x3d\x22\x73\x63\x72\x69\x70\x74\x22\x2b\x28\x6e\
+\x65\x77\x20\x44\x61\x74\x65\x29\x2e\x67\x65\x74\x54\x69\x6d\x65\
+\x28\x29\x2c\x65\x3d\x63\x2e\x64\x6f\x63\x75\x6d\x65\x6e\x74\x45\
+\x6c\x65\x6d\x65\x6e\x74\x3b\x61\x2e\x69\x6e\x6e\x65\x72\x48\x54\
+\x4d\x4c\x3d\x22\x3c\x61\x20\x6e\x61\x6d\x65\x3d\x27\x22\x2b\x64\
+\x2b\x22\x27\x2f\x3e\x22\x2c\x65\x2e\x69\x6e\x73\x65\x72\x74\x42\
+\x65\x66\x6f\x72\x65\x28\x61\x2c\x65\x2e\x66\x69\x72\x73\x74\x43\
+\x68\x69\x6c\x64\x29\x2c\x63\x2e\x67\x65\x74\x45\x6c\x65\x6d\x65\
+\x6e\x74\x42\x79\x49\x64\x28\x64\x29\x26\x26\x28\x6f\x2e\x66\x69\
+\x6e\x64\x2e\x49\x44\x3d\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\
+\x2c\x63\x2c\x64\x29\x7b\x69\x66\x28\x74\x79\x70\x65\x6f\x66\x20\
+\x63\x2e\x67\x65\x74\x45\x6c\x65\x6d\x65\x6e\x74\x42\x79\x49\x64\
+\x21\x3d\x22\x75\x6e\x64\x65\x66\x69\x6e\x65\x64\x22\x26\x26\x21\
+\x64\x29\x7b\x76\x61\x72\x20\x65\x3d\x63\x2e\x67\x65\x74\x45\x6c\
+\x65\x6d\x65\x6e\x74\x42\x79\x49\x64\x28\x61\x5b\x31\x5d\x29\x3b\
+\x72\x65\x74\x75\x72\x6e\x20\x65\x3f\x65\x2e\x69\x64\x3d\x3d\x3d\
+\x61\x5b\x31\x5d\x7c\x7c\x74\x79\x70\x65\x6f\x66\x20\x65\x2e\x67\
+\x65\x74\x41\x74\x74\x72\x69\x62\x75\x74\x65\x4e\x6f\x64\x65\x21\
+\x3d\x22\x75\x6e\x64\x65\x66\x69\x6e\x65\x64\x22\x26\x26\x65\x2e\
+\x67\x65\x74\x41\x74\x74\x72\x69\x62\x75\x74\x65\x4e\x6f\x64\x65\
+\x28\x22\x69\x64\x22\x29\x2e\x6e\x6f\x64\x65\x56\x61\x6c\x75\x65\
+\x3d\x3d\x3d\x61\x5b\x31\x5d\x3f\x5b\x65\x5d\x3a\x62\x3a\x5b\x5d\
+\x7d\x7d\x2c\x6f\x2e\x66\x69\x6c\x74\x65\x72\x2e\x49\x44\x3d\x66\
+\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x29\x7b\x76\x61\x72\
+\x20\x63\x3d\x74\x79\x70\x65\x6f\x66\x20\x61\x2e\x67\x65\x74\x41\
+\x74\x74\x72\x69\x62\x75\x74\x65\x4e\x6f\x64\x65\x21\x3d\x22\x75\
+\x6e\x64\x65\x66\x69\x6e\x65\x64\x22\x26\x26\x61\x2e\x67\x65\x74\
+\x41\x74\x74\x72\x69\x62\x75\x74\x65\x4e\x6f\x64\x65\x28\x22\x69\
+\x64\x22\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x61\x2e\x6e\x6f\x64\
+\x65\x54\x79\x70\x65\x3d\x3d\x3d\x31\x26\x26\x63\x26\x26\x63\x2e\
+\x6e\x6f\x64\x65\x56\x61\x6c\x75\x65\x3d\x3d\x3d\x62\x7d\x29\x2c\
+\x65\x2e\x72\x65\x6d\x6f\x76\x65\x43\x68\x69\x6c\x64\x28\x61\x29\
+\x2c\x65\x3d\x61\x3d\x6e\x75\x6c\x6c\x7d\x28\x29\x2c\x66\x75\x6e\
+\x63\x74\x69\x6f\x6e\x28\x29\x7b\x76\x61\x72\x20\x61\x3d\x63\x2e\
+\x63\x72\x65\x61\x74\x65\x45\x6c\x65\x6d\x65\x6e\x74\x28\x22\x64\
+\x69\x76\x22\x29\x3b\x61\x2e\x61\x70\x70\x65\x6e\x64\x43\x68\x69\
+\x6c\x64\x28\x63\x2e\x63\x72\x65\x61\x74\x65\x43\x6f\x6d\x6d\x65\
+\x6e\x74\x28\x22\x22\x29\x29\x2c\x61\x2e\x67\x65\x74\x45\x6c\x65\
+\x6d\x65\x6e\x74\x73\x42\x79\x54\x61\x67\x4e\x61\x6d\x65\x28\x22\
+\x2a\x22\x29\x2e\x6c\x65\x6e\x67\x74\x68\x3e\x30\x26\x26\x28\x6f\
+\x2e\x66\x69\x6e\x64\x2e\x54\x41\x47\x3d\x66\x75\x6e\x63\x74\x69\
+\x6f\x6e\x28\x61\x2c\x62\x29\x7b\x76\x61\x72\x20\x63\x3d\x62\x2e\
+\x67\x65\x74\x45\x6c\x65\x6d\x65\x6e\x74\x73\x42\x79\x54\x61\x67\
+\x4e\x61\x6d\x65\x28\x61\x5b\x31\x5d\x29\x3b\x69\x66\x28\x61\x5b\
+\x31\x5d\x3d\x3d\x3d\x22\x2a\x22\x29\x7b\x76\x61\x72\x20\x64\x3d\
+\x5b\x5d\x3b\x66\x6f\x72\x28\x76\x61\x72\x20\x65\x3d\x30\x3b\x63\
+\x5b\x65\x5d\x3b\x65\x2b\x2b\x29\x63\x5b\x65\x5d\x2e\x6e\x6f\x64\
+\x65\x54\x79\x70\x65\x3d\x3d\x3d\x31\x26\x26\x64\x2e\x70\x75\x73\
+\x68\x28\x63\x5b\x65\x5d\x29\x3b\x63\x3d\x64\x7d\x72\x65\x74\x75\
+\x72\x6e\x20\x63\x7d\x29\x2c\x61\x2e\x69\x6e\x6e\x65\x72\x48\x54\
+\x4d\x4c\x3d\x22\x3c\x61\x20\x68\x72\x65\x66\x3d\x27\x23\x27\x3e\
+\x3c\x2f\x61\x3e\x22\x2c\x61\x2e\x66\x69\x72\x73\x74\x43\x68\x69\
+\x6c\x64\x26\x26\x74\x79\x70\x65\x6f\x66\x20\x61\x2e\x66\x69\x72\
+\x73\x74\x43\x68\x69\x6c\x64\x2e\x67\x65\x74\x41\x74\x74\x72\x69\
+\x62\x75\x74\x65\x21\x3d\x22\x75\x6e\x64\x65\x66\x69\x6e\x65\x64\
+\x22\x26\x26\x61\x2e\x66\x69\x72\x73\x74\x43\x68\x69\x6c\x64\x2e\
+\x67\x65\x74\x41\x74\x74\x72\x69\x62\x75\x74\x65\x28\x22\x68\x72\
+\x65\x66\x22\x29\x21\x3d\x3d\x22\x23\x22\x26\x26\x28\x6f\x2e\x61\
+\x74\x74\x72\x48\x61\x6e\x64\x6c\x65\x2e\x68\x72\x65\x66\x3d\x66\
+\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x72\x65\x74\x75\x72\
+\x6e\x20\x61\x2e\x67\x65\x74\x41\x74\x74\x72\x69\x62\x75\x74\x65\
+\x28\x22\x68\x72\x65\x66\x22\x2c\x32\x29\x7d\x29\x2c\x61\x3d\x6e\
+\x75\x6c\x6c\x7d\x28\x29\x2c\x63\x2e\x71\x75\x65\x72\x79\x53\x65\
+\x6c\x65\x63\x74\x6f\x72\x41\x6c\x6c\x26\x26\x66\x75\x6e\x63\x74\
+\x69\x6f\x6e\x28\x29\x7b\x76\x61\x72\x20\x61\x3d\x6d\x2c\x62\x3d\
+\x63\x2e\x63\x72\x65\x61\x74\x65\x45\x6c\x65\x6d\x65\x6e\x74\x28\
+\x22\x64\x69\x76\x22\x29\x2c\x64\x3d\x22\x5f\x5f\x73\x69\x7a\x7a\
+\x6c\x65\x5f\x5f\x22\x3b\x62\x2e\x69\x6e\x6e\x65\x72\x48\x54\x4d\
+\x4c\x3d\x22\x3c\x70\x20\x63\x6c\x61\x73\x73\x3d\x27\x54\x45\x53\
+\x54\x27\x3e\x3c\x2f\x70\x3e\x22\x3b\x69\x66\x28\x21\x62\x2e\x71\
+\x75\x65\x72\x79\x53\x65\x6c\x65\x63\x74\x6f\x72\x41\x6c\x6c\x7c\
+\x7c\x62\x2e\x71\x75\x65\x72\x79\x53\x65\x6c\x65\x63\x74\x6f\x72\
+\x41\x6c\x6c\x28\x22\x2e\x54\x45\x53\x54\x22\x29\x2e\x6c\x65\x6e\
+\x67\x74\x68\x21\x3d\x3d\x30\x29\x7b\x6d\x3d\x66\x75\x6e\x63\x74\
+\x69\x6f\x6e\x28\x62\x2c\x65\x2c\x66\x2c\x67\x29\x7b\x65\x3d\x65\
+\x7c\x7c\x63\x3b\x69\x66\x28\x21\x67\x26\x26\x21\x6d\x2e\x69\x73\
+\x58\x4d\x4c\x28\x65\x29\x29\x7b\x76\x61\x72\x20\x68\x3d\x2f\x5e\
+\x28\x5c\x77\x2b\x24\x29\x7c\x5e\x5c\x2e\x28\x5b\x5c\x77\x5c\x2d\
+\x5d\x2b\x24\x29\x7c\x5e\x23\x28\x5b\x5c\x77\x5c\x2d\x5d\x2b\x24\
+\x29\x2f\x2e\x65\x78\x65\x63\x28\x62\x29\x3b\x69\x66\x28\x68\x26\
+\x26\x28\x65\x2e\x6e\x6f\x64\x65\x54\x79\x70\x65\x3d\x3d\x3d\x31\
+\x7c\x7c\x65\x2e\x6e\x6f\x64\x65\x54\x79\x70\x65\x3d\x3d\x3d\x39\
+\x29\x29\x7b\x69\x66\x28\x68\x5b\x31\x5d\x29\x72\x65\x74\x75\x72\
+\x6e\x20\x73\x28\x65\x2e\x67\x65\x74\x45\x6c\x65\x6d\x65\x6e\x74\
+\x73\x42\x79\x54\x61\x67\x4e\x61\x6d\x65\x28\x62\x29\x2c\x66\x29\
+\x3b\x69\x66\x28\x68\x5b\x32\x5d\x26\x26\x6f\x2e\x66\x69\x6e\x64\
+\x2e\x43\x4c\x41\x53\x53\x26\x26\x65\x2e\x67\x65\x74\x45\x6c\x65\
+\x6d\x65\x6e\x74\x73\x42\x79\x43\x6c\x61\x73\x73\x4e\x61\x6d\x65\
+\x29\x72\x65\x74\x75\x72\x6e\x20\x73\x28\x65\x2e\x67\x65\x74\x45\
+\x6c\x65\x6d\x65\x6e\x74\x73\x42\x79\x43\x6c\x61\x73\x73\x4e\x61\
+\x6d\x65\x28\x68\x5b\x32\x5d\x29\x2c\x66\x29\x7d\x69\x66\x28\x65\
+\x2e\x6e\x6f\x64\x65\x54\x79\x70\x65\x3d\x3d\x3d\x39\x29\x7b\x69\
+\x66\x28\x62\x3d\x3d\x3d\x22\x62\x6f\x64\x79\x22\x26\x26\x65\x2e\
+\x62\x6f\x64\x79\x29\x72\x65\x74\x75\x72\x6e\x20\x73\x28\x5b\x65\
+\x2e\x62\x6f\x64\x79\x5d\x2c\x66\x29\x3b\x69\x66\x28\x68\x26\x26\
+\x68\x5b\x33\x5d\x29\x7b\x76\x61\x72\x20\x69\x3d\x65\x2e\x67\x65\
+\x74\x45\x6c\x65\x6d\x65\x6e\x74\x42\x79\x49\x64\x28\x68\x5b\x33\
+\x5d\x29\x3b\x69\x66\x28\x21\x69\x7c\x7c\x21\x69\x2e\x70\x61\x72\
+\x65\x6e\x74\x4e\x6f\x64\x65\x29\x72\x65\x74\x75\x72\x6e\x20\x73\
+\x28\x5b\x5d\x2c\x66\x29\x3b\x69\x66\x28\x69\x2e\x69\x64\x3d\x3d\
+\x3d\x68\x5b\x33\x5d\x29\x72\x65\x74\x75\x72\x6e\x20\x73\x28\x5b\
+\x69\x5d\x2c\x66\x29\x7d\x74\x72\x79\x7b\x72\x65\x74\x75\x72\x6e\
+\x20\x73\x28\x65\x2e\x71\x75\x65\x72\x79\x53\x65\x6c\x65\x63\x74\
+\x6f\x72\x41\x6c\x6c\x28\x62\x29\x2c\x66\x29\x7d\x63\x61\x74\x63\
+\x68\x28\x6a\x29\x7b\x7d\x7d\x65\x6c\x73\x65\x20\x69\x66\x28\x65\
+\x2e\x6e\x6f\x64\x65\x54\x79\x70\x65\x3d\x3d\x3d\x31\x26\x26\x65\
+\x2e\x6e\x6f\x64\x65\x4e\x61\x6d\x65\x2e\x74\x6f\x4c\x6f\x77\x65\
+\x72\x43\x61\x73\x65\x28\x29\x21\x3d\x3d\x22\x6f\x62\x6a\x65\x63\
+\x74\x22\x29\x7b\x76\x61\x72\x20\x6b\x3d\x65\x2c\x6c\x3d\x65\x2e\
+\x67\x65\x74\x41\x74\x74\x72\x69\x62\x75\x74\x65\x28\x22\x69\x64\
+\x22\x29\x2c\x6e\x3d\x6c\x7c\x7c\x64\x2c\x70\x3d\x65\x2e\x70\x61\
+\x72\x65\x6e\x74\x4e\x6f\x64\x65\x2c\x71\x3d\x2f\x5e\x5c\x73\x2a\
+\x5b\x2b\x7e\x5d\x2f\x2e\x74\x65\x73\x74\x28\x62\x29\x3b\x6c\x3f\
+\x6e\x3d\x6e\x2e\x72\x65\x70\x6c\x61\x63\x65\x28\x2f\x27\x2f\x67\
+\x2c\x22\x5c\x5c\x24\x26\x22\x29\x3a\x65\x2e\x73\x65\x74\x41\x74\
+\x74\x72\x69\x62\x75\x74\x65\x28\x22\x69\x64\x22\x2c\x6e\x29\x2c\
+\x71\x26\x26\x70\x26\x26\x28\x65\x3d\x65\x2e\x70\x61\x72\x65\x6e\
+\x74\x4e\x6f\x64\x65\x29\x3b\x74\x72\x79\x7b\x69\x66\x28\x21\x71\
+\x7c\x7c\x70\x29\x72\x65\x74\x75\x72\x6e\x20\x73\x28\x65\x2e\x71\
+\x75\x65\x72\x79\x53\x65\x6c\x65\x63\x74\x6f\x72\x41\x6c\x6c\x28\
+\x22\x5b\x69\x64\x3d\x27\x22\x2b\x6e\x2b\x22\x27\x5d\x20\x22\x2b\
+\x62\x29\x2c\x66\x29\x7d\x63\x61\x74\x63\x68\x28\x72\x29\x7b\x7d\
+\x66\x69\x6e\x61\x6c\x6c\x79\x7b\x6c\x7c\x7c\x6b\x2e\x72\x65\x6d\
+\x6f\x76\x65\x41\x74\x74\x72\x69\x62\x75\x74\x65\x28\x22\x69\x64\
+\x22\x29\x7d\x7d\x7d\x72\x65\x74\x75\x72\x6e\x20\x61\x28\x62\x2c\
+\x65\x2c\x66\x2c\x67\x29\x7d\x3b\x66\x6f\x72\x28\x76\x61\x72\x20\
+\x65\x20\x69\x6e\x20\x61\x29\x6d\x5b\x65\x5d\x3d\x61\x5b\x65\x5d\
+\x3b\x62\x3d\x6e\x75\x6c\x6c\x7d\x7d\x28\x29\x2c\x66\x75\x6e\x63\
+\x74\x69\x6f\x6e\x28\x29\x7b\x76\x61\x72\x20\x61\x3d\x63\x2e\x64\
+\x6f\x63\x75\x6d\x65\x6e\x74\x45\x6c\x65\x6d\x65\x6e\x74\x2c\x62\
+\x3d\x61\x2e\x6d\x61\x74\x63\x68\x65\x73\x53\x65\x6c\x65\x63\x74\
+\x6f\x72\x7c\x7c\x61\x2e\x6d\x6f\x7a\x4d\x61\x74\x63\x68\x65\x73\
+\x53\x65\x6c\x65\x63\x74\x6f\x72\x7c\x7c\x61\x2e\x77\x65\x62\x6b\
+\x69\x74\x4d\x61\x74\x63\x68\x65\x73\x53\x65\x6c\x65\x63\x74\x6f\
+\x72\x7c\x7c\x61\x2e\x6d\x73\x4d\x61\x74\x63\x68\x65\x73\x53\x65\
+\x6c\x65\x63\x74\x6f\x72\x3b\x69\x66\x28\x62\x29\x7b\x76\x61\x72\
+\x20\x64\x3d\x21\x62\x2e\x63\x61\x6c\x6c\x28\x63\x2e\x63\x72\x65\
+\x61\x74\x65\x45\x6c\x65\x6d\x65\x6e\x74\x28\x22\x64\x69\x76\x22\
+\x29\x2c\x22\x64\x69\x76\x22\x29\x2c\x65\x3d\x21\x31\x3b\x74\x72\
+\x79\x7b\x62\x2e\x63\x61\x6c\x6c\x28\x63\x2e\x64\x6f\x63\x75\x6d\
+\x65\x6e\x74\x45\x6c\x65\x6d\x65\x6e\x74\x2c\x22\x5b\x74\x65\x73\
+\x74\x21\x3d\x27\x27\x5d\x3a\x73\x69\x7a\x7a\x6c\x65\x22\x29\x7d\
+\x63\x61\x74\x63\x68\x28\x66\x29\x7b\x65\x3d\x21\x30\x7d\x6d\x2e\
+\x6d\x61\x74\x63\x68\x65\x73\x53\x65\x6c\x65\x63\x74\x6f\x72\x3d\
+\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x63\x29\x7b\x63\x3d\
+\x63\x2e\x72\x65\x70\x6c\x61\x63\x65\x28\x2f\x5c\x3d\x5c\x73\x2a\
+\x28\x5b\x5e\x27\x22\x5c\x5d\x5d\x2a\x29\x5c\x73\x2a\x5c\x5d\x2f\
+\x67\x2c\x22\x3d\x27\x24\x31\x27\x5d\x22\x29\x3b\x69\x66\x28\x21\
+\x6d\x2e\x69\x73\x58\x4d\x4c\x28\x61\x29\x29\x74\x72\x79\x7b\x69\
+\x66\x28\x65\x7c\x7c\x21\x6f\x2e\x6d\x61\x74\x63\x68\x2e\x50\x53\
+\x45\x55\x44\x4f\x2e\x74\x65\x73\x74\x28\x63\x29\x26\x26\x21\x2f\
+\x21\x3d\x2f\x2e\x74\x65\x73\x74\x28\x63\x29\x29\x7b\x76\x61\x72\
+\x20\x66\x3d\x62\x2e\x63\x61\x6c\x6c\x28\x61\x2c\x63\x29\x3b\x69\
+\x66\x28\x66\x7c\x7c\x21\x64\x7c\x7c\x61\x2e\x64\x6f\x63\x75\x6d\
+\x65\x6e\x74\x26\x26\x61\x2e\x64\x6f\x63\x75\x6d\x65\x6e\x74\x2e\
+\x6e\x6f\x64\x65\x54\x79\x70\x65\x21\x3d\x3d\x31\x31\x29\x72\x65\
+\x74\x75\x72\x6e\x20\x66\x7d\x7d\x63\x61\x74\x63\x68\x28\x67\x29\
+\x7b\x7d\x72\x65\x74\x75\x72\x6e\x20\x6d\x28\x63\x2c\x6e\x75\x6c\
+\x6c\x2c\x6e\x75\x6c\x6c\x2c\x5b\x61\x5d\x29\x2e\x6c\x65\x6e\x67\
+\x74\x68\x3e\x30\x7d\x7d\x7d\x28\x29\x2c\x66\x75\x6e\x63\x74\x69\
+\x6f\x6e\x28\x29\x7b\x76\x61\x72\x20\x61\x3d\x63\x2e\x63\x72\x65\
+\x61\x74\x65\x45\x6c\x65\x6d\x65\x6e\x74\x28\x22\x64\x69\x76\x22\
+\x29\x3b\x61\x2e\x69\x6e\x6e\x65\x72\x48\x54\x4d\x4c\x3d\x22\x3c\
+\x64\x69\x76\x20\x63\x6c\x61\x73\x73\x3d\x27\x74\x65\x73\x74\x20\
+\x65\x27\x3e\x3c\x2f\x64\x69\x76\x3e\x3c\x64\x69\x76\x20\x63\x6c\
+\x61\x73\x73\x3d\x27\x74\x65\x73\x74\x27\x3e\x3c\x2f\x64\x69\x76\
+\x3e\x22\x3b\x69\x66\x28\x21\x21\x61\x2e\x67\x65\x74\x45\x6c\x65\
+\x6d\x65\x6e\x74\x73\x42\x79\x43\x6c\x61\x73\x73\x4e\x61\x6d\x65\
+\x26\x26\x61\x2e\x67\x65\x74\x45\x6c\x65\x6d\x65\x6e\x74\x73\x42\
+\x79\x43\x6c\x61\x73\x73\x4e\x61\x6d\x65\x28\x22\x65\x22\x29\x2e\
+\x6c\x65\x6e\x67\x74\x68\x21\x3d\x3d\x30\x29\x7b\x61\x2e\x6c\x61\
+\x73\x74\x43\x68\x69\x6c\x64\x2e\x63\x6c\x61\x73\x73\x4e\x61\x6d\
+\x65\x3d\x22\x65\x22\x3b\x69\x66\x28\x61\x2e\x67\x65\x74\x45\x6c\
+\x65\x6d\x65\x6e\x74\x73\x42\x79\x43\x6c\x61\x73\x73\x4e\x61\x6d\
+\x65\x28\x22\x65\x22\x29\x2e\x6c\x65\x6e\x67\x74\x68\x3d\x3d\x3d\
+\x31\x29\x72\x65\x74\x75\x72\x6e\x3b\x6f\x2e\x6f\x72\x64\x65\x72\
+\x2e\x73\x70\x6c\x69\x63\x65\x28\x31\x2c\x30\x2c\x22\x43\x4c\x41\
+\x53\x53\x22\x29\x2c\x6f\x2e\x66\x69\x6e\x64\x2e\x43\x4c\x41\x53\
+\x53\x3d\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x2c\x63\
+\x29\x7b\x69\x66\x28\x74\x79\x70\x65\x6f\x66\x20\x62\x2e\x67\x65\
+\x74\x45\x6c\x65\x6d\x65\x6e\x74\x73\x42\x79\x43\x6c\x61\x73\x73\
+\x4e\x61\x6d\x65\x21\x3d\x22\x75\x6e\x64\x65\x66\x69\x6e\x65\x64\
+\x22\x26\x26\x21\x63\x29\x72\x65\x74\x75\x72\x6e\x20\x62\x2e\x67\
+\x65\x74\x45\x6c\x65\x6d\x65\x6e\x74\x73\x42\x79\x43\x6c\x61\x73\
+\x73\x4e\x61\x6d\x65\x28\x61\x5b\x31\x5d\x29\x7d\x2c\x61\x3d\x6e\
+\x75\x6c\x6c\x7d\x7d\x28\x29\x2c\x63\x2e\x64\x6f\x63\x75\x6d\x65\
+\x6e\x74\x45\x6c\x65\x6d\x65\x6e\x74\x2e\x63\x6f\x6e\x74\x61\x69\
+\x6e\x73\x3f\x6d\x2e\x63\x6f\x6e\x74\x61\x69\x6e\x73\x3d\x66\x75\
+\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x29\x7b\x72\x65\x74\x75\
+\x72\x6e\x20\x61\x21\x3d\x3d\x62\x26\x26\x28\x61\x2e\x63\x6f\x6e\
+\x74\x61\x69\x6e\x73\x3f\x61\x2e\x63\x6f\x6e\x74\x61\x69\x6e\x73\
+\x28\x62\x29\x3a\x21\x30\x29\x7d\x3a\x63\x2e\x64\x6f\x63\x75\x6d\
+\x65\x6e\x74\x45\x6c\x65\x6d\x65\x6e\x74\x2e\x63\x6f\x6d\x70\x61\
+\x72\x65\x44\x6f\x63\x75\x6d\x65\x6e\x74\x50\x6f\x73\x69\x74\x69\
+\x6f\x6e\x3f\x6d\x2e\x63\x6f\x6e\x74\x61\x69\x6e\x73\x3d\x66\x75\
+\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x29\x7b\x72\x65\x74\x75\
+\x72\x6e\x21\x21\x28\x61\x2e\x63\x6f\x6d\x70\x61\x72\x65\x44\x6f\
+\x63\x75\x6d\x65\x6e\x74\x50\x6f\x73\x69\x74\x69\x6f\x6e\x28\x62\
+\x29\x26\x31\x36\x29\x7d\x3a\x6d\x2e\x63\x6f\x6e\x74\x61\x69\x6e\
+\x73\x3d\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x29\x7b\x72\x65\x74\
+\x75\x72\x6e\x21\x31\x7d\x2c\x6d\x2e\x69\x73\x58\x4d\x4c\x3d\x66\
+\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x76\x61\x72\x20\x62\
+\x3d\x28\x61\x3f\x61\x2e\x6f\x77\x6e\x65\x72\x44\x6f\x63\x75\x6d\
+\x65\x6e\x74\x7c\x7c\x61\x3a\x30\x29\x2e\x64\x6f\x63\x75\x6d\x65\
+\x6e\x74\x45\x6c\x65\x6d\x65\x6e\x74\x3b\x72\x65\x74\x75\x72\x6e\
+\x20\x62\x3f\x62\x2e\x6e\x6f\x64\x65\x4e\x61\x6d\x65\x21\x3d\x3d\
+\x22\x48\x54\x4d\x4c\x22\x3a\x21\x31\x7d\x3b\x76\x61\x72\x20\x79\
+\x3d\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x2c\x63\x29\
+\x7b\x76\x61\x72\x20\x64\x2c\x65\x3d\x5b\x5d\x2c\x66\x3d\x22\x22\
+\x2c\x67\x3d\x62\x2e\x6e\x6f\x64\x65\x54\x79\x70\x65\x3f\x5b\x62\
+\x5d\x3a\x62\x3b\x77\x68\x69\x6c\x65\x28\x64\x3d\x6f\x2e\x6d\x61\
+\x74\x63\x68\x2e\x50\x53\x45\x55\x44\x4f\x2e\x65\x78\x65\x63\x28\
+\x61\x29\x29\x66\x2b\x3d\x64\x5b\x30\x5d\x2c\x61\x3d\x61\x2e\x72\
+\x65\x70\x6c\x61\x63\x65\x28\x6f\x2e\x6d\x61\x74\x63\x68\x2e\x50\
+\x53\x45\x55\x44\x4f\x2c\x22\x22\x29\x3b\x61\x3d\x6f\x2e\x72\x65\
+\x6c\x61\x74\x69\x76\x65\x5b\x61\x5d\x3f\x61\x2b\x22\x2a\x22\x3a\
+\x61\x3b\x66\x6f\x72\x28\x76\x61\x72\x20\x68\x3d\x30\x2c\x69\x3d\
+\x67\x2e\x6c\x65\x6e\x67\x74\x68\x3b\x68\x3c\x69\x3b\x68\x2b\x2b\
+\x29\x6d\x28\x61\x2c\x67\x5b\x68\x5d\x2c\x65\x2c\x63\x29\x3b\x72\
+\x65\x74\x75\x72\x6e\x20\x6d\x2e\x66\x69\x6c\x74\x65\x72\x28\x66\
+\x2c\x65\x29\x7d\x3b\x6d\x2e\x61\x74\x74\x72\x3d\x66\x2e\x61\x74\
+\x74\x72\x2c\x6d\x2e\x73\x65\x6c\x65\x63\x74\x6f\x72\x73\x2e\x61\
+\x74\x74\x72\x4d\x61\x70\x3d\x7b\x7d\x2c\x66\x2e\x66\x69\x6e\x64\
+\x3d\x6d\x2c\x66\x2e\x65\x78\x70\x72\x3d\x6d\x2e\x73\x65\x6c\x65\
+\x63\x74\x6f\x72\x73\x2c\x66\x2e\x65\x78\x70\x72\x5b\x22\x3a\x22\
+\x5d\x3d\x66\x2e\x65\x78\x70\x72\x2e\x66\x69\x6c\x74\x65\x72\x73\
+\x2c\x66\x2e\x75\x6e\x69\x71\x75\x65\x3d\x6d\x2e\x75\x6e\x69\x71\
+\x75\x65\x53\x6f\x72\x74\x2c\x66\x2e\x74\x65\x78\x74\x3d\x6d\x2e\
+\x67\x65\x74\x54\x65\x78\x74\x2c\x66\x2e\x69\x73\x58\x4d\x4c\x44\
+\x6f\x63\x3d\x6d\x2e\x69\x73\x58\x4d\x4c\x2c\x66\x2e\x63\x6f\x6e\
+\x74\x61\x69\x6e\x73\x3d\x6d\x2e\x63\x6f\x6e\x74\x61\x69\x6e\x73\
+\x7d\x28\x29\x3b\x76\x61\x72\x20\x4c\x3d\x2f\x55\x6e\x74\x69\x6c\
+\x24\x2f\x2c\x4d\x3d\x2f\x5e\x28\x3f\x3a\x70\x61\x72\x65\x6e\x74\
+\x73\x7c\x70\x72\x65\x76\x55\x6e\x74\x69\x6c\x7c\x70\x72\x65\x76\
+\x41\x6c\x6c\x29\x2f\x2c\x4e\x3d\x2f\x2c\x2f\x2c\x4f\x3d\x2f\x5e\
+\x2e\x5b\x5e\x3a\x23\x5c\x5b\x5c\x2e\x2c\x5d\x2a\x24\x2f\x2c\x50\
+\x3d\x41\x72\x72\x61\x79\x2e\x70\x72\x6f\x74\x6f\x74\x79\x70\x65\
+\x2e\x73\x6c\x69\x63\x65\x2c\x51\x3d\x66\x2e\x65\x78\x70\x72\x2e\
+\x6d\x61\x74\x63\x68\x2e\x50\x4f\x53\x2c\x52\x3d\x7b\x63\x68\x69\
+\x6c\x64\x72\x65\x6e\x3a\x21\x30\x2c\x63\x6f\x6e\x74\x65\x6e\x74\
+\x73\x3a\x21\x30\x2c\x6e\x65\x78\x74\x3a\x21\x30\x2c\x70\x72\x65\
+\x76\x3a\x21\x30\x7d\x3b\x66\x2e\x66\x6e\x2e\x65\x78\x74\x65\x6e\
+\x64\x28\x7b\x66\x69\x6e\x64\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\
+\x28\x61\x29\x7b\x76\x61\x72\x20\x62\x3d\x74\x68\x69\x73\x2c\x63\
+\x2c\x64\x3b\x69\x66\x28\x74\x79\x70\x65\x6f\x66\x20\x61\x21\x3d\
+\x22\x73\x74\x72\x69\x6e\x67\x22\x29\x72\x65\x74\x75\x72\x6e\x20\
+\x66\x28\x61\x29\x2e\x66\x69\x6c\x74\x65\x72\x28\x66\x75\x6e\x63\
+\x74\x69\x6f\x6e\x28\x29\x7b\x66\x6f\x72\x28\x63\x3d\x30\x2c\x64\
+\x3d\x62\x2e\x6c\x65\x6e\x67\x74\x68\x3b\x63\x3c\x64\x3b\x63\x2b\
+\x2b\x29\x69\x66\x28\x66\x2e\x63\x6f\x6e\x74\x61\x69\x6e\x73\x28\
+\x62\x5b\x63\x5d\x2c\x74\x68\x69\x73\x29\x29\x72\x65\x74\x75\x72\
+\x6e\x21\x30\x7d\x29\x3b\x76\x61\x72\x20\x65\x3d\x74\x68\x69\x73\
+\x2e\x70\x75\x73\x68\x53\x74\x61\x63\x6b\x28\x22\x22\x2c\x22\x66\
+\x69\x6e\x64\x22\x2c\x61\x29\x2c\x67\x2c\x68\x2c\x69\x3b\x66\x6f\
+\x72\x28\x63\x3d\x30\x2c\x64\x3d\x74\x68\x69\x73\x2e\x6c\x65\x6e\
+\x67\x74\x68\x3b\x63\x3c\x64\x3b\x63\x2b\x2b\x29\x7b\x67\x3d\x65\
+\x2e\x6c\x65\x6e\x67\x74\x68\x2c\x66\x2e\x66\x69\x6e\x64\x28\x61\
+\x2c\x74\x68\x69\x73\x5b\x63\x5d\x2c\x65\x29\x3b\x69\x66\x28\x63\
+\x3e\x30\x29\x66\x6f\x72\x28\x68\x3d\x67\x3b\x68\x3c\x65\x2e\x6c\
+\x65\x6e\x67\x74\x68\x3b\x68\x2b\x2b\x29\x66\x6f\x72\x28\x69\x3d\
+\x30\x3b\x69\x3c\x67\x3b\x69\x2b\x2b\x29\x69\x66\x28\x65\x5b\x69\
+\x5d\x3d\x3d\x3d\x65\x5b\x68\x5d\x29\x7b\x65\x2e\x73\x70\x6c\x69\
+\x63\x65\x28\x68\x2d\x2d\x2c\x31\x29\x3b\x62\x72\x65\x61\x6b\x7d\
+\x7d\x72\x65\x74\x75\x72\x6e\x20\x65\x7d\x2c\x68\x61\x73\x3a\x66\
+\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x76\x61\x72\x20\x62\
+\x3d\x66\x28\x61\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x74\x68\x69\
+\x73\x2e\x66\x69\x6c\x74\x65\x72\x28\x66\x75\x6e\x63\x74\x69\x6f\
+\x6e\x28\x29\x7b\x66\x6f\x72\x28\x76\x61\x72\x20\x61\x3d\x30\x2c\
+\x63\x3d\x62\x2e\x6c\x65\x6e\x67\x74\x68\x3b\x61\x3c\x63\x3b\x61\
+\x2b\x2b\x29\x69\x66\x28\x66\x2e\x63\x6f\x6e\x74\x61\x69\x6e\x73\
+\x28\x74\x68\x69\x73\x2c\x62\x5b\x61\x5d\x29\x29\x72\x65\x74\x75\
+\x72\x6e\x21\x30\x7d\x29\x7d\x2c\x6e\x6f\x74\x3a\x66\x75\x6e\x63\
+\x74\x69\x6f\x6e\x28\x61\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x74\
+\x68\x69\x73\x2e\x70\x75\x73\x68\x53\x74\x61\x63\x6b\x28\x54\x28\
+\x74\x68\x69\x73\x2c\x61\x2c\x21\x31\x29\x2c\x22\x6e\x6f\x74\x22\
+\x2c\x61\x29\x7d\x2c\x66\x69\x6c\x74\x65\x72\x3a\x66\x75\x6e\x63\
+\x74\x69\x6f\x6e\x28\x61\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x74\
+\x68\x69\x73\x2e\x70\x75\x73\x68\x53\x74\x61\x63\x6b\x28\x54\x28\
+\x74\x68\x69\x73\x2c\x61\x2c\x21\x30\x29\x2c\x22\x66\x69\x6c\x74\
+\x65\x72\x22\x2c\x61\x29\x7d\x2c\x69\x73\x3a\x66\x75\x6e\x63\x74\
+\x69\x6f\x6e\x28\x61\x29\x7b\x72\x65\x74\x75\x72\x6e\x21\x21\x61\
+\x26\x26\x28\x74\x79\x70\x65\x6f\x66\x20\x61\x3d\x3d\x22\x73\x74\
+\x72\x69\x6e\x67\x22\x3f\x51\x2e\x74\x65\x73\x74\x28\x61\x29\x3f\
+\x66\x28\x61\x2c\x74\x68\x69\x73\x2e\x63\x6f\x6e\x74\x65\x78\x74\
+\x29\x2e\x69\x6e\x64\x65\x78\x28\x74\x68\x69\x73\x5b\x30\x5d\x29\
+\x3e\x3d\x30\x3a\x66\x2e\x66\x69\x6c\x74\x65\x72\x28\x61\x2c\x74\
+\x68\x69\x73\x29\x2e\x6c\x65\x6e\x67\x74\x68\x3e\x30\x3a\x74\x68\
+\x69\x73\x2e\x66\x69\x6c\x74\x65\x72\x28\x61\x29\x2e\x6c\x65\x6e\
+\x67\x74\x68\x3e\x30\x29\x7d\x2c\x63\x6c\x6f\x73\x65\x73\x74\x3a\
+\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x29\x7b\x76\x61\
+\x72\x20\x63\x3d\x5b\x5d\x2c\x64\x2c\x65\x2c\x67\x3d\x74\x68\x69\
+\x73\x5b\x30\x5d\x3b\x69\x66\x28\x66\x2e\x69\x73\x41\x72\x72\x61\
+\x79\x28\x61\x29\x29\x7b\x76\x61\x72\x20\x68\x3d\x31\x3b\x77\x68\
+\x69\x6c\x65\x28\x67\x26\x26\x67\x2e\x6f\x77\x6e\x65\x72\x44\x6f\
+\x63\x75\x6d\x65\x6e\x74\x26\x26\x67\x21\x3d\x3d\x62\x29\x7b\x66\
+\x6f\x72\x28\x64\x3d\x30\x3b\x64\x3c\x61\x2e\x6c\x65\x6e\x67\x74\
+\x68\x3b\x64\x2b\x2b\x29\x66\x28\x67\x29\x2e\x69\x73\x28\x61\x5b\
+\x64\x5d\x29\x26\x26\x63\x2e\x70\x75\x73\x68\x28\x7b\x73\x65\x6c\
+\x65\x63\x74\x6f\x72\x3a\x61\x5b\x64\x5d\x2c\x65\x6c\x65\x6d\x3a\
+\x67\x2c\x6c\x65\x76\x65\x6c\x3a\x68\x7d\x29\x3b\x67\x3d\x67\x2e\
+\x70\x61\x72\x65\x6e\x74\x4e\x6f\x64\x65\x2c\x68\x2b\x2b\x7d\x72\
+\x65\x74\x75\x72\x6e\x20\x63\x7d\x76\x61\x72\x20\x69\x3d\x51\x2e\
+\x74\x65\x73\x74\x28\x61\x29\x7c\x7c\x74\x79\x70\x65\x6f\x66\x20\
+\x61\x21\x3d\x22\x73\x74\x72\x69\x6e\x67\x22\x3f\x66\x28\x61\x2c\
+\x62\x7c\x7c\x74\x68\x69\x73\x2e\x63\x6f\x6e\x74\x65\x78\x74\x29\
+\x3a\x30\x3b\x66\x6f\x72\x28\x64\x3d\x30\x2c\x65\x3d\x74\x68\x69\
+\x73\x2e\x6c\x65\x6e\x67\x74\x68\x3b\x64\x3c\x65\x3b\x64\x2b\x2b\
+\x29\x7b\x67\x3d\x74\x68\x69\x73\x5b\x64\x5d\x3b\x77\x68\x69\x6c\
+\x65\x28\x67\x29\x7b\x69\x66\x28\x69\x3f\x69\x2e\x69\x6e\x64\x65\
+\x78\x28\x67\x29\x3e\x2d\x31\x3a\x66\x2e\x66\x69\x6e\x64\x2e\x6d\
+\x61\x74\x63\x68\x65\x73\x53\x65\x6c\x65\x63\x74\x6f\x72\x28\x67\
+\x2c\x61\x29\x29\x7b\x63\x2e\x70\x75\x73\x68\x28\x67\x29\x3b\x62\
+\x72\x65\x61\x6b\x7d\x67\x3d\x67\x2e\x70\x61\x72\x65\x6e\x74\x4e\
+\x6f\x64\x65\x3b\x69\x66\x28\x21\x67\x7c\x7c\x21\x67\x2e\x6f\x77\
+\x6e\x65\x72\x44\x6f\x63\x75\x6d\x65\x6e\x74\x7c\x7c\x67\x3d\x3d\
+\x3d\x62\x7c\x7c\x67\x2e\x6e\x6f\x64\x65\x54\x79\x70\x65\x3d\x3d\
+\x3d\x31\x31\x29\x62\x72\x65\x61\x6b\x7d\x7d\x63\x3d\x63\x2e\x6c\
+\x65\x6e\x67\x74\x68\x3e\x31\x3f\x66\x2e\x75\x6e\x69\x71\x75\x65\
+\x28\x63\x29\x3a\x63\x3b\x72\x65\x74\x75\x72\x6e\x20\x74\x68\x69\
+\x73\x2e\x70\x75\x73\x68\x53\x74\x61\x63\x6b\x28\x63\x2c\x22\x63\
+\x6c\x6f\x73\x65\x73\x74\x22\x2c\x61\x29\x7d\x2c\x69\x6e\x64\x65\
+\x78\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x69\x66\
+\x28\x21\x61\x29\x72\x65\x74\x75\x72\x6e\x20\x74\x68\x69\x73\x5b\
+\x30\x5d\x26\x26\x74\x68\x69\x73\x5b\x30\x5d\x2e\x70\x61\x72\x65\
+\x6e\x74\x4e\x6f\x64\x65\x3f\x74\x68\x69\x73\x2e\x70\x72\x65\x76\
+\x41\x6c\x6c\x28\x29\x2e\x6c\x65\x6e\x67\x74\x68\x3a\x2d\x31\x3b\
+\x69\x66\x28\x74\x79\x70\x65\x6f\x66\x20\x61\x3d\x3d\x22\x73\x74\
+\x72\x69\x6e\x67\x22\x29\x72\x65\x74\x75\x72\x6e\x20\x66\x2e\x69\
+\x6e\x41\x72\x72\x61\x79\x28\x74\x68\x69\x73\x5b\x30\x5d\x2c\x66\
+\x28\x61\x29\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x66\x2e\x69\x6e\
+\x41\x72\x72\x61\x79\x28\x61\x2e\x6a\x71\x75\x65\x72\x79\x3f\x61\
+\x5b\x30\x5d\x3a\x61\x2c\x74\x68\x69\x73\x29\x7d\x2c\x61\x64\x64\
+\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x29\x7b\x76\
+\x61\x72\x20\x63\x3d\x74\x79\x70\x65\x6f\x66\x20\x61\x3d\x3d\x22\
+\x73\x74\x72\x69\x6e\x67\x22\x3f\x66\x28\x61\x2c\x62\x29\x3a\x66\
+\x2e\x6d\x61\x6b\x65\x41\x72\x72\x61\x79\x28\x61\x26\x26\x61\x2e\
+\x6e\x6f\x64\x65\x54\x79\x70\x65\x3f\x5b\x61\x5d\x3a\x61\x29\x2c\
+\x64\x3d\x66\x2e\x6d\x65\x72\x67\x65\x28\x74\x68\x69\x73\x2e\x67\
+\x65\x74\x28\x29\x2c\x63\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x74\
+\x68\x69\x73\x2e\x70\x75\x73\x68\x53\x74\x61\x63\x6b\x28\x53\x28\
+\x63\x5b\x30\x5d\x29\x7c\x7c\x53\x28\x64\x5b\x30\x5d\x29\x3f\x64\
+\x3a\x66\x2e\x75\x6e\x69\x71\x75\x65\x28\x64\x29\x29\x7d\x2c\x61\
+\x6e\x64\x53\x65\x6c\x66\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\
+\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x74\x68\x69\x73\x2e\x61\x64\
+\x64\x28\x74\x68\x69\x73\x2e\x70\x72\x65\x76\x4f\x62\x6a\x65\x63\
+\x74\x29\x7d\x7d\x29\x2c\x66\x2e\x65\x61\x63\x68\x28\x7b\x70\x61\
+\x72\x65\x6e\x74\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\
+\x7b\x76\x61\x72\x20\x62\x3d\x61\x2e\x70\x61\x72\x65\x6e\x74\x4e\
+\x6f\x64\x65\x3b\x72\x65\x74\x75\x72\x6e\x20\x62\x26\x26\x62\x2e\
+\x6e\x6f\x64\x65\x54\x79\x70\x65\x21\x3d\x3d\x31\x31\x3f\x62\x3a\
+\x6e\x75\x6c\x6c\x7d\x2c\x70\x61\x72\x65\x6e\x74\x73\x3a\x66\x75\
+\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x72\x65\x74\x75\x72\x6e\
+\x20\x66\x2e\x64\x69\x72\x28\x61\x2c\x22\x70\x61\x72\x65\x6e\x74\
+\x4e\x6f\x64\x65\x22\x29\x7d\x2c\x70\x61\x72\x65\x6e\x74\x73\x55\
+\x6e\x74\x69\x6c\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\
+\x62\x2c\x63\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x66\x2e\x64\x69\
+\x72\x28\x61\x2c\x22\x70\x61\x72\x65\x6e\x74\x4e\x6f\x64\x65\x22\
+\x2c\x63\x29\x7d\x2c\x6e\x65\x78\x74\x3a\x66\x75\x6e\x63\x74\x69\
+\x6f\x6e\x28\x61\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x66\x2e\x6e\
+\x74\x68\x28\x61\x2c\x32\x2c\x22\x6e\x65\x78\x74\x53\x69\x62\x6c\
+\x69\x6e\x67\x22\x29\x7d\x2c\x70\x72\x65\x76\x3a\x66\x75\x6e\x63\
+\x74\x69\x6f\x6e\x28\x61\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x66\
+\x2e\x6e\x74\x68\x28\x61\x2c\x32\x2c\x22\x70\x72\x65\x76\x69\x6f\
+\x75\x73\x53\x69\x62\x6c\x69\x6e\x67\x22\x29\x7d\x2c\x6e\x65\x78\
+\x74\x41\x6c\x6c\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\
+\x7b\x72\x65\x74\x75\x72\x6e\x20\x66\x2e\x64\x69\x72\x28\x61\x2c\
+\x22\x6e\x65\x78\x74\x53\x69\x62\x6c\x69\x6e\x67\x22\x29\x7d\x2c\
+\x70\x72\x65\x76\x41\x6c\x6c\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\
+\x28\x61\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x66\x2e\x64\x69\x72\
+\x28\x61\x2c\x22\x70\x72\x65\x76\x69\x6f\x75\x73\x53\x69\x62\x6c\
+\x69\x6e\x67\x22\x29\x7d\x2c\x6e\x65\x78\x74\x55\x6e\x74\x69\x6c\
+\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x2c\x63\x29\
+\x7b\x72\x65\x74\x75\x72\x6e\x20\x66\x2e\x64\x69\x72\x28\x61\x2c\
+\x22\x6e\x65\x78\x74\x53\x69\x62\x6c\x69\x6e\x67\x22\x2c\x63\x29\
+\x7d\x2c\x70\x72\x65\x76\x55\x6e\x74\x69\x6c\x3a\x66\x75\x6e\x63\
+\x74\x69\x6f\x6e\x28\x61\x2c\x62\x2c\x63\x29\x7b\x72\x65\x74\x75\
+\x72\x6e\x20\x66\x2e\x64\x69\x72\x28\x61\x2c\x22\x70\x72\x65\x76\
+\x69\x6f\x75\x73\x53\x69\x62\x6c\x69\x6e\x67\x22\x2c\x63\x29\x7d\
+\x2c\x73\x69\x62\x6c\x69\x6e\x67\x73\x3a\x66\x75\x6e\x63\x74\x69\
+\x6f\x6e\x28\x61\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x66\x2e\x73\
+\x69\x62\x6c\x69\x6e\x67\x28\x61\x2e\x70\x61\x72\x65\x6e\x74\x4e\
+\x6f\x64\x65\x2e\x66\x69\x72\x73\x74\x43\x68\x69\x6c\x64\x2c\x61\
+\x29\x7d\x2c\x63\x68\x69\x6c\x64\x72\x65\x6e\x3a\x66\x75\x6e\x63\
+\x74\x69\x6f\x6e\x28\x61\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x66\
+\x2e\x73\x69\x62\x6c\x69\x6e\x67\x28\x61\x2e\x66\x69\x72\x73\x74\
+\x43\x68\x69\x6c\x64\x29\x7d\x2c\x63\x6f\x6e\x74\x65\x6e\x74\x73\
+\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x72\x65\x74\
+\x75\x72\x6e\x20\x66\x2e\x6e\x6f\x64\x65\x4e\x61\x6d\x65\x28\x61\
+\x2c\x22\x69\x66\x72\x61\x6d\x65\x22\x29\x3f\x61\x2e\x63\x6f\x6e\
+\x74\x65\x6e\x74\x44\x6f\x63\x75\x6d\x65\x6e\x74\x7c\x7c\x61\x2e\
+\x63\x6f\x6e\x74\x65\x6e\x74\x57\x69\x6e\x64\x6f\x77\x2e\x64\x6f\
+\x63\x75\x6d\x65\x6e\x74\x3a\x66\x2e\x6d\x61\x6b\x65\x41\x72\x72\
+\x61\x79\x28\x61\x2e\x63\x68\x69\x6c\x64\x4e\x6f\x64\x65\x73\x29\
+\x7d\x7d\x2c\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x29\
+\x7b\x66\x2e\x66\x6e\x5b\x61\x5d\x3d\x66\x75\x6e\x63\x74\x69\x6f\
+\x6e\x28\x63\x2c\x64\x29\x7b\x76\x61\x72\x20\x65\x3d\x66\x2e\x6d\
+\x61\x70\x28\x74\x68\x69\x73\x2c\x62\x2c\x63\x29\x3b\x4c\x2e\x74\
+\x65\x73\x74\x28\x61\x29\x7c\x7c\x28\x64\x3d\x63\x29\x2c\x64\x26\
+\x26\x74\x79\x70\x65\x6f\x66\x20\x64\x3d\x3d\x22\x73\x74\x72\x69\
+\x6e\x67\x22\x26\x26\x28\x65\x3d\x66\x2e\x66\x69\x6c\x74\x65\x72\
+\x28\x64\x2c\x65\x29\x29\x2c\x65\x3d\x74\x68\x69\x73\x2e\x6c\x65\
+\x6e\x67\x74\x68\x3e\x31\x26\x26\x21\x52\x5b\x61\x5d\x3f\x66\x2e\
+\x75\x6e\x69\x71\x75\x65\x28\x65\x29\x3a\x65\x2c\x28\x74\x68\x69\
+\x73\x2e\x6c\x65\x6e\x67\x74\x68\x3e\x31\x7c\x7c\x4e\x2e\x74\x65\
+\x73\x74\x28\x64\x29\x29\x26\x26\x4d\x2e\x74\x65\x73\x74\x28\x61\
+\x29\x26\x26\x28\x65\x3d\x65\x2e\x72\x65\x76\x65\x72\x73\x65\x28\
+\x29\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x74\x68\x69\x73\x2e\x70\
+\x75\x73\x68\x53\x74\x61\x63\x6b\x28\x65\x2c\x61\x2c\x50\x2e\x63\
+\x61\x6c\x6c\x28\x61\x72\x67\x75\x6d\x65\x6e\x74\x73\x29\x2e\x6a\
+\x6f\x69\x6e\x28\x22\x2c\x22\x29\x29\x7d\x7d\x29\x2c\x66\x2e\x65\
+\x78\x74\x65\x6e\x64\x28\x7b\x66\x69\x6c\x74\x65\x72\x3a\x66\x75\
+\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x2c\x63\x29\x7b\x63\x26\
+\x26\x28\x61\x3d\x22\x3a\x6e\x6f\x74\x28\x22\x2b\x61\x2b\x22\x29\
+\x22\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x62\x2e\x6c\x65\x6e\x67\
+\x74\x68\x3d\x3d\x3d\x31\x3f\x66\x2e\x66\x69\x6e\x64\x2e\x6d\x61\
+\x74\x63\x68\x65\x73\x53\x65\x6c\x65\x63\x74\x6f\x72\x28\x62\x5b\
+\x30\x5d\x2c\x61\x29\x3f\x5b\x62\x5b\x30\x5d\x5d\x3a\x5b\x5d\x3a\
+\x66\x2e\x66\x69\x6e\x64\x2e\x6d\x61\x74\x63\x68\x65\x73\x28\x61\
+\x2c\x62\x29\x7d\x2c\x64\x69\x72\x3a\x66\x75\x6e\x63\x74\x69\x6f\
+\x6e\x28\x61\x2c\x63\x2c\x64\x29\x7b\x76\x61\x72\x20\x65\x3d\x5b\
+\x5d\x2c\x67\x3d\x61\x5b\x63\x5d\x3b\x77\x68\x69\x6c\x65\x28\x67\
+\x26\x26\x67\x2e\x6e\x6f\x64\x65\x54\x79\x70\x65\x21\x3d\x3d\x39\
+\x26\x26\x28\x64\x3d\x3d\x3d\x62\x7c\x7c\x67\x2e\x6e\x6f\x64\x65\
+\x54\x79\x70\x65\x21\x3d\x3d\x31\x7c\x7c\x21\x66\x28\x67\x29\x2e\
+\x69\x73\x28\x64\x29\x29\x29\x67\x2e\x6e\x6f\x64\x65\x54\x79\x70\
+\x65\x3d\x3d\x3d\x31\x26\x26\x65\x2e\x70\x75\x73\x68\x28\x67\x29\
+\x2c\x67\x3d\x67\x5b\x63\x5d\x3b\x72\x65\x74\x75\x72\x6e\x20\x65\
+\x7d\x2c\x6e\x74\x68\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\
+\x2c\x62\x2c\x63\x2c\x64\x29\x7b\x62\x3d\x62\x7c\x7c\x31\x3b\x76\
+\x61\x72\x20\x65\x3d\x30\x3b\x66\x6f\x72\x28\x3b\x61\x3b\x61\x3d\
+\x61\x5b\x63\x5d\x29\x69\x66\x28\x61\x2e\x6e\x6f\x64\x65\x54\x79\
+\x70\x65\x3d\x3d\x3d\x31\x26\x26\x2b\x2b\x65\x3d\x3d\x3d\x62\x29\
+\x62\x72\x65\x61\x6b\x3b\x72\x65\x74\x75\x72\x6e\x20\x61\x7d\x2c\
+\x73\x69\x62\x6c\x69\x6e\x67\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\
+\x28\x61\x2c\x62\x29\x7b\x76\x61\x72\x20\x63\x3d\x5b\x5d\x3b\x66\
+\x6f\x72\x28\x3b\x61\x3b\x61\x3d\x61\x2e\x6e\x65\x78\x74\x53\x69\
+\x62\x6c\x69\x6e\x67\x29\x61\x2e\x6e\x6f\x64\x65\x54\x79\x70\x65\
+\x3d\x3d\x3d\x31\x26\x26\x61\x21\x3d\x3d\x62\x26\x26\x63\x2e\x70\
+\x75\x73\x68\x28\x61\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x63\x7d\
+\x7d\x29\x3b\x76\x61\x72\x20\x56\x3d\x22\x61\x62\x62\x72\x7c\x61\
+\x72\x74\x69\x63\x6c\x65\x7c\x61\x73\x69\x64\x65\x7c\x61\x75\x64\
+\x69\x6f\x7c\x63\x61\x6e\x76\x61\x73\x7c\x64\x61\x74\x61\x6c\x69\
+\x73\x74\x7c\x64\x65\x74\x61\x69\x6c\x73\x7c\x66\x69\x67\x63\x61\
+\x70\x74\x69\x6f\x6e\x7c\x66\x69\x67\x75\x72\x65\x7c\x66\x6f\x6f\
+\x74\x65\x72\x7c\x68\x65\x61\x64\x65\x72\x7c\x68\x67\x72\x6f\x75\
+\x70\x7c\x6d\x61\x72\x6b\x7c\x6d\x65\x74\x65\x72\x7c\x6e\x61\x76\
+\x7c\x6f\x75\x74\x70\x75\x74\x7c\x70\x72\x6f\x67\x72\x65\x73\x73\
+\x7c\x73\x65\x63\x74\x69\x6f\x6e\x7c\x73\x75\x6d\x6d\x61\x72\x79\
+\x7c\x74\x69\x6d\x65\x7c\x76\x69\x64\x65\x6f\x22\x2c\x57\x3d\x2f\
+\x20\x6a\x51\x75\x65\x72\x79\x5c\x64\x2b\x3d\x22\x28\x3f\x3a\x5c\
+\x64\x2b\x7c\x6e\x75\x6c\x6c\x29\x22\x2f\x67\x2c\x58\x3d\x2f\x5e\
+\x5c\x73\x2b\x2f\x2c\x59\x3d\x2f\x3c\x28\x3f\x21\x61\x72\x65\x61\
+\x7c\x62\x72\x7c\x63\x6f\x6c\x7c\x65\x6d\x62\x65\x64\x7c\x68\x72\
+\x7c\x69\x6d\x67\x7c\x69\x6e\x70\x75\x74\x7c\x6c\x69\x6e\x6b\x7c\
+\x6d\x65\x74\x61\x7c\x70\x61\x72\x61\x6d\x29\x28\x28\x5b\x5c\x77\
+\x3a\x5d\x2b\x29\x5b\x5e\x3e\x5d\x2a\x29\x5c\x2f\x3e\x2f\x69\x67\
+\x2c\x5a\x3d\x2f\x3c\x28\x5b\x5c\x77\x3a\x5d\x2b\x29\x2f\x2c\x24\
+\x3d\x2f\x3c\x74\x62\x6f\x64\x79\x2f\x69\x2c\x5f\x3d\x2f\x3c\x7c\
+\x26\x23\x3f\x5c\x77\x2b\x3b\x2f\x2c\x62\x61\x3d\x2f\x3c\x28\x3f\
+\x3a\x73\x63\x72\x69\x70\x74\x7c\x73\x74\x79\x6c\x65\x29\x2f\x69\
+\x2c\x62\x62\x3d\x2f\x3c\x28\x3f\x3a\x73\x63\x72\x69\x70\x74\x7c\
+\x6f\x62\x6a\x65\x63\x74\x7c\x65\x6d\x62\x65\x64\x7c\x6f\x70\x74\
+\x69\x6f\x6e\x7c\x73\x74\x79\x6c\x65\x29\x2f\x69\x2c\x62\x63\x3d\
+\x6e\x65\x77\x20\x52\x65\x67\x45\x78\x70\x28\x22\x3c\x28\x3f\x3a\
+\x22\x2b\x56\x2b\x22\x29\x22\x2c\x22\x69\x22\x29\x2c\x62\x64\x3d\
+\x2f\x63\x68\x65\x63\x6b\x65\x64\x5c\x73\x2a\x28\x3f\x3a\x5b\x5e\
+\x3d\x5d\x7c\x3d\x5c\x73\x2a\x2e\x63\x68\x65\x63\x6b\x65\x64\x2e\
+\x29\x2f\x69\x2c\x62\x65\x3d\x2f\x5c\x2f\x28\x6a\x61\x76\x61\x7c\
+\x65\x63\x6d\x61\x29\x73\x63\x72\x69\x70\x74\x2f\x69\x2c\x62\x66\
+\x3d\x2f\x5e\x5c\x73\x2a\x3c\x21\x28\x3f\x3a\x5c\x5b\x43\x44\x41\
+\x54\x41\x5c\x5b\x7c\x5c\x2d\x5c\x2d\x29\x2f\x2c\x62\x67\x3d\x7b\
+\x6f\x70\x74\x69\x6f\x6e\x3a\x5b\x31\x2c\x22\x3c\x73\x65\x6c\x65\
+\x63\x74\x20\x6d\x75\x6c\x74\x69\x70\x6c\x65\x3d\x27\x6d\x75\x6c\
+\x74\x69\x70\x6c\x65\x27\x3e\x22\x2c\x22\x3c\x2f\x73\x65\x6c\x65\
+\x63\x74\x3e\x22\x5d\x2c\x6c\x65\x67\x65\x6e\x64\x3a\x5b\x31\x2c\
+\x22\x3c\x66\x69\x65\x6c\x64\x73\x65\x74\x3e\x22\x2c\x22\x3c\x2f\
+\x66\x69\x65\x6c\x64\x73\x65\x74\x3e\x22\x5d\x2c\x74\x68\x65\x61\
+\x64\x3a\x5b\x31\x2c\x22\x3c\x74\x61\x62\x6c\x65\x3e\x22\x2c\x22\
+\x3c\x2f\x74\x61\x62\x6c\x65\x3e\x22\x5d\x2c\x74\x72\x3a\x5b\x32\
+\x2c\x22\x3c\x74\x61\x62\x6c\x65\x3e\x3c\x74\x62\x6f\x64\x79\x3e\
+\x22\x2c\x22\x3c\x2f\x74\x62\x6f\x64\x79\x3e\x3c\x2f\x74\x61\x62\
+\x6c\x65\x3e\x22\x5d\x2c\x74\x64\x3a\x5b\x33\x2c\x22\x3c\x74\x61\
+\x62\x6c\x65\x3e\x3c\x74\x62\x6f\x64\x79\x3e\x3c\x74\x72\x3e\x22\
+\x2c\x22\x3c\x2f\x74\x72\x3e\x3c\x2f\x74\x62\x6f\x64\x79\x3e\x3c\
+\x2f\x74\x61\x62\x6c\x65\x3e\x22\x5d\x2c\x63\x6f\x6c\x3a\x5b\x32\
+\x2c\x22\x3c\x74\x61\x62\x6c\x65\x3e\x3c\x74\x62\x6f\x64\x79\x3e\
+\x3c\x2f\x74\x62\x6f\x64\x79\x3e\x3c\x63\x6f\x6c\x67\x72\x6f\x75\
+\x70\x3e\x22\x2c\x22\x3c\x2f\x63\x6f\x6c\x67\x72\x6f\x75\x70\x3e\
+\x3c\x2f\x74\x61\x62\x6c\x65\x3e\x22\x5d\x2c\x61\x72\x65\x61\x3a\
+\x5b\x31\x2c\x22\x3c\x6d\x61\x70\x3e\x22\x2c\x22\x3c\x2f\x6d\x61\
+\x70\x3e\x22\x5d\x2c\x5f\x64\x65\x66\x61\x75\x6c\x74\x3a\x5b\x30\
+\x2c\x22\x22\x2c\x22\x22\x5d\x7d\x2c\x62\x68\x3d\x55\x28\x63\x29\
+\x3b\x62\x67\x2e\x6f\x70\x74\x67\x72\x6f\x75\x70\x3d\x62\x67\x2e\
+\x6f\x70\x74\x69\x6f\x6e\x2c\x62\x67\x2e\x74\x62\x6f\x64\x79\x3d\
+\x62\x67\x2e\x74\x66\x6f\x6f\x74\x3d\x62\x67\x2e\x63\x6f\x6c\x67\
+\x72\x6f\x75\x70\x3d\x62\x67\x2e\x63\x61\x70\x74\x69\x6f\x6e\x3d\
+\x62\x67\x2e\x74\x68\x65\x61\x64\x2c\x62\x67\x2e\x74\x68\x3d\x62\
+\x67\x2e\x74\x64\x2c\x66\x2e\x73\x75\x70\x70\x6f\x72\x74\x2e\x68\
+\x74\x6d\x6c\x53\x65\x72\x69\x61\x6c\x69\x7a\x65\x7c\x7c\x28\x62\
+\x67\x2e\x5f\x64\x65\x66\x61\x75\x6c\x74\x3d\x5b\x31\x2c\x22\x64\
+\x69\x76\x3c\x64\x69\x76\x3e\x22\x2c\x22\x3c\x2f\x64\x69\x76\x3e\
+\x22\x5d\x29\x2c\x66\x2e\x66\x6e\x2e\x65\x78\x74\x65\x6e\x64\x28\
+\x7b\x74\x65\x78\x74\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\
+\x29\x7b\x69\x66\x28\x66\x2e\x69\x73\x46\x75\x6e\x63\x74\x69\x6f\
+\x6e\x28\x61\x29\x29\x72\x65\x74\x75\x72\x6e\x20\x74\x68\x69\x73\
+\x2e\x65\x61\x63\x68\x28\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x62\
+\x29\x7b\x76\x61\x72\x20\x63\x3d\x66\x28\x74\x68\x69\x73\x29\x3b\
+\x63\x2e\x74\x65\x78\x74\x28\x61\x2e\x63\x61\x6c\x6c\x28\x74\x68\
+\x69\x73\x2c\x62\x2c\x63\x2e\x74\x65\x78\x74\x28\x29\x29\x29\x7d\
+\x29\x3b\x69\x66\x28\x74\x79\x70\x65\x6f\x66\x20\x61\x21\x3d\x22\
+\x6f\x62\x6a\x65\x63\x74\x22\x26\x26\x61\x21\x3d\x3d\x62\x29\x72\
+\x65\x74\x75\x72\x6e\x20\x74\x68\x69\x73\x2e\x65\x6d\x70\x74\x79\
+\x28\x29\x2e\x61\x70\x70\x65\x6e\x64\x28\x28\x74\x68\x69\x73\x5b\
+\x30\x5d\x26\x26\x74\x68\x69\x73\x5b\x30\x5d\x2e\x6f\x77\x6e\x65\
+\x72\x44\x6f\x63\x75\x6d\x65\x6e\x74\x7c\x7c\x63\x29\x2e\x63\x72\
+\x65\x61\x74\x65\x54\x65\x78\x74\x4e\x6f\x64\x65\x28\x61\x29\x29\
+\x3b\x72\x65\x74\x75\x72\x6e\x20\x66\x2e\x74\x65\x78\x74\x28\x74\
+\x68\x69\x73\x29\x7d\x2c\x77\x72\x61\x70\x41\x6c\x6c\x3a\x66\x75\
+\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x69\x66\x28\x66\x2e\x69\
+\x73\x46\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x29\x72\x65\x74\
+\x75\x72\x6e\x20\x74\x68\x69\x73\x2e\x65\x61\x63\x68\x28\x66\x75\
+\x6e\x63\x74\x69\x6f\x6e\x28\x62\x29\x7b\x66\x28\x74\x68\x69\x73\
+\x29\x2e\x77\x72\x61\x70\x41\x6c\x6c\x28\x61\x2e\x63\x61\x6c\x6c\
+\x28\x74\x68\x69\x73\x2c\x62\x29\x29\x7d\x29\x3b\x69\x66\x28\x74\
+\x68\x69\x73\x5b\x30\x5d\x29\x7b\x76\x61\x72\x20\x62\x3d\x66\x28\
+\x61\x2c\x74\x68\x69\x73\x5b\x30\x5d\x2e\x6f\x77\x6e\x65\x72\x44\
+\x6f\x63\x75\x6d\x65\x6e\x74\x29\x2e\x65\x71\x28\x30\x29\x2e\x63\
+\x6c\x6f\x6e\x65\x28\x21\x30\x29\x3b\x74\x68\x69\x73\x5b\x30\x5d\
+\x2e\x70\x61\x72\x65\x6e\x74\x4e\x6f\x64\x65\x26\x26\x62\x2e\x69\
+\x6e\x73\x65\x72\x74\x42\x65\x66\x6f\x72\x65\x28\x74\x68\x69\x73\
+\x5b\x30\x5d\x29\x2c\x62\x2e\x6d\x61\x70\x28\x66\x75\x6e\x63\x74\
+\x69\x6f\x6e\x28\x29\x7b\x76\x61\x72\x20\x61\x3d\x74\x68\x69\x73\
+\x3b\x77\x68\x69\x6c\x65\x28\x61\x2e\x66\x69\x72\x73\x74\x43\x68\
+\x69\x6c\x64\x26\x26\x61\x2e\x66\x69\x72\x73\x74\x43\x68\x69\x6c\
+\x64\x2e\x6e\x6f\x64\x65\x54\x79\x70\x65\x3d\x3d\x3d\x31\x29\x61\
+\x3d\x61\x2e\x66\x69\x72\x73\x74\x43\x68\x69\x6c\x64\x3b\x72\x65\
+\x74\x75\x72\x6e\x20\x61\x7d\x29\x2e\x61\x70\x70\x65\x6e\x64\x28\
+\x74\x68\x69\x73\x29\x7d\x72\x65\x74\x75\x72\x6e\x20\x74\x68\x69\
+\x73\x7d\x2c\x77\x72\x61\x70\x49\x6e\x6e\x65\x72\x3a\x66\x75\x6e\
+\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x69\x66\x28\x66\x2e\x69\x73\
+\x46\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x29\x72\x65\x74\x75\
+\x72\x6e\x20\x74\x68\x69\x73\x2e\x65\x61\x63\x68\x28\x66\x75\x6e\
+\x63\x74\x69\x6f\x6e\x28\x62\x29\x7b\x66\x28\x74\x68\x69\x73\x29\
+\x2e\x77\x72\x61\x70\x49\x6e\x6e\x65\x72\x28\x61\x2e\x63\x61\x6c\
+\x6c\x28\x74\x68\x69\x73\x2c\x62\x29\x29\x7d\x29\x3b\x72\x65\x74\
+\x75\x72\x6e\x20\x74\x68\x69\x73\x2e\x65\x61\x63\x68\x28\x66\x75\
+\x6e\x63\x74\x69\x6f\x6e\x28\x29\x7b\x76\x61\x72\x20\x62\x3d\x66\
+\x28\x74\x68\x69\x73\x29\x2c\x63\x3d\x62\x2e\x63\x6f\x6e\x74\x65\
+\x6e\x74\x73\x28\x29\x3b\x63\x2e\x6c\x65\x6e\x67\x74\x68\x3f\x63\
+\x2e\x77\x72\x61\x70\x41\x6c\x6c\x28\x61\x29\x3a\x62\x2e\x61\x70\
+\x70\x65\x6e\x64\x28\x61\x29\x7d\x29\x7d\x2c\x77\x72\x61\x70\x3a\
+\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x76\x61\x72\x20\
+\x62\x3d\x66\x2e\x69\x73\x46\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\
+\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x74\x68\x69\x73\x2e\x65\x61\
+\x63\x68\x28\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x63\x29\x7b\x66\
+\x28\x74\x68\x69\x73\x29\x2e\x77\x72\x61\x70\x41\x6c\x6c\x28\x62\
+\x3f\x61\x2e\x63\x61\x6c\x6c\x28\x74\x68\x69\x73\x2c\x63\x29\x3a\
+\x61\x29\x7d\x29\x7d\x2c\x75\x6e\x77\x72\x61\x70\x3a\x66\x75\x6e\
+\x63\x74\x69\x6f\x6e\x28\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x74\
+\x68\x69\x73\x2e\x70\x61\x72\x65\x6e\x74\x28\x29\x2e\x65\x61\x63\
+\x68\x28\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x29\x7b\x66\x2e\x6e\
+\x6f\x64\x65\x4e\x61\x6d\x65\x28\x74\x68\x69\x73\x2c\x22\x62\x6f\
+\x64\x79\x22\x29\x7c\x7c\x66\x28\x74\x68\x69\x73\x29\x2e\x72\x65\
+\x70\x6c\x61\x63\x65\x57\x69\x74\x68\x28\x74\x68\x69\x73\x2e\x63\
+\x68\x69\x6c\x64\x4e\x6f\x64\x65\x73\x29\x7d\x29\x2e\x65\x6e\x64\
+\x28\x29\x7d\x2c\x61\x70\x70\x65\x6e\x64\x3a\x66\x75\x6e\x63\x74\
+\x69\x6f\x6e\x28\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x74\x68\x69\
+\x73\x2e\x64\x6f\x6d\x4d\x61\x6e\x69\x70\x28\x61\x72\x67\x75\x6d\
+\x65\x6e\x74\x73\x2c\x21\x30\x2c\x66\x75\x6e\x63\x74\x69\x6f\x6e\
+\x28\x61\x29\x7b\x74\x68\x69\x73\x2e\x6e\x6f\x64\x65\x54\x79\x70\
+\x65\x3d\x3d\x3d\x31\x26\x26\x74\x68\x69\x73\x2e\x61\x70\x70\x65\
+\x6e\x64\x43\x68\x69\x6c\x64\x28\x61\x29\x7d\x29\x7d\x2c\x70\x72\
+\x65\x70\x65\x6e\x64\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x29\
+\x7b\x72\x65\x74\x75\x72\x6e\x20\x74\x68\x69\x73\x2e\x64\x6f\x6d\
+\x4d\x61\x6e\x69\x70\x28\x61\x72\x67\x75\x6d\x65\x6e\x74\x73\x2c\
+\x21\x30\x2c\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x74\
+\x68\x69\x73\x2e\x6e\x6f\x64\x65\x54\x79\x70\x65\x3d\x3d\x3d\x31\
+\x26\x26\x74\x68\x69\x73\x2e\x69\x6e\x73\x65\x72\x74\x42\x65\x66\
+\x6f\x72\x65\x28\x61\x2c\x74\x68\x69\x73\x2e\x66\x69\x72\x73\x74\
+\x43\x68\x69\x6c\x64\x29\x7d\x29\x7d\x2c\x62\x65\x66\x6f\x72\x65\
+\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x29\x7b\x69\x66\x28\x74\
+\x68\x69\x73\x5b\x30\x5d\x26\x26\x74\x68\x69\x73\x5b\x30\x5d\x2e\
+\x70\x61\x72\x65\x6e\x74\x4e\x6f\x64\x65\x29\x72\x65\x74\x75\x72\
+\x6e\x20\x74\x68\x69\x73\x2e\x64\x6f\x6d\x4d\x61\x6e\x69\x70\x28\
+\x61\x72\x67\x75\x6d\x65\x6e\x74\x73\x2c\x21\x31\x2c\x66\x75\x6e\
+\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x74\x68\x69\x73\x2e\x70\x61\
+\x72\x65\x6e\x74\x4e\x6f\x64\x65\x2e\x69\x6e\x73\x65\x72\x74\x42\
+\x65\x66\x6f\x72\x65\x28\x61\x2c\x74\x68\x69\x73\x29\x7d\x29\x3b\
+\x69\x66\x28\x61\x72\x67\x75\x6d\x65\x6e\x74\x73\x2e\x6c\x65\x6e\
+\x67\x74\x68\x29\x7b\x76\x61\x72\x20\x61\x3d\x66\x2e\x63\x6c\x65\
+\x61\x6e\x28\x61\x72\x67\x75\x6d\x65\x6e\x74\x73\x29\x3b\x61\x2e\
+\x70\x75\x73\x68\x2e\x61\x70\x70\x6c\x79\x28\x61\x2c\x74\x68\x69\
+\x73\x2e\x74\x6f\x41\x72\x72\x61\x79\x28\x29\x29\x3b\x72\x65\x74\
+\x75\x72\x6e\x20\x74\x68\x69\x73\x2e\x70\x75\x73\x68\x53\x74\x61\
+\x63\x6b\x28\x61\x2c\x22\x62\x65\x66\x6f\x72\x65\x22\x2c\x61\x72\
+\x67\x75\x6d\x65\x6e\x74\x73\x29\x7d\x7d\x2c\x61\x66\x74\x65\x72\
+\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x29\x7b\x69\x66\x28\x74\
+\x68\x69\x73\x5b\x30\x5d\x26\x26\x74\x68\x69\x73\x5b\x30\x5d\x2e\
+\x70\x61\x72\x65\x6e\x74\x4e\x6f\x64\x65\x29\x72\x65\x74\x75\x72\
+\x6e\x20\x74\x68\x69\x73\x2e\x64\x6f\x6d\x4d\x61\x6e\x69\x70\x28\
+\x61\x72\x67\x75\x6d\x65\x6e\x74\x73\x2c\x21\x31\x2c\x66\x75\x6e\
+\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x74\x68\x69\x73\x2e\x70\x61\
+\x72\x65\x6e\x74\x4e\x6f\x64\x65\x2e\x69\x6e\x73\x65\x72\x74\x42\
+\x65\x66\x6f\x72\x65\x28\x61\x2c\x74\x68\x69\x73\x2e\x6e\x65\x78\
+\x74\x53\x69\x62\x6c\x69\x6e\x67\x29\x7d\x29\x3b\x69\x66\x28\x61\
+\x72\x67\x75\x6d\x65\x6e\x74\x73\x2e\x6c\x65\x6e\x67\x74\x68\x29\
+\x7b\x76\x61\x72\x20\x61\x3d\x74\x68\x69\x73\x2e\x70\x75\x73\x68\
+\x53\x74\x61\x63\x6b\x28\x74\x68\x69\x73\x2c\x22\x61\x66\x74\x65\
+\x72\x22\x2c\x61\x72\x67\x75\x6d\x65\x6e\x74\x73\x29\x3b\x61\x2e\
+\x70\x75\x73\x68\x2e\x61\x70\x70\x6c\x79\x28\x61\x2c\x66\x2e\x63\
+\x6c\x65\x61\x6e\x28\x61\x72\x67\x75\x6d\x65\x6e\x74\x73\x29\x29\
+\x3b\x72\x65\x74\x75\x72\x6e\x20\x61\x7d\x7d\x2c\x72\x65\x6d\x6f\
+\x76\x65\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x29\
+\x7b\x66\x6f\x72\x28\x76\x61\x72\x20\x63\x3d\x30\x2c\x64\x3b\x28\
+\x64\x3d\x74\x68\x69\x73\x5b\x63\x5d\x29\x21\x3d\x6e\x75\x6c\x6c\
+\x3b\x63\x2b\x2b\x29\x69\x66\x28\x21\x61\x7c\x7c\x66\x2e\x66\x69\
+\x6c\x74\x65\x72\x28\x61\x2c\x5b\x64\x5d\x29\x2e\x6c\x65\x6e\x67\
+\x74\x68\x29\x21\x62\x26\x26\x64\x2e\x6e\x6f\x64\x65\x54\x79\x70\
+\x65\x3d\x3d\x3d\x31\x26\x26\x28\x66\x2e\x63\x6c\x65\x61\x6e\x44\
+\x61\x74\x61\x28\x64\x2e\x67\x65\x74\x45\x6c\x65\x6d\x65\x6e\x74\
+\x73\x42\x79\x54\x61\x67\x4e\x61\x6d\x65\x28\x22\x2a\x22\x29\x29\
+\x2c\x66\x2e\x63\x6c\x65\x61\x6e\x44\x61\x74\x61\x28\x5b\x64\x5d\
+\x29\x29\x2c\x64\x2e\x70\x61\x72\x65\x6e\x74\x4e\x6f\x64\x65\x26\
+\x26\x64\x2e\x70\x61\x72\x65\x6e\x74\x4e\x6f\x64\x65\x2e\x72\x65\
+\x6d\x6f\x76\x65\x43\x68\x69\x6c\x64\x28\x64\x29\x3b\x72\x65\x74\
+\x75\x72\x6e\x20\x74\x68\x69\x73\x7d\x2c\x65\x6d\x70\x74\x79\x3a\
+\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x29\x0a\x7b\x66\x6f\x72\x28\
+\x76\x61\x72\x20\x61\x3d\x30\x2c\x62\x3b\x28\x62\x3d\x74\x68\x69\
+\x73\x5b\x61\x5d\x29\x21\x3d\x6e\x75\x6c\x6c\x3b\x61\x2b\x2b\x29\
+\x7b\x62\x2e\x6e\x6f\x64\x65\x54\x79\x70\x65\x3d\x3d\x3d\x31\x26\
+\x26\x66\x2e\x63\x6c\x65\x61\x6e\x44\x61\x74\x61\x28\x62\x2e\x67\
+\x65\x74\x45\x6c\x65\x6d\x65\x6e\x74\x73\x42\x79\x54\x61\x67\x4e\
+\x61\x6d\x65\x28\x22\x2a\x22\x29\x29\x3b\x77\x68\x69\x6c\x65\x28\
+\x62\x2e\x66\x69\x72\x73\x74\x43\x68\x69\x6c\x64\x29\x62\x2e\x72\
+\x65\x6d\x6f\x76\x65\x43\x68\x69\x6c\x64\x28\x62\x2e\x66\x69\x72\
+\x73\x74\x43\x68\x69\x6c\x64\x29\x7d\x72\x65\x74\x75\x72\x6e\x20\
+\x74\x68\x69\x73\x7d\x2c\x63\x6c\x6f\x6e\x65\x3a\x66\x75\x6e\x63\
+\x74\x69\x6f\x6e\x28\x61\x2c\x62\x29\x7b\x61\x3d\x61\x3d\x3d\x6e\
+\x75\x6c\x6c\x3f\x21\x31\x3a\x61\x2c\x62\x3d\x62\x3d\x3d\x6e\x75\
+\x6c\x6c\x3f\x61\x3a\x62\x3b\x72\x65\x74\x75\x72\x6e\x20\x74\x68\
+\x69\x73\x2e\x6d\x61\x70\x28\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\
+\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x66\x2e\x63\x6c\x6f\x6e\x65\
+\x28\x74\x68\x69\x73\x2c\x61\x2c\x62\x29\x7d\x29\x7d\x2c\x68\x74\
+\x6d\x6c\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x69\
+\x66\x28\x61\x3d\x3d\x3d\x62\x29\x72\x65\x74\x75\x72\x6e\x20\x74\
+\x68\x69\x73\x5b\x30\x5d\x26\x26\x74\x68\x69\x73\x5b\x30\x5d\x2e\
+\x6e\x6f\x64\x65\x54\x79\x70\x65\x3d\x3d\x3d\x31\x3f\x74\x68\x69\
+\x73\x5b\x30\x5d\x2e\x69\x6e\x6e\x65\x72\x48\x54\x4d\x4c\x2e\x72\
+\x65\x70\x6c\x61\x63\x65\x28\x57\x2c\x22\x22\x29\x3a\x6e\x75\x6c\
+\x6c\x3b\x69\x66\x28\x74\x79\x70\x65\x6f\x66\x20\x61\x3d\x3d\x22\
+\x73\x74\x72\x69\x6e\x67\x22\x26\x26\x21\x62\x61\x2e\x74\x65\x73\
+\x74\x28\x61\x29\x26\x26\x28\x66\x2e\x73\x75\x70\x70\x6f\x72\x74\
+\x2e\x6c\x65\x61\x64\x69\x6e\x67\x57\x68\x69\x74\x65\x73\x70\x61\
+\x63\x65\x7c\x7c\x21\x58\x2e\x74\x65\x73\x74\x28\x61\x29\x29\x26\
+\x26\x21\x62\x67\x5b\x28\x5a\x2e\x65\x78\x65\x63\x28\x61\x29\x7c\
+\x7c\x5b\x22\x22\x2c\x22\x22\x5d\x29\x5b\x31\x5d\x2e\x74\x6f\x4c\
+\x6f\x77\x65\x72\x43\x61\x73\x65\x28\x29\x5d\x29\x7b\x61\x3d\x61\
+\x2e\x72\x65\x70\x6c\x61\x63\x65\x28\x59\x2c\x22\x3c\x24\x31\x3e\
+\x3c\x2f\x24\x32\x3e\x22\x29\x3b\x74\x72\x79\x7b\x66\x6f\x72\x28\
+\x76\x61\x72\x20\x63\x3d\x30\x2c\x64\x3d\x74\x68\x69\x73\x2e\x6c\
+\x65\x6e\x67\x74\x68\x3b\x63\x3c\x64\x3b\x63\x2b\x2b\x29\x74\x68\
+\x69\x73\x5b\x63\x5d\x2e\x6e\x6f\x64\x65\x54\x79\x70\x65\x3d\x3d\
+\x3d\x31\x26\x26\x28\x66\x2e\x63\x6c\x65\x61\x6e\x44\x61\x74\x61\
+\x28\x74\x68\x69\x73\x5b\x63\x5d\x2e\x67\x65\x74\x45\x6c\x65\x6d\
+\x65\x6e\x74\x73\x42\x79\x54\x61\x67\x4e\x61\x6d\x65\x28\x22\x2a\
+\x22\x29\x29\x2c\x74\x68\x69\x73\x5b\x63\x5d\x2e\x69\x6e\x6e\x65\
+\x72\x48\x54\x4d\x4c\x3d\x61\x29\x7d\x63\x61\x74\x63\x68\x28\x65\
+\x29\x7b\x74\x68\x69\x73\x2e\x65\x6d\x70\x74\x79\x28\x29\x2e\x61\
+\x70\x70\x65\x6e\x64\x28\x61\x29\x7d\x7d\x65\x6c\x73\x65\x20\x66\
+\x2e\x69\x73\x46\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x3f\x74\
+\x68\x69\x73\x2e\x65\x61\x63\x68\x28\x66\x75\x6e\x63\x74\x69\x6f\
+\x6e\x28\x62\x29\x7b\x76\x61\x72\x20\x63\x3d\x66\x28\x74\x68\x69\
+\x73\x29\x3b\x63\x2e\x68\x74\x6d\x6c\x28\x61\x2e\x63\x61\x6c\x6c\
+\x28\x74\x68\x69\x73\x2c\x62\x2c\x63\x2e\x68\x74\x6d\x6c\x28\x29\
+\x29\x29\x7d\x29\x3a\x74\x68\x69\x73\x2e\x65\x6d\x70\x74\x79\x28\
+\x29\x2e\x61\x70\x70\x65\x6e\x64\x28\x61\x29\x3b\x72\x65\x74\x75\
+\x72\x6e\x20\x74\x68\x69\x73\x7d\x2c\x72\x65\x70\x6c\x61\x63\x65\
+\x57\x69\x74\x68\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\
+\x7b\x69\x66\x28\x74\x68\x69\x73\x5b\x30\x5d\x26\x26\x74\x68\x69\
+\x73\x5b\x30\x5d\x2e\x70\x61\x72\x65\x6e\x74\x4e\x6f\x64\x65\x29\
+\x7b\x69\x66\x28\x66\x2e\x69\x73\x46\x75\x6e\x63\x74\x69\x6f\x6e\
+\x28\x61\x29\x29\x72\x65\x74\x75\x72\x6e\x20\x74\x68\x69\x73\x2e\
+\x65\x61\x63\x68\x28\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x62\x29\
+\x7b\x76\x61\x72\x20\x63\x3d\x66\x28\x74\x68\x69\x73\x29\x2c\x64\
+\x3d\x63\x2e\x68\x74\x6d\x6c\x28\x29\x3b\x63\x2e\x72\x65\x70\x6c\
+\x61\x63\x65\x57\x69\x74\x68\x28\x61\x2e\x63\x61\x6c\x6c\x28\x74\
+\x68\x69\x73\x2c\x62\x2c\x64\x29\x29\x7d\x29\x3b\x74\x79\x70\x65\
+\x6f\x66\x20\x61\x21\x3d\x22\x73\x74\x72\x69\x6e\x67\x22\x26\x26\
+\x28\x61\x3d\x66\x28\x61\x29\x2e\x64\x65\x74\x61\x63\x68\x28\x29\
+\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x74\x68\x69\x73\x2e\x65\x61\
+\x63\x68\x28\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x29\x7b\x76\x61\
+\x72\x20\x62\x3d\x74\x68\x69\x73\x2e\x6e\x65\x78\x74\x53\x69\x62\
+\x6c\x69\x6e\x67\x2c\x63\x3d\x74\x68\x69\x73\x2e\x70\x61\x72\x65\
+\x6e\x74\x4e\x6f\x64\x65\x3b\x66\x28\x74\x68\x69\x73\x29\x2e\x72\
+\x65\x6d\x6f\x76\x65\x28\x29\x2c\x62\x3f\x66\x28\x62\x29\x2e\x62\
+\x65\x66\x6f\x72\x65\x28\x61\x29\x3a\x66\x28\x63\x29\x2e\x61\x70\
+\x70\x65\x6e\x64\x28\x61\x29\x7d\x29\x7d\x72\x65\x74\x75\x72\x6e\
+\x20\x74\x68\x69\x73\x2e\x6c\x65\x6e\x67\x74\x68\x3f\x74\x68\x69\
+\x73\x2e\x70\x75\x73\x68\x53\x74\x61\x63\x6b\x28\x66\x28\x66\x2e\
+\x69\x73\x46\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x3f\x61\x28\
+\x29\x3a\x61\x29\x2c\x22\x72\x65\x70\x6c\x61\x63\x65\x57\x69\x74\
+\x68\x22\x2c\x61\x29\x3a\x74\x68\x69\x73\x7d\x2c\x64\x65\x74\x61\
+\x63\x68\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x72\
+\x65\x74\x75\x72\x6e\x20\x74\x68\x69\x73\x2e\x72\x65\x6d\x6f\x76\
+\x65\x28\x61\x2c\x21\x30\x29\x7d\x2c\x64\x6f\x6d\x4d\x61\x6e\x69\
+\x70\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x63\x2c\x64\
+\x29\x7b\x76\x61\x72\x20\x65\x2c\x67\x2c\x68\x2c\x69\x2c\x6a\x3d\
+\x61\x5b\x30\x5d\x2c\x6b\x3d\x5b\x5d\x3b\x69\x66\x28\x21\x66\x2e\
+\x73\x75\x70\x70\x6f\x72\x74\x2e\x63\x68\x65\x63\x6b\x43\x6c\x6f\
+\x6e\x65\x26\x26\x61\x72\x67\x75\x6d\x65\x6e\x74\x73\x2e\x6c\x65\
+\x6e\x67\x74\x68\x3d\x3d\x3d\x33\x26\x26\x74\x79\x70\x65\x6f\x66\
+\x20\x6a\x3d\x3d\x22\x73\x74\x72\x69\x6e\x67\x22\x26\x26\x62\x64\
+\x2e\x74\x65\x73\x74\x28\x6a\x29\x29\x72\x65\x74\x75\x72\x6e\x20\
+\x74\x68\x69\x73\x2e\x65\x61\x63\x68\x28\x66\x75\x6e\x63\x74\x69\
+\x6f\x6e\x28\x29\x7b\x66\x28\x74\x68\x69\x73\x29\x2e\x64\x6f\x6d\
+\x4d\x61\x6e\x69\x70\x28\x61\x2c\x63\x2c\x64\x2c\x21\x30\x29\x7d\
+\x29\x3b\x69\x66\x28\x66\x2e\x69\x73\x46\x75\x6e\x63\x74\x69\x6f\
+\x6e\x28\x6a\x29\x29\x72\x65\x74\x75\x72\x6e\x20\x74\x68\x69\x73\
+\x2e\x65\x61\x63\x68\x28\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x65\
+\x29\x7b\x76\x61\x72\x20\x67\x3d\x66\x28\x74\x68\x69\x73\x29\x3b\
+\x61\x5b\x30\x5d\x3d\x6a\x2e\x63\x61\x6c\x6c\x28\x74\x68\x69\x73\
+\x2c\x65\x2c\x63\x3f\x67\x2e\x68\x74\x6d\x6c\x28\x29\x3a\x62\x29\
+\x2c\x67\x2e\x64\x6f\x6d\x4d\x61\x6e\x69\x70\x28\x61\x2c\x63\x2c\
+\x64\x29\x7d\x29\x3b\x69\x66\x28\x74\x68\x69\x73\x5b\x30\x5d\x29\
+\x7b\x69\x3d\x6a\x26\x26\x6a\x2e\x70\x61\x72\x65\x6e\x74\x4e\x6f\
+\x64\x65\x2c\x66\x2e\x73\x75\x70\x70\x6f\x72\x74\x2e\x70\x61\x72\
+\x65\x6e\x74\x4e\x6f\x64\x65\x26\x26\x69\x26\x26\x69\x2e\x6e\x6f\
+\x64\x65\x54\x79\x70\x65\x3d\x3d\x3d\x31\x31\x26\x26\x69\x2e\x63\
+\x68\x69\x6c\x64\x4e\x6f\x64\x65\x73\x2e\x6c\x65\x6e\x67\x74\x68\
+\x3d\x3d\x3d\x74\x68\x69\x73\x2e\x6c\x65\x6e\x67\x74\x68\x3f\x65\
+\x3d\x7b\x66\x72\x61\x67\x6d\x65\x6e\x74\x3a\x69\x7d\x3a\x65\x3d\
+\x66\x2e\x62\x75\x69\x6c\x64\x46\x72\x61\x67\x6d\x65\x6e\x74\x28\
+\x61\x2c\x74\x68\x69\x73\x2c\x6b\x29\x2c\x68\x3d\x65\x2e\x66\x72\
+\x61\x67\x6d\x65\x6e\x74\x2c\x68\x2e\x63\x68\x69\x6c\x64\x4e\x6f\
+\x64\x65\x73\x2e\x6c\x65\x6e\x67\x74\x68\x3d\x3d\x3d\x31\x3f\x67\
+\x3d\x68\x3d\x68\x2e\x66\x69\x72\x73\x74\x43\x68\x69\x6c\x64\x3a\
+\x67\x3d\x68\x2e\x66\x69\x72\x73\x74\x43\x68\x69\x6c\x64\x3b\x69\
+\x66\x28\x67\x29\x7b\x63\x3d\x63\x26\x26\x66\x2e\x6e\x6f\x64\x65\
+\x4e\x61\x6d\x65\x28\x67\x2c\x22\x74\x72\x22\x29\x3b\x66\x6f\x72\
+\x28\x76\x61\x72\x20\x6c\x3d\x30\x2c\x6d\x3d\x74\x68\x69\x73\x2e\
+\x6c\x65\x6e\x67\x74\x68\x2c\x6e\x3d\x6d\x2d\x31\x3b\x6c\x3c\x6d\
+\x3b\x6c\x2b\x2b\x29\x64\x2e\x63\x61\x6c\x6c\x28\x63\x3f\x62\x69\
+\x28\x74\x68\x69\x73\x5b\x6c\x5d\x2c\x67\x29\x3a\x74\x68\x69\x73\
+\x5b\x6c\x5d\x2c\x65\x2e\x63\x61\x63\x68\x65\x61\x62\x6c\x65\x7c\
+\x7c\x6d\x3e\x31\x26\x26\x6c\x3c\x6e\x3f\x66\x2e\x63\x6c\x6f\x6e\
+\x65\x28\x68\x2c\x21\x30\x2c\x21\x30\x29\x3a\x68\x29\x7d\x6b\x2e\
+\x6c\x65\x6e\x67\x74\x68\x26\x26\x66\x2e\x65\x61\x63\x68\x28\x6b\
+\x2c\x62\x70\x29\x7d\x72\x65\x74\x75\x72\x6e\x20\x74\x68\x69\x73\
+\x7d\x7d\x29\x2c\x66\x2e\x62\x75\x69\x6c\x64\x46\x72\x61\x67\x6d\
+\x65\x6e\x74\x3d\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\
+\x2c\x64\x29\x7b\x76\x61\x72\x20\x65\x2c\x67\x2c\x68\x2c\x69\x2c\
+\x6a\x3d\x61\x5b\x30\x5d\x3b\x62\x26\x26\x62\x5b\x30\x5d\x26\x26\
+\x28\x69\x3d\x62\x5b\x30\x5d\x2e\x6f\x77\x6e\x65\x72\x44\x6f\x63\
+\x75\x6d\x65\x6e\x74\x7c\x7c\x62\x5b\x30\x5d\x29\x2c\x69\x2e\x63\
+\x72\x65\x61\x74\x65\x44\x6f\x63\x75\x6d\x65\x6e\x74\x46\x72\x61\
+\x67\x6d\x65\x6e\x74\x7c\x7c\x28\x69\x3d\x63\x29\x2c\x61\x2e\x6c\
+\x65\x6e\x67\x74\x68\x3d\x3d\x3d\x31\x26\x26\x74\x79\x70\x65\x6f\
+\x66\x20\x6a\x3d\x3d\x22\x73\x74\x72\x69\x6e\x67\x22\x26\x26\x6a\
+\x2e\x6c\x65\x6e\x67\x74\x68\x3c\x35\x31\x32\x26\x26\x69\x3d\x3d\
+\x3d\x63\x26\x26\x6a\x2e\x63\x68\x61\x72\x41\x74\x28\x30\x29\x3d\
+\x3d\x3d\x22\x3c\x22\x26\x26\x21\x62\x62\x2e\x74\x65\x73\x74\x28\
+\x6a\x29\x26\x26\x28\x66\x2e\x73\x75\x70\x70\x6f\x72\x74\x2e\x63\
+\x68\x65\x63\x6b\x43\x6c\x6f\x6e\x65\x7c\x7c\x21\x62\x64\x2e\x74\
+\x65\x73\x74\x28\x6a\x29\x29\x26\x26\x28\x66\x2e\x73\x75\x70\x70\
+\x6f\x72\x74\x2e\x68\x74\x6d\x6c\x35\x43\x6c\x6f\x6e\x65\x7c\x7c\
+\x21\x62\x63\x2e\x74\x65\x73\x74\x28\x6a\x29\x29\x26\x26\x28\x67\
+\x3d\x21\x30\x2c\x68\x3d\x66\x2e\x66\x72\x61\x67\x6d\x65\x6e\x74\
+\x73\x5b\x6a\x5d\x2c\x68\x26\x26\x68\x21\x3d\x3d\x31\x26\x26\x28\
+\x65\x3d\x68\x29\x29\x2c\x65\x7c\x7c\x28\x65\x3d\x69\x2e\x63\x72\
+\x65\x61\x74\x65\x44\x6f\x63\x75\x6d\x65\x6e\x74\x46\x72\x61\x67\
+\x6d\x65\x6e\x74\x28\x29\x2c\x66\x2e\x63\x6c\x65\x61\x6e\x28\x61\
+\x2c\x69\x2c\x65\x2c\x64\x29\x29\x2c\x67\x26\x26\x28\x66\x2e\x66\
+\x72\x61\x67\x6d\x65\x6e\x74\x73\x5b\x6a\x5d\x3d\x68\x3f\x65\x3a\
+\x31\x29\x3b\x72\x65\x74\x75\x72\x6e\x7b\x66\x72\x61\x67\x6d\x65\
+\x6e\x74\x3a\x65\x2c\x63\x61\x63\x68\x65\x61\x62\x6c\x65\x3a\x67\
+\x7d\x7d\x2c\x66\x2e\x66\x72\x61\x67\x6d\x65\x6e\x74\x73\x3d\x7b\
+\x7d\x2c\x66\x2e\x65\x61\x63\x68\x28\x7b\x61\x70\x70\x65\x6e\x64\
+\x54\x6f\x3a\x22\x61\x70\x70\x65\x6e\x64\x22\x2c\x70\x72\x65\x70\
+\x65\x6e\x64\x54\x6f\x3a\x22\x70\x72\x65\x70\x65\x6e\x64\x22\x2c\
+\x69\x6e\x73\x65\x72\x74\x42\x65\x66\x6f\x72\x65\x3a\x22\x62\x65\
+\x66\x6f\x72\x65\x22\x2c\x69\x6e\x73\x65\x72\x74\x41\x66\x74\x65\
+\x72\x3a\x22\x61\x66\x74\x65\x72\x22\x2c\x72\x65\x70\x6c\x61\x63\
+\x65\x41\x6c\x6c\x3a\x22\x72\x65\x70\x6c\x61\x63\x65\x57\x69\x74\
+\x68\x22\x7d\x2c\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\
+\x29\x7b\x66\x2e\x66\x6e\x5b\x61\x5d\x3d\x66\x75\x6e\x63\x74\x69\
+\x6f\x6e\x28\x63\x29\x7b\x76\x61\x72\x20\x64\x3d\x5b\x5d\x2c\x65\
+\x3d\x66\x28\x63\x29\x2c\x67\x3d\x74\x68\x69\x73\x2e\x6c\x65\x6e\
+\x67\x74\x68\x3d\x3d\x3d\x31\x26\x26\x74\x68\x69\x73\x5b\x30\x5d\
+\x2e\x70\x61\x72\x65\x6e\x74\x4e\x6f\x64\x65\x3b\x69\x66\x28\x67\
+\x26\x26\x67\x2e\x6e\x6f\x64\x65\x54\x79\x70\x65\x3d\x3d\x3d\x31\
+\x31\x26\x26\x67\x2e\x63\x68\x69\x6c\x64\x4e\x6f\x64\x65\x73\x2e\
+\x6c\x65\x6e\x67\x74\x68\x3d\x3d\x3d\x31\x26\x26\x65\x2e\x6c\x65\
+\x6e\x67\x74\x68\x3d\x3d\x3d\x31\x29\x7b\x65\x5b\x62\x5d\x28\x74\
+\x68\x69\x73\x5b\x30\x5d\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x74\
+\x68\x69\x73\x7d\x66\x6f\x72\x28\x76\x61\x72\x20\x68\x3d\x30\x2c\
+\x69\x3d\x65\x2e\x6c\x65\x6e\x67\x74\x68\x3b\x68\x3c\x69\x3b\x68\
+\x2b\x2b\x29\x7b\x76\x61\x72\x20\x6a\x3d\x28\x68\x3e\x30\x3f\x74\
+\x68\x69\x73\x2e\x63\x6c\x6f\x6e\x65\x28\x21\x30\x29\x3a\x74\x68\
+\x69\x73\x29\x2e\x67\x65\x74\x28\x29\x3b\x66\x28\x65\x5b\x68\x5d\
+\x29\x5b\x62\x5d\x28\x6a\x29\x2c\x64\x3d\x64\x2e\x63\x6f\x6e\x63\
+\x61\x74\x28\x6a\x29\x7d\x72\x65\x74\x75\x72\x6e\x20\x74\x68\x69\
+\x73\x2e\x70\x75\x73\x68\x53\x74\x61\x63\x6b\x28\x64\x2c\x61\x2c\
+\x65\x2e\x73\x65\x6c\x65\x63\x74\x6f\x72\x29\x7d\x7d\x29\x2c\x66\
+\x2e\x65\x78\x74\x65\x6e\x64\x28\x7b\x63\x6c\x6f\x6e\x65\x3a\x66\
+\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x2c\x63\x29\x7b\x76\
+\x61\x72\x20\x64\x2c\x65\x2c\x67\x2c\x68\x3d\x66\x2e\x73\x75\x70\
+\x70\x6f\x72\x74\x2e\x68\x74\x6d\x6c\x35\x43\x6c\x6f\x6e\x65\x7c\
+\x7c\x21\x62\x63\x2e\x74\x65\x73\x74\x28\x22\x3c\x22\x2b\x61\x2e\
+\x6e\x6f\x64\x65\x4e\x61\x6d\x65\x29\x3f\x61\x2e\x63\x6c\x6f\x6e\
+\x65\x4e\x6f\x64\x65\x28\x21\x30\x29\x3a\x62\x6f\x28\x61\x29\x3b\
+\x69\x66\x28\x28\x21\x66\x2e\x73\x75\x70\x70\x6f\x72\x74\x2e\x6e\
+\x6f\x43\x6c\x6f\x6e\x65\x45\x76\x65\x6e\x74\x7c\x7c\x21\x66\x2e\
+\x73\x75\x70\x70\x6f\x72\x74\x2e\x6e\x6f\x43\x6c\x6f\x6e\x65\x43\
+\x68\x65\x63\x6b\x65\x64\x29\x26\x26\x28\x61\x2e\x6e\x6f\x64\x65\
+\x54\x79\x70\x65\x3d\x3d\x3d\x31\x7c\x7c\x61\x2e\x6e\x6f\x64\x65\
+\x54\x79\x70\x65\x3d\x3d\x3d\x31\x31\x29\x26\x26\x21\x66\x2e\x69\
+\x73\x58\x4d\x4c\x44\x6f\x63\x28\x61\x29\x29\x7b\x62\x6b\x28\x61\
+\x2c\x68\x29\x2c\x64\x3d\x62\x6c\x28\x61\x29\x2c\x65\x3d\x62\x6c\
+\x28\x68\x29\x3b\x66\x6f\x72\x28\x67\x3d\x30\x3b\x64\x5b\x67\x5d\
+\x3b\x2b\x2b\x67\x29\x65\x5b\x67\x5d\x26\x26\x62\x6b\x28\x64\x5b\
+\x67\x5d\x2c\x65\x5b\x67\x5d\x29\x7d\x69\x66\x28\x62\x29\x7b\x62\
+\x6a\x28\x61\x2c\x68\x29\x3b\x69\x66\x28\x63\x29\x7b\x64\x3d\x62\
+\x6c\x28\x61\x29\x2c\x65\x3d\x62\x6c\x28\x68\x29\x3b\x66\x6f\x72\
+\x28\x67\x3d\x30\x3b\x64\x5b\x67\x5d\x3b\x2b\x2b\x67\x29\x62\x6a\
+\x28\x64\x5b\x67\x5d\x2c\x65\x5b\x67\x5d\x29\x7d\x7d\x64\x3d\x65\
+\x3d\x6e\x75\x6c\x6c\x3b\x72\x65\x74\x75\x72\x6e\x20\x68\x7d\x2c\
+\x63\x6c\x65\x61\x6e\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\
+\x2c\x62\x2c\x64\x2c\x65\x29\x7b\x76\x61\x72\x20\x67\x3b\x62\x3d\
+\x62\x7c\x7c\x63\x2c\x74\x79\x70\x65\x6f\x66\x20\x62\x2e\x63\x72\
+\x65\x61\x74\x65\x45\x6c\x65\x6d\x65\x6e\x74\x3d\x3d\x22\x75\x6e\
+\x64\x65\x66\x69\x6e\x65\x64\x22\x26\x26\x28\x62\x3d\x62\x2e\x6f\
+\x77\x6e\x65\x72\x44\x6f\x63\x75\x6d\x65\x6e\x74\x7c\x7c\x62\x5b\
+\x30\x5d\x26\x26\x62\x5b\x30\x5d\x2e\x6f\x77\x6e\x65\x72\x44\x6f\
+\x63\x75\x6d\x65\x6e\x74\x7c\x7c\x63\x29\x3b\x76\x61\x72\x20\x68\
+\x3d\x5b\x5d\x2c\x69\x3b\x66\x6f\x72\x28\x76\x61\x72\x20\x6a\x3d\
+\x30\x2c\x6b\x3b\x28\x6b\x3d\x61\x5b\x6a\x5d\x29\x21\x3d\x6e\x75\
+\x6c\x6c\x3b\x6a\x2b\x2b\x29\x7b\x74\x79\x70\x65\x6f\x66\x20\x6b\
+\x3d\x3d\x22\x6e\x75\x6d\x62\x65\x72\x22\x26\x26\x28\x6b\x2b\x3d\
+\x22\x22\x29\x3b\x69\x66\x28\x21\x6b\x29\x63\x6f\x6e\x74\x69\x6e\
+\x75\x65\x3b\x69\x66\x28\x74\x79\x70\x65\x6f\x66\x20\x6b\x3d\x3d\
+\x22\x73\x74\x72\x69\x6e\x67\x22\x29\x69\x66\x28\x21\x5f\x2e\x74\
+\x65\x73\x74\x28\x6b\x29\x29\x6b\x3d\x62\x2e\x63\x72\x65\x61\x74\
+\x65\x54\x65\x78\x74\x4e\x6f\x64\x65\x28\x6b\x29\x3b\x65\x6c\x73\
+\x65\x7b\x6b\x3d\x6b\x2e\x72\x65\x70\x6c\x61\x63\x65\x28\x59\x2c\
+\x22\x3c\x24\x31\x3e\x3c\x2f\x24\x32\x3e\x22\x29\x3b\x76\x61\x72\
+\x20\x6c\x3d\x28\x5a\x2e\x65\x78\x65\x63\x28\x6b\x29\x7c\x7c\x5b\
+\x22\x22\x2c\x22\x22\x5d\x29\x5b\x31\x5d\x2e\x74\x6f\x4c\x6f\x77\
+\x65\x72\x43\x61\x73\x65\x28\x29\x2c\x6d\x3d\x62\x67\x5b\x6c\x5d\
+\x7c\x7c\x62\x67\x2e\x5f\x64\x65\x66\x61\x75\x6c\x74\x2c\x6e\x3d\
+\x6d\x5b\x30\x5d\x2c\x6f\x3d\x62\x2e\x63\x72\x65\x61\x74\x65\x45\
+\x6c\x65\x6d\x65\x6e\x74\x28\x22\x64\x69\x76\x22\x29\x3b\x62\x3d\
+\x3d\x3d\x63\x3f\x62\x68\x2e\x61\x70\x70\x65\x6e\x64\x43\x68\x69\
+\x6c\x64\x28\x6f\x29\x3a\x55\x28\x62\x29\x2e\x61\x70\x70\x65\x6e\
+\x64\x43\x68\x69\x6c\x64\x28\x6f\x29\x2c\x6f\x2e\x69\x6e\x6e\x65\
+\x72\x48\x54\x4d\x4c\x3d\x6d\x5b\x31\x5d\x2b\x6b\x2b\x6d\x5b\x32\
+\x5d\x3b\x77\x68\x69\x6c\x65\x28\x6e\x2d\x2d\x29\x6f\x3d\x6f\x2e\
+\x6c\x61\x73\x74\x43\x68\x69\x6c\x64\x3b\x69\x66\x28\x21\x66\x2e\
+\x73\x75\x70\x70\x6f\x72\x74\x2e\x74\x62\x6f\x64\x79\x29\x7b\x76\
+\x61\x72\x20\x70\x3d\x24\x2e\x74\x65\x73\x74\x28\x6b\x29\x2c\x71\
+\x3d\x6c\x3d\x3d\x3d\x22\x74\x61\x62\x6c\x65\x22\x26\x26\x21\x70\
+\x3f\x6f\x2e\x66\x69\x72\x73\x74\x43\x68\x69\x6c\x64\x26\x26\x6f\
+\x2e\x66\x69\x72\x73\x74\x43\x68\x69\x6c\x64\x2e\x63\x68\x69\x6c\
+\x64\x4e\x6f\x64\x65\x73\x3a\x6d\x5b\x31\x5d\x3d\x3d\x3d\x22\x3c\
+\x74\x61\x62\x6c\x65\x3e\x22\x26\x26\x21\x70\x3f\x6f\x2e\x63\x68\
+\x69\x6c\x64\x4e\x6f\x64\x65\x73\x3a\x5b\x5d\x3b\x66\x6f\x72\x28\
+\x69\x3d\x71\x2e\x6c\x65\x6e\x67\x74\x68\x2d\x31\x3b\x69\x3e\x3d\
+\x30\x3b\x2d\x2d\x69\x29\x66\x2e\x6e\x6f\x64\x65\x4e\x61\x6d\x65\
+\x28\x71\x5b\x69\x5d\x2c\x22\x74\x62\x6f\x64\x79\x22\x29\x26\x26\
+\x21\x71\x5b\x69\x5d\x2e\x63\x68\x69\x6c\x64\x4e\x6f\x64\x65\x73\
+\x2e\x6c\x65\x6e\x67\x74\x68\x26\x26\x71\x5b\x69\x5d\x2e\x70\x61\
+\x72\x65\x6e\x74\x4e\x6f\x64\x65\x2e\x72\x65\x6d\x6f\x76\x65\x43\
+\x68\x69\x6c\x64\x28\x71\x5b\x69\x5d\x29\x7d\x21\x66\x2e\x73\x75\
+\x70\x70\x6f\x72\x74\x2e\x6c\x65\x61\x64\x69\x6e\x67\x57\x68\x69\
+\x74\x65\x73\x70\x61\x63\x65\x26\x26\x58\x2e\x74\x65\x73\x74\x28\
+\x6b\x29\x26\x26\x6f\x2e\x69\x6e\x73\x65\x72\x74\x42\x65\x66\x6f\
+\x72\x65\x28\x62\x2e\x63\x72\x65\x61\x74\x65\x54\x65\x78\x74\x4e\
+\x6f\x64\x65\x28\x58\x2e\x65\x78\x65\x63\x28\x6b\x29\x5b\x30\x5d\
+\x29\x2c\x6f\x2e\x66\x69\x72\x73\x74\x43\x68\x69\x6c\x64\x29\x2c\
+\x6b\x3d\x6f\x2e\x63\x68\x69\x6c\x64\x4e\x6f\x64\x65\x73\x7d\x76\
+\x61\x72\x20\x72\x3b\x69\x66\x28\x21\x66\x2e\x73\x75\x70\x70\x6f\
+\x72\x74\x2e\x61\x70\x70\x65\x6e\x64\x43\x68\x65\x63\x6b\x65\x64\
+\x29\x69\x66\x28\x6b\x5b\x30\x5d\x26\x26\x74\x79\x70\x65\x6f\x66\
+\x20\x28\x72\x3d\x6b\x2e\x6c\x65\x6e\x67\x74\x68\x29\x3d\x3d\x22\
+\x6e\x75\x6d\x62\x65\x72\x22\x29\x66\x6f\x72\x28\x69\x3d\x30\x3b\
+\x69\x3c\x72\x3b\x69\x2b\x2b\x29\x62\x6e\x28\x6b\x5b\x69\x5d\x29\
+\x3b\x65\x6c\x73\x65\x20\x62\x6e\x28\x6b\x29\x3b\x6b\x2e\x6e\x6f\
+\x64\x65\x54\x79\x70\x65\x3f\x68\x2e\x70\x75\x73\x68\x28\x6b\x29\
+\x3a\x68\x3d\x66\x2e\x6d\x65\x72\x67\x65\x28\x68\x2c\x6b\x29\x7d\
+\x69\x66\x28\x64\x29\x7b\x67\x3d\x66\x75\x6e\x63\x74\x69\x6f\x6e\
+\x28\x61\x29\x7b\x72\x65\x74\x75\x72\x6e\x21\x61\x2e\x74\x79\x70\
+\x65\x7c\x7c\x62\x65\x2e\x74\x65\x73\x74\x28\x61\x2e\x74\x79\x70\
+\x65\x29\x7d\x3b\x66\x6f\x72\x28\x6a\x3d\x30\x3b\x68\x5b\x6a\x5d\
+\x3b\x6a\x2b\x2b\x29\x69\x66\x28\x65\x26\x26\x66\x2e\x6e\x6f\x64\
+\x65\x4e\x61\x6d\x65\x28\x68\x5b\x6a\x5d\x2c\x22\x73\x63\x72\x69\
+\x70\x74\x22\x29\x26\x26\x28\x21\x68\x5b\x6a\x5d\x2e\x74\x79\x70\
+\x65\x7c\x7c\x68\x5b\x6a\x5d\x2e\x74\x79\x70\x65\x2e\x74\x6f\x4c\
+\x6f\x77\x65\x72\x43\x61\x73\x65\x28\x29\x3d\x3d\x3d\x22\x74\x65\
+\x78\x74\x2f\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x22\x29\x29\
+\x65\x2e\x70\x75\x73\x68\x28\x68\x5b\x6a\x5d\x2e\x70\x61\x72\x65\
+\x6e\x74\x4e\x6f\x64\x65\x3f\x68\x5b\x6a\x5d\x2e\x70\x61\x72\x65\
+\x6e\x74\x4e\x6f\x64\x65\x2e\x72\x65\x6d\x6f\x76\x65\x43\x68\x69\
+\x6c\x64\x28\x68\x5b\x6a\x5d\x29\x3a\x68\x5b\x6a\x5d\x29\x3b\x65\
+\x6c\x73\x65\x7b\x69\x66\x28\x68\x5b\x6a\x5d\x2e\x6e\x6f\x64\x65\
+\x54\x79\x70\x65\x3d\x3d\x3d\x31\x29\x7b\x76\x61\x72\x20\x73\x3d\
+\x66\x2e\x67\x72\x65\x70\x28\x68\x5b\x6a\x5d\x2e\x67\x65\x74\x45\
+\x6c\x65\x6d\x65\x6e\x74\x73\x42\x79\x54\x61\x67\x4e\x61\x6d\x65\
+\x28\x22\x73\x63\x72\x69\x70\x74\x22\x29\x2c\x67\x29\x3b\x68\x2e\
+\x73\x70\x6c\x69\x63\x65\x2e\x61\x70\x70\x6c\x79\x28\x68\x2c\x5b\
+\x6a\x2b\x31\x2c\x30\x5d\x2e\x63\x6f\x6e\x63\x61\x74\x28\x73\x29\
+\x29\x7d\x64\x2e\x61\x70\x70\x65\x6e\x64\x43\x68\x69\x6c\x64\x28\
+\x68\x5b\x6a\x5d\x29\x7d\x7d\x72\x65\x74\x75\x72\x6e\x20\x68\x7d\
+\x2c\x63\x6c\x65\x61\x6e\x44\x61\x74\x61\x3a\x66\x75\x6e\x63\x74\
+\x69\x6f\x6e\x28\x61\x29\x7b\x76\x61\x72\x20\x62\x2c\x63\x2c\x64\
+\x3d\x66\x2e\x63\x61\x63\x68\x65\x2c\x65\x3d\x66\x2e\x65\x76\x65\
+\x6e\x74\x2e\x73\x70\x65\x63\x69\x61\x6c\x2c\x67\x3d\x66\x2e\x73\
+\x75\x70\x70\x6f\x72\x74\x2e\x64\x65\x6c\x65\x74\x65\x45\x78\x70\
+\x61\x6e\x64\x6f\x3b\x66\x6f\x72\x28\x76\x61\x72\x20\x68\x3d\x30\
+\x2c\x69\x3b\x28\x69\x3d\x61\x5b\x68\x5d\x29\x21\x3d\x6e\x75\x6c\
+\x6c\x3b\x68\x2b\x2b\x29\x7b\x69\x66\x28\x69\x2e\x6e\x6f\x64\x65\
+\x4e\x61\x6d\x65\x26\x26\x66\x2e\x6e\x6f\x44\x61\x74\x61\x5b\x69\
+\x2e\x6e\x6f\x64\x65\x4e\x61\x6d\x65\x2e\x74\x6f\x4c\x6f\x77\x65\
+\x72\x43\x61\x73\x65\x28\x29\x5d\x29\x63\x6f\x6e\x74\x69\x6e\x75\
+\x65\x3b\x63\x3d\x69\x5b\x66\x2e\x65\x78\x70\x61\x6e\x64\x6f\x5d\
+\x3b\x69\x66\x28\x63\x29\x7b\x62\x3d\x64\x5b\x63\x5d\x3b\x69\x66\
+\x28\x62\x26\x26\x62\x2e\x65\x76\x65\x6e\x74\x73\x29\x7b\x66\x6f\
+\x72\x28\x76\x61\x72\x20\x6a\x20\x69\x6e\x20\x62\x2e\x65\x76\x65\
+\x6e\x74\x73\x29\x65\x5b\x6a\x5d\x3f\x66\x2e\x65\x76\x65\x6e\x74\
+\x2e\x72\x65\x6d\x6f\x76\x65\x28\x69\x2c\x6a\x29\x3a\x66\x2e\x72\
+\x65\x6d\x6f\x76\x65\x45\x76\x65\x6e\x74\x28\x69\x2c\x6a\x2c\x62\
+\x2e\x68\x61\x6e\x64\x6c\x65\x29\x3b\x62\x2e\x68\x61\x6e\x64\x6c\
+\x65\x26\x26\x28\x62\x2e\x68\x61\x6e\x64\x6c\x65\x2e\x65\x6c\x65\
+\x6d\x3d\x6e\x75\x6c\x6c\x29\x7d\x67\x3f\x64\x65\x6c\x65\x74\x65\
+\x20\x69\x5b\x66\x2e\x65\x78\x70\x61\x6e\x64\x6f\x5d\x3a\x69\x2e\
+\x72\x65\x6d\x6f\x76\x65\x41\x74\x74\x72\x69\x62\x75\x74\x65\x26\
+\x26\x69\x2e\x72\x65\x6d\x6f\x76\x65\x41\x74\x74\x72\x69\x62\x75\
+\x74\x65\x28\x66\x2e\x65\x78\x70\x61\x6e\x64\x6f\x29\x2c\x64\x65\
+\x6c\x65\x74\x65\x20\x64\x5b\x63\x5d\x7d\x7d\x7d\x7d\x29\x3b\x76\
+\x61\x72\x20\x62\x71\x3d\x2f\x61\x6c\x70\x68\x61\x5c\x28\x5b\x5e\
+\x29\x5d\x2a\x5c\x29\x2f\x69\x2c\x62\x72\x3d\x2f\x6f\x70\x61\x63\
+\x69\x74\x79\x3d\x28\x5b\x5e\x29\x5d\x2a\x29\x2f\x2c\x62\x73\x3d\
+\x2f\x28\x5b\x41\x2d\x5a\x5d\x7c\x5e\x6d\x73\x29\x2f\x67\x2c\x62\
+\x74\x3d\x2f\x5e\x2d\x3f\x5c\x64\x2b\x28\x3f\x3a\x70\x78\x29\x3f\
+\x24\x2f\x69\x2c\x62\x75\x3d\x2f\x5e\x2d\x3f\x5c\x64\x2f\x2c\x62\
+\x76\x3d\x2f\x5e\x28\x5b\x5c\x2d\x2b\x5d\x29\x3d\x28\x5b\x5c\x2d\
+\x2b\x2e\x5c\x64\x65\x5d\x2b\x29\x2f\x2c\x62\x77\x3d\x7b\x70\x6f\
+\x73\x69\x74\x69\x6f\x6e\x3a\x22\x61\x62\x73\x6f\x6c\x75\x74\x65\
+\x22\x2c\x76\x69\x73\x69\x62\x69\x6c\x69\x74\x79\x3a\x22\x68\x69\
+\x64\x64\x65\x6e\x22\x2c\x64\x69\x73\x70\x6c\x61\x79\x3a\x22\x62\
+\x6c\x6f\x63\x6b\x22\x7d\x2c\x62\x78\x3d\x5b\x22\x4c\x65\x66\x74\
+\x22\x2c\x22\x52\x69\x67\x68\x74\x22\x5d\x2c\x62\x79\x3d\x5b\x22\
+\x54\x6f\x70\x22\x2c\x22\x42\x6f\x74\x74\x6f\x6d\x22\x5d\x2c\x62\
+\x7a\x2c\x62\x41\x2c\x62\x42\x3b\x66\x2e\x66\x6e\x2e\x63\x73\x73\
+\x3d\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x63\x29\x7b\x69\
+\x66\x28\x61\x72\x67\x75\x6d\x65\x6e\x74\x73\x2e\x6c\x65\x6e\x67\
+\x74\x68\x3d\x3d\x3d\x32\x26\x26\x63\x3d\x3d\x3d\x62\x29\x72\x65\
+\x74\x75\x72\x6e\x20\x74\x68\x69\x73\x3b\x72\x65\x74\x75\x72\x6e\
+\x20\x66\x2e\x61\x63\x63\x65\x73\x73\x28\x74\x68\x69\x73\x2c\x61\
+\x2c\x63\x2c\x21\x30\x2c\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\
+\x2c\x63\x2c\x64\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x64\x21\x3d\
+\x3d\x62\x3f\x66\x2e\x73\x74\x79\x6c\x65\x28\x61\x2c\x63\x2c\x64\
+\x29\x3a\x66\x2e\x63\x73\x73\x28\x61\x2c\x63\x29\x7d\x29\x7d\x2c\
+\x66\x2e\x65\x78\x74\x65\x6e\x64\x28\x7b\x63\x73\x73\x48\x6f\x6f\
+\x6b\x73\x3a\x7b\x6f\x70\x61\x63\x69\x74\x79\x3a\x7b\x67\x65\x74\
+\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x29\x7b\x69\
+\x66\x28\x62\x29\x7b\x76\x61\x72\x20\x63\x3d\x62\x7a\x28\x61\x2c\
+\x22\x6f\x70\x61\x63\x69\x74\x79\x22\x2c\x22\x6f\x70\x61\x63\x69\
+\x74\x79\x22\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x63\x3d\x3d\x3d\
+\x22\x22\x3f\x22\x31\x22\x3a\x63\x7d\x72\x65\x74\x75\x72\x6e\x20\
+\x61\x2e\x73\x74\x79\x6c\x65\x2e\x6f\x70\x61\x63\x69\x74\x79\x7d\
+\x7d\x7d\x2c\x63\x73\x73\x4e\x75\x6d\x62\x65\x72\x3a\x7b\x66\x69\
+\x6c\x6c\x4f\x70\x61\x63\x69\x74\x79\x3a\x21\x30\x2c\x66\x6f\x6e\
+\x74\x57\x65\x69\x67\x68\x74\x3a\x21\x30\x2c\x6c\x69\x6e\x65\x48\
+\x65\x69\x67\x68\x74\x3a\x21\x30\x2c\x6f\x70\x61\x63\x69\x74\x79\
+\x3a\x21\x30\x2c\x6f\x72\x70\x68\x61\x6e\x73\x3a\x21\x30\x2c\x77\
+\x69\x64\x6f\x77\x73\x3a\x21\x30\x2c\x7a\x49\x6e\x64\x65\x78\x3a\
+\x21\x30\x2c\x7a\x6f\x6f\x6d\x3a\x21\x30\x7d\x2c\x63\x73\x73\x50\
+\x72\x6f\x70\x73\x3a\x7b\x22\x66\x6c\x6f\x61\x74\x22\x3a\x66\x2e\
+\x73\x75\x70\x70\x6f\x72\x74\x2e\x63\x73\x73\x46\x6c\x6f\x61\x74\
+\x3f\x22\x63\x73\x73\x46\x6c\x6f\x61\x74\x22\x3a\x22\x73\x74\x79\
+\x6c\x65\x46\x6c\x6f\x61\x74\x22\x7d\x2c\x73\x74\x79\x6c\x65\x3a\
+\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x63\x2c\x64\x2c\x65\
+\x29\x7b\x69\x66\x28\x21\x21\x61\x26\x26\x61\x2e\x6e\x6f\x64\x65\
+\x54\x79\x70\x65\x21\x3d\x3d\x33\x26\x26\x61\x2e\x6e\x6f\x64\x65\
+\x54\x79\x70\x65\x21\x3d\x3d\x38\x26\x26\x21\x21\x61\x2e\x73\x74\
+\x79\x6c\x65\x29\x7b\x76\x61\x72\x20\x67\x2c\x68\x2c\x69\x3d\x66\
+\x2e\x63\x61\x6d\x65\x6c\x43\x61\x73\x65\x28\x63\x29\x2c\x6a\x3d\
+\x61\x2e\x73\x74\x79\x6c\x65\x2c\x6b\x3d\x66\x2e\x63\x73\x73\x48\
+\x6f\x6f\x6b\x73\x5b\x69\x5d\x3b\x63\x3d\x66\x2e\x63\x73\x73\x50\
+\x72\x6f\x70\x73\x5b\x69\x5d\x7c\x7c\x69\x3b\x69\x66\x28\x64\x3d\
+\x3d\x3d\x62\x29\x7b\x69\x66\x28\x6b\x26\x26\x22\x67\x65\x74\x22\
+\x69\x6e\x20\x6b\x26\x26\x28\x67\x3d\x6b\x2e\x67\x65\x74\x28\x61\
+\x2c\x21\x31\x2c\x65\x29\x29\x21\x3d\x3d\x62\x29\x72\x65\x74\x75\
+\x72\x6e\x20\x67\x3b\x72\x65\x74\x75\x72\x6e\x20\x6a\x5b\x63\x5d\
+\x7d\x68\x3d\x74\x79\x70\x65\x6f\x66\x20\x64\x2c\x68\x3d\x3d\x3d\
+\x22\x73\x74\x72\x69\x6e\x67\x22\x26\x26\x28\x67\x3d\x62\x76\x2e\
+\x65\x78\x65\x63\x28\x64\x29\x29\x26\x26\x28\x64\x3d\x2b\x28\x67\
+\x5b\x31\x5d\x2b\x31\x29\x2a\x2b\x67\x5b\x32\x5d\x2b\x70\x61\x72\
+\x73\x65\x46\x6c\x6f\x61\x74\x28\x66\x2e\x63\x73\x73\x28\x61\x2c\
+\x63\x29\x29\x2c\x68\x3d\x22\x6e\x75\x6d\x62\x65\x72\x22\x29\x3b\
+\x69\x66\x28\x64\x3d\x3d\x6e\x75\x6c\x6c\x7c\x7c\x68\x3d\x3d\x3d\
+\x22\x6e\x75\x6d\x62\x65\x72\x22\x26\x26\x69\x73\x4e\x61\x4e\x28\
+\x64\x29\x29\x72\x65\x74\x75\x72\x6e\x3b\x68\x3d\x3d\x3d\x22\x6e\
+\x75\x6d\x62\x65\x72\x22\x26\x26\x21\x66\x2e\x63\x73\x73\x4e\x75\
+\x6d\x62\x65\x72\x5b\x69\x5d\x26\x26\x28\x64\x2b\x3d\x22\x70\x78\
+\x22\x29\x3b\x69\x66\x28\x21\x6b\x7c\x7c\x21\x28\x22\x73\x65\x74\
+\x22\x69\x6e\x20\x6b\x29\x7c\x7c\x28\x64\x3d\x6b\x2e\x73\x65\x74\
+\x28\x61\x2c\x64\x29\x29\x21\x3d\x3d\x62\x29\x74\x72\x79\x7b\x6a\
+\x5b\x63\x5d\x3d\x64\x7d\x63\x61\x74\x63\x68\x28\x6c\x29\x7b\x7d\
+\x7d\x7d\x2c\x63\x73\x73\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\
+\x61\x2c\x63\x2c\x64\x29\x7b\x76\x61\x72\x20\x65\x2c\x67\x3b\x63\
+\x3d\x66\x2e\x63\x61\x6d\x65\x6c\x43\x61\x73\x65\x28\x63\x29\x2c\
+\x67\x3d\x66\x2e\x63\x73\x73\x48\x6f\x6f\x6b\x73\x5b\x63\x5d\x2c\
+\x63\x3d\x66\x2e\x63\x73\x73\x50\x72\x6f\x70\x73\x5b\x63\x5d\x7c\
+\x7c\x63\x2c\x63\x3d\x3d\x3d\x22\x63\x73\x73\x46\x6c\x6f\x61\x74\
+\x22\x26\x26\x28\x63\x3d\x22\x66\x6c\x6f\x61\x74\x22\x29\x3b\x69\
+\x66\x28\x67\x26\x26\x22\x67\x65\x74\x22\x69\x6e\x20\x67\x26\x26\
+\x28\x65\x3d\x67\x2e\x67\x65\x74\x28\x61\x2c\x21\x30\x2c\x64\x29\
+\x29\x21\x3d\x3d\x62\x29\x72\x65\x74\x75\x72\x6e\x20\x65\x3b\x69\
+\x66\x28\x62\x7a\x29\x72\x65\x74\x75\x72\x6e\x20\x62\x7a\x28\x61\
+\x2c\x63\x29\x7d\x2c\x73\x77\x61\x70\x3a\x66\x75\x6e\x63\x74\x69\
+\x6f\x6e\x28\x61\x2c\x62\x2c\x63\x29\x7b\x76\x61\x72\x20\x64\x3d\
+\x7b\x7d\x3b\x66\x6f\x72\x28\x76\x61\x72\x20\x65\x20\x69\x6e\x20\
+\x62\x29\x64\x5b\x65\x5d\x3d\x61\x2e\x73\x74\x79\x6c\x65\x5b\x65\
+\x5d\x2c\x61\x2e\x73\x74\x79\x6c\x65\x5b\x65\x5d\x3d\x62\x5b\x65\
+\x5d\x3b\x63\x2e\x63\x61\x6c\x6c\x28\x61\x29\x3b\x66\x6f\x72\x28\
+\x65\x20\x69\x6e\x20\x62\x29\x61\x2e\x73\x74\x79\x6c\x65\x5b\x65\
+\x5d\x3d\x64\x5b\x65\x5d\x7d\x7d\x29\x2c\x66\x2e\x63\x75\x72\x43\
+\x53\x53\x3d\x66\x2e\x63\x73\x73\x2c\x66\x2e\x65\x61\x63\x68\x28\
+\x5b\x22\x68\x65\x69\x67\x68\x74\x22\x2c\x22\x77\x69\x64\x74\x68\
+\x22\x5d\x2c\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x29\
+\x7b\x66\x2e\x63\x73\x73\x48\x6f\x6f\x6b\x73\x5b\x62\x5d\x3d\x7b\
+\x67\x65\x74\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x63\
+\x2c\x64\x29\x7b\x76\x61\x72\x20\x65\x3b\x69\x66\x28\x63\x29\x7b\
+\x69\x66\x28\x61\x2e\x6f\x66\x66\x73\x65\x74\x57\x69\x64\x74\x68\
+\x21\x3d\x3d\x30\x29\x72\x65\x74\x75\x72\x6e\x20\x62\x43\x28\x61\
+\x2c\x62\x2c\x64\x29\x3b\x66\x2e\x73\x77\x61\x70\x28\x61\x2c\x62\
+\x77\x2c\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x29\x7b\x65\x3d\x62\
+\x43\x28\x61\x2c\x62\x2c\x64\x29\x7d\x29\x3b\x72\x65\x74\x75\x72\
+\x6e\x20\x65\x7d\x7d\x2c\x73\x65\x74\x3a\x66\x75\x6e\x63\x74\x69\
+\x6f\x6e\x28\x61\x2c\x62\x29\x7b\x69\x66\x28\x21\x62\x74\x2e\x74\
+\x65\x73\x74\x28\x62\x29\x29\x72\x65\x74\x75\x72\x6e\x20\x62\x3b\
+\x62\x3d\x70\x61\x72\x73\x65\x46\x6c\x6f\x61\x74\x28\x62\x29\x3b\
+\x69\x66\x28\x62\x3e\x3d\x30\x29\x72\x65\x74\x75\x72\x6e\x20\x62\
+\x2b\x22\x70\x78\x22\x7d\x7d\x7d\x29\x2c\x66\x2e\x73\x75\x70\x70\
+\x6f\x72\x74\x2e\x6f\x70\x61\x63\x69\x74\x79\x7c\x7c\x28\x66\x2e\
+\x63\x73\x73\x48\x6f\x6f\x6b\x73\x2e\x6f\x70\x61\x63\x69\x74\x79\
+\x3d\x7b\x67\x65\x74\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\
+\x2c\x62\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x62\x72\x2e\x74\x65\
+\x73\x74\x28\x28\x62\x26\x26\x61\x2e\x63\x75\x72\x72\x65\x6e\x74\
+\x53\x74\x79\x6c\x65\x3f\x61\x2e\x63\x75\x72\x72\x65\x6e\x74\x53\
+\x74\x79\x6c\x65\x2e\x66\x69\x6c\x74\x65\x72\x3a\x61\x2e\x73\x74\
+\x79\x6c\x65\x2e\x66\x69\x6c\x74\x65\x72\x29\x7c\x7c\x22\x22\x29\
+\x3f\x70\x61\x72\x73\x65\x46\x6c\x6f\x61\x74\x28\x52\x65\x67\x45\
+\x78\x70\x2e\x24\x31\x29\x2f\x31\x30\x30\x2b\x22\x22\x3a\x62\x3f\
+\x22\x31\x22\x3a\x22\x22\x7d\x2c\x73\x65\x74\x3a\x66\x75\x6e\x63\
+\x74\x69\x6f\x6e\x28\x61\x2c\x62\x29\x7b\x76\x61\x72\x20\x63\x3d\
+\x61\x2e\x73\x74\x79\x6c\x65\x2c\x64\x3d\x61\x2e\x63\x75\x72\x72\
+\x65\x6e\x74\x53\x74\x79\x6c\x65\x2c\x65\x3d\x66\x2e\x69\x73\x4e\
+\x75\x6d\x65\x72\x69\x63\x28\x62\x29\x3f\x22\x61\x6c\x70\x68\x61\
+\x28\x6f\x70\x61\x63\x69\x74\x79\x3d\x22\x2b\x62\x2a\x31\x30\x30\
+\x2b\x22\x29\x22\x3a\x22\x22\x2c\x67\x3d\x64\x26\x26\x64\x2e\x66\
+\x69\x6c\x74\x65\x72\x7c\x7c\x63\x2e\x66\x69\x6c\x74\x65\x72\x7c\
+\x7c\x22\x22\x3b\x63\x2e\x7a\x6f\x6f\x6d\x3d\x31\x3b\x69\x66\x28\
+\x62\x3e\x3d\x31\x26\x26\x66\x2e\x74\x72\x69\x6d\x28\x67\x2e\x72\
+\x65\x70\x6c\x61\x63\x65\x28\x62\x71\x2c\x22\x22\x29\x29\x3d\x3d\
+\x3d\x22\x22\x29\x7b\x63\x2e\x72\x65\x6d\x6f\x76\x65\x41\x74\x74\
+\x72\x69\x62\x75\x74\x65\x28\x22\x66\x69\x6c\x74\x65\x72\x22\x29\
+\x3b\x69\x66\x28\x64\x26\x26\x21\x64\x2e\x66\x69\x6c\x74\x65\x72\
+\x29\x72\x65\x74\x75\x72\x6e\x7d\x63\x2e\x66\x69\x6c\x74\x65\x72\
+\x3d\x62\x71\x2e\x74\x65\x73\x74\x28\x67\x29\x3f\x67\x2e\x72\x65\
+\x70\x6c\x61\x63\x65\x28\x62\x71\x2c\x65\x29\x3a\x67\x2b\x22\x20\
+\x22\x2b\x65\x7d\x7d\x29\x2c\x66\x28\x66\x75\x6e\x63\x74\x69\x6f\
+\x6e\x28\x29\x7b\x66\x2e\x73\x75\x70\x70\x6f\x72\x74\x2e\x72\x65\
+\x6c\x69\x61\x62\x6c\x65\x4d\x61\x72\x67\x69\x6e\x52\x69\x67\x68\
+\x74\x7c\x7c\x28\x66\x2e\x63\x73\x73\x48\x6f\x6f\x6b\x73\x2e\x6d\
+\x61\x72\x67\x69\x6e\x52\x69\x67\x68\x74\x3d\x7b\x67\x65\x74\x3a\
+\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x29\x7b\x76\x61\
+\x72\x20\x63\x3b\x66\x2e\x73\x77\x61\x70\x28\x61\x2c\x7b\x64\x69\
+\x73\x70\x6c\x61\x79\x3a\x22\x69\x6e\x6c\x69\x6e\x65\x2d\x62\x6c\
+\x6f\x63\x6b\x22\x7d\x2c\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x29\
+\x7b\x62\x3f\x63\x3d\x62\x7a\x28\x61\x2c\x22\x6d\x61\x72\x67\x69\
+\x6e\x2d\x72\x69\x67\x68\x74\x22\x2c\x22\x6d\x61\x72\x67\x69\x6e\
+\x52\x69\x67\x68\x74\x22\x29\x3a\x63\x3d\x61\x2e\x73\x74\x79\x6c\
+\x65\x2e\x6d\x61\x72\x67\x69\x6e\x52\x69\x67\x68\x74\x7d\x29\x3b\
+\x72\x65\x74\x75\x72\x6e\x20\x63\x7d\x7d\x29\x7d\x29\x2c\x63\x2e\
+\x64\x65\x66\x61\x75\x6c\x74\x56\x69\x65\x77\x26\x26\x63\x2e\x64\
+\x65\x66\x61\x75\x6c\x74\x56\x69\x65\x77\x2e\x67\x65\x74\x43\x6f\
+\x6d\x70\x75\x74\x65\x64\x53\x74\x79\x6c\x65\x26\x26\x28\x62\x41\
+\x3d\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x29\x7b\x76\
+\x61\x72\x20\x63\x2c\x64\x2c\x65\x3b\x62\x3d\x62\x2e\x72\x65\x70\
+\x6c\x61\x63\x65\x28\x62\x73\x2c\x22\x2d\x24\x31\x22\x29\x2e\x74\
+\x6f\x4c\x6f\x77\x65\x72\x43\x61\x73\x65\x28\x29\x2c\x28\x64\x3d\
+\x61\x2e\x6f\x77\x6e\x65\x72\x44\x6f\x63\x75\x6d\x65\x6e\x74\x2e\
+\x64\x65\x66\x61\x75\x6c\x74\x56\x69\x65\x77\x29\x26\x26\x28\x65\
+\x3d\x64\x2e\x67\x65\x74\x43\x6f\x6d\x70\x75\x74\x65\x64\x53\x74\
+\x79\x6c\x65\x28\x61\x2c\x6e\x75\x6c\x6c\x29\x29\x26\x26\x28\x63\
+\x3d\x65\x2e\x67\x65\x74\x50\x72\x6f\x70\x65\x72\x74\x79\x56\x61\
+\x6c\x75\x65\x28\x62\x29\x2c\x63\x3d\x3d\x3d\x22\x22\x26\x26\x21\
+\x66\x2e\x63\x6f\x6e\x74\x61\x69\x6e\x73\x28\x61\x2e\x6f\x77\x6e\
+\x65\x72\x44\x6f\x63\x75\x6d\x65\x6e\x74\x2e\x64\x6f\x63\x75\x6d\
+\x65\x6e\x74\x45\x6c\x65\x6d\x65\x6e\x74\x2c\x61\x29\x26\x26\x28\
+\x63\x3d\x66\x2e\x73\x74\x79\x6c\x65\x28\x61\x2c\x62\x29\x29\x29\
+\x3b\x72\x65\x74\x75\x72\x6e\x20\x63\x7d\x29\x2c\x63\x2e\x64\x6f\
+\x63\x75\x6d\x65\x6e\x74\x45\x6c\x65\x6d\x65\x6e\x74\x2e\x63\x75\
+\x72\x72\x65\x6e\x74\x53\x74\x79\x6c\x65\x26\x26\x28\x62\x42\x3d\
+\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x29\x7b\x76\x61\
+\x72\x20\x63\x2c\x64\x2c\x65\x2c\x66\x3d\x61\x2e\x63\x75\x72\x72\
+\x65\x6e\x74\x53\x74\x79\x6c\x65\x26\x26\x61\x2e\x63\x75\x72\x72\
+\x65\x6e\x74\x53\x74\x79\x6c\x65\x5b\x62\x5d\x2c\x67\x3d\x61\x2e\
+\x73\x74\x79\x6c\x65\x3b\x66\x3d\x3d\x3d\x6e\x75\x6c\x6c\x26\x26\
+\x67\x26\x26\x28\x65\x3d\x67\x5b\x62\x5d\x29\x26\x26\x28\x66\x3d\
+\x65\x29\x2c\x21\x62\x74\x2e\x74\x65\x73\x74\x28\x66\x29\x26\x26\
+\x62\x75\x2e\x74\x65\x73\x74\x28\x66\x29\x26\x26\x28\x63\x3d\x67\
+\x2e\x6c\x65\x66\x74\x2c\x64\x3d\x61\x2e\x72\x75\x6e\x74\x69\x6d\
+\x65\x53\x74\x79\x6c\x65\x26\x26\x61\x2e\x72\x75\x6e\x74\x69\x6d\
+\x65\x53\x74\x79\x6c\x65\x2e\x6c\x65\x66\x74\x2c\x64\x26\x26\x28\
+\x61\x2e\x72\x75\x6e\x74\x69\x6d\x65\x53\x74\x79\x6c\x65\x2e\x6c\
+\x65\x66\x74\x3d\x61\x2e\x63\x75\x72\x72\x65\x6e\x74\x53\x74\x79\
+\x6c\x65\x2e\x6c\x65\x66\x74\x29\x2c\x67\x2e\x6c\x65\x66\x74\x3d\
+\x62\x3d\x3d\x3d\x22\x66\x6f\x6e\x74\x53\x69\x7a\x65\x22\x3f\x22\
+\x31\x65\x6d\x22\x3a\x66\x7c\x7c\x30\x2c\x66\x3d\x67\x2e\x70\x69\
+\x78\x65\x6c\x4c\x65\x66\x74\x2b\x22\x70\x78\x22\x2c\x67\x2e\x6c\
+\x65\x66\x74\x3d\x63\x2c\x64\x26\x26\x28\x61\x2e\x72\x75\x6e\x74\
+\x69\x6d\x65\x53\x74\x79\x6c\x65\x2e\x6c\x65\x66\x74\x3d\x64\x29\
+\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x66\x3d\x3d\x3d\x22\x22\x3f\
+\x22\x61\x75\x74\x6f\x22\x3a\x66\x7d\x29\x2c\x62\x7a\x3d\x62\x41\
+\x7c\x7c\x62\x42\x2c\x66\x2e\x65\x78\x70\x72\x26\x26\x66\x2e\x65\
+\x78\x70\x72\x2e\x66\x69\x6c\x74\x65\x72\x73\x26\x26\x28\x66\x2e\
+\x65\x78\x70\x72\x2e\x66\x69\x6c\x74\x65\x72\x73\x2e\x68\x69\x64\
+\x64\x65\x6e\x3d\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\
+\x76\x61\x72\x20\x62\x3d\x61\x2e\x6f\x66\x66\x73\x65\x74\x57\x69\
+\x64\x74\x68\x2c\x63\x3d\x61\x2e\x6f\x66\x66\x73\x65\x74\x48\x65\
+\x69\x67\x68\x74\x3b\x72\x65\x74\x75\x72\x6e\x20\x62\x3d\x3d\x3d\
+\x30\x26\x26\x63\x3d\x3d\x3d\x30\x7c\x7c\x21\x66\x2e\x73\x75\x70\
+\x70\x6f\x72\x74\x2e\x72\x65\x6c\x69\x61\x62\x6c\x65\x48\x69\x64\
+\x64\x65\x6e\x4f\x66\x66\x73\x65\x74\x73\x26\x26\x28\x61\x2e\x73\
+\x74\x79\x6c\x65\x26\x26\x61\x2e\x73\x74\x79\x6c\x65\x2e\x64\x69\
+\x73\x70\x6c\x61\x79\x7c\x7c\x66\x2e\x63\x73\x73\x28\x61\x2c\x22\
+\x64\x69\x73\x70\x6c\x61\x79\x22\x29\x29\x3d\x3d\x3d\x22\x6e\x6f\
+\x6e\x65\x22\x7d\x2c\x66\x2e\x65\x78\x70\x72\x2e\x66\x69\x6c\x74\
+\x65\x72\x73\x2e\x76\x69\x73\x69\x62\x6c\x65\x3d\x66\x75\x6e\x63\
+\x74\x69\x6f\x6e\x28\x61\x29\x7b\x72\x65\x74\x75\x72\x6e\x21\x66\
+\x2e\x65\x78\x70\x72\x2e\x66\x69\x6c\x74\x65\x72\x73\x2e\x68\x69\
+\x64\x64\x65\x6e\x28\x61\x29\x7d\x29\x3b\x76\x61\x72\x20\x62\x44\
+\x3d\x2f\x25\x32\x30\x2f\x67\x2c\x62\x45\x3d\x2f\x5c\x5b\x5c\x5d\
+\x24\x2f\x2c\x62\x46\x3d\x2f\x5c\x72\x3f\x5c\x6e\x2f\x67\x2c\x62\
+\x47\x3d\x2f\x23\x2e\x2a\x24\x2f\x2c\x62\x48\x3d\x2f\x5e\x28\x2e\
+\x2a\x3f\x29\x3a\x5b\x20\x5c\x74\x5d\x2a\x28\x5b\x5e\x5c\x72\x5c\
+\x6e\x5d\x2a\x29\x5c\x72\x3f\x24\x2f\x6d\x67\x2c\x62\x49\x3d\x2f\
+\x5e\x28\x3f\x3a\x63\x6f\x6c\x6f\x72\x7c\x64\x61\x74\x65\x7c\x64\
+\x61\x74\x65\x74\x69\x6d\x65\x7c\x64\x61\x74\x65\x74\x69\x6d\x65\
+\x2d\x6c\x6f\x63\x61\x6c\x7c\x65\x6d\x61\x69\x6c\x7c\x68\x69\x64\
+\x64\x65\x6e\x7c\x6d\x6f\x6e\x74\x68\x7c\x6e\x75\x6d\x62\x65\x72\
+\x7c\x70\x61\x73\x73\x77\x6f\x72\x64\x7c\x72\x61\x6e\x67\x65\x7c\
+\x73\x65\x61\x72\x63\x68\x7c\x74\x65\x6c\x7c\x74\x65\x78\x74\x7c\
+\x74\x69\x6d\x65\x7c\x75\x72\x6c\x7c\x77\x65\x65\x6b\x29\x24\x2f\
+\x69\x2c\x62\x4a\x3d\x2f\x5e\x28\x3f\x3a\x61\x62\x6f\x75\x74\x7c\
+\x61\x70\x70\x7c\x61\x70\x70\x5c\x2d\x73\x74\x6f\x72\x61\x67\x65\
+\x7c\x2e\x2b\x5c\x2d\x65\x78\x74\x65\x6e\x73\x69\x6f\x6e\x7c\x66\
+\x69\x6c\x65\x7c\x72\x65\x73\x7c\x77\x69\x64\x67\x65\x74\x29\x3a\
+\x24\x2f\x2c\x62\x4b\x3d\x2f\x5e\x28\x3f\x3a\x47\x45\x54\x7c\x48\
+\x45\x41\x44\x29\x24\x2f\x2c\x62\x4c\x3d\x2f\x5e\x5c\x2f\x5c\x2f\
+\x2f\x2c\x62\x4d\x3d\x2f\x5c\x3f\x2f\x2c\x62\x4e\x3d\x2f\x3c\x73\
+\x63\x72\x69\x70\x74\x5c\x62\x5b\x5e\x3c\x5d\x2a\x28\x3f\x3a\x28\
+\x3f\x21\x3c\x5c\x2f\x73\x63\x72\x69\x70\x74\x3e\x29\x3c\x5b\x5e\
+\x3c\x5d\x2a\x29\x2a\x3c\x5c\x2f\x73\x63\x72\x69\x70\x74\x3e\x2f\
+\x67\x69\x2c\x62\x4f\x3d\x2f\x5e\x28\x3f\x3a\x73\x65\x6c\x65\x63\
+\x74\x7c\x74\x65\x78\x74\x61\x72\x65\x61\x29\x2f\x69\x2c\x62\x50\
+\x3d\x2f\x5c\x73\x2b\x2f\x2c\x62\x51\x3d\x2f\x28\x5b\x3f\x26\x5d\
+\x29\x5f\x3d\x5b\x5e\x26\x5d\x2a\x2f\x2c\x62\x52\x3d\x2f\x5e\x28\
+\x5b\x5c\x77\x5c\x2b\x5c\x2e\x5c\x2d\x5d\x2b\x3a\x29\x28\x3f\x3a\
+\x5c\x2f\x5c\x2f\x28\x5b\x5e\x5c\x2f\x3f\x23\x3a\x5d\x2a\x29\x28\
+\x3f\x3a\x3a\x28\x5c\x64\x2b\x29\x29\x3f\x29\x3f\x2f\x2c\x62\x53\
+\x3d\x66\x2e\x66\x6e\x2e\x6c\x6f\x61\x64\x2c\x62\x54\x3d\x7b\x7d\
+\x2c\x62\x55\x3d\x7b\x7d\x2c\x62\x56\x2c\x62\x57\x2c\x62\x58\x3d\
+\x5b\x22\x2a\x2f\x22\x5d\x2b\x5b\x22\x2a\x22\x5d\x3b\x74\x72\x79\
+\x7b\x62\x56\x3d\x65\x2e\x68\x72\x65\x66\x7d\x63\x61\x74\x63\x68\
+\x28\x62\x59\x29\x7b\x62\x56\x3d\x63\x2e\x63\x72\x65\x61\x74\x65\
+\x45\x6c\x65\x6d\x65\x6e\x74\x28\x22\x61\x22\x29\x2c\x62\x56\x2e\
+\x68\x72\x65\x66\x3d\x22\x22\x2c\x62\x56\x3d\x62\x56\x2e\x68\x72\
+\x65\x66\x7d\x62\x57\x3d\x62\x52\x2e\x65\x78\x65\x63\x28\x62\x56\
+\x2e\x74\x6f\x4c\x6f\x77\x65\x72\x43\x61\x73\x65\x28\x29\x29\x7c\
+\x7c\x5b\x5d\x2c\x66\x2e\x66\x6e\x2e\x65\x78\x74\x65\x6e\x64\x28\
+\x7b\x6c\x6f\x61\x64\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\
+\x2c\x63\x2c\x64\x29\x7b\x69\x66\x28\x74\x79\x70\x65\x6f\x66\x20\
+\x61\x21\x3d\x22\x73\x74\x72\x69\x6e\x67\x22\x26\x26\x62\x53\x29\
+\x72\x65\x74\x75\x72\x6e\x20\x62\x53\x2e\x61\x70\x70\x6c\x79\x28\
+\x74\x68\x69\x73\x2c\x61\x72\x67\x75\x6d\x65\x6e\x74\x73\x29\x3b\
+\x69\x66\x28\x21\x74\x68\x69\x73\x2e\x6c\x65\x6e\x67\x74\x68\x29\
+\x72\x65\x74\x75\x72\x6e\x20\x74\x68\x69\x73\x3b\x76\x61\x72\x20\
+\x65\x3d\x61\x2e\x69\x6e\x64\x65\x78\x4f\x66\x28\x22\x20\x22\x29\
+\x3b\x69\x66\x28\x65\x3e\x3d\x30\x29\x7b\x76\x61\x72\x20\x67\x3d\
+\x61\x2e\x73\x6c\x69\x63\x65\x28\x65\x2c\x61\x2e\x6c\x65\x6e\x67\
+\x74\x68\x29\x3b\x61\x3d\x61\x2e\x73\x6c\x69\x63\x65\x28\x30\x2c\
+\x65\x29\x7d\x76\x61\x72\x20\x68\x3d\x22\x47\x45\x54\x22\x3b\x63\
+\x26\x26\x28\x66\x2e\x69\x73\x46\x75\x6e\x63\x74\x69\x6f\x6e\x28\
+\x63\x29\x3f\x28\x64\x3d\x63\x2c\x63\x3d\x62\x29\x3a\x74\x79\x70\
+\x65\x6f\x66\x20\x63\x3d\x3d\x22\x6f\x62\x6a\x65\x63\x74\x22\x26\
+\x26\x28\x63\x3d\x66\x2e\x70\x61\x72\x61\x6d\x28\x63\x2c\x66\x2e\
+\x61\x6a\x61\x78\x53\x65\x74\x74\x69\x6e\x67\x73\x2e\x74\x72\x61\
+\x64\x69\x74\x69\x6f\x6e\x61\x6c\x29\x2c\x68\x3d\x22\x50\x4f\x53\
+\x54\x22\x29\x29\x3b\x76\x61\x72\x20\x69\x3d\x74\x68\x69\x73\x3b\
+\x66\x2e\x61\x6a\x61\x78\x28\x7b\x75\x72\x6c\x3a\x61\x2c\x74\x79\
+\x70\x65\x3a\x68\x2c\x64\x61\x74\x61\x54\x79\x70\x65\x3a\x22\x68\
+\x74\x6d\x6c\x22\x2c\x64\x61\x74\x61\x3a\x63\x2c\x63\x6f\x6d\x70\
+\x6c\x65\x74\x65\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\
+\x62\x2c\x63\x29\x7b\x63\x3d\x61\x2e\x72\x65\x73\x70\x6f\x6e\x73\
+\x65\x54\x65\x78\x74\x2c\x61\x2e\x69\x73\x52\x65\x73\x6f\x6c\x76\
+\x65\x64\x28\x29\x26\x26\x28\x61\x2e\x64\x6f\x6e\x65\x28\x66\x75\
+\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x63\x3d\x61\x7d\x29\x2c\
+\x69\x2e\x68\x74\x6d\x6c\x28\x67\x3f\x66\x28\x22\x3c\x64\x69\x76\
+\x3e\x22\x29\x2e\x61\x70\x70\x65\x6e\x64\x28\x63\x2e\x72\x65\x70\
+\x6c\x61\x63\x65\x28\x62\x4e\x2c\x22\x22\x29\x29\x2e\x66\x69\x6e\
+\x64\x28\x67\x29\x3a\x63\x29\x29\x2c\x64\x26\x26\x69\x2e\x65\x61\
+\x63\x68\x28\x64\x2c\x5b\x63\x2c\x62\x2c\x61\x5d\x29\x7d\x7d\x29\
+\x3b\x72\x65\x74\x75\x72\x6e\x20\x74\x68\x69\x73\x7d\x2c\x73\x65\
+\x72\x69\x61\x6c\x69\x7a\x65\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\
+\x28\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x66\x2e\x70\x61\x72\x61\
+\x6d\x28\x74\x68\x69\x73\x2e\x73\x65\x72\x69\x61\x6c\x69\x7a\x65\
+\x41\x72\x72\x61\x79\x28\x29\x29\x7d\x2c\x73\x65\x72\x69\x61\x6c\
+\x69\x7a\x65\x41\x72\x72\x61\x79\x3a\x66\x75\x6e\x63\x74\x69\x6f\
+\x6e\x28\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x74\x68\x69\x73\x2e\
+\x6d\x61\x70\x28\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x29\x7b\x72\
+\x65\x74\x75\x72\x6e\x20\x74\x68\x69\x73\x2e\x65\x6c\x65\x6d\x65\
+\x6e\x74\x73\x3f\x66\x2e\x6d\x61\x6b\x65\x41\x72\x72\x61\x79\x28\
+\x74\x68\x69\x73\x2e\x65\x6c\x65\x6d\x65\x6e\x74\x73\x29\x3a\x74\
+\x68\x69\x73\x7d\x29\x2e\x66\x69\x6c\x74\x65\x72\x28\x66\x75\x6e\
+\x63\x74\x69\x6f\x6e\x28\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x74\
+\x68\x69\x73\x2e\x6e\x61\x6d\x65\x26\x26\x21\x74\x68\x69\x73\x2e\
+\x64\x69\x73\x61\x62\x6c\x65\x64\x26\x26\x28\x74\x68\x69\x73\x2e\
+\x63\x68\x65\x63\x6b\x65\x64\x7c\x7c\x62\x4f\x2e\x74\x65\x73\x74\
+\x28\x74\x68\x69\x73\x2e\x6e\x6f\x64\x65\x4e\x61\x6d\x65\x29\x7c\
+\x7c\x62\x49\x2e\x74\x65\x73\x74\x28\x74\x68\x69\x73\x2e\x74\x79\
+\x70\x65\x29\x29\x7d\x29\x2e\x6d\x61\x70\x28\x66\x75\x6e\x63\x74\
+\x69\x6f\x6e\x28\x61\x2c\x62\x29\x7b\x76\x61\x72\x20\x63\x3d\x66\
+\x28\x74\x68\x69\x73\x29\x2e\x76\x61\x6c\x28\x29\x3b\x72\x65\x74\
+\x75\x72\x6e\x20\x63\x3d\x3d\x6e\x75\x6c\x6c\x3f\x6e\x75\x6c\x6c\
+\x3a\x66\x2e\x69\x73\x41\x72\x72\x61\x79\x28\x63\x29\x3f\x66\x2e\
+\x6d\x61\x70\x28\x63\x2c\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\
+\x2c\x63\x29\x7b\x72\x65\x74\x75\x72\x6e\x7b\x6e\x61\x6d\x65\x3a\
+\x62\x2e\x6e\x61\x6d\x65\x2c\x76\x61\x6c\x75\x65\x3a\x61\x2e\x72\
+\x65\x70\x6c\x61\x63\x65\x28\x62\x46\x2c\x22\x5c\x72\x5c\x6e\x22\
+\x29\x7d\x7d\x29\x3a\x7b\x6e\x61\x6d\x65\x3a\x62\x2e\x6e\x61\x6d\
+\x65\x2c\x76\x61\x6c\x75\x65\x3a\x63\x2e\x72\x65\x70\x6c\x61\x63\
+\x65\x28\x62\x46\x2c\x22\x5c\x72\x5c\x6e\x22\x29\x7d\x7d\x29\x2e\
+\x67\x65\x74\x28\x29\x7d\x7d\x29\x2c\x66\x2e\x65\x61\x63\x68\x28\
+\x22\x61\x6a\x61\x78\x53\x74\x61\x72\x74\x20\x61\x6a\x61\x78\x53\
+\x74\x6f\x70\x20\x61\x6a\x61\x78\x43\x6f\x6d\x70\x6c\x65\x74\x65\
+\x20\x61\x6a\x61\x78\x45\x72\x72\x6f\x72\x20\x61\x6a\x61\x78\x53\
+\x75\x63\x63\x65\x73\x73\x20\x61\x6a\x61\x78\x53\x65\x6e\x64\x22\
+\x2e\x73\x70\x6c\x69\x74\x28\x22\x20\x22\x29\x2c\x66\x75\x6e\x63\
+\x74\x69\x6f\x6e\x28\x61\x2c\x62\x29\x7b\x66\x2e\x66\x6e\x5b\x62\
+\x5d\x3d\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x72\x65\
+\x74\x75\x72\x6e\x20\x74\x68\x69\x73\x2e\x6f\x6e\x28\x62\x2c\x61\
+\x29\x7d\x7d\x29\x2c\x66\x2e\x65\x61\x63\x68\x28\x5b\x22\x67\x65\
+\x74\x22\x2c\x22\x70\x6f\x73\x74\x22\x5d\x2c\x66\x75\x6e\x63\x74\
+\x69\x6f\x6e\x28\x61\x2c\x63\x29\x7b\x66\x5b\x63\x5d\x3d\x66\x75\
+\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x64\x2c\x65\x2c\x67\x29\x7b\
+\x66\x2e\x69\x73\x46\x75\x6e\x63\x74\x69\x6f\x6e\x28\x64\x29\x26\
+\x26\x28\x67\x3d\x67\x7c\x7c\x65\x2c\x65\x3d\x64\x2c\x64\x3d\x62\
+\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x66\x2e\x61\x6a\x61\x78\x28\
+\x7b\x74\x79\x70\x65\x3a\x63\x2c\x75\x72\x6c\x3a\x61\x2c\x64\x61\
+\x74\x61\x3a\x64\x2c\x73\x75\x63\x63\x65\x73\x73\x3a\x65\x2c\x64\
+\x61\x74\x61\x54\x79\x70\x65\x3a\x67\x7d\x29\x7d\x7d\x29\x2c\x66\
+\x2e\x65\x78\x74\x65\x6e\x64\x28\x7b\x67\x65\x74\x53\x63\x72\x69\
+\x70\x74\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x63\x29\
+\x7b\x72\x65\x74\x75\x72\x6e\x20\x66\x2e\x67\x65\x74\x28\x61\x2c\
+\x62\x2c\x63\x2c\x22\x73\x63\x72\x69\x70\x74\x22\x29\x7d\x2c\x67\
+\x65\x74\x4a\x53\x4f\x4e\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\
+\x61\x2c\x62\x2c\x63\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x66\x2e\
+\x67\x65\x74\x28\x61\x2c\x62\x2c\x63\x2c\x22\x6a\x73\x6f\x6e\x22\
+\x29\x7d\x2c\x61\x6a\x61\x78\x53\x65\x74\x75\x70\x3a\x66\x75\x6e\
+\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x29\x7b\x62\x3f\x62\x5f\x28\
+\x61\x2c\x66\x2e\x61\x6a\x61\x78\x53\x65\x74\x74\x69\x6e\x67\x73\
+\x29\x3a\x28\x62\x3d\x61\x2c\x61\x3d\x66\x2e\x61\x6a\x61\x78\x53\
+\x65\x74\x74\x69\x6e\x67\x73\x29\x2c\x62\x5f\x28\x61\x2c\x62\x29\
+\x3b\x72\x65\x74\x75\x72\x6e\x20\x61\x7d\x2c\x61\x6a\x61\x78\x53\
+\x65\x74\x74\x69\x6e\x67\x73\x3a\x7b\x75\x72\x6c\x3a\x62\x56\x2c\
+\x69\x73\x4c\x6f\x63\x61\x6c\x3a\x62\x4a\x2e\x74\x65\x73\x74\x28\
+\x62\x57\x5b\x31\x5d\x29\x2c\x67\x6c\x6f\x62\x61\x6c\x3a\x21\x30\
+\x2c\x74\x79\x70\x65\x3a\x22\x47\x45\x54\x22\x2c\x63\x6f\x6e\x74\
+\x65\x6e\x74\x54\x79\x70\x65\x3a\x22\x61\x70\x70\x6c\x69\x63\x61\
+\x74\x69\x6f\x6e\x2f\x78\x2d\x77\x77\x77\x2d\x66\x6f\x72\x6d\x2d\
+\x75\x72\x6c\x65\x6e\x63\x6f\x64\x65\x64\x22\x2c\x70\x72\x6f\x63\
+\x65\x73\x73\x44\x61\x74\x61\x3a\x21\x30\x2c\x61\x73\x79\x6e\x63\
+\x3a\x21\x30\x2c\x61\x63\x63\x65\x70\x74\x73\x3a\x7b\x78\x6d\x6c\
+\x3a\x22\x61\x70\x70\x6c\x69\x63\x61\x74\x69\x6f\x6e\x2f\x78\x6d\
+\x6c\x2c\x20\x74\x65\x78\x74\x2f\x78\x6d\x6c\x22\x2c\x68\x74\x6d\
+\x6c\x3a\x22\x74\x65\x78\x74\x2f\x68\x74\x6d\x6c\x22\x2c\x74\x65\
+\x78\x74\x3a\x22\x74\x65\x78\x74\x2f\x70\x6c\x61\x69\x6e\x22\x2c\
+\x6a\x73\x6f\x6e\x3a\x22\x61\x70\x70\x6c\x69\x63\x61\x74\x69\x6f\
+\x6e\x2f\x6a\x73\x6f\x6e\x2c\x20\x74\x65\x78\x74\x2f\x6a\x61\x76\
+\x61\x73\x63\x72\x69\x70\x74\x22\x2c\x22\x2a\x22\x3a\x62\x58\x7d\
+\x2c\x63\x6f\x6e\x74\x65\x6e\x74\x73\x3a\x7b\x78\x6d\x6c\x3a\x2f\
+\x78\x6d\x6c\x2f\x2c\x68\x74\x6d\x6c\x3a\x2f\x68\x74\x6d\x6c\x2f\
+\x2c\x6a\x73\x6f\x6e\x3a\x2f\x6a\x73\x6f\x6e\x2f\x7d\x2c\x72\x65\
+\x73\x70\x6f\x6e\x73\x65\x46\x69\x65\x6c\x64\x73\x3a\x7b\x78\x6d\
+\x6c\x3a\x22\x72\x65\x73\x70\x6f\x6e\x73\x65\x58\x4d\x4c\x22\x2c\
+\x74\x65\x78\x74\x3a\x22\x72\x65\x73\x70\x6f\x6e\x73\x65\x54\x65\
+\x78\x74\x22\x7d\x2c\x63\x6f\x6e\x76\x65\x72\x74\x65\x72\x73\x3a\
+\x7b\x22\x2a\x20\x74\x65\x78\x74\x22\x3a\x61\x2e\x53\x74\x72\x69\
+\x6e\x67\x2c\x22\x74\x65\x78\x74\x20\x68\x74\x6d\x6c\x22\x3a\x21\
+\x30\x2c\x22\x74\x65\x78\x74\x20\x6a\x73\x6f\x6e\x22\x3a\x66\x2e\
+\x70\x61\x72\x73\x65\x4a\x53\x4f\x4e\x2c\x22\x74\x65\x78\x74\x20\
+\x78\x6d\x6c\x22\x3a\x66\x2e\x70\x61\x72\x73\x65\x58\x4d\x4c\x7d\
+\x2c\x66\x6c\x61\x74\x4f\x70\x74\x69\x6f\x6e\x73\x3a\x7b\x63\x6f\
+\x6e\x74\x65\x78\x74\x3a\x21\x30\x2c\x75\x72\x6c\x3a\x21\x30\x7d\
+\x7d\x2c\x61\x6a\x61\x78\x50\x72\x65\x66\x69\x6c\x74\x65\x72\x3a\
+\x62\x5a\x28\x62\x54\x29\x2c\x61\x6a\x61\x78\x54\x72\x61\x6e\x73\
+\x70\x6f\x72\x74\x3a\x62\x5a\x28\x62\x55\x29\x2c\x61\x6a\x61\x78\
+\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x63\x29\x7b\x66\
+\x75\x6e\x63\x74\x69\x6f\x6e\x20\x77\x28\x61\x2c\x63\x2c\x6c\x2c\
+\x6d\x29\x7b\x69\x66\x28\x73\x21\x3d\x3d\x32\x29\x7b\x73\x3d\x32\
+\x2c\x71\x26\x26\x63\x6c\x65\x61\x72\x54\x69\x6d\x65\x6f\x75\x74\
+\x28\x71\x29\x2c\x70\x3d\x62\x2c\x6e\x3d\x6d\x7c\x7c\x22\x22\x2c\
+\x76\x2e\x72\x65\x61\x64\x79\x53\x74\x61\x74\x65\x3d\x61\x3e\x30\
+\x3f\x34\x3a\x30\x3b\x76\x61\x72\x20\x6f\x2c\x72\x2c\x75\x2c\x77\
+\x3d\x63\x2c\x78\x3d\x6c\x3f\x63\x62\x28\x64\x2c\x76\x2c\x6c\x29\
+\x3a\x62\x2c\x79\x2c\x7a\x3b\x69\x66\x28\x61\x3e\x3d\x32\x30\x30\
+\x26\x26\x61\x3c\x33\x30\x30\x7c\x7c\x61\x3d\x3d\x3d\x33\x30\x34\
+\x29\x7b\x69\x66\x28\x64\x2e\x69\x66\x4d\x6f\x64\x69\x66\x69\x65\
+\x64\x29\x7b\x69\x66\x28\x79\x3d\x76\x2e\x67\x65\x74\x52\x65\x73\
+\x70\x6f\x6e\x73\x65\x48\x65\x61\x64\x65\x72\x28\x22\x4c\x61\x73\
+\x74\x2d\x4d\x6f\x64\x69\x66\x69\x65\x64\x22\x29\x29\x66\x2e\x6c\
+\x61\x73\x74\x4d\x6f\x64\x69\x66\x69\x65\x64\x5b\x6b\x5d\x3d\x79\
+\x3b\x69\x66\x28\x7a\x3d\x76\x2e\x67\x65\x74\x52\x65\x73\x70\x6f\
+\x6e\x73\x65\x48\x65\x61\x64\x65\x72\x28\x22\x45\x74\x61\x67\x22\
+\x29\x29\x66\x2e\x65\x74\x61\x67\x5b\x6b\x5d\x3d\x7a\x7d\x69\x66\
+\x28\x61\x3d\x3d\x3d\x33\x30\x34\x29\x77\x3d\x22\x6e\x6f\x74\x6d\
+\x6f\x64\x69\x66\x69\x65\x64\x22\x2c\x6f\x3d\x21\x30\x3b\x65\x6c\
+\x73\x65\x20\x74\x72\x79\x7b\x72\x3d\x63\x63\x28\x64\x2c\x78\x29\
+\x2c\x77\x3d\x22\x73\x75\x63\x63\x65\x73\x73\x22\x2c\x6f\x3d\x21\
+\x30\x7d\x63\x61\x74\x63\x68\x28\x41\x29\x7b\x77\x3d\x22\x70\x61\
+\x72\x73\x65\x72\x65\x72\x72\x6f\x72\x22\x2c\x75\x3d\x41\x7d\x7d\
+\x65\x6c\x73\x65\x7b\x75\x3d\x77\x3b\x69\x66\x28\x21\x77\x7c\x7c\
+\x61\x29\x77\x3d\x22\x65\x72\x72\x6f\x72\x22\x2c\x61\x3c\x30\x26\
+\x26\x28\x61\x3d\x30\x29\x7d\x76\x2e\x73\x74\x61\x74\x75\x73\x3d\
+\x61\x2c\x76\x2e\x73\x74\x61\x74\x75\x73\x54\x65\x78\x74\x3d\x22\
+\x22\x2b\x28\x63\x7c\x7c\x77\x29\x2c\x6f\x3f\x68\x2e\x72\x65\x73\
+\x6f\x6c\x76\x65\x57\x69\x74\x68\x28\x65\x2c\x5b\x72\x2c\x77\x2c\
+\x76\x5d\x29\x3a\x68\x2e\x72\x65\x6a\x65\x63\x74\x57\x69\x74\x68\
+\x28\x65\x2c\x5b\x76\x2c\x77\x2c\x75\x5d\x29\x2c\x76\x2e\x73\x74\
+\x61\x74\x75\x73\x43\x6f\x64\x65\x28\x6a\x29\x2c\x6a\x3d\x62\x2c\
+\x74\x26\x26\x67\x2e\x74\x72\x69\x67\x67\x65\x72\x28\x22\x61\x6a\
+\x61\x78\x22\x2b\x28\x6f\x3f\x22\x53\x75\x63\x63\x65\x73\x73\x22\
+\x3a\x22\x45\x72\x72\x6f\x72\x22\x29\x2c\x5b\x76\x2c\x64\x2c\x6f\
+\x3f\x72\x3a\x75\x5d\x29\x2c\x69\x2e\x66\x69\x72\x65\x57\x69\x74\
+\x68\x28\x65\x2c\x5b\x76\x2c\x77\x5d\x29\x2c\x74\x26\x26\x28\x67\
+\x2e\x74\x72\x69\x67\x67\x65\x72\x28\x22\x61\x6a\x61\x78\x43\x6f\
+\x6d\x70\x6c\x65\x74\x65\x22\x2c\x5b\x76\x2c\x64\x5d\x29\x2c\x2d\
+\x2d\x66\x2e\x61\x63\x74\x69\x76\x65\x7c\x7c\x66\x2e\x65\x76\x65\
+\x6e\x74\x2e\x74\x72\x69\x67\x67\x65\x72\x28\x22\x61\x6a\x61\x78\
+\x53\x74\x6f\x70\x22\x29\x29\x7d\x7d\x74\x79\x70\x65\x6f\x66\x20\
+\x61\x3d\x3d\x22\x6f\x62\x6a\x65\x63\x74\x22\x26\x26\x28\x63\x3d\
+\x61\x2c\x61\x3d\x62\x29\x2c\x63\x3d\x63\x7c\x7c\x7b\x7d\x3b\x76\
+\x61\x72\x20\x64\x3d\x66\x2e\x61\x6a\x61\x78\x53\x65\x74\x75\x70\
+\x28\x7b\x7d\x2c\x63\x29\x2c\x65\x3d\x64\x2e\x63\x6f\x6e\x74\x65\
+\x78\x74\x7c\x7c\x64\x2c\x67\x3d\x65\x21\x3d\x3d\x64\x26\x26\x28\
+\x65\x2e\x6e\x6f\x64\x65\x54\x79\x70\x65\x7c\x7c\x65\x20\x69\x6e\
+\x73\x74\x61\x6e\x63\x65\x6f\x66\x20\x66\x29\x3f\x66\x28\x65\x29\
+\x3a\x66\x2e\x65\x76\x65\x6e\x74\x2c\x68\x3d\x66\x2e\x44\x65\x66\
+\x65\x72\x72\x65\x64\x28\x29\x2c\x69\x3d\x66\x2e\x43\x61\x6c\x6c\
+\x62\x61\x63\x6b\x73\x28\x22\x6f\x6e\x63\x65\x20\x6d\x65\x6d\x6f\
+\x72\x79\x22\x29\x2c\x6a\x3d\x64\x2e\x73\x74\x61\x74\x75\x73\x43\
+\x6f\x64\x65\x7c\x7c\x7b\x7d\x2c\x6b\x2c\x6c\x3d\x7b\x7d\x2c\x6d\
+\x3d\x7b\x7d\x2c\x6e\x2c\x6f\x2c\x70\x2c\x71\x2c\x72\x2c\x73\x3d\
+\x30\x2c\x74\x2c\x75\x2c\x76\x3d\x7b\x72\x65\x61\x64\x79\x53\x74\
+\x61\x74\x65\x3a\x30\x2c\x73\x65\x74\x52\x65\x71\x75\x65\x73\x74\
+\x48\x65\x61\x64\x65\x72\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\
+\x61\x2c\x62\x29\x7b\x69\x66\x28\x21\x73\x29\x7b\x76\x61\x72\x20\
+\x63\x3d\x61\x2e\x74\x6f\x4c\x6f\x77\x65\x72\x43\x61\x73\x65\x28\
+\x29\x3b\x61\x3d\x6d\x5b\x63\x5d\x3d\x6d\x5b\x63\x5d\x7c\x7c\x61\
+\x2c\x6c\x5b\x61\x5d\x3d\x62\x7d\x72\x65\x74\x75\x72\x6e\x20\x74\
+\x68\x69\x73\x7d\x2c\x67\x65\x74\x41\x6c\x6c\x52\x65\x73\x70\x6f\
+\x6e\x73\x65\x48\x65\x61\x64\x65\x72\x73\x3a\x66\x75\x6e\x63\x74\
+\x69\x6f\x6e\x28\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x73\x3d\x3d\
+\x3d\x32\x3f\x6e\x3a\x6e\x75\x6c\x6c\x7d\x2c\x67\x65\x74\x52\x65\
+\x73\x70\x6f\x6e\x73\x65\x48\x65\x61\x64\x65\x72\x3a\x66\x75\x6e\
+\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x76\x61\x72\x20\x63\x3b\x69\
+\x66\x28\x73\x3d\x3d\x3d\x32\x29\x7b\x69\x66\x28\x21\x6f\x29\x7b\
+\x6f\x3d\x7b\x7d\x3b\x77\x68\x69\x6c\x65\x28\x63\x3d\x62\x48\x2e\
+\x65\x78\x65\x63\x28\x6e\x29\x29\x6f\x5b\x63\x5b\x31\x5d\x2e\x74\
+\x6f\x4c\x6f\x77\x65\x72\x43\x61\x73\x65\x28\x29\x5d\x3d\x63\x5b\
+\x32\x5d\x7d\x63\x3d\x6f\x5b\x61\x2e\x74\x6f\x4c\x6f\x77\x65\x72\
+\x43\x61\x73\x65\x28\x29\x5d\x7d\x72\x65\x74\x75\x72\x6e\x20\x63\
+\x3d\x3d\x3d\x62\x3f\x6e\x75\x6c\x6c\x3a\x63\x7d\x2c\x6f\x76\x65\
+\x72\x72\x69\x64\x65\x4d\x69\x6d\x65\x54\x79\x70\x65\x3a\x66\x75\
+\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x73\x7c\x7c\x28\x64\x2e\
+\x6d\x69\x6d\x65\x54\x79\x70\x65\x3d\x61\x29\x3b\x72\x65\x74\x75\
+\x72\x6e\x20\x74\x68\x69\x73\x7d\x2c\x61\x62\x6f\x72\x74\x3a\x66\
+\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x61\x3d\x61\x7c\x7c\
+\x22\x61\x62\x6f\x72\x74\x22\x2c\x70\x26\x26\x70\x2e\x61\x62\x6f\
+\x72\x74\x28\x61\x29\x2c\x77\x28\x30\x2c\x61\x29\x3b\x72\x65\x74\
+\x75\x72\x6e\x20\x74\x68\x69\x73\x7d\x7d\x3b\x68\x2e\x70\x72\x6f\
+\x6d\x69\x73\x65\x28\x76\x29\x2c\x76\x2e\x73\x75\x63\x63\x65\x73\
+\x73\x3d\x76\x2e\x64\x6f\x6e\x65\x2c\x76\x2e\x65\x72\x72\x6f\x72\
+\x3d\x76\x2e\x66\x61\x69\x6c\x2c\x76\x2e\x63\x6f\x6d\x70\x6c\x65\
+\x74\x65\x3d\x69\x2e\x61\x64\x64\x2c\x76\x2e\x73\x74\x61\x74\x75\
+\x73\x43\x6f\x64\x65\x3d\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\
+\x29\x7b\x69\x66\x28\x61\x29\x7b\x76\x61\x72\x20\x62\x3b\x69\x66\
+\x28\x73\x3c\x32\x29\x66\x6f\x72\x28\x62\x20\x69\x6e\x20\x61\x29\
+\x6a\x5b\x62\x5d\x3d\x5b\x6a\x5b\x62\x5d\x2c\x61\x5b\x62\x5d\x5d\
+\x3b\x65\x6c\x73\x65\x20\x62\x3d\x61\x5b\x76\x2e\x73\x74\x61\x74\
+\x75\x73\x5d\x2c\x76\x2e\x74\x68\x65\x6e\x28\x62\x2c\x62\x29\x7d\
+\x72\x65\x74\x75\x72\x6e\x20\x74\x68\x69\x73\x7d\x2c\x64\x2e\x75\
+\x72\x6c\x3d\x28\x28\x61\x7c\x7c\x64\x2e\x75\x72\x6c\x29\x2b\x22\
+\x22\x29\x2e\x72\x65\x70\x6c\x61\x63\x65\x28\x62\x47\x2c\x22\x22\
+\x29\x2e\x72\x65\x70\x6c\x61\x63\x65\x28\x62\x4c\x2c\x62\x57\x5b\
+\x31\x5d\x2b\x22\x2f\x2f\x22\x29\x2c\x64\x2e\x64\x61\x74\x61\x54\
+\x79\x70\x65\x73\x3d\x66\x2e\x74\x72\x69\x6d\x28\x64\x2e\x64\x61\
+\x74\x61\x54\x79\x70\x65\x7c\x7c\x22\x2a\x22\x29\x2e\x74\x6f\x4c\
+\x6f\x77\x65\x72\x43\x61\x73\x65\x28\x29\x2e\x73\x70\x6c\x69\x74\
+\x28\x62\x50\x29\x2c\x64\x2e\x63\x72\x6f\x73\x73\x44\x6f\x6d\x61\
+\x69\x6e\x3d\x3d\x6e\x75\x6c\x6c\x26\x26\x28\x72\x3d\x62\x52\x2e\
+\x65\x78\x65\x63\x28\x64\x2e\x75\x72\x6c\x2e\x74\x6f\x4c\x6f\x77\
+\x65\x72\x43\x61\x73\x65\x28\x29\x29\x2c\x64\x2e\x63\x72\x6f\x73\
+\x73\x44\x6f\x6d\x61\x69\x6e\x3d\x21\x28\x21\x72\x7c\x7c\x72\x5b\
+\x31\x5d\x3d\x3d\x62\x57\x5b\x31\x5d\x26\x26\x72\x5b\x32\x5d\x3d\
+\x3d\x62\x57\x5b\x32\x5d\x26\x26\x28\x72\x5b\x33\x5d\x7c\x7c\x28\
+\x72\x5b\x31\x5d\x3d\x3d\x3d\x22\x68\x74\x74\x70\x3a\x22\x3f\x38\
+\x30\x3a\x34\x34\x33\x29\x29\x3d\x3d\x28\x62\x57\x5b\x33\x5d\x7c\
+\x7c\x28\x62\x57\x5b\x31\x5d\x3d\x3d\x3d\x22\x68\x74\x74\x70\x3a\
+\x22\x3f\x38\x30\x3a\x34\x34\x33\x29\x29\x29\x29\x2c\x64\x2e\x64\
+\x61\x74\x61\x26\x26\x64\x2e\x70\x72\x6f\x63\x65\x73\x73\x44\x61\
+\x74\x61\x26\x26\x74\x79\x70\x65\x6f\x66\x20\x64\x2e\x64\x61\x74\
+\x61\x21\x3d\x22\x73\x74\x72\x69\x6e\x67\x22\x26\x26\x28\x64\x2e\
+\x64\x61\x74\x61\x3d\x66\x2e\x70\x61\x72\x61\x6d\x28\x64\x2e\x64\
+\x61\x74\x61\x2c\x64\x2e\x74\x72\x61\x64\x69\x74\x69\x6f\x6e\x61\
+\x6c\x29\x29\x2c\x62\x24\x28\x62\x54\x2c\x64\x2c\x63\x2c\x76\x29\
+\x3b\x69\x66\x28\x73\x3d\x3d\x3d\x32\x29\x72\x65\x74\x75\x72\x6e\
+\x21\x31\x3b\x74\x3d\x64\x2e\x67\x6c\x6f\x62\x61\x6c\x2c\x64\x2e\
+\x74\x79\x70\x65\x3d\x64\x2e\x74\x79\x70\x65\x2e\x74\x6f\x55\x70\
+\x70\x65\x72\x43\x61\x73\x65\x28\x29\x2c\x64\x2e\x68\x61\x73\x43\
+\x6f\x6e\x74\x65\x6e\x74\x3d\x21\x62\x4b\x2e\x74\x65\x73\x74\x28\
+\x64\x2e\x74\x79\x70\x65\x29\x2c\x74\x26\x26\x66\x2e\x61\x63\x74\
+\x69\x76\x65\x2b\x2b\x3d\x3d\x3d\x30\x26\x26\x66\x2e\x65\x76\x65\
+\x6e\x74\x2e\x74\x72\x69\x67\x67\x65\x72\x28\x22\x61\x6a\x61\x78\
+\x53\x74\x61\x72\x74\x22\x29\x3b\x69\x66\x28\x21\x64\x2e\x68\x61\
+\x73\x43\x6f\x6e\x74\x65\x6e\x74\x29\x7b\x64\x2e\x64\x61\x74\x61\
+\x26\x26\x28\x64\x2e\x75\x72\x6c\x2b\x3d\x28\x62\x4d\x2e\x74\x65\
+\x73\x74\x28\x64\x2e\x75\x72\x6c\x29\x3f\x22\x26\x22\x3a\x22\x3f\
+\x22\x29\x2b\x64\x2e\x64\x61\x74\x61\x2c\x64\x65\x6c\x65\x74\x65\
+\x20\x64\x2e\x64\x61\x74\x61\x29\x2c\x6b\x3d\x64\x2e\x75\x72\x6c\
+\x3b\x69\x66\x28\x64\x2e\x63\x61\x63\x68\x65\x3d\x3d\x3d\x21\x31\
+\x29\x7b\x76\x61\x72\x20\x78\x3d\x66\x2e\x6e\x6f\x77\x28\x29\x2c\
+\x79\x3d\x64\x2e\x75\x72\x6c\x2e\x72\x65\x70\x6c\x61\x63\x65\x28\
+\x62\x51\x2c\x22\x24\x31\x5f\x3d\x22\x2b\x78\x29\x3b\x64\x2e\x75\
+\x72\x6c\x3d\x79\x2b\x28\x79\x3d\x3d\x3d\x64\x2e\x75\x72\x6c\x3f\
+\x28\x62\x4d\x2e\x74\x65\x73\x74\x28\x64\x2e\x75\x72\x6c\x29\x3f\
+\x22\x26\x22\x3a\x22\x3f\x22\x29\x2b\x22\x5f\x3d\x22\x2b\x78\x3a\
+\x22\x22\x29\x7d\x7d\x28\x64\x2e\x64\x61\x74\x61\x26\x26\x64\x2e\
+\x68\x61\x73\x43\x6f\x6e\x74\x65\x6e\x74\x26\x26\x64\x2e\x63\x6f\
+\x6e\x74\x65\x6e\x74\x54\x79\x70\x65\x21\x3d\x3d\x21\x31\x7c\x7c\
+\x63\x2e\x63\x6f\x6e\x74\x65\x6e\x74\x54\x79\x70\x65\x29\x26\x26\
+\x76\x2e\x73\x65\x74\x52\x65\x71\x75\x65\x73\x74\x48\x65\x61\x64\
+\x65\x72\x28\x22\x43\x6f\x6e\x74\x65\x6e\x74\x2d\x54\x79\x70\x65\
+\x22\x2c\x64\x2e\x63\x6f\x6e\x74\x65\x6e\x74\x54\x79\x70\x65\x29\
+\x2c\x64\x2e\x69\x66\x4d\x6f\x64\x69\x66\x69\x65\x64\x26\x26\x28\
+\x6b\x3d\x6b\x7c\x7c\x64\x2e\x75\x72\x6c\x2c\x66\x2e\x6c\x61\x73\
+\x74\x4d\x6f\x64\x69\x66\x69\x65\x64\x5b\x6b\x5d\x26\x26\x76\x2e\
+\x73\x65\x74\x52\x65\x71\x75\x65\x73\x74\x48\x65\x61\x64\x65\x72\
+\x28\x22\x49\x66\x2d\x4d\x6f\x64\x69\x66\x69\x65\x64\x2d\x53\x69\
+\x6e\x63\x65\x22\x2c\x66\x2e\x6c\x61\x73\x74\x4d\x6f\x64\x69\x66\
+\x69\x65\x64\x5b\x6b\x5d\x29\x2c\x66\x2e\x65\x74\x61\x67\x5b\x6b\
+\x5d\x26\x26\x76\x2e\x73\x65\x74\x52\x65\x71\x75\x65\x73\x74\x48\
+\x65\x61\x64\x65\x72\x28\x22\x49\x66\x2d\x4e\x6f\x6e\x65\x2d\x4d\
+\x61\x74\x63\x68\x22\x2c\x66\x2e\x65\x74\x61\x67\x5b\x6b\x5d\x29\
+\x29\x2c\x76\x2e\x73\x65\x74\x52\x65\x71\x75\x65\x73\x74\x48\x65\
+\x61\x64\x65\x72\x28\x22\x41\x63\x63\x65\x70\x74\x22\x2c\x64\x2e\
+\x64\x61\x74\x61\x54\x79\x70\x65\x73\x5b\x30\x5d\x26\x26\x64\x2e\
+\x61\x63\x63\x65\x70\x74\x73\x5b\x64\x2e\x64\x61\x74\x61\x54\x79\
+\x70\x65\x73\x5b\x30\x5d\x5d\x3f\x64\x2e\x61\x63\x63\x65\x70\x74\
+\x73\x5b\x64\x2e\x64\x61\x74\x61\x54\x79\x70\x65\x73\x5b\x30\x5d\
+\x5d\x2b\x28\x64\x2e\x64\x61\x74\x61\x54\x79\x70\x65\x73\x5b\x30\
+\x5d\x21\x3d\x3d\x22\x2a\x22\x3f\x22\x2c\x20\x22\x2b\x62\x58\x2b\
+\x22\x3b\x20\x71\x3d\x30\x2e\x30\x31\x22\x3a\x22\x22\x29\x3a\x64\
+\x2e\x61\x63\x63\x65\x70\x74\x73\x5b\x22\x2a\x22\x5d\x29\x3b\x66\
+\x6f\x72\x28\x75\x20\x69\x6e\x20\x64\x2e\x68\x65\x61\x64\x65\x72\
+\x73\x29\x76\x2e\x73\x65\x74\x52\x65\x71\x75\x65\x73\x74\x48\x65\
+\x61\x64\x65\x72\x28\x75\x2c\x64\x2e\x68\x65\x61\x64\x65\x72\x73\
+\x5b\x75\x5d\x29\x3b\x69\x66\x28\x64\x2e\x62\x65\x66\x6f\x72\x65\
+\x53\x65\x6e\x64\x26\x26\x28\x64\x2e\x62\x65\x66\x6f\x72\x65\x53\
+\x65\x6e\x64\x2e\x63\x61\x6c\x6c\x28\x65\x2c\x76\x2c\x64\x29\x3d\
+\x3d\x3d\x21\x31\x7c\x7c\x73\x3d\x3d\x3d\x32\x29\x29\x7b\x76\x2e\
+\x61\x62\x6f\x72\x74\x28\x29\x3b\x72\x65\x74\x75\x72\x6e\x21\x31\
+\x7d\x66\x6f\x72\x28\x75\x20\x69\x6e\x7b\x73\x75\x63\x63\x65\x73\
+\x73\x3a\x31\x2c\x65\x72\x72\x6f\x72\x3a\x31\x2c\x63\x6f\x6d\x70\
+\x6c\x65\x74\x65\x3a\x31\x7d\x29\x76\x5b\x75\x5d\x28\x64\x5b\x75\
+\x5d\x29\x3b\x70\x3d\x62\x24\x28\x62\x55\x2c\x64\x2c\x63\x2c\x76\
+\x29\x3b\x69\x66\x28\x21\x70\x29\x77\x28\x2d\x31\x2c\x22\x4e\x6f\
+\x20\x54\x72\x61\x6e\x73\x70\x6f\x72\x74\x22\x29\x3b\x65\x6c\x73\
+\x65\x7b\x76\x2e\x72\x65\x61\x64\x79\x53\x74\x61\x74\x65\x3d\x31\
+\x2c\x74\x26\x26\x67\x2e\x74\x72\x69\x67\x67\x65\x72\x28\x22\x61\
+\x6a\x61\x78\x53\x65\x6e\x64\x22\x2c\x5b\x76\x2c\x64\x5d\x29\x2c\
+\x64\x2e\x61\x73\x79\x6e\x63\x26\x26\x64\x2e\x74\x69\x6d\x65\x6f\
+\x75\x74\x3e\x30\x26\x26\x28\x71\x3d\x73\x65\x74\x54\x69\x6d\x65\
+\x6f\x75\x74\x28\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x29\x7b\x76\
+\x2e\x61\x62\x6f\x72\x74\x28\x22\x74\x69\x6d\x65\x6f\x75\x74\x22\
+\x29\x7d\x2c\x64\x2e\x74\x69\x6d\x65\x6f\x75\x74\x29\x29\x3b\x74\
+\x72\x79\x7b\x73\x3d\x31\x2c\x70\x2e\x73\x65\x6e\x64\x28\x6c\x2c\
+\x77\x29\x7d\x63\x61\x74\x63\x68\x28\x7a\x29\x7b\x69\x66\x28\x73\
+\x3c\x32\x29\x77\x28\x2d\x31\x2c\x7a\x29\x3b\x65\x6c\x73\x65\x20\
+\x74\x68\x72\x6f\x77\x20\x7a\x7d\x7d\x72\x65\x74\x75\x72\x6e\x20\
+\x76\x7d\x2c\x70\x61\x72\x61\x6d\x3a\x66\x75\x6e\x63\x74\x69\x6f\
+\x6e\x28\x61\x2c\x63\x29\x7b\x76\x61\x72\x20\x64\x3d\x5b\x5d\x2c\
+\x65\x3d\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x29\x7b\
+\x62\x3d\x66\x2e\x69\x73\x46\x75\x6e\x63\x74\x69\x6f\x6e\x28\x62\
+\x29\x3f\x62\x28\x29\x3a\x62\x2c\x64\x5b\x64\x2e\x6c\x65\x6e\x67\
+\x74\x68\x5d\x3d\x65\x6e\x63\x6f\x64\x65\x55\x52\x49\x43\x6f\x6d\
+\x70\x6f\x6e\x65\x6e\x74\x28\x61\x29\x2b\x22\x3d\x22\x2b\x65\x6e\
+\x63\x6f\x64\x65\x55\x52\x49\x43\x6f\x6d\x70\x6f\x6e\x65\x6e\x74\
+\x28\x62\x29\x7d\x3b\x63\x3d\x3d\x3d\x62\x26\x26\x28\x63\x3d\x66\
+\x2e\x61\x6a\x61\x78\x53\x65\x74\x74\x69\x6e\x67\x73\x2e\x74\x72\
+\x61\x64\x69\x74\x69\x6f\x6e\x61\x6c\x29\x3b\x69\x66\x28\x66\x2e\
+\x69\x73\x41\x72\x72\x61\x79\x28\x61\x29\x7c\x7c\x61\x2e\x6a\x71\
+\x75\x65\x72\x79\x26\x26\x21\x66\x2e\x69\x73\x50\x6c\x61\x69\x6e\
+\x4f\x62\x6a\x65\x63\x74\x28\x61\x29\x29\x66\x2e\x65\x61\x63\x68\
+\x28\x61\x2c\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x29\x7b\x65\x28\
+\x74\x68\x69\x73\x2e\x6e\x61\x6d\x65\x2c\x74\x68\x69\x73\x2e\x76\
+\x61\x6c\x75\x65\x29\x7d\x29\x3b\x65\x6c\x73\x65\x20\x66\x6f\x72\
+\x28\x76\x61\x72\x20\x67\x20\x69\x6e\x20\x61\x29\x63\x61\x28\x67\
+\x2c\x61\x5b\x67\x5d\x2c\x63\x2c\x65\x29\x3b\x72\x65\x74\x75\x72\
+\x6e\x20\x64\x2e\x6a\x6f\x69\x6e\x28\x22\x26\x22\x29\x2e\x72\x65\
+\x70\x6c\x61\x63\x65\x28\x62\x44\x2c\x22\x2b\x22\x29\x7d\x7d\x29\
+\x2c\x66\x2e\x65\x78\x74\x65\x6e\x64\x28\x7b\x61\x63\x74\x69\x76\
+\x65\x3a\x30\x2c\x6c\x61\x73\x74\x4d\x6f\x64\x69\x66\x69\x65\x64\
+\x3a\x7b\x7d\x2c\x65\x74\x61\x67\x3a\x7b\x7d\x7d\x29\x3b\x76\x61\
+\x72\x20\x63\x64\x3d\x66\x2e\x6e\x6f\x77\x28\x29\x2c\x63\x65\x3d\
+\x2f\x28\x5c\x3d\x29\x5c\x3f\x28\x26\x7c\x24\x29\x7c\x5c\x3f\x5c\
+\x3f\x2f\x69\x3b\x66\x2e\x61\x6a\x61\x78\x53\x65\x74\x75\x70\x28\
+\x7b\x6a\x73\x6f\x6e\x70\x3a\x22\x63\x61\x6c\x6c\x62\x61\x63\x6b\
+\x22\x2c\x6a\x73\x6f\x6e\x70\x43\x61\x6c\x6c\x62\x61\x63\x6b\x3a\
+\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x29\x7b\x72\x65\x74\x75\x72\
+\x6e\x20\x66\x2e\x65\x78\x70\x61\x6e\x64\x6f\x2b\x22\x5f\x22\x2b\
+\x63\x64\x2b\x2b\x7d\x7d\x29\x2c\x66\x2e\x61\x6a\x61\x78\x50\x72\
+\x65\x66\x69\x6c\x74\x65\x72\x28\x22\x6a\x73\x6f\x6e\x20\x6a\x73\
+\x6f\x6e\x70\x22\x2c\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x62\x2c\
+\x63\x2c\x64\x29\x7b\x76\x61\x72\x20\x65\x3d\x62\x2e\x63\x6f\x6e\
+\x74\x65\x6e\x74\x54\x79\x70\x65\x3d\x3d\x3d\x22\x61\x70\x70\x6c\
+\x69\x63\x61\x74\x69\x6f\x6e\x2f\x78\x2d\x77\x77\x77\x2d\x66\x6f\
+\x72\x6d\x2d\x75\x72\x6c\x65\x6e\x63\x6f\x64\x65\x64\x22\x26\x26\
+\x74\x79\x70\x65\x6f\x66\x20\x62\x2e\x64\x61\x74\x61\x3d\x3d\x22\
+\x73\x74\x72\x69\x6e\x67\x22\x3b\x69\x66\x28\x62\x2e\x64\x61\x74\
+\x61\x54\x79\x70\x65\x73\x5b\x30\x5d\x3d\x3d\x3d\x22\x6a\x73\x6f\
+\x6e\x70\x22\x7c\x7c\x62\x2e\x6a\x73\x6f\x6e\x70\x21\x3d\x3d\x21\
+\x31\x26\x26\x28\x63\x65\x2e\x74\x65\x73\x74\x28\x62\x2e\x75\x72\
+\x6c\x29\x7c\x7c\x65\x26\x26\x63\x65\x2e\x74\x65\x73\x74\x28\x62\
+\x2e\x64\x61\x74\x61\x29\x29\x29\x7b\x76\x61\x72\x20\x67\x2c\x68\
+\x3d\x62\x2e\x6a\x73\x6f\x6e\x70\x43\x61\x6c\x6c\x62\x61\x63\x6b\
+\x3d\x66\x2e\x69\x73\x46\x75\x6e\x63\x74\x69\x6f\x6e\x28\x62\x2e\
+\x6a\x73\x6f\x6e\x70\x43\x61\x6c\x6c\x62\x61\x63\x6b\x29\x3f\x62\
+\x2e\x6a\x73\x6f\x6e\x70\x43\x61\x6c\x6c\x62\x61\x63\x6b\x28\x29\
+\x3a\x62\x2e\x6a\x73\x6f\x6e\x70\x43\x61\x6c\x6c\x62\x61\x63\x6b\
+\x2c\x69\x3d\x61\x5b\x68\x5d\x2c\x6a\x3d\x62\x2e\x75\x72\x6c\x2c\
+\x6b\x3d\x62\x2e\x64\x61\x74\x61\x2c\x6c\x3d\x22\x24\x31\x22\x2b\
+\x68\x2b\x22\x24\x32\x22\x3b\x62\x2e\x6a\x73\x6f\x6e\x70\x21\x3d\
+\x3d\x21\x31\x26\x26\x28\x6a\x3d\x6a\x2e\x72\x65\x70\x6c\x61\x63\
+\x65\x28\x63\x65\x2c\x6c\x29\x2c\x62\x2e\x75\x72\x6c\x3d\x3d\x3d\
+\x6a\x26\x26\x28\x65\x26\x26\x28\x6b\x3d\x6b\x2e\x72\x65\x70\x6c\
+\x61\x63\x65\x28\x63\x65\x2c\x6c\x29\x29\x2c\x62\x2e\x64\x61\x74\
+\x61\x3d\x3d\x3d\x6b\x26\x26\x28\x6a\x2b\x3d\x28\x2f\x5c\x3f\x2f\
+\x2e\x74\x65\x73\x74\x28\x6a\x29\x3f\x22\x26\x22\x3a\x22\x3f\x22\
+\x29\x2b\x62\x2e\x6a\x73\x6f\x6e\x70\x2b\x22\x3d\x22\x2b\x68\x29\
+\x29\x29\x2c\x62\x2e\x75\x72\x6c\x3d\x6a\x2c\x62\x2e\x64\x61\x74\
+\x61\x3d\x6b\x2c\x61\x5b\x68\x5d\x3d\x66\x75\x6e\x63\x74\x69\x6f\
+\x6e\x28\x61\x29\x7b\x67\x3d\x5b\x61\x5d\x7d\x2c\x64\x2e\x61\x6c\
+\x77\x61\x79\x73\x28\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x29\x7b\
+\x61\x5b\x68\x5d\x3d\x69\x2c\x67\x26\x26\x66\x2e\x69\x73\x46\x75\
+\x6e\x63\x74\x69\x6f\x6e\x28\x69\x29\x26\x26\x61\x5b\x68\x5d\x28\
+\x67\x5b\x30\x5d\x29\x7d\x29\x2c\x62\x2e\x63\x6f\x6e\x76\x65\x72\
+\x74\x65\x72\x73\x5b\x22\x73\x63\x72\x69\x70\x74\x20\x6a\x73\x6f\
+\x6e\x22\x5d\x3d\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x29\x7b\x67\
+\x7c\x7c\x66\x2e\x65\x72\x72\x6f\x72\x28\x68\x2b\x22\x20\x77\x61\
+\x73\x20\x6e\x6f\x74\x20\x63\x61\x6c\x6c\x65\x64\x22\x29\x3b\x72\
+\x65\x74\x75\x72\x6e\x20\x67\x5b\x30\x5d\x7d\x2c\x62\x2e\x64\x61\
+\x74\x61\x54\x79\x70\x65\x73\x5b\x30\x5d\x3d\x22\x6a\x73\x6f\x6e\
+\x22\x3b\x72\x65\x74\x75\x72\x6e\x22\x73\x63\x72\x69\x70\x74\x22\
+\x7d\x7d\x29\x2c\x66\x2e\x61\x6a\x61\x78\x53\x65\x74\x75\x70\x28\
+\x7b\x61\x63\x63\x65\x70\x74\x73\x3a\x7b\x73\x63\x72\x69\x70\x74\
+\x3a\x22\x74\x65\x78\x74\x2f\x6a\x61\x76\x61\x73\x63\x72\x69\x70\
+\x74\x2c\x20\x61\x70\x70\x6c\x69\x63\x61\x74\x69\x6f\x6e\x2f\x6a\
+\x61\x76\x61\x73\x63\x72\x69\x70\x74\x2c\x20\x61\x70\x70\x6c\x69\
+\x63\x61\x74\x69\x6f\x6e\x2f\x65\x63\x6d\x61\x73\x63\x72\x69\x70\
+\x74\x2c\x20\x61\x70\x70\x6c\x69\x63\x61\x74\x69\x6f\x6e\x2f\x78\
+\x2d\x65\x63\x6d\x61\x73\x63\x72\x69\x70\x74\x22\x7d\x2c\x63\x6f\
+\x6e\x74\x65\x6e\x74\x73\x3a\x7b\x73\x63\x72\x69\x70\x74\x3a\x2f\
+\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x7c\x65\x63\x6d\x61\x73\
+\x63\x72\x69\x70\x74\x2f\x7d\x2c\x63\x6f\x6e\x76\x65\x72\x74\x65\
+\x72\x73\x3a\x7b\x22\x74\x65\x78\x74\x20\x73\x63\x72\x69\x70\x74\
+\x22\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x66\x2e\
+\x67\x6c\x6f\x62\x61\x6c\x45\x76\x61\x6c\x28\x61\x29\x3b\x72\x65\
+\x74\x75\x72\x6e\x20\x61\x7d\x7d\x7d\x29\x2c\x66\x2e\x61\x6a\x61\
+\x78\x50\x72\x65\x66\x69\x6c\x74\x65\x72\x28\x22\x73\x63\x72\x69\
+\x70\x74\x22\x2c\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\
+\x61\x2e\x63\x61\x63\x68\x65\x3d\x3d\x3d\x62\x26\x26\x28\x61\x2e\
+\x63\x61\x63\x68\x65\x3d\x21\x31\x29\x2c\x61\x2e\x63\x72\x6f\x73\
+\x73\x44\x6f\x6d\x61\x69\x6e\x26\x26\x28\x61\x2e\x74\x79\x70\x65\
+\x3d\x22\x47\x45\x54\x22\x2c\x61\x2e\x67\x6c\x6f\x62\x61\x6c\x3d\
+\x21\x31\x29\x7d\x29\x2c\x66\x2e\x61\x6a\x61\x78\x54\x72\x61\x6e\
+\x73\x70\x6f\x72\x74\x28\x22\x73\x63\x72\x69\x70\x74\x22\x2c\x66\
+\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x69\x66\x28\x61\x2e\
+\x63\x72\x6f\x73\x73\x44\x6f\x6d\x61\x69\x6e\x29\x7b\x76\x61\x72\
+\x20\x64\x2c\x65\x3d\x63\x2e\x68\x65\x61\x64\x7c\x7c\x63\x2e\x67\
+\x65\x74\x45\x6c\x65\x6d\x65\x6e\x74\x73\x42\x79\x54\x61\x67\x4e\
+\x61\x6d\x65\x28\x22\x68\x65\x61\x64\x22\x29\x5b\x30\x5d\x7c\x7c\
+\x63\x2e\x64\x6f\x63\x75\x6d\x65\x6e\x74\x45\x6c\x65\x6d\x65\x6e\
+\x74\x3b\x72\x65\x74\x75\x72\x6e\x7b\x73\x65\x6e\x64\x3a\x66\x75\
+\x6e\x63\x74\x69\x6f\x6e\x28\x66\x2c\x67\x29\x7b\x64\x3d\x63\x2e\
+\x63\x72\x65\x61\x74\x65\x45\x6c\x65\x6d\x65\x6e\x74\x28\x22\x73\
+\x63\x72\x69\x70\x74\x22\x29\x2c\x64\x2e\x61\x73\x79\x6e\x63\x3d\
+\x22\x61\x73\x79\x6e\x63\x22\x2c\x61\x2e\x73\x63\x72\x69\x70\x74\
+\x43\x68\x61\x72\x73\x65\x74\x26\x26\x28\x64\x2e\x63\x68\x61\x72\
+\x73\x65\x74\x3d\x61\x2e\x73\x63\x72\x69\x70\x74\x43\x68\x61\x72\
+\x73\x65\x74\x29\x2c\x64\x2e\x73\x72\x63\x3d\x61\x2e\x75\x72\x6c\
+\x2c\x64\x2e\x6f\x6e\x6c\x6f\x61\x64\x3d\x64\x2e\x6f\x6e\x72\x65\
+\x61\x64\x79\x73\x74\x61\x74\x65\x63\x68\x61\x6e\x67\x65\x3d\x66\
+\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x63\x29\x7b\x69\x66\x28\
+\x63\x7c\x7c\x21\x64\x2e\x72\x65\x61\x64\x79\x53\x74\x61\x74\x65\
+\x7c\x7c\x2f\x6c\x6f\x61\x64\x65\x64\x7c\x63\x6f\x6d\x70\x6c\x65\
+\x74\x65\x2f\x2e\x74\x65\x73\x74\x28\x64\x2e\x72\x65\x61\x64\x79\
+\x53\x74\x61\x74\x65\x29\x29\x64\x2e\x6f\x6e\x6c\x6f\x61\x64\x3d\
+\x64\x2e\x6f\x6e\x72\x65\x61\x64\x79\x73\x74\x61\x74\x65\x63\x68\
+\x61\x6e\x67\x65\x3d\x6e\x75\x6c\x6c\x2c\x65\x26\x26\x64\x2e\x70\
+\x61\x72\x65\x6e\x74\x4e\x6f\x64\x65\x26\x26\x65\x2e\x72\x65\x6d\
+\x6f\x76\x65\x43\x68\x69\x6c\x64\x28\x64\x29\x2c\x64\x3d\x62\x2c\
+\x63\x7c\x7c\x67\x28\x32\x30\x30\x2c\x22\x73\x75\x63\x63\x65\x73\
+\x73\x22\x29\x7d\x2c\x65\x2e\x69\x6e\x73\x65\x72\x74\x42\x65\x66\
+\x6f\x72\x65\x28\x64\x2c\x65\x2e\x66\x69\x72\x73\x74\x43\x68\x69\
+\x6c\x64\x29\x7d\x2c\x61\x62\x6f\x72\x74\x3a\x66\x75\x6e\x63\x74\
+\x69\x6f\x6e\x28\x29\x7b\x64\x26\x26\x64\x2e\x6f\x6e\x6c\x6f\x61\
+\x64\x28\x30\x2c\x31\x29\x7d\x7d\x7d\x7d\x29\x3b\x76\x61\x72\x20\
+\x63\x66\x3d\x61\x2e\x41\x63\x74\x69\x76\x65\x58\x4f\x62\x6a\x65\
+\x63\x74\x3f\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x29\x7b\x66\x6f\
+\x72\x28\x76\x61\x72\x20\x61\x20\x69\x6e\x20\x63\x68\x29\x63\x68\
+\x5b\x61\x5d\x28\x30\x2c\x31\x29\x7d\x3a\x21\x31\x2c\x63\x67\x3d\
+\x30\x2c\x63\x68\x3b\x66\x2e\x61\x6a\x61\x78\x53\x65\x74\x74\x69\
+\x6e\x67\x73\x2e\x78\x68\x72\x3d\x61\x2e\x41\x63\x74\x69\x76\x65\
+\x58\x4f\x62\x6a\x65\x63\x74\x3f\x66\x75\x6e\x63\x74\x69\x6f\x6e\
+\x28\x29\x7b\x72\x65\x74\x75\x72\x6e\x21\x74\x68\x69\x73\x2e\x69\
+\x73\x4c\x6f\x63\x61\x6c\x26\x26\x63\x69\x28\x29\x7c\x7c\x63\x6a\
+\x28\x29\x7d\x3a\x63\x69\x2c\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\
+\x61\x29\x7b\x66\x2e\x65\x78\x74\x65\x6e\x64\x28\x66\x2e\x73\x75\
+\x70\x70\x6f\x72\x74\x2c\x7b\x61\x6a\x61\x78\x3a\x21\x21\x61\x2c\
+\x63\x6f\x72\x73\x3a\x21\x21\x61\x26\x26\x22\x77\x69\x74\x68\x43\
+\x72\x65\x64\x65\x6e\x74\x69\x61\x6c\x73\x22\x69\x6e\x20\x61\x7d\
+\x29\x7d\x28\x66\x2e\x61\x6a\x61\x78\x53\x65\x74\x74\x69\x6e\x67\
+\x73\x2e\x78\x68\x72\x28\x29\x29\x2c\x66\x2e\x73\x75\x70\x70\x6f\
+\x72\x74\x2e\x61\x6a\x61\x78\x26\x26\x66\x2e\x61\x6a\x61\x78\x54\
+\x72\x61\x6e\x73\x70\x6f\x72\x74\x28\x66\x75\x6e\x63\x74\x69\x6f\
+\x6e\x28\x63\x29\x7b\x69\x66\x28\x21\x63\x2e\x63\x72\x6f\x73\x73\
+\x44\x6f\x6d\x61\x69\x6e\x7c\x7c\x66\x2e\x73\x75\x70\x70\x6f\x72\
+\x74\x2e\x63\x6f\x72\x73\x29\x7b\x76\x61\x72\x20\x64\x3b\x72\x65\
+\x74\x75\x72\x6e\x7b\x73\x65\x6e\x64\x3a\x66\x75\x6e\x63\x74\x69\
+\x6f\x6e\x28\x65\x2c\x67\x29\x7b\x76\x61\x72\x20\x68\x3d\x63\x2e\
+\x78\x68\x72\x28\x29\x2c\x69\x2c\x6a\x3b\x63\x2e\x75\x73\x65\x72\
+\x6e\x61\x6d\x65\x3f\x68\x2e\x6f\x70\x65\x6e\x28\x63\x2e\x74\x79\
+\x70\x65\x2c\x63\x2e\x75\x72\x6c\x2c\x63\x2e\x61\x73\x79\x6e\x63\
+\x2c\x63\x2e\x75\x73\x65\x72\x6e\x61\x6d\x65\x2c\x63\x2e\x70\x61\
+\x73\x73\x77\x6f\x72\x64\x29\x3a\x68\x2e\x6f\x70\x65\x6e\x28\x63\
+\x2e\x74\x79\x70\x65\x2c\x63\x2e\x75\x72\x6c\x2c\x63\x2e\x61\x73\
+\x79\x6e\x63\x29\x3b\x69\x66\x28\x63\x2e\x78\x68\x72\x46\x69\x65\
+\x6c\x64\x73\x29\x66\x6f\x72\x28\x6a\x20\x69\x6e\x20\x63\x2e\x78\
+\x68\x72\x46\x69\x65\x6c\x64\x73\x29\x68\x5b\x6a\x5d\x3d\x63\x2e\
+\x78\x68\x72\x46\x69\x65\x6c\x64\x73\x5b\x6a\x5d\x3b\x63\x2e\x6d\
+\x69\x6d\x65\x54\x79\x70\x65\x26\x26\x68\x2e\x6f\x76\x65\x72\x72\
+\x69\x64\x65\x4d\x69\x6d\x65\x54\x79\x70\x65\x26\x26\x68\x2e\x6f\
+\x76\x65\x72\x72\x69\x64\x65\x4d\x69\x6d\x65\x54\x79\x70\x65\x28\
+\x63\x2e\x6d\x69\x6d\x65\x54\x79\x70\x65\x29\x2c\x21\x63\x2e\x63\
+\x72\x6f\x73\x73\x44\x6f\x6d\x61\x69\x6e\x26\x26\x21\x65\x5b\x22\
+\x58\x2d\x52\x65\x71\x75\x65\x73\x74\x65\x64\x2d\x57\x69\x74\x68\
+\x22\x5d\x26\x26\x28\x65\x5b\x22\x58\x2d\x52\x65\x71\x75\x65\x73\
+\x74\x65\x64\x2d\x57\x69\x74\x68\x22\x5d\x3d\x22\x58\x4d\x4c\x48\
+\x74\x74\x70\x52\x65\x71\x75\x65\x73\x74\x22\x29\x3b\x74\x72\x79\
+\x7b\x66\x6f\x72\x28\x6a\x20\x69\x6e\x20\x65\x29\x68\x2e\x73\x65\
+\x74\x52\x65\x71\x75\x65\x73\x74\x48\x65\x61\x64\x65\x72\x28\x6a\
+\x2c\x65\x5b\x6a\x5d\x29\x7d\x63\x61\x74\x63\x68\x28\x6b\x29\x7b\
+\x7d\x68\x2e\x73\x65\x6e\x64\x28\x63\x2e\x68\x61\x73\x43\x6f\x6e\
+\x74\x65\x6e\x74\x26\x26\x63\x2e\x64\x61\x74\x61\x7c\x7c\x6e\x75\
+\x6c\x6c\x29\x2c\x64\x3d\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\
+\x2c\x65\x29\x7b\x76\x61\x72\x20\x6a\x2c\x6b\x2c\x6c\x2c\x6d\x2c\
+\x6e\x3b\x74\x72\x79\x7b\x69\x66\x28\x64\x26\x26\x28\x65\x7c\x7c\
+\x68\x2e\x72\x65\x61\x64\x79\x53\x74\x61\x74\x65\x3d\x3d\x3d\x34\
+\x29\x29\x7b\x64\x3d\x62\x2c\x69\x26\x26\x28\x68\x2e\x6f\x6e\x72\
+\x65\x61\x64\x79\x73\x74\x61\x74\x65\x63\x68\x61\x6e\x67\x65\x3d\
+\x66\x2e\x6e\x6f\x6f\x70\x2c\x63\x66\x26\x26\x64\x65\x6c\x65\x74\
+\x65\x20\x63\x68\x5b\x69\x5d\x29\x3b\x69\x66\x28\x65\x29\x68\x2e\
+\x72\x65\x61\x64\x79\x53\x74\x61\x74\x65\x21\x3d\x3d\x34\x26\x26\
+\x68\x2e\x61\x62\x6f\x72\x74\x28\x29\x3b\x65\x6c\x73\x65\x7b\x6a\
+\x3d\x68\x2e\x73\x74\x61\x74\x75\x73\x2c\x6c\x3d\x68\x2e\x67\x65\
+\x74\x41\x6c\x6c\x52\x65\x73\x70\x6f\x6e\x73\x65\x48\x65\x61\x64\
+\x65\x72\x73\x28\x29\x2c\x6d\x3d\x7b\x7d\x2c\x6e\x3d\x68\x2e\x72\
+\x65\x73\x70\x6f\x6e\x73\x65\x58\x4d\x4c\x2c\x6e\x26\x26\x6e\x2e\
+\x64\x6f\x63\x75\x6d\x65\x6e\x74\x45\x6c\x65\x6d\x65\x6e\x74\x26\
+\x26\x28\x6d\x2e\x78\x6d\x6c\x3d\x6e\x29\x2c\x6d\x2e\x74\x65\x78\
+\x74\x3d\x68\x2e\x72\x65\x73\x70\x6f\x6e\x73\x65\x54\x65\x78\x74\
+\x3b\x74\x72\x79\x7b\x6b\x3d\x68\x2e\x73\x74\x61\x74\x75\x73\x54\
+\x65\x78\x74\x7d\x63\x61\x74\x63\x68\x28\x6f\x29\x7b\x6b\x3d\x22\
+\x22\x7d\x21\x6a\x26\x26\x63\x2e\x69\x73\x4c\x6f\x63\x61\x6c\x26\
+\x26\x21\x63\x2e\x63\x72\x6f\x73\x73\x44\x6f\x6d\x61\x69\x6e\x3f\
+\x6a\x3d\x6d\x2e\x74\x65\x78\x74\x3f\x32\x30\x30\x3a\x34\x30\x34\
+\x3a\x6a\x3d\x3d\x3d\x31\x32\x32\x33\x26\x26\x28\x6a\x3d\x32\x30\
+\x34\x29\x7d\x7d\x7d\x63\x61\x74\x63\x68\x28\x70\x29\x7b\x65\x7c\
+\x7c\x67\x28\x2d\x31\x2c\x70\x29\x7d\x6d\x26\x26\x67\x28\x6a\x2c\
+\x6b\x2c\x6d\x2c\x6c\x29\x7d\x2c\x21\x63\x2e\x61\x73\x79\x6e\x63\
+\x7c\x7c\x68\x2e\x72\x65\x61\x64\x79\x53\x74\x61\x74\x65\x3d\x3d\
+\x3d\x34\x3f\x64\x28\x29\x3a\x28\x69\x3d\x2b\x2b\x63\x67\x2c\x63\
+\x66\x26\x26\x28\x63\x68\x7c\x7c\x28\x63\x68\x3d\x7b\x7d\x2c\x66\
+\x28\x61\x29\x2e\x75\x6e\x6c\x6f\x61\x64\x28\x63\x66\x29\x29\x2c\
+\x63\x68\x5b\x69\x5d\x3d\x64\x29\x2c\x68\x2e\x6f\x6e\x72\x65\x61\
+\x64\x79\x73\x74\x61\x74\x65\x63\x68\x61\x6e\x67\x65\x3d\x64\x29\
+\x7d\x2c\x61\x62\x6f\x72\x74\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\
+\x28\x29\x7b\x64\x26\x26\x64\x28\x30\x2c\x31\x29\x7d\x7d\x7d\x7d\
+\x29\x3b\x76\x61\x72\x20\x63\x6b\x3d\x7b\x7d\x2c\x63\x6c\x2c\x63\
+\x6d\x2c\x63\x6e\x3d\x2f\x5e\x28\x3f\x3a\x74\x6f\x67\x67\x6c\x65\
+\x7c\x73\x68\x6f\x77\x7c\x68\x69\x64\x65\x29\x24\x2f\x2c\x63\x6f\
+\x3d\x2f\x5e\x28\x5b\x2b\x5c\x2d\x5d\x3d\x29\x3f\x28\x5b\x5c\x64\
+\x2b\x2e\x5c\x2d\x5d\x2b\x29\x28\x5b\x61\x2d\x7a\x25\x5d\x2a\x29\
+\x24\x2f\x69\x2c\x63\x70\x2c\x63\x71\x3d\x5b\x5b\x22\x68\x65\x69\
+\x67\x68\x74\x22\x2c\x22\x6d\x61\x72\x67\x69\x6e\x54\x6f\x70\x22\
+\x2c\x22\x6d\x61\x72\x67\x69\x6e\x42\x6f\x74\x74\x6f\x6d\x22\x2c\
+\x22\x70\x61\x64\x64\x69\x6e\x67\x54\x6f\x70\x22\x2c\x22\x70\x61\
+\x64\x64\x69\x6e\x67\x42\x6f\x74\x74\x6f\x6d\x22\x5d\x2c\x5b\x22\
+\x77\x69\x64\x74\x68\x22\x2c\x22\x6d\x61\x72\x67\x69\x6e\x4c\x65\
+\x66\x74\x22\x2c\x22\x6d\x61\x72\x67\x69\x6e\x52\x69\x67\x68\x74\
+\x22\x2c\x22\x70\x61\x64\x64\x69\x6e\x67\x4c\x65\x66\x74\x22\x2c\
+\x22\x70\x61\x64\x64\x69\x6e\x67\x52\x69\x67\x68\x74\x22\x5d\x2c\
+\x5b\x22\x6f\x70\x61\x63\x69\x74\x79\x22\x5d\x5d\x2c\x63\x72\x3b\
+\x66\x2e\x66\x6e\x2e\x65\x78\x74\x65\x6e\x64\x28\x7b\x73\x68\x6f\
+\x77\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x2c\x63\
+\x29\x7b\x76\x61\x72\x20\x64\x2c\x65\x3b\x69\x66\x28\x61\x7c\x7c\
+\x61\x3d\x3d\x3d\x30\x29\x72\x65\x74\x75\x72\x6e\x20\x74\x68\x69\
+\x73\x2e\x61\x6e\x69\x6d\x61\x74\x65\x28\x63\x75\x28\x22\x73\x68\
+\x6f\x77\x22\x2c\x33\x29\x2c\x61\x2c\x62\x2c\x63\x29\x3b\x66\x6f\
+\x72\x28\x76\x61\x72\x20\x67\x3d\x30\x2c\x68\x3d\x74\x68\x69\x73\
+\x2e\x6c\x65\x6e\x67\x74\x68\x3b\x67\x3c\x68\x3b\x67\x2b\x2b\x29\
+\x64\x3d\x74\x68\x69\x73\x5b\x67\x5d\x2c\x64\x2e\x73\x74\x79\x6c\
+\x65\x26\x26\x28\x65\x3d\x64\x2e\x73\x74\x79\x6c\x65\x2e\x64\x69\
+\x73\x70\x6c\x61\x79\x2c\x21\x66\x2e\x5f\x64\x61\x74\x61\x28\x64\
+\x2c\x22\x6f\x6c\x64\x64\x69\x73\x70\x6c\x61\x79\x22\x29\x26\x26\
+\x65\x3d\x3d\x3d\x22\x6e\x6f\x6e\x65\x22\x26\x26\x28\x65\x3d\x64\
+\x2e\x73\x74\x79\x6c\x65\x2e\x64\x69\x73\x70\x6c\x61\x79\x3d\x22\
+\x22\x29\x2c\x65\x3d\x3d\x3d\x22\x22\x26\x26\x66\x2e\x63\x73\x73\
+\x28\x64\x2c\x22\x64\x69\x73\x70\x6c\x61\x79\x22\x29\x3d\x3d\x3d\
+\x22\x6e\x6f\x6e\x65\x22\x26\x26\x66\x2e\x5f\x64\x61\x74\x61\x28\
+\x64\x2c\x22\x6f\x6c\x64\x64\x69\x73\x70\x6c\x61\x79\x22\x2c\x63\
+\x76\x28\x64\x2e\x6e\x6f\x64\x65\x4e\x61\x6d\x65\x29\x29\x29\x3b\
+\x66\x6f\x72\x28\x67\x3d\x30\x3b\x67\x3c\x68\x3b\x67\x2b\x2b\x29\
+\x7b\x64\x3d\x74\x68\x69\x73\x5b\x67\x5d\x3b\x69\x66\x28\x64\x2e\
+\x73\x74\x79\x6c\x65\x29\x7b\x65\x3d\x64\x2e\x73\x74\x79\x6c\x65\
+\x2e\x64\x69\x73\x70\x6c\x61\x79\x3b\x69\x66\x28\x65\x3d\x3d\x3d\
+\x22\x22\x7c\x7c\x65\x3d\x3d\x3d\x22\x6e\x6f\x6e\x65\x22\x29\x64\
+\x2e\x73\x74\x79\x6c\x65\x2e\x64\x69\x73\x70\x6c\x61\x79\x3d\x66\
+\x2e\x5f\x64\x61\x74\x61\x28\x64\x2c\x22\x6f\x6c\x64\x64\x69\x73\
+\x70\x6c\x61\x79\x22\x29\x7c\x7c\x22\x22\x7d\x7d\x72\x65\x74\x75\
+\x72\x6e\x20\x74\x68\x69\x73\x7d\x2c\x68\x69\x64\x65\x3a\x66\x75\
+\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x2c\x63\x29\x7b\x69\x66\
+\x28\x61\x7c\x7c\x61\x3d\x3d\x3d\x30\x29\x72\x65\x74\x75\x72\x6e\
+\x20\x74\x68\x69\x73\x2e\x61\x6e\x69\x6d\x61\x74\x65\x28\x63\x75\
+\x28\x22\x68\x69\x64\x65\x22\x2c\x33\x29\x2c\x61\x2c\x62\x2c\x63\
+\x29\x3b\x76\x61\x72\x20\x64\x2c\x65\x2c\x67\x3d\x30\x2c\x68\x3d\
+\x74\x68\x69\x73\x2e\x6c\x65\x6e\x67\x74\x68\x3b\x66\x6f\x72\x28\
+\x3b\x67\x3c\x68\x3b\x67\x2b\x2b\x29\x64\x3d\x74\x68\x69\x73\x5b\
+\x67\x5d\x2c\x64\x2e\x73\x74\x79\x6c\x65\x26\x26\x28\x65\x3d\x66\
+\x2e\x63\x73\x73\x28\x64\x2c\x22\x64\x69\x73\x70\x6c\x61\x79\x22\
+\x29\x2c\x65\x21\x3d\x3d\x22\x6e\x6f\x6e\x65\x22\x26\x26\x21\x66\
+\x2e\x5f\x64\x61\x74\x61\x28\x64\x2c\x22\x6f\x6c\x64\x64\x69\x73\
+\x70\x6c\x61\x79\x22\x29\x26\x26\x66\x2e\x5f\x64\x61\x74\x61\x28\
+\x64\x2c\x22\x6f\x6c\x64\x64\x69\x73\x70\x6c\x61\x79\x22\x2c\x65\
+\x29\x29\x3b\x66\x6f\x72\x28\x67\x3d\x30\x3b\x67\x3c\x68\x3b\x67\
+\x2b\x2b\x29\x74\x68\x69\x73\x5b\x67\x5d\x2e\x73\x74\x79\x6c\x65\
+\x26\x26\x28\x74\x68\x69\x73\x5b\x67\x5d\x2e\x73\x74\x79\x6c\x65\
+\x2e\x64\x69\x73\x70\x6c\x61\x79\x3d\x22\x6e\x6f\x6e\x65\x22\x29\
+\x3b\x72\x65\x74\x75\x72\x6e\x20\x74\x68\x69\x73\x7d\x2c\x5f\x74\
+\x6f\x67\x67\x6c\x65\x3a\x66\x2e\x66\x6e\x2e\x74\x6f\x67\x67\x6c\
+\x65\x2c\x74\x6f\x67\x67\x6c\x65\x3a\x66\x75\x6e\x63\x74\x69\x6f\
+\x6e\x28\x61\x2c\x62\x2c\x63\x29\x7b\x76\x61\x72\x20\x64\x3d\x74\
+\x79\x70\x65\x6f\x66\x20\x61\x3d\x3d\x22\x62\x6f\x6f\x6c\x65\x61\
+\x6e\x22\x3b\x66\x2e\x69\x73\x46\x75\x6e\x63\x74\x69\x6f\x6e\x28\
+\x61\x29\x26\x26\x66\x2e\x69\x73\x46\x75\x6e\x63\x74\x69\x6f\x6e\
+\x28\x62\x29\x3f\x74\x68\x69\x73\x2e\x5f\x74\x6f\x67\x67\x6c\x65\
+\x2e\x61\x70\x70\x6c\x79\x28\x74\x68\x69\x73\x2c\x61\x72\x67\x75\
+\x6d\x65\x6e\x74\x73\x29\x3a\x61\x3d\x3d\x6e\x75\x6c\x6c\x7c\x7c\
+\x64\x3f\x74\x68\x69\x73\x2e\x65\x61\x63\x68\x28\x66\x75\x6e\x63\
+\x74\x69\x6f\x6e\x28\x29\x7b\x76\x61\x72\x20\x62\x3d\x64\x3f\x61\
+\x3a\x66\x28\x74\x68\x69\x73\x29\x2e\x69\x73\x28\x22\x3a\x68\x69\
+\x64\x64\x65\x6e\x22\x29\x3b\x66\x28\x74\x68\x69\x73\x29\x5b\x62\
+\x3f\x22\x73\x68\x6f\x77\x22\x3a\x22\x68\x69\x64\x65\x22\x5d\x28\
+\x29\x7d\x29\x3a\x74\x68\x69\x73\x2e\x61\x6e\x69\x6d\x61\x74\x65\
+\x28\x63\x75\x28\x22\x74\x6f\x67\x67\x6c\x65\x22\x2c\x33\x29\x2c\
+\x61\x2c\x62\x2c\x63\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x74\x68\
+\x69\x73\x7d\x2c\x66\x61\x64\x65\x54\x6f\x3a\x66\x75\x6e\x63\x74\
+\x69\x6f\x6e\x28\x61\x2c\x62\x2c\x63\x2c\x64\x29\x7b\x72\x65\x74\
+\x75\x72\x6e\x20\x74\x68\x69\x73\x2e\x66\x69\x6c\x74\x65\x72\x28\
+\x22\x3a\x68\x69\x64\x64\x65\x6e\x22\x29\x2e\x63\x73\x73\x28\x22\
+\x6f\x70\x61\x63\x69\x74\x79\x22\x2c\x30\x29\x2e\x73\x68\x6f\x77\
+\x28\x29\x2e\x65\x6e\x64\x28\x29\x2e\x61\x6e\x69\x6d\x61\x74\x65\
+\x28\x7b\x6f\x70\x61\x63\x69\x74\x79\x3a\x62\x7d\x2c\x61\x2c\x63\
+\x2c\x64\x29\x7d\x2c\x61\x6e\x69\x6d\x61\x74\x65\x3a\x66\x75\x6e\
+\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x2c\x63\x2c\x64\x29\x7b\x66\
+\x75\x6e\x63\x74\x69\x6f\x6e\x20\x67\x28\x29\x7b\x65\x2e\x71\x75\
+\x65\x75\x65\x3d\x3d\x3d\x21\x31\x26\x26\x66\x2e\x5f\x6d\x61\x72\
+\x6b\x28\x74\x68\x69\x73\x29\x3b\x76\x61\x72\x20\x62\x3d\x66\x2e\
+\x65\x78\x74\x65\x6e\x64\x28\x7b\x7d\x2c\x65\x29\x2c\x63\x3d\x74\
+\x68\x69\x73\x2e\x6e\x6f\x64\x65\x54\x79\x70\x65\x3d\x3d\x3d\x31\
+\x2c\x64\x3d\x63\x26\x26\x66\x28\x74\x68\x69\x73\x29\x2e\x69\x73\
+\x28\x22\x3a\x68\x69\x64\x64\x65\x6e\x22\x29\x2c\x67\x2c\x68\x2c\
+\x69\x2c\x6a\x2c\x6b\x2c\x6c\x2c\x6d\x2c\x6e\x2c\x6f\x3b\x62\x2e\
+\x61\x6e\x69\x6d\x61\x74\x65\x64\x50\x72\x6f\x70\x65\x72\x74\x69\
+\x65\x73\x3d\x7b\x7d\x3b\x66\x6f\x72\x28\x69\x20\x69\x6e\x20\x61\
+\x29\x7b\x67\x3d\x66\x2e\x63\x61\x6d\x65\x6c\x43\x61\x73\x65\x28\
+\x69\x29\x2c\x69\x21\x3d\x3d\x67\x26\x26\x28\x61\x5b\x67\x5d\x3d\
+\x61\x5b\x69\x5d\x2c\x64\x65\x6c\x65\x74\x65\x20\x61\x5b\x69\x5d\
+\x29\x2c\x68\x3d\x61\x5b\x67\x5d\x2c\x66\x2e\x69\x73\x41\x72\x72\
+\x61\x79\x28\x68\x29\x3f\x28\x62\x2e\x61\x6e\x69\x6d\x61\x74\x65\
+\x64\x50\x72\x6f\x70\x65\x72\x74\x69\x65\x73\x5b\x67\x5d\x3d\x68\
+\x5b\x31\x5d\x2c\x68\x3d\x61\x5b\x67\x5d\x3d\x68\x5b\x30\x5d\x29\
+\x3a\x62\x2e\x61\x6e\x69\x6d\x61\x74\x65\x64\x50\x72\x6f\x70\x65\
+\x72\x74\x69\x65\x73\x5b\x67\x5d\x3d\x62\x2e\x73\x70\x65\x63\x69\
+\x61\x6c\x45\x61\x73\x69\x6e\x67\x26\x26\x62\x2e\x73\x70\x65\x63\
+\x69\x61\x6c\x45\x61\x73\x69\x6e\x67\x5b\x67\x5d\x7c\x7c\x62\x2e\
+\x65\x61\x73\x69\x6e\x67\x7c\x7c\x22\x73\x77\x69\x6e\x67\x22\x3b\
+\x69\x66\x28\x68\x3d\x3d\x3d\x22\x68\x69\x64\x65\x22\x26\x26\x64\
+\x7c\x7c\x68\x3d\x3d\x3d\x22\x73\x68\x6f\x77\x22\x26\x26\x21\x64\
+\x29\x72\x65\x74\x75\x72\x6e\x20\x62\x2e\x63\x6f\x6d\x70\x6c\x65\
+\x74\x65\x2e\x63\x61\x6c\x6c\x28\x74\x68\x69\x73\x29\x3b\x63\x26\
+\x26\x28\x67\x3d\x3d\x3d\x22\x68\x65\x69\x67\x68\x74\x22\x7c\x7c\
+\x67\x3d\x3d\x3d\x22\x77\x69\x64\x74\x68\x22\x29\x26\x26\x28\x62\
+\x2e\x6f\x76\x65\x72\x66\x6c\x6f\x77\x3d\x5b\x74\x68\x69\x73\x2e\
+\x73\x74\x79\x6c\x65\x2e\x6f\x76\x65\x72\x66\x6c\x6f\x77\x2c\x74\
+\x68\x69\x73\x2e\x73\x74\x79\x6c\x65\x2e\x6f\x76\x65\x72\x66\x6c\
+\x6f\x77\x58\x2c\x74\x68\x69\x73\x2e\x73\x74\x79\x6c\x65\x2e\x6f\
+\x76\x65\x72\x66\x6c\x6f\x77\x59\x5d\x2c\x66\x2e\x63\x73\x73\x28\
+\x74\x68\x69\x73\x2c\x22\x64\x69\x73\x70\x6c\x61\x79\x22\x29\x3d\
+\x3d\x3d\x22\x69\x6e\x6c\x69\x6e\x65\x22\x26\x26\x66\x2e\x63\x73\
+\x73\x28\x74\x68\x69\x73\x2c\x22\x66\x6c\x6f\x61\x74\x22\x29\x3d\
+\x3d\x3d\x22\x6e\x6f\x6e\x65\x22\x26\x26\x28\x21\x66\x2e\x73\x75\
+\x70\x70\x6f\x72\x74\x2e\x69\x6e\x6c\x69\x6e\x65\x42\x6c\x6f\x63\
+\x6b\x4e\x65\x65\x64\x73\x4c\x61\x79\x6f\x75\x74\x7c\x7c\x63\x76\
+\x28\x74\x68\x69\x73\x2e\x6e\x6f\x64\x65\x4e\x61\x6d\x65\x29\x3d\
+\x3d\x3d\x22\x69\x6e\x6c\x69\x6e\x65\x22\x3f\x74\x68\x69\x73\x2e\
+\x73\x74\x79\x6c\x65\x2e\x64\x69\x73\x70\x6c\x61\x79\x3d\x22\x69\
+\x6e\x6c\x69\x6e\x65\x2d\x62\x6c\x6f\x63\x6b\x22\x3a\x74\x68\x69\
+\x73\x2e\x73\x74\x79\x6c\x65\x2e\x7a\x6f\x6f\x6d\x3d\x31\x29\x29\
+\x7d\x62\x2e\x6f\x76\x65\x72\x66\x6c\x6f\x77\x21\x3d\x6e\x75\x6c\
+\x6c\x26\x26\x28\x74\x68\x69\x73\x2e\x73\x74\x79\x6c\x65\x2e\x6f\
+\x76\x65\x72\x66\x6c\x6f\x77\x3d\x22\x68\x69\x64\x64\x65\x6e\x22\
+\x29\x3b\x66\x6f\x72\x28\x69\x20\x69\x6e\x20\x61\x29\x6a\x3d\x6e\
+\x65\x77\x20\x66\x2e\x66\x78\x28\x74\x68\x69\x73\x2c\x62\x2c\x69\
+\x29\x2c\x68\x3d\x61\x5b\x69\x5d\x2c\x63\x6e\x2e\x74\x65\x73\x74\
+\x28\x68\x29\x3f\x28\x6f\x3d\x66\x2e\x5f\x64\x61\x74\x61\x28\x74\
+\x68\x69\x73\x2c\x22\x74\x6f\x67\x67\x6c\x65\x22\x2b\x69\x29\x7c\
+\x7c\x28\x68\x3d\x3d\x3d\x22\x74\x6f\x67\x67\x6c\x65\x22\x3f\x64\
+\x3f\x22\x73\x68\x6f\x77\x22\x3a\x22\x68\x69\x64\x65\x22\x3a\x30\
+\x29\x2c\x6f\x3f\x28\x66\x2e\x5f\x64\x61\x74\x61\x28\x74\x68\x69\
+\x73\x2c\x22\x74\x6f\x67\x67\x6c\x65\x22\x2b\x69\x2c\x6f\x3d\x3d\
+\x3d\x22\x73\x68\x6f\x77\x22\x3f\x22\x68\x69\x64\x65\x22\x3a\x22\
+\x73\x68\x6f\x77\x22\x29\x2c\x6a\x5b\x6f\x5d\x28\x29\x29\x3a\x6a\
+\x5b\x68\x5d\x28\x29\x29\x3a\x28\x6b\x3d\x63\x6f\x2e\x65\x78\x65\
+\x63\x28\x68\x29\x2c\x6c\x3d\x6a\x2e\x63\x75\x72\x28\x29\x2c\x6b\
+\x3f\x28\x6d\x3d\x70\x61\x72\x73\x65\x46\x6c\x6f\x61\x74\x28\x6b\
+\x5b\x32\x5d\x29\x2c\x6e\x3d\x6b\x5b\x33\x5d\x7c\x7c\x28\x66\x2e\
+\x63\x73\x73\x4e\x75\x6d\x62\x65\x72\x5b\x69\x5d\x3f\x22\x22\x3a\
+\x22\x70\x78\x22\x29\x2c\x6e\x21\x3d\x3d\x22\x70\x78\x22\x26\x26\
+\x28\x66\x2e\x73\x74\x79\x6c\x65\x28\x74\x68\x69\x73\x2c\x69\x2c\
+\x28\x6d\x7c\x7c\x31\x29\x2b\x6e\x29\x2c\x6c\x3d\x28\x6d\x7c\x7c\
+\x31\x29\x2f\x6a\x2e\x63\x75\x72\x28\x29\x2a\x6c\x2c\x66\x2e\x73\
+\x74\x79\x6c\x65\x28\x74\x68\x69\x73\x2c\x69\x2c\x6c\x2b\x6e\x29\
+\x29\x2c\x6b\x5b\x31\x5d\x26\x26\x28\x6d\x3d\x28\x6b\x5b\x31\x5d\
+\x3d\x3d\x3d\x22\x2d\x3d\x22\x3f\x2d\x31\x3a\x31\x29\x2a\x6d\x2b\
+\x6c\x29\x2c\x6a\x2e\x63\x75\x73\x74\x6f\x6d\x28\x6c\x2c\x6d\x2c\
+\x6e\x29\x29\x3a\x6a\x2e\x63\x75\x73\x74\x6f\x6d\x28\x6c\x2c\x68\
+\x2c\x22\x22\x29\x29\x3b\x72\x65\x74\x75\x72\x6e\x21\x30\x7d\x76\
+\x61\x72\x20\x65\x3d\x66\x2e\x73\x70\x65\x65\x64\x28\x62\x2c\x63\
+\x2c\x64\x29\x3b\x69\x66\x28\x66\x2e\x69\x73\x45\x6d\x70\x74\x79\
+\x4f\x62\x6a\x65\x63\x74\x28\x61\x29\x29\x72\x65\x74\x75\x72\x6e\
+\x20\x74\x68\x69\x73\x2e\x65\x61\x63\x68\x28\x65\x2e\x63\x6f\x6d\
+\x70\x6c\x65\x74\x65\x2c\x5b\x21\x31\x5d\x29\x3b\x61\x3d\x66\x2e\
+\x65\x78\x74\x65\x6e\x64\x28\x7b\x7d\x2c\x61\x29\x3b\x72\x65\x74\
+\x75\x72\x6e\x20\x65\x2e\x71\x75\x65\x75\x65\x3d\x3d\x3d\x21\x31\
+\x3f\x74\x68\x69\x73\x2e\x65\x61\x63\x68\x28\x67\x29\x3a\x74\x68\
+\x69\x73\x2e\x71\x75\x65\x75\x65\x28\x65\x2e\x71\x75\x65\x75\x65\
+\x2c\x67\x29\x7d\x2c\x73\x74\x6f\x70\x3a\x66\x75\x6e\x63\x74\x69\
+\x6f\x6e\x28\x61\x2c\x63\x2c\x64\x29\x7b\x74\x79\x70\x65\x6f\x66\
+\x20\x61\x21\x3d\x22\x73\x74\x72\x69\x6e\x67\x22\x26\x26\x28\x64\
+\x3d\x63\x2c\x63\x3d\x61\x2c\x61\x3d\x62\x29\x2c\x63\x26\x26\x61\
+\x21\x3d\x3d\x21\x31\x26\x26\x74\x68\x69\x73\x2e\x71\x75\x65\x75\
+\x65\x28\x61\x7c\x7c\x22\x66\x78\x22\x2c\x5b\x5d\x29\x3b\x72\x65\
+\x74\x75\x72\x6e\x20\x74\x68\x69\x73\x2e\x65\x61\x63\x68\x28\x66\
+\x75\x6e\x63\x74\x69\x6f\x6e\x28\x29\x7b\x66\x75\x6e\x63\x74\x69\
+\x6f\x6e\x20\x68\x28\x61\x2c\x62\x2c\x63\x29\x7b\x76\x61\x72\x20\
+\x65\x3d\x62\x5b\x63\x5d\x3b\x66\x2e\x72\x65\x6d\x6f\x76\x65\x44\
+\x61\x74\x61\x28\x61\x2c\x63\x2c\x21\x30\x29\x2c\x65\x2e\x73\x74\
+\x6f\x70\x28\x64\x29\x7d\x76\x61\x72\x20\x62\x2c\x63\x3d\x21\x31\
+\x2c\x65\x3d\x66\x2e\x74\x69\x6d\x65\x72\x73\x2c\x67\x3d\x66\x2e\
+\x5f\x64\x61\x74\x61\x28\x74\x68\x69\x73\x29\x3b\x64\x7c\x7c\x66\
+\x2e\x5f\x75\x6e\x6d\x61\x72\x6b\x28\x21\x30\x2c\x74\x68\x69\x73\
+\x29\x3b\x69\x66\x28\x61\x3d\x3d\x6e\x75\x6c\x6c\x29\x66\x6f\x72\
+\x28\x62\x20\x69\x6e\x20\x67\x29\x67\x5b\x62\x5d\x26\x26\x67\x5b\
+\x62\x5d\x2e\x73\x74\x6f\x70\x26\x26\x62\x2e\x69\x6e\x64\x65\x78\
+\x4f\x66\x28\x22\x2e\x72\x75\x6e\x22\x29\x3d\x3d\x3d\x62\x2e\x6c\
+\x65\x6e\x67\x74\x68\x2d\x34\x26\x26\x68\x28\x74\x68\x69\x73\x2c\
+\x67\x2c\x62\x29\x3b\x65\x6c\x73\x65\x20\x67\x5b\x62\x3d\x61\x2b\
+\x22\x2e\x72\x75\x6e\x22\x5d\x26\x26\x67\x5b\x62\x5d\x2e\x73\x74\
+\x6f\x70\x26\x26\x68\x28\x74\x68\x69\x73\x2c\x67\x2c\x62\x29\x3b\
+\x66\x6f\x72\x28\x62\x3d\x65\x2e\x6c\x65\x6e\x67\x74\x68\x3b\x62\
+\x2d\x2d\x3b\x29\x65\x5b\x62\x5d\x2e\x65\x6c\x65\x6d\x3d\x3d\x3d\
+\x74\x68\x69\x73\x26\x26\x28\x61\x3d\x3d\x6e\x75\x6c\x6c\x7c\x7c\
+\x65\x5b\x62\x5d\x2e\x71\x75\x65\x75\x65\x3d\x3d\x3d\x61\x29\x26\
+\x26\x28\x64\x3f\x65\x5b\x62\x5d\x28\x21\x30\x29\x3a\x65\x5b\x62\
+\x5d\x2e\x73\x61\x76\x65\x53\x74\x61\x74\x65\x28\x29\x2c\x63\x3d\
+\x21\x30\x2c\x65\x2e\x73\x70\x6c\x69\x63\x65\x28\x62\x2c\x31\x29\
+\x29\x3b\x28\x21\x64\x7c\x7c\x21\x63\x29\x26\x26\x66\x2e\x64\x65\
+\x71\x75\x65\x75\x65\x28\x74\x68\x69\x73\x2c\x61\x29\x7d\x29\x7d\
+\x7d\x29\x2c\x66\x2e\x65\x61\x63\x68\x28\x7b\x73\x6c\x69\x64\x65\
+\x44\x6f\x77\x6e\x3a\x63\x75\x28\x22\x73\x68\x6f\x77\x22\x2c\x31\
+\x29\x2c\x73\x6c\x69\x64\x65\x55\x70\x3a\x63\x75\x28\x22\x68\x69\
+\x64\x65\x22\x2c\x31\x29\x2c\x73\x6c\x69\x64\x65\x54\x6f\x67\x67\
+\x6c\x65\x3a\x63\x75\x28\x22\x74\x6f\x67\x67\x6c\x65\x22\x2c\x31\
+\x29\x2c\x66\x61\x64\x65\x49\x6e\x3a\x7b\x6f\x70\x61\x63\x69\x74\
+\x79\x3a\x22\x73\x68\x6f\x77\x22\x7d\x2c\x66\x61\x64\x65\x4f\x75\
+\x74\x3a\x7b\x6f\x70\x61\x63\x69\x74\x79\x3a\x22\x68\x69\x64\x65\
+\x22\x7d\x2c\x66\x61\x64\x65\x54\x6f\x67\x67\x6c\x65\x3a\x7b\x6f\
+\x70\x61\x63\x69\x74\x79\x3a\x22\x74\x6f\x67\x67\x6c\x65\x22\x7d\
+\x7d\x2c\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x29\x7b\
+\x66\x2e\x66\x6e\x5b\x61\x5d\x3d\x66\x75\x6e\x63\x74\x69\x6f\x6e\
+\x28\x61\x2c\x63\x2c\x64\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x74\
+\x68\x69\x73\x2e\x61\x6e\x69\x6d\x61\x74\x65\x28\x62\x2c\x61\x2c\
+\x63\x2c\x64\x29\x7d\x7d\x29\x2c\x66\x2e\x65\x78\x74\x65\x6e\x64\
+\x28\x7b\x73\x70\x65\x65\x64\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\
+\x28\x61\x2c\x62\x2c\x63\x29\x7b\x76\x61\x72\x20\x64\x3d\x61\x26\
+\x26\x74\x79\x70\x65\x6f\x66\x20\x61\x3d\x3d\x22\x6f\x62\x6a\x65\
+\x63\x74\x22\x3f\x66\x2e\x65\x78\x74\x65\x6e\x64\x28\x7b\x7d\x2c\
+\x61\x29\x3a\x7b\x63\x6f\x6d\x70\x6c\x65\x74\x65\x3a\x63\x7c\x7c\
+\x21\x63\x26\x26\x62\x7c\x7c\x66\x2e\x69\x73\x46\x75\x6e\x63\x74\
+\x69\x6f\x6e\x28\x61\x29\x26\x26\x61\x2c\x64\x75\x72\x61\x74\x69\
+\x6f\x6e\x3a\x61\x2c\x65\x61\x73\x69\x6e\x67\x3a\x63\x26\x26\x62\
+\x7c\x7c\x62\x26\x26\x21\x66\x2e\x69\x73\x46\x75\x6e\x63\x74\x69\
+\x6f\x6e\x28\x62\x29\x26\x26\x62\x7d\x3b\x64\x2e\x64\x75\x72\x61\
+\x74\x69\x6f\x6e\x3d\x66\x2e\x66\x78\x2e\x6f\x66\x66\x3f\x30\x3a\
+\x74\x79\x70\x65\x6f\x66\x20\x64\x2e\x64\x75\x72\x61\x74\x69\x6f\
+\x6e\x3d\x3d\x22\x6e\x75\x6d\x62\x65\x72\x22\x3f\x64\x2e\x64\x75\
+\x72\x61\x74\x69\x6f\x6e\x3a\x64\x2e\x64\x75\x72\x61\x74\x69\x6f\
+\x6e\x20\x69\x6e\x20\x66\x2e\x66\x78\x2e\x73\x70\x65\x65\x64\x73\
+\x3f\x66\x2e\x66\x78\x2e\x73\x70\x65\x65\x64\x73\x5b\x64\x2e\x64\
+\x75\x72\x61\x74\x69\x6f\x6e\x5d\x3a\x66\x2e\x66\x78\x2e\x73\x70\
+\x65\x65\x64\x73\x2e\x5f\x64\x65\x66\x61\x75\x6c\x74\x3b\x69\x66\
+\x28\x64\x2e\x71\x75\x65\x75\x65\x3d\x3d\x6e\x75\x6c\x6c\x7c\x7c\
+\x64\x2e\x71\x75\x65\x75\x65\x3d\x3d\x3d\x21\x30\x29\x64\x2e\x71\
+\x75\x65\x75\x65\x3d\x22\x66\x78\x22\x3b\x64\x2e\x6f\x6c\x64\x3d\
+\x64\x2e\x63\x6f\x6d\x70\x6c\x65\x74\x65\x2c\x64\x2e\x63\x6f\x6d\
+\x70\x6c\x65\x74\x65\x3d\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\
+\x29\x7b\x66\x2e\x69\x73\x46\x75\x6e\x63\x74\x69\x6f\x6e\x28\x64\
+\x2e\x6f\x6c\x64\x29\x26\x26\x64\x2e\x6f\x6c\x64\x2e\x63\x61\x6c\
+\x6c\x28\x74\x68\x69\x73\x29\x2c\x64\x2e\x71\x75\x65\x75\x65\x3f\
+\x66\x2e\x64\x65\x71\x75\x65\x75\x65\x28\x74\x68\x69\x73\x2c\x64\
+\x2e\x71\x75\x65\x75\x65\x29\x3a\x61\x21\x3d\x3d\x21\x31\x26\x26\
+\x66\x2e\x5f\x75\x6e\x6d\x61\x72\x6b\x28\x74\x68\x69\x73\x29\x7d\
+\x3b\x72\x65\x74\x75\x72\x6e\x20\x64\x7d\x2c\x65\x61\x73\x69\x6e\
+\x67\x3a\x7b\x6c\x69\x6e\x65\x61\x72\x3a\x66\x75\x6e\x63\x74\x69\
+\x6f\x6e\x28\x61\x2c\x62\x2c\x63\x2c\x64\x29\x7b\x72\x65\x74\x75\
+\x72\x6e\x20\x63\x2b\x64\x2a\x61\x7d\x2c\x73\x77\x69\x6e\x67\x3a\
+\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x62\x2c\x63\x2c\x64\
+\x29\x7b\x72\x65\x74\x75\x72\x6e\x28\x2d\x4d\x61\x74\x68\x2e\x63\
+\x6f\x73\x28\x61\x2a\x4d\x61\x74\x68\x2e\x50\x49\x29\x2f\x32\x2b\
+\x2e\x35\x29\x2a\x64\x2b\x63\x7d\x7d\x2c\x74\x69\x6d\x65\x72\x73\
+\x3a\x5b\x5d\x2c\x66\x78\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\
+\x61\x2c\x62\x2c\x63\x29\x7b\x74\x68\x69\x73\x2e\x6f\x70\x74\x69\
+\x6f\x6e\x73\x3d\x62\x2c\x74\x68\x69\x73\x2e\x65\x6c\x65\x6d\x3d\
+\x61\x2c\x74\x68\x69\x73\x2e\x70\x72\x6f\x70\x3d\x63\x2c\x62\x2e\
+\x6f\x72\x69\x67\x3d\x62\x2e\x6f\x72\x69\x67\x7c\x7c\x7b\x7d\x7d\
+\x7d\x29\x2c\x66\x2e\x66\x78\x2e\x70\x72\x6f\x74\x6f\x74\x79\x70\
+\x65\x3d\x7b\x75\x70\x64\x61\x74\x65\x3a\x66\x75\x6e\x63\x74\x69\
+\x6f\x6e\x28\x29\x7b\x74\x68\x69\x73\x2e\x6f\x70\x74\x69\x6f\x6e\
+\x73\x2e\x73\x74\x65\x70\x26\x26\x74\x68\x69\x73\x2e\x6f\x70\x74\
+\x69\x6f\x6e\x73\x2e\x73\x74\x65\x70\x2e\x63\x61\x6c\x6c\x28\x74\
+\x68\x69\x73\x2e\x65\x6c\x65\x6d\x2c\x74\x68\x69\x73\x2e\x6e\x6f\
+\x77\x2c\x74\x68\x69\x73\x29\x2c\x28\x66\x2e\x66\x78\x2e\x73\x74\
+\x65\x70\x5b\x74\x68\x69\x73\x2e\x70\x72\x6f\x70\x5d\x7c\x7c\x66\
+\x2e\x66\x78\x2e\x73\x74\x65\x70\x2e\x5f\x64\x65\x66\x61\x75\x6c\
+\x74\x29\x28\x74\x68\x69\x73\x29\x7d\x2c\x63\x75\x72\x3a\x66\x75\
+\x6e\x63\x74\x69\x6f\x6e\x28\x29\x7b\x69\x66\x28\x74\x68\x69\x73\
+\x2e\x65\x6c\x65\x6d\x5b\x74\x68\x69\x73\x2e\x70\x72\x6f\x70\x5d\
+\x21\x3d\x6e\x75\x6c\x6c\x26\x26\x28\x21\x74\x68\x69\x73\x2e\x65\
+\x6c\x65\x6d\x2e\x73\x74\x79\x6c\x65\x7c\x7c\x74\x68\x69\x73\x2e\
+\x65\x6c\x65\x6d\x2e\x73\x74\x79\x6c\x65\x5b\x74\x68\x69\x73\x2e\
+\x70\x72\x6f\x70\x5d\x3d\x3d\x6e\x75\x6c\x6c\x29\x29\x72\x65\x74\
+\x75\x72\x6e\x20\x74\x68\x69\x73\x2e\x65\x6c\x65\x6d\x5b\x74\x68\
+\x69\x73\x2e\x70\x72\x6f\x70\x5d\x3b\x76\x61\x72\x20\x61\x2c\x62\
+\x3d\x66\x2e\x63\x73\x73\x28\x74\x68\x69\x73\x2e\x65\x6c\x65\x6d\
+\x2c\x74\x68\x69\x73\x2e\x70\x72\x6f\x70\x29\x3b\x72\x65\x74\x75\
+\x72\x6e\x20\x69\x73\x4e\x61\x4e\x28\x61\x3d\x70\x61\x72\x73\x65\
+\x46\x6c\x6f\x61\x74\x28\x62\x29\x29\x3f\x21\x62\x7c\x7c\x62\x3d\
+\x3d\x3d\x22\x61\x75\x74\x6f\x22\x3f\x30\x3a\x62\x3a\x61\x7d\x2c\
+\x63\x75\x73\x74\x6f\x6d\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\
+\x61\x2c\x63\x2c\x64\x29\x7b\x66\x75\x6e\x63\x74\x69\x6f\x6e\x20\
+\x68\x28\x61\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x65\x2e\x73\x74\
+\x65\x70\x28\x61\x29\x7d\x76\x61\x72\x20\x65\x3d\x74\x68\x69\x73\
+\x2c\x67\x3d\x66\x2e\x66\x78\x3b\x74\x68\x69\x73\x2e\x73\x74\x61\
+\x72\x74\x54\x69\x6d\x65\x3d\x63\x72\x7c\x7c\x63\x73\x28\x29\x2c\
+\x74\x68\x69\x73\x2e\x65\x6e\x64\x3d\x63\x2c\x74\x68\x69\x73\x2e\
+\x6e\x6f\x77\x3d\x74\x68\x69\x73\x2e\x73\x74\x61\x72\x74\x3d\x61\
+\x2c\x74\x68\x69\x73\x2e\x70\x6f\x73\x3d\x74\x68\x69\x73\x2e\x73\
+\x74\x61\x74\x65\x3d\x30\x2c\x74\x68\x69\x73\x2e\x75\x6e\x69\x74\
+\x3d\x64\x7c\x7c\x74\x68\x69\x73\x2e\x75\x6e\x69\x74\x7c\x7c\x28\
+\x66\x2e\x63\x73\x73\x4e\x75\x6d\x62\x65\x72\x5b\x74\x68\x69\x73\
+\x2e\x70\x72\x6f\x70\x5d\x3f\x22\x22\x3a\x22\x70\x78\x22\x29\x2c\
+\x68\x2e\x71\x75\x65\x75\x65\x3d\x74\x68\x69\x73\x2e\x6f\x70\x74\
+\x69\x6f\x6e\x73\x2e\x71\x75\x65\x75\x65\x2c\x68\x2e\x65\x6c\x65\
+\x6d\x3d\x74\x68\x69\x73\x2e\x65\x6c\x65\x6d\x2c\x68\x2e\x73\x61\
+\x76\x65\x53\x74\x61\x74\x65\x3d\x66\x75\x6e\x63\x74\x69\x6f\x6e\
+\x28\x29\x7b\x65\x2e\x6f\x70\x74\x69\x6f\x6e\x73\x2e\x68\x69\x64\
+\x65\x26\x26\x66\x2e\x5f\x64\x61\x74\x61\x28\x65\x2e\x65\x6c\x65\
+\x6d\x2c\x22\x66\x78\x73\x68\x6f\x77\x22\x2b\x65\x2e\x70\x72\x6f\
+\x70\x29\x3d\x3d\x3d\x62\x26\x26\x66\x2e\x5f\x64\x61\x74\x61\x28\
+\x65\x2e\x65\x6c\x65\x6d\x2c\x22\x66\x78\x73\x68\x6f\x77\x22\x2b\
+\x65\x2e\x70\x72\x6f\x70\x2c\x65\x2e\x73\x74\x61\x72\x74\x29\x7d\
+\x2c\x68\x28\x29\x26\x26\x66\x2e\x74\x69\x6d\x65\x72\x73\x2e\x70\
+\x75\x73\x68\x28\x68\x29\x26\x26\x21\x63\x70\x26\x26\x28\x63\x70\
+\x3d\x73\x65\x74\x49\x6e\x74\x65\x72\x76\x61\x6c\x28\x67\x2e\x74\
+\x69\x63\x6b\x2c\x67\x2e\x69\x6e\x74\x65\x72\x76\x61\x6c\x29\x29\
+\x7d\x2c\x73\x68\x6f\x77\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\
+\x29\x7b\x76\x61\x72\x20\x61\x3d\x66\x2e\x5f\x64\x61\x74\x61\x28\
+\x74\x68\x69\x73\x2e\x65\x6c\x65\x6d\x2c\x22\x66\x78\x73\x68\x6f\
+\x77\x22\x2b\x74\x68\x69\x73\x2e\x70\x72\x6f\x70\x29\x3b\x74\x68\
+\x69\x73\x2e\x6f\x70\x74\x69\x6f\x6e\x73\x2e\x6f\x72\x69\x67\x5b\
+\x74\x68\x69\x73\x2e\x70\x72\x6f\x70\x5d\x3d\x61\x7c\x7c\x66\x2e\
+\x73\x74\x79\x6c\x65\x28\x74\x68\x69\x73\x2e\x65\x6c\x65\x6d\x2c\
+\x74\x68\x69\x73\x2e\x70\x72\x6f\x70\x29\x2c\x74\x68\x69\x73\x2e\
+\x6f\x70\x74\x69\x6f\x6e\x73\x2e\x73\x68\x6f\x77\x3d\x21\x30\x2c\
+\x61\x21\x3d\x3d\x62\x3f\x74\x68\x69\x73\x2e\x63\x75\x73\x74\x6f\
+\x6d\x28\x74\x68\x69\x73\x2e\x63\x75\x72\x28\x29\x2c\x61\x29\x3a\
+\x74\x68\x69\x73\x2e\x63\x75\x73\x74\x6f\x6d\x28\x74\x68\x69\x73\
+\x2e\x70\x72\x6f\x70\x3d\x3d\x3d\x22\x77\x69\x64\x74\x68\x22\x7c\
+\x7c\x74\x68\x69\x73\x2e\x70\x72\x6f\x70\x3d\x3d\x3d\x22\x68\x65\
+\x69\x67\x68\x74\x22\x3f\x31\x3a\x30\x2c\x74\x68\x69\x73\x2e\x63\
+\x75\x72\x28\x29\x29\x2c\x66\x28\x74\x68\x69\x73\x2e\x65\x6c\x65\
+\x6d\x29\x2e\x73\x68\x6f\x77\x28\x29\x7d\x2c\x68\x69\x64\x65\x3a\
+\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x29\x7b\x74\x68\x69\x73\x2e\
+\x6f\x70\x74\x69\x6f\x6e\x73\x2e\x6f\x72\x69\x67\x5b\x74\x68\x69\
+\x73\x2e\x70\x72\x6f\x70\x5d\x3d\x66\x2e\x5f\x64\x61\x74\x61\x28\
+\x74\x68\x69\x73\x2e\x65\x6c\x65\x6d\x2c\x22\x66\x78\x73\x68\x6f\
+\x77\x22\x2b\x74\x68\x69\x73\x2e\x70\x72\x6f\x70\x29\x7c\x7c\x66\
+\x2e\x73\x74\x79\x6c\x65\x28\x74\x68\x69\x73\x2e\x65\x6c\x65\x6d\
+\x2c\x74\x68\x69\x73\x2e\x70\x72\x6f\x70\x29\x2c\x74\x68\x69\x73\
+\x2e\x6f\x70\x74\x69\x6f\x6e\x73\x2e\x68\x69\x64\x65\x3d\x21\x30\
+\x2c\x74\x68\x69\x73\x2e\x63\x75\x73\x74\x6f\x6d\x28\x74\x68\x69\
+\x73\x2e\x63\x75\x72\x28\x29\x2c\x30\x29\x7d\x2c\x73\x74\x65\x70\
+\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x76\x61\x72\
+\x20\x62\x2c\x63\x2c\x64\x2c\x65\x3d\x63\x72\x7c\x7c\x63\x73\x28\
+\x29\x2c\x67\x3d\x21\x30\x2c\x68\x3d\x74\x68\x69\x73\x2e\x65\x6c\
+\x65\x6d\x2c\x69\x3d\x74\x68\x69\x73\x2e\x6f\x70\x74\x69\x6f\x6e\
+\x73\x3b\x69\x66\x28\x61\x7c\x7c\x65\x3e\x3d\x69\x2e\x64\x75\x72\
+\x61\x74\x69\x6f\x6e\x2b\x74\x68\x69\x73\x2e\x73\x74\x61\x72\x74\
+\x54\x69\x6d\x65\x29\x7b\x74\x68\x69\x73\x2e\x6e\x6f\x77\x3d\x74\
+\x68\x69\x73\x2e\x65\x6e\x64\x2c\x74\x68\x69\x73\x2e\x70\x6f\x73\
+\x3d\x74\x68\x69\x73\x2e\x73\x74\x61\x74\x65\x3d\x31\x2c\x74\x68\
+\x69\x73\x2e\x75\x70\x64\x61\x74\x65\x28\x29\x2c\x69\x2e\x61\x6e\
+\x69\x6d\x61\x74\x65\x64\x50\x72\x6f\x70\x65\x72\x74\x69\x65\x73\
+\x5b\x74\x68\x69\x73\x2e\x70\x72\x6f\x70\x5d\x3d\x21\x30\x3b\x66\
+\x6f\x72\x28\x62\x20\x69\x6e\x20\x69\x2e\x61\x6e\x69\x6d\x61\x74\
+\x65\x64\x50\x72\x6f\x70\x65\x72\x74\x69\x65\x73\x29\x69\x2e\x61\
+\x6e\x69\x6d\x61\x74\x65\x64\x50\x72\x6f\x70\x65\x72\x74\x69\x65\
+\x73\x5b\x62\x5d\x21\x3d\x3d\x21\x30\x26\x26\x28\x67\x3d\x21\x31\
+\x29\x3b\x69\x66\x28\x67\x29\x7b\x69\x2e\x6f\x76\x65\x72\x66\x6c\
+\x6f\x77\x21\x3d\x6e\x75\x6c\x6c\x26\x26\x21\x66\x2e\x73\x75\x70\
+\x70\x6f\x72\x74\x2e\x73\x68\x72\x69\x6e\x6b\x57\x72\x61\x70\x42\
+\x6c\x6f\x63\x6b\x73\x26\x26\x66\x2e\x65\x61\x63\x68\x28\x5b\x22\
+\x22\x2c\x22\x58\x22\x2c\x22\x59\x22\x5d\x2c\x66\x75\x6e\x63\x74\
+\x69\x6f\x6e\x28\x61\x2c\x62\x29\x7b\x68\x2e\x73\x74\x79\x6c\x65\
+\x5b\x22\x6f\x76\x65\x72\x66\x6c\x6f\x77\x22\x2b\x62\x5d\x3d\x69\
+\x2e\x6f\x76\x65\x72\x66\x6c\x6f\x77\x5b\x61\x5d\x7d\x29\x2c\x69\
+\x2e\x68\x69\x64\x65\x26\x26\x66\x28\x68\x29\x2e\x68\x69\x64\x65\
+\x28\x29\x3b\x69\x66\x28\x69\x2e\x68\x69\x64\x65\x7c\x7c\x69\x2e\
+\x73\x68\x6f\x77\x29\x66\x6f\x72\x28\x62\x20\x69\x6e\x20\x69\x2e\
+\x61\x6e\x69\x6d\x61\x74\x65\x64\x50\x72\x6f\x70\x65\x72\x74\x69\
+\x65\x73\x29\x66\x2e\x73\x74\x79\x6c\x65\x28\x68\x2c\x62\x2c\x69\
+\x2e\x6f\x72\x69\x67\x5b\x62\x5d\x29\x2c\x66\x2e\x72\x65\x6d\x6f\
+\x76\x65\x44\x61\x74\x61\x28\x68\x2c\x22\x66\x78\x73\x68\x6f\x77\
+\x22\x2b\x62\x2c\x21\x30\x29\x2c\x66\x2e\x72\x65\x6d\x6f\x76\x65\
+\x44\x61\x74\x61\x28\x68\x2c\x22\x74\x6f\x67\x67\x6c\x65\x22\x2b\
+\x62\x2c\x21\x30\x29\x3b\x64\x3d\x69\x2e\x63\x6f\x6d\x70\x6c\x65\
+\x74\x65\x2c\x64\x26\x26\x28\x69\x2e\x63\x6f\x6d\x70\x6c\x65\x74\
+\x65\x3d\x21\x31\x2c\x64\x2e\x63\x61\x6c\x6c\x28\x68\x29\x29\x7d\
+\x72\x65\x74\x75\x72\x6e\x21\x31\x7d\x69\x2e\x64\x75\x72\x61\x74\
+\x69\x6f\x6e\x3d\x3d\x49\x6e\x66\x69\x6e\x69\x74\x79\x3f\x74\x68\
+\x69\x73\x2e\x6e\x6f\x77\x3d\x65\x3a\x28\x63\x3d\x65\x2d\x74\x68\
+\x69\x73\x2e\x73\x74\x61\x72\x74\x54\x69\x6d\x65\x2c\x74\x68\x69\
+\x73\x2e\x73\x74\x61\x74\x65\x3d\x63\x2f\x69\x2e\x64\x75\x72\x61\
+\x74\x69\x6f\x6e\x2c\x74\x68\x69\x73\x2e\x70\x6f\x73\x3d\x66\x2e\
+\x65\x61\x73\x69\x6e\x67\x5b\x69\x2e\x61\x6e\x69\x6d\x61\x74\x65\
+\x64\x50\x72\x6f\x70\x65\x72\x74\x69\x65\x73\x5b\x74\x68\x69\x73\
+\x2e\x70\x72\x6f\x70\x5d\x5d\x28\x74\x68\x69\x73\x2e\x73\x74\x61\
+\x74\x65\x2c\x63\x2c\x30\x2c\x31\x2c\x69\x2e\x64\x75\x72\x61\x74\
+\x69\x6f\x6e\x29\x2c\x74\x68\x69\x73\x2e\x6e\x6f\x77\x3d\x74\x68\
+\x69\x73\x2e\x73\x74\x61\x72\x74\x2b\x28\x74\x68\x69\x73\x2e\x65\
+\x6e\x64\x2d\x74\x68\x69\x73\x2e\x73\x74\x61\x72\x74\x29\x2a\x74\
+\x68\x69\x73\x2e\x70\x6f\x73\x29\x2c\x74\x68\x69\x73\x2e\x75\x70\
+\x64\x61\x74\x65\x28\x29\x3b\x72\x65\x74\x75\x72\x6e\x21\x30\x7d\
+\x7d\x2c\x66\x2e\x65\x78\x74\x65\x6e\x64\x28\x66\x2e\x66\x78\x2c\
+\x7b\x74\x69\x63\x6b\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x29\
+\x7b\x76\x61\x72\x20\x61\x2c\x62\x3d\x66\x2e\x74\x69\x6d\x65\x72\
+\x73\x2c\x63\x3d\x30\x3b\x66\x6f\x72\x28\x3b\x63\x3c\x62\x2e\x6c\
+\x65\x6e\x67\x74\x68\x3b\x63\x2b\x2b\x29\x61\x3d\x62\x5b\x63\x5d\
+\x2c\x21\x61\x28\x29\x26\x26\x62\x5b\x63\x5d\x3d\x3d\x3d\x61\x26\
+\x26\x62\x2e\x73\x70\x6c\x69\x63\x65\x28\x63\x2d\x2d\x2c\x31\x29\
+\x3b\x62\x2e\x6c\x65\x6e\x67\x74\x68\x7c\x7c\x66\x2e\x66\x78\x2e\
+\x73\x74\x6f\x70\x28\x29\x7d\x2c\x69\x6e\x74\x65\x72\x76\x61\x6c\
+\x3a\x31\x33\x2c\x73\x74\x6f\x70\x3a\x66\x75\x6e\x63\x74\x69\x6f\
+\x6e\x28\x29\x7b\x63\x6c\x65\x61\x72\x49\x6e\x74\x65\x72\x76\x61\
+\x6c\x28\x63\x70\x29\x2c\x63\x70\x3d\x6e\x75\x6c\x6c\x7d\x2c\x73\
+\x70\x65\x65\x64\x73\x3a\x7b\x73\x6c\x6f\x77\x3a\x36\x30\x30\x2c\
+\x66\x61\x73\x74\x3a\x32\x30\x30\x2c\x5f\x64\x65\x66\x61\x75\x6c\
+\x74\x3a\x34\x30\x30\x7d\x2c\x73\x74\x65\x70\x3a\x7b\x6f\x70\x61\
+\x63\x69\x74\x79\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\
+\x7b\x66\x2e\x73\x74\x79\x6c\x65\x28\x61\x2e\x65\x6c\x65\x6d\x2c\
+\x22\x6f\x70\x61\x63\x69\x74\x79\x22\x2c\x61\x2e\x6e\x6f\x77\x29\
+\x7d\x2c\x5f\x64\x65\x66\x61\x75\x6c\x74\x3a\x66\x75\x6e\x63\x74\
+\x69\x6f\x6e\x28\x61\x29\x7b\x61\x2e\x65\x6c\x65\x6d\x2e\x73\x74\
+\x79\x6c\x65\x26\x26\x61\x2e\x65\x6c\x65\x6d\x2e\x73\x74\x79\x6c\
+\x65\x5b\x61\x2e\x70\x72\x6f\x70\x5d\x21\x3d\x6e\x75\x6c\x6c\x3f\
+\x61\x2e\x65\x6c\x65\x6d\x2e\x73\x74\x79\x6c\x65\x5b\x61\x2e\x70\
+\x72\x6f\x70\x5d\x3d\x61\x2e\x6e\x6f\x77\x2b\x61\x2e\x75\x6e\x69\
+\x74\x3a\x61\x2e\x65\x6c\x65\x6d\x5b\x61\x2e\x70\x72\x6f\x70\x5d\
+\x3d\x61\x2e\x6e\x6f\x77\x7d\x7d\x7d\x29\x2c\x66\x2e\x65\x61\x63\
+\x68\x28\x5b\x22\x77\x69\x64\x74\x68\x22\x2c\x22\x68\x65\x69\x67\
+\x68\x74\x22\x5d\x2c\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\
+\x62\x29\x7b\x66\x2e\x66\x78\x2e\x73\x74\x65\x70\x5b\x62\x5d\x3d\
+\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x66\x2e\x73\x74\
+\x79\x6c\x65\x28\x61\x2e\x65\x6c\x65\x6d\x2c\x62\x2c\x4d\x61\x74\
+\x68\x2e\x6d\x61\x78\x28\x30\x2c\x61\x2e\x6e\x6f\x77\x29\x2b\x61\
+\x2e\x75\x6e\x69\x74\x29\x7d\x7d\x29\x2c\x66\x2e\x65\x78\x70\x72\
+\x26\x26\x66\x2e\x65\x78\x70\x72\x2e\x66\x69\x6c\x74\x65\x72\x73\
+\x26\x26\x28\x66\x2e\x65\x78\x70\x72\x2e\x66\x69\x6c\x74\x65\x72\
+\x73\x2e\x61\x6e\x69\x6d\x61\x74\x65\x64\x3d\x66\x75\x6e\x63\x74\
+\x69\x6f\x6e\x28\x61\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x66\x2e\
+\x67\x72\x65\x70\x28\x66\x2e\x74\x69\x6d\x65\x72\x73\x2c\x66\x75\
+\x6e\x63\x74\x69\x6f\x6e\x28\x62\x29\x7b\x72\x65\x74\x75\x72\x6e\
+\x20\x61\x3d\x3d\x3d\x62\x2e\x65\x6c\x65\x6d\x7d\x29\x2e\x6c\x65\
+\x6e\x67\x74\x68\x7d\x29\x3b\x76\x61\x72\x20\x63\x77\x3d\x2f\x5e\
+\x74\x28\x3f\x3a\x61\x62\x6c\x65\x7c\x64\x7c\x68\x29\x24\x2f\x69\
+\x2c\x63\x78\x3d\x2f\x5e\x28\x3f\x3a\x62\x6f\x64\x79\x7c\x68\x74\
+\x6d\x6c\x29\x24\x2f\x69\x3b\x22\x67\x65\x74\x42\x6f\x75\x6e\x64\
+\x69\x6e\x67\x43\x6c\x69\x65\x6e\x74\x52\x65\x63\x74\x22\x69\x6e\
+\x20\x63\x2e\x64\x6f\x63\x75\x6d\x65\x6e\x74\x45\x6c\x65\x6d\x65\
+\x6e\x74\x3f\x66\x2e\x66\x6e\x2e\x6f\x66\x66\x73\x65\x74\x3d\x66\
+\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x76\x61\x72\x20\x62\
+\x3d\x74\x68\x69\x73\x5b\x30\x5d\x2c\x63\x3b\x69\x66\x28\x61\x29\
+\x72\x65\x74\x75\x72\x6e\x20\x74\x68\x69\x73\x2e\x65\x61\x63\x68\
+\x28\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x62\x29\x7b\x66\x2e\x6f\
+\x66\x66\x73\x65\x74\x2e\x73\x65\x74\x4f\x66\x66\x73\x65\x74\x28\
+\x74\x68\x69\x73\x2c\x61\x2c\x62\x29\x7d\x29\x3b\x69\x66\x28\x21\
+\x62\x7c\x7c\x21\x62\x2e\x6f\x77\x6e\x65\x72\x44\x6f\x63\x75\x6d\
+\x65\x6e\x74\x29\x72\x65\x74\x75\x72\x6e\x20\x6e\x75\x6c\x6c\x3b\
+\x69\x66\x28\x62\x3d\x3d\x3d\x62\x2e\x6f\x77\x6e\x65\x72\x44\x6f\
+\x63\x75\x6d\x65\x6e\x74\x2e\x62\x6f\x64\x79\x29\x72\x65\x74\x75\
+\x72\x6e\x20\x66\x2e\x6f\x66\x66\x73\x65\x74\x2e\x62\x6f\x64\x79\
+\x4f\x66\x66\x73\x65\x74\x28\x62\x29\x3b\x74\x72\x79\x7b\x63\x3d\
+\x62\x2e\x67\x65\x74\x42\x6f\x75\x6e\x64\x69\x6e\x67\x43\x6c\x69\
+\x65\x6e\x74\x52\x65\x63\x74\x28\x29\x7d\x63\x61\x74\x63\x68\x28\
+\x64\x29\x7b\x7d\x76\x61\x72\x20\x65\x3d\x62\x2e\x6f\x77\x6e\x65\
+\x72\x44\x6f\x63\x75\x6d\x65\x6e\x74\x2c\x67\x3d\x65\x2e\x64\x6f\
+\x63\x75\x6d\x65\x6e\x74\x45\x6c\x65\x6d\x65\x6e\x74\x3b\x69\x66\
+\x28\x21\x63\x7c\x7c\x21\x66\x2e\x63\x6f\x6e\x74\x61\x69\x6e\x73\
+\x28\x67\x2c\x62\x29\x29\x72\x65\x74\x75\x72\x6e\x20\x63\x3f\x7b\
+\x74\x6f\x70\x3a\x63\x2e\x74\x6f\x70\x2c\x6c\x65\x66\x74\x3a\x63\
+\x2e\x6c\x65\x66\x74\x7d\x3a\x7b\x74\x6f\x70\x3a\x30\x2c\x6c\x65\
+\x66\x74\x3a\x30\x7d\x3b\x76\x61\x72\x20\x68\x3d\x65\x2e\x62\x6f\
+\x64\x79\x2c\x69\x3d\x63\x79\x28\x65\x29\x2c\x6a\x3d\x67\x2e\x63\
+\x6c\x69\x65\x6e\x74\x54\x6f\x70\x7c\x7c\x68\x2e\x63\x6c\x69\x65\
+\x6e\x74\x54\x6f\x70\x7c\x7c\x30\x2c\x6b\x3d\x67\x2e\x63\x6c\x69\
+\x65\x6e\x74\x4c\x65\x66\x74\x7c\x7c\x68\x2e\x63\x6c\x69\x65\x6e\
+\x74\x4c\x65\x66\x74\x7c\x7c\x30\x2c\x6c\x3d\x69\x2e\x70\x61\x67\
+\x65\x59\x4f\x66\x66\x73\x65\x74\x7c\x7c\x66\x2e\x73\x75\x70\x70\
+\x6f\x72\x74\x2e\x62\x6f\x78\x4d\x6f\x64\x65\x6c\x26\x26\x67\x2e\
+\x73\x63\x72\x6f\x6c\x6c\x54\x6f\x70\x7c\x7c\x68\x2e\x73\x63\x72\
+\x6f\x6c\x6c\x54\x6f\x70\x2c\x6d\x3d\x69\x2e\x70\x61\x67\x65\x58\
+\x4f\x66\x66\x73\x65\x74\x7c\x7c\x66\x2e\x73\x75\x70\x70\x6f\x72\
+\x74\x2e\x62\x6f\x78\x4d\x6f\x64\x65\x6c\x26\x26\x67\x2e\x73\x63\
+\x72\x6f\x6c\x6c\x4c\x65\x66\x74\x7c\x7c\x68\x2e\x73\x63\x72\x6f\
+\x6c\x6c\x4c\x65\x66\x74\x2c\x6e\x3d\x63\x2e\x74\x6f\x70\x2b\x6c\
+\x2d\x6a\x2c\x6f\x3d\x63\x2e\x6c\x65\x66\x74\x2b\x6d\x2d\x6b\x3b\
+\x72\x65\x74\x75\x72\x6e\x7b\x74\x6f\x70\x3a\x6e\x2c\x6c\x65\x66\
+\x74\x3a\x6f\x7d\x7d\x3a\x66\x2e\x66\x6e\x2e\x6f\x66\x66\x73\x65\
+\x74\x3d\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x76\x61\
+\x72\x20\x62\x3d\x74\x68\x69\x73\x5b\x30\x5d\x3b\x69\x66\x28\x61\
+\x29\x72\x65\x74\x75\x72\x6e\x20\x74\x68\x69\x73\x2e\x65\x61\x63\
+\x68\x28\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x62\x29\x7b\x66\x2e\
+\x6f\x66\x66\x73\x65\x74\x2e\x73\x65\x74\x4f\x66\x66\x73\x65\x74\
+\x28\x74\x68\x69\x73\x2c\x61\x2c\x62\x29\x7d\x29\x3b\x69\x66\x28\
+\x21\x62\x7c\x7c\x21\x62\x2e\x6f\x77\x6e\x65\x72\x44\x6f\x63\x75\
+\x6d\x65\x6e\x74\x29\x72\x65\x74\x75\x72\x6e\x20\x6e\x75\x6c\x6c\
+\x3b\x69\x66\x28\x62\x3d\x3d\x3d\x62\x2e\x6f\x77\x6e\x65\x72\x44\
+\x6f\x63\x75\x6d\x65\x6e\x74\x2e\x62\x6f\x64\x79\x29\x72\x65\x74\
+\x75\x72\x6e\x20\x66\x2e\x6f\x66\x66\x73\x65\x74\x2e\x62\x6f\x64\
+\x79\x4f\x66\x66\x73\x65\x74\x28\x62\x29\x3b\x76\x61\x72\x20\x63\
+\x2c\x64\x3d\x62\x2e\x6f\x66\x66\x73\x65\x74\x50\x61\x72\x65\x6e\
+\x74\x2c\x65\x3d\x62\x2c\x67\x3d\x62\x2e\x6f\x77\x6e\x65\x72\x44\
+\x6f\x63\x75\x6d\x65\x6e\x74\x2c\x68\x3d\x67\x2e\x64\x6f\x63\x75\
+\x6d\x65\x6e\x74\x45\x6c\x65\x6d\x65\x6e\x74\x2c\x69\x3d\x67\x2e\
+\x62\x6f\x64\x79\x2c\x6a\x3d\x67\x2e\x64\x65\x66\x61\x75\x6c\x74\
+\x56\x69\x65\x77\x2c\x6b\x3d\x6a\x3f\x6a\x2e\x67\x65\x74\x43\x6f\
+\x6d\x70\x75\x74\x65\x64\x53\x74\x79\x6c\x65\x28\x62\x2c\x6e\x75\
+\x6c\x6c\x29\x3a\x62\x2e\x63\x75\x72\x72\x65\x6e\x74\x53\x74\x79\
+\x6c\x65\x2c\x6c\x3d\x62\x2e\x6f\x66\x66\x73\x65\x74\x54\x6f\x70\
+\x2c\x6d\x3d\x62\x2e\x6f\x66\x66\x73\x65\x74\x4c\x65\x66\x74\x3b\
+\x77\x68\x69\x6c\x65\x28\x28\x62\x3d\x62\x2e\x70\x61\x72\x65\x6e\
+\x74\x4e\x6f\x64\x65\x29\x26\x26\x62\x21\x3d\x3d\x69\x26\x26\x62\
+\x21\x3d\x3d\x68\x29\x7b\x69\x66\x28\x66\x2e\x73\x75\x70\x70\x6f\
+\x72\x74\x2e\x66\x69\x78\x65\x64\x50\x6f\x73\x69\x74\x69\x6f\x6e\
+\x26\x26\x6b\x2e\x70\x6f\x73\x69\x74\x69\x6f\x6e\x3d\x3d\x3d\x22\
+\x66\x69\x78\x65\x64\x22\x29\x62\x72\x65\x61\x6b\x3b\x63\x3d\x6a\
+\x3f\x6a\x2e\x67\x65\x74\x43\x6f\x6d\x70\x75\x74\x65\x64\x53\x74\
+\x79\x6c\x65\x28\x62\x2c\x6e\x75\x6c\x6c\x29\x3a\x62\x2e\x63\x75\
+\x72\x72\x65\x6e\x74\x53\x74\x79\x6c\x65\x2c\x6c\x2d\x3d\x62\x2e\
+\x73\x63\x72\x6f\x6c\x6c\x54\x6f\x70\x2c\x6d\x2d\x3d\x62\x2e\x73\
+\x63\x72\x6f\x6c\x6c\x4c\x65\x66\x74\x2c\x62\x3d\x3d\x3d\x64\x26\
+\x26\x28\x6c\x2b\x3d\x62\x2e\x6f\x66\x66\x73\x65\x74\x54\x6f\x70\
+\x2c\x6d\x2b\x3d\x62\x2e\x6f\x66\x66\x73\x65\x74\x4c\x65\x66\x74\
+\x2c\x66\x2e\x73\x75\x70\x70\x6f\x72\x74\x2e\x64\x6f\x65\x73\x4e\
+\x6f\x74\x41\x64\x64\x42\x6f\x72\x64\x65\x72\x26\x26\x28\x21\x66\
+\x2e\x73\x75\x70\x70\x6f\x72\x74\x2e\x64\x6f\x65\x73\x41\x64\x64\
+\x42\x6f\x72\x64\x65\x72\x46\x6f\x72\x54\x61\x62\x6c\x65\x41\x6e\
+\x64\x43\x65\x6c\x6c\x73\x7c\x7c\x21\x63\x77\x2e\x74\x65\x73\x74\
+\x28\x62\x2e\x6e\x6f\x64\x65\x4e\x61\x6d\x65\x29\x29\x26\x26\x28\
+\x6c\x2b\x3d\x70\x61\x72\x73\x65\x46\x6c\x6f\x61\x74\x28\x63\x2e\
+\x62\x6f\x72\x64\x65\x72\x54\x6f\x70\x57\x69\x64\x74\x68\x29\x7c\
+\x7c\x30\x2c\x6d\x2b\x3d\x70\x61\x72\x73\x65\x46\x6c\x6f\x61\x74\
+\x28\x63\x2e\x62\x6f\x72\x64\x65\x72\x4c\x65\x66\x74\x57\x69\x64\
+\x74\x68\x29\x7c\x7c\x30\x29\x2c\x65\x3d\x64\x2c\x64\x3d\x62\x2e\
+\x6f\x66\x66\x73\x65\x74\x50\x61\x72\x65\x6e\x74\x29\x2c\x66\x2e\
+\x73\x75\x70\x70\x6f\x72\x74\x2e\x73\x75\x62\x74\x72\x61\x63\x74\
+\x73\x42\x6f\x72\x64\x65\x72\x46\x6f\x72\x4f\x76\x65\x72\x66\x6c\
+\x6f\x77\x4e\x6f\x74\x56\x69\x73\x69\x62\x6c\x65\x26\x26\x63\x2e\
+\x6f\x76\x65\x72\x66\x6c\x6f\x77\x21\x3d\x3d\x22\x76\x69\x73\x69\
+\x62\x6c\x65\x22\x26\x26\x28\x6c\x2b\x3d\x70\x61\x72\x73\x65\x46\
+\x6c\x6f\x61\x74\x28\x63\x2e\x62\x6f\x72\x64\x65\x72\x54\x6f\x70\
+\x57\x69\x64\x74\x68\x29\x7c\x7c\x30\x2c\x6d\x2b\x3d\x70\x61\x72\
+\x73\x65\x46\x6c\x6f\x61\x74\x28\x63\x2e\x62\x6f\x72\x64\x65\x72\
+\x4c\x65\x66\x74\x57\x69\x64\x74\x68\x29\x7c\x7c\x30\x29\x2c\x6b\
+\x3d\x63\x7d\x69\x66\x28\x6b\x2e\x70\x6f\x73\x69\x74\x69\x6f\x6e\
+\x3d\x3d\x3d\x22\x72\x65\x6c\x61\x74\x69\x76\x65\x22\x7c\x7c\x6b\
+\x2e\x70\x6f\x73\x69\x74\x69\x6f\x6e\x3d\x3d\x3d\x22\x73\x74\x61\
+\x74\x69\x63\x22\x29\x6c\x2b\x3d\x69\x2e\x6f\x66\x66\x73\x65\x74\
+\x54\x6f\x70\x2c\x6d\x2b\x3d\x69\x2e\x6f\x66\x66\x73\x65\x74\x4c\
+\x65\x66\x74\x3b\x66\x2e\x73\x75\x70\x70\x6f\x72\x74\x2e\x66\x69\
+\x78\x65\x64\x50\x6f\x73\x69\x74\x69\x6f\x6e\x26\x26\x6b\x2e\x70\
+\x6f\x73\x69\x74\x69\x6f\x6e\x3d\x3d\x3d\x22\x66\x69\x78\x65\x64\
+\x22\x26\x26\x28\x6c\x2b\x3d\x4d\x61\x74\x68\x2e\x6d\x61\x78\x28\
+\x68\x2e\x73\x63\x72\x6f\x6c\x6c\x54\x6f\x70\x2c\x69\x2e\x73\x63\
+\x72\x6f\x6c\x6c\x54\x6f\x70\x29\x2c\x6d\x2b\x3d\x4d\x61\x74\x68\
+\x2e\x6d\x61\x78\x28\x68\x2e\x73\x63\x72\x6f\x6c\x6c\x4c\x65\x66\
+\x74\x2c\x69\x2e\x73\x63\x72\x6f\x6c\x6c\x4c\x65\x66\x74\x29\x29\
+\x3b\x72\x65\x74\x75\x72\x6e\x7b\x74\x6f\x70\x3a\x6c\x2c\x6c\x65\
+\x66\x74\x3a\x6d\x7d\x7d\x2c\x66\x2e\x6f\x66\x66\x73\x65\x74\x3d\
+\x7b\x62\x6f\x64\x79\x4f\x66\x66\x73\x65\x74\x3a\x66\x75\x6e\x63\
+\x74\x69\x6f\x6e\x28\x61\x29\x7b\x76\x61\x72\x20\x62\x3d\x61\x2e\
+\x6f\x66\x66\x73\x65\x74\x54\x6f\x70\x2c\x63\x3d\x61\x2e\x6f\x66\
+\x66\x73\x65\x74\x4c\x65\x66\x74\x3b\x66\x2e\x73\x75\x70\x70\x6f\
+\x72\x74\x2e\x64\x6f\x65\x73\x4e\x6f\x74\x49\x6e\x63\x6c\x75\x64\
+\x65\x4d\x61\x72\x67\x69\x6e\x49\x6e\x42\x6f\x64\x79\x4f\x66\x66\
+\x73\x65\x74\x26\x26\x28\x62\x2b\x3d\x70\x61\x72\x73\x65\x46\x6c\
+\x6f\x61\x74\x28\x66\x2e\x63\x73\x73\x28\x61\x2c\x22\x6d\x61\x72\
+\x67\x69\x6e\x54\x6f\x70\x22\x29\x29\x7c\x7c\x30\x2c\x63\x2b\x3d\
+\x70\x61\x72\x73\x65\x46\x6c\x6f\x61\x74\x28\x66\x2e\x63\x73\x73\
+\x28\x61\x2c\x22\x6d\x61\x72\x67\x69\x6e\x4c\x65\x66\x74\x22\x29\
+\x29\x7c\x7c\x30\x29\x3b\x72\x65\x74\x75\x72\x6e\x7b\x74\x6f\x70\
+\x3a\x62\x2c\x6c\x65\x66\x74\x3a\x63\x7d\x7d\x2c\x73\x65\x74\x4f\
+\x66\x66\x73\x65\x74\x3a\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\
+\x2c\x62\x2c\x63\x29\x7b\x76\x61\x72\x20\x64\x3d\x66\x2e\x63\x73\
+\x73\x28\x61\x2c\x22\x70\x6f\x73\x69\x74\x69\x6f\x6e\x22\x29\x3b\
+\x64\x3d\x3d\x3d\x22\x73\x74\x61\x74\x69\x63\x22\x26\x26\x28\x61\
+\x2e\x73\x74\x79\x6c\x65\x2e\x70\x6f\x73\x69\x74\x69\x6f\x6e\x3d\
+\x22\x72\x65\x6c\x61\x74\x69\x76\x65\x22\x29\x3b\x76\x61\x72\x20\
+\x65\x3d\x66\x28\x61\x29\x2c\x67\x3d\x65\x2e\x6f\x66\x66\x73\x65\
+\x74\x28\x29\x2c\x68\x3d\x66\x2e\x63\x73\x73\x28\x61\x2c\x22\x74\
+\x6f\x70\x22\x29\x2c\x69\x3d\x66\x2e\x63\x73\x73\x28\x61\x2c\x22\
+\x6c\x65\x66\x74\x22\x29\x2c\x6a\x3d\x28\x64\x3d\x3d\x3d\x22\x61\
+\x62\x73\x6f\x6c\x75\x74\x65\x22\x7c\x7c\x64\x3d\x3d\x3d\x22\x66\
+\x69\x78\x65\x64\x22\x29\x26\x26\x66\x2e\x69\x6e\x41\x72\x72\x61\
+\x79\x28\x22\x61\x75\x74\x6f\x22\x2c\x5b\x68\x2c\x69\x5d\x29\x3e\
+\x2d\x31\x2c\x6b\x3d\x7b\x7d\x2c\x6c\x3d\x7b\x7d\x2c\x6d\x2c\x6e\
+\x3b\x6a\x3f\x28\x6c\x3d\x65\x2e\x70\x6f\x73\x69\x74\x69\x6f\x6e\
+\x28\x29\x2c\x6d\x3d\x6c\x2e\x74\x6f\x70\x2c\x6e\x3d\x6c\x2e\x6c\
+\x65\x66\x74\x29\x3a\x28\x6d\x3d\x70\x61\x72\x73\x65\x46\x6c\x6f\
+\x61\x74\x28\x68\x29\x7c\x7c\x30\x2c\x6e\x3d\x70\x61\x72\x73\x65\
+\x46\x6c\x6f\x61\x74\x28\x69\x29\x7c\x7c\x30\x29\x2c\x66\x2e\x69\
+\x73\x46\x75\x6e\x63\x74\x69\x6f\x6e\x28\x62\x29\x26\x26\x28\x62\
+\x3d\x62\x2e\x63\x61\x6c\x6c\x28\x61\x2c\x63\x2c\x67\x29\x29\x2c\
+\x62\x2e\x74\x6f\x70\x21\x3d\x6e\x75\x6c\x6c\x26\x26\x28\x6b\x2e\
+\x74\x6f\x70\x3d\x62\x2e\x74\x6f\x70\x2d\x67\x2e\x74\x6f\x70\x2b\
+\x6d\x29\x2c\x62\x2e\x6c\x65\x66\x74\x21\x3d\x6e\x75\x6c\x6c\x26\
+\x26\x28\x6b\x2e\x6c\x65\x66\x74\x3d\x62\x2e\x6c\x65\x66\x74\x2d\
+\x67\x2e\x6c\x65\x66\x74\x2b\x6e\x29\x2c\x22\x75\x73\x69\x6e\x67\
+\x22\x69\x6e\x20\x62\x3f\x62\x2e\x75\x73\x69\x6e\x67\x2e\x63\x61\
+\x6c\x6c\x28\x61\x2c\x6b\x29\x3a\x65\x2e\x63\x73\x73\x28\x6b\x29\
+\x7d\x7d\x2c\x66\x2e\x66\x6e\x2e\x65\x78\x74\x65\x6e\x64\x28\x7b\
+\x70\x6f\x73\x69\x74\x69\x6f\x6e\x3a\x66\x75\x6e\x63\x74\x69\x6f\
+\x6e\x28\x29\x7b\x69\x66\x28\x21\x74\x68\x69\x73\x5b\x30\x5d\x29\
+\x72\x65\x74\x75\x72\x6e\x20\x6e\x75\x6c\x6c\x3b\x76\x61\x72\x20\
+\x61\x3d\x74\x68\x69\x73\x5b\x30\x5d\x2c\x62\x3d\x74\x68\x69\x73\
+\x2e\x6f\x66\x66\x73\x65\x74\x50\x61\x72\x65\x6e\x74\x28\x29\x2c\
+\x63\x3d\x74\x68\x69\x73\x2e\x6f\x66\x66\x73\x65\x74\x28\x29\x2c\
+\x64\x3d\x63\x78\x2e\x74\x65\x73\x74\x28\x62\x5b\x30\x5d\x2e\x6e\
+\x6f\x64\x65\x4e\x61\x6d\x65\x29\x3f\x7b\x74\x6f\x70\x3a\x30\x2c\
+\x6c\x65\x66\x74\x3a\x30\x7d\x3a\x62\x2e\x6f\x66\x66\x73\x65\x74\
+\x28\x29\x3b\x63\x2e\x74\x6f\x70\x2d\x3d\x70\x61\x72\x73\x65\x46\
+\x6c\x6f\x61\x74\x28\x66\x2e\x63\x73\x73\x28\x61\x2c\x22\x6d\x61\
+\x72\x67\x69\x6e\x54\x6f\x70\x22\x29\x29\x7c\x7c\x30\x2c\x63\x2e\
+\x6c\x65\x66\x74\x2d\x3d\x70\x61\x72\x73\x65\x46\x6c\x6f\x61\x74\
+\x28\x66\x2e\x63\x73\x73\x28\x61\x2c\x22\x6d\x61\x72\x67\x69\x6e\
+\x4c\x65\x66\x74\x22\x29\x29\x7c\x7c\x30\x2c\x64\x2e\x74\x6f\x70\
+\x2b\x3d\x70\x61\x72\x73\x65\x46\x6c\x6f\x61\x74\x28\x66\x2e\x63\
+\x73\x73\x28\x62\x5b\x30\x5d\x2c\x22\x62\x6f\x72\x64\x65\x72\x54\
+\x6f\x70\x57\x69\x64\x74\x68\x22\x29\x29\x7c\x7c\x30\x2c\x64\x2e\
+\x6c\x65\x66\x74\x2b\x3d\x70\x61\x72\x73\x65\x46\x6c\x6f\x61\x74\
+\x28\x66\x2e\x63\x73\x73\x28\x62\x5b\x30\x5d\x2c\x22\x62\x6f\x72\
+\x64\x65\x72\x4c\x65\x66\x74\x57\x69\x64\x74\x68\x22\x29\x29\x7c\
+\x7c\x30\x3b\x72\x65\x74\x75\x72\x6e\x7b\x74\x6f\x70\x3a\x63\x2e\
+\x74\x6f\x70\x2d\x64\x2e\x74\x6f\x70\x2c\x6c\x65\x66\x74\x3a\x63\
+\x2e\x6c\x65\x66\x74\x2d\x64\x2e\x6c\x65\x66\x74\x7d\x7d\x2c\x6f\
+\x66\x66\x73\x65\x74\x50\x61\x72\x65\x6e\x74\x3a\x66\x75\x6e\x63\
+\x74\x69\x6f\x6e\x28\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x74\x68\
+\x69\x73\x2e\x6d\x61\x70\x28\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\
+\x29\x7b\x76\x61\x72\x20\x61\x3d\x74\x68\x69\x73\x2e\x6f\x66\x66\
+\x73\x65\x74\x50\x61\x72\x65\x6e\x74\x7c\x7c\x63\x2e\x62\x6f\x64\
+\x79\x3b\x77\x68\x69\x6c\x65\x28\x61\x26\x26\x21\x63\x78\x2e\x74\
+\x65\x73\x74\x28\x61\x2e\x6e\x6f\x64\x65\x4e\x61\x6d\x65\x29\x26\
+\x26\x66\x2e\x63\x73\x73\x28\x61\x2c\x22\x70\x6f\x73\x69\x74\x69\
+\x6f\x6e\x22\x29\x3d\x3d\x3d\x22\x73\x74\x61\x74\x69\x63\x22\x29\
+\x61\x3d\x61\x2e\x6f\x66\x66\x73\x65\x74\x50\x61\x72\x65\x6e\x74\
+\x3b\x72\x65\x74\x75\x72\x6e\x20\x61\x7d\x29\x7d\x7d\x29\x2c\x66\
+\x2e\x65\x61\x63\x68\x28\x5b\x22\x4c\x65\x66\x74\x22\x2c\x22\x54\
+\x6f\x70\x22\x5d\x2c\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\
+\x63\x29\x7b\x76\x61\x72\x20\x64\x3d\x22\x73\x63\x72\x6f\x6c\x6c\
+\x22\x2b\x63\x3b\x66\x2e\x66\x6e\x5b\x64\x5d\x3d\x66\x75\x6e\x63\
+\x74\x69\x6f\x6e\x28\x63\x29\x7b\x76\x61\x72\x20\x65\x2c\x67\x3b\
+\x69\x66\x28\x63\x3d\x3d\x3d\x62\x29\x7b\x65\x3d\x74\x68\x69\x73\
+\x5b\x30\x5d\x3b\x69\x66\x28\x21\x65\x29\x72\x65\x74\x75\x72\x6e\
+\x20\x6e\x75\x6c\x6c\x3b\x67\x3d\x63\x79\x28\x65\x29\x3b\x72\x65\
+\x74\x75\x72\x6e\x20\x67\x3f\x22\x70\x61\x67\x65\x58\x4f\x66\x66\
+\x73\x65\x74\x22\x69\x6e\x20\x67\x3f\x67\x5b\x61\x3f\x22\x70\x61\
+\x67\x65\x59\x4f\x66\x66\x73\x65\x74\x22\x3a\x22\x70\x61\x67\x65\
+\x58\x4f\x66\x66\x73\x65\x74\x22\x5d\x3a\x66\x2e\x73\x75\x70\x70\
+\x6f\x72\x74\x2e\x62\x6f\x78\x4d\x6f\x64\x65\x6c\x26\x26\x67\x2e\
+\x64\x6f\x63\x75\x6d\x65\x6e\x74\x2e\x64\x6f\x63\x75\x6d\x65\x6e\
+\x74\x45\x6c\x65\x6d\x65\x6e\x74\x5b\x64\x5d\x7c\x7c\x67\x2e\x64\
+\x6f\x63\x75\x6d\x65\x6e\x74\x2e\x62\x6f\x64\x79\x5b\x64\x5d\x3a\
+\x65\x5b\x64\x5d\x7d\x72\x65\x74\x75\x72\x6e\x20\x74\x68\x69\x73\
+\x2e\x65\x61\x63\x68\x28\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x29\
+\x7b\x67\x3d\x63\x79\x28\x74\x68\x69\x73\x29\x2c\x67\x3f\x67\x2e\
+\x73\x63\x72\x6f\x6c\x6c\x54\x6f\x28\x61\x3f\x66\x28\x67\x29\x2e\
+\x73\x63\x72\x6f\x6c\x6c\x4c\x65\x66\x74\x28\x29\x3a\x63\x2c\x61\
+\x3f\x63\x3a\x66\x28\x67\x29\x2e\x73\x63\x72\x6f\x6c\x6c\x54\x6f\
+\x70\x28\x29\x29\x3a\x74\x68\x69\x73\x5b\x64\x5d\x3d\x63\x7d\x29\
+\x7d\x7d\x29\x2c\x66\x2e\x65\x61\x63\x68\x28\x5b\x22\x48\x65\x69\
+\x67\x68\x74\x22\x2c\x22\x57\x69\x64\x74\x68\x22\x5d\x2c\x66\x75\
+\x6e\x63\x74\x69\x6f\x6e\x28\x61\x2c\x63\x29\x7b\x76\x61\x72\x20\
+\x64\x3d\x63\x2e\x74\x6f\x4c\x6f\x77\x65\x72\x43\x61\x73\x65\x28\
+\x29\x3b\x66\x2e\x66\x6e\x5b\x22\x69\x6e\x6e\x65\x72\x22\x2b\x63\
+\x5d\x3d\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x29\x7b\x76\x61\x72\
+\x20\x61\x3d\x74\x68\x69\x73\x5b\x30\x5d\x3b\x72\x65\x74\x75\x72\
+\x6e\x20\x61\x3f\x61\x2e\x73\x74\x79\x6c\x65\x3f\x70\x61\x72\x73\
+\x65\x46\x6c\x6f\x61\x74\x28\x66\x2e\x63\x73\x73\x28\x61\x2c\x64\
+\x2c\x22\x70\x61\x64\x64\x69\x6e\x67\x22\x29\x29\x3a\x74\x68\x69\
+\x73\x5b\x64\x5d\x28\x29\x3a\x6e\x75\x6c\x6c\x7d\x2c\x66\x2e\x66\
+\x6e\x5b\x22\x6f\x75\x74\x65\x72\x22\x2b\x63\x5d\x3d\x66\x75\x6e\
+\x63\x74\x69\x6f\x6e\x28\x61\x29\x7b\x76\x61\x72\x20\x62\x3d\x74\
+\x68\x69\x73\x5b\x30\x5d\x3b\x72\x65\x74\x75\x72\x6e\x20\x62\x3f\
+\x62\x2e\x73\x74\x79\x6c\x65\x3f\x70\x61\x72\x73\x65\x46\x6c\x6f\
+\x61\x74\x28\x66\x2e\x63\x73\x73\x28\x62\x2c\x64\x2c\x61\x3f\x22\
+\x6d\x61\x72\x67\x69\x6e\x22\x3a\x22\x62\x6f\x72\x64\x65\x72\x22\
+\x29\x29\x3a\x74\x68\x69\x73\x5b\x64\x5d\x28\x29\x3a\x6e\x75\x6c\
+\x6c\x7d\x2c\x66\x2e\x66\x6e\x5b\x64\x5d\x3d\x66\x75\x6e\x63\x74\
+\x69\x6f\x6e\x28\x61\x29\x7b\x76\x61\x72\x20\x65\x3d\x74\x68\x69\
+\x73\x5b\x30\x5d\x3b\x69\x66\x28\x21\x65\x29\x72\x65\x74\x75\x72\
+\x6e\x20\x61\x3d\x3d\x6e\x75\x6c\x6c\x3f\x6e\x75\x6c\x6c\x3a\x74\
+\x68\x69\x73\x3b\x69\x66\x28\x66\x2e\x69\x73\x46\x75\x6e\x63\x74\
+\x69\x6f\x6e\x28\x61\x29\x29\x72\x65\x74\x75\x72\x6e\x20\x74\x68\
+\x69\x73\x2e\x65\x61\x63\x68\x28\x66\x75\x6e\x63\x74\x69\x6f\x6e\
+\x28\x62\x29\x7b\x76\x61\x72\x20\x63\x3d\x66\x28\x74\x68\x69\x73\
+\x29\x3b\x63\x5b\x64\x5d\x28\x61\x2e\x63\x61\x6c\x6c\x28\x74\x68\
+\x69\x73\x2c\x62\x2c\x63\x5b\x64\x5d\x28\x29\x29\x29\x7d\x29\x3b\
+\x69\x66\x28\x66\x2e\x69\x73\x57\x69\x6e\x64\x6f\x77\x28\x65\x29\
+\x29\x7b\x76\x61\x72\x20\x67\x3d\x65\x2e\x64\x6f\x63\x75\x6d\x65\
+\x6e\x74\x2e\x64\x6f\x63\x75\x6d\x65\x6e\x74\x45\x6c\x65\x6d\x65\
+\x6e\x74\x5b\x22\x63\x6c\x69\x65\x6e\x74\x22\x2b\x63\x5d\x2c\x68\
+\x3d\x65\x2e\x64\x6f\x63\x75\x6d\x65\x6e\x74\x2e\x62\x6f\x64\x79\
+\x3b\x72\x65\x74\x75\x72\x6e\x20\x65\x2e\x64\x6f\x63\x75\x6d\x65\
+\x6e\x74\x2e\x63\x6f\x6d\x70\x61\x74\x4d\x6f\x64\x65\x3d\x3d\x3d\
+\x22\x43\x53\x53\x31\x43\x6f\x6d\x70\x61\x74\x22\x26\x26\x67\x7c\
+\x7c\x68\x26\x26\x68\x5b\x22\x63\x6c\x69\x65\x6e\x74\x22\x2b\x63\
+\x5d\x7c\x7c\x67\x7d\x69\x66\x28\x65\x2e\x6e\x6f\x64\x65\x54\x79\
+\x70\x65\x3d\x3d\x3d\x39\x29\x72\x65\x74\x75\x72\x6e\x20\x4d\x61\
+\x74\x68\x2e\x6d\x61\x78\x28\x65\x2e\x64\x6f\x63\x75\x6d\x65\x6e\
+\x74\x45\x6c\x65\x6d\x65\x6e\x74\x5b\x22\x63\x6c\x69\x65\x6e\x74\
+\x22\x2b\x63\x5d\x2c\x65\x2e\x62\x6f\x64\x79\x5b\x22\x73\x63\x72\
+\x6f\x6c\x6c\x22\x2b\x63\x5d\x2c\x65\x2e\x64\x6f\x63\x75\x6d\x65\
+\x6e\x74\x45\x6c\x65\x6d\x65\x6e\x74\x5b\x22\x73\x63\x72\x6f\x6c\
+\x6c\x22\x2b\x63\x5d\x2c\x65\x2e\x62\x6f\x64\x79\x5b\x22\x6f\x66\
+\x66\x73\x65\x74\x22\x2b\x63\x5d\x2c\x65\x2e\x64\x6f\x63\x75\x6d\
+\x65\x6e\x74\x45\x6c\x65\x6d\x65\x6e\x74\x5b\x22\x6f\x66\x66\x73\
+\x65\x74\x22\x2b\x63\x5d\x29\x3b\x69\x66\x28\x61\x3d\x3d\x3d\x62\
+\x29\x7b\x76\x61\x72\x20\x69\x3d\x66\x2e\x63\x73\x73\x28\x65\x2c\
+\x64\x29\x2c\x6a\x3d\x70\x61\x72\x73\x65\x46\x6c\x6f\x61\x74\x28\
+\x69\x29\x3b\x72\x65\x74\x75\x72\x6e\x20\x66\x2e\x69\x73\x4e\x75\
+\x6d\x65\x72\x69\x63\x28\x6a\x29\x3f\x6a\x3a\x69\x7d\x72\x65\x74\
+\x75\x72\x6e\x20\x74\x68\x69\x73\x2e\x63\x73\x73\x28\x64\x2c\x74\
+\x79\x70\x65\x6f\x66\x20\x61\x3d\x3d\x22\x73\x74\x72\x69\x6e\x67\
+\x22\x3f\x61\x3a\x61\x2b\x22\x70\x78\x22\x29\x7d\x7d\x29\x2c\x61\
+\x2e\x6a\x51\x75\x65\x72\x79\x3d\x61\x2e\x24\x3d\x66\x2c\x74\x79\
+\x70\x65\x6f\x66\x20\x64\x65\x66\x69\x6e\x65\x3d\x3d\x22\x66\x75\
+\x6e\x63\x74\x69\x6f\x6e\x22\x26\x26\x64\x65\x66\x69\x6e\x65\x2e\
+\x61\x6d\x64\x26\x26\x64\x65\x66\x69\x6e\x65\x2e\x61\x6d\x64\x2e\
+\x6a\x51\x75\x65\x72\x79\x26\x26\x64\x65\x66\x69\x6e\x65\x28\x22\
+\x6a\x71\x75\x65\x72\x79\x22\x2c\x5b\x5d\x2c\x66\x75\x6e\x63\x74\
+\x69\x6f\x6e\x28\x29\x7b\x72\x65\x74\x75\x72\x6e\x20\x66\x7d\x29\
+\x7d\x29\x28\x77\x69\x6e\x64\x6f\x77\x29\x3b\
+"
+
+qt_resource_name = b"\
+\x00\x0a\
+\x08\x94\x81\xf4\
+\x00\x6a\
+\x00\x61\x00\x76\x00\x61\x00\x73\x00\x63\x00\x72\x00\x69\x00\x70\x00\x74\
+\x00\x0e\
+\x0e\x3a\xd6\x33\
+\x00\x71\
+\x00\x77\x00\x65\x00\x62\x00\x63\x00\x68\x00\x61\x00\x6e\x00\x6e\x00\x65\x00\x6c\x00\x2e\x00\x6a\x00\x73\
+\x00\x0c\
+\x06\x7a\xfc\x33\
+\x00\x6a\
+\x00\x71\x00\x75\x00\x65\x00\x72\x00\x79\x00\x2d\x00\x75\x00\x69\x00\x2e\x00\x6a\x00\x73\
+\x00\x09\
+\x0b\xc9\x66\x13\
+\x00\x6a\
+\x00\x71\x00\x75\x00\x65\x00\x72\x00\x79\x00\x2e\x00\x6a\x00\x73\
+"
+
+qt_resource_struct = b"\
+\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
+\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x02\
+\x00\x00\x00\x3c\x00\x01\x00\x00\x00\x01\x00\x00\x0e\xfe\
+\x00\x00\x00\x5a\x00\x00\x00\x00\x00\x01\x00\x00\x33\x36\
+\x00\x00\x00\x1a\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\
+"
+
+def qInitResources():
+    QtCore.qRegisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data)
+
+def qCleanupResources():
+    QtCore.qUnregisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data)
+
+qInitResources()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/data/qml.qrc	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,6 @@
+<!DOCTYPE RCC>
+<RCC version="1.0">
+<qresource>
+  <file>qml/thumbnailer.qml</file>
+</qresource>
+</RCC>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/data/qml/thumbnailer.qml	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,15 @@
+import QtQuick 2.2
+import QtWebEngine 1.0
+
+WebEngineView {
+    width: 1920
+    height: 1080
+
+    onLoadingChanged: {
+        if (loadRequest.status == WebEngineView.LoadStartedStatus)
+            return;
+
+        var ok = loadRequest.status == WebEngineView.LoadSucceededStatus;
+        thumbnailer.createThumbnail(ok);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/data/qml_rc.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,60 @@
+# -*- coding: utf-8 -*-
+
+# Resource object code
+#
+# Created by: The Resource Compiler for PyQt5 (Qt v5.4.1)
+#
+# WARNING! All changes made in this file will be lost!
+
+from PyQt5 import QtCore
+
+qt_resource_data = b"\
+\x00\x00\x01\x48\
+\x69\
+\x6d\x70\x6f\x72\x74\x20\x51\x74\x51\x75\x69\x63\x6b\x20\x32\x2e\
+\x32\x0a\x69\x6d\x70\x6f\x72\x74\x20\x51\x74\x57\x65\x62\x45\x6e\
+\x67\x69\x6e\x65\x20\x31\x2e\x30\x0a\x0a\x57\x65\x62\x45\x6e\x67\
+\x69\x6e\x65\x56\x69\x65\x77\x20\x7b\x0a\x20\x20\x20\x20\x77\x69\
+\x64\x74\x68\x3a\x20\x31\x39\x32\x30\x0a\x20\x20\x20\x20\x68\x65\
+\x69\x67\x68\x74\x3a\x20\x31\x30\x38\x30\x0a\x0a\x20\x20\x20\x20\
+\x6f\x6e\x4c\x6f\x61\x64\x69\x6e\x67\x43\x68\x61\x6e\x67\x65\x64\
+\x3a\x20\x7b\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x69\x66\x20\x28\
+\x6c\x6f\x61\x64\x52\x65\x71\x75\x65\x73\x74\x2e\x73\x74\x61\x74\
+\x75\x73\x20\x3d\x3d\x20\x57\x65\x62\x45\x6e\x67\x69\x6e\x65\x56\
+\x69\x65\x77\x2e\x4c\x6f\x61\x64\x53\x74\x61\x72\x74\x65\x64\x53\
+\x74\x61\x74\x75\x73\x29\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\
+\x20\x20\x20\x72\x65\x74\x75\x72\x6e\x3b\x0a\x0a\x20\x20\x20\x20\
+\x20\x20\x20\x20\x76\x61\x72\x20\x6f\x6b\x20\x3d\x20\x6c\x6f\x61\
+\x64\x52\x65\x71\x75\x65\x73\x74\x2e\x73\x74\x61\x74\x75\x73\x20\
+\x3d\x3d\x20\x57\x65\x62\x45\x6e\x67\x69\x6e\x65\x56\x69\x65\x77\
+\x2e\x4c\x6f\x61\x64\x53\x75\x63\x63\x65\x65\x64\x65\x64\x53\x74\
+\x61\x74\x75\x73\x3b\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x74\x68\
+\x75\x6d\x62\x6e\x61\x69\x6c\x65\x72\x2e\x63\x72\x65\x61\x74\x65\
+\x54\x68\x75\x6d\x62\x6e\x61\x69\x6c\x28\x6f\x6b\x29\x3b\x0a\x20\
+\x20\x20\x20\x7d\x0a\x7d\x0a\
+"
+
+qt_resource_name = b"\
+\x00\x03\
+\x00\x00\x78\x3c\
+\x00\x71\
+\x00\x6d\x00\x6c\
+\x00\x0f\
+\x0a\xb6\x34\x5c\
+\x00\x74\
+\x00\x68\x00\x75\x00\x6d\x00\x62\x00\x6e\x00\x61\x00\x69\x00\x6c\x00\x65\x00\x72\x00\x2e\x00\x71\x00\x6d\x00\x6c\
+"
+
+qt_resource_struct = b"\
+\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
+\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\
+\x00\x00\x00\x0c\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
+"
+
+def qInitResources():
+    QtCore.qRegisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data)
+
+def qCleanupResources():
+    QtCore.qUnregisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data)
+
+qInitResources()
--- a/eric6.e4p	Mon Mar 28 11:52:38 2016 +0200
+++ b/eric6.e4p	Mon Mar 28 11:59:42 2016 +0200
@@ -822,6 +822,8 @@
     <Source>Preferences/ConfigurationPages/TrayStarterPage.py</Source>
     <Source>Preferences/ConfigurationPages/VcsPage.py</Source>
     <Source>Preferences/ConfigurationPages/ViewmanagerPage.py</Source>
+    <Source>Preferences/ConfigurationPages/WebBrowserAppearancePage.py</Source>
+    <Source>Preferences/ConfigurationPages/WebBrowserPage.py</Source>
     <Source>Preferences/ConfigurationPages/__init__.py</Source>
     <Source>Preferences/MouseClickDialog.py</Source>
     <Source>Preferences/PreferencesLexer.py</Source>
@@ -1264,12 +1266,203 @@
     <Source>ViewManager/BookmarkedFilesDialog.py</Source>
     <Source>ViewManager/ViewManager.py</Source>
     <Source>ViewManager/__init__.py</Source>
+    <Source>WebBrowser/AdBlock/AdBlockDialog.py</Source>
+    <Source>WebBrowser/AdBlock/AdBlockExceptionsDialog.py</Source>
+    <Source>WebBrowser/AdBlock/AdBlockIcon.py</Source>
+    <Source>WebBrowser/AdBlock/AdBlockManager.py</Source>
+    <Source>WebBrowser/AdBlock/AdBlockPage.py</Source>
+    <Source>WebBrowser/AdBlock/AdBlockRule.py</Source>
+    <Source>WebBrowser/AdBlock/AdBlockSubscription.py</Source>
+    <Source>WebBrowser/AdBlock/AdBlockTreeWidget.py</Source>
+    <Source>WebBrowser/AdBlock/AdBlockUrlInterceptor.py</Source>
+    <Source>WebBrowser/AdBlock/__init__.py</Source>
+    <Source>WebBrowser/Bookmarks/AddBookmarkDialog.py</Source>
+    <Source>WebBrowser/Bookmarks/BookmarkNode.py</Source>
+    <Source>WebBrowser/Bookmarks/BookmarkPropertiesDialog.py</Source>
+    <Source>WebBrowser/Bookmarks/BookmarksDialog.py</Source>
+    <Source>WebBrowser/Bookmarks/BookmarksImportDialog.py</Source>
+    <Source>WebBrowser/Bookmarks/BookmarksImporters/BookmarksImporter.py</Source>
+    <Source>WebBrowser/Bookmarks/BookmarksImporters/ChromeImporter.py</Source>
+    <Source>WebBrowser/Bookmarks/BookmarksImporters/FirefoxImporter.py</Source>
+    <Source>WebBrowser/Bookmarks/BookmarksImporters/HtmlImporter.py</Source>
+    <Source>WebBrowser/Bookmarks/BookmarksImporters/IExplorerImporter.py</Source>
+    <Source>WebBrowser/Bookmarks/BookmarksImporters/OperaImporter.py</Source>
+    <Source>WebBrowser/Bookmarks/BookmarksImporters/SafariImporter.py</Source>
+    <Source>WebBrowser/Bookmarks/BookmarksImporters/XbelImporter.py</Source>
+    <Source>WebBrowser/Bookmarks/BookmarksImporters/__init__.py</Source>
+    <Source>WebBrowser/Bookmarks/BookmarksManager.py</Source>
+    <Source>WebBrowser/Bookmarks/BookmarksMenu.py</Source>
+    <Source>WebBrowser/Bookmarks/BookmarksModel.py</Source>
+    <Source>WebBrowser/Bookmarks/BookmarksToolBar.py</Source>
+    <Source>WebBrowser/Bookmarks/DefaultBookmarks_rc.py</Source>
+    <Source>WebBrowser/Bookmarks/NsHtmlReader.py</Source>
+    <Source>WebBrowser/Bookmarks/NsHtmlWriter.py</Source>
+    <Source>WebBrowser/Bookmarks/XbelReader.py</Source>
+    <Source>WebBrowser/Bookmarks/XbelWriter.py</Source>
+    <Source>WebBrowser/Bookmarks/__init__.py</Source>
+    <Source>WebBrowser/ClosedTabsManager.py</Source>
+    <Source>WebBrowser/CookieJar/CookieDetailsDialog.py</Source>
+    <Source>WebBrowser/CookieJar/CookieExceptionsModel.py</Source>
+    <Source>WebBrowser/CookieJar/CookieJar.py</Source>
+    <Source>WebBrowser/CookieJar/CookieModel.py</Source>
+    <Source>WebBrowser/CookieJar/CookiesConfigurationDialog.py</Source>
+    <Source>WebBrowser/CookieJar/CookiesDialog.py</Source>
+    <Source>WebBrowser/CookieJar/CookiesExceptionsDialog.py</Source>
+    <Source>WebBrowser/CookieJar/__init__.py</Source>
+    <Source>WebBrowser/Download/DownloadAskActionDialog.py</Source>
+    <Source>WebBrowser/Download/DownloadItem.py</Source>
+    <Source>WebBrowser/Download/DownloadManager.py</Source>
+    <Source>WebBrowser/Download/DownloadModel.py</Source>
+    <Source>WebBrowser/Download/DownloadUtilities.py</Source>
+    <Source>WebBrowser/Download/__init__.py</Source>
+    <Source>WebBrowser/FeaturePermissions/FeaturePermissionBar.py</Source>
+    <Source>WebBrowser/FeaturePermissions/FeaturePermissionManager.py</Source>
+    <Source>WebBrowser/FeaturePermissions/FeaturePermissionsDialog.py</Source>
+    <Source>WebBrowser/FeaturePermissions/__init__.py</Source>
+    <Source>WebBrowser/Feeds/FeedEditDialog.py</Source>
+    <Source>WebBrowser/Feeds/FeedsDialog.py</Source>
+    <Source>WebBrowser/Feeds/FeedsManager.py</Source>
+    <Source>WebBrowser/Feeds/__init__.py</Source>
+    <Source>WebBrowser/FlashCookieManager/FlashCookie.py</Source>
+    <Source>WebBrowser/FlashCookieManager/FlashCookieManager.py</Source>
+    <Source>WebBrowser/FlashCookieManager/FlashCookieManagerDialog.py</Source>
+    <Source>WebBrowser/FlashCookieManager/FlashCookieNotification.py</Source>
+    <Source>WebBrowser/FlashCookieManager/FlashCookieReader.py</Source>
+    <Source>WebBrowser/FlashCookieManager/FlashCookieUtilities.py</Source>
+    <Source>WebBrowser/FlashCookieManager/__init__.py</Source>
+    <Source>WebBrowser/GreaseMonkey/GreaseMonkeyAddScriptDialog.py</Source>
+    <Source>WebBrowser/GreaseMonkey/GreaseMonkeyConfiguration/GreaseMonkeyConfigurationDialog.py</Source>
+    <Source>WebBrowser/GreaseMonkey/GreaseMonkeyConfiguration/GreaseMonkeyConfigurationListDelegate.py</Source>
+    <Source>WebBrowser/GreaseMonkey/GreaseMonkeyConfiguration/GreaseMonkeyConfigurationListWidget.py</Source>
+    <Source>WebBrowser/GreaseMonkey/GreaseMonkeyConfiguration/GreaseMonkeyConfigurationScriptInfoDialog.py</Source>
+    <Source>WebBrowser/GreaseMonkey/GreaseMonkeyConfiguration/__init__.py</Source>
+    <Source>WebBrowser/GreaseMonkey/GreaseMonkeyDownloader.py</Source>
+    <Source>WebBrowser/GreaseMonkey/GreaseMonkeyJavaScript.py</Source>
+    <Source>WebBrowser/GreaseMonkey/GreaseMonkeyManager.py</Source>
+    <Source>WebBrowser/GreaseMonkey/GreaseMonkeyScript.py</Source>
+    <Source>WebBrowser/GreaseMonkey/GreaseMonkeyUrlInterceptor.py</Source>
+    <Source>WebBrowser/GreaseMonkey/__init__.py</Source>
+    <Source>WebBrowser/History/HistoryCompleter.py</Source>
+    <Source>WebBrowser/History/HistoryDialog.py</Source>
+    <Source>WebBrowser/History/HistoryFilterModel.py</Source>
+    <Source>WebBrowser/History/HistoryManager.py</Source>
+    <Source>WebBrowser/History/HistoryMenu.py</Source>
+    <Source>WebBrowser/History/HistoryModel.py</Source>
+    <Source>WebBrowser/History/HistoryTreeModel.py</Source>
+    <Source>WebBrowser/History/__init__.py</Source>
+    <Source>WebBrowser/JavaScript/ExternalJsObject.py</Source>
+    <Source>WebBrowser/JavaScript/PasswordManagerJsObject.py</Source>
+    <Source>WebBrowser/JavaScript/StartPageJsObject.py</Source>
+    <Source>WebBrowser/JavaScript/__init__.py</Source>
+    <Source>WebBrowser/Network/EricSchemeHandler.py</Source>
+    <Source>WebBrowser/Network/FollowRedirectReply.py</Source>
+    <Source>WebBrowser/Network/LoadRequest.py</Source>
+    <Source>WebBrowser/Network/NetworkManager.py</Source>
+    <Source>WebBrowser/Network/NetworkUrlInterceptor.py</Source>
+    <Source>WebBrowser/Network/QtHelpSchemeHandler.py</Source>
+    <Source>WebBrowser/Network/SendRefererWhitelistDialog.py</Source>
+    <Source>WebBrowser/Network/SslErrorExceptionsDialog.py</Source>
+    <Source>WebBrowser/Network/UrlInterceptor.py</Source>
+    <Source>WebBrowser/Network/__init__.py</Source>
+    <Source>WebBrowser/OpenSearch/DefaultSearchEngines/DefaultSearchEngines_rc.py</Source>
+    <Source>WebBrowser/OpenSearch/DefaultSearchEngines/__init__.py</Source>
+    <Source>WebBrowser/OpenSearch/OpenSearchDialog.py</Source>
+    <Source>WebBrowser/OpenSearch/OpenSearchEditDialog.py</Source>
+    <Source>WebBrowser/OpenSearch/OpenSearchEngine.py</Source>
+    <Source>WebBrowser/OpenSearch/OpenSearchEngineAction.py</Source>
+    <Source>WebBrowser/OpenSearch/OpenSearchEngineModel.py</Source>
+    <Source>WebBrowser/OpenSearch/OpenSearchManager.py</Source>
+    <Source>WebBrowser/OpenSearch/OpenSearchReader.py</Source>
+    <Source>WebBrowser/OpenSearch/OpenSearchWriter.py</Source>
+    <Source>WebBrowser/OpenSearch/__init__.py</Source>
+    <Source>WebBrowser/PageScreenDialog.py</Source>
+    <Source>WebBrowser/Passwords/LoginForm.py</Source>
+    <Source>WebBrowser/Passwords/PasswordManager.py</Source>
+    <Source>WebBrowser/Passwords/PasswordModel.py</Source>
+    <Source>WebBrowser/Passwords/PasswordReader.py</Source>
+    <Source>WebBrowser/Passwords/PasswordWriter.py</Source>
+    <Source>WebBrowser/Passwords/PasswordsDialog.py</Source>
+    <Source>WebBrowser/Passwords/__init__.py</Source>
+    <Source>WebBrowser/PersonalInformationManager/PersonalDataDialog.py</Source>
+    <Source>WebBrowser/PersonalInformationManager/PersonalInformationManager.py</Source>
+    <Source>WebBrowser/PersonalInformationManager/__init__.py</Source>
+    <Source>WebBrowser/QtHelp/HelpDocsInstaller.py</Source>
+    <Source>WebBrowser/QtHelp/HelpIndexWidget.py</Source>
+    <Source>WebBrowser/QtHelp/HelpSearchWidget.py</Source>
+    <Source>WebBrowser/QtHelp/HelpTocWidget.py</Source>
+    <Source>WebBrowser/QtHelp/HelpTopicDialog.py</Source>
+    <Source>WebBrowser/QtHelp/QtHelpDocumentationDialog.py</Source>
+    <Source>WebBrowser/QtHelp/QtHelpFiltersDialog.py</Source>
+    <Source>WebBrowser/QtHelp/__init__.py</Source>
+    <Source>WebBrowser/SearchWidget.py</Source>
+    <Source>WebBrowser/SiteInfo/SiteInfoDialog.py</Source>
+    <Source>WebBrowser/SiteInfo/__init__.py</Source>
+    <Source>WebBrowser/SpeedDial/Page.py</Source>
+    <Source>WebBrowser/SpeedDial/PageThumbnailer.py</Source>
+    <Source>WebBrowser/SpeedDial/SpeedDial.py</Source>
+    <Source>WebBrowser/SpeedDial/SpeedDialReader.py</Source>
+    <Source>WebBrowser/SpeedDial/SpeedDialWriter.py</Source>
+    <Source>WebBrowser/SpeedDial/__init__.py</Source>
+    <Source>WebBrowser/Sync/DirectorySyncHandler.py</Source>
+    <Source>WebBrowser/Sync/FtpSyncHandler.py</Source>
+    <Source>WebBrowser/Sync/SyncAssistantDialog.py</Source>
+    <Source>WebBrowser/Sync/SyncCheckPage.py</Source>
+    <Source>WebBrowser/Sync/SyncDataPage.py</Source>
+    <Source>WebBrowser/Sync/SyncDirectorySettingsPage.py</Source>
+    <Source>WebBrowser/Sync/SyncEncryptionPage.py</Source>
+    <Source>WebBrowser/Sync/SyncFtpSettingsPage.py</Source>
+    <Source>WebBrowser/Sync/SyncGlobals.py</Source>
+    <Source>WebBrowser/Sync/SyncHandler.py</Source>
+    <Source>WebBrowser/Sync/SyncHostTypePage.py</Source>
+    <Source>WebBrowser/Sync/SyncManager.py</Source>
+    <Source>WebBrowser/Sync/__init__.py</Source>
+    <Source>WebBrowser/Tools/DelayedFileWatcher.py</Source>
+    <Source>WebBrowser/Tools/Scripts.py</Source>
+    <Source>WebBrowser/Tools/WebBrowserTools.py</Source>
+    <Source>WebBrowser/Tools/WebHitTestResult.py</Source>
+    <Source>WebBrowser/Tools/WebIconDialog.py</Source>
+    <Source>WebBrowser/Tools/WebIconLoader.py</Source>
+    <Source>WebBrowser/Tools/WebIconProvider.py</Source>
+    <Source>WebBrowser/Tools/__init__.py</Source>
+    <Source>WebBrowser/UrlBar/BookmarkActionSelectionDialog.py</Source>
+    <Source>WebBrowser/UrlBar/BookmarkInfoDialog.py</Source>
+    <Source>WebBrowser/UrlBar/FavIconLabel.py</Source>
+    <Source>WebBrowser/UrlBar/StackedUrlBar.py</Source>
+    <Source>WebBrowser/UrlBar/UrlBar.py</Source>
+    <Source>WebBrowser/UrlBar/__init__.py</Source>
+    <Source>WebBrowser/VirusTotal/VirusTotalApi.py</Source>
+    <Source>WebBrowser/VirusTotal/VirusTotalDomainReportDialog.py</Source>
+    <Source>WebBrowser/VirusTotal/VirusTotalIpReportDialog.py</Source>
+    <Source>WebBrowser/VirusTotal/VirusTotalWhoisDialog.py</Source>
+    <Source>WebBrowser/VirusTotal/__init__.py</Source>
+    <Source>WebBrowser/WebBrowserClearPrivateDataDialog.py</Source>
+    <Source>WebBrowser/WebBrowserJavaScriptConsole.py</Source>
+    <Source>WebBrowser/WebBrowserLanguagesDialog.py</Source>
+    <Source>WebBrowser/WebBrowserPage.py</Source>
+    <Source>WebBrowser/WebBrowserSnap.py</Source>
+    <Source>WebBrowser/WebBrowserTabBar.py</Source>
+    <Source>WebBrowser/WebBrowserTabWidget.py</Source>
+    <Source>WebBrowser/WebBrowserView.py</Source>
+    <Source>WebBrowser/WebBrowserWebSearchWidget.py</Source>
+    <Source>WebBrowser/WebBrowserWindow.py</Source>
+    <Source>WebBrowser/WebInspector.py</Source>
+    <Source>WebBrowser/ZoomManager/ZoomManager.py</Source>
+    <Source>WebBrowser/ZoomManager/ZoomValuesDialog.py</Source>
+    <Source>WebBrowser/ZoomManager/ZoomValuesModel.py</Source>
+    <Source>WebBrowser/ZoomManager/__init__.py</Source>
+    <Source>WebBrowser/__init__.py</Source>
+    <Source>WebBrowser/data/html_rc.py</Source>
+    <Source>WebBrowser/data/icons_rc.py</Source>
+    <Source>WebBrowser/data/javascript_rc.py</Source>
+    <Source>WebBrowser/data/qml_rc.py</Source>
     <Source>__init__.py</Source>
     <Source>cleanupSource.py</Source>
     <Source>compileUiFiles.py</Source>
     <Source>eric6.py</Source>
     <Source>eric6.pyw</Source>
     <Source>eric6_api.py</Source>
+    <Source>eric6_browser.py</Source>
+    <Source>eric6_browser.pyw</Source>
     <Source>eric6_compare.py</Source>
     <Source>eric6_compare.pyw</Source>
     <Source>eric6_configure.py</Source>
@@ -1603,6 +1796,8 @@
     <Form>Preferences/ConfigurationPages/TrayStarterPage.ui</Form>
     <Form>Preferences/ConfigurationPages/VcsPage.ui</Form>
     <Form>Preferences/ConfigurationPages/ViewmanagerPage.ui</Form>
+    <Form>Preferences/ConfigurationPages/WebBrowserAppearancePage.ui</Form>
+    <Form>Preferences/ConfigurationPages/WebBrowserPage.ui</Form>
     <Form>Preferences/MouseClickDialog.ui</Form>
     <Form>Preferences/ProgramsDialog.ui</Form>
     <Form>Preferences/ShortcutDialog.ui</Form>
@@ -1659,6 +1854,55 @@
     <Form>VCS/CommandOptionsDialog.ui</Form>
     <Form>VCS/RepositoryInfoDialog.ui</Form>
     <Form>ViewManager/BookmarkedFilesDialog.ui</Form>
+    <Form>WebBrowser/AdBlock/AdBlockDialog.ui</Form>
+    <Form>WebBrowser/AdBlock/AdBlockExceptionsDialog.ui</Form>
+    <Form>WebBrowser/Bookmarks/AddBookmarkDialog.ui</Form>
+    <Form>WebBrowser/Bookmarks/BookmarkPropertiesDialog.ui</Form>
+    <Form>WebBrowser/Bookmarks/BookmarksDialog.ui</Form>
+    <Form>WebBrowser/Bookmarks/BookmarksImportDialog.ui</Form>
+    <Form>WebBrowser/CookieJar/CookieDetailsDialog.ui</Form>
+    <Form>WebBrowser/CookieJar/CookiesConfigurationDialog.ui</Form>
+    <Form>WebBrowser/CookieJar/CookiesDialog.ui</Form>
+    <Form>WebBrowser/CookieJar/CookiesExceptionsDialog.ui</Form>
+    <Form>WebBrowser/Download/DownloadAskActionDialog.ui</Form>
+    <Form>WebBrowser/Download/DownloadItem.ui</Form>
+    <Form>WebBrowser/Download/DownloadManager.ui</Form>
+    <Form>WebBrowser/FeaturePermissions/FeaturePermissionsDialog.ui</Form>
+    <Form>WebBrowser/Feeds/FeedEditDialog.ui</Form>
+    <Form>WebBrowser/Feeds/FeedsDialog.ui</Form>
+    <Form>WebBrowser/Feeds/FeedsManager.ui</Form>
+    <Form>WebBrowser/FlashCookieManager/FlashCookieManagerDialog.ui</Form>
+    <Form>WebBrowser/GreaseMonkey/GreaseMonkeyAddScriptDialog.ui</Form>
+    <Form>WebBrowser/GreaseMonkey/GreaseMonkeyConfiguration/GreaseMonkeyConfigurationDialog.ui</Form>
+    <Form>WebBrowser/GreaseMonkey/GreaseMonkeyConfiguration/GreaseMonkeyConfigurationScriptInfoDialog.ui</Form>
+    <Form>WebBrowser/History/HistoryDialog.ui</Form>
+    <Form>WebBrowser/Network/SendRefererWhitelistDialog.ui</Form>
+    <Form>WebBrowser/Network/SslErrorExceptionsDialog.ui</Form>
+    <Form>WebBrowser/OpenSearch/OpenSearchDialog.ui</Form>
+    <Form>WebBrowser/OpenSearch/OpenSearchEditDialog.ui</Form>
+    <Form>WebBrowser/PageScreenDialog.ui</Form>
+    <Form>WebBrowser/Passwords/PasswordsDialog.ui</Form>
+    <Form>WebBrowser/PersonalInformationManager/PersonalDataDialog.ui</Form>
+    <Form>WebBrowser/QtHelp/HelpTopicDialog.ui</Form>
+    <Form>WebBrowser/QtHelp/QtHelpDocumentationDialog.ui</Form>
+    <Form>WebBrowser/QtHelp/QtHelpFiltersDialog.ui</Form>
+    <Form>WebBrowser/SearchWidget.ui</Form>
+    <Form>WebBrowser/SiteInfo/SiteInfoDialog.ui</Form>
+    <Form>WebBrowser/Sync/SyncCheckPage.ui</Form>
+    <Form>WebBrowser/Sync/SyncDataPage.ui</Form>
+    <Form>WebBrowser/Sync/SyncDirectorySettingsPage.ui</Form>
+    <Form>WebBrowser/Sync/SyncEncryptionPage.ui</Form>
+    <Form>WebBrowser/Sync/SyncFtpSettingsPage.ui</Form>
+    <Form>WebBrowser/Sync/SyncHostTypePage.ui</Form>
+    <Form>WebBrowser/Tools/WebIconDialog.ui</Form>
+    <Form>WebBrowser/UrlBar/BookmarkActionSelectionDialog.ui</Form>
+    <Form>WebBrowser/UrlBar/BookmarkInfoDialog.ui</Form>
+    <Form>WebBrowser/VirusTotal/VirusTotalDomainReportDialog.ui</Form>
+    <Form>WebBrowser/VirusTotal/VirusTotalIpReportDialog.ui</Form>
+    <Form>WebBrowser/VirusTotal/VirusTotalWhoisDialog.ui</Form>
+    <Form>WebBrowser/WebBrowserClearPrivateDataDialog.ui</Form>
+    <Form>WebBrowser/WebBrowserLanguagesDialog.ui</Form>
+    <Form>WebBrowser/ZoomManager/ZoomValuesDialog.ui</Form>
   </Forms>
   <Translations>
     <Translation>i18n/eric6_cs.qm</Translation>
@@ -1690,6 +1934,12 @@
     <Resource>Helpviewer/data/icons.qrc</Resource>
     <Resource>Helpviewer/data/javascript.qrc</Resource>
     <Resource>IconEditor/cursors/cursors.qrc</Resource>
+    <Resource>WebBrowser/Bookmarks/DefaultBookmarks.qrc</Resource>
+    <Resource>WebBrowser/OpenSearch/DefaultSearchEngines/DefaultSearchEngines.qrc</Resource>
+    <Resource>WebBrowser/data/html.qrc</Resource>
+    <Resource>WebBrowser/data/icons.qrc</Resource>
+    <Resource>WebBrowser/data/javascript.qrc</Resource>
+    <Resource>WebBrowser/data/qml.qrc</Resource>
   </Resources>
   <Interfaces/>
   <Others>
@@ -1794,19 +2044,56 @@
     <Other>ThirdParty/Pygments/pygments/PKG-INFO</Other>
     <Other>ThirdParty/Send2Trash/LICENSE</Other>
     <Other>ThirdParty/enum/LICENSE</Other>
+    <Other>WebBrowser/Bookmarks/DefaultBookmarks.xbel</Other>
+    <Other>WebBrowser/OpenSearch/DefaultSearchEngines/Amazoncom.xml</Other>
+    <Other>WebBrowser/OpenSearch/DefaultSearchEngines/Bing.xml</Other>
+    <Other>WebBrowser/OpenSearch/DefaultSearchEngines/DeEn_Beolingus.xml</Other>
+    <Other>WebBrowser/OpenSearch/DefaultSearchEngines/DuckDuckGo.xml</Other>
+    <Other>WebBrowser/OpenSearch/DefaultSearchEngines/Facebook.xml</Other>
+    <Other>WebBrowser/OpenSearch/DefaultSearchEngines/Google.xml</Other>
+    <Other>WebBrowser/OpenSearch/DefaultSearchEngines/Google_Im_Feeling_Lucky.xml</Other>
+    <Other>WebBrowser/OpenSearch/DefaultSearchEngines/LEO_DeuEng.xml</Other>
+    <Other>WebBrowser/OpenSearch/DefaultSearchEngines/LinuxMagazin.xml</Other>
+    <Other>WebBrowser/OpenSearch/DefaultSearchEngines/Reddit.xml</Other>
+    <Other>WebBrowser/OpenSearch/DefaultSearchEngines/Wikia.xml</Other>
+    <Other>WebBrowser/OpenSearch/DefaultSearchEngines/Wikia_en.xml</Other>
+    <Other>WebBrowser/OpenSearch/DefaultSearchEngines/Wikipedia.xml</Other>
+    <Other>WebBrowser/OpenSearch/DefaultSearchEngines/Wiktionary.xml</Other>
+    <Other>WebBrowser/OpenSearch/DefaultSearchEngines/Yahoo.xml</Other>
+    <Other>WebBrowser/OpenSearch/DefaultSearchEngines/YouTube.xml</Other>
+    <Other>WebBrowser/data/html/adblockPage.html</Other>
+    <Other>WebBrowser/data/html/speeddialPage.html</Other>
+    <Other>WebBrowser/data/html/startPage.html</Other>
+    <Other>WebBrowser/data/icons/adBlockPlus16.png</Other>
+    <Other>WebBrowser/data/icons/adBlockPlus64.png</Other>
+    <Other>WebBrowser/data/icons/box-border-small.png</Other>
+    <Other>WebBrowser/data/icons/brokenPage.png</Other>
+    <Other>WebBrowser/data/icons/close.png</Other>
+    <Other>WebBrowser/data/icons/edit.png</Other>
+    <Other>WebBrowser/data/icons/ericWeb16.png</Other>
+    <Other>WebBrowser/data/icons/ericWeb32.png</Other>
+    <Other>WebBrowser/data/icons/loading.gif</Other>
+    <Other>WebBrowser/data/icons/plus.png</Other>
+    <Other>WebBrowser/data/icons/reload.png</Other>
+    <Other>WebBrowser/data/icons/setting.png</Other>
+    <Other>WebBrowser/data/javascript/jquery-ui.js</Other>
+    <Other>WebBrowser/data/javascript/jquery.js</Other>
+    <Other>WebBrowser/data/javascript/qwebchannel.js</Other>
+    <Other>WebBrowser/data/qml/thumbnailer.qml</Other>
     <Other>changelog</Other>
     <Other>default.e4k</Other>
     <Other>default_Mac.e4k</Other>
     <Other>eric6.appdata.xml</Other>
     <Other>eric6.desktop</Other>
     <Other>eric6.e4p</Other>
+    <Other>eric6_browser.desktop</Other>
     <Other>eric6_webbrowser.desktop</Other>
     <Other>eric6config.linux</Other>
     <Other>icons</Other>
     <Other>pixmaps</Other>
     <Other>pylint.rc</Other>
   </Others>
-  <MainScript>eric6.py</MainScript>
+  <MainScript>eric6_browser.py</MainScript>
   <Vcs>
     <VcsType>Mercurial</VcsType>
     <VcsOptions>
@@ -2203,7 +2490,7 @@
               <string>ExcludeMessages</string>
             </key>
             <value>
-              <string>C101, E265, M811, N802, N803, N807, N808, N821, W293, E266, E402</string>
+              <string>C101, E265, E266, M811, N802, N803, N807, N808, N821, W293, E402</string>
             </value>
             <key>
               <string>FixCodes</string>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eric6_browser.desktop	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,15 @@
+[Desktop Entry]
+Version=1.0
+Type=Application
+Exec=eric6_browser@MARKER@
+MimeType=text/html;text/xml;application/xhtml+xml;x-scheme-handler/http;x-scheme-handler/https;
+Icon=ericWeb@MARKER@
+Terminal=false
+Name=eric6 Web Browser@PY_MARKER@
+Name[de]=eric6 Web Browser@PY_MARKER@
+Comment=Web Browser for PyQt5
+Comment[de]=Web Browser für PyQt5
+GenericName=Web Browser
+GenericName[de]=Web Browser
+Categories=Qt;Python;Network;WebBrowser;QtWebEngine
+StartupNotify=true
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eric6_browser.py	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,157 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2002 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Eric6 Web Browser.
+
+This is the main Python script that performs the necessary initialization
+of the web browser and starts the Qt event loop. This is a standalone version
+of the integrated web browser. It is based on QtWebEngine.
+"""
+
+from __future__ import unicode_literals
+
+import Toolbox.PyQt4ImportHook  # __IGNORE_WARNING__
+
+try:  # Only for Py2
+    import Globals.compatibility_fixes     # __IGNORE_WARNING__
+except (ImportError):
+    pass
+
+try:
+    import sip
+    sip.setdestroyonexit(False)
+except AttributeError:
+    pass
+
+import sys
+import os
+
+MIN_QT_VERSION = "5.6.0"
+
+from PyQt5.QtCore import qVersion
+if qVersion() < MIN_QT_VERSION:
+    from PyQt5.QtCore import QTimer
+    from PyQt5.QtWidgets import QApplication
+    from E5Gui import E5MessageBox
+    app = QApplication([])
+    QTimer.singleShot(0, lambda: E5MessageBox.critical(
+        None,
+        "eric6 Web Browser",
+        "You need at least Qt Version {0} to execute the web browser."
+          .format(MIN_QT_VERSION)))
+    app.exec_()
+    sys.exit(100)
+
+SettingsDir = None
+
+for arg in sys.argv[:]:
+    if arg.startswith("--config="):
+        import Globals
+        configDir = arg.replace("--config=", "")
+        Globals.setConfigDir(configDir)
+        sys.argv.remove(arg)
+    elif arg.startswith("--settings="):
+        from PyQt5.QtCore import QSettings
+        SettingsDir = os.path.expanduser(arg.replace("--settings=", ""))
+        if not os.path.isdir(SettingsDir):
+            os.makedirs(SettingsDir)
+        QSettings.setPath(QSettings.IniFormat, QSettings.UserScope,
+                          SettingsDir)
+        sys.argv.remove(arg)
+
+# make ThirdParty package available as a packages repository
+sys.path.insert(2, os.path.join(os.path.dirname(__file__),
+                                "ThirdParty", "Pygments"))
+
+try:
+    from PyQt5 import QtWebEngineWidgets    # __IGNORE_WARNING__
+except ImportError:
+    from PyQt5.QtCore import QTimer
+    from PyQt5.QtWidgets import QApplication
+    from E5Gui import E5MessageBox          # __IGNORE_WARNING__
+    app = QApplication([])
+    QTimer.singleShot(0, lambda: E5MessageBox.critical(
+        None,
+        "eric6 Web Browser",
+        "QtWebEngineWidgets is not installed but needed to execute the"
+        " web browser."))
+    app.exec_()
+    sys.exit(100)
+
+import Globals
+from Globals import AppInfo
+
+from Toolbox import Startup
+
+
+def createMainWidget(argv):
+    """
+    Function to create the main widget.
+    
+    @param argv list of commandline parameters (list of strings)
+    @return reference to the main widget (QWidget)
+    """
+    from WebBrowser.WebBrowserWindow import WebBrowserWindow
+    
+    searchWord = None
+    private = False
+    qthelp = False
+    
+    for arg in reversed(argv):
+        if arg.startswith("--search="):
+            searchWord = argv[1].split("=", 1)[1]
+            argv.remove(arg)
+        elif arg == "--private":
+            private = True
+            argv.remove(arg)
+        elif arg == "--qthelp":
+            qthelp = True
+            argv.remove(arg)
+        elif arg.startswith("--"):
+            argv.remove(arg)
+    
+    try:
+        home = argv[1]
+    except IndexError:
+        home = ""
+    
+    browser = WebBrowserWindow(home, '.', None, 'web_browser',
+                               searchWord=searchWord, private=private,
+                               settingsDir=SettingsDir, qthelp=qthelp)
+    return browser
+
+
+def main():
+    """
+    Main entry point into the application.
+    """
+    options = [
+        ("--config=configDir",
+         "use the given directory as the one containing the config files"),
+        ("--private", "start the browser in private browsing mode"),
+        ("--qthelp", "start the browser with support for QtHelp"),
+        ("--search=word", "search for the given word"),
+        ("--settings=settingsDir",
+         "use the given directory to store the settings files"),
+    ]
+    appinfo = AppInfo.makeAppInfo(sys.argv,
+                                  "eric6 Web Browser",
+                                  "file",
+                                  "web browser",
+                                  options)
+    
+    if not Globals.checkBlacklistedVersions():
+        sys.exit(100)
+    
+    res = Startup.simpleAppStartup(sys.argv,
+                                   appinfo,
+                                   createMainWidget,
+                                   installErrorHandler=True)
+    sys.exit(res)
+
+if __name__ == '__main__':
+    main()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eric6_browser.pyw	Mon Mar 28 11:59:42 2016 +0200
@@ -0,0 +1,14 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2011 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the Windows entry point.
+"""
+
+from __future__ import unicode_literals
+
+from eric6_browser import main
+
+main()
--- a/eric6_configure.py	Mon Mar 28 11:52:38 2016 +0200
+++ b/eric6_configure.py	Mon Mar 28 11:59:42 2016 +0200
@@ -37,6 +37,16 @@
                           settingsDir)
         sys.argv.remove(arg)
 
+from PyQt5.QtCore import qVersion
+if qVersion() < "5.6.0":
+    WEBENGINE_AVAILABLE = False
+else:
+    try:
+        from PyQt5 import QtWebEngineWidgets    # __IGNORE_WARNING__
+        WEBENGINE_AVAILABLE = True
+    except ImportError:
+        WEBENGINE_AVAILABLE = False
+
 # make ThirdParty package available as a packages repository
 sys.path.insert(2, os.path.join(os.path.dirname(__file__),
                                 "ThirdParty", "Pygments"))
@@ -54,7 +64,7 @@
     @return reference to the main widget (QWidget)
     """
     from Preferences.ConfigurationDialog import ConfigurationWindow
-    w = ConfigurationWindow()
+    w = ConfigurationWindow(webEngine=WEBENGINE_AVAILABLE)
     w.show()
     w.showConfigurationPageByName("empty")
     return w
Binary file icons/default/audio-video.png has changed
Binary file icons/default/audiocapture.png has changed
Binary file icons/default/camera.png has changed
Binary file icons/default/icons.png has changed
Binary file icons/default/network-server.png has changed
Binary file icons/default/privateMode.png has changed
Binary file icons/default/w3.png has changed
--- a/install.py	Mon Mar 28 11:52:38 2016 +0200
+++ b/install.py	Mon Mar 28 11:59:42 2016 +0200
@@ -511,7 +511,10 @@
         for name in ["/usr/share/applications/eric6" + marker + ".desktop",
                      "/usr/share/appdata/eric6" + marker + ".appdata.xml",
                      "/usr/share/applications/eric6_webbrowser" + marker +
-                     ".desktop"]:
+                     ".desktop",
+                     "/usr/share/applications/eric6_browser" + marker +
+                     ".desktop",
+                     ]:
             if os.path.exists(name):
                 os.remove(name)
     
@@ -527,7 +530,7 @@
         "eric6_plugininstall", "eric6_pluginuninstall",
         "eric6_pluginrepository", "eric6_sqlbrowser",
         "eric6_webbrowser", "eric6_iconeditor",
-        "eric6_snap", "eric6_hexeditor",
+        "eric6_snap", "eric6_hexeditor", "eric6_browser",
     ]
     if includePythonVariant:
         marker = PythonMarkers[sys.version_info.major]
@@ -637,7 +640,7 @@
                  "eric6_qregularexpression", "eric6_re", "eric6_snap",
                  "eric6_sqlbrowser", "eric6_tray", "eric6_trpreviewer",
                  "eric6_uipreviewer", "eric6_unittest", "eric6_webbrowser",
-                 "eric6"]:
+                 "eric6_browser", "eric6"]:
         wnames.append(createPyWrapper(cfg['ericDir'], name))
     
     # set install prefix, if not None
@@ -814,6 +817,10 @@
                 os.path.join(sourceDir, "eric6_webbrowser.desktop"),
                 os.path.join(dst, "eric6_webbrowser" + marker + ".desktop"),
                 marker)
+            copyDesktopFile(
+                os.path.join(sourceDir, "eric6_browser.desktop"),
+                os.path.join(dst, "eric6_browser" + marker + ".desktop"),
+                marker)
             dst = os.path.normpath(
                 os.path.join(distDir, "usr/share/appdata"))
             if not os.path.exists(dst):
@@ -843,6 +850,11 @@
                 "/usr/share/applications/eric6_webbrowser" + marker +
                 ".desktop",
                 marker)
+            copyDesktopFile(
+                os.path.join(sourceDir, "eric6_browser.desktop"),
+                "/usr/share/applications/eric6_browser" + marker +
+                ".desktop",
+                marker)
     
     # Create a Mac application bundle
     if sys.platform == "darwin":
--- a/uninstall.py	Mon Mar 28 11:52:38 2016 +0200
+++ b/uninstall.py	Mon Mar 28 11:59:42 2016 +0200
@@ -115,6 +115,8 @@
                      "/usr/share/appdata/eric6" + marker + ".appdata.xml",
                      "/usr/share/applications/eric6_webbrowser" + marker +
                      ".desktop",
+                     "/usr/share/applications/eric6_browser" + marker +
+                     ".desktop",
                      "/usr/share/pixmaps/eric" + marker + ".png",
                      "/usr/share/pixmaps/ericWeb" + marker + ".png"]:
             if os.path.exists(name):
@@ -132,7 +134,7 @@
         "eric6_plugininstall", "eric6_pluginuninstall",
         "eric6_pluginrepository", "eric6_sqlbrowser",
         "eric6_webbrowser", "eric6_iconeditor",
-        "eric6_snap", "eric6_hexeditor",
+        "eric6_snap", "eric6_hexeditor", "eric6_browser",
     ]
     if includePythonVariant:
         marker = PythonMarkers[sys.version_info.major]

eric ide

mercurial