Merged the QtWebEngine branch into the default development branch to finalize the port in here.

Sun, 03 Apr 2016 16:33:37 +0200

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Sun, 03 Apr 2016 16:33:37 +0200
changeset 4915
8081031061a2
parent 4911
652cb0520e9a (current diff)
parent 4914
d095b591e6fa (diff)
child 4917
682750cc7bd5

Merged the QtWebEngine branch into the default development branch to finalize the port in here.

install.py file | annotate | diff | comparison | revisions
--- a/.hgignore	Sun Apr 03 12:29:37 2016 +0200
+++ b/.hgignore	Sun Apr 03 16:33:37 2016 +0200
@@ -16,3 +16,4 @@
 glob:__pycache__
 glob:**.DS_Store
 glob:**.coverage
+glob:GPUCache
--- a/DebugClients/Python/DebugClientThreads.py	Sun Apr 03 12:29:37 2016 +0200
+++ b/DebugClients/Python/DebugClientThreads.py	Sun Apr 03 16:33:37 2016 +0200
@@ -200,4 +200,4 @@
 
 #
 # eflag: FileType = Python2
-# eflag: noqa = M601, M702
+# eflag: noqa = M601, M702, E402
--- a/DebugClients/Python3/DebugClientThreads.py	Sun Apr 03 12:29:37 2016 +0200
+++ b/DebugClients/Python3/DebugClientThreads.py	Sun Apr 03 16:33:37 2016 +0200
@@ -199,4 +199,4 @@
     debugClient.main()
 
 #
-# eflag: noqa = M702
+# eflag: noqa = M702, E402
Binary file Documentation/Help/source.qch has changed
--- a/E5Gui/E5ErrorMessage.py	Sun Apr 03 12:29:37 2016 +0200
+++ b/E5Gui/E5ErrorMessage.py	Sun Apr 03 16:33:37 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	Sun Apr 03 12:29:37 2016 +0200
+++ b/Helpviewer/HelpBrowserWV.py	Sun Apr 03 16:33:37 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/Helpviewer/HelpWindow.py	Sun Apr 03 12:29:37 2016 +0200
+++ b/Helpviewer/HelpWindow.py	Sun Apr 03 16:33:37 2016 +0200
@@ -71,8 +71,6 @@
     
     helpwindows = []
 
-    maxMenuFilePathLen = 75
-    
     _fromEric = False
     useQtHelp = QTHELP_AVAILABLE
     
--- a/PluginManager/PluginManager.py	Sun Apr 03 12:29:37 2016 +0200
+++ b/PluginManager/PluginManager.py	Sun Apr 03 16:33:37 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	Sun Apr 03 12:29:37 2016 +0200
+++ b/Preferences/ConfigurationDialog.py	Sun Apr 03 16:33:37 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/HelpViewersPage.py	Sun Apr 03 12:29:37 2016 +0200
+++ b/Preferences/ConfigurationPages/HelpViewersPage.py	Sun Apr 03 16:33:37 2016 +0200
@@ -9,7 +9,21 @@
 
 from __future__ import unicode_literals
 
+from PyQt5.QtCore import qVersion
 from PyQt5.QtWidgets import QButtonGroup
+try:
+    from PyQt5 import QtWebKit      # __IGNORE_WARNING__
+    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 E5Gui.E5PathPicker import E5PathPickerModes
 
@@ -40,14 +54,14 @@
         self.helpViewerGroup.addButton(self.customViewerButton)
         
         # set initial values
-        hvId = Preferences.getHelp("HelpViewerType")
-        # check availability of QtWebKit
-        try:
-            from PyQt5 import QtWebKit      # __IGNORE_WARNING__
-        except ImportError:
-            # not available, reset help viewer to default
+        if WEBENGINE_AVAILABLE:
+            hvId = Preferences.getWebBrowser("HelpViewerType")
+        else:
+            hvId = Preferences.getHelp("HelpViewerType")
+        if not WEBENGINE_AVAILABLE and not WEBKIT_AVAILABLE:
             if hvId == 1:
-                hvId = Preferences.Prefs.helpDefaults["HelpViewerType"]
+                hvId = Preferences.Prefs.webBrowserDefaultsDefaults[
+                    "HelpViewerType"]
             self.helpBrowserButton.setEnabled(False)
         
         if hvId == 1:
@@ -74,6 +88,7 @@
         elif self.customViewerButton.isChecked():
             hvId = 4
         Preferences.setHelp("HelpViewerType", hvId)
+        Preferences.setWebBrowser("HelpViewerType", hvId)
         Preferences.setHelp(
             "CustomViewer",
             self.customViewerPicker.text())
--- a/Preferences/ConfigurationPages/NetworkPage.py	Sun Apr 03 12:29:37 2016 +0200
+++ b/Preferences/ConfigurationPages/NetworkPage.py	Sun Apr 03 16:33:37 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(
@@ -60,17 +68,6 @@
         self.downloadDirPicker.setText(Preferences.getUI("DownloadPath"))
         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()
         
         # HTTP proxy
         self.httpProxyHostEdit.setText(
@@ -111,6 +108,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 +160,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 +169,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 +267,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	Sun Apr 03 12:29:37 2016 +0200
+++ b/Preferences/ConfigurationPages/PrinterPage.py	Sun Apr 03 16:33:37 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	Sun Apr 03 12:29:37 2016 +0200
+++ b/Preferences/ConfigurationPages/PrinterPage.ui	Sun Apr 03 16:33:37 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	Sun Apr 03 12:29:37 2016 +0200
+++ b/Preferences/ConfigurationPages/SecurityPage.py	Sun Apr 03 16:33:37 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	Sun Apr 03 12:29:37 2016 +0200
+++ b/Preferences/ConfigurationPages/SecurityPage.ui	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,300 @@
+# -*- 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
+
+
+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)
+        
+        # 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.7?
+##        self.jsCloseWindowsCheckBox.setChecked(
+##            Preferences.getWebBrowser("JavaScriptCanCloseWindows"))
+        self.jsClipboardCheckBox.setChecked(
+            Preferences.getWebBrowser("JavaScriptCanAccessClipboard"))
+        self.pluginsCheckBox.setChecked(
+            Preferences.getWebBrowser("PluginsEnabled"))
+        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: Qt 5.7?
+        # Hide entries not yet supported
+        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.7?
+##        Preferences.setWebBrowser(
+##            "JavaScriptCanCloseWindows",
+##            self.jsCloseWindowsCheckBox.isChecked())
+        Preferences.setWebBrowser(
+            "JavaScriptCanAccessClipboard",
+            self.jsClipboardCheckBox.isChecked())
+        Preferences.setWebBrowser(
+            "PluginsEnabled",
+            self.pluginsCheckBox.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	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,681 @@
+<?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>
+    </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>
+     </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>webInspectorGroup</tabstop>
+  <tabstop>webInspectorPortSpinBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
--- a/Preferences/Shortcuts.py	Sun Apr 03 12:29:37 2016 +0200
+++ b/Preferences/Shortcuts.py	Sun Apr 03 16:33:37 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	Sun Apr 03 12:29:37 2016 +0200
+++ b/Preferences/__init__.py	Sun Apr 03 16:33:37 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,148 @@
         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),
+        "UserAgent": "",
+        # 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
+    }
+    if QWebEngineSettings:
+        webBrowserDefaults["HelpViewerType"] = 1      # eric browser
+    else:
+        webBrowserDefaults["HelpViewerType"] = 2      # Qt Assistant
+    
+    @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),
+            # TODO: Qt 5.7?
+##            "JavaScriptCanCloseWindows": webEngineSettings.testAttribute(
+##                QWebEngineSettings.JavascriptCanCloseWindows),
+            "JavaScriptCanAccessClipboard": webEngineSettings.testAttribute(
+                QWebEngineSettings.JavascriptCanAccessClipboard),
+            "PluginsEnabled": webEngineSettings.testAttribute(
+                QWebEngineSettings.PluginsEnabled),
+            "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 +1278,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 +2293,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 +2695,191 @@
             "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 QtWebKit based
