Merged with default branch. QtWebEngine

Sun, 06 Mar 2016 14:12:58 +0100

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Sun, 06 Mar 2016 14:12:58 +0100
branch
QtWebEngine
changeset 4816
4f5ca06fa93a
parent 4813
b91c80b5a815 (diff)
parent 4815
74a3a3603ac5 (current diff)
child 4817
0a4e2fb0e93c

Merged with default branch.

Preferences/ConfigurationPages/WebBrowserAppearancePage.py file | annotate | diff | comparison | revisions
Preferences/__init__.py file | annotate | diff | comparison | revisions
--- a/.hgignore	Sun Mar 06 14:09:37 2016 +0100
+++ b/.hgignore	Sun Mar 06 14:12:58 2016 +0100
@@ -16,3 +16,4 @@
 glob:__pycache__
 glob:**.DS_Store
 glob:**.coverage
+glob:GPUCache
--- a/E5Gui/E5ErrorMessage.py	Sun Mar 06 14:09:37 2016 +0100
+++ b/E5Gui/E5ErrorMessage.py	Sun Mar 06 14:12:58 2016 +0100
@@ -44,7 +44,8 @@
             "QFont::",
             "QCocoaMenu::removeMenuItem",
             "QCocoaMenu::insertNative",
-            ",type id:"
+            ",type id:",
+            "Remote debugging server started successfully"
         ]
     
     def __filterMessage(self, message):
--- a/Helpviewer/HelpBrowserWV.py	Sun Mar 06 14:09:37 2016 +0100
+++ b/Helpviewer/HelpBrowserWV.py	Sun Mar 06 14:12:58 2016 +0100
@@ -10,7 +10,7 @@
 
 from __future__ import unicode_literals
 try:
-    str = unicode
+    str = unicode       # __IGNORE_EXCEPTION__
 except NameError:
     pass
 
@@ -1617,8 +1617,8 @@
         
         searchUrl = QUrl(self.page().mainFrame().baseUrl().resolved(
             QUrl(formElement.attribute("action"))))
-        if searchUrl.scheme() != "http":
-            return
+##        if searchUrl.scheme() != "http":
+##            return
         
         if qVersion() >= "5.0.0":
             from PyQt5.QtCore import QUrlQuery
--- a/PluginManager/PluginManager.py	Sun Mar 06 14:09:37 2016 +0100
+++ b/PluginManager/PluginManager.py	Sun Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:09:37 2016 +0100
+++ b/Preferences/ConfigurationDialog.py	Sun Mar 06 14:12:58 2016 +0100
@@ -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,74 @@
                 [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],
+                
+                # TODO: QtHelp
+##                "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 +515,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 +907,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 +931,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 +945,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 +963,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 +1032,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/PrinterPage.py	Sun Mar 06 14:09:37 2016 +0100
+++ b/Preferences/ConfigurationPages/PrinterPage.py	Sun Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:09:37 2016 +0100
+++ b/Preferences/ConfigurationPages/PrinterPage.ui	Sun Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:09:37 2016 +0100
+++ b/Preferences/ConfigurationPages/SecurityPage.py	Sun Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:09:37 2016 +0100
+++ b/Preferences/ConfigurationPages/SecurityPage.ui	Sun Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,316 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the  Web Browser configuration page.
+"""
+
+from PyQt5.QtCore import pyqtSlot, QLocale
+
+from .ConfigurationPageBase import ConfigurationPageBase
+from .Ui_WebBrowserPage import Ui_WebBrowserPage
+
+import Preferences
+import UI.PixmapCache
+
+
+class WebBrowserPage(ConfigurationPageBase, Ui_WebBrowserPage):
+    """
+    Class documentation goes here.
+    """
+    def __init__(self, configDialog):
+        """
+        Constructor
+        
+        @param configDialog reference to the configuration dialog
+            (ConfigurationDialog)
+        """
+        super(WebBrowserPage, self).__init__()
+        self.setupUi(self)
+        self.setObjectName("WebBrowserPage")
+        
+        self.__configDlg = configDialog
+        mw = configDialog.parent().parent()
+        if hasattr(mw, "helpWindow") and mw.helpWindow is not None:
+            # IDE
+            self.__browserWindow = mw.helpWindow
+        elif hasattr(mw, "currentBrowser"):
+            # Web Browser
+            self.__browserWindow = mw
+        else:
+            self.__browserWindow = None
+        self.setCurrentPageButton.setEnabled(self.__browserWindow is not None)
+        
+        defaultSchemes = ["file://", "http://", "https://"]
+        self.defaultSchemeCombo.addItems(defaultSchemes)
+        
+        # TODO: Click2Flash
+##        self.clickToFlashCheckBox.setIcon(
+##            UI.PixmapCache.getIcon("flashBlock.png"))
+        
+        # set initial values
+        self.singleHelpWindowCheckBox.setChecked(
+            Preferences.getWebBrowser("SingleWebBrowserWindow"))
+        self.saveGeometryCheckBox.setChecked(
+            Preferences.getWebBrowser("SaveGeometry"))
+        self.webSuggestionsCheckBox.setChecked(
+            Preferences.getWebBrowser("WebSearchSuggestions"))
+        self.showTabPreviews.setChecked(
+            Preferences.getWebBrowser("ShowPreview"))
+        self.errorPageCheckBox.setChecked(
+            Preferences.getWebBrowser("ErrorPageEnabled"))
+        self.scrollingCheckBox.setChecked(
+            Preferences.getWebBrowser("ScrollAnimatorEnabled"))
+        
+        self.javaScriptGroup.setChecked(
+            Preferences.getWebBrowser("JavaScriptEnabled"))
+        self.jsOpenWindowsCheckBox.setChecked(
+            Preferences.getWebBrowser("JavaScriptCanOpenWindows"))
+        # TODO: Qt 5.6
+##        self.jsCloseWindowsCheckBox.setChecked(
+##            Preferences.getWebBrowser("JavaScriptCanCloseWindows"))
+        self.jsClipboardCheckBox.setChecked(
+            Preferences.getWebBrowser("JavaScriptCanAccessClipboard"))
+##        self.pluginsGroup.setChecked(
+##            Preferences.getWebBrowser("PluginsEnabled"))
+        # TODO: Click2Flash
+##        self.clickToFlashCheckBox.setChecked(
+##            Preferences.getWebBrowser("ClickToFlashEnabled"))
+        # TODO: Qt 5.6
+##        self.doNotTrackCheckBox.setChecked(
+##            Preferences.getWebBrowser("DoNotTrack"))
+##        self.sendRefererCheckBox.setChecked(
+##            Preferences.getWebBrowser("SendReferer"))
+        
+        self.diskCacheCheckBox.setChecked(
+            Preferences.getWebBrowser("DiskCacheEnabled"))
+        self.cacheSizeSpinBox.setValue(
+            Preferences.getWebBrowser("DiskCacheSize"))
+        
+        self.startupCombo.setCurrentIndex(
+            Preferences.getWebBrowser("StartupBehavior"))
+        self.homePageEdit.setText(
+            Preferences.getWebBrowser("HomePage"))
+        
+        self.defaultSchemeCombo.setCurrentIndex(
+            self.defaultSchemeCombo.findText(
+                Preferences.getWebBrowser("DefaultScheme")))
+        
+        historyLimit = Preferences.getWebBrowser("HistoryLimit")
+        idx = 0
+        if historyLimit == 1:
+            idx = 0
+        elif historyLimit == 7:
+            idx = 1
+        elif historyLimit == 14:
+            idx = 2
+        elif historyLimit == 30:
+            idx = 3
+        elif historyLimit == 365:
+            idx = 4
+        elif historyLimit == -1:
+            idx = 5
+        elif historyLimit == -2:
+            idx = 6
+        else:
+            idx = 5
+        self.expireHistory.setCurrentIndex(idx)
+        
+        for language in range(2, QLocale.LastLanguage + 1):
+            countries = [l.country() for l in QLocale.matchingLocales(
+                language, QLocale.AnyScript, QLocale.AnyCountry)]
+            if len(countries) > 0:
+                self.languageCombo.addItem(
+                    QLocale.languageToString(language), language)
+        self.languageCombo.model().sort(0)
+        self.languageCombo.insertSeparator(0)
+        self.languageCombo.insertItem(0, QLocale.languageToString(0), 0)
+        index = self.languageCombo.findData(
+            Preferences.getWebBrowser("SearchLanguage"))
+        if index > -1:
+            self.languageCombo.setCurrentIndex(index)
+        
+        self.spatialCheckBox.setChecked(
+            Preferences.getWebBrowser("SpatialNavigationEnabled"))
+        self.linksInFocusChainCheckBox.setChecked(
+            Preferences.getWebBrowser("LinksIncludedInFocusChain"))
+        self.xssAuditingCheckBox.setChecked(
+            Preferences.getWebBrowser("XSSAuditingEnabled"))
+        
+        self.webInspectorGroup.setChecked(
+            Preferences.getWebBrowser("WebInspectorEnabled"))
+        self.webInspectorPortSpinBox.setValue(
+            Preferences.getWebBrowser("WebInspectorPort"))
+        
+        # TODO: Configuration: finish these with Qt 5.6
+        # Hide entries not yet supported
+        self.accessKeysCheckBox.hide()
+        self.jsCloseWindowsCheckBox.hide()
+        self.pluginsGroup.hide()
+        self.doNotTrackCheckBox.hide()
+        self.sendRefererCheckBox.hide()
+        self.refererWhitelistButton.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(
+            "JavaScriptEnabled",
+            self.javaScriptGroup.isChecked())
+        Preferences.setWebBrowser(
+            "JavaScriptCanOpenWindows",
+            self.jsOpenWindowsCheckBox.isChecked())
+        # TODO: Qt 5.6
+##        Preferences.setWebBrowser(
+##            "JavaScriptCanCloseWindows",
+##            self.jsCloseWindowsCheckBox.isChecked())
+        Preferences.setWebBrowser(
+            "JavaScriptCanAccessClipboard",
+            self.jsClipboardCheckBox.isChecked())
+        # TODO: Qt 5.6
+##        Preferences.setWebBrowser(
+##            "PluginsEnabled",
+##            self.pluginsGroup.isChecked())
+        # TODO: Click2Flash
+##        Preferences.setWebBrowser(
+##            "ClickToFlashEnabled",
+##            self.clickToFlashCheckBox.isChecked())
+        # TODO: Qt 5.6
+##        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)
+    
+    # TODO: Referer
+##    @pyqtSlot()
+##    def on_refererWhitelistButton_clicked(self):
+##        """
+##        Private slot to edit the referer whitelist.
+##        """
+##        from Helpviewer.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 Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,709 @@
+<?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>
+     </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="QGroupBox" name="pluginsGroup">
+        <property name="toolTip">
+         <string>Select to enable plugins in web pages</string>
+        </property>
+        <property name="title">
+         <string>Enable Plug-ins</string>
+        </property>
+        <property name="checkable">
+         <bool>true</bool>
+        </property>
+        <property name="checked">
+         <bool>false</bool>
+        </property>
+        <layout class="QGridLayout" name="gridLayout_7">
+         <item row="0" column="0">
+          <widget class="QCheckBox" name="clickToFlashCheckBox">
+           <property name="enabled">
+            <bool>false</bool>
+           </property>
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+             <horstretch>8</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="toolTip">
+            <string>Select to activate the Flash blocker</string>
+           </property>
+           <property name="text">
+            <string>Use ClickToFlash on Flash plugins</string>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </widget>
+      </item>
+      <item>
+       <widget class="Line" name="line">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QCheckBox" name="doNotTrackCheckBox">
+        <property name="toolTip">
+         <string>Select to enabled the &quot;Do Not Track&quot; feature</string>
+        </property>
+        <property name="text">
+         <string>Tell web sites I do not want to be tracked</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <layout class="QHBoxLayout" name="horizontalLayout_4">
+        <item>
+         <widget class="QCheckBox" name="sendRefererCheckBox">
+          <property name="toolTip">
+           <string>Select to send referer headers to the server</string>
+          </property>
+          <property name="text">
+           <string>Send Referer header to servers</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <spacer name="horizontalSpacer_6">
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>40</width>
+            <height>20</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+        <item>
+         <widget class="QPushButton" name="refererWhitelistButton">
+          <property name="toolTip">
+           <string>Press to edit the list of whitelisted hosts</string>
+          </property>
+          <property name="text">
+           <string>Edit Referer Whitelist ...</string>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="groupBox_6">
+     <property name="title">
+      <string>Security</string>
+     </property>
+     <layout class="QVBoxLayout" name="verticalLayout_2">
+      <item>
+       <widget class="QCheckBox" name="xssAuditingCheckBox">
+        <property name="toolTip">
+         <string>Select to enable XSS auditing</string>
+        </property>
+        <property name="whatsThis">
+         <string>&lt;b&gt;Enable XSS Auditing&lt;/b&gt;
+&lt;p&gt;This selects whether load requests should be monitored for cross-site scripting attempts. Suspicious scripts will be blocked. These will be reported in the JavaScript console. Enabling this feature might have an impact on performance.&lt;/p&gt;</string>
+        </property>
+        <property name="text">
+         <string>Enable XSS Auditing</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="groupBox">
+     <property name="title">
+      <string>History</string>
+     </property>
+     <layout class="QHBoxLayout" name="horizontalLayout">
+      <item>
+       <widget class="QLabel" name="label_4">
+        <property name="text">
+         <string>Remove history items:</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QComboBox" name="expireHistory">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="toolTip">
+         <string>Select the period for expiration of history entries</string>
+        </property>
+        <item>
+         <property name="text">
+          <string>After one day</string>
+         </property>
+        </item>
+        <item>
+         <property name="text">
+          <string>After one week</string>
+         </property>
+        </item>
+        <item>
+         <property name="text">
+          <string>After two weeks</string>
+         </property>
+        </item>
+        <item>
+         <property name="text">
+          <string>After one month</string>
+         </property>
+        </item>
+        <item>
+         <property name="text">
+          <string>After one year</string>
+         </property>
+        </item>
+        <item>
+         <property name="text">
+          <string>Manually</string>
+         </property>
+        </item>
+        <item>
+         <property name="text">
+          <string>On application exit</string>
+         </property>
+        </item>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="cacheGroup">
+     <property name="title">
+      <string>Browser Cache</string>
+     </property>
+     <layout class="QGridLayout" name="gridLayout_2">
+      <item row="0" column="0" colspan="3">
+       <widget class="QCheckBox" name="diskCacheCheckBox">
+        <property name="text">
+         <string>Enable disk cache</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="0">
+       <widget class="QLabel" name="label_7">
+        <property name="text">
+         <string>Cache size:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="1">
+       <widget class="QSpinBox" name="cacheSizeSpinBox">
+        <property name="toolTip">
+         <string>Enter the maximum size of the disk cache</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+        <property name="suffix">
+         <string> MB</string>
+        </property>
+        <property name="minimum">
+         <number>1</number>
+        </property>
+        <property name="maximum">
+         <number>999</number>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="2">
+       <spacer name="horizontalSpacer">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>410</width>
+          <height>20</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="groupBox_5">
+     <property name="title">
+      <string>Web Search</string>
+     </property>
+     <layout class="QHBoxLayout" name="horizontalLayout_2">
+      <item>
+       <widget class="QLabel" name="label_5">
+        <property name="text">
+         <string>Language:</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QComboBox" name="languageCombo">
+        <property name="toolTip">
+         <string>Select the language to be used for web searches</string>
+        </property>
+        <property name="sizeAdjustPolicy">
+         <enum>QComboBox::AdjustToContents</enum>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <spacer name="horizontalSpacer_3">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>450</width>
+          <height>20</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="groupBox_3">
+     <property name="title">
+      <string>Navigation</string>
+     </property>
+     <layout class="QGridLayout" name="gridLayout_6">
+      <item row="0" column="0">
+       <widget class="QCheckBox" name="spatialCheckBox">
+        <property name="toolTip">
+         <string>Select to enable the spatial navigation feature</string>
+        </property>
+        <property name="whatsThis">
+         <string>&lt;b&gt;Enable Spatial Navigation&lt;/b&gt;
+&lt;p&gt;This enables or disables the Spatial Navigation feature, which consists in the ability to navigate between focusable elements in a Web page, such as hyperlinks and form controls, by using Left, Right, Up and Down arrow keys. For example, if a user presses the Right key, heuristics determine whether there is an element he might be trying to reach towards the right and which element he probably wants.&lt;/p&gt;</string>
+        </property>
+        <property name="text">
+         <string>Enable Spatial Navigation</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1">
+       <widget class="QCheckBox" name="linksInFocusChainCheckBox">
+        <property name="toolTip">
+         <string>Select to include links in focus chain</string>
+        </property>
+        <property name="whatsThis">
+         <string>&lt;b&gt;Include Links in Focus Chain&lt;/b&gt;
+&lt;p&gt;This selects whether hyperlinks should be included in the keyboard focus chain.&lt;/p&gt;</string>
+        </property>
+        <property name="text">
+         <string>Include Links in Focus Chain</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="0">
+       <widget class="QCheckBox" name="accessKeysCheckBox">
+        <property name="toolTip">
+         <string>Select to enable support for access keys</string>
+        </property>
+        <property name="text">
+         <string>Enable access keys</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="webInspectorGroup">
+     <property name="toolTip">
+      <string>Select to enable the Web Inspector tool</string>
+     </property>
+     <property name="title">
+      <string>Enable Web Development (Web Inspector)</string>
+     </property>
+     <property name="checkable">
+      <bool>true</bool>
+     </property>
+     <property name="checked">
+      <bool>false</bool>
+     </property>
+     <layout class="QGridLayout" name="gridLayout_8">
+      <item row="0" column="0">
+       <widget class="QLabel" name="label_2">
+        <property name="text">
+         <string>Web Inspector Port:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1">
+       <widget class="QSpinBox" name="webInspectorPortSpinBox">
+        <property name="toolTip">
+         <string>Enter the port to be used by the web inspector</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+        <property name="minimum">
+         <number>1025</number>
+        </property>
+        <property name="maximum">
+         <number>65535</number>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="2">
+       <spacer name="horizontalSpacer_4">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>372</width>
+          <height>20</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+      <item row="1" column="0" colspan="3">
+       <widget class="QLabel" name="label_6">
+        <property name="text">
+         <string>&lt;font color=&quot;red&quot;&gt;&lt;b&gt;Note:&lt;/b&gt; Web Inspector settings are activated after a restart of the application.&lt;/font&gt;</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <spacer>
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>479</width>
+       <height>121</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+  </layout>
+ </widget>
+ <tabstops>
+  <tabstop>singleHelpWindowCheckBox</tabstop>
+  <tabstop>webSuggestionsCheckBox</tabstop>
+  <tabstop>saveGeometryCheckBox</tabstop>
+  <tabstop>showTabPreviews</tabstop>
+  <tabstop>errorPageCheckBox</tabstop>
+  <tabstop>scrollingCheckBox</tabstop>
+  <tabstop>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>pluginsGroup</tabstop>
+  <tabstop>clickToFlashCheckBox</tabstop>
+  <tabstop>doNotTrackCheckBox</tabstop>
+  <tabstop>sendRefererCheckBox</tabstop>
+  <tabstop>refererWhitelistButton</tabstop>
+  <tabstop>xssAuditingCheckBox</tabstop>
+  <tabstop>expireHistory</tabstop>
+  <tabstop>diskCacheCheckBox</tabstop>
+  <tabstop>cacheSizeSpinBox</tabstop>
+  <tabstop>languageCombo</tabstop>
+  <tabstop>spatialCheckBox</tabstop>
+  <tabstop>linksInFocusChainCheckBox</tabstop>
+  <tabstop>accessKeysCheckBox</tabstop>
+  <tabstop>webInspectorGroup</tabstop>
+  <tabstop>webInspectorPortSpinBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
--- a/Preferences/Shortcuts.py	Sun Mar 06 14:09:37 2016 +0100
+++ b/Preferences/Shortcuts.py	Sun Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:09:37 2016 +0100
+++ b/Preferences/__init__.py	Sun Mar 06 14:12:58 2016 +0100
@@ -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,144 @@
         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",
+        "StartupBehavior": 0,      # show home page
+        "HomePage": "http://eric-ide.python-projects.org",     # TODO: eric: scheme
+        "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
+        # 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": "",
+        # Flash Cookie Manager: identical to helpDefaults
+        # PIM:                  identical to helpDefaults
+        # VirusTotal:           identical to helpDefaults
+    }
+    
+    @classmethod
+    def initWebEngineSettingsDefaults(cls):
+        """
+        Class method to initialize the web engine settings related defaults.
+        """
+        if QWebEngineSettings is None:
+            return
+        
+        webEngineSettings = QWebEngineSettings.globalSettings()
+##        fontFamily = webEngineSettings.fontFamily(
+##            QWebEngineSettings.StandardFont)
+##        fontSize = webEngineSettings.fontSize(
+##            QWebEngineSettings.DefaultFontSize)
+##        cls.webBrowserDefaults["StandardFont"] = \
+##            QFont(fontFamily, fontSize).toString()
+##        fontFamily = webEngineSettings.fontFamily(
+##            QWebEngineSettings.FixedFont)
+##        fontSize = webEngineSettings.fontSize(
+##            QWebEngineSettings.DefaultFixedFontSize)
+##        cls.webBrowserDefaults["FixedFont"] = \
+##            QFont(fontFamily, fontSize).toString()
+        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),
+            "SaveUrlColor": QColor(184, 248, 169),
+            "JavaScriptEnabled": webEngineSettings.testAttribute(
+                QWebEngineSettings.JavascriptEnabled),
+            "JavaScriptCanOpenWindows": webEngineSettings.testAttribute(
+                QWebEngineSettings.JavascriptCanOpenWindows),
+##            "JavaScriptCanCloseWindows": webEngineSettings.testAttribute(
+##                QWebEngineSettings.JavascriptCanCloseWindows),
+            "JavaScriptCanAccessClipboard": webEngineSettings.testAttribute(
+                QWebEngineSettings.JavascriptCanAccessClipboard),
+##            "PluginsEnabled":
+##            websettings.testAttribute(QWebSettings.PluginsEnabled),
+##            "OfflineStorageDatabaseEnabled":
+##            websettings.testAttribute(
+##                QWebSettings.OfflineStorageDatabaseEnabled),
+            "LocalStorageEnabled": webEngineSettings.testAttribute(
+                QWebEngineSettings.LocalStorageEnabled),
+            "DefaultTextEncoding": webEngineSettings.defaultTextEncoding(),
+            "SpatialNavigationEnabled": webEngineSettings.testAttribute(
+                QWebEngineSettings.SpatialNavigationEnabled),
+            "LinksIncludedInFocusChain": webEngineSettings.testAttribute(
+                QWebEngineSettings.LinksIncludedInFocusChain),
+            "LocalContentCanAccessRemoteUrls": webEngineSettings.testAttribute(
+                QWebEngineSettings.LocalContentCanAccessRemoteUrls),
+            "LocalContentCanAccessFileUrls": webEngineSettings.testAttribute(
+                QWebEngineSettings.LocalContentCanAccessFileUrls),
+            "XSSAuditingEnabled": webEngineSettings.testAttribute(
+                QWebEngineSettings.XSSAuditingEnabled),
+            "ScrollAnimatorEnabled": webEngineSettings.testAttribute(
+                QWebEngineSettings.ScrollAnimatorEnabled),
+            "ErrorPageEnabled": webEngineSettings.testAttribute(
+                QWebEngineSettings.ErrorPageEnabled),
+        })
+        
+        cls.webEngineSettingsIntitialized = True
+    
+    webEngineSettingsIntitialized = False
 
     # defaults for system settings
     sysDefaults = {
@@ -1128,10 +1274,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 +2289,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"]:
@@ -2542,6 +2690,206 @@
             "Help/" + key, pwConvert(value, encode=True))
     else:
         prefClass.settings.setValue("Help/" + key, value)
+
+
+def getWebBrowser(key, prefClass=Prefs):
+    """
+    Module function to retrieve the various web browser settings.
+    
+    @param key the key of the value to get
+    @param prefClass preferences class used as the storage area
+    @return the requested help setting
+    """
+    # the following entries are identical to the ones of the help viewer
+    # and are being redirected there
+    if key.startswith(("FlashCookie", "Pim", "VirusTotal")):
+        return getHelp(key, prefClass)
+    
+    # Web inspector stuff must come before initializing web engine settings
+    # because that starts the chromium web process
+    if key == "WebInspectorPort":
+        return int(prefClass.settings.value(
+            "WebBrowser/" + key, prefClass.webBrowserDefaults[key]))
+    elif key == "WebInspectorEnabled":
+        return toBool(prefClass.settings.value(
+            "WebBrowser/" + key, prefClass.webBrowserDefaults[key]))
+    
+    if not prefClass.webEngineSettingsIntitialized:
+        prefClass.initWebEngineSettingsDefaults()
+    
+    if key in ["StandardFont", "FixedFont"]:
+        f = QFont()
+        f.fromString(prefClass.settings.value(
+            "WebBrowser/" + key, prefClass.webBrowserDefaults[key]))
+        return f
+    elif key in ["SaveUrlColor"]:
+        col = prefClass.settings.value("WebBrowser/" + key)
+        if col is not None:
+            return QColor(col)
+        else:
+            return prefClass.webBrowserDefaults[key]
+    elif key in ["WebSearchKeywords"]:
+        # return a list of tuples of (keyword, engine name)
+        keywords = []
+        size = prefClass.settings.beginReadArray("WebBrowser/" + key)
+        for index in range(size):
+            prefClass.settings.setArrayIndex(index)
+            keyword = prefClass.settings.value("Keyword")
+            engineName = prefClass.settings.value("Engine")
+            keywords.append((keyword, engineName))
+        prefClass.settings.endArray()
+        return keywords
+    elif key in ["DownloadManagerDownloads"]:
+        # return a list of tuples of (URL, save location, done flag, page url)
+        downloads = []
+        length = prefClass.settings.beginReadArray("WebBrowser/" + key)
+        for index in range(length):
+            prefClass.settings.setArrayIndex(index)
+            url = prefClass.settings.value("URL")
+            location = prefClass.settings.value("Location")
+            done = toBool(prefClass.settings.value("Done"))
+            pageUrl = prefClass.settings.value("PageURL")
+            if pageUrl is None:
+                pageUrl = QUrl()
+            downloads.append((url, location, done, pageUrl))
+        prefClass.settings.endArray()
+        return downloads
+    elif key == "RssFeeds":
+        # return a list of tuples of (URL, title, icon)
+        feeds = []
+        length = prefClass.settings.beginReadArray("WebBrowser/" + key)
+        for index in range(length):
+            prefClass.settings.setArrayIndex(index)
+            url = prefClass.settings.value("URL")
+            title = prefClass.settings.value("Title")
+            icon = prefClass.settings.value("Icon")
+            feeds.append((url, title, icon))
+        prefClass.settings.endArray()
+        return feeds
+    elif key in ["SyncFtpPassword", "SyncEncryptionKey"]:
+        from Utilities.crypto import pwConvert
+        return pwConvert(prefClass.settings.value(
+            "WebBrowser/" + key, prefClass.helpDefaults[key]), encode=False)
+##    elif key == "HelpViewerType":
+##        # special treatment to adjust for missing QtWebKit
+##        value = int(prefClass.settings.value(
+##            "WebBrowser/" + key, prefClass.helpDefaults[key]))
+##        if QWebSettings is None:
+##            value = prefClass.helpDefaults[key]
+##        return value
+##    elif key in ["AcceptCookies",
+##                 "KeepCookiesUntil", "StartupBehavior",
+##                 "OfflineStorageDatabaseQuota",
+##                 "OfflineWebApplicationCacheQuota", "CachePolicy",
+##                 "AdBlockUpdatePeriod",
+##                  ]:
+    elif key in ["StartupBehavior", "HistoryLimit",
+                 "DownloadManagerRemovePolicy","SyncType", "SyncFtpPort",
+                 "SyncFtpIdleTimeout", "SyncEncryptionKeyLength",
+                 "SearchLanguage", "WebInspectorPort",
+                 "DefaultFontSize", "DefaultFixedFontSize",
+                 "MinimumFontSize", "MinimumLogicalFontSize",
+                 "DiskCacheSize", ]:
+        return int(prefClass.settings.value(
+            "WebBrowser/" + key, prefClass.webBrowserDefaults[key]))
+##    elif key in ["DiskCacheEnabled", "FilterTrackingCookies",
+##                 "PrintBackgrounds", "AdBlockEnabled"
+##                 "JavaEnabled",
+##                 "JavaScriptCanCloseWindows",
+##                 "PluginsEnabled", "DnsPrefetchEnabled",
+##                 "OfflineStorageDatabaseEnabled",
+##                 "OfflineWebApplicationCacheEnabled", "LocalStorageEnabled",
+##                 "AccessKeysEnabled",
+##                 "DoNotTrack", "SendReferer",
+##                 "SiteSpecificQuirksEnabled",
+##                 "ClickToFlashEnabled",
+##                 ]:
+    elif key in ["SingleWebBrowserWindow", "SaveGeometry", "JavaScriptEnabled",
+                 "JavaScriptCanOpenWindows", "JavaScriptCanAccessClipboard",
+                 "AutoLoadImages", "LocalStorageEnabled",
+                 "SpatialNavigationEnabled", "LinksIncludedInFocusChain",
+                 "LocalContentCanAccessRemoteUrls",
+                 "LocalContentCanAccessFileUrls", "XSSAuditingEnabled",
+                 "ScrollAnimatorEnabled", "ErrorPageEnabled",
+                 "WarnOnMultipleClose", "WebSearchSuggestions",
+                 "SyncEnabled", "SyncBookmarks", "SyncHistory",
+                 "SyncPasswords", "SyncUserAgents", "SyncSpeedDial",
+                 "SyncEncryptData", "SyncEncryptPasswordsOnly",
+                 "ShowPreview", "WebInspectorEnabled", "DiskCacheEnabled",
+                 ]:
+        return toBool(prefClass.settings.value(
+            "WebBrowser/" + key, prefClass.webBrowserDefaults[key]))
+##    elif key in ["AdBlockSubscriptions", "AdBlockExceptions",
+##                 "ClickToFlashWhitelist", "SendRefererWhitelist",
+##                 "NoCacheHosts",
+    elif key in ["GreaseMonkeyDisabledScripts",
+                 ]:
+        return toList(prefClass.settings.value(
+            "WebBrowser/" + key, prefClass.helpDefaults[key]))
+    else:
+        return prefClass.settings.value("WebBrowser/" + key,
+                                        prefClass.webBrowserDefaults[key])
+    
+
+def setWebBrowser(key, value, prefClass=Prefs):
+    """
+    Module function to store the various web browser settings.
+    
+    @param key the key of the setting to be set
+    @param value the value to be set
+    @param prefClass preferences class used as the storage area
+    """
+    # the following entries are identical to the ones of the help viewer
+    # and are being redirected there
+    if key.startswith(("FlashCookie", "Pim", "VirusTotal")):
+        setHelp(key, value, prefClass)
+    
+    if key in ["StandardFont", "FixedFont"]:
+        prefClass.settings.setValue("WebBrowser/" + key, value.toString())
+    elif key == "SaveUrlColor":
+        prefClass.settings.setValue("WebBrowser/" + key, value.name())
+    elif key == "WebSearchKeywords":
+        # value is list of tuples of (keyword, engine name)
+        prefClass.settings.remove("WebBrowser/" + key)
+        prefClass.settings.beginWriteArray("WebBrowser/" + key, len(value))
+        index = 0
+        for v in value:
+            prefClass.settings.setArrayIndex(index)
+            prefClass.settings.setValue("Keyword", v[0])
+            prefClass.settings.setValue("Engine", v[1])
+            index += 1
+        prefClass.settings.endArray()
+    elif key == "DownloadManagerDownloads":
+        # value is list of tuples of (URL, save location, done flag, page url)
+        prefClass.settings.remove("Help/" + key)
+        prefClass.settings.beginWriteArray("WebBrowser/" + key, len(value))
+        index = 0
+        for v in value:
+            prefClass.settings.setArrayIndex(index)
+            prefClass.settings.setValue("URL", v[0])
+            prefClass.settings.setValue("Location", v[1])
+            prefClass.settings.setValue("Done", v[2])
+            prefClass.settings.setValue("PageURL", v[3])
+            index += 1
+        prefClass.settings.endArray()
+    elif key == "RssFeeds":
+        # value is list of tuples of (URL, title, icon)
+        prefClass.settings.remove("WebBrowser/" + key)
+        prefClass.settings.beginWriteArray("WebBrowser/" + key, len(value))
+        index = 0
+        for v in value:
+            prefClass.settings.setArrayIndex(index)
+            prefClass.settings.setValue("URL", v[0])
+            prefClass.settings.setValue("Title", v[1])
+            prefClass.settings.setValue("Icon", v[2])
+            index += 1
+        prefClass.settings.endArray()
+    elif key in ["SyncFtpPassword", "SyncEncryptionKey"]:
+        from Utilities.crypto import pwConvert
+        prefClass.settings.setValue(
+            "WebBrowser/" + key, pwConvert(value, encode=True))
+    else:
+        prefClass.settings.setValue("WebBrowser/" + key, value)
     
 
 def getSystem(key, prefClass=Prefs):
@@ -3197,6 +3545,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 Mar 06 14:09:37 2016 +0100
+++ b/UI/UserInterface.py	Sun Mar 06 14:12:58 2016 +0100
@@ -9,7 +9,7 @@
 
 from __future__ import unicode_literals
 try:
-    str = unicode
+    str = unicode       # __IGNORE_EXCEPTION__
 except NameError:
     pass
 
@@ -32,6 +32,15 @@
     WEBKIT_AVAILABLE = True
 except ImportError:
     WEBKIT_AVAILABLE = False
+# TODO: adjust this to 5.6.0 when done
+if qVersion() < "5.5.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 +477,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 +501,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 +1611,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 +1940,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 +3017,16 @@
             .format(sip_version_str)
         versionText += """<tr><td><b>QScintilla</b></td><td>{0}</td></tr>"""\
             .format(QSCINTILLA_VERSION_STR)
-        try:
+        if WEBENGINE_AVAILABLE:
+            from WebBrowser.Tools import WebBrowserTools
+            chromeVersion = WebBrowserTools.getWebEngineVersions()[0]
+            versionText += \
+                """<tr><td><b>WebEngine</b></td><td>{0}</td></tr>"""\
+                .format(chromeVersion)
+        if WEBKIT_AVAILABLE:
             from PyQt5.QtWebKit import qWebKitVersion
             versionText += """<tr><td><b>WebKit</b></td><td>{0}</td></tr>"""\
                 .format(qWebKitVersion())
-        except ImportError:
-            pass
         versionText += """<tr><td><b>{0}</b></td><td>{1}</td></tr>"""\
             .format(Program, Version)
         versionText += self.tr("""</table>""")
@@ -5213,12 +5232,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 +5255,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 +5279,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 +5321,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 +5341,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 Mar 06 14:09:37 2016 +0100
+++ b/Utilities/__init__.py	Sun Mar 06 14:12:58 2016 +0100
@@ -1682,6 +1682,15 @@
         qVersion(), linesep, PYQT_VERSION_STR, linesep)
     info += "  sip {0}{1}  QScintilla {2}{3}".format(
         sip_version_str, linesep, QSCINTILLA_VERSION_STR, linesep)
+    # TODO: adjust this to 5.6.0 when done
+    if qVersion() >= "5.5.0":
+        try:
+            from PyQt5 import QtWebEngineWidgets    # __IGNORE_WARNING__
+            from WebBrowser.Tools import WebBrowserTools
+            chromeVersion = WebBrowserTools.getWebEngineVersions()[0]
+            info += "  WebEngine {0}{1}".format(chromeVersion, linesep)
+        except ImportError:
+            pass
     try:
         from PyQt5.QtWebKit import qWebKitVersion
         info += "  WebKit {0}{1}".format(qWebKitVersion(), linesep)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Bookmarks/AddBookmarkDialog.py	Sun Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,615 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the bookmarks manager.
+"""
+
+from __future__ import unicode_literals
+
+import os
+
+from PyQt5.QtCore import pyqtSignal, Qt, QT_TRANSLATE_NOOP, QObject, QFile, \
+    QIODevice, QXmlStreamReader, QDate, QDateTime, QFileInfo, QUrl, \
+    QCoreApplication
+from PyQt5.QtWidgets import QUndoStack, QUndoCommand, QDialog
+
+from E5Gui import E5MessageBox, E5FileDialog
+
+from .BookmarkNode import BookmarkNode
+
+from Utilities.AutoSaver import AutoSaver
+import Utilities
+import Preferences
+
+BOOKMARKBAR = QT_TRANSLATE_NOOP("BookmarksManager", "Bookmarks Bar")
+BOOKMARKMENU = QT_TRANSLATE_NOOP("BookmarksManager", "Bookmarks Menu")
+
+StartRoot = 0
+StartMenu = 1
+StartToolBar = 2
+
+
+class BookmarksManager(QObject):
+    """
+    Class implementing the bookmarks manager.
+    
+    @signal entryAdded(BookmarkNode) emitted after a bookmark node has been
+        added
+    @signal entryRemoved(BookmarkNode, int, BookmarkNode) emitted after a
+        bookmark node has been removed
+    @signal entryChanged(BookmarkNode) emitted after a bookmark node has been
+        changed
+    @signal bookmarksSaved() emitted after the bookmarks were saved
+    @signal bookmarksReloaded() emitted after the bookmarks were reloaded
+    """
+    entryAdded = pyqtSignal(BookmarkNode)
+    entryRemoved = pyqtSignal(BookmarkNode, int, BookmarkNode)
+    entryChanged = pyqtSignal(BookmarkNode)
+    bookmarksSaved = pyqtSignal()
+    bookmarksReloaded = pyqtSignal()
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object (QObject)
+        """
+        super(BookmarksManager, self).__init__(parent)
+        
+        self.__saveTimer = AutoSaver(self, self.save)
+        self.entryAdded.connect(self.__saveTimer.changeOccurred)
+        self.entryRemoved.connect(self.__saveTimer.changeOccurred)
+        self.entryChanged.connect(self.__saveTimer.changeOccurred)
+        
+        self.__initialize()
+    
+    def __initialize(self):
+        """
+        Private method to initialize some data.
+        """
+        self.__loaded = False
+        self.__bookmarkRootNode = None
+        self.__toolbar = None
+        self.__menu = None
+        self.__bookmarksModel = None
+        self.__commands = QUndoStack()
+    
+    @classmethod
+    def getFileName(cls):
+        """
+        Class method to get the file name of the bookmark file.
+        
+        @return name of the bookmark file (string)
+        """
+        return os.path.join(Utilities.getConfigDir(), "web_browser",
+                            "bookmarks.xbel")
+    
+    def close(self):
+        """
+        Public method to close the bookmark manager.
+        """
+        self.__saveTimer.saveIfNeccessary()
+    
+    def undoRedoStack(self):
+        """
+        Public method to get a reference to the undo stack.
+        
+        @return reference to the undo stack (QUndoStack)
+        """
+        return self.__commands
+    
+    def changeExpanded(self):
+        """
+        Public method to handle a change of the expanded state.
+        """
+        self.__saveTimer.changeOccurred()
+    
+    def reload(self):
+        """
+        Public method used to initiate a reloading of the bookmarks.
+        """
+        self.__initialize()
+        self.load()
+        self.bookmarksReloaded.emit()
+    
+    def load(self):
+        """
+        Public method to load the bookmarks.
+        
+        @exception RuntimeError raised to indicate an error loading the
+            bookmarks
+        """
+        if self.__loaded:
+            return
+        
+        self.__loaded = True
+        
+        bookmarkFile = self.getFileName()
+        if not QFile.exists(bookmarkFile):
+            from . import DefaultBookmarks_rc       # __IGNORE_WARNING__
+            bookmarkFile = QFile(":/DefaultBookmarks.xbel")
+            bookmarkFile.open(QIODevice.ReadOnly)
+        
+        from .XbelReader import XbelReader
+        reader = XbelReader()
+        self.__bookmarkRootNode = reader.read(bookmarkFile)
+        if reader.error() != QXmlStreamReader.NoError:
+            E5MessageBox.warning(
+                None,
+                self.tr("Loading Bookmarks"),
+                self.tr(
+                    """Error when loading bookmarks on line {0},"""
+                    """ column {1}:\n {2}""")
+                .format(reader.lineNumber(),
+                        reader.columnNumber(),
+                        reader.errorString()))
+        
+        others = []
+        for index in range(
+                len(self.__bookmarkRootNode.children()) - 1, -1, -1):
+            node = self.__bookmarkRootNode.children()[index]
+            if node.type() == BookmarkNode.Folder:
+                if (node.title == self.tr("Toolbar Bookmarks") or
+                    node.title == BOOKMARKBAR) and \
+                   self.__toolbar is None:
+                    node.title = self.tr(BOOKMARKBAR)
+                    self.__toolbar = node
+                
+                if (node.title == self.tr("Menu") or
+                    node.title == BOOKMARKMENU) and \
+                   self.__menu is None:
+                    node.title = self.tr(BOOKMARKMENU)
+                    self.__menu = node
+            else:
+                others.append(node)
+            self.__bookmarkRootNode.remove(node)
+        
+        if len(self.__bookmarkRootNode.children()) > 0:
+            raise RuntimeError("Error loading bookmarks.")
+        
+        if self.__toolbar is None:
+            self.__toolbar = BookmarkNode(BookmarkNode.Folder,
+                                          self.__bookmarkRootNode)
+            self.__toolbar.title = self.tr(BOOKMARKBAR)
+        else:
+            self.__bookmarkRootNode.add(self.__toolbar)
+        
+        if self.__menu is None:
+            self.__menu = BookmarkNode(BookmarkNode.Folder,
+                                       self.__bookmarkRootNode)
+            self.__menu.title = self.tr(BOOKMARKMENU)
+        else:
+            self.__bookmarkRootNode.add(self.__menu)
+        
+        for node in others:
+            self.__menu.add(node)
+    
+    def save(self):
+        """
+        Public method to save the bookmarks.
+        """
+        if not self.__loaded:
+            return
+        
+        from .XbelWriter import XbelWriter
+        writer = XbelWriter()
+        bookmarkFile = self.getFileName()
+        
+        # save root folder titles in English (i.e. not localized)
+        self.__menu.title = BOOKMARKMENU
+        self.__toolbar.title = BOOKMARKBAR
+        if not writer.write(bookmarkFile, self.__bookmarkRootNode):
+            E5MessageBox.warning(
+                None,
+                self.tr("Saving Bookmarks"),
+                self.tr("""Error saving bookmarks to <b>{0}</b>.""")
+                .format(bookmarkFile))
+        
+        # restore localized titles
+        self.__menu.title = self.tr(BOOKMARKMENU)
+        self.__toolbar.title = self.tr(BOOKMARKBAR)
+        
+        self.bookmarksSaved.emit()
+    
+    def addBookmark(self, parent, node, row=-1):
+        """
+        Public method to add a bookmark.
+        
+        @param parent reference to the node to add to (BookmarkNode)
+        @param node reference to the node to add (BookmarkNode)
+        @param row row number (integer)
+        """
+        if not self.__loaded:
+            return
+        
+        self.setTimestamp(node, BookmarkNode.TsAdded,
+                          QDateTime.currentDateTime())
+        
+        command = InsertBookmarksCommand(self, parent, node, row)
+        self.__commands.push(command)
+    
+    def removeBookmark(self, node):
+        """
+        Public method to remove a bookmark.
+        
+        @param node reference to the node to be removed (BookmarkNode)
+        """
+        if not self.__loaded:
+            return
+        
+        parent = node.parent()
+        row = parent.children().index(node)
+        command = RemoveBookmarksCommand(self, parent, row)
+        self.__commands.push(command)
+    
+    def setTitle(self, node, newTitle):
+        """
+        Public method to set the title of a bookmark.
+        
+        @param node reference to the node to be changed (BookmarkNode)
+        @param newTitle title to be set (string)
+        """
+        if not self.__loaded:
+            return
+        
+        command = ChangeBookmarkCommand(self, node, newTitle, True)
+        self.__commands.push(command)
+    
+    def setUrl(self, node, newUrl):
+        """
+        Public method to set the URL of a bookmark.
+        
+        @param node reference to the node to be changed (BookmarkNode)
+        @param newUrl URL to be set (string)
+        """
+        if not self.__loaded:
+            return
+        
+        command = ChangeBookmarkCommand(self, node, newUrl, False)
+        self.__commands.push(command)
+    
+    def setNodeChanged(self, node):
+        """
+        Public method to signal changes of bookmarks other than title, URL
+        or timestamp.
+        
+        @param node reference to the bookmark (BookmarkNode)
+        """
+        self.__saveTimer.changeOccurred()
+    
+    def setTimestamp(self, node, timestampType, timestamp):
+        """
+        Public method to set the URL of a bookmark.
+        
+        @param node reference to the node to be changed (BookmarkNode)
+        @param timestampType type of the timestamp to set
+            (BookmarkNode.TsAdded, BookmarkNode.TsModified,
+            BookmarkNode.TsVisited)
+        @param timestamp timestamp to set (QDateTime)
+        """
+        if not self.__loaded:
+            return
+        
+        assert timestampType in [BookmarkNode.TsAdded,
+                                 BookmarkNode.TsModified,
+                                 BookmarkNode.TsVisited]
+        
+        if timestampType == BookmarkNode.TsAdded:
+            node.added = timestamp
+        elif timestampType == BookmarkNode.TsModified:
+            node.modified = timestamp
+        elif timestampType == BookmarkNode.TsVisited:
+            node.visited = timestamp
+        self.__saveTimer.changeOccurred()
+    
+    def bookmarks(self):
+        """
+        Public method to get a reference to the root bookmark node.
+        
+        @return reference to the root bookmark node (BookmarkNode)
+        """
+        if not self.__loaded:
+            self.load()
+        
+        return self.__bookmarkRootNode
+    
+    def menu(self):
+        """
+        Public method to get a reference to the bookmarks menu node.
+        
+        @return reference to the bookmarks menu node (BookmarkNode)
+        """
+        if not self.__loaded:
+            self.load()
+        
+        return self.__menu
+    
+    def toolbar(self):
+        """
+        Public method to get a reference to the bookmarks toolbar node.
+        
+        @return reference to the bookmarks toolbar node (BookmarkNode)
+        """
+        if not self.__loaded:
+            self.load()
+        
+        return self.__toolbar
+    
+    def bookmarksModel(self):
+        """
+        Public method to get a reference to the bookmarks model.
+        
+        @return reference to the bookmarks model (BookmarksModel)
+        """
+        if self.__bookmarksModel is None:
+            from .BookmarksModel import BookmarksModel
+            self.__bookmarksModel = BookmarksModel(self, self)
+        return self.__bookmarksModel
+    
+    def importBookmarks(self):
+        """
+        Public method to import bookmarks.
+        """
+        from .BookmarksImportDialog import BookmarksImportDialog
+        dlg = BookmarksImportDialog()
+        if dlg.exec_() == QDialog.Accepted:
+            importRootNode = dlg.getImportedBookmarks()
+            if importRootNode is not None:
+                self.addBookmark(self.menu(), importRootNode)
+    
+    def exportBookmarks(self):
+        """
+        Public method to export the bookmarks.
+        """
+        fileName, selectedFilter = E5FileDialog.getSaveFileNameAndFilter(
+            None,
+            self.tr("Export Bookmarks"),
+            "eric6_bookmarks.xbel",
+            self.tr("XBEL bookmarks (*.xbel);;"
+                    "XBEL bookmarks (*.xml);;"
+                    "HTML Bookmarks (*.html)"))
+        if not fileName:
+            return
+        
+        ext = QFileInfo(fileName).suffix()
+        if not ext:
+            ex = selectedFilter.split("(*")[1].split(")")[0]
+            if ex:
+                fileName += ex
+        
+        ext = QFileInfo(fileName).suffix()
+        if ext == "html":
+            from .NsHtmlWriter import NsHtmlWriter
+            writer = NsHtmlWriter()
+        else:
+            from .XbelWriter import XbelWriter
+            writer = XbelWriter()
+        if not writer.write(fileName, self.__bookmarkRootNode):
+            E5MessageBox.critical(
+                None,
+                self.tr("Exporting Bookmarks"),
+                self.tr("""Error exporting bookmarks to <b>{0}</b>.""")
+                .format(fileName))
+    
+    def iconChanged(self, url):
+        """
+        Public slot to update the icon image for an URL.
+        
+        @param url URL of the icon to update (QUrl or string)
+        """
+        if isinstance(url, QUrl):
+            url = url.toString()
+        nodes = self.bookmarksForUrl(url)
+        for node in nodes:
+            self.bookmarksModel().entryChanged(node)
+    
+    def bookmarkForUrl(self, url, start=StartRoot):
+        """
+        Public method to get a bookmark node for a given URL.
+        
+        @param url URL of the bookmark to search for (QUrl or string)
+        @keyparam start indicator for the start of the search
+            (StartRoot, StartMenu, StartToolBar)
+        @return bookmark node for the given url (BookmarkNode)
+        """
+        if start == StartMenu:
+            startNode = self.__menu
+        elif start == StartToolBar:
+            startNode = self.__toolbar
+        else:
+            startNode = self.__bookmarkRootNode
+        if startNode is None:
+            return None
+        
+        if isinstance(url, QUrl):
+            url = url.toString()
+        
+        return self.__searchBookmark(url, startNode)
+    
+    def __searchBookmark(self, url, startNode):
+        """
+        Private method get a bookmark node for a given URL.
+        
+        @param url URL of the bookmark to search for (string)
+        @param startNode reference to the node to start searching
+            (BookmarkNode)
+        @return bookmark node for the given url (BookmarkNode)
+        """
+        bm = None
+        for node in startNode.children():
+            if node.type() == BookmarkNode.Folder:
+                bm = self.__searchBookmark(url, node)
+            elif node.type() == BookmarkNode.Bookmark:
+                if node.url == url:
+                    bm = node
+            if bm is not None:
+                return bm
+        return None
+    
+    def bookmarksForUrl(self, url, start=StartRoot):
+        """
+        Public method to get a list of bookmark nodes for a given URL.
+        
+        @param url URL of the bookmarks to search for (QUrl or string)
+        @keyparam start indicator for the start of the search
+            (StartRoot, StartMenu, StartToolBar)
+        @return list of bookmark nodes for the given url (list of BookmarkNode)
+        """
+        if start == StartMenu:
+            startNode = self.__menu
+        elif start == StartToolBar:
+            startNode = self.__toolbar
+        else:
+            startNode = self.__bookmarkRootNode
+        if startNode is None:
+            return []
+        
+        if isinstance(url, QUrl):
+            url = url.toString()
+        
+        return self.__searchBookmarks(url, startNode)
+    
+    def __searchBookmarks(self, url, startNode):
+        """
+        Private method get a list of bookmark nodes for a given URL.
+        
+        @param url URL of the bookmarks to search for (string)
+        @param startNode reference to the node to start searching
+            (BookmarkNode)
+        @return list of bookmark nodes for the given url (list of BookmarkNode)
+        """
+        bm = []
+        for node in startNode.children():
+            if node.type() == BookmarkNode.Folder:
+                bm.extend(self.__searchBookmarks(url, node))
+            elif node.type() == BookmarkNode.Bookmark:
+                if node.url == url:
+                    bm.append(node)
+        return bm
+
+
+class RemoveBookmarksCommand(QUndoCommand):
+    """
+    Class implementing the Remove undo command.
+    """
+    def __init__(self, bookmarksManager, parent, row):
+        """
+        Constructor
+        
+        @param bookmarksManager reference to the bookmarks manager
+            (BookmarksManager)
+        @param parent reference to the parent node (BookmarkNode)
+        @param row row number of bookmark (integer)
+        """
+        super(RemoveBookmarksCommand, self).__init__(
+            QCoreApplication.translate("BookmarksManager", "Remove Bookmark"))
+        
+        self._row = row
+        self._bookmarksManager = bookmarksManager
+        try:
+            self._node = parent.children()[row]
+        except IndexError:
+            self._node = BookmarkNode()
+        self._parent = parent
+    
+    def undo(self):
+        """
+        Public slot to perform the undo action.
+        """
+        self._parent.add(self._node, self._row)
+        self._bookmarksManager.entryAdded.emit(self._node)
+    
+    def redo(self):
+        """
+        Public slot to perform the redo action.
+        """
+        self._parent.remove(self._node)
+        self._bookmarksManager.entryRemoved.emit(
+            self._parent, self._row, self._node)
+
+
+class InsertBookmarksCommand(RemoveBookmarksCommand):
+    """
+    Class implementing the Insert undo command.
+    """
+    def __init__(self, bookmarksManager, parent, node, row):
+        """
+        Constructor
+        
+        @param bookmarksManager reference to the bookmarks manager
+            (BookmarksManager)
+        @param parent reference to the parent node (BookmarkNode)
+        @param node reference to the node to be inserted (BookmarkNode)
+        @param row row number of bookmark (integer)
+        """
+        RemoveBookmarksCommand.__init__(self, bookmarksManager, parent, row)
+        self.setText(QCoreApplication.translate(
+            "BookmarksManager", "Insert Bookmark"))
+        self._node = node
+    
+    def undo(self):
+        """
+        Public slot to perform the undo action.
+        """
+        RemoveBookmarksCommand.redo(self)
+    
+    def redo(self):
+        """
+        Public slot to perform the redo action.
+        """
+        RemoveBookmarksCommand.undo(self)
+
+
+class ChangeBookmarkCommand(QUndoCommand):
+    """
+    Class implementing the Insert undo command.
+    """
+    def __init__(self, bookmarksManager, node, newValue, title):
+        """
+        Constructor
+        
+        @param bookmarksManager reference to the bookmarks manager
+            (BookmarksManager)
+        @param node reference to the node to be changed (BookmarkNode)
+        @param newValue new value to be set (string)
+        @param title flag indicating a change of the title (True) or
+            the URL (False) (boolean)
+        """
+        super(ChangeBookmarkCommand, self).__init__()
+        
+        self._bookmarksManager = bookmarksManager
+        self._title = title
+        self._newValue = newValue
+        self._node = node
+        
+        if self._title:
+            self._oldValue = self._node.title
+            self.setText(QCoreApplication.translate(
+                "BookmarksManager", "Name Change"))
+        else:
+            self._oldValue = self._node.url
+            self.setText(QCoreApplication.translate(
+                "BookmarksManager", "Address Change"))
+    
+    def undo(self):
+        """
+        Public slot to perform the undo action.
+        """
+        if self._title:
+            self._node.title = self._oldValue
+        else:
+            self._node.url = self._oldValue
+        self._bookmarksManager.entryChanged.emit(self._node)
+    
+    def redo(self):
+        """
+        Public slot to perform the redo action.
+        """
+        if self._title:
+            self._node.title = self._newValue
+        else:
+            self._node.url = self._newValue
+        self._bookmarksManager.entryChanged.emit(self._node)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Bookmarks/BookmarksMenu.py	Sun Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,240 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a tool bar showing bookmarks.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSignal, Qt, QUrl, QCoreApplication
+from PyQt5.QtGui import QCursor
+from PyQt5.QtWidgets import QMenu
+from PyQt5.QtWebKitWidgets import QWebPage
+
+from E5Gui.E5ModelToolBar import E5ModelToolBar
+
+from .BookmarksModel import BookmarksModel
+
+
+class BookmarksToolBar(E5ModelToolBar):
+    """
+    Class implementing a tool bar showing bookmarks.
+    
+    @signal openUrl(QUrl, str) emitted to open a URL in the current tab
+    @signal newUrl(QUrl, str) emitted to open a URL in a new tab
+    """
+    openUrl = pyqtSignal(QUrl, str)
+    newUrl = pyqtSignal(QUrl, str)
+    
+    def __init__(self, mainWindow, model, parent=None):
+        """
+        Constructor
+        
+        @param mainWindow reference to the main window (HelpWindow)
+        @param model reference to the bookmarks model (BookmarksModel)
+        @param parent reference to the parent widget (QWidget)
+        """
+        E5ModelToolBar.__init__(
+            self, QCoreApplication.translate("BookmarksToolBar", "Bookmarks"),
+            parent)
+        
+        self.__mw = mainWindow
+        self.__bookmarksModel = model
+        
+        self.__mw.bookmarksManager().bookmarksReloaded.connect(self.__rebuild)
+        
+        self.setModel(model)
+        self.setRootIndex(model.nodeIndex(
+            self.__mw.bookmarksManager().toolbar()))
+        
+        self.setContextMenuPolicy(Qt.CustomContextMenu)
+        self.customContextMenuRequested.connect(self.__contextMenuRequested)
+        self.activated.connect(self.__bookmarkActivated)
+        
+        self.setHidden(True)
+        self.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
+        
+        self._build()
+    
+    def __rebuild(self):
+        """
+        Private slot to rebuild the toolbar.
+        """
+        self.__bookmarksModel = \
+            self.__mw.bookmarksManager().bookmarksModel()
+        self.setModel(self.__bookmarksModel)
+        self.setRootIndex(self.__bookmarksModel.nodeIndex(
+            self.__mw.bookmarksManager().toolbar()))
+        self._build()
+    
+    def __contextMenuRequested(self, pos):
+        """
+        Private slot to handle the context menu request.
+        
+        @param pos position the context menu shall be shown (QPoint)
+        """
+        act = self.actionAt(pos)
+        menu = QMenu()
+        
+        if act is not None:
+            v = act.data()
+            
+            if act.menu() is None:
+                menu.addAction(
+                    self.tr("Open"),
+                    self.__openBookmark).setData(v)
+                menu.addAction(
+                    self.tr("Open in New Tab\tCtrl+LMB"),
+                    self.__openBookmarkInNewTab).setData(v)
+                menu.addAction(
+                    self.tr("Open in New Window"),
+                    self.__openBookmarkInNewWindow).setData(v)
+                menu.addAction(
+                    self.tr("Open in New Private Window"),
+                    self.__openBookmarkInPrivateWindow).setData(v)
+                menu.addSeparator()
+            
+            menu.addAction(
+                self.tr("Remove"),
+                self.__removeBookmark).setData(v)
+            menu.addSeparator()
+            
+            menu.addAction(
+                self.tr("Properties..."),
+                self.__edit).setData(v)
+            menu.addSeparator()
+        
+        menu.addAction(self.tr("Add Bookmark..."), self.__newBookmark)
+        menu.addAction(self.tr("Add Folder..."), self.__newFolder)
+        
+        menu.exec_(QCursor.pos())
+    
+    def __bookmarkActivated(self, idx):
+        """
+        Private slot handling the activation of a bookmark.
+        
+        @param idx index of the activated bookmark (QModelIndex)
+        """
+        assert idx.isValid()
+        
+        if self._mouseButton == Qt.XButton1:
+            self.__mw.currentBrowser().pageAction(QWebPage.Back).trigger()
+        elif self._mouseButton == Qt.XButton2:
+            self.__mw.currentBrowser().pageAction(QWebPage.Forward).trigger()
+        elif self._mouseButton == Qt.LeftButton:
+            if self._keyboardModifiers & Qt.ControlModifier:
+                self.newUrl.emit(
+                    idx.data(BookmarksModel.UrlRole),
+                    idx.data(Qt.DisplayRole))
+            else:
+                self.openUrl.emit(
+                    idx.data(BookmarksModel.UrlRole),
+                    idx.data(Qt.DisplayRole))
+    
+    def __openToolBarBookmark(self):
+        """
+        Private slot to open a bookmark in the current browser tab.
+        """
+        idx = self.index(self.sender())
+        
+        if self._keyboardModifiers & Qt.ControlModifier:
+            self.newUrl.emit(
+                idx.data(BookmarksModel.UrlRole),
+                idx.data(Qt.DisplayRole))
+        else:
+            self.openUrl.emit(
+                idx.data(BookmarksModel.UrlRole),
+                idx.data(Qt.DisplayRole))
+        self.resetFlags()
+    
+    def __openBookmark(self):
+        """
+        Private slot to open a bookmark in the current browser tab.
+        """
+        idx = self.index(self.sender())
+        
+        self.openUrl.emit(
+            idx.data(BookmarksModel.UrlRole),
+            idx.data(Qt.DisplayRole))
+    
+    def __openBookmarkInNewTab(self):
+        """
+        Private slot to open a bookmark in a new browser tab.
+        """
+        idx = self.index(self.sender())
+        
+        self.newUrl.emit(
+            idx.data(BookmarksModel.UrlRole),
+            idx.data(Qt.DisplayRole))
+    
+    def __openBookmarkInNewWindow(self):
+        """
+        Private slot to open a bookmark in a new window.
+        """
+        idx = self.index(self.sender())
+        url = idx.data(BookmarksModel.UrlRole)
+        
+        from WebBrowser.WebBrowserWindow import WebBrowserWindow
+        WebBrowserWindow.mainWindow().newWindow(url)
+    
+    def __openBookmarkInPrivateWindow(self):
+        """
+        Private slot to open a bookmark in a new private window.
+        """
+        idx = self.index(self.sender())
+        url = idx.data(BookmarksModel.UrlRole)
+        
+        from WebBrowser.WebBrowserWindow import WebBrowserWindow
+        WebBrowserWindow.mainWindow().newPrivateWindow(url)
+    
+    def __removeBookmark(self):
+        """
+        Private slot to remove a bookmark.
+        """
+        idx = self.index(self.sender())
+        
+        self.__bookmarksModel.removeRow(idx.row(), self.rootIndex())
+    
+    def __newBookmark(self):
+        """
+        Private slot to add a new bookmark.
+        """
+        from .AddBookmarkDialog import AddBookmarkDialog
+        dlg = AddBookmarkDialog()
+        dlg.setCurrentIndex(self.rootIndex())
+        dlg.exec_()
+    
+    def __newFolder(self):
+        """
+        Private slot to add a new bookmarks folder.
+        """
+        from .AddBookmarkDialog import AddBookmarkDialog
+        dlg = AddBookmarkDialog()
+        dlg.setCurrentIndex(self.rootIndex())
+        dlg.setFolder(True)
+        dlg.exec_()
+    
+    def _createMenu(self):
+        """
+        Protected method to create the menu for a tool bar action.
+        
+        @return menu for a tool bar action (E5ModelMenu)
+        """
+        from .BookmarksMenu import BookmarksMenu
+        menu = BookmarksMenu(self)
+        menu.openUrl.connect(self.openUrl)
+        menu.newUrl.connect(self.newUrl)
+        return menu
+    
+    def __edit(self):
+        """
+        Private slot to edit a bookmarks properties.
+        """
+        from .BookmarkPropertiesDialog import BookmarkPropertiesDialog
+        idx = self.index(self.sender())
+        node = self.__bookmarksModel.node(idx)
+        dlg = BookmarkPropertiesDialog(node)
+        dlg.exec_()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Bookmarks/DefaultBookmarks.qrc	Sun Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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/Download/DownloadAskActionDialog.py	Sun Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,57 @@
+# -*- 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
+
+
+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 = QCoreApplication.translate(
+            "DownloadUtilities",
+            "%n:{0:02} minutes remaining""", "",
+            minutes).format(seconds)
+    else:
+        seconds = int(timeRemaining)
+        remaining = QCoreApplication.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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,167 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2015 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the feature permission bar widget.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSlot, QUrl
+from PyQt5.QtWidgets import QLabel, QHBoxLayout, QPushButton
+from PyQt5.QtWebEngineWidgets import QWebEnginePage
+
+from E5Gui.E5AnimatedWidget import E5AnimatedWidget
+
+import UI.PixmapCache
+
+
+class FeaturePermissionBar(E5AnimatedWidget):
+    """
+    Class implementing the feature permission bar widget.
+    """
+    DefaultHeight = 30
+    
+    def __init__(self, page, origin, feature, manager):
+        """
+        Constructor
+        
+        @param page reference to the web page
+        @type QWebView
+        @param origin security origin requesting the feature
+        @type QUrl
+        @param feature requested feature
+        @type QWebPage.Feature
+        @param manager reference to the feature permissions manager
+        @type FeaturePermissionManager
+        """
+        super(FeaturePermissionBar, self).__init__(parent=page.view())
+        
+        self.__origin = QUrl(origin)
+        self.__feature = feature
+        self.__page = page
+        self.__manager = manager
+        
+        self.__permissionFeatureTexts = {
+            # TODO: Qt 5.6
+##            QWebEnginePage.Notifications:
+##                self.tr("{0} wants to use desktop notifications."),
+            QWebEnginePage.Geolocation:
+                self.tr("{0} wants to use your position."),
+            QWebEnginePage.MediaAudioCapture:
+                self.tr("{0} wants to use your microphone."),
+            QWebEnginePage.MediaVideoCapture:
+                self.tr("{0} wants to use your camera."),
+            QWebEnginePage.MediaAudioVideoCapture:
+                self.tr("{0} wants to use your microphone and camera."),
+        }
+        self.__permissionFeatureIconNames = {
+            # TODO: Qt 5.6
+##            QWebEnginePage.Notifications: "notification.png",
+            QWebEnginePage.Geolocation: "geolocation.png",
+            QWebEnginePage.MediaAudioCapture: "audiocapture.png",
+            QWebEnginePage.MediaVideoCapture: "camera.png",
+            QWebEnginePage.MediaAudioVideoCapture: "audio-video.png",
+        }
+        
+        self.setAutoFillBackground(True)
+        self.__layout = QHBoxLayout()
+        self.setLayout(self.__layout)
+        self.__layout.setContentsMargins(9, 0, 0, 0)
+        self.__iconLabel = QLabel(self)
+        self.__layout.addWidget(self.__iconLabel)
+        self.__messageLabel = QLabel(self)
+        self.__layout.addWidget(self.__messageLabel)
+        self.__layout.addStretch()
+        self.__rememberButton = QPushButton(self.tr("Remember"), self)
+        self.__rememberButton.setCheckable(True)
+        self.__allowButton = QPushButton(self.tr("Allow"), self)
+        self.__denyButton = QPushButton(self.tr("Deny"), self)
+        self.__discardButton = QPushButton(UI.PixmapCache.getIcon("close.png"),
+                                           "", self)
+        self.__allowButton.clicked.connect(self.__permissionGranted)
+        self.__denyButton.clicked.connect(self.__permissionDenied)
+        self.__discardButton.clicked.connect(self.__permissionUnknown)
+        self.__layout.addWidget(self.__rememberButton)
+        self.__layout.addWidget(self.__allowButton)
+        self.__layout.addWidget(self.__denyButton)
+        self.__layout.addWidget(self.__discardButton)
+        
+        try:
+            self.__iconLabel.setPixmap(UI.PixmapCache.getPixmap(
+                self.__permissionFeatureIconNames[self.__feature]))
+        except KeyError:
+            pass
+        
+        try:
+            self.__messageLabel.setText(
+                self.__permissionFeatureTexts[self.__feature].format(
+                    self.__origin.host()))
+        except KeyError:
+            self.__messageLabel.setText(
+                self.tr("{0} wants to use an unknown feature.").format(
+                    self.__origin.host()))
+        
+        self.__page.loadStarted.connect(self.hide)
+        
+        self.resize(self.__page.view().width(), self.height())
+        self.startAnimation()
+    
+    @pyqtSlot()
+    def hide(self):
+        """
+        Public slot to hide the animated widget.
+        """
+        self.__page.loadStarted.disconnect(self.hide)
+        super(FeaturePermissionBar, self).hide()
+    
+    def __permissionDenied(self):
+        """
+        Private slot handling the user pressing the deny button.
+        """
+        if self.__page is None or self.__manager is None:
+            return
+        
+        self.__page.setFeaturePermission(
+            self.__origin, self.__feature,
+            QWebEnginePage.PermissionDeniedByUser)
+        
+        if self.__rememberButton.isChecked():
+            self.__manager.rememberFeaturePermission(
+                self.__page.url().host(), self.__feature,
+                QWebEnginePage.PermissionDeniedByUser)
+        
+        self.hide()
+    
+    def __permissionGranted(self):
+        """
+        Private slot handling the user pressing the allow button.
+        """
+        if self.__page is None or self.__manager is None:
+            return
+        
+        self.__page.setFeaturePermission(
+            self.__origin, self.__feature,
+            QWebEnginePage.PermissionGrantedByUser)
+        
+        if self.__rememberButton.isChecked():
+            self.__manager.rememberFeaturePermission(
+                self.__page.url().host(), self.__feature,
+                QWebEnginePage.PermissionGrantedByUser)
+        
+        self.hide()
+    
+    def __permissionUnknown(self):
+        """
+        Private slot handling the user closing the dialog without.
+        """
+        if self.__page is None or self.__manager is None:
+            return
+        
+        self.__page.setFeaturePermission(
+            self.__origin, self.__feature,
+            QWebEnginePage.PermissionUnknown)
+        
+        self.hide()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/FeaturePermissions/FeaturePermissionManager.py	Sun Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,187 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2015 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the feature permission manager object.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import QObject
+from PyQt5.QtWidgets import QDialog
+from PyQt5.QtWebEngineWidgets import QWebEnginePage
+
+import Globals
+import Preferences
+
+
+class FeaturePermissionManager(QObject):
+    """
+    Class implementing the feature permission manager object.
+    """
+    SettingsKeyFormat = "WebBrowser/FeaturePermissions/{0}"
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object
+        @type QObject
+        """
+        super(FeaturePermissionManager, self).__init__(parent)
+        
+        self.__featurePermissions = {
+            # TODO: Qt 5.6
+##            QWebEnginePage.Notifications: {
+##                QWebEnginePage.PermissionGrantedByUser: [],
+##                QWebEnginePage.PermissionDeniedByUser: [],
+##            },
+            QWebEnginePage.Geolocation: {
+                QWebEnginePage.PermissionGrantedByUser: [],
+                QWebEnginePage.PermissionDeniedByUser: [],
+            },
+            QWebEnginePage.MediaAudioCapture: {
+                QWebEnginePage.PermissionGrantedByUser: [],
+                QWebEnginePage.PermissionDeniedByUser: [],
+            },
+            QWebEnginePage.MediaVideoCapture: {
+                QWebEnginePage.PermissionGrantedByUser: [],
+                QWebEnginePage.PermissionDeniedByUser: [],
+            },
+            QWebEnginePage.MediaAudioVideoCapture: {
+                QWebEnginePage.PermissionGrantedByUser: [],
+                QWebEnginePage.PermissionDeniedByUser: [],
+            },
+        }
+        self.__featurePermissionsKeys = {
+            # TODO: Qt 5.6
+##            (QWebEnginePage.Notifications,
+##             QWebEnginePage.PermissionGrantedByUser):
+##            "NotificationsGranted",
+##            (QWebEnginePage.Notifications,
+##             QWebEnginePage.PermissionDeniedByUser):
+##            "NotificationsDenied",
+            (QWebEnginePage.Geolocation,
+             QWebEnginePage.PermissionGrantedByUser):
+            "GeolocationGranted",
+            (QWebEnginePage.Geolocation,
+             QWebEnginePage.PermissionDeniedByUser):
+            "GeolocationDenied",
+            (QWebEnginePage.MediaAudioCapture,
+             QWebEnginePage.PermissionGrantedByUser):
+            "MediaAudioCaptureGranted",
+            (QWebEnginePage.MediaAudioCapture,
+             QWebEnginePage.PermissionDeniedByUser):
+            "MediaAudioCaptureDenied",
+            (QWebEnginePage.MediaVideoCapture,
+             QWebEnginePage.PermissionGrantedByUser):
+            "MediaVideoCaptureGranted",
+            (QWebEnginePage.MediaVideoCapture,
+             QWebEnginePage.PermissionDeniedByUser):
+            "MediaVideoCaptureDenied",
+            (QWebEnginePage.MediaAudioVideoCapture,
+             QWebEnginePage.PermissionGrantedByUser):
+            "MediaAudioVideoCaptureGranted",
+            (QWebEnginePage.MediaAudioVideoCapture,
+             QWebEnginePage.PermissionDeniedByUser):
+            "MediaAudioVideoCaptureDenied",
+        }
+        
+        self.__loaded = False
+
+    def requestFeaturePermission(self, page, origin, feature):
+        """
+        Public method to request a feature permission.
+        
+        @param page reference to the requesting web page
+        @type QWebEnginePage
+        @param origin security origin requesting the feature
+        @type QUrl
+        @param feature requested feature
+        @type QWebEnginePage.Feature
+        """
+        if origin is None or origin.isEmpty():
+            return
+        
+        if not self.__loaded:
+            self.__loadSettings()
+        
+        host = origin.host()
+        
+        if feature in self.__featurePermissions:
+            for permission in self.__featurePermissions[feature]:
+                if host in self.__featurePermissions[feature][permission]:
+                    page.setFeaturePermission(origin, feature, permission)
+                    return
+        
+        from .FeaturePermissionBar import FeaturePermissionBar
+        bar = FeaturePermissionBar(page, origin, feature, self)
+        bar.show()
+    
+    def rememberFeaturePermission(self, host, feature, permission):
+        """
+        Public method to remember a user decision for a feature permission.
+        
+        @param host host name to remember the decision for
+        @type str
+        @param feature feature to be remembered
+        @type QWebEnginePage.Feature
+        @param permission feature permission to be remembered
+        @type QWebEnginePage.PermissionPolicy
+        """
+        if feature in self.__featurePermissions:
+            if host not in self.__featurePermissions[feature][permission]:
+                self.__featurePermissions[feature][permission].append(host)
+                self.__saveSettings()
+    
+    def __loadSettings(self):
+        """
+        Private method to load the remembered feature permissions.
+        """
+        if self.__loaded:
+            # no reloading allowed
+            return
+        
+        for (feature, permission), key in \
+                self.__featurePermissionsKeys.items():
+            self.__featurePermissions[feature][permission] = \
+                Globals.toList(Preferences.Prefs.settings.value(
+                    FeaturePermissionManager.SettingsKeyFormat.format(key),
+                    []
+                ))
+        
+        self.__loaded = True
+    
+    def __saveSettings(self):
+        """
+        Private method to save the remembered feature permissions.
+        """
+        if not self.__loaded:
+            return
+        
+        import WebBrowser.WebBrowserWindow
+        if WebBrowser.WebBrowserWindow.WebBrowserWindow.isPrivate():
+            return
+        
+        for (feature, permission), key in \
+                self.__featurePermissionsKeys.items():
+            Preferences.Prefs.settings.setValue(
+                FeaturePermissionManager.SettingsKeyFormat.format(key),
+                self.__featurePermissions[feature][permission])
+    
+    def showFeaturePermissionsDialog(self):
+        """
+        Public method to show a dialog to manage the remembered feature
+        permissions.
+        """
+        if not self.__loaded:
+            self.__loadSettings()
+        
+        from .FeaturePermissionsDialog import FeaturePermissionsDialog
+        dlg = FeaturePermissionsDialog(self.__featurePermissions)
+        if dlg.exec_() == QDialog.Accepted:
+            newFeaturePermissions = dlg.getData()
+            self.__featurePermissions = newFeaturePermissions
+            self.__saveSettings()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/FeaturePermissions/FeaturePermissionsDialog.py	Sun Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,227 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2015 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the feature permission dialog.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSlot, Qt
+from PyQt5.QtWidgets import QDialog, QTreeWidgetItem, QTreeWidget, \
+    QAbstractItemView
+from PyQt5.QtWebEngineWidgets import QWebEnginePage
+
+import UI.PixmapCache
+
+from .Ui_FeaturePermissionsDialog import Ui_FeaturePermissionsDialog
+
+
+class FeaturePermissionsDialog(QDialog, Ui_FeaturePermissionsDialog):
+    """
+    Class implementing the feature permission dialog.
+    """
+    def __init__(self, featurePermissions, parent=None):
+        """
+        Constructor
+        
+        @param featurePermissions dictionary with remembered feature
+            permissions
+        @type dict of dict of list
+        @param parent reference to the parent widget
+        @type QWidget
+        """
+        super(FeaturePermissionsDialog, self).__init__(parent)
+        self.setupUi(self)
+        
+        # add the various lists
+        self.notifList = QTreeWidget()
+        self.notifList.setAlternatingRowColors(True)
+        self.notifList.setSelectionMode(QAbstractItemView.ExtendedSelection)
+        self.notifList.setRootIsDecorated(False)
+        self.notifList.setItemsExpandable(False)
+        self.notifList.setAllColumnsShowFocus(True)
+        self.notifList.setObjectName("notifList")
+        self.notifList.setSortingEnabled(True)
+        self.notifList.headerItem().setText(0, self.tr("Host"))
+        self.notifList.headerItem().setText(1, self.tr("Permission"))
+        self.tabWidget.addTab(
+            self.notifList, 
+            UI.PixmapCache.getIcon("notification.png"),
+            self.tr("Notifications"))
+        
+        self.geoList = QTreeWidget()
+        self.geoList.setAlternatingRowColors(True)
+        self.geoList.setSelectionMode(QAbstractItemView.ExtendedSelection)
+        self.geoList.setRootIsDecorated(False)
+        self.geoList.setItemsExpandable(False)
+        self.geoList.setAllColumnsShowFocus(True)
+        self.geoList.setObjectName("geoList")
+        self.geoList.setSortingEnabled(True)
+        self.geoList.headerItem().setText(0, self.tr("Host"))
+        self.geoList.headerItem().setText(1, self.tr("Permission"))
+        self.tabWidget.addTab(
+            self.geoList,
+            UI.PixmapCache.getIcon("geolocation.png"),
+            self.tr("Geolocation"))
+        
+        self.micList = QTreeWidget()
+        self.micList.setAlternatingRowColors(True)
+        self.micList.setSelectionMode(QAbstractItemView.ExtendedSelection)
+        self.micList.setRootIsDecorated(False)
+        self.micList.setItemsExpandable(False)
+        self.micList.setAllColumnsShowFocus(True)
+        self.micList.setObjectName("micList")
+        self.micList.setSortingEnabled(True)
+        self.micList.headerItem().setText(0, self.tr("Host"))
+        self.micList.headerItem().setText(1, self.tr("Permission"))
+        self.tabWidget.addTab(
+            self.micList,
+            UI.PixmapCache.getIcon("audiocapture.png"),
+            self.tr("Microphone"))
+        
+        self.camList = QTreeWidget()
+        self.camList.setAlternatingRowColors(True)
+        self.camList.setSelectionMode(QAbstractItemView.ExtendedSelection)
+        self.camList.setRootIsDecorated(False)
+        self.camList.setItemsExpandable(False)
+        self.camList.setAllColumnsShowFocus(True)
+        self.camList.setObjectName("camList")
+        self.camList.setSortingEnabled(True)
+        self.camList.headerItem().setText(0, self.tr("Host"))
+        self.camList.headerItem().setText(1, self.tr("Permission"))
+        self.tabWidget.addTab(
+            self.camList,
+            UI.PixmapCache.getIcon("camera.png"),
+            self.tr("Camera"))
+        
+        self.micCamList = QTreeWidget()
+        self.micCamList.setAlternatingRowColors(True)
+        self.micCamList.setSelectionMode(QAbstractItemView.ExtendedSelection)
+        self.micCamList.setRootIsDecorated(False)
+        self.micCamList.setItemsExpandable(False)
+        self.micCamList.setAllColumnsShowFocus(True)
+        self.micCamList.setObjectName("micCamList")
+        self.micCamList.setSortingEnabled(True)
+        self.micCamList.headerItem().setText(0, self.tr("Host"))
+        self.micCamList.headerItem().setText(1, self.tr("Permission"))
+        self.tabWidget.addTab(
+            self.micCamList,
+            UI.PixmapCache.getIcon("audio-video.png"),
+            self.tr("Microphone && Camera"))
+        
+        self.setTabOrder(self.tabWidget, self.notifList)
+        self.setTabOrder(self.notifList, self.geoList)
+        self.setTabOrder(self.geoList, self.micList)
+        self.setTabOrder(self.micList, self.camList)
+        self.setTabOrder(self.camList, self.micCamList)
+        self.setTabOrder(self.micCamList, self.removeButton)
+        self.setTabOrder(self.removeButton, self.removeAllButton)
+        
+        self.__permissionStrings = {
+            QWebEnginePage.PermissionGrantedByUser: self.tr("Allow"),
+            QWebEnginePage.PermissionDeniedByUser: self.tr("Deny"),
+        }
+        
+        self.__permissionsLists = {
+            # TODO: Qt 5.6
+##            QWebEnginePage.Notifications: self.notifList,
+            QWebEnginePage.Geolocation: self.geoList,
+            QWebEnginePage.MediaAudioCapture: self.micList,
+            QWebEnginePage.MediaVideoCapture: self.camList,
+            QWebEnginePage.MediaAudioVideoCapture: self.micCamList,
+        }
+        
+        for feature, permissionsList in self.__permissionsLists.items():
+            for permission in featurePermissions[feature]:
+                for host in featurePermissions[feature][permission]:
+                    itm = QTreeWidgetItem(
+                        permissionsList,
+                        [host, self.__permissionStrings[permission]])
+                    itm.setData(0, Qt.UserRole, permission)
+        
+        self.__previousCurrent = -1
+        self.tabWidget.currentChanged.connect(self.__currentTabChanged)
+        self.tabWidget.setCurrentIndex(0)
+    
+    @pyqtSlot(int)
+    def __currentTabChanged(self, index):
+        """
+        Private slot handling changes of the selected tab.
+        
+        @param index index of the current tab
+        @type int
+        """
+        if self.__previousCurrent >= 0:
+            previousList = self.tabWidget.widget(self.__previousCurrent)
+            previousList.itemSelectionChanged.disconnect(
+                self.__itemSelectionChanged)
+        
+        self.__updateButtons()
+        
+        currentList = self.tabWidget.currentWidget()
+        currentList.itemSelectionChanged.connect(self.__itemSelectionChanged)
+        self.__previousCurrent = index
+    
+    def __updateButtons(self):
+        """
+        Private method to update the buttons.
+        """
+        currentList = self.tabWidget.currentWidget()
+        self.removeAllButton.setEnabled(
+            currentList.topLevelItemCount() > 0)
+        self.removeButton.setEnabled(
+            len(currentList.selectedItems()) > 0)
+    
+    @pyqtSlot()
+    def __itemSelectionChanged(self):
+        """
+        Private slot handling changes in the current list of selected items.
+        """
+        self.__updateButtons()
+    
+    @pyqtSlot()
+    def on_removeButton_clicked(self):
+        """
+        Private slot to remove selected entries.
+        """
+        currentList = self.tabWidget.currentWidget()
+        for itm in currentList.selectedItems():
+            row = currentList.indexOfTopLevelItem(itm)
+            itm = currentList.takeTopLevelItem(row)
+            del itm
+        self.__updateButtons()
+    
+    @pyqtSlot()
+    def on_removeAllButton_clicked(self):
+        """
+        Private slot to remove all entries.
+        """
+        currentList = self.tabWidget.currentWidget()
+        while currentList.topLevelItemCount() > 0:
+            itm = currentList.takeTopLevelItem(0)      # __IGNORE_WARNING__
+            del itm
+        self.__updateGeoButtons()
+    
+    def getData(self):
+        """
+        Public method to retrieve the dialog contents.
+        
+        @return new feature permission settings
+        @rtype dict of dict of list
+        """
+        featurePermissions = {}
+        for feature, permissionsList in self.__permissionsLists.items():
+            featurePermissions[feature] = {
+                QWebEnginePage.PermissionGrantedByUser: [],
+                QWebEnginePage.PermissionDeniedByUser: [],
+            }
+            for row in range(permissionsList.topLevelItemCount()):
+                itm = permissionsList.topLevelItem(row)
+                host = itm.text(0)
+                permission = itm.data(0, Qt.UserRole)
+                featurePermissions[feature][permission].append(host)
+        
+        return featurePermissions
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/FeaturePermissions/FeaturePermissionsDialog.ui	Sun Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,106 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2011 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog to add RSS feeds.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import QUrl, qVersion
+from PyQt5.QtWidgets import QDialog, QPushButton, QLabel
+
+from E5Gui import E5MessageBox
+
+from .Ui_FeedsDialog import Ui_FeedsDialog
+
+import UI.PixmapCache
+
+
+class FeedsDialog(QDialog, Ui_FeedsDialog):
+    """
+    Class implementing a dialog to add RSS feeds.
+    """
+    def __init__(self, availableFeeds, browser, parent=None):
+        """
+        Constructor
+        
+        @param availableFeeds list of available RSS feeds (list of tuple of
+            two strings)
+        @param browser reference to the browser widget (WebBrowserView)
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(FeedsDialog, self).__init__(parent)
+        self.setupUi(self)
+        
+        self.iconLabel.setPixmap(UI.PixmapCache.getPixmap("rss48.png"))
+        
+        self.__browser = browser
+        
+        self.__availableFeeds = availableFeeds[:]
+        for row in range(len(self.__availableFeeds)):
+            feed = self.__availableFeeds[row]
+            button = QPushButton(self)
+            button.setText(self.tr("Add"))
+            button.feed = feed
+            label = QLabel(self)
+            label.setText(feed[0])
+            self.feedsLayout.addWidget(label, row, 0)
+            self.feedsLayout.addWidget(button, row, 1)
+            button.clicked.connect(self.__addFeed)
+        
+        msh = self.minimumSizeHint()
+        self.resize(max(self.width(), msh.width()), msh.height())
+    
+    def __addFeed(self):
+        """
+        Private slot to add a RSS feed.
+        """
+        button = self.sender()
+        urlString = button.feed[1]
+        url = QUrl(urlString)
+        if url.isRelative():
+            url = self.__browser.url().resolved(url)
+            if qVersion() >= "5.0.0":
+                urlString = url.toDisplayString(QUrl.FullyDecoded)
+            else:
+                urlString = url.toString()
+##            if not urlString.startswith("/"):
+##                urlString = "/" + urlString
+##            urlString = self.__browser.url().host() + urlString
+##            tmpUrl = QUrl(urlString)
+##            if not tmpUrl.scheme():
+##                urlString = "http://" + urlString
+##            tmpUrl = QUrl(urlString)
+##            if not tmpUrl.scheme() or not tmpUrl.host():
+##                return
+        if not url.isValid():
+            return
+        
+        if button.feed[0]:
+            title = button.feed[0]
+        else:
+            title = self.__browser.url().host()
+        
+        from WebBrowser.WebBrowserWindow import WebBrowserWindow
+        feedsManager = WebBrowserWindow.feedsManager()
+        if feedsManager.addFeed(urlString, title, self.__browser.icon()):
+            if WebBrowserWindow.notificationsEnabled():
+                WebBrowserWindow.showNotification(
+                    UI.PixmapCache.getPixmap("rss48.png"),
+                    self.tr("Add RSS Feed"),
+                    self.tr("""The feed was added successfully."""))
+            else:
+                E5MessageBox.information(
+                    self,
+                    self.tr("Add RSS Feed"),
+                    self.tr("""The feed was added successfully."""))
+        else:
+            E5MessageBox.warning(
+                self,
+                self.tr("Add RSS Feed"),
+                self.tr("""The feed was already added before."""))
+            
+        self.close()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Feeds/FeedsDialog.ui	Sun Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,434 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2011 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a RSS feeds manager dialog.
+"""
+
+from __future__ import unicode_literals
+try:
+    str = unicode       # __IGNORE_EXCEPTION__
+except NameError:
+    pass
+
+from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt, QUrl, QXmlStreamReader
+from PyQt5.QtGui import QIcon, QCursor
+from PyQt5.QtWidgets import QDialog, QTreeWidgetItem, QMenu, QApplication
+from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply
+
+from E5Gui import E5MessageBox
+
+from .Ui_FeedsManager import Ui_FeedsManager
+
+import Preferences
+import UI.PixmapCache
+
+
+class FeedsManager(QDialog, Ui_FeedsManager):
+    """
+    Class implementing a RSS feeds manager dialog.
+    
+    @signal openUrl(QUrl, str) emitted to open a URL in the current tab
+    @signal newUrl(QUrl, str) emitted to open a URL in a new tab
+    """
+    openUrl = pyqtSignal(QUrl, str)
+    newUrl = pyqtSignal(QUrl, str)
+    
+    UrlStringRole = Qt.UserRole
+    ErrorDataRole = Qt.UserRole + 1
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(FeedsManager, self).__init__(parent)
+        self.setupUi(self)
+        self.setWindowFlags(Qt.Window)
+        
+        self.__wasShown = False
+        self.__loaded = False
+        self.__feeds = []
+        self.__replies = {}
+        # dict key is the id of the request object
+        # dict value is a tuple of request and tree item
+        
+        self.feedsTree.setContextMenuPolicy(Qt.CustomContextMenu)
+        self.feedsTree.customContextMenuRequested.connect(
+            self.__customContextMenuRequested)
+        self.feedsTree.itemActivated.connect(self.__itemActivated)
+    
+    def show(self):
+        """
+        Public slot to show the feeds manager dialog.
+        """
+        super(FeedsManager, self).show()
+        
+        if not self.__wasShown:
+            self.__enableButtons()
+            self.on_reloadAllButton_clicked()
+            self.__wasShown = True
+    
+    def addFeed(self, urlString, title, icon):
+        """
+        Public method to add a feed.
+        
+        @param urlString URL of the feed (string)
+        @param title title of the feed (string)
+        @param icon icon for the feed (QIcon)
+        @return flag indicating a successful addition of the feed (boolean)
+        """
+        if urlString == "":
+            return False
+        
+        if not self.__loaded:
+            self.__load()
+        
+        # step 1: check, if feed was already added
+        for feed in self.__feeds:
+            if feed[0] == urlString:
+                return False
+        
+        # step 2: add the feed
+        if icon.isNull():
+            icon = UI.PixmapCache.getIcon("rss16.png")
+        feed = (urlString, title, icon)
+        self.__feeds.append(feed)
+        self.__addFeedItem(feed)
+        self.__save()
+        
+        return True
+    
+    def __addFeedItem(self, feed):
+        """
+        Private slot to add a top level feed item.
+        
+        @param feed tuple containing feed info (URL, title, icon)
+            (string, string, QIcon)
+        """
+        itm = QTreeWidgetItem(self.feedsTree, [feed[1]])
+        itm.setIcon(0, feed[2])
+        itm.setData(0, FeedsManager.UrlStringRole, feed[0])
+    
+    def __load(self):
+        """
+        Private method to load the feeds data.
+        """
+        self.__feeds = Preferences.getWebBrowser("RssFeeds")
+        self.__loaded = True
+        
+        # populate the feeds tree top level with the feeds
+        self.feedsTree.clear()
+        for feed in self.__feeds:
+            self.__addFeedItem(feed)
+    
+    def __save(self):
+        """
+        Private method to store the feeds data.
+        """
+        if not self.__loaded:
+            self.__load()
+        
+        Preferences.setWebBrowser("RssFeeds", self.__feeds)
+    
+    @pyqtSlot()
+    def on_reloadAllButton_clicked(self):
+        """
+        Private slot to reload all feeds.
+        """
+        if not self.__loaded:
+            self.__load()
+        
+        for index in range(self.feedsTree.topLevelItemCount()):
+            itm = self.feedsTree.topLevelItem(index)
+            self.__reloadFeed(itm)
+    
+    @pyqtSlot()
+    def on_reloadButton_clicked(self):
+        """
+        Private slot to reload the selected feed.
+        """
+        itm = self.feedsTree.selectedItems()[0]
+        self.__reloadFeed(itm)
+    
+    @pyqtSlot()
+    def on_editButton_clicked(self):
+        """
+        Private slot to edit the selected feed.
+        """
+        itm = self.feedsTree.selectedItems()[0]
+        origTitle = itm.text(0)
+        origUrlString = itm.data(0, FeedsManager.UrlStringRole)
+        
+        feedToChange = None
+        for feed in self.__feeds:
+            if feed[0] == origUrlString:
+                feedToChange = feed
+                break
+        if feedToChange:
+            feedIndex = self.__feeds.index(feedToChange)
+            
+            from .FeedEditDialog import FeedEditDialog
+            dlg = FeedEditDialog(origUrlString, origTitle)
+            if dlg.exec_() == QDialog.Accepted:
+                urlString, title = dlg.getData()
+                for feed in self.__feeds:
+                    if feed[0] == urlString:
+                        E5MessageBox.critical(
+                            self,
+                            self.tr("Duplicate Feed URL"),
+                            self.tr(
+                                """A feed with the URL {0} exists already."""
+                                """ Aborting...""".format(urlString)))
+                        return
+                
+                self.__feeds[feedIndex] = (urlString, title, feedToChange[2])
+                self.__save()
+                
+                itm.setText(0, title)
+                itm.setData(0, FeedsManager.UrlStringRole, urlString)
+                self.__reloadFeed(itm)
+    
+    @pyqtSlot()
+    def on_deleteButton_clicked(self):
+        """
+        Private slot to delete the selected feed.
+        """
+        itm = self.feedsTree.selectedItems()[0]
+        title = itm.text(0)
+        res = E5MessageBox.yesNo(
+            self,
+            self.tr("Delete Feed"),
+            self.tr(
+                """<p>Do you really want to delete the feed"""
+                """ <b>{0}</b>?</p>""".format(title)))
+        if res:
+            urlString = itm.data(0, FeedsManager.UrlStringRole)
+            if urlString:
+                feedToDelete = None
+                for feed in self.__feeds:
+                    if feed[0] == urlString:
+                        feedToDelete = feed
+                        break
+                if feedToDelete:
+                    self.__feeds.remove(feedToDelete)
+                    self.__save()
+                
+                index = self.feedsTree.indexOfTopLevelItem(itm)
+                if index != -1:
+                    self.feedsTree.takeTopLevelItem(index)
+                    del itm
+    
+    @pyqtSlot()
+    def on_feedsTree_itemSelectionChanged(self):
+        """
+        Private slot to enable the various buttons depending on the selection.
+        """
+        self.__enableButtons()
+    
+    def __enableButtons(self):
+        """
+        Private slot to disable/enable various buttons.
+        """
+        selItems = self.feedsTree.selectedItems()
+        if len(selItems) == 1 and \
+           self.feedsTree.indexOfTopLevelItem(selItems[0]) != -1:
+            enable = True
+        else:
+            enable = False
+        
+        self.reloadButton.setEnabled(enable)
+        self.editButton.setEnabled(enable)
+        self.deleteButton.setEnabled(enable)
+    
+    def __reloadFeed(self, itm):
+        """
+        Private method to reload the given feed.
+        
+        @param itm feed item to be reloaded (QTreeWidgetItem)
+        """
+        urlString = itm.data(0, FeedsManager.UrlStringRole)
+        if urlString == "":
+            return
+        
+        for child in itm.takeChildren():
+            del child
+        
+        from WebBrowser.WebBrowserWindow import WebBrowserWindow
+        request = QNetworkRequest(QUrl(urlString))
+        reply = WebBrowserWindow.networkManager().get(request)
+        reply.finished.connect(self.__feedLoaded)
+        self.__replies[id(reply)] = (reply, itm)
+    
+    def __feedLoaded(self):
+        """
+        Private slot to extract the loaded feed data.
+        """
+        reply = self.sender()
+        if id(reply) not in self.__replies:
+            return
+        
+        topItem = self.__replies[id(reply)][1]
+        del self.__replies[id(reply)]
+        
+        if reply.error() == QNetworkReply.NoError:
+            linkString = ""
+            titleString = ""
+            
+            xml = QXmlStreamReader()
+            xmlData = reply.readAll()
+            xml.addData(xmlData)
+            
+            while not xml.atEnd():
+                xml.readNext()
+                if xml.isStartElement():
+                    if xml.name() == "item":
+                        linkString = xml.attributes().value("rss:about")
+                    elif xml.name() == "link":
+                        linkString = xml.attributes().value("href")
+                    currentTag = xml.name()
+                elif xml.isEndElement():
+                    if xml.name() in ["item", "entry"]:
+                        itm = QTreeWidgetItem(topItem)
+                        itm.setText(0, titleString)
+                        itm.setData(0, FeedsManager.UrlStringRole, linkString)
+                        itm.setIcon(0, UI.PixmapCache.getIcon("rss16.png"))
+                        
+                        linkString = ""
+                        titleString = ""
+                elif xml.isCharacters() and not xml.isWhitespace():
+                    if currentTag == "title":
+                        titleString = xml.text()
+                    elif currentTag == "link":
+                        linkString += xml.text()
+            
+            if topItem.childCount() == 0:
+                itm = QTreeWidgetItem(topItem)
+                itm.setText(0, self.tr("Error fetching feed"))
+                itm.setData(0, FeedsManager.UrlStringRole, "")
+                itm.setData(0, FeedsManager.ErrorDataRole,
+                            str(xmlData, encoding="utf-8"))
+            
+            topItem.setExpanded(True)
+        else:
+            linkString = ""
+            titleString = reply.errorString()
+            itm = QTreeWidgetItem(topItem)
+            itm.setText(0, titleString)
+            itm.setData(0, FeedsManager.UrlStringRole, linkString)
+            topItem.setExpanded(True)
+    
+    def __customContextMenuRequested(self, pos):
+        """
+        Private slot to handle the context menu request for the feeds tree.
+        
+        @param pos position the context menu was requested (QPoint)
+        """
+        itm = self.feedsTree.currentItem()
+        if itm is None:
+            return
+        
+        if self.feedsTree.indexOfTopLevelItem(itm) != -1:
+            return
+        
+        urlString = itm.data(0, FeedsManager.UrlStringRole)
+        if urlString:
+            menu = QMenu()
+            menu.addAction(
+                self.tr("&Open"), self.__openMessageInCurrentTab)
+            menu.addAction(
+                self.tr("Open in New &Tab"), self.__openMessageInNewTab)
+            menu.addSeparator()
+            menu.addAction(self.tr("&Copy URL to Clipboard"),
+                           self.__copyUrlToClipboard)
+            menu.exec_(QCursor.pos())
+        else:
+            errorString = itm.data(0, FeedsManager.ErrorDataRole)
+            if errorString:
+                menu = QMenu()
+                menu.addAction(
+                    self.tr("&Show error data"), self.__showError)
+                menu.exec_(QCursor.pos())
+    
+    def __itemActivated(self, itm, column):
+        """
+        Private slot to handle the activation of an item.
+        
+        @param itm reference to the activated item (QTreeWidgetItem)
+        @param column column of the activation (integer)
+        """
+        if self.feedsTree.indexOfTopLevelItem(itm) != -1:
+            return
+        
+        self.__openMessage(
+            QApplication.keyboardModifiers() &
+            Qt.ControlModifier == Qt.ControlModifier)
+        
+    def __openMessageInCurrentTab(self):
+        """
+        Private slot to open a feed message in the current browser tab.
+        """
+        self.__openMessage(False)
+    
+    def __openMessageInNewTab(self):
+        """
+        Private slot to open a feed message in a new browser tab.
+        """
+        self.__openMessage(True)
+    
+    def __openMessage(self, newTab):
+        """
+        Private method to open a feed message.
+        
+        @param newTab flag indicating to open the feed message in a new tab
+            (boolean)
+        """
+        itm = self.feedsTree.currentItem()
+        if itm is None:
+            return
+        
+        urlString = itm.data(0, FeedsManager.UrlStringRole)
+        if urlString:
+            title = itm.text(0)
+            
+            if newTab:
+                self.newUrl.emit(QUrl(urlString), title)
+            else:
+                self.openUrl.emit(QUrl(urlString), title)
+        else:
+            errorString = itm.data(0, FeedsManager.ErrorDataRole)
+            if errorString:
+                self.__showError()
+    
+    def __copyUrlToClipboard(self):
+        """
+        Private slot to copy the URL of the selected item to the clipboard.
+        """
+        itm = self.feedsTree.currentItem()
+        if itm is None:
+            return
+        
+        if self.feedsTree.indexOfTopLevelItem(itm) != -1:
+            return
+        
+        urlString = itm.data(0, FeedsManager.UrlStringRole)
+        if urlString:
+            QApplication.clipboard().setText(urlString)
+    
+    def __showError(self):
+        """
+        Private slot to show error info for a failed load operation.
+        """
+        itm = self.feedsTree.currentItem()
+        if itm is None:
+            return
+        
+        errorStr = itm.data(0, FeedsManager.ErrorDataRole)
+        if errorStr:
+            E5MessageBox.critical(
+                self,
+                self.tr("Error loading feed"),
+                "{0}".format(errorStr))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Feeds/FeedsManager.ui	Sun Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,365 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2015 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the Flash cookie manager.
+"""
+
+from __future__ import unicode_literals
+
+try:
+    str = unicode       # __IGNORE_EXCEPTION__
+except NameError:
+    pass
+
+import shutil
+
+from PyQt5.QtCore import QObject, QTimer, QDir, QFileInfo, QFile
+
+from .FlashCookie import FlashCookie
+from .FlashCookieReader import FlashCookieReader, FlashCookieReaderError
+
+import WebBrowser.WebBrowserWindow
+
+import Preferences
+
+
+class FlashCookieManager(QObject):
+    """
+    Class implementing the Flash cookie manager object.
+    """
+    RefreshInterval = 60 * 1000
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object
+        @type QObject
+        """
+        super(FlashCookieManager, self).__init__(parent)
+        
+        self.__flashCookieManagerDialog = None
+        self.__flashCookies = []    # list of FlashCookie
+        self.__newCookiesList = []  # list of str
+##        self.__whitelist = []       # list of str
+##        self.__blacklist = []       # list of str
+        
+        self.__timer = QTimer(self)
+        self.__timer.setInterval(FlashCookieManager.RefreshInterval)
+        self.__timer.timeout.connect(self.__autoRefresh)
+        
+        # start the timer if needed
+        self.__startStopTimer()
+        
+        if Preferences.getWebBrowser("FlashCookiesDeleteOnStartExit"):
+            self.__loadFlashCookies()
+            self.__removeAllButWhitelisted()
+    
+    def shutdown(self):
+        """
+        Public method to perform shutdown actions.
+        """
+        if self.__flashCookieManagerDialog is not None:
+            self.__flashCookieManagerDialog.close()
+        
+        if Preferences.getWebBrowser("FlashCookiesDeleteOnStartExit"):
+            self.__removeAllButWhitelisted()
+    
+    def setFlashCookies(self, cookies):
+        """
+        Public method to set the list of cached Flash cookies.
+        
+        @param cookies list of Flash cookies to store
+        @type list of FlashCookie
+        """
+        self.__flashCookies = cookies[:]
+    
+    def flashCookies(self):
+        """
+        Public method to get the list of cached Flash cookies.
+        
+        @return list of Flash cookies
+        @rtype list of FlashCookie
+        """
+        if not self.__flashCookies:
+            self.__loadFlashCookies()
+        
+        return self.__flashCookies[:]
+    
+    def newCookiesList(self):
+        """
+        Public method to get the list of newly detected Flash cookies.
+        
+        @return list of newly detected Flash cookies
+        @rtype list of str
+        """
+        return self.__newCookiesList[:]
+    
+    def clearNewOrigins(self):
+        """
+        Public method to clear the list of newly detected Flash cookies.
+        """
+        self.__newCookiesList = []
+    
+    def clearCache(self):
+        """
+        Public method to clear the list of cached Flash cookies.
+        """
+        self.__flashCookies = []
+    
+    def __isBlacklisted(self, cookie):
+        """
+        Private method to check for a blacklisted cookie.
+        
+        @param cookie Flash cookie to be tested
+        @type FlashCookie
+        @return flag indicating a blacklisted cookie
+        @rtype bool
+        """
+        return cookie.origin in \
+            Preferences.getWebBrowser("FlashCookiesBlacklist")
+    
+    def __isWhitelisted(self, cookie):
+        """
+        Private method to check for a whitelisted cookie.
+        
+        @param cookie Flash cookie to be tested
+        @type FlashCookie
+        @return flag indicating a whitelisted cookie
+        @rtype bool
+        """
+        return cookie.origin in \
+            Preferences.getWebBrowser("FlashCookiesWhitelist")
+    
+    def __removeAllButWhitelisted(self):
+        """
+        Private method to remove all non-whitelisted cookies.
+        """
+        for cookie in self.__flashCookies[:]:
+            if not self.__isWhitelisted(cookie):
+                self.removeCookie(cookie)
+    
+    def removeAllCookies(self):
+        """
+        Public method to remove all flash cookies.
+        """
+        for cookie in self.__flashCookies[:]:
+            self.removeCookie(cookie)
+        self.clearNewOrigins()
+        self.clearCache()
+    
+    def __sharedObjectDirName(self):
+        """
+        Private slot to determine the path of the shared data objects.
+        
+        @return path of the shared data objects
+        @rtype str
+        """
+        if "macromedia" in self.flashPlayerDataPath().lower() or \
+                "/.gnash" not in self.flashPlayerDataPath().lower():
+            return "/#SharedObjects/"
+        else:
+            return "/SharedObjects/"
+    
+    def flashPlayerDataPath(self):
+        """
+        Public method to get the Flash Player data path.
+        
+        @return Flash Player data path
+        @rtype str
+        """
+        return Preferences.getWebBrowser("FlashCookiesDataPath")
+    
+    def preferencesChanged(self):
+        """
+        Public slot to handle a change of preferences.
+        """
+        self.__startStopTimer()
+    
+    def removeCookie(self, cookie):
+        """
+        Public method to remove a cookie of the list of cached cookies.
+        
+        @param cookie Flash cookie to be removed
+        @type FlashCookie
+        """
+        if cookie in self.__flashCookies:
+            self.__flashCookies.remove(cookie)
+            shutil.rmtree(cookie.path, True)
+    
+    def __autoRefresh(self):
+        """
+        Private slot to refresh the list of cookies.
+        """
+        if self.__flashCookieManagerDialog and \
+                self.__flashCookieManagerDialog.isVisible():
+            return
+        
+        oldFlashCookies = self.__flashCookies[:]
+        self.__loadFlashCookies()
+        newCookieList = []
+        
+        for cookie in self.__flashCookies[:]:
+            if self.__isBlacklisted(cookie):
+                self.removeCookie(cookie)
+                continue
+            
+            if self.__isWhitelisted(cookie):
+                continue
+            
+            newCookie = True
+            for oldCookie in oldFlashCookies:
+                if (oldCookie.path + oldCookie.name ==
+                        cookie.path + cookie.name):
+                    newCookie = False
+                    break
+            
+            if newCookie:
+                newCookieList.append(cookie.path + "/" + cookie.name)
+        
+        if newCookieList and Preferences.getWebBrowser("FlashCookieNotify"):
+            self.__newCookiesList.extend(newCookieList)
+            win = WebBrowser.WebBrowserWindow.WebBrowserWindow.mainWindow()
+            if win is None:
+                return
+            
+            view = win.currentBrowser()
+            if view is None:
+                return
+            
+            from .FlashCookieNotification import FlashCookieNotification
+            notification = FlashCookieNotification(
+                view, self, len(newCookieList))
+            notification.show()
+    
+    def showFlashCookieManagerDialog(self):
+        """
+        Public method to show the Flash cookies management dialog.
+        """
+        if self.__flashCookieManagerDialog is None:
+            from .FlashCookieManagerDialog import FlashCookieManagerDialog
+            self.__flashCookieManagerDialog = FlashCookieManagerDialog(self)
+        
+        self.__flashCookieManagerDialog.refreshView()
+        self.__flashCookieManagerDialog.showPage(0)
+        self.__flashCookieManagerDialog.show()
+        self.__flashCookieManagerDialog.raise_()
+    
+    def __startStopTimer(self):
+        """
+        Private slot to start or stop the auto refresh timer.
+        """
+        if Preferences.getWebBrowser("FlashCookieAutoRefresh"):
+            if not self.__timer.isActive():
+                if not bool(self.__flashCookies):
+                    self.__loadFlashCookies()
+                
+                self.__timer.start()
+        else:
+            self.__timer.stop()
+    
+    def __loadFlashCookies(self):
+        """
+        Private slot to load the Flash cookies to be cached.
+        """
+        self.__flashCookies = []
+        self.__loadFlashCookiesFromPath(self.flashPlayerDataPath())
+    
+    def __loadFlashCookiesFromPath(self, path):
+        """
+        Private slot to load the Flash cookies from a path.
+        
+        @param path Flash cookies path
+        @type str
+        """
+        if path.endswith("#AppContainer"):
+            # specific to IE and Windows
+            return
+        
+        path = path.replace("\\", "/")
+        solDir = QDir(path)
+        entryList = solDir.entryList()
+        for entry in entryList:
+            if entry == "." or entry == "..":
+                continue
+            entryInfo = QFileInfo(path + "/" + entry)
+            if entryInfo.isDir():
+                self.__loadFlashCookiesFromPath(entryInfo.filePath())
+            else:
+                self.__insertFlashCookie(entryInfo.filePath())
+    
+    def __insertFlashCookie(self, path):
+        """
+        Private method to insert a Flash cookie into the cache.
+        
+        @param path Flash cookies path
+        @type str
+        """
+        solFile = QFile(path)
+        if not solFile.open(QFile.ReadOnly):
+            return
+        
+        dataStr = ""
+        data = bytes(solFile.readAll())
+        if data:
+            try:
+                reader = FlashCookieReader()
+                reader.setBytes(data)
+                reader.parse()
+                dataStr = reader.toString()
+            except FlashCookieReaderError as err:
+                dataStr = err.msg
+        
+        solFileInfo = QFileInfo(solFile)
+        
+        cookie = FlashCookie()
+        cookie.contents = dataStr
+        cookie.name = solFileInfo.fileName()
+        cookie.path = solFileInfo.canonicalPath()
+        cookie.size = int(solFile.size())
+        cookie.lastModified = solFileInfo.lastModified()
+        cookie.origin = self.__extractOriginFrom(path)
+        
+        self.__flashCookies.append(cookie)
+    
+    def __extractOriginFrom(self, path):
+        """
+        Private method to extract the cookie origin given its file name.
+        
+        @param path file name of the cookie file
+        @type str
+        @return cookie origin
+        @rtype str
+        """
+        origin = path
+        if path.startswith(
+                self.flashPlayerDataPath() + self.__sharedObjectDirName()):
+            origin = origin.replace(
+                self.flashPlayerDataPath() + self.__sharedObjectDirName(), "")
+            if "/" in origin:
+                origin = origin.split("/", 1)[1]
+        elif path.startswith(
+            self.flashPlayerDataPath() +
+                "/macromedia.com/support/flashplayer/sys/"):
+            origin = origin.replace(
+                self.flashPlayerDataPath() +
+                "/macromedia.com/support/flashplayer/sys/", "")
+            if origin == "settings.sol":
+                return self.tr("!default")
+            elif origin.startswith("#"):
+                origin = origin[1:]
+        else:
+            origin = ""
+        
+        index = origin.find("/")
+        if index == -1:
+            return self.tr("!other")
+        
+        origin = origin[:index]
+        if origin in ["localhost", "local"]:
+            origin = "!localhost"
+        
+        return origin
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/FlashCookieManager/FlashCookieManagerDialog.py	Sun Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,194 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a delegate for the special list widget for GreaseMonkey
+scripts.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import Qt, QSize, QRect, qVersion
+from PyQt5.QtGui import QFontMetrics, QPalette, QFont
+from PyQt5.QtWidgets import QStyle, QStyledItemDelegate, QApplication
+if qVersion() >= "5.0.0":
+    from PyQt5.QtWidgets import QStyleOptionViewItem
+else:
+    from PyQt5.QtWidgets import QStyleOptionViewItemV4 as QStyleOptionViewItem
+
+import UI.PixmapCache
+import Globals
+
+
+class GreaseMonkeyConfigurationListDelegate(QStyledItemDelegate):
+    """
+    Class implementing a delegate for the special list widget for GreaseMonkey
+    scripts.
+    """
+    IconSize = 32
+    RemoveIconSize = 16
+    CheckBoxSize = 18
+    MinPadding = 5
+    ItemWidth = 200
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object (QObject)
+        """
+        super(GreaseMonkeyConfigurationListDelegate, self).__init__(parent)
+        
+        self.__removePixmap = \
+            UI.PixmapCache.getIcon("greaseMonkeyTrash.png").pixmap(
+                GreaseMonkeyConfigurationListDelegate.RemoveIconSize)
+        self.__rowHeight = 0
+        self.__padding = 0
+    
+    def padding(self):
+        """
+        Public method to get the padding used.
+        
+        @return padding used (integer)
+        """
+        return self.__padding
+    
+    def paint(self, painter, option, index):
+        """
+        Public method to paint the specified list item.
+        
+        @param painter painter object to paint to (QPainter)
+        @param option style option used for painting (QStyleOptionViewItem)
+        @param index model index of the item (QModelIndex)
+        """
+        opt = QStyleOptionViewItem(option)
+        self.initStyleOption(opt, index)
+        
+        widget = opt.widget
+        style = widget.style() if widget is not None else QApplication.style()
+        height = opt.rect.height()
+        center = height // 2 + opt.rect.top()
+        
+        # Prepare title font
+        titleFont = QFont(opt.font)
+        titleFont.setBold(True)
+        titleFont.setPointSize(titleFont.pointSize() + 1)
+        
+        titleMetrics = QFontMetrics(titleFont)
+        if Globals.isWindowsPlatform():
+            colorRole = QPalette.Text
+        else:
+            colorRole = QPalette.HighlightedText \
+                if opt.state & QStyle.State_Selected else QPalette.Text
+        
+        leftPos = self.__padding
+        rightPos = opt.rect.right() - self.__padding - \
+            GreaseMonkeyConfigurationListDelegate.RemoveIconSize
+        
+        # Draw background
+        style.drawPrimitive(QStyle.PE_PanelItemViewItem, opt, painter, widget)
+        
+        # Draw checkbox
+        checkBoxYPos = center - \
+            GreaseMonkeyConfigurationListDelegate.CheckBoxSize // 2
+        opt2 = QStyleOptionViewItem(opt)
+        if opt2.checkState == Qt.Checked:
+            opt2.state |= QStyle.State_On
+        else:
+            opt2.state |= QStyle.State_Off
+        styleCheckBoxRect = style.subElementRect(
+            QStyle.SE_ViewItemCheckIndicator, opt2, widget)
+        opt2.rect = QRect(
+            leftPos, checkBoxYPos,
+            styleCheckBoxRect.width(), styleCheckBoxRect.height())
+        style.drawPrimitive(QStyle.PE_IndicatorViewItemCheck, opt2, painter,
+                            widget)
+        leftPos = opt2.rect.right() + self.__padding
+        
+        # Draw icon
+        iconYPos = center - GreaseMonkeyConfigurationListDelegate.IconSize // 2
+        iconRect = QRect(leftPos, iconYPos,
+                         GreaseMonkeyConfigurationListDelegate.IconSize,
+                         GreaseMonkeyConfigurationListDelegate.IconSize)
+        pixmap = index.data(Qt.DecorationRole).pixmap(
+            GreaseMonkeyConfigurationListDelegate.IconSize)
+        painter.drawPixmap(iconRect, pixmap)
+        leftPos = iconRect.right() + self.__padding
+        
+        # Draw script name
+        name = index.data(Qt.DisplayRole)
+        leftTitleEdge = leftPos + 2
+        rightTitleEdge = rightPos - self.__padding
+        leftPosForVersion = titleMetrics.width(name) + self.__padding
+        nameRect = QRect(leftTitleEdge, opt.rect.top() + self.__padding,
+                         rightTitleEdge - leftTitleEdge, titleMetrics.height())
+        painter.setFont(titleFont)
+        style.drawItemText(painter, nameRect, Qt.AlignLeft, opt.palette, True,
+                           name, colorRole)
+        
+        # Draw version
+        version = index.data(Qt.UserRole)
+        versionRect = QRect(
+            nameRect.x() + leftPosForVersion, nameRect.y(),
+            rightTitleEdge - leftTitleEdge, titleMetrics.height())
+        versionFont = titleFont
+        painter.setFont(versionFont)
+        style.drawItemText(painter, versionRect, Qt.AlignLeft, opt.palette,
+                           True, version, colorRole)
+        
+        # Draw description
+        infoYPos = nameRect.bottom() + opt.fontMetrics.leading()
+        infoRect = QRect(
+            nameRect.x(), infoYPos,
+            nameRect.width(), opt.fontMetrics.height())
+        info = opt.fontMetrics.elidedText(
+            index.data(Qt.UserRole + 1), Qt.ElideRight, infoRect.width())
+        painter.setFont(opt.font)
+        style.drawItemText(painter, infoRect, Qt.AlignLeft | Qt.TextSingleLine,
+                           opt.palette, True, info, colorRole)
+        
+        # Draw remove button
+        removeIconYPos = center - \
+            GreaseMonkeyConfigurationListDelegate.RemoveIconSize // 2
+        removeIconRect = QRect(
+            rightPos, removeIconYPos,
+            GreaseMonkeyConfigurationListDelegate.RemoveIconSize,
+            GreaseMonkeyConfigurationListDelegate.RemoveIconSize)
+        painter.drawPixmap(removeIconRect, self.__removePixmap)
+    
+    def sizeHint(self, option, index):
+        """
+        Public method to get a size hint for the specified list item.
+        
+        @param option style option used for painting (QStyleOptionViewItem)
+        @param index model index of the item (QModelIndex)
+        @return size hint (QSize)
+        """
+        if not self.__rowHeight:
+            opt = QStyleOptionViewItem(option)
+            self.initStyleOption(opt, index)
+            
+            widget = opt.widget
+            style = widget.style() if widget is not None \
+                else QApplication.style()
+            padding = style.pixelMetric(QStyle.PM_FocusFrameHMargin) + 1
+            
+            titleFont = opt.font
+            titleFont.setBold(True)
+            titleFont.setPointSize(titleFont.pointSize() + 1)
+            
+            self.__padding = padding \
+                if padding > GreaseMonkeyConfigurationListDelegate.MinPadding \
+                else GreaseMonkeyConfigurationListDelegate.MinPadding
+            
+            titleMetrics = QFontMetrics(titleFont)
+            
+            self.__rowHeight = 2 * self.__padding + \
+                opt.fontMetrics.leading() + \
+                opt.fontMetrics.height() + \
+                titleMetrics.height()
+        
+        return QSize(GreaseMonkeyConfigurationListDelegate.ItemWidth,
+                     self.__rowHeight)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/GreaseMonkey/GreaseMonkeyConfiguration/GreaseMonkeyConfigurationListWidget.py	Sun Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,181 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the downloader for GreaseMonkey scripts.
+"""
+
+from __future__ import unicode_literals
+
+import os
+
+from PyQt5.QtCore import pyqtSignal, QObject, QSettings, QRegExp, QUrl
+from PyQt5.QtWidgets import QDialog
+from PyQt5.QtNetwork import QNetworkReply
+
+from E5Gui import E5MessageBox
+
+from WebBrowser.WebBrowserWindow import WebBrowserWindow
+from WebBrowser.Network.FollowRedirectReply import FollowRedirectReply
+
+
+class GreaseMonkeyDownloader(QObject):
+    """
+    Class implementing the downloader for GreaseMonkey scripts.
+    """
+    finished = pyqtSignal()
+    
+    def __init__(self, url, manager):
+        """
+        Constructor
+        
+        @param url URL to download script from
+        @type QUrl
+        @param manager reference to the GreaseMonkey manager
+        @type GreaseMonkeyManager
+        """
+        super(GreaseMonkeyDownloader, self).__init__()
+        
+        self.__manager = manager
+        
+        self.__reply = FollowRedirectReply(
+            url, WebBrowserWindow.networkAccessManager())
+        self.__reply.finished.connect(self.__scriptDownloaded)
+        
+        self.__fileName = ""
+        self.__requireUrls = []
+    
+    def __scriptDownloaded(self):
+        """
+        Private slot to handle the finished download of a script.
+        """
+        if self.sender() != self.__reply:
+            self.finished.emit()
+            return
+        
+        response = bytes(self.__reply.readAll()).decode()
+        
+        if self.__reply.error() == QNetworkReply.NoError and \
+           "// ==UserScript==" in response:
+            from WebBrowser.Tools import WebBrowserTools
+            filePath = os.path.join(
+                self.__manager.scriptsDirectory(),
+                WebBrowserTools.getFileNameFromUrl(self.__reply.url()))
+            self.__fileName = WebBrowserTools.ensureUniqueFilename(filePath)
+            
+            try:
+                f = open(self.__fileName, "w", encoding="utf-8")
+            except (IOError, OSError) as err:
+                E5MessageBox.critical(
+                    None,
+                    self.tr("GreaseMonkey Download"),
+                    self.tr(
+                        """<p>The file <b>{0}</b> could not be opened"""
+                        """ for writing.<br/>Reason: {1}</p>""").format(
+                        self.__fileName, str(err)))
+                self.finished.emit()
+                return
+            f.write(response)
+            f.close()
+            
+            settings = QSettings(
+                os.path.join(self.__manager.requireScriptsDirectory(),
+                             "requires.ini"),
+                QSettings.IniFormat)
+            settings.beginGroup("Files")
+            
+            rx = QRegExp("@require(.*)\\n")
+            rx.setMinimal(True)
+            rx.indexIn(response)
+            
+            for i in range(1, rx.captureCount() + 1):
+                url = rx.cap(i).strip()
+                if url and not settings.contains(url):
+                    self.__requireUrls.append(QUrl(url))
+        
+        self.__reply.deleteLater()
+        self.__reply = None
+        
+        self.__downloadRequires()
+    
+    def __requireDownloaded(self):
+        """
+        Private slot to handle the finished download of a required script.
+        """
+        if self.sender() != self.__reply:
+            self.finished.emit()
+            return
+        
+        response = bytes(self.__reply.readAll()).decode()
+        
+        if self.__reply.error() == QNetworkReply.NoError and response:
+            from WebBrowser.Tools import WebBrowserTools
+            filePath = os.path.join(self.__manager.requireScriptsDirectory(),
+                                    "require.js")
+            fileName = WebBrowserTools.ensureUniqueFilename(filePath, "{0}")
+            
+            try:
+                f = open(fileName, "w", encoding="utf-8")
+            except (IOError, OSError) as err:
+                E5MessageBox.critical(
+                    None,
+                    self.tr("GreaseMonkey Download"),
+                    self.tr(
+                        """<p>The file <b>{0}</b> could not be opened"""
+                        """ for writing.<br/>Reason: {1}</p>""").format(
+                        fileName, str(err)))
+                self.finished.emit()
+                return
+            f.write(response)
+            f.close()
+            
+            settings = QSettings(
+                os.path.join(self.__manager.requireScriptsDirectory(),
+                             "requires.ini"),
+                QSettings.IniFormat)
+            settings.beginGroup("Files")
+            settings.setValue(self.__reply.originalUrl().toString(), fileName)
+        
+        self.__reply.deleteLater()
+        self.__reply = None
+        
+        self.__downloadRequires()
+    
+    def __downloadRequires(self):
+        """
+        Private slot to initiate the download of required scripts.
+        """
+        if self.__requireUrls:
+            self.__reply = FollowRedirectReply(
+                self.__requireUrls.pop(0),
+                WebBrowserWindow.networkAccessManager())
+            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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,292 @@
+# -*- 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, QObject, QTimer, QFile, QDir, QSettings
+
+import Utilities
+import Preferences
+
+from WebBrowser.WebBrowserWindow import WebBrowserWindow
+from .GreaseMonkeyUrlInterceptor import GreaseMonkeyUrlInterceptor
+
+
+# TODO: GreaseMonkey: needs testing with Qt 5.6
+class GreaseMonkeyManager(QObject):
+    """
+    Class implementing the manager for GreaseMonkey scripts.
+    """
+    scriptsChanged = pyqtSignal()
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object (QObject)
+        """
+        super(GreaseMonkeyManager, self).__init__(parent)
+        
+        self.__disabledScripts = []
+        self.__scripts = []
+        self.__downloaders = []
+        
+        self.__interceptor = GreaseMonkeyUrlInterceptor(self)
+        WebBrowserWindow.networkManager().installUrlInterceptor(
+            self.__interceptor)
+        
+        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
+        """
+        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 Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,395 @@
+# -*- 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, path):
+        """
+        Private method to parse the given script and populate the data
+        structure.
+        
+        @param path path of the Javascript file (string)
+        """
+        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(path, "r", encoding="utf-8")
+            fileData = f.read()
+            f.close()
+        except (IOError, OSError):
+            # silently ignore because it shouldn't happen
+            return
+        
+        if self.__fileName not in self.__fileWatcher.files():
+            self.__fileWatcher.addPath(self.__fileName)
+        
+        rx = QRegExp("// ==UserScript==(.*)// ==/UserScript==")
+        rx.indexIn(fileData)
+        metaDataBlock = rx.cap(1).strip()
+        
+        if metaDataBlock == "":
+            # invalid script file
+            return
+        
+        requireList = []
+        for line in metaDataBlock.splitlines():
+            if not line.strip():
+                continue
+            
+            if not line.startswith("// @"):
+                continue
+            
+            line = line[3:].replace("\t", " ")
+            index = line.find(" ")
+            if index < 0:
+                continue
+            
+            key = line[:index].strip()
+            value = line[index + 1:].strip()
+            
+            # Ignored values: @resource, @unwrap
+            
+            if not key or not value:
+                continue
+            
+            if key == "@name":
+                self.__name = value
+            
+            elif key == "@namespace":
+                self.__namespace = value
+            
+            elif key == "@description":
+                self.__description = value
+            
+            elif key == "@version":
+                self.__version = value
+            
+##            elif key == "@updateURL":
+##                self.__downloadUrl = QUrl(value)
+##            
+            elif key in ["@include", "@match"]:
+                self.__include.append(value)
+            
+            elif key in ["@exclude", "@exclude_match"]:
+                self.__exclude.append(value)
+            
+            elif key == "@require":
+                requireList.append(value)
+            
+            elif key == "@run-at":
+                if value == "document-end":
+                    self.__startAt = GreaseMonkeyScript.DocumentEnd
+                elif value == "document-start":
+                    self.__startAt = GreaseMonkeyScript.DocumentStart
+                elif value == "document-idle":
+                    self.__startAt = GreaseMonkeyScript.DocumentIdle
+            
+            elif key == "@downloadURL" and self.__downloadUrl.isEmpty():
+                self.__downloadUrl = QUrl(value)
+            
+            elif key == "@updateURL" and self.__updateUrl.isEmpty():
+                self.__updateUrl = QUrl(value)
+        
+        if not self.__include:
+            self.__include.append("*")
+        
+        nspace = bytes(QCryptographicHash.hash(
+            QByteArray(self.fullName().encode("utf-8")),
+            QCryptographicHash.Md4).toHex()).decode("ascii")
+        valuesScript = values_js.format(nspace)
+        runCheck = """
+            for (var value of {0}) {{
+                var re = new RegExp(value);
+                if (re.test(window.location.href)) {{
+                    return;
+                }}
+            }}
+            __eric_includes = false;
+            for (var value of {1}) {{
+                var re = new RegExp(value);
+                if (re.test(window.location.href)) {{
+                    __eric_includes = true;
+                    break;
+                }}
+            }}
+            if (!__eric_includes) {{
+                return;
+            }}
+            delete __eric_includes;""".format(
+                self.__toJavaScriptList(self.__exclude[:]),
+                self.__toJavaScriptList(self.__include[:])
+            )
+        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 Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,39 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a handler for GreaseMonkey related URLs.
+"""
+
+from __future__ import unicode_literals
+
+from ..Network.UrlInterceptor import UrlInterceptor
+
+
+class GreaseMonkeyUrlInterceptor(UrlInterceptor):
+    """
+    Class implementing a handler for GreaseMonkey related URLs.
+    """
+    def __init__(self, manager):
+        """
+        Constructor
+        
+        @param manager reference to the GreaseMonkey manager
+        @type GreaseMonkeyManager
+        """
+        super(GreaseMonkeyUrlInterceptor, self).__init__(manager)
+        
+        self.__manager = manager
+    
+    def interceptRequest(self, info):
+        """
+        Public method to handle a GreaseMonkey request.
+        
+        @param info request info object
+        @type QWebEngineUrlRequestInfo
+        """
+        if info.requestUrl().toString.endswith(".user.js"):
+            self.__manager.downloadScript(info.requestUrl())
+            info.block(True)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/GreaseMonkey/__init__.py	Sun Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,506 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the history manager.
+"""
+
+from __future__ import unicode_literals
+
+import os
+
+from PyQt5.QtCore import pyqtSignal, pyqtSlot, QFileInfo, QDateTime, QDate, \
+    QTime, QUrl, QTimer, QFile, QIODevice, QByteArray, QDataStream, \
+    QTemporaryFile, QObject
+
+from E5Gui import E5MessageBox
+
+from Utilities.AutoSaver import AutoSaver
+import Utilities
+import Preferences
+
+HISTORY_VERSION = 42
+
+
+class HistoryEntry(object):
+    """
+    Class implementing a history entry.
+    """
+    def __init__(self, url=None, dateTime=None, title=None):
+        """
+        Constructor
+        
+        @param url URL of the history entry (string)
+        @param dateTime date and time this entry was created (QDateTime)
+        @param title title string for the history entry (string)
+        """
+        self.url = url and url or ""
+        self.dateTime = dateTime and dateTime or QDateTime()
+        self.title = title and title or ""
+    
+    def __eq__(self, other):
+        """
+        Special method determining equality.
+        
+        @param other reference to the history entry to compare against
+            (HistoryEntry)
+        @return flag indicating equality (boolean)
+        """
+        return other.title == self.title and \
+            other.url == self.url and \
+            other.dateTime == self.dateTime
+    
+    def __lt__(self, other):
+        """
+        Special method determining less relation.
+        
+        Note: History is sorted in reverse order by date and time
+        
+        @param other reference to the history entry to compare against
+            (HistoryEntry)
+        @return flag indicating less (boolean)
+        """
+        return self.dateTime > other.dateTime
+    
+    def userTitle(self):
+        """
+        Public method to get the title of the history entry.
+        
+        @return title of the entry (string)
+        """
+        if not self.title:
+            page = QFileInfo(QUrl(self.url).path()).fileName()
+            if page:
+                return page
+            return self.url
+        return self.title
+
+
+# TODO: Enhancement: Only one entry per URL for latest visit and add no. of visits
+class HistoryManager(QObject):
+    """
+    Class implementing the history manager.
+    
+    @signal historyCleared() emitted after the history has been cleared
+    @signal historyReset() emitted after the history has been reset
+    @signal entryAdded(HistoryEntry) emitted after a history entry has been
+        added
+    @signal entryRemoved(HistoryEntry) emitted after a history entry has been
+        removed
+    @signal entryUpdated(int) emitted after a history entry has been updated
+    @signal historySaved() emitted after the history was saved
+    """
+    historyCleared = pyqtSignal()
+    historyReset = pyqtSignal()
+    entryAdded = pyqtSignal(HistoryEntry)
+    entryRemoved = pyqtSignal(HistoryEntry)
+    entryUpdated = pyqtSignal(int)
+    historySaved = pyqtSignal()
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object (QObject)
+        """
+        super(HistoryManager, self).__init__(parent)
+        
+        self.__saveTimer = AutoSaver(self, self.save)
+        self.__daysToExpire = Preferences.getWebBrowser("HistoryLimit")
+        self.__history = []
+        self.__lastSavedUrl = ""
+        
+        self.__expiredTimer = QTimer(self)
+        self.__expiredTimer.setSingleShot(True)
+        self.__expiredTimer.timeout.connect(self.__checkForExpired)
+        
+        self.__frequencyTimer = QTimer(self)
+        self.__frequencyTimer.setSingleShot(True)
+        self.__frequencyTimer.timeout.connect(self.__refreshFrequencies)
+        
+        self.entryAdded.connect(self.__saveTimer.changeOccurred)
+        self.entryRemoved.connect(self.__saveTimer.changeOccurred)
+        
+        self.__load()
+        
+        from .HistoryModel import HistoryModel
+        from .HistoryFilterModel import HistoryFilterModel
+        from .HistoryTreeModel import HistoryTreeModel
+        
+        self.__historyModel = HistoryModel(self, self)
+        self.__historyFilterModel = \
+            HistoryFilterModel(self.__historyModel, self)
+        self.__historyTreeModel = \
+            HistoryTreeModel(self.__historyFilterModel, self)
+        
+        self.__startFrequencyTimer()
+    
+    def close(self):
+        """
+        Public method to close the history manager.
+        """
+        # remove history items on application exit
+        if self.__daysToExpire == -2:
+            self.clear()
+        self.__saveTimer.saveIfNeccessary()
+    
+    def history(self):
+        """
+        Public method to return the history.
+        
+        @return reference to the list of history entries (list of HistoryEntry)
+        """
+        return self.__history[:]
+    
+    def setHistory(self, history, loadedAndSorted=False):
+        """
+        Public method to set a new history.
+        
+        @param history reference to the list of history entries to be set
+            (list of HistoryEntry)
+        @param loadedAndSorted flag indicating that the list is sorted
+            (boolean)
+        """
+        self.__history = history[:]
+        if not loadedAndSorted:
+            self.__history.sort()
+        
+        self.__checkForExpired()
+        
+        if loadedAndSorted:
+            try:
+                self.__lastSavedUrl = self.__history[0].url
+            except IndexError:
+                self.__lastSavedUrl = ""
+        else:
+            self.__lastSavedUrl = ""
+            self.__saveTimer.changeOccurred()
+        self.historyReset.emit()
+    
+    def addHistoryEntry(self, view):
+        """
+        Public method to add a history entry.
+        
+        @param view reference to the view to add an entry for
+        @type WebBrowserView
+        """
+        import WebBrowser.WebBrowserWindow
+        if WebBrowser.WebBrowserWindow.WebBrowserWindow.isPrivate():
+            return
+        
+        url = view.url()
+        title = view.title()
+        
+        if url.scheme() not in ["eric", "about", "data"]:
+            if url.password():
+                # don't save the password in the history
+                url.setPassword("")
+            if url.host():
+                url.setHost(url.host().lower())
+            itm = HistoryEntry(url.toString(),
+                               QDateTime.currentDateTime(),
+                               title)
+            self.__history.insert(0, itm)
+            self.entryAdded.emit(itm)
+            if len(self.__history) == 1:
+                self.__checkForExpired()
+    
+    def updateHistoryEntry(self, url, title):
+        """
+        Public method to update a history entry.
+        
+        @param url URL of the entry to update (string)
+        @param title title of the entry to update (string)
+        """
+        cleanurl = QUrl(url)
+        if cleanurl.scheme() not in ["eric", "about"]:
+            for index in range(len(self.__history)):
+                if url == self.__history[index].url:
+                    self.__history[index].title = title
+                    self.__saveTimer.changeOccurred()
+                    if not self.__lastSavedUrl:
+                        self.__lastSavedUrl = self.__history[index].url
+                    self.entryUpdated.emit(index)
+                    break
+    
+    def removeHistoryEntry(self, url, title=""):
+        """
+        Public method to remove a history entry.
+        
+        @param url URL of the entry to remove (QUrl)
+        @param title title of the entry to remove (string)
+        """
+        for index in range(len(self.__history)):
+            if url == QUrl(self.__history[index].url) and \
+               (not title or title == self.__history[index].title):
+                itm = self.__history[index]
+                self.__lastSavedUrl = ""
+                self.__history.remove(itm)
+                self.entryRemoved.emit(itm)
+                break
+    
+    def historyModel(self):
+        """
+        Public method to get a reference to the history model.
+        
+        @return reference to the history model (HistoryModel)
+        """
+        return self.__historyModel
+    
+    def historyFilterModel(self):
+        """
+        Public method to get a reference to the history filter model.
+        
+        @return reference to the history filter model (HistoryFilterModel)
+        """
+        return self.__historyFilterModel
+    
+    def historyTreeModel(self):
+        """
+        Public method to get a reference to the history tree model.
+        
+        @return reference to the history tree model (HistoryTreeModel)
+        """
+        return self.__historyTreeModel
+    
+    def __checkForExpired(self):
+        """
+        Private slot to check entries for expiration.
+        """
+        if self.__daysToExpire < 0 or len(self.__history) == 0:
+            return
+        
+        now = QDateTime.currentDateTime()
+        nextTimeout = 0
+        
+        while self.__history:
+            checkForExpired = QDateTime(self.__history[-1].dateTime)
+            checkForExpired.setDate(
+                checkForExpired.date().addDays(self.__daysToExpire))
+            if now.daysTo(checkForExpired) > 7:
+                nextTimeout = 7 * 86400
+            else:
+                nextTimeout = now.secsTo(checkForExpired)
+            if nextTimeout > 0:
+                break
+            
+            itm = self.__history.pop(-1)
+            self.__lastSavedUrl = ""
+            self.entryRemoved.emit(itm)
+        self.__saveTimer.saveIfNeccessary()
+        
+        if nextTimeout > 0:
+            self.__expiredTimer.start(nextTimeout * 1000)
+    
+    def daysToExpire(self):
+        """
+        Public method to get the days for entry expiration.
+        
+        @return days for entry expiration (integer)
+        """
+        return self.__daysToExpire
+    
+    def setDaysToExpire(self, limit):
+        """
+        Public method to set the days for entry expiration.
+        
+        @param limit days for entry expiration (integer)
+        """
+        if self.__daysToExpire == limit:
+            return
+        
+        self.__daysToExpire = limit
+        self.__checkForExpired()
+        self.__saveTimer.changeOccurred()
+    
+    def preferencesChanged(self):
+        """
+        Public method to indicate a change of preferences.
+        """
+        self.setDaysToExpire(Preferences.getWebBrowser("HistoryLimit"))
+    
+    @pyqtSlot()
+    def clear(self, period=0):
+        """
+        Public slot to clear the complete history.
+        
+        @param period history period in milliseconds to be cleared (integer)
+        """
+        if period == 0:
+            self.__history = []
+            self.historyReset.emit()
+        else:
+            breakMS = QDateTime.currentMSecsSinceEpoch() - period
+            while self.__history and \
+                (QDateTime(self.__history[0].dateTime).toMSecsSinceEpoch() >
+                 breakMS):
+                itm = self.__history.pop(0)
+                self.entryRemoved.emit(itm)
+        self.__lastSavedUrl = ""
+        self.__saveTimer.changeOccurred()
+        self.__saveTimer.saveIfNeccessary()
+        self.historyCleared.emit()
+    
+    def getFileName(self):
+        """
+        Public method to get the file name of the history file.
+        
+        @return name of the history file (string)
+        """
+        return os.path.join(Utilities.getConfigDir(), "web_browser", "history")
+    
+    def reload(self):
+        """
+        Public method to reload the history.
+        """
+        self.__load()
+    
+    def __load(self):
+        """
+        Private method to load the saved history entries from disk.
+        """
+        historyFile = QFile(self.getFileName())
+        if not historyFile.exists():
+            return
+        if not historyFile.open(QIODevice.ReadOnly):
+            E5MessageBox.warning(
+                None,
+                self.tr("Loading History"),
+                self.tr(
+                    """<p>Unable to open history file <b>{0}</b>.<br/>"""
+                    """Reason: {1}</p>""")
+                .format(historyFile.fileName, historyFile.errorString()))
+            return
+        
+        history = []
+        
+        # double check, that the history file is sorted as it is read
+        needToSort = False
+        lastInsertedItem = HistoryEntry()
+        data = QByteArray(historyFile.readAll())
+        stream = QDataStream(data, QIODevice.ReadOnly)
+        stream.setVersion(QDataStream.Qt_4_6)
+        while not stream.atEnd():
+            ver = stream.readUInt32()
+            if ver != HISTORY_VERSION:
+                continue
+            itm = HistoryEntry()
+            itm.url = Utilities.readStringFromStream(stream)
+            stream >> itm.dateTime
+            itm.title = Utilities.readStringFromStream(stream)
+            
+            if not itm.dateTime.isValid():
+                continue
+            
+            if itm == lastInsertedItem:
+                if not lastInsertedItem.title and len(history) > 0:
+                    history[0].title = itm.title
+                continue
+            
+            if not needToSort and history and lastInsertedItem < itm:
+                needToSort = True
+            
+            history.insert(0, itm)
+            lastInsertedItem = itm
+        historyFile.close()
+        
+        if needToSort:
+            history.sort()
+        
+        self.setHistory(history, True)
+        
+        # if the history had to be sorted, rewrite the history sorted
+        if needToSort:
+            self.__lastSavedUrl = ""
+            self.__saveTimer.changeOccurred()
+    
+    def save(self):
+        """
+        Public slot to save the history entries to disk.
+        """
+        historyFile = QFile(self.getFileName())
+        if not historyFile.exists():
+            self.__lastSavedUrl = ""
+        
+        saveAll = self.__lastSavedUrl == ""
+        first = len(self.__history) - 1
+        if not saveAll:
+            # find the first one to save
+            for index in range(len(self.__history)):
+                if self.__history[index].url == self.__lastSavedUrl:
+                    first = index - 1
+                    break
+        if first == len(self.__history) - 1:
+            saveAll = True
+        
+        if saveAll:
+            # use a temporary file when saving everything
+            f = QTemporaryFile()
+            f.setAutoRemove(False)
+            opened = f.open()
+        else:
+            f = historyFile
+            opened = f.open(QIODevice.Append)
+        
+        if not opened:
+            E5MessageBox.warning(
+                None,
+                self.tr("Saving History"),
+                self.tr(
+                    """<p>Unable to open history file <b>{0}</b>.<br/>"""
+                    """Reason: {1}</p>""")
+                .format(f.fileName(), f.errorString()))
+            return
+        
+        for index in range(first, -1, -1):
+            data = QByteArray()
+            stream = QDataStream(data, QIODevice.WriteOnly)
+            stream.setVersion(QDataStream.Qt_4_6)
+            itm = self.__history[index]
+            stream.writeUInt32(HISTORY_VERSION)
+            stream.writeString(itm.url.encode("utf-8"))
+            stream << itm.dateTime
+            stream.writeString(itm.title.encode('utf-8'))
+            f.write(data)
+        
+        f.close()
+        if saveAll:
+            if historyFile.exists() and not historyFile.remove():
+                E5MessageBox.warning(
+                    None,
+                    self.tr("Saving History"),
+                    self.tr(
+                        """<p>Error removing old history file <b>{0}</b>."""
+                        """<br/>Reason: {1}</p>""")
+                    .format(historyFile.fileName(),
+                            historyFile.errorString()))
+            if not f.copy(historyFile.fileName()):
+                E5MessageBox.warning(
+                    None,
+                    self.tr("Saving History"),
+                    self.tr(
+                        """<p>Error moving new history file over old one """
+                        """(<b>{0}</b>).<br/>Reason: {1}</p>""")
+                    .format(historyFile.fileName(), f.errorString()))
+        self.historySaved.emit()
+        try:
+            self.__lastSavedUrl = self.__history[0].url
+        except IndexError:
+            self.__lastSavedUrl = ""
+    
+    def __refreshFrequencies(self):
+        """
+        Private slot to recalculate the refresh frequencies.
+        """
+        self.__historyFilterModel.recalculateFrequencies()
+        self.__startFrequencyTimer()
+    
+    def __startFrequencyTimer(self):
+        """
+        Private method to start the timer to recalculate the frequencies.
+        """
+        tomorrow = QDateTime(QDate.currentDate().addDays(1), QTime(3, 0))
+        self.__frequencyTimer.start(
+            QDateTime.currentDateTime().secsTo(tomorrow) * 1000)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/History/HistoryMenu.py	Sun Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,90 @@
+# -*- 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 pyqtSlot, QObject, QUrl, QByteArray
+
+import WebBrowser.WebBrowserWindow
+
+
+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
+    
+    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
+    
+    @pyqtSlot(result=QObject)
+    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
+        
+        # TODO: SpeedDial
+##        return WebBrowser.WebBrowserWindow.WebBrowserWindow.speedDial()
+        return None
+    
+    @pyqtSlot(str)
+    def AddSearchProvider(self, engineUrl):
+        """
+        Public slot to add a search provider.
+        
+        @param engineUrl engineUrl of the XML file defining the search provider
+        @type str
+        """
+        WebBrowser.WebBrowserWindow.WebBrowserWindow.openSearchManager()\
+        .addEngine(QUrl(engineUrl))
+    
+    @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
+        """
+        import WebBrowser.WebBrowserWindow
+        WebBrowser.WebBrowserWindow.WebBrowserWindow.passwordManager()\
+        .formSubmitted(urlStr, userName, password, data,
+                       self.page())
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/JavaScript/__init__.py	Sun Mar 06 14:12:58 2016 +0100
@@ -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/FollowRedirectReply.py	Sun Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,114 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a network reply delegate allowing to check redirects.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSignal, QObject
+from PyQt5.QtNetwork import QNetworkRequest
+
+
+class FollowRedirectReply(QObject):
+    """
+    Class implementing a network reply delegate allowing to check redirects.
+    """
+    finished = pyqtSignal()
+    
+    def __init__(self, url, manager, maxRedirects=5):
+        """
+        Constructor
+        
+        @param url URL to get (QUrl)
+        @param manager reference to the network access manager
+            (QNetworkAccessManager)
+        @keyparam maxRedirects maximum allowed redirects (integer)
+        """
+        super(FollowRedirectReply, self).__init__()
+        
+        self.__manager = manager
+        self.__maxRedirects = maxRedirects
+        self.__redirectCount = 0
+        
+        self.__reply = self.__manager.get(QNetworkRequest(url))
+        self.__reply.finished.connect(self.__replyFinished)
+    
+    def reply(self):
+        """
+        Public method to get the reply object.
+        
+        @return reference to the reply object (QNetworkReply)
+        """
+        return self.__reply
+    
+    def originalUrl(self):
+        """
+        Public method to get the original URL.
+        
+        @return original URL (QUrl)
+        """
+        return self.__reply.request().url()
+    
+    def url(self):
+        """
+        Public method to get the final URL (after redirects).
+        
+        @return final URL (QUrl)
+        """
+        return self.__reply.url()
+    
+    def error(self):
+        """
+        Public method to get the error information.
+        
+        @return error code (QNetworkReply.NetworkError)
+        """
+        return self.__reply.error()
+    
+    def errorString(self):
+        """
+        Public method to get the error message.
+        
+        @return error message (string)
+        """
+        return self.__reply.errorString()
+    
+    def readAll(self):
+        """
+        Public method to read all received data.
+        
+        @return received raw data (QByteArray)
+        """
+        return self.__reply.readAll()
+    
+    def close(self):
+        """
+        Public method to close the data stream.
+        """
+        self.__reply.close()
+    
+    def __replyFinished(self):
+        """
+        Private slot handling the receipt of the requested data.
+        """
+        replyStatus = self.__reply.attribute(
+            QNetworkRequest.HttpStatusCodeAttribute)
+        if (replyStatus != 301 and replyStatus != 302) or \
+           self.__redirectCount == self.__maxRedirects:
+            self.finished.emit()
+            return
+        
+        self.__redirectCount += 1
+        
+        redirectUrl = self.__reply.attribute(
+            QNetworkRequest.RedirectionTargetAttribute)
+        self.__reply.close()
+        self.__reply.deleteLater()
+        self.__reply = None
+        
+        self.__reply = self.__manager.get(QNetworkRequest(redirectUrl))
+        self.__reply.finished.connect(self.__replyFinished)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Network/LoadRequest.py	Sun Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,133 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a page load request object.
+"""
+
+#
+# This code was ported from QupZilla.
+# Copyright (C) David Rosca <nowrep@gmail.com>
+#
+
+from __future__ import unicode_literals
+
+try:
+    from enum import Enum
+except ImportError:
+    from ThirdParty.enum import Enum
+
+from PyQt5.QtCore import QByteArray, QUrl
+
+
+class LoadRequestOperations(Enum):
+    """
+    Class implementing the load request operations.
+    """
+    GetOperation = 0
+    PostOperation = 1
+
+
+class LoadRequest(object):
+    """
+    Class implementing a page load request object.
+    """
+    def __init__(self, urlOrRequest=None,
+                 op=LoadRequestOperations.GetOperation, data=QByteArray()):
+        """
+        Constructor
+        
+        @param urlOrRequest URL or request object
+        @type QUrl or LoadRequest
+        @param op request operation
+        @type LoadRequestOperations
+        @param data request data
+        @type QByteArray
+        """
+        self.__url = QUrl()
+        self.__operation = op
+        self.__data = QByteArray(data)
+        
+        if isinstance(urlOrRequest, QUrl):
+            self.__url = QUrl(urlOrRequest)
+        elif isinstance(urlOrRequest, LoadRequest):
+            self.__url = urlOrRequest.url()
+            self.__operation = urlOrRequest.operation()
+            self.__data = urlOrRequest.data()
+    
+    def isEmpty(self):
+        """
+        Public method to test for an empty request.
+        
+        @return flag indicating an empty request
+        @rtype bool
+        """
+        return self.__url.isEmpty()
+    
+    def url(self):
+        """
+        Public method to get the request URL.
+        
+        @return request URL
+        @rtype QUrl
+        """
+        return QUrl(self.__url)
+    
+    def setUrl(self, url):
+        """
+        Public method to set the request URL.
+        
+        @param url request URL
+        @type QUrl
+        """
+        self.__url = QUrl(url)
+    
+    def urlString(self):
+        """
+        Public method to get the request URL as a string.
+        
+        @return request URL as a string
+        @rtype str
+        """
+        return QUrl.fromPercentEncoding(self.__url.toEncoded())
+    
+    def operation(self):
+        """
+        Public method to get the request operation.
+        
+        @return request operation
+        @rtype one of LoadRequest.GetOperation, LoadRequest.PostOperation
+        """
+        return self.__operation
+    
+    def setOperation(self, op):
+        """
+        Public method to set the request operation.
+        
+        @param op request operation
+        @type one of LoadRequest.GetOperation, LoadRequest.PostOperation
+        """
+        assert op in [LoadRequestOperations.GetOperation,
+                      LoadRequestOperations.PostOperation]
+        
+        self.__operation = op
+    
+    def data(self):
+        """
+        Public method to get the request data.
+        
+        @return request data
+        @rtype QByteArray
+        """
+        return QByteArray(self.__data)
+    
+    def setData(self, data):
+        """
+        Public method to set the request data
+        
+        @param data request data
+        @type QByteArray
+        """
+        self.__data = QByteArray(data)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Network/NetworkManager.py	Sun Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,178 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a network manager class.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtWidgets import QDialog
+from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkProxy
+
+from E5Gui import E5MessageBox
+
+from E5Network.E5NetworkProxyFactory import proxyAuthenticationRequired
+try:
+    from E5Network.E5SslErrorHandler import E5SslErrorHandler
+    SSL_AVAILABLE = True
+except ImportError:
+    SSL_AVAILABLE = False
+
+from WebBrowser.WebBrowserWindow import WebBrowserWindow
+
+import Preferences
+
+
+class NetworkManager(QNetworkAccessManager):
+    """
+    Class implementing a network manager.
+    """
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object (QObject)
+        """
+        super(NetworkManager, self).__init__(parent)
+        
+        if not WebBrowserWindow.mainWindow().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.__ignoredSslErrors = {}
+        # dictionary of temporarily ignored SSL errors
+        
+        self.proxyAuthenticationRequired.connect(proxyAuthenticationRequired)
+        self.authenticationRequired.connect(
+            lambda reply, auth: self.authentication(reply.url(), auth))
+    
+    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
+        """
+        # TODO: permanent SSL certificate error exceptions
+        host = error.url().host()
+        
+        if host in self.__ignoredSslErrors and \
+                self.__ignoredSslErrors[host] == error.error():
+            return True
+        
+        title = self.tr("SSL Certificate Error")
+        accept = E5MessageBox.yesNo(
+            view,
+            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()),
+            icon=E5MessageBox.Warning)
+        if accept:
+            self.__ignoredSslErrors[error.url().host()] = error.error()
+            return True
+        
+        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)
+        
+        # TODO: Qt 5.6
+##        from WebBrowser.WebBrowserWindow import WebBrowserWindow
+##        WebBrowserWindow.webProfile().setHttpAcceptLanguage(
+##            self.__acceptLanguage)
+    
+    def installUrlInterceptor(self, interceptor):
+        # TODO: Qt 5.6, URL Interceptor
+        pass
+    
+    def removeUrlInterceptor(self, interceptor):
+        # TODO: Qt 5.6, URL Interceptor
+        pass
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Network/UrlInterceptor.py	Sun Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,538 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the open search engine.
+"""
+
+from __future__ import unicode_literals
+
+import re
+import json
+
+from PyQt5.QtCore import pyqtSignal, pyqtSlot, QLocale, QUrl, QByteArray, \
+    QBuffer, QIODevice, QObject, qVersion
+from PyQt5.QtGui import QImage
+from PyQt5.QtNetwork import QNetworkRequest, QNetworkAccessManager, \
+    QNetworkReply
+
+from UI.Info import Program
+
+import Preferences
+import Utilities
+
+
+class OpenSearchEngine(QObject):
+    """
+    Class implementing the open search engine.
+    
+    @signal imageChanged() emitted after the icon has been changed
+    @signal suggestions(list of strings) emitted after the suggestions have
+            been received
+    """
+    imageChanged = pyqtSignal()
+    suggestions = pyqtSignal(list)
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object (QObject)
+        """
+        super(OpenSearchEngine, self).__init__(parent)
+        
+        self.__suggestionsReply = None
+        self.__networkAccessManager = None
+        self._name = ""
+        self._description = ""
+        self._searchUrlTemplate = ""
+        self._suggestionsUrlTemplate = ""
+        self._searchParameters = []            # list of two tuples
+        self._suggestionsParameters = []       # list of two tuples
+        self._imageUrl = ""
+        self.__image = QImage()
+        self.__iconMoved = False
+        self.__searchMethod = "get"
+        self.__suggestionsMethod = "get"
+        self.__requestMethods = {
+            "get": QNetworkAccessManager.GetOperation,
+            "post": QNetworkAccessManager.PostOperation,
+        }
+        
+        self.__replies = []
+    
+    @classmethod
+    def parseTemplate(cls, searchTerm, searchTemplate):
+        """
+        Class method to parse a search template.
+        
+        @param searchTerm term to search for (string)
+        @param searchTemplate template to be parsed (string)
+        @return parsed template (string)
+        """
+        locale = QLocale(Preferences.getWebBrowser("SearchLanguage"))
+        language = locale.name().replace("_", "-")
+        country = locale.name().split("_")[0].lower()
+        
+        result = searchTemplate
+        result = result.replace("{count}", "20")
+        result = result.replace("{startIndex}", "0")
+        result = result.replace("{startPage}", "0")
+        result = result.replace("{language}", language)
+        result = result.replace("{country}", country)
+        result = result.replace("{inputEncoding}", "UTF-8")
+        result = result.replace("{outputEncoding}", "UTF-8")
+        result = result.replace(
+            "{searchTerms}",
+            bytes(QUrl.toPercentEncoding(searchTerm)).decode())
+        result = re.sub(r"""\{([^\}]*:|)source\??\}""", Program, result)
+
+        return result
+    
+    @pyqtSlot(result=str)
+    def name(self):
+        """
+        Public method to get the name of the engine.
+        
+        @return name of the engine (string)
+        """
+        return self._name
+    
+    def setName(self, name):
+        """
+        Public method to set the engine name.
+        
+        @param name name of the engine (string)
+        """
+        self._name = name
+    
+    def description(self):
+        """
+        Public method to get the description of the engine.
+        
+        @return description of the engine (string)
+        """
+        return self._description
+    
+    def setDescription(self, description):
+        """
+        Public method to set the engine description.
+        
+        @param description description of the engine (string)
+        """
+        self._description = description
+    
+    def searchUrlTemplate(self):
+        """
+        Public method to get the search URL template of the engine.
+        
+        @return search URL template of the engine (string)
+        """
+        return self._searchUrlTemplate
+    
+    def setSearchUrlTemplate(self, searchUrlTemplate):
+        """
+        Public method to set the engine search URL template.
+        
+        The URL template is processed according to the specification:
+        <a
+          href="http://www.opensearch.org/Specifications/OpenSearch/1.1#OpenSearch_URL_template_syntax">
+        http://www.opensearch.org/Specifications/OpenSearch/1.1#OpenSearch_URL_template_syntax</a>
+
+        A list of template parameters currently supported and what they are
+        replaced with:
+        <table>
+        <tr><td><b>Parameter</b></td><td><b>Value</b></td></tr>
+        <tr><td>{count}</td><td>20</td></tr>
+        <tr><td>{startIndex}</td><td>0</td></tr>
+        <tr><td>{startPage}</td><td>0</td></tr>
+        <tr><td>{language}</td>
+          <td>the default language code (RFC 3066)</td></tr>
+        <tr><td>{country}</td>
+          <td>the default country code (first part of language)</td></tr>
+        <tr><td>{inputEncoding}</td><td>UTF-8</td></tr>
+        <tr><td>{outputEncoding}</td><td>UTF-8</td></tr>
+        <tr><td>{searchTerms}</td><td>the string supplied by the user</td></tr>
+        <tr><td>{*:source}</td>
+          <td>application name, QCoreApplication::applicationName()</td></tr>
+        </table>
+        
+        @param searchUrlTemplate search URL template of the engine (string)
+        """
+        self._searchUrlTemplate = searchUrlTemplate
+    
+    def searchUrl(self, searchTerm):
+        """
+        Public method to get a URL ready for searching.
+        
+        @param searchTerm term to search for (string)
+        @return URL (QUrl)
+        """
+        if not self._searchUrlTemplate:
+            return QUrl()
+        
+        ret = QUrl.fromEncoded(
+            self.parseTemplate(searchTerm, self._searchUrlTemplate)
+            .encode("utf-8"))
+        
+        if self.__searchMethod != "post":
+            if qVersion() >= "5.0.0":
+                from PyQt5.QtCore import QUrlQuery
+                urlQuery = QUrlQuery(ret)
+                for parameter in self._searchParameters:
+                    urlQuery.addQueryItem(
+                        parameter[0],
+                        self.parseTemplate(searchTerm, parameter[1]))
+                ret.setQuery(urlQuery)
+            else:
+                for parameter in self._searchParameters:
+                    ret.addQueryItem(
+                        parameter[0],
+                        self.parseTemplate(searchTerm, parameter[1]))
+        
+        return ret
+    
+    def providesSuggestions(self):
+        """
+        Public method to check, if the engine provides suggestions.
+        
+        @return flag indicating suggestions are provided (boolean)
+        """
+        return self._suggestionsUrlTemplate != ""
+    
+    def suggestionsUrlTemplate(self):
+        """
+        Public method to get the search URL template of the engine.
+        
+        @return search URL template of the engine (string)
+        """
+        return self._suggestionsUrlTemplate
+    
+    def setSuggestionsUrlTemplate(self, suggestionsUrlTemplate):
+        """
+        Public method to set the engine suggestions URL template.
+        
+        @param suggestionsUrlTemplate suggestions URL template of the
+            engine (string)
+        """
+        self._suggestionsUrlTemplate = suggestionsUrlTemplate
+    
+    def suggestionsUrl(self, searchTerm):
+        """
+        Public method to get a URL ready for suggestions.
+        
+        @param searchTerm term to search for (string)
+        @return URL (QUrl)
+        """
+        if not self._suggestionsUrlTemplate:
+            return QUrl()
+        
+        ret = QUrl.fromEncoded(QByteArray(self.parseTemplate(
+            searchTerm, self._suggestionsUrlTemplate).encode("utf-8")))
+        
+        if self.__searchMethod != "post":
+            if qVersion() >= "5.0.0":
+                from PyQt5.QtCore import QUrlQuery
+                urlQuery = QUrlQuery(ret)
+                for parameter in self._suggestionsParameters:
+                    urlQuery.addQueryItem(
+                        parameter[0],
+                        self.parseTemplate(searchTerm, parameter[1]))
+                ret.setQuery(urlQuery)
+            else:
+                for parameter in self._suggestionsParameters:
+                    ret.addQueryItem(
+                        parameter[0],
+                        self.parseTemplate(searchTerm, parameter[1]))
+        
+        return ret
+    
+    def searchParameters(self):
+        """
+        Public method to get the search parameters of the engine.
+        
+        @return search parameters of the engine (list of two tuples)
+        """
+        return self._searchParameters[:]
+    
+    def setSearchParameters(self, searchParameters):
+        """
+        Public method to set the engine search parameters.
+        
+        @param searchParameters search parameters of the engine
+            (list of two tuples)
+        """
+        self._searchParameters = searchParameters[:]
+    
+    def suggestionsParameters(self):
+        """
+        Public method to get the suggestions parameters of the engine.
+        
+        @return suggestions parameters of the engine (list of two tuples)
+        """
+        return self._suggestionsParameters[:]
+    
+    def setSuggestionsParameters(self, suggestionsParameters):
+        """
+        Public method to set the engine suggestions parameters.
+        
+        @param suggestionsParameters suggestions parameters of the
+            engine (list of two tuples)
+        """
+        self._suggestionsParameters = suggestionsParameters[:]
+    
+    def searchMethod(self):
+        """
+        Public method to get the HTTP request method used to perform search
+        requests.
+        
+        @return HTTP request method (string)
+        """
+        return self.__searchMethod
+    
+    def setSearchMethod(self, method):
+        """
+        Public method to set the HTTP request method used to perform search
+        requests.
+        
+        @param method HTTP request method (string)
+        """
+        requestMethod = method.lower()
+        if requestMethod not in self.__requestMethods:
+            return
+        
+        self.__searchMethod = requestMethod
+    
+    def suggestionsMethod(self):
+        """
+        Public method to get the HTTP request method used to perform
+        suggestions requests.
+        
+        @return HTTP request method (string)
+        """
+        return self.__suggestionsMethod
+    
+    def setSuggestionsMethod(self, method):
+        """
+        Public method to set the HTTP request method used to perform
+        suggestions requests.
+        
+        @param method HTTP request method (string)
+        """
+        requestMethod = method.lower()
+        if requestMethod not in self.__requestMethods:
+            return
+        
+        self.__suggestionsMethod = requestMethod
+    
+    def imageUrl(self):
+        """
+        Public method to get the image URL of the engine.
+        
+        @return image URL of the engine (string)
+        """
+        return self._imageUrl
+    
+    def setImageUrl(self, imageUrl):
+        """
+        Public method to set the engine image URL.
+        
+        @param imageUrl image URL of the engine (string)
+        """
+        self._imageUrl = imageUrl
+    
+    def setImageUrlAndLoad(self, imageUrl):
+        """
+        Public method to set the engine image URL.
+        
+        @param imageUrl image URL of the engine (string)
+        """
+        self.setImageUrl(imageUrl)
+        self.__iconMoved = False
+        self.loadImage()
+    
+    def loadImage(self):
+        """
+        Public method to load the image of the engine.
+        """
+        if self.__networkAccessManager is None or not self._imageUrl:
+            return
+        
+        reply = self.__networkAccessManager.get(
+            QNetworkRequest(QUrl.fromEncoded(self._imageUrl.encode("utf-8"))))
+        reply.finished.connect(self.__imageObtained)
+        self.__replies.append(reply)
+    
+    def __imageObtained(self):
+        """
+        Private slot to receive the image of the engine.
+        """
+        reply = self.sender()
+        if reply is None:
+            return
+        
+        response = reply.readAll()
+        
+        reply.close()
+        if reply in self.__replies:
+            self.__replies.remove(reply)
+        reply.deleteLater()
+        
+        if response.isEmpty():
+            return
+        
+        if response.startsWith(b"<html>") or response.startsWith(b"HTML"):
+            self.__iconMoved = True
+            self.__image = QImage()
+        else:
+            self.__image.loadFromData(response)
+        self.imageChanged.emit()
+    
+    def image(self):
+        """
+        Public method to get the image of the engine.
+        
+        @return image of the engine (QImage)
+        """
+        if not self.__iconMoved and self.__image.isNull():
+            self.loadImage()
+        
+        return self.__image
+    
+    def setImage(self, image):
+        """
+        Public method to set the image of the engine.
+        
+        @param image image to be set (QImage)
+        """
+        if not self._imageUrl:
+            imageBuffer = QBuffer()
+            imageBuffer.open(QIODevice.ReadWrite)
+            if image.save(imageBuffer, "PNG"):
+                self._imageUrl = "data:image/png;base64,{0}".format(
+                    bytes(imageBuffer.buffer().toBase64()).decode())
+        
+        self.__image = QImage(image)
+        self.imageChanged.emit()
+    
+    def isValid(self):
+        """
+        Public method to check, if the engine is valid.
+        
+        @return flag indicating validity (boolean)
+        """
+        return self._name and self._searchUrlTemplate
+    
+    def __eq__(self, other):
+        """
+        Special method implementing the == operator.
+        
+        @param other reference to an open search engine (OpenSearchEngine)
+        @return flag indicating equality (boolean)
+        """
+        if not isinstance(other, OpenSearchEngine):
+            return NotImplemented
+        
+        return self._name == other._name and \
+            self._description == other._description and \
+            self._imageUrl == other._imageUrl and \
+            self._searchUrlTemplate == other._searchUrlTemplate and \
+            self._suggestionsUrlTemplate == other._suggestionsUrlTemplate and \
+            self._searchParameters == other._searchParameters and \
+            self._suggestionsParameters == other._suggestionsParameters
+    
+    def __lt__(self, other):
+        """
+        Special method implementing the < operator.
+        
+        @param other reference to an open search engine (OpenSearchEngine)
+        @return flag indicating less than (boolean)
+        """
+        if not isinstance(other, OpenSearchEngine):
+            return NotImplemented
+        
+        return self._name < other._name
+    
+    def requestSuggestions(self, searchTerm):
+        """
+        Public method to request suggestions.
+        
+        @param searchTerm term to get suggestions for (string)
+        """
+        if not searchTerm or not self.providesSuggestions():
+            return
+        
+        if self.__networkAccessManager is None:
+            return
+        
+        if self.__suggestionsReply is not None:
+            self.__suggestionsReply.finished.disconnect(
+                self.__suggestionsObtained)
+            self.__suggestionsReply.abort()
+            self.__suggestionsReply.deleteLater()
+            self.__suggestionsReply = None
+        
+        if self.__suggestionsMethod not in self.__requestMethods:
+            # ignore
+            return
+        
+        if self.__suggestionsMethod == "get":
+            self.__suggestionsReply = self.networkAccessManager().get(
+                QNetworkRequest(self.suggestionsUrl(searchTerm)))
+        else:
+            parameters = []
+            for parameter in self._suggestionsParameters:
+                parameters.append(parameter[0] + "=" + parameter[1])
+            data = "&".join(parameters)
+            self.__suggestionsReply = self.networkAccessManager().post(
+                QNetworkRequest(self.suggestionsUrl(searchTerm)), data)
+        self.__suggestionsReply.finished.connect(
+            self.__suggestionsObtained)
+    
+    def __suggestionsObtained(self):
+        """
+        Private slot to receive the suggestions.
+        """
+        if self.__suggestionsReply.error() == QNetworkReply.NoError:
+            buffer = bytes(self.__suggestionsReply.readAll())
+            response = Utilities.decodeBytes(buffer)
+            response = response.strip()
+            
+            self.__suggestionsReply.close()
+            self.__suggestionsReply.deleteLater()
+            self.__suggestionsReply = None
+            
+            if len(response) == 0:
+                return
+            
+            try:
+                result = json.loads(response)
+            except ValueError:
+                return
+            
+            try:
+                suggestions = result[1]
+            except IndexError:
+                return
+            
+            self.suggestions.emit(suggestions)
+    
+    def networkAccessManager(self):
+        """
+        Public method to get a reference to the network access manager object.
+        
+        @return reference to the network access manager object
+            (QNetworkAccessManager)
+        """
+        return self.__networkAccessManager
+    
+    def setNetworkAccessManager(self, networkAccessManager):
+        """
+        Public method to set the reference to the network access manager.
+        
+        @param networkAccessManager reference to the network access manager
+            object (QNetworkAccessManager)
+        """
+        self.__networkAccessManager = networkAccessManager
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/OpenSearch/OpenSearchEngineAction.py	Sun Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,598 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a manager for open search engines.
+"""
+
+from __future__ import unicode_literals
+
+import os
+
+from PyQt5.QtCore import pyqtSignal, QObject, QUrl, QFile, QDir, QIODevice, \
+    QUrlQuery
+from PyQt5.QtWidgets import QLineEdit, QInputDialog
+from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply
+
+from E5Gui.E5Application import e5App
+from E5Gui import E5MessageBox
+
+from Utilities.AutoSaver import AutoSaver
+import Utilities
+import Preferences
+
+
+class OpenSearchManager(QObject):
+    """
+    Class implementing a manager for open search engines.
+    
+    @signal changed() emitted to indicate a change
+    @signal currentEngineChanged() emitted to indicate a change of
+            the current search engine
+    """
+    changed = pyqtSignal()
+    currentEngineChanged = pyqtSignal()
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object (QObject)
+        """
+        if parent is None:
+            parent = e5App()
+        super(OpenSearchManager, self).__init__(parent)
+        
+        self.__replies = []
+        self.__engines = {}
+        self.__keywords = {}
+        self.__current = ""
+        self.__loading = False
+        self.__saveTimer = AutoSaver(self, self.save)
+        
+        self.changed.connect(self.__saveTimer.changeOccurred)
+        
+        self.load()
+    
+    def close(self):
+        """
+        Public method to close the open search engines manager.
+        """
+        self.__saveTimer.saveIfNeccessary()
+    
+    def currentEngineName(self):
+        """
+        Public method to get the name of the current search engine.
+        
+        @return name of the current search engine (string)
+        """
+        return self.__current
+    
+    def setCurrentEngineName(self, name):
+        """
+        Public method to set the current engine by name.
+        
+        @param name name of the new current engine (string)
+        """
+        if name not in self.__engines:
+            return
+        
+        self.__current = name
+        self.currentEngineChanged.emit()
+        self.changed.emit()
+    
+    def currentEngine(self):
+        """
+        Public method to get a reference to the current engine.
+        
+        @return reference to the current engine (OpenSearchEngine)
+        """
+        if not self.__current or self.__current not in self.__engines:
+            return None
+        
+        return self.__engines[self.__current]
+    
+    def setCurrentEngine(self, engine):
+        """
+        Public method to set the current engine.
+        
+        @param engine reference to the new current engine (OpenSearchEngine)
+        """
+        if engine is None:
+            return
+        
+        for engineName in self.__engines:
+            if self.__engines[engineName] == engine:
+                self.setCurrentEngineName(engineName)
+                break
+    
+    def engine(self, name):
+        """
+        Public method to get a reference to the named engine.
+        
+        @param name name of the engine (string)
+        @return reference to the engine (OpenSearchEngine)
+        """
+        if name not in self.__engines:
+            return None
+        
+        return self.__engines[name]
+    
+    def engineExists(self, name):
+        """
+        Public method to check, if an engine exists.
+        
+        @param name name of the engine (string)
+        @return flag indicating an existing engine (boolean)
+        """
+        return name in self.__engines
+    
+    def allEnginesNames(self):
+        """
+        Public method to get a list of all engine names.
+        
+        @return sorted list of all engine names (list of strings)
+        """
+        return sorted(self.__engines.keys())
+    
+    def enginesCount(self):
+        """
+        Public method to get the number of available engines.
+        
+        @return number of engines (integer)
+        """
+        return len(self.__engines)
+    
+    def addEngine(self, engine):
+        """
+        Public method to add a new search engine.
+        
+        @param engine URL of the engine definition file (QUrl) or
+            name of a file containing the engine definition (string)
+            or reference to an engine object (OpenSearchEngine)
+        @return flag indicating success (boolean)
+        """
+        from .OpenSearchEngine import OpenSearchEngine
+        if isinstance(engine, QUrl):
+            return self.__addEngineByUrl(engine)
+        elif isinstance(engine, OpenSearchEngine):
+            return self.__addEngineByEngine(engine)
+        else:
+            return self.__addEngineByFile(engine)
+    
+    def __addEngineByUrl(self, url):
+        """
+        Private method to add a new search engine given its URL.
+        
+        @param url URL of the engine definition file (QUrl)
+        @return flag indicating success (boolean)
+        """
+        if not url.isValid():
+            return
+        
+        from WebBrowser.WebBrowserWindow import WebBrowserWindow
+
+        reply = WebBrowserWindow.networkManager().get(QNetworkRequest(url))
+        reply.finished.connect(self.__engineFromUrlAvailable)
+        reply.setParent(self)
+        self.__replies.append(reply)
+        
+        return True
+    
+    def __addEngineByFile(self, filename):
+        """
+        Private method to add a new search engine given a filename.
+        
+        @param filename name of a file containing the engine definition
+            (string)
+        @return flag indicating success (boolean)
+        """
+        file_ = QFile(filename)
+        if not file_.open(QIODevice.ReadOnly):
+            return False
+        
+        from .OpenSearchReader import OpenSearchReader
+        reader = OpenSearchReader()
+        engine = reader.read(file_)
+        
+        if not self.__addEngineByEngine(engine):
+            return False
+        
+        return True
+    
+    def __addEngineByEngine(self, engine):
+        """
+        Private method to add a new search engine given a reference to an
+        engine.
+        
+        @param engine reference to an engine object (OpenSearchEngine)
+        @return flag indicating success (boolean)
+        """
+        if engine is None:
+            return False
+        
+        if not engine.isValid():
+            return False
+        
+        if engine.name() in self.__engines:
+            return False
+        
+        engine.setParent(self)
+        self.__engines[engine.name()] = engine
+        
+        self.changed.emit()
+        
+        return True
+    
+    def addEngineFromForm(self, res, view):
+        """
+        Private method to add a new search engine from a form.
+        
+        @param res result of the JavaScript run on by
+            WebBrowserView.__addSearchEngine()
+        @type dict or None
+        @param view reference to the web browser view
+        @type WebBrowserView
+        """
+        if not res:
+            return
+        
+        method = res["method"]
+        isPost = method == "post"
+        actionUrl = QUrl(res["action"])
+        inputName = res["inputName"]
+        
+        if method != "get":
+            E5MessageBox.warning(
+                self,
+                self.tr("Method not supported"),
+                self.tr(
+                    """{0} method is not supported.""").format(method.upper()))
+            return
+        
+        if actionUrl.isRelative():
+            actionUrl = view.url().resolved(actionUrl)
+        
+        searchUrlQuery = QUrlQuery(actionUrl)
+        searchUrlQuery.addQueryItem(inputName, "{searchTerms}")
+        
+        inputFields = res["inputs"]
+        for inputField in inputFields:
+            name = inputField[0]
+            value = inputField[1]
+            
+            if not name or name == inputName or not value:
+                continue
+            
+            searchUrlQuery.addQueryItem(name, value)
+        
+        engineName, ok = QInputDialog.getText(
+            view,
+            self.tr("Engine name"),
+            self.tr("Enter a name for the engine"),
+            QLineEdit.Normal)
+        if not ok:
+            return
+        
+        actionUrl.setQuery(searchUrlQuery)
+        
+        from .OpenSearchEngine import OpenSearchEngine
+        engine = OpenSearchEngine()
+        engine.setName(engineName)
+        engine.setDescription(engineName)
+        engine.setSearchUrlTemplate(actionUrl.toDisplayString(QUrl.FullyDecoded))
+        engine.setImage(view.icon().pixmap(16, 16).toImage())
+        
+        self.__addEngineByEngine(engine)
+    
+    def removeEngine(self, name):
+        """
+        Public method to remove an engine.
+        
+        @param name name of the engine (string)
+        """
+        if len(self.__engines) <= 1:
+            return
+        
+        if name not in self.__engines:
+            return
+        
+        engine = self.__engines[name]
+        for keyword in [k for k in self.__keywords
+                        if self.__keywords[k] == engine]:
+            del self.__keywords[keyword]
+        del self.__engines[name]
+        
+        file_ = QDir(self.enginesDirectory()).filePath(
+            self.generateEngineFileName(name))
+        QFile.remove(file_)
+        
+        if name == self.__current:
+            self.setCurrentEngineName(list(self.__engines.keys())[0])
+        
+        self.changed.emit()
+    
+    def generateEngineFileName(self, engineName):
+        """
+        Public method to generate a valid engine file name.
+        
+        @param engineName name of the engine (string)
+        @return valid engine file name (string)
+        """
+        fileName = ""
+        
+        # strip special characters
+        for c in engineName:
+            if c.isspace():
+                fileName += '_'
+                continue
+            
+            if c.isalnum():
+                fileName += c
+        
+        fileName += ".xml"
+        
+        return fileName
+    
+    def saveDirectory(self, dirName):
+        """
+        Public method to save the search engine definitions to files.
+        
+        @param dirName name of the directory to write the files to (string)
+        """
+        dir = QDir()
+        if not dir.mkpath(dirName):
+            return
+        dir.setPath(dirName)
+        
+        from .OpenSearchWriter import OpenSearchWriter
+        writer = OpenSearchWriter()
+        
+        for engine in list(self.__engines.values()):
+            name = self.generateEngineFileName(engine.name())
+            fileName = dir.filePath(name)
+            
+            file = QFile(fileName)
+            if not file.open(QIODevice.WriteOnly):
+                continue
+            
+            writer.write(file, engine)
+    
+    def save(self):
+        """
+        Public method to save the search engines configuration.
+        """
+        if self.__loading:
+            return
+        
+        self.saveDirectory(self.enginesDirectory())
+        
+        Preferences.setWebBrowser("WebSearchEngine", self.__current)
+        keywords = []
+        for k in self.__keywords:
+            if self.__keywords[k]:
+                keywords.append((k, self.__keywords[k].name()))
+        Preferences.setWebBrowser("WebSearchKeywords", keywords)
+    
+    def loadDirectory(self, dirName):
+        """
+        Public method to load the search engine definitions from files.
+        
+        @param dirName name of the directory to load the files from (string)
+        @return flag indicating success (boolean)
+        """
+        if not QFile.exists(dirName):
+            return False
+        
+        success = False
+        
+        dir = QDir(dirName)
+        for name in dir.entryList(["*.xml"]):
+            fileName = dir.filePath(name)
+            if self.__addEngineByFile(fileName):
+                success = True
+        
+        return success
+    
+    def load(self):
+        """
+        Public method to load the search engines configuration.
+        """
+        self.__loading = True
+        self.__current = Preferences.getWebBrowser("WebSearchEngine")
+        keywords = Preferences.getWebBrowser("WebSearchKeywords")
+        
+        if not self.loadDirectory(self.enginesDirectory()):
+            self.restoreDefaults()
+        
+        for keyword, engineName in keywords:
+            self.__keywords[keyword] = self.engine(engineName)
+        
+        if self.__current not in self.__engines and \
+           len(self.__engines) > 0:
+            self.__current = list(self.__engines.keys())[0]
+        
+        self.__loading = False
+        self.currentEngineChanged.emit()
+    
+    def restoreDefaults(self):
+        """
+        Public method to restore the default search engines.
+        """
+        from .OpenSearchReader import OpenSearchReader
+        from .DefaultSearchEngines import DefaultSearchEngines_rc   # __IGNORE_WARNING__
+        
+        defaultEngineFiles = ["Amazoncom.xml", "Bing.xml",
+                              "DeEn_Beolingus.xml", "DuckDuckGo.xml",
+                              "Facebook.xml", "Google.xml",
+                              "Google_Im_Feeling_Lucky.xml", "LEO_DeuEng.xml",
+                              "LinuxMagazin.xml", "Reddit.xml", "Wikia.xml",
+                              "Wikia_en.xml", "Wikipedia.xml",
+                              "Wiktionary.xml", "Yahoo.xml", "YouTube.xml", ]
+        # Keep this list in sync with the contents of the resource file.
+
+        reader = OpenSearchReader()
+        for engineFileName in defaultEngineFiles:
+            engineFile = QFile(":/" + engineFileName)
+            if not engineFile.open(QIODevice.ReadOnly):
+                continue
+            engine = reader.read(engineFile)
+            self.__addEngineByEngine(engine)
+    
+    def enginesDirectory(self):
+        """
+        Public method to determine the directory containing the search engine
+        descriptions.
+        
+        @return directory name (string)
+        """
+        return os.path.join(
+            Utilities.getConfigDir(), "web_browser", "searchengines")
+    
+    def __confirmAddition(self, engine):
+        """
+        Private method to confirm the addition of a new search engine.
+        
+        @param engine reference to the engine to be added (OpenSearchEngine)
+        @return flag indicating the engine shall be added (boolean)
+        """
+        if engine is None or not engine.isValid():
+            return False
+        
+        host = QUrl(engine.searchUrlTemplate()).host()
+        
+        res = E5MessageBox.yesNo(
+            None,
+            "",
+            self.tr(
+                """<p>Do you want to add the following engine to your"""
+                """ list of search engines?<br/><br/>Name: {0}<br/>"""
+                """Searches on: {1}</p>""").format(engine.name(), host))
+        return res
+    
+    def __engineFromUrlAvailable(self):
+        """
+        Private slot to add a search engine from the net.
+        """
+        reply = self.sender()
+        if reply is None:
+            return
+        
+        if reply.error() != QNetworkReply.NoError:
+            reply.close()
+            if reply in self.__replies:
+                self.__replies.remove(reply)
+            return
+        
+        from .OpenSearchReader import OpenSearchReader
+        reader = OpenSearchReader()
+        engine = reader.read(reply)
+        
+        reply.close()
+        if reply in self.__replies:
+            self.__replies.remove(reply)
+        
+        if not engine.isValid():
+            return
+        
+        if self.engineExists(engine.name()):
+            return
+        
+        if not self.__confirmAddition(engine):
+            return
+        
+        if not self.__addEngineByEngine(engine):
+            return
+    
+    def convertKeywordSearchToUrl(self, keywordSearch):
+        """
+        Public method to get the search URL for a keyword search.
+        
+        @param keywordSearch search string for keyword search (string)
+        @return search URL (QUrl)
+        """
+        try:
+            keyword, term = keywordSearch.split(" ", 1)
+        except ValueError:
+            return QUrl()
+        
+        if not term:
+            return QUrl()
+        
+        engine = self.engineForKeyword(keyword)
+        if engine:
+            return engine.searchUrl(term)
+        
+        return QUrl()
+    
+    def engineForKeyword(self, keyword):
+        """
+        Public method to get the engine for a keyword.
+        
+        @param keyword keyword to get engine for (string)
+        @return reference to the search engine object (OpenSearchEngine)
+        """
+        if keyword and keyword in self.__keywords:
+            return self.__keywords[keyword]
+        
+        return None
+    
+    def setEngineForKeyword(self, keyword, engine):
+        """
+        Public method to set the engine for a keyword.
+        
+        @param keyword keyword to get engine for (string)
+        @param engine reference to the search engine object (OpenSearchEngine)
+            or None to remove the keyword
+        """
+        if not keyword:
+            return
+        
+        if engine is None:
+            try:
+                del self.__keywords[keyword]
+            except KeyError:
+                pass
+        else:
+            self.__keywords[keyword] = engine
+        
+        self.changed.emit()
+    
+    def keywordsForEngine(self, engine):
+        """
+        Public method to get the keywords for a given engine.
+        
+        @param engine reference to the search engine object (OpenSearchEngine)
+        @return list of keywords (list of strings)
+        """
+        return [k for k in self.__keywords if self.__keywords[k] == engine]
+    
+    def setKeywordsForEngine(self, engine, keywords):
+        """
+        Public method to set the keywords for an engine.
+        
+        @param engine reference to the search engine object (OpenSearchEngine)
+        @param keywords list of keywords (list of strings)
+        """
+        if engine is None:
+            return
+        
+        for keyword in self.keywordsForEngine(engine):
+            del self.__keywords[keyword]
+        
+        for keyword in keywords:
+            if not keyword:
+                continue
+            
+            self.__keywords[keyword] = engine
+        
+        self.changed.emit()
+    
+    def enginesChanged(self):
+        """
+        Public slot to tell the search engine manager, that something has
+        changed.
+        """
+        self.changed.emit()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/OpenSearch/OpenSearchReader.py	Sun Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,135 @@
+# -*- 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 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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,412 @@
+# -*- 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 .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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,176 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a class to read login data files.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import QXmlStreamReader, QIODevice, QFile, \
+    QCoreApplication, QUrl, QByteArray
+
+
+class PasswordReader(QXmlStreamReader):
+    """
+    Class implementing a reader object for login data files.
+    """
+    def __init__(self):
+        """
+        Constructor
+        """
+        super(PasswordReader, self).__init__()
+    
+    def read(self, fileNameOrDevice):
+        """
+        Public method to read a login data file.
+        
+        @param fileNameOrDevice name of the file to read (string)
+            or reference to the device to read (QIODevice)
+        @return tuple containing the logins, forms and never URLs
+        """
+        self.__logins = {}
+        self.__loginForms = {}
+        self.__never = []
+        
+        if isinstance(fileNameOrDevice, QIODevice):
+            self.setDevice(fileNameOrDevice)
+        else:
+            f = QFile(fileNameOrDevice)
+            if not f.exists():
+                return self.__logins, self.__loginForms, self.__never
+            f.open(QFile.ReadOnly)
+            self.setDevice(f)
+        
+        while not self.atEnd():
+            self.readNext()
+            if self.isStartElement():
+                version = self.attributes().value("version")
+                if self.name() == "Password" and \
+                   (not version or version == "2.0"):
+                    self.__readPasswords()
+                else:
+                    self.raiseError(QCoreApplication.translate(
+                        "PasswordReader",
+                        "The file is not a Passwords version 2.0 file."))
+        
+        return self.__logins, self.__loginForms, self.__never
+    
+    def __readPasswords(self):
+        """
+        Private method to read and parse the login data file.
+        """
+        if not self.isStartElement() and self.name() != "Password":
+            return
+        
+        while not self.atEnd():
+            self.readNext()
+            if self.isEndElement():
+                break
+            
+            if self.isStartElement():
+                if self.name() == "Logins":
+                    self.__readLogins()
+                elif self.name() == "Forms":
+                    self.__readForms()
+                elif self.name() == "Nevers":
+                    self.__readNevers()
+                else:
+                    self.__skipUnknownElement()
+    
+    def __readLogins(self):
+        """
+        Private method to read the login information.
+        """
+        if not self.isStartElement() and self.name() != "Logins":
+            return
+        
+        while not self.atEnd():
+            self.readNext()
+            if self.isEndElement():
+                if self.name() == "Login":
+                    continue
+                else:
+                    break
+            
+            if self.isStartElement():
+                if self.name() == "Login":
+                    attributes = self.attributes()
+                    key = attributes.value("key")
+                    user = attributes.value("user")
+                    password = attributes.value("password")
+                    self.__logins[key] = (user, password)
+                else:
+                    self.__skipUnknownElement()
+    
+    def __readForms(self):
+        """
+        Private method to read the forms information.
+        """
+        if not self.isStartElement() and self.name() != "Forms":
+            return
+        
+        while not self.atEnd():
+            self.readNext()
+            if self.isStartElement():
+                if self.name() == "Form":
+                    from .LoginForm import LoginForm
+                    attributes = self.attributes()
+                    key = attributes.value("key")
+                    form = LoginForm()
+                    form.url = QUrl(attributes.value("url"))
+                    form.name = attributes.value("name")
+                    
+                elif self.name() == "PostData":
+                    form.postData = self.readElementText()
+                else:
+                    self.__skipUnknownElement()
+            
+            if self.isEndElement():
+                if self.name() == "Form":
+                    self.__loginForms[key] = form
+                    continue
+                elif self.name() == "PostData":
+                    continue
+                elif self.name() in ["Elements", "Element"]:
+                    continue
+                else:
+                    break
+    
+    def __readNevers(self):
+        """
+        Private method to read the never URLs.
+        """
+        if not self.isStartElement() and self.name() != "Nevers":
+            return
+        
+        while not self.atEnd():
+            self.readNext()
+            if self.isEndElement():
+                if self.name() == "Never":
+                    continue
+                else:
+                    break
+            
+            if self.isStartElement():
+                if self.name() == "Never":
+                    self.__never.append(self.attributes().value("url"))
+                else:
+                    self.__skipUnknownElement()
+    
+    def __skipUnknownElement(self):
+        """
+        Private method to skip over all unknown elements.
+        """
+        if not self.isStartElement():
+            return
+        
+        while not self.atEnd():
+            self.readNext()
+            if self.isEndElement():
+                break
+            
+            if self.isStartElement():
+                self.__skipUnknownElement()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Passwords/PasswordWriter.py	Sun Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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/SearchWidget.py	Sun Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,250 @@
+# -*- 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
+        
+        # TODO: Check what Qt 5.6 can offer (maybe using JavaScript)
+        self.wrapCheckBox.setVisible(False)
+        self.highlightAllCheckBox.setVisible(False)
+##        self.wrapCheckBox.setChecked(True)
+        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.on_highlightAllCheckBox_toggled(True)
+        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
+        
+        # TODO: adjust this to the browser API
+        self.__mainWindow.currentBrowser().findNextPrev(
+            self.findtextCombo.currentText(),
+            self.caseCheckBox.isChecked(),
+            self.__findBackwards,
+            self.wrapCheckBox.isChecked(),
+            False,
+            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(bool)
+##    def on_highlightAllCheckBox_toggled(self, checked):
+##        """
+##        Private slot to toggle the highlight of all occurrences.
+##        
+##        @param checked flag indicating the check box toggle state (boolean)
+##        """
+##        cbr = self.__mainWindow.currentBrowser()
+##        if cbr is None:
+##            return
+##        cbr.findNextPrev(
+##            "", False, False, False, True)
+##        if self.highlightAllCheckBox.isChecked():
+##            cbr.findNextPrev(
+##                self.findtextCombo.currentText(),
+##                self.caseCheckBox.isChecked(),
+##                False, False, 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 Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,124 @@
+<?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>718</width>
+    <height>25</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Find</string>
+  </property>
+  <layout class="QHBoxLayout" name="horizontalLayout">
+   <property name="margin">
+    <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="QCheckBox" name="wrapCheckBox">
+     <property name="text">
+      <string>Wrap around</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QCheckBox" name="highlightAllCheckBox">
+     <property name="text">
+      <string>Highlight all</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>wrapCheckBox</tabstop>
+  <tabstop>highlightAllCheckBox</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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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/Sync/DirectorySyncHandler.py	Sun Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,281 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a synchronization handler using a shared directory.
+"""
+
+from __future__ import unicode_literals
+
+import os
+
+from PyQt5.QtCore import pyqtSignal, QByteArray, QFileInfo, QCoreApplication
+
+from .SyncHandler import SyncHandler
+
+from WebBrowser.WebBrowserWindow import WebBrowserWindow
+
+import Preferences
+
+
+class DirectorySyncHandler(SyncHandler):
+    """
+    Class implementing a synchronization handler using a shared directory.
+    
+    @signal syncStatus(type_, message) emitted to indicate the synchronization
+        status (string one of "bookmarks", "history", "passwords",
+        "useragents" or "speeddial", string)
+    @signal syncError(message) emitted for a general error with the error
+        message (string)
+    @signal syncMessage(message) emitted to send a message about
+        synchronization (string)
+    @signal syncFinished(type_, done, download) emitted after a
+        synchronization has finished (string one of "bookmarks", "history",
+        "passwords", "useragents" or "speeddial", boolean, boolean)
+    """
+    syncStatus = pyqtSignal(str, str)
+    syncError = pyqtSignal(str)
+    syncMessage = pyqtSignal(str)
+    syncFinished = pyqtSignal(str, bool, bool)
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object (QObject)
+        """
+        super(DirectorySyncHandler, self).__init__(parent)
+        self.__forceUpload = False
+        
+        self.__remoteFilesFound = []
+    
+    def initialLoadAndCheck(self, forceUpload):
+        """
+        Public method to do the initial check.
+        
+        @keyparam forceUpload flag indicating a forced upload of the files
+            (boolean)
+        """
+        if not Preferences.getWebBrowser("SyncEnabled"):
+            return
+        
+        self.__forceUpload = forceUpload
+        
+        self.__remoteFilesFound = []
+        
+        # check the existence of the shared directory; create it, if it is
+        # not there
+        if not os.path.exists(Preferences.getWebBrowser("SyncDirectoryPath")):
+            try:
+                os.makedirs(Preferences.getWebBrowser("SyncDirectoryPath"))
+            except OSError as err:
+                self.syncError.emit(
+                    self.tr("Error creating the shared directory.\n{0}")
+                    .format(str(err)))
+                return
+        
+        self.__initialSync()
+    
+    def __downloadFile(self, type_, fileName, timestamp):
+        """
+        Private method to downlaod the given file.
+        
+        @param type_ type of the synchronization event (string one
+            of "bookmarks", "history", "passwords", "useragents" or
+            "speeddial")
+        @param fileName name of the file to be downloaded (string)
+        @param timestamp time stamp in seconds of the file to be downloaded
+            (integer)
+        """
+        self.syncStatus.emit(type_, self._messages[type_]["RemoteExists"])
+        try:
+            f = open(os.path.join(
+                    Preferences.getWebBrowser("SyncDirectoryPath"),
+                    self._remoteFiles[type_]), "rb")
+            data = f.read()
+            f.close()
+        except IOError as err:
+            self.syncStatus.emit(
+                type_,
+                self.tr("Cannot read remote file.\n{0}").format(str(err)))
+            self.syncFinished.emit(type_, False, True)
+            return
+        
+        QCoreApplication.processEvents()
+        ok, error = self.writeFile(QByteArray(data), fileName, type_,
+                                   timestamp)
+        if not ok:
+            self.syncStatus.emit(type_, error)
+        self.syncFinished.emit(type_, ok, True)
+    
+    def __uploadFile(self, type_, fileName):
+        """
+        Private method to upload the given file.
+        
+        @param type_ type of the synchronization event (string one
+            of "bookmarks", "history", "passwords", "useragents" or
+            "speeddial")
+        @param fileName name of the file to be uploaded (string)
+        """
+        QCoreApplication.processEvents()
+        data = self.readFile(fileName, type_)
+        if data.isEmpty():
+            self.syncStatus.emit(type_, self._messages[type_]["LocalMissing"])
+            self.syncFinished.emit(type_, False, False)
+            return
+        else:
+            try:
+                f = open(os.path.join(
+                    Preferences.getWebBrowser("SyncDirectoryPath"),
+                    self._remoteFiles[type_]), "wb")
+                f.write(bytes(data))
+                f.close()
+            except IOError as err:
+                self.syncStatus.emit(
+                    type_,
+                    self.tr("Cannot write remote file.\n{0}").format(
+                        str(err)))
+                self.syncFinished.emit(type_, False, False)
+                return
+            
+        self.syncFinished.emit(type_, True, False)
+    
+    def __initialSyncFile(self, type_, fileName):
+        """
+        Private method to do the initial synchronization of the given file.
+        
+        @param type_ type of the synchronization event (string one
+            of "bookmarks", "history", "passwords", "useragents" or
+            "speeddial")
+        @param fileName name of the file to be synchronized (string)
+        """
+        if not self.__forceUpload and \
+                os.path.exists(os.path.join(
+                    Preferences.getWebBrowser("SyncDirectoryPath"),
+                    self._remoteFiles[type_])) and \
+                QFileInfo(fileName).lastModified() <= QFileInfo(
+                    os.path.join(
+                        Preferences.getWebBrowser("SyncDirectoryPath"),
+                        self._remoteFiles[type_])).lastModified():
+            self.__downloadFile(
+                type_, fileName,
+                QFileInfo(os.path.join(
+                    Preferences.getWebBrowser("SyncDirectoryPath"),
+                    self._remoteFiles[type_])).lastModified().toTime_t())
+        else:
+            if not os.path.exists(os.path.join(
+                    Preferences.getWebBrowser("SyncDirectoryPath"),
+                    self._remoteFiles[type_])):
+                self.syncStatus.emit(
+                    type_, self._messages[type_]["RemoteMissing"])
+            else:
+                self.syncStatus.emit(
+                    type_, self._messages[type_]["LocalNewer"])
+            self.__uploadFile(type_, fileName)
+    
+    def __initialSync(self):
+        """
+        Private slot to do the initial synchronization.
+        """
+        QCoreApplication.processEvents()
+        # Bookmarks
+        if Preferences.getWebBrowser("SyncBookmarks"):
+            self.__initialSyncFile(
+                "bookmarks",
+                WebBrowserWindow.bookmarksManager().getFileName())
+        
+        QCoreApplication.processEvents()
+        # History
+        if Preferences.getWebBrowser("SyncHistory"):
+            self.__initialSyncFile(
+                "history",
+                WebBrowserWindow.historyManager().getFileName())
+        
+        QCoreApplication.processEvents()
+        # Passwords
+        if Preferences.getWebBrowser("SyncPasswords"):
+            self.__initialSyncFile(
+                "passwords",
+                WebBrowserWindow.passwordManager().getFileName())
+        
+        # TODO: UserAgents
+##        QCoreApplication.processEvents()
+##        # User Agent Settings
+##        if Preferences.getWebBrowser("SyncUserAgents"):
+##            self.__initialSyncFile(
+##                "useragents",
+##                WebBrowserWindow.userAgentsManager().getFileName())
+        
+        # TODO: SpeedDial
+##        QCoreApplication.processEvents()
+##        # Speed Dial Settings
+##        if Preferences.getWebBrowser("SyncSpeedDial"):
+##            self.__initialSyncFile(
+##                "speeddial",
+##                WebBrowserWindow.speedDial().getFileName())
+        
+        self.__forceUpload = False
+        self.syncMessage.emit(self.tr("Synchronization finished"))
+    
+    def __syncFile(self, type_, fileName):
+        """
+        Private method to synchronize the given file.
+        
+        @param type_ type of the synchronization event (string one
+            of "bookmarks", "history", "passwords", "useragents" or
+            "speeddial")
+        @param fileName name of the file to be synchronized (string)
+        """
+        self.syncStatus.emit(type_, self._messages[type_]["Uploading"])
+        self.__uploadFile(type_, fileName)
+    
+    def syncBookmarks(self):
+        """
+        Public method to synchronize the bookmarks.
+        """
+        self.__syncFile(
+            "bookmarks",
+            WebBrowserWindow.bookmarksManager().getFileName())
+    
+    def syncHistory(self):
+        """
+        Public method to synchronize the history.
+        """
+        self.__syncFile(
+            "history",
+            WebBrowserWindow.historyManager().getFileName())
+    
+    def syncPasswords(self):
+        """
+        Public method to synchronize the passwords.
+        """
+        self.__syncFile(
+            "passwords",
+            WebBrowserWindow.passwordManager().getFileName())
+    
+    # TODO: UserAgents
+    def syncUserAgents(self):
+        """
+        Public method to synchronize the user agents.
+        """
+##        self.__syncFile(
+##            "useragents",
+##            WebBrowserWindow.userAgentsManager().getFileName())
+    
+    # TODO: SpeedDial
+    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 Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,414 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a synchronization handler using FTP.
+"""
+
+from __future__ import unicode_literals
+
+import ftplib
+import io
+
+from PyQt5.QtCore import pyqtSignal, QTimer, QFileInfo, QCoreApplication, \
+    QByteArray
+
+from E5Network.E5Ftp import E5Ftp, E5FtpProxyType, E5FtpProxyError
+
+from .SyncHandler import SyncHandler
+
+from WebBrowser.WebBrowserWindow import WebBrowserWindow
+
+import Preferences
+
+from Utilities.FtpUtilities import FtpDirLineParser, FtpDirLineParserError
+
+
+class FtpSyncHandler(SyncHandler):
+    """
+    Class implementing a synchronization handler using FTP.
+    
+    @signal syncStatus(type_, message) emitted to indicate the synchronization
+        status (string one of "bookmarks", "history", "passwords",
+        "useragents" or "speeddial", string)
+    @signal syncError(message) emitted for a general error with the error
+        message (string)
+    @signal syncMessage(message) emitted to send a message about
+        synchronization (string)
+    @signal syncFinished(type_, done, download) emitted after a
+        synchronization has finished (string one of "bookmarks", "history",
+        "passwords", "useragents" or "speeddial", boolean, boolean)
+    """
+    syncStatus = pyqtSignal(str, str)
+    syncError = pyqtSignal(str)
+    syncMessage = pyqtSignal(str)
+    syncFinished = pyqtSignal(str, bool, bool)
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object (QObject)
+        """
+        super(FtpSyncHandler, self).__init__(parent)
+        
+        self.__state = "idle"
+        self.__forceUpload = False
+        self.__connected = False
+        
+        self.__remoteFilesFound = {}
+    
+    def initialLoadAndCheck(self, forceUpload):
+        """
+        Public method to do the initial check.
+        
+        @keyparam forceUpload flag indicating a forced upload of the files
+            (boolean)
+        """
+        if not Preferences.getWebBrowser("SyncEnabled"):
+            return
+        
+        self.__state = "initializing"
+        self.__forceUpload = forceUpload
+        
+        self.__dirLineParser = FtpDirLineParser()
+        self.__remoteFilesFound = {}
+        
+        self.__idleTimer = QTimer(self)
+        self.__idleTimer.setInterval(
+            Preferences.getWebBrowser("SyncFtpIdleTimeout") * 1000)
+        self.__idleTimer.timeout.connect(self.__idleTimeout)
+        
+        self.__ftp = E5Ftp()
+        
+        # do proxy setup
+        if not Preferences.getUI("UseProxy"):
+            proxyType = E5FtpProxyType.NoProxy
+        else:
+            proxyType = Preferences.getUI("ProxyType/Ftp")
+        if proxyType != E5FtpProxyType.NoProxy:
+            self.__ftp.setProxy(
+                proxyType,
+                Preferences.getUI("ProxyHost/Ftp"),
+                Preferences.getUI("ProxyPort/Ftp"))
+            if proxyType != E5FtpProxyType.NonAuthorizing:
+                self.__ftp.setProxyAuthentication(
+                    Preferences.getUI("ProxyUser/Ftp"),
+                    Preferences.getUI("ProxyPassword/Ftp"),
+                    Preferences.getUI("ProxyAccount/Ftp"))
+        
+        QTimer.singleShot(0, self.__doFtpCommands)
+    
+    def __doFtpCommands(self):
+        """
+        Private slot executing the sequence of FTP commands.
+        """
+        try:
+            ok = self.__connectAndLogin()
+            if ok:
+                self.__changeToStore()
+                self.__ftp.retrlines("LIST", self.__dirListCallback)
+                self.__initialSync()
+                self.__state = "idle"
+                self.__idleTimer.start()
+        except (ftplib.all_errors + (E5FtpProxyError,)) as err:
+            self.syncError.emit(str(err))
+    
+    def __connectAndLogin(self):
+        """
+        Private method to connect to the FTP server and log in.
+        
+        @return flag indicating a successful log in (boolean)
+        """
+        self.__ftp.connect(
+            Preferences.getWebBrowser("SyncFtpServer"),
+            Preferences.getWebBrowser("SyncFtpPort"),
+            timeout=5)
+        self.__ftp.login(
+            Preferences.getWebBrowser("SyncFtpUser"),
+            Preferences.getWebBrowser("SyncFtpPassword"))
+        self.__connected = True
+        return True
+    
+    def __changeToStore(self):
+        """
+        Private slot to change to the storage directory.
+        
+        This action will create the storage path on the server, if it
+        does not exist. Upon return, the current directory of the server
+        is the sync directory.
+        """
+        storePathList = \
+            Preferences.getWebBrowser("SyncFtpPath")\
+            .replace("\\", "/").split("/")
+        if storePathList[0] == "":
+            storePathList.pop(0)
+        while storePathList:
+            path = storePathList[0]
+            try:
+                self.__ftp.cwd(path)
+            except ftplib.error_perm as err:
+                code = err.args[0].strip()[:3]
+                if code == "550":
+                    # path does not exist, create it
+                    self.__ftp.mkd(path)
+                    self.__ftp.cwd(path)
+                else:
+                    raise
+            storePathList.pop(0)
+    
+    def __dirListCallback(self, line):
+        """
+        Private slot handling the receipt of directory listing lines.
+        
+        @param line the received line of the directory listing (string)
+        """
+        try:
+            urlInfo = self.__dirLineParser.parseLine(line)
+        except FtpDirLineParserError:
+            # silently ignore parser errors
+            urlInfo = None
+        
+        if urlInfo and urlInfo.isValid() and urlInfo.isFile():
+            if urlInfo.name() in self._remoteFiles.values():
+                self.__remoteFilesFound[urlInfo.name()] = \
+                    urlInfo.lastModified()
+        
+        QCoreApplication.processEvents()
+    
+    def __downloadFile(self, type_, fileName, timestamp):
+        """
+        Private method to downlaod the given file.
+        
+        @param type_ type of the synchronization event (string one
+            of "bookmarks", "history", "passwords", "useragents" or
+            "speeddial")
+        @param fileName name of the file to be downloaded (string)
+        @param timestamp time stamp in seconds of the file to be downloaded
+            (integer)
+        """
+        self.syncStatus.emit(type_, self._messages[type_]["RemoteExists"])
+        buffer = io.BytesIO()
+        try:
+            self.__ftp.retrbinary(
+                "RETR {0}".format(self._remoteFiles[type_]),
+                lambda x: self.__downloadFileCallback(buffer, x))
+            ok, error = self.writeFile(
+                QByteArray(buffer.getvalue()), fileName, type_, timestamp)
+            if not ok:
+                self.syncStatus.emit(type_, error)
+            self.syncFinished.emit(type_, ok, True)
+        except ftplib.all_errors as err:
+            self.syncStatus.emit(type_, str(err))
+            self.syncFinished.emit(type_, False, True)
+    
+    def __downloadFileCallback(self, buffer, data):
+        """
+        Private method receiving the downloaded data.
+        
+        @param buffer reference to the buffer (io.BytesIO)
+        @param data byte string to store in the buffer (bytes)
+        @return number of bytes written to the buffer (integer)
+        """
+        res = buffer.write(data)
+        QCoreApplication.processEvents()
+        return res
+    
+    def __uploadFile(self, type_, fileName):
+        """
+        Private method to upload the given file.
+        
+        @param type_ type of the synchronization event (string one
+            of "bookmarks", "history", "passwords", "useragents" or
+            "speeddial")
+        @param fileName name of the file to be uploaded (string)
+        @return flag indicating success (boolean)
+        """
+        res = False
+        data = self.readFile(fileName, type_)
+        if data.isEmpty():
+            self.syncStatus.emit(type_, self._messages[type_]["LocalMissing"])
+            self.syncFinished.emit(type_, False, False)
+        else:
+            buffer = io.BytesIO(data.data())
+            try:
+                self.__ftp.storbinary(
+                    "STOR {0}".format(self._remoteFiles[type_]),
+                    buffer,
+                    callback=lambda x: QCoreApplication.processEvents())
+                self.syncFinished.emit(type_, True, False)
+                res = True
+            except ftplib.all_errors as err:
+                self.syncStatus.emit(type_, str(err))
+                self.syncFinished.emit(type_, False, False)
+        return res
+    
+    def __initialSyncFile(self, type_, fileName):
+        """
+        Private method to do the initial synchronization of the given file.
+        
+        @param type_ type of the synchronization event (string one
+            of "bookmarks", "history", "passwords", "useragents" or
+            "speeddial")
+        @param fileName name of the file to be synchronized (string)
+        """
+        if not self.__forceUpload and \
+           self._remoteFiles[type_] in self.__remoteFilesFound:
+            if QFileInfo(fileName).lastModified() < \
+               self.__remoteFilesFound[self._remoteFiles[type_]]:
+                self.__downloadFile(
+                    type_, fileName,
+                    self.__remoteFilesFound[self._remoteFiles[type_]]
+                        .toTime_t())
+            else:
+                self.syncStatus.emit(
+                    type_, self.tr("No synchronization required."))
+                self.syncFinished.emit(type_, True, True)
+        else:
+            if self._remoteFiles[type_] not in self.__remoteFilesFound:
+                self.syncStatus.emit(
+                    type_, self._messages[type_]["RemoteMissing"])
+            else:
+                self.syncStatus.emit(
+                    type_, self._messages[type_]["LocalNewer"])
+            self.__uploadFile(type_, fileName)
+    
+    def __initialSync(self):
+        """
+        Private slot to do the initial synchronization.
+        """
+        # Bookmarks
+        if Preferences.getWebBrowser("SyncBookmarks"):
+            self.__initialSyncFile(
+                "bookmarks",
+                WebBrowserWindow.bookmarksManager().getFileName())
+        
+        # History
+        if Preferences.getWebBrowser("SyncHistory"):
+            self.__initialSyncFile(
+                "history",
+                WebBrowserWindow.historyManager().getFileName())
+        
+        # Passwords
+        if Preferences.getWebBrowser("SyncPasswords"):
+            self.__initialSyncFile(
+                "passwords",
+                WebBrowserWindow.passwordManager().getFileName())
+        
+        # TODO: UserAgents
+        # User Agent Settings
+##        if Preferences.getWebBrowser("SyncUserAgents"):
+##            self.__initialSyncFile(
+##                "useragents",
+##                WebBrowserWindow.userAgentsManager().getFileName())
+        
+        # TODO: SpeedDial
+        # Speed Dial Settings
+##        if Preferences.getWebBrowser("SyncSpeedDial"):
+##            self.__initialSyncFile(
+##                "speeddial",
+##                WebBrowserWindow.speedDial().getFileName())
+        
+        self.__forceUpload = False
+    
+    def __syncFile(self, type_, fileName):
+        """
+        Private method to synchronize the given file.
+        
+        @param type_ type of the synchronization event (string one
+            of "bookmarks", "history", "passwords", "useragents" or
+            "speeddial")
+        @param fileName name of the file to be synchronized (string)
+        """
+        if self.__state == "initializing":
+            return
+        
+        # use idle timeout to check, if we are still connected
+        if self.__connected:
+            self.__idleTimeout()
+        if not self.__connected or self.__ftp.sock is None:
+            ok = self.__connectAndLogin()
+            if not ok:
+                self.syncStatus.emit(
+                    type_, self.tr("Cannot log in to FTP host."))
+                return
+        
+        # upload the changed file
+        self.__state = "uploading"
+        self.syncStatus.emit(type_, self._messages[type_]["Uploading"])
+        if self.__uploadFile(type_, fileName):
+            self.syncStatus.emit(
+                type_, self.tr("Synchronization finished."))
+        self.__state = "idle"
+    
+    def syncBookmarks(self):
+        """
+        Public method to synchronize the bookmarks.
+        """
+        self.__syncFile(
+            "bookmarks",
+            WebBrowserWindow.bookmarksManager().getFileName())
+    
+    def syncHistory(self):
+        """
+        Public method to synchronize the history.
+        """
+        self.__syncFile(
+            "history",
+            WebBrowserWindow.historyManager().getFileName())
+    
+    def syncPasswords(self):
+        """
+        Public method to synchronize the passwords.
+        """
+        self.__syncFile(
+            "passwords",
+            WebBrowserWindow.passwordManager().getFileName())
+    
+    # TODO: UserAgents
+    def syncUserAgents(self):
+        """
+        Public method to synchronize the user agents.
+        """
+##        self.__syncFile(
+##            "useragents",
+##            WebBrowserWindow.userAgentsManager().getFileName())
+    
+    # TODO: SpeedDial
+    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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,58 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the synchronization shared directory settings wizard page.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtWidgets import QWizardPage
+
+from E5Gui.E5PathPicker import E5PathPickerModes
+
+from .Ui_SyncDirectorySettingsPage import Ui_SyncDirectorySettingsPage
+
+import Preferences
+
+
+class SyncDirectorySettingsPage(QWizardPage, Ui_SyncDirectorySettingsPage):
+    """
+    Class implementing the shared directory host settings wizard page.
+    """
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(SyncDirectorySettingsPage, self).__init__(parent)
+        self.setupUi(self)
+        
+        self.directoryPicker.setMode(E5PathPickerModes.DirectoryMode)
+        self.directoryPicker.setText(
+            Preferences.getWebBrowser("SyncDirectoryPath"))
+        
+        self.directoryPicker.textChanged.connect(self.completeChanged)
+    
+    def nextId(self):
+        """
+        Public method returning the ID of the next wizard page.
+        
+        @return next wizard page ID (integer)
+        """
+        # save the settings
+        Preferences.setWebBrowser("SyncDirectoryPath", self.directoryPicker.text())
+        
+        from . import SyncGlobals
+        return SyncGlobals.PageCheck
+    
+    def isComplete(self):
+        """
+        Public method to check the completeness of the page.
+        
+        @return flag indicating completeness (boolean)
+        """
+        return self.directoryPicker.text() != ""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Sync/SyncDirectorySettingsPage.ui	Sun Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,71 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the synchronization FTP host settings wizard page.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtWidgets import QWizardPage
+
+from .Ui_SyncFtpSettingsPage import Ui_SyncFtpSettingsPage
+
+import Preferences
+
+
+class SyncFtpSettingsPage(QWizardPage, Ui_SyncFtpSettingsPage):
+    """
+    Class implementing the synchronization FTP host settings wizard page.
+    """
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(SyncFtpSettingsPage, self).__init__(parent)
+        self.setupUi(self)
+        
+        self.serverEdit.setText(Preferences.getWebBrowser("SyncFtpServer"))
+        self.userNameEdit.setText(Preferences.getWebBrowser("SyncFtpUser"))
+        self.passwordEdit.setText(Preferences.getWebBrowser("SyncFtpPassword"))
+        self.pathEdit.setText(Preferences.getWebBrowser("SyncFtpPath"))
+        self.portSpinBox.setValue(Preferences.getWebBrowser("SyncFtpPort"))
+        self.idleSpinBox.setValue(
+            Preferences.getWebBrowser("SyncFtpIdleTimeout"))
+        
+        self.serverEdit.textChanged.connect(self.completeChanged)
+        self.userNameEdit.textChanged.connect(self.completeChanged)
+        self.passwordEdit.textChanged.connect(self.completeChanged)
+        self.pathEdit.textChanged.connect(self.completeChanged)
+    
+    def nextId(self):
+        """
+        Public method returning the ID of the next wizard page.
+        
+        @return next wizard page ID (integer)
+        """
+        # save the settings
+        Preferences.setWebBrowser("SyncFtpServer", self.serverEdit.text())
+        Preferences.setWebBrowser("SyncFtpUser", self.userNameEdit.text())
+        Preferences.setWebBrowser("SyncFtpPassword", self.passwordEdit.text())
+        Preferences.setWebBrowser("SyncFtpPath", self.pathEdit.text())
+        Preferences.setWebBrowser("SyncFtpPort", self.portSpinBox.value())
+        Preferences.setWebBrowser("SyncFtpIdleTimeout", self.idleSpinBox.value())
+        
+        from . import SyncGlobals
+        return SyncGlobals.PageCheck
+    
+    def isComplete(self):
+        """
+        Public method to check the completeness of the page.
+        
+        @return flag indicating completeness (boolean)
+        """
+        return self.serverEdit.text() != "" and \
+            self.userNameEdit.text() != "" and \
+            self.passwordEdit.text() != "" and \
+            self.pathEdit.text() != ""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Sync/SyncFtpSettingsPage.ui	Sun Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,281 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the synchronization manager class.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import QObject, pyqtSignal
+
+import Preferences
+
+from WebBrowser.WebBrowserWindow import WebBrowserWindow
+
+
+class SyncManager(QObject):
+    """
+    Class implementing the synchronization manager.
+    
+    @signal syncError(message) emitted for a general error with the error
+        message (string)
+    @signal syncMessage(message) emitted to give status info about the sync
+        process (string)
+    @signal syncStatus(type_, message) emitted to indicate the synchronization
+        status (string one of "bookmarks", "history", "passwords",
+        "useragents" or "speeddial", string)
+    @signal syncFinished(type_, done, download) emitted after a
+        synchronization has finished (string one of "bookmarks", "history",
+        "passwords", "useragents" or "speeddial", boolean, boolean)
+    """
+    syncError = pyqtSignal(str)
+    syncMessage = pyqtSignal(str)
+    syncStatus = pyqtSignal(str, str)
+    syncFinished = pyqtSignal(str, bool, bool)
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object (QObject)
+        """
+        super(SyncManager, self).__init__(parent)
+        
+        self.__handler = None
+    
+    def handler(self):
+        """
+        Public method to get a reference to the sync handler object.
+        
+        @return reference to the sync handler object (SyncHandler)
+        """
+        return self.__handler
+    
+    def showSyncDialog(self):
+        """
+        Public method to show the synchronization dialog.
+        """
+        from .SyncAssistantDialog import SyncAssistantDialog
+        dlg = SyncAssistantDialog()
+        dlg.exec_()
+    
+    def loadSettings(self, forceUpload=False):
+        """
+        Public method to load the settings.
+        
+        @keyparam forceUpload flag indicating a forced upload of the files
+            (boolean)
+        """
+        if self.__handler is not None:
+            self.__handler.syncError.disconnect(self.__syncError)
+            self.__handler.syncFinished.disconnect(self.__syncFinished)
+            self.__handler.syncStatus.disconnect(self.__syncStatus)
+            self.__handler.syncMessage.disconnect(self.syncMessage)
+            self.__handler.shutdown()
+        
+        if self.syncEnabled():
+            from . import SyncGlobals
+            if Preferences.getWebBrowser("SyncType") == SyncGlobals.SyncTypeFtp:
+                from .FtpSyncHandler import FtpSyncHandler
+                self.__handler = FtpSyncHandler(self)
+            elif Preferences.getWebBrowser("SyncType") == \
+                    SyncGlobals.SyncTypeDirectory:
+                from .DirectorySyncHandler import DirectorySyncHandler
+                self.__handler = DirectorySyncHandler(self)
+            self.__handler.syncError.connect(self.__syncError)
+            self.__handler.syncFinished.connect(self.__syncFinished)
+            self.__handler.syncStatus.connect(self.__syncStatus)
+            self.__handler.syncMessage.connect(self.syncMessage)
+            
+            self.__handler.initialLoadAndCheck(forceUpload=forceUpload)
+            
+            # connect sync manager to bookmarks manager
+            if Preferences.getWebBrowser("SyncBookmarks"):
+                WebBrowserWindow.bookmarksManager()\
+                    .bookmarksSaved.connect(self.__syncBookmarks)
+            else:
+                try:
+                    WebBrowserWindow.bookmarksManager()\
+                        .bookmarksSaved.disconnect(self.__syncBookmarks)
+                except TypeError:
+                    pass
+            
+            # connect sync manager to history manager
+            if Preferences.getWebBrowser("SyncHistory"):
+                WebBrowserWindow.historyManager().historySaved\
+                    .connect(self.__syncHistory)
+            else:
+                try:
+                    WebBrowserWindow.historyManager()\
+                        .historySaved.disconnect(self.__syncHistory)
+                except TypeError:
+                    pass
+            
+            # connect sync manager to passwords manager
+            if Preferences.getWebBrowser("SyncPasswords"):
+                WebBrowserWindow.passwordManager()\
+                    .passwordsSaved.connect(self.__syncPasswords)
+            else:
+                try:
+                    WebBrowserWindow.passwordManager()\
+                        .passwordsSaved.disconnect(self.__syncPasswords)
+                except TypeError:
+                    pass
+            
+            # TODO: UserAgents
+            # connect sync manager to user agent manager
+##            if Preferences.getWebBrowser("SyncUserAgents"):
+##                WebBrowserWindow.userAgentsManager()\
+##                    .userAgentSettingsSaved.connect(self.__syncUserAgents)
+##            else:
+##                try:
+##                    WebBrowserWindow.userAgentsManager()\
+##                        .userAgentSettingsSaved.disconnect(
+##                            self.__syncUserAgents)
+##                except TypeError:
+##                    pass
+            
+            # TODO: SpeedDial
+            # connect sync manager to speed dial
+##            if Preferences.getWebBrowser("SyncSpeedDial"):
+##                WebBrowserWindow.speedDial()\
+##                    .speedDialSaved.connect(self.__syncSpeedDial)
+##            else:
+##                try:
+##                    WebBrowserWindow.speedDial()\
+##                        .speedDialSaved.disconnect(self.__syncSpeedDial)
+##                except TypeError:
+##                    pass
+        else:
+            self.__handler = None
+            
+            try:
+                WebBrowserWindow.bookmarksManager()\
+                    .bookmarksSaved.disconnect(self.__syncBookmarks)
+            except TypeError:
+                pass
+            try:
+                WebBrowserWindow.historyManager().historySaved\
+                    .disconnect(self.__syncHistory)
+            except TypeError:
+                pass
+            try:
+                WebBrowserWindow.passwordManager()\
+                    .passwordsSaved.disconnect(self.__syncPasswords)
+            except TypeError:
+                pass
+            # TODO: UserAgents
+##            try:
+##                WebBrowserWindow.userAgentsManager()\
+##                    .userAgentSettingsSaved.disconnect(self.__syncUserAgents)
+##            except TypeError:
+##                pass
+            # TODO: SpeedDial
+##            try:
+##                WebBrowserWindow.speedDial()\
+##                    .speedDialSaved.disconnect(self.__syncSpeedDial)
+##            except TypeError:
+##                pass
+    
+    def syncEnabled(self):
+        """
+        Public method to check, if synchronization is enabled.
+        
+        @return flag indicating enabled synchronization
+        """
+        from . import SyncGlobals
+        return Preferences.getWebBrowser("SyncEnabled") and \
+            Preferences.getWebBrowser("SyncType") != SyncGlobals.SyncTypeNone
+    
+    def __syncBookmarks(self):
+        """
+        Private slot to synchronize the bookmarks.
+        """
+        if self.__handler is not None:
+            self.__handler.syncBookmarks()
+    
+    def __syncHistory(self):
+        """
+        Private slot to synchronize the history.
+        """
+        if self.__handler is not None:
+            self.__handler.syncHistory()
+    
+    def __syncPasswords(self):
+        """
+        Private slot to synchronize the passwords.
+        """
+        if self.__handler is not None:
+            self.__handler.syncPasswords()
+    
+    def __syncUserAgents(self):
+        """
+        Private slot to synchronize the user agent settings.
+        """
+        if self.__handler is not None:
+            self.__handler.syncUserAgents()
+    
+    def __syncSpeedDial(self):
+        """
+        Private slot to synchronize the speed dial settings.
+        """
+        if self.__handler is not None:
+            self.__handler.syncSpeedDial()
+    
+    def __syncError(self, message):
+        """
+        Private slot to handle general synchronization issues.
+        
+        @param message error message (string)
+        """
+        self.syncError.emit(message)
+    
+    def __syncFinished(self, type_, status, download):
+        """
+        Private slot to handle a finished synchronization event.
+        
+        @param type_ type of the synchronization event (string one
+            of "bookmarks", "history", "passwords", "useragents" or
+            "speeddial")
+        @param status flag indicating success (boolean)
+        @param download flag indicating a download of a file (boolean)
+        """
+        if status and download:
+            if type_ == "bookmarks":
+                WebBrowserWindow.bookmarksManager().reload()
+            elif type_ == "history":
+                WebBrowserWindow.historyManager().reload()
+            elif type_ == "passwords":
+                WebBrowserWindow.passwordManager().reload()
+            # TODO: UserAgents
+##            elif type_ == "useragents":
+##                WebBrowserWindow.userAgentsManager().reload()
+            # TODO: SpeeedDial
+##            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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,410 @@
+# -*- 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 toggleMediaPause(pos):
+    """
+    Function generating a script to toggle the paused state of a media element.
+    
+    @param pos position of the media element
+    @type QPoint
+    @return script to toggle the element paused state
+    @rtype str
+    """
+    source = """
+        (function() {{
+            var e = document.elementFromPoint({0}, {1});
+            if (!e)
+                return;
+            if (e.paused)
+                e.play();
+            else
+                e.pause();
+        }})()"""
+    return source.format(pos.x(), pos.y())
+
+
+def toggleMediaMute(pos):
+    """
+    Function generating a script to toggle the mute state of a media element.
+    
+    @param pos position of the media element
+    @type QPoint
+    @return script to toggle the element mute state
+    @rtype str
+    """
+    source = """
+        (function() {[
+            var e = document.elementFromPoint({0}, {1});
+            if (!e)
+                return;
+            e.muted = !e.muted;
+        }})()"""
+    return source.format(pos.x(), pos.y())
+
+
+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.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)
+
+###########################################################################
+## 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 Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,177 @@
+# -*- 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
+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 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 Mar 06 14:12:58 2016 +0100
@@ -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/WebIconLoader.py	Sun Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,55 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing an object to load web site icons.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject
+from PyQt5.QtGui import QIcon, QPixmap, QImage
+
+from ..Network.FollowRedirectReply import FollowRedirectReply
+
+import WebBrowser.WebBrowserWindow
+
+
+class WebIconLoader(QObject):
+    """
+    Class implementing a loader for web site icons.
+    
+    @signal iconLoaded(icon) emitted when the con has been loaded
+    """
+    iconLoaded = pyqtSignal(QIcon)
+    
+    def __init__(self, url, parent=None):
+        """
+        Constructor
+        
+        @param url URL to fetch the icon from
+        @type QUrl
+        @param parent reference to the parent object
+        @type QObject
+        """
+        super(WebIconLoader, self).__init__(parent)
+        
+        networkManager = \
+            WebBrowser.WebBrowserWindow.WebBrowserWindow.networkManager()
+        self.__reply = FollowRedirectReply(url, networkManager)
+        self.__reply.finished.connect(self.__finished)
+    
+    @pyqtSlot()
+    def __finished(self):
+        """
+        Private slot handling the downloaded icon.
+        """
+        # ignore any errors and emit an empty icon in this case
+        data = self.__reply.readAll()
+        icon = QIcon(QPixmap.fromImage(QImage.fromData(data)))
+        self.iconLoaded.emit(icon)
+        
+        self.__reply.deleteLater()
+        self.__reply = None
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Tools/WebIconProvider.py	Sun Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,219 @@
+# -*- 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 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
+        
+        if self.__iconDatabasePath:
+            db = {}
+            for url, icon in self.__iconsDB.items():
+                ba = QByteArray()
+                buffer = QBuffer(ba)
+                buffer.open(QIODevice.WriteOnly)
+                icon.pixmap(32).toImage().save(buffer, "PNG")
+                db[url] = bytes(buffer.data()).decode(self.__encoding)
+            
+            filename = os.path.join(self.__iconDatabasePath,
+                                    self.__iconsFileName)
+            try:
+                f = open(filename, "w")
+                json.dump(db, f)
+                f.close()
+            except (IOError, OSError):
+                # ignore silentyl
+                pass
+    
+    def saveIcon(self, view):
+        """
+        Public method to save a web site icon.
+        
+        @param view reference to the view object
+        @type WebBrowserView
+        """
+        scheme = view.url().scheme()
+        if scheme in ["eric", "about", "qthelp", "file", "abp", "ftp"]:
+            return
+        
+        self.load()
+        
+        if view.mainWindow().isPrivate():
+            return
+        
+        urlStr = self.__urlToString(view.url())
+        self.__iconsDB[urlStr] = view.icon()
+        
+        self.changed.emit()
+    
+    def __urlToString(self, url):
+        """
+        Private method to convert an URL to a string.
+        
+        @param url URL to be converted
+        @type QUrl
+        @return string representation of the URL
+        @rtype str
+        """
+        return url.toString(QUrl.PrettyDecoded | QUrl.RemoveUserInfo |
+                            QUrl.RemoveFragment)
+    
+    def iconForUrl(self, url):
+        """
+        Public method to get an icon for an URL.
+        
+        @param url URL to get icon for
+        @type QUrl
+        @return icon for the URL
+        @rtype QIcon
+        """
+        scheme = url.scheme()
+        if scheme in ["eric", "about"]:
+            return UI.PixmapCache.getIcon("ericWeb.png")
+        elif scheme == "qthelp":
+            return UI.PixmapCache.getIcon("qthelp.png")
+        elif scheme == "file":
+            return UI.PixmapCache.getIcon("fileMisc.png")
+        elif scheme == "abp":
+            return UI.PixmapCache.getIcon("adBlockPlus.png")
+        elif scheme == "ftp":
+            return UI.PixmapCache.getIcon("network-server.png")
+        
+        self.load()
+        
+        urlStr = self.__urlToString(url)
+        if urlStr in self.__iconsDB:
+            return self.__iconsDB[urlStr]
+        elif scheme == "https":
+            return UI.PixmapCache.getIcon("securityHigh32.png")
+        else:
+            return UI.PixmapCache.getIcon("defaultIcon.png")
+    
+    def clear(self):
+        """
+        Public method to clear the icons cache.
+        """
+        self.load()
+        self.__iconsDB = {}
+        self.changed.emit()
+        self.__saveTimer.saveIfNeccessary()
+
+
+__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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,91 @@
+# -*- 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"))
+        
+        import WebBrowser.WebBrowserWindow
+        
+        if WebBrowser.WebBrowserWindow.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"))
+        
+        # TODO: SpeedDial
+        self.speeddialPushButton.setText("Future Speed Dial Entry")
+##        if WebBrowser.WebBrowserWindow.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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,444 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2010 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the URL bar widget.
+"""
+
+from __future__ import unicode_literals
+try:
+    str = unicode       # __IGNORE_EXCEPTION__
+except NameError:
+    pass
+
+from PyQt5.QtCore import pyqtSlot, Qt, QPointF, QUrl, QDateTime, QTimer, \
+    qVersion
+from PyQt5.QtGui import QColor, QPalette, QLinearGradient, QIcon
+from PyQt5.QtWidgets import QDialog, QApplication
+try:
+    from PyQt5.QtNetwork import QSslCertificate     # __IGNORE_EXCEPTION__
+except ImportError:
+    QSslCertificate = None      # __IGNORE_WARNING__
+from PyQt5.QtWebEngineWidgets import QWebEnginePage
+
+from E5Gui.E5LineEdit import E5LineEdit
+from E5Gui.E5LineEditButton import E5LineEditButton
+
+from WebBrowser.WebBrowserWindow import WebBrowserWindow
+
+from .FavIconLabel import FavIconLabel
+##from .SslLabel import SslLabel
+##
+import UI.PixmapCache
+import Preferences
+import Utilities
+
+
+class UrlBar(E5LineEdit):
+    """
+    Class implementing a line edit for entering URLs.
+    """
+    def __init__(self, mainWindow, parent=None):
+        """
+        Constructor
+        
+        @param mainWindow reference to the main window (HelpWindow)
+        @param parent reference to the parent widget (HelpBrowser)
+        """
+        E5LineEdit.__init__(self, parent)
+        self.setInactiveText(self.tr("Enter the URL here."))
+        self.setWhatsThis(self.tr("Enter the URL here."))
+        
+        self.__mw = mainWindow
+        self.__browser = None
+        self.__privateMode = WebBrowserWindow.isPrivate()
+        
+        self.__bmActiveIcon = UI.PixmapCache.getIcon("bookmark16.png")
+        self.__bmInactiveIcon = QIcon(
+            self.__bmActiveIcon.pixmap(16, 16, QIcon.Disabled))
+        
+        self.__favicon = FavIconLabel(self)
+        self.addWidget(self.__favicon, E5LineEdit.LeftSide)
+        
+        # TODO: SSL
+##        self.__sslLabel = SslLabel(self)
+##        self.addWidget(self.__sslLabel, E5LineEdit.LeftSide)
+##        self.__sslLabel.setVisible(False)
+        
+        self.__rssButton = E5LineEditButton(self)
+        self.__rssButton.setIcon(UI.PixmapCache.getIcon("rss16.png"))
+        self.addWidget(self.__rssButton, E5LineEdit.RightSide)
+        self.__rssButton.setVisible(False)
+        
+        self.__bookmarkButton = E5LineEditButton(self)
+        self.addWidget(self.__bookmarkButton, E5LineEdit.RightSide)
+        self.__bookmarkButton.setVisible(False)
+        
+        self.__clearButton = E5LineEditButton(self)
+        self.__clearButton.setIcon(UI.PixmapCache.getIcon("clearLeft.png"))
+        self.addWidget(self.__clearButton, E5LineEdit.RightSide)
+        self.__clearButton.setVisible(False)
+        
+        self.__bookmarkButton.clicked.connect(self.__showBookmarkInfo)
+        self.__rssButton.clicked.connect(self.__rssClicked)
+        self.__clearButton.clicked.connect(self.clear)
+        self.textChanged.connect(self.__textChanged)
+        
+        self.__mw.bookmarksManager().entryChanged.connect(
+            self.__bookmarkChanged)
+        self.__mw.bookmarksManager().entryAdded.connect(
+            self.__bookmarkChanged)
+        self.__mw.bookmarksManager().entryRemoved.connect(
+            self.__bookmarkChanged)
+        # TODO: Speed Dial
+##        self.__mw.speedDial().pagesChanged.connect(
+##            self.__bookmarkChanged)
+    
+    def setBrowser(self, browser):
+        """
+        Public method to set the browser connection.
+        
+        @param browser reference to the browser widget (WebBrowserView)
+        """
+        self.__browser = browser
+        self.__favicon.setBrowser(browser)
+        
+        self.__browser.urlChanged.connect(self.__browserUrlChanged)
+        self.__browser.loadProgress.connect(self.update)
+        self.__browser.loadFinished.connect(self.__loadFinished)
+        self.__browser.loadStarted.connect(self.__loadStarted)
+        
+        # TODO: SSL
+##        self.__sslLabel.clicked.connect(self.__browser.page().showSslInfo)
+    
+    def browser(self):
+        """
+        Public method to get the associated browser.
+       
+        @return reference to the associated browser (HelpBrowser)
+        """
+        return self.__browser
+    
+    def __browserUrlChanged(self, url):
+        """
+        Private slot to handle a URL change of the associated browser.
+        
+        @param url new URL of the browser (QUrl)
+        """
+        strUrl = url.toString()
+        if strUrl in ["eric:speeddial", "eric:home",
+                      "about:blank", "about:config"]:
+            strUrl = ""
+        
+        if self.text() != strUrl:
+            self.setText(strUrl)
+        self.setCursorPosition(0)
+    
+    def __loadStarted(self):
+        """
+        Private slot to perform actions before the page is loaded.
+        """
+        pass
+        # TODO: SSL
+##        self.__sslLabel.setVisible(False)
+        self.__bookmarkButton.setVisible(False)
+        self.__rssButton.setVisible(False)
+    
+    def __checkBookmark(self):
+        """
+        Private slot to check the current URL for the bookmarked state.
+        """
+        manager = self.__mw.bookmarksManager()
+        if manager.bookmarkForUrl(self.__browser.url()) is not None:
+            self.__bookmarkButton.setIcon(self.__bmActiveIcon)
+            bookmarks = manager.bookmarksForUrl(self.__browser.url())
+            from WebBrowser.Bookmarks.BookmarkNode import BookmarkNode
+            for bookmark in bookmarks:
+                manager.setTimestamp(bookmark, BookmarkNode.TsVisited,
+                                     QDateTime.currentDateTime())
+        # TODO: SpeedDial
+##        elif self.__mw.speedDial()\
+##                .pageForUrl(self.__browser.url()).url != "":
+##            self.__bookmarkButton.setIcon(self.__bmActiveIcon)
+        else:
+            self.__bookmarkButton.setIcon(self.__bmInactiveIcon)
+    
+    def __loadFinished(self, ok):
+        """
+        Private slot to set some data after the page was loaded.
+        
+        @param ok flag indicating a successful load (boolean)
+        """
+##        try:
+        if self.__browser.url().scheme() in ["eric", "about"]:
+            self.__bookmarkButton.setVisible(False)
+        else:
+            self.__checkBookmark()
+            self.__bookmarkButton.setVisible(True)
+        
+        if ok:
+            QTimer.singleShot(0, self.__setRssButton)
+        
+        # TODO: SSL certificate stuff (if possible)
+##        if ok and \
+##           self.__browser.url().scheme() == "https" and \
+##           QSslCertificate is not None:
+##            sslInfo = self.__browser.page().getSslCertificate()
+##            if sslInfo is not None:
+##                org = Utilities.decodeString(", ".join(
+##                    sslInfo.subjectInfo(QSslCertificate.Organization)))
+##                if org == "":
+##                    cn = Utilities.decodeString(", ".join(
+##                        sslInfo.subjectInfo(
+##                            QSslCertificate.CommonName)))
+##                    if cn != "":
+##                        org = cn.split(".", 1)[1]
+##                    if org == "":
+##                        org = self.tr("Unknown")
+##                self.__sslLabel.setText(" {0} ".format(org))
+##                self.__sslLabel.setVisible(True)
+##                valid = not sslInfo.isBlacklisted()
+##                if valid:
+##                    config = self.__browser.page().getSslConfiguration()
+##                    if config is None or config.sessionCipher().isNull():
+##                        valid = False
+##                self.__sslLabel.setValidity(valid)
+##                return
+##        
+##        self.__sslLabel.setVisible(False)
+##        except RuntimeError:
+##            pass
+    
+    def __textChanged(self, txt):
+        """
+        Private slot to handle changes of the text.
+        
+        @param txt current text (string)
+        """
+        self.__clearButton.setVisible(txt != "")
+    
+    def preferencesChanged(self):
+        """
+        Public slot to handle a change of preferences.
+        """
+        self.update()
+    
+    def __showBookmarkInfo(self):
+        """
+        Private slot to show a dialog with some bookmark info.
+        """
+        from .BookmarkActionSelectionDialog import \
+            BookmarkActionSelectionDialog
+        url = self.__browser.url()
+        dlg = BookmarkActionSelectionDialog(url)
+        if dlg.exec_() == QDialog.Accepted:
+            action = dlg.getAction()
+            if action == BookmarkActionSelectionDialog.AddBookmark:
+                self.__browser.addBookmark()
+            elif action == BookmarkActionSelectionDialog.EditBookmark:
+                bookmark = self.__mw.bookmarksManager()\
+                    .bookmarkForUrl(url)
+                from .BookmarkInfoDialog import BookmarkInfoDialog
+                dlg = BookmarkInfoDialog(bookmark, self.__browser)
+                dlg.exec_()
+            # TODO: SpeedDial
+##            elif action == BookmarkActionSelectionDialog.AddSpeeddial:
+##                self.__mw.speedDial().addPage(
+##                    url, self.__browser.title())
+##            elif action == BookmarkActionSelectionDialog.RemoveSpeeddial:
+##                self.__mw.speedDial().removePage(url)
+    
+    @pyqtSlot()
+    def __bookmarkChanged(self):
+        """
+        Private slot to handle bookmark or speed dial changes.
+        """
+        self.__checkBookmark()
+    
+    def paintEvent(self, evt):
+        """
+        Protected method handling a paint event.
+        
+        @param evt reference to the paint event (QPaintEvent)
+        """
+        if self.__privateMode:
+            backgroundColor = QColor(220, 220, 220)     # light gray
+            foregroundColor = Qt.black
+        else:
+            backgroundColor = QApplication.palette().color(QPalette.Base)
+            foregroundColor = QApplication.palette().color(QPalette.Text)
+        
+        if self.__browser is not None:
+            p = self.palette()
+            progress = self.__browser.progress()
+            if progress == 0 or progress == 100:
+                if self.__browser.url().scheme() == "https":
+##                    if QSslCertificate is not None:
+##                        if self.__browser.page().hasValidSslInfo():
+##                            backgroundColor = Preferences.getWebBrowser(
+##                                "SaveUrlColor")
+##                    else:
+                    backgroundColor = Preferences.getWebBrowser(
+                        "SaveUrlColor")
+                p.setBrush(QPalette.Base, backgroundColor)
+                p.setBrush(QPalette.Text, foregroundColor)
+            else:
+                if self.__browser.url().scheme() == "https":
+##                    if QSslCertificate is not None:
+##                        if self.__browser.page().hasValidSslInfo():
+##                            backgroundColor = Preferences.getWebBrowser(
+##                                "SaveUrlColor")
+##                    else:
+                    backgroundColor = Preferences.getWebBrowser(
+                        "SaveUrlColor")
+                highlight = QApplication.palette().color(QPalette.Highlight)
+                r = (highlight.red() + 2 * backgroundColor.red()) // 3
+                g = (highlight.green() + 2 * backgroundColor.green()) // 3
+                b = (highlight.blue() + 2 * backgroundColor.blue()) // 3
+                
+                loadingColor = QColor(r, g, b)
+                if abs(loadingColor.lightness() -
+                        backgroundColor.lightness()) < 20:
+                    # special handling for special color schemes (e.g Gaia)
+                    r = (2 * highlight.red() + backgroundColor.red()) // 3
+                    g = (2 * highlight.green() + backgroundColor.green()) // 3
+                    b = (2 * highlight.blue() + backgroundColor.blue()) // 3
+                    loadingColor = QColor(r, g, b)
+                
+                gradient = QLinearGradient(
+                    QPointF(0, 0), QPointF(self.width(), 0))
+                gradient.setColorAt(0, loadingColor)
+                gradient.setColorAt(progress / 100.0 - 0.000001, loadingColor)
+                gradient.setColorAt(progress / 100.0, backgroundColor)
+                p.setBrush(QPalette.Base, gradient)
+            
+            self.setPalette(p)
+        
+        E5LineEdit.paintEvent(self, evt)
+    
+    def focusOutEvent(self, evt):
+        """
+        Protected method to handle focus out event.
+        
+        @param evt reference to the focus event (QFocusEvent)
+        """
+        if self.text() == "" and self.__browser is not None:
+            self.__browserUrlChanged(self.__browser.url())
+        E5LineEdit.focusOutEvent(self, evt)
+    
+    def mousePressEvent(self, evt):
+        """
+        Protected method called by a mouse press event.
+        
+        @param evt reference to the mouse event (QMouseEvent)
+        """
+        if evt.button() == Qt.XButton1:
+            self.__mw.currentBrowser().triggerPageAction(
+                QWebEnginePage.Back)
+        elif evt.button() == Qt.XButton2:
+            self.__mw.currentBrowser().triggerPageAction(
+                QWebEnginePage.Forward)
+        else:
+            super(UrlBar, self).mousePressEvent(evt)
+    
+    def mouseDoubleClickEvent(self, evt):
+        """
+        Protected method to handle mouse double click events.
+        
+        @param evt reference to the mouse event (QMouseEvent)
+        """
+        if evt.button() == Qt.LeftButton:
+            self.selectAll()
+        else:
+            E5LineEdit.mouseDoubleClickEvent(self, evt)
+    
+    def keyPressEvent(self, evt):
+        """
+        Protected method to handle key presses.
+        
+        @param evt reference to the key press event (QKeyEvent)
+        """
+        if evt.key() == Qt.Key_Escape:
+            if self.__browser is not None:
+                self.setText(
+                    str(self.__browser.url().toEncoded(), encoding="utf-8"))
+                self.selectAll()
+            completer = self.completer()
+            if completer:
+                completer.popup().hide()
+            return
+        
+        currentText = self.text().strip()
+        if evt.key() in [Qt.Key_Enter, Qt.Key_Return] and \
+           not currentText.lower().startswith("http://"):
+            append = ""
+            if evt.modifiers() == Qt.KeyboardModifiers(Qt.ControlModifier):
+                append = ".com"
+            elif evt.modifiers() == Qt.KeyboardModifiers(
+                    Qt.ControlModifier | Qt.ShiftModifier):
+                append = ".org"
+            elif evt.modifiers() == Qt.KeyboardModifiers(Qt.ShiftModifier):
+                append = ".net"
+            
+            if append != "":
+                url = QUrl("http://www." + currentText)
+                host = url.host()
+                if not host.lower().endswith(append):
+                    host += append
+                    url.setHost(host)
+                    self.setText(url.toString())
+        
+        E5LineEdit.keyPressEvent(self, evt)
+    
+    def dragEnterEvent(self, evt):
+        """
+        Protected method to handle drag enter events.
+        
+        @param evt reference to the drag enter event (QDragEnterEvent)
+        """
+        mimeData = evt.mimeData()
+        if mimeData.hasUrls() or mimeData.hasText():
+            evt.acceptProposedAction()
+        
+        E5LineEdit.dragEnterEvent(self, evt)
+    
+    def dropEvent(self, evt):
+        """
+        Protected method to handle drop events.
+        
+        @param evt reference to the drop event (QDropEvent)
+        """
+        mimeData = evt.mimeData()
+        
+        url = QUrl()
+        if mimeData.hasUrls():
+            url = mimeData.urls()[0]
+        elif mimeData.hasText():
+            url = QUrl.fromEncoded(mimeData.text().encode("utf-8"),
+                                   QUrl.TolerantMode)
+        
+        if url.isEmpty() or not url.isValid():
+            E5LineEdit.dropEvent(self, evt)
+            return
+        
+        self.setText(str(url.toEncoded(), encoding="utf-8"))
+        self.selectAll()
+        
+        evt.acceptProposedAction()
+    
+    def __setRssButton(self):
+        """
+        Private slot to show the RSS button.
+        """
+        self.__rssButton.setVisible(self.__browser.checkRSS())
+    
+    def __rssClicked(self):
+        """
+        Private slot to handle clicking the RSS icon.
+        """
+        from WebBrowser.Feeds.FeedsDialog import FeedsDialog
+        feeds = self.__browser.getRSS()
+        dlg = FeedsDialog(feeds, self.__browser)
+        dlg.exec_()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/UrlBar/__init__.py	Sun Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2010 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Package implementing the URL bar widget.
+"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/VirusTotal/VirusTotalApi.py	Sun Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,416 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2011 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the <a href="http://www.virustotal.com">VirusTotal</a>
+API class.
+"""
+
+from __future__ import unicode_literals
+try:
+    str = unicode       # __IGNORE_EXCEPTION__
+except NameError:
+    pass
+
+import json
+
+from PyQt5.QtCore import QObject, QUrl, QByteArray, pyqtSignal, qVersion
+from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply
+
+from E5Gui import E5MessageBox
+
+import Preferences
+
+
+class VirusTotalAPI(QObject):
+    """
+    Class implementing the <a href="http://www.virustotal.com">VirusTotal</a>
+    API.
+    
+    @signal checkServiceKeyFinished(bool, str) emitted after the service key
+        check has been performed. It gives a flag indicating validity
+        (boolean) and an error message in case of a network error (string).
+    @signal submitUrlError(str) emitted with the error string, if the URL scan
+        submission returned an error.
+    @signal urlScanReport(str) emitted with the URL of the URL scan report page
+    @signal fileScanReport(str) emitted with the URL of the file scan report
+        page
+    """
+    checkServiceKeyFinished = pyqtSignal(bool, str)
+    submitUrlError = pyqtSignal(str)
+    urlScanReport = pyqtSignal(str)
+    fileScanReport = pyqtSignal(str)
+    
+    TestServiceKeyScanID = \
+        "4feed2c2e352f105f6188efd1d5a558f24aee6971bdf96d5fdb19c197d6d3fad"
+    
+    ServiceResult_ItemQueued = -2
+    ServiceResult_ItemNotPresent = 0
+    ServiceResult_ItemPresent = 1
+    
+    # HTTP Status Codes
+    ServiceCode_InvalidKey = 202
+    ServiceCode_RateLimitExceeded = 204
+    ServiceCode_InvalidPrivilege = 403
+    
+    GetFileReportPattern = "{0}://www.virustotal.com/vtapi/v2/file/report"
+    ScanUrlPattern = "{0}://www.virustotal.com/vtapi/v2/url/scan"
+    GetUrlReportPattern = "{0}://www.virustotal.com/vtapi/v2/url/report"
+    GetIpAddressReportPattern = \
+        "{0}://www.virustotal.com/vtapi/v2/ip-address/report"
+    GetDomainReportPattern = "{0}://www.virustotal.com/vtapi/v2/domain/report"
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent object (QObject)
+        """
+        super(VirusTotalAPI, self).__init__(parent)
+        
+        self.__replies = []
+        
+        self.__loadSettings()
+        
+        self.__lastIP = ""
+        self.__lastDomain = ""
+        self.__ipReportDlg = None
+        self.__domainReportDlg = None
+    
+    def __loadSettings(self):
+        """
+        Private method to load the settings.
+        """
+        if Preferences.getWebBrowser("VirusTotalSecure"):
+            protocol = "https"
+        else:
+            protocol = "http"
+        self.GetFileReportUrl = self.GetFileReportPattern.format(protocol)
+        self.ScanUrlUrl = self.ScanUrlPattern.format(protocol)
+        self.GetUrlReportUrl = self.GetUrlReportPattern.format(protocol)
+        self.GetIpAddressReportUrl = self.GetIpAddressReportPattern.format(
+            protocol)
+        self.GetDomainReportUrl = self.GetDomainReportPattern.format(protocol)
+        
+        self.errorMessages = {
+            204: self.tr("Request limit has been reached."),
+            0: self.tr("Requested item is not present."),
+            -2: self.tr("Requested item is still queued."),
+        }
+    
+    def preferencesChanged(self):
+        """
+        Public slot to handle a change of preferences.
+        """
+        self.__loadSettings()
+    
+    def checkServiceKeyValidity(self, key, protocol=""):
+        """
+        Public method to check the validity of the given service key.
+        
+        @param key service key (string)
+        @param protocol protocol used to access VirusTotal (string)
+        """
+        if protocol == "":
+            urlStr = self.GetFileReportUrl
+        else:
+            urlStr = self.GetFileReportPattern.format(protocol)
+        request = QNetworkRequest(QUrl(urlStr))
+        request.setHeader(QNetworkRequest.ContentTypeHeader,
+                          "application/x-www-form-urlencoded")
+        params = QByteArray("apikey={0}&resource={1}".format(
+            key, self.TestServiceKeyScanID).encode("utf-8"))
+        
+        import WebBrowser.WebBrowserWindow
+        nam = WebBrowser.WebBrowserWindow.WebBrowserWindow\
+            .networkManager()
+        reply = nam.post(request, params)
+        reply.finished.connect(self.__checkServiceKeyValidityFinished)
+        self.__replies.append(reply)
+    
+    def __checkServiceKeyValidityFinished(self):
+        """
+        Private slot to determine the result of the service key validity check.
+        """
+        res = False
+        msg = ""
+        
+        reply = self.sender()
+        if reply.error() == QNetworkReply.NoError:
+            res = True
+        elif reply.error() == self.ServiceCode_InvalidKey:
+            res = False
+        else:
+            msg = reply.errorString()
+        self.__replies.remove(reply)
+        reply.deleteLater()
+        
+        self.checkServiceKeyFinished.emit(res, msg)
+    
+    def submitUrl(self, url):
+        """
+        Public method to submit an URL to be scanned.
+        
+        @param url url to be scanned (QUrl)
+        """
+        request = QNetworkRequest(QUrl(self.ScanUrlUrl))
+        request.setHeader(QNetworkRequest.ContentTypeHeader,
+                          "application/x-www-form-urlencoded")
+        params = QByteArray("apikey={0}&url=".format(
+            Preferences.getWebBrowser("VirusTotalServiceKey")).encode("utf-8"))\
+            .append(QUrl.toPercentEncoding(url.toString()))
+        
+        import WebBrowser.WebBrowserWindow
+        nam = WebBrowser.WebBrowserWindow.WebBrowserWindow\
+            .networkManager()
+        reply = nam.post(request, params)
+        reply.finished.connect(self.__submitUrlFinished)
+        self.__replies.append(reply)
+    
+    def __submitUrlFinished(self):
+        """
+        Private slot to determine the result of the URL scan submission.
+        """
+        reply = self.sender()
+        if reply.error() == QNetworkReply.NoError:
+            result = json.loads(str(reply.readAll(), "utf-8"))
+            if result["response_code"] == self.ServiceResult_ItemPresent:
+                self.urlScanReport.emit(result["permalink"])
+                self.__getUrlScanReportUrl(result["scan_id"])
+            else:
+                if result["response_code"] in self.errorMessages:
+                    msg = self.errorMessages[result["response_code"]]
+                else:
+                    msg = result["verbose_msg"]
+                self.submitUrlError.emit(msg)
+        elif reply.error() == self.ServiceCode_RateLimitExceeded:
+            self.submitUrlError.emit(
+                self.errorMessages[result[self.ServiceCode_RateLimitExceeded]])
+        else:
+            self.submitUrlError.emit(reply.errorString())
+        self.__replies.remove(reply)
+        reply.deleteLater()
+    
+    def __getUrlScanReportUrl(self, scanId):
+        """
+        Private method to get the report URL for a URL scan.
+        
+        @param scanId ID of the scan to get the report URL for (string)
+        """
+        request = QNetworkRequest(QUrl(self.GetUrlReportUrl))
+        request.setHeader(QNetworkRequest.ContentTypeHeader,
+                          "application/x-www-form-urlencoded")
+        params = QByteArray("apikey={0}&resource={1}".format(
+            Preferences.getWebBrowser("VirusTotalServiceKey"), scanId)
+            .encode("utf-8"))
+        
+        import WebBrowser.WebBrowserWindow
+        nam = WebBrowser.WebBrowserWindow.WebBrowserWindow\
+            .networkManager()
+        reply = nam.post(request, params)
+        reply.finished.connect(self.__getUrlScanReportUrlFinished)
+        self.__replies.append(reply)
+    
+    def __getUrlScanReportUrlFinished(self):
+        """
+        Private slot to determine the result of the URL scan report URL
+        request.
+        """
+        reply = self.sender()
+        if reply.error() == QNetworkReply.NoError:
+            result = json.loads(str(reply.readAll(), "utf-8"))
+            if "filescan_id" in result and result["filescan_id"] is not None:
+                self.__getFileScanReportUrl(result["filescan_id"])
+        self.__replies.remove(reply)
+        reply.deleteLater()
+    
+    def __getFileScanReportUrl(self, scanId):
+        """
+        Private method to get the report URL for a file scan.
+        
+        @param scanId ID of the scan to get the report URL for (string)
+        """
+        request = QNetworkRequest(QUrl(self.GetFileReportUrl))
+        request.setHeader(QNetworkRequest.ContentTypeHeader,
+                          "application/x-www-form-urlencoded")
+        params = QByteArray("apikey={0}&resource={1}".format(
+            Preferences.getWebBrowser("VirusTotalServiceKey"), scanId)
+            .encode("utf-8"))
+        
+        import WebBrowser.WebBrowserWindow
+        nam = WebBrowser.WebBrowserWindow.WebBrowserWindow\
+            .networkManager()
+        reply = nam.post(request, params)
+        reply.finished.connect(self.__getFileScanReportUrlFinished)
+        self.__replies.append(reply)
+    
+    def __getFileScanReportUrlFinished(self):
+        """
+        Private slot to determine the result of the file scan report URL
+        request.
+        """
+        reply = self.sender()
+        if reply.error() == QNetworkReply.NoError:
+            result = json.loads(str(reply.readAll(), "utf-8"))
+            self.fileScanReport.emit(result["permalink"])
+        self.__replies.remove(reply)
+        reply.deleteLater()
+    
+    def getIpAddressReport(self, ipAddress):
+        """
+        Public method to retrieve a report for an IP address.
+        
+        @param ipAddress valid IPv4 address in dotted quad notation
+        @type str
+        """
+        self.__lastIP = ipAddress
+        
+        queryItems = [
+            ("apikey", Preferences.getWebBrowser("VirusTotalServiceKey")),
+            ("ip", ipAddress),
+        ]
+        url = QUrl(self.GetIpAddressReportUrl)
+        if qVersion() >= "5.0.0":
+            from PyQt5.QtCore import QUrlQuery
+            query = QUrlQuery()
+            query.setQueryItems(queryItems)
+            url.setQuery(query)
+        else:
+            url.setQueryItems(queryItems)
+        request = QNetworkRequest(url)
+        
+        import WebBrowser.WebBrowserWindow
+        nam = WebBrowser.WebBrowserWindow.WebBrowserWindow\
+            .networkManager()
+        reply = nam.get(request)
+        reply.finished.connect(self.__getIpAddressReportFinished)
+        self.__replies.append(reply)
+    
+    def __getIpAddressReportFinished(self):
+        """
+        Private slot to process the IP address report data.
+        """
+        reply = self.sender()
+        if reply.error() == QNetworkReply.NoError:
+            result = json.loads(str(reply.readAll(), "utf-8"))
+            if result["response_code"] == 0:
+                E5MessageBox.information(
+                    None,
+                    self.tr("VirusTotal IP Address Report"),
+                    self.tr("""VirusTotal does not have any information for"""
+                            """ the given IP address."""))
+            elif result["response_code"] == -1:
+                E5MessageBox.information(
+                    None,
+                    self.tr("VirusTotal IP Address Report"),
+                    self.tr("""The submitted IP address is invalid."""))
+            else:
+                owner = result["as_owner"]
+                resolutions = result["resolutions"]
+                try:
+                    urls = result["detected_urls"]
+                except KeyError:
+                    urls = []
+                
+                from .VirusTotalIpReportDialog import VirusTotalIpReportDialog
+                self.__ipReportDlg = VirusTotalIpReportDialog(
+                    self.__lastIP, owner, resolutions, urls)
+                self.__ipReportDlg.show()
+        self.__replies.remove(reply)
+        reply.deleteLater()
+    
+    def getDomainReport(self, domain):
+        """
+        Public method to retrieve a report for a domain.
+        
+        @param domain domain name
+        @type str
+        """
+        self.__lastDomain = domain
+        
+        queryItems = [
+            ("apikey", Preferences.getWebBrowser("VirusTotalServiceKey")),
+            ("domain", domain),
+        ]
+        url = QUrl(self.GetDomainReportUrl)
+        if qVersion() >= "5.0.0":
+            from PyQt5.QtCore import QUrlQuery
+            query = QUrlQuery()
+            query.setQueryItems(queryItems)
+            url.setQuery(query)
+        else:
+            url.setQueryItems(queryItems)
+        request = QNetworkRequest(url)
+        
+        import WebBrowser.WebBrowserWindow
+        nam = WebBrowser.WebBrowserWindow.WebBrowserWindow\
+            .networkManager()
+        reply = nam.get(request)
+        reply.finished.connect(self.__getDomainReportFinished)
+        self.__replies.append(reply)
+    
+    def __getDomainReportFinished(self):
+        """
+        Private slot to process the IP address report data.
+        """
+        reply = self.sender()
+        if reply.error() == QNetworkReply.NoError:
+            result = json.loads(str(reply.readAll(), "utf-8"))
+            if result["response_code"] == 0:
+                E5MessageBox.information(
+                    None,
+                    self.tr("VirusTotal Domain Report"),
+                    self.tr("""VirusTotal does not have any information for"""
+                            """ the given domain."""))
+            elif result["response_code"] == -1:
+                E5MessageBox.information(
+                    None,
+                    self.tr("VirusTotal Domain Report"),
+                    self.tr("""The submitted domain address is invalid."""))
+            else:
+                resolutions = result["resolutions"]
+                try:
+                    urls = result["detected_urls"]
+                except KeyError:
+                    urls = []
+                try:
+                    subdomains = result["subdomains"]
+                except KeyError:
+                    subdomains = []
+                try:
+                    bdCategory = result["BitDefender category"]
+                except KeyError:
+                    bdCategory = self.tr("not available")
+                try:
+                    tmCategory = result["TrendMicro category"]
+                except KeyError:
+                    tmCategory = self.tr("not available")
+                try:
+                    wtsCategory = result["Websense ThreatSeeker category"]
+                except KeyError:
+                    wtsCategory = self.tr("not available")
+                try:
+                    whois = result["whois"]
+                except KeyError:
+                    whois = ""
+                
+                from .VirusTotalDomainReportDialog import \
+                    VirusTotalDomainReportDialog
+                self.__domainReportDlg = VirusTotalDomainReportDialog(
+                    self.__lastDomain, resolutions, urls, subdomains,
+                    bdCategory, tmCategory, wtsCategory, whois)
+                self.__domainReportDlg.show()
+        self.__replies.remove(reply)
+        reply.deleteLater()
+    
+    def close(self):
+        """
+        Public slot to close the API.
+        """
+        for reply in self.__replies:
+            reply.abort()
+        
+        self.__ipReportDlg and self.__ipReportDlg.close()
+        self.__domainReportDlg and self.__domainReportDlg.close()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/VirusTotal/VirusTotalDomainReportDialog.py	Sun Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,72 @@
+# -*- 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) 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(),
+                historyPeriod)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/WebBrowserClearPrivateDataDialog.ui	Sun Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,282 @@
+<?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>353</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="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>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/WebBrowserLanguagesDialog.py	Sun Mar 06 14:12:58 2016 +0100
@@ -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, QByteArray, 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 QByteArray(", ".join(processed).encode("utf-8"))
+    
+    @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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,756 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2008 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+
+"""
+Module implementing the helpbrowser using QWebView.
+"""
+
+from __future__ import unicode_literals
+try:
+    str = unicode       # __IGNORE_EXCEPTION__
+except NameError:
+    pass
+
+from PyQt5.QtCore import pyqtSlot, pyqtSignal, QObject, QT_TRANSLATE_NOOP, \
+    QUrl, QBuffer, QIODevice, QFileInfo, Qt, QTimer, QEvent, \
+    QRect, QFile, QPoint, QByteArray, QEventLoop, qVersion
+from PyQt5.QtGui import QDesktopServices, QClipboard, QMouseEvent, QColor, \
+    QPalette
+from PyQt5.QtWidgets import qApp, QStyle, QMenu, QApplication, QInputDialog, \
+    QLineEdit, QLabel, QToolTip, QFrame, QDialog
+from PyQt5.QtPrintSupport import QPrinter, QPrintDialog
+from PyQt5.QtWebEngineWidgets import QWebEnginePage
+from PyQt5.QtWebChannel import QWebChannel
+from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest
+import sip
+
+from E5Gui import E5MessageBox, E5FileDialog
+
+import WebBrowser
+from WebBrowser.WebBrowserWindow import WebBrowserWindow
+
+from .JavaScript.ExternalJsObject import ExternalJsObject
+
+from .Tools.WebHitTestResult import WebHitTestResult
+from .Tools import Scripts
+
+import Preferences
+import UI.PixmapCache
+import Globals
+
+try:
+    from PyQt5.QtNetwork import QSslCertificate
+    SSL_AVAILABLE = True
+except ImportError:
+    SSL_AVAILABLE = False
+
+# TODO: ExternalJsObject: move this to the object
+###############################################################################
+##
+##
+##class JavaScriptEricObject(QObject):
+##    """
+##    Class implementing an external javascript object to search via the
+##    startpage.
+##    """
+##    # these must be in line with the strings used by the javascript part of
+##    # the start page
+##    translations = [
+##        QT_TRANSLATE_NOOP("JavaScriptEricObject",
+##                          "Welcome to eric6 Web Browser!"),
+##        QT_TRANSLATE_NOOP("JavaScriptEricObject", "eric6 Web Browser"),
+##        QT_TRANSLATE_NOOP("JavaScriptEricObject", "Search!"),
+##        QT_TRANSLATE_NOOP("JavaScriptEricObject", "About eric6"),
+##    ]
+##    
+##    def __init__(self, mw, parent=None):
+##        """
+##        Constructor
+##        
+##        @param mw reference to the main window 8HelpWindow)
+##        @param parent reference to the parent object (QObject)
+##        """
+##        super(JavaScriptEricObject, self).__init__(parent)
+##        
+##        self.__mw = mw
+##    
+##    @pyqtSlot(str, result=str)
+##    def translate(self, trans):
+##        """
+##        Public method to translate the given string.
+##        
+##        @param trans string to be translated (string)
+##        @return translation (string)
+##        """
+##        if trans == "QT_LAYOUT_DIRECTION":
+##            # special handling to detect layout direction
+##            if qApp.isLeftToRight():
+##                return "LTR"
+##            else:
+##                return "RTL"
+##        
+##        return self.tr(trans)
+##    
+##    @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.__mw.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.__mw.openSearchManager().currentEngine()
+##            .searchUrl(searchStr).toEncoded()).decode()
+##
+###############################################################################
+
+
+class WebBrowserPage(QWebEnginePage):
+    """
+    Class implementing an enhanced web page.
+    """
+##    _webPluginFactory = None
+##    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent parent widget of this window (QWidget)
+        """
+        super(WebBrowserPage, self).__init__(
+            WebBrowserWindow.webProfile(), parent)
+        
+        self.__setupWebChannel()
+        
+##        self.setPluginFactory(self.webPluginFactory())
+##        
+##        self.__lastRequest = None
+##        self.__lastRequestType = QWebPage.NavigationTypeOther
+##        
+##        from .Network.NetworkAccessManagerProxy import \
+##            NetworkAccessManagerProxy
+##        self.__proxy = NetworkAccessManagerProxy(self)
+##        self.__proxy.setWebPage(self)
+##        self.__proxy.setPrimaryNetworkAccessManager(
+##            WebBrowserWindow.networkManager())
+##        self.setNetworkAccessManager(self.__proxy)
+        
+        self.__sslConfiguration = None
+##        self.__proxy.finished.connect(self.__managerFinished)
+##        
+        self.__adBlockedEntries = []
+        self.loadStarted.connect(self.__loadStarted)
+##        
+##        self.saveFrameStateRequested.connect(
+##            self.__saveFrameStateRequested)
+##        self.restoreFrameStateRequested.connect(
+##            self.__restoreFrameStateRequested)
+        self.featurePermissionRequested.connect(
+            self.__featurePermissionRequested)
+        
+        self.authenticationRequired.connect(
+            WebBrowserWindow.networkManager().authentication)
+        
+        self.proxyAuthenticationRequired.connect(
+            WebBrowserWindow.networkManager().proxyAuthentication)
+    
+    def acceptNavigationRequest(self, url, type_, isMainFrame):
+        """
+        Public method to determine, if a request may be accepted.
+        
+        @param url URL to navigate to
+        @type QUrl
+        @param type_ type of the navigation request
+        @type QWebEnginePage.NavigationType
+        @param isMainFrame flag indicating, that the request originated from
+            the main frame
+        @type bool
+        @return flag indicating acceptance
+        @rtype bool
+        """
+##        self.__lastRequest = request
+##        if self.__lastRequest.url() != request.url() or \
+##           type_ != QWebPage.NavigationTypeOther:
+##            self.__lastRequestType = type_
+        
+        # TODO: Qt 5.6: move to handleUnknownProtocol
+        scheme = url.scheme()
+        if scheme == "mailto":
+            QDesktopServices.openUrl(url)
+            return False
+        
+        # AdBlock
+        if url.scheme() == "abp":
+            if WebBrowserWindow.adBlockManager().addSubscriptionFromUrl(url):
+                return False
+##        
+##        if type_ == QWebPage.NavigationTypeFormResubmitted:
+##            res = E5MessageBox.yesNo(
+##                self.view(),
+##                self.tr("Resending POST request"),
+##                self.tr(
+##                    """In order to display the site, the request along with"""
+##                    """ all the data must be sent once again, which may lead"""
+##                    """ to some unexpected behaviour of the site e.g. the"""
+##                    """ same action might be performed once again. Do you"""
+##                    """ want to continue anyway?"""),
+##                icon=E5MessageBox.Warning)
+##            if not res:
+##                return False
+        
+        return QWebEnginePage.acceptNavigationRequest(self, url, type_,
+                                                      isMainFrame)
+##    
+##    def populateNetworkRequest(self, request):
+##        """
+##        Public method to add data to a network request.
+##        
+##        @param request reference to the network request object
+##            (QNetworkRequest)
+##        """
+##        try:
+##            request.setAttribute(QNetworkRequest.User + 100, self)
+##            if self.__lastRequest.url() == request.url():
+##                request.setAttribute(QNetworkRequest.User + 101,
+##                                     self.__lastRequestType)
+##                if self.__lastRequestType == \
+##                        QWebPage.NavigationTypeLinkClicked:
+##                    request.setRawHeader(b"X-Eric6-UserLoadAction",
+##                                         QByteArray(b"1"))
+##        except TypeError:
+##            pass
+##    
+##    def pageAttributeId(self):
+##        """
+##        Public method to get the attribute id of the page attribute.
+##        
+##        @return attribute id of the page attribute (integer)
+##        """
+##        return QNetworkRequest.User + 100
+##    
+##    def supportsExtension(self, extension):
+##        """
+##        Public method to check the support for an extension.
+##        
+##        @param extension extension to test for (QWebPage.Extension)
+##        @return flag indicating the support of extension (boolean)
+##        """
+##        try:
+##            if extension in [QWebPage.ErrorPageExtension,
+##                             QWebPage.ChooseMultipleFilesExtension]:
+##                return True
+##        except AttributeError:
+##            pass
+##        
+##        return QWebPage.supportsExtension(self, extension)
+##    
+##    def extension(self, extension, option, output):
+##        """
+##        Public method to implement a specific extension.
+##        
+##        @param extension extension to be executed (QWebPage.Extension)
+##        @param option provides input to the extension
+##            (QWebPage.ExtensionOption)
+##        @param output stores the output results (QWebPage.ExtensionReturn)
+##        @return flag indicating a successful call of the extension (boolean)
+##        """
+##        if extension == QWebPage.ChooseMultipleFilesExtension:
+##            info = sip.cast(option,
+##                            QWebPage.ChooseMultipleFilesExtensionOption)
+##            files = sip.cast(output,
+##                             QWebPage.ChooseMultipleFilesExtensionReturn)
+##            if info is None or files is None:
+##                return super(HelpWebPage, self).extension(
+##                    extension, option, output)
+##            
+##            suggestedFileName = ""
+##            if info.suggestedFileNames:
+##                suggestedFileName = info.suggestedFileNames[0]
+##            
+##            files.fileNames = E5FileDialog.getOpenFileNames(
+##                None,
+##                self.tr("Select files to upload..."),
+##                suggestedFileName)
+##            return True
+##        
+##        if extension == QWebPage.ErrorPageExtension:
+##            info = sip.cast(option, QWebPage.ErrorPageExtensionOption)
+##            
+##            errorPage = sip.cast(output, QWebPage.ErrorPageExtensionReturn)
+##            urlString = bytes(info.url.toEncoded()).decode()
+##            errorPage.baseUrl = info.url
+##            if info.domain == QWebPage.QtNetwork and \
+##               info.error == QNetworkReply.ProtocolUnknownError:
+##                url = QUrl(info.url)
+##                res = E5MessageBox.yesNo(
+##                    None,
+##                    self.tr("Protocol Error"),
+##                    self.tr("""Open external application for {0}-link?\n"""
+##                            """URL: {1}""").format(
+##                        url.scheme(), url.toString(
+##                            QUrl.PrettyDecoded | QUrl.RemovePassword)),
+##                    yesDefault=True)
+##                
+##                if res:
+##                    QDesktopServices.openUrl(url)
+##                return True
+##            elif info.domain == QWebPage.QtNetwork and \
+##                info.error == QNetworkReply.ContentAccessDenied and \
+##                    info.errorString.startswith("AdBlockRule:"):
+##                if info.frame != info.frame.page().mainFrame():
+##                    # content in <iframe>
+##                    docElement = info.frame.page().mainFrame()\
+##                        .documentElement()
+##                    for element in docElement.findAll("iframe"):
+##                        src = element.attribute("src")
+##                        if src in info.url.toString():
+##                            element.setAttribute("style", "display:none;")
+##                    return False
+##                else:
+##                    # the whole page is blocked
+##                    rule = info.errorString.replace("AdBlockRule:", "")
+##                    title = self.tr("Content blocked by AdBlock Plus")
+##                    message = self.tr(
+##                        "Blocked by rule: <i>{0}</i>").format(rule)
+##                    
+##                    htmlFile = QFile(":/html/adblockPage.html")
+##                    htmlFile.open(QFile.ReadOnly)
+##                    html = htmlFile.readAll()
+##                    html = html.replace(
+##                        "@FAVICON@", "qrc:icons/adBlockPlus16.png")
+##                    html = html.replace(
+##                        "@IMAGE@", "qrc:icons/adBlockPlus64.png")
+##                    html = html.replace("@TITLE@", title.encode("utf8"))
+##                    html = html.replace("@MESSAGE@", message.encode("utf8"))
+##                    errorPage.content = html
+##                    return True
+##            
+##            if info.domain == QWebPage.QtNetwork and \
+##               info.error == QNetworkReply.OperationCanceledError and \
+##               info.errorString == "eric6:No Error":
+##                return False
+##            
+##            if info.domain == QWebPage.WebKit and info.error == 203:
+##                # "Loading is handled by the media engine"
+##                return False
+##            
+##            title = self.tr("Error loading page: {0}").format(urlString)
+##            htmlFile = QFile(":/html/notFoundPage.html")
+##            htmlFile.open(QFile.ReadOnly)
+##            html = htmlFile.readAll()
+##            pixmap = qApp.style()\
+##                .standardIcon(QStyle.SP_MessageBoxWarning).pixmap(48, 48)
+##            imageBuffer = QBuffer()
+##            imageBuffer.open(QIODevice.ReadWrite)
+##            if pixmap.save(imageBuffer, "PNG"):
+##                html = html.replace("@IMAGE@", imageBuffer.buffer().toBase64())
+##            pixmap = qApp.style()\
+##                .standardIcon(QStyle.SP_MessageBoxWarning).pixmap(16, 16)
+##            imageBuffer = QBuffer()
+##            imageBuffer.open(QIODevice.ReadWrite)
+##            if pixmap.save(imageBuffer, "PNG"):
+##                html = html.replace(
+##                    "@FAVICON@", imageBuffer.buffer().toBase64())
+##            html = html.replace("@TITLE@", title.encode("utf8"))
+##            html = html.replace("@H1@", info.errorString.encode("utf8"))
+##            html = html.replace(
+##                "@H2@", self.tr("When connecting to: {0}.")
+##                .format(urlString).encode("utf8"))
+##            html = html.replace(
+##                "@LI-1@",
+##                self.tr("Check the address for errors such as "
+##                        "<b>ww</b>.example.org instead of "
+##                        "<b>www</b>.example.org").encode("utf8"))
+##            html = html.replace(
+##                "@LI-2@",
+##                self.tr(
+##                    "If the address is correct, try checking the network "
+##                    "connection.").encode("utf8"))
+##            html = html.replace(
+##                "@LI-3@",
+##                self.tr(
+##                    "If your computer or network is protected by a firewall "
+##                    "or proxy, make sure that the browser is permitted to "
+##                    "access the network.").encode("utf8"))
+##            html = html.replace(
+##                "@LI-4@",
+##                self.tr("If your cache policy is set to offline browsing,"
+##                        "only pages in the local cache are available.")
+##                .encode("utf8"))
+##            html = html.replace(
+##                "@BUTTON@", self.tr("Try Again").encode("utf8"))
+##            errorPage.content = html
+##            return True
+##        
+##        return QWebPage.extension(self, extension, option, output)
+    
+    def __loadStarted(self):
+        """
+        Private slot to handle the loadStarted signal.
+        """
+        self.__adBlockedEntries = []
+##    
+##    def addAdBlockRule(self, rule, url):
+##        """
+##        Public slot to add an AdBlock rule to the page.
+##        
+##        @param rule AdBlock rule to add (AdBlockRule)
+##        @param url URL that matched the rule (QUrl)
+##        """
+##        from .AdBlock.AdBlockPage import AdBlockedPageEntry
+##        entry = AdBlockedPageEntry(rule, url)
+##        if entry not in self.__adBlockedEntries:
+##            self.__adBlockedEntries.append(entry)
+##    
+##    def getAdBlockedPageEntries(self):
+##        """
+##        Public method to get the list of AdBlock page entries.
+##        
+##        @return list of AdBlock page entries (list of AdBlockedPageEntry)
+##        """
+##        return self.__adBlockedEntries
+    
+    # TODO: User Agent Manager
+##    def userAgent(self, resolveEmpty=False):
+##        """
+##        Public method to get the global user agent setting.
+##        
+##        @param resolveEmpty flag indicating to resolve an empty
+##            user agent (boolean)
+##        @return user agent string (string)
+##        """
+##        agent = Preferences.getWebBrowser("UserAgent")
+##        if agent == "" and resolveEmpty:
+##            agent = self.userAgentForUrl(QUrl())
+##        return agent
+##    
+##    def setUserAgent(self, agent):
+##        """
+##        Public method to set the global user agent string.
+##        
+##        @param agent new current user agent string (string)
+##        """
+##        Preferences.setWebBrowser("UserAgent", agent)
+##    
+##    def userAgentForUrl(self, url):
+##        """
+##        Public method to determine the user agent for the given URL.
+##        
+##        @param url URL to determine user agent for (QUrl)
+##        @return user agent string (string)
+##        """
+##        agent = WebBrowserWindow.userAgentsManager().userAgentForUrl(url)
+##        if agent == "":
+##            # no agent string specified for the given host -> use global one
+##            agent = Preferences.getWebBrowser("UserAgent")
+##            if agent == "":
+##                # no global agent string specified -> use default one
+##                agent = QWebPage.userAgentForUrl(self, url)
+##        return agent
+##    
+    # TODO: SSL
+##    def __managerFinished(self, reply):
+##        """
+##        Private slot to handle a finished reply.
+##        
+##        This slot is used to get SSL related information for a reply.
+##        
+##        @param reply reference to the finished reply (QNetworkReply)
+##        """
+##        try:
+##            frame = reply.request().originatingObject()
+##        except AttributeError:
+##            frame = None
+##        
+##        mainFrameRequest = frame == self.mainFrame()
+##        
+##        if mainFrameRequest and \
+##           self.__sslConfiguration is not None and \
+##           reply.url() == self.mainFrame().url():
+##            self.__sslConfiguration = None
+##        
+##        if reply.error() == QNetworkReply.NoError and \
+##           mainFrameRequest and \
+##           self.__sslConfiguration is None and \
+##           reply.url().scheme().lower() == "https" and \
+##           reply.url() == self.mainFrame().url():
+##            self.__sslConfiguration = reply.sslConfiguration()
+##            self.__sslConfiguration.url = QUrl(reply.url())
+##        
+##        if reply.error() == QNetworkReply.NoError and \
+##           mainFrameRequest and \
+##           reply.url() == self.mainFrame().url():
+##            modified = reply.header(QNetworkRequest.LastModifiedHeader)
+##            if modified and modified.isValid():
+##                manager = WebBrowserWindow.bookmarksManager()
+##                from .Bookmarks.BookmarkNode import BookmarkNode
+##                for bookmark in manager.bookmarksForUrl(reply.url()):
+##                    manager.setTimestamp(bookmark, BookmarkNode.TsModified,
+##                                         modified)
+    
+##    def getSslCertificate(self):
+##        """
+##        Public method to get a reference to the SSL certificate.
+##        
+##        @return amended SSL certificate (QSslCertificate)
+##        """
+##        if self.__sslConfiguration is None:
+##            return None
+##        
+##        sslInfo = self.__sslConfiguration.peerCertificate()
+##        sslInfo.url = QUrl(self.__sslConfiguration.url)
+##        return sslInfo
+##    
+##    def getSslCertificateChain(self):
+##        """
+##        Public method to get a reference to the SSL certificate chain.
+##        
+##        @return SSL certificate chain (list of QSslCertificate)
+##        """
+##        if self.__sslConfiguration is None:
+##            return []
+##        
+##        chain = self.__sslConfiguration.peerCertificateChain()
+##        return chain
+##    
+##    def getSslConfiguration(self):
+##        """
+##        Public method to return a reference to the current SSL configuration.
+##        
+##        @return reference to the SSL configuration in use (QSslConfiguration)
+##        """
+##        return self.__sslConfiguration
+##    
+##    def showSslInfo(self, pos):
+##        """
+##        Public slot to show some SSL information for the loaded page.
+##        
+##        @param pos position to show the info at (QPoint)
+##        """
+##        if SSL_AVAILABLE and self.__sslConfiguration is not None:
+##            from E5Network.E5SslInfoWidget import E5SslInfoWidget
+##            widget = E5SslInfoWidget(
+##                self.mainFrame().url(), self.__sslConfiguration, self.view())
+##            widget.showAt(pos)
+##        else:
+##            E5MessageBox.warning(
+##                self.view(),
+##                self.tr("SSL Info"),
+##                self.tr("""This site does not contain SSL information."""))
+##    
+    def hasValidSslInfo(self):
+        """
+        Public method to check, if the page has a valid SSL certificate.
+        
+        @return flag indicating a valid SSL certificate (boolean)
+        """
+        if self.__sslConfiguration is None:
+            return False
+        
+        certList = self.__sslConfiguration.peerCertificateChain()
+        if not certList:
+            return False
+        
+        certificateDict = Globals.toDict(
+            Preferences.Prefs.settings.value("Ssl/CaCertificatesDict"))
+        for server in certificateDict:
+            localCAList = QSslCertificate.fromData(certificateDict[server])
+            for cert in certList:
+                if cert in localCAList:
+                    return True
+        
+        for cert in certList:
+            if cert.isBlacklisted():
+                return False
+        
+        return True
+    
+##    @classmethod
+##    def webPluginFactory(cls):
+##        """
+##        Class method to get a reference to the web plug-in factory
+##        instance.
+##        
+##        @return reference to the web plug-in factory instance (WebPluginFactory
+##        """
+##        if cls._webPluginFactory is None:
+##            from .WebPlugins.WebPluginFactory import WebPluginFactory
+##            cls._webPluginFactory = WebPluginFactory()
+##        
+##        return cls._webPluginFactory
+##    
+##    def event(self, evt):
+##        """
+##        Public method implementing the event handler.
+##        
+##        @param evt reference to the event (QEvent)
+##        @return flag indicating that the event was handled (boolean)
+##        """
+##        if evt.type() == QEvent.Leave:
+##            # Fake a mouse move event just outside of the widget to trigger
+##            # the WebKit event handler's mouseMoved function. This implements
+##            # the interesting mouse-out behavior like invalidating scrollbars.
+##            fakeEvent = QMouseEvent(QEvent.MouseMove, QPoint(0, -1),
+##                                    Qt.NoButton, Qt.NoButton, Qt.NoModifier)
+##            return super(HelpWebPage, self).event(fakeEvent)
+##        
+##        return super(HelpWebPage, self).event(evt)
+##    
+##    def __saveFrameStateRequested(self, frame, itm):
+##        """
+##        Private slot to save the page state (i.e. zoom level and scroll
+##        position).
+##        
+##        Note: Code is based on qutebrowser.
+##        
+##        @param frame frame to be saved
+##        @type QWebFrame
+##        @param itm web history item to be saved
+##        @type QWebHistoryItem
+##        """
+##        try:
+##            if frame != self.mainFrame():
+##                return
+##        except RuntimeError:
+##            # With Qt 5.2.1 (Ubuntu Trusty) we get this when closing a tab:
+##            #     RuntimeError: wrapped C/C++ object of type BrowserPage has
+##            #     been deleted
+##            # Since the information here isn't that important for closing web
+##            # views anyways, we ignore this error.
+##            return
+##        data = {
+##            'zoom': frame.zoomFactor(),
+##            'scrollPos': frame.scrollPosition(),
+##        }
+##        itm.setUserData(data)
+##    
+##    def __restoreFrameStateRequested(self, frame):
+##        """
+##        Private slot to restore scroll position and zoom level from
+##        history.
+##        
+##        Note: Code is based on qutebrowser.
+##        
+##        @param frame frame to be restored
+##        @type QWebFrame
+##        """
+##        if frame != self.mainFrame():
+##            return
+##        
+##        data = self.history().currentItem().userData()
+##        if data is None:
+##            return
+##        
+##        if 'zoom' in data:
+##            frame.page().view().setZoomValue(int(data['zoom'] * 100),
+##                                             saveValue=False)
+##        
+##        if 'scrollPos' in data and frame.scrollPosition() == QPoint(0, 0):
+##            frame.setScrollPosition(data['scrollPos'])
+    
+    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())
+    
+    ##############################################
+    ## Methods below deal with JavaScript messages
+    ##############################################
+    
+    # TODO: JavaScript messages: do this right and add the others
+    def javaScriptConsoleMessage(self, level, message, lineNumber,  sourceId):
+        print("JS-console:", message, lineNumber, sourceId)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/WebBrowserSnap.py	Sun Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,165 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2010 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a specialized tab bar for the web browser.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import Qt, QPoint, QTimer, QEvent
+from PyQt5.QtWidgets import QFrame, QLabel
+
+from E5Gui.E5TabWidget import E5WheelTabBar
+from E5Gui.E5PassivePopup import E5PassivePopup
+
+import Preferences
+
+
+class WebBrowserTabBar(E5WheelTabBar):
+    """
+    Class implementing the tab bar of the web browser.
+    """
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (WebBrowserTabWidget)
+        """
+        super(WebBrowserTabBar, self).__init__(parent)
+        
+        self.__tabWidget = parent
+        
+        self.__previewPopup = None
+        self.__currentTabPreviewIndex = -1
+        
+        self.setMouseTracking(True)
+    
+    def __showTabPreview(self):
+        """
+        Private slot to show the tab preview.
+        """
+        indexedBrowser = self.__tabWidget.browserAt(
+            self.__currentTabPreviewIndex)
+        currentBrowser = self.__tabWidget.currentBrowser()
+        
+        if indexedBrowser is None or currentBrowser is None:
+            return
+        
+        # no previews during load
+        if indexedBrowser.progress() != 0:
+            return
+        
+        w = self.tabSizeHint(self.__currentTabPreviewIndex).width()
+        h = int(w * currentBrowser.height() / currentBrowser.width())
+        
+        self.__previewPopup = E5PassivePopup(self)
+        self.__previewPopup.setFrameShape(QFrame.StyledPanel)
+        self.__previewPopup.setFrameShadow(QFrame.Plain)
+        self.__previewPopup.setFixedSize(w, h)
+        
+        from .WebBrowserSnap import renderTabPreview
+        label = QLabel()
+        label.setPixmap(renderTabPreview(indexedBrowser, w, h))
+        
+        self.__previewPopup.setView(label)
+        self.__previewPopup.layout().setAlignment(Qt.AlignTop)
+        self.__previewPopup.layout().setContentsMargins(0, 0, 0, 0)
+        
+        tr = self.tabRect(self.__currentTabPreviewIndex)
+        pos = QPoint(tr.x(), tr.y() + tr.height())
+        
+        self.__previewPopup.show(self.mapToGlobal(pos))
+    
+    def mouseMoveEvent(self, evt):
+        """
+        Protected method to handle mouse move events.
+        
+        @param evt reference to the mouse move event (QMouseEvent)
+        """
+        if self.count() == 1:
+            return
+        
+        super(WebBrowserTabBar, self).mouseMoveEvent(evt)
+        
+        if Preferences.getWebBrowser("ShowPreview"):
+            # Find the tab under the mouse
+            i = 0
+            tabIndex = -1
+            while i < self.count() and tabIndex == -1:
+                if self.tabRect(i).contains(evt.pos()):
+                    tabIndex = i
+                i += 1
+            
+            # If found and not the current tab then show tab preview
+            if tabIndex != -1 and \
+               tabIndex != self.currentIndex() and \
+               self.__currentTabPreviewIndex != tabIndex and \
+               evt.buttons() == Qt.NoButton:
+                self.__currentTabPreviewIndex = tabIndex
+                QTimer.singleShot(200, self.__showTabPreview)
+            
+            # If current tab or not found then hide previous tab preview
+            if tabIndex == self.currentIndex() or \
+               tabIndex == -1:
+                if self.__previewPopup is not None:
+                    self.__previewPopup.hide()
+                self.__currentTabPreviewIndex = -1
+    
+    def leaveEvent(self, evt):
+        """
+        Protected method to handle leave events.
+        
+        @param evt reference to the leave event (QEvent)
+        """
+        if Preferences.getWebBrowser("ShowPreview"):
+            # If leave tabwidget then hide previous tab preview
+            if self.__previewPopup is not None:
+                self.__previewPopup.hide()
+            self.__currentTabPreviewIndex = -1
+        
+        super(WebBrowserTabBar, self).leaveEvent(evt)
+    
+    def mousePressEvent(self, evt):
+        """
+        Protected method to handle mouse press events.
+        
+        @param evt reference to the mouse press event (QMouseEvent)
+        """
+        if Preferences.getWebBrowser("ShowPreview"):
+            if self.__previewPopup is not None:
+                self.__previewPopup.hide()
+            self.__currentTabPreviewIndex = -1
+        
+        super(WebBrowserTabBar, self).mousePressEvent(evt)
+    
+    def event(self, evt):
+        """
+        Public method to handle event.
+        
+        This event handler just handles the tooltip event and passes the
+        handling of all others to the superclass.
+        
+        @param evt reference to the event to be handled (QEvent)
+        @return flag indicating, if the event was handled (boolean)
+        """
+        if evt.type() == QEvent.ToolTip and \
+           Preferences.getWebBrowser("ShowPreview"):
+            # suppress tool tips if we are showing previews
+            evt.setAccepted(True)
+            return True
+        
+        return super(WebBrowserTabBar, self).event(evt)
+    
+    def tabRemoved(self, index):
+        """
+        Public slot to handle the removal of a tab.
+        
+        @param index index of the removed tab (integer)
+        """
+        if Preferences.getWebBrowser("ShowPreview"):
+            if self.__previewPopup is not None:
+                self.__previewPopup.hide()
+            self.__currentTabPreviewIndex = -1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/WebBrowserTabWidget.py	Sun Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,983 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2010 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the central widget showing the web pages.
+"""
+
+from __future__ import unicode_literals
+
+import os
+
+from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QUrl
+from PyQt5.QtGui import QIcon
+from PyQt5.QtWidgets import QWidget, QHBoxLayout, QMenu, QToolButton, QDialog
+from PyQt5.QtPrintSupport import QPrinter, QPrintDialog
+##from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest
+
+from E5Gui.E5TabWidget import E5TabWidget
+from E5Gui import E5MessageBox
+from E5Gui.E5Application import e5App
+
+from .WebBrowserView import WebBrowserView
+from .Tools import WebBrowserTools
+from . import WebInspector
+
+import UI.PixmapCache
+
+import Utilities
+import Preferences
+import Globals
+
+from eric6config import getConfig
+
+
+class WebBrowserTabWidget(E5TabWidget):
+    """
+    Class implementing the central widget showing the web pages.
+    
+    @signal sourceChanged(WebBrowserView, QUrl) emitted after the URL of a
+        browser has changed
+    @signal titleChanged(WebBrowserView, str) emitted after the title of a
+        browser has changed
+    @signal showMessage(str) emitted to show a message in the main window
+        status bar
+    @signal browserClosed(QWidget) emitted after a browser was closed
+    @signal browserZoomValueChanged(int) emitted to signal a change of the
+        current browser's zoom level
+    """
+    sourceChanged = pyqtSignal(WebBrowserView, QUrl)
+    titleChanged = pyqtSignal(WebBrowserView, str)
+    showMessage = pyqtSignal(str)
+    browserClosed = pyqtSignal(QWidget)
+    browserZoomValueChanged = pyqtSignal(int)
+    
+    def __init__(self, parent):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (QWidget)
+        """
+        super(WebBrowserTabWidget, self).__init__(parent, dnd=True)
+        
+        from .WebBrowserTabBar import WebBrowserTabBar
+        self.__tabBar = WebBrowserTabBar(self)
+        self.setCustomTabBar(True, self.__tabBar)
+        
+        self.__mainWindow = parent
+        
+        self.setUsesScrollButtons(True)
+        self.setDocumentMode(True)
+        self.setElideMode(Qt.ElideNone)
+        
+        from .ClosedTabsManager import ClosedTabsManager
+        self.__closedTabsManager = ClosedTabsManager(self)
+        self.__closedTabsManager.closedTabAvailable.connect(
+            self.__closedTabAvailable)
+        
+        from .UrlBar.StackedUrlBar import StackedUrlBar
+        self.__stackedUrlBar = StackedUrlBar(self)
+        self.__tabBar.tabMoved.connect(self.__stackedUrlBar.moveBar)
+        
+        self.__tabContextMenuIndex = -1
+        self.currentChanged[int].connect(self.__currentChanged)
+        self.setTabContextMenuPolicy(Qt.CustomContextMenu)
+        self.customTabContextMenuRequested.connect(self.__showContextMenu)
+        
+        self.__rightCornerWidget = QWidget(self)
+        self.__rightCornerWidgetLayout = QHBoxLayout(self.__rightCornerWidget)
+        self.__rightCornerWidgetLayout.setContentsMargins(0, 0, 0, 0)
+        self.__rightCornerWidgetLayout.setSpacing(0)
+        
+        self.__navigationMenu = QMenu(self)
+        self.__navigationMenu.aboutToShow.connect(self.__showNavigationMenu)
+        self.__navigationMenu.triggered.connect(self.__navigationMenuTriggered)
+        
+        self.__navigationButton = QToolButton(self)
+        self.__navigationButton.setIcon(
+            UI.PixmapCache.getIcon("1downarrow.png"))
+        self.__navigationButton.setToolTip(
+            self.tr("Show a navigation menu"))
+        self.__navigationButton.setPopupMode(QToolButton.InstantPopup)
+        self.__navigationButton.setMenu(self.__navigationMenu)
+        self.__navigationButton.setEnabled(False)
+        self.__rightCornerWidgetLayout.addWidget(self.__navigationButton)
+        
+        self.__closedTabsMenu = QMenu(self)
+        self.__closedTabsMenu.aboutToShow.connect(
+            self.__aboutToShowClosedTabsMenu)
+        
+        self.__closedTabsButton = QToolButton(self)
+        self.__closedTabsButton.setIcon(UI.PixmapCache.getIcon("trash.png"))
+        self.__closedTabsButton.setToolTip(
+            self.tr("Show a navigation menu for closed tabs"))
+        self.__closedTabsButton.setPopupMode(QToolButton.InstantPopup)
+        self.__closedTabsButton.setMenu(self.__closedTabsMenu)
+        self.__closedTabsButton.setEnabled(False)
+        self.__rightCornerWidgetLayout.addWidget(self.__closedTabsButton)
+        
+        self.__closeButton = QToolButton(self)
+        self.__closeButton.setIcon(UI.PixmapCache.getIcon("close.png"))
+        self.__closeButton.setToolTip(
+            self.tr("Close the current web browser"))
+        self.__closeButton.setEnabled(False)
+        self.__closeButton.clicked.connect(self.closeBrowser)
+        self.__rightCornerWidgetLayout.addWidget(self.__closeButton)
+        if Preferences.getUI("SingleCloseButton") or \
+           not hasattr(self, 'setTabsClosable'):
+            self.__closeButton.show()
+        else:
+            self.setTabsClosable(True)
+            self.tabCloseRequested.connect(self.closeBrowserAt)
+            self.__closeButton.hide()
+        
+        self.setCornerWidget(self.__rightCornerWidget, Qt.TopRightCorner)
+        
+        self.__newTabButton = QToolButton(self)
+        self.__newTabButton.setIcon(UI.PixmapCache.getIcon("plus.png"))
+        self.__newTabButton.setToolTip(
+            self.tr("Open a new web browser tab"))
+        self.setCornerWidget(self.__newTabButton, Qt.TopLeftCorner)
+        self.__newTabButton.clicked.connect(self.__newBrowser)
+        
+        self.__initTabContextMenu()
+        
+        self.__historyCompleter = None
+    
+    def __initTabContextMenu(self):
+        """
+        Private method to create the tab context menu.
+        """
+        self.__tabContextMenu = QMenu(self)
+        self.tabContextNewAct = self.__tabContextMenu.addAction(
+            UI.PixmapCache.getIcon("tabNew.png"),
+            self.tr('New Tab'), self.newBrowser)
+        self.__tabContextMenu.addSeparator()
+        self.leftMenuAct = self.__tabContextMenu.addAction(
+            UI.PixmapCache.getIcon("1leftarrow.png"),
+            self.tr('Move Left'), self.__tabContextMenuMoveLeft)
+        self.rightMenuAct = self.__tabContextMenu.addAction(
+            UI.PixmapCache.getIcon("1rightarrow.png"),
+            self.tr('Move Right'), self.__tabContextMenuMoveRight)
+        self.__tabContextMenu.addSeparator()
+        self.tabContextCloneAct = self.__tabContextMenu.addAction(
+            self.tr("Duplicate Page"), self.__tabContextMenuClone)
+        self.__tabContextMenu.addSeparator()
+        self.tabContextCloseAct = self.__tabContextMenu.addAction(
+            UI.PixmapCache.getIcon("tabClose.png"),
+            self.tr('Close'), self.__tabContextMenuClose)
+        self.tabContextCloseOthersAct = self.__tabContextMenu.addAction(
+            UI.PixmapCache.getIcon("tabCloseOther.png"),
+            self.tr("Close Others"), self.__tabContextMenuCloseOthers)
+        self.__tabContextMenu.addAction(
+            self.tr('Close All'), self.closeAllBrowsers)
+        self.__tabContextMenu.addSeparator()
+        self.__tabContextMenu.addAction(
+            UI.PixmapCache.getIcon("printPreview.png"),
+            self.tr('Print Preview'), self.__tabContextMenuPrintPreview)
+        self.__tabContextMenu.addAction(
+            UI.PixmapCache.getIcon("print.png"),
+            self.tr('Print'), self.__tabContextMenuPrint)
+        if Globals.isLinuxPlatform():
+            self.__tabContextMenu.addAction(
+                UI.PixmapCache.getIcon("printPdf.png"),
+                self.tr('Print as PDF'), self.__tabContextMenuPrintPdf)
+        self.__tabContextMenu.addSeparator()
+        self.__tabContextMenu.addAction(
+            UI.PixmapCache.getIcon("reload.png"),
+            self.tr('Reload All'), self.reloadAllBrowsers)
+        self.__tabContextMenu.addSeparator()
+        self.__tabContextMenu.addAction(
+            UI.PixmapCache.getIcon("addBookmark.png"),
+            self.tr('Bookmark All Tabs'), self.__mainWindow.bookmarkAll)
+        
+        self.__tabBackContextMenu = QMenu(self)
+        self.__tabBackContextMenu.addAction(
+            self.tr('Close All'), self.closeAllBrowsers)
+        self.__tabBackContextMenu.addAction(
+            UI.PixmapCache.getIcon("reload.png"),
+            self.tr('Reload All'), self.reloadAllBrowsers)
+        self.__tabBackContextMenu.addAction(
+            UI.PixmapCache.getIcon("addBookmark.png"),
+            self.tr('Bookmark All Tabs'), self.__mainWindow.bookmarkAll)
+        self.__tabBackContextMenu.addSeparator()
+        self.__restoreClosedTabAct = self.__tabBackContextMenu.addAction(
+            UI.PixmapCache.getIcon("trash.png"),
+            self.tr('Restore Closed Tab'), self.restoreClosedTab)
+        self.__restoreClosedTabAct.setEnabled(False)
+        self.__restoreClosedTabAct.setData(0)
+    
+    def __showContextMenu(self, coord, index):
+        """
+        Private slot to show the tab context menu.
+        
+        @param coord the position of the mouse pointer (QPoint)
+        @param index index of the tab the menu is requested for (integer)
+        """
+        coord = self.mapToGlobal(coord)
+        if index == -1:
+            self.__tabBackContextMenu.popup(coord)
+        else:
+            self.__tabContextMenuIndex = index
+            self.leftMenuAct.setEnabled(index > 0)
+            self.rightMenuAct.setEnabled(index < self.count() - 1)
+            
+            self.tabContextCloseOthersAct.setEnabled(self.count() > 1)
+            
+            self.__tabContextMenu.popup(coord)
+    
+    def __tabContextMenuMoveLeft(self):
+        """
+        Private method to move a tab one position to the left.
+        """
+        self.moveTab(self.__tabContextMenuIndex,
+                     self.__tabContextMenuIndex - 1)
+    
+    def __tabContextMenuMoveRight(self):
+        """
+        Private method to move a tab one position to the right.
+        """
+        self.moveTab(self.__tabContextMenuIndex,
+                     self.__tabContextMenuIndex + 1)
+    
+    def __tabContextMenuClone(self):
+        """
+        Private method to clone the selected tab.
+        """
+        idx = self.__tabContextMenuIndex
+        if idx < 0:
+            idx = self.currentIndex()
+        if idx < 0 or idx > self.count():
+            return
+        
+##        req = QNetworkRequest(self.widget(idx).url())
+##        req.setRawHeader(b"X-Eric6-UserLoadAction", b"1")
+##        self.newBrowser(None, (req, QNetworkAccessManager.GetOperation, b""))
+        url = self.widget(idx).url()
+        self.newBrowser(url)
+    
+    def __tabContextMenuClose(self):
+        """
+        Private method to close the selected tab.
+        """
+        self.closeBrowserAt(self.__tabContextMenuIndex)
+    
+    def __tabContextMenuCloseOthers(self):
+        """
+        Private slot to close all other tabs.
+        """
+        index = self.__tabContextMenuIndex
+        for i in list(range(self.count() - 1, index, -1)) + \
+                list(range(index - 1, -1, -1)):
+            self.closeBrowserAt(i)
+    
+    def __tabContextMenuPrint(self):
+        """
+        Private method to print the selected tab.
+        """
+        browser = self.widget(self.__tabContextMenuIndex)
+        self.printBrowser(browser)
+    
+    def __tabContextMenuPrintPdf(self):
+        """
+        Private method to print the selected tab as PDF.
+        """
+        browser = self.widget(self.__tabContextMenuIndex)
+        self.printBrowserPdf(browser)
+    
+    def __tabContextMenuPrintPreview(self):
+        """
+        Private method to show a print preview of the selected tab.
+        """
+        browser = self.widget(self.__tabContextMenuIndex)
+        self.printPreviewBrowser(browser)
+    
+    @pyqtSlot()
+    def __newBrowser(self):
+        """
+        Private slot to open a new browser tab.
+        """
+        self.newBrowser()
+    
+    def newBrowser(self, link=None, requestData=None, position=-1):
+        """
+        Public method to create a new web browser tab.
+        
+        @param link link to be shown (string or QUrl)
+        @param requestData page load request data (LoadRequest)
+        @keyparam position position to create the new tab at or -1 to add it
+            to the end (integer)
+        """
+        if link is None:
+            linkName = ""
+        elif isinstance(link, QUrl):
+            linkName = link.toString()
+        else:
+            linkName = link
+        
+        from .UrlBar.UrlBar import UrlBar
+        urlbar = UrlBar(self.__mainWindow, self)
+        if self.__historyCompleter is None:
+            import WebBrowser.WebBrowserWindow
+            from .History.HistoryCompleter import HistoryCompletionModel, \
+                HistoryCompleter
+            self.__historyCompletionModel = HistoryCompletionModel(self)
+            self.__historyCompletionModel.setSourceModel(
+                WebBrowser.WebBrowserWindow.WebBrowserWindow.historyManager()
+                .historyFilterModel())
+            self.__historyCompleter = HistoryCompleter(
+                self.__historyCompletionModel, self)
+            self.__historyCompleter.activated[str].connect(self.__pathSelected)
+        urlbar.setCompleter(self.__historyCompleter)
+        urlbar.returnPressed.connect(self.__lineEditReturnPressed)
+        if position == -1:
+            self.__stackedUrlBar.addWidget(urlbar)
+        else:
+            self.__stackedUrlBar.insertWidget(position, urlbar)
+        
+        browser = WebBrowserView(self.__mainWindow, self)
+        urlbar.setBrowser(browser)
+        
+        browser.sourceChanged.connect(self.__sourceChanged)
+        browser.titleChanged.connect(self.__titleChanged)
+        browser.highlighted.connect(self.showMessage)
+        browser.backwardAvailable.connect(
+            self.__mainWindow.setBackwardAvailable)
+        browser.forwardAvailable.connect(self.__mainWindow.setForwardAvailable)
+        browser.loadStarted.connect(self.__loadStarted)
+        browser.loadFinished.connect(self.__loadFinished)
+        browser.iconChanged.connect(self.__iconChanged)
+        browser.search.connect(self.newBrowser)
+        browser.page().windowCloseRequested.connect(
+            self.__windowCloseRequested)
+        browser.zoomValueChanged.connect(self.browserZoomValueChanged)
+        
+        if position == -1:
+            index = self.addTab(browser, self.tr("..."))
+        else:
+            index = self.insertTab(position, browser, self.tr("..."))
+        self.setCurrentIndex(index)
+        
+        self.__mainWindow.closeAct.setEnabled(True)
+        self.__mainWindow.closeAllAct.setEnabled(True)
+        self.__closeButton.setEnabled(True)
+        self.__navigationButton.setEnabled(True)
+        
+        if not linkName and not requestData:
+            if Preferences.getWebBrowser("StartupBehavior") == 0:
+                linkName = Preferences.getWebBrowser("HomePage")
+            elif Preferences.getWebBrowser("StartupBehavior") == 1:
+                linkName = "eric:speeddial"
+        
+        if linkName:
+            browser.setSource(QUrl(linkName))
+            if not browser.documentTitle():
+                self.setTabText(index, self.__elide(linkName, Qt.ElideMiddle))
+                self.setTabToolTip(index, linkName)
+            else:
+                self.setTabText(
+                    index,
+                    self.__elide(browser.documentTitle().replace("&", "&&")))
+                self.setTabToolTip(index, browser.documentTitle())
+        elif requestData:
+            browser.load(requestData)
+    
+    def newBrowserAfter(self, browser, link=None, requestData=None):
+        """
+        Public method to create a new web browser tab after a given one.
+        
+        @param browser reference to the browser to add after (WebBrowserView)
+        @param link link to be shown (string or QUrl)
+        @param requestData page load request data (LoadRequest)
+        """
+        if browser:
+            position = self.indexOf(browser) + 1
+        else:
+            position = -1
+        self.newBrowser(link, requestData, position)
+    
+    def __showNavigationMenu(self):
+        """
+        Private slot to show the navigation button menu.
+        """
+        self.__navigationMenu.clear()
+        for index in range(self.count()):
+            act = self.__navigationMenu.addAction(
+                self.tabIcon(index), self.tabText(index))
+            act.setData(index)
+    
+    def __navigationMenuTriggered(self, act):
+        """
+        Private slot called to handle the navigation button menu selection.
+        
+        @param act reference to the selected action (QAction)
+        """
+        index = act.data()
+        if index is not None:
+            self.setCurrentIndex(index)
+    
+    def __windowCloseRequested(self):
+        """
+        Private slot to handle the windowCloseRequested signal of a browser.
+        """
+        page = self.sender()
+        if page is None:
+            return
+        
+        browser = page.view()
+        if browser is None:
+            return
+        
+        index = self.indexOf(browser)
+        self.closeBrowserAt(index)
+    
+    def reloadAllBrowsers(self):
+        """
+        Public slot to reload all browsers.
+        """
+        for index in range(self.count()):
+            browser = self.widget(index)
+            browser and browser.reload()
+    
+    @pyqtSlot()
+    def closeBrowser(self):
+        """
+        Public slot called to handle the close action.
+        """
+        self.closeBrowserAt(self.currentIndex())
+    
+    def closeAllBrowsers(self, shutdown=False):
+        """
+        Public slot called to handle the close all action.
+        
+        @param shutdown flag indicating a shutdown action
+        @type bool
+        """
+        for index in range(self.count() - 1, -1, -1):
+            self.closeBrowserAt(index, shutdown=shutdown)
+    
+    def closeBrowserAt(self, index, shutdown=False):
+        """
+        Public slot to close a browser based on its index.
+        
+        @param index index of browser to close
+        @type int
+        @param shutdown flag indicating a shutdown action
+        @type bool
+        """
+        browser = self.widget(index)
+        if browser is None:
+            return
+        
+##        if browser.isModified():
+##            ok = E5MessageBox.yesNo(
+##                self,
+##                self.tr("Do you really want to close this page?"),
+##                self.tr("""You have modified this page and when closing it"""
+##                        """ you would lose the modification.\nDo you really"""
+##                        """ want to close this page?"""))
+##            if not ok:
+##                return
+##        
+        urlbar = self.__stackedUrlBar.widget(index)
+        self.__stackedUrlBar.removeWidget(urlbar)
+        urlbar.deleteLater()
+        del urlbar
+        
+        self.__closedTabsManager.recordBrowser(browser, index)
+        
+        browser.closeWebInspector()
+        WebInspector.unregisterView(browser)
+        self.removeTab(index)
+        self.browserClosed.emit(browser)
+        browser.deleteLater()
+        del browser
+        
+        if self.count() == 0 and not shutdown:
+            self.newBrowser()
+        else:
+            self.currentChanged[int].emit(self.currentIndex())
+    
+    def currentBrowser(self):
+        """
+        Public method to get a reference to the current browser.
+        
+        @return reference to the current browser (WebBrowserView)
+        """
+        return self.currentWidget()
+    
+    def browserAt(self, index):
+        """
+        Public method to get a reference to the browser with the given index.
+        
+        @param index index of the browser to get (integer)
+        @return reference to the indexed browser (WebBrowserView)
+        """
+        return self.widget(index)
+    
+    def browsers(self):
+        """
+        Public method to get a list of references to all browsers.
+        
+        @return list of references to browsers (list of WebBrowserView)
+        """
+        li = []
+        for index in range(self.count()):
+            li.append(self.widget(index))
+        return li
+    
+    @pyqtSlot()
+    def printBrowser(self, browser=None):
+        """
+        Public slot called to print the displayed page.
+        
+        @param browser reference to the browser to be printed (WebBrowserView)
+        """
+        if browser is None:
+            browser = self.currentBrowser()
+        
+        printer = QPrinter(mode=QPrinter.HighResolution)
+        if Preferences.getPrinter("ColorMode"):
+            printer.setColorMode(QPrinter.Color)
+        else:
+            printer.setColorMode(QPrinter.GrayScale)
+        if Preferences.getPrinter("FirstPageFirst"):
+            printer.setPageOrder(QPrinter.FirstPageFirst)
+        else:
+            printer.setPageOrder(QPrinter.LastPageFirst)
+        printer.setPageMargins(
+            Preferences.getPrinter("LeftMargin") * 10,
+            Preferences.getPrinter("TopMargin") * 10,
+            Preferences.getPrinter("RightMargin") * 10,
+            Preferences.getPrinter("BottomMargin") * 10,
+            QPrinter.Millimeter
+        )
+        printerName = Preferences.getPrinter("PrinterName")
+        if printerName:
+            printer.setPrinterName(printerName)
+        printer.setResolution(Preferences.getPrinter("Resolution"))
+        
+        printDialog = QPrintDialog(printer, self)
+        if printDialog.exec_() == QDialog.Accepted:
+            browser.render(printer)
+    
+    @pyqtSlot()
+    def printBrowserPdf(self, browser=None):
+        """
+        Public slot called to print the displayed page to PDF.
+        
+        @param browser reference to the browser to be printed (HelpBrowser)
+        """
+        if browser is None:
+            browser = self.currentBrowser()
+        
+        printer = QPrinter(mode=QPrinter.HighResolution)
+        if Preferences.getPrinter("ColorMode"):
+            printer.setColorMode(QPrinter.Color)
+        else:
+            printer.setColorMode(QPrinter.GrayScale)
+        printerName = Preferences.getPrinter("PrinterName")
+        if printerName:
+            printer.setPrinterName(printerName)
+        printer.setOutputFormat(QPrinter.PdfFormat)
+        name = WebBrowserTools.getFileNameFromUrl(browser.url())
+        if name:
+            name = name.rsplit('.', 1)[0]
+            name += '.pdf'
+            printer.setOutputFileName(name)
+        printer.setResolution(Preferences.getPrinter("Resolution"))
+        
+        printDialog = QPrintDialog(printer, self)
+        if printDialog.exec_() == QDialog.Accepted:
+            browser.render(printer)
+    
+    @pyqtSlot()
+    def printPreviewBrowser(self, browser=None):
+        """
+        Public slot called to show a print preview of the displayed file.
+        
+        @param browser reference to the browser to be printed (HelpBrowserWV)
+        """
+        from PyQt5.QtPrintSupport import QPrintPreviewDialog
+        
+        if browser is None:
+            browser = self.currentBrowser()
+        
+        printer = QPrinter(mode=QPrinter.HighResolution)
+        if Preferences.getPrinter("ColorMode"):
+            printer.setColorMode(QPrinter.Color)
+        else:
+            printer.setColorMode(QPrinter.GrayScale)
+        if Preferences.getPrinter("FirstPageFirst"):
+            printer.setPageOrder(QPrinter.FirstPageFirst)
+        else:
+            printer.setPageOrder(QPrinter.LastPageFirst)
+        printer.setPageMargins(
+            Preferences.getPrinter("LeftMargin") * 10,
+            Preferences.getPrinter("TopMargin") * 10,
+            Preferences.getPrinter("RightMargin") * 10,
+            Preferences.getPrinter("BottomMargin") * 10,
+            QPrinter.Millimeter
+        )
+        printerName = Preferences.getPrinter("PrinterName")
+        if printerName:
+            printer.setPrinterName(printerName)
+        printer.setResolution(Preferences.getPrinter("Resolution"))
+        
+        preview = QPrintPreviewDialog(printer, self)
+        preview.paintRequested.connect(lambda p: browser.render(p))
+        preview.exec_()
+    
+    def __sourceChanged(self, url):
+        """
+        Private slot to handle a change of a browsers source.
+        
+        @param url URL of the new site (QUrl)
+        """
+        browser = self.sender()
+        
+        if browser is not None:
+            self.sourceChanged.emit(browser, url)
+    
+    def __titleChanged(self, title):
+        """
+        Private slot to handle a change of a browsers title.
+        
+        @param title new title (string)
+        """
+        browser = self.sender()
+        
+        if browser is not None and isinstance(browser, QWidget):
+            index = self.indexOf(browser)
+            if title == "":
+                title = browser.url().toString()
+            
+            self.setTabText(index, self.__elide(title.replace("&", "&&")))
+            self.setTabToolTip(index, title)
+        
+            self.titleChanged.emit(browser, title)
+    
+    def __elide(self, txt, mode=Qt.ElideRight, length=40):
+        """
+        Private method to elide some text.
+        
+        @param txt text to be elided (string)
+        @keyparam mode elide mode (Qt.TextElideMode)
+        @keyparam length amount of characters to be used (integer)
+        @return the elided text (string)
+        """
+        if mode == Qt.ElideNone or len(txt) < length:
+            return txt
+        elif mode == Qt.ElideLeft:
+            return "...{0}".format(txt[-length:])
+        elif mode == Qt.ElideMiddle:
+            return "{0}...{1}".format(txt[:length // 2], txt[-(length // 2):])
+        elif mode == Qt.ElideRight:
+            return "{0}...".format(txt[:length])
+        else:
+            # just in case
+            return txt
+    
+    def preferencesChanged(self):
+        """
+        Public slot to handle a change of preferences.
+        """
+        for browser in self.browsers():
+            browser.preferencesChanged()
+        
+        for urlbar in self.__stackedUrlBar.urlBars():
+            urlbar.preferencesChanged()
+        
+        if Preferences.getUI("SingleCloseButton"):
+            self.setTabsClosable(False)
+            try:
+                self.tabCloseRequested.disconnect(self.closeBrowserAt)
+            except TypeError:
+                pass
+            self.__closeButton.show()
+        else:
+            self.setTabsClosable(True)
+            self.tabCloseRequested.connect(self.closeBrowserAt)
+            self.__closeButton.hide()
+    
+    def __loadStarted(self):
+        """
+        Private method to handle the loadStarted signal.
+        """
+        browser = self.sender()
+        
+        if browser is not None:
+            index = self.indexOf(browser)
+            anim = self.animationLabel(
+                index, os.path.join(getConfig("ericPixDir"), "loading.gif"),
+                100)
+            if not anim:
+                loading = QIcon(os.path.join(getConfig("ericPixDir"),
+                                "loading.gif"))
+                self.setTabIcon(index, loading)
+            else:
+                self.setTabIcon(index, QIcon())
+            self.setTabText(index, self.tr("Loading..."))
+            self.setTabToolTip(index, self.tr("Loading..."))
+            self.showMessage.emit(self.tr("Loading..."))
+            
+            self.__mainWindow.setLoadingActions(True)
+    
+    def __loadFinished(self, ok):
+        """
+        Private method to handle the loadFinished signal.
+        
+        @param ok flag indicating the result (boolean)
+        """
+        browser = self.sender()
+        if not isinstance(browser, WebBrowserView):
+            return
+        
+        if browser is not None:
+            import WebBrowser.WebBrowserWindow
+            index = self.indexOf(browser)
+            self.resetAnimation(index)
+            self.setTabIcon(
+                index, WebBrowser.WebBrowserWindow.WebBrowserWindow.icon(
+                    browser.url()))
+            if ok:
+                self.showMessage.emit(self.tr("Finished loading"))
+            else:
+                self.showMessage.emit(self.tr("Failed to load"))
+            
+            self.__mainWindow.setLoadingActions(False)
+    
+    def __iconChanged(self):
+        """
+        Private slot to handle a change of the web site icon.
+        """
+        browser = self.sender()
+        
+        if browser is not None and isinstance(browser, QWidget):
+            self.setTabIcon(
+                self.indexOf(browser),
+                browser.icon())
+            self.__mainWindow.bookmarksManager().iconChanged(browser.url())
+    
+    def getSourceFileList(self):
+        """
+        Public method to get a list of all opened Qt help files.
+        
+        @return dictionary with tab id as key and host/namespace as value
+        """
+        sourceList = {}
+        for i in range(self.count()):
+            browser = self.widget(i)
+            if browser is not None and \
+               browser.source().isValid():
+                sourceList[i] = browser.source().host()
+        
+        return sourceList
+    
+    def shallShutDown(self):
+        """
+        Public method to check, if the application should be shut down.
+        
+        @return flag indicating a shut down (boolean)
+        """
+        if self.count() > 1 and Preferences.getWebBrowser(
+                "WarnOnMultipleClose"):
+            mb = E5MessageBox.E5MessageBox(
+                E5MessageBox.Information,
+                self.tr("Are you sure you want to close the window?"),
+                self.tr("""Are you sure you want to close the window?\n"""
+                        """You have %n tab(s) open.""", "", self.count()),
+                modal=True,
+                parent=self)
+            if self.__mainWindow.fromEric:
+                quitButton = mb.addButton(
+                    self.tr("&Close"), E5MessageBox.AcceptRole)
+                quitButton.setIcon(UI.PixmapCache.getIcon("close.png"))
+            else:
+                quitButton = mb.addButton(
+                    self.tr("&Quit"), E5MessageBox.AcceptRole)
+                quitButton.setIcon(UI.PixmapCache.getIcon("exit.png"))
+            closeTabButton = mb.addButton(
+                self.tr("C&lose Current Tab"), E5MessageBox.AcceptRole)
+            closeTabButton.setIcon(UI.PixmapCache.getIcon("tabClose.png"))
+            mb.addButton(E5MessageBox.Cancel)
+            mb.exec_()
+            if mb.clickedButton() == quitButton:
+                return True
+            else:
+                if mb.clickedButton() == closeTabButton:
+                    self.closeBrowser()
+                return False
+        
+        return True
+    
+    def stackedUrlBar(self):
+        """
+        Public method to get a reference to the stacked url bar.
+        
+        @return reference to the stacked url bar (StackedUrlBar)
+        """
+        return self.__stackedUrlBar
+    
+    def currentUrlBar(self):
+        """
+        Public method to get a reference to the current url bar.
+        
+        @return reference to the current url bar (UrlBar)
+        """
+        return self.__stackedUrlBar.currentWidget()
+    
+    def __lineEditReturnPressed(self):
+        """
+        Private slot to handle the entering of an URL.
+        """
+        edit = self.sender()
+        url = self.__guessUrlFromPath(edit.text())
+##        request = QNetworkRequest(url)
+##        request.setRawHeader(b"X-Eric6-UserLoadAction", b"1")
+        if e5App().keyboardModifiers() == Qt.AltModifier:
+            self.newBrowser(url)
+##            self.newBrowser(
+##                None, (request, QNetworkAccessManager.GetOperation, b""))
+        else:
+            self.currentBrowser().setSource(url)
+##            self.currentBrowser().setSource(
+##                None, (request, QNetworkAccessManager.GetOperation, b""))
+            self.currentBrowser().setFocus()
+    
+    def __pathSelected(self, path):
+        """
+        Private slot called when a URL is selected from the completer.
+        
+        @param path path to be shown (string)
+        """
+        url = self.__guessUrlFromPath(path)
+        self.currentBrowser().setSource(url)
+    
+    def __guessUrlFromPath(self, path):
+        """
+        Private method to guess an URL given a path string.
+        
+        @param path path string to guess an URL for (string)
+        @return guessed URL (QUrl)
+        """
+        manager = self.__mainWindow.openSearchManager()
+        path = Utilities.fromNativeSeparators(path)
+        url = manager.convertKeywordSearchToUrl(path)
+        if url.isValid():
+            return url
+        
+        try:
+            url = QUrl.fromUserInput(path)
+        except AttributeError:
+            url = QUrl(path)
+        
+        if url.scheme() == "about" and \
+           url.path() == "home":
+            url = QUrl("eric:home")
+        
+        # TODO: extend this logic to about:config (open config dialog)
+        
+        if url.scheme() in ["s", "search"]:
+            url = manager.currentEngine().searchUrl(url.path().strip())
+        
+        if url.scheme() != "" and \
+           (url.host() != "" or url.path() != ""):
+            return url
+        
+        urlString = Preferences.getWebBrowser("DefaultScheme") + path.strip()
+        url = QUrl.fromEncoded(urlString.encode("utf-8"), QUrl.TolerantMode)
+        
+        return url
+    
+    def __currentChanged(self, index):
+        """
+        Private slot to handle an index change.
+        
+        @param index new index (integer)
+        """
+        self.__stackedUrlBar.setCurrentIndex(index)
+        
+        browser = self.browserAt(index)
+        if browser is not None:
+            if browser.url() == "" and browser.hasFocus():
+                self.__stackedUrlBar.currentWidget.setFocus()
+            elif browser.url() != "":
+                browser.setFocus()
+    
+    def restoreClosedTab(self):
+        """
+        Public slot to restore the most recently closed tab.
+        """
+        if not self.canRestoreClosedTab():
+            return
+        
+        act = self.sender()
+        tab = self.__closedTabsManager.getClosedTabAt(act.data())
+        
+        self.newBrowser(tab.url.toString(), position=tab.position)
+    
+    def canRestoreClosedTab(self):
+        """
+        Public method to check, if closed tabs can be restored.
+        
+        @return flag indicating that closed tabs can be restored (boolean)
+        """
+        return self.__closedTabsManager.isClosedTabAvailable()
+    
+    def restoreAllClosedTabs(self):
+        """
+        Public slot to restore all closed tabs.
+        """
+        if not self.canRestoreClosedTab():
+            return
+        
+        for tab in self.__closedTabsManager.allClosedTabs():
+            self.newBrowser(tab.url.toString(), position=tab.position)
+        self.__closedTabsManager.clearList()
+    
+    def clearClosedTabsList(self):
+        """
+        Public slot to clear the list of closed tabs.
+        """
+        self.__closedTabsManager.clearList()
+    
+    def __aboutToShowClosedTabsMenu(self):
+        """
+        Private slot to populate the closed tabs menu.
+        """
+        fm = self.__closedTabsMenu.fontMetrics()
+        maxWidth = fm.width('m') * 40
+        
+        self.__closedTabsMenu.clear()
+        index = 0
+        for tab in self.__closedTabsManager.allClosedTabs():
+            title = fm.elidedText(tab.title, Qt.ElideRight, maxWidth)
+            self.__closedTabsMenu.addAction(
+                self.__mainWindow.icon(tab.url), title,
+                self.restoreClosedTab).setData(index)
+            index += 1
+        self.__closedTabsMenu.addSeparator()
+        self.__closedTabsMenu.addAction(
+            self.tr("Restore All Closed Tabs"), self.restoreAllClosedTabs)
+        self.__closedTabsMenu.addAction(
+            self.tr("Clear List"), self.clearClosedTabsList)
+    
+    def closedTabsManager(self):
+        """
+        Public slot to get a reference to the closed tabs manager.
+        
+        @return reference to the closed tabs manager (ClosedTabsManager)
+        """
+        return self.__closedTabsManager
+    
+    def __closedTabAvailable(self, avail):
+        """
+        Private slot to handle changes of the availability of closed tabs.
+        
+        @param avail flag indicating the availability of closed tabs (boolean)
+        """
+        self.__closedTabsButton.setEnabled(avail)
+        self.__restoreClosedTabAct.setEnabled(avail)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/WebBrowserView.py	Sun Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,2154 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2008 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+
+"""
+Module implementing the web browser using QWebEngineView.
+"""
+
+from __future__ import unicode_literals
+try:
+    str = unicode           # __IGNORE_EXCEPTION__
+except NameError:
+    pass
+
+from PyQt5.QtCore import pyqtSlot, pyqtSignal, QObject, QT_TRANSLATE_NOOP, \
+    QUrl, QBuffer, QIODevice, QFileInfo, Qt, QTimer, QEvent, \
+    QRect, QFile, QPoint, QByteArray, qVersion
+from PyQt5.QtGui import QDesktopServices, QClipboard, QMouseEvent, QColor, \
+    QPalette, QIcon, QContextMenuEvent
+from PyQt5.QtWidgets import qApp, QStyle, QMenu, QApplication, QInputDialog, \
+    QLineEdit, QLabel, QToolTip, QFrame, QDialog
+from PyQt5.QtPrintSupport import QPrinter, QPrintDialog
+from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest, QHostInfo
+from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEnginePage
+
+from E5Gui import E5MessageBox, E5FileDialog
+
+import WebBrowser
+from .WebBrowserPage import WebBrowserPage
+
+from .Tools.WebIconLoader import WebIconLoader
+from .Tools import WebBrowserTools, Scripts
+
+from .Network.LoadRequest import LoadRequest, LoadRequestOperations
+
+from . import WebInspector
+
+import Preferences
+import UI.PixmapCache
+import Globals
+
+try:
+    from PyQt5.QtNetwork import QSslCertificate
+    SSL_AVAILABLE = True
+except ImportError:
+    SSL_AVAILABLE = False
+
+
+class WebBrowserView(QWebEngineView):
+    """
+    Class implementing the web browser view widget.
+    
+    @signal sourceChanged(QUrl) emitted after the current URL has changed
+    @signal forwardAvailable(bool) emitted after the current URL has changed
+    @signal backwardAvailable(bool) emitted after the current URL has changed
+    @signal highlighted(str) emitted, when the mouse hovers over a link
+    @signal search(QUrl) emitted, when a search is requested
+    @signal zoomValueChanged(int) emitted to signal a change of the zoom value
+    @signal iconChanged() emitted to signal a changed web site icon
+    """
+    sourceChanged = pyqtSignal(QUrl)
+    forwardAvailable = pyqtSignal(bool)
+    backwardAvailable = pyqtSignal(bool)
+    highlighted = pyqtSignal(str)
+    search = pyqtSignal(QUrl)
+    zoomValueChanged = pyqtSignal(int)
+    iconChanged = pyqtSignal()
+    
+    ZoomLevels = [
+        30, 40, 50, 67, 80, 90,
+        100,
+        110, 120, 133, 150, 170, 200, 220, 233, 250, 270, 285, 300,
+    ]
+    ZoomLevelDefault = 100
+    
+    def __init__(self, mainWindow, parent=None, name=""):
+        """
+        Constructor
+        
+        @param mainWindow reference to the main window (WebBrowserWindow)
+        @param parent parent widget of this window (QWidget)
+        @param name name of this window (string)
+        """
+        super(WebBrowserView, self).__init__(parent)
+        self.setObjectName(name)
+        
+        self.__rwhvqt = None
+        self.installEventFilter(self)
+        
+        # TODO: Speeddial
+##        import WebBrowser.WebBrowserWindow
+##        self.__speedDial = WebBrowser.WebBrowserWindow.WebBrowserWindow.speedDial()
+        
+        self.__page = WebBrowserPage(self)
+        self.setPage(self.__page)
+        
+        self.__mw = mainWindow
+        self.__ctrlPressed = False
+        self.__isLoading = False
+        self.__progress = 0
+        self.__siteIconLoader = None
+        self.__siteIcon = QIcon()
+        self.__menu = QMenu(self)
+        self.__clickedPos = QPoint()
+        self.__firstLoad = False
+        
+        self.__currentZoom = 100
+        self.__zoomLevels = WebBrowserView.ZoomLevels[:]
+        
+##        self.__mw.zoomTextOnlyChanged.connect(self.__applyZoom)
+        
+##        self.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks)
+##        self.linkClicked.connect(self.setSource)
+##        
+        self.iconUrlChanged.connect(self.__iconUrlChanged)
+        self.urlChanged.connect(self.__urlChanged)
+##        self.statusBarMessage.connect(self.__statusBarMessage)
+        self.page().linkHovered.connect(self.__linkHovered)
+        
+        self.loadStarted.connect(self.__loadStarted)
+        self.loadProgress.connect(self.__loadProgress)
+        self.loadFinished.connect(self.__loadFinished)
+        
+##        self.page().setForwardUnsupportedContent(True)
+##        self.page().unsupportedContent.connect(self.__unsupportedContent)
+        
+##        self.page().databaseQuotaExceeded.connect(self.__databaseQuotaExceeded)
+        
+        self.__mw.openSearchManager().currentEngineChanged.connect(
+            self.__currentEngineChanged)
+        
+        self.setAcceptDrops(True)
+        
+        # TODO: Access Keys
+##        self.__enableAccessKeys = Preferences.getWebBrowser("AccessKeysEnabled")
+##        self.__accessKeysPressed = False
+##        self.__accessKeyLabels = []
+##        self.__accessKeyNodes = {}
+##        
+##        self.page().loadStarted.connect(self.__hideAccessKeys)
+##        self.page().scrollRequested.connect(self.__hideAccessKeys)
+        
+        self.__rss = []
+        
+        self.__clickedFrame = None
+        
+        self.__mw.personalInformationManager().connectPage(self.page())
+        
+        self.__inspector = None
+        WebInspector.registerView(self)
+        
+        self.grabGesture(Qt.PinchGesture)
+    
+##    def __addExternalBinding(self, frame=None):
+##        """
+##        Private slot to add javascript bindings for adding search providers.
+##        
+##        @param frame reference to the web frame (QWebFrame)
+##        """
+##        self.page().settings().setAttribute(QWebSettings.JavascriptEnabled,
+##                                            True)
+##        if self.__javaScriptBinding is None:
+##            self.__javaScriptBinding = JavaScriptExternalObject(self.__mw, self)
+##        
+##        if frame is None:
+##            # called from QWebFrame.javaScriptWindowObjectCleared
+##            frame = self.sender()
+##            if isinstance(frame, HelpWebPage):
+##                frame = frame.mainFrame()
+##            if frame.url().scheme() == "eric" and frame.url().path() == "home":
+##                if self.__javaScriptEricObject is None:
+##                    self.__javaScriptEricObject = JavaScriptEricObject(
+##                        self.__mw, self)
+##                frame.addToJavaScriptWindowObject(
+##                    "eric", self.__javaScriptEricObject)
+##            elif frame.url().scheme() == "eric" and \
+##                    frame.url().path() == "speeddial":
+##                frame.addToJavaScriptWindowObject(
+##                    "speeddial", self.__speedDial)
+##                self.__speedDial.addWebFrame(frame)
+##        else:
+##            # called from QWebPage.frameCreated
+##            frame.javaScriptWindowObjectCleared.connect(
+##                self.__addExternalBinding)
+##        frame.addToJavaScriptWindowObject("external", self.__javaScriptBinding)
+##    
+    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, urlOrRequest):
+        """
+        Public method to load a web site.
+        
+        @param urlOrRequest URL or request object
+        @type QUrl or LoadRequest
+        """
+        if isinstance(urlOrRequest, QUrl):
+            super(WebBrowserView, self).load(urlOrRequest)
+            
+            if not self.__firstLoad:
+                self.__firstLoad = True
+                WebInspector.pushView(self)
+        elif isinstance(urlOrRequest, LoadRequest):
+            reqUrl = urlOrRequest.url()
+            if reqUrl.isEmpty():
+                return
+            
+            if reqUrl.scheme() == "javascript":
+                script = reqUrl.toString()[11:]
+                # check if the javascript script is percent encode
+                # i.e. it contains '%' characters
+                if '%' in script:
+                    script = QUrl.fromPercentEncoding(
+                        QByteArray(script.encode("utf-8")))
+                self.page().runJavaScript(script)
+                return
+            
+            if self.__isUrlValid(reqUrl):
+                self.loadRequest(urlOrRequest)
+                return
+            
+            # ensure proper loading of hosts without a '.'
+            if not reqUrl.isEmpty() and \
+               reqUrl.scheme() and \
+               not WebBrowserTools.containsSpace(reqUrl.path()) and \
+               '.' not in reqUrl.path():
+                u = QUrl("http://" + reqUrl.path())
+                if u.isValid():
+                    info = QHostInfo.fromName(u.path())
+                    if info.error() == QHostInfo.NoError:
+                        req = LoadRequest(urlOrRequest)
+                        req.setUrl(u)
+                        self.loadRequest(req)
+                        return
+    
+    def loadRequest(self, req):
+        """
+        Public method to load a page via a load request object.
+        
+        @param req loaf request object
+        @type LoadRequest
+        """
+        if req.Operation == LoadRequestOperations.GetOperation:
+            self.load(req.url())
+        else:
+            self.page().runJavaScript(
+                Scripts.sendPostData(req.url(), req.data()))
+    
+    # TODO: eliminate requestData, add param to get rid of __ctrlPressed
+    def setSource(self, name, requestData=None):
+        """
+        Public method used to set the source to be displayed.
+        
+        @param name filename to be shown (QUrl)
+        @param requestData tuple containing the request data (QNetworkRequest,
+            QNetworkAccessManager.Operation, QByteArray)
+        """
+##        if (name is None or not name.isValid()) and requestData is None:
+        if name is None or not name.isValid():
+            return
+        
+##        if name is None and requestData is not None:
+##            name = requestData[0].url()
+##        
+        if self.__ctrlPressed:
+            # open in a new window
+            self.__mw.newTab(name)
+            self.__ctrlPressed = False
+            return
+        
+        if not name.scheme():
+            name.setUrl(Preferences.getWebBrowser("DefaultScheme") +
+                        name.toString())
+        
+        if len(name.scheme()) == 1 or \
+           name.scheme() == "file":
+            # name is a local file
+            if name.scheme() and len(name.scheme()) == 1:
+                # it is a local path on win os
+                name = QUrl.fromLocalFile(name.toString())
+            
+            if not QFileInfo(name.toLocalFile()).exists():
+                E5MessageBox.critical(
+                    self,
+                    self.tr("eric6 Web Browser"),
+                    self.tr(
+                        """<p>The file <b>{0}</b> does not exist.</p>""")
+                    .format(name.toLocalFile()))
+                return
+
+            if name.toLocalFile().endswith(".pdf") or \
+               name.toLocalFile().endswith(".PDF") or \
+               name.toLocalFile().endswith(".chm") or \
+               name.toLocalFile().endswith(".CHM"):
+                started = QDesktopServices.openUrl(name)
+                if not started:
+                    E5MessageBox.critical(
+                        self,
+                        self.tr("eric6 Web Browser"),
+                        self.tr(
+                            """<p>Could not start a viewer"""
+                            """ for file <b>{0}</b>.</p>""")
+                        .format(name.path()))
+                return
+        elif name.scheme() in ["mailto"]:
+            started = QDesktopServices.openUrl(name)
+            if not started:
+                E5MessageBox.critical(
+                    self,
+                    self.tr("eric6 Web Browser"),
+                    self.tr(
+                        """<p>Could not start an application"""
+                        """ for URL <b>{0}</b>.</p>""")
+                    .format(name.toString()))
+            return
+        else:
+            if name.toString().endswith(".pdf") or \
+               name.toString().endswith(".PDF") or \
+               name.toString().endswith(".chm") or \
+               name.toString().endswith(".CHM"):
+                started = QDesktopServices.openUrl(name)
+                if not started:
+                    E5MessageBox.critical(
+                        self,
+                        self.tr("eric6 Web Browser"),
+                        self.tr(
+                            """<p>Could not start a viewer"""
+                            """ for file <b>{0}</b>.</p>""")
+                        .format(name.path()))
+                return
+        
+        if requestData is not None:
+            self.load(*requestData)
+        else:
+            self.load(name)
+
+    def source(self):
+        """
+        Public method to return the URL of the loaded page.
+        
+        @return URL loaded in the help browser (QUrl)
+        """
+        return self.url()
+    
+    def documentTitle(self):
+        """
+        Public method to return the title of the loaded page.
+        
+        @return title (string)
+        """
+        return self.title()
+    
+    def backward(self):
+        """
+        Public slot to move backwards in history.
+        """
+        self.triggerPageAction(QWebEnginePage.Back)
+        self.__urlChanged(self.history().currentItem().url())
+    
+    def forward(self):
+        """
+        Public slot to move forward in history.
+        """
+        self.triggerPageAction(QWebEnginePage.Forward)
+        self.__urlChanged(self.history().currentItem().url())
+    
+    def home(self):
+        """
+        Public slot to move to the first page loaded.
+        """
+        homeUrl = QUrl(Preferences.getWebBrowser("HomePage"))
+        self.setSource(homeUrl)
+        self.__urlChanged(self.history().currentItem().url())
+    
+    def reload(self):
+        """
+        Public slot to reload the current page.
+        """
+        self.triggerPageAction(QWebEnginePage.Reload)
+    
+    def reloadBypassingCache(self):
+        """
+        Public slot to reload the current page bypassing the cache.
+        """
+        self.triggerPageAction(QWebEnginePage.ReloadAndBypassCache)
+    
+    def copy(self):
+        """
+        Public slot to copy the selected text.
+        """
+        self.triggerPageAction(QWebEnginePage.Copy)
+    
+    def cut(self):
+        """
+        Public slot to cut the selected text.
+        """
+        self.triggerPageAction(QWebEnginePage.Cut)
+    
+    def paste(self):
+        """
+        Public slot to paste text from the clipboard.
+        """
+        self.triggerPageAction(QWebEnginePage.Paste)
+    
+    def undo(self):
+        """
+        Public slot to undo the last edit action.
+        """
+        self.triggerPageAction(QWebEnginePage.Undo)
+    
+    def redo(self):
+        """
+        Public slot to redo the last edit action.
+        """
+        self.triggerPageAction(QWebEnginePage.Redo)
+    
+    def selectAll(self):
+        """
+        Public slot to select all text.
+        """
+        self.triggerPageAction(QWebEnginePage.SelectAll)
+    
+    def isForwardAvailable(self):
+        """
+        Public method to determine, if a forward move in history is possible.
+        
+        @return flag indicating move forward is possible (boolean)
+        """
+        return self.history().canGoForward()
+    
+    def isBackwardAvailable(self):
+        """
+        Public method to determine, if a backwards move in history is possible.
+        
+        @return flag indicating move backwards is possible (boolean)
+        """
+        return self.history().canGoBack()
+    
+    def __levelForZoom(self, zoom):
+        """
+        Private method determining the zoom level index given a zoom factor.
+        
+        @param zoom zoom factor (integer)
+        @return index of zoom factor (integer)
+        """
+        try:
+            index = self.__zoomLevels.index(zoom)
+        except ValueError:
+            for index in range(len(self.__zoomLevels)):
+                if zoom <= self.__zoomLevels[index]:
+                    break
+        return index
+    
+    def __applyZoom(self):
+        """
+        Private slot to apply the current zoom factor.
+        """
+        self.setZoomValue(self.__currentZoom)
+    
+    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.__currentZoom = self.__zoomLevels[index + 1]
+        self.__applyZoom()
+    
+    def zoomOut(self):
+        """
+        Public slot to zoom out of the page.
+        """
+        index = self.__levelForZoom(self.__currentZoom)
+        if index > 0:
+            self.__currentZoom = self.__zoomLevels[index - 1]
+        self.__applyZoom()
+    
+    def zoomReset(self):
+        """
+        Public method to reset the zoom factor.
+        """
+        index = self.__levelForZoom(WebBrowserView.ZoomLevelDefault)
+        self.__currentZoom = self.__zoomLevels[index]
+        self.__applyZoom()
+    
+    def hasSelection(self):
+        """
+        Public method to determine, if there is some text selected.
+        
+        @return flag indicating text has been selected (boolean)
+        """
+        return self.selectedText() != ""
+    
+    # TODO: adjust this to what Qt 5.6 is offering
+    def findNextPrev(self, txt, case, backwards, wrap, highlightAll, 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 wrap flag indicating to wrap around (boolean)
+        @param highlightAll flag indicating to highlight all occurrences
+            (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 wrap:
+##            findFlags |= QWebPage.FindWrapsAroundDocument
+##        try:
+##            if highlightAll:
+##                findFlags |= QWebPage.HighlightAllOccurrences
+##        except AttributeError:
+##            pass
+        
+        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)
+        
+        # TODO: AdBlock
+##        if not hitTest.isContentEditable() and not hitTest.isContentSelected():
+##            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()
+        # TODO: Qt 5.6
+##        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()
+        # TODO: Save Image
+##        menu.addAction(
+##            UI.PixmapCache.getIcon("download.png"),
+##            self.tr("Save Image"), self.__downloadImage)
+        # TODO: Copy Image
+##        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())
+        # TODO: AdBlock
+##        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())
+        # TODO: Qt 5.6
+##        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())
+        # TODO: OpenSearch: add a search entry using the current engine
+        self.__searchMenu = menu.addMenu(self.tr("Search with..."))
+        
+        from .OpenSearch.OpenSearchEngineAction import \
+            OpenSearchEngineAction
+        engineNames = self.__mw.openSearchManager().allEnginesNames()
+        for engineName in engineNames:
+            engine = self.__mw.openSearchManager().engine(engineName)
+            act = OpenSearchEngineAction(engine, self.__searchMenu)
+            act.setData(engineName)
+            self.__searchMenu.addAction(act)
+        self.__searchMenu.triggered.connect(self.__searchRequested)
+        
+        menu.addSeparator()
+        
+        from .WebBrowserLanguagesDialog import WebBrowserLanguagesDialog
+        languages = Preferences.toList(
+            Preferences.Prefs.settings.value(
+                "WebBrowser/AcceptLanguages",
+                WebBrowserLanguagesDialog.defaultAcceptLanguages()))
+        if languages:
+            language = languages[0]
+            langCode = language.split("[")[1][:2]
+            googleTranslatorUrl = QUrl(
+                "http://translate.google.com/#auto|{0}|{1}".format(
+                    langCode, self.selectedText()))
+            menu.addAction(
+                UI.PixmapCache.getIcon("translate.png"),
+                self.tr("Google Translate"), self.__openLinkInNewTab)\
+                .setData(googleTranslatorUrl)
+            wiktionaryUrl = QUrl(
+                "http://{0}.wiktionary.org/wiki/Special:Search?search={1}"
+                .format(langCode, self.selectedText()))
+            menu.addAction(
+                UI.PixmapCache.getIcon("wikipedia.png"),
+                self.tr("Dictionary"), self.__openLinkInNewTab)\
+                .setData(wiktionaryUrl)
+            menu.addSeparator()
+        
+        guessedUrl = QUrl.fromUserInput(self.selectedText().strip())
+        if self.__isUrlValid(guessedUrl):
+            menu.addAction(
+                self.tr("Go to web address"),
+                self.__openLinkInNewTab).setData(guessedUrl)
+    
+    def __createPageContextMenu(self, menu):
+        """
+        Private method to populate the basic context menu.
+        
+        @param menu reference to the menu to be populated
+        @type QMenu
+        """
+        
+        menu.addAction(self.__mw.newTabAct)
+        menu.addAction(self.__mw.newAct)
+        menu.addSeparator()
+        # TODO: Save
+##        menu.addAction(self.__mw.saveAsAct)
+##        menu.addSeparator()
+        
+        menu.addAction(
+            UI.PixmapCache.getIcon("bookmark22.png"),
+            self.tr("Bookmark this Page"), self.addBookmark)
+        menu.addAction(
+            UI.PixmapCache.getIcon("editCopy.png"),
+            self.tr("Copy Page Link"), self.__copyLink).setData(self.url())
+        menu.addAction(
+            UI.PixmapCache.getIcon("mailSend.png"),
+            self.tr("Send Page Link"), self.__sendLink).setData(self.url())
+        menu.addSeparator()
+        
+        # TODO: User Agent
+##        from .UserAgent.UserAgentMenu import UserAgentMenu
+##        self.__userAgentMenu = UserAgentMenu(self.tr("User Agent"),
+##                                             url=self.url())
+##        menu.addMenu(self.__userAgentMenu)
+##        menu.addSeparator()
+        menu.addAction(self.__mw.backAct)
+        menu.addAction(self.__mw.forwardAct)
+        menu.addAction(self.__mw.homeAct)
+        menu.addAction(self.__mw.reloadAct)
+        menu.addAction(self.__mw.stopAct)
+        menu.addSeparator()
+        menu.addAction(self.__mw.zoomInAct)
+        menu.addAction(self.__mw.zoomResetAct)
+        menu.addAction(self.__mw.zoomOutAct)
+        menu.addSeparator()
+        menu.addAction(self.__mw.selectAllAct)
+        menu.addSeparator()
+        menu.addAction(self.__mw.findAct)
+        menu.addSeparator()
+        menu.addAction(self.__mw.pageSourceAct)
+        menu.addSeparator()
+        menu.addAction(self.__mw.siteInfoAct)
+        if self.url().scheme() in ["http", "https"]:
+            menu.addSeparator()
+            
+            w3url = QUrl.fromEncoded(
+                b"http://validator.w3.org/check?uri=" +
+                QUrl.toPercentEncoding(bytes(self.url().toEncoded()).decode()))
+            menu.addAction(
+                UI.PixmapCache.getIcon("w3.png"),
+                self.tr("Validate Page"), self.__openLinkInNewTab)\
+                .setData(w3url)
+            
+            from .WebBrowserLanguagesDialog import WebBrowserLanguagesDialog
+            languages = Preferences.toList(
+                Preferences.Prefs.settings.value(
+                    "WebBrowser/AcceptLanguages",
+                    WebBrowserLanguagesDialog.defaultAcceptLanguages()))
+            if languages:
+                language = languages[0]
+                langCode = language.split("[")[1][:2]
+                googleTranslatorUrl = QUrl.fromEncoded(
+                    b"http://translate.google.com/translate?sl=auto&tl=" +
+                    langCode.encode() +
+                    b"&u=" +
+                    QUrl.toPercentEncoding(
+                        bytes(self.url().toEncoded()).decode()))
+                menu.addAction(
+                    UI.PixmapCache.getIcon("translate.png"),
+                    self.tr("Google Translate"), self.__openLinkInNewTab)\
+                    .setData(googleTranslatorUrl)
+        
+    def __checkForForm(self, act, pos):
+        """
+        Private method to check the given position for an open search form.
+        
+        @param act reference to the action to be populated upon success
+        @type QAction
+        @param pos position to be tested
+        @type QPoint
+        """
+        self.__clickedPos = pos
+        
+        from .Tools import Scripts
+        script = Scripts.getFormData(pos)
+        self.page().runJavaScript(
+            script, lambda res: self.__checkForFormCallback(res, act))
+    
+    def __checkForFormCallback(self, res, act):
+        """
+        Private method handling the __checkForForm result.
+        
+        @param res result dictionary generated by JavaScript
+        @type dict
+        @param act reference to the action to be populated upon success
+        @type QAction
+        """
+        if act is None or not bool(res):
+            return
+        
+        url = QUrl(res["action"])
+        method = res["method"]
+        
+        if not url.isEmpty() and method in ["get", "post"]:
+            act.setVisible(True)
+            act.setText(self.tr("Add to web search toolbar"))
+            act.triggered.connect(self.__addSearchEngine)
+    
+    def __isUrlValid(self, url):
+        """
+        Private method to check a URL for validity.
+        
+        @param url URL to be checked (QUrl)
+        @return flag indicating a valid URL (boolean)
+        """
+        return url.isValid() and \
+            bool(url.host()) and \
+            bool(url.scheme()) and \
+            "." in url.host()
+    
+    def __openLinkInNewTab(self):
+        """
+        Private method called by the context menu to open a link in a new
+        tab.
+        """
+        act = self.sender()
+        url = act.data()
+        if url.isEmpty():
+            return
+        
+        # TODO: check, if this can be done simpler
+        self.__ctrlPressed = True
+        self.setSource(url)
+        self.__ctrlPressed = False
+    
+    def __openLinkInNewWindow(self):
+        """
+        Private slot called by the context menu to open a link in a new
+        window.
+        """
+        act = self.sender()
+        url = act.data()
+        if url.isEmpty():
+            return
+        
+        self.__mw.newWindow(url)
+    
+    def __openLinkInNewPrivateWindow(self):
+        """
+        Private slot called by the context menu to open a link in a new
+        private window.
+        """
+        act = self.sender()
+        url = act.data()
+        if url.isEmpty():
+            return
+        
+        self.__mw.newPrivateWindow(url)
+    
+    def __bookmarkLink(self):
+        """
+        Private slot to bookmark a link via the context menu.
+        """
+        act = self.sender()
+        url = act.data()
+        if url.isEmpty():
+            return
+        
+        from .Bookmarks.AddBookmarkDialog import AddBookmarkDialog
+        dlg = AddBookmarkDialog()
+        dlg.setUrl(bytes(url.toEncoded()).decode())
+        dlg.exec_()
+    
+    def __sendLink(self):
+        """
+        Private slot to send a link via email.
+        """
+        act = self.sender()
+        data = act.data()
+        if isinstance(data, QUrl) and data.isEmpty():
+            return
+        
+        if isinstance(data, QUrl):
+            data = data.toString()
+        QDesktopServices.openUrl(QUrl("mailto:?body=" + data))
+    
+    def __copyLink(self):
+        """
+        Private slot to copy a link to the clipboard.
+        """
+        act = self.sender()
+        data = act.data()
+        if isinstance(data, QUrl) and data.isEmpty():
+            return
+        
+        if isinstance(data, QUrl):
+            data = data.toString()
+        QApplication.clipboard().setText(data)
+    
+    # TODO: Qt 5.6
+##    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)
+    
+    # TODO: AdBlock
+##    def __blockImage(self):
+##        """
+##        Private slot to add a block rule for an image URL.
+##        """
+##        import WebBrowser.WebBrowserWindow
+##        act = self.sender()
+##        url = act.data()
+##        dlg = WebBrowser.WebBrowserWindow.WebBrowserWindow.adBlockManager().showDialog()
+##        dlg.addCustomRule(url)
+    
+    # TODO: Qt 5.6
+##    def __downloadMedia(self):
+##        """
+##        Private slot to download a media and save it to disk.
+##        """
+##        self.triggerPageAction(QWebEnginePage.DownloadMediaToDisk)
+    
+    # TODO: Qt 5.6: do this with triggerPageAction()
+    def __pauseMedia(self):
+        """
+        Private slot to pause or play the selected media.
+        """
+        from .Tools import Scripts
+        script = Scripts.toggleMediaPause(self.__clickedPos)
+        self.page().runJavaScript(script)
+    
+    # TODO: Qt 5.6: do this with triggerPageAction()
+    def __muteMedia(self):
+        """
+        Private slot to (un)mute the selected media.
+        """
+        from .Tools import Scripts
+        script = Scripts.toggleMediaMute(self.__clickedPos)
+        self.page().runJavaScript(script)
+    
+    def __virusTotal(self):
+        """
+        Private slot to scan the selected URL with VirusTotal.
+        """
+        act = self.sender()
+        url = act.data()
+        self.__mw.requestVirusTotalScan(url)
+    
+    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)
+            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
+        
+        # TODO: Access Keys
+##        if self.__enableAccessKeys:
+##            self.__accessKeysPressed = (
+##                evt.modifiers() == Qt.ControlModifier and
+##                evt.key() == Qt.Key_Control)
+##            if not self.__accessKeysPressed:
+##                if self.__checkForAccessKey(evt):
+##                    self.__hideAccessKeys()
+##                    evt.accept()
+##                    return
+##                self.__hideAccessKeys()
+##            else:
+##                QTimer.singleShot(300, self.__accessKeyShortcut)
+        
+        self.__ctrlPressed = (evt.key() == Qt.Key_Control)
+        super(WebBrowserView, self).keyPressEvent(evt)
+    
+    def _keyReleaseEvent(self, evt):
+        """
+        Protected method called by a key release.
+        
+        @param evt reference to the key event (QKeyEvent)
+        """
+        # TODO: Access Keys
+##        if self.__enableAccessKeys:
+##            self.__accessKeysPressed = evt.key() == Qt.Key_Control
+        
+        self.__ctrlPressed = False
+        super(WebBrowserView, self).keyReleaseEvent(evt)
+    
+    def focusOutEvent(self, evt):
+        """
+        Protected method called by a focus out event.
+        
+        @param evt reference to the focus event (QFocusEvent)
+        """
+        # TODO: Access Keys
+##        if self.__accessKeysPressed:
+##            self.__hideAccessKeys()
+##            self.__accessKeysPressed = False
+        
+        super(WebBrowserView, self).focusOutEvent(evt)
+    
+    # TODO: Gestures: Obsoleted by eventFilter() (?)
+    def event(self, evt):
+        """
+        Public method handling events.
+        
+        @param evt reference to the event (QEvent)
+        @return flag indicating, if the event was handled (boolean)
+        """
+        if evt.type() == QEvent.Gesture:
+            self._gestureEvent(evt)
+            return True
+        
+        return super(WebBrowserView, self).event(evt)
+    
+    def _gestureEvent(self, evt):
+        """
+        Protected method handling gesture events.
+        
+        @param evt reference to the gesture event (QGestureEvent
+        """
+        pinch = evt.gesture(Qt.PinchGesture)
+        if pinch:
+            if pinch.state() == Qt.GestureStarted:
+                pinch.setScaleFactor(self.__currentZoom / 100.0)
+            else:
+                scaleFactor = pinch.scaleFactor()
+                self.__currentZoom = int(scaleFactor * 100)
+                self.__applyZoom()
+            evt.accept()
+    
+    def eventFilter(self, obj, evt):
+        """
+        Public method to process event for other objects.
+        
+        @param obj reference to object to process events for
+        @type QObject
+        @param evt reference to event to be processed
+        @type QEvent
+        @return flag indicating that the event should be filtered out
+        @rtype bool
+        """
+        # find the render widget receiving events for the web page
+        if obj is self and evt.type() == QEvent.ChildAdded:
+            child = evt.child()
+            if child and child.inherits(
+                    "QtWebEngineCore::RenderWidgetHostViewQtDelegateWidget"):
+                self.__rwhvqt = child
+                self.grabGesture(Qt.PinchGesture)
+                self.__rwhvqt.installEventFilter(self)
+        
+        # forward events to WebBrowserView
+        if obj is self.__rwhvqt:
+            wasAccepted = evt.isAccepted()
+            evt.setAccepted(False)
+            if evt.type() == QEvent.KeyPress:
+                self._keyPressEvent(evt)
+            elif evt.type() == QEvent.KeyRelease:
+                self._keyReleaseEvent(evt)
+            elif evt.type() == QEvent.MouseButtonPress:
+                self._mousePressEvent(evt)
+            elif evt.type() == QEvent.MouseButtonRelease:
+                self._mouseReleaseEvent(evt)
+            elif evt.type() == QEvent.Wheel:
+                self._wheelEvent(evt)
+            elif evt.type() == QEvent.Gesture:
+                self._gestureEvent(evt)
+            ret = evt.isAccepted()
+            evt.setAccepted(wasAccepted)
+            return ret
+        
+        # block already handled events
+        if obj is self:
+            if evt.type() in [QEvent.KeyPress, QEvent.KeyRelease,
+                              QEvent.MouseButtonPress,
+                              QEvent.MouseButtonRelease,
+                              QEvent.Wheel, QEvent.Gesture]:
+                return True
+        
+        return super(WebBrowserView, self).eventFilter(obj, evt)
+    
+    def clearHistory(self):
+        """
+        Public slot to clear the history.
+        """
+        self.history().clear()
+        self.__urlChanged(self.history().currentItem().url())
+    
+    ###########################################################################
+    ## Signal converters below
+    ###########################################################################
+    
+    def __urlChanged(self, url):
+        """
+        Private slot to handle the urlChanged signal.
+        
+        @param url the new url (QUrl)
+        """
+        self.sourceChanged.emit(url)
+        
+        self.forwardAvailable.emit(self.isForwardAvailable())
+        self.backwardAvailable.emit(self.isBackwardAvailable())
+    
+    def __iconUrlChanged(self, url):
+        """
+        Private slot to handle the iconUrlChanged signal.
+        
+        @param url URL to get web site icon from
+        @type QUrl
+        """
+        self.__siteIcon = QIcon()
+        if self.__siteIconLoader is not None:
+            self.__siteIconLoader.deleteLater()
+        self.__siteIconLoader = WebIconLoader(url, self)
+        self.__siteIconLoader.iconLoaded.connect(self.__iconLoaded)
+    
+    def __iconLoaded(self, icon):
+        """
+        Private slot handling the loaded web site icon.
+        
+        @param icon web site icon
+        @type QIcon
+        """
+        self.__siteIcon = icon
+        
+        from .Tools import WebIconProvider
+        WebIconProvider.instance().saveIcon(self)
+        
+        self.iconChanged.emit()
+    
+    def icon(self):
+        """
+        Public method to get the web site icon.
+        
+        @return web site icon
+        @rtype QIcon
+        """
+        if not self.__siteIcon.isNull():
+            return QIcon(self.__siteIcon)
+        
+        from .Tools import WebIconProvider
+        return WebIconProvider.instance().iconForUrl(self.url())
+    
+##    def __statusBarMessage(self, text):
+##        """
+##        Private slot to handle the statusBarMessage signal.
+##        
+##        @param text text to be shown in the status bar (string)
+##        """
+##        self.__mw.statusBar().showMessage(text)
+##    
+    def __linkHovered(self, link):
+        """
+        Private slot to handle the linkHovered signal.
+        
+        @param link the URL of the link (string)
+        """
+        self.highlighted.emit(link)
+    
+    ###########################################################################
+    ## Signal handlers below
+    ###########################################################################
+    
+    def __loadStarted(self):
+        """
+        Private method to handle the loadStarted signal.
+        """
+        self.__isLoading = True
+        self.__progress = 0
+    
+    def __loadProgress(self, progress):
+        """
+        Private method to handle the loadProgress signal.
+        
+        @param progress progress value (integer)
+        """
+        self.__progress = progress
+    
+    def __loadFinished(self, ok):
+        """
+        Private method to handle the loadFinished signal.
+        
+        @param ok flag indicating the result (boolean)
+        """
+        self.__isLoading = False
+        self.__progress = 0
+        
+        # TODO: ClickToFlash (?)
+##        if Preferences.getWebBrowser("ClickToFlashEnabled"):
+##            # this is a hack to make the ClickToFlash button appear
+##            self.zoomIn()
+##            self.zoomOut()
+        
+        from .ZoomManager import ZoomManager
+        zoomValue = ZoomManager.instance().zoomValue(self.url())
+        self.setZoomValue(zoomValue)
+        
+        if ok:
+            self.__mw.historyManager().addHistoryEntry(self)
+            # TODO: AdBlock
+##            self.__mw.adBlockManager().page().hideBlockedPageEntries(self.page())
+            self.__mw.passwordManager().completePage(self.page())
+    
+    def isLoading(self):
+        """
+        Public method to get the loading state.
+        
+        @return flag indicating the loading state (boolean)
+        """
+        return self.__isLoading
+    
+    def progress(self):
+        """
+        Public method to get the load progress.
+        
+        @return load progress (integer)
+        """
+        return self.__progress
+    
+##    def saveAs(self):
+##        """
+##        Public method to save the current page to a file.
+##        """
+##        url = self.url()
+##        if url.isEmpty():
+##            return
+##        
+##        self.__mw.downloadManager().download(url, True, mainWindow=self.__mw)
+    
+##    def __unsupportedContent(self, reply, requestFilename=None,
+##                             download=False):
+##        """
+##        Private slot to handle the unsupportedContent signal.
+##        
+##        @param reply reference to the reply object (QNetworkReply)
+##        @keyparam requestFilename indicating to ask for a filename
+##            (boolean or None). If it is None, the behavior is determined
+##            by a configuration option.
+##        @keyparam download flag indicating a download operation (boolean)
+##        """
+##        if reply is None:
+##            return
+##        
+##        replyUrl = reply.url()
+##        
+##        if replyUrl.scheme() == "abp":
+##            return
+##        
+##        if reply.error() == QNetworkReply.NoError:
+##            if reply.header(QNetworkRequest.ContentTypeHeader):
+##                self.__mw.downloadManager().handleUnsupportedContent(
+##                    reply, webPage=self.page(), mainWindow=self.__mw)
+##                return
+##        
+##        replyUrl = reply.url()
+##        if replyUrl.isEmpty():
+##            return
+##        
+##        notFoundFrame = self.page().mainFrame()
+##        if notFoundFrame is None:
+##            return
+##        
+##        if reply.header(QNetworkRequest.ContentTypeHeader):
+##            data = reply.readAll()
+##            if contentSniff(data):
+##                notFoundFrame.setHtml(str(data, encoding="utf-8"), replyUrl)
+##                return
+##        
+##        urlString = bytes(replyUrl.toEncoded()).decode()
+##        title = self.tr("Error loading page: {0}").format(urlString)
+##        htmlFile = QFile(":/html/notFoundPage.html")
+##        htmlFile.open(QFile.ReadOnly)
+##        html = htmlFile.readAll()
+##        pixmap = qApp.style()\
+##            .standardIcon(QStyle.SP_MessageBoxWarning).pixmap(48, 48)
+##        imageBuffer = QBuffer()
+##        imageBuffer.open(QIODevice.ReadWrite)
+##        if pixmap.save(imageBuffer, "PNG"):
+##            html = html.replace("@IMAGE@", imageBuffer.buffer().toBase64())
+##        pixmap = qApp.style()\
+##            .standardIcon(QStyle.SP_MessageBoxWarning).pixmap(16, 16)
+##        imageBuffer = QBuffer()
+##        imageBuffer.open(QIODevice.ReadWrite)
+##        if pixmap.save(imageBuffer, "PNG"):
+##            html = html.replace("@FAVICON@", imageBuffer.buffer().toBase64())
+##        html = html.replace("@TITLE@", title.encode("utf8"))
+##        html = html.replace("@H1@", reply.errorString().encode("utf8"))
+##        html = html.replace(
+##            "@H2@", self.tr("When connecting to: {0}.")
+##            .format(urlString).encode("utf8"))
+##        html = html.replace(
+##            "@LI-1@",
+##            self.tr("Check the address for errors such as "
+##                    "<b>ww</b>.example.org instead of "
+##                    "<b>www</b>.example.org").encode("utf8"))
+##        html = html.replace(
+##            "@LI-2@",
+##            self.tr("If the address is correct, try checking the network "
+##                    "connection.").encode("utf8"))
+##        html = html.replace(
+##            "@LI-3@",
+##            self.tr(
+##                "If your computer or network is protected by a firewall "
+##                "or proxy, make sure that the browser is permitted to "
+##                "access the network.").encode("utf8"))
+##        html = html.replace(
+##            "@LI-4@",
+##            self.tr("If your cache policy is set to offline browsing,"
+##                    "only pages in the local cache are available.")
+##            .encode("utf8"))
+##        html = html.replace(
+##            "@BUTTON@", self.tr("Try Again").encode("utf8"))
+##        notFoundFrame.setHtml(bytes(html).decode("utf8"), replyUrl)
+##        self.__mw.historyManager().removeHistoryEntry(replyUrl, self.title())
+##        self.loadFinished.emit(False)
+##    
+    
+##    def __databaseQuotaExceeded(self, frame, databaseName):
+##        """
+##        Private slot to handle the case, where the database quota is exceeded.
+##        
+##        @param frame reference to the frame (QWebFrame)
+##        @param databaseName name of the web database (string)
+##        """
+##        securityOrigin = frame.securityOrigin()
+##        if securityOrigin.databaseQuota() > 0 and \
+##           securityOrigin.databaseUsage() == 0:
+##            # cope with a strange behavior of Qt 4.6, if a database is
+##            # accessed for the first time
+##            return
+##        
+##        res = E5MessageBox.yesNo(
+##            self,
+##            self.tr("Web Database Quota"),
+##            self.tr(
+##                """<p>The database quota of <strong>{0}</strong> has"""
+##                """ been exceeded while accessing database <strong>{1}"""
+##                """</strong>.</p><p>Shall it be changed?</p>""")
+##            .format(self.__dataString(securityOrigin.databaseQuota()),
+##                    databaseName),
+##            yesDefault=True)
+##        if res:
+##            newQuota, ok = QInputDialog.getInt(
+##                self,
+##                self.tr("New Web Database Quota"),
+##                self.tr(
+##                    "Enter the new quota in MB (current = {0}, used = {1}; "
+##                    "step size = 5 MB):"
+##                    .format(
+##                        self.__dataString(securityOrigin.databaseQuota()),
+##                        self.__dataString(securityOrigin.databaseUsage()))),
+##                securityOrigin.databaseQuota() // (1024 * 1024),
+##                0, 2147483647, 5)
+##            if ok:
+##                securityOrigin.setDatabaseQuota(newQuota * 1024 * 1024)
+##    
+##    def __dataString(self, size):
+##        """
+##        Private method to generate a formatted data string.
+##        
+##        @param size size to be formatted (integer)
+##        @return formatted data string (string)
+##        """
+##        unit = ""
+##        if size < 1024:
+##            unit = self.tr("bytes")
+##        elif size < 1024 * 1024:
+##            size /= 1024
+##            unit = self.tr("kB")
+##        else:
+##            size /= 1024 * 1024
+##            unit = self.tr("MB")
+##        return "{0:.1f} {1}".format(size, unit)
+    
+    ###########################################################################
+    ## Access key related methods below
+    ###########################################################################
+    
+    # TODO: Access Keys
+##    def __accessKeyShortcut(self):
+##        """
+##        Private slot to switch the display of access keys.
+##        """
+##        if not self.hasFocus() or \
+##           not self.__accessKeysPressed or \
+##           not self.__enableAccessKeys:
+##            return
+##        
+##        if self.__accessKeyLabels:
+##            self.__hideAccessKeys()
+##        else:
+##            self.__showAccessKeys()
+##        
+##        self.__accessKeysPressed = False
+##    
+##    def __checkForAccessKey(self, evt):
+##        """
+##        Private method to check the existence of an access key and activate the
+##        corresponding link.
+##        
+##        @param evt reference to the key event (QKeyEvent)
+##        @return flag indicating, if the event was handled (boolean)
+##        """
+##        if not self.__accessKeyLabels:
+##            return False
+##        
+##        text = evt.text()
+##        if not text:
+##            return False
+##        
+##        key = text[0].upper()
+##        handled = False
+##        if key in self.__accessKeyNodes:
+##            element = self.__accessKeyNodes[key]
+##            p = element.geometry().center()
+##            frame = element.webFrame()
+##            p -= frame.scrollPosition()
+##            frame = frame.parentFrame()
+##            while frame and frame != self.page().mainFrame():
+##                p -= frame.scrollPosition()
+##                frame = frame.parentFrame()
+##            pevent = QMouseEvent(
+##                QEvent.MouseButtonPress, p, Qt.LeftButton,
+##                Qt.MouseButtons(Qt.NoButton),
+##                Qt.KeyboardModifiers(Qt.NoModifier))
+##            qApp.sendEvent(self, pevent)
+##            revent = QMouseEvent(
+##                QEvent.MouseButtonRelease, p, Qt.LeftButton,
+##                Qt.MouseButtons(Qt.NoButton),
+##                Qt.KeyboardModifiers(Qt.NoModifier))
+##            qApp.sendEvent(self, revent)
+##            handled = True
+##        
+##        return handled
+##    
+##    def __hideAccessKeys(self):
+##        """
+##        Private slot to hide the access key labels.
+##        """
+##        if self.__accessKeyLabels:
+##            for label in self.__accessKeyLabels:
+##                label.hide()
+##                label.deleteLater()
+##            self.__accessKeyLabels = []
+##            self.__accessKeyNodes = {}
+##            self.update()
+##    
+##    def __showAccessKeys(self):
+##        """
+##        Private method to show the access key labels.
+##        """
+##        supportedElements = [
+##            "input", "a", "area", "button", "label", "legend", "textarea",
+##        ]
+##        unusedKeys = "A B C D E F G H I J K L M N O P Q R S T U V W X Y Z" \
+##            " 0 1 2 3 4 5 6 7 8 9".split()
+##        
+##        viewport = QRect(self.__page.mainFrame().scrollPosition(),
+##                         self.__page.viewportSize())
+##        # Priority first goes to elements with accesskey attributes
+##        alreadyLabeled = []
+##        for elementType in supportedElements:
+##            result = self.page().mainFrame().findAllElements(elementType)\
+##                .toList()
+##            for element in result:
+##                geometry = element.geometry()
+##                if geometry.size().isEmpty() or \
+##                   not viewport.contains(geometry.topLeft()):
+##                    continue
+##                
+##                accessKeyAttribute = element.attribute("accesskey").upper()
+##                if not accessKeyAttribute:
+##                    continue
+##                
+##                accessKey = ""
+##                i = 0
+##                while i < len(accessKeyAttribute):
+##                    if accessKeyAttribute[i] in unusedKeys:
+##                        accessKey = accessKeyAttribute[i]
+##                        break
+##                    i += 2
+##                if accessKey == "":
+##                    continue
+##                unusedKeys.remove(accessKey)
+##                self.__makeAccessLabel(accessKey, element)
+##                alreadyLabeled.append(element)
+##        
+##        # Pick an access key first from the letters in the text and then
+##        # from the list of unused access keys
+##        for elementType in supportedElements:
+##            result = self.page().mainFrame().findAllElements(elementType)\
+##                .toList()
+##            for element in result:
+##                geometry = element.geometry()
+##                if not unusedKeys or \
+##                   element in alreadyLabeled or \
+##                   geometry.size().isEmpty() or \
+##                   not viewport.contains(geometry.topLeft()):
+##                    continue
+##                
+##                accessKey = ""
+##                text = element.toPlainText().upper()
+##                for c in text:
+##                    if c in unusedKeys:
+##                        accessKey = c
+##                        break
+##                if accessKey == "":
+##                    accessKey = unusedKeys[0]
+##                unusedKeys.remove(accessKey)
+##                self.__makeAccessLabel(accessKey, element)
+##    
+##    def __makeAccessLabel(self, accessKey, element):
+##        """
+##        Private method to generate the access label for an element.
+##        
+##        @param accessKey access key to generate the label for (str)
+##        @param element reference to the web element to create the label for
+##            (QWebElement)
+##        """
+##        label = QLabel(self)
+##        label.setText("<qt><b>{0}</b></qt>".format(accessKey))
+##        
+##        p = QToolTip.palette()
+##        color = QColor(Qt.yellow).lighter(150)
+##        color.setAlpha(175)
+##        p.setColor(QPalette.Window, color)
+##        label.setPalette(p)
+##        label.setAutoFillBackground(True)
+##        label.setFrameStyle(QFrame.Box | QFrame.Plain)
+##        point = element.geometry().center()
+##        point -= self.__page.mainFrame().scrollPosition()
+##        label.move(point)
+##        label.show()
+##        point.setX(point.x() - label.width() // 2)
+##        label.move(point)
+##        self.__accessKeyLabels.append(label)
+##        self.__accessKeyNodes[accessKey] = element
+    
+    ###########################################################################
+    ## Miscellaneous methods below
+    ###########################################################################
+    
+    # TODO: check, if this is needed (referenced anywhere) (same for HelpBrowserWV)
+    def createWindow(self, windowType):
+        """
+        Public method called, when a new window should be created.
+        
+        @param windowType type of the requested window (QWebPage.WebWindowType)
+        @return reference to the created browser window (HelpBrowser)
+        """
+        self.__mw.newTab(addNextTo=self)
+        return self.__mw.currentBrowser()
+    
+    def preferencesChanged(self):
+        """
+        Public method to indicate a change of the settings.
+        """
+        # TODO: Access Keys
+##        self.__enableAccessKeys = Preferences.getWebBrowser("AccessKeysEnabled")
+##        if not self.__enableAccessKeys:
+##            self.__hideAccessKeys()
+        
+        self.reload()
+    
+    ###########################################################################
+    ## RSS related methods below
+    ###########################################################################
+    
+    def checkRSS(self):
+        """
+        Public method to check, if the loaded page contains feed links.
+        
+        @return flag indicating the existence of feed links (boolean)
+        """
+        self.__rss = []
+        
+        script = Scripts.getFeedLinks()
+        feeds = self.page().execJavaScript(script)
+        
+        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
+    
+    ###########################################################################
+    ## Clicked Frame slots
+    ###########################################################################
+    
+##    def __loadClickedFrame(self):
+##        """
+##        Private slot to load the selected frame only.
+##        """
+##        self.setSource(self.__clickedFrame.url())
+##    
+##    def __printClickedFrame(self):
+##        """
+##        Private slot to print the selected frame.
+##        """
+##        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)
+##        
+##        printDialog = QPrintDialog(printer, self)
+##        if printDialog.exec_() == QDialog.Accepted:
+##            try:
+##                self.__clickedFrame.print_(printer)
+##            except AttributeError:
+##                E5MessageBox.critical(
+##                    self,
+##                    self.tr("eric6 Web Browser"),
+##                    self.tr(
+##                        """<p>Printing is not available due to a bug in"""
+##                        """ PyQt5. Please upgrade.</p>"""))
+##    
+##    def __printPreviewClickedFrame(self):
+##        """
+##        Private slot to show a print preview of the clicked frame.
+##        """
+##        from PyQt5.QtPrintSupport import QPrintPreviewDialog
+##        
+##        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)
+##        
+##        preview = QPrintPreviewDialog(printer, self)
+##        preview.paintRequested.connect(self.__generatePrintPreviewClickedFrame)
+##        preview.exec_()
+##    
+##    def __generatePrintPreviewClickedFrame(self, printer):
+##        """
+##        Private slot to generate a print preview of the clicked frame.
+##        
+##        @param printer reference to the printer object (QPrinter)
+##        """
+##        try:
+##            self.__clickedFrame.print_(printer)
+##        except AttributeError:
+##            E5MessageBox.critical(
+##                self,
+##                self.tr("eric6 Web Browser"),
+##                self.tr(
+##                    """<p>Printing is not available due to a bug in PyQt5."""
+##                    """Please upgrade.</p>"""))
+##            return
+##    
+##    def __printPdfClickedFrame(self):
+##        """
+##        Private slot to print the selected frame to PDF.
+##        """
+##        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 = self.__clickedFrame.url().path().rsplit('/', 1)[-1]
+##        if name:
+##            name = name.rsplit('.', 1)[0]
+##            name += '.pdf'
+##            printer.setOutputFileName(name)
+##        
+##        printDialog = QPrintDialog(printer, self)
+##        if printDialog.exec_() == QDialog.Accepted:
+##            try:
+##                self.__clickedFrame.print_(printer)
+##            except AttributeError:
+##                E5MessageBox.critical(
+##                    self,
+##                    self.tr("eric6 Web Browser"),
+##                    self.tr(
+##                        """<p>Printing is not available due to a bug in"""
+##                        """ PyQt5. Please upgrade.</p>"""))
+##                return
+##    
+##    def __zoomInClickedFrame(self):
+##        """
+##        Private slot to zoom into the clicked frame.
+##        """
+##        index = self.__levelForZoom(
+##            int(self.__clickedFrame.zoomFactor() * 100))
+##        if index < len(self.__zoomLevels) - 1:
+##            self.__clickedFrame.setZoomFactor(
+##                self.__zoomLevels[index + 1] / 100)
+##    
+##    def __zoomResetClickedFrame(self):
+##        """
+##        Private slot to reset the zoom factor of the clicked frame.
+##        """
+##        self.__clickedFrame.setZoomFactor(self.__currentZoom / 100)
+##    
+##    def __zoomOutClickedFrame(self):
+##        """
+##        Private slot to zoom out of the clicked frame.
+##        """
+##        index = self.__levelForZoom(
+##            int(self.__clickedFrame.zoomFactor() * 100))
+##        if index > 0:
+##            self.__clickedFrame.setZoomFactor(
+##                self.__zoomLevels[index - 1] / 100)
+##    
+##    def __showClickedFrameSource(self):
+##        """
+##        Private slot to show the source of the clicked frame.
+##        """
+##        from QScintilla.MiniEditor import MiniEditor
+##        src = self.__clickedFrame.toHtml()
+##        editor = MiniEditor(parent=self)
+##        editor.setText(src, "Html")
+##        editor.setLanguage("dummy.html")
+##        editor.show()
+
+
+##def contentSniff(data):
+##    """
+##    Module function to do some content sniffing to check, if the data is HTML.
+##    
+##    @param data data block to sniff at (string)
+##    @return flag indicating HTML content (boolean)
+##    """
+##    if data.contains("<!doctype") or \
+##       data.contains("<script") or \
+##       data.contains("<html") or \
+##       data.contains("<!--") or \
+##       data.contains("<head") or \
+##       data.contains("<iframe") or \
+##       data.contains("<h1") or \
+##       data.contains("<div") or \
+##       data.contains("<font") or \
+##       data.contains("<table") or \
+##       data.contains("<a") or \
+##       data.contains("<style") or \
+##       data.contains("<title") or \
+##       data.contains("<b") or \
+##       data.contains("<body") or \
+##       data.contains("<br") or \
+##       data.contains("<p"):
+##        return True
+##    
+##    return False
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/WebBrowserWebSearchWidget.py	Sun Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,4114 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2002 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the web browser main window.
+"""
+
+from __future__ import unicode_literals
+try:
+    str = unicode           # __IGNORE_EXCEPTION__
+except NameError:
+    pass
+
+import os
+import shutil
+import sys
+
+from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt, QByteArray, QSize, QTimer, \
+    QUrl, QThread, QTextCodec, QProcess
+from PyQt5.QtGui import QDesktopServices, QKeySequence, QFont, QFontMetrics, \
+    QIcon
+from PyQt5.QtWidgets import QWidget, QVBoxLayout, QSizePolicy, QDockWidget, \
+    QComboBox, QLabel, QSplitter, QMenu, QToolButton, QLineEdit, \
+    QApplication, QWhatsThis, QDialog, QHBoxLayout, QProgressBar, QAction, \
+    QInputDialog
+##from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest
+from PyQt5.QtWebEngineWidgets import QWebEngineSettings, QWebEnginePage, \
+    QWebEngineProfile, QWebEngineScript
+try:
+    from PyQt5.QtHelp import QHelpEngine, QHelpEngineCore, QHelpSearchQuery
+    QTHELP_AVAILABLE = True
+except ImportError:
+    QTHELP_AVAILABLE = False
+
+from E5Gui.E5Action import E5Action
+from E5Gui import E5MessageBox, E5FileDialog, E5ErrorMessage
+from E5Gui.E5MainWindow import E5MainWindow
+from E5Gui.E5Application import e5App
+from E5Gui.E5ZoomWidget import E5ZoomWidget
+
+from E5Network.E5NetworkIcon import E5NetworkIcon
+
+import Preferences
+from Preferences import Shortcuts
+
+import Utilities
+import Globals
+
+import UI.PixmapCache
+import UI.Config
+from UI.Info import Version
+##
+##from .data import icons_rc          # __IGNORE_WARNING__
+##from .data import html_rc           # __IGNORE_WARNING__
+from .data import javascript_rc     # __IGNORE_WARNING__
+
+
+from .Tools import Scripts, WebBrowserTools, WebIconProvider
+
+from .ZoomManager import ZoomManager
+
+from eric6config import getConfig
+
+
+class WebBrowserWindow(E5MainWindow):
+    """
+    Class implementing the web browser main window.
+    
+    @signal webBrowserClosed() emitted after the window was requested to close
+    @signal zoomTextOnlyChanged(bool) emitted after the zoom text only setting
+        was changed
+    """
+    webBrowserClosed = pyqtSignal()
+    
+    BrowserWindows = []
+
+##    maxMenuFilePathLen = 75
+##    
+    _fromEric = False
+    UseQtHelp = QTHELP_AVAILABLE
+    _isPrivate = False
+    
+    _webProfile = None
+    _networkManager = None
+##    _cookieJar = None
+##    _helpEngine = None
+    _bookmarksManager = None
+    _historyManager = None
+    _passwordManager = None
+##    _adblockManager = None
+    _downloadManager = None
+    _feedsManager = None
+##    _userAgentsManager = None
+    _syncManager = None
+##    _speedDial = None
+    _personalInformationManager = None
+    _greaseMonkeyManager = None
+    _notification = None
+    _featurePermissionManager = None
+    _flashCookieManager = None
+    
+    def __init__(self, home, path, parent, name, fromEric=False,
+                 initShortcutsOnly=False, searchWord=None,
+                 private=False, 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 settingsDir directory to be used for the settings files (str)
+        """
+        super(WebBrowserWindow, self).__init__(parent)
+        self.setObjectName(name)
+        self.setWindowTitle(self.tr("eric6 Web Browser"))
+        
+        self.__settingsDir = settingsDir
+        self.__fromEric = fromEric
+        WebBrowserWindow._fromEric = fromEric
+        self.__initShortcutsOnly = initShortcutsOnly
+        self.setWindowIcon(UI.PixmapCache.getIcon("ericWeb.png"))
+
+        self.__mHistory = []
+        self.__lastConfigurationPageName = ""
+        
+        WebBrowserWindow._isPrivate = private
+        
+        self.__eventMouseButtons = Qt.NoButton
+        self.__eventKeyboardModifiers = Qt.NoModifier
+        
+        if self.__initShortcutsOnly:
+            self.__initActions()
+        else:
+            if Preferences.getWebBrowser("WebInspectorEnabled"):
+                os.putenv(
+                    "QTWEBENGINE_REMOTE_DEBUGGING",
+                    str(Preferences.getWebBrowser("WebInspectorPort")))
+            
+            self.webProfile(private)
+            
+            from .SearchWidget import SearchWidget
+            # TODO: QtHelp
+##            from .HelpTocWidget import HelpTocWidget
+##            from .HelpIndexWidget import HelpIndexWidget
+##            from .HelpSearchWidget import HelpSearchWidget
+            from .WebBrowserView import WebBrowserView
+            from .WebBrowserTabWidget import WebBrowserTabWidget
+            # TODO: AdBlock
+##            from .AdBlock.AdBlockIcon import AdBlockIcon
+            from .VirusTotal.VirusTotalApi import VirusTotalAPI
+            
+            # TODO: allow using Qt Help even if not called from eric6
+            WebBrowserWindow.setUseQtHelp(self.__fromEric)
+            
+            if not self.__fromEric:
+                self.setStyle(Preferences.getUI("Style"),
+                              Preferences.getUI("StyleSheet"))
+                
+                # TODO: Check if this is needed with QtWebEngine
+                # initialize some SSL stuff
+##                from E5Network.E5SslUtilities import initSSL
+##                initSSL()
+            
+            # TODO: QtHelp
+##            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()
+            
+            # TODO: QtHelp, do these once Qt 5.6 is available
+##            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)
+##            
+            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()
+            
+            # TODO: AdBlock
+##            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
+            # TODO: QtHelp, do these once Qt 5.6 is available
+##            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)
+            
+            # TODO: QtHelp
+##            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()
+            
+            # TODO: QtHelp, do these once Qt 5.6 is available
+##            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"))
+        # TODO: Qt 5.6
+##        settings.setAttribute(
+##            QWebSettings.JavaEnabled,
+##            Preferences.getWebBrowser("JavaEnabled"))
+        settings.setAttribute(
+            QWebEngineSettings.JavascriptEnabled,
+            Preferences.getWebBrowser("JavaScriptEnabled"))
+        settings.setAttribute(
+            QWebEngineSettings.JavascriptCanOpenWindows,
+            Preferences.getWebBrowser("JavaScriptCanOpenWindows"))
+        settings.setAttribute(
+            QWebEngineSettings.JavascriptCanAccessClipboard,
+            Preferences.getWebBrowser("JavaScriptCanAccessClipboard"))
+        # TODO: Qt 5.6
+##        settings.setAttribute(
+##            QWebSettings.PluginsEnabled,
+##            Preferences.getWebBrowser("PluginsEnabled"))
+        
+##        if hasattr(QWebSettings, "PrintElementBackgrounds"):
+##            settings.setAttribute(
+##                QWebSettings.PrintElementBackgrounds,
+##                Preferences.getWebBrowser("PrintBackgrounds"))
+##        
+##        if hasattr(QWebSettings, "setOfflineStoragePath"):
+##            settings.setAttribute(
+##                QWebSettings.OfflineStorageDatabaseEnabled,
+##                Preferences.getWebBrowser("OfflineStorageDatabaseEnabled"))
+##            webDatabaseDir = os.path.join(
+##                Utilities.getConfigDir(), "web_browser", "webdatabases")
+##            if not os.path.exists(webDatabaseDir):
+##                os.makedirs(webDatabaseDir)
+##            settings.setOfflineStoragePath(webDatabaseDir)
+##            settings.setOfflineStorageDefaultQuota(
+##                Preferences.getWebBrowser("OfflineStorageDatabaseQuota") *
+##                1024 * 1024)
+##        
+##        if hasattr(QWebSettings, "OfflineWebApplicationCacheEnabled"):
+##            settings.setAttribute(
+##                QWebSettings.OfflineWebApplicationCacheEnabled,
+##                Preferences.getWebBrowser("OfflineWebApplicationCacheEnabled"))
+##            appCacheDir = os.path.join(
+##                Utilities.getConfigDir(), "web_browser", "webappcaches")
+##            if not os.path.exists(appCacheDir):
+##                os.makedirs(appCacheDir)
+##            settings.setOfflineWebApplicationCachePath(appCacheDir)
+##            settings.setOfflineWebApplicationCacheQuota(
+##                Preferences.getWebBrowser("OfflineWebApplicationCacheQuota") *
+##                1024 * 1024)
+##        
+        if self.isPrivate():
+            settings.setAttribute(
+                QWebEngineSettings.LocalStorageEnabled, False)
+        else:
+            settings.setAttribute(
+                QWebEngineSettings.LocalStorageEnabled,
+                Preferences.getWebBrowser("LocalStorageEnabled"))
+##        localStorageDir = os.path.join(
+##            Utilities.getConfigDir(), "web_browser", "weblocalstorage")
+##        if not os.path.exists(localStorageDir):
+##            os.makedirs(localStorageDir)
+##        settings.setLocalStoragePath(localStorageDir)
+##        
+##        if hasattr(QWebSettings, "DnsPrefetchEnabled"):
+##            settings.setAttribute(
+##                QWebSettings.DnsPrefetchEnabled,
+##                Preferences.getWebBrowser("DnsPrefetchEnabled"))
+##        
+        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"))
+##        
+##        QWebSecurityOrigin.addLocalScheme("eric")
+        settings.setAttribute(
+            QWebEngineSettings.ScrollAnimatorEnabled,
+            Preferences.getWebBrowser("ScrollAnimatorEnabled"))
+        settings.setAttribute(
+            QWebEngineSettings.ErrorPageEnabled,
+            Preferences.getWebBrowser("ErrorPageEnabled"))
+    
+    def __initActions(self):
+        """
+        Private method to define the user interface actions.
+        """
+        # list of all actions
+        self.__actions = []
+        
+        self.newTabAct = E5Action(
+            self.tr('New Tab'),
+            UI.PixmapCache.getIcon("tabNew.png"),
+            self.tr('&New Tab'),
+            QKeySequence(self.tr("Ctrl+T", "File|New Tab")),
+            0, self, 'webbrowser_file_new_tab')
+        self.newTabAct.setStatusTip(self.tr('Open a new web browser tab'))
+        self.newTabAct.setWhatsThis(self.tr(
+            """<b>New Tab</b>"""
+            """<p>This opens a new web browser tab.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.newTabAct.triggered.connect(self.newTab)
+        self.__actions.append(self.newTabAct)
+        
+        self.newAct = E5Action(
+            self.tr('New Window'),
+            UI.PixmapCache.getIcon("newWindow.png"),
+            self.tr('New &Window'),
+            QKeySequence(self.tr("Ctrl+N", "File|New Window")),
+            0, self, 'webbrowser_file_new_window')
+        self.newAct.setStatusTip(self.tr('Open a new web browser window'))
+        self.newAct.setWhatsThis(self.tr(
+            """<b>New Window</b>"""
+            """<p>This opens a new web browser window in the current"""
+            """ privacy mode.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.newAct.triggered.connect(self.newWindow)
+        self.__actions.append(self.newAct)
+        
+        self.newPrivateAct = E5Action(
+            self.tr('New Private Window'),
+            UI.PixmapCache.getIcon("privateMode.png"),
+            self.tr('New &Private Window'),
+            QKeySequence(self.tr("Ctrl+Shift+P", "File|New Private Window")),
+            0, self, 'webbrowser_file_new_private_window')
+        self.newPrivateAct.setStatusTip(self.tr(
+            'Open a new private web browser window'))
+        self.newPrivateAct.setWhatsThis(self.tr(
+            """<b>New Private Window</b>"""
+            """<p>This opens a new private web browser window by starting"""
+            """ a new web browser instance in private mode.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.newPrivateAct.triggered.connect(self.newPrivateWindow)
+        self.__actions.append(self.newPrivateAct)
+        
+        self.openAct = E5Action(
+            self.tr('Open File'),
+            UI.PixmapCache.getIcon("open.png"),
+            self.tr('&Open File'),
+            QKeySequence(self.tr("Ctrl+O", "File|Open")),
+            0, self, 'webbrowser_file_open')
+        self.openAct.setStatusTip(self.tr('Open a file for display'))
+        self.openAct.setWhatsThis(self.tr(
+            """<b>Open File</b>"""
+            """<p>This opens a new file for display."""
+            """ It pops up a file selection dialog.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.openAct.triggered.connect(self.__openFile)
+        self.__actions.append(self.openAct)
+        
+        self.openTabAct = E5Action(
+            self.tr('Open File in New Tab'),
+            UI.PixmapCache.getIcon("openNewTab.png"),
+            self.tr('Open File in New &Tab'),
+            QKeySequence(self.tr("Shift+Ctrl+O", "File|Open in new tab")),
+            0, self, 'webbrowser_file_open_tab')
+        self.openTabAct.setStatusTip(
+            self.tr('Open a file for display in a new tab'))
+        self.openTabAct.setWhatsThis(self.tr(
+            """<b>Open File in New Tab</b>"""
+            """<p>This opens a new file for display in a new tab."""
+            """ It pops up a file selection dialog.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.openTabAct.triggered.connect(self.__openFileNewTab)
+        self.__actions.append(self.openTabAct)
+        # TODO: Save
+##        
+##        self.saveAsAct = E5Action(
+##            self.tr('Save As'),
+##            UI.PixmapCache.getIcon("fileSaveAs.png"),
+##            self.tr('&Save As...'),
+##            QKeySequence(self.tr("Shift+Ctrl+S", "File|Save As")),
+##            0, self, 'webbrowser_file_save_as')
+##        self.saveAsAct.setStatusTip(
+##            self.tr('Save the current page to disk'))
+##        self.saveAsAct.setWhatsThis(self.tr(
+##            """<b>Save As...</b>"""
+##            """<p>Saves the current page to disk.</p>"""
+##        ))
+##        if not self.__initShortcutsOnly:
+##            self.saveAsAct.triggered.connect(self.__savePageAs)
+##        self.__actions.append(self.saveAsAct)
+##        
+        self.savePageScreenAct = E5Action(
+            self.tr('Save Page Screen'),
+            UI.PixmapCache.getIcon("fileSavePixmap.png"),
+            self.tr('Save Page Screen...'),
+            0, 0, self, 'webbrowser_file_save_page_screen')
+        self.savePageScreenAct.setStatusTip(
+            self.tr('Save the current page as a screen shot'))
+        self.savePageScreenAct.setWhatsThis(self.tr(
+            """<b>Save Page Screen...</b>"""
+            """<p>Saves the current page as a screen shot.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.savePageScreenAct.triggered.connect(self.__savePageScreen)
+        self.__actions.append(self.savePageScreenAct)
+        
+        self.saveVisiblePageScreenAct = E5Action(
+            self.tr('Save Visible Page Screen'),
+            UI.PixmapCache.getIcon("fileSaveVisiblePixmap.png"),
+            self.tr('Save Visible Page Screen...'),
+            0, 0, self, 'webbrowser_file_save_visible_page_screen')
+        self.saveVisiblePageScreenAct.setStatusTip(
+            self.tr('Save the visible part of the current page as a'
+                    ' screen shot'))
+        self.saveVisiblePageScreenAct.setWhatsThis(self.tr(
+            """<b>Save Visible Page Screen...</b>"""
+            """<p>Saves the visible part of the current page as a"""
+            """ screen shot.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.saveVisiblePageScreenAct.triggered.connect(
+                self.__saveVisiblePageScreen)
+        self.__actions.append(self.saveVisiblePageScreenAct)
+        
+        bookmarksManager = self.bookmarksManager()
+        self.importBookmarksAct = E5Action(
+            self.tr('Import Bookmarks'),
+            self.tr('&Import Bookmarks...'),
+            0, 0, self, 'webbrowser_file_import_bookmarks')
+        self.importBookmarksAct.setStatusTip(
+            self.tr('Import bookmarks from other browsers'))
+        self.importBookmarksAct.setWhatsThis(self.tr(
+            """<b>Import Bookmarks</b>"""
+            """<p>Import bookmarks from other browsers.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.importBookmarksAct.triggered.connect(
+                bookmarksManager.importBookmarks)
+        self.__actions.append(self.importBookmarksAct)
+        
+        self.exportBookmarksAct = E5Action(
+            self.tr('Export Bookmarks'),
+            self.tr('&Export Bookmarks...'),
+            0, 0, self, 'webbrowser_file_export_bookmarks')
+        self.exportBookmarksAct.setStatusTip(
+            self.tr('Export the bookmarks into a file'))
+        self.exportBookmarksAct.setWhatsThis(self.tr(
+            """<b>Export Bookmarks</b>"""
+            """<p>Export the bookmarks into a file.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.exportBookmarksAct.triggered.connect(
+                bookmarksManager.exportBookmarks)
+        self.__actions.append(self.exportBookmarksAct)
+        
+        self.printAct = E5Action(
+            self.tr('Print'),
+            UI.PixmapCache.getIcon("print.png"),
+            self.tr('&Print'),
+            QKeySequence(self.tr("Ctrl+P", "File|Print")),
+            0, self, 'webbrowser_file_print')
+        self.printAct.setStatusTip(self.tr('Print the displayed help'))
+        self.printAct.setWhatsThis(self.tr(
+            """<b>Print</b>"""
+            """<p>Print the displayed help text.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.printAct.triggered.connect(self.__tabWidget.printBrowser)
+        self.__actions.append(self.printAct)
+        
+        if Globals.isLinuxPlatform():
+            self.printPdfAct = E5Action(
+                self.tr('Print as PDF'),
+                UI.PixmapCache.getIcon("printPdf.png"),
+                self.tr('Print as PDF'),
+                0, 0, self, 'webbrowser_file_print_pdf')
+            self.printPdfAct.setStatusTip(self.tr(
+                'Print the displayed help as PDF'))
+            self.printPdfAct.setWhatsThis(self.tr(
+                """<b>Print as PDF</b>"""
+                """<p>Print the displayed help text as a PDF file.</p>"""
+            ))
+            if not self.__initShortcutsOnly:
+                self.printPdfAct.triggered.connect(
+                    self.__tabWidget.printBrowserPdf)
+            self.__actions.append(self.printPdfAct)
+        else:
+            self.printPdfAct = None
+        
+        self.printPreviewAct = E5Action(
+            self.tr('Print Preview'),
+            UI.PixmapCache.getIcon("printPreview.png"),
+            self.tr('Print Preview'),
+            0, 0, self, 'webbrowser_file_print_preview')
+        self.printPreviewAct.setStatusTip(self.tr(
+            'Print preview of the displayed help'))
+        self.printPreviewAct.setWhatsThis(self.tr(
+            """<b>Print Preview</b>"""
+            """<p>Print preview of the displayed help text.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.printPreviewAct.triggered.connect(
+                self.__tabWidget.printPreviewBrowser)
+        self.__actions.append(self.printPreviewAct)
+        
+        self.closeAct = E5Action(
+            self.tr('Close'),
+            UI.PixmapCache.getIcon("close.png"),
+            self.tr('&Close'),
+            QKeySequence(self.tr("Ctrl+W", "File|Close")),
+            0, self, 'webbrowser_file_close')
+        self.closeAct.setStatusTip(self.tr(
+            'Close the current help window'))
+        self.closeAct.setWhatsThis(self.tr(
+            """<b>Close</b>"""
+            """<p>Closes the current web browser window.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.closeAct.triggered.connect(self.__tabWidget.closeBrowser)
+        self.__actions.append(self.closeAct)
+        
+        self.closeAllAct = E5Action(
+            self.tr('Close All'),
+            self.tr('Close &All'),
+            0, 0, self, 'webbrowser_file_close_all')
+        self.closeAllAct.setStatusTip(self.tr('Close all help windows'))
+        self.closeAllAct.setWhatsThis(self.tr(
+            """<b>Close All</b>"""
+            """<p>Closes all web browser windows except the first one.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.closeAllAct.triggered.connect(
+                self.__tabWidget.closeAllBrowsers)
+        self.__actions.append(self.closeAllAct)
+        
+        self.exitAct = E5Action(
+            self.tr('Quit'),
+            UI.PixmapCache.getIcon("exit.png"),
+            self.tr('&Quit'),
+            QKeySequence(self.tr("Ctrl+Q", "File|Quit")),
+            0, self, 'webbrowser_file_quit')
+        self.exitAct.setStatusTip(self.tr('Quit the eric6 Web Browser'))
+        self.exitAct.setWhatsThis(self.tr(
+            """<b>Quit</b>"""
+            """<p>Quit the eric6 Web Browser.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            if self.__fromEric:
+                self.exitAct.triggered.connect(self.close)
+            else:
+                self.exitAct.triggered.connect(self.__closeAllWindows)
+        self.__actions.append(self.exitAct)
+        
+        self.backAct = E5Action(
+            self.tr('Backward'),
+            UI.PixmapCache.getIcon("back.png"),
+            self.tr('&Backward'),
+            QKeySequence(self.tr("Alt+Left", "Go|Backward")),
+            QKeySequence(self.tr("Backspace", "Go|Backward")),
+            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")),
+            QKeySequence(self.tr("Shift+Backspace", "Go|Forward")),
+            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)
+        
+        # TODO: Cookies
+##        self.cookiesAct = E5Action(
+##            self.tr('Cookies'),
+##            UI.PixmapCache.getIcon("cookie.png"),
+##            self.tr('C&ookies...'), 0, 0, self, 'webbrowser_cookies')
+##        self.cookiesAct.setStatusTip(self.tr(
+##            'Configure cookies handling'))
+##        self.cookiesAct.setWhatsThis(self.tr(
+##            """<b>Cookies</b>"""
+##            """<p>Configure cookies handling.</p>"""
+##        ))
+##        if not self.__initShortcutsOnly:
+##            self.cookiesAct.triggered.connect(
+##                self.__showCookiesConfiguration)
+##        self.__actions.append(self.cookiesAct)
+        
+        self.flashCookiesAct = E5Action(
+            self.tr('Flash Cookies'),
+            UI.PixmapCache.getIcon("flashCookie.png"),
+            self.tr('&Flash Cookies...'), 0, 0, self,
+            'webbrowser_flash_cookies')
+        self.flashCookiesAct.setStatusTip(self.tr(
+            'Manage flash cookies'))
+        self.flashCookiesAct.setWhatsThis(self.tr(
+            """<b>Flash Cookies</b>"""
+            """<p>Show a dialog to manage the flash cookies.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.flashCookiesAct.triggered.connect(
+                self.__showFlashCookiesManagement)
+        self.__actions.append(self.flashCookiesAct)
+        
+        # TODO: Offline Storage
+##        self.offlineStorageAct = E5Action(
+##            self.tr('Offline Storage'),
+##            UI.PixmapCache.getIcon("preferences-html5.png"),
+##            self.tr('Offline &Storage...'), 0, 0,
+##            self, 'webbrowser_offline_storage')
+##        self.offlineStorageAct.setStatusTip(self.tr(
+##            'Configure offline storage'))
+##        self.offlineStorageAct.setWhatsThis(self.tr(
+##            """<b>Offline Storage</b>"""
+##            """<p>Opens a dialog to configure offline storage.</p>"""
+##        ))
+##        if not self.__initShortcutsOnly:
+##            self.offlineStorageAct.triggered.connect(
+##                self.__showOfflineStorageConfiguration)
+##        self.__actions.append(self.offlineStorageAct)
+        
+        self.personalDataAct = E5Action(
+            self.tr('Personal Information'),
+            UI.PixmapCache.getIcon("pim.png"),
+            self.tr('Personal Information...'),
+            0, 0,
+            self, 'webbrowser_personal_information')
+        self.personalDataAct.setStatusTip(self.tr(
+            'Configure personal information for completing form fields'))
+        self.personalDataAct.setWhatsThis(self.tr(
+            """<b>Personal Information...</b>"""
+            """<p>Opens a dialog to configure the personal information"""
+            """ used for completing form fields.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.personalDataAct.triggered.connect(
+                self.__showPersonalInformationDialog)
+        self.__actions.append(self.personalDataAct)
+        
+        self.greaseMonkeyAct = E5Action(
+            self.tr('GreaseMonkey Scripts'),
+            UI.PixmapCache.getIcon("greaseMonkey.png"),
+            self.tr('GreaseMonkey Scripts...'),
+            0, 0,
+            self, 'webbrowser_greasemonkey')
+        self.greaseMonkeyAct.setStatusTip(self.tr(
+            'Configure the GreaseMonkey Scripts'))
+        self.greaseMonkeyAct.setWhatsThis(self.tr(
+            """<b>GreaseMonkey Scripts...</b>"""
+            """<p>Opens a dialog to configure the available GreaseMonkey"""
+            """ Scripts.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.greaseMonkeyAct.triggered.connect(
+                self.__showGreaseMonkeyConfigDialog)
+        self.__actions.append(self.greaseMonkeyAct)
+        
+        self.editMessageFilterAct = E5Action(
+            self.tr('Edit Message Filters'),
+            UI.PixmapCache.getIcon("warning.png"),
+            self.tr('Edit Message Filters...'), 0, 0, self,
+            'webbrowser_manage_message_filters')
+        self.editMessageFilterAct.setStatusTip(self.tr(
+            'Edit the message filters used to suppress unwanted messages'))
+        self.editMessageFilterAct.setWhatsThis(self.tr(
+            """<b>Edit Message Filters</b>"""
+            """<p>Opens a dialog to edit the message filters used to"""
+            """ suppress unwanted messages been shown in an error"""
+            """ window.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.editMessageFilterAct.triggered.connect(
+                E5ErrorMessage.editMessageFilters)
+        self.__actions.append(self.editMessageFilterAct)
+        
+        self.featurePermissionAct = E5Action(
+            self.tr('Edit HTML5 Feature Permissions'),
+            UI.PixmapCache.getIcon("featurePermission.png"),
+            self.tr('Edit HTML5 Feature Permissions...'), 0, 0, self,
+            'webbrowser_edit_feature_permissions')
+        self.featurePermissionAct.setStatusTip(self.tr(
+            'Edit the remembered HTML5 feature permissions'))
+        self.featurePermissionAct.setWhatsThis(self.tr(
+            """<b>Edit HTML5 Feature Permissions</b>"""
+            """<p>Opens a dialog to edit the remembered HTML5"""
+            """ feature permissions.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.featurePermissionAct.triggered.connect(
+                self.__showFeaturePermissionDialog)
+        self.__actions.append(self.featurePermissionAct)
+        
+        # TODO: QtHelp: re-enable once Qt 5.6 is available
+##        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.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)
+        
+        # TODO: AdBlock
+##        self.adblockAct = E5Action(
+##            self.tr('Ad Block'),
+##            UI.PixmapCache.getIcon("adBlockPlus.png"),
+##            self.tr('&Ad Block...'),
+##            0, 0,
+##            self, 'webbrowser_adblock')
+##        self.adblockAct.setStatusTip(self.tr(
+##            'Configure AdBlock subscriptions and rules'))
+##        self.adblockAct.setWhatsThis(self.tr(
+##            """<b>Ad Block...</b>"""
+##            """<p>Opens a dialog to configure AdBlock subscriptions and"""
+##            """ rules.</p>"""
+##        ))
+##        if not self.__initShortcutsOnly:
+##            self.adblockAct.triggered.connect(self.__showAdBlockDialog)
+##        self.__actions.append(self.adblockAct)
+        
+        # TODO: Click2Flash (?)
+##        self.flashblockAct = E5Action(
+##            self.tr('ClickToFlash'),
+##            UI.PixmapCache.getIcon("flashBlock.png"),
+##            self.tr('&ClickToFlash...'),
+##            0, 0,
+##            self, 'webbrowser_flashblock')
+##        self.flashblockAct.setStatusTip(self.tr(
+##            'Configure ClickToFlash whitelist'))
+##        self.flashblockAct.setWhatsThis(self.tr(
+##            """<b>ClickToFlash...</b>"""
+##            """<p>Opens a dialog to configure the ClickToFlash"""
+##            """ whitelist.</p>"""
+##        ))
+##        if not self.__initShortcutsOnly:
+##            self.flashblockAct.triggered.connect(
+##                self.__showClickToFlashDialog)
+##        self.__actions.append(self.flashblockAct)
+        
+        # TODO: Certificates
+##        
+##        from .Network.NetworkManager import SSL_AVAILABLE
+##        if SSL_AVAILABLE:
+##            self.certificatesAct = E5Action(
+##                self.tr('Manage SSL Certificates'),
+##                UI.PixmapCache.getIcon("certificates.png"),
+##                self.tr('Manage SSL Certificates...'),
+##                0, 0,
+##                self, 'webbrowser_manage_certificates')
+##            self.certificatesAct.setStatusTip(self.tr(
+##                'Manage the saved SSL certificates'))
+##            self.certificatesAct.setWhatsThis(self.tr(
+##                """<b>Manage SSL Certificates...</b>"""
+##                """<p>Opens a dialog to manage the saved SSL"""
+##                """ certificates.</p>"""
+##            ))
+##            if not self.__initShortcutsOnly:
+##                self.certificatesAct.triggered.connect(
+##                    self.__showCertificatesDialog)
+##            self.__actions.append(self.certificatesAct)
+        
+        # TODO: Network Monitor (?)
+##        self.toolsMonitorAct = E5Action(
+##            self.tr('Network Monitor'),
+##            self.tr('&Network Monitor...'),
+##            0, 0,
+##            self, 'webbrowser_tools_network_monitor')
+##        self.toolsMonitorAct.setStatusTip(self.tr(
+##            'Show the network monitor dialog'))
+##        self.toolsMonitorAct.setWhatsThis(self.tr(
+##            """<b>Network Monitor...</b>"""
+##            """<p>Shows the network monitor dialog.</p>"""
+##        ))
+##        if not self.__initShortcutsOnly:
+##            self.toolsMonitorAct.triggered.connect(
+##                self.__showNetworkMonitor)
+##        self.__actions.append(self.toolsMonitorAct)
+        
+        self.showDownloadManagerAct = E5Action(
+            self.tr('Downloads'),
+            self.tr('Downloads'),
+            0, 0, self, 'webbrowser_show_downloads')
+        self.showDownloadManagerAct.setStatusTip(self.tr(
+            'Shows the downloads window'))
+        self.showDownloadManagerAct.setWhatsThis(self.tr(
+            """<b>Downloads</b>"""
+            """<p>Shows the downloads window.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.showDownloadManagerAct.triggered.connect(
+                self.__showDownloadsWindow)
+        self.__actions.append(self.showDownloadManagerAct)
+        
+        self.feedsManagerAct = E5Action(
+            self.tr('RSS Feeds Dialog'),
+            UI.PixmapCache.getIcon("rss22.png"),
+            self.tr('&RSS Feeds Dialog...'),
+            QKeySequence(self.tr("Ctrl+Shift+F", "Help|RSS Feeds Dialog")),
+            0, self, 'webbrowser_rss_feeds')
+        self.feedsManagerAct.setStatusTip(self.tr(
+            'Open a dialog showing the configured RSS feeds.'))
+        self.feedsManagerAct.setWhatsThis(self.tr(
+            """<b>RSS Feeds Dialog...</b>"""
+            """<p>Open a dialog to show the configured RSS feeds."""
+            """ It can be used to mange the feeds and to show their"""
+            """ contents.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.feedsManagerAct.triggered.connect(self.__showFeedsManager)
+        self.__actions.append(self.feedsManagerAct)
+        
+        self.siteInfoAct = E5Action(
+            self.tr('Siteinfo Dialog'),
+            UI.PixmapCache.getIcon("helpAbout.png"),
+            self.tr('&Siteinfo Dialog...'),
+            QKeySequence(self.tr("Ctrl+Shift+I", "Help|Siteinfo Dialog")),
+            0, self, 'webbrowser_siteinfo')
+        self.siteInfoAct.setStatusTip(self.tr(
+            'Open a dialog showing some information about the current site.'))
+        self.siteInfoAct.setWhatsThis(self.tr(
+            """<b>Siteinfo Dialog...</b>"""
+            """<p>Opens a dialog showing some information about the current"""
+            """ site.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.siteInfoAct.triggered.connect(self.__showSiteinfoDialog)
+        self.__actions.append(self.siteInfoAct)
+        
+        # TODO: User Agents
+##        self.userAgentManagerAct = E5Action(
+##            self.tr('Manage User Agent Settings'),
+##            self.tr('Manage &User Agent Settings'),
+##            0, 0, self, 'webbrowser_user_agent_settings')
+##        self.userAgentManagerAct.setStatusTip(self.tr(
+##            'Shows a dialog to manage the User Agent settings'))
+##        self.userAgentManagerAct.setWhatsThis(self.tr(
+##            """<b>Manage User Agent Settings</b>"""
+##            """<p>Shows a dialog to manage the User Agent settings.</p>"""
+##        ))
+##        if not self.__initShortcutsOnly:
+##            self.userAgentManagerAct.triggered.connect(
+##                self.__showUserAgentsDialog)
+##        self.__actions.append(self.userAgentManagerAct)
+        
+        self.synchronizationAct = E5Action(
+            self.tr('Synchronize data'),
+            UI.PixmapCache.getIcon("sync.png"),
+            self.tr('&Synchronize Data...'),
+            0, 0, self, 'webbrowser_synchronize_data')
+        self.synchronizationAct.setStatusTip(self.tr(
+            'Shows a dialog to synchronize data via the network'))
+        self.synchronizationAct.setWhatsThis(self.tr(
+            """<b>Synchronize Data...</b>"""
+            """<p>This shows a dialog to synchronize data via the"""
+            """ network.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.synchronizationAct.triggered.connect(
+                self.__showSyncDialog)
+        self.__actions.append(self.synchronizationAct)
+        
+        self.zoomValuesAct = E5Action(
+            self.tr('Manage Saved Zoom Values'),
+            UI.PixmapCache.getIcon("zoomReset.png"),
+            self.tr('Manage Saved Zoom Values...'),
+            0, 0,
+            self, 'webbrowser_manage_zoom_values')
+        self.zoomValuesAct.setStatusTip(self.tr(
+            'Manage the saved zoom values'))
+        self.zoomValuesAct.setWhatsThis(self.tr(
+            """<b>Manage Saved Zoom Values...</b>"""
+            """<p>Opens a dialog to manage the saved zoom values.</p>"""
+        ))
+        if not self.__initShortcutsOnly:
+            self.zoomValuesAct.triggered.connect(self.__showZoomValuesDialog)
+        self.__actions.append(self.zoomValuesAct)
+        
+        self.backAct.setEnabled(False)
+        self.forwardAct.setEnabled(False)
+        
+        # now read the keyboard shortcuts for the actions
+        Shortcuts.readShortcuts(
+            helpViewer=self, helpViewerCategory="webBrowser")
+    
+    def getActions(self):
+        """
+        Public method to get a list of all actions.
+        
+        @return list of all actions (list of E5Action)
+        """
+        return self.__actions[:]
+        
+    def __initMenus(self):
+        """
+        Private method to create the menus.
+        """
+        mb = self.menuBar()
+        
+        menu = mb.addMenu(self.tr('&File'))
+        menu.setTearOffEnabled(True)
+        menu.addAction(self.newTabAct)
+        menu.addAction(self.newAct)
+        menu.addAction(self.newPrivateAct)
+        menu.addAction(self.openAct)
+        menu.addAction(self.openTabAct)
+        menu.addSeparator()
+##        menu.addAction(self.saveAsAct)
+        menu.addAction(self.savePageScreenAct)
+        menu.addAction(self.saveVisiblePageScreenAct)
+        menu.addSeparator()
+        menu.addAction(self.printPreviewAct)
+        menu.addAction(self.printAct)
+        if self.printPdfAct:
+            menu.addAction(self.printPdfAct)
+        menu.addSeparator()
+        menu.addAction(self.closeAct)
+        menu.addAction(self.closeAllAct)
+        menu.addSeparator()
+        menu.addAction(self.exitAct)
+        
+        menu = mb.addMenu(self.tr('&Edit'))
+        menu.setTearOffEnabled(True)
+        menu.addAction(self.undoAct)
+        menu.addAction(self.redoAct)
+        menu.addSeparator()
+        menu.addAction(self.copyAct)
+        menu.addAction(self.cutAct)
+        menu.addAction(self.pasteAct)
+        menu.addSeparator()
+        menu.addAction(self.selectAllAct)
+        menu.addSeparator()
+        menu.addAction(self.findAct)
+        menu.addAction(self.findNextAct)
+        menu.addAction(self.findPrevAct)
+        
+        menu = mb.addMenu(self.tr('&View'))
+        menu.setTearOffEnabled(True)
+        menu.addAction(self.zoomInAct)
+        menu.addAction(self.zoomResetAct)
+        menu.addAction(self.zoomOutAct)
+        menu.addSeparator()
+        menu.addAction(self.pageSourceAct)
+        menu.addAction(self.fullScreenAct)
+        self.__textEncodingMenu = menu.addMenu(
+            self.tr("Text Encoding"))
+        self.__textEncodingMenu.aboutToShow.connect(
+            self.__aboutToShowTextEncodingMenu)
+        self.__textEncodingMenu.triggered.connect(self.__setTextEncoding)
+        
+        menu = mb.addMenu(self.tr('&Go'))
+        menu.setTearOffEnabled(True)
+        menu.addAction(self.backAct)
+        menu.addAction(self.forwardAct)
+        menu.addAction(self.homeAct)
+        menu.addSeparator()
+        menu.addAction(self.stopAct)
+        menu.addAction(self.reloadAct)
+        # TODO: QtHelp
+##        if WebBrowserWindow.UseQtHelp:
+##            menu.addSeparator()
+##            menu.addAction(self.syncTocAct)
+        
+        from .History.HistoryMenu import HistoryMenu
+        self.historyMenu = HistoryMenu(self, self.__tabWidget)
+        self.historyMenu.setTearOffEnabled(True)
+        self.historyMenu.setTitle(self.tr('H&istory'))
+        self.historyMenu.openUrl.connect(self.openUrl)
+        self.historyMenu.newUrl.connect(self.openUrlNewTab)
+        mb.addMenu(self.historyMenu)
+        
+        from .Bookmarks.BookmarksMenu import BookmarksMenuBarMenu
+        self.bookmarksMenu = BookmarksMenuBarMenu(self)
+        self.bookmarksMenu.setTearOffEnabled(True)
+        self.bookmarksMenu.setTitle(self.tr('&Bookmarks'))
+        self.bookmarksMenu.openUrl.connect(self.openUrl)
+        self.bookmarksMenu.newUrl.connect(self.openUrlNewTab)
+        mb.addMenu(self.bookmarksMenu)
+        
+        bookmarksActions = []
+        bookmarksActions.append(self.bookmarksManageAct)
+        bookmarksActions.append(self.bookmarksAddAct)
+        bookmarksActions.append(self.bookmarksAllTabsAct)
+        bookmarksActions.append(self.bookmarksAddFolderAct)
+        bookmarksActions.append("--SEPARATOR--")
+        bookmarksActions.append(self.importBookmarksAct)
+        bookmarksActions.append(self.exportBookmarksAct)
+        self.bookmarksMenu.setInitialActions(bookmarksActions)
+        
+        menu = mb.addMenu(self.tr('&Settings'))
+        menu.setTearOffEnabled(True)
+        menu.addAction(self.prefAct)
+        menu.addAction(self.acceptedLanguagesAct)
+##        menu.addAction(self.cookiesAct)
+        menu.addAction(self.flashCookiesAct)
+##        menu.addAction(self.offlineStorageAct)
+        menu.addAction(self.personalDataAct)
+        menu.addAction(self.greaseMonkeyAct)
+        menu.addAction(self.featurePermissionAct)
+        menu.addSeparator()
+        menu.addAction(self.editMessageFilterAct)
+        menu.addSeparator()
+        menu.addAction(self.searchEnginesAct)
+        menu.addSeparator()
+        menu.addAction(self.passwordsAct)
+        # TODO: Certificates
+##        from .Network.NetworkManager import SSL_AVAILABLE
+##        if SSL_AVAILABLE:
+##            menu.addAction(self.certificatesAct)
+##        menu.addSeparator()
+        menu.addAction(self.zoomValuesAct)
+        menu.addSeparator()
+##        menu.addAction(self.adblockAct)
+##        menu.addAction(self.flashblockAct)
+##        menu.addSeparator()
+        self.__settingsMenu = menu
+        self.__settingsMenu.aboutToShow.connect(
+            self.__aboutToShowSettingsMenu)
+        
+##        from .UserAgent.UserAgentMenu import UserAgentMenu
+##        self.__userAgentMenu = UserAgentMenu(self.tr("Global User Agent"))
+##        menu.addMenu(self.__userAgentMenu)
+##        menu.addAction(self.userAgentManagerAct)
+##        menu.addSeparator()
+        
+        # TODO: QtHelp
+##        if WebBrowserWindow.UseQtHelp:
+##            menu.addAction(self.manageQtHelpDocsAct)
+##            menu.addAction(self.manageQtHelpFiltersAct)
+##            menu.addAction(self.reindexDocumentationAct)
+##            menu.addSeparator()
+        menu.addAction(self.clearPrivateDataAct)
+        menu.addAction(self.clearIconsAct)
+        
+        menu = mb.addMenu(self.tr("&Tools"))
+        menu.setTearOffEnabled(True)
+        menu.addAction(self.feedsManagerAct)
+        menu.addAction(self.siteInfoAct)
+        menu.addSeparator()
+        menu.addAction(self.synchronizationAct)
+        # TODO: Network Monitor
+##        menu.addSeparator()
+##        menu.addAction(self.toolsMonitorAct)
+        
+        menu = mb.addMenu(self.tr("&Window"))
+        menu.setTearOffEnabled(True)
+        menu.addAction(self.showDownloadManagerAct)
+##        if WebBrowserWindow.UseQtHelp:
+##            menu.addSeparator()
+##            menu.addAction(self.showTocAct)
+##            menu.addAction(self.showIndexAct)
+##            menu.addAction(self.showSearchAct)
+        
+        mb.addSeparator()
+        
+        menu = mb.addMenu(self.tr('&Help'))
+        menu.setTearOffEnabled(True)
+        menu.addAction(self.aboutAct)
+        menu.addAction(self.aboutQtAct)
+        menu.addSeparator()
+        menu.addAction(self.whatsThisAct)
+    
+    def __initToolbars(self):
+        """
+        Private method to create the toolbars.
+        """
+        filetb = self.addToolBar(self.tr("File"))
+        filetb.setObjectName("FileToolBar")
+        filetb.setIconSize(UI.Config.ToolBarIconSize)
+        filetb.addAction(self.newTabAct)
+        filetb.addAction(self.newAct)
+        filetb.addAction(self.newPrivateAct)
+        filetb.addAction(self.openAct)
+        filetb.addAction(self.openTabAct)
+        filetb.addSeparator()
+##        filetb.addAction(self.saveAsAct)
+        filetb.addAction(self.savePageScreenAct)
+        filetb.addSeparator()
+        filetb.addAction(self.printPreviewAct)
+        filetb.addAction(self.printAct)
+        if self.printPdfAct:
+            filetb.addAction(self.printPdfAct)
+        filetb.addSeparator()
+        filetb.addAction(self.closeAct)
+        filetb.addAction(self.exitAct)
+        
+        self.savePageScreenMenu = QMenu(self)
+        self.savePageScreenMenu.addAction(self.savePageScreenAct)
+        self.savePageScreenMenu.addAction(self.saveVisiblePageScreenAct)
+        savePageScreenButton = filetb.widgetForAction(self.savePageScreenAct)
+        savePageScreenButton.setMenu(self.savePageScreenMenu)
+        savePageScreenButton.setPopupMode(QToolButton.MenuButtonPopup)
+        
+        edittb = self.addToolBar(self.tr("Edit"))
+        edittb.setObjectName("EditToolBar")
+        edittb.setIconSize(UI.Config.ToolBarIconSize)
+        edittb.addAction(self.undoAct)
+        edittb.addAction(self.redoAct)
+        edittb.addSeparator()
+        edittb.addAction(self.copyAct)
+        edittb.addAction(self.cutAct)
+        edittb.addAction(self.pasteAct)
+        edittb.addSeparator()
+        edittb.addAction(self.selectAllAct)
+        
+        viewtb = self.addToolBar(self.tr("View"))
+        viewtb.setObjectName("ViewToolBar")
+        viewtb.setIconSize(UI.Config.ToolBarIconSize)
+        viewtb.addAction(self.zoomInAct)
+        viewtb.addAction(self.zoomResetAct)
+        viewtb.addAction(self.zoomOutAct)
+        viewtb.addSeparator()
+        viewtb.addAction(self.fullScreenAct)
+        
+        findtb = self.addToolBar(self.tr("Find"))
+        findtb.setObjectName("FindToolBar")
+        findtb.setIconSize(UI.Config.ToolBarIconSize)
+        findtb.addAction(self.findAct)
+        findtb.addAction(self.findNextAct)
+        findtb.addAction(self.findPrevAct)
+        
+        # TODO: QtHelp
+##        if WebBrowserWindow.UseQtHelp:
+##            filtertb = self.addToolBar(self.tr("Filter"))
+##            filtertb.setObjectName("FilterToolBar")
+##            self.filterCombo = QComboBox()
+##            self.filterCombo.setMinimumWidth(
+##                QFontMetrics(QFont()).width("ComboBoxWithEnoughWidth"))
+##            filtertb.addWidget(QLabel(self.tr("Filtered by: ")))
+##            filtertb.addWidget(self.filterCombo)
+##            self.__helpEngine.setupFinished.connect(self.__setupFilterCombo)
+##            self.filterCombo.activated[str].connect(
+##                self.__filterQtHelpDocumentation)
+##            self.__setupFilterCombo()
+        
+        settingstb = self.addToolBar(self.tr("Settings"))
+        settingstb.setObjectName("SettingsToolBar")
+        settingstb.setIconSize(UI.Config.ToolBarIconSize)
+        settingstb.addAction(self.prefAct)
+        settingstb.addAction(self.acceptedLanguagesAct)
+##        settingstb.addAction(self.cookiesAct)
+        settingstb.addAction(self.flashCookiesAct)
+##        settingstb.addAction(self.offlineStorageAct)
+        settingstb.addAction(self.personalDataAct)
+        settingstb.addAction(self.greaseMonkeyAct)
+        settingstb.addAction(self.featurePermissionAct)
+        
+        toolstb = self.addToolBar(self.tr("Tools"))
+        toolstb.setObjectName("ToolsToolBar")
+        toolstb.setIconSize(UI.Config.ToolBarIconSize)
+        toolstb.addAction(self.feedsManagerAct)
+        toolstb.addAction(self.siteInfoAct)
+        toolstb.addSeparator()
+        toolstb.addAction(self.synchronizationAct)
+        
+        helptb = self.addToolBar(self.tr("Help"))
+        helptb.setObjectName("HelpToolBar")
+        helptb.setIconSize(UI.Config.ToolBarIconSize)
+        helptb.addAction(self.whatsThisAct)
+        
+        self.addToolBarBreak()
+        
+        gotb = self.addToolBar(self.tr("Go"))
+        gotb.setObjectName("GoToolBar")
+        gotb.setIconSize(UI.Config.ToolBarIconSize)
+        gotb.addAction(self.backAct)
+        gotb.addAction(self.forwardAct)
+        gotb.addAction(self.reloadAct)
+        gotb.addAction(self.stopAct)
+        gotb.addAction(self.homeAct)
+        gotb.addSeparator()
+        
+        self.__navigationSplitter = QSplitter(gotb)
+        self.__navigationSplitter.addWidget(self.__tabWidget.stackedUrlBar())
+        
+        from .WebBrowserWebSearchWidget import WebBrowserWebSearchWidget
+        self.searchEdit = WebBrowserWebSearchWidget(self)
+        sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
+        sizePolicy.setHorizontalStretch(2)
+        sizePolicy.setVerticalStretch(0)
+        self.searchEdit.setSizePolicy(sizePolicy)
+        self.searchEdit.search.connect(self.__linkActivated)
+        self.__navigationSplitter.addWidget(self.searchEdit)
+        gotb.addWidget(self.__navigationSplitter)
+        
+        self.__navigationSplitter.setSizePolicy(
+            QSizePolicy.Expanding, QSizePolicy.Maximum)
+        self.__navigationSplitter.setCollapsible(0, False)
+        
+        self.backMenu = QMenu(self)
+        self.backMenu.aboutToShow.connect(self.__showBackMenu)
+        self.backMenu.triggered.connect(self.__navigationMenuActionTriggered)
+        backButton = gotb.widgetForAction(self.backAct)
+        backButton.setMenu(self.backMenu)
+        backButton.setPopupMode(QToolButton.MenuButtonPopup)
+        
+        self.forwardMenu = QMenu(self)
+        self.forwardMenu.aboutToShow.connect(self.__showForwardMenu)
+        self.forwardMenu.triggered.connect(
+            self.__navigationMenuActionTriggered)
+        forwardButton = gotb.widgetForAction(self.forwardAct)
+        forwardButton.setMenu(self.forwardMenu)
+        forwardButton.setPopupMode(QToolButton.MenuButtonPopup)
+        
+        from .Bookmarks.BookmarksToolBar import BookmarksToolBar
+        bookmarksModel = self.bookmarksManager().bookmarksModel()
+        self.bookmarksToolBar = BookmarksToolBar(self, bookmarksModel, self)
+        self.bookmarksToolBar.setObjectName("BookmarksToolBar")
+        self.bookmarksToolBar.setIconSize(UI.Config.ToolBarIconSize)
+        self.bookmarksToolBar.openUrl.connect(self.openUrl)
+        self.bookmarksToolBar.newUrl.connect(self.openUrlNewTab)
+        self.addToolBarBreak()
+        self.addToolBar(self.bookmarksToolBar)
+        
+        self.addToolBarBreak()
+        vttb = self.addToolBar(self.tr("VirusTotal"))
+        vttb.setObjectName("VirusTotalToolBar")
+        vttb.setIconSize(UI.Config.ToolBarIconSize)
+        vttb.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
+        self.virustotalScanCurrentAct = vttb.addAction(
+            UI.PixmapCache.getIcon("virustotal.png"),
+            self.tr("Scan current site"),
+            self.__virusTotalScanCurrentSite)
+        self.virustotalIpReportAct = vttb.addAction(
+            UI.PixmapCache.getIcon("virustotal.png"),
+            self.tr("IP Address Report"),
+            self.__virusTotalIpAddressReport)
+        self.virustotalDomainReportAct = vttb.addAction(
+            UI.PixmapCache.getIcon("virustotal.png"),
+            self.tr("Domain Report"),
+            self.__virusTotalDomainReport)
+        if not Preferences.getWebBrowser("VirusTotalEnabled") or \
+           Preferences.getWebBrowser("VirusTotalServiceKey") == "":
+            self.virustotalScanCurrentAct.setEnabled(False)
+            self.virustotalIpReportAct.setEnabled(False)
+            self.virustotalDomainReportAct.setEnabled(False)
+        
+    def __nextTab(self):
+        """
+        Private slot used to show the next tab.
+        """
+        fwidget = QApplication.focusWidget()
+        while fwidget and not hasattr(fwidget, 'nextTab'):
+            fwidget = fwidget.parent()
+        if fwidget:
+            fwidget.nextTab()
+        
+    def __prevTab(self):
+        """
+        Private slot used to show the previous tab.
+        """
+        fwidget = QApplication.focusWidget()
+        while fwidget and not hasattr(fwidget, 'prevTab'):
+            fwidget = fwidget.parent()
+        if fwidget:
+            fwidget.prevTab()
+        
+    def __switchTab(self):
+        """
+        Private slot used to switch between the current and the previous
+        current tab.
+        """
+        fwidget = QApplication.focusWidget()
+        while fwidget and not hasattr(fwidget, 'switchTab'):
+            fwidget = fwidget.parent()
+        if fwidget:
+            fwidget.switchTab()
+        
+    def __whatsThis(self):
+        """
+        Private slot called in to enter Whats This mode.
+        """
+        QWhatsThis.enterWhatsThisMode()
+        
+    def __titleChanged(self, browser, title):
+        """
+        Private slot called to handle a change of a browser's title.
+        
+        @param browser reference to the browser (WebBrowserView)
+        @param title new title (string)
+        """
+        self.historyManager().updateHistoryEntry(
+            browser.url().toString(), title)
+    
+    @pyqtSlot()
+    def newTab(self, link=None, requestData=None, addNextTo=None):
+        """
+        Public slot called to open a new web browser tab.
+        
+        @param link file to be displayed in the new window (string or QUrl)
+        @param requestData page load request data (LoadRequest)
+        @param addNextTo reference to the browser to open the tab after
+            (HelpBrowser)
+        """
+        if addNextTo:
+            self.__tabWidget.newBrowserAfter(addNextTo, link, requestData)
+        else:
+            self.__tabWidget.newBrowser(link, requestData)
+    
+    @pyqtSlot()
+    def newWindow(self, link=None):
+        """
+        Public slot called to open a new web browser window.
+        
+        @param link URL to be displayed in the new window
+        @type str or QUrl
+        """
+        if link is None:
+            linkName = ""
+        elif isinstance(link, QUrl):
+            linkName = link.toString()
+        else:
+            linkName = link
+        h = WebBrowserWindow(linkName, ".", self.parent(), "webbrowser",
+                             self.__fromEric, private=self.isPrivate())
+        h.show()
+    
+    @pyqtSlot()
+    def newPrivateWindow(self, link=None):
+        """
+        Public slot called to open a new private web browser window.
+        
+        @param link URL to be displayed in the new window
+        @type str or QUrl
+        """
+        if link is None:
+            linkName = ""
+        elif isinstance(link, QUrl):
+            linkName = link.toString()
+        else:
+            linkName = link
+        
+        applPath = os.path.join(getConfig("ericDir"), "eric6_browser.py")
+        args = []
+        args.append(applPath)
+        args.append("--config={0}".format(Utilities.getConfigDir()))
+        if self.__settingsDir:
+            args.append("--settings={0}".format(self.__settingsDir))
+        args.append("--private")
+        if linkName:
+            args.append(linkName)
+        
+        if not os.path.isfile(applPath) or \
+                not QProcess.startDetached(sys.executable, args):
+            E5MessageBox.critical(
+                self,
+                self.tr('New Private Window'),
+                self.tr(
+                    '<p>Could not start the process.<br>'
+                    'Ensure that it is available as <b>{0}</b>.</p>'
+                ).format(applPath),
+                self.tr('OK'))
+    
+    def __openFile(self):
+        """
+        Private slot called to open a file.
+        """
+        fn = E5FileDialog.getOpenFileName(
+            self,
+            self.tr("Open File"),
+            "",
+            self.tr("Help Files (*.html *.htm);;"
+                    "PDF Files (*.pdf);;"
+                    "CHM Files (*.chm);;"
+                    "All Files (*)"
+                    ))
+        if fn:
+            if Utilities.isWindowsPlatform():
+                url = "file:///" + Utilities.fromNativeSeparators(fn)
+            else:
+                url = "file://" + fn
+            self.currentBrowser().setSource(QUrl(url))
+        
+    def __openFileNewTab(self):
+        """
+        Private slot called to open a file in a new tab.
+        """
+        fn = E5FileDialog.getOpenFileName(
+            self,
+            self.tr("Open File"),
+            "",
+            self.tr("Help Files (*.html *.htm);;"
+                    "PDF Files (*.pdf);;"
+                    "CHM Files (*.chm);;"
+                    "All Files (*)"
+                    ))
+        if fn:
+            if Utilities.isWindowsPlatform():
+                url = "file:///" + Utilities.fromNativeSeparators(fn)
+            else:
+                url = "file://" + fn
+            self.newTab(url)
+        
+##    def __savePageAs(self):
+##        """
+##        Private slot to save the current page.
+##        """
+##        browser = self.currentBrowser()
+##        if browser is not None:
+##            browser.saveAs()
+##    
+    @pyqtSlot()
+    def __savePageScreen(self, visibleOnly=False):
+        """
+        Private slot to save the current page as a screen shot.
+        
+        @param visibleOnly flag indicating to just save the visible part
+            of the page (boolean)
+        """
+        from .PageScreenDialog import PageScreenDialog
+        self.__pageScreen = PageScreenDialog(
+            self.currentBrowser(), visibleOnly=visibleOnly)
+        self.__pageScreen.show()
+        
+    @pyqtSlot()
+    def __saveVisiblePageScreen(self):
+        """
+        Private slot to save the visible part of the current page as a screen
+        shot.
+        """
+        self.__savePageScreen(visibleOnly=True)
+        
+    def __about(self):
+        """
+        Private slot to show the about information.
+        """
+        chromeVersion, webengineVersion = \
+            WebBrowserTools.getWebEngineVersions()
+        E5MessageBox.about(
+            self,
+            self.tr("eric6 Web Browser"),
+            self.tr(
+                """<b>eric6 Web Browser - {0}</b>"""
+                """<p>The eric6 Web Browser is a combined help file and HTML"""
+                """ browser. It is part of the eric6 development"""
+                """ toolset.</p>"""
+                """<p>It is based on QtWebEngine {1} and Chrome {2}.</p>"""
+            ).format(Version, webengineVersion, chromeVersion))
+        
+    def __aboutQt(self):
+        """
+        Private slot to show info about Qt.
+        """
+        E5MessageBox.aboutQt(self, self.tr("eric6 Web Browser"))
+
+    def setBackwardAvailable(self, b):
+        """
+        Public slot called when backward references are available.
+        
+        @param b flag indicating availability of the backwards action (boolean)
+        """
+        self.backAct.setEnabled(b)
+        
+    def setForwardAvailable(self, b):
+        """
+        Public slot called when forward references are available.
+        
+        @param b flag indicating the availability of the forwards action
+            (boolean)
+        """
+        self.forwardAct.setEnabled(b)
+        
+    def setLoadingActions(self, b):
+        """
+        Public slot to set the loading dependent actions.
+        
+        @param b flag indicating the loading state to consider (boolean)
+        """
+        self.reloadAct.setEnabled(not b)
+        self.stopAct.setEnabled(b)
+        
+    def __addBookmark(self):
+        """
+        Private slot called to add the displayed file to the bookmarks.
+        """
+        view = self.currentBrowser()
+        view.addBookmark()
+        urlStr = bytes(view.url().toEncoded()).decode()
+        title = view.title()
+        
+        script = Scripts.getAllMetaAttributes()
+        view.page().runJavaScript(
+            script,
+            lambda res: self.__addBookmarkCallback(urlStr, title, res))
+    
+    def __addBookmarkCallback(self, url, title, res):
+        """
+        Private callback method of __addBookmark().
+        
+        @param url URL for the bookmark
+        @type str
+        @param title title for the bookmark
+        @type str
+        @param res result of the JavaScript
+        @type list
+        """
+        description = ""
+        for meta in res:
+            if meta["name"] == "description":
+                description = meta["content"]
+        
+        from .Bookmarks.AddBookmarkDialog import AddBookmarkDialog
+        dlg = AddBookmarkDialog()
+        dlg.setUrl(url)
+        dlg.setTitle(title)
+        dlg.setDescription(description)
+        menu = self.bookmarksManager().menu()
+        idx = self.bookmarksManager().bookmarksModel().nodeIndex(menu)
+        dlg.setCurrentIndex(idx)
+        dlg.exec_()
+        
+    def __addBookmarkFolder(self):
+        """
+        Private slot to add a new bookmarks folder.
+        """
+        from .Bookmarks.AddBookmarkDialog import AddBookmarkDialog
+        dlg = AddBookmarkDialog()
+        menu = self.bookmarksManager().menu()
+        idx = self.bookmarksManager().bookmarksModel().nodeIndex(menu)
+        dlg.setCurrentIndex(idx)
+        dlg.setFolder(True)
+        dlg.exec_()
+        
+    def __showBookmarksDialog(self):
+        """
+        Private slot to show the bookmarks dialog.
+        """
+        from .Bookmarks.BookmarksDialog import BookmarksDialog
+        self.__bookmarksDialog = BookmarksDialog(self)
+        self.__bookmarksDialog.openUrl.connect(self.openUrl)
+        self.__bookmarksDialog.newUrl.connect(self.openUrlNewTab)
+        self.__bookmarksDialog.show()
+        
+    def bookmarkAll(self):
+        """
+        Public slot to bookmark all open tabs.
+        """
+        from .Bookmarks.AddBookmarkDialog import AddBookmarkDialog
+        dlg = AddBookmarkDialog()
+        dlg.setFolder(True)
+        dlg.setTitle(self.tr("Saved Tabs"))
+        dlg.exec_()
+        
+        folder = dlg.addedNode()
+        if folder is None:
+            return
+        
+        for view in self.__tabWidget.browsers():
+            urlStr = bytes(view.url().toEncoded()).decode()
+            title = view.title()
+            
+            script = Scripts.getAllMetaAttributes()
+            view.page().runJavaScript(
+                script,
+                lambda res: self.__bookmarkAllCallback(folder, urlStr,
+                                                       title, res))
+    
+    def __bookmarkAllCallback(self, folder, url, title, res):
+        """
+        Private callback method of __addBookmark().
+        
+        @param folder reference to the bookmarks folder
+        @type BookmarkNode
+        @param url URL for the bookmark
+        @type str
+        @param title title for the bookmark
+        @type str
+        @param res result of the JavaScript
+        @type list
+        """
+        description = ""
+        for meta in res:
+            if meta["name"] == "description":
+                description = meta["content"]
+        
+        from .Bookmarks.BookmarkNode import BookmarkNode
+        bookmark = BookmarkNode(BookmarkNode.Bookmark)
+        bookmark.url = url
+        bookmark.title = title
+        bookmark.desc = description
+        
+        self.bookmarksManager().addBookmark(folder, bookmark)
+        
+    def __find(self):
+        """
+        Private slot to handle the find action.
+        
+        It opens the search dialog in order to perform the various
+        search actions and to collect the various search info.
+        """
+        self.__searchWidget.showFind()
+        
+    def __closeAllWindows(self):
+        """
+        Private slot to close all windows.
+        """
+        for browser in WebBrowserWindow.BrowserWindows:
+            if browser != self:
+                browser.close()
+        self.close()
+        
+    def closeEvent(self, e):
+        """
+        Protected event handler for the close event.
+        
+        @param e the close event (QCloseEvent)
+            <br />This event is simply accepted after the history has been
+            saved and all window references have been deleted.
+        """
+        if not self.__shutdownCalled:
+            res = self.shutdown()
+            
+            if res:
+                e.accept()
+                self.webBrowserClosed.emit()
+            else:
+                e.ignore()
+        else:
+            e.accept()
+    
+    def shutdown(self):
+        """
+        Public method to shut down the web browser.
+        
+        @return flag indicating successful shutdown (boolean)
+        """
+        if not self.__tabWidget.shallShutDown():
+            return False
+        
+        if not self.downloadManager().allowQuit():
+            return False
+        
+        self.downloadManager().shutdown()
+        
+        # TODO: Network Monitor
+##        self.__closeNetworkMonitor()
+##        
+        # TODO: Cookies
+##        self.cookieJar().close()
+##        
+        self.bookmarksToolBar.setModel(None)
+        self.bookmarksManager().close()
+        
+        self.historyManager().close()
+        
+        self.passwordManager().close()
+        
+        # TODO: AdBlock
+##        self.adBlockManager().close()
+##        
+        # TODO: UserAgents
+##        self.userAgentsManager().close()
+##        
+        # TODO: SpeedDial
+##        self.speedDial().close()
+        
+        self.syncManager().close()
+        
+        ZoomManager.instance().close()
+        
+        WebIconProvider.instance().close()
+        
+        self.__virusTotal.close()
+        
+        self.flashCookieManager().shutdown()
+        
+        self.searchEdit.openSearchManager().close()
+        
+        # TODO: QtHelp
+##        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
+        
+        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.__isFullScreen():
+            # switch back to normal
+            self.setWindowState(self.windowState() & ~Qt.WindowFullScreen)
+            self.menuBar().show()
+            self.fullScreenAct.setIcon(
+                UI.PixmapCache.getIcon("windowFullscreen.png"))
+            self.fullScreenAct.setIconText(self.tr('Full Screen'))
+        else:
+            # switch to full screen
+            self.setWindowState(self.windowState() | Qt.WindowFullScreen)
+            self.menuBar().hide()
+            self.fullScreenAct.setIcon(
+                UI.PixmapCache.getIcon("windowRestore.png"))
+            self.fullScreenAct.setIconText(self.tr('Restore Window'))
+    
+    def __isFullScreen(self):
+        """
+        Private method to determine, if the window is in full screen mode.
+        
+        @return flag indicating full screen mode (boolean)
+        """
+        return self.windowState() & Qt.WindowFullScreen
+    
+    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.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()
+    
+    # TODO: Cookies
+##    def __showCookiesConfiguration(self):
+##        """
+##        Private slot to configure the cookies handling.
+##        """
+##        from .CookieJar.CookiesConfigurationDialog import \
+##            CookiesConfigurationDialog
+##        dlg = CookiesConfigurationDialog(self)
+##        dlg.exec_()
+    
+    def __showFlashCookiesManagement(self):
+        """
+        Private slot to show the flash cookies management dialog.
+        """
+        self.flashCookieManager().showFlashCookieManagerDialog()
+    
+##    def __showOfflineStorageConfiguration(self):
+##        """
+##        Private slot to configure the offline storage.
+##        """
+##        from .OfflineStorage.OfflineStorageConfigDialog import \
+##            OfflineStorageConfigDialog
+##        dlg = OfflineStorageConfigDialog(self)
+##        if dlg.exec_() == QDialog.Accepted:
+##            dlg.storeData()
+##            self.__initWebEngineSettings()
+    
+    @classmethod
+    def setUseQtHelp(cls, use):
+        """
+        Class method to set the QtHelp usage.
+        
+        @param use flag indicating usage (boolean)
+        """
+        if use:
+            cls.useQtHelp = use and QTHELP_AVAILABLE
+        else:
+            cls.useQtHelp = False
+    
+    # TODO: QtHelp
+##    @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()
+        
+        return cls._networkManager
+    
+    # TODO: Cookies
+##    @classmethod
+##    def cookieJar(cls):
+##        """
+##        Class method to get a reference to the cookie jar.
+##        
+##        @return reference to the cookie jar (CookieJar)
+##        """
+##            from .CookieJar.CookieJar import CookieJar
+##            cls._cookieJar = CookieJar()
+##        return cls.networkManager().cookieJar()
+##        
+    def __clearIconsDatabase(self):
+        """
+        Private slot to clear the icons databse.
+        """
+        WebIconProvider.instance().clear()
+        
+    @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
+        
+    # TODO: QtHelp
+##    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 .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()
+        
+    # TODO: QtHelp
+##    def __syncTOC(self):
+##        """
+##        Private slot to synchronize the TOC with the currently shown page.
+##        """
+##        if WebBrowserWindow.UseQtHelp:
+##            QApplication.setOverrideCursor(Qt.WaitCursor)
+##            url = self.currentBrowser().source()
+##            self.__showTocWindow()
+##            if not self.__tocWindow.syncToContent(url):
+##                self.statusBar().showMessage(
+##                    self.tr("Could not find an associated content."), 5000)
+##            QApplication.restoreOverrideCursor()
+##        
+##    def __showTocWindow(self):
+##        """
+##        Private method to show the table of contents window.
+##        """
+##        if WebBrowserWindow.UseQtHelp:
+##            self.__activateDock(self.__tocWindow)
+##        
+##    def __hideTocWindow(self):
+##        """
+##        Private method to hide the table of contents window.
+##        """
+##        if WebBrowserWindow.UseQtHelp:
+##            self.__tocDock.hide()
+##        
+##    def __showIndexWindow(self):
+##        """
+##        Private method to show the index window.
+##        """
+##        if WebBrowserWindow.UseQtHelp:
+##            self.__activateDock(self.__indexWindow)
+##        
+##    def __hideIndexWindow(self):
+##        """
+##        Private method to hide the index window.
+##        """
+##        if WebBrowserWindow.UseQtHelp:
+##            self.__indexDock.hide()
+##        
+##    def __showSearchWindow(self):
+##        """
+##        Private method to show the search window.
+##        """
+##        if WebBrowserWindow.UseQtHelp:
+##            self.__activateDock(self.__searchWindow)
+##        
+##    def __hideSearchWindow(self):
+##        """
+##        Private method to hide the search window.
+##        """
+##        if WebBrowserWindow.UseQtHelp:
+##            self.__searchDock.hide()
+##        
+##    def __activateDock(self, widget):
+##        """
+##        Private method to activate the dock widget of the given widget.
+##        
+##        @param widget reference to the widget to be activated (QWidget)
+##        """
+##        widget.parent().show()
+##        widget.parent().raise_()
+##        widget.setFocus()
+##        
+##    def __setupFilterCombo(self):
+##        """
+##        Private slot to setup the filter combo box.
+##        """
+##        if WebBrowserWindow.UseQtHelp:
+##            curFilter = self.filterCombo.currentText()
+##            if not curFilter:
+##                curFilter = self.__helpEngine.currentFilter()
+##            self.filterCombo.clear()
+##            self.filterCombo.addItems(self.__helpEngine.customFilters())
+##            idx = self.filterCombo.findText(curFilter)
+##            if idx < 0:
+##                idx = 0
+##            self.filterCombo.setCurrentIndex(idx)
+##        
+##    def __filterQtHelpDocumentation(self, customFilter):
+##        """
+##        Private slot to filter the QtHelp documentation.
+##        
+##        @param customFilter name of filter to be applied (string)
+##        """
+##        if self.__helpEngine:
+##            self.__helpEngine.setCurrentFilter(customFilter)
+##        
+##    def __manageQtHelpDocumentation(self):
+##        """
+##        Private slot to manage the QtHelp documentation database.
+##        """
+##        if WebBrowserWindow.UseQtHelp:
+##            from .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()
+    
+    # TODO: QtHelp
+##    def __manageQtHelpFilters(self):
+##        """
+##        Private slot to manage the QtHelp filters.
+##        """
+##        if WebBrowserWindow.UseQtHelp:
+##            from .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 .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,
+             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)
+            # TODO: Cookies
+##            if cookies:
+##                self.cookieJar().clear()
+            if passwords:
+                self.passwordManager().clear()
+            # TODO: Web Databases
+##            if databases:
+##                if hasattr(QWebDatabase, "removeAllDatabases"):
+##                    QWebDatabase.removeAllDatabases()
+##                else:
+##                    for securityOrigin in QWebSecurityOrigin.allOrigins():
+##                        for database in securityOrigin.databases():
+##                            QWebDatabase.removeDatabase(database)
+            if flashCookies:
+                self.flashCookieManager().removeAllCookies()
+            if zoomValues:
+                ZoomManager.instance().clear()
+        
+    def __showEnginesConfigurationDialog(self):
+        """
+        Private slot to show the search engines configuration dialog.
+        """
+        from .OpenSearch.OpenSearchDialog import OpenSearchDialog
+        
+        dlg = OpenSearchDialog(self)
+        dlg.exec_()
+        
+    def searchEnginesAction(self):
+        """
+        Public method to get a reference to the search engines configuration
+        action.
+        
+        @return reference to the search engines configuration action (QAction)
+        """
+        return self.searchEnginesAct
+        
+    def __showPasswordsDialog(self):
+        """
+        Private slot to show the passwords management dialog.
+        """
+        from .Passwords.PasswordsDialog import PasswordsDialog
+        
+        dlg = PasswordsDialog(self)
+        dlg.exec_()
+    
+    # TODO: Certificates
+##    def __showCertificatesDialog(self):
+##        """
+##        Private slot to show the certificates management dialog.
+##        """
+##        from E5Network.E5SslCertificatesDialog import E5SslCertificatesDialog
+##        
+##        dlg = E5SslCertificatesDialog(self)
+##        dlg.exec_()
+##        
+    # TODO: AdBlock
+##    def __showAdBlockDialog(self):
+##        """
+##        Private slot to show the AdBlock configuration dialog.
+##        """
+##        self.adBlockManager().showDialog()
+##        
+    # TODO: Click2Flash
+##    def __showClickToFlashDialog(self):
+##        """
+##        Private slot to open the ClickToFlash whitelist configuration dialog.
+##        """
+##        from .HelpBrowserWV import HelpWebPage
+##        HelpWebPage.webPluginFactory().plugin("ClickToFlash").configure()
+##        
+    def __showPersonalInformationDialog(self):
+        """
+        Private slot to show the Personal Information configuration dialog.
+        """
+        self.personalInformationManager().showConfigurationDialog()
+        
+    def __showGreaseMonkeyConfigDialog(self):
+        """
+        Private slot to show the GreaseMonkey scripts configuration dialog.
+        """
+        self.greaseMonkeyManager().showConfigurationDialog()
+        
+    def __showFeaturePermissionDialog(self):
+        """
+        Private slot to show the feature permission dialog.
+        """
+        self.featurePermissionManager().showFeaturePermissionsDialog()
+        
+    def __showZoomValuesDialog(self):
+        """
+        Private slot to show the zoom values management dialog.
+        """
+        from .ZoomManager.ZoomValuesDialog import ZoomValuesDialog
+        
+        dlg = ZoomValuesDialog(self)
+        dlg.exec_()
+    
+    # TODO: Network Monitor
+##    def __showNetworkMonitor(self):
+##        """
+##        Private slot to show the network monitor dialog.
+##        """
+##        from E5Network.E5NetworkMonitor import E5NetworkMonitor
+##        monitor = E5NetworkMonitor.instance(self.networkManager())
+##        monitor.show()
+##        
+    def __showDownloadsWindow(self):
+        """
+        Private slot to show the downloads dialog.
+        """
+        self.downloadManager().show()
+        
+    # TODO: Network Monitor
+##    def __closeNetworkMonitor(self):
+##        """
+##        Private slot to close the network monitor dialog.
+##        """
+##        from E5Network.E5NetworkMonitor import E5NetworkMonitor
+##        E5NetworkMonitor.closeMonitor()
+##        
+    def __showPageSource(self):
+        """
+        Private slot to show the source of the current page in an editor.
+        """
+        self.currentBrowser().page().toHtml(self.__showPageSourceCallback)
+        
+    def __showPageSourceCallback(self, src):
+        """
+        Private method to show the source of the current page in an editor.
+        
+        @param src source of the web page
+        @type str
+        """
+        from QScintilla.MiniEditor import MiniEditor
+        editor = MiniEditor(parent=self)
+        editor.setText(src, "Html")
+        editor.setLanguage("dummy.html")
+        editor.show()
+    
+    @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
+    
+    # TODO: AdBlock
+##    @classmethod
+##    def adBlockManager(cls):
+##        """
+##        Class method to get a reference to the AdBlock manager.
+##        
+##        @return reference to the AdBlock manager (AdBlockManager)
+##        """
+##        if cls._adblockManager is None:
+##            from .AdBlock.AdBlockManager import AdBlockManager
+##            cls._adblockManager = AdBlockManager()
+##        
+##        return cls._adblockManager
+##    
+##    def adBlockIcon(self):
+##        """
+##        Public method to get a reference to the AdBlock icon.
+##        
+##        @return reference to the AdBlock icon (AdBlockIcon)
+##        """
+##        return self.__adBlockIcon
+##    
+    @classmethod
+    def downloadManager(cls):
+        """
+        Class method to get a reference to the download manager.
+        
+        @return reference to the download manager (DownloadManager)
+        """
+        if cls._downloadManager is None:
+            from .Download.DownloadManager import DownloadManager
+            cls._downloadManager = DownloadManager()
+        
+        return cls._downloadManager
+        
+    @classmethod
+    def personalInformationManager(cls):
+        """
+        Class method to get a reference to the personal information manager.
+        
+        @return reference to the personal information manager
+            (PersonalInformationManager)
+        """
+        if cls._personalInformationManager is None:
+            from .PersonalInformationManager.PersonalInformationManager \
+                import PersonalInformationManager
+            cls._personalInformationManager = PersonalInformationManager()
+        
+        return cls._personalInformationManager
+        
+    @classmethod
+    def greaseMonkeyManager(cls):
+        """
+        Class method to get a reference to the GreaseMonkey manager.
+        
+        @return reference to the GreaseMonkey manager (GreaseMonkeyManager)
+        """
+        if cls._greaseMonkeyManager is None:
+            from .GreaseMonkey.GreaseMonkeyManager import GreaseMonkeyManager
+            cls._greaseMonkeyManager = GreaseMonkeyManager()
+        
+        return cls._greaseMonkeyManager
+        
+    @classmethod
+    def featurePermissionManager(cls):
+        """
+        Class method to get a reference to the feature permission manager.
+        
+        @return reference to the feature permission manager
+        @rtype FeaturePermissionManager
+        """
+        if cls._featurePermissionManager is None:
+            from .FeaturePermissions.FeaturePermissionManager import \
+                FeaturePermissionManager
+            cls._featurePermissionManager = FeaturePermissionManager()
+        
+        return cls._featurePermissionManager
+        
+    @classmethod
+    def flashCookieManager(cls):
+        """
+        Class method to get a reference to the flash cookies manager.
+        
+        @return reference to the flash cookies manager
+        @rtype FlashCookieManager
+        """
+        if cls._flashCookieManager is None:
+            from .FlashCookieManager.FlashCookieManager import \
+                FlashCookieManager
+            cls._flashCookieManager = FlashCookieManager()
+        
+        return cls._flashCookieManager
+        
+    @classmethod
+    def mainWindow(cls):
+        """
+        Class method to get a reference to the main window.
+        
+        @return reference to the main window (WebBrowserWindow)
+        """
+        if cls.BrowserWindows:
+            return cls.BrowserWindows[0]
+        else:
+            return None
+    
+    @classmethod
+    def mainWindows(cls):
+        """
+        Class method to get references to all main windows.
+        
+        @return references to all main window (list of WebBrowserWindow)
+        """
+        return cls.BrowserWindows
+    
+    def __appFocusChanged(self, old, now):
+        """
+        Private slot to handle a change of the focus.
+        
+        @param old reference to the widget, that lost focus (QWidget or None)
+        @param now reference to the widget having the focus (QWidget or None)
+        """
+        if isinstance(now, WebBrowserWindow):
+            self.__lastActiveWindow = now
+    
+    def getWindow(self):
+        """
+        Public method to get a reference to the most recent active
+        web browser window.
+        
+        @return reference to most recent web browser window
+        @rtype WebBrowserWindow
+        """
+        if self.__lastActiveWindow:
+            return self.__lastActiveWindow
+        
+        return self.mainWindow()
+    
+    def openSearchManager(self):
+        """
+        Public method to get a reference to the opensearch manager object.
+        
+        @return reference to the opensearch manager object (OpenSearchManager)
+        """
+        return self.searchEdit.openSearchManager()
+    
+    def __aboutToShowTextEncodingMenu(self):
+        """
+        Private slot to populate the text encoding menu.
+        """
+        self.__textEncodingMenu.clear()
+        
+        codecs = []
+        for codec in QTextCodec.availableCodecs():
+            codecs.append(str(codec, encoding="utf-8").lower())
+        codecs.sort()
+        
+        defaultTextEncoding = \
+            QWebEngineSettings.globalSettings().defaultTextEncoding().lower()
+        if defaultTextEncoding in codecs:
+            currentCodec = defaultTextEncoding
+        else:
+            currentCodec = ""
+        
+        isDefaultEncodingUsed = True
+        isoMenu = QMenu(self.tr("ISO"), self.__textEncodingMenu)
+        winMenu = QMenu(self.tr("Windows"), self.__textEncodingMenu)
+        isciiMenu = QMenu(self.tr("ISCII"), self.__textEncodingMenu)
+        uniMenu = QMenu(self.tr("Unicode"), self.__textEncodingMenu)
+        otherMenu = QMenu(self.tr("Other"), self.__textEncodingMenu)
+        ibmMenu = QMenu(self.tr("IBM"), self.__textEncodingMenu)
+        
+        for codec in codecs:
+            if codec.startswith(("iso", "latin", "csisolatin")):
+                act = isoMenu.addAction(codec)
+            elif codec.startswith(("windows", "cp1")):
+                act = winMenu.addAction(codec)
+            elif codec.startswith("iscii"):
+                act = isciiMenu.addAction(codec)
+            elif codec.startswith("utf"):
+                act = uniMenu.addAction(codec)
+            elif codec.startswith(("ibm", "csibm", "cp")):
+                act = ibmMenu.addAction(codec)
+            else:
+                act = otherMenu.addAction(codec)
+            
+            act.setData(codec)
+            act.setCheckable(True)
+            if currentCodec == codec:
+                act.setChecked(True)
+                isDefaultEncodingUsed = False
+        
+        act = self.__textEncodingMenu.addAction(
+            self.tr("Default Encoding"))
+        act.setData("")
+        act.setCheckable(True)
+        act.setChecked(isDefaultEncodingUsed)
+        self.__textEncodingMenu.addMenu(uniMenu)
+        self.__textEncodingMenu.addMenu(isoMenu)
+        self.__textEncodingMenu.addMenu(winMenu)
+        self.__textEncodingMenu.addMenu(ibmMenu)
+        self.__textEncodingMenu.addMenu(isciiMenu)
+        self.__textEncodingMenu.addMenu(otherMenu)
+    
+    def __setTextEncoding(self, act):
+        """
+        Private slot to set the selected text encoding as the default for
+        this session.
+        
+        @param act reference to the selected action (QAction)
+        """
+        codec = act.data()
+        if codec == "":
+            QWebEngineSettings.globalSettings().setDefaultTextEncoding("")
+        else:
+            QWebEngineSettings.globalSettings().setDefaultTextEncoding(codec)
+    
+    def eventMouseButtons(self):
+        """
+        Public method to get the last recorded mouse buttons.
+        
+        @return mouse buttons (Qt.MouseButtons)
+        """
+        return self.__eventMouseButtons
+    
+    def eventKeyboardModifiers(self):
+        """
+        Public method to get the last recorded keyboard modifiers.
+        
+        @return keyboard modifiers (Qt.KeyboardModifiers)
+        """
+        return self.__eventKeyboardModifiers
+    
+    def setEventMouseButtons(self, buttons):
+        """
+        Public method to record mouse buttons.
+        
+        @param buttons mouse buttons to record (Qt.MouseButtons)
+        """
+        self.__eventMouseButtons = buttons
+    
+    def setEventKeyboardModifiers(self, modifiers):
+        """
+        Public method to record keyboard modifiers.
+        
+        @param modifiers keyboard modifiers to record (Qt.KeyboardModifiers)
+        """
+        self.__eventKeyboardModifiers = modifiers
+    
+    def mousePressEvent(self, evt):
+        """
+        Protected method called by a mouse press event.
+        
+        @param evt reference to the mouse event (QMouseEvent)
+        """
+        if evt.button() == Qt.XButton1:
+            self.currentBrowser().triggerPageAction(QWebEnginePage.Back)
+        elif evt.button() == Qt.XButton2:
+            self.currentBrowser().triggerPageAction(QWebEnginePage.Forward)
+        else:
+            super(WebBrowserWindow, self).mousePressEvent(evt)
+    
+    @classmethod
+    def feedsManager(cls):
+        """
+        Class method to get a reference to the RSS feeds manager.
+        
+        @return reference to the RSS feeds manager (FeedsManager)
+        """
+        if cls._feedsManager is None:
+            from .Feeds.FeedsManager import FeedsManager
+            cls._feedsManager = FeedsManager()
+        
+        return cls._feedsManager
+    
+    def __showFeedsManager(self):
+        """
+        Private slot to show the feeds manager dialog.
+        """
+        feedsManager = self.feedsManager()
+        feedsManager.openUrl.connect(self.openUrl)
+        feedsManager.newUrl.connect(self.openUrlNewTab)
+        feedsManager.rejected.connect(self.__feedsManagerClosed)
+        feedsManager.show()
+    
+    def __feedsManagerClosed(self):
+        """
+        Private slot to handle closing the feeds manager dialog.
+        """
+        feedsManager = self.sender()
+        feedsManager.openUrl.disconnect(self.openUrl)
+        feedsManager.newUrl.disconnect(self.openUrlNewTab)
+        feedsManager.rejected.disconnect(self.__feedsManagerClosed)
+    
+    def __showSiteinfoDialog(self):
+        """
+        Private slot to show the site info dialog.
+        """
+        from .SiteInfo.SiteInfoDialog import SiteInfoDialog
+        self.__siteinfoDialog = SiteInfoDialog(self.currentBrowser(), self)
+        self.__siteinfoDialog.show()
+
+    # TODO: User Agents
+##    @classmethod
+##    def userAgentsManager(cls):
+##        """
+##        Class method to get a reference to the user agents manager.
+##        
+##        @return reference to the user agents manager (UserAgentManager)
+##        """
+##        if cls._userAgentsManager is None:
+##            from .UserAgent.UserAgentManager import UserAgentManager
+##            cls._userAgentsManager = UserAgentManager()
+##        
+##        return cls._userAgentsManager
+##    
+##    def __showUserAgentsDialog(self):
+##        """
+##        Private slot to show the user agents management dialog.
+##        """
+##        from .UserAgent.UserAgentsDialog import UserAgentsDialog
+##        
+##        dlg = UserAgentsDialog(self)
+##        dlg.exec_()
+##    
+    @classmethod
+    def syncManager(cls):
+        """
+        Class method to get a reference to the data synchronization manager.
+        
+        @return reference to the data synchronization manager (SyncManager)
+        """
+        if cls._syncManager is None:
+            from .Sync.SyncManager import SyncManager
+            cls._syncManager = SyncManager()
+        
+        return cls._syncManager
+    
+    def __showSyncDialog(self):
+        """
+        Private slot to show the synchronization dialog.
+        """
+        self.syncManager().showSyncDialog()
+    
+    # TODO: SpeedDial
+##    @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
+        # TODO: SpeeedDial
+##            
+##            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)
+    
+    ###########################################################################
+    ## 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)
+        """
+        # TODO: AdBlock
+        userStyle = ""
+##        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 Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,184 @@
+# -*- 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:
+            # TODO: Qt 5.6
+##            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 Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,228 @@
+# -*- 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
+        
+        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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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/javascript.qrc	Sun Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -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()
--- a/eric6.e4p	Sun Mar 06 14:09:37 2016 +0100
+++ b/eric6.e4p	Sun Mar 06 14:12:58 2016 +0100
@@ -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,159 @@
     <Source>ViewManager/BookmarkedFilesDialog.py</Source>
     <Source>ViewManager/ViewManager.py</Source>
     <Source>ViewManager/__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/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/__init__.py</Source>
+    <Source>WebBrowser/Network/FollowRedirectReply.py</Source>
+    <Source>WebBrowser/Network/LoadRequest.py</Source>
+    <Source>WebBrowser/Network/NetworkManager.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/SearchWidget.py</Source>
+    <Source>WebBrowser/SiteInfo/SiteInfoDialog.py</Source>
+    <Source>WebBrowser/SiteInfo/__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/WebIconLoader.py</Source>
+    <Source>WebBrowser/Tools/WebIconProvider.py</Source>
+    <Source>WebBrowser/Tools/__init__.py</Source>
+    <Source>WebBrowser/UrlBar/BookmarkActionSelectionDialog.py</Source>
+    <Source>WebBrowser/UrlBar/BookmarkInfoDialog.py</Source>
+    <Source>WebBrowser/UrlBar/FavIconLabel.py</Source>
+    <Source>WebBrowser/UrlBar/StackedUrlBar.py</Source>
+    <Source>WebBrowser/UrlBar/UrlBar.py</Source>
+    <Source>WebBrowser/UrlBar/__init__.py</Source>
+    <Source>WebBrowser/VirusTotal/VirusTotalApi.py</Source>
+    <Source>WebBrowser/VirusTotal/VirusTotalDomainReportDialog.py</Source>
+    <Source>WebBrowser/VirusTotal/VirusTotalIpReportDialog.py</Source>
+    <Source>WebBrowser/VirusTotal/VirusTotalWhoisDialog.py</Source>
+    <Source>WebBrowser/VirusTotal/__init__.py</Source>
+    <Source>WebBrowser/WebBrowserClearPrivateDataDialog.py</Source>
+    <Source>WebBrowser/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/javascript_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 +1752,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 +1810,43 @@
     <Form>VCS/CommandOptionsDialog.ui</Form>
     <Form>VCS/RepositoryInfoDialog.ui</Form>
     <Form>ViewManager/BookmarkedFilesDialog.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/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/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/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/UrlBar/BookmarkActionSelectionDialog.ui</Form>
+    <Form>WebBrowser/UrlBar/BookmarkInfoDialog.ui</Form>
+    <Form>WebBrowser/VirusTotal/VirusTotalDomainReportDialog.ui</Form>
+    <Form>WebBrowser/VirusTotal/VirusTotalIpReportDialog.ui</Form>
+    <Form>WebBrowser/VirusTotal/VirusTotalWhoisDialog.ui</Form>
+    <Form>WebBrowser/WebBrowserClearPrivateDataDialog.ui</Form>
+    <Form>WebBrowser/WebBrowserLanguagesDialog.ui</Form>
+    <Form>WebBrowser/ZoomManager/ZoomValuesDialog.ui</Form>
   </Forms>
   <Translations>
     <Translation>i18n/eric6_cs.qm</Translation>
@@ -1690,6 +1878,9 @@
     <Resource>Helpviewer/data/icons.qrc</Resource>
     <Resource>Helpviewer/data/javascript.qrc</Resource>
     <Resource>IconEditor/cursors/cursors.qrc</Resource>
+    <Resource>WebBrowser/Bookmarks/DefaultBookmarks.qrc</Resource>
+    <Resource>WebBrowser/OpenSearch/DefaultSearchEngines/DefaultSearchEngines.qrc</Resource>
+    <Resource>WebBrowser/data/javascript.qrc</Resource>
   </Resources>
   <Interfaces/>
   <Others>
@@ -1794,19 +1985,40 @@
     <Other>ThirdParty/Pygments/pygments/PKG-INFO</Other>
     <Other>ThirdParty/Send2Trash/LICENSE</Other>
     <Other>ThirdParty/enum/LICENSE</Other>
+    <Other>WebBrowser/Bookmarks/DefaultBookmarks.xbel</Other>
+    <Other>WebBrowser/OpenSearch/DefaultSearchEngines/Amazoncom.xml</Other>
+    <Other>WebBrowser/OpenSearch/DefaultSearchEngines/Bing.xml</Other>
+    <Other>WebBrowser/OpenSearch/DefaultSearchEngines/DeEn_Beolingus.xml</Other>
+    <Other>WebBrowser/OpenSearch/DefaultSearchEngines/DuckDuckGo.xml</Other>
+    <Other>WebBrowser/OpenSearch/DefaultSearchEngines/Facebook.xml</Other>
+    <Other>WebBrowser/OpenSearch/DefaultSearchEngines/Google.xml</Other>
+    <Other>WebBrowser/OpenSearch/DefaultSearchEngines/Google_Im_Feeling_Lucky.xml</Other>
+    <Other>WebBrowser/OpenSearch/DefaultSearchEngines/LEO_DeuEng.xml</Other>
+    <Other>WebBrowser/OpenSearch/DefaultSearchEngines/LinuxMagazin.xml</Other>
+    <Other>WebBrowser/OpenSearch/DefaultSearchEngines/Reddit.xml</Other>
+    <Other>WebBrowser/OpenSearch/DefaultSearchEngines/Wikia.xml</Other>
+    <Other>WebBrowser/OpenSearch/DefaultSearchEngines/Wikia_en.xml</Other>
+    <Other>WebBrowser/OpenSearch/DefaultSearchEngines/Wikipedia.xml</Other>
+    <Other>WebBrowser/OpenSearch/DefaultSearchEngines/Wiktionary.xml</Other>
+    <Other>WebBrowser/OpenSearch/DefaultSearchEngines/Yahoo.xml</Other>
+    <Other>WebBrowser/OpenSearch/DefaultSearchEngines/YouTube.xml</Other>
+    <Other>WebBrowser/data/javascript/jquery-ui.js</Other>
+    <Other>WebBrowser/data/javascript/jquery.js</Other>
+    <Other>WebBrowser/data/javascript/qwebchannel.js</Other>
     <Other>changelog</Other>
     <Other>default.e4k</Other>
     <Other>default_Mac.e4k</Other>
     <Other>eric6.appdata.xml</Other>
     <Other>eric6.desktop</Other>
     <Other>eric6.e4p</Other>
+    <Other>eric6_browser.desktop</Other>
     <Other>eric6_webbrowser.desktop</Other>
     <Other>eric6config.linux</Other>
     <Other>icons</Other>
     <Other>pixmaps</Other>
     <Other>pylint.rc</Other>
   </Others>
-  <MainScript>eric6.py</MainScript>
+  <MainScript>eric6_browser.py</MainScript>
   <Vcs>
     <VcsType>Mercurial</VcsType>
     <VcsOptions>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eric6_browser.desktop	Sun Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:12:58 2016 +0100
@@ -0,0 +1,139 @@
+#!/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
+
+# TODO: adjust this to 5.6.0 when done
+MIN_QT_VERSION = "5.5.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"))
+
+from PyQt5 import QtWebEngineWidgets    # __IGNORE_WARNING__
+
+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
+    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.startswith("--"):
+            argv.remove(arg)
+    
+    try:
+        home = argv[1]
+    except IndexError:
+        home = ""
+    
+    browser = WebBrowserWindow(home, '.', None, 'web_browser',
+                               searchWord=searchWord, private=private,
+                               settingsDir=SettingsDir)
+    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"),
+        ("--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 Mar 06 14:12:58 2016 +0100
@@ -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 Mar 06 14:09:37 2016 +0100
+++ b/eric6_configure.py	Sun Mar 06 14:12:58 2016 +0100
@@ -37,6 +37,17 @@
                           settingsDir)
         sys.argv.remove(arg)
 
+# TODO: adjust this to 5.6.0 when done
+from PyQt5.QtCore import qVersion
+if qVersion() < "5.5.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 +65,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/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 Mar 06 14:09:37 2016 +0100
+++ b/install.py	Sun Mar 06 14:12:58 2016 +0100
@@ -511,7 +511,9 @@
         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 +529,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 +639,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 +816,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 +849,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 Mar 06 14:09:37 2016 +0100
+++ b/uninstall.py	Sun Mar 06 14:12:58 2016 +0100
@@ -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