Continued porting the web browser. QtWebEngine

Sun, 14 Feb 2016 16:47:40 +0100

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Sun, 14 Feb 2016 16:47:40 +0100
branch
QtWebEngine
changeset 4741
f9e1adc69076
parent 4735
84e78ee0f361
child 4742
f9d1090f6ab9

Continued porting the web browser.

- added support for open search engines

Helpviewer/HelpBrowserWV.py file | annotate | diff | comparison | revisions
Preferences/__init__.py file | annotate | diff | comparison | revisions
WebBrowser/Network/LoadRequest.py file | annotate | diff | comparison | revisions
WebBrowser/OpenSearch/DefaultSearchEngines/Amazoncom.xml file | annotate | diff | comparison | revisions
WebBrowser/OpenSearch/DefaultSearchEngines/Bing.xml file | annotate | diff | comparison | revisions
WebBrowser/OpenSearch/DefaultSearchEngines/DeEn_Beolingus.xml file | annotate | diff | comparison | revisions
WebBrowser/OpenSearch/DefaultSearchEngines/DefaultSearchEngines.qrc file | annotate | diff | comparison | revisions
WebBrowser/OpenSearch/DefaultSearchEngines/DefaultSearchEngines_rc.py file | annotate | diff | comparison | revisions
WebBrowser/OpenSearch/DefaultSearchEngines/DuckDuckGo.xml file | annotate | diff | comparison | revisions
WebBrowser/OpenSearch/DefaultSearchEngines/Facebook.xml file | annotate | diff | comparison | revisions
WebBrowser/OpenSearch/DefaultSearchEngines/Google.xml file | annotate | diff | comparison | revisions
WebBrowser/OpenSearch/DefaultSearchEngines/Google_Im_Feeling_Lucky.xml file | annotate | diff | comparison | revisions
WebBrowser/OpenSearch/DefaultSearchEngines/LEO_DeuEng.xml file | annotate | diff | comparison | revisions
WebBrowser/OpenSearch/DefaultSearchEngines/LinuxMagazin.xml file | annotate | diff | comparison | revisions
WebBrowser/OpenSearch/DefaultSearchEngines/Reddit.xml file | annotate | diff | comparison | revisions
WebBrowser/OpenSearch/DefaultSearchEngines/Wikia.xml file | annotate | diff | comparison | revisions
WebBrowser/OpenSearch/DefaultSearchEngines/Wikia_en.xml file | annotate | diff | comparison | revisions
WebBrowser/OpenSearch/DefaultSearchEngines/Wikipedia.xml file | annotate | diff | comparison | revisions
WebBrowser/OpenSearch/DefaultSearchEngines/Wiktionary.xml file | annotate | diff | comparison | revisions
WebBrowser/OpenSearch/DefaultSearchEngines/Yahoo.xml file | annotate | diff | comparison | revisions
WebBrowser/OpenSearch/DefaultSearchEngines/YouTube.xml file | annotate | diff | comparison | revisions
WebBrowser/OpenSearch/DefaultSearchEngines/__init__.py file | annotate | diff | comparison | revisions
WebBrowser/OpenSearch/OpenSearchDialog.py file | annotate | diff | comparison | revisions
WebBrowser/OpenSearch/OpenSearchDialog.ui file | annotate | diff | comparison | revisions
WebBrowser/OpenSearch/OpenSearchEditDialog.py file | annotate | diff | comparison | revisions
WebBrowser/OpenSearch/OpenSearchEditDialog.ui file | annotate | diff | comparison | revisions
WebBrowser/OpenSearch/OpenSearchEngine.py file | annotate | diff | comparison | revisions
WebBrowser/OpenSearch/OpenSearchEngineAction.py file | annotate | diff | comparison | revisions
WebBrowser/OpenSearch/OpenSearchEngineModel.py file | annotate | diff | comparison | revisions
WebBrowser/OpenSearch/OpenSearchManager.py file | annotate | diff | comparison | revisions
WebBrowser/OpenSearch/OpenSearchReader.py file | annotate | diff | comparison | revisions
WebBrowser/OpenSearch/OpenSearchWriter.py file | annotate | diff | comparison | revisions
WebBrowser/OpenSearch/__init__.py file | annotate | diff | comparison | revisions
WebBrowser/Tools/Scripts.py file | annotate | diff | comparison | revisions
WebBrowser/Tools/WebBrowserTools.py file | annotate | diff | comparison | revisions
WebBrowser/Tools/WebIconProvider.py file | annotate | diff | comparison | revisions
WebBrowser/UrlBar/UrlBar.py file | annotate | diff | comparison | revisions
WebBrowser/WebBrowserClearPrivateDataDialog.py file | annotate | diff | comparison | revisions
WebBrowser/WebBrowserClearPrivateDataDialog.ui file | annotate | diff | comparison | revisions
WebBrowser/WebBrowserPage.py file | annotate | diff | comparison | revisions
WebBrowser/WebBrowserTabWidget.py file | annotate | diff | comparison | revisions
WebBrowser/WebBrowserView.py file | annotate | diff | comparison | revisions
WebBrowser/WebBrowserWebSearchWidget.py file | annotate | diff | comparison | revisions
WebBrowser/WebBrowserWindow.py file | annotate | diff | comparison | revisions
eric6.e4p file | annotate | diff | comparison | revisions
--- a/Helpviewer/HelpBrowserWV.py	Sat Feb 13 14:17:39 2016 +0100
+++ b/Helpviewer/HelpBrowserWV.py	Sun Feb 14 16:47:40 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/Preferences/__init__.py	Sat Feb 13 14:17:39 2016 +0100
+++ b/Preferences/__init__.py	Sun Feb 14 16:47:40 2016 +0100
@@ -1017,6 +1017,11 @@
         "UserStyleSheet": "",
         "ZoomValuesDB": "{}",       # empty JSON dictionary
         "HistoryLimit": 30,