+    # 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)
+    elif key == "HelpViewerType":
+        # special treatment to adjust for missing QtWebEngine
+        value = int(prefClass.settings.value(
+            "WebBrowser/" + key, prefClass.helpDefaults[key]))
+        if QWebEngineSettings is None:
+            value = prefClass.helpDefaults[key]
+        return value
+    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 ["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 ["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 QtWebKit based
+    # 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 +3535,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	Sun Apr 03 12:29:37 2016 +0200
+++ b/UI/UserInterface.py	Sun Apr 03 16:33:37 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>""")
@@ -4846,7 +4864,10 @@
         if home.endswith(".chm"):
             self.__chmViewer(home)
         else:
-            hvType = Preferences.getHelp("HelpViewerType")
+            if WEBENGINE_AVAILABLE:
+                hvType = Preferences.getWebBrowser("HelpViewerType")
+            else:
+                hvType = Preferences.getHelp("HelpViewerType")
             if hvType == 1:
                 self.launchHelpViewer(home)
             elif hvType == 2:
@@ -4905,7 +4926,10 @@
         if home.endswith(".chm"):
             self.__chmViewer(home)
         else:
-            hvType = Preferences.getHelp("HelpViewerType")
+            if WEBENGINE_AVAILABLE:
+                hvType = Preferences.getWebBrowser("HelpViewerType")
+            else:
+                hvType = Preferences.getHelp("HelpViewerType")
             if hvType == 1:
                 self.launchHelpViewer(home)
             elif hvType == 2:
@@ -4971,7 +4995,10 @@
             else:
                 home = "file://" + home
         
-        hvType = Preferences.getHelp("HelpViewerType")
+        if WEBENGINE_AVAILABLE:
+            hvType = Preferences.getWebBrowser("HelpViewerType")
+        else:
+            hvType = Preferences.getHelp("HelpViewerType")
         if hvType == 1:
             self.launchHelpViewer(home)
         elif hvType == 2:
@@ -5033,7 +5060,10 @@
         else:
             home = pyqt4DocDir
         
-        hvType = Preferences.getHelp("HelpViewerType")
+        if WEBENGINE_AVAILABLE:
+            hvType = Preferences.getWebBrowser("HelpViewerType")
+        else:
+            hvType = Preferences.getHelp("HelpViewerType")
         if hvType == 1:
             self.launchHelpViewer(home)
         elif hvType == 2:
@@ -5097,7 +5127,10 @@
         else:
             home = pyqt5DocDir
         
-        hvType = Preferences.getHelp("HelpViewerType")
+        if WEBENGINE_AVAILABLE:
+            hvType = Preferences.getWebBrowser("HelpViewerType")
+        else:
+            hvType = Preferences.getHelp("HelpViewerType")
         if hvType == 1:
             self.launchHelpViewer(home)
         elif hvType == 2:
@@ -5134,7 +5167,10 @@
             else:
                 home = "file://" + home
         
-        hvType = Preferences.getHelp("HelpViewerType")
+        if WEBENGINE_AVAILABLE:
+            hvType = Preferences.getWebBrowser("HelpViewerType")
+        else:
+            hvType = Preferences.getHelp("HelpViewerType")
         if hvType == 1:
             self.launchHelpViewer(home)
         elif hvType == 2:
@@ -5186,7 +5222,10 @@
         else:
             home = pysideDocDir
         
-        hvType = Preferences.getHelp("HelpViewerType")
+        if WEBENGINE_AVAILABLE:
+            hvType = Preferences.getWebBrowser("HelpViewerType")
+        else:
+            hvType = Preferences.getHelp("HelpViewerType")
         if hvType == 1:
             self.launchHelpViewer(home)
         elif hvType == 2:
@@ -5213,12 +5252,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 +5275,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 +5299,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 +5341,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 +5361,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	Sun Apr 03 12:29:37 2016 +0200
+++ b/Utilities/__init__.py	Sun Apr 03 16:33:37 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)
--- a/ViewManager/ViewManager.py	Sun Apr 03 12:29:37 2016 +0200
+++ b/ViewManager/ViewManager.py	Sun Apr 03 16:33:37 2016 +0200
@@ -11,7 +11,7 @@
 
 import os
 
-from PyQt5.QtCore import pyqtSignal, pyqtSlot, QSignalMapper, QTimer, \
+from PyQt5.QtCore import pyqtSignal, QSignalMapper, QTimer, \
     QFileInfo, QRegExp, Qt, QCoreApplication
 from PyQt5.QtGui import QColor, QKeySequence, QPalette, QPixmap
 from PyQt5.QtWidgets import QLineEdit, QToolBar, QWidgetAction, QDialog, \
@@ -6574,7 +6574,6 @@
         if editor:
             self.editorRenamedEd.emit(editor)
         
-##    @pyqtSlot(str, int, int)
     def __cursorChanged(self, fn, line, pos):
         """
         Private slot to handle the cursorChanged signal.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/AdBlock/AdBlockDialog.py	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,716 @@
+# -*- 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, QNetworkRequest
+
+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
+        self.__downloading = WebBrowserWindow.networkManager().get(
+            QNetworkRequest(self.location()))
+        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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,614 @@
+# -*- 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_TRANSLATE_NOOP, QObject, QFile, \
+    QIODevice, QXmlStreamReader, 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
+
+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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,241 @@
+# -*- 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().triggerPageAction(QWebEnginePage.Back)
+        elif self._mouseButton == Qt.XButton2:
+            self.__mw.currentBrowser().triggerPageAction(
+                QWebEnginePage.Forward)
+        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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,170 @@
+# -*- 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.7?
+##            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."),
+            QWebEnginePage.MouseLock:
+                self.tr("{0} wants to lock your mouse."),
+        }
+        self.__permissionFeatureIconNames = {
+            # TODO: Qt 5.7?
+##            QWebEnginePage.Notifications: "notification.png",
+            QWebEnginePage.Geolocation: "geolocation.png",
+            QWebEnginePage.MediaAudioCapture: "audiocapture.png",
+            QWebEnginePage.MediaVideoCapture: "camera.png",
+            QWebEnginePage.MediaAudioVideoCapture: "audio-video.png",
+            QWebEnginePage.MouseLock: "mouse.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	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,197 @@
+# -*- 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.7?
+##            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: [],
+            },
+            QWebEnginePage.MouseLock: {
+                QWebEnginePage.PermissionGrantedByUser: [],
+                QWebEnginePage.PermissionDeniedByUser: [],
+            },
+        }
+        self.__featurePermissionsKeys = {
+            # TODO: Qt 5.7?
+##            (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",
+            (QWebEnginePage.MouseLock,
+             QWebEnginePage.PermissionGrantedByUser):
+            "MouseLockGranted",
+            (QWebEnginePage.MouseLock,
+             QWebEnginePage.PermissionDeniedByUser):
+            "MouseLockDenied",
+        }
+        
+        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	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,247 @@
+# -*- 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
+        # TODO: Qt 5.7?
+##        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.mouseLockList = QTreeWidget()
+        self.mouseLockList.setAlternatingRowColors(True)
+        self.mouseLockList.setSelectionMode(QAbstractItemView.ExtendedSelection)
+        self.mouseLockList.setRootIsDecorated(False)
+        self.mouseLockList.setItemsExpandable(False)
+        self.mouseLockList.setAllColumnsShowFocus(True)
+        self.mouseLockList.setObjectName("mouseLockList")
+        self.mouseLockList.setSortingEnabled(True)
+        self.mouseLockList.headerItem().setText(0, self.tr("Host"))
+        self.mouseLockList.headerItem().setText(1, self.tr("Permission"))
+        self.tabWidget.addTab(
+            self.mouseLockList,
+            UI.PixmapCache.getIcon("mouse.png"),
+            self.tr("Mouse Lock"))
+        
+        # TODO: Qt 5.7?
+##        self.setTabOrder(self.tabWidget, self.notifList)
+##        self.setTabOrder(self.notifList, self.geoList)
+        self.setTabOrder(self.tabWidget, 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.mouseLockList)
+        self.setTabOrder(self.mouseLockList, 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.7?
+##            QWebEnginePage.Notifications: self.notifList,
+            QWebEnginePage.Geolocation: self.geoList,
+            QWebEnginePage.MediaAudioCapture: self.micList,
+            QWebEnginePage.MediaVideoCapture: self.camList,
+            QWebEnginePage.MediaAudioVideoCapture: self.micCamList,
+            QWebEnginePage.MouseLock: self.mouseLockList,
+        }
+        
+        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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,95 @@
+# -*- 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
+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)
+            urlString = url.toDisplayString(QUrl.FullyDecoded)
+        
+        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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,363 @@
+# -*- 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.__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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,191 @@
+# -*- 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
+from PyQt5.QtGui import QFontMetrics, QPalette, QFont
+from PyQt5.QtWidgets import QStyle, QStyledItemDelegate, QApplication
+from PyQt5.QtWidgets import 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,179 @@
+# -*- 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, QNetworkRequest
+
+from E5Gui import E5MessageBox
+
+from WebBrowser.WebBrowserWindow import WebBrowserWindow
+
+
+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 = WebBrowserWindow.networkManager().get(
+            QNetworkRequest(url))
+        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 = WebBrowserWindow.networkManager().get(
+                QNetworkRequest(self.__requireUrls.pop(0)))
+            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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,298 @@
+# -*- 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
+
+
+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)
+    
+    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	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,391 @@
+# -*- 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 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	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,45 @@
+# -*- 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 PyQt5.QtWebEngineCore import QWebEngineUrlRequestInfo
+
+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.navigationType() != \
+                QWebEngineUrlRequestInfo.NavigationTypeLink:
+            return
+        
+        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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,505 @@
+# -*- 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
+
+
+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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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/NetworkManager.py	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,323 @@
+# -*- 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, \
+    QNetworkRequest
+
+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()
+    
+    def createRequest(self, op, request, data):
+        """
+        Public method to launch a network action.
+        
+        @param op operation to be performed
+        @type QNetworkAccessManager.Operation
+        @param request request to be operated on
+        @type QNetworkRequest
+        @param data reference to the data to be sent
+        @type QIODevice
+        @return reference to the network reply
+        @rtype QNetworkReply
+        """
+        req = QNetworkRequest(request)
+        req.setAttribute(QNetworkRequest.SpdyAllowedAttribute, True)
+        req.setAttribute(QNetworkRequest.FollowRedirectsAttribute, True)
+        
+        return super(NetworkManager, self).createRequest(op, req, data)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Network/NetworkUrlInterceptor.py	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,92 @@
+# -*- 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
+
+from ..WebBrowserPage import WebBrowserPage
+
+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"")
+        
+        # User Agents header
+        userAgent = WebBrowserPage.userAgentForUrl(info.requestUrl())
+        info.setHttpHeader(b"User-Agent", userAgent.encode())
+        
+        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	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,204 @@
+# -*- 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
+
+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")
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Network/SendRefererWhitelistDialog.py	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,524 @@
+# -*- 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, QUrlQuery, \
+    QByteArray, QBuffer, QIODevice, QObject
+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":
+            urlQuery = QUrlQuery(ret)
+            for parameter in self._searchParameters:
+                urlQuery.addQueryItem(
+                    parameter[0],
+                    self.parseTemplate(searchTerm, parameter[1]))
+            ret.setQuery(urlQuery)
+        
+        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":
+            urlQuery = QUrlQuery(ret)
+            for parameter in self._suggestionsParameters:
+                urlQuery.addQueryItem(
+                    parameter[0],
+                    self.parseTemplate(searchTerm, parameter[1]))
+            ret.setQuery(urlQuery)
+        
+        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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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"]
+        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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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
+
+
+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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,135 @@
+# -*- 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.__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,
+                Qt.SmoothTransformation))
+        self.thumbnailCreated.emit(pixmap)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/SpeedDial/SpeedDial.py	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,431 @@
+# -*- 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:"https://www.riverbankcomputing.com/"|'\
+                'title:"PyQt Web Site";'\
+                'url:"http://www.qt.io/"|title:"Qt Web Site";'\
+                'url:"http://blog.qt.io/"|title:"Qt Blog";'\
+                'url:"https://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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,277 @@
+# -*- 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())
+        
+        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())
+    
+    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	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,410 @@
+# -*- 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())
+        
+        # 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())
+    
+    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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,59 @@
+# -*- 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,72 @@
+# -*- 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,276 @@
+# -*- 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
+            
+            # 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
+            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()
+            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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,54 @@
+# -*- 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 PyQt5.QtNetwork import QNetworkRequest
+
+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 = networkManager.get(QNetworkRequest(url))
+        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	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,250 @@
+# -*- 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)
+        for iconUrlStr in self.__iconsDB:
+            if iconUrlStr.startswith(urlStr):
+                return self.__iconsDB[iconUrlStr]
+        
+        # try replacing http scheme with https scheme
+        url = QUrl(url)
+        if url.scheme() == "http":
+            url.setScheme("https")
+        urlStr = self.__urlToString(url)
+        for iconUrlStr in self.__iconsDB:
+            if iconUrlStr.startswith(urlStr):
+                return self.__iconsDB[iconUrlStr]
+        
+        if 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,381 @@
+# -*- 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
+from PyQt5.QtGui import QColor, QPalette, QLinearGradient, QIcon
+from PyQt5.QtWidgets import QDialog, QApplication
+from PyQt5.QtWebEngineWidgets import QWebEnginePage
+
+from E5Gui.E5LineEdit import E5LineEdit
+from E5Gui.E5LineEditButton import E5LineEditButton
+
+from WebBrowser.WebBrowserWindow import WebBrowserWindow
+
+from .FavIconLabel import FavIconLabel
+import UI.PixmapCache
+import Preferences
+
+
+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)
+        
+        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)
+    
+    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.
+        """
+        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)
+        """
+        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)
+    
+    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":
+                    backgroundColor = Preferences.getWebBrowser(
+                        "SaveUrlColor")
+                p.setBrush(QPalette.Base, backgroundColor)
+                p.setBrush(QPalette.Text, foregroundColor)
+            else:
+                if self.__browser.url().scheme() == "https":
+                    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	Sun Apr 03 16:33:37 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/UserAgent/UserAgentDefaults.qrc	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,6 @@
+<!DOCTYPE RCC>
+<RCC version="1.0">
+<qresource>
+  <file>UserAgentDefaults.xml</file>
+</qresource>
+</RCC>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/UserAgent/UserAgentDefaults.xml	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,53 @@
+<useragentswitcher>
+    <useragentmenu title="Firefox">
+        <useragent description="Firefox 9.0.1 (Windows)" useragent="Mozilla/5.0 (Windows; U; Windows NT 6.2; rv:9.0.1) Gecko/20100101 Firefox/9.0.1"/>
+        <useragent description="Firefox 4.0.1 (Windows)" useragent="Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:2.0.1) Gecko/20100101 Firefox/4.0.1"/>
+        <useragent description="Firefox 3.5.3 (Windows)" useragent="Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3"/>
+    </useragentmenu>
+
+    <useragentmenu title="Chrome">
+        <useragent description="Chrome 18.0 (Windows)" useragent="Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.12 (KHTML, like Gecko) Chrome/18.6.872.0 Safari/535.12"/>
+        <useragent description="Chrome 17.0 (Windows)" useragent="Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.12 Safari/535.11"/>
+        <useragent description="Chrome 12.0 (Windows)" useragent="Mozilla/5.0 (Windows NT 6.1) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.0 Safari/534.30"/>
+        <useragent description="Chrome 11.0 (Windows)" useragent="Mozilla/5.0 (Windows NT 6.1) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.50 Safari/534.24"/>
+    </useragentmenu>
+
+    <useragentmenu title="Internet Explorer">
+        <useragent description="Internet Explorer 10.0" useragent="Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1)"/>
+        <useragent description="Internet Explorer 9.0" useragent="Mozilla/4.0 (compatible; MSIE 9.0; Windows NT 6.1)"/>
+        <useragent description="Internet Explorer 8.0" useragent="Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1)"/>
+        <useragent description="Internet Explorer 7.0" useragent="Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)"/>
+        <useragent description="Internet Explorer 6.0" useragent="Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)"/>
+    </useragentmenu>
+
+    <useragentmenu title="Opera">
+        <useragent description="Opera 12.0 (Linux)" useragent="Opera/9.80 (X11; Linux x86_64; U) Presto/2.9.181 Version/12.00"/>
+        <useragent description="Opera 12.0 (Mac)" useragent="Opera/9.80 (Macintosh; Intel Mac OS X 10.6.7; U) Presto/2.9.181 Version/12.00"/>
+        <useragent description="Opera 12.0 (Windows)" useragent="Opera/9.80 (Windows NT 6.1; U) Presto/2.9.181 Version/12.00"/>
+        <useragent description="Opera 11.1 (Linux)" useragent="Opera/9.80 (X11; Linux x86_64; U; en) Presto/2.8.131 Version/11.10"/>
+        <useragent description="Opera 11.1 (Mac)" useragent="Opera/9.80 (Macintosh; Intel Mac OS X 10.6.7; U; en) Presto/2.8.131 Version/11.10"/>
+        <useragent description="Opera 11.1 (Windows)" useragent="Opera/9.80 (Windows NT 6.1; U; en) Presto/2.8.131 Version/11.10"/>
+        <useragent description="Opera 10.0 (Linux)" useragent="Opera/9.80 (X11; Linux x86_64; U; de) Presto/2.2.15 Version/10.00"/>
+        <useragent description="Opera 10.0 (Mac)" useragent="Opera/9.80 (Macintosh; Intel Mac OS X; U; en) Presto/2.2.15 Version/10.00"/>
+        <useragent description="Opera 10.0 (Windows)" useragent="Opera/9.80 (Windows NT 6.0; U; en) Presto/2.2.15 Version/10.00"/>
+    </useragentmenu>
+
+    <useragentmenu title="Safari">
+        <useragent description="Safari 5.0.5 (Mac)" useragent="Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_7_2; en-us) AppleWebKit/535.12 (KHTML, like Gecko) Version/5.0.5 Safari/535.12"/>
+        <useragent description="Safari 5.0.4 (Mac)" useragent="Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; en-us) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27"/>
+        <useragent description="Safari 4.0.4 (Mac)" useragent="Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_1; en-us) AppleWebKit/531.21.8 (KHTML, like Gecko) Version/4.0.4 Safari/531.21.10"/>
+        <separator/>
+        <useragent description="Mobile Safari 4.3.2 (iPad)" useragent="Mozilla/5.0 (iPad; U; CPU OS 4_3_2 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8H7 Safari/6533.18.5"/>
+        <useragent description="Mobile Safari 4.3.2 (iPhone)" useragent="Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_3_2 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8H7 Safari/6533.18.5"/>
+        <useragent description="Mobile Safari 4.3.2 (iPod touch)" useragent="Mozilla/5.0 (iPod; U; CPU iPhone OS 4_3_2 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8H7 Safari/6533.18.5"/>
+        <useragent description="Mobile Safari 3.1.2 (iPhone)" useragent="Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_1_2 like Mac OS X; en-us) AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0 Mobile/7D11 Safari/528.16"/>
+        <useragent description="Mobile Safari 3.1.2 (iPod touch)" useragent="Mozilla/5.0 (iPod; U; CPU iPhone OS 3_1_2 like Mac OS X; en-us) AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0 Mobile/7D11 Safari/528.16"/>
+    </useragentmenu>
+
+    <useragentmenu title="v_a_r_i_o_u_s">
+        <useragent description="Googlebot 2.1" useragent="Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"/>
+        <useragent description="Msnbot 1.1" useragent="msnbot/1.1 (+http://search.msn.com/msnbot.htm)" appcodename="" appname="" appversion="" platform="" vendor="" vendorsub=""/>
+        <useragent description="Yahoo Slurp" useragent="Mozilla/5.0 (compatible; Yahoo! Slurp; http://help.yahoo.com/help/us/ysearch/slurp)" appcodename="" appname="" appversion="" platform="" vendor="" vendorsub=""/>
+    </useragentmenu>
+
+</useragentswitcher>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/UserAgent/UserAgentDefaults_rc.py	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,98 @@
+# -*- coding: utf-8 -*-
+
+# Resource object code
+#
+# Created: So. Juni 29 18:58:08 2014
+#      by: The Resource Compiler for PyQt (Qt v5.3.1)
+#
+# WARNING! All changes made in this file will be lost!
+
+from PyQt5 import QtCore
+
+qt_resource_data = b"\
+\x00\x00\x03\xd6\
+\x00\
+\x00\x15\xe1\x78\x9c\xd5\x58\xdb\x6e\xdb\x38\x10\x7d\xef\x57\xcc\
+\xfa\x29\xc6\xee\x52\xa2\xee\xae\xd3\x02\x45\xef\x68\xbd\x0d\xe0\
+\x66\xdb\x3e\x09\xb2\xcd\x44\x42\x65\x51\xa0\xe4\x38\xdd\xaf\xdf\
+\x21\xe5\x8b\x6c\xc7\x0a\x65\xab\x05\x1a\xe4\x41\x24\x67\x74\xce\
+\x99\x19\x72\x28\x5f\x2e\x0a\x26\xa2\x5b\x96\x95\xc5\x32\x29\xa7\
+\x31\x13\xcf\x9f\x00\xfe\x5d\x6e\xe6\xe7\x2c\x5b\x40\x99\x94\x29\
+\x7b\xd6\x7b\x93\x08\x76\xc3\xef\x7b\x95\xcd\xae\x1d\xcc\x58\x31\
+\x15\x49\x5e\x26\x3c\xdb\x58\xc2\x80\x98\x84\xc2\xc5\x97\x24\x9b\
+\xf1\x65\xd1\xef\xc1\xc6\xfe\x59\x6f\xc4\xff\x4b\xd2\x34\x32\x5c\
+\x62\x6e\x2c\x86\x70\x3d\x84\xd5\x33\xfc\xf3\x19\x3c\x62\x0d\x41\
+\xdc\x3d\x55\xef\xe9\xc3\x5b\x36\xfd\xce\x0d\xcb\xa4\x26\xfe\x53\
+\x58\xa1\x18\x6a\xb5\x67\xe8\xd3\x72\xce\xa6\x45\x87\xc0\xb2\xbf\
+\xaf\xc7\x8a\x9d\xd5\xc8\xce\x69\xcb\xce\x26\x2e\xb1\xbb\x63\x47\
+\xc9\x80\x50\x62\x6f\xf9\x99\x03\x33\xb0\x9c\x0d\x3f\x05\xb7\xe6\
+\x77\x69\xec\x64\xfe\xf9\x93\x86\x72\x78\x19\x0b\x3e\x67\x1a\xd5\
+\x50\x19\x02\x0d\x6a\xa4\x1f\x97\xb5\x92\xd2\x87\x17\x79\x9e\xb2\
+\x2f\x6c\xf2\x21\x29\x0d\xd7\x76\x09\xb5\xe0\xe2\xc3\xbb\xcf\xa3\
+\x8f\x7f\x41\x9a\x7c\x67\x95\xae\x3e\x54\x28\x06\xa2\x78\x24\xf0\
+\x31\x27\x30\x8e\x6e\x22\x91\xac\x7c\x74\x32\xb0\x26\xea\x77\x44\
+\x94\x36\x12\x45\x14\x32\xf0\x6c\xa9\xa7\xce\x54\xab\x56\xd6\x4c\
+\xad\x2e\x98\x3a\xc4\x36\x1b\x99\xca\x02\xf7\x9d\x9d\x90\x4a\x9f\
+\x36\x44\x69\x37\x44\xb1\x6e\x9b\x88\x22\x0a\xf1\x06\x1e\x71\x77\
+\x98\x5a\xce\x29\xe5\xfd\x3e\x2b\x99\xc8\x58\x09\xaf\xef\xf3\x94\
+\x0b\x26\x34\x2a\xfd\xc0\x07\xa8\x49\xcc\xe3\x72\xa7\x7c\x9e\x47\
+\x65\x32\x49\xd9\x10\x46\xe3\xf7\xaf\x95\xf9\xfe\x66\xee\xeb\xc4\
+\xf9\x10\x79\x70\x04\xd8\x79\x10\x78\xd0\x19\x6e\xd0\x0a\x37\xe8\
+\x0c\xd7\x6f\x85\xeb\x1f\xe0\x9a\x27\xe2\x7a\xad\x70\xbd\x3d\x5c\
+\xb7\xa6\xb7\x4d\x75\x7e\xca\x71\x52\xa3\x22\x95\xdd\xea\x9c\xf8\
+\x98\x64\x8b\xfb\xdd\xcd\xa7\x96\xb1\x81\x06\xb8\xfc\x95\x62\xdf\
+\x50\x36\x70\x1f\x78\xa1\xe7\x60\x63\xe9\xc3\x95\x60\x45\x89\x6d\
+\x43\x76\x91\x80\xc2\xbf\x4c\x14\xf8\x62\x75\x26\x68\x1d\x00\x75\
+\x02\xa3\x68\x7a\x1c\x1e\x17\x93\xac\xe4\x45\x3c\x04\x19\xe4\x14\
+\x70\x02\x3e\x8d\xe1\xab\xdc\x14\x1e\xf1\x3b\x67\xf3\xe0\x69\x54\
+\x67\xb4\xdf\x53\xbb\xc3\xa7\xf2\xfa\x71\x42\x3a\x64\x5f\xaf\x91\
+\x08\x08\xb5\x6b\x24\xf0\xad\xad\x49\x9c\x9b\x92\xee\x19\xb5\x4f\
+\x4b\xa7\x1c\xcc\x13\x77\xca\x10\xdf\x56\x23\x61\x11\xea\x6e\x39\
+\x98\xed\xca\xc3\x3c\x7d\xb3\x1c\x86\xe3\x7c\x26\xed\x32\x62\xb6\
+\xa1\xd0\xe6\xc8\xab\x3a\xba\xc6\x99\x57\x19\xe2\xd1\x6a\x12\xf7\
+\x81\x38\xee\x74\xe0\x5a\x20\xaf\x1f\xa8\xf2\xd0\x0f\x2d\x75\x99\
+\x5e\x14\xda\x17\xd1\xb5\xd0\x0a\xbf\xf5\x25\xb4\xc6\xde\x39\x97\
+\xbd\x17\xfa\x47\xd8\xdb\xc4\x32\x89\xe5\x3e\x2a\xc0\xd9\x0a\xa8\
+\x5c\xfc\x16\x1a\x9c\x6e\x34\xd0\x23\x1a\x28\xb1\x28\x09\x1a\x25\
+\x38\xbb\x12\x94\xc7\xde\x71\x50\xb0\x3c\x12\x51\xc9\x85\x86\xae\
+\x11\x9f\x24\x29\x83\x8d\x3c\x0c\x09\x5c\x24\x57\xd1\xac\x41\x9f\
+\x5c\x56\xd2\x5e\x5e\x5d\x4b\x51\x4e\x68\x87\x56\x45\x76\xbb\x69\
+\x8f\xe4\x08\xbf\x11\x06\x8f\xa6\xc8\x82\x8a\x96\x11\xbc\xf3\xd7\
+\x52\x3d\xe5\x1d\x10\x57\x27\x5b\x47\x54\xc5\x3c\x63\x8d\xba\xa4\
+\xc1\x46\x59\x35\xfc\xad\x04\xf2\x19\x94\x7c\x31\x8d\x1b\x45\xf2\
+\xd9\x6f\x28\x11\xfd\xce\xcb\xa1\x1d\x52\x3d\x81\x16\x76\xdb\x47\
+\xb7\xe0\x5a\x9c\xff\x0a\x3f\x89\xd7\x9b\x51\x7a\x7a\x67\x48\x3b\
+\x3d\x7b\xbf\x5a\x5c\x9b\x26\x77\x17\x46\xa1\x08\x93\x90\x87\x8b\
+\xb0\xd0\xe8\x75\x6f\x39\xbf\x4d\xd9\x84\x97\x80\x4d\x56\xef\x43\
+\x73\xe3\x82\x9d\x19\x8f\xd6\x3f\xe3\xb2\xcc\x9f\x1a\xc6\x72\xb9\
+\x24\xb7\x6a\x89\xa0\xb5\x81\xeb\x24\x2e\xe7\xa9\xd6\x57\xd1\xa8\
+\xc8\x24\x05\xba\x47\x61\xae\xa6\x0d\x75\xad\x5b\xc3\x14\x2c\x12\
+\xd3\x98\xe0\x92\x82\xa9\x4c\x24\x12\xe6\x31\xca\xf3\x29\x9f\xb1\
+\x2c\x9a\x63\x24\xd4\xb0\xf6\x78\x57\x85\x5c\x8e\xf2\x34\x2a\x6f\
+\xb8\x98\xcb\xe7\x3b\x86\x77\x0f\xb1\x7d\x2a\x16\x13\x1c\x68\x70\
+\xfe\x16\xc5\x9c\xc3\x38\x5d\x88\x5c\x2f\x6e\xca\xe1\x8f\xca\x63\
+\x08\x2b\x39\x31\x4b\x73\xf2\x43\xae\x28\x39\x72\x88\xd9\x36\x7e\
+\x54\x32\x8d\x42\x1a\xff\x0c\x65\x87\x35\x55\x9b\xd9\xfe\x98\xfb\
+\x3f\x25\x8f\xae\x3e\
+"
+
+qt_resource_name = b"\
+\x00\x15\
+\x03\x80\x5a\x3c\
+\x00\x55\
+\x00\x73\x00\x65\x00\x72\x00\x41\x00\x67\x00\x65\x00\x6e\x00\x74\x00\x44\x00\x65\x00\x66\x00\x61\x00\x75\x00\x6c\x00\x74\x00\x73\
+\x00\x2e\x00\x78\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\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/UserAgent/UserAgentManager.py	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,195 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a user agent manager.
+"""
+
+from __future__ import unicode_literals
+
+import os
+
+from PyQt5.QtCore import pyqtSignal, QObject, QXmlStreamReader
+
+from E5Gui import E5MessageBox
+
+from Utilities.AutoSaver import AutoSaver
+import Utilities
+
+
+class UserAgentManager(QObject):
+    """
+    Class implementing a user agent manager.
+    
+    @signal changed() emitted to indicate a change
+    @signal userAgentSettingsSaved() emitted after the user agent settings
+        were saved
+    """
+    changed = pyqtSignal()
+    userAgentSettingsSaved = pyqtSignal()
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object (QObject)
+        """
+        super(UserAgentManager, self).__init__(parent)
+        
+        self.__agents = {}
+        # dictionary with agent strings indexed by host name
+        self.__loaded = False
+        self.__saveTimer = AutoSaver(self, self.save)
+        
+        self.changed.connect(self.__saveTimer.changeOccurred)
+    
+    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", "userAgentSettings.xml")
+    
+    def save(self):
+        """
+        Public slot to save the user agent entries to disk.
+        """
+        if not self.__loaded:
+            return
+        
+        from .UserAgentWriter import UserAgentWriter
+        agentFile = self.getFileName()
+        writer = UserAgentWriter()
+        if not writer.write(agentFile, self.__agents):
+            E5MessageBox.critical(
+                None,
+                self.tr("Saving user agent data"),
+                self.tr(
+                    """<p>User agent data could not be saved to"""
+                    """ <b>{0}</b></p>""").format(agentFile))
+        else:
+            self.userAgentSettingsSaved.emit()
+    
+    def __load(self):
+        """
+        Private method to load the saved user agent settings.
+        """
+        agentFile = self.getFileName()
+        from .UserAgentReader import UserAgentReader
+        reader = UserAgentReader()
+        self.__agents = reader.read(agentFile)
+        if reader.error() != QXmlStreamReader.NoError:
+            E5MessageBox.warning(
+                None,
+                self.tr("Loading user agent data"),
+                self.tr("""Error when loading user agent 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 user agent settings.
+        """
+        if not self.__loaded:
+            return
+        
+        self.__agents = {}
+        self.__load()
+    
+    def close(self):
+        """
+        Public method to close the user agents manager.
+        """
+        self.__saveTimer.saveIfNeccessary()
+    
+    def removeUserAgent(self, host):
+        """
+        Public method to remove a user agent entry.
+        
+        @param host host name (string)
+        """
+        if host in self.__agents:
+            del self.__agents[host]
+            self.changed.emit()
+    
+    def allHostNames(self):
+        """
+        Public method to get a list of all host names we a user agent setting
+        for.
+        
+        @return sorted list of all host names (list of strings)
+        """
+        if not self.__loaded:
+            self.__load()
+        
+        return sorted(self.__agents.keys())
+    
+    def hostsCount(self):
+        """
+        Public method to get the number of available user agent settings.
+        
+        @return number of user agent settings (integer)
+        """
+        if not self.__loaded:
+            self.__load()
+        
+        return len(self.__agents)
+    
+    def userAgent(self, host):
+        """
+        Public method to get the user agent setting for a host.
+        
+        @param host host name (string)
+        @return user agent string (string)
+        """
+        if not self.__loaded:
+            self.__load()
+        
+        for agentHost in self.__agents:
+            if host.endswith(agentHost):
+                return self.__agents[agentHost]
+        
+        return ""
+    
+    def setUserAgent(self, host, agent):
+        """
+        Public method to set the user agent string for a host.
+        
+        @param host host name (string)
+        @param agent user agent string (string)
+        """
+        if host != "" and agent != "":
+            self.__agents[host] = agent
+            self.changed.emit()
+    
+    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)
+        """
+        if url.isValid():
+            host = url.host()
+            return self.userAgent(host)
+        
+        return ""
+    
+    def setUserAgentForUrl(self, url, agent):
+        """
+        Public method to set the user agent string for an URL.
+        
+        @param url URL to register user agent setting for (QUrl)
+        @param agent new current user agent string (string)
+        """
+        if url.isValid():
+            host = url.host()
+            self.setUserAgent(host, agent)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/UserAgent/UserAgentMenu.py	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,190 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2010 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a menu to select the user agent string.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import QXmlStreamReader, QFile, QIODevice
+from PyQt5.QtWidgets import QMenu, QAction, QActionGroup, QInputDialog, \
+    QLineEdit
+
+from E5Gui import E5MessageBox
+
+
+class UserAgentMenu(QMenu):
+    """
+    Class implementing a menu to select the user agent string.
+    """
+    def __init__(self, title, url=None, parent=None):
+        """
+        Constructor
+        
+        @param title title of the menu (string)
+        @param url URL to set user agent for (QUrl)
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(UserAgentMenu, self).__init__(title, parent)
+        
+        self.__manager = None
+        self.__url = url
+        if self.__url:
+            if self.__url.isValid():
+                from WebBrowser.WebBrowserWindow import WebBrowserWindow
+                self.__manager = WebBrowserWindow.userAgentsManager()
+            else:
+                self.__url = None
+        
+        self.aboutToShow.connect(self.__populateMenu)
+    
+    def __populateMenu(self):
+        """
+        Private slot to populate the menu.
+        """
+        self.aboutToShow.disconnect(self.__populateMenu)
+        
+        self.__actionGroup = QActionGroup(self)
+        
+        # add default action
+        self.__defaultUserAgent = QAction(self)
+        self.__defaultUserAgent.setText(self.tr("Default"))
+        self.__defaultUserAgent.setCheckable(True)
+        self.__defaultUserAgent.triggered.connect(
+            self.__switchToDefaultUserAgent)
+        if self.__url:
+            self.__defaultUserAgent.setChecked(
+                self.__manager.userAgentForUrl(self.__url) == "")
+        else:
+            from WebBrowser.WebBrowserPage import WebBrowserPage
+            self.__defaultUserAgent.setChecked(
+                WebBrowserPage.userAgent() == "")
+        self.addAction(self.__defaultUserAgent)
+        self.__actionGroup.addAction(self.__defaultUserAgent)
+        isChecked = self.__defaultUserAgent.isChecked()
+        
+        # add default extra user agents
+        isChecked = self.__addDefaultActions() or isChecked
+        
+        # add other action
+        self.addSeparator()
+        self.__otherUserAgent = QAction(self)
+        self.__otherUserAgent.setText(self.tr("Other..."))
+        self.__otherUserAgent.setCheckable(True)
+        self.__otherUserAgent.triggered.connect(
+            self.__switchToOtherUserAgent)
+        self.addAction(self.__otherUserAgent)
+        self.__actionGroup.addAction(self.__otherUserAgent)
+        self.__otherUserAgent.setChecked(not isChecked)
+    
+    def __switchToDefaultUserAgent(self):
+        """
+        Private slot to set the default user agent.
+        """
+        if self.__url:
+            self.__manager.removeUserAgent(self.__url.host())
+        else:
+            from WebBrowser.WebBrowserPage import WebBrowserPage
+            WebBrowserPage.setUserAgent("")
+    
+    def __switchToOtherUserAgent(self):
+        """
+        Private slot to set a custom user agent string.
+        """
+        from WebBrowser.WebBrowserPage import WebBrowserPage
+        userAgent, ok = QInputDialog.getText(
+            self,
+            self.tr("Custom user agent"),
+            self.tr("User agent:"),
+            QLineEdit.Normal,
+            WebBrowserPage.userAgent(resolveEmpty=True))
+        if ok:
+            if self.__url:
+                self.__manager.setUserAgentForUrl(self.__url, userAgent)
+            else:
+                WebBrowserPage.setUserAgent(userAgent)
+    
+    def __changeUserAgent(self):
+        """
+        Private slot to change the user agent.
+        """
+        act = self.sender()
+        if self.__url:
+            self.__manager.setUserAgentForUrl(self.__url, act.data())
+        else:
+            from WebBrowser.WebBrowserPage import WebBrowserPage
+            WebBrowserPage.setUserAgent(act.data())
+    
+    def __addDefaultActions(self):
+        """
+        Private slot to add the default user agent entries.
+        
+        @return flag indicating that a user agent entry is checked (boolean)
+        """
+        from . import UserAgentDefaults_rc              # __IGNORE_WARNING__
+        defaultUserAgents = QFile(":/UserAgentDefaults.xml")
+        defaultUserAgents.open(QIODevice.ReadOnly)
+        
+        menuStack = []
+        isChecked = False
+        
+        if self.__url:
+            currentUserAgentString = self.__manager.userAgentForUrl(self.__url)
+        else:
+            from WebBrowser.WebBrowserPage import WebBrowserPage
+            currentUserAgentString = WebBrowserPage.userAgent()
+        xml = QXmlStreamReader(defaultUserAgents)
+        while not xml.atEnd():
+            xml.readNext()
+            if xml.isStartElement() and xml.name() == "separator":
+                if menuStack:
+                    menuStack[-1].addSeparator()
+                else:
+                    self.addSeparator()
+                continue
+            
+            if xml.isStartElement() and xml.name() == "useragent":
+                attributes = xml.attributes()
+                title = attributes.value("description")
+                userAgent = attributes.value("useragent")
+                
+                act = QAction(self)
+                act.setText(title)
+                act.setData(userAgent)
+                act.setToolTip(userAgent)
+                act.setCheckable(True)
+                act.setChecked(userAgent == currentUserAgentString)
+                act.triggered.connect(self.__changeUserAgent)
+                if menuStack:
+                    menuStack[-1].addAction(act)
+                else:
+                    self.addAction(act)
+                self.__actionGroup.addAction(act)
+                isChecked = isChecked or act.isChecked()
+            
+            if xml.isStartElement() and xml.name() == "useragentmenu":
+                attributes = xml.attributes()
+                title = attributes.value("title")
+                if title == "v_a_r_i_o_u_s":
+                    title = self.tr("Various")
+                
+                menu = QMenu(self)
+                menu.setTitle(title)
+                self.addMenu(menu)
+                menuStack.append(menu)
+            
+            if xml.isEndElement() and xml.name() == "useragentmenu":
+                menuStack.pop()
+        
+        if xml.hasError():
+            E5MessageBox.critical(
+                self,
+                self.tr("Parsing default user agents"),
+                self.tr(
+                    """<p>Error parsing default user agents.</p><p>{0}</p>""")
+                .format(xml.errorString()))
+        
+        return isChecked
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/UserAgent/UserAgentModel.py	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,129 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a model for user agent management.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import Qt, QModelIndex, QAbstractTableModel
+
+
+class UserAgentModel(QAbstractTableModel):
+    """
+    Class implementing a model for user agent management.
+    """
+    def __init__(self, manager, parent=None):
+        """
+        Constructor
+        
+        @param manager reference to the user agent manager (UserAgentManager)
+        @param parent reference to the parent object (QObject)
+        """
+        super(UserAgentModel, self).__init__(parent)
+        
+        self.__manager = manager
+        self.__manager.changed.connect(self.__userAgentsChanged)
+        
+        self.__headers = [
+            self.tr("Host"),
+            self.tr("User Agent String"),
+        ]
+    
+    def __userAgentsChanged(self):
+        """
+        Private slot handling a change of the registered user agent strings.
+        """
+        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)
+        
+        hostsList = self.__manager.allHostNames()
+        for index in range(row, lastRow + 1):
+            self.__manager.removeUserAgent(hostsList[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.hostsCount()
+    
+    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.hostsCount() or index.row() < 0:
+            return None
+        
+        host = self.__manager.allHostNames()[index.row()]
+        userAgent = self.__manager.userAgent(host)
+        
+        if userAgent is None:
+            return None
+        
+        if role == Qt.DisplayRole:
+            if index.column() == 0:
+                return host
+            elif index.column() == 1:
+                return userAgent
+        
+        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/UserAgent/UserAgentReader.py	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,97 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+
+"""
+Module implementing a class to read user agent data files.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import QXmlStreamReader, QIODevice, QFile, QCoreApplication
+
+
+class UserAgentReader(QXmlStreamReader):
+    """
+    Class implementing a reader object for user agent data files.
+    """
+    def __init__(self):
+        """
+        Constructor
+        """
+        super(UserAgentReader, 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 dictionary with user agent data (host as key, agent string as
+            value)
+        """
+        self.__agents = {}
+        
+        if isinstance(fileNameOrDevice, QIODevice):
+            self.setDevice(fileNameOrDevice)
+        else:
+            f = QFile(fileNameOrDevice)
+            if not f.exists():
+                return self.__agents
+            f.open(QFile.ReadOnly)
+            self.setDevice(f)
+        
+        while not self.atEnd():
+            self.readNext()
+            if self.isStartElement():
+                version = self.attributes().value("version")
+                if self.name() == "UserAgents" and \
+                   (not version or version == "1.0"):
+                    self.__readUserAgents()
+                else:
+                    self.raiseError(QCoreApplication.translate(
+                        "UserAgentReader",
+                        "The file is not a UserAgents version 1.0 file."))
+        
+        return self.__agents
+    
+    def __readUserAgents(self):
+        """
+        Private method to read the user agents data.
+        """
+        if not self.isStartElement() and self.name() != "UserAgents":
+            return
+        
+        while not self.atEnd():
+            self.readNext()
+            if self.isEndElement():
+                if self.name() == "UserAgent":
+                    continue
+                else:
+                    break
+            
+            if self.isStartElement():
+                if self.name() == "UserAgent":
+                    attributes = self.attributes()
+                    host = attributes.value("host")
+                    agent = attributes.value("agent")
+                    self.__agents[host] = agent
+                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/UserAgent/UserAgentWriter.py	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,66 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a class to write user agent data files.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import QXmlStreamWriter, QIODevice, QFile
+
+
+class UserAgentWriter(QXmlStreamWriter):
+    """
+    Class implementing a writer object to generate user agent data files.
+    """
+    def __init__(self):
+        """
+        Constructor
+        """
+        super(UserAgentWriter, self).__init__()
+        
+        self.setAutoFormatting(True)
+    
+    def write(self, fileNameOrDevice, agents):
+        """
+        Public method to write a user agent data file.
+        
+        @param fileNameOrDevice name of the file to write (string)
+            or device to write to (QIODevice)
+        @param agents dictionary with user agent data (host as key, agent
+            string as value)
+        @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(agents)
+    
+    def __write(self, agents):
+        """
+        Private method to write a user agent file.
+        
+        @param agents dictionary with user agent data (host as key, agent
+            string as value)
+        @return flag indicating success (boolean)
+        """
+        self.writeStartDocument()
+        self.writeDTD("<!DOCTYPE useragents>")
+        self.writeStartElement("UserAgents")
+        self.writeAttribute("version", "1.0")
+        
+        for host, agent in agents.items():
+            self.writeEmptyElement("UserAgent")
+            self.writeAttribute("host", host)
+            self.writeAttribute("agent", agent)
+        
+        self.writeEndDocument()
+        return True
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/UserAgent/UserAgentsDialog.py	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,56 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog to show all saved user agent settings.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import QSortFilterProxyModel
+from PyQt5.QtGui import QFont, QFontMetrics
+from PyQt5.QtWidgets import QDialog
+
+from WebBrowser.WebBrowserWindow import WebBrowserWindow
+
+from .UserAgentModel import UserAgentModel
+
+from .Ui_UserAgentsDialog import Ui_UserAgentsDialog
+
+
+class UserAgentsDialog(QDialog, Ui_UserAgentsDialog):
+    """
+    Class implementing a dialog to show all saved user agent settings.
+    """
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(UserAgentsDialog, self).__init__(parent)
+        self.setupUi(self)
+        
+        self.removeButton.clicked.connect(
+            self.userAgentsTable.removeSelected)
+        self.removeAllButton.clicked.connect(
+            self.userAgentsTable.removeAll)
+        
+        self.userAgentsTable.verticalHeader().hide()
+        self.__userAgentModel = UserAgentModel(
+            WebBrowserWindow.userAgentsManager(), self)
+        self.__proxyModel = QSortFilterProxyModel(self)
+        self.__proxyModel.setSourceModel(self.__userAgentModel)
+        self.searchEdit.textChanged.connect(
+            self.__proxyModel.setFilterFixedString)
+        self.userAgentsTable.setModel(self.__proxyModel)
+        
+        fm = QFontMetrics(QFont())
+        height = fm.height() + fm.height() // 3
+        self.userAgentsTable.verticalHeader().setDefaultSectionSize(height)
+        self.userAgentsTable.verticalHeader().setMinimumSectionSize(-1)
+        
+        self.userAgentsTable.resizeColumnsToContents()
+        self.userAgentsTable.horizontalHeader().setStretchLastSection(True)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/UserAgent/UserAgentsDialog.ui	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,179 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>UserAgentsDialog</class>
+ <widget class="QDialog" name="UserAgentsDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>700</width>
+    <height>400</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>User Agent Settings</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</string>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="E5TableView" name="userAgentsTable">
+     <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>userAgentsTable</tabstop>
+  <tabstop>removeButton</tabstop>
+  <tabstop>removeAllButton</tabstop>
+  <tabstop>buttonBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>UserAgentsDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>237</x>
+     <y>390</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>UserAgentsDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>325</x>
+     <y>390</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/UserAgent/__init__.py	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2010 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Package implementing the user agents manager.
+"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/VirusTotal/VirusTotalApi.py	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,408 @@
+# -*- 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 pyqtSignal, QObject, QUrl, QUrlQuery, QByteArray
+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)
+        query = QUrlQuery()
+        query.setQueryItems(queryItems)
+        url.setQuery(query)
+        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)
+        query = QUrlQuery()
+        query.setQueryItems(queryItems)
+        url.setQuery(query)
+        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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,240 @@
+# -*- 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 QUrl, QTimer, QEventLoop
+from PyQt5.QtGui import QDesktopServices
+from PyQt5.QtWebEngineWidgets import QWebEnginePage
+from PyQt5.QtWebChannel import QWebChannel
+
+from WebBrowser.WebBrowserWindow import WebBrowserWindow
+
+from .JavaScript.ExternalJsObject import ExternalJsObject
+
+from .Tools.WebHitTestResult import WebHitTestResult
+
+import Preferences
+
+
+class WebBrowserPage(QWebEnginePage):
+    """
+    Class implementing an enhanced web page.
+    """
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent parent widget of this window (QWidget)
+        """
+        super(WebBrowserPage, self).__init__(
+            WebBrowserWindow.webProfile(), parent)
+        
+        self.__setupWebChannel()
+        
+        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
+        """
+        scheme = url.scheme()
+        if scheme == "mailto":
+            QDesktopServices.openUrl(url)
+            return False
+        
+        # AdBlock
+        if url.scheme() == "abp":
+            if WebBrowserWindow.adBlockManager().addSubscriptionFromUrl(url):
+                return False
+        
+        return QWebEnginePage.acceptNavigationRequest(self, url, type_,
+                                                      isMainFrame)
+    
+    @classmethod
+    def userAgent(cls, resolveEmpty=False):
+        """
+        Class 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 = cls.userAgentForUrl(QUrl())
+        return agent
+    
+    @classmethod
+    def setUserAgent(cls, agent):
+        """
+        Class method to set the global user agent string.
+        
+        @param agent new current user agent string (string)
+        """
+        Preferences.setWebBrowser("UserAgent", agent)
+    
+    @classmethod
+    def userAgentForUrl(cls, url):
+        """
+        Class 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 = WebBrowserWindow.webProfile().httpUserAgent()
+        return agent
+    
+    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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,164 @@
+# -*- 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)
+        
+        label = QLabel()
+        label.setPixmap(indexedBrowser.getPreview().scaled(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	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,957 @@
+# -*- 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 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
+        
+        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()
+    
+    def newBrowser(self, link=None, position=-1):
+        """
+        Public method to create a new web browser tab.
+        
+        @param link link to be shown (string or QUrl)
+        @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:
+            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())
+    
+    def newBrowserAfter(self, browser, link=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)
+        """
+        if browser:
+            position = self.indexOf(browser) + 1
+        else:
+            position = -1
+        self.newBrowser(link, 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
+        
+        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())
+        if e5App().keyboardModifiers() == Qt.AltModifier:
+            self.newBrowser(url)
+        else:
+            self.currentBrowser().setSource(url)
+            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")
+        
+        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	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,1602 @@
+# -*- 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 pyqtSignal, QUrl, QFileInfo, Qt, QTimer, QEvent, \
+    QPoint
+from PyQt5.QtGui import QDesktopServices, QClipboard, QIcon, \
+    QContextMenuEvent, QPixmap
+from PyQt5.QtWidgets import qApp, QStyle, QMenu, QApplication
+from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEnginePage
+
+from E5Gui import E5MessageBox
+
+from .WebBrowserPage import WebBrowserPage
+
+from .Tools.WebIconLoader import WebIconLoader
+from .Tools import Scripts
+
+from . import WebInspector
+from .Tools.WebBrowserTools import readAllFileContents, pixmapToDataUrl
+
+import Preferences
+import UI.PixmapCache
+
+
+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.__isLoading = False
+        self.__progress = 0
+        self.__siteIconLoader = None
+        self.__siteIcon = QIcon()
+        self.__menu = QMenu(self)
+        self.__clickedPos = QPoint()
+        self.__firstLoad = False
+        self.__preview = QPixmap()
+        
+        self.__currentZoom = 100
+        self.__zoomLevels = WebBrowserView.ZoomLevels[:]
+        
+        self.iconUrlChanged.connect(self.__iconUrlChanged)
+        self.urlChanged.connect(self.__urlChanged)
+        self.page().linkHovered.connect(self.__linkHovered)
+        
+        self.loadStarted.connect(self.__loadStarted)
+        self.loadProgress.connect(self.__loadProgress)
+        self.loadFinished.connect(self.__loadFinished)
+        self.renderProcessTerminated.connect(self.__renderProcessTerminated)
+        
+        self.__mw.openSearchManager().currentEngineChanged.connect(
+            self.__currentEngineChanged)
+        
+        self.setAcceptDrops(True)
+        
+        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
+        """
+        super(WebBrowserView, self).load(url)
+        
+        if not self.__firstLoad:
+            self.__firstLoad = True
+            WebInspector.pushView(self)
+    
+    def setSource(self, name, newTab=False):
+        """
+        Public method used to set the source to be displayed.
+        
+        @param name filename to be shown (QUrl)
+        @param newTab flag indicating to open the URL in a new tab (bool)
+        """
+        if name is None or not name.isValid():
+            return
+        
+        if newTab:
+            # open in a new tab
+            self.__mw.newTab(name)
+            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
+        
+        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: Qt 5.7: 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()
+        
+        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
+        
+        self.setSource(url, newTab=True)
+    
+    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
+        
+        super(WebBrowserView, self).keyPressEvent(evt)
+    
+    def _keyReleaseEvent(self, evt):
+        """
+        Protected method called by a key release.
+        
+        @param evt reference to the key event (QKeyEvent)
+        """
+        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)
+        """
+        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.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 __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 __renderProcessTerminated(self, status, exitCode):
+        """
+        Private slot handling a crash of the web page render process.
+        
+        @param status termination status
+        @type QWebEnginePage.RenderProcessTerminationStatus
+        @param exitCode exit code of the process
+        @type int
+        """
+        if status == QWebEnginePage.NormalTerminationStatus:
+            return
+        
+        QTimer.singleShot(0, self.__showTabCrashPage)
+    
+    def __showTabCrashPage(self):
+        """
+        Private slot to show the tab crash page.
+        """
+        html = readAllFileContents(":/html/tabCrashPage.html")
+        html = html.replace("@IMAGE@", pixmapToDataUrl(
+            qApp.style().standardIcon(QStyle.SP_MessageBoxWarning).pixmap(
+                48, 48)).toString())
+        html = html.replace("@FAVICON@", pixmapToDataUrl(
+            qApp.style() .standardIcon(QStyle.SP_MessageBoxWarning).pixmap(
+                16, 16)).toString())
+        html = html.replace("@TITLE@", self.tr("Failed loading page"))
+        html = html.replace("@H1@", self.tr("Failed loading page"))
+        html = html.replace(
+            "@LI-1@",
+            self.tr("Something went wrong while loading this page."))
+        html = html.replace(
+            "@LI-2@",
+            self.tr(
+                "Try reloading the page or closing some tabs to make more"
+                " memory available."))
+        html = html.replace(
+            "@BUTTON@", self.tr("Reload Page"))
+        self.page().setHtml(html, self.url())
+    
+    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
+        
+        QTimer.singleShot(200, self.__renderPreview)
+        
+        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 __renderPreview(self):
+        """
+        Private slot to render a preview pixmap after the page was loaded.
+        """
+        from .WebBrowserSnap import renderTabPreview
+        w = 600     # some default width, the preview gets scaled when shown
+        h = int(w * self.height() / self.width())
+        self.__preview = renderTabPreview(self, w, h)
+    
+    def getPreview(self):
+        """
+        Public method to get the preview pixmap.
+        
+        @return preview pixmap
+        @rtype QPixmap
+        """
+        return self.__preview
+    
+    # TODO: Qt 5.7: Save
+##    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)
+    
+    ###########################################################################
+    ## 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.
+        """
+        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();")
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/WebBrowserWebSearchWidget.py	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,4008 @@
+# -*- 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, QTextCodec, QProcess, QEvent
+from PyQt5.QtGui import QDesktopServices, QKeySequence, QFont, QFontMetrics
+from PyQt5.QtWidgets import QWidget, QVBoxLayout, QSizePolicy, QDockWidget, \
+    QComboBox, QLabel, QSplitter, QMenu, QToolButton, QLineEdit, \
+    QApplication, QWhatsThis, QDialog, QHBoxLayout, QProgressBar, QAction, \
+    QInputDialog
+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 = []
+
+    _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:
+            WebBrowserWindow.setUseQtHelp(
+                self.__fromEric or qthelp or bool(searchWord))
+            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"))
+                
+                # 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:
+                # 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: Qt 5.7: 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)
+        
+        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)
+        
+        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)
+        
+        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)
+        
+        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()
+        # TODO: Qt 5.7: Save
+##        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.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)
+        menu.addAction(self.certificateErrorsAct)
+        menu.addSeparator()
+        menu.addAction(self.zoomValuesAct)
+        menu.addAction(self.manageIconsAct)
+        menu.addSeparator()
+        menu.addAction(self.adblockAct)
+        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)
+        
+        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()
+        # TODO: Qt 5.7: Save
+##        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.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, 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 addNextTo reference to the browser to open the tab after
+            (HelpBrowser)
+        """
+        if addNextTo:
+            self.__tabWidget.newBrowserAfter(addNextTo, link)
+        else:
+            self.__tabWidget.newBrowser(link)
+    
+    @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)
+        
+        # TODO: Qt 5.7: Save
+##    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()
+        
+        self.cookieJar().close()
+        
+        self.bookmarksToolBar.setModel(None)
+        self.bookmarksManager().close()
+        
+        self.historyManager().close()
+        
+        self.passwordManager().close()
+        
+        self.adBlockManager().close()
+        
+        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()
+    
+    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()
+    
+    @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 __showIndexWindow(self):
+        """
+        Private method to show the index window.
+        """
+        if WebBrowserWindow.useQtHelp:
+            self.__activateDock(self.__indexWindow)
+        
+    def __showSearchWindow(self):
+        """
+        Private method to show the search window.
+        """
+        if WebBrowserWindow.useQtHelp:
+            self.__activateDock(self.__searchWindow)
+        
+    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_()
+    
+    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()
+        
+    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_()
+    
+    def __showDownloadsWindow(self):
+        """
+        Private slot to show the downloads dialog.
+        """
+        self.downloadManager().show()
+        
+    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()
+
+    @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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,9 @@
+<!DOCTYPE RCC>
+<RCC version="1.0">
+<qresource>
+  <file>html/adblockPage.html</file>
+  <file>html/startPage.html</file>
+  <file>html/speeddialPage.html</file>
+  <file>html/tabCrashPage.html</file>
+</qresource>
+</RCC>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/data/html/adblockPage.html	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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/tabCrashPage.html	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,56 @@
+<!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="data:image/png;base64,@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;
+}
+img {
+  float: left;
+  opacity: .8;
+}
+#box {
+  background: white;
+  border: 1px solid #85784A;
+  width: 600px;
+  padding: 60px;
+  margin: auto;
+  border-radius: 0.8em;
+}
+h1 {
+  font-size: 130%;
+  font-weight: bold;
+  border-bottom: 1px solid #85784A;
+  margin-left: 64px;
+}
+h2 {
+  font-size: 100%;
+  font-weight: normal;
+  border-bottom: 1px solid #85784A;
+  margin-left: 64px;
+}
+ul {
+  font-size: 100%;
+  padding-left: 64px;
+  margin: 5px 0;
+}
+</style>
+</head>
+<body>
+  <div id="box">
+    <img src="@IMAGE@" width="48" height="48"/>
+    <h1>@H1@</h1>
+    <ul>
+      <li>@LI-1@</li>
+      <li>@LI-2@</li>
+      <input type="submit" id="reloadButton" value="@BUTTON@" onclick="window.location.reload()">
+    </ul>
+  </div>
+</body>
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/data/html_rc.py	Sun Apr 03 16:33:37 2016 +0200
@@ -0,0 +1,689 @@
+# -*- 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\x04\x7b\
+\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\x64\x61\x74\x61\x3a\x69\x6d\x61\x67\
+\x65\x2f\x70\x6e\x67\x3b\x62\x61\x73\x65\x36\x34\x2c\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\x69\x6d\x67\x20\x7b\x0a\x20\x20\
+\x66\x6c\x6f\x61\x74\x3a\x20\x6c\x65\x66\x74\x3b\x0a\x20\x20\x6f\
+\x70\x61\x63\x69\x74\x79\x3a\x20\x2e\x38\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\x77\x69\x64\x74\x68\x3a\x20\
+\x36\x30\x30\x70\x78\x3b\x0a\x20\x20\x70\x61\x64\x64\x69\x6e\x67\
+\x3a\x20\x36\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\
+\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\x6c\x65\x66\x74\
+\x3a\x20\x36\x34\x70\x78\x3b\x0a\x7d\x0a\x68\x32\x20\x7b\x0a\x20\
+\x20\x66\x6f\x6e\x74\x2d\x73\x69\x7a\x65\x3a\x20\x31\x30\x30\x25\
+\x3b\x0a\x20\x20\x66\x6f\x6e\x74\x2d\x77\x65\x69\x67\x68\x74\x3a\
+\x20\x6e\x6f\x72\x6d\x61\x6c\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\x6c\x65\x66\x74\x3a\x20\x36\x34\x70\x78\
+\x3b\x0a\x7d\x0a\x75\x6c\x20\x7b\x0a\x20\x20\x66\x6f\x6e\x74\x2d\
+\x73\x69\x7a\x65\x3a\x20\x31\x30\x30\x25\x3b\x0a\x20\x20\x70\x61\
+\x64\x64\x69\x6e\x67\x2d\x6c\x65\x66\x74\x3a\x20\x36\x34\x70\x78\
+\x3b\x0a\x20\x20\x6d\x61\x72\x67\x69\x6e\x3a\x20\x35\x70\x78\x20\
+\x30\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\x34\x38\x22\x20\
+\x68\x65\x69\x67\x68\x74\x3d\x22\x34\x38\x22\x2f\x3e\x0a\x20\x20\
+\x20\x20\x3c\x68\x31\x3e\x40\x48\x31\x40\x3c\x2f\x68\x31\x3e\x0a\
+\x20\x20\x20\x20\x3c\x75\x6c\x3e\x0a\x20\x20\x20\x20\x20\x20\x3c\
+\x6c\x69\x3e\x40\x4c\x49\x2d\x31\x40\x3c\x2f\x6c\x69\x3e\x0a\x20\
+\x20\x20\x20\x20\x20\x3c\x6c\x69\x3e\x40\x4c\x49\x2d\x32\x40\x3c\
+\x2f\x6c\x69\x3e\x0a\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\
+\x69\x64\x3d\x22\x72\x65\x6c\x6f\x61\x64\x42\x75\x74\x74\x6f\x6e\
+\x22\x20\x76\x61\x6c\x75\x65\x3d\x22\x40\x42\x55\x54\x54\x4f\x4e\
+\x40\x22\x20\x6f\x6e\x63\x6c\x69\x63\x6b\x3d\x22\x77\x69\x6e\x64\
+\x6f\x77\x2e\x6c\x6f\x63\x61\x74\x69\x6f\x6e\x2e\x72\x65\x6c\x6f\
+\x61\x64\x28\x29\x22\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x75\x6c\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\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\x11\
+\x0d\xbe\x8d\x7c\
+\x00\x74\
+\x00\x61\x00\x62\x00\x43\x00\x72\x00\x61\x00\x73\x00\x68\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\x04\x00\x00\x00\x02\
+\x00\x00\x00\x0e\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\
+\x00\x00\x00\x86\x00\x00\x00\x00\x00\x01\x00\x00\x1a\x99\
+\x00\x00\x00\x60\x00\x00\x00\x00\x00\x01\x00\x00\x17\x19\
+\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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 12:29:37 2016 +0200
+++ b/eric6.e4p	Sun Apr 03 16:33:37 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,209 @@
     <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/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/UserAgent/UserAgentDefaults_rc.py</Source>
+    <Source>WebBrowser/UserAgent/UserAgentManager.py</Source>
+    <Source>WebBrowser/UserAgent/UserAgentMenu.py</Source>
+    <Source>WebBrowser/UserAgent/UserAgentModel.py</Source>
+    <Source>WebBrowser/UserAgent/UserAgentReader.py</Source>
+    <Source>WebBrowser/UserAgent/UserAgentWriter.py</Source>
+    <Source>WebBrowser/UserAgent/UserAgentsDialog.py</Source>
+    <Source>WebBrowser/UserAgent/__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 +1802,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 +1860,56 @@
     <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/UserAgent/UserAgentsDialog.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 +1941,13 @@
     <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/UserAgent/UserAgentDefaults.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,12 +2052,51 @@
     <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/UserAgent/UserAgentDefaults.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/html/tabCrashPage.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>
@@ -2203,7 +2500,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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 16:33:37 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	Sun Apr 03 12:29:37 2016 +0200
+++ b/eric6_configure.py	Sun Apr 03 16:33:37 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/mouse.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	Sun Apr 03 12:29:37 2016 +0200
+++ b/install.py	Sun Apr 03 16:33:37 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	Sun Apr 03 12:29:37 2016 +0200
+++ b/uninstall.py	Sun Apr 03 16:33:37 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