From defe9154819fc8734a4ea5e44d70eface28436f7 2008-06-24 07:02:30 From: Barry Wark Date: 2008-06-24 07:02:30 Subject: [PATCH] Merged ipython-frontend branch. All changes in IPython.frontend except for updates to IPython.kernel.engineservice.ThreadedEngineService and associated tests. --- diff --git a/IPython/frontend/__init__.py b/IPython/frontend/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/IPython/frontend/__init__.py diff --git a/IPython/frontend/cocoa/.DS_Store b/IPython/frontend/cocoa/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..f3571106fff38a8ee1ba7107a78f46b26eb6745c GIT binary patch literal 6148 zc%1E+K~BRk5Jmqa5sJjd%PxHcxPeub6Z8U5T4<%vAfmRcP<9-KTX7PS_zOi!D#5C# z{3ls&F=KnOu{{A`)7|nKZ~$^d;~k^q90L+MZH9GgJfpx0)x%+PHNMt>v|D_ISKQ&L zKYvk_i>|OGCiaQI#1$0qhM9^bF4b@|ei`S!!Qgb+fAb0yxU$S+S@ zh7jTp(w+~V4s;!Dj3;!~o8xFt2fB_n<|TAS=d`0e9q2lmhnBJVFz#&ZP|VSu4!`str(value)) + waitingForEngine = objc.ivar().bool() + textView = objc.IBOutlet() + + def init(self): + self = super(IPythonCocoaController, self).init() + FrontEndBase.__init__(self, engine=ThreadedEngineService()) + if(self != None): + self._common_init() + + return self + + def _common_init(self): + """_common_init""" + + self.userNS = NSMutableDictionary.dictionary() + self.waitingForEngine = False + + self.lines = {} + self.tabSpaces = 4 + self.tabUsesSpaces = True + self.currentBlockID = self.next_block_ID() + self.blockRanges = {} # blockID=>NSRange + + + def awakeFromNib(self): + """awakeFromNib""" + + self._common_init() + + # Start the IPython engine + self.engine.startService() + NSLog('IPython engine started') + + # Register for app termination + nc = NSNotificationCenter.defaultCenter() + nc.addObserver_selector_name_object_( + self, + 'appWillTerminate:', + NSApplicationWillTerminateNotification, + None) + + self.textView.setDelegate_(self) + self.textView.enclosingScrollView().setHasVerticalRuler_(True) + r = NSRulerView.alloc().initWithScrollView_orientation_( + self.textView.enclosingScrollView(), + NSVerticalRuler) + self.verticalRulerView = r + self.verticalRulerView.setClientView_(self.textView) + self._start_cli_banner() + + + def appWillTerminate_(self, notification): + """appWillTerminate""" + + self.engine.stopService() + + + def complete(self, token): + """Complete token in engine's user_ns + + Parameters + ---------- + token : string + + Result + ------ + Deferred result of + IPython.kernel.engineservice.IEngineBase.complete + """ + + return self.engine.complete(token) + + + def execute(self, block, blockID=None): + self.waitingForEngine = True + self.willChangeValueForKey_('commandHistory') + d = super(IPythonCocoaController, self).execute(block, blockID) + d.addBoth(self._engine_done) + d.addCallback(self._update_user_ns) + + return d + + + def _engine_done(self, x): + self.waitingForEngine = False + self.didChangeValueForKey_('commandHistory') + return x + + def _update_user_ns(self, result): + """Update self.userNS from self.engine's namespace""" + d = self.engine.keys() + d.addCallback(self._get_engine_namespace_values_for_keys) + + return result + + + def _get_engine_namespace_values_for_keys(self, keys): + d = self.engine.pull(keys) + d.addCallback(self._store_engine_namespace_values, keys=keys) + + + def _store_engine_namespace_values(self, values, keys=[]): + assert(len(values) == len(keys)) + self.willChangeValueForKey_('userNS') + for (k,v) in zip(keys,values): + self.userNS[k] = saferepr(v) + self.didChangeValueForKey_('userNS') + + + def update_cell_prompt(self, result): + if(isinstance(result, Failure)): + blockID = result.blockID + else: + blockID = result['blockID'] + + + self.insert_text(self.input_prompt(result=result), + textRange=NSMakeRange(self.blockRanges[blockID].location,0), + scrollToVisible=False + ) + + return result + + + def render_result(self, result): + blockID = result['blockID'] + inputRange = self.blockRanges[blockID] + del self.blockRanges[blockID] + + #print inputRange,self.current_block_range() + self.insert_text('\n' + + self.output_prompt(result) + + result.get('display',{}).get('pprint','') + + '\n\n', + textRange=NSMakeRange(inputRange.location+inputRange.length, + 0)) + return result + + + def render_error(self, failure): + self.insert_text('\n\n'+str(failure)+'\n\n') + self.start_new_block() + return failure + + + def _start_cli_banner(self): + """Print banner""" + + banner = """IPython1 %s -- An enhanced Interactive Python.""" % \ + IPython.__version__ + + self.insert_text(banner + '\n\n') + + + def start_new_block(self): + """""" + + self.currentBlockID = self.next_block_ID() + + + + def next_block_ID(self): + + return uuid.uuid4() + + def current_block_range(self): + return self.blockRanges.get(self.currentBlockID, + NSMakeRange(self.textView.textStorage().length(), + 0)) + + def current_block(self): + """The current block's text""" + + return self.text_for_range(self.current_block_range()) + + def text_for_range(self, textRange): + """text_for_range""" + + ts = self.textView.textStorage() + return ts.string().substringWithRange_(textRange) + + def current_line(self): + block = self.text_for_range(self.current_block_range()) + block = block.split('\n') + return block[-1] + + + def insert_text(self, string=None, textRange=None, scrollToVisible=True): + """Insert text into textView at textRange, updating blockRanges + as necessary + """ + + if(textRange == None): + #range for end of text + textRange = NSMakeRange(self.textView.textStorage().length(), 0) + + for r in self.blockRanges.itervalues(): + intersection = NSIntersectionRange(r,textRange) + if(intersection.length == 0): #ranges don't intersect + if r.location >= textRange.location: + r.location += len(string) + else: #ranges intersect + if(r.location <= textRange.location): + assert(intersection.length == textRange.length) + r.length += textRange.length + else: + r.location += intersection.length + + self.textView.replaceCharactersInRange_withString_( + textRange, string) + self.textView.setSelectedRange_( + NSMakeRange(textRange.location+len(string), 0)) + if(scrollToVisible): + self.textView.scrollRangeToVisible_(textRange) + + + + + def replace_current_block_with_string(self, textView, string): + textView.replaceCharactersInRange_withString_( + self.current_block_range(), + string) + self.current_block_range().length = len(string) + r = NSMakeRange(textView.textStorage().length(), 0) + textView.scrollRangeToVisible_(r) + textView.setSelectedRange_(r) + + + def current_indent_string(self): + """returns string for indent or None if no indent""" + + if(len(self.current_block()) > 0): + lines = self.current_block().split('\n') + currentIndent = len(lines[-1]) - len(lines[-1]) + if(currentIndent == 0): + currentIndent = self.tabSpaces + + if(self.tabUsesSpaces): + result = ' ' * currentIndent + else: + result = '\t' * (currentIndent/self.tabSpaces) + else: + result = None + + return result + + + # NSTextView delegate methods... + def textView_doCommandBySelector_(self, textView, selector): + assert(textView == self.textView) + NSLog("textView_doCommandBySelector_: "+selector) + + + if(selector == 'insertNewline:'): + indent = self.current_indent_string() + if(indent): + line = indent + self.current_line() + else: + line = self.current_line() + + if(self.is_complete(self.current_block())): + self.execute(self.current_block(), + blockID=self.currentBlockID) + self.start_new_block() + + return True + + return False + + elif(selector == 'moveUp:'): + prevBlock = self.get_history_previous(self.current_block()) + if(prevBlock != None): + self.replace_current_block_with_string(textView, prevBlock) + else: + NSBeep() + return True + + elif(selector == 'moveDown:'): + nextBlock = self.get_history_next() + if(nextBlock != None): + self.replace_current_block_with_string(textView, nextBlock) + else: + NSBeep() + return True + + elif(selector == 'moveToBeginningOfParagraph:'): + textView.setSelectedRange_(NSMakeRange( + self.current_block_range().location, + 0)) + return True + elif(selector == 'moveToEndOfParagraph:'): + textView.setSelectedRange_(NSMakeRange( + self.current_block_range().location + \ + self.current_block_range().length, 0)) + return True + elif(selector == 'deleteToEndOfParagraph:'): + if(textView.selectedRange().location <= \ + self.current_block_range().location): + # Intersect the selected range with the current line range + if(self.current_block_range().length < 0): + self.blockRanges[self.currentBlockID].length = 0 + + r = NSIntersectionRange(textView.rangesForUserTextChange()[0], + self.current_block_range()) + + if(r.length > 0): #no intersection + textView.setSelectedRange_(r) + + return False # don't actually handle the delete + + elif(selector == 'insertTab:'): + if(len(self.current_line().strip()) == 0): #only white space + return False + else: + self.textView.complete_(self) + return True + + elif(selector == 'deleteBackward:'): + #if we're at the beginning of the current block, ignore + if(textView.selectedRange().location == \ + self.current_block_range().location): + return True + else: + self.current_block_range().length-=1 + return False + return False + + + def textView_shouldChangeTextInRanges_replacementStrings_(self, + textView, ranges, replacementStrings): + """ + Delegate method for NSTextView. + + Refuse change text in ranges not at end, but make those changes at + end. + """ + + assert(len(ranges) == len(replacementStrings)) + allow = True + for r,s in zip(ranges, replacementStrings): + r = r.rangeValue() + if(textView.textStorage().length() > 0 and + r.location < self.current_block_range().location): + self.insert_text(s) + allow = False + + + self.blockRanges.setdefault(self.currentBlockID, + self.current_block_range()).length +=\ + len(s) + + return allow + + def textView_completions_forPartialWordRange_indexOfSelectedItem_(self, + textView, words, charRange, index): + try: + ts = textView.textStorage() + token = ts.string().substringWithRange_(charRange) + completions = blockingCallFromThread(self.complete, token) + except: + completions = objc.nil + NSBeep() + + return (completions,0) + + diff --git a/IPython/frontend/cocoa/examples/.DS_Store b/IPython/frontend/cocoa/examples/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..33fb2cc3638425cba746b55723dba38a5c9cbc37 GIT binary patch literal 6148 zc%1E+O-{ow5Jo?{L5jqpUb6HN=nbr@EV@JL1t5(em7-W`AcYOej>B+CV*HgKrHHah zq4Ol`CGyyDW)XV=Kuz!S319%Eh*sfZ2Ps<*O($XS+Z+WvDon7z_3pV2jrLsa+b;5DpXjEj z3%~Ai|1@0w1VIplzen7Zke&{^3_%bMknnx5w4>W-#ePjEpT-#4(vEJU72`CW&?#+b zOFO!a=0{7h_)vWEws=v7wzT`zD+|KWFp4r^|G)UY{ucz{AET1lVm8HlLC_=YwYFO`=QZPnjw0ZPB-q2!=C)D^n-)qEBJ3)mHy!KV6^b`wGfA;eFZt9ZQaYX~9!AjAF;=|p#KAAOVmNDKAJ5F$ aeHUK12xR`laEfDIq!Zn_br}%i7VH55gce!= literal 0 Hc$@ + + + 1050 + 9D34 + 629 + 949.33 + 352.00 + + YES + + + + + YES + com.apple.InterfaceBuilderKit + com.apple.InterfaceBuilder.CocoaPlugin + + + YES + + NSApplication + + + FirstResponder + + + + + + AMainMenu + + YES + + + IPython1Sandbox + + 1048576 + 2147483647 + + NSImage + NSMenuCheckmark + + + + NSMenuMixedState + + submenuAction: + + + + YES + + + About IPython1Sandbox + + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + UHJlZmVyZW5jZXPigKY + , + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Services + + 1048576 + 2147483647 + + + submenuAction: + + + + YES + + _NSServicesMenu + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Hide IPython1Sandbox + h + 1048576 + 2147483647 + + + + + + Hide Others + + 1572864 + 2147483647 + + + + + + Show All + + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Quit IPython1Sandbox + q + 1048576 + 2147483647 + + + + + _NSAppleMenu + + + + + File + + 1048576 + 2147483647 + + + submenuAction: + + + + YES + + + New + n + 1048576 + 2147483647 + + + + + + T3BlbuKApg + o + 1048576 + 2147483647 + + + + + + Open Recent + + 1048576 + 2147483647 + + + submenuAction: + + + + YES + + + Clear Menu + + 1048576 + 2147483647 + + + + + _NSRecentDocumentsMenu + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Close + w + 1048576 + 2147483647 + + + + + + Save + s + 1048576 + 2147483647 + + + + + + U2F2ZSBBc+KApg + S + 1179648 + 2147483647 + + + + + + Revert to Saved + + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Page Setup... + P + 1179648 + 2147483647 + + + + + + + UHJpbnTigKY + p + 1048576 + 2147483647 + + + + + + + + + Edit + + 1048576 + 2147483647 + + + submenuAction: + + + + YES + + + Undo + z + 1048576 + 2147483647 + + + + + + Redo + Z + 1179648 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Cut + x + 1048576 + 2147483647 + + + + + + Copy + c + 1048576 + 2147483647 + + + + + + Paste + v + 1048576 + 2147483647 + + + + + + Delete + + 1048576 + 2147483647 + + + + + + Select All + a + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Find + + 1048576 + 2147483647 + + + submenuAction: + + + + YES + + + RmluZOKApg + f + 1048576 + 2147483647 + + + 1 + + + + Find Next + g + 1048576 + 2147483647 + + + 2 + + + + Find Previous + G + 1179648 + 2147483647 + + + 3 + + + + Use Selection for Find + e + 1048576 + 2147483647 + + + 7 + + + + Jump to Selection + j + 1048576 + 2147483647 + + + + + + + + + Spelling and Grammar + + 1048576 + 2147483647 + + + submenuAction: + + + + YES + + + U2hvdyBTcGVsbGluZ+KApg + : + 1048576 + 2147483647 + + + + + + Check Spelling + ; + 1048576 + 2147483647 + + + + + + Check Spelling While Typing + + 1048576 + 2147483647 + + + + + + Check Grammar With Spelling + + 1048576 + 2147483647 + + + + + + + + + Substitutions + + 1048576 + 2147483647 + + + submenuAction: + + + + YES + + + Smart Copy/Paste + + 1048576 + 2147483647 + + + 1 + + + + Smart Quotes + + 1048576 + 2147483647 + + + 2 + + + + Smart Links + + 1179648 + 2147483647 + + + 3 + + + + + + + Speech + + 1048576 + 2147483647 + + + submenuAction: + + + + YES + + + Start Speaking + + 1048576 + 2147483647 + + + + + + Stop Speaking + + 1048576 + 2147483647 + + + + + + + + + + + + Format + + 1048576 + 2147483647 + + + submenuAction: + + + + YES + + + Show Fonts + t + 1048576 + 2147483647 + + + + + + Show Colors + C + 1179648 + 2147483647 + + + + + + + + + View + + 1048576 + 2147483647 + + + submenuAction: + + + + YES + + + Show Toolbar + + 1572864 + 2147483647 + + + + + + Q3VzdG9taXplIFRvb2xiYXLigKY + + 1048576 + 2147483647 + + + + + + + + + Window + + 1048576 + 2147483647 + + + submenuAction: + + + + YES + + + Minimize + m + 1048576 + 2147483647 + + + + + + Zoom + + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Bring All to Front + + 1048576 + 2147483647 + + + + + _NSWindowsMenu + + + + + Help + + 1048576 + 2147483647 + + + submenuAction: + + + + YES + + + IPython1Sandbox Help + ? + 1048576 + 2147483647 + + + + + + + + _NSMainMenu + + + 15 + 2 + {{335, 413}, {725, 337}} + 1946157056 + IPython1 (Cocoa) + NSWindow + + + + 256 + + YES + + + 274 + + YES + + + 22 + + YES + + + 256 + + YES + + + 274 + + YES + + + 2304 + + YES + + + 2322 + + YES + + YES + Apple HTML pasteboard type + Apple PDF pasteboard type + Apple PICT pasteboard type + Apple PNG pasteboard type + Apple URL pasteboard type + CorePasteboardFlavorType 0x6D6F6F76 + CorePasteboardFlavorType 0x75726C20 + NSColor pasteboard type + NSFilenamesPboardType + NSStringPboardType + NeXT Encapsulated PostScript v1.2 pasteboard type + NeXT RTFD pasteboard type + NeXT Rich Text Format v1.0 pasteboard type + NeXT TIFF v4.0 pasteboard type + NeXT font pasteboard type + NeXT ruler pasteboard type + WebURLsWithTitlesPboardType + + + {{0, 38}, {433, 14}} + + + + + + + + + + + + YES + + + 6 + + + + 4.330000e+02 + 1 + + + 346991 + + 2 + MSAwLjk1Mjk0MTI0IDAuODUwOTgwNDYAA + + + 3 + MAA + + + YES + + YES + NSBackgroundColor + NSColor + + + YES + + 6 + System + selectedTextBackgroundColor + + 3 + MC42NjY2NjY2OQA + + + + 6 + + selectedTextColor + + + + + + + YES + + YES + + NSUnderline + + + YES + + 1 + MCAwIDEAA + + + + + + + 6 + {480, 1e+07} + {84, 0} + + + + {{1, 1}, {433, 231}} + {{0, 38}, {433, 231}} + + + + + + 3 + MQA + + + {4, -5} + 1 + + 4 + + + + -2147483392 + {{427, 1}, {15, 263}} + + + + _doScroller: + 3.389175e-01 + + + + 256 + {{-100, -100}, {87, 18}} + + + 1 + + + 1.000000e+00 + 9.456522e-01 + + + {{18, 14}, {435, 233}} + + + + 530 + + + + + + {{1, 1}, {471, 257}} + + + + + {473, 273} + + + {0, 0} + + 67239424 + 0 + Console + + LucidaGrande + 1.100000e+01 + 3100 + + + 6 + + textBackgroundColor + + + + 3 + MCAwLjgwMDAwMDAxAA + + + + 1 + 0 + 2 + NO + + + + 51 + + YES + + + 256 + + YES + + + 274 + + YES + + + 2304 + + YES + + + 256 + {156, 200} + + + YES + + + 256 + {156, 17} + + + + + + + 256 + {{157, 0}, {16, 17}} + + + + + YES + + 7.100000e+01 + 4.000000e+01 + 1.000000e+03 + + 75628032 + 0 + Variable + + + 3 + MC4zMzMzMzI5OQA + + + 6 + + headerTextColor + + + + + 337772096 + 2048 + Text Cell + + + 1.300000e+01 + 1044 + + + + 6 + + controlBackgroundColor + + + + 6 + + controlTextColor + + + + 3 + YES + YES + + + + 7.900000e+01 + 4.000000e+01 + 1.000000e+03 + + 75628032 + 0 + Value + + + + + + 337772096 + 2048 + + + + + + + 3 + YES + YES + + + + 3.000000e+00 + 2.000000e+00 + + + 6 + + gridColor + + 3 + MC41AA + + + 1.700000e+01 + -692060160 + 1 + 4 + 15 + 0 + YES + + + {{1, 17}, {156, 200}} + + + + + + 4 + + + + 256 + {{157, 17}, {15, 200}} + + + + + 9.961240e-01 + + + + 256 + {{1, 217}, {156, 15}} + + + 1 + + + 7.179487e-01 + + + + 2304 + + YES + + + {{1, 0}, {156, 17}} + + + + + + 4 + + + + {{18, 14}, {173, 233}} + + + + 50 + + + + + + QSAAAEEgAABBmAAAQZgAAA + + + {{1, 1}, {209, 257}} + + + + + {{474, 0}, {211, 273}} + + + + + 67239424 + 0 + Workspace + + + + 3 + MCAwLjgwMDAwMDAxAA + + + + 1 + 0 + 2 + NO + + + {{20, 44}, {685, 273}} + + + YES + 2 + ipython1_console_workspace_split + + + + 1313 + + {{689, 20}, {16, 16}} + + + 28938 + 1.600000e+01 + 1.000000e+02 + + + {725, 337} + + + + {{0, 0}, {1280, 778}} + ipython1_sandbox + + + IPython1SandboxAppDelegate + + + + YES + keys + key + value + + YES + YES + YES + YES + YES + + YES + + + YES + compare: + + + YES + + + + + IPythonCocoaController + + + + + YES + + + performMiniaturize: + + + + 37 + + + + arrangeInFront: + + + + 39 + + + + print: + + + + 86 + + + + runPageLayout: + + + + 87 + + + + clearRecentDocuments: + + + + 127 + + + + orderFrontStandardAboutPanel: + + + + 142 + + + + performClose: + + + + 193 + + + + toggleContinuousSpellChecking: + + + + 222 + + + + undo: + + + + 223 + + + + copy: + + + + 224 + + + + checkSpelling: + + + + 225 + + + + paste: + + + + 226 + + + + stopSpeaking: + + + + 227 + + + + cut: + + + + 228 + + + + showGuessPanel: + + + + 230 + + + + redo: + + + + 231 + + + + selectAll: + + + + 232 + + + + startSpeaking: + + + + 233 + + + + delete: + + + + 235 + + + + performZoom: + + + + 240 + + + + performFindPanelAction: + + + + 241 + + + + centerSelectionInVisibleArea: + + + + 245 + + + + toggleGrammarChecking: + + + + 347 + + + + toggleSmartInsertDelete: + + + + 355 + + + + toggleAutomaticQuoteSubstitution: + + + + 356 + + + + toggleAutomaticLinkDetection: + + + + 357 + + + + showHelp: + + + + 360 + + + + orderFrontColorPanel: + + + + 361 + + + + saveDocument: + + + + 362 + + + + saveDocumentAs: + + + + 363 + + + + revertDocumentToSaved: + + + + 364 + + + + runToolbarCustomizationPalette: + + + + 365 + + + + toggleToolbarShown: + + + + 366 + + + + hide: + + + + 367 + + + + hideOtherApplications: + + + + 368 + + + + terminate: + + + + 369 + + + + unhideAllApplications: + + + + 370 + + + + delegate + + + + 374 + + + + contentDictionary: userNS + + + + + + + contentDictionary + userNS + 2 + + + 424 + + + + value: arrangedObjects.value + + + + + + + + arrangedObjects.value + 2 + + + 427 + + + + value: arrangedObjects.key + + + + + + + + arrangedObjects.key + 2 + + + 428 + + + + + + + + 429 + + + + animate: waitingForEngine + + + + + + + animate + waitingForEngine + 2 + + + 437 + + + + filterPredicate: workspaceFilterPredicate + + + + + + + filterPredicate + workspaceFilterPredicate + 2 + + + 440 + + + + ipythonController + + + + 441 + + + + textView + + + + 444 + + + + initialFirstResponder + + + + 445 + + + + + YES + + 0 + + YES + + + + + + -2 + + + RmlsZSdzIE93bmVyA + + + -1 + + + First Responder + + + -3 + + + Application + + + 29 + + + YES + + + + + + + + + + MainMenu + + + 19 + + + YES + + + + + + 56 + + + YES + + + + + + 103 + + + YES + + + + 1 + + + 217 + + + YES + + + + + + 83 + + + YES + + + + + + 81 + + + YES + + + + + + + + + + + + + + + + 75 + + + 3 + + + 80 + + + 8 + + + 78 + + + 6 + + + 72 + + + + + 82 + + + 9 + + + 124 + + + YES + + + + + + 77 + + + 5 + + + 73 + + + + + + 79 + + + 7 + + + 112 + + + 10 + + + 74 + + + 2 + + + 125 + + + YES + + + + + + 126 + + + + + 205 + + + YES + + + + + + + + + + + + + + + + + + 202 + + + + + 198 + + + + + 207 + + + + + 214 + + + + + 199 + + + + + 203 + + + + + 197 + + + + + 206 + + + + + 215 + + + + + 218 + + + YES + + + + + + 216 + + + YES + + + + + + 200 + + + YES + + + + + + + + + 219 + + + + + 201 + + + + + 204 + + + + + 220 + + + YES + + + + + + + + + + 213 + + + + + 210 + + + + + 221 + + + + + 208 + + + + + 209 + + + + + 106 + + + YES + + + + + + + 111 + + + + + 57 + + + YES + + + + + + + + + + + + + + + + 58 + + + + + 134 + + + + + 150 + + + + + 136 + + + 1111 + + + 144 + + + + + 129 + + + 121 + + + 143 + + + + + 236 + + + + + 131 + + + YES + + + + + + 149 + + + + + 145 + + + + + 130 + + + + + 24 + + + YES + + + + + + + + + 92 + + + + + 5 + + + + + 239 + + + + + 23 + + + + + 295 + + + YES + + + + + + 296 + + + YES + + + + + + + 297 + + + + + 298 + + + + + 299 + + + YES + + + + + + 300 + + + YES + + + + + + + 344 + + + + + 345 + + + + + 211 + + + YES + + + + + + 212 + + + YES + + + + + + + 195 + + + + + 196 + + + + + 346 + + + + + 348 + + + YES + + + + + + 349 + + + YES + + + + + + + + 350 + + + + + 351 + + + + + 354 + + + + + 371 + + + YES + + + + + + 372 + + + YES + + + + + + + 373 + + + + + + 385 + + + User Namespace Controller + + + 421 + + + YES + + + + + + + 420 + + + YES + + + + + + 416 + + + YES + + + + + + + + 417 + + + + + 418 + + + + + 419 + + + + + 406 + + + YES + + + + + + 407 + + + YES + + + + + + + + + 411 + + + YES + + + + + + + 410 + + + + + 409 + + + + + 408 + + + + + 413 + + + YES + + + + + + 412 + + + YES + + + + + + 415 + + + + + 414 + + + + + 422 + + + + + 436 + + + + + + + YES + + YES + -1.IBPluginDependency + -2.IBPluginDependency + -3.IBPluginDependency + 103.IBPluginDependency + 103.ImportedFromIB2 + 106.IBPluginDependency + 106.ImportedFromIB2 + 106.editorWindowContentRectSynchronizationRect + 111.IBPluginDependency + 111.ImportedFromIB2 + 112.IBPluginDependency + 112.ImportedFromIB2 + 124.IBPluginDependency + 124.ImportedFromIB2 + 125.IBPluginDependency + 125.ImportedFromIB2 + 125.editorWindowContentRectSynchronizationRect + 126.IBPluginDependency + 126.ImportedFromIB2 + 129.IBPluginDependency + 129.ImportedFromIB2 + 130.IBPluginDependency + 130.ImportedFromIB2 + 130.editorWindowContentRectSynchronizationRect + 131.IBPluginDependency + 131.ImportedFromIB2 + 134.IBPluginDependency + 134.ImportedFromIB2 + 136.IBPluginDependency + 136.ImportedFromIB2 + 143.IBPluginDependency + 143.ImportedFromIB2 + 144.IBPluginDependency + 144.ImportedFromIB2 + 145.IBPluginDependency + 145.ImportedFromIB2 + 149.IBPluginDependency + 149.ImportedFromIB2 + 150.IBPluginDependency + 150.ImportedFromIB2 + 19.IBPluginDependency + 19.ImportedFromIB2 + 195.IBPluginDependency + 195.ImportedFromIB2 + 196.IBPluginDependency + 196.ImportedFromIB2 + 197.IBPluginDependency + 197.ImportedFromIB2 + 198.IBPluginDependency + 198.ImportedFromIB2 + 199.IBPluginDependency + 199.ImportedFromIB2 + 200.IBPluginDependency + 200.ImportedFromIB2 + 200.editorWindowContentRectSynchronizationRect + 201.IBPluginDependency + 201.ImportedFromIB2 + 202.IBPluginDependency + 202.ImportedFromIB2 + 203.IBPluginDependency + 203.ImportedFromIB2 + 204.IBPluginDependency + 204.ImportedFromIB2 + 205.IBPluginDependency + 205.ImportedFromIB2 + 205.editorWindowContentRectSynchronizationRect + 206.IBPluginDependency + 206.ImportedFromIB2 + 207.IBPluginDependency + 207.ImportedFromIB2 + 208.IBPluginDependency + 208.ImportedFromIB2 + 209.IBPluginDependency + 209.ImportedFromIB2 + 210.IBPluginDependency + 210.ImportedFromIB2 + 211.IBPluginDependency + 211.ImportedFromIB2 + 212.IBPluginDependency + 212.ImportedFromIB2 + 212.editorWindowContentRectSynchronizationRect + 213.IBPluginDependency + 213.ImportedFromIB2 + 214.IBPluginDependency + 214.ImportedFromIB2 + 215.IBPluginDependency + 215.ImportedFromIB2 + 216.IBPluginDependency + 216.ImportedFromIB2 + 217.IBPluginDependency + 217.ImportedFromIB2 + 218.IBPluginDependency + 218.ImportedFromIB2 + 219.IBPluginDependency + 219.ImportedFromIB2 + 220.IBPluginDependency + 220.ImportedFromIB2 + 220.editorWindowContentRectSynchronizationRect + 221.IBPluginDependency + 221.ImportedFromIB2 + 23.IBPluginDependency + 23.ImportedFromIB2 + 236.IBPluginDependency + 236.ImportedFromIB2 + 239.IBPluginDependency + 239.ImportedFromIB2 + 24.IBPluginDependency + 24.ImportedFromIB2 + 24.editorWindowContentRectSynchronizationRect + 29.IBPluginDependency + 29.ImportedFromIB2 + 29.WindowOrigin + 29.editorWindowContentRectSynchronizationRect + 295.IBPluginDependency + 296.IBPluginDependency + 296.editorWindowContentRectSynchronizationRect + 297.IBPluginDependency + 298.IBPluginDependency + 299.IBPluginDependency + 300.IBPluginDependency + 300.editorWindowContentRectSynchronizationRect + 344.IBPluginDependency + 345.IBPluginDependency + 346.IBPluginDependency + 346.ImportedFromIB2 + 348.IBPluginDependency + 348.ImportedFromIB2 + 349.IBPluginDependency + 349.ImportedFromIB2 + 349.editorWindowContentRectSynchronizationRect + 350.IBPluginDependency + 350.ImportedFromIB2 + 351.IBPluginDependency + 351.ImportedFromIB2 + 354.IBPluginDependency + 354.ImportedFromIB2 + 371.IBPluginDependency + 371.IBViewEditorWindowController.showingLayoutRectangles + 371.IBWindowTemplateEditedContentRect + 371.NSWindowTemplate.visibleAtLaunch + 371.editorWindowContentRectSynchronizationRect + 372.IBPluginDependency + 373.IBPluginDependency + 385.IBPluginDependency + 407.IBPluginDependency + 409.IBPluginDependency + 410.IBPluginDependency + 411.IBPluginDependency + 412.IBPluginDependency + 413.IBPluginDependency + 414.IBPluginDependency + 415.IBPluginDependency + 416.IBPluginDependency + 417.IBPluginDependency + 418.IBPluginDependency + 419.IBAttributePlaceholdersKey + 419.IBPluginDependency + 422.IBPluginDependency + 436.IBPluginDependency + 5.IBPluginDependency + 5.ImportedFromIB2 + 56.IBPluginDependency + 56.ImportedFromIB2 + 57.IBPluginDependency + 57.ImportedFromIB2 + 57.editorWindowContentRectSynchronizationRect + 58.IBPluginDependency + 58.ImportedFromIB2 + 72.IBPluginDependency + 72.ImportedFromIB2 + 73.IBPluginDependency + 73.ImportedFromIB2 + 74.IBPluginDependency + 74.ImportedFromIB2 + 75.IBPluginDependency + 75.ImportedFromIB2 + 77.IBPluginDependency + 77.ImportedFromIB2 + 78.IBPluginDependency + 78.ImportedFromIB2 + 79.IBPluginDependency + 79.ImportedFromIB2 + 80.IBPluginDependency + 80.ImportedFromIB2 + 81.IBPluginDependency + 81.ImportedFromIB2 + 81.editorWindowContentRectSynchronizationRect + 82.IBPluginDependency + 82.ImportedFromIB2 + 83.IBPluginDependency + 83.ImportedFromIB2 + 92.IBPluginDependency + 92.ImportedFromIB2 + + + YES + + + + + + + + {{596, 852}, {216, 23}} + + + + + + + + + {{522, 812}, {146, 23}} + + + + + + + {{436, 809}, {64, 6}} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{608, 612}, {275, 83}} + + + + + + + + + + + {{365, 632}, {243, 243}} + + + + + + + + + + + + + + + {{608, 612}, {167, 43}} + + + + + + + + + + + + + + + + + {{608, 612}, {241, 103}} + + + + + + + + + + + {{525, 802}, {197, 73}} + + + {74, 862} + {{11, 736}, {489, 20}} + + + {{475, 832}, {234, 43}} + + + + + {{409, 832}, {176, 43}} + + + + + + + + + {{608, 612}, {215, 63}} + + + + + + + + + {{108, 368}, {725, 337}} + + {{108, 368}, {725, 337}} + + + + + + + + + + + + + + + + YES + + YES + + + YES + + + + + + + + + + + + {{23, 794}, {245, 183}} + + + + + + + + + + + + + + + + + + + + + {{323, 672}, {199, 203}} + + + + + + + + + + YES + + YES + + + YES + + + + + YES + + YES + + + YES + + + + 445 + + + + YES + + IPython1SandboxAppDelegate + NSObject + + YES + + YES + + + YES + + + + ipythonController + id + + + IBProjectSource + IPython1SandboxAppDelegate.py + + + + + + + YES + + YES + + + YES + + + + + NSTextView + + + IBUserSource + + + + + + 0 + ../../IPython1Sandbox.xcodeproj + 3 + + YnBsaXN0MDDUAAEAAgADAAQABQAGAAkAClgkdmVyc2lvblQkdG9wWSRhcmNoaXZlclgkb2JqZWN0cxIA +AYag0QAHAAhdSUIub2JqZWN0ZGF0YYABXxAPTlNLZXllZEFyY2hpdmVyrxEC/gALAAwAMQA1ADYAPAA9 +AEIAWABZAFoAWwALAGgAbQB7AIAAlQCZAKAApAC0ALoAzQDRAOYA+gD7APwA/QD+AP8BAAEBAQIBAwEE +AQUBBgEHAQgBCQEKAQsBDwEQARkBIQEmASoBLQExATUBOQE7AT0BTQFSAVUBWgFBAVQBYwFqAWsBbAFv +AXQBdQF4AYAAkAGBAYQBhwGIAYkBjgGPAZABkwGYAZkBmwGeAasBrAGtAbEBvAG9Ab4BwQHCAcQBxQHG +AdIB0wHbAdwB3wHkAeUB6AHtAfAB/AIAAgcCCwIdAiUCLwIzAlECUgJaAmQCZQJoAm4CbwJyAncCiAKP +ApACkwKYApkCnAKmAqcCrAKxArICtwK4ArsCwwLJAsoC0QLWAtcC2gLcAt0C5gLnAvAC8QL1AvYC9wL4 +AvkC/wMAAwIDAwMEAwcDFgMYAxsDHAMfAAsDIAMhAyIDJQNXA10DbgNzA3QDdQN6A3sDfAN/A4MDhAOH +A4gDjAOQA5cDmwOcA50DngOiA6kDqgOrA6wDsAO3A7sDvAO9A74DwgPJA80DzgPPA9AD1APcA90D3gPf +A+MD6wPwA/ED8gPzA/cD/gQCBAMEBAQFBAkEEAQRBBIEEwQXBB4EHwQgBCQEKwQvASkEMAQxBDcEOgQ7 +BDwEPwRDBEoESwRMBFAEVwRcBF0EXgRfBGMEagRrBGwEcAR3BHgEeQR9BIQEhQSGBIcEjASTBJQElQSZ +BKAEpASlBKYEpwSrBLIEswS0BLUEuQTABMEEwgTGBM0EzgTPBNME2gTbBNwE3QThBOgE6QTqBOsE7wT2 +BPcE+AT5BP0FBAUFBQYFBwUMBQ8FEAURBRUFHAUdBR4FIgUpBSoFKwUsBTAFNwU7BTwFPQU+BUMFRgVK +BVEFUgVTBVcFXgViBWMFZAVlBWkFcAV1BXYFdwV7BYIFgwWEBYgFjwWQBZEFkgWXBZgFnQWeBaIFqwWs +Ba0FrgWyBbkFugW7BbwFwAXHBcgFyQXNBdQF1QXWBeAF9gX8Bf0F/gX/BgMGCwYMBg8GEQYXBhgGGQYc +BiMGJAYlBiYGLQYuBi8GNgY3BjgGOQZABkEGQgZDBq0Gtwa4BrkGvgbABskGuAbKBs4GzwbYBrgG2Qbf +BuQG5QbvBvgGuAb5BwcHEgcZBxoHGwckBy0GuAcuBzMHNgc3B0AHSQdKB1MGuAdUB2IHaQdqB2sHcgdz +B3QHfQeGB48GuAeQB6AHqQeyB7sGuAe8B8QHywfMB9MH1AfcB90H3gfnBrgH6AfvB/gGuAf5B/4IBQgG +CA8GuAgQCBUIHga4CB8IJggvCDAIOQa4CDoIPgg/CKkJFAl/CYAJgQmCCYMJhAmFCYYJhwmICYkJigmL +CYwJjQmOCY8JkAmRCZIJkwmUCZUJlgmXCZgJmQmaCZsJnAmdCZ4JnwmgCaEJogmjCaQJpQmmCacJqAmp +CaoJqwmsCa0JrgmvCbAJsQmyCbMJtAm1CbYJtwm4CbkJugm7CbwJvQm+Cb8JwAnBCcIJwwnECcUJxgnH +CcgJyQnKCcsJzAnNCc4JzwnQCdEJ0gnTCdQJ1QnWCdcJ2AnZCdoJ2wncCd0J3gnfCeAJ4QniCeMJ5Anl +CeYJ6QnsCoYLIAshCyILIwskCyULJgsnCygLKQsqCysLLAstCy4LLwswCzELMgszCzQLNQs2CzcLOAs5 +CzoLOws8Cz0LPgs/C0ALQQtCC0MLRAtFC0YLRwtIC0kLSgtLC0wLTQtOC08LUAtRC1ILUwtUC1ULVgtX +C1gLWQtaC1sLXAtdC14LXwtgC2ELYgtjC2QLZQtmC2cLaAtpC2oLawtsC20LbgtvC3ALcQtyC3MLdAt1 +C3YLdwt4C3kLegt7C3wLfQt+C38LgAuBC4ILgwuEC4ULhguHC4gLiQuKC4sLjAuNC44LjwuQC5ELkguT +C5QLlQuWC5cLmAuZC5oLmwucC50LngufC6ALoQuiC6MLpAulC6YLpwuoC6kLqgurC6wLrQuuC68LsAux +C7ILswu0C7ULtgu3C7oLvQvAVSRudWxs3xASAA0ADgAPABAAEQASABMAFAAVABYAFwAYABkAGgAbABwA +HQAeAB8AIAAhACIAIwAkACUAJgAnACgAKQAqACsALAAtAC4ALwAwVk5TUm9vdFYkY2xhc3NdTlNPYmpl +Y3RzS2V5c18QD05TQ2xhc3Nlc1ZhbHVlc18QGU5TQWNjZXNzaWJpbGl0eU9pZHNWYWx1ZXNdTlNDb25u +ZWN0aW9uc1tOU05hbWVzS2V5c1tOU0ZyYW1ld29ya11OU0NsYXNzZXNLZXlzWk5TT2lkc0tleXNdTlNO +YW1lc1ZhbHVlc18QGU5TQWNjZXNzaWJpbGl0eUNvbm5lY3RvcnNdTlNGb250TWFuYWdlcl8QEE5TVmlz +aWJsZVdpbmRvd3NfEA9OU09iamVjdHNWYWx1ZXNfEBdOU0FjY2Vzc2liaWxpdHlPaWRzS2V5c1lOU05l +eHRPaWRcTlNPaWRzVmFsdWVzgAKBAv2BAZuBAmCBAvyArYEB9oAFgQJfgQJhgQH3gQL6gACABoEB9YEC ++xEBv4ECYtIADgAyADMANFtOU0NsYXNzTmFtZYAEgANdTlNBcHBsaWNhdGlvbtIANwA4ADkAOlgkY2xh +c3Nlc1okY2xhc3NuYW1logA6ADteTlNDdXN0b21PYmplY3RYTlNPYmplY3RfEBBJQkNvY29hRnJhbWV3 +b3Jr0gAOAD4APwBAWk5TLm9iamVjdHOAK6EAQYAH2wBDAA4ARABFAEYARwBIAEkASgBLAEwATQBOAE8A +UABRAFIAUwBUAFUAVgArXE5TV2luZG93Vmlld1xOU1NjcmVlblJlY3RfEBNOU0ZyYW1lQXV0b3NhdmVO +YW1lXU5TV2luZG93VGl0bGVZTlNXVEZsYWdzXU5TV2luZG93Q2xhc3NcTlNXaW5kb3dSZWN0XxAPTlNX +aW5kb3dCYWNraW5nXxARTlNXaW5kb3dTdHlsZU1hc2tbTlNWaWV3Q2xhc3OAC4CsgKqAq4AJEnQAAACA +CoAIEAIQD4AAXxAYe3szMzUsIDQxM30sIHs3MjUsIDMzN319XxAQSVB5dGhvbjEgKENvY29hKVhOU1dp +bmRvd9cAXAAOAF0AXgBfAFoAYABhAGIAYwBkAGUAYQBnXxAPTlNOZXh0UmVzcG9uZGVyWk5TU3Vidmll +d3NYTlN2RmxhZ3NbTlNGcmFtZVNpemVbTlNTdXBlcnZpZXeADIBdgA0RAQCAqIAMgKnSAA4APgBpAGqA +NKIAawBsgA6Ao9oAXAAOAG4AXQBeAG8AcABaAGAAcQBNAHMAdAB1AHYAdwBVAGEATQB6V05TRnJhbWVe +TlNBdXRvc2F2ZU5hbWVeTlNEaXZpZGVyU3R5bGVcTlNJc1ZlcnRpY2FsgAuAooCggA8RARKAoYAMgAsJ +0gAOAD4AaQB9gDSiAH4Af4AQgGreAFwAgQAOAIIAgwBdAF4AXwCEAFoAhQCGAGAAhwBrAIkAigCLAIwA +jQCOAI8AkABhAJIAVQBrAJRZTlNCb3hUeXBlW05TVGl0bGVDZWxsXU5TVHJhbnNwYXJlbnRcTlNCb3Jk +ZXJUeXBlWU5TT2Zmc2V0c18QD05TVGl0bGVQb3NpdGlvbl1OU0NvbnRlbnRWaWV3gA4QAIBpgGAIgBEQ +FoBeEAGADIBfgA6AEtIADgA+AGkAl4A0oQCUgBLXAFwADgBuAF0AXgBaAGAAfgBiAJwAnQBkAGEAfoAQ +gF2AXIATgAyAENIADgA+AGkAooA0oQCjgBTcAFwApQAOAG4ApgBdAF4AWgBgAKcAqACHAJQAqgCrAKwA +rQCuAHYAYQCUALEAsgCyW05TSFNjcm9sbGVyWE5Tc0ZsYWdzW05TVlNjcm9sbGVyXU5TTmV4dEtleVZp +ZXeAEoBYgFuAWhECEoAVgAyAEoBUgBaAFtIADgA+AGkAtoA0owCyALEAqoAWgFSAWN0AXAAOAG4AuwC8 +AL0AXQBeAL4AWgC/AGAAqACjAMEAwgDDAMQAxQDGAMcAyABhAMoAowDIWE5TQm91bmRzWE5TQ3Vyc29y +WU5TY3ZGbGFnc1lOU0RvY1ZpZXdZTlNCR0NvbG9ygBSAU4BNgE6AUBAEgBcRCQCAGIAMgE+AFIAY0gAO +AD4AaQDPgDShAMiAGN0AXAAOAG4A0gDTANQA1QBeANYAWgDXAGAA2ACyANoA2wDcAN0A3gDfAOAA4QBh +AOMAsgArXxAPTlNUZXh0Q29udGFpbmVyWU5TVFZGbGFnc1xOU1NoYXJlZERhdGFbTlNEcmFnVHlwZXNZ +TlNNYXhTaXplWE5TTWluaXplWk5TRGVsZWdhdGWAFoBMgCyALRAGgDeAGREJEoBKgAyAS4AWgADSAA4A +PgA/AOiAK68QEQDpAOoA6wDsAO0A7gDvAPAA8QDyAPMA9AD1APYA9wD4APmAGoAbgByAHYAegB+AIIAh +gCKAI4AkgCWAJoAngCiAKYAqXxAZTmVYVCBSVEZEIHBhc3RlYm9hcmQgdHlwZV8QEk5TU3RyaW5nUGJv +YXJkVHlwZV8QGk5lWFQgcnVsZXIgcGFzdGVib2FyZCB0eXBlXxAeTmVYVCBUSUZGIHY0LjAgcGFzdGVi +b2FyZCB0eXBlXxAZQXBwbGUgVVJMIHBhc3RlYm9hcmQgdHlwZV8QI0NvcmVQYXN0ZWJvYXJkRmxhdm9y +VHlwZSAweDZENkY2Rjc2XxAjQ29yZVBhc3RlYm9hcmRGbGF2b3JUeXBlIDB4NzU3MjZDMjBfEBtXZWJV +UkxzV2l0aFRpdGxlc1Bib2FyZFR5cGVfEBlBcHBsZSBQREYgcGFzdGVib2FyZCB0eXBlXxAZQXBwbGUg +UE5HIHBhc3RlYm9hcmQgdHlwZV8QGkFwcGxlIEhUTUwgcGFzdGVib2FyZCB0eXBlXxAVTlNGaWxlbmFt +ZXNQYm9hcmRUeXBlXxAXTlNDb2xvciBwYXN0ZWJvYXJkIHR5cGVfEDFOZVhUIEVuY2Fwc3VsYXRlZCBQ +b3N0U2NyaXB0IHYxLjIgcGFzdGVib2FyZCB0eXBlXxAaQXBwbGUgUElDVCBwYXN0ZWJvYXJkIHR5cGVf +EBlOZVhUIGZvbnQgcGFzdGVib2FyZCB0eXBlXxAqTmVYVCBSaWNoIFRleHQgRm9ybWF0IHYxLjAgcGFz +dGVib2FyZCB0eXBl0gA3ADgBDAENowENAQ4AO1xOU011dGFibGVTZXRVTlNTZXRfEBR7ezAsIDM4fSwg +ezQzMywgMTR9fdUBEQAOARIBEwEUAJABFQDIARcBGFlOU1RDRmxhZ3NaTlNUZXh0Vmlld1dOU1dpZHRo +XxAPTlNMYXlvdXRNYW5hZ2VygDaAGCNAexAAAAAAAIAu1QAOARoBGwEcANgBHQEeAR8A3QArXxAQTlNU +ZXh0Q29udGFpbmVyc11OU1RleHRTdG9yYWdlWU5TTE1GbGFnc4A1gDOAL4AA0wAOASIA2AEjASQAK1hO +U1N0cmluZ4AygDCAANIADgEnASgBKVlOUy5zdHJpbmeAMVDSADcAOAErASyjASwBIgA7XxAPTlNNdXRh +YmxlU3RyaW5n0gA3ADgBLgEbpAEbAS8BMAA7XxAZTlNNdXRhYmxlQXR0cmlidXRlZFN0cmluZ18QEk5T +QXR0cmlidXRlZFN0cmluZ9IADgA+AGkBM4A0oQDcgC3SADcAOAE2ATejATcBOAA7Xk5TTXV0YWJsZUFy +cmF5V05TQXJyYXnSADcAOAE6ARSiARQAO9IANwA4ATwA0qIA0gA72AAOAT4BPwFAAUEBQgFDAUQBRQFG +ACsBSAFJAUoAKwFMV05TRmxhZ3NfEBdOU0RlZmF1bHRQYXJhZ3JhcGhTdHlsZV8QEE5TSW5zZXJ0aW9u +Q29sb3JfEBFOU0JhY2tncm91bmRDb2xvcl8QFE5TU2VsZWN0ZWRBdHRyaWJ1dGVzXxASTlNNYXJrZWRB +dHRyaWJ1dGVzXxAQTlNMaW5rQXR0cmlidXRlc4BJEgAFS2+AAIA6gDiAO4AAgEXTAA4BTgFPAVAAVQFR +XE5TQ29sb3JTcGFjZVVOU1JHQoA5TxAYMSAwLjk1Mjk0MTI0IDAuODUwOTgwNDYA0gA3ADgBUwFUogFU +ADtXTlNDb2xvctMADgFOAVYBUAFYAVlXTlNXaGl0ZYA5EANCMADTAA4BWwA+AVwBXQFgV05TLmtleXOA +RKIBXgFfgDyAPaIBYQFigD6AQtUADgFUAU4BZAFlAVABZwDdAWgBaVtOU0NvbG9yTmFtZV1OU0NhdGFs +b2dOYW1lgDmAQYBAgD9WU3lzdGVtXxAbc2VsZWN0ZWRUZXh0QmFja2dyb3VuZENvbG9y0wAOAU4BVgFQ +AVgBboA5SzAuNjY2NjY2NjkA1QAOAVQBTgFkAWUBUAFIAN0BcgFpgDmAOoBDgD9fEBFzZWxlY3RlZFRl +eHRDb2xvctIANwA4AXYBd6IBdwA7XE5TRGljdGlvbmFyedMADgFbAD4BXAF6AX2ARKIBewFfgEaAPaIB +fgF/gEeASFtOU1VuZGVybGluZdMADgFOAU8BUACQAYOAOUYwIDAgMQDSADcAOAGFAYaiAYYAO18QFE5T +VGV4dFZpZXdTaGFyZWREYXRhXHs0ODAsIDFlKzA3fVd7ODQsIDB90gA3ADgBigESpQESAYsBjAGNADtW +TlNUZXh0Vk5TVmlld1tOU1Jlc3BvbmRlcl8QFHt7MSwgMX0sIHs0MzMsIDIzMX19XxAVe3swLCAzOH0s +IHs0MzMsIDIzMX190wAOAU4BVgFQAVgBkoA5QjEA0wAOAZQBlQGWAZcAkFlOU0hvdFNwb3RcTlNDdXJz +b3JUeXBlgFKAUVd7NCwgLTV90gA3ADgBmgC8ogC8ADvSADcAOAGcAZ2kAZ0BjAGNADtaTlNDbGlwVmll +d9kAXAGfAA4AbgBeAFoBoABgAaEAowCjAaQBpQGmAGEBqACjAapYTlNUYXJnZXRYTlNBY3Rpb25ZTlNQ +ZXJjZW50gBSAFIBXgFUT/////4AAAQCADIBWgBQjP9Ww0wAAAABfEBV7ezQyNywgMX0sIHsxNSwgMjYz +fX1cX2RvU2Nyb2xsZXI60gA3ADgBrgGvpQGvAbABjAGNADtaTlNTY3JvbGxlcllOU0NvbnRyb2zbAFwB +nwAOAG4ApgBeAFoBoABgAbIBoQCjAKMBpAG2AJAAZABhAagAowG6AbtaTlNDdXJWYWx1ZYAUgBSAV4BZ +gAyAVoAUIz/wAAAAAAAAIz/uQshgAAAAXxAYe3stMTAwLCAtMTAwfSwgezg3LCAxOH19XxAWe3sxOCwg +MTR9LCB7NDM1LCAyMzN9fdIANwA4Ab8BwKQBwAGMAY0AO1xOU1Njcm9sbFZpZXdfEBR7ezEsIDF9LCB7 +NDcxLCAyNTd9fdIANwA4AcMBjKMBjAGNADtaezQ3MywgMjczfVZ7MCwgMH3XAccADgFBAcgByQHKAcsB +zAHNAc4BzwHQAIkB0VtOU0NlbGxGbGFnc1pOU0NvbnRlbnRzWU5TU3VwcG9ydFxOU0NlbGxGbGFnczJb +TlNUZXh0Q29sb3ISBAH+AIBogGWAYYBigGdXQ29uc29sZdQADgHUAdUB1gHXAdgB2QHaVk5TU2l6ZVZO +U05hbWVYTlNmRmxhZ3OAZCNAJgAAAAAAAIBjEQwcXEx1Y2lkYUdyYW5kZdIANwA4Ad0B3qIB3gA7Vk5T +Rm9udNUADgFUAU4BZAFlAVAAygDdAeIBaYA5gE+AZoA/XxATdGV4dEJhY2tncm91bmRDb2xvctMADgFO +AVYBUAFYAeeAOU0wIDAuODAwMDAwMDEA0gA3ADgB6QHqpAHqAesB7AA7XxAPTlNUZXh0RmllbGRDZWxs +XE5TQWN0aW9uQ2VsbFZOU0NlbGzSADcAOAHuAe+kAe8BjAGNADtVTlNCb3jeAFwAgQAOAIIAbgCDAF0A +XgCEAFoAhQCGAGAAhwBrAIkAigHzAfQAjAH2AfcAkABhAJIAVQBrAfuADoBpgJ2AnAiAaxAzgAyAX4AO +gGzSAA4APgBpAf6ANKEB+4Bs1wBcAA4AbgBdAF4AWgBgAH8AYgIDAgQAZABhAH+AaoBdgJuAbYAMgGrS +AA4APgBpAgmANKECCoBu3xAPAFwApQAOAG4ApgIMAg0AXQIOAF4AWgBgAKcAqACHAfsCEACrAhICEwIU +AhUCFgIXAHYAYQH7AhoCGwIbXE5TQ29ybmVyVmlld18QEE5TSGVhZGVyQ2xpcFZpZXdcTlNTY3JvbGxB +bXRzgGyAloBbgJoQMoB4gHWAb08QEEEgAABBIAAAQZgAAEGYAACADIBsgJSAcIBw0gAOAD4AaQIfgDSl +AhsCGgIQAhUCFIBwgJSAloB1gHjbAFwADgBuAL0AXQBeAL4AWgC/AGAAqAIKAMECKADFAikAxwIqAGEC +LAIKAiqAboBTgJOAcYBygAyAhoBugHLSAA4APgBpAjGANKECKoBy3xAVAFwCNAAOAjUCNgFBAjcCDAI4 +AjkCOgBeAF8COwBaAjwCPQBgAj4CPwJAAhsAiQJCAkMCRADKAHoCFAJIAMUCSQBkAkoAegBhAk0AkAIb +Ak8AVgJQXxAfTlNEcmFnZ2luZ1NvdXJjZU1hc2tGb3JOb25Mb2NhbFlOU1R2RmxhZ3NcTlNIZWFkZXJW +aWV3XxASTlNBbGxvd3NUeXBlU2VsZWN0XxAXTlNJbnRlcmNlbGxTcGFjaW5nV2lkdGhfEBlOU0NvbHVt +bkF1dG9yZXNpemluZ1N0eWxlXxAYTlNJbnRlcmNlbGxTcGFjaW5nSGVpZ2h0WU5TRW5hYmxlZFtOU0dy +aWRDb2xvcl8QD05TR3JpZFN0eWxlTWFza15OU1RhYmxlQ29sdW1uc18QHE5TRHJhZ2dpbmdTb3VyY2VN +YXNrRm9yTG9jYWxbTlNSb3dIZWlnaHSAcICSE//////WwAAAgHSATwmAeCNACAAAAAAAACNAAAAAAAAA +AIBzCYAMgI+AcIB7I0AxAAAAAAAAWnsxNTYsIDIwMH3XAFwADgBeAF8AWgBgAlMCFQJVAGQCVgBhAhUC +KltOU1RhYmxlVmlld4B1gHeAdoAMgHWActsAXAAOAG4AvQBdAF4AvgBaAL8AYACoAgoAwQJdAMUCXgDH +AkQAYQIsAgoCRIBugFOAmYCYgHSADICGgG6AdFl7MTU2LCAxN33SADcAOAJmAmekAmcBjAGNADtfEBFO +U1RhYmxlSGVhZGVyVmlld9YAXAAOAG4AXgBaAGACCgJqAmsAZABhAgqAboB6gHmADIBuXxAUe3sxNTcs +IDB9LCB7MTYsIDE3fX3SADcAOAJwAnGkAnEBjAGNADtdX05TQ29ybmVyVmlld9IADgA+AGkCdIA0ogJ1 +AnaAfICL2gJ4AA4CeQETAnoCewJ8An0CfgJTAHoCgAKBAoICgwFYAoQChQB6AipeTlNJc1Jlc2l6ZWFi +bGVcTlNIZWFkZXJDZWxsWk5TRGF0YUNlbGxeTlNSZXNpemluZ01hc2taTlNNaW5XaWR0aFpOU01heFdp +ZHRoXE5TSXNFZGl0YWJsZQmAioB9I0BRwAAAAAAAgIMjQEQAAAAAAAAjQI9AAAAAAAAJgHLXAccADgFB +AcgByQHKAcsCiQKKAosCjAHQAIkCjhIEgf4AgIKAf4B+gGKAgFhWYXJpYWJsZdMADgFOAVYBUAFYApKA +OUswLjMzMzMzMjk5ANUADgFUAU4BZAFlAVABSADdApYBaYA5gDqAgYA/XxAPaGVhZGVyVGV4dENvbG9y +0gA3ADgCmgKbpQKbAeoB6wHsADtfEBFOU1RhYmxlSGVhZGVyQ2VsbNgBxwAOAUEByAHJAp0BygHLAp4B +zQIsAqECogIqAqQCpV1OU0NvbnRyb2xWaWV3EhQh/kCAaICGgISAhYByEQgAgIhZVGV4dCBDZWxs1AAO +AdQB1QHWAdcCqQHZAquAZCNAKgAAAAAAAIBjEQQU1QAOAVQBTgFkAWUBUAFnAN0CrwFpgDmAQYCHgD9f +EBZjb250cm9sQmFja2dyb3VuZENvbG9y1QAOAVQBTgFkAWUBUAFIAN0CtQFpgDmAOoCJgD9fEBBjb250 +cm9sVGV4dENvbG9y0gA3ADgCuQK6ogK6ADtdTlNUYWJsZUNvbHVtbtoCeAAOAnkBEwJ6AnsCfAJ9An4C +UwB6AoACvgK/AsABWAKEAoUAegIqCYCKgIwjQFPAAAAAAACAjgmActcBxwAOAUEByAHJAcoBywKJAooC +iwLGAdAAiQKOgIKAf4CNgGKAgFVWYWx1ZdgBxwAOAUEByAHJAp0BygHLAp4BzQIsAqECogIqAqQCpYBo +gIaAhICFgHKAiNUADgFUAU4BZAFlAVAC0wDdAtQBaYA5gJGAkIA/WWdyaWRDb2xvctMADgFOAVYBUAFY +AtmAOUQwLjUA0gA3ADgC2wJTpQJTAbABjAGNADtfEBV7ezEsIDE3fSwgezE1NiwgMjAwfX3ZAFwBnwAO +AG4AXgBaAaAAYAGhAgoCCgGkAuEAZABhAagCCgLlgG6AboBXgJWADIBWgG4jP+/gP4AAAABfEBZ7ezE1 +NywgMTd9LCB7MTUsIDIwMH192gBcAZ8ADgBuAKYAXgBaAaAAYAGhAgoCCgGkAusAkABkAGEBqAIKAu+A +boBugFeAl4AMgFaAbiM/5vlvoAAAAF8QFXt7MSwgMjE3fSwgezE1NiwgMTV9fdIADgA+AGkC84A0oQJE +gHRfEBN7ezEsIDB9LCB7MTU2LCAxN319XxAWe3sxOCwgMTR9LCB7MTczLCAyMzN9fV8QFHt7MSwgMX0s +IHsyMDksIDI1N319XxAWe3s0NzQsIDB9LCB7MjExLCAyNzN9fdcBxwAOAUEByAHJAcoBywHMAc0BzgL8 +AdAAiQL+gGiAZYCegGKAn1lXb3Jrc3BhY2XTAA4BTgFWAVABWAHngDlfEBZ7ezIwLCA0NH0sIHs2ODUs +IDI3M319XxAgaXB5dGhvbjFfY29uc29sZV93b3Jrc3BhY2Vfc3BsaXTSADcAOAMFAwakAwYBjAGNADtb +TlNTcGxpdFZpZXfaAFwADgBuAwgDCQBeAFoDCgBgAwsATQMNAw4DDwMQAxEAYQMTAE0DFVpOU01heFZh +bHVlWk5TTWluVmFsdWVZTlNwaUZsYWdzXE5TRHJhd01hdHJpeIALgKeApiNAWQAAAAAAACNAMAAAAAAA +ABEFIYAMEXEKgAuApNEADgMXgKXSADcAOAMZAxqiAxoAO1pOU1BTTWF0cml4XxAVe3s2ODksIDIwfSwg +ezE2LCAxNn190gA3ADgDHQMepAMeAYwBjQA7XxATTlNQcm9ncmVzc0luZGljYXRvclp7NzI1LCAzMzd9 +XxAVe3swLCAwfSwgezEyODAsIDc3OH19XxAQaXB5dGhvbjFfc2FuZGJveNIANwA4AyMDJKIDJAA7XxAQ +TlNXaW5kb3dUZW1wbGF0ZdIADgA+AGkDJ4A0rxAvAygDKQMqAysDLAMtAy4DLwMwAzEDMgMzAzQDNQM2 +AzcDOAM5AzoDOwM8Az0DPgM/A0ADQQNCA0MDRANFA0YDRwNIA0kDSgNLA0wDTQNOA08DUANRA1IDUwNU +A1UDVoCugLyAwoDHgM2A04DYgN6A5IDpgO2A84D4gPyBAQKBAQaBAQqBAQ+BAROBARmBAR6BASKBASaB +ASuBATCBATWBATqBAT6BAUKBAUeBAU2BAU+BAVOBAVmBAV6BAWKBAWeBAWmBAWuBAXCBAXWBAXmBAX2B +AYyBAZCBAZOBAZfTAA4DWANZA1oDWwNcWE5TU291cmNlV05TTGFiZWyAu4CvgLrZAA4DXgNfA2ADYQNi +A2MDZANlA2YDZwNoA2kDagNrA2wDbQBVV05TVGl0bGVfEBFOU0tleUVxdWl2TW9kTWFza1pOU0tleUVx +dWl2XU5TTW5lbW9uaWNMb2NZTlNPbkltYWdlXE5TTWl4ZWRJbWFnZVZOU01lbnVVTlNUYWeAuYCxEgAQ +AACAshJ/////gLOAt4Cw0wAOA14DbwNwA3EDcltOU01lbnVJdGVtc4EBoIEBp4EBqVxTbWFydCBRdW90 +ZXNRZ9MADgAyA3YDdwN4A3leTlNSZXNvdXJjZU5hbWWAtoC0gLVXTlNJbWFnZV8QD05TTWVudUNoZWNr +bWFya9IANwA4A30DfqIDfgA7XxAQTlNDdXN0b21SZXNvdXJjZdMADgAyA3YDdwN4A4KAtoC0gLhfEBBO +U01lbnVNaXhlZFN0YXRl0gA3ADgDhQOGogOGADtaTlNNZW51SXRlbV8QIXRvZ2dsZUF1dG9tYXRpY1F1 +b3RlU3Vic3RpdHV0aW9uOtIANwA4A4kDiqMDigOLADtfEBVOU05pYkNvbnRyb2xDb25uZWN0b3JeTlNO +aWJDb25uZWN0b3LTAA4DWANZA1oDjgOPgLuAvYDB2AAOA14DXwNgA2EDYgNjA2QDZgOSA2gDkwNqA2sD +bAOWgLmAv4DAgLOAt4C+0wAOA14DbwNwA5kDmoEBoIEB0oEB1F8QEUp1bXAgdG8gU2VsZWN0aW9uUWpf +EB1jZW50ZXJTZWxlY3Rpb25JblZpc2libGVBcmVhOtMADgNYA1kDWgOgA6GAu4DDgMbZAA4DXgNfA2AD +YQNiA2MDZANlA2YDpANoA6UDagNrA2wDbQCQgLmAxIDFgLOAt4CwXxAQU21hcnQgQ29weS9QYXN0ZVFm +XxAYdG9nZ2xlU21hcnRJbnNlcnREZWxldGU60wAOA1gDWQNaA64Dr4C7gMiAzNgADgNeA18DYANhA2ID +YwNkA2YDsgNoA7MDagNrA2wDtoC5gMqAy4CzgLeAydMADgNeA28DcAO5A7qBAaCBAcCBAcJUU2F2ZVFz +XXNhdmVEb2N1bWVudDrTAA4DWANZA1oDwAPBgLuAzoDS2AAOA14DXwNgA2EDYgNjA2QDZgPEA2gDxQNq +A2sDbAPIgLmA0IDRgLOAt4DP0wAOA14DbwNwA8sDzIEBoIEBzIEBzlRVbmRvUXpVdW5kbzrTAA4DWANZ +A1oD0gPTgLuA1IDX2AAOA14DXwNgA2EDYgNjA2QDZgPWA9cD2ANqA2sDbAPIgLmA1RIAEgAAgNaAs4C3 +gM9UUmVkb1FaVXJlZG860wAOA1gDWQNaA+ED4oC7gNmA3dgADgNeA18DYANhA2IDYwNkA2YD5QPmA+cD +agNrA2wD6oC5gNsSABgAAIDcgLOAt4Da1AAOA14B1QNvA3AD7QPuA++BAaCBAa6BAb6BAbBbSGlkZSBP +dGhlcnNRaF8QFmhpZGVPdGhlckFwcGxpY2F0aW9uczrTAA4DWANZA1oD9QP2gLuA34Dj2AAOA14DXwNg +A2EDYgNjA2QDZgP5A9cD+gNqA2sDbAP9gLmA4YDigLOAt4Dg0wAOA14DbwNwBAAEAYEBoIEB4YEB41tT +aG93IENvbG9yc1FDXxAVb3JkZXJGcm9udENvbG9yUGFuZWw60wAOA1gDWQNaBAcECIC7gOWA6NgADgNe +A18DYANhA2IDYwNkA2YECwNoBAwDagNrA2wDyIC5gOaA54CzgLeAz1VQYXN0ZVF2VnBhc3RlOtMADgNY +A1kDWgQVBBaAu4DqgOzZAA4DXgNfA2ADYQNiA2MDZANlA2YEGQNoA6UDagNrA2wDlgCQgLmA64DFgLOA +t4C+ZQBGAGkAbgBkICZfEBdwZXJmb3JtRmluZFBhbmVsQWN0aW9uOtMADgNYA1kDWgQiBCOAu4DugPLY +AA4DXgNfA2ADYQNiA2MDZANmBCYDaAQnA2oDawNsBCqAuYDwgPGAs4C3gO/TAA4DXgNvA3AELQQugQGg +gQGdgQGfXVN0b3AgU3BlYWtpbmddc3RvcFNwZWFraW5nOtQADgQyA1gDWQQzBDQAQQQ2XU5TRGVzdGlu +YXRpb26A94D0gAeA9tIADgAyADMEOYAEgPVfEBZJUHl0aG9uQ29jb2FDb250cm9sbGVyWGRlbGVnYXRl +0gA3ADgEPQQ+owQ+A4sAO18QFE5TTmliT3V0bGV0Q29ubmVjdG9y0wAOA1gDWQNaBEEEQoC7gPmA+9gA +DgNeA18DYANhA2IDYwNkA2YERQNoBCcDagNrA2wDyIC5gPqA8YCzgLeAz1ZEZWxldGVXZGVsZXRlOtMA +DgNYA1kDWgROBE+Au4D9gQEB2AAOA14DXwNgA2EDYgNjA2QDZgRSA2gEUwNqA2sDbARWgLmA/4EBAICz +gLeA/tQADgNeAdUDbwNwBFkEWgRbgQGggQHrgQHvgQHtWE1pbmltaXplUW1fEBNwZXJmb3JtTWluaWF0 +dXJpemU60wAOA1gDWQNaBGEEYoC7gQEDgQEF2AAOA14DXwNgA2EDYgNjA2QDZgRlA2gEJwNqA2sDbARW +gLmBAQSA8YCzgLeA/l8QEkJyaW5nIEFsbCB0byBGcm9udF8QD2FycmFuZ2VJbkZyb250OtMADgNYA1kD +WgRuBG+Au4EBB4EBCdgADgNeA18DYANhA2IDYwNkA2YEcgNoBCcDagNrA2wD6oC5gQEIgPGAs4C3gNpY +U2hvdyBBbGxfEBZ1bmhpZGVBbGxBcHBsaWNhdGlvbnM60wAOA1gDWQNaBHsEfIC7gQELgQEO2AAOA14D +XwNgA2EDYgNjA2QDZgR/A2gEgANqA2sDbAO2gLmBAQyBAQ2As4C3gMlVQ2xvc2VRd11wZXJmb3JtQ2xv +c2U61AAOBDIDWANZA1oAHwSKBIuAu4ACgQEQgQES1wAOA14DYANhA2IDYwNkA2YEjgQnA2oDawNsA+qA +uYEBEYDxgLOAt4DaXxAVQWJvdXQgSVB5dGhvbjFTYW5kYm94XxAdb3JkZXJGcm9udFN0YW5kYXJkQWJv +dXRQYW5lbDrTAA4DWANZA1oElwSYgLuBARSBARjYAA4DXgNfA2ADYQNiA2MDZANmBJsDaAScA2oDawNs +BJ+AuYEBFoEBF4CzgLeBARXTAA4DXgNvA3AEogSjgQGggQHdgQHfXkNoZWNrIFNwZWxsaW5nUTteY2hl +Y2tTcGVsbGluZzrTAA4DWANZA1oEqQSqgLuBARqBAR3YAA4DXgNfA2ADYQNiA2MDZANmBK0DaASuA2oD +awNsA8iAuYEBG4EBHICzgLeAz1pTZWxlY3QgQWxsUWFac2VsZWN0QWxsOtMADgNYA1kDWgS3BLiAu4EB +H4EBIdgADgNeA18DYANhA2IDYwNkA2YEuwNoA+cDagNrA2wD6oC5gQEggNyAs4C3gNpfEBRIaWRlIElQ +eXRob24xU2FuZGJveFVoaWRlOtMADgNYA1kDWgTEBMWAu4EBI4EBJdgADgNeA18DYANhA2IDYwNkA2YE +yANoBCcDagNrA2wEn4C5gQEkgPGAs4C3gQEVXxAbQ2hlY2sgU3BlbGxpbmcgV2hpbGUgVHlwaW5nXxAe +dG9nZ2xlQ29udGludW91c1NwZWxsQ2hlY2tpbmc60wAOA1gDWQNaBNEE0oC7gQEngQEq2AAOA14DXwNg +A2EDYgNjA2QDZgTVA2gE1gNqA2sDbAO2gLmBASiBASmAs4C3gMlmAFAAcgBpAG4AdCAmUXBWcHJpbnQ6 +0wAOA1gDWQNaBN8E4IC7gQEsgQEv2AAOA14DXwNgA2EDYgNjA2QDZgTjA9cE5ANqA2sDbAO2gLmBAS2B +AS6As4C3gMloAFMAYQB2AGUAIABBAHMgJlFTXxAPc2F2ZURvY3VtZW50QXM60wAOA1gDWQNaBO0E7oC7 +gQExgQE02AAOA14DXwNgA2EDYgNjA2QDZgTxA2gE8gNqA2sDbAPqgLmBATKBATOAs4C3gNpfEBRRdWl0 +IElQeXRob24xU2FuZGJveFFxWnRlcm1pbmF0ZTrTAA4DWANZA1oE+wT8gLuBATaBATnZAA4DXgNfA2AD +YQNiA2MDZANlA2YE/wPXBQADagNrA2wDbQFYgLmBATeBATiAs4C3gLBbU21hcnQgTGlua3NRR18QHXRv +Z2dsZUF1dG9tYXRpY0xpbmtEZXRlY3Rpb2461AAOBDIDWANZBDMENAUKBQuA94D0gQE7gQE90gAOADIA +MwUOgASBATxfEBpJUHl0aG9uMVNhbmRib3hBcHBEZWxlZ2F0ZV8QEWlweXRob25Db250cm9sbGVy0wAO +A1gDWQNaBRMFFIC7gQE/gQFB2AAOA14DXwNgA2EDYgNjA2QDZgUXA2gEJwNqA2sDbARWgLmBAUCA8YCz +gLeA/lRab29tXHBlcmZvcm1ab29tOtMADgNYA1kDWgUgBSGAu4EBQ4EBRtgADgNeA18DYANhA2IDYwNk +A2YFJANoBSUDagNrA2wDyIC5gQFEgQFFgLOAt4DPVENvcHlRY1Vjb3B5OtMADgNYA1kDWgUuBS+Au4EB +SIEBTNgADgNeA18DYANhA2IDYwNkA2YFMgPmBTMDagNrA2wFNoC5gQFKgQFLgLOAt4EBSdMADgNeA28D +cAU5BTqBAaCBAeeBAelcU2hvdyBUb29sYmFyUXRfEBN0b2dnbGVUb29sYmFyU2hvd2461AAOBDIDWANZ +BDMFCgVBBDaA94EBO4EBToD20gAOADIAMwA0gASAA9MADgNYA1kDWgVIBUmAu4EBUIEBUtgADgNeA18D +YANhA2IDYwNkA2YFTANoBCcDagNrA2wEKoC5gQFRgPGAs4C3gO9eU3RhcnQgU3BlYWtpbmdec3RhcnRT +cGVha2luZzrTAA4DWANZA1oFVQVWgLuBAVSBAVjYAA4DXgNfA2ADYQNiA2MDZANmBVkDaAVaA2oDawNs +BV2AuYEBVoEBV4CzgLeBAVXTAA4DXgNvA3AFYAVhgQGggQHxgQHzXxAUSVB5dGhvbjFTYW5kYm94IEhl +bHBRP1lzaG93SGVscDrTAA4DWANZA1oFZwVogLuBAVqBAV3YAA4DXgNfA2ADYQNiA2MDZANmBWsDaAQn +A2oDawNsBW+AuYEBXIDxgLOAt4EBW9QADgNeAdUDbwNwBXIFcwV0gQGggQGigQGlgQGkWkNsZWFyIE1l +bnVfEBVjbGVhclJlY2VudERvY3VtZW50czrTAA4DWANZA1oFeQV6gLuBAV+BAWHYAA4DXgNfA2ADYQNi +A2MDZANmBX0DaAQnA2oDawNsBTaAuYEBYIDxgLOAt4EBSW8QEgBDAHUAcwB0AG8AbQBpAHoAZQAgAFQA +bwBvAGwAYgBhAHIgJl8QH3J1blRvb2xiYXJDdXN0b21pemF0aW9uUGFsZXR0ZTrTAA4DWANZA1oFhgWH +gLuBAWOBAWbYAA4DXgNfA2ADYQNiA2MDZANmBYoDaAWLA2oDawNsA8iAuYEBZIEBZYCzgLeAz1NDdXRR +eFRjdXQ61AAOBDIDWANZBDMAyAQ0BZaA94AYgPSBAWhYdGV4dFZpZXfUAA4EMgNYA1kEMwDIAEEFnID3 +gBiAB4EBal8QFWluaXRpYWxGaXJzdFJlc3BvbmRlctMADgNYA1kDWgWgBaGAu4EBbIEBb9kADgWjA14D +XwNgA2EDYgNjA2QDZgQnBaYD1wWnA2oDawNsA7ZZTlNUb29sVGlwgLmA8YEBbYEBboCzgLeAyV1QYWdl +IFNldHVwLi4uUVBecnVuUGFnZUxheW91dDrTAA4DWANZA1oFsAWxgLuBAXGBAXTYAA4DXgNfA2ADYQNi +A2MDZANmBbQDaAW1A2oDawNsBJ+AuYEBcoEBc4CzgLeBARVuAFMAaABvAHcAIABTAHAAZQBsAGwAaQBu +AGcgJlE6XxAPc2hvd0d1ZXNzUGFuZWw60wAOA1gDWQNaBb4Fv4C7gQF2gQF41wAOA14DYANhA2IDYwNk +A2YFwgQnA2oDawNsA7aAuYEBd4DxgLOAt4DJXxAPUmV2ZXJ0IHRvIFNhdmVkXxAWcmV2ZXJ0RG9jdW1l +bnRUb1NhdmVkOtMADgNYA1kDWgXLBcyAu4EBeoEBfNgADgNeA18DYANhA2IDYwNkA2YFzwNoBCcDagNr +A2wEn4C5gQF7gPGAs4C3gQEVXxAbQ2hlY2sgR3JhbW1hciBXaXRoIFNwZWxsaW5nXxAWdG9nZ2xlR3Jh +bW1hckNoZWNraW5nOtcADgQyBdcF2ANYA1kF2QXaBdsF3AXdAnUF3wBVWU5TS2V5UGF0aFlOU0JpbmRp +bmdfEBxOU05pYkJpbmRpbmdDb25uZWN0b3JWZXJzaW9ugQGLgQF+gQGKgQGCgHyBAYnbBeEADgXiBeMF +5AXlBeYF5wXoBekF6gB6BewAegXuAHoF8AXdAHoAegB6BfVfEBpOU0ZpbHRlclJlc3RyaWN0c0luc2Vy +dGlvbl8QFE5TUHJlc2VydmVzU2VsZWN0aW9uXE5TSW5pdGlhbEtleVpOU0VkaXRhYmxlXk5TRGVjbGFy +ZWRLZXlzXk5TSW5pdGlhbFZhbHVlXxAiTlNDbGVhcnNGaWx0ZXJQcmVkaWNhdGVPbkluc2VydGlvbl8Q +GE5TU2VsZWN0c0luc2VydGVkT2JqZWN0c18QFk5TQXZvaWRzRW1wdHlTZWxlY3Rpb25fEBFOU1NvcnRE +ZXNjcmlwdG9ycwmBAYgJgQGBCYEBf4EBggkJCYEBg9IADgA+AGkF+IA0owX5Be4F3YEBgIEBgYEBglRr +ZXlzU2tleVV2YWx1ZdIADgA+BgAGAYEBh6EGAoEBhNQADgYEBgUGBgYHBe4GCQB6VU5TS2V5Wk5TU2Vs +ZWN0b3JbTlNBc2NlbmRpbmeBAYaBAYGBAYUJWGNvbXBhcmU60gA3ADgGDQYOogYOADtfEBBOU1NvcnRE +ZXNjcmlwdG9y0gA3ADgGEAE4ogE4ADvSADcAOAYSBhOlBhMGFAYVBhYAO18QFk5TRGljdGlvbmFyeUNv +bnRyb2xsZXJfEBFOU0FycmF5Q29udHJvbGxlcl8QEk5TT2JqZWN0Q29udHJvbGxlclxOU0NvbnRyb2xs +ZXJfEBp2YWx1ZTogYXJyYW5nZWRPYmplY3RzLmtleV8QE2FycmFuZ2VkT2JqZWN0cy5rZXnSADcAOAYa +BhujBhsDiwA7XxAVTlNOaWJCaW5kaW5nQ29ubmVjdG9y1wAOBDIF1wXYA1gDWQXZBdoENAYfBiAF2wYi +AFWBAYuA9IEBj4EBjoEBfoEBjV8QGWNvbnRlbnREaWN0aW9uYXJ5OiB1c2VyTlNfEBFjb250ZW50RGlj +dGlvbmFyeVZ1c2VyTlPXAA4EMgXXBdgDWANZBdkF2gXbBikF3QJ2BiwAVYEBi4EBfoEBkoEBgoCLgQGR +XxAcdmFsdWU6IGFycmFuZ2VkT2JqZWN0cy52YWx1ZV8QFWFycmFuZ2VkT2JqZWN0cy52YWx1ZdcADgQy +BdcF2ANYA1kF2QXaBQoGMgYzBdsGNQBVgQGLgQE7gQGWgQGVgQF+gQGUXxApZmlsdGVyUHJlZGljYXRl +OiB3b3Jrc3BhY2VGaWx0ZXJQcmVkaWNhdGVfEA9maWx0ZXJQcmVkaWNhdGVfEBh3b3Jrc3BhY2VGaWx0 +ZXJQcmVkaWNhdGXXAA4EMgXXBdgDWANZBdkF2gQ0BjwGPQBsBj8AVYEBi4D0gQGagQGZgKOBAZhfEBlh +bmltYXRlOiB3YWl0aW5nRm9yRW5naW5lV2FuaW1hdGVfEBB3YWl0aW5nRm9yRW5naW5l0gAOAD4GAAZF +gQGHrxBnA1sGRwTfAkQCdQZLBIoEQQUgBW8GUATRBbAFywZUBFYFLgZXBYYGWQIKA20GXATtBl4GXwS3 +BmEGYgBNBdsAawVVBHsFoAZpBmoAyAUKBGEGbgSpBnAAbAZyBBUD9QIaA8gAfwPqAnYEIgZ7BJcGfQOO +Bn8GgAV5AioGgwSfAhAETgPSBogEBwCqBb4D4QaNBo4D/QLAA64AfgaTBG4FEwTEAKMFXQT7BpoGmwOg +BTYDlgafBWcEKgPABDQFQQKDAEEAsQaoBqkFSAO2BqyAr4EBnIEBLIB0gHyBAaGBARCA+YEBQ4EBW4EB +poEBJ4EBcYEBeoEBqoD+gQFIgQHDgQFjgQHwgG6AsIEB2YEBMYEBv4EBz4EBH4EB5oEB5IALgQF+gA6B +AVSBAQuBAWyBAbaBAbWAGIEBO4EBA4EB6oEBGoEBxoCjgQG9gOqA34CUgM+AaoDagIuA7oEBrYEBFIEB +y4C9gQHQgQHugQFfgHKBAbGBARWAloD9gNSBAdGA5YBYgQF2gNmBAdeBAbyA4ICOgMiAEIEB1YEBB4EB +P4EBI4AUgQFVgQE2gQHcgQGygMOBAUmAvoEB4IEBWoDvgM6A9IEBToCDgAeAVIEBuYEByoEBUIDJgQHJ +2gAOBq4DXgNfA2ADYQNiA2MDZAGgA2YEKgQtA2gEJwNqA2sDbAPIBrZZTlNTdWJtZW51gLmA74EBnYDx +gLOAt4DPgQGeVlNwZWVjaF5zdWJtZW51QWN0aW9uOtIADgA+AGkGu4A0ogVIBCKBAVCA7tIANwA4Br8D +ZKIDZAA72gAOBq4DXgNfA2ADYQNiA2MDZAGgA2YFbwVyA2gEJwNqA2sDbAO2BsiAuYEBW4EBooDxgLOA +t4DJgQGjW09wZW4gUmVjZW500gAOAD4AaQbMgDShBWeBAVpfEBZfTlNSZWNlbnREb2N1bWVudHNNZW51 +2gAOBq4DXgNfA2ADYQNiA2MDZAGgA2YDbQNxA2gEJwNqA2sDbAPIBteAuYCwgQGngPGAs4C3gM+BAahd +U3Vic3RpdHV0aW9uc9IADgA+AGkG24A0owOgA1sE+4DDgK+BATbUAA4DXgHVA28DcAbhBuIG44EBoIEB +q4EB9IEBrFlBTWFpbk1lbnXSAA4APgBpBueANKcGewZeBn0GnwZhBm4GWYEBrYEBv4EBy4EB4IEB5oEB +6oEB8NoADgauA14DXwNgA2EDYgNjA2QBoANmA+oD7QNoBCcDagNrA2wGVAb3gLmA2oEBroDxgLOAt4EB +qoEBr18QD0lQeXRob24xU2FuZGJveNIADgA+AGkG+4A0qwSKBoMGmwZqBmkGjgS3A+EEbgZyBO2BARCB +AbGBAbKBAbWBAbaBAbyBAR+A2YEBB4EBvYEBMdoADgNeA18HCANgBwkDYQNiA2MDZANmBCcDaAB6BCcA +egNqA2sDbAPqXU5TSXNTZXBhcmF0b3JcTlNJc0Rpc2FibGVkgLmA8QmA8QmAs4C3gNrYAA4DXgNfA2AD +YQNiA2MDZANmBxQDaAcVA2oDawNsA+qAuYEBs4EBtICzgLeA2mwAUAByAGUAZgBlAHIAZQBuAGMAZQBz +ICZRLNoADgNeA18HCANgBwkDYQNiA2MDZANmBCcDaAB6BCcAegNqA2sDbAPqgLmA8QmA8QmAs4C3gNra +AA4GrgNeA18DYANhA2IDYwNkAaADZgaoBycDaAQnA2oDawNsA+oHLIC5gQG5gQG3gPGAs4C3gNqBAbhY +U2VydmljZXPUAA4DXgHVA28DcAcnBzEHMoEBoIEBt4EBu4EButIADgA+AGkHNYA0oF8QD19OU1NlcnZp +Y2VzTWVuddoADgNeA18HCANgBwkDYQNiA2MDZANmBCcDaAB6BCcAegNqA2sDbAPqgLmA8QmA8QmAs4C3 +gNraAA4DXgNfBwgDYAcJA2EDYgNjA2QDZgQnA2gAegQnAHoDagNrA2wD6oC5gPEJgPEJgLOAt4DaXF9O +U0FwcGxlTWVuddoADgauA14DXwNgA2EDYgNjA2QBoANmA7YDuQNoBCcDagNrA2wGVAdSgLmAyYEBwIDx +gLOAt4EBqoEBwVRGaWxl0gAOAD4AaQdWgDSrBlcGcAZLBqwEewOuBN8FvgapBaAE0YEBw4EBxoEBoYEB +yYEBC4DIgQEsgQF2gQHKgQFsgQEn2AAOA14DXwNgA2EDYgNjA2QDZgdkA2gHZQNqA2sDbAO2gLmBAcSB +AcWAs4C3gMlTTmV3UW7YAA4DXgNfA2ADYQNiA2MDZANmB20DaAduA2oDawNsA7aAuYEBx4EByICzgLeA +yWUATwBwAGUAbiAmUW/aAA4DXgNfBwgDYAcJA2EDYgNjA2QDZgQnA2gAegQnAHoDagNrA2wDtoC5gPEJ +gPEJgLOAt4DJ2gAOA14DXwcIA2AHCQNhA2IDYwNkA2YEJwNoAHoEJwB6A2oDawNsA7aAuYDxCYDxCYCz +gLeAydoADgauA14DXwNgA2EDYgNjA2QBoANmA8gDywNoBCcDagNrA2wGVAeOgLmAz4EBzIDxgLOAt4EB +qoEBzVRFZGl00gAOAD4AaQeSgDStA8AD0gZfBYYFIAQHBEEEqQZ/BogGmgZQBkeAzoDUgQHPgQFjgQFD +gOWA+YEBGoEB0IEB0YEB3IEBpoEBnNoADgNeA18HCANgBwkDYQNiA2MDZANmBCcDaAB6BCcAegNqA2sD +bAPIgLmA8QmA8QmAs4C3gM/aAA4DXgNfBwgDYAcJA2EDYgNjA2QDZgQnA2gAegQnAHoDagNrA2wDyIC5 +gPEJgPEJgLOAt4DP2gAOBq4DXgNfA2ADYQNiA2MDZAGgA2YDlgOZA2gEJwNqA2sDbAPIB7qAuYC+gQHS +gPGAs4C3gM+BAdNURmluZNIADgA+AGkHvoA0pQQVBpMGjQZcA46A6oEB1YEB14EB2YC92QAOA14DXwNg +A2EDYgNjA2QDZQNmB8YDaANpA2oDawNsA5YAVYC5gQHWgLKAs4C3gL5ZRmluZCBOZXh02QAOA14DXwNg +A2EDYgNjA2QDZQNmB84D1wUAA2oDawNsA5YBWIC5gQHYgQE4gLOAt4C+XUZpbmQgUHJldmlvdXPZAA4D +XgNfA2ADYQNiA2MDZANlA2YH1gNoB9cDagNrA2wDlgfbgLmBAdqBAduAs4C3gL4QB18QFlVzZSBTZWxl +Y3Rpb24gZm9yIEZpbmRRZdoADgauA14DXwNgA2EDYgNjA2QBoANmBJ8EogNoBCcDagNrA2wDyAfmgLmB +ARWBAd2A8YCzgLeAz4EB3l8QFFNwZWxsaW5nIGFuZCBHcmFtbWFy0gAOAD4AaQfqgDSkBbAElwTEBcuB +AXGBARSBASOBAXraAA4GrgNeA18DYANhA2IDYwNkAaADZgP9BAADaAQnA2oDawNsBlQH94C5gOCBAeGA +8YCzgLeBAaqBAeJWRm9ybWF00gAOAD4AaQf7gDSiBmID9YEB5IDf2AAOA14DXwNgA2EDYgNjA2QDZggA +A2gFMwNqA2sDbAP9gLmBAeWBAUuAs4C3gOBaU2hvdyBGb250c9oADgauA14DXwNgA2EDYgNjA2QBoANm +BTYFOQNoBCcDagNrA2wGVAgOgLmBAUmBAeeA8YCzgLeBAaqBAehUVmlld9IADgA+AGkIEoA0ogUuBXmB +AUiBAV/aAA4GrgNeA18DYANhA2IDYwNkAaADZgRWBFkDaAQnA2oDawNsBlQIHYC5gP6BAeuA8YCzgLeB +AaqBAexWV2luZG930gAOAD4AaQghgDSkBE4FEwaABGGA/YEBP4EB7oEBA9oADgNeA18HCANgBwkDYQNi +A2MDZANmBCcDaAB6BCcAegNqA2sDbARWgLmA8QmA8QmAs4C3gP5eX05TV2luZG93c01lbnXaAA4GrgNe +A18DYANhA2IDYwNkAaADZgVdBWADaAQnA2oDawNsBlQIOIC5gQFVgQHxgPGAs4C3gQGqgQHyVEhlbHDS +AA4APgBpCDyANKEFVYEBVFtfTlNNYWluTWVuddIADgA+BgAIQYEBh68QZwNtA8gDtgIKAioDtgPqA8gD +yAZLA8gDtgSfBJ8AHwZuBTYDtgPIBlQAfwZQA5YD6gZUA8gD6gZUA/0AQQAfAE0FXQO2A7YD6gPqAKMA +HwRWBlQDyAO2AE0D6gOWA/0CCgZ9AGsGewIqBCoGVASfBlQDlgPIBFYFNgIKA+oGmgIKBFYDyAPIA8gA +owO2A+oDlgPqBp8CdgO2AGsDlgPqBFYEnwB+BlkDbQPIA+oDbQZhBogGVAVvBkcDyAAfAB8CdQAfAKMG +aQO2BCoGXgO2gLCAz4DJgG6AcoDJgNqAz4DPgQGhgM+AyYEBFYEBFYACgQHqgQFJgMmAz4EBqoBqgQGm +gL6A2oEBqoDPgNqBAaqA4IAHgAKAC4EBVYDJgMmA2oDagBSAAoD+gQGqgM+AyYALgNqAvoDggG6BAcuA +DoEBrYBygO+BAaqBARWBAaqAvoDPgP6BAUmAboDagQHcgG6A/oDPgM+Az4AUgMmA2oC+gNqBAeCAi4DJ +gA6AvoDagP6BARWAEIEB8ICwgM+A2oCwgQHmgQHRgQGqgQFbgQGcgM+AAoACgHyAAoAUgQG2gMmA74EB +v4DJ0gAOAD4GAAirgQGHrxBoA1sGRwTfAkQCdQZLBIoEQQUgBW8E0QZQBbAFywUuBlQEVgZXBYYGWQIK +A20GXATtBl4GXwS3BmEGYgBNBdsAawVVBHsFoAZpBmoAyAUKBGEGbgZwBKkAbAZyBBUD9QIaA8gD6gB/ +BCICdgZ7BJcGfQOOBn8GgAV5AioGgwSfAhAETgPSBogEBwCqBb4D4QaNBo4D/QLAA64AfgaTBG4FEwTE +AKMFXQT7AB8GmgabBTYDoAafA5YFZwQqBDQDwAVBAoMAQQCxBqkGqAVIA7YGrICvgQGcgQEsgHSAfIEB +oYEBEID5gQFDgQFbgQEngQGmgQFxgQF6gQFIgQGqgP6BAcOBAWOBAfCAboCwgQHZgQExgQG/gQHPgQEf +gQHmgQHkgAuBAX6ADoEBVIEBC4EBbIEBtoEBtYAYgQE7gQEDgQHqgQHGgQEagKOBAb2A6oDfgJSAz4Da +gGqA7oCLgQGtgQEUgQHLgL2BAdCBAe6BAV+AcoEBsYEBFYCWgP2A1IEB0YDlgFiBAXaA2YEB14EBvIDg +gI6AyIAQgQHVgQEHgQE/gQEjgBSBAVWBATaAAoEB3IEBsoEBSYDDgQHggL6BAVqA74D0gM6BAU6Ag4AH +gFSBAcqBAbmBAVCAyYEBydIADgA+BgAJFoEBh68QaAkXCRgJGQkaCRsJHAkdCR4JHwkgCSEJIgkjCSQJ +JQkmCScJKAkpCSoJKwksCS0JLgkvCTAJMQkyCTMJNAk1CTYJNwk4CTkJOgk7CTwFDgk+CT8JQAlBCUIJ +QwlECUUJRglHCUgJSQlKCUsJTAlNCU4JTwlQCVEJUglTCVQJVQlWCVcJWAlZCVoJWwlcCV0JXglfCWAJ +YQliCWMJZAllCWYJZwloCWkJaglrCWwJbQluCW8JcAlxCXIJcwl0CXUJdgl3CXgJeQl6CXsJfAl9CX6B +AfiBAfmBAfqBAfuBAfyBAf2BAf6BAf+BAgCBAgGBAgKBAgOBAgSBAgWBAgaBAgeBAgiBAgmBAgqBAguB +AgyBAg2BAg6BAg+BAhCBAhGBAhKBAhOBAhSBAhWBAhaBAheBAhiBAhmBAhqBAhuBAhyBAh2BATyBAh6B +Ah+BAiCBAiGBAiKBAiOBAiSBAiWBAiaBAieBAiiBAimBAiqBAiuBAiyBAi2BAi6BAi+BAjCBAjGBAjKB +AjOBAjSBAjWBAjaBAjeBAjiBAjmBAjqBAjuBAjyBAj2BAj6BAj+BAkCBAkGBAkKBAkOBAkSBAkWBAkaB +AkeBAkiBAkmBAkqBAkuBAkyBAk2BAk6BAk+BAlCBAlGBAlKBAlOBAlSBAlWBAlaBAleBAliBAlmBAlqB +AluBAlyBAl2BAl5fEBhNZW51IEl0ZW0gKFNtYXJ0IFF1b3RlcylfEBJNZW51IEl0ZW0gKFNwZWVjaClR +OF8QEVRhYmxlIEhlYWRlciBWaWV3XxAXVGFibGUgQ29sdW1uIChWYXJpYWJsZSlfEBdNZW51IEl0ZW0g +KE9wZW4gUmVjZW50KV8QIU1lbnUgSXRlbSAoQWJvdXQgSVB5dGhvbjFTYW5kYm94KV8QEk1lbnUgSXRl +bSAoRGVsZXRlKV8QEE1lbnUgSXRlbSAoQ29weSlfEBJNZW51IChPcGVuIFJlY2VudClRNl8QGU1lbnUg +SXRlbSAoU3Vic3RpdHV0aW9ucylvEBoATQBlAG4AdQAgAEkAdABlAG0AIAAoAFMAaABvAHcAIABTAHAA +ZQBsAGwAaQBuAGcgJgApXxAnTWVudSBJdGVtIChDaGVjayBHcmFtbWFyIFdpdGggU3BlbGxpbmcpXxAY +TWVudSBJdGVtIChTaG93IFRvb2xiYXIpWE1haW5NZW51XU1lbnUgKFdpbmRvdylROV8QD01lbnUgSXRl +bSAoQ3V0KVExW1Njcm9sbCBWaWV3XxAUTWVudSAoU3Vic3RpdHV0aW9ucylfECJNZW51IEl0ZW0gKFVz +ZSBTZWxlY3Rpb24gZm9yIEZpbmQpVDExMTFfEBBNZW51IEl0ZW0gKEZpbGUpW1NlcGFyYXRvci01XxAg +TWVudSBJdGVtIChIaWRlIElQeXRob24xU2FuZGJveClfEBBNZW51IEl0ZW0gKFZpZXcpXxAWTWVudSBJ +dGVtIChTaG93IEZvbnRzKVxDb250ZW50IFZpZXdfEBlVc2VyIE5hbWVzcGFjZSBDb250cm9sbGVyWlNw +bGl0IFZpZXdfECBNZW51IEl0ZW0gKElQeXRob24xU2FuZGJveCBIZWxwKVMxLTFRNV8QFE1lbnUgSXRl +bSAoU2VydmljZXMpW1NlcGFyYXRvci0xWVRleHQgVmlld18QHk1lbnUgSXRlbSAoQnJpbmcgQWxsIHRv +IEZyb250KV8QEk1lbnUgSXRlbSAoV2luZG93KW8QEQBNAGUAbgB1ACAASQB0AGUAbQAgACgATwBwAGUA +biAmAClfEBZNZW51IEl0ZW0gKFNlbGVjdCBBbGwpXEFzeW5jIEFycm93c1tTZXBhcmF0b3ItMm8QEQBN +AGUAbgB1ACAASQB0AGUAbQAgACgARgBpAG4AZCAmAClfEBdNZW51IEl0ZW0gKFNob3cgQ29sb3JzKV8Q +EVZlcnRpY2FsIFNjcm9sbGVyW01lbnUgKEVkaXQpXxAWTWVudSAoSVB5dGhvbjFTYW5kYm94KV8QD0Jv +eCAoV29ya3NwYWNlKV8QGU1lbnUgSXRlbSAoU3RvcCBTcGVha2luZylfEBRUYWJsZSBDb2x1bW4gKFZh +bHVlKV8QG01lbnUgSXRlbSAoSVB5dGhvbjFTYW5kYm94KV8QGk1lbnUgSXRlbSAoQ2hlY2sgU3BlbGxp +bmcpXxAQTWVudSBJdGVtIChFZGl0KV8QHU1lbnUgSXRlbSAoSnVtcCB0byBTZWxlY3Rpb24pW1NlcGFy +YXRvci02WVNlcGFyYXRvcm8QHgBNAGUAbgB1ACAASQB0AGUAbQAgACgAQwB1AHMAdABvAG0AaQB6AGUA +IABUAG8AbwBsAGIAYQByICYAKV8QHFRhYmxlIFZpZXcgKFZhcmlhYmxlLCBWYWx1ZSlbU2VwYXJhdG9y +LTNfEBtNZW51IChTcGVsbGluZyBhbmQgR3JhbW1hcilfEBNIb3Jpem9udGFsIFNjcm9sbGVyXxAUTWVu +dSBJdGVtIChNaW5pbWl6ZSlfEBBNZW51IEl0ZW0gKFJlZG8pXxAQTWVudSBJdGVtIChGaW5kKV8QEU1l +bnUgSXRlbSAoUGFzdGUpXxAVSG9yaXpvbnRhbCBTY3JvbGxlci0xUjEwXxAXTWVudSBJdGVtIChIaWRl +IE90aGVycylfEBlNZW51IEl0ZW0gKEZpbmQgUHJldmlvdXMpW1NlcGFyYXRvci00XU1lbnUgKEZvcm1h +dClfEB1UZXh0IEZpZWxkIENlbGwgKFRleHQgQ2VsbCktMVEzXUJveCAoQ29uc29sZSlfEBVNZW51IEl0 +ZW0gKEZpbmQgTmV4dClfEBRNZW51IEl0ZW0gKFNob3cgQWxsKV8QEE1lbnUgSXRlbSAoWm9vbSlfECdN +ZW51IEl0ZW0gKENoZWNrIFNwZWxsaW5nIFdoaWxlIFR5cGluZyldU2Nyb2xsIFZpZXctMVEyXxAXTWVu +dSBJdGVtIChTbWFydCBMaW5rcylcRmlsZSdzIE93bmVyXxAgTWVudSBJdGVtIChTcGVsbGluZyBhbmQg +R3JhbW1hcilTMTIxW01lbnUgKFZpZXcpXxAcTWVudSBJdGVtIChTbWFydCBDb3B5L1Bhc3RlKV8QEk1l +bnUgSXRlbSAoRm9ybWF0KVtNZW51IChGaW5kKV8QFk1lbnUgSXRlbSAoQ2xlYXIgTWVudSldTWVudSAo +U3BlZWNoKV8QF1B5dGhvbiBDb2NvYSBDb250cm9sbGVyXxAQTWVudSBJdGVtIChVbmRvKVtBcHBsaWNh +dGlvbl8QG1RleHQgRmllbGQgQ2VsbCAoVGV4dCBDZWxsKV8QGVdpbmRvdyAoSVB5dGhvbjEgKENvY29h +KSlfEBNWZXJ0aWNhbCBTY3JvbGxlci0xUzItMV8QD01lbnUgKFNlcnZpY2VzKV8QGk1lbnUgSXRlbSAo +U3RhcnQgU3BlYWtpbmcpW01lbnUgKEZpbGUpUTfSAA4APgYACeiBAYeg0gAOAD4GAAnrgQGHoNIADgA+ +BgAJ7oEBh68QlwNUA1sGRwNBA0kE3wJEAnUGSwSKBEEFIAVvA0IGUATRA0QDMgNOBbADMQNRA0sFywM+ +AzoGVARWBS4GVwNNBYYGWQMwAgoDbQZcBO0GXgZfAzkDOAS3AywDTAZhBmIDLwBNBdsDQAM0AGsFVQR7 +BaAGaQZqAMgDKwUKBGEGbgSpBnAAbANHBnIEFQP1AhoDyAB/A+oCdgQiAz8GewSXBn0DjgZ/A0oDVgaA +A1UFeQIqA0MGgwSfAhADKgROA9IGiAQHAKoDOwNIBb4D4QaNAzYDPAaOA0UD/QMoAsADrgMzAH4GkwRu +BRMExACjA1IDKQVdBPsAHwaaBpsDoAU2A5YGnwMuBWcDUAQqA8AENANGBUEDNQKDA08AQQCxBqgGqQM3 +Az0DUwMtBUgDtgasgQGQgK+BAZyBATWBAVmBASyAdIB8gQGhgQEQgPmBAUOBAVuBATqBAaaBASeBAUKA +7YEBa4EBcYDpgQF5gQFigQF6gQEmgQETgQGqgP6BAUiBAcOBAWmBAWOBAfCA5IBugLCBAdmBATGBAb+B +Ac+BAQ+BAQqBAR+AzYEBZ4EB5oEB5IDegAuBAX6BATCA+IAOgQFUgQELgQFsgQG2gQG1gBiAx4EBO4EB +A4EB6oEBGoEBxoCjgQFPgQG9gOqA34CUgM+AaoDagIuA7oEBK4EBrYEBFIEBy4C9gQHQgQFegQGXgQHu +gQGTgQFfgHKBAT6BAbGBARWAloDCgP2A1IEB0YDlgFiBARmBAVOBAXaA2YEB14EBAoEBHoEBvIEBR4Dg +gK6AjoDIgPOAEIEB1YEBB4EBP4EBI4AUgQF9gLyBAVWBATaAAoEB3IEBsoDDgQFJgL6BAeCA2IEBWoEB +dYDvgM6A9IEBTYEBToD8gIOBAXCAB4BUgQG5gQHKgQEGgQEigQGMgNOBAVCAyYEBydIADgA+BgAKiIEB +h68QlwqJCooKiwqMCo0KjgqPCpAKkQqSCpMKlAqVCpYKlwqYCpkKmgqbCpwKnQqeCp8KoAqhCqIKowqk +CqUKpgqnCqgKqQqqCqsKrAqtCq4KrwqwCrEKsgqzCrQKtQq2CrcKuAq5CroKuwq8Cr0Kvgq/CsAKwQrC +CsMKxArFCsYKxwrICskKygrLCswKzQrOCs8K0ArRCtIK0wrUCtUK1grXCtgK2QraCtsK3ArdCt4K3wrg +CuEK4grjCuQK5QrmCucK6ArpCuoK6wrsCu0K7grvCvAK8QryCvMK9Ar1CvYK9wr4CvkK+gr7CvwK/Qr+ +Cv8LAAsBCwILAwsECwULBgsHCwgLCQsKCwsLDAsNCw4LDwsQCxELEgsTCxQLFQsWCxcLGAsZCxoLGwsc +Cx0LHgsfgQJjgQJkgQJlgQJmgQJngQJogQJpgQJqgQJrgQJsgQJtgQJugQJvgQJwgQJxgQJygQJzgQJ0 +gQJ1gQJ2gQJ3gQJ4gQJ5gQJ6gQJ7gQJ8gQJ9gQJ+gQJ/gQKAgQKBgQKCgQKDgQKEgQKFgQKGgQKHgQKI +gQKJgQKKgQKLgQKMgQKNgQKOgQKPgQKQgQKRgQKSgQKTgQKUgQKVgQKWgQKXgQKYgQKZgQKagQKbgQKc +gQKdgQKegQKfgQKggQKhgQKigQKjgQKkgQKlgQKmgQKngQKogQKpgQKqgQKrgQKsgQKtgQKugQKvgQKw +gQKxgQKygQKzgQK0gQK1gQK2gQK3gQK4gQK5gQK6gQK7gQK8gQK9gQK+gQK/gQLAgQLBgQLCgQLDgQLE +gQLFgQLGgQLHgQLIgQLJgQLKgQLLgQLMgQLNgQLOgQLPgQLQgQLRgQLSgQLTgQLUgQLVgQLWgQLXgQLY +gQLZgQLagQLbgQLcgQLdgQLegQLfgQLggQLhgQLigQLjgQLkgQLlgQLmgQLngQLogQLpgQLqgQLrgQLs +gQLtgQLugQLvgQLwgQLxgQLygQLzgQL0gQL1gQL2gQL3gQL4gQL5EQGrEQFfENMRAWUQfxBQEQGYEQGc +EHwQOhDKEMUQfREBuREBXBBOEOAQ4xBXEMwQ8REBWxDkEQFaEFYQ4RAdEBgRASkQUhEBvRDHEGcQ4hEB +lxEBXRDdEIgQUxDOEI4QwRCGEN8RAbwRAScRAVgRAWkRAXQRAYERAXEQ6xEBpRBvEEkQTRCDEI8RAaMR +AWoRAXUQBRATEMYQSBEBtBDpEJUQ0REBWREBmRDNEQGWEDkRAZ0QwxEBaxA4EMkQ2RDSENYRAW0RAbUQ +XBEBuBEBKhEBmxDwEOwQyBEBmhEBYxAXENcQ2hDLEQGiEOgRAWgQcBCRENUQJxEBbxCQEQFuEQEsEQFk +EQGeEEsRAa0RAaQQ0BCWEO8Q2xEBoBEBrBD1EGoRAWIRAb4Q2BCBEQFeEQEoENwRASsRAXAQfhEBbBDU +EM8RAaYRAXYT//////////0QJREBnxDmEQFzEQGhEIIQShEBchDeEQGoEOcQxBBREE/SAA4APgBpC7mA +NKDSAA4APgYAC7yBAYeg0gAOAD4GAAu/gQGHoNIANwA4C8ELwqILwgA7Xk5TSUJPYmplY3REYXRhAAgA +GQAiACcAMQA6AD8ARABSAFQAZgZmBmwGtwa+BsUG0wblBwEHDwcbBycHNQdAB04Hagd4B4sHnQe3B8EH +zgfQB9MH1gfZB9wH3gfhB+MH5gfpB+wH7wfxB/MH9gf5B/wH/wgICBQIFggYCCYILwg4CEMISAhXCGAI +cwh8CIcIiQiMCI4IuwjICNUI6wj5CQMJEQkeCTAJRAlQCVIJVAlWCVgJWglfCWEJYwllCWcJaQmECZcJ +oAm9Cc8J2gnjCe8J+wn9Cf8KAQoECgYKCAoKChMKFQoaChwKHgpHCk8KXgptCnoKfAp+CoAKggqFCocK +iQqLCowKlQqXCpwKngqgCtkK4wrvCv0LCgsUCyYLNAs2CzgLOgs8Cz0LPwtBC0MLRQtHC0kLSwtNC1YL +WAtbC10Legt8C34LgAuCC4QLhguPC5ELlAuWC8cL0wvcC+gL9gv4C/oL/Av+DAEMAwwFDAcMCQwLDA0M +FgwYDB8MIQwjDCUMWgxjDGwMdgyADIoMjAyODJAMkgyUDJYMmAybDJ0MnwyhDKMMpQyuDLAMswy1DOoM +/A0GDRMNHw0pDTINPQ0/DUENQw1FDUcNSQ1LDU4NUA1SDVQNVg1YDWENYw2IDYoNjA2ODZANkg2UDZYN +mA2aDZwNng2gDaINpA2mDagNqg3GDdsN+A4ZDjUOWw6BDp8Ouw7XDvQPDA8mD1oPdw+TD8APyQ/QD90P +4w/6EA8QGRAkECwQPhBAEEIQSxBNEGIQdRCDEI0QjxCREJMQlRCiEKsQrRCvELEQuhDEEMYQxxDQENcQ +6RDyEPsRFxEsETURNxE6ETwRRRFMEVsRYxFsEXERehF/EaARqBHCEdUR6RIAEhUSKBIqEi8SMRIzEjUS +NxI5EjsSSBJVElsSXRJ4EoEShhKOEpsSoxKlEqcSqhK3Er8SwRLGEsgSyhLPEtES0xLoEvQTAhMEEwYT +CBMKExETLxM8Ez4TShNfE2ETYxNlE2cTexOEE4kTlhOjE6UTqhOsE64TsxO1E7cTwxPQE9IT2RPiE+cT +/hQLFBMUHBQnFC4UNRRBFFgUcBR9FH8UghSPFJkUphSoFKoUshS7FMAUyRTSFN0VAhULFRQVHhUgFSIV +JBUmFS8VMRUzFTUVPhVWFWMVbBV3FYIVjBW5FcQVxhXIFcoVzBXOFdAV0hXbFeQV/xYYFiEWKhY3Fk4W +VxZeFmkWcBaNFpkWpBauFrsWxxbMFs4W0BbSFtQW1hbeFu8W9hb9FwYXCBcRFxMXFhcjFywXMRc4F00X +TxdRF1MXVRdrF3gXeheIF5EXmhesF7kXwBfJF9IX2BgRGBMYFRgXGBkYGhgcGB4YIBgiGCQYJhgvGDEY +NBg2GFMYVRhXGFkYWxhdGF8YaBhqGG0YbxiuGLsYzhjbGN0Y3xjhGOMY5RjnGOkY6xj+GQAZAhkEGQYZ +CBkRGRMZHhkgGSIZJBkmGSgZVRlXGVkZWxldGV8ZYRljGWUZZxlwGXIZdRl3Gc4Z8Bn6GgcaHBo2GlIa +bRp3GoMalRqkGsMazxrRGtMa3BreGuAa4RrjGuwa9Rr3Gvga+hr8Gv4bABsJGxQbMRs9Gz8bQRtDG0Ub +RxtJG3YbeBt6G3wbfhuAG4IbhBuGG4gbkhubG6QbuBvRG9Mb1RvXG9kb2xvyG/scBBwSHBscHRwiHCQc +JhxPHF4caxx2HIUckBybHKgcqRyrHK0cthy4HMEcyhzLHM0c6hzvHPEc8xz1HPcc+R0CHQ8dER0dHTId +NB02HTgdOh1MHVUdYB10HZUdox2oHaodrB2uHbAdsh21HbcdwR3SHdQd3R3fHeId9x35Hfsd/R3/Hhge +LR4vHjEeMx41HkgeUR5WHmQejR6OHpAekh6bHp0enh6gHr0evx7BHsMexR7HHs0e7h7wHvIe9B72Hvge ++h8PHxEfEx8VHxcfIR8uHzAfNR8+H0kfYR+GH4gfih+MH44fkB+SH5QfnR+2H98f4R/jH+Uf5x/pH+sf +7R/2IA4gFyAZIBwgHiA0IE0gZCB9IJognCCeIKAgoiCkIK4guyC9INYg+SECIQshFyFAIUshViFgIW0h +byFxIXMhfCGFIYghiiGNIY8hkSGWIZghoSGmIbEhySHSIdsh8SH8IhQiJyIwIjUiSCJRIlMitCK2Irgi +uiK8Ir4iwCLCIsQixiLIIsoizCLOItAi0yLWItki3CLfIuIi5SLoIusi7iLxIvQi9yL6Iv0jACMDIwYj +CSMMIw8jEiMVIxgjGyMeIyEjJCMnIyojLSMwIzMjQCNJI1EjUyNVI1cjfCOEI5gjoyOxI7sjyCPPI9Uj +1yPZI94j4CPlI+cj6SPrI/gkBCQHJAokDSQaJBwkKSQ4JDokPCQ+JEYkWCRhJGYkeSSGJIgkiiSMJJ8k +qCStJLgk3CTlJOwlBCUTJSAlIiUkJSYlRyVJJUslTSVPJVElUyVgJWMlZiVpJX0lfyWfJawlriWwJbIl +1yXZJdsl3SXfJeEl4yX2JfgmEyYgJiImJCYmJkcmSSZLJk0mTyZRJlMmYCZjJmYmaSZuJnAmfiaLJo0m +jyaRJrImtCa2Jrgmuia8Jr4myybOJtEm1CbZJtsm4SbuJvAm8ib0JxUnFycZJx4nICciJyQnJicrJy0n +MydAJ0InRCdGJ2cnaSdrJ3Ancid0J3YneCeJJ4wnjyeSJ5UnoSejJ7wnySfLJ80nzyfwJ/In9Cf2J/gn ++if8KAkoDCgPKBIoHiggKDgoRShHKEkoSyhsKG4ocChyKHQodih4KH4ogCiHKJQoliiYKJoovyjBKMMo +xSjHKMkoyyjWKPAo/Sj/KQEpAykkKSYpKCkqKSwpLikwKT0pQClDKUYpVCliKXMpgSmDKYUphymJKZIp +lCmWKa8puCnBKcgp3ynsKe4p8CnyKhMqFSoXKhkqGyodKh8qJiouKjsqPSo/KkIqYyplKmcqaipsKm4q +cCqBKoQqhyqKKo0qliqYKq4quyq9KsAqwyrkKuYq6SrrKu0q7yrxKwYrGCslKycrKistK04rUCtTK1Ur +VytZK1srZCt9K4orjCuPK5Irsyu1K7gruyu9K78rwSvHK8kr1yvoK+or7CvvK/IsDywRLBQsFiwYLBos +HCw0LFQsYSxjLGYsaSyKLIwsjyySLJQsliyZLKYsqSysLK8svizALM8s3CzeLOEs5C0FLQctCi0NLQ8t +ES0TLR4tIC0rLTgtOi09LUAtYS1jLWYtaC1qLWwtbi2FLYstmC2aLZ0toC3BLcMtxi3ILcotzC3PLe0u +Di4bLh0uIC4jLkQuRi5JLkwuTi5QLlIuXy5hLmgudS53LnoufS6eLqAuoy6mLqguqi6sLr0uvy7RLt4u +4C7jLuYvBy8JLwwvDy8RLxMvFS8sLy4vOS9GL0gvSy9OL3MvdS94L3svfS9/L4EvjS+PL68vwC/CL8Qv +xy/KL9Mv1S/YL/UwCTAWMBgwGzAeMD8wQTBEMEYwSDBKMEwwUTBeMGswbTBwMHMwlDCWMJkwnDCeMKAw +ojCnMKkwrzC8ML4wwTDEMOUw5zDqMO0w7zDxMPQxATEEMQcxCjEXMRkxLzFAMUIxRTFIMUoxUzFVMVcx +ZDFmMWkxbDGNMY8xkjGUMZYxmDGaMakxuDHFMccxyjHNMe4x8DHzMfYx+DH6Mf0yCjINMhAyEzIqMiwy +NjJDMkUySDJLMmwybjJxMnMydTJ3MnoyizKOMpEylDKXMqIyujLHMskyzDLPMvAy8jL1Mvcy+TL7Mv4z +JTNHM1QzVjNZM1wzfTN/M4IzhTOHM4kzizOPM5EzljOnM6kzqzOtM7AzuTPKM8wzzjPQM9Mz6zP4M/oz +/TQANCU0LzQxNDM0NjQ5NDs0PTQ/NE00TzReNGs0bTRwNHM0lDSWNJk0nDSeNKA0ozTANMI01DThNOM0 +5jTpNQY1CDULNQ01DzURNRM1JTU+NUs1TTVQNVM1dDV2NXk1ezV9NX81gjWgNbk11jXgNeo2CTYMNg82 +EjYVNhc2GjZHNmQ2ezaINpM2ojaxNtY28TcKNx43HzciNyM3JjcnNyo3LTcuNy83MDczNzw3PjdFN0g3 +SzdON1M3VzddN2Y3aTdsN283gDeGN5E3nTegN6M3pjenN7A3uTe+N9E32jffN+g38zgMOCA4NThCOF84 +dTh+OIU4nTi6OL04vzjCOMU4yDjLOOc4+zkCOR85IjklOSg5KzktOTA5TzlnOYQ5hzmKOY05kDmTOZY5 +wjnUOe86DDoPOhE6FDoXOhk6HDo4OkA6UzpcOl87MDsyOzU7ODs6Ozw7PztCO0Q7RztKO007UDtTO1Y7 +WTtbO147YTtkO2c7aTtrO247cTt0O3c7ejt9O4A7gjuFO4c7ijuNO5A7kzuWO5g7mzueO6E7pDunO6k7 +rDuuO7A7sju0O7Y7uDu6O7w7vzvCO8U7xzvKO8070DvSO9U72DvaO9w73jvhO+M75TvoO+o77TvwO/I7 +9Dv2O/g7+zv+PAE8BDwGPAk8DDwPPBI8FDwXPBk8HDwfPCE8IzwlPCg8KjwsPC48MTw0PDc8OTw8PGU8 +bzxxPHM8djx4PHo8fDx+PIE8iDyXPKA8ojynPKo8rDy1PLo84zzlPOg86zztPO888TzzPPY9Aj0LPQ09 +ED0TPSw9VT1XPVk9XD1ePWA9Yj1kPWc9dT1+PYA9hz2JPYs9jj2fPaI9pT2oPas9tT2+PcA9zz3SPdU9 +2D3bPd494T3kPg0+Dz4RPhQ+Fj4YPho+HT4gPjI+Oz49PlQ+Vz5aPl0+YD5jPmY+aT5rPm4+cT50Pp0+ +qz64Pro+vD69Pr8+wD7CPsQ+xj7nPuk+7D7vPvE+8z71Pw4/ED85Pzs/PT8+P0A/QT9DP0U/Rz9wP3I/ +dT94P3o/fD9+P4A/gz+MP50/oD+jP6Y/qT+yP7Q/tT/HP/A/8j/0P/U/9z/4P/o//D/+QCdAKUArQCxA +LkAvQDFAM0A1QEJAa0BtQG9AckB0QHZAeEB7QH5Ag0CMQI5ApUCoQKtArkCxQLRAtkC5QLxAv0DCQMVA +5kDoQOtA7kDwQPJA9ED4QPpBG0EdQSBBI0ElQSdBKUE0QTZBX0FhQWNBZEFmQWdBaUFrQW1BlkGYQZpB +m0GdQZ5BoEGiQaRBzUHPQdFB1EHWQdhB2kHdQeBB5UHuQfBCC0INQg9CEkIVQhhCGkIcQh9CIkIlQihC +K0IuQldCWUJbQlxCXkJfQmFCY0JlQo5CkEKSQpNClUKWQphCmkKcQsVCx0LJQsxCzkLQQtJC1ELXQtxC +5ULnQvJC9EL3QvpC/UL/QyRDJkMpQytDLUMvQzFDO0NgQ2JDZUNoQ2pDbENuQ3xDoUOjQ6ZDqUOrQ61D +r0OxQ8pDzEP1Q/dD+kP9Q/9EAUQDRAVECEQfRChEKkQzRDZEOUQ8RD9EaERqRGxEb0RxRHNEdUR4RHtE +gkSLRI1EkkSVRJdEuES6RL1EwETCRMRExkTRRPpE/ET/RQJFBEUGRQhFC0UORRNFHEUeRSNFJkUpRVJF +VEVWRVlFW0VdRV9FYkVlRWxFdUV3RYBFgkWFRYhFi0W0RbZFuEW5RbtFvEW+RcBFwkXRRfpF/EX/RgJG +BEYGRghGC0YORhNGHEYeRiFGJEYwRjlGPEcNRw9HEUcTRxVHF0cZRxtHHUcfRyJHJEcmRylHLEcuRzFH +NEc2RzhHO0c9R0BHQkdER0dHSUdLR05HUEdSR1RHVkdZR1tHXUdfR2FHY0dlR2dHakdsR25HcEdyR3RH +dkd4R3tHfUeAR4JHhEeHR4pHjUePR5FHk0eWR5hHmkedR59HoUejR6VHp0epR6tHrUevR7FHtEe2R7hH +uke8R75HwEfDR8VHyEfKR8xHzkfQR9NH1kfZR9xH30fhR+NH5UfnR+lH60fuR/BH8kf1R/dIAEgDSNZI +2EjbSN5I4EjiSOVI6EjqSO1I8EjzSPZI+Uj8SP9JAkkESQdJCkkNSQ9JEUkUSRdJGkkdSSBJI0kmSShJ +K0ktSTBJM0k2STlJPEk+SUFJRElHSUpJTUlPSVJJVElWSVhJWklcSV5JYEliSWVJaElrSW1JcElzSXZJ +eEl7SX5JgEmCSYRJh0mJSYtJjkmQSZNJlkmYSZpJnEmeSaFJpEmnSapJrEmvSbJJtEm3SbpJvUm/ScJJ +xEnHSclJy0nNSdBJ0knUSdZJ2UncSd9J4UnkSe1J8ErDSsZKyUrMSs9K0krVSthK20reSuFK5ErnSupK +7UrwSvNK9kr5SvxK/0sCSwVLCEsLSw5LEUsUSxdLGksdSyBLI0smSylLLEsvSzJLNUs4SztLPktBS0RL +R0tKS01LUEtTS1ZLWUtcS19LYktlS2hLa0tuS3FLdEt3S3pLfUuAS4NLhkuJS4xLj0uSS5VLmEubS55L +oUukS6dLqkutS7BLs0u2S7lLvEu/S8JLxUvIS8tLzkvRS9RL10vaS91L4EvjS+ZL6UvsS+9L8kv1S/hL ++0wWTCtMLUxBTFtMdUyZTK5MwUzWTNhM9E0rTVVNcE15TYdNiU2bTZ1NqU3ATeVN6k39TglOLE4/TlhO +ZU6BToxOr06zTrVOzE7YTuJPA08YTz1PVk9jT29PlE+uT8JPzk/nT/lQFVAsUEpQZ1B6UJpQplCwUO9R +DlEaUThRTlFlUXhRi1GfUbdRulHUUfBR/FIKUipSLFI6UlJSaVJ8UqZStFK2UtBS3VMAUwRTEFMvU0RT +UFNpU3dTkVOkU7BTzlPqVABUBFQWVDNUP1RBVEpUTVROVFdUWlRbVGRUZ1WYVZtVnVWgVaNVplWpVatV +rVWwVbNVtVW4VbtVvlXBVcRVx1XJVcxVz1XRVdRV11XaVd1V4FXjVeVV6FXrVe5V8VX0VfZV+FX6Vf1W +AFYDVgZWCVYMVg9WEVYUVhdWGlYcVh5WIVYkViZWKFYrVi5WMVY0VjdWOVY7Vj5WQVZEVkdWSlZMVk9W +UlZUVlZWWFZaVlxWXlZgVmJWZVZoVmtWblZwVnNWdlZ5VnxWf1aCVoRWh1aKVo1Wj1aRVpNWlVaYVppW +nFafVqJWpVanVqpWrVawVrNWtla4VrpWvFa+VsBWwlbFVshWy1bOVtBW01bVVthW21bdVuBW41blVuhW +6lbtVu9W8lb1VvdW+Vb7Vv5XAVcDVwVXCFcKVwxXD1cSVxVXGFcbVx1XIFciVyVXLlcxWGJYZVhoWGtY +blhxWHRYd1h6WH1YgFiDWIZYiViMWI9YkliVWJhYm1ieWKFYpFinWKpYrViwWLNYtli5WLxYv1jCWMVY +yFjLWM5Y0VjUWNdY2ljdWOBY41jmWOlY7FjvWPJY9Vj4WPtY/lkBWQRZB1kKWQ1ZEFkTWRZZGVkcWR9Z +IlklWShZK1kuWTFZNFk3WTpZPVlAWUNZRllJWUxZT1lSWVVZWFlbWV5ZYVlkWWdZalltWXBZc1l2WXlZ +fFl/WYJZhVmIWYtZjlmRWZRZl1maWZ1ZoFmjWaZZqVmsWa9Zslm1WbhZu1m+WcFZxFnHWcpZzVnQWdNZ +1lnZWdxZ31niWeVZ6FnrWe5Z8Vn0WfdZ+ln9WgBaA1oGWglaDFoPWhJaFVoYWhtaHlohWiRaJ1oqWi1a +L1oyWjRaNlo5WjxaPlpAWkJaRFpGWklaTFpOWlBaUlpUWlZaWFpbWl1aYFpiWmRaZlpoWmtabVpwWnJa +dFp2WnlafFp+WoBaglqEWoZaiFqKWoxaj1qSWpVamFqbWp5aoVqjWqZaqFqqWqxarlqwWrNatlq5Wrta +vVq/WsFaxFrGWshaylrNWtBa0lrVWtda2lrcWt9a4VrjWuVa51rpWuxa71rxWvRa91r6Wvxa/lsAWwNb +BlsIWwpbDFsOWxFbE1sWWxhbGlscWx5bIVsjWyZbKVssWy9bMVs0WzdbOVs7Wz1bP1tCW0VbR1tJW0xb +T1tRW1NbVltZW1tbXlthW2NbZltoW2pbbVtwW3lbe1t+W4Bbg1uGW4hbiluNW49bkluUW5ZbmFuaW6Nb +pVumW69bsluzW7xbv1vAW8lbzgAAAAAAAAICAAAAAAAAC8MAAAAAAAAAAAAAAAAAAFvdA + + + diff --git a/IPython/frontend/cocoa/examples/IPython1Sandbox/IPython1Sandbox.xcodeproj/project.pbxproj b/IPython/frontend/cocoa/examples/IPython1Sandbox/IPython1Sandbox.xcodeproj/project.pbxproj new file mode 100644 index 0000000..92dec61 --- /dev/null +++ b/IPython/frontend/cocoa/examples/IPython1Sandbox/IPython1Sandbox.xcodeproj/project.pbxproj @@ -0,0 +1,293 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 44; + objects = { + +/* Begin PBXBuildFile section */ + 77631A270C06C501005415CB /* Python.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77631A260C06C501005415CB /* Python.framework */; }; + 77631A3F0C0748CF005415CB /* main.py in Resources */ = {isa = PBXBuildFile; fileRef = 77631A3E0C0748CF005415CB /* main.py */; }; + 7790198F0C07548A00326F66 /* IPython1SandboxAppDelegate.py in Resources */ = {isa = PBXBuildFile; fileRef = 7790198E0C07548A00326F66 /* IPython1SandboxAppDelegate.py */; }; + 77C8C1F90C07829500965286 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 77C8C1F70C07829500965286 /* MainMenu.xib */; }; + 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; }; + 8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; }; + 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 089C165DFE840E0CC02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = ""; }; + 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; + 13E42FB307B3F0F600E4EEF1 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = ""; }; + 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; + 29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; + 32CA4F630368D1EE00C91783 /* IPython1Sandbox_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IPython1Sandbox_Prefix.pch; sourceTree = ""; }; + 4CA32F870D8879B100311764 /* IPythonCocoaController Tests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "IPythonCocoaController Tests-Info.plist"; sourceTree = ""; }; + 77631A260C06C501005415CB /* Python.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Python.framework; path = /System/Library/Frameworks/Python.framework; sourceTree = ""; }; + 77631A3E0C0748CF005415CB /* main.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = main.py; sourceTree = ""; }; + 7790198E0C07548A00326F66 /* IPython1SandboxAppDelegate.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = IPython1SandboxAppDelegate.py; sourceTree = ""; }; + 77C8C1F80C07829500965286 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/MainMenu.xib; sourceTree = ""; }; + 8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 8D1107320486CEB800E47090 /* IPython1Sandbox.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = IPython1Sandbox.app; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 8D11072E0486CEB800E47090 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */, + 77631A270C06C501005415CB /* Python.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 080E96DDFE201D6D7F000001 /* Classes */ = { + isa = PBXGroup; + children = ( + 7790198E0C07548A00326F66 /* IPython1SandboxAppDelegate.py */, + ); + name = Classes; + sourceTree = ""; + }; + 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */ = { + isa = PBXGroup; + children = ( + 77631A260C06C501005415CB /* Python.framework */, + 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */, + ); + name = "Linked Frameworks"; + sourceTree = ""; + }; + 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */ = { + isa = PBXGroup; + children = ( + 29B97324FDCFA39411CA2CEA /* AppKit.framework */, + 13E42FB307B3F0F600E4EEF1 /* CoreData.framework */, + 29B97325FDCFA39411CA2CEA /* Foundation.framework */, + ); + name = "Other Frameworks"; + sourceTree = ""; + }; + 19C28FACFE9D520D11CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + 8D1107320486CEB800E47090 /* IPython1Sandbox.app */, + ); + name = Products; + sourceTree = ""; + }; + 29B97314FDCFA39411CA2CEA /* IPython1Sandbox */ = { + isa = PBXGroup; + children = ( + 080E96DDFE201D6D7F000001 /* Classes */, + 29B97315FDCFA39411CA2CEA /* Other Sources */, + 29B97317FDCFA39411CA2CEA /* Resources */, + 29B97323FDCFA39411CA2CEA /* Frameworks */, + 19C28FACFE9D520D11CA2CBB /* Products */, + 4CA32F870D8879B100311764 /* IPythonCocoaController Tests-Info.plist */, + ); + name = IPython1Sandbox; + sourceTree = ""; + }; + 29B97315FDCFA39411CA2CEA /* Other Sources */ = { + isa = PBXGroup; + children = ( + 32CA4F630368D1EE00C91783 /* IPython1Sandbox_Prefix.pch */, + 29B97316FDCFA39411CA2CEA /* main.m */, + 77631A3E0C0748CF005415CB /* main.py */, + ); + name = "Other Sources"; + sourceTree = ""; + }; + 29B97317FDCFA39411CA2CEA /* Resources */ = { + isa = PBXGroup; + children = ( + 77C8C1F70C07829500965286 /* MainMenu.xib */, + 8D1107310486CEB800E47090 /* Info.plist */, + 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */, + ); + name = Resources; + sourceTree = ""; + }; + 29B97323FDCFA39411CA2CEA /* Frameworks */ = { + isa = PBXGroup; + children = ( + 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */, + 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 8D1107260486CEB800E47090 /* IPython1Sandbox */ = { + isa = PBXNativeTarget; + buildConfigurationList = C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "IPython1Sandbox" */; + buildPhases = ( + 8D1107290486CEB800E47090 /* Resources */, + 8D11072C0486CEB800E47090 /* Sources */, + 8D11072E0486CEB800E47090 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = IPython1Sandbox; + productInstallPath = "$(HOME)/Applications"; + productName = IPython1Sandbox; + productReference = 8D1107320486CEB800E47090 /* IPython1Sandbox.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 29B97313FDCFA39411CA2CEA /* Project object */ = { + isa = PBXProject; + buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "IPython1Sandbox" */; + compatibilityVersion = "Xcode 3.0"; + hasScannedForEncodings = 1; + mainGroup = 29B97314FDCFA39411CA2CEA /* IPython1Sandbox */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 8D1107260486CEB800E47090 /* IPython1Sandbox */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 8D1107290486CEB800E47090 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */, + 77631A3F0C0748CF005415CB /* main.py in Resources */, + 7790198F0C07548A00326F66 /* IPython1SandboxAppDelegate.py in Resources */, + 77C8C1F90C07829500965286 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 8D11072C0486CEB800E47090 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D11072D0486CEB800E47090 /* main.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + 089C165DFE840E0CC02AAC07 /* English */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; + 77C8C1F70C07829500965286 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 77C8C1F80C07829500965286 /* English */, + ); + name = MainMenu.xib; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + C01FCF4B08A954540054247B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = IPython1Sandbox_Prefix.pch; + INFOPLIST_FILE = Info.plist; + INSTALL_PATH = "$(HOME)/Applications"; + PRODUCT_NAME = IPython1Sandbox; + VERSIONING_SYSTEM = "apple-generic"; + WRAPPER_EXTENSION = app; + ZERO_LINK = YES; + }; + name = Debug; + }; + C01FCF4C08A954540054247B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_MODEL_TUNING = G5; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = IPython1Sandbox_Prefix.pch; + INFOPLIST_FILE = Info.plist; + INSTALL_PATH = "$(HOME)/Applications"; + PRODUCT_NAME = IPython1Sandbox; + VERSIONING_SYSTEM = "apple-generic"; + WRAPPER_EXTENSION = app; + }; + name = Release; + }; + C01FCF4F08A954540054247B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + PREBINDING = NO; + SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk"; + }; + name = Debug; + }; + C01FCF5008A954540054247B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = ( + ppc, + i386, + ); + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + PREBINDING = NO; + SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "IPython1Sandbox" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C01FCF4B08A954540054247B /* Debug */, + C01FCF4C08A954540054247B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C01FCF4E08A954540054247B /* Build configuration list for PBXProject "IPython1Sandbox" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C01FCF4F08A954540054247B /* Debug */, + C01FCF5008A954540054247B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 29B97313FDCFA39411CA2CEA /* Project object */; +} diff --git a/IPython/frontend/cocoa/examples/IPython1Sandbox/IPython1SandboxAppDelegate.py b/IPython/frontend/cocoa/examples/IPython1Sandbox/IPython1SandboxAppDelegate.py new file mode 100644 index 0000000..361b7b5 --- /dev/null +++ b/IPython/frontend/cocoa/examples/IPython1Sandbox/IPython1SandboxAppDelegate.py @@ -0,0 +1,39 @@ +# +# IPython1SandboxAppDelegate.py +# IPython1Sandbox +# +# Created by Barry Wark on 3/4/08. +# Copyright __MyCompanyName__ 2008. All rights reserved. +# + +from Foundation import NSObject, NSPredicate +import objc +import threading + +from PyObjCTools import AppHelper + +from twisted.internet import reactor + +class IPython1SandboxAppDelegate(NSObject): + ipythonController = objc.IBOutlet() + + def applicationShouldTerminate_(self, sender): + if reactor.running: + reactor.addSystemEventTrigger( + 'after', 'shutdown', AppHelper.stopEventLoop) + reactor.stop() + return False + return True + + + def applicationDidFinishLaunching_(self, sender): + reactor.interleave(AppHelper.callAfter) + assert(reactor.running) + + + def workspaceFilterPredicate(self): + return NSPredicate.predicateWithFormat_("NOT (self.value BEGINSWITH '<')") + + + + diff --git a/IPython/frontend/cocoa/examples/IPython1Sandbox/IPython1Sandbox_Prefix.pch b/IPython/frontend/cocoa/examples/IPython1Sandbox/IPython1Sandbox_Prefix.pch new file mode 100644 index 0000000..7ce12e2 --- /dev/null +++ b/IPython/frontend/cocoa/examples/IPython1Sandbox/IPython1Sandbox_Prefix.pch @@ -0,0 +1,7 @@ +// +// Prefix header for all source files of the 'IPython1Sandbox' target in the 'IPython1Sandbox' project +// + +#ifdef __OBJC__ + #import +#endif diff --git a/IPython/frontend/cocoa/examples/IPython1Sandbox/IPythonCocoaController Tests-Info.plist b/IPython/frontend/cocoa/examples/IPython1Sandbox/IPythonCocoaController Tests-Info.plist new file mode 100644 index 0000000..eb983f2 --- /dev/null +++ b/IPython/frontend/cocoa/examples/IPython1Sandbox/IPythonCocoaController Tests-Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + com.yourcompany.IPythonCocoaController Tests + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + BNDL + CFBundleSignature + ???? + CFBundleVersion + 1.0 + + diff --git a/IPython/frontend/cocoa/examples/IPython1Sandbox/Info.plist b/IPython/frontend/cocoa/examples/IPython1Sandbox/Info.plist new file mode 100644 index 0000000..ed102a5 --- /dev/null +++ b/IPython/frontend/cocoa/examples/IPython1Sandbox/Info.plist @@ -0,0 +1,30 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFile + + CFBundleIdentifier + com.yourcompany.IPython1Sandbox + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + 0.1 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/IPython/frontend/cocoa/examples/IPython1Sandbox/main.m b/IPython/frontend/cocoa/examples/IPython1Sandbox/main.m new file mode 100644 index 0000000..dcf8e84 --- /dev/null +++ b/IPython/frontend/cocoa/examples/IPython1Sandbox/main.m @@ -0,0 +1,49 @@ +// +// main.m +// IPython1Sandbox +// +// Created by Barry Wark on 3/4/08. +// Copyright __MyCompanyName__ 2008. All rights reserved. +// + +#import +#import + +int main(int argc, char *argv[]) +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + NSBundle *mainBundle = [NSBundle mainBundle]; + NSString *resourcePath = [mainBundle resourcePath]; + NSArray *pythonPathArray = [NSArray arrayWithObjects: resourcePath, [resourcePath stringByAppendingPathComponent:@"PyObjC"], nil]; + + setenv("PYTHONPATH", [[pythonPathArray componentsJoinedByString:@":"] UTF8String], 1); + + NSArray *possibleMainExtensions = [NSArray arrayWithObjects: @"py", @"pyc", @"pyo", nil]; + NSString *mainFilePath = nil; + + for (NSString *possibleMainExtension in possibleMainExtensions) { + mainFilePath = [mainBundle pathForResource: @"main" ofType: possibleMainExtension]; + if ( mainFilePath != nil ) break; + } + + if ( !mainFilePath ) { + [NSException raise: NSInternalInconsistencyException format: @"%s:%d main() Failed to find the Main.{py,pyc,pyo} file in the application wrapper's Resources directory.", __FILE__, __LINE__]; + } + + Py_SetProgramName("/usr/bin/python"); + Py_Initialize(); + PySys_SetArgv(argc, (char **)argv); + + const char *mainFilePathPtr = [mainFilePath UTF8String]; + FILE *mainFile = fopen(mainFilePathPtr, "r"); + int result = PyRun_SimpleFile(mainFile, (char *)[[mainFilePath lastPathComponent] UTF8String]); + + if ( result != 0 ) + [NSException raise: NSInternalInconsistencyException + format: @"%s:%d main() PyRun_SimpleFile failed with file '%@'. See console for errors.", __FILE__, __LINE__, mainFilePath]; + + [pool drain]; + + return result; +} diff --git a/IPython/frontend/cocoa/examples/IPython1Sandbox/main.py b/IPython/frontend/cocoa/examples/IPython1Sandbox/main.py new file mode 100644 index 0000000..f01104d --- /dev/null +++ b/IPython/frontend/cocoa/examples/IPython1Sandbox/main.py @@ -0,0 +1,24 @@ +# +# main.py +# IPython1Sandbox +# +# Created by Barry Wark on 3/4/08. +# Copyright __MyCompanyName__ 2008. All rights reserved. +# + +#import modules required by application +import objc +import Foundation +import AppKit + +from PyObjCTools import AppHelper + +from twisted.internet import _threadedselect +reactor = _threadedselect.install() + +# import modules containing classes required to start application and load MainMenu.nib +import IPython1SandboxAppDelegate +import IPython.frontend.cocoa.cocoa_frontend + +# pass control to AppKit +AppHelper.runEventLoop() diff --git a/IPython/frontend/cocoa/tests/__init__.py b/IPython/frontend/cocoa/tests/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/IPython/frontend/cocoa/tests/__init__.py diff --git a/IPython/frontend/cocoa/tests/_trial_temp/test.log b/IPython/frontend/cocoa/tests/_trial_temp/test.log new file mode 100644 index 0000000..1fdde15 --- /dev/null +++ b/IPython/frontend/cocoa/tests/_trial_temp/test.log @@ -0,0 +1,27 @@ +2008-03-14 00:06:49-0700 [-] Log opened. +2008-03-14 00:06:49-0700 [-] --> ipython1.frontend.cocoa.tests.test_cocoa_frontend.TestIPythonCocoaControler.testControllerCompletesToken <-- +2008-03-14 00:06:49-0700 [-] Unhandled error in Deferred: +2008-03-14 00:06:49-0700 [-] Unhandled Error + Traceback (most recent call last): + File "/Library/Python/2.5/site-packages/Twisted-2.5.0_rUnknown-py2.5-macosx-10.3-i386.egg/twisted/internet/utils.py", line 144, in runWithWarningsSuppressed + result = f(*a, **kw) + File "/Users/barry/Desktop/ipython1-cocoa/ipython1/frontend/cocoa/tests/test_cocoa_frontend.py", line 94, in testControllerCompletesToken + self.controller.executeRequest([code]).addCallback(testCompletes) + File "/Library/Python/2.5/site-packages/Twisted-2.5.0_rUnknown-py2.5-macosx-10.3-i386.egg/twisted/internet/defer.py", line 196, in addCallback + callbackKeywords=kw) + File "/Library/Python/2.5/site-packages/Twisted-2.5.0_rUnknown-py2.5-macosx-10.3-i386.egg/twisted/internet/defer.py", line 187, in addCallbacks + self._runCallbacks() + --- --- + File "/Library/Python/2.5/site-packages/Twisted-2.5.0_rUnknown-py2.5-macosx-10.3-i386.egg/twisted/internet/defer.py", line 325, in _runCallbacks + self.result = callback(self.result, *args, **kw) + File "/Users/barry/Desktop/ipython1-cocoa/ipython1/frontend/cocoa/tests/test_cocoa_frontend.py", line 89, in testCompletes + self.assert_("longNameVariable" in result) + File "/Library/Python/2.5/site-packages/Twisted-2.5.0_rUnknown-py2.5-macosx-10.3-i386.egg/twisted/trial/unittest.py", line 136, in failUnless + raise self.failureException(msg) + twisted.trial.unittest.FailTest: None + +2008-03-14 00:06:49-0700 [-] --> ipython1.frontend.cocoa.tests.test_cocoa_frontend.TestIPythonCocoaControler.testControllerExecutesCode <-- +2008-03-14 00:06:49-0700 [-] --> ipython1.frontend.cocoa.tests.test_cocoa_frontend.TestIPythonCocoaControler.testControllerInstantiatesIEngineInteractive <-- +2008-03-14 00:06:49-0700 [-] --> ipython1.frontend.cocoa.tests.test_cocoa_frontend.TestIPythonCocoaControler.testControllerMirrorsUserNSWithValuesAsStrings <-- +2008-03-14 00:06:49-0700 [-] --> ipython1.frontend.cocoa.tests.test_cocoa_frontend.TestIPythonCocoaControler.testControllerRaisesCompilerErrorForIllegalCode <-- +2008-03-14 00:06:49-0700 [-] --> ipython1.frontend.cocoa.tests.test_cocoa_frontend.TestIPythonCocoaControler.testControllerReturnsNoneForIncompleteCode <-- diff --git a/IPython/frontend/cocoa/tests/test_cocoa_frontend.py b/IPython/frontend/cocoa/tests/test_cocoa_frontend.py new file mode 100644 index 0000000..eb4ae43 --- /dev/null +++ b/IPython/frontend/cocoa/tests/test_cocoa_frontend.py @@ -0,0 +1,72 @@ +# encoding: utf-8 +"""This file contains unittests for the +IPython.frontend.cocoa.cocoa_frontend module. +""" +__docformat__ = "restructuredtext en" + +#--------------------------------------------------------------------------- +# Copyright (C) 2005 The IPython Development Team +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#--------------------------------------------------------------------------- + +#--------------------------------------------------------------------------- +# Imports +#--------------------------------------------------------------------------- +from IPython.kernel.core.interpreter import Interpreter +import IPython.kernel.engineservice as es +from IPython.testing.util import DeferredTestCase +from twisted.internet.defer import succeed +from IPython.frontend.cocoa.cocoa_frontend import IPythonCocoaController + +from Foundation import NSMakeRect +from AppKit import NSTextView, NSScrollView + +class TestIPythonCocoaControler(DeferredTestCase): + """Tests for IPythonCocoaController""" + + def setUp(self): + self.controller = IPythonCocoaController.alloc().init() + self.engine = es.EngineService() + self.engine.startService() + + + def tearDown(self): + self.controller = None + self.engine.stopService() + + def testControllerExecutesCode(self): + code ="""5+5""" + expected = Interpreter().execute(code) + del expected['number'] + def removeNumberAndID(result): + del result['number'] + del result['id'] + return result + self.assertDeferredEquals( + self.controller.execute(code).addCallback(removeNumberAndID), + expected) + + def testControllerMirrorsUserNSWithValuesAsStrings(self): + code = """userns1=1;userns2=2""" + def testControllerUserNS(result): + self.assertEquals(self.controller.userNS['userns1'], 1) + self.assertEquals(self.controller.userNS['userns2'], 2) + + self.controller.execute(code).addCallback(testControllerUserNS) + + + def testControllerInstantiatesIEngine(self): + self.assert_(es.IEngineBase.providedBy(self.controller.engine)) + + def testControllerCompletesToken(self): + code = """longNameVariable=10""" + def testCompletes(result): + self.assert_("longNameVariable" in result) + + def testCompleteToken(result): + self.controller.complete("longNa").addCallback(testCompletes) + + self.controller.execute(code).addCallback(testCompletes) + diff --git a/IPython/frontend/frontendbase.py b/IPython/frontend/frontendbase.py new file mode 100644 index 0000000..644166a --- /dev/null +++ b/IPython/frontend/frontendbase.py @@ -0,0 +1,352 @@ +# encoding: utf-8 +# -*- test-case-name: IPython.frontend.tests.test_frontendbase -*- +""" +frontendbase provides an interface and base class for GUI frontends for +IPython.kernel/IPython.kernel.core. + +Frontend implementations will likely want to subclass FrontEndBase. + +Author: Barry Wark +""" +__docformat__ = "restructuredtext en" + +#------------------------------------------------------------------------------- +# Copyright (C) 2008 The IPython Development Team +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#------------------------------------------------------------------------------- + +#------------------------------------------------------------------------------- +# Imports +#------------------------------------------------------------------------------- +import string +import uuid +import _ast + +import zope.interface as zi + +from IPython.kernel.core.history import FrontEndHistory +from IPython.kernel.core.util import Bunch +from IPython.kernel.engineservice import IEngineCore + +from twisted.python.failure import Failure + +############################################################################## +# TEMPORARY!!! fake configuration, while we decide whether to use tconfig or +# not + +rc = Bunch() +rc.prompt_in1 = r'In [$number]: ' +rc.prompt_in2 = r'...' +rc.prompt_out = r'Out [$number]: ' + +############################################################################## + +class IFrontEndFactory(zi.Interface): + """Factory interface for frontends.""" + + def __call__(engine=None, history=None): + """ + Parameters: + interpreter : IPython.kernel.engineservice.IEngineCore + """ + + pass + + + +class IFrontEnd(zi.Interface): + """Interface for frontends. All methods return t.i.d.Deferred""" + + zi.Attribute("input_prompt_template", "string.Template instance\ + substituteable with execute result.") + zi.Attribute("output_prompt_template", "string.Template instance\ + substituteable with execute result.") + zi.Attribute("continuation_prompt_template", "string.Template instance\ + substituteable with execute result.") + + def update_cell_prompt(self, result): + """Subclass may override to update the input prompt for a block. + Since this method will be called as a + twisted.internet.defer.Deferred's callback, + implementations should return result when finished. + + NB: result is a failure if the execute returned a failre. + To get the blockID, you should do something like:: + if(isinstance(result, twisted.python.failure.Failure)): + blockID = result.blockID + else: + blockID = result['blockID'] + """ + + pass + + def render_result(self, result): + """Render the result of an execute call. Implementors may choose the + method of rendering. + For example, a notebook-style frontend might render a Chaco plot + inline. + + Parameters: + result : dict (result of IEngineBase.execute ) + + Result: + Output of frontend rendering + """ + + pass + + def render_error(self, failure): + """Subclasses must override to render the failure. Since this method + ill be called as a twisted.internet.defer.Deferred's callback, + implementations should return result when finished. + """ + + pass + + + def input_prompt(result={}): + """Returns the input prompt by subsituting into + self.input_prompt_template + """ + pass + + def output_prompt(result): + """Returns the output prompt by subsituting into + self.output_prompt_template + """ + + pass + + def continuation_prompt(): + """Returns the continuation prompt by subsituting into + self.continuation_prompt_template + """ + + pass + + def is_complete(block): + """Returns True if block is complete, False otherwise.""" + + pass + + def compile_ast(block): + """Compiles block to an _ast.AST""" + + pass + + + def get_history_previous(currentBlock): + """Returns the block previous in the history. Saves currentBlock if + the history_cursor is currently at the end of the input history""" + pass + + def get_history_next(): + """Returns the next block in the history.""" + + pass + + +class FrontEndBase(object): + """ + FrontEndBase manages the state tasks for a CLI frontend: + - Input and output history management + - Input/continuation and output prompt generation + + Some issues (due to possibly unavailable engine): + - How do we get the current cell number for the engine? + - How do we handle completions? + """ + + zi.implements(IFrontEnd) + zi.classProvides(IFrontEndFactory) + + history_cursor = 0 + + current_indent_level = 0 + + + input_prompt_template = string.Template(rc.prompt_in1) + output_prompt_template = string.Template(rc.prompt_out) + continuation_prompt_template = string.Template(rc.prompt_in2) + + def __init__(self, engine=None, history=None): + assert(engine==None or IEngineCore.providedBy(engine)) + self.engine = IEngineCore(engine) + if history is None: + self.history = FrontEndHistory(input_cache=['']) + else: + self.history = history + + + def input_prompt(self, result={}): + """Returns the current input prompt + + It would be great to use ipython1.core.prompts.Prompt1 here + """ + + result.setdefault('number','') + + return self.input_prompt_template.safe_substitute(result) + + + def continuation_prompt(self): + """Returns the current continuation prompt""" + + return self.continuation_prompt_template.safe_substitute() + + def output_prompt(self, result): + """Returns the output prompt for result""" + + return self.output_prompt_template.safe_substitute(result) + + + def is_complete(self, block): + """Determine if block is complete. + + Parameters + block : string + + Result + True if block can be sent to the engine without compile errors. + False otherwise. + """ + + try: + ast = self.compile_ast(block) + except: + return False + + lines = block.split('\n') + return (len(lines)==1 or str(lines[-1])=='') + + + def compile_ast(self, block): + """Compile block to an AST + + Parameters: + block : str + + Result: + AST + + Throws: + Exception if block cannot be compiled + """ + + return compile(block, "", "exec", _ast.PyCF_ONLY_AST) + + + def execute(self, block, blockID=None): + """Execute the block and return result. + + Parameters: + block : {str, AST} + blockID : any + Caller may provide an ID to identify this block. + result['blockID'] := blockID + + Result: + Deferred result of self.interpreter.execute + """ + + if(not self.is_complete(block)): + return Failure(Exception("Block is not compilable")) + + if(blockID == None): + blockID = uuid.uuid4() #random UUID + + d = self.engine.execute(block) + d.addCallback(self._add_history, block=block) + d.addBoth(self._add_block_id, blockID) + d.addBoth(self.update_cell_prompt) + d.addCallbacks(self.render_result, errback=self.render_error) + + return d + + + def _add_block_id(self, result, blockID): + """Add the blockID to result or failure. Unfortunatley, we have to + treat failures differently than result dicts. + """ + + if(isinstance(result, Failure)): + result.blockID = blockID + else: + result['blockID'] = blockID + + return result + + def _add_history(self, result, block=None): + """Add block to the history""" + + assert(block != None) + self.history.add_items([block]) + self.history_cursor += 1 + + return result + + + def get_history_previous(self, currentBlock): + """ Returns previous history string and decrement history cursor. + """ + command = self.history.get_history_item(self.history_cursor - 1) + + if command is not None: + if(self.history_cursor == len(self.history.input_cache)): + self.history.input_cache[self.history_cursor] = currentBlock + self.history_cursor -= 1 + return command + + + def get_history_next(self): + """ Returns next history string and increment history cursor. + """ + command = self.history.get_history_item(self.history_cursor+1) + + if command is not None: + self.history_cursor += 1 + return command + + ### + # Subclasses probably want to override these methods... + ### + + def update_cell_prompt(self, result): + """Subclass may override to update the input prompt for a block. + Since this method will be called as a + twisted.internet.defer.Deferred's callback, implementations should + return result when finished. + + NB: result is a failure if the execute returned a failre. + To get the blockID, you should do something like:: + if(isinstance(result, twisted.python.failure.Failure)): + blockID = result.blockID + else: + blockID = result['blockID'] + + + """ + + return result + + + def render_result(self, result): + """Subclasses must override to render result. Since this method will + be called as a twisted.internet.defer.Deferred's callback, + implementations should return result when finished. + """ + + return result + + + def render_error(self, failure): + """Subclasses must override to render the failure. Since this method + will be called as a twisted.internet.defer.Deferred's callback, + implementations should return result when finished.""" + + return failure + + + diff --git a/IPython/frontend/tests/__init__.py b/IPython/frontend/tests/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/IPython/frontend/tests/__init__.py diff --git a/IPython/frontend/tests/test_frontendbase.py b/IPython/frontend/tests/test_frontendbase.py new file mode 100644 index 0000000..451e302 --- /dev/null +++ b/IPython/frontend/tests/test_frontendbase.py @@ -0,0 +1,151 @@ +# encoding: utf-8 + +"""This file contains unittests for the frontendbase module.""" + +__docformat__ = "restructuredtext en" + +#--------------------------------------------------------------------------- +# Copyright (C) 2008 The IPython Development Team +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#--------------------------------------------------------------------------- + +#--------------------------------------------------------------------------- +# Imports +#--------------------------------------------------------------------------- + +import unittest +from IPython.frontend import frontendbase +from IPython.kernel.engineservice import EngineService + +class FrontEndCallbackChecker(frontendbase.FrontEndBase): + """FrontEndBase subclass for checking callbacks""" + def __init__(self, engine=None, history=None): + super(FrontEndCallbackChecker, self).__init__(engine=engine, + history=history) + self.updateCalled = False + self.renderResultCalled = False + self.renderErrorCalled = False + + def update_cell_prompt(self, result): + self.updateCalled = True + return result + + def render_result(self, result): + self.renderResultCalled = True + return result + + + def render_error(self, failure): + self.renderErrorCalled = True + return failure + + + + +class TestFrontendBase(unittest.TestCase): + def setUp(self): + """Setup the EngineService and FrontEndBase""" + + self.fb = FrontEndCallbackChecker(engine=EngineService()) + + + def test_implements_IFrontEnd(self): + assert(frontendbase.IFrontEnd.implementedBy( + frontendbase.FrontEndBase)) + + + def test_is_complete_returns_False_for_incomplete_block(self): + """""" + + block = """def test(a):""" + + assert(self.fb.is_complete(block) == False) + + def test_is_complete_returns_True_for_complete_block(self): + """""" + + block = """def test(a): pass""" + + assert(self.fb.is_complete(block)) + + block = """a=3""" + + assert(self.fb.is_complete(block)) + + + def test_blockID_added_to_result(self): + block = """3+3""" + + d = self.fb.execute(block, blockID='TEST_ID') + + d.addCallback(self.checkBlockID, expected='TEST_ID') + + def test_blockID_added_to_failure(self): + block = "raise Exception()" + + d = self.fb.execute(block,blockID='TEST_ID') + d.addErrback(self.checkFailureID, expected='TEST_ID') + + def checkBlockID(self, result, expected=""): + assert(result['blockID'] == expected) + + + def checkFailureID(self, failure, expected=""): + assert(failure.blockID == expected) + + + def test_callbacks_added_to_execute(self): + """test that + update_cell_prompt + render_result + + are added to execute request + """ + + d = self.fb.execute("10+10") + d.addCallback(self.checkCallbacks) + + + def checkCallbacks(self, result): + assert(self.fb.updateCalled) + assert(self.fb.renderResultCalled) + + + def test_error_callback_added_to_execute(self): + """test that render_error called on execution error""" + + d = self.fb.execute("raise Exception()") + d.addCallback(self.checkRenderError) + + def checkRenderError(self, result): + assert(self.fb.renderErrorCalled) + + def test_history_returns_expected_block(self): + """Make sure history browsing doesn't fail""" + + blocks = ["a=1","a=2","a=3"] + for b in blocks: + d = self.fb.execute(b) + + # d is now the deferred for the last executed block + d.addCallback(self.historyTests, blocks) + + + def historyTests(self, result, blocks): + """historyTests""" + + assert(len(blocks) >= 3) + assert(self.fb.get_history_previous("") == blocks[-2]) + assert(self.fb.get_history_previous("") == blocks[-3]) + assert(self.fb.get_history_next() == blocks[-2]) + + + def test_history_returns_none_at_startup(self): + """test_history_returns_none_at_startup""" + + assert(self.fb.get_history_previous("")==None) + assert(self.fb.get_history_next()==None) + + diff --git a/IPython/kernel/engineservice.py b/IPython/kernel/engineservice.py index 88ce0d3..c8ec1fc 100644 --- a/IPython/kernel/engineservice.py +++ b/IPython/kernel/engineservice.py @@ -847,18 +847,30 @@ class Command(object): self.deferred.errback(reason) class ThreadedEngineService(EngineService): + """An EngineService subclass that defers execute commands to a separate + thread. + + ThreadedEngineService uses twisted.internet.threads.deferToThread to + defer execute requests to a separate thread. GUI frontends may want to + use ThreadedEngineService as the engine in an + IPython.frontend.frontendbase.FrontEndBase subclass to prevent + block execution from blocking the GUI thread. + """ zi.implements(IEngineBase) def __init__(self, shellClass=Interpreter, mpi=None): EngineService.__init__(self, shellClass, mpi) + + + def execute(self, lines): # Only import this if we are going to use this class from twisted.internet import threads - def execute(self, lines): - msg = """engine: %r -method: execute(lines) -lines = %s""" % (self.id, lines) - d = threads.deferToThread(self.executeAndRaise, msg, self.shell.execute, lines) + msg = {'engineid':self.id, + 'method':'execute', + 'args':[lines]} + + d = threads.deferToThread(self.shell.execute, lines) d.addCallback(self.addIDToResult) return d diff --git a/IPython/kernel/tests/test_engineservice.py b/IPython/kernel/tests/test_engineservice.py index 40c047f..15c80a4 100644 --- a/IPython/kernel/tests/test_engineservice.py +++ b/IPython/kernel/tests/test_engineservice.py @@ -48,6 +48,18 @@ else: def tearDown(self): return self.engine.stopService() + + class ThreadedEngineServiceTest(DeferredTestCase, + IEngineCoreTestCase, + IEngineSerializedTestCase, + IEnginePropertiesTestCase): + + def setUp(self): + self.engine = es.ThreadedEngineService() + self.engine.startService() + + def tearDown(self): + return self.engine.stopService() class QueuedEngineServiceTest(DeferredTestCase, IEngineCoreTestCase,