+        "WebSearchSuggestions": True,
+        "WebSearchEngine": "DuckDuckGo",
+        "WebSearchKeywords": [],    # array of two tuples (keyword,
+                                    # search engine name)
+        "SearchLanguage": QLocale().language(),
     }
     
     @classmethod
@@ -2653,17 +2658,17 @@
             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("Help/" + 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 ["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 = []
@@ -2742,7 +2747,7 @@
                  "LocalContentCanAccessRemoteUrls",
                  "LocalContentCanAccessFileUrls", "XSSAuditingEnabled",
                  "ScrollAnimatorEnabled", "ErrorPageEnabled",
-                 "WarnOnMultipleClose", 
+                 "WarnOnMultipleClose", "WebSearchSuggestions",
                  ]:
         return toBool(prefClass.settings.value(
             "WebBrowser/" + key, prefClass.webBrowserDefaults[key]))
@@ -2768,19 +2773,19 @@
     """
     if key in ["StandardFont", "FixedFont"]:
         prefClass.settings.setValue("WebBrowser/" + key, value.toString())
-##    elif key == "SaveUrlColor":
-##        prefClass.settings.setValue("Help/" + key, value.name())
-##    elif key == "WebSearchKeywords":
-##        # value is list of tuples of (keyword, engine name)
-##        prefClass.settings.remove("Help/" + key)
-##        prefClass.settings.beginWriteArray("Help/" + 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 == "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)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/Network/LoadRequest.py	Sun Feb 14 16:47:40 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/OpenSearch/DefaultSearchEngines/Amazoncom.xml	Sun Feb 14 16:47:40 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 Feb 14 16:47:40 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 Feb 14 16:47:40 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 Feb 14 16:47:40 2016 +0100
@@ -0,0 +1,21 @@
+<!DOCTYPE RCC>
+<RCC version="1.0">
+<qresource>
+  <file>YouTube.xml</file>
+  <file>Amazoncom.xml</file>
+  <file>Bing.xml</file>
+  <file>DeEn_Beolingus.xml</file>
+  <file>DuckDuckGo.xml</file>
+  <file>Facebook.xml</file>
+  <file>Google_Im_Feeling_Lucky.xml</file>
+  <file>Google.xml</file>
+  <file>LEO_DeuEng.xml</file>
+  <file>LinuxMagazin.xml</file>
+  <file>Reddit.xml</file>
+  <file>Wikia_en.xml</file>
+  <file>Wikia.xml</file>
+  <file>Wikipedia.xml</file>
+  <file>Wiktionary.xml</file>
+  <file>Yahoo.xml</file>
+</qresource>
+</RCC>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/OpenSearch/DefaultSearchEngines/DefaultSearchEngines_rc.py	Sun Feb 14 16:47:40 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\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\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\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\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\
+\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\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\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\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\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\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\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\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\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\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\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\
+"
+
+qt_resource_name = b"\
+\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\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\x09\
+\x01\xf4\xe3\x3c\
+\x00\x57\
+\x00\x69\x00\x6b\x00\x69\x00\x61\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\
+\x00\x08\
+\x00\x4a\x56\x1c\
+\x00\x42\
+\x00\x69\x00\x6e\x00\x67\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\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\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\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\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\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\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\x09\
+\x0f\x62\xe1\xdc\
+\x00\x59\
+\x00\x61\x00\x68\x00\x6f\x00\x6f\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\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\
+"
+
+qt_resource_struct = b"\
+\x00\x00\x00\x00\x00\x02\x00\x00\x00\x10\x00\x00\x00\x01\
+\x00\x00\x00\x84\x00\x00\x00\x00\x00\x01\x00\x00\x08\x78\
+\x00\x00\x01\x32\x00\x00\x00\x00\x00\x01\x00\x00\x11\xc2\
+\x00\x00\x00\x4c\x00\x00\x00\x00\x00\x01\x00\x00\x03\xc1\
+\x00\x00\x00\x64\x00\x00\x00\x00\x00\x01\x00\x00\x06\x19\
+\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
+\x00\x00\x01\xc8\x00\x00\x00\x00\x00\x01\x00\x00\x1c\x0b\
+\x00\x00\x01\x54\x00\x00\x00\x00\x00\x01\x00\x00\x14\x40\
+\x00\x00\x01\x12\x00\x00\x00\x00\x00\x01\x00\x00\x10\x29\
+\x00\x00\x00\x22\x00\x00\x00\x00\x00\x01\x00\x00\x01\x9f\
+\x00\x00\x01\xea\x00\x00\x00\x00\x00\x01\x00\x00\x23\x0d\
+\x00\x00\x01\xac\x00\x00\x00\x00\x00\x01\x00\x00\x1a\x82\
+\x00\x00\x00\x9a\x00\x00\x00\x00\x00\x01\x00\x00\x09\xf5\
+\x00\x00\x01\x7a\x00\x00\x00\x00\x00\x01\x00\x00\x16\x0d\
+\x00\x00\x00\xd6\x00\x00\x00\x00\x00\x01\x00\x00\x0c\x5d\
+\x00\x00\x01\x94\x00\x00\x00\x00\x00\x01\x00\x00\x18\x57\
+\x00\x00\x00\xf4\x00\x00\x00\x00\x00\x01\x00\x00\x0e\xa7\
+"
+
+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 Feb 14 16:47:40 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 Feb 14 16:47:40 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 Feb 14 16:47:40 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 Feb 14 16:47:40 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 Feb 14 16:47:40 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 Feb 14 16:47:40 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 Feb 14 16:47:40 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 Feb 14 16:47:40 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 Feb 14 16:47:40 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 Feb 14 16:47:40 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 Feb 14 16:47:40 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 Feb 14 16:47:40 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 Feb 14 16:47:40 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 Feb 14 16:47:40 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 Feb 14 16:47:40 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 Feb 14 16:47:40 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 Feb 14 16:47:40 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 Feb 14 16:47:40 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 Feb 14 16:47:40 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 Feb 14 16:47:40 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 Feb 14 16:47:40 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 Feb 14 16:47:40 2016 +0100
@@ -0,0 +1,599 @@
+# -*- 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
+    
+    # TODO: Open Search: implement this right
+    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.setHelp("WebSearchEngine", self.__current)
+        keywords = []
+        for k in self.__keywords:
+            if self.__keywords[k]:
+                keywords.append((k, self.__keywords[k].name()))
+        Preferences.setHelp("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 Feb 14 16:47:40 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 Feb 14 16:47:40 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 Feb 14 16:47:40 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.
+"""
--- a/WebBrowser/Tools/Scripts.py	Sat Feb 13 14:17:39 2016 +0100
+++ b/WebBrowser/Tools/Scripts.py	Sun Feb 14 16:47:40 2016 +0100
@@ -14,6 +14,8 @@
 
 from __future__ import unicode_literals
 
+from PyQt5.QtCore import QUrlQuery, QUrl
+
 from .WebBrowserTools import readAllFileContents
 
 
@@ -176,5 +178,68 @@
                 });
             }
             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)
