]> git.saurik.com Git - wxWidgets.git/commitdiff
DocView and ActiveGrid IDE updates from Morgan Hua:
authorRobin Dunn <robin@alldunn.com>
Thu, 20 Apr 2006 06:26:03 +0000 (06:26 +0000)
committerRobin Dunn <robin@alldunn.com>
Thu, 20 Apr 2006 06:26:03 +0000 (06:26 +0000)
    New Features: In Tab-View mode, Ctrl-number will take the user to
    the numbered tab view.  Modified files now show an '*' astrisk in
    the view title.  Debugger framework can now support PHP debugging.
    Not important for python development, but at least that means the
    debugger framework is more generalized.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@38852 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

41 files changed:
wxPython/samples/ide/activegrid/model/projectmodel.py [new file with mode: 0644]
wxPython/samples/ide/activegrid/tool/AboutDialog.py
wxPython/samples/ide/activegrid/tool/AbstractEditor.py
wxPython/samples/ide/activegrid/tool/CodeEditor.py
wxPython/samples/ide/activegrid/tool/DebuggerService.py
wxPython/samples/ide/activegrid/tool/ExtensionService.py
wxPython/samples/ide/activegrid/tool/FindInDirService.py
wxPython/samples/ide/activegrid/tool/HtmlEditor.py
wxPython/samples/ide/activegrid/tool/IDE.py
wxPython/samples/ide/activegrid/tool/MarkerService.py
wxPython/samples/ide/activegrid/tool/MessageService.py
wxPython/samples/ide/activegrid/tool/OutlineService.py
wxPython/samples/ide/activegrid/tool/PHPDebugger.py [new file with mode: 0644]
wxPython/samples/ide/activegrid/tool/PHPEditor.py
wxPython/samples/ide/activegrid/tool/PerlEditor.py
wxPython/samples/ide/activegrid/tool/ProjectEditor.py
wxPython/samples/ide/activegrid/tool/PythonEditor.py
wxPython/samples/ide/activegrid/tool/STCTextEditor.py
wxPython/samples/ide/activegrid/tool/SVNService.py
wxPython/samples/ide/activegrid/tool/UICommon.py
wxPython/samples/ide/activegrid/tool/Wizard.py
wxPython/samples/ide/activegrid/tool/XmlEditor.py
wxPython/samples/ide/activegrid/tool/bmp_source/activegrid.ico [new file with mode: 0644]
wxPython/samples/ide/activegrid/tool/data/tips.txt
wxPython/samples/ide/activegrid/tool/process.py
wxPython/samples/ide/activegrid/tool/project.py
wxPython/samples/ide/activegrid/util/aglogging.py
wxPython/samples/ide/activegrid/util/appdirs.py
wxPython/samples/ide/activegrid/util/datetimeparser.py [new file with mode: 0644]
wxPython/samples/ide/activegrid/util/fileutils.py
wxPython/samples/ide/activegrid/util/objutils.py
wxPython/samples/ide/activegrid/util/parser.py [new file with mode: 0644]
wxPython/samples/ide/activegrid/util/strutils.py
wxPython/samples/ide/activegrid/util/sysutils.py
wxPython/samples/ide/activegrid/util/utillang.py [new file with mode: 0644]
wxPython/samples/ide/activegrid/util/xmlmarshaller.py
wxPython/samples/ide/activegrid/util/xmlutils.py
wxPython/samples/pydocview/FindService.py
wxPython/samples/pydocview/TextEditor.py
wxPython/wx/lib/docview.py
wxPython/wx/lib/pydocview.py

diff --git a/wxPython/samples/ide/activegrid/model/projectmodel.py b/wxPython/samples/ide/activegrid/model/projectmodel.py
new file mode 100644 (file)
index 0000000..aafd8b8
--- /dev/null
@@ -0,0 +1,20 @@
+#----------------------------------------------------------------------------
+# Name:         projectmodel.py
+# Purpose:      This file contains project model information
+#
+# Author:       Morgan Hua
+#
+# Created:      4/18/06
+# CVS-ID:       $Id$
+# Copyright:    (c) 2006 ActiveGrid, Inc.
+# License:      wxWindows License
+#----------------------------------------------------------------------------
+
+
+#----------------------------------------------------------------------------
+# Constants
+#----------------------------------------------------------------------------
+LANGUAGE_PYTHON = "python"
+LANGUAGE_PHP = "php"
+LANGUAGE_DEFAULT = LANGUAGE_PYTHON
+LANGUAGE_LIST = [LANGUAGE_PHP, LANGUAGE_PYTHON]
index 53a00402cab21ea41e0ccc5da6771b40392fe66b..c86043d91dc9d39d69a7534b56e023000ae7908b 100644 (file)
@@ -5,7 +5,7 @@
 # Author:       Morgan Hua
 #
 # Created:      3/22/05
-# Copyright:    (c) 2005 ActiveGrid, Inc.
+# Copyright:    (c) 2005-2006 ActiveGrid, Inc.
 # CVS-ID:       $Id$
 # License:      wxWindows License
 #----------------------------------------------------------------------------
@@ -30,23 +30,25 @@ licenseData = [  # add licenses for base IDE features
     ("Python 2.4", "Python Software Foundation License", "http://www.python.org/2.4/license.html"),
     ("wxPython 2.6", "wxWidgets 2 - LGPL", "http://wxwidgets.org/newlicen.htm"),
     ("wxWidgets", "wxWindows Library License 3", "http://www.wxwidgets.org/manuals/2.6.1/wx_wxlicense.html"),
-    ("pychecker", "MetaSlash - BSD", "http://pychecker.sourceforge.net/COPYRIGHT"), 
+    ("pychecker", "MetaSlash - BSD", "http://pychecker.sourceforge.net/COPYRIGHT"),
     ("process.py", "See file", "http://starship.python.net/~tmick/"),
     ("pysvn", "Apache License, Version 2.0", "http://pysvn.tigris.org/"),
 ]
 
 if not ACTIVEGRID_BASE_IDE:    # add licenses for non-base IDE features such as database connections
     licenseData += [
-        ("pydb2", "LGPL", "http://sourceforge.net/projects/pydb2"), 
+        ("pydb2", "LGPL", "http://sourceforge.net/projects/pydb2"),
         ("pysqlite", "Python License (CNRI)", "http://sourceforge.net/projects/pysqlite"),
-        ("mysql-python", "GPL, Python License (CNRI), Zope Public License", "http://sourceforge.net/projects/mysql-python"), 
-        ("cx_Oracle", "Computronix", "http://www.computronix.com/download/License(cxOracle).txt"), 
+        ("mysql-python", "GPL, Python License (CNRI), Zope Public License", "http://sourceforge.net/projects/mysql-python"),
+        ("cx_Oracle", "Computronix", "http://www.computronix.com/download/License(cxOracle).txt"),
         ("SQLite", "Public Domain", "http://www.sqlite.org/copyright.html"),
         ("PyGreSQL", "BSD", "http://www.pygresql.org"),
         ("pyXML", "CNRI Python License", "http://sourceforge.net/softwaremap/trove_list.php?form_cat=194"),
         ("Zolera Soap Infrastructure", "Zope Public License 2.0", "http://www.zope.org/Resources/License/"),
+        ("python-ldap", "Python Software Foundation License", "http://python-ldap.sourceforge.net"),
         ("Sarissa", "LGPL", "http://sourceforge.net/projects/sarissa/"),
         ("Dynarch DHTML Calendar", "LGPL", "http://www.dynarch.com/projects/calendar/"),
+        ("python-dateutil", "Python Software Foundation License", "http://labix.org/python-dateutil"),        
     ]
 
 if wx.Platform == '__WXMSW__':  # add Windows only licenses
@@ -70,7 +72,7 @@ class AboutDialog(wx.Dialog):
         else:
             splash_bmp = getIDESplashBitmap()
 
-        # find version number from 
+        # find version number from
         versionFilepath = os.path.join(sysutilslib.mainModuleDir, "version.txt")
         if os.path.exists(versionFilepath):
             versionfile = open(versionFilepath, 'r')
@@ -82,7 +84,7 @@ class AboutDialog(wx.Dialog):
 
         image = wx.StaticBitmap(aboutPage, -1, splash_bmp, (0,0), (splash_bmp.GetWidth(), splash_bmp.GetHeight()))
         sizer.Add(image, 0, wx.ALIGN_CENTER|wx.ALL, 0)
-        sizer.Add(wx.StaticText(aboutPage, -1, wx.GetApp().GetAppName() + _("\n%s\n\nCopyright (c) 2003-2005 ActiveGrid Incorporated and Contributors.  All rights reserved.") % version), 0, wx.ALIGN_LEFT|wx.ALL, 10)
+        sizer.Add(wx.StaticText(aboutPage, -1, wx.GetApp().GetAppName() + _("\n%s\n\nCopyright (c) 2003-2006 ActiveGrid Incorporated and Contributors.  All rights reserved.") % version), 0, wx.ALIGN_LEFT|wx.ALL, 10)
         sizer.Add(wx.StaticText(aboutPage, -1, _("http://www.activegrid.com")), 0, wx.ALIGN_LEFT|wx.LEFT|wx.BOTTOM, 10)
         aboutPage.SetSizer(sizer)
         nb.AddPage(aboutPage, _("Copyright"))
@@ -90,15 +92,14 @@ class AboutDialog(wx.Dialog):
         licensePage = wx.Panel(nb, -1)
         grid = wx.grid.Grid(licensePage, -1)
         grid.CreateGrid(len(licenseData), 2)
-        
+
         dc = wx.ClientDC(grid)
         dc.SetFont(grid.GetLabelFont())
         grid.SetColLabelValue(0, _("License"))
         grid.SetColLabelValue(1, _("URL"))
-        w, maxHeight = dc.GetTextExtent(_("License"))
-        w, h = dc.GetTextExtent(_("URL"))
-        if h > maxHeight:
-            maxHeight = h
+        w, h1 = dc.GetTextExtent(_("License"))
+        w, h2 = dc.GetTextExtent(_("URL"))
+        maxHeight = max(h1, h2)
         grid.SetColLabelSize(maxHeight + 6)  # add a 6 pixel margin
 
         maxW = 0
@@ -115,7 +116,7 @@ class AboutDialog(wx.Dialog):
                 grid.SetCellValue(row, 0, license)
             if url:
                 grid.SetCellValue(row, 1, url)
-        
+
         grid.EnableEditing(False)
         grid.EnableDragGridSize(False)
         grid.EnableDragColSize(False)
@@ -132,10 +133,10 @@ class AboutDialog(wx.Dialog):
 
         creditsPage = wx.Panel(nb, -1)
         sizer = wx.BoxSizer(wx.VERTICAL)
-        sizer.Add(wx.StaticText(creditsPage, -1, _("ActiveGrid Development Team:\n\nLarry Abrahams\nLawrence Bruhmuller\nEric Chu\nBeth Fryer\nMatt Fryer\nJoel Hare\nMorgan Hua\nMatt McNulty\nPratik Mehta\nAlan Mullendore\nJeff Norton\nSimon Toens\nKevin Wang\nPeter Yared")), 0, wx.ALIGN_LEFT|wx.ALL, 10)
+        sizer.Add(wx.StaticText(creditsPage, -1, _("ActiveGrid Development Team:\n\nLarry Abrahams\nLawrence Bruhmuller\nEric Chu\nBeth Fryer\nMatt Fryer\nFrankie Fu\nJoel Hare\nMorgan Hua\nMatt McNulty\nPratik Mehta\nAlan Mullendore\nJeff Norton\nKevin Ollivier\nMatt Small\nSimon Toens\nKevin Wang\nPeter Yared\nJeremy Yun")), 0, wx.ALIGN_LEFT|wx.ALL, 10)
         creditsPage.SetSizer(sizer)
         nb.AddPage(creditsPage, _("Credits"))
-            
+
         sizer = wx.BoxSizer(wx.VERTICAL)
         sizer.Add(nb, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
         btn = wx.Button(self, wx.ID_OK)
@@ -145,5 +146,5 @@ class AboutDialog(wx.Dialog):
         self.Layout()
         self.Fit()
         grid.ForceRefresh()  # wxBug: Get rid of unnecessary scrollbars
-        
+
 
index f795ecd6f8e10ed94b8dd72b527644321e4a132d..8a2e9c5232fac8ac742e774344fb87151f1adb62 100644 (file)
@@ -36,6 +36,7 @@ PARKING_VERTICAL = 1
 PARKING_HORIZONTAL = 2
 PARKING_OFFSET = 30    # space between shapes
 
+FORCE_REDRAW_METHOD = "ForceRedraw"
 
 def GetRawModel(model):
     if hasattr(model, "GetRawModel"):
@@ -85,6 +86,7 @@ class CanvasView(wx.lib.docview.View):
         self._propShape = None
         self._maxWidth = 2000
         self._maxHeight = 16000
+        self._valetParking = False
 
 
     def OnDraw(self, dc):
@@ -195,6 +197,16 @@ class CanvasView(wx.lib.docview.View):
         self.SetPropertyModel(None)
                   
 
+    def SetLastRightClick(self, x, y):
+        self._lastRightClick = (x,y)
+        
+
+    def GetLastRightClick(self):
+        if hasattr(self, "_lastRightClick"):
+            return self._lastRightClick
+        return (-1,-1)
+        
+
     def OnKeyPressed(self, event):
         key = event.KeyCode()
         if key == wx.WXK_DELETE:
@@ -211,6 +223,7 @@ class CanvasView(wx.lib.docview.View):
         dc = wx.ClientDC(self._canvas)
         self._canvas.PrepareDC(dc)
         x, y = event.GetLogicalPosition(dc)  # this takes into account scrollbar offset
+        self.SetLastRightClick(x, y)
         shape = self._canvas.FindShape(x, y)[0]
         
         model = None
@@ -260,12 +273,15 @@ class CanvasView(wx.lib.docview.View):
             pass
         else:
             # click on empty part of canvas, deselect everything
+            forceRedrawShapes = []
             needRefresh = False
             for shape in self._diagram.GetShapeList():
                 if hasattr(shape, "GetModel"):
                     if shape.Selected():
                         needRefresh = True
                         shape.Select(False, dc)
+                        if hasattr(shape, FORCE_REDRAW_METHOD):
+                            forceRedrawShapes.append(shape)
             if needRefresh:
                 self._canvas.Redraw(dc)
 
@@ -274,7 +290,8 @@ class CanvasView(wx.lib.docview.View):
         if len(self.GetSelection()) == 0:
             self.SetPropertyShape(None)
 
-
+        for shape in forceRedrawShapes:
+            shape.ForceRedraw()
 
     def OnLeftDoubleClick(self, event):
         propertyService = wx.GetApp().GetService(PropertyService.PropertyService)
@@ -401,8 +418,20 @@ class CanvasView(wx.lib.docview.View):
         dc.EndDrawing()
 
 
+    def SetValetParking(self, enable=True):
+        """ If valet parking is enabled, remember last parking spot and try for a spot near it """
+        self._valetParking = enable
+        if enable:
+            self._valetPosition = None
+        
+
     def FindParkingSpot(self, width, height, parking=PARKING_HORIZONTAL, x=PARKING_OFFSET, y=PARKING_OFFSET):
-        """ given a width and height, find a upper left corner where shape can be parked without overlapping other shape """
+        """
+            Given a width and height, find a upper left corner where shape can be parked without overlapping other shape
+        """
+        if self._valetParking and self._valetPosition:
+            x, y = self._valetPosition
+        
         max = 700  # max distance to the right where we'll place tables
         noParkingSpot = True
 
@@ -422,6 +451,9 @@ class CanvasView(wx.lib.docview.View):
             else:
                 noParkingSpot = False
 
+        if self._valetParking:
+            self._valetPosition = (x, y)
+            
         return x, y
 
 
@@ -518,7 +550,8 @@ class CanvasView(wx.lib.docview.View):
                         self._diagram.RemoveShape(line)
                         line.Delete()
                     
-            shape.RemoveFromCanvas(self._canvas)
+            if self._canvas:
+                shape.RemoveFromCanvas(self._canvas)
             self._diagram.RemoveShape(shape)
             shape.Delete()
 
@@ -698,6 +731,9 @@ class CanvasView(wx.lib.docview.View):
                 self._propShape.SetTextColour("WHITE", 0)
             self._propShape.Draw(dc)
 
+            if hasattr(self._propShape, FORCE_REDRAW_METHOD):
+                self._propShape.ForceRedraw()
+
         dc.EndDrawing()
 
 
index 7e245bcd1f79945232fb2fc114bda740d173b48f..72a5dd2c4f57cb16d2b8475db808c4286088b55f 100644 (file)
@@ -19,7 +19,6 @@ import os
 import re
 import string
 import sys
-import DebuggerService
 import MarkerService
 from UICommon import CaseInsensitiveCompare
 _ = wx.GetTranslation
@@ -120,16 +119,27 @@ class CodeView(STCTextEditor.TextView):
             return False
         id = event.GetId()
         if id == EXPAND_TEXT_ID:
-            event.Enable(self.GetCtrl().CanLineExpand(self.GetCtrl().GetCurrentLine()))
+            if self.GetCtrl().GetViewFolding():
+                event.Enable(self.GetCtrl().CanLineExpand(self.GetCtrl().GetCurrentLine()))
+            else:
+                event.Enable(False)
             return True
         elif id == COLLAPSE_TEXT_ID:
-            event.Enable(self.GetCtrl().CanLineCollapse(self.GetCtrl().GetCurrentLine()))
+            if self.GetCtrl().GetViewFolding():
+                event.Enable(self.GetCtrl().CanLineCollapse(self.GetCtrl().GetCurrentLine()))
+            else:
+                event.Enable(False)
             return True
         elif (id == EXPAND_TOP_ID
         or id == COLLAPSE_TOP_ID
         or id == EXPAND_ALL_ID
-        or id == COLLAPSE_ALL_ID
-        or id == AUTO_COMPLETE_ID
+        or id == COLLAPSE_ALL_ID):
+            if self.GetCtrl().GetViewFolding():
+                event.Enable(self.GetCtrl().GetTextLength() > 0)
+            else:
+                event.Enable(False)
+            return True            
+        elif (id == AUTO_COMPLETE_ID
         or id == CLEAN_WHITESPACE
         or id == INDENT_LINES_ID
         or id == DEDENT_LINES_ID
@@ -140,10 +150,12 @@ class CodeView(STCTextEditor.TextView):
         elif id == CHECK_CODE_ID:
             event.Enable(False)
             return True
-        elif (id == SET_INDENT_WIDTH_ID
-        or id == FOLDING_ID):
+        elif id == SET_INDENT_WIDTH_ID:
             event.Enable(True)
             return True
+        elif id == FOLDING_ID:
+            event.Enable(self.GetCtrl().GetViewFolding())
+            return True
         elif id == USE_TABS_ID:
             event.Enable(True)
             event.Check(self.GetCtrl().GetUseTabs())
@@ -210,7 +222,7 @@ class CodeView(STCTextEditor.TextView):
         filename = document.GetFilename()
         if filename:
             rootItem = treeCtrl.AddRoot(os.path.basename(filename))
-            treeCtrl.SetDoSelectCallback(rootItem, self, None)
+            treeCtrl.SetDoSelectCallback(rootItem, self, (0,0))
         else:
             return True
 
@@ -232,11 +244,13 @@ class CodeView(STCTextEditor.TextView):
             if classLine:
                 indent = classLine.start(0)
                 itemStr = classLine.string[classLine.start(0):classLine.end(0)-1]  # don't take the closing ':'
+                itemStr = itemStr.replace("\n", "").replace("\r", "").replace(",\\", ",").replace("  ", "")  # remove line continuations and spaces from outline view
             else:
                 defLine = defPat.search(line)
                 if defLine:
                     indent = defLine.start(0)
                     itemStr = defLine.string[defLine.start(0):defLine.end(0)]
+                    itemStr = itemStr.replace("\n", "").replace("\r", "").replace(",\\", ",").replace("  ", "")  # remove line continuations and spaces from outline view
 
             if indent == 0:
                 parentItem = rootItem
@@ -467,6 +481,9 @@ class CodeView(STCTextEditor.TextView):
 
 
     def OnUpdate(self, sender = None, hint = None):
+        if wx.lib.docview.View.OnUpdate(self, sender, hint):
+            return
+
         if hint == "ViewStuff":
             self.GetCtrl().SetViewDefaults()
         elif hint == "Font":
@@ -474,6 +491,7 @@ class CodeView(STCTextEditor.TextView):
             self.GetCtrl().SetFont(font)
             self.GetCtrl().SetFontColor(color)
         else:
+            import DebuggerService
             dbg_service = wx.GetApp().GetService(DebuggerService.DebuggerService)
             if dbg_service:
                 dbg_service.SetCurrentBreakpointMarkers(self)
@@ -623,7 +641,7 @@ class CodeCtrl(STCTextEditor.TextCtrl):
     BREAKPOINT_MARKER_MASK = 0x2
     
             
-    def __init__(self, parent, id=-1, style = wx.NO_FULL_REPAINT_ON_RESIZE):
+    def __init__(self, parent, id=-1, style = wx.NO_FULL_REPAINT_ON_RESIZE, clearTab=True):
         STCTextEditor.TextCtrl.__init__(self, parent, id, style)
         
         self.UsePopUp(False)
@@ -635,7 +653,6 @@ class CodeCtrl(STCTextEditor.TextCtrl):
         self.SetMarginType(2, wx.stc.STC_MARGIN_SYMBOL)
         self.SetMarginMask(2, wx.stc.STC_MASK_FOLDERS)
         self.SetMarginSensitive(2, True)
-        self.SetMarginWidth(2, 12)
         
         self.SetMarginSensitive(1, False)
         self.SetMarginMask(1, 0x4)
@@ -657,7 +674,7 @@ class CodeCtrl(STCTextEditor.TextCtrl):
         # Define the breakpoint marker
         self.MarkerDefine(CodeCtrl.BREAKPOINT_MARKER_NUM, wx.stc.STC_MARK_CIRCLE, wx.BLACK, (255,0,0))
         
-        if _WINDOWS:  # should test to see if menu item exists, if it does, add this workaround
+        if _WINDOWS and clearTab:  # should test to see if menu item exists, if it does, add this workaround
             self.CmdKeyClear(wx.stc.STC_KEY_TAB, 0)  # menu item "Indent Lines" from CodeService.InstallControls() generates another INDENT_LINES_ID event, so we'll explicitly disable the tab processing in the editor
 
         wx.stc.EVT_STC_MARGINCLICK(self, self.GetId(), self.OnMarginClick)
@@ -693,7 +710,7 @@ class CodeCtrl(STCTextEditor.TextCtrl):
         item = wx.MenuItem(menu, TOGGLEBREAKPOINT_ID, _("Toggle Breakpoint"))
         menu.AppendItem(item)
         self.Bind(wx.EVT_MENU, self.OnPopToggleMarker, id=TOGGLEMARKER_ID)
-        item = wx.MenuItem(menu, TOGGLEMARKER_ID, _("Toggle Marker"))
+        item = wx.MenuItem(menu, TOGGLEMARKER_ID, _("Toggle Bookmark"))
         menu.AppendItem(item)
         menu.AppendSeparator()
                 
@@ -715,6 +732,7 @@ class CodeCtrl(STCTextEditor.TextCtrl):
 
     def OnPopToggleBP(self, event):
         """ Toggle break point on right click line, not current line """
+        import DebuggerService
         wx.GetApp().GetService(DebuggerService.DebuggerService).OnToggleBreakpoint(event, line=self._rightClickLine)
       
   
@@ -859,6 +877,7 @@ class CodeCtrl(STCTextEditor.TextCtrl):
 
         elif evt.GetMargin() == 0:
             #This is used to toggle breakpoints via the debugger service.
+            import DebuggerService
             db_service = wx.GetApp().GetService(DebuggerService.DebuggerService)
             if db_service:
                 db_service.OnToggleBreakpoint(evt, line=self.LineFromPosition(evt.GetPosition()))
index 485853611bfbcc7d169bbcabcefb70a81d26ba4c..67ed47fac58ffdc1fbc19e9ec20674e2db2e92df 100644 (file)
@@ -1,6 +1,6 @@
 #----------------------------------------------------------------------------
 # Name:         DebuggerService.py
-# Purpose:      Debugger Service for Python.
+# Purpose:      Debugger Service for Python and PHP
 #
 # Author:       Matt Fryer
 #
@@ -21,6 +21,9 @@ import Service
 import STCTextEditor
 import CodeEditor
 import PythonEditor
+import PHPEditor
+import PHPDebugger
+import activegrid.model.projectmodel as projectmodel
 from IDE import ACTIVEGRID_BASE_IDE
 if not ACTIVEGRID_BASE_IDE:
     import ProcessModelEditor
@@ -42,6 +45,10 @@ import DebuggerHarness
 import traceback
 import StringIO
 import UICommon
+import activegrid.util.sysutils as sysutilslib
+import subprocess
+import shutil
+
 if wx.Platform == '__WXMSW__':
     try:
         import win32api
@@ -60,6 +67,11 @@ _ = wx.GetTranslation
 _VERBOSE = False
 _WATCHES_ON = False
 
+import  wx.lib.newevent
+(UpdateTextEvent, EVT_UPDATE_STDTEXT) = wx.lib.newevent.NewEvent()
+(UpdateErrorEvent, EVT_UPDATE_ERRTEXT) = wx.lib.newevent.NewEvent()
+(DebugInternalWebServer, EVT_DEBUG_INTERNAL) = wx.lib.newevent.NewEvent()
+
 # Class to read from stdout or stderr and write the result to a text control.
 # Args: file=file-like object
 #       callback_function= function that takes a single argument, the line of text
@@ -74,11 +86,11 @@ class OutputReaderThread(threading.Thread):
         self._accumulate = accumulate
         self._callbackOnExit = callbackOnExit
         self.setDaemon(True)
-    
+
     def __del__(self):
-        # See comment on DebugCommandUI.StopExecution
+        # See comment on PythonDebuggerUI.StopExecution
         self._keepGoing = False
-             
+
     def run(self):
         file = self._file
         start = time.time()
@@ -95,20 +107,20 @@ class OutputReaderThread(threading.Thread):
                     # Should use a buffer? StringIO?
                     output += text
                 # Seems as though the read blocks if we got an error, so, to be
-                # sure that at least some of the exception gets printed, always 
+                # sure that at least some of the exception gets printed, always
                 # send the first hundred lines back as they come in.
-                if self._lineCount < 100 and self._keepGoing:    
+                if self._lineCount < 100 and self._keepGoing:
                     self._callback_function(output)
                     self._lineCount += 1
-                    output = "" 
+                    output = ""
                 elif time.time() - start > 0.25 and self._keepGoing:
                     try:
-                        self._callback_function(output) 
+                        self._callback_function(output)
                     except wx._core.PyDeadObjectError:
                         # GUI was killed while we were blocked.
                         self._keepGoing = False
                     start = time.time()
-                    output = "" 
+                    output = ""
             #except TypeError:
             #    pass
             except:
@@ -121,32 +133,61 @@ class OutputReaderThread(threading.Thread):
             except wx._core.PyDeadObjectError:
                 pass
         if _VERBOSE: print "Exiting OutputReaderThread"
-        
+
     def AskToStop(self):
         self._keepGoing = False
-                
-import  wx.lib.newevent
-(UpdateTextEvent, EVT_UPDATE_STDTEXT) = wx.lib.newevent.NewEvent()
-(UpdateErrorEvent, EVT_UPDATE_ERRTEXT) = wx.lib.newevent.NewEvent()
-        
+
+
 class Executor:
-    
+    PHP_CGI_BIN_PATH_WIN  = "../../3rdparty/php"
+    PHP_CGI_BIN_PATH_UNIX = "../../../3rdparty/php/bin"
+    PHP_CGI_BIN_PATH_OSX  = "../3rdparty/php/bin"
+    PHP_CGI_EXEC_WIN      = "php-cgi.exe"
+    PHP_CGI_EXEC_UNIX     = "php"
+    PHP_CGI_EXEC_OSX      = "php-cgi"
+
     def GetPythonExecutablePath():
         path = UICommon.GetPythonExecPath()
         if path:
             return path
-        wx.MessageBox(_("To proceed I need to know the location of the python.exe you would like to use.\nTo set this, go to Tools-->Options and use the 'Python' tab to enter a value.\n"), _("Python Executable Location Unknown"))
+        wx.MessageBox(_("To proceed we need to know the location of the python.exe you would like to use.\nTo set this, go to Tools-->Options and use the 'Python' tab to enter a value.\n"), _("Python Executable Location Unknown"))
         return None
-    GetPythonExecutablePath = staticmethod(GetPythonExecutablePath)   
-     
+    GetPythonExecutablePath = staticmethod(GetPythonExecutablePath)
+
+    def GetPHPExecutablePath():
+        if sysutilslib.isWindows():
+            phpCgiBinPath = Executor.PHP_CGI_BIN_PATH_WIN
+            phpCgiExec    = Executor.PHP_CGI_EXEC_WIN
+        elif sys.platform == "darwin":
+            phpCgiBinPath = Executor.PHP_CGI_BIN_PATH_OSX
+            phpCgiExec    = Executor.PHP_CGI_EXEC_OSX
+        else:
+            phpCgiBinPath = Executor.PHP_CGI_BIN_PATH_UNIX
+            phpCgiExec    = Executor.PHP_CGI_EXEC_UNIX
+
+        if sysutilslib.isRelease():
+            phpCgiExecFullPath = os.path.normpath(os.path.join(sysutilslib.mainModuleDir, phpCgiBinPath, phpCgiExec))
+        else:
+            phpCgiExecFullPath = phpCgiExec
+
+        if _VERBOSE:
+            print "php cgi executable full path is: %s" % phpCgiExecFullPath
+
+        return phpCgiExecFullPath
+    GetPHPExecutablePath = staticmethod(GetPHPExecutablePath)
+
     def __init__(self, fileName, wxComponent, arg1=None, arg2=None, arg3=None, arg4=None, arg5=None, arg6=None, arg7=None, arg8=None, arg9=None, callbackOnExit=None):
         self._fileName = fileName
         self._stdOutCallback = self.OutCall
         self._stdErrCallback = self.ErrCall
         self._callbackOnExit = callbackOnExit
         self._wxComponent = wxComponent
-        path = Executor.GetPythonExecutablePath()
-        self._cmd = '"' + path + '" -u \"' + fileName + '\"'
+        if fileName.endswith('.py') or fileName.endswith('.pyc'):
+            self._path = Executor.GetPythonExecutablePath()
+            self._cmd = '"' + self._path + '" -u \"' + fileName + '\"'
+        else:
+            self._path = Executor.GetPHPExecutablePath()
+            self._cmd = '"' + self._path + '"'
         #Better way to do this? Quotes needed for windows file paths.
         def spaceAndQuote(text):
             if text.startswith("\"") and text.endswith("\""):
@@ -171,34 +212,40 @@ class Executor:
             self._cmd += spaceAndQuote(arg8)
         if(arg9 != None):
             self._cmd += spaceAndQuote(arg9)
-        
+
         self._stdOutReader = None
         self._stdErrReader = None
         self._process = None
-        
+
     def OutCall(self, text):
         evt = UpdateTextEvent(value = text)
         wx.PostEvent(self._wxComponent, evt)
-                   
+
     def ErrCall(self, text):
         evt = UpdateErrorEvent(value = text)
         wx.PostEvent(self._wxComponent, evt)
-            
+
     def Execute(self, arguments, startIn=None, environment=None):
         if not startIn:
             startIn = str(os.getcwd())
         startIn = os.path.abspath(startIn)
-        command = self._cmd + ' ' + arguments
+
+        if arguments and arguments != " ":
+            command = self._cmd + ' ' + arguments
+        else:
+            command = self._cmd
+
+        if _VERBOSE: print "start debugger executable: " + command + "\n"
         self._process = process.ProcessOpen(command, mode='b', cwd=startIn, env=environment)
         # Kick off threads to read stdout and stderr and write them
-        # to our text control. 
+        # to our text control.
         self._stdOutReader = OutputReaderThread(self._process.stdout, self._stdOutCallback, callbackOnExit=self._callbackOnExit)
         self._stdOutReader.start()
         self._stdErrReader = OutputReaderThread(self._process.stderr, self._stdErrCallback, accumulate=False)
         self._stdErrReader.start()
-                        
+
     def DoStopExecution(self):
-        # See comment on DebugCommandUI.StopExecution
+        # See comment on PythonDebuggerUI.StopExecution
         if(self._process != None):
             self._stdOutReader.AskToStop()
             self._stdErrReader.AskToStop()
@@ -207,30 +254,33 @@ class Executor:
             except:
                 pass
             self._process = None
-        
+
+    def GetExecPath(self):
+        return self._path
+
 class RunCommandUI(wx.Panel):
     runners = []
-    
+
     def ShutdownAllRunners():
-        # See comment on DebugCommandUI.StopExecution
+        # See comment on PythonDebuggerUI.StopExecution
         for runner in RunCommandUI.runners:
             try:
                 runner.StopExecution(None)
             except wx._core.PyDeadObjectError:
                 pass
-        RunCommandUI.runners = []    
+        RunCommandUI.runners = []
     ShutdownAllRunners = staticmethod(ShutdownAllRunners)
-    
+
     def __init__(self, parent, id, fileName):
         wx.Panel.__init__(self, parent, id)
         self._noteBook = parent
-        
+
         threading._VERBOSE = _VERBOSE
-        
+
         self.KILL_PROCESS_ID = wx.NewId()
         self.CLOSE_TAB_ID = wx.NewId()
-        
-        
+
+
         # GUI Initialization follows
         sizer = wx.BoxSizer(wx.HORIZONTAL)
         self._tb = tb = wx.ToolBar(self,  -1, wx.DefaultPosition, (30,1000), wx.TB_VERTICAL| wx.TB_FLAT, "Runner" )
@@ -240,11 +290,11 @@ class RunCommandUI(wx.Panel):
         close_bmp = getCloseBitmap()
         tb.AddSimpleTool( self.CLOSE_TAB_ID, close_bmp, _('Close Window'))
         wx.EVT_TOOL(self, self.CLOSE_TAB_ID, self.OnToolClicked)
-        
+
         stop_bmp = getStopBitmap()
         tb.AddSimpleTool(self.KILL_PROCESS_ID, stop_bmp, _("Stop the Run."))
         wx.EVT_TOOL(self, self.KILL_PROCESS_ID, self.OnToolClicked)
-        
+
         tb.Realize()
         self._textCtrl = STCTextEditor.TextCtrl(self, wx.NewId()) #id)
         sizer.Add(self._textCtrl, 1, wx.ALIGN_LEFT|wx.ALL|wx.EXPAND, 1)
@@ -257,27 +307,27 @@ class RunCommandUI(wx.Panel):
         self._textCtrl.SetFont(wx.Font(9, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName = font))
         self._textCtrl.SetFontColor(wx.BLACK)
         self._textCtrl.StyleClearAll()
-     
-        #Disabling for now...may interfere with file open. wx.stc.EVT_STC_DOUBLECLICK(self._textCtrl, self._textCtrl.GetId(), self.OnDoubleClick)
+
+        wx.stc.EVT_STC_DOUBLECLICK(self._textCtrl, self._textCtrl.GetId(), self.OnDoubleClick)
 
         self.SetSizer(sizer)
         sizer.Fit(self)
-        
+
         self._stopped = False
         # Executor initialization
         self._executor = Executor(fileName, self, callbackOnExit=self.ExecutorFinished)
         self.Bind(EVT_UPDATE_STDTEXT, self.AppendText)
         self.Bind(EVT_UPDATE_ERRTEXT, self.AppendErrorText)
-        
+
         RunCommandUI.runners.append(self)
-        
+
     def __del__(self):
-        # See comment on DebugCommandUI.StopExecution
+        # See comment on PythonDebuggerUI.StopExecution
         self._executor.DoStopExecution()
-                 
-    def Execute(self, initialArgs, startIn, environment):
+
+    def Execute(self, initialArgs, startIn, environment, onWebServer = False):
         self._executor.Execute(initialArgs, startIn, environment)
-    
+
     def ExecutorFinished(self):
         self._tb.EnableTool(self.KILL_PROCESS_ID, False)
         nb = self.GetParent()
@@ -287,27 +337,27 @@ class RunCommandUI(wx.Panel):
                 newText = text.replace("Running", "Finished")
                 nb.SetPageText(i, newText)
                 break
-           
+
     def StopExecution(self):
         if not self._stopped:
             self._stopped = True
             self.Unbind(EVT_UPDATE_STDTEXT)
             self.Unbind(EVT_UPDATE_ERRTEXT)
             self._executor.DoStopExecution()
-        
+
     def AppendText(self, event):
         self._textCtrl.SetReadOnly(False)
         self._textCtrl.AddText(event.value)
         self._textCtrl.ScrollToLine(self._textCtrl.GetLineCount())
         self._textCtrl.SetReadOnly(True)
-        
+
     def AppendErrorText(self, event):
         self._textCtrl.SetReadOnly(False)
-        self._textCtrl.SetFontColor(wx.RED)   
+        self._textCtrl.SetFontColor(wx.RED)
         self._textCtrl.StyleClearAll()
         self._textCtrl.AddText(event.value)
         self._textCtrl.ScrollToLine(self._textCtrl.GetLineCount())
-        self._textCtrl.SetFontColor(wx.BLACK)   
+        self._textCtrl.SetFontColor(wx.BLACK)
         self._textCtrl.StyleClearAll()
         self._textCtrl.SetReadOnly(True)
 
@@ -317,26 +367,26 @@ class RunCommandUI(wx.Panel):
         index = self._noteBook.GetSelection()
         self._noteBook.GetPage(index).Show(False)
         self._noteBook.RemovePage(index)
-        
+
     #------------------------------------------------------------------------------
     # Event handling
     #-----------------------------------------------------------------------------
-    
+
     def OnToolClicked(self, event):
         id = event.GetId()
-        
+
         if id == self.KILL_PROCESS_ID:
             self.StopExecution()
-            
+
         elif id == self.CLOSE_TAB_ID:
             self.StopAndRemoveUI(event)
-            
+
     def OnDoubleClick(self, event):
         # Looking for a stack trace line.
         lineText, pos = self._textCtrl.GetCurLine()
         fileBegin = lineText.find("File \"")
-        fileEnd = lineText.find("\", line ") 
-        lineEnd = lineText.find(", in ")   
+        fileEnd = lineText.find("\", line ")
+        lineEnd = lineText.find(", in ")
         if lineText == "\n" or  fileBegin == -1 or fileEnd == -1 or lineEnd == -1:
             # Check the line before the one that was clicked on
             lineNumber = self._textCtrl.GetCurrentLine()
@@ -344,11 +394,11 @@ class RunCommandUI(wx.Panel):
                 return
             lineText = self._textCtrl.GetLine(lineNumber - 1)
             fileBegin = lineText.find("File \"")
-            fileEnd = lineText.find("\", line ") 
-            lineEnd = lineText.find(", in ")   
+            fileEnd = lineText.find("\", line ")
+            lineEnd = lineText.find(", in ")
             if lineText == "\n" or  fileBegin == -1 or fileEnd == -1 or lineEnd == -1:
                 return
-                
+
         filename = lineText[fileBegin + 6:fileEnd]
         lineNum = int(lineText[fileEnd + 8:lineEnd])
 
@@ -368,103 +418,72 @@ class RunCommandUI(wx.Panel):
             foundView.Activate()
             foundView.GotoLine(lineNum)
             startPos = foundView.PositionFromLine(lineNum)
-            
-        # FACTOR THIS INTO DocManager
-        openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
-        for openDoc in openDocs:  
-            if(isinstance(openDoc, CodeEditor.CodeDocument)): 
-                openDoc.GetFirstView().GetCtrl().ClearCurrentLineMarkers()    
-                
-        foundView.GetCtrl().MarkerAdd(lineNum -1, CodeEditor.CodeCtrl.CURRENT_LINE_MARKER_NUM)
+            lineText = foundView.GetCtrl().GetLine(lineNum - 1)
+            foundView.SetSelection(startPos, startPos + len(lineText.rstrip("\n")))
+            import OutlineService
+            wx.GetApp().GetService(OutlineService.OutlineService).LoadOutline(foundView, position=startPos)
 
 
 DEFAULT_PORT = 32032
-DEFAULT_HOST = 'localhost' 
-PORT_COUNT = 21      
+DEFAULT_HOST = 'localhost'
+PORT_COUNT = 21
 
-class DebugCommandUI(wx.Panel):
-    debuggerPortList = None   
+class BaseDebuggerUI(wx.Panel):
     debuggers = []
-    
+
     def NotifyDebuggersOfBreakpointChange():
-        for debugger in DebugCommandUI.debuggers:
+        for debugger in BaseDebuggerUI.debuggers:
             debugger.BreakPointChange()
-            
+
     NotifyDebuggersOfBreakpointChange = staticmethod(NotifyDebuggersOfBreakpointChange)
+
     def DebuggerRunning():
-        for debugger in DebugCommandUI.debuggers:
+        for debugger in BaseDebuggerUI.debuggers:
             if debugger._executor:
                 return True
         return False
     DebuggerRunning = staticmethod(DebuggerRunning)
-    
+
+    def DebuggerInWait():
+        for debugger in BaseDebuggerUI.debuggers:
+            if debugger._executor:
+                if debugger._callback._waiting:
+                    return True
+        return False
+    DebuggerInWait = staticmethod(DebuggerInWait)
+
+    def DebuggerPastAutoContinue():
+        for debugger in BaseDebuggerUI.debuggers:
+            if debugger._executor:
+                if debugger._callback._waiting and not debugger._callback._autoContinue:
+                    return True
+        return False
+    DebuggerPastAutoContinue = staticmethod(DebuggerPastAutoContinue)
+
     def ShutdownAllDebuggers():
-        # See comment on DebugCommandUI.StopExecution
-        for debugger in DebugCommandUI.debuggers:
+        for debugger in BaseDebuggerUI.debuggers:
             try:
                 debugger.StopExecution(None)
             except wx._core.PyDeadObjectError:
                 pass
-        DebugCommandUI.debuggers = []    
+        BaseDebuggerUI.debuggers = []
     ShutdownAllDebuggers = staticmethod(ShutdownAllDebuggers)
-    
-    def GetAvailablePort():
-        for index in range( 0, len(DebugCommandUI.debuggerPortList)):               
-            port = DebugCommandUI.debuggerPortList[index]
-            if DebugCommandUI.PortAvailable(port):
-                DebugCommandUI.debuggerPortList.pop(index)
-                return port
-        wx.MessageBox(_("Out of ports for debugging!  Please restart the application builder.\nIf that does not work, check for and remove running instances of python."), _("Out of Ports"))
-        assert False, "Out of ports for debugger."
-        
-    GetAvailablePort = staticmethod(GetAvailablePort)
-
-    def ReturnPortToPool(port):
-        config = wx.ConfigBase_Get()
-        startingPort = config.ReadInt("DebuggerStartingPort", DEFAULT_PORT)
-        val = int(startingPort) + int(PORT_COUNT)
-        if int(port) >= startingPort and (int(port) <= val):
-            DebugCommandUI.debuggerPortList.append(int(port))
-            
-    ReturnPortToPool = staticmethod(ReturnPortToPool)
-
-    def PortAvailable(port):
-        config = wx.ConfigBase_Get()
-        hostname = config.Read("DebuggerHostName", DEFAULT_HOST)
-        try:
-            server = AGXMLRPCServer((hostname, port))
-            server.server_close()
-            if _VERBOSE: print "Port ", str(port), " available."
-            return True
-        except:
-            tp,val,tb = sys.exc_info()
-            if _VERBOSE: traceback.print_exception(tp, val, tb)
-            if _VERBOSE: print "Port ", str(port), " unavailable."
-            return False
-            
-    PortAvailable = staticmethod(PortAvailable)
-        
-    def NewPortRange():
-        config = wx.ConfigBase_Get()
-        startingPort = config.ReadInt("DebuggerStartingPort", DEFAULT_PORT)
-        DebugCommandUI.debuggerPortList = range(startingPort, startingPort + PORT_COUNT)
-    NewPortRange = staticmethod(NewPortRange)
-    
-    def __init__(self, parent, id, command, service):
-        # Check for ports before creating the panel.
-        if not DebugCommandUI.debuggerPortList:
-            DebugCommandUI.NewPortRange()
-        self._debuggerPort = str(DebugCommandUI.GetAvailablePort())
-        self._guiPort = str(DebugCommandUI.GetAvailablePort())
-        self._debuggerBreakPort = str(DebugCommandUI.GetAvailablePort())
 
+    def __init__(self, parent, id):
         wx.Panel.__init__(self, parent, id)
-        
         self._parentNoteBook = parent
-        self._command = command
-        self._service = service
+
+        self._service = None
+        self._executor = None
+        self._callback = None
+        self._stopped = False
+
+        BaseDebuggerUI.debuggers.append(self)
+        self._stopped = True
+        self.Bind(EVT_UPDATE_STDTEXT, self.AppendText)
+        self.Bind(EVT_UPDATE_ERRTEXT, self.AppendErrorText)
         self._executor = None
+
         self.STEP_ID = wx.NewId()
         self.CONTINUE_ID = wx.NewId()
         self.STEP_OUT_ID = wx.NewId()
@@ -478,37 +497,37 @@ class DebugCommandUI(wx.Panel):
         self._tb = tb = wx.ToolBar(self,  -1, wx.DefaultPosition, (1000,30), wx.TB_HORIZONTAL| wx.NO_BORDER| wx.TB_FLAT| wx.TB_TEXT, "Debugger" )
         sizer.Add(tb, 0, wx.EXPAND |wx.ALIGN_LEFT|wx.ALL, 1)
         tb.SetToolBitmapSize((16,16))
-       
+
         close_bmp = getCloseBitmap()
         tb.AddSimpleTool( self.CLOSE_WINDOW_ID, close_bmp, _('Close Window'))
         wx.EVT_TOOL(self, self.CLOSE_WINDOW_ID, self.StopAndRemoveUI)
-        
+
         stop_bmp = getStopBitmap()
         tb.AddSimpleTool( self.KILL_PROCESS_ID, stop_bmp, _("Stop Debugging"))
         wx.EVT_TOOL(self, self.KILL_PROCESS_ID, self.StopExecution)
-        
+
         tb.AddSeparator()
-        
+
         break_bmp = getBreakBitmap()
         tb.AddSimpleTool( self.BREAK_INTO_DEBUGGER_ID, break_bmp, _("Break into Debugger"))
         wx.EVT_TOOL(self, self.BREAK_INTO_DEBUGGER_ID, self.BreakExecution)
-        
+
         tb.AddSeparator()
-                
+
         continue_bmp = getContinueBitmap()
         tb.AddSimpleTool( self.CONTINUE_ID, continue_bmp, _("Continue Execution"))
         wx.EVT_TOOL(self, self.CONTINUE_ID, self.OnContinue)
-         
+        self.Bind(EVT_DEBUG_INTERNAL, self.OnContinue)
+
         tb.AddSeparator()
-               
         next_bmp = getNextBitmap()
         tb.AddSimpleTool( self.NEXT_ID, next_bmp, _("Step to next line"))
         wx.EVT_TOOL(self, self.NEXT_ID, self.OnNext)
-        
+
         step_bmp = getStepInBitmap()
         tb.AddSimpleTool( self.STEP_ID, step_bmp, _("Step in"))
         wx.EVT_TOOL(self, self.STEP_ID, self.OnSingleStep)
-        
+
         stepOut_bmp = getStepReturnBitmap()
         tb.AddSimpleTool(self.STEP_OUT_ID, stepOut_bmp, _("Stop at function return"))
         wx.EVT_TOOL(self, self.STEP_OUT_ID, self.OnStepOut)
@@ -519,11 +538,12 @@ class DebugCommandUI(wx.Panel):
             tb.AddSimpleTool(self.ADD_WATCH_ID, watch_bmp, _("Add a watch"))
             wx.EVT_TOOL(self, self.ADD_WATCH_ID, self.OnAddWatch)
             tb.AddSeparator()
-        
+
         clear_bmp = getClearOutputBitmap()
         tb.AddSimpleTool(self.CLEAR_ID, clear_bmp, _("Clear output pane"))
         wx.EVT_TOOL(self, self.CLEAR_ID, self.OnClearOutput)
-        
+
+        self._toolEnabled = True
         self.framesTab = None
         self.DisableWhileDebuggerRunning()
         self.framesTab = self.MakeFramesUI(self, wx.NewId(), None)
@@ -531,85 +551,72 @@ class DebugCommandUI(wx.Panel):
         self._statusBar = wx.StatusBar( self, -1)
         self._statusBar.SetFieldsCount(1)
         sizer.Add(self._statusBar, 0, wx.EXPAND |wx.ALIGN_LEFT|wx.ALL, 1)
-
         self.SetStatusText("Starting debug...")
-        
         self.SetSizer(sizer)
         tb.Realize()
         sizer.Fit(self)
-        config = wx.ConfigBase_Get()
-        self._debuggerHost = self._guiHost = config.Read("DebuggerHostName", DEFAULT_HOST)
-        url = 'http://' + self._debuggerHost + ':' + self._debuggerPort + '/'
-        self._breakURL = 'http://' + self._debuggerHost + ':' + self._debuggerBreakPort + '/'
-        self._callback = DebuggerCallback(self._guiHost, self._guiPort, url, self._breakURL, self)
-        if DebuggerHarness.__file__.find('library.zip') > 0:
-            try:
-                fname = DebuggerHarness.__file__
-                parts = fname.split('library.zip')
-                path = os.path.join(parts[0],'activegrid', 'tool', 'DebuggerHarness.py')
-            except:
-                tp, val, tb = sys.exc_info()
-                traceback.print_exception(tp, val, tb)   
-            
-        else:
-            print "Starting debugger on these ports: %s, %s, %s" % (str(self._debuggerPort) , str(self._guiPort) , str(self._debuggerBreakPort))
-            path = DebuggerService.ExpandPath(DebuggerHarness.__file__)
-        self._executor = Executor(path, self, self._debuggerHost, \
-                                                self._debuggerPort, self._debuggerBreakPort, self._guiHost, self._guiPort, self._command, callbackOnExit=self.ExecutorFinished)
-        
-        self.Bind(EVT_UPDATE_STDTEXT, self.AppendText)
-        self.Bind(EVT_UPDATE_ERRTEXT, self.AppendErrorText)
-        DebugCommandUI.debuggers.append(self)
-        self._stopped = False
-        
+
     def OnSingleStep(self, event):
         self._callback.SingleStep()
 
     def OnContinue(self, event):
         self._callback.Continue()
-        
+
     def OnStepOut(self, event):
         self._callback.Return()
-        
+
     def OnNext(self, event):
         self._callback.Next()
 
     def BreakPointChange(self):
         if not self._stopped:
-            self._callback.pushBreakpoints()
+            self._callback.PushBreakpoints()
         self.framesTab.PopulateBPList()
-        
+
     def __del__(self):
-        # See comment on DebugCommandUI.StopExecution
-        self.StopExecution(None)        
-        
+        # See comment on PythonDebuggerUI.StopExecution
+        self.StopExecution(None)
+
     def DisableWhileDebuggerRunning(self):
-        self._tb.EnableTool(self.STEP_ID, False)
-        self._tb.EnableTool(self.CONTINUE_ID, False)
-        self._tb.EnableTool(self.STEP_OUT_ID, False)
-        self._tb.EnableTool(self.NEXT_ID, False)
-        self._tb.EnableTool(self.BREAK_INTO_DEBUGGER_ID, True)
-        if _WATCHES_ON:
-            self._tb.EnableTool(self.ADD_WATCH_ID, False)
-        openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
-        for openDoc in openDocs:  
-            if(isinstance(openDoc, CodeEditor.CodeDocument)): 
-                openDoc.GetFirstView().GetCtrl().ClearCurrentLineMarkers() 
-        if self.framesTab:   
-            self.framesTab.ClearWhileRunning()
-        
+        if self._toolEnabled:
+            self._tb.EnableTool(self.STEP_ID, False)
+            self._tb.EnableTool(self.CONTINUE_ID, False)
+            self._tb.EnableTool(self.STEP_OUT_ID, False)
+            self._tb.EnableTool(self.NEXT_ID, False)
+            self._tb.EnableTool(self.BREAK_INTO_DEBUGGER_ID, True)
+    
+            if _WATCHES_ON:
+                self._tb.EnableTool(self.ADD_WATCH_ID, False)
+    
+            self.DeleteCurrentLineMarkers()
+    
+            if self.framesTab:
+                self.framesTab.ClearWhileRunning()
+
+            self._toolEnabled = False
+
     def EnableWhileDebuggerStopped(self):
         self._tb.EnableTool(self.STEP_ID, True)
         self._tb.EnableTool(self.CONTINUE_ID, True)
         self._tb.EnableTool(self.STEP_OUT_ID, True)
         self._tb.EnableTool(self.NEXT_ID, True)
+        self._tb.EnableTool(self.BREAK_INTO_DEBUGGER_ID, False)
+        self._tb.EnableTool(self.KILL_PROCESS_ID, True)
+
         if _WATCHES_ON:
             self._tb.EnableTool(self.ADD_WATCH_ID, True)
-        self._tb.EnableTool(self.BREAK_INTO_DEBUGGER_ID, False)
-    def ExecutorFinished(self):          
+
+        self._toolEnabled = True
+
+    def DisableAfterStop(self):
+        if self._toolEnabled:
+            self.DisableWhileDebuggerRunning()
+            self._tb.EnableTool(self.BREAK_INTO_DEBUGGER_ID, False)
+            self._tb.EnableTool(self.KILL_PROCESS_ID, False)
+
+    def ExecutorFinished(self):
         if _VERBOSE: print "In ExectorFinished"
-        try:          
+        try:
             self.DisableAfterStop()
         except wx._core.PyDeadObjectError:
             pass
@@ -625,15 +632,21 @@ class DebugCommandUI(wx.Panel):
         except:
             if _VERBOSE: print "In ExectorFinished, got exception"
 
-    def DisableAfterStop(self):
-        self.DisableWhileDebuggerRunning()
-        self._tb.EnableTool(self.BREAK_INTO_DEBUGGER_ID, False)
-        self._tb.EnableTool(self.KILL_PROCESS_ID, False)
+    def SetStatusText(self, text):
+        self._statusBar.SetStatusText(text,0)
+
+    def BreakExecution(self, event):
+        self._callback.BreakExecution()
+
+    def StopExecution(self, event):
+        self._callback.ShutdownServer()
+
+    def Execute(self, initialArgs, startIn, environment, onWebServer = False):
+        assert False, "Execute not overridden"
 
     def SynchCurrentLine(self, filename, lineNum, noArrow=False):
-        # FACTOR THIS INTO DocManager
         self.DeleteCurrentLineMarkers()
-        
+
         # Filename will be <string> if we're in a bit of code that was executed from
         # a string (rather than a file). I haven't been able to get the original string
         # for display.
@@ -648,7 +661,7 @@ class DebugCommandUI(wx.Panel):
             if DebuggerService.ComparePaths(openDoc.GetFilename(),filename):
                 foundView = openDoc.GetFirstView()
                 break
-                
+
         if not foundView:
             if _VERBOSE:
                 print "filename=", filename
@@ -660,96 +673,368 @@ class DebugCommandUI(wx.Panel):
             foundView.Activate()
             foundView.GotoLine(lineNum)
             startPos = foundView.PositionFromLine(lineNum)
-            
-        if not noArrow:        
+
+        if not noArrow:
             foundView.GetCtrl().MarkerAdd(lineNum -1, CodeEditor.CodeCtrl.CURRENT_LINE_MARKER_NUM)
 
     def DeleteCurrentLineMarkers(self):
         openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
-        for openDoc in openDocs:  
-            if(isinstance(openDoc, CodeEditor.CodeDocument)): 
-                openDoc.GetFirstView().GetCtrl().ClearCurrentLineMarkers()    
-        
-    def LoadFramesListXML(self, framesXML):
-        self.framesTab.LoadFramesListXML(framesXML)  
-          
-    def SetStatusText(self, text):
-        self._statusBar.SetStatusText(text,0)
-            
-    def Execute(self, initialArgs, startIn, environment):
-        self._callback.start()
-        self._executor.Execute(initialArgs, startIn, environment)
-        self._callback.waitForRPC() 
-                
-    def BreakExecution(self, event):
-        self._callback.BreakExecution()
-        
-                       
-    def StopExecution(self, event):
-        # This is a general comment on shutdown for the running and debugged processes. Basically, the
-        # current state of this is the result of trial and error coding. The common problems were memory
-        # access violations and threads that would not exit. Making the OutputReaderThreads daemons seems
-        # to have side-stepped the hung thread issue. Being very careful not to touch things after calling
-        # process.py:ProcessOpen.kill() also seems to have fixed the memory access violations, but if there
-        # were more ugliness discovered I would not be surprised. If anyone has any help/advice, please send
-        # it on to mfryer@activegrid.com.
-        if not self._stopped:
-            self._stopped = True
-            try:
-                self.DisableAfterStop()
-            except wx._core.PyDeadObjectError:
-                pass
-            try:
-                self._callback.ShutdownServer()
-            except:
-                tp,val,tb = sys.exc_info()
-                traceback.print_exception(tp, val, tb)
+        for openDoc in openDocs:
+            if(isinstance(openDoc, CodeEditor.CodeDocument)):
+                openDoc.GetFirstView().GetCtrl().ClearCurrentLineMarkers()
 
-            try:
-                self.DeleteCurrentLineMarkers()
-            except:
-                pass
-            try:
-                DebugCommandUI.ReturnPortToPool(self._debuggerPort)
-                DebugCommandUI.ReturnPortToPool(self._guiPort)
-                DebugCommandUI.ReturnPortToPool(self._debuggerBreakPort)
-            except:
-                pass
-            try:
-                if self._executor:
-                    self._executor.DoStopExecution()
-                    self._executor = None
-            except:
-                tp,val,tb = sys.exc_info()
-                traceback.print_exception(tp, val, tb)                
     def StopAndRemoveUI(self, event):
         self.StopExecution(None)
-        if self in DebugCommandUI.debuggers:
-            DebugCommandUI.debuggers.remove(self)
+        if self in BaseDebuggerUI.debuggers:
+            BaseDebuggerUI.debuggers.remove(self)
         index = self._parentNoteBook.GetSelection()
         self._parentNoteBook.GetPage(index).Show(False)
-        self._parentNoteBook.RemovePage(index)       
+        self._parentNoteBook.RemovePage(index)
 
     def OnAddWatch(self, event):
         if self.framesTab:
             self.framesTab.OnWatch(event)
-                        
+
     def MakeFramesUI(self, parent, id, debugger):
-        panel = FramesUI(parent, id, self)
-        return panel
-        
+        assert False, "MakeFramesUI not overridden"
+
     def AppendText(self, event):
         self.framesTab.AppendText(event.value)
-        
+
     def AppendErrorText(self, event):
         self.framesTab.AppendErrorText(event.value)
-        
+
     def OnClearOutput(self, event):
         self.framesTab.ClearOutput(None)
 
     def SwitchToOutputTab(self):
         self.framesTab.SwitchToOutputTab()
-        
+
+
+class PHPDebuggerUI(BaseDebuggerUI):
+    DEFAULT_LISTENER_HOST     = "127.0.0.1"
+    DEFAULT_LISTENER_PORT     = 10001
+    DEFAULT_DBG_MOD_TIMEOUT   = 300
+    DEFAULT_DBG_MAX_EXEC_TIME = 240
+    dbgSessSeqId              = 1
+
+    def __init__(self, parent, id, command, service):
+        BaseDebuggerUI.__init__(self, parent, id)
+        #Note host and port need to come out of options or a pool.
+        self._dbgHost        = PHPDebuggerUI.DEFAULT_LISTENER_HOST
+        self._dbgPort        = PHPDebuggerUI.DEFAULT_LISTENER_PORT
+        self._dbgTimeout     = PHPDebuggerUI.DEFAULT_DBG_MOD_TIMEOUT
+        self._dbgMaxExecTime = PHPDebuggerUI.DEFAULT_DBG_MAX_EXEC_TIME
+        self._dbgSessId      = None
+        self._dbgSessParam   = None
+        self._dbgPhpIniFile  = None
+        self._callback       = PHPDebugger.PHPDebuggerCallback(self, service, self._dbgHost, self._dbgPort)
+        self._executor       = Executor(command, self)
+        self._service        = service
+        self._stopped        = False
+        self._allStopped     = False
+        
+        self._createPhpDbgSess()
+
+    def showErrorDialog(self, message, title):
+        wx.MessageBox(_(message), _(title))
+        return
+
+    def _createPhpDbgSess(self):
+        currTimeStr                = str(time.time())
+        (secStr, usecStr)          = currTimeStr.split('.')
+        secLongInt                 = long(secStr)
+        usecLongInt                = long(usecStr)
+        self._dbgSessId            = "%06ld%06ld%04d" % (secLongInt, usecLongInt, PHPDebuggerUI.dbgSessSeqId)
+        PHPDebuggerUI.dbgSessSeqId = PHPDebuggerUI.dbgSessSeqId + 1
+        self._dbgSessParam         = "DBGSESSID=%s@clienthost:%d" % (self._dbgSessId, self._dbgPort)
+
+        if _VERBOSE:
+            print "phpDbgParam=%s" % self._dbgSessParam
+
+        self._service.SetPhpDbgParam(self._dbgSessParam)
+
+    def _preparePhpIniFile(self):
+        success    = False
+
+        phpCgiExec = Executor.GetPHPExecutablePath()
+        phpExec    = phpCgiExec.replace("php-cgi", "php")
+        iniPath    = self._getPhpIniFromRunningPhp(phpExec)
+
+        try:
+            iniDbgPath = os.path.normpath(iniPath + ".ag_debug_enabled")
+            dbgFile    = open(iniDbgPath, "w")
+            oriFile    = open(iniPath, "r")
+    
+            while True:
+                oneOriLine = oriFile.readline()
+                if oneOriLine == '':
+                    break
+    
+                if not oneOriLine.startswith("debugger.") and not oneOriLine.startswith("max_execution_time="):
+                    dbgFile.write(oneOriLine)
+    
+            oriFile.close()
+    
+            if _WINDOWS:
+                dbgExtFile = "php_dbg.dll"
+            else:
+                dbgExtFile = "dbg.so"
+    
+            #
+            # TODO: we should make all of these options configurable.
+            #
+            configStr = "\n; ===============================================================\n; The followings are added by ActiveGrid IDE PHP Debugger Runtime\n; ===============================================================\n\n; As we are running with the dbg module, it takes a much longer time for each script to run.\nmax_execution_time=%d\n\n[debugger]\nextension=%s\ndebugger.enabled=On\ndebugger.JIT_enabled=On\ndebugger.JIT_host=%s\ndebugger.JIT_port=%d\ndebugger.fail_silently=Off\ndebugger.timeout_seconds=%d\ndebugger.ignore_nops=Off\ndebugger.enable_session_cookie=On\ndebugger.session_nocache=On\ndebugger.profiler_enabled=Off\n" % (self._dbgMaxExecTime, dbgExtFile, self._dbgHost, self._dbgPort, self._dbgTimeout)
+            dbgFile.write(configStr)
+            dbgFile.close()
+            success = True
+        except:
+            #TODO: print stack trace.
+            print "Caught exceptions while minipulating php.ini files"
+
+        if success:
+            self._dbgPhpIniFile = iniDbgPath
+        else:
+            self._dbgPhpIniFile = None
+
+    def _getPhpIniFromRunningPhp(self, phpExec):
+        phpIniPath = None
+
+        cmdEnv  = os.environ
+        if cmdEnv.has_key('PYTHONPATH'):
+            del cmdEnv['PYTHONPATH']
+
+        cmdLine = [phpExec, "-r", "phpinfo();"]
+        phpProc = subprocess.Popen(args=cmdLine, bufsize=0, executable=None, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, preexec_fn=None, close_fds=False, shell=False, cwd=None, env=os.environ, universal_newlines=False, startupinfo=None, creationflags=0)
+        phpOutput = phpProc.stdout
+
+        phpIniPattern = "Configuration File (php.ini) Path => "
+        while True:
+            oneLine = phpOutput.readline()
+            if oneLine == '':
+                break
+
+            if oneLine.startswith(phpIniPattern):
+                if oneLine.endswith("\n"):
+                    endIndex   = oneLine.index("\n")
+                    phpIniPath = oneLine[len(phpIniPattern):endIndex]
+                else:
+                    phpIniPath = oneLine[len(phpIniPattern):]
+
+                if phpIniPath and len(phpIniPath) > 0:
+                    phpIniPath = os.path.normpath(phpIniPath)
+                    break
+
+        phpOutput.close()
+
+        if _VERBOSE:
+            print "php.ini path is: %s" % repr(phpIniPath)
+
+        return phpIniPath
+
+    def Execute(self, initialArgs, startIn, environment, onWebServer = False):
+        self._preparePhpIniFile()
+        self._callback.Start()
+
+        if not onWebServer:
+            if self._dbgPhpIniFile:
+                args = '-c "' + self._dbgPhpIniFile + '" ' + initialArgs
+            else:
+                args = initialArgs
+
+            self._executor.Execute(args, startIn, environment)
+
+    def StopExecution(self, event):
+        # This is a general comment on shutdown for the running and debugged processes. Basically, the
+        # current state of this is the result of trial and error coding. The common problems were memory
+        # access violations and threads that would not exit. Making the OutputReaderThreads daemons seems
+        # to have side-stepped the hung thread issue. Being very careful not to touch things after calling
+        # process.py:ProcessOpen.kill() also seems to have fixed the memory access violations, but if there
+        # were more ugliness discovered I would not be surprised. If anyone has any help/advice, please send
+        # it on to mfryer@activegrid.com.
+        if not self._allStopped:
+            self._stopped = True
+            try:
+                self.DisableAfterStop()
+            except wx._core.PyDeadObjectError:
+                pass
+
+            try:
+                #
+                # If this is called by clicking the "Stop" button, we only stop
+                # the current running php script, and keep the listener
+                # running.
+                #
+                if event:
+                    self._callback.ShutdownServer(stopLsnr = False)
+                else:
+                    self._callback.ShutdownServer(stopLsnr = True)
+                    self._allStopped = True
+            except:
+                tp,val,tb = sys.exc_info()
+                traceback.print_exception(tp, val, tb)
+
+            try:
+                self.DeleteCurrentLineMarkers()
+            except:
+                pass
+
+            try:
+                if self._executor:
+                    self._executor.DoStopExecution()
+                    self._executor = None
+            except:
+                tp,val,tb = sys.exc_info()
+                traceback.print_exception(tp, val, tb)
+
+    def MakeFramesUI(self, parent, id, debugger):
+        return PHPFramesUI(parent, id, self)
+
+    def LoadPHPFramesList(self, stackList):
+        self.framesTab.LoadFramesList(stackList)
+
+    #
+    # TODO: this is a hack to overwrite BaseDebuggerUI's function.  The purpose
+    # is to always push breakpoints no matter if a php is running or not.  If
+    # no php is running, an exception will be thrown and handled like nothing
+    # happened.
+    #
+    def BreakPointChange(self):
+        self._callback.PushBreakpoints()
+        self.framesTab.PopulateBPList()
+
+
+class PythonDebuggerUI(BaseDebuggerUI):
+    debuggerPortList = None
+
+    def GetAvailablePort():
+        for index in range( 0, len(PythonDebuggerUI.debuggerPortList)):
+            port = PythonDebuggerUI.debuggerPortList[index]
+            if PythonDebuggerUI.PortAvailable(port):
+                PythonDebuggerUI.debuggerPortList.pop(index)
+                return port
+        wx.MessageBox(_("Out of ports for debugging!  Please restart the application builder.\nIf that does not work, check for and remove running instances of python."), _("Out of Ports"))
+        assert False, "Out of ports for debugger."
+
+    GetAvailablePort = staticmethod(GetAvailablePort)
+
+    def ReturnPortToPool(port):
+        config = wx.ConfigBase_Get()
+        startingPort = config.ReadInt("DebuggerStartingPort", DEFAULT_PORT)
+        val = int(startingPort) + int(PORT_COUNT)
+        if int(port) >= startingPort and (int(port) <= val):
+            PythonDebuggerUI.debuggerPortList.append(int(port))
+
+    ReturnPortToPool = staticmethod(ReturnPortToPool)
+
+    def PortAvailable(port):
+        config = wx.ConfigBase_Get()
+        hostname = config.Read("DebuggerHostName", DEFAULT_HOST)
+        try:
+            server = AGXMLRPCServer((hostname, port))
+            server.server_close()
+            if _VERBOSE: print "Port ", str(port), " available."
+            return True
+        except:
+            tp,val,tb = sys.exc_info()
+            if _VERBOSE: traceback.print_exception(tp, val, tb)
+            if _VERBOSE: print "Port ", str(port), " unavailable."
+            return False
+
+    PortAvailable = staticmethod(PortAvailable)
+
+    def NewPortRange():
+        config = wx.ConfigBase_Get()
+        startingPort = config.ReadInt("DebuggerStartingPort", DEFAULT_PORT)
+        PythonDebuggerUI.debuggerPortList = range(startingPort, startingPort + PORT_COUNT)
+    NewPortRange = staticmethod(NewPortRange)
+
+    def __init__(self, parent, id, command, service, autoContinue=True):
+        # Check for ports before creating the panel.
+        if not PythonDebuggerUI.debuggerPortList:
+            PythonDebuggerUI.NewPortRange()
+        self._debuggerPort = str(PythonDebuggerUI.GetAvailablePort())
+        self._guiPort = str(PythonDebuggerUI.GetAvailablePort())
+        self._debuggerBreakPort = str(PythonDebuggerUI.GetAvailablePort())
+        BaseDebuggerUI.__init__(self, parent, id)
+        self._command = command
+        self._service = service
+        config = wx.ConfigBase_Get()
+        self._debuggerHost = self._guiHost = config.Read("DebuggerHostName", DEFAULT_HOST)
+
+        url = 'http://' + self._debuggerHost + ':' + self._debuggerPort + '/'
+        self._breakURL = 'http://' + self._debuggerHost + ':' + self._debuggerBreakPort + '/'
+        self._callback = PythonDebuggerCallback(self._guiHost, self._guiPort, url, self._breakURL, self, autoContinue)
+        if DebuggerHarness.__file__.find('library.zip') > 0:
+            try:
+                fname = DebuggerHarness.__file__
+                parts = fname.split('library.zip')
+                path = os.path.join(parts[0],'activegrid', 'tool', 'DebuggerHarness.py')
+            except:
+                tp, val, tb = sys.exc_info()
+                traceback.print_exception(tp, val, tb)
+
+        else:
+            print "Starting debugger on these ports: %s, %s, %s" % (str(self._debuggerPort) , str(self._guiPort) , str(self._debuggerBreakPort))
+            path = DebuggerService.ExpandPath(DebuggerHarness.__file__)
+        self._executor = Executor(path, self, self._debuggerHost, \
+                                                self._debuggerPort, self._debuggerBreakPort, self._guiHost, self._guiPort, self._command, callbackOnExit=self.ExecutorFinished)
+
+        self._stopped = False
+
+    def LoadPythonFramesList(self, framesXML):
+        self.framesTab.LoadFramesList(framesXML)
+
+    def Execute(self, initialArgs, startIn, environment, onWebServer = False):
+        self._callback.Start()
+        self._executor.Execute(initialArgs, startIn, environment)
+        self._callback.WaitForRPC()
+
+
+    def StopExecution(self, event):
+        # This is a general comment on shutdown for the running and debugged processes. Basically, the
+        # current state of this is the result of trial and error coding. The common problems were memory
+        # access violations and threads that would not exit. Making the OutputReaderThreads daemons seems
+        # to have side-stepped the hung thread issue. Being very careful not to touch things after calling
+        # process.py:ProcessOpen.kill() also seems to have fixed the memory access violations, but if there
+        # were more ugliness discovered I would not be surprised. If anyone has any help/advice, please send
+        # it on to mfryer@activegrid.com.
+        if not self._stopped:
+            self._stopped = True
+            try:
+                self.DisableAfterStop()
+            except wx._core.PyDeadObjectError:
+                pass
+            try:
+                self._callback.ShutdownServer()
+            except:
+                tp,val,tb = sys.exc_info()
+                traceback.print_exception(tp, val, tb)
+
+            try:
+                self.DeleteCurrentLineMarkers()
+            except:
+                pass
+            try:
+                PythonDebuggerUI.ReturnPortToPool(self._debuggerPort)
+                PythonDebuggerUI.ReturnPortToPool(self._guiPort)
+                PythonDebuggerUI.ReturnPortToPool(self._debuggerBreakPort)
+            except:
+                pass
+            try:
+                if self._executor:
+                    self._executor.DoStopExecution()
+                    self._executor = None
+            except:
+                tp,val,tb = sys.exc_info()
+                traceback.print_exception(tp, val, tb)
+
+
+    def MakeFramesUI(self, parent, id, debugger):
+        panel = PythonFramesUI(parent, id, self)
+        return panel
+
+
 class BreakpointsUI(wx.Panel):
     def __init__(self, parent, id, ui):
         wx.Panel.__init__(self, parent, id)
@@ -763,19 +1048,19 @@ class BreakpointsUI(wx.Panel):
         p1 = self
         self._bpListCtrl = wx.ListCtrl(p1, -1, pos=wx.DefaultPosition, size=(1000,1000), style=wx.LC_REPORT)
         sizer.Add(self._bpListCtrl, 1, wx.ALIGN_LEFT|wx.ALL|wx.EXPAND, 1)
-        self._bpListCtrl.InsertColumn(0, "File")   
-        self._bpListCtrl.InsertColumn(1, "Line")   
-        self._bpListCtrl.InsertColumn(2, "Path")   
+        self._bpListCtrl.InsertColumn(0, "File")
+        self._bpListCtrl.InsertColumn(1, "Line")
+        self._bpListCtrl.InsertColumn(2, "Path")
         self._bpListCtrl.SetColumnWidth(0, 150)
         self._bpListCtrl.SetColumnWidth(1, 50)
         self._bpListCtrl.SetColumnWidth(2, 450)
         self._bpListCtrl.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self.OnListRightClick)
         self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.ListItemSelected, self._bpListCtrl)
         self.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.ListItemDeselected, self._bpListCtrl)
-        
+
         def OnLeftDoubleClick(event):
             self.SyncBPLine(event)
-            
+
         wx.EVT_LEFT_DCLICK(self._bpListCtrl, OnLeftDoubleClick)
 
         self.PopulateBPList()
@@ -783,7 +1068,7 @@ class BreakpointsUI(wx.Panel):
         p1.SetSizer(sizer)
         sizer.Fit(p1)
         p1.Layout()
-        
+
     def PopulateBPList(self):
         list = self._bpListCtrl
         list.DeleteAllItems()
@@ -798,7 +1083,7 @@ class BreakpointsUI(wx.Panel):
                     list.InsertStringItem(index, shortFile)
                     list.SetStringItem(index, 1, str(line))
                     list.SetStringItem(index, 2, fileName)
-                    
+
     def OnListRightClick(self, event):
         menu = wx.Menu()
         item = wx.MenuItem(menu, self.clearBPID, "Clear Breakpoint")
@@ -813,32 +1098,32 @@ class BreakpointsUI(wx.Panel):
             list = self._bpListCtrl
             fileName = list.GetItem(self.currentItem, 2).GetText()
             lineNumber = list.GetItem(self.currentItem, 1).GetText()
-            self._ui.SynchCurrentLine( fileName, int(lineNumber) , noArrow=True)  
+            self._ui.SynchCurrentLine( fileName, int(lineNumber) , noArrow=True)
 
     def ClearBreakPoint(self, event):
         if self.currentItem >= 0:
             list = self._bpListCtrl
             fileName = list.GetItem(self.currentItem, 2).GetText()
             lineNumber = list.GetItem(self.currentItem, 1).GetText()
-            wx.GetApp().GetService(DebuggerService).OnToggleBreakpoint(None, line=int(lineNumber) -1, fileName=fileName )  
-        
+            wx.GetApp().GetService(DebuggerService).OnToggleBreakpoint(None, line=int(lineNumber) -1, fileName=fileName )
+
     def ListItemSelected(self, event):
         self.currentItem = event.m_itemIndex
 
     def ListItemDeselected(self, event):
         self.currentItem = -1
+
 class Watch:
     CODE_ALL_FRAMES = 1
     CODE_THIS_BLOCK = 2
     CODE_THIS_LINE = 4
     CODE_RUN_ONCE = 8
-    
+
     def __init__(self, name, command, show_code=CODE_ALL_FRAMES):
         self._name = name
-        self._command = command 
+        self._command = command
         self._show_code = show_code
-               
+
 class WatchDialog(wx.Dialog):
     WATCH_ALL_FRAMES = "Watch in all frames"
     WATCH_THIS_FRAME = "Watch in this frame only"
@@ -865,22 +1150,22 @@ class WatchDialog(wx.Dialog):
                 return
             self.EndModal(wx.ID_OK)
         self.Bind(wx.EVT_BUTTON, OnOkClick, self._okButton)
-            
+
         self._cancelButton = wx.Button(self, wx.ID_CANCEL, _("Cancel"))
         self._cancelButton.SetHelpText(_("The Cancel button cancels the dialog."))
-        
+
         self.__set_properties()
         self.__do_layout()
-        
+
     def GetSettings(self):
         return self._watchNameTextCtrl.GetValue(), self._watchValueTextCtrl.GetValue(), self.GetSendFrame(), self.GetRunOnce()
-    
+
     def GetSendFrame(self):
         return (WatchDialog.WATCH_ALL_FRAMES != self.radio_box_1.GetStringSelection())
-        
+
     def GetRunOnce(self):
         return (WatchDialog.WATCH_ONCE == self.radio_box_1.GetStringSelection())
-        
+
     def __set_properties(self):
         self.SetTitle("Add a Watch")
         #self.SetSize((400, 250))
@@ -900,15 +1185,15 @@ class WatchDialog(wx.Dialog):
         grid_sizer_4.Add(self.label_4, 0, wx.ALIGN_CENTER_VERTICAL|wx.FIXED_MINSIZE, 0)
         sizer_1.Add(grid_sizer_4, 0, wx.EXPAND, 0)
         sizer_1.Add(self.radio_box_1, 0, wx.EXPAND, 0)
-        
+
         box = wx.BoxSizer(wx.HORIZONTAL)
         box.Add(self._okButton, 0, wx.ALIGN_RIGHT|wx.ALL, 5)
         box.Add(self._cancelButton, 0, wx.ALIGN_RIGHT|wx.ALL, 5)
         sizer_1.Add(box, 1, wx.EXPAND, 0)
         self.SetSizer(sizer_1)
         self.Layout()
-        
-class FramesUI(wx.SplitterWindow):
+
+class BaseFramesUI(wx.SplitterWindow):
     def __init__(self, parent, id, ui):
         wx.SplitterWindow.__init__(self, parent, id, style = wx.SP_3D)
         self._ui = ui
@@ -917,7 +1202,7 @@ class FramesUI(wx.SplitterWindow):
         sizer = wx.BoxSizer(wx.HORIZONTAL)
         framesLabel = wx.StaticText(self, -1, "Stack Frame:")
         sizer.Add(framesLabel, 0, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT|wx.LEFT, border=2)
-                               
+
         self._framesChoiceCtrl = wx.Choice(p1, -1, choices=["                                           "])
         sizer.Add(self._framesChoiceCtrl, 1, wx.ALIGN_LEFT|wx.ALL|wx.EXPAND, 1)
         self._framesChoiceCtrl.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self.OnListRightClick)
@@ -925,21 +1210,20 @@ class FramesUI(wx.SplitterWindow):
 
         sizer2 = wx.BoxSizer(wx.VERTICAL)
         p1.SetSizer(sizer2)
-        
         self._treeCtrl = wx.gizmos.TreeListCtrl(p1, -1, style=wx.TR_DEFAULT_STYLE| wx.TR_FULL_ROW_HIGHLIGHT)
         self._treeCtrl.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK, self.OnRightClick)
         sizer2.Add(sizer, 0, wx.ALIGN_LEFT|wx.ALL|wx.EXPAND, 1)
         sizer2.Add(self._treeCtrl,1, wx.ALIGN_LEFT|wx.ALL|wx.EXPAND, 1)
-        tree = self._treeCtrl                           
+        tree = self._treeCtrl
         tree.AddColumn("Thing")
         tree.AddColumn("Value")
         tree.SetMainColumn(0) # the one with the tree in it...
         tree.SetColumnWidth(0, 175)
         tree.SetColumnWidth(1, 355)
         self._root = tree.AddRoot("Frame")
+        tree.SetPyData(self._root, "root")
         tree.SetItemText(self._root, "", 1)
         tree.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.IntrospectCallback)
-
         self._p2 = p2 = wx.Window(self, -1)
         sizer3 = wx.BoxSizer(wx.HORIZONTAL)
         p2.SetSizer(sizer3)
@@ -953,18 +1237,59 @@ class FramesUI(wx.SplitterWindow):
         self._notebook.AddPage(self.consoleTab, "Output")
         self._notebook.AddPage(self.inspectConsoleTab, "Interact")
         self._notebook.AddPage(self.breakPointsTab, "Break Points")
-
         self.SetMinimumPaneSize(20)
         self.SplitVertically(p1, p2, 550)
         self.currentItem = None
         self._notebook.Show(True)
-   
+
     def PopulateBPList(self):
         self.breakPointsTab.PopulateBPList()
-        
+
     def OnSize(self, event):
         self._notebook.SetSize(self._p2.GetSize())
-             
+
+    def OnDoubleClick(self, event):
+        # Looking for a stack trace line.
+        lineText, pos = self._textCtrl.GetCurLine()
+        fileBegin = lineText.find("File \"")
+        fileEnd = lineText.find("\", line ")
+        lineEnd = lineText.find(", in ")
+        if lineText == "\n" or  fileBegin == -1 or fileEnd == -1 or lineEnd == -1:
+            # Check the line before the one that was clicked on
+            lineNumber = self._textCtrl.GetCurrentLine()
+            if(lineNumber == 0):
+                return
+            lineText = self._textCtrl.GetLine(lineNumber - 1)
+            fileBegin = lineText.find("File \"")
+            fileEnd = lineText.find("\", line ")
+            lineEnd = lineText.find(", in ")
+            if lineText == "\n" or  fileBegin == -1 or fileEnd == -1 or lineEnd == -1:
+                return
+
+        filename = lineText[fileBegin + 6:fileEnd]
+        lineNum = int(lineText[fileEnd + 8:lineEnd])
+
+        foundView = None
+        openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
+        for openDoc in openDocs:
+            if openDoc.GetFilename() == filename:
+                foundView = openDoc.GetFirstView()
+                break
+
+        if not foundView:
+            doc = wx.GetApp().GetDocumentManager().CreateDocument(filename, wx.lib.docview.DOC_SILENT)
+            foundView = doc.GetFirstView()
+
+        if foundView:
+            foundView.GetFrame().SetFocus()
+            foundView.Activate()
+            foundView.GotoLine(lineNum)
+            startPos = foundView.PositionFromLine(lineNum)
+            lineText = foundView.GetCtrl().GetLine(lineNum - 1)
+            foundView.SetSelection(startPos, startPos + len(lineText.rstrip("\n")))
+            import OutlineService
+            wx.GetApp().GetService(OutlineService.OutlineService).LoadOutline(foundView, position=startPos)
+
     def MakeConsoleTab(self, parent, id):
         panel = wx.Panel(parent, id)
         sizer = wx.BoxSizer(wx.HORIZONTAL)
@@ -979,92 +1304,312 @@ class FramesUI(wx.SplitterWindow):
         self._textCtrl.SetFont(wx.Font(9, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName = font))
         self._textCtrl.SetFontColor(wx.BLACK)
         self._textCtrl.StyleClearAll()
+        wx.stc.EVT_STC_DOUBLECLICK(self._textCtrl, self._textCtrl.GetId(), self.OnDoubleClick)
+
         panel.SetSizer(sizer)
         #sizer.Fit(panel)
 
         return panel
-
-    def ReplaceLastLine(self, command):
-        line = self._interCtrl.GetLineCount() - 1
-        self._interCtrl.GotoLine(line)
-        start = self._interCtrl.GetCurrentPos()
-        self._interCtrl.SetTargetStart(start)
-        end = self._interCtrl.GetLineEndPosition(line)
-        self._interCtrl.SetTargetEnd(end)
-        self._interCtrl.ReplaceTarget(">>> " + command)
-        self._interCtrl.GotoLine(line)
-        self._interCtrl.SetSelectionStart(self._interCtrl.GetLineEndPosition(line))
-
     def ExecuteCommand(self, command):
-        if not len(self.command_list) or not command == self.command_list[len(self.command_list) -1]:
-            self.command_list.append(command)
-            self.command_index = len(self.command_list) - 1
-        retval = self._ui._callback._debuggerServer.execute_in_frame(self._framesChoiceCtrl.GetStringSelection(), command)
-        self._interCtrl.AddText("\n" + str(retval))
-        self._interCtrl.ScrollToLine(self._interCtrl.GetLineCount())
-        # Refresh the tree view in case this command resulted in changes there. TODO: Need to reopen tree items.
-        self.PopulateTreeFromFrameMessage(self._framesChoiceCtrl.GetStringSelection())
-        
+        assert False, "ExecuteCommand not overridden"
+
     def MakeInspectConsoleTab(self, parent, id):
-        self.command_list = []
-        self.command_index = 0
-            
+        def handleCommand():
+            cmdStr = self._cmdInput.GetValue()
+            if cmdStr:
+                self._cmdList.append(cmdStr)
+                self._cmdIndex = len(self._cmdList)
+            self._cmdInput.Clear()
+            self._cmdOutput.SetDefaultStyle(style=self._cmdOutputTextStyle)
+            self._cmdOutput.AppendText(">>> " + cmdStr + "\n")
+            self._cmdOutput.SetDefaultStyle(style=self._defaultOutputTextStyle)
+            self.ExecuteCommand(cmdStr)
+            return
+
+        def OnCmdButtonPressed(event):
+            handleCommand()
+            return
+
         def OnKeyPressed(event):
             key = event.KeyCode()
-            if key == wx.WXK_DELETE or key == wx.WXK_BACK:
-                if self._interCtrl.GetLine(self._interCtrl.GetCurrentLine()) == ">>> ":
-                    return
-            elif key == wx.WXK_RETURN:
-                command = self._interCtrl.GetLine(self._interCtrl.GetCurrentLine())[4:]
-                self.ExecuteCommand(command)             
-                self._interCtrl.AddText("\n>>> ")
-                return
+            if key == wx.WXK_RETURN:
+                handleCommand()
             elif key == wx.WXK_UP:
-                if not len(self.command_list):
+                if len(self._cmdList) < 1 or self._cmdIndex < 1:
                     return
-                self.ReplaceLastLine(self.command_list[self.command_index])
-                if self.command_index  == 0:
-                    self.command_index  = len(self.command_list) - 1
-                else:
-                    self.command_index = self.command_index - 1
-                return
+
+                self._cmdInput.Clear()
+                self._cmdInput.AppendText(self._cmdList[self._cmdIndex - 1])
+                self._cmdIndex = self._cmdIndex - 1
             elif key == wx.WXK_DOWN:
-                if not len(self.command_list):
+                if len(self._cmdList) < 1 or self._cmdIndex >= len(self._cmdList):
                     return
-                if self.command_index  < len(self.command_list) - 1:
-                    self.command_index = self.command_index + 1
-                else:
-                    self.command_index = 0
-                self.ReplaceLastLine(self.command_list[self.command_index])                        
-                return
-            event.Skip()
 
-        try:    
-            panel = wx.Panel(parent, id)
-            sizer = wx.BoxSizer(wx.HORIZONTAL)
-            self._interCtrl = STCTextEditor.TextCtrl(panel, wx.NewId())
-            sizer.Add(self._interCtrl, 1, wx.ALIGN_LEFT|wx.ALL|wx.EXPAND, 2)
-            self._interCtrl.SetViewLineNumbers(False)
-            if wx.Platform == '__WXMSW__':
-                font = "Courier New"
+                self._cmdInput.Clear()
+                self._cmdInput.AppendText(self._cmdList[self._cmdIndex - 1])
+                self._cmdIndex = self._cmdIndex + 1
             else:
-                font = "Courier"
-            self._interCtrl.SetFont(wx.Font(9, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName = font))
-            self._interCtrl.SetFontColor(wx.BLACK)
-            self._interCtrl.StyleClearAll()
-            wx.EVT_KEY_DOWN(self._interCtrl, OnKeyPressed)
-            self._interCtrl.AddText(">>> ")
-            panel.SetSizer(sizer)
-        except:
-            tp, val, tb = sys.exc_info()
-            traceback.print_exception(tp, val, tb)   
-            
-        return panel 
-               
+                event.Skip()
+            return
+
+        def OnClrButtonPressed(event):
+            self._cmdOutput.Clear()
+
+        panel           = wx.Panel(parent, id)
+
+        cmdLabel        = wx.StaticText(panel, -1, "Cmd: ")
+        self._cmdInput  = wx.TextCtrl(panel)
+        cmdButton       = wx.Button(panel, label="Execute")
+        clrButton       = wx.Button(panel, label="Clear")
+        self._cmdOutput = wx.TextCtrl(panel, style=wx.TE_MULTILINE | wx.HSCROLL | wx.TE_READONLY | wx.TE_RICH2)
+
+        hbox            = wx.BoxSizer()
+        hbox.Add(cmdLabel, proportion=0, flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL)
+        hbox.Add(self._cmdInput, proportion=1, flag=wx.EXPAND)
+        hbox.Add(cmdButton, proportion=0, flag=wx.RIGHT)
+        hbox.Add(clrButton, proportion=0, flag=wx.RIGHT)
+
+        vbox            = wx.BoxSizer(wx.VERTICAL)
+        vbox.Add(hbox, proportion=0, flag=wx.EXPAND | wx.ALL, border=2)
+        vbox.Add(self._cmdOutput, proportion=1, flag=wx.EXPAND | wx.LEFT, border=2)
+
+        panel.SetSizer(vbox)
+        cmdButton.Bind(wx.EVT_BUTTON, OnCmdButtonPressed)
+        clrButton.Bind(wx.EVT_BUTTON, OnClrButtonPressed)
+        wx.EVT_KEY_DOWN(self._cmdInput, OnKeyPressed)
+
+        fixedFont = wx.Font(self._cmdInput.GetFont().GetPointSize(), family=wx.TELETYPE, style=wx.NORMAL, weight=wx.NORMAL)
+        self._defaultOutputTextStyle = wx.TextAttr("BLACK", wx.NullColour, fixedFont)
+        self._cmdOutputTextStyle = wx.TextAttr("RED", wx.NullColour, fixedFont)
+        self._cmdOutput.SetDefaultStyle(style=self._defaultOutputTextStyle)
+        self._cmdList  = []
+        self._cmdIndex = 0
+
+        panel.Show()
+        return panel
+
     def MakeBreakPointsTab(self, parent, id):
         panel = BreakpointsUI(parent, id, self._ui)
         return panel
-     
+
+    def OnRightClick(self, event):
+        assert False, "OnRightClick not overridden"
+
+    def ClearWhileRunning(self):
+        list = self._framesChoiceCtrl
+        list.Clear()
+        list.Enable(False)
+        tree = self._treeCtrl
+        root = self._root
+        tree.DeleteChildren(root)
+        self._cmdInput.Enable(False)
+        self._cmdOutput.Enable(False)
+
+    def OnListRightClick(self, event):
+        if not hasattr(self, "syncFrameID"):
+            self.syncFrameID = wx.NewId()
+            self.Bind(wx.EVT_MENU, self.OnSyncFrame, id=self.syncFrameID)
+        menu = wx.Menu()
+        item = wx.MenuItem(menu, self.syncFrameID, "Goto Source Line")
+        menu.AppendItem(item)
+        self.PopupMenu(menu, event.GetPosition())
+        menu.Destroy()
+
+    def OnSyncFrame(self, event):
+        assert False, "OnSyncFrame not overridden"
+
+    def LoadFramesList(self, framesXML):
+        assert False, "LoadFramesList not overridden"
+
+    def ListItemSelected(self, event):
+        assert False, "ListItemSelected not overridden"
+
+    def PopulateTreeFromFrameMessage(self, message):
+        assert False, "PopulateTreeFromFrameMessage not overridden"
+
+    def IntrospectCallback(self, event):
+        assert False, "IntrospectCallback not overridden"
+
+    def AppendText(self, text):
+        self._textCtrl.SetReadOnly(False)
+        self._textCtrl.AddText(text)
+        self._textCtrl.ScrollToLine(self._textCtrl.GetLineCount())
+        self._textCtrl.SetReadOnly(True)
+
+    def AppendErrorText(self, text):
+        self._textCtrl.SetReadOnly(False)
+        self._textCtrl.SetFontColor(wx.RED)
+        self._textCtrl.StyleClearAll()
+        self._textCtrl.AddText(text)
+        self._textCtrl.ScrollToLine(self._textCtrl.GetLineCount())
+        self._textCtrl.SetFontColor(wx.BLACK)
+        self._textCtrl.StyleClearAll()
+        self._textCtrl.SetReadOnly(True)
+
+    def ClearOutput(self, event):
+        self._textCtrl.SetReadOnly(False)
+        self._textCtrl.ClearAll()
+        self._textCtrl.SetReadOnly(True)
+
+    def SwitchToOutputTab(self):
+        self._notebook.SetSelection(0)
+
+class PHPFramesUI(BaseFramesUI):
+    def __init__(self, parent, id, ui):
+        BaseFramesUI.__init__(self, parent, id, ui)
+
+    def LoadFramesList(self, stackList):
+        wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
+
+        self._cmdInput.Enable(True)
+        self._cmdOutput.Enable(True)
+        index       = 0
+        self._stack = stackList
+        list        = self._framesChoiceCtrl
+        list.Clear()
+
+        if len(stackList) > 0:
+            self._displayVariableTreeRootNode()
+
+            for stackFrame in stackList:
+                message = stackFrame.getDisplayStr(stackList)
+                list.Append(message)
+
+            self.currentItem = index
+            list.SetSelection(index)
+            list.Enable(True)
+            self.OnSyncFrame(None)
+            self._p1.FitInside()
+
+        wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
+
+    def PopulateTreeFromStackFrame(self, frameNode):
+        vars         = frameNode.getVariables()
+        tree         = self._treeCtrl
+        rootTreeNode = self._root
+
+        #
+        # Build a new sub variable tree from the root node.
+        #
+        tree.DeleteChildren(rootTreeNode)
+        for var in vars:
+            aTreeNode = self.AppendSubTreeFromAVariable(tree, var, rootTreeNode)
+
+        #
+        # No need to expand the node here, as the IntrospectCallback() has
+        # already called it.
+        #
+        self._p2.FitInside()
+
+    def AppendSubTreeFromAVariable(self, tree, var, parentTreeNode, previousOne = None):
+        varName     = var.getName()
+        varValueStr = var.getValueString()
+
+        #
+        # If previously we already have this item in the tree, replace it.
+        # Otherwise, insert a new one.
+        #
+        if previousOne:
+            newNode = tree.InsertItem(parentTreeNode, previousOne, varName)
+        else:
+            newNode = tree.AppendItem(parentTreeNode, varName)
+
+        #
+        # Associate this variable object with this newNode.
+        #
+        tree.SetPyData(newNode, var)
+
+        #
+        # Set this variable's value string (for displaying).
+        #
+        if varValueStr and len(varValueStr) > 0:
+            tree.SetItemText(newNode, varValueStr, 1)
+
+        #
+        # If this variable has child variables, recursively build the sub
+        # variable tree.
+        #
+        if var.hasChildren():
+            childrenVarList = var.getChildrenVariables()
+            for childVar in childrenVarList:
+                self.AppendSubTreeFromAVariable(tree, childVar, newNode)
+
+            #
+            # If its child variables are sortable, sort it.
+            #
+            if var.childrenIsSortable():
+                tree.SortChildren(newNode)
+
+        return newNode
+
+    def IntrospectCallback(self, event):
+        tree = self._treeCtrl
+        item = event.GetItem()
+
+        #
+        # Only when the introspection happens to root, we get the whole
+        # variable tree.  For all the individual nodes, we have populated
+        # the subtree already, so don't need to do anything.
+        #
+        if tree.GetPyData(item) == "root" and self._stack and self._stack[self.currentItem]:
+            stackFrame = self._stack[self.currentItem]
+            self.PopulateTreeFromStackFrame(stackFrame)
+
+        event.Skip()
+
+    def OnSyncFrame(self, event):
+        stackFrame = self._stack[self.currentItem]
+        fileName   = stackFrame.getFileName()
+        lineNo     = stackFrame.getLineNo()
+        if _VERBOSE:
+            print "OnSyncFrame(): about to sync: fileName: %s, lineNo: %d" % (fileName, lineNo)
+        self._ui.SynchCurrentLine(fileName, lineNo)
+        if _VERBOSE:
+            print "OnSyncFrame(): sync done"
+
+    def ListItemSelected(self, event):
+        selectedStackFrameStr = event.GetString()
+
+        if not self._stack or len(self._stack) < 1:
+            return
+
+        found = False
+        for stackFrame in self._stack:
+            if stackFrame.getDisplayStr() == selectedStackFrameStr:
+                self.currentItem = stackFrame.getFrameIndex()
+                found = True
+                break
+
+        if found:
+            self._displayVariableTreeRootNode()
+            self.OnSyncFrame(None)
+
+        return
+
+    def _displayVariableTreeRootNode(self):
+        #
+        # Add a dummy item to rootTreeNode so that it will be shown as
+        # expandable.  Only do real tree population on the fly when the
+        # rootTreeNode is expanded in OnIntrospection().
+        #
+        tree         = self._treeCtrl
+        rootTreeNode = self._root
+        dummyNode    = tree.AppendItem(rootTreeNode, "dummy")
+        tree.Collapse(rootTreeNode)
+
+        return
+
+
+class PythonFramesUI(BaseFramesUI):
+    def __init__(self, parent, id, ui):
+        BaseFramesUI.__init__(self, parent, id, ui)
+
+    def ExecuteCommand(self, command):
+        retval = self._ui._callback._debuggerServer.execute_in_frame(self._framesChoiceCtrl.GetStringSelection(), command)
+        self._cmdOutput.AppendText(str(retval) + "\n")
+        # Refresh the tree view in case this command resulted in changes there. TODO: Need to reopen tree items.
+        self.PopulateTreeFromFrameMessage(self._framesChoiceCtrl.GetStringSelection())
+
     def OnRightClick(self, event):
         #Refactor this...
         self._introspectItem = event.GetItem()
@@ -1098,7 +1643,7 @@ class FramesUI(wx.SplitterWindow):
         menu.Destroy()
         self._parentChain = None
         self._introspectItem = None
-        
+
     def GetItemChain(self, item):
         parentChain = []
         if item:
@@ -1110,18 +1655,18 @@ class FramesUI(wx.SplitterWindow):
                 item = self._treeCtrl.GetItemParent(item)
             parentChain.reverse()
         return parentChain
-    
+
     def OnView(self, event):
         title = self._treeCtrl.GetItemText(self._introspectItem,0)
         value = self._treeCtrl.GetItemText(self._introspectItem,1)
         dlg = wx.lib.dialogs.ScrolledMessageDialog(self, value, title, style=wx.DD_DEFAULT_STYLE | wx.RESIZE_BORDER)
         dlg.Show()
-        
+
     def OnSendToInteract(self, event):
         value = ""
         prevItem = ""
         for item in self._parentChain:
-            
+
             if item.find(prevItem + '[') != -1:
                value += item[item.find('['):]
                continue
@@ -1133,9 +1678,8 @@ class FramesUI(wx.SplitterWindow):
                 value += item
                 prevItem = item
         print value
-        self.ReplaceLastLine(value)
         self.ExecuteCommand(value)
-        
+
     def OnWatch(self, event):
         try:
             if hasattr(self, '_parentChain'):
@@ -1147,7 +1691,7 @@ class FramesUI(wx.SplitterWindow):
                 name, text, send_frame, run_once = wd.GetSettings()
                 if send_frame:
                     frameNode = self._stack[int(self.currentItem)]
-                    message = frameNode.getAttribute("message") 
+                    message = frameNode.getAttribute("message")
                 else:
                     message = ""
                 binType = self._ui._callback._debuggerServer.add_watch(name, text, message, run_once)
@@ -1159,106 +1703,90 @@ class FramesUI(wx.SplitterWindow):
             wd.Destroy()
         except:
             tp, val, tb = sys.exc_info()
-            traceback.print_exception(tp, val, tb)   
-            
+            traceback.print_exception(tp, val, tb)
+
     def OnIntrospect(self, event):
         wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
 
         try:
-            list = self._framesChoiceCtrl
-            frameNode = self._stack[int(self.currentItem)]
-            message = frameNode.getAttribute("message") 
-            binType = self._ui._callback._debuggerServer.attempt_introspection(message, self._parentChain)
-            xmldoc = bz2.decompress(binType.data)
-            domDoc = parseString(xmldoc)
-            #wx.MessageBox(xmldoc, "result of introspection")
-            nodeList = domDoc.getElementsByTagName('replacement')
-            replacementNode = nodeList.item(0)
-            if len(replacementNode.childNodes):
-                thingToWalk = replacementNode.childNodes.item(0)
-                tree = self._treeCtrl
-                parent = tree.GetItemParent(self._introspectItem)
-                treeNode = self.AppendSubTreeFromNode(thingToWalk, thingToWalk.getAttribute('name'), parent, insertBefore=self._introspectItem)
-                if thingToWalk.getAttribute('name').find('[') == -1:
-                    self._treeCtrl.SortChildren(treeNode)
-                self._treeCtrl.Expand(treeNode)
-                tree.Delete(self._introspectItem)
-        except:
-            tp,val,tb = sys.exc_info()
-            traceback.print_exception(tp, val, tb)
-            
-        wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
-        
-    def ClearWhileRunning(self):
-        list = self._framesChoiceCtrl
-        list.Clear()
-        list.Enable(False)
-        tree = self._treeCtrl  
-        root = self._root   
-        tree.DeleteChildren(root)
-        self._interCtrl.Enable(False)
-   
-        #tree.Hide()
-        
-    def OnListRightClick(self, event):
-        if not hasattr(self, "syncFrameID"):
-            self.syncFrameID = wx.NewId()
-            self.Bind(wx.EVT_MENU, self.OnSyncFrame, id=self.syncFrameID)
-        menu = wx.Menu()
-        item = wx.MenuItem(menu, self.syncFrameID, "Goto Source Line")
-        menu.AppendItem(item)
-        self.PopupMenu(menu, event.GetPosition())
-        menu.Destroy()
+
+            try:
+                list = self._framesChoiceCtrl
+                frameNode = self._stack[int(self.currentItem)]
+                message = frameNode.getAttribute("message")
+                binType = self._ui._callback._debuggerServer.attempt_introspection(message, self._parentChain)
+                xmldoc = bz2.decompress(binType.data)
+                domDoc = parseString(xmldoc)
+                nodeList = domDoc.getElementsByTagName('replacement')
+                replacementNode = nodeList.item(0)
+                if len(replacementNode.childNodes):
+                    thingToWalk = replacementNode.childNodes.item(0)
+                    tree = self._treeCtrl
+                    parent = tree.GetItemParent(self._introspectItem)
+                    treeNode = self.AppendSubTreeFromNode(thingToWalk, thingToWalk.getAttribute('name'), parent, insertBefore=self._introspectItem)
+                    if thingToWalk.getAttribute('name').find('[') == -1:
+                        self._treeCtrl.SortChildren(treeNode)
+                    self._treeCtrl.Expand(treeNode)
+                    tree.Delete(self._introspectItem)
+            except:
+                tp,val,tb = sys.exc_info()
+                traceback.print_exception(tp, val, tb)
+
+        finally:
+            wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
 
     def OnSyncFrame(self, event):
         list = self._framesChoiceCtrl
         frameNode = self._stack[int(self.currentItem)]
         file = frameNode.getAttribute("file")
         line = frameNode.getAttribute("line")
-        self._ui.SynchCurrentLine( file, int(line) )  
-        
-    def LoadFramesListXML(self, framesXML):
-        wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
-        self._interCtrl.Enable(True)
+        self._ui.SynchCurrentLine( file, int(line) )
 
+    def LoadFramesList(self, framesXML):
+        wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
         try:
-            domDoc = parseString(framesXML)
-            list = self._framesChoiceCtrl
-            list.Clear()
-            self._stack = []
-            nodeList = domDoc.getElementsByTagName('frame')
-            frame_count = -1
-            for index in range(0, nodeList.length):
+            self._cmdInput.Enable(True)
+            self._cmdOutput.Enable(True)
+
+            try:
+                domDoc = parseString(framesXML)
+                list = self._framesChoiceCtrl
+                list.Clear()
+                self._stack = []
+                nodeList = domDoc.getElementsByTagName('frame')
+                frame_count = -1
+                for index in range(0, nodeList.length):
+                    frameNode = nodeList.item(index)
+                    message = frameNode.getAttribute("message")
+                    list.Append(message)
+                    self._stack.append(frameNode)
+                    frame_count += 1
+                index = len(self._stack) - 1
+                list.SetSelection(index)
+
+                node = self._stack[index]
+                self.currentItem = index
+                self.PopulateTreeFromFrameNode(node)
+                self.OnSyncFrame(None)
+
+                self._p1.FitInside()
                 frameNode = nodeList.item(index)
-                message = frameNode.getAttribute("message")
-                list.Append(message)
-                self._stack.append(frameNode)
-                frame_count += 1
-            index = len(self._stack) - 1
-            list.SetSelection(index) 
+                file = frameNode.getAttribute("file")
+                line = frameNode.getAttribute("line")
+                self._ui.SynchCurrentLine( file, int(line) )
+            except:
+                tp,val,tb=sys.exc_info()
+                traceback.print_exception(tp, val, tb)
 
-            node = self._stack[index]
-            self.currentItem = index
-            self.PopulateTreeFromFrameNode(node)
-            self.OnSyncFrame(None)
+        finally:
+            wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
 
-            self._p1.FitInside()    
-            frameNode = nodeList.item(index)
-            file = frameNode.getAttribute("file")
-            line = frameNode.getAttribute("line")
-            self._ui.SynchCurrentLine( file, int(line) )
-        except:
-            tp,val,tb=sys.exc_info()
-            traceback.print_exception(tp, val, tb)
-              
-        wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
 
-        
     def ListItemSelected(self, event):
         self.PopulateTreeFromFrameMessage(event.GetString())
         self.OnSyncFrame(None)
-        
-    def PopulateTreeFromFrameMessage(self, message):    
+
+    def PopulateTreeFromFrameMessage(self, message):
         index = 0
         for node in self._stack:
             if node.getAttribute("message") == message:
@@ -1271,13 +1799,13 @@ class FramesUI(wx.SplitterWindow):
                     self.PopulateTreeFromFrameNode(nodeList[0])
                 return
             index = index + 1
-                        
+
     def PopulateTreeFromFrameNode(self, frameNode):
         list = self._framesChoiceCtrl
         list.Enable(True)
-        tree = self._treeCtrl 
+        tree = self._treeCtrl
         #tree.Show(True)
-        root = self._root   
+        root = self._root
         tree.DeleteChildren(root)
         children = frameNode.childNodes
         firstChild = None
@@ -1290,9 +1818,9 @@ class FramesUI(wx.SplitterWindow):
         if firstChild:
             tree.Expand(firstChild)
         self._p2.FitInside()
-        
+
     def IntrospectCallback(self, event):
-        tree = self._treeCtrl 
+        tree = self._treeCtrl
         item = event.GetItem()
         if _VERBOSE:
             print "In introspectCallback item is %s, pydata is %s" % (event.GetItem(), tree.GetPyData(item))
@@ -1303,16 +1831,16 @@ class FramesUI(wx.SplitterWindow):
         self._parentChain = self.GetItemChain(item)
         self.OnIntrospect(event)
         event.Skip()
-            
+
     def AppendSubTreeFromNode(self, node, name, parent, insertBefore=None):
-        tree = self._treeCtrl 
+        tree = self._treeCtrl
         if insertBefore != None:
             treeNode = tree.InsertItem(parent, insertBefore, name)
-        else: 
+        else:
             treeNode = tree.AppendItem(parent, name)
         children = node.childNodes
         intro = node.getAttribute('intro')
-            
+
         if intro == "True":
             tree.SetItemHasChildren(treeNode, True)
             tree.SetPyData(treeNode, "Introspect")
@@ -1321,7 +1849,7 @@ class FramesUI(wx.SplitterWindow):
         for index in range(0, children.length):
             subNode = children.item(index)
             if self.HasChildren(subNode):
-                self.AppendSubTreeFromNode(subNode, subNode.getAttribute("name"), treeNode)      
+                self.AppendSubTreeFromNode(subNode, subNode.getAttribute("name"), treeNode)
             else:
                 name = subNode.getAttribute("name")
                 value = self.StripOuterSingleQuotes(subNode.getAttribute("value"))
@@ -1334,73 +1862,44 @@ class FramesUI(wx.SplitterWindow):
         if name.find('[') == -1:
             self._treeCtrl.SortChildren(treeNode)
         return treeNode
-        
+
     def StripOuterSingleQuotes(self, string):
         if string.startswith("'") and string.endswith("'"):
             retval =  string[1:-1]
         elif string.startswith("\"") and string.endswith("\""):
             retval = string[1:-1]
         else:
-            retval = string
-        if retval.startswith("u'") and retval.endswith("'"):
-            retval = retval[1:]
-        return retval
-            
-    def HasChildren(self, node):
-        try:
-            return node.childNodes.length > 0
-        except:
-            tp,val,tb=sys.exc_info()
-            return False
-    def AppendText(self, text):
-        self._textCtrl.SetReadOnly(False)
-        self._textCtrl.AddText(text)
-        self._textCtrl.ScrollToLine(self._textCtrl.GetLineCount())
-        self._textCtrl.SetReadOnly(True)
-        
-    def AppendErrorText(self, text):
-        self._textCtrl.SetReadOnly(False)
-        self._textCtrl.SetFontColor(wx.RED)   
-        self._textCtrl.StyleClearAll()
-        self._textCtrl.AddText(text)
-        self._textCtrl.ScrollToLine(self._textCtrl.GetLineCount())
-        self._textCtrl.SetFontColor(wx.BLACK)   
-        self._textCtrl.StyleClearAll()
-        self._textCtrl.SetReadOnly(True)
-        
-    def ClearOutput(self, event):
-        self._textCtrl.SetReadOnly(False)
-        self._textCtrl.ClearAll()  
-        self._textCtrl.SetReadOnly(True)
+            retval = string
+        if retval.startswith("u'") and retval.endswith("'"):
+            retval = retval[1:]
+        return retval
+
+    def HasChildren(self, node):
+        try:
+            return node.childNodes.length > 0
+        except:
+            tp,val,tb=sys.exc_info()
+            return False
+
 
-    def SwitchToOutputTab(self):
-        self._notebook.SetSelection(0) 
-                          
 class DebuggerView(Service.ServiceView):
-    
+
     #----------------------------------------------------------------------------
     # Overridden methods
     #----------------------------------------------------------------------------
 
     def __init__(self, service):
         Service.ServiceView.__init__(self, service)
-        
+
     def _CreateControl(self, parent, id):
         return None
-        
+
     #------------------------------------------------------------------------------
     # Event handling
     #-----------------------------------------------------------------------------
-    
+
     def OnToolClicked(self, event):
         self.GetFrame().ProcessEvent(event)
-        
-    def ProcessUpdateUIEvent(self, event):
-        return False
-        
-    def ProcessEvent(self, event):
-        return False
 
     #------------------------------------------------------------------------------
     # Class methods
@@ -1412,23 +1911,23 @@ class Interaction:
         self._message = message
         self._info = info
         self._quit = quit
-        
+
     def getFramesXML(self):
         return self._framesXML
-        
+
     def getMessage(self):
         return self._message
-        
+
     def getInfo(self):
         return self._info
-        
+
     def getQuit(self):
-        return self._quit  
-         
+        return self._quit
+
 class AGXMLRPCServer(SimpleXMLRPCServer.SimpleXMLRPCServer):
     def __init__(self, address, logRequests=0):
-        SimpleXMLRPCServer.SimpleXMLRPCServer.__init__(self, address, logRequests=logRequests)               
-                           
+        SimpleXMLRPCServer.SimpleXMLRPCServer.__init__(self, address, logRequests=logRequests)
+
 class RequestHandlerThread(threading.Thread):
     def __init__(self, queue, address):
         threading.Thread.__init__(self)
@@ -1440,31 +1939,31 @@ class RequestHandlerThread(threading.Thread):
         self._server.register_function(self.quit)
         self._server.register_function(self.dummyOperation)
         if _VERBOSE: print "RequestHandlerThread on fileno %s" % str(self._server.fileno())
-        
+
     def run(self):
         while self._keepGoing:
             try:
-                self._server.handle_request() 
+                self._server.handle_request()
             except:
                 tp, val, tb = sys.exc_info()
                 traceback.print_exception(tp, val, tb)
                 self._keepGoing = False
         if _VERBOSE: print "Exiting Request Handler Thread."
+
     def interaction(self, message, frameXML, info):
         if _VERBOSE: print "In RequestHandlerThread.interaction -- adding to queue"
         interaction = Interaction(message, frameXML, info)
         self._queue.put(interaction)
         return ""
-        
+
     def quit(self):
         interaction = Interaction(None, None, info=None, quit=True)
         self._queue.put(interaction)
         return ""
-    
+
     def dummyOperation(self):
         return ""
-        
+
     def AskToStop(self):
         self._keepGoing = False
         if type(self._server) is not types.NoneType:
@@ -1489,7 +1988,7 @@ class RequestBreakThread(threading.Thread):
             self._pushBreakpoints = pushBreakpoints
             self._breakDict = breakDict
             self._kill = kill
-            
+
         def run(self):
             try:
                 if _VERBOSE: print "RequestBreakThread, before call"
@@ -1511,7 +2010,7 @@ class DebuggerOperationThread(threading.Thread):
         def __init__(self, function):
             threading.Thread.__init__(self)
             self._function = function
-            
+
         def run(self):
             if _VERBOSE: print "In DOT, before call"
             try:
@@ -1519,19 +2018,46 @@ class DebuggerOperationThread(threading.Thread):
             except:
                 tp,val,tb = sys.exc_info()
                 traceback.print_exception(tp, val, tb)
-            if _VERBOSE: print "In DOT, after call"
-                
-class DebuggerCallback:
+            if _VERBOSE:
+                print "In DOT, after call"
+
+class BaseDebuggerCallback(object):
+
+    def Start(self):
+        assert False, "Start not overridden"
+
+    def ShutdownServer(self):
+        assert False, "ShutdownServer not overridden"
+
+    def BreakExecution(self):
+        assert False, "BreakExecution not overridden"
+
+    def SingleStep(self):
+        assert False, "SingleStep not overridden"
+
+    def Next(self):
+        assert False, "Next not overridden"
+
+    def Continue(self):
+        assert False, "Start not overridden"
+
+    def Return(self):
+        assert False, "Return not overridden"
+
+    def PushBreakpoints(self):
+        assert False, "PushBreakpoints not overridden"
 
-    def __init__(self, host, port, debugger_url, break_url, debuggerUI):
+class PythonDebuggerCallback(BaseDebuggerCallback):
+
+    def __init__(self, host, port, debugger_url, break_url, debuggerUI, autoContinue=False):
         if _VERBOSE: print "+++++++ Creating server on port, ", str(port)
-        
+        self._timer = None
         self._queue = Queue.Queue(50)
         self._host = host
         self._port = int(port)
         threading._VERBOSE = _VERBOSE
         self._serverHandlerThread = RequestHandlerThread(self._queue, (self._host, self._port))
-        
+
         self._debugger_url = debugger_url
         self._debuggerServer = None
         self._waiting = False
@@ -1541,49 +2067,44 @@ class DebuggerCallback:
         self._breakServer = None
         self._firstInteraction = True
         self._pendingBreak = False
-        
-    def start(self):
+        self._autoContinue = autoContinue
+
+    def Start(self):
         self._serverHandlerThread.start()
-    
+
     def ShutdownServer(self):
         #rbt = RequestBreakThread(self._breakServer, kill=True)
         #rbt.start()
-        self.setWaiting(False)
+        self._waiting = False
         if self._serverHandlerThread:
             self._serverHandlerThread.AskToStop()
             self._serverHandlerThread = None
-                            
+
     def BreakExecution(self):
         rbt = RequestBreakThread(self._breakServer, interrupt=True)
         rbt.start()
-                
+
     def SingleStep(self):
         self._debuggerUI.DisableWhileDebuggerRunning()
         self._debuggerServer.set_step() # Figure out where to set allowNone
-        self.waitForRPC()
+        self.WaitForRPC()
 
     def Next(self):
         self._debuggerUI.DisableWhileDebuggerRunning()
         self._debuggerServer.set_next()
-        self.waitForRPC()
-        
+        self.WaitForRPC()
+
     def Continue(self):
         self._debuggerUI.DisableWhileDebuggerRunning()
         self._debuggerServer.set_continue()
-        self.waitForRPC()
-        
+        self.WaitForRPC()
+
     def Return(self):
         self._debuggerUI.DisableWhileDebuggerRunning()
         self._debuggerServer.set_return()
-        self.waitForRPC()
-        
-    def setWaiting(self, value):
-        self._waiting = value
-        
-    def getWaiting(self):
-        return self._waiting
-            
-    def readQueue(self):
+        self.WaitForRPC()
+
+    def ReadQueue(self):
         if self._queue.qsize():
             try:
                 item = self._queue.get_nowait()
@@ -1591,37 +2112,37 @@ class DebuggerCallback:
                     self.interaction(None, None, None, True)
                 else:
                     data = bz2.decompress(item.getFramesXML().data)
-                    self.interaction(item.getMessage().data, data, item.getInfo(), False)               
+                    self.interaction(item.getMessage().data, data, item.getInfo(), False)
             except Queue.Empty:
                 pass
-                
-    def pushBreakpoints(self):
+
+    def PushBreakpoints(self):
         rbt = RequestBreakThread(self._breakServer, pushBreakpoints=True, breakDict=self._service.GetMasterBreakpointDict())
         rbt.start()
 
-                               
-    def waitForRPC(self):
-        self.setWaiting(True)
-        while self.getWaiting():
+
+    def WaitForRPC(self):
+        self._waiting = True
+        while self._waiting:
             try:
-                self.readQueue()
+                self.ReadQueue()
                 import time
                 time.sleep(0.02)
             except:
                 tp, val, tb = sys.exc_info()
                 traceback.print_exception(tp, val, tb)
             wx.GetApp().Yield(True)
-        if _VERBOSE: print "Exiting waitForRPC."
-        
+        if _VERBOSE: print "Exiting WaitForRPC."
+
     def interaction(self, message, frameXML, info, quit):
-        
+
         #This method should be hit as the debugger starts.
         if self._firstInteraction:
             self._firstInteraction = False
             self._debuggerServer = xmlrpclib.ServerProxy(self._debugger_url,  allow_none=1)
             self._breakServer = xmlrpclib.ServerProxy(self._break_url, allow_none=1)
-            self.pushBreakpoints()
-        self.setWaiting(False)
+            self.PushBreakpoints()
+        self._waiting = False
         if _VERBOSE: print "+"*40
         if(quit):
             self._debuggerUI.StopExecution(None)
@@ -1633,11 +2154,45 @@ class DebuggerCallback:
             self._debuggerUI.SwitchToOutputTab()
         else:
             if _VERBOSE: print "Hit interaction no exception"
+        #if not self._autoContinue:
         self._debuggerUI.SetStatusText(message)
-        self._debuggerUI.LoadFramesListXML(frameXML)        
-        self._debuggerUI.EnableWhileDebuggerStopped()
+        if not self._autoContinue:
+            self._debuggerUI.LoadPythonFramesList(frameXML)
+            self._debuggerUI.EnableWhileDebuggerStopped()
+
+        if self._autoContinue:
+            self._timer = wx.PyTimer(self.DoContinue)
+            self._autoContinue = False
+            self._timer.Start(250)
         if _VERBOSE: print "+"*40
-        
+
+    def DoContinue(self):
+        self._timer.Stop()
+        dbgService = wx.GetApp().GetService(DebuggerService)
+        evt = DebugInternalWebServer()
+        evt.SetId(self._debuggerUI.CONTINUE_ID)
+        wx.PostEvent(self._debuggerUI, evt)
+        if _VERBOSE: print "Event Continue posted"
+
+        evt = DebugInternalWebServer()
+        evt.SetId(DebuggerService.DEBUG_WEBSERVER_NOW_RUN_PROJECT_ID)
+        wx.PostEvent(dbgService._frame, evt)
+        if _VERBOSE: print "Event RunProject posted"
+
+    def SendRunEvent(self):
+        class SendEventThread(threading.Thread):
+            def __init__(self):
+                threading.Thread.__init__(self)
+
+            def run(self):
+                dbgService = wx.GetApp().GetService(DebuggerService)
+                evt = DebugInternalWebServer()
+                evt.SetId(DebuggerService.DEBUG_WEBSERVER_NOW_RUN_PROJECT_ID)
+                wx.PostEvent(dbgService._frame, evt)
+                print "Event posted"
+        set = SendEventThread()
+        set.start()
+
 class DebuggerService(Service.Service):
 
     #----------------------------------------------------------------------------
@@ -1647,9 +2202,12 @@ class DebuggerService(Service.Service):
     CLEAR_ALL_BREAKPOINTS = wx.NewId()
     RUN_ID = wx.NewId()
     DEBUG_ID = wx.NewId()
+    RUN_LAST_ID = wx.NewId()
+    DEBUG_LAST_ID = wx.NewId()
     DEBUG_WEBSERVER_ID = wx.NewId()
     RUN_WEBSERVER_ID = wx.NewId()
-            
+    DEBUG_WEBSERVER_CONTINUE_ID = wx.NewId()
+    DEBUG_WEBSERVER_NOW_RUN_PROJECT_ID = wx.NewId()
     def ComparePaths(first, second):
         one = DebuggerService.ExpandPath(first)
         two = DebuggerService.ExpandPath(second)
@@ -1658,7 +2216,7 @@ class DebuggerService(Service.Service):
         else:
             return one == two
     ComparePaths = staticmethod(ComparePaths)
-            
+
     # Make sure we're using an expanded path on windows.
     def ExpandPath(path):
         if _WINDOWS:
@@ -1667,11 +2225,11 @@ class DebuggerService(Service.Service):
             except:
                 if _VERBOSE:
                     print "Cannot get long path for %s" % path
-                
+
         return path
-        
+
     ExpandPath = staticmethod(ExpandPath)
+
     #----------------------------------------------------------------------------
     # Overridden methods
     #----------------------------------------------------------------------------
@@ -1690,7 +2248,12 @@ class DebuggerService(Service.Service):
                 self._masterBPDict = {}
         else:
             self._masterBPDict = {}
-            
+        self._frame = None
+        self.projectPath = None
+        self.fileToDebug = None
+        self.phpDbgParam = None
+        self.dbgLanguage = projectmodel.LANGUAGE_DEFAULT
+
     def OnCloseFrame(self, event):
         # IS THIS THE RIGHT PLACE?
         try:
@@ -1700,7 +2263,7 @@ class DebuggerService(Service.Service):
             tp,val,tb = sys.exc_info()
             traceback.print_exception(tp, val, tb)
         return True
-        
+
     def _CreateView(self):
         return DebuggerView(self)
 
@@ -1711,20 +2274,28 @@ class DebuggerService(Service.Service):
 
     def InstallControls(self, frame, menuBar = None, toolBar = None, statusBar = None, document = None):
         #Service.Service.InstallControls(self, frame, menuBar, toolBar, statusBar, document)
-
+        self._frame = frame
         config = wx.ConfigBase_Get()
 
         debuggerMenu = wx.Menu()
         if not menuBar.FindItemById(DebuggerService.CLEAR_ALL_BREAKPOINTS):
-                    
+
             debuggerMenu.Append(DebuggerService.RUN_ID, _("&Run...\tCtrl+R"), _("Runs a file"))
             wx.EVT_MENU(frame, DebuggerService.RUN_ID, frame.ProcessEvent)
             wx.EVT_UPDATE_UI(frame, DebuggerService.RUN_ID, frame.ProcessUpdateUIEvent)
-            
+
             debuggerMenu.Append(DebuggerService.DEBUG_ID, _("&Debug...\tCtrl+D"), _("Debugs a file"))
             wx.EVT_MENU(frame, DebuggerService.DEBUG_ID, frame.ProcessEvent)
             wx.EVT_UPDATE_UI(frame, DebuggerService.DEBUG_ID, frame.ProcessUpdateUIEvent)
-            
+
+            debuggerMenu.Append(DebuggerService.RUN_LAST_ID, _("&Run Using Last Settings\tF5"), _("Runs a file using previous settings"))
+            wx.EVT_MENU(frame, DebuggerService.RUN_LAST_ID, frame.ProcessEvent)
+            wx.EVT_UPDATE_UI(frame, DebuggerService.RUN_LAST_ID, frame.ProcessUpdateUIEvent)
+
+            debuggerMenu.Append(DebuggerService.DEBUG_LAST_ID, _("&Debug Using Last Settings\tF8"), _("Debugs a file using previous settings"))
+            wx.EVT_MENU(frame, DebuggerService.DEBUG_LAST_ID, frame.ProcessEvent)
+            wx.EVT_UPDATE_UI(frame, DebuggerService.DEBUG_LAST_ID, frame.ProcessUpdateUIEvent)
+
             if not ACTIVEGRID_BASE_IDE:
                 debuggerMenu.AppendSeparator()
                 debuggerMenu.Append(DebuggerService.DEBUG_WEBSERVER_ID, _("Debug Internal Web Server"), _("Debugs the internal webservier"))
@@ -1733,30 +2304,31 @@ class DebuggerService(Service.Service):
                 debuggerMenu.Append(DebuggerService.RUN_WEBSERVER_ID, _("Restart Internal Web Server"), _("Restarts the internal webservier"))
                 wx.EVT_MENU(frame, DebuggerService.RUN_WEBSERVER_ID, frame.ProcessEvent)
                 wx.EVT_UPDATE_UI(frame, DebuggerService.RUN_WEBSERVER_ID, frame.ProcessUpdateUIEvent)
-            
+
+                frame.Bind(EVT_DEBUG_INTERNAL, frame.ProcessEvent)
             debuggerMenu.AppendSeparator()
-            
-            debuggerMenu.Append(DebuggerService.TOGGLE_BREAKPOINT_ID, _("&Toggle Breakpoint...\tCtrl+B"), _("Toggle a breakpoint"))
+
+            debuggerMenu.Append(DebuggerService.TOGGLE_BREAKPOINT_ID, _("&Toggle Breakpoint\tCtrl+B"), _("Toggle a breakpoint"))
             wx.EVT_MENU(frame, DebuggerService.TOGGLE_BREAKPOINT_ID, self.ProcessEvent)
             wx.EVT_UPDATE_UI(frame, DebuggerService.TOGGLE_BREAKPOINT_ID, self.ProcessUpdateUIEvent)
 
             debuggerMenu.Append(DebuggerService.CLEAR_ALL_BREAKPOINTS, _("&Clear All Breakpoints"), _("Clear All Breakpoints"))
             wx.EVT_MENU(frame, DebuggerService.CLEAR_ALL_BREAKPOINTS, self.ProcessEvent)
             wx.EVT_UPDATE_UI(frame, DebuggerService.CLEAR_ALL_BREAKPOINTS, self.ProcessUpdateUIEvent)
-            
-            
+
+
         viewMenuIndex = menuBar.FindMenu(_("&Project"))
         menuBar.Insert(viewMenuIndex + 1, debuggerMenu, _("&Run"))
-        
+
         toolBar.AddSeparator()
-        toolBar.AddTool(DebuggerService.RUN_ID, getRunningManBitmap(), shortHelpString = _("Run"), longHelpString = _("Run"))
-        toolBar.AddTool(DebuggerService.DEBUG_ID, getDebuggingManBitmap(), shortHelpString = _("Debug"), longHelpString = _("Debug"))
+        toolBar.AddTool(DebuggerService.RUN_LAST_ID, getRunningManBitmap(), shortHelpString = _("Run Using Last Settings"), longHelpString = _("Run Using Last Settings"))
+        toolBar.AddTool(DebuggerService.DEBUG_LAST_ID, getDebuggingManBitmap(), shortHelpString = _("Debug Using Last Settings"), longHelpString = _("Debug Using Last Settings"))
         toolBar.Realize()
 
         return True
 
 
-            
+
     #----------------------------------------------------------------------------
     # Event Processing Methods
     #----------------------------------------------------------------------------
@@ -1782,14 +2354,26 @@ class DebuggerService(Service.Service):
         elif an_id == DebuggerService.DEBUG_ID:
             self.OnDebugProject(event)
             return True
+        elif an_id == DebuggerService.RUN_LAST_ID:
+            self.OnRunProject(event, showDialog=False)
+            return True
+        elif an_id == DebuggerService.DEBUG_LAST_ID:
+            self.OnDebugProject(event, showDialog=False)
+            return True
         elif an_id == DebuggerService.DEBUG_WEBSERVER_ID:
             self.OnDebugWebServer(event)
             return True
+        elif an_id == DebuggerService.DEBUG_WEBSERVER_CONTINUE_ID:
+            self.OnDebugWebServerContinue(event)
+            return True
+        elif an_id == DebuggerService.DEBUG_WEBSERVER_NOW_RUN_PROJECT_ID:
+            self.WaitDebuggerThenRunProject()
+            return True
         elif an_id == DebuggerService.RUN_WEBSERVER_ID:
             self.OnRunWebServer(event)
             return True
         return False
-        
+
     def ProcessUpdateUIEvent(self, event):
         if Service.Service.ProcessUpdateUIEvent(self, event):
             return True
@@ -1803,7 +2387,9 @@ class DebuggerService(Service.Service):
             event.Enable(self.HasBreakpointsSet())
             return True
         elif (an_id == DebuggerService.RUN_ID
-        or an_id == DebuggerService.DEBUG_ID):
+        or an_id == DebuggerService.RUN_LAST_ID
+        or an_id == DebuggerService.DEBUG_ID
+        or an_id == DebuggerService.DEBUG_LAST_ID):
             event.Enable(self.HasAnyFiles())
             return True
         else:
@@ -1811,75 +2397,172 @@ class DebuggerService(Service.Service):
 
     #----------------------------------------------------------------------------
     # Class Methods
-    #----------------------------------------------------------------------------                    
-   
-    def OnDebugProject(self, event):
+    #----------------------------------------------------------------------------
+
+    def OnDebugProject(self, event, showDialog=True):
         if _WINDOWS and not _PYWIN32_INSTALLED:
             wx.MessageBox(_("Python for Windows extensions (pywin32) is required to debug on Windows machines. Please go to http://sourceforge.net/projects/pywin32/, download and install pywin32."))
             return
         if not Executor.GetPythonExecutablePath():
             return
-        if DebugCommandUI.DebuggerRunning():
+        if BaseDebuggerUI.DebuggerRunning():
             wx.MessageBox(_("A debugger is already running. Please shut down the other debugger first."), _("Debugger Running"))
             return
+        config = wx.ConfigBase_Get()
+        host = config.Read("DebuggerHostName", DEFAULT_HOST)
+        if not host:
+            wx.MessageBox(_("No debugger host set. Please go to Tools->Options->Debugger and set one."), _("No Debugger Host"))
+            return
+
         self.ShowWindow(True)
         projectService = wx.GetApp().GetService(ProjectEditor.ProjectService)
         try:
-            dlg = CommandPropertiesDialog(self.GetView().GetFrame(), 'Debug Python File', projectService, None, pythonOnly=True, okButtonName="Debug", debugging=True)
+            dlg = CommandPropertiesDialog(self.GetView().GetFrame(), 'Debug File', projectService, None, okButtonName="Debug", debugging=True)
         except:
             return
         dlg.CenterOnParent()
-        if dlg.ShowModal() == wx.ID_OK:
+        if not showDialog:
+            showDialog = dlg.MustShowDialog()
+        if showDialog and dlg.ShowModal() == wx.ID_OK:
+            projectPath, fileToDebug, initialArgs, startIn, isPython, environment = dlg.GetSettings()
+        elif not showDialog:
             projectPath, fileToDebug, initialArgs, startIn, isPython, environment = dlg.GetSettings()
-            dlg.Destroy()
         else:
             dlg.Destroy()
             return
+        dlg.Destroy()
         self.PromptToSaveFiles()
-
         shortFile = os.path.basename(fileToDebug)
         fileToDebug = DebuggerService.ExpandPath(fileToDebug)
-        try:
-            page = DebugCommandUI(Service.ServiceView.bottomTab, -1, str(fileToDebug), self)
+        if fileToDebug.endswith('.bpel'):
+            self.projectPath = projectPath
+            self.fileToDebug = fileToDebug
+
+            #
+            # TODO: merge getting project stuff and save the results for
+            # WaitDebuggerThenRunProject() which currently does it again.
+            # 
+            projects = projectService.FindProjectByFile(projectPath)
+            if not projects:
+                return
+            project = projects[0]
+            lang    = project.GetAppInfo().language
+            if lang:
+                self.dbgLanguage = lang
+
+            dbgService = wx.GetApp().GetService(DebuggerService)
+
+            evt = DebugInternalWebServer()
+            evt.SetId(DebuggerService.DEBUG_WEBSERVER_CONTINUE_ID)
+            wx.PostEvent(dbgService._frame, evt)
+
+            if lang == projectmodel.LANGUAGE_PHP:
+                evt = DebugInternalWebServer()
+                evt.SetId(DebuggerService.DEBUG_WEBSERVER_NOW_RUN_PROJECT_ID)
+                wx.PostEvent(dbgService._frame, evt)
+
+            return
+
+        elif fileToDebug.endswith('.php'):
+            page = PHPDebuggerUI(Service.ServiceView.bottomTab, -1, str(fileToDebug), self)
             count = Service.ServiceView.bottomTab.GetPageCount()
             Service.ServiceView.bottomTab.AddPage(page, _("Debugging: ") + shortFile)
             Service.ServiceView.bottomTab.SetSelection(count)
+
+            fullPhpScriptPath = os.path.normpath(fileToDebug)
+            environment["REDIRECT_STATUS"] = "200"
+            environment["REDIRECT_URL"] = fullPhpScriptPath
+            environment["SERVER_SOFTWARE"] = "AG PHP Debugger 1.7"
+            environment["SERVER_NAME"] = "localhost"
+            environment["SERVER_ADDR"] = "127.0.0.1"
+            environment["SERVER_PORT"] = "80"
+            environment["REMOTE_ADDR"] = "127.0.0.1"
+            environment["SCRIPT_FILENAME"] = "php"
+            environment["GATEWAY_INTERFACE"] = "CGI/1.1"
+            environment["SERVER_PROTOCOL"] = "HTTP/1.1"
+            environment["REQUEST_METHOD"] = "GET"
+            environment["REQUEST_URI"] = fullPhpScriptPath
+            environment["PATH_INFO"] = fullPhpScriptPath
+            environment["PATH_TRANSLATED"] = fullPhpScriptPath
+            environment["HTTP_COOKIE"] = "DBGSESSID=11439636363807700001@clienthost:10001"
+
             page.Execute(initialArgs, startIn, environment)
-        except:
-            pass
-            
-    def OnDebugWebServer(self, event):
+        else:
+            try:
+                page = PythonDebuggerUI(Service.ServiceView.bottomTab, -1, str(fileToDebug), self)
+                count = Service.ServiceView.bottomTab.GetPageCount()
+                Service.ServiceView.bottomTab.AddPage(page, _("Debugging: ") + shortFile)
+                Service.ServiceView.bottomTab.SetSelection(count)
+                page.Execute(initialArgs, startIn, environment)
+            except:
+                pass
+
+    def WaitDebuggerThenRunProject(self):
+        import time
+        #while not BaseDebuggerUI.DebuggerPastAutoContinue():
+        #    time.sleep(0.2)
+        #    wx.GetApp().Yield(True)
+        #    print "After Yield"
+        time.sleep(2.0)
+        projectService = wx.GetApp().GetService(ProjectEditor.ProjectService)
+        projects = projectService.FindProjectByFile(self.projectPath)
+        if not projects:
+            return
+        project = projects[0]
+        try:
+            deployFilePath = project.GenerateDeployment()
+        except ProjectEditor.DataServiceExistenceException, e:
+            dataSourceName = str(e)
+            projectService.PromptForMissingDataSource(dataSourceName)
+            return
+        projectService.RunProcessModel(self.fileToDebug, project.GetAppInfo().language, deployFilePath)
+
+    def OnDebugWebServerContinue(self, event):
+        self.OnDebugWebServer(event, autoContinue=True)
+
+    def OnDebugWebServer(self, event, autoContinue=False):
+        #print "xxxxx debugging OnDebugWebServer"
         if _WINDOWS and not _PYWIN32_INSTALLED:
             wx.MessageBox(_("Python for Windows extensions (pywin32) is required to debug on Windows machines. Please go to http://sourceforge.net/projects/pywin32/, download and install pywin32."))
             return
         if not Executor.GetPythonExecutablePath():
             return
-        if DebugCommandUI.DebuggerRunning():
+        if BaseDebuggerUI.DebuggerRunning():
             wx.MessageBox(_("A debugger is already running. Please shut down the other debugger first."), _("Debugger Running"))
             return
         import WebServerService
         wsService = wx.GetApp().GetService(WebServerService.WebServerService)
         fileName, args = wsService.StopAndPrepareToDebug()
+        #print "xxxxx OnDebugWebServer: fileName=%s, args=%s" % (repr(fileName), repr(args))
+        config = wx.ConfigBase_Get()
+        host = config.Read("DebuggerHostName", DEFAULT_HOST)
+        if not host:
+            wx.MessageBox(_("No debugger host set. Please go to Tools->Options->Debugger and set one."), _("No Debugger Host"))
+            return
         try:
-            page = DebugCommandUI(Service.ServiceView.bottomTab, -1, fileName, self)
+            if self.dbgLanguage == projectmodel.LANGUAGE_PHP:
+                page = PHPDebuggerUI(Service.ServiceView.bottomTab, -1, fileName, self)
+            else:
+                page = PythonDebuggerUI(Service.ServiceView.bottomTab, -1, fileName, self, autoContinue)
+
             count = Service.ServiceView.bottomTab.GetPageCount()
             Service.ServiceView.bottomTab.AddPage(page, _("Debugging: Internal WebServer"))
             Service.ServiceView.bottomTab.SetSelection(count)
-            page.Execute(args, startIn=os.getcwd(), environment=os.environ)
+            page.Execute(args, startIn=sysutilslib.mainModuleDir, environment=os.environ, onWebServer = True)
         except:
             pass
-            
+
     def OnRunWebServer(self, event):
         if not Executor.GetPythonExecutablePath():
             return
         import WebServerService
         wsService = wx.GetApp().GetService(WebServerService.WebServerService)
-        wsService.ShutDownAndRestart()                
-                               
+        wsService.ShutDownAndRestart()
+
     def HasAnyFiles(self):
         docs = wx.GetApp().GetDocumentManager().GetDocuments()
         return len(docs) > 0
-        
+
     def PromptToSaveFiles(self, running=True):
         filesModified = False
         docs = wx.GetApp().GetDocumentManager().GetDocuments()
@@ -1888,14 +2571,14 @@ class DebuggerService(Service.Service):
                 filesModified = True
                 break
         if filesModified:
-            frame = self.GetView().GetFrame()  
-            if running:              
+            frame = self.GetView().GetFrame()
+            if running:
                 yesNoMsg = wx.MessageDialog(frame,
                           _("Files have been modified.\nWould you like to save all files before running?"),
                           _("Run"),
                           wx.YES_NO|wx.ICON_QUESTION
                           )
-            else:              
+            else:
                 yesNoMsg = wx.MessageDialog(frame,
                           _("Files have been modified.\nWould you like to save all files before debugging?"),
                           _("Debug"),
@@ -1909,10 +2592,10 @@ class DebuggerService(Service.Service):
             yesNoMsg.Destroy()
 
     def OnExit(self):
-        DebugCommandUI.ShutdownAllDebuggers()
+        BaseDebuggerUI.ShutdownAllDebuggers()
         RunCommandUI.ShutdownAllRunners()
-        
-    def OnRunProject(self, event):
+
+    def OnRunProject(self, event, showDialog=True):
         if _WINDOWS and not _PYWIN32_INSTALLED:
             wx.MessageBox(_("Python for Windows extensions (pywin32) is required to run on Windows machines. Please go to http://sourceforge.net/projects/pywin32/, download and install pywin32."))
             return
@@ -1924,30 +2607,38 @@ class DebuggerService(Service.Service):
         except:
             return
         dlg.CenterOnParent()
-        if dlg.ShowModal() == wx.ID_OK:
+        if not showDialog:
+            showDialog = dlg.MustShowDialog()
+        if showDialog and dlg.ShowModal() == wx.ID_OK:
+            projectPath, fileToRun, initialArgs, startIn, isPython, environment = dlg.GetSettings()
+        elif not showDialog:
             projectPath, fileToRun, initialArgs, startIn, isPython, environment = dlg.GetSettings()
-            dlg.Destroy()
         else:
             dlg.Destroy()
             return
+        dlg.Destroy()
         self.PromptToSaveFiles()
-        # This will need to change when we can run more than .py and .bpel files.
-        if not isPython:
+        if fileToRun.endswith('bpel'):
             projects = projectService.FindProjectByFile(projectPath)
             if not projects:
                 return
             project = projects[0]
-            deployFilePath = project.GenerateDeployment()
+            try:
+                deployFilePath = project.GenerateDeployment()
+            except ProjectEditor.DataServiceExistenceException, e:
+                dataSourceName = str(e)
+                projectService.PromptForMissingDataSource(dataSourceName)
+                return
             projectService.RunProcessModel(fileToRun, project.GetAppInfo().language, deployFilePath)
             return
-            
+
         self.ShowWindow(True)
         shortFile = os.path.basename(fileToRun)
         page = RunCommandUI(Service.ServiceView.bottomTab, -1, str(fileToRun))
         count = Service.ServiceView.bottomTab.GetPageCount()
         Service.ServiceView.bottomTab.AddPage(page, "Running: " + shortFile)
         Service.ServiceView.bottomTab.SetSelection(count)
-        page.Execute(initialArgs, startIn, environment)
+        page.Execute(initialArgs, startIn, environment, onWebServer = True)
 
     def OnToggleBreakpoint(self, event, line=-1, fileName=None):
         if not fileName:
@@ -1970,8 +2661,8 @@ class DebuggerService(Service.Service):
                 view.GetCtrl().Refresh()
         # Now refresh all the markers icons in all the open views.
         self.ClearAllBreakpointMarkers()
-        self.SetAllBreakpointMarkers()    
-                 
+        self.SetAllBreakpointMarkers()
+
     def SilentToggleBreakpoint(self, fileName, line):
         found = False
         for lineNumber in self.GetBreakpointList(fileName):
@@ -1982,30 +2673,30 @@ class DebuggerService(Service.Service):
             self.SetBreak(fileName, line)
         else:
             self.ClearBreak(fileName, line)
-                  
+
     def SetBreak(self, fileName, line):
         expandedName = DebuggerService.ExpandPath(fileName)
         if not self._masterBPDict.has_key(expandedName):
             self._masterBPDict[expandedName] = [line]
         else:
             self._masterBPDict[expandedName] += [line]
-        # If we're already debugging, pass this bp off to the DebuggerCallback
+        # If we're already debugging, pass this bp off to the PythonDebuggerCallback
         self.NotifyDebuggersOfBreakpointChange()
-    
+
     def NotifyDebuggersOfBreakpointChange(self):
-        DebugCommandUI.NotifyDebuggersOfBreakpointChange()
-        
+        BaseDebuggerUI.NotifyDebuggersOfBreakpointChange()
+
     def GetBreakpointList(self, fileName):
         expandedName = DebuggerService.ExpandPath(fileName)
         if not self._masterBPDict.has_key(expandedName):
             return []
         else:
-            return self._masterBPDict[expandedName] 
+            return self._masterBPDict[expandedName]
 
     def SetBreakpointList(self, fileName, bplist):
         expandedName = DebuggerService.ExpandPath(fileName)
         self._masterBPDict[expandedName] = bplist
-        
+
     def BreakpointSet(self, fileName, line):
         expandedName = DebuggerService.ExpandPath(fileName)
         if not self._masterBPDict.has_key(expandedName):
@@ -2016,7 +2707,7 @@ class DebuggerService(Service.Service):
                 if(int(number) == int(line)):
                     return True
         return False
-        
+
     def ClearBreak(self, fileName, line):
         expandedName = DebuggerService.ExpandPath(fileName)
         if not self._masterBPDict.has_key(expandedName):
@@ -2029,43 +2720,49 @@ class DebuggerService(Service.Service):
                     newList.append(number)
             self._masterBPDict[expandedName] = newList
         self.NotifyDebuggersOfBreakpointChange()
-       
+
     def HasBreakpointsSet(self):
         for key, value in self._masterBPDict.items():
             if len(value) > 0:
                 return True
         return False
-                 
+
     def ClearAllBreakpoints(self):
         self._masterBPDict = {}
         self.NotifyDebuggersOfBreakpointChange()
         self.ClearAllBreakpointMarkers()
-        
+
     def ClearAllBreakpointMarkers(self):
         openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
-        for openDoc in openDocs:  
-            if isinstance(openDoc, CodeEditor.CodeDocument): 
-                openDoc.GetFirstView().MarkerDeleteAll(CodeEditor.CodeCtrl.BREAKPOINT_MARKER_NUM) 
-                
+        for openDoc in openDocs:
+            if isinstance(openDoc, CodeEditor.CodeDocument):
+                openDoc.GetFirstView().MarkerDeleteAll(CodeEditor.CodeCtrl.BREAKPOINT_MARKER_NUM)
+
     def UpdateBreakpointsFromMarkers(self, view, fileName):
         newbpLines = view.GetMarkerLines(CodeEditor.CodeCtrl.BREAKPOINT_MARKER_NUM)
         self.SetBreakpointList(fileName, newbpLines)
-        
+
     def GetMasterBreakpointDict(self):
         return self._masterBPDict
-        
+
     def SetAllBreakpointMarkers(self):
         openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
-        for openDoc in openDocs:  
-            if(isinstance(openDoc, CodeEditor.CodeDocument)): 
+        for openDoc in openDocs:
+            if(isinstance(openDoc, CodeEditor.CodeDocument)):
                 self.SetCurrentBreakpointMarkers(openDoc.GetFirstView())
-            
+
     def SetCurrentBreakpointMarkers(self, view):
         if isinstance(view, CodeEditor.CodeView) and hasattr(view, 'GetDocument'):
             view.MarkerDeleteAll(CodeEditor.CodeCtrl.BREAKPOINT_MARKER_NUM)
-            for linenum in self.GetBreakpointList(view.GetDocument().GetFilename()): 
+            for linenum in self.GetBreakpointList(view.GetDocument().GetFilename()):
                 view.MarkerAdd(lineNum=int(linenum) - 1, marker_index=CodeEditor.CodeCtrl.BREAKPOINT_MARKER_NUM)
 
+    def GetPhpDbgParam(self):
+        return self.phpDbgParam
+
+    def SetPhpDbgParam(self, value = None):
+        self.phpDbgParam = value
+
 class DebuggerOptionsPanel(wx.Panel):
 
 
@@ -2076,7 +2773,7 @@ class DebuggerOptionsPanel(wx.Panel):
         localHostStaticText = wx.StaticText(self, -1, _("Local Host Name:"))
         self._LocalHostTextCtrl = wx.TextCtrl(self, -1, config.Read("DebuggerHostName", DEFAULT_HOST), size = (150, -1))
         portNumberStaticText = wx.StaticText(self, -1, _("Port Range:"))
-        dashStaticText = wx.StaticText(self, -1, _("through to")) 
+        dashStaticText = wx.StaticText(self, -1, _("through to"))
         startingPort=config.ReadInt("DebuggerStartingPort", DEFAULT_PORT)
         self._PortNumberTextCtrl = wx.lib.intctrl.IntCtrl(self, -1, startingPort, size = (50, -1))
         self._PortNumberTextCtrl.SetMin(1)#What are real values?
@@ -2099,59 +2796,57 @@ class DebuggerOptionsPanel(wx.Panel):
         self._flushPortsButton = wx.Button(self, FLUSH_PORTS_ID, "Reset Port List")
         wx.EVT_BUTTON(parent, FLUSH_PORTS_ID, self.FlushPorts)
         debuggerPanelSizer.Add(self._flushPortsButton, (2,2), (1,2), flag=wx.ALIGN_RIGHT)
-        
+
         debuggerPanelBorderSizer.Add(debuggerPanelSizer, 0, wx.ALL, SPACE)
         self.SetSizer(debuggerPanelBorderSizer)
         self.Layout()
         parent.AddPage(self, _("Debugger"))
-        
+
     def FlushPorts(self, event):
         if self._PortNumberTextCtrl.IsInBounds():
             config = wx.ConfigBase_Get()
             config.WriteInt("DebuggerStartingPort", self._PortNumberTextCtrl.GetValue())
-            DebugCommandUI.NewPortRange()
+            PythonDebuggerUI.NewPortRange()
         else:
             wx.MessageBox(_("The starting port is not valid. Please change the value and try again.", "Invalid Starting Port Number"))
-                          
+
     def MinPortChange(self, event):
         self._EndPortNumberTextCtrl.Enable( True )
         self._EndPortNumberTextCtrl.SetValue( self._PortNumberTextCtrl.GetValue() + PORT_COUNT)
         self._EndPortNumberTextCtrl.Enable( False )
-        
+
     def OnOK(self, optionsDialog):
         config = wx.ConfigBase_Get()
         config.Write("DebuggerHostName", self._LocalHostTextCtrl.GetValue())
         if self._PortNumberTextCtrl.IsInBounds():
             config.WriteInt("DebuggerStartingPort", self._PortNumberTextCtrl.GetValue())
-            
+
 
     def GetIcon(self):
         return getContinueIcon()
 
 
 class CommandPropertiesDialog(wx.Dialog):
-    
-    def __init__(self, parent, title, projectService, currentProjectDocument, pythonOnly=False, okButtonName="Run", debugging=False):
+    def __init__(self, parent, title, projectService, currentProjectDocument, okButtonName="Run", debugging=False):
         self._projService = projectService
         self._pmext = None
-        self._pyext = None
+        self._pyext = '.py'
+        self._phpext = '.php'
         for template in self._projService.GetDocumentManager().GetTemplates():
             if not ACTIVEGRID_BASE_IDE and template.GetDocumentType() == ProcessModelEditor.ProcessModelDocument:
                 self._pmext = template.GetDefaultExtension()
-            if template.GetDocumentType() == PythonEditor.PythonDocument:
-                self._pyext = template.GetDefaultExtension()
-        self._pythonOnly = pythonOnly
-        self._currentProj = currentProjectDocument
+                break
+        self._currentProj = projectService.GetCurrentProject()
         self._projectNameList, self._projectDocumentList, selectedIndex = self.GetProjectList()
         if not self._projectNameList:
             wx.MessageBox(_("To run or debug you must have an open runnable file or project containing runnable files. Use File->Open to open the file you wish to run or debug."), _("Nothing to Run"))
-            raise BadBadBad
+            raise Exception("Nothing to Run or Debug.")
 
         wx.Dialog.__init__(self, parent, -1, title)
-           
-        projStaticText = wx.StaticText(self, -1, _("Project:")) 
-        fileStaticText = wx.StaticText(self, -1, _("File:")) 
-        argsStaticText = wx.StaticText(self, -1, _("Arguments:")) 
+
+        projStaticText = wx.StaticText(self, -1, _("Project:"))
+        fileStaticText = wx.StaticText(self, -1, _("File:"))
+        argsStaticText = wx.StaticText(self, -1, _("Arguments:"))
         startInStaticText = wx.StaticText(self, -1, _("Start in:"))
         pythonPathStaticText = wx.StaticText(self, -1, _("PYTHONPATH:"))
         postpendStaticText = _("Postpend win32api path")
@@ -2163,25 +2858,25 @@ class CommandPropertiesDialog(wx.Dialog):
         if wx.Platform == "__WXMAC__":
             GAP = 10
         flexGridSizer = wx.GridBagSizer(GAP, GAP)
-        
+
         flexGridSizer.Add(projStaticText, (0,0), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT)
         flexGridSizer.Add(self._projList, (0,1), (1,2), flag=wx.EXPAND)
-        
+
         flexGridSizer.Add(fileStaticText, (1,0), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT)
         self._fileList = wx.Choice(self, -1)
         self.Bind(wx.EVT_CHOICE, self.OnFileSelected, self._fileList)
         flexGridSizer.Add(self._fileList, (1,1), (1,2), flag=wx.EXPAND)
-        
+
         config = wx.ConfigBase_Get()
-        self._lastArguments = config.Read("LastRunArguments")
+        self._lastArguments = config.Read(self.GetKey("LastRunArguments"))
         self._argsEntry = wx.TextCtrl(self, -1, str(self._lastArguments))
         self._argsEntry.SetToolTipString(str(self._lastArguments))
 
         flexGridSizer.Add(argsStaticText, (2,0), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT)
         flexGridSizer.Add(self._argsEntry, (2,1), (1,2), flag=wx.EXPAND)
-        
+
         flexGridSizer.Add(startInStaticText, (3,0), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT)
-        self._lastStartIn = config.Read("LastRunStartIn")
+        self._lastStartIn = config.Read(self.GetKey("LastRunStartIn"))
         if not self._lastStartIn:
             self._lastStartIn = str(os.getcwd())
         self._startEntry = wx.TextCtrl(self, -1, self._lastStartIn)
@@ -2191,24 +2886,24 @@ class CommandPropertiesDialog(wx.Dialog):
         self._findDir = wx.Button(self, -1, _("Browse..."))
         self.Bind(wx.EVT_BUTTON, self.OnFindDirClick, self._findDir)
         flexGridSizer.Add(self._findDir, (3,2))
-        
+
         flexGridSizer.Add(pythonPathStaticText, (4,0), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT)
         if os.environ.has_key('PYTHONPATH'):
             startval = os.environ['PYTHONPATH']
         else:
             startval = ""
-        self._lastPythonPath = config.Read("LastPythonPath", startval)
+        self._lastPythonPath = config.Read(self.GetKey("LastPythonPath"), startval)
         self._pythonPathEntry = wx.TextCtrl(self, -1, self._lastPythonPath)
         self._pythonPathEntry.SetToolTipString(self._lastPythonPath)
         flexGridSizer.Add(self._pythonPathEntry, (4,1), (1,2), flag=wx.EXPAND)
-        
+
         if debugging and _WINDOWS:
             self._postpendCheckBox = wx.CheckBox(self, -1, postpendStaticText)
-            checked = bool(config.ReadInt("PythonPathPostpend", 1))
+            checked = bool(config.ReadInt(self.GetKey("PythonPathPostpend"), 1))
             self._postpendCheckBox.SetValue(checked)
             flexGridSizer.Add(self._postpendCheckBox, (5,1), flag=wx.EXPAND)
         cpPanelBorderSizer.Add(flexGridSizer, 0, flag=wx.ALL, border=10)
-        
+
         box = wx.StdDialogButtonSizer()
         self._okButton = wx.Button(self, wx.ID_OK, okButtonName)
         self._okButton.SetDefault()
@@ -2220,14 +2915,15 @@ class CommandPropertiesDialog(wx.Dialog):
         box.AddButton(btn)
         box.Realize()
         cpPanelBorderSizer.Add(box, 0, flag=wx.ALIGN_RIGHT|wx.ALL, border=5)
-        
+
         self.SetSizer(cpPanelBorderSizer)
-                
+
         # Set up selections based on last values used.
         self._fileNameList = None
-        self._selectedFileIndex = 0
-        lastProject = config.Read("LastRunProject")
-        lastFile = config.Read("LastRunFile")
+        self._selectedFileIndex = -1
+        lastProject = config.Read(self.GetKey("LastRunProject"))
+        lastFile = config.Read(self.GetKey("LastRunFile"))
+        self._mustShow = not lastFile
 
         if lastProject in self._projectNameList:
             selectedIndex = self._projectNameList.index(lastProject)
@@ -2240,7 +2936,14 @@ class CommandPropertiesDialog(wx.Dialog):
 
         cpPanelBorderSizer.Fit(self)
 
-            
+    def MustShowDialog(self):
+        return self._mustShow
+
+    def GetKey(self, lastPart):
+        if self._currentProj:
+            return "%s/%s/%s" % (ProjectEditor.PROJECT_KEY, self._currentProj.GetFilename().replace(os.sep, '|'), lastPart)
+
+
     def OnOKClick(self, event):
         startIn = self._startEntry.GetValue()
         fileToRun = self._fileList.GetStringSelection()
@@ -2252,22 +2955,22 @@ class CommandPropertiesDialog(wx.Dialog):
             wx.MessageBox(_("Starting directory does not exist. Please change this value."))
             return
         config = wx.ConfigBase_Get()
-        config.Write("LastRunProject", self._projectNameList[self._selectedProjectIndex])
-        config.Write("LastRunFile", fileToRun)
+        config.Write(self.GetKey("LastRunProject"), self._projectNameList[self._selectedProjectIndex])
+        config.Write(self.GetKey("LastRunFile"), fileToRun)
         # Don't update the arguments or starting directory unless we're runing python.
         if isPython:
-            config.Write("LastRunArguments", self._argsEntry.GetValue())
-            config.Write("LastRunStartIn", self._startEntry.GetValue())
-            config.Write("LastPythonPath",self._pythonPathEntry.GetValue())
+            config.Write(self.GetKey("LastRunArguments"), self._argsEntry.GetValue())
+            config.Write(self.GetKey("LastRunStartIn"), self._startEntry.GetValue())
+            config.Write(self.GetKey("LastPythonPath"),self._pythonPathEntry.GetValue())
             if hasattr(self, "_postpendCheckBox"):
-                config.WriteInt("PythonPathPostpend", int(self._postpendCheckBox.GetValue()))
+                config.WriteInt(self.GetKey("PythonPathPostpend"), int(self._postpendCheckBox.GetValue()))
 
         self.EndModal(wx.ID_OK)
-            
-    def GetSettings(self):  
+
+    def GetSettings(self):
         projectPath = self._selectedProjectDocument.GetFilename()
         filename = self._fileNameList[self._selectedFileIndex]
-        args = self._argsEntry.GetValue() 
+        args = self._argsEntry.GetValue()
         startIn = self._startEntry.GetValue()
         isPython = filename.endswith(self._pyext)
         env = os.environ
@@ -2279,15 +2982,15 @@ class CommandPropertiesDialog(wx.Dialog):
             env['PYTHONPATH'] = self._pythonPathEntry.GetValue() + os.pathsep + os.path.join(os.getcwd(), "3rdparty", "pywin32")
         else:
             env['PYTHONPATH'] = self._pythonPathEntry.GetValue()
-            
+
         return projectPath, filename, args, startIn, isPython, env
-                      
+
     def OnFileSelected(self, event):
         self._selectedFileIndex = self._fileList.GetSelection()
         self.EnableForFileType(event.GetString())
-        
+
     def EnableForFileType(self, fileName):
-        show = fileName.endswith(self._pyext)
+        show = fileName.endswith(self._pyext) or fileName.endswith(self._phpext)
         self._startEntry.Enable(show)
         self._findDir.Enable(show)
         self._argsEntry.Enable(show)
@@ -2296,12 +2999,16 @@ class CommandPropertiesDialog(wx.Dialog):
             self._lastStartIn = self._startEntry.GetValue()
             self._startEntry.SetValue("")
             self._lastArguments = self._argsEntry.GetValue()
-            self._argsEntry.SetValue("")  
+            self._argsEntry.SetValue("")
         else:
-            self._startEntry.SetValue(self._lastStartIn)
-            self._argsEntry.SetValue(self._lastArguments)  
-                
-            
+            if fileName.endswith(self._phpext):
+                self._startEntry.SetValue(os.path.dirname(fileName))
+            else:
+                self._startEntry.SetValue(self._lastStartIn)
+            self._argsEntry.SetValue(self._lastArguments)
+
+
+
     def OnFindDirClick(self, event):
         dlg = wx.DirDialog(self, "Choose a starting directory:", self._startEntry.GetValue(),
                           style=wx.DD_DEFAULT_STYLE|wx.DD_NEW_DIR_BUTTON)
@@ -2309,9 +3016,9 @@ class CommandPropertiesDialog(wx.Dialog):
         dlg.CenterOnParent()
         if dlg.ShowModal() == wx.ID_OK:
             self._startEntry.SetValue(dlg.GetPath())
-            
-        dlg.Destroy() 
-           
+
+        dlg.Destroy()
+
 
     def EvtListBox(self, event):
         if event.GetString():
@@ -2319,14 +3026,11 @@ class CommandPropertiesDialog(wx.Dialog):
             self._selectedProjectDocument = self._projectDocumentList[index]
             self._selectedProjectIndex = index
             self.PopulateFileList(self._selectedProjectDocument)
-            
+
     def FilterFileList(self, list):
-        if self._pythonOnly:
-            files = filter(lambda f: f.endswith(self._pyext), list)
-        else:
-            files = filter(lambda f: (self._pmext and f.endswith(self._pmext)) or f.endswith(self._pyext), list)
+        files = filter(lambda f: (self._phpext and f.endswith(self._phpext)) or (self._pmext and f.endswith(self._pmext)) or f.endswith(self._pyext), list)
         return files
-                 
+
     def PopulateFileList(self, project, shortNameToSelect=None):
         self._fileNameList = self.FilterFileList(project.GetFiles()[:])
         self._fileList.Clear()
@@ -2336,15 +3040,23 @@ class CommandPropertiesDialog(wx.Dialog):
         strings = map(lambda file: os.path.basename(file), self._fileNameList)
         for index in range(0, len(strings)):
             if shortNameToSelect == strings[index]:
-                self._selectedFileIndex = index 
-                break 
+                self._selectedFileIndex = index
+                break
         self._fileList.Hide()
         self._fileList.AppendItems(strings)
         self._fileList.Show()
-        if self._selectedFileIndex not in range(0, len(strings)) : self._selectedFileIndex = 0
+        if self._selectedFileIndex not in range(0, len(strings)):
+            # Pick first bpel file if there is one.
+            for index in range(0, len(strings)):
+                if strings[index].endswith('.bpel'):
+                    self._selectedFileIndex = index
+                    break
+        # Still no selected file, use first file.      
+        if self._selectedFileIndex not in range(0, len(strings)):
+            self._selectedFileIndex = 0
         self._fileList.SetSelection(self._selectedFileIndex)
         self.EnableForFileType(strings[self._selectedFileIndex])
-        
+
     def GetProjectList(self):
         docList = []
         nameList = []
@@ -2365,25 +3077,25 @@ class CommandPropertiesDialog(wx.Dialog):
                 if projectDocument.IsFileInProject(fileName):
                     return True
             return False
-            
+
         unprojectedFiles = []
         for document in self._projService.GetDocumentManager().GetDocuments():
             if not ACTIVEGRID_BASE_IDE and type(document) == ProcessModelEditor.ProcessModelDocument:
                 if not AlreadyInProject(document.GetFilename()):
                     unprojectedFiles.append(document.GetFilename())
-            if type(document) == PythonEditor.PythonDocument:
+            if type(document) == PythonEditor.PythonDocument or type(document) == PHPEditor.PHPDocument:
                 if not AlreadyInProject(document.GetFilename()):
                     unprojectedFiles.append(document.GetFilename())
-         
+
         if unprojectedFiles:
             unprojProj = ProjectEditor.ProjectDocument()
-            unprojProj.SetFilename(_("Not in any Project")) 
+            unprojProj.SetFilename(_("Not in any Project"))
             unprojProj.AddFiles(unprojectedFiles)
             docList.append(unprojProj)
-            nameList.append(_("Not in any Project"))  
-            
+            nameList.append(_("Not in any Project"))
+
         return nameList, docList, index
-        
+
 
 #----------------------------------------------------------------------
 from wx import ImageFromStream, BitmapFromImage
@@ -2400,7 +3112,7 @@ def getBreakData():
 \x03\x98\xa9z\x07\x00%\xd6\xa9\xd27\x90\xac\xbbk\xe5\x15I\xcdD$\xdc\xa7\xceT\
 5a\xce\xf3\xe4\xa0\xaa\x8bO\x12\x11\xabC\xcb\x9c}\xd57\xef\xb0\xf3\xb7\x86p\
 \x97\xf7\xb5\xaa\xde\xb9\xfa|-O\xbdjN\x9b\xf8\x06A\xcb\x00\x00\x00\x00IEND\
-\xaeB`\x82' 
+\xaeB`\x82'
 
 def getBreakBitmap():
     return BitmapFromImage(getBreakImage())
@@ -2426,7 +3138,7 @@ def getClearOutputData():
 \xda\xa7\x92\xfb\xc5w\xdf\t\x07\xc4\x05ym{\xd0\x1a\xe3\xb9xS\x81\x04\x18\x05\
 \xc9\x04\xc9a\x00Dc9\x9d\x82\xa4\xbc\xe8P\xb2\xb5P\xac\xf2\x0c\xd4\xf5\x00\
 \x88>\xac\xe17\x84\xe4\xb9G\x8b7\x9f\xf3\x1fsUl^\x7f\xe7y\x0f\x00\x00\x00\
-\x00IEND\xaeB`\x82' 
+\x00IEND\xaeB`\x82'
 
 def getClearOutputBitmap():
     return BitmapFromImage(getClearOutputImage())
@@ -2452,7 +3164,7 @@ def getCloseData():
 \x08!\x84\x90y\x9e\x11\xf1\x8bP\x96\xa5\xef\xdd\xb6\xad\xb5VJ\xf9\x9b\xe0\
 \xe9\xa6i8\xe7\xbe\xdb\xb6mi\x9a\x0e\xc3\xf0F\x88\xe3\x18\x00\xfa\xbe\x0f\
 \xc3\xd0\'\x9c\xf3eY\xa2(*\x8ab\xc7\x9e\xaed\x8c\xa1\x94\xben\xf5\xb1\xd2W\
-\xfa,\xfce.\x0b\x0f\xb8\x96e\x90gS\xe0v\x00\x00\x00\x00IEND\xaeB`\x82' 
+\xfa,\xfce.\x0b\x0f\xb8\x96e\x90gS\xe0v\x00\x00\x00\x00IEND\xaeB`\x82'
 
 def getCloseBitmap():
     return BitmapFromImage(getCloseImage())
@@ -2477,7 +3189,7 @@ def getContinueData():
 \x98\xc4\x07\x01\x00D\x1dd^\xa8\xa8j\x9ew\xed`\xa9\x16\x99\xde\xa6G\x8b\xd3Y\
 \xe6\x85]\n\r\x7f\x99\xf5\x96Jnlz#\xab\xdb\xc1\x17\x19\xb0XV\xc2\xdf\xa3)\
 \x85<\xe4\x88\x85.F\x9a\xf3H3\xb0\xf3g\xda\xd2\x0b\xc5_|\x17\xe8\xf5R\xd6\
-\x00\x00\x00\x00IEND\xaeB`\x82' 
+\x00\x00\x00\x00IEND\xaeB`\x82'
 
 def getContinueBitmap():
     return BitmapFromImage(getContinueImage())
@@ -2500,7 +3212,7 @@ gK\x06\x00 \xa5=k\x00\x00\xb0\xb2]\xd4?5f\xb1\xdb\xaf\xc6\xa2\xcb\xa8\xf0?\
 \x1c\x98\xae\x82\xbf\x81\xa4\x8eA\x16\xe1\n\xd1\xa4\x19\xb3\xe9\n\xce\xe8\
 \xf1\n\x9eg^\x18\x18\x90\xec<\x11\xf9#\x04XMZ\x19\xaac@+\x94\xd4\x99)SeP\xa1\
 )\xd6\x1dI\xe7*\xdc\xf4\x03\xdf~\xe7\x13T^Q?:X\x19d\x00\x00\x00\x00IEND\xaeB\
-`\x82' 
+`\x82'
 
 def getNextBitmap():
     return BitmapFromImage(getNextImage())
@@ -2522,7 +3234,7 @@ def getStepInData():
 \x96\xd9#\xf6\x06\xc3;p1I\xd1\x14\x0b#|\x17aF\xec\r\xeeF\xa0eB\xd34\xca\xd0A\
 ]j\x84\xa6\x03\x00""\xb7\xb0tRZ\xf7x\xb7\x83\x91]\xcb\x7fa\xd9\x89\x0fC\xfd\
 \x94\x9d|9\x99^k\x13\xa1 \xb3\x16\x0f#\xd4\x88N~\x14\xe1-\x96\x7f\xe3\x0f\
-\x11\x91UC\x0cX\'\x1e\x00\x00\x00\x00IEND\xaeB`\x82' 
+\x11\x91UC\x0cX\'\x1e\x00\x00\x00\x00IEND\xaeB`\x82'
 
 def getStepInBitmap():
     return BitmapFromImage(getStepInImage())
@@ -2542,7 +3254,7 @@ def getStopData():
 \x00\x00QIDAT8\x8d\xdd\x93A\n\xc00\x08\x04g\xb5\xff\x7fq\x13sn\xda&\x01\x0b\
 \xa5]\xf0"\xec(.J\xe6dd)\xf7\x13\x80\xadoD-12\xc8\\\xd3\r\xe2\xa6\x00j\xd9\
 \x0f\x03\xde\xbf\xc1\x0f\x00\xa7\x18\x01t\xd5\\\x05\xc8\\}T#\xe9\xfb\xbf\x90\
-\x064\xd8\\\x12\x1fQM\xf5\xd9\x00\x00\x00\x00IEND\xaeB`\x82' 
+\x064\xd8\\\x12\x1fQM\xf5\xd9\x00\x00\x00\x00IEND\xaeB`\x82'
 
 def getStopBitmap():
     return BitmapFromImage(getStopImage())
@@ -2565,7 +3277,7 @@ def getStepReturnData():
 \x84\xbf\x16V\xb0l\x01@\no\x86\xae\x82Q\xa8=\xa4\x0c\x80\xe70\xbd\x10jh\xbd\
 \x07R\x06#\xc9^N\xb6\xde\x03)\x83\x18\xaeU\x90\x9c>a\xb2P\r\xb3&/Y\xa8\xd1^^\
 \xb6\xf0\x16\xdb\xbf\xf1\x02\x81\xa5TK\x1d\x07\xde\x92\x00\x00\x00\x00IEND\
-\xaeB`\x82" 
+\xaeB`\x82"
 
 def getStepReturnBitmap():
     return BitmapFromImage(getStepReturnImage())
@@ -2576,7 +3288,7 @@ def getStepReturnImage():
 
 def getStepReturnIcon():
     return wx.IconFromBitmap(getStepReturnBitmap())
-    
+
 #----------------------------------------------------------------------
 def getAddWatchData():
     return \
@@ -2587,7 +3299,7 @@ def getAddWatchData():
 \xeb,!w\x100 \x1dK\xac\x10\r\x08\x05".yFL\x85\x8c\x18b\xa8|Ty\xa2\x13\x92\'\
 \xc3\xe4\xff\x9f\x18\x1e3\xb82t\xa2\x88\x13\xedg.\x06aa&\x06VV\x7f\x86\xb9\
 \xcfU\x19\xbc\xb0\xba\x86h\xe0\xc8\xd0\xfc\xbf\x80\xe1>q)\x94\xe6\x00\x00\
-\x85\x923_\xd22\xa4\xcd\x00\x00\x00\x00IEND\xaeB`\x82' 
+\x85\x923_\xd22\xa4\xcd\x00\x00\x00\x00IEND\xaeB`\x82'
 
 def getAddWatchBitmap():
     return BitmapFromImage(getAddWatchImage())
@@ -2598,7 +3310,7 @@ def getAddWatchImage():
 
 def getAddWatchIcon():
     return wx.IconFromBitmap(getAddWatchBitmap())
-    
+
 #----------------------------------------------------------------------
 def getRunningManData():
     return \
@@ -2619,7 +3331,7 @@ CF\xe2t\xef\x1b>\x1f\x8c3Q\xf0\x11\xd3p\xa2yf\x1a\xbc\xcb\n\xdee\x85\xdd>\
 \x03[\xb0.XP\xcafu^m\x04>\x18\xd7\x9aM\xe4\xea\xba\xc0x\xec\x8c\xa9\xca*^\
 \xa5\x1b}\xc0u*\xc9B\xd14\x12\xe8\x97%\x15\xcbF`\xdaH\xba\x80P4\r)\x13#R\xc6\
 \xf0\xdc\x8f2\x01\x80\x94\x89\xe9>\xc9(\xcd:\xb6\xd9\x1aw\xa0\x95i\xf8\x0e\
-\xc6\xd1\'\'\x86\xa2\xd5\x8d \xbe@\x00\x00\x00\x00IEND\xaeB`\x82' 
+\xc6\xd1\'\'\x86\xa2\xd5\x8d \xbe@\x00\x00\x00\x00IEND\xaeB`\x82'
 
 def getRunningManBitmap():
     return BitmapFromImage(getRunningManImage())
@@ -2655,7 +3367,7 @@ f?*4\xd1\xf6\xa2\x0f\x80\x93\xf4\x8e\xe1\xb8\xf2\xf1\xb5\x18\x9cH(\x80\xe4bT\
 \x8d\xa9\x9cf\x1dq\x9au\xc4\x8dM\x0c\x85#\xa2x\x91cw\xd2\xd6i\x83\trk\x13\
 \x9f\x0fL\xab\xda\xe6\xd4\xd6Y+\xf1h\x8f\xb9T~G\xd2\x11\xb4\xd4\xe7O[\xf7\
 \x1e\xd6\x9d\xc7\xe4\xb7\xbe\x86\xf8\xb1?\xf4\x9c\xff\x01\xbe\xe9\xaf\x96\
-\xf0\x7fPA\x00\x00\x00\x00IEND\xaeB`\x82' 
+\xf0\x7fPA\x00\x00\x00\x00IEND\xaeB`\x82'
 
 def getDebuggingManBitmap():
     return BitmapFromImage(getDebuggingManImage())
index 32e4fcffeb105b6a0ebfc9104dbe2ab8f2b341ea..535c46eee898ddd926c0ef07ce1b3b006dfaa2a5 100644 (file)
@@ -6,7 +6,7 @@
 #
 # Created:      5/23/05
 # CVS-ID:       $ID:$
-# Copyright:    (c) 2005 ActiveGrid, Inc.
+# Copyright:    (c) 2005-2006 ActiveGrid, Inc.
 # License:      wxWindows License
 #----------------------------------------------------------------------------
 
@@ -17,27 +17,22 @@ import ProjectEditor
 import os
 import os.path
 import activegrid.util.xmlutils as xmlutils
-
 _ = wx.GetTranslation
 
 
+#----------------------------------------------------------------------------
+# Constants
+#----------------------------------------------------------------------------
 SPACE = 10
 HALF_SPACE = 5
 
 
-EXTENSIONS_CONFIG_STRING = "Extensions"
-
-
-
-# TODO: Redo extensions menu on OK, or provide alert that it won't happen until restart
-
-
 #----------------------------------------------------------------------------
 # Classes
 #----------------------------------------------------------------------------
 
 class Extension:
-    
+
 
     def __init__(self, menuItemName=None):
         self.menuItemName = menuItemName
@@ -48,7 +43,7 @@ class Extension:
         self.commandPostArgs = ''
         self.fileExt = None
         self.opOnSelectedFile = True
-        
+
 
 class ExtensionService(wx.lib.pydocview.DocService):
 
@@ -60,8 +55,8 @@ class ExtensionService(wx.lib.pydocview.DocService):
 
     def __getExtensionKeyName(extensionName):
         return "%s/%s" % (ExtensionService.EXTENSIONS_KEY, extensionName)
-      
-  
+
+
     __getExtensionKeyName = staticmethod(__getExtensionKeyName)
 
 
@@ -79,7 +74,7 @@ class ExtensionService(wx.lib.pydocview.DocService):
                 cont, value, index = config.GetNextEntry(index)
         finally:
             config.SetPath(path)
-                    
+
         for extensionName in extensionNames:
             extensionData = config.Read(self.__getExtensionKeyName(extensionName))
             if extensionData:
@@ -112,10 +107,10 @@ class ExtensionService(wx.lib.pydocview.DocService):
             toolsMenu = menuBar.GetMenu(toolsMenuIndex)
         else:
             toolsMenu = wx.Menu()
-        
+
         if self._extensions:
             if toolsMenu.GetMenuItems():
-                toolsMenu.AppendSeparator()            
+                toolsMenu.AppendSeparator()
             for ext in self._extensions:
                 # Append a tool menu item for each extension
                 ext.id = wx.NewId()
@@ -192,7 +187,7 @@ class ExtensionService(wx.lib.pydocview.DocService):
             if extension.commandPostArgs:
                 cmds.append(extension.commandPostArgs)
             os.spawnv(os.P_NOWAIT, extension.command, cmds)
-                      
+
         else:
             cmd = extension.command
             if extension.commandPreArgs:
@@ -207,24 +202,24 @@ class ExtensionService(wx.lib.pydocview.DocService):
                 view.AddLines(line)
             view.GetControl().EnsureCaretVisible()
             f.close()
-            
+
 
 class ExtensionOptionsPanel(wx.Panel):
 
 
     def __init__(self, parent, id):
         wx.Panel.__init__(self, parent, id)
-        
+
         extOptionsPanelBorderSizer = wx.BoxSizer(wx.VERTICAL)
-        
+
         extOptionsPanelSizer = wx.BoxSizer(wx.HORIZONTAL)
-        
+
         extCtrlSizer = wx.BoxSizer(wx.VERTICAL)
         extCtrlSizer.Add(wx.StaticText(self, -1, _("External Tools:")), 0, wx.BOTTOM, HALF_SPACE)
-        self._extListBox = wx.ListBox(self, -1, size=(-1,160), style=wx.LB_SINGLE)
+        self._extListBox = wx.ListBox(self, -1, style=wx.LB_SINGLE)
         self.Bind(wx.EVT_LISTBOX, self.OnListBoxSelect, self._extListBox)
-        extCtrlSizer.Add(self._extListBox, 1, wx.BOTTOM | wx.EXPAND, SPACE)        
-        buttonSizer = wx.GridSizer(cols=2, vgap=5, hgap=10)
+        extCtrlSizer.Add(self._extListBox, 1, wx.BOTTOM | wx.EXPAND, SPACE)
+        buttonSizer = wx.GridSizer(cols=2, vgap=HALF_SPACE, hgap=HALF_SPACE)
         self._moveUpButton = wx.Button(self, -1, _("Move Up"))
         self.Bind(wx.EVT_BUTTON, self.OnMoveUp, self._moveUpButton)
         buttonSizer.Add(self._moveUpButton, 1, wx.EXPAND)
@@ -243,21 +238,21 @@ class ExtensionOptionsPanel(wx.Panel):
         self._extDetailPanel = wx.Panel(self)
         staticBox = wx.StaticBox(self, label=_("Selected External Tool"))
         staticBoxSizer = wx.StaticBoxSizer(staticBox, wx.VERTICAL)
-        
-        extDetailSizer = wx.FlexGridSizer(cols=2, hgap=5, vgap=3)
+
+        extDetailSizer = wx.FlexGridSizer(cols=2, vgap=5, hgap=5)
         extDetailSizer.AddGrowableCol(1,1)
 
-        extDetailSizer.Add(wx.StaticText(self._extDetailPanel, -1, _("Menu Item Name:")))
+        extDetailSizer.Add(wx.StaticText(self._extDetailPanel, -1, _("Menu Item Name:")), flag=wx.ALIGN_CENTER_VERTICAL)
         self._menuItemNameTextCtrl = wx.TextCtrl(self._extDetailPanel, -1, size = (-1, -1))
         extDetailSizer.Add(self._menuItemNameTextCtrl, 0, wx.EXPAND)
-        self.Bind(wx.EVT_TEXT, self.SaveCurrentItem, self._menuItemNameTextCtrl)        
+        self.Bind(wx.EVT_TEXT, self.SaveCurrentItem, self._menuItemNameTextCtrl)
 
-        extDetailSizer.Add(wx.StaticText(self._extDetailPanel, -1, _("Menu Item Description:")))
+        extDetailSizer.Add(wx.StaticText(self._extDetailPanel, -1, _("Menu Item Description:")), flag=wx.ALIGN_CENTER_VERTICAL)
         self._menuItemDescTextCtrl = wx.TextCtrl(self._extDetailPanel, -1, size = (-1, -1))
         extDetailSizer.Add(self._menuItemDescTextCtrl, 0, wx.EXPAND)
 
-        extDetailSizer.Add(wx.StaticText(self._extDetailPanel, -1, _("Command Path:")))
-        self._commandTextCtrl = wx.TextCtrl(self._extDetailPanel, -1, size = (-1, -1))        
+        extDetailSizer.Add(wx.StaticText(self._extDetailPanel, -1, _("Command Path:")), flag=wx.ALIGN_CENTER_VERTICAL)
+        self._commandTextCtrl = wx.TextCtrl(self._extDetailPanel, -1, size = (-1, -1))
         findFileButton = wx.Button(self._extDetailPanel, -1, _("Browse..."))
         def OnBrowseButton(event):
             fileDlg = wx.FileDialog(self, _("Choose an Executable:"), style=wx.OPEN|wx.FILE_MUST_EXIST|wx.HIDE_READONLY|wx.CHANGE_DIR)
@@ -276,26 +271,26 @@ class ExtensionOptionsPanel(wx.Panel):
         hsizer.Add(findFileButton, 0, wx.LEFT, HALF_SPACE)
         extDetailSizer.Add(hsizer, 0, wx.EXPAND)
 
-        extDetailSizer.Add(wx.StaticText(self._extDetailPanel, -1, _("Command Pre Args:")))
+        extDetailSizer.Add(wx.StaticText(self._extDetailPanel, -1, _("Command Pre Args:")), flag=wx.ALIGN_CENTER_VERTICAL)
         self._commandPreArgsTextCtrl = wx.TextCtrl(self._extDetailPanel, -1, size = (-1, -1))
         extDetailSizer.Add(self._commandPreArgsTextCtrl, 0, wx.EXPAND)
 
-        extDetailSizer.Add(wx.StaticText(self._extDetailPanel, -1, _("Command Post Args:")))
+        extDetailSizer.Add(wx.StaticText(self._extDetailPanel, -1, _("Command Post Args:")), flag=wx.ALIGN_CENTER_VERTICAL)
         self._commandPostArgsTextCtrl = wx.TextCtrl(self._extDetailPanel, -1, size = (-1, -1))
         extDetailSizer.Add(self._commandPostArgsTextCtrl, 0, wx.EXPAND)
 
-        extDetailSizer.Add(wx.StaticText(self._extDetailPanel, -1, _("File Extensions:")))
+        extDetailSizer.Add(wx.StaticText(self._extDetailPanel, -1, _("File Extensions:")), flag=wx.ALIGN_CENTER_VERTICAL)
         self._fileExtTextCtrl = wx.TextCtrl(self._extDetailPanel, -1, size = (-1, -1))
         self._fileExtTextCtrl.SetToolTipString(_("""For example: "txt, text" (comma separated) or "*" for all files"""))
         extDetailSizer.Add(self._fileExtTextCtrl, 0, wx.EXPAND)
 
         self._selFileCtrl = wx.CheckBox(self._extDetailPanel, -1, _("Operate on Selected File"))
-        extDetailSizer.Add(self._selFileCtrl)
+        extDetailSizer.Add(self._selFileCtrl, 0, wx.ALIGN_CENTER_VERTICAL|wx.TOP, SPACE)
         self._selFileCtrl.SetToolTipString(_("If focus is in the project, instead of operating on the project file, operate on the selected file."))
 
         self._extDetailPanel.SetSizer(extDetailSizer)
         staticBoxSizer.Add(self._extDetailPanel, 1, wx.ALL|wx.EXPAND, SPACE)
-        
+
         extOptionsPanelSizer.Add(staticBoxSizer, 1, wx.LEFT|wx.EXPAND, SPACE)
 
         extOptionsPanelBorderSizer.Add(extOptionsPanelSizer, 1, wx.ALL|wx.EXPAND, SPACE)
@@ -306,8 +301,8 @@ class ExtensionOptionsPanel(wx.Panel):
         self.OnListBoxSelect()
 
         self.Layout()
-        
-        parent.AddPage(self, _("External Tools"))        
+
+        parent.AddPage(self, _("External Tools"))
 
 
     def OnOK(self, optionsDialog):
@@ -323,7 +318,7 @@ class ExtensionOptionsPanel(wx.Panel):
                           msgTitle,
                           wx.OK | wx.ICON_INFORMATION,
                           self.GetParent())
-        
+
 
     def PopulateItems(self):
         extensionsService = wx.GetApp().GetService(ExtensionService)
@@ -335,7 +330,7 @@ class ExtensionOptionsPanel(wx.Panel):
         self._currentItem = None
         self._currentItemIndex = -1
         return len(self._extensions)
-      
+
 
     def OnListBoxSelect(self, event=None):
         self.SaveCurrentItem()
@@ -370,7 +365,7 @@ class ExtensionOptionsPanel(wx.Panel):
             else:
                 extension.fileExt = fileExt.split(',')
             extension.opOnSelectedFile = self._selFileCtrl.GetValue()
-            
+
 
     def LoadItem(self, extension):
         if extension:
@@ -401,8 +396,8 @@ class ExtensionOptionsPanel(wx.Panel):
             self._fileExtTextCtrl.SetValue('')
             self._selFileCtrl.SetValue(True)
             self._extDetailPanel.Enable(False)
-            
-      
+
+
     def OnAdd(self, event):
         self.SaveCurrentItem()
         name = _("Untitled")
@@ -417,11 +412,11 @@ class ExtensionOptionsPanel(wx.Panel):
         self.OnListBoxSelect()
         self._menuItemNameTextCtrl.SetFocus()
         self._menuItemNameTextCtrl.SetSelection(-1, -1)
-        
+
 
     def OnDelete(self, event):
         self._extListBox.Delete(self._currentItemIndex)
-        self._extensions.remove(self._currentItem)    
+        self._extensions.remove(self._currentItem)
         self._currentItemIndex = min(self._currentItemIndex, self._extListBox.GetCount() - 1)
         if self._currentItemIndex > -1:
             self._extListBox.SetSelection(self._currentItemIndex)
index 1fd557b00252dae40e8f10661fbb523c8cdeaa5f..da4ed91e19391203c2869e77fa0ad8c2a7e2a04a 100644 (file)
@@ -27,6 +27,7 @@ _ = wx.GetTranslation
 #----------------------------------------------------------------------------
 FILENAME_MARKER = _("Found in file: ")
 PROJECT_MARKER = _("Searching project: ")
+FILE_MARKER = _("Searching file: ")
 FIND_MATCHDIR = "FindMatchDir"
 FIND_MATCHDIRSUBFOLDERS = "FindMatchDirSubfolders"
 
@@ -39,6 +40,7 @@ class FindInDirService(FindService.FindService):
     #----------------------------------------------------------------------------
     # Constants
     #----------------------------------------------------------------------------
+    FINDFILE_ID = wx.NewId()        # for bringing up Find in File dialog box
     FINDALL_ID = wx.NewId()         # for bringing up Find All dialog box
     FINDDIR_ID = wx.NewId()         # for bringing up Find Dir dialog box
 
@@ -47,29 +49,39 @@ class FindInDirService(FindService.FindService):
         FindService.FindService.InstallControls(self, frame, menuBar, toolBar, statusBar, document)
 
         editMenu = menuBar.GetMenu(menuBar.FindMenu(_("&Edit")))
+        wx.EVT_MENU(frame, FindInDirService.FINDFILE_ID, self.ProcessEvent)
+        wx.EVT_UPDATE_UI(frame, FindInDirService.FINDFILE_ID, self.ProcessUpdateUIEvent)
+        editMenu.Append(FindInDirService.FINDFILE_ID, _("Find in File...\tCtrl+Shift+F"), _("Searches for the specified text in the current file"))
         wx.EVT_MENU(frame, FindInDirService.FINDALL_ID, self.ProcessEvent)
         wx.EVT_UPDATE_UI(frame, FindInDirService.FINDALL_ID, self.ProcessUpdateUIEvent)
-        editMenu.Append(FindInDirService.FINDALL_ID, _("Find in Project...\tCtrl+Shift+F"), _("Searches for the specified text in all the files in the project"))
+        editMenu.Append(FindInDirService.FINDALL_ID, _("Find in Project...\tCtrl+Shift+P"), _("Searches for the specified text in all the files in the project"))
         wx.EVT_MENU(frame, FindInDirService.FINDDIR_ID, self.ProcessEvent)
         wx.EVT_UPDATE_UI(frame, FindInDirService.FINDDIR_ID, self.ProcessUpdateUIEvent)
-        editMenu.Append(FindInDirService.FINDDIR_ID, _("Find in Directory..."), _("Searches for the specified text in all the files in the directory"))
+        editMenu.Append(FindInDirService.FINDDIR_ID, _("Find in Directory...\tCtrl+Shift+D"), _("Searches for the specified text in all the files in the directory"))
 
 
     def ProcessEvent(self, event):
         id = event.GetId()
-        if id == FindInDirService.FINDALL_ID:
+        if id == FindInDirService.FINDFILE_ID:
             view = wx.GetApp().GetDocumentManager().GetCurrentView()
             if hasattr(view, "GetCtrl") and view.GetCtrl() and hasattr(view.GetCtrl(), "GetSelectedText"):
-                self.ShowFindAllDialog(view.GetCtrl().GetSelectedText())
+                self.ShowFindInFileDialog(view.GetCtrl().GetSelectedText())
             else:
-                self.ShowFindAllDialog()
+                self.ShowFindInFileDialog()
+            return True
+        elif id == FindInDirService.FINDALL_ID:
+            view = wx.GetApp().GetDocumentManager().GetCurrentView()
+            if hasattr(view, "GetCtrl") and view.GetCtrl() and hasattr(view.GetCtrl(), "GetSelectedText"):
+                self.ShowFindInProjectDialog(view.GetCtrl().GetSelectedText())
+            else:
+                self.ShowFindInProjectDialog()
             return True
         elif id == FindInDirService.FINDDIR_ID:
             view = wx.GetApp().GetDocumentManager().GetCurrentView()
             if hasattr(view, "GetCtrl") and view.GetCtrl() and hasattr(view.GetCtrl(), "GetSelectedText"):
-                self.ShowFindDirDialog(view.GetCtrl().GetSelectedText())
+                self.ShowFindInDirDialog(view.GetCtrl().GetSelectedText())
             else:
-                self.ShowFindDirDialog()
+                self.ShowFindInDirDialog()
             return True
         else:
             return FindService.FindService.ProcessEvent(self, event)
@@ -77,7 +89,14 @@ class FindInDirService(FindService.FindService):
 
     def ProcessUpdateUIEvent(self, event):
         id = event.GetId()
-        if id == FindInDirService.FINDALL_ID:
+        if id == FindInDirService.FINDFILE_ID:
+            view = wx.GetApp().GetDocumentManager().GetCurrentView()
+            if view and view.GetDocument() and not isinstance(view.GetDocument(), ProjectEditor.ProjectDocument):  # don't search project model
+                event.Enable(True)
+            else:
+                event.Enable(False)
+            return True
+        elif id == FindInDirService.FINDALL_ID:
             projectService = wx.GetApp().GetService(ProjectEditor.ProjectService)
             if projectService.GetFilesFromCurrentProject():
                 event.Enable(True)
@@ -90,7 +109,7 @@ class FindInDirService(FindService.FindService):
             return FindService.FindService.ProcessUpdateUIEvent(self, event)
 
 
-    def ShowFindDirDialog(self, findString=None):
+    def ShowFindInDirDialog(self, findString=None):
         config = wx.ConfigBase_Get()
 
         frame = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("Find in Directory"), size= (320,200))
@@ -105,7 +124,7 @@ class FindInDirService(FindService.FindService):
         findDirButton = wx.Button(frame, -1, _("Browse..."))
         lineSizer.Add(findDirButton, 0, wx.LEFT, HALF_SPACE)
         contentSizer.Add(lineSizer, 0, wx.BOTTOM, SPACE)
-        
+
         def OnBrowseButton(event):
             dlg = wx.DirDialog(frame, _("Choose a directory:"), style=wx.DD_DEFAULT_STYLE)
             dir = dirCtrl.GetValue()
@@ -126,10 +145,10 @@ class FindInDirService(FindService.FindService):
         lineSizer = wx.BoxSizer(wx.VERTICAL)    # let the line expand horizontally without vertical expansion
         lineSizer.Add(wx.StaticLine(frame, -1, size = (10,-1)), 0, flag=wx.EXPAND)
         contentSizer.Add(lineSizer, flag=wx.EXPAND|wx.ALIGN_CENTER_VERTICAL|wx.BOTTOM, border=HALF_SPACE)
-        
+
         if wx.Platform == "__WXMAC__":
             contentSizer.Add((-1, 10), 0, wx.EXPAND)
-        
+
         lineSizer = wx.BoxSizer(wx.HORIZONTAL)
         lineSizer.Add(wx.StaticText(frame, -1, _("Find what:")), 0, wx.ALIGN_CENTER | wx.RIGHT, HALF_SPACE)
         if not findString:
@@ -192,13 +211,13 @@ class FindInDirService(FindService.FindService):
                 status = frame.ShowModal()
             else:
                 passedCheck = True
-    
+
 
         # save user choice state for this and other Find Dialog Boxes
         dirString = dirCtrl.GetValue()
         searchSubfolders = subfolderCtrl.IsChecked()
-        self.SaveFindDirConfig(dirString, searchSubfolders)
-        
+        self.SaveFindInDirConfig(dirString, searchSubfolders)
+
         findString = findCtrl.GetValue()
         matchCase = matchCaseCtrl.IsChecked()
         wholeWord = wholeWordCtrl.IsChecked()
@@ -206,52 +225,23 @@ class FindInDirService(FindService.FindService):
         self.SaveFindConfig(findString, wholeWord, matchCase, regExpr)
 
         frame.Destroy()
-        if status == wx.ID_OK:            
+        if status == wx.ID_OK:
             messageService = wx.GetApp().GetService(MessageService.MessageService)
             messageService.ShowWindow()
 
             view = messageService.GetView()
             if view:
                 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
-                view.ClearLines()
-                view.SetCallback(self.OnJumpToFoundLine)
-
-                view.AddLines(_("Searching for '%s' in '%s'\n\n") % (findString, dirString))
                 
-                if os.path.isfile(dirString):
-                    try:
-                        docFile = file(dirString, 'r')
-                        lineNum = 1
-                        needToDisplayFilename = True
-                        line = docFile.readline()
-                        while line:
-                            count, foundStart, foundEnd, newText = self.DoFind(findString, None, line, 0, 0, True, matchCase, wholeWord, regExpr)
-                            if count != -1:
-                                if needToDisplayFilename:
-                                    view.AddLines(FILENAME_MARKER + dirString + "\n")
-                                    needToDisplayFilename = False
-                                line = repr(lineNum).zfill(4) + ":" + line
-                                view.AddLines(line)
-                            line = docFile.readline()
-                            lineNum += 1
-                        if not needToDisplayFilename:
-                            view.AddLines("\n")
-                    except IOError, (code, message):
-                        print _("Warning, unable to read file: '%s'.  %s") % (dirString, message)
-                else:
-                    # do search in files on disk
-                    for root, dirs, files in os.walk(dirString):
-                        if not searchSubfolders and root != dirString:
-                            break
-                            
-                        for name in files:
-                            filename = os.path.join(root, name)
-                            try:
-                                docFile = file(filename, 'r')
-                            except IOError, (code, message):
-                                print _("Warning, unable to read file: '%s'.  %s") % (filename, message)
-                                continue
-        
+                try:
+                    view.ClearLines()
+                    view.SetCallback(self.OnJumpToFoundLine)
+    
+                    view.AddLines(_("Searching for '%s' in '%s'\n\n") % (findString, dirString))
+    
+                    if os.path.isfile(dirString):
+                        try:
+                            docFile = file(dirString, 'r')
                             lineNum = 1
                             needToDisplayFilename = True
                             line = docFile.readline()
@@ -259,7 +249,7 @@ class FindInDirService(FindService.FindService):
                                 count, foundStart, foundEnd, newText = self.DoFind(findString, None, line, 0, 0, True, matchCase, wholeWord, regExpr)
                                 if count != -1:
                                     if needToDisplayFilename:
-                                        view.AddLines(FILENAME_MARKER + filename + "\n")
+                                        view.AddLines(FILENAME_MARKER + dirString + "\n")
                                         needToDisplayFilename = False
                                     line = repr(lineNum).zfill(4) + ":" + line
                                     view.AddLines(line)
@@ -267,27 +257,186 @@ class FindInDirService(FindService.FindService):
                                 lineNum += 1
                             if not needToDisplayFilename:
                                 view.AddLines("\n")
-
-                view.AddLines(_("Search completed."))
-                wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
+                        except IOError, (code, message):
+                            print _("Warning, unable to read file: '%s'.  %s") % (dirString, message)
+                    else:
+                        # do search in files on disk
+                        for root, dirs, files in os.walk(dirString):
+                            if not searchSubfolders and root != dirString:
+                                break
+    
+                            for name in files:
+                                filename = os.path.join(root, name)
+                                try:
+                                    docFile = file(filename, 'r')
+                                except IOError, (code, message):
+                                    print _("Warning, unable to read file: '%s'.  %s") % (filename, message)
+                                    continue
+    
+                                lineNum = 1
+                                needToDisplayFilename = True
+                                line = docFile.readline()
+                                while line:
+                                    count, foundStart, foundEnd, newText = self.DoFind(findString, None, line, 0, 0, True, matchCase, wholeWord, regExpr)
+                                    if count != -1:
+                                        if needToDisplayFilename:
+                                            view.AddLines(FILENAME_MARKER + filename + "\n")
+                                            needToDisplayFilename = False
+                                        line = repr(lineNum).zfill(4) + ":" + line
+                                        view.AddLines(line)
+                                    line = docFile.readline()
+                                    lineNum += 1
+                                if not needToDisplayFilename:
+                                    view.AddLines("\n")
+    
+                    view.AddLines(_("Search completed."))
+                
+                finally:
+                    wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
 
             return True
         else:
             return False
-            
 
-    def SaveFindDirConfig(self, dirString, searchSubfolders):
+
+    def SaveFindInDirConfig(self, dirString, searchSubfolders):
         """ Save search dir patterns and flags to registry.
-        
+
             dirString = search directory
             searchSubfolders = Search subfolders
         """
         config = wx.ConfigBase_Get()
         config.Write(FIND_MATCHDIR, dirString)
         config.WriteInt(FIND_MATCHDIRSUBFOLDERS, searchSubfolders)
+
+
+    def DoFindIn(self, findString, matchCase, wholeWord, regExpr, currFileOnly=False, jumpToFound=False):
+        messageService = wx.GetApp().GetService(MessageService.MessageService)
+        if not messageService:
+            return
+
+        messageService.ShowWindow()
+
+        view = messageService.GetView()
+        if not view:
+            return
+
+        wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
+        
+        try:
+            #Switch to messages tab.
+            view.GetControl().GetParent().SetSelection(0) 
+            view.ClearLines()
+            view.SetCallback(self.OnJumpToFoundLine)
+    
+            projectService = wx.GetApp().GetService(ProjectEditor.ProjectService)
+    
+            if wx.GetApp().GetDocumentManager().GetCurrentView():
+                currDoc = wx.GetApp().GetDocumentManager().GetCurrentView().GetDocument()
+            else:
+                currDoc = None
+            if currFileOnly:
+                if currDoc:
+                    projectFilenames = [currDoc.GetFilename()]
+                    view.AddLines(FILE_MARKER + currDoc.GetFilename() + "\n\n")
+                else:
+                    projectFilenames = []
+            else:
+                projectFilenames = projectService.GetFilesFromCurrentProject()
+    
+                projView = projectService.GetView()
+                if projView:
+                    projName = wx.lib.docview.FileNameFromPath(projView.GetDocument().GetFilename())
+                    view.AddLines(PROJECT_MARKER + projName + "\n\n")
+    
+            firstDef = -1
+    
+            # do search in open files first, open files may have been modified and different from disk because it hasn't been saved
+            openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
+            openDocsInProject = filter(lambda openDoc: openDoc.GetFilename() in projectFilenames, openDocs)
+            if currDoc and currDoc in openDocsInProject:
+                # make sure current document is searched first.
+                openDocsInProject.remove(currDoc)
+                openDocsInProject.insert(0, currDoc)
+            for openDoc in openDocsInProject:
+                if isinstance(openDoc, ProjectEditor.ProjectDocument):  # don't search project model
+                    continue
+    
+                openDocView = openDoc.GetFirstView()
+                # some views don't have a in memory text object to search through such as the PM and the DM
+                # even if they do have a non-text searchable object, how do we display it in the message window?
+                if not hasattr(openDocView, "GetValue"):
+                    continue
+                text = openDocView.GetValue()
+    
+                lineNum = 1
+                needToDisplayFilename = True
+                start = 0
+                end = 0
+                count = 0
+                while count != -1:
+                    count, foundStart, foundEnd, newText = self.DoFind(findString, None, text, start, end, True, matchCase, wholeWord, regExpr)
+                    if count != -1:
+                        if needToDisplayFilename:
+                            view.AddLines(FILENAME_MARKER + openDoc.GetFilename() + "\n")
+                            needToDisplayFilename = False
+    
+                        lineNum = openDocView.LineFromPosition(foundStart)
+                        line = repr(lineNum).zfill(4) + ":" + openDocView.GetLine(lineNum)
+                        view.AddLines(line)
+                        if firstDef == -1:
+                            firstDef = view.GetControl().GetCurrentLine() - 1
+    
+                        start = text.find("\n", foundStart)
+                        if start == -1:
+                            break
+                        end = start
+                if not needToDisplayFilename:
+                    view.AddLines("\n")
+                wx.GetApp().Yield(True)
+            openDocNames = map(lambda openDoc: openDoc.GetFilename(), openDocs)
+    
+            # do search in closed files, skipping the open ones we already searched
+            filenames = filter(lambda filename: filename not in openDocNames, projectFilenames)
+            for filename in filenames:
+                try:
+                    docFile = file(filename, 'r')
+                except IOError, (code, message):
+                    print _("Warning, unable to read file: '%s'.  %s") % (filename, message)
+                    continue
+    
+                lineNum = 1
+                needToDisplayFilename = True
+                line = docFile.readline()
+                while line:
+                    count, foundStart, foundEnd, newText = self.DoFind(findString, None, line, 0, 0, True, matchCase, wholeWord, regExpr)
+                    if count != -1:
+                        if needToDisplayFilename:
+                            view.AddLines(FILENAME_MARKER + filename + "\n")
+                            needToDisplayFilename = False
+                        line = repr(lineNum).zfill(4) + ":" + line
+                        view.AddLines(line)
+                        if firstDef == -1:
+                            firstDef = view.GetControl().GetCurrentLine() - 1
+                    line = docFile.readline()
+                    lineNum += 1
+                if not needToDisplayFilename:
+                    view.AddLines("\n")
+                wx.GetApp().Yield(True)
+    
+            view.AddLines(_("Search for '%s' completed.") % findString)
     
+            if jumpToFound:
+                self.OnJumpToFoundLine(event=None, defLineNum=firstDef)
 
-    def ShowFindAllDialog(self, findString=None):
+        finally:
+            wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
+
+    def FindInProject(self, findString):
+        self.DoFindIn(findString, matchCase=True, wholeWord=True, regExpr=True, jumpToFound=True)
+
+
+    def ShowFindInProjectDialog(self, findString=None):
         config = wx.ConfigBase_Get()
 
         frame = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("Find in Project"), size= (320,200))
@@ -336,97 +485,80 @@ class FindInDirService(FindService.FindService):
         self.SaveFindConfig(findString, wholeWord, matchCase, regExpr)
 
         frame.Destroy()
+
         if status == wx.ID_OK:
-            messageService = wx.GetApp().GetService(MessageService.MessageService)
-            messageService.ShowWindow()
+            self.DoFindIn(findString, matchCase, wholeWord, regExpr)
+            return True
+        else:
+            return False
 
-            view = messageService.GetView()
-            if view:
-                view.ClearLines()
-                view.SetCallback(self.OnJumpToFoundLine)
 
-                projectService = wx.GetApp().GetService(ProjectEditor.ProjectService)
-                projectFilenames = projectService.GetFilesFromCurrentProject()
+    def ShowFindInFileDialog(self, findString=None):
+        config = wx.ConfigBase_Get()
 
-                projView = projectService.GetView()
-                if projView:
-                    projName = wx.lib.docview.FileNameFromPath(projView.GetDocument().GetFilename())
-                    view.AddLines(PROJECT_MARKER + projName + "\n\n")
+        frame = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("Find in File"), size= (320,200))
+        borderSizer = wx.BoxSizer(wx.HORIZONTAL)
 
-                # do search in open files first, open files may have been modified and different from disk because it hasn't been saved
-                openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
-                openDocsInProject = filter(lambda openDoc: openDoc.GetFilename() in projectFilenames, openDocs)
-                for openDoc in openDocsInProject:
-                    if isinstance(openDoc, ProjectEditor.ProjectDocument):  # don't search project model
-                        continue
-
-                    openDocView = openDoc.GetFirstView()
-                    # some views don't have a in memory text object to search through such as the PM and the DM
-                    # even if they do have a non-text searchable object, how do we display it in the message window?
-                    if not hasattr(openDocView, "GetValue"):
-                        continue
-                    text = openDocView.GetValue()
-
-                    lineNum = 1
-                    needToDisplayFilename = True
-                    start = 0
-                    end = 0
-                    count = 0
-                    while count != -1:
-                        count, foundStart, foundEnd, newText = self.DoFind(findString, None, text, start, end, True, matchCase, wholeWord, regExpr)
-                        if count != -1:
-                            if needToDisplayFilename:
-                                view.AddLines(FILENAME_MARKER + openDoc.GetFilename() + "\n")
-                                needToDisplayFilename = False
-
-                            lineNum = openDocView.LineFromPosition(foundStart)
-                            line = repr(lineNum).zfill(4) + ":" + openDocView.GetLine(lineNum)
-                            view.AddLines(line)
-
-                            start = text.find("\n", foundStart)
-                            if start == -1:
-                                break
-                            end = start
-                    if not needToDisplayFilename:
-                        view.AddLines("\n")
-                openDocNames = map(lambda openDoc: openDoc.GetFilename(), openDocs)
-
-                # do search in closed files, skipping the open ones we already searched
-                filenames = filter(lambda filename: filename not in openDocNames, projectFilenames)
-                for filename in filenames:
-                    try:
-                        docFile = file(filename, 'r')
-                    except IOError, (code, message):
-                        print _("Warning, unable to read file: '%s'.  %s") % (filename, message)
-                        continue
-
-                    lineNum = 1
-                    needToDisplayFilename = True
-                    line = docFile.readline()
-                    while line:
-                        count, foundStart, foundEnd, newText = self.DoFind(findString, None, line, 0, 0, True, matchCase, wholeWord, regExpr)
-                        if count != -1:
-                            if needToDisplayFilename:
-                                view.AddLines(FILENAME_MARKER + filename + "\n")
-                                needToDisplayFilename = False
-                            line = repr(lineNum).zfill(4) + ":" + line
-                            view.AddLines(line)
-                        line = docFile.readline()
-                        lineNum += 1
-                    if not needToDisplayFilename:
-                        view.AddLines("\n")
-
-                view.AddLines(_("Search for '%s' completed.") % findString)
+        contentSizer = wx.BoxSizer(wx.VERTICAL)
+        lineSizer = wx.BoxSizer(wx.HORIZONTAL)
+        lineSizer.Add(wx.StaticText(frame, -1, _("Find what:")), 0, wx.ALIGN_CENTER | wx.RIGHT, HALF_SPACE)
+        if not findString:
+            findString = config.Read(FindService.FIND_MATCHPATTERN, "")
+        findCtrl = wx.TextCtrl(frame, -1, findString, size=(200,-1))
+        lineSizer.Add(findCtrl, 0, wx.LEFT, HALF_SPACE)
+        contentSizer.Add(lineSizer, 0, wx.BOTTOM, SPACE)
+        wholeWordCtrl = wx.CheckBox(frame, -1, _("Match whole word only"))
+        wholeWordCtrl.SetValue(config.ReadInt(FindService.FIND_MATCHWHOLEWORD, False))
+        matchCaseCtrl = wx.CheckBox(frame, -1, _("Match case"))
+        matchCaseCtrl.SetValue(config.ReadInt(FindService.FIND_MATCHCASE, False))
+        regExprCtrl = wx.CheckBox(frame, -1, _("Regular expression"))
+        regExprCtrl.SetValue(config.ReadInt(FindService.FIND_MATCHREGEXPR, False))
+        contentSizer.Add(wholeWordCtrl, 0, wx.BOTTOM, SPACE)
+        contentSizer.Add(matchCaseCtrl, 0, wx.BOTTOM, SPACE)
+        contentSizer.Add(regExprCtrl, 0, wx.BOTTOM, SPACE)
+        borderSizer.Add(contentSizer, 0, wx.TOP | wx.BOTTOM | wx.LEFT, SPACE)
+
+        buttonSizer = wx.BoxSizer(wx.VERTICAL)
+        findBtn = wx.Button(frame, wx.ID_OK, _("Find"))
+        findBtn.SetDefault()
+        BTM_SPACE = HALF_SPACE
+        if wx.Platform == "__WXMAC__":
+            BTM_SPACE = SPACE
+        buttonSizer.Add(findBtn, 0, wx.BOTTOM, BTM_SPACE)
+        buttonSizer.Add(wx.Button(frame, wx.ID_CANCEL), 0)
+        borderSizer.Add(buttonSizer, 0, wx.ALL, SPACE)
+
+        frame.SetSizer(borderSizer)
+        frame.Fit()
+
+        frame.CenterOnParent()
+        status = frame.ShowModal()
+
+        # save user choice state for this and other Find Dialog Boxes
+        findString = findCtrl.GetValue()
+        matchCase = matchCaseCtrl.IsChecked()
+        wholeWord = wholeWordCtrl.IsChecked()
+        regExpr = regExprCtrl.IsChecked()
+        self.SaveFindConfig(findString, wholeWord, matchCase, regExpr)
 
+        frame.Destroy()
+
+        if status == wx.ID_OK:
+            self.DoFindIn(findString, matchCase, wholeWord, regExpr, currFileOnly=True)
             return True
         else:
             return False
 
 
-    def OnJumpToFoundLine(self, event):
+    def OnJumpToFoundLine(self, event=None, defLineNum=-1):
         messageService = wx.GetApp().GetService(MessageService.MessageService)
-        lineText, pos = messageService.GetView().GetCurrLine()
-        if lineText == "\n" or lineText.find(FILENAME_MARKER) != -1 or lineText.find(PROJECT_MARKER) != -1:
+        if defLineNum == -1:
+            lineText, pos = messageService.GetView().GetCurrLine()
+        else:
+            lineText = messageService.GetView().GetControl().GetLine(defLineNum)
+            pos = 0
+
+        if lineText == "\n" or lineText.find(FILENAME_MARKER) != -1 or lineText.find(PROJECT_MARKER) != -1 or lineText.find(FILE_MARKER) != -1:
             return
         lineEnd = lineText.find(":")
         if lineEnd == -1:
@@ -435,7 +567,10 @@ class FindInDirService(FindService.FindService):
             lineNum = int(lineText[0:lineEnd])
 
         text = messageService.GetView().GetText()
-        curPos = messageService.GetView().GetCurrentPos()
+        if defLineNum == -1:
+            curPos = messageService.GetView().GetCurrentPos()
+        else:
+            curPos = messageService.GetView().GetControl().GetLineEndPosition(defLineNum)
 
         startPos = text.rfind(FILENAME_MARKER, 0, curPos)
         endPos = text.find("\n", startPos)
@@ -464,5 +599,3 @@ class FindInDirService(FindService.FindService):
                 #         time, we don't see the selection, it is scrolled off screen
                 foundView.SetSelection(startPos - 1 + len(lineText[lineEnd:].rstrip("\n")), startPos)
                 wx.GetApp().GetService(OutlineService.OutlineService).LoadOutline(foundView, position=startPos)
-
-
index bcdbe7ed7bafc4fce7e55bda182f380c4454790c..024a0b13f2f2e88a81cd90be21cabdf6e8e4e8e2 100644 (file)
@@ -119,7 +119,7 @@ class HtmlCtrl(CodeEditor.CodeCtrl):
 
 
     def SetViewDefaults(self):
-        CodeEditor.CodeCtrl.SetViewDefaults(self, configPrefix = "Html", hasWordWrap = True, hasTabs = True)
+        CodeEditor.CodeCtrl.SetViewDefaults(self, configPrefix = "Html", hasWordWrap = True, hasTabs = True, hasFolding=True)
 
 
     def GetFontAndColorFromConfig(self):
@@ -156,7 +156,7 @@ class HtmlCtrl(CodeEditor.CodeCtrl):
 class HtmlOptionsPanel(STCTextEditor.TextOptionsPanel):
 
     def __init__(self, parent, id):
-        STCTextEditor.TextOptionsPanel.__init__(self, parent, id, configPrefix = "Html", label = "HTML", hasWordWrap = True, hasTabs = True)
+        STCTextEditor.TextOptionsPanel.__init__(self, parent, id, configPrefix = "Html", label = "HTML", hasWordWrap = True, hasTabs = True, hasFolding=True)
 
 
     def GetIcon(self):
index a9c6dcc77bcc2cfa0822e52cdbca14d8bbeda292..b1567b4fd2312072cc1023ad36343f9bce5548a8 100644 (file)
@@ -18,9 +18,17 @@ import wx.grid
 import os.path
 import activegrid.util.sysutils as sysutilslib
 import activegrid.util.appdirs as appdirs
+import shutil
+
+# Required for Unicode support with python
+# site.py sets this, but Windows builds don't have site.py because of py2exe problems
+# If site.py has already run, then the setdefaultencoding function will have been deleted.
+if hasattr(sys,"setdefaultencoding"):
+    sys.setdefaultencoding("utf-8")
 
 _ = wx.GetTranslation
-ACTIVEGRID_BASE_IDE = False
+ACTIVEGRID_BASE_IDE = False 
+USE_OLD_PROJECTS = False
 #----------------------------------------------------------------------------
 # Helper functions for command line args
 #----------------------------------------------------------------------------
@@ -44,6 +52,19 @@ def isInArgs(argname, argv):
         
     return result
 
+# The default log action in wx is to prompt with a big message box
+# which is often inappropriate (for example, if the clipboard data
+# is not readable on Mac, we'll get one of these messages repeatedly)
+# so just log the errors instead.
+# NOTE: This does NOT supress fatal system errors. Only non-fatal ones.
+class AppLog(wx.PyLog):
+    def __init__(self):
+        wx.PyLog.__init__(self)
+        self.items = []
+        
+    def DoLogString(self, message, timeStamp):
+        self.items.append(str(timeStamp) + u" " + message.decode())
+
 #----------------------------------------------------------------------------
 # Classes
 #----------------------------------------------------------------------------
@@ -55,9 +76,13 @@ class IDEApplication(wx.lib.pydocview.DocApp):
 
     def OnInit(self):
         global ACTIVEGRID_BASE_IDE
-        
+        global USE_OLD_PROJECTS
         args = sys.argv
-                    
+
+        # Suppress non-fatal errors that might prompt the user even in cases
+        # when the error does not impact them.
+        wx.Log_SetActiveTarget(AppLog())
+        
         if "-h" in args or "-help" in args or "--help" in args\
             or (wx.Platform == "__WXMSW__" and "/help" in args):
             print "Usage: ActiveGridAppBuilder.py [options] [filenames]\n"
@@ -81,6 +106,8 @@ class IDEApplication(wx.lib.pydocview.DocApp):
         elif isInArgs("baseide", args):
             self.SetAppName(_("ActiveGrid IDE"))
             ACTIVEGRID_BASE_IDE = True
+        elif isInArgs("tools", args):
+            USE_OLD_PROJECTS = True
         else:
             self.SetAppName(_("ActiveGrid Application Builder"))
             self.SetDebug(False)
@@ -120,20 +147,22 @@ class IDEApplication(wx.lib.pydocview.DocApp):
         import AboutDialog
         import SVNService
         import ExtensionService
+##        import UpdateLogIniService
                             
         if not ACTIVEGRID_BASE_IDE:
             import activegrid.model.basedocmgr as basedocmgr
-            import UpdateService as updater
+            import UpdateService
             import DataModelEditor
             import ProcessModelEditor
             import DeploymentService
             import WebServerService
             import WelcomeService
-            import ViewEditor
+            import XFormEditor
             import PropertyService
             import WSDLEditor
             import WsdlAgEditor
             import XPathEditor
+            import XPathExprEditor
             import ImportServiceWizard
             import RoleEditor
             import HelpService
@@ -158,9 +187,11 @@ class IDEApplication(wx.lib.pydocview.DocApp):
         if not config.Exists("MDIEmbedRightVisible"):  # Make the properties embedded window hidden as default
             config.WriteInt("MDIEmbedRightVisible", False)
 
-        docManager = wx.lib.docview.DocManager(flags = self.GetDefaultDocManagerFlags())
+        docManager = IDEDocManager(flags = self.GetDefaultDocManagerFlags())
         self.SetDocumentManager(docManager)
 
+        # Note:  These templates must be initialized in display order for the "Files of type" dropdown for the "File | Open..." dialog
+        
         defaultTemplate = wx.lib.docview.DocTemplate(docManager,
                 _("Any"),
                 "*.*",
@@ -184,6 +215,7 @@ class IDEApplication(wx.lib.pydocview.DocApp):
                 _("Deployment View"),
                 XmlEditor.XmlDocument,
                 XmlEditor.XmlView,
+                wx.lib.docview.TEMPLATE_INVISIBLE,
                 icon = DeploymentService.getDPLIcon())
             docManager.AssociateTemplate(dplTemplate)
 
@@ -199,6 +231,20 @@ class IDEApplication(wx.lib.pydocview.DocApp):
                 icon = HtmlEditor.getHTMLIcon())
         docManager.AssociateTemplate(htmlTemplate)
 
+        if not ACTIVEGRID_BASE_IDE:
+            identityTemplate = wx.lib.docview.DocTemplate(docManager,
+                    _("Identity"),
+                    "*.xacml",
+                    _("Identity"),
+                    _(".xacml"),
+                    _("Identity Configuration"),
+                    _("Identity View"),
+                    RoleEditor.RoleEditorDocument,
+                    RoleEditor.RoleEditorView,
+                    wx.lib.docview.TEMPLATE_NO_CREATE,
+                    icon = XmlEditor.getXMLIcon())
+            docManager.AssociateTemplate(identityTemplate)
+
         imageTemplate = wx.lib.docview.DocTemplate(docManager,
                 _("Image"),
                 "*.bmp;*.ico;*.gif;*.jpg;*.jpeg;*.png",
@@ -224,7 +270,6 @@ class IDEApplication(wx.lib.pydocview.DocApp):
                     # XmlEditor.XmlView,
                     LayoutEditor.LayoutEditorDocument,
                     LayoutEditor.LayoutEditorView,
-                    wx.lib.docview.TEMPLATE_NO_CREATE,
                     icon = LayoutEditor.getLytIcon())
             docManager.AssociateTemplate(layoutTemplate)
 
@@ -262,6 +307,7 @@ class IDEApplication(wx.lib.pydocview.DocApp):
                 _("Process View"),
                 ProcessModelEditor.ProcessModelDocument,
                 ProcessModelEditor.ProcessModelView,
+                wx.lib.docview.TEMPLATE_NO_CREATE,
                 icon = ProcessModelEditor.getProcessModelIcon())
             docManager.AssociateTemplate(processModelTemplate)
 
@@ -274,6 +320,7 @@ class IDEApplication(wx.lib.pydocview.DocApp):
                 _("Project View"),
                 ProjectEditor.ProjectDocument,
                 ProjectEditor.ProjectView,
+                wx.lib.docview.TEMPLATE_NO_CREATE,
                 icon = ProjectEditor.getProjectIcon())
         docManager.AssociateTemplate(projectTemplate)
 
@@ -302,6 +349,20 @@ class IDEApplication(wx.lib.pydocview.DocApp):
                 icon = DataModelEditor.getDataModelIcon())
             docManager.AssociateTemplate(dataModelTemplate)
             
+        if not ACTIVEGRID_BASE_IDE:
+            wsdlagTemplate = wx.lib.docview.DocTemplate(docManager,
+                    _("Service Reference"),
+                    "*.wsdlag",
+                    _("Project"),
+                    _(".wsdlag"),
+                    _("Service Reference Document"),
+                    _("Service Reference View"),
+                    WsdlAgEditor.WsdlAgDocument,
+                    WsdlAgEditor.WsdlAgView,
+                    wx.lib.docview.TEMPLATE_NO_CREATE,
+                    icon = WSDLEditor.getWSDLIcon())
+            docManager.AssociateTemplate(wsdlagTemplate)
+
         if not ACTIVEGRID_BASE_IDE and _EDIT_LAYOUTS:
             layoutTemplate = wx.lib.docview.DocTemplate(docManager,
                     _("Skin"),
@@ -317,18 +378,18 @@ class IDEApplication(wx.lib.pydocview.DocApp):
             docManager.AssociateTemplate(layoutTemplate)
 
         if not ACTIVEGRID_BASE_IDE:
-            identityTemplate = wx.lib.docview.DocTemplate(docManager,
-                    _("Identity"),
-                    "*.xacml",
-                    _("Identity"),
-                    _(".xacml"),
-                    _("Identity Configuration"),
-                    _("Identity View"),
-                    RoleEditor.RoleEditorDocument,
-                    RoleEditor.RoleEditorView,
+            sqlTemplate = wx.lib.docview.DocTemplate(docManager,
+                    _("SQL"),
+                    "*.sql",
+                    _("SQL"),
+                    _(".sql"),
+                    _("SQL Document"),
+                    _("SQL View"),
+                    SQLEditor.SQLDocument,
+                    SQLEditor.SQLView,
                     wx.lib.docview.TEMPLATE_NO_CREATE,
-                    icon = XmlEditor.getXMLIcon())
-            docManager.AssociateTemplate(identityTemplate)
+                    icon = SQLEditor.getSQLIcon())
+            docManager.AssociateTemplate(sqlTemplate)
 
         textTemplate = wx.lib.docview.DocTemplate(docManager,
                 _("Text"),
@@ -343,33 +404,6 @@ class IDEApplication(wx.lib.pydocview.DocApp):
         docManager.AssociateTemplate(textTemplate)
 
         if not ACTIVEGRID_BASE_IDE:
-            sqlTemplate = wx.lib.docview.DocTemplate(docManager,
-                    _("SQL"),
-                    "*.sql",
-                    _("SQL"),
-                    _(".sql"),
-                    _("SQL Document"),
-                    _("SQL View"),
-                    SQLEditor.SQLDocument,
-                    SQLEditor.SQLView,
-                    wx.lib.docview.TEMPLATE_NO_CREATE,
-                    icon = SQLEditor.getSQLIcon())
-            docManager.AssociateTemplate(sqlTemplate)
-
-
-            wsdlagTemplate = wx.lib.docview.DocTemplate(docManager,
-                    _("Service Reference"),
-                    "*.wsdlag",
-                    _("Project"),
-                    _(".wsdlag"),
-                    _("Service Reference Document"),
-                    _("Service Reference View"),
-                    WsdlAgEditor.WsdlAgDocument,
-                    WsdlAgEditor.WsdlAgView,
-                    wx.lib.docview.TEMPLATE_NO_CREATE,
-                    icon = WSDLEditor.getWSDLIcon())
-            docManager.AssociateTemplate(wsdlagTemplate)
-
             wsdlTemplate = WSDLEditor.WSDLTemplate(docManager,
                     _("WSDL"),
                     "*.wsdl",
@@ -379,9 +413,11 @@ class IDEApplication(wx.lib.pydocview.DocApp):
                     _("WSDL View"),
                     WSDLEditor.WSDLDocument,
                     WSDLEditor.WSDLView,
+                    wx.lib.docview.TEMPLATE_NO_CREATE,
                     icon = WSDLEditor.getWSDLIcon())
             docManager.AssociateTemplate(wsdlTemplate)
 
+        if not ACTIVEGRID_BASE_IDE:
             xformTemplate = wx.lib.docview.DocTemplate(docManager,
                     _("XForm"),
                     "*.xform",
@@ -389,10 +425,10 @@ class IDEApplication(wx.lib.pydocview.DocApp):
                     _(".xform"),
                     _("XForm Document"),
                     _("XForm View"),
-                    ViewEditor.ViewEditorDocument,
-                    ViewEditor.ViewEditorView,
+                    XFormEditor.XFormDocument,
+                    XFormEditor.XFormView,
                     wx.lib.docview.TEMPLATE_NO_CREATE,
-                    icon = ViewEditor.getXFORMIcon())
+                    icon = XFormEditor.getXFormIcon())
             docManager.AssociateTemplate(xformTemplate)
 
         xmlTemplate = wx.lib.docview.DocTemplate(docManager,
@@ -407,19 +443,22 @@ class IDEApplication(wx.lib.pydocview.DocApp):
                 icon = XmlEditor.getXMLIcon())
         docManager.AssociateTemplate(xmlTemplate)
 
+        
+        # Note:  Child document types aren't displayed in "Files of type" dropdown
         if not ACTIVEGRID_BASE_IDE:
             viewTemplate = wx.lib.pydocview.ChildDocTemplate(docManager,
-                _("View"),
+                _("XForm"),
                 "*.none",
-                _("View"),
+                _("XForm"),
                 _(".bpel"),
-                _("ViewEditor Document"),
-                _("ViewEditor View"),
-                ViewEditor.ViewEditorDocument,
-                ViewEditor.ViewEditorView,
-                icon = ProcessModelEditor.getXFORMIcon())
+                _("XFormEditor Document"),
+                _("XFormEditor View"),
+                XFormEditor.XFormDocument,
+                XFormEditor.XFormView,
+                icon = XFormEditor.getXFormIcon())
             docManager.AssociateTemplate(viewTemplate)
 
+        if not ACTIVEGRID_BASE_IDE:
             bpelTemplate = wx.lib.pydocview.ChildDocTemplate(docManager,
                 _("BPEL"),
                 "*.none",
@@ -432,6 +471,7 @@ class IDEApplication(wx.lib.pydocview.DocApp):
                 icon = ProcessModelEditor.getProcessModelIcon())
             docManager.AssociateTemplate(bpelTemplate)
 
+        if not ACTIVEGRID_BASE_IDE:
             dataModelChildTemplate = wx.lib.pydocview.ChildDocTemplate(docManager,
                 _("Schema"),
                 "*.none",
@@ -453,8 +493,8 @@ class IDEApplication(wx.lib.pydocview.DocApp):
         projectService          = self.InstallService(ProjectEditor.ProjectService("Projects", embeddedWindowLocation = wx.lib.pydocview.EMBEDDED_WINDOW_TOPLEFT))
         findService             = self.InstallService(FindInDirService.FindInDirService())
         if not ACTIVEGRID_BASE_IDE:
-            webServerService    = self.InstallService(WebServerService.WebServerService())
-            webBrowserService   = self.InstallService(WebBrowserService.WebBrowserService())
+            webBrowserService   = self.InstallService(WebBrowserService.WebBrowserService())  # this must be before webServerService since it sets the proxy environment variable that is needed by the webServerService.
+            webServerService    = self.InstallService(WebServerService.WebServerService())  # this must be after webBrowserService since that service sets the proxy environment variables.
         outlineService          = self.InstallService(OutlineService.OutlineService("Outline", embeddedWindowLocation = wx.lib.pydocview.EMBEDDED_WINDOW_BOTTOMLEFT))
         filePropertiesService   = self.InstallService(wx.lib.pydocview.FilePropertiesService())
         markerService           = self.InstallService(MarkerService.MarkerService())
@@ -462,7 +502,7 @@ class IDEApplication(wx.lib.pydocview.DocApp):
         debuggerService         = self.InstallService(DebuggerService.DebuggerService("Debugger", embeddedWindowLocation = wx.lib.pydocview.EMBEDDED_WINDOW_BOTTOM))
         if not ACTIVEGRID_BASE_IDE:
             processModelService = self.InstallService(ProcessModelEditor.ProcessModelService())
-            viewEditorService   = self.InstallService(ViewEditor.ViewEditorService())
+            viewEditorService   = self.InstallService(XFormEditor.XFormService())
             deploymentService   = self.InstallService(DeploymentService.DeploymentService())
             dataModelService    = self.InstallService(DataModelEditor.DataModelService())
             dataSourceService   = self.InstallService(DataModelEditor.DataSourceService())
@@ -477,6 +517,8 @@ class IDEApplication(wx.lib.pydocview.DocApp):
         if not ACTIVEGRID_BASE_IDE:
             helpPath = os.path.join(sysutilslib.mainModuleDir, "activegrid", "tool", "data", "AGDeveloperGuideWebHelp", "AGDeveloperGuideWebHelp.hhp")
             helpService             = self.InstallService(HelpService.HelpService(helpPath))
+        if self.GetUseTabbedMDI():
+            windowService       = self.InstallService(wx.lib.pydocview.WindowMenuService())
         
 
         if not ACTIVEGRID_BASE_IDE:
@@ -515,24 +557,26 @@ class IDEApplication(wx.lib.pydocview.DocApp):
         if not ACTIVEGRID_BASE_IDE:
             propertyService.AddViewTypeForBackgroundHandler(DataModelEditor.DataModelView)
             propertyService.AddViewTypeForBackgroundHandler(ProcessModelEditor.ProcessModelView)
-            propertyService.AddViewTypeForBackgroundHandler(ViewEditor.ViewEditorView)
+            propertyService.AddViewTypeForBackgroundHandler(XFormEditor.XFormView)
             propertyService.AddViewTypeForBackgroundHandler(BPELEditor.BPELView)
             propertyService.AddViewTypeForBackgroundHandler(WSDLEditor.WSDLView)
             propertyService.StartBackgroundTimer()
             
             propertyService.AddCustomCellRenderers(DataModelEditor.GetCustomGridCellRendererDict())
             propertyService.AddCustomCellRenderers(BPELEditor.GetCustomGridCellRendererDict())
-            propertyService.AddCustomCellRenderers(WsdlAgEditor.GetCustomGridCellRendererDict())
+            propertyService.AddCustomCellRenderers(XFormEditor.GetCustomGridCellRendererDict())
             propertyService.AddCustomCellRenderers(XPathEditor.GetCustomGridCellRendererDict())
-            propertyService.AddCustomCellRenderers(ViewEditor.GetCustomGridCellRendererDict())
+            propertyService.AddCustomCellRenderers(XPathExprEditor.GetCustomGridCellRendererDict())
             propertyService.AddCustomCellRenderers(WSDLEditor.GetCustomGridCellRendererDict())
+            propertyService.AddCustomCellRenderers(WsdlAgEditor.GetCustomGridCellRendererDict())
 
             propertyService.AddCustomCellEditors(DataModelEditor.GetCustomGridCellEditorDict())
             propertyService.AddCustomCellEditors(BPELEditor.GetCustomGridCellEditorDict())
-            propertyService.AddCustomCellEditors(ViewEditor.GetCustomGridCellEditorDict())
-            propertyService.AddCustomCellEditors(WsdlAgEditor.GetCustomGridCellEditorDict())
+            propertyService.AddCustomCellEditors(XFormEditor.GetCustomGridCellEditorDict())
             propertyService.AddCustomCellEditors(XPathEditor.GetCustomGridCellEditorDict())
+            propertyService.AddCustomCellEditors(XPathExprEditor.GetCustomGridCellEditorDict())
             propertyService.AddCustomCellEditors(WSDLEditor.GetCustomGridCellEditorDict())
+            propertyService.AddCustomCellEditors(WsdlAgEditor.GetCustomGridCellEditorDict())
         
         if not ACTIVEGRID_BASE_IDE:
             projectService.AddNameDefault(".bpel", projectService.GetDefaultNameCallback)
@@ -554,26 +598,36 @@ class IDEApplication(wx.lib.pydocview.DocApp):
             projectService.AddFileTypeDefault(".gif", basedocmgr.FILE_TYPE_STATIC)
             projectService.AddFileTypeDefault(".jpg", basedocmgr.FILE_TYPE_STATIC)
             projectService.AddFileTypeDefault(".jpeg", basedocmgr.FILE_TYPE_STATIC)
-            # projectService.AddFileTypeDefault(".xform", basedocmgr.FILE_TYPE_XFORM)  # don't register xform as a default, must be explicitly added
+            projectService.AddFileTypeDefault(".xform", basedocmgr.FILE_TYPE_XFORM)
 
-            projectService.AddLogicalViewFolderDefault(".wsdlag", _("Services"))
-            projectService.AddLogicalViewFolderDefault(".wsdl", _("Services"))
-            projectService.AddLogicalViewFolderDefault("wsdl.php", _("Services"))
-            projectService.AddLogicalViewFolderDefault("wsdl.py", _("Services"))
-            projectService.AddLogicalViewFolderDefault(".xsd", _("Schemas"))
-            projectService.AddLogicalViewFolderDefault("xsd.php", _("Schemas"))
-            projectService.AddLogicalViewFolderDefault("xsd.py", _("Schemas"))
-            projectService.AddLogicalViewFolderDefault(".bpel", _("Processes"))
-            projectService.AddLogicalViewFolderDefault(".xform", _("XForms"))
-            projectService.AddLogicalViewFolderDefault(".lyt", _("XForms/Skins_and_Layouts"))
-            projectService.AddLogicalViewFolderDefault(".skn", _("XForms/Skins_and_Layouts"))
-            projectService.AddLogicalViewFolderDefault(".xacml", _("Security"))
-            projectService.AddLogicalViewFolderDefault(".css", _("XForms/Static"))
-            projectService.AddLogicalViewFolderDefault(".js", _("XForms/Static"))
-        projectService.AddLogicalViewFolderDefault(".gif", _("Images"))
-        projectService.AddLogicalViewFolderDefault(".jpeg", _("Images"))
-        projectService.AddLogicalViewFolderDefault(".jpg", _("Images"))
-        projectService.AddLogicalViewFolderDefault(".py", None)
+        projectService.AddLogicalViewFolderDefault(".agp", _("Projects"))
+        projectService.AddLogicalViewFolderDefault(".wsdlag", _("Services"))
+        projectService.AddLogicalViewFolderDefault(".wsdl", _("Services"))
+        projectService.AddLogicalViewFolderDefault(".xsd", _("Data Models"))
+        projectService.AddLogicalViewFolderDefault(".bpel", _("Page Flows"))
+        projectService.AddLogicalViewFolderDefault(".xform", _("Pages"))
+        projectService.AddLogicalViewFolderDefault(".xacml", _("Security"))
+        projectService.AddLogicalViewFolderDefault(".lyt", _("Presentation/Layouts"))
+        projectService.AddLogicalViewFolderDefault(".skn", _("Presentation/Skins"))
+        projectService.AddLogicalViewFolderDefault(".css", _("Presentation/Stylesheets"))
+        projectService.AddLogicalViewFolderDefault(".js", _("Presentation/Javascript"))
+        projectService.AddLogicalViewFolderDefault(".html", _("Presentation/Static"))
+        projectService.AddLogicalViewFolderDefault(".htm", _("Presentation/Static"))
+        projectService.AddLogicalViewFolderDefault(".gif", _("Presentation/Images"))
+        projectService.AddLogicalViewFolderDefault(".jpeg", _("Presentation/Images"))
+        projectService.AddLogicalViewFolderDefault(".jpg", _("Presentation/Images"))
+        projectService.AddLogicalViewFolderDefault(".png", _("Presentation/Images"))
+        projectService.AddLogicalViewFolderDefault(".ico", _("Presentation/Images"))
+        projectService.AddLogicalViewFolderDefault(".bmp", _("Presentation/Images"))
+        projectService.AddLogicalViewFolderDefault(".py", _("Code"))
+        projectService.AddLogicalViewFolderDefault(".php", _("Code"))
+        projectService.AddLogicalViewFolderDefault(".pl", _("Code"))
+        projectService.AddLogicalViewFolderDefault(".sql", _("Code"))
+        projectService.AddLogicalViewFolderDefault(".xml", _("Code"))
+        projectService.AddLogicalViewFolderDefault(".dpl", _("Code"))
+        
+        projectService.AddLogicalViewFolderCollapsedDefault(_("Page Flows"), False)
+        projectService.AddLogicalViewFolderCollapsedDefault(_("Pages"), False)
     
         
         self.SetDefaultIcon(getActiveGridIcon())
@@ -582,9 +636,9 @@ class IDEApplication(wx.lib.pydocview.DocApp):
         else:
             embeddedWindows = wx.lib.pydocview.EMBEDDED_WINDOW_TOPLEFT | wx.lib.pydocview.EMBEDDED_WINDOW_BOTTOMLEFT |wx.lib.pydocview.EMBEDDED_WINDOW_BOTTOM
         if self.GetUseTabbedMDI():
-            self.frame = IDEDocTabbedParentFrame(docManager, None, -1, wx.GetApp().GetAppName(), embeddedWindows=embeddedWindows)
+            self.frame = IDEDocTabbedParentFrame(docManager, None, -1, wx.GetApp().GetAppName(), embeddedWindows=embeddedWindows, minSize=150)
         else:
-            self.frame = IDEMDIParentFrame(docManager, None, -1, wx.GetApp().GetAppName(), embeddedWindows=embeddedWindows)
+            self.frame = IDEMDIParentFrame(docManager, None, -1, wx.GetApp().GetAppName(), embeddedWindows=embeddedWindows, minSize=150)
         self.frame.Show(True)
 
 
@@ -599,12 +653,10 @@ class IDEApplication(wx.lib.pydocview.DocApp):
         # wxBug: On Mac, having the updates fire while the tip dialog is at front
         # for some reason messes up menu updates. This seems a low-level wxWidgets bug,
         # so until I track this down, turn off UI updates while the tip dialog is showing.
-        #wx.UpdateUIEvent.SetUpdateInterval(-1)
-        #appUpdater = updater.AppUpdateService(self)
-        #appUpdater.RunUpdateIfNewer()
         if not ACTIVEGRID_BASE_IDE:
             wx.UpdateUIEvent.SetUpdateInterval(-1)
-            appUpdater = updater.AppUpdateService(self)
+            UpdateService.UpdateVersionNag()
+            appUpdater = UpdateService.AppUpdateService(self)
             appUpdater.RunUpdateIfNewer()
             if not welcomeService.RunWelcomeIfFirstTime():
                 if os.path.isfile(tips_path):
@@ -613,22 +665,50 @@ class IDEApplication(wx.lib.pydocview.DocApp):
             if os.path.isfile(tips_path):
                 self.ShowTip(docManager.FindSuitableParent(), wx.CreateFileTipProvider(tips_path, 0))
 
+        iconPath = os.path.join(sysutilslib.mainModuleDir, "activegrid", "tool", "bmp_source", "activegrid.ico")
+        if os.path.isfile(iconPath):
+            ib = wx.IconBundle()
+            ib.AddIconFromFile(iconPath, wx.BITMAP_TYPE_ANY)
+            wx.GetApp().GetTopWindow().SetIcons(ib)
+        
         wx.UpdateUIEvent.SetUpdateInterval(1000)  # Overhead of updating menus was too much.  Change to update every n milliseconds.
 
-        # we need this for a while due to the Mac 1.0 release which put things
-        # in ~/Documents/ActiveGrid Projects/demos.
-        # Now it should be ~/Documents/ActiveGrid Demos/ 
-        base_path = appdirs.documents_folder
-        if os.path.isdir(os.path.join(base_path, "ActiveGrid Projects", "demos")):
-            message = _("The location where demo files are stored has changed between the 1.0 and 1.1 release as a result of improved multi-user support across platforms. In order for ActiveGrid Application Builder to find these files, they need to be moved from '%s/ActiveGrid Projects/demos' to '%s/ActiveGrid Demos'. Click OK to move the files.") % (base_path, base_path)
-            wx.MessageBox(message, _("Demo Files Location Update"))
-            import shutil
-            shutil.copytree(os.path.join(base_path, "ActiveGrid Projects", "demos"), os.path.join(base_path, "ActiveGrid Demos"))
-            shutil.rmtree(os.path.join(base_path, "ActiveGrid Projects"))
-
-        
         return True
 
+class IDEDocManager(wx.lib.docview.DocManager):
+    
+    # Overriding default document creation.
+    def OnFileNew(self, event):
+        import NewDialog
+        newDialog = NewDialog.NewDialog(wx.GetApp().GetTopWindow())
+        if newDialog.ShowModal() == wx.ID_OK:
+            isTemplate, object = newDialog.GetSelection()
+            if isTemplate:
+                object.CreateDocument('', wx.lib.docview.DOC_NEW)
+            else:
+                import ProcessModelEditor
+                if object == NewDialog.FROM_DATA_SOURCE:
+                    wiz = ProcessModelEditor.CreateAppWizard(wx.GetApp().GetTopWindow(), title=_("New Database Application"), minimalCreate=False, startingType=object)
+                    wiz.RunWizard()
+                elif object == NewDialog.FROM_DATABASE_SCHEMA:
+                    wiz = ProcessModelEditor.CreateAppWizard(wx.GetApp().GetTopWindow(), title=_("New Database Application"), minimalCreate=False, startingType=object)
+                    wiz.RunWizard()
+                elif object == NewDialog.FROM_SERVICE:
+                    wiz = ProcessModelEditor.CreateAppWizard(wx.GetApp().GetTopWindow(), title=_("New Service Application"), minimalCreate=False, startingType=object)
+                    wiz.RunWizard()
+                elif object == NewDialog.CREATE_SKELETON_APP:
+                    wiz = ProcessModelEditor.CreateAppWizard(wx.GetApp().GetTopWindow(), title=_("New Skeleton Application"), minimalCreate=False, startingType=object)
+                    wiz.RunWizard()
+                elif object == NewDialog.CREATE_PROJECT:
+                    import ProjectEditor
+                    for temp in self.GetTemplates():
+                        if isinstance(temp,ProjectEditor.ProjectTemplate):
+                            temp.CreateDocument('', wx.lib.docview.DOC_NEW)
+                            break
+                else:
+                    assert False, "Unknown type returned from NewDialog"
+
+    
 
 class IDEDocTabbedParentFrame(wx.lib.pydocview.DocTabbedParentFrame):
     
@@ -2129,723 +2209,720 @@ u\x9d\xe9\xa9\xeaTu\xb2\xd3\xe96\x9a\xcdN\xa7\xc79\x17R\xeaq\x85R\xe28N!\x9f\
 \x94Zv\xe0\xfb\x8e{\x8bA\x8dT\xd9=\xcf-\x16\x0b\x04\x80RbY\xb6\xfa\xb8\xc5Ba\
 \x9f\nq]7\xf3]\x18cD\xcaR\xa9X*\x15\xdb\xed\xce\xf6\xeen\xb7\xdb\xd7\xb3.3\
 \x11J\xc9\xc0\xb5t\xaa:1Q\xb6(U\xc3\'!$\xcc\xe5\x84\x90T\xb51\xdb\xb6,+\x9f\
-\xcf\x1fD\x15x\xc3E\x07\x1b@\t|\x0f\xb5\xfd\x86{-i\x11\xff\x7f{\xef\xfe\x1b\
-\xd7q\xe6y?u\xae\xdd\xa7\xaf\xbc4u#u#%Y\xa2\xed\xc8\xb2\x13\xc7\xce&\x93\xcc\
-$c\xcf&\xeb\xcc\xcc\xee\xecb\x81\xc5\x02\xefO\x0b,\xf6\xcf\x19\x0c0x\xf1\x02\
-/\x06\x03\x0c2~\xd7\xef$\xefd\xc7\x8e\xb3\x13G\x8e-\xcb\x17I\xbeP\xb2l\x8a\
-\x92LR\x94\xd8$\xc5>};\x97\xaaz\xde\x1f\xaa\xbbX<}QK\xb6(\x92\xaa\x0f\x0c\
-\xa1\xdd\xacS\xa7\xaa\xfa\x9co=\xe7\xa9\xe7<\x15\xdce\xc1*\xc0=\xa6\n\xd3\
-\xcd\xa5r\xfbv\x9e\xb8\xcb+I:\xbe\xc5\r&.Gqo/\r\xff\xe5\xad\xa1?O\xc5\xcb^4\
-\x9f\x8eoY\xbcA\x00\xd1\xb0b\xb3\x18\xa4\x0eE\xa9\t\xee\x8e\x98\xa6i\xb5sgSJ\
-\xabC\xdf[\xcf\x7fWVeY\x96\xeb\xba"/\xa0x\x19\xbd\xb3\x19\xa2\x01\xb0\xd9/a\
-\x9a\xa6\x88\xa0\xe0\xdcdFa\xa5\xf8\'\xe5\xc2\x1f\x1b\xac\xe1\xb2\xb5tt+E\
-\x97-^\x07\xe0\x80\xc0\x8dTd\x8f6\xdd\t\xea\xee\x05wH\xa4!4\x8c\xeeQ\xde\x9d\
-\xbe y\xf30\xc3X\xcf}\xe7n\xf6\xdb&\xab\xa5\xe8r:\xba\xe5\xd2\xb2\x89\xedWH\
-\x0c\x07\xed<\xf7\x0e\x18\xc5c\xe9\xe1\xc3\xe9\xfc\xa8\xeb\xbaD\xc10\x8cf\
-\xfe\xe9\xeb\xee\t1a\x88:-%-b6\x9bM\xa5R\xa2\x97\x03\xfe("\x8cD\xdc\xe7\x0b{\
-\xfe\x8fD|\x8e8\xa9\xf8\xed\xd2\xe9t&\x93\xf1R^\x9f\xa5\xd4\xcesu\xbe\xe2$N\
-\x1d\x86a+\x84\x861\xceM\xdf<]\xf1\xbee`l\xd1\xf5t\xbc\x94\x8e\x97l\xb6n\x00\
-\x03\x04n8\x915\x12\xb8\xe3\x91\xbb\x9f;\xc3r1P\xae\xa3XJ6\xf9\xae\xcd\x90\
-\x13|\xa2\r\xe2\x02\x90\xd6k.\x97\xcd\xe5\xb2\xd0z\xeb\x12\xc5FE\xc2\x08\xb0\
-m[>\x8d\x89\xbe\x8b\xf1\x8fc\x8a(\xfd!-\x97\x0e1\xfa-2\x9b\xa696V\x1a\x1e\
-\x1e"\x848\x8e\x93N\xa7E\xea$\x91\xcfr\x90Q\xed\x1cO\xc30\n\xf9\xfc\x13\xc7\
-\xd3\x88h\xdb\xb6\xf8\xa5\xc4\xbd\xd0k\xceS\']\xb5\x9e\xf6/\xc2\xc5hp\xce\
-\xe3\x98\x06A\x10F\x91\x98\xc9\x08!\xb6m{^\xdaK\xa7\xc5\x8e\xaff{{E\x91Bj\
-\xdf\xde={\xf7\x8c\x99\xa6\xe9\xba\xaeH\x12\xe5y\xde}\xe5U~$lJ6\xc06=\xa8\
-\xab\x0c\xbc\xd7\x12\xc6J\x16\x13\xd6;\xbdEf\xe4(<\x94T\xe3[\x82\xfa\\\xac^I\
-ma\xe5\x88\x18\x9b\xfb+\xa9\xfd\x15\x00Pn\x03q`\xaa\x9d\x8d\x00\x00x{\xfbG)\
-\x9aB\xdd\xa4\xef\xb8\xd3\xa2TM\x15\xa9\x922J/\x8a"q{o8\x85\xad|\x84\xb9(}\
-\xa8\xa2\xd4\x908J\xac\x10\xb6v5\xea\xb6\x9f\xaa\xea\x0b\xc2\xf6n\xf1\xa6i\
-\xca;\x07\xadb\xe0\x14\x02\xef\x98\x1c\x10\x00\x90S\xa0\xe7y\xc4\xc9$d]\x1a\
-\xda\x88h\x18\x86\x14w\xf9R\x92\xd5;\xf0\xbc\x13\xa2,\xab\x8aIHT\x9b\xf0\x0f\
-\xa8\x13\x95\xab\xec1{_\x1a$*\x91\x17\x83*\xac\xe2\x1a\x90\x97\x01\xe7\x16\
-\xb3R\xb5\xd4\xde\x1a<\x93\x18|q\x94\xd3v%Ie\xbfg\x93H{7g\xd1\x86\xc4x\xaa\
-\x17\x80\xaat\x96E:\x0b\xcb\xf5gad\xb4\x1e\x05:^\x81\xf3\xd2\xe9\xce)-\xf1#\
-\x8ao\xd4\xebv\xf0\xf1T\xab\x95\xb7\x89e\xb5\xf4!q/t=\n\xda\x97\xa8Z\x8f\xfa\
-\xa3\xc8\tO\x8c[:\x9dR\x8f\xed\x1c\x13\xf1=\xa5T\\\xe4\x9csQ\xbfzK\xde\xef\
-\xbc\xb5\xc5\xc4\xfeM\xe1\x05\x15;rt-3\xf8^K\xb4~\x9bGU\x00 \x00q\xef\x85Y\'\
-S\xb2\xd3C\xb0\x13\x17T\xa1\xe3J\x92W\xb9eY\xeae$\xa4\n7\xafI&d\x0b\x00\xd4\
-\x8d\xe7\xd5\x8bO\xa8\x8f\xb4\xe0z\xb5A6@\xca\xb4\xf0H\xc8\tCu\xcb&|\x08\xf2\
-.\x92\xe1\x1f\x89\xbd\xea\x13g\x94\xcf\n\xa0\x18Jr>\xebt\x11@[\xdceD\x87\xa9\
-\xbc\xff)O-\xd2\xb1RJe\xf3\xd4\xee\x0f\xe2-\x81\xcdZ\xa3*\xbbt\x9a%:\xa2\xce\
-g\xfd\xdd \xbd.\x00YX\x15#!\xac\x89\xc1\x97W\x02\xc0\xa6iU]\xc6\x90\x8d\xb17\
-oo\xddG\xdfAI\x0f)\xc73q\x01\xa8\xbf\xbe\xfa\xc3%\xd6\'\x08!\xc2\xff \xb6(`\
-\x8c\'\xfc\xec\xb9\x0egHb$\x85\xa1\x03\x00j\xb2\xe2\x07\x982\x13\x17\x86\x98\
-\xf8\x13\xf7\xc2=\xf5\xbd\xebl\xa7\xdaO\t\x7fZb\xf4\xe4\x98\x88\tO\x15w\x19\
-\x8ds\xbf]\xdbzx\\\xa7\x8d\xd6{\xcb}v\xe4\xf0\x86\x8f\xf4\x0fX\x10 \xa7\x1b\
-\xe1\x8f\xd0s\xaa\x00\xc3\xf4FZ>\xe4\x1d)\xee\x02U\xfe\xa4jKqWWuT1\xb5\xd4\
-\xfc\xb4m\xa7\x9e\xaa\x05\xd2(\xb66\xc7\xed\xf6\xd1\xf7\xc4\xa5,\xd2U\xaaO\
-\x03\x9d\xb2\xab\xb6\xc7T\xde\xdd\x97O\x0c}\x8cw\xd8\xac\xec\x8a#"\xb9\xc0H6\
-\x9b\xd2\xc2\x0f`\xb4\x83\xe5\xc5\xf7"\xf0C\xd4\x00\xed\xb9Pu\xb0\x0c~#\x91\
-\xcd\x0f\xe6\x84\x10\xcb\xb2\xc4\x08\x80\xa2\xad\xb2#\xc2X\x96w\xf2\xfd\xca\
-\x90\x98<`\xf3\xe4\x9a\x98Y\x13\x93kb\xf0U;\xb1\xeb\xe0\xf7o\x92:\xc7t\xbd\
-\x00:\x7f}\xb5\xa9\xa2\xb5\xd2\xc8\x10\x87\x18\x86\xb1^\xa90\xa6\xbe\xb0\x03\
-\x84\x90|\xbe\x8b\xa7[*;\x00\x98\xa6)\xc4]\xeac\xd7\x8b\xf6\x9e\xa3\x9a\xb80\
-\x84\x16\xcb\x8b\xa1\xff\x03\x01\xd9l\xf1\xa8#\x9c\x98q;\x17K\xe4\x80\xc8\
-\xc9ULx\x8c11\xa4b\xf2\xeborm+b\xff\x86\xdcH\xaf\xd7\x8e\x1c\xb67,\xac\xec{B\
-\xab\xf3\xc8\x02\xb8\x97\xd9\x9e.L\xc8x\xca\x9d*\xee\x89{[\n\xa5\x0c\x9eI,\
-\xd9\xabf\xb2\xd9\x8e\xa1$m\xa7^\xc2\xc4\x03\xe5I\xbf\xff\xd3\xa8\xa0Z\xad]\
-\xbb6\xc7\x11\xbf\xf5\xf4\x93\xe2~\x90a\x03\xa2f\xf5\xf1\x1c6_\xd0\x89&\xa9\
-\x06W\xe2\x8c\t\xcbH\xde\x84\x9d"\xd2\xa9#\t\x83\x88(&\xb6\xa1x{\xe4\xb9\xd4\
-\xee\xf7\xbf\x91\xc4\xb9\xe6\x17\x16WVV\\\xd7=\xf9\xc4\t9\x03\x19\x86!\x03\
-\x16\x13G\xc9\x9b\xb9\xebL\xd6\xeb,\xbe\xef_\x9b\xbb\x0e\x00g\x9e9\r\x9b\'WY\
-\xa1\x18|1&\x97\xaf\\E\xc4}\xfb\xf6d=O\xb5\x9d\xe5\xc5 \xdb\xa0\xfe\xd0\xf2\
-\xb7\x1ep>Ce\xf7gU\xce\xd4\x9f>1\xb6\xaa\xbe\x9bm\xdf\xa0\xb4\xdc\xdbo\xedo\
-\x98z\xa9\x94\x9brS\x89!J\xfc\x88B\x8e\xa1}\x85\x0cr\xdd\xf6\xea\x8bza\xa8\
-\x0fs]}\x86\x9d\x95\x00\x804\xf0\xa5\x95\xa0*{\xe2vH\xfc\x16\xea\xf3\x01"\nq\
-\x97\xf7\xb2\x9c\x03\xee\xebQo\xeb\xe1\x91\xdfZ\xf9\x04\x88)v7\xdb\x07\xdfk\
-\x89\x06\xb4\xbe\x08\x00\xa4\xaf\x87\xc7\xb0R\xe9\xe2F:\xc9\x9d*\xee\xd0\xcd\
-p\x96vbW\x1f\x85zK\xcb{XX%\xa8\xec\xbc,\xcb\xab\xf6]\xe7\x05\xad\xbaY|\xdf?w\
-\xfe}\x00x\xe6\xf4\xd3\xf2\xf6^^.[\xb6\x95\xf1<Us\xb1\x1d\xdd\xa8\xb6GmR\x7f\
-e\xe9\x943q\xdd\xcb.\xaf\xae\xad9\x8e\x93\xcddd\x17T\xe1\xb3\x14\x07\xba\xda\
-\x0cKY\x04\xeb\xda\xfdN3V\x16\x16\x1f\xca\xcb\xe5s\xe7\xdf\x9f\x18? \xc4\x1d\
-\xdar\xd0\xe9\x94H\xd4\xdf\xdf{\xb0\xbc\\v]\'\x9f\xcf\x8b\x1a*\x15\xff\xdds\
-\xe7\x01\xe0\x99\xd3\xdf\x92\xe5eG\xd4\xc1\x11\xa7\xfel\xe62\x00\x8c\x8d\x95\
-\xb2\xd9l\xe7\xe0\x93\xb6gF\xfd\x89\xd5\xc1\x19\x10q\x15\x01\xc0\x97\xb3\xd7\
-\x0e\x1c\xd8\xef\xa5\xd3\xe2:T/\xc2\xae^\x08\xf5"\x14\xb1\xed\x88\xb8\xb2\
-\xb2\xdahe\xcb\xdah\xc3\xde={\xa4\x87:q%\x88\xd9Z\xf4W\x8a\xbb\xfa\xc3\r\xde\
-\x1192\xb0\xf9\xc2\x00e"\xe9\xf3cu\xb6M\x1e%\x05Z\x9a \x1dq2\x9bn\x04\xd9rq{\
-\x8a;\x14\x15\xff\xea\xfd\xce[[OT\xb9!"\xa3:3\xf1J\xdc\xdc\xde\x81\xf7Z\xba\
-\x8e\xbc\x15C\x15\xf7Y\x98\x1d>B\x8c\rI\xdf\xc1\xe2.\x90\x96\x82a\x18\xc2w,\
-\xaf\x9eN\xb5\x92w\xb5\xbc2\xa4\xb8\'\xd4M=\xa4\xd3@\xc0\xcd\xc8+U^\xd6\x9f_\
-\xfd\xe2\xec\xdb\xef\xb8\xae\xf3\xf2K?\x19\x1d\x19\x91\x95\xab\xfa\xd2\xd9\
-\x9e>\xb2\x9e\xe8\xaf*\x10\xb2\xd7W\xaf~\xf9\xee{\xe7\x1d\xc7\xf9\xd3\x1f\
-\xff\xf1\xe8\xe8HB\xc8\xe4m)\xeb\x97\xdfw\xf6\xbd\x7f\xf7;\x07\x81\xb7_\x1fL\
-\xdc\xd8\xea\x14\xd8\xbf~\xf9s\xc8\xca/_\xfe\xfc\xad\xb3o\xbb\xae\xfb\xef~\
-\xfag\xa3\xa3#\tE\xc0\xcd/\x01\xc8\x9fR\x8c\x86\xdaf\xe1\xfcQ\xbb\x90\x18|\
-\xf5z\xb8/\xbdH\xb4\xf6\xec\x1f\xfe\xb0\x7f\xdf\xbe?\xfe\xd1\x1f\x89(\xbd\
-\xc4E\xd2g`\xa5u\xef\xfb\xd5\x1b7\xe7\x13IT\x081&&\xc6m\xdb\xeel\x9bly\xa7\
-\xe9\xd0\xff\xb7\xeb\x83z\xc1t\xceI\x83W\x98\xb8P\xe5-\x99\xb0\xa2\xa0c\xc2K\
-\x9cBH\xf97\xd5\xbb\xad\x81\x05k<l%!\xef\xe3\x1cO\x17\x0f\rR\x1b\x8f*\xacY\
-\x06\x00"\x92\xb6\xf7\xf0\xf0X\xe9\xa2\x9b\x1d\xdb\xf4\xcd}4y\xbb"\x7f`q\x11\
-\xc8K\x01\x94\xdb/qYt\x1e\xdbK\x80\x12\x1f$\xa8\xbc\xc0\xc2\xda\xa2#\x9e\xac\
-\x11qa\xf1\x16\x00\x84at\xfb\xf6\xf2\xbe\xbd{;\xeb\xef\xda\x9e\xfb\xbdm\xc4\
-\x12\x93\xf8\xd20\x8c\xa5;w\x00 \x8a\xa2\xe5\xf2\xca\xfe\xfd\xfbd\xcd\xea\
-\x87\xc4Y\xc8fI\x1d\xb0\xfbrJS\xff\x95\x7fHt\xea\x9e\x95\xab\xcdP\xa5p~a\x01\
-\x00\xc20\xfcj~\xa1X,$\xc6\xd9\xe8\xb6\xe0\x9c\xb0\x07\xc5\x07\xcb2]\xd7M\
-\x94\xec?,\x83\xa0\xb6v\xe9\xf6\xed\xf3\x1f|\xc89.,\xde\xfa\x977\xde\xfc7/\
-\xbe\xb0\x7f\xff>\xb5\xfb\t-SO\'\x07\xb0\xd9l\xce\\\xf9\xbc\xdehl~\x82\'\xc5\
-b\xbe\xd4\x9e\xaa;{\x01\x1d?b\xd7\xcb{@z\xd5\xd9\xf9\xa7\xc1k#\xed\'\x1bu\
-\xb2\xef: \x89{A\xce+\xdfT\xef\xb6\x8c\xd8\xbf!>\xf41\xdb\xd3\x85\xf1\xc1\
-\xf6Z\xc2\xb8\xfd\x10\x80\xf7\xf0\xf0$\x17fw\x83\xb8\x0b\xd4{\xa6\x97\xa0@\
-\xef\x0b\xe2\xbe.\x14lo\xe2\xda\xf2!\xb6\xdf\x17\x17{m\x03\xc0\xd3ON\xaf\x94\
-W\xf2\xf9\xdc\xf4\xa9\'\xfa??\x0e~^\xd9\xa9\x99\xcbW\xae]\x9b+\x95J\xdf}~#\
-\xed;\xe7\xfc\xe9\xa7\xa6WVV\xc5I\xadv$Y\x9f\xb3<\xc0\xed\xaa6\x06\xdb\xa1{\
-\x1b\xe1(JlR\xffS\xf7\xa9V\x8a\xddSON/\x97Wr\xb9\xec\xb1c\x93"\x92G\x8es\xaf\
-\xdf\x17\x11\xe5R\xc7F\x03\x80HgT\x82\x07V\x079\xbdq\xce\xab\xd5\xda\xd9\xb7\
-\xdfi6\x9b\xe2\xd1\xa5\\^y\xe3\xcd\xdf~\xeb\xe9\xa7N\x9d<\x99N\xa7\xfa\x9cBV\
-\xc2\x18\xbbsg\xf9\xd2\xc7\x9f\x94\xcb\x89\x9d\xcd\xd14\xad\xa9\xc9I\xcf\xf3\
-zY\xa9_\xe7G\xec\xc57^\xe7\x83\xdd\x9b;B\xc7;a\x8de\x11\xb0(\xb4\xb8{!B\xdc\
-\xdc\xbe\xc1j+\xf3p\x1d\x00\x08\x81>\xc9\x06\xdc\xdc\xde\xcet\x92\xbbG\xdcU\
-\x1e\xea\xa5\xa0\xde\x93b\x8d\x88\xb2\x96\xe80\xc6\xc4\xa9\x87\x86\x8a\x7f\
-\xf5\x1f\xfeB\x86\x16|\xcd\xf6\xa8\xd6\x1f"\x86A8\xbf\xb0\x88\x9b[\x82\x88\
-\xc3CC\xff\xf1\x9b;\xe9=\x11\x13[{\x95\x8c\xb5\x1a\xfa\xa0\xef\xbbK+Xv\xa7X,\
-\xfc\xfb\xbfxE\x9dE\xa4d\xab\x0f\xe9\x89\x1a\xe4\xef"["\xdf\\\xff\xa6\x06D\
-\x9e\x88RZ\xad\xd6\xdey\xf7\xbd\xbbw\xd7\xc5\x19D\x81F\xa3y\xee\xbd\xf7\xaf_\
-\xbf\xf0xc\xbe\x00\x00 \x00IDATy\xf8\xf0\xa1\xc9\xa3G\x86\x86\x8a}*YZ\xba=w\
-\xe3\xc6\xdc\xb5\xb9F3\xe8H3@\xf6\xee\x1d;tp\xa2\xab\xbfb\x87\xb2\x0b\xba\
-\xd0\x0f\xe4\x91L\xed\xdb/\x14}t \xb3\x1d\x99|\x08@\xec9U\x10\xd3\xf6\x86\
-\x0ew~\xbf;\xc5}\x0bH\xe8\xbb\xf8R\x8a\xbb\xe0\x1b\xbc\x1bQYE\xe0b\x8f\x11\
-\xdcp\r\xa9\xb1.[#\x01B\x9bD\xf7\x13\x96{\xa7\xec\xdeW\xb5\xb2;r2\x93\xdf\
-\xa8\xe2\xde\xe7\xf0\xa4\xb8?\xe0t\xd3\xb3\x85R\xd9\xe38\xfe\xfc\xea\xd5\x85\
-\xc5E\xd5\x81 \xb9}\xe7\xce\xed;w>\xfdlf\xcfXi\xff\xfe}CCE\x11\xe3!\xc6-\x08\
-\xc2\xd5\xd5\xd5\xc5\xc5[\xabkw\x83@\xca\xfa\xa6\x1a\xf2\xf9\xdc\x93\xa7Nn\
-\xff\xc8\x10\x8ddSj\xdf\x9e;r\x18\xde\xd0\x91\x81j\xab\xddB\xdaDa\xb6\xf7\
-\xcfKcuI\xbd\xb9\x1b\xc4]\xde\xc6\xe5\xf2\xca\xfc\xc2\xc2\xc2\xc2"\x00\xe4\
-\xf3\xf9\xb1\xb1\xd2\xf4\xa9\x93j\x99\x0b\x17/\x01\xc0\xd4\xe4Q\xd7ug._)\x97\
-W\xc20t]\xb7T\x1a}r\xfa\x94\xf4\xcc\xde\xb3\xf0\x13\'\x8e\x8b\x95\xcc\x96\
-\x0c\xb5S)\t\x9f;\x00\xd4j\xb5\xf9\x85E\xc30D\xd0\x9e*F\xe5\xf2\xca\xec\xb5\
-\xb9r\xb9\xac6R\x16\x08\xc3p\xf6\xda\\\xb9\xbc\xe2\xfb>\x00\x8c\x8f\x1f\x98\
-\x9a<\x9a\xcb\xe5\x84f]\xfa\xf8\x13D\xb8\xb5\xb4\x04\x00\xbe\xef_\xb8\xf81\
-\x00\xda\xb6=y\xf4\x08\xe7\xbc\xcfI}\xdf\x9f\xbd6\xd7udz\xf5W\x16\x9e\x9a\
-\x9a\x9c\x18O&\xbeP\xfd\x12\x1b\xef(!r\xceo--\xcd\xcd\xdd\x10\xed/\x95J\x13\
-\x13\xe3\xf2py"B\xc8\xd4\xe4\xd1|>\xaf~?s\xf9J\xb3\x19\x0c\x0f\x15\xc7\xc6J\
-\xa2;7\xbf\x9aG\xc4\'N\x1c\x97\x81\x16\xea\xd9\xd5>\xce/,\xce\xcf\xcf//\x979\
-\xe2P\xb1p\xe8\xe0\x84\'S\xabo.\x1c\x86\xa1\xfc5\xc5P\x88.\xcb\x02\xf3\x0b\
-\x8b\xe5r\xd9u\xdd\xe9S\'\xe7\x17\x16\x17\x16\x16\xcb\xe5\xf2\x0b/<?V*\xc9\
-\xfa\x84\xb8GQT\xc8\xe7\xb3\xd9\x8c\xb2GG\xab\x88\x94\xe9z\xbd6w\xbd~\xfd\
-\xc6M\xc30TqFL\xec\x9aB\x12f{:\x9dzj\xfaT.\x97\xb3\xee\xe7%a\xcd#\x049\x8dk\
-\xf3\xe2s\x9f\xd4\xbenn\xaf\xe9x\xdd\xfe\xb2\xb96\x16\xc5\xd5yqMp\xdes\xf3&\
-\xd3\xf1z\xe5\xa5\xd9\xd9\xe2.o\xf2 \x08~\x7f\xf6\x0f"\x08\xba\xcd\xe2\xcc\
-\xe5+\x9f\xcd\\~\xe5g\xff\xd6u]Q\xec\xdc{\xef\x03\x00\xe7\xfc\xd2\xc7\x9f\
-\x86\xe1\xc6\x1e1\xb3\xd7\xe6.\\\xfc\xf8\xe7\xff\xee\xa7\xa5\xd2\xa8\xac\xb3\
-\x7f\xe1\x97\xfe\xf4O\xf2\xb9\\K\xe00\x19\xc7]\xab7>\xbap\t\x00\xce<sZ1!\xf1\
-\xf7g\xffp\xf9\xca\xe7\x9d\x8d\xfc\xe1\x0f\xbe?::2{m\xee\xec\xdb\xef\xa8\xe7\
-\x9a_X<\xf7\xde\xfb?\xf8\xfe\xf7\x8eMM2\xc6>\xf8\xf0\x82\xfc\x93_\xad\xbe\
-\xff\xc1\x87\x00\xb0\x7f\xdf\xbe\xc9\xa3G\xfa\x9c\xf4\xc2\xc5\x8f\xcf\xbf\
-\xffA\xe7I_\xfe\xd3\x1f\x8b(\xc3\xbe\xfd]\x9c\xb9|\xe5\x87\x7f\xf4\xfd\'\xa7\
-Ou\x1d\xfc\xb6\xc4\xb7\xd4\xf3\xec\xdb\xef\\\xfd\xe2K\xb5\xfd\x17.^\x9a>u\
-\xf2\x87\x7f\xf4}\xd9$q\xa2\xd1\xd1Q\xf1\xd6\xa5T\xdek\xd7\xe6\x16\x16o=\xf7\
-\xec\x99\xb1\xb1\x12\x00Tk\xf5\x8b\x97>\x01\x80\x13\xc7\x8f%B,ZO0\x9c\x8b\
-\x9f\xfe\xbd\xf3\x1f\xa8\xa3\xba\xb4t\xfb\xf2\x95\xab\xcf|\xebi\xb5\x9d\xa2\
-\xf0\xcc\xe5+\xe7\xdf\xff0\xd1\xbb\x8f.\\|\xf9O\x7f2::"\n///\xbfw\xfe\x83\
-\xf1\x03\xfb\x97\x97\xcb\xb2\xdag\x82P>\x91\xc8G\x96(\x8a\xb2\xd9\xcc\xd3ON\
-\x7f\xf1\xe5\xec\xea\xda\x9a\x8c\x18J\xb8V\xe4!\xea\xe0)j.5}\xe3\xa8l63}\xea\
-\x89\xd1\xd1\x11\xf5\x8d\x1e\xd0lohm\x11i\x00 \xf25\xde\x9f\x0b\xa5\x93\xb8z\
-\x13\xd5\xa4\xed=\xc3\x1f\x8f\xf6\xcaK\xb3\x83\xc5]\x15\x17JY\xa5\xe2\xe7r\
-\xd9\xa7\x9fz\xf2\x89\x13\xc7\t!KK\xb7\xdf\xff\xe0\xa3ryE\x88\xa3\xea\xb88\
-\xff\xfe\x87\xae\xe3<\xf7\xec\x99\xd1\x91aBH\xadV\xff\xf8\x93O\xfdj\xf5\x97\
-\xff\xdf\xaf\xff\xc3_\xfe\xb9Hlt\xcf\xc2\xaf\xbf\xf1\xdb?{\xe9\'\xae\xeb$\\\
-\x04\xad\xb5\xa3n\x8d<\xff~K\x83\x9e{\xf6Lit\x04\x08Y]]\xfb\xf8\x93O\x01\x81\
-qF)m6\x9ba\x18\x9e8~\xec\xe9\xa7\x9e\xcc\xe7sqL?\xbf\xfa\xc5\xfb\x1f|x\xf6\
-\xedwFGGR\xae\xfb\xf4S\xd3\x88\xb8\xbc\\\xbe\xb3\\\xcef\xb3\'\x8e\x1f#\x848\
-\x8eM:\x1e\xda\xe5y/^\xfa\xf8\xfd\x0f>\x02\x80\x13\xc7\x8f\x1d9|\x88\x18F\
-\xadV\xfb\xf8\x93O\xcb\xe5\x15\xd1_\xc7qT\x13\xf2\xfc\xfb\x1f\x8e\x8e\x8c\
-\xbc\xf8\xc2\xf3)\xd7\x8d\xa2\xe8\x93Og\xca++o\xfd\xfe\xed\x89\xf1\x03\x85B\
-\x97\r\xc0T\x17\xca\xd2\xed;K\xb7\xef\xec\xdf\xb7\xf7\xc4\x89\xe3)\xd7\x05Bn\
-\xdc\xb8y\xe5\xf3\xab3\x97\xaf8\x8e\xf3\xfcw\x9e\xdb4V\x8aA\x8d\xaarC2\xdef\
-\xa38l\x88\xbbxH\xe2\x9c\xbf{\xee\xfc\xe7W\xbf\x00\x80\xe3\xc7\xa7&\x0e\x1c`\
-\x8c\xf9\xd5\xea\xd5\xab_^\xfc\xf8\x93V\x0b\xdb\x1e\xad\x85\xc5\xc5\xb3o\xbf\
-#\xc6\xff\xe8\x91C\xf9|\xbeV\xaf\x7f\xfc\xf1\xa7W>\xbf\xfa\xfao\xde\xfc\xf7\
-\x7f\xf1s1\x14\x8cq\x00XX\xbc\x05\x8b\xb7FGG&\x8f\x1e\x89\xa2x\xfc\xc0~u`\
-\xa5\xf3\'\x8ec\xd34\x8e\x1e9\x94\xcdxw\x96W\xda9\xdc\xef\t\xc1\x8d\r\xbc\
-\x89j\xb1\x13B\xf6\x8c\x95\xa6&\x8f\x14\n\x05\x99\x9ba\xd78\xdcw1\xc8cZk\xed\
-\xd0\xdb\'GX\xaa0nX\xf7\xcew\xc6\xe3\x9a\xcc8\xc6\xfa\xbe\xe0\xeadF{U\xb2S\
-\xc5]\x15M\xc6\x98i\x1a?\xf9\xf1\x8f\x10Q\xbc\xe8L\x08\xd9\xb3g\xec\xb9\xe7\
-\xce\xfc\xcb\xeb\xbf\xb967\xf7\xc2w\xbf\xa3\xea\xb5\xe38\x7f\xf2\xc7?\x14)\
-\xf4D\xc9C\x87\x0f\xbe\xfe\xc6\x9b++\xab\x17.^\xfa\xde\x8b\xdfU=\x00\x9d\x85\
-\x0f\x1e\x9ax\xe3\x8d\xdf\xae\xac\xae\xce\\\xbe\xf2\xcc\xe9\xa7\xef\xd9N\xa1\
-,\xeb\xeb\x95\x8f?\xf9\x0c\x00\x9e\xff\xces\xc7\xa6&\xc5\x83\xf6\xbe\xbd{\
-\xf6\xed\xdb\x93\xcf\xe5\x1d\xc7f\x8cMM\x1e-\x95F\xc5+H\x94R\xc3 \xd3\xa7\
-\x9eXX\\\xbcukin\xee\xfa\xb1\xa9\xc9\xa3G\x0e\x8bP\x90;\xcb\xe5l\xc6;u\xf2\
-\x84|\xbfQ=\xa9\x8c\xc8\\_\xaf\x08e\x17\'%\x84\x18\x86\xb1w\xcf\xd8\xe1C\x87\
-\xfe\xe5\x8d\xdf\xac\xac\xac\x9e{\xef\xfdD\x7f\xb3\xd9\xec\x1f\xff\xe8\x07\
-\x8e\xe3\x88\xc2\xc3\xc3\xc3\xaf\xfe\xcf\xff\x17\x00\xe6\xe7\x17\xf3\xf9|W}\
-\xe1\x9cc\xfb\xd9e\xf2\xe8\x91\xef>\xffm1V\xa2\x83##\xc3\x7fx\xe7\xdc\xc5K\
-\x1f\x9f8>\xa5\xeey\xcf\x95_P5\xcc7\xe9\xfc\xbdF\xb5R\xf1\x85\xb2?\xff\x9d\
-\xe7&\x8f\x1e\x11\x82[,\x16\xc6J\xa3\xef\x9d\xffp\xbdR\x01\x00l;\xeb\xdf\xfe\
-\xc3\xbb\x00\xf0\xdd\xe7\xbf-\x86"\x8e\xe3t*\xf5\xe2\x0b\xcf\x87a8w\xfd\xc6g\
-3W\x9ez\xf2\x14\xe7\x9c\xb5\x97\xc7\x9f\x9c>\xf5\x9do?+\xe3\xacep^bN\x12-\
-\x19\x1e\x1e\xf2\xbc\xf4\xddu\x7f\xbdRi6\xbbJ\xbc\xe8T;\x10E\xcd.\xd06\xe1\
-\x0b\x85\xc2Xi\xf4\xc0\xfe}\xe9t:\x91\x9c\xb2\xff\x80h\x1e9\xb1\xff\x950\xb4\
-\xc5\x1b\xa4]\x7f0b\xb9\xe9\xc2@\xa9}\xe3\xcau\xb9X\xd4;\xfc\xd1\xe8\xff\x82\
-\xeb\xcev\xe4Iqg\x8c\t\xeb&\x8ec\x91\xa5(\x8a\xa2\xb1\xd2(\x00\x84a$\x9e\xa0\
-\xe5\xc3\xf8\xd1#\x87\xb2\xd9L\xa4`\x10\xf2\xd4\xf4)\x00\xb8\xf2\xf9Uql\xff\
-\xc2\xd3\xa7\x9e\x00\x80\xd9ks\x9d\xedi\xdd\xf0\xed\x1fF4/\x8e\xe3\xd9k\xd7\
-\x00`h\xa8x\xe8\xe0D\xdc\x86R:T,Z\x96)\x93\x97\xa5S)\xf1\'q\xae8\x8e\xf7\xee\
-\x19\x03\x00\xc6Z+xq\x1c\x0b\xd3\x12\xdba\x7f\x1b+\xaa\x9bOJ)\xbd67\x07\x00\
-\xc3CC\x87\x0f\x1d\xdc\xd4\x05\x83<\xf5\xe44\x00\xcc\xcd]\x97\xe7\x12\xc7\
-\x1e9|\x08\x11e\xc9T\xca\xdd\xbfo\x1f\x00\x04a\x17\xc1R\x05N|s\xea\xd4\x13r\
-\xfc\x05\x93G\x8f\x8c\x8c\x0c\x03\xc0\x97\xb3s\xa2\xfd\xadc\x95eR\xd1w)\x9a\
-\xed\x95\x8c\x9e)My;\x1d\x90\x1c\xd5\xc3\x87\x0e\xaa\xf9 \r\xc3\x98<zX-|gy\
-\xb9Z\xade\xb3\x19QR4R\x94?tp\x02\x00\x16\x17\x17\xc5\xf7\x942\x00p\x1c\xe7\
-\xe9\xa7\xa6e.9Q\x95\x1a\xd2\'\xdf\xa8\x94\x96\xb5\xeb\xba{\xf7\x94\x8e\x1e>\
-81~`h\xa8\xe0&\x13\xd2&\xd7K%\xae\xeb\x0e\x0f\x0f\x1d9|\xe8\xc4\xb1\xc9\x03\
-\xfb\xf7\xa5R\xa9T*\x95\xc8\x97\xa9\xf5};\x834\xa0\r%\xb5o\x8fb\x99\xcdo\x90\
-\xf6\x82\x05k\xac\xbd\x7fr\x9fH\xf9T~_\xff\x17\\w\xaa\xe5\x0e\x1d\x0f\xc8\
-\xcdfp\xf3\xab\xaf\xee,\x97\xd7\xd7\xd7\xeb\xf5M\xdb\xdfDQ\xa4\xc6\xb4\x8c\
-\x8c\x8c\xc8\x80th\xbf[q\xa0\xfd\xdc=\xbf\xb002<\xdc\xa70\xe7\\\xbe\xa2r\xe7\
-\xce\xb2P.\xd9$\xbe\xf9\xedj\xf1Z9\xa5\xf4\xd6\xd2m\x008\xb0\x7f\xbf\x88\xa8\
-I\x18\xa7\xb2#++\xab\xf3\x0b\x0bw\xefV\x96\xcb\xe5\xcd\x05\xb8\x92\x97\xa3\
-\x15\x9a",t\xe1\x9dP\x1fM\xc4\n\'c\xac\xbc\xb2\n\x00\xfb\xf7\xef\xa3\xed\x08\
-q\xd2~]P\xf8\x19\xc2(\xbasg9\x97\xcb\xca\xfe\xe6\xf39\xd9_\xd2z\xf5\x91\xcb\
-\xd1\xee\x94\x18\xd9e\x00(\x8d\x8e\x98\x86!\x9f\x03H;\xf1\xd3\xc1\xf1\xf1\
-\xd5\xd5\xb5\xc5[\xb7\x8e\x1f\x9b\x945pe=\xb6\xa5\xa1\x1c\x01\x80\xb1v>\x96\
-\xcd\x1ev\x15\xd1Z\xce\xb9\x1c\xd5V \xbc\x92cg\xef\xdeV\x1aU\xcey\x1c\xc7b}8\
-\n\xa3\x7f}\xeb\xac\xac\x87\x00!\x04\xa28\x06\x80[K\xb7\xd5\xeb\xa4X,\x08\
-\x8b\x01\x15\xe4\xb0$\x12\xc8\x88\x0bC\xf4\xc2u\xdd\x92\xeb\x8e\x0c\x17\x19\
-\xe7A\x10\xd4\xeb\xcd \x0c)\xa5\x9c3\xe4\xadH\'\x83\x18\xc4 \x96i\xa6Rn6\x93\
-\xc9d<\x99\xfdX\xec" \xc4\xfd\x01\xf2\xc3h\x1e\t\xb1\x7f\x038\x07\x02\xbc\
-\xf7\x1b\xa4\xa6\x93\x19,\xb5/\xc6\x95\x96\xd5\xd8?R>U\xb8\xc7\xb6|;X\xdcA\
-\xb1\x1c\xc30\xfc\xcdo\xff\xb5^\xaf\xdb\xb6],\x16\x0e\x1f:(T\xef\xd3\xcf.C;\
-\xdf\x9e\xba\xa2e\xb4\xb7\xe9\x10\x8a\xb0Y\x8e[\x86d\x9f\xc2\x8aBq\xd5\x8b\
-\xc0\xdb\xa9eQ\x11wy\x94\xf8\x86t{\xcdZT~\xf9\xca\xd5O>\xfd\x0c\x00D\x172\
-\x19\x8f\x10\xb2\\^Y^.\x8b\xa3I+\xf5\xc7\xa6wL\x94\xa0\x9d\xe4I\xe5\xf3\x87\
-\\\t\xec\xeco3h\xa6\xd3\xa9\xcd\xcb}\x1b\x9e\x07Ji;\x10f\x93\xc0%J\xaac%\x87\
-K\x88\xaca\x18\x8c3\x00@\x8e\xea4\x99\xb0\xdc\xf9F\xec\xa3L\xb6\xd53\xf0Qv\
-\xa1\xfd\xa7M\xaf\xc8\xcb\xd6\xca\xd1\x88\xda\x9bBDq\xbc\xbc\\\x86\x1e\xb4\
-\x93E\xb3v\x95\xa0V+\x95\x9d\xb4\xf3\xae\x88\x18\x1b\xa2$\xd6\x97\xb9\xb1\
-\xc4\x04\xe0\xd8v\xa1\x1d\x11\xd4\x15y\xac\x9aP>\x95JIo\xbb\x96\xf5\xed\x0f\
-\x0b\xee\xd2f\x19\x00\x00\xfb\xbdA\x9a)\x1d\x87\x01~\xcd\xd8\xbf\xc9\xe3V\
-\xf0\x15\xa5\xd8s7\xed\xfc\x81{F\xca\xeflq\x87\xf6\x9d\xff\xc1\x87\x17\xea\
-\xf5\xfa\xd8X\xe9\xdb\xcf>c\xb6\xf7O0\x0cC\x88;\xdb\x9c!\xb2\xd9l\x8a\xb4\
-\x85mC\x189\xe72\x9a-\x9dJ\xa9Z\xdcY\x981\xd6h\x88\xd4N\xe0\xa57\x854Iq\xef4\
-\xa23\x9e\x07\x00\x8dFC<hwF.\xaf\xae\xad\te\xff\xce\xb7\x9f\xdd\xb7w\x8f\x90\
-\'!\x94\xcb\xcbeB6t\xd30\x88\xec\xa00\xae\xf9\xe6HA)\xee\xc5Bay\xb9\xdch4d\
-\x02U\xf9`Q\xab\x89\xd4\x83\x90\xcf\xe5\xd4\xfe\x1a\x84\xa8\x99\xd48\xe7b\
-\x19S\x95K\x15U\xf8\x1a\xcd\xa6\xa5d\xc9\x1750\xc6\xaa\xd5\x1a\x00x^Z8LZ?\\{\
-\xbe\x90\x12/\\C\x9c\xb7\xa6\x1f\xec\xd8\xabS\x9e_\xfej\xedQm\xcaL\xc8\xb2\
-\xa9\xf27\x12n\x19qU\x94FG^|\xe1yy\xe5H\xa5\x96\xc3\xb8\xb1zA@\xa6ZK\xfcRD\
-\xd9\'D\xb5\xe2\x13\xe9\x97\xf9\xa6H\xc7.\x83&\x1d;B\xd9\xd5\x84\xfe\x83$\
-\x94\xd7l\x0f0\xf6\xe7\xc4\xfa)\xa5}\xde \xdd\xd7\xf9\x06i\'\x9c6h\xb5\x15L\
-\xc99P\xd6\xbd6\xc3Jy\xc3\x87\xefY\xdbN\xf5\xb9K]\x10\xff~5\xbf\x00\x00\'\
-\x8eMY\xca\xb6\xc5\xabkkjay\xec\xe2\xad%K\xd9\x01G\x14\x98_X\x04\x80L\xc6K\
-\xa7S\xea\xfad\xd7\xc2\x0b\x8b\x8b\x00\x90\xf1\xbct:\xa5\xd6\xbc\xa1V\xb8!\
-\xee\xe2V/\x16\x0b\x00\xb0\xb0x\x8bs.w\\2\x95\xec\xd8\xb7o/\x03\xc0\xd8X\xe9\
-\xc0\xfe}\xd2L\xe6\x9c\xcb\xb7\x1f\xd5\xb4|\x00@\xda\xf9\xb96\xce\xab\x9cT\
-\x8c\xcc\x90rR\x99\\W\xedo\xb1X\x10R.\xbb\xa0\xa6\xe1\x15%\xa1e\xb9\xf7\xfc-\
-\x08!by\xb0^o\xac\xac\xae&\xa2\xf7\xc20\x14\x99v\xf2\xf9\x9c:\x03U\xfd\x9a\
-\xda\xcdZ\xad~w]\xf4\xb4\xfd\xb3\xb6\xbb\xd39\xaf\xc8\x87\x95ba\xa3\x83\x89}\
-6\x96n\xb7\xf6I`\x9c3\xc6\x86\x87\x86\x00\xa0\xbc\xb2\xda>\xcbF\xcd\xd5Z-\
-\x0cCuJ\x86\xb6jK\x97\xfa\xa6\xce*\xe6\xb60\xb4\xe5\x16t\x12\xcf\xf3\xd2\xe9\
-\xb4X\x17\x95\xbb;\xc9m\xfc\xe4Q\x9e\xe7%\x0e\x91\t\xee\xb5\xb2\xef\x08hc\
-\x99Gu }\xc3\x1f\r+]<8Hmq\xe5\xba\x8cM\x88Y\xcf{\xce\x1b><\x88\xef~G\x8a;\
-\xb6\xc3\x9c\xa5\xc5\xea86\x00\xc8\xfb\x16\x11\xc30\x14\xd1)\xd0\xe1/\xbesg\
-\xf9\x83\x0f/\x08\xbd\x13Z\xf9\xc5\x97\xb33\x97\xaf\x00\xc0\xe1C\x07\xf9\xe6\
-u\xbc\xce\xc2_\xce^\x9b\xb9\xfc9\x00\x1c:4\xd1i\x9a\xb5\xf5*\xf1\xbfx\xe4\
-\xf0\xa1L&\x13E\xd1\xff\xfe\xdd\xef\xc5\xd3\x80\x90\xbf\x0f?\xbax\xf5\x8b/\
-\xa5\x95W\xaf\xd7\x85\x01+\x8e\xba\xf9\xd5\xfc\xe2\xad%\x00\x10\x96\xbb\xd0\
-\x1aB\x0c\x00X\xbb\xbb.\xbc\x1c\x8a\xbeo\x9cTTx\xe0\xc0\xfe\xa1\xa1b\x14E\
-\xff\xfa\xd6\xd9 \x08\xe5I\xaf~1\xfb\xd9\xcc\x15\x008~lJ\x1c"\x07\xc7\xdc\
-\x9cY^\xd4\'\xbb\xd2\xf9[\xb4\xec\xf6\xf6\x0e\x9f\x7fx\xe7\xbdryE*l\xbd\xdex\
-\xe7\xdd\xf3q\x1c{\x9ew\xe8\xe0\x84\xe8\xd4\x9e\xb1\x12\x00|\xfa\xd9L\x14E\
-\xe2\x9b0\x8a\xce\x7f\xf0a\xbb\xf1r\xdc\xfa]\x00\xadQ=r\xc8q\xecD\x07\r\xc3\
-\x98\x9b\xbb!\xc3\xed\xc5E\x92J\xb9\xe2]\xaas\xef}\xa0\xa6p\xb9\xbb\xbe\xfe\
-\xc1\x87\x17\xfe\xf0\xee{\xeb\xeb\x15)\xdc\x00@\x80$\xccg9D\xaa\xbe[\xed\xcd\
-f\xa5Rg\xb3\xd9l6+>\xc8\x7fU\xb2\x9b\x91\xb2\xaezc\xb4\xb2\xef\x0c\x90\xcb\
-\xdd\x91\x18\xeb\xedB\x19,G\x18\x0b\xd7Y\xb3ue\xf6YG\x1d|[\xbe\x9d\xea\x96\
-\x91\x16\x9f\xf0\xd8\x1e?v\xec\xb3\x99\xcb\x9f\xcd\\\xa9\xd5\xea\x9e\x97\x0e\
-\xa3hi\xe9\xb6m\xdb\xc5Ba\xbdRI\xe8\xc4\xf0\xf0\xd0\xd5/\xbe\xbcq\xe3\xe6\
-\xf0\xc80r^\xad\xd5\xeb\xf5:\x00\x1c>t\xf0\xd8\xd4\xa4\\\x0c\xec_\xf8\xd0\
-\xc1\x89\xa9\xc9\xa3\xd0\xcd#\xdc\xd9N\xf1\xf9{/<\xff\xd6\xd9?\xac\xae\xae\
-\xfd\xe2\xd5\xd7\xc4\xfa\xad\xefW\xab\xd5*\x00\xa4\\\xf7\xf0\xe1\x83\x9f\x7f\
-~\xb5^o\xbcu\xf6\x0fB\x07\xd7+\xfe\xd2\xd2\xed#\x87\x0f]\xbfqS\xea\x8ba\x18{\
-\xc6J3\x97\xafDQ\xf4\xcf\xff\xeb\xf5l6[\xadV\xff\xec\xa5\x9f\xf4j\xc0\x8b/<\
-\xff\xfb\xb3\xef\xac\xae\xae\xfd?\xaf\xfd\xd3\xbe}{\xd5.L\x9f:\xb9\x7f\xdf^\
-\xd8p\xe5\x03(;\xb5b\xf2\xbd\x9b{\x93\xcdf\xa2(~\xfd7\xbf\x1d\x19\x19vl\x9b#\
-\xde\xb9\xb3\x0c\x00\xb6m?w\xa6\xf5^\x15"\x1e9r\xf8\xcer\xb9Z\xab\xbd\xfa?\
-\xffidx\x88#\xae\xad\xdd\x05\x80\x8c\xe7\xd5\x1b\x8d\xfe\xa7\xe8\x1c\xd5\x17\
-\xbf\xfb\xfc\xbb\xef\xbd/;(<N\xf5z\xa3\x90\xcfW|_\x96\x07\x80\x13\xc7\xa7\
-\xfcj\xb5R\xf1\xdf}\xef\xfdb\xb1 6,\x14B_,\x16\xb2\xd9\x8c\xf4\xaa\x03\x001\
-\x88\x1a\t\xd3\xd9\x06\xf1\xa5\x98,\xe5\xbc+\x97X7\x16B:\xa6)\xd5\'\xa3\xc6\
-\xdbt\x9dH4\xdb\x19Z_B\xda\x04\x02\x88\xd8\xeb\rRb\xb9\xea\x06\x1a\xbd\xc1\
-\xb8\xd2z\x07\xb3\xdf:*@fdj\x10\xdf=\xechqW-\xf7cSG\x11\xf1\x8b/go\xdclM\xa4\
-\x07\x0f\x8e\x9f<q\xfc\xc2\xa5O\xd4C\xc4\x87o=\xfd\x94\xef\xfb\x9f~vyi\xe9\
-\xb6\xf8\xc6\xb6\xed\xe3\xc7&\x8f\x1f\x9bB\xe5-\xc4>\x85\x8fM\x1d\x9d\x9a<:\
-\xc8\x1d\xa8\x96)\x14\xf2?\xfa\xe1\xf7?\xf9tfi\xe9\xf6\xe2\xe2-\xf1e6\x9b=\
-\xf9\xc4\xf1\x91\x91a\xce\xf9\xf7^\xfc\xee\xcc\xe5+\xcb\xe5\x95\xcbW\xae\x02\
-\x80\xe7y\xcf=\xfbL*\xe5^\xbfqS\x9a\xc8\xa2\x9e\'N\x1c\xff\xfc\xea\x17\xd5jM\
-\xb8\xb3\xbb\x9eTt!\xe3y/\xff\xe9\x9f|\xf2\xe9\xcc\x17_\xce\xca.d2\xde\xf4\
-\xa9\x93\x07\xda1?\xaa~\x11E_HGTOW\xa4~e2\x99\x1f\xfc\x9b\xd3\x97>\xfe\xe4V\
-\xfbD\x00P*\x8d\x9e:y\xa2X(Hs{\xff\xbe\xbd\xcf\x9dy\xe6\x93\xcff\xa2(\x12\
-\xce\x93Ri\xf4\xe4\x13\xc7\xaf|\xfe\xc5\x80\xe2\xaev\xb0T\x1a\x95\xa3*;x\xf0\
-\xe0\xf8\x89cSo\xfe\xef\xb7\xa0\xfd\xd0\x03\x00\x8e\xe3|\xf7;\xcf]\x9b\xbbq\
-\xf3\xab\xf9\xf5\xf5\xd6^\xe5\x8em\x8f\x8f\x1fx\xf2\xc9S\x8em\xb7\x9f\x9f6Y\
-\xe8}~e\xd2\x8e\x89\x84\xf6J2\xaak\x15\n\x89\xa3\xd4\xa9\xda\xb8\xcf\x84\xfe\
-\x9am\x02r\x1a\xb7\xfd\xe3\x94\xf6~\x83t\xe8\xd0 .\x14Z\xbf\xc3\xa3\x96-\xd2\
-\xe7!\xc0\xcd\xed\xb5R\xfd\x96\xe8U\x06\xba{\xb7\x1b\x88(\x82\xbeE@\xba\x9a%\
-\xaa\\^A\xc4|>\'\xec2\xf9\x8e\x8f0B\x7f\xfd/\xbf\x01\x80\x1f|\xff{\xe3\x07\
-\xf6\x13Bn\xddZb\x9c[\xa6Y(\xe4\xa5\x96\x89\xf57\xce\xf9\xffz\xfd\xcd^\x85\
-\x13\x8d\x11f\x97\xfc_a\xfb\xab\xbb\xc9\xf0\xcd\xfbn3\xc6\xee\xde]G\xc4L\xc6\
-K\xa7\xd3r\'eQ\xbe\xdeh4\xea\r\xd32\x0b\xf9\xbc\xd1\xde2\x8d\xb4\xb7\x1c\x13\
-a<bia}\xbd\x02\x84\x0c\x15\x0b\x84\x10\xd1A\xe9K\x91\x93\x9fj\x12.-\xddf\x9c\
-{\xe9\xb4\xe7\xa5U\x7f\x8e8\xb5\xbao\xb8\x08\x1f\x8c\xa2\x88sn\x9a\xa6\xdc<\
-\xdaR\xb6-\x16\x83\x1f\x86\xa1\xf0\xae\xc8\rHM\xd3\x0c\xc3pee\x951V,\x16\xe4\
-R\xa7\x1c\x07\xb9r\xb0\\.#G\x11\xf8\'\xbeW[E\x94u\xcb^\xa3*;h\x9af\x1c\xc7++\
-\xab\x94\xd1\\6+\x05W\xf1YmD\xd4\x18\x86Q^Y%\x04\x0cb\xec\xdd\xbb\'Q\xa1\xd9\
-\xde/T\x86\x99\x0fxM\xaa\xff&d]\xbd\xcb\xa4\x82\xab\x93\x87\x96\xf5\x1dG\xec\
-\xdf\x88\xfd\xaf\x80\x00r\x0c\xa2\x9e\x89_\n\xe3\xcf\t?j\x1f\x90\xd3\xe0\xce\
-\x87\xc8B\x00@\x84 \xc6\xaeS\x051\xac\xc2\xf8s\x83e\x81\x07\xd8\xb9\x96\xbbD\
-\xbd\x1f\x08!\xa5\xd2(\xb4\xef\xa5\xc4\x9d\xa3F\xb0\x88\xb7XGF\x86\xc5\x1d(\
-\xfd\x12\xf2\x86\x94wcgaP\xb6\x00N\x1c\x0emU\x82\xcd2\x04J\xd8\x8cP1\x91\xc9\
-\xa4\xf3\xc9\x9d\x10\x92\xf1<\x11\x04"\xa6\ri\xdc\xc9s\x89\x0f\x96e\xed\xd93\
-&E_\xaa\xa1jq\x8b\x92r\x19sttD:\x9dT\xe1\x93\x16\xa8\xb1\xd9lOHdb\xa8\x13\
-\x06\xbe\x9cA\xc5T!\xcf%\xeb\x94-\x91*?:2"\xdd\x17\xf2\x8c\xb2Ur\xe9\xb8\xcf\
-\xa8\xca\x0e\x8a\x02##\xc3b\x05[\x8eF".S\xb6\x7f\xac4\xaa\x8e\xadz\xcd\xa8_\
-\xde\xefu\xa8V\x05\x9b5\xbdky\xd0\x82\xbe3A\x1e\xd3Z\xeb\xe1;f\xc9L\xcd\x12o\
-\xe8\xc8=\x95\x1dDR\x1a\xd6\x8aZ\xa6\xac\xbb\xb2\x03@\xba81\xb8\xb2\xc3\xce\
-\x15w\xa2l\xa5h\xb6\xf7}\x87\x8e{FJ\xa1z\xac\xb0\xcd\x8d\xf6\xbe0\xa4\x1d\
-\x17\x01m\xb5\xbdg\xe1\x84I\x98\x10wQRU\x169\xbbH%\xda\xe4\ti\xbfX\x94\xd0P\
-\xa1w\xedE\xd4\x96^K\r\x15\x82%\xf57\xd1\xb6\xc4I\xd5\xf1!\x84\xc8`\x0cP\x8c\
-bU\xec\xe4\xf0\x12e\xa3\xd1\xce\x9f@\xc6\x93\xa8UI\xd9U\x7f#\xd9$9\xd3H\xdbV\
-\xceI\t\x07\xf7=GU=\xa3\x1c\x13l\x078\xaam\x93\xf3\x8a\x9c\xdbd\x1fe\xd7\xc4\
-\x8f"\x86\xf4\xc1\xf4]\x1d\x99\xc4\x07\xcd.#\xf6\xbfBN\x81\x88\xc8\xdd\x9e\
-\xfb\xde\xf5I\xfc"A\x16\xd2\xea\x82\xf8\xcc\xb1w\xf8\xa3\x9dN\x15\x06\xf1\
-\xdd+\r\xb8\xaf\xd2\xdb\x07\xa2\x84\xa3A[\xc2\xe4\x9f\x04\xe2\x7f\xa5\x1d\
-\xd7\xcb\xf0\x94+Z\xd2*\xbfga\xd5]\xa0\xda\xf2\xd0M\x86\xe4_\xd5@@\xb5N\xe9\
-\xaeMt\x01\xda\xcb\x9b2\x04\x13:\x0cm\xa9\x9b\x86\xb2\xe3\xb3\xfc\x06:b\xfc\
-\xa5\x13CUd\xd5\xa5#EM\xcev]\xd7\x15\x89\x12."\x07Y\xfe)Q@\xb6_|\xa9\x8e\x83\
-,\x9f8\xfb\x80S\xa6:[@[\xa0\xe5\xd9et\x90\xe8\x85:\x14j\xf3\xd4\x876\xe9\xe7\
-I\\E\x1a\xcd\x06\xc8\xc5\xa6\xa6\x00\x10\xd3>f\xfb\xe1\x81\xdeZ\xaa\xdc@\xa4\
-\xed\xdaz\xbc\x03\x05\x90\xe9\x9d\xfd\xb1\x17;^\xdc\xa1\xfd\x9a{Wq\x97F\xa2j\
-xZ\xa6i\xdb\xb6\x14q\xa9t\xd2\xa6\x96B\xdf\xabp\xa7\xe8\xa8* \x85I\x1c\x92\
-\x90!9\x19\xa8\xb3H\xa7\xdf@\xd6\xa0\xca\x998Jh1\xe9p \x90\xcd\xebr"\xcfA\
-\xc2b\x15\x87$\xe6\'i\xfe\xab\xa2&\xeb\xe9<\x11\xb4\xcdvDt\x1cG\x18\xbc\x89\
-\'$\xf9\x03%\xe6\x1b\xe96I\xfc^\xaa\x11\xadV\x92\x18UU\xf1\xe5ht\x0e\x1dQ\
-\x1e8\xc8\xe6\xf7\xbc:\x87B\x96\x91\x95\xf4\t\x92\xd1hhc\x19Y$\xccv\xce\xbb\
-\x9b\xedN\xa6d\xa7\xbb\xec\xc0\x95\x80GU\xda\x90/d\xf4\x0c\x7f\xb4\xbda\'[\
-\xba\xdfv\xeeHq\x97\xa6+\xb4\xefd\xf9\x8c\x0f\x8a\xb8K\x1f\x88H\x12"\x0f7\
-\x15\xbdNh\x8a\xea\xfc\xedSX\x15\xf7>\x8d\x94\xea uD\xb5\x1c\xc9f\x17s\xd7.$\
-&\t\xdc\xbcx\xab\xd6\x908JN0Ry\xd5\x93\xf6\x9a\x9fTA\xef\xfc\xac~#\xfe\x15\
-\xeb\xab\xea)T\xc9\xee\x1c.9\x0e]{\xaa\x9e\x02;\xf2\x1c\x0c2\xaa\x9dC\xa7Z\
-\xe5F;\x9aE=<1\xc5\xca?\xa9\'\xd2h6@&\x83d\xfa\xe6k<2HeQeNf\xba\xe8\x9dFf\
-\xd0\xda\x12\xecHq\x07E\xdf\xa5\xb8\xc0\xe6uTB\x88\xf8R<h\x8bbcc%B\x88\xe7y\
-\xea\xd6e\xa4\xed\xf2N({\x9f\xc2\xf2\xb6\xef/\xee\xf23\xb6\x17B\x13:\x9b\x90\
-\x95\xc4s@\xa2\x8cZ\x8fz\x16\xb2Y\xdc\xd5\xb3\xcb\xae\t\xd4\xf6\'\x9a\x978\n\
-7;\xb2\xba*\x9d\xd1^\'\xc0\xcd\xc8C\xe4\x89\x04\xb2N\xb5=\xd01\x91\x0c\xf2\
-\xbbw\x8e*(\xf3\x01\xd9\x0c\xf4\x16\xf7\xae\xbfBb\xfc\xfb\xb7G\xf3\xb8\x11\
-\xd7n\x89\xd8v\xc6\x90\xf7\xca\xd78\xd8^K\xac\xb9"6\xbf\x06\x00\xda\xe7\x1d\
-\xa8\xfc>\xcb\xcd=@Swd(\xa4D\xb5\x16\x13w&\xb4\xd7B\xd5\x94\xb6\xa6\x92\xc7C\
-\xd58B\x88\x08\xfe\x1b\xb0\xf0\x03\xb76\xa1\xcb\xf2_\xf9\xbd*\xee]\xcf%;\xab\
-\xfe\xa9\xb3\x18Q\x02f:\x07\xe7\xc1\xba\x908J\xedK\xa2\xfd\x89\x13%&\xb0\xce\
-b\x0f\xdc\xa4\xfb\xaa\xf0~\x87B+\xbb&\x01\xf28\xb8\xf3\x11\xb2\x08\x00\x82\
-\x88w\xd5Nb\xda\xc5\xf1o\xdf{G\x0e\xe4\xcd;\x1f!m@\xff\xf0G\xd3)N<g\x98\xf7\
-\xde\xdf\xa3\x93\x9dj\xb9\x0b\x06\xb9?I\xdb\x11\x81\x88r-Q\xfa\x97\x13%\x07,\
-\xfcuZ{\xbf\x7f\xfa\x9a%\x1f\x9eB\xf5\x9f]\xb6\xac=\x0fc\xe84\x9a\xae\xd0\
-\xda\xa2\xf0\xb6S\xd6\xd3*N\x0f\xb6\xd7\x12\xad\xdd\x12\xca\x0e}\xc3\x1f\xbd\
-\xa1\x83\x0f\xa6\xec\xb0\xd3\xc5\xfd\x9eH\xb1\x96\xaf\xc0\xf4Y+\xbb\xaf\xc2\
-\x1a\x8d\xe6\xb1\x02Y\xd8\xdaH\x0f\x81\xf6\xf0\x8f\x1bV*5\xd0^K\x18\xb7\xf7\
-\xe4\xeb\x13\xfeh:\x197\xbf\xff\xc1Z\x0b\xbb^\xdc\xe5\x9a\x9b\xfa\xbf\xbd\
-\x96\xcb\xee\xab\xb0F\xa3y\xac\x88\xab_!g"H\xa6\xa7\xd9>X\xb2\x01\xd6\\E\xd6\
-\xda\xda\xac_\xf8\xe3\xc8\xe4 \xef@\xf5b7\x8b\xbb\x10e\xb3\x9d\xda\t\xdb\xa1\
-\x84\xbd\xcc\xf6\xc1\x0bk4\x9a\xc7\nN\x1b2f\xb1Wl\xbb\xe9fS\xb9\xbd\xf7\xae\
-\x0b\xb9\xcc\x11\xc6x\xcfUY;3j{\xc3\xdd\xfe2(\xbbY\xdcA\x89\xd9P\xa3\xf1zY\
-\xe2\xf7UX\xa3\xd1<>\xc4\xfeM\xb1\x91\x1ec=c\xdb\x07|k\x89\xd6nq\xe9m\xa7=\n\
-\x11#\xd3w\xf3\xebA\xd8\xcd\xe2.W\xf0:C\x0c\xbffa\x8dF\xf3\xf8\xc0#\xbf\x95i\
-\xbd\xf7\x8e\x1c\xb67<P\xb2\x01\x1e\xc7\xd5\x9b\xe2\xb3\x08\x7f\xecJ\xba0>H0\
-e\x7fv\xb3\xb8C\xb7\x90m\xe8\xad\xd7\xf7UX\xa3\xd1<&\xc4\xfeW"\xa5/e\xd8\xeb\
-%Ro\xe8\xf0`U\xdd@N\x01\x00\x11h\x8f\xed\x12\x0c\xcbM\x0f\r\xb4sS\x7fv\xb9\
-\xb8\xc3}\xaa\xb3\x96r\x8dF\xa3\xc2\x82\xbb,X\x03!\xc7\xb4\xbbC\xc6\xcd\xed\
-\x19$\xcd:\x8f\xeb\xb4\xde\xdar\xa0\xcf[K\xe9\xa1\x81v\xd1\xbb\';r\x9b=\x8dF\
-\xa3\xd9\x1aZ^\x14\xd2\xdaC\xa3\x13b\x98\xe9\x01\xcd\xf6\xcau@\xf16u\xbf]\
-\xf4\x06Z\x95\x1d\x00-\xee\x1a\x8dF\xd3\x1d\xd6,\xf3\xd0\x97\xfb_\xf70\xdb\
-\xf7\x99v\xfa\xdeU\x05wY\xb0\xda\xfa\xdc+\x98\x92\x90\xcc\xc8\xe4 \xab\xb2\
-\x83\xa0\xc5]\xa3\xd1h\xba\x82r\xff\xeb^\x8b\x9f\xc4t\x06\xf3\x8fc\xec\xb7\
-\xb7H\xed\xedmw2%+Ux\x90\x96vC\x8b\xbbF\xa3\xd1t\x81\xd6o\xf3\xb8.\xf6\xbf\
-\xeee\xb6\xa7\x8b\xe3\x83\xa4\x07\xa0\xf5\xdb<\xaa\xb6>\xf7t\xefX\x99\x91\
-\xc9\xaf\xd3\xe0\x04Z\xdc5\x1a\x8d&\t\xf28\xae\xb6\xcc\xf6\x98BW/\x8a\xe9dR\
-\xf9{\'\x1b@\x16\xc5\xfe\r\xf1\xb9O\xb2\x81\xf4\xd0!\xc3r\x1f\xb0\xb9\xdd\
-\xd0\xe2\xae\xd1h4Ib\xff\x06\xd2P\xac\xa3v\xdfH\x8f\x90l\xe9\xc4 \xbb#E\x95k\
-"\x91$\xb4\x93\rtb\xa7\x8b\xe9\xe2\xc4\xd7jq\x07Z\xdc5\x1a\x8df\x13\x9c6h\
-\xfd\x0e@\xeb\xad\xa5\xaer\xecx\xa3\x03\x85?\x86\x15\xd6X\x06\x00\x02@Y\x8f \
-\x19B\xbco\xd4!#\xd0\xe2\xae\xd1h4\x9b\x88\xfd\x9b"f\x91\xf6\xca\xfd2\xe8\
-\xeeH\x18\xb5\xd3\xc8p\xe8\x19o\x93\xca=\xe0v\x1c\xfd\xd1\xe2\xae\xd1h4\x1bl\
-$\x1b\x80\x9e\xa9}S\xf9\xfd\x03\xed\xb5\xd4(\xf3\xa8\x02\xc2l\xa7\xd0k\x1du\
-\xc00\xf9\xfbE\x8b\xbbF\xa3\xd1l\x10\xfb7\xc5\xfai\xcf\xdc/\x84\xa4\x06I\xb3\
-\x8e,j\x87?\xf2\xdea\xf2\xe9\xa1C\x83l\xee\xf1\x00hq\xd7h4\x9a\x16,\\g\xc1]\
-\x00\xc0\xde^\x14;=4\x88\xd9\x1e\xd7\x16\x91\xb6\x93\xb63\xe8:M\x98\x8e7\xd8\
-\xe6\x1e\x0f\x82\x16w\x8dF\xa3i!3\xad\xf71\xdb\x07\xf1\xb6#\x0biu\x1e\x00\
-\x08\x00\xe7\xd0=\xde\x06\xc0\x1b\xfeZ\xdbq\xf4G\x8b\xbbF\xa3\xd1\x00\x00\
-\xd0\xc6\xb2x\xd5H\xbc\xb5\xd4\xb5\x8c\x9b\xdd3\xc8\xe2g\xec\xdfle\x7f\x04\
-\x88{\xc5\xdbdF\x9d\xcc\xc8\xd7ip\x7f\xb4\xb8k4\x1a\r\x00\xa0\xb0\xb5\x01\
-\x802\xe8\xbac\xf5\x809\xc2x\\\xa3\x8d\xdb \xd6Q9|\xbdx\x9b\x07g\xf7\xa7\xfc\
-\xd5h4\x9a{\xd2J6\x00\xc0{\'\x1bp\xf3\xfbM;\xd5\xaf\x12\xca(e\xdc\xbfA\x90\
-\x03\x10\xec\x13oS8`:\x99o\xa0\xdd\xbd\xd1\xe2\xae\xd1h\x1e{\x90\xc9d\x03\
-\x94u\xdf"\x95\x98\x8eW\xec\x99#\x8cQ\x1a\x06QH\t\x89W\x9ch\x15\x81\x10\x80\
-\x98A\xf7\xa4\xed\x84\x0c\x92\xb7\xe0k\xa2\xdd2\x1a\x8d\xe6q\'\xae-"\r\x01\
-\x80c\x8fd\x03\x00\xe9\xe2\x041\xed\xae\x87s\xc6\xd7\xcb\xe5 lzi\xf4\xe0\x0e\
- \'}\xc3\x1f\x9dL\xa9\xff\x13\xc07\x82\x16w\x8dF\xf3X\x83\x9c\xd2\xda\xa2\
-\xf8\x1c\xd3\xee1\x8b\x86\x9d\xea\x13\xdb\xde\xa8\xd5\xd6\xca\xcb<\xa0f|\x97\
-\x06>\x10\x03D\xf8c\x8f\xcd=\xbc\xaf\xbd\xf9\xf5 h\xb7\x8cF\xa3y\xac\x89\xab\
-\xf3\xc8b\x00`\x1c\x19\xefnkg\x86\'\xfb\xe4\x08C\xc0\xc2\xd0(\xd2 \\\x9f\'\
-\xc4 \x00\xacw\xf8c\xaa0\xb1\x05f;h\xcb]\xa3\xd1<\xce \x0bi}\t\xda1\x8b]\xcb\
-X\xa9\xbc\x93-\xf5\xa9\xc42\xadT~\xc4\xb5j\x04C\xb1\x8e\xda+\xfc\xd1\xb0S\
-\xdfx\xf6\xc7\x9e\xad\xda\x9a\xd3h4\x1a\xcd6$\xf6o\x02\xa7\xad-R{\xeck\xda?f\
-\xb1Y\xab\x87\x11\x02\xad8\xb8\x06`\x12\x00\xca\xb0\xd7\x16\xa9\xfd\x9f\x00\
-\xbeY\xb4\xb8k4\x9a\xc7\x14N\x1b\xb4\xb1\x0cb\xeb\xbb\x1e1\x8bN\xa6d\xa7\x87\
-z\xd5\x10\x06a\xa3\x11"\x18._!\x18#\x98\x1c!\xee\xb1\x1d\x87\x9d\x1e\xea\xff\
-\x04\xf0\xcd\xa2\xc5]\xa3\xd1<\xa6\xc4\x95\x1b\x80\x1c\x080\x8a\xbdb\x16\xd3\
-C\x87\xba\x1f\x8cP\xaf\xd5\xe3(\xe6`:F\xd3\x8c\xca\x08\x06!\x10Sd\x8c\x13\
-\xb1.K\xc8Fv\x01B\xbc\x91\xadXG\x95hq\xd7h4\x8f#<\xf2Y\xb0\n\xad\x1d\xab{$\
-\x1b\xc8\xed\xb3\xdcl\x97? \xd4k\xf5\xa0\x19\x12\xd36\rfE\x8b\x04\x11\x89!^\
-\x80\xb2\xd3\x05;]4\x80\xc7a5\x0e|\xa1\xf3nn\xef\xc3H\xda\xde\x07-\xee\x1a\
-\x8d\xe6q$\xf2o\x88$\x03\xb4\x87\xd9N\x0c\xcb\xeba\xb6W+~\x18\xc6\x86esF\x1d\
-R!\xac\x82\xc4$\x00\xf5\x80\xbb\xf9\x83\xf9=\x93\x00\x040L\x03m\xac|\xd5\xac\
-\xdc&\x96\xe3\r=\xdcd\x03\x9d\xe8h\x19\x8dF\xf3\xd8\xc1\x82\xbb<X\x07\xd2\
-\xcflO\x15\xc6\xbb\xeeX\xdd\xac7\x9aAlZ6c\xd4\xb6\r;\xbe\r\x84\x10\x80\x88b\
-\xcc\\@oc\xa6 \xe0zC\x80\xb67t\xf0!%m\xef\x83\xb6\xdc5\x1a\xcd\xe3\x06\xc6\
-\xfe\r\xf1\x89R\xec\x9e#\xccr\xd3\xc5\xf1\xce#\xeb~5\x08"\xcb2)\xa5\x8e\x9b\
-\xb6\xa2\x9b\xc0\x9b\x04\x0c\x00\xa8\x87h\x18N\xbd\xd6LGq\xc4 l\xc6\x801\x01\
-34FF\xf2]\xaaz\xd8h\xcb]\xa3\xd1<^\xd0F\x99GU \xc0yO\xb3\xdd+\x1e$F\xd2\xf6\
-\xadU\xaa\xcd &\x86\xc59\xda\xb6c\xa1O\xe2\x15\x04\x02\x04\x02\na\xcc9R\xcb\
-\xb5\xeb\x8df\x18\x04\x1c9\xe5\x06\x037=r h\x06\x0f\xbf[I\xb4\xe5\xae\xd1h\
-\x1e/d\xb2\x01\xca\xb0k\x8e0\xd3\xc9\xb8\xf9}\x89/\xabw+a\xccM\xd3\xa0\x94;\
-\x8e\x814\x8c\x9b\x8b\xae\x11\x03\x18\x88Po2\xcb\xb2mo$\x8cXpw\r\x10\x01\x90\
-\x00!\x86A\x88Q\xa3\xf1\xf8\xd1#\x84t\x8d\x90|Xhq\xd7h4\x8f\x11<\xaa\xf2\xb8\
-\x06\xf7\xd8 \xe9Hb\x83$\xffn%\x8c\xb8m\x19\x1c\x88m\x03\x8f\xa3\xb0y7gT\x01\
-\x0c \xd0\x088\x07\xb30z\xacY\xa7\xf5\xda]\xc30\x81\x00\x08\x8f>@kG\xd6(\xb6\
-\xdd-u\xbbkq\xd7h4\x8f\x11qu\x1e\x11\t@L\xbb\x9b\xedV\xaa\xe0dF\xe5\xff"\xe7\
-\xfe\xba\x1f3b\x1a\x18sp,0\x90E\x9c\xdaP!\x06\x00\x10\xcaXH\xcd\xfc\xc8d\x14\
-\x134m\'\x9d\xe6\x8c\x8bC\xdbU\x80i\x99\xa6\xbd\xd5b\xab\xc5]\xa3\xd1<.\xd0f\
-\x996W\x08\x00\xed\x95#\xcc03\xa3\xc76\xca\xc7q\xbd\xda\x88)\x02p4L\xd7"4hR\
-\x00\xdbA\x88\x1a\x04\x00\x91S\x92\xf7\x8a{\x83\x08\x18gn\xcaK\xb9V\xadQg\
-\x8c\x03\xe7\xad*\rc\xa4T2\x8c\xad^\xe0\xd4\xe2\xae\xd1h\x1e\x0f\x90\xc5\x95\
-\x1b\x00\xd0o\x83\xa4\xfc~\xf9\xd6R\x14\x84\xd5J\x8d\x986!`\x98\x8em\xf2\xb0\
-Q3m7?\\\\\x9b\xff(c! \x82=b\x18{\x1a!g,v\xbd\xbc\xe3Xa}\x1dXL\x0c\xe2\xa4\
-\xd2\x04\xc0yg\xfe\x95\x00\x00\x1cPIDATq\xdcl>\xe78[\x1d\x07\tZ\xdc5\x1a\xcd\
-cB\\\xbf\xcdi\x93\x00\xc4\xac\xc7[K\x96+\xf7Z\n\x1a\xcdf3DB\x90S\xc3r,B\xc3z\
-\xc3J\xa5\xf2CC\xcd\xda\x9a\xc1\x1a\xc4\x02L\xed\x8bx1\x88\x90\xb3\xd8\xcb\r\
-\x19@\xa3\xba\xbf\xbez\xdb@\x7fhd\xac\xb8\xe701\x8c-^DU\xd1\xa1\x90\x1a\x8df\
-\xf7\x83\x9c\xc6\xd5\x05\x00@\x00\xd63\xfc\xb1\xb5\xd7R\xd0h\xd6j\x01r@\x04\
-\xc3\xb4\x1d\x935\xeb5\xcbM\x15\x86\x87\x01`\xed\xf6\x97\xb6\x85\xdc\x1d\x8f\
-`\xb4\x112\xc6\xa8\x97\x1b\x06\x1e\xb1\xa0\xb1V\xfe\xca\x84j&m\x16\xc6\x8e\
-\x18\xa6\xf9\x08\x95\x1d\xb4\xe5\xae\xd1h\x1e\x07hm\x01Y(\xbc\xed]\xcdv\xd3N\
-\xb9\xf9\xfd\x00\xd0\xac7\xeb\xb5\xa6a\x1a\x8cq\xcb\xb2,B\x1b\xd5z\xca\xf3\
-\xf2\xc3\xc3\x00\xb0\xbe\xb2`a\xd3H\x1f\x8c\xa0Xo4\r\xd3N\xa7\xb3\xc8\x02\
-\x8c\x9a\xab\xab\x8b\x96\xd1\xcc\xb8\x98\x1b\x197mo\xcb\xbb\x98D\x8b\xbbF\
-\xa3\xd9\xe5 \x8f\xe3\xda-\xe8\xebmO\x0f\x1f%\xc4\xa8W\xebA\x10\x1b\x06A$\
-\x96e9\x164j\x8dT&\x93\x1f\x1a\x02\x00F#\x7f\xe5F.\xbf\x8f\x99#\xcdZ\xc5rs\
-\xb6e\x01F4\xa8\xfb\xeb\xb7,\xd2\xcc\xa5\xc1r\xd2\xe9\xde\xfbho%Z\xdc5\x1a\
-\xcd.\'\xf6\xbfBN\x01\x80\xf5\xf0\xb6\xdb\xa9\xbc\x9b\x1d\xabUjAH\t0N,\x83\
-\x80mb\xcd\xf7\xbd\\.W,\x8abkwnx\xb9=\xa6;\xd2hT\xad\xf4\x10pj\x12\xacW\xd6\
-\xea\xd5e\x934\x0b\x9e\x01\x00\xde\xd0\x91\xceW[\x1f\t\xdb\xa2\x11\x1a\x8dF\
-\xf3\x90\xe0\xb4\x19\xd7\x97\x00\x10\x90tO6@ 5<U\xab\xd4\x1a\xcd\xc84\x10\
-\x0c\xcb@n\x12h6\xea\xd9b1\x93k\xe5\xe9\r\x83\xbaA,+=\x145\xab\xa6\x93G\x16\
-\xa5\\\xbb\xbav\xa7Y_\xb1\x8cf\xc13\x11\xc1J\x15\xdc\xdc\x9e-\xed^o\xb4\xb8k\
-4\x9a\xddL\xec\xdf\x04\xce\x80\xf4\xf2\xb6s;\xb3?\x0c0\x8c\x99i\x12 \x96A8\
-\x01\x8c\x82F\xb68\x9c\xce\xb4\\\xe7\x88\x18\xd4\x1bv\xaa\x184|\xe2\x14\x01i\
-\xca\xb1*+\xb7\xc2\xe6\x9am\x86\xb9\xb4\x89\xf8\x08\xb6\xe3\xe8\x8f\x16w\x8d\
-F\xb3k\xe1q\x8d6\xca\xd0sG\x0e\x04bq\xa3\xc8\xb9A\x08%\x86i\x00\x07\xce\xa2\
-\xa0^\x18\x1ds\xd3\xa9V!\xce\xa3 \xe4\x8c\x04Q`\xbbE\xce\xa8k\x93\xbb\xe5\
-\xc5(XM9\xd4s\r\x91e\xc0\xcd\x8e\xd9\xa9\xc2V\xf6\xae?Z\xdc5\x1a\xcd\xae%\
-\xaa\xdc\x04@ \x84uM\xed\x8b\x1c\x9d\x12\'\x19\xe4\xcc0L\xc3@Nc\x1a\x05\xc3{\
-\xf7Y\xb6-\x8ap\xce\x9b\xb5F\xb3Q\x8d)\xb1\xec\x14r\x96v\xad\xca\xcab\xd0,g\
-\xd3\x98\xb2\x8c\xd6\x86z\x86\xe5\ro#\xb3\x1d\xb4\xb8k4\x9a\xdd\n\x0b\xd7\
-\xdb\x1b\xe9aW\xb3\x1d\r\xd7J\xefc\x80\x84\x10\xd3\x04\x16\x854\x8e\x86\xf7\
-\xee7M\xb3U\x03\xa5A\xbd\x194k\x0c-\xd32\r\xd3\xb4\x0c\xbe\xbe\xb2\xd4l\x94\
-\x0b\x1e\xda\xe6\xc6d\x91.Nt\xdd\xd9\xe3\x11\xa2_b\xd2h4\xbb\x93\xd8\xbf\t\
-\x00\x04\x802\xe8\x96#\x0c\xcd\xf4\x1e\n\x0epf\xdb\x06\x8b\x9a4\x8eJ\xfb\x0f\
-l({L\x83z\xc0y\xcc\x88\x8bHl\xc76\x90\xd6\xd7n7\xebwr)\xae*\xbba\xa7S\xc5\
-\x89\xad\xea\xd6\xa0h\xcb]\xa3\xd1\xecBXs\x85\x85\x15\x00\xe0\xd85\xb5/"Iqs\
-\x048u\\\x9b\x065JY\xe9\xc0\xc6~I\x9c\xb2\xa0\xd1\xa4q\xcc\xc0\xe4,J\xa7\xd3\
-4\xac\x07\xd5\x95Z\xbd\x9c\xf7\xd017\xd5\x97\x19\x99J\xa4\x08\xde\x0ehq\xd7h\
-4\xbb\x0f\x8c\xfc\xaf\xc4\xa7\xae;r "I\xef\x05\xc3t-3n\xfa\x1cI\xe9\xc0\x01\
-\xf9W\x1a\xc5A3\xa01\xe5`Fq\xec\xa5\xd3aP\x0b\xab\xcb\xf5\xc6j\xc1\x03{\xb3\
-\xb2\xdb\xe9!\'3\xf2\xd0;t\xffhq\xd7h4\xbb\rZ\xbf\xc3\xe3\x1a\x01`\xdd\xccvD\
-N\xec\x1cq\x86,\xd3\x88\x1a\xeb\x86\xed\x8e\x96J\xf2\xafq\x10FQ\x14\xc7\x94\
-\xa3A\x19M\xb9N\xd8\xf0\x83\xear\x14\xaf\x17=\xb06+;\x10\xe2\x8dLnE\x97\xee\
-\x1f-\xee\x1a\x8dfw\x81<\xae\xceC;\xd9@\x17o;!\xc4\xddc\x1af\xdc\xac\x98N\
-\xaa8\xba\xb15G\x1c\x86\xf5Z\r\x89\xc9\x90 \xf2\xb4k\xc7\xcdZ\xdd\xbf\xcd\
-\xa8\x9fM\xa1m\x12\xdc\xbc.\xab\xa6\x08\xdenhq\xd7h4\xbb\x8a\xb8vK\xa4\xf6e\
-\xddw\xe4\xe0\x86]4\xed\x02m\xfa\xb6\xd7J\x1a#h\xd6\x1aQ\x10049\xe7\x061L\
-\x93\xc4\xcd\xea\xfa\xda\x12\xf2Z.\xddE\xd9\x89i\xa7\x87\x0e=\xfc\x0e= \xdbn\
-\x11@\xa3\xd1h\x1e\x18\xe4q\\k\xa5\xf6\x8di\x97\x02\x1c\tqKQ\xb3\xeaf\xf3\
-\x1b\xca\x8e\x10\x05a\x14\x04\x14\rJ\x99eZ\x048\x0f\xaa\xab\xe5\x85 \xaaeS\
-\xd0\xa9\xec\x00\xe0\r\x1d6\xccG\xb0\x0b\xc7\x80h\xcb]\xa3\xd1\xec\x1e\xe2\
-\xaaH\xedK(C\x8eI\xb3\x9d#R\xf0\x08%\x99b\xd1\xcb\xb6\xdd)\x88A#\x08\x82\x80\
-\x83I)u\x1c\x1b9\xe5A\xb5\xea/5\xc2\xe6p\x968\x16t*\xbb\xe9f\xdd\xfc\xbe-\
-\xe8\xd1\x03\xa3\xc5]\xa3\xd1\xec\x12\x90E\xb4\xbe\x04]\xdfZ"\xc094#\xb4S\
-\xd9\xec\xf0H*\x9dn\x1d\x82\x10\xd6\x1bA\x18RfPF]\xc7\xe2,\xc6\xb0\x1a\x07K\
-\xf5 H;\xc4s\xbb(;\x00d\x86\x8fn\xc3\xf0G\x15-\xee\x1a\x8df\x97\x10W\xbfB\
-\x16\x03\x01\xcaaS\x8e0\x02\x8cC\x10!\x92Tvx|C\xd99\x06\xf5F\x18\x86\x0c\r\
-\xc6\xb9\xebX\xc8\x19\x0f\xab,\xb8\x83<\xb0-\x92Mu\xc9\x0f\x0c\x00N\xa6d{\
-\xc3[\xd2\xa7\x07G\x8b\xbbF\xa3\xd9\rp\xda\x88\xeb\xb7\x81t\xe4\x08#\xc0\x18\
-\x041"\xc2\xe8\xf8\xc9L\xbe\x95\xdb\x8bs\x1e\xd6\xebA\x183\x0e\x9cs\xcb\x00d\
-\x94\xb0\xa6\x01U \x8d\x98\x13\xdb\x04\xc7\xec~\xae\xf4\xd0\xb6\xd8\x8e\xa3?\
-\xdb\xfa\xb1B\xa3\xd1h\x06$\xf6o\x022\x00`\x0c6r\x84\x11\xa0\x0c\x9a1"\x82\
-\x97\x1b\xc9\xe4[+\xa8\x9c\xb1F\xc5\x0fBF9A0L\x13\x08R\x03\xe2T&\xc5\xe3\x8a\
-m\x19\x8c\x83c\x82\xd9M \xadT\xc1r\xb6i\xf8\xa3\x8a\x16w\x8dF\xb3\xe3\xe1Q\
-\x956V\x00\x08"P\xbea\xb6\xc7\x14\x82\x18\x01\x81\x18\xa4P:\xd2*\xccxP\xab\
-\xc5\x9c\xc4\x94\x13 \x06\xe1\x843\x02,?<\x1c5\x96S\x16C\x04\xc6!\xe5t\xf3\
-\xb6\x8b\xa4\xed\x8ft\xe7\xeb\x01\xd1\xe2\xae\xd1hv<\x91\x7f\x13\x00A\xe4\
-\x08k\x9b\xed\x11\x83\x90\xa2\xc8\x19\xe6\xe5\xc6\x9ct\x0e\x00\x90\xb1f\xad\
-\x1eD<\x8a\xa9e\x9a\x04\x18a1\x00/\x8c\x96h\xdc\x84\xb0l\x99\xa4\x19A\xca\
-\xee.\xe0nv\xcf\xb6J\xda\xde\x07\xeds\xd7h4;\x1b\x16\xac\xb2`\r\x00\x10Q&\
-\x1b\x88(Db/l\x02\x86a\x15\xc7\x8e\x02\x00\xa3\xb4Y\xad\xc5\x0cb\x86\x96i\
-\x02p\xc2c\xdb&\xd9\xa1Q $X\xfb\xc2\xb1\x88\xf8k\xc6\x15\x93\xc5&\x88\xe9x\
-\xc3G\xb6\xb4o_\x03-\xee\x1a\x8dfG\x83\xd1\xfa5\xf1I\xbc\xb5\x84\x00A\x04L\
-\x86\xcb \x14\xf7LZN\x8aEQ\xbd\xd6\x08)p\xc6\\\xc7b,\x8e\x1b\xb5\xa1\xd2\x90\
-\x93)\x00@\xb4~\xcd\x82&\x004BL\xd9\xdd\x83d\xb2\xa5\xe3\xdb-i{\x1f\xb4\xb8k\
-4\x9a\x1d\t\x8di\x18#\x84\xcb$\xae\x13bR\x86\x9c#G\x08b\xe0J \xa4\xedz\xb9\
-\xa1\xfd\\({\x8c\x9cs\xdb6\x19\x8bI\xdc\x18\xde3b\xa7s\x00\xc0i\x83\xd5\x97\
-\x00\x80q0\t\x18\xdd\xa4\xddN\x0f9\x99\xd1.\x7f\xd8\xaehq\xd7h4;\x0cD\xf4\
-\xd7+\x14,\xdb\xe0Fc\x1e\x88\x01\x00\xac\xa5\xec\xc8\xb9L\x15F\x90\xb3\xd1\
-\xfd\'Y\x18\xd4\xebA\x18#\xe3\xcc\xb1L\xe4\x0c\xe2F\xb14j8\xad\xfd\xaf\xa9\
-\x7f\x13\x91\x13"b(\xbb\xbd\xb3\xb4\xcd6\xbf\x1e\x04\xbd\xa0\xaa\xd1hv\x18\
-\xeb+\xab\xeb\xe5\x15\xd72\xd2d\x8d\xf0&\x01\xc2\x18\xc6\x14\x9a\x91\xaa\xec\
-\x80\x9ce\x0b{\x08\x10\xbf\xda\x0c)0\xce]\xdbB\xe4\x845\x8b\xa5\x92Tv\x16\
-\xae\xcbM\xb4\x11\xbb\xaf\xa3\xa6\xf2\xfb-7\xb7E\xdd\xfb\x86\xd0\xe2\xae\xd1\
-hv\x18\x8dz\x1d(\xc7\xb0F\xeb\xb7\x01\x0c\x04\x08c\x14\xaf))\xca\x8e\xa6e{\
-\xb9\xb1z\x00\x94\x01r\xee\xda\x06\xe7\x94\x05\xd5\xe2\xe8\xa8\xe9\xa4\xdb\
-\x95a\\\xb9\x0e\x00\x84\x00\xc7\xaeY$\xb7{\xf6\xc7^hq\xd7h4;\x0c/\x9f\x07\
-\xcb\x8a\xaa_!\x0b\x88A\xc2\x18\x1bQB\xd9\xb9e\x99CcS\x11s\xe3\x98\x02\x80c\
-\x1b\x8c\xc6\x185\x87\xc6\xc6\x0c;%\xab\xa2\x8de\x1eU\x01\x00\x11b\xda\xcd!\
-\x03\xe0\r\x1d\xda\xce\xd9\x1f{\xa1}\xee\x1a\x8df\x87Q(\xe4ic\xdd\xe1u\x00\
-\x83q\xa8\x07\xa8\xbaS\x90s\xdbv\xf3\xa3\x93\x11\xf7(e\x86A,\x93\xd0(D\x16\
-\x8e\xec\xd9\xa4\xec\xc8i\xec\xdf\x04@BH\xcc\x90\xf1.A2\xa6\x93q\xf3\xfb\xb7\
-\xa8c\xdf(Z\xdc5\x1a\xcd\x0e\xc30\xcd\\:dM\x00\x02\xcd\x803\x8e\xaa\xb2;\xae\
-W\x18\x9dl\xc46\x00\x9a\xa6A\x08\xf28B\x1e\x8f\xec\xdd\x93\x08d\xa4\xb5\x05\
-\xa4M\x10\xa9\x80{\x98\xed\x99\x91\xc9m\x9e\xfd\xb1\x17Z\xdc5\x1a\xcd\x0e\
-\x83GU\xd6,\x13 \x94b\x10mR\xf6T:\x97\x1b>\x1a\xc4&g\xdc\xb6-\xc3@\x1a\x04\
-\x96\x89\xf9\xd2\x98amr\xad \x0b\xe3\xda\xa2\xf8L\xd9\xe6,\x92m\xec\xcc\xe8\
-\xf6\xcf\xfe\xd8\x0b-\xee\x1a\x8df\x87\x11Un\x00r \xd0\x08\x91s\x0e\x00\x84\
-\x18\x88<\xe5\x15\xbc\xc2\xa1Fdr\xce-\xd3\x00`\xb4\x19\x98\x16\x14Jc\xc4Hj]T\
-\xb9\x0e\x9c\x02\x00"t7\xdb\x89\x91\xd99\xef\xa3v\xa2\xc5]\xa3\xd1\xec$Xp\
-\x97\x05k@ \xa2\x18QpRY\x00\xa0q\xd3\xf3F\xd2\xf9\x83\x8d\x90 2\xc7\xb1\x019\
-\r\x1an\xca\xc9\r\x8f\x12#\xe9W\xe1Q\x955\x97\xc5\xe7\x98A\x97M\xb4\x01\xd2\
-\x85q\xd3\xc9<\xf4\xfe<4\xb4\xb8k4\x9a\x9d\x04\xad\xdf\x06\x00@\xa4\x98\x1a;\
-\xf4\x84\xeb\xe5y\xdc\x8c\x9a\xf5(6\x1a!!\xc0\x1d\xd7F\xceh\xb3\x91\xce\xa62\
-\xc5\x11\xd2-p=\xaa\xcc\x89\x94\x8f\x9c\x83LG\xa3B,7]\x9cx\xd8}y\xa8\xec\xc8\
-\x85\x02\x8dF\xf3x\x824\xa0\xc1] \x103\x12\xb3\xa2\xe9\xe4\t1\x10L\xcaS!5\
-\x01\xb8i r\x167k^\xce\xcb\x0euWv\xd6\\\xe1aE\x18\xeb1\xeb\xba\x89\x1exC\x87\
-\x89i?\xe4\xde<\\\xb4\xe5\xae\xd1hv\x0c\x91\xff\x15\xf0\x18\x01\x83\xc8\x8c9\
-\xe1\x8cr\xd3\x89cdH,\x0b\x80#\x02\xa1\xf5jv\xb8\xe8\xe5z&\xe6\x8d\xab\xf3\
-\x00\xad}<\xba\x86?Z\xa9|*\xb7\xf7!vcK\xd0\xe2\xae\xd1hv\x06<\xae\xd1\xc6\
-\x1d \x10E\x10RN\x08\x8f\xa38b$\n8 \xb1\x08DqL0\x1e\xda3b\xa7z\xee\x94\xc4\
-\xa3*\x8fk\xd0g\x1d\x15\xc0\x1b\xde\x19\xdbq\xf4G\x8b\xbbF\xa3\xd9\x19D\xfeW\
-\x80\x1c\x01\x1a\x11"\x07\'\x97\x8e(E\xca\x18r\x02\xc0\r\xd30\xa08<j\xba^\
-\xbfJ*\xd7\xc5v\x1e\x94b\xd7\xf0G7\xb7\xc7N\x17\x1f^/\xb6\x0c-\xee\x1a\x8df\
-\x07\xc0B\x9f5V\x80@\x18!e\xc8\xc1\xa9\xd7\x9aa\x18\x03\x80A\x08\x00\x10\xc3\
-\x04d\xc5\xd1\x91~\x954Wx\xb4\xdee\x13\xed6\xc4\xb0\xd2C;8\xfcQE\x8b\xbbF\
-\xa3\xd9\x01\xc4\xfeM\x04$\x08\x94A\xca\x86\x18\xdd\x80\x12\x1a\xc7\x00\x04\
-\x00\x01\x01\x01\x10\x91\x06u\xd3\xee\x91\x07\x06Y\xe4_\x07\x00\x02\x10Q\xc4\
-nf{\xaa8a*\xf9\tv4Z\xdc5\x1a\xcdv\x876Wi\xb0F\xc4f\x1a\x06\x18@L\xc2\xb8\xed\
-2\xca\x84\xb2\x03 "\xb1,b\xb9\xe9\x9e\x95\xd4\x97D\xb2\x01\xd6#\xfc\xd1\xb0\
-\xd3\xe9\xc2\x81\x87\xd8\x8d\xadE\x8b\xbbF\xa3\xd9\xe6`\xec\x7f\x05\x00\x08\
-\xc08\x02\x00\x021\xb0\x91K\xd5\xa87\x1c\x86\x9cs\x06\x00\xa6AFG\n\xa6\xd3\
-\xdd\xeeF\x16\xb5\x82d\x00b\x8a]\xdfZ\xf2\x86\x8ft\xbe\xc8\xbas\xd9==\xd1h4\
-\xbb\x12\xdaXf\x91/\xccv\x19\xb9\x88@ XqS\xa1\xe3flo\x8fiY\xb6\x932\xad\x9e\
-\x89y\xe3\xea<\xf2\x18\x00\x10\x81wK\xdan\xa5\x8bnv\xec\xa1u\xe2\x11\xa0\xc5\
-]\xa3\xd1l_\x10Y\xd46\xdb\xd5%P\x02\x00@\xe2f\xd5\xb4\xe3\xcc\xde\'\xba\x19\
-\xe2\xb2\n\x86q\x85\xd6n\x89"\xdd\xcdvB2#\x93\xdf|\xeb\x1f)Z\xdc5\x1a\xcd\
-\xf6\x85\xd6\xef\xf0\xb8A\x00(\xef\x92\xb8\x91\xb4\xb66\xed\xa6\xec<\x00ZCZ\
-\x8d\x9a\x15\x1e\xd5\xc5\xce\xa8\x9ccWo\xbb\x9b\xdb\xb7\xe3v\xd1\xbb\'Z\xdc5\
-\x1a\xcd6\x059\x8d\xfcy\x10\xde\xf6n\x91\x8bf\xaa\xe0f\xf7(\x070\xe0M\xa0U\
-\x16\xf9a\xb3\x1a4\xa30\xa4\x94\xf1\xa1,1\x0c\x02\xd8\xddl\'\xa6\xed\r\x1f~\
-\x98\xfdx4hq\xd7h4\xdb\x94\xb8\xb6\x88, \xbd\xf2\xad\x13\x92+\x1d\x03\x00\
-\xe0!\xf0:F\x958\xac\x05\x8dz\x18\xd2(b\x94qD@\x00\xdb$\x86A\x00\x80\xf2\xee\
-{-\xa5\x8b\x07w\xe2.z\xf7D\x8b\xbbF\xa3\xd9\x8e \x8b\xe3\xea-\x10y\x02x\x17\
-\xb3\xddIgY\xe4\x87\xd5y`\xf5\xa0\x19\x85\x11\xa51\x17\xe14@\x80\x00\x18\x06\
-\xc4\x0c,\x8b\x10h\xbd\xb5\xd4m\x17=/\xb5\x8b\xc2\x1fU\xb4\xb8k4\x9a\xedHT\
-\xb9\x8e<\x02\x80\x98w\xbcpD\x802\xb8s\xbb\x16\xce\x7fQ\xf4\xc04\x90\xf3\x96\
-\xa0\xab;\xe2q\x04\xc6\xc1\xb5\x10\x10\x82\x18\xc2\x08\x88\x01&\x01\x00@\x04\
-\xc3\x00B\x8c\xcc\xe8\xb1\x1d\xba\x8b\xde=\xd1\xe2\xae\xd1h\xb6\x1d,\xac\xc4\
-\xf5\xdb\x00\xc09p\xd6\xe1%\x07X\xab\xe1J\x85\x8e\xe6\r\xd3 \x88\x9b4]\xbe\
-\xd5\x141b\xdbF\xdas,\xcbD\x1b\x89IM\x03\x08gB\xdb\xc3\x98\x071)\xda;x;\x8e\
-\xfehq\xd7h4\xdb\r\x8c*7Z\xe9\xbdXr\t\x94\x10\x08bX\xafq\xcf52)\x82\x002\xf4\
-\x1d\x11\x00\x90\x18\x86m\x99\xaek9\x8e\xe9\xda\x06A\x16\x061\xa5\x0c8\xa71"\
-\x10B\xc04\xb9k\x11\xbfA\xe38\xec\x13\x1d\xbf\xa3\xd1\xe2\xae\xd1h\xb6\r\xac\
-\n\xbcJ\x9bwY\xb0\x0e\x04X\xb7\xf0G\x00X\xabr\xc6a8M\x0c\x02\x1c[\xb2nZ\x86\
-\xebZ\xaec9\x8ei\x12\xe4\x9c\xd1(j\x04\x8cq\x04D \x04\x89\x83\x04\x0c\x8c\
-\x01yL\xc120\x93",\x0e!\xbd\xdb\x82 \x05Z\xdc5\x1a\xcd6\x80\xc7\xf1\xdd\xcf\
-\xeb\r?\x8c\xd01\xa8m\x11\xc0.\xe1\x8f\x84@#\xc4J\x03\xbd\x14I9\xc0\x18p\x04\
-\xc71\x8aE\xcf\xb1\x08pN)\x8d\x9a\x11\xa3\x8csq\x00\x10@Nl\xf0\xc6M\xb7\xc8\
-\x18\x1a\xd8\xc4\xfau\xc2#\x86\xc4$@\xe3p\xeb\xfb\xba5hq\xd7h4\x8f\x1c\xac\
-\x95/\xaf\xdf\xbdK)"\x80\x9b5\xba\xbf\xb5D\x00\x00\xd6j\x08\x00Y\x97\x10Bl\
-\x87\xd8&\xb1-\x03\xe3\xb0\xde\x10Fz\xfb\xed\xd5\x8d#I-\xc8\xf0(6\xcd\x15B\
-\x08Gbc:mE\xa2$\x8d\x9a[\xd9\xcf\xadD\x8b\xbbF\xa3y\xc4D\xcd\xca\xfa\xdd\xbb\
-\xc2N7\x0cb\x99\x80\x08Ld\x80i\x07\xb7p\x0e\x8c#\xe3$\x932F\x0b\xc4\xb5\r\
-\xc3\x00\x82\xc8\x11\x19\xa54nE\xcbt:q\x10\xc0\xcb\xa6\xea\xa1A)\x05@ f\xda1\
-\xc5\x9f\x0c\x00\x1a\x07[\xd8\xd1-E\x8b\xbbF\xa3y\xc4\xc4Q$b\x19)\x03\xcf\
-\x06\x02@9"\x02G`\x0c\x18\x07\x04 \x84X&q\x1c\x925\x00\x119g\x9c\x82\xf0\xda\
-\x10\xe8\xbf)\x1e:\xc4\xb7\x0bc!\xcdp\x8e6\x89,ZC\x06\x00@\x08p\x1a"b\xd7}\
-\xb4w:Z\xdc5\x1a\xcd#\xc6I\xe5\x80\x10\xe4H\x19\xba\x16!\xa6\x19\xc5\x18D\
-\x94\x10b\x1a\xc4\xb1\x89i\x00!\x88\x1c\x90s\xca\x01\xda\x06\xfa \x92L\x80\
-\xb0\xa8a\xb0\xf9\xb4\x9d\x02 H\x03\xce\xa8<\x14Y\x84\xc8\t1\x1fV\xdf\x1e\
-\x1d\xbb3z\xff1\xc1\xf7\xfd\xf9\x85E\xdf\xf7\x07/\x10\x86a\xe7!\xf3\x0b\x8b\
-\xcb\xe5\xf2Cl\xe8\xfd\xb3\\.\xdfo\xabf._\xe93\x14\xfd\x0f\xb9p\xf1\xd2\xfd\
-\xb5\xef\x9b8\xfb\xf6\xc1\xf7\xfd\x99\xcbW\x1ea\x03l7\xedx#\x84\x90\x91\x91L\
-a\xb4\xe0d2\xe9\x94\x95v\x0c\xcf%\xae\r\x06\xe1\xc89c\x88\xd8\x8a{\xbc_3\x9b\
-\x10\xc29\xa7A\x9d\x865\xc6\xd8\xa6\n\x90\x89\x08\xca\xdd\x87\xb6\xdcw$a\x18\
-\xbe\xfe\xc6\x9b\xae\xeb\x8e\x8d\x95fg\xaf\xf9\xbe\xff\xf2K?q]W-3s\xf9\xca\
-\xef\xde:;1~`\xb9\xbc\xf2\xec\x99\xd3g\x9e9\xed\xfb\xfe/^}m\xac4\xba\\^\xf9\
-\xd1\x0f\x7f05y\x14\x00\xfe\xf1\xd5\xd7\x00 \x08\xc3\xa9\xc9\xa3/\xbe\xf0<\
-\x00\xfc\xf5\xdf\xfc\xad\xac\xe4\xe7\xaf\xfclb<\xf9r\xb6\xef\xfb\x7f\xf7\xf7\
-\xff\xd0\xf5O\x9d\xcc^\x9b\xbbp\xe1\xd2\x7f\xfc\xab\xbf\x1c\xb0k\xcb\xe5\xf2\
-\xebo\xfc6\xe5\xba\xae\xeb\x84a\x14\x84\xe1\x9f\xbf\xf2\xd3|>\xaf\x96\xf9\
-\xc7W_;s\xe6\xb4h\xff\xc6\x89f\xaf\xe5\xf3\xf9D\xc9\xfe\xcd\x96\x87\xbc{\xee\
-\xfc\x99gN\xab\x85\xe5 \xb8\xae+F\xaf\x7fG\xeey\xf6^t\x9e\xe8~k\xf8\xfaT\xfc\
-\xea\xec\xec\xb5\xe9S\'\xb7\xfe\xd4\x92\xa1\xe1\x91\xc8X\xb5l\xde\xf0\xeb4\
-\xa6\x88H\x00\x90\x03\xb6=._\xd3oB\xa0\xbb\xef\xc6\xb4\xcc\xfe>\x9d\x9d\x8b\
-\x16\xf7\x9d\x87P\xf63g\x9e)\x97\xcb\xf3\xf3\x0b\x13\x13\xe3\xd3\xd3\'_\x7f\
-\xe3\xcd\x84\xbe\xcf\xcf/\xbc\xfc\xd2O\xa6&\x8f\nM?\xf3\xcc\xe9\xcff\xae\x08\
-\xf9\x98_X<w\xee\xfc\xd4\xe4\xd1\xd9ks\xae\xeb\xfc\xfc\x95\x9f\x85a\xf8w\x7f\
-\xff\x0f\xcf\x9e9-j\xf8\x1f\xff\xfd\xbf\xf5i\xc0G\x17.\xe5\xf3\xf9\x99\x99\
-\xcb}\xc4\xfd\x97\xbf\xfa\xe7\x9f\xbf\xf23\x00\x18d\x02\x90\xf8\xbe\xff\xcb_\
-\xfdZN<\x000\xbf\xb0\xd8\xa9\x98g\xce\x9c\xbe\xafj\x07o\xb6\x8a\x18\x04\xdf\
-\xf7\xff\xe9W\xbf\x9e\x9a<\xfa\x00g\x94\x83p_\'z\x80\x19b\xa7\x83\xb4\x19U\
-\xaeG1\x86a\x00\x00\x84\x00\x1a.Gb`D\x80\x7fma\xef}^\x00\xcb4\x8d]\x9a~`w\
-\xf6jw3s\xf9\xca\xc4\xc4\xf8\xfc\xfcB\xa5\xe2\x9f9\xf3L\xa5\xe2/,,NMM&\x9e\
-\xac\x85\xb2\xab\xdf\xcc^\x9b\x13\xdfL\x8c\x1f\x08\xc2\xd0\xf7\xfd\xf9\xf9\
-\x85\xa9\xa9I\x00p]Wh\xbd(\xb9\\.\xf7r2\x08\xc7\xce\x9f\xbf\xf2\xd3\xd9ksj\
-\x99\xf9\x85\xc5\x0b\x17/\xcd\\\xbe"\n\x88\xffD\x81|>\'\xbeT\x0b\x87a(\x9at\
-\xe1\xe2%\xf9\'\xd1B\xb5\xd9RRE\x93.\\\xbc\xe4\xfb~>\xbf\xf1\xd6\xc9\xcc\xe5\
-+j\r\xbd\xe8\xd5\xec{\x92\xcf\xe7S\xae[\xf1\xab\xa2#\x89\xce\xfa\xbe/k\x0b\
-\xc3\xf0\xc2\xc5K\x17.^\n\xc3\xb0s\x10DO\xe5\x08\xf7?Q\x18\x86\xa2_\xb2\xf2\
-\xe5rY\x0e\xaf\x1c+\xb5\xe3r|\x84GKV+\x87Z\xd6 \xff$\xca\x8b\x06\x0f> \x0f\
-\x83\xda\xdd[A\x10\x01\x02!\x04\x810g\x1f\xc9?!\xfe\x03+\x8f\x0f\xcfm\x82\
-\x00H\xb9X]\xdduhq\xdfy\xcc\xcf/\x08/\xc1\x8f~\xf8\x83\x89\xf1\x03/\xbe\xf0\
-\xfc\xf8\xf8\x81\xe9S\'\xe7\xe7\x17\xba\x96\xff\xdd[g\x9f=s\x1a\x00|\xdf\x97\
-Va!\x9f\xab\xf8\xd5M\xdf\x14\xf2\xe2&w]\xf7\xdc\xb9\xf3\xff\xf4\xab_\xff\xf2\
-W\xff\xdcY\xdb\xcc\xe5+\x13\xe3\x07\xf2\xf9\xfc\xf4\xa9\x93\x9f\xcd\xb4\x94b\
-\xf6\xda\xdc\xb9s\xe7E\xdbf._\xb9p\xe1"\x00\\\xb8pq~aq\xb9\xbcr\xee\xdcy\xd7\
-u\x7f\xf7\xd6Y!:\xbe\xef\xff\xee\xad\xb3\xae\xeb\xbe{\xee\xfc\xec\xec5QRx\
-\xbd\xc5\x83\x88\xa8SJ\x8f8J4)\x08B\xf1y\xb9\xbc\x02\x00j\r\xe2\x9b^tmv\x7f\
-\xc4\xd9_\x7f\xe3M\x00\x10\xde-\xd1G\xe1\xef\x02\x80\xe5\xe5\xf2/^}M\xea\xf5\
-\xbb\xe7\xce\x8b/_\x7f\xe3\xcd\x8a\xef\xab\x830\xbf\xb0x\xe1\xc2%\x00\x98\
-\x99\xb9\xdc9\xb5$N\x04\x00\xbf\xfc\xd5\xaf+\x15\x1f\x00^\x7f\xe3\xb7B\x9d_\
-\x7f\xe3\xb7b|\xc4ht\x0e\x9d\x1c\x9fT\xb7\xa1\x9e\xbd6\xf7\xd6[o\xcb\xe6A\
-\xfbA!\x08\xc2 \x08E\xcb\x1f!Q\x14\xb5\x9d/\x181\xaf\xe2\x13\xbf\xbc\xd2X[\
-\xad\xde\xad\xf9\r\x0f\xe0\x1bZ\xedD\x8e-\xf8\xc6w,fq\xf4\xcd\xd4\xbf\xcd\
-\xd0n\x99\x1dL\x18\x86\t?{\'B\x86\xee\xcb\x93\xfb_\xff\xcb\x7fv]7\x0c\xc3_\
-\xfe\xea\xd7\xd2\xd8\x97|6s\xe5\xe5\x97~\x0c\x00\xd3\xd3\'\x7f\xf9\xab_\x0bO\
-\xce\x85\x0b\x97^~\xe9\xc7r\x9e8\xf3\xcc\xe9\xbf\xfe\x9b\xbf\x15\x1e\tiE>9}r\
-v\xf6\xda\xc4\xf8\x81\xcff\xae<9}2\x0cC1?\x01\x80\xea\xf5N\xf4H\xccXB\xf2\
-\x9e=sZ\xf5\x0b\x8b\x1a\xfe\xeb\x7f\xf9\xcf\xe2\x8c\x7f\xf7\xf7\xff\xd0\xa7S\
-]\x9b}\xcf\xa1\x98\x9d\x9d\xcb\xe7s?\x7f\xe5\xa7\xea\x97\x1f]\xb8\xd4\xb9\
-\x0c\x00\x00/\xbe\xf0\xbc\x18\xab\xbf\xfe\x9b\xbf\xfdy\xa9\xf4\xf3W~&\x07\
-\xe1\xc2\xc5K\xae\xeb\x8c\x8f\x1f\xe8\xf5C\xa8\'\x9a\xbd6\x97\xcf\xe7\xe4$7;\
-{\xcdu\x1d1J\xe2\xf0^C\'\xc7\'1\xd4\x00p\xe1\xc2\xa53gN\xbb\xae[*\x95~\xf7\
-\xd6Y\xdf\xf7\x85\x8fN\x94/\x14\xf2b\xaaxT\xd8\xa9|\xd0\xbcc\x12\x00\x04\xd3\
-6m\xcbaq\xcc(\x03BR\x8ei\x18\x06r\xf6u\x9d3\x84@j\xaf\xe1\x0c1\xc6Mv\x17\x83\
-\x96)@\x009\x8b\x00\xbco\xa0\x1b\xdb\x0c-\xee;\x8f\x89\x89\xf1\x99\xcbW\x9e\
-\x9c>\xf9\xee\xb9\xf3SS\x93\xb3\xb3\xd7\n\x85|\xb9\xbc"\xe5@\xf2\xbb\xb7\xce\
-\x96\xcb+R\x9b\xf2\xf9\xbc4\xd5+~\xb5\x90\xcf\x89o\x00\x0e\x00@\xa5\xe2\x8f\
-\x8d\x95\xa0-\xaf\xae\xebNM\x1dM\x98\x99\xc2\x11!\xd6`\x05\xb3\xd7\xe6\xa6O\
-\x9d\\.\x97\xef\xe9)\x9e>u\xf2\xff\xfc\xbf\xfe\xef\x17_x~\xe6\xf2\x95\xff\
-\xf4W\x7f\xb9\\^\t\xc3P\x98\xb7\x00P\xc8\xe7D\xd7\x84*\x81\xa2e\x92\xc4)\x96\
-\xcb+\x05\xc5?\xa3~N\xd0\xab\xd9\xfd\x1b|\xe6\x99\xd3S\x93G\x7f\xf1\xeak\x89\
-I4\xf1\xb8#\xbf\xef3[\x88s\t\xeb\xbbsbH\x9c\xc8\xf7\xfd\xe5\xf2J\xd8\x1e\x99\
-R\xa94V*={\xe6\xf4\x85\x0b\x97~\xf7\xd6\xd9\x1f\xfd\xf0\x07b\xeaM\x0c\x1d(\
-\xe3\x93\x18j\x00X.\x97gf.\xcb\xf2A\x18\x96\xcbey\xc1<r/\xbf\x97\x1f\xad\xaf\
-\xcd\x99\x06\x03$\x16\xd6\n\x99\xb4\x93\xceS\x06H)\x89\xd6h\x18\x7fm\xb7;\
-\xd6\xc3,\xe7\x9e\x15S\xc30BV0Y\xdd1\x1b\xe2\xc5\'\x1a5]\xaf\xf8\xcd\xf4d;\
-\xa1\xc5}\xe71}\xea\xe4\xebo\xbc\xf9\xc2\x0b\xcf\x03\xc0\xcc\xcc\xe5\xb1\xb1\
-\xd2\xf8\xf8\x81\xb7\xdez;a`\n\x9b]\x8d\xee\x10^u\xb1\xa0\x9ar\xdd|>?11>3sy\
-\xfaT\xcb\x8e~\xf1\x85\xe7\xe7\x17\x16\xcb\xe5\xb2P\xd5\xf9\xf9\x85\xe9\xe9S\
-j\x9d33W~\xf4\xc3\x1fHY\x9c\xb9|\xe5\xa3\x0b\x97\xa6O\x9d\x9c\x9a<:\xbf\xb08\
-1~@:v:\x9b\xed\xba\xaeh\xb9p\x8f\xe4\xf3y\xd7u\xc5"p\x18\x86\x15\xdf\x17-\
-\xfc\xc5\xab\xaf\x15\nIY\xef\xca\xc4\xf8\x81\xdf\xbduV\x08b\x18\x86\xd2-3{mn\
-b\xfc\x80\xda\x86^\xcd\xbe\xe7)\xf2\xf9\xfc\xb3gN\xbf{\xee\xfc\xcb/\xfdD\x1d\
-\xc6\x0b\x17/\x89\xe8#i\x1a\xdf\x93\xe9S\'\xcf<s\xfa\xdds\xe7\xc5O\xd0\xe7D\
-\xe3\xe3\x07\xe6\xe7\x17\x84\xc9\xbf\\.\x17\xf2y\xd1\xd9\xe9S\'g\xaf\xcd\xcd\
-\xcc\\\xfe\xf9+?\xeb\x1c:\x95\xc4P\x8b6OO\x9f\x12\xb3\xe6\xfc\xc2\xe2X\xa941\
-1^.\x97\xc57\xe5v\xc8\xa9\x88=\x1d+\x95\x06\xe9\xd17\x88e\xbb\xe9\xe2\xa1\
-\xa82g\x8a\x97N\xebw\xe2h\xdd0MNcF\xbf\xbe\xb2\x03\x00q\xd3\x99j#\x8cCN\x00\
-\x11\xcd\xac\x9b"\xa4\x81"\xa5\x98\xb1\x0b\x83\xdcA\x8b\xfbND\xdc\xd8\xaf\
-\xbf\xf1f>\x9f\x1f\x1b+U*\xfe\xfc\xfc\xc2\xcb/\xfd\xb83\x14R\xfe\x0b\x00\xff\
-\xe3\xbf\xff\xb7\'\xa7O\xfe\xe2\xd5\xd7\xe6\xe7\x17D($\x08\x9d\xbap\xe9\x1f_\
-}-\x08\xc3\xe9S\']\xd7-\xe4s\xaf\xbf\xf1\xe6\xfc\xfcB\x18F\xa5\xd2\xa8\xea\
-\x93\x99_X\xac\xf8\xbe\xfa\xcd\xd4\xe4\xd1w\xcf\x9d\x9f_X<s\xe6\xf4/\x7f\xf5\
-\xeb\xb1\xd2h\xc5\xaf>9}\xf2\xcc3\xa7\xc7J\xa5\x7f|\xf5\xb5\xe9\xe9\x93\xaaU\
-855\xf9\xcb_\xfd\xb3\x14\xcag\xcf\x9c\xfe\xbb\xbf\xff\x07q\xd4\xb3gN\x8f\x95\
-J\xf9|\xfe\xe7\xaf\xfc\xf4\xf57~;;;\'|\x11\xf3\x0b\x8b\xa2\xa9]Qk\x90\xdd\
-\x17\xdem5\xde\xa6W\xb3\x07\x19m\xe1\xa3W\x0b\xff\xe8\x87?x\xfd\x8d7\xdf=w>\
-\x9f\xcf\x8f\x95F\xfb\x1c+\x07!\x0c\xc3\xcff\xae\x88u\x8e?\xdf<\x07w\x9eH(\
-\xf2\xdf\xfd\xfd?\x88\xf2/\xbf\xf4\xe30\x8c^\x7f\xe3M\xd1M\x11\xae\xda9t\x89\
-\xda\x12C-\x7f \xf1\xb3\x8a\xa9\xe2\x97\xbf\xfa\xf5\xeclk\xc1@\x8c\xb6\xf8\
-\xdf\xad\x17w\x00(\x8cN\xac\xd10\xae-Y\x06\'@X\x1c\xb2\x18\x00D\xe8\x0c\x00\
-\x01\xf8\x1a\xab\xaa\x88\xe8X\x8d\xc2\xd0X\x10\x01"\xda$v\xb1)\x96Q9\xd8\xbb\
-\xd2l\x07\x00\xf2\x10W\xa25\x0f\x19\xdf\xf7\xa5we\xc0C\x84\x85\x9b8d~a\xd1u\
-\x1dyK\x8b2\xea7\x0fPs\xd7\x13\r\xd8\x1e\x00X.\x97\xc30\x1a\xa4\rb\x10\xc6J\
-\xa3\x83\xf8\xd0\xbfq\xde=w\xbeP\xc8\xf7z\x08P{\'z4x;\x13\xfd\xea\xfc\xad\
-\x07\x1ca\x95\xc4\x0f\xdd\xf5\x9bGK\x14\xd4\x83\xc6:\x8b\x1a\x80\x94\xc5!"\
-\x03N\x018g\x8c\x00\x07\x10q\xefHHWc\x9e\xb4\xfe\xe9&i\x88h:)\xc3\xcer\x8e\
-\x18W\x91E\x00\x84q\xf0FOd\x87\xf6=\xdc^="\xb4\xb8k4\xf7\xc7\xebo\xbc96V*\
-\x95J\xbe\xef\x7ft\xe1\xd2\x7f\xfa\xab\xbf|$\xf3\xca\xe3\x02""g,\xa6\x8d2k\
-\xdc\xaa\x86\xf98\x8a)\xe7\x06A\x038\x10$\xc0\tp\xd3@\x03\xa8a\x00G\x93\x10\
-\xf1\xde)kW\xd0zK\xc9 \xc2\x0b\x03\x00\x84#A\xc3\xf5\x86\x8fd\x8b{\x1fa\xe7\
-\x1e*Z\xdc5\x9a\xfbc\xb9\\\x9e\x99\xb9"\x96U\x9f=s\xfa\x91\xafF>&D\xfeM\x08\
-\x97\xc1\x1a[\xb9\x1b\x07\x14L\x83\xb0v@#\x01\xdc3d\x86\xe1\xdd\x8cMy\xe6h\
-\xdc\xac\xc4\xcd5\x1a\xd5\x11\xc1 \xe0\xb9\x04\x01\xb85D!E\x90\x11\x02\x1c\
-\x0c\']\xf0r#\x86i?\xd2>=\\\xb4\xb8k4\x9a\x1d\x80\xbf\xfc\x85Co\xbb\xe9\x92\
-\xdft\x10\xa0\x1e`\x10#!\x04\x11-\x83\xec\x1f6W\xd7\xcb\x9e\xd5\xe0h\xd0\xf6\
-\x16\x1f\x08\x90u!\x9d&\x80\x00V\x01\x8a\xdfz\xb4]\xd8b\xf4KL\x1a\x8df\x07\
-\x10Ea\x18s\x1e\xafy\x0e-d,\xdb\xb6,\x93\x98\x06\xa4\x1d\xa3\x901\xa3(\xe6,\
-\xa86QUv\x93@\xca!\xc0\x01\x10 \xae@\xe3\xfa\xa3\xed\xc2\x16\xa3\xa3e4\x1a\
-\xcd\x0e\x80\xd1\xb8\xd6\x00\xcb\x88\x11VjA\xb1\x90\xc9\xa7m\xe4\xc8-\x93\
-\xb0\xb8Y\xad\xdf\xe5\x9c!\x92\x8d\xdd\xb4\x11R.\x10CY_\xad/\x80=\x02\xf6\
-\xe3\xe2F\xd3n\x19\x8dF\xb3MA\x1ec\xdc@Z5h\xe5\xf6\xf2j\x10q\xcb\x00\xcf\x85\
-\x88"\x80E\x0c\x0b\x08AN9\xa3\x00\x88H8\x82\xe7\x12h\x9b\xedC\xd9\x8eM8L\x0f\
-\x86\x9e\x81\xdd\x98\xbd\xbd\x13m\xb9k4\x9am\x04\xf2\x18i\x83\x87\x15\x1e\
-\xf9\xad\x98E\x02\x88\xc0\x18\n\xa1nF`\x19\x04\x80\x02\xa7\x00@\x00,\x93\x00\
-\x90\x88n\xe4\xeeE\x84t\xc2l\x17\xb0\x06\xd4o@vrK\xbb\xf4\x88\xd0\xe2\xae\
-\xd1h\x1e)\xc8\x90S\xa4\r\x1eUxT\xc5\xb8\x86<n\xa9r[\x9a9\xdf\x88h\x14\x1b\
-\xa5\x9a\x84\xc8hw\xde*\x80\x86A\x08\x01\xce\xc12 \xe5\x90\xee\xef=5oAz\x1f\
-\x98\xbb0\x99L\x02-\xee\x1a\x8df\xcb\xe1\x11\xd0:\xc6>\r*\x9c6\x81\xc7j\xa6\
-\xc6\x04\x04\x80#pT\xfe_\x01\x01\x00A$\x120\xdao0eR\xdd\xccvyD\xf5\x0b(<\r\
-\xbb4\x8d\xbb\xe4\xff\x07_\xffI\xbb\x17\xf3\xcc{\x00\x00\x00\x00IEND\xaeB`\
-\x82' 
+\xcf\x1fD\x15x\xc3E\x07\x1b@\t|\x0f\xb5\xfd\x86{-i\x11\xff\x7f{\xef\xfe\x1c\
+\xd7q\xe5y\x9e\xbc\xcf\xaa[O<\n|\x01|\x01$EB\x92IJ\xb6,y\xec\xb6\xbb\xed\x96\
+z\xed\x91\xdc=\xd33\xbf\xee\xc6\xc6\xc6DL\xcc\x9f\xb3\xb1\x11\x1b\x13\x131\
+\xd1\xd1\x11\x1d\xdd\xda\xf1\xb6\xbd\xedi\xc9\xf2\x8c\xd5\x94%\x8az\x90\xd4\
+\x03\xa4(\x81 )\x00\x04\x89\x02@\xd4\xad\xd7}d\xe6\xd9\x1f\xb2*\x91\xb8\xf5`\
+\x91\x12A\x00\xccO(\x18\xa5B\xde\xbc\x99Y\xf7~\xf3\xdc\x93\xe7\x9e\x0c\xee\
+\xb1`\x15\xe0>S\x85\xe9\xe6R\xb9};O\xdc\xe5\x95$\x1d\xdf\xe2\x06\x13\x97\xa3\
+\xb8\xb7\x97\x86\xff\xea\xf6\xd0/S\xf1\xb2\x17\xcd\xa7\xe3\xdb\x16o\x10@4\
+\xac\xd8,\x06\xa9CQj\x82\xbb#\xa6iZ\xed\xdc\xd9\x94\xd2\xea\xd0\x0f\xd6\xf3\
+\xdf\x97UY\x96\xe5\xba\xae\xc8\x0b(^F\xefl\x86h\x00l\xf6K\x98\xa6)"(87\x99QX\
+)\xfeY\xb9\xf0\xa7\x06k\xb8l-\x1d\xddN\xd1e\x8b\xd7\x018 p#\x15\xd9\xa3Mw\
+\x82\xba{\xc1\x1d\x12i\x08\r\xa3{\x94w\xa7/H\xde<\xcc0\xd6s\xdf\xbb\x97\xfd\
+\xae\xc9j)\xba\x9c\x8en\xbb\xb4lb\xfb\x15\x12\xc3A;\xcf\xbd\x03F\xf1Xz\xf8p:\
+?\xea\xba.Q0\x0c\xa3\x99\x7f\xf6\x86{BL\x18\xa2NKI\x8b\x98\xcdfS\xa9\x94\xe8\
+\xe5\x80?\x8a\x08#\x11\xf7\xf9\xc2\x9e\xff-\x11\x9f#N*~\xbbt:\x9d\xc9d\xbc\
+\x94\xd7g)\xb5\xf3\\\x9d\xaf8\x89S\x87a\xd8\n\xa1a\x8cs\xd37OW\xbc\xef\x18\
+\x18[t=\x1d/\xa5\xe3%\x9b\xad\x1b\xc0\x00\x81\x1bNd\x8d\x04\xeex\xe4\xee\xe7\
+\xce\xb0\\\x0c\x94\xeb(\x96\x92M\xbek3\xe4\x04\x9fh\x83\xb8\x00\xa4\xf5\x9a\
+\xcbes\xb9,\xb4\xde\xbaD\xb1Q\x910\x02l\xdb\x96Oc\xa2\xefb\xfc\xe3\x98"J\x7f\
+H\xcb\xa5C\x8c~\x8b\xcc\xa6i\x8e\x8d\x95\x86\x87\x87\x08!\x8e\xe3\xa4\xd3i\
+\x91:I\xe4\xb3\x1cdT;\xc7\xd30\x8cB>\xff\xd4\xf14"\xda\xb6-~)q/\xf4\x9a\xf3\
+\xd4IW\xad\xa7\xfd\x8bp1\x1a\x9c\xf38\xa6A\x10\x84Q$f2B\x88m\xdb\x9e\x97\xf6\
+\xd2i\xb1\xe3\xab\xd9\xde^Q\xa4\x90\xda\xb7w\xcf\xde=c\xa6i\xba\xae+\x92Dy\
+\x9e\xf7@y\x95\x1f\x0b\x9b\x92\r\xb0M\x0f\xea*\x03\xef\xb5\x84\xb1\x92\xc5\
+\x84\xf5No\x91\x199\n\x8f$\xd5\xf8\x96\xa0>\x17\xabWR[X9"\xc6\xe6\xfeJj\x7f\
+\x05\x00\x94\xdb@\x1c\x98jg#\x00\x00\xde\xde\xfeQ\x8a\xa6P7\xe9;\xee\xb4(USE\
+\xaa\xa4\x8c\xd2\x8b\xa2H\xdc\xde\x1bNa+\x1fa.J\x1f\xaa(5$\x8e\x12+\x84\xad]\
+\x8d\xba\xed\xa7\xaa\xfa\x82\xb0\xbd[\xbci\x9a\xf2\xceA\xab\x188\x85\xc0;&\
+\x07\x04\x00\xe4\x14\xe8y\x1eq2\tY\x97\x866"\x1a\x86!\xc5]\xbe\x94d\xf5\x0e<\
+\xef\x84(\xcb\xaab\x12\x12\xd5&\xfc\x03\xeaD\xe5*{\xcc>\x90\x06\x89J\xe4\xc5\
+\xa0\n\xab\xb8\x06\xe4e\xc0\xb9\xc5\xacT-\xb5\xb7\x06g\x12\x83/\x8er\xda\xae\
+$\xa9\xec\xf7m\x12i\xef\xe6,\xda\x90\x18O\xf5\x02P\x95\xce\xb2Hga\xb9\xfe,\
+\x8c\x8c\xd6\xa3@\xc7+p^:\xdd9\xa5%~D\xf1\x8dz\xdd\x0e>\x9ej\xb5\xf26\xb1\
+\xac\x96>$\xee\x85\xaeGA\xfb\x12U\xebQ\x7f\x149\xe1\x89qK\xa7S\xea\xb1\x9dc"\
+\xbe\xa7\x94\x8a\x8b\x9cs.\xeaWo\xc9\x07\x9d\xb7\xb6\x98\xd8\xbf%\xbc\xa0bG\
+\x8e\xaee\x06\xdfk\x89\xd6\xef\xf0\xa8\n\x00\x04 \xee\xbd0\xebdJvz\x08v\xe2\
+\x82*t\\I\xf2*\xb7,K\xbd\x8c\x84T\xe1\xe65\xc9\x84l\x01\x80\xba\xf1\xbcz\xf1\
+\t\xf5\x91\x16\\\xaf6\xc8\x06H\x99\x16\x1e\t9a\xa8n\xd9\x84\x0fA\xdeE2\xfc#\
+\xb1W}\xe2\x8c\xf2Y\x01\x14CI\xceg\x9d.\x02h\x8b\xbb\x8c\xe80\x95\xf7?\xe5\
+\xa9E:VJ\xa9l\x9e\xda\xfdA\xbc%\xb0YkTe\x97N\xb3DG\xd4\xf9\xac\xbf\x1b\xa4\
+\xd7\x05 \x0b\xabb$\x8451\xf8\xf2J\x00\xd84\xad\xaa\xcb\x18\xb21\xf6\xe6\xed\
+\xad\xfb\xe8;(\xe9!\xe5x&.\x00\xf5\xd7W\x7f\xb8\xc4\xfa\x04!D\xf8\x1f\xc4\
+\x16\x05\x8c\xf1\x84\x9f=\xd7\xe1\x0cI\x8c\xa40t\x00@MV\xfc\x10Sf\xe2\xc2\
+\x10\x13\x7f\xe2^\xb8\xaf\xbew\x9d\xedT\xfb)\xe1OK\x8c\x9e\x1c\x131\xe1\xa9\
+\xe2.\xa3q\x1e\xb4k[\x0f\x8f\xeb\xb4\xd1zo\xb9\xcf\x8e\x1c\xde\xf0\x91\xfe\
+\x01\x0b\x02\xe4t#\xfc\x11zN\x15`\x98\xdeH\xcb\x87\xbc#\xc5]\xa0\xca\x9fTm)\
+\xee\xea\xaa\x8e*\xa6\x96\x9a\x9f\xb6\xed\xd4S\xb5@\x1a\xc5\xd6\xe6\xb8\xdd>\
+\xfa\x9e\xb8\x94E\xbaJ\xf5i\xa0Sv\xd5\xf6\x98\xca\xbb\xfb\xf2\x89\xa1\x8f\
+\xf1\x0e\x9b\x95]qD$\x17\x18\xc9fSZ\xf8\x01\x8cv\xb0\xbc\xf8^\x04~\x88\x1a\
+\xa0=\x17\xaa\x0e\x96\xc1o$\xb2\xf9\xc1\x9c\x10bY\x96\x18\x01P\xb4UvD\x18\
+\xcb\xf2N~P\x19\x12\x93\x07l\x9e\\\x133kbrM\x0c\xbej\'v\x1d\xfc\xfeMR\xe7\
+\x98\xae\x17@\xe7\xaf\xaf6U\xb4V\x1a\x19\xe2\x10\xc30\xd6+\x15\xc6\xd4\x17v\
+\x80\x10\x92\xcfw\xf1tKe\x07\x00\xd34\x85\xb8K}\xecz\xd1\xdewT\x13\x17\x86\
+\xd0by1\xf4\x7f  \x9b-\x1eu\x84\x133n\xe7b\x89\x1c\x109\xb9\x8a\t\x8f1&\x86T\
+L~\xfdM\xaemE\xec\xdf\x94\x1b\xe9\xf5\xda\x91\xc3\xf6\x86\x85\x95}_hu\x1eY\
+\x00\xf73\xdb\xd3\x85\t\x19O\xb9S\xc5=qoK\xa1\x94\xc13\x89%{\xd5L6\xdb1\x94\
+\xa4\xed\xd4K\x98x\xa0<\xe9\xf7\x7f\x1a\x15T\xab\xb5\xeb\xd7\xe78\xe2w\x9e}Z\
+\xdc\x0f2l@\xd4\xac>\x9e\xc3\xe6\x0b:\xd1$\xd5\xe0J\x9c1a\x19\xc9\x9b\xb0SD:\
+u$a\x10\x11\xc5\xc46\x14o\x8f<\x97\xda\xfd\xfe7\x928\xd7\xfc\xc2\xe2\xca\xca\
+\x8a\xeb\xba\'\x9f:!g \xc30d\xc0b\xe2(y3w\x9d\xc9z\x9d\xc5\xf7\xfd\xebs7\x00\
+\xe0\xec\x99\xd3\xb0yr\x95\x15\x8a\xc1\x17cr\xe5\xea5D\xdc\xb7oO\xd6\xf3T\
+\xdbY^\x0c\xb2\r\xea\x0f-\x7f\xeb\x01\xe73Tv\x7fV\xe5L\xfd\xe9\x13c\xab\xea\
+\xbb\xd9\xf6\rJ\xcb\xbd\xfd\xd6\xfe\x86\xa9\x97J\xb9)7\x95\x18\xa2\xc4\x8f(\
+\xe4\x18\xdaW\xc8 \xd7m\xaf\xbe\xa8\x17\x86\xfa0\xd7\xd5g\xd8Y\t\x00H\x03_Z\
+\t\xaa\xb2\'n\x87\xc4o\xa1>\x1f \xa2\x10wy/\xcb9\xe0\x81\x1e\xf5\xb6\x1e\x1e\
+\xf9\xad\x95O\x80\x98bw\xb3}\xf0\xbd\x96h@\xeb\x8b\x00@\xfazx\x0c+\x95.n\xa4\
+\x93\xdc\xa9\xe2\x0e\xdd\x0cgi\'v\xf5Q\xa8\xb7\xb4\xbc\x87\x85U\x82\xca\xce\
+\xcb\xb2\xbcj\xdfu^\xd0\xaa\x9b\xc5\xf7\xfd\xf3\x17>\x00\x803\xa7\x9f\x95\
+\xb7\xf7\xf2r\xd9\xb2\xad\x8c\xe7\xa9\x9a\x8b\xed\xe8F\xb5=j\x93\xfa+K\xa7\
+\x9c\x89\xeb^vyum\xcdq\x9cl&#\xbb\xa0\n\x9f\xa58\xd0\xd5fX\xca"X\xd7\xeew\
+\x9a\xb1\xb2\xb0\xf8P^.\x9f\xbf\xf0\xc1\xc4\xf8\x01!\xee\xd0\x96\x83N\xa7D\
+\xa2\xfe\xfe\xde\x83\xe5\xe5\xb2\xeb:\xf9|^\xd4P\xa9\xf8\xef\x9d\xbf\x00\x00\
+gN\x7fG\x96\x97\x1dQ\x07G\x9c\xfa\xf3\x99+\x0006V\xcaf\xb3\x9d\x83O\xda\x9e\
+\x19\xf5\'V\x07g@\xc4U\x04\x00_\xcd^?p`\xbf\x97N\x8b\xebP\xbd\x08\xbbz!\xd4\
+\x8bP\xc4\xb6#\xe2\xca\xcaj\xa3\x95-k\xa3\r{\xf7\xec\x91\x1e\xea\xc4\x95 fk\
+\xd1_)\xee\xea\x0f7xG\xe4\xc8\xc0\xe6\x0b\x03\x94\x89\xa4\xcf\x8f\xd5\xd96y\
+\x94\x14hi\x82t\xc4\xc9l\xba\x11d\xcb\xc5\xed)\xeePT\xfc\xab\x0f:om=Q\xe5\
+\xa6\x88\x8c\xea\xcc\xc4+qs{\x07\xdek\xe9\x06\xf2V\x0cU\xdcgav\xf8\x0816$}\
+\x07\x8b\xbb@Z\n\x86a\x08\xdf\xb1\xbcz:\xd5J\xde\xd5\xf2\xca\x90\xe2\x9eP7\
+\xf5\x90N\x03\x017#\xafTyY\x7fq\xed\xcbs\xef\xbc\xeb\xba\xce+/\xffltdDV\xae\
+\xeaKg{\xfa\xc8z\xa2\xbf\xaa@\xc8^_\xbb\xf6\xd5{\xef_p\x1c\xe7\xcf\x7f\xfa\
+\xa7\xa3\xa3#\t!\x93\xb7\xa5\xac_~\xdf\xd9\xf7\xfe\xdd\xef\x1c\x04\xde~}0qc\
+\xabS`\xff\xfa\xe5\xcf!+\xbfr\xe5\x8b\xb7\xcf\xbd\xe3\xba\xee\xbf\xfe\xf9_\
+\x8c\x8e\x8e$\x14\x017\xbf\x04 \x7fJ1\x1aj\x9b\x85\xf3G\xedBb\xf0\xd5\xeb\
+\xe1\x81\xf4"\xd1\xdas\x7f\xfc\xe3\xfe}\xfb\xfe\xf4\'\x7f"\xa2\xf4\x12\x17I\
+\x9f\x81\x95\xd6\xbd\xefWo\xde\x9aO$Q!\xc4\x98\x98\x18\xb7m\xbb\xb3m\xb2\xe5\
+\x9d\xa6C\xff\xdf\xae\x0f\xea\x05\xd39\'\r^a\xe2B\x95\xb7d\xc2\x8a\x82\x8e\t\
+/q\n!\xe5\xdfV\xef\xb6\x06\x16\xac\xf1\xb0\x95\x84\xbc\x8fs<]<4Hm<\xaa\xb0f\
+\x19\x00\x88H\xda\xde\xc3\xc3c\xa5\x8bnvl\xd37\x0f\xd0\xe4\xed\x8a\xfc\x81\
+\xc5E /\x05Pn\xbf\xc4e\xd1yl/\x01J|\x90\xa0\xf2\x02\x0bk\x8b\x8ex\xb2F\xc4\
+\x85\xc5\xdb\x00\x10\x86\xd1\x9d;\xcb\xfb\xf6\xee\xed\xac\xbfk{\x1e\xf4\xb6\
+\x11KL\xe2K\xc30\x96\xee\xde\x05\x80(\x8a\x96\xcb+\xfb\xf7\xef\x935\xab\x1f\
+\x12g!\x9b%u\xc0\xee\xcb)M\xfdW\xfe!\xd1\xa9\xfbV\xae6C\x95\xc2\xf9\x85\x05\
+\x00\x08\xc3\xf0\xeb\xf9\x85b\xb1\x90\x18g\xa3\xdb\x82s\xc2\x1e\x14\x1f,\xcb\
+t]7Q\xb2\xff\xb0\x0c\x82\xda\xda\xa5;w.|\xf8\x11\xe7\xb8\xb0x\xfb\x9f\xdf|\
+\xeb_\xbd\xf4\xe2\xfe\xfd\xfb\xd4\xee\'\xb4L=\x9d\x1c\xc0f\xb39s\xf5\x8bz\
+\xa3\xb1\xf9\t\x9e\x14\x8b\xf9R{\xaa\xee\xec\x05t\xfc\x88]/\xef\x01\xe9Ug\
+\xe7\x9f\x06\xaf\x8d\xb4\x9fl\xd4\xc9\xbe\xeb\x80$\xee\x059\xaf|[\xbd\xdb2b\
+\xff\xa6\xf8\xd0\xc7lO\x17\xc6\x07\xdbk\t\xe3\xf6C\x00\xde\xc7\xc3\x93\\\x98\
+\xdd\r\xe2.P\xef\x99^\x82\x02\xbd/\x88\x07\xbaP\xb0\xbd\x89k\xcb\x87\xd8~_\\\
+\xec\xb5\r\x00\xcf>=\xbdR^\xc9\xe7s\xd3\xa7\x9e\xea\xff\xfc8\xf8ye\xa7f\xae\
+\\\xbd~}\xaeT*}\xff\x85\x8d\xb4\xef\x9c\xf3g\x9f\x99^YY\x15\'\xb5\xda\x91d}\
+\xce\xf2\x10\xb7\xab\xda\x18l\x87\xeem\x84\xa3(\xb1I\xfdO\xdd\xa7Z)v\xcf<=\
+\xbd\\^\xc9\xe5\xb2\xc7\x8eM\x8aH\x1e9\xce\xbd~_D\x94K\x1d\x1b\r\x00"\x9dQ\t\
+\x1eZ\x1d\xe4\xf4\xc69\xafVk\xe7\xdey\xb7\xd9l\x8aG\x97ry\xe5\xcd\xb7~\xff\
+\x9dg\x9f9u\xf2d:\x9d\xeas\nY\tc\xec\xee\xdd\xe5\xcb\x9f|Z.\'v6G\xd3\xb4\xa6\
+&\'=\xcf\xebe\xa5~\x93\x1f\xb1\x17\xdfz\x9d\x0fwo\xee\x08\x1d\xef\x845\x96E\
+\xc0\xa2\xd0\xe2\xee\x85\x08qs\xfb\x06\xab\xad\xcc\xc3u\x00 \x04\xfa$\x1bps{\
+;\xd3I\xee\x1eqWy\xa4\x97\x82zO\x8a5"\xcaZ\xa2\xc3\x18\x13\xa7\x1e\x1a*\xfe\
+\xf5\xbf\xfdK\x19Z\xf0\r\xdb\xa3Z\x7f\x88\x18\x06\xe1\xfc\xc2"nn\t"\x0e\x0f\
+\r\xfd\xbbo\xef\xa4\xf7ELl\xedU2\xd6j\xe8\xc3\xbe\xef.\xad`\xd9\x9db\xb1\xf0\
+o\xfe\xf2Uu\x16\x91\x92\xad>\xa4\'j\x90\xbf\x8bl\x89|s\xfd\xdb\x1a\x10y"Ji\
+\xb5Z{\xf7\xbd\xf7\xef\xdd[\x17g\x10\x05\x1a\x8d\xe6\xf9\xf7?\xb8q\xe3\xd6\
+\xe1\xc6 3\x08\x00\x00 \x00IDAT\xc3\x87&\x8f\x1e\x19\x1a*\xf6\xa9di\xe9\xce\
+\xdc\xcd\x9bs\xd7\xe7\x1a\xcd\xa0#\xcd\x00\xd9\xbbw\xec\xd0\xc1\x89\xae\xfe\
+\x8a\x1d\xca.\xe8B?\x90G2\xb5o\xbfP\xf4\xd1\x81\xccvd\xf2!\x00\xb1\xe7TAL\
+\xdb\x1b:\xdc\xf9\xfd\xee\x14\xf7- \xa1\xef\xe2K)\xee\x82o\xf1nDe\x15\x81\
+\x8b=Fp\xc35\xa4\xc6\xbal\x8d\x04\x08m\x12\xddOX\xee\x9d\xb2\xfb@\xd5\xca\
+\xee\xc8\xc9L~\xa3\x8a{\x9f\xc3\x93\xe2\xfe\x90\xd3M\xcf\x16Je\x8f\xe3\xf8\
+\x8bk\xd7\x16\x16\x17U\x07\x82\xe4\xce\xdd\xbbw\xee\xde\xfd\xec\xf3\x99=c\
+\xa5\xfd\xfb\xf7\r\r\x15E\x8c\x87\x18\xb7 \x08WWW\x17\x17o\xaf\xae\xdd\x0b\
+\x02)\xeb\x9bj\xc8\xe7sO\x9f:\xb9\xfd#C4\x92M\xa9}{\xee\xc8axCG\x06\xaa\xadv\
+\x1bi\x13\x85\xd9\xde?/\x8d\xd5%\xf5\xe6n\x10wy\x1b\x97\xcb+\xf3\x0b\x0b\x0b\
+\x0b\x8b\x00\x90\xcf\xe7\xc7\xc6J\xd3\xa7N\xaae.^\xba\x0c\x00S\x93G]\xd7\x9d\
+\xb9r\xb5\\^\t\xc3\xd0u\xddRi\xf4\xe9\xe9S\xd23{\xdf\xc2O\x9d8.V2[2\xd4N\xa5\
+$|\xee\x00P\xab\xd5\xe6\x17\x16\r\xc3\x10A{\xaa\x18\x95\xcb+\xb3\xd7\xe7\xca\
+\xe5\xb2\xdaHY \x0c\xc3\xd9\xebs\xe5\xf2\x8a\xef\xfb\x000>~`j\xf2h.\x97\x13\
+\x9au\xf9\x93O\x11\xe1\xf6\xd2\x12\x00\xf8\xbe\x7f\xf1\xd2\'\x00h\xdb\xf6\
+\xe4\xd1#\x9c\xf3>\'\xf5}\x7f\xf6\xfa\\\xd7\x91\xe9\xd5_Yxjjrb<\x99\xf8B\xf5\
+Kl\xbc\xa3\x84\xc89\xbf\xbd\xb447wS\xb4\xbfT*ML\x8c\xcb\xc3\xe5\x89\x08!S\
+\x93G\xf3\xf9\xbc\xfa\xfd\xcc\x95\xab\xcdf0<T\x1c\x1b+\x89\xee\xdc\xfaz\x1e\
+\x11\x9f:q\\\x06Z\xa8gW\xfb8\xbf\xb08??\xbf\xbc\\\xe6\x88C\xc5\xc2\xa1\x83\
+\x13\x9eL\xad\xbe\xb9p\x18\x86\xf2\xd7\x14C!\xba,\x0b\xcc/,\x96\xcbe\xd7u\
+\xa7O\x9d\x9c_X\\XX,\x97\xcb/\xbe\xf8\xc2X\xa9$\xeb\x13\xe2\x1eEQ!\x9f\xcff3\
+\xca\x1e\x1d\xad"R\xa6\xeb\xf5\xda\xdc\x8d\xfa\x8d\x9b\xb7\x0c\xc3P\xc5\x191\
+\xb1k\nI\x98\xed\xe9t\xea\x99\xe9S\xb9\\\xcez\x90\x97\x845\x8f\x11\xe44\xae\
+\xcd\x8b\xcf}R\xfb\xba\xb9\xbd\xa6\xe3u\xfb\xcb\xe6\xdaX\x14W\xe7\xc55\xc1y\
+\xcf\xcd\x9bL\xc7\xeb\x95\x97fg\x8b\xbb\xbc\xc9\x83 \xf8\x97s\x7f\x14A\xd0m\
+\x16g\xae\\\xfd|\xe6\xca\xab\xbf\xf8_\\\xd7\x15\xc5\xce\xbf\xff\x01\x00p\xce\
+/\x7f\xf2Y\x18n\xec\x113{}\xee\xe2\xa5O^\xfb\xd7?/\x95Fe\x9d\xfd\x0b\xbf\xfc\
+\xe7\x7f\x96\xcf\xe5Z\x02\x87\xc98\xeeZ\xbd\xf1\xf1\xc5\xcb\x00p\xf6\xcci\
+\xc5\x84\xc4\x7f9\xf7\xc7+W\xbf\xe8l\xe4\x8f\x7f\xf4\xc3\xd1\xd1\x91\xd9\xeb\
+s\xe7\xdeyW=\xd7\xfc\xc2\xe2\xf9\xf7?\xf8\xd1\x0f\x7fplj\x921\xf6\xe1G\x17\
+\xe5\x9f\xfcj\xf5\x83\x0f?\x02\x80\xfd\xfb\xf6M\x1e=\xd2\xe7\xa4\x17/}r\xe1\
+\x83\x0f;O\xfa\xca\x9f\xffTD\x19\xf6\xed\xef\xe2\xcc\x95\xab?\xfe\x93\x1f>=}\
+\xaa\xeb\xe0\xb7%\xbe\xa5\x9e\xe7\xdey\xf7\xda\x97_\xa9\xed\xbfx\xe9\xf2\xf4\
+\xa9\x93?\xfe\x93\x1f\xca&\x89\x13\x8d\x8e\x8e\x8a\xb7.\xa5\xf2^\xbf>\xb7\
+\xb0x\xfb\xf9\xe7\xce\x8e\x8d\x95\x00\xa0Z\xab_\xba\xfc)\x00\x9c8~,\x11b\xd1\
+z\x82\xe1\\\xfc\xf4\xef_\xf8P\x1d\xd5\xa5\xa5;W\xae^;\xf3\x9dg\xd5v\x8a\xc23\
+W\xae^\xf8\xe0\xa3D\xef>\xbex\xe9\x95?\xff\xd9\xe8\xe8\x88(\xbc\xbc\xbc\xfc\
+\xfe\x85\x0f\xc7\x0f\xec_^.\xcbj\xcf\x04\xa1|"\x91\x8f,Q\x14e\xb3\x99g\x9f\
+\x9e\xfe\xf2\xab\xd9\xd5\xb55\x191\x94p\xad\xc8C\xd4\xc1S\xd4\\j\xfa\xc6Q\
+\xd9lf\xfa\xd4S\xa3\xa3#\xea\x1b=\xa0\xd9\xde\xd0\xda"\xd2\x00@\xe4k|0\x17J\
+\'q\xf5\x16\xaaI\xdb{\x86?\x1e\xed\x95\x97f\x07\x8b\xbb*.\x94\xb2J\xc5\xcf\
+\xe5\xb2\xcf>\xf3\xf4S\'\x8e\x13B\x96\x96\xee|\xf0\xe1\xc7\xe5\xf2\x8a\x10G\
+\xd5qq\xe1\x83\x8f\\\xc7y\xfe\xb9\xb3\xa3#\xc3\x84\x90Z\xad\xfe\xc9\xa7\x9f\
+\xf9\xd5\xea\xaf\xff\xbf\xdf\xfe\xdb\xbf\xfa\xa5Hlt\xdf\xc2o\xbc\xf9\xfb\xbf\
+x\xf9g\xae\xeb$\\\x04\xad\xb5\xa3n\x8d\xbc\xf0AK\x83\x9e\x7f\xeelit\x04\x08Y\
+]]\xfb\xe4\xd3\xcf\x00\x81qF)m6\x9ba\x18\x9e8~\xec\xd9g\x9e\xce\xe7sqL\xbf\
+\xb8\xf6\xe5\x07\x1f~t\xee\x9dwGGGR\xae\xfb\xec3\xd3\x88\xb8\xbc\\\xbe\xbb\\\
+\xcef\xb3\'\x8e\x1f#\x848\x8eM:\x1e\xda\xe5y/]\xfe\xe4\x83\x0f?\x06\x80\x13\
+\xc7\x8f\x1d9|\x88\x18F\xadV\xfb\xe4\xd3\xcf\xca\xe5\x15\xd1_\xc7qT\x13\xf2\
+\xc2\x07\x1f\x8d\x8e\x8c\xbc\xf4\xe2\x0b)\xd7\x8d\xa2\xe8\xd3\xcff\xca++o\
+\xff\xcb;\x13\xe3\x07\n\x85.\x1b\x80\xa9.\x94\xa5;w\x97\xee\xdc\xdd\xbfo\xef\
+\x89\x13\xc7S\xae\x0b\x84\xdc\xbcy\xeb\xea\x17\xd7f\xae\\u\x1c\xe7\x85\xef=\
+\xbfi\xac\x14\x83\x1aU\xe5\x86d\xbc\xcdFq\xd8\x10w\xf1\x90\xc49\x7f\xef\xfc\
+\x85/\xae}\t\x00\xc7\x8fOM\x1c8\xc0\x18\xf3\xab\xd5k\xd7\xbe\xba\xf4\xc9\xa7\
+\xad\x16\xb6=Z\x0b\x8b\x8b\xe7\xdeyW\x8c\xff\xd1#\x87\xf2\xf9|\xad^\xff\xe4\
+\x93\xcf\xae~q\xed\x8d\xdf\xbd\xf5o\xfe\xf251\x14\x8cq\x00XX\xbc\r\x8b\xb7GG\
+G&\x8f\x1e\x89\xa2x\xfc\xc0~u`\xa5\xf3\'\x8ec\xd34\x8e\x1e9\x94\xcdxw\x97W\
+\xda9\xdc\xef\x0b\xc1\x8d\r\xbc\x89j\xb1\x13B\xf6\x8c\x95\xa6&\x8f\x14\n\x05\
+\x99\x9ba\xd78\xdcw1\xc8cZk\xed\xd0\xdb\'GX\xaa0nX\xf7\xcfw\xc6\xe3\x9a\xcc8\
+\xc6\xfa\xbe\xe0\xeadF{U\xb2S\xc5]\x15M\xc6\x98i\x1a?\xfb\xe9O\x10Q\xbc\xe8L\
+\x08\xd9\xb3g\xec\xf9\xe7\xcf\xfe\xf3\x1b\xbf\xbb>7\xf7\xe2\xf7\xbf\xa7\xea\
+\xb5\xe38\x7f\xf6\xa7?\x16)\xf4D\xc9C\x87\x0f\xbe\xf1\xe6[++\xab\x17/]\xfe\
+\xc1K\xdfW=\x00\x9d\x85\x0f\x1e\x9ax\xf3\xcd\xdf\xaf\xac\xae\xce\\\xb9z\xe6\
+\xf4\xb3\xf7m\xa7P\x96\xf5\xf5\xca\'\x9f~\x0e\x00/|\xef\xf9cS\x93\xe2A{\xdf\
+\xde=\xfb\xf6\xed\xc9\xe7\xf2\x8ec3\xc6\xa6&\x8f\x96J\xa3\xe2\x15$J\xa9a\x90\
+\xe9SO-,.\xde\xbe\xbd47w\xe3\xd8\xd4\xe4\xd1#\x87E(\xc8\xdd\xe5r6\xe3\x9d:yB\
+\xbe\xdf\xa8\x9eTFd\xae\xafW\x84\xb2\x8b\x93\x12B\x0c\xc3\xd8\xbbg\xec\xf0\
+\xa1C\xff\xfc\xe6\xefVVV\xcf\xbf\xffA\xa2\xbf\xd9l\xf6O\x7f\xf2#\xc7qD\xe1\
+\xe1\xe1\xe1\xd7\xff\xdb\xff\x0b\x00\xf3\xf3\x8b\xf9|\xbe\xab\xbep\xce\xb1\
+\xfd\xec2y\xf4\xc8\xf7_\xf8\xae\x18+\xd1\xc1\x91\x91\xe1?\xbe{\xfe\xd2\xe5ON\
+\x1c\x9fR\xf7\xbc\xe7\xca/\xa8\x1a\xe6\x9bt\xfe~\xa3Z\xa9\xf8B\xd9_\xf8\xde\
+\xf3\x93G\x8f\x08\xc1-\x16\x0bc\xa5\xd1\xf7/|\xb4^\xa9\x00\x00\xb6\x9d\xf5\
+\xef\xfc\xf1=\x00\xf8\xfe\x0b\xdf\x15C\x11\xc7q:\x95z\xe9\xc5\x17\xc20\x9c\
+\xbbq\xf3\xf3\x99\xab\xcf<}\x8as\xce\xda\xcb\xe3OO\x9f\xfa\xdew\x9f\x93q\xd6\
+28/1\'\x89\x96\x0c\x0f\x0fy^\xfa\xde\xba\xbf^\xa94\x9b]%^t\xaa\x1d\x88\xa2f\
+\x17h\x9b\xf0\x85Ba\xac4z`\xff\xbet:\x9dHN\xd9\x7f@4\x8f\x9d\xd8\xffZ\x18\
+\xda\xe2\r\xd2\xae?\x18\xb1\xdcta\xa0\xd4\xbeq\xe5\x86\\,\xea\x1d\xfeh\xf4\
+\x7f\xc1ug;\xf2\xa4\xb83\xc6\x84u\x13\xc7\xb1\xc8R\x14E\xd1Xi\x14\x00\xc20\
+\x12O\xd0\xf2a\xfc\xe8\x91C\xd9l&R0\x08yf\xfa\x14\x00\\\xfd\xe2\x9a8\xb6\x7f\
+\xe1\xe9SO\x01\xc0\xec\xf5\xb9\xce\xf6\xb4n\xf8\xf6\x0f#\x9a\x17\xc7\xf1\xec\
+\xf5\xeb\x0004T<tp"nC)\x1d*\x16-\xcb\x94\xc9\xcb\xd2\xa9\x94\xf8\x938W\x1c\
+\xc7{\xf7\x8c\x01\x00c\xad\x15\xbc8\x8e\x85i\x89\xed\xb0\xbf\x8d\x15\xd5\xcd\
+\'\xa5\x94^\x9f\x9b\x03\x80\xe1\xa1\xa1\xc3\x87\x0en\xea\x82A\x9eyz\x1a\x00\
+\xe6\xe6n\xc8s\x89c\x8f\x1c>\x84\x88\xb2d*\xe5\xee\xdf\xb7\x0f\x00\x82\xb0\
+\x8b`\xa9\x02\'\xbe9u\xea)9\xfe\x82\xc9\xa3GFF\x86\x01\xe0\xab\xd99\xd1\xfe\
+\xd6\xb1\xca2\xa9\xe8\xbb\x14\xcd\xf6JF\xcf\x94\xa6\xbc\x9d\x0eH\x8e\xea\xe1\
+C\x07\xd5|\x90\x86aL\x1e=\xac\x16\xbe\xbb\xbc\\\xad\xd6\xb2\xd9\x8c()\x1a)\
+\xca\x1f:8\x01\x00\x8b\x8b\x8b\xe2{J\x19\x008\x8e\xf3\xec3\xd32\x97\x9c\xa8J\
+\r\xe9\x93oTJ\xcb\xdau\xdd\xbd{JG\x0f\x1f\x9c\x18?04Tp\x93\ti\x93\xeb\xa5\
+\x12\xd7u\x87\x87\x87\x8e\x1c>t\xe2\xd8\xe4\x81\xfd\xfbR\xa9T*\x95J\xe4\xcb\
+\xd4\xfa\xbe\x9dA\x1a\xd0\x86\x92\xda\xb7G\xb1\xcc\xe67H{\xc1\x825\xd6\xde?\
+\xb9O\xa4|*\xbf\xaf\xff\x0b\xae;\xd5r\x87\x8e\x07\xe4f3\xb8\xf5\xf5\xd7w\x97\
+\xcb\xeb\xeb\xeb\xf5\xfa\xa6\xedo\xa2(RcZFFFd@:\xb4\xdf\xad8\xd0~\xee\x9e_X\
+\x18\x19\x1e\xeeS\x98s._Q\xb9{wY(\x97l\x12\xdf\xfcv\xb5x\xad\x9cRz{\xe9\x0e\
+\x00\x1c\xd8\xbf_D\xd4$\x8cS\xd9\x91\x95\x95\xd5\xf9\x85\x85{\xf7*\xcb\xe5\
+\xf2\xe6\x02\\\xc9\xcb\xd1\nM\x11\x16\xba\xf0N\xa8\x8f&b\x85\x931V^Y\x05\x80\
+\xfd\xfb\xf7\xd1v\x848i\xbf.(\xfc\x0ca\x14\xdd\xbd\xbb\x9c\xcbee\x7f\xf3\xf9\
+\x9c\xec/i\xbd\xfa\xc8\xe5hwJ\x8c\xec2\x00\x94FGL\xc3\x90\xcf\x01\xa4\x9d\
+\xf8\xe9\xe0\xf8\xf8\xea\xea\xda\xe2\xed\xdb\xc7\x8fM\xca\x1a\xb8\xb2\x1e\
+\xdb\xd2P\x8e\x00\xc0X;\x1f\xcbf\x0f\xbb\x8ah-\xe7\\\x8ej+\x10^\xc9\xb1\xb3w\
+o+\x8d*\xe7<\x8ec\xb1>\x1c\x85\xd1\xff|\xfb\x9c\xac\x87\x00!\x04\xa28\x06\
+\x80\xdbKw\xd4\xeb\xa4X,\x08\x8b\x01\x15\xe4\xb0$\x12\xc8\x88\x0bC\xf4\xc2u\
+\xdd\x92\xeb\x8e\x0c\x17\x19\xe7A\x10\xd4\xeb\xcd \x0c)\xa5\x9c3\xe4\xadH\'\
+\x83\x18\xc4 \x96i\xa6Rn6\x93\xc9d<\x99\xfdX\xec" \xc4\xfd!\xf2\xc3h\x1e\x0b\
+\xb1\x7f\x138\x07\x02\xbc\xf7\x1b\xa4\xa6\x93\x19,\xb5/\xc6\x95\x96\xd5\xd8?\
+R>U\xb8\xcf\xb6|;X\xdcA\xb1\x1c\xc30\xfc\xdd\xef\xffg\xbd^\xb7m\xbbX,\x1c>tP\
+\xa8\xdeg\x9f_\x81v\xbe=uE\xcbho\xd3!\x14a\xb3\x1c\xb7\x0c\xc9>\x85\x15\x85\
+\xe2\xaa\x17\x81\xb7S\xcb\xa2"\xee\xf2(\xf1\r\xe9\xf6\x9a\xb5\xa8\xfc\xca\
+\xd5k\x9f~\xf69\x00\x88.d2\x1e!d\xb9\xbc\xb2\xbc\\\x16G\x93V\xea\x8fM\xef\
+\x98(A;\xc9\x93\xca\xe7\x0f\xb9\x12\xd8\xd9\xdff\xd0L\xa7S\x9b\x97\xfb6<\x0f\
+\x94\xd2v \xcc&\x81K\x94T\xc7J\x0e\x97\x10Y\xc30\x18g\x00\x80\x1c\xd5i2a\xb9\
+\xf3\x8d\xd8G\x99l\xabg\xe0\xa3\xecB\xfbO\x9b^\x91\x97\xad\x95\xa3\x11\xb57\
+\x85\x88\xe2xy\xb9\x0c=h\'\x8bf\xed*A\xadV*;i\xe7]\x1116DI\xac/sc\x89\t\xc0\
+\xb1\xedB;"\xa8+\xf2X5\xa1|*\x95\x92\xdev-\xeb\xdb\x1f\x16\xdc\xa3\xcd2\x00\
+\x00\xf6{\x834S:\x0e\x03\xfc\x9a\xb1\x7f\x8b\xc7\xad\xe0+J\xb1\xe7n\xda\xf9\
+\x03\xf7\x8d\x94\xdf\xd9\xe2\x0e\xed;\xff\xc3\x8f.\xd6\xeb\xf5\xb1\xb1\xd2w\
+\x9f;c\xb6\xf7O0\x0cC\x88;\xdb\x9c!\xb2\xd9l\x8a\xb4\x85mC\x189\xe72\x9a-\
+\x9dJ\xa9Z\xdcY\x981\xd6h\x88\xd4N\xe0\xa57\x854Iq\xef4\xa23\x9e\x07\x00\x8d\
+FC<hwF.\xaf\xae\xad\te\xff\xdew\x9f\xdb\xb7w\x8f\x90\'!\x94\xcb\xcbeB6t\xd30\
+\x88\xec\xa00\xae\xf9\xe6HA)\xee\xc5Bay\xb9\xdch4d\x02U\xf9`Q\xab\x89\xd4\
+\x83\x90\xcf\xe5\xd4\xfe\x1a\x84\xa8\x99\xd48\xe7b\x19S\x95K\x15U\xf8\x1a\
+\xcd\xa6\xa5d\xc9\x1750\xc6\xaa\xd5\x1a\x00x^Z8LZ?\\{\xbe\x90\x12/\\C\x9c\
+\xb7\xa6\x1f\xec\xd8\xabS\x9e_\xfej\xedQm\xcaL\xc8\xb2\xa9\xf27\x12n\x19qU\
+\x94FG^z\xf1\x05y\xe5H\xa5\x96\xc3\xb8\xb1zA@\xa6ZK\xfcRD\xd9\'D\xb5\xe2\x13\
+\xe9\x97\xf9\xa6H\xc7.\x83&\x1d;B\xd9\xd5\x84\xfe\x83$\x94\xd7l\x0f0\xf6\xe7\
+\xc4\xfa)\xa5}\xde \xdd\xd7\xf9\x06i\'\x9c6h\xb5\x15L\xc99P\xd6\xbd6\xc3Jy\
+\xc3\x87\xef[\xdbN\xf5\xb9K]\x10\xff~=\xbf\x00\x00\'\x8eMY\xca\xb6\xc5\xabkk\
+jay\xec\xe2\xed%K\xd9\x01G\x14\x98_X\x04\x80L\xc6K\xa7S\xea\xfad\xd7\xc2\x0b\
+\x8b\x8b\x00\x90\xf1\xbct:\xa5\xd6\xbc\xa1V\xb8!\xee\xe2V/\x16\x0b\x00\xb0\
+\xb0x\x9bs.w\\2\x95\xec\xd8w\xee,\x03\xc0\xd8X\xe9\xc0\xfe}\xd2L\xe6\x9c\xcb\
+\xb7\x1f\xd5\xb4|\x00@\xda\xf9\xb96\xce\xab\x9cT\x8c\xcc\x90rR\x99\\W\xedo\
+\xb1X\x10R.\xbb\xa0\xa6\xe1\x15%\xa1e\xb9\xf7\xfc-\x08!by\xb0^o\xac\xac\xae&\
+\xa2\xf7\xc20\x14\x99v\xf2\xf9\x9c:\x03U\xfd\x9a\xda\xcdZ\xad~o]\xf4\xb4\xfd\
+\xb3\xb6\xbb\xd39\xaf\xc8\x87\x95ba\xa3\x83\x89}6\x96\xee\xb4\xf6I`\x9c3\xc6\
+\x86\x87\x86\x00\xa0\xbc\xb2\xda>\xcbF\xcd\xd5Z-\x0cCuJ\x86\xb6jK\x97\xfa\
+\xa6\xce*\xe6\xb60\xb4\xe5\x16t\x12\xcf\xf3\xd2\xe9\xb4X\x17\x95\xbb;\xc9m\
+\xfc\xe4Q\x9e\xe7%\x0e\x91\t\xee\xb5\xb2\xef\x08hc\x99Gu }\xc3\x1f\r+]<8Hmq\
+\xe5\x86\x8cM\x88Y\xcf{\xce\x1b><\x88\xef~G\x8a;\xb6\xc3\x9c\xa5\xc5\xea86\
+\x00\xc8\xfb\x16\x11\xc30\x14\xd1)\xd0\xe1/\xbe{w\xf9\xc3\x8f.\n\xbd\x13Z\
+\xf9\xe5W\xb33W\xae\x02\xc0\xe1C\x07\xf9\xe6u\xbc\xce\xc2_\xcd^\x9f\xb9\xf2\
+\x05\x00\x1c:4\xd1i\x9a\xb5\xf5*\xf1\xbfx\xe4\xf0\xa1L&\x13E\xd1\xff\xf8\xc3\
+\xbf\x88\xa7\x01!\x7f\x1f}|\xe9\xda\x97_I+\xaf^\xaf\x0b\x03V\x1cu\xeb\xeb\
+\xf9\xc5\xdbK\x00 ,w\xa15\x84\x18\x00\xb0vo]x9\x14}\xdf8\xa9\xa8\xf0\xc0\x81\
+\xfdCC\xc5(\x8a\xfe\xe7\xdb\xe7\x82 \x94\'\xbd\xf6\xe5\xec\xe73W\x01\xe0\xf8\
+\xb1)q\x88\x1c\x1cssfyQ\x9f\xecJ\xe7o\xd1\xb2\xdb\xdb;|\xfe\xf1\xdd\xf7\xcb\
+\xe5\x15\xa9\xb0\xf5z\xe3\xdd\xf7.\xc4q\xecy\xde\xa1\x83\x13\xa2S{\xc6J\x00\
+\xf0\xd9\xe73Q\x14\x89o\xc2(\xba\xf0\xe1G\xed\xc6\xcbq\xebw\x01\xb4F\xf5\xc8\
+!\xc7\xb1\x13\x1d4\x0ccn\xee\xa6\x0c\xb7\x17\x17I*\xe5\x8aw\xa9\xce\xbf\xff\
+\xa1\x9a\xc2\xe5\xde\xfa\xfa\x87\x1f]\xfc\xe3{\xef\xaf\xafW\xa4p\x03\x00\x01\
+\x920\x9f\xe5\x10\xa9\xfan\xb57\x9b\x95J\x9d\xcdf\xb3\xd9\xac\xf8 \xffU\xc9n\
+F\xca\xba\xea\x8d\xd1\xca\xbe3@.wGb\xac\xb7\x0be\xb0\x1ca,\\g\xcd\xd6\x95\
+\xd9g\x1du\xf0m\xf9v\xaa[FZ|\xc2c{\xfc\xd8\xb1\xcfg\xae|>s\xb5V\xab{^:\x8c\
+\xa2\xa5\xa5;\xb6m\x17\x0b\x85\xf5J%\xa1\x13\xc3\xc3C\xd7\xbe\xfc\xea\xe6\
+\xcd[\xc3#\xc3\xc8y\xb5V\xaf\xd7\xeb\x00p\xf8\xd0\xc1cS\x93r1\xb0\x7f\xe1C\
+\x07\'\xa6&\x8fB7\x8fpg;\xc5\xe7\x1f\xbc\xf8\xc2\xdb\xe7\xfe\xb8\xba\xba\xf6\
+\xf7\xaf\xffJ\xac\xdf\xfa~\xb5Z\xad\x02@\xcau\x0f\x1f>\xf8\xc5\x17\xd7\xea\
+\xf5\xc6\xdb\xe7\xfe(tp\xbd\xe2/-\xdd9r\xf8\xd0\x8d\x9b\xb7\xa4\xbe\x18\x86\
+\xb1g\xac4s\xe5j\x14E\xff\xf4\xdf\xdf\xc8f\xb3\xd5j\xf5/^\xfeY\xaf\x06\xbc\
+\xf4\xe2\x0b\xffr\xee\xdd\xd5\xd5\xb5\xff\xe7W\xff\xb8o\xdf^\xb5\x0b\xd3\xa7\
+N\xee\xdf\xb7\x176\\\xf9\x00\xcaN\xad\x98|\xef\xe6\xfed\xb3\x99(\x8a\xdf\xf8\
+\xdd\xefGF\x86\x1d\xdb\xe6\x88w\xef.\x03\x80m\xdb\xcf\x9fm\xbdW\x85\x88G\x8e\
+\x1c\xbe\xbb\\\xae\xd6j\xaf\xff\xb7\x7f\x1c\x19\x1e\xe2\x88kk\xf7\x00 \xe3y\
+\xf5F\xa3\xff):G\xf5\xa5\xef\xbf\xf0\xde\xfb\x1f\xc8\x0e\n\x8fS\xbd\xde(\xe4\
+\xf3\x15\xdf\x97\xe5\x01\xe0\xc4\xf1)\xbfZ\xadT\xfc\xf7\xde\xff\xa0X,\x88\r\
+\x0b\x85\xd0\x17\x8b\x85l6#\xbd\xea\x00@\x0c\xa2F\xc2t\xb6A|)&K9\xef\xca%\
+\xd6\x8d\x85\x90\x8eiJ\xf5\xc9\xa8\xf16]\'\x12\xcdv\x86\xd6\x97\x906\x81\x00\
+"\xf6z\x83\x94X\xae\xba\x81Fo0\xae\xb4\xde\xc1\xec\xb7\x8e\n\x90\x19\x99\x1a\
+\xc4w\x0f;Z\xdcU\xcb\xfd\xd8\xd4QD\xfc\xf2\xab\xd9\x9b\xb7Z\x13\xe9\xc1\x83\
+\xe3\'O\x1c\xbfx\xf9S\xf5\x10\xf1\xe1;\xcf>\xe3\xfb\xfeg\x9f_YZ\xba#\xbe\xb1\
+m\xfb\xf8\xb1\xc9\xe3\xc7\xa6Py\x0b\xb1O\xe1cSG\xa7&\x8f\x0er\x07\xaae\n\x85\
+\xfcO~\xfc\xc3O?\x9bYZ\xba\xb3\xb8x[|\x99\xcdfO>u|dd\x98s\xfe\x83\x97\xbe?s\
+\xe5\xeary\xe5\xca\xd5k\x00\xe0y\xde\xf3\xcf\x9dI\xa5\xdc\x1b7oI\x13Y\xd4\
+\xf3\xd4\x89\xe3_\\\xfb\xb2Z\xad\twv\xd7\x93\x8a.d<\xef\x95?\xff\xb3O?\x9b\
+\xf9\xf2\xabY\xd9\x85L\xc6\x9b>u\xf2@;\xe6G\xd5/\xa2\xe8\x0b\xe9\x88\xea\xe9\
+\x8a\xd4\xafL&\xf3\xa3\x7fu\xfa\xf2\'\x9f\xden\x9f\x08\x00J\xa5\xd1S\'O\x14\
+\x0b\x05in\xef\xdf\xb7\xf7\xf9\xb3g>\xfd|&\x8a"\xe1<)\x95FO>u\xfc\xea\x17_\
+\x0e(\xeej\x07K\xa5Q9\xaa\xb2\x83\x07\x0f\x8e\x9f86\xf5\xd6\xffx\x1b\xda\x0f\
+=\x00\xe08\xce\xf7\xbf\xf7\xfc\xf5\xb9\x9b\xb7\xbe\x9e__o\xedU\xee\xd8\xf6\
+\xf8\xf8\x81\xa7\x9f>\xe5\xd8v\xfb\xf9i\x93\x85\xde\xe7W&\xed\x98Hh\xaf$\xa3\
+\xbaV\xa1\x908J\x9d\xaa\x8d\x07L\xe8\xaf\xd9& \xa7q\xdb?Ni\xef7H\x87\x0e\r\
+\xe2B\xa1\xf5\xbb<j\xd9"}\x1e\x02\xdc\xdc^+\xd5o\x89^e\xa0\xbbw\xbb\x81\x88"\
+\xe8[\x04\xa4\xabY\xa2\xca\xe5\x15D\xcc\xe7s\xc2.\x93\xef\xf8\x08#\xf4\xb7\
+\xff\xfc;\x00\xf8\xd1\x0f\x7f0~`?!\xe4\xf6\xed%\xc6\xb9e\x9a\x85B^j\x99X\x7f\
+\xe3\x9c\xff\xf77\xde\xeaU8\xd1\x18av\xc9\xff\x15\xb6\xbf\xba\x9b\x0c\xdf\
+\xbc\xef6c\xec\xde\xbduD\xccd\xbct:-wR\x16\xe5\xeb\x8dF\xa3\xde0-\xb3\x90\
+\xcf\x1b\xed-\xd3H{\xcb1\x11\xc6#\x96\x16\xd6\xd7+@\xc8P\xb1@\x08\x11\x1d\
+\x94\xbe\x149\xf9\xa9&\xe1\xd2\xd2\x1d\xc6\xb9\x97N{^Z\xf5\xe7\x88S\xab\xfb\
+\x86\x8b\xf0\xc1(\x8a8\xe7\xa6i\xca\xcd\xa3-e\xdbb1\xf8a\x18\n\xef\x8a\xdc\
+\x80\xd44\xcd0\x0cWVV\x19c\xc5bA.u\xcaq\x90+\x07\xcb\xe52r\x14\x81\x7f\xe2{\
+\xb5UDY\xb7\xec5\xaa\xb2\x83\xa6i\xc6q\xbc\xb2\xb2J\x19\xcde\xb3Rp\x15\x9f\
+\xd5FD\x8da\x18\xe5\x95UB\xc0 \xc6\xde\xbd{\x12\x15\x9a\xed\xfdBe\x98\xf9\
+\x80\xd7\xa4\xfaoB\xd6\xd5\xbbL*\xb8:yhY\xdfq\xc4\xfe\xcd\xd8\xff\x1a\x08 \
+\xc7 \xea\x99\xf8\xa50\xfe\xbc\xf0\xa3\xf6\x019\r\xee~\x84,\x04\x00D\x08b\
+\xec:U\x10\xc3*\x8c??X\x16x\x80\x9dk\xb9K\xd4\xfb\x81\x10R*\x8dB\xfb^J\xdc9j\
+\x04\x8bx\x8buddX\xdc\x81\xd2/!oHy7v\x16\x06e\x0b\xe0\xc4\xe1\xd0V%\xd8,C\
+\xa0\x84\xcd\x08\x15\x13\x99L:\x9f\xdc\t!\x19\xcf\x13A b\xda\x90\xc6\x9d<\
+\x97\xf8`Y\xd6\x9e=cR\xf4\xa5\x1a\xaa\x16\xb7()\x971GGG\xa4\xd3I\x15>i\x81\
+\x1a\x9b\xcd\xf6\x84D&\x86:a\xe0\xcb\x19TL\x15\xf2\\\xb2N\xd9\x12\xa9\xf2\
+\xa3##\xd2}!\xcf([%\x97\x8e\xfb\x8c\xaa\xec\xa0(022,V\xb0\xe5h$\xe22e\xfb\
+\xc7J\xa3\xea\xd8\xaa\xd7\x8c\xfa\xe5\x83^\x87jU\xb0Y\xd3\xbb\x96\x07-\xe8;\
+\x13\xe41\xad\xb5\x1e\xbec\x96\xcc\xd4,\xf1\x86\x8e\xdcW\xd9A$\xa5a\xad\xa8e\
+\xca\xba+;\x00\xa4\x8b\x13\x83+;\xec\\q\'\xcaV\x8af{\xdfw\xe8\xb8g\xa4\x14\
+\xaa\xc7\n\xdb\xdch\xef\x0bC\xdaq\x11\xd0V\xdb\xfb\x16N\x98\x84\tq\x17%Ue\
+\x91\xb3\x8bT\xa2M\x9e\x90\xf6\x8bE\t\r\x15z\xd7^Dm\xe9\xb5\xd4P!XR\x7f\x13m\
+K\x9cT\x1d\x1fB\x88\x0c\xc6\x00\xc5(V\xc5N\x0e/Q6\x1a\xed\xfc\td<\x89Z\x95\
+\x94]\xf57\x92M\x923\x8d\xb4m\xe5\x9c\x94pp\xdfwT\xd53\xca1\xc1v\x80\xa3\xda\
+69\xaf\xc8\xb9M\xf6QvM\xfc(bH\x1fN\xdf\xd5\x91I|\xd0\xec2b\xffk\xe4\x14\x88\
+\x88\xdc\xed\xb9\xef]\x9f\xc4/\x12d!\xad.\x88\xcf\x1c{\x87?\xda\xe9Ta\x10\
+\xdf\xbd\xd2\x80\x07*\xbd} J8\x1a\xb4%L\xfeI \xfeW\xdaq\xbd\x0cO\xb9\xa2%\
+\xad\xf2\xfb\x16V\xdd\x05\xaa-\x0f\xdddH\xfeU\r\x04T\xeb\x94\xee\xdaD\x17\
+\xa0\xbd\xbc)C0\xa1\xc3\xd0\x96\xbai(;>\xcbo\xa0#\xc6_:1TEV]:R\xd4\xe4l\xd7u\
+]\x91(\xe1"r\x90\xe5\x9f\x12\x05d\xfb\xc5\x97\xea8\xc8\xf2\x89\xb3\x0f8e\xaa\
+\xb3\x05\xb4\x05Z\x9e]F\x07\x89^\xa8C\xa16O}h\x93~\x9e\xc4U\xa4\xd1l\x80\\lj\
+\n\x001\xedc\xb6\x1f\x1e\xe8\xad\xa5\xcaMD\xda\xae\xad\xc7;P\x00\x99\xde\xd9\
+\x1f{\xb1\xe3\xc5\x1d\xda\xaf\xb9w\x15wi$\xaa\x86\xa7e\x9a\xb6mK\x11\x97J\'m\
+j)\xf4\xbd\nw\x8a\x8e\xaa\x02R\x98\xc4!\t\x19\x92\x93\x81:\x8bt\xfa\rd\r\xaa\
+\x9c\x89\xa3\x84\x16\x93\x0e\x07\x02\xd9\xbc.\'\xf2\x1c$,VqHb~\x92\xe6\xbf*j\
+\xb2\x9e\xce\x13A\xdblGD\xc7q\x84\xc1\x9bxB\x92?Pb\xbe\x91n\x93\xc4\xef\xa5\
+\x1a\xd1j%\x89QU\x15_\x8eF\xe7\xd0\x11\xe5\x81\x83l~\xcf\xabs(d\x19YI\x9f \
+\x19\x8d\x866\x96\x91E\xc2l\xe7\xbc\xbb\xd9\xeedJv\xba\xcb\x0e\\\txT\xa5\r\
+\xf9BF\xcf\xf0G\xdb\x1bv\xb2\xa5\x07m\xe7\x8e\x14wi\xbaB\xfbN\x96\xcf\xf8\
+\xa0\x88\xbb\xf4\x81\x88$!\xf2pS\xd1\xeb\x84\xa6\xa8\xce\xdf>\x85Uq\xef\xd3H\
+\xa9\x0eRGT\xcb\x91lv1w\xedBb\x92\xc0\xcd\x8b\xb7j\r\x89\xa3\xe4\x04#\x95W=i\
+\xaf\xf9I\x15\xf4\xce\xcf\xea7\xe2_\xb1\xbe\xaa\x9eB\x95\xec\xce\xe1\x92\xe3\
+\xd0\xb5\xa7\xea)\xb0#\xcf\xc1 \xa3\xda9t\xaaUn\xb4\xa3Y\xd4\xc3\x13S\xac\
+\xfc\x93z"\x8df\x03d2H\xa6o\xbe\xc6#\x83T\x16U\xe6d\xa6\x8b\xdeid\x06\xad-\
+\xc1\x8e\x14wP\xf4]\x8a\x0bl^G%\x84\x88/\xc5\x83\xb6(66V"\x84x\x9e\xa7n]F\
+\xda.\xef\x84\xb2\xf7),o\xfb\xfe\xe2.?c{!4\xa1\xb3\tYI<\x07$\xca\xa8\xf5\xa8\
+g!\x9b\xc5]=\xbb\xec\x9a@m\x7f\xa2y\x89\xa3p\xb3#\xab\xab\xd2\x19\xedu\x02\
+\xdc\x8c<D\x9eH \xebT\xdb\x03\x1d\x13\xc9 \xbf{\xe7\xa8\x822\x1f\x90\xcd@oq\
+\xef\xfa+$\xc6\xbf\x7f{4O\x1aq\xed\xb6\x88mg\x0cy\xaf|\x8d\x83\xed\xb5\xc4\
+\x9a+b\xf3k\x00\xa0}\xde\x81\xca\xef\xb3\xdc\xdcC4uG\x86BJTk1qgB{-TMik*y<T\
+\x8d#\x84\x88\xe0\xbf\x01\x0b?tk\x13\xba,\xff\x95\xdf\xab\xe2\xde\xf5\\\xb2\
+\xb3\xea\x9f:\x8b\x11%`\xa6sp\x1e\xae\x0b\x89\xa3\xd4\xbe$\xda\x9f8Qb\x02\
+\xeb,\xf6\xd0Mz\xa0\n\x1ft(\xb4\xb2k\x12 \x8f\x83\xbb\x1f#\x8b\x00 \x88xW\
+\xed$\xa6]\x1c\xff\xee\xfdw\xe4@\xde\xbc\xfb1\xd2\x06\xf4\x0f\x7f4\x9d\xe2\
+\xc4\xf3\x86y\xff\xfd=:\xd9\xa9\x96\xbb`\x90\xfb\x93\xb4\x1d\x11\x88(\xd7\
+\x12\xa5\x7f9Qr\xc0\xc2\xdf\xa4\xb5\x0f\xfa\xa7oX\xf2\xd1)T\xff\xd9e\xcb\xda\
+\xf3(\x86N\xa3\xe9\n\xad-\no;e=\xad\xe2\xf4`{-\xd1\xdam\xa1\xec\xd07\xfc\xd1\
+\x1b:\xf8p\xca\x0e;]\xdc\xef\x8b\x14k\xf9\nL\x9f\xb5\xb2\x07*\xac\xd1h\x9e(\
+\x90\x85\xad\x8d\xf4\x10h\x0f\xff\xb8a\xa5R\x03\xed\xb5\x84q{O\xbe>\xe1\x8f\
+\xa6\x93q\xf3\xfb\x1f\xae\xb5\xb0\xeb\xc5]\xae\xb9\xa9\xff\xdbk\xb9\xec\x81\
+\nk4\x9a\'\x8a\xb8\xfa5r&\x82dz\x9a\xed\x83%\x1b`\xcdUd\xad\xad\xcd\xfa\x85?\
+\x8eL\x0e\xf2\x0eT/v\xb3\xb8\x0bQ6\xdb\xa9\x9d\xb0\x1dJ\xd8\xcbl\x1f\xbc\xb0\
+F\xa3y\xa2\xe0\xb4!c\x16{\xc5\xb6\x9bn6\x95\xdb{\xff\xba\x90\xcb\x1ca\x8c\
+\xf7\\\x95\xb53\xa3\xb67\xdc\xed/\x83\xb2\x9b\xc5\x1d\x94\x98\r5\x1a\xaf\x97\
+%\xfe@\x855\x1a\xcd\x93C\xec\xdf\x12\x1b\xe91\xd63\xb6}\xc0\xb7\x96h\xed6\
+\x97\xdev\xda\xa3\x1012}7\xbf\x1e\x84\xdd,\xeer\x05\xaf3\xc4\xf0\x1b\x16\xd6\
+h4O\x0e<\xf2[\x99\xd6{\xef\xc8a{\xc3\x03%\x1b\xe0q\\\xbd%>\x8b\xf0\xc7\xae\
+\xa4\x0b\xe3\x83\x04S\xf6g7\x8b;t\x0b\xd9\x86\xdez\xfd@\x855\x1a\xcd\x13B\
+\xec\x7f-R\xfaR\x86\xbd^"\xf5\x86\x0e\x0fV\xd5M\xe4\x14\x00\x10\x81\xf6\xd8.\
+\xc1\xb0\xdc\xf4\xd0@;7\xf5g\x97\x8b;<\xa0:k)\xd7h4*,\xb8\xc7\x825\x10rL\xbb\
+;d\xdc\xdc\x9eA\xd2\xac\xf3\xb8N\xeb\xad-\x07\xfa\xbc\xb5\x94\x1e\x1ah\x17\
+\xbd\xfb\xb2#\xb7\xd9\xd3h4\x9a\xad\xa1\xe5E!\xad=4:!\x86\x99\x1e\xd0l\xaf\
+\xdc\x00\x14oS\xf7\xdbEo\xa0U\xd9\x01\xd0\xe2\xae\xd1h4\xdda\xcd2\x0f}\xb9\
+\xffu\x0f\xb3}\x9fi\xa7\xef_Up\x8f\x05\xab\xad\xcf\xbd\x82)\t\xc9\x8cL\x0e\
+\xb2*;\x08Z\xdc5\x1a\x8d\xa6+(\xf7\xbf\xee\xb5\xf8ILg0\xff8\xc6~{\x8b\xd4\
+\xde\xdev\'S\xb2R\x85\x87ii7\xb4\xb8k4\x1aM\x17h\xfd\x0e\x8f\xebb\xff\xeb^f{\
+\xba8>Hz\x00Z\xbf\xc3\xa3j\xebsO\xf7\x8e\x95\x19\x99\xfc&\rN\xa0\xc5]\xa3\
+\xd1h\x92 \x8f\xe3j\xcbl\x8f)t\xf5\xa2\x98N&\x95\xbf\x7f\xb2\x01dQ\xec\xdf\
+\x14\x9f\xfb$\x1bH\x0f\x1d2,\xf7!\x9b\xdb\r-\xee\x1a\x8dF\x93$\xf6o"\r\xc5:j\
+\xf7\x8d\xf4\x08\xc9\x96N\x0c\xb2;RT\xb9.\x12IB;\xd9@\'v\xba\x98.N|\xa3\x16w\
+\xa0\xc5]\xa3\xd1h6\xc1i\x83\xd6\xef\x02\xb4\xdeZ\xea*\xc7\x8e7:P\xf8cXa\x8d\
+e\x00 \x00\x94\xf5\x08\x92!\xc4\xfbV\x1d2\x02-\xee\x1a\x8dF\xb3\x89\xd8\xbf%\
+b\x16i\xaf\xdc/\x83\xee\x8e\x84Q;\x8d\x0c\x87\x9e\xf16\xa9\xdcCn\xc7\xd1\x1f\
+-\xee\x1a\x8dF\xb3\xc1F\xb2\x01\xe8\x99\xda7\x95\xdf?\xd0^K\x8d2\x8f* \xccv\
+\n\xbd\xd6Q\x07\x0c\x93\x7fP\xb4\xb8k4\x1a\xcd\x06\xb1\x7fK\xac\x9f\xf6\xcc\
+\xfdBHj\x904\xeb\xc8\xa2v\xf8#\xef\x1d&\x9f\x1e:4\xc8\xe6\x1e\x0f\x81\x16w\
+\x8dF\xa3i\xc1\xc2u\x16\xdc\x03\x00\xec\xedE\xb1\xd3C\x83\x98\xedqm\x11i;i;\
+\x83\xae\xd3\x84\xe9x\x83m\xee\xf10hq\xd7h4\x9a\x162\xd3z\x1f\xb3}\x10o;\xb2\
+\x90V\xe7\x01\x80\x00p\x0e\xdd\xe3m\x00\xbc\xe1o\xb4\x1dG\x7f\xb4\xb8k4\x1a\
+\r\x00\x00m,\x8bW\x8d\xc4[K]\xcb\xb8\xd9=\x83,~\xc6\xfe\xadV\xf6G\x80\xb8W\
+\xbcMf\xd4\xc9\x8c|\x93\x06\xf7G\x8b\xbbF\xa3\xd1\x00\x00\n[\x1b\x00(\x83\
+\xae;V\x0f\x98#\x8c\xc75\xda\xb8\x03b\x1d\x95\xc37\x8b\xb7yxv\x7f\xca_\x8dF\
+\xa3\xb9/\xadd\x03\x00\xbcw\xb2\x017\xbf\xdf\xb4S\xfd*\xa1\x8cR\xc6\xfd\x9b\
+\x049\x00\xc1>\xf16\x85\x03\xa6\x93\xf9\x16\xda\xdd\x1b-\xee\x1a\x8d\xe6\x89\
+\x07\x99L6@Y\xf7-R\x89\xe9x\xc5\x9e9\xc2\x18\xa5a\x10\x85\x94\x90x\xc5\x89V\
+\x11\x08\x01\x88\x19tO\xdaN\xc8 y\x0b\xbe!\xda-\xa3\xd1h\x9et\xe2\xda"\xd2\
+\x10\x008\xf6H6\x00\x90.N\x10\xd3\xeez8g|\xbd\\\x0e\xc2\xa6\x97F\x0f\xee\x02\
+r\xd27\xfc\xd1\xc9\x94\xfa?\x01|+hq\xd7h4O4\xc8)\xad-\x8a\xcf1\xed\x1e\xb3h\
+\xd8\xa9>\xb1\xed\x8dZm\xad\xbc\xcc\x03j\xc6\xf7h\xe0\x031@\x84?\xf6\xd8\xdc\
+\xc3\xfb\xc6\x9b_\x0f\x82v\xcbh4\x9a\'\x9a\xb8:\x8f,\x06\x00\xc6\x91\xf1\xee\
+\xb6vfx\xb2O\x8e0\x04,\x0c\x8d"\r\xc2\xf5yB\x0c\x02\xc0z\x87?\xa6\n\x13[`\
+\xb6\x83\xb6\xdc5\x1a\xcd\x93\x0c\xb2\x90\xd6\x97\xa0\x1d\xb3\xd8\xb5\x8c\
+\x95\xca;\xd9R\x9fJ,\xd3J\xe5G\\\xabF0\x14\xeb\xa8\xbd\xc2\x1f\r;\xf5\xadg\
+\x7f\xec\xd9\xaa\xad9\x8dF\xa3\xd1lCb\xff\x16p\xda\xda"\xb5\xc7\xbe\xa6\xfdc\
+\x16\x9b\xb5z\x18!\xd0\x8a\x83k\x00&\x01\xa0\x0c{m\x91\xda\xff\t\xe0\xdbE\
+\x8b\xbbF\xa3yB\xe1\xb4A\x1b\xcb \xb6\xbe\xeb\x11\xb3\xe8dJvz\xa8W\ra\x106\
+\x1a!\x82\xe1\xf2\x15\x821\x82\xc9\x11\xe2\x1e\xdbq\xd8\xe9\xa1\xfeO\x00\xdf\
+.Z\xdc5\x1a\xcd\x13J\\\xb9\t\xc8\x81\x00\xa3\xd8+f1=t\xa8\xfb\xc1\x08\xf5Z=\
+\x8eb\x0e\xa6c4\xcd\xa8\x8c`\x10\x021E\xc68\x11\xeb\xb2\x84ld\x17 \xc4\x1b\
+\xd9\x8auT\x89\x16w\x8dF\xf3$\xc2#\x9f\x05\xab\xd0\xda\xb1\xbaG\xb2\x81\xdc>\
+\xcb\xcdv\xf9\x03B\xbdV\x0f\x9a!1m\xd3`V\xb4H\x10\x91\x18\xe2\x05(;]\xb0\xd3\
+E\x03x\x1cV\xe3\xc0\x17:\xef\xe6\xf6>\x8a\xa4\xed}\xd0\xe2\xae\xd1h\x9eD"\
+\xff\xa6H2@{\x98\xed\xc4\xb0\xbc\x1ef{\xb5\xe2\x87alX6g\xd4!\x15\xc2*HL\x02P\
+\x0f\xb8\x9b?\x98\xdf3\t@\x00\xc34\xd0\xc6\xca\xd7\xcd\xca\x1db9\xde\xd0\xa3\
+M6\xd0\x89\x8e\x96\xd1h4O\x1c,\xb8\xc7\x83u \xfd\xcc\xf6Ta\xbc\xeb\x8e\xd5\
+\xcdz\xa3\x19\xc4\xa6e3Fm\xdb\xb0\xe3;@\x08\x01\x88(\xc6\xcc\x05\xf46f\n\x02\
+\xae7\x04h{C\x07\x1fQ\xd2\xf6>h\xcb]\xa3\xd1<i`\xec\xdf\x14\x9f(\xc5\xee9\
+\xc2,7]\x1c\xef<\xb2\xeeW\x83 \xb2,\x93R\xea\xb8i+\xba\x05\xbcI\xc0\x00\x80z\
+\x88\x86\xe1\xd4k\xcdt\x14G\x0c\xc2f\x0c\x18\x130Ccd$\xdf\xa5\xaaG\x8d\xb6\
+\xdc5\x1a\xcd\x93\x05m\x94yT\x05\x02\x9c\xf74\xdb\xbd\xe2Ab$m\xdfZ\xa5\xda\
+\x0cbbX\x9c\xa3m;\x16\xfa$^A @ \xa0\x10\xc6\x9c#\xb5\\\xbb\xdeh\x86A\xc0\x91\
+Sn0p\xd3#\x07\x82f\xf0\xe8\xbb\x95D[\xee\x1a\x8d\xe6\xc9B&\x1b\xa0\x0c\xbb\
+\xe6\x083\x9d\x8c\x9b\xdf\x97\xf8\xb2z\xaf\x12\xc6\xdc4\rJ\xb9\xe3\x18H\xc3\
+\xb8\xb9\xe8\x1a1\x80\x81\x08\xf5&\xb3,\xdb\xf6F\xc2\x88\x05\xf7\xd6\x00\x11\
+\x00\t\x10b\x18\x84\x185\x1a\x8f\x1f=BH\xd7\x08\xc9G\x85\x16w\x8dF\xf3\x04\
+\xc1\xa3*\x8fkp\x9f\r\x92\x8e$6H\xf2\xefU\xc2\x88\xdb\x96\xc1\x81\xd86\xf08\
+\n\x9b\xf7rF\x15\xc0\x00\x02\x8d\x80s0\x0b\xa3\xc7\x9auZ\xaf\xdd3\x0c\x13\
+\x08\x80\xf0\xe8\x03\xb4vd\x8db\xdb\xddR\xb7\xbb\x16w\x8dF\xf3\x04\x11W\xe7\
+\x11\x91\x00\xc4\xb4\xbb\xd9n\xa5\nNfT\xfe/r\xee\xaf\xfb1#\xa6\x811\x07\xc7\
+\x02\x03Y\xc4\xa9\r\x15b\x00\x00\xa1\x8c\x85\xd4\xcc\x8fLF1A\xd3v\xd2i\xce\
+\xb88\xb4]\x05\x98\x96i\xda[-\xb6Z\xdc5\x1a\xcd\x93\x02m\x96is\x85\x00\xd0^9\
+\xc2\x0c33zl\xa3|\x1c\xd7\xab\x8d\x98"\x00G\xc3t-B\x83&\x05\xb0\x1d\x84\xa8A\
+\x00\x109%y\xaf\xb87\x88\x80q\xe6\xa6\xbc\x94k\xd5\x1au\xc68p\xde\xaa\xd20FJ\
+%\xc3\xd8\xea\x05N-\xee\x1a\x8d\xe6\xc9\x00Y\\\xb9\t\x00\xfd6H\xca\xef\x97o-\
+EAX\xad\xd4\x88i\x13\x02\x86\xe9\xd8&\x0f\x1b5\xd3v\xf3\xc3\xc5\xb5\xf9\x8f3\
+\x16\x02"\xd8#\x86\xb1\xa7\x11r\xc6b\xd7\xcb;\x8e\x15\xd6\xd7\x81\xc5\xc4 N*\
+M\x00\x1c\xc7V\xd5#\x86\x00\x00\x1c[IDAT\xcd\xe6s\x8e\xb3\xd5q\x90\xa0\xc5]\
+\xa3\xd1<!\xc4\xf5;\x9c6\t@\xccz\xbc\xb5d\xb9r\xaf\xa5\xa0\xd1l6C$\x0495,\
+\xc7"4\xac7\xacT*?4\xd4\xac\xad\x19\xacA,\xc0\xd4\xbe\x88\x17\x83\x089\x8b\
+\xbd\xdc\x90\x014\xaa\xfb\xeb\xabw\x0c\xf4\x87F\xc6\x8a{\x0e\x13\xc3\xd8\xe2\
+ET\x15\x1d\n\xa9\xd1hv?\xc8i\\]\x00\x00\x04`=\xc3\x1f[{-\x05\x8df\xad\x16 \
+\x07D0L\xdb1Y\xb3^\xb3\xdcTax\x18\x00\xd6\xee|e[\xc8\xdd\xf1\x08F\x1b!c\x8cz\
+\xb9a\xe0\x11\x0b\x1ak\xe5\xafM\xa8f\xd2fa\xec\x88a\x9a\x8fQ\xd9A[\xee\x1a\
+\x8d\xe6I\x80\xd6\x16\x90\x85\xc2\xdb\xde\xd5l7\xed\x94\x9b\xdf\x0f\x00\xcdz\
+\xb3^k\x1a\xa6\xc1\x18\xb7,\xcb"\xb4Q\xad\xa7</?<\x0c\x00\xeb+\x0b\x166\x8d\
+\xf4\xc1\x08\x8a\xf5F\xd30\xedt:\x8b,\xc0\xa8\xb9\xba\xbah\x19\xcd\x8c\x8b\
+\xb9\x91q\xd3\xf6\xb6\xbc\x8bI\xb4\xb8k4\x9a]\x0e\xf28\xae\xdd\x86\xbe\xde\
+\xf6\xf4\xf0QB\x8cz\xb5\x1e\x04\xb1a\x10DbY\x96cA\xa3\xd6He2\xf9\xa1!\x00`4\
+\xf2Wn\xe6\xf2\xfb\x989\xd2\xacU,7g[\x16`D\x83\xba\xbf~\xdb"\xcd\\\x1a,\'\
+\x9d\xee\xbd\x8f\xf6V\xa2\xc5]\xa3\xd1\xecrb\xffk\xe4\x14\x00X\x0fo\xbb\x9d\
+\xca\xbb\xd9\xb1Z\xa5\x16\x84\x94\x00\xe3\xc42\x08\xd8&\xd6|\xdf\xcb\xe5r\
+\xc5\xa2(\xb6v\xf7\xa6\x97\xdbc\xba#\x8dF\xd5J\x0f\x01\xa7&\xc1ze\xad^]6I\
+\xb3\xe0\x19\x00\xe0\r\x1d\xe9|\xb5\xf5\xb1\xb0-\x1a\xa1\xd1h4\x8f\x08N\x9bq\
+}\t\x00\x01I\xf7d\x03\x04R\xc3S\xb5J\xad\xd1\x8cL\x03\xc1\xb0\x0c\xe4&\x81f\
+\xa3\x9e-\x163\xb9V\x9e\xde0\xa8\x1b\xc4\xb2\xd2CQ\xb3j:ydQ\xca\xb5\xabkw\
+\x9b\xf5\x15\xcbh\x16<\x13\x11\xacT\xc1\xcd\xed\xd9\xd2\xee\xf5F\x8b\xbbF\
+\xa3\xd9\xcd\xc4\xfe-\xe0\x0cH/o;\xb73\xfb\xc3\x00\xc3\x98\x99&\x01b\x19\x84\
+\x13\xc0(hd\x8b\xc3\xe9L\xcbu\x8e\x88A\xbda\xa7\x8aA\xc3\'N\x11\x90\xa6\x1c\
+\xab\xb2r;l\xae\xd9f\x98K\x9b\x88\x8fa;\x8e\xfehq\xd7h4\xbb\x16\x1e\xd7h\xa3\
+\x0c=w\xe4@ \x167\x8a\x9c\x1b\x84Pb\x98\x06p\xe0,\n\xea\x85\xd117\x9dj\x15\
+\xe2<\nB\xceH\x10\x05\xb6[\xe4\x8c\xba6\xb9W^\x8c\x82\xd5\x94C=\xd7\x10Y\x06\
+\xdc\xec\x98\x9d*le\xef\xfa\xa3\xc5]\xa3\xd1\xecZ\xa2\xca-\x00\x04BX\xd7\xd4\
+\xbe\xc8\xd1)q\x92A\xce\x0c\xc34\x0c\xe44\xa6Q0\xbcw\x9fe\xdb\xa2\x08\xe7\
+\xbcYk4\x1b\xd5\x98\x12\xcbN!gi\xd7\xaa\xac,\x06\xcdr6\x8d)\xcbhm\xa8gX\xde\
+\xf062\xdbA\x8b\xbbF\xa3\xd9\xad\xb0p\xbd\xbd\x91\x1ev5\xdb\xd1p\xad\xf4>\
+\x06H\x081M`QH\xe3hx\xef~\xd34[5P\x1a\xd4\x9bA\xb3\xc6\xd02-\xd30M\xcb\xe0\
+\xeb+K\xcdF\xb9\xe0\xa1mnL\x16\xe9\xe2D\xd7\x9d=\x1e#\xfa%&\x8dF\xb3;\x89\
+\xfd[\x00@\x00(\x83n9\xc2\xd0L\xef\xa1\xe0\x00g\xb6m\xb0\xa8I\xe3\xa8\xb4\
+\xff\xc0\x86\xb2\xc74\xa8\x07\x9c\xc7\x8c\xb8\x88\xc4vl\x03i}\xedN\xb3~7\x97\
+\xe2\xaa\xb2\x1bv:U\x9c\xd8\xaan\r\x8a\xb6\xdc5\x1a\xcd.\x845WXX\x01\x00\x8e\
+]S\xfb"\x92\x147G\x80S\xc7\xb5iP\xa3\x94\x95\x0el\xec\x97\xc4)\x0b\x1aM\x1a\
+\xc7\x0cL\xce\xa2t:M\xc3zP]\xa9\xd5\xcby\x0f\x1dsS}\x99\x91\xa9D\x8a\xe0\xed\
+\x80\x16w\x8dF\xb3\xfb\xc0\xc8\xffZ|\xea\xba#\x07"\x92\xf4^0L\xd72\xe3\xa6\
+\xcf\x91\x94\x0e\x1c\x90\x7f\xa5Q\x1c4\x03\x1aS\x0ef\x14\xc7^:\x1d\x06\xb5\
+\xb0\xba\\o\xac\x16<\xb07+\xbb\x9d\x1er2#\x8f\xbcC\x0f\x8e\x16w\x8dF\xb3\xdb\
+\xa0\xf5\xbb<\xae\x11\x00\xd6\xcdlG\xe4\xc4\xce\x11g\xc82\x8d\xa8\xb1n\xd8\
+\xeeh\xa9$\xff\x1a\x07a\x14EqL9\x1a\x94\xd1\x94\xeb\x84\r?\xa8.G\xf1z\xd1\
+\x03k\xb3\xb2\x03!\xde\xc8\xe4Vt\xe9\xc1\xd1\xe2\xae\xd1hv\x17\xc8\xe3\xea<\
+\xb4\x93\rt\xf1\xb6\x13B\xdc=\xa6a\xc6\xcd\x8a\xe9\xa4\x8a\xa3\x1b[s\xc4aX\
+\xaf\xd5\x90\x98\x0c\t"O\xbbv\xdc\xac\xd5\xfd;\x8c\xfa\xd9\x14\xda&\xc1\xcd\
+\xeb\xb2j\x8a\xe0\xed\x86\x16w\x8dF\xb3\xab\x88k\xb7Ej_\xd6}G\x0en\xd8E\xd3.\
+\xd0\xa6o{\xad\xa41\x82f\xad\x11\x05\x01C\x93sn\x10\xc34I\xdc\xac\xae\xaf-!\
+\xaf\xe5\xd2]\x94\x9d\x98vz\xe8\xd0\xa3\xef\xd0C\xb2\xed\x16\x014\x1a\x8d\
+\xe6\xa1A\x1e\xc7\xb5Vj\xdf\x98v)\xc0\x91\x10\xb7\x145\xabn6\xbf\xa1\xec\x08\
+Q\x10FA@\xd1\xa0\x94Y\xa6E\x80\xf3\xa0\xbaZ^\x08\xa2Z6\x05\x9d\xca\x0e\x00\
+\xde\xd0a\xc3|\x0c\xbbp\x0c\x88\xb6\xdc5\x1a\xcd\xee!\xae\x8a\xd4\xbe\x842\
+\xe4\x984\xdb9"\x05\x8fP\x92)\x16\xbdl\xdb\x9d\x82\x184\x82 \x088\x98\x94R\
+\xc7\xb1\x91S\x1eT\xab\xfeR#l\x0eg\x89cA\xa7\xb2\x9bn\xd6\xcd\xef\xdb\x82\
+\x1e=4Z\xdc5\x1a\xcd.\x01YD\xebK\xd0\xf5\xad%\x02\x9cC3B;\x95\xcd\x0e\x8f\
+\xa4\xd2\xe9\xd6!\x08a\xbd\x11\x84!e\x06e\xd4u,\xceb\x0c\xabq\xb0T\x0f\x82\
+\xb4C<\xb7\x8b\xb2\x03@f\xf8\xe86\x0c\x7fT\xd1\xe2\xae\xd1hv\tq\xf5kd1\x10\
+\xa0\x1c6\xe5\x08#\xc08\x04\x11"Ie\x87\xc77\x94\x9dcPo\x84a\xc8\xd0`\x9c\xbb\
+\x8e\x85\x9c\xf1\xb0\xca\x82\xbb\xc8\x03\xdb"\xd9T\x97\xfc\xc0\x00\xe0dJ\xb6\
+7\xbc%}zx\xb4\xb8k4\x9a\xdd\x00\xa7\x8d\xb8~\x07HG\x8e0\x02\x8cA\x10#"\x8c\
+\x8e\x9f\xcc\xe4[\xb9\xbd8\xe7a\xbd\x1e\x841\xe3\xc09\xb7\x0c@F\tk\x1aP\x05\
+\xd2\x889\xb1Mp\xcc\xee\xe7J\x0fm\x8b\xed8\xfa\xb3\xad\x1f+4\x1a\x8df@b\xff\
+\x16 \x03\x00\xc6`#G\x18\x01\xca\xa0\x19#"x\xb9\x91L\xbe\xb5\x82\xca\x19kT\
+\xfc d\x94\x13\x04\xc34\x81 5 NeR<\xae\xd8\x96\xc188&\x98\xdd\x04\xd2J\x15,g\
+\x9b\x86?\xaahq\xd7h4;\x1e\x1eUic\x05\x80 \x02\xe5\x1bf{L!\x88\x11\x10\x88A\
+\n\xa5#\xad\xc2\x8c\x07\xb5Z\xccIL9\x01b\x10N8#\xc0\xf2\xc3\xc3Qc9e1D`\x1cRN\
+7o\xbbH\xda\xfeXw\xbe\x1e\x10-\xee\x1a\x8df\xc7\x13\xf9\xb7\x00\x10D\x8e\xb0\
+\xb6\xd9\x1e1\x08)\x8a\x9ca^n\xccI\xe7\x00\x00\x19k\xd6\xeaA\xc4\xa3\x98Z\
+\xa6I\x80\x11\x16\x03\xf0\xc2h\x89\xc6M\x08\xcb\x96I\x9a\x11\xa4\xec\xee\x02\
+\xeef\xf7l\xab\xa4\xed}\xd0>w\x8dF\xb3\xb3a\xc1*\x0b\xd6\x00\x00\x11e\xb2\
+\x81\x88B$\xf6\xc2&`\x18Vq\xec(\x000J\x9b\xd5Z\xcc fh\x99&\x00\'<\xb6m\x92\
+\x1d\x1a\x05B\x82\xb5/\x1d\x8b\x88\xbff\\1Yl\x82\x98\x8e7|dK\xfb\xf6\r\xd0\
+\xe2\xae\xd1hv4\x18\xad_\x17\x9f\xc4[K\x08\x10D\xc0d\xb8\x0cBq\xcf\xa4\xe5\
+\xa4X\x14\xd5k\x8d\x90\x02g\xccu,\xc6\xe2\xb8Q\x1b*\r9\x99\x02\x00D\xeb\xd7-\
+h\x02@#\xc4\x94\xdd=H&[:\xbe\xdd\x92\xb6\xf7A\x8b\xbbF\xa3\xd9\x91\xd0\x98\
+\x861B\xb8L\xe2:!&e\xc89r\x84 \x06\xae\x04B\xda\xae\x97\x1b\xda\xcf\x85\xb2\
+\xc7\xc89\xb7m\x93\xb1\x98\xc4\x8d\xe1=#v:\x07\x00\x9c6X}\t\x00\x18\x07\x93\
+\x80\xd1M\xda\xed\xf4\x90\x93\x19\xed\xf2\x87\xed\x8a\x16w\x8dF\xb3\xc3@D\
+\x7f\xbdB\xc1\xb2\rn4\xe6\x81\x18\x00\xc0Z\xca\x8e\x9c\xcbTa\x049\x1b\xdd\
+\x7f\x92\x85A\xbd\x1e\x8412\xce\x1c\xcbD\xce n\x14K\xa3\x86\xd3\xda\xff\x9a\
+\xfa\xb7\x109!"\x86\xb2\xdb;K\xdbl\xf3\xebA\xd0\x0b\xaa\x1a\x8df\x87\xb1\xbe\
+\xb2\xba^^q-#M\xd6\x08o\x12 \x8caL\xa1\x19\xa9\xca\x0e\xc8Y\xb6\xb0\x87\x00\
+\xf1\xab\xcd\x90\x02\xe3\xdc\xb5-DNX\xb3X*Ieg\xe1\xba\xdcD\x1b\xb1\xfb:j*\
+\xbf\xdfrs[\xd4\xbdo\t-\xee\x1a\x8df\x87\xd1\xa8\xd7\x81r\x0ck\xb4~\x07\xc0@\
+\x800F\xf1\x9a\x92\xa2\xechZ\xb6\x97\x1b\xab\x07@\x19 \xe7\xaempNYP-\x8e\x8e\
+\x9aN\xba]\x19\xc6\x95\x1b\x00@\x08p\xec\x9aEr\xbbg\x7f\xec\x85\x16w\x8dF\
+\xb3\xc3\xf0\xf2y\xb0\xac\xa8\xfa5\xb2\x80\x18$\x8c\xb1\x11%\x94\x9d[\x96946\
+\x1517\x8e)\x008\xb6\xc1h\x8cQshl\xcc\xb0S\xb2*\xdaX\xe6Q\x15\x00\x10!\xa6\
+\xdd\x1c2\x00\xde\xd0\xa1\xed\x9c\xfd\xb1\x17\xda\xe7\xae\xd1hv\x18\x85B\x9e\
+6\xd6\x1d^\x070\x18\x87z\x80\xaa;\x059\xb7m7?:\x19q\x8fRf\x18\xc42\t\x8dBd\
+\xe1\xc8\x9eM\xca\x8e\x9c\xc6\xfe-\x00$\x84\xc4\x0c\x19\xef\x12$c:\x197\xbf\
+\x7f\x8b:\xf6\xad\xa2\xc5]\xa3\xd1\xec0\x0c\xd3\xcc\xa5C\xd6\x04 \xd0\x0c8\
+\xe3\xa8*\xbb\xe3z\x85\xd1\xc9Fl\x03\xa0i\x1a\x84 \x8f#\xe4\xf1\xc8\xde=\x89\
+@FZ[@\xda\x04\x91\n\xb8\x87\xd9\x9e\x19\x99\xdc\xe6\xd9\x1f{\xa1\xc5]\xa3\
+\xd1\xec0xTe\xcd2\x01B)\x06\xd1&eO\xa5s\xb9\xe1\xa3Alr\xc6m\xdb2\x0c\xa4A`\
+\x99\x98/\x8d\x19\xd6&\xd7\n\xb20\xae-\x8a\xcf\x94m\xce"\xd9\xc6\xce\x8cn\
+\xff\xec\x8f\xbd\xd0\xe2\xae\xd1hv\x18Q\xe5& \x07\x02\x8d\x109\xe7\x00@\x88\
+\x81\xc8S^\xc1+\x1cjD&\xe7\xdc2\r\x00F\x9b\x81iA\xa14F\x8c\xa4\xd6E\x95\x1b\
+\xc0)\x00 Bw\xb3\x9d\x18\x99\x9d\xf3>j\'Z\xdc5\x1a\xcdN\x82\x05\xf7X\xb0\x06\
+\x04"\x8a\x11\x05\'\x95\x05\x00\x1a7=o$\x9d?\xd8\x08\t"s\x1c\x1b\x90\xd3\xa0\
+\xe1\xa6\x9c\xdc\xf0(1\x92~\x15\x1eUYsY|\x8e\x19t\xd9D\x1b ]\x187\x9d\xcc#\
+\xef\xcf#C\x8b\xbbF\xa3\xd9I\xd0\xfa\x1d\x00\x00D\x8a\xa9\xb1CO\xb9^\x9e\xc7\
+\xcd\xa8Y\x8fb\xa3\x11\x12\x02\xdcqm\xe4\x8c6\x1b\xe9l*S\x1c!\xdd\x02\xd7\
+\xa3\xca\x9cH\xf9\xc89\xc8t4*\xc4r\xd3\xc5\x89G\xdd\x97G\xca\x8e\\(\xd0h4O&H\
+\x03\x1a\xdc\x03\x021#1+\x9aN\x9e\x10\x03\xc1\xa4<\x15R\x13\x80\x9b\x06"gq\
+\xb3\xe6\xe5\xbc\xecPweg\xcd\x15\x1eV\x84\xb1\x1e\xb3\xae\x9b\xe8\x817t\x98\
+\x98\xf6#\xee\xcd\xa3E[\xee\x1a\x8df\xc7\x10\xf9_\x03\x8f\x110\x88\xcc\x98\
+\x13\xce(7\x9d8F\x86\xc4\xb2\x008"\x10Z\xaff\x87\x8b^\xaegb\xde\xb8:\x0f\xd0\
+\xda\xc7\xa3k\xf8\xa3\x95\xca\xa7r{\x1fa7\xb6\x04-\xee\x1a\x8dfg\xc0\xe3\x1a\
+m\xdc\x05\x02Q\x04!\xe5\x84\xf08\x8a#F\xa2\x80\x03\x12\x8b@\x14\xc7\x04\xe3\
+\xa1=#v\xaa\xe7NI<\xaa\xf2\xb8\x06}\xd6Q\x01\xbc\xe1\x9d\xb1\x1dG\x7f\xb4\
+\xb8k4\x9a\x9dA\xe4\x7f\r\xc8\x11\xa0\x11!rpr\xe9\x88R\xa4\x8c!\'\x00\xdc0\r\
+\x03\x8a\xc3\xa3\xa6\xeb\xf5\xab\xa4rCl\xe7A)v\r\x7fts{\xect\xf1\xd1\xf5b\
+\xcb\xd0\xe2\xae\xd1hv\x00,\xf4Yc\x05\x08\x84\x11R\x86\x1c\x9cz\xad\x19\x861\
+\x00\x18\x84\x00\x001L@V\x1c\x1d\xe9WIs\x85G\xeb]6\xd1nC\x0c+=\xb4\x83\xc3\
+\x1fU\xb4\xb8k4\x9a\x1d@\xec\xdfB@\x82@\x19\xa4l\x88\xd1\r(\xa1q\x0c@\x00\
+\x10\x10\x10\x00\x11iP7\xed\x1ey`\x90E\xfe\r\x00 \x00\x11E\xecf\xb6\xa7\x8a\
+\x13\xa6\x92\x9f`G\xa3\xc5]\xa3\xd1lwhs\x95\x06kDl\xa6a\x80\x01\xc4$\x8c\xdb\
+.\xa3L(;\x00"\x12\xcb"\x96\x9b\xeeYI}I$\x1b`=\xc2\x1f\r;\x9d.\x1cx\x84\xdd\
+\xd8Z\xb4\xb8k4\x9am\x0e\xc6\xfe\xd7\x00\x80\x00\x8c#\x00 \x10\x03\x1b\xb9T\
+\x8dz\xc3a\xc89g\x00`\x1adt\xa4`:\xdd\xedndQ+H\x06 \xa6\xd8\xf5\xad%o\xf8H\
+\xe7\x8b\xac;\x97\xdd\xd3\x13\x8dF\xb3+\xa1\x8de\x16\xf9\xc2l\x97\x91\x8b\
+\x08\x04\x82\x157\x15:n\xc6\xf6\xf6\x98\x96e;)\xd3\xea\x99\x987\xae\xce#\x8f\
+\x01\x00\x11x\xb7\xa4\xedV\xba\xe8f\xc7\x1eY\'\x1e\x03Z\xdc5\x1a\xcd\xf6\x05\
+\x91Em\xb3]]\x02%\x00\x00$nVM;\xce\xec}\xaa\x9b!.\xab`\x18Wh\xed\xb6(\xd2\
+\xddl\'$32\xf9\xed\xb7\xfe\xb1\xa2\xc5]\xa3\xd1l_h\xfd.\x8f\x1b\x04\x80\xf2.\
+\x89\x1bIkk\xd3n\xca\xce\x03\xa05\xa4\xd5\xa8Y\xe1Q]\xec\x8c\xca9v\xf5\xb6\
+\xbb\xb9};n\x17\xbd\xfb\xa2\xc5]\xa3\xd1lS\x90\xd3\xc8\x9f\x07\xe1m\xef\x16\
+\xb9h\xa6\nnv\x8fr\x00\x03\xde\x04Ze\x91\x1f6\xabA3\nCJ\x19\x1f\xca\x12\xc3 \
+\x80\xdd\xcdvb\xda\xde\xf0\xe1G\xd9\x8f\xc7\x83\x16w\x8dF\xb3M\x89k\x8b\xc8\
+\x02\xd2+\xdf:!\xb9\xd21\x00\x00\x1e\x02\xafcT\x89\xc3Z\xd0\xa8\x87!\x8d"F\
+\x19G\x04\x04\xb0Mb\x18\x04\x00(\xef\xbe\xd7R\xbaxp\'\xee\xa2w_\xb4\xb8k4\
+\x9a\xed\x08\xb28\xae\xde\x06\x91\'\x80w1\xdb\x9dt\x96E~X\x9d\x07V\x0f\x9aQ\
+\x18Q\x1as\x11N\x03\x04\x08\x80a@\xcc\xc0\xb2\x08\x81\xd6[K\xddv\xd1\xf3R\
+\xbb(\xfcQE\x8b\xbbF\xa3\xd9\x8eD\x95\x1b\xc8#\x00\x88y\xc7\x0bG\x04(\x83\
+\xbbwj\xe1\xfc\x97E\x0fL\x039o\t\xba\xba#\x1eG`\x1c\\\x0b\x01!\x88!\x8c\x80\
+\x18`\x12\x00\x00D0\x0c \xc4\xc8\x8c\x1e\xdb\xa1\xbb\xe8\xdd\x17-\xee\x1a\
+\x8df\xdb\xc1\xc2J\\\xbf\x03\x00\x9c\x03g\x1d^r\x80\xb5\x1a\xaeT\xe8h\xde0\r\
+\x82\xb8I\xd3\xe5[M\x11#\xb6m\xa4=\xc7\xb2L\xb4\x91\x98\xd44\x80p&\xb4=\x8cy\
+\x10\x93\xa2\xbd\x83\xb7\xe3\xe8\x8f\x16w\x8dF\xb3\xdd\xc0\xa8r\xb3\x95\xde\
+\x8b%\x97@\t\x81 \x86\xf5\x1a\xf7\\#\x93"\x08 C\xdf\x11\x01\x00\x89a\xd8\x96\
+\xe9\xba\x96\xe3\x98\xaem\x10da\x10S\xca\x80s\x1a#\x02!\x04L\x93\xbb\x16\xf1\
+\x1b4\x8e\xc3>\xd1\xf1;\x1a-\xee\x1a\x8df\xdb\xc0\xaa\xc0\xab\xb4y\x8f\x05\
+\xeb@\x80u\x0b\x7f\x04\x80\xb5*g\x1c\x86\xd3\xc4 \xc0\xb1%\xeb\xa6e\xb8\xae\
+\xe5:\x96\xe3\x98&A\xce\x19\x8d\xa2F\xc0\x18G@\x04B\x908H\xc0\xc0\x18\x90\
+\xc7\x14,\x033)\xc2\xe2\x10\xd2\xbb-\x08R\xa0\xc5]\xa3\xd1l\x03x\x1c\xdf\xfb\
+\xa2\xde\xf0\xc3\x08\x1d\x83\xda\x16\x01\xec\x12\xfeH\x084B\xac4\xd0K\x91\
+\x94\x03\x8c\x01Gp\x1c\xa3X\xf4\x1c\x8b\x00\xe7\x94\xd2\xa8\x191\xca8\x17\
+\x07\x00\x01\xe4\xc4\x06o\xdct\x8b\x8c\xa1\x81M\xac\xdf <bHL\x024\x0e\xb7\
+\xbe\xaf[\x83\x16w\x8dF\xf3\xd8\xc1Z\xf9\xca\xfa\xbd{\x94"\x02\xb8Y\xa3\xfb[\
+K\x04\x00`\xad\x86\x00\x90u\t!\xc4v\x88m\x12\xdb20\x0e\xeb\ra\xa4\xb7\xdf^\
+\xdd8\x92\xd4\x82\x0c\x8fb\xd3\\!\x84p$6\xa6\xd3V$J\xd2\xa8\xb9\x95\xfd\xdcJ\
+\xb4\xb8k4\x9a\xc7L\xd4\xac\xac\xdf\xbb\'\xect\xc3 \x96\t\x88\xc0D\x06\x98vp\
+\x0b\xe7\xc082N2)c\xb4@\\\xdb0\x0c \x88\x1c\x91QJ\xe3V\xb4L\xa7\x13\x07\x01\
+\xbcl\xaa\x1e\x1a\x94R\x00\x04b\xa6\x1dS\xfc\xc9\x00\xa0q\xb0\x85\x1d\xddR\
+\xb4\xb8k4\x9a\xc7L\x1cE"\x96\x912\xf0l \x00\x94#"p\x04\xc6\x80q@\x00B\x88e\
+\x12\xc7!Y\x03\x10\x91s\xc6)\x08\xaf\r\x81\xfe\x9b\xe2\xa1C|\xbb0\x16\xd2\
+\x0c\xe7h\x93\xc8\xa25d\x00\x00\x84\x00\xa7!"v\xddG{\xa7\xa3\xc5]\xa3\xd1<f\
+\x9cT\x0e\x08A\x8e\x94\xa1k\x11b\x9aQ\x8cAD\t!\xa6A\x1c\x9b\x98\x06\x10\x82\
+\xc8\x019\xa7\x1c\xa0m\xa0\x0f"\xc9\x04\x08\x8b\x1a\x06\x9bO\xdb)\x00\x824\
+\xe0\x8c\xcaC\x91E\x88\x9c\x10\xf3Q\xf5\xed\xf1\xb1;\xa3\xf7\x9f\x10|\xdf\
+\x9f_X\xf4}\x7f\xf0\x02a\x18v\x1e2\xbf\xb0\xb8\\.?\xc2\x86>8\xcb\xe5\xf2\x83\
+\xb6j\xe6\xca\xd5>C\xd1\xff\x90\x8b\x97.?X\xfb\xbe\x8d\xb3o\x1f|\xdf\x9f\xb9\
+r\xf516\xc0v\xd3\x8e7B\x08\x19\x19\xc9\x14F\x0bN&\x93NYi\xc7\xf0\\\xe2\xda`\
+\x10\x8e\x9c3\x86\x88\xad\xb8\xc7\x075\xb3\t!\x9cs\x1a\xd4iXc\x8cm\xaa\x00\
+\x99\x88\xa0\xdc}h\xcb}G\x12\x86\xe1\x1bo\xbe\xe5\xba\xee\xd8Xiv\xf6\xba\xef\
+\xfb\xaf\xbc\xfc3\xd7u\xd523W\xae\xfe\xe1\xeds\x13\xe3\x07\x96\xcb+\xcf\x9d=\
+}\xf6\xcci\xdf\xf7\xff\xfe\xf5_\x8d\x95F\x97\xcb+?\xf9\xf1\x8f\xa6&\x8f\x02\
+\xc0?\xbc\xfe+\x00\x08\xc2pj\xf2\xe8K/\xbe \x8e\x15%_{\xf5\xe7c\xa5R\xe7\xd9\
+}\xdf\xff\x9b\xbf\xfd\xbb\xd7^\xfd\xc5\xc4\xf8\xfd\xdf\xdb\x9e\xbd>w\xf1\xe2\
+\xe5\x7f\xf7\xd7\x7f5`\xd7\x96\xcb\xe57\xde\xfc}\xcau]\xd7\t\xc3(\x08\xc3_\
+\xbe\xfa\xf3|>\xaf\x96\xf9\x87\xd7\x7fu\xf6\xeci\xd1\xfe\x8d\x13\xcd^\xcf\
+\xe7\xf3\x89\x92\xfd\x9b-\x0fy\xef\xfc\x85\xb3gN\xab\x85\xff\xcf\xff\xeb\xff\
+\x16\x1f\\\xd7\x15\xa3\xd7\xbf#\xf7={/:O\xf4\xa05|s*~uv\xf6\xfa\xf4\xa9\x93[\
+\x7fj\xc9\xd0\xf0Hd\xacZ6o\xf8u\x1aSD$\x00\xc8\x01\xdb\x1e\x97o\xe87!\xd0\
+\xddwcZf\x7f\x9f\xce\xceE\x8b\xfb\xceC(\xfb\xd9\xb3g\xca\xe5\xf2\xfc\xfc\xc2\
+\xc4\xc4\xf8\xf4\xf4\xc97\xde|+\xa1\xef\xf3\xf3\x0b\xaf\xbc\xfc\xb3\xa9\xc9\
+\xa3B\xa9\xcf\x9e9\xfd\xf9\xccU!\x1f\xf3\x0b\x8b\xe7\xcf_\x98\x9a<:{}\xceu\
+\x9d\xd7^\xfdE\x18\x86\x7f\xf3\xb7\x7f\xf7\xdc\xd9\xd3\xa2\x86\xf7\xce_x\xee\
+\xec\xe9\xae\xca\x0e\x00\x1f_\xbc\x9c\xcf\xe7gf\xae\xf4\x11\xf7_\xff\xe6\x9f\
+^{\xf5\x17\x000\xc8\x04 \xf1}\xff\xd7\xbf\xf9\xad\x9cx\x00`~a\xb1S1\xcf\x9e=\
+\xfd@\xd5\x0e\xdel\x95\xff\xf4\x1f\xff\x83h\xd2?\xfe\xe6\xb7S\x93G\x1f\xe2\
+\x8cr\x10\x1e\xe8D\x0f1C\xect\x906\xa3\xca\x8d(\xc60\x0c\x00\x80\x10@\xc3\
+\xe5H\x0c\x8c\x08\xf0o,\xec\xbd\xcf\x0b`\x99\xa6\xb1K\xd3\x0f\xec\xce^\xednf\
+\xae\\\x9d\x98\x18\x9f\x9f_\xa8T\xfc\xb3g\xcfT*\xfe\xc2\xc2\xe2\xd4\xd4d\xe2\
+\xc9Z(\xbb\xfa\xcd\xec\xf59\xf1\xcd\xc4\xf8\x81 \x0c}\xdf\x9f\x9f_\x98\x9a\
+\x9a\x04\x00\xd7u\x85\xd6C\xcbK\xb3\xd2\xcb\x8e\x13\x8e\x9d_\xbe\xfa\xf3\xd9\
+\xebs\xaa#b~a\xf1\xe2\xa5\xcb3W\xae\x8a\x02\xe2?Q \x9f\xcf\x89/\xd5\xc2a\x18\
+\x8a&]\xbctY\xfeI\xb4Pm\xb6\x94\xd4\xe5r\xd9\xf7\xfd\x8b\x97.\xfb\xbe\x9f\
+\xcfo\xbcu2s\xe5\xaaZC/z5\xfb\xbe\xe4\xf3\xf9\x94\xebV\xfc\xaa\xe8H\xa2\xb3\
+\xbe\xef\xcb\xda\xc20\xbcx\xe9\xf2\xc5K\x97\xc30\xec\x1c\x04\xd1S1\xc2\xf7=Q\
+\x18\x86\xa2_\xb2\xf2\xe5rY\x0e\xaf\x1c+\xb5\xe3r|\x84GKV+\x87Z\xd6 \xff$\
+\xca\x8b\x06\x0f> \x8f\x82\xda\xbd\xdbA\x10\x01\x02!\x04\x810g\x1f\xc9?%\xfe\
+\x03+\x8f\x8f\xcem\x82\x00H\xb9X]\xdduhq\xdfy\xcc\xcf/\x08/\xc1O~\xfc\xa3\
+\x89\xf1\x03/\xbd\xf8\xc2\xf8\xf8\x81\xe9S\'\xe7\xe7\x17\xba\x96\xff\xc3\xdb\
+\xe7\x9e;{\x1a\x00|\xdf\x97Va!\x9f\xab\xf8\xd5M\xdf\x14\xf2\xe2&\xff\xc3\xdb\
+\xe7\x00\xe0?\xff\x97\xff\xfa\x87\xb7\xcfu\xde\xf63W\xaeN\x8c\x1f\xc8\xe7\
+\xf3\xd3\xa7N~>\xd3R\x8a\xd9\xebs\xe7\xcf_\x10m\x9b\xb9r\xf5\xe2\xc5K\x00p\
+\xf1\xe2%1O\x9c?\x7f\xc1u\xdd?\xbc}N\x88\x8e\xef\xfb\x7fx\xfb\x9c\xeb\xba\
+\xef\x9d\xbf0;{]\x94\x14^o\xf1 "\xea\x94\xd2#\x8e:\x7f\xfe\xc2?\xfe\xe6\xb7A\
+\x10\x8a\xcf\xcb\xe5\x15\x00Pk\x10\xdf\xf4\xa2k\xb3\xfb#\xce\xfe\xc6\x9bo\
+\x01\x80\xf0n\x89>\n\x7f\x17\x00,/\x97\xff\xfe\xf5_I\xbd~\xef\xfc\x05\xf1\
+\xe5\x1bo\xbeU\xf1}u\x10\xe6\x17\x16/^\xbc\x0c\x0033W:\xa7\x96\xc4\x89\x00\
+\xe0\xd7\xbf\xf9m\xa5\xe2\x03\xc0\x1bo\xfe^\xa8\xf3\x1bo\xfe^\x8c\x8f\x18\
+\x8d\xce\xa1\x93\xe3\x93\xea6\xd4\xb3\xd7\xe7\xde~\xfb\x1d\xd9<h?(\x04A\x18\
+\x04\xa1h\xf9c$\x8a\xa2\xb6\xf3\x05#\xe6U|\xe2\x97W\x1ak\xab\xd5{5\xbf\xe1\
+\x01|K\xab\x9d\xc8\xb1\x05\xdf\xf8\x8e\xc5,\x8e\xbe\x9d\xfa\xb7\x19Z\xdcw0\
+\x83\x18\\B\x86\x06\xf7\xe4\nQ\xf8\xe5\xab?\xff?\xfe\xf7\xffU\xd8\x8f\x89\
+\x02\x9f\xcf\\\x9d\x9e>\t\x00\xd3\xd3\'\xa5!y\xf1\xe2\xe5W^\xfe\xe9\xd93\xa7\
+_y\xf9gg\xcf\x9c\x16\xbe\x88\xd7^\xfd\x85j\xfe?=}R\xe8\xd1\xe73W\x9f\x9e>\
+\x19\x86\xe1\xec\xf5\xb9\xe9\xe9S\xa5Riz\xfa\x94\x14\xdc\xc4\xca\xc1\xfc\xfc\
+B\xb9\xbd\xac\xfa\xdc\xd9\xd3/\xbd\xf8\x82\x9c\x8dD\r\xaf\xbd\xfa\x0bq\xc6\
+\xc4\x81\x834\xfb\xbe\xcc\xce\xce\x01\xc0k\xaf\xfe\\\xfd\xf2\xe3\x8b\x97\x7f\
+\xf9\xea\xcf\xcf\x9e9\xad\xba\x8f\x00\xe0\xa5\x17_\x10#0\xbf\xb08V*\xa9\x83P\
+.\x97]\xd7\x19\x1f?\xf0\xda\xab\xbf\xe8\xeauQO4{}.\x9f\xcfML\x8c\x97J\xa5\
+\xa9\xa9\xa3\xb3\xb3\xd7+\xad\x07\xa0\xbcx\x1a\xeb5tr|\x12C\r\x00\x17/^>{\
+\xf6t\xa9T\x9a\x9a\x9a\\.\xaf\xf8\xbe/|t/\xbd\xf8\xc2K/\xbe \xe6\xfe\xc7\x88\
+\x9d\xca\xb7r\xfa"\x98\xb6i\xa7\x1c\x0e\x10SF\x19\xb3\x1d\xd30\r\x80ol\xbc\
+\x13\x02\xe9\xbdF\xe1)\xcc\x1e7\xd2\x1b{\xa5\x12@\xcev\xa7\xb8k\x9f\xfb\xcec\
+bb|\xe6\xca\xd5\xa7\xa7O\xbew\xfe\xc2\xd4\xd4\xe4\xec\xec\xf5B!_.\xafH\x9bW\
+\xf2\x87\xb7\xcf\x95\xcb+R\x9b\xf2\xf9\xbc4\xd5+~\xb5\x90\xcf\x89o\x00\x0e\
+\x00@\xa5\xe2\x8f\x8d\x95\xca\xe5\xf2\xd3\xd3\'E\x99\xe9\xe9S\x17/^R\'\x06\
+\xe1\x88\x10k\xb0\x82\xd9\xebs\xd3\xa7N.\x97\xcb\xf7\xf5\x14O\x9f:\xf9\x9f\
+\xff\xcb\x7f}\xe9\xc5\x17f\xae\\\xfd\xf7\x7f\xfdW\xcb\xe5\x950\x0c\x85y\x0b\
+\x00\x85|Ntmv\xf6\xba\xb0^\xf3\xf9|bNJ\x9cb\xb9\xbcRP\xfc3\xea\xe7\x04\xbd\
+\x9a\xdd\xbf\xc1g\xcf\x9c\x9e\x9a<\xfa\xf7\xaf\xff*\x0cCu\xe6H<\xee\xc8\xef\
+\xfb\xcc.\xe2\\\xc2\xfa\xee\\\x1fN\x9c\xc8\xf7\xfd\xe5\xf2J\xd8\x1e\x99R\xa9\
+4V*=w\xf6\xf4\xc5\x8b\x97\xff\xf0\xf6\xb9\x9f\xfc\xf8G\xae\xebv\x0e\x1d(\xe3\
+\x93\x18j\x00X.\x97gf\xae\xc8\xf2A\x18\x96\xcbey\xc1<v/\xbf\x97\x1f\xad\xaf\
+\xcd\x99\x06\x03$\x16\xd6\n\x99\xb4\x93\xceS\x06H)\x89\xd6h\x18\x7fc\xb7;\
+\xd6\xc3,\xe7\x9e\x15S\xc30BV0Y\xdd1\x1b\xe2\xc5\'\x1a5]\xaf\xf8\xed\xf4d;\
+\xa1\xc5}\xe71}\xea\xe4\x1bo\xbe\xf5\xe2\x8b/\x00\xc0\xcc\xcc\x95\xb1\xb1\
+\xd2\xf8\xf8\x81\xb7\xdf~\'a`\n\x9b]\x8d\xee\x10^u\xb1\xa0\x9ar\xdd|>?11>3se\
+\xfaT\xcb\x8e~\xe9\xc5\x17\xe6\x17\x16gf\xae\x08U-\x97\xcb\xa5\xcdk\xaa33W\
+\x7f\xf2\xe3\x1fIY\x9c\xb9r\xf5\xe3\x8b\x97\xa7O\x9d\x9c\x9a<:\xbf\xb081~@X\
+\xc4]e\xceu]\xd1r\xe1\x1e\xc9\xe7\xf3\xae\xeb\x8aE\xe00\x0c\x85q*4\xaePH\xca\
+zW&\xc6\x0f\x08\xc7\x91\xa8A\xbaef\xaf\xcfM\x8c\x1fP\xdb\xd0\xab\xd9\xf7=E>\
+\x9f\x7f\xee\xec\xe9\xf7\xce_x\xe5\xe5\x9f\xa9\xc3x\xf1\xd2e\x11}$M\xe3\xfb2\
+}\xea\xe4\xd93\xa7\xdf;\x7fA\xfc\x04}N4>~`~~A\x18\xfe\xcb\xe5r!\x9f\x17\x9d\
+\x9d>ur\xf6\xfa\xdc\xcc\xcc\x15\xf1\x98\x92\x18:\x95\xc4P\x8b6OO\x9f\x12\xb3\
+\xa6x\xb0\x98\x98\x18/\x97\xcb\xe2\x1b\xf9l$bO{-\xa4?:,\xdbM\x17\x0fE\x959S\
+\xbctZ\xbf\x1bG\xeb\x86ir\x1a3\xfa\xcd\x95\x1d\x00\x88\x9b\xceT\x1ba\x1cr\
+\x02\x88hf\xdd\x14!\r\x14)\xc5\x8c]\x18\xe4\x0eZ\xdcw"\xe2\xc6~\xe3\xcd\xb7\
+\xf2\xf9\xfc\xd8X\xa9R\xf1\xe7\xe7\x17^y\xf9\xa7\x9d\xa1\x90\xf2_\x00\xf8O\
+\xff\xf1?<=}\xf2\xef_\xff\xd5\xfc\xfc\x82\x08\x85\x04\xa1S\x17/\xff\xc3\xeb\
+\xbf\n\xc2p\xfa\xd4\xc9\xd6\xb2\xea\xec\xf5\x7fx\xfdW\xae\xebT\xfc\xea/\x95\
+\tc~a\xb1\xe2\xfb\xaa#bj\xf2\xe8{\xe7/\xcc/,\x9e={\xfa\xd7\xbf\xf9\xedXi\xb4\
+\xe2W\x9f\x9e>y\xf6\xcc\xe9\xb1R\xe9\x1f^\xff\xd5t\xfb!\xa0U~j\xf2\xd7\xbf\
+\xf9\')\x94\xcf\x9d=\xfd7\x7f\xfbw\xe2(\x11\x9c\x93\xcf\xe7_{\xf5\xe7o\xbc\
+\xf9\xfb\xd9\xd99\xd7u\xc4IES\xbb\xa2\xd6 \xbb/\xbc\xdbj\xbcM\xaff\x0f2\xda\
+\xc2G\xaf\x16\xfe\xc9\x8f\x7f\xf4\xc6\x9bo\xbdw\xfeB>\x9f\x1f+\x8d\xf69V\x0e\
+B\x18\x86\x9f\xcf\\\x15\xeb\x1c\xbf\xdc<\x07w\x9eH(\xf2\xdf\xfc\xed\xdf\x89\
+\xf2\xaf\xbc\xfc\xd30\x8c\xdex\xf3-\xd1M\x11\xae\xda9t\x89\xda\x12C-\x7f\xa0\
+0\x8cJ\xa5Q1U\xfc\xfa7\xbf\x15\xee \x00\x10\xa3-\xfew\xeb\xc5\x1d\x00\n\xa3\
+\x13k4\x8ckK\x96\xc1\t\x10\x16\x87,\x06\x00\x11:\x03@\xbe\x91c\x06\x11\x1d\
+\xabQ\x18\x1a\x0b"@D\x9b\xc4.6\xc52*\x07{W\x9a\xed\x00@\x1e\xe1J\xb4\xe6\x11\
+\xe3\xfb\xbe\xf4\xae\x0cx\x88\xb0p\x13\x87\xcc/,\xba\xae\xa3\xde\xd2\xcb\xe5\
+r\x18F\x0f\x14\xfc\x97\xa8\xb9\xeb\x89\x06l\x8fl@\xa2U]\x11\x830V\x1a\xed\
+\xefs\x7fD\xbcw\xfeB\xa1\x90\xef\x13Y${\'z4x;\x13\xfd\xea\xfc\xad\x07\x1ca\
+\x95\xce\x1f\xba\xf3\x9b\xc7K\x14\xd4\x83\xc6:\x8b\x1a\x80\x94\xc5!"\x03N\
+\x018g\x8c\x00\x07\x10q\xefHHWc\x9e\xb4\xfe\xe9&i\x88h:)\xc3\xcer\x8e\x18W\
+\x91E\x00\x84q\xf0FOd\x87\xf6=\xda^=&\xb4\xb8k4\x0f\xc6\x1bo\xbe56V*\x95J\
+\xbe\xef\x7f|\xf1\xf2\xbf\xff\xeb\xbfz,\xf3\xca\x93\x02""g,\xa6\x8d2k\xdc\
+\xae\x86\xf98\x8a)\xe7\x06A\x038\x10$\xc0\tp\xd3@\x03\xa8a\x00G\x93\x10\xf1\
+\xde)kW\xd0zK\xc9 \xc2\x0b\x03\x00\x84#A\xc3\xf5\x86\x8fd\x8b{\x1fc\xe7\x1e)\
+Z\xdc5\x9a\x07c\xb9\\\x9e\x99\xb9*\x96U\x9f;{\xfa\xb1\xafF>!D\xfe-\x08\x97\
+\xc1\x1a[\xb9\x17\x07\x14L\x83\xb0v@#\x01\xdc3d\x86\xe1\xbd\x8cMy\xe6h\xdc\
+\xac\xc4\xcd5\x1a\xd5\x11\xc1 \xe0\xb9\x04\x01\xb85D!E\x90\x11\x02\x1c\x0c\'\
+]\xf0r#\x86i?\xd6>=Z\xb4\xb8k4\x9a\x1d\x80\xbf\xfc\xa5C\xef\xb8\xe9\x92\xdft\
+\x10\xa0\x1e`\x10#!\x04\x11-\x83\xec\x1f6W\xd7\xcb\x9e\xd5\xe0h\xd0\xf6\x16\
+\x1f\x08\x90u!\x9d&\x80\x00V\x01\x8a\xdfy\xbc]\xd8bt\x9c\xbbF\xa3\xd9\x01DQ\
+\x18\xc6\x9c\xc7k\x9eC\x0b\x19\xcb\xb6-\xcb$\xa6\x01i\xc7(d\xcc(\x8a9\x0b\
+\xaaMT\x95\xdd$\x90r\x08p\x00\x04\x88+\xd0\xb8\xf1x\xbb\xb0\xc5\xe8h\x19\x8d\
+F\xb3\x03`4\xae5\xc02b\x84\x95ZP,d\xf2i\x1b9r\xcb$,nV\xeb\xf78g\x88dc7m\x84\
+\x94\x0bD}\xff\xa9\xbe\x00\xf6\x08\xd8O\x8a\x1bM\xbbe4\x1a\xcd6\x05y\x8cq\
+\x03i\xd5\xa0\x95;\xcb\xabA\xc4-\x03<\x17"\x8a\x00\x161, \x049\xe5\x8c\x02 "\
+\xe1\x08\x9eK\xa0m\xb6\x0fe;6\xe10=\x18:\x03\xbb1{{\'\xdar\xd7h4\xdb\x08\xe4\
+1\xd2\x06\x0f+<\xf2[1\x8b\x04\x10\x811\x14B\xdd\x8c\xc02\x08\x00\x05N\x01\
+\x80\x00X&\x01 \x11\xdd\xc8\xdd\x8b\x08\xe9\x84\xd9.`\r\xa8\xdf\x84\xec\xe4\
+\x96v\xe91\xa1\xc5]\xa3\xd1<V\x90!\xa7H\x1b<\xaa\xf0\xa8\x8aq\ry\xdcR\xe5\
+\xb64s\xbe\x11\xd1(6J5\t\x91\xd1\xee\xbcU\x00\r\x83\x10\x02\x9c\x83e@\xca!\
+\xdd\xdf{j\xde\x86\xf4>0\xbd-\xe8\xd9\xe3E\x8b\xbbF\xa3\xd9rx\x04\xb4\x8e\
+\xb1O\x83\n\xa7M\xe0\xb1\x9a\xa91\x01\x01\xe0\x08\x1c\x95\xffW@\x00@\x10\x89\
+\x04\x8c\xf6\x1bL\x99T7\xb3]\x1eQ\xfd\x12\n\xcf\xc2.M\xe3.\xf9\xff\x01\x9e\
+\xe6W\xce\xb2\x99\x1b\xa3\x00\x00\x00\x00IEND\xaeB`\x82' 
 
 def getSplashBitmap():
     return BitmapFromImage(getSplashImage())
@@ -4330,562 +4407,563 @@ u\x9d\xe9\xa9\xeaTu\xb2\xd3\xe96\x9a\xcdN\xa7\xc79\x17R\xeaq\x85R\xe28N!\x9f\
 \x94Zv\xe0\xfb\x8e{\x8bA\x8dT\xd9=\xcf-\x16\x0b\x04\x80RbY\xb6\xfa\xb8\xc5Ba\
 \x9f\nq]7\xf3]\x18cD\xcaR\xa9X*\x15\xdb\xed\xce\xf6\xeen\xb7\xdb\xd7\xb3.3\
 \x11J\xc9\xc0\xb5t\xaa:1Q\xb6(U\xc3\'!$\xcc\xe5\x84\x90T\xb51\xdb\xb6,+\x9f\
-\xcf\x1fD\x15x\xc3E\x07\x1b@\t|\x0f\xb5\xfd\x86{-i\x11\xff\x7f{\xef\xfe\x1b\
-\xc9u\xdey?\xe7\xd4\xbd\x9a}#\xd9\x9c\x1b\xa9\xb9*\xf2\xcc(\xc9h\x94D\x91w\
-\xe1\xd7\xce\xeb\xbc\xf6\xc2\x86\x9c56\xbbX`\xb1\xc0\xfe\xb4\xc0b\xff\x9c @\
-\xb0X\xe0\x85a\xc0p\x04\x08+!\xc6+\xad\xbck\xbdr\xa2\x89\x12{4N\xc4\x19\xc7\
-\x99;\xc9\xb9\xb09$\xbb\xfaR\x97sy\xf6\x87\xd3],V_\xd8\x9c\x19r\xd8\x9c\xf3\
-\x81!\xf74\xabO\xd59]\xf5=O?\xe79\xcf\x13m\x88\xe8\t\xc0\x0eS\x85\xe1\x14\
-\xdd\xe2\xb1\xc9\x13\xf7\xf4NJ\x1d\xdf\xea\x01S\xb7\xa3z\xb6\x1fN\x7f\xffA\
-\xf5O\\\xb6\xea\'K\x1e{`\xca\x0e\x01Dj2\xa3\x12\xb9\'\x13wA:3\x86a\x98\xbd\
-\xdc\xd9\x9c\xf3f\xf5_l\x96\xfe0m\xca4M\xc7qT^@\xb5\x19\xbd\xff2\xd4\x05\xc0\
-v\xbf\x84a\x18*\x82BJC\xd0\xf2Z\xe5\xff\xae\x97\xff\x88\x8a\x8e#\xd6\xbd\xe4\
-\x81\xcbWM\xd9\x06\x90\x80 \xa9\x9bX\xb3\xa1\xb3\xc0\x9d\xa3\xe0TU\x1aBJ\x07\
-Gy\xf7\xfb\x82\xd2\x87GP\xbaY\xfc\x83\x8d\xa9\xdf7D\xcb\xe5\xab^\xf2\xc0\xe1\
-u\x03{[H\xa8\x8dVI\xfa\'h\xe5Uo\xfa\x94W\x9au\x1c\x87d\xa0\x94\x86\xa5\xdf\
-\xb9\xe3\xbc\xa6&\x0c\xd5\xa6\x99I\x8b855\xe5\xba\xae\xea\xe5\x98_\x8a\n#Q\
-\xcf\xf9\xf2\x91\xff\x94\x8b\xcfQ\'U\xdf\x9d\xe7y\x85B\xc1w\xfd\x11K\xa9\xfd\
-\xe7\xea\xdf\xe2\xa4N\x1d\xc7q7\x84F\x08)\x8d\xc0\xb8\xd4\xf0\x7f\x97"3\xf9\
-\xa6\xc7\x1ez\xec\xa1%6)\x08@\x90\xd4N\xcc\x99\xc8\x99O\x9c\xe3\xd2\x9eN\x17\
-\x03\xd3u\x143\x93M~\xe0e\xa4\x13|\xee\x1a\xd4\r\x90Z\xaf\xc5\xe2T\xb18\x05\
-\xdd]\x97\xa8\n\x15)#\xc0\xb2\xac\xf4\xd7\x98\xea\xbb\x1a\x7f\xc68b\xea\x0f\
-\xe9\xbat\x08\x1d\xb5\xc8l\x18\xc6\xdc\\mz\xbaJ\x08\xb1m\xdb\xf3<\x95:I\xe5\
-\xb3\x1cgT\xfb\xc7\x93RZ.\x95\xbe\xf2[\x1e"Z\x96\xa5\xbe)\xf5,\x0c\x9b\xf3\
-\xb2\x93n\xb6\x9d\xde7"\xd5hH)\x19\xe3Q\x14\xc5I\xa2f2B\x88eY\xbe\xef\xf9\
-\x9e\xa7*\xbe\x1a\xbd\xf2\x8a*\x85\xd4\xb1\xa3G\x8e\x1e\x993\x0c\xc3q\x1c\
-\x95$\xca\xf7\xfd]\xe5U~!lK6 \xb6\xfdP\xcf2v\xad%d\x99,&bxz\x8b\xc2\xcc\x19\
-\xd8\x93T\xe3\xfbB\xf6wq\xf6N\xea\t\xabDDf\x1co\xb8\xc7\x1b\x00\x90y\x0c\xd4\
-\x07\xdd^6\x02\x00\x90\xbd\xf2\x8f\xa9h*uK}\xc7\xfd\x16e\xd6TIU2\x8d\xd2K\
-\x92D=\xde[Na\xb3\x94`1\xf1N62-\xe4>\xa5V\x08\xbbU\x8d\x06\xd5S\xcd\xfa\x82\
-\xb0W-\xde0\x8c\xf4\xc9A\xb3\x12\xd9\xe5\xc8\x7f5\x1d\x10\x00H\xa7@\xdf\xf7\
-\x89]\xc8\xc9zjh#"\xa54\x15\xf7tS\x929<\xf0\xbc\x1f\x92YVU\x93\x90j6\xe7\x1f\
-\xc8NTN\xa6\xc6\xec\xae4H5\x92\xde\x0cYaU\xf7@z\x1bHi\n\xd3m\xb9G[\xf0Fn\xf0\
-\xd5\xa7\xec\x9e+)U\xf6\x1d/\x89\xf4\xaa9\xabk\xc8\x8dg\xf6\x06\xc8*\x9di\
-\x92\xfe\x83\xd3\xf5gedt\x7f\n\xf4m\x81\xf3=\xaf\x7fJ\xcb}\x89\xea\x9d\xec};\
-\xfexf\x9bM\x1f\x13\xd3\xec\xeaC\xeeY\x18\xf8)\xe8\xdd\xa2\xd9v\xb2_J:\xe1\
-\xa9q\xf3<7\xfb\xd9\xfe1Q\xefs\xce\xd5M.\xa5T\xedg\x1f\xc9\xdd\xce[\xfb\x0c\
-\x0b\xee)/\xa8\xaa\xc81\xf0\x98\xf1k-\xf1\xf6#\x994\x01\x80\x00\xb0\xe1\x0b\
-\xb3v\xa1fyU\x98\xc4\x05U\xe8\xbb\x93\xd2\xbb\xdc4\xcd\xecm\xa4\xa4\n\xb7\
-\xafI\xe6d\x0b\x00\xb2\x85\xe7\xb37\x9fR\x9f\xd4\x82\x1bv\r\xe9\x05\xa42\xad\
-<\x12\xe9\x84\x91u\xcb\xe6|\x08\xe9S\x94\x86\x7f\xe4j\xd5\xe7\xce\x98\xfeV\
-\x80\x8c\xa1\x94\xceg\xfd.\x02\xe8\x89{\x1a\xd1ad\xf6\x7f\xa6\xa7V\xe9X9\xe7\
-\xe9\xe5e\xbb?\x8e\xb7\x04\xb6kMV\xd9S\xa7Y\xae#\xd9\xf9l\xb4\x1bd\xd8\r\x90\
-\x1e\x9c\x15#%\xac\xb9\xc1O\xef\x04\x80m\xd3jv\x19#\xbd\x18k{y\xeb\x11\xfa\
-\x0e\x99\xf4\x90\xe9x\xe6n\x80\xec\xb7\x9f\xfd\xe2r\xeb\x13\x84\x10\xe5\x7fP\
-%\n\x84\x909?{\xb1\xcf\x19\x92\x1bIe\xe8\x00@6Y\xf1SL\x99\xb9\x1bCM\xfc\xb9g\
-aG}\x1f8\xdbe\xed\xa7\x9c?-7z\xe9\x98\xa8\t/+\xeei4\xcen\xbb\xb6\xffH\xd6\
-\xe6\x9d\xee\xbe\xe5\x11\x159\xfc\xe9\xd3\xa3\x03\x16\x14(\xf9V\xf8#\x0c\x9d\
-*\x80\x1a\xfeL\xd7\x87<\x91\xe2\xae\xc8\xca_\xaa\xda\xa9\xb8gWu\xb2bjf\xf3\
-\xd3\xf6\x9czY-H\x8dbs{\xdc\xee\x08}\xcf\xdd\xca*]e\xf6\xd7@\xbf\xecf\xaf\
-\xc7\xc8\xec\xddO\x7f1\x8c0\xdea\xbb\xb2g\x1c\x11\xf9\x05F\xb2\xdd\x94V~\x00\
-\xda\x0b\x96W\xef\xab\xc0\x0f\xd5\x02\xf4\xe6\xc2\xac\x83e\xfc\x07\x89l\xffa\
-N\x081MS\x8d\x00d\xb45\xed\x882\x96\xd3\'y\xb72\xa4&\x0f\xd8>\xb9\xe6f\xd6\
-\xdc\xe4\x9a\x1b\xfc\xac\x9d8p\xf0G_Rv\x8e\x19x\x03\xf4\x7f\xfb\xd9KUW\x9b\
-\x1a\x19\xea#\x94\xd2\xcdFC\x88\xec\x86\x1d \x84\x94J\x03<\xdd\xa9\xb2\x03\
-\x80a\x18J\xdcS}\x1cx\xd3\xee8\xaa\xb9\x1bCiqz3\x8c\xfeA@\xb6[<\xd9\x11\xce\
-\xcd\xb8\xfd\x8b%\xe9\x80\xa4\x93\xab\x9a\xf0\x84\x10jH\xd5\xe47\xda\xe4:P\
-\xb0\xe0nZHoXE\x0e\xcb\x9fVV\xf6\x8e\xf0\xe6\x12\x8a\x08v2\xdb\xbd\xf2B\x1aO\
-9\xa9\xe2\x9e{\xb6S\xa1L\x83grK\xf6\xa9\xa8\x99\xa6\xb9\xb6\xf6\xa4\x11\x04\
-\xae\xe3\xbe\xf6\xda\xab\x00\x90\x95\x80t\x85\'\xfb\xf0\xef\xf8k4k\x0c\xaa\
-\xe7!\r\x1bP-g\x7f\x9e\xc3\xf6\x1b:\xd5w\xb3\xb7\x8e\x97^j\xee\x8c9\xcb(}\
-\x08\xfbE\xa4_Gr\x06\x11\xc9\x98\xd84\xe3\xedI\xcf\x95\xed\xfe\xf8\x0fR\xd6\
-\x04S\x8d\xa4\x01\x8b\xb9#\xd3\x87y\xe0L6&\xd9\xc95m07\xf8\xfd\xe2\x9e\xf6.{\
-\r\xd9/:\xfd\xae\xc7\xec2f\xaa?g\xe5,\xfb\xd5\xe7\xc66\xab\xefF\xcf7\x98Z\
-\xee\xbd]\xfb[\xa6\x9e\xeb:\xae\xe3\xe6\x86(\xf7%*9\x86\xde\x1d2\xfa\xbe\x1d\
-\xdd\x97\xec\x8d\x91\xfd17\xd0g8\xf0KI\r\xfc\xd4J\xc8*{\xeeq\xc8}\x17\xd9\
-\xdf\x07\x88\xa8\xc4=\xfd\x06\xd39`W?\xf5\xf6\x1f\x99\x04\xdd\x95O\x00\xc6q\
-\xb0\xd9>~\xad%\x1e\xf1\xf6\n\x00\x90\x91\x1e\x1ej\xba^e+\x9d\xe4\xa4\x8a;\
-\xf4\x19\xce\xeb\xeb\x1b\x96e\xfa\xbe\x9f\x0b\x7f\x86\x8c5\xaa\xfe\x1b4[W\
-\xbf\xf8\xd5\x89\xe3\xc7_\x7f\xfd\x02\x00\x18\x86\x81\x99\xca\xcbi\xe3Y\xfbn\
-\xc4\r\x9d\xbei\xf4*=\xa9{4;a`\x86\xac\xa1\x97U\x99\xf4\xf5\x08e\xe9\x973u\
-\xdf\x8fp\xfed\x85\xcf\xcc8\xd0\xb3\x97af\x16\xc1\x06v?mj\x9c/%m_\xd9\x929a\
-\xcd\xb5\xfft\xde\x83\xfe1\xc9\xcd\xafr;\x03\x07\x9f\xf4<3\xd9\xaf8;8\xe3_\
-\x80\xea\xe0?\xdf\xbcu\xe2\xc4q\xdf\xf3\xb2\xdf~\xf6\xab\x87\xed_J\xdaq\xe5|\
-\x00\x00D\\[{\xd2\xe9f\xcb\xda\xba\x86\xa3G\x8e\xa4\x1e\xeal\xaf\xd3\x99^u9\
-\x15\xf7\xec\x17\xb7\xdb\xc1\xcc\x1aLYq\x1f\xff\xcb\xea\xbfKS\x81NM\x10\x99\
-\x8f\x93\xd9\xf6 \xa4W\xae~4\xa8\'\x143\xfe\xd5\xdd\xce[\xfbO\xd2\xb8\xab"\
-\xa3\xfa3\xf1\xa68\xc5\xa3c\xd7Z\xba\x83\xb2\x1bC\xc5F,\xccN\x9f&tK\xd2\'X\
-\xdcS\xc9@\xc4\x1b\xbf\xfe\xa7\xff\xff\xd3\xbfv\x1c\xe7;\xff\xea[33\xd3\x98Y\
-h\xcd\x1e\t\xdd;I=\xe1\xa0\x8c\xa6\xf4\xd6\xc9i\x10\xd9\xce\xe8\x8bIoh\xf5_u\
-Sf\xe7\x8c\xec4\x03\xdb\x1f\xf2\xf4\xbf#d=w\xa2\xec]\x9e\x13\x11\xd8\xee\x00\
-\xc9\x9e"7E\xa5\xef\xf7\xf7}\xb7\xdd\xef\x1f\x87\xb4e\xd8>\xfe\xc3\xda\x7f\
-\x96\x07\x95d\xe6We{fG\xbe\x7fv\xc9\r~\xfa\x02v)\xeb\xd9\x1b\xec\xfa\xf5_\
-\x7f\xfa\xd7\x7f}\xfc\xd8\xb1?\xfa\xc6\xff\xa5\xa2\xf4p;\xc3:\x0e\x00\xa9u\
-\x1f\x04\xcd\xbb\xf7\x96rIT\x08\xa1\x0b\x0b\xf3\x96e\xf5_[z\xe5\xfd\xb3\xd7S\
-\x0fl\xf6\x86\xe9\x9f\x93\xc6o0w\xa3\xa6_D\xce\x8a\x82\xbe\t/w\nu\x93?\xaf\
-\xde\xed\x0f"Z\x97q7\t\xf9\x08\xe7\xb8W99Nk2i\x88\xb0\x0e\x00D%m\x1f\xe2\xe1\
-1\xbd\x8a35\xb7\xed\x9d]\\\xf2A"\xf7\xd8,/\xaf\x00@\x1c\xc7\x0f\x1e><rd\x0ez\
-\x0f\x1e\xf6\xb1\xd5\x02\x00\xf4n\x14\x18.@\xb9\x17\xa3\xc9\x1e\x9f}6\x06\
-\xce\x1c\x90\xb9M\x9f\xe2,\xa4g0f5t\xc4Y\xb2\xff\xcc\x1d0\xb0\xef\xb0\xfb\
-\xee\xe7N\xaa\xfe\xb9c\xe3\xbbm\x7f\xc7\xf3\xa6\xc3\x02\x991\xc9\xa9\t\x8c\
-\x1c\x96q\xc8\xdeT\x0f\x1f=\xfa\xfc\xef\x7f!%.\xaf<\xf8\xff>\xfa\xf8_~\xf5\
-\xed\xe3\xc7\x8f\xa5\x87\r;\xbb\xfag*ya\x18.\xde\xf8u\xbb\xd3\xd9\xfe\x0b\
-\x9eT*\xa5\xda\xec\xcc@e\xcf\xbeHO\xf1,\x03;\xac\xcd\xfe?\x8d\xdf\xda\x8e7\
-\xea\xb0g!k\xa3<\x97\xde\xed\x1b,\xb8\xab^\x8c0\xdb\xbd\xf2\xfcx\xb5\x96\x90\
-\xf5~\x04\xe0\x0e\x1e\x9e\xfc\xc2\xec\xa4\x8a;\x00dm\x81\xdf~\xfdb\xbd\xbeV*\
-\x15/\x9c\xff\n\xdd\xbe\xe0\x83\xbdm\x84\x99\xd8\xb8\xbc\xd5\x00\xcf\xf5F\
-\x19\xf1\x84\x8c8\xf8\xa9O\x94>\x06\xbb=\xcb\xd3i\xf7\xf8\x17\xb6G\x8d\x8fy\
-\xf6=\x1d\xfc\xf47\x81\x94\xb2\xd9l}\xfa\xf3\xbf\t\xc3Py\xc9\xeb\xf5\xb5\x8f\
->\xfe\xe9\xef\xfe\xceo_8\x7f\xde\xf3\xdc\x11\xa7H\x1b\x11B<~\xbcz\xedW\xffP\
-\xaf\xe7*\x9b\xa3a\x98\xe7\xce\x9e\xf5}\x7f\x98\x95\xba\x17_\xe2sos\xcco$w\
-\xba\x89\xd0\xf1~DgU\x05,*-\x1e|\x10!N\xf1\xd8x\xad\xd5e\xbc\t\x00\x84\xc0\
-\x88d\x03N\xf1h\x7f:\xc9\x89\x14\xf7\xd4bJ\x17g\xaa\xd5\xca\x9f\xfe\x9b\x7f\
-\xad<q\xd8[hM!\x84(Y\xef\xad\xb0\x89\xb4\x95}\xb8\xda}\xb8/\'\xe8\xd6\xdfg\
-\xf6bd\xd2{\x8fs\xdel\xb6\xfe\xe6\xb3\xbf\xdd\xd8\xd8TgS\x07t:\xe1\x95\xbf\
-\xfd\xbb;w\xee\x9d:u\xf2\xec\x99\xd3\xd5jeD#\x0f\x1f>\xba}\xf7\xee\xed[\xb7;\
-a\xd4\x97f\x80\x1c=:w\xf2\x95\x85\x81\xfe\x8a\t\xe5\x10ta\x14(\x934\xb5\xef\
-\xa8P\xf4\xd9\xb1\xccv\x14\xe9\x8f\x00\xc4\xa1S\x051,\xbfz\xaa\xff\xfd\x89\
-\x14wEj:\xa5\x8b3\xc3\xee~\xf5 \xa9x\xf6\xf4x\xec\xbd\x7f\xc8\xef6\xcds%\xab\
-\xec\x8c\xb1_\xff\xd3?-\xaf\xacd\x1d\x08)\x8f\x1e?~\xf4\xf8\xf1?~\xb9xd\xaev\
-\xfc\xf8\xb1j\xb5\xa2b<\xd4\xad\x18E\xf1\x93\'OVV\x1e<Y\xdf\x88\xa2T\xd6\xb7\
-\xb5P*\x15_\xbfp\xfe\xe0G\x86hR\xb6\xa5\xf6\x1dZ\x91\x83\xfa\xd5\xd3c\xb5\
-\xd6z\x80<De\xb6\x8f\xceKc\x0eH\xbd9\xa9\xe2\x9e\xf5xJ)[\xad\xd6\xd2\xf2\n\
-\xa5\xf4\xf2\x1b\x97 \xf3\xd3/\x8e\xe3\xc5\xc5\x1b\x8fWW\xa3(\xb6,\xb3V\x9b=\
-u\xf2\x95\xee_1\xef\x0f\x8d\xe3x\xf1\xfa\x8dz}-\x8e\xe3R\xa947W;w\xf6\x8c\
-\xe3\x8cNX\xaay\xe9H\xc5=I\x92r\xa945U\xc8\xd4\xe8vd\x9b\xa7\x00\x00 \x00IDA\
-T\xe8\x1e\x92\xcat\xbb\xdd\xba}\xa7}\xe7\xee=JiV\x9c\x11sUSH\xcel\xf7<\xf7\
-\xb7/^(\x16\x8b\xe6n6\tk^ (9k-\xa9\xd7#R\xfb:\xc5\xa3\x86\xed\x0f\xfa\xcb\
-\xf6\xd6D\xc2\x9aK\xea\x9e\x90rh\xf1&\xc3\xf6\x87\xe5\xa5\x99Tq\xcf\xd1jw~y\
-\xf5\x1a\x00\\~\xe3R*\xd9K\xcb+\x1f\xff\xf4\x7f\xc7\xf1V-\x98{\xf7\x97n\xde\
-\xba}dN\xad)o-\xb1"\xe2\xe2\xf5\x1b\x9f\xff\xdd/2\x07\xaf,^\xbf\xf1\xcb\xab\
-\xd7\xbe\xfd\xff|sn\xae\xb6\xcf\xdd\xd1\x1cd\xd2_\x81I\x92LM\x15~\xe7\xf5\
-\x8b\xbf\xf9\xe7\x9bO\xd6\xd7{\x8b^9\x03\x9c\xa4\x1f\xc9\xb6\x91Q\xf3T\xd3\
-\xb7>55U\xb8x\xe1+\xb3\xb33\xd9\x1d=\xfb\xd17\xcd3\xc0[+\xc8#\xe8\xe6k\xdc\
-\x9d\x0b\xa5\x1f\xd6\xbc\x87\xd9\xa4\xedC\xc3\x1f\xcf\x0c\xcbK3\xa9\xe2\x9e[\
-a\xdf\n\x1c\xeb\x85\xd0\xae\xae\xd6?\xfe\xf8\x7f\xc5IR,N]\xbcp\xde\xf3<!\xc4\
-\xfd\xa5\xa5\xbbw\xefon6\xa0\xe7\x96Q\x07/-/\x7f\xfa\xf3\xbf\x01\x80\xdf{\
-\xf3\xf2\x99\xd3\'K\xa5R\xab\xdd\xfe\x87\x7f\xf8\xf2\xfa\x8d_\x7f\xf8??\xfe\
-\xb7\xff\xe6\xfb*\x01\x93F\x93]\xecQ\x9e\x19\xc3\xa0gN\x9f\x9c*\xf8\x8fW\xd7\
-z9\xdcw\x84\xe0V\x01o\xb2-\x8f\x0c!G\xe6j\xe7\xce\x9e.\x97\xcbin\x86C\xe3p?\
-\xc4\xa0d\xbc\xd5\xad\xd0;"G\x98[\x9e\xa7\xe6\xce\xf9\xce$k\xa5\x19\xc7\xc4\
-\xc8\r\xaevavX#\x93*\xee9\xd2YR\xf6\xb2\x88,\xde\xb8\x11\'\xc9\xcc\xf4\xf4\
-\x1f}\xe3kj\xfb\xb2\x10\xa2Z)\x97K\xa5_\xfd\xc3\x97\xb9\x83\x7f\xfe\xd7\x9f\
-\x01\xc0\x1f\xbe\xf5\xfb\xaf\x9e;K\x08a\x8cy\xae\xfb\xd5\xb7\xdf\x8a\xe2\xf8\
-\xf6\xed;\x8b\x8b7._\xbe\xa4\x1f-M\xd6\x8f\x97\xf5\n"\xe2\xf4t\xd5\xf7\xbd\
-\x8d\xcd`\xb3\xd1\x08\xc3\x81\x12\xdf\r\xbe\x85\xee\xffeBAz&|\xb9\\\x9e\xab\
-\xcd\x9e8~\xcc\xf3\xbc\\r\xca\xbd\xee\x9a\xe6\x19a\xc1}eh\xab\x1d\xa4\x03\
-\xbf0b:^y\xac\xd4\xbe\xacq\'-B0<\xfc\x91\x8e\xde\xe0:\xa9\x8e\xbc\xdc\x03\
-\x96\xfa.S{\xea7\xbf\xb9\t\x00*\xc1@6o\xd1\xc2\xfc\x89J\xa5\xac\x9aP\x07?z\
-\xfc\xb8\xd9lMM\x15N\x9d|\x851\xa6\x0eV\x9c:\xf9\n\x00,-\xaf\xbc\xa8nj\x0e\
-\x14\xd9\x90\xbetGejY;\x8es\xf4H\xed\xcc\xa9W\x16\xe6OT\xabe\'\x9f\x906\xbf^\
-\x9a\xe28\xce\xf4t\xf5\xf4\xa9\x93\xaf\xbdz\xf6\xc4\xf1c\xae\xeb\xba\xae\x9b\
-\xcb\x97\xa9\xf5\xfd \x83<\xe2\x9dLj\xdf!\x87\x15\xb6\xef \x1d\x86\x88\xd6E\
-\xaf~\xf2\x88Hy\xb7tl\xf4\x06\xd7\t\xb6\xdc\xd3\x18\x98l\xf6\x12\x15\x0f\xf3\
-\xe0Aw\xa0\x8f\x1e\x99K\xd3Y\xa4\xe9;\x8e\x1f;\xba\xb9\xd9\xc0\xde\x9eo\xb5\
-\x01*\x89\x93\xff\xfd\xc9\xa7i\xe3\x04\x08!\x84q\x06\x00\xc3\xc2!4/\'$\x93\
-\x8e-\xbd\xf7(\xa5\xea\xdes\x1c\xa7\xe683\xd3\x15!e\x14E\xedv\x18\xc51\xe7\\\
-J\x81\x12\xa4\xca\xe7N(\xa1\xc44\x0c\xd7u\xa6\n\x85B\xc1O\xb3\x1f\xab*\x02J\
-\xdc\x9f"?\x8c\xe6\x85\xc0\x82\xbb %\x10\x90\xc3w\x90\x1ava\xbc\xd4\xbe\xc8\
-\x1a\xb7\xbb\xafFF\xca\xbb\xe5\x1d\xca\xf2M\xb6\xb8+\xbd\xde\n]\x07`\x8ce\
-\x17\xafR#\xab?w\x15bw&\x10B\x02@\xc2\xd8\xeaj\x1d\x86\xa0#&5\neD\xab\xbc+*\
-\x92\x8ad\x12\xeb\xa7\xb9\xb1\xd4\x04`[V\xb9T\x1a\xdd\x1a\xcd\xa4\xa1V\t\xe5\
-]\xd7M\xbd\xed\xfa\xae;\xf8\x88h\x83\x87u\x00\x00\x1c\xb5\x83\xb4P\xfb-\x18\
-\xe3\xdbd\xc1=\xc9\xba\xc1W\x9c\xe3\xd0j\xda\xa5\x13;F\xcaO\xaa\xb8g\xb7\x08\
-\xa6Ie\x01@%%O\x0b\'EQT,\x16s\xca\xden\xab@\xd4nm\x04\xc3\xa0\x00P\x9b\x9d\
-\xf9\xea\xdbo\xa5\x8d\xab\xa7NYRi\xbe=\x8d\x06z\x96{\xf6u\x9a\xd56\x9b~y`\
-\xc6\xb4\xf4S\xa4\xaf\xc0@6\xa1\xff8\t\xe55\x07\x03d\xc1m\xb5~\xca\xf9\x88\
-\x1d\xa4\xc7\xfaw\x90\xf6#y\x877\xbb\xc1\x94R\x02\x17\x83[\xa3\xa6\xebO\x9f\
-\xda\xb1\xb5I\x15w\xc8\xe5\x8d\xe9e\x14PO\x94\xef{\x85\x82\xdfnwV\x1e<|\xfdb\
-5\xeb\xa0O\x92\xe4\xc1\xc3G\xea\xf3\xea!\x9c\x99\x9e\x06\x80\xfa\xda\x93\x8d\
-\xcd\xcdj\xa5\x92m\xbc\xd9l"b\x9a\x1bO\xa3\xc9\xba\xddS\xbb\xdb\xc8\xd4\x12H\
-30g\x93\xfd\xa6\xf7O\xff\x07\xd3l\xcc\xd9\x84\xfe\xbb\xca\xc4\xa9y\x81\xf0\
-\xce\xaaL\xda@F\x86?R\xd3\xab\xbc2Nk\xacqG\x15b\x04\x00&\x86\xfc\x08\x00\xf0\
-\xa7O\x8d\xe3\xbb\x9f`qW\xe4\x16TS#\xfd\xf4\xa9S_.^\xbf\xf6\xab\x7f4M\xf3\
-\xe2\x85\xf3\xea1k\xb6Z\x7f\xff\x8b\xab\xaaD=\x02(\xab\xdfq\xec\x85\xf9\x13K\
-\xcb+W\xfe\xf6\xef\x7f\xff\xcd7j\xb5nh\xd1\xe6f\xe3W\xff\xf8%!\xe4_\xfe\x8b\
-\xb7\x8f\x1d=\xaa=3\x1aE\xea\xebK\r\xf04\x8f|\xaa\xec\xb9\xdaXYq\xcf~*\x9b\
-\xc4<\x97\xd0\x1f\xf4\x1a\xcf\xc1\x07eZ\x1dI\x88\xe1.\x94\xf1r\x84\x89xS\x84\
-\xdd\xe4B#\xd6Q\xc7/\xcb7\xf1\xe2\xde\x8fR\xe1\xaf\xbc\xf6j\x10\x04\xf7\x97\
-\x96\x7f\xf1\xcb/\xfe\xe97\xff\\(\x14P\xca\xc7\xabu\x00(\x97K\x8dF\x00\x99\
-\xect\xaf\xfd\xd6\xb9\xa0\xd9l4\x82\xcf\xfe\xf6\xef*\x95\xb2*e\xa7\xb28\xcdL\
-O\x97J%mCir\xa8\xfb!\x9b\xc4_\xd9\xef\xd9<\xfe\xfd\xe9ma\xbbO&\x1bocLNiP\x8d\
-\x82\xb7\x1f"\x0f\x81\x00"\x0e\xdbAJL\'[@c8\xc8\x1aw\xba\xafF\xac\xa3\x02\
-\x14f\xce\x8d\xe3\xbb\x87C#\xee\xfdy\xec\x10\xf1\xed?\xfc\x83\x99\x99\xe9\
-\xc5\xeb\xbfn6[\xcdf\x0b\x00|\xdf?\xff\xda\xab\x9d0T\xe2\x9e\xa6\x99\xb6m\
-\xfb\x0f\xff\xe0\xf7n\xdd\xbe{\xef\xfe\x92\xda\xe2\x04\x00\xb6m\xbd\xb2\xb0\
-\xf0\x07\xbf\xff\xa6\xab3\x10h\x06\x91\x9a\xf0\xd0\x8b\x8c\xc4\xbe\xac\xe5\
-\xc3\xc4=+\xf1d7\t\xfd5\x07\x04\x94\x9c\xf5\xfc\xe3\x9c\x0f\xdfAZ=9\x8e\x0b\
-\x85\xb7\x1f\xcb$P\xafG\xfc\x08p\x8aGMw\xd4\x12}\x96\x9d\xb3\xc5\x1e@T<\x8c\
-\nHW\xff\xc5L&H\xf58\xa5!\r\x94\xd2\'\xeb\xeba\x18QB\x8a\xc5)\xec\x15\x12#\
-\xbd:\t\xb2W7U}\xa4\xbe\xf6\x84\x10\xa0\x84\x1e;v4[CUG\xa4iF0p\x7fS\xfape\
-\x9f\xb2\xfc\xe6\xeag\xc8)\xafy\x81\xb0\xe0.\x0b\xee\x03\x01\x94\x18%C\x13\
-\xbf\x94\xe7\x7f\x8f\x90\x1d\xb6\x13\xa1\xe4\xd1\xe3_\xa0\x88\x01\x00\x11"\
-\x86\x03\xa7\nB\xcd\xf2\xfc\xef\x8d\x97\x05\x1e`\xa2-\xf7\xac\xed\x03=\x1fhv\
-\xd3\xa0\x8ad\'\x84\x94\x8a\xc5\x82\xef\xab@\x9a\xf4G\xb4\xb2\xb3\x08!\xb9"\
-\x9fs\xb5Y\x9aA\xffL\xd6\x8cCV\x9d\x07j\xfa\xc0\xe3A\x0b\xfad\x82\x92\xf1\
-\xd6\x03\xf5\x9a\x89|\xa6\xe6\x14\xbfzzGe\x07\x95\x94Ft\xb3Zq1X\xd9\x01\xc0\
-\xab,\x8c\xaf\xec0\xb9\xe2\x9eU\xf6\xb4\x1c\xb3zN\xd2\xe0\xc8\xdcBk\xfa#:-\
-\xca\xac\xde\xa7\x94\xa6\xfa\x9e\xfb\xbd\xac\xf5]\xf3\x14h\xe1>\xf4\xb0\xe0>\
-J\x0e\x04\xa4\xc4\x11u\xefF$~IA\x11\xf3\xe6\xb2z-qx\xf8\xa3\xe5\xb9\xe5q|\
-\xf7\x99\x0b\xd8\xd5\xd1\x07\x87\xac\xb2g\xa3\x17\x00@9^\x94d\xa7?\x93\xd3\
-\xc0\xe4\xac\xb8\x13B\xd2\x0cPiTrj\xdako\x8cF\xa3\x19\x00JU\xd4\x14\x00\x18\
-\x1fa\xb6\x9f\x1ak\xd7R\xe3."\xef\xb564\xfc\xb10<\xfb\xe30&R\xdc\x95\xfb%U\
-\xf6\xac[\x06zR\xdeo\xbc\xa7\x06x\x1a\x9f\x00\x99\x99 -\xe2A2\xbbK\xf4.\x12\
-\x8dF\x93\x83wVQ$\xcal\x97r\xb0\xd9n\x17j\x967\xa0\x02W\x0e\x994y\xe7\xb1z="\
-\xfc\xd1\xf2\xa7\xed\xa9]\'\x1e\x9fHq\x87\x9e\x04C\xa6\x82h\xaa\xc2\xca\xe7N\
-3E\xd3\xd3\x8f\xa4.\x97tA5\x15\xf7t\xbf\t\xf4"\x1f\xd2\xc3\xb4\xbek4\x9a.(\
-\xd2 \x99\x91\xf9\x1a\xc7\xaa\xb5\x944n\xa79m\x87\xa7\x91\x19\xb7\xb5\x1c\
-\x93*\xee\xd0\xd3\xf7\xdc\xde?\x85\xb2\xc4\xb3\x11\x0b\xb9\xc8\x84\xac^\x0f\
-\x14\xf7\\t\x9aF\xa3\xd1\x00\x00k=P\xb1\xedB\xa0\x1c\x96\xafq\xbcZK"\\S\xc5\
-\xaf\x01\x80\x8f\xd8\x03U:f:\xc5\xa7\xb8\xd4I\x15we\xb0\x0f\x13\xdf\xac\x15\
-\x9f}3\xf7b\xd8\xc1::M\xa3\xd1\xf4\xb3U\x91cd\xb9jo\x9cZK(\x93t\xd7\x12\x02\
-\x13\x83\x8f"\x86\xedUO>\xcd\xb5N\xae\xb8\xc3N\xb2\xbb+Q\xd6\n\xae\xd1hv\x84\
-\xb7V\x94\xb7\x9d\x8b\xa1a\xae\xdex\xb5\x96x\xeb\x01\xf2N\xf7\xf5\xf0\xf0G\
-\xbf\xfa\n5vnm \x93Z\xacC\xa3\xd1h\xf6\x13\x14qj\xb6\xf3!f;5]w\xacZK\xc8z5\
-\xf9F\x84?\x1av\xc1)\x1d\x7f\xba\xab\x05-\xee\x1a\x8dF3\x0e\xacy\x1f\xa5\x00\
-\x022\x9fQb\x0bo\xbcd\x03"|\x82\xa2[\x8bQ\xad\xca\x0e\xa40sv\x9c=P\xc3\xd0\
-\xe2\xae\xd1h4; y\'\x8dYT\xb1\xed\xfd\x18\xce\x94[<\xbas[(\xd3\x1caB\x0e]\
-\x95\xb5\n\xb3\x96?\xfdtW\xab\xd0\xe2\xae\xd1h4;\xc0\x82{\xaa\x90\x9e\x10Cc\
-\xdb\xc7\xdc\xb5\xc4[\x0fd\xeam\xe7C\x0e"\xb40\xb2\xf8\xf58hq\xd7h4\x9aQ\xc8\
-$\xe8fZ\x1f^\x91\xc3\xf2\xa7\xc7J6 \x19k\xdeS\xafU\xf8\xe3@\xbc\xf2\xfc8\xc1\
-\x94\xa3\xd1\xe2\xae\xd1h4\xa3`\xc1\xfdn!=\x89"_\x8c\xb9\x8b?N\xf8#\x00\x0b\
-\xee\xa2\xe4\xa0j8\x0f\t\x7f\xa4\xa6\xe3U\xc7\xaa\xdc4\x1a-\xee\x1a\x8dF3\
-\x14\x11m\x88h\x1d\x94\x1c\xf3\xc1\x0e\x19\xa7xd\x9c4\xeb\x92\xb5y\xfb\x91z=\
-\xcal\xaf\x8eUEoG\xb4\xb8k4\x1a\xcdP\xba^\x14\xd2\xad\xa1\xd1\x0f\xa1\xc6X\
-\xbb\x96\x00X\xe3\x0e\xa0\xaa\xa9;\xaa\x8a\xdeX\xab\xb2c\xa0\xc5]\xa3\xd1h\
-\x06#\xc2\xba\x8c\x83\xb4\xfe\xf5\x10\xb3\xfd\x98ay;7\x15m\x88\xe8I\xf7\xb5\
-\x1c\x12LIHa\xe6\xec\x98U\xf4vD\x8b\xbbF\xa3\xd1\x0c\x04\xd3\xfa\xd7\xc3\xbc\
-(\xc4\xb0\xc7\xf3\x8f#\x0b\xb6\x92\r\x0c\xf3\xb6\xdb\x85\x9a\xe9\x96\x9f\xe6\
-J\x07\xa1\xc5]\xa3\xd1h\x06\xc0\xdb\x8f$k\xab\xfa\xd7\xc3\xccv\xaf2?Nz\x00\
-\xde~$\x93f\xf7\xf5P\xf7\x8eY\x989\xfb,\x17\x9cC\x8b\xbbF\xa3\xd1\xe4A\xc9X\
-\xb3k\xb63\x0e\x03\xbd(\x86]pK;\'\x1b@\x91\xb0\xe0\xaez="\xd9\x80W=IM\xe7)/w\
-\x10Z\xdc5\x1a\x8d&\x0f\x0b\xee"\x8f\xd5:\xea\xe0Bz\x84L\xd5^\x1b\xa7:R\xd2\
-\xb8\x85"\xe96;$\xd9\x80\xe5U\xbc\xca\xc23]q\x1fZ\xdc5\x1a\x8df\x1b\x92wx\
-\xfb1@w\xd7\xd2@9\xb6\xfd\xd9\xb1\xc2\x1f\xe3\x86\xe8\xac\x02\x00\x01\xe0bH\
-\x90\x0c!\xfesu\xc8(\xb4\xb8k4\x1a\xcd6XpO\xc5,\xf2a\xb9_\xc6\xad\x8e\x84i\
-\xd2v\tC\xe3m\xdc\xe2S\x96\xe3\x18\x8d\x16w\x8dF\xa3\xd9b+\xd9\x00\x0cM\xed\
-\xeb\x96\x8e\x8fUk\xa9S\x97I\x03\x94\xd9\xcea\xd8:\xea\x98a\xf2\xbbE\x8b\xbb\
-F\xa3\xd1l\xc1\x82{j\xfdt\xe8&RB\xdcq\xd2\xac\xa3Hz\xe1\x8frx\x98\xbcW=9Nq\
-\x8f\xa7@\x8b\xbbF\xa3\xd1t\x11\xf1\xa6\x886\x00\x00\x87{Q,\xaf:\x8e\xd9\xce\
-Z+\xc8{I\xdb\xc5\x90,\xc1\xb6?^q\x8f\xa7A\x8b\xbbF\xa3\xd1tI3\xad\x8f0\xdb\
-\xc7\xf1\xb6\xa3\x88ys\t\x00\x08\x80\x9408\xde\x06\xc0\x9f~\xa6r\x1c\xa3\xd1\
-\xe2\xae\xd1h4\x00\x00\xbc\xb3\xaa\xb6\x1a\xa9]K\x03\x8fq\xa6\x8e\x8c\xb3\
-\xf8\xc9\x82{\xdd\xec\x8f\x00lX\xbcMa\xd6.\xcc<\xcb\x05\x8fF\x8b\xbbF\xa3\
-\xd1\x00\x00*[\x1b\x00\xb8\x80\x81\x15\xab\xc7\xcc\x11&Y\x8bw\x1e\x81ZG\x95\
-\xf0l\xf16O\xcfsH,\xa9\xd1h4\x93N7\xd9\x00\x80\x1c\x9el\xc0)\x1d7,wT#\\p.dp\
-\x97\xa0\x04 8"\xde\xa6|\xc2\xb0\x0b\xcf\xe1\xba\x87\xa3\xc5]\xa3\xd1\xbc\
-\xf4\xa0H\x93\rp\x01\x08\x83\xccv\xc3\xf6+Cs\x84\t\xce\xe3(\x899!l\xcdN\x9e \
-\x10\x02\xc0\x04\xc8A\xbf\x00\x80\x90q\xf2\x16<#\xda-\xa3\xd1h^vXk\x05y\x0c\
-\x00\x12\x87$\x1b\x00\xf0*\x0b\xc4\xb0\x06~\\\n\xb9Y\xafGq\xe8{\xe8\xc3c@IF\
-\x86?\xda\x85\xda\xe8_\x00\xcf\x05-\xee\x1a\x8d\xe6\xa5\x06%\xe7\xad\x15\xf5\
-\x9a\xf1\xc11\x8b\xd4rG\xc4\xb6wZ\xad\xf5\xfa\xaa\x8c\xb8\xc16x\x14\x00\xa1\
-\xa0\xc2\x1f\x87\x14\xf7\xf0\x9f\xb9\xf8\xf58h\xb7\x8cF\xa3y\xa9a\xcd%\x14\
-\x0c\x00\x84D!\x07\xdb\xda\x85\xe9\xb3#r\x84!`\xb9:\x8b<\x8a7\x97\x08\xa1\
-\x04@\x0c\x0f\x7ft\xcb\x0b\xfb`\xb6\x83\xb6\xdc5\x1a\xcd\xcb\x0c\x8a\x98\xb7\
-\x1fB/fq\xe01\xa6[\xb2\xa7j#\x1a1\r\xd3-\xcd8f\x8b`\xac\xd6Q\x87\x85?R\xcb}\
-\xee\xd9\x1f\x87^\xd5\xfe\x9cF\xa3\xd1h\x0e ,\xb8\x07\x92wK\xa4\x0e\xa9k::f1\
-l\xb5\xe3\x04\x817l\\\x070\x08\x00\x178\xacD\xea\xe8_\x00\xcf\x17-\xee\x1a\
-\x8d\xe6%E\xf2\x0e\xef\xac\x82*}7$f\xd1.\xd4,\xaf:\xac\x858\x8a;\x9d\x18\x81\
-:r\x8d C0$\x02\x1bR\x8e\xc3\xf2\xaa\xa3\x7f\x01<_\xb4\xb8k4\x9a\x97\x14\xd6\
-\xb8\x0b(\x81\x80\xe08,f\xd1\xab\x9e\x1c\xfca\x84v\xab\xcd\x12&\xc1\xb0ih$u\
-\x04J\x080\x8eBH\xa2\xd6e\t\xd9\xca.@\x88?\xb3\x1f\xeb\xa8)Z\xdc5\x1a\xcd\
-\xcb\x88L\x02\x11=\x81n\xc5\xea!\xc9\x06\x8a\xc7Lgj\xc0\x1f\x10\xda\xadv\x14\
-\xc6\xc4\xb0\x0c*\xccd\x85 "\xa1j\x03\x94\xe5\x95-\xafBA\xb2\xb8\xc9\xa2@\
-\xe9\xbcS<\xba\x17I\xdbG\xa0\xc5]\xa3\xd1\xbc\x8c$\xc1]\x95d\x80\x0f1\xdb\t5\
-\xfd!f{\xb3\x11\xc41\xa3\xa6%\x05\xb7I\x83\x88\x06\x12\x83\x00\xb4#\xe9\x94^\
-)\x1d9\x0b@\x00c\x0fxg\xed~\xd8xDL\xdb\xaf\xeem\xb2\x81~t\xb4\x8cF\xa3y\xe9\
-\x10\xd1\x86\x8c6\x81\x8c2\xdb\xdd\xf2\xfc\xc0\x8a\xd5a\xbb\x13F\xcc0-!\xb8e\
-Q\x8b=\x02B\x08@\xc2\x91\t\x07\xd0\xdf\x9a)\x088~\x15\xd0\xf2\xab\xaf\xecQ\
-\xd2\xf6\x11h\xcb]\xa3\xd1\xbcl \x0b\xee\xaaW\x9c\xe3\xe0\x1ca\xa6\xe3U\xe6\
-\xfb?\xd9\x0e\x9aQ\x94\x98\xa6\xc19\xb7\x1d\xcfL\xee\x81\x0c\tP\x00h\xc7H\
-\xa9\xddn\x85^\xc2\x12\x01q\xc8\x00\x19\x01#\xa633\xa5\x01M\xed5\xdar\xd7h4/\
-\x17\xbcS\x97I\x13\x08H9\xd4l\xf7+\xaf\x10\x9a\xb7}[\x8df\x181BM)\xd1\xb2l\
-\x13\x03\xc2\xd6\x10\x08\x10\x888\xc4LJ\xe4\xa6c\xb5;a\x1cE\x12%\x97T\x80\
-\xe3\xcd\x9c\x88\xc2h\xef\xbb\x95G[\xee\x1a\x8d\xe6\xe5"M6\xc0\x05\x0e\xcc\
-\x11f\xd8\x05\xa7t,\xf7fs\xa3\x113i\x18\x94si\xdb\x14y\xcc\xc2\x15\x872\x00\
-\x8a\x08\xedP\x98\xa6e\xf93q"\xa2\x8du@\x04@\x02\x84PJ\x08mq6\x7f\xe64!\x03#\
-$\xf7\n-\xee\x1a\x8d\xe6%B&M\xc9Z\xb0C\x81\xa4\xd3\xb9\x02I\xc1F#N\xa4eR\t\
-\xc4\xb2@\xb2$\x0e7\x8a\xb4\t@\x81@\'\x92\x12\x8c\xf2\xec\xaba\x9b\xb7[\x1b\
-\x94\x1a@\x00\x94G\x1f\xa0[\x915a\x96\xb3\xafnw-\xee\x1a\x8d\xe6%\x825\x97\
-\x10\x91\x000>\xd8l7\xdd\xb2]\x98M\xff\x89R\x06\x9b\x01\x13\xc4\xa0\xc8$\xd8\
-&P\x14\x89\xe4\x164\x08\x05\x00\xc2\x85\x88\xb9Q\x9a9\x9b0\x82\x86e{\x9e\x14\
-R}\xb4\xd7\x04\x18\xa6aX\xfb-\xb6Z\xdc5\x1a\xcd\xcb\x02\x0f\xeb<\\#\x00|X\
-\x8e0j\x14f_\xdd:\x9e\xb1v\xb3\xc38\x02H\xa4\x86c\x12\x1e\x85\x1c\xc0\xb2\
-\x11\x92\x0e\x01@\x94\x9c\x94\xfc\xca\xd1(\x01!\x85\xe3\xfa\xaec\xb6:m!$H\
-\xd9m\x92\xd2\x99Z\x8d\xd2\xfd^\xe0\xd4\xe2\xae\xd1h^\x0eP\xb0\xc6]\x00\x18U\
- \xa9t<\xdd\xb5\x94Dq\xb3\xd1"\x86E\x08P\xc3\xb6\x0c\x19wZ\x86\xe5\x94\xa6+\
-\xebK\xbf,\x98\x08\x88`\xcdPz\xa4\x13K!\x98\xe3\x97l\xdb\x8c\xdb\x9b \x18\
-\xa1\xc4v=\x02`\xdb\xceT\xa9h\xdb\xfb\x1d\x07\tZ\xdc5\x1a\xcdK\x02k?\x92<$\
-\x00L\x0c\xd9\xb5d:i\xad\xa5\xa8\x13\x86a\x8c\x84\xa0\xe4\xd4\xb4M\xc2\xe3v\
-\xc7t\xddR\xb5\x1a\xb6\xd6\xa9\xe8\x10\x13\xd0=\x96\xc8J\x94\xa0\x14\xcc/V)\
-\xf0\xa4\x1dl>yD1\xa8\xce\xccU\x8e\x9c"\x94\xee\xf3"j\x16\x1d\n\xa9\xd1h\x0e\
-?(9k.\x03\x00\x02\x88\xa1\xe1\x8f\xddZKQ\'l\xb5"\x94\x80\x08\xd4\xb0lC\x84\
-\xed\x96\xe9\xb8\xe5\xe9i\x00X\x7f\xf4\xcf\x96\x89\xd2\x99O`\xb6\x13\x0b!\
-\xb8_\x9c\x06\x99\x88\xa8\xb3^\xbfo@\xb3\xe0\x19\xe5\xb9\xd3\xd40^\xa0\xb2\
-\x83\xb6\xdc5\x1a\xcd\xcb\x00o-\xa3\x88\x95\xb7}\xa0\xd9nX\xaeS:\x0e\x00a;l\
-\xb7BjP!\xa4i\x9a&\xe1\x9df\xdb\xf5\xfd\xd2\xf44\x00l\xae-\x9b\x18R\xef\x95\
-\x04*\xedNH\r\xcb\xf3\xa6PD\x98\x84O\x9e\xac\x984,8X\x9c\x997,\x7f\xdf\xbb\
-\x98G\x8b\xbbF\xa39\xe4\xa0d\xac\xf5\x00Fz\xdb\xbd\xe93\x84\xd0v\xb3\x1dE\
-\x8cR\x82HL\xd3\xb4M\xe8\xb4:n\xa1P\xaaV\x01@\xf0$X\xbb[,\x1d\x13\xc6L\xd8j\
-\x98N\xd12M\xc0\x84G\xed`\xf3\x81I\xc2\xa2\x07\xa6\xedy\xc3\xebh\xef\'Z\xdc5\
-\x1a\xcd!\x87\x05\xf7Qr\x00\x10C\xbc\xed\x96[r\xa6\xe6Z\x8dV\x14s\x02B\x12\
-\x93\x12\xb0\x0cl\x05\x81_,\x16+\x15u\xd8\xfa\xe3\xbb~\xf1\x88\xe1\xcct:M\
-\xd3\xab\x82\xe4\x06\xc1vc\xbd\xdd\\5HX\xf6)\x00\xf8\xd5\xd3\xfd[[_\x08\x07\
-\xe2"4\x1a\x8df\x8f\x90<d\xed\x87\x00\x08H\x06\'\x1b \xe0N\x9fk5Z\x9d01(\x02\
-5)J\x83@\xd8iOU*\x85b7Oo\x1c\xb5)1M\xaf\x9a\x84M\xc3.\xa1H\\\xc7j\xae?\x0e\
-\xdbk&\r\xcb\xbe\x81\x08\xa6[v\x8aG\xf6\xb5{\xc3\xd1\xe2\xae\xd1h\x0e3,\xb8\
-\x07R\x00\x19\xe6m\x97V\xe1x\x1ca\xcc\x84a\x10 &%\x92\x00&Qg\xaa2\xed\x15\
-\xba\xaesD\x8c\xda\x1d\xcb\xadD\x9d\x80\xd8\x15@\xee\xdafc\xedA\x1c\xae[F\\\
-\xf4\x0c\xc4\x17P\x8ec4Z\xdc5\x1a\xcd\xa1E\xb2\x16\xef\xd4ahE\x0e\x04bJZ\x91\
-\x92\x12\xc2\t5(H\x90"\x89\xda\xe5\xd99\xc7s\xbb\x07I\x99D\xb1\x14$J"\xcb\
-\xa9H\xc1\x1d\x8bl\xd4W\x92\xe8\x89ks\xdf\xa1*\xcb\x8035g\xb9\xe5\xfd\xec\
-\xddh\xb4\xb8k4\x9aCK\xd2\xb8\x07\x80@\x88\x18\x98\xda\x17%\xda5I\n(\x05\xa5\
-\x06\xa5(9\xe3I4}\xf4\x98iY\xea\x10)e\xd8\xea\x84\x9d&\xe3\xc4\xb4\\\x94\xc2\
-s\xcc\xc6\xdaJ\x14\xd6\xa7<tM\xda-\xa8GM\x7f\xfa\x00\x99\xed\xa0\xc5]\xa3\
-\xd1\x1cVD\xbc\xd9+\xa4\x87\x03\xcdv\xa4\x8e\xe9\x1d\x13\x80\x84\x10\xc3\x00\
-\x91\xc4\x9c%\xd3G\x8f\x1b\x86\xd1m\x81\xf3\xa8\x1dFaK\xa0i\x98\x065\x0c\x93\
-\xca\xcd\xb5\x87a\xa7^\xf6\xd12\xb6&\x0b\xaf\xb20\xb0\xb2\xc7\x0bDob\xd2h4\
-\x87\x13\x16\xdc\x03\x00\x02\xc0\x05\x0c\xca\x11\x86\x86w\x84\x83\rRX\x16\
-\x15I\xc8YR;~bK\xd9\x19\x8f\xda\x91\x94L\x10\x07\x91X\xb6E\x91\xb7\xd7\x1f\
-\x85\xed\xc7EWf\x95\x9dZ\x9e[Y\xd8\xafn\x8d\x8b\xb6\xdc5\x1a\xcd!D\x84k"n\
-\x00\x80\xc4\x81\xa9}\x11\x89+\x8d\x19\x90\xdcv,\x1e\xb58\x17\xb5\x13[\xf5\
-\x92$\x17Q\'\xe4\x8c\t0\xa4H<\xcf\xe3q;j\xae\xb5\xda\xf5\x92\x8f\xb6\xb1\xad\
-\xbd\xc2\xcc\xb9\\\x8a\xe0\x83\x80\x16w\x8dFs\xf8\xc0$\xb8\xaf^\r\xac\xc8\
-\x81\x88\xc4;\n\xd4pL\x83\x85\x81DR;q"\xfd+OX\x14F\x9cq\tF\xc2\x98\xefyq\xd4\
-\x8a\x9b\xab\xed\xce\x93\xb2\x0f\xd6ve\xb7\xbc\xaa]\x98\xd9\xf3\x0e\xed\x1e-\
-\xee\x1a\x8d\xe6\xb0\xc1\xdb\x8f%k\x11\x001\xc8lG\x94\xc4*\x12\xbbj\x1a4\xe9\
-lR\xcb\x99\xad\xd5\xd2\xbf\xb2(N\x92\x841.\x91r\xc1]\xc7\x8e;A\xd4\\M\xd8f\
-\xc5\x07s\xbb\xb2\x03!\xfe\xcc\xd9\xfd\xe8\xd2\xee\xd1\xe2\xae\xd1h\x0e\x17(\
-Ys\tz\xc9\x06\x06x\xdb\t!\xce\x11\x83\x1a,l\x18\xb6[\x99\xdd*\xcd\xc1\xe2\
-\xb8\xddj!1\x04\x12D\xe99\x16\x0b[\xed\xe0\x91\xe0\xc1\x94\x8b\x96Ap\xfb\xba\
-l6E\xf0AC\x8b\xbbF\xa39T\xb0\xd6\x03\x95\xdaW\x0c\xae\xc8!\xa9U1\xac2\x0f\
-\x03\xcb\xef&\x8dQ\x84\xadN\x12E\x02\r)%%\xd40\x08\x0b\x9b\x9b\xeb\x0fQ\xb6\
-\x8a\xde\x00e\'\x86\xe5UO\xee}\x87\x9e\x92\x03\xb7\x08\xa0\xd1h4O\rJ\xc6Z\
-\xdd\xd4\xbe\x8c\x0f8@"!N-\t\x9b\xceTiK\xd9\x11\x92(N\xa2\x88#\xe5\\\x98\x86\
-I@\xca\xa8\xf9\xa4\xbe\x1c%\xad)\x17\xfa\x95\x1d\x00\xfc\xea)j\xbc\x80*\x1cc\
-\xa2-w\x8dFsx`M\x95\xda\x97p\x81\x12\xf3f\xbbD\xe4\xe0\x13N\n\x95\x8a?\xd5s\
-\xa7 F\x9d(\x8a"\t\x06\xe7\xdc\xb6-\x94\\F\xcdf\xf0\xb0\x13\x87\xd3S\xc46\
-\xa1_\xd9\rg\xca)\x1d\xdb\x87\x1e=5Z\xdc5\x1a\xcd!\x01E\xc2\xdb\x0fa\xe0\xae\
-%\x02RB\x98\xa0\xe5NMM\xcf\xb8\x9e\xd7\xfd\x08B\xdc\xeeDq\xcc\x05\xe5\x82;\
-\xb6)\x05\xc3\xb8\xc9\xa2\x87\xed(\xf2l\xe2;\x03\x94\x1d\x00\n\xd3g\x0e`\xf8\
-c\x16-\xee\x1a\x8d\xe6\x90\xc0\x9a\xf7Q0 \xc0%l\xcb\x11F@H\x88\x12D\xe2NM\
-\xcfo)\xbb\xc4\xa8\xdd\x89\xe3X \x15R:\xb6\x89R\xc8\xb8)\xa2\xc7(#\xcb$S\xee\
-\x80\xfc\xc0\x00`\x17j\x96?\xbd/}zz\xb4\xb8k4\x9a\xc3\x80\xe4\x1d\xd6~\x04\
-\xa4/G\x18\x01! b\x88\x08\xb3\xf3\xe7\x0b\xa5nn/)e\xdcnG1\x13\x12\xa4\x94&\
-\x05\x14\x9c\x88\x90B\x13H\x87Ib\x19`\x1b\x83\xcf\xe5U\x0fD9\x8e\xd1\x1c\xe8\
-\x9f\x15\x1a\x8dF3&,\xb8\x07(\x00@\x08\xd8\xca\x11F\x80\x0b\x08\x19"\x82_\
-\x9c)\x94\xba+\xa8R\x88N#\x88b\xc1%A\xa0\x86\x01\x049\x05\xe6\x16\\\xc9\x1a\
-\x96I\x85\x04\xdb\x00c\x90@\x9an\xd9\xb4\x0fh\xf8c\x16-\xee\x1a\x8df\xe2\x91\
-I\x93w\xd6\x00\x08"p\xb9e\xb63\x0e\x11C@ \x94\x94k\xa7\xbb\x07\x0b\x19\xb5ZL\
-\x12\xc6%\x01B\x89$R\x10\x10\xa5\xe9\xe9\xa4\xb3\xea\x9a\x02\x11\x84\x04\xd7\
-\x1e\xe4mWI\xdb_h\xe5\xeb1\xd1\xe2\xae\xd1h&\x9e$\xb8\x07\x80\xa0r\x84\xf5\
-\xcc\xf6D@\xccQ\xe5\x0c\xf3\x8bs\xb6W\x04\x00\x14"l\xb5\xa3D&\x8c\x9b\x86A@\
-\x10\xc1\x00dy\xb6\xc6Y\x08q\xdd4H\x98\x80k\r\x16pg\xea\xc8\x81J\xda>\x02\
-\xeds\xd7h4\x93\x8d\x88\x9e\x88h\x1d\x00\x101M6\x90pHT-l\x02\x94\x9a\x95\xb9\
-3\x00 8\x0f\x9b-&\x80\t4\r\x03@\x12\xc9,\x8bLUg\x81\x90h\xfd7\xb6I\xd4_\x0b\
-\x8e\x9a,\xb6A\x0c\xdb\x9f>\xbd\xaf}{\x06\xb4\xb8k4\x9a\x89\x06\x93\xcd[\xea\
-\x95\xda\xb5\x84\x00Q\x02"\r\x97A\xa8\x1c9k\xda\xaeH\x92v\xab\x13s\x90B8\xb6\
-)\x04c\x9dV\xb5V\xb5\x0be\x00H6o\x99\x10\x02@\'F\xd7\x1a\x1c$3U\xfb\xad\x83\
-\x96\xb4}\x04Z\xdc5\x1a\xcdD\xc2\x19\x8f\x19B\xbcJX\x9b\x10\x83\x0b\x94\x12%\
-B\xc4@f\x02!-\xc7/V\x8fK\xa5\xec\x0c\xa5\x94\x96e\x08\xc1\x08\xebL\x1f\x99\
-\xb1\xbc"\x00H\xde\x11\xed\x87\x00 $\x18\x04\xe8 i\xb7\xbc\xaa]\x98\x1d\xf0\
-\x87\x83\x8a\x16w\x8dF3a b\xb0\xd9\xe0`ZT\xd2\xce\x12\x10\n\x00\xa2\xab\xec(\
-e\x9a*\x8c\xa0\x14\xb3\xc7\xcf\x8b8j\xb7\xa3\x98\xa1\x90\xc26\r\x94\x02X\xa7\
-R\x9b\xa5v\xb7\xfe5\x0f\xee!JBT\x0c\xe5\xa0=K\x07\xac\xf8\xf58\xe8\x05U\x8dF\
-3al\xae=\xd9\xac\xaf9&\xf5\xc8:\x91!\x01"\x042\x0ea\x92Uv@)\xa6\xcaG\x08\x90\
-\xa0\x19\xc6\x1c\x84\x94\x8ee"J"\xc2J\xad\x96*\xbb\x887\xd3"\xda\x88\x83\xd7\
-Q\xdd\xd2q\xd3)\xeeS\xf7\x9e\x13Z\xdc5\x1a\xcd\x84\xd1i\xb7\x81K\x8c[\xbc\
-\xfd\x08\x80"@\xccPmS\xca(;\x1a\xa6\xe5\x17\xe7\xda\x11p\x01(\xa5cQ)\xb9\x88\
-\x9a\x95\xd9Y\xc3\xf6z\x8d!k\xdc\x01\x00B@\xe2\xc0,\x92\x07=\xfb\xe30\xb4\
-\xb8k4\x9a\t\xc3/\x95\xc04\x93\xe6}\x14\x11\xa1$f\xd8Ir\xca.M\xd3\xa8\xce\
-\x9dK\x84\xc3\x18\x07\x00\xdb\xa2\x823L\xc2\xea\xdc\x1c\xb5\xdc\xb4)\xdeY\
-\x95I\x13\x00\x10\x81\xf1A\x0e\x19\x00\xbfz\xf2 g\x7f\x1c\x86\xf6\xb9k4\x9a\
-\t\xa3\\.\xf1\xce\xa6-\xdb\x00THhG\x98u\xa7\xa0\x94\x96\xe5\x94f\xcf&\xd2\
-\xe7\\PJL\x83\xf0$F\x11\xcf\x1c\xd9\xa6\xec(9\x0b\xee\x01 !\x84\t\x14r@\x90\
-\x8ca\x17\x9c\xd2\xf1}\xea\xd8sE\x8b\xbbF\xa3\x990\xa8a\x14\xbdX\x84\x00\x04\
-\xc2H\n\x89Ye\xb7\x1d\xbf<{\xb6\xc3,\x004\x0cJ\x08J\x96\xa0d3G\x8f\xe4\x02\
-\x19yk\x19y\x08*\x15\xf0\x10\xb3\xbd0s\xf6\x80g\x7f\x1c\x86\x16w\x8dF3a\xc8\
-\xa4)\xc2:\x01\xc29F\xc96ew\xbdbq\xfaL\xc4\x0c)\xa4e\x99\x94"\x8f"\xd3\xc0Rm\
-\x8e\x9a\xdb\\+(b\xd6ZQ\xaf\xb9\xd8\x9eE\xb2\x87U\x98=\xf8\xd9\x1f\x87\xa1\
-\xc5]\xa3\xd1L\x18I\xe3.\xa0\x04\x02\x9d\x18\xa5\x94\x00@\x08E\x94\xae_\xf6\
-\xcb\';\x89!\xa54\r\n x\x18\x19&\x94ks\x84\xe6\xb5.i\xdc\x01\xc9\x01\x00\x11\
-\x06\x9b\xed\x84\x16&g?j?Z\xdc5\x1a\xcd$!\xa2\r\x11\xad\x03\x81\x84c\xc2\xc1\
-v\xa7\x00\x80\xb3\xd0\xf7g\xbc\xd2+\x9d\x98 \n\xdb\xb6\x00%\x8f:\x8ek\x17\
-\xa7g\t\xcd\xfbUd\xd2\x14\xe1\xaaz\xcd\x04\x0c(\xa2\r\xe0\x95\xe7\r\xbb\xb0\
-\xe7\xfd\xd93\xb4\xb8k4\x9aI\x82\xb7\x1f\x01\x00 rt\xe7N~\xc5\xf1K\x92\x85I\
-\xd8N\x18\xed\xc4\x84\x80\xb4\x1d\x0b\xa5\xe0a\xc7\x9br\x0b\x95\x192(p=i\xdc\
-V)\x1f\xa5\x844\x1dM\x16b:^ea\xaf\xfb\xb2\xa7L\xe4B\x81F\xa3y9A\x1e\xf1h\x03\
-\x080A\x98\xa8\x18v\x89\x10\x8a`p\xe9\xc6\xdc\x00\x90\x06E\x94\x82\x85-\xbf\
-\xe8OU\x07+\xbb\x08\xd7d\xdcP\xc6:\x13\x03\x8b\xe8\x81_=E\x0ck\x8f{\xb3\xb7h\
-\xcb]\xa3\xd1L\x0cIp\x1f$C\xc0(1\x98$Rpi\xd8\x8c\xa1@b\x9a\x00\x12\x11\x08o7\
-\xa7\xa6+~qhb^\xd6\\\x02\xe8\xd6\xf1\x18\x18\xfeh\xba%\xb7xt\x0f\xbb\xb1/hq\
-\xd7h4\x93\x81d-\xdey\x0c\x04\x92\x04b.\t\x91,a\x89 I$\x01\x89I a\x8c \xab\
-\x1e\x99\xb1\xdc\xa1\x95\x92d\xd2\x94\xac\x05#\xd6Q\x01\xfc\xe9\xc9(\xc71\
-\x1a-\xee\x1a\x8df2H\x82\xfb\x80\x12\x01:\t\xa2\x04\xbb\xe8%\x9c#\x17\x02%\
-\x01\x90\xd4\xa0\x14*\xd3\xb3\x86\xe3\x8fj\xa4qG\x95\xf3\xe0\x1c\x07\x86?:\
-\xc5#\x96W\xd9\xbb^\xec\x1bZ\xdc5\x1a\xcd\x04 \xe2@t\xd6\x80@\x9c \x17(\xc1n\
-\xb7\xc28f\x00@\t\x01\x00B\r@Q\x99\x9d\x19\xd5H\xb8&\x93\xcd\x01E\xb4{\x10jz\
-\xd5\t\x0e\x7f\xcc\xa2\xc5]\xa3\xd1L\x00,\xb8\x87\x80\x04\x81\x0bp-`\xe8D\
-\x9cp\xc6\x00\x08\x00\x02\x02\x02 "\x8f\xda\x865$\x0f\x0c\x8a$\xb8\x03\x00\
-\x04 \xe1\x88\x83\xccv\xb7\xb2`d\xf2\x13L4Z\xdc5\x1a\xcdA\x87\x87Ox\xb4NT1\r\
-\n\x14\x88A\x84\xb4\x1c\xc1\x85Rv\x00D$\xa6IL\xc7\x1b\xdaH\xfb\xa1J6 \x86\
-\x84?R\xcb\xf3\xca\'\xf6\xb0\x1b\xfb\x8b\x16w\x8dFs\xc0A\x16\xdc\x07\x00\x04\
-\x10\x12\x01\x00\x81P\xec\x14\xdd\x16\xf7\xa7\xe3XJ)\x00\xc0\xa0dv\xa6l\xd8\
-\x83\xedn\x14I7H\x06\x80q\x1c\xb8k\xc9\x9f>\xdd\xbf\x91ur9<=\xd1h4\x87\x12\
-\xdeY\x15I\xa0\xcc\xf64r\x11\x81@\xb4\xe6\xb8\xb1\xed\x14,\xff\x88a\x9a\x96\
-\xed\x1a\xe6\xd0\xc4\xbc\xac\xb9\x84\x92\x01\x00"\xc8AI\xdbM\xaf\xe2L\xcd\
-\xedY\'^\x00Z\xdc5\x1a\xcd\xc1\x05Q$=\xb3=\xbb\x04J\x00\x00\x08\x0b\x9b\x86\
-\xc5\nG\xbf2\xc8\x10O\x9b\x10\xc8\x1a\xbc\xf5@\x1d2\xd8l\'\xa40s\xf6\xf9_\
-\xfd\x0bE\x8b\xbbF\xa39\xb8\xf0\xf6c\xc9:\x04\x80\xcb\x01\x89\x1bI\xb7\xb4\
-\xe9 e\x97\x11\xf0\x16\xf2f\x126d\xd2V\x95Q\xa5\xc4\x81\xdev\xa7xl\xe2\xaa\
-\xe8\xed\x88\x16w\x8dFs@A\xc9\x93`\t\x94\xb7}P\xe4\xa2\xe1\x96\x9d\xa9#\x99\
-\x0f\x08\x90!\xf0\xa6H\x828lFa\x12\xc7\x9c\x0bY\x9d"\x94\x12\xc0\xc1f;1,\x7f\
-\xfa\xd4^\xf6\xe3\xc5\xa0\xc5]\xa3\xd1\x1cPXk\x05ED\x86\xe5[\'\xa4X{\x15\x00\
-@\xc6 \xdb\x984X\xdc\x8a:\xed8\xe6I"\xb8\x90\x88\x80\x00\x96A(%\x00\xc0\xe5\
-\xe0ZK^\xe5\x95I\xac\xa2\xb7#Z\xdc5\x1a\xcdA\x04\x05c\xcd\x07\xa0\xf2\x04\
-\xc8\x01f\xbb\xedM\x89$\x88\x9bK \xdaQ\x98\xc4\t\xe7L\xaap\x1a @\x00(\x05&\
-\xc04\t\x81\xee\xae\xa5AU\xf4|\xf7\x10\x85?f\xd1\xe2\xae\xd1h\x0e"I\xe3\x0e\
-\xca\x04\x00\x98\xec\xdbpD\x80\x0bx\xfc\xa8\x15/\xfd\xa6\xe2\x83AQ\xca\xae\
-\xa0g+\xe2I\x04!\xc11\x11\x10"\x06q\x02\x84\x82A\x00\x00\x10\x81R \x84\x16f_\
-\x9d\xd0*z;\xa2\xc5]\xa3\xd1\x1c8D\xdc`\xedG\x00 %H\xd1\xe7%\x07Xo\xe1Z\x83\
-\xcf\x96\xa8A\t\xe26MOw5%\x82X\x16\xf5|\xdb4\r\xb4\x90\x18\xdc\xa0@\xa4P\xda\
-\x1e3\x191R\xb1&\xb8\x1c\xc7h\xb4\xb8k4\x9a\x83\x06&\x8d\xbb\xdd\xf4^"\xbf\
-\x04J\x08D\x0c6[\xd2wh\xc1%\x08\x90\x86\xbe#\x02\x00\x12J-\xd3p\x1c\xd3\xb6\
-\r\xc7\xa2\x04E\x1c1\xce\x05H\xc9\x19"\x10B\xc00\xa4c\x92\xa0\xc3\x19\x8bGD\
-\xc7O4Z\xdc5\x1a\xcd\x81A4A6y\xb8!\xa2M  \x06\x85?\x02\xc0zS\n\t\xd3\x1e\xa1\
-\x04$ve\xdd0\xa9\xe3\x98\x8em\xda\xb6a\x10\x94R\xf0$\xe9DBH\x04D \x04\x89\
-\x8d\x04(2@\xc98\x98\x14\x0b.\x11,\x06\xef\xb0\x05A*\xb4\xb8k4\x9a\x03\x80dl\
-\xe3\xd7\xedN\x10\'hSn\x99\x04p@\xf8#!\xd0\x89\xb1\xd1A\xdf%\xae\rB\x80D\xb0\
-mZ\xa9\xf8\xb6I@J\xcey\x12&\x82\x0b)\xd5\x07\x80\x00Jb\x81?o8\x15!\x90b\x88\
-\xed;D&\x02\x89A\x80\xb3x\xff\xfb\xba?hq\xd7h4/\x1cl\xd5\xafonlp\x8e\x08\xe0\
-L\xd1\xc1\xbb\x96\x08\x00\xc0z\x0b\x01`\xca!\x84\x10\xcb&\x96A,\x93"\x8b\xdb\
-\x1de\xa4\xf7v\xafn}\x92\xb4\xa2\x82L\x98a\xac\x11B$\x12\x0b=\xcfL\xd4\x91<\
-\t\xf7\xb3\x9f\xfb\x89\x16w\x8dF\xf3\x82I\xc2\xc6\xe6\xc6\x86\xb2\xd3)%\xa6\
-\x01\x88 T\x06\x98^p\x8b\x94 $\nI\n.\x9d-\x13\xc7\xa2\x94\x02A\x94\x88\x82s\
-\xce\xba\xd12\xfdN\x1c\x04\xf0\xa7\xdcvL9\xe7\x00\x08\xc4\xf0lC\xfd\x89\x02p\
-\x16\xedcG\xf7\x15-\xee\x1a\x8d\xe6\x05\xc3\x92D\xc52r\x01\xbe\x05\x04\x80KD\
-\x04\x89 \x04\x08\t\x08@\x081\rb\xdbd\x8a\x02"J)$\x07\xe5\xb5!0\xba(\x1e\xda\
-$\xb0\xcas1/H\x89\x16IL\xdeB\x01\x00@9\x1e!C\x00\x00\x0c\x95IDAT\x08H\x1e#\
-\xe2\xc0:\xda\x93\x8e\x16w\x8dF\xf3\x82\xb1\xdd"\x10\x82\x12\xb9@\xc7$\xc40\
-\x12\x86Q\xc2\t!\x06%\xb6E\x0c\n\x84 J@)\xb9\x04\xe8\x19\xe8\xe3H2\x01"\x92\
-\x0e\x15K\x9e\xe5\x02\x10\xe4\x91\x14<\xfd(\x8a\x04Q\x12b\xecU\xdf^\x1c\x873\
-z\xff%!\x08\x82\xa5\xe5\x95 \x08\xc6? \x8e\xe3\xfe\x8f,-\xaf\xac\xd6\xeb{x\
-\xa1\xbbg\xb5^\xdf\xedU-^\xbf1b(F\x7f\xe4\xea\x17\xd7vw}\xcf\xe3\xec\x07\x87\
- \x08\x16\xaf\xdfx\x81\x17`9\x9e\xed\xcf\x10Bff\n\xe5\xd9\xb2](x\xae\xe9\xd9\
-\xd4w\x88c\x01%\x12\xa5\x14\x02\x11\xbbq\x8f\xbb5\xb3\t!RJ\x1e\xb5y\xdc\x12B\
-lk\x00\x85\x8a\xa0<|h\xcb}"\x89\xe3\xf8\xc3\x8f>v\x1cgn\xaev\xf3\xe6\xad \
-\x08\xbe\xfd\xad?v\x1c\'{\xcc\xe2\xf5\x1b?\xfb\xe4\xd3\x85\xf9\x13\xab\xf5\
-\xb57/_\xba\xfc\xc6\xa5 \x08~\xfc\xee{s\xb5\xd9\xd5\xfa\xda7\xbe\xfe\xb5sg\
-\xcf\x00\xc0_\xbe\xfb\x1e\x00Dq|\xee\xec\x99\xaf\xbe\xfd\x16\x00\xfc\xd9\x9f\
-\xffE\xda\xc8\xf7\xde\xf9\xee\xc2|~sv\x10\x04?\xf8\xe1\x8f\x06\xfe\xa9\x9f\
-\x9b\xb7n_\xbdz\xed\xdf\xfe\xe9\xf7\xc7\xec\xdaj\xbd\xfe\xe1G?u\x1d\xc7q\xec\
-8N\xa28\xfe\x93w\xbeS*\x95\xb2\xc7\xfc\xe5\xbb\xef]\xbe|I]\xff\xd6\x89n\xde*\
-\x95J\xb9#G_v\xfa\x91\xcf\xae|~\xf9\x8dK\xd9\x83\xd3Ap\x1cG\x8d\xde\xe8\x8e\
-\xecx\xf6a\xf4\x9fh\xb7-<;\x8d\xa0y\xf3\xe6\xad\x8b\x17\xce\xef\xff\xa9S\xaa\
-\xd33\t}bZ\xb2\x13\xb49\xe3\x88H\x00P\x02\xf6<.\xcf\xe87!0\xd8wc\x98\xc6h\
-\x9f\xce\xe4\xa2\xc5}\xf2P\xca~\xf9\xf2\x1b\xf5z}iiyaa\xfe\xe2\xc5\xf3\x1f~\
-\xf4qN\xdf\x97\x96\x96\xbf\xfd\xad?>w\xf6\x8c\xd2\xf4\xcbo\\\xfar\xf1\x86\
-\x92\x8f\xa5\xe5\x95+W>?w\xf6\xcc\xcd[\xb7\x1d\xc7\xfe\xde;\xdf\x8d\xe3\xf8\
-\x07?\xfc\xd1\x9b\x97/\xa9\x16\xfe\xeb\x7f\xf9\xcf#.\xe0\x97W\xaf\x95J\xa5\
-\xc5\xc5\xeb#\xc4\xfd\xfd\x0f\xfe\xea{\xef|\x17\x00\xc6\x99\x00R\x82 x\xff\
-\x83\x9f\xa4\x13\x0f\x00,-\xaf\xf4+\xe6\xe5\xcb\x97v\xd5\xec\xf8\x97\x9dE\rB\
-\x10\x04\xff\xe3\x83\x9f\x9c;{\xe6)\xce\x98\x0e\xc2\xaeN\xf4\x143\xc4\xa4\
-\x83<L\x1aw\x12\x86q\x1c\x01\x00!\x80\xd4\x91H(&\x04\xe43\x0b\xfb\xf0\xf3\
-\x02\x98\x86A\x0fi\xfa\x81\xc3\xd9\xab\xc3\xcd\xe2\xf5\x1b\x0b\x0b\xf3KK\xcb\
-\x8dFp\xf9\xf2\x1b\x8dF\xb0\xbc\xbcr\xee\xdc\xd9\xdc/k\xa5\xec\xd9wn\xde\xba\
-\xad\xdeY\x98?\x11\xc5q\x10\x04KK\xcb\xe7\xce\x9d\x05\x00\xc7q\x94\xd6\xab#W\
-\xeb\xf5aN\x06\xe5\xd8\xf9\x93w\xbes\xf3\xd6\xed\xec1K\xcb+W\xbf\xb8\xb6x\
-\xfd\x86:@\xfdO\x1dP*\x15\xd5\x9b\xd9\x83\xe38V\x97t\xf5\x8bk\xe9\x9f\xd4\
-\x15f/;\x95TuIW\xbf\xb8\x16\x04A\xa9\xb4\xb5\xebd\xf1\xfa\x8dl\x0b\xc3\x18v\
-\xd9;R*\x95\\\xc7i\x04M\xd5\x91\\g\x83 H[\x8b\xe3\xf8\xea\x17\xd7\xae~q-\x8e\
-\xe3\xfeAP=MGx\xf4\x89\xe28V\xfdJ\x1b_\xad\xd7\xd3\xe1M\xc7*\xdb\xf1t|\x94G+\
-m6\x1d\xea\xb4\x85\xf4O\xeaxu\xc1\xe3\x0f\xc8^\xd0\xdax\x10E\t \x10B\x10\x88\
-\xb0\x8f\x91\xd2W\xd4\xff\xc0,\xe1\xde\xb9M\x10\x00\xb9T\xab\xab\x87\x0e-\
-\xee\x93\xc7\xd2\xd2\xb2\xf2\x12|\xe3\xeb_[\x98?\xf1\xd5\xb7\xdf\x9a\x9f?q\
-\xf1\xc2\xf9\xa5\xa5\xe5\x81\xc7\xff\xec\x93O\xdf\xbc|\t\x00\x82 H\xad\xc2r\
-\xa9\xd8\x08\x9a\xdb\xde)\x97\xd4C\xee8\xce\x95+\x9f\xff\x8f\x0f~\xf2\xfe\
-\x07\x7f\xd5\xdf\xda\xe2\xf5\x1b\x0b\xf3\'J\xa5\xd2\xc5\x0b\xe7\xbf\\\xec*\
-\xc5\xcd[\xb7\xaf\\\xf9\\]\xdb\xe2\xf5\x1bW\xaf~\x01\x00W\xaf~\xb1\xb4\xbc\
-\xb2Z_\xbbr\xe5s\xc7q~\xf6\xc9\xa7Jt\x82 \xf8\xd9\'\x9f:\x8e\xf3\xd9\x95\xcf\
-o\xde\xbc\xa5\x8eT^o\xf5CD\xb5\x99J\x8f\xfa\x94\xba\xa4(\x8a\xd5\xeb\xd5\xfa\
-\x1a\x00d[P\xef\x0cc\xe0e\x8fF\x9d\xfd\xc3\x8f>\x06\x00\xe5\xddR}T\xfe.\x00X\
-]\xad\xff\xf8\xdd\xf7R\xbd\xfe\xec\xca\xe7\xea\xcd\x0f?\xfa\xb8\x11\x04\xd9A\
-XZ^\xb9z\xf5\x1a\x00,.^\xef\x9fZr\'\x02\x80\xf7?\xf8I\xa3\x11\x00\xc0\x87\
-\x1f\xfdT\xa9\xf3\x87\x1f\xfdT\x8d\x8f\x1a\x8d\xfe\xa1K\xc7\xc7\x1d4\xd47o\
-\xdd\xfe\xe4\x93\x9f\xa7\x97\x07\xbd\x1f\nQ\x14GQ\xac\xae\xfc\x05\x92$I\xcf\
-\xf9\x82\x89\xf0\x1b\x01\t\xeak\x9d\xf5\'\xcd\x8dV\xd0\xf1\x01\x9e\xd3j\'J\
-\xec"\xb7\xde\x13L\xb0\xe4\xf9\xb4\x7f\xc0\xd0n\x99\t&\x8e\xe3\x9c\x9f\xbd\
-\x1f%C\xbb\xf2\xe4\xfe\xc7\xff\xf0\xef\x1d\xc7\x89\xe3\xf8\xfd\x0f~\x92\x1a\
-\xfb)_.\xde\xf8\xf6\xb7\xbe\t\x00\x17/\x9e\x7f\xff\x83\x9f(O\xce\xd5\xab\xd7\
-\xbe\xfd\xado\xa6\xf3\xc4\xe57.\xfd\xd9\x9f\xff\x85\xf2H\xa4V\xe4\xeb\x17\
-\xcf\xdf\xbcyka\xfe\xc4\x97\x8b7^\xbfx>\x8ec5?\x01@\xd6\xeb\x9d\xeb\x91\x9a\
-\xb1\x94\xe4\xbdy\xf9R\xd6/\xacZ\xf8\x8f\xff\xe1\xdf\xab3\xfe\xe0\x87?\x1a\
-\xd1\xa9\x81\x97\xbd\xe3P\xdc\xbcy\xbbT*~\xef\x9d\xefd\xdf\xfc\xe5\xd5k\xfd\
-\xcb\x00\x00\xf0\xd5\xb7\xdfRc\xf5g\x7f\xfe\x17\xdf\xab\xd5\xbe\xf7\xcew\xd3\
-A\xb8\xfa\xc55\xc7\xb1\xe7\xe7O\x0c\xfb"\xb2\'\xbay\xebv\xa9TL\'\xb9\x9b7o9\
-\x8e\xadFI}|\xd8\xd0\xa5\xe3\x93\x1bj\x00\xb8z\xf5\xda\xe5\xcb\x97\x1c\xc7\
-\xa9\xd5j?\xfb\xe4\xd3 \x08\x94\x8fN\x1d_.\x97\xd4T\xf1\xa2\xb0\xdcR\x14>6\
-\x08\x00\x82a\x19\x96i\x0b\xc6\x04\x17@\x88k\x1b\x94R\x94\xe2Y\x9d3\x84\x80{\
-\x94\xdaU!\xa4!60\xea\x9a\x02\x04P\x8a\x04\xc0\x7f\x0e\xdd8`hq\x9f<\x16\x16\
-\xe6\x17\xaf\xdfx\xfd\xe2\xf9\xcf\xae|~\xee\xdc\xd9\x9b7o\x95\xcb\xa5z}-\x95\
-\x83\x94\x9f}\xf2i\xbd\xbe\x96jS\xa9TJM\xf5F\xd0,\x97\x8a\xea\x1d\x80\x13\
-\x00\xd0h\x04ss5\xe8\xc9\xab\xe38\xe7\xce\x9d\xc9\x99\x99\xca\x11\xa1\xd6`\
-\x157o\xdd\xbex\xe1\xfcj\xbd\xbe\xa3\xa7\xf8\xe2\x85\xf3\xff\xed\xbf\xff\xbf\
-_}\xfb\xad\xc5\xeb7\xfe\xdd\x9f~\x7f\xb5\xbe\x16\xc7\xb12o\x01\xa0\\*\xaa\
-\xae)U\x82\x8c\x96\xa5\xe4N\xb1Z_+g\xfc3\xd9\xd79\x86]\xf6\xe8\x0b\xbe\xfc\
-\xc6\xa5sg\xcf\xfc\xf8\xdd\xf7r\x93h\xee\xe7N\xfa\xfe\x88\xd9B\x9dKY\xdf\xfd\
-\x13C\xeeDA\x10\xac\xd6\xd7\xe2\xde\xc8\xd4j\xb5\xb9Z\xed\xcd\xcb\x97\xae^\
-\xbd\xf6\xb3O>\xfd\xc6\xd7\xbf\xa6\xa6\xde\xdc\xd0Af|rC\r\x00\xab\xf5\xfa\
-\xe2\xe2\xf5\xf4\xf8(\x8e\xeb\xf5zz\xc3\xbcp/\xbf_\x9am\xaf\xdf6\xa8\x00$&\
-\xb6\xca\x05\xcf\xf6J\\\x00rN\x92u\x1e\xb3gv\xbbc;\x9e\x92\xd27\x19\xa7\x94\
-\xc6\xa2l\x88\xb6mt\xd4\xc6\'\x9e\x84\x8e_y>=9Hhq\x9f<.^8\xff\xe1G\x1f\xbf\
-\xfd\xf6[\x00\xb0\xb8x}n\xae6?\x7f\xe2\x93O~\x9e30\x95\xcd\x9e\x8d\xeeP^u\
-\xb5\xa0\xea:N\xa9TZX\x98_\\\xbc~\xf1B\xd7\x8e\xfe\xea\xdbo--\xaf\xd4\xebu\
-\xa5\xaaKK\xcb\x17/^\xc8\xb6\xb9\xb8x\xe3\x1b_\xffZ*\x8b\x8b\xd7o\xfc\xf2\
-\xea\xb5\x8b\x17\xce\x9f;{fiyea\xfeD\xea\xd8\xe9\xbfl\xc7q\xd4\x95+\xf7H\xa9\
-Tr\x1cG-\x02\xc7q\xdc\x08\x02u\x85?~\xf7\xbdr9/\xeb\x03Y\x98?\xf1\xb3O>U\x82\
-\x18\xc7q\xea\x96\xb9y\xeb\xf6\xc2\xfc\x89\xec5\x0c\xbb\xec\x1dOQ*\x95\xde\
-\xbc|\xe9\xb3+\x9f\x7f\xfb[\x7f\x9c\x1d\xc6\xab_\\S\xd1G\xa9i\xbc#\x17/\x9c\
-\xbf\xfc\xc6\xa5\xcf\xae|\xae\xbe\x82\x11\'\x9a\x9f?\xb1\xb4\xb4\xacL\xfe\
-\xd5z\xbd\\*\xa9\xce^\xbcp\xfe\xe6\xad\xdb\x8b\x8b\xd7\xbf\xf7\xcew\xfb\x87.\
-Kn\xa8\xd55_\xbcxA\xcd\x9aK\xcb+s\xb5\xda\xc2\xc2|\xbd^W\xef\xd4{!\xa7*\xf6t\
-\xaeV\x1b\xa7G\xcf\x11\xd3r\xbc\xca\xc9\xa4q\xdbP\x9bN\xdb\x8fY\xb2I\rCr&\
-\xf8\xb3+;\x00\x10\xc7+4;1\x8b%\x01D4\xa6\x1c\x97\x90\x0e\xaa\x94b\xf4\x10\
-\x06\xb9\x83\x16\xf7ID=\xd8\x1f~\xf4q\xa9T\x9a\x9b\xab5\x1a\xc1\xd2\xd2\xf2\
-\xb7\xbf\xf5\xcd\xfeP\xc8\xf4\xbf\x00\xf0_\xff\xcb\x7f~\xfd\xe2\xf9\x1f\xbf\
-\xfb\xde\xd2\xd2\xb2\n\x85\x04\xa5SW\xaf\xfd\xe5\xbb\xefEq|\xf1\xc2y\xc7q\
-\xca\xa5\xe2\x87\x1f}\xbc\xb4\xb4\x1c\xc7I\xad6\x9b\xf5\xc9,-\xaf4\x82 \xfb\
-\xce\xb9\xb3g>\xbb\xf2\xf9\xd2\xf2\xca\xe5\xcb\x97\xde\xff\xe0\'s\xb5\xd9F\
-\xd0|\xfd\xe2\xf9\xcbo\\\x9a\xab\xd5\xfe\xf2\xdd\xf7.^<\x9f\xb5\n\xcf\x9d;\
-\xfb\xfe\x07\x7f\x95\n\xe5\x9b\x97/\xfd\xe0\x87?R\x9fz\xf3\xf2\xa5\xb9Z\xadT\
-*}\xef\x9d\xef|\xf8\xd1Oo\xde\xbc\xad|\x11K\xcb+\xeaR\x07\x92m!\xed\xbe\xf2n\
-g\xe3m\x86]\xf68\xa3\xad|\xf4\xd9\x83\xbf\xf1\xf5\xaf}\xf8\xd1\xc7\x9f]\xf9\
-\xbcT*\xcd\xd5fG|6\x1d\x848\x8e\xbf\\\xbc\xa1\xd69\xfed\xfb\x1c\xdc\x7f"\xa5\
-\xc8?\xf8\xe1\x8f\xd4\xf1\xdf\xfe\xd67\xe38\xf9\xf0\xa3\x8fU7U\xb8j\xff\xd0\
-\xe5Z\xcb\ru\xfa\x05\xa9\xafUM\x15\xef\x7f\xf0\x93\x9b7\xbb\x0b\x06j\xb4\xd5\
-?\xf7_\xdc\x01\xa0<\xbb\xb0\xcec\xd6zhRI\x80\x08\x16\x0b\x06\x00*t\x06\x80\
-\x00<\xc3\xaa*"\xdaf\xa7\\\x9d\x8b\x12@D\x8b0\x07C\xb5\x8c*\xc1:\x94f;\x00\
-\x90=\\\x89\xd6\xec1A\x10\xa4\xde\x951?\xa2,\xdc\xdcG\x96\x96W\x1c\xc7N\x1fi\
-uL\xf6\x9d\xa7hy\xe0\x89\xc6\xbc\x1e\x00X\xad\xd7\xe38\x19\xe7\x1a\xd4 \xcc\
-\xd5f\xc7\xf1\xa1?w>\xbb\xf2y\xb9\\\x1a\xf6# \xdb;\xd5\xa3\xf1\xaf3\xd7\xaf\
-\xfe\xefz\xcc\x11\xce\x92\xfb\xa2\x07\xbe\xf3bI\xa2v\xd4\xd9\x14I\x07\x90\
-\x0b\x16#\n\x90\x1c@J!\x08H\x00\x15\xf7\x8e\x84\x0c4\xe6I\xf7?\x83$\r\x11\r\
-\xdb\xa5\xd6\x94\x94\x88\xac\x89"\x01 B\x82?\xfb\xdaT\xf5\xd8\xde\xf6\xea\
-\x05\xa1\xc5]\xa3\xd9\x1d\x1f~\xf4\xf1\xdc\\\xadV\xab\x05A\xf0\xcb\xab\xd7\
-\xfe\xdd\x9f~\xff\x85\xcc+/\x0b\x88\x88R\x08\xc6;u\xd1y\xd0\x8cK,a\\JJ\x90\
-\x82\x04\x82\x04$\x01iP\xa4\xc0)\x05\x89\x06!j\xdf\xa9\xe85\xd0\xdd\xa5D\x89\
-\xf2\xc2\x00\x00\x91H\x90:\xfe\xf4\xe9\xa9\xca\xd1\x17\xd8\xb9=E\x8b\xbbF\
-\xb3;V\xeb\xf5\xc5\xc5\x1bjY\xf5\xcd\xcb\x97^\xf8j\xe4KB\x12\xdc\x83x\x15\
-\xcc\xb9\xb5\r\x16q0(\x11\xbd\x80F\x02x\xa4j\xc4\xf1F\xc1\xe2\xb2p\x86\x85\r\
-\x16\xae\xf3\xa4\x8d\x08\x94\x80\xef\x10\x04\x90f\x95\x83KP\x10\x02\x12\xa8\
-\xed\x95\xfd\xe2\x0c5\xac\x17\xda\xa7\xbdE\x8b\xbbF\xa3\x99\x00\x82\xd5\xdf\
-\xd8\xfc\x91\xe3\xd5\x82\xd0F\x80v\x84\x11CB\x08"\x9a\x94\x1c\x9f6\x9el\xd6}\
-\xb3#\x91\xf2^\x89\x0f\x04\x98r\xc0\xf3\x08 \x80Y\x86\xca\xef\xbe\xd8.\xec3z\
-\x13\x93F\xa3\x99\x00\x92$\x8e\x99\x94l\xdd\xb7y\xb9`Z\x96i\x1a\xc4\xa0\xe0\
-\xd9\xb4\\0\x92\x84I\x115C\xcc*\xbbA\xc0\xb5\tH\x00\x04`\r\xe8\xdcy\xb1]\xd8\
-gt\xb4\x8cF\xa3\x99\x00\x04g\xad\x0e\x98\x94!\xac\xb5\xa2J\xb9P\xf2,\x94(M\
-\x83\x08\x166\xdb\x1bR\nD\xb2UM\x1b\xc1u\x80\xd0\xcc\xfaj{\x19\xac\x19\xb0^\
-\x167\x9av\xcbh4\x9a\x03\nJ\x86\xac\x83\xbcIy\xe3\xd1\xea\x93(\x91&\x05\xdf\
-\x81\x84#\x80I\xa8\t\x84\xa0\xe4Rp\x00D$\x12\xc1w\x08\xf4\xcc\xf6\xeaT_\x11\
-\x0e\xc3\x87\xea\x1bp\x18\xb3\xb7\xf7\xa3-w\x8dFs\x80@\xc9\x90wd\xdc\x90I\
-\xd0\x8dY$\x80\x08B\xa0\x12\xea0\x01\x93\x12\x00\x0e\x92\x03\x00\x010\r\x02@\
-\x12\xbe\x95\xbb\x17\x11\xbc\x9c\xd9\xae\x10\x1dh\xdf\x85\xa9\xb3\xfb\xda\
-\xa5\x17\x84\x16w\x8dF\xf3BA\x81\x92#\xef\xc8\xa4!\x93&\xb2\x16J\xd6U\xe5\
-\x9e4K\xb9\x15\xd1\xa8\n\xa5\x1a\x84\xa4\xd1\xee\xb2{\x00RJ\x08\x01)\xc1\xa4\
-\xe0\xdad\xf0\xbe\xa7\xf0\x01x\xc7\xc08\x84\xc9drhq\xd7h4\xfb\x8eL\x80\xb7\
-\x91\x05<jH\x1e\x82d\xd9L\x8d9\x08\x80D\x90\x98\xf9w\x06\x04\x00\x04\x95H\
-\x80\xf6v0\x15\xdcAf{\xfa\x89\xe6o\xa0\xfc;pH\xd3\xb8\xa7\xfc\x1f\x1b\x081\
-\x83\xa9\x05{\xd0\x00\x00\x00\x00IEND\xaeB`\x82' 
+\xcf\x1fD\x15x\xc3E\x07\x1b@\t|\x0f\xb5\xfd\x86{-i\x11\xff\x7f{\xef\xfe\x1bI\
+v\xddy\x9e{\xe3\x1d\xc9|\x91L\xd6\x8b\xecz\xb6ZUl\xdb\xd5l\xdb\xed\xee\x19h%\
+\x8f\xbd\xd2BB\xb7F\x18\xcf\xfc:\x8b\xc5b\x80\xc1\xfc9\x8b\x05\x16\x83\x01\
+\x06\x86\x01\xc3j@\x18\t#l\xf7\xb6v\xd4\xdb\xb2\xbb\xdc\xb6T]\xb2\x9bU\xb2\\\
+o\x92\xf5`\xb2X\xcc\xc8G<\xee\xe3\xec\x0f73\x18\x8c|0\xeb\xc1G\xb2\xee\x07B+\
++\x19q#\xee\xcd\x88o\x9c8\xf7\xdcs\xa2\xa7"z\x02\xb0\xcb\xa3\xc2p\x8an\xf1\
+\xc4\xe4\x89{z%\xa5\x8eou\x83\xa9\xcbQ\xdd\xdb\x0f\xa7\x7f\xf0\xa0\xfa}\x97\
+\xad\xfb\xc9\x8a\xc7\x1e\x98\xb2C\x00\x91\x9a\xcc\xa8D\xee\xe9\xc4]\x90\xce\
+\x8ca\x18f/w6\xe7\xbcY\xfd\x17[\xa5?J\x9b2M\xd3q\x1c\x95\x17P-F\xef?\ru\x02\
+\xb0\xd3/a\x18\x86\x8a\xa0\x90\xd2\x10\xb4\xbcQ\xf9W\xf5\xf2\x1fS\xd1q\xc4\
+\xa6\x97<p\xf9\xba)\xdb\x00\x12\x10$u\x13k6t\x16\xb8s\x1c\x9c\xaaJCH\xe9\xe0\
+(\xef~_Pz\xf3\x08J\xb7\x8a\x7f\xf8t\xea\x0f\x0c\xd1r\xf9\xba\x97<px\xdd\xc0\
+\xde\x12\x12j\xa3U\x92\xfe)Zy\xdd\x9b>\xe3\x95f\x1d\xc7!\x19(\xa5a\xe9w\xef8\
+o\xa8\x07\x86j\xd3\xcc\xa4E\x9c\x9a\x9ar]W\xf5r\xcc\x1fE\x85\x91\xa8\xfb|\
+\xf5\xd8\xff\x9a\x8b\xcfQ\x07U\xbf\x9d\xe7y\x85B\xc1w\xfd\x11S\xa9\xfd\xc7\
+\xea_\xe2\xa4\x0e\x1d\xc7q7\x84F\x08)\x8d\xc0\xb8\xdc\xf0\x7f\x8f"3\xf9\x96\
+\xc7\x1ez\xec\xa1%\xb6(\x08@\x90\xd4N\xcc\x99\xc8\x99O\x9c\x93\xd2\x9eN\'\
+\x03\xd3y\x143\x93M~\xe0i\xa4\x0f\xf8\xdc9\xa8\x0b \xb5^\x8b\xc5\xa9bq\n\xba\
+\xab.Q\x15*RF\x80eY\xe9\xdb\x98\xea\xbb\x1a\x7f\xc68b\xea\x0f\xe9\xbat\x08\
+\x1d5\xc9l\x18\xc6\xdc\\mz\xbaJ\x08\xb1m\xdb\xf3<\x95:I\xe5\xb3\x1cgT\xfb\
+\xc7\x93RZ.\x95\xbe\xfe5\x0f\x11-\xcbR\xbf\x94\xba\x17\x86=\xf3\xb2\x0f\xddl\
+;\xbd_D\xaa\xd1\x90R2\xc6\xa3(\x8a\x93D=\xc9\x08!\x96e\xf9\xbe\xe7{\x9e\xaa\
+\xf8j\xf4\xca+\xaa\x14R\'\x8e\x1f;~l\xce0\x0c\xc7qT\x92(\xdf\xf7\x9f)\xaf\
+\xf2\x81\xb0#\xd9\x80\xd8\xf1\xa2\x9ee\xecZK\xc82YL\xc4\xf0\xf4\x16\x85\x99s\
+\xb0\'\xa9\xc6\xf7\x85\xec{q\xf6J\xea\t\xabDDf\x9cl\xb8\'\x1b\x00\x90\xb9\r\
+\xd4\x8en/\x1b\x01\x00\xc8^\xf9\xc7T4\x95\xba\xa5\xbe\xe3~\x8b2k\xaa\xa4*\
+\x99F\xe9%I\xa2n\xefm\xa7\xb0YJ\xb0\x98x\xa7\x1b\x99\x16r{\xa9\x19\xc2nU\xa3\
+A\xf5T\xb3\xbe \xecU\x8b7\x0c#\xbds\xd0\xacDv9\xf2_O\x07\x04\x00\xd2G\xa0\
+\xef\xfb\xc4.\xe4d=5\xb4\x11\x91R\x9a\x8a{\xba(\xc9\x1c\x1ex\xde\x0f\xc9L\
+\xab\xaa\x87\x90j6\xe7\x1f\xc8>\xa8\x9cL\x8d\xd9g\xd2 \xd5Hz1d\x85U]\x03\xe9\
+e \xa5)L\xb7\xe5\x1eo\xc1[\xb9\xc1W{\xd9=WR\xaa\xec\xbb\x9e\x12\xe9UsV\xe7\
+\x90\x1b\xcf\xec\x05\x90U:\xd3$\xfd\x1b\xa7\xf3\xcf\xca\xc8\xe8\xbe\n\xf4-\
+\x81\xf3=\xaf\xff\x91\x96\xfb\x11\xd57\xd9\xebv\xfc\xf1\xcc6\x9b\xde&\xa6\
+\xd9\xd5\x87\xdc\xbd0p/\xe8]\xa2\xd9v\xb2?J\xfa\xc0S\xe3\xe6ynv\xdf\xfe1Q\
+\xdfs\xce\xd5E.\xa5T\xedgo\xc9g}n\xed3,\xb8\xa7\xbc\xa0\xaa"\xc7\xc0m\xc6\
+\xaf\xb5\xc4\xdb\x8fd\xd2\x04\x00\x02\xc0\x86O\xcc\xda\x85\x9a\xe5Ua\x12\'T\
+\xa1\xefJJ\xafr\xd34\xb3\x97\x91\x92*\xdc9\'\x99\x93-\x00\xc8\x16\x9e\xcf^|J\
+}R\x0bn\xd89\xa4\'\x90\xca\xb4\xf2H\xa4\x0f\x8c\xac[6\xe7CH\xef\xa24\xfc#W\
+\xab>w\xc4\xf4]\x012\x86R\xfa<\xebw\x11@O\xdc\xd3\x88\x0e#\xb3\xfe3=\xb4J\
+\xc7\xca9OO/\xdb\xfdq\xbc%\xb0Sk\xb2\xca\x9e:\xcdr\x1d\xc9>\xcfF\xbbA\x86]\
+\x00\xe9\xc6Y1R\xc2\x9a\x1b\xfc\xf4J\x00\xd8\xf1X\xcdNc\xa4\'c\xed,o=B\xdf!\
+\x93\x1e2\x1d\xcf\xdc\x05\x90\xfd\xf5\xb3?\\n~\x82\x10\xa2\xfc\x0f\xaaD\x81\
+\x102\xe7g/\xf69Cr#\xa9\x0c\x1d\x00\xc8&+~\x8eGf\xee\xc2P\x0f\xfe\xdc\xbd\
+\xb0\xab\xbe\x0f|\xdae\xed\xa7\x9c?-7z\xe9\x98\xa8\x07^V\xdc\xd3h\x9cg\xed\
+\xda\xfe#Y\x9bw\xba\xeb\x96GT\xe4\xf0\xa7\xcf\x8e\x0eXP\xa0\xe4\xdb\xe1\x8f0\
+\xf4Q\x01\xd4\xf0g\xba>\xe4\x89\x14wEV\xfeR\xd5N\xc5=;\xab\x93\x15S3\x9b\x9f\
+\xb6\xe7\xd4\xcbjAj\x14\x9b;\xe3vG\xe8{\xeeRV\xe9*\xb3o\x03\xfd\xb2\x9b=\x1f\
+#\xb3v?}c\x18a\xbc\xc3Ne\xcf8"\xf2\x13\x8cd\xa7)\xad\xfc\x00\xb4\x17,\xaf\
+\xbeW\x81\x1f\xaa\x05\xe8=\x0b\xb3\x0e\x96\xf1o$\xb2\xf3\xc5\x9c\x10b\x9a\
+\xa6\x1a\x01\xc8hk\xda\x11e,\xa7w\xf2\xb3\xca\x90zx\xc0\xce\x87k\xee\xc9\x9a\
+{\xb8\xe6\x06?k\'\x0e\x1c\xfc\xd1\xa7\x94}\xc6\x0c\xbc\x00\xfa\x7f\xfd\xec\
+\xa9\xaa\xb3M\x8d\x0c\xb5\x0b\xa5t\xab\xd1\x10"\xbb`\x07\x08!\xa5\xd2\x00Ow\
+\xaa\xec\x00`\x18\x86\x12\xf7T\x1f\x07^\xb4\xbb\x8ej\xee\xc2PZ\x9c^\x0c\xa3_\
+\x08\xc8N\x8b\';\xc2\xb9\'n\xffdI: \xe9\xc3U=\xf0\x84\x10jH\xd5\xc3o\xb4\xc9\
+u\xa8`\xc1\xdd\xb4\x90\xde\xb0\x8a\x1c\x96?\xad\xac\xec]\xe1\xcd\x15\x14\x11\
+\xecf\xb6{\xe5\x854\x9erR\xc5=wo\xa7B\x99\x06\xcf\xe4\xa6\xecSQ3Msc\xe3I#\
+\x08\\\xc7}\xe3\x8d\xd7\x01 +\x01\xe9\x0cO\xf6\xe6\xdf\xf5m4k\x0c\xaa\xfb!\r\
+\x1bP-g_\xcfa\xe7\x05\x9d\xea\xbb\xd9\x9b\xc7KO5w\xc4\x9ce\x94\xde\x84\xfd"\
+\xd2\xaf#9\x83\x88dLl\x9a\xf1\xf6\xa4\xc7\xcav\x7f\xfc\x1b)k\x82\xa9F\xd2\
+\x80\xc5\xdc\x96\xe9\xcd<\xf0I6&\xd9\x87k\xda`n\xf0\xfb\xc5=\xed]\xf6\x1c\
+\xb2?t\xfa[\x8f\xd9e\xccT\x7f\xce\xcaY\xf6\xa7\xcf\x8dmV\xdf\x8d\x9eo0\xb5\
+\xdc{\xab\xf6\xb7M=\xd7u\\\xc7\xcd\rQ\xeeGTr\x0c\xbd+d\xf4u;\xba/\xd9\x0b#\
+\xfb27\xd0g8\xf0GI\r\xfc\xd4J\xc8*{\xeev\xc8\xfd\x16\xd9\xf7\x03DT\xe2\x9e\
+\xfe\x82\xe93\xe0\x99^\xf5\xf6\x1f\x99\x04\xdd\x99O\x00\xc6q\xb0\xd9>~\xad%\
+\x1e\xf1\xf6\x1a\x00\x90\x91\x1e\x1ej\xba^e;\x9d\xe4\xa4\x8a;\xf4\x19\xce\
+\x9b\x9bO-\xcb\xf4}?\x17\xfe\x0c\x19kT\xfd7h\xb6\xae~\xf9\xebS\'O\xbe\xf9\
+\xe6%\x000\x0c\x033\x95\x97\xd3\xc6\xb3\xf6\xdd\x88\x0b:\xfd\xd2\xe8UzR\xd7h\
+\xf6\x81\x81\x19\xb2\x86^Ve\xd2\xcf#\x94\xa5_\xce\xd4u?\xc2\xf9\x93\x15>3\
+\xe3@\xcf\x9e\x86\x99\x99\x04\x1b\xd8\xfd\xb4\xa9q~\x94\xb4}eK\xe6\x845\xd7\
+\xfe\xf3y\x0f\xfa\xc7$\xf7|\x95;\x198\xf8\xa4\xe7\x99\xc9\xfe\xc4\xd9\xc1\
+\x19\xff\x04T\x07\xff\xf9\xe6\xadS\xa7N\xfa\x9e\x97\xfd\xf5\xb3?=\xec\xfcQ\
+\xd2\x8e+\xe7\x03\x00 \xe2\xc6\xc6\x93N7[\xd6\xf69\x1c?v,\xf5Pg{\x9d>\xe9U\
+\x97Sq\xcf\xfep\xcf:\x98Y\x83)+\xee\xe3\xffX\xfdWi*\xd0\xa9\t"\xf3q2;n\x84\
+\xf4\xcc\xd5K\x83\xbaC1\xe3_}\xd6\xe7\xd6\xfe\x934\xee\xaa\xc8\xa8\xfeL\xbc)\
+N\xf1\xf8\xd8\xb5\x96\xee\xa0\xec\xc6P\xb1\x11\x13\xb3\xd3g\t\xdd\x96\xf4\t\
+\x16\xf7T2\x10\xf1\xc6o\xfe\xe9\xff\xfb\xec\xaf\x1d\xc7\xf9\xee\xff\xf2\xed\
+\x99\x99i\xccL\xb4f\xb7\x84\xee\x95\xa4\xeepPFSz\xe9\xe44\x88\xecd\xf4\xc9\
+\xa4\x17\xb4\xfa\xaf\xba(\xb3\xcf\x8c\xecc\x06v\xde\xe4\xe9\x7fG\xc8z\xee@\
+\xd9\xab<\'"\xb0\xd3\x01\x92=D\xee\x11\x95~\xdf\xdf\xf7g\xed~\xff8\xa4-\xc3\
+\xce\xf1\x1f\xd6\xfe\x8b\xdc\xa8$\xf3|U\xb6gv\xe4\xfb\x9f.\xb9\xc1O?\xc03\
+\xcaz\xf6\x02\xbb~\xfd7\x9f\xfd\xf5_\x9f<q\xe2\x8f\xbf\xf5?\xa9(=\xdc\xc9\
+\xb0\x8e\x03@j\xdd\x07A\xf3\xee\xbd\x95\\\x12\x15B\xe8\xc2\xc2\xbceY\xfd\xe7\
+\x96\x9ey\xff\xd3\xeb\xb9\x076{\xc1\xf4?\x93\xc6o0w\xa1\xa6?D\xce\x8a\x82\
+\xbe\x07^\xee\x10\xea"\x7fY\xbd\xdb\x1fD\xb4)\xe3n\x12\xf2\x11\xceq\xafrz\
+\x9c\xd6d\xd2\x10a\x1d\x00\x88J\xda>\xc4\xc3cz\x15gjn\xc77\xcfp\xca\x87\x89\
+\xdcm\xb3\xba\xba\x06\x00q\x1c?x\xf8\xf0\xd8\xb19\xe8\xddx\xd8\xc7v\x0b\x00\
+\xd0\xbbP`\xb8\x00\xe5>\x8c&\xbb}\xf6\xde\x18\xf8\xe4\x80\xcce\xfa\x1cG!=\
+\x831\xab\xa1#\x8e\x92\xfdgn\x83\x81}\x87g\xef~\xee\xa0\xea\x9f\xbb6\xfe\xac\
+\xed\xefz\xdctX 3&95\x81\x91\xc32\x0e\xd9\x8b\xea\xe1\xa3G_\xfc\xfd/\xa5\xc4\
+\xd5\xb5\x07\xff\xf7\xc7\x9f\xfc\xcb\xf7\xde=y\xf2D\xba\xd9\xb0\xa3\xab\x7f\
+\xa6\x92\x17\x86\xe1\xf2\x8d\xdf\xb4;\x9d\x9do\xf0\xa4R)\xd5fg\x06*{\xf6Cz\
+\x88\x17\x19\xd8am\xf6\xffi\xfc\xd6v\xbdP\x87\xdd\x0bY\x1b\xe5\xa5\xf4n\xdf`\
+\xc1]\xf5a\x84\xd9\xee\x95\xe7\xc7\xab\xb5\x84\xac\xf7\x12\x80\xbbxx\xf2\x13\
+\xb3\x93*\xee\x00\x90\xb5\x05~\xe7\xcd\xc5z}\xa3T*^\xba\xf8u\xbas\xc2\x07{\
+\xcb\x083\xb1qy\xab\x01^\xea\x852\xe2\x0e\x19\xb1\xf1s\x1f(\xbd\r\x9e\xf5(\
+\xcf\xa7\xdd\xe3\x9f\xd8\x1e5>\xe6\xd1\xf7t\xf0\xd3w\x02)e\xb3\xd9\xfa\xec\
+\x17\x7f\x13\x86\xa1\xf2\x92\xd7\xeb\x1b\x1f\x7f\xf2\xb3\xdf\xfb\xdd\xdf\xb9\
+t\xf1\xa2\xe7\xb9#\x0e\x916"\x84x\xfcx\xfd\xda\xaf\xff\xa1^\xcfU6G\xc30/\x9c\
+?\xef\xfb\xfe0+u/~\xc4\x97\xde\xe6\x98\xbfH\xeep\x13\xa1\xe3\xfd\x88\xce\xba\
+\nXTZ<x#B\x9c\xe2\x89\xf1Z\xab\xcbx\x0b\x00\x08\x81\x11\xc9\x06\x9c\xe2\xf1\
+\xfet\x92\x13)\xee\xa9\xc5\x94N\xceT\xab\x95?\xfb7\xffZy\xe2\xb07\xd1\x9aB\
+\x08Q\xb2\xde\x9ba\x13i+\xfbp\xb6\xfbp]N\xd0\xa5\xbf\xcf\xec\xc5\xc8\xa4\xd7\
+\x1e\xe7\xbc\xd9l\xfd\xcd\xe7\x7f\xfb\xf4\xe9\x96:\x9a\xda\xa0\xd3\t\xaf\xfc\
+\xed\xdf\xdd\xb9s\xef\xcc\x99\xd3\xe7\xcf\x9d\xadV+#\x1ay\xf8\xf0\xd1\xed\
+\xbbwo\xdf\xba\xdd\t\xa3\xbe4\x03\xe4\xf8\xf1\xb9\xd3\xaf-\x0c\xf4WL(G\xa0\
+\x0b\xa3@\x99\xa4\xa9}G\x85\xa2\xcf\x8ee\xb6\xa3H_\x02\x10\x87>*\x88a\xf9\
+\xd53\xfd\xdfO\xa4\xb8+R\xd3)\x9d\x9c\x19v\xf5\xab\x1bI\xc5\xb3\xa7\xdbc\xef\
+\xfb#~\xb5i^*Yeg\x8c\xfd\xe6\x9f\xfeium-\xeb@Hy\xf4\xf8\xf1\xa3\xc7\x8f\xff\
+\xf1\xab\xe5cs\xb5\x93\'OT\xab\x15\x15\xe3\xa1.\xc5(\x8a\x9f<y\xb2\xb6\xf6\
+\xe0\xc9\xe6\xd3(Je}G\x0b\xa5R\xf1\xcdK\x17\x0f\x7fd\x88&eGj\xdf\xa1\x159\
+\xa8_=;Vk\xad\x07\xc8CTf\xfb\xe8\xbc4\xe6\x80\xd4\x9b\x93*\xeeY\x8f\xa7\x94\
+\xb2\xd5j\xad\xac\xaeQJ\x97\xde\xba\x0c\x99W\xbf8\x8e\x97\x97o<^_\x8f\xa2\
+\xd8\xb2\xccZm\xf6\xcc\xe9\xd7\xba\x7f\xc5\xbc?4\x8e\xe3\xe5\xeb7\xea\xf5\
+\x8d8\x8eK\xa5\xd2\xdc\\\xed\xc2\xf9s\x8e3:a\xa9\xe6\x95#\x15\xf7$I\xca\xa5\
+\xd2\xd4T\xd5\x8e\xa82\x00\x00 \x00IDAT!S\xa3\xa3\xbbI*\xd3\xedv\xeb\xf6\x9d\
+\xf6\x9d\xbb\xf7(\xa5YqF\xccUM!9\xb3\xdd\xf3\xdc\xdfY\xbcT,\x16\xcdgY$\xac9@\
+Pr\xd6ZQ\x9fG\xa4\xf6u\x8a\xc7\r\xdb\x1f\xf4\x97\x9d\xad\x89\x845W\xd45!\xe5\
+\xd0\xe2M\x86\xed\x0f\xcbK3\xa9\xe2\x9e\xa3\xd5\xee\xfc\xea\xea5\x00Xz\xebr*\
+\xd9+\xabk\x9f\xfc\xec\x7f\xc4\xf1v-\x98{\xf7Wn\xde\xba}lN\xcd)oO\xb1"\xe2\
+\xf2\xf5\x1b_\xfc\xdd/3\x1b\xaf-_\xbf\xf1\xab\xab\xd7\xbe\xf3?\xff\xc9\xdc\\\
+m\x9f\xbb\xa39\xcc\xa4o\x81I\x92LM\x15~\xf7\xcd\xc5\xdf\xfe\xf3\xcd\'\x9b\
+\x9b\xbdI\xaf\x9c\x01N\xd2]\xb2md\xd4<\xd5\xf4\xed\xbd\xa6\xa6\n\x8b\x97\xbe\
+>;;\x93]\xd1\xb3\x1f}\xd3\xbc\x00\xbc\xb5\x86<\x82n\xbe\xc6gs\xa1\xf4\xc3\
+\x9a\xf70\x9b\xb4}h\xf8\xe3\xb9ayi&U\xdcs3\xec\xdb\x81c\xbd\x10\xda\xf5\xf5\
+\xfa\'\x9f\xfc\xbfq\x92\x14\x8bS\x8b\x97.z\x9e\'\x84\xb8\xbf\xb2r\xf7\xee\
+\xfd\xad\xad\x06\xf4\xdc2j\xe3\x95\xd5\xd5\xcf~\xf17\x00\xf0\xfbo/\x9d;{\xba\
+T*\xb5\xda\xed\x7f\xf8\x87\xaf\xae\xdf\xf8\xcdG\xff\xcf\'\xff\xf6\xdf\xfc@%`\
+\xd2h\xb2\x93=\xca3c\x18\xf4\xdc\xd9\xd3S\x05\xff\xf1\xfaF/\x87\xfb\xae\x10\
+\xdc.\xe0Mv\xe4\x91!\xe4\xd8\\\xed\xc2\xf9\xb3\xe5r9\xcd\xcdpd\x1c\xeeG\x18\
+\x94\x8c\xb7\xba\x15zG\xe4\x08s\xcb\xf3\xd4\xdc=\xdf\x99d\xad4\xe3\x98\x18\
+\xb9\xc0\xd5.\xcc\x0ekdR\xc5=G\xfa\x94\x94\xbd,"\xcb7n\xc4I23=\xfd\xc7\xdf\
+\xfa\x86Z\xbe,\x84\xa8V\xca\xe5R\xe9\xd7\xff\xf0Un\xe3_\xfc\xf5\xe7\x00\xf0G\
+\xef\xfc\xc1\xeb\x17\xce\x13B\x18c\x9e\xeb\xbe\xf7\xee;Q\x1c\xdf\xbe}gy\xf9\
+\xc6\xd2\xd2e}ki\xb2~\xbc\xacW\x10\x11\xa7\xa7\xab\xbe\xef=\xdd\n\xb6\x1a\
+\x8d0\x1c(\xf1\xdd\xe0[\xe8\xfe_&\x14\xa4g\xc2\x97\xcb\xe5\xb9\xda\xec\xa9\
+\x93\'<\xcf\xcb%\xa7\xdc\xeb\xaei^\x10\x16\xdcW\x86\xb6ZA:\xf0\x07#\xa6\xe3\
+\x95\xc7J\xed\xcb\x1aw\xd2"\x04\xc3\xc3\x1f\xe9\xe8\x05\xae\x93\xea\xc8\xcb\
+\xdd`\xa9\xef2\xb5\xa7~\xfb\xdb\x9b\x00\xa0\x12\x0cd\xf3\x16-\xcc\x9f\xaaT\
+\xca\xaa\t\xb5\xf1\xa3\xc7\x8f\x9b\xcd\xd6\xd4T\xe1\xcc\xe9\xd7\x18cjc\xc5\
+\x99\xd3\xaf\x01\xc0\xca\xea\xdaAuSs\xa8\xc8\x86\xf4\xa5+*S\xcb\xdaq\x9c\xe3\
+\xc7j\xe7\xce\xbc\xb60\x7f\xaaZ-;\xf9\x84\xb4\xf9\xf9\xd2\x14\xc7q\xa6\xa7\
+\xabg\xcf\x9c~\xe3\xf5\xf3\xa7N\x9ep]\xd7u\xdd\\\xbeL\xad\xef\x87\x19\xe4\
+\x11\xefdR\xfb\x0e\xd9\xac\xb0s\x05\xe90D\xb4)z\xf5\x93GD\xca\xbb\xa5\x13\
+\xa3\x17\xb8N\xb0\xe5\x9e\xc6\xc0d\xb3\x97\xa8x\x98\x07\x0f\xba\x03}\xfc\xd8\
+\\\x9a\xce"M\xdfq\xf2\xc4\xf1\xad\xad\x06\xf6\xd6|\xab\x05PI\x9c\xfc\x8fO?K\
+\x1b\'@\x08!\x8c3\x00\x18\x16\x0e\xa1y5!\x99tl\xe9\xb5G)U\xd7\x9e\xe385\xc7\
+\x99\x99\xae\x08)\xa3(j\xb7\xc3(\x8e9\xe7R\n\x94 U>wB\t%\xa6a\xb8\xae3U(\x14\
+\n~\x9a\xfdXU\x11P\xe2\xfe\x1c\xf9a4\x07\x02\x0b\xee\x82\x94@@\x0e_Aj\xd8\
+\x85\xf1R\xfb"k\xdc\xee~\x1a\x19)\xef\x96w)\xcb7\xd9\xe2\xae\xf4z;t\x1d\x801\
+\x96\x9d\xbcJ\x8d\xac\xfe\xdcU\x88\xdd\'\x81\x10\x12\x00\x12\xc6\xd6\xd7\xeb\
+0\x04\x1d1\xa9Q(#Z\xe5]Q\x91T$\x93X?\xcd\x8d\xa5\x1e\x00\xb6e\x95K\xa5\xd1\
+\xad\xd1L\x1aj\x95P\xdeu\xdd\xd4\xdb\xae\xaf\xba\xc3\x8f\x88\x9e\xf2\xb0\x0e\
+\x00\x80\xa3V\x90\x16j_\x831~M\x16\xdc\x93\xac\x1b|\xc59\x0e\xad\xa6]:\xb5k\
+\xa4\xfc\xa4\x8a{v\x89`\x9aT\x16\x00TR\xf2\xb4pR\x14E\xc5b1\xa7\xec\xed\xb6\
+\nD\xed\xd6F0\x0c\n\x00\xb5\xd9\x99\xf7\xde}\'m\\\xddu\xca\x92J\xf3\xedi4\
+\xd0\xb3\xdc\xb3\x9f\xd3\xac\xb6\xd9\xf4\xcb\x033\xa6\xa5{\x91\xbe\x02\x03\
+\xd9\x84\xfe\xe3$\x94\xd7\x1c\x0e\x90\x05\xb7\xd5\xfc)\xe7#V\x90\x9e\xe8_A\
+\xda\x8f\xe4\x1d\xde\xec\x06SJ\t\\\x0cn\x8d\x9a\xae?}f\xd7\xd6&U\xdc!\x977\
+\xa6\x97Q@\xddQ\xbe\xef\x15\n~\xbb\xddY{\xf0\xf0\xcd\xc5j\xd6A\x9f$\xc9\x83\
+\x87\x8f\xd4\xfe\xea&\x9c\x99\x9e\x06\x80\xfa\xc6\x93\xa7[[\xd5J%\xdbx\xb3\
+\xd9D\xc447\x9eF\x93u\xbb\xa7v\xb7\x91\xa9%\x90f`\xce&\xfbM\xaf\x9f\xfe\x1d\
+\xd3l\xcc\xd9\x84\xfe\xcf\x94\x89Ss\x80\xf0\xce\xbaL\xda@F\x86?R\xd3\xab\xbc\
+6Nk\xacqG\x15b\x04\x00&\x86\xbc\x04\x00\xf8\xd3g\xc6\xf1\xddO\xb0\xb8+r\x13\
+\xaa\xa9\x91~\xf6\xcc\x99\xaf\x96\xaf_\xfb\xf5?\x9a\xa6\xb9x\xe9\xa2\xba\xcd\
+\x9a\xad\xd6\xdf\xff\xf2\xaa*Q\x8f\x00\xca\xeaw\x1c{a\xfe\xd4\xca\xea\xda\
+\x95\xbf\xfd\xfb?x\xfb\xadZ\xad\x1bZ\xb4\xb5\xd5\xf8\xf5?~E\x08\xf9\x97\xff\
+\xe2\xdd\x13\xc7\x8fk\xcf\x8cF\x91\xfa\xfaR\x03<\xcd#\x9f*{\xae6VV\xdc\xb3{e\
+\x93\x98\xe7\x12\xfa\x83\x9e\xe39\xfc\xa0L\xab#\t1\xdc\x852^\x8e0\x11o\x89\
+\xb0\x9b\\h\xc4<\xea\xf8e\xf9&^\xdc\xfbQ*\xfc\xf57^\x0f\x82\xe0\xfe\xca\xea/\
+\x7f\xf5\xe5?\xfd\xf6\x9f\x0b\x85\x02J\xf9x\xbd\x0e\x00\xe5r\xa9\xd1\x08 \
+\x93\x9d\xee\x8d\xaf]\x08\x9a\xcdF#\xf8\xfco\xff\xaeR)\xabRv*\x8b\xd3\xcc\
+\xf4t\xa9T\xd26\x94&\x87\xba\x1e\xb2I\xfc\x95\xfd\x9e\xcd\xe3\xdf\x9f\xde\
+\x16v\xfad\xb2\xf16\xc6\xe4\x94\x06\xd5(x\xfb!\xf2\x10\x08 \xe2\xb0\x15\xa4\
+\xc4t\xb2\x054\x86\x83\xacq\xa7\xfbi\xc4<*@a\xe6\xc28\xbe{82\xe2\xde\x9f\xc7\
+\x0e\x11\xdf\xfd\xa3?\x9c\x99\x99^\xbe\xfe\x9bf\xb3\xd5l\xb6\x00\xc0\xf7\xfd\
+\x8bo\xbc\xde\tC%\xeei\x9ai\xdb\xb6\xff\xe8\x0f\x7f\xff\xd6\xed\xbb\xf7\xee\
+\xaf\xa8%N\x00`\xdb\xd6k\x0b\x0b\x7f\xf8\x07o\xbb:\x03\x81f\x10\xa9\t\x0f\
+\xbd\xc8H\xec\xcbZ>L\xdc\xb3\x12O\x9e%\xa1\xbf\xe6\x90\x80\x92\xb3\x9e\x7f\
+\x9c\xf3\xe1+H\xab\xa7\xc7q\xa1\xf0\xf6c\x99\x04\xea\xf3\x88\x97\x00\xa7x\
+\xdctGM\xd1g\xd9=[\xec!D\xc5\xc3\xa8\x80t\xf5_\xccd\x82T\xb7S\x1a\xd2@)}\xb2\
+\xb9\x19\x86\x11%\xa4X\x9c\xc2^!1\xd2\xab\x93 {uS\xd5.\xf5\x8d\'\x84\x00%\
+\xf4\xc4\x89\xe3\xd9\x1a\xaa:"M3\x82\x81\xeb\x9b\xd2\x9b+{\x97\xe5\x17W\xbf@\
+Ny\xcd\x01\xc2\x82\xbb,\xb8\x0f\x04Pb\x94\x0cM\xfcR\x9e\xff}BvYN\x84\x92G\
+\x8f\x7f\x89"\x06\x00D\x88\x18\x0e|T\x10j\x96\xe7\x7f\x7f\xbc,\xf0\x00\x13m\
+\xb9gm\x1f\xe8\xf9@\xb3\x8b\x06U$;!\xa4T,\x16|_\x05\xd2\xa4/\xd1\xca\xce"\
+\x84\xe4\x8a|\xce\xd5fi\x06\xfd\x9a\xac\x19\x87\xac:\x0f\xd4\xf4\x81\xdb\x83\
+\x16\xf4\xc9\x04%\xe3\xad\x07\xea3\x13\xf9L\xcd)~\xf5\xec\xae\xca\x0e*)\x8d\
+\xe8f\xb5\xe2b\xb0\xb2\x03\x80WY\x18_\xd9ar\xc5=\xab\xeci9fu\x9f\xa4\xc1\x91\
+\xb9\x89\xd6\xf4%:-\xca\xac\xbe\xa7\x94\xa6\xfa\x9e{_\xd6\xfa\xaey\x0e\xb4p\
+\x1fyXp\x1f%\x07\x02R\xe2\x88\xbaw#\x12\xbf\xa4\xa0\x88ysU}\x968<\xfc\xd1\
+\xf2\xdc\xf28\xbe\xfb\xcc\t<\xd3\xd6\x87\x87\xac\xb2g\xa3\x17\x00@9^\x94d\
+\xa7\xaf\xc9i`rV\xdc\t!i\x06\xa84*95\xed\xb57F\xa3\xd1\x0c\x00\xa5*j\n\x00\
+\x8c\x8f0\xdb\xcf\x8c\xb5j\xa9q\x17\x91\xf7Z\x1b\x1a\xfeX\x18\x9e\xfdq\x18\
+\x13)\xee\xca\xfd\x92*{\xd6-\x03=)\xef7\xdeS\x03<\x8dO\x80\xcc\x93 -\xe2A2\
+\xabK\xf4*\x12\x8dF\x93\x83w\xd6Q$\xcal\x97r\xb0\xd9n\x17j\x967\xa0\x02W\x0e\
+\x994y\xe7\xb1\xfa<"\xfc\xd1\xf2\xa7\xed\xa9gN<>\x91\xe2\x0e=\t\x86L\x05\xd1\
+T\x85\x95\xcf\x9df\x8a\xa6\xa7\xbb\xa4.\x97tB5\x15\xf7t\xbd\t\xf4"\x1f\xd2\
+\xcd\xb4\xbek4\x9a.(\xd2 \x99\x91\xf9\x1a\xc7\xaa\xb5\x944n\xa79m\x87\xa7\
+\x91\x19\xb7\xb5\x1c\x93*\xee\xd0\xd3\xf7\xdc\xda?\x85\xb2\xc4\xb3\x11\x0b\
+\xb9\xc8\x84\xac^\x0f\x14\xf7\\t\x9aF\xa3\xd1\x00\x00k=P\xb1\xedB\xa0\x1c\
+\x96\xafq\xbcZK"\xdcP\xc5\xaf\x01\x80\x8fX\x03U:a:\xc5\xe78\xd5I\x15we\xb0\
+\x0f\x13\xdf\xac\x15\x9f\xfd2\xf7a\xd8\xc6::M\xa3\xd1\xf4\xb3]\x91cd\xb9jo\
+\x9cZK(\x93t\xd5\x12\x02\x13\x83\xb7"\x86\xedUO?\xcf\xb9N\xae\xb8\xc3n\xb2\
+\xfbL\xa2\xac\x15\\\xa3\xd1\xec\no\xad)o;\x17C\xc3\\\xbd\xf1j-\xf1\xd6\x03\
+\xe4\x9d\xee\xe7\xe1\xe1\x8f~\xf55j\xec\xde\xda@&\xb5X\x87F\xa3\xd1\xec\'(\
+\xe2\xd4l\xe7C\xccvj\xba\xeeX\xb5\x96\x90\xf5j\xf2\x8d\x08\x7f4\xec\x82S:\
+\xf9|g\x0bZ\xdc5\x1a\x8df\x1cX\xf3>J\x01\x04d>\xa3\xc46\xdex\xc9\x06D\xf8\
+\x04E\xb7\x16\xa3\x9a\x95\x1dHa\xe6\xfc8k\xa0\x86\xa1\xc5]\xa3\xd1hvA\xf2N\
+\x1a\xb3\xa8b\xdb\xfb1\x9c)\xb7x|\xf7\xb6P\xa69\xc2\x84\x1c:+k\x15f-\x7f\xfa\
+\xf9\xceV\xa1\xc5]\xa3\xd1hv\x81\x05\xf7T!=!\x86\xc6\xb6\x8f\xb9j\x89\xb7\
+\x1e\xc8\xd4\xdb\xce\x87lDhad\xf1\xebq\xd0\xe2\xae\xd1h4\xa3\x90I\xd0\xcd\
+\xb4>\xbc"\x87\xe5O\x8f\x95l@2\xd6\xbc\xa7>\xab\xf0\xc7\x81x\xe5\xf9q\x82)G\
+\xa3\xc5]\xa3\xd1hF\xc1\x82\xfb\xddBz\x12E\xbe\x18s\x17\x7f\x9c\xf0G\x00\x16\
+\xdcE\xc9A\xd5p\x1e\x12\xfeHM\xc7\xab\x8eU\xb9i4Z\xdc5\x1a\x8df("z*\xa2MPr\
+\xcc\x07;d\x9c\xe2\xb1q\xd2\xacK\xd6\xe6\xedG\xea\xf3(\xb3\xbd:V\x15\xbd]\
+\xd1\xe2\xae\xd1h4C\xe9zQH\xb7\x86F?\x84\x1ac\xadZ\x02`\x8d;\x80\xaa\xa6\xee\
+\xa8*zc\xcd\xca\x8e\x81\x16w\x8dF\xa3\x19\x8c\x08\xeb2\x0e\xd2\xfa\xd7C\xcc\
+\xf6\x13\x86\xe5\xed\xdeT\xf4TDO\xba\x9f\xe5\x90`JB\n3\xe7\xc7\xac\xa2\xb7+Z\
+\xdc5\x1a\x8df \x98\xd6\xbf\x1e\xe6E!\x86=\x9e\x7f\x1cY\xb0\x9dl`\x98\xb7\
+\xdd.\xd4L\xb7\xfc<g:\x08-\xee\x1a\x8dF3\x00\xde~$Y[\xd5\xbf\x1ef\xb6{\x95\
+\xf9q\xd2\x03\xf0\xf6#\x994\xbb\x9f\x87\xbaw\xcc\xc2\xcc\xf9\x179\xe1\x1cZ\
+\xdc5\x1a\x8d&\x0fJ\xc6\x9a]\xb3\x9dq\x18\xe8E1\xec\x82[\xda=\xd9\x00\x8a\
+\x84\x05w\xd5\xe7\x11\xc9\x06\xbc\xeaij:\xcfy\xba\x83\xd0\xe2\xae\xd1h4yXp\
+\x17y\xac\xe6Q\x07\x17\xd2#d\xaa\xf6\xc68\xd5\x91\x92\xc6-\x14I\xb7\xd9!\xc9\
+\x06,\xaf\xe2U\x16^\xe8\x8c\xfb\xd0\xe2\xae\xd1h4;\x90\xbc\xc3\xdb\x8f\x01\
+\xba\xab\x96\x06\xca\xb1\xed\xcf\x8e\x15\xfe\x187Dg\x1d\x00\x08\x00\x17C\x82\
+d\x08\xf1_\xaaCF\xa1\xc5]\xa3\xd1hv\xc0\x82{*f\x91\x0f\xcb\xfd2nu$L\x93\xb6K\
+\x18\x1ao\xe3\x16\x9f\xb3\x1c\xc7h\xb4\xb8k4\x1a\xcd6\xdb\xc9\x06`hj_\xb7tr\
+\xacZK\x9d\xbaL\x1a\xa0\xccv\x0e\xc3\xe6Q\xc7\x0c\x93\x7fV\xb4\xb8k4\x1a\xcd\
+6,\xb8\xa7\xe6O\x87."%\xc4\x1d\'\xcd:\x8a\xa4\x17\xfe(\x87\x87\xc9{\xd5\xd3\
+\xe3\x14\xf7x\x0e\xb4\xb8k4\x1aM\x17\x11o\x89\xe8)\x00\xe0p/\x8a\xe5U\xc71\
+\xdbYk\ry/i\xbb\x18\x92%\xd8\xf6\xc7+\xee\xf1<hq\xd7h4\x9a.i\xa6\xf5\x11f\
+\xfb8\xdev\x141o\xae\x00\x00\x01\x90\x12\x06\xc7\xdb\x00\xf8\xd3/T\x8ec4Z\
+\xdc5\x1a\x8d\x06\x00\x80w\xd6\xd5R#\xb5ji\xe06\xce\xd4\xb1q&?Yp\xaf\x9b\xfd\
+\x11\x80\r\x8b\xb7)\xcc\xda\x85\x99\x179\xe1\xd1hq\xd7h4\x1a\x00@ek\x03\x00\
+\x170\xb0b\xf5\x989\xc2$k\xf1\xce#P\xf3\xa8\x12^,\xde\xe6\xf9y\t\x89%5\x1a\
+\x8df\xd2\xe9&\x1b\x00\x90\xc3\x93\r8\xa5\x93\x86\xe5\x8ej\x84\x0b\xce\x85\
+\x0c\xee\x12\x94\x00\x04G\xc4\xdb\x94O\x19v\xe1%\x9c\xf7p\xb4\xb8k4\x9aW\x1e\
+\x14i\xb2\x01.\x00a\x90\xd9n\xd8~eh\x8e0\xc1y\x1c%1\'\x84m\xd8\xc9\x13\x04B\
+\x00\x98\x009\xe8\r\x00\x08\x19\'o\xc1\x0b\xa2\xdd2\x1a\x8d\xe6U\x87\xb5\xd6\
+\x90\xc7\x00 qH\xb2\x01\x00\xaf\xb2@\x0ck\xe0\xeeR\xc8\xadz=\x8aC\xdfC\x1f\
+\x1e\x03J22\xfc\xd1.\xd4F\xbf\x01\xbc\x14\xb4\xb8k4\x9aW\x1a\x94\x9c\xb7\xd6\
+\xd4g\xc6\x07\xc7,R\xcb\x1d\x11\xdb\xdei\xb56\xeb\xeb2\xe2\x06{\xca\xa3\x00\
+\x08\x05\x15\xfe8\xa4\xb8\x87\xff\xc2\xc5\xaf\xc7A\xbbe4\x1a\xcd+\rk\xae\xa0\
+`\x00 $\n9\xd8\xd6.L\x9f\x1f\x91#\x0c\x01\xcb\xd5Y\xe4Q\xbc\xb5B\x08%\x00bx\
+\xf8\xa3[^\xd8\x07\xb3\x1d\xb4\xe5\xae\xd1h^eP\xc4\xbc\xfd\x10z1\x8b\x03\xb7\
+1\xdd\x92=U\x1b\xd1\x88i\x98ni\xc61[\x04c5\x8f:,\xfc\x91Z\xeeK\xcf\xfe8\xf4\
+\xac\xf6\xe70\x1a\x8dFs\x08a\xc1=\x90\xbc["uH]\xd3\xd11\x8ba\xab\x1d\'\x08\
+\xbca\xe3&\x80A\x00\xb8\xc0a%RG\xbf\x01\xbc\\\xb4\xb8k4\x9aW\x14\xc9;\xbc\
+\xb3\x0e\xaa\xf4\xdd\x90\x98E\xbbP\xb3\xbc\xea\xb0\x16\xe2(\xeetb\x04\xea\
+\xc8\r\x82\x0c\xc1\x90\x08lH9\x0e\xcb\xab\x8e~\x03x\xb9hq\xd7h4\xaf(\xacq\
+\x17P\x02\x01\xc1qX\xcc\xa2W==xg\x84v\xab\xcd\x12&\xc1\xb0ih$u\x04J\x080\x8e\
+BH\xa2\xe6e\t\xd9\xce.@\x88?\xb3\x1f\xf3\xa8)Z\xdc5\x1a\xcd\xab\x88L\x02\x11\
+=\x81n\xc5\xea!\xc9\x06\x8a\'Lgj\xc0\x1f\x10\xda\xadv\x14\xc6\xc4\xb0\x0c*\
+\xccd\x8d "\xa1j\x01\x94\xe5\x95-\xafBA\xb2\xb8\xc9\xa2@\xe9\xbcS<\xbe\x17I\
+\xdbG\xa0\xc5]\xa3\xd1\xbc\x8a$\xc1]\x95d\x80\x0f1\xdb\t5\xfd!f{\xb3\x11\xc4\
+1\xa3\xa6%\x05\xb7I\x83\x88\x06\x12\x83\x00\xb4#\xe9\x94^+\x1d;\x0f@\x00c\
+\x0fxg\xe3~\xd8xDL\xdb\xaf\xeem\xb2\x81~t\xb4\x8cF\xa3y\xe5\x10\xd1S\x19m\
+\x01\x19e\xb6\xbb\xe5\xf9\x81\x15\xab\xc3v\'\x8c\x98aZBp\xcb\xa2\x16{\x04\
+\x84\x10\x80\x84#\x13\x0e\xa0\xbf\xfd\xa4 \xe0\xf8U@\xcb\xaf\xbe\xb6GI\xdbG\
+\xa0-w\x8dF\xf3\xaa\x81,\xb8\xab>q\x8e\x83s\x84\x99\x8eW\x99\xef\xdf\xb3\x1d\
+4\xa3(1M\x83sn;\x9e\x99\xdc\x03\x19\x12\xa0\x00\xd0\x8e\x91R\xbb\xdd\n\xbd\
+\x84%\x02\xe2\x90\x012\x02FLgfJ\x03\x9a\xdak\xb4\xe5\xae\xd1h^-x\xa7.\x93&\
+\x10\x90r\xa8\xd9\xeeW^#4o\xfb\xb6\x1a\xcd0b\x84\x9aR\xa2e\xd9&\x06\x84m \
+\x10 \x10q\x88\x99\x94\xc8M\xc7jw\xc28\x8a$J.\xa9\x00\xc7\x9b9\x15\x85\xd1\
+\xdew+\x8f\xb6\xdc5\x1a\xcd\xabE\x9al\x80\x0b\x1c\x98#\xcc\xb0\x0bN\xe9D\xee\
+\xcb\xe6\xd3F\xcc\xa4aP\xce\xa5mS\xe41\x0b\xd7\x1c\xca\x00("\xb4Ca\x9a\x96\
+\xe5\xcf\xc4\x89\x88\x9en\x02"\x00\x12 \x84RBh\x8b\xb3\xf9sg\t\x19\x18!\xb9W\
+hq\xd7h4\xaf\x102iJ\xd6\x82]\n$\x9d\xcd\x15H\n\x9e6\xe2DZ&\x95@,\x0b$K\xe2\
+\xf0i\x916\x01(\x10\xe8DR\x82Q\x9e}=l\xf3v\xeb)\xa5\x06\x10\x00\xe5\xd1\x07\
+\xe8VdM\x98\xe5\xec\xab\xdb]\x8b\xbbF\xa3y\x85`\xcd\x15D$\x00\x8c\x0f6\xdbM\
+\xb7l\x17f\xd3\x7f\xa2\x94\xc1V\xc0\x041(2\t\xb6\t\x14E"\xb9\x05\rB\x01\x80p\
+!bn\x94f\xce\'\x8c\xa0a\xd9\x9e\'\x85T\xbb\xf6\x9a\x00\xc34\x0ck\xbf\xc5V\
+\x8b\xbbF\xa3yU\xe0a\x9d\x87\x1b\x04\x80\x0f\xcb\x11F\x8d\xc2\xec\xeb\xdb\
+\xdb3\xd6nv\x18G\x00\x89\xd4pL\xc2\xa3\x90\x03X6B\xd2!\x00\x88\x92\x93\x92_9\
+\x1e% \xa4p\\\xdfu\xccV\xa7-\x84\x04)\xbbMR:S\xabQ\xba\xdf\x13\x9cZ\xdc5\x1a\
+\xcd\xab\x01\n\xd6\xb8\x0b\x00\xa3\n$\x95N\xa6\xab\x96\x92(n6Z\xc4\xb0\x08\
+\x01j\xd8\x96!\xe3N\xcb\xb0\x9c\xd2tes\xe5W\x05\x13\x01\x11\xac\x19J\x8fub)\
+\x04s\xfc\x92m\x9bq{\x0b\x04#\x94\xd8\xaeG\x00l\xdb\x99*\x15m{\xbf\xe3 A\x8b\
+\xbbF\xa3yE`\xedG\x92\x87\x04\x80\x89!\xab\x96L\'\xad\xb5\x14u\xc20\x8c\x91\
+\x10\x94\x9c\x9a\xb6Ix\xdc\xee\x98\xae[\xaaV\xc3\xd6&\x15\x1db\x02\xba\'\x12\
+Y\x89\x12\x94\x82\xf9\xc5*\x05\x9e\xb4\x83\xad\'\x8f(\x06\xd5\x99\xb9\xca\
+\xb13\x84\xd2}\x9eD\xcd\xa2C!5\x1a\xcd\xd1\x07%g\xcdU\x00@\x0014\xfc\xb1[k)\
+\xea\x84\xadV\x84\x12\x10\x81\x1a\x96m\x88\xb0\xdd2\x1d\xb7<=\r\x00\x9b\x8f\
+\xfe\xd92Q:\xf3\t\xccvb!\x04\xf7\x8b\xd3 \x13\x11u6\xeb\xf7\rh\x16<\xa3<w\
+\x96\x1a\xc6\x01*;h\xcb]\xa3\xd1\xbc\n\xf0\xd6*\x8aXy\xdb\x07\x9a\xed\x86\
+\xe5:\xa5\x93\x00\x10\xb6\xc3v+\xa4\x06\x15B\x9a\xa6i\x12\xdei\xb6]\xdf/MO\
+\x03\xc0\xd6\xc6\xaa\x89!\xf5^K\xa0\xd2\xee\x84\xd4\xb0<o\nE\x84I\xf8\xe4\
+\xc9\x9aI\xc3\x82\x83\xc5\x99y\xc3\xf2\xf7\xbd\x8by\xb4\xb8k4\x9a#\x0eJ\xc6Z\
+\x0f`\xa4\xb7\xdd\x9b>G\x08m7\xdbQ\xc4(%\x88\xc44M\xdb\x84N\xab\xe3\x16\n\
+\xa5j\x15\x00\x04O\x82\x8d\xbb\xc5\xd2\ta\xcc\x84\xad\x86\xe9\x14-\xd3\x04Lx\
+\xd4\x0e\xb6\x1e\x98$,z`\xda\x9e7\xbc\x8e\xf6~\xa2\xc5]\xa3\xd1\x1cqXp\x1f%\
+\x07\x001\xc4\xdbn\xb9%gj\xae\xd5hE1\' $1)\x01\xcb\xc0V\x10\xf8\xc5b\xb1RQ\
+\x9bm>\xbe\xeb\x17\x8f\x19\xceL\xa7\xd34\xbd*Hn\x10l76\xdb\xcdu\x83\x84e\x9f\
+\x02\x80_=\xdb\xbf\xb4\xf5@8\x14\'\xa1\xd1h4{\x84\xe4!k?\x04@@28\xd9\x00\x01\
+w\xfaB\xab\xd1\xea\x84\x89A\x11\xa8IQ\x1a\x04\xc2N{\xaaR)\x14\xbbyz\xe3\xa8M\
+\x89iz\xd5$l\x1av\tE\xe2:Vs\xf3q\xd8\xde0iX\xf6\rD0\xdd\xb2S<\xb6\xaf\xdd\
+\x1b\x8e\x16w\x8dFs\x94a\xc1=\x90\x02\xc80o\xbb\xb4\n\'\xe3\x08c&\x0c\x83\
+\x001)\x91\x040\x89:S\x95i\xaf\xd0u\x9d#b\xd4\xeeXn%\xea\x04\xc4\xae\x00r\
+\xd76\x1b\x1b\x0f\xe2p\xd32\xe2\xa2g \x1e@9\x8e\xd1hq\xd7h4G\x16\xc9Z\xbcS\
+\x87\xa1\x159\x10\x88)iEJJ\x08\'\xd4\xa0 A\x8a$j\x97g\xe7\x1c\xcf\xedn$e\x12\
+\xc5R\x90(\x89,\xa7"\x05w,\xf2\xb4\xbe\x96DO\\\x9b\xfb\x0eUY\x06\x9c\xa99\
+\xcb-\xefg\xefF\xa3\xc5]\xa3\xd1\x1cY\x92\xc6=\x00\x04B\xc4\xc0\xd4\xbe(\xd1\
+\xaeIR@)(5(E\xc9\x19O\xa2\xe9\xe3\'L\xcbR\x9bH)\xc3V\'\xec4\x19\'\xa6\xe5\
+\xa2\x14\x9ec66\xd6\xa2\xb0>\xe5\xa1k\xd2nA=j\xfa\xd3\x87\xc8l\x07-\xee\x1a\
+\x8d\xe6\xa8"\xe2\xad^!=\x1ch\xb6#uL\xef\x84\x00$\x84\x18\x06\x88$\xe6,\x99>\
+~\xd20\x8cn\x0b\x9cG\xed0\n[\x02M\xc34\xa8a\x98Tnm<\x0c;\xf5\xb2\x8f\x96\xb1\
+\xfd\xb0\xf0*\x0b\x03+{\x1c z\x11\x93F\xa39\x9a\xb0\xe0\x1e\x00\x10\x00.`P\
+\x8e04\xbcc\x1cl\x90\xc2\xb2\xa8HB\xce\x92\xda\xc9S\xdb\xca\xcex\xd4\x8e\xa4\
+d\x828\x88\xc4\xb2-\x8a\xbc\xbd\xf9(l?.\xba2\xab\xec\xd4\xf2\xdc\xca\xc2~uk\
+\\\xb4\xe5\xae\xd1h\x8e "\xdc\x10q\x03\x00$\x0eL\xed\x8bH\\i\xcc\x80\xe4\xb6\
+c\xf1\xa8\xc5\xb9\xa8\x9d\xda\xae\x97$\xb9\x88:!gL\x80!E\xe2y\x1e\x8f\xdbQs\
+\xa3\xd5\xae\x97|\xb4\x8d\x1d\xed\x15f.\xe4R\x04\x1f\x06\xb4\xb8k4\x9a\xa3\
+\x07&\xc1}\xf5i`E\x0eD$\xdeq\xa0\x86c\x1a,\x0c$\x92\xda\xa9S\xe9_y\xc2\xa20\
+\xe2\x8cK0\x12\xc6|\xcf\x8b\xa3V\xdc\\ow\x9e\x94}\xb0v*\xbb\xe5U\xed\xc2\xcc\
+\x9ew\xe8\xd9\xd1\xe2\xae\xd1h\x8e\x1a\xbc\xfdX\xb2\x16\x01\x10\x83\xccvDI\
+\xac"\xb1\xab\xa6A\x93\xce\x16\xb5\x9c\xd9Z-\xfd+\x8b\xe2$I\x18\xe3\x12)\x17\
+\xdcu\xec\xb8\x13D\xcd\xf5\x84mU|0w*;\x10\xe2\xcf\x9c\xdf\x8f.=;Z\xdc5\x1a\
+\xcd\xd1\x02%k\xae@/\xd9\xc0\x00o;!\xc49fP\x83\x85\r\xc3v+\xb3\xdb\xa59X\x1c\
+\xb7[-$\x86@\x82(=\xc7ba\xab\x1d<\x12<\x98r\xd12\x08\xee\x9c\x97\xcd\xa6\x08\
+>lhq\xd7h4G\n\xd6z\xa0R\xfb\x8a\xc1\x159$\xb5*\x86U\xe6a`\xf9\xdd\xa41\x8a\
+\xb0\xd5I\xa2H\xa0!\xa5\xa4\x84\x1a\x06aask\xf3!\xcaV\xd1\x1b\xa0\xec\xc4\
+\xb0\xbc\xea\xe9\xbd\xef\xd0sr\xe8&\x014\x1a\x8d\xe6\xb9A\xc9X\xab\x9b\xda\
+\x97\xf1\x01\x1bH$\xc4\xa9%a\xd3\x99*m+;B\x12\xc5I\x14q\xa4\x9c\x0b\xd30\tH\
+\x195\x9f\xd4W\xa3\xa45\xe5B\xbf\xb2\x03\x80_=C\x8d\x03\xa8\xc21&\xdar\xd7h4\
+G\x07\xd6T\xa9}\t\x17(1o\xb6KD\x0e>\xe1\xa4P\xa9\xf8S=w\nb\xd4\x89\xa2(\x92`\
+p\xcem\xdbB\xc9e\xd4l\x06\x0f;q8=El\x13\xfa\x95\xddp\xa6\x9c\xd2\x89}\xe8\
+\xd1s\xa3\xc5]\xa3\xd1\x1c\x11P$\xbc\xfd\x10\x06\xaeZ" %\x84\tZ\xee\xd4\xd4\
+\xf4\x8c\xeby\xdd]\x10\xe2v\'\x8ac.(\x17\xdc\xb1M)\x18\xc6M\x16=lG\x91g\x13\
+\xdf\x19\xa0\xec\x00P\x98>w\x08\xc3\x1f\xb3hq\xd7h4G\x04\xd6\xbc\x8f\x82\x01\
+\x01.aG\x8e0\x02BB\x94 \x12wjz~[\xd9%F\xedN\x1c\xc7\x02\xa9\x90\xd2\xb1M\x94\
+B\xc6M\x11=F\x19Y&\x99r\x07\xe4\x07\x06\x00\xbbP\xb3\xfc\xe9}\xe9\xd3\xf3\
+\xa3\xc5]\xa3\xd1\x1c\x05$\xef\xb0\xf6# }9\xc2\x08\x08\x01\x11CD\x98\x9d\xbf\
+X(us{I)\xe3v;\x8a\x99\x90 \xa54)\xa0\xe0D\x84\x14\x9a@:L\x12\xcb\x00\xdb\x18\
+|,\xafz(\xcaq\x8c\xe6P\xbfVh4\x1a\xcd\x98\xb0\xe0\x1e\xa0\x00\x00!`;G\x18\
+\x01. d\x88\x08~q\xa6P\xea\xce\xa0J!:\x8d \x8a\x05\x97\x04\x81\x1a\x06\x10\
+\xe4\x14\x98[p%kX&\x15\x12l\x03\x8cA\x02i\xbae\xd3>\xa4\xe1\x8fY\xb4\xb8k4\
+\x9a\x89G&M\xde\xd9\x00 \x88\xc0\xe5\xb6\xd9\xce8D\x0c\x01\x81PR\xae\x9d\xed\
+n,d\xd4j1I\x18\x97\x04\x08%\x92HA@\x94\xa6\xa7\x93\xce\xbak\nD\x10\x12\\{\
+\x90\xb7]%m?\xd0\xca\xd7c\xa2\xc5]\xa3\xd1L<Ip\x0f\x00A\xe5\x08\xeb\x99\xed\
+\x89\x80\x98\xa3\xca\x19\xe6\x17\xe7l\xaf\x08\x00(D\xd8jG\x89L\x187\r\x83\
+\x80 \x82\x01\xc8\xf2l\x8d\xb3\x10\xe2\xbai\x900\x01\xd7\x1a,\xe0\xce\xd4\
+\xb1C\x95\xb4}\x04\xda\xe7\xae\xd1h&\x1b\x11=\x11\xd1&\x00 b\x9al \xe1\x90\
+\xa8Z\xd8\x04(5+s\xe7\x00@p\x1e6[L\x00\x13h\x1a\x06\x80$\x92Y\x16\x99\xaa\
+\xce\x02!\xd1\xe6om\x93\xa8\xbf\x16\x1c\xf5\xb0\xd8\x011l\x7f\xfa\xec\xbe\
+\xf6\xed\x05\xd0\xe2\xae\xd1h&\x1aL\xb6n\xa9Oj\xd5\x12\x02D\t\x884\\\x06\xa1\
+r\xec\xbci\xbb"I\xda\xadN\xccA\n\xe1\xd8\xa6\x10\x8cuZ\xd5Z\xd5.\x94\x01 \
+\xd9\xbaeB\x08\x00\x9d\x18]kp\x90\xccT\xedk\x87-i\xfb\x08\xb4\xb8k4\x9a\x89\
+\x843\x1e3\x84x\x9d\xb06!\x06\x17(%J\x84\x88\x81\xcc\x04BZ\x8e_\xac\x9e\x94J\
+\xd9\x19J)-\xcb\x10\x82\x11\xd6\x99>6cyE\x00\x90\xbc#\xda\x0f\x01@H0\x08\xd0\
+A\xd2nyU\xbb0;\xe0\x0f\x87\x15-\xee\x1a\x8df\xc2@\xc4`\xab\xc1\xc1\xb4\xa8\
+\xa4\x9d\x15 \x14\x00DW\xd9Q\xca4U\x18A)fO^\x14q\xd4nG1C!\x85m\x1a(\x05\xb0N\
+\xa56K\xedn\xfdk\x1e\xdcC\x94\x84\xa8\x18\xcaAk\x96\x0eY\xf1\xebq\xd0\x13\
+\xaa\x1a\x8df\xc2\xd8\xdax\xb2U\xdfpL\xea\x91M"C\x02D\x08d\x1c\xc2$\xab\xec\
+\x80RL\x95\x8f\x11 A3\x8c9\x08)\x1d\xcbD\x94D\x84\x95Z-Uv\x11o\xa5E\xb4\x11\
+\x07\xcf\xa3\xba\xa5\x93\xa6S\xdc\xa7\xee\xbd$\xb4\xb8k4\x9a\t\xa3\xd3n\x03\
+\x97\x18\xb7x\xfb\x11\x00E\x80\x98\xa1Z\xa6\x94Qv4L\xcb/\xce\xb5#\xe0\x02PJ\
+\xc7\xa2Rr\x115+\xb3\xb3\x86\xed\xf5\x1aC\xd6\xb8\x03\x00\x84\x80\xc4\x81Y$\
+\x0f{\xf6\xc7ahq\xd7h4\x13\x86_*\x81i&\xcd\xfb("BI\xcc\xb0\x93\xe4\x94]\x9a\
+\xa6Q\x9d\xbb\x90\x08\x871\x0e\x00\xb6E\x05g\x98\x84\xd5\xb99j\xb9iS\xbc\xb3\
+.\x93&\x00 \x02\xe3\x83\x1c2\x00~\xf5\xf4a\xce\xfe8\x0c\xeds\xd7h4\x13F\xb9\
+\\\xe2\x9d-[\xb6\x01\xa8\x90\xd0\x8e0\xebNA)-\xcb)\xcd\x9eO\xa4\xcf\xb9\xa0\
+\x94\x98\x06\xe1I\x8c"\x9e9\xb6C\xd9Qr\x16\xdc\x03@B\x08\x13(\xe4\x80 \x19\
+\xc3.8\xa5\x93\xfb\xd4\xb1\x97\x8a\x16w\x8dF3aP\xc3(z\xb1\x08\x01\x08\x84\
+\x91\x14\x12\xb3\xcan;~y\xf6|\x87Y\x00h\x18\x94\x10\x94,A\xc9f\x8e\x1f\xcb\
+\x052\xf2\xd6*\xf2\x10T*\xe0!f{a\xe6\xfc!\xcf\xfe8\x0c-\xee\x1a\x8df\xc2\x90\
+IS\x84u\x02\x84s\x8c\x92\x1d\xca\xeez\xc5\xe2\xf4\xb9\x88\x19RH\xcb2)E\x1eE\
+\xa6\x81\xa5\xda\x1c5w\xb8VP\xc4\xac\xb5\xa6>s\xb13\x8bd\x0f\xab0{\xf8\xb3?\
+\x0eC\x8b\xbbF\xa3\x990\x92\xc6]@\t\x04:1J)\x01\x80\x10\x8a(]\xbf\xec\x97Ow\
+\x12CJi\x1a\x14@\xf002L(\xd7\xe6\x08\xcdk]\xd2\xb8\x03\x92\x03\x00"\x0c6\xdb\
+\t-L\xcez\xd4~\xb4\xb8k4\x9aIBDOE\xb4\t\x04\x12\x8e\t\x07\xdb\x9d\x02\x00\
+\xceB\xdf\x9f\xf1J\xafub\x82(l\xdb\x02\x94<\xea8\xae]\x9c\x9e%4\xefW\x91IS\
+\x84\xeb\xea3\x130\xa0\x886\x80W\x9e7\xec\xc2\x9e\xf7g\xcf\xd0\xe2\xae\xd1h&\
+\t\xde~\x04\x00\x80\xc8\xd1\x9d;\xfdu\xc7/I\x16&a;a\xb4\x13\x13\x02\xd2v,\
+\x94\x82\x87\x1do\xca-Tf\xc8\xa0\xc0\xf5\xa4q[\xa5|\x94\x12\xd2t4Y\x88\xe9x\
+\x95\x85\xbd\xee\xcb\x9e2\x91\x13\x05\x1a\x8d\xe6\xd5\x04y\xc4\xa3\xa7@\x80\
+\t\xc2D\xc5\xb0K\x84P\x04\x83K7\xe6\x06\x804(\xa2\x14,l\xf9E\x7f\xaa:X\xd9E\
+\xb8!\xe3\x862\xd6\x99\x18XD\x0f\xfc\xea\x19bX{\xdc\x9b\xbdE[\xee\x1a\x8dfbH\
+\x82\xfb \x19\x02F\x89\xc1$\x91\x82K\xc3f\x0c\x05\x12\xd3\x04\x90\x88@x\xbb9\
+5]\xf1\x8bC\x13\xf3\xb2\xe6\n@\xb7\x8e\xc7\xc0\xf0G\xd3-\xb9\xc5\xe3{\xd8\
+\x8d}A\x8b\xbbF\xa3\x99\x0c$k\xf1\xcec \x90$\x10sI\x88d\tK\x04I"\tHL\x02\tc\
+\x04Y\xf5\xd8\x8c\xe5\x0e\xad\x94$\x93\xa6d-\x181\x8f\n\xe0OOF9\x8e\xd1hq\
+\xd7h4\x93A\x12\xdc\x07\x94\x08\xd0I\x10%\xd8E/\xe1\x1c\xb9\x10(\t\x80\xa4\
+\x06\xa5P\x99\x9e5\x1c\x7fT#\x8d;\xaa\x9c\x07\xe780\xfc\xd1)\x1e\xb3\xbc\xca\
+\xde\xf5b\xdf\xd0\xe2\xae\xd1h&\x00\x11\x07\xa2\xb3\x01\x04\xe2\x04\xb9@\tv\
+\xbb\x15\xc61\x03\x00J\x08\x00\x10j\x00\x8a\xca\xec\xcc\xa8F\xc2\r\x99l\r(\
+\xa2\xdd\x83P\xd3\xabNp\xf8c\x16-\xee\x1a\x8df\x02`\xc1=\x04$\x08\\\x80k\x01\
+C\'\xe2\x843\x06@\x00\x10\x10\x10\x00\x11y\xd46\xac!y`P$\xc1\x1d\x00 \x00\tG\
+\x1cd\xb6\xbb\x95\x05#\x93\x9f`\xa2\xd1\xe2\xae\xd1h\x0e;<|\xc2\xa3M\xa2\x8a\
+iP\xa0@\x0c"\xa4\xe5\x08.\x94\xb2\x03 "1Mb:\xde\xd0F\xda\x0fU\xb2\x011$\xfc\
+\x91Z\x9eW>\xb5\x87\xdd\xd8_\xb4\xb8k4\x9aC\x0e\xb2\xe0>\x00 \x80\x90\x08\
+\x00\x08\x84b\xa7\xe8\xb6\xb8?\x1d\xc7RJ\x01\x00\x06%\xb33e\xc3\x1elw\xa3H\
+\xbaA2\x00\x8c\xe3\xc0UK\xfe\xf4\xd9\xfe\x85\xac\x93\xcb\xd1\xe9\x89F\xa39\
+\x92\xf0\xce\xbaH\x02e\xb6\xa7\x91\x8b\x08\x04\xa2\r\xc7\x8dm\xa7`\xf9\xc7\
+\x0c\xd3\xb4l\xd70\x87&\xe6e\xcd\x15\x94\x0c\x00\x10A\x0eJ\xdanz\x15gjn\xcf:\
+q\x00hq\xd7h4\x87\x17D\x91\xf4\xcc\xf6\xec\x14(\x01\x00 ,l\x1a\x16+\x1c\xff\
+\xfa C<mB k\xf0\xd6\x03\xb5\xc9`\xb3\x9d\x90\xc2\xcc\xf9\x97\x7f\xf6\x07\x8a\
+\x16w\x8dFsx\xe1\xed\xc7\x92u\x08\x00\x97\x03\x127\x92ni\xd3A\xca.#\xe0-\xe4\
+\xcd$l\xc8\xa4\xad*\xa3J\x89\x03\xbd\xedN\xf1\xc4\xc4U\xd1\xdb\x15-\xee\x1a\
+\x8d\xe6\x90\x82\x92\'\xc1\n(o\xfb\xa0\xc8E\xc3-;S\xc72;\x08\x90!\xf0\xa6H\
+\x828lFa\x12\xc7\x9c\x0bY\x9d"\x94\x12\xc0\xc1f;1,\x7f\xfa\xcc^\xf6\xe3`\xd0\
+\xe2\xae\xd1h\x0e)\xac\xb5\x86""\xc3\xf2\xad\x13R\xac\xbd\x0e\x00 c\x90mL\
+\x1a,nE\x9dv\x1c\xf3$\x11\\HD@\x00\xcb \x94\x12\x00\xe0rp\xad%\xaf\xf2\xda$V\
+\xd1\xdb\x15-\xee\x1a\x8d\xe60\x82\x82\xb1\xe6\x03Py\x02\xe4\x00\xb3\xdd\xf6\
+\xa6D\x12\xc4\xcd\x15\x10\xed(L\xe2\x84s&U8\r\x10 \x00\x94\x02\x13`\x9a\x84@\
+w\xd5\xd2\xa0*z\xbe{\x84\xc2\x1f\xb3hq\xd7h4\x87\x91\xa4q\x07e\x02\x00L\xf6-\
+8"\xc0\x05<~\xd4\x8aW~[\xf1\xc1\xa0(eW\xd0\xb3\x15\xf1$\x82\x90\xe0\x98\x08\
+\x08\x11\x838\x01B\xc1 \x00\x00\x88@)\x10B\x0b\xb3\xafOh\x15\xbd]\xd1\xe2\
+\xae\xd1h\x0e\x1d"n\xb0\xf6#\x00\x90\x12\xa4\xe8\xf3\x92\x03l\xb6p\xa3\xc1gK\
+\xd4\xa0\x04q\x87\xa6\xa7\xab\x9a\x12A,\x8bz\xbem\x9a\x06ZH\x0cnP R(m\x8f\
+\x99\x8c\x18\xa9X\x13\\\x8ec4Z\xdc5\x1a\xcda\x03\x93\xc6\xddnz/\x91\x9f\x02%\
+\x04"\x06[-\xe9;\xb4\xe0\x12\x04HC\xdf\x11\x01\x00\t\xa5\x96i8\x8ei\xdb\x86c\
+Q\x82"\x8e\x18\xe7\x02\xa4\xe4\x0c\x11\x08!`\x18\xd21I\xd0\xe1\x8c\xc5#\xa2\
+\xe3\'\x1a-\xee\x1a\x8d\xe6\xd0 \x9a \x9b<|*\xa2-  \x06\x85?\x02\xc0fS\n\t\
+\xd3\x1e\xa1\x04$ve\xdd0\xa9\xe3\x98\x8em\xda\xb6a\x10\x94R\xf0$\xe9DBH\x04D\
+ \x04\x89\x8d\x04(2@\xc98\x98\x14\x0b.\x11,\x06\xef\xa8\x05A*\xb4\xb8k4\x9aC\
+\x80d\xec\xe9o\xda\x9d N\xd0\xa6\xdc2\t\xe0\x80\xf0GB\xa0\x13c\xa3\x83\xbeK\
+\\\x1b\x84\x00\x89`\xdb\xb4R\xf1m\x93\x80\x94\x9c\xf3$L\x04\x17R\xaa\x1d\x80\
+\x00Jb\x81?o8\x15!\x90b\x88\xed;D&\x02\x89A\x80\xb3x\xff\xfb\xba?hq\xd7h4\
+\x07\x0e\xb6\xea\xd7\xb7\x9e>\xe5\x1c\x11\xc0\x99\xa2\x83W-\x11\x00\x80\xcd\
+\x16\x02\xc0\x94C\x08!\x96M,\x83X&E\x16\xb7;\xcaH\xef\xad^\xdd\xde\x93\xb4\
+\xa2\x82L\x98al\x10B$\x12\x0b=\xcfL\xd4\x96<\t\xf7\xb3\x9f\xfb\x89\x16w\x8dF\
+s\xc0$ac\xeb\xe9Se\xa7SJL\x03\x10A\xa8\x0c0\xbd\xe0\x16)AH\x14\x92\x14\\:[&\
+\x8eE)\x05\x82(\x11\x05\xe7\x9cu\xa3e\xfa\x9d8\x08\xe0O\xb9\xed\x98r\xce\x01\
+\x10\x88\xe1\xd9\x86\xfa\x13\x05\xe0,\xda\xc7\x8e\xee+Z\xdc5\x1a\xcd\x01\xc3\
+\x92D\xc52r\x01\xbe\x05\x04\x80KD\x04\x89 \x04\x08\t\x08@\x081\rb\xdbd\x8a\
+\x02"J)$\x07\xe5\xb5!0\xba(\x1e\xda$\xb0\xcas1/H\x89\x16IL\xdeB\x01\x00\xf7\
+\x9f\x18\xb1\x00\x00\x0c\xa1IDAT@\x08H\x1e#\xe2\xc0:\xda\x93\x8e\x16w\x8dFs\
+\xc0\xd8n\x11\x08A\x89\\\xa0c\x12b\x18\t\xc3(\xe1\x84\x10\x83\x12\xdb"\x06\
+\x05B\x10%\xa0\x94\\\x02\xf4\x0c\xf4q$\x99\x00\x11I\x87\x8a\x15\xcfr\x01\x08\
+\xf2H\n\x9e\xee\x8a"A\x94\x84\x18{\xd5\xb7\x83\xe3hF\xef\xbf"\x04A\xb0\xb2\
+\xba\x16\x04\xc1\xf8\x1b\xc4q\xdc\xbf\xcb\xca\xea\xdaz\xbd\xbe\x87\'\xfa\xec\
+\xac\xd7\xeb\xcfzV\xcb\xd7o\x8c\x18\x8a\xd1\xbb\\\xfd\xf2\xda\xb3\x9d\xdf\
+\xcb8\xfa\xe1!\x08\x82\xe5\xeb7\x0e\xf0\x04,\xc7\xb3\xfd\x19B\xc8\xccL\xa1<[\
+\xb6\x0b\x05\xcf5=\x9b\xfa\x0eq,\xa0D\xa2\x94B b7\xee\xf1Y\xcdlB\x88\x94\x92\
+Gm\x1e\xb7\x84\x10;\x1a@\xa1"(\x8f\x1e\xdar\x9fH\xe28\xfe\xe8\xe3O\x1c\xc7\
+\x99\x9b\xab\xdd\xbcy+\x08\x82\xef|\xfbO\x1d\xc7\xc9n\xb3|\xfd\xc6\xcf?\xfdl\
+a\xfe\xd4z}\xe3\xed\xa5\xcbKo]\x0e\x82\xe0\xaf>\xfc\xd1\\mv\xbd\xbe\xf1\xado\
+~\xe3\xc2\xf9s\x00\xf0\xc3\x0f\x7f\x04\x00Q\x1c_8\x7f\xee\xbdw\xdfQ\xfb\xaa-\
+?x\xff\xbbs\xb5Z\xff\xd1\x83 \xf8\xf3\xbf\xf8\xcb\x0f\xde\xff\xde\xc2\xfc\
+\xee\xeb\xb6o\xde\xba}\xf5\xea\xb5\x7f\xfbg?\x18\xb3k\xeb\xf5\xfaG\x1f\xff\
+\xccu\x1c\xc7\xb1\xe38\x89\xe2\xf8\xfb\xef\x7f\xb7T*e\xb7\xf9\xe1\x87?ZZ\xba\
+\xac\xce\x7f\xfb@7o\x95J\xa5\xdc\x96\xa3O;\xdd\xe5\xf3+_,\xbdu9\xbb\xf1\xff\
+\xf1\x7f\xfe_\xea\x83\xe38j\xf4Fwd\xd7\xa3\x0f\xa3\xff@\xcf\xda\xc2\x8b\xd3\
+\x08\x9a7o\xdeZ\xbctq\xff\x0f\x9dR\x9d\x9eI\xe8\x13\xd3\x92\x9d\xa0\xcd\x19G\
+D\x02\x80\x12\xb0\xe7qyA\xbf\t\x81\xc1\xbe\x1b\xc34F\xfbt&\x17-\xee\x93\x87R\
+\xf6\xa5\xa5\xb7\xea\xf5\xfa\xca\xca\xea\xc2\xc2\xfc\xe2\xe2\xc5\x8f>\xfe$\
+\xa7\xef++\xab\xdf\xf9\xf6\x9f^8\x7fN)\xf5\xd2[\x97\xbfZ\xbe\xa1\xe4ceu\xed\
+\xca\x95/.\x9c?w\xf3\xd6m\xc7\xb1?x\xff{q\x1c\xff\xf9_\xfc\xe5\xdbK\x97U\x0b\
+\x9f_\xf9\xe2\xed\xa5\xcb\x03\x95\x1d\x00~u\xf5Z\xa9TZ^\xbe>B\xdc\x7f\xfc\
+\x93\xff\xfe\xc1\xfb\xdf\x03\x80q\x1e\x00)A\x10\xfc\xf8\'?M\x1f<\x00\xb0\xb2\
+\xba\xd6\xaf\x98KK\x97\x9f\xa9\xd9\xf1O;\xcb\x7f\xfa\x8f\xffA\x9d\xd2\x7f\
+\xfb\xc9O/\x9c?\xf7\x1cGL\x07\xe1\x99\x0e\xf4\x1cO\x88I\x07y\x984\xee$\x0c\
+\xe38\x02\x00B\x00\xa9#\x91PL\x08\xc8\x17\x16\xf6\xe1\xc7\x050\r\x83\x1e\xd1\
+\xf4\x03G\xb3WG\x9b\xe5\xeb7\x16\x16\xe6WVV\x1b\x8d`i\xe9\xadF#X]]\xbbp\xe1|\
+\xee\xcdZ){\xf6\x9b\x9b\xb7n\xabo\x16\xe6OEq\x1c\x04\xc1\xca\xca\xea\x85\x0b\
+\xe7\x01\xc0q\x1c\xa5\xf5\xd0\xf5\xd2l\x0c\xb3\xe3\x94c\xe7\xfb\xef\x7f\xf7\
+\xe6\xad\xdbYG\xc4\xca\xea\xda\xd5/\xaf-_\xbf\xa16P\xffS\x1b\x94JE\xf5ev\xe3\
+8\x8e\xd5)]\xfd\xf2Z\xfa\'u\x86\xd9\xd3N%u\xbd^\x0f\x82\xe0\xea\x97\xd7\x82 \
+(\x95\xb6W\x9d,_\xbf\x91ma\x18\xc3N{WJ\xa5\x92\xeb8\x8d\xa0\xa9:\x92\xebl\
+\x10\x04ikq\x1c_\xfd\xf2\xda\xd5/\xaf\xc5q\xdc?\x08\xaa\xa7j\x84w=P\x1c\xc7\
+\xaa_i\xe3\xeb\xf5z:\xbc\xe9Xe;\x9e\x8e\x8f\xf2h\xa5\xcd\xa6C\x9d\xb6\x90\
+\xfeIm\xafNx\xfc\x01\xd9\x0bZO\x1fDQ\x02\x08\x84\x10\x04"\xec\x13\xa4\xf4u\
+\xf5?0K\xb8wn\x13\x04@.\xd5\xec\xea\x91C\x8b\xfb\xe4\xb1\xb2\xb2\xaa\xbc\x04\
+\xdf\xfa\xe67\x16\xe6O\xbd\xf7\xee;\xf3\xf3\xa7\x16/]\\YY\x1d\xb8\xfd\xcf?\
+\xfd\xec\xed\xa5\xcb\x00\x10\x04Aj\x15\x96K\xc5F\xd0\xdc\xf1M\xb9\xa4n\xf2\
+\x9f\x7f\xfa\x19\x00\xfc\xe7\xff\xf2_\x7f\xfe\xe9g\xfd\xb7\xfd\xf2\xf5\x1b\
+\x0b\xf3\xa7J\xa5\xd2\xe2\xa5\x8b_-w\x95\xe2\xe6\xad\xdbW\xae|\xa1\xcem\xf9\
+\xfa\x8d\xabW\xbf\x04\x80\xabW\xbfT\xcf\x89+W\xbep\x1c\xe7\xe7\x9f~\xa6D\'\
+\x08\x82\x9f\x7f\xfa\x99\xe38\x9f_\xf9\xe2\xe6\xcd[jK\xe5\xf5V/"\xaa\xcdTz\
+\xd4^W\xae|\xf1\xdf~\xf2\xd3(\x8a\xd5\xe7\xf5\xfa\x06\x00d[P\xdf\x0cc\xe0i\
+\x8fF\x1d\xfd\xa3\x8f?\x01\x00\xe5\xddR}T\xfe.\x00X_\xaf\xff\xd5\x87?J\xf5\
+\xfa\xf3+_\xa8/?\xfa\xf8\x93F\x10d\x07aeu\xed\xea\xd5k\x00\xb0\xbc|\xbd\xff\
+\xd1\x92;\x10\x00\xfc\xf8\'?m4\x02\x00\xf8\xe8\xe3\x9f)u\xfe\xe8\xe3\x9f\xa9\
+\xf1Q\xa3\xd1?t\xe9\xf8\xb8\x83\x86\xfa\xe6\xad\xdb\x9f~\xfa\x8b\xf4\xf4\xa0\
+\xf7\xa2\x10Eq\x14\xc5\xea\xcc\x0f\x90$Iz\xce\x17L\x84\xdf\x08HP\xdf\xe8l>i>\
+m\x05\x1d\x1f\xe0%\xcdv\xa2\xc4.r\xfb;\xc1\x04K^N\xfb\x87\x0c-\xee\x13\xcc8\
+\x06\x97\x92\xa1\xf1=\xb9J\x14\xbe\xff\xfew\xff\xf7\xff\xed\xdf+\xfb1\xb7\
+\xc1W\xcb7\x16\x17/\x02\xc0\xe2\xe2\xc5\xd4\x90\xbcz\xf5\xdaw\xbe\xfd\'Ko]\
+\xfe\xce\xb7\xfft\xe9\xad\xcb\xca\x17\xf1\xc1\xfb\xdf\xcb\x9a\xffo.^Tz\xf4\
+\xd5\xf2\x8d7\x17/\xc6q|\xf3\xd6\xed\xc5\xc5K\xb5Zmq\xf1R*\xb8\xb9\x99\x83\
+\x95\x95\xd5zoZ\xf5\xed\xa5\xcb\xef\xbd\xfbN\xfa4R-|\xf0\xfe\xf7\xd4\x11s;\
+\x8es\xda\xbbr\xf3\xe6m\x00\xf8\xe0\xfd\xeff\xbf\xfc\xd5\xd5k\xdf\x7f\xff\
+\xbbKo]\xce\xba\x8f\x00\xe0\xbdw\xdfQ#\xb0\xb2\xba6W\xabe\x07\xa1^\xaf;\x8e=\
+?\x7f\xea\x83\xf7\xbf7\xd0\xeb\x92=\xd0\xcd[\xb7K\xa5\xe2\xc2\xc2|\xadV\xbbp\
+\xe1\xdc\xcd\x9b\xb7\x1a\xdd\x17\xa0\x92z\x1b\x1b6t\xe9\xf8\xe4\x86\x1a\x00\
+\xae^\xbd\xb6\xb4t\xb9V\xab]\xb8p~\xbd\xbe\x11\x04\x81\xf2\xd1\xbd\xf7\xee;\
+\xef\xbd\xfb\x8ez\xf6\x1f \x96[\xea\xe6\xf4E0,\xc3rm\t\xc0\xb8\xe0BX\xb6A\r\
+\n\xf0\xc2\xc6;!\xe0\x1d\xa7\xe5\xaf\xe3\xd4\xd7\xa8\xb7]+\x95\x00Jq4\xc5]\
+\xfb\xdc\'\x8f\x85\x85\xf9\xe5\xeb7\xde\\\xbc\xf8\xf9\x95/.\\8\x7f\xf3\xe6\
+\xadr\xb9T\xafo\xa46o\xca\xcf?\xfd\xac^\xdfH\xb5\xa9T*\xa5\xa6z#h\x96KE\xf5\
+\r\xc0)\x00h4\x82\xb9\xb9Z\xbd^\x7fs\xf1\xa2\xdafq\xf1\xd2\xd5\xab_f\x1f\x0c\
+\xca\x11\xa1\xe6`\x157o\xdd^\xbctq\xbd^\xdf\xd5S\xbcx\xe9\xe2\x7f\xfe/\xff\
+\xf5\xbdw\xdfY\xbe~\xe3\xdf\xfd\xd9\x0f\xd6\xeb\x1bq\x1c+\xf3\x16\x00\xca\
+\xa5\xa2\xea\xda\xcd\x9b\xb7\x94\xf5Z*\x95r\xcf\xa4\xdc!\xd6\xeb\x1b\xe5\x8c\
+\x7f&\xfb9\xc7\xb0\xd3\x1e}\xc2Ko]\xbep\xfe\xdc_}\xf8\xa38\x8e\xb3O\x8e\xdc\
+\xebN\xfa\xfd\x88\xa7\x8b:\x96\xb2\xbe\xfb\xe7\x87s\x07\n\x82`\xbd\xbe\x11\
+\xf7F\xa6V\xab\xcd\xd5jo/]\xbez\xf5\xda\xcf?\xfd\xec[\xdf\xfc\x86\xe38\xfdC\
+\x07\x99\xf1\xc9\r5\x00\xac\xd7\xeb\xcb\xcb\xd7\xd3\xed\xa38\xae\xd7\xeb\xe9\
+\x05s\xe0^~\xbf4\xdb\xde\xbcmP\x01HLl\x95\x0b\x9e\xed\x95\xb8\x00\xe4\x9c$\
+\x9b<f/\xecv\xc7v<%\xa5o2N)\x8dE\xd9\x10m\xdb\xe8\xa8\x85O<\t\x1d\xbf\xf2rzr\
+\x98\xd0\xe2>y,^\xba\xf8\xd1\xc7\x9f\xbc\xfb\xee;\x00\xb0\xbc|}n\xae6?\x7f\
+\xea\xd3O\x7f\x9130\x95\xcd\x9e\x8d\xeeP^u5\xa1\xea:N\xa9TZX\x98_^\xbe\xbex\
+\xa9kG\xbf\xf7\xee;+\xabk\xcb\xcb\xd7\x95\xaa\xd6\xeb\xf5\xda\xce9\xd5\xe5\
+\xe5\x1b\xdf\xfa\xe67RY\\\xbe~\xe3WW\xaf-^\xbax\xe1\xfc\xb9\x95\xd5\xb5\x85\
+\xf9S\xca"\x1e(s\x8e\xe3\xa83W\xee\x91R\xa9\xe48\x8e\x9a\x04\x8e\xe3X\x19\
+\xa7J\xe3\xca\xe5\xbc\xac\x0fda\xfe\x94r\x1c\xa9\x16R\xb7\xcc\xcd[\xb7\x17\
+\xe6Oe\xcfa\xd8i\xefz\x88R\xa9\xf4\xf6\xd2\xe5\xcf\xaf|\xf1\x9do\xffiv\x18\
+\xaf~yME\x1f\xa5\xa6\xf1\xae,^\xba\xb8\xf4\xd6\xe5\xcf\xaf|\xa1~\x82\x11\x07\
+\x9a\x9f?\xb5\xb2\xb2\xaa\x0c\xff\xf5z\xbd\\*\xa9\xce.^\xbax\xf3\xd6\xed\xe5\
+\xe5\xeb\xea5%7tYrC\xad\xceyq\xf1\x92zj\xaa\x17\x8b\x85\x85\xf9z\xbd\xae\xbe\
+I\xdf\x8dT\xec\xe9\xb0\x89\xf4\xbd\xc3\xb4\x1c\xafr:i\xdc6\xd4\xa2\xd3\xf6c\
+\x96lQ\xc3\x90\x9c\t\xfe\xe2\xca\x0e\x00\xc4\xf1\n\xcdN\xccbI\x00\x11\x8d)\
+\xc7%\xa4\x83*\xa5\x18=\x82A\xee\xa0\xc5}\x12Q7\xf6G\x1f\x7fR*\x95\xe6\xe6j\
+\x8dF\xb0\xb2\xb2\xfa\x9do\xffI\x7f(d\xfa_\x00\xf8O\xff\xf1?\xbc\xb9x\xf1\
+\xaf>\xfc\xd1\xca\xca\xaa\n\x85\x04\xa5SW\xaf\xfd\xf0\xc3\x1fEq\xbcx\xe9bwZ\
+\xf5\xe6\xad\x1f~\xf8#\xc7\xb1\x1bA\xf3\xfb\x99\x07\xc6\xca\xeaZ#\x08\xb2\
+\x8e\x88\x0b\xe7\xcf}~\xe5\x8b\x95\xd5\xb5\xa5\xa5\xcb?\xfe\xc9O\xe7j\xb3\
+\x8d\xa0\xf9\xe6\xe2\xc5\xa5\xb7.\xcf\xd5j?\xfc\xf0G\x8b\xbd\x97\x80\xee\xf6\
+\x17\xce\xff\xf8\'\xff=\x15\xca\xb7\x97.\xff\xf9_\xfc\xa5\xdaK\x05\xe7\x94J\
+\xa5\x0f\xde\xff\xeeG\x1f\xff\xec\xe6\xcd\xdb\x8ec\xab\x83\xaaS\x1dH\xb6\x85\
+\xb4\xfb\xca\xbb\x9d\x8d\xb7\x19v\xda\xe3\x8c\xb6\xf2\xd1g7\xfe\xd67\xbf\xf1\
+\xd1\xc7\x9f|~\xe5\x8bR\xa94W\x9b\x1d\xb1o:\x08q\x1c\x7f\xb5|C\xcds|\x7f\xe7\
+3\xb8\xff@J\x91\xff\xfc/\xfeRm\xff\x9do\xffI\x1c\'\x1f}\xfc\x89\xea\xa6\nW\
+\xed\x1f\xba\\k\xb9\xa1N\x7f\xa08Nj\xb5Y\xf5\xa8\xf8\xf1O~\xaa\xdcA\x00\xa0F\
+[\xfds\xff\xc5\x1d\x00\xca\xb3\x0b\x9b<f\xad\x87&\x95\x04\x88`\xb1`\x00\xa0B\
+g\x00\xc8\x0b9f\x10\xd16;\xe5\xea\\\x94\x00"Z\x849\x18\xaaiT\t\xd6\x914\xdb\
+\x01\x80\xec\xe1L\xb4f\x8f\t\x82 \xf5\xae\x8c\xb9\x8b\xb2ps\xbb\xac\xac\xae9\
+\x8e\x9d\xbd\xa5\xd7\xeb\xf58N\x9e)\xf8/\xd7\xf2\xc0\x03\x8dy>\xe9\t\xe4\xce\
+j j\x10\xe6j\xb3\xa3}\xee{\xc4\xe7W\xbe(\x97K#"\x8b\xd2\xde\xa9\x1e\x8d\x7f\
+\x9e\xb9~\xf5\xff\xd6c\x8ep\x96\xfe\x1f\xba\xff\x9b\x83%\x89\xdaQgK$\x1d@.X\
+\x8c(@r\x00)\x85  \x01T\xdc;\x122\xd0\x98\'\xdd\xff\x0c\x924D4l\x97ZSR"\xb2&\
+\x8a\x04\x80\x08\t\xfe\xec\x1bS\xd5\x13{\xdb\xab\x03B\x8b\xbbF\xf3l|\xf4\xf1\
+\'ss\xb5Z\xad\x16\x04\xc1\xaf\xae^\xfbw\x7f\xf6\x83\x03y\xae\xbc* "J!\x18\
+\xef\xd4E\xe7A3.\xb1\x84q))A\n\x12\x08\x12\x90\x04\xa4A\x91\x02\xa7\x14$\x1a\
+\x84\xa8u\xa7\xa2\xd7@w\x95\x12%\xca\x0b\x03\x00D"A\xea\xf8\xd3g\xa7*\xc7\
+\x0f\xb0s{\x8a\x16w\x8d\xe6\xd9X\xaf\xd7\x97\x97o\xa8i\xd5\xb7\x97.\x1f\xf8l\
+\xe4+B\x12\xdc\x83x\x1d\xcc\xb9\x8d\xa7,\xe2`P"z\x01\x8d\x04\xf0X\xd5\x88\
+\xe3\xa7\x05\x8b\xcb\xc29\x166X\xb8\xc9\x936"P\x02\xbeC\x10@\x9aU\x0e.AA\x08\
+H\xa0\xb6W\xf6\x8b3\xd4\xb0\x0e\xb4O{\x8b\x16w\x8dF3\x01\x04\xeb\xbf\xb5\xf9\
+#\xc7\xab\x05\xa1\x8d\x00\xed\x08#\x86\x84\x10D4)99m<\xd9\xaa\xfbfG"\xe5\xbd\
+\x12\x1f\x080\xe5\x80\xe7\x11@\x00\xb3\x0c\x95\xdf;\xd8.\xec3:\xce]\xa3\xd1L\
+\x00I\x12\xc7LJ\xb6\xe9\xdb\xbc\\0-\xcb4\rbP\xf0lZ.\x18I\xc2\xa4\x88\x9a!f\
+\x95\xdd \xe0\xda\x04$\x00\x02\xb0\x06t\xee\x1cl\x17\xf6\x19\x1d-\xa3\xd1h&\
+\x00\xc1Y\xab\x03&e\x08\x1b\xad\xa8R.\x94<\x0b%J\xd3 \x82\x85\xcd\xf6S)\x05"\
+\xd9\xae\xa6\x8d\xe0:@\xb2\xeb\x9f\xda\xab`\xcd\x80\xf5\xaa\xb8\xd1\xb4[F\
+\xa3\xd1\x1cRP2d\x1d\xe4M\xca\x1b\x8f\xd6\x9fD\x894)\xf8\x0e$\x1c\x01LBM \
+\x04%\x97\x82\x03 "\x91\x08\xbeC\xa0g\xb6W\xa7\xfa\x8ap\x18>T\xdf\x82\xa3\
+\x98\xbd\xbd\x1fm\xb9k4\x9aC\x04J\x86\xbc#\xe3\x86L\x82n\xcc"\x01D\x10\x02\
+\x95P\x87\t\x98\x94\x00p\x90\x1c\x00\x08\x80i\x10\x00\x92\xf0\xed\xdc\xbd\
+\x88\xe0\xe5\xccv\x85\xe8@\xfb.L\x9d\xdf\xd7.\x1d\x10Z\xdc5\x1a\xcd\x81\x82\
+\x02%G\xde\x91IC&Md-\x94\xac\xab\xca=i\x96r;\xa2Q\x15J5\x08I\xa3\xddew\x03\
+\xa4\x94\x10\x02R\x82I\xc1\xb5\xc9\xe0uO\xe1\x03\xf0N\x80\xe1\xefC\xcf\x0e\
+\x16-\xee\x1a\x8df\xdf\x91\t\xf06\xb2\x80G\r\xc9C\x90,\x9b\xa91\x07\x01\x90\
+\x08\x123\xff\xce\x80\x00\x80\xa0\x12\t\xd0\xde\n\xa6\x82;\xc8lO\xf7h\xfe\
+\x16\xca\xbf\x0bG4\x8d{\xca\xff\x0fY\xef?\x965\xa5@\x17\x00\x00\x00\x00IEND\
+\xaeB`\x82' 
 
 def getIDESplashBitmap():
     return BitmapFromImage(getIDESplashImage())
index 54375dd482324f8ac30d126ab9ebc7eb9554f466..5fd24b5c0f1ad9998744dd227d5e07c438381953 100644 (file)
@@ -36,16 +36,16 @@ class MarkerService(wx.lib.pydocview.DocService):
 
         editMenu = menuBar.GetMenu(menuBar.FindMenu(_("&Edit")))
         editMenu.AppendSeparator()
-        editMenu.Append(MarkerService.MARKERTOGGLE_ID, _("Toggle &Marker\tCtrl+M"), _("Toggles a jump marker to text line"))
+        editMenu.Append(MarkerService.MARKERTOGGLE_ID, _("Toggle &Bookmark\tCtrl+M"), _("Toggles a bookmark at text line"))
         wx.EVT_MENU(frame, MarkerService.MARKERTOGGLE_ID, frame.ProcessEvent)
         wx.EVT_UPDATE_UI(frame, MarkerService.MARKERTOGGLE_ID, frame.ProcessUpdateUIEvent)
-        editMenu.Append(MarkerService.MARKERDELALL_ID, _("Clear Markers"), _("Removes all jump markers from selected file"))
+        editMenu.Append(MarkerService.MARKERDELALL_ID, _("Clear Bookmarks"), _("Removes all jump bookmarks from selected file"))
         wx.EVT_MENU(frame, MarkerService.MARKERDELALL_ID, frame.ProcessEvent)
         wx.EVT_UPDATE_UI(frame, MarkerService.MARKERDELALL_ID, frame.ProcessUpdateUIEvent)
-        editMenu.Append(MarkerService.MARKERNEXT_ID, _("Marker Next\tF4"), _("Moves to next marker in selected file"))
+        editMenu.Append(MarkerService.MARKERNEXT_ID, _("Bookmark Next\tF4"), _("Moves to next bookmark in selected file"))
         wx.EVT_MENU(frame, MarkerService.MARKERNEXT_ID, frame.ProcessEvent)
         wx.EVT_UPDATE_UI(frame, MarkerService.MARKERNEXT_ID, frame.ProcessUpdateUIEvent)
-        editMenu.Append(MarkerService.MARKERPREV_ID, _("Marker Previous\tShift+F4"), _("Moves to previous marker in selected file"))
+        editMenu.Append(MarkerService.MARKERPREV_ID, _("Bookmark Previous\tShift+F4"), _("Moves to previous bookmark in selected file"))
         wx.EVT_MENU(frame, MarkerService.MARKERPREV_ID, frame.ProcessEvent)
         wx.EVT_UPDATE_UI(frame, MarkerService.MARKERPREV_ID, frame.ProcessUpdateUIEvent)
 
index 1dd15ebd886179505fe485db6edcc576a127017a..4765a40ead3e317f372be83c6ffebde05351f749 100644 (file)
@@ -124,6 +124,7 @@ class MessageView(Service.ServiceView):
 
 
     def AddLines(self, text):
+        self.GetControl().SetCurrentPos(self.GetControl().GetTextLength())
         self.GetControl().SetReadOnly(False)
         self.GetControl().AddText(text)
         self.GetControl().SetReadOnly(True)
index 75fc67e0d224acd63fafd0dcb17090029473711a..289d4315cb336a476d58db02d16d35c06020711a 100644 (file)
@@ -147,7 +147,11 @@ class OutlineView(Service.ServiceView):
             return
 
         treeCtrl = self.GetControl()
+        
         parentItem = treeCtrl.GetRootItem()
+        if not parentItem:
+            return
+            
         if expanded[0] != treeCtrl.GetItemText(parentItem):
             return
 
@@ -157,8 +161,7 @@ class OutlineView(Service.ServiceView):
                 treeCtrl.Expand(child)
             (child, cookie) = treeCtrl.GetNextChild(parentItem, cookie)
 
-        if parentItem:
-            treeCtrl.EnsureVisible(parentItem)
+        treeCtrl.EnsureVisible(parentItem)
 
 
 class OutlineTreeCtrl(wx.TreeCtrl):
@@ -267,7 +270,7 @@ class OutlineTreeCtrl(wx.TreeCtrl):
 
         if self.ItemHasChildren(item):
             child, cookie = self.GetFirstChild(item)
-            while child and child.IsOk():
+            while child.IsOk():
                 self.FindDistanceToTreeItems(child, position, distances, items)
                 child, cookie = self.GetNextChild(item, cookie)
         return False
diff --git a/wxPython/samples/ide/activegrid/tool/PHPDebugger.py b/wxPython/samples/ide/activegrid/tool/PHPDebugger.py
new file mode 100644 (file)
index 0000000..8dd0d17
--- /dev/null
@@ -0,0 +1,2105 @@
+#---------------------------------------------------------------------------
+# Name:         PHPDebugger.py
+# Purpose:      php dbg client and supporting code
+# Author:       Matt Fryer, Kevin Wang
+# Created:      2/1/06
+# Copyright:    (c) 2006 ActiveGrid, Inc.
+# License:      wxWindows License
+#---------------------------------------------------------------------------
+
+
+import os
+import socket
+import sys
+import threading
+import traceback
+import wx
+import DebuggerService
+import activegrid.util.sysutils as sysutils
+
+
+DBGC_REPLY                              = 0x0               # reply to previous DBGA_REQUEST request
+DBGC_STARTUP                            = 0x0001            # script startup
+DBGC_END                                = 0x0002            # script done
+DBGC_BREAKPOINT                         = 0x0003            # user definded breakpoint occured
+DBGC_STEPINTO_DONE                      = 0x0004            # step to the next statement is completed
+DBGC_STEPOVER_DONE                      = 0x0005            # step to the next statement is completed
+DBGC_STEPOUT_DONE                       = 0x0006            # step to the next statement is completed
+DBGC_EMBEDDED_BREAK                     = 0x0007            # breakpoint caused by DebugBreak() function
+DBGC_ERROR                              = 0x0010            # error occured
+DBGC_LOG                                = 0x0011            # logging support
+DBGC_SID                                = 0x0012            # send SID
+DBGC_PAUSE                              = 0x0013            # pause current session as soon as possible
+DBGC_AG_SHUTDOWN_REQ                    = 0x0201            # special ActiveGrid UI shutdown listening thread command
+
+
+FRAME_STACK                             = 100000            # "call:stack" - e.g. backtrace
+FRAME_SOURCE                            = 100100            # source text
+FRAME_SRC_TREE                          = 100200            # tree of source files
+FRAME_RAWDATA                           = 100300            # raw data or string
+FRAME_ERROR                             = 100400            # error notification
+FRAME_EVAL                              = 100500            # evaluating/watching
+FRAME_BPS                               = 100600            # set/remove breakpoint
+FRAME_BPL                               = 100700            # breakpoint(s) request = get the list
+FRAME_VER                               = 100800            # version request
+FRAME_SID                               = 100900            # session id info
+FRAME_SRCLINESINFO                      = 101000            # source lines info
+FRAME_SRCCTXINFO                        = 101100            # source contexts info
+FRAME_LOG                               = 101200            # logging
+FRAME_PROF                              = 101300            # profiler
+FRAME_PROF_C                            = 101400            # profiler counter/accuracy
+FRAME_SET_OPT                           = 101500            # set/update options
+
+
+DBGF_STARTED                            = 0x0001            # debugger has been started
+DBGF_FINISHED                           = 0x0002            # DBGC_END notification has been sent
+DBGF_WAITACK                            = 0x0004            # awaiting replay|request
+DBGF_UNSYNC                             = 0x0008            # protocol has been unsynchronized
+DBGF_REQUESTPENDING                     = 0x0010            # Debug session request pending
+DBGF_REQUESTFOUND                       = 0x0020            # Debug session request found
+DBGF_REJECTIONFOUND                     = 0x0040            # DBGSESSID=-1 found - session rejection
+
+
+E_ERROR                                 = 1 << 0
+E_WARNING                               = 1 << 1
+E_PARSE                                 = 1 << 2
+E_NOTICE                                = 1 << 3
+E_CORE_ERROR                            = 1 << 4
+E_CORE_WARNING                          = 1 << 5
+E_COMPILE_ERROR                         = 1 << 6
+E_COMPILE_WARNING                       = 1 << 7
+E_USER_ERROR                            = 1 << 8
+E_USER_WARNING                          = 1 << 9
+E_USER_NOTICE                           = 1 << 10
+
+
+BPS_DELETED                             = 0
+BPS_DISABLED                            = 1
+BPS_ENABLED                             = 2
+BPS_UNRESOLVED                          = 0x100
+
+
+DBG_SYNC                                = 0x5953
+DBG_SYNC2_STR                           = chr(0) + chr(0) + chr(89) + chr(83)
+RESPONSE_HEADER_SIZE                    = 16
+
+
+_VERBOSE = False
+def myprint(format, vlist=None):
+    if _VERBOSE:
+        if vlist:
+            print format % vlist
+        else:
+            print format
+
+
+#
+# 4 Char's to an Integer
+#
+def C4ToInt(ch, startPos):
+    retval = 0
+    pos    = startPos
+
+    retval = retval + (CharToInt(ch[pos]) << 24)
+    pos    = pos + 1
+    retval = retval + (CharToInt(ch[pos]) << 16)
+    pos    = pos + 1
+    retval = retval + (CharToInt(ch[pos]) << 8)
+    pos    = pos + 1
+    retval = retval + (CharToInt(ch[pos]) << 0)
+
+    return retval
+
+
+def CharToInt(ch):
+    return int((ord(ch) & 0x00FF));
+
+
+#
+# An Integer to 4 Char's
+#
+def IntToC4(num):
+    retval  = chr((num >> 24) & 0x00FF)
+    retval += chr((num >> 16) & 0x00FF)
+    retval += chr((num >> 8 ) & 0x00FF)
+    retval += chr((num >> 0 ) & 0x00FF)
+
+    return retval
+
+
+DBGA_CONTINUE                           = IntToC4(0x8001)
+DBGA_STOP                               = IntToC4(0x8002)
+DBGA_STEPINTO                           = IntToC4(0x8003)
+DBGA_STEPOVER                           = IntToC4(0x8004)
+DBGA_STEPOUT                            = IntToC4(0x8005)
+DBGA_IGNORE                             = IntToC4(0x8006)
+DBGA_REQUEST                            = IntToC4(0x8010)
+
+
+def getCommandString(code):
+    if code == DBGC_REPLY:
+        return "REPLY"
+    elif code == DBGC_STARTUP:
+        return "STARTUP"
+    elif code == DBGC_END:
+        return "END"
+    elif code == DBGC_BREAKPOINT:
+        return "BREAKPOINT"
+    elif code == DBGC_STEPINTO_DONE:
+        return "STEPINTO DONE"
+    elif code == DBGC_STEPOVER_DONE:
+        return "STEPOVER DONE"
+    elif code == DBGC_STEPOUT_DONE:
+        return "STEPOUT DONE"
+    elif code == DBGC_EMBEDDED_BREAK:
+        return "EMBEDDED BREAK"
+    elif code == DBGC_PAUSE:
+        return "PAUSE"
+    elif code == DBGC_ERROR:
+        return "ERROR"
+    elif code == DBGC_LOG:
+        return "LOG"
+    elif code == DBGC_SID:
+        return "SEND SID"
+    elif code == DBGC_AG_SHUTDOWN_REQ:
+        return "AG SHUTDOWN REQ"
+
+
+def reportFlags(flagsValue):
+    flagsRetVal = ""
+    if flagsValue & DBGF_STARTED:                  # debugger has been started
+        flagsRetVal += "started+"
+    if flagsValue & DBGF_FINISHED:                 # DBGC_END notification has been sent
+        flagsRetVal += "finished+"
+    if flagsValue & DBGF_WAITACK:                  # awaiting replay|request
+        flagsRetVal += "awaiting ack+"
+    if flagsValue & DBGF_UNSYNC:                   # protocol has been unsynchronized
+        flagsRetVal += "protocol unsynchronized+"
+    if flagsValue & DBGF_REQUESTPENDING:           # Debug session request pending
+        flagsRetVal += "request pending+"
+    if flagsValue & DBGF_REQUESTFOUND:             # Debug session request found
+        flagsRetVal += "request found+"
+    if flagsValue & DBGF_REJECTIONFOUND :          # DBGSESSID=-1 found - session rejection
+        flagsRetVal += "session rejection+"
+    return flagsRetVal
+
+
+def getErrorTypeString(code):
+    if code == E_ERROR:
+        return "[Error]"
+    elif code == E_WARNING:
+        return "[Warning]"
+    elif code == E_PARSE:
+         return "[Parse Error]"
+    elif code == E_NOTICE:
+        return "[Notice]"
+    elif code == E_CORE_ERROR:
+        return "[Core Error]"
+    elif code == E_CORE_WARNING:
+        return "[Core Warning]"
+    elif code == E_COMPILE_ERROR:
+        return  "[Compile Error]"
+    elif code == E_COMPILE_WARNING:
+        return  "[Compile Warning]"
+    elif code == E_USER_ERROR:
+        return  "[User Error]"
+    elif code == E_USER_WARNING:
+        return  "[User Warning]"
+    elif code == E_USER_NOTICE:
+        return  "[User Notice]"
+    else:
+        return "[Unexpected Error]"
+
+
+class ResponseHeader(object):
+    def __init__(self, conn, blocking = False):
+        self.isValid = False
+        receivedData = conn.recv(RESPONSE_HEADER_SIZE, blocking)
+
+        if not receivedData:
+            myprint("Tried to get %d bytes of PHP DBG header, got None\n" % RESPONSE_HEADER_SIZE)
+            return
+        elif len(receivedData) != RESPONSE_HEADER_SIZE:
+            myprint("Tried to get %d bytes of PHP DBG header, got %d\n" % (RESPONSE_HEADER_SIZE, len(receivedData)))
+            return
+
+        self.sync    = C4ToInt(receivedData, 0)
+        self.command = C4ToInt(receivedData, 4)
+        self.flags   = C4ToInt(receivedData, 8)
+        self.toRead  = C4ToInt(receivedData, 12)
+
+        myprint("ResponseHeader: sync=%x, command=%s, flags=(%s), toRead=%s\n", (self.sync, getCommandString(self.command), reportFlags(self.flags), self.toRead))
+        if self.sync != DBG_SYNC:
+            myprint("Sync wrong for header! Expected %x, got %s\n" % (DBG_SYNC, self.sync))
+            return
+
+        self.isValid = True
+
+
+class ResponsePacketFrame(object):
+    def __init__(self, conn, size, data, blocking = False):
+        self.isValid      = False
+        self.conn         = conn
+        self.data         = ''
+
+        if data:
+            self.data     = data
+            newlyReceived = False
+        elif conn:
+            newlyReceived = True
+
+            sizeToReceive = size
+            while True:
+                oneChunk  = conn.recv(sizeToReceive, blocking)
+                sizeReceived = len(oneChunk)
+                if sizeReceived > 0:
+                    self.data = self.data + oneChunk
+
+                if sizeReceived < sizeToReceive:
+                    sizeToReceive = sizeToReceive - sizeReceived
+                    continue
+                else:
+                    break
+
+            if len(self.data) != size:
+                myprint("Expected to get %d bytes of a PHP DBG packet, got %d\n" % (size, len(self.data)))
+                return
+        else:
+            return
+
+        self.frameName = C4ToInt(self.data, 0)
+        self.frameSize = C4ToInt(self.data, 4)
+        if newlyReceived:
+            myprint("Newly received ResponsePacketFrame: frameName=%d, frameSize=%d", (self.frameName, self.frameSize))
+        else:
+            myprint("Created from existing ResponsePacketFrame: frameName=%d, frameSize=%d", (self.frameName, self.frameSize))
+
+        if self.frameSize == 0:
+            return
+
+        self.currPos      = 8
+        self.totalDataLen = len(self.data)
+        self.length       = 8 + self.frameSize
+        self.isValid      = True
+        myprint("new ResponsePacketFrame: currPos=%s, totalDataLen=%s, length=%s", (repr(self.currPos), repr(self.totalDataLen), repr(self.length)))
+        return
+
+    def getNextInt(self):
+        myprint("getNextInt(): currPos=%s, totalDataLen=%s, length=%s", (repr(self.currPos), repr(self.totalDataLen), repr(self.length)))
+        if self.isValid and self.currPos + 4 <= self.length:
+            val = C4ToInt(self.data, self.currPos)
+            self.currPos = self.currPos + 4
+            myprint("getNextInt(): got an integar: %s", repr(val))
+            return val
+        else:
+            return self._errorReturn("getNextInt(): no more integar available with current frame: ")
+
+    def getNextString(self, strLen):
+        endPos = self.currPos + strLen
+        if self.isValid and endPos <= self.length:
+            #
+            # Trim the ending '\0'.  TODO: confirm this applies to all raw string data.
+            #
+            str          = self.data[self.currPos:endPos - 1]
+            self.currPos = endPos
+            myprint("getNextString(): got a string: %s", str)
+            return str
+        else:
+            return self._errorReturn("getNextString(): no more string available with current frame: ")
+
+    def getNextFrame(self, useAbsolutePos = False):
+        if useAbsolutePos:
+            #
+            # Skip this frame's header (8 bytes for frameSize and frameSize) and frame data (frameSize).
+            #
+            self.currPos = self.length
+
+        if self.isValid and self.currPos < self.totalDataLen:
+            return ResponsePacketFrame(None, None, self.data[self.currPos:])
+        else:
+            return self._errorReturn("getNextFrame(): no more frame available with current frame: ")
+
+    def _errorReturn(self, preMsg = ''):
+        myprint(preMsg + "frameName=%s, frameSize=%s, totalDataLen=%s, length=%s, currPos:%s", (repr(self.frameName), repr(self.frameSize), repr(self.totalDataLen), repr(self.length), repr(self.currPos)))
+        self.isValid = False
+        return None
+
+
+class PHPDBGFrame(object):
+    FRAME_HEADER_SIZE = 8
+    def __init__(self, frameType):
+        self.frameType = IntToC4(frameType)
+        self.frameData = ""
+
+    def addInt(self, intVal):
+        self.frameData = self.frameData + IntToC4(intVal)
+
+    def addChar(self, charVal):
+        self.frameData = self.frameData + charVal
+
+    def addStr(self, string):
+        #
+        # Add the trailing '\0'.
+        #
+        self.frameData = self.frameData + string + '\0'
+
+    def getSize(self):
+        return len(self.frameData) + PHPDBGFrame.FRAME_HEADER_SIZE
+
+    def writeFrame(self, conn):
+        header = self.frameType + IntToC4(len(self.frameData))
+        conn.sendall(header)
+        conn.sendall(self.frameData)
+
+
+class PHPDBGPacket(object):
+    def __init__(self, packetType):
+        self.header     = DBG_SYNC2_STR + packetType
+        self.frames     = []
+        self.packetSize = 0
+
+    def addFrame(self, frame):
+        self.frames.append(frame)
+        self.packetSize += frame.getSize()
+
+    def sendPacket(self, conn, flags = 0):
+        self.header += IntToC4(flags)
+        self.header += IntToC4(self.packetSize)
+        conn.sendall(self.header)
+        for frame in self.frames:
+            frame.writeFrame(conn)
+
+
+class PHPDBGException(Exception):
+    def __init__(self, msg = None, cause = None):
+        if (msg == None):
+            Exception.__init__(self)
+        elif (cause == None):
+            Exception.__init__(self, msg)
+        else:
+            Exception.__init__(self, "PHPDBGException: message:%s\n, cause:%s" % (msg, cause))
+
+
+class PHPDBGConnException(PHPDBGException):
+    pass
+
+
+class PHPDebuggerCallback(object):
+    ACTION_NONE              = 0
+    ACTION_STOP              = 1
+    ACTION_LISTEN            = 2
+
+    def __init__(self, ui, service, lsnrHosti, lsnrPorti):
+        self.ui              = ui
+        self.service         = service
+        self.lsnrHost        = lsnrHosti
+        self.lsnrPort        = lsnrPorti
+        self.lsnrThr         = None
+        self.lsnrAction      = PHPDebuggerCallback.ACTION_NONE
+        self.clearInternals()
+        self.initLsnrThr()
+
+
+    ############################################################################
+    #                      Public callback functions begin
+    #
+    def Start(self):
+        self.lsnrThr.start()
+
+    def ShutdownServer(self, stopLsnr = True):
+        #
+        # First to tell php debugger to stop execution of the current PHP
+        # program.  Disconnect with php dbg module too.
+        #
+        self.stopPhpDbg()
+
+        #
+        # Stop debug listener.
+        #
+        if stopLsnr:
+            self.stopLsnr()
+
+    def BreakExecution(self):
+        reqPacket = PHPDBGPacket(IntToC4(DBGC_PAUSE))
+
+        try:
+            reqPacket.sendPacket(self.lsnrThr)
+            self.awaitAndHandleResponse()
+        except PHPDBGConnException:
+            self.currConnFinished()
+            return
+
+        self.ui.LoadPHPFramesList(self.stackList)
+        return
+
+    def SingleStep(self):
+        reqPacket = PHPDBGPacket(DBGA_STEPINTO)
+
+        try:
+            reqPacket.sendPacket(self.lsnrThr)
+            self.lastCommand = DBGA_STEPINTO
+            self.awaitAndHandleResponse(blocking = True)
+        except PHPDBGConnException:
+            self.currConnFinished()
+            return
+
+        self.ui.LoadPHPFramesList(self.stackList)
+        return
+
+    def Next(self):
+        reqPacket = PHPDBGPacket(DBGA_STEPOVER)
+
+        try:
+            reqPacket.sendPacket(self.lsnrThr)
+            self.lastCommand = DBGA_STEPOVER
+            self.awaitAndHandleResponse(blocking = True)
+        except PHPDBGConnException:
+            self.currConnFinished()
+            return
+
+        self.ui.LoadPHPFramesList(self.stackList)
+        return
+
+    def Continue(self):
+        reqPacket = PHPDBGPacket(DBGA_CONTINUE)
+
+        try:
+            reqPacket.sendPacket(self.lsnrThr)
+            self.lastCommand = DBGA_CONTINUE
+            self.awaitAndHandleResponse(blocking = True)
+        except PHPDBGConnException:
+            self.currConnFinished()
+            return
+
+        self.ui.LoadPHPFramesList(self.stackList)
+        return
+
+    def Return(self):
+        reqPacket = PHPDBGPacket(DBGA_STEPOUT)
+
+        try:
+            reqPacket.sendPacket(self.lsnrThr)
+            self.lastCommand = DBGA_STEPOUT
+            self.awaitAndHandleResponse(blocking = True)
+        except PHPDBGConnException:
+            self.currConnFinished()
+            return
+
+        self.ui.LoadPHPFramesList(self.stackList)
+        return
+
+    def PushBreakpoints(self, noRemove = False):
+        tmpList = []
+        bps     = self.service.GetMasterBreakpointDict()
+        for fileName in bps.keys():
+            if fileName.endswith('.php'):
+                lines = bps[fileName]
+                if lines:
+                    for lineNo in lines:
+                        if lineNo:
+                            #
+                            # A tuple (fileName, lineNo) is an item which is
+                            # used as a key in self.bpDict.
+                            #
+                            tmpList.append(self.createBpKey(fileName, lineNo))
+                            myprint("PushBreakpoints(): global breakpoint \'%s:%i\'", (fileName, lineNo))
+
+        #
+        # Check to see if we have any new breakpoints added.
+        #
+        for oneKey in tmpList:
+            if not self.bpDict.has_key(oneKey):
+                #
+                # A new breakpoint.
+                #
+                newBp = BreakPoint(self, oneKey[0], oneKey[1])
+                newBp.addSelf()
+                self.bpDict[oneKey] = newBp
+                myprint("PushBreakpoints(): newly added global breakpoint \'%s:%i\'", (oneKey[0], oneKey[1]))
+
+        if noRemove:
+            return
+
+        #
+        # Check to see if any bp that is in our list, but not in the latest
+        # global list.  If so, it must have been removed recently in the
+        # global one.  Remove it from our list and tell php debugger to do
+        # so as well.
+        #
+        toRemoveList = []
+        for oneKey in self.bpDict.keys():
+            if tmpList.count(oneKey) == 0:
+                toRemoveList.append((oneKey, self.bpDict[oneKey]))
+                myprint("PushBreakpoints(): recently removed global breakpoint \'%s:%i\'", (oneKey[0], oneKey[1]))
+
+        for bp in toRemoveList:
+            bp[1].removeSelf()
+            del self.bpDict[bp[0]]
+            myprint("PushBreakpoints(): successfully removed breakpoint \'%s:%i\' from both our local list and php debugger", (bp[0][0], bp[0][1]))
+
+        return
+    #
+    #                      Public callback functions end
+    ############################################################################
+
+
+    def newConnEventHandler(self):
+        #
+        # Ok, we've got a connection from the php debugger, and some initial
+        # frame data from it.  Everything is ready and let's make some initial
+        # actions.
+        #
+        self.clearInternals()
+
+        try:
+            self.awaitAndHandleResponse(self.lsnrThr.getConnHeader())
+        except PHPDBGConnException:
+            self.currConnFinished()
+            return
+
+        self.PushBreakpoints(True)
+        self.ui.LoadPHPFramesList(self.stackList)
+
+    #
+    # This could be called when this object is constructed or when self is
+    # re-initialized after getting a new dbg module connection as a new
+    # session.
+    #
+    def clearInternals(self):
+        self.stackList       = []
+        self.errStackList    = []
+        self.stackFrameIndex = 0
+        self.isErrStack      = False
+        self.errStr          = ''
+        self.modList         = []
+        self.stopOnError     = True
+        self.lastCommand     = None
+        self.evalRet         = ''
+        self.modDict         = {}
+        self.bpDict          = {}
+        self.rawDataDict     = {}
+        self.sessID          = 0
+        self.sessType        = 0
+        self.sessEnded       = False
+        self.frameCounter    = 1000
+        self.variableList    = []
+        self.verMajor        = 0
+        self.verMinor        = 0
+        self.verDesc         = None
+
+    def initLsnrThr(self):
+        self.actionEvent = threading.Event()
+        self.lsnrThr     = PHPDBGLsnrThr(self, self.lsnrHost, self.lsnrPort, self.actionEvent, self.ui)
+
+    def awaitAndHandleResponse(self, header = None, blocking = False, disable = True, stopping = False):
+        if disable:
+            self.ui.DisableWhileDebuggerRunning()
+
+        while self.readResponse(header, blocking) != 0:
+            myprint("Waiting for response")
+
+        if stopping:
+            self.ui.DisableAfterStop()
+        else:
+            self.ui.EnableWhileDebuggerStopped()
+
+    def requestDBGVersion(self):
+        #TODO: necessary?
+        pass
+
+    def getSourceTree(self):
+        #TODO: necessary?
+        pass
+
+    def addDBGModName(self):
+        #TODO: necessary?
+        pass
+
+    def getNextFrameCounter(self):
+        self.frameCounter = self.frameCounter + 1
+        return self.frameCounter
+
+    def getVariables(self, stack):
+        self.variableList = []
+
+        reqPacket = PHPDBGPacket(DBGA_REQUEST)
+        reqFrame  = PHPDBGFrame(FRAME_EVAL)
+
+        reqFrame.addInt(0)
+        reqFrame.addInt(stack.getFrameScopeId())
+        reqPacket.addFrame(reqFrame)
+        myprint("PHPDebuggerCallback::getVariables(): about to send eval request")
+
+        try:
+            reqPacket.sendPacket(self.lsnrThr)
+            self.awaitAndHandleResponse(disable = False)
+        except PHPDBGConnException:
+            self.currConnFinished()
+            return self.variableList
+
+        myprint("PHPDebuggerCallback::getVariables(): evalRet=%s", self.evalRet)
+        evalStr = PHPDBGEvalString(stack, self.evalRet)
+        if evalStr:
+            self.variableList = evalStr.getVars()
+        myprint("PHPDebuggerCallback::getVariables(): about to return")
+
+        return self.variableList
+
+    def evalBlock(self, stack, evalStr):
+        reqPacket = PHPDBGPacket(DBGA_REQUEST)
+        reqFrame1 = PHPDBGFrame(FRAME_EVAL)
+        reqFrame2 = PHPDBGFrame(FRAME_RAWDATA)
+
+        frameID = self.getNextFrameCounter()
+        reqFrame1.addInt(frameID)
+        reqFrame1.addInt(1)
+
+        reqFrame2.addInt(frameID)
+        reqFrame2.addInt(len(evalStr) + 1)
+        reqFrame2.addStr(evalString)
+
+        reqPacket.addFrame(reqFrame2)
+        reqPacket.addFrame(reqFrame1)
+
+        try:
+            reqPacket.sendPacket(self.lsnrThr)
+            self.awaitAndHandleResponse(disable = False)
+        except PHPDBGConnException:
+            self.currConnFinished()
+            return None
+
+        evalStr = PHPDBGEvalString(stack, self.evalRet)
+
+        return evalStr.getVars()
+
+    def getBPUnderHit(self):
+        for bp in self.bpDict.values():
+            if bp.isUnderHit():
+                return bp
+
+        return None
+
+    def getRawFrameData(self, frameNo):
+        if self.rawDataDict.has_key(frameNo):
+            #
+            # Once the frameData is consumed, remove it from rawDataDict.
+            #
+            return self.rawDataDict.pop(frameNo)
+        else:
+            #
+            # TODO: do we need to handle the case when the raw frame data hasn't
+            # been received before?
+            #
+            return None
+
+    def getModByNum(self, modNum):
+        if self.modDict.has_key(modNum):
+            return self.modDict[modNum]
+        else:
+            return None
+
+    def getModByFileName(self, fileName):
+        for mn, fn in self.modDict.iteritems():
+            if fn == fileName:
+                return mn
+
+        return 0
+
+    def setMod(self, modNum, fileName):
+        if modNum != 0 and fileName:
+            self.modDict[modNum] = fileName
+
+        return
+
+    def readResponse(self, headeri = None, blockingi = False):
+        inHeader        = headeri
+        header          = None
+        cmdReceived     = 0
+        isFirstPacket   = True
+        blocking        = blockingi
+        self.isErrStack = False
+        self.rawDataDict.clear()
+
+        while True:
+            #
+            # If we have already received the first packet, we can't block any
+            # more.
+            #
+            if not isFirstPacket:
+                blocking = False
+
+            #
+            # If this is the first loop and we have a non-empty header passed in, use it.  Otherwise,
+            # read in a new header.  For subsequent loops, inHeader is None so we always read a new
+            # header from the wire.
+            #
+            if inHeader:
+                header   = inHeader
+                inHeader = None
+            else:
+                header   = ResponseHeader(self.lsnrThr, blocking)
+
+            if not header.isValid:
+                return 0
+
+            cmdReceived = header.command
+            frame       = ResponsePacketFrame(self.lsnrThr, header.toRead, None, blocking)
+            if not frame.isValid:
+                return 0
+
+            isFirstPacket     = False
+            isFirstStackFrame = True
+            while frame and frame.isValid:
+                frameName = frame.frameName
+                if frameName == FRAME_STACK:
+                    if self.isErrStack:
+                        self.errStackList = self.handleRespFrameStack(self.errStackList, frame, isFirstStackFrame)
+                    else:
+                        self.stackList = self.handleRespFrameStack(self.stackList, frame, isFirstStackFrame)
+
+                    if isFirstStackFrame:
+                        isFirstStackFrame = False
+                elif frameName == FRAME_SOURCE:
+                    self.handleRespFrameSource(frame)
+                elif frameName == FRAME_SRC_TREE:
+                    self.handleRespFrameSrcTree(frame)
+                elif frameName == FRAME_RAWDATA:
+                    self.handleRespFrameRawdata(frame)
+                elif frameName == FRAME_ERROR:
+                    self.handleRespFrameError(frame)
+                elif frameName == FRAME_EVAL:
+                    self.handleRespFrameEval(frame)
+                elif frameName == FRAME_BPS:
+                    self.handleRespFrameBps(frame)
+                elif frameName == FRAME_BPL:
+                    self.handleRespFrameBpl(frame)
+                elif frameName == FRAME_VER:
+                    self.handleRespFrameVer(frame)
+                elif frameName == FRAME_SID:
+                    self.handleRespFrameSid(frame)
+                elif frameName == FRAME_SRCLINESINFO:
+                    self.handleRespFrameSrclinesinfo(frame)
+                elif frameName == FRAME_SRCCTXINFO:
+                    self.handleRespFrameSrcctxinfo(frame)
+                elif frameName == FRAME_LOG:
+                    self.handleRespFrameLog(frame)
+                elif frameName == FRAME_PROF:
+                    self.handleRespFrameProf(frame)
+                elif frameName == FRAME_PROF_C:
+                    self.handleRespFrameProfC(frame)
+                elif frameName == FRAME_SET_OPT:
+                    self.handleRespFrameSetOpt(frame)
+                else:
+                    self.handleRespFrameUnknown(frame)
+                    return 0
+
+                #
+                # After handling of this frame, force frame to point to the
+                # next one based on current frame's absolute size.
+                #
+                frame = frame.getNextFrame(True)
+
+            if cmdReceived == DBGC_REPLY:
+                self.handleRespCmdReply()
+            elif cmdReceived == DBGC_STARTUP:
+                self.handleRespCmdStartup()
+            elif cmdReceived == DBGC_END:
+                self.handleRespCmdEnd()
+            elif cmdReceived == DBGC_BREAKPOINT:
+                self.handleRespCmdBreakpoint()
+                cmdReceived = 0
+            elif cmdReceived == DBGC_STEPINTO_DONE:
+                self.handleRespCmdStepintoDone()
+            elif cmdReceived == DBGC_STEPOVER_DONE:
+                self.handleRespCmdStepoverDone()
+            elif cmdReceived == DBGC_STEPOUT_DONE:
+                self.handleRespCmdStepoutDone()
+            elif cmdReceived == DBGC_EMBEDDED_BREAK:
+                self.handleRespCmdEmbeddedBreak()
+            elif cmdReceived == DBGC_PAUSE:
+                self.handleRespCmdPause()
+            elif cmdReceived == DBGC_ERROR:
+                self.handleRespCmdError()
+            elif cmdReceived == DBGC_LOG:
+                self.handleRespCmdLog()
+            elif cmdReceived == DBGC_SID:
+                self.handleRespCmdSid()
+            else:
+                self.handleRespCmdUnknown()
+
+        return cmdReceived
+
+    def handleRespFrameStack(self, stackList, frame, isFirst):
+        if isFirst:
+            stackList            = []
+            self.stackFrameIndex = 0
+
+        lineNo  = frame.getNextInt()
+        modNo   = frame.getNextInt()
+        scopeId = frame.getNextInt()
+        frameId = frame.getNextInt()
+        if modNo != 0:
+            newStackFrame = PHPStackFrame(self, self.getModByNum(modNo), lineNo, self.stackFrameIndex, scopeId, self.getRawFrameData(frameId), modNo)
+            stackList.append(newStackFrame)
+            self.stackFrameIndex = self.stackFrameIndex + 1
+
+        return stackList
+
+    def handleRespFrameSource(self, frame):
+        modNo           = frame.getNextInt()
+        fromFilePos     = frame.getNextInt()
+        error           = frame.getNextInt()
+        fullSize        = frame.getNextInt()
+        fileNameFrameId = frame.getNextInt()
+        textFrameId     = frame.getNextInt()
+
+        fileName = self.getModByNum(modNo)
+        if not fileName:
+            self.setFileMod(modNo, fileNameFrameId)
+
+        #
+        # TODO: fullSize string and textFrameId are not handled here.
+        #
+        return
+
+    def handleRespFrameSrcTree(self, frame):
+        parentModNo     = frame.getNextInt()
+        parentLineNo    = frame.getNextInt()
+        modNo           = frame.getNextInt()
+        fileNameFrameId = frame.getNextInt()
+
+        fileName = self.getModByNum(modNo)
+        if not fileName:
+            self.setFileMod(modNo, fileNameFrameId)
+
+        return
+
+    def handleRespFrameRawdata(self, frame):
+        frameNo = frame.getNextInt()
+        if frameNo > 0:
+            toRead = frame.getNextInt()
+            if toRead > 0:
+                str = frame.getNextString(toRead)
+                self.rawDataDict[frameNo] = str
+                myprint("handleRespFrameRawdata(): added \'%d\'=\'%s\' to rawDataDict.", (frameNo, str))
+
+        return
+
+    def handleRespFrameError(self, frame):
+        self.isErrStack = True
+
+        #
+        # Type of the error.
+        #
+        errInt0         = frame.getNextInt()
+        #
+        # ID of error message.
+        #
+        errInt1         = frame.getNextInt()
+
+        if errInt0 == E_ERROR:
+            errIdStr = "[Error]"
+        elif errInt0 == E_WARNING:
+            errIdStr = "[Warning]"
+        elif errInt0 == E_PARSE:
+            errIdStr = "[Parse Error]"
+        elif errInt0 == E_NOTICE:
+            errIdStr = "[Notice]"
+        elif errInt0 == E_CORE_ERROR:
+            errIdStr = "[Core Error]"
+        elif errInt0 == E_CORE_WARNING:
+            errIdStr = "[Core Warning]"
+        elif errInt0 == E_COMPILE_ERROR:
+            errIdStr = "[Compile Error]"
+        elif errInt0 == E_COMPILE_WARNING:
+            errIdStr = "[Compile Warning]"
+        elif errInt0 == E_USER_ERROR:
+            errIdStr = "[User Error]"
+        elif errInt0 == E_USER_WARNING:
+            errIdStr = "[User Warning]"
+        elif errInt0 == E_USER_NOTICE:
+            errIdStr = "[User Notice]"
+        else:
+            errIdStr = "[Unexpected Error]"
+
+        errMsg = self.getRawFrameData(errInt1)
+        if errMsg and len(errMsg) > 0:
+            self.errStr = errIdStr + ": " + errMsg + "\n"
+        else:
+            self.errStr = errIdStr + ": <Invalid Error Message>\n"
+
+        if not self.stopOnError:
+            if self.lastCommand == DBGA_CONTINUE:
+                self.Continue()
+            elif self.lastCommand == DBGA_STEPINTO:
+                self.SingleStep()
+            elif self.lastCommand == DBGA_STEPOUT:
+                self.Return()
+            elif self.lastCommand == DBGA_STEPOVER:
+                self.Next()
+
+        return
+
+    def handleRespFrameEval(self, frame):
+        evalInt0        = frame.getNextInt()
+        evalInt1        = frame.getNextInt()
+        evalInt2        = frame.getNextInt()
+        self.evalRet    = self.getRawFrameData(evalInt1)
+        #TODO: is the following necessary?
+        evalStr         = self.getRawFrameData(evalInt0)
+
+        return
+
+    def handleRespFrameBps(self, frame):
+        return
+
+    def handleRespFrameBpl(self, frame):
+        #
+        # Get this breakpoint.
+        #
+        dbgBp = []
+        for i in range(10):
+            dbgBp.append(frame.getNextInt())
+
+        if dbgBp[2] != 0:
+            #
+            # If filename is sent, get it from the rawDataDict.
+            #
+            fileName = self.getRawFrameData(dbgBp[2])
+            if not fileName:
+                return
+
+            #
+            # If this filename comes with a mod number, store this
+            # modNum/fileName into this session's modDict.  Notice it might
+            # overwrite previous value.
+            #
+            if dbgBp[0] != 0:
+                self.setMod(dbgBp[0], fileName)
+        elif dbgBp[0] != 0:
+            #
+            # Use modNum to get the fileName.
+            #
+            fileName = self.getModByNum(dbgBp[0])
+            if not fileName:
+                return
+        else:
+            #
+            # Couldn't get the filename; nothing we can do with.
+            #
+            return
+
+        bpKey = self.createBpKey(fileName, dbgBp[1])
+        if not self.bpDict.has_key(bpKey):
+            #
+            # Not in our bp list?  Anyway, create one for it.
+            #
+            ourBp              = BreakPoint(self, fileName, dbgBp[1], dbgBp[0], dbgBp[3], dbgBp[4], dbgBp[5], dbgBp[6], dbgBp[7], dbgBp[8], dbgBp[9])
+            self.bpDict[bpKey] = ourBp
+            newlyCreated       = True
+        else:
+            ourBp              = self.bpDict[bpKey]
+            newlyCreated       = False
+
+        #
+        # Update with the latest bp information.
+        #
+        if not newlyCreated:
+            ourBp.update(dbgBp)
+
+        return
+
+    def handleRespFrameVer(self, frame):
+        self.verMajor = frame.getNextInt()
+        self.verMinor = frame.getNextInt()
+        verFrameNo    = frame.getNextInt()
+        self.verDesc  = self.getRawFrameData(verFrameNo)
+        myprint("respFrameVer: verMajor=%s, verMinor=%s, verDesc=%s", (repr(self.verMajor), repr(self.verMinor), repr(self.verDesc)))
+
+        return
+
+    def handleRespFrameSid(self, frame):
+        self.sessID   = frame.getNextInt()
+        self.sessType = frame.getNextInt()
+        myprint("respFrameSid: sessID=%s, sessType=%s", (self.sessID, self.sessType))
+
+        return
+
+    def handleRespFrameSrclinesinfo(self, frame):
+        return
+
+    def handleRespFrameSrcctxinfo(self, frame):
+        return
+
+    def handleRespFrameLog(self, frame):
+        #
+        # TODO:
+        # Now we don't do much here besides following the protocol to retrieve
+        # the data.
+        #
+        logId           = frame.getNextInt()
+        logType         = frame.getNextInt()
+        modNo           = frame.getNextInt()
+        lineNo          = frame.getNextInt()
+        fileNameFrameId = frame.getNextInt()
+        extInfo         = frame.getNextInt()
+
+        fileName = self.getModByNum(modNo)
+        if not fileName:
+            self.setFileMod(modNo, fileNameFrameId)
+
+        return
+
+    def handleRespFrameProf(self, frame):
+        return
+
+    def handleRespFrameProfC(self, frame):
+        return
+
+    def handleRespFrameSetOpt(self, frame):
+        return
+
+    def handleRespCmdReply(self):
+        return
+
+    def handleRespCmdStartup(self):
+        return
+
+    def handleRespCmdEnd(self):
+        self.sessEnded = True
+        return
+
+    def handleRespCmdBreakpoint(self):
+        return
+
+    def handleRespCmdStepintoDone(self):
+        return
+
+    def handleRespCmdStepoverDone(self):
+        return
+
+    def handleRespCmdStepoutDone(self):
+        return
+
+    def handleRespCmdEmbeddedBreak(self):
+        return
+
+    def handleRespCmdPause(self):
+        return
+
+    def handleRespCmdError(self):
+        self.stackList = []
+
+        if len(self.errStackList) > 0:
+            self.errStr = self.errStr + "Stack Trace:\n"
+
+        while len(self.errStackList) > 0:
+            oneStack = self.errStackList.pop()
+            self.errStr = self.errStr + "%s\n" % oneStack.getLongDisplayStr()
+
+        self.ui.showErrorDialog(self.errStr, "PHP Error")
+        myprint("Got PHP Error:\n%s", self.errStr)
+
+        return
+
+    def handleRespCmdLog(self):
+        return
+
+    def handleRespCmdSid(self):
+        return
+
+    def setFileMod(self, modNo, fileNameFrameId):
+        if fileNameFrameId != 0:
+            fileName = self.getRawFrameData(fileNameFrameId)
+            if fileName and modNo != 0:
+                self.setMod(modNo, fileName)
+
+        return
+
+    def createBpKey(self, fileName, lineNo):
+        #
+        # This is to work around a bug in dbg module where it changes the path
+        # names that we pass to it to lower cases.
+        #
+        if sysutils.isWindows():
+            fileName = fileName.lower()
+
+        return (fileName, lineNo)
+
+    def setLsnrAction(self, actioni):
+        self.lsnrAction = actioni
+        return
+
+    def getLsnrAction(self):
+        return self.lsnrAction
+
+    def currConnFinished(self):
+        self.clearInternals()
+        self.setLsnrAction(PHPDebuggerCallback.ACTION_LISTEN)
+        self.actionEvent.set()
+        self.ui.DisableAfterStop()
+
+    def stopPhpDbg(self):
+        #
+        # TODO: should send a request to stop the current running PHP program.
+        #       should handle network blocking issue correctly, otherwise, we
+        #       might hang here.
+        #
+        reqPacket = PHPDBGPacket(DBGA_STOP)
+
+        try:
+            reqPacket.sendPacket(self.lsnrThr)
+            self.awaitAndHandleResponse(stopping = True)
+        except PHPDBGConnException:
+            pass
+
+        self.currConnFinished()
+        return
+
+    def stopLsnr(self):
+        if not self.lsnrThr:
+            return
+
+        #
+        # Then we try to stop our listener thread.
+        #
+        if self.lsnrThr.hasBeenConnected():
+            #
+            # If the listener thread has already accepted a connection from a
+            # php debug module/client, it is sleeping now and wait for this
+            # condition object to be set so that it can exit.
+            #
+            self.setLsnrAction(PHPDebuggerCallback.ACTION_STOP)
+            self.actionEvent.set()
+        else:
+            #
+            # If the listener thread has never been connected from a php debug
+            # module/client, it is still blocking on a accept() call.  We
+            # connect to it here and send a special shutdown command asking it
+            # to exit.
+            #
+            shutdownMessage = IntToC4(DBG_SYNC) + IntToC4(DBGC_AG_SHUTDOWN_REQ) + IntToC4(0) + IntToC4(0)
+            tempSocket      = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+            try:
+                tempSocket.connect((self.lsnrHost, self.lsnrPort))
+                tempSocket.sendall(shutdownMessage)
+            except:
+                myprint("shutdown connection/send message got exception!")
+
+            tempSocket.close()
+
+        self.lsnrThr.join()
+        self.lsnrThr = None
+
+
+class PHPDBGLsnrThr(threading.Thread):
+    def __init__(self, interfacei, hosti, porti, actionEventi, uii):
+        threading.Thread.__init__(self)
+        self.interface          = interfacei
+        self.svrHost            = hosti
+        self.svrPort            = porti
+        self.actionEvent        = actionEventi
+        self.svrSocket          = None
+        self.clntConn           = None
+        self.clntAddr           = None
+        self.nonBlockingTimeout = 1
+        self.connHeader         = None
+        self.ui                 = uii
+
+    def initSvrSocket(self):
+        self.svrSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        self.svrSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+        self.svrSocket.bind((self.svrHost, self.svrPort))
+
+    def waitForClntConn(self):
+        self.svrSocket.listen(5)
+        self.clntConn, self.clntAddr = self.svrSocket.accept()
+        self.clntConn.settimeout(self.nonBlockingTimeout)
+
+    def run(self):
+        #
+        # Initialize this server socket.
+        #
+        self.initSvrSocket()
+
+        while True:
+            #
+            # Block until we get a new connection from a php debug client or our
+            # debugger ui (with a special shutting down header/command).
+            #
+            self.waitForClntConn()
+    
+            #
+            # Ok, a new connection comes in ...  Read the header to see where it
+            # comes from.
+            #
+            self.connHeader = ResponseHeader(self)
+            if self.connHeader.command == DBGC_AG_SHUTDOWN_REQ:
+                #
+                # This is a special command coming from our UI asking this
+                # thread to exit.  This only happens if after this thread has
+                # been waiting for new connections from PHP debug module, no one
+                # connects, and UI is ready to shutdown this thread.
+                #
+                self.shutdown()
+                break
+            else:
+                #
+                # Tell the main gui thread to handle this new connection.
+                #
+                wx.CallAfter(self.interface.newConnEventHandler)
+    
+                #
+                # From now on, PHPDebuggerCallback will communicate with the php
+                # debug module using this thread's clntConn socket.  This thread
+                # itself will keep sleeping until get notified to make some
+                # actions.
+                #
+                self.actionEvent.wait()
+                self.actionEvent.clear()
+
+                action = self.interface.getLsnrAction()
+                if action == PHPDebuggerCallback.ACTION_STOP:
+                    self.shutdown()
+                    break
+                elif action == PHPDebuggerCallback.ACTION_LISTEN:
+                    if self.clntConn:
+                        self.clntConn.shutdown(socket.SHUT_RDWR)
+                        self.clntConn.close()
+                        self.clntConn = None
+
+                    continue
+                else:
+                    continue
+
+    def shutdown(self):
+        #
+        # Cleanup and ready to exit.
+        #
+        self.clntConn.shutdown(socket.SHUT_RDWR)
+        self.clntConn.close()
+        self.svrSocket.close()
+
+    def recv(self, size, blocking = False):
+        if self.clntConn:
+            myprint("recv: trying to receive %d bytes of data ...", size)
+            if blocking:
+                self.clntConn.settimeout(None)
+            else:
+                self.clntConn.settimeout(self.nonBlockingTimeout)
+
+            try:
+                rData = self.clntConn.recv(size)
+            except socket.timeout:
+                myprint("recv: got timed out")
+                rData = None
+            except:
+                myprint("recv: got an unexpected exception: %s", sys.exc_info()[0])
+                raise PHPDBGConnException
+
+            return rData
+        else:
+            raise PHPDBGConnException
+
+    def sendall(self, message):
+        if self.clntConn:
+            try:
+                self.clntConn.sendall(message)
+            except:
+                myprint("sendall: got an unexpected exception: %s", sys.exc_info()[0])
+                raise PHPDBGConnException
+        else:
+            raise PHPDBGConnException
+
+    def hasBeenConnected(self):
+        return self.clntConn != None
+
+    def getConnHeader(self):
+        return self.connHeader
+
+
+class PHPValue(object):
+    PEV_NAMES           = ("undefined", "long", "double", "string", "array", "object", "boolean", "resource", "reference", "soft reference", "null")
+    PEVT_UNKNOWN        = 0
+    PEVT_LONG           = 1
+    PEVT_DOUBLE         = 2
+    PEVT_STRING         = 3
+    PEVT_ARRAY          = 4
+    PEVT_OBJECT         = 5
+    PEVT_BOOLEAN        = 6
+    PEVT_RESOURCE       = 7
+    PEVT_REF            = 8
+    PEVT_SOFTREF        = 9
+    PEVT_NULL           = 10
+
+    NULL_VALUE_STR      = "NULL"
+    TRUE_VALUE_STR      = "True"
+    FALSE_VALUE_STR     = "False"
+    OBJECT_VALUE_STR    = "<%s> object"
+    STRING_VALUE_STR    = "\"%s\""
+    REFERENCE_VALUE_STR = "<reference><%s>"
+    RESOURCE_VALUE_STR  = "<%s><%s>"
+
+    def __init__(self, frame, type, valueList):
+        self.fStackFrame = frame
+        self.fValueType  = type
+
+        if type == self.PEVT_OBJECT:
+            self.fValueString = self.OBJECT_VALUE_STR % valueList[0]
+            self.fVariables   = valueList[1:]
+        elif type == self.PEVT_ARRAY:
+            self.fValueString = ''
+            self.fVariables   = valueList
+        else:
+            self.fVariables = []
+            if type == self.PEVT_STRING:
+                self.fValueString = self.STRING_VALUE_STR % valueList[0]
+            elif type == self.PEVT_NULL:
+                self.fValueString = self.NULL_VALUE_STR
+            elif type == self.PEVT_BOOLEAN:
+                if valueList[0] == "0":
+                    self.fValueString = self.FALSE_VALUE_STR
+                else:
+                    self.fValueString = self.TRUE_VALUE_STR
+            elif type == self.PEVT_REF or type == self.PEVT_SOFTREF:
+                self.fValueString = self.REFERENCE_VALUE_STR % valueList[0]
+            elif type == self.PEVT_RESOURCE:
+                self.fValueString = self.RESOURCE_VALUE_STR % (valueList[0], valueList[1])
+            else:
+                self.fValueString = valueList[0]
+
+    def addVariable(self, item):
+        if item != None:
+            self.fVariables.append(item)
+
+        return self.fVariables
+
+    def setParent(self, parent):
+        if self.fVariables != None and len(self.fVariables) > 0:
+            for item in self.fVariables:
+                item.setParent(parent)
+
+    def getReferenceType(self):
+        return self.fValueType
+
+    def getReferenceTypeName(self):
+        return self.PEV_NAMES[self.fValueType]
+
+    def setReferenceType(self, type):
+        self.fValueType = type
+
+    def getValueString(self):
+        return self.fValueString
+
+    def getChildrenVariables(self):
+        return self.fVariables
+
+    def hasVariables(self):
+        return len(self.fVariables) > 0
+
+    def childrenIsSortable(self):
+        #
+        # TODO: if self.fValueType != self.PEVT_ARRAY:
+        #
+        return True
+
+
+class PHPVariable(object):
+    def __init__(self, frame, parent, valueType, name, valueList):
+        self.fStackFrame = frame
+        self.fName       = None
+        self.fLongName   = None
+        self.fPureName   = None
+        self.fValue      = PHPValue(frame, valueType, valueList)
+        self.fParent     = parent
+        self.setName(name)
+        self.setChildrensParent(valueList)
+
+    def setName(self, name):
+        self.fPureName = name
+
+        type = self.getReferenceType()
+        if type == PHPValue.PEVT_ARRAY:
+            numItems = len(self.getChildrenVariables())
+            self.fName = name + "[" + str(numItems) + "]"
+        else:
+            self.fName = name
+
+        if not self.fParent or self.fParent.getName() == None:
+            self.fLongName = name
+        else:
+            self.setLongName()
+
+        return
+
+    def setLongName(self):
+        parentType = self.fParent.getReferenceType()
+        if parentType == PHPValue.PEVT_ARRAY:
+            self.fLongName = self.fParent.getLongName() + "['" + self.fPureName + "']"
+        elif parentType == PHPValue.PEVT_OBJECT:
+            self.fLongName = self.fParent.getLongName() + "." + self.fName
+        else:
+            self.fLongName = self.fName
+
+        return
+
+    def getValue(self):
+        return self.fValue
+
+    def getValueString(self):
+        return self.fValue.getValueString()
+
+    def getChildrenVariables(self):
+        return self.fValue.getChildrenVariables()
+
+    def getName(self):
+        return self.fName
+
+    def getParent(self):
+        return self.fParent
+
+    def setParent(self, parent):
+        self.fParent = parent
+        self.setLongName()
+        return
+
+    def setChildrensParent(self, childrenList):
+        if self.fValue.hasVariables():
+            for child in self.fValue.getChildrenVariables():
+                child.setParent(self)
+
+    def getLongName(self):
+        return self.fLongName
+
+    def getReferenceTypeName(self):
+        return self.fValue.getReferenceTypeName()
+
+    def getReferenceType(self):
+        return self.fValue.getReferenceType()
+
+    def setReferenceType(self, type):
+        tp = self.getValue.setReferenceType(type)
+        return tp
+
+    def setValue(self, expression):
+        if self.fValue.getReferenceType() == PHPValue.PEVT_STRING:
+            evalString = self.fLongName + "=\"" + expression + "\""
+        else:
+            evalString = self.fLongName + "=" + expression
+
+        vars = self.fStackFrame.getPHPDBGInterface().evalBlock(self.fStackFrame, evalString)
+        self.fValue = vars[0].fValue
+
+    def toString(self):
+        rtype = self.getReferenceType()
+        if rtype == PHPValue.PEVT_ARRAY:
+            elements = len(self.fValue.getChildrenVariables())
+            if elements == 0:
+                tmpStr = self.getName() + " [no elements]"
+            elif elements == 1:
+                tmpStr = self.getName() + " [1 element]"
+            else:
+                tmpStr = self.getName() + " [" + str(elements) + " elements]"
+        elif rtype == PHPValue.PEVT_OBJECT:
+            tmpStr = self.getName() + " [ class: " + self.fValue.getValueString() + "]"
+        elif rtype == PHPValue.PEVT_STRING:
+            tmpStr = self.getName() + " = \"" + self.fValue.getValueString() + "\""
+        else:
+            tmpStr = self.getName() + " = " + self.fValue.getValueString()
+
+        return tmpStr
+
+    def hasChildren(self):
+        return self.fValue.hasVariables()
+
+    def childrenIsSortable(self):
+        return self.fValue.childrenIsSortable()
+
+
+class PHPStackFrame(object):
+    def __init__(self, interface, file, line, frameIndex, scopeId, desc, modNum):
+        self.interface      = interface
+        self.fileName       = file
+        self.lineNo         = line
+        self.frameIndex     = frameIndex
+        self.scopeId        = scopeId
+        self.desc           = desc
+        self.modNum         = modNum
+        self.variables      = []
+        self.shortFileName  = None
+        self.shortDesc      = None
+        self.displayStr     = None
+        self.longDisplayStr = None
+
+        self._getFileNamesAndShortDesc()
+
+        myprint("PHPStackFrame::__init__(): new PHPStackFrame: file=%s, lineNo=%s, frameIndex=%s, scopeId=%s, desc=%s, modNum=%s, shortFileName=%s, shortDesc=%s", (repr(file), repr(line), repr(frameIndex), repr(scopeId), repr(desc), repr(modNum), repr(self.shortFileName), repr(self.shortDesc)))
+
+    def _getFileNamesAndShortDesc(self):
+        tmp = []
+        if self.desc:
+            tmp = self.desc.split("::")
+
+        if self.fileName:
+            self.shortFileName = os.path.basename(self.fileName)
+            if len(tmp) == 2:
+                self.shortDesc = tmp[1]
+            elif len(tmp) == 1:
+                self.shortDesc = tmp[0]
+            else:
+                self.shortDesc = None
+
+            return
+
+        #
+        # The fileName is None, we will try our best efforts to get it.
+        #
+        if len(tmp) == 2:
+            #
+            # We retrieved long finename from the description.  If we haven't
+            # stored the file mod before, set this one as the new one.
+            # Otherwise, we prefer to keep the stored one.
+            #
+            if self.modNum != 0:
+                storedFileName = self.interface.getModByNum(self.modNum)
+                if not storedFileName:
+                    self.interface.setMod(self.modNum, tmp[0])
+
+            self.fileName      = tmp[0]
+            self.shortFileName = os.path.basename(tmp[0])
+            self.shortDesc     = tmp[1]
+        elif len(tmp) == 1:
+            self.fileName      = None
+            self.shortFileName = None
+            self.shortDesc     = tmp[0]
+        else:
+            self.shortFileName = None
+            self.shortDesc     = None
+            myprint("PHPStackFrame::_getFileNamesAndShortDesc(): something wrong with desc: %s?", self.desc)
+
+        return
+
+    def getShortFileName(self):
+        return self.shortFileName
+
+    def setShortFileName(self, shortFileName):
+        self.shortFileName = shortFileName
+
+    def getShortDesc(self):
+        return self.shortDesc
+
+    def getLineNo(self):
+        return self.lineNo
+
+    def getInterface(self):
+        return self.interface
+
+    def getVariables(self):
+        if len(self.variables) == 0:
+            self.variables = self.interface.getVariables(self)
+
+        return self.variables
+
+    def findVariables(self, s):
+        if self.hasVariables():
+            name = "$" + s
+            for var in self.variables:
+                if var.getName() == name:
+                    return var
+
+        return None
+
+    def hasVariables(self):
+        if len(self.variables) == 0:
+            return False
+        else:
+            return True
+
+    def getName(self):
+        if self.getDesc():
+            return self.getDesc() + " [line: " + str(self.getLineNo()) + "]"
+        else:
+            return self.getFileName() + " [line: " + str(self.getLineNo()) + "]"
+
+    def getFileName(self):
+        return self.fileName
+
+    def setFileName(self, fileName):
+        self.fileName = fileName
+
+    def setDesc(self, desc):
+        self.desc = desc
+
+    def getDesc(self):
+        return self.desc
+
+    def getFrameScopeId(self):
+        return self.scopeId
+
+    def getFrameIndex(self):
+        return self.frameIndex
+
+    def getDisplayStr(self, stackList = None):
+        if self.displayStr:
+            return self.displayStr
+
+        if not self.shortFileName:
+            if stackList:
+                i = stackList.index(self)
+                for j in range(i + 1, len(stackList)):
+                    self.shortFileName = stackList[j].getShortFileName()
+                    if self.shortFileName:
+                        self.fileName = stackList[j].getFileName()
+
+        if self.shortFileName:
+            if self.shortDesc:
+                self.displayStr = "<%s> at %s:%d" % (self.shortDesc, self.shortFileName, self.lineNo)
+            else:
+                self.displayStr = "%s:%d" % (self.shortFileName, self.lineNo)
+        else:
+            if self.shortDesc:
+                self.displayStr = "<%s>" % self.shortDesc
+            else:
+                self.displayStr = "<internal stack error>"
+
+        return self.displayStr
+
+    def getLongDisplayStr(self):
+        if self.longDisplayStr:
+            return self.longDisplayStr
+
+        if self.fileName:
+            if self.shortDesc:
+                self.longDisplayStr = "<%s> at %s:%d" % (self.shortDesc, self.fileName, self.lineNo)
+            else:
+                self.longDisplayStr = "%s:%d" % (self.fileName, self.lineNo)
+        else:
+            if self.shortDesc:
+                self.longDisplayStr = "<%s>" % self.shortDesc
+            else:
+                self.longDisplayStr = "<internal stack error>"
+
+        return self.longDisplayStr
+
+class BreakPoint(object):
+    def __init__(self, interface, fileName, lineNo, modNum = 0, state = BPS_ENABLED + BPS_UNRESOLVED, isTemp = 0, hitCount = 0, skipHits = 0, condition = 0, bpId = 0, isUnderHit = 0):
+        self.interface  = interface
+        self.fileName   = fileName
+        self.lineNo     = lineNo
+        self.bpID       = bpId
+        self.state      = state
+        self.isTemp     = isTemp
+        self.hitCount   = hitCount
+        self.skipHits   = skipHits
+        self.condition  = condition
+        self.isUnderHit = 0
+        if modNum == 0:
+            self.modNum = self.interface.getModByFileName(fileName)
+        else:
+            self.modNum = modNum
+
+        if self.modNum:
+            self.fCounterOrZero = 0
+        else:
+            self.fCounterOrZero = interface.getNextFrameCounter()
+
+    def sendSelf(self):
+        reqPacket = PHPDBGPacket(DBGA_REQUEST)
+        reqFrame1 = PHPDBGFrame(FRAME_BPS)
+
+        if self.modNum:
+            reqFrame1.addInt(self.modNum)
+        else:
+            #
+            # 0 in modNum to tell to use fileName instead.
+            #
+            reqFrame1.addInt(0)
+
+        reqFrame1.addInt(self.lineNo)             # lineNo
+        reqFrame1.addInt(self.fCounterOrZero)     # fileName frameCounter or 0
+        reqFrame1.addInt(self.state)              # state
+        reqFrame1.addInt(self.isTemp)             # isTemp
+        reqFrame1.addInt(self.hitCount)           # hitCount
+        reqFrame1.addInt(self.skipHits)           # skipHits
+        reqFrame1.addInt(self.condition)          # condition
+        reqFrame1.addInt(self.bpID)               # breakpoint sequence id
+        reqFrame1.addInt(self.isUnderHit)         # isUnderHit
+
+        if not self.modNum:
+            reqFrame2 = PHPDBGFrame(FRAME_RAWDATA)
+            reqFrame2.addInt(self.fCounterOrZero)
+            reqFrame2.addInt(len(self.fileName) + 1)
+            reqFrame2.addStr(self.fileName)
+            reqPacket.addFrame(reqFrame2)
+
+        reqPacket.addFrame(reqFrame1)
+
+        try:
+            reqPacket.sendPacket(self.interface.lsnrThr)
+            self.interface.awaitAndHandleResponse()
+        except PHPDBGConnException:
+            self.interface.currConnFinished()
+
+        return
+
+    def addSelf(self):
+        self.sendSelf()
+
+    def removeSelf(self):
+        self.state = BPS_DISABLED
+        self.sendSelf()
+
+    def isUnderHit(self):
+        return self.isUnderHit == 1
+
+    def update(self, dbgBp):
+        self.modNum     = dbgBp[0]
+        self.state      = dbgBp[3]
+        self.isTemp     = dbgBp[4]
+        self.hitCount   = dbgBp[5]
+        self.skipHits   = dbgBp[6]
+        self.condition  = dbgBp[7]
+        self.bpID       = dbgBp[8]
+        self.isUnderHit = dbgBp[9]
+
+
+class PHPDBGEvalString(object):
+    def __init__(self, stackFrame, dataStr):
+        self.stackFrame = stackFrame
+        self.dataStr    = dataStr
+
+    #
+    # Get a list of variables under self.stackFrame.
+    #
+    def getVars(self):
+        return self.parseAVariable(isRealVar = False)
+
+    #
+    # if isRealVar:
+    #    returnList[0] = The Variable
+    # else:
+    #    returnList    = list of variables.
+    #
+    def parseAVariable(self, isRealVar = True):
+        returnList = []
+
+        #
+        # Get the variable name first.  Notice we ignore this entity's data
+        # type here.
+        #
+        if isRealVar:
+            nameEntity = self.parseAnEntity()
+            if not nameEntity or len(nameEntity) != 2 or type(nameEntity[1]) != str:
+                myprint("PHPDBGEvalStr::parseAVariable() got a wrong name entity")
+                return returnList
+            else:
+                varName = nameEntity[1]
+
+        #
+        # Get the variable's value.
+        #
+        valueEntity = self.parseAnEntity()
+        if not valueEntity or len(valueEntity) < 1:
+            myprint("PHPDBGEvalStr::parseAVariable(): couldn't get a variable's value entity.")
+            return returnList
+
+        #
+        # This variable's data type.
+        #
+        varType = valueEntity[0]
+
+        if isRealVar:
+            #
+            # If this is a real variable, return a list which contains only
+            # this variable item.
+            #
+            #valueEntity = valueEntity[1:]
+            variable    = PHPVariable(self.stackFrame, None, varType, varName, valueEntity[1:])
+            #myprint("xxxxCreated variable varName=%s, valueEntity=%s", (repr(varName), repr(valueEntity[1])))
+            myprint("xxxxCreated variable: %s", repr(variable.toString()))
+            returnList.append(variable)
+        else:
+            #
+            # If this is a root variable container, returns a list of
+            # variables under the root.  Do a sanity check here.
+            #
+            if valueEntity[0] != PHPValue.PEVT_ARRAY:
+                myprint("PHPDBGEvalStr::parseAVariable(): failed to parse the root variable container.")
+            else:
+                returnList = valueEntity[1:]
+
+        return returnList
+
+    #
+    # An entity could be a variable's name or its value.
+    #
+    # returnList[0]  = variable data type
+    # returnList[1:] = the real list
+    #
+    def parseAnEntity(self):
+        if not self.dataStr or len(self.dataStr) < 2  or (self.dataStr[1] != ':' and self.dataStr[1] != ';'):
+            myprint("PHPDBGEvalStr::parseAnEntity(): failed to parse %s.", repr(self.dataStr))
+            return None
+
+        returnList    = []
+        typeChar      = self.dataStr[0]
+        self.dataStr  = self.dataStr[2:]
+        if typeChar   == 'i':
+            returnList.append(PHPValue.PEVT_LONG)
+            self.parseInt(returnList)
+        elif typeChar == 'a':
+            returnList.append(PHPValue.PEVT_ARRAY)
+            self.parseArray(returnList)
+        elif typeChar == 's':
+            returnList.append(PHPValue.PEVT_STRING)
+            self.parseString(returnList)
+        elif typeChar == 'O':
+            returnList.append(PHPValue.PEVT_OBJECT)
+            self.parseObject(returnList)
+        elif typeChar == 'r':
+            returnList.append(PHPValue.PEVT_SOFTREF)
+            self.parseReference(returnList, isSoftRef = True)
+        elif typeChar == 'R':
+            returnList.append(PHPValue.PEVT_REF)
+            self.parseReference(returnList, isSoftRef = False)
+        elif typeChar == 'b':
+            returnList.append(PHPValue.PEVT_BOOLEAN)
+            self.parseBoolean(returnList)
+        elif typeChar == 'd':
+            returnList.append(PHPValue.PEVT_DOUBLE)
+            self.parseDouble(returnList)
+        elif typeChar == 'z':
+            returnList.append(PHPValue.PEVT_RESOURCE)
+            self.parseResource(returnList)
+        elif typeChar == 'N':
+            returnList.append(PHPValue.PEVT_NULL)
+            self.parseNull(returnList)
+        else:
+            myprint("PHPDBGEvalStr::parseAnEntity(): unknown data type: %s", typeChar)
+
+        return returnList
+
+    def parseInt(self, returnList):
+        myprint("enter parseInt().")
+        returnList.append(self.getAnIntStr(';'))
+
+        return
+
+    def parseArray(self, returnList):
+        myprint("enter parseArray().")
+        #
+        # The shortest array is 'a:0:{}'.
+        #
+        if len(self.dataStr) < 4:
+            myprint("PHPDBGEvalStr::parseArray(): failed (1) to parse an array: %s.", repr(self.dataStr))
+            return
+
+        expectedNumItems = self.getAnInt(':')
+        if len(self.dataStr) < 2 or self.dataStr[0] != '{':
+            myprint("PHPDBGEvalStr::parseArray(): failed (3) to parse an array: %s.", repr(self.dataStr))
+            return
+
+        self.dataStr = self.dataStr[1:]
+        varList      = []
+        while self.dataStr and len(self.dataStr) > 0 and self.dataStr[0] != '}':
+            tmpList = self.parseAVariable()
+            if not tmpList or len(tmpList) != 1 or not tmpList[0]:
+                myprint("PHPDBGEvalStr::parseArray(): failed (4) to parse an array.  dataStr=%s.", repr(self.dataStr))
+                break
+            else:
+                varList.append(tmpList[0])
+
+        if expectedNumItems != len(varList):
+            myprint("PHPDBGEvalStr::parseArray(): failed (5) expected no. of items=%d, but got %d", (expectedNumItems, len(varList)))
+
+        #
+        # An array should end with a '}'.
+        #
+        if self.dataStr and len(self.dataStr) > 0 and self.dataStr[0] == '}':
+            self.dataStr = self.dataStr[1:]
+            returnList.extend(varList)
+        else:
+            myprint("PHPDBGEvalStr::parseArray(): failed (6) to parse an array.  dataStr=%s.", repr(self.dataStr))
+
+        myprint("parseArray() ends.")
+        return
+
+    def parseString(self, returnList, endChar = ';'):
+        myprint("enter parseString().")
+        #
+        # The shortest string is 's:<str_len>:"<str>"<endChar>'.
+        #
+        if len(self.dataStr) < 5:
+            myprint("PHPDBGEvalStr::parseString(): failed (1) to parse a string.  dataStr=%s.", repr(self.dataStr))
+            return
+
+        expectedStrLen = self.getAnInt(':')
+        if len(self.dataStr) < expectedStrLen + 3 or self.dataStr[0] != '"':
+            myprint("PHPDBGEvalStr::parseString(): failed (3) to parse a string.  dataStr=%s.", repr(self.dataStr))
+            return
+
+        strValue = self.dataStr[1:expectedStrLen + 1]
+        if self.dataStr[expectedStrLen + 1:expectedStrLen + 3] != '"' + endChar:
+            myprint("PHPDBGEvalStr::parseString(): failed (4) to parse a string.  dataStr=%s.", repr(self.dataStr))
+            return
+
+        #
+        # Skip the starting double quote, real string, ending double quote, and ending semicolon.
+        #
+        self.dataStr = self.dataStr[expectedStrLen + 3:]
+        returnList.append(strValue)
+
+        return
+
+    def parseObject(self, returnList):
+        #
+        # A simple sanity check.  The shortest object is:
+        # 'O:<class_name_len>:"<class_name>":<num_of_items>:{<list_of_items>}'
+        #
+        if not self.dataStr or len(self.dataStr) < 10:
+            myprint("PHPDBGEvalStr::parseObject(): failed (1) to parse an object: %s.", repr(self.dataStr))
+
+        #
+        # Get the class name in classNameList[0].
+        #
+        classNameList = []
+        self.parseString(classNameList, ':')
+
+        expectedNumItems = self.getAnInt(':')
+        if len(self.dataStr) < 2 or self.dataStr[0] != '{':
+            myprint("PHPDBGEvalStr::parseObject(): failed (2) to parse an object: %s.", repr(self.dataStr))
+            return
+
+        self.dataStr = self.dataStr[1:]
+        varList      = []
+        while self.dataStr and len(self.dataStr) > 0 and self.dataStr[0] != '}':
+            tmpList = self.parseAVariable()
+            if not tmpList or len(tmpList) != 1 or not tmpList[0]:
+                myprint("PHPDBGEvalStr::parseObject(): failed (3) to parse an object.  dataStr=%s.", repr(self.dataStr))
+                break
+            else:
+                varList.append(tmpList[0])
+
+        if expectedNumItems != len(varList):
+            myprint("PHPDBGEvalStr::parseObject(): failed (4) expected no. of items=%d, but got %d", (expectedNumItems, len(varList)))
+
+        #
+        # An object should end with a '}'.
+        #
+        if self.dataStr and len(self.dataStr) > 0 and self.dataStr[0] == '}':
+            self.dataStr = self.dataStr[1:]
+            returnList.append(classNameList[0])
+            returnList.extend(varList)
+        else:
+            myprint("PHPDBGEvalStr::parseObject(): failed (6) to parse an object.  dataStr=%s.", repr(self.dataStr))
+
+        myprint("parseObject() ends.")
+        return
+
+    def parseReference(self, returnList, isSoftRef):
+        myprint("enter parseReference().")
+        intStr = self.getAnIntStr(';')
+        if intStr:
+            returnList.append(intStr)
+
+        return
+
+    def parseBoolean(self, returnList):
+        tmpBooleanStr = self.getAnIntStr(';')
+        returnList.append(tmpBooleanStr)
+
+        return
+
+    def parseDouble(self, returnList):
+        tmpStr = self.getAStrTillEndChar(';')
+        if tmpStr:
+            returnList.append(tmpStr)
+
+        return
+
+    def parseResource(self, returnList):
+        tmpList = []
+        self.parseString(tmpList, ':')
+
+        if len(tmpList) == 1:
+            returnList.extend(tmpList)
+        else:
+            return
+
+        resourceId = self.getAnIntStr(';')
+        if resourceId:
+            returnList.append(resourceId)
+
+        return
+
+    def parseNull(self, returnList):
+        return
+
+    def getAStrTillEndChar(self, endChar):
+        if len(self.dataStr) < 1:
+            myprint("PHPDBGEvalStr::getAStrTillEndChar(): no more data string to work with.")
+            return
+
+        i = self.findNextChar(self.dataStr, endChar)
+        if i == -1:
+            myprint("PHPDBGEvalStr::getAStrTillEndChar(): no double/float string supplied.")
+            return
+
+        tmpStr       = self.dataStr[:i]
+        self.dataStr = self.dataStr[i + 1:]
+
+        if self.isFloat(tmpStr):
+            return tmpStr
+        else:
+            myprint("PHPDBGEvalStr::getAStrTillEndChar(): parsing error.  tried to get an float number, but get %s.", tmpStr)
+            return None
+
+    def getAnInt(self, endChar):
+        tmpStr = self.getAnIntStr(endChar)
+        if tmpStr:
+            return int(tmpStr)
+        else:
+            return 0
+
+    def getAnIntStr(self, endChar):
+        if len(self.dataStr) == 0:
+            myprint("PHPDBGEvalStr::getAnIntStr(): no more data string to work with.")
+            return
+
+        i = self.findNextChar(self.dataStr, endChar)
+        if i == -1:
+            tmpStr = self.dataStr
+            self.dataStr = ''
+        else:
+            tmpStr = self.dataStr[:i]
+            self.dataStr = self.dataStr[i + 1:]
+
+        if self.isInt(tmpStr):
+            return tmpStr
+        else:
+            myprint("PHPDBGEvalStr::getAnIntStr(): parsing error.  tried to get an integer, but get %s.", tmpStr)
+            return None
+
+    def isInt(self, aStr):
+        try:
+            int(aStr)
+        except ValueError:
+            return False
+
+        return True
+
+    def isFloat(self, aStr):
+        try:
+            float(aStr)
+        except ValueError:
+            return False
+
+        return True
+
+    def findNextChar(self, aStr, aChar):
+        try:
+            index = aStr.index(aChar)
+        except ValueError:
+            index = -1
+
+        return index
index 5e78d118f676f14aef3f0eb22445c48dbdd9b2d8..840ad1fc4448ba6fdf0585f5053bdd2effa3c7c2 100644 (file)
@@ -17,6 +17,10 @@ import CodeEditor
 import OutlineService
 import os
 import re
+import FindInDirService
+import activegrid.util.appdirs as appdirs
+import activegrid.util.sysutils as sysutils
+_ = wx.GetTranslation
 
 
 class PHPDocument(CodeEditor.CodeDocument):
@@ -153,18 +157,52 @@ class PHPCtrl(CodeEditor.CodeCtrl):
 
     def __init__(self, parent, id=-1, style=wx.NO_FULL_REPAINT_ON_RESIZE):
         CodeEditor.CodeCtrl.__init__(self, parent, id, style)
-        self.SetLexer(wx.stc.STC_LEX_PHP)
+        self.SetLexer(wx.stc.STC_LEX_HTML)
         self.SetStyleBits(7)
         self.SetKeyWords(4, string.join(PHPKEYWORDS))
         self.SetProperty("fold.html", "1")
 
 
+    def CreatePopupMenu(self):
+        FINDCLASS_ID = wx.NewId()
+        FINDDEF_ID = wx.NewId()
+        
+        menu = CodeEditor.CodeCtrl.CreatePopupMenu(self)
+
+        self.Bind(wx.EVT_MENU, self.OnPopFindDefinition, id=FINDDEF_ID)
+        menu.Insert(1, FINDDEF_ID, _("Find 'function'"))
+
+        self.Bind(wx.EVT_MENU, self.OnPopFindClass, id=FINDCLASS_ID)
+        menu.Insert(2, FINDCLASS_ID, _("Find 'class'"))
+
+        return menu
+        
+
+    def OnPopFindDefinition(self, event):
+        view = wx.GetApp().GetDocumentManager().GetCurrentView()
+        if hasattr(view, "GetCtrl") and view.GetCtrl() and hasattr(view.GetCtrl(), "GetSelectedText"):
+            pattern = view.GetCtrl().GetSelectedText().strip()
+            if pattern:
+                searchPattern = "function\s+%s" % pattern
+                wx.GetApp().GetService(FindInDirService.FindInDirService).FindInProject(searchPattern)
+
+
+    def OnPopFindClass(self, event):
+        view = wx.GetApp().GetDocumentManager().GetCurrentView()
+        if hasattr(view, "GetCtrl") and view.GetCtrl() and hasattr(view.GetCtrl(), "GetSelectedText"):
+            definition = "class\s+%s"
+            pattern = view.GetCtrl().GetSelectedText().strip()
+            if pattern:
+                searchPattern = definition % pattern
+                wx.GetApp().GetService(FindInDirService.FindInDirService).FindInProject(searchPattern)
+
+
     def CanWordWrap(self):
         return True
 
 
     def SetViewDefaults(self):
-        CodeEditor.CodeCtrl.SetViewDefaults(self, configPrefix = "PHP", hasWordWrap = True, hasTabs = True)
+        CodeEditor.CodeCtrl.SetViewDefaults(self, configPrefix = "PHP", hasWordWrap = True, hasTabs = True, hasFolding=True)
 
 
     def GetFontAndColorFromConfig(self):
@@ -216,8 +254,110 @@ class PHPCtrl(CodeEditor.CodeCtrl):
 class PHPOptionsPanel(STCTextEditor.TextOptionsPanel):
 
     def __init__(self, parent, id):
-        STCTextEditor.TextOptionsPanel.__init__(self, parent, id, configPrefix = "PHP", label = "PHP", hasWordWrap = True, hasTabs = True)
-
+        wx.Panel.__init__(self, parent, id)
+        mainSizer = wx.BoxSizer(wx.VERTICAL)                
+
+        config = wx.ConfigBase_Get()
+
+        pathLabel = wx.StaticText(self, -1, _("PHP Executable Path:"))
+        path = config.Read("ActiveGridPHPLocation")
+        self._pathTextCtrl = wx.TextCtrl(self, -1, path, size = (150, -1))
+        self._pathTextCtrl.SetToolTipString(self._pathTextCtrl.GetValue())
+        self._pathTextCtrl.SetInsertionPointEnd()
+        choosePathButton = wx.Button(self, -1, _("Browse..."))
+        pathSizer = wx.BoxSizer(wx.HORIZONTAL)
+        HALF_SPACE = 5
+        SPACE = 10
+        pathSizer.Add(pathLabel, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.TOP, HALF_SPACE)
+        pathSizer.Add(self._pathTextCtrl, 1, wx.EXPAND|wx.LEFT|wx.TOP, HALF_SPACE)
+        pathSizer.Add(choosePathButton, 0, wx.ALIGN_RIGHT|wx.LEFT|wx.RIGHT|wx.TOP, HALF_SPACE)
+        wx.EVT_BUTTON(self, choosePathButton.GetId(), self.OnChoosePath)
+        mainSizer.Add(pathSizer, 0, wx.EXPAND|wx.LEFT|wx.RIGHT|wx.TOP, SPACE)
+
+        iniLabel = wx.StaticText(self, -1, _("php.ini Path:"))
+        ini = config.Read("ActiveGridPHPINILocation")
+        if not ini:
+            if sysutils.isRelease():
+                ini = os.path.normpath(os.path.join(appdirs.getSystemDir(), "php.ini"))
+            else:
+                tmp = self._pathTextCtrl.GetValue().strip()
+                if tmp and len(tmp) > 0:
+                    ini = os.path.normpath(os.path.join(os.path.dirname(tmp), "php.ini"))
+
+        self._iniTextCtrl = wx.TextCtrl(self, -1, ini, size = (150, -1))
+        self._iniTextCtrl.SetToolTipString(self._iniTextCtrl.GetValue())
+        self._iniTextCtrl.SetInsertionPointEnd()
+        chooseIniButton = wx.Button(self, -1, _("Browse..."))
+        iniSizer = wx.BoxSizer(wx.HORIZONTAL)
+        HALF_SPACE = 5
+        SPACE = 10
+        iniSizer.Add(iniLabel, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.TOP, HALF_SPACE)
+        iniSizer.Add(self._iniTextCtrl, 1, wx.EXPAND|wx.LEFT|wx.TOP, HALF_SPACE)
+        iniSizer.Add(chooseIniButton, 0, wx.ALIGN_RIGHT|wx.LEFT|wx.RIGHT|wx.TOP, HALF_SPACE)
+        wx.EVT_BUTTON(self, chooseIniButton.GetId(), self.OnChooseIni)
+        mainSizer.Add(iniSizer, 0, wx.EXPAND|wx.LEFT|wx.RIGHT|wx.TOP, SPACE)
+
+        self._otherOptions = STCTextEditor.TextOptionsPanel(self, -1, configPrefix = "PHP", label = "PHP", hasWordWrap = True, hasTabs = True, addPage=False, hasFolding=False)
+        #STCTextEditor.TextOptionsPanel.__init__(self, parent, id, configPrefix = "PHP", label = "PHP", hasWordWrap = True, hasTabs = True)
+        mainSizer.Add(self._otherOptions, 0, wx.EXPAND|wx.BOTTOM, SPACE)
+
+        self.SetSizer(mainSizer)
+        parent.AddPage(self, _("PHP"))
+
+    def OnChoosePath(self, event):
+        defaultDir = os.path.dirname(self._pathTextCtrl.GetValue().strip())
+        defaultFile = os.path.basename(self._pathTextCtrl.GetValue().strip())
+        if wx.Platform == '__WXMSW__':
+            wildcard = _("Executable (*.exe)|*.exe|All|*.*")
+            if not defaultFile:
+                defaultFile = "php-cgi.exe"
+        else:
+            wildcard = _("*")
+        dlg = wx.FileDialog(wx.GetApp().GetTopWindow(),
+                               _("Select a File"),
+                               defaultDir=defaultDir,
+                               defaultFile=defaultFile,
+                               wildcard=wildcard,
+                               style=wx.OPEN|wx.FILE_MUST_EXIST|wx.HIDE_READONLY)
+        # dlg.CenterOnParent()  # wxBug: caused crash with wx.FileDialog
+        if dlg.ShowModal() == wx.ID_OK:
+            path = dlg.GetPath()
+            if path:  
+                self._pathTextCtrl.SetValue(path)
+                self._pathTextCtrl.SetToolTipString(self._pathTextCtrl.GetValue())
+                self._pathTextCtrl.SetInsertionPointEnd()
+        dlg.Destroy()            
+
+    def OnChooseIni(self, event):
+        defaultDir = os.path.dirname(self._iniTextCtrl.GetValue().strip())
+        defaultFile = os.path.basename(self._iniTextCtrl.GetValue().strip())
+        if wx.Platform == '__WXMSW__':
+            wildcard = _("Ini (*.ini)|*.ini|All|*.*")
+            if not defaultFile:
+                defaultFile = "php.ini"
+        else:
+            wildcard = _("*")
+        dlg = wx.FileDialog(wx.GetApp().GetTopWindow(),
+                               _("Select a File"),
+                               defaultDir=defaultDir,
+                               defaultFile=defaultFile,
+                               wildcard=wildcard,
+                               style=wx.OPEN|wx.FILE_MUST_EXIST|wx.HIDE_READONLY)
+        # dlg.CenterOnParent()  # wxBug: caused crash with wx.FileDialog
+        if dlg.ShowModal() == wx.ID_OK:
+            ini = dlg.GetPath()
+            if ini:  
+                self._iniTextCtrl.SetValue(ini)
+                self._iniTextCtrl.SetToolTipString(self._iniTextCtrl.GetValue())
+                self._iniTextCtrl.SetInsertionPointEnd()
+        dlg.Destroy()            
+
+    def OnOK(self, optionsDialog):
+        config = wx.ConfigBase_Get()
+        config.Write("ActiveGridPHPLocation", self._pathTextCtrl.GetValue().strip())
+        config.Write("ActiveGridPHPINILocation", self._iniTextCtrl.GetValue().strip())
+
+        self._otherOptions.OnOK(optionsDialog)
 
     def GetIcon(self):
         return getPHPIcon()
index bc7c48f0c12b7f557b6dc9d6626328d87361cc26..544d0fbd534c5e88acfdfc08790f298f61a62555 100644 (file)
@@ -73,7 +73,7 @@ class PerlCtrl(CodeEditor.CodeCtrl):
 
 
     def SetViewDefaults(self):
-        CodeEditor.CodeCtrl.SetViewDefaults(self, configPrefix = "Perl", hasWordWrap = True, hasTabs = True)
+        CodeEditor.CodeCtrl.SetViewDefaults(self, configPrefix = "Perl", hasWordWrap = True, hasTabs = True, hasFolding=True)
 
 
     def GetFontAndColorFromConfig(self):
@@ -130,7 +130,7 @@ class PerlCtrl(CodeEditor.CodeCtrl):
 class PerlOptionsPanel(STCTextEditor.TextOptionsPanel):
 
     def __init__(self, parent, id):
-        STCTextEditor.TextOptionsPanel.__init__(self, parent, id, configPrefix = "Perl", label = "Perl", hasWordWrap = True, hasTabs = True)
+        STCTextEditor.TextOptionsPanel.__init__(self, parent, id, configPrefix = "Perl", label = "Perl", hasWordWrap = True, hasTabs = True, hasFolding=True)
 
 
     def GetIcon(self):
index af95b890d3edd8e718c9c069cb3e6c3fbaba4427..d826156d34ac49f28d27e17dbfacf7723b5a782b 100644 (file)
@@ -6,7 +6,7 @@
 #
 # Created:      8/15/03
 # CVS-ID:       $Id$
-# Copyright:    (c) 2003, 2004, 2005 ActiveGrid, Inc.
+# Copyright:    (c) 2003-2006 ActiveGrid, Inc.
 # License:      wxWindows License
 #----------------------------------------------------------------------------
 
@@ -25,6 +25,7 @@ import time
 import types
 import activegrid.util.appdirs as appdirs
 import activegrid.util.fileutils as fileutils
+import activegrid.util.aglogging as aglogging
 import UICommon
 import Wizard
 import SVNService
@@ -36,14 +37,19 @@ if not ACTIVEGRID_BASE_IDE:
     import activegrid.server.deployment as deploymentlib
     import ProcessModelEditor
     import DataModelEditor
+    import DeploymentGeneration
     import WsdlAgEditor
+    import WsdlAgModel
     APP_LAST_LANGUAGE = "LastLanguage"
     import activegrid.model.basedocmgr as basedocmgr
     import activegrid.model.basemodel as basemodel
+    import activegrid.model.projectmodel as projectmodel
     import PropertyService
     from activegrid.server.toolsupport import GetTemplate
     import activegrid.util.xmlutils as xmlutils
     import activegrid.util.sysutils as sysutils
+    DataServiceExistenceException = DeploymentGeneration.DataServiceExistenceException
+    import WebBrowserService
 
 from SVNService import SVN_INSTALLED
 
@@ -61,6 +67,10 @@ SPACE = 10
 HALF_SPACE = 5
 PROJECT_EXTENSION = ".agp"
 
+if not ACTIVEGRID_BASE_IDE:
+    PRE_17_TMP_DPL_NAME = "RunTime_tmp" + deploymentlib.DEPLOYMENT_EXTENSION
+    _17_TMP_DPL_NAME = ".tmp" + deploymentlib.DEPLOYMENT_EXTENSION
+
 # wxBug: the wxTextCtrl and wxChoice controls on Mac do not correctly size
 # themselves with sizers, so we need to add a right border to the sizer to
 # get the control to shrink itself to fit in the sizer.
@@ -72,70 +82,85 @@ if wx.Platform == "__WXMAC__":
 PROJECT_KEY = "/AG_Projects"
 PROJECT_DIRECTORY_KEY = "NewProjectDirectory"
 
-NEW_PROJECT_DIRECTORY_DEFAULT = appdirs.documents_folder
+NEW_PROJECT_DIRECTORY_DEFAULT = appdirs.getSystemDir()
 
 #----------------------------------------------------------------------------
 # Methods
 #----------------------------------------------------------------------------
 
-def getProjectKeyName(projectName, mode):
-    return "%s/%s/%s" % (PROJECT_KEY, projectName.replace(os.sep, '|'), mode)
+def AddProjectMapping(doc, projectDoc=None, hint=None):
+    projectService = wx.GetApp().GetService(ProjectService)
+    if projectService:
+        if not projectDoc:
+            if not hint:
+                hint = doc.GetFilename()
+            projectDocs = projectService.FindProjectByFile(hint)
+            if projectDocs:
+                projectDoc = projectDocs[0]
+                
+        projectService.AddProjectMapping(doc, projectDoc)
+        if hasattr(doc, "GetModel"):
+            projectService.AddProjectMapping(doc.GetModel(), projectDoc)
+
+
+def getProjectKeyName(projectName, mode=None):
+    if mode:
+        return "%s/%s/%s" % (PROJECT_KEY, projectName.replace(os.sep, '|'), mode)
+    else:
+        return "%s/%s" % (PROJECT_KEY, projectName.replace(os.sep, '|'))
 
 
 def GetDocCallback(filepath):
     """ Get the Document used by the IDE and the in-memory document model used by runtime engine """
     docMgr = wx.GetApp().GetDocumentManager()
     
-    doc = docMgr.CreateDocument(filepath, docMgr.GetFlags()|wx.lib.docview.DOC_SILENT|wx.lib.docview.DOC_OPEN_ONCE|wx.lib.docview.DOC_NO_VIEW)
-    if (doc == None):  # already open
-        for d in docMgr.GetDocuments():
-            if os.path.normcase(d.GetFilename()) == os.path.normcase(filepath):
-                doc = d
-                break
-    else:
-        projectService = wx.GetApp().GetService(ProjectService)
-        if projectService:
-            projectDocs = projectService.FindProjectByFile(filepath)
-            if projectDocs:
-                projectDoc = projectDocs[0]
-                projectService.AddProjectMapping(doc, projectDoc)
-                if hasattr(doc, "GetModel"):
-                    projectService.AddProjectMapping(doc.GetModel(), projectDoc)
-                
-            
-    if doc and doc.GetDocumentTemplate().GetDocumentType() == WsdlAgEditor.WsdlAgDocument:
-        # get referenced wsdl doc instead
-        if os.path.isabs(doc.GetModel().filePath):  # if absolute path, leave it alone
-            filepath = doc.GetModel().filePath
-        else:
-            filepath = doc.GetAppDocMgr().fullPath(doc.GetModel().filePath)  # check relative to project homeDir
-    
-            if not os.path.isfile(filepath):
-                filepath = os.path.normpath(os.path.join(os.path.dirname(doc.GetFilename()), doc.GetModel().filePath))  # check relative to wsdlag file
-                
-                if not os.path.isfile(filepath):
-                    filename = os.sep + os.path.basename(doc.GetModel().filePath)  # check to see if in project file
-                    filePaths = findDocumentMgr(doc).filePaths
-                    for fp in filePaths:
-                        if fp.endswith(filename):
-                            filepath = fp
-                            break
-                
+    try:
         doc = docMgr.CreateDocument(filepath, docMgr.GetFlags()|wx.lib.docview.DOC_SILENT|wx.lib.docview.DOC_OPEN_ONCE|wx.lib.docview.DOC_NO_VIEW)
-        if (doc == None):  # already open
+        if doc:
+            AddProjectMapping(doc)
+        else:  # already open
             for d in docMgr.GetDocuments():
                 if os.path.normcase(d.GetFilename()) == os.path.normcase(filepath):
                     doc = d
                     break
+    except Exception,e:
+        doc = None            
+        aglogging.reportException(e, stacktrace=True)
+            
+    if doc and doc.GetDocumentTemplate().GetDocumentType() == WsdlAgEditor.WsdlAgDocument:
+        # get referenced wsdl doc instead
+        if doc.GetModel().filePath:
+            if os.path.isabs(doc.GetModel().filePath):  # if absolute path, leave it alone
+                filepath = doc.GetModel().filePath
+            else:
+                filepath = doc.GetAppDocMgr().fullPath(doc.GetModel().filePath)  # check relative to project homeDir
+        
+                if not os.path.isfile(filepath):
+                    filepath = os.path.normpath(os.path.join(os.path.dirname(doc.GetFilename()), doc.GetModel().filePath))  # check relative to wsdlag file
+                    
+                    if not os.path.isfile(filepath):
+                        filename = os.sep + os.path.basename(doc.GetModel().filePath)  # check to see if in project file
+                        filePaths = findDocumentMgr(doc).filePaths
+                        for fp in filePaths:
+                            if fp.endswith(filename):
+                                filepath = fp
+                                break
+        
+            try:
+                doc = docMgr.CreateDocument(filepath, docMgr.GetFlags()|wx.lib.docview.DOC_SILENT|wx.lib.docview.DOC_OPEN_ONCE|wx.lib.docview.DOC_NO_VIEW)
+            except Exception,e:
+                doc = None
+                aglogging.reportException(e, stacktrace=True)
+                
+            if doc: 
+                AddProjectMapping(doc)
+            else:  # already open
+                for d in docMgr.GetDocuments():
+                    if os.path.normcase(d.GetFilename()) == os.path.normcase(filepath):
+                        doc = d
+                        break
         else:
-            projectService = wx.GetApp().GetService(ProjectService)
-            if projectService:
-                projectDocs = projectService.FindProjectByFile(filepath)
-                if projectDocs:
-                    projectDoc = projectDocs[0]
-                    projectService.AddProjectMapping(doc, projectDoc)
-                    if hasattr(doc, "GetModel"):
-                        projectService.AddProjectMapping(doc.GetModel(), projectDoc)
+            doc = None
 
     if doc:
         docModel = doc.GetModel()
@@ -202,8 +227,82 @@ if not ACTIVEGRID_BASE_IDE:
 # Classes
 #----------------------------------------------------------------------------
 
-class ProjectDocument(wx.lib.docview.Document):
+if not ACTIVEGRID_BASE_IDE:
+    class IDEResourceFactory(DeploymentGeneration.DeploymentResourceFactory):
+        
+        def __init__(self, openDocs, dataSourceService, projectDir,
+                     preview=False, deployFilepath=None):
+
+            self.openDocs = openDocs
+            self.dataSourceService = dataSourceService
+            self.projectDir = projectDir
+            self.preview = preview
+            self.deployFilepath = deployFilepath
+            
+            self.defaultFlagsNoView = (
+                wx.GetApp().GetDocumentManager().GetFlags()|
+                wx.lib.docview.DOC_SILENT|
+                wx.lib.docview.DOC_OPEN_ONCE|
+                wx.lib.docview.DOC_NO_VIEW)
+            
+        def getModel(self, projectFile):
+            doc = wx.GetApp().GetDocumentManager().CreateDocument(
+                projectFile.filePath, flags=self.defaultFlagsNoView)
+            if (doc == None):  # already open
+                doc = self._findOpenDoc(projectFile.filePath)
+            else:
+                AddProjectMapping(doc)
+            if (doc != None):
+                return doc.GetModel()
+
+        def getDataSource(self, dataSourceName):
+            # in preview mode, runtime needs the generated Deployment
+            # to contain the requried data source. But runtime doesn't
+            # actually need to communicate to db. So here is the logic to
+            # make preview works if the required data soruce has not
+            # yet been defined.            
+            dataSource = self.dataSourceService.getDataSource(dataSourceName)
+            if (dataSource != None):
+                return dataSource
+            elif not self.preview:
+                raise DataServiceExistenceException(dataSourceName)
+            else:
+                # first to see if an existing dpl file is there, if so,
+                # use the data source in dpl file
+                if (self.deployFilepath != None):
+                    tempDply = None
+                    try:
+                        tempDply = xmlutils.load(deployFilepath)
+                    except:
+                        pass
+                    if (tempDply != None):
+                        for tempDataSource in tempDply.dataSources:
+                            if (tempDataSource.name == dataSourceName):
+                                return tempDataSource
+
+                # if unable to use dpl file, then create a dummy data source
+                import activegrid.data.dataservice as dataservice
+                return dataservice.DataSource(
+                    name=dataSourceName, dbtype=dataservice.DB_TYPE_SQLITE)
+
+        def initDocumentRef(self, projectFile, documentRef, dpl):
+            doc = self._findOpenDoc(projectFile.filePath)
+            if (doc and hasattr(doc, 'GetModel')):
+                documentRef.document = doc.GetModel()
+                if isinstance(documentRef, deploymentlib.XFormRef):
+                    doc.GetModel().linkDeployment(dpl, dpl.loader)
+
+        def _findOpenDoc(self, filePath):
+            for openDoc in self.openDocs:
+                if openDoc.GetFilename() == filePath:
+                    return openDoc
+            return None
+
+        def getProjectDir(self):
+            return self.projectDir
+
 
+class ProjectDocument(wx.lib.docview.Document):
 
     def __init__(self, model=None):
         wx.lib.docview.Document.__init__(self)
@@ -482,24 +581,22 @@ class ProjectDocument(wx.lib.docview.Document):
 
         return [fileRef.filePath for fileRef in invalidFileRefs]
 
+
     def SetStageProjectFile(self):
         self._stageProjectFile = True
 
-    def ArchiveProject(self, zipdest, tmpdir=None, stagedir=None):
-        """Stages the application files in tmpdir, and zips the stagedir, creating a zipfile that has the projectname, in zipdest. Returns path to zipfile. Optionally, pass in stagedir and we assume the app is already staged at stagedir (we don't stage again in that case)."""
-        if not stagedir:
-            if not tmpdir:
-                raise AssertionError("'tmpdir' must be set when not passing 'stagedir' so we know where to stage the app")
-            stagedir = self.StageProject(tmpdir)
+
+    def ArchiveProject(self, zipdest, stagedir):
+        """Zips stagedir, creates a zipfile that has as name the projectname, in zipdest. Returns path to zipfile."""
         if os.path.exists(zipdest):
             raise AssertionError("Cannot archive project, %s already exists" % zipdest)
         fileutils.zip(zipdest, stagedir)
 
         return zipdest
 
-        
-    def StageProject(self, tmpdir):
-        """ Copies all files that project knows about into staging location. Files that live outside of the project dir are copied into the root of the stage dir, and their recorded file path is updated. Files that live inside of the project dir keep their relative path. Generates .dpl file into staging dir. Returns path to staging dir."""
+
+    def StageProject(self, tmpdir, targetDataSourceMapping={}):
+        """ Copies all files this project knows about into staging location. Files that live outside of the project dir are copied into the root of the stage dir, and their recorded file path is updated. Files that live inside of the project dir keep their relative path. Generates .dpl file into staging dir. Returns path to staging dir."""
 
         projname = self.GetProjectName()
         stagedir = os.path.join(tmpdir, projname)
@@ -523,6 +620,9 @@ class ProjectDocument(wx.lib.docview.Document):
         # copy files to staging dir
         self._StageFiles(fileDict)
 
+        # set target data source for schemas
+        self._SetSchemaTargetDataSource(fileDict, targetDataSourceMapping)
+
         # it is unfortunate we require this. it would be nice if filepaths
         # were only in the project
         self._FixWsdlAgFiles(stagedir)
@@ -530,19 +630,27 @@ class ProjectDocument(wx.lib.docview.Document):
         # generate .dpl file
         dplfilename = projname + deploymentlib.DEPLOYMENT_EXTENSION
         dplfilepath = os.path.join(stagedir, dplfilename)
-        self.GenerateDeployment(dplfilepath, productionDeployment=True)
+        self.GenerateDeployment(dplfilepath)
 
         if self._stageProjectFile:
             # save project so we get the .agp file. not required for deployment
             # but convenient if user wants to open the deployment in the IDE
             agpfilename = projname + PROJECT_EXTENSION
-            agpfilepath = os.path.join(stagedir, agpfilename)        
+            agpfilepath = os.path.join(stagedir, agpfilename)
+
+            # if this project has deployment data sources configured, remove
+            # them. changing the project is fine, since this is a clone of
+            # the project the IDE has.
+            self.GetModel().GetAppInfo().ResetDeploymentDataSources()
+            
             f = None
             try:
                 f = open(agpfilepath, "w")
+                
                 # setting homeDir correctly is required for the "figuring out
                 # relative paths" logic when saving the project
                 self.GetModel().homeDir = stagedir
+                
                 projectlib.save(f, self.GetModel(), productionDeployment=True)
             finally:
                 try:
@@ -551,24 +659,36 @@ class ProjectDocument(wx.lib.docview.Document):
 
         return stagedir
 
-
     def _FixWsdlAgFiles(self, stagedir):
-        """For each wsdlag file in the stagedir:
-           Ensure the referenced wsdl file lives in root of stagedir. This
-           should be the case if wsdl is part of project (and staging has run).
-           If it is not at root of stagedir, copy it. Then update path in
-           wsdlag."""
+        """For each wsdlag file in the stagedir: if referenced artifact (wsdl or code file) is a known product file (such as securityservice.wsdl), make sure patch to it is parameterized with special env var. We do not want to copy those files. For user artifacts, ensure the file lives in root of stagedir. This should be the case if it is part of project (since staging has run). If it is not at root of stagedir, copy it. Then update path in wsdlag."""
         files = os.listdir(stagedir)
         for f in files:
-            if f.endswith(WsdlAgEditor.WsdlAgDocument.WSDL_AG_EXT):
+            if (f.endswith(WsdlAgEditor.WsdlAgDocument.WSDL_AG_EXT)):
                 wsdlagpath = os.path.join(stagedir, f)
                 fileObject = None
-                mod = False
+                modified = False
                 try:
                     fileObject = open(wsdlagpath)
                     serviceref = WsdlAgEditor.load(fileObject)
-                    if hasattr(serviceref, "filePath") and serviceref.filePath:
-                        mod = self.UpdateServiceRefFilePath(stagedir,serviceref)
+
+                    # referenced wsdl
+                    if (hasattr(serviceref, WsdlAgModel.WSDL_FILE_ATTR)):
+                        modified = (modified |
+                                    self._UpdateServiceRefPathAttr(
+                                        stagedir, serviceref,
+                                        WsdlAgModel.WSDL_FILE_ATTR))
+
+                    # referenced code file
+                    if (hasattr(serviceref, WsdlAgModel.LOCAL_SERVICE_ELEMENT)):
+                        lse = getattr(serviceref,
+                                      WsdlAgModel.LOCAL_SERVICE_ELEMENT)
+                        if (hasattr(lse, WsdlAgModel.LOCAL_SERVICE_FILE_ATTR)):
+                            modified = (modified |
+                                        self._UpdateServiceRefPathAttr(
+                                            stagedir, lse,
+                                            WsdlAgModel.LOCAL_SERVICE_FILE_ATTR))
+
+                    
                 finally:
                     try:
                         fileObject.close()
@@ -576,7 +696,7 @@ class ProjectDocument(wx.lib.docview.Document):
                         pass
 
                 # no need to save the file if we did not change anything
-                if not mod: continue
+                if not modified: continue
 
                 # write the wsdlag file
                 fileObject = open(wsdlagpath)
@@ -589,31 +709,81 @@ class ProjectDocument(wx.lib.docview.Document):
                         pass
                     
 
-    def UpdateServiceRefFilePath(self, stagedir, serviceref):
-        """Returns True if serviceref.filePath has been updated, False otherwise."""
-        if not os.path.exists(serviceref.filePath):
-            # should be an error? wrong place to
-            # validate that referenced file exists
-            # could print warning
+    def _UpdateServiceRefPathAttr(self, stagedir, serviceref, attrName):
+        """Returns True if serviceref path has been updated, False otherwise."""
+
+        filePath = getattr(serviceref, attrName)
+
+        if (filePath == None):
+            return False
+
+        filePath = filePath.strip()
+
+        if (len(filePath) == 0):
+            return False
+            
+
+        # if filePath starts with one of the AG systems vars, we don't
+        # have to do anything
+        if (fileutils.startsWithAgSystemVar(filePath)):
+            return False
+
+        # remove any known env var refs (we'll put them back a little below)
+        # we remove them here so that paths that do not have env vars also
+        # get parameterized correctly below
+        filePath = fileutils.expandKnownAGVars(filePath)
+
+        # make sure we have forward slashes. this is a workaround, which
+        # would not be necessary if we only write paths with forward slashes
+        # into our files
+        filePath = filePath.replace("\\", "/")
+        
+        filePath = os.path.abspath(filePath)        
+
+        if (not os.path.exists(filePath)):
+            # Wrong place to validate that referenced file exists, so just
+            # give up
             return False
             
         # If the referenced file is in stagedir already, there's nothing to do
-        if fileutils.hasAncestorDir(serviceref.filePath, stagedir):
+        if (fileutils.hasAncestorDir(filePath, stagedir)):
             return False
 
         # The path points outside of stagedir.
 
         # Check if we already have the referenced wsdl file at root, should be
-        # the case if the referenced wsdl is part of project
-        # Copy it if we don't have it
-        relPath = os.path.basename(serviceref.filePath)
-        stagepath = os.path.join(stagedir, relPath)
-        if not os.path.exists(stagepath):
-            fileutils.copyFile(serviceref.filePath, stagepath)
-
-        serviceref.filePath = relPath
+        # the case if the referenced wsdl is part of project.
+        # Copy it if we don't have it, unless it lives in one of the known
+        # product directories - in which case we parameterize the known path
+        # with one of our AG system vars
+        relPath = os.path.basename(filePath)
+        stagePath = os.path.join(stagedir, relPath)
+
+        if (not os.path.exists(stagePath)):
+            pFilePath = fileutils.parameterizePathWithAGSystemVar(filePath)
+            if pFilePath == filePath: # no parameterization happened, copy
+                fileutils.copyFile(filePath, stagePath)
+                setattr(serviceref, attrName, relPath)
+            else:
+                setattr(serviceref, attrName, pFilePath.replace("\\", "/"))
+        else:
+            setattr(serviceref, attrName, relPath)
 
         return True
+
+
+    def _SetSchemaTargetDataSource(self, projectFiles, dsmapping):
+        """Update schema's default data source, if necessary."""
+
+        for projectFile in projectFiles:
+            if (projectFile.type == basedocmgr.FILE_TYPE_SCHEMA):
+                name = os.path.basename(projectFile.filePath)
+                if (dsmapping.has_key(name)):
+                    schema = xmlutils.load(projectFile.filePath)
+                    defaultName = schema.getDefaultDataSourceName()
+                    if (defaultName != dsmapping[name]):
+                        schema.setDefaultDataSourceName(dsmapping[name])
+                        xmlutils.save(projectFile.filePath, schema)
         
         
     def _StageFiles(self, fileDict):
@@ -664,11 +834,6 @@ class ProjectDocument(wx.lib.docview.Document):
         for filename in foreignFiles:
             if filename in projectRootFiles:
                 raise IOError("File outside of project, \"%s\", cannot have same name as file at project root" % filename)
-
-        # REVIEW stoens@activegrid.com 19-Oct-05 --
-        # We could also validate that user does not already have a .dpl file
-        # since we're going to generate one...
-        
         return rtn
     
                             
@@ -680,7 +845,19 @@ class ProjectDocument(wx.lib.docview.Document):
         self.Modify(True)
         return True
 
+    def GetSchemas(self):
+        """Returns list of schema models (activegrid.model.schema.schema) for all schemas in this project."""
+        
+        rtn = []
+        resourceFactory = self._GetResourceFactory()
+        for projectFile in self.GetModel().projectFiles:
+            if (projectFile.type == basedocmgr.FILE_TYPE_SCHEMA):
+                schema = resourceFactory.getModel(projectFile)
+                if (schema != None):
+                    rtn.append(schema)
 
+        return rtn
+        
     def GetFiles(self):
         return self.GetModel().filePaths
 
@@ -709,199 +886,117 @@ class ProjectDocument(wx.lib.docview.Document):
         return os.path.splitext(os.path.basename(self.GetFilename()))[0]
 
 
-    def GetDeploymentFilepath(self):
-        projectName = self.GetProjectName()
-        return os.path.join(self.GetModel().homeDir, projectName + "RunTime_tmp" + deploymentlib.DEPLOYMENT_EXTENSION)
-        
+    def GetDeploymentFilepath(self, pre17=False):
+        if (pre17):
+            name = self.GetProjectName() + PRE_17_TMP_DPL_NAME
+        else:
+            name = self.GetProjectName() + _17_TMP_DPL_NAME
+        return os.path.join(self.GetModel().homeDir, name)
+    
 
-    def GenerateDeployment(self, deployFilepath=None, preview=False, productionDeployment=False):
+    def _GetResourceFactory(self, preview=False, deployFilepath=None):
+        return IDEResourceFactory(
+            openDocs=wx.GetApp().GetDocumentManager().GetDocuments(),
+            dataSourceService=wx.GetApp().GetService(DataModelEditor.DataSourceService),
+            projectDir=os.path.dirname(self.GetFilename()),
+            preview=preview,
+            deployFilepath=deployFilepath)
+
+    def GenerateDeployment(self, deployFilepath=None, preview=False):
+        
         if ACTIVEGRID_BASE_IDE:
             return
-        
-        def FindOpenDoc(filePath):
-            openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
-            for openDoc in openDocs:
-                if openDoc.GetFilename() == filePath:
-                    return openDoc
-            return None
 
         if not deployFilepath:
             deployFilepath = self.GetDeploymentFilepath()
-            
-        deployment = deploymentlib.Deployment(deployFilepath)
 
-        defaultFlagsNoView = wx.GetApp().GetDocumentManager().GetFlags()|wx.lib.docview.DOC_SILENT|wx.lib.docview.DOC_OPEN_ONCE|wx.lib.docview.DOC_NO_VIEW
-        self.GetAppInfo().CopyToDeployment(deployment)
-
-        for file in self.GetModel()._files:
-            if not file.type:
-                continue
-            elif file.type == basedocmgr.FILE_TYPE_SERVICE:  # set serviceRefs
-                doc = wx.GetApp().GetDocumentManager().CreateDocument(file.filePath, flags=defaultFlagsNoView)
-                if (doc == None):  # already open
-                    doc = FindOpenDoc(file.filePath)
-                if doc:
-                    serviceRef = doc.GetModel()
-                    if serviceRef:
-                        documentRef = copy.copy(serviceRef)
-                        deployment.serviceRefs.append(documentRef)
-
-                        if not productionDeployment:
-                            # filePath should point to location of wsdl file
-                            # wsdlag filePath points to relative path to wsdl file from wsdlag location
-                            # but deployment needs relative path from deployment location, so here's the conversion
-                            curDir = os.path.dirname(self.GetFilename()) + os.sep
-                            filePath = file.document.fileName
-                            if (filePath == None):
-                                raise Exception("Cannot find file \"%s\"" % file.filePath)
-                            if filePath.startswith(curDir):
-                                filePath = filePath[len(curDir):]
-                            if os.sep != '/':
-                                filePath = filePath.replace(os.sep, "/")
-                            documentRef.filePath = filePath
-                        
-                            documentRef.document = file.document
-                            
-                        if serviceRef.serviceType == deploymentlib.SERVICE_DATABASE and serviceRef.databaseService:
-                            dataSourceService = wx.GetApp().GetService(DataModelEditor.DataSourceService)
-                            ds = dataSourceService.getDataSource(serviceRef.databaseService.datasourceName)
-                            if ds:
-                                found = False
-                                for d in deployment.dataSources:
-                                    if d.name == ds.name:
-                                        found = True
-                                        break
-                                if not found:
-                                    deployment.dataSources.append(ds)
-            else:
-                curDir = os.path.dirname(self.GetFilename()) + os.sep
-                filePath = file.filePath
-                if filePath.startswith(curDir):
-                    filePath = filePath[len(curDir):]
-                if os.sep != '/':
-                    filePath = filePath.replace(os.sep, "/")
-                    
-                if file.type == basedocmgr.FILE_TYPE_XFORM:
-                    documentRef = deploymentlib.XFormRef()
-                    deployment.xformRefs.append(documentRef)
-                elif file.type == basedocmgr.FILE_TYPE_PROCESS:
-                    documentRef = deploymentlib.ProcessRef()
-                    deployment.processRefs.append(documentRef)
-                elif file.type == basedocmgr.FILE_TYPE_SCHEMA:
-                    # set schemaRefs
-                    documentRef = deploymentlib.SchemaRef()
-                    deployment.schemaRefs.append(documentRef)
-
-                    # set dataSources
-                    doc = wx.GetApp().GetDocumentManager().CreateDocument(file.filePath, flags=defaultFlagsNoView)
-                    if (doc == None):  # already open
-                        doc = FindOpenDoc(file.filePath)
-                    if doc:
-                        dataSourceService = wx.GetApp().GetService(DataModelEditor.DataSourceService)
-                        ds = dataSourceService.getDataSource(doc.GetModel().getDefaultDataSourceName())
-                        if ds:
-                            found = False
-                            for d in deployment.dataSources:
-                                if d.name == ds.name:
-                                    found = True
-                                    break
-                            if not found:
-                                deployment.dataSources.append(ds)
-                                
-                        # set keyServices
-                        keyServices = doc.GetModel().keyServices
-                        for keyService in keyServices:        
-                            # add default key service to deployment
-                            if not productionDeployment:
-                                mainModuleDir = sysutils.mainModuleDir
-                            else:
-                                mainModuleDir = sysutils.MAINMODULE_DIR_VAR
-                            wsdlFullPath = os.path.join(mainModuleDir, "..", "wsdl", DataModelEditor.DEFAULT_KEYSERVICE_WSDL_FILENAME)
-                            keyServiceRef = deploymentlib.ServiceRef(filePath=wsdlFullPath)
-                            deployment.serviceRefs.append(keyServiceRef)
-                            
-                            keyServiceRef.name = keyService
-                            keyServiceRef.serviceType = deploymentlib.SERVICE_LOCAL
-                            keyServiceRef.localService = deploymentlib.LocalService()
-                            if keyService == DataModelEditor.DEFAULT_KEYSERVICE:
-                                keyServiceRef.filePath = wsdlFullPath
-                                keyServiceRef.localServiceClassName = DataModelEditor.DEFAULT_KEYSERVICE_CLASSNAME
-                            
+        d = DeploymentGeneration.DeploymentGenerator(
+            self.GetModel(), self._GetResourceFactory(preview,
+                                                      deployFilepath))
+                
+        dpl = d.getDeployment(deployFilepath)
 
-                elif file.type == basedocmgr.FILE_TYPE_SKIN:
-                    documentRef = deploymentlib.SkinRef(deployment)
-                    deployment.skinref = documentRef
-                elif file.type == basedocmgr.FILE_TYPE_IDENTITY:
-                    documentRef = deploymentlib.IdentityRef()
-                    deployment.identityRefs.append(documentRef)
-                else:
-                    continue
-                        
-                documentRef.name = file.name
-                documentRef.filePath = filePath
-                doc = FindOpenDoc(file.filePath)
-                if doc and hasattr(doc, 'GetModel'):
-                    documentRef.document = doc.GetModel()
-                    if isinstance(documentRef, deploymentlib.XFormRef):
-                        doc.GetModel().linkDeployment(deployment, deployment.loader)
-       
         if preview:
-            deployment.initialize()  # used in preview only
-            
-        if 0: # preview:  # setPrototype not working, commented this out
-            deploymentlib._deploymentCache.setPrototype(deployment.fileName, deployment)
-        else:
-            deploymentlib.saveThroughCache(deployment.fileName, deployment)
+            dpl.initialize()  # used in preview only
 
-        return deployFilepath        
-        
+        # REVIEW 07-Apr-06 stoens@activegrid.com -- Check if there's a
+        # tmp dpl file with pre 17 name, if so, delete it, so user doesn't end
+        # up with unused file in project dir. We should probably remove this
+        # check after 1.7 goes out.
+        fileutils.remove(self.GetDeploymentFilepath(pre17=True))
 
+        deploymentlib.saveThroughCache(dpl.fileName, dpl)
+        return deployFilepath
+        
     def AddNameSpaces(self, filePaths):
-        """ Add any new wsdl namespaces to bpel files """
+        """ Add any new wsdl and schema namespaces to bpel files """
         """ Add any new schema namespaces to wsdl files """
         if ACTIVEGRID_BASE_IDE:
             return
 
-        serviceRefs = self.GetAppDocMgr().allServiceRefs  # wsdl
-
         processRefs = self.GetAppDocMgr().findRefsByFileType(basedocmgr.FILE_TYPE_PROCESS) # bpel
-        if processRefs and serviceRefs:
+        schemaRefs = self.GetAppDocMgr().findRefsByFileType(basedocmgr.FILE_TYPE_SCHEMA) # xsd
+        serviceRefs = self.GetAppDocMgr().allServiceRefs  # wsdl
+        
+        # update bpel files
+        if processRefs and (serviceRefs or schemaRefs):
             for processRef in processRefs:
-                processDoc = processRef._GetDoc()
+                processDoc = processRef.ideDocument
                 process = processDoc.GetModel()
                 
-                modified = False
-                for serviceRef in serviceRefs:
-                    wsdl = serviceRef.document
-                    if (wsdl.fileName in filePaths
-                    or serviceRef.filePath in filePaths):
-                        wsdlLongNS = wsdl.targetNamespace
-                        wsdlShortNS = self.GetAppDocMgr().findShortNS(wsdlLongNS)
-                        if not wsdlShortNS:
-                            wsdlShortNS = xmlutils.genShortNS(process, wsdlLongNS)
-                        xmlutils.addNSAttribute(process, wsdlShortNS, wsdlLongNS)
-                        modified = True
-                if modified:
-                    processDoc.OnSaveDocument(processDoc.GetFilename())
-
-        schemaRefs = self.GetAppDocMgr().findRefsByFileType(basedocmgr.FILE_TYPE_SCHEMA)
-        if schemaRefs and serviceRefs:
+                if processDoc and process:
+                    modified = False
+                    
+                    # add wsdl namespaces to bpel file
+                    for serviceRef in serviceRefs:
+                        wsdl = serviceRef.document
+                        if (wsdl
+                        and (wsdl.fileName in filePaths
+                        or serviceRef.filePath in filePaths)):
+                            wsdlLongNS = wsdl.targetNamespace
+                            wsdlShortNS = self.GetAppDocMgr().findShortNS(wsdlLongNS)
+                            if not wsdlShortNS:
+                                wsdlShortNS = xmlutils.genShortNS(process, wsdlLongNS)
+                            xmlutils.addNSAttribute(process, wsdlShortNS, wsdlLongNS)
+                            modified = True
+                            
+                    # add schema namespaces to bpel file
+                    for schemaRef in schemaRefs:
+                        schema = schemaRef.document
+                        if schema and schema.fileName in filePaths:
+                            schemaLongNS = schema.targetNamespace
+                            schemaShortNS = self.GetAppDocMgr().findShortNS(schemaLongNS)
+                            if not schemaShortNS:
+                                schemaShortNS = xmlutils.genShortNS(process, schemaLongNS)
+                            xmlutils.addNSAttribute(process, schemaShortNS, schemaLongNS)
+                            modified = True
+    
+                    if modified:
+                        processDoc.OnSaveDocument(processDoc.GetFilename())
+
+
+        # update wsdl files
+        if serviceRefs and schemaRefs:
             for serviceRef in serviceRefs:
                 wsdl = serviceRef.document
                 wsdlDoc = serviceRef.ideDocument
                 
-                modified = False
-                for schemaRef in schemaRefs:
-                    schema = schemaRef.document
-                    if schema.fileName in filePaths:
-                        schemaLongNS = schema.targetNamespace
-                        schemaShortNS = self.GetAppDocMgr().findShortNS(schemaLongNS)
-                        if not schemaShortNS:
-                            schemaShortNS = xmlutils.genShortNS(process, schemaLongNS)
-                        xmlutils.addNSAttribute(wsdl, schemaShortNS, schemaLongNS)
-                        modified = True
-                if modified:
-                    wsdlDoc.OnSaveDocument(wsdlDoc.GetFilename())
+                if wsdl and wsdlDoc:
+                    modified = False
+                    
+                    # add schema namespace to wsdl file
+                    for schemaRef in schemaRefs:
+                        schema = schemaRef.document
+                        if schema and schema.fileName in filePaths:
+                            schemaLongNS = schema.targetNamespace
+                            schemaShortNS = self.GetAppDocMgr().findShortNS(schemaLongNS)
+                            if not schemaShortNS:
+                                schemaShortNS = xmlutils.genShortNS(wsdl, schemaLongNS)
+                            xmlutils.addNSAttribute(wsdl, schemaShortNS, schemaLongNS)
+                            modified = True
+                            
+                    if modified:
+                        wsdlDoc.OnSaveDocument(wsdlDoc.GetFilename())
 
 
 class NewProjectWizard(Wizard.BaseWizard):
@@ -918,10 +1013,10 @@ class NewProjectWizard(Wizard.BaseWizard):
 
 
     def CreateProjectLocation(self,wizard):
-        page = Wizard.TitledWizardPage(wizard, _("Project File Location"))
+        page = Wizard.TitledWizardPage(wizard, _("Name and Location"))
 
-        page.GetSizer().Add(wx.StaticText(page, -1, _("\nSelect the directory and filename for the project.\n\n")))
-        self._projectName, self._dirCtrl, sizer, self._fileValidation = UICommon.CreateDirectoryControl(page, fileExtension="agp", appDirDefaultStartDir=True)
+        page.GetSizer().Add(wx.StaticText(page, -1, _("\nEnter the name and location for the project.\n")))
+        self._projectName, self._dirCtrl, sizer, self._fileValidation = UICommon.CreateDirectoryControl(page, fileExtension="agp", appDirDefaultStartDir=True, fileLabel=_("Name:"), dirLabel=_("Location:"))
         page.GetSizer().Add(sizer, 1, flag=wx.EXPAND)
 
         wizard.Layout()
@@ -930,7 +1025,7 @@ class NewProjectWizard(Wizard.BaseWizard):
 
 
     def RunWizard(self, existingTables = None, existingRelationships = None):
-        status = wx.wizard.Wizard.RunWizard(self, self._projectLocationPage)
+        status = Wizard.BaseWizard.RunWizard(self, self._projectLocationPage)
         if status:
             wx.ConfigBase_Get().Write(PROJECT_DIRECTORY_KEY, self._dirCtrl.GetValue())
             docManager = wx.GetApp().GetTopWindow().GetDocumentManager()
@@ -959,7 +1054,7 @@ class NewProjectWizard(Wizard.BaseWizard):
     def OnWizPageChanging(self, event):
         if event.GetDirection():  # It's going forwards
             if event.GetPage() == self._projectLocationPage:
-                if not self._fileValidation(noFirstCharDigit=True):
+                if not self._fileValidation(validClassName=True):
                     event.Veto()
                     return
                 self._fullProjectPath = os.path.join(self._dirCtrl.GetValue(),UICommon.MakeNameEndInExtension(self._projectName.GetValue(), PROJECT_EXTENSION))
@@ -1473,8 +1568,6 @@ class ProjectView(wx.lib.docview.View):
 
         self._treeCtrl = ProjectTreeCtrl(panel, -1, style = wx.TR_HIDE_ROOT | wx.TR_HAS_BUTTONS | wx.TR_EDIT_LABELS | wx.TR_DEFAULT_STYLE | wx.TR_MULTIPLE | wx.TR_EXTENDED)
         self._treeCtrl.AddRoot(_("Projects"))
-        wx.EVT_TREE_BEGIN_DRAG(self._treeCtrl, self._treeCtrl.GetId(), self.OnBeginDrag)
-        wx.EVT_TREE_END_DRAG(self._treeCtrl, self._treeCtrl.GetId(), self.OnEndDrag)
         if self._embeddedWindow:
             sizer.Add(self._treeCtrl, 1, wx.EXPAND|wx.BOTTOM, HALF_SPACE)  # allow space for embedded window resize-sash
         else:
@@ -1484,7 +1577,7 @@ class ProjectView(wx.lib.docview.View):
         sizer = wx.BoxSizer(wx.VERTICAL)
         
         if wx.GetApp().IsMDI():
-            sizer.Add(panel, 1, wx.EXPAND|wx.BOTTOM, 1) # wx.Bug: without bottom margin, can't resize embedded window
+            sizer.Add(panel, 1, wx.EXPAND|wx.BOTTOM, 3) # wxBug: without bottom margin, can't resize embedded window
         else:
             sizer.Add(panel, 1, wx.EXPAND)
             
@@ -1503,16 +1596,17 @@ class ProjectView(wx.lib.docview.View):
         wx.EVT_TREE_BEGIN_LABEL_EDIT(self._treeCtrl, self._treeCtrl.GetId(), self.OnBeginLabelEdit)
         wx.EVT_TREE_END_LABEL_EDIT(self._treeCtrl, self._treeCtrl.GetId(), self.OnEndLabelEdit)
         wx.EVT_RIGHT_DOWN(self._treeCtrl, self.OnRightClick)
-        wx.EVT_LEFT_DOWN(self._treeCtrl, self.OnLeftClick)
         wx.EVT_KEY_DOWN(self._treeCtrl, self.OnKeyPressed)
         wx.EVT_TREE_ITEM_COLLAPSED(self._treeCtrl, self._treeCtrl.GetId(), self.SaveFolderState)
         wx.EVT_TREE_ITEM_EXPANDED(self._treeCtrl, self._treeCtrl.GetId(), self.SaveFolderState)
-        # wx.EVT_COMMAND_RIGHT_CLICK(self._treeCtrl, self._treeCtrl.GetId(), self.OnRightClick) # wxBug: This isn't working for some reason
+        wx.EVT_TREE_BEGIN_DRAG(self._treeCtrl, self._treeCtrl.GetId(), self.OnBeginDrag)
+        wx.EVT_TREE_END_DRAG(self._treeCtrl, self._treeCtrl.GetId(), self.OnEndDrag)
+        wx.EVT_LEFT_DOWN(self._treeCtrl, self.OnLeftClick)
 
         # drag-and-drop support
         dt = ProjectFileDropTarget(self)
         self._treeCtrl.SetDropTarget(dt)
-
+        
         return True
 
 
@@ -1625,9 +1719,11 @@ class ProjectView(wx.lib.docview.View):
         else:
             # need this to accelerate closing down app if treeCtrl has lots of items
             self._treeCtrl.Freeze()
-            rootItem = self._treeCtrl.GetRootItem()
-            self._treeCtrl.DeleteChildren(rootItem)
-            self._treeCtrl.Thaw()
+            try:
+                rootItem = self._treeCtrl.GetRootItem()
+                self._treeCtrl.DeleteChildren(rootItem)
+            finally:
+                self._treeCtrl.Thaw()
 
         # We don't need to delete the window since it is a floater/embedded
         return True
@@ -1638,7 +1734,8 @@ class ProjectView(wx.lib.docview.View):
 
 
     def OnUpdate(self, sender = None, hint = None):
-        wx.lib.docview.View.OnUpdate(self, sender, hint)
+        if wx.lib.docview.View.OnUpdate(self, sender, hint):
+            return
         
         if hint:
             if hint[0] == "add":
@@ -1648,54 +1745,56 @@ class ProjectView(wx.lib.docview.View):
                     
                 self._treeCtrl.Freeze()
 
-                newFilePaths = hint[2]  # need to be added and selected, and sorted
-                oldFilePaths = hint[3]  # need to be selected
-                self._treeCtrl.UnselectAll()
-                
-                mode = self.GetMode()
-                
-                project = projectDoc.GetModel()
-                projectDir = project.homeDir
-                rootItem = self._treeCtrl.GetRootItem()
+                try:
+                    newFilePaths = hint[2]  # need to be added and selected, and sorted
+                    oldFilePaths = hint[3]  # need to be selected
+                    self._treeCtrl.UnselectAll()
                     
-                # add new folders and new items
-                addList = []                    
-                for filePath in newFilePaths:
-                    file = project.FindFile(filePath)
-                    if file:
-                        if mode == ProjectView.LOGICAL_MODE:
-                            folderPath = file.logicalFolder
-                        else:  # ProjectView.PHYSICAL_MODE
-                            folderPath = file.GetRelativeFolder(projectDir)
-                        if folderPath:
-                            self._treeCtrl.AddFolder(folderPath)
-                            folder = self._treeCtrl.FindFolder(folderPath)
-                        else:
-                            folder = rootItem
-                        item = self._treeCtrl.AppendItem(folder, os.path.basename(file.filePath), file)
-                        addList.append(item)
-
-                # sort folders with new items
-                parentList = []
-                for item in addList:
-                    parentItem = self._treeCtrl.GetItemParent(item)
-                    if parentItem not in parentList:
-                        parentList.append(parentItem)
-                for parentItem in parentList:
-                    self._treeCtrl.SortChildren(parentItem)
-
-                # select all the items user wanted to add
-                lastItem = None
-                for filePath in (oldFilePaths + newFilePaths):
-                    item = self._treeCtrl.FindItem(filePath)
-                    if item:
-                        self._treeCtrl.SelectItem(item)
-                        lastItem = item
+                    mode = self.GetMode()
+                    
+                    project = projectDoc.GetModel()
+                    projectDir = project.homeDir
+                    rootItem = self._treeCtrl.GetRootItem()
                         
-                if lastItem:        
-                    self._treeCtrl.EnsureVisible(lastItem)
+                    # add new folders and new items
+                    addList = []                    
+                    for filePath in newFilePaths:
+                        file = project.FindFile(filePath)
+                        if file:
+                            if mode == ProjectView.LOGICAL_MODE:
+                                folderPath = file.logicalFolder
+                            else:  # ProjectView.PHYSICAL_MODE
+                                folderPath = file.physicalFolder
+                            if folderPath:
+                                self._treeCtrl.AddFolder(folderPath)
+                                folder = self._treeCtrl.FindFolder(folderPath)
+                            else:
+                                folder = rootItem
+                            item = self._treeCtrl.AppendItem(folder, os.path.basename(file.filePath), file)
+                            addList.append(item)
+    
+                    # sort folders with new items
+                    parentList = []
+                    for item in addList:
+                        parentItem = self._treeCtrl.GetItemParent(item)
+                        if parentItem not in parentList:
+                            parentList.append(parentItem)
+                    for parentItem in parentList:
+                        self._treeCtrl.SortChildren(parentItem)
+    
+                    # select all the items user wanted to add
+                    lastItem = None
+                    for filePath in (oldFilePaths + newFilePaths):
+                        item = self._treeCtrl.FindItem(filePath)
+                        if item:
+                            self._treeCtrl.SelectItem(item)
+                            lastItem = item
+                            
+                    if lastItem:        
+                        self._treeCtrl.EnsureVisible(lastItem)
 
-                self._treeCtrl.Thaw()
+                finally:
+                    self._treeCtrl.Thaw()
                 return
 
             elif hint[0] == "remove":
@@ -1705,17 +1804,19 @@ class ProjectView(wx.lib.docview.View):
                     
                 self._treeCtrl.Freeze()
 
-                filePaths = hint[2]
-                self._treeCtrl.UnselectAll()
-                
-                for filePath in filePaths:
-                    item = self._treeCtrl.FindItem(filePath)
-                    if item:
-                        self._treeCtrl.Delete(item)
-
-                self._treeCtrl.UnselectAll()  # wxBug: even though we unselected earlier, an item still gets selected after the delete
+                try:
+                    filePaths = hint[2]
+                    self._treeCtrl.UnselectAll()
+                    
+                    for filePath in filePaths:
+                        item = self._treeCtrl.FindItem(filePath)
+                        if item:
+                            self._treeCtrl.Delete(item)
+    
+                    self._treeCtrl.UnselectAll()  # wxBug: even though we unselected earlier, an item still gets selected after the delete
                 
-                self._treeCtrl.Thaw()
+                finally:
+                    self._treeCtrl.Thaw()
                 return
                 
             elif hint[0] == "rename":
@@ -1724,10 +1825,12 @@ class ProjectView(wx.lib.docview.View):
                     return
                     
                 self._treeCtrl.Freeze()
-                item = self._treeCtrl.FindItem(hint[2])
-                self._treeCtrl.SetItemText(item, os.path.basename(hint[3]))
-                self._treeCtrl.EnsureVisible(item)
-                self._treeCtrl.Thaw()
+                try:
+                    item = self._treeCtrl.FindItem(hint[2])
+                    self._treeCtrl.SetItemText(item, os.path.basename(hint[3]))
+                    self._treeCtrl.EnsureVisible(item)
+                finally:
+                    self._treeCtrl.Thaw()
                 return
                 
             elif hint[0] == "rename folder":
@@ -1736,14 +1839,16 @@ class ProjectView(wx.lib.docview.View):
                     return
                     
                 self._treeCtrl.Freeze()
-                item = self._treeCtrl.FindFolder(hint[2])
-                if item:
-                    self._treeCtrl.UnselectAll()
-                    self._treeCtrl.SetItemText(item, os.path.basename(hint[3]))
-                    self._treeCtrl.SortChildren(self._treeCtrl.GetItemParent(item))
-                    self._treeCtrl.SelectItem(item)
-                    self._treeCtrl.EnsureVisible(item)
-                self._treeCtrl.Thaw()
+                try:
+                    item = self._treeCtrl.FindFolder(hint[2])
+                    if item:
+                        self._treeCtrl.UnselectAll()
+                        self._treeCtrl.SetItemText(item, os.path.basename(hint[3]))
+                        self._treeCtrl.SortChildren(self._treeCtrl.GetItemParent(item))
+                        self._treeCtrl.SelectItem(item)
+                        self._treeCtrl.EnsureVisible(item)
+                finally:
+                    self._treeCtrl.Thaw()
                 return
      
 
@@ -1776,9 +1881,23 @@ class ProjectView(wx.lib.docview.View):
     def ProcessEvent(self, event):
         id = event.GetId()
         if id == ProjectService.CLOSE_PROJECT_ID:
-            document = self.GetDocument()
-            if document:
-                if self.GetDocumentManager().CloseDocument(document, False):
+            projectDoc = self.GetDocument()
+            if projectDoc:
+                projectService = wx.GetApp().GetService(ProjectService)
+                if projectService:
+                    openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
+                    for openDoc in openDocs[:]:  # need to make a copy, as each file closes we're off by one
+                        if projectDoc == openDoc:  # close project last
+                            continue
+                            
+                        if projectDoc == projectService.FindProjectFromMapping(openDoc):
+                            self.GetDocumentManager().CloseDocument(openDoc, False)
+                            
+                            projectService.RemoveProjectMapping(openDoc)
+                            if hasattr(openDoc, "GetModel"):
+                                projectService.RemoveProjectMapping(openDoc.GetModel())
+                    
+                if self.GetDocumentManager().CloseDocument(projectDoc, False):
                     self.RemoveCurrentDocumentUpdate()
             return True
         elif id == ProjectService.ADD_FILES_TO_PROJECT_ID:
@@ -1881,6 +2000,19 @@ class ProjectView(wx.lib.docview.View):
             return True
         elif (id == wx.ID_CLEAR
         or id == ProjectService.RENAME_ID):
+            items = self._treeCtrl.GetSelections()
+            if items:
+                hasViewSelected = False
+                for item in items:
+                    if self._IsItemFile(item):
+                        file = self._GetItemFile(item)
+                        if file.type == 'xform':
+                            hasViewSelected = True
+                            break
+                if hasViewSelected:
+                    event.Enable(False)
+                    return True
+
             event.Enable(self._HasFilesSelected() or (self.GetDocument() != None and self.GetMode() == ProjectView.LOGICAL_MODE and self._HasFoldersSelected()))
             return True
         elif id == wx.ID_PASTE:
@@ -1979,61 +2111,70 @@ class ProjectView(wx.lib.docview.View):
         wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
         self._treeCtrl.Freeze()
 
-        rootItem = self._treeCtrl.GetRootItem()
-        self._treeCtrl.DeleteChildren(rootItem)
-            
-        if document:
-            mode = self.GetMode()
-            docFilePath = document.GetFilename()
+        try:
+            rootItem = self._treeCtrl.GetRootItem()
+            self._treeCtrl.DeleteChildren(rootItem)
             
-            if mode == ProjectView.LOGICAL_MODE:
-                folders = document.GetModel().logicalFolders
-            else:
-                folders = document.GetModel().GetRelativeFolders()
+            if document:
+                mode = self.GetMode()
+                docFilePath = document.GetFilename()
                 
-            folders.sort()
-            folderItems = []
-            for folderPath in folders:
-                folderItems = folderItems + self._treeCtrl.AddFolder(folderPath)
-                                        
-            for file in document.GetModel()._files:
                 if mode == ProjectView.LOGICAL_MODE:
-                    folder = file.logicalFolder
+                    folders = document.GetModel().logicalFolders
                 else:
-                    folder = file.GetRelativeFolder(document.GetModel().homeDir)
-                if folder:
-                    folderTree = folder.split('/')
-                
-                    item = rootItem
-                    for folderName in folderTree:
-                        found = False
-                        (child, cookie) = self._treeCtrl.GetFirstChild(item)
-                        while child.IsOk():
-                            if self._treeCtrl.GetItemText(child) == folderName:
-                                item = child 
-                                found = True
+                    folders = document.GetModel().physicalFolders
+                    
+                folders.sort()
+                folderItems = []
+                for folderPath in folders:
+                    folderItems = folderItems + self._treeCtrl.AddFolder(folderPath)
+                                            
+                for file in document.GetModel()._files:
+                    if mode == ProjectView.LOGICAL_MODE:
+                        folder = file.logicalFolder
+                    else:
+                        folder = file.physicalFolder
+                    if folder:
+                        folderTree = folder.split('/')
+                    
+                        item = rootItem
+                        for folderName in folderTree:
+                            found = False
+                            (child, cookie) = self._treeCtrl.GetFirstChild(item)
+                            while child.IsOk():
+                                if self._treeCtrl.GetItemText(child) == folderName:
+                                    item = child 
+                                    found = True
+                                    break
+                                (child, cookie) = self._treeCtrl.GetNextChild(item, cookie)
+                                
+                            if not found:
+                                print "error folder '%s' not found for %s" % (folder, file.filePath)
                                 break
-                            (child, cookie) = self._treeCtrl.GetNextChild(item, cookie)
-                            
-                        if not found:
-                            print "error folder '%s' not found for %s" % (folder, file.filePath)
-                            break
-                else:
-                    item = rootItem
+                    else:
+                        item = rootItem
+                        
+                    fileItem = self._treeCtrl.AppendItem(item, os.path.basename(file.filePath), file)
                     
-                fileItem = self._treeCtrl.AppendItem(item, os.path.basename(file.filePath), file)
+                self._treeCtrl.SortChildren(rootItem)
+                for item in folderItems:
+                    self._treeCtrl.SortChildren(item)
+    
+                self.LoadFolderState()
+    
+                self._treeCtrl.SetFocus()
+                (child, cookie) = self._treeCtrl.GetFirstChild(self._treeCtrl.GetRootItem())
+                if child.IsOk():
+                    self._treeCtrl.UnselectAll()
+                    self._treeCtrl.SelectItem(child)
+                    self._treeCtrl.ScrollTo(child)
                 
-            self._treeCtrl.SortChildren(rootItem)
-            for item in folderItems:
-                self._treeCtrl.SortChildren(item)
-
-            self.LoadFolderState()
-
-            if self._embeddedWindow:
-                document.GetCommandProcessor().SetEditMenu(wx.GetApp().GetEditMenu(self._GetParentFrame()))
+                if self._embeddedWindow:
+                    document.GetCommandProcessor().SetEditMenu(wx.GetApp().GetEditMenu(self._GetParentFrame()))
 
-        self._treeCtrl.Thaw()
-        wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
+        finally:
+            self._treeCtrl.Thaw()
+            wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
 
 
     def ProjectHasFocus(self):
@@ -2053,6 +2194,11 @@ class ProjectView(wx.lib.docview.View):
         return False
 
 
+    def ClearFolderState(self):
+        config = wx.ConfigBase_Get()
+        config.DeleteGroup(getProjectKeyName(self.GetDocument().GetFilename()))
+        
+
     def SaveFolderState(self, event=None):
         """ Save the open/close state of folders """
 
@@ -2070,25 +2216,32 @@ class ProjectView(wx.lib.docview.View):
 
 
     def LoadFolderState(self):
-        """ Load the open/close state of folders """
+        """ Load the open/close state of folders. """
         self._loading = True
-        
+      
         config = wx.ConfigBase_Get()
-        openFolderData = config.Read(getProjectKeyName(self.GetDocument().GetFilename(), self.GetMode()))
+        openFolderData = config.Read(getProjectKeyName(self.GetDocument().GetFilename(), self.GetMode()), "")
         if openFolderData:
             folderList = eval(openFolderData)
                 
             folderItemList = self._GetFolderItems(self._treeCtrl.GetRootItem())
             for item in folderItemList:
-                f = self._GetItemFolderPath(item)
-                if f in folderList:
+                folderPath = self._GetItemFolderPath(item)
+                if folderPath in folderList:
                     self._treeCtrl.Expand(item)
-##                else:  # not needed, initial state is collapsed
-##                    self._treeCtrl.Collapse(item)
-        else:  # default is to open all folders    
+                else:
+                    self._treeCtrl.Collapse(item)
+
+        else:
+            projectService = wx.GetApp().GetService(ProjectService)
+            
             folderItemList = self._GetFolderItems(self._treeCtrl.GetRootItem())
             for item in folderItemList:
-                self._treeCtrl.Expand(item)
+                folderPath = self._GetItemFolderPath(item)
+                if projectService.FindLogicalViewFolderCollapsedDefault(folderPath):  # get default initial state
+                    self._treeCtrl.Collapse(item)
+                else:
+                    self._treeCtrl.Expand(item)
             
         self._loading = False
 
@@ -2190,7 +2343,7 @@ class ProjectView(wx.lib.docview.View):
                     if len(descr) > 0:
                         descr = descr + _('|')
                     descr = descr + temp.GetDescription() + _(" (") + temp.GetFileFilter() + _(") |") + temp.GetFileFilter()  # spacing is important, make sure there is no space after the "|", it causes a bug on wx_gtk
-            descr = _("All (*.*)|*.*|%s") % descr # spacing is important, make sure there is no space after the "|", it causes a bug on wx_gtk
+            descr = _("All|*.*|%s") % descr # spacing is important, make sure there is no space after the "|", it causes a bug on wx_gtk
         else:
             descr = _("*.*")
 
@@ -2254,7 +2407,7 @@ class ProjectView(wx.lib.docview.View):
                 descr = descr + _('|')
             descr = template.GetDescription() + _(" (") + template.GetFileFilter() + _(")")
             choices.append(descr)
-        choices.insert(0, _("All (*.*)"))  # first item
+        choices.insert(0, _("All"))  # first item
         filterChoice = wx.Choice(frame, -1, size=(250, -1), choices=choices)
         filterChoice.SetSelection(0)
         filterChoice.SetToolTipString(_("Select file type filter."))
@@ -2301,63 +2454,54 @@ class ProjectView(wx.lib.docview.View):
         if status == wx.ID_OK:
             wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
 
-            doc = self.GetDocument()
-            searchSubfolders = subfolderCtrl.IsChecked()
-            dirString = dirCtrl.GetValue()
-
-            if os.path.isfile(dirString):
-                # If they pick a file explicitly, we won't prevent them from adding it even if it doesn't match the filter.
-                # We'll assume they know what they're doing.
-                paths = [dirString]
-            else:
-                paths = []
-
-                index = filterChoice.GetSelection()
-                lastIndex = filterChoice.GetCount()-1
-                if index and index != lastIndex:  # if not All or Any
-                    template = visibleTemplates[index-1]
-
-                # do search in files on disk
-                for root, dirs, files in os.walk(dirString):
-                    if not searchSubfolders and root != dirString:
-                        break
-
-                    for name in files:
-                        if index == 0:  # All
-                            for template in visibleTemplates:
-                                if template.FileMatchesTemplate(name):
-                                    filename = os.path.join(root, name)
-
-                                    # if already in project, don't add it, otherwise undo will remove it from project even though it was already in it.
-                                    if doc.IsFileInProject(filename):
-                                        break
-
-                                    paths.append(filename)
-                                    break
-                        elif index == lastIndex:  # Any
-                            filename = os.path.join(root, name)
-                            # if already in project, don't add it, otherwise undo will remove it from project even though it was already in it.
-                            if not doc.IsFileInProject(filename):
-                                paths.append(filename)
-                        else:  # use selected filter
-                            if template.FileMatchesTemplate(name):
+            try:
+                doc = self.GetDocument()
+                searchSubfolders = subfolderCtrl.IsChecked()
+                dirString = dirCtrl.GetValue()
+    
+                if os.path.isfile(dirString):
+                    # If they pick a file explicitly, we won't prevent them from adding it even if it doesn't match the filter.
+                    # We'll assume they know what they're doing.
+                    paths = [dirString]
+                else:
+                    paths = []
+    
+                    index = filterChoice.GetSelection()
+                    lastIndex = filterChoice.GetCount()-1
+                    if index and index != lastIndex:  # if not All or Any
+                        template = visibleTemplates[index-1]
+    
+                    # do search in files on disk
+                    for root, dirs, files in os.walk(dirString):
+                        if not searchSubfolders and root != dirString:
+                            break
+    
+                        for name in files:
+                            if index == 0:  # All
                                 filename = os.path.join(root, name)
                                 # if already in project, don't add it, otherwise undo will remove it from project even though it was already in it.
                                 if not doc.IsFileInProject(filename):
                                     paths.append(filename)
+                            else:  # use selected filter
+                                if template.FileMatchesTemplate(name):
+                                    filename = os.path.join(root, name)
+                                    # if already in project, don't add it, otherwise undo will remove it from project even though it was already in it.
+                                    if not doc.IsFileInProject(filename):
+                                        paths.append(filename)
+    
+                folderPath = None
+                if self.GetMode() == ProjectView.LOGICAL_MODE:
+                    selections = self._treeCtrl.GetSelections()
+                    if selections:
+                        item = selections[0]
+                        if not self._IsItemFile(item):
+                            folderPath = self._GetItemFolderPath(item)
 
-            folderPath = None
-            if self.GetMode() == ProjectView.LOGICAL_MODE:
-                selections = self._treeCtrl.GetSelections()
-                if selections:
-                    item = selections[0]
-                    if not self._IsItemFile(item):
-                        folderPath = self._GetItemFolderPath(item)
-
-            wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
-
-            doc.GetCommandProcessor().Submit(ProjectAddFilesCommand(doc, paths, folderPath=folderPath))
-            self.Activate()  # after add, should put focus on project editor
+                doc.GetCommandProcessor().Submit(ProjectAddFilesCommand(doc, paths, folderPath=folderPath))
+                self.Activate()  # after add, should put focus on project editor
+                
+            finally:
+                wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
 
 
     def DoAddFilesToProject(self, filePaths, folderPath):
@@ -2385,23 +2529,19 @@ class ProjectView(wx.lib.docview.View):
 
 
     def OnLeftClick(self, event):
-        """ wxBug: If tree has selection, but focus is in another window, single click in tree should do
-            single selection of item at mouse position.  But what it does is just put focus back into the
-            window and all items go from inactive selection to active selection.  Another click on the item
-            either activates double-click or edit for that item.  This behavior is odd.
-            
-            This fix makes gives the tree view the focus and makes the item under the mouse position the
-            only active selection, as expected.
-        """
-        if not self.ProjectHasFocus() and not self.FilesHasFocus() and not event.ShiftDown() and not event.ControlDown() and not event.MetaDown():
-            self._treeCtrl.UnselectAll()
-            event.Skip()
+        """ 
+            wxBug: We also spurious drag events on a single click of on item that is already selected,
+            so the solution was to consume the left click event.  But his broke the single click expand/collapse
+            of a folder, so if it is a folder, we do an event.Skip() to allow the expand/collapse,
+            otherwise we consume the event.
+        """            
+        # if folder let it collapse/expand
+        if wx.Platform == '__WXMSW__':
             item, flags = self._treeCtrl.HitTest(event.GetPosition())
-            self._treeCtrl.SelectItem(item)
-            return
-            
-        event.Skip()
-
+            if item.IsOk() and self._treeCtrl.GetChildrenCount(item, False):
+                event.Skip()
+        else:
+            event.Skip()
 
     def OnRightClick(self, event):
         self.Activate()
@@ -2444,17 +2584,39 @@ class ProjectView(wx.lib.docview.View):
             if not itemID:
                 menu.AppendSeparator()
             else:
-                if itemID == ProjectService.RUN_SELECTED_PM_ID:
-                    menu.Append(ProjectService.RUN_SELECTED_PM_ID, _("Run Process"))
-                    wx.EVT_MENU(self._GetParentFrame(), ProjectService.RUN_SELECTED_PM_ID, self.OnRunSelectedPM)
+                if itemID == ProjectService.RUN_SELECTED_PM_ID and not ACTIVEGRID_BASE_IDE:
+                    webBrowserService = wx.GetApp().GetService(WebBrowserService.WebBrowserService)
+                    if webBrowserService:
+                        if wx.Platform == '__WXMSW__':
+                            menu.Append(ProjectService.RUN_SELECTED_PM_ID, _("Run Process"))
+                            wx.EVT_MENU(self._GetParentFrame(), ProjectService.RUN_SELECTED_PM_ID, self.ProjectServiceProcessEvent)
+
+                        if wx.Platform == '__WXMSW__':
+                            menuLabel = _("Run Process in External Browser")
+                        else:
+                            menuLabel = _("Run Process")
+                        menu.Append(ProjectService.RUN_SELECTED_PM_EXTERNAL_BROWSER_ID, menuLabel)
+                        wx.EVT_MENU(self._GetParentFrame(), ProjectService.RUN_SELECTED_PM_EXTERNAL_BROWSER_ID, self.ProjectServiceProcessEvent)
+                        
+                        if wx.Platform == '__WXMSW__':
+    
+                            if wx.GetApp().GetUseTabbedMDI():
+                                menuLabel = _("Run Process in new Tab")
+                            else:
+                                menuLabel = _("Run Process in new Window")
+                            menu.Append(ProjectService.RUN_SELECTED_PM_INTERNAL_WINDOW_ID, menuLabel)
+                            wx.EVT_MENU(self._GetParentFrame(), ProjectService.RUN_SELECTED_PM_INTERNAL_WINDOW_ID, self.ProjectServiceProcessEvent)
+                        
                 elif itemID == ProjectService.REMOVE_FROM_PROJECT:
                     menu.Append(ProjectService.REMOVE_FROM_PROJECT, _("Remove Selected Files from Project"))
                     wx.EVT_MENU(self._GetParentFrame(), ProjectService.REMOVE_FROM_PROJECT, self.OnClear)
                     wx.EVT_UPDATE_UI(self._GetParentFrame(), ProjectService.REMOVE_FROM_PROJECT, self._GetParentFrame().ProcessUpdateUIEvent)
                 else:
-                    svnService = wx.GetApp().GetService(SVNService.SVNService)
                     item = menuBar.FindItemById(itemID)
                     if item:
+                        if SVN_INSTALLED:
+                            svnService = wx.GetApp().GetService(SVNService.SVNService)
+                            
                         if itemID in svnIDs:
                             if SVN_INSTALLED and svnService:
                                 wx.EVT_MENU(self._GetParentFrame(), itemID, svnService.ProcessEvent)
@@ -2467,21 +2629,35 @@ class ProjectView(wx.lib.docview.View):
         menu.Destroy()
 
 
-    def OnRunSelectedPM(self, event):
+    def ProjectServiceProcessEvent(self, event):
         projectService = wx.GetApp().GetService(ProjectService)
         if projectService:
-            projectService.OnRunProcessModel(event, runSelected=True)
+            projectService.ProcessEvent(event)
 
 
     def OnRename(self, event=None):
         items = self._treeCtrl.GetSelections()
-        if items:
-            self._treeCtrl.EditLabel(items[0])
+        if not items:
+            return
+        item = items[0]
+        if wx.Platform == "__WXGTK__":
+            dlg = wx.TextEntryDialog(self.GetFrame(), _("Enter New Name"), _("Enter New Name"))
+            dlg.CenterOnParent()
+            if dlg.ShowModal() == wx.ID_OK:
+                text = dlg.GetValue()
+                self.ChangeLabel(item, text)
+        else:
+            if items:
+                self._treeCtrl.EditLabel(item)
 
 
     def OnBeginLabelEdit(self, event):
         self._editingSoDontKillFocus = True
         item = event.GetItem()
+        if self._IsItemFile(item):
+            file = self._GetItemFile(item)
+            if file.type == 'xform':
+                event.Veto()
         if (self.GetMode() == ProjectView.PHYSICAL_MODE) and not self._IsItemFile(item):
             event.Veto()
 
@@ -2490,16 +2666,19 @@ class ProjectView(wx.lib.docview.View):
         self._editingSoDontKillFocus = False
         item = event.GetItem()
         newName = event.GetLabel()
-        if not newName:
+        if not self.ChangeLabel(item, newName):
             event.Veto()
-            return
+            
+
+    def ChangeLabel(self, item, newName):
+        if not newName:
+            return False
         if self._IsItemFile(item):
             oldFilePath = self._GetItemFilePath(item)
             newFilePath = os.path.join(os.path.dirname(oldFilePath), newName)
             doc = self.GetDocument()
             if not doc.GetCommandProcessor().Submit(ProjectRenameFileCommand(doc, oldFilePath, newFilePath)):
-                event.Veto()
-                return
+                return False
             self._treeCtrl.SortChildren(self._treeCtrl.GetItemParent(item))
         else:
             oldFolderPath = self._GetItemFolderPath(item)
@@ -2512,26 +2691,25 @@ class ProjectView(wx.lib.docview.View):
                             "Rename Folder",
                             wx.OK | wx.ICON_EXCLAMATION,
                             self.GetFrame())
-                event.Veto()
-                return
+                return False
             doc = self.GetDocument()
             if not doc.GetCommandProcessor().Submit(ProjectRenameFolderCommand(doc, oldFolderPath, newFolderPath)):
-                event.Veto()
-                return
+                return False
             self._treeCtrl.SortChildren(self._treeCtrl.GetItemParent(item))
 
+        return True
+        
 
     def CanPaste(self):
         # wxBug: Should be able to use IsSupported/IsSupportedFormat here
         #fileDataObject = wx.FileDataObject()
         #hasFilesInClipboard = wx.TheClipboard.IsSupportedFormat(wx.FileDataObject)
+        hasFilesInClipboard = False
         if not wx.TheClipboard.IsOpened():
             if wx.TheClipboard.Open():
                 fileDataObject = wx.FileDataObject()
                 hasFilesInClipboard = wx.TheClipboard.GetData(fileDataObject)
                 wx.TheClipboard.Close()
-        else:
-            hasFilesInClipboard = False
         return hasFilesInClipboard
 
 
@@ -2660,7 +2838,12 @@ class ProjectView(wx.lib.docview.View):
 
         if closeFiles or delFiles:
             filesInProject = doc.GetFiles()
-            filesInProject.append(self.GetDocument().GetDeploymentFilepath())  # remove deployment file also.
+            deploymentFilePath = self.GetDocument().GetDeploymentFilepath()
+            if deploymentFilePath:
+                filesInProject.append(deploymentFilePath)  # remove deployment file also.
+                import activegrid.server.secutils as secutils
+                keystoreFilePath = os.path.join(os.path.dirname(deploymentFilePath), secutils.AGKEYSTORE_FILENAME)
+                filesInProject.append(keystoreFilePath)  # remove keystore file also.
                 
             # don't remove self prematurely
             filePath = doc.GetFilename()
@@ -2705,8 +2888,10 @@ class ProjectView(wx.lib.docview.View):
                                       
         filePath = doc.GetFilename()
         
+        self.ClearFolderState()  # remove from registry folder settings
+
         # close project
-        if doc:
+        if doc:            
             doc.Modify(False)  # make sure it doesn't ask to save the project
             if self.GetDocumentManager().CloseDocument(doc, True):
                 self.RemoveCurrentDocumentUpdate()
@@ -2813,11 +2998,7 @@ class ProjectView(wx.lib.docview.View):
                     if not doc and filepath.endswith(PROJECT_EXTENSION):  # project already open
                         self.SetProject(filepath)
                     elif doc:
-                        projectService = wx.GetApp().GetService(ProjectService)
-                        if projectService:
-                            projectService.AddProjectMapping(doc)
-                            if hasattr(doc, "GetModel"):
-                                projectService.AddProjectMapping(doc.GetModel())
+                        AddProjectMapping(doc)
                         
 
         except IOError, (code, message):
@@ -3056,18 +3237,20 @@ class ProjectPropertiesDialog(wx.Dialog):
         tab.SetSizer(spacerGrid)
         notebook.AddPage(tab, _("Physical View"))
 
+        if wx.Platform == "__WXMSW__":
+            notebook.SetPageSize((310,300))
+
         if not ACTIVEGRID_BASE_IDE:
             tab = wx.Panel(notebook, -1)
             self._appInfoCtrl = PropertyService.PropertyCtrl(tab, header=False)
             self._appInfoCtrl.SetDocument(document)
             self._appInfoCtrl.SetModel(document.GetAppInfo())
             sizer = wx.BoxSizer(wx.HORIZONTAL)
-            sizer.Add(self._appInfoCtrl, 1, wx.EXPAND)
+            sizer.Add(self._appInfoCtrl, 1, wx.EXPAND|wx.ALL, PropertyService.LEAVE_MARGIN)
             tab.SetSizer(sizer)
             notebook.AddPage(tab, _("App Info"))
+            self._appInfoCtrl._grid.AutoSizeColumns()
 
-        if wx.Platform == "__WXMSW__":
-            notebook.SetPageSize((310,300))
 
         sizer = wx.BoxSizer(wx.VERTICAL)
         sizer.Add(notebook, 0, wx.ALL | wx.EXPAND, SPACE)
@@ -3092,13 +3275,13 @@ class ProjectOptionsPanel(wx.Panel):
         projectSizer.Add(self._projSaveDocsCheckBox, 0, wx.ALL, HALF_SPACE)
         if not ACTIVEGRID_BASE_IDE:
             self._projShowWelcomeCheckBox = wx.CheckBox(self, -1, _("Show Welcome Dialog"))
-            self._projShowWelcomeCheckBox.SetValue(config.ReadInt("RunWelcomeDialog", True))
+            self._projShowWelcomeCheckBox.SetValue(config.ReadInt("RunWelcomeDialog2", True))
             projectSizer.Add(self._projShowWelcomeCheckBox, 0, wx.ALL, HALF_SPACE)
             
             sizer = wx.BoxSizer(wx.HORIZONTAL)
             sizer.Add(wx.StaticText(self, -1, _("Default language for projects:")), 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, HALF_SPACE)
-            self._langCtrl = wx.Choice(self, -1, choices=deploymentlib.LANGUAGE_LIST)            
-            self._langCtrl.SetStringSelection(config.Read(APP_LAST_LANGUAGE, deploymentlib.LANGUAGE_DEFAULT))
+            self._langCtrl = wx.Choice(self, -1, choices=projectmodel.LANGUAGE_LIST)            
+            self._langCtrl.SetStringSelection(config.Read(APP_LAST_LANGUAGE, projectmodel.LANGUAGE_DEFAULT))
             self._langCtrl.SetToolTipString(_("Programming language to be used throughout the project."))
             sizer.Add(self._langCtrl, 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, MAC_RIGHT_BORDER)
             projectSizer.Add(sizer, 0, wx.ALL, HALF_SPACE)
@@ -3125,7 +3308,7 @@ class ProjectOptionsPanel(wx.Panel):
         config = wx.ConfigBase_Get()
         config.WriteInt("ProjectSaveDocs", self._projSaveDocsCheckBox.GetValue())
         if not ACTIVEGRID_BASE_IDE:
-            config.WriteInt("RunWelcomeDialog", self._projShowWelcomeCheckBox.GetValue())
+            config.WriteInt("RunWelcomeDialog2", self._projShowWelcomeCheckBox.GetValue())
             config.Write(APP_LAST_LANGUAGE, self._langCtrl.GetStringSelection())
 
 
@@ -3140,7 +3323,11 @@ class ProjectService(Service.Service):
     #----------------------------------------------------------------------------
     SHOW_WINDOW = wx.NewId()  # keep this line for each subclass, need unique ID for each Service
     RUN_SELECTED_PM_ID = wx.NewId()
+    RUN_SELECTED_PM_INTERNAL_WINDOW_ID = wx.NewId()
+    RUN_SELECTED_PM_EXTERNAL_BROWSER_ID = wx.NewId()
     RUN_CURRENT_PM_ID = wx.NewId()
+    RUN_CURRENT_PM_INTERNAL_WINDOW_ID = wx.NewId()
+    RUN_CURRENT_PM_EXTERNAL_BROWSER_ID = wx.NewId()
     RENAME_ID = wx.NewId()
     OPEN_SELECTION_ID = wx.NewId()
     REMOVE_FROM_PROJECT = wx.NewId()
@@ -3163,6 +3350,7 @@ class ProjectService(Service.Service):
         self._runHandlers = []
         self._suppressOpenProjectMessages = False
         self._logicalViewDefaults = []
+        self._logicalViewOpenDefaults = []
         self._fileTypeDefaults = []
         self._nameDefaults = []
         self._mapToProject = dict()
@@ -3230,7 +3418,7 @@ class ProjectService(Service.Service):
                 wx.EVT_MENU(frame, ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID, frame.ProcessEvent)
                 wx.EVT_UPDATE_UI(frame, ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID, frame.ProcessUpdateUIEvent)
             if not menuBar.FindItemById(ProjectService.ADD_FOLDER_ID):
-                projectMenu.Append(ProjectService.ADD_FOLDER_ID, _("Add Folder to Project"), _("Adds a new folder"))
+                projectMenu.Append(ProjectService.ADD_FOLDER_ID, _("New Folder"), _("Creates a new folder"))
                 wx.EVT_MENU(frame, ProjectService.ADD_FOLDER_ID, frame.ProcessEvent)
                 wx.EVT_UPDATE_UI(frame, ProjectService.ADD_FOLDER_ID, frame.ProcessUpdateUIEvent)
             if not menuBar.FindItemById(ProjectService.CLOSE_PROJECT_ID):
@@ -3309,6 +3497,12 @@ class ProjectService(Service.Service):
         self._mapToProject[key] = projectDoc
         
 
+    def RemoveProjectMapping(self, key):
+        """ Remove mapping from model or document to project.  """
+        if self._mapToProject.has_key(key):
+            del self._mapToProject[key]
+        
+
     #----------------------------------------------------------------------------
     # Default Logical View Folder Methods
     #----------------------------------------------------------------------------
@@ -3324,6 +3518,18 @@ class ProjectService(Service.Service):
         return None
 
 
+    def AddLogicalViewFolderCollapsedDefault(self, folderName, collapsed=True):
+        # default is collapsed, don't add to list if collapse is True
+        if not collapsed:
+            self._logicalViewOpenDefaults.append(folderName)
+        
+
+    def FindLogicalViewFolderCollapsedDefault(self, folderName):
+        if folderName in self._logicalViewOpenDefaults:
+            return False
+        return True
+
+
     #----------------------------------------------------------------------------
     # Default File Type Methods
     #----------------------------------------------------------------------------
@@ -3406,9 +3612,21 @@ class ProjectService(Service.Service):
         if id == ProjectService.RUN_SELECTED_PM_ID:
             self.OnRunProcessModel(event, runSelected=True)
             return True
+        elif id == ProjectService.RUN_SELECTED_PM_INTERNAL_WINDOW_ID:
+            self.OnRunProcessModel(event, runSelected=True, newWindow=True, forceInternal=True)
+            return True
+        elif id == ProjectService.RUN_SELECTED_PM_EXTERNAL_BROWSER_ID:
+            self.OnRunProcessModel(event, runSelected=True, newWindow=True, forceExternal=True)
+            return True
         elif id == ProjectService.RUN_CURRENT_PM_ID:
             self.OnRunProcessModel(event, runCurrentFile=True)
             return True
+        elif id == ProjectService.RUN_CURRENT_PM_INTERNAL_WINDOW_ID:
+            self.OnRunProcessModel(event, runCurrentFile=True, newWindow=True, forceInternal=True)
+            return True
+        elif id == ProjectService.RUN_CURRENT_PM_EXTERNAL_BROWSER_ID:
+            self.OnRunProcessModel(event, runCurrentFile=True, newWindow=True, forceExternal=True)
+            return True
         elif id == ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID:
             self.OnAddCurrentFileToProject(event)
             return True
@@ -3430,27 +3648,31 @@ class ProjectService(Service.Service):
             return True
 
         id = event.GetId()
-        if (id == ProjectService.RUN_SELECTED_PM_ID
-        or id == ProjectService.RUN_CURRENT_PM_ID):
+        if id in [ProjectService.RUN_SELECTED_PM_ID,
+        ProjectService.RUN_SELECTED_PM_INTERNAL_WINDOW_ID,
+        ProjectService.RUN_SELECTED_PM_EXTERNAL_BROWSER_ID,
+        ProjectService.RUN_CURRENT_PM_ID,
+        ProjectService.RUN_CURRENT_PM_INTERNAL_WINDOW_ID,
+        ProjectService.RUN_CURRENT_PM_EXTERNAL_BROWSER_ID]:
             event.Enable(True)
             return True
         elif id == ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID:
             event.Enable(self._CanAddCurrentFileToProject())
             return True
-        elif (id == ProjectService.ADD_FILES_TO_PROJECT_ID
-        or id == ProjectService.ADD_DIR_FILES_TO_PROJECT_ID
-        or id == ProjectService.RENAME_ID
-        or id == ProjectService.OPEN_SELECTION_ID
-        or id == ProjectService.DELETE_FILE_ID):
+        elif id in [ProjectService.ADD_FILES_TO_PROJECT_ID,
+        ProjectService.ADD_DIR_FILES_TO_PROJECT_ID,
+        ProjectService.RENAME_ID,
+        ProjectService.OPEN_SELECTION_ID,
+        ProjectService.DELETE_FILE_ID]:
             event.Enable(False)
             return True
         elif id == ProjectService.PROJECT_PROPERTIES_ID:
             event.Enable(self._HasOpenedProjects())
             return True
-        elif (id == wx.lib.pydocview.FilePropertiesService.PROPERTIES_ID
-        or id == ProjectService.ADD_FOLDER_ID
-        or id == ProjectService.DELETE_PROJECT_ID
-        or id == ProjectService.CLOSE_PROJECT_ID):
+        elif id in [wx.lib.pydocview.FilePropertiesService.PROPERTIES_ID,
+        ProjectService.ADD_FOLDER_ID,
+        ProjectService.DELETE_PROJECT_ID,
+        ProjectService.CLOSE_PROJECT_ID]:
             if self.GetView():
                 return self.GetView().ProcessUpdateUIEvent(event)
             else:
@@ -3459,7 +3681,7 @@ class ProjectService(Service.Service):
             return False
 
 
-    def OnRunProcessModel(self, event, runSelected=False, runCurrentFile=False):
+    def OnRunProcessModel(self, event, runSelected=False, runCurrentFile=False, newWindow=False, forceExternal=False, forceInternal=False):
         project = self.GetCurrentProject()
 
         if runCurrentFile:
@@ -3537,14 +3759,19 @@ class ProjectService(Service.Service):
                 fileToRun = files[res]
             else:
                 fileToRun = files[0]
-
-            deployFilePath = project.GenerateDeployment()
-            self.RunProcessModel(fileToRun, project.GetAppInfo().language, deployFilePath)
+                
+            try:
+                deployFilePath = project.GenerateDeployment()
+            except DataServiceExistenceException, e:
+                dataSourceName = str(e)
+                self.PromptForMissingDataSource(dataSourceName)
+                return
+            self.RunProcessModel(fileToRun, project.GetAppInfo().language, deployFilePath, newWindow, forceExternal, forceInternal)
 
 
-    def RunProcessModel(self, fileToRun, language, deployFilePath):
+    def RunProcessModel(self, fileToRun, language, deployFilePath, newWindow=True, forceExternal=False, forceInternal=False):
         for runHandler in self.GetRunHandlers():
-            if runHandler.RunProjectFile(fileToRun, language, deployFilePath):
+            if runHandler.RunProjectFile(fileToRun, language, deployFilePath, newWindow, forceExternal, forceInternal):
                 return
         os.system('"' + fileToRun + '"')
 
@@ -3623,13 +3850,24 @@ class ProjectService(Service.Service):
                     retval.append(document)
                 elif document.IsFileInProject(filename):
                     retval.append(document)
+                    
+        # make sure current project is first in list
+        currProject = self.GetCurrentProject()
+        if currProject and currProject in retval:
+            retval.remove(currProject)
+            retval.insert(0, currProject)
+                
         return retval
 
 
     def OnAddCurrentFileToProject(self, event):
-        file = self.GetDocumentManager().GetCurrentDocument().GetFilename()
-        document = self.GetView().GetDocument()
-        document.GetCommandProcessor().Submit(ProjectAddFilesCommand(document, [file]))
+        doc = self.GetDocumentManager().GetCurrentDocument()
+        file = doc.GetFilename()
+        projectDoc = self.GetView().GetDocument()
+        projectDoc.GetCommandProcessor().Submit(ProjectAddFilesCommand(projectDoc, [file]))
+
+        AddProjectMapping(doc, projectDoc)
+
         self.GetView().Activate()  # after add, should put focus on project editor
 
 
@@ -3649,10 +3887,13 @@ class ProjectService(Service.Service):
             if docString:
                 doc = None
                 docList = eval(docString)
+                self.GetView()._treeCtrl.Freeze()
+
                 for fileName in docList:
                     if isinstance(fileName, types.StringTypes):
                         if os.path.exists(fileName):
                             doc = self.GetDocumentManager().CreateDocument(fileName, wx.lib.docview.DOC_SILENT|wx.lib.docview.DOC_OPEN_ONCE)
+                self.GetView()._treeCtrl.Thaw()
 
                 if doc:
                     openedDocs = True
@@ -3664,6 +3905,38 @@ class ProjectService(Service.Service):
         return openedDocs
 
 
+    def PromptForMissingDataSource(self, dataSourceName):
+        prompt = "A required Data Source '%s' was not found.  The process cannot be run without this Data Source.\n\nWould you like to configure this Data Source now?" % dataSourceName
+        msgTitle = "Unknown Data Source"
+        dataSourceMissingDlg = wx.MessageDialog(self.GetView().GetFrame(), prompt, msgTitle, wx.YES_NO|wx.ICON_QUESTION)
+        dataSourceMissingDlg.CenterOnParent()
+        if dataSourceMissingDlg.ShowModal() == wx.ID_YES:
+            dataSourceMissingDlg.Destroy()
+            self._AddDataSource(dataSourceName)
+        else:
+            dataSourceMissingDlg.Destroy()
+
+
+    def _AddDataSource(self, defaultDataSourceName=None):
+        dataSourceService = wx.GetApp().GetService(DataModelEditor.DataSourceService)
+        dsChoices = dataSourceService.getDataSourceNames()
+        dlg = DataModelEditor.AddDataSourceDialog(self.GetView().GetFrame(), 'Add Data Source', dsChoices, defaultDataSourceName)
+        dlg.CenterOnParent()
+        if dlg.ShowModal() == wx.ID_OK:
+            dataSource = dlg.GetDataSource()
+            dlg.Destroy()
+        else:
+            dlg.Destroy()
+            return False
+        if (dataSource == None):
+            wx.MessageBox(_("Error getting data source."), self._title)
+        dataSourceService.updateDataSource(dataSource)
+        if ((dsChoices == None) or (len(dsChoices) <= 0)):
+            wx.ConfigBase_Get().Write(DataModelEditor.SchemaOptionsPanel.DEFAULT_DATASOURCE_KEY, dataSource.name)
+        dataSourceService.save()
+        return True
+
+
 #----------------------------------------------------------------------------
 # Icon Bitmaps - generated by encode_bitmaps.py
 #----------------------------------------------------------------------------
index ea5f6b71d1ccb834073f229223c47746964aeb65..abde6fd97b8a51c4465e65ef651e745242f4b01a 100644 (file)
@@ -25,6 +25,7 @@ import keyword # for GetAutoCompleteKeywordList
 import sys # for GetAutoCompleteKeywordList
 import MessageService # for OnCheckCode
 import OutlineService
+import FindInDirService
 from UICommon import CaseInsensitiveCompare
 try:
     import checker # for pychecker
@@ -143,11 +144,13 @@ class PythonView(CodeEditor.CodeView):
         # Set cursor to Wait cursor
         wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
 
-        # This takes a while for involved code
-        checker.checkSyntax(self.GetDocument().GetFilename(), view)
+        try:
+            # This takes a while for involved code
+            checker.checkSyntax(self.GetDocument().GetFilename(), view)
 
-        # Set cursor to Default cursor
-        wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
+        finally:
+            # Set cursor to Default cursor
+            wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
 
 
     def OnJumpToFoundLine(self, event):
@@ -369,8 +372,42 @@ class PythonCtrl(CodeEditor.CodeCtrl):
         self.SetKeyWords(0, string.join(keyword.kwlist))
 
 
+    def CreatePopupMenu(self):
+        FINDCLASS_ID = wx.NewId()
+        FINDDEF_ID = wx.NewId()
+
+        menu = CodeEditor.CodeCtrl.CreatePopupMenu(self)
+
+        self.Bind(wx.EVT_MENU, self.OnPopFindDefinition, id=FINDDEF_ID)
+        menu.Insert(1, FINDDEF_ID, _("Find 'def'"))
+
+        self.Bind(wx.EVT_MENU, self.OnPopFindClass, id=FINDCLASS_ID)
+        menu.Insert(2, FINDCLASS_ID, _("Find 'class'"))
+
+        return menu
+
+
+    def OnPopFindDefinition(self, event):
+        view = wx.GetApp().GetDocumentManager().GetCurrentView()
+        if hasattr(view, "GetCtrl") and view.GetCtrl() and hasattr(view.GetCtrl(), "GetSelectedText"):
+            pattern = view.GetCtrl().GetSelectedText().strip()
+            if pattern:
+                searchPattern = "def\s+%s" % pattern
+                wx.GetApp().GetService(FindInDirService.FindInDirService).FindInProject(searchPattern)
+
+
+    def OnPopFindClass(self, event):
+        view = wx.GetApp().GetDocumentManager().GetCurrentView()
+        if hasattr(view, "GetCtrl") and view.GetCtrl() and hasattr(view.GetCtrl(), "GetSelectedText"):
+            definition = "class\s+%s"
+            pattern = view.GetCtrl().GetSelectedText().strip()
+            if pattern:
+                searchPattern = definition % pattern
+                wx.GetApp().GetService(FindInDirService.FindInDirService).FindInProject(searchPattern)
+
+
     def SetViewDefaults(self):
-        CodeEditor.CodeCtrl.SetViewDefaults(self, configPrefix = "Python", hasWordWrap = True, hasTabs = True)
+        CodeEditor.CodeCtrl.SetViewDefaults(self, configPrefix="Python", hasWordWrap=True, hasTabs=True, hasFolding=True)
 
 
     def GetFontAndColorFromConfig(self):
@@ -567,7 +604,7 @@ class PythonOptionsPanel(wx.Panel):
         mainSizer = wx.BoxSizer(wx.VERTICAL)                
         mainSizer.Add(pathSizer, 0, wx.EXPAND|wx.LEFT|wx.RIGHT|wx.TOP, SPACE)
 
-        self._otherOptions = STCTextEditor.TextOptionsPanel(self, -1, configPrefix = "Python", label = "Python", hasWordWrap = True, hasTabs = True, addPage=False)
+        self._otherOptions = STCTextEditor.TextOptionsPanel(self, -1, configPrefix = "Python", label = "Python", hasWordWrap = True, hasTabs = True, addPage=False, hasFolding=True)
         mainSizer.Add(self._otherOptions, 0, wx.EXPAND|wx.BOTTOM, SPACE)
         self.SetSizer(mainSizer)
         parent.AddPage(self, _("Python"))
@@ -577,7 +614,7 @@ class PythonOptionsPanel(wx.Panel):
         defaultDir = os.path.dirname(self._pathTextCtrl.GetValue().strip())
         defaultFile = os.path.basename(self._pathTextCtrl.GetValue().strip())
         if _WINDOWS:
-            wildcard = _("Executable (*.exe)|*.exe|All (*.*)|*.*")
+            wildcard = _("Executable (*.exe)|*.exe|All|*.*")
             if not defaultFile:
                 defaultFile = "python.exe"
         else:
index fd9b84a91a6fa94c91aa4b05735d14fad174a57a..f3ab9508e2b75441874e99db2bb061aa87326682 100644 (file)
@@ -6,7 +6,7 @@
 #
 # Created:      8/10/03
 # CVS-ID:       $Id$
-# Copyright:    (c) 2003-2005 ActiveGrid, Inc.
+# Copyright:    (c) 2003-2006 ActiveGrid, Inc.
 # License:      wxWindows License
 #----------------------------------------------------------------------------
 
@@ -47,9 +47,15 @@ TEXT_STATUS_BAR_ID = wx.NewId()
 class TextDocument(wx.lib.docview.Document):
 
 
+    def __init__(self):
+        wx.lib.docview.Document .__init__(self)
+        self._inModify = False
+
+
     def SaveObject(self, fileObject):
         view = self.GetFirstView()
         fileObject.write(view.GetValue())
+        view.SetModifyFalse()
         return True
         
 
@@ -57,24 +63,31 @@ class TextDocument(wx.lib.docview.Document):
         view = self.GetFirstView()
         data = fileObject.read()
         view.SetValue(data)
+        view.SetModifyFalse()
         return True
 
 
     def IsModified(self):
         view = self.GetFirstView()
         if view:
-            return wx.lib.docview.Document.IsModified(self) or view.IsModified()
-        else:
-            return wx.lib.docview.Document.IsModified(self)
+            return view.IsModified()
+        return False
 
 
-    def Modify(self, mod):
+    def Modify(self, modify):
+        if self._inModify:
+            return
+        self._inModify = True
+        
         view = self.GetFirstView()
-        wx.lib.docview.Document.Modify(self, mod)
-        if not mod and view:
+        if not modify and view:
             view.SetModifyFalse()
 
+        wx.lib.docview.Document.Modify(self, modify)  # this must called be after the SetModifyFalse call above.
 
+        self._inModify = False
+        
+    
     def OnCreateCommandProcessor(self):
         # Don't create a command processor, it has its own
         pass
@@ -142,6 +155,8 @@ class TextView(wx.lib.docview.View):
             self._dynSash._view = self
             self._textEditor = self.GetCtrlClass()(self._dynSash, -1, style=wx.NO_BORDER)
         wx.EVT_LEFT_DOWN(self._textEditor, self.OnLeftClick)
+        self._textEditor.Bind(wx.stc.EVT_STC_MODIFIED, self.OnModify)
+        
         self._CreateSizer(frame)
         self.Activate()
         frame.Show(True)
@@ -149,6 +164,10 @@ class TextView(wx.lib.docview.View):
         return True
 
 
+    def OnModify(self, event):
+        self.GetDocument().Modify(self._textEditor.GetModify())
+        
+
     def _CreateSizer(self, frame):
         sizer = wx.BoxSizer(wx.HORIZONTAL)
         sizer.Add(self._dynSash, 1, wx.EXPAND)
@@ -161,6 +180,9 @@ class TextView(wx.lib.docview.View):
 
 
     def OnUpdate(self, sender = None, hint = None):
+        if wx.lib.docview.View.OnUpdate(self, sender, hint):
+            return
+
         if hint == "ViewStuff":
             self.GetCtrl().SetViewDefaults()
         elif hint == "Font":
@@ -571,9 +593,11 @@ class TextView(wx.lib.docview.View):
     def EnsureVisible(self, line):
         self.GetCtrl().EnsureVisible(line-1)  # line numbering for editor is 0 based, we are 1 based.
 
+
     def EnsureVisibleEnforcePolicy(self, line):
         self.GetCtrl().EnsureVisibleEnforcePolicy(line-1)  # line numbering for editor is 0 based, we are 1 based.
 
+
     def LineFromPosition(self, pos):
         return self.GetCtrl().LineFromPosition(pos)+1  # line numbering for editor is 0 based, we are 1 based.
 
@@ -813,11 +837,12 @@ class TextStatusBar(wx.StatusBar):
 class TextOptionsPanel(wx.Panel):
 
 
-    def __init__(self, parent, id, configPrefix = "Text", label = "Text", hasWordWrap = True, hasTabs = False, addPage=True):
+    def __init__(self, parent, id, configPrefix = "Text", label = "Text", hasWordWrap = True, hasTabs = False, addPage=True, hasFolding=False):
         wx.Panel.__init__(self, parent, id)
         self._configPrefix = configPrefix
         self._hasWordWrap = hasWordWrap
         self._hasTabs = hasTabs
+        self._hasFolding = hasFolding
         SPACE = 10
         HALF_SPACE   = 5
         config = wx.ConfigBase_Get()
@@ -854,6 +879,9 @@ class TextOptionsPanel(wx.Panel):
         self._viewRightEdgeCheckBox.SetValue(config.ReadInt(self._configPrefix + "EditorViewRightEdge", False))
         self._viewLineNumbersCheckBox = wx.CheckBox(self, -1, _("Show line numbers"))
         self._viewLineNumbersCheckBox.SetValue(config.ReadInt(self._configPrefix + "EditorViewLineNumbers", True))
+        if self._hasFolding:
+            self._viewFoldingCheckBox = wx.CheckBox(self, -1, _("Show folding"))
+            self._viewFoldingCheckBox.SetValue(config.ReadInt(self._configPrefix + "EditorViewFolding", True))
         if self._hasTabs:
             self._hasTabsCheckBox = wx.CheckBox(self, -1, _("Use spaces instead of tabs"))
             self._hasTabsCheckBox.SetValue(not wx.ConfigBase_Get().ReadInt(self._configPrefix + "EditorUseTabs", False))
@@ -874,6 +902,8 @@ class TextOptionsPanel(wx.Panel):
         textPanelSizer.Add(self._viewIndentationGuideCheckBox, 0, wx.ALL, HALF_SPACE)
         textPanelSizer.Add(self._viewRightEdgeCheckBox, 0, wx.ALL, HALF_SPACE)
         textPanelSizer.Add(self._viewLineNumbersCheckBox, 0, wx.ALL, HALF_SPACE)
+        if self._hasFolding:
+            textPanelSizer.Add(self._viewFoldingCheckBox, 0, wx.ALL, HALF_SPACE)
         if self._hasTabs:
             textPanelSizer.Add(self._hasTabsCheckBox, 0, wx.ALL, HALF_SPACE)
             textIndentWidthSizer = wx.BoxSizer(wx.HORIZONTAL)
@@ -947,6 +977,9 @@ class TextOptionsPanel(wx.Panel):
         config.WriteInt(self._configPrefix + "EditorViewRightEdge", self._viewRightEdgeCheckBox.GetValue())
         doViewStuffUpdate = doViewStuffUpdate or config.ReadInt(self._configPrefix + "EditorViewLineNumbers", True) != self._viewLineNumbersCheckBox.GetValue()
         config.WriteInt(self._configPrefix + "EditorViewLineNumbers", self._viewLineNumbersCheckBox.GetValue())
+        if self._hasFolding:
+            doViewStuffUpdate = doViewStuffUpdate or config.ReadInt(self._configPrefix + "EditorViewFolding", True) != self._viewFoldingCheckBox.GetValue()
+            config.WriteInt(self._configPrefix + "EditorViewFolding", self._viewFoldingCheckBox.GetValue())
         if self._hasWordWrap:
             doViewStuffUpdate = doViewStuffUpdate or config.ReadInt(self._configPrefix + "EditorWordWrap", False) != self._wordWrapCheckBox.GetValue()
             config.WriteInt(self._configPrefix + "EditorWordWrap", self._wordWrapCheckBox.GetValue())
@@ -1062,13 +1095,15 @@ class TextCtrl(wx.stc.StyledTextCtrl):
         event.Skip()
         
 
-    def SetViewDefaults(self, configPrefix = "Text", hasWordWrap = True, hasTabs = False):
+    def SetViewDefaults(self, configPrefix="Text", hasWordWrap=True, hasTabs=False, hasFolding=False):
         config = wx.ConfigBase_Get()
         self.SetViewWhiteSpace(config.ReadInt(configPrefix + "EditorViewWhitespace", False))
         self.SetViewEOL(config.ReadInt(configPrefix + "EditorViewEOL", False))
         self.SetIndentationGuides(config.ReadInt(configPrefix + "EditorViewIndentationGuides", False))
         self.SetViewRightEdge(config.ReadInt(configPrefix + "EditorViewRightEdge", False))
         self.SetViewLineNumbers(config.ReadInt(configPrefix + "EditorViewLineNumbers", True))
+        if hasFolding:
+            self.SetViewFolding(config.ReadInt(configPrefix + "EditorViewFolding", True))
         if hasWordWrap:
             self.SetWordWrap(config.ReadInt(configPrefix + "EditorWordWrap", False))
         if hasTabs:  # These methods do not exist in STCTextEditor and are meant for subclasses
@@ -1237,6 +1272,17 @@ class TextCtrl(wx.stc.StyledTextCtrl):
             self.SetMarginWidth(1, 0)
 
 
+    def GetViewFolding(self):
+        return self.GetMarginWidth(2) > 0
+
+
+    def SetViewFolding(self, viewFolding = True):
+        if viewFolding:
+            self.SetMarginWidth(2, 12)
+        else:
+            self.SetMarginWidth(2, 0)
+
+
     def CanWordWrap(self):
         return True
 
index 785acf3866b442e4920aa75c0fc79a23f89aae13..c5e2af652f52e01bb3716f5703974ed25a149455 100644 (file)
@@ -324,526 +324,529 @@ class SVNService(wx.lib.pydocview.DocService):
             return False
 
 
-        if id == SVNService.SVN_UPDATE_ID:
-            wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
-            
-            filenames = self.GetCurrentDocuments()
-            if filenames:
-                filenames = filenames[:]
-                filenames.sort(self.BasenameCaseInsensitiveCompare)
-            else:
-                folderPath = self.GetCurrentFolder()
-                if folderPath:
-                    filenames = [folderPath]
-
-            messageService = wx.GetApp().GetService(MessageService.MessageService)
-            messageService.ShowWindow()
-
-            view = messageService.GetView()
-            view.ClearLines()
-            view.AddLines(_("SVN Update:\n"))
-
-            for filename in filenames:
-                view.AddLines("%s\n" % filename)
-                try:
-                    status = self._client.update(filename)
-    
-                    if status.number > 0:
-                        view.AddLines(_("Updated to revision %s\n") % status.number)
-
-                        openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
-                        for doc in openDocs:
-                            if doc.GetFilename() == filename:
-                                yesNoMsg = wx.MessageDialog(wx.GetApp().GetTopWindow(),
-                                                         _("Updated file '%s' is currently open.  Close it?") % os.path.basename(filename),
-                                                         _("Close File"),
-                                                         wx.YES_NO|wx.ICON_QUESTION)
-                                yesNoMsg.CenterOnParent()
-                                status = yesNoMsg.ShowModal()
-                                yesNoMsg.Destroy()
-                                if status == wx.ID_YES:
-                                    doc.DeleteAllViews()
-                                break
-                    else:
-                        view.AddLines(_("Update failed.\n"))
+        try:
+            if id == SVNService.SVN_UPDATE_ID:
+                wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
+                
+                filenames = self.GetCurrentDocuments()
+                if filenames:
+                    filenames = filenames[:]
+                    filenames.sort(self.BasenameCaseInsensitiveCompare)
+                else:
+                    folderPath = self.GetCurrentFolder()
+                    if folderPath:
+                        filenames = [folderPath]
     
-                except pysvn.ClientError, e:
-                    view.AddLines("%s\n" % str(e))
-                    wx.MessageBox(str(e), _("SVN Update"), wx.OK | wx.ICON_EXCLAMATION)
-                except:
-                    extype, ex, tb = sys.exc_info()
-                    view.AddLines("Update failed: (%s) %s\n" % (extype, str(ex)))
-                    for line in traceback.format_tb(tb):
-                        view.AddLines(line)
+                messageService = wx.GetApp().GetService(MessageService.MessageService)
+                messageService.ShowWindow()
     
-                    wx.MessageBox(_("Update failed."), _("SVN Update"), wx.OK | wx.ICON_EXCLAMATION)
-
-            wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
-
-            return True
-            
-        elif id == SVNService.SVN_UPDATE_ALL_ID:
-            wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
-
-            messageService = wx.GetApp().GetService(MessageService.MessageService)
-            messageService.ShowWindow()
-
-            view = messageService.GetView()
-            view.ClearLines()
-            view.AddLines(_("SVN Update:\n"))
-            
-            project = self.GetCurrentProject()
-            if project:
-                openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
-                for doc in openDocs:
-                    if doc.GetFilename() == project:
-                        filenames = doc.GetFiles()[:]  # make a copy and sort it.
-                        filenames.sort(self.BasenameCaseInsensitiveCompare)
-
-                        for filename in filenames:
-                            view.AddLines("%s\n" % filename)
-                            try:
-                                status = self._client.update(filename)
+                view = messageService.GetView()
+                view.ClearLines()
+                view.AddLines(_("SVN Update:\n"))
+    
+                for filename in filenames:
+                    view.AddLines("%s\n" % filename)
+                    try:
+                        status = self._client.update(filename)
+        
+                        if status.number > 0:
+                            view.AddLines(_("Updated to revision %s\n") % status.number)
+    
+                            openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
+                            for doc in openDocs:
+                                if doc.GetFilename() == filename:
+                                    yesNoMsg = wx.MessageDialog(wx.GetApp().GetTopWindow(),
+                                                             _("Updated file '%s' is currently open.  Close it?") % os.path.basename(filename),
+                                                             _("Close File"),
+                                                             wx.YES_NO|wx.ICON_QUESTION)
+                                    yesNoMsg.CenterOnParent()
+                                    status = yesNoMsg.ShowModal()
+                                    yesNoMsg.Destroy()
+                                    if status == wx.ID_YES:
+                                        doc.DeleteAllViews()
+                                    break
+                        else:
+                            view.AddLines(_("Update failed.\n"))
+        
+                    except pysvn.ClientError, e:
+                        view.AddLines("%s\n" % str(e))
+                        wx.MessageBox(str(e), _("SVN Update"), wx.OK | wx.ICON_EXCLAMATION)
+                    except:
+                        extype, ex, tb = sys.exc_info()
+                        view.AddLines("Update failed: (%s) %s\n" % (extype, str(ex)))
+                        for line in traceback.format_tb(tb):
+                            view.AddLines(line)
+        
+                        wx.MessageBox(_("Update failed."), _("SVN Update"), wx.OK | wx.ICON_EXCLAMATION)
+    
+                wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
+    
+                return True
                 
-                                if status.number > 0:
-                                    view.AddLines(_("Updated to revision %s\n") % status.number)
-            
-                                    openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
-                                    for doc in openDocs:
-                                        if doc.GetFilename() == filename:
-                                            yesNoMsg = wx.MessageDialog(wx.GetApp().GetTopWindow(),
-                                                                     _("Updated file '%s' is currently open.  Close it?") % os.path.basename(filename),
-                                                                     _("Close File"),
-                                                                     wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION)
-                                            yesNoMsg.CenterOnParent()
-                                            status = yesNoMsg.ShowModal()
-                                            yesNoMsg.Destroy()
-                                            if status == wx.ID_YES:
-                                                doc.DeleteAllViews()
-                                            elif status == wx.ID_NO:
-                                                pass
-                                            else: # elif status == wx.CANCEL:
-                                                wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
-                                                return True
-                                            break
-                                else:
-                                    view.AddLines(_("Update failed.\n"))
+            elif id == SVNService.SVN_UPDATE_ALL_ID:
+                wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
+    
+                messageService = wx.GetApp().GetService(MessageService.MessageService)
+                messageService.ShowWindow()
+    
+                view = messageService.GetView()
+                view.ClearLines()
+                view.AddLines(_("SVN Update:\n"))
                 
-                            except pysvn.ClientError, e:
-                                view.AddLines("%s\n" % str(e))
-                                wx.MessageBox(str(e), _("SVN Update"), wx.OK | wx.ICON_EXCLAMATION)
-                            except:
-                                extype, ex, tb = sys.exc_info()
-                                view.AddLines("Update failed: (%s) %s\n" % (extype, str(ex)))
-                                for line in traceback.format_tb(tb):
-                                    view.AddLines(line)
+                project = self.GetCurrentProject()
+                if project:
+                    openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
+                    for doc in openDocs:
+                        if doc.GetFilename() == project:
+                            filenames = doc.GetFiles()[:]  # make a copy and sort it.
+                            filenames.sort(self.BasenameCaseInsensitiveCompare)
+    
+                            for filename in filenames:
+                                view.AddLines("%s\n" % filename)
+                                try:
+                                    status = self._client.update(filename)
+                    
+                                    if status.number > 0:
+                                        view.AddLines(_("Updated to revision %s\n") % status.number)
                 
-                                wx.MessageBox(_("Update failed."), _("SVN Update"), wx.OK | wx.ICON_EXCLAMATION)
-
-            wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
-            return True
-
-        elif id == SVNService.SVN_CHECKIN_ALL_ID:
-            filenames = []
-            project = self.GetCurrentProject()
-            if project:
+                                        openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
+                                        for doc in openDocs:
+                                            if doc.GetFilename() == filename:
+                                                yesNoMsg = wx.MessageDialog(wx.GetApp().GetTopWindow(),
+                                                                         _("Updated file '%s' is currently open.  Close it?") % os.path.basename(filename),
+                                                                         _("Close File"),
+                                                                         wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION)
+                                                yesNoMsg.CenterOnParent()
+                                                status = yesNoMsg.ShowModal()
+                                                yesNoMsg.Destroy()
+                                                if status == wx.ID_YES:
+                                                    doc.DeleteAllViews()
+                                                elif status == wx.ID_NO:
+                                                    pass
+                                                else: # elif status == wx.CANCEL:
+                                                    wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
+                                                    return True
+                                                break
+                                    else:
+                                        view.AddLines(_("Update failed.\n"))
+                    
+                                except pysvn.ClientError, e:
+                                    view.AddLines("%s\n" % str(e))
+                                    wx.MessageBox(str(e), _("SVN Update"), wx.OK | wx.ICON_EXCLAMATION)
+                                except:
+                                    extype, ex, tb = sys.exc_info()
+                                    view.AddLines("Update failed: (%s) %s\n" % (extype, str(ex)))
+                                    for line in traceback.format_tb(tb):
+                                        view.AddLines(line)
+                    
+                                    wx.MessageBox(_("Update failed."), _("SVN Update"), wx.OK | wx.ICON_EXCLAMATION)
+    
+                wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
+                return True
+    
+            elif id == SVNService.SVN_CHECKIN_ALL_ID:
+                filenames = []
+                project = self.GetCurrentProject()
+                if project:
+                    openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
+                    for doc in openDocs:
+                        if doc.GetFilename() == project:
+                            for filename in doc.GetFiles():
+                                if filename not in filenames:
+                                    filenames.append(filename)
+                filenames.sort(self.BasenameCaseInsensitiveCompare)
+    
+                # ask user if dirty files should be saved first
                 openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
-                for doc in openDocs:
-                    if doc.GetFilename() == project:
-                        for filename in doc.GetFiles():
-                            if filename not in filenames:
-                                filenames.append(filename)
-            filenames.sort(self.BasenameCaseInsensitiveCompare)
-
-            # ask user if dirty files should be saved first
-            openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
-            for filename in filenames:
-                for doc in openDocs:
-                    if doc.GetFilename() == filename and doc.IsModified():
-                        yesNoMsg = wx.MessageDialog(wx.GetApp().GetTopWindow(),
-                                                 _("'%s' has unsaved modifications.  Save it before commit?") % os.path.basename(filename),
-                                                 _("SVN Commit"),
-                                                 wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION)
-                        yesNoMsg.CenterOnParent()
-                        status = yesNoMsg.ShowModal()
-                        yesNoMsg.Destroy()
-                        if status == wx.ID_YES:
-                            doc.Save()
-                        elif status == wx.ID_NO:
-                            pass
-                        else: # elif status == wx.CANCEL:
-                            return True
-                        break
-
-            shortFilenames = []
-            for i, filename in enumerate(filenames):
-                shortFilename = os.path.basename(filename)
-                shortFilenames.append(shortFilename)
-
-            dlg = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("SVN Commit"))
-
-            sizer = wx.BoxSizer(wx.VERTICAL)
-            sizer.Add(wx.StaticText(dlg, -1, _("Comment:")), 0, wx.ALIGN_CENTER_VERTICAL)
-            commentText = wx.TextCtrl(dlg, -1, size=(250,-1), style=wx.TE_MULTILINE)
-            sizer.Add(commentText, 1, wx.EXPAND|wx.TOP, HALF_SPACE)
-
-            sizer.Add(wx.StaticText(dlg, -1, _("Files:")), 0, wx.ALIGN_CENTER_VERTICAL|wx.TOP, SPACE)
-            fileList = wx.CheckListBox(dlg, -1, choices = shortFilenames)
-            for i in range(fileList.GetCount()):
-                fileList.Check(i, True)
-            sizer.Add(fileList, 0, wx.EXPAND|wx.TOP, HALF_SPACE)
-
-            buttonSizer = wx.StdDialogButtonSizer()
-            okBtn = wx.Button(dlg, wx.ID_OK)
-            okBtn.SetDefault()
-            buttonSizer.AddButton(okBtn)
-            buttonSizer.AddButton(wx.Button(dlg, wx.ID_CANCEL))
-            buttonSizer.Realize()
-
-            contentSizer = wx.BoxSizer(wx.VERTICAL)
-            contentSizer.Add(sizer, 0, wx.ALL, SPACE)
-            contentSizer.Add(buttonSizer, 0, wx.ALL|wx.ALIGN_RIGHT, SPACE)
-
-            dlg.SetSizer(contentSizer)
-            dlg.Fit()
-            dlg.Layout()
-
-            dlg.CenterOnParent()
-            if dlg.ShowModal() == wx.ID_OK:
+                for filename in filenames:
+                    for doc in openDocs:
+                        if doc.GetFilename() == filename and doc.IsModified():
+                            yesNoMsg = wx.MessageDialog(wx.GetApp().GetTopWindow(),
+                                                     _("'%s' has unsaved modifications.  Save it before commit?") % os.path.basename(filename),
+                                                     _("SVN Commit"),
+                                                     wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION)
+                            yesNoMsg.CenterOnParent()
+                            status = yesNoMsg.ShowModal()
+                            yesNoMsg.Destroy()
+                            if status == wx.ID_YES:
+                                doc.Save()
+                            elif status == wx.ID_NO:
+                                pass
+                            else: # elif status == wx.CANCEL:
+                                return True
+                            break
+    
+                shortFilenames = []
+                for i, filename in enumerate(filenames):
+                    shortFilename = os.path.basename(filename)
+                    shortFilenames.append(shortFilename)
+    
+                dlg = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("SVN Commit"))
+    
+                sizer = wx.BoxSizer(wx.VERTICAL)
+                sizer.Add(wx.StaticText(dlg, -1, _("Comment:")), 0, wx.ALIGN_CENTER_VERTICAL)
+                commentText = wx.TextCtrl(dlg, -1, size=(250,-1), style=wx.TE_MULTILINE)
+                sizer.Add(commentText, 1, wx.EXPAND|wx.TOP, HALF_SPACE)
+    
+                sizer.Add(wx.StaticText(dlg, -1, _("Files:")), 0, wx.ALIGN_CENTER_VERTICAL|wx.TOP, SPACE)
+                fileList = wx.CheckListBox(dlg, -1, choices = shortFilenames)
+                for i in range(fileList.GetCount()):
+                    fileList.Check(i, True)
+                sizer.Add(fileList, 0, wx.EXPAND|wx.TOP, HALF_SPACE)
+    
+                buttonSizer = wx.StdDialogButtonSizer()
+                okBtn = wx.Button(dlg, wx.ID_OK)
+                okBtn.SetDefault()
+                buttonSizer.AddButton(okBtn)
+                buttonSizer.AddButton(wx.Button(dlg, wx.ID_CANCEL))
+                buttonSizer.Realize()
+    
+                contentSizer = wx.BoxSizer(wx.VERTICAL)
+                contentSizer.Add(sizer, 0, wx.ALL, SPACE)
+                contentSizer.Add(buttonSizer, 0, wx.ALL|wx.ALIGN_RIGHT, SPACE)
+    
+                dlg.SetSizer(contentSizer)
+                dlg.Fit()
+                dlg.Layout()
+    
+                dlg.CenterOnParent()
+                if dlg.ShowModal() == wx.ID_OK:
+                    wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
+    
+                    messageService = wx.GetApp().GetService(MessageService.MessageService)
+                    messageService.ShowWindow()
+    
+                    view = messageService.GetView()
+                    view.ClearLines()
+                    view.AddLines(_("SVN Commit:\n"))
+    
+                    try:
+                        selFilenames = []
+                        for i in range(fileList.GetCount()):
+                            if fileList.IsChecked(i):
+                                selFilenames.append(filenames[i])
+                                view.AddLines("%s\n" % filenames[i])
+                            
+                        if len(selFilenames):
+                            comment = commentText.GetValue()
+                            status = self._client.checkin(selFilenames, comment)
+    
+                            if status is None:
+                                view.AddLines(_("Nothing to commit.\n"))
+                            elif status.number > 0:
+                                view.AddLines(_("Committed as revision %s.\n") % status.number)
+                            else:
+                                view.AddLines(_("Commit failed.\n"))
+    
+                    except pysvn.ClientError, e:
+                        view.AddLines("%s\n" % str(e))
+                        wx.MessageBox(str(e), _("SVN Commit"), wx.OK | wx.ICON_EXCLAMATION)
+                    except:
+                        extype, ex, tb = sys.exc_info()
+                        view.AddLines("Commit failed: (%s) %s\n" % (extype, str(ex)))
+                        for line in traceback.format_tb(tb):
+                            view.AddLines(line)
+                        wx.MessageBox(_("Commit failed."), _("SVN Commit"), wx.OK | wx.ICON_EXCLAMATION)
+    
+                    wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
+                dlg.Destroy()
+                return True
+    
+    
+            elif id == SVNService.SVN_CHECKIN_ID:
+                filenames = self.GetCurrentDocuments()[:]
+                filenames.sort(self.BasenameCaseInsensitiveCompare)            
+    
+                # ask user if dirty files should be saved first
+                openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
+                for filename in filenames:
+                    for doc in openDocs:
+                        if doc.GetFilename() == filename and doc.IsModified():
+                            yesNoMsg = wx.MessageDialog(wx.GetApp().GetTopWindow(),
+                                                     _("'%s' has unsaved modifications.  Save it before commit?") % os.path.basename(filename),
+                                                     _("SVN Commit"),
+                                                     wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION)
+                            yesNoMsg.CenterOnParent()
+                            status = yesNoMsg.ShowModal()
+                            yesNoMsg.Destroy()
+                            if status == wx.ID_YES:
+                                doc.Save()
+                            elif status == wx.ID_NO:
+                                pass
+                            else: # elif status == wx.CANCEL:
+                                return True
+                            break
+    
+                shortFilenames = []
+                for i, filename in enumerate(filenames):
+                    shortFilename = os.path.basename(filename)
+                    shortFilenames.append(shortFilename)
+    
+                dlg = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("SVN Commit"))
+    
+                sizer = wx.BoxSizer(wx.VERTICAL)
+                sizer.Add(wx.StaticText(dlg, -1, _("Comment:")), 0, wx.ALIGN_CENTER_VERTICAL)
+                commentText = wx.TextCtrl(dlg, -1, size=(250,-1), style=wx.TE_MULTILINE)
+                sizer.Add(commentText, 1, wx.EXPAND|wx.TOP, HALF_SPACE)
+    
+                sizer.Add(wx.StaticText(dlg, -1, _("Files:")), 0, wx.ALIGN_CENTER_VERTICAL|wx.TOP, SPACE)
+                fileList = wx.CheckListBox(dlg, -1, choices = shortFilenames)
+                for i in range(fileList.GetCount()):
+                    fileList.Check(i, True)
+                sizer.Add(fileList, 0, wx.EXPAND|wx.TOP, HALF_SPACE)
+    
+                buttonSizer = wx.StdDialogButtonSizer()
+                okBtn = wx.Button(dlg, wx.ID_OK)
+                okBtn.SetDefault()
+                buttonSizer.AddButton(okBtn)
+                buttonSizer.AddButton(wx.Button(dlg, wx.ID_CANCEL))
+                buttonSizer.Realize()
+                
+                contentSizer = wx.BoxSizer(wx.VERTICAL)
+                contentSizer.Add(sizer, 0, wx.ALL, SPACE)
+                contentSizer.Add(buttonSizer, 0, wx.ALL|wx.ALIGN_RIGHT, SPACE)
+    
+                dlg.SetSizer(contentSizer)
+                dlg.Fit()
+                dlg.Layout()
+    
+                dlg.CenterOnParent()
+                if dlg.ShowModal() == wx.ID_OK:
+                    wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
+    
+                    messageService = wx.GetApp().GetService(MessageService.MessageService)
+                    messageService.ShowWindow()
+    
+                    view = messageService.GetView()
+                    view.ClearLines()
+                    view.AddLines(_("SVN Commit:\n"))
+    
+                    try:
+                        selFilenames = []
+                        for i in range(fileList.GetCount()):
+                            if fileList.IsChecked(i):
+                                selFilenames.append(filenames[i])
+                                view.AddLines("%s\n" % filenames[i])
+                            
+                        if len(selFilenames):
+                            comment = commentText.GetValue()
+                            status = self._client.checkin(selFilenames, comment)
+    
+                            if status is None:
+                                view.AddLines(_("Nothing to commit.\n"))
+                            elif status.number > 0:
+                                view.AddLines(_("Committed as revision %s.\n") % status.number)
+                            else:
+                                view.AddLines(_("Commit failed.\n"))
+    
+                    except pysvn.ClientError, e:
+                        view.AddLines("%s\n" % str(e))
+                        wx.MessageBox(str(e), _("SVN Commit"), wx.OK | wx.ICON_EXCLAMATION)
+                    except:
+                        extype, ex, tb = sys.exc_info()
+                        view.AddLines("Commit failed: (%s) %s\n" % (extype, str(ex)))
+                        for line in traceback.format_tb(tb):
+                            view.AddLines(line)
+                        wx.MessageBox(_("Commit failed."), _("SVN Commit"), wx.OK | wx.ICON_EXCLAMATION)
+    
+                    wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
+                dlg.Destroy()
+                return True
+    
+            elif id == SVNService.SVN_CHECKOUT_ID:
+                config = wx.ConfigBase_Get()
+                svnUrl = config.Read(SVN_REPOSITORY_URL, self._defaultURL)
+    
+                dlg = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("SVN Checkout"))
+    
+                gridSizer = wx.FlexGridSizer(cols = 2, hgap = 5, vgap = 5)
+                gridSizer.Add(wx.StaticText(dlg, -1, _("Repository URL:")), 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT|wx.TOP, HALF_SPACE)
+                svnUrlList = ReadSvnUrlList()
+                svnURLCombobox = wx.ComboBox(dlg, -1, size=(200, -1), choices=svnUrlList, style=wx.CB_DROPDOWN)
+                if len(svnUrlList):
+                    svnURLCombobox.SetToolTipString(svnUrlList[0])
+                    svnURLCombobox.SetStringSelection(svnUrlList[0])
+                else:
+                    svnURLCombobox.SetToolTipString(_("Set Repository URL"))
+                gridSizer.Add(svnURLCombobox, 0)
+    
+                gridSizer.Add(wx.StaticText(dlg, -1, _("Checkout to dir:")), 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT|wx.TOP, HALF_SPACE)
+                localPath = wx.TextCtrl(dlg, -1, size = (200, -1))
+                localPath.SetToolTipString(_("Path in local file system where files will be located."))
+                findDirButton = wx.Button(dlg, -1, _("Browse..."))
+    
+                def OnBrowseButton(event):
+                    dirDlg = wx.DirDialog(wx.GetApp().GetTopWindow(), _("Choose a directory:"), style=wx.DD_DEFAULT_STYLE)
+                    dir = localPath.GetValue()
+                    if len(dir):
+                        dirDlg.SetPath(dir)
+                    dirDlg.CenterOnParent()
+                    if dirDlg.ShowModal() == wx.ID_OK:
+                        localPath.SetValue(dirDlg.GetPath())
+                        localPath.SetToolTipString(localPath.GetValue())
+                        localPath.SetInsertionPointEnd()
+                    dirDlg.Destroy()
+                wx.EVT_BUTTON(findDirButton, -1, OnBrowseButton)
+    
+                sizer = wx.BoxSizer(wx.HORIZONTAL)
+                sizer.Add(localPath, 1, wx.EXPAND)
+                sizer.Add(findDirButton, 0, wx.LEFT, HALF_SPACE)
+                gridSizer.Add(sizer, 0)
+    
+                buttonSizer = wx.StdDialogButtonSizer()
+                okBtn = wx.Button(dlg, wx.ID_OK)
+                okBtn.SetDefault()
+                buttonSizer.AddButton(okBtn)
+                buttonSizer.AddButton(wx.Button(dlg, wx.ID_CANCEL))
+                buttonSizer.Realize()
+    
+                contentSizer = wx.BoxSizer(wx.VERTICAL)
+                contentSizer.Add(gridSizer, 0, wx.ALL, SPACE)
+                contentSizer.Add(buttonSizer, 0, wx.ALL|wx.ALIGN_RIGHT, SPACE)
+    
+                dlg.SetSizer(contentSizer)
+                dlg.Fit()
+                dlg.Layout()
+    
+                dlg.CenterOnParent()
+                if dlg.ShowModal() == wx.ID_OK:
+                    wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
+                    
+                    WriteSvnUrlList(svnURLCombobox)
+    
+                    messageService = wx.GetApp().GetService(MessageService.MessageService)
+                    messageService.ShowWindow()
+    
+                    view = messageService.GetView()
+                    view.ClearLines()
+                    view.AddLines(_("SVN Checkout:\n"))
+    
+                    svnUrl = svnURLCombobox.GetValue()
+                    toLocation = localPath.GetValue()
+                    try:
+                        self._client.checkout(svnUrl, toLocation)
+                        view.AddLines(_("Checkout completed.\n"))
+                    except pysvn.ClientError, e:
+                        view.AddLines(_("Checkout failed.  %s\n") % str(e))
+                        wx.MessageBox(_("Checkout failed.  %s") % str(e), _("SVN Checkout"), wx.OK | wx.ICON_EXCLAMATION)
+                    except:
+                        extype, ex, tb = sys.exc_info()
+                        view.AddLines("Checkout failed: (%s) %s\n" % (extype, str(ex)))
+                        for line in traceback.format_tb(tb):
+                            view.AddLines(line)
+                        wx.MessageBox(_("Checkout failed."), _("SVN Checkout"), wx.OK | wx.ICON_EXCLAMATION)
+    
+                    wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
+                dlg.Destroy()
+                return True
+    
+            elif id == SVNService.SVN_REVERT_ID:
                 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
-
+    
+                filenames = self.GetCurrentDocuments()
+    
                 messageService = wx.GetApp().GetService(MessageService.MessageService)
                 messageService.ShowWindow()
-
+    
                 view = messageService.GetView()
                 view.ClearLines()
-                view.AddLines(_("SVN Commit:\n"))
-
+                view.AddLines(_("SVN Revert:\n"))
+                for filename in filenames:
+                    view.AddLines("%s\n" % filename)
+    
                 try:
-                    selFilenames = []
-                    for i in range(fileList.GetCount()):
-                        if fileList.IsChecked(i):
-                            selFilenames.append(filenames[i])
-                            view.AddLines("%s\n" % filenames[i])
-                        
-                    if len(selFilenames):
-                        comment = commentText.GetValue()
-                        status = self._client.checkin(selFilenames, comment)
-
-                        if status is None:
-                            view.AddLines(_("Nothing to commit.\n"))
-                        elif status.number > 0:
-                            view.AddLines(_("Committed as revision %s.\n") % status.number)
-                        else:
-                            view.AddLines(_("Commit failed.\n"))
-
+                    self._client.revert(filenames)
+                    view.AddLines(_("Revert completed.\n"))
+                    
+                    openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
+                    for doc in openDocs[:]:   # need to make a copy of the list otherwise ordinality changes as we close the files
+                        if doc.GetFilename() in filenames:
+                            yesNoMsg = wx.MessageDialog(wx.GetApp().GetTopWindow(),
+                                                     _("Reverted file '%s' is currently open.  Close it?") % os.path.basename(doc.GetFilename()),
+                                                     _("Close File"),
+                                                     wx.YES_NO|wx.ICON_QUESTION)
+                            yesNoMsg.CenterOnParent()
+                            status = yesNoMsg.ShowModal()
+                            yesNoMsg.Destroy()
+                            if status == wx.ID_YES:
+                                doc.DeleteAllViews()
+    
                 except pysvn.ClientError, e:
                     view.AddLines("%s\n" % str(e))
-                    wx.MessageBox(str(e), _("SVN Commit"), wx.OK | wx.ICON_EXCLAMATION)
+                    wx.MessageBox(str(e), _("SVN Revert"), wx.OK | wx.ICON_EXCLAMATION)
                 except:
                     extype, ex, tb = sys.exc_info()
-                    view.AddLines("Commit failed: (%s) %s\n" % (extype, str(ex)))
+                    view.AddLines("Revert failed: (%s) %s\n" % (extype, str(ex)))
                     for line in traceback.format_tb(tb):
                         view.AddLines(line)
-                    wx.MessageBox(_("Commit failed."), _("SVN Commit"), wx.OK | wx.ICON_EXCLAMATION)
-
+                    wx.MessageBox(_("Revert failed."), _("SVN Revert"), wx.OK | wx.ICON_EXCLAMATION)
+    
                 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
-            dlg.Destroy()
-            return True
-
-
-        elif id == SVNService.SVN_CHECKIN_ID:
-            filenames = self.GetCurrentDocuments()[:]
-            filenames.sort(self.BasenameCaseInsensitiveCompare)            
-
-            # ask user if dirty files should be saved first
-            openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
-            for filename in filenames:
-                for doc in openDocs:
-                    if doc.GetFilename() == filename and doc.IsModified():
-                        yesNoMsg = wx.MessageDialog(wx.GetApp().GetTopWindow(),
-                                                 _("'%s' has unsaved modifications.  Save it before commit?") % os.path.basename(filename),
-                                                 _("SVN Commit"),
-                                                 wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION)
-                        yesNoMsg.CenterOnParent()
-                        status = yesNoMsg.ShowModal()
-                        yesNoMsg.Destroy()
-                        if status == wx.ID_YES:
-                            doc.Save()
-                        elif status == wx.ID_NO:
-                            pass
-                        else: # elif status == wx.CANCEL:
-                            return True
-                        break
-
-            shortFilenames = []
-            for i, filename in enumerate(filenames):
-                shortFilename = os.path.basename(filename)
-                shortFilenames.append(shortFilename)
-
-            dlg = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("SVN Commit"))
-
-            sizer = wx.BoxSizer(wx.VERTICAL)
-            sizer.Add(wx.StaticText(dlg, -1, _("Comment:")), 0, wx.ALIGN_CENTER_VERTICAL)
-            commentText = wx.TextCtrl(dlg, -1, size=(250,-1), style=wx.TE_MULTILINE)
-            sizer.Add(commentText, 1, wx.EXPAND|wx.TOP, HALF_SPACE)
-
-            sizer.Add(wx.StaticText(dlg, -1, _("Files:")), 0, wx.ALIGN_CENTER_VERTICAL|wx.TOP, SPACE)
-            fileList = wx.CheckListBox(dlg, -1, choices = shortFilenames)
-            for i in range(fileList.GetCount()):
-                fileList.Check(i, True)
-            sizer.Add(fileList, 0, wx.EXPAND|wx.TOP, HALF_SPACE)
-
-            buttonSizer = wx.StdDialogButtonSizer()
-            okBtn = wx.Button(dlg, wx.ID_OK)
-            okBtn.SetDefault()
-            buttonSizer.AddButton(okBtn)
-            buttonSizer.AddButton(wx.Button(dlg, wx.ID_CANCEL))
-            buttonSizer.Realize()
-            
-            contentSizer = wx.BoxSizer(wx.VERTICAL)
-            contentSizer.Add(sizer, 0, wx.ALL, SPACE)
-            contentSizer.Add(buttonSizer, 0, wx.ALL|wx.ALIGN_RIGHT, SPACE)
-
-            dlg.SetSizer(contentSizer)
-            dlg.Fit()
-            dlg.Layout()
-
-            dlg.CenterOnParent()
-            if dlg.ShowModal() == wx.ID_OK:
+                return True
+    
+            elif id == SVNService.SVN_ADD_ID:
                 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
-
+    
+                filenames = self.GetCurrentDocuments()
+    
                 messageService = wx.GetApp().GetService(MessageService.MessageService)
                 messageService.ShowWindow()
-
+    
                 view = messageService.GetView()
                 view.ClearLines()
-                view.AddLines(_("SVN Commit:\n"))
-
+                view.AddLines(_("SVN Add:\n"))
+                for filename in filenames:
+                    view.AddLines("%s\n" % filename)
+    
                 try:
-                    selFilenames = []
-                    for i in range(fileList.GetCount()):
-                        if fileList.IsChecked(i):
-                            selFilenames.append(filenames[i])
-                            view.AddLines("%s\n" % filenames[i])
-                        
-                    if len(selFilenames):
-                        comment = commentText.GetValue()
-                        status = self._client.checkin(selFilenames, comment)
-
-                        if status is None:
-                            view.AddLines(_("Nothing to commit.\n"))
-                        elif status.number > 0:
-                            view.AddLines(_("Committed as revision %s.\n") % status.number)
-                        else:
-                            view.AddLines(_("Commit failed.\n"))
-
+                    self._client.add(filenames)
+                    view.AddLines(_("Add completed.\n"))
                 except pysvn.ClientError, e:
                     view.AddLines("%s\n" % str(e))
-                    wx.MessageBox(str(e), _("SVN Commit"), wx.OK | wx.ICON_EXCLAMATION)
+                    wx.MessageBox(str(e), _("SVN Add"), wx.OK | wx.ICON_EXCLAMATION)
                 except:
                     extype, ex, tb = sys.exc_info()
-                    view.AddLines("Commit failed: (%s) %s\n" % (extype, str(ex)))
+                    view.AddLines("Add failed: (%s) %s\n" % (extype, str(ex)))
                     for line in traceback.format_tb(tb):
                         view.AddLines(line)
-                    wx.MessageBox(_("Commit failed."), _("SVN Commit"), wx.OK | wx.ICON_EXCLAMATION)
-
+                    wx.MessageBox(_("Add failed."), _("SVN Add"), wx.OK | wx.ICON_EXCLAMATION)
+    
                 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
-            dlg.Destroy()
-            return True
-
-        elif id == SVNService.SVN_CHECKOUT_ID:
-            config = wx.ConfigBase_Get()
-            svnUrl = config.Read(SVN_REPOSITORY_URL, self._defaultURL)
-
-            dlg = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("SVN Checkout"))
-
-            gridSizer = wx.FlexGridSizer(cols = 2, hgap = 5, vgap = 5)
-            gridSizer.Add(wx.StaticText(dlg, -1, _("Repository URL:")), 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT|wx.TOP, HALF_SPACE)
-            svnUrlList = ReadSvnUrlList()
-            svnURLCombobox = wx.ComboBox(dlg, -1, size=(200, -1), choices=svnUrlList, style=wx.CB_DROPDOWN)
-            if len(svnUrlList):
-                svnURLCombobox.SetToolTipString(svnUrlList[0])
-                svnURLCombobox.SetStringSelection(svnUrlList[0])
-            else:
-                svnURLCombobox.SetToolTipString(_("Set Repository URL"))
-            gridSizer.Add(svnURLCombobox, 0)
-
-            gridSizer.Add(wx.StaticText(dlg, -1, _("Checkout to dir:")), 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT|wx.TOP, HALF_SPACE)
-            localPath = wx.TextCtrl(dlg, -1, size = (200, -1))
-            localPath.SetToolTipString(_("Path in local file system where files will be located."))
-            findDirButton = wx.Button(dlg, -1, _("Browse..."))
-
-            def OnBrowseButton(event):
-                dirDlg = wx.DirDialog(wx.GetApp().GetTopWindow(), _("Choose a directory:"), style=wx.DD_DEFAULT_STYLE)
-                dir = localPath.GetValue()
-                if len(dir):
-                    dirDlg.SetPath(dir)
-                dirDlg.CenterOnParent()
-                if dirDlg.ShowModal() == wx.ID_OK:
-                    localPath.SetValue(dirDlg.GetPath())
-                    localPath.SetToolTipString(localPath.GetValue())
-                    localPath.SetInsertionPointEnd()
-                dirDlg.Destroy()
-            wx.EVT_BUTTON(findDirButton, -1, OnBrowseButton)
-
-            sizer = wx.BoxSizer(wx.HORIZONTAL)
-            sizer.Add(localPath, 1, wx.EXPAND)
-            sizer.Add(findDirButton, 0, wx.LEFT, HALF_SPACE)
-            gridSizer.Add(sizer, 0)
-
-            buttonSizer = wx.StdDialogButtonSizer()
-            okBtn = wx.Button(dlg, wx.ID_OK)
-            okBtn.SetDefault()
-            buttonSizer.AddButton(okBtn)
-            buttonSizer.AddButton(wx.Button(dlg, wx.ID_CANCEL))
-            buttonSizer.Realize()
-
-            contentSizer = wx.BoxSizer(wx.VERTICAL)
-            contentSizer.Add(gridSizer, 0, wx.ALL, SPACE)
-            contentSizer.Add(buttonSizer, 0, wx.ALL|wx.ALIGN_RIGHT, SPACE)
-
-            dlg.SetSizer(contentSizer)
-            dlg.Fit()
-            dlg.Layout()
-
-            dlg.CenterOnParent()
-            if dlg.ShowModal() == wx.ID_OK:
+                return True
+    
+            elif id == SVNService.SVN_DELETE_ID:
                 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
-                
-                WriteSvnUrlList(svnURLCombobox)
-
+    
+                filenames = self.GetCurrentDocuments()
+    
                 messageService = wx.GetApp().GetService(MessageService.MessageService)
                 messageService.ShowWindow()
-
+    
                 view = messageService.GetView()
                 view.ClearLines()
-                view.AddLines(_("SVN Checkout:\n"))
-
-                svnUrl = svnURLCombobox.GetValue()
-                toLocation = localPath.GetValue()
+                view.AddLines(_("SVN Delete:\n"))
+                for filename in filenames:
+                    view.AddLines("%s\n" % filename)
+    
                 try:
-                    self._client.checkout(svnUrl, toLocation)
-                    view.AddLines(_("Checkout completed.\n"))
+                    self._client.remove(filenames)
+                    view.AddLines(_("Delete completed.\n"))
                 except pysvn.ClientError, e:
-                    view.AddLines(_("Checkout failed.  %s\n") % str(e))
-                    wx.MessageBox(_("Checkout failed.  %s") % str(e), _("SVN Checkout"), wx.OK | wx.ICON_EXCLAMATION)
+                    view.AddLines("%s\n" % str(e))
+                    wx.MessageBox(str(e), _("SVN Delete"), wx.OK | wx.ICON_EXCLAMATION)
                 except:
                     extype, ex, tb = sys.exc_info()
-                    view.AddLines("Checkout failed: (%s) %s\n" % (extype, str(ex)))
+                    view.AddLines("Delete failed: (%s) %s\n" % (extype, str(ex)))
                     for line in traceback.format_tb(tb):
                         view.AddLines(line)
-                    wx.MessageBox(_("Checkout failed."), _("SVN Checkout"), wx.OK | wx.ICON_EXCLAMATION)
-
+                    wx.MessageBox(_("Delete failed."), _("SVN Delete"), wx.OK | wx.ICON_EXCLAMATION)
+    
                 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
-            dlg.Destroy()
-            return True
-
-        elif id == SVNService.SVN_REVERT_ID:
-            wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
-
-            filenames = self.GetCurrentDocuments()
-
-            messageService = wx.GetApp().GetService(MessageService.MessageService)
-            messageService.ShowWindow()
-
-            view = messageService.GetView()
-            view.ClearLines()
-            view.AddLines(_("SVN Revert:\n"))
-            for filename in filenames:
-                view.AddLines("%s\n" % filename)
-
-            try:
-                self._client.revert(filenames)
-                view.AddLines(_("Revert completed.\n"))
-                
-                openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
-                for doc in openDocs[:]:   # need to make a copy of the list otherwise ordinality changes as we close the files
-                    if doc.GetFilename() in filenames:
-                        yesNoMsg = wx.MessageDialog(wx.GetApp().GetTopWindow(),
-                                                 _("Reverted file '%s' is currently open.  Close it?") % os.path.basename(doc.GetFilename()),
-                                                 _("Close File"),
-                                                 wx.YES_NO|wx.ICON_QUESTION)
-                        yesNoMsg.CenterOnParent()
-                        status = yesNoMsg.ShowModal()
-                        yesNoMsg.Destroy()
-                        if status == wx.ID_YES:
-                            doc.DeleteAllViews()
-
-            except pysvn.ClientError, e:
-                view.AddLines("%s\n" % str(e))
-                wx.MessageBox(str(e), _("SVN Revert"), wx.OK | wx.ICON_EXCLAMATION)
-            except:
-                extype, ex, tb = sys.exc_info()
-                view.AddLines("Revert failed: (%s) %s\n" % (extype, str(ex)))
-                for line in traceback.format_tb(tb):
-                    view.AddLines(line)
-                wx.MessageBox(_("Revert failed."), _("SVN Revert"), wx.OK | wx.ICON_EXCLAMATION)
-
-            wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
-            return True
-
-        elif id == SVNService.SVN_ADD_ID:
-            wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
-
-            filenames = self.GetCurrentDocuments()
-
-            messageService = wx.GetApp().GetService(MessageService.MessageService)
-            messageService.ShowWindow()
-
-            view = messageService.GetView()
-            view.ClearLines()
-            view.AddLines(_("SVN Add:\n"))
-            for filename in filenames:
-                view.AddLines("%s\n" % filename)
-
-            try:
-                self._client.add(filenames)
-                view.AddLines(_("Add completed.\n"))
-            except pysvn.ClientError, e:
-                view.AddLines("%s\n" % str(e))
-                wx.MessageBox(str(e), _("SVN Add"), wx.OK | wx.ICON_EXCLAMATION)
-            except:
-                extype, ex, tb = sys.exc_info()
-                view.AddLines("Add failed: (%s) %s\n" % (extype, str(ex)))
-                for line in traceback.format_tb(tb):
-                    view.AddLines(line)
-                wx.MessageBox(_("Add failed."), _("SVN Add"), wx.OK | wx.ICON_EXCLAMATION)
-
-            wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
-            return True
-
-        elif id == SVNService.SVN_DELETE_ID:
-            wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
-
-            filenames = self.GetCurrentDocuments()
-
-            messageService = wx.GetApp().GetService(MessageService.MessageService)
-            messageService.ShowWindow()
-
-            view = messageService.GetView()
-            view.ClearLines()
-            view.AddLines(_("SVN Delete:\n"))
-            for filename in filenames:
-                view.AddLines("%s\n" % filename)
-
-            try:
-                self._client.remove(filenames)
-                view.AddLines(_("Delete completed.\n"))
-            except pysvn.ClientError, e:
-                view.AddLines("%s\n" % str(e))
-                wx.MessageBox(str(e), _("SVN Delete"), wx.OK | wx.ICON_EXCLAMATION)
-            except:
-                extype, ex, tb = sys.exc_info()
-                view.AddLines("Delete failed: (%s) %s\n" % (extype, str(ex)))
-                for line in traceback.format_tb(tb):
-                    view.AddLines(line)
-                wx.MessageBox(_("Delete failed."), _("SVN Delete"), wx.OK | wx.ICON_EXCLAMATION)
-
+                return True
+    
+            return False
+        finally:
             wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
-            return True
-
-        return False
 
 
     def ProcessUpdateUIEvent(self, event):
index cb16f7bbf55d7448581dcb4dcb3e17fda6a49fce..a70cdf3bc445817cc47118a69eee42196597850f 100644 (file)
@@ -6,7 +6,7 @@
 #
 # Created:      3/10/05
 # CVS-ID:       $Id$
-# Copyright:    (c) 2005 ActiveGrid, Inc.
+# Copyright:    (c) 2005-2006 ActiveGrid, Inc.
 # License:      wxWindows License
 #----------------------------------------------------------------------------
 
@@ -15,12 +15,14 @@ import os.path
 import wx
 import string
 import ProjectEditor
-import activegrid.util.sysutils as sysutils
-import activegrid.util.strutils as strutils
 import activegrid.util.appdirs as appdirs
+import activegrid.util.fileutils as fileutils
+import activegrid.util.strutils as strutils
+import activegrid.util.sysutils as sysutils
+import activegrid.util.xmlutils as xmlutils
 _ = wx.GetTranslation
 
-def CreateDirectoryControl( parent, fileLabel=_("File Name:"), dirLabel=_("Directory"), fileExtension="*", startingName="", startingDirectory=None, choiceDirs=None, appDirDefaultStartDir=False, returnAll=False):
+def CreateDirectoryControl( parent, fileLabel=_("File Name:"), dirLabel=_("Directory:"), fileExtension="*", startingName="", startingDirectory=None, choiceDirs=None, appDirDefaultStartDir=False, returnAll=False, useDirDialog=False):
 
     if not choiceDirs:
         choiceDirs = []
@@ -61,8 +63,8 @@ def CreateDirectoryControl( parent, fileLabel=_("File Name:"), dirLabel=_("Direc
             
         if os.getcwd() not in choiceDirs:
             choiceDirs.append(os.getcwd())                
-        if appdirs.documents_folder not in choiceDirs:
-            choiceDirs.append(appdirs.documents_folder
+        if appdirs.getSystemDir() not in choiceDirs:
+            choiceDirs.append(appdirs.getSystemDir()
 
     if not startingDirectory:
         startingDirectory = os.getcwd()
@@ -85,12 +87,18 @@ def CreateDirectoryControl( parent, fileLabel=_("File Name:"), dirLabel=_("Direc
             else:
                 name = _("%s.%s") % (nameCtrlValue, fileExtension)
                 
-        dlg = wx.FileDialog(parent, _("Choose a filename and directory"),
+        if not useDirDialog:
+            dlg = wx.FileDialog(parent, _("Choose a filename and directory"),
                        defaultDir = dirControl.GetValue().strip(),
                        defaultFile = name,
                        wildcard= "*.%s" % fileExtension,
                        style=wx.SAVE|wx.CHANGE_DIR)
-
+        else:
+            dlg = wx.DirDialog(wx.GetApp().GetTopWindow(),
+                        _("Choose a directory:"),
+                        defaultPath=dirControl.GetValue().strip(),
+                        style=wx.DD_DEFAULT_STYLE|wx.DD_NEW_DIR_BUTTON)
+            
         if dlg.ShowModal() != wx.ID_OK:
             dlg.Destroy()
             return
@@ -98,49 +106,78 @@ def CreateDirectoryControl( parent, fileLabel=_("File Name:"), dirLabel=_("Direc
         dlg.Destroy()
         
         if path:
-            dir, filename = os.path.split(path)
-            if dirControl.FindString(dir) == wx.NOT_FOUND:
-                dirControl.Insert(dir, 0)
-            dirControl.SetValue(dir)
-            dirControl.SetToolTipString(dir)
-            nameControl.SetValue(filename)
+            if not useDirDialog:
+                dir, filename = os.path.split(path)
+                if dirControl.FindString(dir) == wx.NOT_FOUND:
+                    dirControl.Insert(dir, 0)
+                dirControl.SetValue(dir)
+                dirControl.SetToolTipString(dir)
+                nameControl.SetValue(filename)
+            else:
+                dirControl.SetValue(path)
+                dirControl.SetToolTipString(path)
 
     parent.Bind(wx.EVT_BUTTON, OnFindDirClick, button)
     
-    def Validate(allowOverwriteOnPrompt=False, infoString='', noFirstCharDigit=False):
+    def Validate(allowOverwriteOnPrompt=False, infoString='', validClassName=False, ignoreFileConflicts=False):
         projName = nameControl.GetValue().strip()
         if projName == "":
             wx.MessageBox(_("Please provide a %sfile name.") % infoString, _("Provide a File Name"))            
             return False
-        if noFirstCharDigit and projName[0].isdigit():
-            wx.MessageBox(_("File name cannot start with a number.  Please enter a different name."), _("Invalid File Name"))            
-            return False
         if projName.find(' ') != -1:
             wx.MessageBox(_("Please provide a %sfile name that does not contains spaces.") % infoString, _("Spaces in File Name"))            
             return False
-        if not os.path.exists(dirControl.GetValue()):
-            wx.MessageBox(_("That %sdirectory does not exist. Please choose an existing directory.") % infoString, _("Provide a Valid Directory"))            
+        if validClassName:
+            if projName[0].isdigit():
+                wx.MessageBox(_("File name cannot start with a number.  Please enter a different name."), _("Invalid File Name"))            
+                return False
+            if projName.endswith(".agp"):
+                projName2 = projName[:-4]
+            else:
+                projName2 = projName
+            if not projName2.replace("_", "a").isalnum():  # [a-zA-Z0-9_]  note '_' is allowed and ending '.agp'.
+                wx.MessageBox(_("Name must be alphanumeric ('_' allowed).  Please enter a valid name."), _("Project Name"))
+                return False
+
+        dirName = dirControl.GetValue().strip()
+        if dirName == "":
+            wx.MessageBox(_("No directory.  Please provide a directory."), _("Provide a Directory"))            
             return False
-        
-        filePath = os.path.join(dirControl.GetValue(), MakeNameEndInExtension(projName, "." + fileExtension))
-        if os.path.exists(filePath):
-            if allowOverwriteOnPrompt:
-                res = wx.MessageBox(_("That %sfile already exists. Would you like to overwrite it.") % infoString, "File Exists", style=wx.YES_NO|wx.NO_DEFAULT)
-                return (res == wx.YES)  
-            else:                
-                wx.MessageBox(_("That %sfile already exists. Please choose a different name.") % infoString, "File Exists")
-                return False                  
+        if os.sep == "\\" and dirName.find("/") != -1:
+            wx.MessageBox(_("Wrong delimiter '/' found in directory path.  Use '%s' as delimiter.") % os.sep, _("Provide a Valid Directory"))            
+            return False
+        if not os.path.exists(dirName):
+            wx.MessageBox(_("That %sdirectory does not exist.  Please choose an existing directory.") % infoString, _("Provide a Valid Directory"))            
+            return False
+        if not ignoreFileConflicts:
+            filePath = os.path.join(dirName, MakeNameEndInExtension(projName, "." + fileExtension))
+            if os.path.exists(filePath):
+                if allowOverwriteOnPrompt:
+                    res = wx.MessageBox(_("That %sfile already exists. Would you like to overwrite it.") % infoString, "File Exists", style=wx.YES_NO|wx.NO_DEFAULT)
+                    return (res == wx.YES)  
+                else:                
+                    wx.MessageBox(_("That %sfile already exists. Please choose a different name.") % infoString, "File Exists")
+                    return False
+
         return True    
     HALF_SPACE = 5
     flexGridSizer = wx.FlexGridSizer(cols = 3, vgap = HALF_SPACE, hgap = HALF_SPACE)
     flexGridSizer.AddGrowableCol(1,1)
-    flexGridSizer.Add(nameLabelText, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT)
-    flexGridSizer.Add(nameControl, 2, flag=wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
-    flexGridSizer.Add(button, flag=wx.ALIGN_RIGHT|wx.LEFT, border=HALF_SPACE)
+    if not useDirDialog:
+        flexGridSizer.Add(nameLabelText, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT)
+        flexGridSizer.Add(nameControl, 2, flag=wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
+        flexGridSizer.Add(button, flag=wx.ALIGN_RIGHT|wx.LEFT, border=HALF_SPACE)        
+        flexGridSizer.Add(dirLabelText, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT)
+        flexGridSizer.Add(dirControl, 2, flag=wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
+        flexGridSizer.Add(wx.StaticText(parent, -1, ""), 0)
+    else:
+        flexGridSizer.Add(nameLabelText, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT)
+        flexGridSizer.Add(nameControl, 2, flag=wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
+        flexGridSizer.Add(wx.StaticText(parent, -1, ""), 0)
+        flexGridSizer.Add(dirLabelText, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT)
+        flexGridSizer.Add(dirControl, 2, flag=wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
+        flexGridSizer.Add(button, flag=wx.ALIGN_RIGHT|wx.LEFT, border=HALF_SPACE)        
         
-    flexGridSizer.Add(dirLabelText, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT)
-    flexGridSizer.Add(dirControl, 2, flag=wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
-    flexGridSizer.Add(wx.StaticText(parent, -1, ""), 0)
     if returnAll:
         return nameControl, dirControl, flexGridSizer, Validate, allControls
     else:
@@ -188,8 +225,8 @@ def CreateDirectoryOnlyControl( parent, dirLabel=_("Location:"), startingDirecto
 
         if os.getcwd() not in choiceDirs:
             choiceDirs.append(os.getcwd())                
-        if appdirs.documents_folder not in choiceDirs:
-            choiceDirs.append(appdirs.documents_folder)                
+        if appdirs.getSystemDir() not in choiceDirs:
+            choiceDirs.append(appdirs.getSystemDir())                
             
 
     if not startingDirectory:
@@ -221,6 +258,9 @@ def CreateDirectoryOnlyControl( parent, dirLabel=_("Location:"), startingDirecto
         if dirName == "":
             wx.MessageBox(_("Please provide a directory."), _("Provide a Directory"))            
             return False
+        if os.sep == "\\" and dirName.find("/") != -1:
+            wx.MessageBox(_("Wrong delimiter '/' found in directory path.  Use '%s' as delimiter.") % os.sep, _("Provide a Valid Directory"))            
+            return False
         if not os.path.exists(dirName):
             wx.MessageBox(_("That directory does not exist. Please choose an existing directory."), _("Provide a Valid Directory"))            
             return False
@@ -241,17 +281,25 @@ def CreateNameOnlyControl( parent, fileLabel, startingName="", startingDirectory
     fileLabelText = wx.StaticText(parent, -1, fileLabel)
     nameControl = wx.TextCtrl(parent, -1, startingName, size=(-1,-1))
         
-    def Validate(allowOverwriteOnPrompt=False, noFirstCharDigit=False):
+    def Validate(allowOverwriteOnPrompt=False, validClassName=False):
         projName = nameControl.GetValue().strip()
         if projName == "":
             wx.MessageBox(_("Blank name.  Please enter a valid name."), _("Project Name"))            
             return False
-        if noFirstCharDigit and projName[0].isdigit():
-            wx.MessageBox(_("Name cannot start with a number.  Please enter a valid name."), _("Project Name"))            
-            return False
         if projName.find(' ') != -1:
-            wx.MessageBox(_("Spaces in name.  Name cannot have spaces.") % infoString, _("Project Name"))            
+            wx.MessageBox(_("Spaces in name.  Name cannot have spaces."), _("Project Name"))            
             return False
+        if validClassName:
+            if projName[0].isdigit():
+                wx.MessageBox(_("Name cannot start with a number.  Please enter a valid name."), _("Project Name"))            
+                return False
+            if projName.endswith(".agp"):
+                projName2 = projName[:-4]
+            else:
+                projName2 = projName
+            if not projName2.replace("_", "a").isalnum():  # [a-zA-Z0-9_]  note '_' is allowed and ending '.agp'.
+                wx.MessageBox(_("Name must be alphanumeric ('_' allowed).  Please enter a valid name."), _("Project Name"))
+                return False
         path = os.path.join(startingDirectoryControl.GetValue().strip(), projName)
         if os.path.exists(path):
             if os.path.isdir(path):
@@ -280,6 +328,29 @@ def CreateNameOnlyControl( parent, fileLabel, startingName="", startingDirectory
     return nameControl, flexGridSizer, Validate
     
 
+def ValidateName(name, ext=None, hint="name"):
+    """ Returns an error string if there is something wrong with the name.
+        Otherwise it returns None
+    """
+    if name == "":
+        return _("Blank %s.  Please enter a valid %s.") % (hint, hint)
+
+    if name.find(' ') != -1:
+        return _("Spaces in %s.  %s cannot have spaces.") % (hint, hint.title())
+
+    if name[0].isdigit():
+        return _("%s cannot start with a number.  Please enter a valid %s.") % (hint.title(), hint)
+
+    if ext and name.endswith(ext):  # strip extension if provided
+        lenExt = len(ext)
+        name = name[:-lenExt]
+        
+    if not name.replace("_", "a").isalnum():  # [a-zA-Z0-9_]  note '_' is allowed and ext ending.
+        return _("%s must be alphanumeric ('_' allowed).  Please enter a valid %s.") % (hint.title(), hint)
+
+    return None
+
+
 def GetCurrentProject():
     projectDocument = None
     projectService = wx.GetApp().GetService(ProjectEditor.ProjectService)
@@ -330,6 +401,16 @@ def GetPythonExecPath():
     return pythonExecPath
     
 
+def GetPHPExecPath():
+    PHPExecPath = wx.ConfigBase_Get().Read("ActiveGridPHPLocation")
+    return PHPExecPath
+
+
+def GetPHPINIPath():
+    PHPINIPath = wx.ConfigBase_Get().Read("ActiveGridPHPINILocation")
+    return PHPINIPath
+
+
 def _DoRemoveRecursive(path, skipFile=None, skipped=False):
     if path == skipFile:
         skipped = True
@@ -362,16 +443,62 @@ def CaseInsensitiveCompare(s1, s2):
 
 def GetAnnotation(model, elementName):
     """ Get an object's annotation used for tooltips """
-    if hasattr(model, "__xsdcomplextype__"):
+    if hasattr(model, "_complexType"):
+        ct = model._complexType
+    elif hasattr(model, "__xsdcomplextype__"):
         ct = model.__xsdcomplextype__
-        if ct:
-            el = ct.findElement(elementName)
-            if el and el.annotation:
-                return el.annotation
+    else:
+        ct = None
+            
+    if ct:
+        el = ct.findElement(elementName)
+        if el and el.annotation:
+            return el.annotation
             
     return ""
 
 
+def GetDisplayName(doc, name):
+    if name:
+        appDocMgr = doc.GetAppDocMgr()
+        if appDocMgr:
+            name = appDocMgr.toDisplayTypeName(name)
+        else:
+            namespace, name = xmlutils.splitType(name)
+            if namespace and hasattr(doc.GetModel(), "getXmlNamespaces"):
+                for xmlkey, xmlval in doc.GetModel().getXmlNamespaces().iteritems():
+                    if xmlval == namespace:
+                        name = "%s:%s" % (xmlkey, name)
+                        break                    
+    
+        if name:
+            import activegrid.model.schema as schemalib
+            baseTypeName = schemalib.mapXsdType(name)
+            if baseTypeName:
+                name = baseTypeName
+
+    return name
+
+
+def GetInternalName(doc, name):
+    if name:
+        appDocMgr = doc.GetAppDocMgr()
+        if appDocMgr:
+            name = appDocMgr.toInternalTypeName(name)
+        else:
+            namespace, name = xmlutils.splitType(name)
+            if namespace and hasattr(doc.GetModel(), "getXmlNamespaces"):
+                for xmlkey, xmlval in doc.GetModel().getXmlNamespaces().iteritems():
+                    if xmlkey == namespace:
+                        name = "%s:%s" % (xmlval, name)
+                        break  
+                                          
+        import activegrid.model.schema as schemalib
+        name = schemalib.mapAGType(name)
+
+    return name
+
+
 #----------------------------------------------------------------------------
 # Methods for finding application level info
 #----------------------------------------------------------------------------
@@ -435,7 +562,8 @@ def GetAppDocMgrForDoc(doc):
 
 
 def GetAppInfoLanguage(doc=None):
-    from activegrid.server.deployment import LANGUAGE_DEFAULT
+    from activegrid.server.projectmodel import LANGUAGE_DEFAULT
+    
     if doc:
         language = doc.GetAppInfo().language
     else:
@@ -449,3 +577,159 @@ def GetAppInfoLanguage(doc=None):
             doc.GetAppInfo().language = language  # once it is selected, it must be set.
         
     return language
+
+def AddWsdlAgToProjectFromWsdlRegistration(wsdlRegistration):
+    """Add wsdl ag for registry entry."""
+
+    wsdlPath = wsdlRegistration.path
+    rootPath = None
+    serviceRefName = wsdlRegistration.name
+    
+    agwsDoc = _InitWsdlAg(wsdlPath, rootPath, serviceRefName)
+
+    if (agwsDoc == None):
+        return
+
+    serviceRef = agwsDoc.GetModel()    
+    
+    serviceRef.serviceType = wsdlRegistration.type
+
+    import activegrid.server.deployment as deployment
+
+    if (serviceRef.serviceType == deployment.SERVICE_LOCAL):
+        serviceRef.localService = deployment.LocalService(
+            wsdlRegistration.codeFile)
+        
+    elif (serviceRef.serviceType == deployment.SERVICE_DATABASE):
+        serviceRef.databaseService = deployment.DatabaseService(
+            wsdlRegistration.datasourceName)
+        
+    elif (serviceRef.serviceType == deployment.SERVICE_SOAP):
+        pass
+    
+    elif (serviceRef.serviceType == deployment.SERVICE_RSS):
+        serviceRef.rssService = deployment.RssService(wsdlRegistration.feedUrl)
+        
+    elif (serviceRef.serviceType == deployment.SERVICE_REST):
+        serviceRef.restService = deployment.RestService(
+            wsdlRegistration.baseUrl)
+    else:
+        raise AssertionError("Unknown service type")
+
+    _AddToProject(agwsDoc, addWsdl=True)
+    
+
+def AddWsdlAgToProject(wsdlPath, rootPath=fileutils.AG_SYSTEM_STATIC_VAR_REF,
+                       serviceRefName=None, className=None, serviceType=None,
+                       dataSourceName=None):
+    """
+       wsdlPath: path to wsdl from rootPath. If wsdlPath is absolute, rootPath
+       is ignored. rootPath is also ignored when rootPath is set to None. 
+       rootPath: defaults to ${AG_SYSTEM_STATIC}.
+       serviceRefName: If None, it will be set to the wsdl file name without
+       the .wsdl file extension.
+       className: if not None, will be used for the the wsdlag's ClassName.
+       serviceType: defaults to local.
+       dataSourceName: if serviceType is deployment.DATABASE, the ds must be
+       provided.
+    """
+    import WsdlAgEditor
+    import XFormWizard
+    import activegrid.model.basedocmgr as basedocmgr
+    import activegrid.server.deployment as deployment
+
+    if (serviceType == None):
+        serviceType = deployment.SERVICE_LOCAL
+
+
+    agwsDoc = _InitWsdlAg(wsdlPath, rootPath, serviceRefName)
+
+    if (agwsDoc == None):
+        return
+
+    serviceRef = agwsDoc.GetModel()    
+    
+    serviceRef.serviceType = serviceType
+
+    if (serviceType == deployment.SERVICE_DATABASE and dataSourceName != None):
+        serviceRef.databaseService = deployment.DatabaseService(dataSourceName)
+    else:
+        serviceRef.localService = deployment.LocalService(className=className)
+
+    _AddToProject(agwsDoc)
+        
+
+def _AddToProject(agwsDoc, addWsdl=False):
+    import activegrid.model.basedocmgr as basedocmgr    
+    projectDoc = GetCurrentProject()
+    agwsDoc.OnSaveDocument(agwsDoc.GetFilename())
+
+    files = [agwsDoc.fileName]
+    types = [basedocmgr.FILE_TYPE_SERVICE]
+    names = [agwsDoc.GetModel().name]
+    if (addWsdl):
+        m = agwsDoc.GetModel()        
+        wsdlName = os.path.splitext(os.path.basename(m.filePath))[0]
+        appDocMgr = projectDoc.GetAppDocMgr()
+        if (appDocMgr.findService(wsdlName) == None):
+            m = agwsDoc.GetModel()            
+            files.append(m.filePath)
+            types.append(None)
+            names.append(wsdlName)
+    
+    ProjectEditor.ProjectAddFilesCommand(projectDoc, files, types=types,
+                                         names=names).Do()
+    
+
+def _InitWsdlAg(wsdlPath, rootPath=fileutils.AG_SYSTEM_STATIC_VAR_REF,
+                serviceRefName=None):
+
+    projectDoc = GetCurrentProject()
+    appDocMgr = projectDoc.GetAppDocMgr()
+
+    if (serviceRefName == None):
+        serviceRefName = os.path.splitext(os.path.basename(wsdlPath))[0]
+    
+    if (appDocMgr.findServiceRef(serviceRefName) != None):
+        return None
+
+    import WsdlAgEditor
+    import XFormWizard
+    import activegrid.server.deployment as deployment
+
+    template = XFormWizard.GetTemplate(WsdlAgEditor.WsdlAgDocument)
+    ext = template.GetDefaultExtension()
+    fullPath = os.path.join(appDocMgr.homeDir, serviceRefName + ext)
+
+    agwsDoc = template.CreateDocument(
+        fullPath, flags=(wx.lib.docview.DOC_NO_VIEW|wx.lib.docview.DOC_NEW|
+                         wx.lib.docview.DOC_OPEN_ONCE))
+    
+    serviceRef = agwsDoc.GetModel()
+    serviceRef.name = serviceRefName
+
+    if (rootPath == None or os.path.isabs(wsdlPath)):
+        serviceRef.filePath = wsdlPath
+    else:
+        # make sure to use forward slashes for the path to the .wsdl
+        wsdlPath = wsdlPath.replace("\\", "/")
+        
+        if not wsdlPath.startswith("/"):
+            wsdlPath = "/%s" % wsdlPath
+        serviceRef.filePath = "%s%s" % (rootPath, wsdlPath)
+
+    agwsDoc.fileName = fullPath        
+
+    return agwsDoc
+
+
+def GetSchemaName(schema):
+    return os.path.basename(schema.fileName)
+
+
+class AGChoice(wx.Choice):
+    """Extension to wx.Choice that fixes linux bug where first item of choices
+    passed into ctor would be visible, but not selected."""
+    def __init__(self, parent, id, choices=[]):
+        super(AGChoice, self).__init__(parent=parent, id=id)
+        self.AppendItems(choices)
index af2b9dad1b6b78f914895d7826d77a932cb48e50..ab70874adf0c2bee5c4436b9c5f082815be97aa9 100644 (file)
@@ -13,7 +13,12 @@ import wx
 import wx.xrc as xrc
 import wx.wizard
 
-
+WHITE_COLOR = wx.Color(0xFF, 0xFF, 0xFF)
+LABEL_FONT = wx.Font(8, wx.SWISS, wx.NORMAL, wx.NORMAL, faceName="Arial")  
+SELECTED_LABEL_FONT = wx.Font(8, wx.SWISS, wx.NORMAL, wx.BOLD, faceName="Arial")  
+TINY_FONT = wx.Font(6, wx.SWISS, wx.NORMAL, wx.BOLD, faceName="Arial")
+ELLIPSIS_FONT = wx.Font(12, wx.SWISS, wx.NORMAL, wx.NORMAL, faceName="Arial")
+ACTIVEGRID_ORANGE_COLOR = wx.Color(0xF9, 0x9E, 0x1B)
 #----------------------------------------------------------------------------
 # Classes
 #----------------------------------------------------------------------------
@@ -22,9 +27,14 @@ class BaseWizard(wx.wizard.Wizard):
 
 
     def __init__(self, parent, title, pos=(-1,-1)):
-        wizardBitMap = getWizardBitmap()
-        wx.wizard.Wizard.__init__(self, parent, wx.NewId(), title, wizardBitMap, pos=pos)
+        self.bitmap = getWizardBGShorterBitmap()
+        self.title = title
+        wx.wizard.Wizard.__init__(self, parent, wx.NewId(), title, self.bitmap, pos=pos)
+        self.myDC = wx.MemoryDC()
+        self.crumbs = []
+        self.firstPage = None
 
+        
     def GetDocument(self):
         if self.GetParent() and hasattr(self.GetParent(), 'GetDocument'):
             return self.GetParent().GetDocument()
@@ -34,12 +44,94 @@ class BaseWizard(wx.wizard.Wizard):
     def SetPrevNext(self, prev, next):
         prev.SetNext(next)
         next.SetPrev(prev)
-
-
+        
+    def RunWizard(self, firstPage):
+        self.firstPage = firstPage
+        return wx.wizard.Wizard.RunWizard(self, firstPage)
+        
+    def BuildCrumbsList(self, onPage):
+        def PastThisPage(currentPage, pageToCheck):
+            foundPageToCheck = False
+            tempPage = self.firstPage
+            while hasattr(tempPage, '_next'):
+                if tempPage == currentPage:
+                    return foundPageToCheck
+                if tempPage == pageToCheck:
+                    foundPageToCheck = True
+                tempPage = tempPage._next
+        self.crumbs = []
+        currPage = self.firstPage
+        while hasattr(currPage, '_next'):
+            self.crumbs.append(currPage.title.GetLabel())
+            if currPage.pauseCrumbTrail and not PastThisPage(onPage, currPage):
+                self.crumbs.append('?')
+                return
+            currPage = currPage._next
+            
+        
+    def GetBreadcrumbsBitmap(self, page):
+        bitmap = getWizardBGShorterBitmap()
+        highlightText = page.title.GetLabel()
+        self.BuildCrumbsList(page)
+        self.myDC.BeginDrawing()
+        self.myDC.SelectObject(bitmap)
+        #self.myDC.SetFont(TINY_FONT)
+        #self.myDC.DrawText(self.title, 10, 35)
+        #print "Title was w=%i, h=%i" % self.myDC.GetTextExtent(self.title)
+        x = 20
+        y = 50
+        for crumb in self.crumbs:
+            if crumb == highlightText:
+                self.myDC.SetTextForeground(ACTIVEGRID_ORANGE_COLOR)
+                self.myDC.SetFont(SELECTED_LABEL_FONT)
+            else:
+                self.myDC.SetTextForeground(WHITE_COLOR)
+                self.myDC.SetFont(LABEL_FONT)
+            lines = self.BreakIntoLines(crumb)
+            offset = 0
+            w = h = 0
+            for line in lines:                  
+                offset += h + 3
+                if line == '? ':
+                    decisionBM = getDecisionBitmap()
+                    x1 = (bitmap.GetWidth() - decisionBM.GetWidth()) / 2
+                    self.myDC.DrawBitmap(decisionBM, x1, y + offset, True)
+                else:
+                    self.myDC.DrawText(line, x, y + offset)
+                w, h = self.myDC.GetTextExtent(line)
+            y += 30 + offset
+        self.myDC.EndDrawing()
+        self.myDC.SelectObject(wx.NullBitmap)
+        return bitmap
+    
+    def CenterTextUnderParent(self, dc, parentWidth, parentX, text):
+        xbase = parentX + parentWidth / 2
+        w,h = dc.GetTextExtent(text)
+        return xbase - w / 2 
+           
+    def BreakIntoLines(self, text, maxLineLength=22):
+        words = text.split(' ')
+        retval = []
+        count = 0
+        currentLineLength = 0
+        currentLine = ''
+        for word in words:
+            if len(word) + currentLineLength >= maxLineLength:
+                retval.append(currentLine)
+                currentLine = word + ' '
+                currentLineLength = len(word) + 1
+            else:
+                currentLine += word + ' '
+                currentLineLength += len(word) + 1
+        if currentLine:
+            retval.append(currentLine)
+        return retval
+        
 class TitledWizardPage(wx.wizard.PyWizardPage):
 
 
-    def __init__(self, parent, title=None):
+    def __init__(self, parent, title=None, pauseCrumbTrail=False):
+        self.pauseCrumbTrail = pauseCrumbTrail
         self._prev = None
         self._prevFunc = None
         self._next = None
@@ -48,6 +140,9 @@ class TitledWizardPage(wx.wizard.PyWizardPage):
         self.SetSizer(wx.BoxSizer(wx.VERTICAL))
         self.MakePageTitle(title)
 
+    def GetBitmap(self):
+        return self.GetParent().GetBreadcrumbsBitmap(self)
+        
     def SetTitle(self, title):
         if not title: title = ""
         self.title.SetLabel(title)
@@ -291,3 +386,2042 @@ def getWizardImage():
     stream = cStringIO.StringIO(getWizardData()) # NOTE: This reverts us to the bitmap Peter likes.
     return ImageFromStream(stream)
 
+#----------------------------------------------------------------------
+def getWizardBackgroundData():
+    return \
+'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\xac\x00\x00\x01\x87\x08\x02\
+\x00\x00\x00\xa2O.\xce\x00\x00\x00\x03sBIT\x08\x08\x08\xdb\xe1O\xe0\x00\x00 \
+\x00IDATx\x9c\xc5\xbdy\x94^\xc7u\x1fx\xeb\xeb\xd7\xdd@\xa3\xb1\x10\x04\x08\
+\x80\x04\x08\x82+\xb8\x8b\xa4HK\xa4<\x92M*\x8e#Y\x96\x9c\x13I\xf6$\xb2\x8e\
+\x97\xf1L&c+\x93\x8cg\xec\xe33\xf6\xc9\xccx\xc6g\x12\'>\x89c;\xb6\x13\xc7\
+\x1eG\x923^\x8emY\x96M\x91>\xb1\xb9J$ER\xe0N\x80\x00\x01\x02\x8d\xbd\x81\x06\
+\x1a\xbd\xd6\xfc\xf1^U\xdd\xb5\xaa\xde\xd7MM\x11\xec\xef\xbdZn\xdd\xaa\xfb\
+\xab{o-\xef=\xf7\xde\xef\xfdI\xd0\x82\x07\x9f\xae\xbdo\xa3x\x8c\xc8\xd9\x06\
+\x07\xae\xbbp\x0e\xe7\xe79=/H\xa9\x04"\x81\x9a\x91\xcbH\x8d\xd1\xd9J0{\x89\
+\xc3<c\x887T\x9b#\x9c\xe0K\xe7@\xb6\xc2n\x13\xcb)\xbb7\x17<)\xe2\x8b\r\x01\
+\x00\x80\x81\xc5P\xa6\xebu\x04x\xdf\xfd\xab\xe4\xbb\x8e\xbfoMp\x0cp\x16\xb0B\
+n5Z\xed\xf1\xbe\x08 y\\b\xc9\x81\xb3\xfee(;\xe7\xd4A\xc2\xe2\x9b\xae\x983G\
+\x0ck\x1b\x1f4!\x96\\;\xe7\xc1;p:\x12\xfb\x88\x7f\xa5j`u\x03\xadN\xf2\xe6\
+\xbd\xefX\xf2\x89\x07\x92M0\xd6\xa6\xb6\x9d\xe9P\x99t\xe1\x0b\x9d\xa0\xb0\
+\x89\xf2{\xf0V/\xc5\xf8F\xa6q\x86\xb4@\x14\x80\x92\x9cp\xa0\x14\xa9\t\x06~\
+\x03y\x83\x94\xc5\xb2\r\xf1*\x1e\xda\xea\xd41\xdd\x83\x1a\xbet\xac\x93s@\xb1\
+*\xack\x11\x06\x99\x15\x02\x08D\x1ea\xbf\x01\x00\xbc\xf7\xb5\xb2\x0c8P\xe2\
+\xab\x03\xee\x1a\x0cgS\x01\x18T\xac \xc1\x94\x18&\x06\xbe\xca;\xa9\xe4!\xaa\
+\xf7bN\x91\x12\xc6@\xcbd@v\x8d\x98\xd5\xeab\x11E\x13\x90|\xd8\xfcG\x04\x18\
+\x82\xf4\xac\x03\xd1\x00\xca\x94Jyh\xf9az\xdc\xe7:\x91\xe7\xad\x80#\xee\xf7\
+\x1a~\x12:M\x11\xbb\x94\xeaI\x82U\xb5E\xc4\x83W4\x9c\xe7Yy<\x8a\x89\n\xa9IiQ\
+\x04!-u\x13&-G\x8f6\xda\xd3@\xea\xeb\x00\xf6\x1a\xe52\xf4\xc1\x01*\xa4\xf9tX\
+\x0f\xf5B\x80\x1dS\x19Lw\xcf\xf3<\xdey\xf0\x86\x1ap\xc6u\x8c\t\xde\x86\xa9\t\
+\x18\x02:5P\x81\x80\x18\xcfz@\xe6\xe4]\xe4\\\x061x\xe0\x16:W\xe2@\xed"\xe7\
+\xf4\x89\xab\x96Se\xa3\x86\x1f\x05I\xf9\x99\x87\xe2\x1c\xb4z\xb1u"\x02\xb3R\
+\x97P*UF;Te\xfa\x048\xa8\xae@\xaf\x11^\xc8\x1c!c[\xdf\x81\x83\x91\xc1\xf2\
+\xc2\xd2\xa0G\xad\x1d\xf1\xde%0\x03L\xc0\xedm\xc1\x94\xac\xca\xdc\xc4\x01\
+\x1b&a\xe8"\x148\xef\xbc\xbb\xf3\x96\xdd\x9f\xf9\xc4\x87\x00\xe0O\x1f\xf9\
+\xfa\x13\xcf\xbd66\xda\xc9t\xe6\xc2\xec\xd8\xd8\xe8\xa5\xf9\x85\xe5\xe5\xe5\
+\xb1\xa6i\x9a\x91\x8b\x97\xe6\xd4\xaa\xcc\xd9A\xbcA\xd7\x9e\xfe\x92\xcc\xda|\
+)?\xb6\xcd\xc0H9\xe7\x9a\xc1\xd2\x87v\xbc\xbdu\xed\xec\x93S;\x0e\x9c\xdf\xd4\
+\x83V\xdd\xbcWe 7\xbe]\x9a\xfd\xf2l\x0e\x94x\x0f\xe0\x82ca\x18\xac\x8d\x93\
+\x13\xdb\xb6n\x9a:q\xf6\xdc\xcc\xac\xa5\xc9\x9dO\xf7\xb1\x01\x9b7N~\xe0\xbd{\
+\x01\xe0\xf1g_}\xff]7\xde\x7f\xcf^\x00\x98:y\xf6/\xfe\xfa\xf9\xef\xfb\xaeo{\
+\xf4\xc9o>\xf7\xd2\x81o\xbf\xf7\xe6\xebvo\xffw_\xf8K\xb59\x1c\x04\xda\x887\
+\xdc\xc0\x8a!\x86L\n\xc7J\xa5\xb9t\xce\x8d\x8f,~\xee\xf6g?\xbe\xe7M\x00\xf8\
+\x077\xbe\xf2\x8b\xcf\xdf\xf5\xf0\x91k\xb2e\x12\x8bC\x87\xa29\xd73\x88\xb8\
+\xe4Q2\x1c\xd0\xf0\x9e\x9b\xaf\xf9\xdcg?r\xfd5;\xde>z\xea\xd7\xbf\xf0\x97\
+\x8f>\xb5\x8fRm=A\x08j\xc0l\xdaKo\x1c\xbe\xf5\xc6\xabo\xbda\xe7\x9f>\xf2\xf5\
+\xcb6\xae\xfb\xfe\xef\xf9\xc0\xe2\xd2\xf2\x0b\xaf\x1e\xfa\xe4G\xee\xcf\xb4\
+\x85hWk\xb9\x91.!g\xa8AX5L\xff@\x83\x8b\xd2\x81\x82n\\\xd5\xfa\xf6\xed\x87\
+\xbf\xfb\xea\xb7\xda\xc8\xcb\xc6/\xfd\xfd\x1b_\x19\x1fY49\xe8\x89\x80\xcc\
+\x9a\xf7\xbb\x14d\x8dk\xc6\xc7>\xf3\x89\x0f\xddr\xc3\xae\xb1\xd1\xe6\xba\xab\
+\xb7}\xe6\x13\x1f\xdc\xb0n\xad,\xe8:\xde\xba_\x95\xcb\xa3\'\xce\xec{\xed\xd0\
+\xab\xfb\xdfy\xee\xa5\x03\x00\xf0\xec\xbe\xfdKKK\xdf\xf7\xb7\xbe\xed\xf0\xb1\
+\xd3\xfb^?l\xb1\xd4\xb4|\x81\x8a\x80\xec\xa4\x80\xf2gv\x1cn\xb3\x9a\xed}\xdb\
+\xa6\xee\xdcr\xb2\xbd\x9eY\x18\xfd\x93\x83\xd7\x9e\x9b\x1f\xc59\xb7\xae\x9d\
+\x1d\x1fY\x8a\xb7\x9b\xc6\xe7F\x07KsK\x9aK\xdbS|\x19e\x96\xd6\xfe\xeaC>;1\
+\x0ed\xc29\xd2\x0c&\xd6\x8e\xc5\xdb\xf1\xf1\xb1\xb5k\xc6\xce]\x98E\xf9S\xee\
+\xbcG\xd8\x86u\x13k\x06\x83\x01\x00\x1c>v\xfa\xf8\xa9s?\xf0\xb1\x0f\xfc\x9b\
+\xdf\xfe\xf2u\xbb\xb7\xeb\xb9\xbd4\x07b\x91\x18O\n\xa4z_yx\xff\xf6c\x9f\xbe\
+\xfe\xd5\xf6zni\xe4\xc9\xa9\x1d\xe7\xe6G\x01\x89\xe1\xc9\xa9\x1d\x9f\xba\xfe\
+\xb5mk/\xb6y\x9e8\xb6\xe3\xc2\xc2\x98B\xa8\xff\x00v\xa0,gE\t\x15p`\xad\x04XE\
+\xb0WO\xed\xe3\x85\x0b\x97~\xefK\x8f\xef\xd9y\xc5e\x1b\'/\xce\xce\xfd\xfe\
+\x9f?y\xfc\xf4\xb9\xb8Z\x12\n\x8bu\xc4\xb04\xcfbOO\xcf\x9c>{\xde/\xfb\x8b\
+\x97\xe6\xa7N\x9e}\xe3\xe0\xd1W\xde<rx\xea\xf4\xe6M\xeb\x19\x0f14\x8c\'\xce\
+\xb4\xde\x9c\xd4\x00VP\xed\xd6\xfa\xb0\xb0<\x88U\xc4\xb1r\xe0\xfc\xa6\x9f\
+\x7f\xe6\xde\xcf\xdc\xf4\xca\xe5kf\xbfqr\xeb/\xef\xbb\xd3\xc7\xf5yT\xf1p\x01\
+\xaf\xe0\xaa\xfeP\xa5>\xd0\xf7i\xaa\xd9z\xe4\xc9o\xbeyh\xea\xba\xab\xb7\x1d\
+\x9e:\xfd\xfa[G\x8b.\x93\x8b\xb8\x10n\xe9\x8b\xaf\xbe\xfd\xf2\x1bG\xbc\xf7o\
+\xbcu\xec\xe0\xe1\x13s\xf3\x0b/\xbczhyiy\xff\xdb\xc7\xbb<\x02\nD\x13\xe4\xd7\
+\x06\xd2\x82^\xcfU\x14\x96\x93\xb1}q1\xf1\xb0\xb0<rq\xb1IK\xce\xbe\xeb\xdc\
+\xa7O\\\xf9\xf4\x89+\x9b\xc1\xd2\xa2\x1f\xc1\x14S\x93z\x06\xbc\r\xc1\x16\xf0\
+\x19\x14\x8a8\xe0;\x90\xc3\x86\x83\xef\x9c8\xf4\xce\xc9\x96\x10\xaa\x1ex\x0c\
+\xaaY\x1dqKKK\x8bK\x8b\xe0`iiiii\xc9\x83_^\\\x06\x80\xf9\xf9ye\x83\n\x00"\
+\x08$=f\x17v\xac\x9b\xdd=y\xee\xaa\xc9\x995#K\x97\x96F\xce\xcf\x8f\xbdyn\xd3\
+\x1bg\xd7\xe7\x1b\xb6i\xcd\xd2-\x9bN\xc5RGf&_:{\xf9\xb9\xb9\x06\x006\x8c/\
+\xee\x9e<wqqt\xf3\xf8\xa5\x98\x7ft\xb0\xb4{\xf2\xdcD\xd3\xf9}\xef\\\x9c\xbc\
+\xb482:\xb2\xbck\xdd\xf96f\xa2Y8yi\xed\xd4\xa5\xc9\xf1\xc1\xe2\x95\x133\x13\
+\xcd\xc2\xc5\xc5\xd16\xf2\xf8\xec:\xcfw\xf9\xfc5\xeb\xa7c\x9e\x8b\x8b\xa3rz\
+\xb9~l\x9eqxpf\xc3\xb1\x8b\xebXW\xc8y\xe0X\xd3l\xbd|\xe3\x86\xf5\x13ss\x0bG\
+\x8e\x9fY\x98_P\xdao\xa3b\xc7\xd6\xcb6\xac\x9f\x98:qv\xfa\xfc\xc5\x90\x17/%y\
+\x1f\xd6\x88pX\xb3ft\xcb\xa6\xf5\xe3\xe3\xa3ss\x0bGO\x9cY\\\xf2bi\xd1\xf9\
+\xf6\xbf\xb0\xa8,7\xab\xa4a\xd1\xdc+\n\xae\xdb/?\xfd\xd1\xdd\x07\xde\xb3\xe5\
+\xc4\x8e\x89\x0b\xd8A\x9b\x9a\x9dx\xe2\xd8\x8e\xdfz\xf5\x96\xa3\x17\xd6J_\
+\xe1\xaa\xc9K\x1f\xbbf\xffw^\xf5\xf6\x965\xb3\x93\xa3]\x07\xcd,\x8c\x1e\xb90\
+\xf9\x95\xb7w\xff\xc1\x81\x1b>\xbc\xf3\xe0\x0f\xde\xf42\x00\xackR\xf7M\x8e.\
+\xfc\xdc\xbdO\xcd/\x0f\x00`v\xb1\xf9\xdf\x9f\xb9\xef\x9b\xa7\xb7\\>>\xfb\xb3\
+\xef}r\xd3x\xb7\xd0\xf1\xa5\x83{~\xfd\x95;6\x8d]\xfa\'w>\xbbs\xb2\x03\xc7\
+\x89\xd9\x89\x9fy\xfa\xfdS\xb3\x93]#=\x00\xc05\xeb\xa7\x7f\xfe\xdb\x1e[\x1b \
+\xf5\xa5\x83{~\xfd\xe5\x04\x82\xf1\x91\xc5\xef\xdb\xf3\xfaw\xed:x\xd5\xba\
+\x99\xc8\xe1\xdc\xd2\xc8\xd1\x8b\xeb\xfe\xf8\xadk\xbf\xf0\xc6\xdeeC\xc7l\xde\
+\xb4\xfe\xc3\x1f\xb8\xf3\xa1\x07n\xbf\xe2\xf2\x8dc\xa3\xcd\xfc\xc2\xe2\xf1S\
+\xd3\x0f?\xf6\xe2\x1f\x7f\xf5k;\xb6^\xf6\x13\x9f\xfd\xc8\xc4\xda\xb1\x87\x1f\
+{\xf1?\x7f\xf9\xf16\xff\xe8\xe8\xe8\x7f\xf3\xa9\x07\xef\xd8\xbb\xfb\xd5\xfd\
+\xef\xfc\xeb\xdf\xf9\xf3\xdbn\xd8\xf9\xc9\xbfs\xff\xde\xeb\xae\x1a\x1bm~\xeb\
+\xf7\xff\xea\xff\xfd\xf2\x13\xe0`l\xb4\xf9\x91O>x\xc7\xde\xdd\x87\x8f\x9d\
+\xfe\xd7\xff\xf1\xcf\x82W\x98\xd6]wn\xdf\xfc\x91\xef\xb8\xfb\xbe;\xae\xdf\
+\xb0~\x02W\xfa\x07\x7f\xf1\x94\xcee\x1b\xd0\xdapw\xab\x05\xd3\'p\xce9\xf0?r\
+\xf37?y\xddk\xb1\x8fp\xd8\xb6\xf6\xe2\xc7\xf7\xbcy\xfd\xc6\xe9\x9fy\xfa\xfd\
+\xef\xcc\xac\xc5D\xee\xdcr\xe6g\xeey\xfa\x9a\xf5\xe7X\x91\xc9\xd1\x85\x9b6\
+\x9d\xb9i\xd3\x99\xc9\xd1\x85\x99\x85\xd1\xe8\xeb\xe1p\x19R\x0cm\x98h\x16\
+\xafX;\x1b\xe3[\xcdqbn\xc3\xeb\xd3\x9b\xee\xd9:\x15\x99\xb9\x7f\xfb;\x7fx\
+\xe0\xc6\xb61mx\xdf\xb6\xa3\x91\x87\xb9\xa5\x91\xc7\x8f\xed\x884\xf7\xac?\
+\xfbO\xee|6\x16\x8fa|d\xe9\x9a\xf5\xe7~\xfc\xf6o\\=y\xfe_\xbcp\x8f\\\xa0\xbc\
+\xe7\xf6\xeb>\xf7\xd9\x8f\xdcr\xc3.\x1c\xb9s\xfb\xe5w\xdfz\xed\xdd\xb7\xeeyv\
+\xdf\x81v\xdd\xe6\xd5\xfd\xef\xc4\xd4\x81\x83\xfb\xee\xb8\xfe\x96\x1bvm\\\
+\xbf\xee\xe3GO}\xf6\xef~\xe8\x8a\xcb7\xb6IW\\\xbe\xa1\x15\xcc\xc8`\xd0\xe6\
+\xd9\xb3\xf3\x8a\xdf\xf8\xe2\xc3\xe7.\xcc\xe2\xb1\xf8\xa1\xfbn\xf9G\x9f\xf9\
+\xee\x9d\xdb/\x97\x95\xee\xbd\xee\xaaW\xde<"\xbb\x91l2\x19!\xe6i26ud\xb0\x0c\
+\x00\x11\x01S\xb3\x13\x87g\xd6\x03\xc0\xce\xc9\xf3Q~\xb7m>\xf9\xe9\xeb_\xfb\
+\xc5o\xdc\t\xc1\xa6\xdc\xb0iFE@\x0co\x9d\xdf\xf0\xf0\xe1\xab\xdf\xb7\xed\xa8\
+Y\xb1\x08\xf3\xcbI\x18\x97\x96\x1a\x00X\xf6\xcb\x0f\x1f\xde\xf5\xd1\xdd\xfb#\
+{\xf7n\x9d\xfa\xb3C\xd7\xc6\xa9\xa3\x03\xff\xc0\xf6T\xc57Ooy\xe9\xcc\x96\xf6\
+zrt\xee\xa7\xef\xfe\xdam\x9bO\xc6\xd4\xa9\xd9\x89\xd9\xc5\x06+\xad\x8f\xefy\
+\xf3\xd0\xcc\xfa\xcf\xbfq3f\xe3\xda\xab\xb7\xff\xaf\xff\xc3\xdfk\x85qqv\xee\
+\xd9}\x07\x0e\x1c>\xde\x8c\x0cv\xed\xd8r\xf7\xad{\x1ez\xe0\x8e{n\xbbv~aql\
+\xb4Y\\Z\xc6\x05/\xcd/\x00\xc0\xd6\xcd\x1b\xfe\xd1?\xf8\xdb\x13k\xc7\xcf\xcd\
+\xcc\x9e8}n|\xac9~\xaa\xeb%\xef\xbb<\x17f\xe7\x00\xf0l\xc0\xdf\xb1w\xf7O\xff\
+\xc3\xbf\xbbar-\x00\x9c\x99\x9e\xd9\xf7\xfa\xe1\xb7\x8f\x9e\x04\x80\x9b\xae\
+\xbd\xf2\xb6\x1b\xaf\xfe\xd8\x83\xef}\xdf{n\xe0\xfd\x15t\xa1\x93\x1e\xb4\x08\
+\x0e\x9c1\xdb\xf6\x00\x00\x0bK\x83\xdfx\xf9\xf6\x89f\xf1\xa3\xbb\xf7?|\xf8\
+\xea\xdf{\xf3\xc67\xa7\xd7\x03\xc0u\x1b\xcf\xff\xe8\xcd/~\xc7U\xdd\xe2\xc3\
+\xfb\xb6\x1d\xdd\xb8\xe6\xd6ss\x8d\xf70>\xb2\xfc\xa37\xbf\x88\x110\xb30\xfa\
+\xb5\xe3\xdb\xa6\xe7\xc7wM\xce\xdc\xb3ujjv\xe2\xa7\x9fz\xe0\xf5\xb3\x93[\xd7\
+l|\xf4\xc8\xce\xe9\xf9\xf1\x9b/;}\xd3\xa63m\xe6v\xbcN\xcf\x8f\xb7\xb7\'/)k&1\
+\xbc:\xbd\xf9k\xc7\xb7E6\xee\xbdb\xea\xca\x89\x99h\xf5\xafY?\x8d\xc5\xfcG\
+\x07\xae\x8d\x83\xe2\x87\xf7\xee\x8bI3\x0b\xa3\xff\xfe\x95[\x1f9\xb2\xeb\xc2\
+\xe2\xe8-\x9bN\xfd\xc3\xdb^\x88\xcc|\xea\xfa\xd7\x1e9\xb2\xab31\x00\xa3c\xa3\
+?\xf6\xfd\x1fn\x11\xf0\xe6\xa1\xa9_\xf8\xb5?|v\xdf~\x08\xe3\xe9\xc1\xfbo\xff\
+\xd1O\x7f\xf8\xba\xab\xb7\xa9\xbd\xdc^\xb4\x82\xfc\xe3\xaf~\xfdO\x1f\xf9\xfa\
+\x81\xb7\x8f\xaf]36{i\x1e,\x8f\xd2\x038\xd8\xb0n\xed\xe7>\xfb\x91\xb6\xe0\
+\xb3\xfb\xf6\xff\xea\x7f\xfa\x8b\x17^=\xd4\xa6\x8f\x8c\x0c\xbe\xf7\xc1{\xb1^\
+a\xb5\xea\xd7\xac\n\x00h\xcd\x01\xdf8Fa\xc9\xfb_\xdew\xe7\xc3\x87w\xbdxjs\
+\x8c|\xe3\xec\xfa\x9f\x7f\xee\xbe=\x1b\xce\xb5\xc2\xde\xb2f\xf6\x96M\xa7\x9e\
+8\xb6\r\x00\xee\xd9z2J\x05\x00\xde:\xbf\xe1_>\x7f\xd73\'\xb6\xcc-\x0e6\xad]\
+\xfa\xf0\xce\x83\xaf\x9d\xbd\xec\xf5\xb3\x93\x00\xf0\xc4\xb1mO\x1c\xdb\xe6\
+\x1c\xfc\xd8\xad\xfbb\xbf/,\x0f~\xe9\xc5\xbb\x8e^X\x0b\xd0\xc28\xe7o/\xfa\
+\x91G\x8e\xec\xba\x7f\xfb\xd1\xd6S\x99\x1c]xh\xe7\xa1h\xf5\x1f\xday(z0S\xb3\
+\x13O\x1e\xefl\xc1\x9e\xf5g\xbfk\xd7\xc1H\xe4\xdf\xee\xbb\xa33"\x00O\x9f\xb8\
+\xf2\xc4\xd7\'\xfe\xdd\x07\x1fn\xf5\x0111\x00w\xdct\xf5\x7fu\xdf-\x00p\xfc\
+\xd4\xf4\xcf\xfe\xd2\x17_y\xf30f\xef\xab\x8f\xbfxzz\xe6\x9f\xff\xd4\x0f\xb6\
+\x02\x03C\xb4\x0f?\xf6\xc2/\xfc\xea\x1f.,-\x01\xc0\xf4\x8cb\rY\xf8\xf0\x07\
+\xeelM\xcf\x9b\x87\xa6~\xee\x97~\xef\xf8\xe9\xf31iii\xf9\x0f\xbe\xf2\xd4\xe9\
+\xb3\xe7\xff\xd9?\xfet\xdc4\x02:\xe9\xb5\xc8\xa6c)\x00\x90|\x02\xba)\x84)\
+\xce/\xc1\xbe\xd3\x9b\xaf\xdbx\xfe=[\x8eoYsi\xa2Y\\3\xb2\x08\x00\xd1\xe1\x1a\
+\x1d,o\x18\x9bo\xaf\xe3\xda\x1f\x00\xcc-\x8d\xfc\xe6\xcb\xb7>~\xec\x8a\xf6\
+\xf6\xec\xec\xc8\x7f~\xe3\xdab\xb3\xe3\xd4 \x06<\x87\xec\xb8\xf2\xbeuw\x9e<\
+\xbe\xe3\xe8\xc5uQ\xf1<\xb0\xfd\x9d\xdf~\xed\x96\xb9\xa5f|d\xf1\xbe+\x92\xbd\
+\xff\xd2\xc1=3\x0b\x9dvy\xcf\x96\xe3\xd1\xbd\x98[\x1a\x01\x80\xfb\xb6&\xfb\
+\xbdal\xfe\xc2\xe2h4\n7n<\x1b\x93\xee\xbf\xfb\xa6\xb6\xaf\xbf\xfa\xf8\x8b\
+\xaf\xbc\xa9,\xc1>\xf7\xd2\x81\xbfzj\xdf\xc7\x1e|/\x004#\xcan\xe7\xc5\xd9\
+\xb9\xdf\xfb\xb3\xc7[\x04\xe0`\xad\xac4\x83\xc1\xde\xeb\xaej\xaf\xc3\n\x12\
+\x0f\x7f\xf5\xd4KO?\xffF\xeb\x88\xc4P8\xb6D\x0f-z\xf0Mfm\xa7\xdd\xbci]\xe88X\
+e\x18\x1fYZ?6\xef\x1c\x80\x87=\xeb\xa7c\xfc\xd9\xf9\xf18\xfe\x043i&$\xa5\xde\
+\xd6\r\xc5uI\x0f3\x0b\xe3\x8f\x1c\xd9\xf5C{\xbb\xed\x96k\xd6\x9f\xbbs\xf3\
+\xf1\xa7O\\y\xfd\x863\xd7\x04N\x98K\xb8eMr<\xc7G\x96~\xf2=\xcfdj\xd88\xd6MI\
+\x06#\x83\xe8\x97=\xff\xf2[V\xfe\xc7\x9fy\xa5\x05\x01\x80\xb2\x9dxzz\xe6\xcc\
+\xf4\x85\\\x8bh\x18\x1b\x1f\xdd\xb9}3\x00\xcc/,\xbeqPs\xa1\x1c\x80\x87\x97\
+\xdf<L@\xe0\x0c\'\xc0^y\x1e\x80\xbdO89:\xf7\xb3\xf7<\xf1\xe3\xb7\x7f#\x83\
+\x00\x1c\x06\x03\xd80\x96\xe6\x11g\xe7\xc6\t\x07\xab\xb7/\x83]\xde\x87\x0f_=\
+\xb30\xda^\x8f\x8f,\xb5\xc6\xe8\xfe\xedG\xe3h~\xfc\xd8\x8e\x833\x1bb~\x1dsF\
+\x88\xcdq\xe0\xd6\x8c\x8f\x01\xc0\xfc\xc2\xe2\xe1\xa9\xd3JV\x07\x0378==\x83"\
+\xf8\x91\xf0\xb9\xf9\xc5\xb9\xf9\x05\xfd\xa8\xb8\x16\x9a\xc1`\xcd\xd8(\x00\\\
+\x9a[83}\x01\xbc.\xdd7\x0f\x1e\xc3lt\x7f\xe5?t\x95r\xb6\x15\xa9\xedi+\xfb\
+\xfe\xeb_e\x06\xfe\xc9\xa9\x1d\x87f&\xcf\xcf\x8f\x01\xc0\x0f\xdf\xbc\x8fM\
+\x01\x96=\xb4\xcb\xfem\xd84>\xb7\xaeY8\x0b# B\xd5\x91=y8)\xa5\xa4\xd5\x9b\
+\xb7\xceo|\xf8\xf0\xd5\xedF3\x00\xbcg\xcb\x89mkg\xee\xbc<Y\xa5\xaf\x9d\xd8\
+\x16m\x01P\xe32\xb74\xf2\xd6\xf9\r3\xeaN\x04\xc0\xf8\xc8\xd2\xeb\xd3i]\xe1\
+\xd2\xdc<\x00\x8c\x8d6\x13k\xc6\x80\xa9\\\xd7q\xb5y\xe3d\xa1Q(d\x96\xd8Y\xfc\
+\x9a\xf1\xd1\xf1\xf1\xd1n\xc6\xe0\xc9P\xf6\x00\x9b7\xe1%;t\xae\x8f\xd4\x85\
+\x89sU\xd0d\x8eW<\xb0=\x19\xcbgNl\xfb\xdf\x9e\xb9/.\xa5\r\x1c||\xcf~9\x0f|\
+\xf1\xf4\x96\x88\x9bmk/~\xe7Uo\xff\xce\xab7\x00\xd6\xffq\xaf\xd2\xc0\xc1D\
+\xbbvT:\x8b\x12q\xe0\xc1=zdg\x04\xc1\x965\xb3?q\xfbsq\x11\xe9\xad\xf3\x1b\
+\x1e?v%.x\xe0\\\xd2\n\xe3#K\xff\xcfk{-\x9b\x05\x00\x0b\xcb\x1d\x82\x97\x96\
+\x97\x0e\x1f;\xd5^\xdf{\xc7\xf5\xed\xbc\x00\x87\xb6-w\xde|M\xe2\xb0\xeelj&\
+\xcc\xce\xcd\xbfs\xfcL\xbb\xc5|\xc7M\xbb_\x7f\xeb\xa8\x0ff2T\x01\x00\xfe\xee\
+[\xf7 6\xd0Pg]\x98\x1c\x82$\xffV\x1c\xb9\xd3ZW\xacM\xbb\x99\xcf\x9f\xda\x12\
+\'K\x00p\xef\xd6\xa37m\xa2Z\xd19\x00x\xe1\xd4\x96\xd6\xdbj\xc3\x0f\xed\xdd\
+\xf7\xf7\xae\xdf\xbfi\xcd\xd2\xc65\x8bWN\xce\xfe\xf4=\xcf\xfc\xed\xab\x93j\
+\x91\xe3`rt\xe1\xfe\xedG\xc7\x1a\x188\xd80\xbe86R\xb51\xf0\xd2\xd9\xcb\x9f9\
+\xb1-R\xf8\x8e\xab\x0e\xc7e\x8co\x9c\xdc\x8a\xd9\x06\x80\x17Oo\x99\x9a\x9d\
+\x88\xb7\x1f\xdf\xb3\x7f]\xb30\xb30\xde\xfe\x03\x80\xff\xfe\xd6\xe7\x7fx\xef\
+>\x00\x98Y\x18O\x1b\xd6\x1e\x1e\x7f\xb6\xdb\xea\xfc\xee\x0f\xdeu\xe5\x15\x9b\
+\xd1\xe1\xf4\xee\xf7\xc6k\xaf|\xf0\xfe\xdb\x8b\xdc\xe2\x93\x16\x99L\x000?\
+\xbf\xf8\xec\xbe\x03m\xc4G\xbf\xe3\xee\xb1\xb11<8Z\x04\xdc\xb9w\xf7}w\xa2u\
+\x02\xac\xf6\xb99 \xdc\xe2\xbar\xcb\xc6\xd3\xf3c\xd1\x91~`\xfb;\xcf\x9f\xdcr\
+pf\xc3D\xb3x\xdd\x86\xb3?|\xf3>u\x19\xf1\x953\x1b\xbf|\xe8\x9a8.\'G\x17~\xf2\
+=\xcf|\xf2\xba\xd7O]Z\xdb.1}\xf0\xca#\xe7\xe6\xc7\xe2\x94\xc1\x83g\xce\xff\
+\x7f}\xc3+{\xd6O\xb7\xeb\n\xbf\xb2\xef\xf6\x17O_\xce\xeb\x88l\x06e0\xb30\xfe\
+\xd8\xb1\x1dr\xf9oni\xe4\xd1#;Y\xe4\xd4\xec\xe4\x17\xdf\xb8\xf1\xc7o\xffF{{\
+\xcf\xd6\xa9_x\xdf\xdf|\xe5\xed\xdd\'f\xd7n];\xfb\x9dW\x1dn\x97\x10n\xdb|\
+\xea\xd7_\xba\xf5k\'vD\xff\xe3\xe57\x8e<\xfc\xd8\x0b\x0f=p\xc7\xce\xed\x97\
+\xff\xdcO|\xf2W\xff\xd3_\xec{\xed\xed\xf9\xc5E\x00\x18\x1f\x1b\xbd\xe5\xfa\
+\x9d\x9f\xfb\xecG\xf4)\xbb\xc5\x7f\xe64\x03x\xe7\x1d\x00\xfc\xcd\xd7_\xfe\
+\x81\x8f}`\xe7\xf6\xcbo\xb9a\xd7\xff\xf4#\xdf\xf3\x1b_|x\xea\xd4t,v\xe7\xde\
+\xdd\xff\xf3\x8f}"NJ\x01\x80n\xcfevt=\xfe\xcf~\xee\xc0\xc1\x93S;\xa2\xc2\xbf\
+i\xd3\x99\xff\xe3\xdb\x1e?ra\xb2]X5\x8a\xb8\x05\xdf\xfc\xd6\xab\xb7\\\xbf\
+\x91,\xd4\\\xb3\xfe\\,r\xd9\xf8\xa5\x9f\xbb\xf7\xa9_|\xfe\xae??\xd4\x89\xe7\
+\xf9\x93[0\x8d\xe8\xdc\x01\x00\xc0\xedy\xbb`\x9d9h\xc3\xeb\xd3\x97={JY\xbd\
+\xf9\x83\x037\xdc\xbe9\xadg\xb4+\xd9,\xcfm\x9bO~|\xcf\x9b\xcf\x9e\xda\xb6\
+\x18,\xc2\xc5\xd9K\xbf\xf6\xf9\xbf\xdc{\xddU\xedz\xed?\xff\xa9\x1f|\xfa\xf9\
+\xd7\x8f\x9e8\x0b\x00{v^q\xf7\xad{&\xd6\x8e\x1f>v\xaa\xddP\xc8\xf0l5\x84\xc7\
+\x80\x07\x80\xa3\'\xce\xfc\x9b\xdf\xfer\xbb\x12\xf0\xb1\x07\xdf{\xeb\r\xbb\
+\xfe\xfak/\xb5\xeb\x8c{\xaf\xbb\xea\xdb\xdf\xbb\xb7=\x820\xb1\xb6sz\xc48\x8f\
+\xba\xdf\x9a3\x00\x10\xc7P\xe4\xf9\xc2\x1b7\xde\xb6\xf9T\x14g\xbb\xf2\xdf^?z\
+d\xe7\x95\xeb.\xc4\xdb5#K\xd1\xcd9zq\xe2g\x9e~\xffO\xdc\xfe\x1cv*q\xb8l\xfc\
+\xd2\x9e\r\tF\xcf\x9c\xd8\xf2G\x07\xae\x8b\xca#\x86hV&\x9a\xc5\xb1AZ\x85]3\
+\xb2\xe8`\xd9\x07C\xd6\xe2\xe0\xc0\xf9MO\x1c\xdb\xc1\x88<rdg\x14!\xa1\xbc\
+\xdc\xfc\xfcs\xf7M\xcd\xae\x8b\x87Ydx\xf4\xc8\xce_z\xf1.V|\xff\xdb\xc7\xfe\
+\x97\xff\xfbw\xff\xc7\x1f\xfa\xe8\xdd\xb7^\xbbar\xedC\x0f\xdc\x81S\x1f~\xec\
+\x85\x87\x1f{\xe1\x9f\xfd\xe3O3R\xceA\xeb\xe4\x8f\x8fQp \xe9D?W\x02\xe8\xd1\
+\'\xf7\xfd\xab\xff\xf0\xa5\xff\xf6\x07\xfe\xd6\x86\xc9\xb5\xd7]\xbd\x8d-J>\
+\xbbo\xff3\xdf\xdc\xff\xa3\x9fzHm\x85\xa33\xa9T)\xd5C#;n\xba\x1f\xa7\xa5\xd2\
+\x00\x17\x16\xc7\x9e\x9e\xda\x06\xe0\xb6\xae\x9d\x1d\x1d,/\xf9\xc1\xa5\xa5\
+\xe6\x9d\x8b\x93\xbf\xfd\xda\xcd\xbf\xf9\xcam;&.\x8e\x0e\x96\x8f]\x9c<>;\xf1\
+\xf4\xf1m\xfb\xcfm\x8a~\xd0\xcc\xc2\xe8\x7f9\xb6\xeb\xc0\xb9\r\x0e`bt\x11\
+\x00<\xb8KK\xcd\x99\xf95_;\xbe\xed_\xbdp\xd7\x9f\x1d\xbcz\xc9w\x99\x97\xfd\
+\xe0\xa9\xe3\xdbf\x16\xc7\xb7M\\l\x06\xcb\x1e\\[\xd1\xf1\xd9\x89?\x7f\xfb\
+\x9a3s\xe3k\x9a\xe5k\xd6\x9f\x9b_\x1e9vq\xf2\xcc\xdc\xf8\xb3\'\xafx\xf1\xf4V\
+\xda\xcb\x0e\x00\xce\xce\x8d]\xb5\xee\xc2\xf9\x85\xb1\x96\xa5\xb7g\xd6\xff\
+\xc7Wo\xb9\xb0(<\x7f\x07\x000\xbf\xdc<}\xe2\xca\x17Nn^3\xb2\x149\\\xf2\x83\
+\xf3\x0b\xe3/\x9d\xb9\xfcW\xf6\xdd\xf1\x1f^\xbb\xed\xfc\xc2\x1aY\xf6\xe4\xe9\
+s\x8f<\xbe\xef\xc4\xe9s\xde\xc3`\xe0\x00\xdc\xf4\xf9\x8b\xaf\x1ex\xe7w\xfe\
+\xe8\xbf\xfc\xea\xe7\x1f^\xbfn\xcd\xc7?|\x1f\x00|\xf3\xb5CO~\xe3\xb5\xb6C\
+\x06\x83\xc1\xcd\xd7\xefl\x9a\x917\xde:\xfa\xd4\xf3\xaf\xcf\\\xbc$;\xdc\r\
+\x06w\xec\xdd\xdd4#\xaf\xee?\xf2WO\xed\xbb4\xb7\x10Y\xf5\xe0_~\xe3\xf0\x0b\
+\xaf\x1e\xf4\x00\x97m\\\xe7\x9c\xf3\x00s\xf3\x0bS\xa7\xa6\xbf\xf8\xa5\xc7\
+\xff\xc5o\xfe\xc9\xa5\xb9\xf9[n\xd8uf\xfa\xc2c_\x7f\xf9\xd0;\xa7\xb8\x13 \
+\x10\x90\x9eU\tW\xee\xee\x8f\xfdS\x0b\x04\xb1\xe4\xb6\xb53\xbb\'\xcf\x01\xc0\
+\x85\xc5\xd1\x833\x1bZ\x07\n\x9f\xf6\\\xf2\xae\xddp\x93Fh\xc7\xc4\xc5-kf\xd7\
+5\x0b\x17\x16GO^Z\xcb\xf6\x1bqX7\x06\xd7o8\xd3\xee,\xb7\x15\xb5\'\x0f\x00`t\
+\xb0\x14\xd7\xe0\x96\xbc\x9b_r\xea\xa9\xf0\xc8\xd2\xe8`iayD9\x87\xa8u\xcd\
+\xb653-\x87\x00p\xe2\xd2\xc4[\x17.\xf3\xd6.r\xbb\xe3\xef\x9c\x03\xe7\x06n\
+\xe3\xe4\xc4\xa6\r\xeb\xe6\xe6\x17N\x9d9\xbf\xb0\xb8\x04\x00\xdf\xf9\xfe\xdb\
+\xfe\xaf\x9f\xfc\xfb\x00\xf0[\xbf\xff\xe8\xbf\xfd\xdd\xafD&\xc7F\x9b\x91f\
+\xb0\xbc\xb4|i~A\xbe\xec\xa1\xa5<6\xda\x8c\x8e6\x8b\x0bKs1O\x9a\xd2wW\xdb\
+\xb7n\xda\xbeu\xd3\xe6\x8d\x93\xa7\xa7g\x0e\xbc}\xfc\xdc\x85K\x0e`\xa4\x19\
+\x8c\x8d6\xcd`07\xbfH\x96#y=Q\xfa\xc4!\x00\xf3\t$z\xa0ejv\x92\xf9\xd8\x00\
+\x10\xbb\x98<\x1b$\x9c\x91\xa3\x17\xd6v{\x01\xa5Z.\xcc\xc3\xf3\'/\xa3\x0f\
+\xa1v\x17\x0b\xcb#\x0b\xcb)\xd6z.`n\xa9{\xd2~n\xd9\xd8\x1d\xd5\x0e\xeaL]\x9a\
+\x9c\xba4I2I\x03\x8a\x96\xff\xe2\t\xc4\xb3\xe7/\x9c=\x7f\x013\x1c\x0fs\xb6\
+\xdb\xbbqSfn~\x01\xe6\x01\x0c\xf4\xc7<s\xf1d\nW\xe1\xddl\xf3\xd8\x89\xb3\xc7\
+N\x9c\x85x\xd8\xd8\x01\x80[ZZ\x9e]\x9aG1V\xb3\xcd0\xc8\xe51\x96\xa8V\x18<\
+\x83\xa8\xe7\xa9#\x03\x88K\xef\xe1\xbd\x17<(\x8f|\xd4/J\xd64J\x9bV]\xb5\xed\
+\xf2O\xfe\x9d\x07&\xd6\x8e\xab\x15m\xdf\xba\xe9\xbb?x\x17\x00\x1c?5\xfd\xe6!\
+2U\xa9\x9a\x16\x16X\xd6\x8b{\x90#\x9e\xa6VT\x9a;OP\x19r\x0f\xf5\x99\x8f*\xda\
+\xb7\x0e\x96:m\xec\x94\xd2rkq\xf5V\xa3\x95\x80\x88ON\xac\xfd\x91O=\xf4\xb1\
+\x07\xdf{\xf7\xad{~\xed\xf3\x7fy\xe8\xe8\xc9\xe5\xe5e\x00\xf0\xe0\x07np\xf5\
+\x95[~\xea\xbf\xfbD\xbb\xbf\xf0\xd5\xc7_<\xf4\xce\xc9\xd4(mj\x9e\xad\xb3b\
+\xdf$\xd2q\x1e\xc0\xd1\x0b\\\x05ZW\xb2;\xaa\xf7d\xa66\x0c\xfd6\x1a\x0f\xc1e\
+\xf4A\xed\x87\xa4x\xa4\xb3\xf4\x04x\x97\xb4\x1aj,\xd6\xf5\xfe\xbbnl7\x87\xda\
+\xc3#\x7f\xfd\xf5W^y\xf3\xc8\xe9\xb3\xe77oZ\xbfk\xc7\xe5\x0f\xde\x7f{\xbbH\
+\xf0\xd2\xebo\x7f\xfeO\xfef\xd9\xa7\xe9\x8c\xf3\x1a\x97tuW\xae-b((\x8f\xf8\
+\xa1\xe5\xc8\xb8\x9c\x1c\x8e\xcac\xc2\xa6\xc6\xed\xea\xf0\xce\x83ww\x7f\xcf?\
+%l\xf5\xef8\xe5\x056\xd9\x17\x18ps\x80\xd2\x13\x1f\xdd\x1f\x0e\x02\x87\xdf\
+\x0f\x85\xec4f\x83\xd3SCV\x7f(\x0f\x99\x83\xdb\xb4a\xdd\'?r\xff\xf7>tofE\xe8\
+\xd9}\xfb\xff\xcf_\xf9\xc3\xb7\x8e\x1c\xb72\xe8\xc7\xfd,\xaf<\xbb\x19HI\xc5W\
+:D\x18\xe0\xbef$\x89\x7f\x08i\x8a\x08f\xbf\xb4\x9d+\xfb\xa5\xf2\xcdX`A"\'\
+\x06\xa7\x80\x80\xf9\x83\xd1Q\x1f\xe2\x1d\x00F\t\x17\x02\xe6\xa3\xdb\xf1spi~\
+\xe1\x99o\xee\x7f\xe6\x9b\xfb\xcf\xcd\\\\\xf6~dd0:\xda\x8c\x8c\x0c\xe6\x17\
+\x16O\x9d=\xff\xdcKo}\xe1O\x1f\xfb\xe5\xdf\xfd\xca\xf1S\xd3\x00\xfa\xb0Kms"I\
+\xae\xef\x82\x16\xc9-ar\x0f\x1dK\xe5C\xcd\xb1\x04\x1c\x1a\x9e\x07\xd3\xa9{[\
+\x00\xa7\xdd\xfa\x07^Kb\x81hlO0k\xd80\x86\x00\x96T\xc6\xa5\r\x18&{V$*\xe7\
+\xd7\xf6\xbf\xf3\xda\xfew6\xae\x9f\xd8\xb4a\xdde\x1b\xbb\xed\xb43\xd3\x17\
+\x8e\x9f\x9aN\x0f~[N\x9aS\xaek\x1f\xd7\xa9\x81:1\x04\x19\xb7\xab\xbdO1\x15/\
+\xa9\x08\xb7\xf2\xbdQEOp\x15\x9fV\xab\t\x1c\x07\xcc-\xa8@\x80\x9ch\xe1\x18\
+\x17\x0c\xf1\xf4\xf9\x8b\xd3\xe7/f\xd4~-\xc3\xe0\x80b\x0e?\xdd%#Y`\x9eA\xfa\
+\xb5ZmH\xa3\x87cX\x1eg\xf4Y%\xdb\xf6\xbf\xcb.=\xab\xa8\xa8\x1d\xd8+\x92\xd0\
+\xd0\x8f\xb7\xcau\xbeR\xa8\xa8\xd7 Wq\xd2"e\xed\xb6\x9a2\x8f\xac\xf3\xf9B{I\
+\xdc\x06m5\xbb\xa5_\xf3J\x8e\x1c\x9f}\x0b\xba\xf0\xd8>\x19\x8b+|\x83\x11&\
+\x85j\xe2Mc\xfd\xce\xf4\x01?\x0cQ\xd9\xb2\x0c\xfe\xccG\xcby|M7\xc6-\xc7L\x0e\
+\x85ZZ=\xae{]\x8dU7\xbd/\x92\xc8L\x9c\xf3\xf1\x069\xed)\xc1\x15b\x97\xb9`\
+\x12\x1c\xdd"`e\x7f\xe1!\xd8\x1f\xcdz-^\\;\x92\xb3\xea$\x8b\xc7\x180\xcc\x81\
+\xda\x89x\xce\x9a\xdb\x0b\x97\x13\x1e\x0f\x1ce\xd2M\xe9\xb2jS \x10\x93\xc3R\
+\xa8\xc9\xa9\xe4Q}\xef\xca\x1ai\xb7\x90[\xe9aT\x84\x1e\xaa\x94\x0e.\xea(\xe8\
+\xddNv\x11\xcc\xa7\x92\xfb27\xf4\xc8\xc3\xac\x89P\x14\xe50\xaf\x92\xc8\x86\
+\xec\n\xbc\xac\x1e\xf2\x99\x87~P?W\xcaQY\xe2\x14\xdf1\x9f?\xae\xd2\xfe\xe0\
+\xe2\t\x04\xd6\xaco\x85\x07\xe5"\x87:KF\xaeB\xa5h\x8a%q`\x8e\x03\x933*x\xcd\
+\x10\xe8<\x90;\xbd\xae\xbe8`f\x1b\xb3\x84\x89\x1a\x86\xc2~1\x16]\x1dJ\'=\xbd\
+w\xce5@\xc5/9\xe6\x07&\xb3o(\xc4\xef0\x10\xa4x\x9bTn\xd3\xdb\x02\x92Tz\xd8\
+\x02\xb2\ti\xcf\xb2\xa2\xba^%\x88\x9b\xdc\x80\x17u\x05\xa5-\xf9IA\xed96OA\
+\x8f\x9dc\xe7\xa0\xc3\x01\'@Q@\x85\xde(\x08`*A\xbe\xa2\x98\xbd\xafv\xd8\xc0M\
+\xb0\x9e\xa9\xea\x88~ \xd1\x0f(\xfd\x94?*\xdf\xe1WE\xb1\x9a\xb9T\x97\xb1C\
+\xd8\xdb\xdd\xcb\xd9\x82\x80\x00\xb9\x02\x94\x1eM\xef\xaa\xc9\xbf\xa4vE\xb6_\
+\x1b\xf6\xc6\x1d\xdf(6\xde\x158\\\xb0\x14\x00\xda\xa6\x17\xd5y\x9e\xd3\x83\
+\xf9\xd4wol10yE\x90\xea<P\x99\xb2\x96\x97r\x94\x1c\xc8\'\xc8\x10Pe\x9f}Ye\
+\xc1\x10\xaeD\x8a\xf9\xae7\x0b\x15&5\\=\xb0\x8a4\x1c\xe0[\xbe\xd2\xd7\xb7\
+\x81r%\xa7\\\xa2\xdf\x80\xcc\xe4\xa7\xcf"\xaa\xd6=\xd3\x9e\xe1\x14\xc3\xd03\
+\xb1\xea\x8c\xea\xe2\xabR\x97t\xb8\xf8u\x8c\xa2o\x7f\xc1\x0b\x00\x92r\x9d\
+\x1fg\xf1\x1c\xefSY\xcd\x8d\xa8\xa5\x83\x93\x8c\xc7\x8e\x1bU\x90^\x00C{\x19}\
+\xae\x14\tb\xcd\xa40j\xa9K\x98\xc9\xa9\x86!feF-\x8eC d\x96\x0b\x00\xa6\'\x9f\
+\x89$\xa5MC^\x0e\x95 \xb3G\xac\xf6nc\xa1\x9a\x9c8\xe4#\xd1\xc0\xbdI9;\xa0\
+\xd3\xbf\xe2;\xd0S\xb9\x92\x07J6\xb7h\xa5\xe6,\xb7\xe4\x9f\x84\xe24\x05yyV\
+\xebz\x87\x1a1\xb3<\x8e\xc6\xdbU\x9b\x82\xc7\x90\xd5\x9f@2\xba\x92\xb2\x11:=\
+\xb7\xaa\xa1\xbc\xe5\xb0\xbb\xc8,\xb08yU\x15\x8a+$\xfd\x95J\x98M\x19^\xa4Zc\
+\xa6\x96U\xdbS\x95\xb3\x12a\x9e\x8c\xafO\xa1\xcc\xf1B\xbe\xbd\xcc\xa3\x05\
+\x05^\x07\x8a\xf4\xfci\xc7r\x17\xe7\x11`\xbc.ZY\x1e(\xee\x0e\xa8\xe2\xc9\xb0\
+\xa7\xbaxq\xf5\xad\x8bc\xde\xa2\xd0\x07C\xae7\xa0e\x15\xd0PR\x98%\x1a\x9dV\
+\xdet\xa0\x05\x9b8\x01\xcc\xed\xffzq%&Y\xb2;\x92)\xc1-\xa9\xec\xab\xd2\xc2>\
+\xcd+\x1c{^T\xc4\x07\xb1\xa6$\xd5\xa8\xa7\xa1\xa3cN\xf74\xbd~[\\=d\x8b?R\x1f\
+\x13n{MA\xb3\xa1]1\xa4U\x06-(\xbb\x05U\xed\xe3yP\x89\x86\xdaa\xa1:\x14\xaaD\
+\xd1e\xee\xbcSO\xfd\xac\'\x85%\x17\xb3\xc7\xf3\xfe/\x08\xf1\x18\xcaC\xab\xb9\
+ .\xce\xd5\x10D\xb4\x167\xfa\xd0\xa7\xb6\xde\xf6.\\J\xc6r2\\\x01\x9b3\xa1\
+\xf3;\xe7q\xa89\xb7\xacZ\x983\x1c2*79\x13\x96kfT+l4t\xe7><\xc5\x01r\xec+\x0f\
++\xe4\xfd\x1buM\xb0\xc8v\x13K(\x1d%T\x81\xe2\x1bdh\xe7\x83a\x18\x90\xebPE\
+\xd6\xda=\xc2Qj5\xc5\x90\x96\xd9\xf5~\xe7\x92vb8P\xf5\xa1M2c&Oi\th\x16]\xf5\
+\x94$\x11Pjl\xe3\xa9<\x19+z5\xf4Z:O}\x83\x94\xdcp\x1b\xc4\x16\x02\xc8\xcc\
+\x9e\x9a\xb4z\xe2l\x07\x88T\x19\x84\xef\xc4\xb0\x81\x90\xdaA\x00M2-\x06Tg\
+\xb3r7r\xb8\xd34\rs\x05".Qw%VT%\xe1!\xedc\x13\\w\xb6\x02\xbdF\\\x9b\xfe\xad\
+\x16\x02(\t|\xe9\x80\xe1\xc0\x0e\xba+\xe0\xb5\xb6$R\xa8\x05\x08\x05iL\x11C\
+\x80\xdc\xcc\x98\xb7\xbf\xe0\x98\xb0\x87~\x1f\x7fK\x87?\x86\xe6Q\x0f@l\xbe\
+\xce\xa5\x8f\x05\xd8\x9f\x94\x8e\xfa\xaer\x01\xa0/\x02\xf0c\x11\xcc\'\x95k\
+\xc6\xb5C?x\xca\xfa\xd9=\xcf@\x86S\x1c\x89qX\xf6\xf1O\xba\xe9^G.\xf2\xabL\r}\
+D%\xffE/\x00\x18\xf8\xf0\xa4\xa3\x8fk\x04\xf1_l\x1a\xe0\xeb\xee_\xb8\xd2\xfe\
+\xc4\xf2\x92g\xe4\xfc\xb7\xffVe\tE\xaaA\xb2\x0f\x14\xfe\xd5L[<zx4^p\x9a\xd0%\
+\x8bJ\x91\xa2p\xe1"V\x8e\xe2\x19<+\x83\xe50y\xef\xadg\x84j\x06\xd5 \x88\x8e8\
+A\x816\xfe\xe3c\xc3\xbd\x82\x0b\x1f\x8b\xd6\x88\xd4\xda\x82\x03)\xce\x15Z\
+\x06\xc5I\xa8}\x8d`bI\xda\xee\x04\x81\n:N\xb9\xc2\xfc\xf0\xcc\xd6?\xc4? \xe9\
+\xb2\xe7A\x1c\re\xf6\xd2\xdb\xcb\x90`\xf1P\xc0\x89)\x8bWD\x1d4(N\xaf\x02Eq\
+\x93\xb72\xe8_p[\x05-#h\xa2@\xe4Kf\x05\xae\x02\x1f\xd8\x99\xa8\xd0R\x14\n\
+\x10\xe4M8\x0c*!\x1f|\x08\xed\xed@\xaf:\xaa\x07\xd5\x14\xc4[Q\x08\xc7\xf7\
+\x15BR\xc2+8\xba\xa2\xac\xb8\xad\x16\x0e0\xcd\xce\xd7\x14yT\xd1\x0b-\xc2\x06\
+\x11>\xca\xc0\xfe)l$\x83b\xea3&\xe3b\x08\x9a\x00sO\x0c\x82\xe7\xb2\xa7\xed\
+\xd1\xa3\xaa\xfb\x9d\xa9\x1c+S/A2\x1c\x10(;\xd2\x895\xd4l7-VbpA\x13\xc5| \\\
+\x13\xfdQ[;\xf3r,\xb5\xaf?:@N\x8dx\xef\xfd@\xb3v\x8cq\xaef\xb9\xdf\xa0z\x11\
+\xd9 -N|%\tW\x06C\x8dc\xa6\x03\x14\xa3\x0e\x00}q\xa0C\x07\xf1i\x8f\x13\xa1\
+\x9d\xec*\x84\x1f\xc0\xb4\x02W\x00\x08\x07E\'\xc0R\x0f\xd4\'`\n=\xc4K\xb7Q\
+\x81\x82r\xa93\x92\x8b\xf4\x1e\xfc\xf0FAi\xa4\xc0\xb4\x1a,\xd5J"\xa9\x10\xbd\
+\x88\x11\xdc(\x08\xd0\xf9\xa0\xf0"\xcc\x084X\xc8H\xc4\x0c4\xf4:T\xe2\x05\xa3\
+\xddc\x8f\x16\tR.8\xaa\xca6ckL\x1d(\x87\xd6%\xbf\xccQZ\xe1\x1c\x811\x19X\\\
+\x11\x1d\xf4z\x18o3\xc8T\xa3&\xff\xe8d\x90w\x9ap\x7f\xd9)\x85\xdal\xe1\xd3g\
+\xdc\xbf\x96O\x91[\xa1\xe1\xf26\x1bb\xeaN)p\xd9)i[\xd9\x0b\x1cd\x8e\xaa\xfa\
+\xec4\x97\x95c\x98\xcbnf\xae\x04\x01>\x9c\xed\xf7N\xab\x02\r\x06f\x93\x8c\
+\xd0\xcd%<\x00y\'u\x87\r1\xa0\x11h\x88\xaf\x03\x00h\xa1I\x99\xc8\xd8\xe70\
+\x1aJ\'*9\xc1\xa6:\xb8\x91uP\x8ceH\x1b5\x99\x00\x00 \x00IDAT\xec\xa4\xd0/tQ6\
++\x06\xa6-\xbch\xb6R$0\xba\xda\xcf\xa6uA#\xc9\xe1\xee\xc5E\x96\x90\xe3\xb1\
+\xe8\x11s\x9c\xe6q6{F\xcd4D\xb8!\xef\xdf\xd0\xa0\xd0\x10\xa4:\x9d\xfb\xf4\
+\xf5w\xb6\x8cJ\xc4d\x8d<O\xc6\x85\xf3`\xcb\x88=\xd5B\x94\xc1pC\x17\x91\xca,\
+\xc7\x82\xecVOQ\xab\xaa\x99\xd6(\xf8\xa4\xe0d\x83$\xe7<\x1b\x15\xb6\x13\xb1D\
+CH\xe5\xc7\x0c\x01\x9a\xe2e\xe0\xc2\xde\xe6A\xcd\x81\xde\xd1\xb83<\x93\xbc\
+\xd6,\x12\x82\xd1\x8c\\\xb7\xa2m\x1dZ\x80\x8cF\xf0\xe9c\xb9`\xcf\x820\x9b\
+\xdd/{\xe9\x84\x9e\xd7^\xae\xee\x85\xb6V\xdfu\xaf\x890a\xc0y\xb4\x80 \x1d\
+\xafx\xd2\x9dZ\x01\xbe\x1f\xe6E\x8b\xa8\x81\x88\xe68]#\x1c\xc4\'\x90\xf4\x90\
+\x04Op@\xb3x\x02@^\x06\xb0\xfa\xe8,D\x1c>\x80\xa0\xaa\xd9\xd7\x0e\x07\x0e\
+\xf4\xf7\x11\xa9\xc8 \xe6 \xe3\xb3\x15\x03k7O$\xfa\xd1\t\x17\xa1\xe4\x03\x90\
+,\x9a\x02\x082\xf6\\\x17Y\x9e` \x81\xdc\x02\xe1+\x00\x1a\x00\xb1\x97\x82O@\
+\x9c\x81T\xa9&S\xa1\x1a\x81\x8e<\xa6\xfe\x13\xe1\x16\x10>\xee\xa0\xf1\xde\
+\x89\x14\xb1Q\x88\xfa@4\xb2WP6\xacC\'Zs]\xca\x93\x02&\x84\x8f\x88\xd6\x12\
+\xe8\x92\x06L\xba_Q\x0fq\xef\x11\x84z\x08\x1e\xb6d\xd5\xdc5\x15V\x9e\x01\xa8\
+\xd1|\xc1$>\x00\xaa\x00\xb8L\x8d\x16s\x0f9R\n\xf8t\xda\x93ul$E(\xd0\x87\x1c\
+\xb0f\xab\x0c\xc3\xe1&q"\x1f\xf9N@O\xceA\x8b\x03E\xa5\x9b\\\xb1\x1b\x14A\xf7\
+\x9d\x01;\x17.<\x03E\x7fT\xf8r\xabA[\x15S\x9b,\xf8\xdb_,/\x9f\x08R\xb5\xa0N\
+\x1e\xba\x0civ\x90(z\'\xb0DW#:\xb4b\x8f\x0b\xafw:hmDa\xf3\xb4\xbf\xf4U\x90%a\
+g\x8bv8\xc0\xf5\x1a\xe2\x11\x1c*\x8f\xdc\xfa\x84yd\x16\x84\xfdO8\x08\xbcj\
+\x95\x08C\x83\xe2\x1bbj\xf5\xc1\xcd4\x0fz!\x06@\xfbPj\x9c9+u\xcbYSZ|\x8a\xe9\
+\\\xed\x00\x12\x86\xeey\xcb\x05%\xa9\x89m\x04\x98\x90\x15\xf9\x18?\x90\xc7\
+\x87\x0b\xfa+\x15D\x12\x93\xfc8\xf4?\xb9\xec4\x81\xee\x02\x96\xc0HkP\xec v\
+\x17@\xcc\x0e:\x00R\xf3\x84\x18n#:\xa3\x0e-\xa3\x98\x1c\x0b\xfa3*\xc8\xb08$}\
+\xc0~G\xc8]\x9a\x81\x95\x16\x94\x10\xc7\xf5\x91y\xf3\x91[\x86\xf2Qy\xb7\x19\
+\x9c\xa3\xf6=\x8d\xf8\xae\xbd\xcc#\xa4\xeeBZ\x0c\x8c:4\xaa\x04O\x95=U\x1a6\
+\xeb\xa16\xe2.\x0c<@\xfc\xd7\xfdz\xf0\xd0\xf9K\x1eR2\xca\x06\xc8\xa3\no8W\
+\x1c\x0bT&\x11\t[G\xe8\x16@\xfc\x89\xec\x84\xb6e\xd6\x96\xd8n\x13\xdf|\x92\
+\xac\xc9\xaeA\xbdS\xeb@di\x1a\x89\x18\x01a#\xc0E\x05\xe0\xc2/\x82\x86\x8b\
+\x11)\xc1\x01\xca\xe3\x80\xe4\xce\x07#3\xd9@J}\xef\xc9=\xce\x13\xc5\xd8F\xc5\
+n\xf78\x07\xb1\x01\x1c\x05D\xfc\x12\x00\x95\xca\xaer\xf4G\x18\x19m)TV\x81\
+\x87\x92\xda\xe8r9\x8e\x00l\x03\\\xf8_\x12M\x82g\xba\x83\xf0W\x8f\x03P\x1a\
+\xd5\x04Nc\x97\xc5\x13\xb1\xe1\xde \xd5\xf9d\xdd\xb7\xab=uN4\x1b\xc1\x96\x19\
+\x88\xd5K~\x90\x13\x9e8\xe45\xb0\xce\x9bwhb\xd9\xf2\x08\xd1\x86!\x9a\xba\xa5\
+W\xab\x92>G]p\xf4\xca\xa9\x92\xcaSV\xdc\x00\xdc\xe7\xcce\xe8L\x83\xe5\x0c\
+\xca \xdfO\xc0\x07\xa3z\xfc\xb5\x15g\xf0\xcd<\x92],\xc6P@\xcd}p\xa3\x82\xdc\
+\x83\x97A\xf8\xe7\xbd\x9e<\x83\xfc\xaeR`\x8e\x96U\xec\xa5\xde5\x8a\x13j\xd6f\
+\x05\'.\xa8\x02\xc8\x0cgd\x02Y\xa6(p4=\xe4\x13\x83\x04\tVE\xee@o\x03H\x11$&"\
+O\x8a\xd7\x8fG*\x12?\x9f\xf9{\xde\xe1\t$Q\x19\xe0u#\xd7M\x1c\t\xaa\x80\xd44\
+\xdcH\xc4L\x14\x8f\xeb\xe7k\xb0\x94\x01\x8ft<:"\xc0\xcc\x84a\xa3J\xcb\xa5s\
+\xeaAC{\xb5\x80\xd4\xc1\xa9\xbc\x88\xf5\x00\x00\x83\xe0tI\x0b\xd9\xd9\xff\
+\xf6\xa0\x06ZWc\x96;)\xdd\xe0\xf3u\xff\x92k\x89\x08zTY\xf2\x0e\x02\x1d/o\xb5\
+\xf6\x90\x05\x83\xcaq\xaay\x00U\x07\xf14E\t`\xc1%\xd8\xfe8\xdc\x1d\xcd\xe9\
+\xe8\xff\xc8%$\xd6\x88k\n\x97.\xc2\x7f@\xfd\xc6\xbe\x01\xf7\xdb \xf9x\xf6\
+\xbf\x04\x08J!\xdez\x0e\x80(\xc8\xe4\x93\xf9\xa8r\xa2oH\xe4\x1d/4\x1c(\x8d\
+\xf0\x04\nX\x96\x82{\x89\xed\xca~Rr:vG$\x9cw\xfd4:\xc468\xe3\x9a\xde\n\x1c\
+\xa8,\xd6\xe0"\xf4r0\x07)V\x81zZ\x1bLJ\x99\xac\x16(\n\xa93Z\xd2\x9ap>\xbcsq\
+\xf5\xa8[\x7f\xc7v\xc1r\x0b\xd0}\xc1R\xa8\xb5GM\nb\xc3\xa9\x14\xac\xba\xb0^\
+\xc7\xc3Z\xd8\x86\x1a\x01E\xe5\xcf\xab\x08\x16?\xac\xba\xc6E\xf8\xe0\x1ct\
+\x1e0\x80\xe9\x06\x88\xc8\xc6@\x00\xbdN\x8bH\x1e\xa2\'b\x0b\x18\x89\x1f\xcd5\
+\x04\xba\x02~|\x87\x83xb\x0b\xef[\xb7\xff\xa3\xbdG\xadY\x88\x8c\x1d\xc8\xbb\
+\xed\x87;uR,$\x05\x9e\x8f\xcf\x91\x0c\xae`\x1b\x02j\xc9|\xc6#\x0f!.\xc4\x07\
+\x14\x10\xc7\x81\x06&\xb68E\xcc\x87Vq8$\x1dL\t+\xaaTK\x10-\xf0\x99\x82\\#\
+\x8e8\xc0\xdb\xb3\xe4\x08\x03Jj\xc7\x82\xd6y\x96\x81\x97_\xc6\x91\x88\xa9\
+\x9c+\xa2\xf5\x1an\x0bRm*\x13\xc4@\xe4r\xea\x82Cv"\xcd\x0c\x85F \x8c\xd2)d&\
+\xa8\xbb\x88\x00\ty4\x8eS\x8d\x1e<2\x01\xb1}\xde\xfb\xae\xe1\xcc\xcc\xf8\xf8\
+\x8bp`\rs6\x01\xf1\xc0z_\xec5\xb3\xe0\x80\x7f\xb6\x11cUZ\x04\xc9\x08\xe7\xcb\
+4\xc6j\xed\xc1E\xb0\xb2W\x08)C;M\xaat\x81#ub\xd7\xc2w\x11U_\x98A\x81\x883\
+\xf9\x8a\xc1\x04t\x99\\\xdc\x05f\x87\xa6<\xfe\xa5\xba?%"\x06\xd0\xfc\x13\xa9\
+\x07\xb6\xbb\xa8\x9eRB[\xcff\xd0p\x80\xb9\xc5C\x1f\xa5T\x99ua\x06\xb04\xc8\
+\xd3\xf9\x9c\xa5\x1a\xda]^\x17\x8f\xb8Q/\xa2\xb4\xcd\x9cH9\xe6\x18\xea\xb9\
+\xc3\x08\xe2/\xb1\xf5\x80\xdb\x12\x1f\xb3\xee\xd0\xe0}\xba&\x84\xd2\x1e\x1b\
+\xb52\xedj\x12v\xf5|\xd4\x1b$Sw\x9e\xcb\xd8eF\xfcE{\xc55?\xd5%\t\x07@\xc7\
+\xec\x10\x08\xe0i\t\n\xaa\xed\x901\x19\x81%\xbf0\x85\x14AV\x8e\x0c\xfd\xae\
+\x87\xb0N@g\\z\xb0::-\x0c\xc4\x03c>L\xaf\xd0J\x03\xe5\x1b\xcd\xbf\xd0J\x03\
+\xaa&\xcd/Q6\x9f.\xbc\xc5\x91\xc9$\x8f\xc3\x9eo\x15\x99\x1e\xdbK\nx\x88c\xac\
+\xe8\x1c\x9dJ!)i\xab8oD\xda\'\xf4\xb4\xd1\xc0@{\xa0%\xae $$\xf8 \xc6.A.3\x90\
+r\xf8\'Zpm\xc5\x08]\xf4\xc1\x01(P\xc8\xe1\xc0\xf1\x8b\x1eK2\x9dtR~\x8f\xfe\'\
+\xe9\x12\x0fe\xd2*20\n\x02\xaf\xb6ramQ_k\x9b5!\xe2Yh\x99J\xb4=>\x87\xcb=\x00\
+3\x04;\xc0<8\xa4\xc8\x0b\x04\n!\xcd\xab\xb0\tX\x01M\x07D\xa2\xf4\xc7\xe3N`95\
+J\x16\x1eY\xb27\n85\x89L\xe6\xe8\xe9\xa4Anph\xc1\xd3 S9\x152\x8e\x91\x82\x0f\
+\xc7\x108\x05\x92\x8d,\x1a\xcaa\xab*\x03\xb9|i\xb6\x05\x9d\x89\x88,w\xb6\xcb\
+(\xa5$d\xa49\\\xe8\xb5\x0c\xcc\xec\x0e\x03\x8c\xb5\xb6H\xf5\x1c1\x07+y1\x00\
+\x0fB\xd3Z8P\x8a\x86l(\xbfW \xb0J\xec1\xab\x05F?\x90l10\xf7\x7fu\x025\xee\
+\xd4\x0005\x80n\xb8\xed\x8f\xa2\xb7p\x10o\x13\x08V\x13\x016\xbb]\x8c\xa2\xa4\
+2\x14*\xea\xf2\xfc\xa2\x9c;\xeb*\x16&\x96m\x08\x08\xd0!`\x88\xa96\x84!.\xbcK\
+\xaf\x93\xe6\xd1\x88\x06\xc5AroC|\xb3\xea\xb2G\xdc\xf1\xa9w\xa5\xcdM\x8e\x80\
+\xb1~\x84\x86H\x99^n\x81\xd8\x98C\xa2\xfa\xf5\x87N\x8b!\xfa0-\x97\xc5 \x9e\
+\xce\xed&}\x89\x1c&\x8d\xb2DWZ\xd1\x03\x1e\xff\xe6\xc2*\xcf\x0e\xec\xa7\x0c\
+\xcd\x08:\x89\xe6\x8e\xc2\xea#\xb4\xef\xdeqO\xea\xe1\x8ff;b\xf5\xcc\x913Q\
+\xc2\x12\x92o\xe4\xa3\xeb\x93\xe23\xce\nQ\x06)*\x84\xd5\x04\x81\xbd4\xeaM\
+\xfd*K(B\xa2\xa8\x10\x94\xd0\xf9=q|??\n\xe3\xcb\x81\x14.\xb8\xa3P\x0ex0\xf6W\
+\xff\x98#\x83\xb6\x8c\xf3j\x87 r\xf1O\xb8\xd7\x88\xaf\xf6:\x01\x0b\xfa\xb0s\
+\x92\x19f\xe5\xacV\xd5t\xab\x0e\x08\x86\x14\xab\xb3E}R\xd1\xcaL\x9a9NIQ-\xd7\
+\x9b\x15nD\t?(6\r\xaeb\xc7\xf0>\xc7}\xac\x7f\xf9\xa4W(l\xe0"\x0fZ\xe4s8\x9fO\
+y*\x9d\x87\x0cK\xc6\xbd\\\x12F\x95[!\xeed\x07\xe6\xe4bG=\xc3\xaa\xd6a\x87\
+\x0eLf\xa4j\x1aV\xe3\x102\xef\xa6&p\x0e\x01$\xdfG\xf9f8-W\x95\xff\xde\xb1\
+\xc1\xc9\xf1\xd5\xdf\xe2\x99\x14s\xbc%%\x10\x07}f\xde[\x19\x9cq]\xc8\x9a\xc9\
+\x91\xcf\xb6\n \xc8\xcc/L/\x11[N\x90\xd7\xc6\xe2\x87M\xc8\x0c\x8e\x0b;#\xef\
+\x92J\xab\x1du\xf9\x81\\i\x17\x12/\x99\xcc\xc3\xadR\x08\xca\xef\xb2O\x80\x83\
+*1]\x8cF7Z\x98\xaa\x1beQ\xfc\xea\xfb\xbdz\x9d5\xb2<\x7fq\xa1\xf8\xbd\nc\xd8.\
+!\xdd"\x0f\xa6\xa9jU\x19\xea\xb6\x93\xa2\x86w\x13\x04^\x19\xe86o\xb2\xbf\xf2\
+ND\xa4\x9b!\xc9=\x00&\xfb\xe2;\x80\xd9\xe8\xc7\x1b\x9b\x95K\x98\xab\xbb\x94h\
+\x11\xd6U>B\x94gyiX\x1d\x10\x0c\xbb\xe2\xd4{\x1d\x86\xf7\x7f\x14\x8f\xdc#\
+\xd0\x10\xd0\xaf\x1a|\x1e\xc9\x13((8\x88\xe5<\xb9h\xab/\xd4\xd6\xbb\xf3\x107\
+\xae\xfbWa\xfaIx\xb7\xd6\td\x90\xad#\x1a\x8e\xae\x90\x8b\\\xa2\xb4\xb12\x82\
+\xf6\x81$!\x87\x11\xd0\xf7\xa5\xbf\xf8\xa9\x8bN\xf6\x85\xd5\x0e\xe6\x1e\x1a\
+\x1d\xd0e\xf0\xb4X\xcc\xd0\xb2\xaa4\xc7\xe3?\x1c\xf4\n\x0e<\xcb(\x07\x9d\x03\
+`S\xc4\xe5\xe5e\x96g0\xe8\x8d\x12z\x82[}\xa6\\;\xc6Eid@\xedX6\xdf\xc5\xfa\
+\xecC\xec9&AC\x1db!n7\x9b\xeb\xd8\x99\x8a,/7\x9a\xc7^\x0b\x08\xa4,\x0e\xae%%\
+\x0e\x19\x1b\x98E\xaf>p\xe0\x06\xcb(\xf4cEe/3\xc8\x92cF~\xb2\xe4\x8a\x89r\
+\xe6\x9cXa\x8c\xf5\xb6Y4;\xdf^b\x88QG\xad\xa6\x0e\xb0)\xc3j\xc3{\x12m\xf2a\
+\x86N\x11p\x98\nz1_\x0c\xab\xbfw`\x98\xde4/1\x98\xd5=\x04da\xb1\x1d\x8e\xe7\
+\x8e\x92-\x88\x89t\x13\xbb\xa2\x07k&c\x98\xa0E\x92\x0e>sz,\xe5\x82<\x1b\xb5B\
+\x96M\x16\x0c\x01\xb5\xa4n\xe1\xa3\xbd*\x80\xa0R=\x98>\x97\\+r\xf46rE\x8b\
+\xf1\x08\xca\xb8\xa7(\x00\x94Hp\xd0\x9e|D\xe7K\xca\x0cg\x83r\x88&\x0f2VINE\
+\xc7*\xa87\xe0\xe9\x1f\x95,\xbb\x97\xfbD\xf1^\x8e$\x0fP\xa3\tV\xc5L\x80&}\
+\xe2q\xeb\xfd\xa9E\x12\x0b\xc0\xfb\'\xe5\xf2\xf2\xaaB1\xe8b3B\xe8JJV\x81\xb9\
+^\xb2l\x18\x93I\xe1!L\n\xec t\xad6Z\xda\xd0\x81\xa0\xaf\xdbLj\xeb1\xaa\xa2\
+\xaf\xc5\xe4O\xf94\x0b\xf3\x8aT\x08\xe0\xc9\x02V\t\xc1so\x93<\xa3\xb2Z\x81\
+\xf9y\xab\xb4N AV\x1f\xe2|\x04\xf7\xb3\x8f\x89\x0e\xdc\x8a|\x82\xc2W\x16\x94\
+\x8d\x03t\xe0\x18\xb4\xde\xd7\x14&R]\xa6\xb84J\x02\n\xa1~\x85N\x06\x07FR\xf5\
+\xd6\x05\xa5\xa4:\x89\xc9OPhj\x0b\x00\x8e\xfe\x8bT\xa8\xb2\xa7\xd5\x98:s%\
+\x8eaA\x01\x88d!_d\xa1\xe3\x1f\xd5\x93E%X\xd7\xd3\x0e\xf2\xa9\'\x91\xd7\xc3\
+\xbd\xc5\x80\x03\xae\xf6L)\xa4\xc8\n\x9dg\xafz\x18Q\xd8\xcd\x110\x19\xe6PS\n\
+\x15.H[\xc9\x004\x7f\x07\x87!\x96\n\x00\x84\xa7\x8bdO\x1fF\x90\x05c\xae\x0cq\
+%\xc6\x07\x91\x935>\xe9-\x02\xc1A_#\xa8L\xc0X\x1a\x01\xa6X\xdc\xd5V\x05\x84\
+\xde\xf32Z\xab[$\x0b\xa2\x96\x7f\xc9(\x11\x01K\xa4\x0f\x89\x00\x1e<\xbf\xd55\
+\xb2\x84\xae\xed+\xaa\xe8\xb1iX8\x08w\xab\xea\x17(S\xf1\x14\x1d/\xb3\x12\xf5\
+\x8a\xf1X\xd5\x807T\x87\x97q\xa1\xe3\xd43[\xea\xa5Y\x81\xa6\xcf\xe2u\x7f=\
+\x99\xc7A1\xf0]\xc7\xf2\x9a\xa6>\xd1e9\xe3Z%\x01\xb0\xa7\x17!^\xe3\x15\xe7\
+\x1e&t\x8eaf\'-??4q\xd0k\xaf\xc6\\\x92Om\xcb85\xb4^\x03\x1c\xef\xde\x882\xc6\
+<\xcb\xd02\xc1\xc0\x9c\xc13N\x95\x17\x083"\x91\x8e\x9b\xac7\x928_\xa9\xb6\
+\xcf=\x8d\x94\xef|\xfd\x1c\'7e\x9ef\x05\xda\xe3$\xb79\x00yAu\t\xa1\x18\xf8k.\
+\xf2\xc8bO\x03\xa2b\xa5^!\x82M\xde\r\xd6\x12\x19\x1f\xb6\x8fn\x8b\xa1\x00\
+\x82\xa1}\x02\xaay\xbb }}^\xa4\x18\xf0\x84(W\x8a\x03~EAN\xd0\xf4<\xe5j\xa8\
+\x1fI\xae\xb5\xc2\xcc\xc7\x11\nQ\xf1\xb5\xb4x\xe9\xad\xd2\xfaVs\xef\x80+\x83\
+\xe1\xde\x0bd\x11\x0f\x7f\x88\x06\xc1\x8a\xa2\xa0y,\x93\x9a\x1eG\xcc3P\xef\
+\x13\xd4\x87\xdcx\xc6#\xde6\t"O\xfe\xb0\x8b\xce\xf3\xbbs\x9e\xa0\xdd{\xc5uV\
+\xf4\x98\x92Y\xee\x91K\xf3\x90b\xca\xaa\x04M\x07\x94x\x1d\x07|!\x9e\'\xf1\
+\xcdQJ7\xcbL\x99W$_}Y]\xec30\xfaxm\xb0\xfd\xe7\xe4)\x8e\x1c\x08V4?$=j(\xd0\
+\xe1\xc6\x92\x89\x03\x88n&\xef\x10\x84\x90<\x0e\xfa\x06\x84\xd2\xd8\xb7t\xd5\
+\xab\n\x9cJ\x90c^\xa4J\xd7\x86\xad\xc0\x98mbC\xeb[v\xc6\x10\x805\x85b\xa0\
+\xb0\xd2\xc6TB\x108Y\x1a\xaa\xeem\xe1\x8cxt\xc9,Z\xf8\xa5+\xe0\xe21o\xeb\t\
+\xa8J~\x98\x9c5\xb3\xdf\x07I\x96g\xc0C\xe0u\x95A\xc0\xa6\t\x08\x97b\x84V\xb0\
+\x18;\\\xd7\xb4C\x8d0=h\x07\xd3\xd4\xd9\x14\x08@\x82\xe9\x14f\x9e\xbc\xb7X\
+\xf7\n\x9c\x05@t\xd7F\xc6I\x96\x84K\xd8^\x9b X\x89-@\xaf/\xc2\xa0`8H\x8f\x04\
+S\x1e3V\x02\x9b\xbf\xfc\xd8\xaf\x1a\x90\xbd\xd7\t\x1d\xb9\x08\xb0\x18\xda\
+\xaa\xc9\x7f]\x8a5\xfc\xb3\x0e\xb0\xe1\xd0\x14\xb9s\x06\x08Ve\xb5\x18I_1Z\
+\x19\x9e\xf0\x8f\xa4\xaa\xfb\xcaJ\xb6\xd5\x0cD\r\xd8f\x8c\xad`\xc8<u\x13ID\
+\x02\xc1\x01\xdd\x0b\xf0Q\x14\x05MUS\xd3j,\x16\xe5\x83\x8e\x83\x0cG\xda+5Ju\
+\x0c\xe9\xd3A\xb5\x01w\xe1D\x1c\x89T\xd0\x9a!\xe7\x92h\x94x\xa3`\xd6\xa7\xca\
+\x07R\x9fV\t\xe6\xe6]\x7f\x02I\x99wu\x11\xd5\x8f\xe533 \x1dB\x96I\x92\x18\
+\xce[S\xf8\xa2 \x8d\xe3\xda\xd9\x85\xcc\xb1o\xca^\xda9\xad\x07\xad\x86\x8b\
+\xca+\xec\xc1\xb7\xf014\x93\x07}\xf4\x13\xd5\x1aO\x06i\x8e3U\x06\x9a\x8bG\
+\xc8U\xeb\x19s~\x95\x10\x10D\xacK\xd4eoQ5\x86D9UG\xb3\xaf\xc0\xe4Qt\xe8 X\
+\xads\x85$HG\x90\x06\x97OF\x84\xcc\xf6\xa7\rX\xdfQ\xaa\xd4\x01\xb5\x86!]tO\\\
+\x07(T\xb8_,\x93\x98\x18\x02v\x99\xa4\xf0\xc1\x98\x16\x94\xb9\xd6kC8\xf8\x96\
+j\x02sz\x0e\xc8\x1f\xccug\xed\xbc0\x93\t=\x96\x1a\xa3\\\x8c\'\xeb\x01m79\x87\
+_\xf6\xe2\x80\xdc\xe1W]\x10(\x10\x99\xd7\t\x8a\xbb6\x86\x1b\xe1J\x84\x1dC\r\
+\xc7\x99,\xf5\xad\x00\x01v\x0f[>\x14\xe6\xa3\x8a3{\xcc\xd0\xf3\xcaE\xd9\x07-\
+\xbd\xc6&f\x13\x8f\xb2\x13\x04\x94h\xe4s\xd0\xe9a\x1e)\xbau\x97*\xa8R\xf3\
+\x91\xc9\x86\xd7\xdfT\xb2J\x07\x8a\xf4\xda\xe5\xf7LP\x12t\xfd\xc1^\x82\xcd\
+\x03r\xc9T\x87\xc2C\xfc\x82F\xc1\x1b\x90j (\x00`\x9e \x99\x1d\xf6^\x19\xec8\
+\x82j9\x89\xe0x_D\xa8\xf6s\x0e\xb8\x03\xe2\xa1i\xe5\xdd:\x01\xef\xa2\xec\xb3\
+\x9f%\xe9\xc4\x85_\x17\x97\x01\x8b\x08\xea#\xb9\x89\x80\xa0\x923\x04N\x13?!n\
+\xac`R\x86\xd4\xd0a\x00\xe9)\xa1\xd7%L\x98\xe8\x1d\xce\xc3\xbc(\xa2\xfc\x1d\
+\x80\xe7\x7fx\x9ep\xd9i\x82wO\xfc<\xf8\xee\x13\xdb\x1d[\x8ew\x06\xfe\xf6\t\
+\xa0/-\xf2\x8e\x1dz<\x95\x10\xc0\xc6>\xaa\xb0B\xfc\x89\xb1\xa0\xc5D\x81a|z2\
+\xe6\xb9y\xca\xd3\x15\xfaC\x99\x89|\xeb\x1cC\xfc\xf2\xe0\xe4\x1ddKh\xbe\xb2C\
+\x83\x00\xc9\xc5\xe1b\x80f\x8d\xd9\xb5)\x1b\x01,[\xdd\x012\x12U\xb4\xf1\x9a\
+\xf8\xb8\x91\xe7\xdc8\xf1/^i{l\xe97\xdb\x17\xab\xf0\xf6\xb2!CF_\xd3\xe0\xe4\
+\xa5\xeb\xfe\xaa2\x13\x95\xa8D\xc3\x80\x8dr\xa7\x08P\xcdA\x9a"\x08\xce\xcc\
+\x18\x1a\xab\\\x15\x96z\xa26\x8fYs4\xd8\xb8\xaf\xdcb\xfd\xffq\xb1\x08\xb1\
+\xa60n\x07&\xf9\xe1\xfd\xact\xb6\x16\xcf\x0c\xbbq/\xcc\x0f\x9f\x140\xc3\xcd\
+\xc7p\x0f\x848\x94!\xdb\x9a\x94\xee \xacN8\xd4!\x943\xb6\xa4\x14\n+\x954\xd2\
+_\xab9\x84\x9f>\x1b\xb5\x82\x13\xfbH\r`OA\xc9\x06\xc8\xc6\x16\xc6>U\xa1TRZ)"\
+}F\x02\xa7\x81\x89\x00B\xc8\x93t\x0c\xd4\x0c\xcb\x0e\xd8\xd7\xec\x18\xc9D\
+\x8d\xbbHl\xe4\xbb\xf4\x15]\xcc\x97 \xce\xbcE\xc5\x1c\xc8\x0f\xc8\xc9\x0c\
+\x99\xd4\xd5-\x15\x1b@\x01\x82\xee\x8a\xc6\x84ITc\x8cM\x05q\xb1\x18\xa9L\x13\
+\x80F\x04y\x99\x08Q\xd9K\xe5\x8c\xcc\xb8F\xe7E\x82C\xcbn\xc4,$;B?\x95\'5\xad\
+\xee\x130\x1cd\xe4\x17\xdf\x05\x94\xcf\xa0\xa7\xe0\xbb8/ \xe2\x8a\xadK\xb1h9\
+A\xf9\x8e*\x15\xdd\xcaB\x1c\xfa\x04\x01\xca\xbb1#gN\x89\xafy\x9d\xa7\x95$\
+\xe7F\x15mr\xdd[h\x10?a\xfc#*\xf8\x03\xeb\r\xb8\xf0\x8d3\xfc\xa2\xa1\x9e\x03\
+\xd7\x82B\xff\'8\xbd\xf1Z O\xbf\x8e(p\xa0S\x8d?96\xbc\xef\xbe2\xc9\xd4\x7fD\
+\x00Q\x08\x9cet\xd3s\xd1F\r\x08Q\x848\xd57`\xdc%\xb9\xba\xf0\x11A\xc4\x9af\n\
+\x1c\x00@#\xc1\xed\x1d\xff\xf4]\xa8\xa2\xed\xf9!\x06\xbd\x96m\x88q\x1a\xd7\
+\x0e\xa4\xe4=\x918\xd6\x07C\x06\xe9\x07\x84x)\x1b\xaf\xdc9V\xc4\x96")\xac\
+\xda\x87\nf#\xe5T>.*D\x00\xe0O\xab\xb6Y\xdaR\x9aO\xd0\xe2\xc0\xaa\x86*\x8c\
+\xdeA\xfa\xa1\xac\xc3\xf2\xad\x8f\x9a\xc2\xa7\xfe\xd2>\xae\xcd\x8cJ05\x92x\
+\xeb\x9d\xda\x0b\x9a\x04Z\x8c\x86\xd2\xf7\xc0p\xa7\xdc\x88\x0f \x03A@\xb0\
+\xe2\xa5\xa9#\xaaNf\x95\xc6\xa0\xfb\x83\xafc\xeeF\xd545\xe6k\xa8\x95/\xe5\
+\xb5\x15l\x0c\xd0\xaa\xe9HK!\xe7\r\xd2\xb1\x8a+\xc7l\x94F\x9an\x97\x9cJ<\x01\
+\xc5\x0b=\x90\x1c\x0b\xdct\xac\xa5U\xbe]\x1e\x07\xb1\x84\xa7W\x14\x96\x1e\
+\xe7sQ?\xc4\x0f\x9e\x87iz\xc3\xb1\xca*\xd6e\x1e|\x08\xf9\xc8Q\xd5#\xaa+\xf7\
+\xd90\xe5LJ\xdb\xce\xaa\x97\x0f\x06\xff\xc2\xca\x1c\xfd\x84,\x0fN\xee\x12Q\
+\x04\xb0\x02\x045"\x0f\xf7\xe7\x14\x1aH\xa6\xd2U\xe0\x90@F\x80\xb8\x07\x8dcJ\
+\x89\xc2\x00Y\x01Z\xbf\xf7\xaazU\xfc\t>x\x8cU\xbeJX\xa8C<k\xfe\xb9\x12\xaf>?\
+\x92\xcdg\x9bx\xae\xd9u\x18\xf88\x84s\xd5\xd0\x11\x0bBT\x105<\xf3"\xbcsx\xd9\
+\rC\xa0\x8b\x89>\x02\xb4>\x81\x81RT^\xe4\xf1\x8e}\n\xdd\x0e\xe4\x1d\xa3\xc5u\
+^\xb9\x1e\xa0\xb3\x85\xa2\x15\x1b\x9d\x0fd\xcb\xa0\x8d\xc9\x1b\x08\x1dy\x91E\
+%\x19\x8b\x1f8\x02\x9c\xe5|\xb7\x91Z\x12\x91\x1d\xad\xdf!\x7f\x8f\xe6\x0f\
+\xf3\x03\x8fb0\xd31\x7f# \x1a(#\x8aH\xdc\xc8ip\xb4\xfd\xe1\xf9C\x05\x17\xb9w\
+\xcd\xa6\xa9B\xb7\x80\xe8\x9d6H\x0cC\x8fT\xb4\x13eX\x1d\xed\x15G\x00\xceoo\
+\x13\xb2\x81\x81F;\x19\xa8*\xbfZ\xd3\x1d\xf2\xb0\xdb\x1f\x8f3;\xde\xaf\x06#\
+\x89o/\xc1\x18\xcd\x04\x85\x82"\x9f\xc6T\xcbi\x16\xd0\x89\x9b\xbb\xbe1\xae\
+\xbd\x8b\xbaAC2^\xe1a\xfd.\x82\x99\x97\x14\x13\xfa\nE\xd2q\'H\xf0g\xcarj\x00\
+\xa1LI\x11H\xf2\x1e\'\xa9b\xd6\xca\xe3\x91)D\xa9\xb2\xc5\xfbX\xb6 \xca\xde!\
+\x8fP\r>h\x02\xa2\xdb\xc9\x88\xf0.\xfd/[\x11\xca\xa5\\\x0e<Qv9\x1bL\xd2\xa8\
+\x15\xc8\x96r\x91Im\xc1\xb7\xaa\xb6\x84\x00R\xbdc\xffU\xdb\x98T\x85G\x08\xc8\
+\xe1=\xa5S{\x8d\x90\xd1\x91d\x8by\xb25\xe6Ch\xae\xfd\x1a\xb4\xa2\x00\xf0}\
+\x13k\xa6L`C\xeb\x81k\xfet\x95\xec\x82\'8@\xe7\xc38o\x9a"NT-u\x1cK\x15\x10\
+\x90\x95Y\xda9D\x08\xd0\n\x0b\xf9\xa7\x81]t,s\xe9d\xbc\xd3I\xa31R\xb1\xe10+r\
+\x9aM\xf2\x01D.\xac\x89Z\xaa \xad\x13P\x1d\x1a)\x05\x86\xb9\xe4C\xbe\xa4\x06\
+\xe2\x94\x81\x1a\x10\xc1qw\xdbc\x84%\xb9u\x8e\xb6\xc3BSww\x0c\x1a\x88\x17\
+\x8e\x00\x02.R \x99\x04\x16U\xa8M\xebQe\x0e\x19\xd3\xdb\xf5~\xa9\x06\xcb\xd6\
+\x044\xff4\xed\xff\x04\xe89\xa6\xf1C\xc6\x86\xf5\x1e\xe7 JRH\x1e(B\x82\x15\
+\x80x\x0f\xdabzF?\n)Qx&a\xa1T*HT>\x14%D\xc5w\x8f\xa8\x0e@\xea\x05[s\x87\xffr\
+&1\xb5"\xac\x13\x9dr\x90S\x07e\x82\x88\xe0\xc1\xa8v\x06\xc4\x85o5\x88\xd7fa_\
+\x91;\x86\x92\xc3\xae"bVR\x1c\xa4+\xe7|\x07G\xe4F\xaa\n\x08KM\x18\x9f\x94\
+\x05\xba\x86\xb4?\xdd\r\xd1\x01\\!\xe0:\x9cu\xa3s\xc3\x11@P\xe4\xc8T_\x8er\
+\x85\xbc\xc8T\x08\xe5\xb5Z\xdeM\xb1\x06M\xcf;\x05Ff\xa0\xcb\xc6D_\x08:\xc4F0\
+~Z\xab\x90\xacCB\x030\xbf\x05\x8dF\xea=c\x92\x04"\x8c\x01u\xf42\x02\x02\x01\
+\xd2\x1b\xc0\x95Hg\x10\xd9\x17\xea\n\x94\x11`\xc8\xb1\x80\x8ad\xb3-\xf5i\xe8\
+\x01\xa8\x97\xb8\xb2\xfa\xd0-\x16IU\xe0U#\xa4\xf1-\x8a\xc5\xffI\x0e\'\xd68\
+\x88\xcd4T9\x16.\xefA.5P\x84\x81+bb\xa3\xe2Wi\x12E\xe4D\xbfc\xf2 \xc4$[U\x13\
+tY\xe2\x01_\x1f(\x9c\xc4t\x92./5\x9c}\xcf\xef\n\x8da&\x019\x87\xd2~\x04\x16\
+\xa8\rp\xa9\x1a\x96-\xc4(\xf2\xe5\xd0Ur$\xf1\xcb\xa5!M\r\xe8\xe5\xc3\x85\xa6\
+\xf1\x8b*\x1f\xebN\x0f`L\x98\n\x04$\n\xe8\xc2\x1d\xbeA8B\xa7;\xbc\xcc\xde\
+\xe6\xec\xe0\xd0\x00.,\x97\x06\xd5z4%m\x9a\x04\x99Sv:6v\x8eEsi\x01\xc9\xab\t\
+(\rO\x1d\x01\x1a\x17xN\xe8\xe2\xaf\x8a\x00\xbc\xdc\xac\xb7\x8fkl,<\xaf\xc4\
+\x0b*\x86\x19\x8e\r\xa1\xe9^^\x92w\x06Q\xad\xe0\xf0\xbaD\x1b\xdb\x98\x16%\
+\x88\\\x82\xc2\x91t\x1a\x9fT\x01\xab8]fl\xa6H\xc5\x136l\xa6\x13\xcdN$\xd2\
+\xed\xef\xa2\x85\x86\xe1\xdb\x99\t\x0b\xc8r`+ \xcd\x8dr\x95\x99\r\xe3\x0c.\
+\xdeU\xe8\xf6\x94\xd9D\x9bJ\xc5\xcb\x99@\xb6\x12\xe5YD\xa7B\xcbfQ\xc4\x9b\
+\x03\x05\xc5\xe7\x81@rQ\x13]R\xa7|\xf4czXC(\x16\x80\xd7\xab!\x80\xfeT\x05!o{\
+\xc4i\xdc\xe4\xed\x88\x1c\xc0\xe2\xed\xbb(\xaf\x9cF8\x00\xef:s`(\x03B\xa8\
+\xb2\xe1\x86\xee\xad*\xaf\xa8^,/v\x91\xa6\xf3\xd4\xfb\x93\x08p\xd4@ \xaa\xc2\
+\x11\xc48L\xe8\xa9n\x01\x00\xc4\x91\xeb\x85\xdeVW|\xa8\xb5\xd0+\xaa\xf5\'\
+\x04\xdc\xf4\tG\x10wg\x14\\\xd4\x04a\xc1\xcfc\x8f\xae\x8f?\xdaRecY\xa4g\xa8*\
+}\xad\xc8\x89\xabw\xdd\x01\xc4LT \x80\x1a#dl"y\xd1\x88|[\xba\xc0\xfb\x91\x8b\
+\x99\xa9\xdc\xb0G\xc0\xe1\x83 %q`\x0f\xe0\xfc\xf3G\x08\xe8\xc6chU\xd2\xd7-\
+\x13\x1d\xb4\x1am\xa7\x94T\xc1\xa3 \x00\xfd\x90\x89\x9ef\xfe\xdbH\xe6j\x9aZ@\
+B-\xfcU\x1a\xa2\xb5\xcd\x92\x866\xa24\xb7;9u|\xe3\xa5\xf7\xa4\x82U!\x0f\xbd\
+\xd1\xa0\x80\xa0\xef\xf0WG\xbe\xa9\x0c\xd2:c;\xc4\x10{\xcc(\x03\xa8rbkM\x16\
+\x02\xd8\xd6\x80\xa4\'t\x80\xf4\x12\xad\xb9iP\x02\xc2YVZ\x1b\xfe\xa8;\x81)\
+\x9b\xe7\xb7rm%\x13X-U\xd9Q\x88 (|\xc7\x84\xfe\x89\xb4\x84\xf5E\xd7$\x8a,\
+\x19\xe0\x82T\xe2\x9c\x9a\xc3\xe6\xbe\x80\x80\x94B\xcbH\x84!Q;\x12\x85U\x8c\
+\x18\x88E\xcf@\xa8\x02)\x1b\t\x05\xab\xdfQN\xdc]\x12\x19|.(\xa2\xf3q\x00\x01\
+\x04\\\x9f+\xb93~\xb9\xd4\xe2"\x89\x125\xc6=h\xa2\x92\xfa\xdfr\x03\xbb\xb1\
+\xeb\xb0\xd4\xb9!\x01$\xcf\xa0\x08\x1c\xba\n\xd4\x83\xcd\xe6\x9a\x19\xf7\x81\
+S\x0e\x82j}\xa7\xbc\xc3\xaf\xf0U\xef\xa4\x1dINR\x1132\xf6\xa4\x80\xf3\xa5\
+\xc86\xf3h\xba\xb4\xf4\xe2\x12\x80\xf4m\xf8e#\xc9i\xa7\x9f8\xa8\xec\xe1Z\xa5\
+\x00l\x048\xce\xa21\xfa\x05\x02z\x87\x16-\xfa\x18\x12y\xcd<\x01X.\xe5t\xb5\
+\x1f\x83\xd7\x08U0\x93\xa6\x886\xf3X^\x8aB0L\x02K\xc86\x01\x0fV\xe6\xa7a\xa1\
+\xc8\x13\x82\xa8\x16\xfc\\i,/\xb2I\x04\x98\xcb\xc3\xbc\xd7-\xff\\\xda;\xfd\
+\xf8Fq=\xa9G\xba,\x90y\x8a\xb8\x08\x83\x8c&H\xc6[\xd5\xdc1\x93\x96\xae\t\xbd\
+\x08\x03\xa1\xc0\xd9f\xa0\x8b;\x81\x82\x1a\x7f\xb2XPK2\xc5\xbe_r\r\xf3\xfb\
+\x00a jS\x7f\xbd5\x15\x8f\x8e\x98e\xf3<twCP61\xd2\x04\xf5\xe5\x90)"\x8f+\xca\
+\xce\xe3L\xab\x1e\x9d\xa9\xed\xd9\xc0q4\x97i\x02$\xb5\x90H]\x04\x05\x01\xf2|\
+@T&L\xd7\x14\x0c\xb5\xe3\x8cc>\x86\x94y\xa0]\xa8=\x93O\xba\x1c\x9a\x11`\x1dN\
+\xd2\x85&\x88\x18p\xd82Q\x8b@\xf3\x93\x1et<7\xbaW\xb437\x195N\x00E\x80E[ \
+\x80 \x01\xa9~\xbeFe\x9d\x0f\xe8\xa3\xa1\r5\x90\x04a%\xeb\x95`E\x04\xfa\x88&\
+{\x86\xb95\xa2\xc0\x01\xce\xd0\xa0\x18\xe7\xc2S\xe9x\x17\x88\x8ekU\x1f\xe4\
+\xc4\x9f"\x8d^t\xfc\x0f\x11r\x8c\x91\x0f\x8c\xf2yAN\x07\x90#!\x0e\xfd\xd5y\
+\x12\xf1\x04\x08z\xcf\xf6\x01I9\x88\xb5\xc5\x84\x03V#[\x1eP\xd6\x88\x8a\x01\
+\x9f6\x06\xf4`L\xac\xc0\x89\xde\x05\xd2`Er\xa4\x02\xcdZ\xd0\x15"\xaa\x0cT\'X\
+9\x10@\xd7\x82\x86C@Qn\xba\xfa\xeb\'\xee\xe2\x8b_B(9\x1c\xe2\xf1>/\xe2k\xde\
+\xd9\xc6\xa8\xb4>A\x90K0\x01Q\x0f\x00R\xa1\\\x19\x00\x93\x1e\x80\x94\xb42\
+\xc8e^JJ\xfa\x01\xaa3\xa8\xae\x06\x1a\x08\xa0\xea\xac\x1a\x01\x94C\xa6\x01*\
+\xc0\xa3>\x1aV\xf4\x1bj}\x03\x00e\x92\x99\xf9\xf0Ca!\xd0x]\r\xbdS\xb6`\x8c\
+\x02,]\xa2\x810\x16rs\x81\xb0\xfaT\x9e\x80\xf3D#\xa9\x06\xd7\xe6\x9b\x82\x7f\
+\x87o\xc4%\xf2\x9a\x87\x0f\x08L\x16\x19g\xde\xd8\x81\xad\x08\xe8\xb7\x19\xaa\
+\xca\x8a!g$\x8d:S\xf6@:[\xd4#\xa0\x83\x9e\x91\xe7xC\x85\xd2\xb0\xa5Y8\xc4\
+\x84E\xa0\xfa\x84!\x80d\x10\r\x92H\x8d?\x04\x10\xb5\xca \xb0\x8c-\xb7\x8d\
+\xa4,o"\xa8`*!@\x0bD\x13\x04\x8b\x10\x19\xe2\xe3\xc8\xe2\x90#@A\x8bSo\xd5y J\
+\xd5\x11@\xd4\x80\xddi\xd4~\xe5\xf87\x17\x82bb\xca\x93\xa1"\xeaO/\x94s|0\xa2\
+\xb3\xc5\xf5b\'L\x99\xfein\xa1\xca\x01\x92o\x88\x8d+\x86\x80V\x060\xaf\\\xb9\
+k\x1c\xd3\x91h\x01\x81\x95q\xfc\x86LC\xb55\xc1\x9e\x81\xb0\xc3\r\x84\x11\xe2\
+f\x01\xebH\x84\x83\x9e< \x1c\xb0W\xc2\xf5\xb2,m\x7f\xa8\x0ef/`\xa6\xcc\x88T\
+\x83\x98\x89P\xc0C\x88H>k\xa4c\x01\x85\x9f\x9c\x1b\xa6.\x06\x1b\xb5(\xf1B0\
+\x18\xc7xy\x90\xa2[\x92\xc2q\xb2\xb3e\xea\x90\xd8\xc4\xa1\x12\x05\xc6\x89\
+\x06\x8f(\x84\xe7~\xa4{\xa8\xcd\x1e\xd9NDX\'\x00\x04\x05m(;\xfa\x87\xdf\xa4\
+\xa2R\x81\xcb\x06\x90d\x1d\x01*\x1e\x84\xc2h\xdb\x9b^1C$\x1d\xd1A\x17\x858QR\
+Y\xcc$\x1f\xaa\x16+\x86+\x84\x82\x00@\x85C\xc0\xf4\x01\xfde\xde\x00\xa7M\xe3\
+\xc9"t\x83\xb5}\xd7\xa7\xba\xde\xd7\x10`\xb9\x08\xb5F\xb3\x16\x01|~\x02\xa6f\
+\xb6%-!\xc2K\xa8*\x17\x1d\xf9\x02\xf2\xc2\xd2\x0e\x03\xc3\xda\xf4>\x81\x9dBp\
+N\xb3\x0b\xb9\xa5b\x11Am\x1bs\x0c\x89\x90\xd1\xa5\xf9g\xc8@6\xeb\x94JA"C\x86\
+B\xff\x17T\x90\xcaN\xe9\x88\x87\x80\xc2\xf0\xc1\x81|\xe5\xc3*\x04\xbd\x01jl\
+\x8c\x0c\x8f\xa1\xa9\x965^\x1a\xe2/z\x7fz\x10Z\x9d\xd5g\x96\x93\x99z\x8dCKyT\
+S`\x07A\x83u\x90\xaf\x97\x10UD\xc5\x81\x07\xb1\xd0\xd1\xfd\x02-\xee\xe9\x9fB\
+\xa0\xb6M\x7f\x99e\x1e\x03\x90\x15\x7f\xde\x07\xc4\xc53c\xbd\xf2\xe5\xa8\x99\
+\xa0\xbb\x07Y\xd4T\xbe\x9d\x13\xcf *\xa8v\xd2J\x7fL\xba\x00}\xce\x15\xeaf\
+\xc1\x84\x81a\x1a\x1c@\xfa\xde\x81Z\xa9\xa3nb\xb8\x1a\x12\x01<\'\xcd\xba\x92\
+1Q \x13\xbe\xdd\r\xd4\xa1\xee\xd8\xb0\xc8x\x9aS\x8d\xc1Q\xd9\x96+FTs\xe1\x12\
+\xb9\xe1\xbb\x83j\x87\xf0\xcf\xe7HrM\x80\xb9\xadT\x00\xf5n`\xa4\x90\x1f\xe8\
+\xca\xab$\xf4|"&\x8dO\xc7sx\x00\xc7\x8f\x0cVC\xd6:\xb3]\xb4F+\x81\xb6I\x1c\
+\x0f\xff*\x05S\n\xf2\xcb\'\x8e(\x00\xe0\x08p\x8e\xb8\xa9z?*\xc7@J\x08\xe8\
+\xa5\xfe\x9d\x91?T2\x84\xe7\x96\xb3\x05R\x95*\x99-\x972\x8d\xef\xbc\xc5\xe6c\
+\x95*-\xa3\x9f-b\xc5\xdaH\x1f\xf2ecp\\\xf2\xe1\xd7aV\xca\x86\xdf\x18\xcdE\
+\x04(\xb3A#g\xca\x86\xd6\'L\xcf\xcf\xa5\x021\x94\x9c\x80\xe83\xe73\xc4\\>\
+\xc4\x85\x82\xdcq3J\xb3\xb4\xd2\xd8ESYE\x07\xa4\xa8j5\x94@\x80d\xaf#\xa0\x1c\
+\xb4\xc5\x1cs1\x98V,\xe2\x14i\x97\xf3P%0\x84>\xa0\xc1\x1cL\xf4\xb0\x9ftn<\
+\xb9\xc3\t\\>m\xaf\xa7\xc3\xd8\x84\x1c\xf5>\xf5@`\xa0na\x0b\x16\x19=\x9f^kK\
+\xbd\x16<4k:\xd2Z\xfc\xf7\xf4\xd6\xe2\x83\x11\x83Bf}\xf5\x1a/\x1bfBr\xc2s\
+\xdaLA\x80\x82\xa9$R\xd5?!y\xaa\xc7\xa5\xca\x1c;s\x9e\xa3\xd6\xf1\xde\x01"g~\
+Bh\x18\x02p\xdd|\xe5X\x19x\xaa\x9f8\xcc\xd0\xc7\xf93\xaf2U\xc5\xaf\x9c-\xa1\
+\x81\x8f\xa8\xae\x9b\x8b\x13\xbb\x00\x17\xcd\'`\xca\x80I\xae\n\x01\xb4~]Z\
+\x88u\xebA6:\xee\xb3N\xa2V/\xb0\x17Wu0@?5\xfesY\xe1\xf7\x0f\x12\x07\x96z\xd0\
+\x8e\x11D"\xe0\xc4\xffi\xbd\xcf\x80\x06\xa5\x81\xa0\x90\xe7\xd7\xd0\x05\xc6\
+\x90\x8d\xa2\xea@V?\x8b\xa8\xd6(d\x0f\x9b\xf3GA\xca\xa6\x88\t\x01\x15\x07\
+\xf0\x88\t0\xdf\n\x03\x14\x9aY\xb2\xe1\xad{\\\x1f\xf0\xed\x8c47\xcf\x1b+\x0f\
+\xe00\x16 =\xe8)\x14{q\xbaW\x08JyC`(g\xc9\x857\x08\xa3\xb4*\x00\x85\xa6\x1bI\
+\xca\xc71\xeb\x9d\x00t\x17\x84\xa4\x16u\xe8_E\xd0\xcfe\n\x95\x8f\xe7\x04h\
+\xae*\xe6Y\xf4R|\x9f\xd7W\x8c\xc4Z\xee\xc9\xe8.g,\xe7+\xd6G=\xcclM\xe0\xd1t\
+\x95$\x05MP#\x1ek3v\x95L\x00\'\x9b\xe5\t\xa5\xe2\xc3\xc4\x00q\xe4h#\xb3\xcb\
+\x87\x15\x04\x0f\xc5^\xc5UR\x8eR\xf9\x8c!\x10\xf5P\xae\r\xab\xa4R\xa0$\x1c\
+\x9f\x98\xd8\xb5\xf2v\xf3\xd7\xda\xf6\n\xa6\xfb&:\xa9\xd7\x87Tk\x8f\x15a\xa7\
+%\xdf\x00"\xd9\xf85V\xe0\xfb\x81U\x00\xc8! R\xa9G\x00r\xe4\xc8\xb4\x8e\xcd\
+\x06\x14\xfbe\xfb\x92\xda\xab\x89\xcc\xec\x80^k[\x82\x81\xba\xf5W\x1d\xaa\
+\x0f\xdfGn\x046\xd1\x08\xe1KC\x19U\xe8\xa4#\x0f\x00\xe1 \x0e\xdb\x07\x12\x85\
+i\x13lf)\x97\xd9\x86\xf2\xa3\x85\x8aF\x08$\xd3%\x93xv\x06\xe0\xc9\x8fZ\x0b/\
+\xdc\x94\xc5\x9f\xaan\x87^;\x9a:B\x85]\x80\xa4$=z\xdbr\x88d/\xda4\xd6\x12\
+\x86F^[\xfa\x03\x915\x00\x00 \x00IDAT18:\xb7b>\xa2<"\xa0\xd5\x96G\x00\x16J\
+\xee\x99s\xea,H\x7f\xa5\x18\xb06\xe7\xfac\x05\xceE8cHT\x8fw|\x04u!\xad\xe9\
+\xaa\xd3U\x1e\xe1\xf8\xb5-M\xf9l\x83$b\x94\x94Q\xf4\xfb#\xa1\xe7\xbc\x12\x03\
+\xa0\x7f\x10\xd1\xf2oI-\x80\xa1\xd3\xd6kIB\xac\x14""\xca\xa5R\x8dR\x86\x94\
+\xc5\xa6\xac\n\x0fH\xe5\xa03\x86\x99\xc0\x1e\x0bW2\xc8\x08\xb5k\x93b\x00\x81\
+$q\xac\xb9\x8c3%\xa4\xce\xa0\x1e\x16\x1d\xf4(\xbd\xc3\xfc\xb0~Q\xac\xb3/\x02\
+\xba\xe1\xacXo\x06-\xb3B\x83fK\xa6\xf2\xd9x\x0f\xe0\xf0\x19CH\\U~K0\xd6\xa8\
+\x11\xf7L\xa8\x98I\xc9#\xdd\xb6\xac\xad:c\xd2\xc3\x14\xc0\x13C\x00\xc95\xeel\
+D\xc2\xc00o\x03\x81"\x02P\xcd\xf2\x92\xba\x07JptR\x17\x8b\xf55%m\xbfZ\xd8i\
+\xd8\xf1:m\xfcQ5 $\xca\xf3\x96xi\xcbZ\x03}\x18\x05@\xbd,\xe2O\x85\xe9@"Fg\nm\
+?\x07\xbb\xa0\x1c\x1a\xb2\xab\x8c\x7fK\x9e \xbf\xe8\xee\x10\xb3\xf5z\x08\xbb\
+\x88\xbd\xbd\x01\xc5R$s\xd0\xde24Tred\xeb\xe7\xcda\xf1\xf7\x1c\x8c>J\x93\xcd\
+\xb1(\x0ep<n\\\xf7\xa5e\x00p\xed\x07\x93\xa3>\xb0m1\x8b\xd5\xfd{TA\xf8\xf1|\
+\x04yQ\x8a\x8f0\xfe]e\xd6\x0ea\x0c\xf2\x0e\x81\x95\xd4p\xe13\xf1\xf7\xf1\x03\
+z\x04\xd5\x87\xa8#ha\xa5b\xf5\x0c\xe5\x85\xf4\x81md\xb9:\x1c@\x80E\xa4V\xf5m\
+7\xc6E\xa8\t_T\xa9q\x9c\xe2H\toe\x8bV)\x9b\x8a\xc8\x92\xd4\xce\'\xf0\x10\x07\
+\x00in\xcdi\xb0\x90\x9d\xba\xf7\xa2&\xbd\x94\xc8\xcc\x9c\xc7\x8a@\xfd.Y\xa9$\
+\xe3\x82\xe0}\xbcM~\xa2\x0f>Br\x1e\x90\xa7V\xb3\x06@k\xf6\x82E\x94\xcc<c\xc9\
+l\xf4S\xd83\xa6\x94F^\x19\x15C\xfc\xdeAh$\x15\xc6\xf0\xcf\x04V"\xc0\x89\x18\
+\xed\xda\x0e\xaa\x19\xe0\xbdK\x86\x16$\x9f\xd1\x05U\x90\xd6\x12|\xb0\x0b\xf4\
+\x95\xfd\xe8\xd3\xa0\x16W\xea\xeb!p\x94\x92l\x02\x17i5\xe5\x05\x85\x86\xb2\
+\xc17\x02\x06\xa2 \xaa\xb4\xe1\x8b?\xd6\xd0_\x89\xf2\xd7\x82\x89\x80\xcayn\
+\xde\xf4\xa5\xd9 \xb6\x11]$\xc2AR;\xed\x0f~M\x87\xf3a\x12\x13?\x0b\x89\xbf\
+\xf3G\xfc\n\xcc\x91\xfa\xab!\x800l%\xeb\x02\xd7D\x9a\xc1\x9aF\x1b\x8d\x94\
+\xa0\t\xfaK\xddXd\xe1\x915g\x85\xb9\x99\xafr\xc8\xb2!v+2\xeb1\xcdK?(T\x9c\
+\xe6\x8a\xedG\x9b[m\x90\xac\x05\xf2+\xb9\xfd-\xcb\x9a1\x08\x91\xb5x\x9f\xe9s\
+\xeeMh*\xc5\x80\x01\x1f\x07\xb1\xaap\xa3\xbd\xbd\xac"TN\x05\xf5s`$\xa3\xe3\
+\x11\xe8\'h\xa7\\\xd7\x92\xe7\'C\x8cC#\x96\x94u4R\xf4\x8aO8h\xbf4\xed\x01\
+\xc8G\x82[PxN\x88\xf9\x03\x15\x031$\x90\xc7\x97$\x14\x14\xa2fGt\\\xa8j\xa9\
+\xbbI\xfd\x8a=\x1de+\xb9rk8?\xc4{\x8b\x1f\x99[\xa1\x11\x9co\xff\xe0>\xb2\x87\
+[\xce\x99\x08:P\xd7\x14\x18\x07\x10?\xdfN\x9e@\xf4@\x14\x03\x80&\xfe~\xaa+\
+\x8e_\xda\xef:\x11-V\x08\xdcV\x05ldt\x15\xa6O\xe2\xadT\xf6!Y\xbd.\x1a\x85\
+\xd2\xe9\x81n\xbc\xf7\xe6\x07\x00\xa2n\xc0\x11\xc2\x9b\x0c\xb6\x00\x92\xbd\
+\xe80\xc0\x9e@\xa4\x8eAIO\xb3\xb1\xed\xf5F(\x1ck\x11\x05]\xa03\xa1\xcb\xbd\
+\xbbo[\xda@\x10\x7f\xe9\xa0\x16\xd7\xbaJ\xc8\x08\xc9,\'4\x00swB\xf5.o\x15\
+\xea\x83$\x10\xcc\xb2\'8\x00\xe0P\x88\'\xd3\xf4\x91\x15\x89\xab\xc6\'&z\x92\
+\x82gC\n\x11\xc7#V\x1c\xb8\xc5\xf1\xbe\x9d\x1d\x109\xf5\xaa\x8b\xdb\xd4x\xe9\
+\xc8E\xb8+\x0cvv!\xd0\x10\xac\x02t7\xc5QFu\xb6\x89`V\x10Y\x08q\xce\x1b\xc3\
+\x02d\x8f\x82\xe8\x0f\xda\x1a/r\xf1\xe2\x8c3?\x8cT\x0cZ\xf60n\x14\xcf\\\xab\
+\xd1\\\xd2\xe7\x96]\x8c{\x86\x802\x10<\xb9\xc37\x08\x05I5D\x8e\x1dh\xc2\xec\
+\xb8\xf2\xe0=\x8d\x91\xb5k\xac\xf17\xca&X\xd8\xea\x80\xf3^\x1a\xcaY\xedZ+~\
+\xe9\x9d\xe2\xd21!y\xcd\xa8\xb3\xc5gr\xc1\xeaI#\x83\xee\xe2\xb1x\x89\x00KZ ;\
+\x95\xd8\x848c@)\xd8\xc1&f\xd3\x00t\xc8\x91\xdd)M\xeaA\xe2 U\xedpN\r\xbd%3\
+\xee\x8a\x9dm\xa5\t-d\xe4P(\xf1\xd9S\xfb\x04\x12\xf6\x94\x19\x93\x94\xa8\xea\
+\xc7\x95\xfc5\x89\x00J\x95\xa7[\x92qQ\x01x\x19\x8f\x83:4I\x1b\x95\xef\x17\
+\xc9\xef\x91H\xbb\x80\x90\x11\x99Of\x812o\xbar\xc2\x0b*\x84\xb2\x1b8D)\x92\
+\xcc\xcf\x13\xb4A_\xc0\xb7\x11\x90u\xfe\x0b-\xd5\x93\xd5X\xf2\n\xfe\x14\xa9]\
+\x0e\x17\xe8\xa6s$\xcap@\x93q\x07Q\xaeM#P\xd0P\x88zE\x9al\xbfG\x11e\x05\x02\
+\x00\x03,\xda\xf8\x0f\xb1k\x8b\xd0Q\x04\xb8\x14\x932\xb0C\x9e\x95!\xa3\n\x0c\
+\x96t\x83\x04\x82\xa52\'\xde\xd6\xab\xca*>-\x91\x0e\xf6\xe7\xdb*S\x8bE\x8c\
+\xcc\x1c\x01\x02\x06Y\x12\x81\x10~\xd5}\x08\\Y\x13E\xaa\x9f\x01T<A+\xd4\x9d\
+\xdaM\x9bg \xdb\x14XR\x92L\xca\x99\x17\x80\x1b>\xbevLY\x9cY\x15\x19\x14\xcaJ\
+H\xd6\xc4`\x82\x97,\xf8\x16\x9e\xfe\xc9V\xcb\xcfty\x87V\x0cU=\x94\xd5\\N\x11\
+\xbe\xa1\xcd\x1c\xfb\xd5S\xe3\r\xf6\x0f\xac7q$\xd3\x80]\xb5\x8e\x89*\x83\xd1\
+\xef\x14\x1du\x118\xd7@|\xa62\x1d\xd0@c(!\xd3\xbd \x19y\x06cc[\xc2\x0b\x7f0\
+\xbb\xba7:\x8a\xce\xeaD\xe9\xab\xdbuX\xe2GC"\x83\x83\x04\x12P\xbc\xb3\x82\
+\xd6w\xed/\xdeP\xce\x14`\x05=\xa4\x8a\x11\xbf\xbd\xbc\x12[\xfb\x8a\\^\xbb\
+\xc1\x9d\xc4\xd6\xaeu>\xacx\xebU\xf7\xc4\x04\xc4\x98\x8a \xa4j\xdf\xe2h\xd1\
+\x9dh\x80WA\x00\xf8\xd4\x12\x97b\xdd\xe6\xc3\xdfl\x9b\xccD1G\xa8\x1f@\x96\
+\xdc\x13\xcd\xac\xb5g\x89j\x9fT?\xa1\x9a\x98\xc0o*\xe13\x82\x0e\n\xea\xe1e\
+\xcdG3\r\x1aD\xcd\xa1\xf7\x96\xd0\xad.\xfc\xd5T\x89\xe7\x11\xec2p\xaf\xbc\
+\xb3#\x178g\x94\xb0T\xdbT\x86\xd6Ie\xcb\xc9\x84\x00_\xd5q\xe6\xd6]jr\x15\xd6\
+1\x8f\xb4\xc9\xd9\xc7\x97\xd5\xf7\x18"_\x0f\x93\xa3\xa43\x9b\x02\xfa\xb8\xf0\
+\xd0m\xd1\x9a\x05\x89)\x10z\x80\xe8\x86L\x08\xc5\xed\xb3\xf7\x92\x027B\x1a$2\
+\xe6;8\n\x9aR/\x80P\x93\x17h00<\x9b\x0cy\xa1](\xc3)\xf0\xe3e2S\xeaK\x1a)\xeb\
+$\x9e3\xa3\x8640\x1dE\xf1\xadR\xf8\x7f\x00\xe0\x12$f\xc1i\xe8\xec\x15\x18\
+\x94\xad\x1e\xc0\x86\xa3\x87V\x89\xc1v\xd8\x1d )\x99\x88\xb1\x9c\xbcb\x9d\
+\x06==\xff\x00]{\xd5\x9f\x04\xdaAl!A\x92\xd4\xeb-\xb8\x03\x1c\x014\x8b\x8b\
+\x7f\xb0\xf4j\x0c\xb16\x06T\xe74\xd0v\x94r\xc6\xa9\xa9t\x03\x02\x0c\x0c\xf1z\
+\xf2\x0f\xe7Kg\xd5k\x11\x90\xcf\xa7K\xcd\x03DMP\xf1\x9a\x86Z\xdfG1\x90\xfd\
+\x06\x90=\xba\x1d\xddB\x92\x85Huru9\r\xfaR{\x91{C\xd5\x98Y\xa9\x12\xea\xcfK\
+\xc7?\x00\xa4yx\xdeG\x1d\xb5a\xb4\x9f\x19\x8c\xcf\xe4\xf6\xae\x829\x85\x86\
+\xa3d;_\x8a\x1a \xb2"S\x00\x0e\x05\xe1Kp\xf6\x15w2/\xc0(\xf0`\x85b\xb4\x19r[\
+A\xa6\x93\x94~\xbd\x88\xd6\xfc\xc1^\xe2\xa7l\x9bK\xc8\xea\xec\xa0_\x08\xdde\
+\x8e_!x\xbd\x1a\xc3\x13\x13d\x89oP\x08\xda#\x8f*\x97\xdcE\'\x06\xbbX\x19o\
+\x91=MWjT\xdd@\x85\x14s \xa5Z\x1a6\xa4\xc5\xa2!\x11\xa0\x04\xa7\xfd\xa4T\x14\
+\x9f\\Bd\xe7UbA(\xd4-D\x83T\x1d\xf9\xd8{\xf4f\xfb\xb0C\x10\x83\xd7p`\x80Ww\
+\xec\x11\x1f(J\x1b\xee\xa4\xa4\x0e\x84r\x0c\xe1HSD\xd9"i+\xb9_ \xfe9sq\xad\
+\x02\xa0!\x80\'f\t\xa0\x89\n;`\xd2\xf9T\xc4\xbe\xb4\xb3D\xf53\x95\x06g($\x1c\
+T\xb4(\x1b\x02\x0cL\xbf,\\\xfa|\xbcr\x84:\x87\xec*\xb6Z\xb2\xfaVr\x15\xdd R3\
+\x05\xdda\xc5\x80\x1e;!z Fq?\x80$\xfb\xe0\x17%P8\x82\x03A*\x00\x06t\xb5k7\
+\xdfg\xd2\xebN\xe5z\xf3\x06G\xd4/\xf2\xa7\xea[\xb6V\xc9(\x88\x93E\x16\xf4y}\
+\xba\xec\xf9`\xd4\xdevB\xe25\xb3\xe1\x8c\xfeON \x86B\xd6\xd3\xd74\x06\xafK\
+\xf6\xa3\xc7\xa9\xe4\xc6*\xd1?\xf4D@Ju)\x039(\xc6(\xf7a\x11\x7f\x17\x11\n>W\
+\xaaO\x886\x98f>\xe2q**\x98\xe9\xc8\xb6\x12\x8a$J<^FU\xef\xe8\xd26u\xba\x1c\
+\x96\xbe\xed\x1b\xa4\xb2>\x15V\xe78\xfdu\x00\x8d)\x95\xaf\xf5\x0cj\x18\xd0]\
+\xa6\x90\x14\x00\xd7`\x89\xd6|\xa4\x85#\xc0\xe9\xd7\xe69c\x17\x13\xa9\xac$\
+\x8b\xec\xca\xa5\xb1c0h\xf2\xadC\xa0\xa0K\x89\xa3\xe3R\xfe\x8a\x17\x9a /\xa0\
+\x87\xec\xf2:\xc0\xd0\xa854\x8b\xa1\xa9\xc2\xb5\xc1\x87\x95\xa6X|\x1e\x85\
+\xcf\x8cd\xe8j\x10\xa3\xfa\xdfq\x070\x06\x02\x96\xe0A`zf\x0f\x99\xf0\xec\x10\
+\xc1\x17\xc8\xa5\x1b\xd7\xfe\xc9\x0e\xa5\xd8\xf8\xd4\x9e\xfa5\x00M\xcb\x02\
+\x89\xc9C\x8af\x10\x87J2!\x19$V\xbba\x1d\xd8\x14\x80\xdf\xd2\x0ePB\xa2\xc2\
+\xdeu\x97\x1d\xf1\xb9(\x81\x11PH\x15\xbb\x82BA\x12\xd1\xa1\xc3\x8b\xeb8\x182\
+h\xb6&\xe7X\xd0 >\x98][\xa1\xa3\x7f\x14\xc3\x1f"\x99CE\xffw\xfaN5\x0b\x02\
+\x07\x81$.\x1c\x06;\x9d\\\x08/\x00%+\xa2\xa3\x99s\xba/@\x01\xd3\x848\xc0Duz\
+\xa86\x18\xb9\'g\xd8\xad\xcch\x82\xa1K\xa0\'\x8b|-_\x8e]\x96<A\x92\xc1\xd1ZV\
+8\x10\x04\xdb\x8a_X\x0c\x1e]8NB\xe6J\x15q\x99\xa3\xbb\xbeM\xaa\x1c\x11\x9a\
+\xfe2\xd7\x12l6pE\xca\xd7\xd0L$0g\xcd\xd18~\xaf\x8f|\x94\xa3\x16\x07\xc5\xd7\
+\xe1\x06=A]\xbf\xa4\xeeu\x1cx\xe5\x0fv\x009\x8fR\xdc&_\xa9H\xcb\x96\xe2\xd4e\
+\xcb\xcb\xecbt\x05\n\xc3\xef,\xc4"\xde\x1b\x1bH\x05\x9e\xa8854(b\xe7\x1e\x01\
+0\xac\x91\x1b\x0f\xd97\x14\x92\xba\x00\xc2\x9b\x04\x08\x17\xe9\x14D\xc2\x81\
+\xec"\x1a\xedyC\xba\xa2\x1c,\xa5\xe0IS\xca\xc7Y\xe3(\xd0\xb6;l\x1f\x90VIz\
+\xb0\x0f\xceT\x10\xe4U\xa8\xf0\x01\xe2]\x94\xba\x18\xf5\x1a\x02\x94\xbaH\x0e\
+\x1f,\xaeVP\xb6Q\xa8\xf0\xf8\xb0b\xef\x93f\x9cR{\xed\xc5U\xa8F\x8d\xa7\x84(\
+\x0e\xac\x8c\x1d\xea\x0c\xcd!\x892\xd3C\xf9\xcd0\x93\xae\xbd\x07\x80\xc6#\
+\xdb\x8681\x03\xe7\x87\n=\xbd\xfb\x18\xd3*\xaf\xb1\x8a\x01P,\x91\x02u\x04\
+\x81\x81J\xf7\xbf5\xfd\x83\x06\x11\x1dU:\x16\xa0\x1b|\xf2\xac\xf7P\xad\x90\
+\xfcT\x07\x03\x9d\x81^+^/\x0f"\xc4\x17U5`x\x94\x96s\xa4gt\x08\x07(\x96\xd0\
+\xc9\x12@\x03w\x88\x9e\x93J\x9c%\x04\xca\x8e\xeai-\xc4QA\xa4\xed\x81\xc4a\
+\xfb\xc12\x85\x9a(w5\xbdP\xed\x1fcf\xb2\x08\xc0\xf1\xaa\xf8\xdb`\xf9\x04\xd4\
+\xbf\xb2\xacs\x9b3\n\x91\x1a\x85\xfea\xb8R\x1aG\xb2K\xd4H\xa6\xd7\x83V\xe5\
+\xb6#v\xba\xe1\x93\xcb\xe0\xd9\x13\xe0Z\x11\x89\x8c*\x1c\xd8\x08 _\x90\xb2\
+\xbfE \xdf\xc3\x1c\xbf\x8b\xc8\xb8\xc15r\xdb\x8eG\xb9\xa3\xb1C\xbd"\xbag\xe0\
+\xfe\x99\xa6\xb2HwR3\xa3\xf5\xb3\x98G(z\xc0|\x87\xb5\x1a\xed\x08\x0eT\xd1\
+\x9a\x0e#k\x10\xceRB\x80\xe43\xff\x8d\x896\x18\xcb\xc6I\x89:\x1e]\'e\xf6\x86\
+\x8f\xd5\x0b\x1e\n\x06Js\x8b5\xa9gF1\x04\t%]*\xbe\x9fUfS\xbc7U:=\x85\x89\x03\
+K\xf1\xba\xf8!\x03\xd0X\xd4\x8b\xef: \x17H3\x07N\xbd$9Z4T\x9eE`\xef\xcb]A\
+\x90*\x99\xd4\xc3\xee4g0\xcf\x84\xc75\xa0>\'\xf6 ?\x1dH}\xa2\xb9\x8d,\x15\
+\x04\x0eL\xe2\xc2\x0b\xf1\xe0u\xd9\x1b\x9e\x15\xc9L\xfb%\xcc\x0e\xb4b$\x94\
+\x91\x81(9\x99w5\xb5AEpD\xbb[\x08`\x19hG{^X\xf1\xb0d @\xd1\x86>IuZ\xa9,\xe5x\
+\xad \x80A\xde"&\xcae\x17\x8b\x84)\xa0w\xfd\xdc@\xbc\xfeo\xbdQ\xb4\x8a\x8e\
+\x16\xd9\x83\x8c\x06\x87\x18\xc7%\xcd\xd0 \x1c.,\t\xaco\x15=\x8f\x06(;\x12b\
+\xa1\xca:]^{\xe0@\xc5\x81V\xb4\x91\xb2\xa5yM!\x93\x84\xaay\xa0\x97\xfb@\xc6\
+\xceP\xef`\xcc\xeaE\xf0\xf4\x0f\x8eCj\xa0\x88\x00v\xcdb\xe2\x17^\x03o\xd8?\n\
+\xec\xf6\n\xac\xd3\xa4sk5\xba\xd4\xb7\xddb\x11\x8bu\xecW\r:0\xd080\x9f:\xf0\
+\xd5\xb3\x07\xa1I\xc9\xa5\xe22)\xbe^\xc9\x01\xa8\xcb\x97-^\xd1\x1cE\xd5#\x05\
+\x10>\xb9\xa1\x14\x91K\x935+\x9f\x85o\xcci9\xb57\x95\xd4\x07E\xe1dl\x11H\xd7\
+\xbd\xea#\x92\xce\x00D\xb6\x14#\xacL\xfc\xbb+s=\x98SU\x1d+\x8d5\xc5\x15\x97P\
+\xd0\x1c\xc3\x1ao#\x1f2\xfa)\xb1)\x0c\x19{\xb71\x1a\xc3\xdaT\x0b\xe5\xe4\x0c\
+\xa6w~\x86\xb9\x00{\xdf\x17\xb24E\xd0\xd9V\xb2kFW\x85]\x9e\xfe\x16\xb2\x19Uv\
+\xcee\x1f\xdd\x9b\xc5A&\xbfN\xb3\xa7\xd5HK\x05v\xc3\x1d(_)\xad\xdeED\x0e\x7f\
+e\xe3\xf0;\x81E\x91\x0e\x07B\x9c\xc3\x9d,\xc8oE\xc7?<\x92\xd7\xbar\xef$\xaf\
+\x07\x8d\xfc5S\x03\xa0\x8dl\xa7\xe7\xea\x141\xb4Ek\x8csj]\x83\x8e\x95R\xfd\
+\x1d\x8d\xb8W\x14\xffQ\x06ZS\x16^\xe3\xd57t\xba9\xfe\x0bU\xeb\x95e\xb8\xc5nz\
+\xfaS\xc1\x91\xaf\x7f\x06\xb8H\xca+\xb38\x85\xb8O\xfc\xa5\xd4>\xcd\x8d\xa1B\
+\xf7\xf8 "\xc2F\xc3\xb0\xcb\xbaO\xab\xaaX\x8f\x03\xe0\x87\x01\xea\xfa\x9f\
+\x8d\xc7\x8cG\xa3\x96\xcf\\\x0b\'PZ\xa4\xfcBT}`\r\x17sH}\':\xb8\x99|\x91\x91\
+1H\x83\xf9\x05\xea\xc2W\x1a=S\t\x83\\nT\x7f9=\r^\x99\x1b\xfbQd\x9c\'\x86\xe9\
+\xb4\xacwP \xa0\x8f\xba\xaa\xc6P\x1eW\xebAMK{\x93U)\x8fbB\xcf\x00\xee\x9e\
+\xd5\xf0\x1e\x98J\x18\x88\xc9V\xdf:\xa2g\x8b\xcb\xa6\xa5v\xf4!0\xad\x0b$n\
+\xd4\x8eb\xa3\xc7\xe5^\x80\xc0:M\x90\xa2x\xc5\xff\xe9\xb5\xf7\xd7\x10vv\x8f\
+\x02\xb9eP\xe0\x83\xc2v\xf4V\xb2c\x17p \xbf\x95\\z\xb8\xa2\xac\xadQN\xe1\xc6\
+\xebk\x85\xd9M\xd9P\xd2v\xba\xb0\xb2\xb7\xe0\xe4\xc9E\xbd\xc2\xe1[\x88\xab\
+\xe30\x00\xa1\x19\xaf\xbb%\x03c%\xb1\xde\xb5\xe9\xc3\x04\xe8o9\x07\xd1\xdd\
+\x9e\xfa\xfb\x92\xb5.X\xab\x94d\x1dI\xcb\x80\xae\xf9\x9a\x8arC\x01\x84\xe4\
+\xc3\xb6z\x8c@\x87XN\r\xe4\x11\xc0viy\x1d\xfd\x87h\x84\x029\x8b@`-\xba\xcf\
+\x98\x05\xe4\xdd\x026\xd0\xab\xa7\x88I\x9ar\t\x80fs\xb2\x94\xcc*\x87\xac\xea\
+I\x94>Z\x8c\xb7xM\xc1#\x93\x93\xf1<\x0c\x04\x0c\x1f0\xeb|,\x18\x95v\x8a3=k)\
+\xd9\x88\xc83w\x05;R\x85\xb5\x14<\xa0\x1bM\x96v\xb7\'\x95\x10\xa6\x00\xb4\
+\x04\x1d\xaeaf\xe9CV>\xa2b\xdfP\xa9\xa8\xc3_\xb2\xc2wzS<\xe5\x98]\xe7\x96\
+\x08\xd5\x1e_\xb5\xa0\rn\x9e\x01\xa0]\x00p\xc1\x81\xd7\xdd\xc9\x9aH~\xbaD\
+\xab\xd0\x03h\x9a\xa0\xd2\xd3h\xc7\x95\x96\x99Xq\x88\x18P\xed\x1b"\x8580EO\
+\xc0S\xa1\xf9u\xd0\x89L:\x01\xa5\xa3\xb5\xa5\xce\xdc\xe2\xa0\xaa\xdd\xb4Z\
+\x08\x05\x0f\xd6BP\xb1\x16\x04\xf0\xac9\x00\x0fh\xd6\x83A\xd0\xad\xcbTU\x99\
+\xe6\x04\xca\xe6AG.\xa8\r \xe2\x96\x9ck\xe5\xd0\x1d\xfb\xe2\x0c\x03B%\x02|\
+\xfc\x9f\x95\xf38O"VsVG^\xf7\xf2\xd5s\x14\xea]\x8a$t\xdc\xb9\xb6\x15\xc0m\
+\x0c\xdb\x16\xc41\xec\xd3\x02\x94[n\x0e\x000Y\x96EEc\xe4\xe7PPn\xd3\xacs\xb0\
+Q\x18\xf8\x94\t\xdb\x03\xa1\xa3*\xf6$\xcc\xf4\x9a-\x83l-\x89\x02\x1el\x15\
+\xf5B\x02x\x0f\x13\xd6\x16\xa9u\x0c\xc9\xf8W\x82\xdc\t!\xb2\xec\x01\x03<\xea\
+;\'\xd9\x13:\xd6"\x10#\xc5\x10\x10z\x89O \xa8\x0eX\xb9\x13P\x83\x03"3\x1a\\\
+\xf7\xbdn\xe4dU\xd4\x98\x11\x7f\xabH\xbd\xd63m}\x00\xd0\x0c3\x95\x019\xfaM\
+\x0es\xd1\xb6\xcf\x17R}\xfb%J\x87\xe0\xa4\xf8\x16J\x02\xd6\x05\xde{\x00\x82\
+\x02E\xfc\xc0\x10P\xea}s\xc9V\xc3Aa\x13\x01y\xea\xad\x8a\xceM>E]\x12\x01\x96\
+\x83\xa8\x0f\x1f\xfd\xa0\xa9\xac\x89\x14aWvn\xa8s\x86\xb1;\x808\xc5_\xa2\xc4\
+\x1f%\xad\x85\x01\x96r\xea\xa9\n\x05\xb0R]`\x06ix\xf8-\xda\\`{\xbe\xd6\xe3\
+\x03\x0c\x01YS\x86\x9d\xa3(\x18\x07\xed\xf1\xb2^\xad6\xa4\x9f\x1d\xf1\x96\
+\xca\x17I\xd4mA\x1f\x9e\xb2\xde*\xc2e\xc70Om\xbe\xb6\x98T#\xfe!lDT\x065{\xfc\
+\xb8Xw\xe1\xe8\xae\xb1\xc1\x83\x8a\x00^P\x1f%\x84\x9a\xfe\xf0\t\xad\xc9\x0c5\
+\x05\x9d\x82\x84\xe2\xa2I\x17\x99\xbe\x08P\xc6\x00\x00\x117p?\x80\xf9\x03\
+\xb6\xf8Wkm@\x17\x7f%q2%R\x1e#\x94\x04\x19\x02D\xbdV\xf5\xdey\xd7\x144\xb6\
+\x08\xc8\x1900@t\x8b\xde\xe80j\x95uQZ\x93\xa7\xcbL.W\x80\xaay\x1f*\'\xf2\x07\
+\xf0\xe0\x8b\xa3\x7fU\xd6\xe7\xb5\x81\xd8\x87,u\x14\xf2D\x08\x02Ls\xac\xec\
+\xd6\xb5<\xb2\xe3ez\xa7\x94\x9c\xddRV\xae\x02<I\xc8{\x0c\x11\x03i\xf3\xc2\
+\xccLL\x80q\x93\xd9\n\xd2\'Z\xf9\xe3\xf1\x99c\x9c\\O\xad\x18X\xa6\x1f*\xab\
+\x8bm\x14re4<x\xc7g\x07J5\x19\x04(\xfe\x81\xa77\xa2Ju\xe2\x9f\xcb\xe2\x8d\
+\x9cI#qW\x8f\r}\xc4\x8b\x89\x00>g\xab[x\xe1\xa9|gM\xc0\xa86\x91\xe6\x93G\xf0\
+\x94N\xc3\xed"\xdc\xd7\xcc\xfd\x1a\xae`{8\x8am\xc7\xb1\x0f\x18\x92\t=\x8a\
+\x07\xc0\x8e\x9eI\x91\xd0,T\x8e\xc8)\x83?\xd5-0\xa1y\x00\xf9\xa96\x14\xa5\
+\xa5\x96\x89\xd7\xb9s\xdf\x158P\xa9\xd6\xc9\xa9\x9c\xc9\xf3)b\xb7{\xa5\x93\
+\xd1\xf1\x11Wr|\xfa\xab\xfd\xc1\xef|\xc3\x05\x1c+\x16h\xe6\x97\x990\x9e\xb0\
+\xe5\x8f\x97 l\xa4\xfa\xb8.\xe0\xde\xcc\x8f\xb6"\x142\xf2,m\xecF\xca\x19\r\
+\xa1 \xc0\xa0\xaa\x08\n\xf7\xb5\x08\xdd\x19Cj\x12h^\x8c\tL>\xd1Ut,\x1d\x89\
+\x98.\x9d\xf3\xa7\xea(\xb2\xed\xb7\x04\xc6R\x18=^\xa9\x8d\xb0`\x1a\x02\x81\
+\x80\xcchsd\'\xc3\x0eC\x99\x7fk\x85G\xee\tk\x08\xf0\xf8\xca\xb4\xd1x<\xc7[u\
+\xb1HXmM\x03\x18\xa6\x9a\x0e=\x9e\xbbC<\xb7\xee\x1a\xa8\xa9\xb6\xd7\x02\xd7\
+\xe3\xa0\xf7\x8ei\x08P~E\xfc\x9cZ\x87__\xc0\xc1p\xe2\x07\x0fhO/C\xd2\xf3\xbe\
+\xa5?\x00\xfc\xd6\xd4\xa2\xe4\x02=\x8bh8\xf6\x96\x93\xc0P\x15b\x98+\xe0\x80\
+\tF\x18\x05\xd5\xb8\xb9XZ\xd6\xc7\xe3\x14\xe5\xcf\x0c\x83\xfa\\Nf\xa5%\x9d\
+\xf0\xc7X\xa78\x80\xbe^\x82\x16z\xcdEm\xef\xcf,P\x13\x9cx\x02\x89\xdd\x98\
+\x10\xc0=D|\x02\xea\x94\x99*\xc4y-M2\xa0\x97&\x976\x022\x85y\x8ajJdq\xe1\xfe\
+\x7f\x0b^\xcc\x02\\\xfc\x80\xb5 \x00\xb2\xd1\xa51c\xfa\x04\xd9E\x80\xccT\xc1\
++\xd7^\x17\xbd\xecS\x97\x99\xb9(J@r\xc1t\xbc\xe7,\xe0TM\r\xc4+`\x08\xc8\x8c\
+\x9e\xb8^\x102E\x95\x00\x15n\xddp\xa1 ~v\xad\xb8\x03f\xc0\x19\x1b\x14\x99\
+\xe0$\xfc1\x89\x86\xf2TR\xce\xd4cQ\xb6\x14\xaa=\xb1\x8b\xa2$\x02X\x15\x06#\
+\xf5\xca\xb6\x1e\x01\xacT\xc6\x90\xaf\x9a\xb1\xc8\x88_+S\x13\x18kx\xb1\x88s\
+\xedZUO\xde"\xefIRe\x9d\x9a\xdf\xe2\x1d\x97%\xedP\xef\x91\x0b\xc9T?.\xc5\x88\
+\xa0\\\xd4\x1b0\x94S\xad\'g\x0c\xb2\x84fi\xf7\xfaL\x18%\x98P\xeb\xf0\xcc\x15\
+\xec>\xaf\x00]\x18\xe6\x8cD\xc3\x19U\xaa\xe8p\x00\x01\x13\xe1\n\xd0d\xb2\xa7\
+V\xc0\x16\x01\xa9Y\x81\x03 Z\xa9\x87\x17\xd5\x96W\x10\xc0lA\r\x99\x8eKiw\xbb\
+\xe4Z}P\xbby\xa4b\x9e\x82VN\x9d\xb53\xea\x16\x06y\x046\x07\xed\xafbU\xbbt\
+\xaf\xfc\t9\xb0j\xa8|\x1d\x9f\x1c\xfd*\x97\n!\x93\xba\xa1$\xbaHcW7\xdd\xea\
+\xaa\xcd\xc9\xce\xe8\xa2\r~\xc8\x03\xb1\xd9~\xc8}\xf1\x82{*D\tH\xaa\xc1\x8a\
+\xdaX\xa5I\x98\x82<Yd\x8b\xd0V\xff\x11\x02\x8e\xf4\x92A\xa8\x8f\xffR\x19z\
+\xcd\xb5\x00\x00\xbb\x84$H\xc6\xbc\x1a[`&\xf3\xde\t\x15\xf7\xc2\x01D7\xd5J\
+\x909\x8b\xd69\x1cI\xaa;T\xc2\xd5:\x11\xa0\xa1\xf33\xaby\xf4NY\xaeaU\n\xc7\
+\xbbG\x90\xc6X\x1dR\xbd\x0ew\xe0P9C\xc2\xf9Ak\xb2f\xf5L\xdf\x99\xbb\x81\x1aD\
+J\xc8\xe4\xdc\xd9\xedn\xa2\x95\x8f\x94\xd9\xf3/v\xf1.[\xd2Y\xf9\xee\xf5\xe2\
+\x96\xbe\xf6s\x98PU\xf1\x8a\x83:\n\x8a\x8de\x83\x04A!#{\x95\xb6\x19\x9bw\xc9\
+\xb4T<\xbf\x8d\x91M\xb8M\xd2v\xf8\xd74\x93%\xe3`\x874\x14\x18\x0e\xcaE+\xd6C\
+\xacr\xf6\xb9Pf\xb6\xd4\xb9\xab\x813B\xb1\x1d\x13\x96\x89\x87\x98\xaa4\x81\
+\xd8{\x9ad\xdcf"E\xaa\xbap\x8eggr\x9d\xa0\x10z\x8fX\x8d\xb0\xc4A\x97\xd7\x9b\
+\x9d._\x00\x89\x93d~\xf5\x85M\xe1\xd1\xc5rK\xe5k\xca=\x1f\x12\x9cJ\xd1v\xc7\
+\x87\x8d9\xa7\x05^\xf4tuL\x9b\xd9TO\x12\xba\xce\xcc\x9f6\x8e+\x05A7T\xbd\x8d\
+\xbd\xa0\x06\xe2\x05{I\x07h\xb2\xc7\x05\xcb\xdf\x8d\xa8^\xb9PY\x02\xda\xa1\
+\xd9#E\xc6\xd2MVQ!\x8d\x97\xe3\x86\xd1)6(\xe3{f\xf7\x1b\x899\xc0e\xa3\xb4\
+\xed\xba\x1d\xc6\xbc\xa1\xc3\xb8\x07\xa9c\x87`\xd9\xaaOw\xd7e.\xfa%\x03c\x85\
+\xc0\x83\xb7\x06^f<\xf5x\xe3&w\x05\xa8\xb9\xf0\xfc\xa0\x8f\xb3\xc83/AS\xa5<\
+\'\x9d\xb2\xf6z\xbb\x8a8TB6v\x19\x07\x82\xb5\x9e\x86\xca\x1a\xe89\xcd\xa6\
+\xe9\x7f\xb5\x91=>\x1a\xd1\x12\xb1<\x1a7\xc4\xa2/\x12\x85a/\xba\x04\x8c\x828\
+\\\xaby\xe4\xd6]*\x9e\xbe\xf3,\x0f\xa0\x9d\'\xe0k#\x061\xb6e\xccx\xa9bB\x7f\
+\xecW2\x94\xecFO]\x1fGK:v\xaczk\xdc\x04\xa8\x08\xa0n\x01\xf9\x02\x9bZ\xb7\
+\xea\xd8\xb1u\xc7\xdcxQ\\C\xab\xb3-\x02jP\xdaVz\x02\x894\xd2u=I*\xca\xe9y\
+\x93C\xf3\xe0\xac\xe2\xcdu\x95P\xe6-\x7f\xde\xa2\xc9W\t\xfb\xbb\x0e2\xe8.o\
+\x85h\x88\x87\xa8.\x1d\x89;c\x83\xbeJq\x16C\xd914\xbc{\xcaMd4\xdb\xb9}5v\xac\
+\x8c;\r6 \xb2\x84<\xfd\xa5\xbce\xfb\xae\xb3\x11T\x19Ty\xa1\x9a\xa4\x11\x04\
+\xa0p\x1eXU\xb2\\\xaf\x10NU\xf3\x8d\x98P\x18\xea\xf5]D\xa9\x06\x18\x0cB\xa6\
+\xa1\x83\xe00\xbe\xb3Cn7\x93\x8b\x02U\xaf\xae\x13\xf72\xfcY\xf7\xd0R\n\x19\
+\x96\xc8e`\xc4i\x19p\xbf\x93Y\x89\xfenl\xc4Lb\x98\x0f#\xd2q\xe6{\x0c\xed\xfe\
+\xe1j\xc0W\x8d\x08J\\9K\x0b\x02_\xe1N\xd3\xe75\x05W=\xb8\xf8\xb1\x17\xcc\xbe\
+#?Z\xb12\xe5\xd0\x08\x9f5(\xe1<\xad\'\xc5$\xfa<\x80\xf7\x0c/*H\xbb\xb8\xa6<\
+\x9a4%\xcemS&\x14\x8d\x84QJ\xd5\x07`\xcd\xe5\xeaj\xa8\x9a\xec\x95v\x7fk\x8dZ\
+\xdd\xe4\x16g.\xb3\xa6\x9ag\xaa\'\x11\x880X\xb4ok\xb8n\xda:(\xb92\x89\xc5R\
+\xf0I\xde>\xfd\xb3V\xac\xaa^\x02\x8e\xeftK\x1e\xfea&Pq\xcd\xd3L\xff4\x82\xfd\
+\xdd*\xec\xa1\x82\xab$\xe1\xc1\xd7\x8f\x8d\xa0\x80\xd0\xffF\x1d\x1a\x02\xe2\
+\xad\xf8\x17d3`E\xa9\x1f*\x0c~\x9f\x90\xda\xe9\xe9\xed\xaa\x04\xaa\x12c\x8d\
+\xa0\n\x1e2b\x1f\xa2\xe6\x96\xbe\xae%Z)\xe5k2\xcf\x10\xe4\x03\xc6\x81B\x93#@\
+\x9ai\x0c\x81X\xbf\x07\x18\xf0R\x1a\x02\x88\xcd\xc5\x84\xb1\x1f\xc3\x06.\xb1\
+qzkW\xf4JV\xd0Z\x19\x11\x10\xe2\xd3w{{\xcd\x9cZ\x8b\xaa\x9cPMU\x8b\xf1\xdf\
+\xd5\x11%\xa5\x80N\xb7cBS\nY\x95\xf9\'\xf9\xd9\x98\x96\xc4p\x85\x1e<\xfeL.\
+\xd5\xa3B\x0b\xf8\xa2\xe1\xe0\xed\xb1\xc5\x1fC\xda\xd4\xa9^\xf6W\x97\x17\xcb\
++\x13m\x86\xbcSP\xf1\xed>\xab"\xec.:\xf0\xc1r;W\xec\xb5h\xd3WAK\xda\xd6\x80\
+\xdf\xe3\r|\xcf\xa7\x88^\\\x04>\x93\rQk\'K\xf7\t\x0f\xe5\x96\x11\x1c(\xdc\
+\xa3J\x82x\xd4w\xc6g\xde\xefX\xe7\xc7\x15\x10\xc0\xa7\x97\x99\xbb\x18\xe7!\
+\xa8\x06\xb6\x84\xcc]K\x1b[\x95\nL\x9a\x02\x91\xcc}\xa7\x04\xbcnv\xe0Y~\xcfo\
+\xbd\x8c\xe2U\x00\xcb9\xa4\x07\xa0B\xc1\x93T\xf62\x9f\xfcK\'z\xb0QB\x80\xe6\
+\xe4\x8a\x19cP\x00\x1ep[\x0c\x15Tb\xcd\x875\xf3l+\xbc\x14?\x83\x18bMY\x8bhx\
+\x11\xaf\xdc\x08\xc2\xd8D\x10%\x81~<h\xa3\xb3\xd6\x0f\x90[#\xf8\x9a\xce\x1e\
+\x95lC\x84J\x04\xc8\xe0\xc4\xadP\xbdA%\xd4\x1c\xd3gxG\xa7?\xa2\x19TIT\x1eL\
+\xf7\x8ca\x0f\xe9\xdd\xc6\x12\xe2X\xb2\x80\xe5\x8e\xd3\xb5\xa1\x1fs\x1a\xfa\
+\xb9\x9f?\xa86F@!\xd3\xb3\xe8\xc1\xc2\x1e\x9b\x83\xfa\xc3\xa9\x8c\xb0]\xa5\
+\xbe\xda.\xb9\xe2\xb94\xbcs\xb1i\x9czv\x8f\xa3\x1c\xbd\x8e\xf4R\x99\xc6\xb4 \
+\xa6Y\xd7\x0c\x01\x07L\xae\xfd\xab\xf6\xd6\xe8j2\x04\x07E\xaa+1(+\n\x16\xde3\
+>cD\x00\x93\xbca\xac\x11I\x1c\x1a\x9a\x98~\x85\xe7BQ\xeb\xc5\x7f\xb4\xa0n\
+\xa7+\xc6\xa1\xfcj_"S\xfd\xb2\xd0\x0c\x91lYz\x9b\xd1\xff\xb8;\xe4H\xcd\x8a\
+\x80\x10\xd7,\xb8\x92\xb7\xab\x95\xc5U\x00\xda^T\xc0\xa1\xd1\x8c\x00\x00aQ\
+\x0c\xa2\xe1\x10\x00e4X\x9fw\xcf\x8b3\xf7\xc8i\xe47\x1bj\xc5\x1fC\x06\x8aUr\
+\xb5rj\x8b\xbf]B\x84\x02^\xad\xc0\x05\x19\x9dD]\xeeN1\xee\x1a\x8d[\r\x08\xa8\
+0\x9e+\xc8\xd9`\xd5\xdbA\x0bv\xce\x0c\xb9w:\x8aP\xe1W\xb745"\xac\x90\xc5m\
+\xdb\xdbUc\x12\xdf\xf4\xb10Q\x0c.L51B\xba\xeb\xba\xa5j#[\x83\xd8!\x933\xd23\
+\xc4sL\xa3\x9f\xbc-\xb6-U\x83\x80\x15\x07!\xb0p\xcb?\x8fU\xab\x00\xb4\xb9_Jw\
+\x00\xfa\xa1\xbdb\xcf\xcb\xceP8\xc2\x838+N\xb9\x80\'\xc83\x97\xb4r\xa0!M\xe0\
+\xb0\x9c\x01\xe3\xd5q\x8d\xafM\x0b\xfb"\xa0\xbf2\xa0\xb3W\xad\x02\xeb\xb8H\
+\xcd"`\x1a^\x8e\xd1\xf2\x00\xe9\x90h\xdd^\x1a\x1a\xab\x99v\xaa\\\xa5H\xa7\
+\xa1\xa8\x18\x02\x0eJ\x93\xd18\xe4];;0\x96\x8dY\xdfH\x0c\x80\x85\x80\xca \
+\xfag\x88\xaf\xbaT\xd4\xa2\xe7\'\xd1>E8\xd1dH\xc7\xc1\xac\xa7G\x04aR\xde8\
+\x9f>TPa\xce\xa2\x11\x0e\x8c\x10\xf4Z\x9b\xa5\tb&.\t\xd7\x8f4N\x995\x0e=\xeb\
+\xab\x1e\'r\x8eWx\xe07\xbf\xe1\xc2\x0c\x81\xac\x96A\xa4\xeaQ9f\xaf\xbdg\x1e\
+\x9aQ\x9d\xd3\xae\r~\x05o]\xd0\xc6}F\x17xb\xdf\xe2\x87\xb0\x98\xbc\xd9\x1fS\
+\xfc`!\xa0zCHozd\xc7X\xfc\xa7\x96\'ZD\x873T\x9cX\xc0d\xf5%\xb3Z\x86\xf9p\xef\
+n)\x96t\xfa\xb6\xa4\x88\x8a\xc2\xc4)\xdbC\x85`\xdf\xa0{\x99%\x1fs\x80\xfbGA\
+\xc0j\xa85YeE6\r\x82\xe0\xe5\xb5\xfa\xf6\x90"&\x0c\x1b,\xb5?[\xbd\x11\xea?3p\
+\xa9<=\xac\\\xa05}\xa7\xe6I\xadhD\x16\xa1\x00\xc0\xd4\x01\x1d1\xebY\xcfze@\
+\x03\xfb\xee\xc7\x10\x0bv\xacH\xc4\x84\x89\x83d!\xf9\xa8c\xfe@\xcc\xc7\xa6\
+\xec\xa9\x80\x16)\x9a\xa0!\x00\x88\x96V\xd4\x00\xf4\xec\xd2B\xce\x84re\x03\t\
+\xa8\xf8\xd3m\xe8\x15\xccs\x81\x0f\xe4\x82\xe6\xf3*j\x9fw\x90\xee\r\x0cw\x8c\
+\xdd\n\xd6\xe6\x0c\x00`?\xcb\xc7);\xb0\xd6e\x11\x80\xd4\x85\xc9q\x9b[\xe5C\
+\xb1\x05\\&\xc2p\x00J7\xcdN\xc3(K1pK\xa6\xe6\xc4\x15\xc6\x11\x9c\xa6\xefF\
+\xed\x8c\x17\xf0x\xc6o\xd1_\xc9\xce\x83\xeaI\xc4\xfbP\xb5\xaa\x1b\x01\xa1\
+\x00=UH,\xbf2\x0b\xb0\xfc\x00\x8bAZ\x95\xb6\x8f!\xafpL\x9f\xce\x89\x00j\x82\
+\xae\xd7\x86\xbeU\x1b\xb3\xc4\xf4\xacG\x0c\x99W\x02\x10RQ\x07\x10\x11\xe5\
+\x10\xb0\xf2\xa1_X\x1f\x04\x88X\xc0r\rf\x80\xd8\x06\xcen\x01\x01\x19\xceu\
+\xdf\xc1TM\xd4\x99\xe0\xbcvM\x00\x92IU&\xed\xec \xb7\x01H\xc7}f!V\xdd\xda\
+\xa9\xc5A\x85P3\x93:\xc6\xaa`"K_\x95\xa53$\x92\xa0\xe0\x15\rkk\xd3\x9a\xaa\
+\xf5t"mg\xd9\xab\xea1\xa1\x17\x0f\xbb\x88\xded]u\xbf9\xeda\x8f\x8c\xaa/\x94\
+\xcb\xc0\xa6\xa4]\xf9\x80\xe4\x8b|\xd9\x0e\xd4U\xad\xaav\x95\xf9\x81\xc5^\
+\x8d\x80\x94\x01\xaau\xa8O\xd1\xa4\x84\xb5\xd7\xec-K\xccb\x9b\xbcbd\xbb\x82l\
+\x8b\xb6\xbd\x1d\x0e\x01\xe6\x0b\xe5\xc4x"\xea\x87Y\xdfb-\x80\x8f\xce\xf1BeH\
+X\xd5(\xb6\x9a\xb0W\xe4\xaa2\'\x00P\xb7\xaf\xaf\x1d\xcc\xe4OR\xeb\xd6\t\xe4\
+\x1cP]\x0fV\xbf\x1b\xd1\x17\x04\x99\x17\x89\xa9:_\xb1V\xf5]\x11|\xb8^:4c>\
+\xf4$\n\x1d\xae\xc2\xcd5\xc1\n(k~\xbd\x82@m~\xc0n\xa8\x90\x08[M\xdf\xadz5\
+\x8f\x9c\x11X\xc8\xa8\xf9P\x9c\xb6*E\xceRJ\xcfGr\x99|8f\xdc\x8c"\xd6,K\x91zv\
+\x10{\x96"T\xb2\x92A2\xa1S\x1fv\xe1\x853\x92\xaai\xaf\xd4\x15CE\x07\x98}&s\
+\x86\x98\xdc\x87dk\x1a\x83\xc5\x8en\xf5\x0e\x94\x92rZl\xb16zo\xd8J~\xac/KL\
+\xf0\xe1\x95\xffq\xe6h\xe8\r\xcep\xc8\xe9`\xe66\x80\x00$\xdd;(\x06\x99\xa7\
+\xe4\xc3@\x1eC}\x82O\x17e[\x90f1\xac+\xfb\xf3`yy\xc5=A^s\xce\x80\xe8 \xcb\
+\x92\xaf\n\xba\xbeQ \xe0\x00;\x86\xe6\xf4\xaf\xc8\x8d\xac\xc0\xa0P2= \x19\
+\x88\x8d\xb1w_\x94~l\x8fi\xcb\xc7zr\x16=\xe9J\xe6\x87\xea\x06\xbf\xa6W\xb4\
+\xe1\x07Y\x14\xadL\xf2DcJj.p\xe3\x89\xb5v\xc1\'p\xf6\n]\xaa\x81-c\xcb\xf3\
+\x80\xc3\xcd\x13\xf5\xe3!\xec\x87 \xa0<\x11\xf7\xc6\xf2@\xa6\xdf\xf5W(\xea\
+\x9a\x9fs\xd7\x05~\xfa(\xa0\xcb\x1b\xca\x9d\xd0\x969zvg\x154\xd5\xd0\xad\x13\
+\x14\xbf\xf6nY\xfd:\xde\x08\x85\x8a\x03B(\r\xa9\x02\xad8\xad\xa3\x0bN\x89c\
+\xdc\x88\xfb\xb8\x17@\x0c\x08\xd7J\x8es\xc5\x88E\xad\x1a\x01\x10\xa1\x85k\
+\xf4\xda\xb5\x98\x08\xe0\xcd$\x92h@Y\xaf#\xb6\xcf\x0e\xa5\xd7\xd5\x90\x0e\
+\x0f:\xb6>\xe4\x11\x10s)\x02N}\x9d\x95f\r\xf4\xb3\x9dC2\xb5\xe9\x19?"\x83\
+\x00\xb5F\xdf>Hff\x90f[\xe6\xeb\x0c\x9c\x99\xa7\x82!\x87\x8c\x1dH@\xc4C%i\
+\xd2_\xb0\xdc\x16\x14ll\x98\x04\xd5\x99a\x14\xbc\xde)\xa5n\x93%\x84\xa9\xe4\
+\xc9Z\x91"=\xab\x8c\xe8\x06\xdaa\x98\x15N\n\xcb\x19+3\x8c\x02\x9aAe\x88\x19\
+\x19\xc4\x90\x13\x17m\x08S\xc4\xb8\x1c\xf9n\x1e\x17F\x0b\x00htz\x94D\x10@\
+\xab\xc7\x93d\xa6!\x98\xea\xd5\xfbD\xed,\xfd\xa6\xa2!\x84/\x1ck\x1cG5\x17f\
+\xa5\xcc\x8d\xaa\xb8NR\x99\xa1\xc1\xe9\x83UD\xa5\xbd\x83\xc8\x8b\xa4\xcd\xbf\
+"\x88\x96\x86\x92[0\x14b\x12\x02\xb0\\\xb3gX2\xdd\xd2\x0e\xb96\x88\x81\x97\
+\xd3\x07J\rlh{6\xf6Pf\xee\xfb\xa3R\xb5u\xac(\x93\x16:$\x06@Zz\xaa\xcbA}\x02Q\
+\xa1\xfe5\xf9\xd6\xd6A\xb7\x8fPt\x0f-\x13\x83\x11\x10q\xc0\xb90\xac\xa0fT\
+\x01o\xa5d\x07\x9e\x19{\xe3[\xc0\x00\x00\x0c\x15IDAT\x14\xa0DkL\x8a"\xd5]\
+\xf1a\xf2DV\x98\xf6>fU\xc4\xa83\n\xc5\xfc\xab\x1eA\xc2Hc\xad\xff\x17\xbe&\
+\x8f\xde-\xd8\xefAc\xe9\x07P\x14\xf0V\x89\xcc\xa1\xebu\xea"\xc61c\xa0\xfb\'\
+\xc2\xc2\xa2^UJ\xd8\xef\x04\xf4h:\x9eg\xb0\xe7\x08Wq\xa4\xbe/\x1b\xdf;\xd4~\
+\xc7\x93b\xe6\xc6w\xdf}\xe5<\xf5:\xb8QXB\xd0\x08R\xb5\x82}\x82\x95\x06\xaa\
+\xfb\x88K`;\x9cI\x8aD\x118\x89\n\x80\xf4\xb6eY\xa7\xc1\x8f\xec\xdd\x1c\xc3r\
+\xb2hU\x81\x8a\xb2Q\x8e~\x1cPH:\xc7r\xb6\x07M\x153\xa0U)Y\xd0_\xa9^\xf9\x18\
+\x82\xb6yY\n\x0e\xbaM\xc1\x0e\xe1N\xefP6N\xb0\xc3\x81\xb3%\xaa\xa8\x1c\xf7#\
+\x0c\xc6\x1d\xef[&\x02:\xe65kh\xc4\x0c\xfd\xf6".\t]\x17q\x04\x80\x83fEg\xf6D\
+P\'\x90\xbaM\x11\xaci2ey\xe2\x8b\x1d\x1c~9\x147\xe3I\xf5S\xef\x90QF\n6\x9fQ\
+\xe5\xb6}\xb4\xc4;>\xf8(\xfd\xee\xca$(\xc0\x08\xc2\xbat\xf1\xec3\x92\xfc\xbb\
+s\x06\x93\xdaeR\x19\xa1\x9c\xb2XD\xce\xf2Z\xb6>\x9c\xdfV\x8f\x9d\x19K\xf4v\
+\xaf8\xd4*\x17\xbaA\xa3\x81\xce\x16\xeb8\xa0B\xd4\x9d\x0c1\xcc\xd9\x1a&V\xcf\
+\xea5\xf8\xce\xdc\xfa\xf4R!\xde\xd8D\x9f\xfe(\xf9H\xfb\xa4\xad\x8f\xa0 *=\
+\xf6\x98|\xd1n\x0e\x11Q\xf88S\\6VY\xacz\x8e\x07j\x1c\x88\xba\xd3c]\x88m\xb6r\
+\x06\xd9c\xaf-3\x8c\x89\x88\xf3\xd9B:\xd3\xe8\x8a\x1a\xe9\xc4\x8fM8\x9f-\x17\
+-\x82\xc1(\xeeN\xd5\xb1\x0f\xc8\xa7\xb6_\xc9\x07,\x01\x10\x02R\xa6&s\x80\x93\
+=\xc7#>\xa8k\x00\x84\x15\xe0{\x10<7\xe6Rr\xd0\xd3>\xe6\xbb]\x05\x8c\xeeR\xa8\
+\xf3>\xee\x0c\xb6J \xcd\x8e\xb0"\xaf\xd2\x03\x19\xfeQ\x0c\x97\xb4A\xcb\xb2Kx\
+\xdcG\xe0\xe0(\xd7(\x13\xb3\x04\xe8\xd2\xa7t\xf2\x83\xbab\xf9\x99\xd4\xab\
+\xb8\xc5F)\xde\xb3\xdc\xeb\xf7B\x02\xd4\xe8\xcb\x1b\x1c\x81\xab\x00\xdc\xb9\
+\xb4{\x10e\x82\x03\x85\xe2\xbb\x14\x14\xd9p$\x90\xcf\xba\xb02Q#h\x87JP[\x87\
+\xd942\x82\xf2\xe0\x876\x08\x15N\xf4R\xa9\x83\xf9\xb8\x96c\x9c\x8cl\xbbV]fQU\
+\xa6\xaf\xb7abx\xb5Di\x82\xa8\xa8\xd8\x95\x02j\x9e}ATP(\x90\x14\x9e\xa0\x8b\
+\xff\xb5A{q\x95\xd2V\x9bc\xd4i\x99NP\x0e\x8cD\x0b,\xaag\xf7\xea\xde\xb1p\xbc\
+\xb0\x1d\xe6\x08H`&ex\x11\xc6\x86\xe3\xf2\xf0qc\xc0\xe3>B8\x10]\xd6\xcf\x18\
+\xb0\x92\x12\x07\x88\x9d\x1c\xa8T9\xe0\xb7<3\xfd\xd0\xf0>\x10mL\x8c\x94\x9a"\
+\x9d\x04e\xf4\x83\x10\xbfJV\x82&\xe54\x9d;\xcf3\xb2<8\xda\x83`\x84\xc5\xf8\
+\xe0\xd3\xa0%\x81\x96-G\xfb\x084\xb3i\x18\xa4\xda\xc1U\x0c:\r\xc7\xee\xd0\
+\xd0\x07rA\xf4\x83|*\x99\x06\x8au;Wg5$P4A2\x13\xaeJ\x1a\x8c\x85\xa4t2\x81\
+\x11\xe0\xc8\xb0P\xa2\x1b\x8clh\xf9\xc5{eu\xdf\x1d\xac\xae\xa2\xe8\x0bi&\x19\
+/\tc2Z\x01n\x0cp\xa4\\\'pQ\xee1"\xe0\xc0\xe2\x8d-\x0f\xa0\xc34\x8atU\xcfN\
+\xe2\x81l\x13\xe0D2\x80\xb9\xcaW%\x9c\'\x113(2m5\x9b\xfe\xe2k>\x9e\x89b Uu\
+\xd4\xb1\xc0\x94\xedf4\x0f\xb2\xd0E\xaa\x0c\xbd\xec@\x9d\xc8hKB"!\xa67\x9e\
+\x14\xb5\x9b\x08Z>\xae\xe4\r\xb8p5\x0e\x19m\x9e=Z\xa2\x8fb!}=ID*-\x93\xa1\
+\x1b\xa4\xdd\xcaX\x8a\xe1\xac[\xabFP\xf4\xab\xf1\xc8\xb4\xd8\x91j\xdeJ\x03>\
+\'\xacXL"\x87J\xa4\xfd\xa4\x8b!\x90\xe2\x18-\x8f.\x94x&\x07s\xb1\x00\x99\x00\
+/\xa7y\x98\x92\x94l\r\xd2rJ\xa2&`\xaf\x87\xdb\x84\x96w\xd9x\x87obp\xca\x15\
+\x8e\xa2*^h\x0e>JqYE\x0bpC\xc0\xa8\x15\x9e;\x88\xa6A\x81\x80UPH\x98\rL\xa9\
+\xfc\x81*\x00\x94b\x0c\xea$D\x97\xb2U\x99\x03\xa5\xf2Z7MZ\x04t~\x10\xb9}\xbc\
+\x189f\xc8\xc4\xc9\'T\xe2\x96\xe4\x97V\x84\x05\xe2\tRe\xa0\x16\xf3\xd4\'\xb0\
+\xbbD\x1aC\xe0\x10\x10\x93)\xb5\xb4FHx\x7fd \xf3_ \xd2\x15D\x15\x13R\x1c\xf6\
+R\x08^\xbc\xf7\x90@\x97\x9d\x9e\x88\xd1E\x01\xa9z\xdb\xba\xf3l\xe4J\xe3\xae\
+\x95\xe4\x08P\x14\x00f<\xc6\xea\xa7\x8d\xb1s\xa0\x06\n\x015WY\xd7\xea\xa7\
+\x89\x14\xc9\xdbF \x95\xf28o*j\x97\x18>\xa8\xeb\x83\xe8C"\xaa\xa8\x14\x9b\
+\xac\xe1\xc6+I%\x04\x08z\x08\n\\S\xa9\x01\x81\xa0V-\xf2\xa0\xd9\'\x13<\xea\
+\xb0\x94\xe3\xd5P\xed\xd6\xa8\xee-\xee\xa44\xe8\x01\x18f\xaf\xec\xf2r\xdf\
+\x84?\x00\x07\xb8[\x98\x03Q%\x1a\x12\x8a9\x85\x15\xe8\x11\x02\x08\x8aEe\xcb8\
+\xec\xa5\xfa\x88^\xafT\xaf\x92\xb8\xa2\xc7U\x15\xa1J{\xf8C\x11\xe8x\x9d\x8a\
+\x00\x1b\xce!\x85\xc0\x08-P\x9b]\xea\xd1x\x8d\xbfX\x05\xe0\xa0\x9a\x199\xe7\
+\xc3|TX&\x9a\xa9Qs+-\x8fv?\xd7\xdd=E\xd1\xf6\xba\x82\x80\xf2\xc8\x16\xa9\x1c\
+\x92\xa9Sk\x98j\x97\x05\x95\xa5.\x0f \xdd7\xad,\xf1\rK\x12P\xb2HF\x99\xa5\
+\x17\x81\xce\x1b\x12\x0bj9\xbd\xc6.4\xf1\xa9\x08s\x08\xf0\xc2\xa8\xbb\x9d\
+\x8c\xd7\xcb\xc8\xc0\x16\x83\xd8\x80/\xebv=\x87ZF\x01\x82\x12\x95Q&5\x0b\xe6\
+\xcc\xb2\xe8n>-\x94\xf3\x0cK\xe3\x19#\x80\x1a\x9a\x1a\x83\xe0\xc8\x8f\xf5\
+\x18\x9a\xe5\x80\xe3\x1b\x1dg\xbaW\x84N\x01QjX\x05\xd7Zu\xcePnN\x92\xaeB\xb7\
+\xc5Z\xe2\xd0\xaf\xf8\x1c\xe2\x8aB\xa5{\xa6\x171\xd3\xda?\xcc\x04\x0cQ\x15t\
+\xe6\xa0\xaa\xf95\x03]q\x81\xa9^\x8eB\x10\xeb\x8a+p\xe5\xb1%0\x8a\xd3\x8d\
+\x93\xf8*x\xa4\xc6usP\x19\xd8\xf9]\xaa\x95-\xe7>3\xca\xeb4\x84\xe1\x04ht\xf3\
+\x9578\x9ay\xe8\xa6\x8d\x8a\xd9*\xc0\x90\t\xfa\x14\xa1,\x08\x1d\xb6\x8a~\xa0\
+\x16\xcd\x11@v\xff\xafJ\xc8\xd0\xe9\xe4\x84\xc4\xc4\x9d\x17J\xa8h\x12\xa8\
+\x1bX\xd5\x82\xa2ai\xd0u\xb4\xc6`+\xc7|\xad\x19\\h\xf6\x9b\xba\xe3\xb5z\x80\
+\xe8\x14[\x91\xd1\xce\xd6\xfc\xb0Z\x1dH\xa8\x1a\xcf\xea\xd8\xbcv\x7f\x8d\x81\
+Jt\'O\xd2\xa5\xa7\xc5Z\xda\x86\xbb\x8f!x\x92\xdat\x97\xe5\x97\x06(\xb4\xab\
+\x83p\xf7\x15[\xa0\xdf\x94Ig\xee\xab<\xa4~\xd5U\xbe\x9e3[#\xb9\x13\xe8LKN\
+\xa6\x0e\xd7V\x02\xed}"\r\x1b4\xa6q\xc5\x17D\xe7\xf4\x97\x9a\xdb|\xe5k~\xc6g\
+Eqc\x9fW\x001P\x86\xed\xcc\x88|\xda\x1c\xca\xd1V\x1e\xd9\x13\x15\x8a\xca\x83\
+J\xd0\xbc\xa6\x14!l\x87\xca\xaf\x93?VV\xa3.6\xc1\xd3|\x02\x9c\x91F\xb0\x035\
+\xb8:\xaf\\\x86\x8c\x9d\x9d\xf1\xe9B\xad\x08Ylsv\xc2j\x1dN\xa7\xc7\xd2]\x05\
+\x1eh7\x91\xd3\x82+\x19\xf7e\x1f\xad\xc6\x9d\xcf\x8e\xf7l]Z\x0e\x1f\xb4M\xaa\
+8\x9c6^Y\x7fb\x9a\x9a~\xf6\x04\n)\xdb\xca\xa6e*\xbf\xd9\xbeHr/\x11\x8e\xa1\
+\x12\x10\x8a\xeeM\x1a#\xa9\x81\xe0\xce\x13.\xeba\xd0\x13\x01\xaa\x8b\xa6\xe8\
+$\xfbu5\xc2\xbb\xa6\xd4E\xfd\x86q&2O\xbe\'V\x07\xdcY\xb0\xcd\xbc*\xf3\x95\
+\xcf\xef\xa9\xab\xd9\xc5\x88\xeaT\x04\x98\x93C\x87#\x91\x9aO\xe0\xe0\x9e\xab\
+\x81\x01\xcb\xd9\xcb\xe6\xe8\xe8\xc7\x11O\xe0 4\x0b\x01AP\x07\xc8\xf3V\xea\
+\xcawz\xd56A\x7f\xb9\xa9\xfa%1\xb6R\x8d\xd2\xd98:&\xad/\xcb\x91\xe99\xa2\x12\
+\x11\xe0$\x02\xd8YaV\xdc\x81)\xca\x9cf\xab\xdd-\xc2H\x90\xb6%.\x1b\xc7\x07=\
+\xdb&k\x03\xa1\xe8w+;\x84\xd9S\xa6\xe6s\xe2\xc5\x80\n"?B\xcf\xa7s\xac\xe7\
+\xcf\xca\x01\xaf&i=\x99~\xa9\xe3\x06\x0c\nH\xa53\xff\x8c]\xe7\xbd\xa1\xb2\ra\
+\x81\xe9\x04l\x9a\xb4\x83\xa6\xdd\xff\x80]\xbc\xbcc\xcf\xf2\x19/\x1b\xc0\xf3\
+\xc4\xcc:D\x1a|\t\x92\x15X1r\xa5\x8eV\x95\xa8\xc4\x89\x842\xe2\xccR\xfe)\xc6\
+\xb1D\xe5\xb8\x1f\xad\xd0I\x82]\x84w\xa5fW#@\x94#%\xa5O\xa0\xae\xf3;v\xc8\
+\x0c\xdb3\xd6k\xf8\x9c \x08|\xd8l\xf5H\x0f(M?J\t\x9c\xbd\xe4re\' \x89\x9c.o\
+\x8e\x06\xb2Dm#@\xe8f\x11W<\xa0\xda+Q\xb5a\x00\xe0=\x03\x01\x1fz\x8e\xa6\x81\
+\n\x05B\x11\xc8\xee\xa0\xb0\x10\xa9\xcfQU\xc8\x05\x91\xd6\x03W\x0e\tW\xb1I\
+\x1et\xc3\x9dJ\xca\xf1*jh\xcb[\xde\x13\xf0*2\xe3\x9b$E\x85\xab\xe8n\xfd\xf8\
+\xa0\xa2)\x86\x08\x9a\x86"\xe4\xd9\x19\x97&f\xf3\x86$\x12A.\xb4\x10\xa4\xe7\
+\xcf\x1c\x83\x84)|\xadT\x1290\xa5J\xcc\x94O\x11\xb2\x84.v\xda\xcf\x9e6\xac\
+\xa8\x8d\x1c\xf6\xaer\xe2\x87\x08\x01\xe7\x92Hbm\xd4a\x10l\x11\xa6K\xba\n@\
+\x11Z\x0f\xf8x\xf0x\xb1\xa8\xf5\x1b\x90\xa8\xf5\xaaB\xb7\x07\x1a@\x10\x10~\
+\x19\n\xe8\x05dd\xacA\xcd\xb1R\xd2\x168\x00\x0fj\x11\xdc>\xa9\x90k\x9dS\xa9\
+\xe1u\x04pd\xb4\x91d]@\\g\x14@\x84\x89\xa6\xc5y!}t1\xc5\x87\x94A\x8cn\x94\
+\x1dT\x07\xe6\xc6\xaa+J\x92o\x13\xe8~\x96l\x93\x96\xcd2l\x02\n\xc9\x8f4\x86\
+\x80y\xd6\x82\x83G\x80\xc8\x00\x16\x99\x04\x86$z\xaa\xc4\xe1b!Sj\xa7`HA\x82a\
+\xc4 \xf8\xa8\xa6\xc7\xe51\x01\x87#\xbd$\x05\rn\x90\xf7\xc1.\xb4\xfa,\xe7\
+\xc6c"\xba%\xa7\x0cJ7\\!$\xafD\x11[\x83\xe8f\x84\x8d5\xd1\x01Y\xbaNgW\xba~\
+\x96\x9f\x88\x06\xb1\x03\x07\xaes\xf8K\x8f2J\x05\x91\xd5\x85<\x9aR\x11A4\x95\
+.\x16!\xd2\xe4\x15Q\x8ctY\x7fV/\x07E|Tj\x0ca)"?\x02\x07L(\x00b\xe3\xa7\xe4\
+\x02\x18\xd1\x1cR|=X1\xf5!K\x04\x88\xbaiG\xc8:\x11C\xae\xc2\x86\x87\x1798A\
+\xdc[\xd2Qw\x000\x90]#\x16;bC\x86\r\xc4\xa8\x85\x8e\xf1\xaa\x17)\x9aP"\x178v\
+@\xfew\xed\x1f)\x1eW\xd3\x10>\xa0\x08\xf3R\xe5\x87\x0eC\x08p\xe0R\x0f*\x15\
+\xca\x05\x85T\x97\x03\x14g\xf1\xcb\x96-\x9d\xf8G\x82\xa7\xffP\xbc\x87\xce14\
+\x1d\x00\'\xdeT"\x8c\xb6\x07\xecQ\x86HJ\x11\xeb*\xc2\x1e\x9a\xa19\xec\xa4\
+\xa3|\xba\xe2q\xc0\x96\x08\x98>\xe0\xdd\x80q#t\t\xcfH~\x83\xed\x15\xc8Cj\x00\
+\xff"\xa0\x98\x11\x88oQ\xb34\x02*\x06<$\xb1\xc9\xc6h\x9d\xacV\n\x00^\xae\x18\
+\x82\xeaw\x06\x12T\xfdx\xfc\x8b\xb3\x87\xf3{\xc2\x8e\xd8\x81\xe2\xa0r\xb4\
+\xe6\xe5H\xa2\xb8\x02G\xbed\xfc#\x8b\x89\t\xbe\x10\x91\xe6\xd9\x1b\x8a<\xc7T\
+\xd2\x1f*\xd2l\x1aNn\x84\xd3re\xcf\x8c/\x16i\x8cz^\xd2\xa3?\x89\x9a\xf3\xce\
+\xb7\xef;V\xd7}Q\xb5\x96\x11H%\xd9d^c\xab>\x16:Ee\xf6\'kM\x1b\x97\x8cd\xf6\
+\xa8\x8f#\x17\xba\xfc\x94\xed\x02\x95\x0bd!\xcaA\x19\x8eJ\r\xb4\x9b\xbd\x0e\
+\x81t\xb2\x08\xef\x8e\xd0!\xcc\xd6\x91\xda\x15\x93tV\x17\xe5\xed\xbc\xdf\xf4\
+\xda\xd9\xa4\xb4\xb3\xad\x01\x02\xa7\x9a.\xa0\xe5\xfb\x14\xf6\xa2\xff\x08\
+\xb7m,~\xbd\x0f\xfe\xb5\xc4L\xa2y\x96\x92\x02$%\xc4\xda\x1e\xb6\xb36\tZ\xbf\
+\xf7\x1cl\xaa\xe3\x95\x9cD\xfc,"\xc1\x01 \xf1&\xab\xdf\x89,\xec/\x00\xea\xbb\
+\x00\x91\x16\x07a\x11\x0f\x8f0\xad\xf7W\x14\x88\xa2a\xb3y\xac\x8a\xaap\xe5\
+\x80v\xa8\xb6\xae\x0bRl\x00\xd8\xb5.\xc8\xdcb\x85\x98\x81"\x8d\x1e\x81M\xf2\
+\x91\xc6\xc0]\xdf\xe0Z\x83\\\t\x14\x10E\x04\x05d\xf2\xb9&\xedf\xc3\xcey\x00\
+\xe7=\xd1\x05H\xf8DMd\x01\xa1&&\x08\x02v)\xe5\xe0t\xbc\x0ce\x83\xe7\x89\x8d\
+\xc0\xf1\xe6:\x83\xe2%h0Q\x03&\xee\xd2E\xd0B\xf1\xd1\x988\x02\x8d\xf2)("\xd7\
+^\xc8 \x86\xc5\xff\x075\x19\xcc\xa5\xaa\xbc\x13V\x00\x00\x00\x00IEND\xaeB`\
+\x82' 
+
+def getWizardBackgroundBitmap():
+    return BitmapFromImage(getWizardBackgroundImage())
+
+def getWizardBackgroundImage():
+    stream = cStringIO.StringIO(getWizardBackgroundData())
+    return ImageFromStream(stream)
+
+def getWizardBackgroundIcon():
+    icon = EmptyIcon()
+    icon.CopyFromBitmap(getWizardBackgroundBitmap())
+    return icon
+
+#----------------------------------------------------------------------
+def getWizardBGShorterData():
+    return \
+'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\xac\x00\x00\x01,\x08\x02\
+\x00\x00\x00\xd1\x94\xcf\xe3\x00\x00\x00\x03sBIT\x08\x08\x08\xdb\xe1O\xe0\
+\x00\x00 \x00IDATx\x9c\xc5\xbdy\x94^\xc7u\x1fx\xeb\xeb\xaf\xbb\x81Fc!\x16\
+\x02 \x01\x82 \xb8\x80\xbbH\x8a\xb4D\xca#\xd9\xa4\xe28\x92e\xc99\x91dO"\xebx\
+\x19\xcfd2\xb62\xc9x\xc6>>c\x9f\xcc\x8cg|&q\xe2\x938\xb6c;q\xecq$9\xe3\xe5\
+\xd8\x96e\xd9\x14\xe9\x13\x9b\xabDR$\x05\x92 \t\x80\x00\x01\x02\x8d\xbd\x81\
+\x06\x1a\xbd|]\xf3\xc7{Uu\xd7\xaaz_75E\xb0\xbf\xf7j\xb9u\xab\xee\xaf\xee\xbd\
+\xb5\xbc\xf7\xdc{\xbf\xf7\'A\x0b\x1e|\xba\xf6\xbe\x89\xe21"g\x13\x1c\xb8\xf6\
+\xc29\x9c\x9f\xe7\xf4\xbc \xa5\x12\x88\x04jF.#5Fg+\xc1\xec%\x0e\xf3\x8c!\xde\
+Pm\x8ep\x82/\x9d\x03\xd9\n\xbbM,\xa7\xec\xde\\\xf0\xa4\x88/6\x04\x00\x00z\
+\x16C\x99\xae\xd7\x11\xe0}\xfb\xaf\x92\xef:\xfe\xbe5\xc11\xc0Y\xc0\n\xb9\xd5\
+h\xb5\xc7\xbb"\x80\xe4q\x89%\x07\xce\xfa\x97\xa1\xec\x9cS\x07\t\x8b\xef\xb7\
+\xc5\x9c9bX\xdb\xf8\xa0\t\xb1\xe4\xda9\x0f\xde\x81\xd3\x91\xd8E\xfc\xcbU\x03\
++\x1bhu\x927\xef}\xcb\x92O<\x90l\x82\xb1&\xb5\xe9L\x87\xca\xa4\x0b_\xe8\x04\
+\x85M\x94\xdf\x83\xb7z)\xc6\xf7e\x1agH\x0bD\x01(\xc9\t\x07J\x91\x9a`\xe07\
+\x907HY,\xdb\x10\xaf\xe2\xa1\xa9N\x1d\xd3\x1d\xa8\xe1K\xc7:9\x07\x14\xab\xc2\
+\xba\x16a\x90Y!\x80@\xe4\x11\xf6\x1b\x00\xc0{_+\xcb\x80\x03%\xbe:\xe0\xae\
+\xc1p6\x15\x80A\xc5\n\x12L\x89ab\xe0\xab\xbc\x93J\x1e\xa2z/\xe6\x14)a\x0c4L\
+\x06d\xd7\x88Y\xad.\x16Q4\x01\xc9\x87\xcd\x7fD\x80!H\xcf:\x10\r\xa0L\xa9\x94\
+\x87\x96\x1f\xa6\xc7}\xae\x13y\xde\n8\xe2~\xaf\xe1\'\xa1\xd3\x14\xb1K\xa9\
+\x9e$XU[D<xE\xc3y\x9e\x95\xc7\xa3\x98\xa8\x90\xfa)-\x8a \xa4\xa5n\xc2\xa4\
+\xe5\xe8\xd1F{\x1aH]\x1d\xc0N\xa3\\\x86.8@\x854\x9f\x0e\xeb\xa1N\x08\xb0c*\
+\x83\xe9\xeey\x9e\xc7;\x0f\xdeP\x03\xce\xb8\x8e1\xc1\xdb05\x01C@\xab\x06*\
+\x10\x10\xe3Y\x0f\xc8\x9c\xbc\x8b\x9c\xcb \x06\x0f\xdcB\xe7J\x1c\xa8]\xe4\
+\x9c>q\xd5r\xaal\xd4\xf0\xa3 )?\xf3P\x9c\x83F/6ND`V\xea\x12J\xa5\xcah\x87\
+\xaaL\x9f\x00\x07\xd5\x15\xe84\xc2\x0b\x99#dl\xeb\xdbs0\xd2[Z\x18\xf4:\xd4\
+\xda\x12\xef\\\x023\xc0\x04\xdc\xdc\x16L\xc9\x8a\xccM\x1c\xb0a\x12\x86.B\x81\
+\xf3\xce\xbb\xbbo\xdb\xf5\x99O|\x08\x00\xfe\xf4\xb1\xaf?\xf5\xc2\xebc\xa3\
+\xadLg.\xcd\x8e\x8d\x8d^\x99_XZZ\x1a\xeb\xf7\xfb\xfd\x91\xcbW\xe6\xd4\xaa\
+\xcc\xd9A\xbcA\xd7\x9e\xfe\x92\xcc\xda|)?\xb6\xcd\xc0H9\xe7\xfa\xbd\xc1\x87\
+\xb6\xbf\xbde\xf5\xec\xd3S\xdb\x0f]\xdc\xd0\x81V\xdd\xbcWe 7\xbe]\x9a\xfd\
+\xf2l\x0e\x94x\x0f\xe0\x82ca\x18\xac\xf5\x93\x13[\xb7l\x98:u\xfe\xc2\xcc\xac\
+\xa5\xc9\x9dO\xf7\xb1\x01\x1b\xd7O~\xe0\xbd{\x01\xe0\xc9\xe7\xf7\xbf\xff\x9e\
+\x9b\x1f\xbco/\x00L\x9d>\xff\x17\x7f\xfd\xe2\xf7}\xd7\xb7=\xfe\xf47_x\xe5\
+\xd0\xb7\xdf\x7f\xeb\x9e]\xdb\xfe\xdd\x17\xfeRm\x0e\x07\x816\xe2\r7\xb0b\x88\
+!\x93\xc2\xb1Ri.\x9ds\xe3#\x8b\x9f\xbb\xf3\xf9\x8f\xef>\x00\x00\xff\xe0\xe6\
+\xd7~\xf1\xc5{\x1e=v}\xb6Lbq\xe8P4\xe7z\x06\x11\x97<J\x86\x03\x1a\xdes\xeb\
+\xf5\x9f\xfb\xecGn\xbc~\xfb\xdb\xc7\xcf\xfc\xfa\x17\xfe\xf2\xf1g\xf6Q\xaa\
+\x8d\'\x08A\r\x98M{\xe5\xcd\xa3\xb7\xdf|\xdd\xed7\xed\xf8\xd3\xc7\xbe~\xd5\
+\xfa5\xdf\xff=\x1fX\x1c,\xbd\xb4\xff\xc8\'?\xf2`\xa6-D\xbbZ\xcb\x8dt\t9C\r\
+\xc2\xaaa\xfa\x07\x1a\\\x94\x0e\x14t\xe3\xaa\xd6\xb7o;\xfa\xdd\xd7\xbd\xd5D^\
+5~\xe5\xef\xdf\xfc\xda\xf8\xc8\xa2\xc9AG\x04d\xd6\xbc\xdf\xa5 k\\5>\xf6\x99O\
+|\xe8\xb6\x9bv\x8e\x8d\xf6\xf7\\\xb7\xf53\x9f\xf8\xe0\xba5\xabeA\xd7\xf2\xd6\
+\xfe\xaa\\\x1e?un\xdf\xebG\xf6\x1f|\xe7\x85W\x0e\x01\xc0\xf3\xfb\x0e\x0e\x06\
+\x83\xef\xfb[\xdfv\xf4\xc4\xd9}o\x1c\xb5X\xea7|\x81\x8a\x80\xec\xa4\x80\xf2g\
+v\x1cn\xb3\x9a\xed}[\xa7\xee\xde|\xba\xb9\x9eY\x18\xfd\x93\xc37\\\x98\x1f\
+\xc59\xb7\xac\x9e\x1d\x1f\x19\xc4\xdb\r\xe3s\xa3\xbd\xc1\xdc@si;\x8a/\xa3\
+\xcc\xd2\xda_}\xc8g\'\xc6\x81L8G\xfa\xbd\x89\xd5c\xf1v||l\xf5\xaa\xb1\x0b\
+\x97fQ\xfe\x94;\xef\x116a\xcd\xc4\xaa^\xaf\x07\x00GO\x9c=y\xe6\xc2\x0f|\xec\
+\x03\xff\xe6\xb7\xbf\xbcg\xd76=\xb7\x97\xe6@,\x12\xe3I\x81T\xef\xcb\x0f\xef\
+\xdfv\xe2\xd37\xeeo\xae\xe7\x06#OOm\xbf0?\nH\x0cOOm\xff\xd4\x8d\xafo]}\xb9\
+\xc9\xf3\xd4\x89\xed\x97\x16\xc6\x14B\xdd\x07\xb0\x03e9+J\xa8\x80\x03k%\xc0*\
+\x82\xbdzj\x1f/]\xba\xf2{_zr\xf7\x8e\xab\xafZ?yyv\xee\xf7\xff\xfc\xe9\x93g/\
+\xc4\xd5\x92PX\xac#\x86\xa5y\x16{vz\xe6\xec\xf9\x8b~\xc9_\xbe2?u\xfa\xfc\x9b\
+\x87\x8f\xbfv\xe0\xd8\xd1\xa9\xb3\x1b7\xace<\xc4\xd0g<q\xa6\xf5\xe6\xa4\x06\
+\xb0\x82j\xb7\xd6\x87\x85\xa5^\xac"\x8e\x95C\x177\xfc\xfcs\xf7\x7f\xe6\x96\
+\xd76\xad\x9a\xfd\xc6\xe9-\xbf\xbc\xefn\x1f\xd7\xe7Q\xc5\xc3\x05\xbc\x82\xab\
+\xfaC\x95\xfa@\xdf\xa7\xa9f\xeb\xb1\xa7\xbfy\xe0\xc8\xd4\x9e\xeb\xb6\x1e\x9d\
+:\xfb\xc6[\xc7\x8b.\x93\x8b\xb8\x10n\xe9\xcb\xfb\xdf~\xf5\xcdc\xde\xfb7\xdf:\
+q\xf8\xe8\xa9\xb9\xf9\x85\x97\xf6\x1fY\x1a,\x1d|\xfbd\x9bG@\x81h\x82\xfc\xda\
+@Z\xd0\xeb\xb8\x8a\xc2r2\xb6//&\x1e\x16\x96F./\xf6\xd3\x92\xb3o;\xf7\xd9S\
+\xd7<{\xea\x9a~o\xb0\xe8G0\xc5\xd4\xa4\x8e\x01oC\xb0\x05|\x06\x85"\x0e\xf8\
+\x0e\xe4\xb0\xe1\xf0;\xa7\x8e\xbcs\xba!\x84\xaa\x07\x1e\x83jVG\xdc`0X\x1c,\
+\x82\x83\xc1`0\x18\x0c<\xf8\xa5\xc5%\x00\x98\x9f\x9fW6\xa8\x00 \x82@\xd2cva\
+\xfb\x9a\xd9]\x93\x17\xae\x9d\x9cY52\xb82\x18\xb98?v\xe0\xc2\x867\xcf\xaf\
+\xcd7l\xc3\xaa\xc1m\x1b\xce\xc4R\xc7f&_9\xbf\xe9\xc2\\\x1f\x00\xd6\x8d/\xee\
+\x9a\xbcpyqt\xe3\xf8\x95\x98\x7f\xb47\xd85ya\xa2\xdf\xfa}\xef\\\x9e\xbc\xb28\
+2:\xb2\xb4s\xcd\xc5&f\xa2\xbfp\xfa\xca\xea\xa9+\x93\xe3\xbd\xc5k&f&\xfa\x0b\
+\x97\x17G\x9b\xc8\x93\xb3k<\xdf\xe5\xf3\xd7\xaf\x9d\x8ey./\x8e\xca\xe9\xe5\
+\xda\xb1y\xc6\xe1\xe1\x99u\'.\xafa]!\xe7\x81c\xfd\xfe\x96M\xeb\xd7\xad\x9d\
+\x98\x9b[8v\xf2\xdc\xc2\xfc\x82\xd2~\x1b\x15\xdb\xb7\\\xb5n\xed\xc4\xd4\xa9\
+\xf3\xd3\x17/\x87\xbcx)\xc9\xfb\xb0F\x84\xc3\xaaU\xa3\x9b7\xac\x1d\x1f\x1f\
+\x9d\x9b[8~\xea\xdc\xe2\xc0\x8b\xa5E\xe7\x9b\xff\xc2\xa2\xb2\xdc\xac\x92\x86\
+Es\xaf(\xb8\xee\xdct\xf6\xa3\xbb\x0e\xbdg\xf3\xa9\xed\x13\x97\xb0\x8365;\xf1\
+\xd4\x89\xed\xbf\xb5\xff\xb6\xe3\x97VK_\xe1\xda\xc9+\x1f\xbb\xfe\xe0w^\xfb\
+\xf6\xe6U\xb3\x93\xa3m\x07\xcd,\x8c\x1e\xbb4\xf9\x95\xb7w\xfd\xc1\xa1\x9b>\
+\xbc\xe3\xf0\x0f\xde\xf2*\x00\xac\xe9\xa7\xee\x9b\x1c]\xf8\xb9\xfb\x9f\x99_\
+\xea\x01\xc0\xecb\xff\x7f\x7f\xee\x81o\x9e\xdd\xbci|\xf6g\xdf\xfb\xf4\x86\
+\xf1v\xa1\xe3K\x87w\xff\xfakwm\x18\xbb\xf2O\xee~~\xc7d\x0b\x8eS\xb3\x13?\xf3\
+\xec\xfb\xa7f\'\xdbFz\x00\x80\xeb\xd7N\xff\xfc\xb7=\xb1:@\xeaK\x87w\xff\xfa\
+\xab\t\x04\xe3#\x8b\xdf\xb7\xfb\x8d\xef\xday\xf8\xda53\x91\xc3\xb9\xc1\xc8\
+\xf1\xcbk\xfe\xf8\xad\x1b\xbe\xf0\xe6\xde%C\xc7l\xdc\xb0\xf6\xc3\x1f\xb8\xfb\
+\x91\x87\xee\xbcz\xd3\xfa\xb1\xd1\xfe\xfc\xc2\xe2\xc93\xd3\x8f>\xf1\xf2\x1f\
+\x7f\xf5k\xdb\xb7\\\xf5\x13\x9f\xfd\xc8\xc4\xea\xb1G\x9fx\xf9?\x7f\xf9\xc9&\
+\xff\xe8\xe8\xe8\x7f\xf3\xa9\x87\xef\xda\xbbk\xff\xc1w\xfe\xf5\xef\xfc\xf9\
+\x1d7\xed\xf8\xe4\xdfyp\xef\x9ek\xc7F\xfb\xbf\xf5\xfb\x7f\xf5\xff~\xf9)p06\
+\xda\xff\x91O>|\xd7\xde]GO\x9c\xfd\xd7\xff\xf1\xcf\x82W\x98\xd6]wl\xdb\xf8\
+\x91\xef\xb8\xf7\x81\xbbn\\\xb7v\x02W\xfa\x07\x7f\xf1\x8c\xcee\x13\xd0\xdap{\
+\xab\x05\xd3\'p\xce9\xf0?r\xeb7?\xb9\xe7\xf5\xd8G8l]}\xf9\xe3\xbb\x0f\xdc\
+\xb8~\xfag\x9e}\xff;3\xab1\x91\xbb7\x9f\xfb\x99\xfb\x9e\xbd~\xed\x05Vdrt\xe1\
+\x96\r\xe7n\xd9pnrtafa4\xfaz8\\\x85\x14C\x13&\xfa\x8bW\xaf\x9e\x8d\xf1\x8d\
+\xe685\xb7\xee\x8d\xe9\r\xf7m\x99\x8a\xcc<\xb8\xed\x9d?<ts\xd3\x98&\xbco\xeb\
+\xf1\xc8\xc3\xdc`\xe4\xc9\x13\xdb#\xcd\xddk\xcf\xff\x93\xbb\x9f\x8f\xc5c\x18\
+\x1f\x19\\\xbf\xf6\xc2\x8f\xdf\xf9\x8d\xeb&/\xfe\x8b\x97\xee\x93\x0b\x94\xf7\
+\xdd\xb9\xe7s\x9f\xfd\xc8m7\xed\xc4\x91;\xb6m\xba\xf7\xf6\x1b\xee\xbd}\xf7\
+\xf3\xfb\x0e5\xeb6\xfb\x0f\xbe\x13S{\x0e\x1e\xb8\xeb\xc6\xdbn\xda\xb9~\xed\
+\x9a\x8f\x1f?\xf3\xd9\xbf\xfb\xa1\xab7\xado\x92\xae\xde\xb4\xae\x11\xccH\xaf\
+\xd7\xe4\xd9\xbd\xe3\xea\xdf\xf8\xe2\xa3\x17.\xcd\xe2\xb1\xf8\xa1\x07n\xfbG\
+\x9f\xf9\xee\x1d\xdb6\xc9J\xf7\xee\xb9\xf6\xb5\x03\xc7d7\x92M&#\xc4<\xfd\x8c\
+M\x1d\xe9-\x01@D\xc0\xd4\xec\xc4\xd1\x99\xb5\x00\xb0c\xf2b\x94\xdf\x1d\x1bO\
+\x7f\xfa\xc6\xd7\x7f\xf1\x1bwC\xb0)7m\x98Q\x11\x10\xc3[\x17\xd7=z\xf4\xba\
+\xf7m=nV,\xc2\xfcR\x12\xc6\x95A\x1f\x00\x96\xfc\xd2\xa3Gw~t\xd7\xc1\xc8\xde\
+\xfd[\xa6\xfe\xec\xc8\rq\xea\xe8\xc0?\xb4-U\xf1\xcd\xb3\x9b_9\xb7\xb9\xb9\
+\x9e\x1c\x9d\xfb\xe9{\xbfv\xc7\xc6\xd31ujvbv\xb1\x8f\x95\xd6\xc7w\x1f82\xb3\
+\xf6\xf3o\xde\x8a\xd9\xb8\xe1\xbam\xff\xeb\xff\xf0\xf7\x1aa\\\x9e\x9d{~\xdf\
+\xa1CGO\xf6Gz;\xb7o\xbe\xf7\xf6\xdd\x8f<t\xd7}w\xdc0\xbf\xb086\xda_\x1c,\xe1\
+\x82W\xe6\x17\x00`\xcb\xc6u\xff\xe8\x1f\xfc\xed\x89\xd5\xe3\x17ffO\x9d\xbd0>\
+\xd6?y\xa6\xed%\xef\xdb<\x97f\xe7\x00\xf0l\xc0\xdf\xb5w\xd7O\xff\xc3\xbf\xbb\
+nr5\x00\x9c\x9b\x9e\xd9\xf7\xc6\xd1\xb7\x8f\x9f\x06\x80[n\xb8\xe6\x8e\x9b\
+\xaf\xfb\xd8\xc3\xef}\xdf{n\xe2\xfd\x15t\xa1\x93\x1e\xb4\x08\x0e\x9c1\xdb\
+\xf6\x00\x00\x0b\x83\xdeo\xbcz\xe7D\x7f\xf1\xa3\xbb\x0e>z\xf4\xba\xdf;p\xf3\
+\x81\xe9\xb5\x00\xb0g\xfd\xc5\x1f\xbd\xf5\xe5\xef\xb8\xb6]|x\xdf\xd6\xe3\xeb\
+W\xdd~a\xae\xef=\x8c\x8f,\xfd\xe8\xad/c\x04\xcc,\x8c~\xed\xe4\xd6\xe9\xf9\
+\xf1\x9d\x933\xf7m\x99\x9a\x9a\x9d\xf8\xe9g\x1ez\xe3\xfc\xe4\x96U\xeb\x1f?\
+\xb6cz~\xfc\xd6\xab\xce\xde\xb2\xe1\\\x93\xb9\x19\xaf\xd3\xf3\xe3\xcd\xed\
+\xe9+\xca\x9aI\x0c\xfb\xa77~\xed\xe4\xd6\xc8\xc6\xfdWO]31\x13\xad\xfe\xf5k\
+\xa7\xb1\x98\xff\xe8\xd0\rqP\xfc\xf0\xde}1ifa\xf4\xdf\xbfv\xfbc\xc7v^Z\x1c\
+\xbdm\xc3\x99\x7fx\xc7K\x91\x99O\xdd\xf8\xfac\xc7v\xb6&\x06`tl\xf4\xc7\xbe\
+\xff\xc3\r\x02\x0e\x1c\x99\xfa\x85_\xfb\xc3\xe7\xf7\x1d\x840\x9e\x1e~\xf0\
+\xce\x1f\xfd\xf4\x87\xf7\\\xb7U\xed\xe5\xe6\xa2\x11\xe4\x1f\x7f\xf5\xeb\x7f\
+\xfa\xd8\xd7\x0f\xbd}r\xf5\xaa\xb1\xd9+\xf3`y\x94\x1e\xc0\xc1\xba5\xab?\xf7\
+\xd9\x8f4\x05\x9f\xdfw\xf0W\xff\xd3_\xbc\xb4\xffH\x93>2\xd2\xfb\xde\x87\xef\
+\xc7z\x85\xd5\xaa_\xb3*\x00\xa01\x07|\xe3\x18\x85\x81\xf7\xbf\xbc\xef\xeeG\
+\x8f\xee|\xf9\xcc\xc6\x18\xf9\xe6\xf9\xb5?\xff\xc2\x03\xbb\xd7]h\x84\xbdy\
+\xd5\xecm\x1b\xce<ub+\x00\xdc\xb7\xe5t\x94\n\x00\xbcuq\xdd\xbf|\xf1\x9e\xe7N\
+m\x9e[\xecmX=\xf8\xf0\x8e\xc3\xaf\x9f\xbf\xea\x8d\xf3\x93\x00\xf0\xd4\x89\
+\xadO\x9d\xd8\xea\x1c\xfc\xd8\xed\xfbb\xbf/,\xf5~\xe9\xe5{\x8e_Z\r\xd0\xc08\
+\xe7o/\xfa\x91\xc7\x8e\xed|p\xdb\xf1\xc6S\x99\x1c]xd\xc7\x91h\xf5\x1f\xd9q$z\
+0S\xb3\x13O\x9flm\xc1\xee\xb5\xe7\xbfk\xe7\xe1H\xe4\xdf\xee\xbb\xab5"\x00\
+\xcf\x9e\xba\xe6\xd4\xd7\'\xfe\xdd\x07\x1fm\xf4\x0111\x00w\xddr\xdd\x7f\xf5\
+\xc0m\x00p\xf2\xcc\xf4\xcf\xfe\xd2\x17_;p\x14\xb3\xf7\xd5\'_>;=\xf3\xcf\x7f\
+\xea\x07\x1b\x81\x81!\xdaG\x9fx\xe9\x17~\xf5\x0f\x17\x06\x03\x00\x98\x9eQ\
+\xac!\x0b\x1f\xfe\xc0\xdd\x8d\xe99pd\xea\xe7~\xe9\xf7N\x9e\xbd\x18\x93\x06\
+\x83\xa5?\xf8\xca3g\xcf_\xfcg\xff\xf8\xd3q\xd3\x08\xe8\xa4\xd7"\x9b\x8e\xa5\
+\x00@\xf2\t\xe8\xa6\x10\xa68?\x80}g7\xeeY\x7f\xf1=\x9bOn^ue\xa2\xbf\xb8jd\
+\x11\x00\xa2\xc35\xda[Z76\xdf\\\xc7\xb5?\x00\x98\x1b\x8c\xfc\xe6\xab\xb7?y\
+\xe2\xea\xe6\xf6\xfc\xec\xc8\x7f~\xf3\x86b\xb3\xe3\xd4 \x06<\x87l\xb9\xf2\
+\xbeqw\x9e>\xb9\xfd\xf8\xe55Q\xf1<\xb4\xed\x9d\xdf~\xfd\xb6\xb9A\x7f|d\xf1\
+\x81\xab\x93\xbd\xff\xd2\xe1\xdd3\x0b\xadvy\xcf\xe6\x93\xd1\xbd\x98\x1b\x8c\
+\x00\xc0\x03[\x92\xfd^76\x7fiq4\x1a\x85\x9b\xd7\x9f\x8fI\x0f\xde{K\xd3\xd7_}\
+\xf2\xe5\xd7\x0e(K\xb0/\xbcr\xe8\xaf\x9e\xd9\xf7\xb1\x87\xdf\x0b\x00\xfd\x11\
+e\xb7\xf3\xf2\xec\xdc\xef\xfd\xd9\x93\r\x02p\xb0VV\xfa\xbd\xde\xde=\xd76\xd7\
+a\x05\x89\x87\xbfz\xe6\x95g_|\xb3qDb(\x1c[\xa2\x87\x16=\xf8~fm\xa7\xd9\xbci\
+\\\xe88Xe\x18\x1f\x19\xac\x1d\x9bw\x0e\xc0\xc3\xee\xb5\xd31\xfe\xfc\xfcx\x1c\
+\x7f\x82\x994\x13\x92Ro\xea\x86\xe2\xba\xa4\x87\x99\x85\xf1\xc7\x8e\xed\xfc\
+\xa1\xbd\xedv\xcb\xf5k/\xdc\xbd\xf1\xe4\xb3\xa7\xae\xb9q\xdd\xb9\xeb\x03\'\
+\xcc%\xdc\xbc*9\x9e\xe3#\x83\x9f|\xcfs\x99\x1a\xd6\x8f\xb5S\x92\xdeH/\xfae/\
+\xbe\xfa\x96\x95\xff\xc9\xe7^k@\x00\xa0l\'\x9e\x9d\x9e97})\xd7"\x1a\xc6\xc6G\
+wl\xdb\x08\x00\xf3\x0b\x8bo\x1e\xd6\\(\x07\xe0\xe1\xd5\x03G\t\x08\x9c\xe1\
+\x04\xd8+\xcf=\xb0\xf7\t\'G\xe7~\xf6\xbe\xa7~\xfc\xceod\x10\x80C\xaf\x07\xeb\
+\xc6\xd2<\xe2\xfc\xdc8\xe1`\xe5\xf6e\xb0\xcb\xfb\xe8\xd1\xebf\x16F\x9b\xeb\
+\xf1\x91Ac\x8c\x1e\xdcv<\x8e\xe6\'Ol?<\xb3.\xe6\xd71g\x84\xd8\x1c\x07n\xd5\
+\xf8\x18\x00\xcc/,\x1e\x9d:\xabdu\xd0s\xbd\xb3\xd33(\x82\x1f\t\x9f\x9b_\x9c\
+\x9b_\xd0\x8f\x8ak\xa1\xdf\xeb\xad\x1a\x1b\x05\x80+s\x0b\xe7\xa6/\x81\xd7\
+\xa5{\xe0\xf0\t\xccF\xfbW\xfeCW)gS\x91\xda\x9e\xa6\xb2\xef\xbfq?3\xf0OOm?23y\
+q~\x0c\x00~\xf8\xd6}l\n\xb0\xe4\xa1Y\xf6o\xc2\x86\xf1\xb95\xfd\x85\xf30\x02"\
+T\x1d\xd9\x93\x87\x93RJZ\xbdy\xeb\xe2\xfaG\x8f^\xd7l4\x03\xc0{6\x9f\xda\xbaz\
+\xe6\xeeM\xc9*}\xed\xd4\xd6h\x0b\x80\x1a\x97\xb9\xc1\xc8[\x17\xd7\xcd\xa8;\
+\x11\x00\xe3#\x837\xa6\xd3\xba\xc2\x95\xb9y\x00\x18\x1b\xedO\xac\x1a\x03\xa6\
+r]\xcb\xd5\xc6\xf5\x93\x85F\xa1\x90Ybg\xf1\xab\xc6G\xc7\xc7G\xdb\x19\x83\'C\
+\xd9\x03l\xdc\x80\x97\xec\xd0\xb9>R\x17&\xceUA?s\xbc\xe2\xa1m\xc9X>wj\xeb\
+\xff\xf6\xdc\x03q)\xad\xe7\xe0\xe3\xbb\x0f\xcay\xe0\xcbg7G\xdcl]}\xf9;\xaf}\
+\xfbw\xf6\xdf\x04X\xff\xc7\xbdJ\x03\x07\x13\xcd\xdaQ\xe9,J\xc4\x81\x07\xf7\
+\xf8\xb1\x1d\x11\x04\x9bW\xcd\xfe\xc4\x9d/\xc4E\xa4\xb7.\xae{\xf2\xc45\xb8\
+\xe0\xa1\x0bI+\x8c\x8f\x0c\xfe\x9f\xd7\xf7Z6\x0b\x00\x16\x96Z\x04\x0f\x96\
+\x06GO\x9ci\xae\xef\xbf\xeb\xc6f^\x80C\xd3\x96\xbbo\xbd>qXw65\x13f\xe7\xe6\
+\xdf9y\xae\xd9b\xbe\xeb\x96]o\xbcu\xdc\x073\x19\xaa\x00\x00\x7f\xef\xed\xbb\
+\x11\x1bh\xa8\xb3.L\x0eA\x92\x7f#\x8e\xdci\xad\xabW\xa7\xdd\xcc\x17\xcfl\x8e\
+\x93%\x00\xb8\x7f\xcb\xf1[6P\xad\xe8\x1c\x00\xbctfs\xe3m5\xe1\x87\xf6\xee\
+\xfb{7\x1e\xdc\xb0j\xb0~\xd5\xe25\x93\xb3?}\xdfs\x7f\xfb\xba\xa4Z\xe48\x98\
+\x1c]xp\xdb\xf1\xb1>\xf4\x1c\xac\x1b_\x1c\x1b\xa9\xda\x18x\xe5\xfc\xa6\xe7Nm\
+\x8d\x14\xbe\xe3\xda\xa3q\x19\xe3\x1b\xa7\xb7`\xb6\x01\xe0\xe5\xb3\x9b\xa7f\
+\'\xe2\xed\xc7w\x1f\\\xd3_\x98Y\x18o\xfe\x01\xc0\x7f\x7f\xfb\x8b?\xbcw\x1f\
+\x00\xcc,\x8c\xa7\rk\x0fO>\xdfnu~\xf7\x07\xef\xb9\xe6\xea\x8d\xe8pz\xfb{\xf3\
+\r\xd7<\xfc\xe0\x9dEn\xf1I\x8bL&\x00\x98\x9f_|~\xdf\xa1&\xe2\xa3\xdfq\xef\
+\xd8\xd8\x18\x1e\x1c\r\x02\xee\xde\xbb\xeb\x81\xbb\xd1:\x01V\xfb\xdc\x1c\x10\
+nq]\xb9e\xe3\xe9\xf9\xb1\xe8H?\xb4\xed\x9d\x17Oo><\xb3n\xa2\xbf\xb8g\xdd\xf9\
+\x1f\xbeu\x9f\xba\x8c\xf8\xda\xb9\xf5_>r}\x1c\x97\x93\xa3\x0b?\xf9\x9e\xe7>\
+\xb9\xe7\x8d3WV7KL\x1f\xbc\xe6\xd8\x85\xf9\xb18e\xf0\xe0\x99\xf3\xff_\xdf\
+\xf4\xda\xee\xb5\xd3\xcd\xba\xc2\xaf\xec\xbb\xf3\xe5\xb3\x9bx\x1d\x91\xcd\
+\xa0\x0cf\x16\xc6\x9f8\xb1].\xff\xcd\rF\x1e?\xb6\x83EN\xcdN~\xf1\xcd\x9b\x7f\
+\xfc\xceo4\xb7\xf7m\x99\xfa\x85\xf7\xfd\xcdW\xde\xdeujv\xf5\x96\xd5\xb3\xdfy\
+\xed\xd1f\t\xe1\x8e\x8dg~\xfd\x95\xdb\xbfvj{\xf4?^}\xf3\xd8\xa3O\xbc\xf4\xc8\
+Cw\xed\xd8\xb6\xe9\xe7~\xe2\x93\xbf\xfa\x9f\xfeb\xdf\xebo\xcf/.\x02\xc0\xf8\
+\xd8\xe8m7\xee\xf8\xdcg?\xa2O\xd9-\xfe3\xa7\x19\xc0;\xef\x00\xe0o\xbe\xfe\
+\xea\x0f|\xec\x03;\xb6m\xba\xed\xa6\x9d\xff\xd3\x8f|\xcfo|\xf1\xd1\xa93\xd3\
+\xb1\xd8\xdd{w\xfd\xcf?\xf6\x898)\x05\x00\xba=\x97\xd9\xd1\xf5\xf8?\xfb\xb9\
+\x03\x07OOm\x8f\n\xff\x96\r\xe7\xfe\x8fo{\xf2\xd8\xa5\xc9fa\xd5(\xe2\x16|\
+\xff\xb7\xf6\xdfv\xe3z\xb2Ps\xfd\xda\x0b\xb1\xc8U\xe3W~\xee\xfeg~\xf1\xc5{\
+\xfe\xfcH+\x9e\x17Oo\xc64\xa2s\x07\x00\x00w\xe6\xed\x82u\xe6\xa0\toL_\xf5\
+\xfc\x19e\xf5\xe6\x0f\x0e\xddt\xe7\xc6\xb4\x9e\xd1\xacd\xb3<wl<\xfd\xf1\xdd\
+\x07\x9e?\xb3u1X\x84\xcb\xb3W~\xed\xf3\x7f\xb9w\xcf\xb5\xcdz\xed?\xff\xa9\
+\x1f|\xf6\xc57\x8e\x9f:\x0f\x00\xbbw\\}\xef\xed\xbb\'V\x8f\x1f=q\xa6\xd9P\
+\xc8\xf0l5\x84\xc7\x80\x07\x80\xe3\xa7\xce\xfd\x9b\xdf\xfer\xb3\x12\xf0\xb1\
+\x87\xdf{\xfbM;\xff\xfak\xaf4\xeb\x8c{\xf7\\\xfb\xed\xef\xdd\xdb\x1cA\x98X\
+\xdd:=b\x9cG\xddo\xcd\x19\x00\x88c(\xf2|\xe1\xcd\x9b\xef\xd8x&\x8a\xb3Y\xf9o\
+\xae\x1f?\xb6\xe3\x9a5\x97\xe2\xed\xaa\x91Ats\x8e_\x9e\xf8\x99g\xdf\xff\x13w\
+\xbe\x80\x9dJ\x1c\xae\x1a\xbf\xb2{]\x82\xd1s\xa76\xff\xd1\xa1=Qy\xc4\x10\xcd\
+\xcaD\x7fq\xac\x97VaW\x8d,:X\xf2\xc1\x90588tq\xc3S\'\xb63"\x8f\x1d\xdb\x11EH\
+(/\xf5\x7f\xfe\x85\x07\xa6f\xd7\xc4\xc3,2<~l\xc7/\xbd|\x0f+~\xf0\xed\x13\xff\
+\xcb\xff\xfd\xbb\xff\xe3\x0f}\xf4\xde\xdboX7\xb9\xfa\x91\x87\xee\xc2\xa9\x8f\
+>\xf1\xd2\xa3O\xbc\xf4\xcf\xfe\xf1\xa7\x19)\xe7\xa0q\xf2\xc7\xc7(8\x90t\xa2\
+\x9f+\x01\xf4\xf8\xd3\xfb\xfe\xd5\x7f\xf8\xd2\x7f\xfb\x03\x7fk\xdd\xe4\xea=\
+\xd7me\x8b\x92\xcf\xef;\xf8\xdc7\x0f\xfe\xe8\xa7\x1eQ[\xe1\xe8L*UJ\xf5\xd0\
+\xc8\xf6[\x1e\xc4i\xa94\xc0\xa5\xc5\xb1g\xa7\xb6\x02\xb8-\xabgG{K\x03\xdf\
+\xbb2\xe8\xbfsy\xf2\xb7_\xbf\xf57_\xbbc\xfb\xc4\xe5\xd1\xde\xd2\x89\xcb\x93\
+\'g\'\x9e=\xb9\xf5\xe0\x85\r\xd1\x0f\x9aY\x18\xfd/\'v\x1e\xba\xb0\xce\x01L\
+\x8c.\x02\x80\x07we\xd0?7\xbf\xeak\'\xb7\xfe\xab\x97\xee\xf9\xb3\xc3\xd7\r|\
+\x9by\xc9\xf7\x9e9\xb9ufq|\xeb\xc4\xe5~o\xc9\x83k*:9;\xf1\xe7o_\x7fnn|U\x7f\
+\xe9\xfa\xb5\x17\xe6\x97FN\\\x9e<77\xfe\xfc\xe9\xab_>\xbb\x85\xf6\xb2\x03\
+\x80\xf3sc\xd7\xae\xb9tqa\xaca\xe9\xed\x99\xb5\xffq\xffm\x97\x16\x85\xe7\xef\
+\x00\x00\xe6\x97\xfa\xcf\x9e\xba\xe6\xa5\xd3\x1bW\x8d\x0c"\x87\x03\xdf\xbb\
+\xb80\xfe\xca\xb9M\xbf\xb2\xef\xae\xff\xf0\xfa\x1d\x17\x17V\xc9\xb2\xa7\xcf^\
+x\xec\xc9}\xa7\xce^\xf0\x1ez=\x07\xe0\xa6/^\xde\x7f\xe8\x9d\xdf\xf9\xa3\xff\
+\xf2\xab\x9f\x7ft\xed\x9aU\x1f\xff\xf0\x03\x00\xf0\xcd\xd7\x8f<\xfd\x8d\xd7\
+\x9b\x0e\xe9\xf5z\xb7\xde\xb8\xa3\xdf\x1fy\xf3\xad\xe3\xcf\xbc\xf8\xc6\xcc\
+\xe5+\xb2\xc3]\xafw\xd7\xde]\xfd\xfe\xc8\xfe\x83\xc7\xfe\xea\x99}W\xe6\x16"\
+\xab\x1e\xfc\xabo\x1e}i\xffa\x0fp\xd5\xfa5\xce9\x0f07\xbf0uf\xfa\x8b_z\xf2_\
+\xfc\xe6\x9f\\\x99\x9b\xbf\xed\xa6\x9d\xe7\xa6/=\xf1\xf5W\x8f\xbcs\x86;\x01\
+\x02\x01\xe9Y\x95p\xe5\xee\xfd\xd8?\xb5@\x10Kn]=\xb3k\xf2\x02\x00\\Z\x1c=<\
+\xb3\xaeq\xa0\xf0i\xcf\x81w\xcd\x86\x9b4B\xdb\'.o^5\xbb\xa6\xbfpiq\xf4\xf4\
+\x95\xd5l\xbf\x11\x875cp\xe3\xbas\xcd\xcerSQs\xf2\x00\x00F{\x83\xb8\x067\xf0\
+n~\xe0\xd4S\xe1\x91\xa5\xd1\xde`aiD9\x87\xa8u\xcd\xd6U3\r\x87\x00p\xea\xca\
+\xc4[\x97\xae\xf2\xd6.r\xb3\xe3\xef\x9c\x03\xe7zn\xfd\xe4\xc4\x86uk\xe6\xe6\
+\x17\xce\x9c\xbb\xb8\xb08\x00\x80\xef|\xff\x1d\xff\xd7O\xfe}\x00\xf8\xad\xdf\
+\x7f\xfc\xdf\xfe\xeeW"\x93c\xa3\xfd\x91~oi\xb0te~A\xbe\xec\xa1\xa1<6\xda\x1f\
+\x1d\xed/.\x0c\xe6b\x9e4\xa5o\xaf\xb6m\xd9\xb0m\xcb\x86\x8d\xeb\'\xcfN\xcf\
+\x1cz\xfb\xe4\x85KW\x1c\xc0H\xbf76\xda\xef\xf7zs\xf3\x8bd9\x92\xd7\x13\xa5O\
+\x1c\x020\x9f@\xa2\x07Z\xa6f\'\x99\x8f\r\x00\xb1\x8b\xc9\xb3A\xc2\x199~iu\
+\xbb\x17P\xaa\xe5\xd2<\xbcx\xfa*\xfa\x10j{\xb1\xb04\xb2\xb0\x94b\xad\xe7\x02\
+\xe6\x06\xed\x93\xf6sK\xc6\xee\xa8vPg\xea\xca\xe4\xd4\x95I\x92I\x1aP\xb4\xfc\
+\x17O \x9e\xbfx\xe9\xfc\xc5K\x98\xe1x\x98\xb3\xd9\xde\x8d\x9b2s\xf3\x0b0\x0f\
+`\xa0?\xe6\x99\x8b\'S\xb8\nog\x9b\'N\x9d?q\xea<\xc4\xc3\xc6\x0e\x00\xdc`\xb0\
+4;\x98G1V\xb3\xcd\xd0\xcb\xe51\x96\xa8\x96\x19<\x83\xa8\xe7\xa9#=\x88K\xef\
+\xe1\xbd\x17<(\x8f|\xd4/J\xd64J\x9bV]\xbbu\xd3\'\xff\xceC\x13\xab\xc7\xd5\
+\x8a\xb6m\xd9\xf0\xdd\x1f\xbc\x07\x00N\x9e\x99>p\x84LU\xaa\xa6\x85\x05\x96\
+\xf5\xe2\x1e\xe4\x88\xa7\xa9\x15\x95\xe6\xce\x13T\x86\xdcC}\xe6\xa3\x8a\xf6\
+\xad\x83A\xab\x8d\x9dRZn-\xae\xdcj\xb4\x12\x10\xf1\xc9\x89\xd5?\xf2\xa9G>\
+\xf6\xf0{\xef\xbd}\xf7\xaf}\xfe/\x8f\x1c?\xbd\xb4\xb4\x04\x00\x1e|\xcf\xf5\
+\xae\xbbf\xf3O\xfdw\x9fh\xf6\x17\xbe\xfa\xe4\xcbG\xde9\x9d\x1a\xa5M\xcd\xb3u\
+V\xec\x9bD:\xce\x038z\x81\xab@\xebJvGu\x9e\xcc\xd4\x86\xa1\xdfF\xe3!\xb8\x8c\
+>\xa8\xfd\x90\x14\x8ft\x96\x9e\x00o\x93VB\x8d\xc5\xba\xde\x7f\xcf\xcd\xcd\
+\xe6Psx\xe4\xaf\xbf\xfe\xdak\x07\x8e\x9d=\x7fq\xe3\x86\xb5;\xb7oz\xf8\xc1;\
+\x9bE\x82W\xdex\xfb\xf3\x7f\xf27K>Mg\x9c\xd7\xb8\xa4\xab\xbbrm\x11CAy\xc4\
+\x0f-G\xc6\xe5\xe4pT\x1e\x1365n[\x87w\x1e\xbc\xbb\xf7{\xfe)a\xab{\xc7)/\xb0\
+\xc9\xbe\xc0\x80\x9b\x03\x94\x9e\xf8h\xffp\x108\xfc~(d\xa71\x1b\x9c\x9e\x1a\
+\xb2\xfaCy\xc8\x1c\xdc\x86uk>\xf9\x91\x07\xbf\xf7\x91\xfb3+B\xcf\xef;\xf8\
+\x7f\xfe\xca\x1f\xbeu\xec\xa4\x95A?\xeegy\xe5\xd9\xcd@J*\xbe\xd2!\xc2\x00\
+\xf75#I\xfcCHSD0\xfb\xa5\xe9\\\xd9/\x95o\xc6\x02\x0b\x12918\x05\x04\xcc\x1f\
+\x8c\x8e\xfa\x10\xef\x000J\xb8\x100\x1f\xed\x8e\x9f\x83+\xf3\x0b\xcf}\xf3\
+\xe0s\xdf<xa\xe6\xf2\x92\xf7##\xbd\xd1\xd1\xfe\xc8Ho~a\xf1\xcc\xf9\x8b/\xbc\
+\xf2\xd6\x17\xfe\xf4\x89_\xfe\xdd\xaf\x9c<3\r\xa0\x0f\xbb\xd46\'\x92\xe4\xfa\
+.h\x91\xdc\x12&\xf7\xd0\xb1T>\xd4\x1cK\xc0\xa1\xcf\xf3`:uo\x0b\xe0\xb4\x1b\
+\xff\xc0kI,\x10\x8d\xed\tf\r\x1b\xc6\x10\xc0\x92\xca\xb8\xb4\x01\xc3d\xcf\
+\x8aD\xe5\xfc\xfa\xc1w^?\xf8\xce\xfa\xb5\x13\x1b\xd6\xad\xb9j}\xbb\x9dvn\xfa\
+\xd2\xc93\xd3\xe9\xc1o\xcbIs\xcau\xed\xe3:5P\'\x86 \xe3v5\xf7)\xa6\xe2%\x15\
+\xe1V\xbe7\xaa\xe8\t\xae\xe0\xd3j5\x81\xe3\x80\xb9\x05\x15\x08\x90\x13-\x1c\
+\xe3\x82!\x9e\xbexy\xfa\xe2\xe5\x8c\xda\xafe\x18\x1cP\xcc\xe1\xa7\xbbd$\x0b\
+\xcc3H\xbfV\xab\ritp\x0c\xcb\xe3\x8c>\xabd\xdb\xfew\xd9\xa5g\x15\x15\xb5\x03\
+{E\x12\x1a\xfa\xf1V\xb9\xceW\n\x15\xf5\x1a\xe4*NZ\xa4\xac\xedVS\xe6\x91u>_h.\
+\x89\xdb\xa0\xadf7\xf4k^\xc9\x91\xe3\xb3kA\x17\x1e\xdb\'cq\x99o0\xc2\xa4PM\
+\xbci\xac\xdf\x99>\xe0\x87!*[\x96\xc1\x9f\xf9h9\x8f\xaf\xe9\xc6\xb8\xe5\x98\
+\xc9\xa1PK\xab\xc7u\xaf\xab\xb1\xea\xa6\xf7E\x12\x99\x89s>\xde \xa7=%\xb8L\
+\xec2\x17L\x82\xa3]\x04\xac\xec/<\x04\xbb\xa3Y\xaf\xc5\x8bkGrV\x9dd\xf1\x18\
+\x03\x869P;\x11\xcfYs{\xe1r\xc2\xe3\x81\xa3L\xba)mVm\n\x04brX\n59\x95<\xaa\
+\xef]Y#\xed\x16r+=\x8c\x8a\xd0A\x95\xd2\xc1E\x1d\x05\xbd\xdb\xc9.\x82\xf9TrW\
+\xe6\x86\x1ey\x985\x11\x8a\xa2\x1c\xe6U\x12\xd9\x90]\x81\x97\xd5C>\xf3\xd0\
+\x0f\xea\xe7J9*K\x9c\xe2[\xe6\xf3\xc7U\x9a\x1f\\<\x81\xc0\x9a\xf5-\xf3\xa0\\\
+\xe4Pg\xc9\xc8U\xa8\x14M\xb1$\x0e\xccq`rF\x05\xaf\x19\x02\x9d\x07r\xa7\xd7\
+\xd5\x15\x07\xcclc\x960Q\xc3P\xd8/\xc6\xa2\xabC\xe9\xa4\xa7\xf7\xce\xb9>P\
+\xf1K\x8e\xf9\x81\xc9\xec\x1b\n\xf1;\x0c\x04)\xde&\x95\xdb\xf4\xb6\x80$\x95\
+\x0e\xb6\x80lB\xda\xb3\xac\xa8\xaeW\x08\xe2&7\xe0E]AiK~RP{\x8e\xcdS\xd0c\xe7\
+\xd89hq\xc0\tP\x14P\xa1\xf7\x15\x040\x95 _Q\xcc\xdeW;l\xe0&X\xcfTuD?\x90\xe8\
+\x06\x94n\xca\x1f\x95o\xf1\xab\xa2X\xcd\\\xaa\xcb\xd8!\xec\xec\xee\xe5lA@\
+\x80\\\x01J\x8f\xa6\xb7\xd5\xe4_R\xbb,\xdb\xaf\r{\xe3\x8eo\x14\x1b\xef\n\x1c\
+.X\n\x00m\xd3\x8b\xea<\xcf\xe9\xc1|\xea\xbb3\xb6\x18\x98\xbc"Hu\x1e\xa8LY\
+\xcbK9J\x0e\xe4\x13d\x08\xa8\xb2\xcf\xbe\xac\xb2`\x08\x97#\xc5|\xd7\x9b\x85\
+\n\x93\x1a\xae\x1eXE\x1a\x0e\xf0-_\xe9\xeb\xda@\xb9\x92S.\xd1m@f\xf2\xd3g\
+\x11U\xeb\x9ei\xcfp\x8aa\xe8\x99XuFu\xf1U\xa9K:\\\xfc:F\xd1\xb7\xbf\xe0\x05\
+\x00I\xb9\xce\x8f\xb3x\x8e\xf7\xa9\xac\xe6F\xd4\xd2\xc1I\xc6c\xc7}U\x90^\x00\
+C{\x19}\xae\x14\tb\xcd\xa40j\xa9K\x98\xc9\xa9\x86!feF-\x8eC d\x96\x0b\x00\
+\xa6\'\x9f\x89$\xa5MC^\x0e\x95 \xb3G\xac\xf6nc\xa1\x9a\x9c8\xe4#\xd1\xc0\xbd\
+I9;\xa0\xd3\xbf\xe2;\xd0S\xb9\x92\x07J6\xb7h\xa5\xe6,\xb7\xe4\x9f\x84\xe24\
+\x05yyV\xeb:\x87\x1a1\xb3<\x8e\xc6\xdbU\x9b\x82\xc7\x90\xd5\x9f@2\xba\x92\
+\xb2\x11:=\xb7\xaa\xa1\xbc\xe5\xb0\xbd\xc8,\xb08yU\x15\x8a+$\xdd\x95J\x98M\
+\x19^\xa4Zc\xa6\x96\x15\xdbS\x95\xb3\x12a\x9e\x8c\xafO\xa1\xcc\xf1B\xbe\xbd\
+\xcc\xa3\x05\x05^\x07\x8a\xf4\xfci\xc7r\x17\xe7\x11`\xbc.ZY\x1e(\xee\x0e\xa8\
+\xe2\xc9\xb0\xa7\xbaxq\xf5\xad\x8dc\xde\xa2\xd0\x07C\xae7\xa0e\x15\xd0PR\x98\
+%\x1a\x9dV\xdet\xa0\x05\xfbq\x02\x98\xdb\xff\xf5\xe2JL\xb2dw$S\x82[R\xd9W\
+\xa5\x85}\x9aW8\xf6\xbc\xa8\x88\x0fbMI\xaaQOCG\xc7\x9c\xeeiz\xfd\xb6\xb8z\
+\xc8\x16\x7f\xa4>&\xdcv\x9a\x82fC\xb3bH\xab\x0cZPv\x0b\xaa\xda\xc7\xf3\xa0\
+\x12\r\xb5\xc3Bu(T\x89\xa2\xcb\xdcy\xa7\x8e\xfaYO\nK.f\x8f\xe7\xfd_\x10\xe21\
+\x94\x87VsA\\\x9c\xab!\x88h-\xee\xebC\x9f\xdaz\xdb\xbbp)\x19\xcb\xc9p\x05l\
+\xce\x84\xceo\x9d\xc7\xa1\xe6\xdc\xb2ja\xcep\xc8\xa8\xdc\xe4LX\xae\x99Q\xad\
+\xb0\xd1\xd0\x9e\xfb\xf0\x14\x07\xc8\xb1\xaf<\xac\x90\xf7o\xd45\xc1"\xdb\xfd\
+XB\xe9(\xa1\n\x14\xdf C;\x1f\x0c\xc3\x80\\\x87*\xb2\xd6\xee\x11\x8eR\xab)\
+\x86\xb4\xcc\xae\xf7;\x97\xb4\x13\xc3\x81\xaa\x0fm\x92\x193yJK@\xb3\xe8\xaa\
+\xa7$\x89\x80Rc\xfb\x9e\xca\x93\xb1\xa2WC\xaf\xa5\xf3\xd45H\xc9\r\xb7Al!\x80\
+\xcc\xec\xa9I\xab\'\xcev\x80H\x95A\xf8N\x0c\x1b\x08\xa9-\x04\xd0$\xd3b@u6+w#\
+\x87;M\xd3g\xae@\xc4%\xea\xae\xc4\x8a\xaa$<\xa4}l\x82\xeb\xd6V\xa0\xd7\x88k\
+\xd3\xbf\x95B\x00%\x81/\x1d0\x1c\xd8Aw\x05\xbc\xd6\x96D\n\xb5\x00\xa1 \x8d)b\
+\x08\x90\x9b\x19\xf3v\x17\x1c\x13\xf6\xd0\xef\xe3o\xe8\xf0\xc7\xd0<\xea\x01\
+\x88\xcd\xd7\xb9\xf4\xb1\x00\xfb\x93\xd2Q\xdfU.\x00tE\x00~,\x82\xf9\xa4r\xcd\
+\xb8v\xe8\x07OY?\xbb\xe7\x19\xc8p\x8a#1\x0e\xcb>\xfeI7\xed\xeb\xc8E~\x95\xa9\
+\xa1\x8f\xa8\xe4\xbf\xe8\x05\x00=\x1f\x9et\xf4q\x8d \xfe\x8bM\x03|\xdd\xfe\
+\x0bW\xda\x9fX^\xf2\x8c\x9c\xff\xe6\xdf\x8a,\xa1H5H\xf6\x81\xc2\xbf\x9ai\x8b\
+G\x0f\x8f\xc6\x0bN\x13\xdadQ)R\x14.\\\xc4\xcaQ<\x83ge\xb0\x1c&\xef\xbd\xf5\
+\x8cP\xcd\xa0\xea\x05\xd1\x11\'(\xd0\xc6\x7f|l\xb8Wp\xe1c\xd1\x1a\x91Z[p \
+\xc5\xb9L\xcb\xa08\t\xb5\xaf\x11L,I\xdb\x9d PA\xc7)W\x98\x1f\x9e\xd9\xfa\x87\
+\xf8\x07$]\xf6<\x88\xa3\xa1\xcc^z{\x19\x12,\x1e\n81e\xf1\x8a\xa8\x83\x06\xc5\
+\xe9U\xa0(n\xf2V\x06\xfd\x0bn+\xa0e\x04M\x14\x88|\xc9\xac\xc0U\xe0\x03;\x13\
+\x15Z\x8aB\x01\x82\xbc\t\x87A%\xe4\x83\x0f\xa1\xb9\xed\xe9UG\xf5\xa0\x9a\x82\
+x+\n\xe1\xf8\xaeBHJx\x19GW\x94\x15\xb7\x95\xc2\x01\xa6\xd9\xfa\x9a"\x8f*z\
+\xa1E\xd8 \xc2G\x19\xd8?\x85\x8ddPL}\xc6d\\\x0cA\x13`\xee\x89A\xf0\\\xf6\xb4\
+=zTu\xbf3\x95ce\xea$H\x86\x03\x02eG:\xb1\x86\x9a\xed\xa6\xc5J\x0c.h\xa2\x98\
+\x0f\x84k\xa2?jkg^\x8e\xa5\xf6\xf5G\x07\xc8\xa9\x11\xef\xbd\xefi\xd6\x8e1\
+\xce\xd5,\xf7\x1bT/"\x1b\xa4\xc5\x89\xaf$\xe1\xca`\xa8q\xcct\x80b\xd4\x01\
+\xa0+\x0et\xe8 >\xedq"\xb4\x93]\x85\xf0\x03\x98V\xe0\n\x00\xe1\xa0\xe8\x04X\
+\xea\x81\xfa\x04L\xa1\x87x\xe96*PP.uFr\x91\xde\x83\x1f\xde((\x8d\x14\x98V\
+\x83\xa5ZI$\x15\xa2\x171\x82\x1b\x05\x01:\x1f\x14^\x84\x19\x81\x06\x0b\x19\
+\x89\x98\x81\x86N\x87J\xbc`\xb4}\xec\xd1"A\xca\x05GU\xd9fl\x8c\xa9\x03\xe5\
+\xd0\xba\xe4\x979J\xcb\x9c#0&\x03\x8b\xcb\xa2\x83^\x0f\xe3m\x06\x99j\xd4\xe4\
+\x1f\x9d\x0c\xf2N\x13\xee/;\xa5P\x93-|\xfa\x8c\xfb\xd7\xf2)r+\xf4\xb9\xbc\
+\xcd\x86\x98\xbaS\n\\vJ\xdaV\xf6\x02\x07\x99\xa3\xaa>;\xcde\xe5\x18\xe6\xb2\
+\x9b\x99\xcbA\x80\x0fg\xfb\xbd\xd3\xaa@\x83\x81\xd9$#\xb4s\t\x0f@\xdeI\xddbC\
+\x0ch\x04\x1a\xe2\xeb\x00\x00ZhR&2\xf69\x8c>\xa5\x13\x95\x9c`S\x1d\xdc\xc8:(\
+\xc62vR\xe8\x17\xba\xc5>\x19C\x00\x00 \x00IDAT(\x9b\x15\x03\xd3\x16^4[)\x12\
+\x18]\xe9g\xd3\xda\xa0\x91\xe4p\xf7\xe2"K\xc8\xf1X\xf4\x889N\xf38\x9b=\xa3f\
+\x1a"\xdc\x90\xf7ohP\xe8\x13\xa4:\x9d\xfb\xf4\xf5w\xb6\x8cJ\xc4d\x8d<O\xc6\
+\x85\xf3`\xcb\x88=\xd5B\x94\xc1pC\x17\x91\xca,\xc7\x82\xecVOQ\xab\xaa\x99\
+\xc6(\xf8\xa4\xe0d\x83$\xe7<\x1b\x15\xb6\x13\xb1DCH\xe5\xc7\x0c\x01\x9a\xe2e\
+\xe0\xc2\xde\xe6A\xcd\x81\xde\xd1\xb83<\x93\xbc\xd6,\x12\x82\xd1\x8c\\7\xa2m\
+\x1cZ\x80\x8cF\xf0\xe9c\xb9`\xcf\x820\x9b\xed/{\xe9\x84\x9e\xd7^\xae\xee\x84\
+\xb6F\xdf\xb5\xaf\x890a\xc0y\xb4\x80 \x1d\xafx\xd2\x9dZ\x01\xbe\x1f\xe6E\x8b\
+\xa8\x81\x88\xe68]#\x1c\xc4\'\x90\xf4\x90\x04Op@\xb3x\x02@^\x06\xb0\xfah-D\
+\x1c>\x80\xa0\xaa\xd9\xd7\x16\x07\x0e\xf4\xf7\x11\xa9\xc8 \xe6 \xe3\xb3\x15\
+\x03k7O$\xfa\xd1\t\x17\xa1\xe4\x03\x90,\x9a\x02\x082\xf6\\\x17Y\x9e` \x81\
+\xdc\x02\xe1+\x00\x1a\x00\xb1\x97\x82O@\x9c\x81T\xa9&S\xa1\x1a\x81\x8e<\xa6\
+\xfe\x13\xe1\x06\x10>\xee\xa0\xf1\xde\x89\x14\xb1Q\x88\xfa@4\xb2SP6\xacC\'Zs\
+]\xca\x93\x02&\x84\x8f\x88\xd6\x12\xe8\x92\x06L\xba_Q\x0fq\xef\x11\x84z\x08\
+\x1e\xb6d\xd5\xdc5\x15V\x9e\x01\xa8\xaf\xf9\x82I|\x00T\x01p\x99\x1a-\xe6\x1e\
+r\xa4\x14\xf0\xe9\xb4\'\xeb\xd8H\x8aP\xa0\x0f9`\xcdV\x19\x86\xc3M\xe2D>\xf2\
+\x9d\x80\x9e\x9c\x83\x06\x07\x8aJ7\xb9b7(\x82\xee;\x03v.\\x\x06\x8a\xfe\xa8\
+\xf0\xe5V\x83\xb6*\xa6\xf6\xb3\xe0o~\xb1\xbc|"H\xd5\x82:yh3\xa4\xd9A\xa2\xe8\
+\x9d\xc0\x12]\x8dh\xd1\x8a=.\xbc\xde\xe9\xa0\xb1\x11\x85\xcd\xd3\xee\xd2WA\
+\x96\x84\x9d-\xda\xe2\x00\xd7k\x88Gp\xa8<r\xeb\x13\xe6\x91Y\x10\xf6?\xe1 \
+\xf0\xaaU"\x0c\r\x8a\xef\x13S\xab\x0fn\xa6y\xd0\x0b1\x00\x9a\x87R\xe3\xccY\
+\xa9[\xce\x9a\xd2\xe2SL\xe7j\x07\x900t\xcf[.(IMl#\xc0\x84\xac\xc8\xc7\xf8\
+\x81<>\\\xd0_\xa9 \x92\x98\xe4\xc7\xa1\xff\xc9e\xab\tt\x17\xb0\x04FZ\x83b\
+\x07\xb1\xbb\x00bv\xd0\x02\x90\x9a\'\xc4p\x13\xd1\x1auh\x18\xc5\xe4X\xd0\x9f\
+QA\x86\xc5!\xe9\x03\xf6;B\xee\xd2\x0c\xac\xb4\xa0\x848\xae\x8f\xcc\x9b\x8f\
+\xdc2\x94\x8f\xca\xbb\xc9\xe0\x1c\xb5\xefi\xc4\xb7\xede\x1e!u\x17\xd2b`\xd4\
+\xa1Q%x\xaa\xec\xa9\xd2\xb0Y\x0f\xb5\x11w\xa1\xe7\x01\xe2\xbf\xf6\xd7\x83\
+\x87\xd6_\xf2\x90\x92Q6@\x1eUx\xc3\xb9\xe2X\xa02\x89H\xd8:B\xb7\x00\xe2Od\'\
+\xb4-\xb3\xb6\xc4v\x9b\xf8\xe6\x93dMv\r\xea\x9dZ\x07"K\xd3H\xc4\x08\x08\x1b\
+\x01.*\x00\x17~\x114\\\x8cH\t\x0eP\x1e\x07$w>\x18\x99\xc9\x06R\xea{O\xeeq\
+\x9e(\xc6&*v\xbb\xc79\x88\r\xe0( \xe2\x97\x00\xa8Tv\x95\xa3?\xc2\xc8hK\xa1\
+\xb2\n<\x94\xd4F\x9b\xcbq\x04`\x1b\xe0\xc2\xff\x92h\x12<\xd3\x1d\x84\xbfz\
+\x1c\x80\xd2\xa8~\xe04vY<\x11\x1b\xee\rR\xadO\xd6~\xbb\xdaS\xe7D\xb3\x11l\
+\x99\x81X\xbd\xe4\x079\xe1\x89C^\x03\xeb\xbcy\x87&\x96\r\x8f\x10m\x18\xa2\
+\xa9[z\xb5*\xe9s\xd4\x05G\xaf\x9c*\xa9<e\xc5\r\xc0}\xce\\\x86\xd64X\xce\xa0\
+\x0c\xf2\xfd\x04|0\xaa\xc7_\x1bq\x06\xdf\xcc#\xd9\xc5b\x0c\x05\xd4\xdc\x077*\
+\xc8=x\x19\x84\x7f\xde\xeb\xc93\xc8\xef*\x05\xe6hY\xc5^\xea]\xa38\xa1fmVp\
+\xe2\x82*\x80\xccpF&\x90e\x8a\x02G\xd3C>1H\x90`U\xe4\x0e\xf4\xf6\x01)\x82\
+\xc4D\xe4I\xf1\xfa\xf1HE\xe2\xe73\x7f\xcf;<\x81$*\x03\xbcn\xe4\xda\x89#A\x15\
+\x90\x9a\x86\x1b\x89\x98\x89\xe2q\xfd|\r\x962\xe0\x91\x8eGG\x04\x98\x990lTi\
+\xb9tN=hh\xaf\x16\x90:8\x95\x17\xb1\x1e\x00\xa0\x17\x9c.i![\xfb\xdf\x1c\xd4@\
+\xebj\xccr\'\xa5\x1b|\xbe\xf6_r-\x11A\x8f*K\xdeA\xa0\xe3\xe5\xad\xd6\x1e\xb2\
+`P9N5\x0f\xa0\xea \x9e\xa6(\x01,\xb8\x04\xdb\x1f\x87\xbb\xa39\x1d\xfd\x1f\
+\xb9\x84\xc4\x1aqM\xe1\xd2E\xf8\x0f\xa8\xdf\xd85\xe0~\xeb%\x1f\xcf\xfe\x97\
+\x00A)\xc4[\xcf\x01\x10\x05\x99|2\x1fUN\xf4\r\x89\xbc\xe3\x85\x86\x03\xa5\
+\x11\x9e@\x01\xcbRp/\xb1]\xd9OJN\xc7\xee\x88\x84\xf3\xae\x9fF\x87\xd8\x06g\\\
+\xd3[\x81\x03\x95\xc5\x1a\\\x84^\x0e\xe6 \xc5*POk\x83I)\x93\xd5\x02E!\xb5FKZ\
+\x13\xce\x87w.\xae\x1e\xb5\xeb\xef\xd8.Xn\x01\xba/X\n\xb5\xf6\xa8IAl8\x95\
+\x82U\x17\xd6\xebxX\x0b\xdbP#\xa0\xa8\xfcy\x15\xc1\xe2\x87U\xd7\xb8\x08\x1f\
+\x9c\x83\xd6\x03\x060\xdd\x00\x11\xd97\x10@\xaf\xd3"\x92\x87\xe8\x89\xd8\x02\
+F\xe2Gs\r\x81\xae\x80\x1f\xdf\xe2 \x9e\xd8\xc2\xfb\xd6\xcd\xffh\xefQk\x16"c\
+\x07\xf2n\xfb\xe1N\x9d\x14\x0bI\x81\xe7\xe3s$\x83+\xd8\x84\x80Z2\x9f\xf1\xc8\
+C\x88\x0b\xf1\x01\x05\xc4q\xa0\x81\x89-N\x11\xf3\xa1Q\x1c\x0eI\x07S\xc2\x8a*\
+\xd5\x12D\x0b|\xa6 \xd7\x88#\x0e\xf0\xf6,9\xc2\x80\x92\x9a\xb1\xa0u\x9ee\xe0\
+\xe5\x97q$b*\xe7\x8ah\xbd\x86\xdb\x82T\x9b\xca\x041\x10\xb9\x9c\xba\xe0\x90\
+\x9dH3C\xa1\x11\x08\xa3t\n\x99\t\xea."@B\x1e\x8d\xe3T\xa3\x07\x8fL@l\x9f\xf7\
+\xbem833>\xfe"\x1cX\xc3\x9cM@<\xb0\xde\x17{\xcd,8\xe0\x9fm\xc4X\x95\x16A2\
+\xc2\xf92\x8d\xb1Z{p\x11\xac\xec\x15B\xca\xd0N\x93*]\xe0H\x9d\xd8\xb5\xf0]D\
+\xd5\x17fP \xe2L\xbeb0\x01m&\x17w\x81\xd9\xa1)\x8f\x7f\xa9\xeeO\x89\x88\x014\
+\xffD\xea\x81\xed.\xaa\xa7\x94\xd0\xd6\xb3\x194\x1c`n\xf1\xd0G)Uf]\x98\x01,\
+\r\xf2t>g\xa9\x86v\x9b\xd7\xc5#n\xd4\x8b(m3\'R\x8e9\x86z\xee0\x82\xf8Kl=\xe0\
+\xb6\xc4\xc7\xac[4x\x9f\xae\t\xa1\xb4\xc7F\xadL\xb3\x9a\x84]=\x1f\xf5\x06\
+\xc9\xd4\x9e\xe72v\x99\x11\x7f\xd1^q\xcdOuI\xc2\x01\xd01;\x04\x02xZ\x82\x82j\
+;dLF`\xc9/L!E\x90\x95#C\xbf\xeb!\xac\x13\xd0\x19\x97\x1e\xac\x8eN\x0b\x03\
+\xf1\xc0\x98\x0f\xd3+\xb4\xd2@\xf9F\xf3/\xb4\xd2\x80\xaaI\xf3K\x94\xcd\xa7\
+\x0boqd2\xc9\xe3\xb0\xe7[E\xa6\xc3\xf6\x92\x02\x1e\xe2\x18+:G\xa7RHJ\xda*\
+\xce\x1b\x91\xf6\t=m40\xd0\xeei\x89\xcb\x08\t\t>\x88\xb1M\x90\xcb\x0c\xa4\
+\x1c\xfe\x89\x16\\[1B\x17]p\x00\n\x14r8p\xfc\xa2\xc3\x92L+\x9d\x94\xdf\xa3\
+\xffI\xba\xc4C\x99\xb4\x8a\x0c\x8c\x82\xc0\xab\xad\\X[\xd4\xd7\xdafM\x88x\
+\x16Z\xa6\x12m\x8f\xcf\xe1r\x0f\xc0\x0c\xc1\x0e0\x0f\x0e)\xf2\x02\x81BH\xf3*\
+l\x02\x96A\xd3\x01\x91(\xfd\xf1\xb8\x13XN\x8d\x92\x85G\x96\xec\x8d\x02NM"\
+\x939z:\xa9\x97\x1b\x1cZ\xf04\xc8TN\x85\x8cc\xa4\xe0\xc31\x04N\x81d#\x8b\x86\
+r\xd8\xaa\xca@._\x9amAg""\xcb\xad\xed2J)\t\x19i\x0e\x17:-\x033\xbb\xc3\x00c\
+\xad-R=G\xcc\xc1r^\x0c\xc0\x83\xd0\xb4\x16\x0e\x94\xa2!\x1b\xca\xef\x15\x08\
+\xac\x10{\xccj\x81\xd1\x0f$[\x0c\xcc\xfd_\x99@\x8d;5\x00L\r\xa0\x1bn\xfb\xa3\
+\xe8-\x1c\xc4\xdb\x04\x82\x95D\x80\xcdn\x1b\xa3(\xa9\x0c\x85\x8a\xba<\xbf(\
+\xe7\xce\xba\x8a\x85\x89e\x13\x02\x02t\x08\x18b\xaa\ra\x88\x0b\xef\xd2\xeb\
+\xa4y4\xa2Aq\x90\xdc\xdb\x10\xdf_q\xd9#\xee\xf8\xd4\xbb\xd2\xe6&G\xc0X?BC\
+\xa4L/\xb7@l\xcc!Q\xfd\xfaC\xa7\xc5\x10}\x98\x86\xcbb\x10O\xe7\xb6\x93\xbeD\
+\x0e\x93FY\xa2+\xad\xe8\x01\x8f\x7fsa\x85g\x07\xf6S\x86f\x04\x9dDsGa\xe5\x11\
+\xdau\xef\xb8#\xf5\xf0G\xb3\x1d\xb1z\xe6\xc8\x99(a\t\xc97\xf2\xd1\xf5I\xf1\
+\x19g\x85(\x83\x14\x15\xc2J\x82\xc0^\x1a\xf5\xa6~\x95%\x14!QT\x08J\xe8\xfc\
+\x9e8\xbe\x9f\x1f\x85\xf1\xe5@\n\x17\xdcQ(\x07<\x18\xbb\xab\x7f\xcc\x91A[\
+\xc6y\xb5C\x10\xb9\xf8\'\xdck\xc4Wz\x9d\x80\x05}\xd89\xc9\x0c\xb3rV\xabj\xba\
+U\x07\x04C\x8a\xd5\xd9\xa2>\xa9he&\xcd\x1c\xa7\xa4\xa8\x96\xeb\xcd\n7\xa2\
+\x84\x1f\x14\x9b\x06W\xb1cx\x9f\xe3>\xd6\xbf|\xd2)\x146p\x91\x07-\xf29\x9c\
+\xcf\xa7<\x95\xceC\x86%\xe3^.\t\xa3\xca\xad\x10w\xb2\x03sr\xb1\xa3\x9eaU\xeb\
+\xb0C\x07&3R5\r\xabq\x08\x99wS\x138\x87\x00\x92\xef\xa3|3\x9c\x96\xab\xca\
+\x7fo\xd9\xe0\xe4\xf8\xeao\xf1L\x8a9\xde\x92\x12\x88\x83>3\xef\xad\x0c\xce\
+\xb8.d\xcd\xe4\xc8g[\x01\x10d\xe6\x17\xa6\x97\x88-\'\xc8kc\xf1\xc3&d\x06\xc7\
+\x85\x9d\x91wI\xa5\xd5\x8e\xba\xfc@\xae\xb4\x0b\x89\x97L\xe6\xe1V)\x04\xe5w\
+\xd9\'\xc0A\x95\x98.F\xa3\x1b-L\xd5\x8d\xb2(~\xf5\xfd^\x9d\xce\x1aY\x9e\xbf\
+\xb8P\xfc^\x851l\x97\x90n\x91\x07\xd3T\xb5\xaa\x0cu\xdbIQ\xc3\xbb\t\x02\xaf\
+\x0ct\x9b7\xd9_y\'"\xd2\xcd\x90\xe4\x1e\x00\x93}\xf1\x1d\xc0l\xf4\xe3\x8d\
+\xcd\xca%\xcc\x95]J\xb4\x08\xeb*\x1f!\xca\xb3\xbc4\xac\x0c\x08\x86]q\xea\xbc\
+\x0e\xc3\xfb?\x8aG\xee\x11h\x08\xe8V\r>\x8f\xe4\t\x14\x14\x1c\xc4r\x9e\\4\
+\xd5\x17j\xeb\xdcy\x88\x1b\xd7\xfe\xab0\xfd$\xbc[\xeb\x042\xc8\xd6\x11\rGW\
+\xc8E.Q\xdaX\x19A\xfb@\x92\x90\xc3\x08\xe8\xfa\xd2_\xfc\xd4E+\xfb\xc2j\x07s\
+\x0f\x8d\x0eh3xZ,fhXU\x9a\xe3\xf1\x1f\x0ez\x05\x07\x9ee\x94\x83\xce\x01\xb0)\
+\xe2\xd2\xd2\x12\xcb\xd3\xebuF\t=\xc1\xad>S\xae\x1d\xe3\xa242\xa0v,\x9boc}\
+\xf6!\xf6\x1c\x93\xa0\xa1\x0e\xb1\x10\xb7\x9b\xcdu\xecLE\x96\x97\x1b\xcdc\
+\xa7\x05\x04R\x16\x07\xd7\x90\x12\x87\x8c\r\xcc\xa2W\x1f8p\xbd%\x14\xba\xb1\
+\xa2\xb2\x97\x19d\xc91#?Yr\xc5D9sN\xac0\xc6:\xdb,\x9a\x9do/1\xc4\xa8\xa3VS\
+\x07\xd8\x94a\xb5\xe1=\x896\xf90C\xab\x088L\x05\xbd\x98/\x86\x95\xdf;0Lo\x9a\
+\x97\x18\xcc\xea\x1e\x02\xb2\xb0\xd8\x0e\xc7sG\xc9\x16\xc4D\xba\x89]\xd1\x83\
+5\x931L\xd0"I\x07\x9f9=\x96rA\x9e\x8dZ!\xcb&\x0b\x86\x80ZR\xb7\xf0\xd1\\\x15\
+@P\xa9\x1eL\x9fK\xae\x159z\x1b\xb9\xa2\xc5x\x04e\xdcS\x14\x00J$8hN>\xa2\xf3%\
+e\x86\xb3A9D\x93\x07\x19\xab$\xa7\xa2c\x15\xd4\x1b\xf0\xf4\x8fJ\x96\xdd\xcb}\
+\xa2x/G\x92\x07\xa8\xd1\x04+b&@\x93>\xf1\xb8\xf5\xfe\xd4"\x89\x05\xe0\xfd\
+\x93ryyU\xa1\x18t\xb1\x19!t%%\xab\xc0\\/Y6\x8c\xc9\xa4\xf0\x10&\x05v\x10\xba\
+V\x1b-MhA\xd0\xd5m&\xb5u\x18U\xd1\xd7b\xf2\xa7|\x9a\x85yE*\x04\xf0d\x01\xab\
+\x84\xe0\xb97I\x9eQY\xa9\xc0\xfc\xbc\x15Z\'\x90 \xab\x0fq>\x82\xfb\xd9\xc7D\
+\x07nY>A\xe1+\x0b\xca\xc6\x01:p\x0cZ\xefk\n\x13\xa9.S\\\x1a%\x01\x85P\xbfB\'\
+\x83\x03#\xa9z\xeb\x82RR\x9d\xc4\xe4\'(4\xb5\x05\x00G\xffE*T\xd9\xd3jL\x9d\
+\xb9\x1c\xc7\xb0\xa0\x00D\xb2\x90/\xb2\xd0\xf1\x8f\xea\xc9\xa2\x12\xac\xebi\
+\x07\xf9\xd4\x93\xc8\xeb\xe1\xdeb\xc0\x01W{\xa6\x14Rd\x85\xce\xb3W=\x8c(\xec\
+\xe6\x08\x98\x0cs\xa8)\x85\n\x17\xa4\xa9\xa4\x07\x9a\xbf\x83\xc3\x10K\x05\
+\x00\xc2\xd3E\xb2\xa7\x0f#\xc8\x821W\x86\xb8\x12\xe3\x83\xc8\xc9\x1a\x9f\xf4\
+\x16\x81\xe0\xa0\xab\x11T&`,\x8d\x00S,\xeej\xab\x02B\xefy\x19\xad\xd5-\x92\
+\x05Q\xcb\xbfd\x94\x88\x80%\xd2\x87D\x00\x0f\x9e\xdf\xea\x1aYB\xd7\xf6\x15U\
+\xf4\xd84,\x1c\x84\xbb\x15\xf5\x0b\x94\xa9x\x8a\x8e\x97Y\x89z\xc5x\xach\xc0\
+\x1b\xaa\xc3\xcb\xb8\xd0q\xea\x99-\xf5\xd2\xac@\xd3g\xf1\xba\xbb\x9e\xcc\xe3\
+\xa0\x18\xf8\xaecyMS\x9f\xe8\xb2\x9cq\xad\x92\x00\xd8\xd3\x8b\x10\xaf\xf1\
+\x8as\x0f\x13Z\xc70\xb3\x93\x96\x9f\x1f\x9a8\xe8\xb4Wc.\xc9\xa7\xb6e\x9c\x1a\
+Z\xaf\x01\x8ewoD\x19c\x9eeh\x98``\xce\xe0\x19\xa7\xca\x0b\x84\x19\x91H\xc7M\
+\xd6\x1bI\x9c/W\xdb\xe7\x9eF\xcaw\xbe~\x8e\x93\x9b2O\xb3\x02\xedq\x92\xdb\
+\x1c\x80\xbc\xa0\xba\x84P\x0c\xfc5\x17yd\xb1\xa7\x01Q\xb1R\xaf\x10\xc1&\xef\
+\x06k\x89\x8c\x0f\xdbE\xb7\xc5P\x00\xc1\xd0>\x01\xd5\xbcm\x90\xbe>/R\x0cxB\
+\x94+\xc5\x01\xbf\xac \'hz\x9er5\xd4\x8f$\xd7Za\xe6\xe3\x08\x85\xa8\xf8ZZ\
+\xbc\xf4Vi}+\xb9w\xc0\x95\xc1p\xef\x05\xb2\x88\x87?D\x83`EQ\xd0<\x96IM\x8f#\
+\xe6\x19\xa8\xf7\t\xeaCn<\xe3\x11o\x9b\x04\x91\'\x7f\xd8E\xe7\xf9\xdd9O\xd0\
+\xec\xbd\xe2:+zL\xc9,\xf7\xc8\xa5yH1eU\x82\xa6\x03J\xbc\x8e\x03\xbe\x10\xcf\
+\x93\xf8\xe6(\xa5\x9be\xa6\xcc+\x92\xaf\xbe\xac.\xf6\x19\x18}\xbc6\xd8\xfcs\
+\xf2\x14G\x0e\x04\xcb\x9a\x1f\x92\x1e5\x14\xe8pc\xc9\xc4\x01D7\x93w\x08BH\
+\x1e\x07]\x03Bi\xec[\xba\xeaU\x05N%\xc81/R\xa5k\xc3V`\xcc6\xb1\xa1\xf5-;c\
+\x08\xc0\x9aB1PXic*!\x08\x9c,\rU\xf7\xb6pF<\xbad\x16-\xfc\xd2\x15p\xf1\x98\
+\xb7\xf5\x04T%?L\xce\x9a\xd9\xef\x82$\xcb3\xe0!\xf0\xba\xc2 `\xd3\x04\x84K1B\
++X\x8c\x1d\xaek\xda\xa1F\x98\x1e\xb4\x83i\xeal\n\x04 \xc1t\n3O\xde[\xac{\x05\
+\xce\x02 \xbak#\xe3$K\xc2%l\xaeM\x10,\xc7\x16\xa0\xd7\x17aP0\x1c\xa4G\x82)\
+\x8f\x19+\x81\xcd_~\xecW\r\xc8\xce\xeb\x84\x8e\\\x04X\x0cm\xd5\xe4\xbf6\xc5\
+\x1a\xfeY\x07\xd8ph\x8a\xdc9\x03\x04+\xb2Z\x8c\xa4\xaf\x18\xad\x0cO\xf8GR\
+\xd5}e%\xdbJ\x06\xa2\x06l3\xc6V0d\x9e\xba\x89$"\x81\xe0\x80\xee\x05\xf8(\x8a\
+\x82\xa6\xaa\xa9i%\x16\x8b\xf2A\xc7A\x86#\xed\x95\x1a\xa5:\x86\xf4\xe9\xa0\
+\xda\x80\xbbp"\x8eD*h\xcd\x90sI4J\xbcQ0\xebS\xe5\x03\xa9O\xab\x04s\xf3\xae?\
+\x81\xa4\xcc\xbb\xda\x88\xea\xc7\xf2\x99\x19\x90\x0e!\xcb$I\x0c\xe7\xad)|Q\
+\x90\xc6q\xed\xecB\xe6\xd87e/\xed\x9c\xd6\x83V\xc3E\xe5\x15\xf6\xe0[\xf8\x18\
+\x9a\xc9\x83>\xfa\x89j\x8d\'\x834\xc7\x99*\x03\xcd\xc5#\xe4\xaa\xf5\x8c9\xbf\
+J\x08\x08"\xd6%\xea\xb2\xb7\xa8\x1aC\xa2\x9c\xaa\xa3\xd9\x97a\xf2(:t\x10\xac\
+\xd4\xb9B\x12\xa4#H\x83\xcb\'#Bf\xfb\xd3\x06\xaco)U\xea\x80Z\xc3\x90.\xda\'\
+\xae\x03\x14*\xdc/\x96IL\x0c\x01\xbbLR\xf8`L\x0b\xca\\\xeb\xb5!\x1c|K5\x819=\
+\x07\xe4\x0f\xe6\xba\xb3v^\x98\xc9\x84\x1eK\x8dQ.\xc6\x93\xf5\x80\xa6\x9b\
+\x9c\xc3/{q@\xee\xf0\xab.\x08\x14\x88\xcc\xeb\x04\xc5]\x1b\xc3\x8dp%\xc2\x8e\
+\xa1\x86\xe3L\x96\xfaV\x80\x00\xbb\x87\r\x1f\n\xf3Q\xc5\x99=f\xe8y\xe5\xa2\
+\xec\x83\x96^c\x13\xb3\x89G\xd9\t\x02J4\xf29\xe8\xf40\x8f\x14\xdd\xbaK\x15T\
+\xa9\xf9\xc8d\xc3\xebo*Y\xa1\x03Ez\xed\xf2{&(\t\xda\xfe`/\xc1\xe6\x01\xb9d\
+\xaaC\xe1!~A\xa3\xe0\rH5\x10\x14\x000O\x90\xcc\x0e;\xaf\x0c\xb6\x1cA\xb5\x9c\
+Dp\xbc/"T\xbb9\x07\xdc\x01\xf1\xd0o\xe4\xdd8\x01\xef\xa2\xec\xb3\x9f%i\xc5\
+\x85_\x17\x97\x01\x8b\x08\xea#\xb9\x89\x80\xa0\x923\x04N\x13?!n\xac`R\x86\
+\xd4\xd0b\x00\xe9)\xa1\xd7%L\x98\xe8\x1d\xce\xc3\xbc(\xa2\xfc\x1d\x80\xe7\
+\x7fx\x9ep\xd9j\x82wO\xfc<\xf8\xf6\x13\xdb-[\x8ew\x06\xfe\xf6\t\xa0/-\xf2\
+\x8e\x1dz<\x95\x10\xc0\xc6>\xaa\xb0B\xfc\x89\xb1\xa0\xc5D\x81a|z2\xe6\xb9y\
+\xca\xd3\x15\xfaC\x99\x89|\xeb\x1cC\xfc\xf2\xe0\xe4\x1ddKh\xbe\xb2C\x83\x00\
+\xc9\xc5\xe1b\x80f\x8d\xd9\xb5)\x1b\x01,[\xdd\x012\x12U\xb4\xf1\x9a\xf8\xb8\
+\x91\xe7\xdc8\xf1/^i{l\xe97\xdb\x17+\xf0\xf6\xb2!CF_\xd3\xe0\xe4\xa5k\xff\
+\xaa2\x13\x95\xa8D\xc3\x80\x8dr\xa7\x08P\xcdA\x9a"\x08\xce\xcc\x18\x1a\xab\\\
+\x15\x96z\xa26\x8fYs4\xd8\xb8\xaf\xdcb\xfd\xffq\xb1\x08\xb1\xa60n\x07&\xf9\
+\xe1\xfd\xact\xb6\x16\xcf\x0c\xdbq/\xcc\x0f\x9f\x140\xc3\xcd\xc7p\x07\x848\
+\x94!\xdb\x9a\x94\xee \xacN8\xd4!\x943\xb6\xa4\x14\n+\x95\xf4\xa5\xbfVs\x08?\
+}6j\x19\'\xf6\x91\x1a\xc0\x9e\x82\x92\r\x90\x8d-\x8c}\xaaB\xa9\xa4\xb4RD\xfa\
+\x8c\x04N\x03\x13\x01\x84\x90\'\xe9\x18\xa8\x19\x96\x1d\xb0\xaf\xd91\x92\x89\
+\x1aw\x91\xd8\xc8w\xe9+\xba\x98/A\x9cy\x8b\x8a9\x90\x1f\x90\x93\x192\xa9+[*6\
+\x80\x02\x04\xdd\x15\x8d\t\x93\xa8\xc6\x18\x9b\n\xe2b1R\x99&\x00\x8d\x08\xf2\
+2\x11\xa2\xb2\x97\xca\x19\x99q\x8d\xce\x8b\x04\x87\x96\xdd\x88YHv\x84~*OjZ\
+\xdd\'`8\xc8\xc8/\xbe\x0b(\x9fAO\xc1wq^@\xc4\x15[\x97b\xd1r\x82\xf2\x1dU*\
+\xba\xe5\x858\xf4\t\x02\x94wcF\xce\x9c\x12_\xf3:O+I\xce\x8d*\xda\xe4\xda\xb7\
+\xd0 ~\xc2\xf8GT\xf0\x07\xd6\xfb\xe0\xc27\xce\xf0\x8b\x86:\x0e\\\x0b\n\xdd\
+\x9f\xe0\xf4\xc6k\x81<\xfd:\xa2\xc0\x81N5\xfe\xe4\xd8\xf0\xbe\xfd\xca$S\xff\
+\x11\x01D!p\x96\xd1M\xc7E\x1b5 D\x11\xe2T\xdf\x80q\x97\xe4\xea\xc2G\x04\x11k\
+\x9a)p\x00\x00}\tn\xef\xf8\xa7\xefB\x15M\xcf\x0f1\xe8\xb5lC\x8c\xd3\xb8v %\
+\xef\x89\xc4\xb1>\x182H? \xc4K\xd9x\xe5\xce\xb1"\xb6\x14Ia\xd5>T0\x1b)\xa7\
+\xf2qQ!\x02\x00\x7fZ\xb5\xc9\xd2\x94\xd2|\x82\x06\x07V5Tat\x0e\xd2\x0fe\x1d\
+\x96o}\xd4\x14>\xf5\x97\xf6qmfT\x82\xa9\x91\xc4\x1b\xef\xd4^\xd0$\xd0b4\x94\
+\xbe\x07\x86;\xe5F|\x00\x19\x08\x02\x82\x15/M\x1dQu2\xab4\x06\xed\x1f|\x1ds\
+\xf7UMSc\xbe\x86Z\xf9R^[\xc1\xc6\x00\xad\x9a\x8e\xb4\x14r\xde \x1d\xab\xb8r\
+\xccFi\xa4\xe9v\xc9\xa9\xc4\x13P\xbc\xd0\x03\xc9\xb1\xc0M\xc7ZZ\xe5\xdb\xe5q\
+\x10KxzEa\xe9q>\x17\xf5C\xfc\xe0y\x98\xa6\xf79VY\xc5\xba\xcc\x83\x0f!\x1f9\
+\xaazDu\xf9>\x1b\xa6\x9cIi\xdaY\xf5\xf2\xc1\xe0_X\x99\xa3\x9f\x90\xe5\xc1\
+\xc9]"\x8a\x00V\x80\xa0F\xe4\xe1\xfe\x9cB\x03\xc9T\xba\n\x1c\x12\xc8\x08\x10\
+\xf7\xa0\xef\x98R\xa20@V\x80\xd6\xef\xbd\xaa^\x15\x7f\x82\x0f\x1ec\x95\xaf\
+\x12\x16\xea\x10\xcf\x9a\x7f\xae\xc4\xab\xcf\x8fd\xf3\xd9&\x9ekv\x1d\x06>\
+\x0e\xe1\\5t\xc4\x82\x10\x15D\r\xcf\xbc\x08\xef\x1c^v\xc3\x10hc\xa2\x8f\x00\
+\x8dO`\xa0\x14\x95\x17y\xbcc\x9fB\xb7\x03y\xc7hq\x9dW\xae\x07\xe8l\xa1h\xc5F\
+\xe7\x03\xd92hb\xf2\x06BG^dQI\xc6\xe2\x07\x8e\x00g9\xdfM\xa4\x96DdG\xebw\xc8\
+\xdf\xa3\xf9\xc3\xfc\xc0\xa3\x18\xcct\xcc\xdf\x17\x10\r\x94\x11E$n\xe448\xda\
+\xfe\xf0\xfc\xa1\x82\x8b\xdc\xbbf\xd3T\xa1]@\xf4N\x1b$\x86\xa1G*\xda\x892\
+\xac\x8e\xe6\x8a#\x00\xe7\xb7\xb7\t\xd9\xc0@\xa3\x9d\x0cT\x95_\xad\xe9\x0ey\
+\xd8\xcd\x8f\xc7\x99\x1d\xefW\x83\x91\xc4\xb7\x97`\x8cf\x82BA\x91O\xdfT\xcbi\
+\x16\xd0\x8a\x9b\xbb\xbe1\xae\xb9\x8b\xbaAC2^\xe1a\xfd.\x82\x99\x97\x14\x13\
+\xfa\nE\xd2q\'H\xf0g\xcarj\x00\xa1LI\x11H\xf2\x1e\'\xa9b\xd6\xca\xe3\x91)D\
+\xa9\xb2\xc5\xfbX\xb6 \xca\xde!\x8fP\r>h\x02\xa2\xdb\xc9\x88\xf0.\xfd/[\x11\
+\xca\xa5\\\x0e<Qv9\x1bL\xd2\xa8\x15\xc8\x96r\x91Im\xc1\xb7\xaa\xb6\x84\x00R\
+\xbdc\xffU\xdb\x98T\x85G\x08\xc8\xe1=\xa5S{\x8d\x90\xd1\x92d\x8by\xb25\xe6Ch\
+\xae\xf9\x1a\xb4\xa2\x00\xf0}?\xd6L\x99\xc0\x86\xd6\x03\xd7\xfc\xe9*\xd9\x05\
+Op\x80\xce\x87q\xde4E\x9c\xa8Z\xea8\x96*  +\xb3\xb4s\x88\x10\xa0\x15\x16\xf2\
+O\x03\xbb\xe8X\xe6\xd2\xc9x\xa7\x93Fc\xa4b\xc3aV\xe44\x9b\xe4\x03\x88\\X\x13\
+\xb5TAZ\'\xa0:4R\n\x0cs\xc9\x87|I\r\xc4)\x035 \x82\xe3\xf6\xb6\xc3\x08Krk\
+\x1dm\x87\x85\xa6\xee\xee\x184\x10/\x1c\x01\x04\\\xa4@2\t,\xaaP\x9b\xd6\xa3\
+\xca\x1c2\xa67\xeb\xfdR\r\x96\xad\th\xfei\xda\xff\t\xd0sL\xe3\x87\x8c}\xd6{\
+\x9c\x83(I!y\xa0\x08\tV\x00\xe2=h\x8b\xe9\x19\xfd(\xa4D\xe1\x99\x84\x85R\xa9\
+ Q\xf9P\x94\x10\x15\xdf=\xa2:\x00\xa9\x17l\xcd\x1d\xfe\xcb\x99\xc4\xd4\x8a\
+\xb0Nt\xcaAN\x1d\x94\t"\x82\x07\xa3\xda\x1a\x10\x17\xbe\xd5 ^\x9b\x85}E\xee\
+\x18J\x0e\xdb\x8a\x88YIq\x90\xae\x9c\xf3-\x1c\x91\x1b\xa9* ,5a|R\x16h\x1b\
+\xd2\xfc\xb47D\x07p\x85\x80\xebp\xd6\x8d\xce\rG\x00A\x91#S}9\xca\x15\xf2"S!\
+\x94\xd7jy7\xc5\x1a4=\xef\x14\x18\x99\x81.\x1b\x13}!\xe8\x10\x1b\xc1\xf8i\
+\xacB\xb2\x0e\t\r\xc0\xfc\x164\x1a\xa9\xf7\x8cI\x12\x880\x06\xd4\xd1\xcb\x08\
+\x08\x04Ho\x00W"\x9dAd_\xa8+PF\x80!\xc7\x02*\x92\xcd\xb6\xd4\xa7\xa1\x07\xa0\
+^\xe2\xca\xeaC\xbbX$U\x81W\x8d\x90\xc6\xb7(\x16\xff\'9\x9cX\xe3 6\xd3P\xe5X\
+\xb8\xbc\x07\xb9\xd4@\x11\x06\xae\x88\x89\x8d\x8a_\xa5I\x14\x91\x13\xfd\x8e\
+\xc9\x83\x10\x93lUM\xd0e\x89\x07|}\xa0p\x12\xd3I\xba\xbc\xd4\xe7\xec{~Wh\x0c\
+3\t\xc89\x94\xf6#\xb0@m\x80K\xd5\xb0l!F\x91/\x87\xae\x92#\x89_.\rij@/\x1f.4\
+\x8d_T\xf9Xwz\x00c\xc2T  Q@\x17\xee\xf0\r\xc2\x11:\xdd\xe1e\xf6&g\x0b\x87>\
+\xe0\xc2riP\xadGS\xd2\xa6I\x909e\xa7cc\xe7X4\x97\x16\x90\xbc\x9a\x80\xd2\xf0\
+\xd4\x11\xa0q\x81\xe7\x84.\xfe\xaa\x08\xc0\xcb\xcdz\xfb\xb8\xc6\xc6\xc2\xf3J\
+\xbc\xa0b\x98\xe1\xd8\x10\x9a\xee\xe5%yg\x10\xd5\n\x0e\xafK4\xb1}\xd3\xa2\
+\x04\x91KP8\x92N\xe3\x93*`\x15\xa7\xcb\x8c\xcd\x14\xa9x\xc2\x86\xcdt\xa2\xd9\
+\x8aD\xba\xfdm\xb4\xd00|;3a\x01Y\x0el\x05\xa4\xb9Q\xae2\xb3a\x9c\xc1\xc5\xbb\
+\n\xdd\x9e2\x9bhS\xa9x9\x13\xc8V\xa2<\x8b\xe8Th\xd9,\x8axs\xa0\xa0\xf8<\x10H\
+.j\xa2K\xea\x94\x8f~L\x0fk\x08\xc5\x02\xf0z5\x04\xd0\x9f\xaa \xe4m\x8f8\x8d\
+\x9b\xbc\x1d\x91\x03X\xbc}\x17\xe5\x95\xd3\x08\x07\xe0]k\x0e\x0ce@\x08U6\xdc\
+\xd0\xbdU\xe5\x15\xd5\x8b\xe5\xc5.\xd2t\x9ez\x7f\x12\x01\x8e\x1a\x08DU8\x82\
+\x18\x87\t=\xd5-\x00\x808r\xbd\xd0\xdb\xea\x8a\x0f\xb5\x16zE\xb5\xfe\x84\x80\
+\x9b>\xe1\x08\xe2n\x8d\x82\x8b\x9a ,\xf8y\xec\xd1u\xf1G\x1b\xaal,\x8b\xf4\
+\x0cU\xa5\xaf\x159q\xf5\xae;\x80\x98\x89\n\x04Pc\x84\x8cM$/\x1a\x91oK\x1bx?r\
+13\x95\x1b\xf6\x088|\x10\xa4$\x0e\xec\x01\x9c\x7f\xfe\x08\x01\xddx\x0c\xadJ\
+\xfa\xbae\xa2\x83V\xa3\xed\x94\x92*x\x14\x04\xa0\x1f2\xd1\xd3\xcc\x7f\x13\
+\xc9\\MS\x0bH\xa8\x85\xbfJC\xb4\xb6Y\xd2\xd0F\x94\xe6v\'\xa7\x8eo\xbct\x9eT\
+\xb0*\xe4\xa17\x1a\x14\x10t\x1d\xfe\xea\xc87\x95AZgl\x86\x18b\x8f\x19e\x00UN\
+l\xad\xc9B\x00\xdb\x1a\x90\xf4\x84\x0e\x90^\xa257\rJ@8\xcbJk\xc3\x1fu\'0e\
+\xf3\xfcV\xae\xadd\x02\xab\xa5*;\n\x11\x04\x85\xef\x98\xd0?\x91\x96\xb0\xbe\
+\xe8\x9aD\x91%\x03\\\x90J\x9cSs\xd8\xdc\x17\x10\x90Rh\x19\x890$jG\xa2\xb0\
+\x8a\x11\x03\xb1\xe8\x19\x08U e#\xa1`\xf5;\xca\x89\xbbK"\x83\xcf\x05Et>\x0e \
+\x80\x80\xebs%w\xc6/\x97Z\\$Q\xa2\xc6\xb8\x07MTR\xff[n`;v\x1d\x96:7$\x80\xe4\
+\x19\x14\x81CW\x81z\xb0\xd9\\3\xe3>p\xcaAP\xad\xef\x94w\xf8\x15\xbe\xea\x9d\
+\xb4#\xc9I*bF\xc6\x9e\x14p\xbe\x14\xd9f\x1eM\x97\x96^\\\x02\x90\xbe\r\xbfl$9\
+\xed\xf4\x13\x07\x95=\\\xab\x14\x80\x8d\x00\xc7Y4F\xbf@@\xe7\xd0\xa0E\x1fC"\
+\xaf\x99\'\x00\xcb\xa5\x9c\xae\xf6c\xf0\x1a\xa1\nf\xd2\x14\xd1f\x1e\xcbKQ\
+\x08\x86I`\t\xd9&\xe0\xc1\xca\xfc4,\x14yB\x10\xd5\x82\x9f+\x8d\xe5E6\x89\x00\
+sy\x98\xf7\xba\xe5\x9fK{\xa7\x1f\xdf(\xae\'uH\x97\x052O\x11\x17a\x90\xd1\x04\
+\xc9x\xab\x9a;f\xd2\xd25\xa1\x17a \x148\xdb\x0ctq\'PP\xe3O\x16\x0bjI\xa6\xd8\
+\xf7K\xaea~\x1f \x0cDm\xea\xaf\xb7\xa6\xe2\xd1\x11\xb3l\x9e\x87\xf6n\x08\xca\
+&F\xfaA}9d\x8a\xc8\xe3\x8a\xb2\xf38\xd3\xaaGgj{6p\x1c\xcde\x9a\x00I-$R\x17AA\
+\x80<\x1f\x10\x95\t\xd35\x05C\xed8\xe3\x98\x8f!e\x1eh\x17j\xcf\xe4\x93.\x87f\
+\x04X\x87\x93t\xa1\t"\x06\x1c\xb6L\xd4"\xd0\xfc\xa4\x07\x1d\xcf\x8d\xee\x15\
+\xed\xccMF\x8d\x13@\x11`\xd1\x16\x08 H@\xaa\x9f\xafQY\xe7\x03\xbahhC\r$AX\
+\xc9z%X\x11\x81>\xa2\xc9\x9ean\x8d(p\x803\xf4Q\x8cs\xe1\xa9t\xbc\x0bD\xc7\
+\xb5\xaa\x0fr\xe2O\x91F/:\xfe\x87\x089\xc6\xc8\x07F\xf9\xbc \xa7\x03\xc8\x91\
+\x10\x87\xfe\xea<\x89x\x02\x04\xbdg\xbb\x80\xa4\x1c\xc4\xdab\xc2\x01\xab\x91\
+-\x0f(kD\xc5\x80O\x1b\x03z0&V\xe0D\xef\x02i\xb0"9R\x81f-\xe8\n\x11U\x06\xaa\
+\x13\xac\x1c\x08\xa0kA\xc3!\xa0(7]\xfdu\x13w\xf1\xc5/!\x94\x1c\x0e\xf1x\x9f\
+\x17\xf15\xeflcT\x1a\x9f \xc8%\x98\x80\xa8\x07\x00\xa9P\xae\x0c\x80I\x0f@JZ\
+\x19\xe42/%%\xfd\x00\xd5\x19TW\x03\r\x04PuV\x8d\x00\xca!\xd3\x00\x15\xe0Q\
+\x1f\r+\xfa\r\xb5\xbe\x01\x802\xc9\xcc|\xf8\xa1\xb0\x10h\xbc\xae\x86\xde)[0F\
+\x01\x96.\xd1@\x18\x0b\xb9\xb9@X}*O\xc0y\xa2\x91T\x83k\xf3M\xc1\xbf\xc37\xe2\
+\x12y\xcd\xc3\x07\x04&\x8b\x8c3o\xec\xc0V\x04\xf4\xdb\x0cUe\xc5\x903\x92F\
+\x9d){ \x9d-\xea\x11\xd0A\xcf\xc8s\xbc\xa1Bi\xd8\xd2,\x1cb\xc2"P}\xc2\x10@2\
+\x88\x06I\xa4\xc6\x1f\x02\x88Ze\x10X\xc6\x96\xdbFR\x967\x11T0\x95\x10\xa0\
+\x05\xa2\t\x82E\x88\x0c\xf1qdq\xc8\x11\xa0\xa0\xc5\xa9\xb7\xea<\x10\xa5\xea\
+\x08 j\xc0\xee4j\xbfr\xfc\x9b\x0bA11\xe5\xc9P\x11\xf5\xa7\x17\xca9>\x18\xd1\
+\xd9\xe2z\xb1\x13\xa6L\xff4\xb7P\xe5\x00\xc97\xc4\xc6\x15C@+\x03\x98W\xae\
+\xdc5\x8e\xe9H\xb4\x80\xc0\xca8~C\xa6\xa1\xda\x9a`\xc7@\xd8\xe1\x06\xc2\x08q\
+\xb3\x80u$\xc2AG\x1e\x10\x0e\xd8+\xe1:Y\x96\xa6?T\x07\xb3\x130SfD\xaa\x8f\
+\x98\x89P\xc0C\x88H>k\xa4c\x01\x85\x9f\x9c\x1b\xa6.\x06\x1b\xb5(\xf1B0\x18\
+\xc7xy\x90\xa2[\x92\xc2q\xb2\xb3e\xea\x90\xd8\xc4\xa1\x12\x05\xc6\x89\x06\
+\x8f(\x84\xe7~\xa4{\xa8\xcd\x1e\xd9NDX\'\x00\x04\x05m(;\xfa\x87\xdf\xa4\xa2R\
+\x81\xcb\x06\x90d\x1d\x01*\x1e\x84\xc2h\xda\x9b^1C$\x1d\xd1A\x17\x858QRY\xcc\
+$\x1f\xaa\x16+\x86\xcb\x84\x82\x00@\x85C\xc0\xf4\x01\xfde\xde\x00\xa7M\xe3\
+\xc9"t\x1fk\xfb\xb6Ou\xbd\xaf!\xc0r\x11j\x8df-\x02\xf8\xfc\x04L\xcdlKZB\x84\
+\x97PU.:\xf2\x05\xe4\x85\xa5-\x06\x86\xb5\xe9]\x02;\x85\xe0\x9cf\x17rK\xc5"\
+\x82\xda6\xe6\x18\x12!\xa3K\xf3\xcf\x90\x81l\xd6)\x95\x82D\x86\x0c\x85\xfe/\
+\xa8 \x95\x9d\xd2\x11\x0f\x01\x85\xe1\x83\x03\xf9\xca\x87\x15\x08z\x03\xd4\
+\xd8\x18\x19\x1eCS-k\xbc4\xc4_\xf4\xfe\xf4 \xb4:\xab\xcf,\'3u\x1a\x87\x96\
+\xf2\xa8\xa6\xc0\x0e\x82\x06\xeb _/!\xaa\x88\x8a\x03\x0fb\xa1\xa3\xbb\x05Z\
+\xdc\xd3?\x85@m\x9b\xfe2\xcb<\x06 +\xfe\xbc\x0f\x88\x8bg\xc6z\xe5\xcbQ3Aw\
+\x0f\xb2\xa8\xa9|;\'\x9eATPm\xa5\x95\xfe\x98t\x01\xba\x9c+\xd4\xcd\x82\t\x03\
+\xc348\x80\xf4\xbd\x03\xb5RG\xdd\xc4p5$\x02xN\x9au9c\xa2@&|\xbb\x1b\xa8C\xdd\
+\xb2a\x91\xf14\xa7\x1a\x83\xa3\xb2-W\x8c\xa8\xe6\xc2%r\xc3w\x07\xd5\x0e\xe1\
+\x9f\xcf\x91\xe4\x9a\x00s[\xa9\x00\xea\xdd\xc0H!?\xd0\x95WI\xe8\xf9DL\x1a\
+\x9f\x8e\xe7\xf0\x00\x8e\x1f\x19\xac\x86\xacuf\xbbh\x8d\x96\x03m\x938\x1e\
+\xfeU\n\xa6\x14\xe4\x97O\x1cQ\x00\xc0\x11\xe0\x1cqS\xf5~T\x8e\x81\x94\x10\
+\xd0I\xfd;#\x7f\xa8d\x08\xcf-g\x0b\xa4*U2[.e\x1a\xdfy\x8b\xcd\xc7*UZF?[\xc4\
+\x8a\xb5\x91>\xe4\xcb\xc6\xe0\xb8\xe4\xc3\xaf\xc3\xac\x94\r\xbf1\x9a\x8b\x08\
+Pf\x83F\xce\x94\r\xadO\x98\x9e\x9fK\x05b(9\x01\xd1g\xceg\x88\xb9|\x88\x0b\
+\x05\xb9\xe3f\x94fi\xa5\xb1\x8b\xa6\xb2\x8a\x0eHQ\xd5j(\x81\x00\xc9^G@9h\x8b\
+9\xe6b0\xadX\xc4)\xd2.\xe7\xa1J`\x08}@\x839\x98\xe8a?\xe9\xdcxr\x87\x13\xb8|\
+\x9a^O\x87\xb1\t9\xea}\xea\x81\xc0@\xdd\xc2\x16,2z>\xbd\xd6\x96z-xh\xd6t\xa4\
+\xb5\xf8\xef\xe9\xad\xc5\x07#\x06\x85\xcc\xfa\xea5^6\xcc\x84\xe4\x84\xe7\xb4\
+\x99\x82\x00\x05SI\xa4\xaa\x7fB\xf2T\x8fK\x959v\xe6<G\xad\xe5\xbd\x05D\xce\
+\xfc\x84\xd0g\x08\xc0u\xf3\x95ce\xe0\xa9~\xe20C\x1f\xe7\xcf\xbc\xcaT\x15\xbf\
+r\xb6\x84\x06>\xa2\xdan.N\xec\x02\\4\x9f\x80)\x03&\xb9*\x04\xd0\xfaui!\xd6\
+\xad\x07\xd9\xe8\xb8\xcf:\x89Z\xbd\xc0^\\\xd5\xc2\x00\xfd\xd4\xf8\xcfe\x85\
+\xdf=H\x1cX\xeaA;F\x10\x89\x80\x13\xff\xa7\xf5>\x03\x1a\x94\x06\x82B\x9e_C\
+\x17\x18C6\x8a\xaa\x05Y\xfd,\xa2Z\xa3\x90=l\xce\x1f\x05)\x9b"&\x04T\x1c\xc0#\
+&\xc0|+\x0cPhf\xc9\x86\xb7\xeeq}\xc0\xb73\xd2\xdc<o\xac<\x80\xc3X\x80\xf4\
+\xa0\xa7P\xec\xc5\xe9^!(\xe5\r\x81\xa1\x9c%\x17\xde \x8c\xd2\xaa\x00\x14\x9a\
+n$)\x1f\xc7\xacw\x02\xd0]\x10\x92Z\xd4\xa1\x7f\x15A?\x97)T>\x9e\x13\xa0\xb9\
+\xaa\x98g\xd1K\xf1}^_1\x12k\xb9\'\xa3\xbb\x9c\xb1\x9c\xafX\x1f\xf50\xb35\x81\
+G\xd3U\x92\x144A\x8dx\xac\xcd\xd8\x152\x01\x9cl\x96\'\x94\x8a\x0f\x13\x03\
+\xc4\x91\xa3\x8d\xcc6\x1fV\x10<\x14{\x15WI9J\xe53\x86@\xd4C\xb96\xac\x92J\
+\x81\x92p|bb\xd7\xca\xdb\xcd_k\xdb)\x98\xee\x9b\xe8\xa4N\x1fR\xad=V\x84\x9d\
+\x96|\x03\x88d\xe3\xd7X\x81\xef\x07V\x01 \x87\x80H\xa5\x1e\x01\xc8\x91#\xd3:\
+6\x1bP\xec\x97\xedKj\xaf&2\xb3\x03z\xadm\t\x06\xea\xd6_u\xa8>|\x1f\xb9\x11\
+\xd8D#\x84/\reT\xa1\x93\x8e<\x00\x84\x838l\x1fH\x14\xa6M\xb0\x99\xa5\\f\x1b\
+\xca\x8f\x16*\x1a!\x90L\x97L\xe2\xd9\x19\x80\'?j-\xbcp\xbf,\xfeTu3\xf4\x9a\
+\xd1\xd4\x12*\xec\x02$%\xe9\xd1\xdb\x96C${\xd1\xa6\xb1\x9604\xf2\x9a\x8a\xc1\
+1\xcf\xa9\xf5\x00\x00\x16mIDAT\xd1\xb9\x15\xf3\x11\xe5\x11\x01\xad\xb6<\x02\
+\xb0Pr\xcf\x9cSgA\xfa+\xc5\x80\xb59\xd7\x1f\xcbp.\xc2\x19C\xa2z\xbc\xe3#\xa8\
+\riMW\x9d\xae\xf2\x08\xc7\xafmi\xcag\x1b$\x11\xa3\xa4\x8c\xa2\xdf\x1f\t=\xe7\
+\x95\x18\x00\xfd\x83\x88\x96\x7fKj\x01\x0c\x9d\xa6^K\x12b\xa5\x10\x11Q.\x95j\
+\x942\xa4,6eUx@*\x07\x9d1\xcc\x04\xf6X\xb8\x92AF\xa8]\x9b\x14\x03\x08$\x89c\
+\xcde\x9c)!u\x06\xf5\xb0\xe8\xa0G\xe9-\xe6\x87\xf5\x8bb\x9d]\x11\xd0\x0eg\
+\xc5z3h\x99\x15\x1a4\x1b2\x95\xcf\xc6{\x00\x87\xcf\x18B\xe2\xaa\xf2[\x82\xb1\
+F\x8d\xb8gB\xc5LJ\x1e\xe9\xb6em\xd5\x19\x93\x1e\xa6\x00\x9e\x18\x02H\xaeqk#\
+\x12\x06\x86y\x1b\x08\x14\x11\x80j\x96\x97\xd4=P\x82\xa3\x93\xbaX\xac\xab)i\
+\xfa\xd5\xc2N\x9f\x1d\xaf\xd3\xc6\x1fU\x03B\xa2<o\x89\x97\xa6\xac5\xd0\x87Q\
+\x00\xd4\xcb"\xfeT\x98\x0e$bt\xa6\xd0\xf4s\xb0\x0b\xca\xa1!\xbb\xca\xf8\xb7\
+\xe4\t\xf2\x8b\xf6\x0e1[\xaf\x87\xb0\x8b\xd8\xd9\x1bP,E2\x07\xcd-CC%WF\xb6n\
+\xde\x1c\x16\x7f\xc7\xc1\xe8\xa34\xd9\x1c\x8b\xe2\x00\xc7\xe3\xc6\xb5_Z\x06\
+\x00\xd7|09\xea\x03\xdb\x16\xb3X\xdd\xbfG\x15\x84\x1f\xcfG\x90\x17\xa5\xf8\
+\x08\xe3\xdfUf\xed\x10\xc6 \xef\x10XI}.|&\xfe.~@\x87\xa0\xfa\x10u\x04-\xacT\
+\xac\x9e\xa1\xbc\x90>\xb0\x8d,W\x8b\x03\x08\xb0\x88\xd4\xaa\xbe\xed\xc6\xb8\
+\x085\xe1\x8b*5\x8eS\x1c)\xe1\xadl\xd1*eS\x11Y\x92\xda\xfa\x04\x1e\xe2\x00 \
+\xcd\xad9\r\x16\xb2S\xf7^\xd4\xa4\x97\x12\x99\x99\xf3X\x11\xa8\xdf%+\x95d\\\
+\x10\xbc\x8f\xb7\xc9O\xf4\xc1GH\xce\x03\xf2\xd4j\xd6\x00h\xcd^\xb0\x88\x92\
+\x99g,\x99\x8d~\n{\xc6\x94\xd2\xc8+\xa3b\x88\xdf;\x08\x8d\xa4\xc2\x18\xfe\
+\x99\xc0J\x048\x11\xa3]\xdbA5\x03\xbcw\xc9\xd0\x82\xe43\xba\xa0\n\xd2Z\x82\
+\x0fv\x81\xbe\xb2\x1f}\x1a\xd4\xe2J}=\x04\x8eR\x92M\xe0"\xad\xa6\xbc\xa0\xd0\
+P6\xf8F\xc0@\x14D\x95\xf6\xf9\xe2\x8f5\xf4\x97\xa3\xfc\xb5`"\xa0r\x9e\x9b7}i\
+6\x88mD\x1b\x89p\x90\xd4N\xf3\x83_\xd3\xe1|\x98\xc4\xc4\xcfB\xe2\xef\xfc\x11\
+\xbf\x02s\xa4\xfej\x08 \x0c[\xc9\xba\xc05\x91f\xb0\xa6\xd1F#%h\x82\xeeR7\x16\
+Yxd\xcdYan\xe6\xab\x1c\xb2l\x88\xdd\x8a\xcczL\xf3\xd2\x0f\n\x15\xa7\xb9b\xf3\
+\xd1\xe6F\x1b$k\x81\xfcJn\x7f\xcb\xb2f\x0cBd-\xdeg\xfa\x9c{\x13\x9aJ1`\xc0\
+\xc7A\xac*\xdcho/\xab\x08\x95SA\xfd\x1c\x18\xc9\xe8x\x04\xfa\t\xda)\xd7\xb5\
+\xe4\xf9\xc9\x10\xe3\xd0\x88%e\x1d\x8d\x14\xbd\xe2\x13\x0e\x9a/M{\x00\xf2\
+\x91\xe0\x06\x14\x9e\x13b\xfe@\xc5@\x0c\t\xe4\xf1%\t\x05\x85\xa8\xd9\x11-\
+\x17\xaaZjoR\xbfbOG\xd9J\xae\xdc\x1a\xce\x0f\xf1\xce\xe2G\xe6Vh\x04\xe7\x9b?\
+\xb8\x8f\xec\xe1\x96s&\x82\x0e\xd45\x05\xc6\x01\xc4\xcf\xb7\x93\'\x10=\x10\
+\xc5\x00\xa0\x89\xbf\x9b\xea\x8a\xe3\x97\xf6\xbbND\x8b\x15\x02\xb7U\x01\x1b\
+\x19m\x85\xe9\x93x\xcb\x95}HV\xaf\x8bF\xa1tz\xa0\x1d\xef\x9d\xf9\x01\x80\xa8\
+\x1bp\x84\xf0&\x83-\x80d/Z\x0c\xb0\'\x10\xa9cP\xd2\xd3ll{\xbd\x11\n\xc7ZDA\
+\x17\xe8L\xe8ro\xef\x9b\x96\xf6!\x88\xbftP\x8bk]%d\x84d\x96\x13\x1a\x80\xb9;\
+\xa1z\x97\xb7\n\xf5A\x12\x08f\xd9\x13\x1c\x00p(\xc4\x93i\xfa\xc8\x8a\xc4U\
+\xe3\x13\x13=I\xc1\xb3!\x85\x88\xe3\x11\xcb\x0e\xdc\xe2x\xdf\xcc\x0e\x88\x9c\
+:\xd5\xc5mj\xbct\xe4"\xdc\x15\x06;\xbb\x10h\x08V\x01\xda\x9b\xe2(\xa3:\xdbD0\
++\x88,\x848\xe7\x8da\x01\xb2GA\xf4\x07m\x8d\x17\xb9xq\xc6\x99\x1fF*\x06-{\
+\x18\xf7\x15\xcf\\\xab\xd1\\\xd2\xe7\x96]\x8c{\x86\x802\x10<\xb9\xc37\x08\
+\x05I5D\x8e\x1dh\xc2l\xb9\xf2\xe0=\x8d\x91\xb5k\xac\xf17\xca&X\xd8\xea\x80\
+\xf3^\x1a\xcaY\xedZ+~\xe9\x9d\xe2\xd21!y\xcd\xa8\xb3\xc5gr\xc1\xeaI#\x83\xee\
+\xe2\xb1x\x89\x00KZ ;\x95\xd8\x848c@)\xd8\xc1&f\xd3\x00t\xc8\x91\xdd)M\xeaA\
+\xe2 U\xedpN\r\xbd%3\xee\x8a\x9dm\xa5\t-d\xe4P(\xf1\xd9S\xf3\x04\x12\xf6\x94\
+\x19\x93\x94\xa8\xea\xc7\x95\xfc5\x89\x00J\x95\xa7[\x92qQ\x01x\x19\x8f\x83:4\
+I\x1b\x95\xef\x17\xc9\xef\x91H\xbb\x80\x90\x11\x99Of\x812o\xbar\xc2\x0b*\x84\
+\xb2\x1b8D)\x92\xcc\xcf\x134A_\xc0\xb7\x11\x90u\xfe\x0b-\xd5\x93\xd5X\xf2\n\
+\xfe\x14\xa9]\x0e\x17\xe8\xa6s$\xcap@\x93q\x07Q\xaeM#P\xd0P\x88zE\x9al\xbfG\
+\x11e\x05\x02\x00=,\xda\xf8\x0f\xb1k\x8b\xd0Q\x04\xb8\x14\x932\xb0C\x9e\x95!\
+\xa3\n\x0c\x96t\x83\x04\x82\xa52\'\xde\xd6\xab\xca*>-\x91\x0e\xf6\xe7\xdb*S\
+\x8bE\x8c\xcc\x1c\x01\x02\x06Y\x12\x81\x10~\xd5}\x08\\Y\x13E\xaa\x9f\x01T<A+\
+\xd4\x9d\xdaM\x9bg \xdb\x14XR\x92L\xca\x99\x17\x80\x1b>\xbevLY\x9cY\x15\x19\
+\x14\xcaJH\xd6\xc4`\x82\x97,\xf8\x16\x9e\xfe\xc9V\xcb\xcfty\x87V\x0cU=\x94\
+\xd5\\N\x11\xbe\xa1\xcd\x1c\xfb\xd5S\xe3\r\xf6\x0f\xac7q$\xd3\x80]\xb5\x96\
+\x89*\x83\xd1\xed\x14\x1du\x118\xd7@|\xa62\x1d\xd0@c(!\xd3\xbd \x19y\x06cc[\
+\xc2\x0b\x7f0\xbb\xba7Z\x8a\xce\xeaD\xe9\xab\xdbuX\xe2GC"\x83\x83\x04\x12P\
+\xbc\xb3\x82\xd6w\xcd/\xdeP\xce\x14`\x05=\xa4\x8a\x11\xbf\x9d\xbc\x12[\xfb\
+\x8a\\^\xbb\xc1\x9d\xc4\xd6\xaeu>\xacx\xebU\xf7\xc4\x04\xc4\x98\x8a \xa4j\
+\xdf\xe2h\xd1\x9dh\x80WA\x00\xf8\xd4\x12\x97b\xdd\xe6\xc3\xdfl\x9b\xccD1G\
+\xa8\x1f@\x96\xdc\x13\xcd\xac\xb5g\x89j\x9fT?\xa1\x9a\x98\xc0o*\xe13\x82\x16\
+\n\xea\xe1e\xcdG3\r\x1aD\xcd\xa1\xf7\x96\xd0\xad.\xfc\xd5T\x89\xe7\x11\xec2p\
+\xaf\xbc\xb3#\x178g\x94\xb0T\xdbT\x86\xd6Ie\xcb\xc9\x84\x00_\xd5q\xe6\xd6]jr\
+\x15\xd61\x8f\xb4\xc9\xd9\xc7\x97\xd5\xf7\x18"_\x0f\x93\xa3\xa43\x9b\x02\xfa\
+\xb8\xf0\xd0n\xd1\x9a\x05\x89)\x10z\x80\xe8\x86L\x08\xc5\xed\xb3\xf7\x92\x02\
+7B\x1a$2\xe6;8\n\x9aR/\x80P\x93\x17h00<\x9b\x0cy\xa1](\xc3)\xf0\xe3e2S\xeaK\
+\x1a)\xeb$\x9e3\xa3\x8640\x1dE\xf1\xadR\xf8\x7f\x00\xe0\x12$f\xc1i\xe8\xec\
+\x14\x18\x94\xad\x1e\xc0\x86\xa3\x83V\x89\xc1v\xd8\x1d )\x99\x88\xb1\x9c\xbc\
+b\x9d\x06==\x7f\x0f]{\xd5\x9f\x04\xdaAl!A\x92\xd4\xeb-\xb8\x03\x1c\x014\x8b\
+\x8b\x7f\xb0\xf4j\x0c\xb16\x06T\xe74\xd0v\x94r\xc6\xa9\xa9t\x03\x02\x0c\x0c\
+\xf1z\xf2\x0f\xe7Kg\xd5k\x11\x90\xcf\xa7K\xcd\x03DMP\xf1\x9a\x86Z\xdfG1\x90\
+\xdd\x06\x90=\xba\x1d\xddB\x92\x85Huru9\r\xfaR{\x91{C\xd5\x98Y\xa9\x12\xea\
+\xcfK\xc7?\x00\xa4yx\xdeG\x1d\xb5a\xb4\x9f\x19\x8c\xcf\xe4v\xae\x829\x85\x86\
+\xa3d;_\x8a\x1a \xb2"S\x00\x0e\x05\xe1Kp\xf6\x15w2/\xc0(\xf0`\x85b\xb4\x19r[\
+A\xa6\x93\x94~\xbd\x88\xd6\xfc\xc1N\xe2\xa7l\x9bK\xc8\xea\xec\xa0[\x08\xdde\
+\x8e_!x\xbd\x1a\xc3\x13\x13d\x89oP\x08\xda#\x8f*\x97\xdcE\'\x06\xbbX\x19o\
+\x91=MWjT\xdd@\x85\x14s \xa5Z\x1a6\xa4\xc5\xa2!\x11\xa0\x04\xa7\xfd\xa4T\x14\
+\x9f\\Bd\xe7UbA(\xd4-D\x83T\x1d\xf9\xd8{\xf4f\xfb\xb0C\x10\x83\xd7p`\x80Ww\
+\xec\x11\x1f(J\x1b\xee\xa4\xa4\x0e\x84r\x0c\xe1HSD\xd9"i+\xb9[ \xfe9sq\xad\
+\x02\xa0!\x80\'f\t\xa0\x89\n;`\xd2\xfaT\xc4\xbe4\xb3D\xf53\x95\x06g($\x1cT\
+\xb4(\x1b\x02\x0cL\xbf,\\\xfa|\xbcr\x84:\x87\xec*\xb6\x1a\xb2\xfaVr\x15\xdd \
+R3\x05\xdda\xc5\x80\x1e;!z Fq?\x80$\xfb\xe0\x17%P8\x82\x03A*\x00\x06t\xb5k7\
+\xdfg\xd2\xebN\xe5z\xf3\x06G\xd4/\xf2\xa7\xea\x1b\xb6V\xc8(\x88\x93E\x16\xf4\
+y}\xba\xec\xf9`\xd4\xdevB\xe25\xb3\xe1\x8c\xfeON \x86B\xd6\xd3\xd74\x06\xafK\
+\xf6\xa3\xc7\xa9\xe4\xc6*\xd1=tD@Ju)\x039(\xc6(wa\x11\x7f\x17\x11\n>W\xaaO\
+\x886\x98f>\xe2q**\x98\xe9\xc8\xa6\x12\x8a$J<^FU\xef\xe8\xd26u\xba\x1c\x96\
+\xbe\xed\x1b\xa4\xb2>\x15V\xe78\xddu\x00\x8d)\x95\xaf\xf5\x0cj\x18\xd0]\xa6\
+\x90\x14\x00\xd7\xc7\x12\xad\xf9H\x0bG\x80\xd3\xaf\xcds\xc6.&RYI\x16\xd9\x95\
+Kc\xc7`\xd0\xe4[\x87@A\x97\x12G\xc7\xa5\xfc\x15/4A^@\x07\xd9\xe5u\x80\xa1Qkh\
+\x16C\xbf\n\xd7\x06\x1fV\x9ab\xf1y\x14>3\x92\xa1\xabA\x8c\xea\x7f\xc7\x1d\
+\xc0\x18\x08X\x82\x07\x81\xe9\x99=d\xc2\xb3E\x04_ \x97n\\\xf3\';\x94b\xe3S{\
+\xea\xd7\x004-\x0b$&\x0f)\x9aA\x1c*\xc9\x84d\x90X\xed\x86u`S\x00~K;@\t\x89\n\
+{\xd7]v\xc4\xe7\xa2\x04F@!U\xec\n\n\x05ID\x87\x0e/\xae\xe3`\xc8\xa0\xd9\x9a\
+\x9ccA\x83\xf8`vm\x85\x8e\xfeQ\x0c\x7f\x88d\x0e\x15\xfd\xdf\xe9;\xd5,\x08\
+\x1c\x04\x92\xb8p\x18\xectr!\xbc\x00\x94\xac\x88\x8ef\xce\xe9\xbe\x00\x05L\
+\x13\xe2\x00\x13\xd5\xe9\xa1\xda`\xe4\x9e\x9ca\xb72\xa3\t\x866\x81\x9e,\xf2\
+\xb5|9vY\xf2\x04I\x06GkY\xe6@\x10l+~a1xt\xe18\t\x99+U\xc4e\x8e\xee\xba6\xa9r\
+Dh\xfa\xcb\\K\xb0\xd9\xc0\x15)_C3\x91\xc0\x9c5G\xe3\xf8\xbd>\xf2Q\x8eZ\x1c\
+\x14_\x87\x1b\xf4\x04u\xfd\x92\xba\xd7q\xe0\x95?\xd8\x01\xe4<Jq\x9b|\xa5"\r[\
+\x8aS\x97-/\xb3\x8b\xd1\x15(\x0c\xbf\xb3\x10\x8bxol \x15x\xa2\xe2\xd4\xd0\
+\xa0\x88\x9d{\x04\xc0\xb0Fn<d\xdfPH\xea\x02\x08o\x12 \\\xa4S\x10\t\x07\xb2\
+\x8bh\xb4\xe7\ri\x8br\xb0\x94\x82\'M)\x1fg\x8d\xa3@\xdb\xee\xb0}@Z%\xe9\xc1.\
+8SA\x90W\xa1\xc2\x07\x88wQ\xeab\xd4k\x08P\xea"9|\xb0\xb8ZA\xd9F\xa1\xc2\xe3\
+\xc3\x8a\x9dO\x9aqJ\xcd\xb5\x17W\xa1\x1a5\x9e\x12\xa28\xb02\xb6\xa834\x87$\
+\xcaL\x0f\xe57\xc3L\xba\xf6\x1e\x00\xfa\x1e\xd96\xc4\x89\x198?T\xe8\xe9\xdd\
+\xc7\x98Vy\x8dU\x0c\x80b\x89\x14\xa8#\x08\x0cT\xba\xff\xad\xe9\x1f4\x88\xe8\
+\xa8\xd2\xb1\x00\xed\xe0\x93g\xbd\x87j\x85\xe4\xa7:\x18\xe8\x0c\xf4\x1a\xf1z\
+y\x10!\xbe\xa8\xaa\x0f\x86Gi9GzF\x87p\x80b\t\x9d,\x014p\x87\xe89\xa9\xc4YB\
+\xa0\xec\xa8\x9e\xd6B\x1c\x15D\xda\x1eH\x1c\xb6\x1f,S\xa8\x89rW\xd3\x0b\xd5\
+\xfe1f&\x8b\x00\x1c\xaf\x8a\xbf\t\x96O@\xfd+\xcb:79\xa3\x10\xa9Q\xe8\x1e\x86\
++\xa5q$\xbbD\x8ddz=hUn;b\xa7\x1b>\xb9\x0c\x9e=\x01\xae\x15\x91\xc8\xa8\xc2\
+\x81\x8d\x00\xf2\x05)\xfb[\x04\xf2=\xcc\xf1\xbb\x88\x8c\x1b\\#\xb7\xedx\x94;\
+\x1a;\xd4+\xa2;\x06\xee\x9fi*\x8bt\'53Z?\x8by\x84\xa2\x07\xccwX\xab\xd1\x8e\
+\xe0@\x15\xad\xe90\xb2\x06\xe1,%\x04H>\xf3\xdf\x98h\x82\xb1l\x9c\x94\xa8\xe3\
+\xd1uRfo\xf8X\xb9\xe0\xa1`\xa04\xb7X\x93zf\x14C\x90P\xd2\xa5\xe2\xfbYe6\xc5{\
+S\xa5\xd3S\x988\xb0\x14\xaf\x8b\x1f2\x00\x8dE\xbd\xf8\xae\x03r\x814s\xe0\xd4\
+K\x92\xa3AC\xe5Y\x04\xf6\xbe\xdce\x04\xa9\x92I=\xecNs\x06\xf3Lx\\\x03\xeasb\
+\x0f\xf2\xd3\x81\xd4\'\x9a\xdb\xc8RA\xe0\xc0$.\xbc\x10\x0f^\x97\xbd\xe1Y\x91\
+\xcc\xb4_\xc2\xec@+FB\x19\x19\x88\x92\x93yWR\x1bT\x04G\xb4\xbb\x85\x00\x96\
+\x81v\xb4\xe7\x85\x15\x0fK\x06\x02\x14m\xe8\x93T\xa7\x95\xcaR\x8e\xd7\n\x02\
+\x18\xe4-b\xa2\\v\xb1H\x98\x02z\xd7\xcd\r\xc4\xeb\xff\xd6\x1bE\xab\xe8h\x91\
+\x1d\xc8hp\x88q\\\xd2\x0c\r\xc2\xe1\xc2\x92\xc0\xfaV\xd1\xf3h\x80\xb2#!\x16\
+\xaa\xac\xd3\xe5\xb5\x07\x0eT\x1chE\xfbR\xb64\xaf)d\x92P5\x0f\xf4r\x1f\xc8\
+\xd8\x19\xea\x1c\x8cY\xbd\x08\x9e\xfe\xc1qH\r\x14\x11\xc0\xaeYL\xfc\xc2k\xe0\
+\r\xfbG\x81\xddN\x81u\x9atn\xadF\x97\xfa\xb6],b\xb1\x8e\xfd\xaaA\x07\x06\x1a\
+\x07\xe6S\x07\xbez\xf6 4)\xb9T\\&\xc5\xd7+9\x00u\xf9\xb2\xc5+\x9a\xa3\xa8z\
+\xa4\x00\xc2\'7\x94"ri\xb2f\xe5\xb3\xf0\x8d9-\xa7\xf6\xa6\x92\xfa\xa0(\x9c\
+\x8c-\x02\xe9\xbaW}D\xd2\x19\x80\xc8\x96b\x84\x95\x89\x7f{e\xae\x07s\xaa\xaa\
+c\xa5\xb1\xa6\xb8\xe2\x12\n\x9acX\xe3m\xe4CF?%6\x85!c\xef6FcX\x9bj\xa1\x9c\
+\x9c\xc1\xf4\xce\xcf0\x17`\xef\xfbB\x96\xa6\x08:\xdbJ\xb6\xcdh\xab\xb0\xcb\
+\xd3\xdfB6\xa3\xca\xd6\xb9\xec\xa2{\xb38\xc8\xe4\xd7iv\xb4\x1ai\xa9\xc0n\xb8\
+\x03\xe5+\xa5\xd5\xbb\x88\xc8\xe1\xafl\x1c~\'\xb0(\xd2\xe2@\x88s\xb8\x93\x05\
+\xf9\xad\xe8\xf8\x87G\xf2Z\x97\xef\x9d\xe4\xf5\xa0\x91\xbffj\x00\xb4\x91\xcd\
+\xf4\\\x9d"\x86\xb6h\x8dqN\xad\xab\xd7\xb2R\xaa\xbf\xa5\x11\xf7\x8a\xe2?\xca\
+@c\xca\xc2k\xbc\xba\x86V7\xc7\x7f\xa1j\xbd\xb2\x0c\xb7\xd8MO\x7f*8\xf2\xf5\
+\xcf\x00\x17Iye\x16\xa7\x10\xf7\x89\xbf\x94\xda\xa5\xb91T\xe8\x1e\x1fDD\xd8\
+\xe83\xec\xb2\xee\xd3\xaa*\xd6\xe3\x00\xf8a\x80\xba\xfeg\xe31\xe3\xd1\xa8\
+\xe53\xd7\xc2\t\x94\x16)\xbf\x10U\x1fX\xc3\xc5\x1cR\xdf\x89\x0en&_dd\x0c\xd2\
+`~\x81\xba\xf0\x95F\xcfTB/\x97\x1b\xd5_NO\x83W\xe6\xc6~\x14\x19\xe7\x89a:-\
+\xeb\x1c\x14\x08\xe8\xa3\xae\xaa1\x94\xc7\x95zP\xd3\xd2\xdedU\xca\xa3\x98\
+\xd03\x80\xbbg%\xbc\x07\xa6\x12zb\xb2\xd5\xb5\x8e\xe8\xd9\xe2\xb2i\xa9\x1d}\
+\x08L\xeb\x02\x89\x1b\xb5\xa3\xd8\xe8q\xb9\x17 \xb0N\x13\xa4(^\xf1\x7fz\xed\
+\xdd5\x84\x9d\xdd\xa3@n\x19\x14\xf8\xa0\xb0\x1d\xbd\xe5\xec\xd8\x05\x1c\xc8o\
+%\x97\x1e\xae(kk\x94S\xb8\xf1\xfaZavS6\x94\xb4\x9d.\xac\xec-8yrQ\xafp\xf8\
+\x16\xe2\xca8\x0c@h\xc6\xebv\xc9\xc0XI\xacwm\xba0\x01\xfa[\xceAt\xb7\xa7\xfe\
+\xbed\xad\r\xd6*%YG\xd22\xa0k\xbe\xa6\xa2\xdcP\x00!\xf9\xb0\xad\x1e#\xd0!\
+\x96S\x03y\x04\xb0]Z^G\xf7!\x1a\xa1@\xce"\x10X\x8b\xee3f\x01y\xb7\x80\r\xf4\
+\xea)b\x92\xa6\\\x02\xa0\xd9\x9c,%\xb3\xca!\xabz\x12\xa5\x8f\x16\xe3-^S\xf0\
+\xc8\xe4d<\x0f\x03\x01\xc3\x07\xcc:\x1f\x0bF\xa5\xad\xe2L\xcfZJ6"\xf2\xcc]\
+\xc1\x96Ta-\x05\x0f\xe8\xbe&K\xbb\xdb\x93J\x08S\x00Z\x82\x0e\xd70\xb3\xf4!+\
+\x1fQ\xb1o\xa8T\xd4\xe1/Y\xe1;\xbd)\x9er\xcc\xaesK\x84j\x8f\xafX\xd0\x067\
+\xcf\x00\xd0,\x00\xb8\xe0\xc0\xeb\xeedM$?]\xa2U\xe8\x014MP\xe9i4\xe3J\xcbL\
+\xac8D\x0c\xa8\xf6\r\x91B\x1c\x98\xa2\'\xe0\xa9\xd0\xfc:\xe8D&\x9d\x80\xd2\
+\xd1\xdaRgnqP\xd5nZ-\x84\x82\x07k!\xa8X\x0b\x02x\xd6\x1c\x80\x074\xeb\xc1 h\
+\xd7e\xaa\xaaLs\x02e\xf3\xa0%\x17\xd4\x06\x10qK\xce\xb5r\xe8\x8e}q\x86\x01\
+\xa1\x12\x01>\xfe\xcf\xcay\x9c\'\x11\xab9\xab#\xaf;\xf9\xea9\n\xf5.E\x12:\
+\xee\\\xdb\n\xe06\x86m\x0b\xe2\x18vi\x01\xca-7\x07\x00\x98,\xcb\xa2\xa21\xf2\
+s((\xb7i\xd69\xd8(\x0c|\xca\x84\xed\x81\xd0Q\x15{\x12fz\xcd\x96A\xb6\x96D\
+\x01\x0f\xb6\x8az!\x01\xbc\x83\tk\x8a\xd4:\x86d\xfc+A\xee\x84\x10Yv\x80\x01\
+\x1e\xf5\xad\x93\xec\t\x1dk\x11\x88\x91b\x08\x08\xbd\xc4\'\x10T\x07,\xdf\t\
+\xa8\xc1\x01\x91\x19\r\xae\xfd^7r\xb2*j\xcc\x88\xbfQ\xa4^\xeb\x99\xa6>\x00\
+\xe8\x0f3\x95\x019\xfaM\x0es\xd1\xb6\xcf\x17R}\xf3%J\x87\xe0\xa4\xf8\x16J\
+\x02\xd6\x05\xde{\x00\x82\x02E\xfc\xc0\x10P\xea}s\xc9V\xc3Aa\x13\x01y\xea\
+\x8d\x8a\xceM>E]\x12\x01\x96\x83\xa8\x0f\x1f\xfd\xa0\xa9\xac\x89\x14aWvn\xa8\
+s\x86\xb1;\x808\xc5_\xa2\xc4\x1f%\xad\x85\x01\x96r\xea\xa9\n\x05\xb0\\]`\x06\
+ix\xf8-\xda\\`{\xbe\xd6\xe3\x03\x0c\x01YS\x86\x9d\xa3(\x18\x07\xcd\xf1\xb2N\
+\xad6\xa4\x9f\x1d\xf1\x96\xca\x17I\xd4mA\x1f\x9e\xb2\xde*\xc2e\xc70Om\xbe\
+\xb6\x98T#\xfe!lDT\x065{\xfc\xb8X{\xe1\xe8\xae\xb1\xc1\x83\x8a\x00^P\x1f%\
+\x84\x9a\xfe\xf0\t\xad\xc9\x0c5\x05\x9d\x82\x84\xe2\xa2I\x1b\x99\xbe\x08P\
+\xc6\x00\x00\x117p?\x80\xf9\x03\xb6\xf8Wjm@\x17\x7f%q2%R\x1e#\x94\x04\x19\
+\x02D\xbdV\xf5\xdey\xd7/hl\x11\x903``\x80\xe8\x16\xbd\xd1a\xd4*\xeb\xa2\xb4&\
+O\x97\x99\\\xae\x00U\xf3>TN\xe4\x0f\xe0\xc1\x17G\xff\x8a\xac\xcfk\x03\xb1\
+\x0bY\xea(\xe4\x89\x10\x04\x98\xe6X\xd9\xadkxd\xc7\xcb\xf4N)9\xbb\xa5\xac\\\
+\x05x\x92\x90\xf7\x18"\x06\xd2\xe6\x85\x99\x99\x98\x00\xe3&\xb3\x15\xa4O\xb4\
+\xf2\xc7\xe33\xc78\xb9\x9eZ6\xb0L?TV\x17\xdb(\xe4\xcahx\xf0\x8e\xcf\x0e\x94j\
+2\x08P\xfc\x03OoD\x95\xea\xc4?\x97\xc5\x1b9\x93F\xe2\xae\x1e\x1b\xfa\x88\x17\
+\x13\x01|\xceV\xb7\xf0\xc2S\xf9\xce\x9a\x80Qm"\xcd\'\x8f\xe0)\x9d\x86\xdbE\
+\xb8\xaf\x99\xfb\xf5\xb9\x82\xed\xe0(6\x1d\xc7>`H&\xf4(\x1e\x00;z&EB\xb3P9"\
+\xa7\x0c\xfeT\xb7\xc0\x84\xe6\x01\xe4\xa7\xdaP\x94\x96Z&^\xe7\xce}W\xe0@\xa5\
+Z\'\xa7r&\xcf\xa7\x88\xed\xee\x95NF\xc7G\\\xc9\xf1\xe9\xaf\xf6\x07\xbf\xf3\r\
+\x17p\xacX\xa0\x99_f\xc2x\xc2\x96?^\x82\xb0\x91\xea\xe3\xba\x80{3?\xda\x8aP\
+\xc8\xc8\xb3\xb4\xb1\x1b)g4\x84\x82\x00\x83\xaa"(\xdc\xd7"\xb4g\x0c\xa9I\xa0\
+y1&0\xf9DW\xd1\xb1t$b\xbat\xce\x9f\xaa\xa3\xc8\xb6\xdf\x12\x18Ka\xf4x\xa56\
+\xc2\x82i\x08\x04\x022\xa3\xcd\x91\x9d\x0c;\x0ce\xfe\xad\x15\x1e\xb9\'\xac!\
+\xc0\xe3+\xd3F\xe3\xf1\x1co\xd5\xc5"a\xb55\r`\x98j:\xf4x\xee\x16\xf1\xdc\xba\
+k\xa0\xa6\xda^\x0b\\\x8f\x83\xde;\xa6!@\xf9\x15\xf1sj-~}\x01\x07\xc3\x89\x1f\
+<\xa0=\xbd\x0cI\xcf\xfb\x96\xfe\x00\xf0[S\x8b\x92\x0b\xf4,\xa2\xe1\xd8[N\x02\
+CU\x88a\xae\x80\x03&\x18a\x14T\xe3\xe6biY\x1f\x8fS\x94?3\x0c\xeas9\x99\x95\
+\x96t\xc2\x1fc\x9d\xe2\x00\xbaz\tZ\xe84\x17\xb5\xbd?\xb3@Mp\xe2\t$vcB\x00\
+\xf7\x10\xf1\t\xa8Sf\xaa\x10\xe7\xb54\xc9\x80^\x9a\\\xda\x08\xc8\x14\xe6)\
+\xaa)\x91\xc5\x85\xfb\xff-x1\x0bp\xf1\x03\xd6\x82\x00\xc8F\x97\xc6\x8c\xe9\
+\x13d\x17\x012S\x05\xaf\\{]\xf4\xb2O]f\xe6\xa2(\x01\xc9\x05\xd3\xf1\x9e\xb3\
+\x80S55\x10\xaf\x80! 3z\xe2zA\xc8\x14U\x02T\xb8u\xc3\x85\x82\xf8\xd9\xb5\xe2\
+\x0e\x98\x01g\xec\xa3\xc8\x04\'\xe1\x8fI4\x94\xa7\x92r\xa6\x1e\x8b\xb2\xa5P\
+\xed\x89]\x14%\x11\xc0\xaa0\x18\xa9W\xb6\xf5\x08`\xa52\x86|\xc5\x8cEF\xfcZ\
+\x99\x9a\xc0X\xc3\x8bE\x9ck\xd7\xa8z\xf2\x16yO\x92*\xeb\xd4\xfc\x16\xef\xb8,\
+i\x87z\x8f\\H\xa6\xfaq)F\x04\xe5\xa2\xde\x80\xa1\x9cj=9c\x90%4K\xbb\xd7e\xc2\
+(\xc1\x84Z\x87g\xae`\xf7y\x05\xe8\xc20g$\xfa\x9cQ\xa5\x8a\x16\x07\x100\x11\
+\xae\x00M&;j\x05l\x11\x90\x9a\x158\x00\xa2\x95:xQMy\x05\x01\xcc\x16\xd4\x90i\
+\xb9\x94v\xb7M\xae\xd5\x07\xb5\x9bG*\xe6)h\xe5\xd4Y;\xa3na\x90G`s\xd0\xfc*V\
+\xb5M\xf7\xca\x9f\x90\x03\xab\x86\xca\xd7\xf1\xc9\xd1\xafr\xa9\x102\xa9\x1bJ\
+\xa2\x8d4vu\xd3\xad\xae\xda\x9c\xec\x8c6\xda\xe0\x87<\x10\x9b\xed\x87\xdc\
+\x17/\xb8\xa7B\x94\x80\xa4\x1a\xac\xa8\x8dU\x9a\x84)\xc8\x93E\xb6\x08m\xf5\
+\x1f!\xe0H/\x19\x84\xba\xf8/\x95\xa1\xd3\\\x0b\x00\xb0KH\x82d\xcc\xab\xb1\
+\x05f2\xef\x9dPq/\x1c@tS\xad\x04\x99\xb3h\x9d\xc3\x91\xa4\xdaC%\\\xad\x13\
+\x01\x1a:?\xb3\x9aG\xef\x94\xe5\x1aV\xa5p\xbc;\x04i\x8c\xd5!\xd5\xe9p\x07\
+\x0e\x953$\x9c\x1f\xb4&kV\xcf\xf4\x9d\xb9\x1b\xa8A\xa4\x84L\xce\x9d\xdd\xee~\
+\xb4\xf2\x912{\xfe\xc5.\xdefK:+\xdf\xbd^\xdc\xd2\xd7~\x0e\x13\xaa*^vPGA\xb1\
+\xb1l\x90 (dd\xaf\xd26c\xf3.\x99\x96\x8a\xe7\xb71\xb2\x1fn\x93\xb4\x1d\xfe5\
+\xcdd\xc98\xd8!\r\x05\x86\x83r\xd1\x8a\xf5\x10\xab\x9c}.\x94\x99-u\xeej\xe0\
+\x8cPl\xc6\x84e\xe2!\xa6*M \xf6\x9e&\x19\xb7\x99H\x91\xaa.\x9c\xe3\xd9\x99\\\
+\'(\x84\xce#V#,q\xd0\xe6\xf5f\xa7\xcb\x17@\xe2$\x99_}aSxt\xb1\xdcR\xf9\x9ar\
+\xcf\x87\x04\xa7R\xb4\xdd\xf1ac\xcei\x81\x17=]\x1d\xd3f6\xd5\x93\x84\xb63\
+\xf3\xa7\x8d\xe3JA\xd0\rUoc/\xa8\x81x\xc1^\xd2\x01\x9a\xecq\xc1\xf2w#\xaaW.T\
+\x96\x80vh\xf6H\x91\xb1t\x93UTH\xe3\xe5\xb8at\x8a\r\xca\xf8\x9e\xd9\xfdFb\
+\x0ep\xd9(m\xbbn\x871o\xe80\xeeA\xea\xd8!X\xb6\xea\xd3\xddu\x99\x8b~\xc9\xc0\
+X!\xf0\xe0\xad\x81\x97\x19O\x1d\xde\xb8\xc9]\x01j.<?\xe8\xe3,\xf2\xccK\xd0T)\
+\xcfI\xa7\xac\x9d\xde\xae"\x0e\x95\x90\x8d]\xc6\x81`\xad\xa3\xa1\xb2\x06zN\
+\xb3i\xfa_md\x87\x8fF4D,\x8f\xc6\r\xb1\xe8\x8bDa\xd8\x8b6\x01\xa3 \x0e\xd7j\
+\x1e\xb9u\x97\x8a\xa7\xeb<\xcb\x038\xf8\xff\x00\xddb\xc0\x06\xa4k\x96\x16\
+\x00\x00\x00\x00IEND\xaeB`\x82' 
+
+def getWizardBGShorterBitmap():
+    return BitmapFromImage(getWizardBGShorterImage())
+
+def getWizardBGShorterImage():
+    stream = cStringIO.StringIO(getWizardBGShorterData())
+    return ImageFromStream(stream)
+
+def getWizardBGShorterIcon():
+    icon = EmptyIcon()
+    icon.CopyFromBitmap(getWizardBGShorterBitmap())
+    return icon
+#----------------------------------------------------------------------
+def getDecisionData():
+    return \
+'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00A\x00\x00\x00+\x08\x06\x00\
+\x00\x00\'\x98\xe5y\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\x00\
+\x05kIDATh\x81\xed\x9a]lSe\x18\xc7\x7f\xa7_k\xd7n\xeb\xea\xbe`\x02]\xecpt\
+\x02c[\xa7|\x04\x13\x16\x13/\x0c\xc3\xa8\x89\x91\xc4xa4^3\x83\x04\tC \x18\
+\xaf\xf5B\x83\x89\xa0D\x83^h\xd4\x04T\x96\x19>6\xb6e\x1d n\x8e\x91-\x84\x91\
+\xc9\xa0v\xec\xbbk\xcf\xeb\xc5Y\xbb\xb6\xfb\xea\xd89-\xc6\xfd\xaf\x9a\x9esv\
+\xde\xfd\xf2\xff?\xcfs\xdeSI\xd2\xe9I\x95v\xbd}L\x84?\x7f\xff\xe9>)U\xeb\xd0\
+\xa5\xea\xc6\x8f\x92\x96!\xb0\x0c\x01X\x86\x00\x80\x94\xca\xc2\xf8\xa8h\xd9\
+\t,C\x00\xc0\x90\xca\x9bW\xec\xdc#\xca\xdcNzo\x0fp\xee\xab\x0f\xfe\x7fs\x82\
+\xa7\xa6V<\xbd\xd1\xc5S\xc5\xab\xa9\xda\xe8b\xcb\xcb\xef\x89\x85\xaf\xd2F))\
+\x8c\x955\xb5\xc2\xb3\xe1\t\xd6:WN}#\xb8\xf3\xb7\x1foG\x0f\xbf}y(\xe9\x8eH\
+\x89\x13b\x01\x00H\xac\xcc\xcf\xa6\xbc\xb4\x88\xcd/\xedM\xba#\x92\xea\x04\
+\xcf\x94\x03\x8a\xa3\x00\x8c\x8eM`1\x9b\x90$\xc5\x00\xfd\xf7\xfc\xb4^\xbb\
+\x99\xd4\x1a\x914\'\xcc\x05`l<\xc0\xf0\xc88B(\x06(\xc8\xb1\xb3\xc9]\xc4\x96W\
+\x92W#\x92\x06\xa1r\x0e\x00\x00\x81\xc9`\x0c\x88\xc2\xfcl\xb6{\xdcT\xef>\x90\
+\x14\x10\x9a\xc7\xa1\xb2\xa6VT\xcd\x03 Z&\xa3\x01\x9b\xd5\x1c\x89\xc6\xed\
+\xfe\xfb4z\xbb\xb8\xf4\xdd\x87\x9aFCS\x08\x9e\x9aZQ\x19W\x04\xe7\x02\x10V\
+\x9a\xc9\x805}\x1a\xc4]\xdf \x8dm]4|}D3\x10\x9a\xc5\xa1b\xe7\x9eE\x03\x00\
+\x98\x08\x04\x19\x19\x9d\x8eF\x9e#\x8bMn\'\xcf\xbe\xba_\xb3hh\xe2\x84\x8a\
+\x9d{D\xd5F\xd7\xbc\x00\xba{\xfb\xf0\xf9\x87\x18\x1e\x19\x03\xa0 \xcf\x81\
+\xcbY\x88\xc9\xa8\x0c\xb1\xf1\x8e\xb8\xf7\xcf\x03.\xb6v\xd2\xf0\xcdQ\xd5\x1d\
+\xa1:\x84D"\xd0\xdc\xdeIwo\xdf\x8ckMF\x03;\xb6\x96\xe3\xb0g\x00\n\x88tK\x1a:\
+\x9db\xd8[w\x06h\xbb\xde\xc3\xef*\x83\xd0K\x92z\x89H\x04@wo\x1f\xd7:{p\xd83\
+\xd8\\Q\xca\xe6\x8aR\x8aV\xad $\xcb\x0c\xf8\x06\xf1\xf9\x1f\xe0r\x16\x02\x10\
+\n\xc9\x08!0\x18\xf4H\x92DV\x86\x95\x1cG\x06\xc2^Rw\xeb\xfa\x85Cj\xad[\xd5\
+\x9a\x90H\r\x08;`\x9bg=\x05\xb9\x0e\x00lV\x0bUe%\x14\xe4:\xf0\xf9\x87\xf0\
+\xf9\x87"\xe7O\x04\x82\x8c\x8eM \xcbJI\xc8\xc9\xce\xa4j\x83\x8b\x1d*\xb6OU\
+\x9e"\x17\xd3\x05\nr\x1d8\xec\x99\xd8\xac\x96\x99\xc7\xf2\x1c\xf4\x0f\xf8\
+\x08LN\xc6|?\x11\x08\x02\xe3\xa4[\xcc\xe8t\x12\xabV\xe4`2\x19\tL\xee\x13\x17\
+N\x1f[r4\x96\xec\x04\xcf\x8c\x87\xa1\xf9\xbb@Y\xa9\x8b\xaa\xb2\x92Y\x8f\xf5\
+\xdf\xf5\x01\xe0\xb0g\xce8\xa68b<\xe2\x88\xfc\xc7\xb2x\xa6l\xad*\x8e0D\xef\
+\xfd\x87\x15\xfd\x0e`\xa1\xe3[\xca\x9f\xc4\xf9x^\xe4X"mp65\xb7w\xd2?\xe0\xa3\
+\xac\xd4\x15\xe9\x10\xf1\x8awDa\xbe\x834\x93\x81\xfaS\xd3\xe7<\xcc\xff\xb3\
+\xe48\xe4\xe7\xda#\x9fC!\x99\xc9`hQ\xd7\x07&\x83\xd4_l\xc3\xe7\x1f\xc2\xe5,\
+\xc4]\xbcf\x81\xf3C\x18\x8dA\xd2LF\x00r\xb23\x16\xbf\xe88-9\x0e\xf5\x8d\x7f0\
+<6\x0e\x80^\xaf\xc3jI\xc3`H\xbc\xed^h\xbe\x16\x010WL\xc2\x92\x00[\xba9\x02 \
+\x18\x92i\xf2\xdex\xe8\xb5G\xfe\xae\x1as\xc2\x0bo\x1e\x11\x9b\xdcE8\xb2l\x00\
+\x04\x83!F\xc6&\x08.\xe0\n\x9f\x7f\x883\r\xcd\xac.\xccc\x9bg\xfd\xfc\x0bE\
+\xe9"&\x93b\xde\x90,\xd3|\xa5\x9b\xd3\x1f\xbf\x9b\xfa\xc2\x08\xf0\xd3\xf1\
+\xf7\xa5\xe6+7"\x8e0\x18\xf4\t9\xa2\x7f@)\x84\xabW\xe6\xcf{^<\x00\x10\xaa\
+\x01\x00\x15\xe7\x84\xb3\'\xea\xa4&o\x17\xbe\xc1a 1\x10&\xa3\x81\x82\\\xc7\
+\xac\xed2,\t\xb0Z\xcd\x11\x00AY\xe6R\xdb_\xaa\x01\x00\x90^|\xe7#U\xdf\x0c?\
+\xff\xc6!\xb1\xdd\xe3\xc6b6\x01\x89Gc\xd6\xc5I\x12\xb6\xf4X\x00-*9 \xbaK\xa8\
+\xfe\x14y\xe6\x8b\x83R\xc3\xe5\xeb\xdc\x9f\x9a\xfa\xe6sDwo\x1f\xf5\x17\xbd1\
+\x13bX\xf1\x00d!T\x03\x10/M\x1e\xa5\x7f9Y\'y\xff\xeca$\xaaF\xd8\xd2\xcd3@\
+\x04&\x83\xb3N\x88\xf1\x00B\xb2\xcc\xe5\xf6\x1b\x9a\x00\x00\r\xf7\x13~\xfe\
+\xfc\x80t\xbe\xa5#R#\xf4z\x9d\x02B?}Kw\xf1\x1a^\xdbU\x1dy\x86\x80Y"\x10\x92\
+\xb9\xdc\xde\xa5\x19\x00\xd0x\x8f\xf1\xec\t\xc5\x11\xd1s\x84\xcdj\x89\x01\
+\x11\xad\xd9\x1c\xd0r\xb5\x9bo?\xd9\xfb\xdf\xdd^\x0b\xabz\xf7\x01\xb1\xadr\
+\x1d\xf6L+\xa0L\x96\xc3#c\x04Cr\xd4Bb\x01\x08!h\xd20\x02\xd1J\xcan\xf3\xb9S\
+\x87\xa5\xf6\x8e\x1e\x86\xa6v\x91\xe2\x1d\x11\x9e\x04\xa3#\xd0\xe8U\xb7\r\
+\xce\xa7\xa4\xbe|y\xee\xf5\x83bky\tY\x19\xe9\xc0\xb4#,\xe64M&\xc1D\xa5\xfa\
+\x9c\xb0\x90j\xde:*\xca\xd6\x15\xc5DC\x1f\xa9\x11\x82For"\xa0\xe9\x9c\xb0\
+\x90~\xf8l\xbft\xbe\xb5#&\x1a\xa08@\xedI0Q\xa5\xe4\x85l\xfd\xa9\xc3R\xcb\xd5\
+\x9b\xf8\x1f\x8c\x00\xca$\xd8|E\xfb.0\x97R\xf6\xfb\x84\x1f\x8f\xef\x97\x9a\
+\xda\xbb\x18\x1c\x1a\xa55\xc95 ^)\xfd\xa5\xca\xd9\x13u\x92,\xea\xc4\xaf\'\
+\xebR\x06\x00\xe0_Y\xc4y\xb5x\x97\x05\x86\x00\x00\x00\x00IEND\xaeB`\x82' 
+
+def getDecisionBitmap():
+    return BitmapFromImage(getDecisionImage())
+
+def getDecisionImage():
+    stream = cStringIO.StringIO(getDecisionData())
+    return ImageFromStream(stream)
+
+def getDecisionIcon():
+    icon = EmptyIcon()
+    icon.CopyFromBitmap(getDecisionBitmap())
+    return icon
+
+#----------------------------------------------------------------------
+
index 66fd95cf1ef1ea69e335b21484629c11a0aa5100..4a6ed85ad1502fb7173c49cf3f81b4e22ca03494 100644 (file)
@@ -78,7 +78,7 @@ class XmlCtrl(CodeEditor.CodeCtrl):
 
 
     def SetViewDefaults(self):
-        CodeEditor.CodeCtrl.SetViewDefaults(self, configPrefix = "Xml", hasWordWrap = True, hasTabs = True)
+        CodeEditor.CodeCtrl.SetViewDefaults(self, configPrefix = "Xml", hasWordWrap = True, hasTabs = True, hasFolding=True)
 
 
     def GetFontAndColorFromConfig(self):
@@ -115,7 +115,7 @@ class XmlCtrl(CodeEditor.CodeCtrl):
 class XmlOptionsPanel(STCTextEditor.TextOptionsPanel):
 
     def __init__(self, parent, id):
-        STCTextEditor.TextOptionsPanel.__init__(self, parent, id, configPrefix = "Xml", label = "XML", hasWordWrap = True, hasTabs = True)
+        STCTextEditor.TextOptionsPanel.__init__(self, parent, id, configPrefix = "Xml", label = "XML", hasWordWrap = True, hasTabs = True, hasFolding=True)
 
 
     def GetIcon(self):
diff --git a/wxPython/samples/ide/activegrid/tool/bmp_source/activegrid.ico b/wxPython/samples/ide/activegrid/tool/bmp_source/activegrid.ico
new file mode 100644 (file)
index 0000000..fb15dfe
Binary files /dev/null and b/wxPython/samples/ide/activegrid/tool/bmp_source/activegrid.ico differ
index 1d2008f10a6df4ea902d5d2dc444cf7940f42cf9..7992a831f0947c394f87b0b120c5c08e6886b34e 100644 (file)
@@ -1,7 +1,7 @@
+Middle-Mouse-Click on the tab for an open file will close the file.
 Ctrl-Space in any editor does code completion.
-Right-clicking on something in the 'Thing' column of the debugger's frame tab may allow you to introspect it for more information.
 Right-Mouse-Click in Outline window allows you to change display sorting.
 In an editor, you can add line markers via Ctrl-M and jump to the next marker with F4 or previous marker with Shift-F4.
 In an editor. you can use the numpad + and - keys to toggle folding.
-In 'Find in Directory', if you specify a file, it will display all matches in the Message Window.
-Breakpoints for the debugger can be set while the process is running
\ No newline at end of file
+Breakpoints for the debugger can be set while the process is running.
+A split window can be closed by dragging the sash to one of its borders.
\ No newline at end of file
index a521f0cf509d27cc5dbc2663c0cf9d12341867be..1de68e1bc91eeda0f3470bdffa1d4f8455ba123d 100644 (file)
@@ -407,7 +407,13 @@ _SaferCreateProcess(appName=%r,
                 elif env:
                     uenv = {}
                     for key, val in env.items():
-                        uenv[unicode(key)] = unicode(val)
+                        try:
+                            uenv[unicode(key)] = unicode(val)   # default encoding
+                        except UnicodeError:
+                            try:
+                                uenv[unicode(key, 'iso-8859-1')] = unicode(val, 'iso-8859-1')   # backup encoding
+                            except UnicodeError:
+                                log.warn('Skipping environment variable "%s" in execution process: unable to convert to unicode using either the default encoding or ISO-8859-1' % (key))
                     env = uenv
                 hProcess, hThread, processId, threadId\
                     = win32process.CreateProcess(appName, cmd, processSA,
index babdadea4286142b6b0dad6c117e564216f933ea..6b8035f33558231c30823174d576278548ac9ee4 100644 (file)
@@ -14,7 +14,16 @@ import copy
 import os
 import os.path
 import activegrid.util.xmlutils as xmlutils
-from IDE import ACTIVEGRID_BASE_IDE
+import activegrid.util.aglogging as aglogging
+
+# REVIEW 07-Mar-06 stoens@activegrid.com -- Ideally move the pieces required
+# to generate the .dpl file out of this module so there's no dependency on wx,
+# instead of doing this try/catch (IDE drags in wx).
+try:
+    from IDE import ACTIVEGRID_BASE_IDE
+except:
+    ACTIVEGRID_BASE_IDE = False
+    
 if not ACTIVEGRID_BASE_IDE:
     import activegrid.model.basedocmgr as basedocmgr
     import AppInfo
@@ -27,64 +36,6 @@ if not ACTIVEGRID_BASE_IDE:
 PROJECT_VERSION_050730 = '10'
 PROJECT_VERSION_050826 = '11'
 
-    
-#----------------------------------------------------------------------------
-# XML Marshalling Methods
-#----------------------------------------------------------------------------
-
-def load(fileObject):
-    version = xmlutils.getAgVersion(fileObject.name)
-    # most current versions on top
-    if version == PROJECT_VERSION_050826:
-        fileObject.seek(0)
-        if ACTIVEGRID_BASE_IDE:
-            KNOWNTYPES = {"ag:project" : Project, "ag:file" : ProjectFile}
-        else:
-            KNOWNTYPES = {"ag:project" : Project, "ag:file" : ProjectFile, "ag:appInfo" : AppInfo.AppInfo}
-        project = xmlutils.load(fileObject.name, knownTypes=KNOWNTYPES, knownNamespaces=xmlutils.KNOWN_NAMESPACES)
-    elif version == PROJECT_VERSION_050730:
-        fileObject.seek(0)
-        project = xmlutils.load(fileObject.name, knownTypes={"project" : Project_10})
-        project = project.upgradeVersion()
-    else:
-        # assume it is old version without version number
-        fileObject.seek(0)
-        project = xmlutils.load(fileObject.name, knownTypes={"project" : Project_10})
-        if project:
-            project = project.upgradeVersion()
-        else:
-            print "Project, unknown version:", version
-            return None
-            
-    if project:
-        project._projectDir = os.path.dirname(fileObject.name)
-        project.RelativeToAbsPath()
-        
-    return project
-
-
-def save(fileObject, project, productionDeployment=False):
-    if not project._projectDir:
-        project._projectDir = os.path.dirname(fileObject.name)
-    project.AbsToRelativePath()  # temporarily change it to relative paths for saving
-    if ACTIVEGRID_BASE_IDE:
-        KNOWNTYPES = {"ag:project" : Project, "ag:file" : ProjectFile}
-    else:
-        KNOWNTYPES = {"ag:project" : Project, "ag:file" : ProjectFile, "ag:appInfo" : AppInfo.AppInfo}
-
-    savedHomeDir = project.homeDir        
-    if productionDeployment:
-        # for deployments, we don't want an abs path in homeDir since that
-        # would tie the app to the current filesystem. So unset it.
-        project.homeDir = None
-        
-    xmlutils.save(fileObject.name, project, prettyPrint=True, knownTypes=KNOWNTYPES, knownNamespaces=xmlutils.KNOWN_NAMESPACES)
-
-    if productionDeployment:
-        project.homeDir = savedHomeDir
-    
-    project.RelativeToAbsPath()  # swap it back to absolute path
-
 
 #----------------------------------------------------------------------------
 # Classes
@@ -93,7 +44,7 @@ def save(fileObject, project, productionDeployment=False):
 class BaseProject(object):
 
     __xmlname__ = "project"
-    __xmlexclude__ = ('fileName', '_projectDir', '_getDocCallback')
+    __xmlexclude__ = ('fileName', '_projectDir', '_getDocCallback', '_cacheEnabled')
     __xmlattributes__ = ("_homeDir", "version")
     __xmlrename__ = { "_homeDir":"homeDir", "_appInfo":"appInfo" }
     __xmlflattensequence__ = { "_files":("file",) }
@@ -107,9 +58,15 @@ class BaseProject(object):
         self._files = []
         self._projectDir = None  # default for homeDir, set on load
         self._homeDir = None         # user set homeDir for use in calculating relative path
+        self._cacheEnabled = 0
         if not ACTIVEGRID_BASE_IDE:
             self._appInfo = AppInfo.AppInfo()
-        
+
+
+    def initialize(self):
+        for file in self._files:
+            file._parentProj = self
+
 
     def __copy__(self):
         clone = Project()
@@ -117,18 +74,13 @@ class BaseProject(object):
         clone._projectDir = self._projectDir
         clone._homeDir = self._homeDir
         if not ACTIVEGRID_BASE_IDE:
-            clone._appInfo = self._appInfo
+            clone._appInfo = copy.copy(self._appInfo)
         return clone
 
-    
-    def initialize(self):
-        """ Required method for xmlmarshaller """
-        pass
-
 
     def GetAppInfo(self):
         return self._appInfo
-        
+
 
     def AddFile(self, filePath=None, logicalFolder=None, type=None, name=None, file=None):
         """ Usage: self.AddFile(filePath, logicalFolder, type, name)  # used for initial generation of object
@@ -138,7 +90,7 @@ class BaseProject(object):
         if file:
             self._files.append(file)
         else:
-            self._files.append(ProjectFile(filePath, logicalFolder, type, name, getDocCallback=self._getDocCallback))
+            self._files.append(ProjectFile(self, filePath, logicalFolder, type, name, getDocCallback=self._getDocCallback))
 
 
     def RemoveFile(self, file):
@@ -150,7 +102,7 @@ class BaseProject(object):
             for file in self._files:
                 if file.filePath == filePath:
                     return file
-                
+
         return None
 
 
@@ -160,6 +112,10 @@ class BaseProject(object):
 
     filePaths = property(_GetFilePaths)
 
+    def _GetProjectFiles(self):
+        return self._files
+    projectFiles = property(_GetProjectFiles)
+
 
     def _GetLogicalFolders(self):
         folders = []
@@ -170,7 +126,7 @@ class BaseProject(object):
 
 
     logicalFolders = property(_GetLogicalFolders)
-    
+
 
     def _GetPhysicalFolders(self):
         physicalFolders = []
@@ -189,11 +145,11 @@ class BaseProject(object):
             return self._homeDir
         else:
             return self._projectDir
-        
+
 
     def _SetHomeDir(self, parentPath):
         self._homeDir = parentPath
-        
+
 
     def _IsDefaultHomeDir(self):
         return (self._homeDir == None)
@@ -212,7 +168,7 @@ class BaseProject(object):
             if relFolder and relFolder not in relativeFolders:
                 relativeFolders.append(relFolder)
         return relativeFolders
-        
+
 
     def AbsToRelativePath(self):
         for file in self._files:
@@ -224,6 +180,33 @@ class BaseProject(object):
             file.RelativeToAbsPath(self.homeDir)
 
 
+    def _SetCache(self, enable):
+        """
+            Only turn this on if your operation assumes files on disk won't change.
+            Once your operation is done, turn this back off.
+            Nested enables are allowed, only the last disable will disable the cache.
+            
+            This bypasses the IsDocumentModificationDateCorrect call because the modification date check is too costly, it hits the disk and takes too long.
+        """
+        if enable:
+            if self._cacheEnabled == 0:
+                # clear old cache, don't want to accidentally return stale value
+                for file in self._files:
+                    file.ClearCache()        
+
+            self._cacheEnabled += 1
+        else:
+            self._cacheEnabled -= 1
+            
+
+
+    def _GetCache(self):
+        return (self._cacheEnabled > 0)
+
+
+    cacheEnabled = property(_GetCache, _SetCache)
+
+
     #----------------------------------------------------------------------------
     # BaseDocumentMgr methods
     #----------------------------------------------------------------------------
@@ -231,7 +214,7 @@ class BaseProject(object):
 
     def fullPath(self, fileName):
         fileName = super(BaseProject, self).fullPath(fileName)
-        
+
         if os.path.isabs(fileName):
             absPath = fileName
         elif self.homeDir:
@@ -242,7 +225,7 @@ class BaseProject(object):
 
 
     def documentRefFactory(self, name, fileType, filePath):
-        return ProjectFile(filePath=self.fullPath(filePath), type=fileType, name=name, getDocCallback=self._getDocCallback)
+        return ProjectFile(self, filePath=self.fullPath(filePath), type=fileType, name=name, getDocCallback=self._getDocCallback)
 
 
     def findAllRefs(self):
@@ -256,8 +239,8 @@ class BaseProject(object):
         if not xformdir:
             xformdir = self.homeDir
         return xformdir
-         
-   
+
+
     def setRefs(self, files):
         self._files = files
 
@@ -295,16 +278,17 @@ if ACTIVEGRID_BASE_IDE:
 else:
     class Project(BaseProject, basedocmgr.BaseDocumentMgr):
         pass
-        
+
 
 class ProjectFile(object):
     __xmlname__ = "file"
-    __xmlexclude__ = ('_getDocCallback', '_docCallbackCacheReturnValue', '_docModelCallbackCacheReturnValue', '_doc',)
+    __xmlexclude__ = ('_parentProj', '_getDocCallback', '_docCallbackCacheReturnValue', '_docModelCallbackCacheReturnValue', '_doc',)
     __xmlattributes__ = ["filePath", "logicalFolder", "type", "name"]
     __xmldefaultnamespace__ = xmlutils.AG_NS_URL
 
 
-    def __init__(self, filePath=None, logicalFolder=None, type=None, name=None, getDocCallback=None):
+    def __init__(self, parent=None, filePath=None, logicalFolder=None, type=None, name=None, getDocCallback=None):
+        self._parentProj = parent
         self.filePath = filePath
         self.logicalFolder = logicalFolder
         self.type = type
@@ -316,35 +300,44 @@ class ProjectFile(object):
 
 
     def _GetDocumentModel(self):
-        # possible bug is if document gets replaced outside of IDE, where we'll return previous doc.
-        # originally added a timestamp check but that increased the time again to 4x
-        if self._docModelCallbackCacheReturnValue:  # accelerator for caching document, got 4x speed up.
+        if (self._docCallbackCacheReturnValue
+        and (self._parentProj.cacheEnabled or self._docCallbackCacheReturnValue.IsDocumentModificationDateCorrect())):
             return self._docModelCallbackCacheReturnValue
-            
+
         if self._getDocCallback:
             self._docCallbackCacheReturnValue, self._docModelCallbackCacheReturnValue = self._getDocCallback(self.filePath)
             return self._docModelCallbackCacheReturnValue
-            
+
         return None
 
-        
+
     document = property(_GetDocumentModel)
-    
+
 
     def _GetDocument(self):
-        # Hack, just return the IDE document wrapper that corresponds to the runtime document model
-        # callers should have called ".document" before calling ".ideDocument"
-        if self._docCallbackCacheReturnValue:  # accelerator for caching document, got 4x speed up.
+        # Return the IDE document wrapper that corresponds to the runtime document model
+        if (self._docCallbackCacheReturnValue
+        and (self._parentProj.cacheEnabled or self._docCallbackCacheReturnValue.IsDocumentModificationDateCorrect())):
             return self._docCallbackCacheReturnValue
+
+        if self._getDocCallback:
+            self._docCallbackCacheReturnValue, self._docModelCallbackCacheReturnValue = self._getDocCallback(self.filePath)
+            return self._docCallbackCacheReturnValue
+
         return None
-    
+
 
     ideDocument = property(_GetDocument)
 
 
+    def ClearCache(self):
+        self._docCallbackCacheReturnValue = None
+        self._docModelCallbackCacheReturnValue = None
+
+
     def _typeEnumeration(self):
         return basedocmgr.FILE_TYPE_LIST
-        
+
 
     def _GetPhysicalFolder(self):
         dir = None
@@ -356,7 +349,7 @@ class ProjectFile(object):
 
 
     physicalFolder = property(_GetPhysicalFolder)
-    
+
 
     def GetRelativeFolder(self, parentPath):
         parentPathLen = len(parentPath)
@@ -364,7 +357,7 @@ class ProjectFile(object):
         dir = None
         if self.filePath:
             dir = os.path.dirname(self.filePath)
-            if dir.startswith(parentPath):
+            if dir.startswith(parentPath + os.sep):
                 dir = "." + dir[parentPathLen:]  # convert to relative path
             if os.sep != '/':
                 dir = dir.replace(os.sep, '/')  # always save out with '/' as path separator for cross-platform compatibility.
@@ -375,7 +368,7 @@ class ProjectFile(object):
         """ Used to convert path to relative path for saving (disk format) """
         parentPathLen = len(parentPath)
 
-        if self.filePath.startswith(parentPath):
+        if self.filePath.startswith(parentPath + os.sep):
             self.filePath = "." + self.filePath[parentPathLen:]  # convert to relative path
             if os.sep != '/':
                 self.filePath = self.filePath.replace(os.sep, '/')  # always save out with '/' as path separator for cross-platform compatibility.
@@ -385,8 +378,8 @@ class ProjectFile(object):
 
     def RelativeToAbsPath(self, parentPath):
         """ Used to convert path to absolute path (for any necessary disk access) """
-        if self.filePath.startswith("."):  # relative to project file
-            self.filePath = os.path.normpath(os.path.join(parentPath, self.filePath))
+        if self.filePath.startswith("./"):  # relative to project file
+            self.filePath = os.path.normpath(os.path.join(parentPath, self.filePath))  # also converts '/' to os.sep
 
 
     #----------------------------------------------------------------------------
@@ -399,21 +392,29 @@ class ProjectFile(object):
         import wx.lib.docview
         if not self._doc:
             docMgr = wx.GetApp().GetDocumentManager()
-            
-            doc = docMgr.CreateDocument(self.filePath, docMgr.GetFlags()|wx.lib.docview.DOC_SILENT|wx.lib.docview.DOC_OPEN_ONCE|wx.lib.docview.DOC_NO_VIEW)
-            if (doc == None):  # already open
-                docs = docMgr.GetDocuments()
-                for d in docs:
-                    if d.GetFilename() == self.filePath:
-                        doc = d
-                        break
-            self._doc = doc
+
+            try:
+                doc = docMgr.CreateDocument(self.filePath, docMgr.GetFlags()|wx.lib.docview.DOC_SILENT|wx.lib.docview.DOC_OPEN_ONCE|wx.lib.docview.DOC_NO_VIEW)
+                if (doc == None):  # already open
+                    docs = docMgr.GetDocuments()
+                    for d in docs:
+                        if d.GetFilename() == self.filePath:
+                            doc = d
+                            break
+                self._doc = doc
+            except Exception,e:
+                aglogging.reportException(e, stacktrace=True)
+                
         return self._doc
-        
+
 
     def _GetLocalServiceProcessName(self):
         # HACK: temporary solution to getting process name from wsdlag file.
-        return self._GetDoc().GetModel().processName
+        doc = self._GetDoc()
+        if doc:
+            return doc.GetModel().processName
+        else:
+            return None
 
 
     processName = property(_GetLocalServiceProcessName)
@@ -458,33 +459,39 @@ class ProjectFile(object):
     localServiceClassName = property(_GetLocalServiceClassName, _SetLocalServiceClassName)
 
 
+    def getServiceParameter(self, message, part):
+        return self._GetDoc().GetModel().getServiceParameter(message, part)
+
 
 # only activate this code if we programatically need to access these values
 ##    def _GetRssServiceBaseURL(self):
 ##        return self._GetDoc().GetModel().rssServiceBaseURL
 ##
-##        
+##
 ##    def _SetRssServiceBaseURL(self, baseURL):
 ##        self._GetDoc().GetModel().rssServiceBaseURL = baseURL
-##         
-##   
+##
+##
 ##    rssServiceBaseURL = property(_GetRssServiceBaseURL, _SetRssServiceBaseURL)
 ##
 ##
 ##    def _GetRssServiceRssVersion(self):
 ##        return self._GetDoc().GetModel().rssServiceRssVersion
-##        
+##
 ##
 ##    def _SetRssServiceRssVersion(self, rssVersion):
 ##        self._GetDoc().GetModel().rssServiceRssVersion = rssVersion
-##            
+##
 ##
 ##    rssServiceRssVersion = property(_GetRssServiceRssVersion, _SetRssServiceRssVersion)
 
 
     def _GetServiceRefServiceType(self):
         # HACK: temporary solution to getting service type from wsdlag file.
-        model = self._GetDoc().GetModel()
+        doc = self._GetDoc()
+        if not doc:
+            return None
+        model = doc.GetModel()
         if hasattr(model, 'serviceType'):
             return model.serviceType
         else:
@@ -494,25 +501,27 @@ class ProjectFile(object):
     def _SetServiceRefServiceType(self, serviceType):
         # HACK: temporary solution to getting service type from wsdlag file.
         self._GetDoc().GetModel().serviceType = serviceType
-        
+
 
     serviceType = property(_GetServiceRefServiceType, _SetServiceRefServiceType)
 
 
     def getExternalPackage(self):
         # HACK: temporary solution to getting custom code filename from wsdlag file.
-        import activegrid.server.deployment as deploymentlib    
-        
+        import activegrid.model.projectmodel as projectmodel
+        import wx
+        import ProjectEditor
+
         appInfo = self._GetDoc().GetAppInfo()
 
         if appInfo.language == None:
-            language = deploymentlib.LANGUAGE_DEFAULT
+            language = wx.ConfigBase_Get().Read(ProjectEditor.APP_LAST_LANGUAGE, projectmodel.LANGUAGE_DEFAULT)
         else:
             language = appInfo.language
-            
-        if language == deploymentlib.LANGUAGE_PYTHON:
+
+        if language == projectmodel.LANGUAGE_PYTHON:
             suffix = ".py"
-        elif language == deploymentlib.LANGUAGE_PHP:
+        elif language == projectmodel.LANGUAGE_PHP:
             suffix = ".php"
         pyFilename = self.name + suffix
         return self._GetDoc().GetAppDocMgr().fullPath(pyFilename)
@@ -543,7 +552,63 @@ class Project_10:
     def upgradeVersion(self):
         currModel = Project()
         for file in self._files:
-            currModel._files.append(ProjectFile(file))
+            currModel._files.append(ProjectFile(currModel, file))
         return currModel
 
 
+#----------------------------------------------------------------------------
+# XML Marshalling Methods
+#----------------------------------------------------------------------------
+
+if ACTIVEGRID_BASE_IDE:
+    KNOWNTYPES = {"ag:project" : Project, "ag:file" : ProjectFile}
+else:
+    KNOWNTYPES = {"ag:project" : Project, "ag:file" : ProjectFile,
+                  "ag:appInfo" : AppInfo.AppInfo,
+                  "ag:deploymentDataSource" : AppInfo.DeploymentDataSource,
+                  "ag:dataSourceBinding" : AppInfo.DataSourceBinding}
+
+def load(fileObject):
+    version = xmlutils.getAgVersion(fileObject.name)
+    # most current versions on top
+    if version == PROJECT_VERSION_050826:
+        fileObject.seek(0)
+        project = xmlutils.load(fileObject.name, knownTypes=KNOWNTYPES, knownNamespaces=xmlutils.KNOWN_NAMESPACES, createGenerics=True)
+    elif version == PROJECT_VERSION_050730:
+        fileObject.seek(0)
+        project = xmlutils.load(fileObject.name, knownTypes={"project" : Project_10}, createGenerics=True)
+        project = project.upgradeVersion()
+    else:
+        # assume it is old version without version number
+        fileObject.seek(0)
+        project = xmlutils.load(fileObject.name, knownTypes={"project" : Project_10}, createGenerics=True)
+        if project:
+            project = project.upgradeVersion()
+        else:
+            print "Project, unknown version:", version
+            return None
+
+    if project:
+        project._projectDir = os.path.dirname(fileObject.name)
+        project.RelativeToAbsPath()
+
+    return project
+
+
+def save(fileObject, project, productionDeployment=False):
+    if not project._projectDir:
+        project._projectDir = os.path.dirname(fileObject.name)
+    project.AbsToRelativePath()  # temporarily change it to relative paths for saving
+    savedHomeDir = project.homeDir
+    if productionDeployment:
+        # for deployments, we don't want an abs path in homeDir since that
+        # would tie the app to the current filesystem. So unset it.
+        project.homeDir = None
+
+    xmlutils.save(fileObject.name, project, prettyPrint=True, knownTypes=KNOWNTYPES, knownNamespaces=xmlutils.KNOWN_NAMESPACES)
+
+    if productionDeployment:
+        project.homeDir = savedHomeDir
+
+    project.RelativeToAbsPath()  # swap it back to absolute path
+
index 227e9726fa17f9bf64eb6b69853feeb38a2d7111..17ab688e1c617c384a4dc48d0a5fab6fb45d0dbb 100644 (file)
@@ -19,6 +19,7 @@ import logging.config
 from activegrid.util.lang import *
 import activegrid.util.objutils as objutils
 import activegrid.util.sysutils as sysutils
+import activegrid.util.appdirs as appdirs
 
 LEVEL_FATAL = logging.FATAL
 LEVEL_ERROR = logging.ERROR
@@ -27,38 +28,47 @@ LEVEL_INFO = logging.INFO
 LEVEL_DEBUG = logging.DEBUG
 
 EXCEPTION_INFO = 'exceptionInfo'
+loggingInitialized = False
 
 LOG_MODE_IDE = 1
 LOG_MODE_TESTRUN = 2
 LOG_MODE_RUN = 3
-def initLogging(mode):
-    configFile = None
-    if (mode == LOG_MODE_IDE):
-        configFile = os.getenv("AG_LOGCONFIG_IDE")
-    elif (mode == LOG_MODE_TESTRUN):
-        configFile = os.getenv("AG_LOGCONFIG_TESTRUN")
-    else:
-        configFile = os.getenv("AG_LOGCONFIG_RUN")
-    if ((configFile == None) or not os.path.exists(configFile)):
+def initLogging(mode, force=False):
+    global ag_debugLogger, loggingInitialized
+    if (force or not loggingInitialized):
+        loggingInitialized = True
+        configFile = None
         if (mode == LOG_MODE_IDE):
-            configFile = "IDELog"
+            configFile = os.getenv("AG_LOGCONFIG_IDE")
         elif (mode == LOG_MODE_TESTRUN):
-            configFile = "TestRunLog"
+            configFile = os.getenv("AG_LOGCONFIG_PYTESTRUN")
         else:
-            configFile = "RunLog"
-        configFile = sysutils.mainModuleDir + "/py" + configFile + ".ini"
-    if (os.path.exists(configFile)):
-        fileConfig(configFile)
-    else:
-        defaultStream = sys.stderr
-        if (mode == LOG_MODE_RUN):
-            defaultStream = sys.stdout
-        handler = logging.StreamHandler(defaultStream)
-        handler.setLevel(logging.INFO)
-        handler.setFormatter(logging.Formatter("%(asctime)s %(name)s %(levelname)s: %(message)s"))
-        logging.getLogger().addHandler(handler)
-    return configFile
-
+            configFile = os.getenv("AG_LOGCONFIG_RUN")
+        if ((configFile == None) or not os.path.exists(configFile)):
+            if (mode == LOG_MODE_IDE):
+                configFile = "IDELog"
+            elif (mode == LOG_MODE_TESTRUN):
+                configFile = "TestRunLog"
+            else:
+                configFile = "RunLog"
+            configFile = os.path.join(appdirs.getSystemDir(appdirs.AG_LOGS_DIR), "py" + configFile + ".ini")
+        if (os.path.exists(configFile)):
+            print "Using logging configuration file: %s" % configFile
+            fileConfig(configFile)
+        else:
+            print "*** Cannot find logging configuration file (%s) -- setting default logging level to WARN ***" % (configFile)
+            defaultStream = sys.stderr
+            if (mode == LOG_MODE_RUN):
+                defaultStream = sys.stdout
+            handler = logging.StreamHandler(defaultStream)
+            handler.setLevel(logging.DEBUG)
+            handler.setFormatter(logging.Formatter("%(asctime)s %(name)s %(levelname)s: %(message)s"))
+            logging.getLogger().addHandler(handler)
+            logging.getLogger().setLevel(logging.WARN)
+        ag_debugLogger = logging.getLogger("activegrid.debug")
+        ag_debugLogger.setLevel(logging.DEBUG)
+        return configFile
+    
 ag_debugLogger = logging.getLogger("activegrid.debug")
 
 def log(logger, level, msg, *params):
@@ -181,21 +191,18 @@ def addExceptionInfo(e, key, value):
     if not e.exceptionInfo.has_key(key): # Never overwrite exception info since we assume earlier info is more specific
         e.exceptionInfo[key] = value
             
-def reportException(out=None, stacktrace=False, diffable=False, exception=None):
+def reportException(exception, out=None, stacktrace=False, diffable=False):
     exstr = exceptionToString(exception, stacktrace, diffable)
     if (out == None):
         print exstr
     else:
         print >> out, exstr
 
-def exceptionToString(exception=None, stacktrace=False, diffable=False):
-    if (exception == None):
-        extype, val, t = sys.exc_info()
-    else:
-        extype = objutils.typeToString(exception)
-        val = exception
-        if (stacktrace):
-            e,v,t = sys.exc_info()
+def exceptionToString(exception, stacktrace=False, diffable=False):
+    extype = objutils.typeToString(exception)
+    val = exception
+    if (stacktrace):
+        e,v,t = sys.exc_info()
     if (diffable):
         exstr = removeFileRefs(str(val))
     else:
index 7b73d5c4f489c91211ac56f2ffeb97b5fa6205b6..d3e038b04c8aa9d3b333cf016ec7e98fdd5c7989 100644 (file)
@@ -2,7 +2,7 @@
 # Name:         appdirs.py
 # Purpose:      Utilities for retrieving special application dirs
 #
-# Author:       Kevin Ollivier
+# Author:       Kevin Ollivier, Jeff Norton
 #
 # Created:      8/27/05
 # CVS-ID:       $Id$
 # License:      wxWindows License
 #----------------------------------------------------------------------------
 
-# NOTE: This was made a separate file because it depends upon the 
-# wx.StandardPaths module, and thus, on wxWidgets, unlike other
-# utils modules. I wanted to ensure this module is never loaded
-# from the web server, etc.
-
+from activegrid.util.lang import *
 import sys
 import os
 import string
-import wx
-
-def isWindows():
-    return os.name == 'nt'
-
-def _generateDocumentsDir():
-    path = ""
-    if sys.platform == "win32":
-        from win32com.shell import shell, shellcon
-        path=shell.SHGetFolderPath(0, shellcon.CSIDL_PERSONAL, None, 0)
-    elif sys.platform == "darwin":
-        import macfs, MACFS
-        fsspec_disk, fsspec_desktop = macfs.FindFolder( MACFS.kOnSystemDisk, MACFS.kDocumentsFolderType, 0)
-        path = macfs.FSSpec((fsspec_disk, fsspec_desktop, '')).as_pathname()
-    
-    if path == "":
-        path = os.path.expanduser("~")
-
-    return path
+import activegrid.util.sysutils as sysutils
+
+def _getSystemDir(kind):
+    if (kind == AG_LOGS_DIR):
+        return os.path.join(getSystemDir(AG_SYSTEM_DIR) , "logs")
+    elif (kind == AG_DEMOS_DIR):
+        return os.path.join(getSystemDir(AG_SYSTEM_DIR), "demos")
+    else:
+        path = ""
+        if (sysutils.isServer()):
+            path = os.getenv("ACTIVEGRID_SERVER_HOME")
+            if ((path is None) or (len(path) < 1)):
+                path = sysutils.mainModuleDir
+        else:
+            path = os.getenv("AG_DOCUMENTS_DIR")
+            if ((path is None) or (len(path) < 1)):
+                if sysutils.isWindows():
+                    ifDefPy()
+                    try:
+                        from win32com.shell import shell, shellcon
+                        path = shell.SHGetFolderPath(0, shellcon.CSIDL_PERSONAL, None, 0)
+                    except:
+                        pass
+                    endIfDef()
+                    if ((path is None) or (len(path) < 1)):
+                        homedrive = asString(os.getenv("HOMEDRIVE"))
+                        homepath = os.getenv("HOMEPATH")
+##                        if ((homedrive is not None) and (len(homedrive) > 0) and (homepath is not None) and (len(homepath) > 0)):
+                        path = os.path.join(homedrive, homepath, "MYDOCU~1")
+                else:
+                    ifDefPy()
+                    if sys.platform == "darwin":
+                        try:
+                            import macfs
+                            import MACFS
+                            fsspec_disk, fsspec_desktop = macfs.FindFolder(MACFS.kOnSystemDisk, MACFS.kDocumentsFolderType, 0)
+                            path = macfs.FSSpec((fsspec_disk, fsspec_desktop, '')).as_pathname()
+                        except:
+                            pass
+                    endIfDef()
+                
+                ifDefPy()
+                if ((path is None) or (len(path) < 1)):
+                    path = os.path.expanduser("~")
+                endIfDef()
+                if ((path is None) or (len(path) < 1)):
+                    path = "/"
+                path = os.path.join(path, "ActiveGrid")
             
-documents_folder = _generateDocumentsDir()
+        return path
+        
+
+AG_SYSTEM_DIR = 0
+AG_LOGS_DIR = 1
+AG_DEMOS_DIR = 2
+
+__systemDir = None
+__logsDir = None
+__demosDir = None
+
+def getSystemDir(kind=0):
+    if (kind == AG_SYSTEM_DIR):
+        global __systemDir
+        if (__systemDir is None):
+            __systemDir = _getSystemDir(kind)
+        return __systemDir
+    elif (kind == AG_LOGS_DIR):
+        global __logsDir
+        if (__logsDir is None):
+            __logsDir = _getSystemDir(kind)
+        return __logsDir
+    elif (kind == AG_DEMOS_DIR):
+        global __demosDir
+        if (__demosDir is None):
+            __demosDir = _getSystemDir(kind)
+        return __demosDir
+    return None
+
 
 # NOTE: We don't set this at startup because wxStandardPaths needs a running
 # application object. This makes sure the wxApp will always be created when
 # we get the folder.
+ifDefPy()
 def getAppDataFolder():
-    # wxStandardPaths requires a running app
-    if wx.GetApp() and wx.Platform != "__WXGTK__":
-        data_folder = wx.StandardPaths.Get().GetUserDataDir()
-        if not os.path.exists(data_folder):
-            os.mkdir(data_folder)
-        return data_folder
-    else:
-        # wxBug: on *nix, it wants to point to ~/.appname, but
-        # so does wxConfig... For now, redirect this to ~/.appbuilder
-        # when this is fixed, we'll migrate settings to the correct place
-        return os.path.join(os.path.expanduser("~"), ".appbuilder")
+    try:
+        # NOTE: cannot import wx from the server
+        import wx
+        # wxStandardPaths requires a running app
+        if wx.GetApp() and wx.Platform != "__WXGTK__":
+            data_folder = wx.StandardPaths.Get().GetUserDataDir()
+            if not os.path.exists(data_folder):
+                os.mkdir(data_folder)
+            return data_folder
+    except:
+        pass
+    # wxBug: on *nix, it wants to point to ~/.appname, but
+    # so does wxConfig... For now, redirect this to ~/.appbuilder
+    # when this is fixed, we'll migrate settings to the correct place
+    return os.path.join(os.path.expanduser("~"), ".appbuilder")
+endIfDef()
 
-    return ""
+ifDefPy()
+def createSystemDirs():
+    if (not os.path.exists(getSystemDir())):
+        os.mkdir(getSystemDir())
+    if (not os.path.exists(getSystemDir(AG_LOGS_DIR))):
+        os.mkdir(getSystemDir(AG_LOGS_DIR))
+    if (not os.path.exists(getSystemDir(AG_DEMOS_DIR))):
+        os.mkdir(getSystemDir(AG_DEMOS_DIR))
+endIfDef()
diff --git a/wxPython/samples/ide/activegrid/util/datetimeparser.py b/wxPython/samples/ide/activegrid/util/datetimeparser.py
new file mode 100644 (file)
index 0000000..3731658
--- /dev/null
@@ -0,0 +1,118 @@
+#----------------------------------------------------------------------------
+# Name:         datetimeparser.py
+#
+# Purpose:      - Instantiate datetime.datetime/date instance from a string
+#                 date representation.
+#                 Uses dateutil from http://labix.org/python-dateutil.
+#
+#               - Creates string representation of datetime/date instance.
+#
+#
+# Author:       Simon Toens
+#
+# Created:      28-Feb-06
+# CVS-ID:
+# Copyright:    (c) 2005 ActiveGrid, Inc.
+# License:      wxWindows License
+#----------------------------------------------------------------------------
+
+import datetime
+
+try:
+    import dateutil.parser
+    DATEUTIL_INSTALLED = True
+except ImportError:
+    DATEUTIL_INSTALLED = False
+
+ISO_8601_DATE_FORMAT = "%Y-%m-%d"
+ISO_8601_TIME_FORMAT = "%H:%M:%S"
+ISO_8601_DATETIME_FORMAT = "%s %s" %(ISO_8601_DATE_FORMAT,
+                                     ISO_8601_TIME_FORMAT)
+
+DEFAULT_DATETIME = datetime.datetime(1, 1, 1, 0, 0, 0, 0)
+
+
+def format(dateobj, formatstr=None):
+    if (formatstr != None and _isDateTimeObject(dateobj)):
+        return dateobj.strftime(str(formatstr))
+    return str(dateobj)
+
+
+def parse(datestr, formatstr=None, asdate=False, astime=False):
+    """Instantiates and returns a datetime instance from the datestr datetime
+       representation.
+       
+       Optionally, a format string may be used. The format is only loosely
+       interpreted, its only purpose beeing to determine if the year is first
+       or last in datestr, and whether the day is in front or follows the
+       month. If no formatstr is passed in, dateutil tries its best to parse
+       the datestr. The default date format is YYYY-mm-dd HH:SS.
+       
+       If asdate is True, returns a date instance instead of a datetime
+       instance, if astime is True, returns a time instance instead of a
+       datetime instance."""
+    
+       
+    dayfirst, yearfirst = _getDayFirstAndYearFirst(formatstr)
+
+    rtn = None
+
+    try:
+        if DATEUTIL_INSTALLED:
+            rtn = dateutil.parser.parse(str(datestr), fuzzy=True,
+                                        dayfirst=dayfirst, yearfirst=yearfirst,
+                                        default=DEFAULT_DATETIME)
+        else:
+            rtn = DEFAULT_DATETIME
+    except:
+        rtn = DEFAULT_DATETIME
+
+    if (asdate and isinstance(rtn, datetime.datetime)):
+        rtn = datetime.date(rtn.year, rtn.month, rtn.day)
+    elif (astime and isinstance(rtn, datetime.datetime)):
+        rtn = datetime.time(rtn.hour, rtn.minute, rtn.second, rtn.microsecond)
+
+    return rtn
+
+
+def _isDateTimeObject(obj):
+    return (isinstance(obj, datetime.datetime) or
+            isinstance(obj, datetime.date) or
+            isinstance(obj, datetime.time))
+
+
+def _getDayFirstAndYearFirst(formatstr):
+    dayFirst = False
+    yearFirst = False
+    
+    gotYear = False
+    gotMonth = False
+    gotDay = False
+
+    if (formatstr == None):
+        formatstr = ""
+    
+    for c in formatstr:
+        if (c.lower() == "y"):
+            if (gotYear):
+                continue
+            if (not gotDay and not gotMonth):
+                yearFirst = True
+            gotYear = True
+
+        elif (c.lower() == "m"):
+            if (gotMonth):
+                continue
+            if (not gotDay):
+                dayFirst = False
+            gotMonth = True
+            
+        elif (c.lower() == "d"):
+            if (gotDay):
+                continue
+            if (not gotMonth):
+                dayFirst = True
+            gotDay = True
+        
+
+    return dayFirst, yearFirst
index 2a75997aeab4b4e184018483b5439379209cfdf3..ca5ee1fb8fa63a646aa60f22dbad96eb291143e6 100644 (file)
@@ -19,6 +19,7 @@ import zipfile
 
 import activegrid.util.aglogging as aglogging
 import activegrid.util.sysutils as sysutils
+import activegrid.util.utillang as utillang
 from activegrid.util.lang import *
 
 global fileutilsLogger
@@ -31,6 +32,65 @@ fileutilsLogger = logging.getLogger("activegrid.util.fileutils")
 aglogging.setLevelFatal(fileutilsLogger)
 #logging.getLogger().addHandler(logging.StreamHandler(sys.stderr))
 
+def addRef(varname):
+    return "${%s}" % varname
+
+AG_SYSTEM_VAR_NAMES = [] # all AG System vars, with ${} syntax
+
+AG_SYSTEM_VAR = "AG_SYSTEM"
+AG_SYSTEM_VAR_REF = addRef(AG_SYSTEM_VAR)
+AG_SYSTEM_VAR_NAMES.append(AG_SYSTEM_VAR_REF)
+
+AG_SYSTEM_STATIC_VAR = "AG_SYSTEM_STATIC"
+AG_SYSTEM_STATIC_VAR_REF = addRef(AG_SYSTEM_STATIC_VAR)
+AG_SYSTEM_VAR_NAMES.append(AG_SYSTEM_STATIC_VAR_REF)
+
+AG_APP_VAR = "AG_APP"
+AG_APP_STATIC_VAR = "AG_APP_STATIC"
+
+# _initAGSystemVars needs to be called to initialize the following two
+# containers:
+EXPANDED_AG_SYSTEM_VARS = {} # ${varname} -> value (path)
+# ${varname}, ordered from longest to shortest path value
+AG_SYSTEM_VARS_LENGTH_ORDER = [] 
+
+def _initAGSystemVars():
+    if (len(EXPANDED_AG_SYSTEM_VARS) > 0):
+        return
+    
+    for v in AG_SYSTEM_VAR_NAMES:
+        EXPANDED_AG_SYSTEM_VARS[v] = os.path.abspath(expandVars(v))
+        AG_SYSTEM_VARS_LENGTH_ORDER.append(v)
+        
+    AG_SYSTEM_VARS_LENGTH_ORDER.sort(_sortByValLength)
+
+
+def parameterizePathWithAGSystemVar(inpath):
+    """Returns parameterized path if path starts with a known AG directory. Otherwise returns path as it was passed in."""
+    _initAGSystemVars()
+    path = inpath
+    if not sysutils.isWindows():
+        # ensure we have forward slashes
+        path = path.replace("\\", "/")
+        
+    path = os.path.abspath(path)
+
+    for varname in AG_SYSTEM_VARS_LENGTH_ORDER:
+        varval = EXPANDED_AG_SYSTEM_VARS[varname]
+        if path.startswith(varval):
+            return path.replace(varval, varname)
+        
+    return inpath
+
+def startsWithAgSystemVar(path):
+    """Returns True if path starts with a known AG system env var, False otherwise."""
+    for varname in AG_SYSTEM_VAR_NAMES:
+        if path.startswith(varname):
+            return True
+    return False
+        
+def _sortByValLength(v1, v2):
+    return len(EXPANDED_AG_SYSTEM_VARS[v2]) - len(EXPANDED_AG_SYSTEM_VARS[v1])
 
 def makeDirsForFile(filename):
     d = os.path.dirname(filename)
@@ -44,7 +104,7 @@ def createFile(filename, mode='w'):
     f = file(filename, mode)
     return f
 
-def compareFiles(file1, file2):
+def compareFiles(file1, file2, ignore=None):
 ##    result = filecmp.cmp(file1, file2)
 ##    if result:
 ##        return 0
@@ -62,6 +122,9 @@ def compareFiles(file1, file2):
         elif (len(line2) == 0):
             return -1
         elif (line1 != line2):
+            if (ignore != None):
+                if (line1.startswith(ignore) or line2.startswith(ignore)):
+                    continue
             line1 = line1.replace(" ", "")
             line2 = line2.replace(" ", "")
             if (line1 != line2):
@@ -81,7 +144,10 @@ def compareFiles(file1, file2):
                         continue
                 return -1
 
-def expandVars(value):
+def expandKnownAGVars(value):
+    return expandVars(value, includeEnv=False)
+
+def expandVars(value, includeEnv=True):
     """Syntax: ${myvar,default="default value"}"""
     import activegrid.runtime as runtime
     sx = value.find("${")
@@ -97,16 +163,19 @@ def expandVars(value):
                     defaultValue = value[defsx+10:endx-1]
             if (defaultValue == None):
                 varname = value[sx+2:endx]
-            if (varname == "AG_SYSTEM"):
+            if (varname == AG_SYSTEM_VAR):
                 varval = runtime.appInfo.getSystemDir()
-            elif (varname == "AG_SYSTEM_STATIC"):
+            elif (varname == AG_SYSTEM_STATIC_VAR):
                 varval = runtime.appInfo.getSystemStaticDir()
-            elif (varname == "AG_APP"):
+            elif (varname == AG_APP_VAR):
                 varval = runtime.appInfo.getAppDir()
-            elif (varname == "AG_APP_STATIC"):
+            elif (varname == AG_APP_STATIC_VAR):
                 varval = runtime.appInfo.getAppStaticDir()
             else:
-                varval = os.getenv(varname)
+                if (includeEnv):
+                    varval = os.getenv(varname)
+                else:
+                    varval = None
             if ((varval == None) and (defaultValue != None)):
                 varval = defaultValue
             if (varval == None):
@@ -148,22 +217,21 @@ def convertSourcePath(path, to, otherdir=None):
         return otherdir + path[ix+7:]
 
 
-def visit(directory, files, extension):
+def visit(directory, files, extension, maxLevel=None, level=1):
     testdirs = os.listdir(directory)
     for thing in testdirs:
         fullpath = os.path.join(directory, thing)
-        if (os.path.isdir(fullpath)):
-            visit(fullpath, files, extension)
+        if (os.path.isdir(fullpath) and (maxLevel == None or level < maxLevel)):
+            visit(fullpath, files, extension, maxLevel, level+1)
         elif thing.endswith(extension):
             fullname = os.path.normpath(os.path.join(directory, thing))
             if not fullname in files:
                 files.append(fullname)
  
-def listFilesByExtensionInPath(path=[], extension='.lyt'):
-    #Collect input and output arguments into one bunch
+def listFilesByExtensionInPath(path=[], extension='.lyt', maxLevel=None):
     retval = []
     for directory in path:
-        visit(directory, retval, extension)
+        visit(directory, retval, extension, maxLevel)
     return retval
 
 def getFileLastModificationTime(fileName):
@@ -311,6 +379,21 @@ def remove(file):
         shutil.rmtree(file)
 endIfDef()
 
+def getUserTempDir():
+    systemTempDir = utillang.getSystemTempDir()
+    userName = sysutils.getUserName()
+    userNameNoSpace = userName.replace('_','__').replace(' ','_')
+    userTempDir = systemTempDir + os.sep + "activegrid_" + userNameNoSpace
+    return userTempDir
+
+def createUserTempDir():
+    userTempDir = getUserTempDir()
+    if not os.path.exists(userTempDir):
+        os.makedirs(userTempDir)
+        os.chmod(userTempDir, 0700)
+
+createUserTempDir()
+
 ifDefPy()
 import warnings
 warnings.filterwarnings("ignore", message="tmpnam is a potential security risk to your program")
index 79cb02919e323217b1039171b650b2196b812a80..96e96df38c5e434ad22eef36c624aa22c8f4886c 100644 (file)
@@ -16,7 +16,8 @@ import sys
 import os
 import __builtin__
 import types
-import xml.sax.saxutils as saxutils
+import activegrid.util.utillang as utillang
+import activegrid.util.datetimeparser as datetimeparser
 from types import *
 from activegrid.util.lang import *
 
@@ -65,6 +66,18 @@ def setStaticAttr(obj, attr, value):
         classDesc = obj.__class__
     setattr(classDesc, attr, value)
 
+def hasAttrFast(obj, name):
+    if hasRawAttr(obj, name):
+        return True
+    if hasattr(obj, '_complexType'):
+        complexType=obj._complexType
+        element=complexType.findElement(name)
+        if element:
+            return True
+    if hasattr(obj, name):
+        return True
+    return False
+
 def moduleForName(moduleName):
     module = None
     pathList = moduleName.split('.')
@@ -114,15 +127,22 @@ def newInstance(className, objargs=None):
         if ((len(objargs) < 1) or (objargs[0].lower() == "false") or (not objargs[0])):
             return False
         return True
-    if className == "str" or className == "unicode": # don"t strip: blanks are significant
+    if className == "str" or className == "unicode": # don't strip: blanks are significant
         if len(objargs) > 0:
             try:
-                return saxutils.unescape(objargs[0]).encode()
+                return utillang.unescape(objargs[0]).encode()
             except:
                 return "?"
         else:
             return ""
-
+            
+    if className == "date":
+        return datetimeparser.parse(objargs[0], asdate=True)
+    if className == "datetime":
+        return datetimeparser.parse(objargs[0])
+    if className == "time":
+        return datetimeparser.parse(objargs[0], astime=True)
+        
     classtype = classForName(className)
     if (classtype == None):
         raise Exception("Could not find class %s" % className)
@@ -135,23 +155,35 @@ def newInstance(className, objargs=None):
 def getClassProperty(classType, propertyName):
     return getattr(classType, propertyName)
     
-def toDiffableRepr(value, exclude=None):
+def toDiffableRepr(value, maxLevel=None):
     if (value == None):
         return "None"
+    if (maxLevel == None):
+        maxLevel = 8
+    maxLevel -= 1
+    if (maxLevel < 0):
+        return typeToString(value, PRINT_OBJ_DIFFABLE)
+##    if ((exclude != None) and not isinstance(value, (basestring, int))):
+##        for v in exclude:
+##            if (v is value):
+##                return "<recursive reference>"
+##        exclude.append(value)
 ##    elif (isinstance(value, ObjectType) and hasattr(value, "__dict__")):
 ##        if (exclude == None):
 ##            exclude = []
 ##        s = "%s(%s)" % (type(value), toDiffableString(value.__dict__, exclude))
-    elif (not isinstance(value, (BooleanType, ClassType, ComplexType, DictType, DictionaryType, 
+    if (not isinstance(value, (BooleanType, ClassType, ComplexType, DictType, DictionaryType, 
                                FloatType, IntType, ListType, LongType, StringType, TupleType, 
                                UnicodeType, BufferType, BuiltinFunctionType, BuiltinMethodType,
                                CodeType, FrameType, FunctionType, GeneratorType, InstanceType,
                                LambdaType, MethodType, ModuleType, SliceType, TracebackType,
                                TypeType, XRangeType))):
-        if (hasattr(value, "__str__")):
+        if (hasattr(value, "_toDiffableString")):
+            s = value._toDiffableString(maxLevel)
+        elif (hasattr(value, "__str__")):
             s = str(value)
         elif (hasattr(value, "__dict__")):
-            s = "%s(%s)" % (type(value), toDiffableString(value.__dict__, exclude))
+            s = "%s(%s)" % (type(value), toDiffableString(value.__dict__, maxLevel))
         else:
             s = str(type(value))
         ix2 = s.find(" object at 0x")
@@ -173,33 +205,31 @@ def toDiffableRepr(value, exclude=None):
                 else:
                     items.append("'%s'" % v)
             else:
-                items.append(toDiffableString(v, exclude))
+                items.append(toDiffableString(v, maxLevel))
         s = "[" + ", ".join(items) + "]"
     elif (isinstance(value, dict)):
-        if (exclude == None):
-            exclude = []
         items = []
         for key, val in value.iteritems():
             if (isinstance(val, UnicodeType)):
-                items.append("'%s': u'%s'" % (key, toDiffableString(val, exclude)))
+                items.append("'%s': u'%s'" % (key, toDiffableString(val, maxLevel)))
             elif (isinstance(val, basestring)):
-                items.append("'%s': '%s'" % (key, toDiffableString(val, exclude)))
+                items.append("'%s': '%s'" % (key, toDiffableString(val, maxLevel)))
             else:
-                items.append("'%s': %s" % (key, toDiffableString(val, exclude)))
+                items.append("'%s': %s" % (key, toDiffableString(val, maxLevel)))
         s = "{" + ", ".join(items) + "}"
     else:
         s = str(value)
     return s
     
-def toDiffableString(value, exclude=None):
-    if (value == None):
-        return "None"
-    if ((exclude != None) and not isinstance(value, (basestring, int))):
-        for v in exclude:
-            if (v is value):
-                return "<recursive reference>"
-        exclude.append(value)
-    s = toDiffableRepr(value)
+def toDiffableString(value, maxLevel=None):
+##    if (value == None):
+##        return "None"
+##    if ((exclude != None) and not isinstance(value, (basestring, int))):
+##        for v in exclude:
+##            if (v is value):
+##                return "<recursive reference>"
+##        exclude.append(value)
+    s = toDiffableRepr(value, maxLevel)
     ds = ""
     i = s.find(" at 0x") 
     start = 0
diff --git a/wxPython/samples/ide/activegrid/util/parser.py b/wxPython/samples/ide/activegrid/util/parser.py
new file mode 100644 (file)
index 0000000..364099b
--- /dev/null
@@ -0,0 +1,380 @@
+#----------------------------------------------------------------------------
+# Name:         parser.py
+# Purpose:      parsing utilities
+#
+# Author:       Jeff Norton
+#
+# Created:      8/9/05
+# CVS-ID:       $Id$
+# Copyright:    (c) 2004-2005 ActiveGrid, Inc.
+# License:      wxWindows License
+#----------------------------------------------------------------------------
+
+import re
+from activegrid.util.lang import *
+ifDefPy()
+import string
+import array
+endIfDef()
+
+XPATH_ROOT_VAR = '__rootObj__'
+GETOBJECTPARTNAMES  =   ["primaryRef", "ref", "orderings", "limit"]
+
+class Tokenizer(object):
+    
+    TOKEN_IDENT = 1
+    TOKEN_STRING = 2
+    TOKEN_OP = 3
+    TOKEN_WS = 4
+##    TOKEN_PLACEHOLDER = 5
+    
+    def __init__(self, text, identStart=None, tokenSep=None, ignoreWhitespace=True):
+        """
+Turn a string into individual tokens.  Three types of tokens are recognized:
+    TOKEN_IDENT:   identifiers (those that start with the identStart pattern)
+    TOKEN_STRING:  quoted string
+    TOKEN_OP:      everything else
+Tokens are separated by white space or the tokenSep pattern.  
+Constructor parameters:
+    text:  The string to tokenize
+    identStart:  A regular expression describing characters which start an identifier
+                 The default expression accepts letters, "_", and "/".
+    tokenSep:    A regular expression describing the characters which end a token 
+                 (in addition to whitespace).  The default expression accepts
+                 anything except alpha-numerics, "_", "/", and ":".
+Usage:
+    Invoke getNextToken (or next) to get the next token.  The instance variables
+    token, and tokenVal will be populated with the current token type (TOKEN_IDENT,
+    TOKEN_STRING, or TOEKN_OP) and value respectively.  nextToken and nextTokenVal
+    will also be available for lookahead.   The next method is similar to
+    getNextToken but also returns the token value.  A value of None signals end
+    of stream.                                           
+        """
+        self.ignoreWhitespace=ignoreWhitespace
+        ifDefPy()
+        if (isinstance(text, array.array)):
+            text = text.tostring()
+        endIfDef()
+        self.text = asString(text)
+        self.textIndex = 0
+        self.textLen = len(self.text)
+        self.token = None
+        self.tokenVal = None
+        self.nextToken = None
+        self.nextTokenVal = None
+        if (identStart == None):
+            identStart = "[a-zA-Z_/]"
+        if (tokenSep == None):
+            tokenSep = "[^a-zA-Z0-9_/:]"
+        self.identStart = re.compile(identStart)
+        self.tokenSep = re.compile(tokenSep)
+        self.getNextToken() # Prime the pump
+        
+    def isEscaped(text, index):
+        if ((index > 0) and (text[index-1] == '\\') and ((index < 2) or (text[index-2] != '\\'))):
+            return True
+        return False
+    isEscaped = staticmethod(isEscaped)
+
+    def findClosingQuote(text, index, char):
+        index = index + 1
+        while True:
+            endIndex = text.find(char, index)
+            if (endIndex < 1):
+                return -1
+            if (Tokenizer.isEscaped(text, endIndex)):
+                index = endIndex+1
+            else:
+                break
+        return endIndex + 1
+    findClosingQuote = staticmethod(findClosingQuote)
+
+    def _findClosing(self, char):
+        if (self.textIndex >= self.textLen):
+            raise Exception("The text \"%s\" has an unmatched string starting at %d" % (self.text, self.textIndex))
+        index = Tokenizer.findClosingQuote(self.text, self.textIndex, char)
+        if (index < 0):
+            raise Exception("The text \"%s\" has an unmatched string starting at %d" % (self.text, self.textIndex-1))
+        return index
+        
+    def next(self):
+        self.getNextToken()
+        if (self.token == None):
+            raise StopIteration()
+        return self.tokenVal
+
+    def getNextToken(self):
+        self.token = self.nextToken
+        self.tokenVal = self.nextTokenVal
+        while (self.textIndex < self.textLen):
+            c = self.text[self.textIndex]
+            if (c not in string.whitespace):
+                if (c == '"' or c == "'" or c == '`'):
+                    endIndex = self._findClosing(c)
+                    self.nextToken = self.TOKEN_STRING
+                    self.nextTokenVal = self.text[self.textIndex:endIndex]
+                    self.textIndex = endIndex
+                    return
+                elif (self.identStart.search(c)):
+                    endMatch = self.tokenSep.search(self.text, self.textIndex+1)
+                    if (endMatch):
+                        endIndex = endMatch.start()
+                    else:
+                        endIndex = self.textLen
+                    self.nextToken = self.TOKEN_IDENT
+                    self.nextTokenVal = self.text[self.textIndex:endIndex]
+                    self.textIndex = endIndex
+                    return
+                else:
+                    self.nextToken = self.TOKEN_OP
+                    endIndex = self.textIndex + 1
+                    if (c == '<' or c == '>' or c == '!' or c == '='):
+                        if ((endIndex < self.textLen) and (self.text[endIndex] == '=')):
+                            endIndex += 1
+                    elif ((c == '%') and (endIndex < self.textLen)):
+                        c = self.text[endIndex]
+                        if (c in ['d', 'i', 'o', 'u', 'x', 'X', 'e', 'E', 'f', 'F', 'g', 'G', 'c', 'r', 's', '%']):
+                            endIndex += 1
+##                            self.nextToken = self.TOKEN_PLACEHOLDER # Should really be this but no one can handle it yet
+                    self.nextTokenVal = self.text[self.textIndex:endIndex]
+                    self.textIndex = endIndex
+                    return
+            elif not self.ignoreWhitespace:
+                self.nextToken=self.TOKEN_WS
+                self.nextTokenVal=""
+                while c in string.whitespace:
+                    self.nextTokenVal+=c
+                    self.textIndex+=1
+                    if self.textIndex==len(self.text):
+                        break
+                    c=self.text[self.textIndex]
+                return
+            self.textIndex += 1
+        self.nextToken = None
+        self.nextTokenVal = None
+
+def isXPathNonVar(var):
+    """Returns true iff var is a string ("foo" or 'foo') or a number."""
+    if (var.startswith("'") and var.endswith("'")) or \
+            (var.startswith('"') and var.endswith('"')):
+        return True
+
+    # list from XPathToCode, below
+    if var.lower() in ["count", "empty", "true", "false", "null", "and", "or", \
+            "like", "not"]:
+        return True
+
+    try:
+        t=int(var)
+        return True
+    except TypeError, e:
+        pass
+    except ValueError, e:
+        pass
+
+    return False
+
+def xpathToCode(xpaths, convertBracket=True):
+    if ((xpaths == None) or (len(xpaths) < 1)):
+        return "True"
+    if (not isinstance(xpaths, (list, tuple))):
+        xpaths = [xpaths]
+    result = []
+    for xpath in xpaths:
+        t = Tokenizer(xpath, "[a-zA-Z0-9_/:\.]", "[^a-zA-Z0-9_/:\.]", ignoreWhitespace=False)
+        expr = []
+        lastToken=None
+        while t.nextToken != None:
+            t.getNextToken()
+            if (t.token == Tokenizer.TOKEN_WS):
+                expr.append(" ")
+            elif (t.token == Tokenizer.TOKEN_OP):
+                if (t.tokenVal == "="):
+                    expr.append("==")
+                elif (t.tokenVal == "[" and convertBracket):
+                    expr.append("(")
+                elif (t.tokenVal == "]" and convertBracket):
+                    expr.append(")")
+                else:
+                    expr.append(t.tokenVal)
+            elif (t.token == Tokenizer.TOKEN_IDENT):
+                if (t.tokenVal == "and"):
+                    expr.append(" and ")
+                elif (t.tokenVal == "or"):
+                    expr.append(" or ")
+                elif (t.tokenVal == "not"):
+                    expr.append(" not ")
+                elif (t.tokenVal == "like"):
+                    # REVIEW stoens@activegrid.com 02-Nov-05 --
+                    # This is very limited support for like:
+                    # typically like queries look like this: "foo like 'blah%'".
+                    # So translate this into "foo.startswith(blah)".
+                    # We should use a regular expression to support '%'s in
+                    # arbitrary places in the string. After 1.1.
+                    if t.nextToken and t.nextTokenVal.endswith("%'"):
+                        t.getNextToken() # throw away the "like" token
+                        last = len(expr) - 1
+                        expr[last] = "%s.startswith(%s')"\
+                            % (expr[last], t.tokenVal[:-2])
+                    else:
+                        # old behavior
+                        expr.append(t.tokenVal)
+                    
+                elif (t.tokenVal == "count"):
+                    expr.append("len")
+                elif (t.tokenVal == 'empty'):
+                    expr.append('ctx.isEmptyPath')
+                elif (t.tokenVal == 'true'):
+                    expr.append(_parseConstantFunction(t, 'True'))
+                elif (t.tokenVal == 'false'):
+                    expr.append(_parseConstantFunction(t, 'False'))
+                elif (t.tokenVal == 'null'):
+                    expr.append(_parseConstantFunction(t, 'None'))
+                elif (-1!=t.tokenVal.find(':')):
+                    serviceDef, args=_parseServiceFunction(t)
+
+                    # XXX handle serviceDef, args being None
+
+                    for i in range(len(args)):
+                        args[i]=xpathToCode(args[i], False)
+                    jargs="[%s]" % (",".join(args))
+
+                    # XXX should be processmodel.DATASERVICE_PROCESS_NAME, not "dataservice"
+                    if serviceDef[0]=='dataservice':
+                        expr.append("runtimesupport.invokeDataServiceWrapper(%s, %s, ctx, locals())" % \
+                                (serviceDef, jargs))
+                    else:
+                        expr.append("runtimesupport.invokeServiceWrapper(%s, %s, ctx)" % \
+                                (serviceDef, jargs))
+                else:
+                    if (lastToken==')' or lastToken==']'):
+                        wasFunc=True
+                    else:
+                        wasFunc=False
+                    if (t.tokenVal.startswith('/')) and not wasFunc:
+                        expr.append(XPATH_ROOT_VAR)
+                    expr.append(t.tokenVal.replace('/','.'))
+                lastToken=t.tokenVal
+            else: 
+                expr.append(t.tokenVal)
+                
+
+        if (len(expr) == 2 and expr[0]==" "):
+            expr = "".join(expr)
+            result.append(expr)
+        elif (len(expr) > 1):
+            expr = "".join(expr)
+            result.append("(%s)" % expr)
+        elif (len(expr) > 0):
+            result.append(expr[0])
+        
+    return " and ".join(result)
+
+def _parseArgs(t):
+    args=[]
+    argcon=""
+
+    if t.tokenVal!='(':
+        return []
+    if t.nextTokenVal==')':
+        t.getNextToken()
+        return []
+
+    depth=1
+
+    while(depth!=0):
+        if not t.nextToken:
+            raise Exception("parameters list with no closing ) after token: %s" % t.tokenVal)
+        t.getNextToken()
+
+        if t.tokenVal=='(':
+            depth+=1
+        if t.tokenVal==')':
+            depth-=1
+
+        if depth==0 or (depth==1 and t.tokenVal==','):
+            args.append(argcon)
+            argcon=""
+        else:
+            argcon+=t.tokenVal
+    return args
+
+def _parseServiceFunction(t):
+    """Parses what appears to be a service function call into serviceDefs and args lists.
+
+    Returns None, None if the serviceFunction appears to be invalid.
+    """
+    if t.nextTokenVal!='(':
+        return t.tokenVal, None
+
+    serviceDef=t.tokenVal.split(':')
+    t.getNextToken()
+    args=_parseArgs(t)
+
+    return serviceDef, args
+    
+def _parseConstantFunction(t, outputValue):
+    firstVal = t.tokenVal
+    if t.nextTokenVal != '(':
+        return firstVal
+    t.getNextToken()
+    if t.nextTokenVal != ')':
+        return "%s%s" % (firstVal, '(')
+    t.getNextToken()
+    return outputValue
+
+def parseDSPredicate(ctx, str, vars, valueList=None):
+    from activegrid.util.utillang import evalCode
+    from activegrid.util.utillang import ObjAsDict
+
+    if valueList == None:
+        valueList = []
+    indexVar=0
+    oldIndexVar=0
+    sourceStr=str
+    inlinedPredicate=[]
+    qualifications=[]
+    while True:
+        oldIndexVar = indexVar
+        dollarCurlForm = False
+        quoted = False
+        indexVar = sourceStr.find("bpws:getVariableData", indexVar)
+        if indexVar == -1:
+            indexVar = sourceStr.find("${", oldIndexVar)
+            if indexVar == -1:
+                break
+            dollarCurlForm = True
+        if indexVar > 0 and sourceStr[indexVar-1] in ('"',"'"):
+            quoted = True
+        if not dollarCurlForm:
+            openParen = sourceStr.find("(", indexVar)
+            if openParen == -1:
+                break
+            closeParen = sourceStr.find(")", openParen)
+            if closeParen == -1:
+                break
+        else:
+            openParen = indexVar+1
+            closeParen = sourceStr.find("}", openParen)
+            if closeParen == -1:
+                break
+        varRef = sourceStr[openParen+1: closeParen]
+        if varRef.startswith('"') or varRef.startswith("'"):
+            varRef = varRef[1:]
+        if varRef.endswith('"') or varRef.endswith("'"):
+            varRef = varRef[:-1]
+        if isinstance(vars, dict) or isinstance(vars, ObjAsDict):
+            varRefCode = xpathToCode(varRef)
+            value = evalCode(varRefCode, vars)
+        else:
+            value = ctx.evalPath(vars, varRef)
+        inlinedPredicate.append(sourceStr[oldIndexVar:indexVar])
+        if quoted:
+            inlinedPredicate.append("%s" % value)
+        else:
+            inlinedPredicate.append('%s')
+            valueList.append(value)
+        indexVar = closeParen+1
+    inlinedPredicate.append(sourceStr[oldIndexVar:])
+    qualifications.append(''.join(inlinedPredicate))
+    return qualifications, valueList
index b79bd2a474d6f6e6c04939148da1f737bd24f360..f70d03ee548f34f6c3f26b3dd3c919d8a8a0eacb 100644 (file)
@@ -21,3 +21,91 @@ def caseInsensitiveCompare(s1, s2):
         return -1
     else:
         return 1
+
+def multiSplit(stringList, tokenList=[" "]):
+    """Splits strings in stringList by tokens, returns list of string."""
+    if not stringList: return []
+    if isinstance(tokenList, basestring):
+        tokenList = [tokenList]
+    if isinstance(stringList, basestring):
+        stringList = [stringList]
+    rtnList = stringList
+    for token in tokenList:
+        rtnList = rtnList[:]
+        for string in rtnList:
+            if string.find(token) > -1:
+                rtnList.remove(string)
+                names = string.split(token)
+                for name in names:
+                    name = name.strip()
+                    if name:
+                        rtnList.append(name)
+    return rtnList
+
+QUOTES = ("\"", "'")
+
+def _findArgStart(argStr):
+    i = -1
+    for c in argStr:
+        i += 1
+        if (c == " "):
+            continue
+        elif (c == ","):
+            continue
+        return i
+    return None
+
+def _findArgEnd(argStr):
+    quotedArg = True
+    argEndChar = argStr[0]
+    if (not argEndChar in QUOTES):
+        argEndChar = ","
+        quotedArg = False
+    i = -1
+    firstChar = True
+    for c in argStr:
+        i+= 1
+        if (firstChar):
+            firstChar = False
+            if (quotedArg):
+                continue
+        if (c == argEndChar):
+            if (quotedArg):
+                return min(i+1, len(argStr))
+            else:
+                return i
+    return i
+
+def parseArgs(argStr, stripQuotes=False):
+    """
+    Given a str representation of method arguments, returns list arguments (as
+    strings).
+    
+    Input: "('[a,b]', 'c', 1)" -> Output: ["'[a,b]'", "'c'", "1"].
+
+    If stripQuotes, removes quotes from quoted arg.
+    """
+    if (argStr.startswith("(")):
+        argStr = argStr[1:]
+        if (argStr.endswith(")")):
+            argStr = argStr[:-1]
+        else:
+            raise AssertionError("Expected argStr to end with ')'")
+
+    rtn = []
+    argsStr = argStr.strip()
+    while (True):
+        startIndex = _findArgStart(argStr)
+        if (startIndex == None):
+            break
+        argStr = argStr[startIndex:]
+        endIndex = _findArgEnd(argStr)
+        if (endIndex == len(argStr) - 1):
+            rtn.append(argStr.strip())
+            break        
+        t = argStr[:endIndex].strip()
+        if (stripQuotes and t[0] in QUOTES and t[-1] in QUOTES):
+            t = t[1:-1]
+        rtn.append(t)
+        argStr = argStr[endIndex:]
+    return rtn
index eb8bbf08f10fdff626eb5841248772c43e3564c3..d7e68bd6857bbbe790e728dc8f4472a8772ce57e 100644 (file)
@@ -12,6 +12,7 @@
 
 import sys
 import os
+import time
 
 # this will be set to true in IDE.py when we are running release builds.
 isRelease = False
@@ -26,6 +27,12 @@ isRelease = False
 
 MAINMODULE_DIR = "AG_MAINMODULE_DIR"
 IS_RELEASE = "AG_IS_RELEASE"
+IS_COMMERCIAL = "AG_IS_COMMERCIAL"
+AG_SYSTEM_START_TIME_ENV_NAME = "AG_SYSTEM_START_TIME"
+
+def isCommercial():
+    
+    return os.path.exists(os.path.join(mainModuleDir,"commercial.txt")) or 'true' == (str(os.getenv(IS_COMMERCIAL)).lower())
  
 def isRelease():
     return 'true' == (str(os.getenv(IS_RELEASE)).lower())
@@ -39,7 +46,16 @@ def setRelease(value):
 def isWindows():
     return os.name == 'nt'
 
+__isServer = False
 
+def setServerMode(isServer):
+    global __isServer
+    __isServer = isServer
+    
+def isServer():
+    global __isServer
+    return __isServer
+    
 def _generateMainModuleDir():
     mainModuleDir = os.getenv(MAINMODULE_DIR)
     if mainModuleDir:  # if environment variable set, return it
@@ -85,3 +101,16 @@ def getCommandNameForExecPath(execPath):
         return '"%s"' % execPath
     return execPath
 
+def getUserName():
+    if isWindows():
+        return os.getenv('USERNAME')
+    else:
+        # 06-Feb-06 stoens@activegrid.com --
+        # this blows up the linux cc runs with "Inappropriate ioctl for device"
+        #return os.getlogin()
+        return os.getenv('USER')        
+
+def getCurrentTimeAsFloat():
+    return time.time()
+
+systemStartTime = getCurrentTimeAsFloat()
diff --git a/wxPython/samples/ide/activegrid/util/utillang.py b/wxPython/samples/ide/activegrid/util/utillang.py
new file mode 100644 (file)
index 0000000..fad7e20
--- /dev/null
@@ -0,0 +1,146 @@
+#----------------------------------------------------------------------------
+# Name:         utillang.py
+# Purpose:      Provide language specific utilities 
+#
+# Author:       Joel Hare
+#
+# Created:      8/23/05
+# CVS-ID:       $Id$
+# Copyright:    (c) 2004-2005 ActiveGrid, Inc.
+# License:      wxWindows License
+#----------------------------------------------------------------------------
+
+import os
+import sys
+import UserDict
+import tempfile
+import xml.sax.saxutils as saxutils
+
+import activegrid.util.parser as parser
+
+PY2WEB_codepages = {
+     'cp1251' : 'CP-1251',
+     'koi8_r' : 'KOI8-R',
+}
+
+def evalXPath(xpath, data, specialEntries=None):
+    codeStr = parser.xpathToCode(xpath)
+    return evalCode(codeStr, data, specialEntries)
+
+def evalCode(codeStr, data, specialEntries=None):
+    if isinstance(data, ObjAsDict):
+        namespace = data
+    elif isinstance(data, dict):
+        namespace = dict(data)
+    else:
+        namespace = ObjAsDict(data)
+    if specialEntries:
+        for key, value in specialEntries.items():
+            namespace.addSpecialEntry(key, value)
+    return eval(codeStr, {}, namespace)
+
+def deriveCharset():
+    charset = None
+    encodingString = sys.getdefaultencoding()
+    if encodingString != 'ascii':
+        charset = PY2WEB_codepages.get(encodingString.lower())
+        if charset == None:
+            charset = encodingString
+    return charset
+
+def toUTF8(value):
+    """
+    Converts all unicode and non-string values to utf-8.
+    This assumes string instances are already encoded in utf-8.
+    Note that us-ascii is a subset of utf-8.
+    """
+    if isinstance(value, unicode):
+        return value.encode('utf-8')
+    return str(value)
+
+def toUnicode(value):
+    """
+    Converts all strings non-string values to unicode.
+    This assumes string instances are encoded in utf-8.
+    Note that us-ascii is a subset of utf-8.
+    """
+    if not isinstance(value, unicode):
+        if not isinstance(value, str):
+            return unicode(value)
+        return unicode(value, 'utf-8')
+    return value
+    
+
+def getSystemTempDir():
+    return tempfile.gettempdir()
+
+def getEnvVar(name, defaultVal=None):
+    if os.environ.has_key(name):
+        return os.environ[name]
+    return defaultVal
+
+class ObjAsDict(UserDict.DictMixin):
+    """
+    Passing this to eval as the local variables dictionary allows the
+    evaluated code to access properties in the wrapped object
+    """
+    def __init__(self, obj):
+        self.obj = obj
+        self.specialEntries = {}
+
+    def __getitem__(self, key):
+        try:
+            return getattr(self.obj, key)
+        except AttributeError, e:
+            if self.specialEntries.has_key(key):
+                return self.specialEntries[key]
+            raise KeyError(e.args)
+    def __setitem__(self, key, item): setattr(self.obj, key, item)
+    def __delitem__(self, key): delattr(self.obj, key)
+    def keys(self):
+        ret=[]
+        for i in list(dir(self.obj)+self.specialEntries.keys()):
+            if i=="__doc__" or i=="__module__":
+                pass
+            elif i not in ret:
+                ret.append(i)
+        return ret
+
+    def addSpecialEntry(self, key, value):
+        self.specialEntries[key] = value
+
+global saxXMLescapeDoubleQuote
+saxXMLescapeDoubleQuote = {'"':'&quot;'}
+
+global saxXMLescapesAllQuotes
+# IE doesn't support &apos; but it doesn't seem like we should need this escaped at all so I took it out.
+saxXMLescapesAllQuotes = {'"':'&quot;', "'":"&#039;"}
+
+global saxXMLunescapes
+saxXMLunescapes = {'&quot;':'"', "&#039;":"'"}
+
+def escape(data, extraEscapes=None):
+    """Escape ', ", &, <, and > in a string of data.
+
+    Basically, everything that saxutils.escape does (and this calls that, at
+    least for now), but with " and ' added as well.
+
+    TODO: make this faster; saxutils.escape() is really slow
+    """
+    
+    global saxXMLescapeDoubleQuote
+    if (extraEscapes == None):
+        extraEscapes = saxXMLescapeDoubleQuote
+    return saxutils.escape(data, extraEscapes)
+
+def unescape(data):
+    """Unescape ', ", &, <, and > in a string of data.
+
+    Basically, everything that saxutils.unescape does (and this calls that, at
+    least for now), but with " and ' added as well.
+
+    TODO: make this faster; saxutils.unescape() is really slow
+    """
+    
+    global saxXMLunescapes
+    return saxutils.unescape(data, saxXMLunescapes)
index 6a74d2c486bd2805438516918170f6f2d33f6900..ee758350d79e0c1a885ac30ebfe1767c1a17e562 100644 (file)
@@ -2,7 +2,7 @@
 # Name:         xmlmarshaller.py
 # Purpose:
 #
-# Authors:       John Spurling, Joel Hare, Alan Mullendore
+# Authors:      John Spurling, Joel Hare, Jeff Norton, Alan Mullendore
 #
 # Created:      7/28/04
 # CVS-ID:       $Id$
@@ -16,14 +16,18 @@ import logging
 ifDefPy()
 import xml.sax
 import xml.sax.handler
+import xml.sax.saxutils
+import datetime
 endIfDef()
-import xml.sax.saxutils as saxutils
+import activegrid.util.utillang as utillang
 import activegrid.util.objutils as objutils
+import activegrid.util.sysutils as sysutils
 import activegrid.util.aglogging as aglogging
 
 MODULE_PATH = "__main__"
 
 ## ToDO remove maxOccurs "unbounded" resolves to -1 hacks after bug 177 is fixed
+##unboundedVal = 2147483647 # value used for maxOccurs == "unbounded"
 
 """
 Special attributes that we recognize:
@@ -109,7 +113,6 @@ __xmlcdatacontent__ = "messyContent"
 
 global xmlMarshallerLogger
 xmlMarshallerLogger = logging.getLogger("activegrid.util.xmlmarshaller.marshal")
-xmlMarshallerLogger.setLevel(aglogging.LEVEL_WARN)
 # INFO  : low-level info
 # DEBUG : debugging info
 
@@ -184,6 +187,7 @@ DICT_ITEM_VALUE_NAME = "value"
 ################################################################################
 
 def setattrignorecase(object, name, value):
+##    print "[setattrignorecase] name = %s, value = %s" % (name, value)
     if (name not in object.__dict__):
         namelow = name.lower()
         for attr in object.__dict__:
@@ -193,27 +197,95 @@ def setattrignorecase(object, name, value):
     object.__dict__[name] = value
 
 def getComplexType(obj):
+    if (hasattr(obj, "_instancexsdcomplextype")):
+        return obj._instancexsdcomplextype
     if (hasattr(obj, "__xsdcomplextype__")):
         return obj.__xsdcomplextype__
     return None
 
-def _objectfactory(objname, objargs=None, objclass=None):
-    "dynamically create an object based on the objname and return it."
-##    print "[objectfactory] objname [%s]" % (objname)
+def _objectfactory(objtype, objargs=None, objclass=None):
+    "dynamically create an object based on the objtype and return it."
     if not isinstance(objargs, list):
         objargs = [objargs]
     if (objclass != None):
+        obj = None
         if (len(objargs) > 0):
             if (hasattr(objclass, "__xmlcdatacontent__")):
                 obj = objclass()
                 contentAttr = obj.__xmlcdatacontent__
                 obj.__dict__[contentAttr] = str(objargs[0])
-                return obj
-            return objclass(*objargs)
+            else:
+                obj = objclass(*objargs)
         else:
-            return objclass()
-    return objutils.newInstance(objname, objargs)
+            obj = objclass()
+        if ((obj != None) and (hasattr(obj, 'postUnmarshal'))):
+            obj.postUnmarshal()
+        return obj
+    return objutils.newInstance(objtype, objargs)
+
+class GenericXMLObject(object):
+    def __init__(self, content=None):
+        if content != None:
+            self._content = content
+            self.__xmlcontent__ = '_content'
+
+    def __str__(self):
+        return "GenericXMLObject(%s)" % objutils.toDiffableString(self.__dict__)
 
+    def setXMLAttributes(self, xmlName, attrs=None, children=None, nsMap=None, defaultNS=None):
+        if xmlName != None:
+            i = xmlName.rfind(':')
+            if i < 0:
+                self.__xmlname__ = xmlName
+                if defaultNS != None:
+                    self.__xmldefaultnamespace__ = str(defaultNS)
+            else:
+                self.__xmlname__ = xmlName[i+1:]
+                prefix = xmlName[:i]
+                if nsMap.has_key(prefix):
+                    self.__xmldefaultnamespace__ = str(nsMap[prefix])
+        if attrs != None:
+            for attrname, attr in attrs.items():
+                attrname = str(attrname)
+                if attrname == XMLNS or attrname.startswith(XMLNS_PREFIX):
+                    pass
+                elif attrname == "objtype":
+                    pass
+                else:
+                    if not hasattr(self, '__xmlattributes__'):
+                        self.__xmlattributes__ = []
+                    i = attrname.rfind(':')
+                    if i >= 0:
+                        prefix = attrname[:i]
+                        attrname = attrname[i+1:]
+                        if not hasattr(self, '__xmlattrnamespaces__'):
+                            self.__xmlattrnamespaces__ = {}
+                        if self.__xmlattrnamespaces__.has_key(prefix):
+                            alist = self.__xmlattrnamespaces__[prefix]
+                        else:
+                            alist = []
+                        alist.append(attrname)
+                        self.__xmlattrnamespaces__[prefix] = alist
+                    self.__xmlattributes__.append(attrname)
+            if hasattr(self, '__xmlattributes__'):
+                self.__xmlattributes__.sort()
+        if children != None and len(children) > 0:
+            childList = []
+            flattenList = {}
+            for childname, child in children:
+                childstr = str(childname)
+                if childstr in childList:
+                    if not flattenList.has_key(childstr):
+                        flattenList[childstr] = (childstr,)
+                else:
+                    childList.append(childstr)
+            if len(flattenList) > 0:
+                self.__xmlflattensequence__ = flattenList
+                
+    def initialize(self, arg1=None):
+        pass
+            
+    
 class Element:
     def __init__(self, name, attrs=None, xsname=None):
         self.name = name
@@ -222,9 +294,11 @@ class Element:
         self.children = []
         self.objclass = None
         self.xsname = xsname
+        self.objtype = None
         
     def getobjtype(self):
-        objtype = self.attrs.get("objtype")
+#        objtype = self.attrs.get("objtype")
+        objtype = self.objtype
         if (objtype == None):
             if (len(self.children) > 0):
                 objtype = "dict"
@@ -238,16 +312,35 @@ class NsElement(object):
         self.targetNS = None
         self.defaultNS = None
         self.prefix = None
-        
-    def isEmpty(self):
-        return ((self.nsMap == {}) and (self.targetNS == None) and (self.defaultNS == None))
 
+    def __str__(self):
+        if self.prefix == None:
+            strVal = 'prefix = None; '
+        else:
+            strVal = 'prefix = "%s"; ' % (self.prefix)
+        if self.targetNS == None:
+            strVal += 'targetNS = None; '
+        else:
+            strVal += 'targetNS = "%s"; ' % (self.targetNS)
+        if self.defaultNS == None:
+            strVal += 'defaultNS = None; '
+        else:
+            strVal += 'defaultNS = "%s"; ' % (self.defaultNS)
+        if len(self.nsMap) == 0:
+            strVal += 'nsMap = None; '
+        else:
+            strVal += 'nsMap = {'
+            for ik, iv in self.nsMap.iteritems():
+                strVal += '%s=%s; ' % (ik,iv)
+            strVal += '}'
+        return strVal
+               
     def setKnownTypes(self, masterKnownTypes, masterKnownNamespaces, parentNSE):
         # if we're a nested element, extend our parent element's mapping
         if parentNSE != None:
             self.knownTypes = parentNSE.knownTypes.copy()
             # but if we have a different default namespace, replace the parent's default mappings
-            if parentNSE.defaultNS != self.defaultNS:
+            if (self.defaultNS != None) and (parentNSE.defaultNS != self.defaultNS):
                 newKT = self.knownTypes.copy()
                 for tag in newKT:
                     if tag.find(':') < 0:
@@ -283,7 +376,6 @@ class NsElement(object):
                     self.knownTypes[knownTagName] = mapClass
             else:                                           # e.g. "ItemSearchRequest"
                 self.knownTypes[tag] = mapClass
-##                print 'mapping <%s> to class "%s"' % (tag, mapClass.__name__)
 
     def expandQName(self, eName, attrName, attrValue):
         bigValue = attrValue
@@ -298,38 +390,57 @@ class NsElement(object):
                 if shortNs == attrNS:
                     bigValue = '%s:%s' % (longNs, attrNCName)
                     break
-##        print '[expandQName] input attrName = "%s" and attrValue "%s"; output = "%s"' % (attrName, attrValue, bigValue)
         return bigValue
 
 class XMLObjectFactory(xml.sax.ContentHandler):
-    def __init__(self, knownTypes=None, knownNamespaces=None):
+    def __init__(self, knownTypes=None, knownNamespaces=None, xmlSource=None, createGenerics=False):
         self.rootelement = None
-        if (knownTypes == None):
-            self.knownTypes = {}
-        else:
-            self.knownTypes = knownTypes
-        if (knownNamespaces == None):
-            self.knownNamespaces = {}
+        if xmlSource == None:
+            self.xmlSource = "unknown"
         else:
-            self.knownNamespaces = knownNamespaces
+            self.xmlSource = xmlSource
+        self.createGenerics = createGenerics
         self.skipper = False
         self.elementstack = []
         self.nsstack = []
         self.collectContent = None
+        if (knownNamespaces == None):
+            self.knownNamespaces = {}
+        else:
+            self.knownNamespaces = knownNamespaces
+        self.reversedNamespaces = {}
+        for longns, shortns in self.knownNamespaces.iteritems():
+            self.reversedNamespaces[shortns] = longns
+        self.knownTypes = {}
+        if (knownTypes != None):
+            for tag, cls in knownTypes.iteritems():
+                i = tag.rfind(':')
+                if i >= 0:
+                    shortns = tag[:i]
+                    tag = tag[i+1:]
+                    if not self.reversedNamespaces.has_key(shortns):
+                        errorString = 'Error unmarshalling XML document from source "%s": knownTypes specifies an unmapped short namespace "%s" for element "%s"' % (self.xmlSource, shortns, tag)
+                        raise UnmarshallerException(errorString)
+                    longns = self.reversedNamespaces[shortns]
+                    tag = '%s:%s' % (longns, tag)
+                self.knownTypes[tag] = cls
+        #printKnownTypes(self.knownTypes, 'Unmarshaller.XMLObjectFactory.__init__')
         xml.sax.handler.ContentHandler.__init__(self)
 
     def appendElementStack(self, newElement, newNS):
         self.elementstack.append(newElement)
-        if (newNS.isEmpty()):
-            if (len(self.nsstack) > 0):
-                newNS = self.nsstack[-1]
-            else:
-                newNS.knownTypes = self.knownTypes.copy()
-        else:
-            if (len(self.nsstack) > 0):
-                newNS.setKnownTypes(self.knownTypes, self.knownNamespaces, self.nsstack[-1])
-            else:
-                newNS.setKnownTypes(self.knownTypes, self.knownNamespaces, None)
+        if (len(self.nsstack) > 0):
+            oldNS = self.nsstack[-1]
+            if newNS.defaultNS == None:
+                newNS.defaultNS = oldNS.defaultNS
+            if newNS.targetNS == None:
+                newNS.targetNS = oldNS.targetNS
+            if len(newNS.nsMap) == 0:
+                newNS.nsMap = oldNS.nsMap
+            elif len(oldNS.nsMap) > 0:
+                map = oldNS.nsMap.copy()
+                map.update(newNS.nsMap)
+                newNS.nsMap = map
         self.nsstack.append(newNS)
         return newNS
     
@@ -353,11 +464,16 @@ class XMLObjectFactory(xml.sax.ContentHandler):
             strVal += '>'
             self.collectContent.content += strVal
         xsname = name
-        if name.find(':') > -1:  # Strip namespace prefixes for now until actually looking them up in xsd
-            name = name[name.rfind(":") + 1:]
+        i = name.rfind(':')
+        if i >= 0:
+            nsname = name[:i]
+            name = name[i+1:]
+        else:
+            nsname = None
         element = Element(name, attrs.copy(), xsname=xsname)
         # if the element has namespace attributes, process them and add them to our stack
         nse = NsElement()
+        objtype = None
         for k in attrs.getNames():
             if k.startswith('xmlns'):
                 longNs = attrs[k]
@@ -371,8 +487,28 @@ class XMLObjectFactory(xml.sax.ContentHandler):
                     nse.nsMap[shortNs] = longNs
             elif k == 'targetNamespace':
                 nse.targetNS = attrs.getValue(k)
+            elif k == 'objtype':
+                objtype = attrs.getValue(k)
         nse = self.appendElementStack(element, nse)
-        element.objclass = nse.knownTypes.get(xsname)
+        if nsname != None:
+            if nse.nsMap.has_key(nsname):
+                longname = '%s:%s' % (nse.nsMap[nsname], name)
+##            elif objtype == None:
+##                errorString = 'Error unmarshalling XML document from source "%s": tag "%s" at line "%d", column "%d" has an undefined namespace' % (self.xmlSource, xsname, self._locator.getLineNumber(), self._locator.getColumnNumber())
+##                raise UnmarshallerException(errorString)
+            elif self.reversedNamespaces.has_key(nsname):
+                longname = '%s:%s' % (self.reversedNamespaces[nsname], name)
+            else:
+                longname = xsname
+        elif nse.defaultNS != None:
+            longname = '%s:%s' % (nse.defaultNS, name)
+        else:
+            longname = name
+        element.objtype = objtype
+        element.objclass = self.knownTypes.get(longname)
+        if element.objclass == None and len(self.knownNamespaces) == 0:
+            # handles common case where tags are unqualified and knownTypes are too, but there's a defaultNS
+            element.objclass = self.knownTypes.get(name)
         if (hasattr(element.objclass, "__xmlcontent__")):
             self.collectContent = element
 
@@ -387,8 +523,9 @@ class XMLObjectFactory(xml.sax.ContentHandler):
     def endElement(self, name):
 ##        print "[endElement] </%s>" % name
         xsname = name
-        if name.find(":") > -1:  # Strip namespace prefixes for now until actually looking them up in xsd
-            name = name[name.find(":") + 1:]
+        i = name.rfind(':')
+        if i >= 0:  # Strip namespace prefixes for now until actually looking them up in xsd
+            name = name[i+1:]
         if self.skipper:
             if xsname == "xs:annotation" or xsname == "xsd:annotation":     # here too
                 self.skipper = False
@@ -405,34 +542,36 @@ class XMLObjectFactory(xml.sax.ContentHandler):
         element, nse = self.popElementStack()
         if ((len(self.elementstack) > 1) and (self.elementstack[-1].getobjtype() == "None")):
             parentElement = self.elementstack[-2]
-##            print "[endElement] %s: found parent with objtype==None: using its grandparent" % name
         elif (len(self.elementstack) > 0):
             parentElement = self.elementstack[-1]
         objtype = element.getobjtype()
-##        print "element objtype is: ", objtype
         if (objtype == "None"):
-##            print "[endElement] %s: skipping a (objtype==None) end tag" % name
             return
         constructorarglist = []
         if (len(element.content) > 0):
             strippedElementContent = element.content.strip()
             if (len(strippedElementContent) > 0):
                 constructorarglist.append(element.content)
+        # If the element requires an object, but none is known, use the GenericXMLObject class
+        if ((element.objclass == None) and (element.attrs.get("objtype") == None) and ((len(element.attrs) > 0) or (len(element.children) > 0))):
+            if self.createGenerics:
+                element.objclass = GenericXMLObject
         obj = _objectfactory(objtype, constructorarglist, element.objclass)
+        if element.objclass == GenericXMLObject:
+            obj.setXMLAttributes(str(xsname), element.attrs, element.children, nse.nsMap, nse.defaultNS)
         complexType = getComplexType(obj)
         if (obj != None):
             if (hasattr(obj, "__xmlname__") and getattr(obj, "__xmlname__") == "sequence"):
                 self.elementstack[-1].children = oldChildren
                 return
         if (len(element.attrs) > 0) and not isinstance(obj, list):
-##            print "[endElement] %s: element has attrs and the obj is not a list" % name
             for attrname, attr in element.attrs.items():
                 if attrname == XMLNS or attrname.startswith(XMLNS_PREFIX):
                     if attrname.startswith(XMLNS_PREFIX):
                         ns = attrname[XMLNS_PREFIX_LENGTH:]
                     else:
                         ns = ""
-                    if complexType != None:
+                    if complexType != None or element.objclass == GenericXMLObject:
                         if not hasattr(obj, "__xmlnamespaces__"):
                             obj.__xmlnamespaces__ = {ns:attr}
                         elif ns not in obj.__xmlnamespaces__:
@@ -447,7 +586,6 @@ class XMLObjectFactory(xml.sax.ContentHandler):
                         xsdElement = complexType.findElement(attrname)
                         if (xsdElement != None):
                             type = xsdElement.type
-##                            print 'Unmarshalling element "%s", attribute "%s" with type "%s"' % (name, xsdElement.name, type)
                             if (type != None):
                                 if (type == TYPE_QNAME):
                                     attr = nse.expandQName(name, attrname, attr)
@@ -455,11 +593,15 @@ class XMLObjectFactory(xml.sax.ContentHandler):
                                 ### ToDO remove maxOccurs hack after bug 177 is fixed
                                 if attrname == "maxOccurs" and attr == "unbounded":
                                     attr = "-1"
-                                attr = _objectfactory(type, attr)
+                                try:
+                                    attr = _objectfactory(type, attr)
+                                except Exception, exceptData:
+                                    errorString = 'Error unmarshalling attribute "%s" at line %d, column %d in XML document from source "%s": %s' % (attrname, self._locator.getLineNumber(), self._locator.getColumnNumber(), self.xmlSource, str(exceptData))
+                                    raise UnmarshallerException(errorString)
                     try:
                         setattrignorecase(obj, _toAttrName(obj, attrname), attr)
                     except AttributeError:
-                        errorString = 'Error unmarshalling XML document at line %i, column %i: The object type of attribute "%s" of XML element "%s": not specified or known' % (self._locator.getLineNumber(), self._locator.getColumnNumber(), attrname, name)
+                        errorString = 'Error setting value of attribute "%s" at line %d, column %d in XML document from source "%s": object type of XML element "%s" is not specified or known' % (attrname, self._locator.getLineNumber(), self._locator.getColumnNumber(), self.xmlSource, name)
                         raise UnmarshallerException(errorString)
 ##                    obj.__dict__[_toAttrName(obj, attrname)] = attr
         # stuff any child attributes meant to be in a sequence via the __xmlflattensequence__
@@ -474,14 +616,12 @@ class XMLObjectFactory(xml.sax.ContentHandler):
                         flattenDict[str(xmlnametuple)] = sequencename
                     else:
                         for xmlname in xmlnametuple:
-##                            print "[endElement]: adding flattenDict[%s] = %s" % (xmlname, sequencename)
                             flattenDict[xmlname] = sequencename
             else:
                 raise Exception("Invalid type for __xmlflattensequence___ : it must be a dict")
 
         # reattach an object"s attributes to it
         for childname, child in element.children:
-##            print "[endElement] childname is: ", childname, "; child is: ", child
             if (childname in flattenDict):
                 sequencename = _toAttrName(obj, flattenDict[childname])
                 if (not hasattr(obj, sequencename)):
@@ -499,12 +639,13 @@ class XMLObjectFactory(xml.sax.ContentHandler):
                 else:
                     obj[childname] = child
             else:
-##                print "childname = %s, obj = %s, child = %s" % (childname, repr(obj), repr(child))
-                try:
-                    setattrignorecase(obj, _toAttrName(obj, childname), child)
-                except AttributeError:
-                    raise MarshallerException("Error unmarshalling child element \"%s\" of XML element \"%s\": object type not specified or known" % (childname, name))
-##                obj.__dict__[_toAttrName(obj, childname)] = child
+                # don't replace a good attribute value with a bad one
+                childAttrName = _toAttrName(obj, childname)
+                if (not hasattr(obj, childAttrName)) or (getattr(obj, childAttrName) == None) or (getattr(obj, childAttrName) == []) or (not isinstance(child, GenericXMLObject)):
+                    try:
+                        setattrignorecase(obj, childAttrName, child)
+                    except AttributeError:
+                        raise MarshallerException("Error unmarshalling child element \"%s\" of XML element \"%s\": object type not specified or known" % (childname, name))
 
         if (complexType != None):
             for element in complexType.elements:
@@ -524,7 +665,6 @@ class XMLObjectFactory(xml.sax.ContentHandler):
         if (len(self.elementstack) > 0):
 ##            print "[endElement] appending child with name: ", name, "; objtype: ", objtype
             parentElement.children.append((name, obj))
-##            print "parentElement now has ", len(parentElement.children), " children"
         else:
             self.rootelement = obj
             
@@ -539,7 +679,12 @@ def _toAttrName(obj, name):
                 break
 ##    if (name.startswith("__") and not name.endswith("__")):
 ##        name = "_%s%s" % (obj.__class__.__name__, name)
-    return name
+    return str(name)
+    
+def printKnownTypes(kt, where):
+    print 'KnownTypes from %s' % (where)
+    for tag, cls in kt.iteritems():
+        print '%s => %s' % (tag, str(cls))
 
 __typeMappingXsdToLang = {
     "string": "str",
@@ -569,7 +714,7 @@ def xsdToLangType(xsdType):
     if xsdType.startswith(XMLSCHEMA_XSD_URL):
         xsdType = xsdType[len(XMLSCHEMA_XSD_URL)+1:]
     elif xsdType.startswith(AG_URL):
-        xsdType = xsdType[len(AG_URL)+1:]        
+        xsdType = xsdType[len(AG_URL)+1:]
     langType = __typeMappingXsdToLang.get(xsdType)
     if (langType == None):
         raise Exception("Unknown xsd type %s" % xsdType)
@@ -588,8 +733,11 @@ def _getXmlValue(langValue):
     else:
         return str(langValue)
 
-def unmarshal(xmlstr, knownTypes=None, knownNamespaces=None, xmlSource=None):
-    objectfactory = XMLObjectFactory(knownTypes, knownNamespaces)
+def unmarshal(xmlstr, knownTypes=None, knownNamespaces=None, xmlSource=None, createGenerics=False):
+    objectfactory = XMLObjectFactory(knownTypes, knownNamespaces, xmlSource, createGenerics)
+    # on Linux, pyXML's sax.parseString fails when passed unicode
+    if (not sysutils.isWindows()):
+        xmlstr = str(xmlstr)
     try:
         xml.sax.parseString(xmlstr, objectfactory)
     except xml.sax.SAXParseException, errorData:
@@ -600,17 +748,19 @@ def unmarshal(xmlstr, knownTypes=None, knownNamespaces=None, xmlSource=None):
     return objectfactory.getRootObject()
 
 def marshal(obj, elementName=None, prettyPrint=False, marshalType=True, indent=0, knownTypes=None, knownNamespaces=None, encoding=-1):
-##    print '[marshal] entered with elementName = "%s"' % (elementName)
     worker = XMLMarshalWorker(prettyPrint=prettyPrint, marshalType=marshalType, knownTypes=knownTypes, knownNamespaces=knownNamespaces)    
     if obj != None and hasattr(obj, '__xmldeepexclude__'):
         worker.xmldeepexclude = obj.__xmldeepexclude__
     xmlstr = "".join(worker._marshal(obj, elementName, indent=indent))
-    if (isinstance(encoding, basestring)):
-        return '<?xml version="1.0" encoding="%s"?>\n%s' % (encoding, xmlstr.encode(encoding))
-    elif (encoding == None):
+    aglogging.info(xmlMarshallerLogger, "marshal produced string of type %s", type(xmlstr))
+    if (encoding == None):
         return xmlstr
-    else:
-        return '<?xml version="1.0" encoding="%s"?>\n%s' % (sys.getdefaultencoding(), xmlstr)
+    if (not isinstance(encoding, basestring)):
+        encoding = sys.getdefaultencoding()
+    if (not isinstance(xmlstr, unicode)):
+        xmlstr = xmlstr.decode()
+    xmlstr = u'<?xml version="1.0" encoding="%s"?>\n%s' % (encoding, xmlstr)
+    return xmlstr.encode(encoding)
 
 class XMLMarshalWorker(object):
     def __init__(self, marshalType=True, prettyPrint=False, knownTypes=None, knownNamespaces=None):
@@ -695,7 +845,7 @@ class XMLMarshalWorker(object):
             newNS.prefix = self.nsstack[-1].prefix
         else:
             newNS.prefix = ''
-        if hasattr(obj, "__xmldefaultnamespace__"):
+        if obj != None and hasattr(obj, "__xmldefaultnamespace__"):
             longPrefixNS = getattr(obj, "__xmldefaultnamespace__")
             if longPrefixNS == defaultLongNS:
                 newNS.prefix = ''
@@ -705,13 +855,12 @@ class XMLMarshalWorker(object):
                         if v == longPrefixNS:
                             newNS.prefix = k + ':'
                             break;
-##                    print '[appendNSStack] found longPrefixNS in nameSpaces = "%s"' % (newNS.prefix)
                 except:
                     if (longPrefixNS in asDict(self.knownNamespaces)):
                         newNS.prefix = self.knownNamespaces[longPrefixNS] + ':'
                     else:
                         raise MarshallerException('Error marshalling __xmldefaultnamespace__ ("%s") not defined in namespace stack' % (longPrefixNS))
-        if hasattr(obj, "targetNamespace"):
+        if obj != None and hasattr(obj, "targetNamespace"):
             newNS.targetNS = obj.targetNamespace
         elif len(self.nsstack) > 0:
             newNS.targetNS = self.nsstack[-1].targetNS
@@ -749,9 +898,11 @@ class XMLMarshalWorker(object):
             
     def _marshal(self, obj, elementName=None, nameSpacePrefix="", indent=0):
         if (obj != None):
-            xmlMarshallerLogger.debug("--> _marshal: elementName=%s%s, type=%s, obj=%s, indent=%d" % (nameSpacePrefix, elementName, type(obj), str(obj), indent))
+            aglogging.debug(xmlMarshallerLogger, "--> _marshal: elementName=%s%s, type=%s, obj=%s, indent=%d", nameSpacePrefix, elementName, type(obj), str(obj), indent)
         else:
-            xmlMarshallerLogger.debug("--> _marshal: elementName=%s%s, obj is None, indent=%d" % (nameSpacePrefix, elementName, indent))
+            aglogging.debug(xmlMarshallerLogger, "--> _marshal: elementName=%s%s, obj is None, indent=%d", nameSpacePrefix, elementName, indent)
+        if ((obj != None) and (hasattr(obj, 'preMarshal'))):
+            obj.preMarshal()
         excludeAttrs = []
         excludeAttrs.extend(self.xmldeepexclude)
         if hasattr(obj, "__xmlexclude__"):
@@ -768,8 +919,8 @@ class XMLMarshalWorker(object):
             newline = ""
             increment = 0
         ## Determine the XML element name. If it isn"t specified in the
-        ## parameter list, look for it in the __xmlname__ Lang
-        ## attribute, else use the default generic BASETYPE_ELEMENT_NAME.
+        ## parameter list, look for it in the __xmlname__ attribute,
+        ## else use the default generic BASETYPE_ELEMENT_NAME.
         nameSpaceAttrs = self.appendNSStack(obj)
         nameSpacePrefix = self.getNSPrefix()       
         if not elementName:
@@ -779,13 +930,15 @@ class XMLMarshalWorker(object):
                 elementName = nameSpacePrefix + BASETYPE_ELEMENT_NAME
         else:
             elementName = nameSpacePrefix + elementName
-##        print '[XMLMarshalWorker._marshal] elementName "%s"; nameSpaceAttrs is "%s"' % (elementName, nameSpaceAttrs)
     
         if (hasattr(obj, "__xmlsequencer__")) and (obj.__xmlsequencer__ != None):
             if (XMLSCHEMA_XSD_URL in self.nsstack[-1].nameSpaces.values()):
                 for kShort, vLong in self.nsstack[-1].nameSpaces.iteritems():
                     if vLong == XMLSCHEMA_XSD_URL:
-                        xsdPrefix = kShort + ':'
+                        if kShort != DEFAULT_NAMESPACE_KEY:
+                            xsdPrefix = kShort + ':'
+                        else:
+                            xsdPrefix = ''
                         break
             else:
                 xsdPrefix = 'xs:'
@@ -793,7 +946,6 @@ class XMLMarshalWorker(object):
         else:
             elementAdd = None
                    
-    ##    print "marshal: entered with elementName: ", elementName
         members_to_skip = []
         ## Add more members_to_skip based on ones the user has selected
         ## via the __xmlexclude__ and __xmldeepexclude__ attributes.
@@ -806,7 +958,6 @@ class XMLMarshalWorker(object):
             xmlattributes = obj.__xmlattributes__
             members_to_skip.extend(xmlattributes)
             for attr in xmlattributes:
-##                print 'Processing element "%s"; attribute "%s"' % (elementName, attr)
                 internalAttrName = attr
                 ifDefPy()
                 if (attr.startswith("__") and not attr.endswith("__")): 
@@ -814,7 +965,6 @@ class XMLMarshalWorker(object):
                 endIfDef()
                 # Fail silently if a python attribute is specified to be
                 # an XML attribute but is missing.
-##            print "marshal:   processing attribute ", internalAttrName
                 attrNameSpacePrefix = ""
                 if hasattr(obj, "__xmlattrnamespaces__"):
                     for nameSpaceKey, nameSpaceAttributes in getattr(obj, "__xmlattrnamespaces__").iteritems():
@@ -856,8 +1006,7 @@ class XMLMarshalWorker(object):
                 else:
                     value = objutils.toDiffableRepr(value)
     
-                objattrs += ' %s%s="%s"' % (attrNameSpacePrefix, attr, saxutils.escape(value))
-    ##            print "marshal:   new objattrs is: ", objattrs
+                objattrs += ' %s%s="%s"' % (attrNameSpacePrefix, attr, utillang.escape(value))
         if (obj == None):
             xmlString = [""]
         elif isinstance(obj, bool):
@@ -873,9 +1022,18 @@ class XMLMarshalWorker(object):
             objTypeStr = self._genObjTypeStr("float")
             xmlString = ['%s<%s%s>%s</%s>%s' % (prefix, elementName, objTypeStr, str(obj), elementName, newline)]
         elif isinstance(obj, unicode): # have to check before basestring - unicode is instance of base string
-            xmlString = ['%s<%s>%s</%s>%s' % (prefix, elementName, saxutils.escape(obj.encode()), elementName, newline)]
+            xmlString = ['%s<%s>%s</%s>%s' % (prefix, elementName, utillang.escape(obj.encode()), elementName, newline)]
         elif isinstance(obj, basestring):
-            xmlString = ['%s<%s>%s</%s>%s' % (prefix, elementName, saxutils.escape(obj), elementName, newline)]
+            xmlString = ['%s<%s>%s</%s>%s' % (prefix, elementName, utillang.escape(obj), elementName, newline)]
+        elif isinstance(obj, datetime.datetime):
+            objTypeStr = self._genObjTypeStr("datetime")
+            xmlString = ['%s<%s%s>%s</%s>%s' % (prefix, elementName, objTypeStr, str(obj), elementName, newline)]
+        elif isinstance(obj, datetime.date):
+            objTypeStr = self._genObjTypeStr("date")
+            xmlString = ['%s<%s%s>%s</%s>%s' % (prefix, elementName, objTypeStr, str(obj), elementName, newline)]
+        elif isinstance(obj, datetime.time):
+            objTypeStr = self._genObjTypeStr("time")
+            xmlString = ['%s<%s%s>%s</%s>%s' % (prefix, elementName, objTypeStr, str(obj), elementName, newline)]
         elif isinstance(obj, list):
             if len(obj) < 1:
                 xmlString = ""
@@ -910,13 +1068,15 @@ class XMLMarshalWorker(object):
         elif hasattr(obj, "__xmlcontent__"):
             contentValue = getattr(obj, obj.__xmlcontent__)
             if contentValue == None: 
-                contentValue = ''
+                xmlString = ["%s<%s%s%s/>%s" % (prefix, elementName, nameSpaceAttrs, objattrs, newline)]        
             else:
-                contentValue = saxutils.escape(contentValue)
-            xmlString = ["%s<%s%s%s>%s</%s>%s" % (prefix, elementName, nameSpaceAttrs, objattrs, contentValue, elementName, newline)]        
+                contentValue = utillang.escape(contentValue)
+                xmlString = ["%s<%s%s%s>%s</%s>%s" % (prefix, elementName, nameSpaceAttrs, objattrs, contentValue, elementName, newline)]        
         else:
             # Only add the objtype if the element tag is unknown to us.
-            if (self.isKnownType(elementName) == True): 
+            if (isinstance(obj, GenericXMLObject)):
+                objTypeStr = ""
+            elif (self.isKnownType(elementName) == True):
                 objTypeStr = ""
             else:
                 objTypeStr = self._genObjTypeStr("%s.%s" % (obj.__class__.__module__, className))
@@ -929,7 +1089,7 @@ class XMLMarshalWorker(object):
             if hasattr(obj, "__xmlbody__"):
                 xmlbody = getattr(obj, obj.__xmlbody__)
                 if xmlbody != None:
-                    xmlMemberString.append(xmlbody)           
+                    xmlMemberString.append(utillang.escape(xmlbody))
             else:
                 if hasattr(obj, "__xmlattrgroups__"):
                     attrGroups = obj.__xmlattrgroups__.copy()
@@ -975,21 +1135,17 @@ class XMLMarshalWorker(object):
                             xmlname = None
                             if (len(xmlnametuple) == 1):
                                 xmlname = xmlnametuple[0]
-    ##                        ix = 0
                             if not isinstance(value, (list, tuple)):
                               value = [value]
                             for seqitem in value:
-    ##                            xmlname = xmlnametuple[ix]
-    ##                            ix += 1
-    ##                            if (ix >= len(xmlnametuple)):
-    ##                                ix = 0
                                 xmlMemberString.extend(self._marshal(seqitem, xmlname, subElementNameSpacePrefix, indent=indent+increment))
                         else:
                             if (hasattr(obj, "__xmlrename__") and name in asDict(obj.__xmlrename__)):
                                 xmlname = obj.__xmlrename__[name]
                             else:
                                 xmlname = name
-                            xmlMemberString.extend(self._marshal(value, xmlname, subElementNameSpacePrefix, indent=indent+increment))
+                            if (value != None):
+                                xmlMemberString.extend(self._marshal(value, xmlname, subElementNameSpacePrefix, indent=indent+increment))
                     if (eName != "__nogroup__"):
                         xmlMemberString.append("%s</%s>%s" % (prefix, eName, newline))
                         prefix = prefix[:-increment]
@@ -1022,8 +1178,8 @@ class XMLMarshalWorker(object):
                     xmlString.append("><![CDATA[%s]]></%s>%s" % (cdataContent, elementName, newline))
                 else:
                     xmlString.append("/>%s" % newline)
-    ##        return xmlString
-        xmlMarshallerLogger.debug("<-- _marshal: %s" % str(xmlString))
+        if aglogging.isEnabledForDebug(xmlMarshallerLogger):
+            aglogging.debug(xmlMarshallerLogger, "<-- _marshal: %s", objutils.toDiffableString(xmlString))
         #print "<-- _marshal: %s" % str(xmlString)
         self.popNSStack()
         return xmlString
index 02c0c8b2ca890acdcd3b612f77f63af8963ab734..411d55e087a7debf456032ab065e26b79fb6e151 100644 (file)
@@ -22,35 +22,44 @@ import activegrid.util.aglogging as aglogging
 
 xmlLogger = logging.getLogger("activegrid.util.xml")
     
-def load(fileName, knownTypes=None, knownNamespaces=None):
+def load(fileName, knownTypes=None, knownNamespaces=None, createGenerics=False):
     loadedObject = None
     fileObject = file(fileName)
     timeStart = time.time()
+    xml = ""
     try:
         xml = fileObject.read()
-        loadedObject = unmarshal(xml, knownTypes=knownTypes, knownNamespaces=knownNamespaces, xmlSource=fileName)
+        loadedObject = unmarshal(xml, knownTypes=knownTypes, knownNamespaces=knownNamespaces, xmlSource=fileName, createGenerics=createGenerics)
         loadedObject.fileName = os.path.abspath(fileName)
         if hasattr(loadedObject, 'initialize'):
             loadedObject.initialize()
     finally:
         fileObject.close()
-        timeDone = time.time()
-        aglogging.info(xmlLogger, ('Load statistics for file %s: elapsed time = %f secs' % (fileName, timeDone-timeStart)))
+        if xmlLogger.isEnabledFor(aglogging.LEVEL_INFO):
+            timeDone = time.time()
+            aglogging.info(xmlLogger, ('Load statistics for file %s (%d bytes): elapsed time = %f secs' % (fileName, len(xml), timeDone-timeStart)))
     return loadedObject
 
-def loadURI(uri, knownTypes=None, knownNamespaces=None, xmlSource=None):
+def loadURI(uri, knownTypes=None, knownNamespaces=None, xmlSource=None, createGenerics=False):
     loadedObject = None
-    xml = urllib.urlopen(uri).read()
-    loadedObject = unmarshal(xml, knownTypes=knownTypes, knownNamespaces=knownNamespaces, xmlSource=xmlSource)
-    loadedObject.fileName = uri
-    if hasattr(loadedObject, 'initialize'):
-        loadedObject.initialize()
+    timeStart = time.time()
+    xml = ""
+    try:
+        xml = urllib.urlopen(uri).read()
+        loadedObject = unmarshal(xml, knownTypes=knownTypes, knownNamespaces=knownNamespaces, xmlSource=xmlSource, createGenerics=createGenerics)
+        loadedObject.fileName = uri
+        if hasattr(loadedObject, 'initialize'):
+            loadedObject.initialize()
+    finally:
+        if xmlLogger.isEnabledFor(aglogging.LEVEL_INFO):
+            timeDone = time.time()
+            aglogging.info(xmlLogger, ('Load statistics for URI %s (%d bytes): elapsed time = %f secs' % (uri, len(xml), timeDone-timeStart)))
     return loadedObject
 
-def unmarshal(xml, knownTypes=None, knownNamespaces=None, xmlSource=None):
+def unmarshal(xml, knownTypes=None, knownNamespaces=None, xmlSource=None, createGenerics=False):
     if (knownTypes == None): 
         knownTypes, knownNamespaces = getAgKnownTypes()
-    return xmlmarshaller.unmarshal(xml, knownTypes=knownTypes, knownNamespaces=knownNamespaces, xmlSource=xmlSource)    
+    return xmlmarshaller.unmarshal(xml, knownTypes=knownTypes, knownNamespaces=knownNamespaces, xmlSource=xmlSource, createGenerics=createGenerics)    
 
 def save(fileName, objectToSave, prettyPrint=True, marshalType=True, knownTypes=None, knownNamespaces=None, encoding='utf-8'):
     if hasattr(objectToSave, '_xmlReadOnly') and objectToSave._xmlReadOnly == True:
@@ -155,41 +164,6 @@ def getAgVersion(fileName):
             version = xml[i+1:j]
     return version
 
-def escape(data):
-    """Escape ', ", &, <, and > in a string of data.
-
-    Basically, everything that saxutils.escape does (and this calls that, at
-    least for now), but with " added as well.
-
-    XXX TODO make this faster; saxutils.escape() is really slow
-    """
-
-    import xml.sax.saxutils as saxutils
-
-    data=saxutils.escape(data)
-    data=data.replace("\"", "&quot;")
-
-    # IE doesn't support &apos;
-    # data=data.replace("\'", "&apos;")
-    data=data.replace("\'", "&#039;")
-
-    return data
-
-def unescape(data):
-    """Unescape ', ", &, <, and > in a string of data.
-
-    Basically, everything that saxutils.unescape does (and this calls that, at
-    least for now), but with " added as well.
-
-    XXX TODO make this faster; saxutils.unescape() is really slow
-    """
-
-    import xml.sax.saxutils as saxutils
-
-    data=data.replace("&quot;", "\"")
-    data=data.replace("&apos;", "\'")
-    return saxutils.unescape(data)
-
     
 AG_NS_URL = "http://www.activegrid.com/ag.xsd"
 BPEL_NS_URL = "http://schemas.xmlsoap.org/ws/2003/03/business-process"
@@ -197,7 +171,9 @@ HTTP_WSDL_NS_URL = "http://schemas.xmlsoap.org/wsdl/http/"
 MIME_WSDL_NS_URL = "http://schemas.xmlsoap.org/wsdl/mime/"
 SOAP_NS_URL = "http://schemas.xmlsoap.org/wsdl/soap/"
 SOAP12_NS_URL = "http://schemas.xmlsoap.org/wsdl/soap12/"
+SOAP_NS_ENCODING = "http://schemas.xmlsoap.org/soap/encoding/"
 WSDL_NS_URL = "http://schemas.xmlsoap.org/wsdl/"
+WSSE_NS_URL = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
 XFORMS_NS_URL = "http://www.w3c.org/xform.xsd"
 XMLSCHEMA_NS_URL = "http://www.w3.org/2001/XMLSchema"
 XSI_NS_URL = "http://www.w3.org/2001/XMLSchema-instance"
@@ -209,7 +185,8 @@ KNOWN_NAMESPACES = { AG_NS_URL          :  "ag",
                      MIME_WSDL_NS_URL   :  "mime",
                      SOAP_NS_URL        :  "soap",
                      SOAP12_NS_URL      :  "soap12",
-                     WSDL_NS_URL        :  "wsdl", 
+                     WSDL_NS_URL        :  "wsdl",
+                     WSSE_NS_URL        :  "wsse", 
                      XFORMS_NS_URL      :  "xforms",                             
                      XMLSCHEMA_NS_URL   :  "xs",
                      XACML_NS_URL       :  "xacml",
@@ -226,19 +203,23 @@ def getAgXsdToClassName():
             "ag:body"            : "activegrid.model.processmodel.Body",
             "ag:category_substitutions"    : "activegrid.server.layoutrenderer.CategorySubstitutions",
             "ag:command"         : "activegrid.model.wsdl.Command",
+            "ag:setElement"      : "activegrid.model.processmodel.SetElementOperation",
             "ag:css"             : "activegrid.server.layoutrenderer.CSS", 
-            "ag:cssRule"         : "activegrid.model.processmodel.CssRule",
             "ag:databaseService" : "activegrid.server.deployment.DatabaseService",
             "ag:datasource"      : "activegrid.data.dataservice.DataSource",
             "ag:dataObjectList"  : "activegrid.data.datalang.DataObjectList",
             "ag:debug"           : "activegrid.model.processmodel.DebugOperation",
             "ag:deployment"      : "activegrid.server.deployment.Deployment",
+            "ag:formData"        : "activegrid.model.processmodel.FormData",
+            "ag:formVar"         : "activegrid.model.processmodel.FormVar",
             "ag:generator"       : "activegrid.server.layoutrenderer.SerializableGenerator", 
             "ag:head"            : "activegrid.server.layoutrenderer.Head", 
             "ag:hr"              : "activegrid.model.processmodel.HorizontalRow",
             "ag:identity"        : "activegrid.model.identitymodel.Identity",
             "ag:identityref"     : "activegrid.server.deployment.IdentityRef",
             "ag:image"           : "activegrid.model.processmodel.Image",
+            "ag:inputPart"       : "activegrid.model.processmodel.InputPart",
+            "ag:keystore"        : "activegrid.model.identitymodel.KeyStore",
             "ag:label"           : "activegrid.model.processmodel.Label",
             "ag:layout"          : "activegrid.server.layoutrenderer.Layout", 
             "ag:layouts"         : "activegrid.server.layoutrenderer.Layouts", 
@@ -246,9 +227,11 @@ def getAgXsdToClassName():
             "ag:localService"    : "activegrid.server.deployment.LocalService",
             "ag:parameter"       : "activegrid.server.layoutrenderer.Parameter",
             "ag:parameters"      : "activegrid.server.layoutrenderer.Parameters",
+            "ag:postInitialize"  : "activegrid.model.processmodel.PostInitialize",
             "ag:processref"      : "activegrid.server.deployment.ProcessRef",
             "ag:query"           : "activegrid.model.processmodel.Query",
             "ag:soapService"     : "activegrid.server.deployment.SoapService",
+            "ag:redirect"        : "activegrid.server.layoutrenderer.Redirect", 
             "ag:requiredFile"    : "activegrid.server.layoutrenderer.RequiredFile", 
             "ag:resource"        : "activegrid.model.identitymodel.IDResource",
             "ag:restService"     : "activegrid.server.deployment.RestService",
@@ -355,27 +338,38 @@ def getAgXsdToClassName():
             "xforms:xforms"      : "activegrid.model.processmodel.XFormsRoot",
             "xs:all"             : "activegrid.model.schema.XsdSequence",
             "xs:any"             : "activegrid.model.schema.XsdAny",
+            "xs:anyAttribute"    : "activegrid.model.schema.XsdAnyAttribute",
             "xs:attribute"       : "activegrid.model.schema.XsdAttribute",
+            "xs:choice"          : "activegrid.model.schema.XsdChoice",
             "xs:complexContent"  : "activegrid.model.schema.XsdComplexContent",
             "xs:complexType"     : "activegrid.model.schema.XsdComplexType",
+            "xs:documentation"   : "activegrid.model.schema.XsdDocumentation",
             "xs:element"         : "activegrid.model.schema.XsdElement",
-            "xs:enumeration"     : "activegrid.model.schema.XsdEnumeration",
+            "xs:enumeration"     : "activegrid.model.schema.XsdFacetEnumeration",
             "xs:extension"       : "activegrid.model.schema.XsdExtension",
+            "xs:fractionDigits"  : "activegrid.model.schema.XsdFacetFractionDigits",
             "xs:field"           : "activegrid.model.schema.XsdKeyField",
             "xs:import"          : "activegrid.model.schema.XsdInclude",
             "xs:include"         : "activegrid.model.schema.XsdInclude",
             "xs:key"             : "activegrid.model.schema.XsdKey",
             "xs:keyref"          : "activegrid.model.schema.XsdKeyRef",
-            "xs:length"          : "activegrid.model.schema.XsdLength",
+            "xs:length"          : "activegrid.model.schema.XsdFacetLength",
             "xs:list"            : "activegrid.model.schema.XsdList",
-            "xs:maxLength"       : "activegrid.model.schema.XsdMaxLength",
+            "xs:maxExclusive"    : "activegrid.model.schema.XsdFacetMaxExclusive",
+            "xs:maxInclusive"    : "activegrid.model.schema.XsdFacetMaxInclusive",
+            "xs:maxLength"       : "activegrid.model.schema.XsdFacetMaxLength",
+            "xs:minExclusive"    : "activegrid.model.schema.XsdFacetMinExclusive",
+            "xs:minInclusive"    : "activegrid.model.schema.XsdFacetMinInclusive",
+            "xs:minLength"       : "activegrid.model.schema.XsdFacetMinLength",
+            "xs:pattern"         : "activegrid.model.schema.XsdFacetPattern",
             "xs:restriction"     : "activegrid.model.schema.XsdRestriction",
             "xs:schema"          : "activegrid.model.schema.Schema",
             "xs:selector"        : "activegrid.model.schema.XsdKeySelector",              
             "xs:sequence"        : "activegrid.model.schema.XsdSequence",
             "xs:simpleContent"   : "activegrid.model.schema.XsdSimpleContent",
             "xs:simpleType"      : "activegrid.model.schema.XsdSimpleType",
-            "xs:totalDigits"     : "activegrid.model.schema.XsdTotalDigits",
+            "xs:totalDigits"     : "activegrid.model.schema.XsdFacetTotalDigits",
+            "xs:whiteSpace"      : "activegrid.model.schema.XsdFacetWhiteSpace",
         }
     return agXsdToClassName
     
index 2fac20da2319fce20316142822779c24b7cf1cc8..3d5e2e40836a308c57d9e7970d3e960d29364112 100644 (file)
@@ -121,6 +121,7 @@ class FindService(wx.lib.pydocview.DocService):
                 self._findDialog = None
 
             self._replaceDialog = FindReplaceDialog(self.GetDocumentManager().FindSuitableParent(), -1, _("Replace"), size=(320,200), findString=findString)
+            self._replaceDialog.CenterOnParent()
             self._replaceDialog.Show(True)
         else:
             if self._replaceDialog != None:
@@ -129,6 +130,7 @@ class FindService(wx.lib.pydocview.DocService):
                 self._replaceDialog = None
 
             self._findDialog = FindDialog(self.GetDocumentManager().FindSuitableParent(), -1, _("Find"), size=(320,200), findString=findString)
+            self._findDialog.CenterOnParent()
             self._findDialog.Show(True)
 
 
@@ -152,6 +154,7 @@ class FindService(wx.lib.pydocview.DocService):
         """ Display Goto Line Number dialog box """
         line = -1
         dialog = wx.TextEntryDialog(parent, _("Enter line number to go to:"), _("Go to Line"))
+        dialog.CenterOnParent()
         if dialog.ShowModal() == wx.ID_OK:
             try:
                 line = int(dialog.GetValue())
@@ -356,7 +359,10 @@ class FindDialog(wx.Dialog):
         wx.EVT_BUTTON(self, FindService.FINDONE_ID, self.OnActionEvent)
         cancelBtn = wx.Button(self, wx.ID_CANCEL)
         wx.EVT_BUTTON(self, wx.ID_CANCEL, self.OnClose)
-        buttonSizer.Add(findBtn, 0, wx.BOTTOM, HALF_SPACE)
+        BTM_SPACE = HALF_SPACE
+        if wx.Platform == "__WXMAC__":
+            BTM_SPACE = SPACE
+        buttonSizer.Add(findBtn, 0, wx.BOTTOM, BTM_SPACE)
         buttonSizer.Add(cancelBtn, 0)
         gridSizer.Add(buttonSizer, pos=(0,2), span=(3,1))
 
@@ -455,9 +461,14 @@ class FindReplaceDialog(FindDialog):
         wx.EVT_BUTTON(self, FindService.REPLACEONE_ID, self.OnActionEvent)
         replaceAllBtn = wx.Button(self, FindService.REPLACEALL_ID, _("Replace All"))
         wx.EVT_BUTTON(self, FindService.REPLACEALL_ID, self.OnActionEvent)
-        buttonSizer.Add(findBtn, 0, wx.BOTTOM, HALF_SPACE)
-        buttonSizer.Add(replaceBtn, 0, wx.BOTTOM, HALF_SPACE)
-        buttonSizer.Add(replaceAllBtn, 0, wx.BOTTOM, HALF_SPACE)
+        
+        BTM_SPACE = HALF_SPACE
+        if wx.Platform == "__WXMAC__":
+            BTM_SPACE = SPACE
+            
+        buttonSizer.Add(findBtn, 0, wx.BOTTOM, BTM_SPACE)
+        buttonSizer.Add(replaceBtn, 0, wx.BOTTOM, BTM_SPACE)
+        buttonSizer.Add(replaceAllBtn, 0, wx.BOTTOM, BTM_SPACE)
         buttonSizer.Add(cancelBtn, 0)
         gridSizer.Add(buttonSizer, pos=(0,2), span=(3,1))
 
@@ -495,12 +506,24 @@ def getFindData():
     return \
 '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\
 \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\
-\x00\x00\x81IDAT8\x8d\xa5S\xc1\x16\xc0\x10\x0ckk\xff\xff\xc7d\x87\xad^U\r\
-\x93S\xe5U$\n\xb3$:\xc1e\x17(\x19Z\xb3$\x9e\xf1DD\xe2\x15\x01x\xea\x93\xef\
-\x04\x989\xea\x1b\xf2U\xc0\xda\xb4\xeb\x11\x1f:\xd8\xb5\xff8\x93\xd4\xa9\xae\
-@/S\xaaUwJ3\x85\xc0\x81\xee\xeb.q\x17C\x81\xd5XU \x1a\x93\xc6\x18\x8d\x90\
-\xe8}\x89\x00\x9a&\x9b_k\x94\x0c\xdf\xd78\xf8\x0b\x99Y\xb4\x08c\x9e\xfe\xc6\
-\xe3\x087\xf9\xd0D\x180\xf1#\x8e\x00\x00\x00\x00IEND\xaeB`\x82' 
+\x00\x01\xb1IDAT8\x8d\xa5\x93=o\xd3P\x14\x86\x1f\xa7\x11\x95<\xdc\xc6\xecN+5\
+[\x86B\x99\xacLQ2Zr[\x89\xa1\xfd\x0b%\x95\x90\x00\xf1\x03\x80\x01\x98\x80\
+\x19G\xac\x0cm\xff@Y\xd9:\xd9Ck\x94\xd6\xddb\x94\x9b\x98\xc8\xd2e1C\xe5\x8b\
+\xdd\x14\x96\xbe\xdb=\x1f\xefy\xef\xf90\x8c\xda\x12wA\xbd\xfc\x18\xfa\x9fs\
+\x80\xf9|\x0e\xc0\x93\xc1\x81\x01\xf0\xe6\xf5\xab\x1c`:\x9d\x02\xf0\xf6\xdd{\
+\xa3\xc8\xa9\xddd\xec\xf5z\xb4Z\xeb\x00\x1c\x1f\x1d\xe6\x85\xdd\xf3<\x06\x83\
+\xc1\x82\xbd\xa2 \x0cCL\xd3d<\x1e\x13\xc71\xb6m\x030\x1a\x8d\x08\x82\x00\x80\
+\xb3\xb3s:\x9d\x8e\xce\xa9(h6\x9b8\x8e\x83m\xdb4\x1a\r\x82 \xe0\xc5\xf3g\xb9\
+eY\xb4\xdbm\x1c\xc7Y\xe8\x81&\xf8\xf4\xf1C\xde\xedv+\xce\x97Owx\xfc\xe8k\xc5\
+\xb6\xb7\xb7\x8b\xef\x0foW \x84\xe0\xea\xea\x02\xa5\x94n\x18\x80\x94\x92\xd9\
+l\x02@\x96e\x95>\xd4nVO\xd3\xb9\x0e\xba\r\xa6i\xd2\xef\xf7\xf0\xfd!\xc7G\x87\
+y\xed:)\xd5\x01J\xfd\xd6c\xfc~\x9a\xfc\x93\xe8\xf2\xf2\x02(Ma6\x9b \x84@)\
+\xa5\t}\xff\x0b\xd0\'I~R\x14\xca\xb2L\xfb\x97\x97\xef-\xeeA!_J\x89\xeb\xba\
+\xb8\xae\xab\xbf\x06\x7f\x97\xacP[\x87\xeb9\x0b!H\x92\ta\x18"\xa5\xd4U\xbd\
+\xadm\xe3\xe1\x83\x8d<\x8a~\x90\xa6\xbf\x88\xe3\x18)\xa5&\xa9\x03X\x96E\xab\
+\xb5\x8em7\xf5\xc2\x94\xb1\xba\xba\xc6\xe6\xe6\x06++\xf7\x89\xa2\xa8\xe2\xd3\
+=89\xf9Va.\x14\x14\xd8\xdf?X VJa\x14\xd7X\xde\xef2\xbc\xadm\xe3\x7f~\xe3\xae\
+\xe7\xfc\x07\x84;\xc5\x82\xa1m&\x95\x00\x00\x00\x00IEND\xaeB`\x82' 
 
 
 def getFindBitmap():
index 496c489802c2987a1472f51ba4cbbf9f63b39a34..9fa41a92ca1eccb1b4d3508ca59f6be32b5375d3 100644 (file)
@@ -17,45 +17,45 @@ import FindService
 _ = wx.GetTranslation
 
 class TextDocument(wx.lib.docview.Document):
+    
+    def __init__(self):
+        wx.lib.docview.Document .__init__(self)
+        self._inModify = False
 
 
-    def OnSaveDocument(self, filename):
+    def SaveObject(self, fileObject):
         view = self.GetFirstView()
-        if not view.GetTextCtrl().SaveFile(filename):
-            return False
-        self.Modify(False)
-        self.SetDocumentSaved(True)
-        #if wx.Platform == "__WXMAC__":
-        #    fn = wx.Filename(filename)
-        #    fn.MacSetDefaultTypeAndCreator()
+        fileObject.write(view.GetTextCtrl().GetValue())
         return True
 
 
-    def OnOpenDocument(self, filename):
+    def LoadObject(self, fileObject):
         view = self.GetFirstView()
-        if not view.GetTextCtrl().LoadFile(filename):
-            return False
-        self.SetFilename(filename, True)
-        self.Modify(False)
-        self.UpdateAllViews()
-        self._savedYet = True
+        data = fileObject.read()
+        view.GetTextCtrl().SetValue(data)
         return True
 
 
     def IsModified(self):
         view = self.GetFirstView()
         if view and view.GetTextCtrl():
-            return wx.lib.docview.Document.IsModified(self) or view.GetTextCtrl().IsModified()
-        else:
-            return wx.lib.docview.Document.IsModified(self)
+            return view.GetTextCtrl().IsModified()
+        return False
 
 
-    def Modify(self, mod):
+    def Modify(self, modify):
+        if self._inModify:
+            return
+        self._inModify = True
+        
         view = self.GetFirstView()
-        wx.lib.docview.Document.Modify(self, mod)
-        if not mod and view and view.GetTextCtrl():
+        if not modify and view and view.GetTextCtrl():
             view.GetTextCtrl().DiscardEdits()
 
+        wx.lib.docview.Document.Modify(self, modify)  # this must called be after the DiscardEdits call above.
+
+        self._inModify = False
+
 
 class TextView(wx.lib.docview.View):
 
@@ -75,6 +75,7 @@ class TextView(wx.lib.docview.View):
         sizer = wx.BoxSizer()
         font, color = self._GetFontAndColorFromConfig()
         self._textCtrl = self._BuildTextCtrl(frame, font, color = color)
+        self._textCtrl.Bind(wx.EVT_TEXT, self.OnModify)
         sizer.Add(self._textCtrl, 1, wx.EXPAND, 0)
         frame.SetSizer(sizer)
         frame.Layout()
@@ -83,6 +84,10 @@ class TextView(wx.lib.docview.View):
         return True
 
 
+    def OnModify(self, event):
+        self.GetDocument().Modify(True)
+
+
     def _BuildTextCtrl(self, parent, font, color = wx.BLACK, value = "", selection = [0, 0]):
         if self._wordWrap:
             wordWrapStyle = wx.TE_WORDWRAP
@@ -131,6 +136,9 @@ class TextView(wx.lib.docview.View):
 
 
     def OnUpdate(self, sender = None, hint = None):
+        if wx.lib.docview.View.OnUpdate(self, sender, hint):
+            return
+
         if hint == "Word Wrap":
             self.SetWordWrap(wx.ConfigBase_Get().ReadInt("TextEditorWordWrap", True))
         elif hint == "Font":
index 85b6253205acf4b06810cce6e85ca3948cc6b014..3d24b224d3f8c7fa384ec73cf2248392c02b302d 100644 (file)
@@ -6,7 +6,7 @@
 #
 # Created:      5/15/03
 # CVS-ID:       $Id$
-# Copyright:    (c) 2003-2005 ActiveGrid, Inc. (Port of wxWindows classes by Julian Smart et al)
+# Copyright:    (c) 2003-2006 ActiveGrid, Inc. (Port of wxWindows classes by Julian Smart et al)
 # License:      wxWindows license
 #----------------------------------------------------------------------------
 
@@ -214,8 +214,10 @@ class Document(wx.EvtHandler):
         false otherwise. You may need to override this if your document view
         maintains its own record of being modified (for example if using
         xTextWindow to view and edit the document).
+        This method has been extended to notify its views that the dirty flag has changed.
         """
         self._documentModified = modify
+        self.UpdateAllViews(hint=("modify", self, self._documentModified))
 
 
     def SetDocumentModificationDate(self):
@@ -236,6 +238,16 @@ class Document(wx.EvtHandler):
         return self._documentModificationDate
 
 
+    def IsDocumentModificationDateCorrect(self):
+        """
+        Returns False if the file has been modified outside of the application.
+        This method has been added to wxPython and is not in wxWindows.
+        """
+        if not os.path.exists(self.GetFilename()):  # document must be in memory only and can't be out of date
+            return True
+        return self._documentModificationDate == os.path.getmtime(self.GetFilename())
+
+
     def GetViews(self):
         """
         Returns the list whose elements are the views on the document.
@@ -271,6 +283,7 @@ class Document(wx.EvtHandler):
         Destructor. Removes itself from the document manager.
         """
         self.DeleteContents()
+        self._documentModificationDate = None
         if self.GetDocumentManager():
             self.GetDocumentManager().RemoveDocument(self)
         wx.EvtHandler.Destroy(self)
@@ -364,7 +377,7 @@ class Document(wx.EvtHandler):
             return True
 
         """ check for file modification outside of application """
-        if os.path.exists(self.GetFilename()) and os.path.getmtime(self.GetFilename()) != self.GetDocumentModificationDate():
+        if not self.IsDocumentModificationDateCorrect():
             msgTitle = wx.GetApp().GetAppName()
             if not msgTitle:
                 msgTitle = _("Application")
@@ -485,9 +498,9 @@ class Document(wx.EvtHandler):
                           self.GetDocumentWindow())
             return False
 
+        self.SetDocumentModificationDate()
         self.SetFilename(filename, True)
         self.Modify(False)
-        self.SetDocumentModificationDate()
         self.SetDocumentSaved(True)
         #if wx.Platform == '__WXMAC__':  # Not yet implemented in wxPython
         #    wx.FileName(file).MacSetDefaultTypeAndCreator()
@@ -529,9 +542,9 @@ class Document(wx.EvtHandler):
                           self.GetDocumentWindow())
             return False
 
+        self.SetDocumentModificationDate()
         self.SetFilename(filename, True)
         self.Modify(False)
-        self.SetDocumentModificationDate()
         self.SetDocumentSaved(True)
         self.UpdateAllViews()
         return True
@@ -614,7 +627,7 @@ class Document(wx.EvtHandler):
             return True
 
         """ check for file modification outside of application """
-        if os.path.exists(self.GetFilename()) and os.path.getmtime(self.GetFilename()) != self.GetDocumentModificationDate():
+        if not self.IsDocumentModificationDateCorrect():
             msgTitle = wx.GetApp().GetAppName()
             if not msgTitle:
                 msgTitle = _("Warning")
@@ -844,8 +857,14 @@ class View(wx.EvtHandler):
         unused but may in future contain application-specific information for
         making updating more efficient.
         """
-        pass
-
+        if hint:
+            if hint[0] == "modify":  # if dirty flag changed, update the view's displayed title
+                frame = self.GetFrame()
+                if frame and hasattr(frame, "OnTitleIsModified"):
+                    frame.OnTitleIsModified()
+                    return True
+        return False
+        
 
     def OnChangeFilename(self):
         """
@@ -916,11 +935,11 @@ class View(wx.EvtHandler):
         Call this from your view frame's OnActivate member to tell the
         framework which view is currently active. If your windowing system
         doesn't call OnActivate, you may need to call this function from
-        any place where you know the view must be active, and
+        OnMenuCommand or any place where you know the view must be active, and
         the framework will need to get the current view.
 
         The prepackaged view frame wxDocChildFrame calls wxView.Activate from
-        its OnActivate member.
+        its OnActivate member and from its OnMenuCommand member.
         """
         if self.GetDocument() and self.GetDocumentManager():
             self.OnActivateView(activate, self, self.GetDocumentManager().GetCurrentView())
@@ -1865,7 +1884,7 @@ class DocManager(wx.EvtHandler):
             for document in self._docs:
                 if document.GetFilename() and os.path.normcase(document.GetFilename()) == os.path.normcase(path):
                     """ check for file modification outside of application """
-                    if os.path.exists(path) and os.path.getmtime(path) != document.GetDocumentModificationDate():
+                    if not document.IsDocumentModificationDateCorrect():
                         msgTitle = wx.GetApp().GetAppName()
                         if not msgTitle:
                             msgTitle = _("Warning")
@@ -2148,7 +2167,7 @@ class DocManager(wx.EvtHandler):
                     if len(descr) > 0:
                         descr = descr + _('|')
                     descr = descr + temp.GetDescription() + _(" (") + temp.GetFileFilter() + _(") |") + temp.GetFileFilter()  # spacing is important, make sure there is no space after the "|", it causes a bug on wx_gtk
-            descr = _("All (*.*)|*.*|%s") % descr  # spacing is important, make sure there is no space after the "|", it causes a bug on wx_gtk
+            descr = _("All|*.*|%s") % descr  # spacing is important, make sure there is no space after the "|", it causes a bug on wx_gtk
         else:
             descr = _("*.*")
 
@@ -2791,6 +2810,7 @@ class DocMDIChildFrame(wx.MDIChildFrame):
             self._childView.Activate(event.GetActive())
         self._activated = 0
 
+
     def OnCloseWindow(self, event):
         """
         Closes and deletes the current view and document.
@@ -2846,6 +2866,28 @@ class DocMDIChildFrame(wx.MDIChildFrame):
         self._childView = view
 
 
+    def OnTitleIsModified(self):
+        """
+        Add/remove to the frame's title an indication that the document is dirty.
+        If the document is dirty, an '*' is appended to the title
+        This method has been added to wxPython and is not in wxWindows.
+        """
+        title = self.GetTitle()
+        if title:
+            if self.GetDocument().IsModified():
+                if title.endswith("*"):
+                    return
+                else:
+                    title = title + "*"
+                    self.SetTitle(title)
+            else:
+                if title.endswith("*"):
+                    title = title[:-1]
+                    self.SetTitle(title)                
+                else:
+                    return
+
+
 class DocPrintout(wx.Printout):
     """
     DocPrintout is a default Printout that prints the first page of a document
@@ -2892,15 +2934,6 @@ class DocPrintout(wx.Printout):
         return pageNum == 1
 
 
-    def OnBeginDocument(self, startPage, endPage):
-        """
-        Not quite sure why this was overridden, but it was in wxWindows! :)
-        """
-        if not wx.Printout.OnBeginDocument(self, startPage, endPage):
-            return False
-        return True
-
-
     def GetPageInfo(self):
         """
         Indicates that the DocPrintout only has a single page.
index 2ac99a3e6b5cca8db710f8a2359345ec0a6d7f02..4e8d665c79ad744e89277f2d051da40bda360269 100644 (file)
@@ -2,11 +2,11 @@
 # Name:         pydocview.py
 # Purpose:      Python extensions to the wxWindows docview framework
 #
-# Author:       Peter Yared, Morgan Hua
+# Author:       Peter Yared, Morgan Hua, Matt Fryer
 #
 # Created:      5/15/03
 # CVS-ID:       $Id$
-# Copyright:    (c) 2003-2005 ActiveGrid, Inc.
+# Copyright:    (c) 2003-2006 ActiveGrid, Inc.
 # License:      wxWindows license
 #----------------------------------------------------------------------------
 
@@ -57,7 +57,7 @@ class DocFrameMixIn:
     Class with common code used by DocMDIParentFrame, DocTabbedParentFrame, and
     DocSDIFrame.
     """
-    
+
 
     def GetDocumentManager(self):
         """
@@ -132,7 +132,7 @@ class DocFrameMixIn:
         if sdi:
             if self.GetDocument() and self.GetDocument().GetCommandProcessor():
                 self.GetDocument().GetCommandProcessor().SetEditMenu(editMenu)
-            
+
         viewMenu = wx.Menu()
         viewMenu.AppendCheckItem(VIEW_TOOLBAR_ID, _("&Toolbar"), _("Shows or hides the toolbar"))
         wx.EVT_MENU(self, VIEW_TOOLBAR_ID, self.OnViewToolBar)
@@ -151,7 +151,7 @@ class DocFrameMixIn:
 
         if sdi:  # TODO: Is this really needed?
             wx.EVT_COMMAND_FIND_CLOSE(self, -1, self.ProcessEvent)
-            
+
         return menuBar
 
 
@@ -194,12 +194,12 @@ class DocFrameMixIn:
         Saves all of the currently open documents.
         """
         docs = wx.GetApp().GetDocumentManager().GetDocuments()
-        
+
         # save child documents first
         for doc in docs:
             if isinstance(doc, wx.lib.pydocview.ChildDocument):
                 doc.Save()
-                
+
         # save parent and other documents later
         for doc in docs:
             if not isinstance(doc, wx.lib.pydocview.ChildDocument):
@@ -258,7 +258,7 @@ class DocMDIParentFrameMixIn:
     """
     Class with common code used by DocMDIParentFrame and DocTabbedParentFrame.
     """
-    
+
 
     def _GetPosSizeFromConfig(self, pos, size):
         """
@@ -275,13 +275,13 @@ class DocMDIParentFrameMixIn:
 
             if wx.Display_GetFromPoint(pos) == -1:  # Check if the frame position is offscreen
                 pos = wx.DefaultPosition
-                
+
             if size == wx.DefaultSize:
                 size = wx.Size(config.ReadInt("MDIFrameXSize", 450), config.ReadInt("MDIFrameYSize", 300))
         return pos, size
 
 
-    def _InitFrame(self, embeddedWindows):
+    def _InitFrame(self, embeddedWindows, minSize):
         """
         Initializes the frame and creates the default menubar, toolbar, and status bar.
         """
@@ -306,7 +306,7 @@ class DocMDIParentFrameMixIn:
             # wxBug: On maximize, statusbar leaves a residual that needs to be refereshed, happens even when user does it
             self.Maximize()
 
-        self.CreateEmbeddedWindows(embeddedWindows)
+        self.CreateEmbeddedWindows(embeddedWindows, minSize)
         self._LayoutFrame()
 
         if wx.Platform == '__WXMAC__':
@@ -333,7 +333,7 @@ class DocMDIParentFrameMixIn:
         if id == SAVEALL_ID:
             self.OnFileSaveAll(event)
             return True
-            
+
         return wx.GetApp().ProcessEvent(event)
 
 
@@ -367,25 +367,24 @@ class DocMDIParentFrameMixIn:
                 if doc.IsModified():
                     filesModified = True
                     break
-                
+
             event.Enable(filesModified)
             return True
         else:
             return wx.GetApp().ProcessUpdateUIEvent(event)
 
 
-    def CreateEmbeddedWindows(self, windows=0):
+    def CreateEmbeddedWindows(self, windows=0, minSize=20):
         """
         Create the specified embedded windows around the edges of the frame.
         """
         frameSize = self.GetSize()   # TODO: GetClientWindow.GetSize is still returning 0,0 since the frame isn't fully constructed yet, so using full frame size
-        MIN_SIZE = 20
-        defaultHSize = max(MIN_SIZE, int(frameSize[0] / 6))
-        defaultVSize = max(MIN_SIZE, int(frameSize[1] / 7))
+        defaultHSize = max(minSize, int(frameSize[0] / 6))
+        defaultVSize = max(minSize, int(frameSize[1] / 7))
         defaultSubVSize = int(frameSize[1] / 2)
         config = wx.ConfigBase_Get()
         if windows & (EMBEDDED_WINDOW_LEFT | EMBEDDED_WINDOW_TOPLEFT | EMBEDDED_WINDOW_BOTTOMLEFT):
-            self._leftEmbWindow = self._CreateEmbeddedWindow(self, (max(MIN_SIZE,config.ReadInt("MDIEmbedLeftSize", defaultHSize)), -1), wx.LAYOUT_VERTICAL, wx.LAYOUT_LEFT, visible = config.ReadInt("MDIEmbedLeftVisible", 1), sash = wx.SASH_RIGHT)
+            self._leftEmbWindow = self._CreateEmbeddedWindow(self, (max(minSize,config.ReadInt("MDIEmbedLeftSize", defaultHSize)), -1), wx.LAYOUT_VERTICAL, wx.LAYOUT_LEFT, visible = config.ReadInt("MDIEmbedLeftVisible", 1), sash = wx.SASH_RIGHT)
         else:
             self._leftEmbWindow = None
         if windows & EMBEDDED_WINDOW_TOPLEFT:
@@ -397,7 +396,7 @@ class DocMDIParentFrameMixIn:
         else:
             self._bottomLeftEmbWindow = None
         if windows & (EMBEDDED_WINDOW_RIGHT | EMBEDDED_WINDOW_TOPRIGHT | EMBEDDED_WINDOW_BOTTOMRIGHT):
-            self._rightEmbWindow = self._CreateEmbeddedWindow(self, (max(MIN_SIZE,config.ReadInt("MDIEmbedRightSize", defaultHSize)), -1), wx.LAYOUT_VERTICAL, wx.LAYOUT_RIGHT, visible = config.ReadInt("MDIEmbedRightVisible", 1), sash = wx.SASH_LEFT)
+            self._rightEmbWindow = self._CreateEmbeddedWindow(self, (max(minSize,config.ReadInt("MDIEmbedRightSize", defaultHSize)), -1), wx.LAYOUT_VERTICAL, wx.LAYOUT_RIGHT, visible = config.ReadInt("MDIEmbedRightVisible", 1), sash = wx.SASH_LEFT)
         else:
             self._rightEmbWindow = None
         if windows & EMBEDDED_WINDOW_TOPRIGHT:
@@ -409,11 +408,11 @@ class DocMDIParentFrameMixIn:
         else:
             self._bottomRightEmbWindow = None
         if windows & EMBEDDED_WINDOW_TOP:
-            self._topEmbWindow = self._CreateEmbeddedWindow(self, (-1, max(MIN_SIZE,config.ReadInt("MDIEmbedTopSize", defaultVSize))), wx.LAYOUT_HORIZONTAL, wx.LAYOUT_TOP, visible = config.ReadInt("MDIEmbedTopVisible", 1), sash = wx.SASH_BOTTOM)
+            self._topEmbWindow = self._CreateEmbeddedWindow(self, (-1, max(minSize,config.ReadInt("MDIEmbedTopSize", defaultVSize))), wx.LAYOUT_HORIZONTAL, wx.LAYOUT_TOP, visible = config.ReadInt("MDIEmbedTopVisible", 1), sash = wx.SASH_BOTTOM)
         else:
             self._topEmbWindow = None
         if windows & EMBEDDED_WINDOW_BOTTOM:
-            self._bottomEmbWindow = self._CreateEmbeddedWindow(self, (-1, max(MIN_SIZE,config.ReadInt("MDIEmbedBottomSize", defaultVSize))), wx.LAYOUT_HORIZONTAL, wx.LAYOUT_BOTTOM, visible = config.ReadInt("MDIEmbedBottomVisible", 1), sash = wx.SASH_TOP)
+            self._bottomEmbWindow = self._CreateEmbeddedWindow(self, (-1, max(minSize,config.ReadInt("MDIEmbedBottomSize", defaultVSize))), wx.LAYOUT_HORIZONTAL, wx.LAYOUT_BOTTOM, visible = config.ReadInt("MDIEmbedBottomVisible", 1), sash = wx.SASH_TOP)
         else:
             self._bottomEmbWindow = None
 
@@ -499,7 +498,7 @@ class DocMDIParentFrameMixIn:
 
     def _CreateEmbeddedWindow(self, parent, size, orientation, alignment, visible=True, sash=None):
         """
-        Creates the embedded window with the specified size, orientation, and alignment.  If the 
+        Creates the embedded window with the specified size, orientation, and alignment.  If the
         window is not visible it will retain the size with which it was last viewed.
         """
         window = wx.SashLayoutWindow(parent, wx.NewId(), style = wx.NO_BORDER | wx.SW_3D)
@@ -544,7 +543,7 @@ class DocMDIParentFrameMixIn:
         if isinstance(window.GetParent(), wx.SashLayoutWindow):  # It is a parent sashwindow with multiple embedded sashwindows
             parentSashWindow = window.GetParent()
             if show:  # Make sure it is visible in case all of the subwindows were hidden
-                parentSashWindow.Show()                
+                parentSashWindow.Show()
             if show and window._sizeBeforeHidden:
                 if window._sizeBeforeHidden[1] == parentSashWindow.GetClientSize()[1]:
                     if window == self.GetEmbeddedWindow(EMBEDDED_WINDOW_BOTTOMLEFT) and self.GetEmbeddedWindow(EMBEDDED_WINDOW_TOPLEFT).IsShown():
@@ -568,7 +567,7 @@ class DocMDIParentFrameMixIn:
                             self.GetEmbeddedWindow(EMBEDDED_WINDOW_TOPRIGHT).SetDefaultSize(otherWindowSize)
                         elif window == self.GetEmbeddedWindow(EMBEDDED_WINDOW_TOPRIGHT):
                             self.GetEmbeddedWindow(EMBEDDED_WINDOW_BOTTOMRIGHT).SetDefaultSize(otherWindowSize)
-                    
+
             if not show:
                 if window == self.GetEmbeddedWindow(EMBEDDED_WINDOW_BOTTOMRIGHT) and not self.GetEmbeddedWindow(EMBEDDED_WINDOW_TOPRIGHT).IsShown() \
                     or window == self.GetEmbeddedWindow(EMBEDDED_WINDOW_TOPRIGHT) and not self.GetEmbeddedWindow(EMBEDDED_WINDOW_BOTTOMRIGHT).IsShown() \
@@ -618,7 +617,7 @@ class DocTabbedChildFrame(wx.Panel):
         Dummy method since the icon of tabbed frames are managed by the notebook.
         """
         return None
-        
+
 
     def SetIcon(self, icon):
         """
@@ -664,6 +663,23 @@ class DocTabbedChildFrame(wx.Panel):
         wx.GetApp().GetTopWindow().SetNotebookPageTitle(self, title)
 
 
+    def OnTitleIsModified(self):
+        """
+        Add/remove to the frame's title an indication that the document is dirty.
+        If the document is dirty, an '*' is appended to the title
+        """
+        title = self.GetTitle()
+        if title:
+            if self.GetDocument().IsModified():
+                if not title.endswith("*"):
+                    title = title + "*"
+                    self.SetTitle(title)
+            else:
+                if title.endswith("*"):
+                    title = title[:-1]
+                    self.SetTitle(title)
+        
+
     def ProcessEvent(event):
         """
         Processes an event, searching event tables and calling zero or more
@@ -719,7 +735,7 @@ class DocTabbedParentFrame(wx.Frame, DocFrameMixIn, DocMDIParentFrameMixIn):
     """
 
 
-    def __init__(self, docManager, frame, id, title, pos = wx.DefaultPosition, size = wx.DefaultSize, style = wx.DEFAULT_FRAME_STYLE, name = "DocTabbedParentFrame", embeddedWindows = 0):
+    def __init__(self, docManager, frame, id, title, pos = wx.DefaultPosition, size = wx.DefaultSize, style = wx.DEFAULT_FRAME_STYLE, name = "DocTabbedParentFrame", embeddedWindows = 0, minSize=20):
         """
         Constructor.  Note that the event table must be rebuilt for the
         frame since the EvtHandler is not virtual.
@@ -764,20 +780,20 @@ class DocTabbedParentFrame(wx.Frame, DocFrameMixIn, DocMDIParentFrameMixIn):
         # End From docview.MDIParentFrame
 
         self.CreateNotebook()
-        self._InitFrame(embeddedWindows)
-        
+        self._InitFrame(embeddedWindows, minSize)
+
 
     def _LayoutFrame(self):
         """
         Lays out the frame.
         """
         wx.LayoutAlgorithm().LayoutFrame(self, self._notebook)
-        
+
 
     def CreateNotebook(self):
         """
         Creates the notebook to use for the tabbed document interface.
-        """     
+        """
         if wx.Platform != "__WXMAC__":
             self._notebook = wx.Notebook(self, wx.NewId())
         else:
@@ -789,7 +805,7 @@ class DocTabbedParentFrame(wx.Frame, DocFrameMixIn, DocMDIParentFrameMixIn):
             wx.EVT_LISTBOOK_PAGE_CHANGED(self, self._notebook.GetId(), self.OnNotebookPageChanged)
         wx.EVT_RIGHT_DOWN(self._notebook, self.OnNotebookRightClick)
         wx.EVT_MIDDLE_DOWN(self._notebook, self.OnNotebookMiddleClick)
-        
+
         # wxBug: wx.Listbook does not implement HitTest the same way wx.Notebook
         # does, so for now don't fire MouseOver events.
         if wx.Platform != "__WXMAC__":
@@ -808,7 +824,7 @@ class DocTabbedParentFrame(wx.Frame, DocFrameMixIn, DocMDIParentFrameMixIn):
                         print "Warning: icon for '%s' isn't 16x16, not crossplatform" % template._docTypeName
                 iconIndex = iconList.AddIcon(icon)
                 self._iconIndexLookup.append((template, iconIndex))
-                
+
         icon = getBlankIcon()
         if icon.GetHeight() != 16 or icon.GetWidth() != 16:
             icon.SetHeight(16)
@@ -824,7 +840,7 @@ class DocTabbedParentFrame(wx.Frame, DocFrameMixIn, DocMDIParentFrameMixIn):
         Returns the notebook used by the tabbed document interface.
         """
         return self._notebook
-        
+
 
     def GetActiveChild(self):
         """
@@ -849,14 +865,16 @@ class DocTabbedParentFrame(wx.Frame, DocFrameMixIn, DocMDIParentFrameMixIn):
     def OnNotebookMouseOver(self, event):
         # wxBug: On Windows XP the tooltips don't automatically disappear when you move the mouse and it is on a notebook tab, has nothing to do with this code!!!
         index, type = self._notebook.HitTest(event.GetPosition())
-        
+
         if index > -1:
             doc = self._notebook.GetPage(index).GetView().GetDocument()
-            self._notebook.SetToolTip(wx.ToolTip(doc.GetFilename()))
+            # wxBug: Tooltips no longer appearing on tabs except on
+            # about a 2 pixel area between tab top and contents that will show tip.
+            self._notebook.GetParent().SetToolTip(wx.ToolTip(doc.GetFilename()))
         else:
             self._notebook.SetToolTip(wx.ToolTip(""))
         event.Skip()
-            
+
 
     def OnNotebookMiddleClick(self, event):
         """
@@ -868,7 +886,7 @@ class DocTabbedParentFrame(wx.Frame, DocFrameMixIn, DocMDIParentFrameMixIn):
             doc = self._notebook.GetPage(index).GetView().GetDocument()
             if doc:
                 doc.DeleteAllViews()
-                
+
     def OnNotebookRightClick(self, event):
         """
         Handles right clicks for the notebook, enabling users to either close
@@ -911,11 +929,11 @@ class DocTabbedParentFrame(wx.Frame, DocFrameMixIn, DocMDIParentFrameMixIn):
                 def OnRightMenuSelect(event):
                     self._notebook.SetSelection(selectIDs[event.GetId()])
                 wx.EVT_MENU(self, id, OnRightMenuSelect)
-        
+
         self._notebook.PopupMenu(menu, wx.Point(x, y))
         menu.Destroy()
-            
-            
+
+
     def AddNotebookPage(self, panel, title):
         """
         Adds a document page to the notebook.
@@ -934,7 +952,7 @@ class DocTabbedParentFrame(wx.Frame, DocFrameMixIn, DocMDIParentFrameMixIn):
                     break
         if not found:
             self._notebook.SetPageImage(index, self._blankIconIndex)
-        
+
         # wxBug: the wxListbook used on Mac needs its tabs list resized
         # whenever a new tab is added, but the only way to do this is
         # to resize the entire control
@@ -942,9 +960,12 @@ class DocTabbedParentFrame(wx.Frame, DocFrameMixIn, DocMDIParentFrameMixIn):
             content_size = self._notebook.GetSize()
             self._notebook.SetSize((content_size.x+2, -1))
             self._notebook.SetSize((content_size.x, -1))
-        
+
         self._notebook.Layout()
 
+        windowMenuService = wx.GetApp().GetService(WindowMenuService)
+        if windowMenuService:
+            windowMenuService.BuildWindowMenu(wx.GetApp().GetTopWindow())  # build file menu list when we open a file
 
 
     def RemoveNotebookPage(self, panel):
@@ -953,7 +974,18 @@ class DocTabbedParentFrame(wx.Frame, DocFrameMixIn, DocMDIParentFrameMixIn):
         """
         index = self.GetNotebookPageIndex(panel)
         if index > -1:
+            if self._notebook.GetPageCount() == 1 or index < 2:
+                pass
+            elif index >= 1:
+                self._notebook.SetSelection(index - 1)
+            elif index < self._notebook.GetPageCount():
+                self._notebook.SetSelection(index + 1)
             self._notebook.DeletePage(index)
+            self._notebook.GetParent().SetToolTip(wx.ToolTip(""))
+
+        windowMenuService = wx.GetApp().GetService(WindowMenuService)
+        if windowMenuService:
+            windowMenuService.BuildWindowMenu(wx.GetApp().GetTopWindow())  # build file menu list when we open a file
 
 
     def ActivateNotebookPage(self, panel):
@@ -964,15 +996,19 @@ class DocTabbedParentFrame(wx.Frame, DocFrameMixIn, DocMDIParentFrameMixIn):
         if index > -1:
             self._notebook.SetFocus()
             self._notebook.SetSelection(index)
-        
+
 
     def GetNotebookPageTitle(self, panel):
-        return self._notebook.GetPageText(self.GetNotebookPageIndex(panel))
-        
+        index = self.GetNotebookPageIndex(panel)
+        if index != -1:
+            return self._notebook.GetPageText(self.GetNotebookPageIndex(panel))
+        else:
+            return None
+
 
     def SetNotebookPageTitle(self, panel, title):
         self._notebook.SetPageText(self.GetNotebookPageIndex(panel), title)
-        
+
 
     def GetNotebookPageIndex(self, panel):
         """
@@ -984,7 +1020,7 @@ class DocTabbedParentFrame(wx.Frame, DocFrameMixIn, DocMDIParentFrameMixIn):
                 index = i
                 break
         return index
-        
+
 
     def ProcessEvent(self, event):
         """
@@ -1065,7 +1101,7 @@ class DocTabbedParentFrame(wx.Frame, DocFrameMixIn, DocMDIParentFrameMixIn):
             self.Destroy()
         else:
             event.Veto()
-            
+
 
 class DocMDIChildFrame(wx.MDIChildFrame):
     """
@@ -1121,6 +1157,27 @@ class DocMDIChildFrame(wx.MDIChildFrame):
             self._childView.Activate(True)
 
 
+    def OnTitleIsModified(self):
+        """
+        Add/remove to the frame's title an indication that the document is dirty.
+        If the document is dirty, an '*' is appended to the title
+        """
+        title = self.GetTitle()
+        if title:
+            if self.GetDocument().IsModified():
+                if title.endswith("*"):
+                    return
+                else:
+                    title = title + "*"
+                    self.SetTitle(title)
+            else:
+                if title.endswith("*"):
+                    title = title[:-1]
+                    self.SetTitle(title)                
+                else:
+                    return
+
+
     def ProcessEvent(event):
         """
         Processes an event, searching event tables and calling zero or more
@@ -1216,9 +1273,6 @@ class DocMDIChildFrame(wx.MDIChildFrame):
         self._childView = view
 
 
-
-
-
 class DocService(wx.EvtHandler):
     """
     An abstract class used to add reusable services to a docview application.
@@ -1247,7 +1301,7 @@ class DocService(wx.EvtHandler):
 
     def ProcessEventBeforeWindows(self, event):
         """
-        Processes an event before the main window has a chance to process the window. 
+        Processes an event before the main window has a chance to process the window.
         Override this method for a particular service.
         """
         return False
@@ -1360,7 +1414,7 @@ class DocOptionsService(DocService):
                 toolsMenu.AppendSeparator()
             toolsMenu.Append(self._toolOptionsID, _("&Options..."), _("Sets options"))
             wx.EVT_MENU(frame, self._toolOptionsID, frame.ProcessEvent)
-            
+
 
     def ProcessEvent(self, event):
         """
@@ -1379,7 +1433,7 @@ class DocOptionsService(DocService):
         Return the modes supported by the application.  Use docview.DOC_SDI and
         docview.DOC_MDI flags to check if SDI and/or MDI modes are supported.
         """
-        return self._supportedModes        
+        return self._supportedModes
 
 
     def SetSupportedModes(self, _supportedModessupportedModes):
@@ -1400,7 +1454,7 @@ class DocOptionsService(DocService):
 
     def AddOptionsPanel(self, optionsPanel):
         """
-        Adds an options panel to the options dialog. 
+        Adds an options panel to the options dialog.
         """
         self._optionsPanels.append(optionsPanel)
 
@@ -1429,7 +1483,7 @@ class OptionsDialog(wx.Dialog):
         """
         Initializes the options dialog with a notebook page that contains new
         instances of the passed optionsPanelClasses.
-        """        
+        """
         wx.Dialog.__init__(self, parent, -1, _("Options"))
 
         self._optionsPanels = []
@@ -1445,15 +1499,15 @@ class OptionsDialog(wx.Dialog):
         else:
             optionsNotebook = wx.Notebook(self, wx.NewId(), style=wx.NB_MULTILINE)  # NB_MULTILINE is windows platform only
         sizer.Add(optionsNotebook, 0, wx.ALL | wx.EXPAND, SPACE)
-        
+
         if wx.Platform == "__WXMAC__":
             iconList = wx.ImageList(16, 16, initialCount = len(optionsPanelClasses))
             self._iconIndexLookup = []
-               
+
             for optionsPanelClass in optionsPanelClasses:
                 optionsPanel = optionsPanelClass(optionsNotebook, -1)
                 self._optionsPanels.append(optionsPanel)
-             
+
                 # We need to populate the image list before setting notebook images
                 if hasattr(optionsPanel, "GetIcon"):
                     icon = optionsPanel.GetIcon()
@@ -1467,18 +1521,18 @@ class OptionsDialog(wx.Dialog):
                             print "Warning: icon for '%s' isn't 16x16, not crossplatform" % template._docTypeName
                     iconIndex = iconList.AddIcon(icon)
                     self._iconIndexLookup.append((optionsPanel, iconIndex))
-            
+
                 else:
                     # use -1 to represent that this panel has no icon
                     self._iconIndexLookup.append((optionsPanel, -1))
-                
+
             optionsNotebook.AssignImageList(iconList)
-        
+
             # Add icons to notebook
             for index in range(0, len(optionsPanelClasses)-1):
                 iconIndex = self._iconIndexLookup[index][1]
                 if iconIndex >= 0:
-                    optionsNotebook.SetPageImage(index, iconIndex)        
+                    optionsNotebook.SetPageImage(index, iconIndex)
         else:
             for optionsPanelClass in optionsPanelClasses:
                 optionsPanel = optionsPanelClass(optionsNotebook, -1)
@@ -1525,7 +1579,7 @@ class GeneralOptionsPanel(wx.Panel):
     def __init__(self, parent, id):
         """
         Initializes the panel by adding an "Options" folder tab to the parent notebook and
-        populating the panel with the generic properties of a pydocview application. 
+        populating the panel with the generic properties of a pydocview application.
         """
         wx.Panel.__init__(self, parent, id)
         SPACE = 10
@@ -1544,7 +1598,7 @@ class GeneralOptionsPanel(wx.Panel):
             choices.append(self._mdiChoice)
             if wx.Platform == "__WXMSW__":
                 choices.append(self._winMdiChoice)
-            self._documentRadioBox = wx.RadioBox(self, -1, _("Document Interface"),
+            self._documentRadioBox = wx.RadioBox(self, -1, _("Document Display Style"),
                                           choices = choices,
                                           majorDimension=1,
                                           )
@@ -1574,7 +1628,7 @@ class GeneralOptionsPanel(wx.Panel):
         self.SetSizer(optionsBorderSizer)
         self.Layout()
         self._documentInterfaceMessageShown = False
-        parent.AddPage(self, _("Options"))
+        parent.AddPage(self, _("General"))
 
 
     def _AllowModeChanges(self):
@@ -1619,7 +1673,7 @@ class DocApp(wx.PySimpleApp):
             self._debug = False
         if not hasattr(self, "_singleInstance"):  # only set if not already initialized
             self._singleInstance = True
-            
+
         # if _singleInstance is TRUE only allow one single instance of app to run.
         # When user tries to run a second instance of the app, abort startup,
         # But if user also specifies files to open in command line, send message to running app to open those files
@@ -1655,14 +1709,14 @@ class DocApp(wx.PySimpleApp):
                         break
                     else:
                         time.sleep(1)  # give enough time for buffer to be available
-                        
+
                 return False
             else:
                 self._timer = wx.PyTimer(self.DoBackgroundListenAndLoad)
                 self._timer.Start(250)
 
         return True
-        
+
 
     def OpenMainFrame(self):
         docManager = self.GetDocumentManager()
@@ -1670,19 +1724,19 @@ class DocApp(wx.PySimpleApp):
             if self.GetUseTabbedMDI():
                 frame = wx.lib.pydocview.DocTabbedParentFrame(docManager, None, -1, self.GetAppName())
             else:
-                frame = wx.lib.pydocview.DocMDIParentFrame(docManager, None, -1, self.GetAppName())                
+                frame = wx.lib.pydocview.DocMDIParentFrame(docManager, None, -1, self.GetAppName())
             frame.Show(True)
 
     def MacOpenFile(self, filename):
         self.GetDocumentManager().CreateDocument(os.path.normpath(filename), wx.lib.docview.DOC_SILENT)
-            
+
         # force display of running app
         topWindow = wx.GetApp().GetTopWindow()
         if topWindow.IsIconized():
             topWindow.Iconize(False)
         else:
             topWindow.Raise()
-                
+
     def DoBackgroundListenAndLoad(self):
         """
         Open any files specified in the given command line argument passed in via shared memory
@@ -1699,15 +1753,15 @@ class DocApp(wx.PySimpleApp):
             for arg in args:
                 if (wx.Platform != "__WXMSW__" or arg[0] != "/") and arg[0] != '-' and os.path.exists(arg):
                     self.GetDocumentManager().CreateDocument(os.path.normpath(arg), wx.lib.docview.DOC_SILENT)
-            
+
             # force display of running app
             topWindow = wx.GetApp().GetTopWindow()
             if topWindow.IsIconized():
                 topWindow.Iconize(False)
             else:
                 topWindow.Raise()
-            
-        
+
+
         self._timer.Start(1000) # 1 second interval
 
 
@@ -1742,7 +1796,7 @@ class DocApp(wx.PySimpleApp):
     def ProcessEventBeforeWindows(self, event):
         """
         Enables services to process an event before the main window has a chance to
-        process the window. 
+        process the window.
         """
         for service in self._services:
             if service.ProcessEventBeforeWindows(event):
@@ -1824,11 +1878,11 @@ class DocApp(wx.PySimpleApp):
             service.OnExit()
         config = wx.ConfigBase_Get()
         self._docManager.FileHistorySave(config)
-        
+
         if hasattr(self, "_singleInstanceChecker"):
             del self._singleInstanceChecker
 
-    
+
     def GetDefaultDocManagerFlags(self):
         """
         Returns the default flags to use when creating the DocManager.
@@ -1875,7 +1929,7 @@ class DocApp(wx.PySimpleApp):
         Returns True if Windows MDI should use folder tabs instead of child windows.
         """
         return self._useTabbedMDI
-        
+
 
     def SetUseTabbedMDI(self, useTabbedMDI):
         """
@@ -1886,7 +1940,7 @@ class DocApp(wx.PySimpleApp):
 
     def CreateDocumentFrame(self, view, doc, flags, id = -1, title = "", pos = wx.DefaultPosition, size = wx.DefaultSize, style = wx.DEFAULT_FRAME_STYLE):
         """
-        Called by the DocManager to create and return a new Frame for a Document. 
+        Called by the DocManager to create and return a new Frame for a Document.
         Chooses whether to create an MDIChildFrame or SDI Frame based on the
         DocManager's flags.
         """
@@ -2057,7 +2111,7 @@ class DocApp(wx.PySimpleApp):
         """
         Creates a child window of a document that edits an object.  The child window
         is managed by the parent document frame, so it will be prompted to close if its
-        parent is closed, etc.  Child Documents are useful when there are complicated 
+        parent is closed, etc.  Child Documents are useful when there are complicated
         Views of a Document and users will need to tunnel into the View.
         """
         for document in self.GetDocumentManager().GetDocuments()[:]:  # Cloning list to make sure we go through all docs even as they are deleted
@@ -2117,7 +2171,7 @@ class DocApp(wx.PySimpleApp):
             splash_bmp = image
         else:
             splash_bmp = wx.Image(image).ConvertToBitmap()
-        self._splash = wx.SplashScreen(splash_bmp, wx.SPLASH_CENTRE_ON_SCREEN|wx.SPLASH_NO_TIMEOUT, 0, None, -1, style=wx.SIMPLE_BORDER|wx.FRAME_NO_TASKBAR) 
+        self._splash = wx.SplashScreen(splash_bmp, wx.SPLASH_CENTRE_ON_SCREEN|wx.SPLASH_NO_TIMEOUT, 0, None, -1, style=wx.SIMPLE_BORDER|wx.FRAME_NO_TASKBAR)
         self._splash.Show()
 
 
@@ -2127,8 +2181,8 @@ class DocApp(wx.PySimpleApp):
         """
         if self._splash:
             self._splash.Close(True)
-        
-    
+
+
 class _DocFrameFileDropTarget(wx.FileDropTarget):
     """
     Class used to handle drops into the document frame.
@@ -2155,7 +2209,7 @@ class _DocFrameFileDropTarget(wx.FileDropTarget):
             msgTitle = wx.GetApp().GetAppName()
             if not msgTitle:
                 msgTitle = _("File Error")
-            wx.MessageBox("Could not open '%s'.  '%s'" % (docview.FileNameFromPath(file), sys.exc_value),
+            wx.MessageBox("Could not open '%s'.  '%s'" % (wx.lib.docview.FileNameFromPath(file), sys.exc_value),
                           msgTitle,
                           wx.OK | wx.ICON_EXCLAMATION,
                           self._docManager.FindSuitableParent())
@@ -2167,9 +2221,9 @@ class DocMDIParentFrame(wx.lib.docview.DocMDIParentFrame, DocFrameMixIn, DocMDIP
     features such as a default menubar, toolbar, and status bar, and a mechanism to manage embedded windows
     on the edges of the DocMDIParentFrame.
     """
-    
 
-    def __init__(self, docManager, parent, id, title, pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE, name="DocMDIFrame", embeddedWindows=0):
+
+    def __init__(self, docManager, parent, id, title, pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE, name="DocMDIFrame", embeddedWindows=0, minSize=20):
         """
         Initializes the DocMDIParentFrame with the default menubar, toolbar, and status bar.  Use the
         optional embeddedWindows parameter with the embedded window constants to create embedded
@@ -2177,7 +2231,7 @@ class DocMDIParentFrame(wx.lib.docview.DocMDIParentFrame, DocFrameMixIn, DocMDIP
         """
         pos, size = self._GetPosSizeFromConfig(pos, size)
         wx.lib.docview.DocMDIParentFrame.__init__(self, docManager, parent, id, title, pos, size, style, name)
-        self._InitFrame(embeddedWindows)
+        self._InitFrame(embeddedWindows, minSize)
 
 
     def _LayoutFrame(self):
@@ -2186,7 +2240,7 @@ class DocMDIParentFrame(wx.lib.docview.DocMDIParentFrame, DocFrameMixIn, DocMDIP
         """
         wx.LayoutAlgorithm().LayoutMDIFrame(self)
         self.GetClientWindow().Refresh()
-        
+
 
     def ProcessEvent(self, event):
         """
@@ -2239,7 +2293,7 @@ class DocMDIParentFrame(wx.lib.docview.DocMDIParentFrame, DocFrameMixIn, DocMDIP
                 wx.IDM_WINDOWPREV = 4006  # wxBug: Not defined for some reason
                 windowMenu.Enable(wx.IDM_WINDOWPREV, has2OrMoreWindows)
                 windowMenu.Enable(wx.IDM_WINDOWNEXT, has2OrMoreWindows)
-                
+
 
 
     def OnSize(self, event):
@@ -2272,7 +2326,7 @@ class DocSDIFrame(wx.lib.docview.DocChildFrame, DocFrameMixIn):
     The DocSDIFrame host DocManager Document windows.  It offers features such as a default menubar,
     toolbar, and status bar.
     """
-    
+
 
     def __init__(self, doc, view, parent, id, title, pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE, name="DocSDIFrame"):
         """
@@ -2308,7 +2362,7 @@ class DocSDIFrame(wx.lib.docview.DocChildFrame, DocFrameMixIn):
         Lays out the Frame.
         """
         self.Layout()
-        
+
 
     def OnExit(self, event):
         """
@@ -2403,7 +2457,7 @@ class DocSDIFrame(wx.lib.docview.DocChildFrame, DocFrameMixIn):
                 if doc.IsModified():
                     filesModified = True
                     break
-                
+
             event.Enable(filesModified)
             return True
         else:
@@ -2436,7 +2490,7 @@ class AboutService(DocService):
         else:
             self._dlg = AboutDialog  # use default AboutDialog
             self._image = image
-        
+
 
     def ShowAbout(self):
         """
@@ -2462,7 +2516,7 @@ class AboutDialog(wx.Dialog):
     """
     Opens an AboutDialog.  Shared by DocMDIParentFrame and DocSDIFrame.
     """
-    
+
     def __init__(self, parent, image=None):
         """
         Initializes the about dialog.
@@ -2474,13 +2528,13 @@ class AboutDialog(wx.Dialog):
             imageItem = wx.StaticBitmap(self, -1, image.ConvertToBitmap(), (0,0), (image.GetWidth(), image.GetHeight()))
             sizer.Add(imageItem, 0, wx.ALIGN_CENTER|wx.ALL, 0)
         sizer.Add(wx.StaticText(self, -1, wx.GetApp().GetAppName()), 0, wx.ALIGN_CENTRE|wx.ALL, 5)
-    
+
         btn = wx.Button(self, wx.ID_OK)
         sizer.Add(btn, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
-    
+
         self.SetSizer(sizer)
         sizer.Fit(self)
-    
+
 
 
 class FilePropertiesService(DocService):
@@ -2680,7 +2734,7 @@ class ChildDocument(wx.lib.docview.Document):
     """
     A ChildDocument is a document that represents a portion of a Document.  The child
     document is managed by the parent document, so it will be prompted to close if its
-    parent is closed, etc.  Child Documents are useful when there are complicated 
+    parent is closed, etc.  Child Documents are useful when there are complicated
     Views of a Document and users will need to tunnel into the View.
     """
 
@@ -2709,7 +2763,7 @@ class ChildDocument(wx.lib.docview.Document):
     def SetParentDocument(self, parentDocument):
         """
         Sets the parent Document of the ChildDocument.
-        """        
+        """
         self._parentDocument = parentDocument
 
 
@@ -2749,7 +2803,7 @@ class ChildDocument(wx.lib.docview.Document):
         Called when the ChildDocument is saved and does the minimum such that the
         ChildDocument looks like a real Document to the framework.
         """
-        return self.OnSaveDocument(self._documentFile)        
+        return self.OnSaveDocument(self._documentFile)
 
 
 class ChildDocTemplate(wx.lib.docview.DocTemplate):
@@ -2758,7 +2812,7 @@ class ChildDocTemplate(wx.lib.docview.DocTemplate):
     that represents a portion of a Document.  The child document is managed by the parent document,
     so it will be prompted to close if its parent is closed, etc.  Child Documents are useful
     when there are complicated  Views of a Document and users will need to tunnel into the View.
-    """    
+    """
 
 
     def __init__(self, manager, description, filter, dir, ext, docTypeName, viewTypeName, docType, viewType, flags=wx.lib.docview.TEMPLATE_INVISIBLE, icon=None):
@@ -2794,23 +2848,24 @@ class WindowMenuService(DocService):
     by the DocSDIFrame.  The MDIFrame automatically includes a Window menu and does not use
     the WindowMenuService.
     """
-    
+
+    #----------------------------------------------------------------------------
+    # Constants
+    #----------------------------------------------------------------------------
+    ARRANGE_WINDOWS_ID = wx.NewId()
+    SELECT_MORE_WINDOWS_ID = wx.NewId()
+    SELECT_NEXT_WINDOW_ID = wx.NewId()
+    SELECT_PREV_WINDOW_ID = wx.NewId()
+    CLOSE_CURRENT_WINDOW_ID = wx.NewId()
+
 
     def __init__(self):
         """
         Initializes the WindowMenu and its globals.
         """
-        self.ARRANGE_WINDOWS_ID = wx.NewId()
-        self.SELECT_WINDOW_1_ID = wx.NewId()
-        self.SELECT_WINDOW_2_ID = wx.NewId()
-        self.SELECT_WINDOW_3_ID = wx.NewId()
-        self.SELECT_WINDOW_4_ID = wx.NewId()
-        self.SELECT_WINDOW_5_ID = wx.NewId()
-        self.SELECT_WINDOW_6_ID = wx.NewId()
-        self.SELECT_WINDOW_7_ID = wx.NewId()
-        self.SELECT_WINDOW_8_ID = wx.NewId()
-        self.SELECT_WINDOW_9_ID = wx.NewId()
-        self.SELECT_MORE_WINDOWS_ID = wx.NewId()
+        self._selectWinIds = []
+        for i in range(0, 9):
+            self._selectWinIds.append(wx.NewId())
 
 
     def InstallControls(self, frame, menuBar=None, toolBar=None, statusBar=None, document=None):
@@ -2818,35 +2873,56 @@ class WindowMenuService(DocService):
         Installs the Window menu.
         """
 
-        if not self.GetDocumentManager().GetFlags() & wx.lib.docview.DOC_SDI:
-            return  # Only need windows menu for SDI mode, MDI frame automatically creates one
-
-        if not _WINDOWS:  # Arrange All and window navigation doesn't work on Linux
-            return
+        windowMenu = None
+        if hasattr(frame, "GetWindowMenu"):
+            windowMenu = frame.GetWindowMenu()
+        if not windowMenu:
+            needWindowMenu = True
+            windowMenu = wx.Menu()
+        else:
+            needWindowMenu = False
             
-        windowMenu = wx.Menu()
-        item = windowMenu.Append(self.ARRANGE_WINDOWS_ID, _("&Arrange All"), _("Arrange the open windows"))
-        windowMenu.AppendSeparator()
-
-        wx.EVT_MENU(frame, self.ARRANGE_WINDOWS_ID, frame.ProcessEvent)
-        wx.EVT_UPDATE_UI(frame, self.ARRANGE_WINDOWS_ID, frame.ProcessUpdateUIEvent)
-        wx.EVT_MENU(frame, self.SELECT_WINDOW_1_ID, frame.ProcessEvent)  # wxNewId may have been nonsequential, so can't use EVT_MENU_RANGE
-        wx.EVT_MENU(frame, self.SELECT_WINDOW_2_ID, frame.ProcessEvent)
-        wx.EVT_MENU(frame, self.SELECT_WINDOW_3_ID, frame.ProcessEvent)
-        wx.EVT_MENU(frame, self.SELECT_WINDOW_4_ID, frame.ProcessEvent)
-        wx.EVT_MENU(frame, self.SELECT_WINDOW_5_ID, frame.ProcessEvent)
-        wx.EVT_MENU(frame, self.SELECT_WINDOW_6_ID, frame.ProcessEvent)
-        wx.EVT_MENU(frame, self.SELECT_WINDOW_7_ID, frame.ProcessEvent)
-        wx.EVT_MENU(frame, self.SELECT_WINDOW_8_ID, frame.ProcessEvent)
-        wx.EVT_MENU(frame, self.SELECT_WINDOW_9_ID, frame.ProcessEvent)
-        wx.EVT_MENU(frame, self.SELECT_MORE_WINDOWS_ID, frame.ProcessEvent)
-
-        helpMenuIndex = menuBar.FindMenu(_("&Help"))
-        menuBar.Insert(helpMenuIndex, windowMenu, _("&Window"))
+        if self.GetDocumentManager().GetFlags() & wx.lib.docview.DOC_SDI:
+            if not _WINDOWS:  # Arrange All and window navigation doesn't work on Linux
+                return
+                
+            item = windowMenu.Append(self.ARRANGE_WINDOWS_ID, _("&Arrange All"), _("Arrange the open windows"))
+            wx.EVT_MENU(frame, self.ARRANGE_WINDOWS_ID, frame.ProcessEvent)
+            wx.EVT_UPDATE_UI(frame, self.ARRANGE_WINDOWS_ID, frame.ProcessUpdateUIEvent)
+            windowMenu.AppendSeparator()
+
+            for i, id in enumerate(self._selectWinIds):
+                wx.EVT_MENU(frame, id, frame.ProcessEvent)
+            wx.EVT_MENU(frame, self.SELECT_MORE_WINDOWS_ID, frame.ProcessEvent)
+        elif wx.GetApp().GetUseTabbedMDI():
+            item = windowMenu.Append(self.SELECT_PREV_WINDOW_ID, _("Previous"), _("Previous Tab"))
+            wx.EVT_MENU(frame, self.SELECT_PREV_WINDOW_ID, frame.ProcessEvent)
+            wx.EVT_UPDATE_UI(frame, self.SELECT_PREV_WINDOW_ID, frame.ProcessUpdateUIEvent)
+            item = windowMenu.Append(self.SELECT_NEXT_WINDOW_ID, _("Next"), _("Next Tab"))
+            wx.EVT_MENU(frame, self.SELECT_NEXT_WINDOW_ID, frame.ProcessEvent)
+            wx.EVT_UPDATE_UI(frame, self.SELECT_NEXT_WINDOW_ID, frame.ProcessUpdateUIEvent)
+            item = windowMenu.Append(self.CLOSE_CURRENT_WINDOW_ID, _("Close Current\tCtrl+F4"), _("Close Current Tab"))
+            wx.EVT_MENU(frame, self.CLOSE_CURRENT_WINDOW_ID, frame.ProcessEvent)
+            wx.EVT_UPDATE_UI(frame, self.CLOSE_CURRENT_WINDOW_ID, frame.ProcessUpdateUIEvent)
+            self._sep = None
+
+            for i, id in enumerate(self._selectWinIds):
+                wx.EVT_MENU(frame, id, self.OnCtrlKeySelect)
+
+        if needWindowMenu:
+            helpMenuIndex = menuBar.FindMenu(_("&Help"))
+            menuBar.Insert(helpMenuIndex, windowMenu, _("&Window"))
 
         self._lastFrameUpdated = None
 
 
+    def OnCtrlKeySelect(self, event):
+        i = self._selectWinIds.index(event.GetId())
+        notebook = wx.GetApp().GetTopWindow()._notebook
+        if i < notebook.GetPageCount():
+            notebook.SetSelection(i)
+
+
     def ProcessEvent(self, event):
         """
         Processes a Window menu event.
@@ -2858,9 +2934,27 @@ class WindowMenuService(DocService):
         elif id == self.SELECT_MORE_WINDOWS_ID:
             self.OnSelectMoreWindows(event)
             return True
-        elif id == self.SELECT_WINDOW_1_ID or id == self.SELECT_WINDOW_2_ID or id == self.SELECT_WINDOW_3_ID or id == self.SELECT_WINDOW_4_ID or id == self.SELECT_WINDOW_5_ID or id == self.SELECT_WINDOW_6_ID or id == self.SELECT_WINDOW_7_ID or id == self.SELECT_WINDOW_8_ID or id == self.SELECT_WINDOW_9_ID:
+        elif id in self._selectWinIds:
             self.OnSelectWindowMenu(event)
             return True
+        elif wx.GetApp().GetUseTabbedMDI():
+            if id == self.SELECT_NEXT_WINDOW_ID:
+                notebook = wx.GetApp().GetTopWindow()._notebook
+                i = notebook.GetSelection()
+                notebook.SetSelection(i+1)
+                return True
+            elif id == self.SELECT_PREV_WINDOW_ID:
+                notebook = wx.GetApp().GetTopWindow()._notebook
+                i = notebook.GetSelection()
+                notebook.SetSelection(i-1)
+                return True
+            elif id == self.CLOSE_CURRENT_WINDOW_ID:
+                notebook = wx.GetApp().GetTopWindow()._notebook
+                i = notebook.GetSelection()
+                if i != -1:
+                    doc = notebook.GetPage(i).GetView().GetDocument()
+                    wx.GetApp().GetDocumentManager().CloseDocument(doc, False)
+                return True
         else:
             return False
 
@@ -2876,6 +2970,38 @@ class WindowMenuService(DocService):
                 self.BuildWindowMenu(frame)  # It's a new frame, so update the windows menu... this is as if the View::OnActivateMethod had been invoked
                 self._lastFrameUpdated = frame
             return True
+        elif wx.GetApp().GetUseTabbedMDI():
+            if id == self.SELECT_NEXT_WINDOW_ID:
+                self.BuildWindowMenu(event.GetEventObject())  # build file list only when we are updating the windows menu
+                
+                notebook = wx.GetApp().GetTopWindow()._notebook
+                i = notebook.GetSelection()
+                if i == -1:
+                    event.Enable(False)
+                    return True
+                i += 1
+                if i >= notebook.GetPageCount():
+                    event.Enable(False)
+                    return True
+                event.Enable(True)
+                return True
+            elif id == self.SELECT_PREV_WINDOW_ID:
+                notebook = wx.GetApp().GetTopWindow()._notebook
+                i = notebook.GetSelection()
+                if i == -1:
+                    event.Enable(False)
+                    return True
+                i -= 1
+                if i < 0:
+                    event.Enable(False)
+                    return True
+                event.Enable(True)
+                return True
+            elif id == self.CLOSE_CURRENT_WINDOW_ID:
+                event.Enable(wx.GetApp().GetTopWindow()._notebook.GetSelection() != -1)
+                return True
+
+            return False
         else:
             return False
 
@@ -2884,42 +3010,70 @@ class WindowMenuService(DocService):
         """
         Builds the Window menu and adds menu items for all of the open documents in the DocManager.
         """
+        if wx.GetApp().GetUseTabbedMDI():
+            currentFrame = wx.GetApp().GetTopWindow()
+
         windowMenuIndex = currentFrame.GetMenuBar().FindMenu(_("&Window"))
         windowMenu = currentFrame.GetMenuBar().GetMenu(windowMenuIndex)
-        ids = self._GetWindowMenuIDList()
-        frames = self._GetWindowMenuFrameList(currentFrame)
-        max = WINDOW_MENU_NUM_ITEMS
-        if max > len(frames):
-            max = len(frames)
-        i = 0
-        for i in range(0, max):
-            frame = frames[i]
-            item = windowMenu.FindItemById(ids[i])
-            label = '&' + str(i + 1) + ' ' + frame.GetTitle()
-            if not item:
-                item = windowMenu.AppendCheckItem(ids[i], label)
-            else:
-                windowMenu.SetLabel(ids[i], label)
-            windowMenu.Check(ids[i], (frame == currentFrame))
-        if len(frames) > WINDOW_MENU_NUM_ITEMS:  # Add the more items item
-            if not windowMenu.FindItemById(self.SELECT_MORE_WINDOWS_ID):
-                windowMenu.Append(self.SELECT_MORE_WINDOWS_ID, _("&More Windows..."))
-        else:  # Remove any extra items
-            if windowMenu.FindItemById(self.SELECT_MORE_WINDOWS_ID):
-                windowMenu.Remove(self.SELECT_MORE_WINDOWS_ID)
-
-
 
-            for j in range(i + 1, WINDOW_MENU_NUM_ITEMS):
-                if windowMenu.FindItemById(ids[j]):
-                    windowMenu.Remove(ids[j])
+        if self.GetDocumentManager().GetFlags() & wx.lib.docview.DOC_SDI:
+            frames = self._GetWindowMenuFrameList(currentFrame)
+            max = WINDOW_MENU_NUM_ITEMS
+            if max > len(frames):
+                max = len(frames)
+            i = 0
+            for i in range(0, max):
+                frame = frames[i]
+                item = windowMenu.FindItemById(self._selectWinIds[i])
+                label = '&' + str(i + 1) + ' ' + frame.GetTitle()
+                if not item:
+                    item = windowMenu.AppendCheckItem(self._selectWinIds[i], label)
+                else:
+                    windowMenu.SetLabel(self._selectWinIds[i], label)
+                windowMenu.Check(self._selectWinIds[i], (frame == currentFrame))
+            if len(frames) > WINDOW_MENU_NUM_ITEMS:  # Add the more items item
+                if not windowMenu.FindItemById(self.SELECT_MORE_WINDOWS_ID):
+                    windowMenu.Append(self.SELECT_MORE_WINDOWS_ID, _("&More Windows..."))
+            else:  # Remove any extra items
+                if windowMenu.FindItemById(self.SELECT_MORE_WINDOWS_ID):
+                    windowMenu.Remove(self.SELECT_MORE_WINDOWS_ID)
+
+                for j in range(i + 1, WINDOW_MENU_NUM_ITEMS):
+                    if windowMenu.FindItemById(self._selectWinIds[j]):
+                        windowMenu.Remove(self._selectWinIds[j])
+
+        elif wx.GetApp().GetUseTabbedMDI():
+            notebook = wx.GetApp().GetTopWindow()._notebook
+            numPages = notebook.GetPageCount()
+
+            for id in self._selectWinIds:
+                item = windowMenu.FindItemById(id)
+                if item:
+                    windowMenu.DeleteItem(item)
+            if numPages == 0 and self._sep:
+                windowMenu.DeleteItem(self._sep)
+                self._sep = None
+
+            if numPages > len(self._selectWinIds):
+                for i in range(len(self._selectWinIds), numPages):
+                    self._selectWinIds.append(wx.NewId())
+                    wx.EVT_MENU(currentFrame, self._selectWinIds[i], self.OnCtrlKeySelect)                    
+            
+            for i in range(0, numPages):
+                if i == 0 and not self._sep:
+                    self._sep = windowMenu.AppendSeparator()
+                if i < 9:
+                    menuLabel = "%s\tCtrl+%s" % (notebook.GetPageText(i), i+1)
+                else:
+                    menuLabel = notebook.GetPageText(i)
+                windowMenu.Append(self._selectWinIds[i], menuLabel)
 
 
     def _GetWindowMenuIDList(self):
         """
         Returns a list of the Window menu item IDs.
         """
-        return [self.SELECT_WINDOW_1_ID, self.SELECT_WINDOW_2_ID, self.SELECT_WINDOW_3_ID, self.SELECT_WINDOW_4_ID, self.SELECT_WINDOW_5_ID, self.SELECT_WINDOW_6_ID, self.SELECT_WINDOW_7_ID, self.SELECT_WINDOW_8_ID, self.SELECT_WINDOW_9_ID]
+        return self._selectWinIds
 
 
     def _GetWindowMenuFrameList(self, currentFrame=None):
@@ -2988,7 +3142,7 @@ class WindowMenuService(DocService):
         Frame to the front of the desktop.
         """
         id = event.GetId()
-        index = self._GetWindowMenuIDList().index(id)
+        index = self._selectWinIds.index(id)
         if index > -1:
             currentFrame = event.GetEventObject()
             frame = self._GetWindowMenuFrameList(currentFrame)[index]
@@ -3036,7 +3190,7 @@ def getNewData():
 \x902\xb2@\xc8\xc2\x8b\xd9\xbcX\xc0\x045\xac\xc1 Jg\xe6\x08\xe8)\xa7o\xd5\
 \xb0\xbf\xcb\nd\x86x\x0b\x9c+p\x0b\x0c\xa9\x16~\xbc_\xeb\x9d\xd3\x03\xcb3q\
 \xefo\xbc\xfa/\x14\xd9\x19\x1f\xfb\x8aa\x87\xf2\xf7\x16\x00\x00\x00\x00IEND\
-\xaeB`\x82" 
+\xaeB`\x82"
 
 def getNewBitmap():
     return BitmapFromImage(getNewImage())
@@ -3064,7 +3218,7 @@ D\xee\xf4\x88\xb2\xfa5)\xab(\x99"\x00\xb9\x87c\x0b;\x19\xf1\x0b\x80\xb9pZ\
 \xad\xb1\xab\x99\x98bdb\xd4q\xa7\xefd\xbb\x05\xa7\xdd\x8f\x0e/\x9d\x01\x85\
 \xbc\nX+8K\\\x99\xe5\x02x\x16\xf6\xba\x02$\xc9\xe56\x1fF[\xda\x8bn\x9er\xa7\
 \x02\xc1\x90\xedoH\xed\xdf\x18\x8fE\xc5o\x0c\x8e\x80\xbf\xea\x13\xa8\x18\x89\
-5\xe7L\xb3:\x00\x00\x00\x00IEND\xaeB`\x82' 
+5\xe7L\xb3:\x00\x00\x00\x00IEND\xaeB`\x82'
 
 def getOpenBitmap():
     return BitmapFromImage(getOpenImage())
@@ -3091,7 +3245,7 @@ def getCopyData():
 \xb9\xe7\x1fE\xae\xb7\\\xb6\x1f\xe0\x8d\x15H$\x99\x1b?\x12@\xd7\xdf\xd0\x0f\
 \nN!\x91\x98\x9e\xd8\x0c\x10\xbd>\xdeU\xeco\np\xf7\xf8\xebK\x14fvF\xc8ds\xce\
 \xff\xbd\xb6u(\xbc\x89\xbc\x17\xf6\x9f\x14E\x8d\x04\x8a\xdeDa\xcads\xca\x1f\
-\x0cI\xd4\xda\x88E\x9d\xc4\x00\x00\x00\x00IEND\xaeB`\x82' 
+\x0cI\xd4\xda\x88E\x9d\xc4\x00\x00\x00\x00IEND\xaeB`\x82'
 
 def getCopyBitmap():
     return BitmapFromImage(getCopyImage())
@@ -3120,7 +3274,7 @@ pz\x8d\x82\x12\x0b\x82\xc1HM\xect-c\xf7\xaa!\x10\xc9\xe0]rR\xac\xb4\x01\xc8\
 \xb0\x98\x1f\x00x-\xd5\xb0\xce\xc3\xd1~LW\x98\x15\xab\xccM\x8f\xfe\xaf\x03\
 \x00w0\xccS\xfdgm\xfb\xc3\xd7\xf7++w\xd5\x16\x0f\x92\t\xe4\xe9zN\x86\xbe\xa7\
 1\xaa\xfbLY\xb1:\x10 (\xe3\x0c?\x03\xf2_\xb9W=\xc2\x17\x1c\xf8\x87\x9a\x03\
-\x12\xd7\xb9\x00\x00\x00\x00IEND\xaeB`\x82" 
+\x12\xd7\xb9\x00\x00\x00\x00IEND\xaeB`\x82"
 
 def getPasteBitmap():
     return BitmapFromImage(getPasteImage())
@@ -3146,7 +3300,7 @@ def getSaveData():
 g\x1f!U\xac\xe0y^\xe62\xc6p\xd6h\x14\x8e4s\x89\xc6\xa4\xcb[\xa9V\xffG\xa0\
 \xb5\xce\x8a\x97j[\xb4\xe3\xb8\x90@)\'\xfd\xbe\xd7\xf5\xe2\x83\xeau\xec~w\'\
 \x9a\x12\x00\\6\xc3\xd2\xab,\xec`^|\x03\xb6\xdf|Q.\xa7\x15\x89\x00\x00\x00\
-\x00IEND\xaeB`\x82' 
+\x00IEND\xaeB`\x82'
 
 def getSaveBitmap():
     return BitmapFromImage(getSaveImage())
@@ -3173,7 +3327,7 @@ def getSaveAllData():
 \xa9&\xb3\x86c\xd3r![\xe47\x14 |\x14\xcf\xb7\x13JNZ7\xab\xc2\xe9\xddn7\x9e\
 \xbb>\xcb\x01\x98\xc9\xa0T\x93Y\x93\xdbH\xa2\xaa*4MC\xb5Z\xcdt \x84\x98\xfa(\
 S\xf2\xf9\xfc\xdc+0&\xc9\xa9\xc1\x86\xf3}\x1d\xbf\r\xacm\x84\xf5\xc2\x02\x00\
-Pw\xefR\x99d\xf1\x05z\x94\xd0b\xcb S\xf3\x00\x00\x00\x00IEND\xaeB`\x82' 
+Pw\xefR\x99d\xf1\x05z\x94\xd0b\xcb S\xf3\x00\x00\x00\x00IEND\xaeB`\x82'
 
 def getSaveAllBitmap():
     return BitmapFromImage(getSaveAllImage())
@@ -3203,7 +3357,7 @@ def getPrintData():
 \xe95n4\xea\x01\xab\x9dN\xc7\xe3"9\x1fGr>\xeeYs\x8fr:\x9d\x06c\x0c\x86ax\nL\
 \xcb;\xbb\x1f\x84\xd0\x10*\xe5\x12WU\x15\xcd7`f\xf2\xc7z\x00\x80\xae\xeb\xc8\
 \xe5rXI\xad\x12"nc\xa5\\\xe2{G*\xba\xef\xfa\xaf\x02\xa2\xd9u \xe0?\xe7\xdfA4\
-\x03\xc0\'\xe3\x82\xc9\x18g\x90\x8e]\x00\x00\x00\x00IEND\xaeB`\x82' 
+\x03\xc0\'\xe3\x82\xc9\x18g\x90\x8e]\x00\x00\x00\x00IEND\xaeB`\x82'
 
 def getPrintBitmap():
     return BitmapFromImage(getPrintImage())
@@ -3230,7 +3384,7 @@ pz\x0e\xa2~\x91\x0bx\x00-m\xe9D-W>%h\xc0\x1f_\xbf\x15\xef\xeb\x90\xaf\xc1\
 \xf2#u\xc3\xb2m`t\x00&\x07E4\xcb]x.QH\xa6\xec$\x13\xf83q^\xb44^\x8f\xb8\xa5"\
 p\x9c\x88\xa3\x91\xe1\x9d5\x00\x14Eu\xc9y\x9c\xa4\xeb\xba\xe5}\xb6\x9a\x01`\
 \xc1\x07\xf39\x97\xa2(\xaa\xab\x17+\xd5]\xe0\xf5dC\x9a\xfc\xcb\xc0\xc9\xd00\
-\xf9\x011\xc9\x87\xf3\xb4\xd1t\xaf\x00\x00\x00\x00IEND\xaeB`\x82' 
+\xf9\x011\xc9\x87\xf3\xb4\xd1t\xaf\x00\x00\x00\x00IEND\xaeB`\x82'
 
 def getPrintPreviewBitmap():
     return BitmapFromImage(getPrintPreviewImage())
@@ -3257,7 +3411,7 @@ def getCutData():
 \x00,\xa6\x9f\x00\x14o+\xec\x9f\x15X\xba\x97\xf1\tTC\x1c\xfe]e\x80v\xa9\xcc\
 \xb8\xeb2\xfb\xf8\xe2\xf5\xaeA\xbbT\xd6\xea"c\x1c\xf4{r\xfbe\xf5Y?\xa7\xd5\
 \x80W\xd1w\n7k\xa3\xd4\xee\x81\x8a\x18\x16\xea8\x80_\\\xa2\x8b\x88!\xd2S\x08\
-\x00\x00\x00\x00IEND\xaeB`\x82' 
+\x00\x00\x00\x00IEND\xaeB`\x82'
 
 def getCutBitmap():
     return BitmapFromImage(getCutImage())
@@ -3288,7 +3442,7 @@ def getUndoData():
 \x06#\x13\x0c}\x1a\x06 \xdc\xfc\xc87\xf0?\xb8\x1e\xc1\n\xa1\xac\x10Zk\xe9\
 \x18k\x95\x9fGS\xf2\xa58*\x9f7S\xd2\x92\x0c\x8b\xd6Z\xccL\xd0\xf6\x1d\xb4\
 \xd6\xd2\x92\x0c\xcb\xea\xdf\x0f\r\xc1w\x047%\x8d\xc0\x81\x02#i\x04VV\x88k\
-\x82\xbe\xde\xc2\xb0\xb2\xea\xa7\x00\x00\x00\x00IEND\xaeB`\x82" 
+\x82\xbe\xde\xc2\xb0\xb2\xea\xa7\x00\x00\x00\x00IEND\xaeB`\x82"
 
 def getUndoBitmap():
     return BitmapFromImage(getUndoImage())
@@ -3317,7 +3471,7 @@ Eqc~S\xec\xd7\x94\x18\xaa\xafY*e^l\x10\x87\xf5\xb4,W\xb1<\x98\x16q\x98W\xa1\
 \xbd\xd1\xfe\x10=\xfc\xe8\x1eg\x91\xbc\xfc\x06\x81\xa0\xc2\xd2\x13\xa789\xbe\
 \x91\xde\xce\x14\x07\x82\nC\xaf\xeb\xd6\xe0\x9c\x93/9Lj%L\xa9\xf2\x1c\xa5\
 \xcas\xe4\r\xb9m\xaf\xf0'\xc0\x84xCnR+\xe1_\xe2\xbe\x00V\x88\xec\x9f\xf4\x05\
-0!\xb2\xfc\x0f\xe0\xc4\xb6\xad\x97R\xe5z\x00\x00\x00\x00IEND\xaeB`\x82" 
+0!\xb2\xfc\x0f\xe0\xc4\xb6\xad\x97R\xe5z\x00\x00\x00\x00IEND\xaeB`\x82"
 
 def getRedoBitmap():
     return BitmapFromImage(getRedoImage())
@@ -3325,7 +3479,7 @@ def getRedoBitmap():
 def getRedoImage():
     stream = cStringIO.StringIO(getRedoData())
     return ImageFromStream(stream)
-    
+
 #----------------------------------------------------------------------------
 
 def getBlankData():
@@ -3336,7 +3490,7 @@ def getBlankData():
 \xa8X:\xd4\x13\x03:\x1b\x01\xa45T\xd4\xefBsh\xd7Hk\xdc\x02\x00@\x8a\x19$\xa1\
 9\x14A,\x95\xf3\x82G)\xd3\x00\xf24\xf7\x90\x1ev\x07\xee\x1e\xf4:\xc1J?\xe0\
 \x0b\x80\xc7\x1d\xf8\x1dg\xc4\xea7\x96G8\x00\xa8\x91\x19(\x85#P\x7f\x00\x00\
-\x00\x00IEND\xaeB`\x82' 
+\x00\x00IEND\xaeB`\x82'
 
 
 def getBlankBitmap():
@@ -3348,5 +3502,5 @@ def getBlankImage():
 
 def getBlankIcon():
     return wx.IconFromBitmap(getBlankBitmap())
-    
+