Merged with default branch to prepare release 19.02. maintenance release-19.02

Sat, 02 Feb 2019 11:12:54 +0100

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Sat, 02 Feb 2019 11:12:54 +0100
branch
maintenance
changeset 6693
3629d88ae235
parent 6647
2a11e1b2dcbe (current diff)
parent 6692
c104c120e043 (diff)
child 6694
1cccd74fd355

Merged with default branch to prepare release 19.02.

APIs/Python3/eric6.api file | annotate | diff | comparison | revisions
DebugClients/Python/coverage/doc/AUTHORS.txt file | annotate | diff | comparison | revisions
Documentation/Help/source.qch file | annotate | diff | comparison | revisions
Documentation/Help/source.qhp file | annotate | diff | comparison | revisions
Preferences/__init__.py file | annotate | diff | comparison | revisions
ThirdParty/Jasy/jasy/js/__init__.py file | annotate | diff | comparison | revisions
ThirdParty/Jasy/jasy/js/api/Comment.py file | annotate | diff | comparison | revisions
ThirdParty/Jasy/jasy/js/api/Text.py file | annotate | diff | comparison | revisions
ThirdParty/Jasy/jasy/js/api/__init__.py file | annotate | diff | comparison | revisions
ThirdParty/Jasy/jasy/js/parse/Node.py file | annotate | diff | comparison | revisions
ThirdParty/Jasy/jasy/js/parse/Parser.py file | annotate | diff | comparison | revisions
ThirdParty/Jasy/jasy/js/parse/VanillaBuilder.py file | annotate | diff | comparison | revisions
ThirdParty/Jasy/jasy/js/parse/__init__.py file | annotate | diff | comparison | revisions
ThirdParty/Jasy/jasy/js/tokenize/Lang.py file | annotate | diff | comparison | revisions
ThirdParty/Jasy/jasy/js/tokenize/Tokenizer.py file | annotate | diff | comparison | revisions
ThirdParty/Jasy/jasy/js/tokenize/__init__.py file | annotate | diff | comparison | revisions
ThirdParty/Jasy/jasy/js/util/__init__.py file | annotate | diff | comparison | revisions
WebBrowser/WebBrowserView.py file | annotate | diff | comparison | revisions
changelog file | annotate | diff | comparison | revisions
eric6.e4p file | annotate | diff | comparison | revisions
i18n/eric6_cs.ts file | annotate | diff | comparison | revisions
i18n/eric6_de.qm file | annotate | diff | comparison | revisions
i18n/eric6_de.ts file | annotate | diff | comparison | revisions
i18n/eric6_empty.ts file | annotate | diff | comparison | revisions
i18n/eric6_en.ts file | annotate | diff | comparison | revisions
i18n/eric6_es.qm file | annotate | diff | comparison | revisions
i18n/eric6_es.ts file | annotate | diff | comparison | revisions
i18n/eric6_fr.ts file | annotate | diff | comparison | revisions
i18n/eric6_it.ts file | annotate | diff | comparison | revisions
i18n/eric6_pt.ts file | annotate | diff | comparison | revisions
i18n/eric6_ru.qm file | annotate | diff | comparison | revisions
i18n/eric6_ru.ts file | annotate | diff | comparison | revisions
i18n/eric6_tr.ts file | annotate | diff | comparison | revisions
i18n/eric6_zh_CN.ts file | annotate | diff | comparison | revisions
--- a/APIs/Python3/eric6.api	Thu Jan 10 14:23:49 2019 +0100
+++ b/APIs/Python3/eric6.api	Sat Feb 02 11:12:54 2019 +0100
@@ -1486,6 +1486,11 @@
 eric6.E5Network.E5RFC6266.normalize_ws?4(text)
 eric6.E5Network.E5RFC6266.parse_ext_value?4(val)
 eric6.E5Network.E5RFC6266.parse_headers?4(content_disposition)
+eric6.E5Network.E5SslCertificateSelectionDialog.E5SslCertificateSelectionDialog.CertRole?7
+eric6.E5Network.E5SslCertificateSelectionDialog.E5SslCertificateSelectionDialog.getSelectedCertificate?4()
+eric6.E5Network.E5SslCertificateSelectionDialog.E5SslCertificateSelectionDialog.on_certificatesTree_itemSelectionChanged?4()
+eric6.E5Network.E5SslCertificateSelectionDialog.E5SslCertificateSelectionDialog.on_viewButton_clicked?4()
+eric6.E5Network.E5SslCertificateSelectionDialog.E5SslCertificateSelectionDialog?1(certificates, parent=None)
 eric6.E5Network.E5SslCertificatesDialog.E5SslCertificatesDialog.CertRole?7
 eric6.E5Network.E5SslCertificatesDialog.E5SslCertificatesDialog.on_caCertificatesTree_currentItemChanged?4(current, previous)
 eric6.E5Network.E5SslCertificatesDialog.E5SslCertificatesDialog.on_caDeleteButton_clicked?4()
@@ -3620,14 +3625,15 @@
 eric6.MultiProject.AddProjectDialog.AddProjectDialog.getData?4()
 eric6.MultiProject.AddProjectDialog.AddProjectDialog.on_filenamePicker_textChanged?4(txt)
 eric6.MultiProject.AddProjectDialog.AddProjectDialog.on_nameEdit_textChanged?4(txt)
-eric6.MultiProject.AddProjectDialog.AddProjectDialog?1(parent=None, startdir=None, project=None, categories=None)
+eric6.MultiProject.AddProjectDialog.AddProjectDialog?1(parent=None, startdir="", project=None, categories=None, category="")
 eric6.MultiProject.MultiProject.MultiProject.addE5Actions?4(actions)
-eric6.MultiProject.MultiProject.MultiProject.addNewProject?4(startdir=None)
+eric6.MultiProject.MultiProject.MultiProject.addNewProject?4(startdir="", category="")
 eric6.MultiProject.MultiProject.MultiProject.addProject?4(project)
 eric6.MultiProject.MultiProject.MultiProject.changeProjectProperties?4(pro)
 eric6.MultiProject.MultiProject.MultiProject.checkDirty?4()
 eric6.MultiProject.MultiProject.MultiProject.clearRecent?4()
 eric6.MultiProject.MultiProject.MultiProject.closeMultiProject?4()
+eric6.MultiProject.MultiProject.MultiProject.deleteProject?4(uid)
 eric6.MultiProject.MultiProject.MultiProject.dirty?7
 eric6.MultiProject.MultiProject.MultiProject.getActions?4()
 eric6.MultiProject.MultiProject.MultiProject.getCategories?4()
@@ -9843,6 +9849,7 @@
 eric6.UI.FindFileNameDialog.FindFileNameDialog?1(project, parent=None)
 eric6.UI.Info.BugAddress?7
 eric6.UI.Info.Copyright?7
+eric6.UI.Info.CopyrightShort?7
 eric6.UI.Info.FeatureAddress?7
 eric6.UI.Info.Homepage?7
 eric6.UI.Info.Program?7
@@ -12161,6 +12168,7 @@
 eric6.WebBrowser.Tools.Scripts.getFeedLinks?4()
 eric6.WebBrowser.Tools.Scripts.getFormData?4(pos)
 eric6.WebBrowser.Tools.Scripts.getOpenSearchLinks?4()
+eric6.WebBrowser.Tools.Scripts.scrollToAnchor?4(anchor)
 eric6.WebBrowser.Tools.Scripts.sendPostData?4(url, data)
 eric6.WebBrowser.Tools.Scripts.setCss?4(css)
 eric6.WebBrowser.Tools.Scripts.setStyleSheet?4(css)
@@ -12335,7 +12343,7 @@
 eric6.WebBrowser.WebBrowserPage.WebBrowserPage.mapToViewport?4(pos)
 eric6.WebBrowser.WebBrowserPage.WebBrowserPage.navigationRequestAccepted?7
 eric6.WebBrowser.WebBrowserPage.WebBrowserPage.printCallback?4(resDict=resultDict)
-eric6.WebBrowser.WebBrowserPage.WebBrowserPage.printRequested?7
+eric6.WebBrowser.WebBrowserPage.WebBrowserPage.printPageRequested?7
 eric6.WebBrowser.WebBrowserPage.WebBrowserPage.resultCallback?4(resDict=resultDict)
 eric6.WebBrowser.WebBrowserPage.WebBrowserPage.runJavaScript?4(script, worldId=-1, callback=None)
 eric6.WebBrowser.WebBrowserPage.WebBrowserPage.safeBrowsingAbort?7
@@ -12768,10 +12776,12 @@
 eric6.install.defaultMacPythonExe?7
 eric6.install.determinePyQtVariant?4()
 eric6.install.distDir?7
+eric6.install.doCleanDesktopLinks?7
 eric6.install.doCleanup?7
 eric6.install.doCompile?7
 eric6.install.doDependancyChecks?4()
 eric6.install.exit?4(rcode=0)
+eric6.install.forceCleanDesktopLinks?7
 eric6.install.getWinregEntry?4(name, path)
 eric6.install.includePythonVariant?7
 eric6.install.initGlobals?4()
--- a/APIs/Python3/eric6.bas	Thu Jan 10 14:23:49 2019 +0100
+++ b/APIs/Python3/eric6.bas	Sat Feb 02 11:12:54 2019 +0100
@@ -205,6 +205,7 @@
 E5SingleApplicationServer SingleApplicationServer
 E5SqueezeLabel QLabel
 E5SqueezeLabelPath QLabel
+E5SslCertificateSelectionDialog QDialog Ui_E5SslCertificateSelectionDialog
 E5SslCertificatesDialog QDialog Ui_E5SslCertificatesDialog
 E5SslCertificatesInfoDialog QDialog Ui_E5SslCertificatesInfoDialog
 E5SslCertificatesInfoWidget QWidget Ui_E5SslCertificatesInfoWidget
--- a/DebugClients/Python/DebugBase.py	Thu Jan 10 14:23:49 2019 +0100
+++ b/DebugClients/Python/DebugBase.py	Sat Feb 02 11:12:54 2019 +0100
@@ -708,10 +708,11 @@
         @return list of lists with file name (string), line number (integer)
             and function name (string)
         """
+        tb_lineno = None
         if frame is None:
             fr = self.getCurrentFrame()
         elif type(frame) == list:
-            fr = frame.pop(0)
+            fr, tb_lineno = frame.pop(0)
         else:
             fr = frame
         
@@ -732,7 +733,7 @@
                      "ThreadExtension.py", "threading.py")):
                 break
             
-            fline = fr.f_lineno
+            fline = tb_lineno or fr.f_lineno
             ffunc = fr.f_code.co_name
             
             if ffunc == '?':
@@ -754,7 +755,7 @@
             # is it a stack frame or exception list?
             if type(frame) == list:
                 if frame != []:
-                    fr = frame.pop(0)
+                    fr, tb_lineno = frame.pop(0)
                 else:
                     fr = None
             else:
@@ -886,7 +887,7 @@
             frlist = self.__extract_stack(exctb)
             frlist.reverse()
             
-            self.currentFrame = frlist[0]
+            self.currentFrame = frlist[0][0]
             stack = self.getStack(frlist[self.skipFrames:])
         
         self._dbgClient.lockClient()
@@ -936,7 +937,7 @@
         tb = exctb
         stack = []
         while tb is not None:
-            stack.append(tb.tb_frame)
+            stack.append((tb.tb_frame, tb.tb_lineno))
             tb = tb.tb_next
         tb = None
         return stack
--- a/DebugClients/Python/coverage/cmdline.py	Thu Jan 10 14:23:49 2019 +0100
+++ b/DebugClients/Python/coverage/cmdline.py	Sat Feb 02 11:12:54 2019 +0100
@@ -115,7 +115,10 @@
     )
     rcfile = optparse.make_option(
         '', '--rcfile', action='store',
-        help="Specify configuration file.  Defaults to '.coveragerc'",
+        help=(
+            "Specify configuration file.  "
+            "By default '.coveragerc', 'setup.cfg' and 'tox.ini' are tried."
+        ),
     )
     source = optparse.make_option(
         '', '--source', action='store', metavar="SRC1,SRC2,...",
--- a/DebugClients/Python/coverage/control.py	Thu Jan 10 14:23:49 2019 +0100
+++ b/DebugClients/Python/coverage/control.py	Sat Feb 02 11:12:54 2019 +0100
@@ -855,8 +855,7 @@
         # Find files that were never executed at all.
         for pkg in self.source_pkgs:
             if (not pkg in sys.modules or
-                not hasattr(sys.modules[pkg], '__file__') or
-                not os.path.exists(sys.modules[pkg].__file__)):
+                not module_has_file(sys.modules[pkg])):
                 continue
             pkg_file = source_for_file(sys.modules[pkg].__file__)
             self._find_unexecuted_files(self._canonical_path(pkg_file))
@@ -878,15 +877,12 @@
             self._warn("Module %s was never imported." % pkg, slug="module-not-imported")
             return
 
-        is_namespace = hasattr(mod, '__path__') and not hasattr(mod, '__file__')
-        has_file = hasattr(mod, '__file__') and os.path.exists(mod.__file__)
-
-        if is_namespace:
+        if module_is_namespace(mod):
             # A namespace package. It's OK for this not to have been traced,
             # since there is no code directly in it.
             return
 
-        if not has_file:
+        if not module_has_file(mod):
             self._warn("Module %s has no Python source." % pkg, slug="module-not-python")
             return
 
@@ -1204,6 +1200,19 @@
         return info
 
 
+def module_is_namespace(mod):
+    """Is the module object `mod` a PEP420 namespace module?"""
+    return hasattr(mod, '__path__') and getattr(mod, '__file__', None) is None
+
+
+def module_has_file(mod):
+    """Does the module object `mod` have an existing __file__ ?"""
+    mod__file__ = getattr(mod, '__file__', None)
+    if mod__file__ is None:
+        return False
+    return os.path.exists(mod__file__)
+
+
 # FileDisposition "methods": FileDisposition is a pure value object, so it can
 # be implemented in either C or Python.  Acting on them is done with these
 # functions.
--- a/DebugClients/Python/coverage/doc/AUTHORS.txt	Thu Jan 10 14:23:49 2019 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,73 +0,0 @@
-Coverage.py was originally written by Gareth Rees, and since 2004 has been
-extended and maintained by Ned Batchelder.
-
-Other contributions have been made by:
-
-Adi Roiban
-Alex Gaynor
-Alexander Todorov
-Anthony Sottile
-Arcadiy Ivanov
-Ben Finney
-Bill Hart
-Brandon Rhodes
-Brett Cannon
-Buck Evan
-Carl Gieringer
-Catherine Proulx
-Chris Adams
-Chris Rose
-Christian Heimes
-Christine Lytwynec
-Christoph Zwerschke
-Conrad Ho
-Danek Duvall
-Danny Allen
-David Christian
-David Stanek
-Detlev Offenbach
-Devin Jeanpierre
-Dmitry Shishov
-Dmitry Trofimov
-Eduardo Schettino
-Edward Loper
-Geoff Bache
-George Paci
-George Song
-Greg Rogers
-Guillaume Chazarain
-Ilia Meerovich
-Imri Goldberg
-Ionel Cristian Mărieș
-JT Olds
-Jessamyn Smith
-Jon Chappell
-Joseph Tate
-Julian Berman
-Krystian Kichewko
-Leonardo Pistone
-Lex Berezhny
-Marc Abramowitz
-Marcus Cobden
-Mark van der Wal
-Martin Fuzzey
-Matthew Desmarais
-Max Linke
-Mickie Betz
-Noel O'Boyle
-Pablo Carballo
-Patrick Mezard
-Peter Portante
-Rodrigue Cloutier
-Roger Hu
-Ross Lawley
-Sandra Martocchia
-Sigve Tjora
-Stan Hu
-Stefan Behnel
-Steve Leonard
-Steve Peak
-Ted Wexler
-Titus Brown
-Yury Selivanov
-Zooko Wilcox-O'Hearn
--- a/DebugClients/Python/coverage/doc/CHANGES.rst	Thu Jan 10 14:23:49 2019 +0100
+++ b/DebugClients/Python/coverage/doc/CHANGES.rst	Sat Feb 02 11:12:54 2019 +0100
@@ -16,6 +16,27 @@
     ..  ----------------------------
 
 
+.. _changes_452:
+
+Version 4.5.2 --- 2018-11-12
+----------------------------
+
+- Namespace packages are supported on Python 3.7, where they used to cause
+  TypeErrors about path being None. Fixes `issue 700`_.
+
+- Python 3.8 (as of today!) passes all tests.  Fixes `issue 707` and
+  `issue 714`_.
+
+- Development moved from `Bitbucket`_ to `GitHub`_.
+
+.. _issue 700: https://github.com/nedbat/coveragepy/issues/700
+.. _issue 707: https://github.com/nedbat/coveragepy/issues/707
+.. _issue 714: https://github.com/nedbat/coveragepy/issues/714
+
+.. _Bitbucket: https://bitbucket.org/ned/coveragepy
+.. _GitHub: https://github.com/nedbat/coveragepy
+
+
 .. _changes_451:
 
 Version 4.5.1 --- 2018-02-10
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/DebugClients/Python/coverage/doc/CONTRIBUTORS.txt	Sat Feb 02 11:12:54 2019 +0100
@@ -0,0 +1,105 @@
+Coverage.py was originally written by Gareth Rees, and since 2004 has been
+extended and maintained by Ned Batchelder.
+
+Other contributions, including writing code, updating docs, and submitting
+useful bug reports, have been made by:
+
+Adi Roiban
+Alex Gaynor
+Alex Groce
+Alex Sandro
+Alexander Todorov
+Andrew Hoos
+Anthony Sottile
+Arcadiy Ivanov
+Aron Griffis
+Artem Dayneko
+Ben Finney
+Bill Hart
+Brandon Rhodes
+Brett Cannon
+Buck Evan
+Calen Pennington
+Carl Gieringer
+Catherine Proulx
+Chris Adams
+Chris Jerdonek
+Chris Rose
+Chris Warrick
+Christian Heimes
+Christine Lytwynec
+Christoph Zwerschke
+Conrad Ho
+Cosimo Lupo
+Dan Riti
+Dan Wandschneider
+Danek Duvall
+Daniel Hahler
+Danny Allen
+David Christian
+David MacIver
+David Stanek
+Detlev Offenbach
+Devin Jeanpierre
+Dirk Thomas
+Dmitry Shishov
+Dmitry Trofimov
+Eduardo Schettino
+Emil Madsen
+Edward Loper
+Geoff Bache
+George Paci
+George Song
+Greg Rogers
+Guillaume Chazarain
+Ilia Meerovich
+Imri Goldberg
+Ionel Cristian Mărieș
+JT Olds
+Jessamyn Smith
+Joe Doherty
+Jon Chappell
+Jon Dufresne
+Joseph Tate
+Josh Williams
+Julian Berman
+Krystian Kichewko
+Kyle Altendorf
+Lars Hupfeldt Nielsen
+Leonardo Pistone
+Lex Berezhny
+Loïc Dachary
+Marc Abramowitz
+Marcus Cobden
+Mark van der Wal
+Martin Fuzzey
+Matthew Boehm
+Matthew Desmarais
+Max Linke
+Mickie Betz
+Nathan Land
+Noel O'Boyle
+Olivier Grisel
+Pablo Carballo
+Patrick Mezard
+Peter Baughman
+Peter Ebden
+Peter Portante
+Rodrigue Cloutier
+Roger Hu
+Ross Lawley
+Roy Williams
+Sandra Martocchia
+Scott Belden
+Sigve Tjora
+Stan Hu
+Stefan Behnel
+Stephen Finucane
+Steve Leonard
+Steve Peak
+Ted Wexler
+Titus Brown
+Ville Skyttä
+Yury Selivanov
+Zac Hatfield-Dodds
+Zooko Wilcox-O'Hearn
--- a/DebugClients/Python/coverage/doc/README.rst	Thu Jan 10 14:23:49 2019 +0100
+++ b/DebugClients/Python/coverage/doc/README.rst	Sat Feb 02 11:12:54 2019 +0100
@@ -9,7 +9,8 @@
 
 |  |license| |versions| |status| |docs|
 |  |ci-status| |win-ci-status| |codecov|
-|  |kit| |format| |saythanks|
+|  |kit| |format| |repos|
+|  |tidelift| |saythanks|
 
 .. downloads badge seems to be broken... |downloads|
 
@@ -17,9 +18,25 @@
 the code analysis tools and tracing hooks provided in the Python standard
 library to determine which lines are executable, and which have been executed.
 
+.. |tideliftlogo| image:: doc/media/Tidelift_Logos_RGB_Tidelift_Shorthand_On-White_small.png
+   :width: 75
+   :alt: Tidelift
+
+.. list-table::
+   :widths: 10 100
+
+   * - |tideliftlogo|
+     - Professional support for coverage.py is available as part of the `Tidelift
+       Subscription`_.  Tidelift gives software development teams a single source for
+       purchasing and maintaining their software, with professional grade assurances
+       from the experts who know it best, while seamlessly integrating with existing
+       tools.
+
+.. _Tidelift Subscription: https://tidelift.com/subscription/pkg/pypi-coverage?utm_source=pypi-coverage&utm_medium=referral&utm_campaign=readme
+
 Coverage.py runs on many versions of Python:
 
-* CPython 2.6, 2.7 and 3.3 through 3.7.
+* CPython 2.6, 2.7 and 3.3 through pre-alpha 3.8.
 * PyPy2 5.10 and PyPy3 5.10.
 * Jython 2.7.1, though not for reporting.
 * IronPython 2.7.7, though not for reporting.
@@ -105,6 +122,12 @@
 .. |codecov| image:: http://codecov.io/github/nedbat/coveragepy/coverage.svg?branch=master&precision=2
     :target: http://codecov.io/github/nedbat/coveragepy?branch=master
     :alt: Coverage!
+.. |repos| image:: https://repology.org/badge/tiny-repos/python:coverage.svg
+    :target: https://repology.org/metapackage/python:coverage/versions
+    :alt: Packaging status
 .. |saythanks| image:: https://img.shields.io/badge/saythanks.io-%E2%98%BC-1EAEDB.svg
     :target: https://saythanks.io/to/nedbat
     :alt: Say thanks :)
+.. |tidelift| image:: https://tidelift.com/badges/github/nedbat/coveragepy
+    :target: https://tidelift.com/subscription/pkg/pypi-coverage?utm_source=pypi-coverage&utm_medium=referral&utm_campaign=readme
+    :alt: Tidelift
--- a/DebugClients/Python/coverage/env.py	Thu Jan 10 14:23:49 2019 +0100
+++ b/DebugClients/Python/coverage/env.py	Sat Feb 02 11:12:54 2019 +0100
@@ -24,6 +24,24 @@
 PY2 = PYVERSION < (3, 0)
 PY3 = PYVERSION >= (3, 0)
 
+# Python behavior
+class PYBEHAVIOR(object):
+    """Flags indicating this Python's behavior."""
+
+    # When a break/continue/return statement in a try block jumps to a finally
+    # block, does the finally block do the break/continue/return (pre-3.8), or
+    # does the finally jump back to the break/continue/return (3.8) to do the
+    # work?
+    finally_jumps_back = (PYVERSION >= (3, 8))
+
+    # When a function is decorated, does the trace function get called for the
+    # @-line and also the def-line (new behavior in 3.8)? Or just the @-line
+    # (old behavior)?
+    trace_decorated_def = (PYVERSION >= (3, 8))
+
+    # Are while-true loops optimized into absolute jumps with no loop setup?
+    nix_while_true = (PYVERSION >= (3, 8))
+
 # Coverage.py specifics.
 
 # Are we using the C-implemented trace function?
--- a/DebugClients/Python/coverage/execfile.py	Thu Jan 10 14:23:49 2019 +0100
+++ b/DebugClients/Python/coverage/execfile.py	Sat Feb 02 11:12:54 2019 +0100
@@ -111,7 +111,15 @@
 
     pathname = os.path.abspath(pathname)
     args[0] = pathname
-    run_python_file(pathname, args, package=packagename, modulename=modulename, path0="")
+    # Python 3.7.0b3 changed the behavior of the sys.path[0] entry for -m. It
+    # used to be an empty string (meaning the current directory). It changed
+    # to be the actual path to the current directory, so that os.chdir wouldn't
+    # affect the outcome.
+    if sys.version_info >= (3, 7, 0, 'beta', 3):
+        path0 = os.getcwd()
+    else:
+        path0 = ""
+    run_python_file(pathname, args, package=packagename, modulename=modulename, path0=path0)
 
 
 def run_python_file(filename, args, package=None, modulename=None, path0=None):
--- a/DebugClients/Python/coverage/files.py	Thu Jan 10 14:23:49 2019 +0100
+++ b/DebugClients/Python/coverage/files.py	Sat Feb 02 11:12:54 2019 +0100
@@ -260,19 +260,8 @@
 class FnmatchMatcher(object):
     """A matcher for files by file name pattern."""
     def __init__(self, pats):
-        self.pats = pats[:]
-        # fnmatch is platform-specific. On Windows, it does the Windows thing
-        # of treating / and \ as equivalent. But on other platforms, we need to
-        # take care of that ourselves.
-        fnpats = (fnmatch.translate(p) for p in pats)
-        # Python3.7 fnmatch translates "/" as "/", before that, it translates as "\/",
-        # so we have to deal with maybe a backslash.
-        fnpats = (re.sub(r"\\?/", r"[\\\\/]", p) for p in fnpats)
-        flags = 0
-        if env.WINDOWS:
-            # Windows is also case-insensitive, so make the regex case-insensitive.
-            flags |= re.IGNORECASE
-        self.re = re.compile(join_regex(fnpats), flags=flags)
+        self.pats = list(pats)
+        self.re = fnmatches_to_regex(self.pats, case_insensitive=env.WINDOWS)
 
     def __repr__(self):
         return "<FnmatchMatcher %r>" % self.pats
@@ -296,6 +285,39 @@
     return the_sep
 
 
+def fnmatches_to_regex(patterns, case_insensitive=False, partial=False):
+    """Convert fnmatch patterns to a compiled regex that matches any of them.
+
+    Slashes are always converted to match either slash or backslash, for
+    Windows support, even when running elsewhere.
+
+    If `partial` is true, then the pattern will match if the target string
+    starts with the pattern. Otherwise, it must match the entire string.
+
+    Returns: a compiled regex object.  Use the .match method to compare target
+    strings.
+
+    """
+    regexes = (fnmatch.translate(pattern) for pattern in patterns)
+    # Python3.7 fnmatch translates "/" as "/". Before that, it translates as "\/",
+    # so we have to deal with maybe a backslash.
+    regexes = (re.sub(r"\\?/", r"[\\\\/]", regex) for regex in regexes)
+
+    if partial:
+        # fnmatch always adds a \Z to match the whole string, which we don't
+        # want, so we remove the \Z.  While removing it, we only replace \Z if
+        # followed by paren (introducing flags), or at end, to keep from
+        # destroying a literal \Z in the pattern.
+        regexes = (re.sub(r'\\Z(\(\?|$)', r'\1', regex) for regex in regexes)
+
+    flags = 0
+    if case_insensitive:
+        flags |= re.IGNORECASE
+    compiled = re.compile(join_regex(regexes), flags=flags)
+
+    return compiled
+
+
 class PathAliases(object):
     """A collection of aliases for paths.
 
@@ -343,18 +365,8 @@
         if not pattern.endswith(pattern_sep):
             pattern += pattern_sep
 
-        # Make a regex from the pattern.  fnmatch always adds a \Z to
-        # match the whole string, which we don't want, so we remove the \Z.
-        # While removing it, we only replace \Z if followed by paren, or at
-        # end, to keep from destroying a literal \Z in the pattern.
-        regex_pat = fnmatch.translate(pattern)
-        regex_pat = re.sub(r'\\Z(\(|$)', r'\1', regex_pat)
-
-        # We want */a/b.py to match on Windows too, so change slash to match
-        # either separator.
-        regex_pat = regex_pat.replace(r"\/", r"[\\/]")
-        # We want case-insensitive matching, so add that flag.
-        regex = re.compile(r"(?i)" + regex_pat)
+        # Make a regex from the pattern.
+        regex = fnmatches_to_regex([pattern], case_insensitive=True, partial=True)
 
         # Normalize the result: it must end with a path separator.
         result_sep = sep(result)
--- a/DebugClients/Python/coverage/parser.py	Thu Jan 10 14:23:49 2019 +0100
+++ b/DebugClients/Python/coverage/parser.py	Sat Feb 02 11:12:54 2019 +0100
@@ -409,6 +409,8 @@
                     yield (byte_num, line_num)
                     last_line_num = line_num
                 byte_num += byte_incr
+            if env.PYVERSION >= (3, 6) and line_incr >= 0x80:
+                line_incr -= 0x100
             line_num += line_incr
         if line_num != last_line_num:
             yield (byte_num, line_num)
@@ -503,6 +505,10 @@
         self.lineno = body[0].lineno
 
 
+# TODO: some add_arcs methods here don't add arcs, they return them. Rename them.
+# TODO: the cause messages have too many commas.
+# TODO: Shouldn't the cause messages join with "and" instead of "or"?
+
 class AstArcAnalyzer(object):
     """Analyze source text with an AST to find executable code paths."""
 
@@ -544,6 +550,7 @@
             if code_object_handler is not None:
                 code_object_handler(node)
 
+    @contract(start=int, end=int)
     def add_arc(self, start, end, smsg=None, emsg=None):
         """Add an arc, including message fragments to use if it is missing."""
         if self.debug:                      # pragma: debugging
@@ -572,9 +579,19 @@
         else:
             return node.lineno
 
+    def _line_decorated(self, node):
+        """Compute first line number for things that can be decorated (classes and functions)."""
+        lineno = node.lineno
+        if env.PYBEHAVIOR.trace_decorated_def:
+            if node.decorator_list:
+                lineno = node.decorator_list[0].lineno
+        return lineno
+
     def _line__Assign(self, node):
         return self.line_for_node(node.value)
 
+    _line__ClassDef = _line_decorated
+
     def _line__Dict(self, node):
         # Python 3.5 changed how dict literals are made.
         if env.PYVERSION >= (3, 5) and node.keys:
@@ -587,6 +604,8 @@
         else:
             return node.lineno
 
+    _line__FunctionDef = _line_decorated
+
     def _line__List(self, node):
         if node.elts:
             return self.line_for_node(node.elts[0])
@@ -690,6 +709,13 @@
             node = None
         return node
 
+    # Missing nodes: _missing__*
+    #
+    # Entire statements can be optimized away by Python. They will appear in
+    # the AST, but not the bytecode.  These functions are called (by
+    # find_non_missing_node) to find a node to use instead of the missing
+    # node.  They can return None if the node should truly be gone.
+
     def _missing__If(self, node):
         # If the if-node is missing, then one of its children might still be
         # here, but not both. So return the first of the two that isn't missing.
@@ -717,10 +743,24 @@
             return non_missing_children[0]
         return NodeList(non_missing_children)
 
+    def _missing__While(self, node):
+        body_nodes = self.find_non_missing_node(NodeList(node.body))
+        if not body_nodes:
+            return None
+        # Make a synthetic While-true node.
+        new_while = ast.While()
+        new_while.lineno = body_nodes.lineno
+        new_while.test = ast.Name()
+        new_while.test.lineno = body_nodes.lineno
+        new_while.test.id = "True"
+        new_while.body = body_nodes.body
+        new_while.orelse = None
+        return new_while
+
     def is_constant_expr(self, node):
         """Is this a compile-time constant?"""
         node_name = node.__class__.__name__
-        if node_name in ["NameConstant", "Num"]:
+        if node_name in ["Constant", "NameConstant", "Num"]:
             return "Num"
         elif node_name == "Name":
             if node.id in ["True", "False", "None", "__debug__"]:
@@ -805,10 +845,10 @@
     # Handlers: _handle__*
     #
     # Each handler deals with a specific AST node type, dispatched from
-    # add_arcs.  Each deals with a particular kind of node type, and returns
-    # the set of exits from that node. These functions mirror the Python
-    # semantics of each syntactic construct.  See the docstring for add_arcs to
-    # understand the concept of exits from a node.
+    # add_arcs.  Handlers return the set of exits from that node, and can
+    # also call self.add_arc to record arcs they find.  These functions mirror
+    # the Python semantics of each syntactic construct.  See the docstring
+    # for add_arcs to understand the concept of exits from a node.
 
     @contract(returns='ArcStarts')
     def _handle__Break(self, node):
@@ -820,13 +860,18 @@
     @contract(returns='ArcStarts')
     def _handle_decorated(self, node):
         """Add arcs for things that can be decorated (classes and functions)."""
-        last = self.line_for_node(node)
+        main_line = last = node.lineno
         if node.decorator_list:
+            if env.PYBEHAVIOR.trace_decorated_def:
+                last = None
             for dec_node in node.decorator_list:
                 dec_start = self.line_for_node(dec_node)
-                if dec_start != last:
+                if last is not None and dec_start != last:
                     self.add_arc(last, dec_start)
-                    last = dec_start
+                last = dec_start
+            if env.PYBEHAVIOR.trace_decorated_def:
+                self.add_arc(last, main_line)
+                last = main_line
             # The definition line may have been missed, but we should have it
             # in `self.statements`.  For some constructs, `line_for_node` is
             # not what we'd think of as the first line in the statement, so map
@@ -968,21 +1013,45 @@
             final_exits = self.add_body_arcs(node.finalbody, prev_starts=final_from)
 
             if try_block.break_from:
-                self.process_break_exits(
-                    self._combine_finally_starts(try_block.break_from, final_exits)
-                )
+                if env.PYBEHAVIOR.finally_jumps_back:
+                    for break_line in try_block.break_from:
+                        lineno = break_line.lineno
+                        cause = break_line.cause.format(lineno=lineno)
+                        for final_exit in final_exits:
+                            self.add_arc(final_exit.lineno, lineno, cause)
+                    breaks = try_block.break_from
+                else:
+                    breaks = self._combine_finally_starts(try_block.break_from, final_exits)
+                self.process_break_exits(breaks)
+
             if try_block.continue_from:
-                self.process_continue_exits(
-                    self._combine_finally_starts(try_block.continue_from, final_exits)
-                )
+                if env.PYBEHAVIOR.finally_jumps_back:
+                    for continue_line in try_block.continue_from:
+                        lineno = continue_line.lineno
+                        cause = continue_line.cause.format(lineno=lineno)
+                        for final_exit in final_exits:
+                            self.add_arc(final_exit.lineno, lineno, cause)
+                    continues = try_block.continue_from
+                else:
+                    continues = self._combine_finally_starts(try_block.continue_from, final_exits)
+                self.process_continue_exits(continues)
+
             if try_block.raise_from:
                 self.process_raise_exits(
                     self._combine_finally_starts(try_block.raise_from, final_exits)
                 )
+
             if try_block.return_from:
-                self.process_return_exits(
-                    self._combine_finally_starts(try_block.return_from, final_exits)
-                )
+                if env.PYBEHAVIOR.finally_jumps_back:
+                    for return_line in try_block.return_from:
+                        lineno = return_line.lineno
+                        cause = return_line.cause.format(lineno=lineno)
+                        for final_exit in final_exits:
+                            self.add_arc(final_exit.lineno, lineno, cause)
+                    returns = try_block.return_from
+                else:
+                    returns = self._combine_finally_starts(try_block.return_from, final_exits)
+                self.process_return_exits(returns)
 
             if exits:
                 # The finally clause's exits are only exits for the try block
--- a/DebugClients/Python/coverage/python.py	Thu Jan 10 14:23:49 2019 +0100
+++ b/DebugClients/Python/coverage/python.py	Sat Feb 02 11:12:54 2019 +0100
@@ -135,7 +135,7 @@
     def __init__(self, morf, coverage=None):
         self.coverage = coverage
 
-        if hasattr(morf, '__file__'):
+        if hasattr(morf, '__file__') and morf.__file__:
             filename = morf.__file__
         elif isinstance(morf, types.ModuleType):
             # A module should have had .__file__, otherwise we can't use it.
--- a/DebugClients/Python/coverage/version.py	Thu Jan 10 14:23:49 2019 +0100
+++ b/DebugClients/Python/coverage/version.py	Sat Feb 02 11:12:54 2019 +0100
@@ -5,7 +5,7 @@
 # This file is exec'ed in setup.py, don't import anything!
 
 # Same semantics as sys.version_info.
-version_info = (4, 5, 1, 'final', 0)
+version_info = (4, 5, 2, 'final', 0)
 
 
 def _make_version(major, minor, micro, releaselevel, serial):
--- a/DebugClients/Python/coverage/xmlreport.py	Thu Jan 10 14:23:49 2019 +0100
+++ b/DebugClients/Python/coverage/xmlreport.py	Sat Feb 02 11:12:54 2019 +0100
@@ -1,3 +1,4 @@
+# coding: utf-8
 # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
 
@@ -5,6 +6,7 @@
 
 import os
 import os.path
+import re
 import sys
 import time
 import xml.dom.minidom
@@ -123,11 +125,8 @@
             xcoverage.setAttribute("branch-rate", "0")
         xcoverage.setAttribute("complexity", "0")
 
-        # Use the DOM to write the output file.
-        out = self.xml_out.toprettyxml()
-        if env.PY2:
-            out = out.encode("utf8")
-        outfile.write(out)
+        # Write the output file.
+        outfile.write(serialize_xml(self.xml_out))
 
         # Return the total percentage.
         denom = lnum_tot + bnum_tot
@@ -218,3 +217,23 @@
         package[2] += class_lines
         package[3] += class_br_hits
         package[4] += class_branches
+
+
+def serialize_xml(dom):
+    """Serialize a minidom node to XML."""
+    out = dom.toprettyxml()
+    if env.PY2:
+        out = out.encode("utf8")
+    # In Python 3.8, minidom lost the sorting of attributes: https://bugs.python.org/issue34160
+    # For the limited kinds of XML we produce, this re-sorts them.
+    if env.PYVERSION >= (3, 8):
+        rx_attr = r' [\w-]+="[^"]*"'
+        rx_attrs = r'(' + rx_attr + ')+'
+        fixed_lines = []
+        for line in out.splitlines(True):
+            hollow_line = re.sub(rx_attrs, u"☺", line)
+            attrs = sorted(re.findall(rx_attr, line))
+            new_line = hollow_line.replace(u"☺", "".join(attrs))
+            fixed_lines.append(new_line)
+        out = "".join(fixed_lines)
+    return out
Binary file Documentation/Help/source.qch has changed
--- a/Documentation/Help/source.qhp	Thu Jan 10 14:23:49 2019 +0100
+++ b/Documentation/Help/source.qhp	Sat Feb 02 11:12:54 2019 +0100
@@ -135,6 +135,7 @@
             <section title="eric6.E5Network.E5NetworkMonitor" ref="eric6.E5Network.E5NetworkMonitor.html" />
             <section title="eric6.E5Network.E5NetworkProxyFactory" ref="eric6.E5Network.E5NetworkProxyFactory.html" />
             <section title="eric6.E5Network.E5RFC6266" ref="eric6.E5Network.E5RFC6266.html" />
+            <section title="eric6.E5Network.E5SslCertificateSelectionDialog" ref="eric6.E5Network.E5SslCertificateSelectionDialog.html" />
             <section title="eric6.E5Network.E5SslCertificatesDialog" ref="eric6.E5Network.E5SslCertificatesDialog.html" />
             <section title="eric6.E5Network.E5SslCertificatesInfoDialog" ref="eric6.E5Network.E5SslCertificatesInfoDialog.html" />
             <section title="eric6.E5Network.E5SslCertificatesInfoWidget" ref="eric6.E5Network.E5SslCertificatesInfoWidget.html" />
@@ -3513,16 +3514,13 @@
       <keyword name="CreateDialogCodeDialog (Constructor)" id="CreateDialogCodeDialog (Constructor)" ref="eric6.Project.CreateDialogCodeDialog.html#CreateDialogCodeDialog.__init__" />
       <keyword name="CreateDialogCodeDialog (Module)" id="CreateDialogCodeDialog (Module)" ref="eric6.Project.CreateDialogCodeDialog.html" />
       <keyword name="CreateDialogCodeDialog.__className" id="CreateDialogCodeDialog.__className" ref="eric6.Project.CreateDialogCodeDialog.html#CreateDialogCodeDialog.__className" />
-      <keyword name="CreateDialogCodeDialog.__classNameExternal" id="CreateDialogCodeDialog.__classNameExternal" ref="eric6.Project.CreateDialogCodeDialog.html#CreateDialogCodeDialog.__classNameExternal" />
       <keyword name="CreateDialogCodeDialog.__generateCode" id="CreateDialogCodeDialog.__generateCode" ref="eric6.Project.CreateDialogCodeDialog.html#CreateDialogCodeDialog.__generateCode" />
       <keyword name="CreateDialogCodeDialog.__generatePythonCode" id="CreateDialogCodeDialog.__generatePythonCode" ref="eric6.Project.CreateDialogCodeDialog.html#CreateDialogCodeDialog.__generatePythonCode" />
       <keyword name="CreateDialogCodeDialog.__mapType" id="CreateDialogCodeDialog.__mapType" ref="eric6.Project.CreateDialogCodeDialog.html#CreateDialogCodeDialog.__mapType" />
       <keyword name="CreateDialogCodeDialog.__objectName" id="CreateDialogCodeDialog.__objectName" ref="eric6.Project.CreateDialogCodeDialog.html#CreateDialogCodeDialog.__objectName" />
-      <keyword name="CreateDialogCodeDialog.__objectNameExternal" id="CreateDialogCodeDialog.__objectNameExternal" ref="eric6.Project.CreateDialogCodeDialog.html#CreateDialogCodeDialog.__objectNameExternal" />
       <keyword name="CreateDialogCodeDialog.__runUicLoadUi" id="CreateDialogCodeDialog.__runUicLoadUi" ref="eric6.Project.CreateDialogCodeDialog.html#CreateDialogCodeDialog.__runUicLoadUi" />
       <keyword name="CreateDialogCodeDialog.__signatures" id="CreateDialogCodeDialog.__signatures" ref="eric6.Project.CreateDialogCodeDialog.html#CreateDialogCodeDialog.__signatures" />
       <keyword name="CreateDialogCodeDialog.__updateSlotsModel" id="CreateDialogCodeDialog.__updateSlotsModel" ref="eric6.Project.CreateDialogCodeDialog.html#CreateDialogCodeDialog.__updateSlotsModel" />
-      <keyword name="CreateDialogCodeDialog.__updateSlotsModelExternal" id="CreateDialogCodeDialog.__updateSlotsModelExternal" ref="eric6.Project.CreateDialogCodeDialog.html#CreateDialogCodeDialog.__updateSlotsModelExternal" />
       <keyword name="CreateDialogCodeDialog.initError" id="CreateDialogCodeDialog.initError" ref="eric6.Project.CreateDialogCodeDialog.html#CreateDialogCodeDialog.initError" />
       <keyword name="CreateDialogCodeDialog.on_buttonBox_clicked" id="CreateDialogCodeDialog.on_buttonBox_clicked" ref="eric6.Project.CreateDialogCodeDialog.html#CreateDialogCodeDialog.on_buttonBox_clicked" />
       <keyword name="CreateDialogCodeDialog.on_classNameCombo_activated" id="CreateDialogCodeDialog.on_classNameCombo_activated" ref="eric6.Project.CreateDialogCodeDialog.html#CreateDialogCodeDialog.on_classNameCombo_activated" />
@@ -4867,6 +4865,14 @@
       <keyword name="E5SqueezeLabelPath.setSurrounding" id="E5SqueezeLabelPath.setSurrounding" ref="eric6.E5Gui.E5SqueezeLabels.html#E5SqueezeLabelPath.setSurrounding" />
       <keyword name="E5SqueezeLabelPath.setTextPath" id="E5SqueezeLabelPath.setTextPath" ref="eric6.E5Gui.E5SqueezeLabels.html#E5SqueezeLabelPath.setTextPath" />
       <keyword name="E5SqueezeLabels (Module)" id="E5SqueezeLabels (Module)" ref="eric6.E5Gui.E5SqueezeLabels.html" />
+      <keyword name="E5SslCertificateSelectionDialog" id="E5SslCertificateSelectionDialog" ref="eric6.E5Network.E5SslCertificateSelectionDialog.html#E5SslCertificateSelectionDialog" />
+      <keyword name="E5SslCertificateSelectionDialog (Constructor)" id="E5SslCertificateSelectionDialog (Constructor)" ref="eric6.E5Network.E5SslCertificateSelectionDialog.html#E5SslCertificateSelectionDialog.__init__" />
+      <keyword name="E5SslCertificateSelectionDialog (Module)" id="E5SslCertificateSelectionDialog (Module)" ref="eric6.E5Network.E5SslCertificateSelectionDialog.html" />
+      <keyword name="E5SslCertificateSelectionDialog.__createCaCertificateEntry" id="E5SslCertificateSelectionDialog.__createCaCertificateEntry" ref="eric6.E5Network.E5SslCertificateSelectionDialog.html#E5SslCertificateSelectionDialog.__createCaCertificateEntry" />
+      <keyword name="E5SslCertificateSelectionDialog.__populateCertificatesTree" id="E5SslCertificateSelectionDialog.__populateCertificatesTree" ref="eric6.E5Network.E5SslCertificateSelectionDialog.html#E5SslCertificateSelectionDialog.__populateCertificatesTree" />
+      <keyword name="E5SslCertificateSelectionDialog.getSelectedCertificate" id="E5SslCertificateSelectionDialog.getSelectedCertificate" ref="eric6.E5Network.E5SslCertificateSelectionDialog.html#E5SslCertificateSelectionDialog.getSelectedCertificate" />
+      <keyword name="E5SslCertificateSelectionDialog.on_certificatesTree_itemSelectionChanged" id="E5SslCertificateSelectionDialog.on_certificatesTree_itemSelectionChanged" ref="eric6.E5Network.E5SslCertificateSelectionDialog.html#E5SslCertificateSelectionDialog.on_certificatesTree_itemSelectionChanged" />
+      <keyword name="E5SslCertificateSelectionDialog.on_viewButton_clicked" id="E5SslCertificateSelectionDialog.on_viewButton_clicked" ref="eric6.E5Network.E5SslCertificateSelectionDialog.html#E5SslCertificateSelectionDialog.on_viewButton_clicked" />
       <keyword name="E5SslCertificatesDialog" id="E5SslCertificatesDialog" ref="eric6.E5Network.E5SslCertificatesDialog.html#E5SslCertificatesDialog" />
       <keyword name="E5SslCertificatesDialog (Constructor)" id="E5SslCertificatesDialog (Constructor)" ref="eric6.E5Network.E5SslCertificatesDialog.html#E5SslCertificatesDialog.__init__" />
       <keyword name="E5SslCertificatesDialog (Module)" id="E5SslCertificatesDialog (Module)" ref="eric6.E5Network.E5SslCertificatesDialog.html" />
@@ -11309,6 +11315,7 @@
       <keyword name="MultiProject.checkDirty" id="MultiProject.checkDirty" ref="eric6.MultiProject.MultiProject.html#MultiProject.checkDirty" />
       <keyword name="MultiProject.clearRecent" id="MultiProject.clearRecent" ref="eric6.MultiProject.MultiProject.html#MultiProject.clearRecent" />
       <keyword name="MultiProject.closeMultiProject" id="MultiProject.closeMultiProject" ref="eric6.MultiProject.MultiProject.html#MultiProject.closeMultiProject" />
+      <keyword name="MultiProject.deleteProject" id="MultiProject.deleteProject" ref="eric6.MultiProject.MultiProject.html#MultiProject.deleteProject" />
       <keyword name="MultiProject.getActions" id="MultiProject.getActions" ref="eric6.MultiProject.MultiProject.html#MultiProject.getActions" />
       <keyword name="MultiProject.getCategories" id="MultiProject.getCategories" ref="eric6.MultiProject.MultiProject.html#MultiProject.getCategories" />
       <keyword name="MultiProject.getDependantProjectFiles" id="MultiProject.getDependantProjectFiles" ref="eric6.MultiProject.MultiProject.html#MultiProject.getDependantProjectFiles" />
@@ -11339,6 +11346,7 @@
       <keyword name="MultiProjectBrowser.__configure" id="MultiProjectBrowser.__configure" ref="eric6.MultiProject.MultiProjectBrowser.html#MultiProjectBrowser.__configure" />
       <keyword name="MultiProjectBrowser.__contextMenuRequested" id="MultiProjectBrowser.__contextMenuRequested" ref="eric6.MultiProject.MultiProjectBrowser.html#MultiProjectBrowser.__contextMenuRequested" />
       <keyword name="MultiProjectBrowser.__createPopupMenu" id="MultiProjectBrowser.__createPopupMenu" ref="eric6.MultiProject.MultiProjectBrowser.html#MultiProjectBrowser.__createPopupMenu" />
+      <keyword name="MultiProjectBrowser.__deleteProject" id="MultiProjectBrowser.__deleteProject" ref="eric6.MultiProject.MultiProjectBrowser.html#MultiProjectBrowser.__deleteProject" />
       <keyword name="MultiProjectBrowser.__findCategoryItem" id="MultiProjectBrowser.__findCategoryItem" ref="eric6.MultiProject.MultiProjectBrowser.html#MultiProjectBrowser.__findCategoryItem" />
       <keyword name="MultiProjectBrowser.__findProjectItem" id="MultiProjectBrowser.__findProjectItem" ref="eric6.MultiProject.MultiProjectBrowser.html#MultiProjectBrowser.__findProjectItem" />
       <keyword name="MultiProjectBrowser.__multiProjectClosed" id="MultiProjectBrowser.__multiProjectClosed" ref="eric6.MultiProject.MultiProjectBrowser.html#MultiProjectBrowser.__multiProjectClosed" />
@@ -17834,6 +17842,7 @@
       <keyword name="WebBrowserPage (Constructor)" id="WebBrowserPage (Constructor)" ref="eric6.WebBrowser.WebBrowserPage.html#WebBrowserPage.__init__" />
       <keyword name="WebBrowserPage (Module)" id="WebBrowserPage (Module)" ref="eric6.Preferences.ConfigurationPages.WebBrowserPage.html" />
       <keyword name="WebBrowserPage (Module)" id="WebBrowserPage (Module)" ref="eric6.WebBrowser.WebBrowserPage.html" />
+      <keyword name="WebBrowserPage.__contentsSizeChanged" id="WebBrowserPage.__contentsSizeChanged" ref="eric6.WebBrowser.WebBrowserPage.html#WebBrowserPage.__contentsSizeChanged" />
       <keyword name="WebBrowserPage.__featurePermissionRequested" id="WebBrowserPage.__featurePermissionRequested" ref="eric6.WebBrowser.WebBrowserPage.html#WebBrowserPage.__featurePermissionRequested" />
       <keyword name="WebBrowserPage.__fullScreenRequested" id="WebBrowserPage.__fullScreenRequested" ref="eric6.WebBrowser.WebBrowserPage.html#WebBrowserPage.__fullScreenRequested" />
       <keyword name="WebBrowserPage.__loadProgress" id="WebBrowserPage.__loadProgress" ref="eric6.WebBrowser.WebBrowserPage.html#WebBrowserPage.__loadProgress" />
@@ -18009,6 +18018,7 @@
       <keyword name="WebBrowserView.__searchDefaultRequested" id="WebBrowserView.__searchDefaultRequested" ref="eric6.WebBrowser.WebBrowserView.html#WebBrowserView.__searchDefaultRequested" />
       <keyword name="WebBrowserView.__searchImage" id="WebBrowserView.__searchImage" ref="eric6.WebBrowser.WebBrowserView.html#WebBrowserView.__searchImage" />
       <keyword name="WebBrowserView.__searchRequested" id="WebBrowserView.__searchRequested" ref="eric6.WebBrowser.WebBrowserView.html#WebBrowserView.__searchRequested" />
+      <keyword name="WebBrowserView.__selectClientCertificate" id="WebBrowserView.__selectClientCertificate" ref="eric6.WebBrowser.WebBrowserView.html#WebBrowserView.__selectClientCertificate" />
       <keyword name="WebBrowserView.__sendLink" id="WebBrowserView.__sendLink" ref="eric6.WebBrowser.WebBrowserView.html#WebBrowserView.__sendLink" />
       <keyword name="WebBrowserView.__setRwhvqt" id="WebBrowserView.__setRwhvqt" ref="eric6.WebBrowser.WebBrowserView.html#WebBrowserView.__setRwhvqt" />
       <keyword name="WebBrowserView.__showEventSlot" id="WebBrowserView.__showEventSlot" ref="eric6.WebBrowser.WebBrowserView.html#WebBrowserView.__showEventSlot" />
@@ -19345,6 +19355,7 @@
       <keyword name="saveToolGroups" id="saveToolGroups" ref="eric6.Preferences.__init__.html#saveToolGroups" />
       <keyword name="sbsdiff" id="sbsdiff" ref="eric6.UI.CompareDialog.html#sbsdiff" />
       <keyword name="schemeFromProxyType" id="schemeFromProxyType" ref="eric6.E5Network.E5NetworkProxyFactory.html#schemeFromProxyType" />
+      <keyword name="scrollToAnchor" id="scrollToAnchor" ref="eric6.WebBrowser.Tools.Scripts.html#scrollToAnchor" />
       <keyword name="sendPostData" id="sendPostData" ref="eric6.WebBrowser.Tools.Scripts.html#sendPostData" />
       <keyword name="setActions" id="setActions" ref="eric6.Preferences.Shortcuts.html#setActions" />
       <keyword name="setConfigDir" id="setConfigDir" ref="eric6.Globals.__init__.html#setConfigDir" />
@@ -19615,6 +19626,7 @@
       <file>eric6.E5Network.E5NetworkMonitor.html</file>
       <file>eric6.E5Network.E5NetworkProxyFactory.html</file>
       <file>eric6.E5Network.E5RFC6266.html</file>
+      <file>eric6.E5Network.E5SslCertificateSelectionDialog.html</file>
       <file>eric6.E5Network.E5SslCertificatesDialog.html</file>
       <file>eric6.E5Network.E5SslCertificatesInfoDialog.html</file>
       <file>eric6.E5Network.E5SslCertificatesInfoWidget.html</file>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Documentation/Source/eric6.E5Network.E5SslCertificateSelectionDialog.html	Sat Feb 02 11:12:54 2019 +0100
@@ -0,0 +1,144 @@
+<!DOCTYPE html>
+<html><head>
+<title>eric6.E5Network.E5SslCertificateSelectionDialog</title>
+<meta charset="UTF-8">
+<style>
+body {
+    background: #EDECE6;
+    margin: 0em 1em 10em 1em;
+    color: black;
+}
+
+h1 { color: white; background: #85774A; }
+h2 { color: white; background: #85774A; }
+h3 { color: white; background: #9D936E; }
+h4 { color: white; background: #9D936E; }
+    
+a { color: #BA6D36; }
+
+</style>
+</head>
+<body><a NAME="top" ID="top"></a>
+<h1>eric6.E5Network.E5SslCertificateSelectionDialog</h1>
+<p>
+Module implementing a dialog to select a SSL certificate.
+</p>
+<h3>Global Attributes</h3>
+<table>
+<tr><td>None</td></tr>
+</table>
+<h3>Classes</h3>
+<table>
+<tr>
+<td><a href="#E5SslCertificateSelectionDialog">E5SslCertificateSelectionDialog</a></td>
+<td>Class implementing a dialog to select a SSL certificate.</td>
+</tr>
+</table>
+<h3>Functions</h3>
+<table>
+<tr><td>None</td></tr>
+</table>
+<hr /><hr />
+<a NAME="E5SslCertificateSelectionDialog" ID="E5SslCertificateSelectionDialog"></a>
+<h2>E5SslCertificateSelectionDialog</h2>
+<p>
+    Class implementing a dialog to select a SSL certificate.
+</p>
+<h3>Derived from</h3>
+QDialog, Ui_E5SslCertificateSelectionDialog
+<h3>Class Attributes</h3>
+<table>
+<tr><td>CertRole</td></tr>
+</table>
+<h3>Class Methods</h3>
+<table>
+<tr><td>None</td></tr>
+</table>
+<h3>Methods</h3>
+<table>
+<tr>
+<td><a href="#E5SslCertificateSelectionDialog.__init__">E5SslCertificateSelectionDialog</a></td>
+<td>Constructor</td>
+</tr><tr>
+<td><a href="#E5SslCertificateSelectionDialog.__createCaCertificateEntry">__createCaCertificateEntry</a></td>
+<td>Private method to create a certificate entry.</td>
+</tr><tr>
+<td><a href="#E5SslCertificateSelectionDialog.__populateCertificatesTree">__populateCertificatesTree</a></td>
+<td>Private slot to populate the certificates tree.</td>
+</tr><tr>
+<td><a href="#E5SslCertificateSelectionDialog.getSelectedCertificate">getSelectedCertificate</a></td>
+<td>Public method to get the selected certificate.</td>
+</tr><tr>
+<td><a href="#E5SslCertificateSelectionDialog.on_certificatesTree_itemSelectionChanged">on_certificatesTree_itemSelectionChanged</a></td>
+<td>Private slot to handle the selection of an item.</td>
+</tr><tr>
+<td><a href="#E5SslCertificateSelectionDialog.on_viewButton_clicked">on_viewButton_clicked</a></td>
+<td>Private slot to show data of the selected certificate.</td>
+</tr>
+</table>
+<h3>Static Methods</h3>
+<table>
+<tr><td>None</td></tr>
+</table>
+<a NAME="E5SslCertificateSelectionDialog.__init__" ID="E5SslCertificateSelectionDialog.__init__"></a>
+<h4>E5SslCertificateSelectionDialog (Constructor)</h4>
+<b>E5SslCertificateSelectionDialog</b>(<i>certificates, parent=None</i>)
+<p>
+        Constructor
+</p><dl>
+<dt><i>certificates</i> (list of QSslCertificate)</dt>
+<dd>
+list of SSL certificates to select from
+</dd><dt><i>parent</i> (QWidget)</dt>
+<dd>
+reference to the parent widget
+</dd>
+</dl><a NAME="E5SslCertificateSelectionDialog.__createCaCertificateEntry" ID="E5SslCertificateSelectionDialog.__createCaCertificateEntry"></a>
+<h4>E5SslCertificateSelectionDialog.__createCaCertificateEntry</h4>
+<b>__createCaCertificateEntry</b>(<i>cert</i>)
+<p>
+        Private method to create a certificate entry.
+</p><dl>
+<dt><i>cert</i> (QSslCertificate)</dt>
+<dd>
+certificate to insert
+</dd>
+</dl><a NAME="E5SslCertificateSelectionDialog.__populateCertificatesTree" ID="E5SslCertificateSelectionDialog.__populateCertificatesTree"></a>
+<h4>E5SslCertificateSelectionDialog.__populateCertificatesTree</h4>
+<b>__populateCertificatesTree</b>(<i>certificates</i>)
+<p>
+        Private slot to populate the certificates tree.
+</p><dl>
+<dt><i>certificates</i> (list of QSslCertificate)</dt>
+<dd>
+list of SSL certificates to select from
+</dd>
+</dl><a NAME="E5SslCertificateSelectionDialog.getSelectedCertificate" ID="E5SslCertificateSelectionDialog.getSelectedCertificate"></a>
+<h4>E5SslCertificateSelectionDialog.getSelectedCertificate</h4>
+<b>getSelectedCertificate</b>(<i></i>)
+<p>
+        Public method to get the selected certificate.
+</p><dl>
+<dt>Returns:</dt>
+<dd>
+selected certificate
+</dd>
+</dl><dl>
+<dt>Return Type:</dt>
+<dd>
+QSslCertificate
+</dd>
+</dl><a NAME="E5SslCertificateSelectionDialog.on_certificatesTree_itemSelectionChanged" ID="E5SslCertificateSelectionDialog.on_certificatesTree_itemSelectionChanged"></a>
+<h4>E5SslCertificateSelectionDialog.on_certificatesTree_itemSelectionChanged</h4>
+<b>on_certificatesTree_itemSelectionChanged</b>(<i></i>)
+<p>
+        Private slot to handle the selection of an item.
+</p><a NAME="E5SslCertificateSelectionDialog.on_viewButton_clicked" ID="E5SslCertificateSelectionDialog.on_viewButton_clicked"></a>
+<h4>E5SslCertificateSelectionDialog.on_viewButton_clicked</h4>
+<b>on_viewButton_clicked</b>(<i></i>)
+<p>
+        Private slot to show data of the selected certificate.
+</p>
+<div align="right"><a href="#top">Up</a></div>
+<hr />
+</body></html>
\ No newline at end of file
--- a/Documentation/Source/eric6.MultiProject.AddProjectDialog.html	Thu Jan 10 14:23:49 2019 +0100
+++ b/Documentation/Source/eric6.MultiProject.AddProjectDialog.html	Sat Feb 02 11:12:54 2019 +0100
@@ -79,22 +79,25 @@
 </table>
 <a NAME="AddProjectDialog.__init__" ID="AddProjectDialog.__init__"></a>
 <h4>AddProjectDialog (Constructor)</h4>
-<b>AddProjectDialog</b>(<i>parent=None, startdir=None, project=None, categories=None</i>)
+<b>AddProjectDialog</b>(<i>parent=None, startdir="", project=None, categories=None, category=""</i>)
 <p>
         Constructor
 </p><dl>
-<dt><i>parent</i></dt>
+<dt><i>parent</i> (QWidget)</dt>
 <dd>
-parent widget of this dialog (QWidget)
-</dd><dt><i>startdir</i></dt>
+parent widget of this dialog
+</dd><dt><i>startdir</i> (str)</dt>
 <dd>
-start directory for the selection dialog (string)
-</dd><dt><i>project</i></dt>
+start directory for the selection dialog
+</dd><dt><i>project</i> (dict)</dt>
 <dd>
 dictionary containing project data
-</dd><dt><i>categories</i></dt>
+</dd><dt><i>categories</i> (list of str)</dt>
 <dd>
-list of already used categories (list of string)
+list of already used categories
+</dd><dt><i>category</i> (str)</dt>
+<dd>
+category to be preset
 </dd>
 </dl><a NAME="AddProjectDialog.__updateUi" ID="AddProjectDialog.__updateUi"></a>
 <h4>AddProjectDialog.__updateUi</h4>
--- a/Documentation/Source/eric6.MultiProject.MultiProject.html	Thu Jan 10 14:23:49 2019 +0100
+++ b/Documentation/Source/eric6.MultiProject.MultiProject.html	Sat Feb 02 11:12:54 2019 +0100
@@ -161,6 +161,9 @@
 <td><a href="#MultiProject.closeMultiProject">closeMultiProject</a></td>
 <td>Public slot to close the current multi project.</td>
 </tr><tr>
+<td><a href="#MultiProject.deleteProject">deleteProject</a></td>
+<td>Public slot to delete project(s) from the multi project and disk.</td>
+</tr><tr>
 <td><a href="#MultiProject.getActions">getActions</a></td>
 <td>Public method to get a list of all actions.</td>
 </tr><tr>
@@ -375,13 +378,16 @@
 </dd>
 </dl><a NAME="MultiProject.addNewProject" ID="MultiProject.addNewProject"></a>
 <h4>MultiProject.addNewProject</h4>
-<b>addNewProject</b>(<i>startdir=None</i>)
+<b>addNewProject</b>(<i>startdir="", category=""</i>)
 <p>
         Public slot used to add a new project to the multi-project.
 </p><dl>
-<dt><i>startdir</i></dt>
+<dt><i>startdir</i> (str)</dt>
 <dd>
-start directory for the selection dialog (string)
+start directory for the selection dialog
+</dd><dt><i>category</i> (str)</dt>
+<dd>
+category to be preset
 </dd>
 </dl><a NAME="MultiProject.addProject" ID="MultiProject.addProject"></a>
 <h4>MultiProject.addProject</h4>
@@ -428,6 +434,17 @@
 <dd>
 flag indicating success (boolean)
 </dd>
+</dl><a NAME="MultiProject.deleteProject" ID="MultiProject.deleteProject"></a>
+<h4>MultiProject.deleteProject</h4>
+<b>deleteProject</b>(<i>uid</i>)
+<p>
+        Public slot to delete project(s) from the multi project and disk.
+</p><dl>
+<dt><i>uid</i> (str)</dt>
+<dd>
+UID of the project to be removed from the multi
+            project
+</dd>
 </dl><a NAME="MultiProject.getActions" ID="MultiProject.getActions"></a>
 <h4>MultiProject.getActions</h4>
 <b>getActions</b>(<i></i>)
--- a/Documentation/Source/eric6.MultiProject.MultiProjectBrowser.html	Thu Jan 10 14:23:49 2019 +0100
+++ b/Documentation/Source/eric6.MultiProject.MultiProjectBrowser.html	Sat Feb 02 11:12:54 2019 +0100
@@ -75,6 +75,9 @@
 <td><a href="#MultiProjectBrowser.__createPopupMenu">__createPopupMenu</a></td>
 <td>Private method to create the popup menu.</td>
 </tr><tr>
+<td><a href="#MultiProjectBrowser.__deleteProject">__deleteProject</a></td>
+<td>Private method to handle the Delete context menu entry.</td>
+</tr><tr>
 <td><a href="#MultiProjectBrowser.__findCategoryItem">__findCategoryItem</a></td>
 <td>Private method to find the item for a category.</td>
 </tr><tr>
@@ -173,6 +176,11 @@
 <b>__createPopupMenu</b>(<i></i>)
 <p>
         Private method to create the popup menu.
+</p><a NAME="MultiProjectBrowser.__deleteProject" ID="MultiProjectBrowser.__deleteProject"></a>
+<h4>MultiProjectBrowser.__deleteProject</h4>
+<b>__deleteProject</b>(<i></i>)
+<p>
+        Private method to handle the Delete context menu entry.
 </p><a NAME="MultiProjectBrowser.__findCategoryItem" ID="MultiProjectBrowser.__findCategoryItem"></a>
 <h4>MultiProjectBrowser.__findCategoryItem</h4>
 <b>__findCategoryItem</b>(<i>category</i>)
--- a/Documentation/Source/eric6.Project.CreateDialogCodeDialog.html	Thu Jan 10 14:23:49 2019 +0100
+++ b/Documentation/Source/eric6.Project.CreateDialogCodeDialog.html	Sat Feb 02 11:12:54 2019 +0100
@@ -63,9 +63,6 @@
 <td><a href="#CreateDialogCodeDialog.__className">__className</a></td>
 <td>Private method to get the class name of a form.</td>
 </tr><tr>
-<td><a href="#CreateDialogCodeDialog.__classNameExternal">__classNameExternal</a></td>
-<td>Private method to get the class name of a form via an external interpreter.</td>
-</tr><tr>
 <td><a href="#CreateDialogCodeDialog.__generateCode">__generateCode</a></td>
 <td>Private slot to generate the code as requested by the user.</td>
 </tr><tr>
@@ -78,9 +75,6 @@
 <td><a href="#CreateDialogCodeDialog.__objectName">__objectName</a></td>
 <td>Private method to get the object name of a form.</td>
 </tr><tr>
-<td><a href="#CreateDialogCodeDialog.__objectNameExternal">__objectNameExternal</a></td>
-<td>Private method to get the object name of a form via an external interpreter.</td>
-</tr><tr>
 <td><a href="#CreateDialogCodeDialog.__runUicLoadUi">__runUicLoadUi</a></td>
 <td>Private method to run the UicLoadUi.py script with the given command and return the output.</td>
 </tr><tr>
@@ -90,9 +84,6 @@
 <td><a href="#CreateDialogCodeDialog.__updateSlotsModel">__updateSlotsModel</a></td>
 <td>Private slot to update the slots tree display.</td>
 </tr><tr>
-<td><a href="#CreateDialogCodeDialog.__updateSlotsModelExternal">__updateSlotsModelExternal</a></td>
-<td>Private slot to update the slots tree display getting the data via an external interpreter.</td>
-</tr><tr>
 <td><a href="#CreateDialogCodeDialog.initError">initError</a></td>
 <td>Public method to determine, if there was an initialzation error.</td>
 </tr><tr>
@@ -144,22 +135,6 @@
 <dd>
 str
 </dd>
-</dl><a NAME="CreateDialogCodeDialog.__classNameExternal" ID="CreateDialogCodeDialog.__classNameExternal"></a>
-<h4>CreateDialogCodeDialog.__classNameExternal</h4>
-<b>__classNameExternal</b>(<i></i>)
-<p>
-        Private method to get the class name of a form via an external
-        interpreter.
-</p><dl>
-<dt>Returns:</dt>
-<dd>
-class name
-</dd>
-</dl><dl>
-<dt>Return Type:</dt>
-<dd>
-str
-</dd>
 </dl><a NAME="CreateDialogCodeDialog.__generateCode" ID="CreateDialogCodeDialog.__generateCode"></a>
 <h4>CreateDialogCodeDialog.__generateCode</h4>
 <b>__generateCode</b>(<i></i>)
@@ -201,22 +176,6 @@
 <dd>
 str
 </dd>
-</dl><a NAME="CreateDialogCodeDialog.__objectNameExternal" ID="CreateDialogCodeDialog.__objectNameExternal"></a>
-<h4>CreateDialogCodeDialog.__objectNameExternal</h4>
-<b>__objectNameExternal</b>(<i></i>)
-<p>
-        Private method to get the object name of a form via an external
-        interpreter.
-</p><dl>
-<dt>Returns:</dt>
-<dd>
-object name
-</dd>
-</dl><dl>
-<dt>Return Type:</dt>
-<dd>
-str
-</dd>
 </dl><a NAME="CreateDialogCodeDialog.__runUicLoadUi" ID="CreateDialogCodeDialog.__runUicLoadUi"></a>
 <h4>CreateDialogCodeDialog.__runUicLoadUi</h4>
 <b>__runUicLoadUi</b>(<i>command</i>)
@@ -253,12 +212,6 @@
 <b>__updateSlotsModel</b>(<i></i>)
 <p>
         Private slot to update the slots tree display.
-</p><a NAME="CreateDialogCodeDialog.__updateSlotsModelExternal" ID="CreateDialogCodeDialog.__updateSlotsModelExternal"></a>
-<h4>CreateDialogCodeDialog.__updateSlotsModelExternal</h4>
-<b>__updateSlotsModelExternal</b>(<i></i>)
-<p>
-        Private slot to update the slots tree display getting the data via an
-        external interpreter.
 </p><a NAME="CreateDialogCodeDialog.initError" ID="CreateDialogCodeDialog.initError"></a>
 <h4>CreateDialogCodeDialog.initError</h4>
 <b>initError</b>(<i></i>)
--- a/Documentation/Source/eric6.UI.Info.html	Thu Jan 10 14:23:49 2019 +0100
+++ b/Documentation/Source/eric6.UI.Info.html	Sat Feb 02 11:12:54 2019 +0100
@@ -25,7 +25,7 @@
 </p>
 <h3>Global Attributes</h3>
 <table>
-<tr><td>BugAddress</td></tr><tr><td>Copyright</td></tr><tr><td>FeatureAddress</td></tr><tr><td>Homepage</td></tr><tr><td>Program</td></tr><tr><td>Version</td></tr><tr><td>VersionOnly</td></tr>
+<tr><td>BugAddress</td></tr><tr><td>Copyright</td></tr><tr><td>CopyrightShort</td></tr><tr><td>FeatureAddress</td></tr><tr><td>Homepage</td></tr><tr><td>Program</td></tr><tr><td>Version</td></tr><tr><td>VersionOnly</td></tr>
 </table>
 <h3>Classes</h3>
 <table>
--- a/Documentation/Source/eric6.Utilities.ClassBrowsers.jsclbr.html	Thu Jan 10 14:23:49 2019 +0100
+++ b/Documentation/Source/eric6.Utilities.ClassBrowsers.jsclbr.html	Sat Feb 02 11:12:54 2019 +0100
@@ -287,7 +287,7 @@
 </p><dl>
 <dt><i>node</i></dt>
 <dd>
-reference to the node (jasy.js.parse.Node.Node)
+reference to the node (jasy.script.parse.Node.Node)
 </dd>
 </dl><a NAME="Visitor.visit_function" ID="Visitor.visit_function"></a>
 <h4>Visitor.visit_function</h4>
@@ -297,7 +297,7 @@
 </p><dl>
 <dt><i>node</i></dt>
 <dd>
-reference to the node (jasy.js.parse.Node.Node)
+reference to the node (jasy.script.parse.Node.Node)
 </dd>
 </dl><a NAME="Visitor.visit_noop" ID="Visitor.visit_noop"></a>
 <h4>Visitor.visit_noop</h4>
@@ -307,7 +307,7 @@
 </p><dl>
 <dt><i>node</i></dt>
 <dd>
-reference to the node (jasy.js.parse.Node.Node)
+reference to the node (jasy.script.parse.Node.Node)
 </dd>
 </dl><a NAME="Visitor.visit_property_init" ID="Visitor.visit_property_init"></a>
 <h4>Visitor.visit_property_init</h4>
@@ -317,7 +317,7 @@
 </p><dl>
 <dt><i>node</i></dt>
 <dd>
-reference to the node (jasy.js.parse.Node.Node)
+reference to the node (jasy.script.parse.Node.Node)
 </dd>
 </dl><a NAME="Visitor.visit_var" ID="Visitor.visit_var"></a>
 <h4>Visitor.visit_var</h4>
@@ -327,7 +327,7 @@
 </p><dl>
 <dt><i>node</i></dt>
 <dd>
-reference to the node (jasy.js.parse.Node.Node)
+reference to the node (jasy.script.parse.Node.Node)
 </dd>
 </dl>
 <div align="right"><a href="#top">Up</a></div>
--- a/Documentation/Source/eric6.WebBrowser.Tools.Scripts.html	Thu Jan 10 14:23:49 2019 +0100
+++ b/Documentation/Source/eric6.WebBrowser.Tools.Scripts.html	Sat Feb 02 11:12:54 2019 +0100
@@ -52,6 +52,9 @@
 <td><a href="#getOpenSearchLinks">getOpenSearchLinks</a></td>
 <td>Function generating a script to extract all open search links.</td>
 </tr><tr>
+<td><a href="#scrollToAnchor">scrollToAnchor</a></td>
+<td>Function generating script to scroll to a given anchor.</td>
+</tr><tr>
 <td><a href="#sendPostData">sendPostData</a></td>
 <td>Function generating a script to send Post data.</td>
 </tr><tr>
@@ -190,6 +193,29 @@
 </dl>
 <div align="right"><a href="#top">Up</a></div>
 <hr /><hr />
+<a NAME="scrollToAnchor" ID="scrollToAnchor"></a>
+<h2>scrollToAnchor</h2>
+<b>scrollToAnchor</b>(<i>anchor</i>)
+<p>
+    Function generating script to scroll to a given anchor.
+</p><dl>
+<dt><i>anchor</i> (str)</dt>
+<dd>
+name of the anchor to scroll to
+</dd>
+</dl><dl>
+<dt>Returns:</dt>
+<dd>
+script to set the style sheet
+</dd>
+</dl><dl>
+<dt>Return Type:</dt>
+<dd>
+str
+</dd>
+</dl>
+<div align="right"><a href="#top">Up</a></div>
+<hr /><hr />
 <a NAME="sendPostData" ID="sendPostData"></a>
 <h2>sendPostData</h2>
 <b>sendPostData</b>(<i>url, data</i>)
--- a/Documentation/Source/eric6.WebBrowser.WebBrowserPage.html	Thu Jan 10 14:23:49 2019 +0100
+++ b/Documentation/Source/eric6.WebBrowser.WebBrowserPage.html	Sat Feb 02 11:12:54 2019 +0100
@@ -49,10 +49,10 @@
 <dd>
 emitted
         to signal an accepted navigation request
-</dd><dt>printRequested()</dt>
+</dd><dt>printPageRequested()</dt>
 <dd>
-emitted to indicate a print request of the shown
-        web page
+emitted to indicate a print request of the
+        shown web page
 </dd><dt>safeBrowsingAbort()</dt>
 <dd>
 emitted to indicate an abort due to a safe
@@ -88,6 +88,9 @@
 <td><a href="#WebBrowserPage.__init__">WebBrowserPage</a></td>
 <td>Constructor</td>
 </tr><tr>
+<td><a href="#WebBrowserPage.__contentsSizeChanged">__contentsSizeChanged</a></td>
+<td>Private slot to work around QWebEnginePage not scrolling to anchors when opened in a background tab.</td>
+</tr><tr>
 <td><a href="#WebBrowserPage.__featurePermissionRequested">__featurePermissionRequested</a></td>
 <td>Private slot handling a feature permission request.</td>
 </tr><tr>
@@ -210,6 +213,17 @@
 <dd>
 parent widget of this window (QWidget)
 </dd>
+</dl><a NAME="WebBrowserPage.__contentsSizeChanged" ID="WebBrowserPage.__contentsSizeChanged"></a>
+<h4>WebBrowserPage.__contentsSizeChanged</h4>
+<b>__contentsSizeChanged</b>(<i>size</i>)
+<p>
+        Private slot to work around QWebEnginePage not scrolling to anchors
+        when opened in a background tab.
+</p><dl>
+<dt><i>size</i> (QSize)</dt>
+<dd>
+changed contents size (unused)
+</dd>
 </dl><a NAME="WebBrowserPage.__featurePermissionRequested" ID="WebBrowserPage.__featurePermissionRequested"></a>
 <h4>WebBrowserPage.__featurePermissionRequested</h4>
 <b>__featurePermissionRequested</b>(<i>url, feature</i>)
--- a/Documentation/Source/eric6.WebBrowser.WebBrowserView.html	Thu Jan 10 14:23:49 2019 +0100
+++ b/Documentation/Source/eric6.WebBrowser.WebBrowserView.html	Sat Feb 02 11:12:54 2019 +0100
@@ -233,6 +233,9 @@
 <td><a href="#WebBrowserView.__searchRequested">__searchRequested</a></td>
 <td>Private slot to search for some text with a selected search engine.</td>
 </tr><tr>
+<td><a href="#WebBrowserView.__selectClientCertificate">__selectClientCertificate</a></td>
+<td>Private slot to handle the client certificate selection request.</td>
+</tr><tr>
 <td><a href="#WebBrowserView.__sendLink">__sendLink</a></td>
 <td>Private slot to send a link via email.</td>
 </tr><tr>
@@ -903,6 +906,17 @@
 <dd>
 reference to the action that triggered this slot (QAction)
 </dd>
+</dl><a NAME="WebBrowserView.__selectClientCertificate" ID="WebBrowserView.__selectClientCertificate"></a>
+<h4>WebBrowserView.__selectClientCertificate</h4>
+<b>__selectClientCertificate</b>(<i>clientCertificateSelection</i>)
+<p>
+            Private slot to handle the client certificate selection request.
+</p><dl>
+<dt><i>clientCertificateSelection</i> (QWebEngineClientCertificateSelection)</dt>
+<dd>
+list of client SSL certificates
+                found in system's client certificate store
+</dd>
 </dl><a NAME="WebBrowserView.__sendLink" ID="WebBrowserView.__sendLink"></a>
 <h4>WebBrowserView.__sendLink</h4>
 <b>__sendLink</b>(<i>act</i>)
--- a/Documentation/Source/eric6.install.html	Thu Jan 10 14:23:49 2019 +0100
+++ b/Documentation/Source/eric6.install.html	Sat Feb 02 11:12:54 2019 +0100
@@ -25,7 +25,7 @@
 </p>
 <h3>Global Attributes</h3>
 <table>
-<tr><td>BlackLists</td></tr><tr><td>PlatformsBlackLists</td></tr><tr><td>PythonMarkers</td></tr><tr><td>PythonTextMarkers</td></tr><tr><td>apisDir</td></tr><tr><td>cfg</td></tr><tr><td>configLength</td></tr><tr><td>configName</td></tr><tr><td>currDir</td></tr><tr><td>defaultMacAppBundleName</td></tr><tr><td>defaultMacAppBundlePath</td></tr><tr><td>defaultMacPythonExe</td></tr><tr><td>distDir</td></tr><tr><td>doCleanup</td></tr><tr><td>doCompile</td></tr><tr><td>includePythonVariant</td></tr><tr><td>installApis</td></tr><tr><td>macAppBundleName</td></tr><tr><td>macAppBundlePath</td></tr><tr><td>macPythonExe</td></tr><tr><td>modDir</td></tr><tr><td>platBinDir</td></tr><tr><td>platBinDirOld</td></tr><tr><td>progLanguages</td></tr><tr><td>progName</td></tr><tr><td>pyModDir</td></tr><tr><td>pyqtOverride</td></tr><tr><td>pyqtVariant</td></tr><tr><td>sourceDir</td></tr>
+<tr><td>BlackLists</td></tr><tr><td>PlatformsBlackLists</td></tr><tr><td>PythonMarkers</td></tr><tr><td>PythonTextMarkers</td></tr><tr><td>apisDir</td></tr><tr><td>cfg</td></tr><tr><td>configLength</td></tr><tr><td>configName</td></tr><tr><td>currDir</td></tr><tr><td>defaultMacAppBundleName</td></tr><tr><td>defaultMacAppBundlePath</td></tr><tr><td>defaultMacPythonExe</td></tr><tr><td>distDir</td></tr><tr><td>doCleanDesktopLinks</td></tr><tr><td>doCleanup</td></tr><tr><td>doCompile</td></tr><tr><td>forceCleanDesktopLinks</td></tr><tr><td>includePythonVariant</td></tr><tr><td>installApis</td></tr><tr><td>macAppBundleName</td></tr><tr><td>macAppBundlePath</td></tr><tr><td>macPythonExe</td></tr><tr><td>modDir</td></tr><tr><td>platBinDir</td></tr><tr><td>platBinDirOld</td></tr><tr><td>progLanguages</td></tr><tr><td>progName</td></tr><tr><td>pyModDir</td></tr><tr><td>pyqtOverride</td></tr><tr><td>pyqtVariant</td></tr><tr><td>sourceDir</td></tr>
 </table>
 <h3>Classes</h3>
 <table>
--- a/Documentation/Source/index-eric6.E5Network.html	Thu Jan 10 14:23:49 2019 +0100
+++ b/Documentation/Source/index-eric6.E5Network.html	Sat Feb 02 11:12:54 2019 +0100
@@ -56,6 +56,9 @@
 <td><a href="eric6.E5Network.E5RFC6266.html">E5RFC6266</a></td>
 <td>Module implementing a Content-Disposition parser iaw.</td>
 </tr><tr>
+<td><a href="eric6.E5Network.E5SslCertificateSelectionDialog.html">E5SslCertificateSelectionDialog</a></td>
+<td>Module implementing a dialog to select a SSL certificate.</td>
+</tr><tr>
 <td><a href="eric6.E5Network.E5SslCertificatesDialog.html">E5SslCertificatesDialog</a></td>
 <td>Module implementing a dialog to show and edit all certificates.</td>
 </tr><tr>
--- a/DocumentationTools/QtHelpGenerator.py	Thu Jan 10 14:23:49 2019 +0100
+++ b/DocumentationTools/QtHelpGenerator.py	Sat Feb 02 11:12:54 2019 +0100
@@ -16,7 +16,7 @@
 import subprocess
 
 from Utilities import joinext, relpath, html_encode, getQtBinariesPath, \
-    generateQtToolName
+    generateQtToolName, isExecutable
 
 HelpCollection = r"""<?xml version="1.0" encoding="utf-8" ?>
 <QHelpCollectionProject version="1.0">
@@ -270,23 +270,30 @@
         
         cwd = os.getcwd()
         # generate the compressed files
+        qhelpgeneratorExe = os.path.join(
+            getQtBinariesPath(), generateQtToolName("qhelpgenerator")
+        )
         shutil.copy(
             os.path.join(self.outputDir, HelpProjectFile), self.htmlDir)
         os.chdir(self.htmlDir)
         subprocess.call([
-            os.path.join(getQtBinariesPath(),
-                         generateQtToolName("qhelpgenerator")),
+            qhelpgeneratorExe,
             HelpProjectFile, "-o", os.path.join(self.outputDir, HelpHelpFile)])
         os.remove(HelpProjectFile)
         
         if self.createCollection:
+            qcollectiongeneratorExe = os.path.join(
+                getQtBinariesPath(), generateQtToolName("qcollectiongenerator")
+            )
+            if not isExecutable(qcollectiongeneratorExe):
+                # assume Qt >= 5.12.0
+                qcollectiongeneratorExe = qhelpgeneratorExe
             sys.stdout.write("Generating QtHelp collection...\n")
             sys.stdout.flush()
             sys.stderr.flush()
             os.chdir(self.outputDir)
             subprocess.call([
-                os.path.join(getQtBinariesPath(),
-                             generateQtToolName("qcollectiongenerator")),
+                qcollectiongeneratorExe,
                 HelpCollectionProjectFile, "-o", HelpCollectionFile])
         
         os.chdir(cwd)
--- a/E5Gui/E5PathPicker.py	Thu Jan 10 14:23:49 2019 +0100
+++ b/E5Gui/E5PathPicker.py	Sat Feb 02 11:12:54 2019 +0100
@@ -16,7 +16,7 @@
 except ImportError:
     from ThirdParty.enum import Enum
 
-from PyQt5.QtCore import pyqtSignal, Qt, QFileInfo, QCoreApplication
+from PyQt5.QtCore import pyqtSignal, Qt, QFileInfo, QCoreApplication, QDir
 from PyQt5.QtWidgets import QWidget, QHBoxLayout, QToolButton, QSizePolicy
 
 from . import E5FileDialog
@@ -25,7 +25,6 @@
 from .E5ComboBox import E5ClearableComboBox
 
 import UI.PixmapCache
-import Utilities
 
 
 class E5PathPickerModes(Enum):
@@ -129,7 +128,7 @@
         @type str
         """
         if self._completer and not self._completer.popup().isVisible():
-            self._completer.setRootPath(Utilities.toNativeSeparators(path))
+            self._completer.setRootPath(QDir.toNativeSeparators(path))
     
     def setMode(self, mode):
         """
@@ -246,7 +245,7 @@
             self._setEditorText(path)
         else:
             if toNative:
-                path = Utilities.toNativeSeparators(path)
+                path = QDir.toNativeSeparators(path)
             self._setEditorText(path)
             if self._completer:
                 self._completer.setRootPath(path)
@@ -264,14 +263,14 @@
         if self.__mode == E5PathPickerModes.OpenFilesMode:
             if toNative:
                 return ";".join(
-                    [Utilities.toNativeSeparators(path)
+                    [QDir.toNativeSeparators(path)
                      for path in self._editorText().split(";")])
             else:
                 return self._editorText()
         else:
             if toNative:
                 return os.path.expanduser(
-                    Utilities.toNativeSeparators(self._editorText()))
+                    QDir.toNativeSeparators(self._editorText()))
             else:
                 return os.path.expanduser(self._editorText())
     
@@ -520,7 +519,7 @@
             directory = os.path.expanduser(directory)
         if not os.path.isabs(directory) and self.__defaultDirectory:
             directory = os.path.join(self.__defaultDirectory, directory)
-        directory = Utilities.fromNativeSeparators(directory)
+        directory = QDir.fromNativeSeparators(directory)
         
         if self.__mode == E5PathPickerModes.OpenFileMode:
             path = E5FileDialog.getOpenFileName(
@@ -528,14 +527,14 @@
                 windowTitle,
                 directory,
                 self.__filters)
-            path = Utilities.toNativeSeparators(path)
+            path = QDir.toNativeSeparators(path)
         elif self.__mode == E5PathPickerModes.OpenFilesMode:
             paths = E5FileDialog.getOpenFileNames(
                 self,
                 windowTitle,
                 directory,
                 self.__filters)
-            path = ";".join([Utilities.toNativeSeparators(path)
+            path = ";".join([QDir.toNativeSeparators(path)
                              for path in paths])
         elif self.__mode == E5PathPickerModes.SaveFileMode:
             path = E5FileDialog.getSaveFileName(
@@ -544,7 +543,7 @@
                 directory,
                 self.__filters,
                 E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite))
-            path = Utilities.toNativeSeparators(path)
+            path = QDir.toNativeSeparators(path)
         elif self.__mode == E5PathPickerModes.SaveFileEnsureExtensionMode:
             path, selectedFilter = E5FileDialog.getSaveFileNameAndFilter(
                 self,
@@ -553,7 +552,7 @@
                 self.__filters,
                 None,
                 E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite))
-            path = Utilities.toNativeSeparators(path)
+            path = QDir.toNativeSeparators(path)
             if path:
                 ext = QFileInfo(path).suffix()
                 if not ext:
@@ -566,14 +565,14 @@
                 windowTitle,
                 directory,
                 self.__filters)
-            path = Utilities.toNativeSeparators(path)
+            path = QDir.toNativeSeparators(path)
         elif self.__mode == E5PathPickerModes.DirectoryMode:
             path = E5FileDialog.getExistingDirectory(
                 self,
                 windowTitle,
                 directory,
                 E5FileDialog.Options(E5FileDialog.ShowDirsOnly))
-            path = Utilities.toNativeSeparators(path)
+            path = QDir.toNativeSeparators(path)
             while path.endswith(os.sep):
                 path = path[:-1]
         elif self.__mode == E5PathPickerModes.DirectoryShowFilesMode:
@@ -582,7 +581,7 @@
                 windowTitle,
                 directory,
                 E5FileDialog.Options(E5FileDialog.DontUseNativeDialog))
-            path = Utilities.toNativeSeparators(path)
+            path = QDir.toNativeSeparators(path)
             while path.endswith(os.sep):
                 path = path[:-1]
         
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/E5Network/E5SslCertificateSelectionDialog.py	Sat Feb 02 11:12:54 2019 +0100
@@ -0,0 +1,149 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2019 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog to select a SSL certificate.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSlot, Qt
+from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QTreeWidgetItem
+try:
+    from PyQt5.QtNetwork import QSslCertificate
+except ImportError:
+    pass
+
+from .Ui_E5SslCertificateSelectionDialog import \
+    Ui_E5SslCertificateSelectionDialog
+
+import Utilities
+import UI.PixmapCache
+from Globals import qVersionTuple
+
+
+class E5SslCertificateSelectionDialog(QDialog,
+                                      Ui_E5SslCertificateSelectionDialog):
+    """
+    Class implementing a dialog to select a SSL certificate.
+    """
+    CertRole = Qt.UserRole + 1
+    
+    def __init__(self, certificates, parent=None):
+        """
+        Constructor
+        
+        @param certificates list of SSL certificates to select from
+        @type list of QSslCertificate
+        @param parent reference to the parent widget
+        @type QWidget
+        """
+        super(E5SslCertificateSelectionDialog, self).__init__(parent)
+        self.setupUi(self)
+        
+        self.viewButton.setIcon(
+            UI.PixmapCache.getIcon("certificates.png"))
+        
+        self.buttonBox.button(QDialogButtonBox.OK).setEnabled(False)
+        self.viewButton.setEnabled(False)
+        
+        self.__populateCertificatesTree(certificates)
+    
+    def __populateCertificatesTree(self, certificates):
+        """
+        Private slot to populate the certificates tree.
+        
+        @param certificates list of SSL certificates to select from
+        @type list of QSslCertificate
+        """
+        for cert in certificates():
+            self.__createCertificateEntry(cert)
+        
+        self.certificatesTree.expandAll()
+        for i in range(self.certificatesTree.columnCount()):
+            self.certificatesTree.resizeColumnToContents(i)
+        self.certificatesTree.sortItems(0, Qt.AscendingOrder)
+    
+    def __createCaCertificateEntry(self, cert):
+        """
+        Private method to create a certificate entry.
+        
+        @param cert certificate to insert
+        @type QSslCertificate
+        """
+        # step 1: extract the info to be shown
+        if qVersionTuple() >= (5, 0, 0):
+            organisation = Utilities.decodeString(
+                ", ".join(cert.subjectInfo(QSslCertificate.Organization)))
+            commonName = Utilities.decodeString(
+                ", ".join(cert.subjectInfo(QSslCertificate.CommonName)))
+        else:
+            organisation = Utilities.decodeString(
+                cert.subjectInfo(QSslCertificate.Organization))
+            commonName = Utilities.decodeString(
+                cert.subjectInfo(QSslCertificate.CommonName))
+        if organisation is None or organisation == "":
+            organisation = self.tr("(Unknown)")
+        if commonName is None or commonName == "":
+            commonName = self.tr("(Unknown common name)")
+        expiryDate = cert.expiryDate().toString("yyyy-MM-dd")
+        
+        # step 2: create the entry
+        items = self.certificatesTree.findItems(
+            organisation,
+            Qt.MatchFixedString | Qt.MatchCaseSensitive)
+        if len(items) == 0:
+            parent = QTreeWidgetItem(self.certificatesTree, [organisation])
+            parent.setFirstColumnSpanned(True)
+        else:
+            parent = items[0]
+        
+        itm = QTreeWidgetItem(parent, [commonName, expiryDate])
+        itm.setData(0, self.CertRole, cert.toPem())
+    
+    @pyqtSlot()
+    def on_certificatesTree_itemSelectionChanged(self):
+        """
+        Private slot to handle the selection of an item.
+        """
+        enable = len(self.certificatesTree.selectedItems()) > 0 and \
+            self.certificatesTree.selectedItems()[0].parent() is not None
+        self.buttonBox.button(QDialogButtonBox.OK).setEnabled(enable)
+        self.viewButton.setEnabled(enable)
+    
+    @pyqtSlot()
+    def on_viewButton_clicked(self):
+        """
+        Private slot to show data of the selected certificate.
+        """
+        try:
+            from E5Network.E5SslCertificatesInfoDialog import \
+                E5SslCertificatesInfoDialog
+            cert = QSslCertificate.fromData(
+                self.certificatesTree.selectedItems()[0].data(
+                    0, self.CertRole))
+            dlg = E5SslCertificatesInfoDialog(cert, self)
+            dlg.exec_()
+        except ImportError:
+            pass
+    
+    def getSelectedCertificate(self):
+        """
+        Public method to get the selected certificate.
+        
+        @return selected certificate
+        @rtype QSslCertificate
+        """
+        valid = len(self.certificatesTree.selectedItems()) > 0 and \
+            self.certificatesTree.selectedItems()[0].parent() is not None
+        
+        if valid:
+            certificate = QSslCertificate.fromData(
+                self.certificatesTree.selectedItems()[0].data(
+                    0, self.CertRole))
+        else:
+            certificate = None
+        
+        return certificate
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/E5Network/E5SslCertificateSelectionDialog.ui	Sat Feb 02 11:12:54 2019 +0100
@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>E5SslCertificateSelectionDialog</class>
+ <widget class="QDialog" name="E5SslCertificateSelectionDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>760</width>
+    <height>440</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>SSL Certificate Selection</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QLabel" name="label">
+     <property name="text">
+      <string>Select a SSL certificate:</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QTreeWidget" name="certificatesTree">
+     <column>
+      <property name="text">
+       <string>Certificate name</string>
+      </property>
+     </column>
+     <column>
+      <property name="text">
+       <string>Expiry Date</string>
+      </property>
+     </column>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <widget class="QPushButton" name="viewButton">
+       <property name="enabled">
+        <bool>false</bool>
+       </property>
+       <property name="toolTip">
+        <string>Press to view the selected certificate</string>
+       </property>
+       <property name="text">
+        <string>&amp;View...</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="horizontalSpacer_2">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>E5SslCertificateSelectionDialog</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>E5SslCertificateSelectionDialog</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>
--- a/E5Network/E5SslCertificatesDialog.py	Thu Jan 10 14:23:49 2019 +0100
+++ b/E5Network/E5SslCertificatesDialog.py	Sat Feb 02 11:12:54 2019 +0100
@@ -110,6 +110,7 @@
         if len(items) == 0:
             parent = QTreeWidgetItem(
                 self.serversCertificatesTree, [organisation])
+            parent.setFirstColumnSpanned(True)
         else:
             parent = items[0]
         
@@ -321,6 +322,7 @@
             Qt.MatchFixedString | Qt.MatchCaseSensitive)
         if len(items) == 0:
             parent = QTreeWidgetItem(self.caCertificatesTree, [organisation])
+            parent.setFirstColumnSpanned(True)
         else:
             parent = items[0]
         
--- a/MultiProject/AddProjectDialog.py	Thu Jan 10 14:23:49 2019 +0100
+++ b/MultiProject/AddProjectDialog.py	Sat Feb 02 11:12:54 2019 +0100
@@ -25,15 +25,21 @@
     """
     Class implementing the add project dialog.
     """
-    def __init__(self, parent=None, startdir=None, project=None,
-                 categories=None):
+    def __init__(self, parent=None, startdir="", project=None,
+                 categories=None, category=""):
         """
         Constructor
         
-        @param parent parent widget of this dialog (QWidget)
-        @param startdir start directory for the selection dialog (string)
+        @param parent parent widget of this dialog
+        @type QWidget
+        @param startdir start directory for the selection dialog
+        @type str
         @param project dictionary containing project data
-        @param categories list of already used categories (list of string)
+        @type dict
+        @param categories list of already used categories
+        @type list of str
+        @param category category to be preset
+        @type str
         """
         super(AddProjectDialog, self).__init__(parent)
         self.setupUi(self)
@@ -44,6 +50,7 @@
         if categories:
             self.categoryComboBox.addItem("")
             self.categoryComboBox.addItems(sorted(categories))
+        self.categoryComboBox.setEditText(category)
         
         self.startdir = startdir
         self.uid = ""
--- a/MultiProject/MultiProject.py	Thu Jan 10 14:23:49 2019 +0100
+++ b/MultiProject/MultiProject.py	Sat Feb 02 11:12:54 2019 +0100
@@ -10,6 +10,7 @@
 from __future__ import unicode_literals
 
 import os
+import shutil
 
 from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QFileInfo, QFile, \
     QIODevice, QObject
@@ -303,11 +304,14 @@
         self.__projects[project['uid']] = project
     
     @pyqtSlot()
-    def addNewProject(self, startdir=None):
+    def addNewProject(self, startdir="", category=""):
         """
         Public slot used to add a new project to the multi-project.
         
-        @param startdir start directory for the selection dialog (string)
+        @param startdir start directory for the selection dialog
+        @type str
+        @param category category to be preset
+        @type str
         """
         from .AddProjectDialog import AddProjectDialog
         if not startdir:
@@ -315,7 +319,7 @@
         if not startdir:
             startdir = Preferences.getMultiProject("Workspace")
         dlg = AddProjectDialog(self.ui, startdir=startdir,
-                               categories=self.categories)
+                               categories=self.categories, category=category)
         if dlg.exec_() == QDialog.Accepted:
             name, filename, isMaster, description, category, uid = \
                 dlg.getData()
@@ -414,6 +418,21 @@
             self.projectRemoved.emit(project)
             self.setDirty(True)
     
+    def deleteProject(self, uid):
+        """
+        Public slot to delete project(s) from the multi project and disk.
+        
+        @param uid UID of the project to be removed from the multi
+            project
+        @type str
+        """
+        if uid in self.__projects:
+            project = self.__projects[uid]
+            projectPath = os.path.dirname(project["file"])
+            shutil.rmtree(projectPath, True)
+            
+            self.removeProject(uid)
+    
     def __newMultiProject(self):
         """
         Private slot to build a new multi project.
--- a/MultiProject/MultiProjectBrowser.py	Thu Jan 10 14:23:49 2019 +0100
+++ b/MultiProject/MultiProjectBrowser.py	Sat Feb 02 11:12:54 2019 +0100
@@ -9,14 +9,19 @@
 
 from __future__ import unicode_literals
 
+import os
+import glob
+
 from PyQt5.QtCore import Qt
 from PyQt5.QtWidgets import QTreeWidget, QTreeWidgetItem, QDialog, QMenu
 
 from E5Gui.E5Application import e5App
+from E5Gui import E5MessageBox
 
 import UI.PixmapCache
 
 
+# TODO: add action to copy a project on disk
 class MultiProjectBrowser(QTreeWidget):
     """
     Class implementing the multi project browser.
@@ -291,6 +296,65 @@
             if uid:
                 self.multiProject.removeProject(uid)
     
+    def __deleteProject(self):
+        """
+        Private method to handle the Delete context menu entry.
+        """
+        itm = self.currentItem()
+        if itm is not None and itm.parent() is not None:
+            projectFile = itm.data(0, MultiProjectBrowser.ProjectFileNameRole)
+            projectPath = os.path.dirname(projectFile)
+            
+            if self.project.getProjectPath() == projectPath:
+                E5MessageBox.warning(
+                    self,
+                    self.tr("Delete Project"),
+                    self.tr("""The current project cannot be deleted."""
+                            """ Please close it first."""))
+            else:
+                projectFiles = glob.glob(os.path.join(projectPath, "*.e4p"))
+                if not projectFiles:
+                    # Oops, that should not happen; play it save
+                    res = False
+                elif len(projectFiles) == 1:
+                    res = E5MessageBox.yesNo(
+                        self,
+                        self.tr("Delete Project"),
+                        self.tr("""<p>Shall the project <b>{0}</b> (Path:"""
+                                """ {1}) really be deleted?</p>""").format(
+                            itm.text(0), projectPath))
+                else:
+                    res = E5MessageBox.yesNo(
+                        self,
+                        self.tr("Delete Project"),
+                        self.tr("""<p>Shall the project <b>{0}</b> (Path:"""
+                                """ {1}) really be deleted?</p>"""
+                                """<p><b>Warning:</b> It contains <b>{2}</b>"""
+                                """ sub-projects.</p>""").format(
+                            itm.text(0), projectPath, len(projectFiles)))
+                if res:
+                    for subprojectFile in projectFiles:
+                        # remove all sub-projects before deleting the directory
+                        if subprojectFile != projectFile:
+                            projectData = {
+                                'name': "",
+                                'file': subprojectFile,
+                                'master': False,
+                                'description': "",
+                                'category': "",
+                                'uid': "",
+                            }
+                            pitm = self.__findProjectItem(projectData)
+                            if pitm:
+                                uid = pitm.data(
+                                    0, MultiProjectBrowser.ProjectUidRole)
+                                if uid:
+                                    self.multiProject.removeProject(uid)
+                        
+                    uid = itm.data(0, MultiProjectBrowser.ProjectUidRole)
+                    if uid:
+                        self.multiProject.deleteProject(uid)
+    
     def __showProjectProperties(self):
         """
         Private method to show the data of a project entry.
@@ -322,7 +386,16 @@
         """
         Private method to add a new project entry.
         """
-        self.multiProject.addNewProject()
+        itm = self.currentItem()
+        if itm is not None:
+            if itm.parent() is None:
+                # current item is a category item
+                category = itm.text(0)
+            else:
+                category = itm.parent().text(0)
+        else:
+            category = ""
+        self.multiProject.addNewProject(category=category)
     
     def __createPopupMenu(self):
         """
@@ -330,7 +403,10 @@
         """
         self.__menu = QMenu(self)
         self.__menu.addAction(self.tr("Open"), self.__openItem)
-        self.__menu.addAction(self.tr("Remove"), self.__removeProject)
+        self.__menu.addAction(self.tr("Remove from Multi Project"),
+                              self.__removeProject)
+        self.__menu.addAction(self.tr("Delete from Disk"),
+                              self.__deleteProject)
         self.__menu.addAction(self.tr("Properties"),
                               self.__showProjectProperties)
         self.__menu.addSeparator()
--- a/Plugins/CheckerPlugins/SyntaxChecker/jsCheckSyntax.py	Thu Jan 10 14:23:49 2019 +0100
+++ b/Plugins/CheckerPlugins/SyntaxChecker/jsCheckSyntax.py	Sat Feb 02 11:12:54 2019 +0100
@@ -172,8 +172,8 @@
             (file name, line number, column, codestring (only at syntax
             errors), the message, a list with arguments for the message)
     """
-    import jasy.js.parse.Parser as jsParser
-    import jasy.js.tokenize.Tokenizer as jsTokenizer
+    import jasy.script.parse.Parser as jsParser
+    import jasy.script.tokenize.Tokenizer as jsTokenizer
     
     codestring = normalizeCode(codestring)
     
--- a/Plugins/PluginEricdoc.py	Thu Jan 10 14:23:49 2019 +0100
+++ b/Plugins/PluginEricdoc.py	Sat Feb 02 11:12:54 2019 +0100
@@ -72,7 +72,10 @@
     })
     
     # 2. Qt Help Generator
-    exe = os.path.join(Utilities.getQtBinariesPath(), 'qhelpgenerator')
+    exe = os.path.join(
+        Utilities.getQtBinariesPath(),
+        Utilities.generateQtToolName('qhelpgenerator')
+    )
     if Utilities.isWindowsPlatform():
         exe += '.exe'
     dataList.append({
@@ -88,20 +91,25 @@
     })
     
     # 3. Qt Collection Generator
-    exe = os.path.join(Utilities.getQtBinariesPath(), 'qcollectiongenerator')
+    exe = os.path.join(
+        Utilities.getQtBinariesPath(),
+        Utilities.generateQtToolName('qcollectiongenerator')
+    )
     if Utilities.isWindowsPlatform():
         exe += '.exe'
-    dataList.append({
-        "programEntry": True,
-        "header": QCoreApplication.translate(
-            "EricdocPlugin", "Qt Help Tools"),
-        "exe": exe,
-        "versionCommand": '-v',
-        "versionStartsWith": 'Qt',
-        "versionPosition": -1,
-        "version": "",
-        "versionCleanup": (0, -1),
-    })
+    if Utilities.isExecutable(exe):
+        # assume Qt 5.,12 if it is missing
+        dataList.append({
+            "programEntry": True,
+            "header": QCoreApplication.translate(
+                "EricdocPlugin", "Qt Help Tools"),
+            "exe": exe,
+            "versionCommand": '-v',
+            "versionStartsWith": 'Qt',
+            "versionPosition": -1,
+            "version": "",
+            "versionCleanup": (0, -1),
+        })
     
     return dataList
 
--- a/Preferences/ConfigurationPages/EditorStylesPage.py	Thu Jan 10 14:23:49 2019 +0100
+++ b/Preferences/ConfigurationPages/EditorStylesPage.py	Sat Feb 02 11:12:54 2019 +0100
@@ -257,6 +257,12 @@
         self.initColour("MarkerMapBackground",
                         self.markerMapBackgroundButton,
                         Preferences.getEditorColour)
+        self.changesMarkerCheckBox.setChecked(
+            Preferences.getEditor("ShowMarkerChanges"))
+        self.coverageMarkerCheckBox.setChecked(
+            Preferences.getEditor("ShowMarkerCoverage"))
+        self.searchMarkerCheckBox.setChecked(
+            Preferences.getEditor("ShowMarkerSearch"))
         
         self.indentguidesCheckBox.setChecked(
             Preferences.getEditor("IndentationGuides"))
@@ -355,6 +361,15 @@
         Preferences.setEditor(
             "ShowMarkerMapOnRight",
             self.markerMapRightCheckBox.isChecked())
+        Preferences.setEditor(
+            "ShowMarkerChanges",
+            self.changesMarkerCheckBox.isChecked())
+        Preferences.setEditor(
+            "ShowMarkerCoverage",
+            self.coverageMarkerCheckBox.isChecked())
+        Preferences.setEditor(
+            "ShowMarkerSearch",
+            self.searchMarkerCheckBox.isChecked())
         
         self.saveColours(Preferences.setEditorColour)
         for key in list(self.editorColours.keys()):
--- a/Preferences/ConfigurationPages/EditorStylesPage.ui	Thu Jan 10 14:23:49 2019 +0100
+++ b/Preferences/ConfigurationPages/EditorStylesPage.ui	Sat Feb 02 11:12:54 2019 +0100
@@ -7,7 +7,7 @@
     <x>0</x>
     <y>0</y>
     <width>655</width>
-    <height>2650</height>
+    <height>2772</height>
    </rect>
   </property>
   <layout class="QVBoxLayout" name="verticalLayout_9">
@@ -1887,6 +1887,55 @@
         </layout>
        </widget>
       </item>
+      <item>
+       <widget class="QGroupBox" name="groupBox_17">
+        <property name="title">
+         <string>Shown Markers</string>
+        </property>
+        <layout class="QGridLayout" name="gridLayout_11">
+         <item row="0" column="0">
+          <widget class="QCheckBox" name="changesMarkerCheckBox">
+           <property name="toolTip">
+            <string>Select to show change markers</string>
+           </property>
+           <property name="text">
+            <string>Changes</string>
+           </property>
+          </widget>
+         </item>
+         <item row="0" column="1">
+          <widget class="QCheckBox" name="coverageMarkerCheckBox">
+           <property name="toolTip">
+            <string>Select to show coverage markers</string>
+           </property>
+           <property name="text">
+            <string>Coverage</string>
+           </property>
+          </widget>
+         </item>
+         <item row="1" column="0">
+          <widget class="QCheckBox" name="searchMarkerCheckBox">
+           <property name="toolTip">
+            <string>Select to show search markers</string>
+           </property>
+           <property name="text">
+            <string>Search Markers</string>
+           </property>
+          </widget>
+         </item>
+         <item row="2" column="0" colspan="2">
+          <widget class="QLabel" name="label_17">
+           <property name="text">
+            <string>&lt;b&gt;Note&lt;/b&gt;: Marker types not listed are always shown.</string>
+           </property>
+           <property name="wordWrap">
+            <bool>true</bool>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </widget>
+      </item>
      </layout>
     </widget>
    </item>
@@ -2005,6 +2054,9 @@
   <tabstop>searchMarkerMapButton</tabstop>
   <tabstop>conflictMarkerMapButton</tabstop>
   <tabstop>markerMapBackgroundButton</tabstop>
+  <tabstop>changesMarkerCheckBox</tabstop>
+  <tabstop>coverageMarkerCheckBox</tabstop>
+  <tabstop>searchMarkerCheckBox</tabstop>
   <tabstop>miniMenuCheckBox</tabstop>
   <tabstop>hideFormatButtonsCheckBox</tabstop>
  </tabstops>
--- a/Preferences/ConfigurationPages/WebBrowserPage.py	Thu Jan 10 14:23:49 2019 +0100
+++ b/Preferences/ConfigurationPages/WebBrowserPage.py	Sat Feb 02 11:12:54 2019 +0100
@@ -84,6 +84,12 @@
         except KeyError:
             self.autoplayMediaCheckBox.setEnabled(False)
             self.webRtcPublicOnlyCheckBox.setEnabled(False)
+        try:
+            # Qt 5.12
+            self.dnsPrefetchCheckBox.setChecked(
+                Preferences.getWebBrowser("DnsPrefetchEnabled"))
+        except KeyError:
+            self.dnsPrefetchCheckBox.setEnabled(False)
         
         self.javaScriptGroup.setChecked(
             Preferences.getWebBrowser("JavaScriptEnabled"))
@@ -281,6 +287,11 @@
                 "WebRTCPublicInterfacesOnly",
                 self.webRtcPublicOnlyCheckBox.isChecked())
         
+        if self.dnsPrefetchCheckBox.isEnabled():
+            Preferences.setWebBrowser(
+                "DnsPrefetchEnabled",
+                self.dnsPrefetchCheckBox.isChecked())
+        
         Preferences.setWebBrowser(
             "JavaScriptEnabled",
             self.javaScriptGroup.isChecked())
--- a/Preferences/ConfigurationPages/WebBrowserPage.ui	Thu Jan 10 14:23:49 2019 +0100
+++ b/Preferences/ConfigurationPages/WebBrowserPage.ui	Sat Feb 02 11:12:54 2019 +0100
@@ -147,6 +147,16 @@
         </property>
        </widget>
       </item>
+      <item row="5" column="1">
+       <widget class="QCheckBox" name="dnsPrefetchCheckBox">
+        <property name="toolTip">
+         <string>Select to try to pre-fetch DNS entries to speed up browsing</string>
+        </property>
+        <property name="text">
+         <string>Enable DNS Prefetch</string>
+        </property>
+       </widget>
+      </item>
      </layout>
     </widget>
    </item>
@@ -1220,6 +1230,7 @@
   <tabstop>webGLCheckBox</tabstop>
   <tabstop>autoplayMediaCheckBox</tabstop>
   <tabstop>webRtcPublicOnlyCheckBox</tabstop>
+  <tabstop>dnsPrefetchCheckBox</tabstop>
   <tabstop>startupCombo</tabstop>
   <tabstop>newTabCombo</tabstop>
   <tabstop>homePageEdit</tabstop>
--- a/Preferences/ProgramsDialog.py	Thu Jan 10 14:23:49 2019 +0100
+++ b/Preferences/ProgramsDialog.py	Sat Feb 02 11:12:54 2019 +0100
@@ -340,6 +340,7 @@
         font = itm.font(0)
         font.setBold(True)
         itm.setFont(0, font)
+        rememberedExe = exe
         if not exe:
             itm.setText(1, self.tr("(not configured)"))
         else:
@@ -367,7 +368,8 @@
                                      'replace')
                         if exeModule and exeModule[0] == "-m" and \
                             ("ImportError:" in output or
-                             "ModuleNotFoundError:" in output):
+                             "ModuleNotFoundError:" in output or
+                             proc.exitCode() != 0):
                             version = self.tr("(module not found)")
                         else:
                             if versionRe is None:
@@ -398,7 +400,12 @@
                     QTreeWidgetItem(itm, [exe, version])
                 itm.setExpanded(True)
             else:
-                itm.setText(1, self.tr("(not found)"))
+                if itm.childCount() == 0:
+                    itm.setText(1, self.tr("(not found)"))
+                else:
+                    QTreeWidgetItem(itm, [rememberedExe,
+                                          self.tr("(not found)")])
+                    itm.setExpanded(True)
         QApplication.processEvents()
         self.programsList.header().resizeSections(QHeaderView.ResizeToContents)
         self.programsList.header().setStretchLastSection(True)
--- a/Preferences/__init__.py	Thu Jan 10 14:23:49 2019 +0100
+++ b/Preferences/__init__.py	Sat Feb 02 11:12:54 2019 +0100
@@ -499,6 +499,9 @@
         "MouseClickHandlersEnabled": True,
         
         "ShowMarkerMapOnRight": True,
+        "ShowMarkerChanges": True,
+        "ShowMarkerCoverage": True,
+        "ShowMarkerSearch": True,
         
         # All (most) lexers
         "AllFoldCompact": True,
@@ -1316,6 +1319,15 @@
             })
         except AttributeError:
             pass
+        try:
+            # Qt 5.12+
+            cls.webBrowserDefaults.update({
+                "DnsPrefetchEnabled":
+                    webEngineSettings.testAttribute(
+                        QWebEngineSettings.DnsPrefetchEnabled),
+            })
+        except AttributeError:
+            pass
         
         cls.webEngineSettingsIntitialized = True
     
--- a/Project/CreateDialogCodeDialog.py	Thu Jan 10 14:23:49 2019 +0100
+++ b/Project/CreateDialogCodeDialog.py	Sat Feb 02 11:12:54 2019 +0100
@@ -13,15 +13,14 @@
 except NameError:
     pass
 
+import sys
 import os
 import json
-import xml.etree.ElementTree
 
-from PyQt5.QtCore import QMetaObject, QByteArray, QRegExp, Qt, pyqtSlot, \
-    QMetaMethod, QSortFilterProxyModel, QProcess, QProcessEnvironment
+from PyQt5.QtCore import pyqtSlot, Qt, QMetaObject, QRegExp, \
+    QSortFilterProxyModel, QProcess, QProcessEnvironment
 from PyQt5.QtGui import QStandardItemModel, QBrush, QStandardItem
-from PyQt5.QtWidgets import QWidget, QDialog, QDialogButtonBox, QAction
-from PyQt5 import uic
+from PyQt5.QtWidgets import QDialog, QDialogButtonBox
 
 
 from E5Gui.E5Application import e5App
@@ -32,8 +31,6 @@
 
 from eric6config import getConfig
 
-from Globals import qVersionTuple
-
 
 pyqtSignatureRole = Qt.UserRole + 1
 pythonSignatureRole = Qt.UserRole + 2
@@ -162,6 +159,9 @@
         interpreter = venvManager.getVirtualenvInterpreter(venvName)
         execPath = venvManager.getVirtualenvExecPath(venvName)
         
+        if not interpreter:
+            interpreter = sys.executable
+        
         env = QProcessEnvironment.systemEnvironment()
         if execPath:
             if env.contains("PATH"):
@@ -215,10 +215,9 @@
         
         return uicText, ok
     
-    def __objectNameExternal(self):
+    def __objectName(self):
         """
-        Private method to get the object name of a form via an external
-        interpreter.
+        Private method to get the object name of a form.
         
         @return object name
         @rtype str
@@ -231,34 +230,9 @@
         
         return objectName
     
-    def __objectName(self):
-        """
-        Private method to get the object name of a form.
-        
-        @return object name
-        @rtype str
+    def __className(self):
         """
-        if self.project.getDebugProperty("VIRTUALENV"):
-            return self.__objectNameExternal()
-        else:
-            try:
-                dlg = uic.loadUi(
-                    self.formFile, package=self.project.getProjectPath())
-                return dlg.objectName()
-            except (AttributeError, ImportError,
-                    xml.etree.ElementTree.ParseError) as err:
-                E5MessageBox.critical(
-                    self,
-                    self.tr("uic error"),
-                    self.tr(
-                        """<p>There was an error loading the form <b>{0}</b>"""
-                        """.</p><p>{1}</p>""").format(self.formFile, str(err)))
-                return ""
-    
-    def __classNameExternal(self):
-        """
-        Private method to get the class name of a form via an external
-        interpreter.
+        Private method to get the class name of a form.
         
         @return class name
         @rtype str
@@ -271,30 +245,6 @@
         
         return className
     
-    def __className(self):
-        """
-        Private method to get the class name of a form.
-        
-        @return class name
-        @rtype str
-        """
-        if self.project.getDebugProperty("VIRTUALENV"):
-            return self.__classNameExternal()
-        else:
-            try:
-                dlg = uic.loadUi(
-                    self.formFile, package=self.project.getProjectPath())
-                return dlg.metaObject().className()
-            except (AttributeError, ImportError,
-                    xml.etree.ElementTree.ParseError) as err:
-                E5MessageBox.critical(
-                    self,
-                    self.tr("uic error"),
-                    self.tr(
-                        """<p>There was an error loading the form <b>{0}</b>"""
-                        """.</p><p>{1}</p>""").format(self.formFile, str(err)))
-                return ""
-    
     def __signatures(self):
         """
         Private slot to get the signatures.
@@ -346,11 +296,12 @@
         
         return mapped
     
-    def __updateSlotsModelExternal(self):
+    def __updateSlotsModel(self):
         """
-        Private slot to update the slots tree display getting the data via an
-        external interpreter.
+        Private slot to update the slots tree display.
         """
+        self.filterEdit.clear()
+        
         output, ok = self.__runUicLoadUi("signatures")
         if ok and output:
             objectsList = json.loads(output.strip())
@@ -396,147 +347,6 @@
                     itm2.setCheckState(Qt.Unchecked)
             
             self.slotsView.sortByColumn(0, Qt.AscendingOrder)
-    
-    def __updateSlotsModel(self):
-        """
-        Private slot to update the slots tree display.
-        """
-        self.filterEdit.clear()
-        
-        if self.project.getDebugProperty("VIRTUALENV"):
-            self.__updateSlotsModelExternal()
-        else:
-            try:
-                dlg = uic.loadUi(
-                    self.formFile, package=self.project.getProjectPath())
-                objects = dlg.findChildren(QWidget) + dlg.findChildren(QAction)
-                
-                signatureList = self.__signatures()
-                
-                self.slotsModel.clear()
-                self.slotsModel.setHorizontalHeaderLabels([""])
-                for obj in objects:
-                    name = obj.objectName()
-                    if not name or name.startswith("qt_"):
-                        # ignore un-named or internal objects
-                        continue
-                    
-                    metaObject = obj.metaObject()
-                    className = metaObject.className()
-                    itm = QStandardItem("{0} ({1})".format(name, className))
-                    self.slotsModel.appendRow(itm)
-                    for index in range(metaObject.methodCount()):
-                        metaMethod = metaObject.method(index)
-                        if metaMethod.methodType() == QMetaMethod.Signal:
-                            if qVersionTuple() >= (5, 0, 0):
-                                itm2 = QStandardItem("on_{0}_{1}".format(
-                                    name,
-                                    bytes(metaMethod.methodSignature())
-                                    .decode()
-                                ))
-                            else:
-                                itm2 = QStandardItem("on_{0}_{1}".format(
-                                    name, metaMethod.signature()
-                                ))
-                            itm.appendRow(itm2)
-                            if self.__module is not None:
-                                if qVersionTuple() >= (5, 0, 0):
-                                    method = "on_{0}_{1}".format(
-                                        name,
-                                        bytes(metaMethod.methodSignature())
-                                        .decode().split("(")[0])
-                                else:
-                                    method = "on_{0}_{1}".format(
-                                        name,
-                                        metaMethod.signature().split("(")[0])
-                                method2 = "{0}({1})".format(
-                                    method,
-                                    ", ".join([
-                                        self.__mapType(t)
-                                        for t in metaMethod.parameterTypes()
-                                    ])
-                                )
-                                
-                                if method2 in signatureList or \
-                                        method in signatureList:
-                                    itm2.setFlags(
-                                        Qt.ItemFlags(Qt.ItemIsEnabled))
-                                    itm2.setCheckState(Qt.Checked)
-                                    itm2.setForeground(QBrush(Qt.blue))
-                                    continue
-                            
-                            returnType = self.__mapType(
-                                metaMethod.typeName().encode())
-                            if returnType == 'void':
-                                returnType = ""
-                            parameterTypesList = [
-                                self.__mapType(t)
-                                for t in metaMethod.parameterTypes()]
-                            pyqtSignature = ", ".join(parameterTypesList)
-                            
-                            parameterNames = metaMethod.parameterNames()
-                            if parameterNames:
-                                for index in range(len(parameterNames)):
-                                    if not parameterNames[index]:
-                                        parameterNames[index] = \
-                                            QByteArray("p{0:d}".format(index)
-                                                       .encode("utf-8"))
-                            parameterNamesList = [bytes(n).decode()
-                                                  for n in parameterNames]
-                            methNamesSig = ", ".join(parameterNamesList)
-                            
-                            if methNamesSig:
-                                if qVersionTuple() >= (5, 0, 0):
-                                    pythonSignature = \
-                                        "on_{0}_{1}(self, {2})".format(
-                                            name,
-                                            bytes(metaMethod.methodSignature())
-                                            .decode().split("(")[0],
-                                            methNamesSig)
-                                else:
-                                    pythonSignature = \
-                                        "on_{0}_{1}(self, {2})".format(
-                                            name,
-                                            metaMethod.signature()
-                                            .split("(")[0],
-                                            methNamesSig)
-                            else:
-                                if qVersionTuple() >= (5, 0, 0):
-                                    pythonSignature = "on_{0}_{1}(self)"\
-                                        .format(
-                                            name,
-                                            bytes(metaMethod.methodSignature())
-                                            .decode().split("(")[0])
-                                else:
-                                    pythonSignature = "on_{0}_{1}(self)"\
-                                        .format(
-                                            name,
-                                            metaMethod.signature().split(
-                                                "(")[0])
-                            itm2.setData(pyqtSignature, pyqtSignatureRole)
-                            itm2.setData(pythonSignature, pythonSignatureRole)
-                            itm2.setData(returnType, returnTypeRole)
-                            itm2.setData(parameterTypesList,
-                                         parameterTypesListRole)
-                            itm2.setData(parameterNamesList,
-                                         parameterNamesListRole)
-                            
-                            itm2.setFlags(Qt.ItemFlags(
-                                Qt.ItemIsUserCheckable |
-                                Qt.ItemIsEnabled |
-                                Qt.ItemIsSelectable)
-                            )
-                            itm2.setCheckState(Qt.Unchecked)
-                
-                self.slotsView.sortByColumn(0, Qt.AscendingOrder)
-            except (AttributeError, ImportError,
-                    xml.etree.ElementTree.ParseError) as err:
-                E5MessageBox.critical(
-                    self,
-                    self.tr("uic error"),
-                    self.tr(
-                        """<p>There was an error loading the form <b>{0}</b>"""
-                        """.</p><p>{1}</p>""").format(self.formFile, str(err)))
         
     def __generateCode(self):
         """
--- a/Project/DebuggerPropertiesDialog.py	Thu Jan 10 14:23:49 2019 +0100
+++ b/Project/DebuggerPropertiesDialog.py	Sat Feb 02 11:12:54 2019 +0100
@@ -84,7 +84,8 @@
         self.venvComboBox.setCurrentIndex(venvIndex)
         if self.project.debugProperties["DEBUGCLIENT"]:
             self.debugClientPicker.setText(
-                self.project.debugProperties["DEBUGCLIENT"])
+                self.project.debugProperties["DEBUGCLIENT"],
+                toNative=False)
         else:
             if self.project.pdata["PROGLANGUAGE"] in ["Python", "Python2",
                                                       "Python3"]:
@@ -93,7 +94,7 @@
                     "DebugClients", "Python", "DebugClient.py")
             else:
                 debugClient = ""
-            self.debugClientPicker.setText(debugClient)
+            self.debugClientPicker.setText(debugClient, toNative=False)
         self.debugEnvironmentOverrideCheckBox.setChecked(
             self.project.debugProperties["ENVIRONMENTOVERRIDE"])
         self.debugEnvironmentEdit.setText(
@@ -141,7 +142,7 @@
             self.venvComboBox.currentText()
         
         self.project.debugProperties["DEBUGCLIENT"] = \
-            self.debugClientPicker.text()
+            self.debugClientPicker.text(toNative=False)
         if not self.project.debugProperties["DEBUGCLIENT"]:
             if self.project.pdata["PROGLANGUAGE"] in ["Python", "Python2",
                                                       "Python3"]:
@@ -184,7 +185,7 @@
         """
         Private method to save the path picker histories.
         """
-        debugClient = self.debugClientPicker.text()
+        debugClient = self.debugClientPicker.text(toNative=False)
         debugClientsHistory = self.debugClientPicker.getPathItems()
         if debugClient not in debugClientsHistory:
             debugClientsHistory.insert(0, debugClient)
--- a/Project/ProjectFormsBrowser.py	Thu Jan 10 14:23:49 2019 +0100
+++ b/Project/ProjectFormsBrowser.py	Sat Feb 02 11:12:54 2019 +0100
@@ -861,6 +861,7 @@
         self.compileProc.readyReadStandardError.connect(self.__readStderr)
         
         self.noDialog = noDialog
+        self.compileProc.setWorkingDirectory(self.project.getProjectPath())
         self.compileProc.start(uicompiler, args)
         procStarted = self.compileProc.waitForStarted(5000)
         if procStarted:
--- a/QScintilla/EditorMarkerMap.py	Thu Jan 10 14:23:49 2019 +0100
+++ b/QScintilla/EditorMarkerMap.py	Sat Feb 02 11:12:54 2019 +0100
@@ -82,12 +82,14 @@
         # draw indicators in reverse order of priority
         
         # 1. changes
-        for line in self._master.getChangeLines():
-            self.__drawIndicator(line, painter, self.__changeColor)
+        if Preferences.getEditor("ShowMarkerChanges"):
+            for line in self._master.getChangeLines():
+                self.__drawIndicator(line, painter, self.__changeColor)
         
         # 2. coverage
-        for line in self._master.getCoverageLines():
-            self.__drawIndicator(line, painter, self.__coverageColor)
+        if Preferences.getEditor("ShowMarkerCoverage"):
+            for line in self._master.getCoverageLines():
+                self.__drawIndicator(line, painter, self.__coverageColor)
         
         # 3. tasks
         for line in self._master.getTaskLines():
@@ -102,21 +104,22 @@
             self.__drawIndicator(line, painter, self.__bookmarkColor)
         
         # 6. search markers
-        for line in self._master.getSearchIndicatorLines():
-            self.__drawIndicator(line, painter, self.__searchMarkerColor)
+        if Preferences.getEditor("ShowMarkerSearch"):
+            for line in self._master.getSearchIndicatorLines():
+                self.__drawIndicator(line, painter, self.__searchMarkerColor)
         
         # 7. warnings
         for line in self._master.getWarningLines():
             self.__drawIndicator(line, painter, self.__warningColor)
         
-        # 8a. VCS conflict markers
+        # 8. VCS conflict markers
         for line in self._master.getVcsConflictMarkerLines():
             self.__drawIndicator(line, painter, self.__vcsConflictMarkerColor)
         
-        # 8b. errors
+        # 9. errors
         for line in self._master.getSyntaxErrorLines():
             self.__drawIndicator(line, painter, self.__errorColor)
         
-        # 9. current line
+        # 10. current line
         self.__drawIndicator(self._master.getCursorPosition()[0], painter,
                              self.__currentLineMarker)
--- a/ThirdParty/Jasy/jasy/__init__.py	Thu Jan 10 14:23:49 2019 +0100
+++ b/ThirdParty/Jasy/jasy/__init__.py	Sat Feb 02 11:12:54 2019 +0100
@@ -7,13 +7,33 @@
 """
 **Jasy - Web Tooling Framework**
 
-Jasy is a powerful Python3-based tooling framework. 
-It makes it easy to manage heavy web projects. 
-Its main goal is to offer an API which could be used by developers to write
-their custom build/deployment scripts.
+Jasy is a powerful Python3-based tooling framework.
+It makes it easy to manage heavy web projects.
+Its main goal is to offer an API which could be used by developers to write their custom build/deployment scripts.
 """
 
 from __future__ import unicode_literals
 
-__version__ = "1.5-beta5"
+__version__ = "1.5-beta6"
 __author__ = "Sebastian Werner <info@sebastian-werner.net>"
+
+import os.path
+datadir = os.path.join(os.path.dirname(__file__), "data")
+
+def info():
+    """
+    Prints information about Jasy to the console.
+    """
+
+    import jasy.core.Console as Console
+
+    print("Jasy %s is a powerful web tooling framework" % __version__)
+    print("Visit %s for details." % Console.colorize("https://github.com/sebastian-software/jasy", "underline"))
+    print()
+
+
+class UserError(Exception):
+    """
+    Standard Jasy error class raised whenever something happens which the system understands (somehow excepected)
+    """
+    pass
--- a/ThirdParty/Jasy/jasy/core/Console.py	Thu Jan 10 14:23:49 2019 +0100
+++ b/ThirdParty/Jasy/jasy/core/Console.py	Sat Feb 02 11:12:54 2019 +0100
@@ -2,27 +2,124 @@
 
 # Copyright (c) 2013 - 2019 Detlev Offenbach <detlev@die-offenbachs.de>
 #
+# Jasy - Web Tooling Framework
+# Copyright 2010-2012 Zynga Inc.
+# Copyright 2013-2014 Sebastian Werner
+#
+
+"""
+Centralized logging for complete Jasy environment.
+"""
 
 from __future__ import unicode_literals
 
-import logging
+import logging, sys
+
+__all__ = ["colorize", "header", "error", "warn", "info", "debug", "indent", "outdent"]
+
+
+
+# ---------------------------------------------
+# Colorized Output
+# ---------------------------------------------
+
+__colors = {
+    'bold'      : ['\033[1m',  '\033[22m'],
+    'italic'    : ['\033[3m',  '\033[23m'],
+    'underline' : ['\033[4m',  '\033[24m'],
+    'inverse'   : ['\033[7m',  '\033[27m'],
+
+    'white'     : ['\033[37m', '\033[39m'],
+    'grey'      : ['\033[90m', '\033[39m'],
+    'black'     : ['\033[30m', '\033[39m'],
+
+    'blue'      : ['\033[34m', '\033[39m'],
+    'cyan'      : ['\033[36m', '\033[39m'],
+    'green'     : ['\033[32m', '\033[39m'],
+    'magenta'   : ['\033[35m', '\033[39m'],
+    'red'       : ['\033[31m', '\033[39m'],
+    'yellow'    : ['\033[33m', '\033[39m']
+}
+
+def colorize(text, color="red"):
+    """Uses to colorize the given text for output on Unix terminals"""
+
+    # Not supported on console on Windows native
+    # Note: Cygwin has a different platform value
+    if sys.platform == "win32":
+        return text
+
+    entry = __colors[color]
+    return "%s%s%s" % (entry[0], text, entry[1])
+
+
+
+# ---------------------------------------------
+# Logging API
+# ---------------------------------------------
+
+__level = 0
+
+def __format(text):
+    global __level
+
+    if __level == 0 or text == "":
+        return text
+    elif __level == 1:
+        return "- %s" % text
+    else:
+        return "%s- %s" % ("  " * (__level-1), text)
+
+def indent():
+    """
+    Increments global indenting level. Prepends spaces to the next
+    logging messages until outdent() is called.
+
+    Should be called whenever leaving a structural logging section.
+    """
+
+    global __level
+    __level += 1
+
+def outdent(all=False):
+    """
+    Decrements global indenting level.
+    Should be called whenever leaving a structural logging section.
+    """
+
+    global __level
+
+    if all:
+        __level = 0
+    else:
+        __level -= 1
 
 def error(text, *argv):
-    """Outputs an error message"""
+    """Outputs an error message (visible by default)"""
 
-    logging.error(text, *argv)
+    logging.warn(__format(colorize(colorize(text, "red"), "bold")), *argv)
 
 def warn(text, *argv):
-    """Outputs an warning"""
+    """Outputs an warning (visible by default)"""
 
-    logging.warn(text, *argv)
+    logging.warn(__format(colorize(text, "red")), *argv)
 
 def info(text, *argv):
-    """Outputs an info message"""
+    """Outputs an info message (visible by default, disable via --quiet option)"""
 
-    logging.info(text, *argv)
+    logging.info(__format(text), *argv)
 
 def debug(text, *argv):
-    """Output a debug message"""
+    """Output a debug message (hidden by default, enable via --verbose option)"""
+
+    logging.debug(__format(text), *argv)
+
+def header(title):
+    """Outputs the given title with prominent formatting"""
 
-    logging.debug(text, *argv)
+    global __level
+    __level = 0
+
+    logging.info("")
+    logging.info(colorize(colorize(">>> %s" % title.upper(), "blue"), "bold"))
+    logging.info(colorize("-------------------------------------------------------------------------------", "blue"))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ThirdParty/Jasy/jasy/core/Text.py	Sat Feb 02 11:12:54 2019 +0100
@@ -0,0 +1,87 @@
+#
+# Jasy - Web Tooling Framework
+# Copyright 2010-2012 Zynga Inc.
+# Copyright 2013-2014 Sebastian Werner
+#
+
+from __future__ import unicode_literals
+
+import re
+
+
+#
+# MARKDOWN TO HTML
+#
+
+try:
+    # import hoedown
+    #
+    # hoedownExt = hoedown.EXT_AUTOLINK | hoedown.EXT_NO_INTRA_EMPHASIS | hoedown.EXT_FENCED_CODE | hoedown.EXT_TABLES | hoedown.EXT_FOOTNOTES | hoedown.EXT_QUOTE | hoedown.EXT_STRIKETHROUGH | hoedown.EXT_UNDERLINE | hoedown.EXT_HIGHLIGHT
+    # hoedownExt = hoedown.EXT_AUTOLINK
+    # hoedownRender = hoedown.HTML_SKIP_STYLE | hoedown.HTML_SMARTYPANTS
+
+    import misaka
+
+    hoedownExt = misaka.EXT_AUTOLINK | misaka.EXT_NO_INTRA_EMPHASIS | misaka.EXT_FENCED_CODE
+    hoedownRender = misaka.HTML_SKIP_STYLE | misaka.HTML_SMARTYPANTS
+    hoedown = misaka
+
+    supportsMarkdown = True
+
+except:
+    supportsMarkdown = False
+
+def markdownToHtml(markdownStr):
+    """
+    Converts Markdown to HTML. Supports GitHub's fenced code blocks,
+    auto linking and typographic features by SmartyPants.
+    """
+
+    return hoedown.html(markdownStr, hoedownExt, hoedownRender)
+
+
+#
+# HIGHLIGHT CODE BLOCKS
+#
+
+try:
+    from pygments import highlight
+    from pygments.formatters import HtmlFormatter
+    from pygments.lexers import get_lexer_by_name
+
+    # By http://misaka.61924.nl/#toc_3
+    codeblock = re.compile(r'<pre(?: lang="([a-z0-9]+)")?><code(?: class="([a-z0-9]+).*?")?>(.*?)</code></pre>', re.IGNORECASE | re.DOTALL)
+
+    supportsHighlighting = True
+
+except ImportError:
+
+    supportsHighlighting = False
+
+def highlightCodeBlocks(html, tabsize=2, defaultlang="javascript"):
+    """
+    Patches 'code' elements in HTML to apply HTML based syntax highlighting. Automatically
+    chooses the matching language detected via a CSS class of the 'code' element.
+    """
+
+    def unescape(html):
+        html = html.replace('&lt;', '<')
+        html = html.replace('&gt;', '>')
+        html = html.replace('&amp;', '&')
+        html = html.replace('&quot;', '"')
+        return html.replace('&#39;', "'")
+
+    def replace(match):
+        language, classname, code = match.groups()
+        if language is None:
+            language = classname if classname else defaultlang
+
+        lexer = get_lexer_by_name(language, tabsize=tabsize)
+        formatter = HtmlFormatter(linenos="table")
+
+        code = unescape(code)
+
+        # for some reason pygments escapes our code once again so we need to reverse it twice
+        return unescape(highlight(code, lexer, formatter))
+
+    return codeblock.sub(replace, html)
--- a/ThirdParty/Jasy/jasy/core/__init__.py	Thu Jan 10 14:23:49 2019 +0100
+++ b/ThirdParty/Jasy/jasy/core/__init__.py	Sat Feb 02 11:12:54 2019 +0100
@@ -4,6 +4,6 @@
 #
 
 #
-# This is an eric6 dummy package to provide some specially variants of modules
+# This is an eric6 dummy package to provide some special variants of modules
 # found in the standard jasy package
 #
--- a/ThirdParty/Jasy/jasy/js/api/Comment.py	Thu Jan 10 14:23:49 2019 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,163 +0,0 @@
-#
-# Jasy - Web Tooling Framework
-# Copyright 2010-2012 Zynga Inc.
-#
-
-from __future__ import unicode_literals
-
-import re
-
-import jasy.core.Console as Console
-
-__all__ = ["CommentException", "Comment"]
-
-
-# Used to measure the doc indent size (with leading stars in front of content)
-docIndentReg = re.compile(r"^(\s*\*\s*)(\S*)")
-
-
-class CommentException(Exception):
-    """
-    Thrown when errors during comment processing are detected.
-    """
-    def __init__(self, message, lineNo=0):
-        Exception.__init__(self, "Comment error: %s (line: %s)" % (message, lineNo+1))
-
-
-class Comment():
-    """
-    Comment class is attached to parsed nodes and used to store all comment related
-    information.
-    
-    The class supports a new Markdown and TomDoc inspired dialect to make developers life
-    easier and work less repeative.
-    """
-    
-    # Relation to code
-    context = None
-    
-    # Collected text of the comment
-    text = None
-    
-    def __init__(self, text, context=None, lineNo=0, indent="", fileId=None):
-
-        # Store context (relation to code)
-        self.context = context
-        
-        # Store fileId
-        self.fileId = fileId
-        
-        # Figure out the type of the comment based on the starting characters
-
-        # Inline comments
-        if text.startswith("//"):
-            # "// hello" => "   hello"
-            text = "  " + text[2:]
-            self.variant = "single"
-            
-        # Doc comments
-        elif text.startswith("/**"):
-            # "/** hello */" => "    hello "
-            text = "   " + text[3:-2]
-            self.variant = "doc"
-
-        # Protected comments which should not be removed
-        # (e.g these are used for license blocks)
-        elif text.startswith("/*!"):
-            # "/*! hello */" => "    hello "
-            text = "   " + text[3:-2]
-            self.variant = "protected"
-            
-        # A normal multiline comment
-        elif text.startswith("/*"):
-            # "/* hello */" => "   hello "
-            text = "  " + text[2:-2]
-            self.variant = "multi"
-            
-        else:
-            raise CommentException("Invalid comment text: %s" % text, lineNo)
-
-        # Multi line comments need to have their indentation removed
-        if "\n" in text:
-            text = self.__outdent(text, indent, lineNo)
-
-        # For single line comments strip the surrounding whitespace
-        else:
-            # " hello " => "hello"
-            text = text.strip()
-
-        # The text of the comment
-        self.text = text
-    
-    def __outdent(self, text, indent, startLineNo):
-        """
-        Outdent multi line comment text and filtering empty lines
-        """
-        
-        lines = []
-
-        # First, split up the comments lines and remove the leading indentation
-        for lineNo, line in enumerate((indent+text).split("\n")):
-
-            if line.startswith(indent):
-                lines.append(line[len(indent):].rstrip())
-
-            elif line.strip() == "":
-                lines.append("")
-
-            else:
-                # Only warn for doc comments, otherwise it might just be code commented
-                # out which is sometimes formatted pretty crazy when commented out
-                if self.variant == "doc":
-                    Console.warn("Could not outdent doc comment at line %s in %s",
-                        startLineNo+lineNo, self.fileId)
-                    
-                return text
-
-        # Find first line with real content, then grab the one after it to get the 
-        # characters which need 
-        outdentString = ""
-        for lineNo, line in enumerate(lines):
-
-            if line != "" and line.strip() != "":
-                matchedDocIndent = docIndentReg.match(line)
-                
-                if not matchedDocIndent:
-                    # As soon as we find a non doc indent like line we stop
-                    break
-                    
-                elif matchedDocIndent.group(2) != "":
-                    # otherwise we look for content behind the indent to get the 
-                    # correct real indent (with spaces)
-                    outdentString = matchedDocIndent.group(1)
-                    break
-                
-            lineNo += 1
-
-        # Process outdenting to all lines (remove the outdentString from the start
-        # of the lines)
-        if outdentString != "":
-
-            lineNo = 0
-            outdentStringLen = len(outdentString)
-
-            for lineNo, line in enumerate(lines):
-                if len(line) <= outdentStringLen:
-                    lines[lineNo] = ""
-
-                else:
-                    if not line.startswith(outdentString):
-                        
-                        # Only warn for doc comments, otherwise it might just be code
-                        # commented out which is sometimes formatted pretty crazy when
-                        # commented out
-                        if self.variant == "doc":
-                            Console.warn(
-                                "Invalid indentation in doc comment at line %s in %s",
-                                startLineNo+lineNo, self.fileId)
-                        
-                    else:
-                        lines[lineNo] = line[outdentStringLen:]
-
-        # Merge final lines and remove leading and trailing new lines
-        return "\n".join(lines).strip("\n")
--- a/ThirdParty/Jasy/jasy/js/api/Text.py	Thu Jan 10 14:23:49 2019 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,38 +0,0 @@
-#
-# Jasy - Web Tooling Framework
-# Copyright 2010-2012 Zynga Inc.
-#
-
-from __future__ import unicode_literals
-
-import re
-import jasy.core.Console as Console
-
-__all__ = ["extractSummary"]
-
-# Used to filter first paragraph from HTML
-paragraphExtract = re.compile(r"^(.*?)(\. |\? |\! |$)")
-newlineMatcher = re.compile(r"\n")
-
-# Used to remove markup sequences after doc processing of comment text
-stripMarkup = re.compile(r"<.*?>")
-
-def extractSummary(text):
-    try:
-        text = stripMarkup.sub("", newlineMatcher.sub(" ", text))
-        matched = paragraphExtract.match(text)
-    except TypeError:
-        matched = None
-        
-    if matched:
-        summary = matched.group(1)
-        if summary is not None:
-            if not summary.endswith((".", "!", "?")):
-                summary = summary.strip() + "."
-            return summary
-            
-    else:
-        Console.warn("Unable to extract summary for: %s", text)
-    
-    return None
-    
--- a/ThirdParty/Jasy/jasy/js/parse/Node.py	Thu Jan 10 14:23:49 2019 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,331 +0,0 @@
-#
-# Jasy - Web Tooling Framework
-# Copyright 2010-2012 Zynga Inc.
-#
-
-#
-# License: MPL 1.1/GPL 2.0/LGPL 2.1
-# Authors: 
-#   - Brendan Eich <brendan@mozilla.org> (Original JavaScript) (2004)
-#   - Sebastian Werner <info@sebastian-werner.net> (Refactoring Python) (2010)
-#
-
-from __future__ import unicode_literals
-
-import json
-import copy
-
-class Node(list):
-    
-    __slots__ = [
-        # core data
-        "line", "type", "tokenizer", "start", "end", "rel", "parent", 
-        
-        # dynamic added data by other modules
-        "comments", "scope", 
-        
-        # node type specific
-        "value", "expression", "body", "functionForm", "parenthesized",
-        "fileId", "params", "name", "readOnly", "initializer", "condition",
-        "isLoop", "isEach", "object", "assignOp", "iterator", "thenPart",
-        "exception", "elsePart", "setup", "postfix", "update", "tryBlock",
-        "block", "defaultIndex", "discriminant", "label", "statements",
-        "finallyBlock", "statement", "variables", "names", "guard", "for",
-        "tail", "expressionClosure"
-    ]
-    
-    
-    def __init__(self, tokenizer=None, type=None, args=[]):
-        list.__init__(self)
-        
-        self.start = 0
-        self.end = 0
-        self.line = None
-        
-        if tokenizer:
-            token = getattr(tokenizer, "token", None)
-            if token:
-                # We may define a custom type but use the same positioning as another token
-                # e.g. transform curlys in block nodes, etc.
-                self.type = type if type else getattr(token, "type", None)
-                self.line = token.line
-                
-                # Start & end are file positions for error handling.
-                self.start = token.start
-                self.end = token.end
-            
-            else:
-                self.type = type
-                self.line = tokenizer.line
-                self.start = None
-                self.end = None
-
-            self.tokenizer = tokenizer
-            
-        elif type:
-            self.type = type
-
-        for arg in args:
-            self.append(arg)
-            
-            
-    def getUnrelatedChildren(self):
-        """Collects all unrelated children"""
-        
-        collection = []
-        for child in self:
-            if not hasattr(child, "rel"):
-                collection.append(child)
-            
-        return collection
-        
-
-    def getChildrenLength(self, filter=True):
-        """Number of (per default unrelated) children"""
-        
-        count = 0
-        for child in self:
-            if not filter or not hasattr(child, "rel"):
-                count += 1
-        return count
-            
-    
-    def remove(self, kid):
-        """Removes the given kid"""
-        
-        if not kid in self:
-            raise Exception("Given node is no child!")
-        
-        if hasattr(kid, "rel"):
-            delattr(self, kid.rel)
-            del kid.rel
-            del kid.parent
-            
-        list.remove(self, kid)
-        
-        
-    def insert(self, index, kid):
-        """Inserts the given kid at the given index"""
-        
-        if index is None:
-            return self.append(kid)
-            
-        if hasattr(kid, "parent"):
-            kid.parent.remove(kid)
-            
-        kid.parent = self
-
-        return list.insert(self, index, kid)
-            
-
-    def append(self, kid, rel=None):
-        """Appends the given kid with an optional relation hint"""
-        
-        # kid can be null e.g. [1, , 2].
-        if kid:
-            if hasattr(kid, "parent"):
-                kid.parent.remove(kid)
-            
-            # Debug
-            if not isinstance(kid, Node):
-                raise Exception("Invalid kid: %s" % kid)
-            
-            if hasattr(kid, "tokenizer"):
-                if hasattr(kid, "start"):
-                    if not hasattr(self, "start") or \
-                       self.start == None or \
-                       kid.start < self.start:
-                        self.start = kid.start
-
-                if hasattr(kid, "end"):
-                    if not hasattr(self, "end") or \
-                       self.end == None or \
-                       self.end < kid.end:
-                        self.end = kid.end
-                
-            kid.parent = self
-            
-            # alias for function
-            if rel != None:
-                setattr(self, rel, kid)
-                setattr(kid, "rel", rel)
-
-        # Block None kids when they should be related
-        if not kid and rel:
-            return
-            
-        return list.append(self, kid)
-
-    
-    def replace(self, kid, repl):
-        """Replaces the given kid with a replacement kid"""
-        
-        if repl in self:
-            self.remove(repl)
-        
-        self[self.index(kid)] = repl
-        
-        if hasattr(kid, "rel"):
-            repl.rel = kid.rel
-            setattr(self, kid.rel, repl)
-            
-            # cleanup old kid
-            delattr(kid, "rel")
-            
-            
-        elif hasattr(repl, "rel"):
-            # delete old relation on new child
-            delattr(repl, "rel")
-
-        delattr(kid, "parent")
-        repl.parent = self
-        
-        return kid
-        
-
-    def toXml(self, format=True, indent=0, tab="  "):
-        """Converts the node to XML"""
-
-        lead = tab * indent if format else ""
-        innerLead = tab * (indent+1) if format else ""
-        lineBreak = "\n" if format else ""
-
-        relatedChildren = []
-        attrsCollection = []
-        
-        for name in self.__slots__:
-            # "type" is used as node name - no need to repeat it as an attribute
-            # "parent" is a relation to the parent node - for serialization we ignore these at the moment
-            # "rel" is used internally to keep the relation to the parent - used by nodes which need to keep track of specific children
-            # "start" and "end" are for debugging only
-            if hasattr(self, name) and name not in ("type", "parent", "comments", "rel", "start", "end") and name[0] != "_":
-                value = getattr(self, name)
-                if isinstance(value, Node):
-                    if hasattr(value, "rel"):
-                        relatedChildren.append(value)
-
-                elif type(value) in (bool, int, float, str, list, set, dict):
-                    if type(value) == bool:
-                        value = "true" if value else "false" 
-                    elif type(value) in (int, float):
-                        value = str(value)
-                    elif type(value) in (list, set, dict):
-                        if type(value) == dict:
-                            value = value.keys()
-                        if len(value) == 0:
-                            continue
-                        try:
-                            value = ",".join(value)
-                        except TypeError:
-                            raise Exception("Invalid attribute list child at: %s" % name)
-                            
-                    attrsCollection.append('%s=%s' % (name, json.dumps(value)))
-
-        attrs = (" " + " ".join(attrsCollection)) if len(attrsCollection) > 0 else ""
-        
-        comments = getattr(self, "comments", None)
-        scope = getattr(self, "scope", None)
-        
-        if len(self) == 0 and len(relatedChildren) == 0 and (not comments or len(comments) == 0) and not scope:
-            result = "%s<%s%s/>%s" % (lead, self.type, attrs, lineBreak)
-
-        else:
-            result = "%s<%s%s>%s" % (lead, self.type, attrs, lineBreak)
-            
-            if comments:
-                for comment in comments:
-                    result += '%s<comment context="%s" variant="%s">%s</comment>%s' % (innerLead, comment.context, comment.variant, comment.text, lineBreak)
-                    
-            if scope:
-                for statKey in scope:
-                    statValue = scope[statKey]
-                    if statValue != None and len(statValue) > 0:
-                        if type(statValue) is set:
-                            statValue = ",".join(statValue)
-                        elif type(statValue) is dict:
-                            statValue = ",".join(statValue.keys())
-                        
-                        result += '%s<stat name="%s">%s</stat>%s' % (innerLead, statKey, statValue, lineBreak)
-
-            for child in self:
-                if not child:
-                    result += "%s<none/>%s" % (innerLead, lineBreak)
-                elif not hasattr(child, "rel"):
-                    result += child.toXml(format, indent+1)
-                elif not child in relatedChildren:
-                    raise Exception("Oops, irritated by non related: %s in %s - child says it is related as %s" % (child.type, self.type, child.rel))
-
-            for child in relatedChildren:
-                result += "%s<%s>%s" % (innerLead, child.rel, lineBreak)
-                result += child.toXml(format, indent+2)
-                result += "%s</%s>%s" % (innerLead, child.rel, lineBreak)
-
-            result += "%s</%s>%s" % (lead, self.type, lineBreak)
-
-        return result
-        
-        
-    def __deepcopy__(self, memo):
-        """Used by deepcopy function to clone Node instances"""
-        
-        # Create copy
-        if hasattr(self, "tokenizer"):
-            result = Node(tokenizer=self.tokenizer)
-        else:
-            result = Node(type=self.type)
-        
-        # Copy children
-        for child in self:
-            if child is None:
-                list.append(result, None)
-            else:
-                # Using simple list appends for better performance
-                childCopy = copy.deepcopy(child, memo)
-                childCopy.parent = result
-                list.append(result, childCopy)
-        
-        # Sync attributes
-        # Note: "parent" attribute is handled by append() already
-        for name in self.__slots__:
-            if hasattr(self, name) and not name in ("parent", "tokenizer"):
-                value = getattr(self, name)
-                if value is None:
-                    pass
-                elif type(value) in (bool, int, float, str):
-                    setattr(result, name, value)
-                elif type(value) in (list, set, dict, Node):
-                    setattr(result, name, copy.deepcopy(value, memo))
-                # Scope can be assigned (will be re-created when needed for the copied node)
-                elif name == "scope":
-                    result.scope = self.scope
-
-        return result
-        
-        
-    def getSource(self):
-        """Returns the source code of the node"""
-
-        if not self.tokenizer:
-            raise Exception("Could not find source for node '%s'" % node.type)
-            
-        if getattr(self, "start", None) is not None:
-            if getattr(self, "end", None) is not None:
-                return self.tokenizer.source[self.start:self.end]
-            return self.tokenizer.source[self.start:]
-    
-        if getattr(self, "end", None) is not None:
-            return self.tokenizer.source[:self.end]
-    
-        return self.tokenizer.source[:]
-
-
-    # Map Python built-ins
-    __repr__ = toXml
-    __str__ = toXml
-    
-    
-    def __eq__(self, other):
-        return self is other
-
-    def __bool__(self): 
-        return True
--- a/ThirdParty/Jasy/jasy/js/parse/Parser.py	Thu Jan 10 14:23:49 2019 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1448 +0,0 @@
-#
-# Jasy - Web Tooling Framework
-# Copyright 2010-2012 Zynga Inc.
-# Copyright 2013-2014 Sebastian Werner
-#
-
-#
-# License: MPL 1.1/GPL 2.0/LGPL 2.1
-# Authors:
-#   - Brendan Eich <brendan@mozilla.org> (Original JavaScript) (2004-2010)
-#   - Sebastian Werner <info@sebastian-werner.net> (Python Port) (2010-2012)
-#
-
-from __future__ import unicode_literals
-
-import jasy.js.tokenize.Tokenizer
-import jasy.js.parse.VanillaBuilder
-import jasy.js.tokenize.Lang
-
-__all__ = [ "parse", "parseExpression" ]
-
-def parseExpression(source, fileId=None, line=1, builder=None):
-    if builder == None:
-        builder = jasy.js.parse.VanillaBuilder.VanillaBuilder()
-
-    # Convert source into expression statement to be friendly to the Tokenizer
-    if not source.endswith(";"):
-        source = source + ";"
-
-    tokenizer = jasy.js.tokenize.Tokenizer.Tokenizer(source, fileId, line)
-    staticContext = StaticContext(False, builder)
-
-    return Expression(tokenizer, staticContext)
-
-
-
-def parse(source, fileId=None, line=1, builder=None):
-    if builder == None:
-        builder = jasy.js.parse.VanillaBuilder.VanillaBuilder()
-
-    tokenizer = jasy.js.tokenize.Tokenizer.Tokenizer(source, fileId, line)
-    staticContext = StaticContext(False, builder)
-    node = Script(tokenizer, staticContext)
-
-    # store fileId on top-level node
-    node.fileId = tokenizer.fileId
-
-    # add missing comments e.g. empty file with only a comment etc.
-    # if there is something non-attached by an inner node it is attached to
-    # the top level node, which is not correct, but might be better than
-    # just ignoring the comment after all.
-    if len(node) > 0:
-        builder.COMMENTS_add(node[-1], None, tokenizer.getComments())
-    else:
-        builder.COMMENTS_add(node, None, tokenizer.getComments())
-
-    if not tokenizer.done():
-        raise SyntaxError("Unexpected end of file", tokenizer)
-
-    return node
-
-
-
-class SyntaxError(Exception):
-    def __init__(self, message, tokenizer):
-        Exception.__init__(self, "Syntax error: %s\n%s:%s" % (message, tokenizer.fileId, tokenizer.line))
-
-
-# Used as a status container during tree-building for every def body and the global body
-class StaticContext(object):
-    # inFunction is used to check if a return stm appears in a valid context.
-    def __init__(self, inFunction, builder):
-        # Whether this is inside a function, mostly True, only for top-level scope it's False
-        self.inFunction = inFunction
-
-        self.hasEmptyReturn = False
-        self.hasReturnWithValue = False
-        self.isGenerator = False
-        self.blockId = 0
-        self.builder = builder
-        self.statementStack = []
-
-        # Sets to store variable uses
-        # self.functions = set()
-        # self.variables = set()
-
-        # Status
-        # self.needsHoisting = False
-        self.bracketLevel = 0
-        self.curlyLevel = 0
-        self.parenLevel = 0
-        self.hookLevel = 0
-
-        # Configure strict ecmascript 3 mode
-        self.ecma3OnlyMode = False
-
-        # Status flag during parsing
-        self.inForLoopInit = False
-
-
-def Script(tokenizer, staticContext):
-    """Parses the toplevel and def bodies."""
-    node = Statements(tokenizer, staticContext)
-
-    # change type from "block" to "script" for script root
-    node.type = "script"
-
-    # copy over data from compiler context
-    # node.functions = staticContext.functions
-    # node.variables = staticContext.variables
-
-    return node
-
-
-def nest(tokenizer, staticContext, node, func, end=None):
-    """Statement stack and nested statement handler."""
-    staticContext.statementStack.append(node)
-    node = func(tokenizer, staticContext)
-    staticContext.statementStack.pop()
-    end and tokenizer.mustMatch(end)
-
-    return node
-
-
-def Statements(tokenizer, staticContext):
-    """Parses a list of Statements."""
-
-    builder = staticContext.builder
-    node = builder.BLOCK_build(tokenizer, staticContext.blockId)
-    staticContext.blockId += 1
-
-    builder.BLOCK_hoistLets(node)
-    staticContext.statementStack.append(node)
-
-    prevNode = None
-    while not tokenizer.done() and tokenizer.peek(True) != "right_curly":
-        comments = tokenizer.getComments()
-        childNode = Statement(tokenizer, staticContext)
-        builder.COMMENTS_add(childNode, prevNode, comments)
-        builder.BLOCK_addStatement(node, childNode)
-        prevNode = childNode
-
-    staticContext.statementStack.pop()
-    builder.BLOCK_finish(node)
-
-    # if getattr(node, "needsHoisting", False):
-    #     # TODO
-    #     raise Exception("Needs hoisting went true!!!")
-    #     builder.setHoists(node.id, node.variables)
-    #     # Propagate up to the function.
-    #     staticContext.needsHoisting = True
-
-    return node
-
-
-def Block(tokenizer, staticContext):
-    tokenizer.mustMatch("left_curly")
-    node = Statements(tokenizer, staticContext)
-    tokenizer.mustMatch("right_curly")
-
-    return node
-
-
-def Statement(tokenizer, staticContext):
-    """Parses a Statement."""
-
-    tokenType = tokenizer.get(True)
-    builder = staticContext.builder
-
-    # Cases for statements ending in a right curly return early, avoiding the
-    # common semicolon insertion magic after this switch.
-
-    if tokenType == "function":
-        # "declared_form" extends functions of staticContext, "statement_form" doesn'tokenizer.
-        if len(staticContext.statementStack) > 1:
-            kind = "statement_form"
-        else:
-            kind = "declared_form"
-
-        return FunctionDefinition(tokenizer, staticContext, True, kind)
-
-
-    elif tokenType == "left_curly":
-        node = Statements(tokenizer, staticContext)
-        tokenizer.mustMatch("right_curly")
-
-        return node
-
-
-    elif tokenType == "if":
-        node = builder.IF_build(tokenizer)
-        builder.IF_setCondition(node, ParenExpression(tokenizer, staticContext))
-        staticContext.statementStack.append(node)
-        builder.IF_setThenPart(node, Statement(tokenizer, staticContext))
-
-        if tokenizer.match("else"):
-            comments = tokenizer.getComments()
-            elsePart = Statement(tokenizer, staticContext)
-            builder.COMMENTS_add(elsePart, node, comments)
-            builder.IF_setElsePart(node, elsePart)
-
-        staticContext.statementStack.pop()
-        builder.IF_finish(node)
-
-        return node
-
-
-    elif tokenType == "switch":
-        # This allows CASEs after a "default", which is in the standard.
-        node = builder.SWITCH_build(tokenizer)
-        builder.SWITCH_setDiscriminant(node, ParenExpression(tokenizer, staticContext))
-        staticContext.statementStack.append(node)
-
-        tokenizer.mustMatch("left_curly")
-        tokenType = tokenizer.get()
-
-        while tokenType != "right_curly":
-            if tokenType == "default":
-                if node.defaultIndex >= 0:
-                    raise SyntaxError("More than one switch default", tokenizer)
-
-                childNode = builder.DEFAULT_build(tokenizer)
-                builder.SWITCH_setDefaultIndex(node, len(node)-1)
-                tokenizer.mustMatch("colon")
-                builder.DEFAULT_initializeStatements(childNode, tokenizer)
-
-                while True:
-                    tokenType=tokenizer.peek(True)
-                    if tokenType == "case" or tokenType == "default" or tokenType == "right_curly":
-                        break
-                    builder.DEFAULT_addStatement(childNode, Statement(tokenizer, staticContext))
-
-                builder.DEFAULT_finish(childNode)
-
-            elif tokenType == "case":
-                childNode = builder.CASE_build(tokenizer)
-                builder.CASE_setLabel(childNode, Expression(tokenizer, staticContext))
-                tokenizer.mustMatch("colon")
-                builder.CASE_initializeStatements(childNode, tokenizer)
-
-                while True:
-                    tokenType=tokenizer.peek(True)
-                    if tokenType == "case" or tokenType == "default" or tokenType == "right_curly":
-                        break
-                    builder.CASE_addStatement(childNode, Statement(tokenizer, staticContext))
-
-                builder.CASE_finish(childNode)
-
-            else:
-                raise SyntaxError("Invalid switch case", tokenizer)
-
-            builder.SWITCH_addCase(node, childNode)
-            tokenType = tokenizer.get()
-
-        staticContext.statementStack.pop()
-        builder.SWITCH_finish(node)
-
-        return node
-
-
-    elif tokenType == "for":
-        node = builder.FOR_build(tokenizer)
-        forBlock = None
-
-        if tokenizer.match("identifier") and tokenizer.token.value == "each":
-            builder.FOR_rebuildForEach(node)
-
-        tokenizer.mustMatch("left_paren")
-        tokenType = tokenizer.peek()
-        childNode = None
-
-        if tokenType != "semicolon":
-            staticContext.inForLoopInit = True
-
-            if tokenType == "var" or tokenType == "const":
-                tokenizer.get()
-                childNode = Variables(tokenizer, staticContext)
-
-            elif tokenType == "let":
-                tokenizer.get()
-
-                if tokenizer.peek() == "left_paren":
-                    childNode = LetBlock(tokenizer, staticContext, False)
-
-                else:
-                    # Let in for head, we need to add an implicit block
-                    # around the rest of the for.
-                    forBlock = builder.BLOCK_build(tokenizer, staticContext.blockId)
-                    staticContext.blockId += 1
-                    staticContext.statementStack.append(forBlock)
-                    childNode = Variables(tokenizer, staticContext, forBlock)
-
-            else:
-                childNode = Expression(tokenizer, staticContext)
-
-            staticContext.inForLoopInit = False
-
-        if childNode and tokenizer.match("in"):
-            builder.FOR_rebuildForIn(node)
-            builder.FOR_setObject(node, Expression(tokenizer, staticContext), forBlock)
-
-            if childNode.type == "var" or childNode.type == "let":
-                if len(childNode) != 1:
-                    raise SyntaxError("Invalid for..in left-hand side", tokenizer)
-
-                builder.FOR_setIterator(node, childNode, forBlock)
-
-            else:
-                builder.FOR_setIterator(node, childNode, forBlock)
-
-        else:
-            builder.FOR_setSetup(node, childNode)
-            tokenizer.mustMatch("semicolon")
-
-            if node.isEach:
-                raise SyntaxError("Invalid for each..in loop", tokenizer)
-
-            if tokenizer.peek() == "semicolon":
-                builder.FOR_setCondition(node, None)
-            else:
-                builder.FOR_setCondition(node, Expression(tokenizer, staticContext))
-
-            tokenizer.mustMatch("semicolon")
-
-            if tokenizer.peek() == "right_paren":
-                builder.FOR_setUpdate(node, None)
-            else:
-                builder.FOR_setUpdate(node, Expression(tokenizer, staticContext))
-
-        tokenizer.mustMatch("right_paren")
-        builder.FOR_setBody(node, nest(tokenizer, staticContext, node, Statement))
-
-        if forBlock:
-            builder.BLOCK_finish(forBlock)
-            staticContext.statementStack.pop()
-
-        builder.FOR_finish(node)
-        return node
-
-
-    elif tokenType == "while":
-        node = builder.WHILE_build(tokenizer)
-
-        builder.WHILE_setCondition(node, ParenExpression(tokenizer, staticContext))
-        builder.WHILE_setBody(node, nest(tokenizer, staticContext, node, Statement))
-        builder.WHILE_finish(node)
-
-        return node
-
-
-    elif tokenType == "do":
-        node = builder.DO_build(tokenizer)
-
-        builder.DO_setBody(node, nest(tokenizer, staticContext, node, Statement, "while"))
-        builder.DO_setCondition(node, ParenExpression(tokenizer, staticContext))
-        builder.DO_finish(node)
-
-        if not staticContext.ecma3OnlyMode:
-            # <script language="JavaScript"> (without version hints) may need
-            # automatic semicolon insertion without a newline after do-while.
-            # See http://bugzilla.mozilla.org/show_bug.cgi?id=238945.
-            tokenizer.match("semicolon")
-            return node
-
-        # NO RETURN
-
-
-    elif tokenType == "break" or tokenType == "continue":
-        if tokenType == "break":
-            node = builder.BREAK_build(tokenizer)
-        else:
-            node = builder.CONTINUE_build(tokenizer)
-
-        if tokenizer.peekOnSameLine() == "identifier":
-            tokenizer.get()
-
-            if tokenType == "break":
-                builder.BREAK_setLabel(node, tokenizer.token.value)
-            else:
-                builder.CONTINUE_setLabel(node, tokenizer.token.value)
-
-        statementStack = staticContext.statementStack
-        i = len(statementStack)
-        label = node.label if hasattr(node, "label") else None
-
-        if label:
-            while True:
-                i -= 1
-                if i < 0:
-                    raise SyntaxError("Label not found", tokenizer)
-                if getattr(statementStack[i], "label", None) == label:
-                    break
-
-            #
-            # Both break and continue to label need to be handled specially
-            # within a labeled loop, so that they target that loop. If not in
-            # a loop, then break targets its labeled statement. Labels can be
-            # nested so we skip all labels immediately enclosing the nearest
-            # non-label statement.
-            #
-            while i < len(statementStack) - 1 and statementStack[i+1].type == "label":
-                i += 1
-
-            if i < len(statementStack) - 1 and getattr(statementStack[i+1], "isLoop", False):
-                i += 1
-            elif tokenType == "continue":
-                raise SyntaxError("Invalid continue", tokenizer)
-
-        else:
-            while True:
-                i -= 1
-                if i < 0:
-                    if tokenType == "break":
-                        raise SyntaxError("Invalid break", tokenizer)
-                    else:
-                        raise SyntaxError("Invalid continue", tokenizer)
-
-                if getattr(statementStack[i], "isLoop", False) or (tokenType == "break" and statementStack[i].type == "switch"):
-                    break
-
-        if tokenType == "break":
-            builder.BREAK_finish(node)
-        else:
-            builder.CONTINUE_finish(node)
-
-        # NO RETURN
-
-
-    elif tokenType == "try":
-        node = builder.TRY_build(tokenizer)
-        builder.TRY_setTryBlock(node, Block(tokenizer, staticContext))
-
-        while tokenizer.match("catch"):
-            childNode = builder.CATCH_build(tokenizer)
-            tokenizer.mustMatch("left_paren")
-            nextTokenType = tokenizer.get()
-
-            if nextTokenType == "left_bracket" or nextTokenType == "left_curly":
-                # Destructured catch identifiers.
-                tokenizer.unget()
-                exception = DestructuringExpression(tokenizer, staticContext, True)
-
-            elif nextTokenType == "identifier":
-                exception = builder.CATCH_wrapException(tokenizer)
-
-            else:
-                raise SyntaxError("Missing identifier in catch", tokenizer)
-
-            builder.CATCH_setException(childNode, exception)
-
-            if tokenizer.match("if"):
-                if staticContext.ecma3OnlyMode:
-                    raise SyntaxError("Illegal catch guard", tokenizer)
-
-                if node.getChildrenLength() > 0 and not node.getUnrelatedChildren()[0].guard:
-                    raise SyntaxError("Guarded catch after unguarded", tokenizer)
-
-                builder.CATCH_setGuard(childNode, Expression(tokenizer, staticContext))
-
-            else:
-                builder.CATCH_setGuard(childNode, None)
-
-            tokenizer.mustMatch("right_paren")
-
-            builder.CATCH_setBlock(childNode, Block(tokenizer, staticContext))
-            builder.CATCH_finish(childNode)
-
-            builder.TRY_addCatch(node, childNode)
-
-        builder.TRY_finishCatches(node)
-
-        if tokenizer.match("finally"):
-            builder.TRY_setFinallyBlock(node, Block(tokenizer, staticContext))
-
-        if node.getChildrenLength() == 0 and not hasattr(node, "finallyBlock"):
-            raise SyntaxError("Invalid try statement", tokenizer)
-
-        builder.TRY_finish(node)
-        return node
-
-
-    elif tokenType == "catch" or tokenType == "finally":
-        raise SyntaxError(tokens[tokenType] + " without preceding try", tokenizer)
-
-
-    elif tokenType == "throw":
-        node = builder.THROW_build(tokenizer)
-
-        builder.THROW_setException(node, Expression(tokenizer, staticContext))
-        builder.THROW_finish(node)
-
-        # NO RETURN
-
-
-    elif tokenType == "return":
-        node = returnOrYield(tokenizer, staticContext)
-
-        # NO RETURN
-
-
-    elif tokenType == "with":
-        node = builder.WITH_build(tokenizer)
-
-        builder.WITH_setObject(node, ParenExpression(tokenizer, staticContext))
-        builder.WITH_setBody(node, nest(tokenizer, staticContext, node, Statement))
-        builder.WITH_finish(node)
-
-        return node
-
-
-    elif tokenType == "var" or tokenType == "const":
-        node = Variables(tokenizer, staticContext)
-
-        # NO RETURN
-
-
-    elif tokenType == "let":
-        if tokenizer.peek() == "left_paren":
-            node = LetBlock(tokenizer, staticContext, True)
-        else:
-            node = Variables(tokenizer, staticContext)
-
-        # NO RETURN
-
-
-    elif tokenType == "debugger":
-        node = builder.DEBUGGER_build(tokenizer)
-
-        # NO RETURN
-
-
-    elif tokenType == "newline" or tokenType == "semicolon":
-        node = builder.SEMICOLON_build(tokenizer)
-
-        builder.SEMICOLON_setExpression(node, None)
-        builder.SEMICOLON_finish(tokenizer)
-
-        return node
-
-
-    else:
-        if tokenType == "identifier":
-            tokenType = tokenizer.peek()
-
-            # Labeled statement.
-            if tokenType == "colon":
-                label = tokenizer.token.value
-                statementStack = staticContext.statementStack
-
-                i = len(statementStack)-1
-                while i >= 0:
-                    if getattr(statementStack[i], "label", None) == label:
-                        raise SyntaxError("Duplicate label", tokenizer)
-
-                    i -= 1
-
-                tokenizer.get()
-                node = builder.LABEL_build(tokenizer)
-
-                builder.LABEL_setLabel(node, label)
-                builder.LABEL_setStatement(node, nest(tokenizer, staticContext, node, Statement))
-                builder.LABEL_finish(node)
-
-                return node
-
-        # Expression statement.
-        # We unget the current token to parse the expression as a whole.
-        node = builder.SEMICOLON_build(tokenizer)
-        tokenizer.unget()
-        builder.SEMICOLON_setExpression(node, Expression(tokenizer, staticContext))
-        node.end = node.expression.end
-        builder.SEMICOLON_finish(node)
-
-        # NO RETURN
-
-
-    MagicalSemicolon(tokenizer)
-    return node
-
-
-
-def MagicalSemicolon(tokenizer):
-    if tokenizer.line == tokenizer.token.line:
-        tokenType = tokenizer.peekOnSameLine()
-
-        if tokenType != "end" and tokenType != "newline" and tokenType != "semicolon" and tokenType != "right_curly":
-            raise SyntaxError("Missing ; before statement", tokenizer)
-
-    tokenizer.match("semicolon")
-
-
-
-def returnOrYield(tokenizer, staticContext):
-    builder = staticContext.builder
-    tokenType = tokenizer.token.type
-
-    if tokenType == "return":
-        if not staticContext.inFunction:
-            raise SyntaxError("Return not in function", tokenizer)
-
-        node = builder.RETURN_build(tokenizer)
-
-    else:
-        if not staticContext.inFunction:
-            raise SyntaxError("Yield not in function", tokenizer)
-
-        staticContext.isGenerator = True
-        node = builder.YIELD_build(tokenizer)
-
-    nextTokenType = tokenizer.peek(True)
-    if nextTokenType != "end" and nextTokenType != "newline" and nextTokenType != "semicolon" and nextTokenType != "right_curly" and (tokenType != "yield" or (nextTokenType != tokenType and nextTokenType != "right_bracket" and nextTokenType != "right_paren" and nextTokenType != "colon" and nextTokenType != "comma")):
-        if tokenType == "return":
-            builder.RETURN_setValue(node, Expression(tokenizer, staticContext))
-            staticContext.hasReturnWithValue = True
-        else:
-            builder.YIELD_setValue(node, AssignExpression(tokenizer, staticContext))
-
-    elif tokenType == "return":
-        staticContext.hasEmptyReturn = True
-
-    # Disallow return v; in generator.
-    if staticContext.hasReturnWithValue and staticContext.isGenerator:
-        raise SyntaxError("Generator returns a value", tokenizer)
-
-    if tokenType == "return":
-        builder.RETURN_finish(node)
-    else:
-        builder.YIELD_finish(node)
-
-    return node
-
-
-
-def FunctionDefinition(tokenizer, staticContext, requireName, functionForm):
-    builder = staticContext.builder
-    functionNode = builder.FUNCTION_build(tokenizer)
-
-    if tokenizer.match("identifier"):
-        builder.FUNCTION_setName(functionNode, tokenizer.token.value)
-    elif requireName:
-        raise SyntaxError("Missing def identifier", tokenizer)
-
-    tokenizer.mustMatch("left_paren")
-
-    if not tokenizer.match("right_paren"):
-        builder.FUNCTION_initParams(functionNode, tokenizer)
-        prevParamNode = None
-        while True:
-            tokenType = tokenizer.get()
-            if tokenType == "left_bracket" or tokenType == "left_curly":
-                # Destructured formal parameters.
-                tokenizer.unget()
-                paramNode = DestructuringExpression(tokenizer, staticContext)
-
-            elif tokenType == "identifier":
-                paramNode = builder.FUNCTION_wrapParam(tokenizer)
-
-            else:
-                raise SyntaxError("Missing formal parameter", tokenizer)
-
-            builder.FUNCTION_addParam(functionNode, tokenizer, paramNode)
-            builder.COMMENTS_add(paramNode, prevParamNode, tokenizer.getComments())
-
-            if not tokenizer.match("comma"):
-                break
-
-            prevParamNode = paramNode
-
-        tokenizer.mustMatch("right_paren")
-
-    # Do we have an expression closure or a normal body?
-    tokenType = tokenizer.get()
-    if tokenType != "left_curly":
-        builder.FUNCTION_setExpressionClosure(functionNode, True)
-        tokenizer.unget()
-
-    childContext = StaticContext(True, builder)
-
-    if staticContext.inFunction:
-        # Inner functions don't reset block numbering, only functions at
-        # the top level of the program do.
-        childContext.blockId = staticContext.blockId
-
-    if tokenType != "left_curly":
-        builder.FUNCTION_setBody(functionNode, AssignExpression(tokenizer, staticContext))
-        if staticContext.isGenerator:
-            raise SyntaxError("Generator returns a value", tokenizer)
-
-    else:
-        builder.FUNCTION_hoistVars(childContext.blockId)
-        builder.FUNCTION_setBody(functionNode, Script(tokenizer, childContext))
-
-    if tokenType == "left_curly":
-        tokenizer.mustMatch("right_curly")
-
-    functionNode.end = tokenizer.token.end
-    functionNode.functionForm = functionForm
-
-    builder.COMMENTS_add(functionNode.body, functionNode.body, tokenizer.getComments())
-    builder.FUNCTION_finish(functionNode, staticContext)
-
-    return functionNode
-
-
-
-def Variables(tokenizer, staticContext, letBlock=None):
-    """Parses a comma-separated list of var declarations (and maybe initializations)."""
-
-    builder = staticContext.builder
-    if tokenizer.token.type == "var":
-        build = builder.VAR_build
-        addDecl = builder.VAR_addDecl
-        finish = builder.VAR_finish
-        childContext = staticContext
-
-    elif tokenizer.token.type == "const":
-        build = builder.CONST_build
-        addDecl = builder.CONST_addDecl
-        finish = builder.CONST_finish
-        childContext = staticContext
-
-    elif tokenizer.token.type == "let" or tokenizer.token.type == "left_paren":
-        build = builder.LET_build
-        addDecl = builder.LET_addDecl
-        finish = builder.LET_finish
-
-        if not letBlock:
-            statementStack = staticContext.statementStack
-            i = len(statementStack) - 1
-
-            # a BLOCK *must* be found.
-            while statementStack[i].type != "block":
-                i -= 1
-
-            # Lets at the def toplevel are just vars, at least in SpiderMonkey.
-            if i == 0:
-                build = builder.VAR_build
-                addDecl = builder.VAR_addDecl
-                finish = builder.VAR_finish
-                childContext = staticContext
-
-            else:
-                childContext = statementStack[i]
-
-        else:
-            childContext = letBlock
-
-    node = build(tokenizer)
-
-    while True:
-        tokenType = tokenizer.get()
-
-        # Done in Python port!
-        # FIXME Should have a special DECLARATION node instead of overloading
-        # IDENTIFIER to mean both identifier declarations and destructured
-        # declarations.
-        childNode = builder.DECL_build(tokenizer)
-
-        if tokenType == "left_bracket" or tokenType == "left_curly":
-            # Pass in childContext if we need to add each pattern matched into
-            # its variables, else pass in staticContext.
-            # Need to unget to parse the full destructured expression.
-            tokenizer.unget()
-            builder.DECL_setNames(childNode, DestructuringExpression(tokenizer, staticContext, True, childContext))
-
-            if staticContext.inForLoopInit and tokenizer.peek() == "in":
-                addDecl(node, childNode, childContext)
-                if tokenizer.match("comma"):
-                    continue
-                else:
-                    break
-
-            tokenizer.mustMatch("assign")
-            if tokenizer.token.assignOp:
-                raise SyntaxError("Invalid variable initialization", tokenizer)
-
-            # Parse the init as a normal assignment.
-            builder.DECL_setInitializer(childNode, AssignExpression(tokenizer, staticContext))
-            builder.DECL_finish(childNode)
-            addDecl(node, childNode, childContext)
-
-            # Copy over names for variable list
-            # for nameNode in childNode.names:
-            #    childContext.variables.add(nameNode.value)
-
-            if tokenizer.match("comma"):
-                continue
-            else:
-                break
-
-        if tokenType != "identifier":
-            raise SyntaxError("Missing variable name", tokenizer)
-
-        builder.DECL_setName(childNode, tokenizer.token.value)
-        builder.DECL_setReadOnly(childNode, node.type == "const")
-        addDecl(node, childNode, childContext)
-
-        if tokenizer.match("assign"):
-            if tokenizer.token.assignOp:
-                raise SyntaxError("Invalid variable initialization", tokenizer)
-
-            initializerNode = AssignExpression(tokenizer, staticContext)
-            builder.DECL_setInitializer(childNode, initializerNode)
-
-        builder.DECL_finish(childNode)
-
-        # If we directly use the node in "let" constructs
-        # if not hasattr(childContext, "variables"):
-        #    childContext.variables = set()
-
-        # childContext.variables.add(childNode.name)
-
-        if not tokenizer.match("comma"):
-            break
-
-    finish(node)
-    return node
-
-
-
-def LetBlock(tokenizer, staticContext, isStatement):
-    """Does not handle let inside of for loop init."""
-    builder = staticContext.builder
-
-    # tokenizer.token.type must be "let"
-    node = builder.LETBLOCK_build(tokenizer)
-    tokenizer.mustMatch("left_paren")
-    builder.LETBLOCK_setVariables(node, Variables(tokenizer, staticContext, node))
-    tokenizer.mustMatch("right_paren")
-
-    if isStatement and tokenizer.peek() != "left_curly":
-        # If this is really an expression in let statement guise, then we
-        # need to wrap the "let_block" node in a "semicolon" node so that we pop
-        # the return value of the expression.
-        childNode = builder.SEMICOLON_build(tokenizer)
-        builder.SEMICOLON_setExpression(childNode, node)
-        builder.SEMICOLON_finish(childNode)
-        isStatement = False
-
-    if isStatement:
-        childNode = Block(tokenizer, staticContext)
-        builder.LETBLOCK_setBlock(node, childNode)
-
-    else:
-        childNode = AssignExpression(tokenizer, staticContext)
-        builder.LETBLOCK_setExpression(node, childNode)
-
-    builder.LETBLOCK_finish(node)
-    return node
-
-
-def checkDestructuring(tokenizer, staticContext, node, simpleNamesOnly=None, data=None):
-    if node.type == "array_comp":
-        raise SyntaxError("Invalid array comprehension left-hand side", tokenizer)
-
-    if node.type != "array_init" and node.type != "object_init":
-        return
-
-    builder = staticContext.builder
-
-    for child in node:
-        if child == None:
-            continue
-
-        if child.type == "property_init":
-            lhs = child[0]
-            rhs = child[1]
-        else:
-            lhs = None
-            rhs = None
-
-
-        if rhs and (rhs.type == "array_init" or rhs.type == "object_init"):
-            checkDestructuring(tokenizer, staticContext, rhs, simpleNamesOnly, data)
-
-        if lhs and simpleNamesOnly:
-            # In declarations, lhs must be simple names
-            if lhs.type != "identifier":
-                raise SyntaxError("Missing name in pattern", tokenizer)
-
-            elif data:
-                childNode = builder.DECL_build(tokenizer)
-                builder.DECL_setName(childNode, lhs.value)
-
-                # Don't need to set initializer because it's just for
-                # hoisting anyways.
-                builder.DECL_finish(childNode)
-
-                # Each pattern needs to be added to variables.
-                # data.variables.add(childNode.name)
-
-
-# JavaScript 1.7
-def DestructuringExpression(tokenizer, staticContext, simpleNamesOnly=None, data=None):
-    node = PrimaryExpression(tokenizer, staticContext)
-    checkDestructuring(tokenizer, staticContext, node, simpleNamesOnly, data)
-
-    return node
-
-
-# JavsScript 1.7
-def GeneratorExpression(tokenizer, staticContext, expression):
-    builder = staticContext.builder
-    node = builder.GENERATOR_build(tokenizer)
-
-    builder.GENERATOR_setExpression(node, expression)
-    builder.GENERATOR_setTail(node, comprehensionTail(tokenizer, staticContext))
-    builder.GENERATOR_finish(node)
-
-    return node
-
-
-# JavaScript 1.7 Comprehensions Tails (Generators / Arrays)
-def comprehensionTail(tokenizer, staticContext):
-    builder = staticContext.builder
-
-    # tokenizer.token.type must be "for"
-    body = builder.COMPTAIL_build(tokenizer)
-
-    while True:
-        node = builder.FOR_build(tokenizer)
-
-        # Comprehension tails are always for..in loops.
-        builder.FOR_rebuildForIn(node)
-        if tokenizer.match("identifier"):
-            # But sometimes they're for each..in.
-            if tokenizer.token.value == "each":
-                builder.FOR_rebuildForEach(node)
-            else:
-                tokenizer.unget()
-
-        tokenizer.mustMatch("left_paren")
-
-        tokenType = tokenizer.get()
-        if tokenType == "left_bracket" or tokenType == "left_curly":
-            tokenizer.unget()
-            # Destructured left side of for in comprehension tails.
-            builder.FOR_setIterator(node, DestructuringExpression(tokenizer, staticContext))
-
-        elif tokenType == "identifier":
-            # Removed variable/declaration substructure in Python port.
-            # Variable declarations are not allowed here. So why process them in such a way?
-
-            # declaration = builder.DECL_build(tokenizer)
-            # builder.DECL_setName(declaration, tokenizer.token.value)
-            # builder.DECL_finish(declaration)
-            # childNode = builder.VAR_build(tokenizer)
-            # builder.VAR_addDecl(childNode, declaration)
-            # builder.VAR_finish(childNode)
-            # builder.FOR_setIterator(node, declaration)
-
-            # Don't add to variables since the semantics of comprehensions is
-            # such that the variables are in their own def when desugared.
-
-            identifier = builder.PRIMARY_build(tokenizer, "identifier")
-            builder.FOR_setIterator(node, identifier)
-
-        else:
-            raise SyntaxError("Missing identifier", tokenizer)
-
-        tokenizer.mustMatch("in")
-        builder.FOR_setObject(node, Expression(tokenizer, staticContext))
-        tokenizer.mustMatch("right_paren")
-        builder.COMPTAIL_addFor(body, node)
-
-        if not tokenizer.match("for"):
-            break
-
-    # Optional guard.
-    if tokenizer.match("if"):
-        builder.COMPTAIL_setGuard(body, ParenExpression(tokenizer, staticContext))
-
-    builder.COMPTAIL_finish(body)
-
-    return body
-
-
-def ParenExpression(tokenizer, staticContext):
-    tokenizer.mustMatch("left_paren")
-
-    # Always accept the 'in' operator in a parenthesized expression,
-    # where it's unambiguous, even if we might be parsing the init of a
-    # for statement.
-    oldLoopInit = staticContext.inForLoopInit
-    staticContext.inForLoopInit = False
-    node = Expression(tokenizer, staticContext)
-    staticContext.inForLoopInit = oldLoopInit
-
-    err = "expression must be parenthesized"
-    if tokenizer.match("for"):
-        if node.type == "yield" and not node.parenthesized:
-            raise SyntaxError("Yield " + err, tokenizer)
-
-        if node.type == "comma" and not node.parenthesized:
-            raise SyntaxError("Generator " + err, tokenizer)
-
-        node = GeneratorExpression(tokenizer, staticContext, node)
-
-    tokenizer.mustMatch("right_paren")
-
-    return node
-
-
-def Expression(tokenizer, staticContext):
-    """Top-down expression parser matched against SpiderMonkey."""
-    builder = staticContext.builder
-    node = AssignExpression(tokenizer, staticContext)
-
-    if tokenizer.match("comma"):
-        childNode = builder.COMMA_build(tokenizer)
-        builder.COMMA_addOperand(childNode, node)
-        node = childNode
-        while True:
-            childNode = node[len(node)-1]
-            if childNode.type == "yield" and not childNode.parenthesized:
-                raise SyntaxError("Yield expression must be parenthesized", tokenizer)
-            builder.COMMA_addOperand(node, AssignExpression(tokenizer, staticContext))
-
-            if not tokenizer.match("comma"):
-                break
-
-        builder.COMMA_finish(node)
-
-    return node
-
-
-def AssignExpression(tokenizer, staticContext):
-    builder = staticContext.builder
-
-    # Have to treat yield like an operand because it could be the leftmost
-    # operand of the expression.
-    if tokenizer.match("yield", True):
-        return returnOrYield(tokenizer, staticContext)
-
-    comments = tokenizer.getComments()
-    node = builder.ASSIGN_build(tokenizer)
-    lhs = ConditionalExpression(tokenizer, staticContext)
-    builder.COMMENTS_add(lhs, None, comments)
-
-    if not tokenizer.match("assign"):
-        builder.ASSIGN_finish(node)
-        return lhs
-
-    if lhs.type == "object_init" or lhs.type == "array_init":
-        checkDestructuring(tokenizer, staticContext, lhs)
-    elif lhs.type == "identifier" or lhs.type == "dot" or lhs.type == "index" or lhs.type == "call":
-        pass
-    else:
-        raise SyntaxError("Bad left-hand side of assignment", tokenizer)
-
-    builder.ASSIGN_setAssignOp(node, tokenizer.token.assignOp)
-    builder.ASSIGN_addOperand(node, lhs)
-    builder.ASSIGN_addOperand(node, AssignExpression(tokenizer, staticContext))
-    builder.ASSIGN_finish(node)
-
-    return node
-
-
-def ConditionalExpression(tokenizer, staticContext):
-    builder = staticContext.builder
-    node = OrExpression(tokenizer, staticContext)
-
-    if tokenizer.match("hook"):
-        childNode = node
-        node = builder.HOOK_build(tokenizer)
-        builder.HOOK_setCondition(node, childNode)
-
-        # Always accept the 'in' operator in the middle clause of a ternary,
-        # where it's unambiguous, even if we might be parsing the init of a
-        # for statement.
-        oldLoopInit = staticContext.inForLoopInit
-        staticContext.inForLoopInit = False
-        builder.HOOK_setThenPart(node, AssignExpression(tokenizer, staticContext))
-        staticContext.inForLoopInit = oldLoopInit
-
-        if not tokenizer.match("colon"):
-            raise SyntaxError("Missing : after ?", tokenizer)
-
-        builder.HOOK_setElsePart(node, AssignExpression(tokenizer, staticContext))
-        builder.HOOK_finish(node)
-
-    return node
-
-
-def OrExpression(tokenizer, staticContext):
-    builder = staticContext.builder
-    node = AndExpression(tokenizer, staticContext)
-
-    while tokenizer.match("or"):
-        childNode = builder.OR_build(tokenizer)
-        builder.OR_addOperand(childNode, node)
-        builder.OR_addOperand(childNode, AndExpression(tokenizer, staticContext))
-        builder.OR_finish(childNode)
-        node = childNode
-
-    return node
-
-
-def AndExpression(tokenizer, staticContext):
-    builder = staticContext.builder
-    node = BitwiseOrExpression(tokenizer, staticContext)
-
-    while tokenizer.match("and"):
-        childNode = builder.AND_build(tokenizer)
-        builder.AND_addOperand(childNode, node)
-        builder.AND_addOperand(childNode, BitwiseOrExpression(tokenizer, staticContext))
-        builder.AND_finish(childNode)
-        node = childNode
-
-    return node
-
-
-def BitwiseOrExpression(tokenizer, staticContext):
-    builder = staticContext.builder
-    node = BitwiseXorExpression(tokenizer, staticContext)
-
-    while tokenizer.match("bitwise_or"):
-        childNode = builder.BITWISEOR_build(tokenizer)
-        builder.BITWISEOR_addOperand(childNode, node)
-        builder.BITWISEOR_addOperand(childNode, BitwiseXorExpression(tokenizer, staticContext))
-        builder.BITWISEOR_finish(childNode)
-        node = childNode
-
-    return node
-
-
-def BitwiseXorExpression(tokenizer, staticContext):
-    builder = staticContext.builder
-    node = BitwiseAndExpression(tokenizer, staticContext)
-
-    while tokenizer.match("bitwise_xor"):
-        childNode = builder.BITWISEXOR_build(tokenizer)
-        builder.BITWISEXOR_addOperand(childNode, node)
-        builder.BITWISEXOR_addOperand(childNode, BitwiseAndExpression(tokenizer, staticContext))
-        builder.BITWISEXOR_finish(childNode)
-        node = childNode
-
-    return node
-
-
-def BitwiseAndExpression(tokenizer, staticContext):
-    builder = staticContext.builder
-    node = EqualityExpression(tokenizer, staticContext)
-
-    while tokenizer.match("bitwise_and"):
-        childNode = builder.BITWISEAND_build(tokenizer)
-        builder.BITWISEAND_addOperand(childNode, node)
-        builder.BITWISEAND_addOperand(childNode, EqualityExpression(tokenizer, staticContext))
-        builder.BITWISEAND_finish(childNode)
-        node = childNode
-
-    return node
-
-
-def EqualityExpression(tokenizer, staticContext):
-    builder = staticContext.builder
-    node = RelationalExpression(tokenizer, staticContext)
-
-    while tokenizer.match("eq") or tokenizer.match("ne") or tokenizer.match("strict_eq") or tokenizer.match("strict_ne"):
-        childNode = builder.EQUALITY_build(tokenizer)
-        builder.EQUALITY_addOperand(childNode, node)
-        builder.EQUALITY_addOperand(childNode, RelationalExpression(tokenizer, staticContext))
-        builder.EQUALITY_finish(childNode)
-        node = childNode
-
-    return node
-
-
-def RelationalExpression(tokenizer, staticContext):
-    builder = staticContext.builder
-    oldLoopInit = staticContext.inForLoopInit
-
-    # Uses of the in operator in shiftExprs are always unambiguous,
-    # so unset the flag that prohibits recognizing it.
-    staticContext.inForLoopInit = False
-    node = ShiftExpression(tokenizer, staticContext)
-
-    while tokenizer.match("lt") or tokenizer.match("le") or tokenizer.match("ge") or tokenizer.match("gt") or (oldLoopInit == False and tokenizer.match("in")) or tokenizer.match("instanceof"):
-        childNode = builder.RELATIONAL_build(tokenizer)
-        builder.RELATIONAL_addOperand(childNode, node)
-        builder.RELATIONAL_addOperand(childNode, ShiftExpression(tokenizer, staticContext))
-        builder.RELATIONAL_finish(childNode)
-        node = childNode
-
-    staticContext.inForLoopInit = oldLoopInit
-
-    return node
-
-
-def ShiftExpression(tokenizer, staticContext):
-    builder = staticContext.builder
-    node = AddExpression(tokenizer, staticContext)
-
-    while tokenizer.match("lsh") or tokenizer.match("rsh") or tokenizer.match("ursh"):
-        childNode = builder.SHIFT_build(tokenizer)
-        builder.SHIFT_addOperand(childNode, node)
-        builder.SHIFT_addOperand(childNode, AddExpression(tokenizer, staticContext))
-        builder.SHIFT_finish(childNode)
-        node = childNode
-
-    return node
-
-
-def AddExpression(tokenizer, staticContext):
-    builder = staticContext.builder
-    node = MultiplyExpression(tokenizer, staticContext)
-
-    while tokenizer.match("plus") or tokenizer.match("minus"):
-        childNode = builder.ADD_build(tokenizer)
-        builder.ADD_addOperand(childNode, node)
-        builder.ADD_addOperand(childNode, MultiplyExpression(tokenizer, staticContext))
-        builder.ADD_finish(childNode)
-        node = childNode
-
-    return node
-
-
-def MultiplyExpression(tokenizer, staticContext):
-    builder = staticContext.builder
-    node = UnaryExpression(tokenizer, staticContext)
-
-    while tokenizer.match("mul") or tokenizer.match("div") or tokenizer.match("mod"):
-        childNode = builder.MULTIPLY_build(tokenizer)
-        builder.MULTIPLY_addOperand(childNode, node)
-        builder.MULTIPLY_addOperand(childNode, UnaryExpression(tokenizer, staticContext))
-        builder.MULTIPLY_finish(childNode)
-        node = childNode
-
-    return node
-
-
-def UnaryExpression(tokenizer, staticContext):
-    builder = staticContext.builder
-    tokenType = tokenizer.get(True)
-
-    if tokenType in ["delete", "void", "typeof", "not", "bitwise_not", "plus", "minus"]:
-        node = builder.UNARY_build(tokenizer)
-        builder.UNARY_addOperand(node, UnaryExpression(tokenizer, staticContext))
-
-    elif tokenType == "increment" or tokenType == "decrement":
-        # Prefix increment/decrement.
-        node = builder.UNARY_build(tokenizer)
-        builder.UNARY_addOperand(node, MemberExpression(tokenizer, staticContext, True))
-
-    else:
-        tokenizer.unget()
-        node = MemberExpression(tokenizer, staticContext, True)
-
-        # Don't look across a newline boundary for a postfix {in,de}crement.
-        if tokenizer.tokens[(tokenizer.tokenIndex + tokenizer.lookahead - 1) & 3].line == tokenizer.line:
-            if tokenizer.match("increment") or tokenizer.match("decrement"):
-                childNode = builder.UNARY_build(tokenizer)
-                builder.UNARY_setPostfix(childNode)
-                builder.UNARY_finish(node)
-                builder.UNARY_addOperand(childNode, node)
-                node = childNode
-
-    builder.UNARY_finish(node)
-    return node
-
-
-def MemberExpression(tokenizer, staticContext, allowCallSyntax):
-    builder = staticContext.builder
-
-    if tokenizer.match("new"):
-        node = builder.MEMBER_build(tokenizer)
-        builder.MEMBER_addOperand(node, MemberExpression(tokenizer, staticContext, False))
-
-        if tokenizer.match("left_paren"):
-            builder.MEMBER_rebuildNewWithArgs(node)
-            builder.MEMBER_addOperand(node, ArgumentList(tokenizer, staticContext))
-
-        builder.MEMBER_finish(node)
-
-    else:
-        node = PrimaryExpression(tokenizer, staticContext)
-
-    while True:
-        tokenType = tokenizer.get()
-        if tokenType == "end":
-            break
-
-        if tokenType == "dot":
-            childNode = builder.MEMBER_build(tokenizer)
-            builder.MEMBER_addOperand(childNode, node)
-            tokenizer.mustMatch("identifier")
-            builder.MEMBER_addOperand(childNode, builder.MEMBER_build(tokenizer))
-
-        elif tokenType == "left_bracket":
-            childNode = builder.MEMBER_build(tokenizer, "index")
-            builder.MEMBER_addOperand(childNode, node)
-            builder.MEMBER_addOperand(childNode, Expression(tokenizer, staticContext))
-            tokenizer.mustMatch("right_bracket")
-
-        elif tokenType == "left_paren" and allowCallSyntax:
-            childNode = builder.MEMBER_build(tokenizer, "call")
-            builder.MEMBER_addOperand(childNode, node)
-            builder.MEMBER_addOperand(childNode, ArgumentList(tokenizer, staticContext))
-
-        else:
-            tokenizer.unget()
-            return node
-
-        builder.MEMBER_finish(childNode)
-        node = childNode
-
-    return node
-
-
-def ArgumentList(tokenizer, staticContext):
-    builder = staticContext.builder
-    node = builder.LIST_build(tokenizer)
-
-    if tokenizer.match("right_paren", True):
-        return node
-
-    while True:
-        childNode = AssignExpression(tokenizer, staticContext)
-        if childNode.type == "yield" and not childNode.parenthesized and tokenizer.peek() == "comma":
-            raise SyntaxError("Yield expression must be parenthesized", tokenizer)
-
-        if tokenizer.match("for"):
-            childNode = GeneratorExpression(tokenizer, staticContext, childNode)
-            if len(node) > 1 or tokenizer.peek(True) == "comma":
-                raise SyntaxError("Generator expression must be parenthesized", tokenizer)
-
-        builder.LIST_addOperand(node, childNode)
-        if not tokenizer.match("comma"):
-            break
-
-    tokenizer.mustMatch("right_paren")
-    builder.LIST_finish(node)
-
-    return node
-
-
-def PrimaryExpression(tokenizer, staticContext):
-    builder = staticContext.builder
-    tokenType = tokenizer.get(True)
-
-    if tokenType == "function":
-        node = FunctionDefinition(tokenizer, staticContext, False, "expressed_form")
-
-    elif tokenType == "left_bracket":
-        node = builder.ARRAYINIT_build(tokenizer)
-        while True:
-            tokenType = tokenizer.peek(True)
-            if tokenType == "right_bracket":
-                break
-
-            if tokenType == "comma":
-                tokenizer.get()
-                builder.ARRAYINIT_addElement(node, None)
-                continue
-
-            builder.ARRAYINIT_addElement(node, AssignExpression(tokenizer, staticContext))
-
-            if tokenType != "comma" and not tokenizer.match("comma"):
-                break
-
-        # If we matched exactly one element and got a "for", we have an
-        # array comprehension.
-        if len(node) == 1 and tokenizer.match("for"):
-            childNode = builder.ARRAYCOMP_build(tokenizer)
-            builder.ARRAYCOMP_setExpression(childNode, node[0])
-            builder.ARRAYCOMP_setTail(childNode, comprehensionTail(tokenizer, staticContext))
-            node = childNode
-
-        builder.COMMENTS_add(node, node, tokenizer.getComments())
-        tokenizer.mustMatch("right_bracket")
-        builder.PRIMARY_finish(node)
-
-    elif tokenType == "left_curly":
-        node = builder.OBJECTINIT_build(tokenizer)
-
-        if not tokenizer.match("right_curly"):
-            while True:
-                tokenType = tokenizer.get()
-                tokenValue = getattr(tokenizer.token, "value", None)
-                comments = tokenizer.getComments()
-
-                if tokenValue in ("get", "set") and tokenizer.peek() == "identifier":
-                    if staticContext.ecma3OnlyMode:
-                        raise SyntaxError("Illegal property accessor", tokenizer)
-
-                    fd = FunctionDefinition(tokenizer, staticContext, True, "expressed_form")
-                    builder.OBJECTINIT_addProperty(node, fd)
-
-                else:
-                    if tokenType == "identifier" or tokenType == "number" or tokenType == "string":
-                        id = builder.PRIMARY_build(tokenizer, "identifier")
-                        builder.PRIMARY_finish(id)
-
-                    elif tokenType == "right_curly":
-                        if staticContext.ecma3OnlyMode:
-                            raise SyntaxError("Illegal trailing ,", tokenizer)
-
-                        tokenizer.unget()
-                        break
-
-                    else:
-                        if tokenValue in jasy.js.tokenize.Lang.keywords:
-                            id = builder.PRIMARY_build(tokenizer, "identifier")
-                            builder.PRIMARY_finish(id)
-                        else:
-                            print("Value is '%s'" % tokenValue)
-                            raise SyntaxError("Invalid property name", tokenizer)
-
-                    if tokenizer.match("colon"):
-                        childNode = builder.PROPERTYINIT_build(tokenizer)
-                        builder.COMMENTS_add(childNode, node, comments)
-                        builder.PROPERTYINIT_addOperand(childNode, id)
-                        builder.PROPERTYINIT_addOperand(childNode, AssignExpression(tokenizer, staticContext))
-                        builder.PROPERTYINIT_finish(childNode)
-                        builder.OBJECTINIT_addProperty(node, childNode)
-
-                    else:
-                        # Support, e.g., |var {staticContext, y} = o| as destructuring shorthand
-                        # for |var {staticContext: staticContext, y: y} = o|, per proposed JS2/ES4 for JS1.8.
-                        if tokenizer.peek() != "comma" and tokenizer.peek() != "right_curly":
-                            raise SyntaxError("Missing : after property", tokenizer)
-                        builder.OBJECTINIT_addProperty(node, id)
-
-                if not tokenizer.match("comma"):
-                    break
-
-            builder.COMMENTS_add(node, node, tokenizer.getComments())
-            tokenizer.mustMatch("right_curly")
-
-        builder.OBJECTINIT_finish(node)
-
-    elif tokenType == "left_paren":
-        # ParenExpression does its own matching on parentheses, so we need to unget.
-        tokenizer.unget()
-        node = ParenExpression(tokenizer, staticContext)
-        node.parenthesized = True
-
-    elif tokenType == "let":
-        node = LetBlock(tokenizer, staticContext, False)
-
-    elif tokenType in ["null", "this", "true", "false", "identifier", "number", "string", "regexp"]:
-        node = builder.PRIMARY_build(tokenizer, tokenType)
-        builder.PRIMARY_finish(node)
-
-    else:
-        raise SyntaxError("Missing operand. Found type: %s" % tokenType, tokenizer)
-
-    return node
--- a/ThirdParty/Jasy/jasy/js/parse/VanillaBuilder.py	Thu Jan 10 14:23:49 2019 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,679 +0,0 @@
-#
-# Jasy - Web Tooling Framework
-# Copyright 2010-2012 Zynga Inc.
-# Copyright 2013-2014 Sebastian Werner
-#
-
-#
-# License: MPL 1.1/GPL 2.0/LGPL 2.1
-# Authors:
-#   - Brendan Eich <brendan@mozilla.org> (Original JavaScript) (2004-2010)
-#   - Sebastian Werner <info@sebastian-werner.net> (Python Port) (2010)
-#
-
-from __future__ import unicode_literals
-
-import jasy.js.parse.Node
-
-class VanillaBuilder:
-    """The vanilla AST builder."""
-
-    def COMMENTS_add(self, currNode, prevNode, comments):
-        if not comments:
-            return
-
-        currComments = []
-        prevComments = []
-        for comment in comments:
-            # post comments - for previous node
-            if comment.context == "inline":
-                prevComments.append(comment)
-
-            # all other comment styles are attached to the current one
-            else:
-                currComments.append(comment)
-
-        # Merge with previously added ones
-        if hasattr(currNode, "comments"):
-            currNode.comments.extend(currComments)
-        else:
-            currNode.comments = currComments
-
-        if prevNode:
-            if hasattr(prevNode, "comments"):
-                prevNode.comments.extend(prevComments)
-            else:
-                prevNode.comments = prevComments
-        else:
-            # Don't loose the comment in tree (if not previous node is there, attach it to this node)
-            currNode.comments.extend(prevComments)
-
-    def IF_build(self, tokenizer):
-        return jasy.js.parse.Node.Node(tokenizer, "if")
-
-    def IF_setCondition(self, node, expression):
-        node.append(expression, "condition")
-
-    def IF_setThenPart(self, node, statement):
-        node.append(statement, "thenPart")
-
-    def IF_setElsePart(self, node, statement):
-        node.append(statement, "elsePart")
-
-    def IF_finish(self, node):
-        pass
-
-    def SWITCH_build(self, tokenizer):
-        node = jasy.js.parse.Node.Node(tokenizer, "switch")
-        node.defaultIndex = -1
-        return node
-
-    def SWITCH_setDiscriminant(self, node, expression):
-        node.append(expression, "discriminant")
-
-    def SWITCH_setDefaultIndex(self, node, index):
-        node.defaultIndex = index
-
-    def SWITCH_addCase(self, node, childNode):
-        node.append(childNode)
-
-    def SWITCH_finish(self, node):
-        pass
-
-    def CASE_build(self, tokenizer):
-        return jasy.js.parse.Node.Node(tokenizer, "case")
-
-    def CASE_setLabel(self, node, expression):
-        node.append(expression, "label")
-
-    def CASE_initializeStatements(self, node, tokenizer):
-        node.append(jasy.js.parse.Node.Node(tokenizer, "block"), "statements")
-
-    def CASE_addStatement(self, node, statement):
-        node.statements.append(statement)
-
-    def CASE_finish(self, node):
-        pass
-
-    def DEFAULT_build(self, tokenizer):
-        return jasy.js.parse.Node.Node(tokenizer, "default")
-
-    def DEFAULT_initializeStatements(self, node, tokenizer):
-        node.append(jasy.js.parse.Node.Node(tokenizer, "block"), "statements")
-
-    def DEFAULT_addStatement(self, node, statement):
-        node.statements.append(statement)
-
-    def DEFAULT_finish(self, node):
-        pass
-
-    def FOR_build(self, tokenizer):
-        node = jasy.js.parse.Node.Node(tokenizer, "for")
-        node.isLoop = True
-        node.isEach = False
-        return node
-
-    def FOR_rebuildForEach(self, node):
-        node.isEach = True
-
-    # NB: This function is called after rebuildForEach, if that'statement called at all.
-    def FOR_rebuildForIn(self, node):
-        node.type = "for_in"
-
-    def FOR_setCondition(self, node, expression):
-        node.append(expression, "condition")
-
-    def FOR_setSetup(self, node, expression):
-        node.append(expression, "setup")
-
-    def FOR_setUpdate(self, node, expression):
-        node.append(expression, "update")
-
-    def FOR_setObject(self, node, expression, forBlock=None):
-        # wpbasti: not sure what forBlock stands for but it is used in the parser
-        # JS tolerates the optinal unused parameter, but not so Python.
-        node.append(expression, "object")
-
-    def FOR_setIterator(self, node, expression, forBlock=None):
-        # wpbasti: not sure what forBlock stands for but it is used in the parser
-        # JS tolerates the optinal unused parameter, but not so Python.
-        node.append(expression, "iterator")
-
-    def FOR_setBody(self, node, statement):
-        node.append(statement, "body")
-
-    def FOR_finish(self, node):
-        pass
-
-    def WHILE_build(self, tokenizer):
-        node = jasy.js.parse.Node.Node(tokenizer, "while")
-        node.isLoop = True
-        return node
-
-    def WHILE_setCondition(self, node, expression):
-        node.append(expression, "condition")
-
-    def WHILE_setBody(self, node, statement):
-        node.append(statement, "body")
-
-    def WHILE_finish(self, node):
-        pass
-
-    def DO_build(self, tokenizer):
-        node = jasy.js.parse.Node.Node(tokenizer, "do")
-        node.isLoop = True
-        return node
-
-    def DO_setCondition(self, node, expression):
-        node.append(expression, "condition")
-
-    def DO_setBody(self, node, statement):
-        node.append(statement, "body")
-
-    def DO_finish(self, node):
-        pass
-
-    def BREAK_build(self, tokenizer):
-        return jasy.js.parse.Node.Node(tokenizer, "break")
-
-    def BREAK_setLabel(self, node, label):
-        node.label = label
-
-    def BREAK_setTarget(self, node, target):
-        # Hint, no append() - relation, but not a child
-        node.target = target
-
-    def BREAK_finish(self, node):
-        pass
-
-    def CONTINUE_build(self, tokenizer):
-        return jasy.js.parse.Node.Node(tokenizer, "continue")
-
-    def CONTINUE_setLabel(self, node, label):
-        node.label = label
-
-    def CONTINUE_setTarget(self, node, target):
-        # Hint, no append() - relation, but not a child
-        node.target = target
-
-    def CONTINUE_finish(self, node):
-        pass
-
-    def TRY_build(self, tokenizer):
-        node = jasy.js.parse.Node.Node(tokenizer, "try")
-        return node
-
-    def TRY_setTryBlock(self, node, statement):
-        node.append(statement, "tryBlock")
-
-    def TRY_addCatch(self, node, childNode):
-        node.append(childNode)
-
-    def TRY_finishCatches(self, node):
-        pass
-
-    def TRY_setFinallyBlock(self, node, statement):
-        node.append(statement, "finallyBlock")
-
-    def TRY_finish(self, node):
-        pass
-
-    def CATCH_build(self, tokenizer):
-        node = jasy.js.parse.Node.Node(tokenizer, "catch")
-        return node
-
-    def CATCH_wrapException(self, tokenizer):
-        node = jasy.js.parse.Node.Node(tokenizer, "exception")
-        node.value = tokenizer.token.value
-        return node
-
-    def CATCH_setException(self, node, exception):
-        node.append(exception, "exception")
-
-    def CATCH_setGuard(self, node, expression):
-        node.append(expression, "guard")
-
-    def CATCH_setBlock(self, node, statement):
-        node.append(statement, "block")
-
-    def CATCH_finish(self, node):
-        pass
-
-    def THROW_build(self, tokenizer):
-        return jasy.js.parse.Node.Node(tokenizer, "throw")
-
-    def THROW_setException(self, node, expression):
-        node.append(expression, "exception")
-
-    def THROW_finish(self, node):
-        pass
-
-    def RETURN_build(self, tokenizer):
-        return jasy.js.parse.Node.Node(tokenizer, "return")
-
-    def RETURN_setValue(self, node, expression):
-        node.append(expression, "value")
-
-    def RETURN_finish(self, node):
-        pass
-
-    def YIELD_build(self, tokenizer):
-        return jasy.js.parse.Node.Node(tokenizer, "yield")
-
-    def YIELD_setValue(self, node, expression):
-        node.append(expression, "value")
-
-    def YIELD_finish(self, node):
-        pass
-
-    def GENERATOR_build(self, tokenizer):
-        return jasy.js.parse.Node.Node(tokenizer, "generator")
-
-    def GENERATOR_setExpression(self, node, expression):
-        node.append(expression, "expression")
-
-    def GENERATOR_setTail(self, node, childNode):
-        node.append(childNode, "tail")
-
-    def GENERATOR_finish(self, node):
-        pass
-
-    def WITH_build(self, tokenizer):
-        return jasy.js.parse.Node.Node(tokenizer, "with")
-
-    def WITH_setObject(self, node, expression):
-        node.append(expression, "object")
-
-    def WITH_setBody(self, node, statement):
-        node.append(statement, "body")
-
-    def WITH_finish(self, node):
-        pass
-
-    def DEBUGGER_build(self, tokenizer):
-        return jasy.js.parse.Node.Node(tokenizer, "debugger")
-
-    def SEMICOLON_build(self, tokenizer):
-        return jasy.js.parse.Node.Node(tokenizer, "semicolon")
-
-    def SEMICOLON_setExpression(self, node, expression):
-        node.append(expression, "expression")
-
-    def SEMICOLON_finish(self, node):
-        pass
-
-    def LABEL_build(self, tokenizer):
-        return jasy.js.parse.Node.Node(tokenizer, "label")
-
-    def LABEL_setLabel(self, node, label):
-        node.label = label
-
-    def LABEL_setStatement(self, node, statement):
-        node.append(statement, "statement")
-
-    def LABEL_finish(self, node):
-        pass
-
-    def FUNCTION_build(self, tokenizer):
-        node = jasy.js.parse.Node.Node(tokenizer)
-        if node.type != "function":
-            if tokenizer.token.value == "get":
-                node.type = "getter"
-            else:
-                node.type = "setter"
-
-        return node
-
-    def FUNCTION_setName(self, node, identifier):
-        node.name = identifier
-
-    def FUNCTION_initParams(self, node, tokenizer):
-        node.append(jasy.js.parse.Node.Node(tokenizer, "list"), "params")
-
-    def FUNCTION_wrapParam(self, tokenizer):
-        param = jasy.js.parse.Node.Node(tokenizer)
-        param.value = tokenizer.token.value
-        return param
-
-    def FUNCTION_addParam(self, node, tokenizer, expression):
-        node.params.append(expression)
-
-    def FUNCTION_setExpressionClosure(self, node, expressionClosure):
-        node.expressionClosure = expressionClosure
-
-    def FUNCTION_setBody(self, node, statement):
-        # copy over function parameters to function body
-        params = getattr(node, "params", None)
-        #if params:
-        #    statement.params = [param.value for param in params]
-
-        node.append(statement, "body")
-
-    def FUNCTION_hoistVars(self, x):
-        pass
-
-    def FUNCTION_finish(self, node, x):
-        pass
-
-    def VAR_build(self, tokenizer):
-        return jasy.js.parse.Node.Node(tokenizer, "var")
-
-    def VAR_addDecl(self, node, childNode, childContext=None):
-        node.append(childNode)
-
-    def VAR_finish(self, node):
-        pass
-
-    def CONST_build(self, tokenizer):
-        return jasy.js.parse.Node.Node(tokenizer, "const")
-
-    def CONST_addDecl(self, node, childNode, childContext=None):
-        node.append(childNode)
-
-    def CONST_finish(self, node):
-        pass
-
-    def LET_build(self, tokenizer):
-        return jasy.js.parse.Node.Node(tokenizer, "let")
-
-    def LET_addDecl(self, node, childNode, childContext=None):
-        node.append(childNode)
-
-    def LET_finish(self, node):
-        pass
-
-    def DECL_build(self, tokenizer):
-        return jasy.js.parse.Node.Node(tokenizer, "declaration")
-
-    def DECL_setNames(self, node, expression):
-        node.append(expression, "names")
-
-    def DECL_setName(self, node, identifier):
-        node.name = identifier
-
-    def DECL_setInitializer(self, node, expression):
-        node.append(expression, "initializer")
-
-    def DECL_setReadOnly(self, node, readOnly):
-        node.readOnly = readOnly
-
-    def DECL_finish(self, node):
-        pass
-
-    def LETBLOCK_build(self, tokenizer):
-        node = jasy.js.parse.Node.Node(tokenizer, "let_block")
-        return node
-
-    def LETBLOCK_setVariables(self, node, childNode):
-        node.append(childNode, "variables")
-
-    def LETBLOCK_setExpression(self, node, expression):
-        node.append(expression, "expression")
-
-    def LETBLOCK_setBlock(self, node, statement):
-        node.append(statement, "block")
-
-    def LETBLOCK_finish(self, node):
-        pass
-
-    def BLOCK_build(self, tokenizer, id):
-        node = jasy.js.parse.Node.Node(tokenizer, "block")
-        # node.id = id
-        return node
-
-    def BLOCK_hoistLets(self, node):
-        pass
-
-    def BLOCK_addStatement(self, node, childNode):
-        node.append(childNode)
-
-    def BLOCK_finish(self, node):
-        pass
-
-    def EXPRESSION_build(self, tokenizer, tokenType):
-        return jasy.js.parse.Node.Node(tokenizer, tokenType)
-
-    def EXPRESSION_addOperand(self, node, childNode):
-        node.append(childNode)
-
-    def EXPRESSION_finish(self, node):
-        pass
-
-    def ASSIGN_build(self, tokenizer):
-        return jasy.js.parse.Node.Node(tokenizer, "assign")
-
-    def ASSIGN_addOperand(self, node, childNode):
-        node.append(childNode)
-
-    def ASSIGN_setAssignOp(self, node, operator):
-        node.assignOp = operator
-
-    def ASSIGN_finish(self, node):
-        pass
-
-    def HOOK_build(self, tokenizer):
-        return jasy.js.parse.Node.Node(tokenizer, "hook")
-
-    def HOOK_setCondition(self, node, expression):
-        node.append(expression, "condition")
-
-    def HOOK_setThenPart(self, node, childNode):
-        node.append(childNode, "thenPart")
-
-    def HOOK_setElsePart(self, node, childNode):
-        node.append(childNode, "elsePart")
-
-    def HOOK_finish(self, node):
-        pass
-
-    def OR_build(self, tokenizer):
-        return jasy.js.parse.Node.Node(tokenizer, "or")
-
-    def OR_addOperand(self, node, childNode):
-        node.append(childNode)
-
-    def OR_finish(self, node):
-        pass
-
-    def AND_build(self, tokenizer):
-        return jasy.js.parse.Node.Node(tokenizer, "and")
-
-    def AND_addOperand(self, node, childNode):
-        node.append(childNode)
-
-    def AND_finish(self, node):
-        pass
-
-    def BITWISEOR_build(self, tokenizer):
-        return jasy.js.parse.Node.Node(tokenizer, "bitwise_or")
-
-    def BITWISEOR_addOperand(self, node, childNode):
-        node.append(childNode)
-
-    def BITWISEOR_finish(self, node):
-        pass
-
-    def BITWISEXOR_build(self, tokenizer):
-        return jasy.js.parse.Node.Node(tokenizer, "bitwise_xor")
-
-    def BITWISEXOR_addOperand(self, node, childNode):
-        node.append(childNode)
-
-    def BITWISEXOR_finish(self, node):
-        pass
-
-    def BITWISEAND_build(self, tokenizer):
-        return jasy.js.parse.Node.Node(tokenizer, "bitwise_and")
-
-    def BITWISEAND_addOperand(self, node, childNode):
-        node.append(childNode)
-
-    def BITWISEAND_finish(self, node):
-        pass
-
-    def EQUALITY_build(self, tokenizer):
-        # NB: tokenizer.token.type must be "eq", "ne", "strict_eq", or "strict_ne".
-        return jasy.js.parse.Node.Node(tokenizer)
-
-    def EQUALITY_addOperand(self, node, childNode):
-        node.append(childNode)
-
-    def EQUALITY_finish(self, node):
-        pass
-
-    def RELATIONAL_build(self, tokenizer):
-        # NB: tokenizer.token.type must be "lt", "le", "ge", or "gt".
-        return jasy.js.parse.Node.Node(tokenizer)
-
-    def RELATIONAL_addOperand(self, node, childNode):
-        node.append(childNode)
-
-    def RELATIONAL_finish(self, node):
-        pass
-
-    def SHIFT_build(self, tokenizer):
-        # NB: tokenizer.token.type must be "lsh", "rsh", or "ursh".
-        return jasy.js.parse.Node.Node(tokenizer)
-
-    def SHIFT_addOperand(self, node, childNode):
-        node.append(childNode)
-
-    def SHIFT_finish(self, node):
-        pass
-
-    def ADD_build(self, tokenizer):
-        # NB: tokenizer.token.type must be "plus" or "minus".
-        return jasy.js.parse.Node.Node(tokenizer)
-
-    def ADD_addOperand(self, node, childNode):
-        node.append(childNode)
-
-    def ADD_finish(self, node):
-        pass
-
-    def MULTIPLY_build(self, tokenizer):
-        # NB: tokenizer.token.type must be "mul", "div", or "mod".
-        return jasy.js.parse.Node.Node(tokenizer)
-
-    def MULTIPLY_addOperand(self, node, childNode):
-        node.append(childNode)
-
-    def MULTIPLY_finish(self, node):
-        pass
-
-    def UNARY_build(self, tokenizer):
-        # NB: tokenizer.token.type must be "delete", "void", "typeof", "not", "bitwise_not",
-        # "unary_plus", "unary_minus", "increment", or "decrement".
-        if tokenizer.token.type == "plus":
-            tokenizer.token.type = "unary_plus"
-        elif tokenizer.token.type == "minus":
-            tokenizer.token.type = "unary_minus"
-
-        return jasy.js.parse.Node.Node(tokenizer)
-
-    def UNARY_addOperand(self, node, childNode):
-        node.append(childNode)
-
-    def UNARY_setPostfix(self, node):
-        node.postfix = True
-
-    def UNARY_finish(self, node):
-        pass
-
-    def MEMBER_build(self, tokenizer, tokenType=None):
-        node = jasy.js.parse.Node.Node(tokenizer, tokenType)
-        if node.type == "identifier":
-            node.value = tokenizer.token.value
-        return node
-
-    def MEMBER_rebuildNewWithArgs(self, node):
-        node.type = "new_with_args"
-
-    def MEMBER_addOperand(self, node, childNode):
-        node.append(childNode)
-
-    def MEMBER_finish(self, node):
-        pass
-
-    def PRIMARY_build(self, tokenizer, tokenType):
-        # NB: tokenizer.token.type must be "null", "this", "true", "false", "identifier", "number", "string", or "regexp".
-        node = jasy.js.parse.Node.Node(tokenizer, tokenType)
-        if tokenType in ("identifier", "string", "regexp", "number"):
-            node.value = tokenizer.token.value
-
-        return node
-
-    def PRIMARY_finish(self, node):
-        pass
-
-    def ARRAYINIT_build(self, tokenizer):
-        return jasy.js.parse.Node.Node(tokenizer, "array_init")
-
-    def ARRAYINIT_addElement(self, node, childNode):
-        node.append(childNode)
-
-    def ARRAYINIT_finish(self, node):
-        pass
-
-    def ARRAYCOMP_build(self, tokenizer):
-        return jasy.js.parse.Node.Node(tokenizer, "array_comp")
-
-    def ARRAYCOMP_setExpression(self, node, expression):
-        node.append(expression, "expression")
-
-    def ARRAYCOMP_setTail(self, node, childNode):
-        node.append(childNode, "tail")
-
-    def ARRAYCOMP_finish(self, node):
-        pass
-
-    def COMPTAIL_build(self, tokenizer):
-        return jasy.js.parse.Node.Node(tokenizer, "comp_tail")
-
-    def COMPTAIL_setGuard(self, node, expression):
-        node.append(expression, "guard")
-
-    def COMPTAIL_addFor(self, node, childNode):
-        node.append(childNode, "for")
-
-    def COMPTAIL_finish(self, node):
-        pass
-
-    def OBJECTINIT_build(self, tokenizer):
-        return jasy.js.parse.Node.Node(tokenizer, "object_init")
-
-    def OBJECTINIT_addProperty(self, node, childNode):
-        node.append(childNode)
-
-    def OBJECTINIT_finish(self, node):
-        pass
-
-    def PROPERTYINIT_build(self, tokenizer):
-        return jasy.js.parse.Node.Node(tokenizer, "property_init")
-
-    def PROPERTYINIT_addOperand(self, node, childNode):
-        node.append(childNode)
-
-    def PROPERTYINIT_finish(self, node):
-        pass
-
-    def COMMA_build(self, tokenizer):
-        return jasy.js.parse.Node.Node(tokenizer, "comma")
-
-    def COMMA_addOperand(self, node, childNode):
-        node.append(childNode)
-
-    def COMMA_finish(self, node):
-        pass
-
-    def LIST_build(self, tokenizer):
-        return jasy.js.parse.Node.Node(tokenizer, "list")
-
-    def LIST_addOperand(self, node, childNode):
-        node.append(childNode)
-
-    def LIST_finish(self, node):
-        pass
-
-    def setHoists(self, id, vds):
-        pass
--- a/ThirdParty/Jasy/jasy/js/tokenize/Lang.py	Thu Jan 10 14:23:49 2019 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,24 +0,0 @@
-#
-# Jasy - Web Tooling Framework
-# Copyright 2010-2012 Zynga Inc.
-#
-
-from __future__ import unicode_literals
-
-"""JavaScript 1.7 keywords"""
-keywords = set([
-    "break",
-    "case", "catch", "const", "continue",
-    "debugger", "default", "delete", "do",
-    "else",
-    "false", "finally", "for", "function",
-    "if", "in", "instanceof",
-    "let",
-    "new", "null",
-    "return",
-    "switch",
-    "this", "throw", "true", "try", "typeof",
-    "var", "void",
-    "yield",
-    "while", "with"
-])
--- a/ThirdParty/Jasy/jasy/js/tokenize/Tokenizer.py	Thu Jan 10 14:23:49 2019 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,609 +0,0 @@
-#
-# Jasy - Web Tooling Framework
-# Copyright 2010-2012 Zynga Inc.
-#
-
-#
-# License: MPL 1.1/GPL 2.0/LGPL 2.1
-# Authors: 
-#   - Brendan Eich <brendan@mozilla.org> (Original JavaScript) (2004-2010)
-#   - Sebastian Werner <info@sebastian-werner.net> (Python Port) (2010)
-#
-
-from __future__ import unicode_literals
-
-import copy
-
-import jasy.js.tokenize.Lang as Lang
-import jasy.js.api.Comment as Comment
-import jasy.core.Console as Console
-
-__all__ = [ "Tokenizer" ]
-
-
-# Operator and punctuator mapping from token to tree node type name.
-# NB: because the lexer doesn't backtrack, all token prefixes must themselves
-# be valid tokens (e.g. !== is acceptable because its prefixes are the valid
-# tokens != and !).
-operatorNames = {
-    '<'   : 'lt', 
-    '>'   : 'gt', 
-    '<='  : 'le', 
-    '>='  : 'ge', 
-    '!='  : 'ne', 
-    '!'   : 'not', 
-    '=='  : 'eq', 
-    '===' : 'strict_eq', 
-    '!==' : 'strict_ne', 
-
-    '>>'  : 'rsh', 
-    '<<'  : 'lsh',
-    '>>>' : 'ursh', 
-     
-    '+'   : 'plus', 
-    '*'   : 'mul', 
-    '-'   : 'minus', 
-    '/'   : 'div', 
-    '%'   : 'mod', 
-
-    ','   : 'comma', 
-    ';'   : 'semicolon', 
-    ':'   : 'colon', 
-    '='   : 'assign', 
-    '?'   : 'hook', 
-
-    '&&'  : 'and', 
-    '||'  : 'or', 
-
-    '++'  : 'increment', 
-    '--'  : 'decrement', 
-
-    ')'   : 'right_paren', 
-    '('   : 'left_paren', 
-    '['   : 'left_bracket', 
-    ']'   : 'right_bracket', 
-    '{'   : 'left_curly', 
-    '}'   : 'right_curly', 
-
-    '&'   : 'bitwise_and', 
-    '^'   : 'bitwise_xor', 
-    '|'   : 'bitwise_or', 
-    '~'   : 'bitwise_not'
-}
-
-
-# Assignment operators
-assignOperators = ["|", "^", "&", "<<", ">>", ">>>", "+", "-", "*", "/", "%"]
-
-
-
-
-#
-# Classes
-#
-
-class Token: 
-    __slots__ = ["type", "start", "line", "assignOp", "end", "value"]
-
-
-class ParseError(Exception):
-    def __init__(self, message, fileId, line):
-        Exception.__init__(self, "Syntax error: %s\n%s:%s" % (message, fileId, line))
-
-
-class Tokenizer(object):
-    def __init__(self, source, fileId="", line=1):
-        # source: JavaScript source
-        # fileId: Filename (for debugging proposes)
-        # line: Line number (for debugging proposes)
-        self.cursor = 0
-        self.source = str(source)
-        self.tokens = {}
-        self.tokenIndex = 0
-        self.lookahead = 0
-        self.scanNewlines = False
-        self.fileId = fileId
-        self.line = line
-        self.comments = []
-
-    input_ = property(lambda self: self.source[self.cursor:])
-    token = property(lambda self: self.tokens.get(self.tokenIndex))
-
-
-    def done(self):
-        # We need to set scanOperand to true here because the first thing
-        # might be a regexp.
-        return self.peek(True) == "end"
-        
-
-    def match(self, tokenType, scanOperand=False):
-        return self.get(scanOperand) == tokenType or self.unget()
-
-
-    def mustMatch(self, tokenType):
-        if not self.match(tokenType):
-            raise ParseError("Missing " + tokenType, self.fileId, self.line)
-            
-        return self.token
-
-
-    def peek(self, scanOperand=False):
-        if self.lookahead:
-            next = self.tokens.get((self.tokenIndex + self.lookahead) & 3)
-            if self.scanNewlines and (getattr(next, "line", None) != getattr(self, "line", None)):
-                tokenType = "newline"
-            else:
-                tokenType = getattr(next, "type", None)
-        else:
-            tokenType = self.get(scanOperand)
-            self.unget()
-            
-        return tokenType
-
-
-    def peekOnSameLine(self, scanOperand=False):
-        self.scanNewlines = True
-        tokenType = self.peek(scanOperand)
-        self.scanNewlines = False
-        return tokenType
-        
-
-    def getComments(self):
-        if self.comments:
-            comments = self.comments
-            self.comments = []
-            return comments
-            
-        return None
-
-
-    def skip(self):
-        """Eats comments and whitespace."""
-        input = self.source
-        startLine = self.line
-
-        # Whether this is the first called as happen on start parsing a file (eat leading comments/white space)
-        startOfFile = self.cursor is 0
-        
-        indent = ""
-        
-        while (True):
-            if len(input) > self.cursor:
-                ch = input[self.cursor]
-            else:
-                return
-                
-            self.cursor += 1
-            
-            if len(input) > self.cursor:
-                next = input[self.cursor]
-            else:
-                next = None
-
-            if ch == "\n" and not self.scanNewlines:
-                self.line += 1
-                indent = ""
-                
-            elif ch == "/" and next == "*":
-                self.cursor += 1
-                text = "/*"
-                inline = startLine == self.line and startLine > 1
-                commentStartLine = self.line
-                if startLine == self.line and not startOfFile:
-                    mode = "inline"
-                elif (self.line-1) > startLine:
-                    # distance before this comment means it is a comment block for a whole section (multiple lines of code)
-                    mode = "section"
-                else:
-                    # comment for maybe multiple following lines of code, but not that important (no visual white space divider)
-                    mode = "block"
-                    
-                while (True):
-                    try:
-                        ch = input[self.cursor]
-                        self.cursor += 1
-                    except IndexError:
-                        raise ParseError("Unterminated comment", self.fileId, self.line)
-                        
-                    if ch == "*":
-                        next = input[self.cursor]
-                        if next == "/":
-                            text += "*/"
-                            self.cursor += 1
-                            break
-                            
-                    elif ch == "\n":
-                        self.line += 1
-                        
-                    text += ch
-                    
-                
-                # Filter escaping on slash-star combinations in comment text
-                text = text.replace("*\/", "*/")
-                
-                try:
-                    self.comments.append(Comment.Comment(text, mode, commentStartLine, indent, self.fileId))
-                except Comment.CommentException as commentError:
-                    Console.error("Ignoring comment in %s: %s", self.fileId, commentError)
-                    
-                    
-            elif ch == "/" and next == "/":
-                self.cursor += 1
-                text = "//"
-                if startLine == self.line and not startOfFile:
-                    mode = "inline"
-                elif (self.line-1) > startLine:
-                    # distance before this comment means it is a comment block for a whole section (multiple lines of code)
-                    mode = "section"
-                else:
-                    # comment for maybe multiple following lines of code, but not that important (no visual white space divider)
-                    mode = "block"
-                    
-                while (True):
-                    try:
-                        ch = input[self.cursor]
-                        self.cursor += 1
-                    except IndexError:
-                        # end of file etc.
-                        break
-
-                    if ch == "\n":
-                        self.line += 1
-                        break
-                    
-                    text += ch
-                    
-                try:
-                    self.comments.append(Comment.Comment(text, mode, self.line-1, "", self.fileId))
-                except Comment.CommentException:
-                    Console.error("Ignoring comment in %s: %s", self.fileId, commentError)
-
-            # check for whitespace, also for special cases like 0xA0
-            elif ch in "\xA0 \t":
-                indent += ch
-
-            else:
-                self.cursor -= 1
-                return
-
-
-    # Lexes the exponential part of a number, if present. Returns True if an
-    # exponential part was found.
-    def lexExponent(self):
-        input = self.source
-        next = input[self.cursor]
-        if next == "e" or next == "E":
-            self.cursor += 1
-            ch = input[self.cursor]
-            self.cursor += 1
-            if ch == "+" or ch == "-":
-                ch = input[self.cursor]
-                self.cursor += 1
-
-            if ch < "0" or ch > "9":
-                raise ParseError("Missing exponent", self.fileId, self.line)
-
-            while(True):
-                ch = input[self.cursor]
-                self.cursor += 1
-                if not (ch >= "0" and ch <= "9"):
-                    break
-                
-            self.cursor -= 1
-            return True
-
-        return False
-
-
-    def lexZeroNumber(self, ch):
-        token = self.token
-        input = self.source
-        token.type = "number"
-
-        ch = input[self.cursor]
-        self.cursor += 1
-        if ch == ".":
-            while(True):
-                ch = input[self.cursor]
-                self.cursor += 1
-                if not (ch >= "0" and ch <= "9"):
-                    break
-                
-            self.cursor -= 1
-            self.lexExponent()
-            token.value = input[token.start:self.cursor]
-            
-        elif ch == "x" or ch == "X":
-            while(True):
-                ch = input[self.cursor]
-                self.cursor += 1
-                if not ((ch >= "0" and ch <= "9") or (ch >= "a" and ch <= "f") or (ch >= "A" and ch <= "F")):
-                    break
-                    
-            self.cursor -= 1
-            token.value = input[token.start:self.cursor]
-
-        elif ch >= "0" and ch <= "7":
-            while(True):
-                ch = input[self.cursor]
-                self.cursor += 1
-                if not (ch >= "0" and ch <= "7"):
-                    break
-                    
-            self.cursor -= 1
-            token.value = input[token.start:self.cursor]
-
-        else:
-            self.cursor -= 1
-            self.lexExponent()     # 0E1, &c.
-            token.value = 0
-    
-
-    def lexNumber(self, ch):
-        token = self.token
-        input = self.source
-        token.type = "number"
-
-        floating = False
-        while(True):
-            ch = input[self.cursor]
-            self.cursor += 1
-            
-            if ch == "." and not floating:
-                floating = True
-                ch = input[self.cursor]
-                self.cursor += 1
-                
-            if not (ch >= "0" and ch <= "9"):
-                break
-
-        self.cursor -= 1
-
-        exponent = self.lexExponent()
-        segment = input[token.start:self.cursor]
-        
-        # Protect float or exponent numbers
-        if floating or exponent:
-            token.value = segment
-        else:
-            token.value = int(segment)
-
-
-    def lexDot(self, ch):
-        token = self.token
-        input = self.source
-        next = input[self.cursor]
-        
-        if next >= "0" and next <= "9":
-            while (True):
-                ch = input[self.cursor]
-                self.cursor += 1
-                if not (ch >= "0" and ch <= "9"):
-                    break
-
-            self.cursor -= 1
-            self.lexExponent()
-
-            token.type = "number"
-            token.value = input[token.start:self.cursor]
-
-        else:
-            token.type = "dot"
-
-
-    def lexString(self, ch):
-        token = self.token
-        input = self.source
-        token.type = "string"
-
-        hasEscapes = False
-        delim = ch
-        ch = input[self.cursor]
-        self.cursor += 1
-        while ch != delim:
-            if ch == "\\":
-                hasEscapes = True
-                self.cursor += 1
-
-            ch = input[self.cursor]
-            self.cursor += 1
-
-        if hasEscapes:
-            token.value = eval(input[token.start:self.cursor])
-        else:
-            token.value = input[token.start+1:self.cursor-1]
-
-
-    def lexRegExp(self, ch):
-        token = self.token
-        input = self.source
-        token.type = "regexp"
-
-        while (True):
-            try:
-                ch = input[self.cursor]
-                self.cursor += 1
-            except IndexError:
-                raise ParseError("Unterminated regex", self.fileId, self.line)
-
-            if ch == "\\":
-                self.cursor += 1
-                
-            elif ch == "[":
-                while (True):
-                    if ch == "\\":
-                        self.cursor += 1
-
-                    try:
-                        ch = input[self.cursor]
-                        self.cursor += 1
-                    except IndexError:
-                        raise ParseError("Unterminated character class", self.fileId, self.line)
-                    
-                    if ch == "]":
-                        break
-                    
-            if ch == "/":
-                break
-
-        while(True):
-            ch = input[self.cursor]
-            self.cursor += 1
-            if not (ch >= "a" and ch <= "z"):
-                break
-
-        self.cursor -= 1
-        token.value = input[token.start:self.cursor]
-    
-
-    def lexOp(self, ch):
-        token = self.token
-        input = self.source
-
-        op = ch
-        while(True):
-            try:
-                next = input[self.cursor]
-            except IndexError:
-                break
-                
-            if (op + next) in operatorNames:
-                self.cursor += 1
-                op += next
-            else:
-                break
-        
-        try:
-            next = input[self.cursor]
-        except IndexError:
-            next = None
-
-        if next == "=" and op in assignOperators:
-            self.cursor += 1
-            token.type = "assign"
-            token.assignOp = operatorNames[op]
-            op += "="
-            
-        else:
-            token.type = operatorNames[op]
-            token.assignOp = None
-
-
-    # FIXME: Unicode escape sequences
-    # FIXME: Unicode identifiers
-    def lexIdent(self, ch):
-        token = self.token
-        input = self.source
-
-        try:
-            while True:
-                ch = input[self.cursor]
-                self.cursor += 1
-            
-                if not ((ch >= "a" and ch <= "z") or (ch >= "A" and ch <= "Z") or (ch >= "0" and ch <= "9") or ch == "$" or ch == "_"):
-                    break
-                    
-        except IndexError:
-            self.cursor += 1
-            pass
-        
-        # Put the non-word character back.
-        self.cursor -= 1
-
-        identifier = input[token.start:self.cursor]
-        if identifier in Lang.keywords:
-            token.type = identifier
-        else:
-            token.type = "identifier"
-            token.value = identifier
-
-
-    def get(self, scanOperand=False):
-        """ 
-        It consumes input *only* if there is no lookahead.
-        Dispatches to the appropriate lexing function depending on the input.
-        """
-        while self.lookahead:
-            self.lookahead -= 1
-            self.tokenIndex = (self.tokenIndex + 1) & 3
-            token = self.tokens[self.tokenIndex]
-            if token.type != "newline" or self.scanNewlines:
-                return token.type
-
-        self.skip()
-
-        self.tokenIndex = (self.tokenIndex + 1) & 3
-        self.tokens[self.tokenIndex] = token = Token()
-
-        token.start = self.cursor
-        token.line = self.line
-
-        input = self.source
-        if self.cursor == len(input):
-            token.end = token.start
-            token.type = "end"
-            return token.type
-
-        ch = input[self.cursor]
-        self.cursor += 1
-        
-        if (ch >= "a" and ch <= "z") or (ch >= "A" and ch <= "Z") or ch == "$" or ch == "_":
-            self.lexIdent(ch)
-        
-        elif scanOperand and ch == "/":
-            self.lexRegExp(ch)
-        
-        elif ch == ".":
-            self.lexDot(ch)
-
-        elif self.scanNewlines and ch == "\n":
-            token.type = "newline"
-            self.line += 1
-
-        elif ch in operatorNames:
-            self.lexOp(ch)
-        
-        elif ch >= "1" and ch <= "9":
-            self.lexNumber(ch)
-        
-        elif ch == "0":
-            self.lexZeroNumber(ch)
-        
-        elif ch == '"' or ch == "'":
-            self.lexString(ch)
-        
-        else:
-            raise ParseError("Illegal token: %s (Code: %s)" % (ch, ord(ch)), self.fileId, self.line)
-
-        token.end = self.cursor
-        return token.type
-        
-
-    def unget(self):
-        """ Match depends on unget returning undefined."""
-        self.lookahead += 1
-        
-        if self.lookahead == 4: 
-            raise ParseError("PANIC: too much lookahead!", self.fileId, self.line)
-        
-        self.tokenIndex = (self.tokenIndex - 1) & 3
-        
-    
-    def save(self):
-        return {
-            "cursor" : self.cursor,
-            "tokenIndex": self.tokenIndex,
-            "tokens": copy.copy(self.tokens),
-            "lookahead": self.lookahead,
-            "scanNewlines": self.scanNewlines,
-            "line": self.line
-        }
-
-    
-    def rewind(self, point):
-        self.cursor = point["cursor"]
-        self.tokenIndex = point["tokenIndex"]
-        self.tokens = copy.copy(point["tokens"])
-        self.lookahead = point["lookahead"]
-        self.scanNewline = point["scanNewline"]
-        self.line = point["line"]
--- a/ThirdParty/Jasy/jasy/js/util/__init__.py	Thu Jan 10 14:23:49 2019 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,16 +0,0 @@
-#
-# Jasy - Web Tooling Framework
-# Copyright 2010-2012 Zynga Inc.
-#
-
-#
-# minimized for using just the parser within eric6
-# Copyright (c) 2013 - 2019 Detlev Offenbach <detlev@die-offenbachs.de>
-#
-
-from __future__ import unicode_literals
-
-pseudoTypes = set(["any", "var", "undefined", "null", "true", "false", "this",
-                   "arguments"])
-builtinTypes = set(["Object", "String", "Number", "Boolean", "Array", "Function",
-                    "RegExp", "Date"])
--- a/ThirdParty/Jasy/jasy/parse/AbstractNode.py	Thu Jan 10 14:23:49 2019 +0100
+++ b/ThirdParty/Jasy/jasy/parse/AbstractNode.py	Sat Feb 02 11:12:54 2019 +0100
@@ -3,8 +3,6 @@
 # Copyright 2013-2014 Sebastian Werner
 #
 
-from __future__ import unicode_literals
-
 import json, copy
 
 class AbstractNode(list):
@@ -332,7 +330,7 @@
         """Returns the source code of the node"""
 
         if not self.tokenizer:
-            raise Exception("Could not find source for node '%s'" % self.type)
+            raise Exception("Could not find source for node '%s'" % node.type)
 
         if getattr(self, "start", None) is not None:
             if getattr(self, "end", None) is not None:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ThirdParty/Jasy/jasy/script/api/Comment.py	Sat Feb 02 11:12:54 2019 +0100
@@ -0,0 +1,677 @@
+#
+# Jasy - Web Tooling Framework
+# Copyright 2010-2012 Zynga Inc.
+# Copyright 2013-2014 Sebastian Werner
+#
+
+from __future__ import unicode_literals
+
+import re
+
+import jasy.core.Text as Text
+import jasy.core.Console as Console
+
+from jasy import UserError
+from jasy.script.util import *
+
+
+# Used to measure the doc indent size (with leading stars in front of content)
+docIndentReg = re.compile(r"^(\s*\*\s*)(\S*)")
+
+# Used to split type lists as supported by throw, return and params
+listSplit = re.compile("\s*\|\s*")
+
+# Used to remove markup sequences after doc processing of comment text
+stripMarkup = re.compile(r"<.*?>")
+
+
+
+# Matches return blocks in comments
+returnMatcher = re.compile(r"^\s*\{([a-zA-Z0-9_ \.\|\[\]]+)\}")
+
+# Matches type definitions in comments
+typeMatcher = re.compile(r"^\s*\{=([a-zA-Z0-9_ \.]+)\}")
+
+# Matches tags
+tagMatcher = re.compile(r"#([a-zA-Z][a-zA-Z0-9]+)(\((\S+)\))?(\s|$)")
+
+# Matches param declarations in own dialect
+paramMatcher = re.compile(r"@([a-zA-Z0-9_][a-zA-Z0-9_\.]*[a-zA-Z0-9_]|[a-zA-Z0-9_]+)(\s*\{([a-zA-Z0-9_ \.\|\[\]]+?)(\s*\.{3}\s*)?((\s*\?\s*(\S+))|(\s*\?\s*))?\})?")
+
+# Matches links in own dialect
+linkMatcher = re.compile(r"(\{((static|member|property|event)\:)?([a-zA-Z0-9_\.]+)?(\#([a-zA-Z0-9_]+))?\})")
+
+# matches backticks and has a built-in failsafe for backticks which do not terminate on the same line
+tickMatcher = re.compile(r"(`[^\n`]*?`)")
+
+
+class CommentException(Exception):
+    """
+    Thrown when errors during comment processing are detected.
+    """
+
+    def __init__(self, message, lineNo=0):
+        Exception.__init__(self, "Comment error: %s (line: %s)" % (message, lineNo+1))
+
+
+
+
+class Comment():
+    """
+    Comment class is attached to parsed nodes and used to store all comment related information.
+
+    The class supports a new Markdown and TomDoc inspired dialect to make developers life easier and work less repeative.
+    """
+
+    # Relation to code
+    context = None
+
+    # Dictionary of tags
+    tags = None
+
+    # Dictionary of params
+    params = None
+
+    # List of return types
+    returns = None
+
+    # Static type
+    type = None
+
+    # Collected text of the comment (without the extracted doc relevant data)
+    text = None
+
+    # Text with extracted / parsed data
+    __processedText = None
+
+    # Text of the comment converted to HTML including highlighting (only for doc comment)
+    __highlightedText = None
+
+    # Text / Code Blocks in the comment
+    __blocks = None
+
+
+    def __init__(self, text, context=None, lineNo=0, indent="", fileId=None):
+
+        # Store context (relation to code)
+        self.context = context
+
+        # Store fileId
+        self.fileId = fileId
+
+        # Figure out the type of the comment based on the starting characters
+
+        # Inline comments
+        if text.startswith("//"):
+            # "// hello" => "   hello"
+            text = "  " + text[2:]
+            self.variant = "single"
+
+        # Doc comments
+        elif text.startswith("/**"):
+            # "/** hello */" => "    hello "
+            text = "   " + text[3:-2]
+            self.variant = "doc"
+
+        # Protected comments which should not be removed (e.g these are used for license blocks)
+        elif text.startswith("/*!"):
+            # "/*! hello */" => "    hello "
+            text = "   " + text[3:-2]
+            self.variant = "protected"
+
+        # A normal multiline comment
+        elif text.startswith("/*"):
+            # "/* hello */" => "   hello "
+            text = "  " + text[2:-2]
+            self.variant = "multi"
+
+        else:
+            raise CommentException("Invalid comment text: %s" % text, lineNo)
+
+        # Multi line comments need to have their indentation removed
+        if "\n" in text:
+            text = self.__outdent(text, indent, lineNo)
+
+        # For single line comments strip the surrounding whitespace
+        else:
+            # " hello " => "hello"
+            text = text.strip()
+
+        # The text of the comment before any processing took place
+        self.text = text
+
+
+        # Perform annotation parsing, markdown conversion and code highlighting on doc blocks
+        if self.variant == "doc":
+
+            # Separate text and code blocks
+            self.__blocks = self.__splitBlocks(text)
+
+            # Re-combine everything and apply processing and formatting
+            plainText = '' # text without annotations but with markdown
+            for b in self.__blocks:
+
+                if b["type"] == "comment":
+
+                    processed = self.__processDoc(b["text"], lineNo)
+                    b["processed"] = processed
+
+                    if "<" in processed:
+                        plainText += stripMarkup.sub("", processed)
+
+                    else:
+                        plainText += processed
+
+                else:
+                    plainText += "\n\n" + b["text"] + "\n\n"
+
+            # The without any annotations
+            self.text = plainText.strip()
+
+
+    def __splitBlocks(self, text):
+        """
+        Splits up text and code blocks in comments.
+
+        This will try to use hoedown for Markdown parsing if available and will
+        fallback to a simpler implementation in order to allow processing of
+        doc parameters and links without hoedown being installed.
+        """
+
+        if not Text.supportsMarkdown:
+            return self.__splitSimple(text)
+
+        marked = Text.markdownToHtml(text)
+
+        def unescape(html):
+            html = html.replace('&lt;', '<')
+            html = html.replace('&gt;', '>')
+            html = html.replace('&amp;', '&')
+            html = html.replace('&quot;', '"')
+            return html.replace('&#39;', "'")
+
+        parts = []
+
+        lineNo = 0
+        lines = text.split("\n")
+        markedLines = marked.split("\n")
+
+        i = 0
+        while i < len(markedLines):
+
+            l = markedLines[i]
+
+            # the original text of the line
+            parsed = unescape(stripMarkup.sub("", l))
+
+            # start of a code block, grab all text before it and move it into a block
+            if l.startswith('<pre><code>'):
+
+                # everything since the last code block and before this one must be text
+                comment = []
+                for s in range(lineNo, len(lines)):
+
+                    source = lines[s]
+                    if source.strip() == parsed.strip():
+                        lineNo = s
+                        break
+
+                    comment.append(source)
+
+                parts.append({
+                    "type": "comment",
+                    "text": "\n".join(comment)
+                })
+
+                # Find the end of the code block
+                e = i
+                while i < len(markedLines):
+                    l = markedLines[i]
+                    i += 1
+
+                    if l.startswith('</code></pre>'):
+                        break
+
+                lineCount = (i - e) - 1
+
+                # add the code block
+                parts.append({
+                    "type": "code",
+                    "text": "\n".join(lines[lineNo:lineNo + lineCount])
+                })
+
+                lineNo += lineCount
+
+            else:
+                i += 1
+
+        # append the rest of the comment as text
+        parts.append({
+            "type": "comment",
+            "text": "\n".join(lines[lineNo:])
+        })
+
+        return parts
+
+
+    def __splitSimple(self, text):
+        """Splits comment text and code blocks by manually parsing a subset of markdown"""
+
+        inCode = False
+        oldIndent = 0
+        parts = []
+        wasEmpty = False
+        wasList = False
+
+        lineNo = 0
+        lines = text.split("\n")
+
+        for s, l in enumerate(lines):
+
+            # ignore empty lines
+            if not l.strip() == "":
+
+                # get indentation value and change
+                indent = len(l) - len(l.lstrip())
+                change = indent - oldIndent
+
+                # detect code blocks
+                if change >= 4 and wasEmpty:
+                    if not wasList:
+                        oldIndent = indent
+                        inCode = True
+
+                        parts.append({
+                            "type": "comment",
+                            "text": "\n".join(lines[lineNo:s])
+                        })
+
+                        lineNo = s
+
+                # detect outdents
+                elif change < 0:
+                    inCode = False
+
+                    parts.append({
+                        "type": "code",
+                        "text": "\n".join(lines[lineNo:s - 1])
+                    })
+
+                    lineNo = s
+
+                # only keep track of old previous indentation outside of comments
+                if not inCode:
+                    oldIndent = indent
+
+                # remember whether this marked a list or not
+                wasList = l.strip().startswith('-') or l.strip().startswith('*')
+                wasEmpty = False
+
+            else:
+                wasEmpty = True
+
+        parts.append({
+            "type": "code" if inCode else "comment",
+            "text": "\n".join(lines[lineNo:])
+        })
+
+        return parts
+
+
+    def getHtml(self, highlight=True):
+        """
+        Returns the comment text converted to HTML
+
+        :param highlight: Whether to highlight the code
+        :type highlight: bool
+        """
+
+        if not Text.supportsMarkdown:
+            raise UserError("Markdown is not supported by the system. Documentation comments could converted to HTML.")
+
+        if highlight:
+
+            if self.__highlightedText is None:
+
+                highlightedText = ""
+
+                for block in self.__blocks:
+
+                    if block["type"] == "comment":
+                        highlightedText += Text.highlightCodeBlocks(Text.markdownToHtml(block["processed"]))
+                    else:
+                        highlightedText += "\n%s" % Text.highlightCodeBlocks(Text.markdownToHtml(block["text"]))
+
+                self.__highlightedText = highlightedText
+
+            return self.__highlightedText
+
+        else:
+
+            if self.__processedText is None:
+
+                processedText = ""
+
+                for block in self.__blocks:
+
+                    if block["type"] == "comment":
+                        processedText += Text.markdownToHtml(block["processed"])
+                    else:
+                        processedText += "\n%s\n\n" % block["text"]
+
+                self.__processedText = processedText.strip()
+
+            return self.__processedText
+
+
+    def hasContent(self):
+        return self.variant == "doc" and len(self.text)
+
+
+    def getTags(self):
+        return self.tags
+
+
+    def hasTag(self, name):
+        if not self.tags:
+            return False
+
+        return name in self.tags
+
+
+    def __outdent(self, text, indent, startLineNo):
+        """
+        Outdent multi line comment text and filtering empty lines
+        """
+
+        lines = []
+
+        # First, split up the comments lines and remove the leading indentation
+        for lineNo, line in enumerate((indent+text).split("\n")):
+
+            if line.startswith(indent):
+                lines.append(line[len(indent):].rstrip())
+
+            elif line.strip() == "":
+                lines.append("")
+
+            else:
+                # Only warn for doc comments, otherwise it might just be code commented out
+                # which is sometimes formatted pretty crazy when commented out
+                if self.variant == "doc":
+                    Console.warn("Could not outdent doc comment at line %s in %s", startLineNo+lineNo, self.fileId)
+
+                return text
+
+        # Find first line with real content, then grab the one after it to get the
+        # characters which need
+        outdentString = ""
+        for lineNo, line in enumerate(lines):
+
+            if line != "" and line.strip() != "":
+                matchedDocIndent = docIndentReg.match(line)
+
+                if not matchedDocIndent:
+                    # As soon as we find a non doc indent like line we stop
+                    break
+
+                elif matchedDocIndent.group(2) != "":
+                    # otherwise we look for content behind the indent to get the
+                    # correct real indent (with spaces)
+                    outdentString = matchedDocIndent.group(1)
+                    break
+
+            lineNo += 1
+
+        # Process outdenting to all lines (remove the outdentString from the start of the lines)
+        if outdentString != "":
+
+            lineNo = 0
+            outdentStringLen = len(outdentString)
+
+            for lineNo, line in enumerate(lines):
+                if len(line) <= outdentStringLen:
+                    lines[lineNo] = ""
+
+                else:
+                    if not line.startswith(outdentString):
+
+                        # Only warn for doc comments, otherwise it might just be code commented out
+                        # which is sometimes formatted pretty crazy when commented out
+                        if self.variant == "doc":
+                            Console.warn("Invalid indentation in doc comment at line %s in %s", startLineNo+lineNo, self.fileId)
+
+                    else:
+                        lines[lineNo] = line[outdentStringLen:]
+
+        # Merge final lines and remove leading and trailing new lines
+        return "\n".join(lines).strip("\n")
+
+
+    def __processDoc(self, text, startLineNo):
+
+        text = self.__extractStaticType(text)
+        text = self.__extractReturns(text)
+        text = self.__extractTags(text)
+
+        # Collapse new empty lines at start/end
+        text = text.strip("\n\t ")
+
+        parsed = ''
+
+        # Now parse only the text outside of backticks
+        last = 0
+        def split(match):
+
+            # Grab the text before the back tick and process any parameters in it
+            nonlocal parsed
+            nonlocal last
+
+            start, end = match.span()
+            before = text[last:start]
+            parsed += self.__processParams(before) + match.group(1)
+            last = end
+
+        tickMatcher.sub(split, text)
+
+        # add the rest of the text
+        parsed += self.__processParams(text[last:])
+
+        text = self.__processLinks(parsed)
+
+        return text
+
+
+    def __splitTypeList(self, decl):
+
+        if decl is None:
+            return decl
+
+        splitted = listSplit.split(decl.strip())
+
+        result = []
+        for entry in splitted:
+
+            # Figure out if it is marked as array
+            isArray = False
+            if entry.endswith("[]"):
+                isArray = True
+                entry = entry[:-2]
+
+            store = {
+                "name" : entry
+            }
+
+            if isArray:
+                store["array"] = True
+
+            if entry in builtinTypes:
+                store["builtin"] = True
+
+            if entry in pseudoTypes:
+                store["pseudo"] = True
+
+            result.append(store)
+
+        return result
+
+
+
+    def __extractReturns(self, text):
+        """
+        Extracts leading return defintion (when type is function)
+        """
+
+        def collectReturn(match):
+            self.returns = self.__splitTypeList(match.group(1))
+            return ""
+
+        return returnMatcher.sub(collectReturn, text)
+
+
+
+    def __extractStaticType(self, text):
+        """
+        Extracts leading type defintion (when value is a static type)
+        """
+
+        def collectType(match):
+            self.type = match.group(1).strip()
+            return ""
+
+        return typeMatcher.sub(collectType, text)
+
+
+
+    def __extractTags(self, text):
+        """
+        Extract all tags inside the give doc comment. These are replaced from
+        the text and collected inside the "tags" key as a dict.
+        """
+
+        def collectTags(match):
+             if not self.tags:
+                 self.tags = {}
+
+             name = match.group(1)
+             param = match.group(3)
+
+             if name in self.tags:
+                 self.tags[name].add(param)
+             elif param:
+                 self.tags[name] = set([param])
+             else:
+                 self.tags[name] = True
+
+             return ""
+
+        return tagMatcher.sub(collectTags, text)
+
+
+    def __processParams(self, text):
+
+        def collectParams(match):
+
+            paramName = match.group(1)
+            paramTypes = match.group(3)
+            paramDynamic = match.group(4) is not None
+            paramOptional = match.group(5) is not None
+            paramDefault = match.group(7)
+
+            if paramTypes:
+                paramTypes = self.__splitTypeList(paramTypes)
+
+            if self.params is None:
+                self.params = {}
+
+            params = self.params
+            fullName = match.group(1).strip()
+            names = fullName.split('.')
+
+            for i, mapName in enumerate(names):
+
+                # Ensure we have the map object in the params
+                if not mapName in params:
+                    params[mapName] = {}
+
+                # Add new entries and overwrite if a type is defined in this entry
+                if not mapName in params or paramTypes is not None:
+
+                    # Make sure to not overwrite something like @options {Object} with the type of @options.x {Number}
+                    if i == len(names) - 1:
+
+                        paramEntry = params[mapName] = {}
+
+                        if paramTypes is not None:
+                            paramEntry["type"] = paramTypes
+
+                        if paramDynamic:
+                            paramEntry["dynamic"] = paramDynamic
+
+                        if paramOptional:
+                            paramEntry["optional"] = paramOptional
+
+                        if paramDefault is not None:
+                            paramEntry["default"] = paramDefault
+
+                    else:
+                        paramEntry = params[mapName]
+
+
+                else:
+                    paramEntry = params[mapName]
+
+                # create fields for new map level
+                if i + 1 < len(names):
+                    if not "fields" in paramEntry:
+                        paramEntry["fields"] = {}
+
+                    params = paramEntry["fields"]
+
+            return '<code class="param">%s</code>' % fullName
+
+        return paramMatcher.sub(collectParams, text)
+
+
+    def __processLinks(self, text):
+
+        def formatTypes(match):
+
+            parsedSection = match.group(3)
+            parsedFile = match.group(4)
+            parsedItem = match.group(6)
+
+            # Do not match {}
+            if parsedSection is None and parsedFile is None and parsedItem is None:
+                return match.group(1)
+
+            # Minor corrections
+            if parsedSection and not parsedItem:
+                parsedSection = ""
+
+            attr = ""
+            link = ""
+            label = ""
+
+            if parsedSection:
+                link += '%s:' % parsedSection
+
+            if parsedFile:
+                link += parsedFile
+                label += parsedFile
+
+            if parsedItem:
+                link += "~%s" % parsedItem
+                if label == "":
+                    label = parsedItem
+                else:
+                    label += "#%s" % parsedItem
+
+            # add link to attributes list
+            attr += ' href="#%s"' % link
+
+            # build final HTML
+            return '<a%s><code>%s</code></a>' % (attr, label)
+
+        return linkMatcher.sub(formatTypes, text)
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ThirdParty/Jasy/jasy/script/api/Text.py	Sat Feb 02 11:12:54 2019 +0100
@@ -0,0 +1,38 @@
+#
+# Jasy - Web Tooling Framework
+# Copyright 2010-2012 Zynga Inc.
+# Copyright 2013-2014 Sebastian Werner
+#
+
+from __future__ import unicode_literals
+
+import re
+import jasy.core.Console as Console
+
+
+# Used to filter first paragraph from HTML
+paragraphExtract = re.compile(r"^(.*?)(\. |\? |\! |$)")
+newlineMatcher = re.compile(r"\n")
+
+# Used to remove markup sequences after doc processing of comment text
+stripMarkup = re.compile(r"<.*?>")
+
+def extractSummary(text):
+    try:
+        text = stripMarkup.sub("", newlineMatcher.sub(" ", text))
+        matched = paragraphExtract.match(text)
+    except TypeError:
+        matched = None
+
+    if matched:
+        summary = matched.group(1)
+        if summary is not None:
+            if not summary.endswith((".", "!", "?")):
+                summary = summary.strip() + "."
+            return summary
+
+    else:
+        Console.warn("Unable to extract summary for: %s", text)
+
+    return None
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ThirdParty/Jasy/jasy/script/output/Compressor.py	Sat Feb 02 11:12:54 2019 +0100
@@ -0,0 +1,564 @@
+#
+# Jasy - Web Tooling Framework
+# Copyright 2010-2012 Zynga Inc.
+# Copyright 2013-2014 Sebastian Werner
+#
+
+from __future__ import unicode_literals
+
+import re, sys, json
+
+from jasy.script.tokenize.Lang import keywords
+from jasy.script.parse.Lang import expressions, futureReserved
+
+high_unicode = re.compile(r"\\u[2-9A-Fa-f][0-9A-Fa-f]{3}")
+ascii_encoder = json.JSONEncoder(ensure_ascii=True)
+unicode_encoder = json.JSONEncoder(ensure_ascii=False)
+
+#
+# Class
+#
+
+class Compressor:
+    __semicolonSymbol = ";"
+    __commaSymbol = ","
+
+
+    def __init__(self, format=None):
+        if format:
+            if format.has("semicolon"):
+                self.__semicolonSymbol = ";\n"
+
+            if format.has("comma"):
+                self.__commaSymbol = ",\n"
+
+        self.__forcedSemicolon = False
+
+
+
+    #
+    # Main
+    #
+
+    def compress(self, node):
+        type = node.type
+        result = None
+
+        if type in self.__simple:
+            result = type
+        elif type in self.__prefixes:
+            if getattr(node, "postfix", False):
+                result = self.compress(node[0]) + self.__prefixes[node.type]
+            else:
+                result = self.__prefixes[node.type] + self.compress(node[0])
+
+        elif type in self.__dividers:
+            first = self.compress(node[0])
+            second = self.compress(node[1])
+            divider = self.__dividers[node.type]
+
+            # Fast path
+            if node.type not in ("plus", "minus"):
+                result = "%s%s%s" % (first, divider, second)
+
+            # Special code for dealing with situations like x + ++y and y-- - x
+            else:
+                result = first
+                if first.endswith(divider):
+                    result += " "
+
+                result += divider
+
+                if second.startswith(divider):
+                    result += " "
+
+                result += second
+
+        else:
+            try:
+                result = getattr(self, "type_%s" % type)(node)
+            except AttributeError:
+                raise Exception("Script compressor does not support type '%s' from line %s in file %s" % (type, node.line, node.getFileName()))
+
+        if getattr(node, "parenthesized", None):
+            return "(%s)" % result
+        else:
+            return result
+
+
+
+    #
+    # Helpers
+    #
+
+    def __statements(self, node):
+        result = []
+        for child in node:
+            result.append(self.compress(child))
+
+        return "".join(result)
+
+    def __handleForcedSemicolon(self, node):
+        if node.type == "semicolon" and not hasattr(node, "expression"):
+            self.__forcedSemicolon = True
+
+    def __addSemicolon(self, result):
+        if not result.endswith(self.__semicolonSymbol):
+            if self.__forcedSemicolon:
+                self.__forcedSemicolon = False
+
+            return result + self.__semicolonSymbol
+
+        else:
+            return result
+
+    def __removeSemicolon(self, result):
+        if self.__forcedSemicolon:
+            self.__forcedSemicolon = False
+            return result
+
+        if result.endswith(self.__semicolonSymbol):
+            return result[:-len(self.__semicolonSymbol)]
+        else:
+            return result
+
+
+    #
+    # Data
+    #
+
+    __simple_property = re.compile(r"^[a-zA-Z_$][a-zA-Z0-9_$]*$")
+    __number_property = re.compile(r"^[0-9]+$")
+
+    __simple = ["true", "false", "null", "this", "debugger"]
+
+    __dividers = {
+        "plus"        : '+',
+        "minus"       : '-',
+        "mul"         : '*',
+        "div"         : '/',
+        "mod"         : '%',
+        "dot"         : '.',
+        "or"          : "||",
+        "and"         : "&&",
+        "strict_eq"   : '===',
+        "eq"          : '==',
+        "strict_ne"   : '!==',
+        "ne"          : '!=',
+        "lsh"         : '<<',
+        "le"          : '<=',
+        "lt"          : '<',
+        "ursh"        : '>>>',
+        "rsh"         : '>>',
+        "ge"          : '>=',
+        "gt"          : '>',
+        "bitwise_or"  : '|',
+        "bitwise_xor" : '^',
+        "bitwise_and" : '&'
+    }
+
+    __prefixes = {
+        "increment"   : "++",
+        "decrement"   : "--",
+        "bitwise_not" : '~',
+        "not"         : "!",
+        "unary_plus"  : "+",
+        "unary_minus" : "-",
+        "delete"      : "delete ",
+        "new"         : "new ",
+        "typeof"      : "typeof ",
+        "void"        : "void "
+    }
+
+
+
+    #
+    # Script Scope
+    #
+
+    def type_script(self, node):
+        return self.__statements(node)
+
+
+
+    #
+    # Expressions
+    #
+
+    def type_comma(self, node):
+        return self.__commaSymbol.join(map(self.compress, node))
+
+    def type_object_init(self, node):
+        return "{%s}" % self.__commaSymbol.join(map(self.compress, node))
+
+    def type_property_init(self, node):
+        key = self.compress(node[0])
+        value = self.compress(node[1])
+
+        if type(key) in (int, float):
+            pass
+
+        elif self.__number_property.match(key):
+            pass
+
+        # Protect keywords and special characters
+        elif key in keywords or key in futureReserved or not self.__simple_property.match(key):
+            key = self.type_string(node[0])
+
+        return "%s:%s" % (key, value)
+
+    def type_array_init(self, node):
+        def helper(child):
+            return self.compress(child) if child != None else ""
+
+        return "[%s]" % ",".join(map(helper, node))
+
+    def type_array_comp(self, node):
+        return "[%s %s]" % (self.compress(node.expression), self.compress(node.tail))
+
+    def type_string(self, node):
+        # Omit writing real high unicode character which are not supported well by browsers
+        ascii = ascii_encoder.encode(node.value)
+
+        if high_unicode.search(ascii):
+            return ascii
+        else:
+            return unicode_encoder.encode(node.value)
+
+    def type_number(self, node):
+        value = node.value
+
+        # Special handling for protected float/exponential
+        if type(value) == str:
+            # Convert zero-prefix
+            if value.startswith("0.") and len(value) > 2:
+                value = value[1:]
+
+            # Convert zero postfix
+            elif value.endswith(".0"):
+                value = value[:-2]
+
+        elif int(value) == value and node.parent.type != "dot":
+            value = int(value)
+
+        return "%s" % value
+
+    def type_regexp(self, node):
+        return node.value
+
+    def type_identifier(self, node):
+        return node.value
+
+    def type_list(self, node):
+        return ",".join(map(self.compress, node))
+
+    def type_index(self, node):
+        return "%s[%s]" % (self.compress(node[0]), self.compress(node[1]))
+
+    def type_declaration(self, node):
+        names = getattr(node, "names", None)
+        if names:
+            result = self.compress(names)
+        else:
+            result = node.name
+
+        initializer = getattr(node, "initializer", None)
+        if initializer:
+            result += "=%s" % self.compress(node.initializer)
+
+        return result
+
+    def type_assign(self, node):
+        assignOp = getattr(node, "assignOp", None)
+        operator = "=" if not assignOp else self.__dividers[assignOp] + "="
+
+        return self.compress(node[0]) + operator + self.compress(node[1])
+
+    def type_call(self, node):
+        return "%s(%s)" % (self.compress(node[0]), self.compress(node[1]))
+
+    def type_new_with_args(self, node):
+        result = "new %s" % self.compress(node[0])
+
+        # Compress new Object(); => new Object;
+        if len(node[1]) > 0:
+            result += "(%s)" % self.compress(node[1])
+        else:
+            parent = getattr(node, "parent", None)
+            if parent and parent.type is "dot":
+                result += "()"
+
+        return result
+
+    def type_exception(self, node):
+        return node.value
+
+    def type_generator(self, node):
+        """ Generator Expression """
+        result = self.compress(getattr(node, "expression"))
+        tail = getattr(node, "tail", None)
+        if tail:
+            result += " %s" % self.compress(tail)
+
+        return result
+
+    def type_comp_tail(self, node):
+        """  Comprehensions Tails """
+        result = self.compress(getattr(node, "for"))
+        guard = getattr(node, "guard", None)
+        if guard:
+            result += "if(%s)" % self.compress(guard)
+
+        return result
+
+    def type_in(self, node):
+        first = self.compress(node[0])
+        second = self.compress(node[1])
+
+        if first.endswith("'") or first.endswith('"'):
+            pattern = "%sin %s"
+        else:
+            pattern = "%s in %s"
+
+        return pattern % (first, second)
+
+    def type_instanceof(self, node):
+        first = self.compress(node[0])
+        second = self.compress(node[1])
+
+        return "%s instanceof %s" % (first, second)
+
+
+
+    #
+    # Statements :: Core
+    #
+
+    def type_block(self, node):
+        return "{%s}" % self.__removeSemicolon(self.__statements(node))
+
+    def type_let_block(self, node):
+        begin = "let(%s)" % ",".join(map(self.compress, node.variables))
+        if hasattr(node, "block"):
+            end = self.compress(node.block)
+        elif hasattr(node, "expression"):
+            end = self.compress(node.expression)
+
+        return begin + end
+
+    def type_const(self, node):
+        return self.__addSemicolon("const %s" % self.type_list(node))
+
+    def type_var(self, node):
+        return self.__addSemicolon("var %s" % self.type_list(node))
+
+    def type_let(self, node):
+        return self.__addSemicolon("let %s" % self.type_list(node))
+
+    def type_semicolon(self, node):
+        expression = getattr(node, "expression", None)
+        return self.__addSemicolon(self.compress(expression) if expression else "")
+
+    def type_label(self, node):
+        return self.__addSemicolon("%s:%s" % (node.label, self.compress(node.statement)))
+
+    def type_break(self, node):
+        return self.__addSemicolon("break" if not hasattr(node, "label") else "break %s" % node.label)
+
+    def type_continue(self, node):
+        return self.__addSemicolon("continue" if not hasattr(node, "label") else "continue %s" % node.label)
+
+
+    #
+    # Statements :: Functions
+    #
+
+    def type_function(self, node):
+        if node.type == "setter":
+            result = "set"
+        elif node.type == "getter":
+            result = "get"
+        else:
+            result = "function"
+
+        name = getattr(node, "name", None)
+        if name:
+            result += " %s" % name
+
+        params = getattr(node, "params", None)
+        result += "(%s)" % self.compress(params) if params else "()"
+
+        # keep expression closure format (may be micro-optimized for other code, too)
+        if getattr(node, "expressionClosure", False):
+            result += self.compress(node.body)
+        else:
+            result += "{%s}" % self.__removeSemicolon(self.compress(node.body))
+
+        return result
+
+    def type_getter(self, node):
+        return self.type_function(node)
+
+    def type_setter(self, node):
+        return self.type_function(node)
+
+    def type_return(self, node):
+        result = "return"
+        if hasattr(node, "value"):
+            valueCode = self.compress(node.value)
+
+            # Micro optimization: Don't need a space when a block/map/array/group/strings are returned
+            if not valueCode.startswith(("(","[","{","'",'"',"!","-","/")):
+                result += " "
+
+            result += valueCode
+
+        return self.__addSemicolon(result)
+
+
+
+    #
+    # Statements :: Exception Handling
+    #
+
+    def type_throw(self, node):
+        return self.__addSemicolon("throw %s" % self.compress(node.exception))
+
+    def type_try(self, node):
+        result = "try%s" % self.compress(node.tryBlock)
+
+        for catch in node:
+            if catch.type == "catch":
+                if hasattr(catch, "guard"):
+                    result += "catch(%s if %s)%s" % (self.compress(catch.exception), self.compress(catch.guard), self.compress(catch.block))
+                else:
+                    result += "catch(%s)%s" % (self.compress(catch.exception), self.compress(catch.block))
+
+        if hasattr(node, "finallyBlock"):
+            result += "finally%s" % self.compress(node.finallyBlock)
+
+        return result
+
+
+
+    #
+    # Statements :: Loops
+    #
+
+    def type_while(self, node):
+        result = "while(%s)%s" % (self.compress(node.condition), self.compress(node.body))
+        self.__handleForcedSemicolon(node.body)
+        return result
+
+
+    def type_do(self, node):
+        # block unwrapping don't help to reduce size on this loop type
+        # but if it happens (don't like to modify a global function to fix a local issue), we
+        # need to fix the body and re-add braces around the statement
+        body = self.compress(node.body)
+        if not body.startswith("{"):
+            body = "{%s}" % body
+
+        return self.__addSemicolon("do%swhile(%s)" % (body, self.compress(node.condition)))
+
+
+    def type_for_in(self, node):
+        # Optional variable declarations
+        varDecl = getattr(node, "varDecl", None)
+
+        # Body is optional - at least in comprehensions tails
+        body = getattr(node, "body", None)
+        if body:
+            body = self.compress(body)
+        else:
+            body = ""
+
+        result = "for"
+        if node.isEach:
+            result += " each"
+
+        result += "(%s in %s)%s" % (self.__removeSemicolon(self.compress(node.iterator)), self.compress(node.object), body)
+
+        if body:
+            self.__handleForcedSemicolon(node.body)
+
+        return result
+
+
+    def type_for(self, node):
+        setup = getattr(node, "setup", None)
+        condition = getattr(node, "condition", None)
+        update = getattr(node, "update", None)
+
+        result = "for("
+        result += self.__addSemicolon(self.compress(setup) if setup else "")
+        result += self.__addSemicolon(self.compress(condition) if condition else "")
+        result += self.compress(update) if update else ""
+        result += ")%s" % self.compress(node.body)
+
+        self.__handleForcedSemicolon(node.body)
+        return result
+
+
+
+    #
+    # Statements :: Conditionals
+    #
+
+    def type_hook(self, node):
+        """aka ternary operator"""
+        condition = node.condition
+        thenPart = node.thenPart
+        elsePart = node.elsePart
+
+        if condition.type == "not":
+            [thenPart,elsePart] = [elsePart,thenPart]
+            condition = condition[0]
+
+        return "%s?%s:%s" % (self.compress(condition), self.compress(thenPart), self.compress(elsePart))
+
+
+    def type_if(self, node):
+        result = "if(%s)%s" % (self.compress(node.condition), self.compress(node.thenPart))
+
+        elsePart = getattr(node, "elsePart", None)
+        if elsePart:
+            result += "else"
+
+            elseCode = self.compress(elsePart)
+
+            # Micro optimization: Don't need a space when the child is a block
+            # At this time the brace could not be part of a map declaration (would be a syntax error)
+            if not elseCode.startswith(("{", "(", ";")):
+                result += " "
+
+            result += elseCode
+
+            self.__handleForcedSemicolon(elsePart)
+
+        return result
+
+
+    def type_switch(self, node):
+        result = "switch(%s){" % self.compress(node.discriminant)
+        for case in node:
+            if case.type == "case":
+                labelCode = self.compress(case.label)
+                if labelCode.startswith('"'):
+                    result += "case%s:" % labelCode
+                else:
+                    result += "case %s:" % labelCode
+            elif case.type == "default":
+                result += "default:"
+            else:
+                continue
+
+            for statement in case.statements:
+                temp = self.compress(statement)
+                if len(temp) > 0:
+                    result += self.__addSemicolon(temp)
+
+        return "%s}" % self.__removeSemicolon(result)
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ThirdParty/Jasy/jasy/script/parse/Lang.py	Sat Feb 02 11:12:54 2019 +0100
@@ -0,0 +1,211 @@
+#
+# Jasy - Web Tooling Framework
+# Copyright 2010-2012 Zynga Inc.
+# Copyright 2013-2014 Sebastian Werner
+#
+
+from __future__ import unicode_literals
+
+futureReserved = set([
+    "abstract",
+    "boolean",
+    "byte",
+    "char",
+    "class",
+    "const",
+    "debugger",
+    "double",
+    "enum",
+    "export",
+    "extends",
+    "final",
+    "float",
+    "goto",
+    "implements",
+    "import",
+    "int",
+    "interface",
+    "long",
+    "native",
+    "package",
+    "private",
+    "protected",
+    "public",
+    "short",
+    "static",
+    "super",
+    "synchronized",
+    "throws",
+    "transient",
+    "volatile"
+])
+
+
+statements = [
+    # With semicolon at end
+    "semicolon",
+    "return",
+    "throw",
+    "label",
+    "break",
+    "continue",
+    "var",
+    "const",
+    "debugger",
+
+    # Only semicolon when no-block braces are created
+    "block",
+    "let_block",
+    "while",
+    "do",
+    "for",
+    "for_in",
+    "if",
+    "switch",
+    "hook",
+    "with",
+
+    # no semicolons
+    # function, setter and getter as statement_form or declared_form
+    "function",
+    "setter",
+    "getter",
+    "try",
+    "label"
+]
+
+
+# All allowed expression types of JavaScript 1.7
+# They may be separated by "comma" which is quite of special
+# and not allowed everywhere e.g. in conditional statements
+expressions = [
+    # Primary Expression - Part 1 (expressed form)
+    "function",
+
+    # Primary Expression - Part 2
+    "object_init",
+    "array_init",
+    "array_comp",
+
+    # Primary Expression - Part 3
+    "let",
+
+    # Primary Expression - Part 4
+    "null",
+    "this",
+    "true",
+    "false",
+    "identifier",
+    "number",
+    "string",
+    "regexp",
+
+    # Member Expression - Part 1
+    "new_with_args",
+    "new",
+
+    # Member Expression - Part 2
+    "dot",
+    "call",
+    "index",
+
+    # Unary Expression
+    "unary_plus",
+    "unary_minus",
+    "delete",
+    "void",
+    "typeof",
+    "not",
+    "bitwise_not",
+    "increment",
+    "decrement",
+
+    # Multiply Expression
+    "mul",
+    "div",
+    "mod",
+
+    # Add Expression
+    "plus",
+    "minus",
+
+    # Shift Expression
+    "lsh",
+    "rsh",
+    "ursh",
+
+    # Relational Expression
+    "lt",
+    "le",
+    "ge",
+    "gt",
+    "in",
+    "instanceof",
+
+    # Equality Expression
+    "eq",
+    "ne",
+    "strict_eq",
+    "strict_ne",
+
+    # BitwiseAnd Expression
+    "bitwise_and",
+
+    # BitwiseXor Expression
+    "bitwise_xor",
+
+    # BitwiseOr Expression
+    "bitwise_or",
+
+    # And Expression
+    "and",
+
+    # Or Expression
+    "or",
+
+    # Conditional Expression
+    "hook",
+
+    # Assign Expression
+    "assign",
+
+    # Expression
+    "comma"
+]
+
+
+
+
+def __createOrder():
+    expressions = [
+        ["comma"],
+        ["assign"],
+        ["hook"],
+        ["or"],
+        ["and"],
+        ["bitwise_or"],
+        ["bitwise_xor",],
+        ["bitwise_and"],
+        ["eq","ne","strict_eq","strict_ne"],
+        ["lt","le","ge","gt","in","instanceof"],
+        ["lsh","rsh","ursh"],
+        ["plus","minus"],
+        ["mul","div","mod"],
+        ["unary_plus","unary_minus","delete","void","typeof","not","bitwise_not","increment","decrement"],
+        ["dot","call","index"],
+        ["new_with_args","new"],
+        ["null","this","true","false","identifier","number","string","regexp"],
+        ["let"],
+        ["object_init","array_init","array_comp"],
+        ["function"]
+    ]
+
+    result = {}
+    for priority, itemList in enumerate(expressions):
+        for item in itemList:
+            result[item] = priority
+
+    return result
+
+expressionOrder = __createOrder()
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ThirdParty/Jasy/jasy/script/parse/Node.py	Sat Feb 02 11:12:54 2019 +0100
@@ -0,0 +1,26 @@
+#
+# Jasy - Web Tooling Framework
+# Copyright 2013-2014 Sebastian Werner
+#
+
+from __future__ import unicode_literals
+
+import jasy.parse.AbstractNode as AbstractNode
+
+class Node(AbstractNode.AbstractNode):
+
+    __slots__ = [
+        # core data
+        "line", "type", "tokenizer", "start", "end", "rel", "parent",
+
+        # dynamic added data by other modules
+        "comments", "scope",
+
+        # node type specific
+        "value", "expression", "body", "functionForm", "parenthesized", "fileId", "params",
+        "name", "readOnly", "initializer", "condition", "isLoop", "isEach", "object", "assignOp",
+        "iterator", "thenPart", "exception", "elsePart", "setup", "postfix", "update", "tryBlock",
+        "block", "defaultIndex", "discriminant", "label", "statements", "finallyBlock",
+        "statement", "variables", "names", "guard", "for", "tail", "expressionClosure"
+    ]
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ThirdParty/Jasy/jasy/script/parse/Parser.py	Sat Feb 02 11:12:54 2019 +0100
@@ -0,0 +1,1448 @@
+#
+# Jasy - Web Tooling Framework
+# Copyright 2010-2012 Zynga Inc.
+# Copyright 2013-2014 Sebastian Werner
+#
+
+#
+# License: MPL 1.1/GPL 2.0/LGPL 2.1
+# Authors:
+#   - Brendan Eich <brendan@mozilla.org> (Original JavaScript) (2004-2010)
+#   - Sebastian Werner <info@sebastian-werner.net> (Python Port) (2010-2012)
+#
+
+from __future__ import unicode_literals
+
+import jasy.script.tokenize.Tokenizer
+import jasy.script.parse.VanillaBuilder
+import jasy.script.tokenize.Lang
+
+__all__ = [ "parse", "parseExpression" ]
+
+def parseExpression(source, fileId=None, line=1, builder=None):
+    if builder == None:
+        builder = jasy.script.parse.VanillaBuilder.VanillaBuilder()
+
+    # Convert source into expression statement to be friendly to the Tokenizer
+    if not source.endswith(";"):
+        source = source + ";"
+
+    tokenizer = jasy.script.tokenize.Tokenizer.Tokenizer(source, fileId, line)
+    staticContext = StaticContext(False, builder)
+
+    return Expression(tokenizer, staticContext)
+
+
+
+def parse(source, fileId=None, line=1, builder=None):
+    if builder == None:
+        builder = jasy.script.parse.VanillaBuilder.VanillaBuilder()
+
+    tokenizer = jasy.script.tokenize.Tokenizer.Tokenizer(source, fileId, line)
+    staticContext = StaticContext(False, builder)
+    node = Script(tokenizer, staticContext)
+
+    # store fileId on top-level node
+    node.fileId = tokenizer.fileId
+
+    # add missing comments e.g. empty file with only a comment etc.
+    # if there is something non-attached by an inner node it is attached to
+    # the top level node, which is not correct, but might be better than
+    # just ignoring the comment after all.
+    if len(node) > 0:
+        builder.COMMENTS_add(node[-1], None, tokenizer.getComments())
+    else:
+        builder.COMMENTS_add(node, None, tokenizer.getComments())
+
+    if not tokenizer.done():
+        raise SyntaxError("Unexpected end of file", tokenizer)
+
+    return node
+
+
+
+class SyntaxError(Exception):
+    def __init__(self, message, tokenizer):
+        Exception.__init__(self, "Syntax error: %s\n%s:%s" % (message, tokenizer.fileId, tokenizer.line))
+
+
+# Used as a status container during tree-building for every def body and the global body
+class StaticContext(object):
+    # inFunction is used to check if a return stm appears in a valid context.
+    def __init__(self, inFunction, builder):
+        # Whether this is inside a function, mostly True, only for top-level scope it's False
+        self.inFunction = inFunction
+
+        self.hasEmptyReturn = False
+        self.hasReturnWithValue = False
+        self.isGenerator = False
+        self.blockId = 0
+        self.builder = builder
+        self.statementStack = []
+
+        # Sets to store variable uses
+        # self.functions = set()
+        # self.variables = set()
+
+        # Status
+        # self.needsHoisting = False
+        self.bracketLevel = 0
+        self.curlyLevel = 0
+        self.parenLevel = 0
+        self.hookLevel = 0
+
+        # Configure strict ecmascript 3 mode
+        self.ecma3OnlyMode = False
+
+        # Status flag during parsing
+        self.inForLoopInit = False
+
+
+def Script(tokenizer, staticContext):
+    """Parses the toplevel and def bodies."""
+    node = Statements(tokenizer, staticContext)
+
+    # change type from "block" to "script" for script root
+    node.type = "script"
+
+    # copy over data from compiler context
+    # node.functions = staticContext.functions
+    # node.variables = staticContext.variables
+
+    return node
+
+
+def nest(tokenizer, staticContext, node, func, end=None):
+    """Statement stack and nested statement handler."""
+    staticContext.statementStack.append(node)
+    node = func(tokenizer, staticContext)
+    staticContext.statementStack.pop()
+    end and tokenizer.mustMatch(end)
+
+    return node
+
+
+def Statements(tokenizer, staticContext):
+    """Parses a list of Statements."""
+
+    builder = staticContext.builder
+    node = builder.BLOCK_build(tokenizer, staticContext.blockId)
+    staticContext.blockId += 1
+
+    builder.BLOCK_hoistLets(node)
+    staticContext.statementStack.append(node)
+
+    prevNode = None
+    while not tokenizer.done() and tokenizer.peek(True) != "right_curly":
+        comments = tokenizer.getComments()
+        childNode = Statement(tokenizer, staticContext)
+        builder.COMMENTS_add(childNode, prevNode, comments)
+        builder.BLOCK_addStatement(node, childNode)
+        prevNode = childNode
+
+    staticContext.statementStack.pop()
+    builder.BLOCK_finish(node)
+
+    # if getattr(node, "needsHoisting", False):
+    #     # TODO
+    #     raise Exception("Needs hoisting went true!!!")
+    #     builder.setHoists(node.id, node.variables)
+    #     # Propagate up to the function.
+    #     staticContext.needsHoisting = True
+
+    return node
+
+
+def Block(tokenizer, staticContext):
+    tokenizer.mustMatch("left_curly")
+    node = Statements(tokenizer, staticContext)
+    tokenizer.mustMatch("right_curly")
+
+    return node
+
+
+def Statement(tokenizer, staticContext):
+    """Parses a Statement."""
+
+    tokenType = tokenizer.get(True)
+    builder = staticContext.builder
+
+    # Cases for statements ending in a right curly return early, avoiding the
+    # common semicolon insertion magic after this switch.
+
+    if tokenType == "function":
+        # "declared_form" extends functions of staticContext, "statement_form" doesn'tokenizer.
+        if len(staticContext.statementStack) > 1:
+            kind = "statement_form"
+        else:
+            kind = "declared_form"
+
+        return FunctionDefinition(tokenizer, staticContext, True, kind)
+
+
+    elif tokenType == "left_curly":
+        node = Statements(tokenizer, staticContext)
+        tokenizer.mustMatch("right_curly")
+
+        return node
+
+
+    elif tokenType == "if":
+        node = builder.IF_build(tokenizer)
+        builder.IF_setCondition(node, ParenExpression(tokenizer, staticContext))
+        staticContext.statementStack.append(node)
+        builder.IF_setThenPart(node, Statement(tokenizer, staticContext))
+
+        if tokenizer.match("else"):
+            comments = tokenizer.getComments()
+            elsePart = Statement(tokenizer, staticContext)
+            builder.COMMENTS_add(elsePart, node, comments)
+            builder.IF_setElsePart(node, elsePart)
+
+        staticContext.statementStack.pop()
+        builder.IF_finish(node)
+
+        return node
+
+
+    elif tokenType == "switch":
+        # This allows CASEs after a "default", which is in the standard.
+        node = builder.SWITCH_build(tokenizer)
+        builder.SWITCH_setDiscriminant(node, ParenExpression(tokenizer, staticContext))
+        staticContext.statementStack.append(node)
+
+        tokenizer.mustMatch("left_curly")
+        tokenType = tokenizer.get()
+
+        while tokenType != "right_curly":
+            if tokenType == "default":
+                if node.defaultIndex >= 0:
+                    raise SyntaxError("More than one switch default", tokenizer)
+
+                childNode = builder.DEFAULT_build(tokenizer)
+                builder.SWITCH_setDefaultIndex(node, len(node)-1)
+                tokenizer.mustMatch("colon")
+                builder.DEFAULT_initializeStatements(childNode, tokenizer)
+
+                while True:
+                    tokenType=tokenizer.peek(True)
+                    if tokenType == "case" or tokenType == "default" or tokenType == "right_curly":
+                        break
+                    builder.DEFAULT_addStatement(childNode, Statement(tokenizer, staticContext))
+
+                builder.DEFAULT_finish(childNode)
+
+            elif tokenType == "case":
+                childNode = builder.CASE_build(tokenizer)
+                builder.CASE_setLabel(childNode, Expression(tokenizer, staticContext))
+                tokenizer.mustMatch("colon")
+                builder.CASE_initializeStatements(childNode, tokenizer)
+
+                while True:
+                    tokenType=tokenizer.peek(True)
+                    if tokenType == "case" or tokenType == "default" or tokenType == "right_curly":
+                        break
+                    builder.CASE_addStatement(childNode, Statement(tokenizer, staticContext))
+
+                builder.CASE_finish(childNode)
+
+            else:
+                raise SyntaxError("Invalid switch case", tokenizer)
+
+            builder.SWITCH_addCase(node, childNode)
+            tokenType = tokenizer.get()
+
+        staticContext.statementStack.pop()
+        builder.SWITCH_finish(node)
+
+        return node
+
+
+    elif tokenType == "for":
+        node = builder.FOR_build(tokenizer)
+        forBlock = None
+
+        if tokenizer.match("identifier") and tokenizer.token.value == "each":
+            builder.FOR_rebuildForEach(node)
+
+        tokenizer.mustMatch("left_paren")
+        tokenType = tokenizer.peek()
+        childNode = None
+
+        if tokenType != "semicolon":
+            staticContext.inForLoopInit = True
+
+            if tokenType == "var" or tokenType == "const":
+                tokenizer.get()
+                childNode = Variables(tokenizer, staticContext)
+
+            elif tokenType == "let":
+                tokenizer.get()
+
+                if tokenizer.peek() == "left_paren":
+                    childNode = LetBlock(tokenizer, staticContext, False)
+
+                else:
+                    # Let in for head, we need to add an implicit block
+                    # around the rest of the for.
+                    forBlock = builder.BLOCK_build(tokenizer, staticContext.blockId)
+                    staticContext.blockId += 1
+                    staticContext.statementStack.append(forBlock)
+                    childNode = Variables(tokenizer, staticContext, forBlock)
+
+            else:
+                childNode = Expression(tokenizer, staticContext)
+
+            staticContext.inForLoopInit = False
+
+        if childNode and tokenizer.match("in"):
+            builder.FOR_rebuildForIn(node)
+            builder.FOR_setObject(node, Expression(tokenizer, staticContext), forBlock)
+
+            if childNode.type == "var" or childNode.type == "let":
+                if len(childNode) != 1:
+                    raise SyntaxError("Invalid for..in left-hand side", tokenizer)
+
+                builder.FOR_setIterator(node, childNode, forBlock)
+
+            else:
+                builder.FOR_setIterator(node, childNode, forBlock)
+
+        else:
+            builder.FOR_setSetup(node, childNode)
+            tokenizer.mustMatch("semicolon")
+
+            if node.isEach:
+                raise SyntaxError("Invalid for each..in loop", tokenizer)
+
+            if tokenizer.peek() == "semicolon":
+                builder.FOR_setCondition(node, None)
+            else:
+                builder.FOR_setCondition(node, Expression(tokenizer, staticContext))
+
+            tokenizer.mustMatch("semicolon")
+
+            if tokenizer.peek() == "right_paren":
+                builder.FOR_setUpdate(node, None)
+            else:
+                builder.FOR_setUpdate(node, Expression(tokenizer, staticContext))
+
+        tokenizer.mustMatch("right_paren")
+        builder.FOR_setBody(node, nest(tokenizer, staticContext, node, Statement))
+
+        if forBlock:
+            builder.BLOCK_finish(forBlock)
+            staticContext.statementStack.pop()
+
+        builder.FOR_finish(node)
+        return node
+
+
+    elif tokenType == "while":
+        node = builder.WHILE_build(tokenizer)
+
+        builder.WHILE_setCondition(node, ParenExpression(tokenizer, staticContext))
+        builder.WHILE_setBody(node, nest(tokenizer, staticContext, node, Statement))
+        builder.WHILE_finish(node)
+
+        return node
+
+
+    elif tokenType == "do":
+        node = builder.DO_build(tokenizer)
+
+        builder.DO_setBody(node, nest(tokenizer, staticContext, node, Statement, "while"))
+        builder.DO_setCondition(node, ParenExpression(tokenizer, staticContext))
+        builder.DO_finish(node)
+
+        if not staticContext.ecma3OnlyMode:
+            # <script language="JavaScript"> (without version hints) may need
+            # automatic semicolon insertion without a newline after do-while.
+            # See http://bugzilla.mozilla.org/show_bug.cgi?id=238945.
+            tokenizer.match("semicolon")
+            return node
+
+        # NO RETURN
+
+
+    elif tokenType == "break" or tokenType == "continue":
+        if tokenType == "break":
+            node = builder.BREAK_build(tokenizer)
+        else:
+            node = builder.CONTINUE_build(tokenizer)
+
+        if tokenizer.peekOnSameLine() == "identifier":
+            tokenizer.get()
+
+            if tokenType == "break":
+                builder.BREAK_setLabel(node, tokenizer.token.value)
+            else:
+                builder.CONTINUE_setLabel(node, tokenizer.token.value)
+
+        statementStack = staticContext.statementStack
+        i = len(statementStack)
+        label = node.label if hasattr(node, "label") else None
+
+        if label:
+            while True:
+                i -= 1
+                if i < 0:
+                    raise SyntaxError("Label not found", tokenizer)
+                if getattr(statementStack[i], "label", None) == label:
+                    break
+
+            #
+            # Both break and continue to label need to be handled specially
+            # within a labeled loop, so that they target that loop. If not in
+            # a loop, then break targets its labeled statement. Labels can be
+            # nested so we skip all labels immediately enclosing the nearest
+            # non-label statement.
+            #
+            while i < len(statementStack) - 1 and statementStack[i+1].type == "label":
+                i += 1
+
+            if i < len(statementStack) - 1 and getattr(statementStack[i+1], "isLoop", False):
+                i += 1
+            elif tokenType == "continue":
+                raise SyntaxError("Invalid continue", tokenizer)
+
+        else:
+            while True:
+                i -= 1
+                if i < 0:
+                    if tokenType == "break":
+                        raise SyntaxError("Invalid break", tokenizer)
+                    else:
+                        raise SyntaxError("Invalid continue", tokenizer)
+
+                if getattr(statementStack[i], "isLoop", False) or (tokenType == "break" and statementStack[i].type == "switch"):
+                    break
+
+        if tokenType == "break":
+            builder.BREAK_finish(node)
+        else:
+            builder.CONTINUE_finish(node)
+
+        # NO RETURN
+
+
+    elif tokenType == "try":
+        node = builder.TRY_build(tokenizer)
+        builder.TRY_setTryBlock(node, Block(tokenizer, staticContext))
+
+        while tokenizer.match("catch"):
+            childNode = builder.CATCH_build(tokenizer)
+            tokenizer.mustMatch("left_paren")
+            nextTokenType = tokenizer.get()
+
+            if nextTokenType == "left_bracket" or nextTokenType == "left_curly":
+                # Destructured catch identifiers.
+                tokenizer.unget()
+                exception = DestructuringExpression(tokenizer, staticContext, True)
+
+            elif nextTokenType == "identifier":
+                exception = builder.CATCH_wrapException(tokenizer)
+
+            else:
+                raise SyntaxError("Missing identifier in catch", tokenizer)
+
+            builder.CATCH_setException(childNode, exception)
+
+            if tokenizer.match("if"):
+                if staticContext.ecma3OnlyMode:
+                    raise SyntaxError("Illegal catch guard", tokenizer)
+
+                if node.getChildrenLength() > 0 and not node.getUnrelatedChildren()[0].guard:
+                    raise SyntaxError("Guarded catch after unguarded", tokenizer)
+
+                builder.CATCH_setGuard(childNode, Expression(tokenizer, staticContext))
+
+            else:
+                builder.CATCH_setGuard(childNode, None)
+
+            tokenizer.mustMatch("right_paren")
+
+            builder.CATCH_setBlock(childNode, Block(tokenizer, staticContext))
+            builder.CATCH_finish(childNode)
+
+            builder.TRY_addCatch(node, childNode)
+
+        builder.TRY_finishCatches(node)
+
+        if tokenizer.match("finally"):
+            builder.TRY_setFinallyBlock(node, Block(tokenizer, staticContext))
+
+        if node.getChildrenLength() == 0 and not hasattr(node, "finallyBlock"):
+            raise SyntaxError("Invalid try statement", tokenizer)
+
+        builder.TRY_finish(node)
+        return node
+
+
+    elif tokenType == "catch" or tokenType == "finally":
+        raise SyntaxError(tokens[tokenType] + " without preceding try", tokenizer)
+
+
+    elif tokenType == "throw":
+        node = builder.THROW_build(tokenizer)
+
+        builder.THROW_setException(node, Expression(tokenizer, staticContext))
+        builder.THROW_finish(node)
+
+        # NO RETURN
+
+
+    elif tokenType == "return":
+        node = returnOrYield(tokenizer, staticContext)
+
+        # NO RETURN
+
+
+    elif tokenType == "with":
+        node = builder.WITH_build(tokenizer)
+
+        builder.WITH_setObject(node, ParenExpression(tokenizer, staticContext))
+        builder.WITH_setBody(node, nest(tokenizer, staticContext, node, Statement))
+        builder.WITH_finish(node)
+
+        return node
+
+
+    elif tokenType == "var" or tokenType == "const":
+        node = Variables(tokenizer, staticContext)
+
+        # NO RETURN
+
+
+    elif tokenType == "let":
+        if tokenizer.peek() == "left_paren":
+            node = LetBlock(tokenizer, staticContext, True)
+        else:
+            node = Variables(tokenizer, staticContext)
+
+        # NO RETURN
+
+
+    elif tokenType == "debugger":
+        node = builder.DEBUGGER_build(tokenizer)
+
+        # NO RETURN
+
+
+    elif tokenType == "newline" or tokenType == "semicolon":
+        node = builder.SEMICOLON_build(tokenizer)
+
+        builder.SEMICOLON_setExpression(node, None)
+        builder.SEMICOLON_finish(tokenizer)
+
+        return node
+
+
+    else:
+        if tokenType == "identifier":
+            tokenType = tokenizer.peek()
+
+            # Labeled statement.
+            if tokenType == "colon":
+                label = tokenizer.token.value
+                statementStack = staticContext.statementStack
+
+                i = len(statementStack)-1
+                while i >= 0:
+                    if getattr(statementStack[i], "label", None) == label:
+                        raise SyntaxError("Duplicate label", tokenizer)
+
+                    i -= 1
+
+                tokenizer.get()
+                node = builder.LABEL_build(tokenizer)
+
+                builder.LABEL_setLabel(node, label)
+                builder.LABEL_setStatement(node, nest(tokenizer, staticContext, node, Statement))
+                builder.LABEL_finish(node)
+
+                return node
+
+        # Expression statement.
+        # We unget the current token to parse the expression as a whole.
+        node = builder.SEMICOLON_build(tokenizer)
+        tokenizer.unget()
+        builder.SEMICOLON_setExpression(node, Expression(tokenizer, staticContext))
+        node.end = node.expression.end
+        builder.SEMICOLON_finish(node)
+
+        # NO RETURN
+
+
+    MagicalSemicolon(tokenizer)
+    return node
+
+
+
+def MagicalSemicolon(tokenizer):
+    if tokenizer.line == tokenizer.token.line:
+        tokenType = tokenizer.peekOnSameLine()
+
+        if tokenType != "end" and tokenType != "newline" and tokenType != "semicolon" and tokenType != "right_curly":
+            raise SyntaxError("Missing ; before statement", tokenizer)
+
+    tokenizer.match("semicolon")
+
+
+
+def returnOrYield(tokenizer, staticContext):
+    builder = staticContext.builder
+    tokenType = tokenizer.token.type
+
+    if tokenType == "return":
+        if not staticContext.inFunction:
+            raise SyntaxError("Return not in function", tokenizer)
+
+        node = builder.RETURN_build(tokenizer)
+
+    else:
+        if not staticContext.inFunction:
+            raise SyntaxError("Yield not in function", tokenizer)
+
+        staticContext.isGenerator = True
+        node = builder.YIELD_build(tokenizer)
+
+    nextTokenType = tokenizer.peek(True)
+    if nextTokenType != "end" and nextTokenType != "newline" and nextTokenType != "semicolon" and nextTokenType != "right_curly" and (tokenType != "yield" or (nextTokenType != tokenType and nextTokenType != "right_bracket" and nextTokenType != "right_paren" and nextTokenType != "colon" and nextTokenType != "comma")):
+        if tokenType == "return":
+            builder.RETURN_setValue(node, Expression(tokenizer, staticContext))
+            staticContext.hasReturnWithValue = True
+        else:
+            builder.YIELD_setValue(node, AssignExpression(tokenizer, staticContext))
+
+    elif tokenType == "return":
+        staticContext.hasEmptyReturn = True
+
+    # Disallow return v; in generator.
+    if staticContext.hasReturnWithValue and staticContext.isGenerator:
+        raise SyntaxError("Generator returns a value", tokenizer)
+
+    if tokenType == "return":
+        builder.RETURN_finish(node)
+    else:
+        builder.YIELD_finish(node)
+
+    return node
+
+
+
+def FunctionDefinition(tokenizer, staticContext, requireName, functionForm):
+    builder = staticContext.builder
+    functionNode = builder.FUNCTION_build(tokenizer)
+
+    if tokenizer.match("identifier"):
+        builder.FUNCTION_setName(functionNode, tokenizer.token.value)
+    elif requireName:
+        raise SyntaxError("Missing def identifier", tokenizer)
+
+    tokenizer.mustMatch("left_paren")
+
+    if not tokenizer.match("right_paren"):
+        builder.FUNCTION_initParams(functionNode, tokenizer)
+        prevParamNode = None
+        while True:
+            tokenType = tokenizer.get()
+            if tokenType == "left_bracket" or tokenType == "left_curly":
+                # Destructured formal parameters.
+                tokenizer.unget()
+                paramNode = DestructuringExpression(tokenizer, staticContext)
+
+            elif tokenType == "identifier":
+                paramNode = builder.FUNCTION_wrapParam(tokenizer)
+
+            else:
+                raise SyntaxError("Missing formal parameter", tokenizer)
+
+            builder.FUNCTION_addParam(functionNode, tokenizer, paramNode)
+            builder.COMMENTS_add(paramNode, prevParamNode, tokenizer.getComments())
+
+            if not tokenizer.match("comma"):
+                break
+
+            prevParamNode = paramNode
+
+        tokenizer.mustMatch("right_paren")
+
+    # Do we have an expression closure or a normal body?
+    tokenType = tokenizer.get()
+    if tokenType != "left_curly":
+        builder.FUNCTION_setExpressionClosure(functionNode, True)
+        tokenizer.unget()
+
+    childContext = StaticContext(True, builder)
+
+    if staticContext.inFunction:
+        # Inner functions don't reset block numbering, only functions at
+        # the top level of the program do.
+        childContext.blockId = staticContext.blockId
+
+    if tokenType != "left_curly":
+        builder.FUNCTION_setBody(functionNode, AssignExpression(tokenizer, staticContext))
+        if staticContext.isGenerator:
+            raise SyntaxError("Generator returns a value", tokenizer)
+
+    else:
+        builder.FUNCTION_hoistVars(childContext.blockId)
+        builder.FUNCTION_setBody(functionNode, Script(tokenizer, childContext))
+
+    if tokenType == "left_curly":
+        tokenizer.mustMatch("right_curly")
+
+    functionNode.end = tokenizer.token.end
+    functionNode.functionForm = functionForm
+
+    builder.COMMENTS_add(functionNode.body, functionNode.body, tokenizer.getComments())
+    builder.FUNCTION_finish(functionNode, staticContext)
+
+    return functionNode
+
+
+
+def Variables(tokenizer, staticContext, letBlock=None):
+    """Parses a comma-separated list of var declarations (and maybe initializations)."""
+
+    builder = staticContext.builder
+    if tokenizer.token.type == "var":
+        build = builder.VAR_build
+        addDecl = builder.VAR_addDecl
+        finish = builder.VAR_finish
+        childContext = staticContext
+
+    elif tokenizer.token.type == "const":
+        build = builder.CONST_build
+        addDecl = builder.CONST_addDecl
+        finish = builder.CONST_finish
+        childContext = staticContext
+
+    elif tokenizer.token.type == "let" or tokenizer.token.type == "left_paren":
+        build = builder.LET_build
+        addDecl = builder.LET_addDecl
+        finish = builder.LET_finish
+
+        if not letBlock:
+            statementStack = staticContext.statementStack
+            i = len(statementStack) - 1
+
+            # a BLOCK *must* be found.
+            while statementStack[i].type != "block":
+                i -= 1
+
+            # Lets at the def toplevel are just vars, at least in SpiderMonkey.
+            if i == 0:
+                build = builder.VAR_build
+                addDecl = builder.VAR_addDecl
+                finish = builder.VAR_finish
+                childContext = staticContext
+
+            else:
+                childContext = statementStack[i]
+
+        else:
+            childContext = letBlock
+
+    node = build(tokenizer)
+
+    while True:
+        tokenType = tokenizer.get()
+
+        # Done in Python port!
+        # FIXME Should have a special DECLARATION node instead of overloading
+        # IDENTIFIER to mean both identifier declarations and destructured
+        # declarations.
+        childNode = builder.DECL_build(tokenizer)
+
+        if tokenType == "left_bracket" or tokenType == "left_curly":
+            # Pass in childContext if we need to add each pattern matched into
+            # its variables, else pass in staticContext.
+            # Need to unget to parse the full destructured expression.
+            tokenizer.unget()
+            builder.DECL_setNames(childNode, DestructuringExpression(tokenizer, staticContext, True, childContext))
+
+            if staticContext.inForLoopInit and tokenizer.peek() == "in":
+                addDecl(node, childNode, childContext)
+                if tokenizer.match("comma"):
+                    continue
+                else:
+                    break
+
+            tokenizer.mustMatch("assign")
+            if tokenizer.token.assignOp:
+                raise SyntaxError("Invalid variable initialization", tokenizer)
+
+            # Parse the init as a normal assignment.
+            builder.DECL_setInitializer(childNode, AssignExpression(tokenizer, staticContext))
+            builder.DECL_finish(childNode)
+            addDecl(node, childNode, childContext)
+
+            # Copy over names for variable list
+            # for nameNode in childNode.names:
+            #    childContext.variables.add(nameNode.value)
+
+            if tokenizer.match("comma"):
+                continue
+            else:
+                break
+
+        if tokenType != "identifier":
+            raise SyntaxError("Missing variable name", tokenizer)
+
+        builder.DECL_setName(childNode, tokenizer.token.value)
+        builder.DECL_setReadOnly(childNode, node.type == "const")
+        addDecl(node, childNode, childContext)
+
+        if tokenizer.match("assign"):
+            if tokenizer.token.assignOp:
+                raise SyntaxError("Invalid variable initialization", tokenizer)
+
+            initializerNode = AssignExpression(tokenizer, staticContext)
+            builder.DECL_setInitializer(childNode, initializerNode)
+
+        builder.DECL_finish(childNode)
+
+        # If we directly use the node in "let" constructs
+        # if not hasattr(childContext, "variables"):
+        #    childContext.variables = set()
+
+        # childContext.variables.add(childNode.name)
+
+        if not tokenizer.match("comma"):
+            break
+
+    finish(node)
+    return node
+
+
+
+def LetBlock(tokenizer, staticContext, isStatement):
+    """Does not handle let inside of for loop init."""
+    builder = staticContext.builder
+
+    # tokenizer.token.type must be "let"
+    node = builder.LETBLOCK_build(tokenizer)
+    tokenizer.mustMatch("left_paren")
+    builder.LETBLOCK_setVariables(node, Variables(tokenizer, staticContext, node))
+    tokenizer.mustMatch("right_paren")
+
+    if isStatement and tokenizer.peek() != "left_curly":
+        # If this is really an expression in let statement guise, then we
+        # need to wrap the "let_block" node in a "semicolon" node so that we pop
+        # the return value of the expression.
+        childNode = builder.SEMICOLON_build(tokenizer)
+        builder.SEMICOLON_setExpression(childNode, node)
+        builder.SEMICOLON_finish(childNode)
+        isStatement = False
+
+    if isStatement:
+        childNode = Block(tokenizer, staticContext)
+        builder.LETBLOCK_setBlock(node, childNode)
+
+    else:
+        childNode = AssignExpression(tokenizer, staticContext)
+        builder.LETBLOCK_setExpression(node, childNode)
+
+    builder.LETBLOCK_finish(node)
+    return node
+
+
+def checkDestructuring(tokenizer, staticContext, node, simpleNamesOnly=None, data=None):
+    if node.type == "array_comp":
+        raise SyntaxError("Invalid array comprehension left-hand side", tokenizer)
+
+    if node.type != "array_init" and node.type != "object_init":
+        return
+
+    builder = staticContext.builder
+
+    for child in node:
+        if child == None:
+            continue
+
+        if child.type == "property_init":
+            lhs = child[0]
+            rhs = child[1]
+        else:
+            lhs = None
+            rhs = None
+
+
+        if rhs and (rhs.type == "array_init" or rhs.type == "object_init"):
+            checkDestructuring(tokenizer, staticContext, rhs, simpleNamesOnly, data)
+
+        if lhs and simpleNamesOnly:
+            # In declarations, lhs must be simple names
+            if lhs.type != "identifier":
+                raise SyntaxError("Missing name in pattern", tokenizer)
+
+            elif data:
+                childNode = builder.DECL_build(tokenizer)
+                builder.DECL_setName(childNode, lhs.value)
+
+                # Don't need to set initializer because it's just for
+                # hoisting anyways.
+                builder.DECL_finish(childNode)
+
+                # Each pattern needs to be added to variables.
+                # data.variables.add(childNode.name)
+
+
+# JavaScript 1.7
+def DestructuringExpression(tokenizer, staticContext, simpleNamesOnly=None, data=None):
+    node = PrimaryExpression(tokenizer, staticContext)
+    checkDestructuring(tokenizer, staticContext, node, simpleNamesOnly, data)
+
+    return node
+
+
+# JavsScript 1.7
+def GeneratorExpression(tokenizer, staticContext, expression):
+    builder = staticContext.builder
+    node = builder.GENERATOR_build(tokenizer)
+
+    builder.GENERATOR_setExpression(node, expression)
+    builder.GENERATOR_setTail(node, comprehensionTail(tokenizer, staticContext))
+    builder.GENERATOR_finish(node)
+
+    return node
+
+
+# JavaScript 1.7 Comprehensions Tails (Generators / Arrays)
+def comprehensionTail(tokenizer, staticContext):
+    builder = staticContext.builder
+
+    # tokenizer.token.type must be "for"
+    body = builder.COMPTAIL_build(tokenizer)
+
+    while True:
+        node = builder.FOR_build(tokenizer)
+
+        # Comprehension tails are always for..in loops.
+        builder.FOR_rebuildForIn(node)
+        if tokenizer.match("identifier"):
+            # But sometimes they're for each..in.
+            if tokenizer.token.value == "each":
+                builder.FOR_rebuildForEach(node)
+            else:
+                tokenizer.unget()
+
+        tokenizer.mustMatch("left_paren")
+
+        tokenType = tokenizer.get()
+        if tokenType == "left_bracket" or tokenType == "left_curly":
+            tokenizer.unget()
+            # Destructured left side of for in comprehension tails.
+            builder.FOR_setIterator(node, DestructuringExpression(tokenizer, staticContext))
+
+        elif tokenType == "identifier":
+            # Removed variable/declaration substructure in Python port.
+            # Variable declarations are not allowed here. So why process them in such a way?
+
+            # declaration = builder.DECL_build(tokenizer)
+            # builder.DECL_setName(declaration, tokenizer.token.value)
+            # builder.DECL_finish(declaration)
+            # childNode = builder.VAR_build(tokenizer)
+            # builder.VAR_addDecl(childNode, declaration)
+            # builder.VAR_finish(childNode)
+            # builder.FOR_setIterator(node, declaration)
+
+            # Don't add to variables since the semantics of comprehensions is
+            # such that the variables are in their own def when desugared.
+
+            identifier = builder.PRIMARY_build(tokenizer, "identifier")
+            builder.FOR_setIterator(node, identifier)
+
+        else:
+            raise SyntaxError("Missing identifier", tokenizer)
+
+        tokenizer.mustMatch("in")
+        builder.FOR_setObject(node, Expression(tokenizer, staticContext))
+        tokenizer.mustMatch("right_paren")
+        builder.COMPTAIL_addFor(body, node)
+
+        if not tokenizer.match("for"):
+            break
+
+    # Optional guard.
+    if tokenizer.match("if"):
+        builder.COMPTAIL_setGuard(body, ParenExpression(tokenizer, staticContext))
+
+    builder.COMPTAIL_finish(body)
+
+    return body
+
+
+def ParenExpression(tokenizer, staticContext):
+    tokenizer.mustMatch("left_paren")
+
+    # Always accept the 'in' operator in a parenthesized expression,
+    # where it's unambiguous, even if we might be parsing the init of a
+    # for statement.
+    oldLoopInit = staticContext.inForLoopInit
+    staticContext.inForLoopInit = False
+    node = Expression(tokenizer, staticContext)
+    staticContext.inForLoopInit = oldLoopInit
+
+    err = "expression must be parenthesized"
+    if tokenizer.match("for"):
+        if node.type == "yield" and not node.parenthesized:
+            raise SyntaxError("Yield " + err, tokenizer)
+
+        if node.type == "comma" and not node.parenthesized:
+            raise SyntaxError("Generator " + err, tokenizer)
+
+        node = GeneratorExpression(tokenizer, staticContext, node)
+
+    tokenizer.mustMatch("right_paren")
+
+    return node
+
+
+def Expression(tokenizer, staticContext):
+    """Top-down expression parser matched against SpiderMonkey."""
+    builder = staticContext.builder
+    node = AssignExpression(tokenizer, staticContext)
+
+    if tokenizer.match("comma"):
+        childNode = builder.COMMA_build(tokenizer)
+        builder.COMMA_addOperand(childNode, node)
+        node = childNode
+        while True:
+            childNode = node[len(node)-1]
+            if childNode.type == "yield" and not childNode.parenthesized:
+                raise SyntaxError("Yield expression must be parenthesized", tokenizer)
+            builder.COMMA_addOperand(node, AssignExpression(tokenizer, staticContext))
+
+            if not tokenizer.match("comma"):
+                break
+
+        builder.COMMA_finish(node)
+
+    return node
+
+
+def AssignExpression(tokenizer, staticContext):
+    builder = staticContext.builder
+
+    # Have to treat yield like an operand because it could be the leftmost
+    # operand of the expression.
+    if tokenizer.match("yield", True):
+        return returnOrYield(tokenizer, staticContext)
+
+    comments = tokenizer.getComments()
+    node = builder.ASSIGN_build(tokenizer)
+    lhs = ConditionalExpression(tokenizer, staticContext)
+    builder.COMMENTS_add(lhs, None, comments)
+
+    if not tokenizer.match("assign"):
+        builder.ASSIGN_finish(node)
+        return lhs
+
+    if lhs.type == "object_init" or lhs.type == "array_init":
+        checkDestructuring(tokenizer, staticContext, lhs)
+    elif lhs.type == "identifier" or lhs.type == "dot" or lhs.type == "index" or lhs.type == "call":
+        pass
+    else:
+        raise SyntaxError("Bad left-hand side of assignment", tokenizer)
+
+    builder.ASSIGN_setAssignOp(node, tokenizer.token.assignOp)
+    builder.ASSIGN_addOperand(node, lhs)
+    builder.ASSIGN_addOperand(node, AssignExpression(tokenizer, staticContext))
+    builder.ASSIGN_finish(node)
+
+    return node
+
+
+def ConditionalExpression(tokenizer, staticContext):
+    builder = staticContext.builder
+    node = OrExpression(tokenizer, staticContext)
+
+    if tokenizer.match("hook"):
+        childNode = node
+        node = builder.HOOK_build(tokenizer)
+        builder.HOOK_setCondition(node, childNode)
+
+        # Always accept the 'in' operator in the middle clause of a ternary,
+        # where it's unambiguous, even if we might be parsing the init of a
+        # for statement.
+        oldLoopInit = staticContext.inForLoopInit
+        staticContext.inForLoopInit = False
+        builder.HOOK_setThenPart(node, AssignExpression(tokenizer, staticContext))
+        staticContext.inForLoopInit = oldLoopInit
+
+        if not tokenizer.match("colon"):
+            raise SyntaxError("Missing : after ?", tokenizer)
+
+        builder.HOOK_setElsePart(node, AssignExpression(tokenizer, staticContext))
+        builder.HOOK_finish(node)
+
+    return node
+
+
+def OrExpression(tokenizer, staticContext):
+    builder = staticContext.builder
+    node = AndExpression(tokenizer, staticContext)
+
+    while tokenizer.match("or"):
+        childNode = builder.OR_build(tokenizer)
+        builder.OR_addOperand(childNode, node)
+        builder.OR_addOperand(childNode, AndExpression(tokenizer, staticContext))
+        builder.OR_finish(childNode)
+        node = childNode
+
+    return node
+
+
+def AndExpression(tokenizer, staticContext):
+    builder = staticContext.builder
+    node = BitwiseOrExpression(tokenizer, staticContext)
+
+    while tokenizer.match("and"):
+        childNode = builder.AND_build(tokenizer)
+        builder.AND_addOperand(childNode, node)
+        builder.AND_addOperand(childNode, BitwiseOrExpression(tokenizer, staticContext))
+        builder.AND_finish(childNode)
+        node = childNode
+
+    return node
+
+
+def BitwiseOrExpression(tokenizer, staticContext):
+    builder = staticContext.builder
+    node = BitwiseXorExpression(tokenizer, staticContext)
+
+    while tokenizer.match("bitwise_or"):
+        childNode = builder.BITWISEOR_build(tokenizer)
+        builder.BITWISEOR_addOperand(childNode, node)
+        builder.BITWISEOR_addOperand(childNode, BitwiseXorExpression(tokenizer, staticContext))
+        builder.BITWISEOR_finish(childNode)
+        node = childNode
+
+    return node
+
+
+def BitwiseXorExpression(tokenizer, staticContext):
+    builder = staticContext.builder
+    node = BitwiseAndExpression(tokenizer, staticContext)
+
+    while tokenizer.match("bitwise_xor"):
+        childNode = builder.BITWISEXOR_build(tokenizer)
+        builder.BITWISEXOR_addOperand(childNode, node)
+        builder.BITWISEXOR_addOperand(childNode, BitwiseAndExpression(tokenizer, staticContext))
+        builder.BITWISEXOR_finish(childNode)
+        node = childNode
+
+    return node
+
+
+def BitwiseAndExpression(tokenizer, staticContext):
+    builder = staticContext.builder
+    node = EqualityExpression(tokenizer, staticContext)
+
+    while tokenizer.match("bitwise_and"):
+        childNode = builder.BITWISEAND_build(tokenizer)
+        builder.BITWISEAND_addOperand(childNode, node)
+        builder.BITWISEAND_addOperand(childNode, EqualityExpression(tokenizer, staticContext))
+        builder.BITWISEAND_finish(childNode)
+        node = childNode
+
+    return node
+
+
+def EqualityExpression(tokenizer, staticContext):
+    builder = staticContext.builder
+    node = RelationalExpression(tokenizer, staticContext)
+
+    while tokenizer.match("eq") or tokenizer.match("ne") or tokenizer.match("strict_eq") or tokenizer.match("strict_ne"):
+        childNode = builder.EQUALITY_build(tokenizer)
+        builder.EQUALITY_addOperand(childNode, node)
+        builder.EQUALITY_addOperand(childNode, RelationalExpression(tokenizer, staticContext))
+        builder.EQUALITY_finish(childNode)
+        node = childNode
+
+    return node
+
+
+def RelationalExpression(tokenizer, staticContext):
+    builder = staticContext.builder
+    oldLoopInit = staticContext.inForLoopInit
+
+    # Uses of the in operator in shiftExprs are always unambiguous,
+    # so unset the flag that prohibits recognizing it.
+    staticContext.inForLoopInit = False
+    node = ShiftExpression(tokenizer, staticContext)
+
+    while tokenizer.match("lt") or tokenizer.match("le") or tokenizer.match("ge") or tokenizer.match("gt") or (oldLoopInit == False and tokenizer.match("in")) or tokenizer.match("instanceof"):
+        childNode = builder.RELATIONAL_build(tokenizer)
+        builder.RELATIONAL_addOperand(childNode, node)
+        builder.RELATIONAL_addOperand(childNode, ShiftExpression(tokenizer, staticContext))
+        builder.RELATIONAL_finish(childNode)
+        node = childNode
+
+    staticContext.inForLoopInit = oldLoopInit
+
+    return node
+
+
+def ShiftExpression(tokenizer, staticContext):
+    builder = staticContext.builder
+    node = AddExpression(tokenizer, staticContext)
+
+    while tokenizer.match("lsh") or tokenizer.match("rsh") or tokenizer.match("ursh"):
+        childNode = builder.SHIFT_build(tokenizer)
+        builder.SHIFT_addOperand(childNode, node)
+        builder.SHIFT_addOperand(childNode, AddExpression(tokenizer, staticContext))
+        builder.SHIFT_finish(childNode)
+        node = childNode
+
+    return node
+
+
+def AddExpression(tokenizer, staticContext):
+    builder = staticContext.builder
+    node = MultiplyExpression(tokenizer, staticContext)
+
+    while tokenizer.match("plus") or tokenizer.match("minus"):
+        childNode = builder.ADD_build(tokenizer)
+        builder.ADD_addOperand(childNode, node)
+        builder.ADD_addOperand(childNode, MultiplyExpression(tokenizer, staticContext))
+        builder.ADD_finish(childNode)
+        node = childNode
+
+    return node
+
+
+def MultiplyExpression(tokenizer, staticContext):
+    builder = staticContext.builder
+    node = UnaryExpression(tokenizer, staticContext)
+
+    while tokenizer.match("mul") or tokenizer.match("div") or tokenizer.match("mod"):
+        childNode = builder.MULTIPLY_build(tokenizer)
+        builder.MULTIPLY_addOperand(childNode, node)
+        builder.MULTIPLY_addOperand(childNode, UnaryExpression(tokenizer, staticContext))
+        builder.MULTIPLY_finish(childNode)
+        node = childNode
+
+    return node
+
+
+def UnaryExpression(tokenizer, staticContext):
+    builder = staticContext.builder
+    tokenType = tokenizer.get(True)
+
+    if tokenType in ["delete", "void", "typeof", "not", "bitwise_not", "plus", "minus"]:
+        node = builder.UNARY_build(tokenizer)
+        builder.UNARY_addOperand(node, UnaryExpression(tokenizer, staticContext))
+
+    elif tokenType == "increment" or tokenType == "decrement":
+        # Prefix increment/decrement.
+        node = builder.UNARY_build(tokenizer)
+        builder.UNARY_addOperand(node, MemberExpression(tokenizer, staticContext, True))
+
+    else:
+        tokenizer.unget()
+        node = MemberExpression(tokenizer, staticContext, True)
+
+        # Don't look across a newline boundary for a postfix {in,de}crement.
+        if tokenizer.tokens[(tokenizer.tokenIndex + tokenizer.lookahead - 1) & 3].line == tokenizer.line:
+            if tokenizer.match("increment") or tokenizer.match("decrement"):
+                childNode = builder.UNARY_build(tokenizer)
+                builder.UNARY_setPostfix(childNode)
+                builder.UNARY_finish(node)
+                builder.UNARY_addOperand(childNode, node)
+                node = childNode
+
+    builder.UNARY_finish(node)
+    return node
+
+
+def MemberExpression(tokenizer, staticContext, allowCallSyntax):
+    builder = staticContext.builder
+
+    if tokenizer.match("new"):
+        node = builder.MEMBER_build(tokenizer)
+        builder.MEMBER_addOperand(node, MemberExpression(tokenizer, staticContext, False))
+
+        if tokenizer.match("left_paren"):
+            builder.MEMBER_rebuildNewWithArgs(node)
+            builder.MEMBER_addOperand(node, ArgumentList(tokenizer, staticContext))
+
+        builder.MEMBER_finish(node)
+
+    else:
+        node = PrimaryExpression(tokenizer, staticContext)
+
+    while True:
+        tokenType = tokenizer.get()
+        if tokenType == "end":
+            break
+
+        if tokenType == "dot":
+            childNode = builder.MEMBER_build(tokenizer)
+            builder.MEMBER_addOperand(childNode, node)
+            tokenizer.mustMatch("identifier")
+            builder.MEMBER_addOperand(childNode, builder.MEMBER_build(tokenizer))
+
+        elif tokenType == "left_bracket":
+            childNode = builder.MEMBER_build(tokenizer, "index")
+            builder.MEMBER_addOperand(childNode, node)
+            builder.MEMBER_addOperand(childNode, Expression(tokenizer, staticContext))
+            tokenizer.mustMatch("right_bracket")
+
+        elif tokenType == "left_paren" and allowCallSyntax:
+            childNode = builder.MEMBER_build(tokenizer, "call")
+            builder.MEMBER_addOperand(childNode, node)
+            builder.MEMBER_addOperand(childNode, ArgumentList(tokenizer, staticContext))
+
+        else:
+            tokenizer.unget()
+            return node
+
+        builder.MEMBER_finish(childNode)
+        node = childNode
+
+    return node
+
+
+def ArgumentList(tokenizer, staticContext):
+    builder = staticContext.builder
+    node = builder.LIST_build(tokenizer)
+
+    if tokenizer.match("right_paren", True):
+        return node
+
+    while True:
+        childNode = AssignExpression(tokenizer, staticContext)
+        if childNode.type == "yield" and not childNode.parenthesized and tokenizer.peek() == "comma":
+            raise SyntaxError("Yield expression must be parenthesized", tokenizer)
+
+        if tokenizer.match("for"):
+            childNode = GeneratorExpression(tokenizer, staticContext, childNode)
+            if len(node) > 1 or tokenizer.peek(True) == "comma":
+                raise SyntaxError("Generator expression must be parenthesized", tokenizer)
+
+        builder.LIST_addOperand(node, childNode)
+        if not tokenizer.match("comma"):
+            break
+
+    tokenizer.mustMatch("right_paren")
+    builder.LIST_finish(node)
+
+    return node
+
+
+def PrimaryExpression(tokenizer, staticContext):
+    builder = staticContext.builder
+    tokenType = tokenizer.get(True)
+
+    if tokenType == "function":
+        node = FunctionDefinition(tokenizer, staticContext, False, "expressed_form")
+
+    elif tokenType == "left_bracket":
+        node = builder.ARRAYINIT_build(tokenizer)
+        while True:
+            tokenType = tokenizer.peek(True)
+            if tokenType == "right_bracket":
+                break
+
+            if tokenType == "comma":
+                tokenizer.get()
+                builder.ARRAYINIT_addElement(node, None)
+                continue
+
+            builder.ARRAYINIT_addElement(node, AssignExpression(tokenizer, staticContext))
+
+            if tokenType != "comma" and not tokenizer.match("comma"):
+                break
+
+        # If we matched exactly one element and got a "for", we have an
+        # array comprehension.
+        if len(node) == 1 and tokenizer.match("for"):
+            childNode = builder.ARRAYCOMP_build(tokenizer)
+            builder.ARRAYCOMP_setExpression(childNode, node[0])
+            builder.ARRAYCOMP_setTail(childNode, comprehensionTail(tokenizer, staticContext))
+            node = childNode
+
+        builder.COMMENTS_add(node, node, tokenizer.getComments())
+        tokenizer.mustMatch("right_bracket")
+        builder.PRIMARY_finish(node)
+
+    elif tokenType == "left_curly":
+        node = builder.OBJECTINIT_build(tokenizer)
+
+        if not tokenizer.match("right_curly"):
+            while True:
+                tokenType = tokenizer.get()
+                tokenValue = getattr(tokenizer.token, "value", None)
+                comments = tokenizer.getComments()
+
+                if tokenValue in ("get", "set") and tokenizer.peek() == "identifier":
+                    if staticContext.ecma3OnlyMode:
+                        raise SyntaxError("Illegal property accessor", tokenizer)
+
+                    fd = FunctionDefinition(tokenizer, staticContext, True, "expressed_form")
+                    builder.OBJECTINIT_addProperty(node, fd)
+
+                else:
+                    if tokenType == "identifier" or tokenType == "number" or tokenType == "string":
+                        id = builder.PRIMARY_build(tokenizer, "identifier")
+                        builder.PRIMARY_finish(id)
+
+                    elif tokenType == "right_curly":
+                        if staticContext.ecma3OnlyMode:
+                            raise SyntaxError("Illegal trailing ,", tokenizer)
+
+                        tokenizer.unget()
+                        break
+
+                    else:
+                        if tokenValue in jasy.script.tokenize.Lang.keywords:
+                            id = builder.PRIMARY_build(tokenizer, "identifier")
+                            builder.PRIMARY_finish(id)
+                        else:
+                            print("Value is '%s'" % tokenValue)
+                            raise SyntaxError("Invalid property name", tokenizer)
+
+                    if tokenizer.match("colon"):
+                        childNode = builder.PROPERTYINIT_build(tokenizer)
+                        builder.COMMENTS_add(childNode, node, comments)
+                        builder.PROPERTYINIT_addOperand(childNode, id)
+                        builder.PROPERTYINIT_addOperand(childNode, AssignExpression(tokenizer, staticContext))
+                        builder.PROPERTYINIT_finish(childNode)
+                        builder.OBJECTINIT_addProperty(node, childNode)
+
+                    else:
+                        # Support, e.g., |var {staticContext, y} = o| as destructuring shorthand
+                        # for |var {staticContext: staticContext, y: y} = o|, per proposed JS2/ES4 for JS1.8.
+                        if tokenizer.peek() != "comma" and tokenizer.peek() != "right_curly":
+                            raise SyntaxError("Missing : after property", tokenizer)
+                        builder.OBJECTINIT_addProperty(node, id)
+
+                if not tokenizer.match("comma"):
+                    break
+
+            builder.COMMENTS_add(node, node, tokenizer.getComments())
+            tokenizer.mustMatch("right_curly")
+
+        builder.OBJECTINIT_finish(node)
+
+    elif tokenType == "left_paren":
+        # ParenExpression does its own matching on parentheses, so we need to unget.
+        tokenizer.unget()
+        node = ParenExpression(tokenizer, staticContext)
+        node.parenthesized = True
+
+    elif tokenType == "let":
+        node = LetBlock(tokenizer, staticContext, False)
+
+    elif tokenType in ["null", "this", "true", "false", "identifier", "number", "string", "regexp"]:
+        node = builder.PRIMARY_build(tokenizer, tokenType)
+        builder.PRIMARY_finish(node)
+
+    else:
+        raise SyntaxError("Missing operand. Found type: %s" % tokenType, tokenizer)
+
+    return node
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ThirdParty/Jasy/jasy/script/parse/VanillaBuilder.py	Sat Feb 02 11:12:54 2019 +0100
@@ -0,0 +1,679 @@
+#
+# Jasy - Web Tooling Framework
+# Copyright 2010-2012 Zynga Inc.
+# Copyright 2013-2014 Sebastian Werner
+#
+
+#
+# License: MPL 1.1/GPL 2.0/LGPL 2.1
+# Authors:
+#   - Brendan Eich <brendan@mozilla.org> (Original JavaScript) (2004-2010)
+#   - Sebastian Werner <info@sebastian-werner.net> (Python Port) (2010)
+#
+
+from __future__ import unicode_literals
+
+import jasy.script.parse.Node
+
+class VanillaBuilder:
+    """The vanilla AST builder."""
+
+    def COMMENTS_add(self, currNode, prevNode, comments):
+        if not comments:
+            return
+
+        currComments = []
+        prevComments = []
+        for comment in comments:
+            # post comments - for previous node
+            if comment.context == "inline":
+                prevComments.append(comment)
+
+            # all other comment styles are attached to the current one
+            else:
+                currComments.append(comment)
+
+        # Merge with previously added ones
+        if hasattr(currNode, "comments"):
+            currNode.comments.extend(currComments)
+        else:
+            currNode.comments = currComments
+
+        if prevNode:
+            if hasattr(prevNode, "comments"):
+                prevNode.comments.extend(prevComments)
+            else:
+                prevNode.comments = prevComments
+        else:
+            # Don't loose the comment in tree (if not previous node is there, attach it to this node)
+            currNode.comments.extend(prevComments)
+
+    def IF_build(self, tokenizer):
+        return jasy.script.parse.Node.Node(tokenizer, "if")
+
+    def IF_setCondition(self, node, expression):
+        node.append(expression, "condition")
+
+    def IF_setThenPart(self, node, statement):
+        node.append(statement, "thenPart")
+
+    def IF_setElsePart(self, node, statement):
+        node.append(statement, "elsePart")
+
+    def IF_finish(self, node):
+        pass
+
+    def SWITCH_build(self, tokenizer):
+        node = jasy.script.parse.Node.Node(tokenizer, "switch")
+        node.defaultIndex = -1
+        return node
+
+    def SWITCH_setDiscriminant(self, node, expression):
+        node.append(expression, "discriminant")
+
+    def SWITCH_setDefaultIndex(self, node, index):
+        node.defaultIndex = index
+
+    def SWITCH_addCase(self, node, childNode):
+        node.append(childNode)
+
+    def SWITCH_finish(self, node):
+        pass
+
+    def CASE_build(self, tokenizer):
+        return jasy.script.parse.Node.Node(tokenizer, "case")
+
+    def CASE_setLabel(self, node, expression):
+        node.append(expression, "label")
+
+    def CASE_initializeStatements(self, node, tokenizer):
+        node.append(jasy.script.parse.Node.Node(tokenizer, "block"), "statements")
+
+    def CASE_addStatement(self, node, statement):
+        node.statements.append(statement)
+
+    def CASE_finish(self, node):
+        pass
+
+    def DEFAULT_build(self, tokenizer):
+        return jasy.script.parse.Node.Node(tokenizer, "default")
+
+    def DEFAULT_initializeStatements(self, node, tokenizer):
+        node.append(jasy.script.parse.Node.Node(tokenizer, "block"), "statements")
+
+    def DEFAULT_addStatement(self, node, statement):
+        node.statements.append(statement)
+
+    def DEFAULT_finish(self, node):
+        pass
+
+    def FOR_build(self, tokenizer):
+        node = jasy.script.parse.Node.Node(tokenizer, "for")
+        node.isLoop = True
+        node.isEach = False
+        return node
+
+    def FOR_rebuildForEach(self, node):
+        node.isEach = True
+
+    # NB: This function is called after rebuildForEach, if that'statement called at all.
+    def FOR_rebuildForIn(self, node):
+        node.type = "for_in"
+
+    def FOR_setCondition(self, node, expression):
+        node.append(expression, "condition")
+
+    def FOR_setSetup(self, node, expression):
+        node.append(expression, "setup")
+
+    def FOR_setUpdate(self, node, expression):
+        node.append(expression, "update")
+
+    def FOR_setObject(self, node, expression, forBlock=None):
+        # wpbasti: not sure what forBlock stands for but it is used in the parser
+        # JS tolerates the optinal unused parameter, but not so Python.
+        node.append(expression, "object")
+
+    def FOR_setIterator(self, node, expression, forBlock=None):
+        # wpbasti: not sure what forBlock stands for but it is used in the parser
+        # JS tolerates the optinal unused parameter, but not so Python.
+        node.append(expression, "iterator")
+
+    def FOR_setBody(self, node, statement):