--- a/WebBrowser/Tools/WebBrowserTools.py	Sat Feb 13 14:17:39 2016 +0100
+++ b/WebBrowser/Tools/WebBrowserTools.py	Sun Feb 14 16:47:40 2016 +0100
@@ -44,3 +44,19 @@
         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
--- a/WebBrowser/Tools/WebIconProvider.py	Sat Feb 13 14:17:39 2016 +0100
+++ b/WebBrowser/Tools/WebIconProvider.py	Sun Feb 14 16:47:40 2016 +0100
@@ -185,8 +185,19 @@
         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
--- a/WebBrowser/UrlBar/UrlBar.py	Sat Feb 13 14:17:39 2016 +0100
+++ b/WebBrowser/UrlBar/UrlBar.py	Sun Feb 14 16:47:40 2016 +0100
@@ -304,13 +304,13 @@
             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")
+##                    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:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/WebBrowserClearPrivateDataDialog.py	Sun Feb 14 16:47:40 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 Feb 14 16:47:40 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>
--- a/WebBrowser/WebBrowserPage.py	Sat Feb 13 14:17:39 2016 +0100
+++ b/WebBrowser/WebBrowserPage.py	Sun Feb 14 16:47:40 2016 +0100
@@ -35,6 +35,7 @@
 from .JavaScript.ExternalJsObject import ExternalJsObject
 
 from .Tools.WebHitTestResult import WebHitTestResult
+from .Tools import Scripts
 
 import Preferences
 import UI.PixmapCache
@@ -434,7 +435,7 @@
     
     def __loadStarted(self):
         """
-        Private method to handle the loadStarted signal.
+        Private slot to handle the loadStarted signal.
         """
         self.__adBlockedEntries = []
 ##    
--- a/WebBrowser/WebBrowserTabWidget.py	Sat Feb 13 14:17:39 2016 +0100
+++ b/WebBrowser/WebBrowserTabWidget.py	Sun Feb 14 16:47:40 2016 +0100
@@ -297,14 +297,12 @@
         """
         self.newBrowser()
     
-    # TODO: remove requestData from signature
     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 tuple containing the request data (QNetworkRequest,
-            QNetworkAccessManager.Operation, QByteArray)
+        @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)
         """
@@ -317,18 +315,18 @@
         
         from .UrlBar.UrlBar import UrlBar
         urlbar = UrlBar(self.__mainWindow, self)
-##        if self.__historyCompleter is None:
-##            import Helpviewer.HelpWindow
-##            from .History.HistoryCompleter import HistoryCompletionModel, \
-##                HistoryCompleter
-##            self.__historyCompletionModel = HistoryCompletionModel(self)
-##            self.__historyCompletionModel.setSourceModel(
-##                Helpviewer.HelpWindow.HelpWindow.historyManager()
-##                .historyFilterModel())
-##            self.__historyCompleter = HistoryCompleter(
-##                self.__historyCompletionModel, self)
-##            self.__historyCompleter.activated[str].connect(self.__pathSelected)
-##        urlbar.setCompleter(self.__historyCompleter)
+        if self.__historyCompleter is None:
+            import Helpviewer.HelpWindow
+            from .History.HistoryCompleter import HistoryCompletionModel, \
+                HistoryCompleter
+            self.__historyCompletionModel = HistoryCompletionModel(self)
+            self.__historyCompletionModel.setSourceModel(
+                Helpviewer.HelpWindow.HelpWindow.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)
@@ -364,8 +362,7 @@
         self.__closeButton.setEnabled(True)
         self.__navigationButton.setEnabled(True)
         
-##        if not linkName and not requestData:
-        if not linkName:
+        if not linkName and not requestData:
             if Preferences.getWebBrowser("StartupBehavior") == 0:
                 linkName = Preferences.getWebBrowser("HomePage")
             elif Preferences.getWebBrowser("StartupBehavior") == 1:
@@ -381,18 +378,16 @@
                     index,
                     self.__elide(browser.documentTitle().replace("&", "&&")))
                 self.setTabToolTip(index, browser.documentTitle())
-##        elif requestData:
-##            browser.load(*requestData)
+        elif requestData:
+            browser.load(requestData)
     
-    # TODO: remove requestData from signature
     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 tuple containing the request data (QNetworkRequest,
-            QNetworkAccessManager.Operation, QByteArray)
+        @param requestData page load request data (LoadRequest)
         """
         if browser:
             position = self.indexOf(browser) + 1
@@ -893,14 +888,14 @@
 ##                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 __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):
         """
@@ -909,12 +904,11 @@
         @param path path string to guess an URL for (string)
         @return guessed URL (QUrl)
         """
-        # TODO: Open Search
-##        manager = self.__mainWindow.openSearchManager()
-##        path = Utilities.fromNativeSeparators(path)
-##        url = manager.convertKeywordSearchToUrl(path)
-##        if url.isValid():
-##            return url
+        manager = self.__mainWindow.openSearchManager()
+        path = Utilities.fromNativeSeparators(path)
+        url = manager.convertKeywordSearchToUrl(path)
+        if url.isValid():
+            return url
         
         try:
             url = QUrl.fromUserInput(path)
@@ -927,8 +921,8 @@
         
         # 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() in ["s", "search"]:
+            url = manager.currentEngine().searchUrl(url.path().strip())
         
         if url.scheme() != "" and \
            (url.host() != "" or url.path() != ""):
--- a/WebBrowser/WebBrowserView.py	Sat Feb 13 14:17:39 2016 +0100
+++ b/WebBrowser/WebBrowserView.py	Sun Feb 14 16:47:40 2016 +0100
@@ -22,7 +22,7 @@
 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
+from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest, QHostInfo
 from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEnginePage
 
 from E5Gui import E5MessageBox, E5FileDialog
@@ -31,6 +31,9 @@
 from .WebBrowserPage import WebBrowserPage
 
 from .Tools.WebIconLoader import WebIconLoader
+from .Tools import WebBrowserTools, Scripts
+
+from .Network.LoadRequest import LoadRequest, LoadRequestOperations
 
 import Preferences
 import UI.PixmapCache
@@ -128,9 +131,8 @@
         
 ##        self.page().databaseQuotaExceeded.connect(self.__databaseQuotaExceeded)
         
-        # TODO: Open Search
-##        self.__mw.openSearchManager().currentEngineChanged.connect(
-##            self.__currentEngineChanged)
+        self.__mw.openSearchManager().currentEngineChanged.connect(
+            self.__currentEngineChanged)
         
         self.setAcceptDrops(True)
         
@@ -190,48 +192,12 @@
 ##                self.__addExternalBinding)
 ##        frame.addToJavaScriptWindowObject("external", self.__javaScriptBinding)
 ##    
-##    def linkedResources(self, relation=""):
-##        """
-##        Public method to extract linked resources.
-##        
-##        @param relation relation to extract (string)
-##        @return list of linked resources (list of LinkedResource)
-##        """
-##        resources = []
-##        
-##        baseUrl = self.page().mainFrame().baseUrl()
-##        
-##        linkElements = self.page().mainFrame().findAllElements(
-##            "html > head > link")
-##        
-##        for linkElement in linkElements.toList():
-##            rel = linkElement.attribute("rel")
-##            href = linkElement.attribute("href")
-##            type_ = linkElement.attribute("type")
-##            title = linkElement.attribute("title")
-##            
-##            if href == "" or type_ == "":
-##                continue
-##            if relation and rel != relation:
-##                continue
-##            
-##            resource = LinkedResource()
-##            resource.rel = rel
-##            resource.type_ = type_
-##            resource.href = baseUrl.resolved(
-##                QUrl.fromEncoded(href.encode("utf-8")))
-##            resource.title = title
-##            
-##            resources.append(resource)
-##        
-##        return resources
-##    
-##    def __currentEngineChanged(self):
-##        """
-##        Private slot to track a change of the current search engine.
-##        """
-##        if self.url().toString() == "eric:home":
-##            self.reload()
+    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):
         """
@@ -242,6 +208,61 @@
         """
         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)
+        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):
         """
@@ -786,21 +807,20 @@
             UI.PixmapCache.getIcon("mailSend.png"),
             self.tr("Send Text"),
             self.__sendLink).setData(self.selectedText())
-        # TODO: OpenSearch
         # 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()
+        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()
         
         # TODO: Languages Dialog
 ##        from .HelpLanguagesDialog import HelpLanguagesDialog
@@ -1074,11 +1094,10 @@
         """
         from .Tools import Scripts
         script = Scripts.getFormData(self.__clickedPos)
-        # TODO: OpenSearch: add ew method
-##        self.page().runJavaScript(
-##            script,
-##            lambda res: self.__mw.openSearchManager().addEngineFromForm(
-##                res, self))
+        self.page().runJavaScript(
+            script,
+            lambda res: self.__mw.openSearchManager().addEngineFromForm(
+                res, self))
     
     # TODO: WebInspector
 ##    def __webInspector(self):
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebBrowser/WebBrowserWebSearchWidget.py	Sun Feb 14 16:47:40 2016 +0100
@@ -0,0 +1,404 @@
+# -*- 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\
+                .mainWindow().getWindow().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)
--- a/WebBrowser/WebBrowserWindow.py	Sat Feb 13 14:17:39 2016 +0100
+++ b/WebBrowser/WebBrowserWindow.py	Sun Feb 14 16:47:40 2016 +0100
@@ -1492,57 +1492,55 @@
 ##                    self.__searchEngine.reindexDocumentation)
 ##            self.__actions.append(self.reindexDocumentationAct)
         
-        # TODO: Clear Private Data
-##        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.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)
         
-        # TODO: Open Search
-##        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.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)
         
         # TODO: Passwords
 ##        self.passwordsAct = E5Action(
@@ -1863,8 +1861,8 @@
 ##        menu.addAction(self.featurePermissionAct)
 ##        menu.addSeparator()
         menu.addAction(self.editMessageFilterAct)
-##        menu.addSeparator()
-##        menu.addAction(self.searchEnginesAct)
+        menu.addSeparator()
+        menu.addAction(self.searchEnginesAct)
 ##        menu.addSeparator()
 ##        menu.addAction(self.passwordsAct)
 ##        if SSL_AVAILABLE:
@@ -1890,8 +1888,8 @@
 ##            menu.addAction(self.manageQtHelpFiltersAct)
 ##            menu.addAction(self.reindexDocumentationAct)
 ##            menu.addSeparator()
-##        menu.addAction(self.clearPrivateDataAct)
-##        menu.addAction(self.clearIconsAct)
+        menu.addAction(self.clearPrivateDataAct)
+        menu.addAction(self.clearIconsAct)
         
 ##        menu = mb.addMenu(self.tr("&Tools"))
 ##        menu.setTearOffEnabled(True)
@@ -2030,14 +2028,14 @@
         self.__navigationSplitter = QSplitter(gotb)
         self.__navigationSplitter.addWidget(self.__tabWidget.stackedUrlBar())
         
-##        from .HelpWebSearchWidget import HelpWebSearchWidget
-##        self.searchEdit = HelpWebSearchWidget(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)
+        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(
@@ -2136,9 +2134,8 @@
         @param browser reference to the browser (WebBrowserView)
         @param title new title (string)
         """
-        pass
-##        self.historyManager().updateHistoryEntry(
-##            browser.url().toString(), title)
+        self.historyManager().updateHistoryEntry(
+            browser.url().toString(), title)
     
     @pyqtSlot()
     def newTab(self, link=None, requestData=None, addNextTo=None):
@@ -2146,8 +2143,7 @@
         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 tuple containing the request data (QNetworkRequest,
-            QNetworkAccessManager.Operation, QByteArray)
+        @param requestData page load request data (LoadRequest)
         @param addNextTo reference to the browser to open the tab after
             (HelpBrowser)
         """
@@ -2155,7 +2151,6 @@
             self.__tabWidget.newBrowserAfter(addNextTo, link, requestData)
         else:
             self.__tabWidget.newBrowser(link, requestData)
-        # TODO: check the above
     
     @pyqtSlot()
     def newWindow(self, link=None):
@@ -2176,7 +2171,7 @@
     
     # TODO: Private Window
     
-    # TODO: check if this is still needed
+    # TODO: check if this is still needed/possible
     def previewer(self):
         """
         Public method to get a reference to the previewer tab.
@@ -2505,8 +2500,8 @@
 ##        
 ##        self.flashCookieManager().shutdown()
 ##        
-##        self.searchEdit.openSearchManager().close()
-##        
+        self.searchEdit.openSearchManager().close()
+        
 ##        if WebBrowserWindow.UseQtHelp:
 ##            self.__searchEngine.cancelIndexing()
 ##            self.__searchEngine.cancelSearching()
@@ -2514,8 +2509,8 @@
 ##            if self.__helpInstaller:
 ##                self.__helpInstaller.stop()
 ##        
-##        self.searchEdit.saveSearches()
-##        
+        self.searchEdit.saveSearches()
+        
         self.__tabWidget.closeAllBrowsers()
         
         state = self.saveState()
@@ -2806,9 +2801,8 @@
         
         self.__tabWidget.preferencesChanged()
         
-        # TODO: OpenSearch
-##        self.searchEdit.preferencesChanged()
-##        
+        self.searchEdit.preferencesChanged()
+        
         # TODO: VirusTotal
 ##        self.__virusTotal.preferencesChanged()
 ##        if not Preferences.getWebBrowser("VirusTotalEnabled") or \
@@ -2923,12 +2917,12 @@
 ##            cls._cookieJar = CookieJar()
 ##        return cls.networkAccessManager().cookieJar()
 ##        
-##    def __clearIconsDatabase(self):
-##        """
-##        Private slot to clear the icons databse.
-##        """
-##        QWebSettings.clearIconDatabase()
-##        
+    def __clearIconsDatabase(self):
+        """
+        Private slot to clear the icons databse.
+        """
+        WebIconProvider.instance().clear()
+        
     @pyqtSlot(QUrl)
     def __linkActivated(self, url):
         """
@@ -3285,37 +3279,43 @@
             # go forward
             history.goToItem(history.forwardItems(historyCount)[offset - 1])
         
-##    def __clearPrivateData(self):
-##        """
-##        Private slot to clear the private data.
-##        """
-##        from .HelpClearPrivateDataDialog import HelpClearPrivateDataDialog
-##        dlg = HelpClearPrivateDataDialog(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()
-##            if searches:
-##                self.searchEdit.clear()
+    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()
+            if searches:
+                self.searchEdit.clear()
+            # TODO: Downloads
 ##            if downloads:
 ##                self.downloadManager().cleanup()
 ##                self.downloadManager().hide()
-##            if favicons:
-##                self.__clearIconsDatabase()
+            if favicons:
+                self.__clearIconsDatabase()
+            # TODO: Cache Cleaning
 ##            if cache:
 ##                try:
 ##                    self.networkAccessManager().cache().clear()
 ##                except AttributeError:
 ##                    pass
+            # TODO: Cookies
 ##            if cookies:
 ##                self.cookieJar().clear()
+            # TODO: Passwords
 ##            if passwords:
 ##                self.passwordManager().clear()
+            # TODO: Web Databases
 ##            if databases:
 ##                if hasattr(QWebDatabase, "removeAllDatabases"):
 ##                    QWebDatabase.removeAllDatabases()
@@ -3323,6 +3323,7 @@
 ##                    for securityOrigin in QWebSecurityOrigin.allOrigins():
 ##                        for database in securityOrigin.databases():
 ##                            QWebDatabase.removeDatabase(database)
+            # TODO: Flash Cookie Manager
 ##            if flashCookies:
 ##                from .HelpLanguagesDialog import HelpLanguagesDialog
 ##                languages = Preferences.toList(
@@ -3336,27 +3337,27 @@
 ##                    "http://www.macromedia.com/support/documentation/"
 ##                    "{0}/flashplayer/help/settings_manager07.html".format(
 ##                        langCode))
-##            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
-##        
+            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.
@@ -3661,14 +3662,14 @@
         
         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 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.
--- a/eric6.e4p	Sat Feb 13 14:17:39 2016 +0100
+++ b/eric6.e4p	Sun Feb 14 16:47:40 2016 +0100
@@ -1301,8 +1301,20 @@
     <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/__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/SearchWidget.py</Source>
     <Source>WebBrowser/Tools/Scripts.py</Source>
     <Source>WebBrowser/Tools/WebBrowserTools.py</Source>
@@ -1316,10 +1328,12 @@
     <Source>WebBrowser/UrlBar/StackedUrlBar.py</Source>
     <Source>WebBrowser/UrlBar/UrlBar.py</Source>
     <Source>WebBrowser/UrlBar/__init__.py</Source>
+    <Source>WebBrowser/WebBrowserClearPrivateDataDialog.py</Source>
     <Source>WebBrowser/WebBrowserPage.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/ZoomManager/ZoomManager.py</Source>
     <Source>WebBrowser/ZoomManager/ZoomValuesDialog.py</Source>
@@ -1729,9 +1743,12 @@
     <Form>WebBrowser/Bookmarks/BookmarksDialog.ui</Form>
     <Form>WebBrowser/Bookmarks/BookmarksImportDialog.ui</Form>
     <Form>WebBrowser/History/HistoryDialog.ui</Form>
+    <Form>WebBrowser/OpenSearch/OpenSearchDialog.ui</Form>
+    <Form>WebBrowser/OpenSearch/OpenSearchEditDialog.ui</Form>
     <Form>WebBrowser/SearchWidget.ui</Form>
     <Form>WebBrowser/UrlBar/BookmarkActionSelectionDialog.ui</Form>
     <Form>WebBrowser/UrlBar/BookmarkInfoDialog.ui</Form>
+    <Form>WebBrowser/WebBrowserClearPrivateDataDialog.ui</Form>
     <Form>WebBrowser/ZoomManager/ZoomValuesDialog.ui</Form>
   </Forms>
   <Translations>
@@ -1765,6 +1782,7 @@
     <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/>
@@ -1871,6 +1889,22 @@
     <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>

eric ide

mercurial