diff --git a/Hosts/Silverlight/Iron7/.gitignore b/Hosts/Silverlight/Iron7/.gitignore new file mode 100644 index 0000000000..c60cc619da --- /dev/null +++ b/Hosts/Silverlight/Iron7/.gitignore @@ -0,0 +1,5 @@ +Bin/ +Obj/ +*.suo +for_free_version/ +for_full_version/ diff --git a/Hosts/Silverlight/Iron7/App.xaml b/Hosts/Silverlight/Iron7/App.xaml new file mode 100644 index 0000000000..b26f5c1c41 --- /dev/null +++ b/Hosts/Silverlight/Iron7/App.xaml @@ -0,0 +1,331 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Hosts/Silverlight/Iron7/App.xaml.cs b/Hosts/Silverlight/Iron7/App.xaml.cs new file mode 100644 index 0000000000..cf11edad2f --- /dev/null +++ b/Hosts/Silverlight/Iron7/App.xaml.cs @@ -0,0 +1,237 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Navigation; +using System.Windows.Shapes; +using Microsoft.Phone.Controls; +using Microsoft.Phone.Shell; +using Iron7.Common; + +namespace Iron7 +{ + public partial class App : Application + { + private static MainViewModel viewModel = null; + + /// + /// A static ViewModel used by the views to bind against. + /// + /// The MainViewModel object. + public static MainViewModel ViewModel + { + get + { + // Delay creation of the view model until necessary + if (viewModel == null) + viewModel = new MainViewModel(); + + return viewModel; + } + } + + /// + /// Provides easy access to the root frame of the Phone Application. + /// + /// The root frame of the Phone Application. + public PhoneApplicationFrame RootFrame { get; private set; } + + /// + /// Constructor for the Application object. + /// + public App() + { + // Global handler for uncaught exceptions. + UnhandledException += Application_UnhandledException; + + // Show graphics profiling information while debugging. + if (System.Diagnostics.Debugger.IsAttached) + { + // Display the current frame rate counters. + Application.Current.Host.Settings.EnableFrameRateCounter = true; + + // Show the areas of the app that are being redrawn in each frame. + //Application.Current.Host.Settings.EnableRedrawRegions = true; + + // Enable non-production analysis visualization mode, + // which shows areas of a page that are being GPU accelerated with a colored overlay. + //Application.Current.Host.Settings.EnableCacheVisualization = true; + } + + // Standard Silverlight initialization + InitializeComponent(); + + // Phone-specific initialization + InitializePhoneApplication(); + + RootFrame.Navigating += new NavigatingCancelEventHandler(RootFrame_Navigating); + } + + void RootFrame_Navigating(object sender, NavigatingCancelEventArgs e) + { + // Only care about HomePage + if (e.Uri.ToString().Contains("/HomePage.xaml") != true) + return; + + e.Cancel = true; + RootFrame.Dispatcher.BeginInvoke(delegate + { + RootFrame.Navigate(new Uri("/Views/MainPage.xaml", UriKind.Relative)); + }); + } + + // Code to execute when the application is launching (eg, from Start) + // This code will not execute when the application is reactivated + private void Application_Launching(object sender, LaunchingEventArgs e) + { + // Ensure that application state is restored appropriately + if (!App.ViewModel.IsDataLoaded) + { + App.ViewModel.LoadData(); + SubscribeToPropertyChanges(); + } + + // make sure we have the edit area html installed + Helper.SaveEditAreaHtml(); + } + + void ItemViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) + { + var item = sender as ItemViewModel; + item.Store(); + } + + // Code to execute when the application is activated (brought to foreground) + // This code will not execute when the application is first launched + private void Application_Activated(object sender, ActivatedEventArgs e) + { + // Ensure that application state is restored appropriately + if (!App.ViewModel.IsDataLoaded) + { + App.ViewModel.LoadData(); + SubscribeToPropertyChanges(); + } + } + + private void SubscribeToPropertyChanges() + { + App.ViewModel.Items.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(Items_CollectionChanged); + foreach (var item in App.ViewModel.Items) + { + item.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(ItemViewModel_PropertyChanged); + } + App.ViewModel.Account.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(Account_PropertyChanged); + } + + void Account_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) + { + try + { + App.ViewModel.Account.Save(Constants.MyAccountFileName); + } + catch (Exception exc) + { + MessageBox.Show("Sorry - there was a problem saving your Iron7 account details " + exc.Message); + } + } + + void Items_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) + { + switch (e.Action) + { + case System.Collections.Specialized.NotifyCollectionChangedAction.Remove: + foreach (ItemViewModel item in e.OldItems) + { + item.DeleteFromStore(); + } + break; + case System.Collections.Specialized.NotifyCollectionChangedAction.Add: + foreach (ItemViewModel item in e.NewItems) + { + item.Store(); + item.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(ItemViewModel_PropertyChanged); + } + break; + case System.Collections.Specialized.NotifyCollectionChangedAction.Reset: + case System.Collections.Specialized.NotifyCollectionChangedAction.Replace: + MessageBox.Show("Unexpected action seen - " + e.Action + " - sorry - you may lose data :("); + break; + } + } + + // Code to execute when the application is deactivated (sent to background) + // This code will not execute when the application is closing + private void Application_Deactivated(object sender, DeactivatedEventArgs e) + { + // Ensure that required application state is persisted here. + } + + // Code to execute when the application is closing (eg, user hit Back) + // This code will not execute when the application is deactivated + private void Application_Closing(object sender, ClosingEventArgs e) + { + } + + // Code to execute if a navigation fails + private void RootFrame_NavigationFailed(object sender, NavigationFailedEventArgs e) + { + if (System.Diagnostics.Debugger.IsAttached) + { + // A navigation has failed; break into the debugger + System.Diagnostics.Debugger.Break(); + } + } + + // Code to execute on Unhandled Exceptions + private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e) + { + if (System.Diagnostics.Debugger.IsAttached) + { + // An unhandled exception has occurred; break into the debugger + System.Diagnostics.Debugger.Break(); + } + } + + #region Phone application initialization + + // Avoid double-initialization + private bool phoneApplicationInitialized = false; + + // Do not add any additional code to this method + private void InitializePhoneApplication() + { + if (phoneApplicationInitialized) + return; + + // Create the frame but don't set it as RootVisual yet; this allows the splash + // screen to remain active until the application is ready to render. + RootFrame = new PhoneApplicationFrame(); + RootFrame.Navigated += CompleteInitializePhoneApplication; + + // Handle navigation failures + RootFrame.NavigationFailed += RootFrame_NavigationFailed; + + // Ensure we don't initialize again + phoneApplicationInitialized = true; + } + + // Do not add any additional code to this method + private void CompleteInitializePhoneApplication(object sender, NavigationEventArgs e) + { + // Set the root visual to allow the application to render + if (RootVisual != RootFrame) + RootVisual = RootFrame; + + // Remove this handler since it is no longer needed + RootFrame.Navigated -= CompleteInitializePhoneApplication; + } + + #endregion + } +} \ No newline at end of file diff --git a/Hosts/Silverlight/Iron7/AppResources.Designer.cs b/Hosts/Silverlight/Iron7/AppResources.Designer.cs new file mode 100644 index 0000000000..b62ac8a868 --- /dev/null +++ b/Hosts/Silverlight/Iron7/AppResources.Designer.cs @@ -0,0 +1,864 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Iron7 { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class AppResources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal AppResources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Iron7.AppResources", typeof(AppResources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to include System + ///include System::Windows + ///include System::Windows::Media + ///include System::Windows::Media::Imaging + ///include System::Windows::Controls + ///include System::Windows::Shapes + ///include Microsoft::Phone::Controls::Maps + /// + ///def get_brush(color) + /// brush = SolidColorBrush.new + /// brush.Color = color + /// return brush + ///end + /// + ///def init + /// $WIDTH = 400 + /// $HEIGHT = 400 + /// $BUBBLE_RADIUS = 25 + /// + /// $mid_x = ($WIDTH - $BUBBLE_RADIUS)/2 + /// $mid_y = ($HEIGHT - $BUBBLE_RADIUS)/2 + /// $last_x = $mid_x + /// $last_y = $mid_y [rest of string was truncated]";. + /// + internal static string Accelerometer { + get { + return ResourceManager.GetString("Accelerometer", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to include System + ///include System::Windows + ///include System::Windows::Media + ///include System::Windows::Media::Imaging + ///include System::Windows::Controls + ///include System::Windows::Shapes + ///include Microsoft::Phone::Controls::Maps + /// + ///def init + /// $WIDTH = 480 + /// $HEIGHT = 800 + /// $SHIP_SIZE = 100 + /// $HALF_SHIP_SIZE = 50 + /// + /// $mid_x = ($WIDTH - $SHIP_SIZE)/2 + /// $mid_y = ($HEIGHT - $SHIP_SIZE)/2 + /// $x = $mid_x + /// $y = $mid_y + /// + /// # note - if no internet connection, then no ship will show! + /// # could work around this by [rest of string was truncated]";. + /// + internal static string AccelerometerSpace { + get { + return ResourceManager.GetString("AccelerometerSpace", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to <html> + ///<head> + ///<title></title> + ///<meta name='viewport' content='width=device-width,height=device-height' /> + ///<meta name="viewport" content="user-scalable=no" /> + ///<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" /> + ///<link rel='Stylesheet' type="text/css" href='rubycolors.css' /> + ///<script type="text/javascript" src="util.js"></script> + ///<script type="text/javascript" src="stringstream.js"></script> + ///<script type="text/javascript" src="select.js"></script> + ///<script type="text/javascript" src="undo.js [rest of string was truncated]";. + /// + internal static string BaseEditorHtml { + get { + return ResourceManager.GetString("BaseEditorHtml", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to # a simple button game + /// + ///include System + ///include System::Windows + ///include System::Windows::Media + ///include System::Windows::Media::Imaging + ///include System::Windows::Controls + ///include System::Windows::Shapes + ///include Microsoft::Phone::Controls::Maps + /// + ///CLICKS_PER_GAME = 5 + ///BUTTON_DIMENSION = 100 + ///SPARE_WIDTH = 380 + ///SPARE_HEIGHT = 700 + /// + ///def set_button_idle_state + /// $button.content = "go" + /// $button.margin = Thickness.new(0,0,0,0) + /// $button.background = SolidColorBrush.new(Colors.red) + /// $button.foreground = [rest of string was truncated]";. + /// + internal static string ButtonGame { + get { + return ResourceManager.GetString("ButtonGame", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to # based loosely on https://github.com/jschementi/ironruby-rubyinside-article/blob/master/ruby-circles.html + /// + ///include System + ///include System::Windows + ///include System::Windows::Shapes + ///include System::Windows::Media + ///include System::Windows::Controls + /// + ///# called once at startup + ///def setup + /// $draw_on = false + /// $d = $d_start = 10 + /// $color_set = [[255,0, 113,118], + /// [255,0, 173,239], + /// [255,68, 199,244], + /// [255,157,220,249], + /// [255,255,235,149]] + /// [rest of string was truncated]";. + /// + internal static string circle_drawing { + get { + return ResourceManager.GetString("circle_drawing", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to include System ///include System::Windows ///include System::Windows::Controls ///include System::Windows::Shapes ///include System::Windows::Media::Animation ///include System::Windows::Media + /// + ///def init + /// $layout_root = Host.content_holder /// $snowflake = nil /// + /// $random_number = System::Random.new /// + ///end + /// /// + ///class Snowflake /// def initialize ///end + /// def __init__(self, xaml, randomNumber): /// + /// self.xaml = xaml /// + /// self.randomNumber = randomNumber /// + /// self.x = 0 /// + /// self.y = [rest of string was truncated]";. + /// + internal static string composition_rendering { + get { + return ResourceManager.GetString("composition_rendering", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to # this script based on fingerpaint + ///# from http://sigurdsnorteland.wordpress.com/ + /// + ///include System + ///include System::Windows + ///include System::Windows::Media + ///include System::Windows::Media::Imaging + ///include System::Windows::Controls + ///include System::Windows::Shapes + ///include Microsoft::Phone::Controls::Maps + /// + ///def get_brush(color) + /// brush = SolidColorBrush.new + /// brush.Color = color + /// return brush + ///end + /// + ///def create_button(color) + /// rectangle = Rectangle.new + /// rectangle.height = 40 + /// rectangle.width = 40 [rest of string was truncated]";. + /// + internal static string Drawing { + get { + return ResourceManager.GetString("Drawing", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to include System + ///include System::Windows + ///include System::Windows::Media + ///include System::Windows::Media::Imaging + ///include System::Windows::Controls + ///include System::Windows::Shapes + ///include Microsoft::Phone::Controls::Maps + /// + ///def init + /// $initialized=1 + /// $counter_button=0 + /// + /// stack_panel = StackPanel.new + /// + /// $text_block = TextBlock.new + /// $text_block.font_size = 40 + /// $text_block.text_wrapping = TextWrapping.wrap + /// stack_panel.children.add($text_block) + /// + /// button1 = Button.new + /// button1.content = "pre [rest of string was truncated]";. + /// + internal static string DynamicExamples { + get { + return ResourceManager.GetString("DynamicExamples", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to # for a full bing maps tutorial, see + ///# http://msdn.microsoft.com/en-us/wp7trainingcourse_usingbingmapslab_topic2 + /// + ///include System + ///include System::Windows + ///include System::Windows::Media + ///include System::Windows::Media::Imaging + ///include System::Windows::Controls + ///include System::Windows::Shapes + ///include Microsoft::Phone::Controls::Maps + /// + ///def init + /// $initialized = 1 + /// stack_panel = StackPanel.new + /// map = Map.new + /// map.height = 400 + /// map.width = 400 + /// # this key for Iron7 use only + /// # to create your [rest of string was truncated]";. + /// + internal static string FirstMap { + get { + return ResourceManager.GetString("FirstMap", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to include System::Windows + ///include System::Windows::Media + ///include System::Windows::Controls + ///include System::Windows::Shapes + ///include System::Windows::Media::Imaging + ///include Microsoft::Phone::Controls + /// + ///def random_string + /// items = [ + /// "WP7", + /// "WindowsPhone", + /// "sunset", + /// "beach", + /// "ruby", + /// "ironruby"] + /// + /// random = System::Random.new + /// which = random.next(items.length) + /// + /// return items[which] + ///end + /// + ///def init + /// scroll_viewer = ScrollViewer.new + /// stack_panel_main = StackPanel.new + /// + /// $textbox_1 = [rest of string was truncated]";. + /// + internal static string Flickr { + get { + return ResourceManager.GetString("Flickr", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to # fractal algortithm adapted from http://www.codeproject.com/KB/WPF/Fractals.aspx + ///# used under the Code Project Open License - huge thanks to "logicchild" + ///# note that drawing a fractal using lots of "Line" instances is not the most optimal way to draw! + /// + ///include System + ///include System::Windows + ///include System::Windows::Media + ///include System::Windows::Media::Imaging + ///include System::Windows::Controls + ///include System::Windows::Shapes + ///include Microsoft::Phone::Controls::Maps + /// + ///def init + /// $FRACTAL_WIDTH = [rest of string was truncated]";. + /// + internal static string FractalTree { + get { + return ResourceManager.GetString("FractalTree", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to # clock based on http://www.infoq.com/articles/ironruby-wpf + ///# author Edd Morgan + ///# used with permission - thanks Edd! + /// + ///include System + ///include System::Windows + ///include System::Windows::Media + ///include System::Windows::Media::Imaging + ///include System::Windows::Controls + ///include System::Windows::Shapes + ///include Microsoft::Phone::Controls::Maps + /// + ///def create_hand(color, thickness) + /// hand = Line.new + /// hand.stroke = SolidColorBrush.new(color) + /// hand.stroke_thickness = thickness + /// hand.stroke_end_line_cap = [rest of string was truncated]";. + /// + internal static string GraphicsExample { + get { + return ResourceManager.GetString("GraphicsExample", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to include System + ///include System::Windows + ///include System::Windows::Media + ///include System::Windows::Media::Imaging + ///include System::Windows::Controls + ///include System::Windows::Shapes + ///include Microsoft::Phone::Controls::Maps + /// + ///def init + /// stack_panel_main = StackPanel.new + /// stack_panel_main.horizontal_alignment = HorizontalAlignment.center + /// + /// $textbox1 = TextBox.new + /// $textbox1.text = "Hello" + /// stack_panel_main.children.add($textbox1) + /// + /// textblock_plus = TextBlock.new + /// textblock_plus.text = "+" + /// s [rest of string was truncated]";. + /// + internal static string LogicExample { + get { + return ResourceManager.GetString("LogicExample", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to # mandelbrot algortithm adapted from http://www.codeproject.com/KB/graphics/mandelbrot.aspx + ///# note that this is not a very optimal drawing mechanism! + ///# there are lots of interesting (beautiful) mandelbrot sites online + ///# note that different algorithm draw the axes at different angles - can get very confusing! + /// + ///include System + ///include System::Windows + ///include System::Windows::Media + ///include System::Windows::Media::Imaging + ///include System::Windows::Controls + ///include System::Windows::Shapes + ///include Micros [rest of string was truncated]";. + /// + internal static string MandelBrotDrawing { + get { + return ResourceManager.GetString("MandelBrotDrawing", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to # for full bing maps sample, see + ///# http://msdn.microsoft.com/en-us/wp7trainingcourse_usingbingmapslab_topic2 + /// + ///include System + ///include System::Windows + ///include System::Windows::Media + ///include System::Windows::Media::Imaging + ///include System::Windows::Controls + ///include System::Windows::Shapes + ///include Microsoft::Phone::Controls::Maps + /// + ///def create_text_block(color, size, text) + /// textBlock = TextBlock.new + /// textBlock.text = text + /// textBlock.foreground = SolidColorBrush.new(color) unless color.nil? + /// text [rest of string was truncated]";. + /// + internal static string MapWithLocation { + get { + return ResourceManager.GetString("MapWithLocation", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to include System + ///include System::Windows + ///include System::Windows::Input + ///include System::Windows::Media + ///include System::Windows::Media::Imaging + ///include System::Windows::Controls + ///include System::Windows::Shapes + ///include Microsoft::Phone::Controls::Maps + /// + ///def numeric_input_scope(textbox) + /// numeric_input = InputScope.new + /// textbox.input_scope = numeric_input + /// numeric_input_name = InputScopeName.new + /// numeric_input_name.name_value = InputScopeNameValue.Number + /// numeric_input.names.add(numeric_input_ [rest of string was truncated]";. + /// + internal static string MathsExample { + get { + return ResourceManager.GetString("MathsExample", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to include System + ///include System::Windows + ///include System::Windows::Media + ///include System::Windows::Media::Imaging + ///include System::Windows::Controls + ///include System::Windows::Shapes + ///include Microsoft::Phone::Controls::Maps + /// + ///def init + /// + /// $text_block_x = TextBlock.new + /// $text_block_y = TextBlock.new + /// $text_block_z = TextBlock.new + /// + /// text_stack_panel = StackPanel.new + /// text_stack_panel.children.add($text_block_x) + /// text_stack_panel.children.add($text_block_y) + /// text_stack_panel.children.add($text_bl [rest of string was truncated]";. + /// + internal static string NewAccelerometerAppTemplate { + get { + return ResourceManager.GetString("NewAccelerometerAppTemplate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to include System + ///include System::Windows + ///include System::Windows::Media + ///include System::Windows::Media::Imaging + ///include System::Windows::Controls + ///include System::Windows::Shapes + ///include Microsoft::Phone::Controls::Maps + /// + ///def init + /// text_block = TextBlock.new + /// text_block.text = "HELLO WORLD" + /// text_block.font_size = 64 + /// + /// Host.content_holder.children.add(text_block) + /// + /// $initialized = 1 + ///end + /// + ///init if $initialized.nil? + ///. + /// + internal static string NewAppTemplate { + get { + return ResourceManager.GetString("NewAppTemplate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to include System + ///include System::Windows + ///include System::Windows::Media + ///include System::Windows::Media::Imaging + ///include System::Windows::Controls + ///include System::Windows::Shapes + ///include Microsoft::Phone::Controls::Maps + /// + ///def init + /// $initialized=1 + /// $timer_count=0 + /// + /// stack_panel = StackPanel.new + /// + /// $text_block = TextBlock.new + /// $text_block.font_size = 40 + /// stack_panel.children.add($text_block) + /// + /// button = Button.new + /// button.content = "reset" + /// stack_panel.children.add(button) + /// + /// Host.cont [rest of string was truncated]";. + /// + internal static string NewButtonAndTimerAppTemplate { + get { + return ResourceManager.GetString("NewButtonAndTimerAppTemplate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to include System + ///include System::Windows + ///include System::Windows::Media + ///include System::Windows::Media::Imaging + ///include System::Windows::Controls + ///include System::Windows::Shapes + ///include Microsoft::Phone::Controls::Maps + /// + ///def init + /// $WIDTH = 800 + /// $HEIGHT = 800 + /// + /// $canvas = Canvas.new + /// $canvas.width = $WIDTH + /// $canvas.height = $HEIGHT + /// + /// Host.content_holder.children.add($canvas) + /// + /// ellipse_params = { + /// "margin_left"=>10, + /// "margin_top"=>10, + /// "width"=>100, + /// "height"=>20 [rest of string was truncated]";. + /// + internal static string NewCanvasTemplate { + get { + return ResourceManager.GetString("NewCanvasTemplate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to include System + ///include System::Windows + ///include System::Windows::Media + ///include System::Windows::Media::Imaging + ///include System::Windows::Controls + ///include System::Windows::Shapes + ///include Microsoft::Phone::Controls::Maps + /// + ///def create_text_block(color, size, text) + /// textBlock = TextBlock.new + /// textBlock.text = text + /// textBlock.foreground = SolidColorBrush.new(color) unless color.nil? + /// textBlock.font_size = size + /// textBlock.text_wrapping = TextWrapping.Wrap + /// return textBlock + ///end + /// + ///def init + /// sta [rest of string was truncated]";. + /// + internal static string NewLocationAppTemplate { + get { + return ResourceManager.GetString("NewLocationAppTemplate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to # for a longer but similar tutorial + ///# try http://www.troubleshooters.com/codecorn/ruby/basictutorial.htm + ///# an excellent overview by Steve Litt + /// + ///# first let's set up a console + ///include System::Windows + ///include System::Windows::Controls + ///include System::Windows::Media + /// + ///# note that $ before a variable name simply + ///# makes that variable global + ///$stack = StackPanel.new + ///scroll = ScrollViewer.new + ///scroll.content = $stack + ///Host.content_holder.children.add(scroll) + /// + ///def add_text_block(t, size, color) + /// tb [rest of string was truncated]";. + /// + internal static string RubyIntro { + get { + return ResourceManager.GetString("RubyIntro", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to # adapted from MonoDroid sample - used under MIT license + ///# original https://github.com/mono/monodroid-samples + ///# thanks to jpobst + /// + ///include System + ///include System::Windows + ///include System::Windows::Media + ///include System::Windows::Media::Imaging + ///include System::Windows::Controls + ///include System::Windows::Shapes + ///include Microsoft::Phone::Controls::Maps + /// + ///def init + /// $pixels_per_square = 40 + /// $WIDTH = 480/$pixels_per_square + /// $HEIGHT = 800/$pixels_per_square + /// + /// $color_head = Color.from_argb(255,0,255, [rest of string was truncated]";. + /// + internal static string SnakeGame { + get { + return ResourceManager.GetString("SnakeGame", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to # originally based on https://github.com/jschementi/ironruby-rubyinside-article/blob/master/ruby-squares.html + ///# this script uses a timer for simple animation + ///# the alien images used come royalty free from clker.com + ///# sounds from http://www.freesound.org/samplesViewSingle.php?id=18397 + ///# and from http://www.freesound.org/samplesViewSingle.php?id=76966 + ///# all used under creative commons licensing + /// + ///include System + ///include System::Windows + ///include System::Windows::Shapes + ///include System::Windows::Media + ///in [rest of string was truncated]";. + /// + internal static string square_aliens { + get { + return ResourceManager.GetString("square_aliens", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to # originally based on https://github.com/jschementi/ironruby-rubyinside-article/blob/master/ruby-squares.html + ///# this script uses a timer for simple animation + ///# there are smoother animation techniques for WP7 + /// + ///include System + ///include System::Windows + ///include System::Windows::Shapes + ///include System::Windows::Media + ///include System::Windows::Controls + /// + ///def setup + /// $canvas = Canvas.new + /// add_squares(10) + /// Host.content_holder.children.add($canvas) + /// Host.fix_orientation_portrait + /// Host.start_timer("time [rest of string was truncated]";. + /// + internal static string square_animating { + get { + return ResourceManager.GetString("square_animating", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to # originally based on https://github.com/jschementi/ironruby-rubyinside-article/blob/master/ruby-squares.html + ///# this script uses a timer for simple animation + ///# there are smoother animation techniques for WP7 + /// + ///include System + ///include System::Windows + ///include System::Windows::Shapes + ///include System::Windows::Media + ///include System::Windows::Controls + /// + ///def setup + /// $speed = 21 + /// $canvas = Canvas.new + /// Host.content_holder.children.add($canvas) + /// Host.fix_orientation_portrait + /// Host.start_timer("timer1", [rest of string was truncated]";. + /// + internal static string square_animating_game { + get { + return ResourceManager.GetString("square_animating_game", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to # originally based on https://github.com/jschementi/ironruby-rubyinside-article/blob/master/ruby-squares.html + /// + ///include System + ///include System::Windows + ///include System::Windows::Shapes + ///include System::Windows::Media + ///include System::Windows::Controls + /// + ///def setup + /// $canvas = Canvas.new + /// Host.content_holder.children.add($canvas) + /// Host.fix_orientation_portrait + /// Host.start_timer("timer1", TimeSpan.from_seconds(0.1), "add_squares") + ///end + /// + ///def add_squares + /// if $canvas.children.count > 500 + /// Host.st [rest of string was truncated]";. + /// + internal static string square_drawing { + get { + return ResourceManager.GetString("square_drawing", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to # everyone needs a hello world example + /// + ///include System::Windows::Controls + /// + ///text_block = TextBlock.new + ///text_block.text = "HELLO WORLD" + ///text_block.font_size = 64 + /// + ///Host.content_holder.children.add(text_block) + ///. + /// + internal static string StaticHelloWorld { + get { + return ResourceManager.GetString("StaticHelloWorld", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to # Credit for code logic (license open - see original sites for more info) to: + ///# http://www.silverlight.net/community/samples/silverlight-samples/yygames---yytetris/ + ///# http://sigurdsnorteland.wordpress.com/2010/11/15/tetris7-a-wp7-game-source-code-included/ + ///# Credit for sound effects (creative commons 2) to: + ///# Grunz - http://www.freesound.org/samplesViewSingle.php?id=109663 + ///# Synapse - http://www.freesound.org/samplesViewSingle.php?id=2324 + ///# Stuckinthemud - http://www.freesound.org/samplesViewSingle.ph [rest of string was truncated]";. + /// + internal static string Tetris { + get { + return ResourceManager.GetString("Tetris", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to include System + ///include System::Windows + ///include System::Windows::Media + ///include System::Windows::Media::Imaging + ///include System::Windows::Controls + ///include System::Windows::Shapes + ///include Microsoft::Phone::Controls::Maps + /// + ///def init + /// $initialized=1 + /// $counter_timer=0 + /// + /// stack_panel = StackPanel.new + /// + /// $text_block = TextBlock.new + /// $text_block.font_size = 40 + /// $text_block.text_wrapping = TextWrapping.wrap + /// stack_panel.children.add($text_block) + /// + /// Host.content_holder.horizontal_alignment = Hori [rest of string was truncated]";. + /// + internal static string TimerExample { + get { + return ResourceManager.GetString("TimerExample", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to include System + ///include System::Windows + ///include System::Windows::Media + ///include System::Windows::Media::Imaging + ///include System::Windows::Controls + ///include System::Windows::Shapes + ///include Microsoft::Phone::Controls::Maps + /// + ///def random_string + /// items = [ + /// "WP7", + /// "WindowsPhone", + /// "sunset", + /// "beach", + /// "slodge", + /// "ruby", + /// "ironruby"] + /// + /// random = System::Random.new + /// which = random.next(items.length) + /// + /// return items[which] + ///end + /// + ///def init + /// stack_panel_main = StackPanel.new + /// + /// $textbox_1 = T [rest of string was truncated]";. + /// + internal static string Twitter { + get { + return ResourceManager.GetString("Twitter", resourceCulture); + } + } + } +} diff --git a/Hosts/Silverlight/Iron7/AppResources.resx b/Hosts/Silverlight/Iron7/AppResources.resx new file mode 100644 index 0000000000..a1ce264975 --- /dev/null +++ b/Hosts/Silverlight/Iron7/AppResources.resx @@ -0,0 +1,214 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + Resources\Accelerometer.txt;System.String, mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e;Windows-1252 + + + Resources\AccelerometerSpace.txt;System.String, mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e;Windows-1252 + + + Resources\BaseEditorHtml.txt;System.String, mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e;Windows-1252 + + + Resources\ButtonGame.txt;System.String, mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e;Windows-1252 + + + Resources\circle_drawing.txt;System.String, mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e;Windows-1252 + + + Resources\Drawing.txt;System.String, mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e;Windows-1252 + + + Resources\DynamicExamples.txt;System.String, mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e;Windows-1252 + + + Resources\FirstMap.txt;System.String, mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e;Windows-1252 + + + Resources\Flickr.txt;System.String, mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e;Windows-1252 + + + Resources\FractalTree.txt;System.String, mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e;Windows-1252 + + + Resources\GraphicsExample.txt;System.String, mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e;Windows-1252 + + + Resources\LogicExample.txt;System.String, mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e;Windows-1252 + + + Resources\MandelBrotDrawing.txt;System.String, mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e;Windows-1252 + + + Resources\MapWithLocation.txt;System.String, mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e;Windows-1252 + + + Resources\MathsExample.txt;System.String, mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e;Windows-1252 + + + Resources\NewAccelerometerAppTemplate.txt;System.String, mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e;Windows-1252 + + + Resources\NewAppTemplate.txt;System.String, mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e;Windows-1252 + + + Resources\NewButtonAndTimerAppTemplate.txt;System.String, mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e;Windows-1252 + + + Resources\NewCanvasTemplate.txt;System.String, mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e;Windows-1252 + + + Resources\NewLocationAppTemplate.txt;System.String, mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e;Windows-1252 + + + Resources\RubyIntro.txt;System.String, mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e;Windows-1252 + + + Resources\SnakeGame.txt;System.String, mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e;Windows-1252 + + + Resources\square_aliens.txt;System.String, mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e;Windows-1252 + + + Resources\square_animating.txt;System.String, mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e;Windows-1252 + + + Resources\square_animating_game.txt;System.String, mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e;Windows-1252 + + + Resources\square_drawing.txt;System.String, mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e;Windows-1252 + + + Resources\StaticHelloWorld.txt;System.String, mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e;Windows-1252 + + + Resources\TimerExample.txt;System.String, mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e;Windows-1252 + + + Resources\Twitter.txt;System.String, mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e;Windows-1252 + + + Resources\Tetris.txt;System.String, mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e;Windows-1252 + + + Resources\composition_rendering.txt;System.String, mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e;Windows-1252 + + \ No newline at end of file diff --git a/Hosts/Silverlight/Iron7/Common/AudioPlayer.cs b/Hosts/Silverlight/Iron7/Common/AudioPlayer.cs new file mode 100644 index 0000000000..a2d6c3263d --- /dev/null +++ b/Hosts/Silverlight/Iron7/Common/AudioPlayer.cs @@ -0,0 +1,32 @@ +using System; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Ink; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; +using Microsoft.Xna.Framework.Audio; +using Microsoft.Xna.Framework; + +namespace Iron7.Common +{ + public class SoundEffectPlayer + { + + private readonly SoundEffect soundEffect; + + public SoundEffectPlayer(SoundEffect soundEffect) + { + this.soundEffect = soundEffect; + } + + public void Play() + { + FrameworkDispatcher.Update(); + soundEffect.Play(); + } + } +} diff --git a/Hosts/Silverlight/Iron7/Common/Constants.cs b/Hosts/Silverlight/Iron7/Common/Constants.cs new file mode 100644 index 0000000000..9fedc8d482 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Common/Constants.cs @@ -0,0 +1,22 @@ +using System; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Ink; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; + +namespace Iron7.Common +{ + public class Constants + { + public static string TwitterAccess = "TwitterAccess"; + public static string MyScriptsFileName = "MyScriptsFile"; + public static string MyScriptsDirectoryName = "Scripts"; + public static string Title = "Iron7"; + public static string MyAccountFileName = "I7Account.json"; + } +} diff --git a/Hosts/Silverlight/Iron7/Common/DynamicJson.cs b/Hosts/Silverlight/Iron7/Common/DynamicJson.cs new file mode 100644 index 0000000000..4439cc4309 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Common/DynamicJson.cs @@ -0,0 +1,59 @@ +using System; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Ink; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; +using Newtonsoft.Json.Linq; +using System.Dynamic; +using System.Collections.Generic; +using System.Linq; + +#if false // Can't get this to work - problems with Microsoft.CSharp.dll :( +namespace Iron7.Common +{ + public static class DynamicJson + { + public static object ConvertJsonStringToObject(string json) + { + // Create the json.Net Linq object for our json string + JObject jsonObject = JObject.Parse(json); + return ConvertJTokenToObject(jsonObject); + } + + // taken from http://blog.petegoo.com/blog/archive/2009/10/27/using-json.net-to-eval-json-into-a-dynamic-variable-in.aspx + public static object ConvertJTokenToObject(JToken token) + { + if (token is JValue) + { + return ((JValue)token).Value; + } + if (token is JObject) + { + ExpandoObject expando = new ExpandoObject(); + (from childToken in ((JToken)token) where childToken is JProperty select childToken as JProperty).ToList().ForEach(property => + { + ((IDictionary)expando).Add(property.Name, ConvertJTokenToObject(property.Value)); + }); + return expando; + } + if (token is JArray) + { + object[] array = new object[((JArray)token).Count]; + int index = 0; + foreach (JToken arrayItem in ((JArray)token)) + { + array[index] = ConvertJTokenToObject(arrayItem); + index++; + } + return array; + } + throw new ArgumentException(string.Format("Unknown token type '{0}'", token.GetType()), "token"); + } + } +} +#endif \ No newline at end of file diff --git a/Hosts/Silverlight/Iron7/Common/Helper.cs b/Hosts/Silverlight/Iron7/Common/Helper.cs new file mode 100644 index 0000000000..8148ed58dd --- /dev/null +++ b/Hosts/Silverlight/Iron7/Common/Helper.cs @@ -0,0 +1,151 @@ +using System; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Ink; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; +using System.IO.IsolatedStorage; +using System.Runtime.Serialization; +using System.IO; +using Newtonsoft.Json; + +namespace Iron7.Common +{ + public static class Helper + { + private static Object _thisLock = new Object(); + + public static T LoadSetting(string fileName) + { + using (var store = IsolatedStorageFile.GetUserStoreForApplication()) + { + if (!store.FileExists(fileName)) + return default(T); + + lock (_thisLock) + { + try + { + using (var stream = store.OpenFile(fileName, FileMode.Open, FileAccess.Read)) + { + using (var sr = new StreamReader(stream)) + { + return (T)JsonConvert.DeserializeObject(sr.ReadToEnd()); + } + } + } + catch (Newtonsoft.Json.JsonSerializationException se) + { + Deployment.Current.Dispatcher.BeginInvoke( + () => MessageBox.Show(String.Format("Serialize file error {0}:{1}", se.Message, fileName))); + return default(T); + } + catch (Exception e) + { + Deployment.Current.Dispatcher.BeginInvoke( + () => MessageBox.Show(String.Format("Load file error {0}:{1}", e.Message, fileName))); + return default(T); + } + } + } + } + + public static void SaveSetting(string fileName, T dataToSave) + { + using (var store = IsolatedStorageFile.GetUserStoreForApplication()) + { + lock (_thisLock) + { + try + { + using (var stream = store.CreateFile(fileName)) + { + using (var sw = new StreamWriter(stream)) + { + var serial = JsonConvert.SerializeObject(dataToSave); + sw.Write(serial); + } + } + } + catch (Exception e) + { + MessageBox.Show(String.Format("Save file error {0}:{1}", e.Message, fileName)); + return; + } + } + } + } + + public static void SaveEditAreaHtml() + { + using (var store = IsolatedStorageFile.GetUserStoreForApplication()) + { + if (store.DirectoryExists("WebContent") == false) + store.CreateDirectory("WebContent"); + + WriteLocalWebFile(store, "baseEditor.html", AppResources.BaseEditorHtml); + WriteLocalWebFile(store, "codemirror.js", ScriptResources.codemirror_js); + WriteLocalWebFile(store, "editor.js", ScriptResources.editor_js); + WriteLocalWebFile(store, "parseruby.js", ScriptResources.parseruby_js); + WriteLocalWebFile(store, "rubycolors.css", ScriptResources.rubycolors_css); + WriteLocalWebFile(store, "select.js", ScriptResources.select_js); + WriteLocalWebFile(store, "stringstream.js", ScriptResources.stringstream_js); + WriteLocalWebFile(store, "tokenize.js", ScriptResources.tokenize_js); + WriteLocalWebFile(store, "tokenizeruby.js", ScriptResources.tokenizeruby_js); + WriteLocalWebFile(store, "undo.js", ScriptResources.undo_js); + WriteLocalWebFile(store, "util.js", ScriptResources.util_js); + //WriteLocalWebFile(store, "greyback.jpg", ScriptResources.greyback); + } + } + + private static void WriteLocalWebFile(IsolatedStorageFile store, string filename, byte[] resource) + { + using (IsolatedStorageFileStream isfs = store.OpenFile("WebContent\\" + filename, System.IO.FileMode.Create)) + using (StreamWriter sw = new StreamWriter(isfs)) + { + sw.Write(resource); + } + } + + private static void WriteLocalWebFile(IsolatedStorageFile store, string filename, string resource) + { + using (IsolatedStorageFileStream isfs = store.OpenFile("WebContent\\" + filename, System.IO.FileMode.Create)) + using (StreamWriter sw = new StreamWriter(isfs)) + { + sw.Write(resource); + } + } + + public static void DeleteFile(string fileName) + { + using (var store = IsolatedStorageFile.GetUserStoreForApplication()) + { + if (store.FileExists(fileName)) + store.DeleteFile(fileName); + } + } + + public static DateTime ParseDateTime(string date) + { + var dayOfWeek = date.Substring(0, 3).Trim(); + var month = date.Substring(4, 3).Trim(); + var dayInMonth = date.Substring(8, 2).Trim(); + var time = date.Substring(11, 9).Trim(); + var offset = date.Substring(20, 5).Trim(); + var year = date.Substring(25, 5).Trim(); + var dateTime = string.Format("{0}-{1}-{2} {3}", dayInMonth, month, year, time); + var ret = DateTime.Parse(dateTime).ToLocalTime(); + + return ret; + } + + public static void ShowMessage(string message) + { + Deployment.Current.Dispatcher.BeginInvoke(() => MessageBox.Show(message)); + } + } +} diff --git a/Hosts/Silverlight/Iron7/Common/RubyExceptionHelper.cs b/Hosts/Silverlight/Iron7/Common/RubyExceptionHelper.cs new file mode 100644 index 0000000000..fdde9b9ef1 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Common/RubyExceptionHelper.cs @@ -0,0 +1,215 @@ +using System; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Ink; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; +using Microsoft.Scripting.Hosting; +using System.Text; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using IronRuby.Runtime; +using Microsoft.Scripting; + +namespace Iron7.Common +{ + public abstract class RubyExceptionHelperBase + { + protected static string SafeGetLine(string[] codePerLine, int lineNumber) + { + // line numbers are 1-based + if (lineNumber > 0 && lineNumber <= codePerLine.Length) + return codePerLine[lineNumber - 1].Trim(); + + return "?"; + } + + public abstract string LongErrorText(string code); + } + + public class RubySyntaxExceptionHelper : RubyExceptionHelperBase + { + private SyntaxErrorException exception; + + public RubySyntaxExceptionHelper(SyntaxErrorException exception) + { + this.exception = exception; + } + + public override string LongErrorText(string code) + { + var toReturn = new StringBuilder(); + toReturn.AppendLine("Error:"); + toReturn.AppendLine(exception.GetType().Name); + toReturn.AppendLine(); + toReturn.AppendLine("Message:"); + toReturn.AppendLine(exception.Message); + toReturn.AppendLine(); + toReturn.AppendLine("Location:"); + toReturn.AppendLine(exception.RawSpan.ToString()); + toReturn.AppendLine(); + toReturn.AppendLine("Near:"); + var codePerLine = code.Split('\n'); + toReturn.AppendLine(SafeGetLine(codePerLine, exception.Line)); + return toReturn.ToString(); + } + } + + public class RubyExceptionHelper : RubyExceptionHelperBase + { + public class LineAndMethod + { + public int LineNumber { get; set; } + public string MethodName { get; set; } + } + + public string MessageType { get; protected set; } + public string MessageBrief { get; protected set; } + public List StackEntries { get; private set; } + public string FullStack { get; private set; } + + public RubyExceptionHelper(ScriptEngine engine, Exception exc) + { + Parse(engine, exc); + } + + private void Parse(ScriptEngine engine, Exception exc) + { + var service = engine.GetService(); + string briefMessage; + string errorTypeName; + service.GetExceptionMessage(exc, out briefMessage, out errorTypeName); + var formattedMessage = service.FormatException(exc); + /* + var stackFrames = service.GetStackFrames(exc); + var ctxt = Microsoft.Scripting.Hosting.Providers.HostingHelpers.GetLanguageContext(engine); + var sink = ctxt.GetCompilerErrorSink(); + var rubyContext = (IronRuby.Runtime.RubyContext)ctxt; + RubyExceptionData data = RubyExceptionData.GetInstance(exc); + IronRuby.Builtins.RubyArray backtrace = data.Backtrace; + var s = 12; + */ + + switch (errorTypeName) + { + case "TypeInitializationException": + MessageType = "Sorry - Iron7 is unable to use dynamic ruby delegates"; + break; + default: + MessageType = errorTypeName; + break; + } + MessageBrief = briefMessage; + + var lines = formattedMessage.Replace("\r","").Split('\n'); + AttemptParseLineNumber(lines); + CaptureRelevantStack(lines); + } + + private void CaptureRelevantStack(string[] lines) + { + StringBuilder output = new StringBuilder(); + foreach (var l in lines) + { + if (l.EndsWith("`GenericScriptAction'")) + break; + output.AppendLine(l); + } + FullStack = output.ToString(); + } + + const string MagicEvalLine = "from .eval.:(?\\d+):in `(?.*?)'"; + private static Regex MagicEvalRegex = new Regex(MagicEvalLine); //, RegexOptions..Compiled); + private bool TryParseLineNumber(string line, out LineAndMethod lineAndMethod) + { + lineAndMethod = null; + var match = MagicEvalRegex.Match(line); + if (false == match.Success) + return false; + + int lineNumber = -1; + if (false == int.TryParse(match.Groups[1].Value, out lineNumber)) + return false; + + lineAndMethod = new LineAndMethod() + { + LineNumber = lineNumber, + MethodName = match.Groups[2].Value + }; + + return true; + /* + var trimmed = line.Trim(); + if (false == trimmed.StartsWith(MagicEvalLine)) + return false; + + var nextColon = trimmed.IndexOf(':', MagicEvalLine.Length); + if (nextColon < 0) + return false; + + trimmed = trimmed.Substring(MagicEvalLine.Length, nextColon - MagicEvalLine.Length); + return int.TryParse(trimmed, out lineNumber); + */ + } + + private void AttemptParseLineNumber(string[] lines) + { + StackEntries = new List(); + foreach (var l in lines) + { + LineAndMethod lineAndMethod; + if (TryParseLineNumber(l, out lineAndMethod)) + { + StackEntries.Add(lineAndMethod); + } + } + } + + public override string LongErrorText(string code) + { + var toReturn = new StringBuilder(); + toReturn.AppendLine("Error:"); + toReturn.AppendLine(this.MessageType); + toReturn.AppendLine(); + var codePerLine = code.Split('\n'); + if (StackEntries.Count > 0) + { + toReturn.AppendLine("Stack:"); + foreach (var stack in StackEntries) + { + string safeLine = SafeGetLine(codePerLine, stack.LineNumber); + toReturn.AppendLine(string.Format(":{0}:{1}:{2}", stack.LineNumber, stack.MethodName, safeLine)); + } + toReturn.AppendLine(); + } + toReturn.AppendLine("Message:"); + toReturn.AppendLine(this.MessageBrief); + toReturn.AppendLine(); + toReturn.AppendLine("Full:"); + toReturn.AppendLine(this.FullStack); + + return toReturn.ToString(); + } + + + /* + private static string SafeGetStartOfLine(string line) + { + line = line.Trim(); + + if (line.Length < 15) + { + return line; + } + else + { + return line.Substring(0, 13).Trim() + "..."; + } + } + */ + } +} diff --git a/Hosts/Silverlight/Iron7/Common/TwitterSettings.cs b/Hosts/Silverlight/Iron7/Common/TwitterSettings.cs new file mode 100644 index 0000000000..cad2d1970b --- /dev/null +++ b/Hosts/Silverlight/Iron7/Common/TwitterSettings.cs @@ -0,0 +1,34 @@ +using System; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Ink; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; + +namespace Iron7.Common +{ + public class TwitterSettings + { + // Make sure you set your own ConsumerKey and Secret from dev.twitter.com + public static string ConsumerKey = "V3NLoYbTHsidf67BJ5ZlA"; + public static string ConsumerKeySecret = "uZcCs4mGwrqb4rVLCcjcf2BwOojFqtSTi0Sv94nZU"; + + public static string RequestTokenUri = "https://api.twitter.com/oauth/request_token"; + public static string OAuthVersion = "1.0"; + public static string CallbackUri = "http://www.cirrious.com"; + public static string AuthorizeUri = "https://api.twitter.com/oauth/authorize"; + public static string AccessTokenUri = "https://api.twitter.com/oauth/access_token"; + } + + public class TwitterAccess + { + public string AccessToken { get; set; } + public string AccessTokenSecret { get; set; } + public string UserId { get; set; } + public string ScreenName { get; set; } + } +} diff --git a/Hosts/Silverlight/Iron7/Common/WhichTheme.cs b/Hosts/Silverlight/Iron7/Common/WhichTheme.cs new file mode 100644 index 0000000000..7450434d56 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Common/WhichTheme.cs @@ -0,0 +1,24 @@ +using System; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Ink; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; + +namespace Iron7.Common +{ + public class WhichTheme + { + public static bool IsLight() + { + // thanks to http://insomniacgeek.com/code/how-to-detect-dark-or-light-theme-in-windows-phone-7/ + // don't use binding because of bug - http://betaforums.silverlight.net/forums/p/14658/229679.aspx + var isLightTheme = (Visibility)Application.Current.Resources["PhoneLightThemeVisibility"]; + return isLightTheme == Visibility.Visible; + } + } +} diff --git a/Hosts/Silverlight/Iron7/Controls/RelativeAnimatingContentControl.cs b/Hosts/Silverlight/Iron7/Controls/RelativeAnimatingContentControl.cs new file mode 100644 index 0000000000..ae56cb8fad --- /dev/null +++ b/Hosts/Silverlight/Iron7/Controls/RelativeAnimatingContentControl.cs @@ -0,0 +1,626 @@ +// (c) Copyright Microsoft Corporation. +// This source is subject to the Microsoft Public License (Ms-PL). +// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. +// All other rights reserved. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; +using System.Windows.Media.Animation; + +// This is a very special primitive control that works around a limitation in +// the core animation subsystem of Silverlight: there is no way to declare in +// VSM states relative properties, such as animating from 0 to 33% the width of +// the control, using double animations for translation. +// +// It's a tough problem to solve property, but this primitive, unsupported +// control does offer a solution based on magic numbers that still allows a +// designer to make alterations to their animation values to present their +// vision for custom templates. +// +// This is instrumental in offering a Windows Phone ProgressBar implementation +// that uses the render thread instead of animating UI thread-only properties. +// +// For questions, please see +// http://www.jeff.wilcox.name/performanceprogressbar/ +// +// This control is licensed Ms-PL and as such comes with no warranties or +// official support. +// +// Style Note +// - - - +// The style that must be used with this is present at the bottom of this file. +// + +namespace Microsoft.Phone.Controls.Unsupported +{ + /// + /// A very specialized primitive control that works around a specific visual + /// state manager issue. The platform does not support relative sized + /// translation values, and this special control walks through visual state + /// animation storyboards looking for magic numbers to use as percentages. + /// This control is not supported, unofficial, and is a hack in many ways. + /// It is used to enable a Windows Phone native platform-style progress bar + /// experience in indeterminate mode that remains performant. + /// + public class RelativeAnimatingContentControl : ContentControl + { + /// + /// A simple Epsilon-style value used for trying to determine the magic + /// state, if any, of a double. + /// + private const double SimpleDoubleComparisonEpsilon = 0.000009; + + /// + /// The last known width of the control. + /// + private double _knownWidth; + + /// + /// The last known height of the control. + /// + private double _knownHeight; + + /// + /// A set of custom animation adapters used to update the animation + /// storyboards when the size of the control changes. + /// + private List _specialAnimations; + + /// + /// Initializes a new instance of the RelativeAnimatingContentControl + /// type. + /// + public RelativeAnimatingContentControl() + { + SizeChanged += OnSizeChanged; + } + + /// + /// Handles the size changed event. + /// + /// The source object. + /// The event arguments. + private void OnSizeChanged(object sender, SizeChangedEventArgs e) + { + if (e != null && e.NewSize.Height > 0 && e.NewSize.Width > 0) + { + _knownWidth = e.NewSize.Width; + _knownHeight = e.NewSize.Height; + + Clip = new RectangleGeometry { Rect = new Rect(0, 0, _knownWidth, _knownHeight), }; + + UpdateAnyAnimationValues(); + } + } + + /// + /// Walks through the known storyboards in the control's template that + /// may contain magic double animation values, storing them for future + /// use and updates. + /// + private void UpdateAnyAnimationValues() + { + if (_knownHeight > 0 && _knownWidth > 0) + { + // Initially, before any special animations have been found, + // the visual state groups of the control must be explored. + // By definition they must be at the implementation root of the + // control, and this is designed to not walk into any other + // depth. + if (_specialAnimations == null) + { + _specialAnimations = new List(); + + foreach (VisualStateGroup group in VisualStateManager.GetVisualStateGroups(this)) + { + if (group == null) + { + continue; + } + foreach (VisualState state in group.States) + { + if (state != null) + { + Storyboard sb = state.Storyboard; + if (sb != null) + { + // Examine all children of the storyboards, + // looking for either type of double + // animation. + foreach (Timeline timeline in sb.Children) + { + DoubleAnimation da = timeline as DoubleAnimation; + DoubleAnimationUsingKeyFrames dakeys = timeline as DoubleAnimationUsingKeyFrames; + if (da != null) + { + ProcessDoubleAnimation(da); + } + else if (dakeys != null) + { + ProcessDoubleAnimationWithKeys(dakeys); + } + } + } + } + } + } + } + + // Update special animation values relative to the current size. + UpdateKnownAnimations(); + } + } + + /// + /// Walks through all special animations, updating based on the current + /// size of the control. + /// + private void UpdateKnownAnimations() + { + foreach (AnimationValueAdapter adapter in _specialAnimations) + { + adapter.UpdateWithNewDimension(_knownWidth, _knownHeight); + } + } + + /// + /// Processes a double animation with keyframes, looking for known + /// special values to store with an adapter. + /// + /// The double animation using key frames instance. + private void ProcessDoubleAnimationWithKeys(DoubleAnimationUsingKeyFrames da) + { + // Look through all keyframes in the instance. + foreach (DoubleKeyFrame frame in da.KeyFrames) + { + var d = DoubleAnimationFrameAdapter.GetDimensionFromMagicNumber(frame.Value); + if (d.HasValue) + { + _specialAnimations.Add(new DoubleAnimationFrameAdapter(d.Value, frame)); + } + } + } + + /// + /// Processes a double animation looking for special values. + /// + /// The double animation instance. + private void ProcessDoubleAnimation(DoubleAnimation da) + { + // Look for a special value in the To property. + if (da.To.HasValue) + { + var d = DoubleAnimationToAdapter.GetDimensionFromMagicNumber(da.To.Value); + if (d.HasValue) + { + _specialAnimations.Add(new DoubleAnimationToAdapter(d.Value, da)); + } + } + + // Look for a special value in the From property. + if (da.From.HasValue) + { + var d = DoubleAnimationFromAdapter.GetDimensionFromMagicNumber(da.To.Value); + if (d.HasValue) + { + _specialAnimations.Add(new DoubleAnimationFromAdapter(d.Value, da)); + } + } + } + + #region Private animation updating system + /// + /// A selection of dimensions of interest for updating an animation. + /// + private enum DoubleAnimationDimension + { + /// + /// The width (horizontal) dimension. + /// + Width, + + /// + /// The height (vertical) dimension. + /// + Height, + } + + /// + /// A simple class designed to store information about a specific + /// animation instance and its properties. Able to update the values at + /// runtime. + /// + private abstract class AnimationValueAdapter + { + /// + /// Gets or sets the original double value. + /// + protected double OriginalValue { get; set; } + + /// + /// Initializes a new instance of the AnimationValueAdapter type. + /// + /// The dimension of interest for updates. + public AnimationValueAdapter(DoubleAnimationDimension dimension) + { + Dimension = dimension; + } + + /// + /// Gets the dimension of interest for the control. + /// + public DoubleAnimationDimension Dimension { get; private set; } + + /// + /// Updates the original instance based on new dimension information + /// from the control. Takes both and allows the subclass to make the + /// decision on which ratio, values, and dimension to use. + /// + /// The width of the control. + /// The height of the control. + public abstract void UpdateWithNewDimension(double width, double height); + } + + private abstract class GeneralAnimationValueAdapter : AnimationValueAdapter + { + /// + /// Stores the animation instance. + /// + protected T Instance { get; set; } + + /// + /// Gets the value of the underlying property of interest. + /// + /// Returns the value of the property. + protected abstract double GetValue(); + + /// + /// Sets the value for the underlying property of interest. + /// + /// The new value for the property. + protected abstract void SetValue(double newValue); + + /// + /// Gets the initial value (minus the magic number portion) that the + /// designer stored within the visual state animation property. + /// + protected double InitialValue { get; private set; } + + /// + /// The ratio based on the original magic value, used for computing + /// the updated animation property of interest when the size of the + /// control changes. + /// + private double _ratio; + + /// + /// Initializes a new instance of the GeneralAnimationValueAdapter + /// type. + /// + /// The dimension of interest. + /// The animation type instance. + public GeneralAnimationValueAdapter(DoubleAnimationDimension d, T instance) + : base(d) + { + Instance = instance; + + InitialValue = StripMagicNumberOff(GetValue()); + _ratio = InitialValue / 100; + } + + /// + /// Approximately removes the magic number state from a value. + /// + /// The initial number. + /// Returns a double with an adjustment for the magic + /// portion of the number. + public double StripMagicNumberOff(double number) + { + return Dimension == DoubleAnimationDimension.Width ? number - .1 : number - .2; + } + + /// + /// Retrieves the dimension, if any, from the number. If the number + /// is not magic, null is returned instead. + /// + /// The double value. + /// Returs a double animation dimension, if the number was + /// partially magic; otherwise, returns null. + public static DoubleAnimationDimension? GetDimensionFromMagicNumber(double number) + { + double floor = Math.Floor(number); + double remainder = number - floor; + + if (remainder >= .1 - SimpleDoubleComparisonEpsilon && remainder <= .1 + SimpleDoubleComparisonEpsilon) + { + return DoubleAnimationDimension.Width; + } + if (remainder >= .2 - SimpleDoubleComparisonEpsilon && remainder <= .2 + SimpleDoubleComparisonEpsilon) + { + return DoubleAnimationDimension.Height; + } + return null; + } + + /// + /// Updates the animation instance based on the dimensions of the + /// control. + /// + /// The width of the control. + /// The height of the control. + public override void UpdateWithNewDimension(double width, double height) + { + double size = Dimension == DoubleAnimationDimension.Width ? width : height; + UpdateValue(size); + } + + /// + /// Updates the value of the property. + /// + /// The size of interest to use with a ratio + /// computation. + private void UpdateValue(double sizeToUse) + { + SetValue(sizeToUse * _ratio); + } + } + + /// + /// Adapter for DoubleAnimation's To property. + /// + private class DoubleAnimationToAdapter : GeneralAnimationValueAdapter + { + /// + /// Gets the value of the underlying property of interest. + /// + /// Returns the value of the property. + protected override double GetValue() + { + return (double)Instance.To; + } + + /// + /// Sets the value for the underlying property of interest. + /// + /// The new value for the property. + protected override void SetValue(double newValue) + { + Instance.To = newValue; + } + + /// + /// Initializes a new instance of the DoubleAnimationToAdapter type. + /// + /// The dimension of interest. + /// The instance of the animation type. + public DoubleAnimationToAdapter(DoubleAnimationDimension dimension, DoubleAnimation instance) + : base(dimension, instance) + { + } + } + + /// + /// Adapter for DoubleAnimation's From property. + /// + private class DoubleAnimationFromAdapter : GeneralAnimationValueAdapter + { + /// + /// Gets the value of the underlying property of interest. + /// + /// Returns the value of the property. + protected override double GetValue() + { + return (double)Instance.From; + } + + /// + /// Sets the value for the underlying property of interest. + /// + /// The new value for the property. + protected override void SetValue(double newValue) + { + Instance.From = newValue; + } + + /// + /// Initializes a new instance of the DoubleAnimationFromAdapter + /// type. + /// + /// The dimension of interest. + /// The instance of the animation type. + public DoubleAnimationFromAdapter(DoubleAnimationDimension dimension, DoubleAnimation instance) + : base(dimension, instance) + { + } + } + + /// + /// Adapter for double key frames. + /// + private class DoubleAnimationFrameAdapter : GeneralAnimationValueAdapter + { + /// + /// Gets the value of the underlying property of interest. + /// + /// Returns the value of the property. + protected override double GetValue() + { + return Instance.Value; + } + + /// + /// Sets the value for the underlying property of interest. + /// + /// The new value for the property. + protected override void SetValue(double newValue) + { + Instance.Value = newValue; + } + + /// + /// Initializes a new instance of the DoubleAnimationFrameAdapter + /// type. + /// + /// The dimension of interest. + /// The instance of the animation type. + public DoubleAnimationFrameAdapter(DoubleAnimationDimension dimension, DoubleKeyFrame frame) + : base(dimension, frame) + { + } + } + #endregion + } + + /* + This is the style that should be used with the control. Make sure to define + the XMLNS at the top of the style file similar to this: + xmlns:unsupported="clr-namespace:Microsoft.Phone.Controls.Unsupported" + + + + */ +} + diff --git a/Hosts/Silverlight/Iron7/Images/appbar.delete.rest.png b/Hosts/Silverlight/Iron7/Images/appbar.delete.rest.png new file mode 100644 index 0000000000..95bb16dabe Binary files /dev/null and b/Hosts/Silverlight/Iron7/Images/appbar.delete.rest.png differ diff --git a/Hosts/Silverlight/Iron7/Images/appbar.edit.rest.png b/Hosts/Silverlight/Iron7/Images/appbar.edit.rest.png new file mode 100644 index 0000000000..7aa7412d93 Binary files /dev/null and b/Hosts/Silverlight/Iron7/Images/appbar.edit.rest.png differ diff --git a/Hosts/Silverlight/Iron7/Images/appbar.feature.email.rest.png b/Hosts/Silverlight/Iron7/Images/appbar.feature.email.rest.png new file mode 100644 index 0000000000..416a26023a Binary files /dev/null and b/Hosts/Silverlight/Iron7/Images/appbar.feature.email.rest.png differ diff --git a/Hosts/Silverlight/Iron7/Images/appbar.new.rest.png b/Hosts/Silverlight/Iron7/Images/appbar.new.rest.png new file mode 100644 index 0000000000..a90137f7df Binary files /dev/null and b/Hosts/Silverlight/Iron7/Images/appbar.new.rest.png differ diff --git a/Hosts/Silverlight/Iron7/Images/appbar.transport.play.rest.png b/Hosts/Silverlight/Iron7/Images/appbar.transport.play.rest.png new file mode 100644 index 0000000000..5cf7d9c401 Binary files /dev/null and b/Hosts/Silverlight/Iron7/Images/appbar.transport.play.rest.png differ diff --git a/Hosts/Silverlight/Iron7/Iron7.csproj b/Hosts/Silverlight/Iron7/Iron7.csproj new file mode 100644 index 0000000000..e472082c96 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Iron7.csproj @@ -0,0 +1,313 @@ + + + + Debug + AnyCPU + 10.0.20506 + 2.0 + {BA04401A-D2DF-4BD5-814A-85F2959F3CBB} + {C089C8C0-30E0-4E22-80C0-CE093F111A43};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} + Library + Properties + Iron7 + Iron7 + v4.0 + $(TargetFrameworkVersion) + WindowsPhone + Silverlight + true + + + true + true + Iron7.xap + Properties\AppManifest.xml + Iron7.App + true + true + + + true + full + false + Bin\Debug + DEBUG;TRACE;SILVERLIGHT;WINDOWS_PHONE + true + true + prompt + 4 + + + pdbonly + true + Bin\Release + TRACE;SILVERLIGHT;WINDOWS_PHONE + true + true + prompt + 4 + + + + + + + + + + + ..\..\..\Util\Iron7Libs\JSON.Net\Silverlight\Newtonsoft.Json.Silverlight.dll + + + + + + + + ..\..\..\Util\Iron7Libs\Silverlight\System.Windows.Controls.dll + + + + ..\..\..\bin\Silverlight3$(Configuration)\IronRuby.dll + + + ..\..\..\bin\Silverlight3$(Configuration)\IronRuby.dll + + + ..\..\..\bin\Silverlight3$(Configuration)\IronRuby.Libraries.dll + + + ..\..\..\bin\Silverlight3$(Configuration)\Microsoft.Dynamic.dll + + + ..\..\..\bin\Silverlight3$(Configuration)\Microsoft.Scripting.dll + + + ..\..\..\bin\Silverlight3$(Configuration)\Microsoft.Scripting.Core.dll + + + + + + App.xaml + + + True + True + AppResources.resx + + + + + + + + + + + + + + True + True + ScriptResources.resx + + + + + + + + + + + + CodeMirrorCredits.xaml + + + + ScriptListOnlinePage.xaml + + + EditPage.xaml + + + + IronPage.xaml + + + + MainPage.xaml + + + + + + PropertiesPage.xaml + + + StyledChildWindow.xaml + + + TagsOnlinePage.xaml + + + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + + + + Designer + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + + + + + + + + + + + + + + + + + + + + + + + Always + + + Always + + + Always + + + + Always + + + + + + + + + + + + + + + + + + + + + + + + + + + PreserveNewest + + + + PreserveNewest + + + + + + + ResXFileCodeGenerator + AppResources.Designer.cs + Designer + + + ResXFileCodeGenerator + ScriptResources.Designer.cs + + + + + + + + + + + + + \ No newline at end of file diff --git a/Hosts/Silverlight/Iron7/Iron7.sln b/Hosts/Silverlight/Iron7/Iron7.sln new file mode 100644 index 0000000000..a0c126b625 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Iron7.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Iron7", "Iron7.csproj", "{BA04401A-D2DF-4BD5-814A-85F2959F3CBB}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {BA04401A-D2DF-4BD5-814A-85F2959F3CBB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BA04401A-D2DF-4BD5-814A-85F2959F3CBB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BA04401A-D2DF-4BD5-814A-85F2959F3CBB}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {BA04401A-D2DF-4BD5-814A-85F2959F3CBB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BA04401A-D2DF-4BD5-814A-85F2959F3CBB}.Release|Any CPU.Build.0 = Release|Any CPU + {BA04401A-D2DF-4BD5-814A-85F2959F3CBB}.Release|Any CPU.Deploy.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Hosts/Silverlight/Iron7/Iron7Server/ScriptListing.cs b/Hosts/Silverlight/Iron7/Iron7Server/ScriptListing.cs new file mode 100644 index 0000000000..87ec1c85fb --- /dev/null +++ b/Hosts/Silverlight/Iron7/Iron7Server/ScriptListing.cs @@ -0,0 +1,29 @@ +using System; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Ink; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; +using System.Collections.Generic; + +namespace Iron7.Iron7Server +{ + public class ScriptListing + { + public class Item + { + public string AuthorName { get; set; } + public string ScriptId { get; set; } + public string Title { get; set; } + public DateTime WhenLastEdited { get; set; } + } + + public List Scripts { get; set; } + public bool MoreAvailable { get; set; } + public string NextPageUrl { get; set; } + } +} diff --git a/Hosts/Silverlight/Iron7/Iron7Server/SimpleScriptDetail.cs b/Hosts/Silverlight/Iron7/Iron7Server/SimpleScriptDetail.cs new file mode 100644 index 0000000000..b76c1b1191 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Iron7Server/SimpleScriptDetail.cs @@ -0,0 +1,23 @@ +using System; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Ink; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; + +namespace Iron7.Iron7Server +{ + public class SimpleScriptDetail + { + public string AuthorName { get; set; } + public string Code { get; set; } + public string TagsAsText { get; set; } + public string ScriptId { get; set; } + public string Title { get; set; } + public DateTime WhenLastEdited { get; set; } + } +} diff --git a/Hosts/Silverlight/Iron7/Iron7Server/TagListing.cs b/Hosts/Silverlight/Iron7/Iron7Server/TagListing.cs new file mode 100644 index 0000000000..b387ca04e0 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Iron7Server/TagListing.cs @@ -0,0 +1,19 @@ +using System; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Ink; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; + +namespace Iron7.Iron7Server +{ + public class TagListItem + { + public string Tag { get; set; } + public int Count { get; set; } + } +} diff --git a/Hosts/Silverlight/Iron7/Properties/AppManifest.xml b/Hosts/Silverlight/Iron7/Properties/AppManifest.xml new file mode 100644 index 0000000000..6712a11783 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Properties/AppManifest.xml @@ -0,0 +1,6 @@ + + + + diff --git a/Hosts/Silverlight/Iron7/Properties/AssemblyInfo.cs b/Hosts/Silverlight/Iron7/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..716d029f8c --- /dev/null +++ b/Hosts/Silverlight/Iron7/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Iron7 Free")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Cirrious")] +[assembly: AssemblyProduct("Iron7 Free")] +[assembly: AssemblyCopyright("Copyright © Cirrious 2010")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("08FE0B2C-7BAE-4929-88E2-EFC233F2E93A")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("1.8.0.0")] +[assembly: AssemblyFileVersion("1.8.0.0")] diff --git a/Hosts/Silverlight/Iron7/Properties/WMAppManifest.xml b/Hosts/Silverlight/Iron7/Properties/WMAppManifest.xml new file mode 100644 index 0000000000..eaede03f3b --- /dev/null +++ b/Hosts/Silverlight/Iron7/Properties/WMAppManifest.xml @@ -0,0 +1,31 @@ + + + + spacesquare62.png + + + + + + + + + + + + + + + + + + + + spacesquare173.png + 0 + Iron7 Free + + + + + \ No newline at end of file diff --git a/Hosts/Silverlight/Iron7/Resources/Accelerometer.txt b/Hosts/Silverlight/Iron7/Resources/Accelerometer.txt new file mode 100644 index 0000000000..6af6828598 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Resources/Accelerometer.txt @@ -0,0 +1,81 @@ +include System +include System::Windows +include System::Windows::Media +include System::Windows::Media::Imaging +include System::Windows::Controls +include System::Windows::Shapes +include Microsoft::Phone::Controls::Maps + +def get_brush(color) + brush = SolidColorBrush.new + brush.Color = color + return brush +end + +def init + $WIDTH = 400 + $HEIGHT = 400 + $BUBBLE_RADIUS = 25 + + $mid_x = ($WIDTH - $BUBBLE_RADIUS)/2 + $mid_y = ($HEIGHT - $BUBBLE_RADIUS)/2 + $last_x = $mid_x + $last_y = $mid_y + + $canvas = Canvas.new + $canvas.width = $WIDTH + $canvas.height = $HEIGHT + + rect = Rectangle.new + rect.width = $WIDTH + rect.height = $HEIGHT + rect.fill = get_brush(Colors.white) + rect.stroke = get_brush(Colors.light_gray) + $canvas.children.add(rect) + + $bubble = Ellipse.new + $bubble.fill = get_brush(Colors.blue) + $bubble.width = $BUBBLE_RADIUS + $bubble.height = $BUBBLE_RADIUS + $bubble.margin = Thickness.new($last_x, $last_y, 0, 0) + + $canvas.children.add($bubble) + + $text_block_x = TextBlock.new + $text_block_y = TextBlock.new + $text_block_z = TextBlock.new + + text_stack_panel = StackPanel.new + text_stack_panel.horizontal_alignment = HorizontalAlignment.center + text_stack_panel.children.add($text_block_x) + text_stack_panel.children.add($text_block_y) + text_stack_panel.children.add($text_block_z) + + stack_panel = StackPanel.new + stack_panel.horizontal_alignment = HorizontalAlignment.center + stack_panel.children.add($canvas) + stack_panel.children.add(text_stack_panel) + + Host.content_holder.children.add(stack_panel); + + Host.fix_orientation_portrait + Host.start_accelerometer(0.1, "show_current_accel") +end + +def show_current_accel + x = AccelerometerReading.x + y = AccelerometerReading.y + z = AccelerometerReading.z + + $text_block_x.text = "x is " + x.to_s + $text_block_y.text = "y is " + y.to_s + $text_block_z.text = "z is " + z.to_s + + # could do with some smoothing here really... + $last_x = $mid_x * (1 - x) + $last_y = $mid_y * (1 + y) + + $bubble.margin = Thickness.new($last_x, $last_y, 0, 0) +end + +init if $canvas.nil? \ No newline at end of file diff --git a/Hosts/Silverlight/Iron7/Resources/AccelerometerSpace.txt b/Hosts/Silverlight/Iron7/Resources/AccelerometerSpace.txt new file mode 100644 index 0000000000..1fabf32c81 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Resources/AccelerometerSpace.txt @@ -0,0 +1,66 @@ +include System +include System::Windows +include System::Windows::Media +include System::Windows::Media::Imaging +include System::Windows::Controls +include System::Windows::Shapes +include Microsoft::Phone::Controls::Maps + +def init + $WIDTH = 480 + $HEIGHT = 800 + $SHIP_SIZE = 100 + $HALF_SHIP_SIZE = 50 + + $mid_x = ($WIDTH - $SHIP_SIZE)/2 + $mid_y = ($HEIGHT - $SHIP_SIZE)/2 + $x = $mid_x + $y = $mid_y + + # note - if no internet connection, then no ship will show! + # could work around this by calling + ship_src = Uri.new("http://iron7.com/forapp/spaceship.png") + ship_image = BitmapImage.new(ship_src) + ship = Image.new + ship.source = ship_image + ship.width = $SHIP_SIZE + ship.height = $SHIP_SIZE + + $ship_border = Border.new + $ship_border.border_brush = SolidColorBrush.new(Colors.red) + $ship_border.border_thickness = Thickness.new(1) + $ship_border.child = ship + show_ship + + $canvas = Canvas.new + $canvas.width = $WIDTH + $canvas.height = $HEIGHT + $canvas.children.add($ship_border) + + Host.monitor_control("ship", ship, "image_listener") + Host.content_holder.children.add($canvas); + Host.fix_orientation_portrait + Host.start_accelerometer(0.05, "show_current_accel") +end + +def show_ship + $ship_border.margin = Thickness.new($x,$y,0,0) +end + +def show_current_accel + x = AccelerometerReading.x + y = AccelerometerReading.y + + # could do with some smoothing here really... + $x = $mid_x * (1 - x) + $y = $mid_y * (1 + 0.75 * y) + $ship_border.margin = Thickness.new($x,$y,0,0) + show_ship +end + +def image_listener + MessageBox.show("Sorry - could not load image") if Calling_event == "image_failed" + $ship_border.border_thickness = Thickness.new(0) if Calling_event == "image_opened" +end + +init if $ship_border.nil? diff --git a/Hosts/Silverlight/Iron7/Resources/BaseEditorHtml.html b/Hosts/Silverlight/Iron7/Resources/BaseEditorHtml.html new file mode 100644 index 0000000000..988090749e --- /dev/null +++ b/Hosts/Silverlight/Iron7/Resources/BaseEditorHtml.html @@ -0,0 +1,54 @@ + + + + + + + + + +

+Name +

+ +

+Code +

+ + + \ No newline at end of file diff --git a/Hosts/Silverlight/Iron7/Resources/BaseEditorHtml.txt b/Hosts/Silverlight/Iron7/Resources/BaseEditorHtml.txt new file mode 100644 index 0000000000..98188cb19c --- /dev/null +++ b/Hosts/Silverlight/Iron7/Resources/BaseEditorHtml.txt @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Hosts/Silverlight/Iron7/Resources/BigButtonGame.txt b/Hosts/Silverlight/Iron7/Resources/BigButtonGame.txt new file mode 100644 index 0000000000..e74f41ed0d --- /dev/null +++ b/Hosts/Silverlight/Iron7/Resources/BigButtonGame.txt @@ -0,0 +1,110 @@ +# a simple button game + +include System +include System::Windows +include System::Windows::Media +include System::Windows::Media::Imaging +include System::Windows::Controls +include System::Windows::Shapes +include Microsoft::Phone::Controls::Maps + +CLICKS_PER_GAME = 5 +BUTTON_DIMENSION = 100 +SPARE_WIDTH = 380 +SPARE_HEIGHT = 700 + +def set_button_idle_state + $button.content = "go" + $button.margin = Thickness.new(0,0,0,0) + $button.background = SolidColorBrush.new(Colors.red) + $button.foreground = SolidColorBrush.new(Colors.white) +end + +def set_button_playing_state + #$button.background = SolidColorBrush.new(Colors.cyan) + #$button.foreground = SolidColorBrush.new(Colors.magenta) +end + +def update_button_game_state + $button.content = $clicks_left.to_s + new_x = $random.next(SPARE_WIDTH) + new_y = $random.next(SPARE_HEIGHT) + $button.margin = Thickness.new(new_x,new_y,SPARE_WIDTH-new_x,SPARE_HEIGHT-new_y) +end + +def format_score(name, score) + return System::String.format("{0}: not played", name) if score.nil? + return System::String.format("{0}: {1:0.000}", name, score) +end + +def update_scores_text + $text_block_best.text = format_score("high", $best_score) + $text_block_last.text = format_score("last", $last_score) +end + +def init + $last_score = nil + $best_score = nil + stack = StackPanel.new + $text_block_best = TextBlock.new + $text_block_best.font_size = 30 + stack.children.add($text_block_best) + $text_block_last = TextBlock.new + $text_block_last.font_size = 30 + stack.children.add($text_block_last) + stack.horizontal_alignment=HorizontalAlignment.center + + $button = Button.new + $button.width = BUTTON_DIMENSION + $button.height = BUTTON_DIMENSION + + $clicks_left = 0 + $started = false + $when_game_started = Environment.tick_count + $random = Random.new + + Host.content_holder.children.add(stack) + Host.monitor_control("button_go", $button, "button_listener") + Host.content_holder.children.add($button) + Host.fix_orientation_portrait + + update_scores_text + set_button_idle_state +end + +def end_game + $started = false + time_taken = Environment.tick_count - $when_game_started + $last_score = time_taken.to_f / 1000.0 + if $best_score.nil? || $best_score > $last_score + $best_score = $last_score + end + update_scores_text + set_button_idle_state +end + +def test_for_end_of_game + return $clicks_left <= 0 +end + +def start_new_game + set_button_playing_state + $clicks_left = CLICKS_PER_GAME + $when_game_started = Environment.tick_count + $started = true +end + +init if $button.nil? +update_button_game_state + +def button_listener + if Calling_event=='button_clicked' + if $started + $clicks_left = $clicks_left - 1 + end_game if test_for_end_of_game + else + start_new_game + end + end + update_button_game_state +end diff --git a/Hosts/Silverlight/Iron7/Resources/ButtonGame.txt b/Hosts/Silverlight/Iron7/Resources/ButtonGame.txt new file mode 100644 index 0000000000..08495b8e4e --- /dev/null +++ b/Hosts/Silverlight/Iron7/Resources/ButtonGame.txt @@ -0,0 +1,127 @@ +# a simple button game + +include System +include System::Windows +include System::Windows::Media +include System::Windows::Media::Imaging +include System::Windows::Controls +include System::Windows::Shapes +include Microsoft::Phone::Controls::Maps + +CLICKS_PER_GAME = 5 +BUTTON_DIMENSION = 100 +SPARE_WIDTH = 380 +SPARE_HEIGHT = 700 + +def set_button_idle_state + $button.content = "go" + $button.margin = Thickness.new(0,0,0,0) + $button.background = SolidColorBrush.new(Colors.red) + $button.foreground = SolidColorBrush.new(Colors.white) +end + +def set_button_playing_state + # commented out - they just never worked! + # maybe some issue like http://forums.silverlight.net/forums/p/17896/207999.aspx + $button.background = SolidColorBrush.new(Colors.cyan) + $button.foreground = SolidColorBrush.new(Colors.magenta) +end + +def update_button_game_state + $button.content = $clicks_left.to_s + new_x = $random.next(SPARE_WIDTH) + new_y = $random.next(SPARE_HEIGHT) + $button.margin = Thickness.new(new_x,new_y,SPARE_WIDTH-new_x,SPARE_HEIGHT-new_y) +end + +def format_score(name, score) + return System::String.format("{0}: not played", name) if score.nil? + return System::String.format("{0}: {1:0.000}", name, score) +end + +def update_scores_text + $text_block_best.text = format_score("best", $best_score) + $text_block_last.text = format_score("last", $last_score) + $text_block_worst.text = format_score("worst", $worst_score) +end + +def create_text_block_in(panel) + text_block = TextBlock.new + text_block.font_size = 30 + panel.children.add(text_block) + return text_block +end + +def init + $last_score = nil + $best_score = nil + $worst_score = nil + + stack = StackPanel.new + stack.horizontal_alignment=HorizontalAlignment.center + + $text_block_best = create_text_block_in(stack) + $text_block_last = create_text_block_in(stack) + $text_block_worst = create_text_block_in(stack) + + $button = Button.new + $button.width = BUTTON_DIMENSION + $button.height = BUTTON_DIMENSION + + $clicks_left = 0 + $started = false + $when_game_started = Environment.tick_count + $random = Random.new + + Host.content_holder.children.add(stack) + Host.monitor_control("button_go", $button, "button_listener") + Host.content_holder.children.add($button) + Host.fix_orientation_portrait + + update_scores_text + set_button_idle_state +end + +def end_game + $started = false + time_taken = Environment.tick_count - $when_game_started + $last_score = time_taken.to_f / 1000.0 + if $best_score.nil? || $best_score > $last_score + $best_score = $last_score + end + if $worst_score.nil? || $worst_score < $last_score + $worst_score = $last_score + end + update_scores_text + set_button_idle_state +end + +def test_for_end_of_game + return $clicks_left <= 0 +end + +def start_new_game + set_button_playing_state + $clicks_left = CLICKS_PER_GAME + $when_game_started = Environment.tick_count + $started = true +end + +init if $button.nil? + +def button_listener + if Calling_event=='button_clicked' + if $started + $clicks_left = $clicks_left - 1 + if test_for_end_of_game + end_game + else + update_button_game_state + end + else + start_new_game + update_button_game_state + end + Host.vibrate(TimeSpan.from_seconds(0.05)) + end +end \ No newline at end of file diff --git a/Hosts/Silverlight/Iron7/Resources/Copy of Accelerometer.txt b/Hosts/Silverlight/Iron7/Resources/Copy of Accelerometer.txt new file mode 100644 index 0000000000..0f66ed8471 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Resources/Copy of Accelerometer.txt @@ -0,0 +1,149 @@ +# Include namespaces for ease of use +include System::Windows::Media +include System::Windows::Controls +include System::Windows::Threading +include System::Windows + + +def init + $WIDTH = 300 + $HEIGHT = 300 + $BUBBLE_RADIUS = 25 + $MAX_MOVEMENT = 5 + + # build our canvas + $canvas = System::Windows::Controls::Canvas.new + $canvas.width = $WIDTH + $canvas.height = $HEIGHT + + rect = System::Windows::Shapes::Rectangle.new + rect.width = $WIDTH + rect.height = $HEIGHT + rect_brush = System::Windows::Media::SolidColorBrush.new + rect_brush.Color = System::Windows::Media::Colors.white + rect.fill = rect_brush + rect_brush = System::Windows::Media::SolidColorBrush.new + rect_brush.Color = System::Windows::Media::Colors.light_gray + rect.stroke = rect_brush + $canvas.children.add(rect) + + $bubble = System::Windows::Shapes::Ellipse.new + bubble_brush = System::Windows::Media::SolidColorBrush.new + bubble_brush.Color = System::Windows::Media::Colors.blue + $bubble.fill = bubble_brush + $bubble.width = $BUBBLE_RADIUS + $bubble.height = $BUBBLE_RADIUS + + $mid_x = ($WIDTH - $BUBBLE_RADIUS)/2 + $mid_y = ($HEIGHT - $BUBBLE_RADIUS)/2 + $last_x = $mid_x + $last_y = $mid_y + $bubble.margin = System::Windows::Thickness.new($last_x, $last_y, 0, 0) + + $canvas.children.add($bubble) + + $text_block_x = TextBlock.new + $text_block_y = TextBlock.new + $text_block_z = TextBlock.new + + stack_panel = StackPanel.new + stack_panel.horizontal_alignment = HorizontalAlignment.center + stack_panel.children.add($canvas) + stack_panel.children.add($text_block_x) + stack_panel.children.add($text_block_y) + stack_panel.children.add($text_block_z) + + Phone.find_name("ContentGrid").children.add(stack_panel) + + Phone.fix_orientation_portrait + Phone.start_accelerometer(0.1) +end + +def show_current_accel + x = AccelerometerReading.x + y = AccelerometerReading.y + z = AccelerometerReading.z + + $text_block_x.text = "x is " + x.to_s + $text_block_y.text = "y is " + y.to_s + $text_block_z.text = "z is " + z.to_s + + $last_x = $mid_x * (1 - x) + $last_y = $mid_y * (1 + y) + + $bubble.margin = System::Windows::Thickness.new($last_x, $last_y, 0, 0) +end + +if $canvas.nil? + init +end + +if Calling_sender == "accelerometer" + show_current_accel +end + +def ignore_this + case Orientation + when 34 # Microsoft::Phone::PageOrientation.LandscapeRight + x = y0 + y = -x0 + z = z0 + when 18 # Microsoft::Phone::PageOrientation.LandscapeLeft + x = y0 + y = x0 + z = z0 + when 2 # Microsoft::Phone::PageOrientation.Landscape + x = y0 + y = x0 + z = z0 + when 9 # Microsoft::Phone::PageOrientation.PortraitDown + x = -x0 + y = y0 + z = z0 + when 5 # Microsoft::Phone::PageOrientation.PortraitUp + x = x0 + y = y0 + z = z0 + when 1 # Microsoft::Phone::PageOrientation.Portrait + x = x0 + y = y0 + z = z0 + else + x = x0 + y = y0 + z = z0 + end + + + new_x_ideal = $mid_x * (1 - x) + new_y_ideal = $mid_y * (1 + y) + + # TODO - make this more DRY + if new_y_ideal > $last_y + if new_y_ideal - $last_y > $MAX_MOVEMENT + $last_y = $last_y + $MAX_MOVEMENT + else + $last_y = new_y_ideal + end + else + if $last_y - new_y_ideal > $MAX_MOVEMENT + $last_y = $last_y - $MAX_MOVEMENT + else + $last_y = new_y_ideal + end + end + + if new_x_ideal > $last_x + if new_x_ideal - $last_x > $MAX_MOVEMENT + $last_x = $last_x + $MAX_MOVEMENT + else + $last_x = new_x_ideal + end + else + if $last_x - new_x_ideal > $MAX_MOVEMENT + $last_x = $last_x - $MAX_MOVEMENT + else + $last_x = new_x_ideal + end + end +end \ No newline at end of file diff --git a/Hosts/Silverlight/Iron7/Resources/Drawing.txt b/Hosts/Silverlight/Iron7/Resources/Drawing.txt new file mode 100644 index 0000000000..746e588f9c --- /dev/null +++ b/Hosts/Silverlight/Iron7/Resources/Drawing.txt @@ -0,0 +1,123 @@ +# this script based on fingerpaint +# from http://sigurdsnorteland.wordpress.com/ + +include System +include System::Windows +include System::Windows::Media +include System::Windows::Media::Imaging +include System::Windows::Controls +include System::Windows::Shapes +include Microsoft::Phone::Controls::Maps + +def get_brush(color) + brush = SolidColorBrush.new + brush.Color = color + return brush +end + +def create_button(color) + rectangle = Rectangle.new + rectangle.height = 40 + rectangle.width = 40 + rectangle.fill = get_brush(color) + new_button = Button.new + new_button.content = rectangle + new_button.padding = Thickness.new(0,0,0,0) + return new_button +end + +def init + $colors = { + "magenta" => Colors.magenta, + "cyan" => Colors.cyan, + "yellow" => Colors.yellow, + "red" => Colors.red, + "green" => Colors.green, + "orange" => Colors.orange, + "white" => Colors.white, + "black" => Colors.black + } + $current_color = Colors.cyan + + $last_point = nil + + $canvas = Canvas.new + $canvas.width = 800 + $canvas.height = 800 + $canvas.background = get_brush(Colors.white) + + stack_panel_controls = StackPanel.new + stack_panel_controls.orientation = Orientation.horizontal + + $colors.each { |key, value| + color_button = create_button(value) + stack_panel_controls.children.add(color_button) + Host.monitor_control(key, color_button, "button_listener") + } + + stack_panel_main = StackPanel.new + stack_panel_main.children.add(stack_panel_controls) + stack_panel_main.children.add($canvas) + + Host.content_holder.horizontal_alignment = HorizontalAlignment.Center + Host.content_holder.children.add(stack_panel_main) + Host.monitor_control("canvas", $canvas, "canvas_listener") +end + +def current_point + return [Mouse_x, Mouse_y] +end + +def on_mouse_down + $last_point = current_point +end + +def on_mouse_move + if $last_point.nil? + return + end + + new_point = current_point + + new_line = Line.new + new_line.stroke = get_brush($current_color) + new_line.stroke_thickness = 20 + new_line.stroke_start_line_cap = PenLineCap.round; + new_line.stroke_end_line_cap = PenLineCap.round; + + new_line.opacity = 0.8; + + new_line.x1 = $last_point[0] + new_line.x2 = new_point[0] + new_line.y1 = $last_point[1] + new_line.y2 = new_point[1] + + $canvas.children.add(new_line) + + $last_point = new_point +end + +def on_mouse_up + return if $last_point.nil? + $last_point = nil +end + + +init if $canvas.nil? + +def canvas_listener + case Calling_event + when "mouse_left_button_down" + on_mouse_down + when "mouse_left_button_up" + on_mouse_up + when "mouse_move" + on_mouse_move + end +end + +def button_listener + if Calling_event == "button_clicked" + $current_color = $colors[Calling_hint.to_s] + end +end \ No newline at end of file diff --git a/Hosts/Silverlight/Iron7/Resources/DynamicExamples.txt b/Hosts/Silverlight/Iron7/Resources/DynamicExamples.txt new file mode 100644 index 0000000000..f60e09ce27 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Resources/DynamicExamples.txt @@ -0,0 +1,40 @@ +include System +include System::Windows +include System::Windows::Media +include System::Windows::Media::Imaging +include System::Windows::Controls +include System::Windows::Shapes +include Microsoft::Phone::Controls::Maps + +def init + $initialized=1 + $counter_button=0 + + stack_panel = StackPanel.new + + $text_block = TextBlock.new + $text_block.font_size = 40 + $text_block.text_wrapping = TextWrapping.wrap + stack_panel.children.add($text_block) + + button1 = Button.new + button1.content = "press me" + stack_panel.children.add(button1) + + Host.content_holder.horizontal_alignment = HorizontalAlignment.center + Host.content_holder.children.add(stack_panel) + Host.monitor_control("button1", button1, "button_listener") + update_text +end + +def update_text + text = "button pressed " + $counter_button.to_s + " times" + $text_block.text = text +end + +def button_listener + $counter_button = $counter_button + 1 + update_text +end + +init if $initialized.nil? \ No newline at end of file diff --git a/Hosts/Silverlight/Iron7/Resources/FirstMap.txt b/Hosts/Silverlight/Iron7/Resources/FirstMap.txt new file mode 100644 index 0000000000..da36974b94 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Resources/FirstMap.txt @@ -0,0 +1,33 @@ +# for a full bing maps tutorial, see +# http://msdn.microsoft.com/en-us/wp7trainingcourse_usingbingmapslab_topic2 + +include System +include System::Windows +include System::Windows::Media +include System::Windows::Media::Imaging +include System::Windows::Controls +include System::Windows::Shapes +include Microsoft::Phone::Controls::Maps + +def init + $initialized = 1 + stack_panel = StackPanel.new + map = Map.new + map.height = 400 + map.width = 400 + # this key for Iron7 use only + # to create your own apps, register for a key at www.bingmapsportal.com + map.credentials_provider = ApplicationIdCredentialsProvider.new("YOUR_KEY") + rect = LocationRect.new(90,180,-90,-180) + map.set_view(rect) + stack_panel.children.add(map) + + textBlock = TextBlock.new + textBlock.font_size = 40 + textBlock.text = "hello world!" + stack_panel.children.add(textBlock) + + Host.content_holder.children.add(stack_panel) +end + +init if $initialized.nil? \ No newline at end of file diff --git a/Hosts/Silverlight/Iron7/Resources/Flickr.txt b/Hosts/Silverlight/Iron7/Resources/Flickr.txt new file mode 100644 index 0000000000..26c8be9e1b --- /dev/null +++ b/Hosts/Silverlight/Iron7/Resources/Flickr.txt @@ -0,0 +1,114 @@ +include System::Windows +include System::Windows::Media +include System::Windows::Controls +include System::Windows::Shapes +include System::Windows::Media::Imaging +include Microsoft::Phone::Controls + +def random_string + items = [ + "WP7", + "WindowsPhone", + "sunset", + "beach", + "ruby", + "ironruby"] + + random = System::Random.new + which = random.next(items.length) + + return items[which] +end + +def init + scroll_viewer = ScrollViewer.new + stack_panel_main = StackPanel.new + + $textbox_1 = TextBox.new + $textbox_1.text = random_string + stack_panel_main.children.add($textbox_1) + + button_1 = Button.new + button_1.content = "Search" + + stack_panel_main.children.add(button_1) + + $wrap_panel = WrapPanel.new + stack_panel_main.children.add($wrap_panel) + scroll_viewer.content = stack_panel_main + + $big_image = Image.new + $big_image.width = 480 + $big_image.height = 480 + $big_image.horizontal_alignment = HorizontalAlignment.center; + $big_image.vertical_alignment = VerticalAlignment.center; + $big_image.visibility = Visibility.collapsed + + grid = Grid.new + grid.children.add(scroll_viewer) + grid.children.add($big_image) + + Host.monitor_control("button_1", button_1, "button_listener") + Host.monitor_control("-1", $big_image, "big_image_listener") + Host.content_holder.children.add(grid) +end + +def hack_regex_flickr_urls(response) + image_urls=[] + # simple regex for: media": {"m":"http://farm6.static.flickr.com/nums_m.jpg" + regex = Regexp.new(/media.: ....:.(.*)..,$/) + matchdata = regex.match(response) + while matchdata != nil + image_urls.push(matchdata[1]) # extract just the first group + string1 = matchdata.post_match + matchdata = regex.match(string1) + end + + # MessageBox.show("num matches is " + image_urls.length.to_s) + # MessageBox.show("first match is " + image_urls[0]) + + return image_urls +end + +def process_flickr(response) + $wrap_panel.children.clear() + $big_image.visibility = Visibility.collapsed + + image_urls = hack_regex_flickr_urls(response) + image_urls.each { |image_url| + + index = $wrap_panel.children.count + image = Image.new + image.stretch = Stretch.fill + image.horizontal_alignment = HorizontalAlignment.left; + image.vertical_alignment = VerticalAlignment.top; + image.height = 160 + image.width = 160 + bitmap_image_source = BitmapImage.new + bitmap_image_source.uri_source = System::Uri.new(image_url) + image.source = bitmap_image_source + Host.monitor_control(index.to_s, image, "image_listener") + + $wrap_panel.children.add(image) + } +end + +init if $wrap_panel.nil? + +def web_listener + process_flickr(Web_response) +end +def button_listener + Host.call_text_web_service("flickr", "http://api.flickr.com/services/feeds/photos_public.gne?format=json&tags=" + $textbox_1.text, "web_listener") +end +def big_image_listener + if Calling_event == "mouse_left_button_down" + $big_image.visibility = Visibility.collapsed + end +end +def image_listener + if Calling_event == "mouse_left_button_down" + $big_image.source = Calling_sender.source + $big_image.visibility = Visibility.visible + end +end \ No newline at end of file diff --git a/Hosts/Silverlight/Iron7/Resources/FractalSnowflake.txt b/Hosts/Silverlight/Iron7/Resources/FractalSnowflake.txt new file mode 100644 index 0000000000..3a32d60496 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Resources/FractalSnowflake.txt @@ -0,0 +1,80 @@ +# fractal algortithm adapted from http://www.codeproject.com/KB/WPF/Fractals.aspx +# thanks to logicchild +# used under the Code Project Open License + +include System::Windows::Media +include System::Windows::Controls +include System::Windows::Shapes +include System::Windows + +def init + $FRACTAL_WIDTH = 300 + $FRACTAL_HEIGHT = 300 + $LENGTH_SCALE = 0.75; + $DELTA_THETA = Math::PI / 5; + $GLOBAL_II = 0 + + # build our canvas + $canvas = System::Windows::Controls::Canvas.new + $canvas.width = $FRACTAL_WIDTH + $canvas.height = $FRACTAL_HEIGHT + + $text_block = TextBlock.new + + stack_panel = StackPanel.new + stack_panel.horizontal_alignment = HorizontalAlignment.center + stack_panel.children.add($canvas) + stack_panel.children.add($text_block) + + Phone.find_name("ContentGrid").children.add(stack_panel) + + timer1 = System::Windows::Threading::DispatcherTimer.new + timer1.interval = System::TimeSpan.FromSeconds(1) + timer1.start + Phone.timers.add("timer1",timer1) + +end + +def drawBinaryTree(depth, x, y, length, theta) + x2 = x + length * Math.cos(theta); + y2 = y + length * Math.sin(theta); + + line = System::Windows::Shapes::Line.new; + brush = System::Windows::Media::SolidColorBrush.new + brush.color = System::Windows::Media::Colors.blue + line.stroke = brush; + line.x1 = x; + line.y1 = y; + line.x2 = x2; + line.y2 = y2; + $canvas.children.add(line) + + if depth > 1 + drawBinaryTree(depth - 1, x2, y2, length * $LENGTH_SCALE, theta + $DELTA_THETA) + drawBinaryTree(depth - 1, x2, y2, length * $LENGTH_SCALE, theta - $DELTA_THETA) + end +end + +def next_hop + $GLOBAL_II = $GLOBAL_II + 1 + if $GLOBAL_II < 21 + x = $canvas.width / 2 + y = 0.83 * $canvas.height + + $canvas.children.clear + drawBinaryTree($GLOBAL_II, x, y, 0.2 * $canvas.width, - Math::PI / 2) + + $text_block.text = "tree depth is " + $GLOBAL_II.to_s + end + + if $GLOBAL_II > 30 + $GLOBAL_II = 0 + end + +end + +if $canvas.nil? + init +end + +next_hop \ No newline at end of file diff --git a/Hosts/Silverlight/Iron7/Resources/FractalTree.txt b/Hosts/Silverlight/Iron7/Resources/FractalTree.txt new file mode 100644 index 0000000000..1fc79bdfc4 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Resources/FractalTree.txt @@ -0,0 +1,88 @@ +# fractal algortithm adapted from http://www.codeproject.com/KB/WPF/Fractals.aspx +# used under the Code Project Open License - huge thanks to "logicchild" +# note that drawing a fractal using lots of "Line" instances is not the most optimal way to draw! + +include System +include System::Windows +include System::Windows::Media +include System::Windows::Media::Imaging +include System::Windows::Controls +include System::Windows::Shapes +include Microsoft::Phone::Controls::Maps + +def init + $FRACTAL_WIDTH = 480 + $FRACTAL_HEIGHT = 480 + $LENGTH_SCALE = 0.75; + $DELTA_THETA = Math::PI / 5; + $HOP_COUNTER = 0 + $colorNow = Colors.Magenta + $colors = [ + Colors.Magenta, + Colors.Cyan, + Colors.Yellow, + Colors.Red, + Color.from_argb(255,0,255,0), + Colors.Orange] + # build our canvas + $canvas = Canvas.new + $canvas.width = $FRACTAL_WIDTH + $canvas.height = $FRACTAL_HEIGHT + + $text_block = TextBlock.new + $text_block.font_size = 30 + + Host.content_holder.horizontal_alignment = HorizontalAlignment.center + Host.content_holder.children.add($canvas) + Host.content_holder.children.add($text_block) + Host.start_timer("timer1", System::TimeSpan.FromSeconds(1), "timer_listener") + + next_hop +end + +def add_line(x1, y1, x2, y2) + line = Line.new + line.stroke = SolidColorBrush.new($colorNow) + line.stroke_thickness = 3 + line.x1 = x1 + line.y1 = y1 + line.x2 = x2 + line.y2 = y2 + $canvas.children.add(line) +end + +def drawBinaryTree(depth, x, y, length, theta) + x2 = x + length * Math.cos(theta) + y2 = y + length * Math.sin(theta) + + if depth == 1 + add_line(x,y,x2,y2) + end + + if depth > 1 + drawBinaryTree(depth - 1, x2, y2, length * $LENGTH_SCALE, theta + $DELTA_THETA) + drawBinaryTree(depth - 1, x2, y2, length * $LENGTH_SCALE, theta - $DELTA_THETA) + end +end + +def next_hop + $HOP_COUNTER = $HOP_COUNTER + 1 + if $HOP_COUNTER < 11 + $colorNow = $colors[$HOP_COUNTER%$colors.length] + x = $canvas.width / 2 + y = 0.83 * $canvas.height + drawBinaryTree($HOP_COUNTER, x, y, 0.2 * $canvas.width, - Math::PI / 2) + $text_block.text = "tree depth is " + $HOP_COUNTER.to_s + end + + if $HOP_COUNTER > 20 + $canvas.children.clear + $HOP_COUNTER = 0 + end +end + +init if $canvas.nil? + +def timer_listener + next_hop +end \ No newline at end of file diff --git a/Hosts/Silverlight/Iron7/Resources/GraphicsExample.txt b/Hosts/Silverlight/Iron7/Resources/GraphicsExample.txt new file mode 100644 index 0000000000..6c34a4ea3e --- /dev/null +++ b/Hosts/Silverlight/Iron7/Resources/GraphicsExample.txt @@ -0,0 +1,153 @@ +# clock based on http://www.infoq.com/articles/ironruby-wpf +# author Edd Morgan +# used with permission - thanks Edd! + +include System +include System::Windows +include System::Windows::Media +include System::Windows::Media::Imaging +include System::Windows::Controls +include System::Windows::Shapes +include Microsoft::Phone::Controls::Maps + +def create_hand(color, thickness) + hand = Line.new + hand.stroke = SolidColorBrush.new(color) + hand.stroke_thickness = thickness + hand.stroke_end_line_cap = PenLineCap.round; + hand.x1 = $CLOCK_WIDTH / 2 + hand.y1 = $CLOCK_HEIGHT / 2 + $canvas.children.add(hand) + return hand +end + +def init + $CLOCK_WIDTH = 300 + $CLOCK_HEIGHT = 300 + $LABEL_HEIGHT = $CLOCK_HEIGHT / 7 + $LABEL_WIDTH = $CLOCK_WIDTH / 7 + $HOUR_RADIUS = $CLOCK_WIDTH / 3 + $RADIUS = $CLOCK_WIDTH / 2 + $RADS = Math::PI / 180 + $MIN_LOCATIONS = {} + $HOUR_LOCATIONS = {} + $HOURTEXT_LOCATIONS = {} + + calc_plot_locations + + $canvas = Canvas.new + $canvas.width = $CLOCK_WIDTH + $canvas.height = $CLOCK_HEIGHT + + Host.content_holder.children.add($canvas) + + $hour_hand = create_hand(Colors.black, 10) + $minute_hand = create_hand(Colors.black, 5) + $second_hand = create_hand(Colors.red, 3) + + plot_face + plot_labels + plot_hands + plot_hub + + Host.start_timer("clocktimer", System::TimeSpan.FromSeconds(0.2), "timer_listener") +end + +def calc_plot_locations + for i in (0..60) # 60 minutes + a = i * 6 + x = ($RADIUS * Math.sin(a * $RADS)).to_i + ($CLOCK_WIDTH / 2) + y = ($CLOCK_HEIGHT / 2) - ($RADIUS * Math.cos(a * $RADS)).to_i + coords = [x, y] + $MIN_LOCATIONS[i] = coords + end + + for i in (0..12) # 12 hours - for the hour hand + a = i * 30 + x = ($HOUR_RADIUS * Math.sin(a * $RADS)).to_i + ($CLOCK_WIDTH / 2) + y = ($CLOCK_HEIGHT / 2) - ($HOUR_RADIUS * Math.cos(a * $RADS)).to_i + coords = [x, y] + $HOUR_LOCATIONS[i] = coords + end + + for i in (0..12) # 12 hours - for the hour text + a = i * 30 + x = ($RADIUS * Math.sin(a * $RADS)).to_i + ($CLOCK_WIDTH / 2) + y = ($CLOCK_HEIGHT / 2) - ($RADIUS * Math.cos(a * $RADS)).to_i + coords = [x, y] + $HOURTEXT_LOCATIONS[i] = coords + end +end + +def plot_face + extra_x = ($CLOCK_WIDTH * 0.15) # pad our circle a little + extra_y = ($CLOCK_HEIGHT * 0.15) + + face = Ellipse.new + face.fill = SolidColorBrush.new(Colors.white) + face.width = $CLOCK_WIDTH + extra_x + face.height = $CLOCK_HEIGHT + extra_y + face.margin = Thickness.new(0 - (extra_x/2), 0 - (extra_y/2), 0, 0) + face.stroke = SolidColorBrush.new(Colors.dark_gray) + face.stroke_thickness = 5 + Canvas.set_z_index(face, -1) # send our face to the back + + $canvas.children.add(face) +end + +def plot_hub + hub_radius = ($RADIUS * 0.15) + + hub = Ellipse.new + hub.fill = SolidColorBrush.new(Colors.black) + hub.width = hub_radius + hub.height = hub_radius + hub.margin = Thickness.new(($CLOCK_WIDTH - hub_radius)/2, ($CLOCK_HEIGHT-hub_radius)/2, 0, 0) + + $canvas.children.add(hub) +end + +def plot_labels + brush = SolidColorBrush.new(Colors.dark_gray) + $HOURTEXT_LOCATIONS.each_pair do |p, coords| + unless p == 0 + lbl = TextBlock.new + lbl.horizontal_alignment = HorizontalAlignment.center + lbl.width = $LABEL_WIDTH + lbl.height = $LABEL_HEIGHT + lbl.font_size = 36 + lbl.text = ' ' + p.to_s + lbl.margin = Thickness.new(coords[0] - ($LABEL_WIDTH / 2), coords[1] - ($LABEL_HEIGHT / 2), 0, 0) + lbl.padding = Thickness.new(0, 0, 0, 0) + lbl.foreground = brush + $canvas.children.add(lbl) + end + end +end + +def plot_hands + time = Time.now + hours = time.hour + minutes = time.min + seconds = time.sec + + if !$minutes || minutes != $minutes + $hours = hours >= 12 ? hours - 12 : hours + $minutes = minutes == 0 ? 60 : minutes + $minute_hand.x2 = $MIN_LOCATIONS[$minutes][0] + $minute_hand.y2 = $MIN_LOCATIONS[$minutes][1] + $hour_hand.x2 = $HOUR_LOCATIONS[$hours][0] + $hour_hand.y2 = $HOUR_LOCATIONS[$hours][1] + end + if !$seconds || seconds != $seconds + $seconds = seconds == 0 ? 60 : seconds + $second_hand.x2 = $MIN_LOCATIONS[$seconds][0] + $second_hand.y2 = $MIN_LOCATIONS[$seconds][1] + end +end + +init if $CLOCK_WIDTH.nil? + +def timer_listener + plot_hands +end diff --git a/Hosts/Silverlight/Iron7/Resources/LogicExample.txt b/Hosts/Silverlight/Iron7/Resources/LogicExample.txt new file mode 100644 index 0000000000..3ae4dad224 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Resources/LogicExample.txt @@ -0,0 +1,49 @@ +include System +include System::Windows +include System::Windows::Media +include System::Windows::Media::Imaging +include System::Windows::Controls +include System::Windows::Shapes +include Microsoft::Phone::Controls::Maps + +def init + stack_panel_main = StackPanel.new + stack_panel_main.horizontal_alignment = HorizontalAlignment.center + + $textbox1 = TextBox.new + $textbox1.text = "Hello" + stack_panel_main.children.add($textbox1) + + textblock_plus = TextBlock.new + textblock_plus.text = "+" + stack_panel_main.children.add(textblock_plus) + + $textbox2 = TextBox.new + $textbox2.text = "World" + stack_panel_main.children.add($textbox2) + + textblock_eq = TextBlock.new + textblock_eq.text = "=" + stack_panel_main.children.add(textblock_eq) + + $textbox3 = TextBox.new + $textbox3.is_read_only = true + $textbox3.text = $textbox1.text + $textbox2.text + stack_panel_main.children.add($textbox3) + + Host.monitor_control("textbox1", $textbox1, "textbox_listener") + Host.monitor_control("textbox2", $textbox2, "textbox_listener") + Host.content_holder.children.add(stack_panel_main) + + update_display +end + +def textbox_listener + update_display +end + +def update_display + $textbox3.text = $textbox1.text + $textbox2.text +end + +init if $textbox1.nil? \ No newline at end of file diff --git a/Hosts/Silverlight/Iron7/Resources/MandelBrotDrawing.txt b/Hosts/Silverlight/Iron7/Resources/MandelBrotDrawing.txt new file mode 100644 index 0000000000..05767e0aaf --- /dev/null +++ b/Hosts/Silverlight/Iron7/Resources/MandelBrotDrawing.txt @@ -0,0 +1,268 @@ +# mandelbrot algortithm adapted from http://www.codeproject.com/KB/graphics/mandelbrot.aspx +# note that this is not a very optimal drawing mechanism! +# there are lots of interesting (beautiful) mandelbrot sites online +# note that different algorithm draw the axes at different angles - can get very confusing! + +include System +include System::Windows +include System::Windows::Media +include System::Windows::Media::Imaging +include System::Windows::Controls +include System::Windows::Shapes +include Microsoft::Phone::Controls::Maps + +# playing with this color array can produce some lovely effects +def color_array_one + $colors = [] + for i in 0...128 + $colors.push(Color.from_argb(255,255-(i*2),255-(i*2),0)) + end + for i in 0...128 + $colors.push(Color.from_argb(255,i*2,i*2,0)) + end +end + +def color_array_two + $colors = [] + red = 0 + green = 0 + blue = 0 + + # red to yellow + red = 255 + for i in 0...64 + $colors.push(Color.from_argb(255,red,green,blue)) + green = green + 4 + end + # yellow to green + green = 255 + for i in 0...64 + $colors.push(Color.from_argb(255,red,green,blue)) + red = red - 4 + end + red=0 + # green to cyan + green = 255 + for i in 0...64 + $colors.push(Color.from_argb(255,red,green,blue)) + blue = blue + 4 + end + # green to cyan + blue = 255 + for i in 0...64 + $colors.push(Color.from_argb(255,red,green,blue)) + green = green - 4 + end +end + +def color_array_three + $colors = [] + red = 0 + green = 0 + blue = 0 + + # black to red + red = 0 + for i in 0...64 + $colors.push(Color.from_argb(255,red,green,blue)) + red = red + 4 + end + + # red to magenta + red = 255 + for i in 0...64 + $colors.push(Color.from_argb(255,red,green,blue)) + blue = blue + 4 + end + red = 255 + blue = 255 + + # magenta to yellow + for i in 0...64 + $colors.push(Color.from_argb(255,red,green,blue)) + blue = blue - 4 + green = green + 4 + end + green = 255 + blue = 0 + + # yellow to white + for i in 0...64 + $colors.push(Color.from_argb(255,red,green,blue)) + blue = blue + 4 + end + + $colors = $colors.reverse +end + +def create_button(name) + button = Button.new + button.content = "preset " + name + Host.monitor_control(name, button, "button_listener") + $stack.children.add(button) +end + +def init + #MessageBox.show("keep watching... I'm doing a lot of mathematics") + + $BASE_WIDTH = 320 + $BASE_HEIGHT = 240 + + # Creates the Canvas we draw to + $canvas = Canvas.new + $canvas.width = 320 + $canvas.height = 240 + + Host.fix_orientation_portrait + Host.content_holder.background = SolidColorBrush.new(Colors.black) + + $text_settings = TextBlock.new + $text_settings.foreground = SolidColorBrush.new(Colors.white) + $text_progress = TextBlock.new + $text_progress.foreground = SolidColorBrush.new(Colors.white) + + $stack = StackPanel.new + $stack.horizontal_alignment = HorizontalAlignment.center + create_button("1") + create_button("2") + create_button("3") + $stack.children.add($canvas) + $stack.children.add($text_settings) + $stack.children.add($text_progress) + + Host.content_holder.children.add($stack) + + init_settings_one + + $init = 1 + Host.start_timer("myTimer", TimeSpan.from_seconds(0.1), "timer_listener") +end + +def init_settings_one + color_array_one + + $xmin = -1.8 # Start x value, normally -2.1 + $xmax = 0.7 # Finish x value, normally 1 + $ymin = -1.0 # Start y value, normally -1.3 + $ymax = 1.0 # Finish y value, normally 1.3 + + init_settings_common +end + +def init_settings_two + color_array_two + + $xmin = -1.30989899 + $xmax = -1.19878788 + $ymin = -0.40390572 + $ymax = -0.26922559 + + init_settings_common +end + +def init_settings_three + color_array_three + + $xmin = 0.28689 + $xmax = 0.28694 + $ymin = 0.0142 + $ymax = 0.0143 + + init_settings_common +end + +def init_settings_common + # to help get through the maths we only process every $SCALE_DOWN point... + # then draw each calculation represent $SCALE_UP pixels square + $SCALE_DOWN = 256 + $SCALE_UP = 256 + $FINISH_SCALE = 2 + + $text_settings.text = "x in %.4f,%.4f; y in %.4f,%.4f" % [$xmin,$xmax,$ymin,$ymax] + + $canvas.children.clear + + next_level +end + +def next_level + if $SCALE_UP < $FINISH_SCALE + Host.stop_timer("myTimer") + return + end + + $SCALE_DOWN = $SCALE_DOWN/2 + $SCALE_UP = $SCALE_UP/2 + + $WIDTH = $BASE_WIDTH/$SCALE_DOWN + $HEIGHT = $BASE_HEIGHT/$SCALE_DOWN + + $xIncrement = ($xmax - $xmin) / $WIDTH + $yIncrement = ($ymax - $ymin) / $HEIGHT + + $x = $xmin + $s = 0 + +end + +init if $init.nil? + +def set_pixel(x,y,color) + # very inefficient... add a small rectangle! + rec = Rectangle.new + rec.margin = Thickness.new(x *$SCALE_UP,y*$SCALE_UP,0,0) + rec.width = $SCALE_UP + rec.height = $SCALE_UP + rec.fill = SolidColorBrush.new(color) + $canvas.children.add(rec) +end + +def next_line + if $s >= $WIDTH + next_level + return + end + + y = $ymin + + for z in 0...$HEIGHT + x1 = 0 + y1 = 0 + looper = 0.0 + while looper < 100 && x1 *x1 + y1* y1 < 4 do + looper = looper +1.0 + xx = (x1 * x1) - (y1 * y1) + $x + y1 = 2 * x1 * y1 + y + x1 = xx + end + + # Get the percent of where the looper stopped + perc = looper / (100.0) + + # Get that part of a 255 scale + val = (perc * 255).floor + + # Use that number to set the color + set_pixel($s,z,$colors[val]) + y = $yIncrement + y + end + $x = $x + $xIncrement + $s = $s + 1 +end + +def button_listener + case Calling_hint + when "1" + init_settings_one + when "2" + init_settings_two + when "3" + init_settings_three + end +end + +def timer_listener + $text_progress.text = "Detail level " << $SCALE_UP.to_s << ", " << $s.to_s << " lines out of " << $WIDTH.to_s + next_line +end + diff --git a/Hosts/Silverlight/Iron7/Resources/Mandelbrot2.txt b/Hosts/Silverlight/Iron7/Resources/Mandelbrot2.txt new file mode 100644 index 0000000000..5b8623c5ae --- /dev/null +++ b/Hosts/Silverlight/Iron7/Resources/Mandelbrot2.txt @@ -0,0 +1,165 @@ +# not used - it's the same alg... + +# mandelbrot algortithm adapted from http://knol.google.com/k/mandelbrot-set-c# +# note that this is not a very optimal drawing mechanism! + +include System +include System::Windows +include System::Windows::Media +include System::Windows::Media::Imaging +include System::Windows::Controls +include System::Windows::Shapes +include Microsoft::Phone::Controls::Maps + +def complex_new(i, j) + return [i,j] +end + +def complex_add(c1,c2) + return [ + c1[0] + c2[0], + c1[1] + c2[1]] +end +def complex_subtract(c1,c2) + return [ + c1[0] - c2[0], + c1[1] - c2[1]] +end +def complex_multiply(c1,c2) + return [ + c1[0]*c2[0]-c1[1]*c2[1], + c1[1]*c2[0]+c1[0]*c2[1]] +end + +def complex_module_squared(c) + return c[0]*c[0] + c[1]*c1[1] +end + +def complex_diverg(c_old) + i=0 + c_new = complex_new(c_old[0], c_old[1] + + while i= $WIDTH + next_level + return + end + + y = $ymin + + for z in 0...$HEIGHT + x1 = 0 + y1 = 0 + looper = 0.0 + while looper < 100 && x1 *x1 + y1* y1 < 4 do + looper = looper +1.0 + xx = (x1 * x1) - (y1 * y1) + $x + y1 = 2 * x1 * y1 + y + x1 = xx + end + + # Get the percent of where the looper stopped + perc = looper / (100.0) + + # Get that part of a 255 scale + val = System::Math.floor(perc * 255) + + # Use that number to set the color + set_pixel($s,z,$colors[val]) + y = $yIncrement + y + end + $x = $x + $xIncrement + $s = $s + 1 +end + +$text_progress.text = "Detail level " << $SCALE_UP.to_s << ", " << $s.to_s << " lines out of " << $WIDTH.to_s +next_line \ No newline at end of file diff --git a/Hosts/Silverlight/Iron7/Resources/MapWithLocation.txt b/Hosts/Silverlight/Iron7/Resources/MapWithLocation.txt new file mode 100644 index 0000000000..b2c6b2a4a6 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Resources/MapWithLocation.txt @@ -0,0 +1,93 @@ +# for full bing maps sample, see +# http://msdn.microsoft.com/en-us/wp7trainingcourse_usingbingmapslab_topic2 + +include System +include System::Windows +include System::Windows::Media +include System::Windows::Media::Imaging +include System::Windows::Controls +include System::Windows::Shapes +include Microsoft::Phone::Controls::Maps + +def create_text_block(color, size, text) + textBlock = TextBlock.new + textBlock.text = text + textBlock.foreground = SolidColorBrush.new(color) unless color.nil? + textBlock.font_size = size + textBlock.text_wrapping = TextWrapping.Wrap + return textBlock +end + +if $initialized.nil? + stack_panel = StackPanel.new + stack_panel.horizontal_alignment = HorizontalAlignment.center + + $map = Map.new + $map.height = 400 + $map.width = 400 + # this key for Iron7 use only + # to create your own apps, register for a key at www.bingmapsportal.com + $map.credentials_provider = ApplicationIdCredentialsProvider.new("YOUR_KEY") + rect = LocationRect.new(90,180,-90,-180) + $map.set_view(rect) + stack_panel.children.add($map) + + $text_block_state = create_text_block(Colors.blue, 40, "?") + stack_panel.children.add($text_block_state) + + $text_block_longitude = create_text_block(nil, 20, "") + $text_block_latitude = create_text_block(nil, 20, "") + $text_block_horizontal_accuracy = create_text_block(nil, 20, "") + $text_block_speed = create_text_block(nil, 20, "") + $text_block_altitude = create_text_block(nil, 20, "") + $text_block_vertical_accuracy = create_text_block(nil, 20, "") + + stack_panel.children.add($text_block_longitude) + stack_panel.children.add($text_block_latitude) + stack_panel.children.add($text_block_horizontal_accuracy) + stack_panel.children.add($text_block_speed) + stack_panel.children.add($text_block_altitude) + stack_panel.children.add($text_block_vertical_accuracy) + + Host.content_holder.children.add(stack_panel) + Host.start_geo_coordinate_watcher("high", "handle_status_changed", "handle_position_changed") + Host.fix_orientation_portrait + + $initialized=1 +end + +def handle_position_changed + return if GeoPositionChange.nil? + return if GeoPositionChange.position.nil? + return if GeoPositionChange.position.location.nil? + + loc = GeoPositionChange.position.location + + $text_block_longitude.text = "lng: " + loc.longitude.to_s + $text_block_latitude.text = "lat: " + loc.latitude.to_s + $text_block_altitude.text = "alt: " + loc.altitude.to_s + $text_block_speed.text = "speed: " + loc.speed.to_s + $text_block_horizontal_accuracy.text = "h-acc: " + loc.horizontal_accuracy.to_s + $text_block_vertical_accuracy.text = "v-acc: " + loc.vertical_accuracy.to_s + + if $pushpin.nil? + $pushpin = Pushpin.new + $pushpin.content = "*" + # technically it might be better to add a pushpin layer here + $map.children.add($pushpin) + end + + $pushpin.location = loc + $map.set_view(loc, 12) + +end + +def handle_status_changed + if GeoStatusChange == 1 # magic numbers here - sorry! + $text_block_state.text = "GPS lock" + $text_block_state.foreground = SolidColorBrush.new(Colors.green) + else + $text_block_state.text = "waiting" + $text_block_state.foreground = SolidColorBrush.new(Colors.red) + end +end diff --git a/Hosts/Silverlight/Iron7/Resources/MathsExample.txt b/Hosts/Silverlight/Iron7/Resources/MathsExample.txt new file mode 100644 index 0000000000..9efaafdc1b --- /dev/null +++ b/Hosts/Silverlight/Iron7/Resources/MathsExample.txt @@ -0,0 +1,62 @@ +include System +include System::Windows +include System::Windows::Input +include System::Windows::Media +include System::Windows::Media::Imaging +include System::Windows::Controls +include System::Windows::Shapes +include Microsoft::Phone::Controls::Maps + +def numeric_input_scope(textbox) + numeric_input = InputScope.new + textbox.input_scope = numeric_input + numeric_input_name = InputScopeName.new + numeric_input_name.name_value = InputScopeNameValue.Number + numeric_input.names.add(numeric_input_name) +end + +def init + stack_panel_main = StackPanel.new + stack_panel_main.horizontal_alignment = HorizontalAlignment.center + + $textbox1 = TextBox.new + $textbox1.text = "42" + numeric_input_scope($textbox1) + stack_panel_main.children.add($textbox1) + + textblock_plus = TextBlock.new + textblock_plus.text = "+" + stack_panel_main.children.add(textblock_plus) + + $textbox2 = TextBox.new + $textbox2.text = "99" + numeric_input_scope($textbox2) + stack_panel_main.children.add($textbox2) + + textblock_eq = TextBlock.new + textblock_eq.text = "=" + stack_panel_main.children.add(textblock_eq) + + $textbox3 = TextBox.new + $textbox3.is_read_only = true + stack_panel_main.children.add($textbox3) + + Host.monitor_control("textbox1", $textbox1, "textbox_listener") + Host.monitor_control("textbox2", $textbox2, "textbox_listener") + Host.content_holder.children.add(stack_panel_main) + + update_result +end + +def textbox_listener + update_result +end + +def update_result + t1 = ($textbox1.text).to_f + t2 = ($textbox2.text).to_f + total = t1+t2 + $textbox3.text = total.to_s +end + +init if $textbox1.nil? \ No newline at end of file diff --git a/Hosts/Silverlight/Iron7/Resources/NewAccelerometerAppTemplate.txt b/Hosts/Silverlight/Iron7/Resources/NewAccelerometerAppTemplate.txt new file mode 100644 index 0000000000..56a8c0500d --- /dev/null +++ b/Hosts/Silverlight/Iron7/Resources/NewAccelerometerAppTemplate.txt @@ -0,0 +1,36 @@ +include System +include System::Windows +include System::Windows::Media +include System::Windows::Media::Imaging +include System::Windows::Controls +include System::Windows::Shapes +include Microsoft::Phone::Controls::Maps + +def init + + $text_block_x = TextBlock.new + $text_block_y = TextBlock.new + $text_block_z = TextBlock.new + + text_stack_panel = StackPanel.new + text_stack_panel.children.add($text_block_x) + text_stack_panel.children.add($text_block_y) + text_stack_panel.children.add($text_block_z) + + Host.content_holder.children.add(text_stack_panel); + + Host.fix_orientation_portrait + Host.start_accelerometer(0.1, "accelerometer_listener") # accel rate limited to one every 0.1 seconds + + $initialized = 1 +end + +def accelerometer_listener + r = AccelerometerReading + + $text_block_x.text = "x is " + r.x.to_s + $text_block_y.text = "y is " + r.y.to_s + $text_block_z.text = "z is " + r.z.to_s +end + +init if $initialized.nil? diff --git a/Hosts/Silverlight/Iron7/Resources/NewAppTemplate.txt b/Hosts/Silverlight/Iron7/Resources/NewAppTemplate.txt new file mode 100644 index 0000000000..6706ce2b69 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Resources/NewAppTemplate.txt @@ -0,0 +1,20 @@ +include System +include System::Windows +include System::Windows::Media +include System::Windows::Media::Imaging +include System::Windows::Controls +include System::Windows::Shapes +include Microsoft::Phone::Controls +include Microsoft::Phone::Controls::Maps + +def init + text_block = TextBlock.new + text_block.text = "HELLO WORLD" + text_block.font_size = 64 + + Host.content_holder.children.add(text_block) + + $initialized = 1 +end + +init if $initialized.nil? diff --git a/Hosts/Silverlight/Iron7/Resources/NewButtonAndTimerAppTemplate.txt b/Hosts/Silverlight/Iron7/Resources/NewButtonAndTimerAppTemplate.txt new file mode 100644 index 0000000000..892cbac969 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Resources/NewButtonAndTimerAppTemplate.txt @@ -0,0 +1,45 @@ +include System +include System::Windows +include System::Windows::Media +include System::Windows::Media::Imaging +include System::Windows::Controls +include System::Windows::Shapes +include Microsoft::Phone::Controls::Maps + +def init + $initialized=1 + $timer_count=0 + + stack_panel = StackPanel.new + + $text_block = TextBlock.new + $text_block.font_size = 40 + stack_panel.children.add($text_block) + + button = Button.new + button.content = "reset" + stack_panel.children.add(button) + + Host.content_holder.horizontal_alignment = HorizontalAlignment.center + Host.content_holder.children.add(stack_panel) + Host.monitor_control("reset button", button, "button_listener") + Host.start_timer("counting timer", System::TimeSpan.from_seconds(1), "timer_listener") + + update_text +end + +def button_listener + $timer_count = 0 + update_text +end + +def timer_listener + $timer_count = $timer_count + 1 + update_text +end + +def update_text + $text_block.text = $timer_count.to_s +end + +init if $initialized.nil? \ No newline at end of file diff --git a/Hosts/Silverlight/Iron7/Resources/NewCanvasTemplate.txt b/Hosts/Silverlight/Iron7/Resources/NewCanvasTemplate.txt new file mode 100644 index 0000000000..c4bb4cdf92 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Resources/NewCanvasTemplate.txt @@ -0,0 +1,92 @@ +include System +include System::Windows +include System::Windows::Media +include System::Windows::Media::Imaging +include System::Windows::Controls +include System::Windows::Shapes +include Microsoft::Phone::Controls::Maps + +def init + $WIDTH = 800 + $HEIGHT = 800 + + $canvas = Canvas.new + $canvas.width = $WIDTH + $canvas.height = $HEIGHT + + Host.content_holder.children.add($canvas) + + ellipse_params = { + "margin_left"=>10, + "margin_top"=>10, + "width"=>100, + "height"=>200, + "fill_color"=>Colors.red, + "stroke_color"=>Color.from_argb(128,128,0,0), + "stroke_thickness"=>5 } + plot_ellipse(ellipse_params) + + line_params = { + "x1"=>210, + "y1"=>210, + "x2"=>310, + "y2"=>110, + "stroke_color"=>Colors.blue, + "stroke_thickness"=>20 } + plot_line(line_params) + + rect_params = { + "margin_left"=>10, + "margin_top"=>210, + "width"=>100, + "height"=>200, + "corner_radius_x"=>10, + "corner_radius_y"=>20, + "fill_color"=>Color.from_argb(128,0,128,0), + "stroke_color"=>Color.from_argb(255,0,255,0), + "stroke_thickness"=>1 } + plot_rectangle(rect_params) + + $initialized = 1 +end + +def plot_line(line_params) + line = Line.new + line.x1 = line_params["x1"] + line.x2 = line_params["x2"] + line.y1 = line_params["y1"] + line.y2 = line_params["y2"] + line.stroke = SolidColorBrush.new(line_params["stroke_color"]) + line.stroke_thickness = line_params["stroke_thickness"] + + $canvas.children.add(line) +end + +def plot_ellipse(ellipse_params) + + ellipse = Ellipse.new + ellipse.fill = SolidColorBrush.new(ellipse_params["fill_color"]) + ellipse.width = ellipse_params["width"] + ellipse.height = ellipse_params["height"] + ellipse.margin = Thickness.new(ellipse_params["margin_left"], ellipse_params["margin_top"], 0, 0) + ellipse.stroke = SolidColorBrush.new(ellipse_params["stroke_color"]) + ellipse.stroke_thickness = ellipse_params["stroke_thickness"] + + $canvas.children.add(ellipse) +end + +def plot_rectangle(rect_params) + rect = Rectangle.new + rect.fill = SolidColorBrush.new(rect_params["fill_color"]) + rect.width = rect_params["width"] + rect.height = rect_params["height"] + rect.radius_x = rect_params["corner_radius_x"] + rect.radius_y = rect_params["corner_radius_y"] + rect.margin = Thickness.new(rect_params["margin_left"], rect_params["margin_top"], 0, 0) + rect.stroke = SolidColorBrush.new(rect_params["stroke_color"]) + rect.stroke_thickness = rect_params["stroke_thickness"] + + $canvas.children.add(rect) +end + +init if $initialized .nil? diff --git a/Hosts/Silverlight/Iron7/Resources/NewLocationAppTemplate.txt b/Hosts/Silverlight/Iron7/Resources/NewLocationAppTemplate.txt new file mode 100644 index 0000000000..0853ebc046 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Resources/NewLocationAppTemplate.txt @@ -0,0 +1,60 @@ +include System +include System::Windows +include System::Windows::Media +include System::Windows::Media::Imaging +include System::Windows::Controls +include System::Windows::Shapes +include Microsoft::Phone::Controls::Maps + +def create_text_block(color, size, text) + textBlock = TextBlock.new + textBlock.text = text + textBlock.foreground = SolidColorBrush.new(color) unless color.nil? + textBlock.font_size = size + textBlock.text_wrapping = TextWrapping.Wrap + return textBlock +end + +def init + stack_panel = StackPanel.new + stack_panel.horizontal_alignment = HorizontalAlignment.center + + $text_block_state = create_text_block(Colors.blue, 40, "?") + stack_panel.children.add($text_block_state) + + $text_block_longitude = create_text_block(nil, 20, "") + stack_panel.children.add($text_block_longitude) + + $text_block_latitude = create_text_block(nil, 20, "") + stack_panel.children.add($text_block_latitude) + + Host.content_holder.children.add(stack_panel) + Host.start_geo_coordinate_watcher("high", "handle_status_changed", "handle_position_changed") + Host.fix_orientation_portrait + + $initialized=1 +end + +def handle_position_changed + return if GeoPositionChange.nil? + return if GeoPositionChange.position.nil? + return if GeoPositionChange.position.location.nil? + + loc = GeoPositionChange.position.location + + $text_block_longitude.text = "lng: " + loc.longitude.to_s + $text_block_latitude.text = "lat: " + loc.latitude.to_s + +end + +def handle_status_changed + if GeoStatusChange == 1 # magic numbers here - sorry! + $text_block_state.text = "GPS lock" + $text_block_state.foreground = SolidColorBrush.new(Colors.green) + else + $text_block_state.text = "waiting" + $text_block_state.foreground = SolidColorBrush.new(Colors.red) + end +end + +init if $initialized.nil? diff --git a/Hosts/Silverlight/Iron7/Resources/RubyIntro.txt b/Hosts/Silverlight/Iron7/Resources/RubyIntro.txt new file mode 100644 index 0000000000..3d50561029 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Resources/RubyIntro.txt @@ -0,0 +1,252 @@ +# for a longer but similar tutorial +# try http://www.troubleshooters.com/codecorn/ruby/basictutorial.htm +# an excellent overview by Steve Litt + +# first let's set up a console +include System::Windows +include System::Windows::Controls +include System::Windows::Media + +# note that $ before a variable name simply +# makes that variable global +$stack = StackPanel.new +scroll = ScrollViewer.new +scroll.content = $stack +Host.content_holder.children.add(scroll) + +def add_text_block(t, size, color) + tb = TextBlock.new + tb.text = t.nil? ? "nil" : t.to_s + tb.font_size = size unless size.nil? + tb.foreground = SolidColorBrush.new(color) unless color.nil? + tb.text_wrapping = TextWrapping.wrap + $stack.children.add(tb) +end + +def add_text(t) + add_text_block(t, nil, nil) +end + +def add_title(t) + add_text(" ") + add_text_block(t, 24, Colors.magenta) +end + +# now let's go through some language constructs +add_text_block("Ruby 101", 40, Colors.red) + +# a for loop +add_title("for loops") + +# for loop +for i in 1...3 + add_text(i.to_s) +end + +add_title("while statement") + +i = 0 +while i<10 do + add_text("while " + i.to_s) + i = i + 5 +end + +add_title("if statements") + +# if +if $stack.children.count>2 + add_text("if I") +end + +# if part 2 +add_text("if II") if $stack.children.count > 3 + +# unless - not if +add_text("unless") unless $stack.children.count > 3 + +# if else +if $stack.children.count > 3 + add_text("elsif not else") +else + add_text("elsif else") +end + +# if elsif too - less syntax the else if +if $stack.children.count == 2 + add_text("elsif not else") +elsif $stack.children.count < 20 + add_text("elsif elsif") +else + add_text("elsif else") +end + +# case +add_title("case statement") + +case $stack.children.count + when 0 + add_text(0) + when 3 + add_text(3) + else + add_text("case else") +end + +add_title("arrays") + +# arrays +arr = ["my","good","friend"] + +# array 1 +add_text("for loop iterated:") +for i in 0...arr.length + add_text(arr[i]) +end + +# array 2 +add_text("each iterated:") +arr.each{ |i| add_text(i) } + +# array 3 +add_text("popped:") +arr.each do |i| + add_text(i) +end + +# arrays also have useful methods like push, pop, shift, unshift +arr2 = [] +arr2.push(1) +arr2.push(2) +arr2.push(3) +add_text("popped:") +add_text(arr2.pop) + +# arrays can be sparse +# but this doesn't seem to work that well in ironruby? +arr2 = Array.new +arr2[0] = "0" +arr2[2] = "2" +arr2[4] = "4" +add_text("sparse array:") +for i in 0...arr2.length + begin + add_text("arr2[" << i << "] = '" << val << "'") + rescue + add_text("exception seen for arr2[" << i << "] :/") + end +end + +# warning - there are some reference differences between {} and do ... end + +# also for Lookup, we have Hashes - key/value pairs +add_title("hashes") + +hash = { + "one" => 1, + "two" => 2, + "three" => "bee", + "four" => 0.6 +} + +add_text("hash iteration:") +hash.each { |a,b| + add_text(a.to_s + ", " + b.to_s) +} + +add_text("hash access:") +add_text(hash["three"]); + +# Hashes can be sorted +add_title("sorting hashes") +sorted = hash.sort { |a,b| a[0] <=> b[0] } + +sorted.each { |a,b| + add_text(a.to_s + ", " + b.to_s) +} + +# some strings +add_title("strings") +s = "my best friend" + +add_text("pre insert:" + s) +s[3,4] = "last" +add_text("post insert:" + s) + +s2 = s.reverse +add_text("reversed:" + s2) + +#c style printf +s = "printf %02d, %2d, %s" % [1,2.12345,"hello"] +add_text(s) + +# some exceptions +add_title("exceptions") + +begin + broken = nil + broken.do_something +rescue + add_text("problem caught - $! is " << $!) +end + +# classes info at http://www.tutorialspoint.com/ruby/ruby_classes.htm +# some classes +add_title("classes") + +class Customer + @@no_of_customers=0 + def initialize(name, addr) + @cust_id=@@no_of_customers + @cust_name=name + @cust_addr=addr + @@no_of_customers = @@no_of_customers + 1 + end + def say_hello + "Hi I'm " << @cust_name << " from " << @cust_addr << ", my id is " << @cust_id.to_s + end +end + +# Now using above class to create objects +customers=[] +customers.push(Customer.new("Stuart", "London, UK")) +customers.push(Customer.new("Thomas", "Seattle, US")) + +add_text("there are " << Customer.class_eval("@@no_of_customers").to_s << " customers") +customers.each{ |c| + add_text(c.say_hello) +} + +# lets look at modules and mixins +add_title("modules for mixins") + +# modules info at http://www.tutorialspoint.com/ruby/ruby_modules.htm +module Week + FIRST_DAY = "Sunday" + def Week.weeks_in_month + "You have four weeks in a month" + end + def Week.weeks_in_year + "You have 52 weeks in a year" + end +end + +class Decade +include Week + no_of_yrs=10 + def no_of_months + number=10*12 + number + end +end + +d1=Decade.new +add_text(Week::FIRST_DAY) +add_text(Week.weeks_in_month) +add_text(Week.weeks_in_year) +add_text(d1.no_of_months) + + +=begin +... there's plenty more to learn and play with +For more ideas look on the web - the ruby community is great :) +=end \ No newline at end of file diff --git a/Hosts/Silverlight/Iron7/Resources/SnakeGame.txt b/Hosts/Silverlight/Iron7/Resources/SnakeGame.txt new file mode 100644 index 0000000000..6f8ecca6c5 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Resources/SnakeGame.txt @@ -0,0 +1,258 @@ +# adapted from MonoDroid sample - used under MIT license +# original https://github.com/mono/monodroid-samples +# thanks to jpobst + +include System +include System::Windows +include System::Windows::Media +include System::Windows::Media::Imaging +include System::Windows::Controls +include System::Windows::Shapes +include Microsoft::Phone::Controls::Maps + +def init + $pixels_per_square = 40 + $WIDTH = 480/$pixels_per_square + $HEIGHT = 800/$pixels_per_square + + $color_head = Color.from_argb(255,0,255,0) + $color_head_eating = Color.from_argb(255,0,255,255) + $color_body = Colors.green + $color_apple = Colors.yellow + $color_collision = Colors.red + + $vibrate_duration = TimeSpan.from_seconds(0.1) + + $text_score = TextBlock.new + $canvas = Canvas.new + $canvas.height = 800 + $canvas.width = 480 + + init_new_game + + Host.fix_orientation_portrait + Host.content_holder.children.add($text_score) + Host.content_holder.children.add($canvas) + Host.start_accelerometer(0.05, "on_tick") +end + +def setup_lookup + $lookup = [] + for x in 0...$WIDTH + $lookup[x] = [] + for y in 0...$HEIGHT + $lookup[x][y] = nil + end + end +end + +def set_lookup(x,y,content) + $lookup[x][y] = content +end + +def make_part(color, x, y, code) + set_lookup(x, y, code) + part = [x, y, create_and_add_tile(x, y)] + set_part_color(part, color) + return part +end + +def make_snake_part(color, x, y) + return make_part(color, x, y, 's') +end + +def make_apple_part(color, x, y) + return make_part(color, x, y, 'a') +end + +def set_part_color(part, color) + part[2].fill = SolidColorBrush.new(color) +end + +def remove_part(part) + $lookup[part[0]][part[1]]=nil + remove_tile(part[2]) +end + +def init_new_game + $mode = "running" + $canvas.children.clear + + $snake = [] + $apples = [] + setup_lookup + + for i in 2...8 + $snake.insert(0, make_snake_part($color_body,7,i)) + end + set_part_color($snake[0], $color_head) + + add_random_apple + add_random_apple + + $direction = "S" + $next_direction = "S" + $score = 0 + $move_delay = 600 + $last_played = Environment.tick_count + update_score_display +end + +def add_random_apple + new_x = 0 + new_y = 0 + + rng = System::Random.new + + found = false + while found == false do + # Choose a new location for our apple + new_x = 1 + rng.next($WIDTH - 2) + new_y = 1 + rng.next($HEIGHT - 2) + + # if we're here and there's no collision, then we have + # a good location for an apple. Otherwise, we'll circle back + # and try again + found = $lookup[new_x][new_y].nil? + end + + new_coord = make_apple_part($color_apple, new_x, new_y) + $apples.push(new_coord) +end + +def remove_tile(rec) + $canvas.children.remove(rec) +end + +def create_and_add_tile(x, y) + # a bit inefficient... add a small rectangle! + rec = Rectangle.new + rec.margin = Thickness.new(x *$pixels_per_square,y*$pixels_per_square,0,0) + rec.width = $pixels_per_square + rec.height = $pixels_per_square + $canvas.children.add(rec) + return rec +end + +def update + if $mode == "running" + update_snake + else + MessageBox.show("game over - you scored " << $score.to_s) + init_new_game + end +end + +def do_collision + set_part_color($snake[0], $color_collision) + $mode = "stop" +end + + +def update_snake + apple_eaten = false + + # grab the snake by the head + head = $snake[0] + new_x = head[0] + new_y = head[1] + + get_next_direction + $direction = $next_direction + + case $direction + when "E" + new_x = new_x + 1 + when "W" + new_x = new_x - 1 + when "N" + new_y = new_y - 1 + when "S" + new_y = new_y + 1 + end + + # Collision detection + # Have we gone off screen? + if (new_x < 0) || (new_y < 0) || (new_x >= $WIDTH) || (new_y >= $HEIGHT) + do_collision + return + end + + # Look for collisions with itself + current_lookup = $lookup[new_x][new_y] + if current_lookup == 's' + do_collision + return + end + + + # Look for apples + if current_lookup == 'a' + Host.vibrate($vibrate_duration) + $score = $score+1 + $move_delay = $move_delay * 0.9 + add_random_apple + + apple_eaten = true + $apples.each { |apple| + if apple[0] == new_x && apple[1] == new_y + remove_part(apple) + $apples.delete(apple) + end + } + end + + # make the old head "part of the body" + set_part_color(head, $color_body) + + # Push a new head onto the List + new_head_color = apple_eaten ? $color_head_eating : $color_head + new_head = make_snake_part(new_head_color, new_x, new_y) + $snake.insert(0, new_head) + + # Unless we want the snake to grow, remove the last tail piece + if false == apple_eaten + last = $snake.pop + remove_part(last) + end +end + +def get_next_direction + $next_direction = $direction + + begin + return if AccelerometerReading.nil? + rescue + return + end + + x = AccelerometerReading.x + y = AccelerometerReading.y + if x.abs > y.abs + if x < -0.12 + $next_direction = "W" if $direction != "E" + elsif x > 0.12 + $next_direction = "E" if $direction != "W" + end + else + if y < -0.12 + $next_direction = "S" if $direction != "N" + elsif y > 0.12 + $next_direction = "N" if $direction != "S" + end + end +end + +def on_tick + current = Environment.tick_count + if (current - $last_played) > $move_delay + update + $last_played = Environment.tick_count + update_score_display + end +end + +def update_score_display + $text_score.text = "Score: " << $score.to_s +end +init if $canvas.nil? diff --git a/Hosts/Silverlight/Iron7/Resources/StaticHelloWorld.txt b/Hosts/Silverlight/Iron7/Resources/StaticHelloWorld.txt new file mode 100644 index 0000000000..146bcb38e5 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Resources/StaticHelloWorld.txt @@ -0,0 +1,9 @@ +# everyone needs a hello world example + +include System::Windows::Controls + +text_block = TextBlock.new +text_block.text = "HELLO WORLD" +text_block.font_size = 64 + +Host.content_holder.children.add(text_block) diff --git a/Hosts/Silverlight/Iron7/Resources/Tetris.txt b/Hosts/Silverlight/Iron7/Resources/Tetris.txt new file mode 100644 index 0000000000..6afcc5f12a --- /dev/null +++ b/Hosts/Silverlight/Iron7/Resources/Tetris.txt @@ -0,0 +1,564 @@ +# Credit for code logic (license open - see original sites for more info) to: +# http://www.silverlight.net/community/samples/silverlight-samples/yygames---yytetris/ +# http://sigurdsnorteland.wordpress.com/2010/11/15/tetris7-a-wp7-game-source-code-included/ +# Credit for sound effects (creative commons 2) to: +# Grunz - http://www.freesound.org/samplesViewSingle.php?id=109663 +# Synapse - http://www.freesound.org/samplesViewSingle.php?id=2324 +# Stuckinthemud - http://www.freesound.org/samplesViewSingle.php?id=27882 + +include System +include System::Windows +include System::Windows::Shapes +include System::Windows::Media +include System::Windows::Media::Imaging +include System::Windows::Controls + +class WrappedRect + def initialize(rect) + @rect = rect + @filled = false + @color_brush = $blank_brush + end + def fill(color_brush) + @rect.fill = color_brush + @filled = true + @color_brush = color_brush + end + def is_filled? + @filled + end + def brush + @color_brush + end + def copy(rhs) + if rhs.is_filled? + fill(rhs.brush) + else + clear + end + end + def clear() + @rect.fill = $blank_brush + @filled = false + @color_brush = $blank_brush + end +end + +class ShapeTemplate + def initialize(color) + @patterns = [] + @brush = SolidColorBrush.new(color) + @current_rotation = 0 + end + def brush + @brush + end + def add_pattern(matrix) + @patterns.push(matrix) + end + def pattern_count + @patterns.length + end + def pattern(i) + @patterns[i] + end +end + +class ShapeInstance + def initialize(template) + @template = template + @current_rotation = 0 + @positionX = 3 + @positionY = 0 + end + def brush + return @template.brush + end + def get_positionX + return @positionX + end + def get_positionY + return @positionY + end + def move(x,y) + @positionX = @positionX+x + @positionY = @positionY+y + end + def is_piece_blocked + matrix = current_pattern + for i in 0...4 + for j in 0...4 + if matrix[i][j] == 1 + if j + @positionX > $columns - 1 || + i + @positionY > $rows - 1 || + j + @positionX < 0 || + $container[i + @positionY][j + @positionX].is_filled? + return true + end + end + end + end + + return false + end + + def rotate_right + @current_rotation = @current_rotation + 1 + @current_rotation = 0 if @current_rotation >= @template.pattern_count + end + def rotate_left + @current_rotation = @current_rotation - 1 + @current_rotation = @template.pattern_count - 1 if @current_rotation < 0 + end + def current_pattern + return @template.pattern(@current_rotation) + end +end + +def init_templates + + $templates = [] + + template_I = ShapeTemplate.new(Color.from_argb(255,40,40,255)) + template_I.add_pattern([[0,1,0,0], + [0,1,0,0], + [0,1,0,0], + [0,1,0,0]]) + template_I.add_pattern([[0,0,0,0], + [1,1,1,1], + [0,0,0,0], + [0,0,0,0]]) + $templates.push(template_I) + + template_L = ShapeTemplate.new(Color.from_argb(255,60,168,60)) + template_L.add_pattern([[0,1,0,0], + [0,1,0,0], + [0,1,1,0], + [0,0,0,0]]) + template_L.add_pattern([[0,0,0,0], + [1,1,1,0], + [1,0,0,0], + [0,0,0,0]]) + template_L.add_pattern([[1,1,0,0], + [0,1,0,0], + [0,1,0,0], + [0,0,0,0]]) + template_L.add_pattern([[0,0,1,0], + [1,1,1,0], + [0,0,0,0], + [0,0,0,0]]) + $templates.push(template_L) + + template_L2 = ShapeTemplate.new(Color.from_argb(255,255,192,0)) + template_L2.add_pattern([[0,0,1,0], + [0,0,1,0], + [0,1,1,0], + [0,0,0,0]]) + template_L2.add_pattern([[0,1,0,0], + [0,1,1,1], + [0,0,0,0], + [0,0,0,0]]) + template_L2.add_pattern([[0,0,1,1], + [0,0,1,0], + [0,0,1,0], + [0,0,0,0]]) + template_L2.add_pattern([[0,0,0,0], + [0,1,1,1], + [0,0,0,1], + [0,0,0,0]]) + $templates.push(template_L2) + + template_N = ShapeTemplate.new(Color.from_argb(255,255,34,34)) + template_N.add_pattern([[0,0,1,0], + [0,1,1,0], + [0,1,0,0], + [0,0,0,0]]) + template_N.add_pattern([[0,1,1,0], + [0,0,1,1], + [0,0,0,0], + [0,0,0,0]]) + $templates.push(template_N) + + template_N2 = ShapeTemplate.new(Color.from_argb(255,255,34,255)) + template_N2.add_pattern([[0,1,0,0], + [0,1,1,0], + [0,0,1,0], + [0,0,0,0]]) + template_N2.add_pattern([[0,0,1,1], + [0,1,1,0], + [0,0,0,0], + [0,0,0,0]]) + $templates.push(template_N2) + + template_O = ShapeTemplate.new(Color.from_argb(255,0,255,255)) + template_O.add_pattern([[0,1,1,0], + [0,1,1,0], + [0,0,0,0], + [0,0,0,0]]) + $templates.push(template_O) + + template_T = ShapeTemplate.new(Color.from_argb(255,192,192,192)) + template_T.add_pattern([[0,1,1,1], + [0,0,1,0], + [0,0,0,0], + [0,0,0,0]]) + template_T.add_pattern([[0,0,1,0], + [0,1,1,0], + [0,0,1,0], + [0,0,0,0]]) + template_T.add_pattern([[0,0,1,0], + [0,1,1,1], + [0,0,0,0], + [0,0,0,0]]) + template_T.add_pattern([[0,0,1,0], + [0,0,1,1], + [0,0,1,0], + [0,0,0,0]]) + $templates.push(template_T) +end + +def init + init_templates + + $blank_brush = SolidColorBrush.new(Color.from_argb(0,0,0,0)) + + $container = [] + $next_container = [] + $game_status = "play" + + $rows = 20 + $columns = 10 + + $current_piece = nil + $next_piece = nil + + $init_speed = 0.4 + $level_speed = 50 + $square_size = 30 + + $score = 0 + $level = 0 + $remove_row_count = 0 + + $canvas = Canvas.new + $canvas.height = 800 + $canvas.width = 480 + Host.content_holder.children.add($canvas) + + game_canvas = Canvas.new + game_canvas.margin = Thickness.new(20,20,0,0) + $canvas.children.add(game_canvas) + $container = create_game_grid(game_canvas, $rows, $columns) + + add_button(0,480,160,160, "\\", "rotate_left") + add_button(320,480,160,160, "/", "rotate_right") + add_button(0,640,160,160, "L", "move_to_left") + add_button(320,640,160,160, "R", "move_to_right") + add_button(160,640,160,160, "D", "move_to_down") + + rhs_stack_panel = StackPanel.new + rhs_stack_panel.margin = Thickness.new(340,30,0,0) + $canvas.children.add(rhs_stack_panel) + + add_text(rhs_stack_panel, 24, "Score:") + $text_score = add_text(rhs_stack_panel, 36, "0") + + add_text(rhs_stack_panel, 24, "Lines:") + $text_lines = add_text(rhs_stack_panel, 36, "0") + + add_text(rhs_stack_panel, 24, "Level:") + $text_level = add_text(rhs_stack_panel, 36, "1") + + add_text(rhs_stack_panel, 24, "Next:") + next_canvas = Canvas.new + $next_container = create_game_grid(next_canvas, 4, 4) + rhs_stack_panel.children.add(next_canvas) + + create_piece + add_piece + + Host.start_timer("game_timer", TimeSpan.from_seconds($init_speed), "timer_tick") + + $sound_effects = {} + load_sound_effect("level_up", "sound_effect_callback") + load_sound_effect("row_done_1", "sound_effect_callback") + load_sound_effect("row_done_2", "sound_effect_callback") + load_sound_effect("row_done_3", "sound_effect_callback") + load_sound_effect("row_done_4", "sound_effect_callback") +end + +def play_sound_effect(name) + effect = $sound_effects[name] + return if effect.nil? + effect.play +end + +def play_level_up_sound + play_sound_effect("level_up") +end + +def play_row_done_sound(num_rows) + play_sound_effect("row_done_" << num_rows.floor.to_s) +end + +def load_sound_effect(name, callback) + $sound_effects[name] = nil + Host.load_sound_effect(name, "http://iron7.com/forapp/tts/" << name << ".wav", callback) +end + +def sound_effect_callback + return if Calling_event != "sound_effect_loaded" + + # note that to_s is needed here - not 100% sure why - some issue with Ruby/.Net strings? + $sound_effects[Calling_hint.to_s] = Calling_sender +end + +def create_game_grid(panel, rows, cols) + container = [] + for i in 0...rows + current_row = [] + for j in 0...cols + rect = Rectangle.new + rect.height = $square_size + rect.width = $square_size + rect.fill = $blank_brush + rect.margin = Thickness.new(j*$square_size, i*$square_size,0,0) + rect.stroke = SolidColorBrush.new(Color.from_argb(48,255,255,255)) + panel.children.add(rect) + current_row.push(WrappedRect.new(rect)) + end + container.push(current_row) + end + return container +end + +def add_text(parent, font_size, text) + tb = TextBlock.new + tb.font_size = font_size + tb.text = text + parent.children.add(tb) + return tb +end + +def add_button(left, top, width, height, text, callback) + button = Button.new + button.content = text + button.margin = Thickness.new(left,top,0,0) + button.height = height + button.width = width + button.foreground = SolidColorBrush.new(Color.from_argb(96,128,128,128)) + button.border_brush = SolidColorBrush.new(Color.from_argb(96,128,128,128)) + button.background = SolidColorBrush.new(Color.from_argb(32,128,128,128)) + $canvas.children.add(button) + Host.monitor_control(button,button,callback) +end + +def play() + $game_status = "play" +end + +def pause() + $game_status = "pause" +end + +def create_piece + for x in 0...$columns + if $container[0][x].is_filled? + on_game_over + return + end + end + + if $next_piece.nil? + $current_piece = ShapeInstance.new($templates[rand($templates.length)]) + else + $current_piece = $next_piece + end + + $next_piece = ShapeInstance.new($templates[rand($templates.length)]) + + set_next_container_UI +end + +def timer_tick + move_to_down +end + +def move_to(x,y) + return if $game_status != "play" + + remove_piece + $current_piece.move(x,y) + if false == $current_piece.is_piece_blocked + add_piece + return true + end + $current_piece.move(-x,-y) + add_piece + return false +end + +def move_to_left + move_to(-1,0) +end + +def move_to_right() + move_to(1,0) +end + +def increment_score(increment) + $score = $score + increment + $text_score.text = $score.to_s +end + +def move_to_down() + return if $game_status != "play" + + result = move_to(0,1) + + increment_score(1) + + return if result + + remove_row + create_piece +end + +def rotate(is_left) + return if $game_status != "play" + + remove_piece + + if is_left + $current_piece.rotate_left + else + $current_piece.rotate_right + end + + blocked = $current_piece.is_piece_blocked + + if blocked + if is_left + $current_piece.rotate_right + else + $current_piece.rotate_left + end + end + + add_piece +end + +def rotate_left + rotate(true) +end + +def rotate_right + rotate(false) +end + +def clear + for row in $container + for cell in row + cell.clear + end + end +end + +def set_next_container_UI + next_pattern = $next_piece.current_pattern + for x in 0...4 + for y in 0...4 + if next_pattern[x][y] == 1 + $next_container[x][y].fill($next_piece.brush) + else + $next_container[x][y].clear + end + end + end +end + +def remove_piece + pattern = $current_piece.current_pattern + for i in 0...4 + for j in 0...4 + if pattern[i][j] == 1 + obj = $container[i + $current_piece.get_positionY][j + $current_piece.get_positionX] + obj.clear + end + end + end +end + +def add_piece + pattern = $current_piece.current_pattern + for i in 0...4 + for j in 0...4 + if pattern[i][j] == 1 + obj = $container[i + $current_piece.get_positionY][j + $current_piece.get_positionX] + obj.fill($current_piece.brush) + end + end + end +end + +def is_complete_line(y) + for x in 0...$columns + return false if not $container[y][x].is_filled? + end + return true +end + +def process_complete_line(y) + for x in 0...$columns + $container[y][x].clear + end + + y.downto(1) { |i| + for x in 0...$columns + $container[i][x].copy($container[i - 1][x]) + end + } +end + +def remove_row + remove_row_count = 0 + + for y in 0...$rows + if is_complete_line(y) + process_complete_line(y) + remove_row_count = remove_row_count + 1 + end + end + + return if remove_row_count <= 0 + + increment_score(10 ** remove_row_count) + play_row_done_sound(remove_row_count) + + $remove_row_count += remove_row_count + $text_lines.text = $remove_row_count.to_s + + test_for_new_level +end + +def test_for_new_level + new_level = Math.sqrt($remove_row_count / 5) + new_level = new_level.floor + + return if new_level <= $level + + $level = new_level + $text_level.text = ($level + 1).to_s + play_level_up_sound + + level_seconds = $init_speed - $level_speed * $level + level_seconds = $level_speed if level_seconds < $level_speed + Host.change_timer("game_timer", TimeSpan.from_seconds(level_seconds)) +end + +def on_game_over + $game_status = "game over" + MessageBox.show("game over"); +end + +init \ No newline at end of file diff --git a/Hosts/Silverlight/Iron7/Resources/TimerExample.txt b/Hosts/Silverlight/Iron7/Resources/TimerExample.txt new file mode 100644 index 0000000000..ef14425d49 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Resources/TimerExample.txt @@ -0,0 +1,37 @@ +include System +include System::Windows +include System::Windows::Media +include System::Windows::Media::Imaging +include System::Windows::Controls +include System::Windows::Shapes +include Microsoft::Phone::Controls::Maps + +def init + $initialized=1 + $counter_timer=0 + + stack_panel = StackPanel.new + + $text_block = TextBlock.new + $text_block.font_size = 40 + $text_block.text_wrapping = TextWrapping.wrap + stack_panel.children.add($text_block) + + Host.content_holder.horizontal_alignment = HorizontalAlignment.center + Host.content_holder.children.add(stack_panel) + Host.start_timer("timer1",System::TimeSpan.FromSeconds(1), "timer_listener") + + update_text +end + +def timer_listener + $counter_timer=$counter_timer+1 + update_text +end + +def update_text + text = "timer ticked " + $counter_timer.to_s + " times" + $text_block.text=text +end + +init if $initialized.nil? \ No newline at end of file diff --git a/Hosts/Silverlight/Iron7/Resources/Twitter.txt b/Hosts/Silverlight/Iron7/Resources/Twitter.txt new file mode 100644 index 0000000000..d2992245b0 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Resources/Twitter.txt @@ -0,0 +1,120 @@ +include System +include System::Windows +include System::Windows::Media +include System::Windows::Media::Imaging +include System::Windows::Controls +include System::Windows::Shapes +include Microsoft::Phone::Controls::Maps + +def random_string + items = [ + "WP7", + "WindowsPhone", + "sunset", + "beach", + "slodge", + "ruby", + "ironruby"] + + random = System::Random.new + which = random.next(items.length) + + return items[which] +end + +def init + stack_panel_main = StackPanel.new + + $textbox_1 = TextBox.new + $textbox_1.text = random_string + stack_panel_main.children.add($textbox_1) + + button_1 = Button.new + button_1.content = "Search" + + stack_panel_main.children.add(button_1) + + $stack_panel_child = StackPanel.new + stack_panel_main.children.add($stack_panel_child) + scroll_viewer = ScrollViewer.new + scroll_viewer.content = stack_panel_main + + Host.monitor_control("button_1", button_1, "button_listener") + Host.content_holder.children.add(scroll_viewer) +end + +def simple_json_splitter(response) + return response.split("\"") +end + +def find_json_property(object, name) + node = object.first + while false == node.nil? + property = node + #System::Windows::MessageBox.show(property.name + " " + property.value.value) + if property.name == name + return property.value.value + end + node = node.next + end + return "" +end + +def create_image_for(image_url) + image = Image.new + image.stretch = Stretch.fill + image.horizontal_alignment = HorizontalAlignment.left; + image.vertical_alignment = VerticalAlignment.top; + image.height = 100 + image.width = 100 + bitmap_image_source = BitmapImage.new + bitmap_image_source.uri_source = System::Uri.new(image_url) + image.source = bitmap_image_source + return image +end + +def create_textblock_for(tweet) + text_block = TextBlock.new + text_block.text = tweet + text_block.text_wrapping = TextWrapping.wrap + text_block.width = 380 + text_block.padding = Thickness.new(10,10,0,0) + return text_block +end + +def create_panel_for(tweet, image_url) + stack_panel = StackPanel.new + stack_panel.orientation = Orientation.horizontal + + image = create_image_for(image_url) + stack_panel.children.add(image) + + text_block = create_textblock_for(tweet) + stack_panel.children.add(text_block) + + return stack_panel +end + +def process_json(response) + $stack_panel_child.children.clear() + + response.each { |i| + image_url = find_json_property(i, "profile_image_url") + tweet = find_json_property(i, "text") + + stack_panel = create_panel_for(tweet, image_url) + $stack_panel_child.children.add(stack_panel) + } +end + +init if $stack_panel_child.nil? + +def json_listener + process_json(Json_response.first.first) +end + +def button_listener + if Calling_event=="button_clicked" + Host.call_json_service("twitter", "http://search.twitter.com/search.json?q=" + $textbox_1.text, "json_listener") + end +end \ No newline at end of file diff --git a/Hosts/Silverlight/Iron7/Resources/circle_drawing.txt b/Hosts/Silverlight/Iron7/Resources/circle_drawing.txt new file mode 100644 index 0000000000..e5f63abdcb --- /dev/null +++ b/Hosts/Silverlight/Iron7/Resources/circle_drawing.txt @@ -0,0 +1,94 @@ +# based loosely on https://github.com/jschementi/ironruby-rubyinside-article/blob/master/ruby-circles.html + +include System +include System::Windows +include System::Windows::Shapes +include System::Windows::Media +include System::Windows::Controls + +# called once at startup +def setup + $draw_on = false + $d = $d_start = 10 + $color_set = [[255,0, 113,118], + [255,0, 173,239], + [255,68, 199,244], + [255,157,220,249], + [255,255,235,149]] + + $drawing_canvas = Canvas.new + $drawing_canvas.width = $drawing_canvas.height= 800 + $drawing_canvas.background = SolidColorBrush.new(Colors.black) + Host.content_holder.children.add($drawing_canvas) + + $transparent_canvas = Canvas.new + $transparent_canvas.background = SolidColorBrush.new(Colors.black) + $transparent_canvas.opacity = 0.000 + Host.content_holder.children.add($transparent_canvas) + + Host.monitor_control($transparent_canvas, $transparent_canvas, "on_canvas_event") +end + +def on_canvas_event + case Calling_event + when "mouse_left_button_down" + mouse_pressed + when "mouse_left_button_up" + mouse_released + when "mouse_move" + mouse_dragged + end +end + +def mouse_pressed + $draw_on = true + add_circle +end + +def mouse_released + $draw_on = false + $d = $d_start +end + +def mouse_dragged + $d += 2 + add_circle +end + +def add_circle + return unless $draw_on + my_circle(Mouse_x, Mouse_y, $d) +end + +def random_color + $color_set[rand($color_set.size)] +end + +def random_brush + SolidColorBrush.new(Color.from_argb(*random_color)) +end + +def random_transparency + rand +end + +def keep_circle_total_low + while $drawing_canvas.children.count > 100 do + $drawing_canvas.children.remove_at(0) + end +end + +def my_circle(x,y,d) + circle = Ellipse.new + circle.height = circle.width=d + circle.fill =random_brush + circle.fill.opacity = random_transparency + circle.stroke = random_brush + circle.stroke.opacity = random_transparency + circle.stroke_thickness = 10 + circle.margin = Thickness.new(x-d/2, y-d/2, 0, 0) + $drawing_canvas.children.add(circle) + keep_circle_total_low +end + +setup \ No newline at end of file diff --git a/Hosts/Silverlight/Iron7/Resources/codemirror.js b/Hosts/Silverlight/Iron7/Resources/codemirror.js new file mode 100644 index 0000000000..87e59b5879 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Resources/codemirror.js @@ -0,0 +1,403 @@ +/* CodeMirror main module + * + * Implements the CodeMirror constructor and prototype, which take care + * of initializing the editor frame, and providing the outside interface. + */ + +// The CodeMirrorConfig object is used to specify a default +// configuration. If you specify such an object before loading this +// file, the values you put into it will override the defaults given +// below. You can also assign to it after loading. +var CodeMirrorConfig = window.CodeMirrorConfig || {}; + +var CodeMirror = (function(){ + function setDefaults(object, defaults) { + for (var option in defaults) { + if (!object.hasOwnProperty(option)) + object[option] = defaults[option]; + } + } + function forEach(array, action) { + for (var i = 0; i < array.length; i++) + action(array[i]); + } + + // These default options can be overridden by passing a set of + // options to a specific CodeMirror constructor. See manual.html for + // their meaning. + setDefaults(CodeMirrorConfig, { + stylesheet: "", + path: "", + parserfile: [], + basefiles: ["util.js", "stringstream.js", "select.js", "undo.js", "editor.js", "tokenize.js"], + iframeClass: null, + passDelay: 200, + passTime: 50, + lineNumberDelay: 200, + lineNumberTime: 50, + continuousScanning: false, + saveFunction: null, + onChange: null, + undoDepth: 50, + undoDelay: 800, + disableSpellcheck: true, + textWrapping: true, + readOnly: false, + width: "", + height: "300px", + autoMatchParens: false, + parserConfig: null, + tabMode: "indent", // or "spaces", "default", "shift" + reindentOnLoad: false, + activeTokens: null, + cursorActivity: null, + lineNumbers: false, + indentUnit: 2 + }); + + function addLineNumberDiv(container) { + var nums = document.createElement("DIV"), + scroller = document.createElement("DIV"); + nums.style.position = "absolute"; + nums.style.height = "100%"; + if (nums.style.setExpression) { + try {nums.style.setExpression("height", "this.previousSibling.offsetHeight + 'px'");} + catch(e) {} // Seems to throw 'Not Implemented' on some IE8 versions + } + nums.style.top = "0px"; + nums.style.overflow = "hidden"; + container.appendChild(nums); + scroller.className = "CodeMirror-line-numbers"; + nums.appendChild(scroller); + return nums; + } + + function CodeMirror(place, options) { + // Backward compatibility for deprecated options. + if (options.dumbTabs) options.tabMode = "spaces"; + else if (options.normalTab) options.tabMode = "default"; + + // Use passed options, if any, to override defaults. + this.options = options = options || {}; + setDefaults(options, CodeMirrorConfig); + + var frame = this.frame = document.createElement("IFRAME"); + if (options.iframeClass) frame.className = options.iframeClass; + frame.frameBorder = 0; + frame.src = "javascript:false;"; + frame.style.border = "0"; + frame.style.width = '100%'; + frame.style.height = '100%'; + // display: block occasionally suppresses some Firefox bugs, so we + // always add it, redundant as it sounds. + frame.style.display = "block"; + + var div = this.wrapping = document.createElement("DIV"); + div.style.position = "relative"; + div.className = "CodeMirror-wrapping"; + div.style.width = options.width; + div.style.height = options.height; + + if (place.appendChild) place.appendChild(div); + else place(div); + div.appendChild(frame); + if (options.lineNumbers) this.lineNumbers = addLineNumberDiv(div); + + // Link back to this object, so that the editor can fetch options + // and add a reference to itself. + frame.CodeMirror = this; + this.win = frame.contentWindow; + + if (typeof options.parserfile == "string") + options.parserfile = [options.parserfile]; + if (typeof options.stylesheet == "string") + options.stylesheet = [options.stylesheet]; + + var html = [""]; + // Hack to work around a bunch of IE8-specific problems. + html.push(""); + forEach(options.stylesheet, function(file) { + html.push(""); + }); + forEach(options.basefiles.concat(options.parserfile), function(file) { + html.push("';}for(i=0;i';t.iframe_script+='';}if(!t.iframe_css){t.iframe_css="";}template=t.template.replace(/\[__BASEURL__\]/g,t.baseURL);template=template.replace("[__TOOLBAR__]",html_toolbar_content);template=t.translate(template,area["settings"]["language"],"template");template=template.replace("[__CSSRULES__]",t.iframe_css);template=template.replace("[__JSCODE__]",t.iframe_script);template=template.replace("[__EA_VERSION__]",t.version);area.textarea=d.getElementById(area["settings"]["id"]);eAs[area["settings"]["id"]]["textarea"]=area.textarea;if(typeof(window.frames["frame_"+area["settings"]["id"]])!='undefined')delete window.frames["frame_"+area["settings"]["id"]];father=area.textarea.parentNode;content=d.createElement("iframe");content.name="frame_"+area["settings"]["id"];content.id="frame_"+area["settings"]["id"];content.style.borderWidth="0px";setAttribute(content,"frameBorder","0");content.style.overflow="hidden";content.style.display="none";next=area.textarea.nextSibling;if(next==null)father.appendChild(content); +else father.insertBefore(content,next);f=window.frames["frame_"+area["settings"]["id"]];f.document.open();f.eAs=eAs;f.area_id=area["settings"]["id"];f.document.area_id=area["settings"]["id"];f.document.write(template);f.document.close();},toggle:function(id,toggle_to){if(!toggle_to)toggle_to=(eAs[id]["displayed"]==true)?"off":"on";if(eAs[id]["displayed"]==true&&toggle_to=="off"){this.toggle_off(id);} +else if(eAs[id]["displayed"]==false&&toggle_to=="on"){this.toggle_on(id);}return false;},toggle_off:function(id){var fs=window.frames,f,t,parNod,nxtSib,selStart,selEnd,scrollTop,scrollLeft;if(fs["frame_"+id]){f=fs["frame_"+id];t=eAs[id]["textarea"];if(f.editArea.fullscreen['isFull'])f.editArea.toggle_full_screen(false);eAs[id]["displayed"]=false;t.wrap="off";setAttribute(t,"wrap","off");parNod=t.parentNode;nxtSib=t.nextSibling;parNod.removeChild(t);parNod.insertBefore(t,nxtSib);t.value=f.editArea.textarea.value;selStart=f.editArea.last_selection["selectionStart"];selEnd=f.editArea.last_selection["selectionEnd"];scrollTop=f.document.getElementById("result").scrollTop;scrollLeft=f.document.getElementById("result").scrollLeft;document.getElementById("frame_"+id).style.display='none';t.style.display="inline";try{t.focus();}catch(e){};if(this.isIE){t.selectionStart=selStart;t.selectionEnd=selEnd;t.focused=true;set_IE_selection(t);} +else{if(this.isOpera&&this.isOpera < 9.6){t.setSelectionRange(0,0);}try{t.setSelectionRange(selStart,selEnd);}catch(e){};}t.scrollTop=scrollTop;t.scrollLeft=scrollLeft;f.editArea.execCommand("toggle_off");}},toggle_on:function(id){var fs=window.frames,f,t,selStart=0,selEnd=0,scrollTop=0,scrollLeft=0,curPos,elem;if(fs["frame_"+id]){f=fs["frame_"+id];t=eAs[id]["textarea"];area=f.editArea;area.textarea.value=t.value;curPos=eAs[id]["settings"]["cursor_position"];if(t.use_last==true){selStart=t.last_selectionStart;selEnd=t.last_selectionEnd;scrollTop=t.last_scrollTop;scrollLeft=t.last_scrollLeft;t.use_last=false;} +else if(curPos=="auto"){try{selStart=t.selectionStart;selEnd=t.selectionEnd;scrollTop=t.scrollTop;scrollLeft=t.scrollLeft;}catch(ex){}}this.set_editarea_size_from_textarea(id,document.getElementById("frame_"+id));t.style.display="none";document.getElementById("frame_"+id).style.display="inline";area.execCommand("focus");eAs[id]["displayed"]=true;area.execCommand("update_size");f.document.getElementById("result").scrollTop=scrollTop;f.document.getElementById("result").scrollLeft=scrollLeft;area.area_select(selStart,selEnd-selStart);area.execCommand("toggle_on");} +else{elem=document.getElementById(id);elem.last_selectionStart=elem.selectionStart;elem.last_selectionEnd=elem.selectionEnd;elem.last_scrollTop=elem.scrollTop;elem.last_scrollLeft=elem.scrollLeft;elem.use_last=true;eAL.start(id);}},set_editarea_size_from_textarea:function(id,frame){var elem,width,height;elem=document.getElementById(id);width=Math.max(eAs[id]["settings"]["min_width"],elem.offsetWidth)+"px";height=Math.max(eAs[id]["settings"]["min_height"],elem.offsetHeight)+"px";if(elem.style.width.indexOf("%")!=-1)width=elem.style.width;if(elem.style.height.indexOf("%")!=-1)height=elem.style.height;frame.style.width=width;frame.style.height=height;},set_base_url:function(){var t=this,elems,i,docBasePath;if(!this.baseURL){elems=document.getElementsByTagName('script');for(i=0;i';html+='';return html;},get_control_html:function(button_name,lang){var t=this,i,but,html,si;for(i=0;i";case "|":case "separator":return '';case "select_font":html="";return html;case "syntax_selection":html="";return html;}return "["+button_name+"]";},get_template:function(){if(this.template==""){var xhr_object=null;if(window.XMLHttpRequest)xhr_object=new XMLHttpRequest(); +else if(window.ActiveXObject)xhr_object=new ActiveXObject("Microsoft.XMLHTTP"); +else{alert("XMLHTTPRequest not supported. EditArea not loaded");return;}xhr_object.open("GET",this.baseURL+"template.html",false);xhr_object.send(null);if(xhr_object.readyState==4)this.template=xhr_object.responseText; +else this.has_error();}},translate:function(text,lang,mode){if(mode=="word")text=eAL.get_word_translation(text,lang); +else if(mode="template"){eAL.current_language=lang;text=text.replace(/\{\$([^\}]+)\}/gm,eAL.translate_template);}return text;},translate_template:function(){return eAL.get_word_translation(EAL.prototype.translate_template.arguments[1],eAL.current_language);},get_word_translation:function(val,lang){var i;for(i in eAL.lang[lang]){if(i==val)return eAL.lang[lang][i];}return "_"+val;},load_script:function(url){var t=this,d=document,script,head;if(t.loadedFiles[url])return;try{script=d.createElement("script");script.type="text/javascript";script.src=url;script.charset="UTF-8";d.getElementsByTagName("head")[0].appendChild(script);}catch(e){d.write('');}t.loadedFiles[url]=true;},add_event:function(obj,name,handler){try{if(obj.attachEvent){obj.attachEvent("on"+name,handler);} +else{obj.addEventListener(name,handler,false);}}catch(e){}},remove_event:function(obj,name,handler){try{if(obj.detachEvent)obj.detachEvent("on"+name,handler); +else obj.removeEventListener(name,handler,false);}catch(e){}},reset:function(e){var formObj,is_child,i,x;formObj=eAL.isIE ? window.event.srcElement:e.target;if(formObj.tagName!='FORM')formObj=formObj.form;for(i in eAs){is_child=false;for(x=0;x old_sel["start"])this.setSelectionRange(id,new_sel["end"],new_sel["end"]); +else this.setSelectionRange(id,old_sel["start"]+open_tag.length,old_sel["start"]+open_tag.length);},hide:function(id){var fs=window.frames,d=document,t=this,scrollTop,scrollLeft,span;if(d.getElementById(id)&&!t.hidden[id]){t.hidden[id]={};t.hidden[id]["selectionRange"]=t.getSelectionRange(id);if(d.getElementById(id).style.display!="none"){t.hidden[id]["scrollTop"]=d.getElementById(id).scrollTop;t.hidden[id]["scrollLeft"]=d.getElementById(id).scrollLeft;}if(fs["frame_"+id]){t.hidden[id]["toggle"]=eAs[id]["displayed"];if(fs["frame_"+id]&&eAs[id]["displayed"]==true){scrollTop=fs["frame_"+id].document.getElementById("result").scrollTop;scrollLeft=fs["frame_"+id].document.getElementById("result").scrollLeft;} +else{scrollTop=d.getElementById(id).scrollTop;scrollLeft=d.getElementById(id).scrollLeft;}t.hidden[id]["scrollTop"]=scrollTop;t.hidden[id]["scrollLeft"]=scrollLeft;if(eAs[id]["displayed"]==true)eAL.toggle_off(id);}span=d.getElementById("EditAreaArroundInfos_"+id);if(span){span.style.display='none';}d.getElementById(id).style.display="none";}},show:function(id){var fs=window.frames,d=document,t=this,span;if((elem=d.getElementById(id))&&t.hidden[id]){elem.style.display="inline";elem.scrollTop=t.hidden[id]["scrollTop"];elem.scrollLeft=t.hidden[id]["scrollLeft"];span=d.getElementById("EditAreaArroundInfos_"+id);if(span){span.style.display='inline';}if(fs["frame_"+id]){elem.style.display="inline";if(t.hidden[id]["toggle"]==true)eAL.toggle_on(id);scrollTop=t.hidden[id]["scrollTop"];scrollLeft=t.hidden[id]["scrollLeft"];if(fs["frame_"+id]&&eAs[id]["displayed"]==true){fs["frame_"+id].document.getElementById("result").scrollTop=scrollTop;fs["frame_"+id].document.getElementById("result").scrollLeft=scrollLeft;} +else{elem.scrollTop=scrollTop;elem.scrollLeft=scrollLeft;}}sel=t.hidden[id]["selectionRange"];t.setSelectionRange(id,sel["start"],sel["end"]);delete t.hidden[id];}},getCurrentFile:function(id){return this.execCommand(id,'get_file',this.execCommand(id,'curr_file'));},getFile:function(id,file_id){return this.execCommand(id,'get_file',file_id);},getAllFiles:function(id){return this.execCommand(id,'get_all_files()');},openFile:function(id,file_infos){return this.execCommand(id,'open_file',file_infos);},closeFile:function(id,file_id){return this.execCommand(id,'close_file',file_id);},setFileEditedMode:function(id,file_id,to){var reg1,reg2;reg1=new RegExp('\\\\','g');reg2=new RegExp('"','g');return this.execCommand(id,'set_file_edited_mode("'+file_id.replace(reg1,'\\\\').replace(reg2,'\\"')+'",'+to+')');},execCommand:function(id,cmd,fct_param){switch(cmd){case "EA_init":if(eAs[id]['settings']["EA_init_callback"].length>0)eval(eAs[id]['settings']["EA_init_callback"]+"('"+id+"');");break;case "EA_delete":if(eAs[id]['settings']["EA_delete_callback"].length>0)eval(eAs[id]['settings']["EA_delete_callback"]+"('"+id+"');");break;case "EA_submit":if(eAs[id]['settings']["submit_callback"].length>0)eval(eAs[id]['settings']["submit_callback"]+"('"+id+"');");break;}if(window.frames["frame_"+id]&&window.frames["frame_"+id].editArea){if(fct_param!=undefined)return eval('window.frames["frame_'+id+'"].editArea.'+cmd+'(fct_param);'); +else return eval('window.frames["frame_'+id+'"].editArea.'+cmd+';');}return false;}};var eAL=new EAL();var eAs={}; function getAttribute(elm,aName){var aValue,taName,i;try{aValue=elm.getAttribute(aName);}catch(exept){}if(! aValue){for(i=0;i < elm.attributes.length;i++){taName=elm.attributes[i] .name.toLowerCase();if(taName==aName){aValue=elm.attributes[i] .value;return aValue;}}}return aValue;};function setAttribute(elm,attr,val){if(attr=="class"){elm.setAttribute("className",val);elm.setAttribute("class",val);} +else{elm.setAttribute(attr,val);}};function getChildren(elem,elem_type,elem_attribute,elem_attribute_match,option,depth){if(!option)var option="single";if(!depth)var depth=-1;if(elem){var children=elem.childNodes;var result=null;var results=[];for(var x=0;x0){results=results.concat(result);}} +else if(result!=null){return result;}}}}if(option=="all")return results;}return null;};function isChildOf(elem,parent){if(elem){if(elem==parent)return true;while(elem.parentNode !='undefined'){return isChildOf(elem.parentNode,parent);}}return false;};function getMouseX(e){if(e!=null&&typeof(e.pageX)!="undefined"){return e.pageX;} +else{return(e!=null?e.x:event.x)+document.documentElement.scrollLeft;}};function getMouseY(e){if(e!=null&&typeof(e.pageY)!="undefined"){return e.pageY;} +else{return(e!=null?e.y:event.y)+document.documentElement.scrollTop;}};function calculeOffsetLeft(r){return calculeOffset(r,"offsetLeft")};function calculeOffsetTop(r){return calculeOffset(r,"offsetTop")};function calculeOffset(element,attr){var offset=0;while(element){offset+=element[attr];element=element.offsetParent}return offset;};function get_css_property(elem,prop){if(document.defaultView){return document.defaultView.getComputedStyle(elem,null).getPropertyValue(prop);} +else if(elem.currentStyle){var prop=prop.replace(/-\D/gi,function(sMatch){return sMatch.charAt(sMatch.length-1).toUpperCase();});return elem.currentStyle[prop];} +else return null;}var _mCE;function start_move_element(e,id,frame){var elem_id=(e.target||e.srcElement).id;if(id)elem_id=id;if(!frame)frame=window;if(frame.event)e=frame.event;_mCE=frame.document.getElementById(elem_id);_mCE.frame=frame;frame.document.onmousemove=move_element;frame.document.onmouseup=end_move_element;mouse_x=getMouseX(e);mouse_y=getMouseY(e);_mCE.start_pos_x=mouse_x-(_mCE.style.left.replace("px","")||calculeOffsetLeft(_mCE));_mCE.start_pos_y=mouse_y-(_mCE.style.top.replace("px","")||calculeOffsetTop(_mCE));return false;};function end_move_element(e){_mCE.frame.document.onmousemove="";_mCE.frame.document.onmouseup="";_mCE=null;};function move_element(e){var newTop,newLeft,maxLeft;if(_mCE.frame&&_mCE.frame.event)e=_mCE.frame.event;newTop=getMouseY(e)-_mCE.start_pos_y;newLeft=getMouseX(e)-_mCE.start_pos_x;maxLeft=_mCE.frame.document.body.offsetWidth-_mCE.offsetWidth;max_top=_mCE.frame.document.body.offsetHeight-_mCE.offsetHeight;newTop=Math.min(Math.max(0,newTop),max_top);newLeft=Math.min(Math.max(0,newLeft),maxLeft);_mCE.style.top=newTop+"px";_mCE.style.left=newLeft+"px";return false;};var nav=eAL.nav;function getSelectionRange(textarea){return{"start":textarea.selectionStart,"end":textarea.selectionEnd};};function setSelectionRange(t,start,end){t.focus();start=Math.max(0,Math.min(t.value.length,start));end=Math.max(start,Math.min(t.value.length,end));if(nav.isOpera&&nav.isOpera < 9.6){t.selectionEnd=1;t.selectionStart=0;t.selectionEnd=1;t.selectionStart=0;}t.selectionStart=start;t.selectionEnd=end;if(nav.isIE)set_IE_selection(t);};function get_IE_selection(t){var d=document,div,range,stored_range,elem,scrollTop,relative_top,line_start,line_nb,range_start,range_end,tab;if(t&&t.focused){if(!t.ea_line_height){div=d.createElement("div");div.style.fontFamily=get_css_property(t,"font-family");div.style.fontSize=get_css_property(t,"font-size");div.style.visibility="hidden";div.innerHTML="0";d.body.appendChild(div);t.ea_line_height=div.offsetHeight;d.body.removeChild(div);}range=d.selection.createRange();try{stored_range=range.duplicate();stored_range.moveToElementText(t);stored_range.setEndPoint('EndToEnd',range);if(stored_range.parentElement()==t){elem=t;scrollTop=0;while(elem.parentNode){scrollTop+=elem.scrollTop;elem=elem.parentNode;}relative_top=range.offsetTop-calculeOffsetTop(t)+scrollTop;line_start=Math.round((relative_top / t.ea_line_height)+1);line_nb=Math.round(range.boundingHeight / t.ea_line_height);range_start=stored_range.text.length-range.text.length;tab=t.value.substr(0,range_start).split("\n");range_start+=(line_start-tab.length)*2;t.selectionStart=range_start;range_end=t.selectionStart+range.text.length;tab=t.value.substr(0,range_start+range.text.length).split("\n");range_end+=(line_start+line_nb-1-tab.length)*2;t.selectionEnd=range_end;}}catch(e){}}if(t&&t.id){setTimeout("get_IE_selection(document.getElementById('"+t.id+"'));",50);}};function IE_textarea_focus(){event.srcElement.focused=true;}function IE_textarea_blur(){event.srcElement.focused=false;}function set_IE_selection(t){var nbLineStart,nbLineStart,nbLineEnd,range;if(!window.closed){nbLineStart=t.value.substr(0,t.selectionStart).split("\n").length-1;nbLineEnd=t.value.substr(0,t.selectionEnd).split("\n").length-1;try{range=document.selection.createRange();range.moveToElementText(t);range.setEndPoint('EndToStart',range);range.moveStart('character',t.selectionStart-nbLineStart);range.moveEnd('character',t.selectionEnd-nbLineEnd-(t.selectionStart-nbLineStart));range.select();}catch(e){}}};eAL.waiting_loading["elements_functions.js"]="loaded"; + EAL.prototype.start_resize_area=function(){var d=document,a,div,width,height,father;d.onmouseup=eAL.end_resize_area;d.onmousemove=eAL.resize_area;eAL.toggle(eAL.resize["id"]);a=eAs[eAL.resize["id"]]["textarea"];div=d.getElementById("edit_area_resize");if(!div){div=d.createElement("div");div.id="edit_area_resize";div.style.border="dashed #888888 1px";}width=a.offsetWidth-2;height=a.offsetHeight-2;div.style.display="block";div.style.width=width+"px";div.style.height=height+"px";father=a.parentNode;father.insertBefore(div,a);a.style.display="none";eAL.resize["start_top"]=calculeOffsetTop(div);eAL.resize["start_left"]=calculeOffsetLeft(div);};EAL.prototype.end_resize_area=function(e){var d=document,div,a,width,height;d.onmouseup="";d.onmousemove="";div=d.getElementById("edit_area_resize");a=eAs[eAL.resize["id"]]["textarea"];width=Math.max(eAs[eAL.resize["id"]]["settings"]["min_width"],div.offsetWidth-4);height=Math.max(eAs[eAL.resize["id"]]["settings"]["min_height"],div.offsetHeight-4);if(eAL.isIE==6){width-=2;height-=2;}a.style.width=width+"px";a.style.height=height+"px";div.style.display="none";a.style.display="inline";a.selectionStart=eAL.resize["selectionStart"];a.selectionEnd=eAL.resize["selectionEnd"];eAL.toggle(eAL.resize["id"]);return false;};EAL.prototype.resize_area=function(e){var allow,newHeight,newWidth;allow=eAs[eAL.resize["id"]]["settings"]["allow_resize"];if(allow=="both"||allow=="y"){newHeight=Math.max(20,getMouseY(e)-eAL.resize["start_top"]);document.getElementById("edit_area_resize").style.height=newHeight+"px";}if(allow=="both"||allow=="x"){newWidth=Math.max(20,getMouseX(e)-eAL.resize["start_left"]);document.getElementById("edit_area_resize").style.width=newWidth+"px";}return false;};eAL.waiting_loading["resize_area.js"]="loaded"; + EAL.prototype.get_regexp=function(text_array){res="(\\b)(";for(i=0;i0)res+="|";res+=this.get_escaped_regexp(text_array[i]);}res+=")(\\b)";reg=new RegExp(res);return res;};EAL.prototype.get_escaped_regexp=function(str){return str.toString().replace(/(\.|\?|\*|\+|\\|\(|\)|\[|\]|\}|\{|\$|\^|\|)/g,"\\$1");};EAL.prototype.init_syntax_regexp=function(){var lang_style={};for(var lang in this.load_syntax){if(!this.syntax[lang]){this.syntax[lang]={};this.syntax[lang]["keywords_reg_exp"]={};this.keywords_reg_exp_nb=0;if(this.load_syntax[lang]['KEYWORDS']){param="g";if(this.load_syntax[lang]['KEYWORD_CASE_SENSITIVE']===false)param+="i";for(var i in this.load_syntax[lang]['KEYWORDS']){if(typeof(this.load_syntax[lang]['KEYWORDS'][i])=="function")continue;this.syntax[lang]["keywords_reg_exp"][i]=new RegExp(this.get_regexp(this.load_syntax[lang]['KEYWORDS'][i]),param);this.keywords_reg_exp_nb++;}}if(this.load_syntax[lang]['OPERATORS']){var str="";var nb=0;for(var i in this.load_syntax[lang]['OPERATORS']){if(typeof(this.load_syntax[lang]['OPERATORS'][i])=="function")continue;if(nb>0)str+="|";str+=this.get_escaped_regexp(this.load_syntax[lang]['OPERATORS'][i]);nb++;}if(str.length>0)this.syntax[lang]["operators_reg_exp"]=new RegExp("("+str+")","g");}if(this.load_syntax[lang]['DELIMITERS']){var str="";var nb=0;for(var i in this.load_syntax[lang]['DELIMITERS']){if(typeof(this.load_syntax[lang]['DELIMITERS'][i])=="function")continue;if(nb>0)str+="|";str+=this.get_escaped_regexp(this.load_syntax[lang]['DELIMITERS'][i]);nb++;}if(str.length>0)this.syntax[lang]["delimiters_reg_exp"]=new RegExp("("+str+")","g");}var syntax_trace=[];this.syntax[lang]["quotes"]={};var quote_tab=[];if(this.load_syntax[lang]['QUOTEMARKS']){for(var i in this.load_syntax[lang]['QUOTEMARKS']){if(typeof(this.load_syntax[lang]['QUOTEMARKS'][i])=="function")continue;var x=this.get_escaped_regexp(this.load_syntax[lang]['QUOTEMARKS'][i]);this.syntax[lang]["quotes"][x]=x;quote_tab[quote_tab.length]="("+x+"(\\\\.|[^"+x+"])*(?:"+x+"|$))";syntax_trace.push(x);}}this.syntax[lang]["comments"]={};if(this.load_syntax[lang]['COMMENT_SINGLE']){for(var i in this.load_syntax[lang]['COMMENT_SINGLE']){if(typeof(this.load_syntax[lang]['COMMENT_SINGLE'][i])=="function")continue;var x=this.get_escaped_regexp(this.load_syntax[lang]['COMMENT_SINGLE'][i]);quote_tab[quote_tab.length]="("+x+"(.|\\r|\\t)*(\\n|$))";syntax_trace.push(x);this.syntax[lang]["comments"][x]="\n";}}if(this.load_syntax[lang]['COMMENT_MULTI']){for(var i in this.load_syntax[lang]['COMMENT_MULTI']){if(typeof(this.load_syntax[lang]['COMMENT_MULTI'][i])=="function")continue;var start=this.get_escaped_regexp(i);var end=this.get_escaped_regexp(this.load_syntax[lang]['COMMENT_MULTI'][i]);quote_tab[quote_tab.length]="("+start+"(.|\\n|\\r)*?("+end+"|$))";syntax_trace.push(start);syntax_trace.push(end);this.syntax[lang]["comments"][i]=this.load_syntax[lang]['COMMENT_MULTI'][i];}}if(quote_tab.length>0)this.syntax[lang]["comment_or_quote_reg_exp"]=new RegExp("("+quote_tab.join("|")+")","gi");if(syntax_trace.length>0)this.syntax[lang]["syntax_trace_regexp"]=new RegExp("((.|\n)*?)(\\\\*("+syntax_trace.join("|")+"|$))","gmi");if(this.load_syntax[lang]['SCRIPT_DELIMITERS']){this.syntax[lang]["script_delimiters"]={};for(var i in this.load_syntax[lang]['SCRIPT_DELIMITERS']){if(typeof(this.load_syntax[lang]['SCRIPT_DELIMITERS'][i])=="function")continue;this.syntax[lang]["script_delimiters"][i]=this.load_syntax[lang]['SCRIPT_DELIMITERS'];}}this.syntax[lang]["custom_regexp"]={};if(this.load_syntax[lang]['REGEXPS']){for(var i in this.load_syntax[lang]['REGEXPS']){if(typeof(this.load_syntax[lang]['REGEXPS'][i])=="function")continue;var val=this.load_syntax[lang]['REGEXPS'][i];if(!this.syntax[lang]["custom_regexp"][val['execute']])this.syntax[lang]["custom_regexp"][val['execute']]={};this.syntax[lang]["custom_regexp"][val['execute']][i]={'regexp':new RegExp(val['search'],val['modifiers']),'class':val['class']};}}if(this.load_syntax[lang]['STYLES']){lang_style[lang]={};for(var i in this.load_syntax[lang]['STYLES']){if(typeof(this.load_syntax[lang]['STYLES'][i])=="function")continue;if(typeof(this.load_syntax[lang]['STYLES'][i])!="string"){for(var j in this.load_syntax[lang]['STYLES'][i]){lang_style[lang][j]=this.load_syntax[lang]['STYLES'][i][j];}} +else{lang_style[lang][i]=this.load_syntax[lang]['STYLES'][i];}}}var style="";for(var i in lang_style[lang]){if(lang_style[lang][i].length>0){style+="."+lang+" ."+i.toLowerCase()+" span{"+lang_style[lang][i]+"}\n";style+="."+lang+" ."+i.toLowerCase()+"{"+lang_style[lang][i]+"}\n";}}this.syntax[lang]["styles"]=style;}}};eAL.waiting_loading["reg_syntax.js"]="loaded"; +var editAreaLoader= eAL;var editAreas=eAs;EditAreaLoader=EAL;editAreaLoader.iframe_script= "".replace(//g,'this').replace(//g,'textarea').replace(//g,'function').replace(//g,'prototype').replace(//g,'settings').replace(//g,'length').replace(//g,'style').replace(//g,'parent').replace(//g,'last_selection').replace(//g,'value').replace(//g,'true').replace(//g,'false'); +editAreaLoader.template= " EditArea [__CSSRULES__] [__JSCODE__]
[__TOOLBAR__]
 
 
{$position}: {$line_abbr} 0, {$char_abbr} 0 {$total}: {$line_abbr} 0, {$char_abbr} 0 resize
{$processing}
{$search} {$close_popup}
{$replace} {$move_popup}

{$find_next} {$replace} {$replace_all}
{$close_popup}

Editarea [__EA_VERSION__]


{$shortcuts}:

{$tab}: {$add_tab}
{$shift}+{$tab}: {$remove_tab}
{$ctrl}+f: {$search_command}
{$ctrl}+r: {$replace_command}
{$ctrl}+h: {$highlight}
{$ctrl}+g: {$go_to_line}
{$ctrl}+z: {$undo}
{$ctrl}+y: {$redo}
{$ctrl}+e: {$help}
{$ctrl}+q, {$esc}: {$close_popup}
{$accesskey} E: {$toggle}

{$about_notice}
"; +editAreaLoader.iframe_css= ""; diff --git a/Hosts/Silverlight/Iron7/Resources/editor_js.txt b/Hosts/Silverlight/Iron7/Resources/editor_js.txt new file mode 100644 index 0000000000..2c67f68055 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Resources/editor_js.txt @@ -0,0 +1,1454 @@ +/* The Editor object manages the content of the editable frame. It + * catches events, colours nodes, and indents lines. This file also + * holds some functions for transforming arbitrary DOM structures into + * plain sequences of and
elements + */ + +var internetExplorer = document.selection && window.ActiveXObject && /MSIE/.test(navigator.userAgent); +var webkit = /AppleWebKit/.test(navigator.userAgent); +var safari = /Apple Computers, Inc/.test(navigator.vendor); +var gecko = /gecko\/(\d{8})/i.test(navigator.userAgent); + +// Make sure a string does not contain two consecutive 'collapseable' +// whitespace characters. +function makeWhiteSpace(n) { + var buffer = [], nb = true; + for (; n > 0; n--) { + buffer.push((nb || n == 1) ? nbsp : " "); + nb = !nb; + } + return buffer.join(""); +} + +// Create a set of white-space characters that will not be collapsed +// by the browser, but will not break text-wrapping either. +function fixSpaces(string) { + if (string.charAt(0) == " ") string = nbsp + string.slice(1); + return string.replace(/\t/g, function(){return makeWhiteSpace(indentUnit);}) + .replace(/[ \u00a0]{2,}/g, function(s) {return makeWhiteSpace(s.length);}); +} + +function cleanText(text) { + return text.replace(/\u00a0/g, " ").replace(/\u200b/g, ""); +} + +// Create a SPAN node with the expected properties for document part +// spans. +function makePartSpan(value, doc) { + var text = value; + if (value.nodeType == 3) text = value.nodeValue; + else value = doc.createTextNode(text); + + var span = doc.createElement("SPAN"); + span.isPart = true; + span.appendChild(value); + span.currentText = text; + return span; +} + +// On webkit, when the last BR of the document does not have text +// behind it, the cursor can not be put on the line after it. This +// makes pressing enter at the end of the document occasionally do +// nothing (or at least seem to do nothing). To work around it, this +// function makes sure the document ends with a span containing a +// zero-width space character. The traverseDOM iterator filters such +// character out again, so that the parsers won't see them. This +// function is called from a few strategic places to make sure the +// zwsp is restored after the highlighting process eats it. +var webkitLastLineHack = webkit ? + function(container) { + var last = container.lastChild; + if (!last || !last.isPart || last.textContent != "\u200b") + container.appendChild(makePartSpan("\u200b", container.ownerDocument)); + } : function() {}; + + var Editor = (function () { + // The HTML elements whose content should be suffixed by a newline + // when converting them to flat text. + var newlineElements = { "P": true, "DIV": true, "LI": true }; + + function asEditorLines(string) { + var tab = makeWhiteSpace(indentUnit); + return map(string.replace(/\t/g, tab).replace(/\u00a0/g, " ").replace(/\r\n?/g, "\n").split("\n"), fixSpaces); + } + + // Helper function for traverseDOM. Flattens an arbitrary DOM node + // into an array of textnodes and
tags. + function simplifyDOM(root, atEnd) { + var doc = root.ownerDocument; + var result = []; + var leaving = true; + + function simplifyNode(node, top) { + if (node.nodeType == 3) { + var text = node.nodeValue = fixSpaces(node.nodeValue.replace(/[\r\u200b]/g, "").replace(/\n/g, " ")); + if (text.length) leaving = false; + result.push(node); + } + else if (isBR(node) && node.childNodes.length == 0) { + leaving = true; + result.push(node); + } + else { + forEach(node.childNodes, simplifyNode); + if (!leaving && newlineElements.hasOwnProperty(node.nodeName.toUpperCase())) { + leaving = true; + if (!atEnd || !top) + result.push(doc.createElement("BR")); + } + } + } + + simplifyNode(root, true); + return result; + } + + // Creates a MochiKit-style iterator that goes over a series of DOM + // nodes. The values it yields are strings, the textual content of + // the nodes. It makes sure that all nodes up to and including the + // one whose text is being yielded have been 'normalized' to be just + // and
elements. + // See the story.html file for some short remarks about the use of + // continuation-passing style in this iterator. + function traverseDOM(start) { + function _yield(value, c) { cc = c; return value; } + function push(fun, arg, c) { return function () { return fun(arg, c); }; } + function stop() { cc = stop; throw StopIteration; }; + var cc = push(scanNode, start, stop); + var owner = start.ownerDocument; + var nodeQueue = []; + + // Create a function that can be used to insert nodes after the + // one given as argument. + function pointAt(node) { + var parent = node.parentNode; + var next = node.nextSibling; + return function (newnode) { + parent.insertBefore(newnode, next); + }; + } + var point = null; + + // This an Opera-specific hack -- always insert an empty span + // between two BRs, because Opera's cursor code gets terribly + // confused when the cursor is between two BRs. + var afterBR = true; + + // Insert a normalized node at the current point. If it is a text + // node, wrap it in a , and give that span a currentText + // property -- this is used to cache the nodeValue, because + // directly accessing nodeValue is horribly slow on some browsers. + // The dirty property is used by the highlighter to determine + // which parts of the document have to be re-highlighted. + function insertPart(part) { + var text = "\n"; + if (part.nodeType == 3) { + select.snapshotChanged(); + part = makePartSpan(part, owner); + text = part.currentText; + afterBR = false; + } + else { + if (afterBR && window.opera) + point(makePartSpan("", owner)); + afterBR = true; + } + part.dirty = true; + nodeQueue.push(part); + point(part); + return text; + } + + // Extract the text and newlines from a DOM node, insert them into + // the document, and yield the textual content. Used to replace + // non-normalized nodes. + function writeNode(node, c, end) { + var toYield = []; + forEach(simplifyDOM(node, end), function (part) { + toYield.push(insertPart(part)); + }); + return _yield(toYield.join(""), c); + } + + // Check whether a node is a normalized element. + function partNode(node) { + if (node.isPart && node.childNodes.length == 1 && node.firstChild.nodeType == 3) { + node.currentText = node.firstChild.nodeValue; + return !/[\n\t\r]/.test(node.currentText); + } + return false; + } + + // Handle a node. Add its successor to the continuation if there + // is one, find out whether the node is normalized. If it is, + // yield its content, otherwise, normalize it (writeNode will take + // care of yielding). + function scanNode(node, c) { + if (node.nextSibling) + c = push(scanNode, node.nextSibling, c); + + if (partNode(node)) { + nodeQueue.push(node); + afterBR = false; + return _yield(node.currentText, c); + } + else if (isBR(node)) { + if (afterBR && window.opera) + node.parentNode.insertBefore(makePartSpan("", owner), node); + nodeQueue.push(node); + afterBR = true; + return _yield("\n", c); + } + else { + var end = !node.nextSibling; + point = pointAt(node); + removeElement(node); + return writeNode(node, c, end); + } + } + + // MochiKit iterators are objects with a next function that + // returns the next value or throws StopIteration when there are + // no more values. + return { next: function () { return cc(); }, nodes: nodeQueue }; + } + + // Determine the text size of a processed node. + function nodeSize(node) { + return isBR(node) ? 1 : node.currentText.length; + } + + // Search backwards through the top-level nodes until the next BR or + // the start of the frame. + function startOfLine(node) { + while (node && !isBR(node)) node = node.previousSibling; + return node; + } + function endOfLine(node, container) { + if (!node) node = container.firstChild; + else if (isBR(node)) node = node.nextSibling; + + while (node && !isBR(node)) node = node.nextSibling; + return node; + } + + function time() { return new Date().getTime(); } + + // Client interface for searching the content of the editor. Create + // these by calling CodeMirror.getSearchCursor. To use, call + // findNext on the resulting object -- this returns a boolean + // indicating whether anything was found, and can be called again to + // skip to the next find. Use the select and replace methods to + // actually do something with the found locations. + function SearchCursor(editor, string, fromCursor, caseFold) { + this.editor = editor; + this.caseFold = caseFold; + if (caseFold) string = string.toLowerCase(); + this.history = editor.history; + this.history.commit(); + + // Are we currently at an occurrence of the search string? + this.atOccurrence = false; + // The object stores a set of nodes coming after its current + // position, so that when the current point is taken out of the + // DOM tree, we can still try to continue. + this.fallbackSize = 15; + var cursor; + // Start from the cursor when specified and a cursor can be found. + if (fromCursor && (cursor = select.cursorPos(this.editor.container))) { + this.line = cursor.node; + this.offset = cursor.offset; + } + else { + this.line = null; + this.offset = 0; + } + this.valid = !!string; + + // Create a matcher function based on the kind of string we have. + var target = string.split("\n"), self = this; + this.matches = (target.length == 1) ? + // For one-line strings, searching can be done simply by calling + // indexOf on the current line. + function () { + var line = cleanText(self.history.textAfter(self.line).slice(self.offset)); + var match = (self.caseFold ? line.toLowerCase() : line).indexOf(string); + if (match > -1) + return { from: { node: self.line, offset: self.offset + match }, + to: { node: self.line, offset: self.offset + match + string.length } + }; + } : + // Multi-line strings require internal iteration over lines, and + // some clunky checks to make sure the first match ends at the + // end of the line and the last match starts at the start. + function () { + var firstLine = cleanText(self.history.textAfter(self.line).slice(self.offset)); + var match = (self.caseFold ? firstLine.toLowerCase() : firstLine).lastIndexOf(target[0]); + if (match == -1 || match != firstLine.length - target[0].length) + return false; + var startOffset = self.offset + match; + + var line = self.history.nodeAfter(self.line); + for (var i = 1; i < target.length - 1; i++) { + var lineText = cleanText(self.history.textAfter(line)); + if ((self.caseFold ? lineText.toLowerCase() : lineText) != target[i]) + return false; + line = self.history.nodeAfter(line); + } + + var lastLine = cleanText(self.history.textAfter(line)); + if ((self.caseFold ? lastLine.toLowerCase() : lastLine).indexOf(target[target.length - 1]) != 0) + return false; + + return { from: { node: self.line, offset: startOffset }, + to: { node: line, offset: target[target.length - 1].length } + }; + }; + } + + SearchCursor.prototype = { + findNext: function () { + if (!this.valid) return false; + this.atOccurrence = false; + var self = this; + + // Go back to the start of the document if the current line is + // no longer in the DOM tree. + if (this.line && !this.line.parentNode) { + this.line = null; + this.offset = 0; + } + + // Set the cursor's position one character after the given + // position. + function saveAfter(pos) { + if (self.history.textAfter(pos.node).length > pos.offset) { + self.line = pos.node; + self.offset = pos.offset + 1; + } + else { + self.line = self.history.nodeAfter(pos.node); + self.offset = 0; + } + } + + while (true) { + var match = this.matches(); + // Found the search string. + if (match) { + this.atOccurrence = match; + saveAfter(match.from); + return true; + } + this.line = this.history.nodeAfter(this.line); + this.offset = 0; + // End of document. + if (!this.line) { + this.valid = false; + return false; + } + } + }, + + select: function () { + if (this.atOccurrence) { + select.setCursorPos(this.editor.container, this.atOccurrence.from, this.atOccurrence.to); + select.scrollToCursor(this.editor.container); + } + }, + + replace: function (string) { + if (this.atOccurrence) { + var end = this.editor.replaceRange(this.atOccurrence.from, this.atOccurrence.to, string); + this.line = end.node; + this.offset = end.offset; + this.atOccurrence = false; + } + } + }; + + // The Editor object is the main inside-the-iframe interface. + function Editor(options) { + this.options = options; + window.indentUnit = options.indentUnit; + this.parent = parent; + this.doc = document; + var container = this.container = this.doc.body; + this.win = window; + this.history = new History(container, options.undoDepth, options.undoDelay, this); + var self = this; + + if (!Editor.Parser) + throw "No parser loaded."; + if (options.parserConfig && Editor.Parser.configure) + Editor.Parser.configure(options.parserConfig); + + if (!options.readOnly) + select.setCursorPos(container, { node: null, offset: 0 }); + + this.dirty = []; + this.importCode(options.content || ""); + this.history.onChange = options.onChange; + + if (!options.readOnly) { + if (options.continuousScanning != false) { + this.scanner = this.documentScanner(options.passTime); + this.scheduleScanIn(0); + } + + function setEditable() { + // In IE, designMode frames can not run any scripts, so we use + // contentEditable instead. + if (document.body.contentEditable != undefined && internetExplorer) + document.body.contentEditable = "true"; + else + document.designMode = "on"; + + document.documentElement.style.borderWidth = "0"; + if (!options.textWrapping) + container.style.whiteSpace = "nowrap"; + } + + // If setting the frame editable fails, try again when the user + // focus it (happens when the frame is not visible on + // initialisation, in Firefox). + try { + setEditable(); + } + catch (e) { + var focusEvent = addEventHandler(document, "focus", function (evt) { + focusEvent(); + setEditable(); + }, true); + } + + addEventHandler(document, "keypress", method(this, "stuartKeyDown")); + /*addEventHandler(document, "keydown", method(this, "keyDown")); + addEventHandler(document, "keypress", method(this, "keyPress")); + addEventHandler(document, "keyup", method(this, "keyUp"));*/ + + function cursorActivity() { self.cursorActivity(false); } + addEventHandler(document.body, "mouseup", cursorActivity); + addEventHandler(document.body, "cut", cursorActivity); + + // workaround for a gecko bug [?] where going forward and then + // back again breaks designmode (no more cursor) + if (gecko) + addEventHandler(this.win, "pagehide", function () { self.unloaded = true; }); + + addEventHandler(document.body, "paste", function (event) { + cursorActivity(); + var text = null; + try { + var clipboardData = event.clipboardData || window.clipboardData; + if (clipboardData) text = clipboardData.getData('Text'); + } + catch (e) { } + if (text !== null) { + event.stop(); + self.replaceSelection(text); + select.scrollToCursor(self.container); + } + }); + + if (this.options.autoMatchParens) + addEventHandler(document.body, "click", method(this, "scheduleParenHighlight")); + } + else if (!options.textWrapping) { + container.style.whiteSpace = "nowrap"; + } + } + + function isSafeKey(code) { + return (code >= 16 && code <= 18) || // shift, control, alt + (code >= 33 && code <= 40); // arrows, home, end + } + + Editor.prototype = { + // Import a piece of code into the editor. + importCode: function (code) { + this.history.push(null, null, asEditorLines(code)); + this.history.reset(); + this.scheduleScanIn(0); + }, + + // Extract the code from the editor. + getCode: function () { + if (!this.container.firstChild) + return ""; + + var accum = []; + select.markSelection(this.win); + forEach(traverseDOM(this.container.firstChild), method(accum, "push")); + webkitLastLineHack(this.container); + select.selectMarked(); + return cleanText(accum.join("")); + }, + + checkLine: function (node) { + if (node === false || !(node == null || node.parentNode == this.container)) + throw parent.CodeMirror.InvalidLineHandle; + }, + + cursorPosition: function (start) { + if (start == null) start = true; + var pos = select.cursorPos(this.container, start); + if (pos) return { line: pos.node, character: pos.offset }; + else return { line: null, character: 0 }; + }, + + firstLine: function () { + return null; + }, + + lastLine: function () { + if (this.container.lastChild) return startOfLine(this.container.lastChild); + else return null; + }, + + nextLine: function (line) { + this.checkLine(line); + var end = endOfLine(line, this.container); + return end || false; + }, + + prevLine: function (line) { + this.checkLine(line); + if (line == null) return false; + return startOfLine(line.previousSibling); + }, + + selectLines: function (startLine, startOffset, endLine, endOffset) { + this.checkLine(startLine); + var start = { node: startLine, offset: startOffset }, end = null; + if (endOffset !== undefined) { + this.checkLine(endLine); + end = { node: endLine, offset: endOffset }; + } + select.setCursorPos(this.container, start, end); + select.scrollToCursor(this.container); + }, + + lineContent: function (line) { + var accum = []; + for (line = line ? line.nextSibling : this.container.firstChild; + line && !isBR(line); line = line.nextSibling) + accum.push(nodeText(line)); + return cleanText(accum.join("")); + }, + + setLineContent: function (line, content) { + this.history.commit(); + this.replaceRange({ node: line, offset: 0 }, + { node: line, offset: this.history.textAfter(line).length }, + content); + this.addDirtyNode(line); + this.scheduleHighlight(); + }, + + removeLine: function (line) { + var node = line ? line.nextSibling : this.container.firstChild; + while (node) { + var next = node.nextSibling; + removeElement(node); + if (isBR(node)) break; + node = next; + } + this.addDirtyNode(line); + this.scheduleHighlight(); + }, + + insertIntoLine: function (line, position, content) { + var before = null; + if (position == "end") { + before = endOfLine(line, this.container); + } + else { + for (var cur = line ? line.nextSibling : this.container.firstChild; cur; cur = cur.nextSibling) { + if (position == 0) { + before = cur; + break; + } + var text = nodeText(cur); + if (text.length > position) { + before = cur.nextSibling; + content = text.slice(0, position) + content + text.slice(position); + removeElement(cur); + break; + } + position -= text.length; + } + } + + var lines = asEditorLines(content), doc = this.container.ownerDocument; + for (var i = 0; i < lines.length; i++) { + if (i > 0) this.container.insertBefore(doc.createElement("BR"), before); + this.container.insertBefore(makePartSpan(lines[i], doc), before); + } + this.addDirtyNode(line); + this.scheduleHighlight(); + }, + + // Retrieve the selected text. + selectedText: function () { + var h = this.history; + h.commit(); + + var start = select.cursorPos(this.container, true), + end = select.cursorPos(this.container, false); + if (!start || !end) return ""; + + if (start.node == end.node) + return h.textAfter(start.node).slice(start.offset, end.offset); + + var text = [h.textAfter(start.node).slice(start.offset)]; + for (var pos = h.nodeAfter(start.node); pos != end.node; pos = h.nodeAfter(pos)) + text.push(h.textAfter(pos)); + text.push(h.textAfter(end.node).slice(0, end.offset)); + return cleanText(text.join("\n")); + }, + + // Replace the selection with another piece of text. + replaceSelection: function (text) { + this.history.commit(); + + var start = select.cursorPos(this.container, true), + end = select.cursorPos(this.container, false); + if (!start || !end) return; + + end = this.replaceRange(start, end, text); + select.setCursorPos(this.container, end); + webkitLastLineHack(this.container); + }, + + reroutePasteEvent: function () { + if (this.capturingPaste || window.opera) return; + this.capturingPaste = true; + var te = parent.document.createElement("TEXTAREA"); + te.style.position = "absolute"; + te.style.left = "-10000px"; + te.style.width = "10px"; + te.style.top = nodeTop(frameElement) + "px"; + var wrap = window.frameElement.CodeMirror.wrapping; + wrap.parentNode.insertBefore(te, wrap); + parent.focus(); + te.focus(); + + var self = this; + this.parent.setTimeout(function () { + self.capturingPaste = false; + self.win.focus(); + if (self.selectionSnapshot) // IE hack + self.win.select.setBookmark(self.container, self.selectionSnapshot); + var text = te.value; + if (text) { + self.replaceSelection(text); + select.scrollToCursor(self.container); + } + removeElement(te); + }, 10); + }, + + replaceRange: function (from, to, text) { + var lines = asEditorLines(text); + lines[0] = this.history.textAfter(from.node).slice(0, from.offset) + lines[0]; + var lastLine = lines[lines.length - 1]; + lines[lines.length - 1] = lastLine + this.history.textAfter(to.node).slice(to.offset); + var end = this.history.nodeAfter(to.node); + this.history.push(from.node, end, lines); + return { node: this.history.nodeBefore(end), + offset: lastLine.length + }; + }, + + getSearchCursor: function (string, fromCursor, caseFold) { + return new SearchCursor(this, string, fromCursor, caseFold); + }, + + // Re-indent the whole buffer + reindent: function () { + if (this.container.firstChild) + this.indentRegion(null, this.container.lastChild); + }, + + reindentSelection: function (direction) { + if (!select.somethingSelected(this.win)) { + this.indentAtCursor(direction); + } + else { + var start = select.selectionTopNode(this.container, true), + end = select.selectionTopNode(this.container, false); + if (start === false || end === false) return; + this.indentRegion(start, end, direction); + } + }, + + grabKeys: function (eventHandler, filter) { + this.frozen = eventHandler; + this.keyFilter = filter; + }, + ungrabKeys: function () { + this.frozen = "leave"; + this.keyFilter = null; + }, + + setParser: function (name) { + Editor.Parser = window[name]; + if (this.container.firstChild) { + forEach(this.container.childNodes, function (n) { + if (n.nodeType != 3) n.dirty = true; + }); + this.addDirtyNode(this.firstChild); + this.scheduleHighlight(); + } + }, + + stuartKeyDown: function (event) { + if (event.keyCode == 13) { + select.insertNewlineAtCursor(this.win); + this.indentAtCursor(); + select.scrollToCursor(this.container); + event.stop(); + } + // Don't scan when the user is typing. + this.delayScanning(); + }, + + // Intercept enter and tab, and assign their new functions. + keyDown: function (event) { + if (this.frozen == "leave") this.frozen = null; + if (this.frozen && (!this.keyFilter || this.keyFilter(event.keyCode))) { + event.stop(); + this.frozen(event); + return; + } + + var code = event.keyCode; + // Don't scan when the user is typing. + this.delayScanning(); + // Schedule a paren-highlight event, if configured. + if (this.options.autoMatchParens) + this.scheduleParenHighlight(); + + // The various checks for !altKey are there because AltGr sets both + // ctrlKey and altKey to true, and should not be recognised as + // Control. + if (code == 13) { // enter + if (event.ctrlKey && !event.altKey) { + this.reparseBuffer(); + } + else { + select.insertNewlineAtCursor(this.win); + this.indentAtCursor(); + select.scrollToCursor(this.container); + } + event.stop(); + } + else if (code == 9 && this.options.tabMode != "default" && !event.ctrlKey) { // tab + this.handleTab(!event.shiftKey); + event.stop(); + } + else if (code == 32 && event.shiftKey && this.options.tabMode == "default") { // space + this.handleTab(true); + event.stop(); + } + else if (code == 36 && !event.shiftKey && !event.ctrlKey) { // home + if (this.home()) event.stop(); + } + else if (code == 35 && !event.shiftKey && !event.ctrlKey) { // end + if (this.end()) event.stop(); + } + else if ((code == 219 || code == 221) && event.ctrlKey && !event.altKey) { // [, ] + this.highlightParens(event.shiftKey, true); + event.stop(); + } + else if (event.metaKey && !event.shiftKey && (code == 37 || code == 39)) { // Meta-left/right + var cursor = select.selectionTopNode(this.container); + if (cursor === false || !this.container.firstChild) return; + + if (code == 37) select.focusAfterNode(startOfLine(cursor), this.container); + else { + var end = endOfLine(cursor, this.container); + select.focusAfterNode(end ? end.previousSibling : this.container.lastChild, this.container); + } + event.stop(); + } + else if ((event.ctrlKey || event.metaKey) && !event.altKey) { + if ((event.shiftKey && code == 90) || code == 89) { // shift-Z, Y + select.scrollToNode(this.history.redo()); + event.stop(); + } + else if (code == 90 || (safari && code == 8)) { // Z, backspace + select.scrollToNode(this.history.undo()); + event.stop(); + } + else if (code == 83 && this.options.saveFunction) { // S + this.options.saveFunction(); + event.stop(); + } + else if (internetExplorer && code == 86) { + this.reroutePasteEvent(); + } + } + }, + + // Check for characters that should re-indent the current line, + // and prevent Opera from handling enter and tab anyway. + keyPress: function (event) { + var electric = Editor.Parser.electricChars, self = this; + // Hack for Opera, and Firefox on OS X, in which stopping a + // keydown event does not prevent the associated keypress event + // from happening, so we have to cancel enter and tab again + // here. + if ((this.frozen && (!this.keyFilter || this.keyFilter(event.keyCode))) || + event.code == 13 || (event.code == 9 && this.options.tabMode != "default") || + (event.keyCode == 32 && event.shiftKey && this.options.tabMode == "default")) + event.stop(); + else if (electric && electric.indexOf(event.character) != -1) + this.parent.setTimeout(function () { self.indentAtCursor(null); }, 0); + else if ((event.character == "v" || event.character == "V") + && (event.ctrlKey || event.metaKey) && !event.altKey) // ctrl-V + this.reroutePasteEvent(); + }, + + // Mark the node at the cursor dirty when a non-safe key is + // released. + keyUp: function (event) { + this.cursorActivity(isSafeKey(event.keyCode)); + }, + + // Indent the line following a given
, or null for the first + // line. If given a
element, this must have been highlighted + // so that it has an indentation method. Returns the whitespace + // element that has been modified or created (if any). + indentLineAfter: function (start, direction) { + // whiteSpace is the whitespace span at the start of the line, + // or null if there is no such node. + var whiteSpace = start ? start.nextSibling : this.container.firstChild; + if (whiteSpace && !hasClass(whiteSpace, "whitespace")) + whiteSpace = null; + + // Sometimes the start of the line can influence the correct + // indentation, so we retrieve it. + var firstText = whiteSpace ? whiteSpace.nextSibling : (start ? start.nextSibling : this.container.firstChild); + var nextChars = (start && firstText && firstText.currentText) ? firstText.currentText : ""; + + // Ask the lexical context for the correct indentation, and + // compute how much this differs from the current indentation. + var newIndent = 0, curIndent = whiteSpace ? whiteSpace.currentText.length : 0; + if (direction != null && this.options.tabMode == "shift") + newIndent = direction ? curIndent + indentUnit : Math.max(0, curIndent - indentUnit) + else if (start) + newIndent = start.indentation(nextChars, curIndent, direction); + else if (Editor.Parser.firstIndentation) + newIndent = Editor.Parser.firstIndentation(nextChars, curIndent, direction); + var indentDiff = newIndent - curIndent; + + // If there is too much, this is just a matter of shrinking a span. + if (indentDiff < 0) { + if (newIndent == 0) { + if (firstText) select.snapshotMove(whiteSpace.firstChild, firstText.firstChild, 0); + removeElement(whiteSpace); + whiteSpace = null; + } + else { + select.snapshotMove(whiteSpace.firstChild, whiteSpace.firstChild, indentDiff, true); + whiteSpace.currentText = makeWhiteSpace(newIndent); + whiteSpace.firstChild.nodeValue = whiteSpace.currentText; + } + } + // Not enough... + else if (indentDiff > 0) { + // If there is whitespace, we grow it. + if (whiteSpace) { + whiteSpace.currentText = makeWhiteSpace(newIndent); + whiteSpace.firstChild.nodeValue = whiteSpace.currentText; + } + // Otherwise, we have to add a new whitespace node. + else { + whiteSpace = makePartSpan(makeWhiteSpace(newIndent), this.doc); + whiteSpace.className = "whitespace"; + if (start) insertAfter(whiteSpace, start); + else this.container.insertBefore(whiteSpace, this.container.firstChild); + } + if (firstText) select.snapshotMove(firstText.firstChild, whiteSpace.firstChild, curIndent, false, true); + } + if (indentDiff != 0) this.addDirtyNode(start); + return whiteSpace; + }, + + // Re-highlight the selected part of the document. + highlightAtCursor: function () { + var pos = select.selectionTopNode(this.container, true); + var to = select.selectionTopNode(this.container, false); + if (pos === false || to === false) return false; + + select.markSelection(this.win); + if (this.highlight(pos, endOfLine(to, this.container), true, 20) === false) + return false; + select.selectMarked(); + return true; + }, + + // When tab is pressed with text selected, the whole selection is + // re-indented, when nothing is selected, the line with the cursor + // is re-indented. + handleTab: function (direction) { + if (this.options.tabMode == "spaces") + select.insertTabAtCursor(this.win); + else + this.reindentSelection(direction); + }, + + // Custom home behaviour that doesn't land the cursor in front of + // leading whitespace unless pressed twice. + home: function () { + var cur = select.selectionTopNode(this.container, true), start = cur; + if (cur === false || !(!cur || cur.isPart || isBR(cur)) || !this.container.firstChild) + return false; + + while (cur && !isBR(cur)) cur = cur.previousSibling; + var next = cur ? cur.nextSibling : this.container.firstChild; + if (next && next != start && next.isPart && hasClass(next, "whitespace")) + select.focusAfterNode(next, this.container); + else + select.focusAfterNode(cur, this.container); + + select.scrollToCursor(this.container); + return true; + }, + + // Some browsers (Opera) don't manage to handle the end key + // properly in the face of vertical scrolling. + end: function () { + var cur = select.selectionTopNode(this.container, true); + if (cur === false) return false; + cur = endOfLine(cur, this.container); + if (!cur) return false; + select.focusAfterNode(cur.previousSibling, this.container); + select.scrollToCursor(this.container); + return true; + }, + + // Delay (or initiate) the next paren highlight event. + scheduleParenHighlight: function () { + if (this.parenEvent) this.parent.clearTimeout(this.parenEvent); + var self = this; + this.parenEvent = this.parent.setTimeout(function () { self.highlightParens(); }, 300); + }, + + // Take the token before the cursor. If it contains a character in + // '()[]{}', search for the matching paren/brace/bracket, and + // highlight them in green for a moment, or red if no proper match + // was found. + highlightParens: function (jump, fromKey) { + var self = this; + // give the relevant nodes a colour. + function highlight(node, ok) { + if (!node) return; + if (self.options.markParen) { + self.options.markParen(node, ok); + } + else { + node.style.fontWeight = "bold"; + node.style.color = ok ? "#8F8" : "#F88"; + } + } + function unhighlight(node) { + if (!node) return; + if (self.options.unmarkParen) { + self.options.unmarkParen(node); + } + else { + node.style.fontWeight = ""; + node.style.color = ""; + } + } + if (!fromKey && self.highlighted) { + unhighlight(self.highlighted[0]); + unhighlight(self.highlighted[1]); + } + + if (!window.select) return; + // Clear the event property. + if (this.parenEvent) this.parent.clearTimeout(this.parenEvent); + this.parenEvent = null; + + // Extract a 'paren' from a piece of text. + function paren(node) { + if (node.currentText) { + var match = node.currentText.match(/^[\s\u00a0]*([\(\)\[\]{}])[\s\u00a0]*$/); + return match && match[1]; + } + } + // Determine the direction a paren is facing. + function forward(ch) { + return /[\(\[\{]/.test(ch); + } + + var ch, cursor = select.selectionTopNode(this.container, true); + if (!cursor || !this.highlightAtCursor()) return; + cursor = select.selectionTopNode(this.container, true); + if (!(cursor && ((ch = paren(cursor)) || (cursor = cursor.nextSibling) && (ch = paren(cursor))))) + return; + // We only look for tokens with the same className. + var className = cursor.className, dir = forward(ch), match = matching[ch]; + + // Since parts of the document might not have been properly + // highlighted, and it is hard to know in advance which part we + // have to scan, we just try, and when we find dirty nodes we + // abort, parse them, and re-try. + function tryFindMatch() { + var stack = [], ch, ok = true; + for (var runner = cursor; runner; runner = dir ? runner.nextSibling : runner.previousSibling) { + if (runner.className == className && isSpan(runner) && (ch = paren(runner))) { + if (forward(ch) == dir) + stack.push(ch); + else if (!stack.length) + ok = false; + else if (stack.pop() != matching[ch]) + ok = false; + if (!stack.length) break; + } + else if (runner.dirty || !isSpan(runner) && !isBR(runner)) { + return { node: runner, status: "dirty" }; + } + } + return { node: runner, status: runner && ok }; + } + + while (true) { + var found = tryFindMatch(); + if (found.status == "dirty") { + this.highlight(found.node, endOfLine(found.node)); + // Needed because in some corner cases a highlight does not + // reach a node. + found.node.dirty = false; + continue; + } + else { + highlight(cursor, found.status); + highlight(found.node, found.status); + if (fromKey) + self.parent.setTimeout(function () { unhighlight(cursor); unhighlight(found.node); }, 500); + else + self.highlighted = [cursor, found.node]; + if (jump && found.node) + select.focusAfterNode(found.node.previousSibling, this.container); + break; + } + } + }, + + // Adjust the amount of whitespace at the start of the line that + // the cursor is on so that it is indented properly. + indentAtCursor: function (direction) { + if (!this.container.firstChild) return; + // The line has to have up-to-date lexical information, so we + // highlight it first. + if (!this.highlightAtCursor()) return; + var cursor = select.selectionTopNode(this.container, false); + // If we couldn't determine the place of the cursor, + // there's nothing to indent. + if (cursor === false) + return; + var lineStart = startOfLine(cursor); + var whiteSpace = this.indentLineAfter(lineStart, direction); + if (cursor == lineStart && whiteSpace) + cursor = whiteSpace; + // This means the indentation has probably messed up the cursor. + if (cursor == whiteSpace) + select.focusAfterNode(cursor, this.container); + }, + + // Indent all lines whose start falls inside of the current + // selection. + indentRegion: function (start, end, direction) { + var current = (start = startOfLine(start)), before = start && startOfLine(start.previousSibling); + if (!isBR(end)) end = endOfLine(end, this.container); + this.addDirtyNode(start); + + do { + var next = endOfLine(current, this.container); + if (current) this.highlight(before, next, true); + this.indentLineAfter(current, direction); + before = current; + current = next; + } while (current != end); + select.setCursorPos(this.container, { node: start, offset: 0 }, { node: end, offset: 0 }); + }, + + // Find the node that the cursor is in, mark it as dirty, and make + // sure a highlight pass is scheduled. + cursorActivity: function (safe) { + // pagehide event hack above + if (this.unloaded) { + this.win.document.designMode = "off"; + this.win.document.designMode = "on"; + this.unloaded = false; + } + + if (internetExplorer) { + this.container.createTextRange().execCommand("unlink"); + this.selectionSnapshot = select.getBookmark(this.container); + } + + var activity = this.options.cursorActivity; + if (!safe || activity) { + var cursor = select.selectionTopNode(this.container, false); + if (cursor === false || !this.container.firstChild) return; + cursor = cursor || this.container.firstChild; + if (activity) activity(cursor); + if (!safe) { + this.scheduleHighlight(); + this.addDirtyNode(cursor); + } + } + }, + + reparseBuffer: function () { + forEach(this.container.childNodes, function (node) { node.dirty = true; }); + if (this.container.firstChild) + this.addDirtyNode(this.container.firstChild); + }, + + // Add a node to the set of dirty nodes, if it isn't already in + // there. + addDirtyNode: function (node) { + node = node || this.container.firstChild; + if (!node) return; + + for (var i = 0; i < this.dirty.length; i++) + if (this.dirty[i] == node) return; + + if (node.nodeType != 3) + node.dirty = true; + this.dirty.push(node); + }, + + allClean: function () { + return !this.dirty.length; + }, + + // Cause a highlight pass to happen in options.passDelay + // milliseconds. Clear the existing timeout, if one exists. This + // way, the passes do not happen while the user is typing, and + // should as unobtrusive as possible. + scheduleHighlight: function () { + // Timeouts are routed through the parent window, because on + // some browsers designMode windows do not fire timeouts. + var self = this; + this.parent.clearTimeout(this.highlightTimeout); + this.highlightTimeout = this.parent.setTimeout(function () { self.highlightDirty(); }, this.options.passDelay); + }, + + // Fetch one dirty node, and remove it from the dirty set. + getDirtyNode: function () { + while (this.dirty.length > 0) { + var found = this.dirty.pop(); + // IE8 sometimes throws an unexplainable 'invalid argument' + // exception for found.parentNode + try { + // If the node has been coloured in the meantime, or is no + // longer in the document, it should not be returned. + while (found && found.parentNode != this.container) + found = found.parentNode; + if (found && (found.dirty || found.nodeType == 3)) + return found; + } catch (e) { } + } + return null; + }, + + // Pick dirty nodes, and highlight them, until options.passTime + // milliseconds have gone by. The highlight method will continue + // to next lines as long as it finds dirty nodes. It returns + // information about the place where it stopped. If there are + // dirty nodes left after this function has spent all its lines, + // it shedules another highlight to finish the job. + highlightDirty: function (force) { + // Prevent FF from raising an error when it is firing timeouts + // on a page that's no longer loaded. + if (!window.select) return false; + + if (!this.options.readOnly) select.markSelection(this.win); + var start, endTime = force ? null : time() + this.options.passTime; + while ((time() < endTime || force) && (start = this.getDirtyNode())) { + var result = this.highlight(start, endTime); + if (result && result.node && result.dirty) + this.addDirtyNode(result.node); + } + if (!this.options.readOnly) select.selectMarked(); + if (start) this.scheduleHighlight(); + return this.dirty.length == 0; + }, + + // Creates a function that, when called through a timeout, will + // continuously re-parse the document. + documentScanner: function (passTime) { + var self = this, pos = null; + return function () { + // FF timeout weirdness workaround. + if (!window.select) return; + // If the current node is no longer in the document... oh + // well, we start over. + if (pos && pos.parentNode != self.container) + pos = null; + select.markSelection(self.win); + var result = self.highlight(pos, time() + passTime, true); + select.selectMarked(); + var newPos = result ? (result.node && result.node.nextSibling) : null; + pos = (pos == newPos) ? null : newPos; + self.delayScanning(); + }; + }, + + // Starts the continuous scanning process for this document after + // a given interval. + delayScanning: function () { + this.scheduleScanIn(this.options.passDelay); + }, + + // Starts the continuous scanning process for this document after + // a given interval. + scheduleScanIn: function (interval) { + if (this.scanner) { + this.parent.clearTimeout(this.documentScan); + this.documentScan = this.parent.setTimeout(this.scanner, interval); + } + }, + + // The function that does the actual highlighting/colouring (with + // help from the parser and the DOM normalizer). Its interface is + // rather overcomplicated, because it is used in different + // situations: ensuring that a certain line is highlighted, or + // highlighting up to X milliseconds starting from a certain + // point. The 'from' argument gives the node at which it should + // start. If this is null, it will start at the beginning of the + // document. When a timestamp is given with the 'target' argument, + // it will stop highlighting at that time. If this argument holds + // a DOM node, it will highlight until it reaches that node. If at + // any time it comes across two 'clean' lines (no dirty nodes), it + // will stop, except when 'cleanLines' is true. maxBacktrack is + // the maximum number of lines to backtrack to find an existing + // parser instance. This is used to give up in situations where a + // highlight would take too long and freeze the browser interface. + highlight: function (from, target, cleanLines, maxBacktrack) { + var container = this.container, self = this, active = this.options.activeTokens; + var endTime = (typeof target == "number" ? target : null); + + if (!container.firstChild) + return false; + // Backtrack to the first node before from that has a partial + // parse stored. + while (from && (!from.parserFromHere || from.dirty)) { + if (maxBacktrack != null && isBR(from) && (--maxBacktrack) < 0) + return false; + from = from.previousSibling; + } + // If we are at the end of the document, do nothing. + if (from && !from.nextSibling) + return false; + + // Check whether a part ( node) and the corresponding token + // match. + function correctPart(token, part) { + return !part.reduced && part.currentText == token.value && part.className == token.style; + } + // Shorten the text associated with a part by chopping off + // characters from the front. Note that only the currentText + // property gets changed. For efficiency reasons, we leave the + // nodeValue alone -- we set the reduced flag to indicate that + // this part must be replaced. + function shortenPart(part, minus) { + part.currentText = part.currentText.substring(minus); + part.reduced = true; + } + // Create a part corresponding to a given token. + function tokenPart(token) { + var part = makePartSpan(token.value, self.doc); + part.className = token.style; + return part; + } + + function maybeTouch(node) { + if (node) { + var old = node.oldNextSibling; + if (lineDirty || old === undefined || node.nextSibling != old) + self.history.touch(node); + node.oldNextSibling = node.nextSibling; + } + else { + var old = self.container.oldFirstChild; + if (lineDirty || old === undefined || self.container.firstChild != old) + self.history.touch(null); + self.container.oldFirstChild = self.container.firstChild; + } + } + + // Get the token stream. If from is null, we start with a new + // parser from the start of the frame, otherwise a partial parse + // is resumed. + var traversal = traverseDOM(from ? from.nextSibling : container.firstChild), + stream = stringStream(traversal), + parsed = from ? from.parserFromHere(stream) : Editor.Parser.make(stream); + + function surroundedByBRs(node) { + return (node.previousSibling == null || isBR(node.previousSibling)) && + (node.nextSibling == null || isBR(node.nextSibling)); + } + + // parts is an interface to make it possible to 'delay' fetching + // the next DOM node until we are completely done with the one + // before it. This is necessary because often the next node is + // not yet available when we want to proceed past the current + // one. + var parts = { + current: null, + // Fetch current node. + get: function () { + if (!this.current) + this.current = traversal.nodes.shift(); + return this.current; + }, + // Advance to the next part (do not fetch it yet). + next: function () { + this.current = null; + }, + // Remove the current part from the DOM tree, and move to the + // next. + remove: function () { + container.removeChild(this.get()); + this.current = null; + }, + // Advance to the next part that is not empty, discarding empty + // parts. + getNonEmpty: function () { + var part = this.get(); + // Allow empty nodes when they are alone on a line, needed + // for the FF cursor bug workaround (see select.js, + // insertNewlineAtCursor). + while (part && isSpan(part) && part.currentText == "") { + // Leave empty nodes that are alone on a line alone in + // Opera, since that browsers doesn't deal well with + // having 2 BRs in a row. + if (window.opera && surroundedByBRs(part)) { + this.next(); + part = this.get(); + } + else { + var old = part; + this.remove(); + part = this.get(); + // Adjust selection information, if any. See select.js for details. + select.snapshotMove(old.firstChild, part && (part.firstChild || part), 0); + } + } + + return part; + } + }; + + var lineDirty = false, prevLineDirty = true, lineNodes = 0; + + // This forEach loops over the tokens from the parsed stream, and + // at the same time uses the parts object to proceed through the + // corresponding DOM nodes. + forEach(parsed, function (token) { + var part = parts.getNonEmpty(); + + if (token.value == "\n") { + // The idea of the two streams actually staying synchronized + // is such a long shot that we explicitly check. + if (!isBR(part)) + throw "Parser out of sync. Expected BR."; + + if (part.dirty || !part.indentation) lineDirty = true; + maybeTouch(from); + from = part; + + // Every
gets a copy of the parser state and a lexical + // context assigned to it. The first is used to be able to + // later resume parsing from this point, the second is used + // for indentation. + part.parserFromHere = parsed.copy(); + part.indentation = token.indentation; + part.dirty = false; + + // If the target argument wasn't an integer, go at least + // until that node. + if (endTime == null && part == target) throw StopIteration; + + // A clean line with more than one node means we are done. + // Throwing a StopIteration is the way to break out of a + // MochiKit forEach loop. + if ((endTime != null && time() >= endTime) || (!lineDirty && !prevLineDirty && lineNodes > 1 && !cleanLines)) + throw StopIteration; + prevLineDirty = lineDirty; lineDirty = false; lineNodes = 0; + parts.next(); + } + else { + if (!isSpan(part)) + throw "Parser out of sync. Expected SPAN."; + if (part.dirty) + lineDirty = true; + lineNodes++; + + // If the part matches the token, we can leave it alone. + if (correctPart(token, part)) { + part.dirty = false; + parts.next(); + } + // Otherwise, we have to fix it. + else { + lineDirty = true; + // Insert the correct part. + var newPart = tokenPart(token); + container.insertBefore(newPart, part); + if (active) active(newPart, token, self); + var tokensize = token.value.length; + var offset = 0; + // Eat up parts until the text for this token has been + // removed, adjusting the stored selection info (see + // select.js) in the process. + while (tokensize > 0) { + part = parts.get(); + // hack hack - stuart did this.. + var partsize = 0; + if (part && part.currentText) { + partsize = part.currentText.length; + select.snapshotReplaceNode(part.firstChild, newPart.firstChild, tokensize, offset); + } + if (partsize > tokensize) { + shortenPart(part, tokensize); + tokensize = 0; + } + else { + tokensize -= partsize; + offset += partsize; + parts.remove(); + } + } + } + } + }); + maybeTouch(from); + webkitLastLineHack(this.container); + + // The function returns some status information that is used by + // hightlightDirty to determine whether and where it has to + // continue. + return { node: parts.getNonEmpty(), + dirty: lineDirty + }; + } + }; + + return Editor; + })(); + +addEventHandler(window, "load", function() { + //var CodeMirror = window.frameElement.CodeMirror; + editor = new Editor(CodeMirror.options); + //this.parent.setTimeout(method(CodeMirror, "init"), 0); +}); diff --git a/Hosts/Silverlight/Iron7/Resources/en.js b/Hosts/Silverlight/Iron7/Resources/en.js new file mode 100644 index 0000000000..9209f89470 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Resources/en.js @@ -0,0 +1,48 @@ +editAreaLoader.lang["en"]={ +new_document: "new empty document", +search_button: "search and replace", +search_command: "search next / open search area", +search: "search", +replace: "replace", +replace_command: "replace / open search area", +find_next: "find next", +replace_all: "replace all", +reg_exp: "regular expressions", +match_case: "match case", +not_found: "not found.", +occurrence_replaced: "occurences replaced.", +search_field_empty: "Search field empty", +restart_search_at_begin: "End of area reached. Restart at begin.", +move_popup: "move search popup", +font_size: "--Font size--", +go_to_line: "go to line", +go_to_line_prompt: "go to line number:", +undo: "undo", +redo: "redo", +change_smooth_selection: "enable/disable some display features (smarter display but more CPU charge)", +highlight: "toggle syntax highlight on/off", +reset_highlight: "reset highlight (if desyncronized from text)", +word_wrap: "toggle word wrapping mode", +help: "about", +save: "save", +load: "load", +line_abbr: "Ln", +char_abbr: "Ch", +position: "Position", +total: "Total", +close_popup: "close popup", +shortcuts: "Shortcuts", +add_tab: "add tabulation to text", +remove_tab: "remove tabulation to text", +about_notice: "Notice: syntax highlight function is only for small text", +toggle: "Toggle editor", +accesskey: "Accesskey", +tab: "Tab", +shift: "Shift", +ctrl: "Ctrl", +esc: "Esc", +processing: "Processing...", +fullscreen: "fullscreen", +syntax_selection: "--Syntax--", +close_tab: "Close file" +}; diff --git a/Hosts/Silverlight/Iron7/Resources/parseruby_js.txt b/Hosts/Silverlight/Iron7/Resources/parseruby_js.txt new file mode 100644 index 0000000000..020c22ab11 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Resources/parseruby_js.txt @@ -0,0 +1,415 @@ + +var RubyParser = Editor.Parser = (function() { + // Token types that can be considered to be atoms. + var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true}; + // Constructor for the lexical context objects. + function JSLexical(indented, column, type, align, prev, info) { + // indentation at start of this line + this.indented = indented; + // column at which this scope was opened + this.column = column; + // type of scope ('vardef', 'stat' (statement), 'form' (special form), '[', '{', or '(') + this.type = type; + // '[', '{', or '(' blocks that have any text after their opening + // character are said to be 'aligned' -- any lines below are + // indented all the way to the opening character. + if (align != null) + this.align = align; + // Parent scope, if any. + this.prev = prev; + this.info = info; + } + + var NORMALCONTEXT = 'rb-normal'; + var ERRORCLASS = 'rb-error'; + var COMMENTCLASS = 'rb-comment'; + var SYMBOLCLASS = 'rb-symbol'; + var CONSTCLASS = 'rb-constant'; + var OPCLASS = 'rb-operator'; + var INSTANCEMETHODCALLCLASS = 'rb-method' + var VARIABLECLASS = 'rb-variable'; + var STRINGCLASS = 'rb-string'; + var FIXNUMCLASS = 'rb-fixnum rb-numeric'; + var METHODCALLCLASS = 'rb-method-call'; + var HEREDOCCLASS = 'rb-heredoc'; + var ERRORCLASS = 'rb-parse-error'; + var BLOCKCOMMENT = 'rb-block-comment'; + var FLOATCLASS = 'rb-float'; + var HEXNUMCLASS = 'rb-hexnum'; + var BINARYCLASS = 'rb-binary'; + var ASCIICODE = 'rb-ascii' + var LONGCOMMENTCLASS = 'rb-long-comment'; + var WHITESPACEINLONGCOMMENTCLASS = 'rb-long-comment-whitespace'; + var KEWORDCLASS = 'rb-keyword'; + var REGEXPCLASS = 'rb-regexp'; + var GLOBALVARCLASS = 'rb-global-variable'; + var EXECCLASS = 'rb-exec'; + var INTRANGECLASS = 'rb-range'; + var OPCLASS = 'rb-operator'; + var METHODPARAMCLASS = 'rb-method-parameter'; + + + // My favourite JavaScript indentation rules. + function indentRuby(lexical) { + return function(firstChars) { + var firstChar = firstChars && firstChars.charAt(0), type = lexical.type; + var closing = firstChar == type; + //console.log(lexical); + + if (type == "vardef") + return lexical.indented + 2; + else if (type == "form" && firstChar == "{") + return lexical.indented; + else if (type == "stat" || type == "form") + return lexical.indented + indentUnit; + else if (lexical.info == "switch" && !closing) + return lexical.indented + (/^(?:case|default)\b/.test(firstChars) ? indentUnit : 2 * indentUnit); + else if (lexical.align) + return lexical.column - (closing ? 1 : 0); + else + return lexical.indented + (closing ? 0 : indentUnit); + }; + } + + // The parser-iterator-producing function itself. + function parseRuby(input, basecolumn) { + // Wrap the input in a token stream + var tokens = tokenizeRuby(input); + // The parser state. cc is a stack of actions that have to be + // performed to finish the current statement. For example we might + // know that we still need to find a closing parenthesis and a + // semicolon. Actions at the end of the stack go first. It is + // initialized with an infinitely looping action that consumes + // whole statements. + var cc = [statement]; + // Context contains information about the current local scope, the + // variables defined in that, and the scopes above it. + var context = {prev: null, vars: {"this": true, "arguments": true, 'block' : 0}}; + // The lexical scope, used mostly for indentation. + var lexical = new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false); + // Current column, and the indentation at the start of the current + // line. Used to create lexical scope objects. + var column = 0; + var indented = 0; + // Variables which are used by the mark, cont, and pass functions + // below to communicate with the driver loop in the 'next' + // function. + var consume, marked; + + // The iterator object. + var parser = {next: next, copy: copy}; + + function next(){ + // Start by performing any 'lexical' actions (adjusting the + // lexical variable), or the operations below will be working + // with the wrong lexical state. + while(cc[cc.length - 1].lex) + cc.pop()(); + + // Fetch a token. + var token = tokens.next(); + + // Adjust column and indented. + if (token.type == "whitespace" && column == 0) { + indented = token.value.length; + } + column += token.value.length; + if (token.content == "\n"){ + indented = column = 0; + // If the lexical scope's align property is still undefined at + // the end of the line, it is an un-aligned scope. + if (!("align" in lexical)) + lexical.align = false; + // Newline tokens get an indentation function associated with + // them. + token.indentation = indentRuby(lexical); + } + // No more processing for meaningless tokens. + //if (token.type == "whitespace" || token.type == "comment") + // return token; + + // When a meaningful token is found and the lexical scope's + // align is undefined, it is an aligned scope. + if (!("align" in lexical)) + lexical.align = true; + + // Execute actions until one 'consumes' the token and we can + // return it. + while(true) { + consume = marked = false; + // Take and execute the topmost action. + cc.pop()(token, token.content); + if (consume){ + // Marked is used to change the style of the current token. + if (marked) + token.style = marked; + return token; + } + } + } + + // This makes a copy of the parser state. It stores all the + // stateful variables in a closure, and returns a function that + // will restore them when called with a new input stream. Note + // that the cc array has to be copied, because it is contantly + // being modified. Lexical objects are not mutated, and context + // objects are not mutated in a harmful way, so they can be shared + // between runs of the parser. + function copy(){ + var _context = context, _lexical = lexical, _cc = cc.concat([]), _tokenState = tokens.state; + + return function copyParser(input){ + context = _context; + lexical = _lexical; + cc = _cc.concat([]); // copies the array + column = indented = 0; + tokens = tokenizeRuby(input, _tokenState); + return parser; + }; + } + + // Helper function for pushing a number of actions onto the cc + // stack in reverse order. + function push(fs){ + for (var i = fs.length - 1; i >= 0; i--) + cc.push(fs[i]); + } + // cont and pass are used by the action functions to add other + // actions to the stack. cont will cause the current token to be + // consumed, pass will leave it for the next action. + function cont(){ + push(arguments); + consume = true; + } + function pass(){ + push(arguments); + consume = false; + } + // Used to change the style of the current token. + function mark(style){ + marked = style; + } + + // Push a new scope. Will automatically link the current scope. + function pushcontext(){ + //context = {prev: context, vars: {"this": true, "arguments": true, 'block' : 0}}; + } + // Pop off the current scope. + function popcontext(){ + //context = context.prev; + } + // Register a variable in the current scope. + function register(varname, style){ + if (context){ + context.vars[varname] = style; + } + } + // Register a variable in the current scope. + function isRegistered(varname){ + //console.log('"'+varname+'"', context); + if (context) { + if (context.vars[varname]) return true; + var c = context.prev; + while(c) { + //console.log(c); + if (c.vars[varname]) return true; + c = c.prev; + } + } + return false; + } + + function registeredMark(varname){ + return context.vars[varname]; + } + + // Check whether a variable is defined in the current scope. + function inScope(varname){ + var cursor = context; + while (cursor) { + if (cursor.vars[varname]) + return true; + cursor = cursor.prev; + } + return false; + } + + // Push a new lexical context of the given type. + function pushlex(type, info) { + var result = function(){ + lexical = new JSLexical(indented, column, type, null, lexical, info) + }; + result.lex = true; + return result; + } + // Pop off the current lexical context. + function poplex(){ + lexical = lexical.prev; + } + poplex.lex = true; + // The 'lex' flag on these actions is used by the 'next' function + // to know they can (and have to) be ran before moving on to the + // next token. + + // Creates an action that discards tokens until it finds one of + // the given type. + function expect(wanted){ + return function expecting(type){ + if (type == wanted) cont(); + else cont(arguments.callee); + }; + } + + // Looks for a statement, and then calls itself. +/* function statements(type){ + return pass(statement, statements); + }*/ + // Dispatches various types of statements based on the type of the + // current token. + var lastToken = null; + var shown = 0; + + function statement(token){ + /* + if(shown > 100 && shown < 200) { + console.log('statement', token); + }*/ + shown += 1; + if (token.content == "begin" || token.content == "class" || token.content == "module") { + lastToken = token; + pushcontext(); + cont(statement); + return; + } + /* + if (token.content == "do") + { + if (!context.block) { + context.block = 0; + } + context.block += 1 + } + + if (token.style == KEWORDCLASS && token.content == "end") { + if (context.block && context.block > 0) { + context.block -= 1; + } else{ + popcontext(); + } + } + */ + + if (token.style == INSTANCEMETHODCALLCLASS) { + lastVar = token; + } + if (token.content == '|' && (lastToken.content == "do" || lastToken.content == "{")) { + lastToken = token; + //console.log('after 2', context); + mark(NORMALCONTEXT); + cont(blockparams); + return; + //cont(statement); + } + + if (token.content == "def") { + pushcontext(); + cont(functiondef); + lastToken = token; + return; + } + if (token.style == 'rb-method' || token.style == 'rb-variable') { + //console.log('is "'+token.content+'" registered?'); + if (isRegistered(token.content)) { + mark(registeredMark(token.content)); + } + } + + if (token.style == VARIABLECLASS) { + register(token.content, VARIABLECLASS); + } + + // long comment support + if (lastToken && lastToken.content == "\n") { + if (token.content == '=begin') { + cont(longccomment); + lastToken = token; + return; + } + } + + lastToken = token; + cont(statement); + } + + function longccomment(token, value) { + if (token.style == 'whitespace') { + token.style += ' '+WHITESPACEINLONGCOMMENTCLASS; + } else { + token.style = LONGCOMMENTCLASS; + } + if (lastToken && lastToken.content == "\n") { + if (token.content == 'end') { + cont(statement); + return; + } + } + lastToken = token; + cont(longccomment); + } + + function blockparams(token, value) { + //console.log('blockparams', token); + if (token.style == 'rb-method' || token.style == 'rb-variable') { + mark(METHODPARAMCLASS); + register(token.value, METHODPARAMCLASS); + } + if (value != '|' && value != "\n") { + cont(blockparams); + } else { + if (token.content == '|') { + token.style = NORMALCONTEXT; + } + // return to statement + cont(statement); + //console.log('returning to statement'); + } + lastToken = token; + } + + // A function definition creates a new context, and the variables + // in its argument list have to be added to this context. + function functiondef(token, value){ + if (token.style == 'rb-method' || token.style == 'rb-variable') { + register(value, METHODPARAMCLASS); + mark(METHODPARAMCLASS); + cont(functiondef); + } + else if (value == "\n") { + //console.log('returning to statement'); + cont(statement); + } else { + cont(functiondef); + } + } + + function longComment(token, value) { + // TODO + } + + function funarg(type, value){ + if (type == "variable"){register(value); cont();} + } + + // Look for statements until a closing brace is found. + function block(type){ + if (type == "}") cont(); + else pass(statement, block); + } + + + return parser; + } + + return {make: parseRuby, electricChars: "{}:"}; +})(); + diff --git a/Hosts/Silverlight/Iron7/Resources/ruby.js b/Hosts/Silverlight/Iron7/Resources/ruby.js new file mode 100644 index 0000000000..2049cf50e6 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Resources/ruby.js @@ -0,0 +1,68 @@ +/** + * Ruby syntax v 1.0 + * + * v1.0 by Patrice De Saint Steban (2007/01/03) + * +**/ +editAreaLoader.load_syntax["ruby"] = { + 'DISPLAY_NAME' : 'Ruby' + ,'COMMENT_SINGLE' : {1 : '#'} + ,'COMMENT_MULTI' : {} + ,'QUOTEMARKS' : {1: "'", 2: '"'} + ,'KEYWORD_CASE_SENSITIVE' : true + ,'KEYWORDS' : { + 'reserved' : [ + 'alias', 'and', 'BEGIN', 'begin', 'break', 'case', 'class', 'def', 'defined', 'do', 'else', + 'elsif', 'END', 'end', 'ensure', 'false', 'for', 'if', + 'in', 'module', 'next', 'not', 'or', 'redo', 'rescue', 'retry', + 'return', 'self', 'super', 'then', 'true', 'undef', 'unless', 'until', 'when', 'while', 'yield' + ] + } + ,'OPERATORS' :[ + '+', '-', '/', '*', '=', '<', '>', '%', '!', '&', ';', '?', '`', ':', ',' + ] + ,'DELIMITERS' :[ + '(', ')', '[', ']', '{', '}' + ] + ,'REGEXPS' : { + 'constants' : { + 'search' : '()([A-Z]\\w*)()' + ,'class' : 'constants' + ,'modifiers' : 'g' + ,'execute' : 'before' + } + ,'variables' : { + 'search' : '()([\$\@\%]+\\w+)()' + ,'class' : 'variables' + ,'modifiers' : 'g' + ,'execute' : 'before' + } + ,'numbers' : { + 'search' : '()(-?[0-9]+)()' + ,'class' : 'numbers' + ,'modifiers' : 'g' + ,'execute' : 'before' + } + ,'symbols' : { + 'search' : '()(:\\w+)()' + ,'class' : 'symbols' + ,'modifiers' : 'g' + ,'execute' : 'before' + } + } + ,'STYLES' : { + 'COMMENTS': 'color: #AAAAAA;' + ,'QUOTESMARKS': 'color: #660066;' + ,'KEYWORDS' : { + 'reserved' : 'font-weight: bold; color: #0000FF;' + } + ,'OPERATORS' : 'color: #993300;' + ,'DELIMITERS' : 'color: #993300;' + ,'REGEXPS' : { + 'variables' : 'color: #E0BD54;' + ,'numbers' : 'color: green;' + ,'constants' : 'color: #00AA00;' + ,'symbols' : 'color: #879EFA;' + } + } +}; diff --git a/Hosts/Silverlight/Iron7/Resources/rubycolors_css.txt b/Hosts/Silverlight/Iron7/Resources/rubycolors_css.txt new file mode 100644 index 0000000000..fdccd6268a --- /dev/null +++ b/Hosts/Silverlight/Iron7/Resources/rubycolors_css.txt @@ -0,0 +1,175 @@ +.editbox { + padding: .4em; + margin: 0; + font-family: consolas, monospace; + font-size: 12pt; + line-height: 1.1em; +} + +.editbox .rb-linenum { + padding:0.1em 1em 0.2em 0; + width:75px; +} + +.editbox .rb-pan { + padding-bottom:0.1em; + padding-top:0.2em; +} + +.editbox .rb-comment, .editbox .rb-long-comment { + font-style:italic; +} + +.editbox .rb-keyword, .editbox .rb-operator { + font-weight:bold; +} + +.editbox .rb-long-comment-whitespace {} + +.editbox .rb-global-variable { + color:#61B2DF; + font-style:italic; +} + + +/* dark-specific */ +.editbox-dark .rb-comment, .editbox .rb-long-comment { + color:#AEAEAE; +} + +.editbox-dark .rb-method-parameter { + color:#8AA6C1; +} + +.editbox-dark { + background-color: #000000; + color:#ffffff; +} + +.editbox-dark .rb-linenum { + background-color:#EEEEEE; + color:#888888; +} + +.editbox-dark .rb-variable, .editbox-dark .rb-method-call { + color:#f0f0f0; +} + +.editbox-dark .rb-symbol, .editbox-dark .rb-fixnum, .editbox-dark .rb-ascii { + color:#6FB1FF; +} +.editbox-dark .rb-hexnum, .editbox-dark .rb-binary { + color:#DFC745; +} +.editbox-dark .rb-float { + color:#7FDF61; +} +.editbox-dark .rb-entity { + color:#89BDFF; +} +.editbox-dark .rb-keyword, .editbox-dark .rb-operator { + color:#CE864B; +} +.editbox-dark .rb-storage { + color:#99CF50; +} +.editbox-dark .rb-string, .editbox-dark .rb-heredoc, .editbox-dark .rb-exec { + color:#65B042; +} +.editbox-dark .rb-class, .editbox-dark .rb-constant { + color:#9B859D; +} +.editbox-dark .rb-instance-var { + color:#61B2DF; +} +.editbox-dark .rb-global-variable { + color:#61B2DF; +} +.editbox-dark .rb-class-var { + color:#8BC9DF; +} + +.editbox-dark .rb-string .constant { + color:#DDF2A4; +} + +.editbox-dark .rb-regexp { + color:#DF5045; +} +.editbox-dark .rb-method { + color:#DAD085; +} + +/* light-specific */ +.editbox-light +{ + font-weight:bold; +} +.editbox-light .rb-comment, .editbox-light .rb-long-comment { + color:#006600; +} + +.editbox-light .rb-method-parameter { + color:#000066; +} + +.editbox-light { + background-color:#ffffff; + color:#000000; +} + +.editbox-light .rb-linenum { + background-color:#111111; + color:#888888; +} + +.editbox-light .rb-variable, .editbox-light .rb-method-call { + color:#000066; +} + +.editbox-light .rb-symbol, .editbox-light .rb-ascii { + color:#000066; +} + +.editbox-light .rb-hexnum, .editbox-light .rb-binary, .editbox-light .rb-fixnum { + color:#990000; +} +.editbox-light .rb-float { + color:#990000; +} + +.editbox-light .rb-entity { + color:#FF66FF; +} +.editbox-light .rb-keyword, .editbox-light .rb-operator { + color:#660099; +} +.editbox-light .rb-storage { + color:#000066; +} +.editbox-light .rb-string, .editbox-light .rb-heredoc, .editbox-light .rb-exec { + color:#990000; +} +.editbox-light .rb-class, .editbox-light .rb-constant { + color:#660000; +} +.editbox-light .rb-instance-var { + color:#660000; +} +.editbox-light .rb-global-variable { + color:#21007F; +} +.editbox-light .rb-class-var { + color:#660000; +} + +.editbox-light .rb-string .constant { + color:#990000; +} + +.editbox-light .rb-regexp { + color:#660000; +} +.editbox-light .rb-method { + color:#660000; +} diff --git a/Hosts/Silverlight/Iron7/Resources/select_js.txt b/Hosts/Silverlight/Iron7/Resources/select_js.txt new file mode 100644 index 0000000000..efd83a4be9 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Resources/select_js.txt @@ -0,0 +1,619 @@ +/* Functionality for finding, storing, and restoring selections + * + * This does not provide a generic API, just the minimal functionality + * required by the CodeMirror system. + */ + +// Namespace object. +var select = {}; + +(function() { + select.ie_selection = document.selection && document.selection.createRangeCollection; + + // Find the 'top-level' (defined as 'a direct child of the node + // passed as the top argument') node that the given node is + // contained in. Return null if the given node is not inside the top + // node. + function topLevelNodeAt(node, top) { + while (node && node.parentNode != top) + node = node.parentNode; + return node; + } + + // Find the top-level node that contains the node before this one. + function topLevelNodeBefore(node, top) { + while (!node.previousSibling && node.parentNode != top) + node = node.parentNode; + return topLevelNodeAt(node.previousSibling, top); + } + + var fourSpaces = "\u00a0\u00a0\u00a0\u00a0"; + + select.scrollToNode = function(element) { + if (!element) return; + var doc = element.ownerDocument, body = doc.body, + win = (doc.defaultView || doc.parentWindow), + html = doc.documentElement, + atEnd = !element.nextSibling || !element.nextSibling.nextSibling + || !element.nextSibling.nextSibling.nextSibling; + // In Opera (and recent Webkit versions), BR elements *always* + // have a offsetTop property of zero. + var compensateHack = 0; + while (element && !element.offsetTop) { + compensateHack++; + element = element.previousSibling; + } + // atEnd is another kludge for these browsers -- if the cursor is + // at the end of the document, and the node doesn't have an + // offset, just scroll to the end. + if (compensateHack == 0) atEnd = false; + + var y = compensateHack * (element ? element.offsetHeight : 0), x = 0, pos = element; + while (pos && pos.offsetParent) { + y += pos.offsetTop; + // Don't count X offset for
nodes + if (!isBR(pos)) + x += pos.offsetLeft; + pos = pos.offsetParent; + } + + var scroll_x = body.scrollLeft || html.scrollLeft || 0, + scroll_y = body.scrollTop || html.scrollTop || 0, + screen_x = x - scroll_x, screen_y = y - scroll_y, scroll = false; + + if (screen_x < 0 || screen_x > (win.innerWidth || html.clientWidth || 0)) { + scroll_x = x; + scroll = true; + } + if (screen_y < 0 || atEnd || screen_y > (win.innerHeight || html.clientHeight || 0) - 50) { + scroll_y = atEnd ? 1e6 : y; + scroll = true; + } + if (scroll) win.scrollTo(scroll_x, scroll_y); + }; + + select.scrollToCursor = function(container) { + select.scrollToNode(select.selectionTopNode(container, true) || container.firstChild); + }; + + // Used to prevent restoring a selection when we do not need to. + var currentSelection = null; + + select.snapshotChanged = function() { + if (currentSelection) currentSelection.changed = true; + }; + + // This is called by the code in editor.js whenever it is replacing + // a text node. The function sees whether the given oldNode is part + // of the current selection, and updates this selection if it is. + // Because nodes are often only partially replaced, the length of + // the part that gets replaced has to be taken into account -- the + // selection might stay in the oldNode if the newNode is smaller + // than the selection's offset. The offset argument is needed in + // case the selection does move to the new object, and the given + // length is not the whole length of the new node (part of it might + // have been used to replace another node). + select.snapshotReplaceNode = function(from, to, length, offset) { + if (!currentSelection) return; + + function replace(point) { + if (from == point.node) { + currentSelection.changed = true; + if (length && point.offset > length) { + point.offset -= length; + } + else { + point.node = to; + point.offset += (offset || 0); + } + } + } + replace(currentSelection.start); + replace(currentSelection.end); + }; + + select.snapshotMove = function(from, to, distance, relative, ifAtStart) { + if (!currentSelection) return; + + function move(point) { + if (from == point.node && (!ifAtStart || point.offset == 0)) { + currentSelection.changed = true; + point.node = to; + if (relative) point.offset = Math.max(0, point.offset + distance); + else point.offset = distance; + } + } + move(currentSelection.start); + move(currentSelection.end); + }; + + // Most functions are defined in two ways, one for the IE selection + // model, one for the W3C one. + if (select.ie_selection) { + function selectionNode(win, start) { + var range = win.document.selection.createRange(); + range.collapse(start); + + function nodeAfter(node) { + var found = null; + while (!found && node) { + found = node.nextSibling; + node = node.parentNode; + } + return nodeAtStartOf(found); + } + + function nodeAtStartOf(node) { + while (node && node.firstChild) node = node.firstChild; + return {node: node, offset: 0}; + } + + var containing = range.parentElement(); + if (!isAncestor(win.document.body, containing)) return null; + if (!containing.firstChild) return nodeAtStartOf(containing); + + var working = range.duplicate(); + working.moveToElementText(containing); + working.collapse(true); + for (var cur = containing.firstChild; cur; cur = cur.nextSibling) { + if (cur.nodeType == 3) { + var size = cur.nodeValue.length; + working.move("character", size); + } + else { + working.moveToElementText(cur); + working.collapse(false); + } + + var dir = range.compareEndPoints("StartToStart", working); + if (dir == 0) return nodeAfter(cur); + if (dir == 1) continue; + if (cur.nodeType != 3) return nodeAtStartOf(cur); + + working.setEndPoint("StartToEnd", range); + return {node: cur, offset: size - working.text.length}; + } + return nodeAfter(containing); + } + + select.markSelection = function(win) { + currentSelection = null; + var sel = win.document.selection; + if (!sel) return; + var start = selectionNode(win, true), + end = selectionNode(win, false); + if (!start || !end) return; + currentSelection = {start: start, end: end, window: win, changed: false}; + }; + + select.selectMarked = function() { + if (!currentSelection || !currentSelection.changed) return; + var win = currentSelection.window, doc = win.document; + + function makeRange(point) { + var range = doc.body.createTextRange(), + node = point.node; + if (!node) { + range.moveToElementText(currentSelection.window.document.body); + range.collapse(false); + } + else if (node.nodeType == 3) { + range.moveToElementText(node.parentNode); + var offset = point.offset; + while (node.previousSibling) { + node = node.previousSibling; + offset += (node.innerText || "").length; + } + range.move("character", offset); + } + else { + range.moveToElementText(node); + range.collapse(true); + } + return range; + } + + var start = makeRange(currentSelection.start), end = makeRange(currentSelection.end); + start.setEndPoint("StartToEnd", end); + start.select(); + }; + + // Get the top-level node that one end of the cursor is inside or + // after. Note that this returns false for 'no cursor', and null + // for 'start of document'. + select.selectionTopNode = function(container, start) { + var selection = container.ownerDocument.selection; + if (!selection) return false; + + var range = selection.createRange(), range2 = range.duplicate(); + range.collapse(start); + var around = range.parentElement(); + if (around && isAncestor(container, around)) { + // Only use this node if the selection is not at its start. + range2.moveToElementText(around); + if (range.compareEndPoints("StartToStart", range2) == 1) + return topLevelNodeAt(around, container); + } + + // Move the start of a range to the start of a node, + // compensating for the fact that you can't call + // moveToElementText with text nodes. + function moveToNodeStart(range, node) { + if (node.nodeType == 3) { + var count = 0, cur = node.previousSibling; + while (cur && cur.nodeType == 3) { + count += cur.nodeValue.length; + cur = cur.previousSibling; + } + if (cur) { + try{range.moveToElementText(cur);} + catch(e){return false;} + range.collapse(false); + } + else range.moveToElementText(node.parentNode); + if (count) range.move("character", count); + } + else { + try{range.moveToElementText(node);} + catch(e){return false;} + } + return true; + } + + // Do a binary search through the container object, comparing + // the start of each node to the selection + var start = 0, end = container.childNodes.length - 1; + while (start < end) { + var middle = Math.ceil((end + start) / 2), node = container.childNodes[middle]; + if (!node) return false; // Don't ask. IE6 manages this sometimes. + if (!moveToNodeStart(range2, node)) return false; + if (range.compareEndPoints("StartToStart", range2) == 1) + start = middle; + else + end = middle - 1; + } + return container.childNodes[start] || null; + }; + + // Place the cursor after this.start. This is only useful when + // manually moving the cursor instead of restoring it to its old + // position. + select.focusAfterNode = function(node, container) { + var range = container.ownerDocument.body.createTextRange(); + range.moveToElementText(node || container); + range.collapse(!node); + range.select(); + }; + + select.somethingSelected = function(win) { + var sel = win.document.selection; + return sel && (sel.createRange().text != ""); + }; + + function insertAtCursor(window, html) { + var selection = window.document.selection; + if (selection) { + var range = selection.createRange(); + range.pasteHTML(html); + range.collapse(false); + range.select(); + } + } + + // Used to normalize the effect of the enter key, since browsers + // do widely different things when pressing enter in designMode. + select.insertNewlineAtCursor = function(window) { + insertAtCursor(window, "
"); + }; + + select.insertTabAtCursor = function(window) { + insertAtCursor(window, fourSpaces); + }; + + // Get the BR node at the start of the line on which the cursor + // currently is, and the offset into the line. Returns null as + // node if cursor is on first line. + select.cursorPos = function(container, start) { + var selection = container.ownerDocument.selection; + if (!selection) return null; + + var topNode = select.selectionTopNode(container, start); + while (topNode && !isBR(topNode)) + topNode = topNode.previousSibling; + + var range = selection.createRange(), range2 = range.duplicate(); + range.collapse(start); + if (topNode) { + range2.moveToElementText(topNode); + range2.collapse(false); + } + else { + // When nothing is selected, we can get all kinds of funky errors here. + try { range2.moveToElementText(container); } + catch (e) { return null; } + range2.collapse(true); + } + range.setEndPoint("StartToStart", range2); + + return {node: topNode, offset: range.text.length}; + }; + + select.setCursorPos = function(container, from, to) { + function rangeAt(pos) { + var range = container.ownerDocument.body.createTextRange(); + if (!pos.node) { + range.moveToElementText(container); + range.collapse(true); + } + else { + range.moveToElementText(pos.node); + range.collapse(false); + } + range.move("character", pos.offset); + return range; + } + + var range = rangeAt(from); + if (to && to != from) + range.setEndPoint("EndToEnd", rangeAt(to)); + range.select(); + } + + // Some hacks for storing and re-storing the selection when the editor loses and regains focus. + select.getBookmark = function (container) { + var from = select.cursorPos(container, true), to = select.cursorPos(container, false); + if (from && to) return {from: from, to: to}; + }; + + // Restore a stored selection. + select.setBookmark = function(container, mark) { + if (!mark) return; + select.setCursorPos(container, mark.from, mark.to); + }; + } + // W3C model + else { + // Store start and end nodes, and offsets within these, and refer + // back to the selection object from those nodes, so that this + // object can be updated when the nodes are replaced before the + // selection is restored. + select.markSelection = function (win) { + var selection = win.getSelection(); + if (!selection || selection.rangeCount == 0) + return (currentSelection = null); + var range = selection.getRangeAt(0); + + currentSelection = { + start: {node: range.startContainer, offset: range.startOffset}, + end: {node: range.endContainer, offset: range.endOffset}, + window: win, + changed: false + }; + + // We want the nodes right at the cursor, not one of their + // ancestors with a suitable offset. This goes down the DOM tree + // until a 'leaf' is reached (or is it *up* the DOM tree?). + function normalize(point){ + while (point.node.nodeType != 3 && !isBR(point.node)) { + var newNode = point.node.childNodes[point.offset] || point.node.nextSibling; + point.offset = 0; + while (!newNode && point.node.parentNode) { + point.node = point.node.parentNode; + newNode = point.node.nextSibling; + } + point.node = newNode; + if (!newNode) + break; + } + } + + normalize(currentSelection.start); + normalize(currentSelection.end); + }; + + select.selectMarked = function () { + var cs = currentSelection; + // on webkit-based browsers, it is apparently possible that the + // selection gets reset even when a node that is not one of the + // endpoints get messed with. the most common situation where + // this occurs is when a selection is deleted or overwitten. we + // check for that here. + function focusIssue() { + return cs.start.node == cs.end.node && cs.start.offset == 0 && cs.end.offset == 0; + } + if (!cs || !(cs.changed || (webkit && focusIssue()))) return; + var win = cs.window, range = win.document.createRange(); + + function setPoint(point, which) { + if (point.node) { + // Some magic to generalize the setting of the start and end + // of a range. + if (point.offset == 0) + range["set" + which + "Before"](point.node); + else + range["set" + which](point.node, point.offset); + } + else { + range.setStartAfter(win.document.body.lastChild || win.document.body); + } + } + + setPoint(cs.end, "End"); + setPoint(cs.start, "Start"); + selectRange(range, win); + }; + + // Helper for selecting a range object. + function selectRange(range, window) { + var selection = window.getSelection(); + selection.removeAllRanges(); + selection.addRange(range); + } + function selectionRange(window) { + var selection = window.getSelection(); + if (!selection || selection.rangeCount == 0) + return false; + else + return selection.getRangeAt(0); + } + + // Finding the top-level node at the cursor in the W3C is, as you + // can see, quite an involved process. + select.selectionTopNode = function(container, start) { + var range = selectionRange(container.ownerDocument.defaultView); + if (!range) return false; + + var node = start ? range.startContainer : range.endContainer; + var offset = start ? range.startOffset : range.endOffset; + // Work around (yet another) bug in Opera's selection model. + if (window.opera && !start && range.endContainer == container && range.endOffset == range.startOffset + 1 && + container.childNodes[range.startOffset] && isBR(container.childNodes[range.startOffset])) + offset--; + + // For text nodes, we look at the node itself if the cursor is + // inside, or at the node before it if the cursor is at the + // start. + if (node.nodeType == 3){ + if (offset > 0) + return topLevelNodeAt(node, container); + else + return topLevelNodeBefore(node, container); + } + // Occasionally, browsers will return the HTML node as + // selection. If the offset is 0, we take the start of the frame + // ('after null'), otherwise, we take the last node. + else if (node.nodeName.toUpperCase() == "HTML") { + return (offset == 1 ? null : container.lastChild); + } + // If the given node is our 'container', we just look up the + // correct node by using the offset. + else if (node == container) { + return (offset == 0) ? null : node.childNodes[offset - 1]; + } + // In any other case, we have a regular node. If the cursor is + // at the end of the node, we use the node itself, if it is at + // the start, we use the node before it, and in any other + // case, we look up the child before the cursor and use that. + else { + if (offset == node.childNodes.length) + return topLevelNodeAt(node, container); + else if (offset == 0) + return topLevelNodeBefore(node, container); + else + return topLevelNodeAt(node.childNodes[offset - 1], container); + } + }; + + select.focusAfterNode = function(node, container) { + var win = container.ownerDocument.defaultView, + range = win.document.createRange(); + range.setStartBefore(container.firstChild || container); + // In Opera, setting the end of a range at the end of a line + // (before a BR) will cause the cursor to appear on the next + // line, so we set the end inside of the start node when + // possible. + if (node && !node.firstChild) + range.setEndAfter(node); + else if (node) + range.setEnd(node, node.childNodes.length); + else + range.setEndBefore(container.firstChild || container); + range.collapse(false); + selectRange(range, win); + }; + + select.somethingSelected = function(win) { + var range = selectionRange(win); + return range && !range.collapsed; + }; + + function insertNodeAtCursor(window, node) { + var range = selectionRange(window); + if (!range) return; + + range.deleteContents(); + range.insertNode(node); + webkitLastLineHack(window.document.body); + range = window.document.createRange(); + range.selectNode(node); + range.collapse(false); + selectRange(range, window); + } + + select.insertNewlineAtCursor = function(window) { + insertNodeAtCursor(window, window.document.createElement("BR")); + }; + + select.insertTabAtCursor = function(window) { + insertNodeAtCursor(window, window.document.createTextNode(fourSpaces)); + }; + + select.cursorPos = function(container, start) { + var range = selectionRange(window); + if (!range) return; + + var topNode = select.selectionTopNode(container, start); + while (topNode && !isBR(topNode)) + topNode = topNode.previousSibling; + + range = range.cloneRange(); + range.collapse(start); + if (topNode) + range.setStartAfter(topNode); + else + range.setStartBefore(container); + return {node: topNode, offset: range.toString().length}; + }; + + select.setCursorPos = function(container, from, to) { + var win = container.ownerDocument.defaultView, + range = win.document.createRange(); + + function setPoint(node, offset, side) { + if (offset == 0 && node && !node.nextSibling) { + range["set" + side + "After"](node); + return true; + } + + if (!node) + node = container.firstChild; + else + node = node.nextSibling; + + if (!node) return; + + if (offset == 0) { + range["set" + side + "Before"](node); + return true; + } + + var backlog = [] + function decompose(node) { + if (node.nodeType == 3) + backlog.push(node); + else + forEach(node.childNodes, decompose); + } + while (true) { + while (node && !backlog.length) { + decompose(node); + node = node.nextSibling; + } + var cur = backlog.shift(); + if (!cur) return false; + + var length = cur.nodeValue.length; + if (length >= offset) { + range["set" + side](cur, offset); + return true; + } + offset -= length; + } + } + + to = to || from; + if (setPoint(to.node, to.offset, "End") && setPoint(from.node, from.offset, "Start")) + selectRange(range, win); + }; + } +})(); diff --git a/Hosts/Silverlight/Iron7/Resources/square_aliens.txt b/Hosts/Silverlight/Iron7/Resources/square_aliens.txt new file mode 100644 index 0000000000..9e3e948db5 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Resources/square_aliens.txt @@ -0,0 +1,166 @@ +# originally based on https://github.com/jschementi/ironruby-rubyinside-article/blob/master/ruby-squares.html +# this script uses a timer for simple animation +# the alien images used come royalty free from clker.com +# sounds from http://www.freesound.org/samplesViewSingle.php?id=18397 +# and from http://www.freesound.org/samplesViewSingle.php?id=76966 +# all used under creative commons licensing + +include System +include System::Windows +include System::Windows::Shapes +include System::Windows::Media +include System::Windows::Media::Imaging +include System::Windows::Controls + +def create_canvas(color, opacity) + canvas = Canvas.new + canvas.width = 480 + canvas.height = 800 + canvas.background = SolidColorBrush.new(color) unless color.nil? + canvas.opacity = opacity unless opacity.nil? + return canvas +end + +def load_sound_effect(name, callback) + Host.load_sound_effect(name, "http://iron7.com/forapp/aliens/" + name + ".wav", callback) +end + +def setup + $speed = 21 + background_canvas = create_canvas(Colors.black, 0.001) + Host.content_holder.children.add(background_canvas) + $canvas = create_canvas(nil, nil) + Host.content_holder.children.add($canvas) + $laser_sound = nil; + $death_sound = nil; + Host.fix_orientation_portrait + Host.start_timer("timer1", TimeSpan.from_seconds(0.05), "animate") + load_sound_effect("las16", "laser_loaded") + load_sound_effect("death", "death_loaded") + + Host.monitor_control(background_canvas, background_canvas, "alien_missed") + new_game +end + +def death_loaded + if Calling_event == "sound_effect_loaded" + $death_sound = Calling_sender + end +end + +def laser_loaded + if Calling_event == "sound_effect_loaded" + $laser_sound = Calling_sender + end +end + +def new_game + add_aliens(10) +end + +def next_level + $speed = $speed + 10 +end + +def add_aliens(count) + count.times do |i| + my_alien(15) + end +end + +def generate_speed + rand($speed) - $speed/2 +end + +def my_alien(length) + size = rand(480 / length) + 60 + left = rand(480 - size) + top = rand(800 - size) + speed_x = generate_speed + speed_y = generate_speed + image_index = rand(10) + 1 + image_url = Uri.new("http://iron7.com/forapp/aliens/" << image_index.to_s << ".png") + image_bitmap = BitmapImage.new(image_url) + alien = Image.new + alien.source = image_bitmap + alien.width = size + alien.height = size + alien_border = Border.new + alien_border.border_brush = SolidColorBrush.new(Colors.red) + alien_border.border_thickness = Thickness.new(1) + alien_border.tag = { "left"=>left, "top"=>top, "x"=>speed_x, "y"=>speed_y } + alien_border.margin = Thickness.new(left,top,0,0) + alien_border.child = alien + alien.tag = alien_border + Host.monitor_control(alien,alien,"alien_event") + $canvas.children.add(alien_border) +end + +def test_for_next_level + if $canvas.children.count == 0 + MessageBox.show("level complete... next level gets faster") + next_level + new_game + end +end + +def any_shot + $laser_sound.play unless $laser_sound.nil? +end + +def alien_missed + any_shot +end + +def alien_event + alien = Calling_sender + if Calling_event == "mouse_left_button_down" + alien_pressed(alien) + elsif Calling_event == "image_opened" + alien.tag.border_thickness = Thickness.new(0) + end +end + +def alien_pressed(alien) + $canvas.children.remove(alien.tag) + any_shot + $death_sound.play unless $death_sound.nil? + Host.vibrate(TimeSpan.from_milliseconds(10)) + test_for_next_level +end + +def brushes + $brushes ||= %W(red orange yellow cyan blue magenta green).map do |c| + SolidColorBrush.new(Colors.send(c)) + end +end + +def rand_brush + brushes[rand(brushes.size)] +end + +def animate_tagged_child(tagged) + left = tagged.tag["left"] + top = tagged.tag["top"] + x = tagged.tag["x"] + y = tagged.tag["y"] + + left = left + x + if left > 480 - tagged.actual_width || left < 0 + x = -x + end + top = top + y + if top > 800- tagged.actual_height || top < 0 + y = -y + end + tagged.margin = Thickness.new(left,top,0,0) + tagged.tag = { "left"=>left, "top"=>top, "x"=>x, "y"=>y } +end + +def animate + $canvas.children.each { |child| + animate_tagged_child(child) + } +end + +setup \ No newline at end of file diff --git a/Hosts/Silverlight/Iron7/Resources/square_animating.txt b/Hosts/Silverlight/Iron7/Resources/square_animating.txt new file mode 100644 index 0000000000..4df114c850 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Resources/square_animating.txt @@ -0,0 +1,72 @@ +# originally based on https://github.com/jschementi/ironruby-rubyinside-article/blob/master/ruby-squares.html +# this script uses a timer for simple animation +# there are smoother animation techniques for WP7 + +include System +include System::Windows +include System::Windows::Shapes +include System::Windows::Media +include System::Windows::Controls + +def setup + $canvas = Canvas.new + add_squares(10) + Host.content_holder.children.add($canvas) + Host.fix_orientation_portrait + Host.start_timer("timer1", TimeSpan.from_seconds(0.05), "animate") +end + +def add_squares(count) + count.times do |i| + my_square(15) + end +end + +def my_square(length) + size = rand(480 / length) + 20 + left = rand(480 - size) + top = rand(800 - size) + speed_x = rand(31) - 15 + speed_y = rand(31) - 15 + rect = Rectangle.new + rect.width = size + rect.height = size + rect.fill = rand_brush + rect.stroke = rand_brush + rect.stroke_thickness = rand(480 / (length * 7)) + 4 + rect.margin = Thickness.new(left,top,0,0) + rect.tag = { "left"=>left, "top"=>top, "x"=>speed_x, "y"=>speed_y } + $canvas.children.add(rect) +end + +def brushes + $brushes ||= %W(red orange yellow cyan blue magenta green).map do |c| + SolidColorBrush.new(Colors.send(c)) + end +end + +def rand_brush + brushes[rand(brushes.size)] +end + +def animate + $canvas.children.each { |rect| + left = rect.tag["left"] + top = rect.tag["top"] + x = rect.tag["x"] + y = rect.tag["y"] + + left = left + x + if left > 480 - rect.width || left < 0 + x = -x + end + top = top + y + if top > 800- rect.height || top < 0 + y = -y + end + rect.margin = Thickness.new(left,top,0,0) + rect.tag = { "left"=>left, "top"=>top, "x"=>x, "y"=>y } + } +end + +setup \ No newline at end of file diff --git a/Hosts/Silverlight/Iron7/Resources/square_animating_game.txt b/Hosts/Silverlight/Iron7/Resources/square_animating_game.txt new file mode 100644 index 0000000000..4b9b8d74ee --- /dev/null +++ b/Hosts/Silverlight/Iron7/Resources/square_animating_game.txt @@ -0,0 +1,102 @@ +# originally based on https://github.com/jschementi/ironruby-rubyinside-article/blob/master/ruby-squares.html +# this script uses a timer for simple animation +# there are smoother animation techniques for WP7 + +include System +include System::Windows +include System::Windows::Shapes +include System::Windows::Media +include System::Windows::Controls + +def setup + $speed = 21 + $canvas = Canvas.new + Host.content_holder.children.add($canvas) + Host.fix_orientation_portrait + Host.start_timer("timer1", TimeSpan.from_seconds(0.05), "animate") + new_game +end + +def new_game + add_squares(10) +end + +def next_level + $speed = $speed + 10 +end + +def add_squares(count) + count.times do |i| + my_square(15) + end +end + +def my_square(length) + size = rand(480 / length) + 60 + left = rand(480 - size) + top = rand(800 - size) + speed_x = rand($speed) - $speed/2 + speed_y = rand($speed) - $speed/2 + rect = Rectangle.new + rect.width = size + rect.height = size + rect.fill = rand_brush + rect.stroke = rand_brush + rect.stroke_thickness = rand(480 / (length * 7)) + 4 + rect.margin = Thickness.new(left,top,0,0) + rect.tag = { "left"=>left, "top"=>top, "x"=>speed_x, "y"=>speed_y } + Host.monitor_control(rect,rect,"square_pressed") + $canvas.children.add(rect) +end + +def test_for_next_level + if $canvas.children.count == 0 + MessageBox.show("level complete... next level gets faster") + next_level + new_game + end +end + +def square_pressed + if Calling_event == "mouse_left_button_down" + $canvas.children.remove(Calling_sender) + Host.vibrate(TimeSpan.from_milliseconds(10)) + test_for_next_level + end +end + +def brushes + $brushes ||= %W(red orange yellow cyan blue magenta green).map do |c| + SolidColorBrush.new(Colors.send(c)) + end +end + +def rand_brush + brushes[rand(brushes.size)] +end + +def animate_rect(rect) + left = rect.tag["left"] + top = rect.tag["top"] + x = rect.tag["x"] + y = rect.tag["y"] + + left = left + x + if left > 480 - rect.width || left < 0 + x = -x + end + top = top + y + if top > 800- rect.height || top < 0 + y = -y + end + rect.margin = Thickness.new(left,top,0,0) + rect.tag = { "left"=>left, "top"=>top, "x"=>x, "y"=>y } +end + +def animate + $canvas.children.each { |rect| + animate_rect(rect) + } +end + +setup \ No newline at end of file diff --git a/Hosts/Silverlight/Iron7/Resources/square_drawing.txt b/Hosts/Silverlight/Iron7/Resources/square_drawing.txt new file mode 100644 index 0000000000..0eb4654521 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Resources/square_drawing.txt @@ -0,0 +1,51 @@ +# originally based on https://github.com/jschementi/ironruby-rubyinside-article/blob/master/ruby-squares.html + +include System +include System::Windows +include System::Windows::Shapes +include System::Windows::Media +include System::Windows::Controls + +def setup + $canvas = Canvas.new + Host.content_holder.children.add($canvas) + Host.fix_orientation_portrait + Host.start_timer("timer1", TimeSpan.from_seconds(0.1), "add_squares") +end + +def add_squares + if $canvas.children.count > 500 + Host.stop_timer("timer1") + return + end + 5.times do |i| + my_square(15) + end +end + +def my_square(length) + size = rand(480 / length) + left = rand(480 - size) + top = rand(800 - size) + rect = Rectangle.new + rect.width = size + rect.height = size + rect.fill = rand_brush + rect.stroke = rand_brush + rect.stroke_thickness = rand(480 / (length * 7)) + 4 + rect.margin = Thickness.new(left,top,0,0) + rect.tag = { "left"=>left, "top"=>top, "x"=>rand(3)-1, "y"=>rand(3)-1 } + $canvas.children.add(rect) +end + +def brushes + $brushes ||= %W(red orange yellow cyan blue magenta green).map do |c| + SolidColorBrush.new(Colors.send(c)) + end +end + +def rand_brush + brushes[rand(brushes.size)] +end + +setup \ No newline at end of file diff --git a/Hosts/Silverlight/Iron7/Resources/stringstream_js.txt b/Hosts/Silverlight/Iron7/Resources/stringstream_js.txt new file mode 100644 index 0000000000..4f5bc61156 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Resources/stringstream_js.txt @@ -0,0 +1,140 @@ +/* String streams are the things fed to parsers (which can feed them + * to a tokenizer if they want). They provide peek and next methods + * for looking at the current character (next 'consumes' this + * character, peek does not), and a get method for retrieving all the + * text that was consumed since the last time get was called. + * + * An easy mistake to make is to let a StopIteration exception finish + * the token stream while there are still characters pending in the + * string stream (hitting the end of the buffer while parsing a + * token). To make it easier to detect such errors, the stringstreams + * throw an exception when this happens. + */ + +// Make a stringstream stream out of an iterator that returns strings. +// This is applied to the result of traverseDOM (see codemirror.js), +// and the resulting stream is fed to the parser. +var stringStream = function(source){ + // String that's currently being iterated over. + var current = ""; + // Position in that string. + var pos = 0; + // Accumulator for strings that have been iterated over but not + // get()-ed yet. + var accum = ""; + // Make sure there are more characters ready, or throw + // StopIteration. + function ensureChars() { + while (pos == current.length) { + accum += current; + current = ""; // In case source.next() throws + pos = 0; + try {current = source.next();} + catch (e) { + if (e != StopIteration) throw e; + else return false; + } + } + return true; + } + + return { + // Return the next character in the stream. + peek: function() { + if (!ensureChars()) return null; + return current.charAt(pos); + }, + // Get the next character, throw StopIteration if at end, check + // for unused content. + next: function() { + if (!ensureChars()) { + if (accum.length > 0) + throw "End of stringstream reached without emptying buffer ('" + accum + "')."; + else + throw StopIteration; + } + return current.charAt(pos++); + }, + // Return the characters iterated over since the last call to + // .get(). + get: function() { + var temp = accum; + accum = ""; + if (pos > 0){ + temp += current.slice(0, pos); + current = current.slice(pos); + pos = 0; + } + return temp; + }, + // Push a string back into the stream. + push: function(str) { + current = current.slice(0, pos) + str + current.slice(pos); + }, + lookAhead: function(str, consume, skipSpaces, caseInsensitive) { + function cased(str) {return caseInsensitive ? str.toLowerCase() : str;} + str = cased(str); + var found = false; + + var _accum = accum, _pos = pos; + if (skipSpaces) this.nextWhileMatches(/[\s\u00a0]/); + + while (true) { + var end = pos + str.length, left = current.length - pos; + if (end <= current.length) { + found = str == cased(current.slice(pos, end)); + pos = end; + break; + } + else if (str.slice(0, left) == cased(current.slice(pos))) { + accum += current; current = ""; + try {current = source.next();} + catch (e) {break;} + pos = 0; + str = str.slice(left); + } + else { + break; + } + } + + if (!(found && consume)) { + current = accum.slice(_accum.length) + current; + pos = _pos; + accum = _accum; + } + + return found; + }, + + // Utils built on top of the above + more: function() { + return this.peek() !== null; + }, + applies: function(test) { + var next = this.peek(); + return (next !== null && test(next)); + }, + nextWhile: function(test) { + var next; + while ((next = this.peek()) !== null && test(next)) + this.next(); + }, + matches: function(re) { + var next = this.peek(); + return (next !== null && re.test(next)); + }, + nextWhileMatches: function(re) { + var next; + while ((next = this.peek()) !== null && re.test(next)) + this.next(); + }, + equals: function(ch) { + return ch === this.peek(); + }, + endOfLine: function() { + var next = this.peek(); + return next == null || next == "\n"; + } + }; +}; diff --git a/Hosts/Silverlight/Iron7/Resources/tokenize_js.txt b/Hosts/Silverlight/Iron7/Resources/tokenize_js.txt new file mode 100644 index 0000000000..071970ce3d --- /dev/null +++ b/Hosts/Silverlight/Iron7/Resources/tokenize_js.txt @@ -0,0 +1,57 @@ +// A framework for simple tokenizers. Takes care of newlines and +// white-space, and of getting the text from the source stream into +// the token object. A state is a function of two arguments -- a +// string stream and a setState function. The second can be used to +// change the tokenizer's state, and can be ignored for stateless +// tokenizers. This function should advance the stream over a token +// and return a string or object containing information about the next +// token, or null to pass and have the (new) state be called to finish +// the token. When a string is given, it is wrapped in a {style, type} +// object. In the resulting object, the characters consumed are stored +// under the content property. Any whitespace following them is also +// automatically consumed, and added to the value property. (Thus, +// content is the actual meaningful part of the token, while value +// contains all the text it spans.) + +function tokenizer(source, state) { + // Newlines are always a separate token. + function isWhiteSpace(ch) { + // The messy regexp is because IE's regexp matcher is of the + // opinion that non-breaking spaces are no whitespace. + return ch != "\n" && /^[\s\u00a0]*$/.test(ch); + } + + var tokenizer = { + state: state, + + take: function(type) { + if (typeof(type) == "string") + type = {style: type, type: type}; + + type.content = (type.content || "") + source.get(); + if (!/\n$/.test(type.content)) + source.nextWhile(isWhiteSpace); + type.value = type.content + source.get(); + return type; + }, + + next: function () { + if (!source.more()) throw StopIteration; + + var type; + if (source.equals("\n")) { + source.next(); + return this.take("whitespace"); + } + + if (source.applies(isWhiteSpace)) + type = "whitespace"; + else + while (!type) + type = this.state(source, function(s) {tokenizer.state = s;}); + + return this.take(type); + } + }; + return tokenizer; +} diff --git a/Hosts/Silverlight/Iron7/Resources/tokenizeruby_js.txt b/Hosts/Silverlight/Iron7/Resources/tokenizeruby_js.txt new file mode 100644 index 0000000000..3a5f71c523 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Resources/tokenizeruby_js.txt @@ -0,0 +1,475 @@ + +var tokenizeRuby = (function() { + + function buildProgress(word) { + var progress = {}, current; + for (var i in word) { + current = progress; + for (var ch in word[i]) { + if(!current[word[i][ch]]) current[word[i][ch]] = {}; + current = current[word[i][ch]]; + } + } + return progress; + } + + + function wordRegexp(words) { + return new RegExp("^(?:" + words.join("|") + ")$"); + } + var NORMALCONTEXT = 'rb-normal'; + var ERRORCLASS = 'rb-error'; + var COMMENTCLASS = 'rb-comment'; + var SYMBOLCLASS = 'rb-symbol'; + var CONSTCLASS = 'rb-constant'; + var OPCLASS = 'rb-operator'; + var INSTANCEMETHODCALLCLASS = 'rb-method' + var VARIABLECLASS = 'rb-variable'; + var STRINGCLASS = 'rb-string'; + var FIXNUMCLASS = 'rb-fixnum rb-numeric'; + var METHODCALLCLASS = 'rb-method-call'; + var HEREDOCCLASS = 'rb-heredoc'; + var ERRORCLASS = 'rb-parse-error'; + var BLOCKCOMMENT = 'rb-block-comment'; + var FLOATCLASS = 'rb-float'; + var HEXNUMCLASS = 'rb-hexnum'; + var BINARYCLASS = 'rb-binary'; + var ASCIICODE = 'rb-ascii' + var LONGCOMMENTCLASS = 'rb-long-comment'; + var WHITESPACEINLONGCOMMENTCLASS = 'rb-long-comment-whitespace'; + var KEWORDCLASS = 'rb-keyword'; + var REGEXPCLASS = 'rb-regexp'; + var GLOBALVARCLASS = 'rb-global-variable'; + var EXECCLASS = 'rb-exec'; + var INTRANGECLASS = 'rb-range'; + var OPCLASS = 'rb-operator'; + + + var identifierStarters = /[_A-Za-z]/; + var stringStarters = /['"]/; + var numberStarters = /[0-9]/; + var keywords = wordRegexp(['begin', 'class', 'ensure', 'nil', 'self', 'when', 'end', 'def', 'false', 'not', 'super', 'while', 'alias', 'defined', 'for', 'or', 'then', 'yield', 'and', 'do', 'if', 'redo', 'true', 'begin', 'else', 'in', 'rescue', 'undef', 'break', 'elsif', 'module', 'retry', 'unless', 'case', 'end', 'next', 'return', 'until', 'include']); + + var changeOperators = ['=', '%=', '/=', '-=', '+=', '|=', '&=', '>>=', '<<=', '*=', '&&=', '||=', '**=']; + var boolOperators = ['>=', '<=', '==', '===', '=~', '!=', '<>', '!', '?', ':']; + var mathOperators = ['/', '*', '+', '-'] + var structureOperators = ['=>']; + var rangeOperators = ['..', '...']; + var operators = changeOperators. + concat(boolOperators). + concat(mathOperators). + concat(structureOperators). + concat(rangeOperators); + + var operatorProgress = buildProgress(operators); + + var py, keywords, types, stringStarters, stringTypes, config; + + + + function configure(conf) { config = conf; } + + var wasDef = false; + var wasDivideByLValueLast = false; + + return tokenizeRuby = (function() { + + /* state nesting */ + var stateStack = [normal]; + function pushState(state, setState) { + stateStack.push(state); + setState(state); + } + + function popState(setState) { + stateStack.pop(); + setState(topState()); + } + + function topState() { + return stateStack[stateStack.length-1]; + } + + function ancestorState(level) { + level = level ? level : '1' + return stateStack[stateStack.length-1-level]; + } + + + /* special states */ + + function inHereDoc(style, keyword) { + return function(source, setState) { + var st = ''; + while (!source.endOfLine()) { + var ch = source.next(); + if (ch == keyword[keyword.length-1]) { + st += source.get(); + if (st.substr(st.length - keyword.length, keyword.length) == keyword) { + setState(normal); + return {content:st, style:style}; + } + } + } + return style; + } + } + + function inRubyInsertableString(style, ending_char) { + return inString(style, ending_char, true); + } + + function inStaticString(style, ending_char) { + return inString(style, ending_char, false); + } + + function inString(style, ending_char, ruby_insertable) { + return function(source, setState) { + var stringDelim, threeStr, temp, type, word, possible = {}; + while (!source.endOfLine()) { + var ch = source.next(); + // Skip escaped characters + if (ch == '\\') { + ch = source.next(); + ch = source.next(); + } + if (ch == ending_char) { + //popState(setState); + setState(normal); + break; + } + if (ch == '#' && source.peek() == '{') { + //source.next(); + if (ruby_insertable) { + //setState(inRubyInsertableStringNormal(style, inString(style, ending_char, ruby_insertable))); + } else { + //pushState(inIgnoredBracesString(style), setState); + } + return style; + } + } + return style; + } + } + + /* states for #{} in strings */ + + + function inRubyInsertableStringNormal(style, returnTo) { + var originalState = ancestorState(); + var waitingForBraces = 1; + return function(source, setState) { + if (source.peek() == '}') { + waitingForBraces -= 1; + } + if (source.peek() == '{') { + waitingForBraces += 1; + } + if (waitingForBraces == 0) { + source.next(); + //popState(setState); + setState(returnTo); + return style; + } else { + return originalState(source, setState); + } + } + } +/* + function inIgnoredBracesString(style) { + return function(source, setState) { + var ch = source.next(); + if (ch == '}') { + popState(setState); + } + return style; + } + } +*/ + + + + function testOperator(source, ch) { + if (operatorProgress[ch]) { + var current = operatorProgress; + while (current[ch][source.peek()]) { + current = current[ch]; + ch = source.next(); + } + return {content:source.get(), style:OPCLASS}; + } else { + return false; + } + } + + + /* the default ruby code state */ + function normal(source, setState) { + var stringDelim, threeStr, temp, type, word, possible = {}; + var ch = source.next(); + + // Handle comments + if (ch == '#') { + while (!source.endOfLine()) { + source.next(); + } + return COMMENTCLASS; + } + + if (ch == '=') { + var peek = source.peek(); + if (peek == 'b' || peek == 'e') { + source.nextWhile(matcher(/[\w\d]/)); + word = source.get(); + return {content:word, style:LONGCOMMENTCLASS}; + } + } + + // alterred by Stuart - only way you can add this back in is + // if you somehow detecte the previous token was = or '(' or '=' + // - otherwise this conflicts with divide by... + if (ch == '/' && source.peek() != ' ') { + if (false == wasDivideByLValueLast) { + setState(inStaticString(REGEXPCLASS, '/')); + return null; + } + } + + wasDivideByLValueLast = false; + + if (ch == ':') { + type = SYMBOLCLASS; + source.nextWhile(matcher(/[\w\d]/)); + word = source.get(); + wasDivideByLValueLast = true; + return {content:word, style:type}; + } + + if (ch == '@') { + type = 'rb-instance-var'; + if (source.peek() == '@') { + source.next() + type = 'rb-class-var'; + } + source.nextWhile(matcher(/[\w\d]/)); + word = source.get(); + return {content:word, style:type}; + } + + + if (numberStarters.test(ch) || + ( ch == '-' && numberStarters.test(source.peek()) ) + ) { + var type = FIXNUMCLASS; + source.nextWhile(matcher(/[0-9_]/)); + word = source.get(); + if (source.peek() == 'x') { + source.next() + source.nextWhile(matcher(/[a-f0-9]/)); + word += source.get(); + return {content:word, style:HEXNUMCLASS}; + } + if (source.peek() == 'b') { + source.next() + source.nextWhile(matcher(/[01]/)); + word += source.get(); + return {content:word, style:BINARYCLASS}; + } + if (source.peek() == '.') { + source.next(); + if (source.peek() != '.') { + type = FLOATCLASS; + source.nextWhile(matcher(/[0-9_]/)); + word += source.get(); + } else { + word += source.get(); + // two dots are used as a range operator + source.push('.'); + return {content:word.substr(0, word.length-1), style:type}; + } + } + if (source.peek() == 'e') { + source.next(); + if (source.peek() == '-') { + source.next(); + } + type = FLOATCLASS; + source.nextWhile(matcher(/[0-9_]/)); + word += source.get(); + } + return {content:word, style:type}; + } + + + if (ch == '%') { + type = STRINGCLASS; + var peek = source.peek(); + if (peek == 'w') { + setState(inStaticString(STRINGCLASS, '}')); + return null; + } + if (peek == 'W') { + setState(inRubyInsertableString(STRINGCLASS, '}')); + return null; + } + + if (peek == 'q' || peek == 'Q') { + source.next(); + var ending = source.next(); + if (ending == '(') ending = ')'; + if (ending == '{') ending = '}'; + setState(inString(STRINGCLASS, ending, peek == 'Q')); + return {content:source.get(), style:STRINGCLASS}; + } + setState(inRubyInsertableString(STRINGCLASS, source.peek())); + source.next(); + return {content:source.get(), style:STRINGCLASS}; + } + + if (ch == '$') { + if ((/[!@_\.&~n=\/\\0\*$\?]/).test(source.peek())) { + source.next(); + } else { + source.nextWhile(matcher(/[A-Za-z0-9_]/)); + } + wasDivideByLValueLast = true; + return {content:source.get(), style:GLOBALVARCLASS}; + } + + if (ch == '\'') { + setState(inStaticString(STRINGCLASS, '\'')); + return null; + } + + if (ch == '`') { + setState(inRubyInsertableString(EXECCLASS, '`')); + return null; + } + + + if (ch == '\"') { + setState(inRubyInsertableString(STRINGCLASS, "\"")); + return null; + } + + + if (ch == '.') { + if (source.peek() != '.') { + source.nextWhile(matcher(/[\w\d]/)); + word = source.get(); + return {content:word, style:METHODCALLCLASS}; + } + } + + /* + // removed by Stuart - can't get it to work! + if (ch == '?') { + var peek = source.peek(); + if (peek == '\\') { + source.next(); + source.nextWhile(matcher(/[A-Za-z\\-]/)); + return {content:source.get(), style:ASCIICODE}; + } else { + source.next(); + return {content:source.get(), style:ASCIICODE}; + } + } + */ + + if (ch == '<') { + if (source.peek() == '<') { + source.next(); + if (source.peek() == '-' || source.peek() == '`') { + source.next(); + } + if (identifierStarters.test(source.peek())) { + source.nextWhile(matcher(/[\w\d]/)); + var keyword = source.get(); + setState(inHereDoc(HEREDOCCLASS, keyword.match(/\w+/)[0])); + return {content:keyword, style:HEREDOCCLASS}; + } + } + } + + var result = testOperator(source, ch); + if (result) { + return result; + } + + if (identifierStarters.test(ch)) { + + source.nextWhile(matcher(/[A-Za-z0-9_]/)); + if ((/[!\?]/).test(source.peek())) { + source.next(); + } + word = source.get(); + /* for now, all identifiers are considered method calls */ + //type = 'rb-identifier'; + type = INSTANCEMETHODCALLCLASS; + + if (keywords.test(word)) { + type = KEWORDCLASS; + } + if (wasDef) { + type = 'rb-method rb-methodname'; + } + + wasDef = (word == 'def'); + + if (ch.toUpperCase() == ch) { + type = CONSTCLASS; + while (source.peek() == ':') { + source.next(); + if (source.peek() == ':') { + source.next(); + source.nextWhile(matcher(/[\w\d]/)); + } + } + word += source.get(); + } + + + if (type == INSTANCEMETHODCALLCLASS) { + var char = null; + pushback = ''; + while(!source.endOfLine()) { + char = source.next(); + + if (char == ',') { + // get another variable + } + var result = testOperator(source, char); + if (result) { + pushback += result.content; + //console.log('RRRRRRRRresult is ', result); + } + pushback += source.get(); + + if (result && result.content == '=') { + //console.log('YESSSSSSss ', result); + type = VARIABLECLASS; + break; + } + } + //console.log('pushback "'+pushback+'"'); + //console.log('word "'+word+'"'); + //console.log('get ', source.get()); + + source.push(pushback); + wasDivideByLValueLast = true; // just assume these are all l-values + return {content:word, style:type}; + } + + return {content:word, style:type}; + } + + wasDivideByLValueLast = true; // just assume these are all l-values + return NORMALCONTEXT; + } + + return function(source, startState) { + return tokenizer(source, startState || normal); + }; + })(); +})(); + diff --git a/Hosts/Silverlight/Iron7/Resources/undo_js.txt b/Hosts/Silverlight/Iron7/Resources/undo_js.txt new file mode 100644 index 0000000000..8e0218088d --- /dev/null +++ b/Hosts/Silverlight/Iron7/Resources/undo_js.txt @@ -0,0 +1,410 @@ +/** + * Storage and control for undo information within a CodeMirror + * editor. 'Why on earth is such a complicated mess required for + * that?', I hear you ask. The goal, in implementing this, was to make + * the complexity of storing and reverting undo information depend + * only on the size of the edited or restored content, not on the size + * of the whole document. This makes it necessary to use a kind of + * 'diff' system, which, when applied to a DOM tree, causes some + * complexity and hackery. + * + * In short, the editor 'touches' BR elements as it parses them, and + * the History stores these. When nothing is touched in commitDelay + * milliseconds, the changes are committed: It goes over all touched + * nodes, throws out the ones that did not change since last commit or + * are no longer in the document, and assembles the rest into zero or + * more 'chains' -- arrays of adjacent lines. Links back to these + * chains are added to the BR nodes, while the chain that previously + * spanned these nodes is added to the undo history. Undoing a change + * means taking such a chain off the undo history, restoring its + * content (text is saved per line) and linking it back into the + * document. + */ + +// A history object needs to know about the DOM container holding the +// document, the maximum amount of undo levels it should store, the +// delay (of no input) after which it commits a set of changes, and, +// unfortunately, the 'parent' window -- a window that is not in +// designMode, and on which setTimeout works in every browser. +function History(container, maxDepth, commitDelay, editor) { + this.container = container; + this.maxDepth = maxDepth; this.commitDelay = commitDelay; + this.editor = editor; this.parent = editor.parent; + // This line object represents the initial, empty editor. + var initial = {text: "", from: null, to: null}; + // As the borders between lines are represented by BR elements, the + // start of the first line and the end of the last one are + // represented by null. Since you can not store any properties + // (links to line objects) in null, these properties are used in + // those cases. + this.first = initial; this.last = initial; + // Similarly, a 'historyTouched' property is added to the BR in + // front of lines that have already been touched, and 'firstTouched' + // is used for the first line. + this.firstTouched = false; + // History is the set of committed changes, touched is the set of + // nodes touched since the last commit. + this.history = []; this.redoHistory = []; this.touched = []; +} + +History.prototype = { + // Schedule a commit (if no other touches come in for commitDelay + // milliseconds). + scheduleCommit: function() { + var self = this; + this.parent.clearTimeout(this.commitTimeout); + this.commitTimeout = this.parent.setTimeout(function(){self.tryCommit();}, this.commitDelay); + }, + + // Mark a node as touched. Null is a valid argument. + touch: function(node) { + this.setTouched(node); + this.scheduleCommit(); + }, + + // Undo the last change. + undo: function() { + // Make sure pending changes have been committed. + this.commit(); + + if (this.history.length) { + // Take the top diff from the history, apply it, and store its + // shadow in the redo history. + var item = this.history.pop(); + this.redoHistory.push(this.updateTo(item, "applyChain")); + this.notifyEnvironment(); + return this.chainNode(item); + } + }, + + // Redo the last undone change. + redo: function() { + this.commit(); + if (this.redoHistory.length) { + // The inverse of undo, basically. + var item = this.redoHistory.pop(); + this.addUndoLevel(this.updateTo(item, "applyChain")); + this.notifyEnvironment(); + return this.chainNode(item); + } + }, + + clear: function() { + this.history = []; + this.redoHistory = []; + }, + + // Ask for the size of the un/redo histories. + historySize: function() { + return {undo: this.history.length, redo: this.redoHistory.length}; + }, + + // Push a changeset into the document. + push: function(from, to, lines) { + var chain = []; + for (var i = 0; i < lines.length; i++) { + var end = (i == lines.length - 1) ? to : this.container.ownerDocument.createElement("BR"); + chain.push({from: from, to: end, text: cleanText(lines[i])}); + from = end; + } + this.pushChains([chain], from == null && to == null); + this.notifyEnvironment(); + }, + + pushChains: function(chains, doNotHighlight) { + this.commit(doNotHighlight); + this.addUndoLevel(this.updateTo(chains, "applyChain")); + this.redoHistory = []; + }, + + // Retrieve a DOM node from a chain (for scrolling to it after undo/redo). + chainNode: function(chains) { + for (var i = 0; i < chains.length; i++) { + var start = chains[i][0], node = start && (start.from || start.to); + if (node) return node; + } + }, + + // Clear the undo history, make the current document the start + // position. + reset: function() { + this.history = []; this.redoHistory = []; + }, + + textAfter: function(br) { + return this.after(br).text; + }, + + nodeAfter: function(br) { + return this.after(br).to; + }, + + nodeBefore: function(br) { + return this.before(br).from; + }, + + // Commit unless there are pending dirty nodes. + tryCommit: function() { + if (!window.History) return; // Stop when frame has been unloaded + if (this.editor.highlightDirty()) this.commit(true); + else this.scheduleCommit(); + }, + + // Check whether the touched nodes hold any changes, if so, commit + // them. + commit: function(doNotHighlight) { + this.parent.clearTimeout(this.commitTimeout); + // Make sure there are no pending dirty nodes. + if (!doNotHighlight) this.editor.highlightDirty(true); + // Build set of chains. + var chains = this.touchedChains(), self = this; + + if (chains.length) { + this.addUndoLevel(this.updateTo(chains, "linkChain")); + this.redoHistory = []; + this.notifyEnvironment(); + } + }, + + // [ end of public interface ] + + // Update the document with a given set of chains, return its + // shadow. updateFunc should be "applyChain" or "linkChain". In the + // second case, the chains are taken to correspond the the current + // document, and only the state of the line data is updated. In the + // first case, the content of the chains is also pushed iinto the + // document. + updateTo: function(chains, updateFunc) { + var shadows = [], dirty = []; + for (var i = 0; i < chains.length; i++) { + shadows.push(this.shadowChain(chains[i])); + dirty.push(this[updateFunc](chains[i])); + } + if (updateFunc == "applyChain") + this.notifyDirty(dirty); + return shadows; + }, + + // Notify the editor that some nodes have changed. + notifyDirty: function(nodes) { + forEach(nodes, method(this.editor, "addDirtyNode")) + this.editor.scheduleHighlight(); + }, + + notifyEnvironment: function() { + // Used by the line-wrapping line-numbering code. + if (window.frameElement && window.frameElement.CodeMirror.updateNumbers) + window.frameElement.CodeMirror.updateNumbers(); + if (this.onChange) this.onChange(); + }, + + // Link a chain into the DOM nodes (or the first/last links for null + // nodes). + linkChain: function(chain) { + for (var i = 0; i < chain.length; i++) { + var line = chain[i]; + if (line.from) line.from.historyAfter = line; + else this.first = line; + if (line.to) line.to.historyBefore = line; + else this.last = line; + } + }, + + // Get the line object after/before a given node. + after: function(node) { + return node ? node.historyAfter : this.first; + }, + before: function(node) { + return node ? node.historyBefore : this.last; + }, + + // Mark a node as touched if it has not already been marked. + setTouched: function(node) { + if (node) { + if (!node.historyTouched) { + this.touched.push(node); + node.historyTouched = true; + } + } + else { + this.firstTouched = true; + } + }, + + // Store a new set of undo info, throw away info if there is more of + // it than allowed. + addUndoLevel: function(diffs) { + this.history.push(diffs); + if (this.history.length > this.maxDepth) + this.history.shift(); + }, + + // Build chains from a set of touched nodes. + touchedChains: function() { + var self = this; + + // The temp system is a crummy hack to speed up determining + // whether a (currently touched) node has a line object associated + // with it. nullTemp is used to store the object for the first + // line, other nodes get it stored in their historyTemp property. + var nullTemp = null; + function temp(node) {return node ? node.historyTemp : nullTemp;} + function setTemp(node, line) { + if (node) node.historyTemp = line; + else nullTemp = line; + } + + function buildLine(node) { + var text = []; + for (var cur = node ? node.nextSibling : self.container.firstChild; + cur && !isBR(cur); cur = cur.nextSibling) + if (cur.currentText) text.push(cur.currentText); + return {from: node, to: cur, text: cleanText(text.join(""))}; + } + + // Filter out unchanged lines and nodes that are no longer in the + // document. Build up line objects for remaining nodes. + var lines = []; + if (self.firstTouched) self.touched.push(null); + forEach(self.touched, function(node) { + if (node && node.parentNode != self.container) return; + + if (node) node.historyTouched = false; + else self.firstTouched = false; + + var line = buildLine(node), shadow = self.after(node); + if (!shadow || shadow.text != line.text || shadow.to != line.to) { + lines.push(line); + setTemp(node, line); + } + }); + + // Get the BR element after/before the given node. + function nextBR(node, dir) { + var link = dir + "Sibling", search = node[link]; + while (search && !isBR(search)) + search = search[link]; + return search; + } + + // Assemble line objects into chains by scanning the DOM tree + // around them. + var chains = []; self.touched = []; + forEach(lines, function(line) { + // Note that this makes the loop skip line objects that have + // been pulled into chains by lines before them. + if (!temp(line.from)) return; + + var chain = [], curNode = line.from, safe = true; + // Put any line objects (referred to by temp info) before this + // one on the front of the array. + while (true) { + var curLine = temp(curNode); + if (!curLine) { + if (safe) break; + else curLine = buildLine(curNode); + } + chain.unshift(curLine); + setTemp(curNode, null); + if (!curNode) break; + safe = self.after(curNode); + curNode = nextBR(curNode, "previous"); + } + curNode = line.to; safe = self.before(line.from); + // Add lines after this one at end of array. + while (true) { + if (!curNode) break; + var curLine = temp(curNode); + if (!curLine) { + if (safe) break; + else curLine = buildLine(curNode); + } + chain.push(curLine); + setTemp(curNode, null); + safe = self.before(curNode); + curNode = nextBR(curNode, "next"); + } + chains.push(chain); + }); + + return chains; + }, + + // Find the 'shadow' of a given chain by following the links in the + // DOM nodes at its start and end. + shadowChain: function(chain) { + var shadows = [], next = this.after(chain[0].from), end = chain[chain.length - 1].to; + while (true) { + shadows.push(next); + var nextNode = next.to; + if (!nextNode || nextNode == end) + break; + else + next = nextNode.historyAfter || this.before(end); + // (The this.before(end) is a hack -- FF sometimes removes + // properties from BR nodes, in which case the best we can hope + // for is to not break.) + } + return shadows; + }, + + // Update the DOM tree to contain the lines specified in a given + // chain, link this chain into the DOM nodes. + applyChain: function(chain) { + // Some attempt is made to prevent the cursor from jumping + // randomly when an undo or redo happens. It still behaves a bit + // strange sometimes. + var cursor = select.cursorPos(this.container, false), self = this; + + // Remove all nodes in the DOM tree between from and to (null for + // start/end of container). + function removeRange(from, to) { + var pos = from ? from.nextSibling : self.container.firstChild; + while (pos != to) { + var temp = pos.nextSibling; + removeElement(pos); + pos = temp; + } + } + + var start = chain[0].from, end = chain[chain.length - 1].to; + // Clear the space where this change has to be made. + removeRange(start, end); + + // Insert the content specified by the chain into the DOM tree. + for (var i = 0; i < chain.length; i++) { + var line = chain[i]; + // The start and end of the space are already correct, but BR + // tags inside it have to be put back. + if (i > 0) + self.container.insertBefore(line.from, end); + + // Add the text. + var node = makePartSpan(fixSpaces(line.text), this.container.ownerDocument); + self.container.insertBefore(node, end); + // See if the cursor was on this line. Put it back, adjusting + // for changed line length, if it was. + if (cursor && cursor.node == line.from) { + var cursordiff = 0; + var prev = this.after(line.from); + if (prev && i == chain.length - 1) { + // Only adjust if the cursor is after the unchanged part of + // the line. + for (var match = 0; match < cursor.offset && + line.text.charAt(match) == prev.text.charAt(match); match++); + if (cursor.offset > match) + cursordiff = line.text.length - prev.text.length; + } + select.setCursorPos(this.container, {node: line.from, offset: Math.max(0, cursor.offset + cursordiff)}); + } + // Cursor was in removed line, this is last new line. + else if (cursor && (i == chain.length - 1) && cursor.node && cursor.node.parentNode != this.container) { + select.setCursorPos(this.container, {node: line.from, offset: line.text.length}); + } + } + + // Anchor the chain in the DOM tree. + this.linkChain(chain); + return start; + } +}; diff --git a/Hosts/Silverlight/Iron7/Resources/util_js.txt b/Hosts/Silverlight/Iron7/Resources/util_js.txt new file mode 100644 index 0000000000..c7021c243c --- /dev/null +++ b/Hosts/Silverlight/Iron7/Resources/util_js.txt @@ -0,0 +1,130 @@ +/* A few useful utility functions. */ + +// Capture a method on an object. +function method(obj, name) { + return function() {obj[name].apply(obj, arguments);}; +} + +// The value used to signal the end of a sequence in iterators. +var StopIteration = {toString: function() {return "StopIteration"}}; + +// Apply a function to each element in a sequence. +function forEach(iter, f) { + if (iter.next) { + try {while (true) f(iter.next());} + catch (e) {if (e != StopIteration) throw e;} + } + else { + for (var i = 0; i < iter.length; i++) + f(iter[i]); + } +} + +// Map a function over a sequence, producing an array of results. +function map(iter, f) { + var accum = []; + forEach(iter, function(val) {accum.push(f(val));}); + return accum; +} + +// Create a predicate function that tests a string againsts a given +// regular expression. No longer used but might be used by 3rd party +// parsers. +function matcher(regexp){ + return function(value){return regexp.test(value);}; +} + +// Test whether a DOM node has a certain CSS class. Much faster than +// the MochiKit equivalent, for some reason. +function hasClass(element, className){ + var classes = element.className; + return classes && new RegExp("(^| )" + className + "($| )").test(classes); +} + +// Insert a DOM node after another node. +function insertAfter(newNode, oldNode) { + var parent = oldNode.parentNode; + parent.insertBefore(newNode, oldNode.nextSibling); + return newNode; +} + +function removeElement(node) { + if (node.parentNode) + node.parentNode.removeChild(node); +} + +function clearElement(node) { + while (node.firstChild) + node.removeChild(node.firstChild); +} + +// Check whether a node is contained in another one. +function isAncestor(node, child) { + while (child = child.parentNode) { + if (node == child) + return true; + } + return false; +} + +// The non-breaking space character. +var nbsp = "\u00a0"; +var matching = {"{": "}", "[": "]", "(": ")", + "}": "{", "]": "[", ")": "("}; + +// Standardize a few unportable event properties. +function normalizeEvent(event) { + if (!event.stopPropagation) { + event.stopPropagation = function() {this.cancelBubble = true;}; + event.preventDefault = function() {this.returnValue = false;}; + } + if (!event.stop) { + event.stop = function() { + this.stopPropagation(); + this.preventDefault(); + }; + } + + if (event.type == "keypress") { + event.code = (event.charCode == null) ? event.keyCode : event.charCode; + event.character = String.fromCharCode(event.code); + } + return event; +} + +// Portably register event handlers. +function addEventHandler(node, type, handler, removeFunc) { + function wrapHandler(event) { + handler(normalizeEvent(event || window.event)); + } + if (typeof node.addEventListener == "function") { + node.addEventListener(type, wrapHandler, false); + if (removeFunc) return function() {node.removeEventListener(type, wrapHandler, false);}; + } + else { + node.attachEvent("on" + type, wrapHandler); + if (removeFunc) return function() {node.detachEvent("on" + type, wrapHandler);}; + } +} + +function nodeText(node) { + return node.textContent || node.innerText || node.nodeValue || ""; +} + +function nodeTop(node) { + var top = 0; + while (node.offsetParent) { + top += node.offsetTop; + node = node.offsetParent; + } + return top; +} + +function isBR(node) { + var nn = node.nodeName; + return nn == "BR" || nn == "br"; +} +function isSpan(node) { + var nn = node.nodeName; + return nn == "SPAN" || nn == "span"; +} diff --git a/Hosts/Silverlight/Iron7/SampleData/MainViewModelSampleData.xaml b/Hosts/Silverlight/Iron7/SampleData/MainViewModelSampleData.xaml new file mode 100644 index 0000000000..7706eb59cb --- /dev/null +++ b/Hosts/Silverlight/Iron7/SampleData/MainViewModelSampleData.xaml @@ -0,0 +1,16 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/Hosts/Silverlight/Iron7/ScriptResources.Designer.cs b/Hosts/Silverlight/Iron7/ScriptResources.Designer.cs new file mode 100644 index 0000000000..aaf8899440 --- /dev/null +++ b/Hosts/Silverlight/Iron7/ScriptResources.Designer.cs @@ -0,0 +1,291 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Iron7 { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class ScriptResources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal ScriptResources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Iron7.ScriptResources", typeof(ScriptResources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to /* CodeMirror main module + /// * + /// * Implements the CodeMirror constructor and prototype, which take care + /// * of initializing the editor frame, and providing the outside interface. + /// */ + /// + ///// The CodeMirrorConfig object is used to specify a default + ///// configuration. If you specify such an object before loading this + ///// file, the values you put into it will override the defaults given + ///// below. You can also assign to it after loading. + ///var CodeMirrorConfig = window.CodeMirrorConfig || {}; + /// + ///var CodeMirror = (function(){ [rest of string was truncated]";. + /// + internal static string codemirror_js { + get { + return ResourceManager.GetString("codemirror_js", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to /* The Editor object manages the content of the editable frame. It + /// * catches events, colours nodes, and indents lines. This file also + /// * holds some functions for transforming arbitrary DOM structures into + /// * plain sequences of <span> and <br> elements + /// */ + /// + ///var internetExplorer = document.selection && window.ActiveXObject && /MSIE/.test(navigator.userAgent); + ///var webkit = /AppleWebKit/.test(navigator.userAgent); + ///var safari = /Apple Computers, Inc/.test(navigator.vendor); + ///var gecko = /gecko\/(\d{8})/i.test(na [rest of string was truncated]";. + /// + internal static string editor_js { + get { + return ResourceManager.GetString("editor_js", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to + ///var RubyParser = Editor.Parser = (function() { + /// // Token types that can be considered to be atoms. + /// var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true}; + /// // Constructor for the lexical context objects. + /// function JSLexical(indented, column, type, align, prev, info) { + /// // indentation at start of this line + /// this.indented = indented; + /// // column at which this scope was opened + /// this.column = column; + /// // type of scope ('vardef', 'stat' (statement), [rest of string was truncated]";. + /// + internal static string parseruby_js { + get { + return ResourceManager.GetString("parseruby_js", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to .editbox { + /// padding: .4em; + /// margin: 0; + /// font-family: consolas, monospace; + /// font-size: 12pt; + /// line-height: 1.1em; + ///} + /// + ///.editbox .rb-linenum { + /// padding:0.1em 1em 0.2em 0; + /// width:75px; + ///} + /// + ///.editbox .rb-pan { + /// padding-bottom:0.1em; + /// padding-top:0.2em; + ///} + /// + ///.editbox .rb-comment, .editbox .rb-long-comment { + /// font-style:italic; + ///} + /// + ///.editbox .rb-keyword, .editbox .rb-operator { + /// font-weight:bold; + ///} + /// + ///.editbox .rb-long-comment-whitespace {} + /// + ///.editbox .rb-global-variable { + /// color:#61 [rest of string was truncated]";. + /// + internal static string rubycolors_css { + get { + return ResourceManager.GetString("rubycolors_css", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to /* Functionality for finding, storing, and restoring selections + /// * + /// * This does not provide a generic API, just the minimal functionality + /// * required by the CodeMirror system. + /// */ + /// + ///// Namespace object. + ///var select = {}; + /// + ///(function() { + /// select.ie_selection = document.selection && document.selection.createRangeCollection; + /// + /// // Find the 'top-level' (defined as 'a direct child of the node + /// // passed as the top argument') node that the given node is + /// // contained in. Return null if the given node is not insid [rest of string was truncated]";. + /// + internal static string select_js { + get { + return ResourceManager.GetString("select_js", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to /* String streams are the things fed to parsers (which can feed them + /// * to a tokenizer if they want). They provide peek and next methods + /// * for looking at the current character (next 'consumes' this + /// * character, peek does not), and a get method for retrieving all the + /// * text that was consumed since the last time get was called. + /// * + /// * An easy mistake to make is to let a StopIteration exception finish + /// * the token stream while there are still characters pending in the + /// * string stream (hitting the end of the [rest of string was truncated]";. + /// + internal static string stringstream_js { + get { + return ResourceManager.GetString("stringstream_js", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to // A framework for simple tokenizers. Takes care of newlines and + ///// white-space, and of getting the text from the source stream into + ///// the token object. A state is a function of two arguments -- a + ///// string stream and a setState function. The second can be used to + ///// change the tokenizer's state, and can be ignored for stateless + ///// tokenizers. This function should advance the stream over a token + ///// and return a string or object containing information about the next + ///// token, or null to pass and have the (n [rest of string was truncated]";. + /// + internal static string tokenize_js { + get { + return ResourceManager.GetString("tokenize_js", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to + ///var tokenizeRuby = (function() { + /// + /// function buildProgress(word) { + /// var progress = {}, current; + /// for (var i in word) { + /// current = progress; + /// for (var ch in word[i]) { + /// if(!current[word[i][ch]]) current[word[i][ch]] = {}; + /// current = current[word[i][ch]]; + /// } + /// } + /// return progress; + /// } + /// + /// + /// function wordRegexp(words) { + /// return new RegExp("^(?:" + words.join("|") + ")$"); + /// } + /// var NORMALCONTEXT = 'rb-normal'; + /// var ERRORCLASS = 'rb-error'; + /// var COMMENTCLASS = 'rb-comment'; + /// var SYMBOLCLASS = 'rb-symbol [rest of string was truncated]";. + /// + internal static string tokenizeruby_js { + get { + return ResourceManager.GetString("tokenizeruby_js", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to /** + /// * Storage and control for undo information within a CodeMirror + /// * editor. 'Why on earth is such a complicated mess required for + /// * that?', I hear you ask. The goal, in implementing this, was to make + /// * the complexity of storing and reverting undo information depend + /// * only on the size of the edited or restored content, not on the size + /// * of the whole document. This makes it necessary to use a kind of + /// * 'diff' system, which, when applied to a DOM tree, causes some + /// * complexity and hackery. + /// * + /// * In sh [rest of string was truncated]";. + /// + internal static string undo_js { + get { + return ResourceManager.GetString("undo_js", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to /* A few useful utility functions. */ + /// + ///// Capture a method on an object. + ///function method(obj, name) { + /// return function() {obj[name].apply(obj, arguments);}; + ///} + /// + ///// The value used to signal the end of a sequence in iterators. + ///var StopIteration = {toString: function() {return "StopIteration"}}; + /// + ///// Apply a function to each element in a sequence. + ///function forEach(iter, f) { + /// if (iter.next) { + /// try {while (true) f(iter.next());} + /// catch (e) {if (e != StopIteration) throw e;} + /// } + /// else { + /// for (var i = 0 [rest of string was truncated]";. + /// + internal static string util_js { + get { + return ResourceManager.GetString("util_js", resourceCulture); + } + } + } +} diff --git a/Hosts/Silverlight/Iron7/ScriptResources.resx b/Hosts/Silverlight/Iron7/ScriptResources.resx new file mode 100644 index 0000000000..a60e7583f8 --- /dev/null +++ b/Hosts/Silverlight/Iron7/ScriptResources.resx @@ -0,0 +1,151 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + Resources\codemirror_js.txt;System.String, mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e;Windows-1252 + + + Resources\editor_js.txt;System.String, mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e;Windows-1252 + + + Resources\parseruby_js.txt;System.String, mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e;Windows-1252 + + + Resources\rubycolors_css.txt;System.String, mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e;Windows-1252 + + + Resources\select_js.txt;System.String, mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e;Windows-1252 + + + Resources\stringstream_js.txt;System.String, mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e;Windows-1252 + + + Resources\tokenizeruby_js.txt;System.String, mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e;Windows-1252 + + + Resources\tokenize_js.txt;System.String, mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e;Windows-1252 + + + Resources\undo_js.txt;System.String, mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e;Windows-1252 + + + Resources\util_js.txt;System.String, mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e;Windows-1252 + + \ No newline at end of file diff --git a/Hosts/Silverlight/Iron7/SplashScreenImage.jpg b/Hosts/Silverlight/Iron7/SplashScreenImage.jpg new file mode 100644 index 0000000000..7a47ba50b2 Binary files /dev/null and b/Hosts/Silverlight/Iron7/SplashScreenImage.jpg differ diff --git a/Hosts/Silverlight/Iron7/Utils/ScriptDownloader.cs b/Hosts/Silverlight/Iron7/Utils/ScriptDownloader.cs new file mode 100644 index 0000000000..0220546352 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Utils/ScriptDownloader.cs @@ -0,0 +1,82 @@ +using System; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Ink; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; +using System.Windows.Threading; +using System.IO; +using Newtonsoft.Json; +using System.Windows.Navigation; + +namespace Iron7.Utils +{ + public class ScriptDownloader : CommunicationBase + { + OnlineScriptViewModel toDownload; + Action successfulAction; + + public void Download(OnlineScriptViewModel toDownload, Dispatcher dispatcher, Action successfulAction) + { + this.successfulAction = successfulAction; + this.toDownload = toDownload; + this.dispatcher = dispatcher; + childWindow = new Views.StyledChildWindow(); + + var backgroundThread = new System.Threading.Thread(DownloadProc); + backgroundThread.Start(); + + childWindow.Closed += (sender, args) => + { + cancelRequested = true; + try + { + if (myRequest != null) + myRequest.Abort(); + } + catch + { + // ignore problems :/ + } + }; + + childWindow.Show("copying script " + toDownload.Title); + childWindow.ShowCancelButton(); + } + + private void DownloadProc() + { + try + { + if (string.IsNullOrEmpty(toDownload.Author)) + throw new ApplicationException("to download scripts from your own account, you must register with script.iron7.com and provide your account information in 'Share online'"); + + var url = string.Format("http://script.iron7.com/Script/Detail?scriptId={0}&userLowerCaseName={1}&rand={2}", + Uri.EscapeDataString(toDownload.ScriptId), + Uri.EscapeDataString(toDownload.Author), + new Random().Next(100000)); + + DoGet(url, (s) => { + try + { + var script = JsonConvert.DeserializeObject(s); + childWindow.Close(); + this.successfulAction(script); + } + catch (Exception exc) + { + ShowErrorMessage(exc); + } + }); + } + catch (Exception exc) + { + ShowErrorMessage(exc); + } + } + } +} diff --git a/Hosts/Silverlight/Iron7/Utils/ScriptUploader.cs b/Hosts/Silverlight/Iron7/Utils/ScriptUploader.cs new file mode 100644 index 0000000000..bbce465393 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Utils/ScriptUploader.cs @@ -0,0 +1,216 @@ +using System; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Ink; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; +using System.Windows.Threading; +using System.IO; +using Newtonsoft.Json; +using System.Windows.Navigation; +using System.Text; + +namespace Iron7.Utils +{ + public class CommunicationBase + { + protected Views.StyledChildWindow childWindow; + protected Dispatcher dispatcher; + protected HttpWebRequest myRequest; + protected CookieContainer cookieContainer = new CookieContainer(); + protected bool cancelRequested = false; + + protected void DoGet(string url, Action doOnFinish) + { + try + { + myRequest = HttpWebRequest.CreateHttp(url); + myRequest.Accept = "application/json"; + myRequest.Method = "GET"; + myRequest.CookieContainer = cookieContainer; + var async1 = myRequest.BeginGetResponse((result) => + { + HandleEndResponse(doOnFinish, result); + }, + null); + } + catch (Exception exc) + { + ShowErrorMessage(exc); + } + } + + protected void DoPost(string url, string post, Action doOnFinish) + { + try + { + myRequest = HttpWebRequest.CreateHttp(url); + myRequest.Accept = "application/json"; + myRequest.Method = "POST"; + myRequest.ContentType = "application/x-www-form-urlencoded"; + myRequest.CookieContainer = cookieContainer; + var async1 = myRequest.BeginGetRequestStream((result1) => + { + HandleEndGetRequestStream(post, doOnFinish, result1); + }, + null); + } + catch (Exception exc) + { + ShowErrorMessage(exc); + } + } + + protected void HandleEndGetRequestStream(string post, Action doOnFinish, IAsyncResult result1) + { + if (true == cancelRequested) + return; + + using (var requestStream = myRequest.EndGetRequestStream(result1)) + { + var data = Encoding.UTF8.GetBytes(post); + requestStream.Write(data, 0, data.Length); + } + + var async2 = myRequest.BeginGetResponse((result2) => + { + HandleEndResponse(doOnFinish, result2); + }, + null); + } + + protected void HandleEndResponse(Action doOnFinish, IAsyncResult result2) + { + try + { + var response = myRequest.EndGetResponse(result2); + using (var rs = response.GetResponseStream()) + { + using (var sr = new StreamReader(rs)) + { + var text = sr.ReadToEnd(); + this.dispatcher.BeginInvoke(() => + { + doOnFinish(text); + }); + } + } + } + catch (Exception exc) + { + ShowErrorMessage(exc); + } + } + + protected void ShowErrorMessage(Exception exc) + { + dispatcher.BeginInvoke(() => + { + childWindow.ShowError("Sorry - there was a problem - " + exc.Message); + }); + } + } + + public class ScriptUploader : CommunicationBase + { + ItemViewModel toUpload; + AccountViewModel account; + + public void Upload(AccountViewModel account, ItemViewModel toUpload, Dispatcher dispatcher) + { + this.account = account; + this.toUpload = toUpload; + this.dispatcher = dispatcher; + childWindow = new Views.StyledChildWindow(); + + var backgroundThread = new System.Threading.Thread(UploadProc); + backgroundThread.Start(); + + childWindow.Closed += (sender, args) => + { + cancelRequested = true; + try + { + if (myRequest != null) + myRequest.Abort(); + } + catch + { + // ignore problems :/ + } + }; + + childWindow.Show("uploading script '" + toUpload.Title + "'"); + childWindow.ShowCancelButton(); + } + + private bool TryLogOn(Action doOnSuccess) + { + // TODO - POST request sequence needed here... unless you do a nasty hacky combo... which might be worth it... + if (account.IsEmpty) + throw new ApplicationException("to upload scripts, you must register with script.iron7.com and provide your account information in 'Share online'"); + + var url = "http://script.iron7.com/Account/LogOn"; + string post = string.Format( + "UserName={0}&Password={1}", + Uri.EscapeDataString(account.UserName), + Uri.EscapeDataString(account.Password)); + + DoPost(url, post, (s) => { + if (s.Contains("success")) + { + doOnSuccess(); + } + else + { + childWindow.ShowError("Error - logon failed - please check your password and your network connection"); + } + }); + + return true; + } + + private void UploadProc() + { + try + { + TryLogOn(() => + { + DoCodePost(); + }); + } + catch (Exception exc) + { + ShowErrorMessage(exc); + } + } + + private void DoCodePost() + { + var url = "http://script.iron7.com/Script/Upsert"; + string post = string.Format( + "Code={0}&ScriptId={1}&Title={2}&TagsAsText={3}", + Uri.EscapeDataString(toUpload.Code), + Uri.EscapeDataString(toUpload.UniqueId), + Uri.EscapeDataString(toUpload.Title), + Uri.EscapeDataString(toUpload.CategoryTag)); + + DoPost(url, post, (s) => + { + if (s.Contains("success")) + { + childWindow.Close(); + } + else + { + childWindow.ShowError("Error - sorry - failed to update the online script"); + } + }); + } + + } +} diff --git a/Hosts/Silverlight/Iron7/Utils/VisualExtensions.cs b/Hosts/Silverlight/Iron7/Utils/VisualExtensions.cs new file mode 100644 index 0000000000..559f6c9715 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Utils/VisualExtensions.cs @@ -0,0 +1,30 @@ +using System; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Ink; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; +using System.Collections.Generic; + +namespace Iron7.Utils +{ + public static class VisualExtensions + { + // from http://stackoverflow.com/questions/1784369/silverlight-find-all-controls-of-type-in-layout + public static IEnumerable Descendents(this DependencyObject root) + { + int count = VisualTreeHelper.GetChildrenCount(root); + for (int i = 0; i < count; i++) + { + var child = VisualTreeHelper.GetChild(root, i); + yield return child; + foreach (var descendent in Descendents(child)) + yield return descendent; + } + } + } +} diff --git a/Hosts/Silverlight/Iron7/ViewModels/AccountViewModel.cs b/Hosts/Silverlight/Iron7/ViewModels/AccountViewModel.cs new file mode 100644 index 0000000000..edf276efac --- /dev/null +++ b/Hosts/Silverlight/Iron7/ViewModels/AccountViewModel.cs @@ -0,0 +1,71 @@ +using System; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Ink; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; +using System.IO.IsolatedStorage; + +namespace Iron7 +{ + public class AccountViewModel : BaseViewModel + { + private string _username; + public string UserName + { + get + { + return _username; + } + set + { + if (value == _username) + return; + + _username = value; + NotifyPropertyChanged("UserName"); + } + } + + private string _password; + public string Password + { + get + { + return _password; + } + set + { + if (value == _password) + return; + + _password = value; + NotifyPropertyChanged("Password"); + } + } + + public void Save(string path) + { + WriteTextFile(path, Newtonsoft.Json.JsonConvert.SerializeObject(this)); + } + + public static AccountViewModel LoadFrom(IsolatedStorageFile isf, string path) + { + string text = ReadTextFile(isf, path); + var deserialized = Newtonsoft.Json.JsonConvert.DeserializeObject(text); + return deserialized; + } + + public bool IsEmpty + { + get + { + return string.IsNullOrEmpty(this.UserName) || string.IsNullOrEmpty(this.Password); + } + } + } +} diff --git a/Hosts/Silverlight/Iron7/ViewModels/BaseViewModel.cs b/Hosts/Silverlight/Iron7/ViewModels/BaseViewModel.cs new file mode 100644 index 0000000000..ae40226250 --- /dev/null +++ b/Hosts/Silverlight/Iron7/ViewModels/BaseViewModel.cs @@ -0,0 +1,141 @@ +using System; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Ink; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; +using System.ComponentModel; +using System.IO.IsolatedStorage; +using System.IO; + +namespace Iron7 +{ + public class BaseViewModel : INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + + protected void NotifyPropertyChanged(String propertyName) + { + PropertyChangedEventHandler handler = PropertyChanged; + if (null != handler) + { + handler(this, new PropertyChangedEventArgs(propertyName)); + } + } + + protected static void WriteTextFile(string path, string text) + { + using (var isf = IsolatedStorageFile.GetUserStoreForApplication()) + { + WriteTextFile(isf, path, text); + } + } + + protected static void WriteTextFile(IsolatedStorageFile isf, string path, string text) + { + using (var fs = isf.CreateFile(path)) + { + using (var sw = new StreamWriter(fs)) + { + sw.Write(text); + } + } + } + + protected static string ReadTextFile(string path) + { + using (var isf = IsolatedStorageFile.GetUserStoreForApplication()) + { + return ReadTextFile(isf, path); + } + } + + protected static string ReadTextFile(IsolatedStorageFile isf, string path) + { + string text; + using (var fileStream = isf.OpenFile(path, FileMode.Open)) + { + using (StreamReader sr = new StreamReader(fileStream)) + { + text = sr.ReadToEnd(); + } + } + return text; + } + } + + public class AsyncLoadingViewModel : BaseViewModel + { + + private bool _asyncLoading; + public bool AsyncLoading + { + get + { + return _asyncLoading; + } + set + { + _asyncLoading = value; + NotifyPropertyChanged("AsyncLoading"); + } + } + + protected void DoGenericLoad(System.Windows.Threading.Dispatcher dispatcher, string urlToCall, Action funcToCall) + { + var request = (HttpWebRequest)WebRequest.Create(urlToCall); + AsyncLoading = true; + request.Accept = "application/json"; + try + { + var async = request.BeginGetResponse((result) => + { + try + { + var response = request.EndGetResponse(result); + using (var rs = response.GetResponseStream()) + { + using (var sr = new StreamReader(rs)) + { + var text = sr.ReadToEnd(); + dispatcher.BeginInvoke(() => + { + try + { + AsyncLoading = false; + funcToCall(text); + } + catch (Exception exc) + { + ShowError(dispatcher, exc); + } + }); + } + } + } + catch (Exception exc) + { + ShowError(dispatcher, exc); + } + }, + null); + } + catch (Exception exc) + { + ShowError(dispatcher, exc); + } + } + + protected static void ShowError(System.Windows.Threading.Dispatcher dispatcher, Exception exc) + { + dispatcher.BeginInvoke(() => + { + MessageBox.Show("Sorry - there was a problem - " + exc.Message); + }); + } + } +} diff --git a/Hosts/Silverlight/Iron7/ViewModels/ItemViewModel.cs b/Hosts/Silverlight/Iron7/ViewModels/ItemViewModel.cs new file mode 100644 index 0000000000..4684cedc12 --- /dev/null +++ b/Hosts/Silverlight/Iron7/ViewModels/ItemViewModel.cs @@ -0,0 +1,262 @@ +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Ink; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; +using Newtonsoft.Json; +using Iron7.Common; +using System.IO.IsolatedStorage; +using System.IO; + +namespace Iron7 +{ + public class ItemViewModel : BaseViewModel + { + private string _uniqueId; + public string UniqueId + { + get { return _uniqueId; } + set + { + if (value == _uniqueId) + return; + + _uniqueId = value; + NotifyPropertyChanged("UniqueId"); + } + } + + private long _whenLastModified; + public long WhenLastModified + { + get + { + return _whenLastModified; + } + set + { + if (value == _whenLastModified) + return; + _whenLastModified = value; + NotifyPropertyChanged("WhenLastModified"); + } + } + + private string _title; + /// + /// Sample ViewModel property; this property is used in the view to display its value using a Binding. + /// + /// + public string Title + { + get + { + return _title; + } + set + { + if (value != _title) + { + _title = value; + NotifyPropertyChanged("Title"); + } + } + } + + private bool _hasWebPermission; + public bool HasWebPermission + { + get { return _hasWebPermission; } + set + { + if (_hasWebPermission == value) + return; + + _hasWebPermission = value; + NotifyPropertyChanged("HasWebPermission"); + } + } + + private bool _hasGPSPermission; + public bool HasGPSPermission + { + get { return _hasGPSPermission; } + set + { + if (_hasGPSPermission == value) + return; + + _hasGPSPermission = value; + NotifyPropertyChanged("HasGPSPermission"); + } + } + + private string _code; + /// + /// Sample ViewModel property; this property is used in the view to display its value using a Binding. + /// + /// + [JsonIgnore] + public string Code + { + get + { + if (_code == null) + { + LoadCode(); + } + return _code; + } + set + { + if (value != _code) + { + _code = value; + NotifyPropertyChanged("Code"); + } + } + } + + private string _categoryTag; + /// + /// Sample ViewModel property; this property is used in the view to display its value using a Binding. + /// + /// + public string CategoryTag + { + get + { + return _categoryTag; + } + set + { + if (value != _categoryTag) + { + _categoryTag = value; + NotifyPropertyChanged("CategoryTag"); + } + } + } + + private string _author; + public string Author + { + get + { + return _author; + } + set + { + if (value == _author) + return; + + _author = value; + NotifyPropertyChanged("Author"); + } + } + + // This should not be here - I must learn how to use Blend! (and Silverlight! + [Newtonsoft.Json.JsonIgnore] + public SolidColorBrush HighlightColorBrush + { + get + { + return Common.WhichTheme.IsLight() ? + new SolidColorBrush(Colors.Green) : + new SolidColorBrush(Color.FromArgb(255, 127, 255, 63)); + } + } + + public void SetAll(string code, string title, string tags, bool canUseLocation, bool canUseWeb) + { + Code = code; + Title = title; + CategoryTag = tags; + HasGPSPermission = canUseLocation; + HasWebPermission = canUseWeb; + } + + public ItemViewModel() + { + UniqueId = Guid.NewGuid().ToString("N"); + WhenLastModified = DateTime.UtcNow.Ticks; + } + + public void DeleteFromStore() + { + using (var isf = IsolatedStorageFile.GetUserStoreForApplication()) + { + if (false == isf.DirectoryExists(Constants.MyScriptsDirectoryName)) + { + isf.CreateDirectory(Constants.MyScriptsDirectoryName); + } + var path = GetIndexPath(); + if (isf.FileExists(path)) + isf.DeleteFile(path); + var textPath = GetCodePath(); + if (isf.FileExists(textPath)) + isf.DeleteFile(textPath); + } + } + + public static ItemViewModel LoadItemIndexFrom(IsolatedStorageFile isf, string path) + { + string text = ReadTextFile(isf, path); + var deserialized = Newtonsoft.Json.JsonConvert.DeserializeObject(text); + return deserialized; + } + + private string GetIndexPath() + { + return string.Format("\\{0}\\{1}.index", Constants.MyScriptsDirectoryName, UniqueId); + } + + private string GetCodePath() + { + return string.Format("\\{0}\\{1}.code", Constants.MyScriptsDirectoryName, UniqueId); + } + + public void Store() + { + using (var isf = IsolatedStorageFile.GetUserStoreForApplication()) + { + if (false == isf.DirectoryExists(Constants.MyScriptsDirectoryName)) + { + isf.CreateDirectory(Constants.MyScriptsDirectoryName); + } + + var path = GetIndexPath(); + + var text = JsonConvert.SerializeObject(this); + WriteTextFile(isf, path, text); + + if (_code != null) + { + var textPath = GetCodePath(); + WriteTextFile(isf, textPath, _code); + } + } + } + + private void LoadCode() + { + try + { + var path = GetCodePath(); + _code = ReadTextFile(path); + } + catch (Exception) + { + MessageBox.Show("Sorry - there was a problem reading the code file"); + _code = "Sorry - there was a problem reading the code file"; + } + } + } +} \ No newline at end of file diff --git a/Hosts/Silverlight/Iron7/ViewModels/MainViewModel.cs b/Hosts/Silverlight/Iron7/ViewModels/MainViewModel.cs new file mode 100644 index 0000000000..8ebc3c6ad6 --- /dev/null +++ b/Hosts/Silverlight/Iron7/ViewModels/MainViewModel.cs @@ -0,0 +1,268 @@ +using System; +using System.Linq; +using System.ComponentModel; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; +using System.Collections.ObjectModel; +using System.IO.IsolatedStorage; +using System.IO; +using Iron7.Common; +using System.Windows.Navigation; +using Newtonsoft.Json; +using System.Net; + +namespace Iron7 +{ + public class MainViewModel : AsyncLoadingViewModel + { + public MainViewModel() + { + this.Items = new ObservableCollection(); + this.OnlineTags = new ObservableCollection(); + Account = new AccountViewModel(); + } + + /// + /// A collection for ItemViewModel objects. + /// + public ObservableCollection Items { get; private set; } + + private string _sampleProperty = "Sample Runtime Property Value"; + /// + /// Sample ViewModel property; this property is used in the view to display its value using a Binding + /// + /// + public string SampleProperty + { + get + { + return _sampleProperty; + } + set + { + if (value != _sampleProperty) + { + _sampleProperty = value; + NotifyPropertyChanged("SampleProperty"); + } + } + } + + /// + /// The user's Account data + /// + public AccountViewModel Account { get; set; } + + public bool IsDataLoaded + { + get; + private set; + } + + public void LoadData() + { + LoadAccountData(); + LoadScripts(); + } + + private void LoadAccountData() + { + using (var isf = IsolatedStorageFile.GetUserStoreForApplication()) + { + if (isf.FileExists(Constants.MyAccountFileName)) + { + try + { + Account = AccountViewModel.LoadFrom(isf, Constants.MyAccountFileName); + } + catch (Exception exc) + { + MessageBox.Show("Sorry - there was a problem loading your Iron7 account details - " + exc.Message); + } + } + } + } + + public void LoadOnlineTags(System.Windows.Threading.Dispatcher dispatcher) + { + var url = "http://script.iron7.com/Script/Index"; + base.DoGenericLoad(dispatcher, url, OnSuccessfulLoad); + } + + private void OnSuccessfulLoad(string text) + { + var items = JsonConvert.DeserializeObject(text); + OnlineTags.Clear(); + foreach (var item in items) + { + var model = new OnlineTagListViewModel() + { + Tag = item.Tag, + Count = item.Count + }; + OnlineTags.Add(model); + } + } + + /// + /// Online scripts + /// + public ObservableCollection OnlineTags { get; set; } + + /// + /// Creates and adds a few ItemViewModel objects into the Items collection. + /// + private void LoadScripts() + { + using (var isf = IsolatedStorageFile.GetUserStoreForApplication()) + { + // for debug only... + //DeleteAllFiles(isf); + + bool errorShown = false; + if (false == isf.DirectoryExists(Constants.MyScriptsDirectoryName)) + { + CreateDefaultData(); + } + + var files = isf.GetFileNames(string.Format("{0}\\*.index", Constants.MyScriptsDirectoryName)); + var list = new List(); + foreach (var f in files) + { + if (false == f.EndsWith(".index")) + { + // note sure why this happens... + continue; + } + + try + { + var path = string.Format("{0}\\{1}", Constants.MyScriptsDirectoryName, f); + list.Add(LoadItem(isf, path)); + } + catch (Exception exc) + { + if (errorShown == false) + { + errorShown = true; + MessageBox.Show("Sorry - there was a problem loading one or more of your scripts"); + } + } + } + list.Sort((x, y) => + { + // return reverse time order + return -x.WhenLastModified.CompareTo(y.WhenLastModified); + }); + this.Items = new ObservableCollection(); + foreach (var item in list) + { + this.Items.Add(item); + } + } + + this.IsDataLoaded = true; + } + + private static void DeleteAllFiles(IsolatedStorageFile isf) + { + if (false == isf.DirectoryExists(Constants.MyScriptsDirectoryName)) + return; + + var filesDel = isf.GetFileNames(string.Format("{0}\\*", Constants.MyScriptsDirectoryName)); + foreach (var f in filesDel) + { + isf.DeleteFile(Constants.MyScriptsDirectoryName + "\\" + f); + } + isf.DeleteDirectory(Constants.MyScriptsDirectoryName); + } + + private ItemViewModel LoadItem(IsolatedStorageFile isf, string path) + { + return ItemViewModel.LoadItemIndexFrom(isf, path); + } + + private void StoreItem(ItemViewModel item) + { + item.Store(); + } + + public void CreateNewItemBasedOn(ItemViewModel currentItem, NavigationService navigationService, bool includeCopyText = true, bool copyUniqueId = false) + { + var newItem = new ItemViewModel() + { + Title = (includeCopyText ? "From " : string.Empty) + currentItem.Title, + Code = currentItem.Code, + HasGPSPermission = currentItem.HasGPSPermission, + HasWebPermission = currentItem.HasWebPermission, + CategoryTag = currentItem.CategoryTag + }; + if (copyUniqueId) + newItem.UniqueId = currentItem.UniqueId; + StoreItem(newItem); + + App.ViewModel.Items.Insert(0, newItem); + + // TODO - get navigation service out of here! + if (navigationService != null) + { + navigationService.Navigate(new Uri("/Views/EditPage.xaml?ScriptIndex=" + newItem.UniqueId, UriKind.Relative)); + } + } + + public void CreateDefaultData() + { + var items = new List(); + + // just included for experimentation + //items.Add(new ItemViewModel() { Title = "CSharp1", Code = ScriptResources.CSharpDemo, CategoryTag = "simple" }); + + items.Add(new ItemViewModel() { Title = "Hello World", Code = AppResources.StaticHelloWorld, CategoryTag = "simple" }); + items.Add(new ItemViewModel() { Title = "Ruby 101", Code = AppResources.RubyIntro, CategoryTag = "ruby" }); + items.Add(new ItemViewModel() { Title = "Clock", Code = AppResources.GraphicsExample, CategoryTag = "canvas, timer" }); + items.Add(new ItemViewModel() { Title = "Fractal Tree", Code = AppResources.FractalTree, CategoryTag = "canvas, timer, math" }); + items.Add(new ItemViewModel() { Title = "FingerPaint", Code = AppResources.Drawing, CategoryTag = "canvas, event" }); + items.Add(new ItemViewModel() { Title = "Button Game", Code = AppResources.ButtonGame, CategoryTag = "game" }); + items.Add(new ItemViewModel() { Title = "Snake Game", Code = AppResources.SnakeGame, CategoryTag = "game, sensor, canvas" }); + items.Add(new ItemViewModel() { Title = "SquaresIV", Code = AppResources.square_aliens, CategoryTag = "game, canvas, image" }); + items.Add(new ItemViewModel() { Title = "Tetris", Code = AppResources.Tetris, CategoryTag = "canvas, timer, game" }); + items.Add(new ItemViewModel() { Title = "Twitter", Code = AppResources.Twitter, CategoryTag = "network, json" }); + + /* + items.Add(new ItemViewModel() { Title = "A Button", Code = AppResources.DynamicExamples, CategoryTag = "simple, event" }); + items.Add(new ItemViewModel() { Title = "A Timer", Code = AppResources.TimerExample, CategoryTag = "simple, timer" }); + items.Add(new ItemViewModel() { Title = "Hello Map", Code = AppResources.FirstMap, CategoryTag = "tutorial, map" }); + items.Add(new ItemViewModel() { Title = "Mandelbrot Set", Code = AppResources.MandelBrotDrawing, CategoryTag = "canvas, timer, math" }); + items.Add(new ItemViewModel() { Title = "Text Add", Code = AppResources.LogicExample, CategoryTag = "simple, event" }); + items.Add(new ItemViewModel() { Title = "Number Add", Code = AppResources.MathsExample, CategoryTag = "simple, event" }); + items.Add(new ItemViewModel() { Title = "Flickr", Code = AppResources.Flickr, CategoryTag = "network, webtext" }); + items.Add(new ItemViewModel() { Title = "Accelerometer", Code = AppResources.Accelerometer, CategoryTag = "sensor, canvas" }); + items.Add(new ItemViewModel() { Title = "SpaceShip", Code = AppResources.AccelerometerSpace, CategoryTag = "sensor, image" }); + items.Add(new ItemViewModel() { Title = "Location", Code = AppResources.MapWithLocation, CategoryTag = "sensor, map" }); + items.Add(new ItemViewModel() { Title = "Squares", Code = AppResources.square_drawing, CategoryTag = "canvas, timer" }); + items.Add(new ItemViewModel() { Title = "SquaresII", Code = AppResources.square_animating, CategoryTag = "canvas, timer" }); + items.Add(new ItemViewModel() { Title = "SquaresIII", Code = AppResources.square_animating_game, CategoryTag = "game, canvas" }); + items.Add(new ItemViewModel() { Title = "Circles", Code = AppResources.circle_drawing, CategoryTag = "canvas, touch" }); + */ + long fakeWhenLastModified = DateTime.UtcNow.Ticks; + long oneHour = TimeSpan.FromHours(1.0).Ticks; + foreach (var i in items) + { + fakeWhenLastModified -= oneHour; + i.WhenLastModified = fakeWhenLastModified; + i.UniqueId = i.Title; + i.Author = "iron7"; + StoreItem(i); + } + } + } +} \ No newline at end of file diff --git a/Hosts/Silverlight/Iron7/ViewModels/OnlineScriptViewModel.cs b/Hosts/Silverlight/Iron7/ViewModels/OnlineScriptViewModel.cs new file mode 100644 index 0000000000..1c4df1ad02 --- /dev/null +++ b/Hosts/Silverlight/Iron7/ViewModels/OnlineScriptViewModel.cs @@ -0,0 +1,21 @@ +using System; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Ink; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; + +namespace Iron7 +{ + public class OnlineScriptViewModel + { + public string Author { get; set; } + public string Title { get; set; } + public string ScriptId { get; set; } + + } +} diff --git a/Hosts/Silverlight/Iron7/ViewModels/OnlineScriptsViewModel.cs b/Hosts/Silverlight/Iron7/ViewModels/OnlineScriptsViewModel.cs new file mode 100644 index 0000000000..500c8e4415 --- /dev/null +++ b/Hosts/Silverlight/Iron7/ViewModels/OnlineScriptsViewModel.cs @@ -0,0 +1,112 @@ +using System; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Ink; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; +using System.Collections.ObjectModel; +using System.IO; +using Newtonsoft.Json; +using Iron7.Iron7Server; + +namespace Iron7 +{ + public class OnlineScriptsViewModel : AsyncLoadingViewModel + { + public OnlineScriptsViewModel() + { + Scripts = new ObservableCollection(); + } + + public string TagAndCount + { + get + { + return Tag + " (" + Count.ToString() + ")"; + } + } + + public int Count { get; set; } + + private string _tag; + public string Tag + { + get + { + return _tag; + } + set + { + _tag = value; + NotifyPropertyChanged("Tag"); + } + } + + private bool _loaded; + public bool Loaded + { + get + { + return _loaded; + } + set + { + _loaded = value; + NotifyPropertyChanged("Loaded"); + } + } + + Visibility _moreVisibility = Visibility.Collapsed; + public Visibility MoreVisibility + { + get + { + return _moreVisibility; + } + set + { + if (_moreVisibility == value) + return; + _moreVisibility = value; + NotifyPropertyChanged("MoreVisibility"); + } + + } + + public ObservableCollection Scripts { get; set; } + + public string MoreUrl { get; set; } + + public void LoadUrl(System.Windows.Threading.Dispatcher dispatcher, string url) + { + string urlToCall = "http://script.iron7.com" + url + "&r=" + new Random().Next(10000).ToString(); + Action funcToCall = OnSuccessfulLoad; + DoGenericLoad(dispatcher, urlToCall, funcToCall); + } + + private void OnSuccessfulLoad(string text) + { + var listing = JsonConvert.DeserializeObject(text); + Scripts.Clear(); + foreach (var item in listing.Scripts) + { + var model = new OnlineScriptViewModel() + { + Author = item.AuthorName, + Title = item.Title, + ScriptId = item.ScriptId + }; + Scripts.Add(model); + } + if (listing.MoreAvailable) + { + this.MoreVisibility = Visibility.Visible; + this.MoreUrl = listing.NextPageUrl; + } + } + } +} diff --git a/Hosts/Silverlight/Iron7/ViewModels/OnlineTagListViewModel.cs b/Hosts/Silverlight/Iron7/ViewModels/OnlineTagListViewModel.cs new file mode 100644 index 0000000000..24bd57c761 --- /dev/null +++ b/Hosts/Silverlight/Iron7/ViewModels/OnlineTagListViewModel.cs @@ -0,0 +1,71 @@ +using System; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Ink; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; + +namespace Iron7 +{ + public class OnlineTagListViewModel : BaseViewModel + { + public string TagAndCount + { + get + { + return Tag + " (" + Count.ToString() + ")"; + } + } + + public int Count { get; set; } + + private string _tag; + public string Tag + { + get + { + return _tag; + } + set + { + _tag = value; + NotifyPropertyChanged("Tag"); + } + } + + private bool _loaded; + public bool Loaded + { + get + { + return _loaded; + } + set + { + _loaded = value; + NotifyPropertyChanged("Loaded"); + } + } + + Visibility _moreVisibility = Visibility.Collapsed; + public Visibility MoreVisibility + { + get + { + return _moreVisibility; + } + set + { + if (_moreVisibility == value) + return; + _moreVisibility = value; + NotifyPropertyChanged("MoreVisibility"); + } + + } + } +} diff --git a/Hosts/Silverlight/Iron7/Views/BaseDetailPage.cs b/Hosts/Silverlight/Iron7/Views/BaseDetailPage.cs new file mode 100644 index 0000000000..6a16382c70 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Views/BaseDetailPage.cs @@ -0,0 +1,35 @@ +using System; +using System.Linq; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Ink; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; +using Microsoft.Phone.Controls; + +namespace Iron7.Views +{ + public class BaseDetailPage : PhoneApplicationPage + { + protected Brush BackgroundBrush + { + get + { + var url = Common.WhichTheme.IsLight() + ? "/Assets/jimkster_2846315611_light.png" + : "/Assets/jimkster_2846315611_dark.png"; + return new ImageBrush() + { + ImageSource = new System.Windows.Media.Imaging.BitmapImage(new Uri(url, UriKind.Relative)), + Stretch = Stretch.None, + AlignmentX = AlignmentX.Left, + AlignmentY = AlignmentY.Top + }; + } + } + } +} diff --git a/Hosts/Silverlight/Iron7/Views/BaseItemPage.cs b/Hosts/Silverlight/Iron7/Views/BaseItemPage.cs new file mode 100644 index 0000000000..8454cf62a2 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Views/BaseItemPage.cs @@ -0,0 +1,45 @@ +using System; +using System.Linq; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Ink; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; +using Microsoft.Phone.Controls; + +namespace Iron7.Views +{ + public class BaseItemPage : BaseDetailPage + { + protected ItemViewModel CurrentItem { get; private set; } + + protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e) + { + base.OnNavigatedTo(e); + + string scriptIndex = NavigationContext.QueryString["ScriptIndex"]; + + CurrentItem = App.ViewModel.Items.FirstOrDefault(x => x.UniqueId == scriptIndex); + + if (CurrentItem == null) + { + MessageBox.Show("Sorry - script not found", Common.Constants.Title, MessageBoxButton.OK); + try + { + NavigationService.GoBack(); + } + catch (InvalidOperationException) + { + // just mask this problem + } + return; + } + + DataContext = CurrentItem; + } + } +} diff --git a/Hosts/Silverlight/Iron7/Views/CodeMirrorCredits.xaml b/Hosts/Silverlight/Iron7/Views/CodeMirrorCredits.xaml new file mode 100644 index 0000000000..ac0ecacf67 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Views/CodeMirrorCredits.xaml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Hosts/Silverlight/Iron7/Views/CodeMirrorCredits.xaml.cs b/Hosts/Silverlight/Iron7/Views/CodeMirrorCredits.xaml.cs new file mode 100644 index 0000000000..ef22fd2a47 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Views/CodeMirrorCredits.xaml.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; +using Microsoft.Phone.Controls; + +namespace Iron7.Views +{ + public partial class CodeMirrorCredits : PhoneApplicationPage + { + public CodeMirrorCredits() + { + InitializeComponent(); + } + } +} \ No newline at end of file diff --git a/Hosts/Silverlight/Iron7/Views/EditPage.xaml b/Hosts/Silverlight/Iron7/Views/EditPage.xaml new file mode 100644 index 0000000000..24d540e5e4 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Views/EditPage.xaml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Hosts/Silverlight/Iron7/Views/EditPage.xaml.cs b/Hosts/Silverlight/Iron7/Views/EditPage.xaml.cs new file mode 100644 index 0000000000..d19f04d855 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Views/EditPage.xaml.cs @@ -0,0 +1,288 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; +using Microsoft.Phone.Controls; +using System.Windows.Threading; +using Microsoft.Phone.Tasks; +using System.Windows.Navigation; + +namespace Iron7.Views +{ + public partial class EditPage : BaseItemPage + { + private int failedSets = 0; + + private enum EditorState + { + None, + WaitingToNavigate, + Navigating, + Navigated, + AlmostFullyInitialised, + FullyInitialised, + MovedAway + } + + EditorState currentState = EditorState.None; + + DispatcherTimer timer; + + public EditPage() + { + InitializeComponent(); + webBrowser1.Visibility = Visibility.Collapsed; + webBrowser1.Base = "WebContent"; + webBrowser1.Navigated += new EventHandler(webBrowser1_Navigated); + //webBrowser1.Background = BackgroundBrush; + } + + void timer_Tick(object sender, EventArgs e) + { + switch (currentState) + { + case EditorState.None: + case EditorState.WaitingToNavigate: + try + { + webBrowser1.Navigate(new Uri("baseEditor.html", UriKind.Relative)); + currentState = EditorState.Navigating; + ProgressBar.Visibility = Visibility.Visible; + ProgressBar.IsIndeterminate = true; + LoadingBlock.Visibility = Visibility.Visible; + } + catch + { + // do nothing... + } + break; + case EditorState.Navigating: + break; + case EditorState.Navigated: + if (CurrentItem != null) + try + { + if (failedSets == 10) + { + MessageBox.Show("Sorry - the editor is taking a long time", Common.Constants.Title, MessageBoxButton.OK); + } + + var setOK = (string)webBrowser1.InvokeScript("showCode", CurrentItem.Code); + + if (setOK == "1") + { + webBrowser1.InvokeScript("setBackground", Common.WhichTheme.IsLight() ? "true" : "false"); + currentState = EditorState.AlmostFullyInitialised; + } + else + { + failedSets++; + } + } + catch (Exception) + { + // do nothing - the webbrowser can be flakey :/ + failedSets++; + } + break; + case EditorState.AlmostFullyInitialised: + webBrowser1.Visibility = Visibility.Visible; + ProgressBar.Visibility = Visibility.Collapsed; + LoadingBlock.Visibility = Visibility.Collapsed; + currentState = EditorState.FullyInitialised; + break; + + case EditorState.FullyInitialised: + // do nothing + timer.Stop(); + break; + case EditorState.MovedAway: + // do nothing + break; + default: + break; + } + } + + protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e) + { + webBrowser1.Visibility = Visibility.Collapsed; + base.OnNavigatedTo(e); + if (CurrentItem == null) + return; + + currentState = EditorState.WaitingToNavigate; + if (timer == null) + { + timer = new DispatcherTimer(); + timer.Interval = TimeSpan.FromSeconds(0.5); + timer.Tick += new EventHandler(timer_Tick); + } + timer.Start(); + } + + private bool PerformCodeUpdates() + { + if (currentState == EditorState.FullyInitialised) + { + try + { + var code = (string)webBrowser1.InvokeScript("getCode"); + CurrentItem.Code = code; + } + catch (Exception exc) + { + Dispatcher.BeginInvoke(() => + { + MessageBox.Show("Sorry - there was a problem with the editor - " + exc.Message); + }); + return false; + } + } + return true; + } + + protected override void OnNavigatingFrom(System.Windows.Navigation.NavigatingCancelEventArgs e) + { + if (false == PerformCodeUpdates()) + { + e.Cancel = true; + return; + } + + currentState = EditorState.MovedAway; + base.OnNavigatingFrom(e); + } + + private bool TestHtmlCheckbox(string checkboxName) + { + var obj = webBrowser1.InvokeScript(checkboxName); + if (obj == null) + return false; + + if (obj.ToString() == "1") + return true; + + return false; + } + + void webBrowser1_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e) + { + if (currentState == EditorState.Navigating) + currentState = EditorState.Navigated; + } + + private void ApplicationBarPlay_Click (object sender, EventArgs e) + { + if (false == PerformCodeUpdates()) + return; + + NavigationService.Navigate(new Uri("/Views/IronPage.xaml?ScriptIndex=" + CurrentItem.UniqueId, UriKind.Relative)); + } + + private void ApplicationBarCopyToNew_Click(object sender, EventArgs e) + { + if (false == PerformCodeUpdates()) + return; + + App.ViewModel.CreateNewItemBasedOn(CurrentItem, NavigationService); + } + + private void ApplicationBarDelete_Click(object sender, EventArgs e) + { + if (MessageBox.Show("Are you sure?", "Delete", MessageBoxButton.OKCancel) != MessageBoxResult.OK) + return; + + try + { + App.ViewModel.Items.Remove(CurrentItem); + NavigationService.GoBack(); + } + catch + { + // I've never seen an exception here... but just in case (because of the mail task problems) + } + } + + private void ApplicationBarEmail_Click(object sender, EventArgs e) + { + if (false == PerformCodeUpdates()) + return; + + try + { + var sb = new System.Text.StringBuilder(); + sb.Append("Hello!\r\nHere's my latest script\r\nI am sending it to you under a Creative Commons 2.0 Attribution license\r\nYou may use this script however you wish - as long as you include my twitter id - !HERE!\r\n"); + sb.Append("\r\nThe script is called: " + this.CurrentItem.Title); + sb.Append("\r\nThe script is of type: " + this.CurrentItem.CategoryTag); + //sb.Append("\r\nI fully understand that you may decide not to use this script and that you do not need to send me any reason for this decision."); + //sb.Append("\r\nI fully understand that you may have other similar scripts or similar ideas in the past or in the future, and I have no right or claim over any of them."); + sb.Append("\r\nHere's my script\r\n"); + sb.Append(this.CurrentItem.Code); + + EmailComposeTask emailComposeTask = new EmailComposeTask(); + //emailComposeTask.To = "scripts@iron7.com"; + emailComposeTask.Subject = "My Script:\r\n\r\n" + this.CurrentItem.Title; + emailComposeTask.Body = sb.ToString(); + emailComposeTask.Show(); + } + catch (InvalidOperationException) + { + // see http://blogs.msdn.com/b/oren/archive/2010/12/01/wp7-gotcha-launchers-will-crash-while-navigating.aspx + } + } + + private void ApplicationBarProperties_Click(object sender, EventArgs e) + { + if (false == PerformCodeUpdates()) + return; + + NavigationService.Navigate(new Uri("/Views/PropertiesPage.xaml?ScriptIndex=" + CurrentItem.UniqueId, UriKind.Relative)); + } + + private void ApplicationBarShareToIron7_Click(object sender, EventArgs e) + { + if (false == PerformCodeUpdates()) + return; + + var u = new Utils.ScriptUploader(); + u.Upload(App.ViewModel.Account, this.CurrentItem, this.Dispatcher); + } + + private void ApplicationBarUpdateFromIron7_Click(object sender, EventArgs e) + { + var d = new Utils.ScriptDownloader(); + var item = new OnlineScriptViewModel + { + Author = App.ViewModel.Account.UserName, + ScriptId = base.CurrentItem.UniqueId, + Title = base.CurrentItem.Title + }; + d.Download(item, this.Dispatcher, + (simpleScriptDetail) => + { + base.CurrentItem.CategoryTag = simpleScriptDetail.TagsAsText; + base.CurrentItem.Title = simpleScriptDetail.Title; + base.CurrentItem.Code = simpleScriptDetail.Code; + base.CurrentItem.WhenLastModified = DateTime.UtcNow.Ticks; + try + { + webBrowser1.InvokeScript("showCode", CurrentItem.Code); + } + catch (Exception exc) + { + // ho hum! + MessageBox.Show("Sorry - there was a problem updating the editor - " + exc.Message); + } + }); + } + + } +} \ No newline at end of file diff --git a/Hosts/Silverlight/Iron7/Views/IIronScriptHost.cs b/Hosts/Silverlight/Iron7/Views/IIronScriptHost.cs new file mode 100644 index 0000000000..77d17beb6f --- /dev/null +++ b/Hosts/Silverlight/Iron7/Views/IIronScriptHost.cs @@ -0,0 +1,25 @@ +using System; +using System.Windows; +using System.Windows.Controls; +namespace Iron7.Views +{ + public interface IIronScriptHost + { + void Vibrate(TimeSpan duration); + void CallJsonService(string name, string url, string callback); + void CallTextWebService(string name, string url, string callback); + void LoadSoundEffect(string name, string url, string callback); + void FixOrientationLandscape(); + void FixOrientationPortrait(); + void StartAccelerometer(double secondsBetweenReadings, string readingCallback); + void StartGeoCoordinateWatcher(string statusCallback, string positionCallback); + void StartGeoCoordinateWatcher(string needHigh, string statusCallback, string positionCallback); + void StartTimer(string name, TimeSpan interval, string callback); + void ChangeTimer(string name, TimeSpan interval); + void StopTimer(string name); + void MonitorControl(object sender, object element, string callback); + Grid ContentHolder { get; } + void MonitorComposition(object hint, string callback); + void MonitorTouchPoints(object hint, string callback); + } +} diff --git a/Hosts/Silverlight/Iron7/Views/IronPage.xaml b/Hosts/Silverlight/Iron7/Views/IronPage.xaml new file mode 100644 index 0000000000..1b5dd36308 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Views/IronPage.xaml @@ -0,0 +1,25 @@ + + + + + + + + + + + diff --git a/Hosts/Silverlight/Iron7/Views/IronPage.xaml.cs b/Hosts/Silverlight/Iron7/Views/IronPage.xaml.cs new file mode 100644 index 0000000000..e61bd9e531 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Views/IronPage.xaml.cs @@ -0,0 +1,891 @@ +using System; +using System.Collections.Generic; +using System.Device.Location; +using System.IO; +using System.Linq; +using System.Net; +using System.Reflection; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Shapes; +using System.Windows.Threading; +using Iron7.Common; +using Iron7.Utils; +using IronRuby; +using Microsoft.Devices; +using Microsoft.Devices.Sensors; +using Microsoft.Phone.Controls; +using Microsoft.Phone.Controls.Maps; +using Microsoft.Scripting.Hosting; +using Newtonsoft.Json; +using Microsoft.Scripting; +using System.Windows.Media.Animation; + +namespace Iron7.Views +{ + public partial class IronPage : BaseItemPage, IIronScriptHost + { + DispatcherTimer startTimer = null; + const int MaxConcurrentUIDispatchesAllowed = 1; + int numberOfDispatchesActive = 0; + private Dictionary Timers { get; set; } + + public IronPage() + { + InitializeComponent(); + Timers = new Dictionary(); + this.LayoutRoot.Background = BackgroundBrush; + } + + private int progressVisibilityCount = 0; + public void ShowProgress() + { + lock (this) + { + progressVisibilityCount++; + ProgressBar.Visibility = Visibility.Visible; + ProgressBar.IsIndeterminate = true; + } + } + + public void HideProgress() + { + lock (this) + { + progressVisibilityCount--; + if (progressVisibilityCount <= 0) + { + ProgressBar.Visibility = Visibility.Collapsed; + } + } + } + + protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e) + { + base.OnNavigatedTo(e); + + if (CurrentItem == null) + return; + + ShowProgress(); + + // start timer just delays the launch - just to allow the screen to update + startTimer = new DispatcherTimer(); + startTimer.Interval = TimeSpan.FromSeconds(0.5); + startTimer.Tick += new EventHandler(startTimer_Tick); + startTimer.Start(); + } + + void startTimer_Tick(object sender, EventArgs e) + { + startTimer.Stop(); + InitialiseEngine(); + HideProgress(); + } + + protected override void OnNavigatingFrom(System.Windows.Navigation.NavigatingCancelEventArgs e) + { + try + { + foreach (var t in this.Timers) + { + t.Value.Stop(); + } + } + catch + { + // mask any errors here + } + if (startTimer != null) + { + startTimer.Stop(); + startTimer = null; + } + foreach (var t in Timers) + { + t.Value.Stop(); + } + if (monitoringCompositionCallback != null) + { + CompositionTarget.Rendering -= new EventHandler(CompositionTarget_Rendering); + monitoringCompositionCallback = null; + monitoringCompositionHint = null; + } + if (geoCoordinateWatcher != null) + { + geoCoordinateWatcher.Stop(); + geoCoordinateWatcher = null; + } + if (accelerometer != null) + { + accelerometer.Stop(); + accelerometer = null; + } + if (engine != null) + { + engine = null; + } + if (scope != null) + { + scope = null; + } + base.OnNavigatingFrom(e); + } + + #region IIronScriptHost implementation + + private string monitoringCompositionCallback = null; + private object monitoringCompositionHint = null; + + public void MonitorComposition(object hint, string callback) + { + if (monitoringCompositionCallback != null) + return; + + monitoringCompositionHint = hint; + monitoringCompositionCallback = callback; + + CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering); + } + + void CompositionTarget_Rendering(object sender, EventArgs e) + { + GenericScriptAction("compositiontarget", monitoringCompositionHint, null, "compositiontarget_rendering", monitoringCompositionCallback); + } + + private string monitoringTouchCallback = null; + private object monitoringTouchHint = null; + + public void MonitorTouchPoints(object hint, string callback) + { + if (monitoringCompositionCallback != null) + return; + + monitoringTouchHint = hint; + monitoringCompositionCallback = callback; + + Touch.FrameReported += new TouchFrameEventHandler(Touch_FrameReported); + } + + void Touch_FrameReported(object sender, TouchFrameEventArgs e) + { + GenericScriptAction("touch", monitoringTouchHint, null, "touch_frame_reported", monitoringTouchCallback); + } + + public void Vibrate(TimeSpan duration) + { + var totalSeconds = duration.TotalSeconds; + if (totalSeconds<0 || totalSeconds>1) + return; + + VibrateController vc = VibrateController.Default; + vc.Start(duration); + } + + public void StartTimer(string name, TimeSpan interval, string callback) + { + if (Timers.ContainsKey(name)) + return; + + if (interval.TotalMilliseconds < 20) + interval = TimeSpan.FromMilliseconds(20); + + DispatcherTimer timer = new DispatcherTimer(); + timer.Interval = interval; + timer.Tick += (sender, args) => + { + SafeUIInvoke(() => + { + GenericScriptAction("timer", name, null, "timer_tick", callback); + }, + false); + }; + timer.Start(); + } + + public void ChangeTimer(string name, TimeSpan interval) + { + DispatcherTimer timer; + if (false == Timers.TryGetValue(name, out timer)) + return; + + if (interval.TotalMilliseconds < 20) + interval = TimeSpan.FromMilliseconds(20); + + timer.Interval = interval; + } + + public void StopTimer(string name) + { + DispatcherTimer timer; + if (false == Timers.TryGetValue(name, out timer)) + return; + + timer.Stop(); + Timers.Remove(name); + } + + public void LoadSoundEffect(string name, string url, string callback) + { + CallWebServiceInner(name, url, (Stream stream) => + { + var soundEffect = Microsoft.Xna.Framework.Audio.SoundEffect.FromStream(stream); + + SafeUIInvoke(() => + { + try + { + GenericScriptAction("soundeffect", name, new SoundEffectPlayer(soundEffect), "sound_effect_loaded", callback); + } + catch (Exception exc) + { + ShowMessageBox("Error seen processing sound effect " + exc.Message); + } + finally + { + HideProgress(); + } + }, + true); + }); + } + + public void MonitorControl(object hint, object element, string callback) + { + if (element is Button) + { + (element as Button).Click += (sender, args) => + { + GenericScriptAction("uielement", hint, element, "button_clicked", callback); + }; + } + if (element is Pivot) + { + var pivot = element as Pivot; + pivot.LoadedPivotItem += (sender, args) => + { + var dict = new Dictionary(); + dict["Pivot_item_event_args"] = args; + GenericScriptAction("uielement", hint, element, "pivot_loaded_pivot_item", callback, dict); + }; + pivot.LoadingPivotItem += (sender, args) => + { + var dict = new Dictionary(); + dict["Pivot_item_event_args"] = args; + GenericScriptAction("uielement", hint, element, "pivot_loading_pivot_item", callback, dict); + }; + pivot.SelectionChanged += (sender, args) => + { + var dict = new Dictionary(); + dict["Selection_changed_event_args"] = args; + GenericScriptAction("uielement", hint, element, "pivot_selection_changed", callback, dict); + }; + pivot.UnloadedPivotItem += (sender, args) => + { + var dict = new Dictionary(); + dict["Pivot_item_event_args"] = args; + GenericScriptAction("uielement", hint, element, "pivot_unloaded_pivot_item", callback, dict); + }; + pivot.UnloadingPivotItem += (sender, args) => + { + var dict = new Dictionary(); + dict["Pivot_item_event_args"] = args; + GenericScriptAction("uielement", hint, element, "pivot_unloading_pivot_item", callback, dict); + }; + } + if (element is Panorama) + { + var panorama = element as Panorama; + panorama.SelectionChanged += (sender, args) => + { + var dict = new Dictionary(); + dict["Selection_changed_event_args"] = args; + GenericScriptAction("uielement", hint, element, "panorama_selection_changed", callback, dict); + }; + } + if (element is MediaElement) + { + var media_element = element as MediaElement; + media_element.BufferingProgressChanged += (sender, args) => + { + GenericScriptAction("uielement", hint, element, "media_element_buffering_progress_changed", callback); + }; + media_element.CurrentStateChanged += (sender, args) => + { + GenericScriptAction("uielement", hint, element, "media_element_current_state_changed", callback); + }; + media_element.DownloadProgressChanged += (sender, args) => + { + GenericScriptAction("uielement", hint, element, "media_element_download_progress_changed", callback); + }; + media_element.MediaOpened += (sender, args) => + { + GenericScriptAction("uielement", hint, element, "media_element_media_opened", callback); + }; + media_element.MediaFailed += (sender, args) => + { + var dict = new Dictionary(); + dict["Error_exception"] = args.ErrorException; + GenericScriptAction("uielement", hint, element, "media_element_media_failed", callback, dict); + }; + media_element.MediaEnded += (sender, args) => + { + GenericScriptAction("uielement", hint, element, "media_element_media_ended", callback); + }; + } + else if (element is Storyboard) + { + (element as Storyboard).Completed += (sender, args) => + { + GenericScriptAction("uielement", hint, element, "storyboard_completed", callback); + }; + } + else if (element is TextBox) + { + (element as TextBox).TextChanged += (sender, args) => + { + GenericScriptAction("uielement", hint, element, "text_changed", callback); + }; + } + else if (element is Map) + { + var map = element as Map; + map.MapPan += (sender, args) => + { + Dictionary dict = new Dictionary(); + dict["MapDrag"] = args; + GenericScriptAction("uielement", hint, element, "map_panned", callback); + }; + map.MapZoom += (sender, args) => + { + Dictionary dict = new Dictionary(); + dict["MapZoom"] = args; + GenericScriptAction("uielement", hint, element, "map_zoomed", callback); + }; + } + else if (element is ListBox) + { + var lb = element as ListBox; + lb.SelectionChanged += (sender, args) => + { + GenericScriptAction("uielement", hint, element, "list_box_selection_changed", callback); + }; + } + else if (element is ComboBox) + { + var cb = element as ComboBox; + cb.SelectionChanged += (sender, args) => + { + GenericScriptAction("uielement", hint, element, "combo_box_selection_changed", callback); + }; + } + else if (element is CheckBox) + { + var cb = element as CheckBox; + cb.Checked += (sender, args) => + { + GenericScriptAction("uielement", hint, element, "check_box_checked", callback); + }; + cb.Unchecked += (sender, args) => + { + GenericScriptAction("uielement", hint, element, "check_box_unchecked", callback); + }; + } + else if (element is Slider) + { + var slider = element as Slider; + slider.ValueChanged += (sender, args) => + { + GenericScriptAction("uielement", hint, element, "slider_value_changed", callback); + }; + } + else if (element is MediaElement) + { + var me = element as MediaElement; + me.CurrentStateChanged += (sender, args) => + { + GenericScriptAction("uielement", hint, element, "media_element_current_state_changed", callback); + }; + } + else if (element is RadioButton) + { + var rb = element as RadioButton; + rb.Checked += (sender, args) => + { + GenericScriptAction("uielement", hint, element, "radio_button_checked", callback); + }; + } + else if (element is PasswordBox) + { + var pb = element as PasswordBox; + pb.PasswordChanged += (sender, args) => + { + GenericScriptAction("uielement", hint, element, "password_box_password_changed", callback); + }; + } + else if (element is HyperlinkButton) + { + (element as HyperlinkButton).Click += (sender, args) => + { + GenericScriptAction("uielement", hint, element, "hyperlink_button_clicked", callback); + }; + } + else if (element is Image) + { + var image = element as Image; + image.ImageFailed += (sender, args) => + { + var dict = new Dictionary(); + dict["Error_exception"] = args.ErrorException; + GenericScriptAction("uielement", hint, element, "image_failed", callback, dict); + }; + image.ImageOpened += (sender, args) => + { + GenericScriptAction("uielement", hint, element, "image_opened", callback); + }; + } + else if (element is WebBrowser) + { + var wb = element as WebBrowser; + wb.Navigating += (sender, args) => + { + var dict = new Dictionary(); + dict["WebBrowserNavigating"] = args; + GenericScriptAction("uielement", hint, element, "web_browser_navigating", callback); + }; + wb.Navigated += (sender, args) => + { + var dict = new Dictionary(); + dict["WebBrowserNavigation"] = args; + GenericScriptAction("uielement", hint, element, "web_browser_navigated", callback, dict); + }; + } + + + if (ShouldRespondToMouse(element)) + { + UIElement uielement = (element as UIElement); + uielement.MouseLeftButtonDown += (sender, args) => + { + var mouseArgs = GetMouseArgs(uielement, args); + GenericScriptAction("uielement", hint, element, "mouse_left_button_down", callback, mouseArgs); + args.Handled = true; + }; + + uielement.MouseLeftButtonUp += (sender, args) => + { + var mouseArgs = GetMouseArgs(uielement, args); + GenericScriptAction("uielement", hint, element, "mouse_left_button_up", callback, mouseArgs); + args.Handled = true; + }; + + uielement.MouseMove += (sender, args) => + { + var mouseArgs = GetMouseArgs(uielement, args); + GenericScriptAction("uielement", hint, element, "mouse_move", callback, mouseArgs); + }; + } + } + + private bool ShouldRespondToMouse(object element) + { + if (element is Canvas) + return true; + + if (element is Image) + return true; + + if (element is Shape) + return true; + + return false; + } + + private static Dictionary GetMouseArgs(UIElement element, MouseEventArgs args) + { + var mouseArgs = new Dictionary(); + mouseArgs["Mouse_x"] = args.GetPosition(element).X; + mouseArgs["Mouse_y"] = args.GetPosition(element).Y; + return mouseArgs; + } + + public Grid ContentHolder + { + get + { + return ContentGrid; + } + } + + private GeoCoordinateWatcher geoCoordinateWatcher; + + public void StartGeoCoordinateWatcher(string statusCallback, string readingCallback) + { + StartGeoCoordinateWatcher(string.Empty, statusCallback, readingCallback); + } + + public void StartGeoCoordinateWatcher(string needHigh, string statusCallback, string readingCallback) + { + if (geoCoordinateWatcher != null) + return; + + if (false == GetGPSPermission()) + return; + + GeoPositionAccuracy accuracy = GeoPositionAccuracy.Default; + if (needHigh == "high") + accuracy = GeoPositionAccuracy.High; + + geoCoordinateWatcher = new GeoCoordinateWatcher(accuracy); + geoCoordinateWatcher.StatusChanged += (sender, args) => + { + var dict = new Dictionary(); + dict["GeoStatusChange"] = (int)args.Status; + + SafeUIInvoke(() => + { + GenericScriptAction("geoCoordinateWatcher", "geoCoordinateWatcher", null, "status_changed", statusCallback, dict); + }, true); + }; + + geoCoordinateWatcher.PositionChanged += (sender, args) => + { + var dict = new Dictionary(); + dict["GeoPositionChange"] = args; + + SafeUIInvoke(() => + { + GenericScriptAction("geoCoordinateWatcher", "geoCoordinateWatcher", null, "position_changed", readingCallback, dict); + }, + false); + }; + geoCoordinateWatcher.Start(); + } + + private bool GetGPSPermission() + { + if (CurrentItem == null) + return false; + + if (CurrentItem.HasGPSPermission) + return true; + + if (MessageBox.Show("Allow this script to access your position?", CurrentItem.Title, MessageBoxButton.OKCancel) != MessageBoxResult.OK) + { + return false; + } + + CurrentItem.HasGPSPermission = true; + return true; + } + + private bool GetWebPermission() + { + if (CurrentItem == null) + return false; + + if (CurrentItem.HasWebPermission) + return true; + + if (MessageBox.Show("Allow this script to access Internet based content?", CurrentItem.Title, MessageBoxButton.OKCancel) != MessageBoxResult.OK) + { + return false; + } + + CurrentItem.HasWebPermission = true; + return true; + } + + private void SafeUIInvoke(Action toInvoke, bool isImportant = false) + { + // we need to keep the number of dispatches to a safe level - otherwise the UI can crawl to a halt + // - e.g. if too many accelerometer reading + if (false == isImportant + && numberOfDispatchesActive >= MaxConcurrentUIDispatchesAllowed) + return; + + System.Threading.Interlocked.Increment(ref numberOfDispatchesActive); + Dispatcher.BeginInvoke(() => + { + try + { + toInvoke(); + } + finally + { + System.Threading.Interlocked.Decrement(ref numberOfDispatchesActive); + } + }); + } + + private Accelerometer accelerometer; + DateTime timeLastAccelerometerReadingSent = DateTime.MinValue; + TimeSpan timeBetweenReadings; + + public void StartAccelerometer(double secondsBetweenReadings, string readingCallback) + { + if (accelerometer != null) + return; + + if (secondsBetweenReadings < 0.01) + secondsBetweenReadings = 0.01; + + timeBetweenReadings = TimeSpan.FromSeconds(secondsBetweenReadings); + + if (accelerometer != null) + return; + + accelerometer = new Accelerometer(); + accelerometer.Start(); + accelerometer.ReadingChanged += (object sender, AccelerometerReadingEventArgs e) => + { + // we have to rate limit because the script is too slow + DateTime now = DateTime.Now; + if (now - timeLastAccelerometerReadingSent < timeBetweenReadings) + return; + + timeLastAccelerometerReadingSent = now; + var dict = new Dictionary(); + dict["AccelerometerReading"] = e; + + SafeUIInvoke(() => + { + dict["Orientation"] = Orientation; + GenericScriptAction("accelerometer", "accelerometer", null, "reading_changed", readingCallback, dict); + }, false); + }; + } + + public void FixOrientationLandscape() + { + this.SupportedOrientations = SupportedPageOrientation.Landscape; + } + + public void FixOrientationPortrait() + { + this.SupportedOrientations = SupportedPageOrientation.Portrait; + } + + public void CallJsonService(string sender, string url, string callback) + { + CallWebServiceInner(sender, url, (string s) => + { + var dict = new Dictionary(); + dict["Web_response"] = s; + dict["Json_response"] = JsonConvert.DeserializeObject(s); // would be nice to use DynamicJson.ConvertJsonStringToObject(s); + GenericScriptAction("json_service", sender, null, url, callback, dict); + }); + } + + public void CallTextWebService(string sender, string url, string callback) + { + CallWebServiceInner(sender, url, (string s) => + { + var dict = new Dictionary(); + dict["Web_response"] = s; + GenericScriptAction("web_service", sender, null, url, callback, dict); + }); + } + + private void CallWebServiceInner(string sender, string url, Action onResponse) + { + CallWebServiceInner(sender, url, (Stream stream) => + { + using (var sr = new StreamReader(stream)) + { + string response = sr.ReadToEnd(); + SafeUIInvoke(() => + { + try + { + onResponse(response); + } + catch (Exception exc) + { + ShowMessageBox("Error seen processing web response " + exc.Message); + } + finally + { + HideProgress(); + } + }, + true); + } + }); + } + + private void CallWebServiceInner(string sender, string url, Action onResponse) + { + if (false == GetWebPermission()) + return; + + ShowProgress(); + + var webRequest = WebRequest.Create(url); + try + { + webRequest.BeginGetResponse((firstResult) => + { + try + { + var webResponse = webRequest.EndGetResponse(firstResult); + using (var s = webResponse.GetResponseStream()) + { + onResponse(s); + } + } + catch (Exception exc) + { + ShowMessageBox("Error seen in web response " + exc.Message); + SafeUIInvoke(() => { HideProgress(); } + , true); + } + }, null); + } + catch (Exception exc) + { + ShowMessageBox("Error seen in web request " + exc.Message); + HideProgress(); + } + } + + #endregion + + #region UIHelper + + private void ShowMessageBox(string message, bool useDispatcher=true) + { + if (useDispatcher) + { + SafeUIInvoke(() => + { + MessageBox.Show(message, Constants.Title, MessageBoxButton.OK); + }, + true); + } + else + { + MessageBox.Show(message, Constants.Title, MessageBoxButton.OK); + } + } + + #endregion + + #region Script Hookup + + private void InitialiseEngine() + { + // Allow both portrait and landscape orientations + SupportedOrientations = SupportedPageOrientation.PortraitOrLandscape; + + // Create an IronRuby engine and prevent compilation + //var setup = new ScriptRuntimeSetup(); + //setup.DebugMode = true; + //setup.AddRubySetup(); + //var rt = Ruby.CreateRuntime(setup); + engine = Ruby.CreateEngine((ls) => + { + ls.ExceptionDetail = true; + }); + + // Load the System.Windows.Media assembly to the IronRuby context + engine.Runtime.LoadAssembly(typeof(Color).Assembly); + // Load the Bing Maps assembly to the IronRuby context + engine.Runtime.LoadAssembly(typeof(Map).Assembly); + // Load the Phone controls assembly to the IronRuby context + engine.Runtime.LoadAssembly(typeof(Pivot).Assembly); + // Load the Phone Toolkit controls assembly to the IronRuby context + engine.Runtime.LoadAssembly(typeof(Microsoft.Phone.Controls.ToggleSwitch).Assembly); + // Load the GeoCoordinate assembly - useful if you want to handplot maps + engine.Runtime.LoadAssembly(typeof(GeoCoordinate).Assembly); + + // TODO - in future need to look at XML and JSON + // Allow xml parsing + // engine.Runtime.LoadAssembly(typeof(System.Xml.Linq.XDocument).Assembly); + // Add a global constant named Phone, which will allow access to this class + engine.Runtime.Globals.SetVariable("Host", new IronScriptHostProxy(this)); + + // Read the IronRuby code + Assembly execAssembly = Assembly.GetExecutingAssembly(); + + // Create the IronRuby scope + scope = engine.CreateScope(); + + GenericScriptAction(string.Empty, null, null, string.Empty, CurrentItem.Code); + } + + #endregion + + #region the script! + + ScriptEngine engine; + ScriptScope scope; + bool executionStopped; + + private void GenericScriptAction(string method, object hint, object sender, string eventName, string ruby, IDictionary extraVariables = null) + { + if (engine == null) + return; + + if (executionStopped) + return; + + engine.Runtime.Globals.SetVariable("Calling_hint", hint); + engine.Runtime.Globals.SetVariable("Calling_method", method); + engine.Runtime.Globals.SetVariable("Calling_sender", sender); + engine.Runtime.Globals.SetVariable("Calling_event", eventName); + + if (extraVariables != null) + { + foreach (var kvp in extraVariables) + { + engine.Runtime.Globals.SetVariable(kvp.Key, kvp.Value); + } + } + try + { + engine.Execute(ruby, scope); + } + catch (SyntaxErrorException exc) + { + var exceptionHelper = new RubySyntaxExceptionHelper(exc); + ShowScriptErrorMessage(exceptionHelper); + } + catch (Exception exc) + { + RubyExceptionHelper exceptionHelper = new RubyExceptionHelper(engine, exc); + ShowScriptErrorMessage(exceptionHelper); + } + CheckForSneakyWebControl(); + } + + private void ShowScriptErrorMessage(RubyExceptionHelperBase exceptionHelper) + { + var toShow = exceptionHelper.LongErrorText(this.CurrentItem.Code); + if (MessageBox.Show(toShow, Constants.Title, MessageBoxButton.OKCancel) != MessageBoxResult.OK) + { + executionStopped = true; + } + } + + #endregion + + private void CheckForSneakyWebControl() + { + if (true == CurrentItem.HasWebPermission) + return; + + var qryAllBrowsers = this.LayoutRoot.Descendents().OfType(); + if (qryAllBrowsers.Count() == 0) + return; + + if (false == GetWebPermission()) + { + executionStopped = true; + NavigationService.GoBack(); + } + } + } +} \ No newline at end of file diff --git a/Hosts/Silverlight/Iron7/Views/IronScriptHostProxy.cs b/Hosts/Silverlight/Iron7/Views/IronScriptHostProxy.cs new file mode 100644 index 0000000000..ceff6fe953 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Views/IronScriptHostProxy.cs @@ -0,0 +1,107 @@ +using System; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Ink; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; + +namespace Iron7.Views +{ + public class IronScriptHostProxy : IIronScriptHost + { + readonly IIronScriptHost realHost; + + public IronScriptHostProxy(IIronScriptHost realHost) + { + this.realHost = realHost; + } + + public void Vibrate(TimeSpan duration) + { + this.realHost.Vibrate(duration); + } + + public void LoadSoundEffect(string name, string url, string callback) + { + realHost.LoadSoundEffect(name, url, callback); + } + + public void CallJsonService(string name, string url, string callback) + { + realHost.CallJsonService(name, url, callback); + } + + public void CallTextWebService(string name, string url, string callback) + { + realHost.CallTextWebService(name, url, callback); + } + + public void FixOrientationLandscape() + { + realHost.FixOrientationLandscape(); + } + + public void FixOrientationPortrait() + { + realHost.FixOrientationPortrait(); + } + + public void StartAccelerometer(double secondsBetweenReadings, string callback) + { + realHost.StartAccelerometer(secondsBetweenReadings, callback); + } + + public void StartGeoCoordinateWatcher(string statusCallback, string readingCallback) + { + realHost.StartGeoCoordinateWatcher(statusCallback, readingCallback); + } + + public void StartGeoCoordinateWatcher(string needHigh, string statusCallback, string readingCallback) + { + realHost.StartGeoCoordinateWatcher(needHigh, statusCallback, readingCallback); + } + + public void StartTimer(string name, TimeSpan interval, string callback) + { + realHost.StartTimer(name, interval, callback); + } + + public void ChangeTimer(string name, TimeSpan interval) + { + realHost.ChangeTimer(name, interval); + } + + public void StopTimer(string name) + { + realHost.StopTimer(name); + } + + + public void MonitorControl(object sender, object element, string callback) + { + realHost.MonitorControl(sender, element, callback); + } + + public Grid ContentHolder + { + get + { + return realHost.ContentHolder; + } + } + + public void MonitorComposition(object hint, string callback) + { + realHost.MonitorComposition(hint, callback); + } + + public void MonitorTouchPoints(object hint, string callback) + { + realHost.MonitorTouchPoints(hint, callback); + } + } +} diff --git a/Hosts/Silverlight/Iron7/Views/MainPage.xaml b/Hosts/Silverlight/Iron7/Views/MainPage.xaml new file mode 100644 index 0000000000..e172c65ce6 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Views/MainPage.xaml @@ -0,0 +1,182 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Hosts/Silverlight/Iron7/Views/MainPage.xaml.cs b/Hosts/Silverlight/Iron7/Views/MainPage.xaml.cs new file mode 100644 index 0000000000..799e31704f --- /dev/null +++ b/Hosts/Silverlight/Iron7/Views/MainPage.xaml.cs @@ -0,0 +1,276 @@ +///#define Iron7Free + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; +using Microsoft.Phone.Controls; +using Microsoft.Phone.Tasks; +using Iron7.Common; + +namespace Iron7.Views +{ + public partial class MainPage : PhoneApplicationPage + { + // Constructor + public MainPage() + { + InitializeComponent(); + + // Set the data context of the listbox control to the sample data + DataContext = App.ViewModel; + this.Loaded += new RoutedEventHandler(MainPage_Loaded); + + UpdateCachedTrialResult(); + + if (CachedTrialResult == TrialResult.Full) + { + this.Purchase1.Visibility = Visibility.Collapsed; + this.Purchase2.Visibility = Visibility.Collapsed; + } + else + { + this.ThanksPurchase1.Visibility = Visibility.Collapsed; + } + + var url = Common.WhichTheme.IsLight() + ? "/Assets/jimkster_2846315611_light_header.png" + : "/Assets/jimkster_2846315611_dark_header.png"; + + Panorama1.Background = new ImageBrush() + { + ImageSource = new System.Windows.Media.Imaging.BitmapImage(new Uri(url, UriKind.Relative)) + }; + } + + private void ButtonCreateNew_Simple_Click(object sender, RoutedEventArgs e) + { + CreateNewCommon("script", "simple", AppResources.NewAppTemplate); + } + + private void ButtonCreateNew_Location_Click(object sender, RoutedEventArgs e) + { + CreateNewCommon("location", "location", AppResources.NewLocationAppTemplate); + } + + private void ButtonCreateNew_Canvas_Click(object sender, RoutedEventArgs e) + { + CreateNewCommon("canvas", "canvas", AppResources.NewCanvasTemplate); + } + + private void ButtonCreateNew_Click(object sender, RoutedEventArgs e) + { + CreateNewCommon("script", "simple", string.Empty); + } + + private void ButtonCreateNew_Accelerometer_Click(object sender, RoutedEventArgs e) + { + CreateNewCommon("sensor", "sensor", AppResources.NewAccelerometerAppTemplate); + } + + private void ButtonCreateNew_EventBased_Click(object sender, RoutedEventArgs e) + { + CreateNewCommon("events", "simple", AppResources.NewButtonAndTimerAppTemplate); + } + + private void CreateNewCommon(string title, string tags, string code) + { + var selectedItem = new ItemViewModel() + { + CategoryTag = tags, + Code = code, + Title = title + }; + App.ViewModel.CreateNewItemBasedOn(selectedItem, NavigationService, false); + } + + // Load data for the ViewModel Items + private void MainPage_Loaded(object sender, RoutedEventArgs e) + { + if (!App.ViewModel.IsDataLoaded) + { + App.ViewModel.LoadData(); + } + } + + private void HyperlinkButtonCirrious_Click(object sender, RoutedEventArgs e) + { + ShowWebPage("http://www.cirrious.com/"); + } + + private void PinwheelGalaxyHyperlinkButton_Click(object sender, RoutedEventArgs e) + { + ShowWebPage("http://m.flickr.com/#/photos/jimkster/2846315611/"); + } + + private void CodeMirrorHyperlinkButton_Click(object sender, RoutedEventArgs e) + { + NavigationService.Navigate(new Uri("/Views/CodeMirrorCredits.xaml", UriKind.Relative)); + } + + private void HyperlinkButton_Click(object sender, RoutedEventArgs e) + { + ShowWebPage("http://ironruby.codeplex.com/"); + } + + private void HyperlinkButtonCreateMsdnCom_Click(object sender, RoutedEventArgs e) + { + ShowWebPage("http://create.msdn.com/"); + } + + private void CreativeCommonsHyperlinkButton_Click(object sender, RoutedEventArgs e) + { + ShowWebPage("http://creativecommons.org/about/licenses/"); + } + + private void HyperlinkButtonScriptIron7_Click(object sender, RoutedEventArgs e) + { + ShowWebPage("http://script.iron7.com/"); + } + + private void HyperlinkButtonIron7_Click(object sender, RoutedEventArgs e) + { + ShowWebPage("http://www.iron7.com/"); + } + + private void ButtonDownloadScript_Click(object sender, RoutedEventArgs e) + { + NavigationService.Navigate(new Uri("/Views/TagsOnlinePage.xaml", UriKind.Relative)); + } + + private void ButtonMyScripts_Click(object sender, RoutedEventArgs e) + { + if (string.IsNullOrEmpty(App.ViewModel.Account.UserName)) + { + MessageBox.Show("To download your scripts, you must register with script.iron7.com and provide your account information in 'Share online'", Constants.Title, MessageBoxButton.OK); + return; + } + var url = new Uri("/Views/ScriptListOnlinePage.xaml?Url=" + Uri.EscapeDataString("/Script/UserScripts?userName=" + App.ViewModel.Account.UserName), UriKind.Relative); + NavigationService.Navigate(url); + } + + private void ButtonIron7Scripts_Click(object sender, RoutedEventArgs e) + { + var url = new Uri("/Views/ScriptListOnlinePage.xaml?Url=" + Uri.EscapeDataString("/Script/UserScripts?userName=iron7"), UriKind.Relative); + NavigationService.Navigate(url); + } + + //private void TwitterHyperlinkButton_Click(object sender, RoutedEventArgs e) + //{ + // NavigationService.Navigate(new Uri("/Views/EditPage.xaml", UriKind.Relative)); + //} + + private void ShowWebPage(string url) + { + var task = new WebBrowserTask() + { + URL = url + }; + task.Show(); + } + + private void ButtonListItem_Click(object sender, EventArgs e) + { + var button = sender as Button; + if (null != button) + { + if (CheckLicense() == false) + { + DoMarketPlaceMessage(); + return; + } + var item = button.Tag as ItemViewModel; + if (null != item) + { + NavigationService.Navigate(new Uri("/Views/EditPage.xaml?ScriptIndex=" + item.UniqueId, UriKind.Relative)); + //NavigationService.Navigate(new Uri("/Views/EditPage.xaml?ScriptIndex=" + App.ViewModel.Items[ListBoxScripts.SelectedIndex].UniqueId, UriKind.Relative)); + } + } + } + + private void PurchaseHyperlinkButton_Click(object sender, EventArgs e) + { + GoToMarketplace(); + } + + private bool DoMarketPlaceMessage() + { + if (MessageBox.Show("Thanks for using Iron7. Purchasing Iron7 helps support future development. Would you like to purchase now?", "Iron7", MessageBoxButton.OKCancel) == MessageBoxResult.OK) + { + GoToMarketplace(); + return false; + } + return true; + } + + private static void GoToMarketplace() + { +#if Iron7Free + var task = new Microsoft.Phone.Tasks.MarketplaceDetailTask() + { + ContentIdentifier = "10aa051d-e907-e011-9264-00237de2db9e" + }; +#else + var task = new Microsoft.Phone.Tasks.MarketplaceDetailTask(); +#endif + task.Show(); + } + + private const int Trial_Prompt_Every_N_Activities = 6; + + private enum TrialResult + { + Unknown, + Trial, + Full + } + + private TrialResult CachedTrialResult = TrialResult.Unknown; + + static int numChecks = 0; + + private bool CheckLicense() + { + UpdateCachedTrialResult(); + + if (CachedTrialResult == TrialResult.Full) + return true; + + numChecks++; + + // numChecks>0 every nth time at random we ask them about upgrading + if (numChecks > 3 && new Random().Next(Trial_Prompt_Every_N_Activities) == 0) + { + numChecks = 0; + return false; + } + + return true; + } + + private void UpdateCachedTrialResult() + { +#if Iron7Free + CachedTrialResult = TrialResult.Trial; +#else + if (CachedTrialResult == TrialResult.Unknown) + { + Microsoft.Phone.Marketplace.LicenseInformation license = new Microsoft.Phone.Marketplace.LicenseInformation(); + if (license.IsTrial()) + CachedTrialResult = TrialResult.Trial; + else + CachedTrialResult = TrialResult.Full; + } +#endif + } + + + } +} \ No newline at end of file diff --git a/Hosts/Silverlight/Iron7/Views/PropertiesPage.xaml b/Hosts/Silverlight/Iron7/Views/PropertiesPage.xaml new file mode 100644 index 0000000000..003deefef7 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Views/PropertiesPage.xaml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Hosts/Silverlight/Iron7/Views/PropertiesPage.xaml.cs b/Hosts/Silverlight/Iron7/Views/PropertiesPage.xaml.cs new file mode 100644 index 0000000000..a793a045e8 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Views/PropertiesPage.xaml.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; +using Microsoft.Phone.Controls; + +namespace Iron7.Views +{ + public partial class PropertiesPage : BaseItemPage + { + public PropertiesPage() + { + InitializeComponent(); + this.LayoutRoot.Background = BackgroundBrush; + } + } +} \ No newline at end of file diff --git a/Hosts/Silverlight/Iron7/Views/ScriptListOnlinePage.xaml b/Hosts/Silverlight/Iron7/Views/ScriptListOnlinePage.xaml new file mode 100644 index 0000000000..405f1118ac --- /dev/null +++ b/Hosts/Silverlight/Iron7/Views/ScriptListOnlinePage.xaml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Hosts/Silverlight/Iron7/Views/ScriptListOnlinePage.xaml.cs b/Hosts/Silverlight/Iron7/Views/ScriptListOnlinePage.xaml.cs new file mode 100644 index 0000000000..852b4a9f78 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Views/ScriptListOnlinePage.xaml.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; +using Microsoft.Phone.Controls; + +namespace Iron7.Views +{ + public partial class ScriptListOnlinePage : BaseDetailPage + { + private OnlineScriptsViewModel Model = null; + + public ScriptListOnlinePage() + { + InitializeComponent(); + LayoutRoot.Background = BackgroundBrush; + } + + protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e) + { + base.OnNavigatedTo(e); + + var param = NavigationContext.QueryString["Url"]; + if (string.IsNullOrEmpty(param)) + { + MessageBox.Show("Internal error - empty tag - sorry!"); + return; + } + + Model = new OnlineScriptsViewModel(); + this.DataContext = Model; + Model.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(model_PropertyChanged); + Model.LoadUrl(Dispatcher, param); + } + + void model_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) + { + if (e.PropertyName == "AsyncLoading") + { + this.ProgressBar.Visibility = ((OnlineScriptsViewModel)sender).AsyncLoading ? + Visibility.Visible : + Visibility.Collapsed; + } + } + + private void ButtonScript_Click(object sender, RoutedEventArgs e) + { + //NavigationService.Navigate(); + var d = new Utils.ScriptDownloader(); + var item = (sender as Button).Tag as OnlineScriptViewModel; + d.Download(item, this.Dispatcher, + (simpleScriptDetail) => + { + var baseItem = new ItemViewModel() + { + CategoryTag = simpleScriptDetail.TagsAsText, + Code = simpleScriptDetail.Code, + Title = simpleScriptDetail.Title, + UniqueId = simpleScriptDetail.ScriptId + }; + + bool sameAuthor = false; + if (App.ViewModel.Account.UserName != null) + sameAuthor = (simpleScriptDetail.AuthorName.ToLowerInvariant() == App.ViewModel.Account.UserName.ToLowerInvariant()); + + if (sameAuthor) + { + var existing = App.ViewModel.Items.Where(x => x.UniqueId == simpleScriptDetail.ScriptId).FirstOrDefault(); + if (existing != null) + { + var result = MessageBox.Show("Replace your current script?", Iron7.Common.Constants.Title, MessageBoxButton.OKCancel); + if (result != MessageBoxResult.OK) + return; + App.ViewModel.Items.Remove(existing); + } + } + App.ViewModel.CreateNewItemBasedOn(baseItem, NavigationService, !sameAuthor, sameAuthor); + }); + } + + private void ButtonMore_Click(object sender, RoutedEventArgs e) + { + var uri = new Uri("/Views/ScriptListOnlinePage.xaml?Url=" + Uri.EscapeDataString(Model.MoreUrl), UriKind.Relative); + NavigationService.Navigate(uri); + } + } +} \ No newline at end of file diff --git a/Hosts/Silverlight/Iron7/Views/StyledChildWindow.xaml b/Hosts/Silverlight/Iron7/Views/StyledChildWindow.xaml new file mode 100644 index 0000000000..581e5b8770 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Views/StyledChildWindow.xaml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + diff --git a/Hosts/Silverlight/Iron7/Views/TagsOnlinePage.xaml.cs b/Hosts/Silverlight/Iron7/Views/TagsOnlinePage.xaml.cs new file mode 100644 index 0000000000..ea3644e406 --- /dev/null +++ b/Hosts/Silverlight/Iron7/Views/TagsOnlinePage.xaml.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; +using Microsoft.Phone.Controls; + +namespace Iron7.Views +{ + public partial class TagsOnlinePage : BaseDetailPage + { + public TagsOnlinePage() + { + InitializeComponent(); + this.DataContext = App.ViewModel; + LayoutRoot.Background = BackgroundBrush; + App.ViewModel.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(ViewModel_PropertyChanged); + } + + void ViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) + { + if (e.PropertyName == "AsyncLoading") + { + ProgressBar.Visibility = App.ViewModel.AsyncLoading ? System.Windows.Visibility.Visible : System.Windows.Visibility.Collapsed; + } + } + + protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e) + { + base.OnNavigatedTo(e); + if (App.ViewModel.OnlineTags.Count == 0) + { + App.ViewModel.LoadOnlineTags(Dispatcher); + } + } + + private void ButtonTag_Click(object sender, RoutedEventArgs e) + { + var tag = (sender as Button).Tag.ToString(); + var url = new Uri("/Views/ScriptListOnlinePage.xaml?Url=" + Uri.EscapeDataString("/Script/ByTag?tag=" + tag), UriKind.Relative); + NavigationService.Navigate(url); + } + } +} \ No newline at end of file diff --git a/Hosts/Silverlight/Iron7/assets/imagelicense.txt b/Hosts/Silverlight/Iron7/assets/imagelicense.txt new file mode 100644 index 0000000000..17bc2607a1 --- /dev/null +++ b/Hosts/Silverlight/Iron7/assets/imagelicense.txt @@ -0,0 +1,3 @@ +Pinwheel Galaxy by jimkster on flickr + +Used under Creative Commons Attribution license \ No newline at end of file diff --git a/Hosts/Silverlight/Iron7/assets/jimkster_2846315611.png b/Hosts/Silverlight/Iron7/assets/jimkster_2846315611.png new file mode 100644 index 0000000000..d70a670c29 Binary files /dev/null and b/Hosts/Silverlight/Iron7/assets/jimkster_2846315611.png differ diff --git a/Hosts/Silverlight/Iron7/assets/jimkster_2846315611_dark.pdn b/Hosts/Silverlight/Iron7/assets/jimkster_2846315611_dark.pdn new file mode 100644 index 0000000000..ca584123d6 Binary files /dev/null and b/Hosts/Silverlight/Iron7/assets/jimkster_2846315611_dark.pdn differ diff --git a/Hosts/Silverlight/Iron7/assets/jimkster_2846315611_dark.png b/Hosts/Silverlight/Iron7/assets/jimkster_2846315611_dark.png new file mode 100644 index 0000000000..9ff2e58603 Binary files /dev/null and b/Hosts/Silverlight/Iron7/assets/jimkster_2846315611_dark.png differ diff --git a/Hosts/Silverlight/Iron7/assets/jimkster_2846315611_dark_header.pdn b/Hosts/Silverlight/Iron7/assets/jimkster_2846315611_dark_header.pdn new file mode 100644 index 0000000000..75924ef897 Binary files /dev/null and b/Hosts/Silverlight/Iron7/assets/jimkster_2846315611_dark_header.pdn differ diff --git a/Hosts/Silverlight/Iron7/assets/jimkster_2846315611_dark_header.png b/Hosts/Silverlight/Iron7/assets/jimkster_2846315611_dark_header.png new file mode 100644 index 0000000000..ea7a1146ea Binary files /dev/null and b/Hosts/Silverlight/Iron7/assets/jimkster_2846315611_dark_header.png differ diff --git a/Hosts/Silverlight/Iron7/assets/jimkster_2846315611_light.pdn b/Hosts/Silverlight/Iron7/assets/jimkster_2846315611_light.pdn new file mode 100644 index 0000000000..cd1f249aaa Binary files /dev/null and b/Hosts/Silverlight/Iron7/assets/jimkster_2846315611_light.pdn differ diff --git a/Hosts/Silverlight/Iron7/assets/jimkster_2846315611_light.png b/Hosts/Silverlight/Iron7/assets/jimkster_2846315611_light.png new file mode 100644 index 0000000000..755b13d9a4 Binary files /dev/null and b/Hosts/Silverlight/Iron7/assets/jimkster_2846315611_light.png differ diff --git a/Hosts/Silverlight/Iron7/assets/jimkster_2846315611_light_header.pdn b/Hosts/Silverlight/Iron7/assets/jimkster_2846315611_light_header.pdn new file mode 100644 index 0000000000..6d3faee036 Binary files /dev/null and b/Hosts/Silverlight/Iron7/assets/jimkster_2846315611_light_header.pdn differ diff --git a/Hosts/Silverlight/Iron7/assets/jimkster_2846315611_light_header.png b/Hosts/Silverlight/Iron7/assets/jimkster_2846315611_light_header.png new file mode 100644 index 0000000000..4d587c1b73 Binary files /dev/null and b/Hosts/Silverlight/Iron7/assets/jimkster_2846315611_light_header.png differ diff --git a/Hosts/Silverlight/Iron7/spacesquare100.png b/Hosts/Silverlight/Iron7/spacesquare100.png new file mode 100644 index 0000000000..be5b79d890 Binary files /dev/null and b/Hosts/Silverlight/Iron7/spacesquare100.png differ diff --git a/Hosts/Silverlight/Iron7/spacesquare173.png b/Hosts/Silverlight/Iron7/spacesquare173.png new file mode 100644 index 0000000000..6c94a8ea9f Binary files /dev/null and b/Hosts/Silverlight/Iron7/spacesquare173.png differ diff --git a/Hosts/Silverlight/Iron7/spacesquare200.png b/Hosts/Silverlight/Iron7/spacesquare200.png new file mode 100644 index 0000000000..f3d7a527b1 Binary files /dev/null and b/Hosts/Silverlight/Iron7/spacesquare200.png differ diff --git a/Hosts/Silverlight/Iron7/spacesquare62.png b/Hosts/Silverlight/Iron7/spacesquare62.png new file mode 100644 index 0000000000..d1aa0041a9 Binary files /dev/null and b/Hosts/Silverlight/Iron7/spacesquare62.png differ diff --git a/Hosts/Silverlight/Iron7/spacesquare99.png b/Hosts/Silverlight/Iron7/spacesquare99.png new file mode 100644 index 0000000000..ac0f7ea757 Binary files /dev/null and b/Hosts/Silverlight/Iron7/spacesquare99.png differ diff --git a/Hosts/Silverlight/Iron7/splashscreen.pdn b/Hosts/Silverlight/Iron7/splashscreen.pdn new file mode 100644 index 0000000000..60ab74cf3d --- /dev/null +++ b/Hosts/Silverlight/Iron7/splashscreen.pdn @@ -0,0 +1,1898 @@ +PDN3 OPaintDotNet.Data, Version=3.56.3972.42623, Culture=neutral, PublicKeyToken=null ISystem, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089PaintDotNet.Document +isDisposedlayerswidthheight userMetaData savedWithPaintDotNet.LayerList2System.Collections.Specialized.NameValueCollectionSystem.Version    PaintDotNet.LayerListparentArrayList+_itemsArrayList+_sizeArrayList+_versionPaintDotNet.Document  2System.Collections.Specialized.NameValueCollectionReadOnly HashProviderComparerCountKeysValuesVersion2System.Collections.CaseInsensitiveHashCodeProvider*System.Collections.CaseInsensitiveComparer + !System.Version_Major_Minor_Build _Revision8{    2System.Collections.CaseInsensitiveHashCodeProviderm_textSystem.Globalization.TextInfo  +*System.Collections.CaseInsensitiveComparer m_compareInfo System.Globalization.CompareInfo   +$exif.tag4 +$exif.tag8 +$exif.tag9 $exif.tag10      OPaintDotNet.Core, Version=3.56.3972.42620, Culture=neutral, PublicKeyToken=null PaintDotNet.BitmapLayer +propertiessurfaceLayer+isDisposed Layer+width Layer+heightLayer+properties-PaintDotNet.BitmapLayer+BitmapLayerPropertiesPaintDotNet.Surface!PaintDotNet.Layer+LayerProperties         ! "  #System.Globalization.TextInfom_listSeparator m_isReadOnlycustomCultureName m_nDataItemm_useUserOverride m_win32LangID + + System.Globalization.CompareInfo win32LCIDculturem_name$System.Collections.ArrayList_items_size_version % & ' (-PaintDotNet.BitmapLayer+BitmapLayerPropertiesblendOp&PaintDotNet.UserBlendOps+NormalBlendOp )PaintDotNet.Surfacescan0widthheightstridePaintDotNet.MemoryBlock * !PaintDotNet.Layer+LayerPropertiesname userMetaDatavisible isBackgroundopacity2System.Collections.Specialized.NameValueCollection+ +Background , - .  /Layer 5 0! 1" 2 #3Layer 4 4%5D&6/'77(87)&PaintDotNet.UserBlendOps+NormalBlendOp*PaintDotNet.MemoryBlocklength64 hasParentdeferred p, + ; <-).*p0 = > ? @1)2*p4 A B C D;<= E> + F?@A GB + HCDE + +F $G + +H $ N MԽדdizޗ޻tuuޕw]W{cgzάZXr%( FAR" )t+BпysNVVVuW./Ȭ̓'N}kmvl6a N؝.m;<쇇9<9.ush׺oOhzaw~v-w8Uagt:r{~VFgaemorߒ8{lv~qx97v.Gָ99~yr>2l!ptr|NOp>8܇; #r!@@B{]xp{\։ߏpk&vv{|& }/'^ Gx|GpyG}.;t6~9p8g6c\ܗc)y8tk./7Xx^t!3 ?Iu>xN!}ߎ`y^~/U> `w"D0u~¾(?A8p܍H( + QNeqů)fBWwjP_7N`h8ѡLAow! {gz0<2LNN`b Ό 1=n$}iLMbj4SO 13x ffBn r8*沘e0ӱ8₎%l +vϞ*- unŽpm`gk [paW{OO'3|nǹs^ +VWjKXy\,balaչ2:,J +JY>AVGBn I,_N"m2)3 y},Rܮjy |||^pL=JzyyPh<++X\e[/d3-Y/p& +3>dvU0FYN~GY5W 4r|Sn]s8\NmF"-`~n<*-Rdtp32Y&+ٙ#k((>\ەVK|>M.f25Lvl )(rRIٛNsr4rsdylqc+U96X>}i8OzyOW񾊑߬?v'NɛAb`_Vy8L4ZawX[i^[Ů6Xm寲W)>&o[u<ڝgM!ɚCKt9@Fz=dpԨ.2WW,;va#k pna4/Y$Ta|y;RtۑpW-Kd;oݿoW;WZgG-@Vx975워ST78C+zޯq[ѿ(C}kW4A AggQa_B|{舵NcζNbho#c~uQϿ:{C/ᡘ2;vj0;=vcԷS#|mTG6=Lr Drfc:UNO+Sd.u0̜FX|}:1zoX!{Eo..VI:%nao,+p.\kq.n y9|vp6ru*߽wc<W/j +nܸkJL.P /Q\/\P%<*\3p˧gQ&sR-S1_|MRVQ)^syikK+X/.QeUGI33I_rdr1} r95t*GnOX yn_7oWWm!U]bgĦ$=ܦWxM@_lIijKHL'0Ӈ ct:H#l/Gf0s4ΌwctiSdiF;0;M({q0ٓ@L>E}2 ߊ9[<9F}\u/ceukKZŽ|ܽMFn9/`65ܽM^_ߗp _@|,veѿv|<|pă7q}\GSWb.]&y:WDRCPBin%=9t ԗbOSۧNs|dSԮ3YjUU5q<_6NQD&"2"2RQ[`mxMsjt:|hĎtNR{x>i_tdm h3Հ9v4e]b<Vz^e!؃e[9'˷N픵Sؒ=ߗN`0ۭfqjn|dQp@Yu&vpX~נں}W_E'kATr7ΣXMFN͒q2oKb6>d:d|J)ۗ **% +dqXZD>3JBZ:;p *1,ΑO>u./YXdr&噲N׮v^ƣ<5 . _y_n{tX?HUv95m/ϵu-ց_)kS aڌe[ +Ռ-~2X]3m>C9ΥZ:@}yuΆO:Mۭu-;-{a {n}z Py8_)y $I:>:ԯ.Qݨ߰`q\>wQkgŗ~}ZVlu5jhկ~]+zes['2^m0V9_=~9և\u/?)oͿ~OQ_<^ j T8a)+;[E!#+p{K|sR} xBX$DnS+BOoݝ!br6֋pzÈp CoOrۉ;7؉@ ;Agĩ.L#d$0GXNOPS1|}|%[ [3_Kef%QO=M~Lϋq,v\J"v6ͳ8ceϒ;t<^y=O^Ɠ:kWp]l{dؿ|+q"G]µWέx%j=mm`ayM,Q.,,^_T]^SWKa,nnȽtdy9d(q~RȒDF]p9ُ:Z"%|-El2t&M6 ?; _:(l.2/OsXqkGazY5Q>uCƼYLp_Cok6CZ톯n.Z\*]nGnmb66{ܘӦ9+՝bmWo1kuX&}ݲ(sGkv a :Cv4͕_Or 'ՑjTߥsb3wHb3 boUvCKzLhh7Y$X2\NG*jj*Za_Ivq.^is{9GveALЭN3.Ybd#&GST=ݭ׀a{tU5[㮌yN\gp9;c%6*ZѵoO .x /?W}3Syt QemH:,w|^o@~^P/Gz4"ƵECQ:;#"|Gguz؏hBmh#{{:Չ} cpHl]856a{{4Xqħ85(!=1ɑy)?q&>Rv ۳G"+ȏ輜/bc{ݘopw{kωw[Y?{޿F|x&uubszQ߾vƵkv6%ko}Selu,stԪs:s̗1G}ZX"9:mjCVļs&> Yңi^o*y#84A&3ʹA6e9C=;1V3S\$nSRRKgDb%,: +:}{k W/a~n-r$SX8cŸ'6|d=8k՟_M!߹\8r fn^uO}S~ԬSsmW*l)E_'k_y|Va4ӴKLҨ=;fg.s]GZqSkamuz͚㱛1j寰nΗjw%^Ǻߩ>E͡4VV5^g.Ǧ̵G9,13ioԣ4bX.9Fo,P5EDz5ڽYcƻnrK.gᐘ/Wjs:g'k&vb]؍.<.ӯ˵[ }Cd`0Q Fub=~gkLTG1QW:o XXRy4${7$}H|׏pԅ6_rvd2݉(:z5Jbbi?uq8=2 Sĩwctq27ӣCrȌad,5Xr*qArA Hg(K|o)E7)̑ԃ q:tgKL:.la {;|<_Ý]o*wr$'*HNjsIl6ɢj5:ٻZ".]`rn3h ߟf,=xݦxӚ/:~_Kj=r~S_,wjb8}Ot &=_=A 7ަ&m #ϾRSaR4WӈjuFf$}{H\}ivXtA8҅\a9ij/Nc̭߶iGnGtat- :`1QC0ɩ$fFN~8rLQ v{]sxFܖa]w8]~=wX+[N|}."}>ar_b%ۮٯ,?u0X&W|^gBdc؍auy ?s3<}'Ȥ3HEϠl s_A)9>ېw< c,J"~'B+(z;"kх㮶NDDWwuqXY<<Ў0z1?SF12L;܋ 1yzc;H +g3CGk!K9j,+,Z@\qTz/Idtu +r)An8VVM^lm{Ӽk܋{kxxo=?_OSģ{փxѽ}o~o^WbpU\ML]mj]22;|aCr}j'G 1yJ!4oS$f=di,N#q]3sY:sJmBԼUtOQr'[MoF<p7xʷo^x#wغu,_15>8M~Qlnw'j΃Z.לCܼiRŸW̷m&SX{/WL #7 _#Ej % [ngbvM飵⌭|"hJ(x/CG[ ?k)>۰9f=}?ewbbz gJP'Wն.Li̓W%NpPr 9?ֱzgYK\` v͹L-s&fy @2U5Kb\b 'B5oЧvPȏXuPㆺx|m%kaoowi7 mAtH{0OM;Cֶchtq1:6SLuaz0ONam|z=NL5qBRJ`rT"J 2ȧY.S_)ȋ"5&7ְuVMrnwW.e^:[uZ*9zUy> GޤǻOo铛ݾ~__\#Ὓx.ի[9G{}1y/M..j[9*(PK\ͯp.'PW,9Bsn/W1cz:E搣N">]b|j<rgXl&O/bvVt-Y)2|=L&beY̊=G6S#*'6Jgϐ܎(yO&UC|ףa#Nոgq=Zmxwkwߒ'ӬUԪVޫۼq|'m7y>&p3=Vlq]Ӱ>=[۲g[~,>: Ncg 3ˈr3ujĴ 8X2_H}@YqFVN0KŞ.g?)\~baW+5lQZC|kOrC}7շz[NY8CǎᯬI$IsnuUa=֬dۀD ^9Wq3b +ɼ2n +ԍ9WkG ; 7bd%XFď&q^1jR_Ԉ#mvB;눢:?z0׋~?ԽfZjvڨ{Ɇ)LM cx4fcHgƌigyT/gy'o 3(ssZlC^#O*Ys|BmXs;x|ܿzMs>/nݸR>wٓ⳷oџ?ܼ߹l.}7bS^5wn}=ݥ'Sjsȓ\(H:2jylR#.pȱ S%5 ֨Oȸ4oMޤM[?{<]z?k/M|N?4Q=5\5[_b5~rlcoȱq+[Fޗy@MA?>8Ζm+yv+Wqȓ1\G%&fn^Qy4~ب4c%ys?^4fJW Z)s-)|2oc c:R>m>] #rI=G2sKi?ӶmZ׬a]z.=G C|Z/ĩk- c^eœ+^5Yy,yP=qTQj:BSuGf-5>>敺^+A +m(:[laytPh'_E&M=C__N9GcmU`oHm}>j͌ٙ %$ϐç1z4iħH̪vʐѹ &&Ò%ji,b%oTc95\ezN, ɐ+˪{ϝKG]zyYm_{W}[x <}t]zp_|?=|}<{zO_WѸIKƵu&>|<)<{7z ,mbk|:Vvj2NX2;43W,sPȋN I ,&MOa$jgԽ*Sϩ[h\YY#M};y,@8}y  uΑvؖCGx3Gl-O1~9b_?;~ rp~Eݴ;MFq1Uca~]sm}&|4_y9V|m40l-qOMYþoצc<\q}ڷuy,9L⡜HkH.:cjN&*V Q{ѩ|j*fKѰ5~%hߨ˨Ae\n׺X>znYj37.yg>v-y0-nujˆ"5٣Cbt O@j<˰pGծZM _RRs~Uh K^:XԳ[5qX9KmnӺ~NkԴ>En:ׅPD mNE|Q GsԮ]v{H^uR뎠FFF1ř o153HAj6)MbGg;罞:VZZNj+ j<_3F=Zus2(5 KC޸qz%?³-7Լ7ux ~~6;/b>6^~>%>ֶv=8waǻM.qKds!+)7*S_qZK#eL\ÐyɷZɓWogVsxBșdR=WJ9~,64GLݚ#qk)NjiDwHJcH_Jnۈm)Lp}oP>rri>l3?ʌ<) +GN]gqn=7c}:0V?%./ vf#H^ԜN3WXr1a23iW("}|Cz>7^@"neLjXI<ԷZD׎N7oPd-9ՋcgRK#6 JnNG7N9#gxCjz%/rj.#}|F{zK|+[ɩjUkPhi)wipe陰T`kcMǹM\=jӷJ<} =}v?x+S;<?Ƌ⽷n.~LJ;n ;wq1w~O5>yo>>;.^]ԸԱj]{B`Ν^\\ܖ~.#5kN&JdnZ%k~Q#F2yg2e2ISYa/Nqsܳ^clhׁyZWu'ofqKΧú͚?#f[+hͧ>a\ǝqj ,\p8KyXk i7h&ϦJY0Ne/ ͯ6:F +#؋-ָ̥2ʇ%],֘gΝЦ[vk˞5Ac!9C^ئˬ1dg؅6͞VoD&kQpW+_0ոd?pސf~U"C<6&~ݨ +]"tDփH#)돡]yGG01:ԏ3rxFdӘDziBD&PK=5CΜ!{ +XIy,/\_4(=JWkX[6/hnؠGҞ99Ebwo=Ez:rU}w٧o㳏O-|M|ݧ{xJ>!5>޸W'x='OqSln_Am,52Jݺhߢ|Yz%Sj8$ +%?T\bI + ;qJY' CRg!+sYU8s<69 kSRE:gO%0M`jv71bY4rê灌{3$2+5l. V2{eOlք3"-=voJuz.xtC=+M}c.3cGw^j~\AաGo+-h?=Tb5YNpΡOѺm蓧+w2rn\RWijw2f/]agQMc,ӟn`YsKlnGXc^[x)x6K;[mղVޘ^s.6s9[No-pvrGxo٫iMjq:YW{NHIyZҳ:Ѵ}RYr4n8H75[cn_P0Yi_#Wώ{ak haAW[7 ?A?^E83މaLQbzj);ORM#>;٩Q[ڐR=[&'xo.Km%].b.ܑ;^Is9-s7:9EG:v{ƕMOgɓx>>z)>-|;Q>Ňw;C|SϸԿtYkn߸Dm{ ;{uW{Ou&]#{jy>R[i/BQrQPR +w3IѫRC+5F*4c$@ [&ӳMOs*umUUҥ\i#!>c-MX*9_شFfr3hr&چĴT$'Sbj)qܝ[s3*ϣ^b~Z]9j-x7#/kݻö<؛%Xkj:GtoYC؛><b?7Z%UݶF[oGoAp\۫{$毅 ^6 .yHjMe_/Y9}@L# 6J_yuõW눯ҎHاyWbÈ'Y8&iY^}ȋ^՚V5U7F%^.'w"fKO52>Q?E{F__&T=]!Z _9p>jSap(7oW4g$@lAKsqAxBLÍIuvwF,+dm ifm;=3}} u/XQHH2~ubtԼȤ&8FȈI!gdP^E\~bM{!HyShV,}v\E Խ۸u7Gg32]>ޣ>'>Լ_}{swɋ{ ~=<~pSk?K׮a p ;{j݋/# WXB/5$*O57Bz5H{$hZc$Z2jdM{rx HVǜkY!0R%j5z-}p4qXaN_U5+xpX{t3l{⩎@k}c~vy -=$Vծ6v[BYb47DS-@lbW}jha',q?+IɿX*wcr-"WyI=ǵ_;:cjCnVRw_ϊ5::;bc C];3ArwԿ3#lR0A5Iԋ3K PF} 8uQ R E7 |]_Y0b7mؒZQVElװWT)Q79{?~ |ɻཇGďuở~<~1G|>W~%>{~Goa׮_w;Zrc,VqX.KWCA> +chZǕ,a*j\vsQ! +ȕfTgXG'SHrlU8(d.Y#8cص E& wL.t?ό9OKurY9zX0y ,qb3ZV:"\SUr6wf  lyo_qc6U[+6Oo0o#Av=^eG{l3_opȮþ{-槊} =b'vh͙46FzikۡЫ{|,8[hCX4ta6b͹M6{E4M|[CA咵F v Ka^G/v:b>1<˨M!4L.U(>j+c2,PVאTb]x{)"vWeӵnؒ]^ۡx0YG;e uŏ}LN-h?X=ǰ=>򚏯i}WkiܳJ(6DBQ RC&18xZmқGmAQ@8q j`06qzBZ9"u$MsR;FNG&aãԺO+.ЩNr:iBmޏ'1W`m{w=Hlj5A-7WBKRAJYllP[Z"lbsczyI9|nsؗwnx?_C|GOOw_>.GO?O w>#L]?}_W!d{{WyK{99TP!LOMܲQk#\RY#TBۥ3e_yCAhE6ePSn,i\ZQ)!ED2KҔڞ˹I.̨+$))`F5y )~&K3F">JV'y%|Qs Qʫ[ja˒+FֹNX_9z:YqVOzxݭ\^g#|Z=bV;7K"~7?v>{uH=ڌy^ך4r0t@lRc:ڳm3-/iG?ph`G'6ZO#z뉡+#vUum}6xttw8lI׏:}r4rs|2X6S9fohMU6y~3eZ;TIbz`AzW;9PlNNԑ0ANz Iܛ GOP6۴Nߦ|eIE!*[vg+Z|vfyai:Sjzu՟-Q9>ez^)W˚Cm׮6a=6p!,ՇѱI5أe]G,# kM % 9vhzu]h;~uh=}msc/|jl';^x|\(`e{wo᫯>aj$5P ++/-acU.cvmcg/;ݳ9Tqvzvִ@]owdW_CM_~1۪=O~~O3ѿ_>?s|M._ ?c{.^;<=6FXEIjrimr*a|645h8GJ͍:9Y%GImI OI~.q jW .j,s\kpPuck+A2 ~x}]2U~ kEx}9ڵ:v߿5y;m;a;7nl>fWsxWW.C-Q-YO8 Ni'dՑhٻAQT߰i47_QcvovQ?c"F_՗ŠGp9;?vJ Rdž.w Nj0؉`cJI+$1b5R9_؈)aK' 5{%l?k:ZN`ibsάVPp/I[4ӈ ;3+5D;koc<(K)xWcN 3KjyMvRGxnZwϹ,McWj\uvD:!&G8vt zxCXtQw|'{#${1>ރ1):A.>Yޣ u0F]ƝױCN,;d35RU}gWWL.PR߮qg׵.ՕEjEHn:?3sZs-_:֣x.>x^|p/?z~Ɨ??!~/o~ϩ?O~㛟)7ڷpElae}\[Dm9 +5e=, +YrOؙeɅ"$̼ԝ"GirQlYiՍߴċiM>I~׸fHQNRs QW+g ۵EUKd36)}߳&̦*2-PxԾM+\gK<k,>pMťnq߶BWrl>o'՛hG=6舽oÞijT?Ѭ#, ]{ ʿ>ux{x~'WBj-̾>7j/9F~;3j54cly9~0VNSp4'˰8-mȰ^&"#Jl@b+}~ttK\Ì9edoS)" +~qC+KEa[/q.ǩ@9~Zqɖ9AfLṴ)#6Z"Ov3oX/_WKy^OVmKk_تb/^ bpe@+uݾ-M&:7(}B[[m ꌩnW;mODdgPsA~7w?6]tT'Fz1:҃.Jxz1RXXHc}hψJ]^yku +ܽEn;Գ;g- Z[=n,ݳpn+%:;xcr~ӯg?z?}Ow~?ċO_|/>1}cy!ܺ c{g:WXXE}*$w9JE_}1q 0=(Ų-s(2ZgQG֨!S%rSKXl"r2uæ-q-ڻ^Jr)}E*Hn/,sYоY><4v%^.^ceŊSUtEGU).͕ߢS2zJB?7[MxH_8ތ='{_+f;^8:Ѱ:|'ڟ};Cn~瀃 }P䶊Rw^a;n:쯞OWhNznf1pfl-M{u3LY.}O)%WezK?EǻfϞQX"%ipۍiJ 3vp1F;ɮZrljuc$=ݩvDŽya{c0/ e:nAiYSHm[4߫q߲poFjg4H5K+<_}|(Yő@?i}ۏpxa_=,y`aisU3+ԗbk N-I6r9+I"1uq=$Jڑsi2zfL*5? UE)bFVsyI NHdjHK *_ۅ|A{+:JY2Xc%03V$~K{_y \"?ũiX/qsEߺ!yd s:z_o{1f=q1!ƫVCz4\OUߓ}mko7Zdoh/3{i},mز [}Zgw: FWYfZ5ǂyϷE?Zf-RvS6ۓKM2]{>5?X4԰N{䋑sd{vγQQ}j5gi:YC}XzIdq4U甘qAֿUJуx78Mm,=ԗ0r>ېakv9]5Ɍl~6FԸ(F5 _1dvt/^nS{UyN.Cs伄`r4(!{klԾnڴ/;Ͼ!?~|;X_ߡ__S[{3&5굪U߹lAsgv.?$l-ٹj{44j$ssŚ>$&+#>2ӕ% RCRRيD6A15gP:М"+V.q͐-r?Y+sӨ2XaXmsRJ}ˢVX6'uQvo|T d+y-v[Ŀ#anʓo_?nnamܻ鹘coF@}v{iww4675Ϗkvh_M?ݪ%n?{TFDXb.Cnv:mK,e\}R 3j*=Z"ߐ?8c{םmv|5VѨ?&[8pf^UZ۫ņfe49>@Pm) %(ԯQa̍`nGϧ@~HE}B4&9L.ݿIB`z!_jJ#tU-~dѿ?ڢ }" ԴcG;yN}&;14ЉvpFƙS]H!!|0jSX/K,רy47ʽ pngKy.ljM2wgVUxI-}wՎrW}-+}*}v=O?~~{_/uY~2*!g4:s@#B*U +9wsNbOO g8Cr((Q,.J]jU̵dId+lk}}oE1qpOUW+_ϿI˿BK???gK{z7i}YZ}Hw?y^z"ޤmo8kH‘?X?>Z y}uJDGW17;|Ì?(^kXGhlb9.1.|Gy55-8,af]|AR\Ϫ=p̵b}|w- J?իW8-y?"_/b{^9x2L@|{Ó~xg:}{[+^WDw_>W]1aYկ|ǴfSJ[ć叫ⷕkr$TW[9E~J5Uj}W?UKY\߲NI~Xa\bk@^>CR_)a/ZJ+؁x5p>3i1EXf!Yj͢kWYJ&k!VnuOs?K Bk 0zyQ#ekk٦;S|J[_i)ba:`Q+̴IZggU=8/6>f21bVa]<]q\2k1y&ְMmN-zxHRk[Y#uY6lI𷧭NGl1xQx]9Nγ0<8勬}FX`M4y"(D;=O3gS5\Rе3S )\t|me1'Z?:ݹCoN/=S؋7_}>ܧ{_wIooOAzOhmٹG;huch>F)gqOxFoJ֫=>fdg>F^fzBU[GQ46dp@|@k }=¯w 3Y^8+y1ُK[\D4Q>ί +~~/fl5~KfKH+(߫o)4!c XX 21 nrzOpeYoz,8<^ueXsqfl/jm-\Yde`zk_s_(y#4<?nxZ7S: ӣWCcE-ki<7=2HGՕϗzuԚ^IWVոuM +ez)j=XgHY.+q?W>EީnKo}\iUnޛʨϰ3{hhrVTkr;WCI>oUm5]Zy4y z~Xb^`~NF͛\k j +>NрF<k-2vhypmZE? =KU+=<~օ_ط͍yb)11^AOu0uښz3s-$ndi +k,UWmyfxTM2n>" rNhtDuڡI uoMc;x=<5Vrձng3+nki멣ﻩDxs u>ΚLG;Љ.:H'S'Μjޓ~ΟwU;:Ȃk!G&& +JN4N&) J.г+uSiM,-s\rs)K2gSJ&(Q(DC B Z^L=Mߣwy ~sw? +?oK[课7{iz/K/Loݤ7moutn /);c;wT,x&<̰bsopO#!m,ʼӸwFCœ"&PHnQ +oFT֐W$̼"T;<0?fzeC?->.}^^q|ve{>]ty@/nK2{)f&%J٨CV~T1kck53oLu?*]O_Tw6iݗ JߊAoۊS]kf}ͅ~yͅ*qΡU\OCI +$ϊ1h,wձXE&b ]jT_C6jg i 4vS5ol:ndcfԋ~c;d2I zm6h #5g +[Ԍk4֮_Dj.)um}b7H:{qzB/>쵍jQ/%v9f[m5z"6Iyl,rыsdwc?@u-0b_Lkֆ&jku|6~oWwx9몫ۘGlo]Otqt?{Ν9K/XO .CO_['Im+wcaS8h2Uw*=#ٴ l+Y~|ffm& %SS5)x ݽA.Ipwgknހ}/җE>ϿM_/Я??ߡ1oo?N_ڷ3~z3hww?wv᭵Mu_eέ.Fi838HP#5&~̣ЅfG4;Dgdg09ޭ!˼ërLb#!0 _G7ǂSܿ7D@|~\8mMc.0f`@Iz5}ߙ<:|&?()~A_濏k,rϣ| xZo,Kt.iRb!-o`,oz(;qµXSJ^~D?9 axliv_o1ڦXo UF0[T~S=(Qx7*&[z\E|ӬM6^KhI0j1iɷ?׬0Vyna.Acuf`R`Frt2kztHoCNK/CyRdks#M`P[|Fo_]jX^;K&O."az,_0,pY;~ƿ99BxCyX70WZU}%5\CbQC3k߶Vjfvϵ0[MZ'z5:Qv~McF:ZDuuNtN>O'N ۾+dN|\$"/8D07S"g4 +S&aw\Ë)ּi5orqJghvz2ҙ Hkt=zwk6Xnb&"ݼ!=LX3}~Kҷ_Y~_пѿџ~#? ħާJ/<ݾ{ؤ77UZZ_e=G mQ8@4gf Ȇ9#>E3z02Tf(%8;^Vm^[:|Ts{T_颮fVj9x\꣜ykLZ|W; Sif 5:vݦ4TmV[<|$̚j^V߰07YY{gX%[Y6ZR"Խ >c/kԌqc\mt?K ۏ)8xJZkǁMt_}N2>vˌcK^fm%a7Kqg5VTkǏPg[Ħͺ(7t}ޞ-&~m5cnf. RǏ;@6:cctx3;vzx;}9s5Yw|S֑ k y6F"A=4|%eަ#4KKоU'ifvhqeVVwˏރ[]֧w}n_ߦ5ssC<7> f,ܡ7^}Dy1}>CퟤHC~~~ۿH?՟7z2z٣m,,M/d2~mYg]db¬+uyosl2ƌ1PQ_D|<^/ssv4yF" FaaKŋ1ޛ: &iP O\+?kW/ /sDy_B4 >YSон}>z"=N1>ǬAk!G># B {/k^?:Kg*7Sc{QKйmڇS7}n]^byv-4qj ~V +~-9e~Drڱ>Aŵ>g|B %kϻD`rL%-_ypi1~Ã$5Ěr Wm2COU_AVVO~ +/9u,Zs *2 az-T. ,ks,YsyGC+]nףF#ׅEWT=/d 5Ac}u1ú  ֟YuSqTݔVO-gE?u RwO(a6f[jdR k| +ʼ^mvZՉ$fEv f7sQ|nPfe*_'31lZ#k,wf&!ֽQJOͲnl9 1[c mH9Is-/ +ݹFwLbҏfhin֖0;6[:=Io0/7i}}n]/ӏ}η_oo_ +=~*ݽw?Cy7iksחi)zegfi*=M<)i6E2RI{5&C)ѱĖXo5?4BcaƜ Kfϛn G4zw0WC\{Q?<}}42<"LGރz?$ y}%,*Wy9$K>UW)-O߲8>yk +C +u*{/fhg-^X7Oo/ +Oa[)W4 B[?")-,WU#m]_aƕE%3Z cZl9 +23*T%m&lҼT~TjmW5k=OzAIߘujw,sKVW;?`9`5I[lYkZm׵Fx[Y]Ԉjgm~ЪbUyZa~hn @SȲiNkO,*6]ZgMxmsMond: 걆v23sꩩb[KMur5Pck^d妓'҉Ss?ucar3bSdvCRd6Q<cNG427$zr0CY֒ѵ<0k ibn2h4MxKt&ɌVs^z/ޤg-;[˴ίۤGo_Ky1}G?_ί_~/~?_o7>z{ݡ۴E[k$5Y(Pwbc/_fx@V~ Ca58N1z2Ǚ2y1"< "/Q=k~for:EYz'_ ǽ܂ľ^|yJeF&=ha>M+x_K||j>I>~>aMC,uWX[e4Td߷mx=z|k,=(} oY~0ƳJ7ku~Z>OtPQGH0G]JPU\ +[Ia0IփZ=ΕAbfEӅuS,\6x![>JX7 ~9dɨj 6s }?m'K̛V*U홖ӑ5k{ĺM/r9ěRQ5nqzzV큗c)PS[ZoYJߛTl_%yjF $15ݘQT'5SuOɰIo3Z]rt翗Np+55PkG'Σ=skzjrPw1G[+iޣ: =v@gΝSٳ2r'Fd8rcW)$ҨěQ +} +KoozzJzx݅ -M _VϧY k2?_?Ǭh9sz,K-r=wy%|wiٻE7ogOA??BI=/U[ǘ۬ï.|ksy34=hOO R7s󌎴; #tH_lsG4f*^$O`VzrOΐϗfn}/kIĠƃ4\ s85zx=xr\P+5##]a/t3Y[īJtk`Nx_L"<:fgv& ZXHNMQd8$#cX9C_O~_ܮ;oO}nlyJgOxwHM +WZRF3?0ޕ Wl6Tu|xUDs*oQd~~zU2{yT*Ԕ5USg5#XML +FY +5kPS'H'*Yk|~%mn ]O~كY d:/3uzzu$ 'N.R5?Wv+.@<:TO ߨ[$V%"GftڨUsiRk\6{INސs|:uR 6^ysYZ.W#50[ZZ;.8AݽXdajhkg,}GґMtH;:}Cgδӹw]K'i|`Ƈ=2E!\:f&#^łJ(17KVL3ޢ>>>Oo)o_|>'鷞7<'r{{<fx T2inb(I +럡q88m +i`hRhTGbEXTm d50|ߠ%I5d[VKdY)w~0kjO2}W7_^^~.ݿkUZ[Uk߳a˲f&)x,AQmw2)3A?aIPaB럌)Jfǣ4/;ɭ㓹P ֳY{zѻ[^oC\ˌ6#Rk'`dBP;Ě7Ŀ5s|bTz' kO֍beuI1k:x잕;3?+Q%=a[y&OZG)Nk\EQyqmsUդՒ%>^ܛ˴0B&lZ l5T +-؇jTSvYAUݔc6Kyi>9y`tK=]:TA"~𲰈?qg{u5JL>v1%m"G;&5O&R"{p?aԂ~` ZZɩY|*]lz.[X%Ȍ Iy[engK-Eqb/JrV۩4/kHW?XK MNjjgME-dANbfs*뚙MBO[ۨAz.~x<?{_H+utQ{k kF:s{Nlo7x[]|1Wh5qR/Ec |o4 Gc4zp:tJ_,lyNno,.rgo%ܘ>Y~k\ֺkZ΋?r+K31sx?{k9ٸ1pgk&=yzSWAoѓ?C={yzƓg'Ӄ{zyZYLKLJ;P2D|)'s`o88I1.E||pM1b4EL/-7g^hq֩I(bW6f&|&| MfF#!{cfY7Z>k?8Ec|l8sգiė~X&S浘@ hDt?;BA>74iczݲ5yz7[ס^tV~?U +]<֢C-oe}{ç?kǟ폪+oe?Z9y +1I\ uJ[Õg:r:EGHem~ v*vQ: >;W 9wݢ΁U毝u]jm^`(vyCI~c3FOE^f`9y=Kd{FAl65J߮ϓou9RGF5sWf)LwZ/S켟.b*+1[YYb1/q4hh9]v_G$?9T'}n:XZZZHg7S'^q'o|:}3'Yй3r{ :{4]rQN17ghW|DzhRL<&'if:It:&3d^ɶά]uX[.g7x9[h9# m<6?뫢חuf=Cߡ'cϼA =z{n{?iR>w:\Z0/1P2`'$O %i2Emao4*ğs1- 3PFًA CSO^j^Pc!xCFPXa< b?#4cx .w0>`~Jt=$v&huҨGr$|~yrNB0 ć1SapV?OӁoyUUzu\W=\+BUbU=FUvkoW<.- W?_jjʵM75k(T~WCh>&uͯQFUuCFV*p]jjdjR=JͭO4*X=jwX鿟ATH5Z,EӒlL +oKI7iVUnSŠmf'o j&+lr3y-crJvHNf6 V"V-nhl +-V_dz|Plu٨?jQ_ #?gwZdƠY4~o*Z!e]KuNԲY䶾wmȌl: 5b>oK=2c49X۩ښE梮:[Gǻ\t:;sMh=Dݧ:XO=]8zE|$;.tB/]:K>ѻ“M%¢3-61g~trNb[%u} +Xg-ŏ0cg(|,-36issVVef3{wgi(פO(Ds9~2=C{S_ҷ-z7g7|[|*TK}6]џ>K5O*c>I$^5(}4k1t7@7>kW*+/|Ueva(6AEoj5n5ok hr +;:;m{s=5p{t7N咽6pMzgYqsq֖riֽifSIo<)y9>ob"&eL$6BXyp Z0*M(&3 ChLǘg^_#,~2踯kOQ嵁P@4EW)K3ĥ^(.^f+t+Emo$B 3Rcy"$P(" |#c2פOb8nijG2S5u@|Tx3koX6 /W75 .Z^ۣݟJ(>XV[]¾|T֔\c3TlVkRm,-}Nex}[5G8(8OgH&xHH<=cdmcǪxI֮-Vo0(MY2XGS2JK_,}=`,*>|VM߇?Ɵ Kݗ\Sma&lMYת9 ~,}]X::Mk|۲gF<{fz-])fڴ%̎?sl{CX֗_B~y ?A|6.*IU*ͤ֔RmrEK=?3UߛTW{_k_h?ۑogQfӭ|gcmr֓Nc#Uͯ@]X:+tf*?&wױmYovPcsԓ52g[;X뚙ŪĘکO~2ӧOGht):{yN^Nӕsǩ%20JCwks?[?oC|-GI0;_x4oۙl0KpZӬ7(Q;Ӵ;Ϗͱ]eX:M|6ѳ4E+yMEK,]]kU(1lI%(Rvnyiؒ^7?YϬ{wn]wsvhc52w~"2C"3bS|,%)#5'鴟Fh: sS_ S16Sq+U!LD MӸ/`u^YxYI0"y؇Kq 1{fc&`Y{ǒӼ@%! LP29M>!֮Sҳ4r̡|yMLę BR2d1ˏzFUYnZy yFUm}S-9~Q#ŒxS1]zia<<{ٹeׯ)T\?17/>Ӻv)Z3Q?'z^-fm +slkcv:!&ȏQ-bbV:]Kur/fbR=s0m]ꨙ?3ꩥYی jhe6ӉuvB =t( [1Ksҕt]z4xh _Hk2ͳ[d>N3fp$-d4MnLKO*r t&DSnor[tcwË`4z|Ļ[KREp f-0O[Rǵtns|MGu沈c~pV76nkꖢQUjn +Zyupkj/hAaDZcC%xfF3f֣7*u!/sMFGO}g1af귚At~+ shm?9 s>TbSifJ[] {BCGGo3kZ3c +sv(ԥt u# R=W6{AU߿QӅزI@9ձmB?ֹ_ ա$ʼnNMnd&Eݬ{[f&76%`.n:sfpnf0 Y3bŘ{̓('\" ̤TRixtfYIamG05C(6KL9.BWJmS?a/|PGG'}Px:7q[ƒa.6>lD|Ťw*gf3l1Uӌi(++~kSό&x&s?d >_U~{]ZѼQ榮#tGy;!E,hWu5ё#tX5?|NRvE潊(X +{]l34ͺiٛ_s`{N)Z/; +k+Zv^]gݻud.:f[^-y<3?t045Ly\5_9s_k&\[U!u`ۋwk5zxo~6=">sJS5n2)٧qY_hHFrqasB"w`ugHʘdxi}ķYSgx|"*EV ^X,#3Ǡ_<]1%7 QOCoDŽg9 qX4#=D19&^LhaaV4C>࿗8%AOR2EsslN4;s--w׬&K+ٜw_mƖts_u?KOSOl]?f٬uSr]<؏,zxmH_ӼNby|.͢NH4|.3xn~YJUn&ͯTZGȀ>anPoHb9=xtfhU < +u$WCtBXKSfDzomg=7kh*}֩A<8/~;L%+B zX><%6jd*L|9xQe&Wj*_/ ZbTҜiᾡ㫼ҏʢ6WwAc5}3$;\,Qt]Z[:' }EBU3Lҷd 24K6Z_+Osnįw$kc;.-kjVSf-@[m9Ǥ׽8cmVԏv$d!GNsX[E&jlbMb&.^ymP+Z5rkCzz;n>z::ۨ_lwR+O6A]ݭt)=vNj'R:wst%z̏_:ߤDjGY^ E +Ǚ_;C|]e7AӬoْJB,P 3!9OeZ{wWu־Ez'7ߧz@o}zgg- +5^7e~inٻEKyNJzwq!'boxf`ZގD/zwmEb%ea;kt +d<ǶkZ`==34⪙֔ MMee-0:o:v6ŌNNΤ|g8B]7UXbp4 +7ѯ'h/*^ +ui8Y NE_q,wbTP6ou.M)0#ow=FS'"afeD44b_q䬓uLz3~Zթ-l64yev.@4%3i>?Pט:?OVG#6h*r~);NxN| &\cQp53 +r4-:f_CgT_W癋X_>M4ăsFcg}ԘOis^kz /S2#Dz uojBR_0i=9FTy,̤-Jm!ս Z߂I7jLf]OE7!+9^S6䊷f;ESdW1FͿYŽx1ܺ$z1 "mfDZEGzf=fsY>;;)No[[575%Tgx3J]:Em-5:AGbt9|Xcıv:w4]8KϜ+eXFF?6LA0s9HH^a<_Sg3̓rS̺k67K9f zП.<$ٔ 3ofݻEo>MޛHWޤ/ ߦwvieyļ\"".59!u?En-7f2'76 ~xc,hs}AX59޸EZ,ZI]4sh3x2g$ͱ&gfgg?Y ͤXRVfg=مHD"#1c>bpN<}}I"oFoR"f68Ÿ0;4z*'bмs)ֺS{NnMƙS̋5TкUqwH%%I#2Z7k0I Ҳl>oq~r1Ror>;Uof^[Y&ۡ Z׵5P?g/Z_gTП&/W2[~'T5 kktCiiϦ0Tm>R:\qȫSLjuJaMSԕ_$SzccGA׫}h%>dPx;j}PQy|Yb$&9nsAOsԹQuVEp3xtW5K +/KkBΤ4׌&AR\AgoUy>^kwbe}m3)>ZEϴ1L8meʧJ{UOg ĤO7S] gӅGtC440B}4/~{w*FgNp͊V6QnnNnK\YB +s{MX [~ ^] Ưg}ssfnkWKM{7o-4Dr51ʰC>t5ڐ+腍siq0itBzxT_0|('&Xo(#lпz`\|,QcAkK# )^#ybԠ5}*ՌwUPKt삇HMU|ۨ֕_E|su}0 +\M %J'بTe&(NUd+z (xP^Ǹ~ek65d ZnSWe~z}ׇ/ꡕ6c%=z?2+עFvZ; .òZ&+u3VFZ9 V7j9{b yq:a^*1]ʚs{k,"{Qf1lKG\',8fb1k:xOׅ6_dz.zxoBGz% \o;#y5ZI:w~Nm]=ګ&=Iϟ+ze¹mteo;Do|}TKdY氚;k“5Vk+|]] qg0ę7>n]٠'/ߥ_}9;ї%?6}~L/xwwha9..5@j*)_/bˬh)?'>j^1p>D[;[כ{҃+lסuϳ6^]%ݫ^oᛷ묿YQ:g0sdZd.y{>&=Ϳu<<[|iaq57^&{@<2WHLK]Ɛ|qyUuMVh؟Mjiln\'e4T]W+M)1*B/Q* =z_iQku#Ֆpg(EBQɼ^hikP`V'$uZ d19@GZI=UB-\/BF b;ϟ]ߵЛ`1et0 BבO^;ZbpXo񪄮Gܷy{*$H $;f@D9r9`nf ,K%Y=YȞxsfvf?ٳϾ{- + ="Pu-~ k% +kOn4f!e'N;@sφoQ.s 13C'uʞA5ØnSg A|Z W?xտWr ,9"qhHuT|>6N"u [#12[E3'GWEc=ې=i=#YiޟݻcWt8CE;=_;ӯW_? =:._|K_/N&:zy/*-S/I9VC\&EO[nok8ՊۋmXYnܽՇ+x$>D/:ZQYWJlCRcsh:XȻ\2zk*/LrgՈΕ{VΫ\ΫVc aU&||]ƦKoĚ +v lDCSjP\V'!pz y QX$(Wn_FuKx5ylnhB^c\W_y;8YHqEtsEF<=etrۘ9żl?Z*ۉὧwPε\@}*ΝY%Kos"0wC&P[sOEF-~na5'Њ1kݢ'r.8O +IG1!'$Bu1#"b.brLܑd0g sÉ~#DF"|l-OjR$RR␚$NŖmBZz"o!==adl߹Udggoṋ)\>tpzkrl'%8^=uF{3{{uE􏸀 \*ސ/~!֔+{:V[zZDwH+nxx{!<7[#oGk[+jDO輆*ahJ|t*/}T +kj*FA9\U]i%l>*_/2!lmVדˆ|.-+境5娬].FS!X{%}N9ʞG׍Bam2gNXiNc9~\^璡gqY9>eȽLj7{E wu5 g.ePPP3+ÿ_pD7ko(sb˻NMعeY<s/*.׾zsPWuzL/"ٯjxX+}6g;Ъ:iw; A/>)hϱzlsכCm~sq㮯%3 ~ A 6tlןyn>HI+uGsvq<]!,\͘o7٭kFF:z;yꯈHc΀_<_cMfun anbqu_,ی3 a>-yGi|Һ+341kƮz';0i lf-bk_29]FZ]rtqG\hw RO/LFGŚs|Y,+%? zA"&6RND'sDZHJJƖDGFitdi-70Lc~Z%Cj,w^ZZר݈2Jf;Yu5?W|yR{v_ocnZu57!:ߩ2^u`\U2]Z]g=TF+kMlM{[De/ Z t +909ٯjM5\;Z0zxpxX#=B#\#HtƟc"뜀`O07,(NG#l<#9w@fN 1;sKtoR#.xl݅["%uHCƎddcpyLٙ"NĮ]8ÇR/Ѭ8qt?N>S'N؉lQ1[߷ϟSu}|ψ=ko#EbU ]¢Ց\Կ\d^RcnA_o=Fvx7v?G04%lEM} ڼ $%,~_k]h3YZ^jԊFnhC,h+T7P[S$eQ&{?ـ*ьo.*c̴g=\EM['FVV'?8s2s~1Fƥ+W4^n~Acg/~CO]TT79*kWޱJǪ@5\rL~Vx]kY[]Zgxy}U6׊U;\Z\!{2ە7j5=~v>?km/]~ zE]_/lޚFjWZ/ +u 7֚٭ ׂ{${±̾s_WdΟ'ح=m7،I뼁0~Y(Y=^Kל5:g1.kC6k=c9#ȪgX](9p1˜g_?s:Csʅt˄ƐX1`ޘ^!QYz~fv2^ dxU9#ܴ 9Vy(3ױk^77н8yC7{t=ֈ˹s﫧p(+2 2jcxbobLb"-:Y Gr|zZpB| RF`Kj,lߊۑ=iw+vBddf#kk2D&8;}+p|3xqF\rs. +E^+k}Hw1k ֦JzK^uTQێnLNat m@_W+&;0=݃Q GtkShZߎVԋ,.+:&2TxVTTՊjBU]5[00~Mu+kWkNؚGuZqrCכ0q_^>`5Y>{|xklawj62l~Z g f%Ό70&8oaQ1k6թˍ)s?bz3֙FA609|AƉל 20/`_,!B9 c' h},'Mj0=Y6f?\.ޮ w9"nxhqֲ k]G3n@ݫi3?R"y˘uG+eǁ9pF>#Pg#TWs"¢y+ptTK}4ⅽJLEBɉш5sp6nOKFʖXHߞ zw 2ӓk[ +vݝ{f܂{38Gwfgıܣߣx~a^~;,KFg\ οuWsoڗl]+[4רnlk8dk ԓuN c, f&{_>̣ݘp]^47 kXuꪑ(M_^"s(Y^^),4=}^?hoLVhN=-r~:sU7_ѵ[Jt]SC=넿u+EUD [ qMXHc<77w {5-'=185CY\@I59:OMayv-ø_#: }AZL-]PT%6cvjŭ[Kn[ZmDgw\^4{[6u'sYOjroiIΖGY)cW[ݻ~Mu0I(Ϗ'k듬?w?bк8bq#94_~??7딌YĶȽ~/ m<bx$׮#J޿Q5Ez`ѿ`lx[0<{i,hܹý] 0M۾6 uvnFS}=*J4|!cNg|ڨ˪1fukß;k|_18ohպ2^e=X_WApﷱ p/Ь^^ar#}ZEKe0ZH{lOJO J.:gzRaY +M + +U^:_"o\=#{WR/JTh_K [XTolASK\6al+<=݇vap ::Յf}ϕ"{ۛ| ^>b{"}DcXqjTkQLyM_2Z t*V~~csY__7^G^_CӅMvz.+Fmϰh8hM z#ZUZ375^3پ>u6 >>2}E|=˫9um;zYWe;5F w+zPC)'HD.;!1/ ]uѡ1HJG|\ғҐ3"+ mM@j;Ήxضҷ`ߞ}8o v>ؿٻ`%ݟ/k:=>G8?voU^x7&{a&_f\bh-=,ittOPO2#]peO كI<=wbS8ۂN͌g6h(YZT1_X*}]Zˑ{PW+JXa靛??73iGO\[cl F5[D ;c1X2F-0P]C&kuFSY7}vPd+[ אOr hq>Aa g?UI*J.+V&ϥ7W][R+]^rT63[qwi>^t˞ur}5*;]rI߶FT*ꪕkEUyE+Z I2m} |< Ԥ~W/2}|/xo^ĖF:wی̺K=V6Xm_K0nV6X~/^eǔ"Z{b}ùZ#>0ٜc{oe=K{lV{y<ߦX샢i`k5 ^{t]5Ofr#AP}\àvuy3괍"cdQu߬XsgC3d֏25j'Xov^[&֜c0k>$ -a'8[JEDK#22Q -Lj +DX +F .!Tt-4LF(71IaXdس'{daTdǾ}ܑ #pXtw4ǎCWq3:ʅwp ruŕsȻf]ZL[Lf-렚i])?oV,hwl؋Lvc~G= ĝ[#5݅}֖̀Jі^LOO~25;]SII,MY=_ ۷?xnQ^^C-X^ĭ l{ʇѾhGmuh&⢾5h2V'z-&kr'1h,]=_8ilj=ōjcKeqk$ljUOL~d7sEԡrj?[-ְHx(lm⌋Vܹ=octdPQ:::}_&SkڹRIͬq梏 oz\f-^6~}e?:e.߃⯧1wk_xCq3{_0{AV#0wlooj]}yѵ{5 1ZZ3i,4_NBxh<>VC]\cQxs qktN-'ĊNAjI)HHBjz +v ߷w`4vo"fއw +w8z«OSxգ8V4/g˝?guk9#8S􌸪=?,`_}~o?_}|L3x2mv +󽸷<[XYt/yS^a_'m5}ieW\uo^PqWjɒ2 WDkwg߽}ϞLv+ڭa[7 w۵'jr[<*+V~x弦x3kZY}j9`θ-(2bFk/ʲ2QgBu]zC3ꧫӫJ^Q.)-\+cuM^A!{av?[o݌N흝?iU f4pEZj&woooAkk:&ϭWvz! nBM}#z109@)#~n&6WAO/6׫jwa`~|răF׊g[>&k0ǿj[[ ߻ޟFu]StP[\xn~ xf֝j_wk}qHDL$r^ް93„—03kf`qֽ̤?6Shϔ͜ooylN=vkz/-,dhL, ]N÷ErZUFW}(ML>c{ֹ ||ؓ(Z7Q}2X'̅Xyc~+F wFGc2۶#1^t0aKJ2$!)>[!-en~;vDzF2w"k^fQNdɿKÁ> G=cdzq)y5s]!Luw5G_++sK +u^P}m0)_7?ſi/Wנ\s+0-.)GA!G7뵪*C.V̖6,.a2m][W^ɵլ-3W][Z%Zc]'{do542]Yo}w7K4mk3jeШ7d^[]M܋45`tG{ZeԅQ,O`ldӌ`|rXXZȈp_sM N0nw/mwX߀ٽFL y;w᯲Gٌ]~w1dSܵk^yESkfwX=~њƼٛ\#Ãn; f^ԍ6_:KOͷ*Tn"m߈P[{FK3n=C!vQ ^'O2MMv6 6)[3x2}Vog^7٢ex~BD76Yașո{/OGP>R Kgfߌ^!Ѳ(A#tL\8܈q""҅bZxCc#%= +[Dn G&$#E؛ I򸄄D 32`؝]{;SroN޿ 3p`:N=t?G_W7^C΅븒wsrs yo"9ܸvE粷}QxE'ڢҲ"G +8CɝY/?3Zg4%S2hw <{0>L `iSݘY=je 넿:1Ӂf45թC!cekqk^N=pA=c1uuqck]+zSoQYKy[ry~Mǃ5V94Z[a"OŢc]\ܮCc^a/6Z{~^]Gx0iaߵ;V_ZQgQ/ ~ie'ΫOW} ^ޑr#56uErZm7g}R{rQrL4vWR1`2[g{Կq/#>aYb`Cd?AOk7GBۦwWxCg%D ucQ߳*ZऔXoE׆c[RRSSE&"5YyKlْ"MDƖlԴ؞]Q#M؁ ؝#a_^=~+w=8y ^;u 'OīEF^ r. wP]rMR^ zz5[)сkw_~wї'x2[ }XҢܟՇLfx2!Lu`J40W Uu]WçJۃVџre%5fiQhOw::ћWt#acѵ/ ;psss̃6ƣ=Ș+=Xdݧ%;R=E,B.T}.BbѼ7DV^o暇ǧq=|X\YApIx+%[r :MۤI95`ھQ}WW澽u+f\~:ѼuFY[U׈VѫԳlިuꌗwuwbjz}DžS0>&?%<Aph]ƽY6ZAt{nGۍ?}qaqbuwM}^Wc1zwn|[]O؈V;Ы>ef͘Y/NF1x]/=ĘEd#i79KW{dO +3=,x΀pkyjoYGdz6;T<1XvÊ.ך=\c8r=أ8NYnx mػ'Fޙ}!Wug;v`Oo947m3dd]Z+FS)aPbD LULD80aIIIm[K?HNIEJj2Ҷ'##s vg& kW<2w&!󌲲{d {fog`ov6R'W} y Y\ ŋoiѵsw/ ?O_|\\E+E7z>6cno -M)x8'w4`e7¶}_!'σ:j`H\ޮfiuQ6h]15*Wg{jƺ6SloiƊ?z}DEFs ~Wm-ko:Ƭ +S[[q{ !k~e]5c<A`xng͂-v){ԥPcnsPgnW{7^cfS'T=I8(,"L46vrv}YhD& 4unѽkÐϢb3ߔdah mÖ-gpdڅ][q`W7 dط/K}deԩx;8kxڛx׵Ȼz.w{ms-Fz 7E5ƌ|w g&:vjy~P[{MC2M㋏g.t|'ݛ^27aXrwPըyvj-z 6n4i-[~zTV +j +E^Y8R/i 6>Yw1(ﭢkj6\EMR#LC}ցvtë*2ebƢ~#&ZoOYOϊzylƟ5ĭ^¨0jqv)ݯݿg#3+ϙT?AooOzTԦ?7 :ڵf}rK= /kZoؓӪ>=tdWܤh1LMOcvai{ؿ'Vhxh(όA: /[cMxqYc0s򨛝ﺽJ@Zfz7?6X?_cMWf5[Lj:}~z:yY{0̓rrQ8{4NDHPOwuOtR}, L5֞e7|CRcA +e@H6EgaQMRco'l]+fz0lD:[>Wm霄0LTyacsWxVm=2}χ,PG޳y ܄wD"RoDd0t֌EyH(l|(cCԔM GlL⓱%eRRb-lLmص#[S#73YoƾE¡Yص;'ȡt9,މǏh;焻o}UΗ޹59ytmNkͲMXŧOF{3,f}rư<=V8{כøЅY/޽݃ +9g a~#y14@Vb<8y#l-k|/g6_g>1_<߈7On۟.h^i5(2Ue --ŢjƸjg7{0uu&oYl_l3fTVԏs,/#qt33l`X7Ӹ#Ç?~Qc4^_f_n~! kKڅgi7kzXѤ3#햽P'F8 Y~߭;!ٿ 6־~{D`y+cr1+{ vfXC_ǽ[SwsXYk>=J}v|!7۩n. [^_~z%~/ӫk_ix k^Vz-ɪ#Z_Sl*ZaǺVcHwy )e]Wwۅ@=ǹU1i_͈z:5lLsmH!a15K:[~~J=5<>.ӡ3D-!lxSrI]뿬عfgue,F5Ttu 3\yDu6 !ny&c2YLpr$L XѴ$$z4&8).$'F"%) +[䯱~y[tܖ]۷aV5Ʈxdnѽ;<(+; {K>ܿGǁ=8}(N?Wljuog¥ gq|)yyӛsZr&"Vjs<Ͽwx?ŧQ>L &;Y=XT;.=^e9׎L + JP}`7Y`2=aRte +TVu UQh s}¦r|5lOwhzd ƙ%ڽzss++23ź+z1r-hՈӘGCZ8a}GS.v vhTcHXtuTIN`JXz(F:09#٭?c1>73,WO^,0Woo.Mí <;gy;w&eo6/_<Ɵ_e_wHx|ϛaWZw"w!)(MFl)j(?O޺v8?Pn1bu}v_Nӯ*cKeyh`=lqK&kwoaxX~E? +cݔ=鵶7ب-21ޘ`ysbAfEwԿ`X(;TZK9lZꅲ t.%s͚fgы2K*7֭C~kp62YL˸C>%:>!:'!61ؒq|0nRB&E o͢9w{jS#-ɸ5):[pgvߟ!deCC/8[~;K4Q=r' |~s{>+kwtx߼ϋ]Xo;#ԭO||? Qa``֩@MWgWuu¸:-AodZ=+G0?ѦLbR9 mZCGLޤ5ESHRjka^qfeYkѫ<_-64 7g:poWc~e;K9*\Eu_ȸ6aYRddNS +e8gCD?~ n.]`hW>>//;+079qruvz@_¢^lj@6\ă;X콕 ܿ='ьf<ܝSGwW?&?|O6п՚Mk1nΑX78B 8:[O6wF

S}TOKÏ5sz#tzSks0S\d/=/_k8nų6{eatjt/cG/Lu>Wn3sse$fFlAgѱHO@DD"x/RRoKK8df$#mk1w{$2E`h^Oߙ{lL¾8|h#fѓ8phݏ},:#2qѾ_uv9:g7T\ \瑛3RTף}ξX<=OEE||w|ߎP5Wt†.Mtŭ<ߛ6`;GZESruWgzzm\'\jUסTXX\P3*gAYFa8kX[6as}-:CKgT\ޒbnf?{f=N͍ z[_[,849[hhqoO?mxKx7cF'EaL]Owꞇ<6.V 7;1;1,a31<\Ż9&0N˚){DoWn  Ή3a;/+y|d?~g?OzAxxK{_T߳ w ^7!}p}/dirF@IWͬE2jւ7 }jzFςaԄ}7mN>u"dzzx}몞!pm6#̹Ny?!vc.k2e;<7ކF"| >h7eCJj&bR+W*(Y +#㒅 D"Q'Ά uKhE };{y-=[oA:gelvb׮=8 {d@!8t¡'q1>;S'_kƠ'qS,=ܼpEz]]՜st&^TSUqZ0ӥDo|{ih@#ƆDu蚛j9h._{cnzm{٨(}y:WA{\9NIpQjP^ZiQVYf2roFVw njos!^نjР&u=Q~T^uZKU^Qr+Vοۦ5 xӛ[ʹѪg{ub qcjNjyr\.91Y[1#3o.fp,G>OpC,]-;[rvˋN`>)ƙ.jGԺŇ 7nM|bϿ&//~lMnkS|aW`f |&] +]slƜ!;w!뽺LM+5'/1ب]ջf͟]տ> l^>ߩLsy-p=\="^ j㵖؜+h2fU10WPϲ{ 8tֽeD S6b*pꭱ0-RGq]^[{{C4sh+_2™ s[1gY WN_B:rKc"Yp91"z+"be@aC{#´(&`يp݇?zw}ݔp3oO7zozMew5x +|}צfӵYXړyP0cbÚm3$yfel7<Noub2m1;p?[0ǭǫ~ah K4jgjqS3tfݬ>Nj\B_֐ٝ?]ZlWzCB AhS:4\hXGL\pnG#!>1D\t(w鱑+IV-[Ӱ%5kBzjR␞*N=)ܞ4߉ؿ7ƾ' ޷8؁g sS撽[goo™7K8%C (Gn.w%UmPW[1vL)o/*&;}M;-L\/k?x2ۋ}ߙfyL Ƈ15"ZiDv3̘sGV3Fwu/uoeUdkdUW@=(K+Y$SslLjpˉnѐlCO{v'[hMFw#{oe9Ҧ*kn/=ǝrQ4IяbrtP/u-J2ejS}2+'pw Li[3[goo?ɏk{WtY]OkM{._Ys4>xƟ-g\/ѿ-;/_ݏ'*} +یq&uH7XwS: uFؾGXo_vqQ뗜V> ^PcD9_@MpZXƼ&z}NG \$rv]H>A63gȱ?Io6z:->S+'cNcStn;`˶z~3sߖD/q~wZfurͧC.D9XanFF˞$3YvDkw~+SuBdDX˅$F"9%IrxlIV2xNdef c{82Ҷ k~s;v/݆c8z=СL?z\ +=qNQ~y8qE\-B|}M\&_W pU޸)GAqNa|/ͰSuûy&Do ܢ>Ϋnُܿgqh6A3<1$\i`KN{12X)L-+TXŘs[-Ƅ :?3Z]Y FhL8rQ3zܠR:Iȱ&E/ ith-Bwpw9kOSs}^WWS,s5=!km0='8?+G0E;Տ*Krm_ƞxg2:6kWFKKwȺ.)?f%]O۟o +>.+w_[Caϖ[zySѺL*{-?߽xF<ߟƗ>[>~'1᯾o~>ϗRjB?qբ8!97֝{ePׂ_MAuԚ#Y~kv?0\dlU-2jrڼ74|mAep={٬˲7G-Ĝ;`Ŕ% +nkyr:1nkδ ܙKeیzfyQȸP{՛"ԝ5F +W0ǥnA#H{*/CuV~{GDD|xM__a&󩑃9)B4b]9>ݲ͚F*$̭zZ5n<_8&7%ڜyxzK%!*FpD[='߱8;()jmۡ5=s]z5[1<߄z] 0>$-nwOҬKC\Z,RoJEaOzNT~_sVaz\1V4?(oBFEg!y_{;uAu7Fl E7 +oElEOI/-9N7wtttk_y*&DN;yTnU1+|fWsO43.4'zc53_\Ʌ=FgD8_+LF ):QS8}R|uTjﭦM,UZ̷UxW4_{ze5֚fNWpkEEӶj׍9z\xYgm֗lJ~Wz~lQ^˽zLgPbz5Q,0\~+(Foǝ\Ho)34վiul}Z*+5Lp;q]eF#g(9J^^~ |H:^nv;\\8ob7}iOA~} _H?/DXh("Bg.@Բ\Z8 +QY +7nƎ]c'i %67m5۱&z3VEG݌ͱm[cGV۶ac8HwCc/||'NS8I-]4vm-G?i402q$&J H*$oG]@<'f^ENFyYQw]ͳ_fEnm&m6ԋzIK!9+WY{W)amrRM\ge.Bb+bkb[H/N1V^RDAf]ҵ׈9Wq*A_~Sdv:ޗwޔcWV戮Fɤ+~9VIUx5S+Ʊdb=˹Ҳ<هsPRܭPM%yeo{zj*Ҡ!5l_ MǷoϟ w```3F0_G%b߉OF0=J:^0kZ$=t;HzUx4V7~{҆ƅ__[ךQ61҂nuyfX5fbd_Xq?_ShX8%5M}gy߲TTоt/ _:+=LWN4D;^=@zӈN<Ɨ/gf&1:ԌAb5ǟzH6;G!7{uSѾy,%}\%uFonMF:jc[h>_fnjk^?Ap2&.=s/o+ ,yhc |ݼ⩲JYhښLy] ğ5,jLB0GYE;Uжs>Qzv> ~ɊrlO! 'Nbf$jIHQ4{y:;댥ڠ7]WEjy Z c;s\/ +9}k6KbF8v kO'cчz[MkE7zz݈n71S򿵢{vBߩm ^ګ:ZYStd3>LW{ڹs{^'ikzF c47#W~Zx \\Xݮym:9a]}s5Кf0}XsN24׆ܷ ॰BgzaN]V\,5MuYk̐8B[qߗ[֧Yŋ9-7j~c56-o3_ChNҖjݵ߉Hm^4(})ﭳ<>'9AqeAxWY{9,l}؃u%~Iݗ߃cè7;$f+줽-3K_r}e[zlZ;ž=c 5>W9.[Ƒ wB9~;vwڍ`q1" ;s4q;:D,:un(ģ +|  _$ZY˷{n(B9]%It1k[n޼қX +K nH\:'l܋\(sj HG7Sꩮtғ7os/3HJL•3w{jDsh:[wPQURtorV8Gx8҈BڪkׯKSvMC>Tw#ѽ'VZǨo޽tmJG{lz.~Gf.eVngSfLƺԒ-.ٺc!g ]W*ǹX'@ f'rO&d}ֿ3]lGWG3H7cd$..d*uW\݀0Jzs=JbJ vWޭ~V1m8MQ3=8Wo[%1 +͕ o_Y>uV~Dz'|BG~jVZW@b)UN4y}~!^[Vc8*R ;)N6Z|v|^tl>t4*Η9 US5q~ wmԩ9][ٻ̋?4]QC\{^8![/+<^qm͠Wzuj|[r_[Us2sR禗=S7;;f4xHst,#aKžeygOV;w*z%"yh[{uW7{HaLN_gWW/{9ÕAx=Gz6aHĬމ,^WGpzoE>_H]@DE "<+cti;jDG"),#/1X2 +6muظa36oEƵXvv܁;oÎmV8ܵ v۝Jy~+b#qgp#ӧeh+}֢4F: 15e⛯Ś{-%Ƒ-ʛ߂ܛ'e>i_(MBI \uy٩JpmvZE%_y9e\̞I8w,H^T._$=>\{|.']FVn!~GI@ƆZ14Ђ.|lϧ{PW[zxrl{t6_!k=])lxo[ i*}܎/wנAU_>mO6ٗ4)?7S'߅ՙpΝ{ yu;Ղn}IKA\ %6boE/^X5JQ=6bYZo.>?uQc s|JnNj8F8qr,\yV27ArtL܏fHf=uvQ*qNgb_l+>v\SWw=Ŀ@'< g7G#<,qyWM¢XދI>$ͫ[mq7XQXd%G-UXtF/CL2*ZkVG#vGY{iҴHnĖ-^b⽱-~+7Nuiضm?vً{?R#8'>'pȧ8qN3nJ-l̾hm`ag{ͫa<}h#}^#ZَV6 5xo7th%^7 FR5@ϙi~ߡj"~W.']])KgQJ^Wg_ ??utv毩DYHJO46JDSw~-)hUs~yG:Uh,?u 8q,$r|2Rn(Ww ֔gJlAUi&C\WWawq>=Yfۗq +\M(vG]a{DΞNc;'%ZzYzr/e?h"t)hŻx9;gC|rã#5EUEodA̖״oeY0_q_Py!16 w2q=Mp=gegC +d_҈C5JQ]}=-CW{%zZ6H<*8+16؄'3xJ{of\F|WW/zݛ.SmEO:H6w=hmG]} uJLzgh<蠽 +qy ]xpnM&j$<9Bڹtt=jƵRK=>Јks+9ItLXqA~&Yńuކ{x CgbeelܛGXpxU}|k^|XT?ToIlҲVSz1mTFY@Z<\՞UaϭKb*K㷦G?70{Q 4kXu2'8jw:=GgOm?)Ž\$\ܯԜs6oK2zćCeJGKQj-|[[E-v}E9b3 a'Υ:!""AAD:^z1wz?5OůSN; WNgz;lrlڍ]hd/ ⾃ \\MvvuV|w1: 3{ѱ]FDO$7`]$̇xCׁ~HvVYU~^+fJ_7Clv]M qe;6uشq{Zw?>ݻwⓏ>"1z{!''F N%DiY׮\3Hޭ&~V'3}^7M005${X&.i霌(FIܹ;W*4qzZ5yc޳E˝muZҵxp$FA +4 vq~j- ^?^HXJ8{'պPU}ұ^w篅~TO3k/mԹ:YonN/,nٚ;N'ڨ&ݤ79-KoPgZwy:GxKJBmI-汢h_FUtL.b+8pZ(Qsd<^fPjdcИkk\k`{?g*,u,Qj]퉫psgl#grN^Iߒ>&ll5/v0ykseF'('vt nwp7#pk_E^ޣg 8L:׏/BCB\ #&/+WDA@,̛!jY8ƒ8HFCUYe[ZvMxk_ظib7Ko솕j8pLM=cߞ8^/}8(=6A}߳Έg\"ܻƺla\-+NVۚ{TO&2\[tDH;qWv#I,!':6n]Eg쟴۷+geCHL<pEa<ψ=1nʵ2ם{wPRVB#~+(tl;@SCĖYȼZ!{[7/)Da=YEEd_}M.E_M8wsp/s~B<[T1V)qfU8O +mGGV3sX>߉߼ß_H c6w[1t{iVbb#^vtOo^locCXEc|߿3}[Λ?%MLYO̮@U/Q][zq4g^ctͧƻ\UՕB.%\E2wZ#> f)k< +7Bg4}+kWw^5֫ZRa߂5~Z݊S^bz}S͖A%Tk~f^W@ Wc{sFWUM5KHJ6gV˙)oeά7Jsoֽ\Yv&j3 zQعK1_s+:Fmd+ \kCGwҺ2[Y8zYQ4_#D~[VyINc~-u6`3wi?@nvp;<\x9#4O>^Npr+= ?`oHP @ZE R4e'DNJH乾Xb1„`%Ĭ(ݰnqc7<_һ6{jbݹ%[D oٲ۷¡OMҤSiOp!x>g1$:gq6&ǷHW$4_OgK".ޑx4u1߼F*]B-ZSQ{U{b9KL,Bz8Uzx_5;wp5Db䗉Wuɢy6akȸK9#wH?gd0Gr9_ +3#lBccJ K<-.iZC) + +se|MM&,5t x}E2H+'WhOBpT0y夷0@l^''W5WCLa//־C}͒g?-{7K ɃnkƗ/GΊ!{HoYo[[T']o'];sỷxd]}0FzՄ_~6~c#x6=#jFKS-PԊ6n#"9}}==mhnC3FTҾEf.glwZk*kmO&_ֺt>>ZOxa+hcZVא{wUST4Λc6f9`uVk9nz5ֆ]Ԙ-,ʶՙ:aי+uPL~Xn%Q*'s9B.?bUHef&O Yr~v\*#3 ڱ ̟-WR?"sku3_#ifQ>`C+ \#yj{{W/=/o֘m?A^f޳߰wkMٟuֳwRW/Z\];mcg]O5/`[ߩU5/P~?t=V2Oy>ck\%x~W)K~X*nU:HYgćY'Rq'ݗx%n% .B`6 +U?k>G7b9w˷vGnGyɵzg!sƍwn/#6JgsǩsŠzer>c|ߞ;q jO1+t0^c57 ]႐H >ՍE=k +􃯧B$E@"7, F`X֬\UaX2+7"&z5I[1+InǦXa3bׯn"nَm۷c#غm7>9pRYOH=s{&<]'*{5H?Pqa:Z mhbL+5Tf徥9SV3rԦ~,k]0kYeg?Zo< ^Z̜Z<'ʢ^J~^i[T˭S!{9e{&R?Hc:)FLZ9WY<^]f)k6dƐb::s{%OVo_vsf }+o9伍Ws텿!3/&LJı,γa]Wxv{]rIiR\] ձ d{]A~e;w +QDLH<+grѥqzܿFN*5T.ٳr|)ci?^żFvϰҿ,Woy>9m|ּޠbR7FCҫ8gR屘1X{ܚZ +2>~~,|y/H?xĘ'z鼙d7-eOvk8QG: Ug%1S$k&g!k~j_߅ҼAɉ|߇;/*{9-xcN+ V|^wN8^s06 { LcQuZOu(3~}Ybe.I5W{EKNu68?3Y'Y̳r 3mGRb"UؠYi_A,*^he1ݵs*s+;˼_ߢl[Va;{{{-s~e֒+\\NׇXo ҼaAqg blDJ o Py"2,n+,b V,YL]$k]1kb3VڕH+/ƪ+jJl]X[?;n݂m;Ioۺxۂ`8|:OOrܙ3{Vfߺ}xw4M ql8g-ڛݭmG{ ks_wQ]N8\!μ9n2^Eڭ2^zi֫HI9<3)YI쫑Hx1_9һg38u&Ip>({[iWeQ1uwQ|e|ll]1I92K+QSzbhs [ֵH6$ˊE޺bbݶ5L+>lƫGݸ5֣Cx1~~x?<·xNjryFhǏX}8i!Ϧ:30Mz Og 6EyU9JKjH7 =5ܯ^Iҿf Ǡz}߾{0<<cfjߓ>tŋ|+=_2@ci־wzEooi9.k_'Z[yy2Y*Ysˣ.t:h_~n^rJTۦԌblߎ"&\bf8lZ8˵SZWޤyKj5k68)Gii}uw_Y}sErq o6ںzl%qV?VsAIvá}Y?K(uT!!` /qR$+Ig!lŧ}H;\g݃*bt*o _+!\<!40"0 ~Ԓ0 4WnC׋(l1;`:+v2DE!&jVG/Ǫ^U1a q7 +&ppߧؽsv{|b-m;s@BDfɬ Ѱ2Ks<@rչ2gUdyc(l "6WsH+wJ5-׉}v:ϟ 7_=ǟ-+{1zg3ĤbmNfƾjg%*]%zjdvPVM Y?G;0%TC2ψ<ԅ5܌ztсv 4cEãuxrqR>{2M߿o}W';0ޞ~<';_?#" ?HY\=;A%L01~4q}>nK=ݍk +U 3LOԾ;<#tي^z忁smhl|n0gh>_.7хk-4|Aˋ[{ǔ+ +nQ5 n_eϜ6ng)qӌi.ZzJpVjMu%u\BK _-"VysVhҖqN앺lZi+4iMOy5NvnY:y9b_if\eww/={xv{;t-<a + 5"#~βP`,_0##!24kV-ꕑذn9- !51K~*qo:ҿ+8pJXGwٍݻv#幾w_Ç /'ɓGd6ߩ8I\;E5ynYgJIMݻu3j +18Uĝb+x؟!~|>_QHZY0ʟے:KW%߹''cyGٷׇu#X5`zX?Y +;*,~_uޠX9@|l + _b̭ͫ3*3׌[QQe0VyNk̞SOם8`ۓu='ߏAp7B9,Q%{,[Qˢ|7f%WD+H.Ś˰nJlZ*48>ta&M;wJ;vtQ9rDY)ޒ LjqAz|8Μ=&+''i$ܸ~7QBue1g r=oP%: +$\Sq7QB $~N&}zQrn{&'*sO>'i2I鑓O=W/4[- ѣL-F777cw8Xhպe6TfQU"M{[|6x.3nv&du6ԖJ-> nsVScW57KL~98Fg~~/ /Nį<*L֞(p7&':tC!|j4^͎H_P?1ӓc3cѿ?'׏{>>7<=׎K0%xCWG{V9՟WRV>[9m 1ҧ)8;ǟ?߅eܗ}Xo {C?Iu5ZkbkҚʟ-f^W{~{ngmpv63{3c`RRfy#O(U5E'ҩuMA/7Zp7Ƌ42e)1[\NQu9<,tw:9p~Lae[aRc2gHkֱߥc5g75QT}¹8p0?V6zG;uV׸lz-נ]Iv @}(.o(Ϗ owc򃗫xMze0W, E%DX`EG- EDx Z"L:x]Kv2_#m&lټؾy/o۱{^:ݲbcs޹{/ډ}~*Z1{8|1$| USR+DMDm^F̥ +҅=^n߼1[%PS&ys +2גd6sp˽ǎ/t<6n^3.tq8rxDq@hyIJŬy ?s=tUyR;e7v/@ďe<(Cv++p'{ߦ24֗{ mQ,\_>vdĜz6Bk ?Ɵ~x{_w?ŏ͋!Q?\n+&eοjd&lֈ}uzxn Nݳqe/47add?3?֏v7Hnkw~WQ|fDzX>&nN ]O?ğg'OxOяى~<t?봔ZNѿKQ' +j\08omj̿M:=u\Zk͎O[_.$fiVoz?91C9y6sfkyTW1mexYM3e}m㜻FJhf˾/r'~\yԏĜ59*\Ke#ί1jy^%gQ WYaR̙Vfg{98\gϓ\-{@:Æ tplhgO|TzYWL@>N-س3+{>n5ߩ輪+5v<{Ƽ1WWҬ!Kp&:<̳{:iŴ#›#<ܤϗx\3B Ţ`-8KIFFF bi"B#8b -_A^ek#bn&l11_kC9nܴ1qFz#ҽq[w|Qlcύ]k}AkObテسفCcGǑGp1p qgp]/$S%]$>&I?kqN*22.{o0.*Ikw<9ҋzɯ{RC\tO61N{Z~iQ|b 7-~$9_ҿIx0,=]%mlks\+3 x/9]־OmA_O iJz8;'S=2; poI~d_q85M-=G_|Kx83\57ϙ}x:M3{$6uW-m袽B[s y^k,|/]3}=׏ͫܟҏZuۼ|BX=C).*L5Ū=msǫ7b^~b?djP~Lrs+~Aֽ:,6,P/Z W7wbh 縱r^l %?2wŽ_l+uң#z^O]o89dH}5=Aa ?yS?9~s~Fe{R9*uѴ_o>V4w<3ćVt?6'0;~:O7 nNs"GGPa|_OGzx@|`?_rZ, ĒX޿>9D=C8| /L:7<.\ 'rx؛+ɸq22@VF*2]#]:25#[ +I畠E9Ĺ{tK=@h$N::*-:C{%9'x%H/DYt88p8*y`9{a{wKnIӲ23g:ޕثpXa02: +1[Z t~;TD^Q1Bcte2Cgj)<󾞴qsC jy6}1cO|%go4-x>s{ovS#lboGsJe63wyP4bg$[3kF;01)͵ScNa33 ҽ&O9'yCxl_y! ||g/f|v/o>H?kzsz}xK՚京.-hBoOAuգYt^'9O+|^JS8VP]vRjCG9O2(Igb͟tF<}Rnb +YWw> +?Ҹ~ y ~Xd&@Ops B!!G 4oxdFbMTbf9V百QذqqXi w7gׯ<7pvBۀ;G>bjm O_VNq<G'w>~ +ǎ'yR{1$&@R)+\NdU>oWCQޕ~EOf,eR#q9٤=y›ͼ{7ٿ+%I8>I':MI:ct O %xw'{E{63 I 6.N0X뱇]e}wʼnLj'xzN8F8xa8G9w8|/\ADTFcbo >ō+}+w#>JCwD>cY!}J|xJ2ۀsgDbI(*"!Ԕx#\mLFJsb%Hboxq=w½oݹG40(_K:k7Kʵ;qj,ବ4d'K3}yq.Yr-/IWȫjW&+1_J#~JLdd}uoO>o7iҎRo߆zҨVa Zb'$?mN~|9^v̏^j%m@:6V^R$0[GHc 9v<7%:;B9S=` ;9fd/Ǐx@vo͈0W 㤡7 q+bIb&賎 IGwI/ a(qg8A:N$f =,ZӤWͿjzK'|]#U&{sE V^-Oj50昵~y_ӢWz#_/+GַiSr,4kF[[g8; AV>[sKeo̯k{JY+X9|ߴb_.c@pt] W;e_gq(Kؾ;Tރüe/*T8볗gn~]+CRest]ĥmYf +::ى=3~Ím-\\i<6I_.6w7l؈6bfٹ;76os>_b8c@u>|4i#:y gBpKlqDYސ`Z!>sqr$gWd\+a|r)?SJi;IǷ{r.WT 6xHdd Ut/vҐ|9?ٙYEIHK}8dFKszc$'(ޒwܔIo{p׮C=ƽGpX\#F߼qw\C<{IM: e%ϣ@u3I.5<zS5qp +(>Wr\ѾO4`bͤK R%YU;j>6`j? +f2m8$͵hks5jkv vgsfڈRËMX$`(.LGI\FO^?/oӻQe@#i-3!b怢GHc=&Hʾ=#sgF:iq@znח+4m%jeS _MSk߻_ۮv{zmi_:cg㼫3뗞S<+I<%Sduon5+A:]y`eMGopM#G:sʙf3~^λ;p}[/=&~\mZItyF2{[rkD:ٕ,f_\$[pzⱋxKnHs#o] N;׃Fݻ w^:~.[S:D NFGqaSp9pΜ0\ qM_w''33qĘ#(}cHNAVf_D.d(Q.}?Q +#O돤+y;t\bOc8Ħ栢 ~?\C<=cQQωod]VY7AKc:[ P~O(Tr\\Lڸ: ta=rc_^<~bjx@Nsjϕhmy57Tϕ vj]xj6KW??NFof:rXYfmkתƎFUkɵk5?Ԅ?ħ7x;\풸i_Ͼw CxuR3iݿ~ 9߾!-?H1@g^_-\73x͎'֋YZ\5/Zu8ǔωg!u#:n|rz)@zt1M|_^I"euȫ{]M~{ܰ#w]Ew9>n).9F6^~qyݔ渱[+X#s.iZhKuZFוt_dm8grJ E/{a0/U'ͻ<aW|9ZhK_{-EKᤩiֳ!MwvI, #kntf< yk:+KVܓg[8;O#s:֖?]ֆ{yqM|>v~bb;!-77W_/شg[߃CG Z<2YE|'8{OS!8v"1ѿg9tV0ΐ> +:3PYϞ!zX|%7n^'xz[8#= +)}ޏ>212h$?'J:6=y 2ۀy*󍷹6:7+ ɱHLᧈ}hzx@YHp~*TYaˑ]R&7-/id?| QR'"`e(*EJ?osC6z:(eޖ(zsK ^ͽ*#Va8]N .`XkdpYI#=;6HZxK?|ʚ"Ѿ]־5%hm(E+=Ԩ_jkm& 9qɉv7x{B7P49&7\X_M +֚{f燛-1Cufzjx!3Xtɬ]ֿ3}xC8Q|z7__~OO<^̏I萒!~`rXq3WYr+bQϰh^־<ȱy|O~`oeթ߯֒}JM*jӟ'Շ_e_v 9̊y|22Tq\[[CIc n MPzW|^-?/ Uc*1mf^rVjbSYKkKe/ZfHVd'yQɯ|/Vשذ{ߥّZ{m v۰}\IS{z=֮q#{RִgG{[a5ʵ`+7n{ұzI]Zvr&]lg5|9ZJ̙ρu-j-h''W+Wb_]MF4˷7m:za=՝g n-<Ǘ&xanY^g 7Հo/?gD# ‘8pFtk8|ALj8w"."8h.ŋptpeܼy=MN(Ҿ;x6""y#=3wyN"ݚi(d' /'%ĮThm8kyi4hnf2Rb'=^VWٸ<O Lc즌4ͯJ]niޏ=}}l9Gwe2X6ְI>>,5?p׳T{3$- +'U42W?Bi60+ؿV|-,ϢZ=6Jh#sn •돰iVwkqϰ=lz8 +Oe֠A[ W8m#{2L[s+"%g̵V6jL\oE{Gzttp"f[@L&k#n_` ~-uۤmIܾ [6n!&{SvMZw6xmϭ}{qiK\Ѕ_/>4'?:y' #%CYbqq\ {1<.\@Xu!,.!<.^+ץk}@$ƈƍqQ{\4j\| WdZJ+#`?eg⺦҄Byy:o0^f &&='GOo;76iX7bLz]bǏٱ2!;#YVnV$<%< +3T6n- +e%ihE{S5i k:j#TύyBJoPK𷶺4i9ښkPUQ¢BFD qsr /ډRq~p?>.U+=Y +z-z:tH箭Bs3R6xWSE:4w#:[ Ҹo07-8{WgV{LsrMtD2v\RX9}Sb9ygy2O/\V:͒j:Rږk,ů@Qyme(X⯝ \ }2{NfnuE7Z5poK:8 + M{}[̬Uyiyc[Kېne[kE\jsl[4K=q" mkg'\N`[EWZ>4ֹ8vwz7Gl^_gnX[6{vbv?ھm3vzgb~{;sv6<_8t4d6oo'98w g! 9uB. EH1㺫P\p C{,·l0q8(@ ^ /_5?"-☷-ijGql,8bb"4gs*Ryɨ," ԍzҒ92g0r2g(%%!ܹwW*<{=8#!!)1ҿTx<{G݇Qy7Rrď"-9NWBiQ.itZZlZ|#˸޹4*%[_ VwkI{U\px8yPO4h%MFڗ0+~<ih߆2ڟTΡŜDcmp%GۈW]\3#w݋^ 4>B6$3MUqz:f,p%tM3Irw\jGO 494i߽:zAΧxBz4@Z5}a }=關(} ~rǹdžx<!M_ܗ5CWnbv2/]rz_~~T=rJV?U|T뤦k׫kWW=%VYv%v7wl9 &{~,ӿy|cŞҤgGC|!Wk=K~/S+ھCtÕNZ }~\YWjugVvNص |Nɜ`"5p2`䯓_;[e6/elܰh#^[7q}\qXGgodso344t wE_(:gFNObAg %K؛碶u\g\\7meiqcb[ 5Q3 |KM[&[H݄ĝvX}\ʆ N2opݚMX nN䄍;6 "nAuҽ;}nVNM\ g__7&]g7=p*ΜE@>S'O#8sO#1J  H <,.y_ +Ƶ+<*.]KWFLݻכ9DF}ILqiѼ`]u\=%*g ' C&v8GhO@<#ulq,xV)q2YEhn,E31y+B^a+P[=Ihl$TV7bv mk&63ۅW fZݖ*#9 GݭJ_o+̍G=¸p˹Q]dIzl/:/g/?_Y 齝G/o^g=NYct{h=VzNSsjPW߄646t4l?ǝiQO^F4ZݴHg {. !s#~_УO9ū̓Zʹ$O3U˖<^s~k^P}@->{ݖ_b*~X^Rgkvs{?TuZ>M+0+T|Bxޘ]ړi^!;A}\տ*,$ng{+uV{y`<q;1n%aPރ{ "tN\lطŁ-KϑG:m^_}̟Kf.cQFJM~r=t6Z PzT-g&wt\G][$8r߰5ܸhָТoyf۰ޝ۷bvx >oFO/Һۤ>Go7v8g2_avم={|{J:al\@@8@Pq`}g\y ŕKCFܽv!_#{7Z8q%ij~2o?Cڪ'zvW<MO,}"W>n'vvԣӣo8kޞY_ S4~~0^⟾_?Y ^ >|5߾{~z?,K0 uYCGHrxar5>µSu=6+q3eK/w"2.cYYQe>34ٮi_}]9.6L_ǩ ҞfgrJmRzuYՒo٤|577P+K?4V_N/3z֣VPf5׍9ԾdfsYcV=+C0ډ8sY.|9mLXnKf_*+KZY{ۊ˃ ~-汽1{:'7-IZmfڵvظ nk6}&;lmၭN ];g#vl}k=ow|/q񁯯؏ :Χ'8v'#( ΆDBgϐ "l~1W^ ^ExG1!> I&'=C\#ROLCvF2Kosc[Ԕ$?y(IBVf^qooq 1b~HoT)2_8--N7`j eH@tq=Dzn ˌ%흎4ff +{%M.2KXO{,Q^/>\y\ow,q?"]+\$wvw>B{{v +a]OvxNדs[Z[[YjZZ{5~ͯ5}_ߔE+şW,m|ϦԏiZgkyϯ/AkOUfqW&V~+U)W3XJ\"^:{lZ ~[Xa/1q|zr?_io,U[GKqOf<Aɬ9=9tsrr_jǖ`WZt.Ng3{mq=xzjZ@86n+_gw]5vXϳ7m$;n%kZ޵~eݻl@!O߂}ג_/{/q98r4p?:D eQwA6iڐ09}VCΞSKaC"19 +J:TW^ bpN^.2shHsMM]2 n-hfoGSYZY|[$fCy;39$>?9➢q|f8:ObfscR[54=LgwS >'<$ǟ^w>s8~04Ѕ~&K0yp.{\|-^uOr46"#!6ZџiIBTS<})(OAY^,RG3S.IA[}>:59ğl韭"͙-ώF^SѽNK!'<g^i⇙8d$ ƳhU!XdҼΌ5aEĽirN'K0/K$)ϣs.$档xXjnXlÇWθh>Hc]ĝ[[eoQNZyM2Tzk+5kni/nGWVB(~Mdb4 xs~ńHEn?~xW3#C[Owgex;?މqLONidb;ƿ)}=7,qꗳck^;-ׅq1u6W31tnWgq|9\#?_Y5N +?W.|q-M}y\\m/6]F/c/ղb^ +*NcfMV)cc=J[x2@Ɍ^cKySݬ0YGL]V2PߖyY31C[䘯Ĥ%2kdV֞4+E:CL5.+ɎH >7lH2$VYc1FrΎV\gbظ=\ny^ҵ>q~:~tis^^;swazU:qN$;')G >F=I|Ti݋0]qz8._ƕW,O;+/^_L%_=p Rf-dzRXsHdơ2ĥ\Ғ %Şi}sˈe<^kPUF8?압hf*u\ߕOYqHgNڭX$$EG I HDzc#;k2>z$bsBbSw+ŤE ؇yrd>sXS25{{:0_+uWS Xow =ż0*<[tXu,-Auu)*dEKJ $/K\eO2ow{%zӪkxm_VΓcC%wH^v=\ԏb/3?~xhqniqoSk?uoE2y(GT_Q?"Ίևq,ΎH?̘51/>7Ikx/sT+ߦ&k$F\̋ lT-_Ųجio7W^VSmVx _~Fcj*ry,^]+X38ciev!mZSp_'_ijl隰We55X kqG]X悭mZl <7ǃ >Eގ`7oZg]^|Mz'±Cqͺ9.9~4?N94$A8}:#8$!8>7o\FbcDE?onBl8.1o/dإ+p|0Kw'x&M7Xj+OEIs/ %)h-|a.ixcJMQ2F$C5(91%Ytk|M1(:+%ŝyIWˣBm5u\%gf$jrb)=D[ѲJBtޚ}%vźYs/^rƜոyRL(ẝ`MNذGK89* m@ ߺsl!MkŷkwݺMܼmHKh!:˙lʖm r 9&mggWmX~i-XFŎt#֯ ּ686ִ;|v9bvw؉]۷ށCwݍcV'z}wm>>/$ |v8ᇓ q;u'O"80 }J³Ϗ9_TY\4e,I,M:, {rQ̈'6&gV!{.d!'+3ݲ4;Ų4q63$MHspIbm*RSH{{璶 }E׳˼"ˋh/E[KetbNbǘ5_+zG.suTOڵ\VUy PX\rk}K i + _O׌cb=I#%v\EA+A|ڈQ$E.quW} ]ʼf߿ÇiҾx!bmqwpdLqNxatq >)ƇS~Y?yw xEZw~vc|AdęF_>rtC.'s>k[iB_W'`t6+imOo,KמմL,;?5Z[mi,O;_ xgg&U룗j&W?ܫ՜7بWsǬ'kI}ianZ<̈́^ Pj 6Frui3X׶RNSl{JOߏ~zeNAScڌFVK2ψ{{ۂߛ{zA`dQ˵\|2AԩWmdk]~asڪ^V'z5ζ_U+h$`L^~vظuع}w&Wxxl؊[`Wxmلc^wΝގ=;{8P=rG\~/? :IpgΜęt +޼PbmPp·O@@|®" aW\ǵ+ti_>~s;û|~ gQ_֢_?XE5?I`XҼ o}MyK/qkϹe=,$\pZŹ߸爍M"^W e~i5'G"q"d ?/K(,Hϐ-eU棞 +PWS$q_j-CO+8X?LS1:P+_]?o1Ag Νwod}1%{}/wtO^Y\f6کQa0|vjSP42ґ|~3Aꕸ˅i+NSp^xtK)t/1EǪW"= fK H34X̺{=|mC|Bce74nffroݟBC-,Ȕ(d!??)Y%ϋ{~k*rh-PBt4aqE3#hPćb"ekQP-BzyD#'/_++'٤ K +7P[_AloCw{=4p-qI6oC;؎r ^sǧ9"[bKbltT4+~z|}^7w_?_>o-/޿ċb'3~WCzIg3k``PsʼI/+>!Jm7e#O(3dn +c?_&f~T&ϯ1@7y=,<C٘U=#-9?y]Z !LukZ)koBՠKƚgȱg 5glir-7XYƖK._myPbm -`M1I>E4jVM3#~JXl,3 ~!TБ.6h[=F]h_kRǕ~X…4Z:w'{fkǾtNkI:Ύ.Xv-7nṉn;uqwxxa֭ڍIzgV~$^Os؏'O=8}H=}炂|*!SI3!8$C"\t a׉7vtoY38礮k^B ,טy9}M9?DB\ġF<r=1s2-5ZQQTT#/+V2yI 4[ռX'_Vzj2L"ͯpOAy*O"%u $o||R3K8ražs.&M|y eXRUcf˚hGcx_ހbϼʊY#yyŤKyFN~J*JPVUDέ UuM5RGnB[KĠ9fe46[MGטÿ<7>H^7x95~ ~"~QbIr~|I-341&cc=;1aԿ3\5:![8HrBK6Ǣ5fC+NGkvo;xףQVs 4rOo}~HU[kݲeToƣW?_9×f3!V}ӼMV#1ƲZƈ>-6b 4yn/khXK~Yx]q-i|OM:שbc-ҏAI_^VfYXzKc/6橦MSjݲ&>G+BRO7iyN{k8ӹ8fJY\'&slيzV<Ivp%y2P붑:0gA8ž&3Zn[IVX3:+qk~N:ػXIݳ#ogo/ݜ]ᾞ+1c6s-7xc=݆[];7{7k}s7qy6ؿvb^b>8t~{O{/9kNx.!8I=&}sP i0kŻ27$$B:.a҃t˾._g~>/}ve߻ < \CO"'xƳx-!qˌAe10VjIcf!5X|dR^q(U%%}>H$' CQ%M8>FXENF 66!qIё/uUt^}Y,-BEyO`..@EY+2es'6W:T|#*0šwӣu2Kart=U=Fƹ\}841<ҾŊs,)ΗtY%ibkʥFHWՋh4Wۊ?$j,Xf*s5Kl+x(mXZdPޗo󵴳ws"E?$gW88 +4HxX*?>juNqy9X9`>nx^$In܌Ç`}G^<^;^۱ 6k7cMy78~ w{Cp~??b?>sΜO_z(p=용3O/^3NUhV[(cj4V'IyxXĴJ܅֫RZ}fEډ \3<[sA0+jf--sVr0ve@I9Ҳ +U]}]a|=T5|϶f`.JF+[ǡ^_KcԬeԧ}Ec> fϦM*kK81#ľ?VOF%Ǣlj٣Cx0ЋN{ZƊفҼMfTf +GmkB3:ZnTWuut 7n +TUnk^q{k=j+kpm".sgcWWۼ-Agb*Ϧ=&?S]i۩54'!N?oka^_l-,>Md`IߏT7}Z{VGNZ֓rJ|l?uX[([RW9ċ{q5.tJ̘1Y*3''W J߭~eW+!WiW=$fW5}-{4Cb={Fu92/!'=7<(1H⚭_ZE 3wyY(ȎFNU \N23>?'z䃣.n)td61cHwupBl =Khѿ2)^ouJ4;3QYK-@UEji5rQMğ*HYa?WFI-%F"XL$='#:>q)&uYIKC4+gֻ\f0}yZ54H6ati#>hś=1ӻ'OQ4r7FA'< ܃y~o#+5ZFH'w-k}tMj9֊fbmzuL{N,&7_VMu%-I"][-qe'4kVT% ea{}u}*k +^uYc ^55Z]~f'X`tJm̈́Tkt6?̷<Ϧ_zq[_Ч1IlƑy}'DЫ9.>Mo30ڬAJגּRգ?-3OM|JQ)eNR<.8Z圶^)^tXN6-Jagsߓo6(sy6q'6xګl;-gpV OY ߥƎsOu϶'l~!^a"3iɱz%]%=G6ΰzbl9(.srfuyΘ5Dž740{oMwҳ^XkzJrVpidvlD_O/ZGw#1>>޸xض {w܌;wfٿ®ݻ1ֲagGx %=d;t/` A`p$#"y<~J8|""4q(""YGtq.}.9Vm<0Y{Afd^CIa + VA&JK7G^N,NArnֈe{1.t/ć}Ӓw^/nbXz<ٛ<.3-RǜD1b TC="|lB5ȳ~ۚJp5~EݘTiacOF)z4<5k$? kjTz~c}z0ä|D6qc%3T=jw8if1\1V0{φ}TWӱeyQKc>WR()Vh;7Ra1TវJ7^s-C}_ /!y2$7޾! ܇#wZ!>`|C}߽Bmm6p\hk!M";-jKjPNڙ\4_ `p-":5xG(ӵhnGeAWG[2籪O&CuS}&#^~y _*AQ;Y<,aŇkZ/!"D['ǫ-iSyޏAW>Ix0YKR_%~*|i|ڴɤJ;ӵQ}Y#r-ӔCZO̹T ~Zx8OZ*s,TH 5~`)&TaIcS۬aZz3*lL{Y2X'g8cgH׊^<46ֳaL̵>^GGij/IGkdk̝`[mNtku:s3gϖ9GtَpLZ6nn3xL^ m }䊕s.|Wyb+kϛjx{%{cqyݺUشq6oybۖعgvڎ+n#xܿA߈ >~cG!4(]^ 8bodX `a`~̡GH!sM̙S8y$iOq(ba$eߪqf&_F~v˽5YV=A{:@֎c۲}|><!qp/J-Pѡ> ׶ +s\bqsS#16co//~M/x0s.6tw@w^,>7˼x0YDVSٿ_/I񺰞_#{XVìW9;)>Z[5[ +--صY!֖Nj|+'G,;RO<3dNĮVzc}֟C\6=[VG~(+e1XaR6{Xɦ5`g[u9XbjH: {s9<_d'Z6mmu򙭭d~>soy#g''1FI|8Xpq` &\<.WıPtб ׳o~C!!0|q$+838SN3<\a{YԫOTþQixSCͨSۥUdM%&29Gֿf!{أtt.3Դ,17y5!Wċ˱gf67/7o +,#9~Mk1Bt/3yg/'Uշ|m5p-ޖ]xF_J[4pi{ rzlf?鑞bpR9ŗ׃}:P>JTRv⯃L-pTS= R;xFu__XrMɵғ(:kO0P㯢Uz[mK }=yf5 z!+GT|} *Wy4oҔ_u6ʬ?j +M:>񴰶K#wşʠ3yO宑z-F1Й<%Qɳ \C%l_>#f*dJPىS`k1ʒ֊4<(? ٳfL tN8]fa;̛gYmyXH:m""]t\Xܱm!޹X^yHsl8=2i$_CQ^=Y٨I:6uĴ{;7SOls&r/-dzE4uj*<_AL3XϞ_q9& + ȈWfٙNE:^HK=A:HSQT=Gy$Vݾ;ׅ\oU$q^7 +{{KP[IHۍ )i9ʖ|oBU3e&CVnrI2J@VW\AmN_5]f65Vc9#٣"^12ІW{QY}zot@/&-?G`|/LJ%gi~5ޔYL^~Ҷfs.{z:yabj-<ӡkb-3kOew.Μ9~/Ժ)\ {[(?[&MsflNNӇ|t/&J<0<=³Ci#p<86\XIGě5/kӧg_,iݓ'Kљ3q"!A*qUYhA{碦]{z(+| +RAyh<^D i脸($&Dl\2{uWu*H_ȒtdH 7**~s=v׏g'xG<~iSf^{zHwc+߿|ox~ ~|x sI0wYK$ֶp?_foogʱgMCL⮎tK4獵85?f J=8̗ 5w.FͅMǟ>6yWLN) [JNg_x PkBg3f|6󐸯y^gxG6l Wzq3nZnL~]4| +{sM3ZZ8k7+|uwaP5:|{ɳi[bgޯ0 S4gl^^z_3ts,8;΅%1Z4 ل^qO;x.o".^ +Xzclߺvlƾ}{^<&D@AbHKJčG<>.CȑcCpdqB/ǟ+ 0PCHѿGpx0N?)Cphb*]9u ^Fss.ˊ1L_7z0@9x{Mt.ǫ REZ (zXEK$FBiQ&rRRpj5-=i 2f9'f >y֙_NNΤDĥrrPX\R߽M쮔y1`ןx!Zh1LvrP= 6NM5qNI-'/m.Ǧ~~#Se{AM?(:݂5^2 <2\,~L }Y<6H>nb^שCşvB Ӵty-K~^J|a҉ƗgIS| sREl!~v;&v;k2X+^X,ksza:D9:8u{{{'潃#]wvr$l''3y\N?<bb 1˗-rWx/Ǣ%1gBt_wEXMenq]XҾ|iVxY3^5kzIM^?wlZoڌ{`b޽طkߍ~c@{*r<=,+$4X{HFnD E"D \C #8H3gsc . +i$<ƿ0~E|踃(-\P[[O *Ff5]u<\ۉb/ %Ĵ#4j2 VNCi1Nt\48rvrJ2O( EKkn$_⿙Yc\DžT޹N+Wz}Zfoc i+r 䀛doE TVBGSiFvMRߝOZ6tmJ65Utn +iwCȱ4dd+nKwtxيoWUoTݫP_|}GxhHXÛq./Kyų^á> ۍ^e +$~-O_ ++_㯿}C ~-돤1yo=9~hOO\5:@Ǿ. wwўC^|Se0!Zh/12ǃuԝ$g5Y&~⯩7Ŵ[c>Y_N(9BU/۔Y<%lN7ӼSkƕezn +M+qQ5 4kV}:)G^`27C7eZޙIC;8J}n_l-3/RX%{D+h4m9k),=`^g}`g'{ᯣ^8;5{<̤,r-̐mҹV-eSb*Yo5XbՕ7q^lT:JYt8q>ҾQ⾿?ˇI^1p7Fx9uwbb:13=+Kݗq#y-ǟ^=b?i/7==>dP/tJh_+t E$I̚Y˜e!\jp~ЋvifܨiZ^;cj_x\Wgw\c^bΖ*l\4X\t|5W=GLz>_xNF ϳFrssuJy=Ɯ$QLit+e>kW([jszɳroļ&K}'3Zwu=m[+?0(f_yv-ёΫ홵ܟf z.zy͝Iϡ5k >Z<>Z%p_6 gg]ֻs<׸c2x,'KR>ҏ_w[7mǛm[t{y|Om vo_vc}8tv|ԓĩk(-GteFH ȓ8iDdB#‰2Aud6ұ :7icK6?A +83p~ ]AbiԼx -y=XT/.^Bt\,i(]ei-&qqd+<"2RҴiWD MJ빉ķ|ɓ> +? r7UҢ=؜ĤzאN3;fRIRUZesq`*{l'>ntZy7[΁r/ߕga+<=Wb,h1V{֑la5X>||~ؼ~ K&&obٷ{woľ{0vF͙0q=M TI߲pT8y +!!8I<=Gh`0Cq{Hh〣G)2<!ds~( 'k?ȓpL~s1.&ԧ牿y269%{1Q(\HǍ!F_>sJA^6OƢLz535)hm.FMyZK$SpX~˕ӤF+&sӑ $JO|·PSQ7HO}Ѿ?%߿"]K\}o/r/^tߟ{' +{0c]/k௞ {}xE%.#3=h#jg`ZN?KӚ,'3/CGks5jMu܏AKOd5g{04ǚiⱓfY ؚ^h14=VSt{I?Ϛ4,8Ӭ؏?Bf)YÓLw7J;NqUݝo9sJ~Z{ɓa֌ 6m"f̜igaE9g̝gcId3h2ج¼`l^-ƚbúeX~%6n^ -Xv=m߀Omx9a {;.}#7M0;[; pu3lmƍi3]Do=l 7Oolǖw]q'l|yXwu {,P 8vއCi߾]8z䀰{øx0BBN!2"b".!"$/"91~ @DeCxyD_BFj8RBx!A| at\tbcp52Ξ?3N,siIWQŒ0d!=5 1(GkuU%,4/%9(ΎFѱ(ȋBIn +J\ƪe#/,= +QH +pw !~ +򇯿/|ŸփBh"]Nz< +Qe$";+yit\WnTAg#w4zt5Ԡ$%h,ESM-ꫪPWYj2ThojŭV{tq;;-JPVMϙ,栨03hmcˊPSOewS*yzzJja'WH&K'e +,Ǔ>\_LE)6~kKgiiM'|{>C鈹sW{C?rz6\׻}]gr]}{C&m+ڧ{w Wh~i] @{Mz.JǦw}I.MVjߊ^+-7V@RAчZv*\>_}>~tSh*}arJ7MtBqew{Uj=+5PBMC6>OR* 4j(}F061FCWAEԨ5P >X[}}~}[>#[bpkXXc!>d( !CM0t 13 ֘0j&Ƙі0nƏqmA>u&OIGc6e4m)`?/cbތ?o%=wqx +V5bظv 6[-b㆏aG㭛bGuv7x +zx8Sy3\ +w7'an.h't#Sy31ڎlOB*mvJyprs76ouÆMزvp8gΜ=Ç8v`Çp\8qI|%6G]FI\tA/flYbY(_$'ePyap??x_??i_rBrb(Ή&F:񕬾4Mɨ("Ơ 3K󒐙pKũĎ 姣 H$!&2aؠ g˾Wp!a CdT$"rSQIAI^7ZS&uFv%1 rPWQokm-Zj+mcvMEpZtӛgO?/^=WWwo;]voW_a&3^_sȕcRzQ/W5}qmC: =fb ae}f02**Cj1^Z#⭑1 `bjDf3MLy:ԊccscX~k+# ba2 +cF8jk3xKZb4}M|4aNFGwGcڤIL2q$̚E `d,=6'bެ )y3o.y&aٲX UؼM}LZs6l^Wb-񔸪3/b`x}<] wo^c{Ƶ +غ&eoZ/XWw/ؒe킻CM2;w8 NA\l:i ҜH{1ҹ'O f8~OspQ\;E\=C|= =hPK'q124Ap)`f6[SrsF0od76ap"N~@f2,I֔ģ 幑&V$ e'FG֙9 #HF ;LKh+ }W|20DG{WM&ކU\@QBe&Rѽ,.H#.evd |Z-^v}u!+HWjk5wun47f[ >u/Y/yv߽xDKҼi?݅niOolohwnuG}vKhǴko-xٌ㛭5hԖ8Rֺ +WHXMY!jedS?$7޵!&Ӷܬ4d%%1-;EoJ&kzl}z?u͋q~w߀~2C-|ㄾO}9FɔC`l4$'UjY~ u}vkVHӞf)lu^!i\X4j 3&x%:2=R%&ʕStMzYϚB-4fqú({eXq\b*?s +:*}⺊M|kO}b +&tJOye"m˟Նf-MXeB0}LǩjsMC6Mx?؜=tc[[b SbƏȱ#0aL`#>kҺC1aMf[`1BΟ=ff̚Yfcy;gl.ĢdZ+Vm˰yJ^cld=no]8vN9= ήS[6^M^'a>ػ{v䰁&p-\bmزIbΝtܱv]]aHۻ΄#1GN =~gct&7*##6 |K8q.OkO#`nDa`+8$e?>~^ |GBL0r"Ѩ*D]Q``oTԖ&%|һJAb| 22"YɢLMirT;Y B|vU7<2 + C@`C9.>i)q(̦ӽ+2PR2וYhGGcн{f2jţk)GeU irtWUmutL3yxG1{TbNҳ\ҽH>C:C¾y_>'/;h#!igk|m T+/bouy?gg? yYIHFFz2SSJtgP$G0ou㥾h@oI~?=4?zJvӓ^=}*'[l@M2-%sZ&BaЯͦzL&mvn+6߃]jx* =}\eڮvs(MJZJ}*[WxNhJjOdR h&Yrl}o5VyeCzдޯ,{w# L@| +}^Iv'|roIJf fl513<}n;Pc + >k1-H XIeBMahD|600TȐxk3 6c'cQc3 C0=1}\̘sf,$`T,G|]0l9qXx.KgcXM]b>Zt:a6mfb&TԴ#467\Iv:bgqNoOowvrqy J쵷G_#:Wi3\ܱs.=q#Mw7nĖmnt+<$><%I_ls#/;+gp >`ҳވ L +@SU7e#%'ī'|i݋x=,}O6!&Pp/#5I KWI_FPiDHBi^,* +"H&Q[ɚ+RIJ(cMHG-igm񭂸XI38Ć 8~ FXx(""а n4bÐDIO!v'8U%JE)A[mZXVMnkF̭#6ԖGF-֟ lEkCt5;%{6~~ՁxE%m޻Yg}bm&c >i6IhenRp[#T-:T'.Q PV"iNn&R3Hf 33驈OMB1㽹TbO[h㮒o? {qL1ؗ=~=bDmApO?JzK~Ş!^nN+%ާ= +qQm Aa:l%'wR{~ݔ4>1 >q@)*%s}7# [IeKQY# +]-޵L* !K_zBQ>Vc5qI {|ǞCe}/ O8#k)9,WVe5?#Y}ȈX}}8.4&CҮ,Ĥ V͉͆lѱƴ߀t8T++ҿ)qxLL`bdk"-^66XtV'|6l'7`-uqSP[ێ߾-ݎAz۽IzE{uu+1۶n6[Z;bV{le 脽?bמ={gHuV[W8xN;}Q=p +v.?De{I/N<*ӈ>sJf #* H+q*_?q0YTD!.olt0#G8!/@_ER|$r28Kl͏&Ǡ0 h*OD]qoC^M\.ˏ%&J>lҷYtNUEb}W f@ 3w#/(Ԟ.Ҽy٤k+ҴmĮ*Wc[-lASmkR_'4ZjlCҠ߶zF<xBg7: +ƾm?z$3ZL{_O}s/a^2Su]f3gk5h{3ݣ/~;eI "[Ttdeg"3-dS%L&M̼Mq>Ļ$S LgxhxW'kEԓM?O]n?WD|VK_{zFPLŮ8x,v;`6l cLꩫ{sWـam;~L,Y#RC9+{4$Ιuq/Yx{uZSW͜YC\2ϊ%2rq/Ab\IZ=W9KXX *j;iM +MҵQ+O"+\V{3U*x69.^kZX_G25\ֿj௾jQ54JcHZ4B|57DZ嘷!i^+6 kjJ1b0 44nq ĘC041}HL8XL23f-`$GܝE f`3g/+aG˰vR[ XW/²eX7}8}97;_?ַ}U_x:Û\α;}L==WguG-B 3mm6{gb;cb;\Ho|jΥv'޴dnlvV{7xoߍc?gA;}|\:FAR%d$0=yBsf -Ad!HL&|,ƒ/ +/̟`DvrHK梼8I8~I'M"1cPWbp2ڪŒl8pAV4Y,Rc}Iho( ΔIgq8B:$Iш +EPp`oPp`7C%[tLHsU +RO@lKGQ~&;uh*$:[o4U NsZV_GOnFKڛF\ǫ7}Ee}u><S|9xܓஈث8.Aں]ؓ-8_H97xYK].AYQI瑥! NINAi z?'JGanlrWOHuV/~Lt4:[ɕO].S_=7sYo4o*{OڥAd&ښ#"63f! u>_Yczl? .Ģ!c Ygwqzvwi*Ч|Ηұ9̟EUy|6L#5xDi5|=e]K҈c"gcu߻.fp3Ǭf@A'}kD̜eL"Wy=fXcͅ50 SXYh0|0C1xPLka=e|`.ld3fΚF` 6gfϳqX`~4A5~ >)JnPmgm/ҾwxbWOO_giS{T,_H<|${55V+ݛt6ɍnqSG5xU7k/7p5p9 +Pc +Q]%y,z7Y(~Jogq |ʴ<~Gt,~XSp~ IIoj}ݗ}=`e|gb~~\ +3 <nT'0lb-dK2o#`䨵3k~v$OCb羅D&/`\Gk1ҎìaY>S}e+ _50^%}_Bqx<^I4՗)$ro1]*쓶JE6է:-ΚX]5*\ZccV&Zmo(10419uOjMAְ4 :La֦Gb!F у0a'QlFNqGJz:=`Ա>&Y6|M֊d."9%189)y=Fy쮳||D޲л"yпN_⧳#i`)v%.;E`䄭ۭvv1\`gwoxnEvK݉c8zǮ>|S' 8zǎŋg{aM:$A 18!( A~VӮ"'91oELe1><Ζ-0W}/ 40I aRQVE߉(F!3-tj];U^loT-J%Ǔ^AVF,@,HBQn211 u5"3+ I{~tAr +!0 t,[R<)%҄-"Tg%dhCڃ{ZДX\[&|[oۉxv:< >q>q.z|?yz>?)ׇxq{ ǹmVһbѓۍ7qS"+p-}>uRâg-΢ͤGiA $.ςd %yʦn&U/R/cu]҄]b?Rޚ~/82@Zy} (l$3?ZSV@o i,.S_$׵Qwx&/*3(ԃf2~v2m{wXo<~GPO` $x1&H9%+nmdۤ:Կ*~|<>Le>ʵE GX_hiiTh U02|* iL&L}sMooMMalj%9&`ԆjXZ[beXr+&NJtΠ!0bF6rFGbh3m1#'YG6G87w +iީ+ ł9cIN1Db,_2W.+Fw}6_)v&Ҟvb.3ĵp gWҫN.Npv1׃E\7ҳi殳\\$99r\؍N|&:1~#\E W8ОIa+8|> O8r0y~"*#9e jK[_)[Pfk}В奇"!äد^ʥS|4y "ŬyҐd5hIk%xؖdbA*jD\(ǒNAq~!,Zc +AxD0@ IqN7X7EQƒImJF2ɤ3C(79^/R\+#[:Q[D|s_ogk&|ow,߻'|ͯH׾\}?߾ >~}?y){+-œ};"V[u^;-w OI3ߧ}-uϛ\q>4sNteY* P)=oM8zi 53E0eF_}}߾7"={i@Y庸~tcw\"I*HQ2?C!Sץz=d¤-&ͯrR^z!E.u߁Zc,|#'2zE1Y¢^Z݋._>1i=e=>cU*˜/˜G–M+n:ҭHz{>%::܈DLFuuzًtv/s_WE.nptzYc]w_[Ot/o޹݋{8JҲHHH@\p. 2".ĩ38p4NsQ)|U- +GcYX/", G),7 UQ;KKG\?b_y.y\p~WΐH _CTKӓ"T2=/o^'f~[˓M wy|qjr`-W}k|\ǖt7ĥ:R*6,L1x(^jr3C)ϊ:ubȯRKzZ>k5fQ˾ΙڀHFH+$5}62Wsj jL1~9ƍSF +zA9u&}L= đWA(ȉ'MI<h(+ G։yuű.֪x (&n;EqXMiDN{a TҾiQH%s(;%Ԧl*OVYguDFf".e#s6! X$G`__?/.ސ j{{ӈg \ĤD$"'RZjCȽb<֨LVU411u/8=р1:1vG+n_#īGw_KW^|Isbksb[g_7_Lwt_!gYW&@mk$ζ58s&c&wcKEܷP(mٴ?w:Kjӓ7IRߤHӎ_yox-/{x&;H=IW·wC^;2t{_n|zK\2|So\Gڜ"I} yMO|MY\_* ^/zjilB89Y~mQJTɺ5LF ~/&D^m8NT%cu2aF}zZy;OAߙFñn'}7|Mż P&V>s`k5,:h`eE2ìE-c,1}pL1`kbʄqfOW'xhe8n+l785'},==\_W{y[It\g;v"N{Vv|@ߓ?#g?a!Č\U2Sp%תQEtnMIR$E%\mD{Ui8~NDhgBz"|\OK_?9'&GH"**M͈sŸQ4Jg {\BDx0b!H6'@\@!/^ m ޣ6^Al ZzjKt{v;ZC ϵ78߬;psw7"\YL)~:O>/o^&1W>29s\o~K9˺U_J6Nb՚ߓS_j*P?V^= ?[9iOPHj=v}"֫<1W_f/׮xkcrUiuoك8"iLnl<T9Yk|k^WS;F, +e՗u /`]FfBߊ3QHdc L|MOԈ ̙6 &˵4-:ÇcHi &BZx8Oga"z1XMl]x:wV.]K`YXp͝UK?ʕKr\7ömaOh wҭE]8V7n&1WskK8KT\R[YA}np9`/G}}v卝>s::88ÖiΗ_wO%8~⤨L>y8Ο9 gs8A2\EWm&gN[6ڪ2I G8;E? iͦt< WFt棲(E"؈+/\@he^ ji1^ojBr3D 0(F8e$T.&Vg##- R㑚 k|LoJb yPa6N "J;~IK8," !HLd'0eYC|45<" Rw}xMeֶ_k9T;_ۀ[ĿΖl圭.[?ǯ+?_|z3Ᏼ/B-o{:7oׯ7Ib56֡^hr,he{xVjbak,4UC#~Wϟ _ןWx~C*D?u5퇎a uSxz12xj_/VO;YsuOf\fqPVhA-޽owkrxGrsOyFikHciYbBU}Ӿ=oxO%="ZE}+I'֬Uz;v.u1GƦV\+}QSZ{ qؘknp>`bjHfci|0[eeF9x)˵xlZ68íGV6̊tXlH5%Gap6#XbXsb`7#Fq֘2q<&FL}Lc1t ̝9;-i6o6,[X<_ď͟WXz>di_[1oٟBN}9ز,8!~.q+R.,ryE9b|ppvawvq#a9g*N<7q΁x +7vǾ~?gOƩq9bSȵk +\֚4$0'Zą*鷺Uޒ}~_~?)|Ԏۭ9hH~HgE(+q {ω89\|}/ 1;4,($ (N<&E +͛@f&vNMe?2[VF"4qt݈ DG"&Mಟ?.?04;ݏ0ň\c4Ǹf >1xE" \Wjn#^M]U+c 6o +I^NH&Fwo'Ob13w}wlMON ;{<+WLcq_5ke[\ ΜEmqrڋN !}|kA9~Z؞:yI=u.FDX %>YI<Bj\H/MA^J!>ï^v<~zu/A[.* EmHT!3QW/ť+1PQe&&#;;QX9ٚ1^/3a=d k $F,U>{%8LڜV E"\?&y2SR_ԗ[qnU B,kZje|f4c+j,~%՜C|){Rhiگw +_#߿G_B0Owoވ<,f9xNFz4i#j[]c&\7*qP[2ѳהy\'{q~Xĸ\Qwp Ff>8P=\;Uk˹]kݞCwE{H +a'ܣb>I\gh#XatoR'cz){?~L^UC\=߉Q<`1Oצ2Qscv򆋛`Xg17'Յ1`ۻSûsqҕu\šC{p.#6rk8ےc7։tYrصv:Bٳ8~6x $#?=\ڒk0NsqF#~xщ/A<RHEN +$wC9ϽPH%xY]xrw;Zbהu<.dž9XGڙ=) =Ӆo>_{_oڟ%q7ǿs߾#J:{7NzߐٛWo3Qxr޼kb.f+@9D&W~Y/Or J[jX/jorwmE1jʈtdzD辰4vX*^ކ>h ٟ?8=&n.y_o~`>{wW.><h`'bOi'wWmEL%x-vw0/#gnv5"i+ ;~l5=uI?SRqYb^;<2y_s ]=~RPH2yQ|{o~ѠVs%C ݇f%'9t ͈FtM;Y {š]Ugs%wf.BqWOg`γEvNڷ8*` ]>"<21q18u8=V s,6Gi|`7#kKLھ.!":'N_&&;}p<| GpIpsE g_ēNM599)Q͵8H'z]&g*hn}W!{L)kۮ>ZޛZ&z\# +r* +RǞkc"YHuCydօRa(^6&MHWr>6b60~&O)F`Q=y<K٤,|9q;cΝ.._X<$,^8+-Ə7~\\6nz]ׯҼ7l%-HzSYJ: fM&:Vu89Y;vxҿp|/'pSﻰob0g[(ae m +ggu~qsq>EG[řoP;ĵĀ$= HB (]:I{{f̞=s箵ު(Ns}ޢ(y߂O}ϒgEF"261Lz~ J:˧ +yn.㩓׸Q>v{G.=b~jds5žxJgޓv<ν>WWqggz i{77NH߼7? %sśw}oI-X'񷳭 ]bE߽I:.^>&frwKxBӄz{AY476X{K֊XA1I 6=uP(Y} uf*9ӞY:^I}7BWo.^֍dLRz蟩|Ƙú9͆/eQӭO73He sNYehfrWyƁY[G/y[6RfR{%jHEcʦw@Ae}ǖ7c2qelܤɘ{{̔,g@\Ğ㥩3Ԛ+=|PgIh;VϠ-1w!}'{89 +t>glLf7L`3⬕2=~ocx`ZMMd 5X..6kOu\`i0x qC8c5Y1ޱĸΘ:f3&a9a1yfN]DN:{=w +fϟ`91}-Zu+C O XziXv 6lX*wAjR"Q3յ\EFH9Fdž#pT (e#kG&6e$#%5ɤUb :\`g1XMIz;  G@`(<< +߀P{{WMhp:ƑK@l2i̝غ5#9;2oV:%O<]8}H{T~!]|qp7#ʹsq};q2o?ʎ^83};_9@݇Ca8k#Gha脶c_ֺ3hP SW|9 eVa}Ulh- Bu%=?8NQ:{8v,?' +q^~eը(ƹ:t\jW11cb곻x%#d >>uOx;smt?ٷGb|{@'b 'iab/??_2ᣇ.bkI_9~UzCUyZx/ۿ{~X|O^ϡ_äKs7{ߋsFɬ$|꿗ߧ9<V$P] 搕xsWKi*5|?#2yƦYK+LV-] +Ĕ͕9jFj|-;SSsJ]3Y27*ZrP0sH?dž".>IIHMM49"tqjr18}J` +H O%v'7=O\gKezK<7'X"=1xEF@XTF#bx[ioRGxh>o9`cb/6+e=:&41WֆX2 3M$ %">P/hVu}z޴J6JTeް&nKI63\`⥭}6;ҵ&VINQpqs5YNsƳSR߰dJR {-L``g+89 0tlu 8 +s9=h-E8X<';ifMI|)H O& X>^W8`fwB#ѤI.$9l[kf8l:u+iӭس;ܝiSw=ۉYؾ Gw *IڏbBɽ̽(,8H1lqRC + ?J*.r>XUj[:|ߙB4㞩 +7ӸVĮcGK%EV>)-+Tt4>'y<5y=&*3Y~Hxܻ 'xŹW&yq~/ϔX5Vm5ZOff L Hr}~06TQyI䪽-LY{?5kkKI:yZH )4V] fs.+hSf}3,ԋ&+Ky`n%ƴ1 +#sk8ښ^T݋D:VvjKYz2歅vs5-Xښ,ä]\macHk8A0MEL Bvs`h>_#3Ϟ9r$UR|Lj_e}׵Eh)ņ2t+ĘZk էPUO{&9#Ǐ=ӧKQWW*skPU]3҈;Lzg.^dخ?+W=?Ӆwn1x~'sH߾3~ojo_w_ާw#"(Ks?/7׸|{[\-I^'Mxu5J_ړ R'}3 'ޱAbz>z|Пз@_=o~y؟N׾u6R{=vB$I߉WK4R5^&I}O}xz^6zl(s9s8&fߥݐÄ" 4f >__z0S64z%3@3WZѤF4_>ݰz[1Cs72"LIZY4o}gjlI\$}i+a*#2[L=oQ_~"-kߤP]W%t%iR coWJf~ogOgl(}VX3cẔesXs_-͖t]yf |q.g氷=1Ltb[#Wk𐁤]1bu#b䁘>m2w'O){ÜiI+~.\cҤIX~=H\z{ f?v5/_%Kf`YX˳W/'/ƚ HOVb%{w ++)HLd~)Uc/Y1K^27QELtA+#-3c5뻳qt&C:tk҉))QHNOJL5>!mދ'=A󄻷_o<[W_L:!hc4iPE"4:Q<ؓ#cS$"{[ +vnOޜTH`f=#Sرs3vfb{N&1|;rsSut󼅇pA={}8F9 +shS'ˎI3H/ N + +'x1ˣ هX^rb9N_$n +PO9]r(Fwys=4SvE *UeDEEΜ9\l:K%wIup6c{ݍGnn`1 zN ~)zJͧ=Ͽ?/  Es_I}/ǿ[ux +?}nJC^)#l/_M}r[mް%y_S$I]斶;qOYD]lU/^?и7%ެ/ӘX*[}4nl]W;Wfkc:_[^ddҒFPfPfJѪoaE&qs YYZ%+s圌4sMNT<~h:>/[Ϧݞz tH:U.+|nN9C3mf <^s LzYGA.}+5}pReG KYJBw7sn/f1̄t=ACFCo܄ٌċ1uSS%?lii,zׂ45-[+sZ,̌&ur!޺u=-lx摃#,ME8@֠Aw0C:a[ dtAn aIxa8nNF 1̵WSy1s3s"Ν+ 7oVX`ҕX2!V,eKaXb9^Ky֯uϱH VV\R$_pH(3龄PҴaؔ-i;3*۽c8 /[3c[6!p4fpp{z%]) IOLDtl,7'=v/ֹ' +)H#V@Dx h$܆ddoOsNbl.:'ZU3t^;w9lþ};d;-l޷gG?Ug5՗b-.?M,Fc]2 O ܃s /$ӣR?}g]5eC.,8N8O棨H?i/ǣXV^ 8\]!Gֿ߇:enUlWz\ׯM}woJMG+~޼o߾>2|?徢~C-afo'b4+>__<{{uK u1|UzY뺊;p /OEtuanE$:fG#1tUUhnۊ?|xfE !&H|;>&D407$ȋ/ tBl$1贔HlɈ[XH1(`ړCTdmXvzNzjO:>~ +9TϽH!^U#~xG'(Ѥc鱈&'#mח7'I=fe.I?ݝ8MZwv$'򏒾ݵxBn6v!][bGv.ڋ#u/iҮϝ9%=D7qXwjh/s9_ ՒF5يPEK>u\Q +z]5(XGQi*pdAk0ZօZaoFt5.G͎bq*{q\#_!=zCI>7%1W7Oowṃqw2o|N\V_7<|Fw]Sۥ_E?Wgk] 6+]i=۝KnsˤTFaI ELf~4p-7kƾ_G뾞AC~*^[_[`RaJ#Q1g0`L`|4f=$ssRzuk z_r>W{#EXRǤj=6ذg?`j4^V{#֔JY94ڔkePk<@L8Fk8_JYÜسv1+6J2H'u~4Q/qm _`_#0(]`XڋC^Ay“0sOrD ]C'-i~)H'fnI-ؾ54ٓN:89[3qvcw^{fY ޲-Y9[vرkֻً\1#ĻI5YuU'plnTJntTRcJL hm(#-[L=%zkV֮u\VSp"{NkNՕr/7t!N=Xt s녿Z΋2d +bvl'y7w:/:xo|^ݿ+룇xW״>#Mvf7o+qg |}ߺnҾH ?ݍG7m thms亪6]nk'UBTVVBt[4`r"=쌁``' tq!ez!;z(Ə)S&aƜQ5o8ј;k*f͘FOYs0yssxl,%.'-z.^0[~d",[\j7(>4]?GKo8T<(3X덏 b qQa7f }H칑)6'-8Sy^M%F I<84A%cjo,[+=X<ܻ>^^ Ce +GhH$ӒwflߜN疀MqLEdlΈGb߻XUeo8wgp[;vn! 43Gz {܋[Qvp>I֟C[S n]5w49mu4fUIl߮ת(/SyQcLe8sPr }Q*-g*Kpl%S=C-ϱbx@ho|orݝij6\ ˼]gwo C|C}@r97?#ݷ_J}~~۷}y/2A~䁰ŝ.ܹq4MeCga4}ARD=A*vX#8ğ̈́Vvp##&,y@ome ٜ51keseV;+F5+U^g<GR_W } vknЦ2{_kyv*|^c8[wI%363xƯ9lmŋNN.2h3{ᯓ!R{5C1x6 #ɜa81g)r~{1md̙>;2,\8gĔi0{R̞5 sfƼsh,= ²t|̟3 6^X+,Zz+y6K=UFohxQFJ/cαQclt96N8O(/.&18{3Nہ#`NJ z_.cm"C?i-&vIo֭:_\+zuV%zIGbcx^/AMǎlޔ"ݔ4e&l;Yfބc#Xv7ks҅hj.3}uff;,rҿ'oŸvnVIKCsCa=S,5Y_jKd񼢚E8sŨ1=y}C;&~Х%'XVZ 5<ې52_ոP3xRwgв=;3D_h#6X͛R e YݿO>/}o?HG^t'󯉹_k^ۏ>פ}o{^#7Nu~Rt1֖ -y]:/}n>1}":zxƼ7px84/֪˧s!b'5gRi_<ǧϟe>}p_ܺ緺hrj;.58$_n'̋Y|y>SSeٔ gK5XD{6fn:ڗHZ'1'gЗzy> ̽ꑄJTVA3WwvH|gO0?fm?_ߡRxgA%xK_|}sFPoc%gFjmޓ*ekXC/RehzOҏNNe_eJGjg5JgZ3/B RtLM k%Ѥ-lXǒ^`ank6s5i]SbrXXK~mg7'8H/@7Kka`3jGژ{1f3 zQ#HcI#i`12p5q8Ly_b-Ă\< + Lyb5Yr.x1V,_bҥpG7sAY/ko7}t=ZqT"a!HYYrtb+LbTGNOE{fqVz,oN\Sͽ}k5XbVa:^녵k=E 'vG!($R^!H!4a}2iSb$JƑUKgmK=B*iXY\#͚o Cw_6"x{7g )sO nJc *\l,@Cu"3+ڠ\G[nLE/I3*J8*f`%%{?天H{>#y߮+(6sxUk`ILځ܋tEt>~twTbФg$mˋoG?">_|f&>}O=EWsK^딘R̽G<:.7L.{h嫗i>"-E6^Vʫjp +H|x@;qJ[n\*ǵ IWk8{ u{' >(:9G_'151^޼ݞ~o>:lULN}K} սl/55LgUKj:^"u dIU/V?HݿHL#cCWQR_~?blMk-wf\=0'ٿpxq+{;QV^i|ݤX9oea!537i(Z!nvebprd? kfa;հ|lllr? A:0$TpF%&zM)HMFrR,EMJNĶ͉J b+f?Nڱ=Y ރrpP(1!\ܹ "e6BeǪ e\b@ M UĀZ8[usv}mkէP|$[^ěU%f-4RuMWY91NWWK]+f]:j_$.{ŋkxŽ7n7^ݻW? Jߖ83f1ŵ[Ca/qq>ط^# މ;7:[cx:-MI|&tA7 h;_jɁsw}m%=JqZX=o%jy-G]M/⣿ Qj,dһRU/Ӻ\=iyW?>缞e~}zstYo25ޚkeS㥝h[ViڧX3P$x=O37׌YrJ6ޭ߾L +l3kZK5P3H᯹D[džʌ~g,un.ckүv \mW7sJ^;[{ҾΤum0D<ȕxbi9mGL;a ~ρQ1~pҾ01}*ǘgbɘ=s +f͚ysgaX`fLs&he#.Y(sV\-=kV<d ù/幂PbHMEkǟw!pc$='8ֽ +rVrQ=M1ؒع-ALVnH,HbXl-U5뽅>\ 3F"8,I^B|B,YiI D6iݭҔ-)ؾ=o>{ۄS;Ǐ={H]w";'>K>B\}v/Ɲ̏յR'R ݫ/^gϟKxq>x/x6iM]t.7dM i.\l=Cԋ=){h/=XZFcC |Mer +kFI;My QY'Xg}*[ft~ {-gx@ z3>~^?o?kϻ/zj\ U= zrˆ}/uFf+3ψ󠚘ԷEsJ_2QfY|5L3tMpiFg26zc*lO.SsfaBoʞ#lѿ{vsq^޷;v!->ӽؽ8YpOi# TWH4k]Ѽ,ոz +M8S]A PC쭯T}8_MgI;W*1 +~.|tm3t)K=-IAڶ _>o֍Gg$z1~߾z*o>~=z$yxr.vw 9}uu&=ہ+헴/(=^%ss#Lz>ҹgzt4=W&ޞ ͱshm*Ekizl|5UE04>yҏ_kսzy9:, +|Wq=u6Gj ~zI}5wj=5_ϕE)L^ __g=gb>ٸz;PeMk4Rؾޚ\ 2ҝ j-=?\c,3z5LTFߵ70⧬ĢE_O=W[*1pPX+>*sNTk[}J})Uۊdk4x0&uNpt9F;,̜ainWW89ʆtGbCuQntH{ 4JǨV=b<ƎCvĸѣ0nHL"N cFb>s^Sg.Ŕ0gTL3aXXfcXbt:i{I /VKo{z-:ho Z@?n#?eZ! +GXFG_<8͹^~k`~ǘSy0^IH%nNŦ$bq|$ңq>%JE\&.GXr-<v/V ~X֛1"/p#g8 0DVPpC#A,"]HDE%":.itiG89%AXO?'!{K؄7cndر[ rb.ޝrܕ{"wnݗ8z#=}Gx<+sKd]{:_zT<9x>RΖjOs4?[Iۖ>% ]s:EBz,s(PŞ "{(^㛝/սxoݒu6S |Ûwi'}F >t11SZwvߺI,~~:O~Yח\]m?/ޒݗo׍HJsks#Z~ n[z+p+Mϩ?SrYj+=CKOwWCh__y?K>wo|[O=sMad3e)F.Oj;=&RSgs9vm:{Jzz^H?aML{=8kVb5>!i }+636r,dߦ*keƠ9.xH+HosB|̹&{T_)9\+++Ҳ]88Ê} +8v<Ȏn8s񀶲1:\V9Q<\l13z+ƿ?43j8t0Æn8v &? {0A0a<X! +LU3w!X8gϟE&q,/ fa^Xn)kpwWk-:xz}-}?jx/9 y_?IM!);Ms5c9+=9Ja06)saJdzCBUk׬ UX'9~XK  H ^ 5IѺakGlob@=q +;HJKhޜ쭩صc 2eKLlۜط;r6rya.,^‡I=yyiy(.#mZ]R:SYsĀrj%.po3|T΋gp\\:Be)y{MſRAfKֿkkKPwF<˹PZ58WWE\:Kuy :ZމsO{/+xƽga/^Jgψ\MG}Ҿw}0/~;]N;;qeҼmkĚۈ݆|ZU]om}O5J,5yt,@Y)aO ]4»o>޵M=1nCc_&44Lx"0Ԉzɺu}z<ӛn*\Qw>祗urIennHċ\Ǥav#e.$ެUVJD HbFj =JX*_/W9lƞV`{rْ4-X(q $;[sᮥp^f)Fkog'@s L:vj'g+qрp$5 Zvpv&nPg jÆbu5t@cGQ.?n$&OO%;&M;`kn1<`JK/?E1w,? HĽٌ4bݚk`51Ó xOo!8((~\nyqL9,J^7&X3ff3㣄I`5\0?.5)N+s%c4Ҙi8?Sjbb%uh?7uZ每 |{ @ˌE(iXѿqqHOsCrr,3xr7%$b2/LޖӑLf wؓC9{w{p2 + HLֱǥW@ 9]QB:RYMgOg+|Z|.߶RterS<&NO<()a߬|5<1 g|pg+sT>P*/B׎)SMK+b?<;ר_&z/Uo + zzkia,0 58wKAe1Bo0o~Y~^f2fҋųLzg54擩V)Y#Ӌ?;snX u2K׸5G]{*NtôcXǐuX/s1kL,Z˖,dl_k֬F =ޡ󈎌 `up@{<܉i~D0+2k eo$q8.D&^XsI^>s U 2ңOaĬx/dD wȱJ(/Di~ٌԴdD W$uk9/t!ҬW eB{c^(*?/h?+sc\p$N<9EO'P\tBޫ \WtJem$t~_OnEEg^k'my ׈7u|{u߿">a?[xu>~X'çr>|ǤvuK̙~ZӃ=X^k#vq0?lKM(ѭV<"mםnm=jO+~-hDUU)ʙ%w} +k_oEY!M}?JoNPbћ/W֭x7A~}rœ^UGqMbpk>y;y%l3fkWW%ݰ'ƪך^嚕\eqxz朗L\KY*9^ bi n`ij4|ĂḼ{͕<1$ےn5%{]+_ܷWG'$=FJߟs|}k9x8Df}\gfn McVƒ{f?+iO l`gc s Fp#r-0fҦ30ޙ~61\qfMao6&y0Ćl#l0iXҺ0rH;&' 5.2q&~ OH,\Â0o|Z|lY;뗓$u!muaQXvN] Ouذ4ڕ"M ~ Z 1$DqXHj965wPC#=$!U|ɖ{A @ Bx7ޖtUWUwOWwOϛ3s޾=syϾ={od$Q4=N$Iddd '|/ǃ{lGg{=i>ulBfGw=e>xALe*#-_ +G݋Wx9?xwb1{kƳxB_@95W=hmnBSs#Q]SF qK:[k1@??t.1A*etOT]YRWQ$HsTW*zw|XEӰ̋?8w<.kU궏s30}ywCPZ;/ ̡NFZ-~Ϭʽ π0PU=լ4)~cCGį2)IZ뮌^LXZF\ `f/s[66UFƢwT|8قLUgit,۩{czl!w|,ݳ~ŋ-0gΝ?OMMd͜jFZVzlgM6X4wͳ򥶰[a%XA,^t1a֭ؼa-6mAzxb''9|vÎeRk?ߣ]}w8{ #9)!s8~ N>uU'YgbOSGW.ij>QAھ^ H7O71.S]@o?zze~ѫ܃Ûss<%.?֮{+ޒ/5:`H{78^nom**PSSҼ=tάׇ{5O}6܊w|~x} +%͵շ(J9[ZNRd'Wp̚}wï LE%:~o+w!<.ȼ:/lf5ۉk8z'&'x^Zazu>F/ν5=c@bU554xn(L@紾Zŵn_f=xѿf&0imS| kp}/L(VjESOnմ)P{ Թd:GEVkB,H_ NTs;f֮^589VSwLzs[[yN!= ŋHӰd,X-\-[(X6lƍcغk?ǶOaϰa +ض¾;q|q Mء#u.99uǎ/N3O>C.\8Wҷ.NNprpcx2k +쏥xd|`f/烙a,PO$oӓ9`f0$'*,NBlayג7C!W2.!&"&9)i-{p4o ŗ_!%*;{Zzp85{ 74,t_mfu?=ǪfM?.ɤۯd$#'ӈ2÷2j*H\! 暰 +4BcC)6i9! YFWw/zzda ~"sgWOOފg+}tOU]~o`uk\[ҿNa/[i'?k;tq=wϥ#;~~!25[3n霃03a]?a]:!r@!3䈏UO0)/5_6xI=}m P)S?Ӎ>MәW|`1&6Bꚙ~敵u:!c:{Xv%X/_&|o`Izu2ODeľ}z2Y^k&dN!]; +?i͛K]h5+gaZ-saXh*̚c K,oE%K96X6g͞'sgϥ͟&Śun VVszl#MiLlZ7mħm~#Ov`'[q~8o}N:/Igp :uǎ}su/`^@W/_{K_߽%->{ ޕݑ-=6k˚{ͱ;N:osS+Z/GAq=:%w6<O|Z̓<`{): +P,,o{Vg(s sPo_?|_|<ѭ +W!ߝOy Z_h5_4au둍Fg+h--F7Xߥk_gxqewsح⏢zkkzI?~jnݙj?3#P, laan$35_\dhw+g;C}=EX[̂,>fR}Gz{ |66Q/dn$S#w>,̦ʊk%kAǜ1931kL[8K-&;wn5¥ d|̘53gb,c̛Cx9qsM⹋`d &^K,tTZ8VǪsqlZ7 aXb56nل6-mld9wvOwgػb~:xǎ'azΝ=*I]Ǖ쉯'҉g>pu;+ruVg@毟7Dy xuy]@zj^G r9>U\:*@|C=I32L&'F߰ǡ2Az0obhg]q=^8G:Iho&&NǡC{g's7@'O/{56u[q>Gڛ9]\V"+J +5%MK|x.|2Wc'ڌL2Svtb051y,Wg"O:3j]ՆZ Q_Cqҍُ;R3ruzqտJīث|a=OV=K_(tj˜_#;s⧑^AؓH|/Yj[oz4a6Q=I|% +M4S{k\rS577cYYg}יDZ[Z3lgb܅X+6cͱh5̜Y9.=+V-ʕkHnEh̛Nؖ %lwtY\H-cztl۸a +;jض[6n[~ >d$؋/)Ҽ'I?B=%~Vsgo'Ӱ'.'=u8KO\x5y71s7!֟$U5nߜHdSI) ײ3{gq4$ܣI~HHEJr/GF }mH/~z#ן^=7:q#]5t,#FsRk&k2*a |sZGb5iff k?!}:IbrlO5WƣGx4BzW_Vt[ef~w +&s*Jr-ux8 _c(Lx& +I&_G;΁(1s}%>bF׊-fJ:T8 T/ rײW?ž^K_h ZLGO_1ktL+'en%ט͛ +MKү3g]5b}ykIZa钹XMd^Y=H3/ϳI's2wspz<!v-v һu:]+~ +|mvo߅H؈=7݉Iҹpg9a/_N}Ϝ'O™D:;u3ÃIW + p?kpk9Ǜ1Ɉ{1. `f1Tճ88ԗMM#enblİ/{}x _Ùh낀@^2sJ:6LyI7 0J:˱ Ѿ]@&sŏ޴_ ds_ֿQQшᚬ\NKKH73QWEࢂjq*}ߓ-"t1eoU(yHZ!8TEMi`(uD>Uc}F\K;*Issojګ +1dV8:ZdՊ/kvm8AIt9먆y~x`7sIJ;Z[POmbJ\$VýFSKԄ蒸/ǗCoK~)=p 5`VF-ewf2- o^C~~ +o7ψkCm|QwSM>-&vaF(^=OP%9!~jhCS! J,}_I2ϜfWɧ3oW~4TuTJ2{G~;2.jNv|Y76Jx=bԥ1}p 7,Ȝ eK[K3ṻIqV䙍V 7[VRe(Dfy퍷Z2MlmDMmҢ)@[MZeq?k?\50 3k1SGپRw4^-\} hj#eF!mPW:C[{fvjogU,흡6t7˚R߁Nf=܋;= m \%=nN~Beс֦.t`xn7}ޏ )o^K'=;e _&2בZ6绅B(/BUY*JPr;ER#w Ź?ij}?|X{Lq~ Kw~D!V}ƿׄx3)zO_'KL: VSf'K62~d_x:|LL\:))CQ>$'Oʹq{}%ad)#$j̣1eCb״1{L+G-[)n`y.Rt M2K{XhlZ+#毩93 e˱9bC/KwXоk, `ae ++s̘i*gZat3ٻt5/)5 Zc"qfϕ%s$`L,]>s>sdMg/uav̕ vXb\%ذ3l'~l\ 6m\͛۱uFl߱ +;wq8yN8.3<_gsgNكpt8&hxn.puwG}s#1sC=e{P$7,X!l\dĦX}~sل84ikd{l^_/H,%5WY˒2=پ2bCaẨ@FJ,/{@ŃG{}O|%̳8&B%5ۈ_{fFF#:.|8MVVF*rn'^&V?"*)bSTBi-CU!_ߓ(H66Ԡ}4ſj4j#g3dvHO=7IR} k`Zh5*lg}k-M-`NobbIوG%5lg4҉F&xrm0iۤψO[St]a,sM|>c&FReae-ifiSSwm1wJŊSI]4S45S1o%̞"xޜjk gnz[1aiݥXj.Y,-[5+a˦Mذe)m\[wmnlZ} +}m{Μ: 3r98tY{Ҿ;E|v:GG<\Dz9(sĜd@‘a<ׅ8Ⱥ1HKOClb|iy#5%ٙģpo + I&[yK"Wtx? \HG~Ậ`} g䪽Is]r0XX7Oz+.8;c~ۗ40__b;k_?W?s[p,qxX8*lBDt bb%"brrj +eK5BI3cV]mU*J%(('mYC=#1R2=[[&ii]r03wt,YR%~[Bښw{Sx$&X:Ҥ2F?nonrglF[K#:;:0Tw>ph`%L@]5{Q桽&ZJ+wc5}yXd?6$n.UKVbXj%/Œenߴ +Wʕke wieX&;7,FҾmګ=;w?+ϱoX A|=\h/a<3<8|Yz _Ҿc9&ޠ@%`d'JstaF_yLN %MĞQ4Қ<#~k5XR#u+'' !>8KږppH |B|Vzx!vvuKyG~ ` zf߀@ sLkWku!_NJgt8"#K$UץKHOEnN: + PMweČbжJ{jHU4j{/BgW7J*j%YX5ҷ˽¬wKkrLm1;ˉ9}ߎzoWkNe?H/Ϲ%r_p7pO* {4D 3kꚈ\; sz;4}A+i< g0^S߮߇fϭt-C2(U&v*ă x3D&Vq: +s+2Ob QBXl aq|h]okx~Y7=VMcϞl^+$gjg 5J^֞̌ Wf*k\O~Jք ld΁iS=2FgǟͬLv|*rqH*GeblMk!=2Y_m/#b+XsJ^ZJnfrnzt`$Xc:[#/cEuY̐ `٤SbbKҼVXv3mF,݄UH9B G|݆5k`eXz6]kͱyRlY.ز?kp`Q:vG(en._ֿ<;0˃)L=/.8|<Ί{눰'CKxy +B>ʓ聐p/, s$Ȝ!^SK%~y3==y@1% ozOxxy yq|ؗ{<=ſ*8$Dꕹ /1׋XAgں~vsyxsN8P8<>W~2<>$Tr0{v^k"có /"-b ϗӑp3 +J󮣡tmbmZP~2XKZkI֢^o{'{#((e_[Ũm=ab>jA"t4jW {H# ϤxXpgkk:'XHڔ5ʏdS?x@ONU^@hqN( ˇdo⻧xI}2!t-+P_qwڒm+˫Rt4PZ<-nFWQ^K-Yy=KfTu7PJl}ʊs,X}S+]7 /2}yC7Os 5L5>x~W>|zi8!uRz+Cunиnu^vnјsmZC,s,aoVΗ8kืf,^p֣ew9.lbBLeJcCÜ#3|Ɲg(KZ_\rA<523mR΋>[S# <ġ5C୩ ,-INp LBW6X kqL]>k/Ջu l`Km;g.U+bvBҼ daz|u'= 8pv;Hu۱u5Xv5;o-۰vһb'q]8~l/}qGÑ{q~>tp7xω\=rĬً[ Q5xy//-*9\!USA~eN+ڗ \ox84eh1Oҹ^"A`^<+)2L%]KK|]\upe}0iXⱓ.8Ix݈"}M J^~ E a,k^-^z+9}\.܍_,&KHD\NK\LAVFr3P|4pu4壅Vk]ESwӰKUn$;޼_G[*sd|c ښj\_%sl[e&{I6{Ƽ@W=izM+IwcУ{u{&^?%-$6ݭpËA}-{V/oJX^?!;MNՄZWTv PQYݤ瘛[eE]-TI AH]UYq6Jne5˔ڸ›($&]x|3ZΏCTgN!fRGžR_%s}qdX97<Еtܼ>>Roy{op3X=Ė +G'$ÃK\Ju%MGL8rDD$"HJ1?XB -dWBF{ kx+uϡRc 8oE޽r2rDfZ{S +{s^F ܺL\%h)@wk9׃Y? lEYy+k]ͳ{2,s~Z9[!|-oyz<זj=5R ՁFҹs7ֈ4eu F9^'ιvj~Y =_?ƒ.aoO'uNnOfes|9LWy߽߾{;1~={GzHl9VO:ZjĚk$Lڗ7٣jH6s hR:&Dr + ?[{,-go dݤO ^Ji.!#'|3e7o>Oi$}9z|\>V{@0_׾Fھ[O+te4^V*){j3Mʳjo_#&}bU4xY+]JL7{R|)XL:5r,0K)s5=0RO5X\eꛉ{=kv^ϴi2y VXf7FZ+VŽsv[B-Y iJ۳ggo1w DhD"de!z2.#&6w ;?݋={vСϰovbgOtؼe-6m@=q⋓pur%&y"׃x>/i>G/J_Ľ'eqQ*0}ޢm9ˬ 6sm3su1놅LB\>V>{vyX7o{ >:2q1!9>={[Zhdphq0Q1H%&J/oqiEDF 11t֙}&95^A%/e,3jQlt8#ď#0$&+!1֗p%}w5ڦ syi;}Qv*JrQIqFAM$~*ր7e-U.jۛDcªVNj6c_̳}[S ZsmkT<ۚH_Ab}:ӆ6o%3C/ve^һwof=#ioXw "u߮ =C:voh"\2hY|"txo^tf(G{#=n^ײpZ6f_Ԕ__7?[N:Wvxy9Vb?,Glj +dbc:SX;pMĠ,eNT%O,B̯71Tj&&A5)s|5ydEIxHDѭejc񙶒5 r62V/Mٔ(9o#asK+Mٿ26ҳlIljkKb岹Xt1xws\y,ZLʎ*,ǖ8ex$gVi5jMMu(,G|\q GS]8N9ݣGaޝصs>#.) \\!41qKFBR バqُW8bE!L/n8p=->#`ֲX3ǕYfLu^ ] b(x9̞V\e !@%W<187b {__ "z ]#yִLҖ$=y2d2W`ȷOWr[kaY*Tz5U2kuu!̅Y7ifo#>5ƪC}wfjs¤{=:PG7T}`?/=i ]y:%_<ߦ]wL$?NsAl2.4m5hB_mjQ^27~\ + 煩j-bJ??>X#s>7Y~7V=xJopxIk9EIKK@~N}߈GFJА0! W4,N CP`(F,$a!DXljc|-Rr!mۋ??u_f4.{y}:7@ԉq4~}>G[HvЪ&^+)V4oq[yȽv;]묫Jz6'WqV6k+)ޢlb3Ǡ%3+)/;XqQ5Kw6?cև;!;W'ƪkj`U? W<lLgkm#eك-} q؈-|Ty̛S0}̚is,d/&.qRlZ[7úuKm:yiMz/߼_ 7헤Qʑ}- \7S'|;'ދ');^GH)!.Ux|#ݥҳs5E+LAtd0ByA9;Q}J4]wֹ~vgirx6tq7ibf+Qfr9]jسsǜ YFHmc?F| f?Jz>gF$]) PIK# ћ\c|)5 K(LFyq"!;-Aq/@f+pg# 20*2B;7 _0wG /o#"O0?6qKqH$mveZ鸞hgLR TU|K_HgHqP[3B6:`]⼥ee(..FC@Y> píx0snttsZĽ]@wN֓&>Db#;؁&u%vwbۮҽ͍ Cm{b}y=z|G|5y҇{a?x<ҎakkggR/f{ QWۤm󳑟Z7 W9sdfJK+鸖4bn: i Auyx8WtK?#;5~w<Gc(o)0DN_vzͦ{m uǵ5U| 'WjZ㯏Bj4i&v,qdQ>t_LƦeȳx_1M.X3ӒujdFG%j]~R辆gPXŬi^V+5ޔ^K:h>>w޲&ms&VrD|-an,f-fϙYl龁g< ̙m x-kcX|!ٱ<\[}<$h?}7?Bw ]Oԋz^pw?N:'8} 9'Ib0XpDeG:u9ĔbDG"._LG|"}%w>셻Tf0k}]^Yѽ~r̬ų5N`r'M˺[f18'̋\U^ z+f/3Xp\l䂙!Ng :Ǟϡt44T\Fwm&PY ǃYp>~2ksyyB@p!ƅ5Bjr.]ER"i}:E5eK/] -1$Q 8YPT9]pK7p"?t/q>K1C nre+w~z ou^+jkxI=׏{I^/י?HoNw4y%ݑisN<5ePS^@ѽA8ssQand&p5k)%{9-iɸz\/FHHŔKg5ejgjj>;x_F]c87nVs_oD|)N~|WXC~=~py!溕௡:4 0T}h0XDgfQH֋Sb(a-){njsZ`!FzjiM,VfcY-ʖB--G٫/x#}Ig?K͵孕E?lny,6Sm4X`=9=b{Kn6oۊ{b38i9sχWh7ҙgOƅq 18}\xnO8Gs83.ÇjXk`rÉptt iULj@Z?n^Nz.P4\]a=Iva [bh_;3ü6 3xQxmy5:O6" +_ OK/3WEgFx$1?.IN EJ"ϐ' GU"6R\̋e2'A,1p1AVbR"2H2ʶ(?9I],<(,Ȕ0{󶪢0j 7Okq[w^i&P:Z#mEҐ}~;ߺ/cO ~mkh Oߑ>F45mX091ٝn<#OݍA4h< ;oGitAǤ{tucJ5t-JH*Ue7P]Zʫ*gk˱4dr%W\KoIGffψOJ!&"26^<_3CM/=S)ӓ|2otֶ!z>K<%ٳ2* 1t>8ҳ5ϼϚ7:S‘Le^Gq⣑L:?RN"eHOM@&qe,H'Qt.bIFG#1qR \Z񺊉垣(e.38Gb"{aŐG$\I:ҽiȿċ+-!&.ec92/:Tp#X +¯IoC<# 4Փ.~ n|~zux?#&>×G{xhDNڵޣCap+ߒvq7=$z߇󻭤;.׫]2h=59'͚ t}FM-..B/y(y9-/AQU}L\bn6i^ٗQp32.wrJ +KH|Td6]K&F237>1{(5uAKC6@ߓysװjRrl[+RUO_5^o<Ϫ/Wc+;>n0yyCm]R?&>jZ߬{R#ef$sܪO%lB?3cjaul`abSKYΝEyHcM܇ZV=j*U?V|59c c0k%nʱnM9V6Q45Tf{2`ai)fHifm3̞ey,l Vst6`-ޓ8rsOyM[(ȻIpY;z݅Cv#8r(8Çر8w,79tug_w⢓#eCx^ާ H@PZĴp}=8YNtlᯗ3 \suAj}yWz?ɾy 6oҘ1<([xfscO F~b$%C#IF!t)B "&'s/~q}.'G";=2pr IoIQ$cIKG*=!(y0~ʹ8?5̤u%EA׋03?2*\^o#7/灅1jn8qqDcRSٯ3t{2zNr}Cz2"E칤%e +n]AyQ6QFld хZ}G$ζ! 1/uǃ/ow?C7/?ś<;<$NCÛaz>a7{:~d^~8%sYGswgtdF`'Z,.ǚnV + xQ.*Jn"t]n"F6SrWҤreɱgoI&_JA*/־tv9YYE]e./V˗D yfS1sXO௑^u[1o󝬉Ly^Q~L?5uקc#~ u1 u{ '341So`8:'J}Ie*!9믡',eV%*}/nhybS14n؈>`=3weHZuڜuX0u90{3SYl>j^(cLf~})_ &oԔ&-4u[sk$fuZ{’yHsff.;DŽk% n ]5 kWz߻nv>c'pi&  G' Z.\dqZN'Oϱ~qA<{w&}|ny۟$ Wt'6reoPa qƒٷ9gĩy9; ә~ޮu|<ܴub+D~H-TEG?Z2')>$Ƅ s#ey޸015)Ԕh$ƇVfz.#MI+!rY78{9nh%Hz*bIWp(5(Z8HJ siPsEZ9=="XFs#$Uf14og1TWȾHH+IJ%i9|{˅77D2ůjTwu'~_~4Ԁ{/s来y̤r%[l@1Y$(")愜9G9K]ˮ]v4{v0{v~ُ{_`dOt@nxּGS!<ӟ~ +ǟ/ßo  _|?Nf ՏW_ÿ~K_}?1~MzX/`־[%?g/86~[ ~N4~:>yt=9PcrYsyrc#|8y2iߑ^m-JޖkktAIt(Kǜ(QNui|8}8yq+F:mp K<EFڭ}>\G% I3<#Jd0W[W.a^R-k-*Ѷ_늙X"kFOfQl$FkJǵt]K\7ry\y%fV 8"Wz~K%f5hjfZh=3nia$ m٠-&\(mj4ea[n i`fhnN><'UҌ%/Oy#k/1𧟱]T_G/n~Dc7_|OD+:1w?~.Ow} O:#o>+w69Zz|wsmoMq}?yq ?Aĕ^LvJ]ܾ:@vzL+yr;}NhlՂ:ktZVX\^݄CݭNi@|\O:ՁVzeſ;;-mDHX4yoWUyWgܾ3zS}WM]_=7>~b[>j{=Z菱HW-Yf+gDZP{Iڃ,CLV<:s>=+×p<9AvC""#=|5 ҥ<A|}Mg*cp 21e񕸹R|bFghsI{x#·zucXᇷڰ.ۤ7m^Jk ً8dҳ)('uQО8QJs-B75!!!!YH=L؇Bs)yGdC8himy1bTsQ'~͹8{0*ΧHϞ>"+fc8A,>~W<\Jt/i7>"cYhn+;/`⥯g^J-j4dYYҨVAru7oOeJfbYg^*m:mh] ?&;,ur*ү^ot? ğ|GII:3zBѸ^kWFHbr_f|4%O3{y/ T?n֝"'ۥt?ƺ0:m`RC'pBgk,hn{^ӄ& wZhkbώFxzzE7nhrҊɧ +! d/g,N{03VRs|;r۝K sH1^VV?fMyq8,^>J"u\%oˬb[ \#/Z\rf0jx gPP0BBCHsJhi Nlc8gN 9y^o]gsl9׽$5+G]\7m_C5Xɵǵr~NA!/ڣd$6Z8k%m\!XJ\V:_c3Ҽʔ +yK+d׮WW)=\/Y*Y 6=ZlF{t~9eD'>zv]4 &ԓb7GáGճXu<%oNuŋ#ĵ1$?;wGnx2>}v 6~{/SXϞ?O-;.҃s%?_@M0Cpz/t e B<24+iv5n(v6M]w7Fbuرy>|mlmlEvv +Px(t> &!njr<3S8/84%-z(' )p;tQ[F,=_f%/[H?,a,338/|8]x$GX!"-.,@Vzΰ-ukH=AxR4.//Zgt}yk/Hܹ=EŕKeq\/nVG%WfN;]Iр6@[CVb6j)Ĭ z*'Z!JԩU,^\U^Y%etyJJeFFY9VqeoUM=&~i?x1`R-tu60@#CyŚL ~a{ӡew>G<ѩF0v8e &L/V7{xr{O ʺyuצI#$-|7O}&5cxBk?5bM|~]|/n[bo8OHgߦ}i<;Ei=~p2ߚ Qap=&&SR EĮ.mcˣTxKe lI^(gֽǟ9]RV*f,2qMns0atq'=v iwO4V:V1w5Zv4&6j`td^Ý$-xfq{qN#=¶'i{//㫏{b~^{i/_wϫ'ܿ1=!sݿ.7WKF{#eƬZky^?gU^o5]؟5sjmݬxY?e6denx@vU8O5I^<}A2C(wO6#"l%֯].CrboaѬCYS{k$_`Tf +^җkůýG X5ԒV&k{@87䭕:l 7"4Tb!A пaAׄ?tIJ^c@l~/[6&M7ۄ]D}HEj~d$sw!9>t~ٓ,b18y“(8ta?Qc3QGqtݞO0N<,=|(OV~^.HcGPPķA^v. '[jP|+8£pi?ϟyJlܚ3!byEҩhn('~Z7sӷasJޖjj/~tpTb֧ۦzt: vwX麉~i'}$B 6˲HjH~1c>~ǪMz(sϝ™J%>y/idX]MWfj"vyoaJ{a0V'nr\4Kb$N:I.ֹ%ep[oi~ my=mXF:HGP\w{"ZPh ;\ֶ-Hᯗ~䯗GޘS?=۟9V~n:yYfK,^/u\2h)5^ۇY ˟Ƚe_P3[l\ Q~Fx/}{5k[p5Af-={㞩9|v^Z}VI;.H=TAK%v.s#MkeX:KlV. #-AXfZL+[_Wع-al݂=[{Z܄HH؇iIHI݁{}!@Gfz +fg(/9K,=,/(* %ZӐ.+7+r9YN‚Lrq0.d6S'qq9uttdo oTU6scХ夅.-! \?~?FcsJq/@e.Y[sXY)Qmr%ޒal>ү=\rV҇ͤa]γlf0KiJ:ZUf'(YKu(TK, +ouurZw4X循*uJF꥝t>ͤicFVf2{Y$f'݈fMᯃY[M|{$ڀN^f5y>H1].'GK_N#ҫ_O\& 1릿&%sxxqx +_Q.<>>ҝ=uty>,9W\_5>+~}mŷH_7/EOg+z:c]i/@}18MEb#jf _Ӌ-=aF]Ϝ{Z;$^|3+_?t~.w^tQ>Ί.}O|y>sW^_f9ָnLp@~gNnw>U\~f{6sپ>JV噻J̝}}o o߃2/_ѐA:XDFjae/,%nN(iFf aIjs☶&@/k`Rjn٫bq & _[V0χ9N@ׂykC!_@?GJ""|G{UX<kWE"ٌ1ېGB+.v}۱sػwb>DjB4 +ew(w5u0Or bΟFi a/|=7+I,Djj22 `hef#?'e ^p~})IK7qx/nMR,GtFk~~hniF!=FUzX7T|3 + +ŽF;PV"Pҿ +IJX˂IaΆ){ >w9#sŏO'{}<:W٧̓P\*%sՒfs_=Oi:x%VEB19$!X<*c,[+C%mJزm\Ak%}|e3"w׻GI1xaqtTvm߆}?D߃; '07g P}1ʣϡi(=E8pIz\`Z"7 sildE8+yY'P;*=y'2T<5*OK}3k*<08e^W +${ű>nl܁nm5>h=o%nܜ@Fztok$Gk ];;8fY +^9 $,2?+;kV`?l^;>Xm[b+i6ݾ]$D;$g:$ލM7}}w>ރ]>#9! qQHFzj䋙!? Nĉ,:/Ig<@JbKlOH'K,NCjZR2@L$f!'/<9Npm.t =6L7132҇~q.I͌'tZ|2~B(VǏ&+C]j3\Ur]5ysl`0XӚŴWZTPIZ5{;~/V  Srp>, OH 4fU5_O{N<.$yW7fznY>JR{f'-DnO/7fSKqȗ2k7_Y\CEc_ %잵!K97 PJY#~\WuT~2Qݗy]Ny/λkUs䙋C!M B`` (a\p19$T8oV,źUXzիyzpn ͑غm݂;y ɉ[s&&ۊ{ vvĶ[k7]߁ؽHIEZJ<=7 +tұώG~n 3( 2qp6gg!` )5Xh߷#-=#GС\-tp𷨘=O +K8^a2{Ggjk*5Kod{Y7X+͍vz{D~d?GF'\$-܎{W?gZ R̹nSB; \^F6~W1E t!VװV Svs#~ZgZ:q?#N̳juZz? eJ/5f|\rs칳k#n oϠEb}]z[EwW*'Abp;Doo;qMr\@g;ϱtw9 kM- +ŧ>Ń^e~.Ϸgz3j <-3ljm7~dR<~{2OIܿ1D:}/CSS#SkֻFN.Y&֒2P6;iѓ5B 9.QQUK5TQ+ߗVzV]JߋG\>oR`p,ͨqUqMBB"%G{罾{]_{ߟdf0g*_jGwWyJxǃ~Oj(sdy֐3sa<*}Zf a_6%@@xa5ʌF^;'ސ\彄⯥zly=*Otgï,xC@P(¸KIdfMH^5!X< k׭w"eco +-kvH8 ?D-ؽ{vx n^[{b7xd1{9wÇRqD.т"M|(7 ?3 笴; Ұɩ{H%MJ؋ +3.yi(,ȓ< 9(*:ǎsM<;kk΋>j%Y-y +"ӇWHNts7id7Yxai^ mtbE3]gkBdMjeһ$>F(h7ù[TN/s=g[W~zeh-Edh^˚.z]ǰyVgU.ZMEsmP\ҽS=&K}meen*< <׬̡%p4\flj-E;;io㐙~-9uFMzwj6T+}<&+\~ڏ{7w񛯞_Vt9yO1߆6'.^Viuۭg^#ft7>>}Y9L}*˪Jg0Ao?jxgu6w❷uKbz|HCѡ,E'bI *kdmEE). DžX֗TqpUEX h1 u0ǜ X1=b.oձ&tat4{|'8HmQ]&ýN"q&Aꑛof%i.{8i=ꉃC\M=p+ -i[bLV-?jˉwt8ߥn2އ\+Uup-*w@ߛ= 7j$1vl|}ՙT'7Z5;.\] zn^*g9ΟA?kbݔHDABl ؝=HKNBB\b㈽KM$N -qPf f=#Gn"T̩8y'N3'piTVI_`+H. 4ފL׉z\ߵf/&gYW?3+lr}.1tQ/yz=#ZGG{c"Al}_OX43sFlTݨfa $efa}=#s+8-+?c`1wgPҵa!bov01.$8a՚bl("B#h`O-.K"$4K/*$^[J<^Ey@Ï9OO0'4V!=Ҿ~I9=8ƒ=CH(?<ט}",<a^X<kW.["qC$֬ ذ1>DT{H݌ې^@|{ű(lvl]J!fF#/;[cG HstV[x8NJe%!91t~+v..i{^? HJ敆YHEf/bq1aHeM=N=t>HS8{[:hYT*8O9.W I-ZH0H|nӡE.Z|u&bc@_Nb~;.p/R#5KNxjԅ~;kz,}9g5҃ǷG1\Ksה>fzM|Dv|C]TW*bq5V'مUg,Tq*u($\]#3rРhAbnv3V2j!uZfBq[<\tγAzIεa<0wq'{1Awd4p+BC^;KڥnIO47Jߍ`!vaFtɳ3z^e^nSbb~Ig\ 15K=Keܺڏ]o?l{V1y.'n FEb6YK*pR5J*+|I^DyQfCh Xj9NM!M`A}ya09 ,x⣯y%,j54jk}={t%J2gQ>+3{>S^_8o~z-=Ex^>sxZ]C L|>5Ľ bkDX`bUG  +pp`9Klc\^@bf+豑 5K%3AʬfW^͚\ 4D[>C!unh_?bK׽YC{ yl Ch'eIp+Wcժҽxwc8}'n +";#m\D8 8q")f viŒ.޻ qAzr18 g|{==JӅ֮WMoqΧvȞDkӞcvzZέC|_E+%'.6ѿOjimsRꅵj7c765^!:}mO7<75ܻ9}jV})qygfr|{ҧkK'pLoYM5.բJupE \o%}ZO #ӱFUJ9Ͼo5+7x$ij!a?/~{a}ΟY~A%~wA[ Ϛ/df. jՙ _ܳsZ.{x?j(\ + RϾ{erc# }G C:u4}V/%RgX\oLq0},@jݳ (#c> :_y ?O/B$sv΋gs׏{h_$#H MA}Vĺ ˈxkMY=[k[޳ɻw +]܅D:tJá(dB|CJR>@|̻8#8q~7^$Ν-D">9p0e/6!Naq ~I#'$!9- 2҅ܣ(~8~4'qx:XfuKN8z/ZˈG:Fm}1\kG]GYVs3[[8ۀ^bHvMm]1€j-daz9=-xpcg]X_ujGfϏ_ϏbLXDj\=⽋NpW}8 Kܿ糼3 w@78 Qø?_V+ɾA>}5>'>^ae?-x{-cPh|@/s#ݵ\?e!{yp[]ݻc=ػ]Dm@jFf# 8wN!C9K܃?@r^Ef~+řGPz4/Ù(>''K5'+AMF|~>Z;ISCB|5IGzJ2Ӕ̃}(GqHkrPPP*@F6q96Q*sPRr{L[Sw~kbHK=7[IՑr\A~C3eGN@r5hEO#K e0o\rx:1gMbtH G>iw,QyuoWՏ"|zN3ib'zz3^Wɓa< z훓L˝x]HC9Ů& ZMsWإg +\Wm8CTkF{WYzҚXLٽnf;-3Ã"w:ݬ7ЊֱQ;a̍hk" 5z-id=QXE[l/aMjd(QƋkZHv7aboŽJ~=W{H4o3A59:JtNOSzukS)giQYYOtBff幋tYNl$ߕf#wgU뿡.l#7ـXvw:Yz}x9ޕW7U5#9W/s"gjܽ9ٵ[U&+\=gifgO Ws-2Á(k@A_/҉ Bdxx&G5O=A0^W3SK^RTc<痲ŪU^_x_ΕYjxq@6#-}#BI9c>Bz`\(߷׬m}{ز]غQ#3yr,b.Nߋ9)ȧu05g'IݎT.'p1TUBUExsT):BIw:}\|(֊Ϣsepe\՗Ki"MkǕ^i3mjDF=H :bIVo;Hs͖=:z[qom0f09O.Mfѵ!mߌVa+7:H`0PQK:{u&=MUzY<ؓ_v:>ҸUmc_kx,|z7s^II &/qΛ&[\f0/崘rYG9+ _^5D*{|ɟÅKjkn!Te=W#=<kvya˗.Ŧ +Y ay~J.:@se mpVzt VAz&2O5^w P@=&Uϭp9O毪Y9K}0@CYB["+Vh>kWapF|m#vlyv;q(-iiI:FwNEH^;K9DI?sB1wz+73 rӑs0XCw2ޅ{D2U&ubsCyY M͓yz3C2ԩS8q8rsG*~k{`UU"̆K!Y@ee k.TE=(a5 +V5#eh@O-z7MzI':j%n&$OաȠR3 Foh.5cOoIz9o_>§ɣq<5 >{4~|\ܞw|u]NO.|nvd Jβф7QzoGyNz6\Fbtwn>=@hinB#1Xte+R̾PCer/[I׉v' i@Hk5x hIo~@{&Γr/Z7px|qlSS0OXdOz1H9w9LCrܤsY˷1k^ut`f.3X,φDIit*z\]^>?Zm(mvqW#}N8|Vؕ|knNFƐ twu3doE.?/ȏP΋?37BCIm.gW7濧)~](6 +۷mLJcՈZO:u3&@0;krbP)ۑ "f?)$[JɔYEG +{Ӆ7,mt]C޽ki]tH+).9){ypPreY\+O8ùE:ÇE<Ľ.,`ֿ%p6TZZ5砫@UCKY\RX 0ɡC#1٪'Mnqh#};w/ќs:閦:G6%>/G0ʽKaxy?~L~h@?7Lk8:BǏ&$^/=ŧnk{|~;'OH|n\\b/CѺ&OG=mouKcANڈ&'}׉_}t6=EB<{d7k6֯{fm˺O0fZ2k/l~(,q7V/=U5ust .;}VaP#/5vgj>ǟG]~ytACf2 ո:k߷Rv<8,%[tˉɕ}Qǽ޼w)yu6 EkffEJocزH)AjV VIIKʬ7wG2oG=[7}r=qq\↑8JyvF%aj"/b|g]s~cx +~E`u0Kf9F?/ߙ;WCѸ/%Kx`?9kaR_lE/]wW[X"+VcYkV.٥pHk2gY KiZ{Ja9je'='K^XzV؀w6U!0n'rZ;ŊWc)=f{k6oل۷!:=]jR. -z8/Ӣ?Yi鹻4dg#boX8M\h኏%Gž(oBicboLJA%sRSqPΝG)kӤm PX|X+=Vt}Gri/#m~\8 +/FYEV]X$=M_Ȩ+A ꋨ#lЕA[sFr]u#Գײ1cMXEOz VKjqf0ǝ\?e&Ml/hcǝ6т7^^&ڎǷ:,~4F晸=Gwp?K ÷/w֟/&m;Px~i/@x,qA:%~]cd'6MjVĘz:αh I5H^j*^ _`#ߙ{H<P +9tibt93 +UHU(d{fvzvfvfeפ\K["EX>EZ9Y<>o=W(!w%xWUUw]<Ly8wlXA.a՘&5I{9Adp?6y-*&:`f5э酱ɼh|A9,kG:{z>d9{gtmG ]U80د}tڏ-\D'DuYN֮.csRV%{X^^s_-ߩ5QuCD;EBX$ߴ(ecmp=qXX~M}*fx; CeM~GO \&w'vM zү끷3W ?ך G5;:x3wo^_fM8@?Gmaa[su l5D %.+ q)0IO֕ڦh8ͼ]p}N67=G=2 /{#.e1hܜ3(#3R5zrب圏Kx \[FJJ*w& +̹xqBǏ$㐘xK0|Dsq9x{g6WOwѩ{}U=nxogp\6pW./_ǽ{ECg4=(̽9]p +2zoܾ_ HOLd!?\_Ѱ 햠DV .ך164ը6k}mpu= wͭ1ipvՉVwj oGoW>9n CCZyNrգܚR^Yzjm*{Y8l/j|ϖ_WEK7/aOR.?>~O?m__~~}ǛD? mg5?ɸB-jy&uSf}2#\5Ә1{y%uPxJP ׆ږ<}lV31?'ř>69.lǛn.r3 {Cb`rS{8Z>LO:E`qA|#7LLKr_yO|<~Zȇ| zx,\~1OQ.6iO'ϖEo3-|?/s=Kc_hxb =Y~Y-bDߐu69`6.<5==| 0⡷zGfX;<,K|a-sC?><9!Ifѳs2{i:BYDԿC6+myktRy;{Vݫr|(Gu<sn$:3a--V5ACޙn\f?|g_5rw9\pkk<0HWEi3"igg[opUܾv ^n^71̌(.TȂ\ednA&Jw.Ӑs E(@uyCPUYjlDYe1*\Wzgs;4$Cۦ-fu}Թmp /sm"-TK=r]o'F]}:W[Lctc}0\1K1,͏oy'V\a樰ϫ䌤M%M>~d6ڗEN39s\}6Es?x:gO~#|:_<[V/HzNSSї78@g,,/ |SaLGcҙ:YWƜǴ-NNa" Eǘ3wgTr?OkPKK^XYOc;~n1G%MfNJa+ezX{eΚ?SnؓQ!wpH}?ӣV-=hj#9y`h4{-LhNK8?G,a7^E>|/b~BxdyunT^9\wm]Գ#tAZfٵ9E%5`ɝ;냿CqXhЬ@th5+J_A'yU`~| cZlW +S?bLD9!Ѱ.f醲VɮߟgBlZ_dc84Jɉa#u1|0)Atq$RSBqh_4^Tu0XGj$9@U>{Î̥pyό->1a*<.&ގ8cGqx$>K +O•ɸv[QT.TSըpY\x},΋p$.](<[>ꌛwy3]4u\%**FUY6kP'חC:9}y騭,Aey +QRT z7 U5B]M +ji-hc޷Q٤-,emt{shv}l ?]CM,N?C.Vg7b.NsKKu0ug]ffu*{g:sX:.ZlLYOm58}Z[pcNؘKdgK z||Ͽ7Lc9ᛙйKd+'D[>^EUXYfޕ3\py\8+q1L*C}g.cя֢ +'3=WsyƜ]zkΑy,Nbcc +벷 Fs|d۵W4f5Ǻ0o>SwbhsmY\Ժ}[EM]h=ܣ/x{u;_S%`Λ3r\[u_sFr1227}~-z.KhF)wͳɽ`/cՏf08.QPvG||/Aʡ[sstv֛;Gß;7䁥9L=P^3 +z[%CͬC>f:Pl+~!:(RSdxNH'9q)Pp'dH#-- dh[O?~[!Rۍ?V#Ic +V4c vdzg9Vg"IvNHLT[}xcC~3z>< +ׯ_{S4k00%y7 3:ݹׯ+WƅkqYsZڇ|[c9D~nU-j/CwG9z:jT(Uhi(G]u)K P&GKs9jP[]ryjubٌ1wf=pzmġ6%k{1%DPS\v5#\3~xܝD3ף-m-[[I.h558aT^/M |y]wjbHYL_-.r5ak lnLhy ߜW8o_{3 ՗y͹{*P~fy{o>çcQ/ RO +#ƦnW>;a*5}5ȨN&הW5sHtwF>aF72̹snjcJ^?zt +F=ӺkMtJ}Worζ` zM9}Źp1!6¡}.U_pb"eESELjv̟Ǚp`zg=kRpY#/6",u ,Id/k:kX.{9}SNKZceq\x1}V1/K}ƙ~<.vkSX]+kBg)Ø3_Sy={exI$7O1L1)\y>x,ц0wLq^6W=e_KY 5sG4Iis.i|3ž]@`aS\a-^-Ϭ?w;zǓ_߀3Mc^7-&Ҿ&폳h W{s #w9I 5H?>dVҿd=X"BUI{E?2|h8Rphba['Op~|QrdVlҸr4{LP_bעO5X8s}PZ108zB'&$8dO8xܗ}qHNNƾ8tȁs=wϝ{ヒ4^F]9+{-+E}e!KQVZʊJ!#1LZtyl 2r3PRb T_.a@>Up}9?C}-\%Y.Eh+D{ch +јl=Z^ѽpVcCAGDCayvS:[wjҭF vcQwoOX__vQ-Pѻ=nGWWW0mvy.Cۢ{:[ѥ9`a0cO737#&t1>#uy5ܸfS3ZD=miir5c|Ÿ!ixv;57= _ٓh,&'uۖǢ>ăuz͇XC#%߲[?nž{W1jvϳ_fӎyJ_b须oGتgCC ^?dGmv~&=L{}?8jugй%'~CcafdKp ҙ:30:*Spx*KxIhs&IV"9{B1='pDx_pw +7)yfҳG3I'a^^V_$:1Ix'd8F&Dwjq_.g?/}Es ߔWgڲn%[-!5gY ᖇ廘aM쌉E#LӲFӫҜ0) geqV_1ub (\]6qL H^spQ3~)+gOճ|y=hwdF[{irCf.àeߕ`m8p~{8_k}֊/ڪ]7¬нx˯o@{ =;B~:buWEo1߰Pn<wAkǻ։1)kB{Ղ}M+uPƫ9ԤaW$' 8G7B,FۢhǾ(ɉ_N +:) +1=#)/0-$xUFD^:){9(&.8MGkbyfFEY\P?kƷ'+OI&=q;pB4𙳉8y"OsGpiYqTYaQ/FGk=zv!M{7Q¼2V/tWG-z2ƣugٛ&<i՘optсkg +c-`ζ]ujˌ=Wi5ۨS^A4h?t nmDMm QM_ʆUΏƿo㛯~WuLgW:;;[3(_%6xGܿ]K`O#:Ӓs=oӣ)2qi2Xs^9.=o Y[xDkGΞ!{,wvu })a ^\_5:,u1]/gB>'Q!{ynםTŲhmk + *B1ΆwY][$̅0dsƞx}55q~{CӃuS:/ҦK%7>wv +G|]e0.DkczS$HIGB~d-s]YY Cct󊰛"Qqwyd/;+:>Z-' Oq IONQǘÎE=- I8} \:6Wk}lnGK=*\f_Fν Ⱥs7Q^X|Tg%_U pp|S}XvbfCXm~UNXpwjE4r#01&n]cQ_Qzs.-zNY4w\nndV9JK +PVZRh>GˆEtm GL0[5έm1(IݢM=x_L5k> ukLy'cbZ4ﴰ9:X{'ry`~0^egƃye*Obw%VKӓӜ-8/ZN~g<|qrR;=yYҧdllXx^G9Kȳ0o/mǢz7qf҂'GKs@ "#g͑8 uփWssFcffJZq+Yy_}h<ߊsO^gO7 L/7Zm]=Ago\FU|Ώ2zcq>Qo|7|~,733rV’D?ݔ̧y֖V~/4(vkNYK-K#ֿo7~m05H\o߭|VV|;8<{0/D r'U2C;>j@կ-Qԍ3g~XZ%a0c1{bt}8ωr]bk+KNDk8s}Br[T9&ZۨXYqE#!)QX(CD4u/=Mgl1XD"qIwBh#:uA' pA8~7!>$xjzOLٓ8w8E4p +n\4D~n. +\jտ7peͼ#z@65V=mpGL̏kl%\nD[S/}=܂9O3:؅YOzoVެUhQ4vg >}JM5hޭ}h^$ r5h)\"wVfgjD֩f4J?yރХឞ.ttv};;ٮǾ>bm0H&F1;!< *0YN/KzbLǏ}|x1XzDM[XgLljY^ rݒ0}c9DGNΌcbmzgcY.^暣>_ͲnJ²a>~ιc/hpvS`O5W2ll`>=} Ǚ!WuU2SR/}57MK<BhAUZ3X?tʾ%އy!Qr?Ko.7=dW[^j`^Člu>%9KGqZgܻpyť/9HO<ѸIX[Ң}, + Jt `%ګ㪆w_=z.nko{kܷI݊.̋>q6y s:k<'J.DʲQUںRTWT0?_n*EW(CmU?UV53'{ݭ-hihBhjXjRHWVo FW9ЪE2M-#Z+-,g>}JTS2]CƗ'yn>?œ܍Ix9 #Wt<2ejKo/+ggᛞVӻZ1ly SM^!݅vK{4بS ˩9~svMo87>5qY֙$~ٛ> o'O~O71}Eߝx3װ׶} Zo*~N't;r;=-zc},|K˿* &:42 m鵎̹̞HD.պ&m7|#HM:}HL8t|b8C9)^xefr_sё:0V4pgƲvZlT tD&^kαzq^bx_8ezs~q;;=TZ 3\̲ e}ԃ2!.V\TRS"qCtGGԉx]o};׮^,@OpyYHsŢmMGA gBVz +EgNtl-j*DӊNm.G_O:D3W=5qɭjao% -y^̓]rl& k8Ht+YUzWW%r^((DIa1rrr~A" P]]*̮=B jjt\O {{3补Q}*@'{6y;Yݡ=Qz'7KJwoDXCo=..0pxd}lafqr2X{-Bk0(w9#&L!}IRph +pRbGbR$ȊFRb +81,L +[cD#qiC/wi>se9_29ԥsr4's*9BWg7. ^>_3:3 tnР0`pӿK҃sدYe??s˃ <֣~[Rj?,{V\|wR@d1s[7K}"χ00>xgMz//;믯j4罃ugf05#zwz]s09F{OqBW[{{wg|Dka 8vW\d//q +Ycc=F˫SG_a_ +|”oq|}U~ z`xQZ^׾{Įm,~g6hϯڿX˚;obv?wM,waZBgRFi ]GGzvDL;qx' G/wtBbc3 fOMc6]$|1m웲h-wCHD"BI~>, ^X:S"N6qqkR%Z<1) S5D٤o2t ^G=?9Rgs'Ӈp1w_?w"==ܾs[s7/2 D_ͥE˦FTdZDsV\X\ttM4qކ"^V_u.jPY!}i()ˑ-D~nr?/JDӖe weOPxOʊ*eP"l +sQPs,FiaVҒ"e:ZtuU=_j +7G\=l/ό5;;5:4إ\.b5fv7>եgr}__w\nujfcaܰlZ}5sgN//cԿ~?}sg%~{ħ!|kg`v^~zWy_N{nt{[5]ׂ׮Ӟ^.SU'j~=4h~f5Y{0ztںМs!o?M8/Fkõc8W4rb>$ȑD혜}U9{a&n+ь+G6:s'O$M{ }-ܺuW/瑓~ QsJ QWYbEY=a}1Efiը(UW\qh*яhFuu.lfe~@h9 +csu!g6pvC\Vg@grbCey.- ܧCs*Jsֻ]m]שdh`.zK::5~LfGuX\kfۜȠS/Ew<_jieH7f{1n=*`/C.oÄ)Ƅ]^0&yJ%x mZѮװzR/W:'Vʏy)~>ԹȚ6 >Eއgp{d]F֝u n\;yI8ﯯ=m´ TRpW#3EҒ<3Jii>]s/zUE*)Zf2 u;+3Y:0?[ޘw̟ǀ\uHla +`{Ǐ7&>X63gfb,zZnjMV{zfyW>7)=iw AkEkJ!g ,,ѥ53{tͨ7{cΜh˩9s&fEʞ1f< ^1esP=GёNdF<#f1=9tffoT^fC5~[3ϝs-L7/Yܳ,mӏgwnOڶ⑿}{# ڷ=ox)o\|m]+_?l˯5oo k0^bX0>Y^G9ļ~UڔJ,= _- HJZ(>6 +Q#cAG6㳩~ ^SpcvR+&Io.rDk^3 +cDw' oe%1a?g#ǏI8x0OƱ#I8zС퓢ϝ9ǵE_}Y47i‡GS :[߈\SXY\.Y4ly1: y7wUUhP'FCUhB 멓X+TYtde PX`boAn..TΖ[]Ye=vmѼd0rlh/;t4/eq˺,΁4˼.̘tndfH8:Ytjo_λcoтj;=ݜǃ r.t06*`+€~Y?Vs;1Xa)cd/G|=]̸Fki_KZ9aSץܜQ[=0KOCoY<UH͓~xYٺ<+̾e욾/)pƲhx~r^e_1!%L/L_׈\5ޢWY̚)ΔP i|a r|D>W^Usߥ2Kܧp%9KcA/?/'r7/ȿ@n{kozWi~_-ύWUn}fMCG {7..gq{hGj4 ~jkNEkܙe[LEل:0B=/Qc'N#M~`#.Ok/=lh!7ZcΑ<ѫԵQ5710'c_^^VoH^L L1:,f}e{TXѽ d OH@RbSAr~8zHF9r%GhG:_V +Ξ݇K3yqኢlw'SvTMnf0&2!;.{UQQtEeQ>joi\YzYvğ eݛK4-eg0__639#NucU~֑rPߖz4բF ee]u/cd1e9C-KI.pz`P`ݭ fΗ3{:CgOΉyCqKߞ?Z_<_>OWɋ <Q vK4[8WѹӜ(`tx̭%{a_sͫS ӿb3ҢGw8"Lg>:x3:5 v^հƌ_6^+MNisb>|cuQ=8w ks1h>y:ctC>I\] hmQoܾ}G=^y}~žKcay~`ڋ+g.<,R᭞@3rO3Rhs_[h4Nûkk͂;CڵCȹu{j7,6#Emn+"+K(H/@AUaoA=*^-˻,tnFcT_CYaҵ:Go087323 2e(;>05r,bIƪ9GX \zJ1,tQ}} j š~Xm]mE=ܮhrZyaKmCN?S#u3{O[#w'[tx?&ƄOe)kmGvk}kXB:[#̙S: BX<+,1,Ù g`%8cQjyiR;{Uo^Mys|nzJx4y _nW4ul~#k}xhM59hBE'O2,z3nm9s<,ZW}cz3X- }͙“cƕי 㠫GIg~'d7Vl|Y +k1A4\7'(ö?^z˫p[BBdw?Lc77:ҁ⍻Z;g\f&8]Ozm=Q{>`?j^R6b+هf1i|B ? ? xR֧.Yxxc^[j^A+G3'hn^\Gb,j-SϜmQsM/M}B cE3}=U#Z=؟Sǎ8?e9y;+\#9ܺt +W]EzUTV[8D\"UPW)l ae|owӐ}=g_Eq%]z Yr.5*@h4ͺgri|a '+[˞#<~ 7MiF;ei9ZѷopH4w9KPY]*YtCKShjѪoqtk?Qkߕh5&{-,A:a6Kw?rOiWyqO?~?~7ODjlqsTǎɑ^aԭ1XK3g,7mqxJpvvLx7FrY{ +[O+ƃ?zp^y%ٮ 釱8?ˌO3gVgT ^ ;?pxOl6 ѿ>L '>Y9^-˾dBAPϲijB/c]pղIrq1̼L=o3i}|X7ICs.kp9Ǔfwy1n4;GZ\O&+f5 ֙.xmBa^Oݫ(1Kc_;.=oV9xmqÅa)ELlߝ6`oM{1DKY +/8zWb>S;l_z0n5078w{k&w|DD'gAL4i6"sE|3g:8N~"뵭K:)ܜ{Xͱ/b9`1ccE:+Z_^P򌎊}TDCR1aq,R:ۉsH0Q8|'v"2(\­[Wqd_M!Sx]ѓ%iΛ+|ݽt#yI܌ι{w/s^n^C9kQV\w9ro0G#KŅ\2͚ *ʿr|e([kJ-jN@^XVsBU ˄%ZUWS*ojY"Dԙeo2mSkX4kPeW1CCqQ8ܯArm紋fo軇˚Oe%9av ?$}dM=' ;>5kH8a#ԋkӲ'K٪E<ޤsG/k+f/7S +e%ܥ]~I53g2z|i O6?~ ^1']lss]y6Ͼ#Zzg漢t>g;Ve_|r2=@L=75=ktL XԄ/w +9ӷ_Y|;Wbr̫]=¼ѻ>ׯh?exd?1ӋdJA LK w[} }~)'_ ~:_ 3X[W뼭9D<,񐹼u#mk"T?-{sw};>?R{ɚ(Y6g [#< A⡓p$6h?s#oMTic}TYzO: $/l!#l"zZ2nlZ~se| #yI:]mdݳu^`K%&8/~W8r0E␐cE'ĩ8G?Wqg7;Ka ;MvZXLF0;c,%Vw –q=2?˸e}1s/uc_#?/<?[ÇFǗ\мRW1kkS8-LiъhAwY86k"Uܧ +~G=ŗy%~7>GOVzC^U yoW}ׇ\[^,Ld3'g +Uk]O_zDg;>(:hߙ[55}7e5>>9>e= cl"WW9KFoʾgq6Wkی'09fsf~|^ewx(3~;xu(v xŶs 'ӯ 쯅6ԾJgΆ* D&s^?^}3C Bl?ag>wg&0:BCL?~{p$G#191EouF Ѩӵdm)YeA# 1·8u$$HHdrAq9A5:[pSczmipښI ѱшsUjf)zN'8f9 + c':Q3٧t`Dءd?yg>Ο[HK+i8.|xM;/#/ +#/W4jNrrԢwn{"vn],ӯӸs"wkHuW4m&su?*/Wn:C,AsȢs;PXtM]iQq1 +Q"T KD[g> +sKePZdjsMS*gORuƣ+xzV7>+ACm*E +UE_>z^`d FsN;0H=?r'k<5z!e.k9{vj=ja^a?hkS/oک r9Ai߰a,G&-{skmuDtGդڌ. +q=nAl|F۞+9vnmZ{@6=ޠw2=^6=Kj2yqKr^k4 ]ZMg/?}xcMx>}|?'>y($sYGMV`OOcLt8&b|n'ujd0sc#G;(#8_-zXeηQYL&G~f8ۻ{8,K%# '=>Lx}uuTwuLfG;*bb6F +""8羙 buFҏ7LK{s͹ݲ{1`[_x3koA5+vudWΎ`ex9 3AӢӢgD.8&s_t9^]r `ruZ{x=f=j`<<#_P>&?F~}ݛ󇳪z:\圃%>]17ʞXS\'OG?ͽw߾?yH4>^g{WOr GWn~O޾`Eg-.CVv8\=CNWޱC{|lvM}:e>aHg:Nۄ)}ΙNnL涙_wW3v* GsxK gV:;~^?A;|?w]˧yzf?9?(mj4탔fƻ϶>^k]LJŋޓ%r Ɵ U*W$]^5Z~(!#C͜^#?XxoP/.$J[q(ziu'}u./uhhj\R}7HxvAb|:׭}txʾf6qpPwPXBHyp K9Q9G"6:Xp!}p`4N> "'+iiHDhŚ&WZm I+Q\X <@I6/)(g}y(.IΞF~07C~:/\N +/ Q~%Ot3cWqJ\acR Tޗ20/_g.L޳X^(c=W5iU' K7oz *P%V>7q[toUܤ[÷oW:kE mmO}zEm[;g_8wnyg26O'Z]=d0u0נ#5>Ѧ]ѹ9ng[`z(^N] +o9}uJtM좩m~ Z0Z=t[?x{_,ᣗ+u8hF=+cLdMONLb7)MdV-q+xS7,ɚc h/_UI{Oǿӯg{ELLM* Xu}hH''̌zl Y6 dX{fgFG01if#OMNzfC9Okm7kM~B98_+0c4w0Կq_Gǟwλݑ/~75y8k'/^;[/Mnb4zɓ'Yo짾HJIó__HtlyoOY{??N]\}My;>R<_jQr1@8oz9r@#y ao2Wkƞepg@x|?Y$= w͢${B,I־:@zgXr@fbZ.\|쇰@b-E +cctX0E7 q!DtDDž`|LJ +q>pizW˵.eVyR4oJJd(}s2" dgssD EY(v%뚒j[TRkWQ-,m}YQdhrK K(+N-)_r (+pU}EťRT"تT\cmt7[JtrMyj:UU~FSo:]C3P;Uߛ\]x2MnasO;|ro?, ͌(oF1>9!!5Ž/|4{'9[i5il`?φ'k.2>_oO>}?yߺE8'o~ܳe7=cZy\Sޥ{ӯ=ܣNr{焙?pԤ~^zdSGnK*{W "e?6:g>p'?/5>hW|.N5sǝ5˭_w7 f:aKSo ($܋ǛuOn%m~bt>ٓz/ג]G5NWɹ<_w<[~|gjkG_ϧsL}Ѷ쯶B,S5䒬 ћεqKǩlERV9|Cc[J̜c`XU?իse6*مAZb QOz2^$pmLj0ն񗤯t<&\{ YƢyCԻ+<WQ G 2*aGGLTFlL`_\8x8T8Cx!ڐܹzcޠ|d /t2/##R/iUA.}4Pd^&j˅%B~a* +JbSpR)?/Io3qϢrߢJqu`z[%$*kT;_j@zkj!kLtu*]5WeSY]SYܸ3 Z7՟]my YHZ,Y=nf|'ٗ9d~'Fl8=zMӉ!Hcuމ!lwKxh:pdx@;DK;0lÐ}@W+z <ݘ#{/O VEk~O߼oL}O_'ΫoT/7hٗ;93@ ji ziJ7]=P9;wXbӆ}[|o~')'OVkVOLM~`\ְŽt  bh_Xì=d(=3_<5_`|yy\c<&1i:ט^4vpw>n|ww<5<=v|K?7;~ymx#9oH˼x7DZ6=0v_}>pC9}ъ'qf=1P_w3u;w֧!Y:~sޛ_*. 8)Τ!4 2qwW<_~A o5f O>D‘iS ?DydM='ƔC\ + +ռ,-`-䯵جY V| SOW^Ѿ:`A@hx ҷ+ZcaS;xwƨcCuVa\tG">΂cq7!GcqDNڇsoCA~.joVk@W;㫭D Q=Y#"|MAan +2sv"23. + 73YA3QUk}dinAv& + uQ0]c%9Qց suqi!룯4y4yloYI;s_+X^X8+ G24޾׭VsWP{3ʕyK\S]Ztqu7T+`v +c;|qzhqO㚬?oY7f6 Z9]IѺ#hSqn`q<^~ +~wo_B4U@uS4هkS3BDM{wUtٚhk2zs]^*英?W<ŇG×K/jiWV1 "sbahcƢV=9Cs1M25R S{,x+bO2=^gp:Ś1.<ľG5XaԸB>~âu/WlX{Y_^:sa.%8$kM2\]Yf bWtℲV2rĚ.~C^뱶/ѿs.ώ:7T޽|-m%5uo{ş-WKzݚwV곸{?ҟ=' +>6.*RIX5 ~.dϑ7vwb=wzmpWZe7z)#\d s:Ε >WIɅx-_bٷ8}AL`(Cvd)K.SK[BB]_Y멹 + +wH[a`_|`i8U횃l< sXޝ8+ LDKoয়oE?m5wH2=\әuKkXz>E>;iI9.-yq45Ax.w⭧Z2z2/k˳ֹԍfUMcJ}VWǴW4|&oǴG./3xQu_シ1ʵyְ.ff1+zwlTtmXu$s F}EeNئ-_ʞffjLcœ?kvua2+:}6dBE֗};5lj弍 `@giك*b{Fۺ^__/p]:;Cfm[ +; ǿ׿M^w/'Gֺ&.E;zOܕvS _Š;OŨO+5Y]|ޱ||Mֆyyν'%l+_"Rև?@Y]a6D4fV8ȓO73_A>8x==ryyD%@9oԷF2lRzش…#3_(ًZyrD:gfKK-~0fگuԼ+*ۘM@xh ,ъ "A_{4E7~#ᷟ>/p}!λţ{ KԺVjz\"}O>g>dk5l.KQ7c#03'1qlܛT,ƃ^ho=Oj8d)\{oXxd+ ? 9:nizXu_{tfkdaS;%YkEz,9o13{x=|wͯ +|cٿ"SoT 5G!$,Ac#~&'˺lo&Gp1]A,jό̕by%ZDCZ*.UYEwF</cS5M/Es~!*s-Lɀ`e%Cz{x1e?ڗˇxx>C9;iZ}:e^FGkDGw?za=k8gGG4>3wnәڜ0;f=RώimKgyx}_XY] 3ònԨ}On}[wW~y'S癧eyKo$hsoٶUNW叵3œG̽m3aOc'2/1>d39Ms޲^%w}|=>7a_z_(WKCf~uLj*(~:3ޞ<'`^1ш?.4Ҽ`r901roF=oѸ@`0=/@ cS6Ȟ! yA{y 7"#/ +ÁH<(Ѽqq;!䛢esPu-}mU7݈tcf v5^k877<23ΪwVnVRR#=IcY/T)HyaeoqA73#U_ /yy 9e^Kdeg-Γ02ʕ\+s^R柯Wkoor\)-Q-~*+dq\x{M8,luM}Ռ92}XtݸQmj=I-749뼆;hGW[ 8֡ P7zz[Em&zOyǿƿ?~ywz-:x7Iɱ^LhgG++k<ݘO<ǿoߗxtO40f#Z=0(J26aѨfʢj /}^|mZ0h=ً^+CLMObZs:{oVYs{LK5}}^Ƭw}D_PxW'DVO˯_}/Wihwr9ÂἥKvthU=:qrRCkظ'YҌzvosa1b/w91/ߕ֚;o=?Gk>Zǝ_ANΓߝ5$W%x}KSw| \V_ ᑟ +@)?:_mRmr@Jڹ߽Lږ]O|]~N~#ڼZSb3e-@1q}1{=fw9a/ew5'`&oM} |]}aX0B-!R&h\`0~_\2 Uo.}e4c֌US/GG!.2G{!F0?!e?o?,_)uq u+dYBA_PEGa| /8H!X#qP3Gp3i YgwUv8-hnxm{9?2Ϡ 'IH|V5oVj +rrSyٗQ\(.EՕ_.znp {s#93 r +JP"auQa07O'O޷X4TVToQ1kuv0s٬kf{ +{}D*dUWs{N}55Y-Mwրz]Mhh&aq{Njҭ{z\mEOw8${t#}9tti8=򗢅˯;5X_Z*{WNYwmiKZ\znOê}pXO?z,Zv,h V[G{!а0ahE&Q7?x?_|XZN75^<BO}Nͯ(aL[m6 wjtJc{kse1?ߧ%<XpwiZ<>p eo>_ {eCKևW>_Tkxg't.ᰓ3Z#3`>pWvG8^]| ~˟i ."Z^!sbMO?kTe [oO_aSdG^r~,v9^;8~N?烉8 DDF-4X:c,q0؃[$w2NX[#[;/W?f)#V 0h3>">Zl>䱜egV/ +&o1|w8ovu6!s +t9[tPyfLsuIg&W{(UzD + OAҹ#8F$+\0xyy>b9FbuzxEZ,䫾&f<c11D!Rt0k#:a?`q8|xɗrq;Y'>7óKDWW}8Ɵove$'CZ1Idcy()mY1hj`-+^ +WiOUg Ӓ\ѾsapzVw! +epYWsKJrEs磤eŦzuƟDެ^X~}â8P@Um5EV]Ztރ}Zؓž;hw6O4cм%s߾. Y;Ө )W5Dm7?o>>˷ք?XѢ!fGu kr帺*Y3 56!M:QO-mhi]ڧaFG/|'890-\Yǽ{W^mOoU,/a|؉.k}6YCaMև>ͣZu^Sbe"W_z,k|_ћ+_fg?;o|Y<;w=Ӹ+5Y[|s8;E[礫ۊA֐NL~%?;ݿxg+McQJ?e9f} g N看q<])o~>97xr!ϊnv uv75;PWxa:tVԆ髥uN>|~;{7Yi~W;꽩5 ̟y~eW?|"񸖫li߫xKGϱ17ܹ˟f>>> k\!}gT9>p͸n~6O]E_3_$uEVT?C} 8SGp͓8w$DF  u^Gt,57/`E^ E%JϏ0n0|v!B,P +#"/:b߁x/{c'O +*dZ?/)=m 4.A-CX{l$#;#yEJJþRah&2Sy(-Ӟ j*ѣysϡ(&MΔd r26s3P/Y\"Yeͅ(( +_5WP'YSeX|։U^\>J|ܸYF9.L4T&ww6[jZtuԡVm5WNnvimƈS#68LplU4|-}<;r.kKg?Ïԟ{l`)WglJmz Eg0SeyhTV +PsVkm#s _aDowz] [n*Z.|[[0=1 kcrRNaF=#wifV&(X ;+Kx._QQN45MCܥz}F߯_Ov{M}=>Hz@aSo yR1^r㌃:u=V0LjSm+n +sYd;CrbqީsVmXWk/xɲz ӏcA/M3WbTלYQkC/L/,^] ܯ)8,幢c ;k9!9^Z^2&0>9yΪ߶<;oztip1sϯ{8*8. ooPOO#\}Tyνmu6M:WV^W.>sǚd߭|u|=v;'Ouw_:@g񾭹h/s'?xMHY6]>O/`-0bq b]{!\5W>W=]>W pPtolrﳧqǩDboB 4]w0ѷb|."bE҇C?:DDDa_|r@|ˊqH+ÇpRa  2ճޥe,-ctthkU +.S'O…8,. IL$ NByI! + +r)4dd%<|3(?]r$LLjra&OGp9-2.ipcEaw + 3ua{_W+ϘD%8B|puEjkQm7up^unC6ܢۺ8V;j%m.S>y{w종rbunwGH>x6K8NMyE84H}ډf4{;څ5Ѭ3 ^[]}+5ֽƤ5g"̥w&V&ۅhhQWֈdCӓ֣/x0}m̉~za>˾bD^{^z-+ <^2~2^K@}7njV?);jR{rweף^[efR tI|+wه}s;"ctnvswȹ0q채vaGz~z_[>.>} srRqԴ MĹl,yL!e(Zk5Z:ʵpxl؏F3;BZwj,M 5EVsVkh&L8;0'ZeiO %rV,=.[ $z:#wjש.v^fI?ukѹdp[Z]Fa0qaަ&ջ--hvgW;Z9Ixܮ|w. P-㲷E}>_|oO~mzwko}~Ooc>vk|\s^=mM3~XgϴgswIz͞؊<^khU/Zk gq>}uuvjY9Pk7=jY4Y@IV/1}H_ WfF $4A8r8/p0dtڲl35pdd?&u d%4D}7Ds@B`/U2Eʿ/>c?.Bݏ8x""&1HX9X<g}ofDTެB[shы/M. =Bl0F܌$eIpUXvSSs22Rӑd䊖ͼSKe,d_6uW5kKP\ҲTUFUXE>y0ޛcMtT .;1*م&^O4 {[QۜpM5p4({GEca :6_|[1's׭X[棟T\5Ik C;A{K;aho:.k𹣳]^IS757I*Y|KVV̜ѡG o{8+ODqg2[sHsd˨zjVܣ<G{wD997=bj6tlaSvuP+rQe3uN~yKn睜d]y9Kǫu6[=™Rccףdq}.rzSn{00,{C}:WKuޫ5=^߻~(Kg_ߝp_[|_/N?/7NΧm!+?|_Kݽr?oϱylr{Ԩ:i^o=̺ޭƻ?ǚ?}im_O{}Kb3} +06U~G`o7D/+qiK?"C'l=d<Ϭ~'z],p= +K@B,:-=ȘDVǫechhoK'LB|\4qQPpDYd#& b"V̙p>ut( O"Or0(# +R277'-G(]S['z679س9]4r.yZ 8NY (~sdg*/P_奥RVׅCzۍ]ŗ{Sre=U\DcliG{" @o^sO~-4O:[X¢tCpņnz'q_t0lp`B׃ :]Kԫ=r.42}K}{MhchloGpSڭTm%gE/3!ZSԹ.a}Op&h-ڶ5 }14<"Luuh vdd +y/=4hrXceɉU9_Ǣv伽aw+''\sq'#:i|TsSv|2KQoajf-Zx^%wa"{a990{헿N7<Gc?[w/w>ugo.=b[_e_kƟݹgIA_=B=ZzݝC|_ Oog/#d}S. =~G[S1jloo286k кe_~Y&{? 1` VvU/o``c~f>aXE/ZW- `SVekqgے/Pyb:i{ +@D"܍EtT t>p`D2Wup3}#OxwgO!iU\^7ncZY |S &Dҥ(*JCYq2W!ӸVL95ԌRs W1%/Weؙ(˿7QwSK=-sY}Y{WkpN-Z0*׶9F6EU +obނ!ѧ-ahMѿ}-=%%{7D/'Uk4peyfAN +$M(rSuu3ylqh.̱5>ٌ?f xf5:2~Jp +5FViyn֮cҬm! /,Eٓ-}cZ:Fθ̇'lwyc|Bc~d.밳O3n͑sƧ}nߞnmwľG pslwmmzw^^|wyۭeWڰZtKl@c{LJ\ͧrFSAչ:Obו%>㪏6UzĄb7G7=G6g-L Y&^n#}L E/$}%6۝/w=Df?ʪ{lR1,+z41p0"8q el&~FKBufGdT^Ld,|,ؿD+5@=9]~GKG +o{}Jҗ20BE_˙ "EF""ʢ5`QucZ}?C8v0dЇ0.DJqy.LB^F:ғ/ +,J6|oAe SK +9NEN^?M9S(ϻ p*JrG0/Sl\M>.m%U_*CB[aª8?%:"< D侜KolZ!C, =XyndTÃj BtD2Xg3B4sheW RMjXk-47j/ W,PM2s,!O F 8"|Ro63 8S) +Gu xY#qA$] 2.\o"E;s g bE\pit.pf9.*D~N07(.D03?;ssڅgPsIÜ[VZ39 &RP,.+vQq1 + j/2{nTF-jѐ5V[:ܪoݭ +/jZv E +{w`o-ݎqgDbmaPt/f:#Ehf&MW(LetiC-e/9\WO-VѪCw5e]!2 >sFzp֣Mmd_\G vCD/Lo}iƟNVN4=}FMmsi<>ܠo̠#Sss6ujzyD?sfgD{;{uݹe*k8pn}c<>4?+?{o:ߖP?oPƌ}7Mk6F=I#XS+:0]d& WOzk(Yz_?!:sB1r,:ЗBd?%Gtdڰ@DDrvSp$&D!>>P}9XM_}NLHā}F7D_^F颅/3gƉx. {Rr +)gt>CjE CK +DfL‹:o!#)LO_BKfja0;]}9 Q osE/ȿE榩G5yaJϕJI*j_TƤ֭]7Xs򷳹F[#?[;c,7v[1:"VO`aRtYܮeomQ7h%iQo; Q_(oƝ:_}VM.,BWW4ljWM| _D]]_Et}[k"^{::n;Kmckbzf#>r}#Ql>f8yRnᕩ75طS5rUqX`GJހR56vfswkߋuvMub9cR*|MO ym rشzPkM˥}LIwޥWfviƆZթl}`'ef1ݭeҦzu?;ϻ6ּvmWķ7oei7[tW<'VR[oav剥Bw%O|ݥ=y^6vwߵGWf=TrOXz_1%U'z3)"ZLOٷ9Q +E_hZC?Pm@08&B4+L0ћQPDEYVѶa!:˗1 gVР8`}%:$4\޲ ccb„!Â"TgGG!.:Vx/p$CBB0hXXDGЁhSx +RDG<ʓP"LtJk |#VoRao +/"#52朝v̜ &222۲D4+{2oE d"KM%_@L_BAeo99oW*y %r[$ڹ8Gz½;¶ +MuF|EXXYuY5?_^MqwǶ|q[^dWl-|yM~.(/zH)R6u=yҎ+w+vz[3~5VdgU>&ؗh<z-Q֊Ьr9GCoѹ s|Mlֲ*>>q1z#Ƭ}L?0g0^˵g-T6ZDr&c5K(=O` kj:gz?g) P=}TC`uv,јs\T-՜v/Gx $>SGwUŕ\).@IQ- ٙp,ʼn#q!19 畿iy&ͣG$d 73M{ ҂4夡 SmneEftzhe G7-M1hr3ԇ+{qN5nޮS}YX75WQQ!}wV_~y6 858}:o1خҜ;8*>$\W +;;eh]}=jd?u m}Xw]};ok4v֠Uԧ9=2u*sY,ogƙYWyߡn96aZ+G%WPmH?G +WT?lYb2i'[Lr=<}Γ7&y#qx5{Ƙ[eNW? m}d\xYO]7:4M/tՄ}7GF?#? ?7?Hj!?gzػe_F]߮Ɵz!|/#3[;3 % {Xb `cc xal3MG괚^TE{^ UUV4u/*K<=ym0KH+my$RODcar.CGooiv~[XA௉ZoK;c&?͵\֋]k<3Z>@=FĢ3y~W\{]Ӱ? x>"KycqyS'Lh̙kM<(˸6) 8(!?t2. Ц]Ye;7T+^qCv1Kɘ2< _k_:ww76eemCpaD=bQS2N\ht[KLr7㑵}Gsw]F::K~YyyE08hXcwُtz~ϸU3fjsLQjq_y|zdYa5)w9>Mhq\Sߤ{f5'{Ku^WG~''1ޘwK? 5ga| >IiX? >7/͜0d_s/X/E1=|3?s;PI?oK??VI08}1GƥKaB{wƪ~Zˍ%;w2*Wk:g Koa+ͽ!}+T|w7J;;?w8o͹q(ծܬ}zWO2 暞|eo2ϛg﹗2jf1yBf/=_p$1YNۨu9Ei׮Bjk XwMM͕"hnOS{ + =` cմ{&52UU@k˕+.Gzi/RiO7ߓf-EBcѥe.ΰ ~򘳃KrIGM=ئQ|f{"(T/Vl~!]7(C ب AvhwɣVTo rxdx}"xQ~:9i7mziJ:)geXkeeqZ=V腹iYZZ8'kKs_']^ytIe; +cahJH]C3 -gǣ۞jCյqOZHz|*G,?GΗ E2^]_31ؾHeqߖx ~fl|p2}hs,~t_9#/;싯egw8 $~Sϕ^RvH< +>6|{?fnOޥs&|SYއ}c׿C1P h?rQgI{`d3=|zٽ70\%Ͽ蒊nCoQ+הә{IYEuڭ[롬fnƩ-lsԹPܯzցUg6\u-*ni:uHy]*R-*ܫ,oExJbx^q.&nxq(\OhaOx\Uc- +bWQC76 +)yTIe!{'8JpQ75Ks+g7V+viP/mǵCz>f郮 ֡Go^:dUF}Y O<[4d|D=&yI>y cNA+FGdeerv`ꂬ-/B[2x|iEcks:amq%kKX_yY_{: xIkYKjNόepuY5/81'{Nm-%w7Ym%3 +i/T*cj^5/yn~~ٷT}܎hNrOt`jك{O-)#w\oq-q}w-:0{:s޳nd1}9}~WWr\|۲wRmyMyfkun1}/ukMJ:eazb\|[VX>3iwO\zRk3dLDZNgc1~mQߒ[Aە.MKmZ7ZՂs<P[6Dg$Z쒻%N9 +qiSyRQV5͎;N:_0GKZ]듋܌i;iukoR1..+-bۢ%<T{`|wrإ|HkSTu5RX/ՕPS/PW%m ҂;Z[jFz:N;{:dSڶ[A޾G6wJVo_e_}ig2:<nʔ?}пtvLANŅgs`f"L1cG<.dQ +e {\?=H{uFe* I +q9Q2#aT/Odfl<N MĽd[vWMU]]MUw)o{Me77of` ` 0 PHA 1DF2AB +D(ORHek}3uAÉy͹7o}ާ$.MߦOkx_<6[e``@ntZ208&!d`.#vṷ!\ AeքLN̠ Ӳ`Nώɣ۲tmVחķ"U}(OcnoOVWeumM66eۻ%>WBll׿%Ѹ_b$"QŃIe%׷.a/ZlݗoC"D~D%H.|6#R^ +deCR*ťRIJ1R>K"8Z*T&-tRɐddZ^֫rxPDn 5?|+ _%!FAz$zYvܩJQf*{ ՊNU5ohO7ds5%N~&{m4wx G 9;Ekʋ؞γ7g{rܻrx|(/#X޼xώų]y?op8H޼y{{''ǻTƳ;8ؕ]i5ecs#=8ޑ4vܗZcvG^n]_ cU^Sk",dv.͍mdjh\ ϫ6G{6?k|~&e5=s6s9?}^9łZ깰n{]N2bÊ{I[,p6cjCNvbym? zpm8mVqnw-kp>sK__ +[.0=I x!2=1.32;5$c;: Mz0uHGzs0{Dzzq ۮUx<>>`ϰp1kꖱ\g})<;" e=d9qrLk ;`ߗ7ӟ~-_uOϞ=sO^hp~S7\>'Syq`#c ;ϔacWd=| +ksrnvaK5|.Gx8\kI'ls!^c/k|lj]{ߤɠ v1|}>ߋZWs;`al﫟uUSk힒@v.0Hۅ`65w۵o}PZ}ܐjb3Xf` ;&Cfr@g|ve.bv =>v+wԬlvcWlׁϻ fsͮZbНЙnaund=xƹK=}28lNɝ;NTcHjSLin'tlꓞ>nl>N_<8/pQ1OLޖ{riYs_޾⬬-ߕw~,O*{֞ʓ邬>{T6֖dk}]^oAn+McH0$5,koJ.j),ɐW‘ $#fRg$ +`k&bIIcK $S~3x9R-&eճҨl15 >y*Qտ.o_@B a_j)rVN&wwվJAJԠU&8\O%h/r=06k0_G'`ҡW[^kV} dS#pZ@Y}Z{-mG` ϸ?5s> |?Ǫ·?ɟ_AW`!uxHV6 {dѱ~F5!Z/~,ӿgo_ʏ~kr K վG<)끶ݽ~ ہϩK3o}&1xwploGVl3`?yh4eUg5[>RmI>VnH#Z_}nu>&%Kۿkzǎox>嶩}}sGk#vW}].Զ-e`pkH26ȹ>7ʽ_+7ds]`-#v=nA!c@v[@;Rڝi8 mLAUz6pwe_f03 azuW͌܆ TLt&Sgdv ef0%oSze J[:}}=.\#S28<(##26/دl=1(.!1=u_;y0?/O +*tc nIHu 6Ļ +=XЯ~?x8(2DQX\ kf| +I&n(9)(4r<8H6Aւ!8A#GM% fRQ)`oV4_q/Η؟2Ж)Ђj]eeIݺ)!IC޼ RcJcgGImӛ4o(wҬs@z4v4ne~Qu11t!8 >P6p|S05FS2|.>;umQ#飆fdqhҗyOӿPS8 _?ߓ7[_~c/zMu8{[ԚMeW-Ua]'? ix;-iy4`S!'zM}Ϟ3C>{N 퓹Uߒ&{}j쀵T6Y\&4|-<:s삿5If`Rd.s)O,</~~3ѯh6_M w\3]ԯK{?ʹ=ZcdvkӭCW}v\>ow/Om CK[~a{bF[0fvpL [:SvXvsj.0no ;T>VηU}6{Uv Nm{U{72=q792 +[21BOwC ? /ˣG 29>.x\78ofvC[CexdD{#cc٩\6L ww<7&swgɣEޠ/veVN] yK$C.;`lo.ڲ 0؛Lx% noDbx zm7u4w_z9lJⱠRI)RϦ8 Br^Z>X8B>#|aTXrdYܭzb/EE9ltre9>hd`|oCyv +8wRʅF +t2kR`~VNNNȞrg1ggtlY=1tAI|rRӶ6[l=Co5zYZr\hMMMd8%,~ A mrG`׷\PWwJ^y-Gк'/ |l?wip <|NT} +td}t }U\:Զ{+5|/ԓF ^ay hy9pwsy;gn ejR}v|smJ ~WR-mYl:wkd>|wW珯=]M_':5@- 88:8]lR_䆭WO͡l+`UnbYl娅lv?l/65+m[.Վ2NEߨm.vw4&Lcp y,`SF26 +9j> ž\ v- Io(4Cnej[]оF=YIh]ˠܾu l>^=} :Jxw6tOF;c-7}Kޟƾ/Od84:֢};>+>pp l(s~,b-IAS[A3$ޖ|!#bQBh᠞}ugU,M~ _rTK9gըR`o" vUh]hc9 R-éḌAr(/X٪s{͒^V5hlRv%kʷ*^gr~ndoe_<lQΎjٮB5:ܯϫ5y!g'Ԣv]9uphFέc̹_^8 ;k2&3?Wg^W}ZfӤ}?k<|=_?w_~WyX2u,l㢤0NOL6oW5 w9Z?oxerd3ZOvdufuߙs(.eW <д]ۜ&"gm8I?3xl=s>LjR m'3I&еC}npG&zq?=2Lݺ/}7nsk'1GaV{^x;gitLAO3y zzbT͋ȣ5Yz2%wemیI^E Vؔ`pS"~lWm ؽ"p@%p-*VTy%ɸ\ <ݒ`d[҉`i$? 2OIZ8Vg21eD C%J,s03N:,ip=q9Mc-ŭZEcu~ G_rZ?sRLgUVKYIN5Z11V +yC7v~|O}P6+yے<;.go*뺼>]9>0xI]\l<>{>xKK]}|x.!4{^h [zv57+-b\ }t7y.Fߓmߣe,w\|?ϝs#Ca4gPm"|}.LؠݘWR3s"4~&h͌sG;wFww{p1xzG/mS?u.=.xv&3v1p3z<~=`zn~- `^@n/|=yA\ȭQFvr =}G5pݺn::_#87Zgܞ+w.eY +вf2 +Z6]?tvc%9/m0xs[U\Km ֠J68^!ɧRx?ΆGk`_*H< +'QDs 0L[9T*dWhQRmI2ƹB$4`V9 +8'8oH&_іL;'5/x橴ycqGbrc| |ʱm47&w1;k^x%Sy|zq/eS[:q^w+3wK6!߶U{*[ ;Ƶqڙ>V; n9@O;ՙVۦoc#}p ~:ypVfdV{o n<Y7oM8s'-pW\$ݾx^/4/5:4=7#@BV7T>~"3NJ.-{мغ/9Ļul|bܑmߦll,u oI8kx'WY{yCG9p'*LVc+e6yD2쁁|t[(+;uoOR&USWWkK s [)^͵}D)s`iZWOjyUy~X\<AR-mP5ϹRDl:%tG +?-x&CQ"T( +[$[Z~ӝZA^+_hNu: +v}w7gO)/g?PB~1N[.+qrZJ`ZT[cjvծ1KVpm(kꗮߺ1Z;|c8;Xrƨ39Y '+`mZWgL3l8^Ǯ+;7;9s݌'vq69e2b7YOi)Al0m5b\`c̼"2lu(sm ڣM}yv/\`3Ƹh2U.s{~|Z0%Nmu['d|:~LURqptrbҜ>l}`wOFq>`@L2zKyޣCߑ_?mg~*×RޭwwCYpc2W9&l6(oej;oN֨K;&gTe..vww5c_sGFƉhNK7u d/*}uY2DY//Ӹd2+HZB ~؆cg姲Ke_|gorD^[u<<ᮏ| HGtzo3u=]{y7^x7O3Ia/#sڵ,&d9+W>//'3bôƖŨc9U,:=rW_zO+J5k] [z<]Zrk NͥvCiU1Ǝnd/55gLq\>K7 rkS7tl۷o;&edlHL{dlԭI_<X9jq0d,0x >ڡVPm8x>lCmvsx~b<푇sP"eeW!ו5DVWJ(Oq梅d1B ӏk-qlpdoLM%}ʲ/0K"o[-eqIZ)2n9,OvsrO +I/8迤KI\ ,15F|JȲ+⇦Mš#`89V9.50ݐ)Ous`qSk`ujphQ\K9kӜUd8/_[!c`QwkE{mYTV>"t_6QwGj#S5*UC߳esiqZZ|eF?@Q딤l<'G4[A ylO>L f0=}\OtXす╼x]y;r?j<4KxF ?9cȼ. c3(~5,;siL7ǓAuqSCE[ lO}ZӜi_XBf J$`~fj~όo3Y*xfQeny1xM:|~x'cO#1Z ?;}W <<׉Wc,}] \wvCgNܞwfot4?.W"{L]oÀXnC[;:|]|2R-L˥ѺhОw?׿%ݓ&xo +̺=./snu08Wjc?na8nO`ol?[\ѥu8lVO㿘;nh1sV'5]<`_ȝ{tL$|>UY fVꕑAgM~0vdXAf^ZMx]2cbt@)?!#}2;yW ){E֦DqļގrDzƦ,?^l.E%LI,wKTѼ/?oɳio!-4.2KI #a4 Y^{`]\R08?:Ħjz)X4ǧVJ&[mfP9 +j)f,5m:YQ󊟝6u)k>B5 +OFsؒyz6.8J;#z5/#&hL5n:SS`tZ/crxY0y1|ZYo[BMY\.{MնeQXo~F6[]}_KqC8w~|Tv94Bnmg_m~344:'b/_o3lʳ#75ח1ݜn4qίq +u҉~m$'x$4nXͅDJK7͊?lc_: T]hG'uW{SE|*oW'~r^4ilK\_.*6GV֏7uCe_wP> /v$}wVtj{X~k???;[Kl&GVk]N]GQ7s嬚kw4wM1$[ϧ+j2mKp֝2p126 mhgπxƴFG$=}6'O)64Y<'4-sxeܝ[o-MP[ >w{9'; 8m ט~Q蕅>Yz0.KOd}yA֖iպ+Ke Ϸ"kdy,=_s`+1&uW6IZ7ѸDT)?T-MC5O8m\l3qFhiLr6 ķ-ID}Cߢ2 CFt2V$ &8&&ƿڑ1趞+11b$؇~QiUS`hF[#*e?}Au RW+E9q^+,׋Z{ ?;flVE}$%56aa@_%ݵu6!^'])͙|5 T #QM8 + x.Ip2Nqy;3F7++i+Gq bϵ%ՊErc|tcx]~N2xs}| y g'x6qБUe>BynIzPa}8wv\;M9>3ygώ%k;Z/_3|Ͱ 8M37[ͺ yox]N4R0ARYؘ1~gGxL^jα/³KW|ןW"}k0ѫ5.^KY׻92/?_#qQd_)>Yg`Rn?LrXs-yrxtXwiQ mK>̅ei![ؼ/y9]f,24Q+<<æsΚ{tky!qN>OSJϔ2.q.Cc*玵VZk:[J`cB&0wlh^d6:_Ueb1Fh1ģ@8+qMj' ND"1bLkk>#vX~<,ZVcԏ[k8pz +][S^^-Φjd(hr-i 9i|NVVʆN_03Αf5_2w\|loz|e}4[5\1uWc1/zuy EC+Bÿyޒ=ZԿk\KE;W.s5YυqwԸi<$o +6cΧ3cJG]S{sҹ v!p'~D`eȜ*kUl8̚ˌͦ/:XhYh +~d-}=ѵKΜfK_ +Ĺ쵒h0O7 W" +;|Mġۓ)D#/S2g;G$Q__˿!}E_ ĺ~Z<,ߜO?oק{d{aY'>fW՜5FG-Գ tmV3S}rB3kEZ zj=)h:Ne/㷔ͼڮseiM[k;+oKk*zĞq4.݌fk[kj٭F-L|ZJ\ۨG󟨑{=v]׉q\hC\F9^Zσd뀌 50'e~ܿGfg 7 -˽) / 3sxVoAߑ% z1p_qUi!:jNMQQuvX`|]!0- +`3}v_;~y0۫Bk&wD5tu!A-͜#0gp*s)뢮_RJ8Vi*{|2{UGhwpmxPЧ +~{:h@}_/t&-4]gA|LY wԶʶ7!`Fb̧{QNԌ Cr I00 R]<(׫c8Wt)Eg9vrqƝr +l.7ql]i01 Z>U UP䰱>k;Y/O] +vE]?qg#גMϜx[o {L*#ajy`b{³E+_ƿEuXR;HȓOno_~3~F-8*U~yvayWKY'k0wf#J .uu-C5' 9i5nuz-XM9m_ݠ`l.FJ %ƴV^CsiyS0˯1Oך63]a[9QxY-f%9_dc\aIrХ3f oկi0}uj%$ثΝl<g>Qu;Z))>U)< cʵVf\sBw`p!L} -bN˜6 okU!6W=zQY"x1<\W96 ca>Ǡ57΅NQj2uY8lyvⲱ946c{"I0d<%ߔ/%a)c9S#oTy|_rSAsu>Za󉘏xbW%g/TTrKVs~P?>Z #Mr)NHC?|, W<}(l +0#篣%$ [0㢱,l8a9|Ga< qzZ+70gu^7 Ap? g/o%Gpj^2%I:AK'V15P--kr)(QCp|BAib r*;?νHkn 6X;BFqE]GBUc}PX#mooЦU'[5p,qi0 B˱)7~(*_>4`??PZM٠Da΍u礐mb +5TF}i<yGCF,f8WӚZèYmJPr}yhhzVkVML.[̷b]x_׵֏$L'r5XĘێ@xѨU;   B&@}a Cjbt>"O˿-]s/ +_7^Ouil~&}|&c}]{b䛘5M`7㣱x|C>+ۗlVI!{8Ś<ަU]?zW٦|ƶqVp%}n]jih^n2OFwr}?$5*cl> +Ik4u78 87s'Y=n3nI㫍}:PVw Kؤ[nݖf[gu{pm֦Uv^u{&yk&#=r{)OϾ%{F6}%w'eZ9׬Iy׾soE_)̄LMt{`~JdY]]uO|GBkk,+ .lm@=Is}k9-p4mV=DX"UcAyK+5z5IǍ'BZ{/j\p +:T\ha֞A3Ƌ5q8MiMKȰ6sFЗeh|!&jQXߐs5&kSt$uQDc^M_Y^?AI;~`8q4:.~p ]{dӛPeo ֻ_6L7$ n{We˿1?><-oe}cSxdA| +m@Ge~^u lko ؟Gyws6O ~.t{r)cbM-LC%Rº&h6~} JPMh%KK:pwKJM X|\&%y+t7u s_Yc"I19RkQx/B|*A8&bL1pq`:z\cqУׄߛΙO\J'm8!k8xZ9pاsrL>BБyg" +ME?  L?t{4؄qxol=GU2pG֒sxqk\S#uAC< ;k^y C7n`P^} vFVЉI&As^Z9혲7 ( 4󣲙[fN$W*5nk9κFKe%GA* Z[:]ռsMr,&gM`h"Ҽ_ ݟEԾDQqFC2S=Gds]mdk;{L [Ru,F= z׸vU.+^IVehCm_ͫ:F-js; +/~Y4ɨ=mVZgnVr)>QeLM`a7 +ָv6Xo-]v.rAOo6r5˪yf͋bn[c9l&տjX=Y|vػ~tv%_+so`Ϟu)yM zdhtBfgU<{dftVfTϾ_Z^OcF>XSqgoȝq}kHf4[#N~<;.E{>+ ڊ\hZط]˘u -#RDMYG771cY6U oa^˶ulyKAJ?ry/Z ·t/>C_rC/^/6ض'g7nGuk1#/쌃1q:I1ε_* G ~n=6}q͇NE>CzQѷ,벺+k|]G Ony81A.~'vmm6&Upzy]ǵ7З/g $mćo [A[?37 +;%{@A~<a,&u0470χbyתQprQk+58wXOH؇>M%5ʈO+_Y#L&sSB˜(cxh)GY>l挩fb}9s{a6axCo+}Wq/]v5zc;58&&/çy #C1۵A<ӵ?ox״s'/̨{%:I+zs2 9 FLecgnnyoF 6VQK첷c.סu=5=brN0Ѥ;eGm׀e2_kFH6lQt0;vtMngSq.{L~p_ ܽ' G hZ<}@;iۗr{"OV#2wFnssԟ|nc-M;(c 2?7/sYO=#wf?"O0îǸn` +.cLߖ@Qo#q $ 1Ζ27+ `l yӲ]2@H +LKF 6h1Ɓ1A k p:ʵ 'h .1 K@XhU5$NLJ9Z(DTF#c0&SJoJl/Ԧ;W*Q5z>0*L_?;y!/A♮.C7o=c{kGCn`wZv|"K+s| +$[绱xu)cw"[K`=Y}9jj/ݶ~<7zsC8x6Ё8&5CKke2doI}ƬcɚZP2Ys,i0%yYWrޘeDc1?QS3VDPԫ~ }3. +. +{)fs:M]QOI2eG 43O&խtqtKLnbގ|?˜gzD 8iv1ojˈ7uz>ǪCE]^\/)u ١K+Ig-M?"6S1P\om چ±NVwF}WUa u f+1}gMbB%%Yj}%va{s=\x98?xo柣7gG/|/R|z8ڧoTwM ~HՏW{k~m-AkQq}f ºcS=qW +lܰsez"F3ĈƔA_SU&M_gj[}Dlwہjdmߪa'ܲE5FuɎ}ڧߓg!I;Mo%윌O[k|ܪ=ݎi'>3ynjYC_'޳a'ɹ1qm0uWbM7}"Ko(!}6V5Y\ YZrڋϬk/n񸽲 lv3oM #${aK'IߦI5t9i1ȭ ܷX䳯{ɯs^O\.SѾ.6ϓ=d-pV9^7Ψo9GNX6KqWV-[{oۘEn7eQ.y\uWZrIG73gmR_No0?7ZS]7,>C?;8N]XX%MO*&3m*0tecQ-T=۞o]nk#[ 8'ss\o%Y^^ݭek} ^x>{C7^w# G%:Ubnp Gr6:\R.cc+jOP.6U +0^ְv7XUѼgj('nO[|l1Fp3iVj8[ |mUX-(JKOR!9Tր'8??|sH4KKe ㌢ŽO#y$I2|p\6I8AI'qD5wPDuTTHg8;E՚lofT6qTA-{kǩ6 ,b?Q:ϝN%i#VCjnv/GԁbF/A;|k2ۆ +/{߽UK} 5{9+5z7G-_Ɵxg /u\NKs ӻ;Zt_{Ʊs!竘d_ߦ^Eerbjk8L2m5=yy>8ggej^Yz:sf5r?!ONOwW*tVŲ(7ew|ZW_b pNRkܪ?Xbw<5aO+.NL++=3;+tnkOꜬͱ#[k+M^ߖMY۞5αϋ>:lm8domQ֗첹TU ?>ȁ;XkW )ؿ%>x־ḋ$ *X_DIF6}fvL]{q3dun\T;mOS<%3ǗgTϨg{xZXSfpsyU''c=W1~1ܘ[&q\R|.jkr쫢~=>^u\joxI}I.kmշ`/G;¿X67:{[K`gEg̨3F½S}+RC8>f͍&kjlfQYY9gT\wf߃biyE恣c2.X]ڜSwfd{.Ks-p>^YY]ƌw8֚x,m <zpEB!' +RX p.`e%gR57Ԅn֣x.'/-TZHT >bQepּrSu +HÖ;UVG+ZEՎ.QzۖFa?#^FiXY5c\tQLs> jzSԻ`-s@덆jF퇝2pJ >ڏH2&Tp<*'9W$ / I:E| +}IFw%ܒ 0;]Oζ%/?q91 n ^ +mSH9T>p..QDxa|>yqtl + qOjUWVi`OVM=1G̜/g9=U%i78 ڐғ2p{Ѽ5fOf=Ufn_qr󖞶Mq5ZjT)~,-{L/3/yLjuqhV;cߓ;L%Qjz.sX4scN+P]ȅcԀu8^IKNXu80}}ij2'k3lFO/^XܒP;z.k6& N<]p͵eٗ}%lX+sݔĈ-{wK8TMH(k~֣9 b;@܏ (f`jR*0𳨜hd'UGQm*?fޗ<0 vy25u:CS'g]y٥@ WqpP*pD܃/uh4k} 1m3p< F<٦DǜH:Z% / J%:[/CQ՞|/V'jgEM <$RCb ןZ͢44Q^<WϮq U^SSgNݞqiys^]PU ߋRz9o(Oѓs{_zk9>9}dwC~22O`2q!Ԕ^{JW =-m`kxq{uukt (ѠWDOgQ8Tf$|^n*/7VQHtá3y`8g>gGxNªX.+5z~5J3獏uL$RcPKRdge٬MbhNyȞ>5t Xi\fNhG%N L2Hݐ\3F"g2D</NfRnjq^}~DhkF_ .nݒIÇ}{"9c%8˅M̯r+#u˜;J}4稈5vT=RہNrSN zMߠ\k 9??vxGXK/D|[4#zC>=㉜7=6/} +{۪]eޥϽH>峟Eyy+_~#y5bj65_(ԕߤV>3@>ܙjBrߡK=;翚_~mP?>y9~,3+Y +Ly.kso|j|zh,1i[gڬSL=eq?gZpWv.kje}W՜\8{ojgf%Դ89c9jRMkJyl_'1>`sY&;xnU} ۹$cڣe1r{a9RcsW94 vʒ|4csz,9e<>Z[*۫3Lwnk{S{+X],_LXa b@NcZڛ| +xgPw~y bl6$%#*ܷQz]Z*g*[ɧr죍=!9`W8JJjzԤrLX7w* ߠ;3ViIz>.Tޛv3e J*f_[˲8,W1U +qH& lQ- + OD|u N?@8*F2 Im2DJ<H<+XR~jc!`0bppL!rS~w@2 Og uHqUx'q>13oI#Vpi: n>_Xe!9ouŜgKtTC]?^fIF^go\{ƉrK_ jR?wCythH#<(VU;E늿N{r~)^Vn_sf{WOݓoz{5ω[3S߰}YiQ՚VO+ 0Ӧz̪;e5/Tk=coօQg'3?̙1>Oi'an~V{&X#PmYKP߻Agz~]=^lԀVJpoz(Q}?yqLOeeu]K2 |]\%"wItuqA67g;Z\ZT_%疬m-KoK667U呝7{܋XwL8O)w=?iA^7zDLbWRiJVF+Y `fYa7:R*ӪL>zDGYS)(VÙFCsV;(F/uܩ& +9`gX_.ր3ʢQ3RI!Fd2*)T s~9 )f%WRq38׎K: I"JkljOT"€Hlž`u$N[T4-H؜$p;Ѻ@$\Hh[$*Qd|Guמz>\; 1800!O?# (kԔHgʒPryy>C1!~zju5]ů\yw3oV}a YoJ'F3)Q {/-=?g|no蹿y=fo]>rУLp`q5GOsc^XJoƖ&y^.伶ѷLQG2YљyizbԜRϥ3M=09̔7sm"#c^|ϞoQ,-}[S>橁y~dhs]UoQחegkێ,]Xwĵ.]ϻnt3χ]7*ب\Rv+Y)U/YMͦت UÂ\VHӟY]o 7բ{jڛU,f]p%pf|jt>]#`>o-M} ֛s^#n״gi#Ѩ$ #X"!a +=}#8&?/T\5Ѩ_qɈCnuS#*a> 4Ks>(y} SG4Suan~EԻ9MUl3>F<xqX|uzwp + 4ooʟ鿖_o-Ky{0`|rp`2S5c/iyqW: +WP +SpjGrjiv]RC*5C[)5^&֌)mR[Prx'+f9?Mf>sGa`/N/ɣGrq~=ӓs9=;W= F?nr$wߓ??RDٷFD>y>U]0Z77s|u{hZ5XS֛zMfz`묮Mb5'_/ԳӋ2=9!3vb,88$QosO/5߭ }n %RX0{O37<G 03|}|9gWBG;g,Yemա>0vQ6}eym'^k +^}<ܻϺ+6!ԕ>9=eAs%w#=ؚ5g*(6sr-˓G]yT?_wQz0X/61oN[WbRSc\V\6%R M eI>SV6T=mR7҆}؜ W&5Kg䷱H֋ZîRWUM?c?HH.BpyܐbC~`,҅xA?k9mhB<^|s5-ϕx1#LJ <<D{՟׽~Z*4q!&tvMͱrEk C.ۖoЩƀkVJT*dts3kK=I΁AMI>zyUfKOQz^Lvx!<.O.{t.gOsܳ:}(?wg =~"C9 =WGO|W޼y)a\jX|į>9g9VG]SS>f:ti=^T} ^F-o/#c =7r̍=Lk,ajpo H~zo9g漉{~=cWleK<4̚ +c71jbMk O+K3:D@-wgyjL:fL>?=karb IqqZ]yؽ$kx~=$N'9SdssN6םл[[ll暬s\ȃͅb=cMΧTcROjLke ٣T7'7)F&,7' gG2Cù&Fq3-XɆv۪jRC˙ߚs@Isd`Hz1ejLq$I?%_:ۨ;˅Hhs*]V fR5 :X(s&T|.)ZQ =W5sU[:y]džX?8Qɋ" Irv= ubg^Ͼ>r-{S>ؐ=n=^9`2E +bX({vM F(QH!# jhُ3p \fWeiXQY5ӫ _0ԷX,1pMFO,汩ROj~ +܈gb hڗgɳwWcy]դ>RSZi!GOG/>ǒVtGH^=9w^~_?/{=1G̾+L֞fk oɗzsW"M]M?‘{+Ҽͭ}o{-fؕ\q{/K&E}L䡿S-C;N{׺O 7Xկ!\w7߰٣}\[^lѾha7NcA9kdP؏i5&uN8{)+#h,o&gx̴}&f3}57m3}vz2Xe־i*Oڮm]-M1!8ڼ,j~R_w7emyM8kpyVðᐭC֗q{+8% +8{omJ#Kgm9U(\F{^4uI}å|R{~mc.xae%2H=R5Ԩb .`ۨWkZ)s?O j +8?㺜?DDR1G@=Ƹzd-җ>L2UI#+Iէ /Le+J0w<vC* aF2п#-zW[oQw??_w%U|xd\xo@<K|^k !M=sxmطkβ(PNFvG_DR)p~-lMIf8S7oZ3Q0p6PuNѐnމu&XR<-PA2$_iH?uBj9JE#vKɊ|T}ؓ>Gϴ|x +}F{|owMw%K)TR}1^t%)+aJNUԕ喜q^Կ|Ug5?eqUH/nm=>> Wfyh>=ѷ(-s>Q>eFo >URԎ}3:KMB3`ng觐SX8ꂱ;K k*>þdpkj|WM.~ɿ7_O~I('{ȗCxb½ufNnqmpOǧ;{CAY%%O#6s=. q\@zD!RL_qMD'U_u:9yXt\Ӻby .Y/Vǐ>RUiUR+LlQ\͠1I^Uz7MGɫW/7>D~ߗ\~|rjs0R1~:p3MxP(83yH.z"8>j̚aK}IX6mg=- 敿_^a0!{_]><~-rv {7cg-b(nc0[ط{Żl}NXԔy lu77woV JǦ&CVmQ3k>_~S_pyyܫanrgbWoVF^hߖS-K.occW^pdSS%H,TjpcZѠw<*(촫ڣ3 +lHHh/񷨚RG˨AJ QLe ԪgOpdzz֦y6u9=>Rgj|7jm'fuq`41?OI<h5lt}c7< WW띪a"՘*,uǛG O^->; xM^t/׭a#Wr_ݡ#\K旘V<77k׳"k>'0pZ{9kӦzҶ;p͓_]1kLf4>geY4&pe1'3cW5`?S{cmiIz8e~ax<'K\ nƺnз+[K:ﻱ$+ԈQ:;KuvGӮ!IƓVK_N2pt!dY`Fz>`x ^Os٠ւ9' -fm4|XrHjq㲎nuCJEuJ՛UjA~nzVfOkߚ,S30܌IkC=x/ +>9'b}%NFszZUߎ\hqH|~,rP*!@\S2)aH]nC:oݏ]3v2= #a\C,818)*_9/.>L]q +:GٛsSGcK}m^<מx# e>)|b{EGQS{% +,.q[ߓd&5$Goc"5RGPXyk#SѾQ4nz C̆ԣ>2o<9fvٸV33LX\juA e.^ˮ|[?%ߗ?PoT~ !gK#Z(j(拸#n=`ҷ:n&6jqI9`S_cueN1V YY'xU_ce {nd%_Ś-+KWxe8iObTV5.h7Ra>VnfQX8/as+>,`nXv<[.^`b( .u>oþ R2.kqoժsY 703 7bɀnoI3+@T<(ⅈ1R>AA ic!!g%sMrJ"޵,b\LF}!$8OY1JxNKxy7 ރʌJE;U.ɹ07zaz3Um+eD itXֺӋ' yX~_(??˿[9yyt|c8DH#{K!pXk߫M=?6 1o{_Ά5^ޗA o}V{{CW/X:xN}1voǭ=0˨s]__2v+\9Gu]f=Zz\}żN=Vp:vjQ&",N7+&H HLM(&Z21e8l~UY]zsԔk_RrT樟1# gݒM28C Xow%p(u?gL%º .tK]/^}A/ԐW2)z<{|)gg5yz֐˓tOoikN{3Z#Mӛ/m$UkZNF\>7R."U&_R-_ +sʹ/۔ө>o;uaoU$"Zu#qenz"rTLSP>LrkPc"NLJcr3lqމZZͬv;yWyaYd.뾑005,>ny$_;^硶d$S͍`(X[̀oWEH)'ǽisWB=~/>{njGH:.w׳*ol b_x%pZ7JE9?ج\I)';Oooo78ss 9wy޷ߛzix}Q\ @j6G'7 T7P ?עڶj+ؗ5֛!yjZF;>1jjQNn^.L 9QPҜ87jzԌf/}˼6u)Yc63cY-.3승}%<3c#Wygs_QkeyEWdeuRW;aiaN\eqy8`+w[3{\[qp@ x/SS!<ʓO<:?ӺYE{r[pb\{k%VK[U0smzѤueߨU'g\Pv"GXGj)mT`=sŊQo|^Fe*.Ӻ?/H7f'\TԉL'~))Rˣ +`W%V#kksDaZ}"o/W"uX_v!`C*liL}߽ k}n\`D^pU샳Կ4[&f! v7ާ8n 2IkN>I!޺vO{ը{U,PO<˹8έ_#Ep\&^րaV G)T$[H:W\:s`in@\ +/GwGZp`ĐS>9B|[ow~oʯ~*O^99jJ*8(KP} \~s3pe}2LK^:mE{nߛ;~G/=cr7?-?~g|չu}tҜ/7 o 8sdҚjre;>9~Kfܭ>ǞZΓvCirR-^w]s:psKP|d wʁ}'&U枹)#?OuOԺ$GpiVgdk{Fv].Z:esmV\*C+K[޻ڻ 1yY8Һ$aA^?mWʋgO_ʛ'Oݗρm}/Xs*sY9崝W|>u5(pج&͒r`zU"tFjFSX  Iչ'Bg~z+N5Ǚ^+Jt5`[=e몿Ɇ7)s`jcjh6Ϩ(fx|5nQc#ԇBX>1% 8pP_ J8ԭ<z}~S\zdmC|:Yqվt%$b߈\8$Cǰ/b;a' +YxV6%H5X~Og"9/&dLJ1Ś/ `Q6p>/oe 5\*yC{ߘYHIڰW^haOKkR5m]1⤶<{%Ow~~W~Ͼ-OI{&FK-KE _ E5>q\TMYd'7ղ#N{(8L~ώŋ)ͩ%l X;߂DǴV{ \mLclYawg%O1yޚ Z{kUΆy>Ͼs}ogݾ_\uܚO\a:r'U ͝K%GqYU'SU~z>SÆb|rT{&'u^h nuq ggAehLNL!u3n=y/{f=,Ծ/rdD ιeazt*!(K9؜ƚڽ!!׆śk Ǖepyq-i.냋]☬؀XݛHo;7gxq"(ˇwœg)ֱ#'朥JZi!0E>eu);$xNTuے)FUzaG>Rgo[bf[! lGEGRcٵjEg)c$dR0tj"Ul`+c^9?'_gꙘO%LbČDApp6䮑h8 EHܿ!} \գHow'Wal@^ٷG9qxpu84b04uJF% u[DAB8TT> M>IKRS8S[HݬL cP9fSl5]\ c<QQdI%sN=C}ԛǸ~)'gnT^~*}C%?wo˗3񯷺T%g=ǪU(4vS}!2HV-Zxm+|}g0mdn[ƛs_壩<$?;F#<7fyipc,ŅUyX\b4܃=L~ ~)>![ck:{7_e8~^ǝg337g>-(d~In=R6#(NM9MMi Q.L%N7_{̜qQf/{M)`󴩏=ibcQfp/:9O_W 'wU e˪s6{&KSl^U\]5`0zwo]I)r~ѕOZ-<-'U7^4,/k,F!r~fB}~ԯ(uira7)jD5kr-7x80jd}Ym3X)=,P!ϪO0=:cetcY (bd*WMp"op^u3 cg(+y\"-氏4}Sr~ؐ<{cH.]^;I6n:ZJ|>fK5Z q/gz]ڣvg +p|HE}"4:.ypD*DF=3z^yp:x{zԂnqu*GTW\%p|w|_>Ͻ#O#9::ou!N=\JhB2 K\g\ut\(g 0fF_R{c ~Ł96ΚLRCiشtc?'x]e_Z{}`{f,RU}w\9<2BM4b(ˮ}} /L>`&Q`$}gOWgTGZy->e%N/pts8%n׮x; 62>uˁ{_|./_B^`/N-^G1xg{],=.\ksaĂ{yOE%I}2Y1ߑ)Qmn:D$:߇ss B[D5X^|$ep xn6Gn>_Yl[X<+!p4t%MH ﶩޔcj8?S9>:ϟwޡV{7?O?Xky ^{,/Gxzt0L?h:P׼dd5^PP=^Qο'RI"nx7G{wn5'{s2m#Ԛ_epNլxǭpC<)ZĜf^^msoG.ܿ4>݋8w>RZWL}QV`˝s1VM!vE\99>ZW{XI}1k~Cʶ+frY_`)Kf;M>kmD=mSIsœNMŒl8eumMVڜm,{gMVesYV9˴"++8gz8-RXMyyْVDFVjTNj3C3XT뗹M;" H4tBq6U$!g)Wys(ǜ21sFU9iwO ,㢪)^35iݿ~ZnK־NCNغS`;Wsy쥼zR={Տ?OKyzL` +%Ɯ |g8GJ}FJ̱EIXRe#Z,¬ ~gRf$bnC/?w`=͝BfwF8ҝ?l+264X͞Zl:w2[{юnp\D+Ά nXV{㉛7~hktA15u65wN~| w8ȁ-f.N|0Џ6zZOWݶ 8+l59|7!_]@|@:"Ǹﯩ<9dث=Uؚ~΃+1-j]~Q;ACCx)vOH }ִ}Jkk+3.o?簿E Xč3/{{k}ǻelo.ϩVփ>Rz] I'uy>mUc,!'Mt7־ i`XJ=4*%՟L 3 +bM07o`Z2u\!嗙tkkP9"nOc:Tfod+ |pHH"ceƂ_@\2dZ҉za`.0;9k拣e Gp ]p}vs< g_0ւwﺽmv~zu&(^"ͫxp%lL[z@nXjWyþ basps'^{;9`_4891s!_~x~='b(@2U~r>GORKBMI\S)as޷^)?!uUTu=G\X?X:˪>w_=k_|~R>赼~u!/_{x=cϺe-9SrYj"b&vut= +){to YooSwn:M k}SiJE=w!-!ߗ?)箰ت?7zL)0N|@Tֻ^w>zG7|1_}ꙤHCc1 큞ߖ3U&^ef֡Dp\y{;һ9\usΩn[99'uRVH ! H +HLM `0x@Gqތxf֛Ykoխjc<񭮪ؿ} {KAr΅zr(½3LJцr#]'[榋 <41<8.dLP]ښB՜nn*XE\k+`iBS[!ek m٣6gBԉhKgege 󓽪,:8ۋeͨ7bb:DAye&(IN uTX?BSyn]Bq'޹hX:ٵO*SM#$$7KsO,x/Z‹eW"Z!ui/ο>r9hVK5iH{kN'>^ +x5,^ +#w^:KLK/uaq͘E*uQMpÌ.gup:^4rfLM +ct _vm䣻ু|#A~VޫAwo⼻ o~ٺ֏Ƨ?E'~]K_xg.Yo/E{z+fl\gs #:Gtff&}$$FiM¯ځQED>]"2s,RBxmW}s?~c}|eťzo,=F%\>Cv_z3(j5> +Ƿj߲ͬ.vAN{DϷl8jy^׺oZkNbdhu'wL,&᲋E{,af:[<7X˘KoeF摝 ;0jnGgEmո@%Wn;}<}O ΗxeW5UO+k D#%z +J=}qwc~ߩ)1$lzY#dVtzg^Mǰg}8~(.^8C]p;sgNpAߥy=,~1'+ՂNl1T^OeZش jnv-7[;uͽ}o.2;w{]6WF 7 _M͙^,-{+\?O~X9,*Fnn3Qo3ކ;Fv~ݟ1G犌ucoL=?jM~)GGs _DMnh%ާj" Wk59QXX,zY9ڳM\TJ,=Wŕ(!v +O+ +ڄG[_ + +_OqӰzPQM, sQX"¹TԢXsPYћ==:ik ,mh#?DWk9 sڨ-|Ugvplx' CWa ObnnqI՝<@.G~Pnv s=O&4J n%zIH&DPz<l،P z5% H N~ Y !ۡV!.?#3L~'yzH(::#/Z]5N8^:pZߖ:E+ ;9Mj u}a%vv2@+[mX4n4J~mJZWzXn34霮7~JcjPr\> >=wq' ݼF4.'Gz,#v<"6zǐ&p/1L ,D̃di>~{H~7E;gω?<;.~QyzYF02qGܤ^D1rt{>`Xz7˯ +'(9|tvJ[5̨WƼQtwoP\y9YS'ɹo_}?n߉[vv<=\=6m/iDkq_^ϵ6Az빉u]_475I?Lmy{]k)&'R>47ۉe|O) + +gһ%U“HԁKKQYY9bJj{Ggy?aT4ee(%_?SUO-FaN9r0V(BqF%Sވ6bqJJ\M俵5]m-jr`FKKZ J6;:(ݏ Obaykq~?}WK #8}yn=wc˫G8gtVXe3֝R`T_'>@;5VD"@,OrK[jn,z(zMR%j +kk +Qbp/>y)DC8 =d]6wKOU@$yJ=*E;cGO8}<p{>Ço$ƞߤhr %W,&5k&Fb;-T{Zg;(bpWd,sR2m o _=+o_sQ $2 ˑn'wGf9[Z;=?/忍WlC}|TIs2?3ł׺-\c^x2d#3x?<'10wv} 7@PSӄ +bxYi=D{%- +Y|#دKՕhF44o[\ "%/.C[x7OpKW 9R+״vr X^[^Fƒ%?s/.x}Wfѳ? bX}eB<U{KG6=5),auy K aUn8~t/Ο;w¹3 +McmeZK48y.5P'"ΆDUz%Lc9MZݚ1ֵo77o~\Efc7kh7f?U+c/?/|ħÛozOI|7?Μ>'skabzCE3IsH>/5UjrJu;կwd8@YWe7%zaH^,ѢC#b1ϒШhf%q;%΢Y<}=AwJ͗An(x.3)mVqo߫ejsg5<#eNdN~kmV^,fK;;L73+q]Z.IV]]44=frfIgl_8Ѩ5hOנڡ`;DgRt7j/aQ߅>膺#-8leLd#F~fK$Ky|슥ם37 )~Eba^c +G/uwk%{2g&Fz>o3a䘝"J;;3LNaEf}זp`/wnS-ex +'åGq!:A\^in|n"#$t+~Py-qv]br?Ss^j) x7L-2;;떸r >ƒƜgygxsuS^50rowWwy{|c~ɯ\ڈoWc柯 w1c*U6(6s֙߼lO.50<\^# K?Ybs}=q 5U \0ȇQSQN\D+h^UWW\iQI\XŸ>d:a@O=)p~y^7^Ͻa /pӸz\;{2ǦuSdzd"#ؠ zzB"^vݑ(1[}.ADXe4!f/iRAwC=s>sj:{/`iycâm*~XWބMb1Caf9}(X8-n}<߁d}Uo )Yױ>+z6'gO5Szh9ڳUZҀʊ:NKjDm]3:;X@}4W/GumLF}Q k%>+CC>ְx+ +&^ռJ$úN#pt0&Wu쐜s#q)^Qqv{d ;Mt)ȉlN⒟kVG!ݢ-ZR.ht-up9}]⩵NmooR?.S|[.]f=6>2,-Mcea_‰Skr|x+8r0g8+8rp KػՆvن|~}7{s7R_/z9;77?G}|Ec[J5Nr =ˬu<qKޗ_вoʽ_W‚%OX,#=2[g;PU0?"N&FUueP<.VlUA|!c:UtMm!X#ޓD?yh8y/"ފr\sY#trLc5HjlR }}uMJ5CS٩i\ᳶKOkpY?zPqM^8g0iOڃfhI  K`XE+-c*3R` yphXܷ;$=n7ŋk5ylzMfWpFQ\ձ{f-nA}-yUEm|QCl|6pVyقrYFM{k=[{`v}ʮrؒ_33ML@{_=D#[u%g^ W%#q)UinX+l!\L~\]Q[S)x/WWiqjG棓j#響D1FSm-kԟL { F!8[= +hBqxTK6UCWf;@+5p[a"5[kEs\A,|K_SgF_meIl'綷x -vnW)/{ժhc^f͊ 0v!~>_jo{F$_t(cZ տY^LɩCê,~#!G"vǣQ:-}1]"uԤ` 1̨֨022Ij"]|^>s1~'o%o-4z׫]mqZ).SR&: 5גXX$cvL7]C.Z5=f/]}2>x7g>oog[zl ^~Zk/D0[-BȨwac9ي7׮c6${,^bw[?j"jGgx "%jM5FQ0w[ɇ뫫kTs-UhikEsK+- KF xwUK?%bs33z2NYŝNSxމO7g5w/^s<>(y~_~d)1b< Vl]fxت^cvcVeӿ6z#~>[LxPVF";kݺmCk_n67[gv ]r'7Sv:9bԵ'p?{|cG_Ciu71-ڮ ڕGTR\6vze6);{]Rz +lbxr`5Jt^H_xRhj&~}FVJ<%g%64ח8OgJs)sjbU_+47]YE\Ÿo)1ulchW*Օy]_]v٦.ѥϵ6ttI/ -M\nk!\'zK<Α`D7x쑻ޅ^~ +Y>_~7G_/⋟(>Q{Ʊ1{zr >C!LL`uq >YzG KNZrLˇ)0NܑܥO{vejV!_1w&xr9BZIդsBZua'Ɗg^ /yhۥ~fbhuvX]һBjxsXg9Nl98ua1#p $.ůDm&zv}v`65_Dc/"ojmœfWpY,Čj1C[ yNȇEIgY꿳XY@s%9fD<6bcQM OsHZuSG2> ?1~_BzT+mbt|wK N8ft[!/>9Ƒ|8&F0>1qzQy~}io3#f84gԮ@oϔhy/}W7\Gy[.ݠk}qݎjSnwv݉G[gk|ni_ގsڣ)kԓ ,9;x~xo <$=uBl޸ƬhF1 -ОrWlpFK?V^%dDC{˨LF[Z^b6ViOTMU%kZj@L,CmqQgzkR.-NqUWMMu*G}+P_WȂM e5ʭ[},-d57Ƀ[ي&ѩlz_CN%'N]z#=íC)܉vˉx?Ѩz V.gGAn[揼2GjӜ֢‰EZ=A;!'Ϲ>x qlB4$ C1<$1h9//LlcxX8CpMLs)~v +}_`<?.]{ȉc h(+ܥ}>;?ԐMv٥y;{COnޖ?o?zao7<ڼm׋ޖo֚KvzJo?Og9jQ7|nٳsk`q { +P:lܬ ^{`\KwJ]\$+όpfN^CܭAjF= [@~\L^\E\'j+BRI^[:r⚪W䠩U5@\bbxiriseVeE.}k娨(pym)TVW+vߒ4YOn%ʍDguB%cjo%/@{Wz9x藞bxňp){*{x٧o?"q͇4<;\v)q#Xj<yykӞh rɸǝ/7^ogg+oY)kgw;1m߮ecMk1ovwӼ[gkQe]_9<9\3 gk~a?)cQޝ;ћɱj.SF\+yA͇g߳d;MmaQʖY(++#@QQX?Q@.Be9pY9 +E-WYƺJt6WU)V/W3wZfJZe +_.֞k .AIE=* 0>yi۷Ç׈]܅S|'>ƒ__~?#׿}'+|3O?kw߁/'N?Mjmǟ=ݏo7q\;ГD2է:+s"ב:dCf_P 3BAuXe5317c.?yA#Ӷ6xܖ&"FvCW?w=?k4d"6koM9ə^Z;TKS=9rX9n di=e!ϵl;EmG/^!={h;0E}(1~Z:%~Y}ϰ'ƒĶHcb<|uH{eN(ѫ:ʃQaNMq:} ;J%sآ_I$bmQna' }10waMaev +sؿwGV.c~vYYtRf iv,Et<1AH_#"/dOj9! E _( OO$乼icݵGU{!\7whZonwzG6 !߷-~y +Cx{#|f?gMWywL>{J~p''wCcO 䖅+fAYΖ^2sTJ|.3ɋ+ՏXы׹MM:[REY3E|-'B++Q^\GLCChxX¡%\c*G}U;nx<͵UKn)S#Z.҃ej:sg#l:݊w8:jUTO $d^,,b8~4Nフ~ӯ>I|S{g_{xIߍ~㣯Ͽa{y33šޅg}wN#onB|{c{ԏ04s #q#s!,ݭ:Op|C[tHjpʌkOp:mJ,X~rfGN+ypgT9n՜J cNyޕ p=ޯVX;auwnK(:N+Nq{ +S]0IN2;&/l^ykgx6c\c>(?p8V],D$7#uHHz%<"֖0;Or#ϋϻG5ce%=ax]@DsȒ:xEWݻɥϬ.nf0>}Y+cۤpC~Kco.l: \sv`Q~*of篌~~rxc;LQ^=/j؁K 7:^Yg_7 yy%FٌPT$٪-s2,Ϗ`̉L2pT-f4TV{K{!W= + +ɝKU'DfKK+QM(Z%BbchT`uE9Zj_+kG|o24UmjWנvr:XÝmhkF;cb^1 +5.-6sxCW>|27}_|Wğ|>KߋGcFG07+{&"10IS~% 2Kx5ߧh\j_w_r z 1~65*yjrZqs +_=:k$G`V@сk. ьS -vy ~CsNG24!U(o1&Nk#z|R1lj#/vˬԉeIT:#"1!`Ó^GqY9s޵8UeV08g1?:WCS%_~꬯BᄇG2# L-,L_B_ʏq VK_ +=<8ytLt+EO4"I# X\}19{&gNSطŹiL iyfb佳䕅X]&Yz ~_[E/TC0[+c2j>v0Ѓ ty,G_w3X םǙm:gw_3o;>Faڪ*7sC{wǫM~_ T?x_oFZkVn+*)lށ7{vxވo_#k>~s[ցZrҧ$zTSU1"/GYUs Z*$ה";cxsO<ke ZK(.EQ&fx8\=\G+Jʪ* (+AcC :@\רBT7U\MCaO ]L[ؠu: +noY8*a6w0u)kuLuτn:<9>90?K1wV ;݅{O?>,?G7?:/~?_|~ˏW/~Ƚw}X9Ire4$ҽ#ڛ+~Qr=GЗ  Im&bjg%9(בϓTs#qz2kvn+vbZ6b):FI<_ɗ5tz{L-b|Dx#;9ٯN~k{x,n=@Ȭzng6iY륷Kϒ?xOE/xlA*f8V•/ӟ|?_'*&߷{;8H1ҭ0a[\$tdt2gch_k#! _F_+SIg틑'O a{pDiG$r'K_W޾;\y2._8?U|~' pA(Ο9^~1Xk9U:q~b۪aAާbe MN|xu~g[>yGt+<ޜ!nwZe#{v]r_gfo3qf}tyFsm ʟ㺶K<˖1:6#2?+w-,X++Cm.nZ@֡ Tcf>(D1B_v >v ^F,#QSVҢlT3߃=- WufL5$ /q:7eUyi-:+PVbi[U[SA-_37Js}M.Z*Z_cWl!n0nhm.GGs6btj[Ԣkx-\ZXMzoz|?^xQ||?-߿'?sgooa˳@IrQb0SG^{}h#Ta bf|CA7I^ED:&ɉG!^bì$^w6W)n陮&v֐KUb%mȣu,mWsJo/|1Anɩ^2K/ﱛ\"1;!?sԨ<^;|o0ܪނ_Gt}A{066N⩧+o~3x]w_}\=W#{q2Vǰ@\[&gWp|c8B!,Lq060N<` =ߒk- 7QMxi|O4'Ÿ|cϽCqa~{ m淁s ז^YY2jzʑwg͘gR(PN.؛'[ +C$.ct,PЧ/|Aii~v GO xa1uŇ1ހU/Y.{'BAe$&բ}NZ`:lx7/‘:Mu3.G OR^?O>m&!s:gLxL^r}`5 &f6k_ԛN/U hT[djWiX^\Ƒ'w?}u?Y|ޅGwǹӇq>k+{w w8ﻌ?;Xđ1Ņi3C#g\1 eLt$&Hnѫ:2a\#)rh?cQ$Vqk_z _'?GqxwOԳځCLjFG151!=0+Vpc4k~P@|C B70 ONs CC-sQhB>4υX5?.ތ_mT w3ڟ3)뻿4ޠ{:5z ʸFn73-\m-;s܏]Gv;]w]*{]*+hEy'\[<Է֗#|N< Cl:_o@XZ"|=K{re3:lj !eyQ[U^=ˉH5 +:ހ&MwtThnij%o;;Xy"G +=n;4,3= =u r~sso<7_{Sx??ƛo| zU| O<]xKS^kXZ"h}qxxR~&SLID&a4)߳HNvvkkGQ!7ymvs s4w 1A+V@sE5W:͵ErWXziV}e@N̵/G4:\vTh\#<+,~!&⸬n1MKA>ށVmc?DxPD2" wÈ ]ʼn3cwA|w^+/O?qN5=$B8Xđ8w.9Kʝpp^ޯ%ܿYgza7>@}}zC5:uy|܌c۸apydڥ+|Nd8e,ak= ,ق59fjF}7oays3U4gnӷܳ~3>޽{-Ƿn=g0wr L>y9Y(mն\UC73^YF>;'_9@{]*PZsA| ɗ +og\"G!Y,㜋~FfmPQZL0oAQOU(:-ԋ+rPY%nZsW=jrPWWbpM ӭ5ZߖVP]z=7?okk[Kg%bFA{|&ŻeٞX;z"]91)711d2Zcs;?q.qGO|Ɨ>gއ|s 8yN8ږWȟrV}cbZGI#Q2L%uS4)>SqY&=y` +V14R %/L El-zvU:zu=puXC>6lzweڬ\7DL|쀃 +O(#ފ}(vy;r*[L>t._3zŏ(aWM>J3~nEaISX/Q}33 <_E^g{cOxxc.*W.^ ͨ1^9?{-.?ؾ}hK7=҃43mT JMZF5taׇB%Ki?2VwekK5LN>G}+ўu9񪖸:#OﳨO1[4<<&}ڥjB(ī0gN] ҇o:O c&x۝Y]o~}-[gFbŞ fm\l3N}wy;\Ҏu-ܞ[L ׍_|&e&1 ,܅o'^&^6yrsKOZPo-1>yebED~~ + +rP\Dbe=$˳̌{o"{}DzSYUY}W3RvER (HZ@ $ ?H?"2#3L,)~x0/^;\sΰ'tt>ώa~Sͣó%w]|||}|]z"<9.F'ȻL~616G<)r!bfc||\~ k O|+,^<\cfH((S02yU|cCvε-ē~_}{wȗ\ǝ=rCnP՗Ûo#k8:WrxE%-+C&M%([(V +8;<|^g/R9,&6ktY{L\]Y8`ysaxﺼW4]jSv|< +"J!&:QXYDZWCdfѤ䎥y8yȵi_z/=ےp  g\w<:8ii bsTI,}۬^?S|OOz~]ܼɸǟ=_|3<}njP)dc"^$+ө l(UuHNx$oIΥAjsU&EA4خo6wS8=奈R +bXK#9%J> ^D-:veM> /*9sk Z[[]8GgCB>3o.+O_?Wce~|y61*_sLmj70y:zǴʨq_Ͻ/*{AW:~9"cW>u1W8nKc7:Zϗnbhߺn9j +jYGaKMѿ{Ekܸ A1zzDKz:/S{13Վ9b FS1LMknZ7FF}f4BjSh?.bjXV}1L2œ} +^$yAQLOE>2xA(BW4Ri)(s897p#5֛x7k_ q~EgFDO>>wɉOq:]}\z}%YʡP akCܾ}7^ÛOoɶKd-$u L3W˺U5dD'W0̫\r9rmj !-yF[#[q _ztIf yNd&m_*y\w''U? #.bۮvˢǺĈYMEQ%P$/X)*ZU$xL7:~g? 3 =<{<'>ų GOW?z/?{uw3QF[L"!M9^na{wSUVWk^zѹ/Ifyl{|mJZooq67a$ zZ CGǒAXLRVi<[U[jiƥ6fth۹udCWDu?&6{o^Z_?0# k֨$j/ =z׊; .ޮW߫567>߂R+y_32#|kE<^5Oֶ.IDrޭ[`RK5!}Z}ZWhW?Nt@?zȑ^,+[نeΨk!vcǵkvvH05Սٹ^,Mj뜇߳%3ȏG1F;0؊1p;uek,燱H޻8ZX60˪9)3vXWo'163ǧZhT5_G"!\!>X+sײoE{֪u`pCw!oyZ ⷬ6׊Wj!x&@1T(%$9O/#y[OxCGO᳏#LLizjd=XXROs*cuu0?;~ 3p,sehT9uyE{u 6OEtq}a\I<{!!xɃBĥhlD&6[*ilpm\v ;8B&hprA.GEt(Gw#ʣXȣB/wwէWGN#?Z !Mt{l]'Svq4[,Xx#L^ș66q|\@Lr}|wȹF,">밉`-vobf-^Ũ@d] "a#"ACC*jMeeI>5&f'#e$)D'~|~‡>goy?ϟ=?ħ/?Nڣ#E[q9=ު`o;JR%7*1<1_W,Dn 129mx#Ϫ'C󛉃>&B9­,\+(\T1ZfsQBkx}vxn<2&'OJ٣`/ms(~.;NdZ xKk𨆸<.}Yy+]2˽+ JoY.IWP^]^5ËY=\?kb4xVsYQLm:4T&xNcxāuKMMU ~y*lsjmsQ=6Z-E]u;e_qyzegZλ[ e~]ᆪ{@uTC|z ,*拦;lQďX4zz.gX|لnƜSvt&u?cU,&vcyn3kg$4~>N>83^3#Xein/DZm}}< xݭ\2?gynۼ@VیMKԾ(yZ _(LΩ︬ct|u[\Cs+e$EEV9b<6D>[.~Tr軷}>u6nk<w6_|o^'oo# o #慸ǣiȩEͫNE.O@jz+\oc6)ݛk}T.zD,AKr}'fl\ncb%D>'M\XXG2j!~̧ խ&kx72cK( O 7J[*% x*(f}#r}611Sg?g_o>/?x>~w#'?w|ݳ'ķۚ~c^cSbb.JׂʱG}*x#!hes9 &yxNֈ[f2cb +>aN9B1gpX'l܎4cGiGݢ0^pb>_+_@PmZ_`gk̨T 'vk]@{~tv&(m𐜇!cW3_ڦZ_Z6coS+e>{?.W50p\_">+ݤTzeoO'3~Ɣnwah XʬjG-Mui]D:*]{qɉM&L`yz[KNևA,,MW٬SU<_̪$3 u$>r{zk_}gveun(+uG)v$ZޏfQT*Lz[ rNJ3_Lہ|ƍ27+1"“ +~" f/q 2wszuv$H.Vp|X~u{/C~$^BJ@7.iX\6^zf>X%J.hCn[SBTDqw>g|>H|+|Շg3O[_~_|~wO1>"'~￳M yd<%ރ"F1I (x/Bl=.egevvNYѐ&fFd&x.'=Bn FU3Rjaa:x.|m0KѰW ;t8MQr`Qq'K_]=]N?l%2i2S&+sZ:'/{<`gUpW+{q 7ʚ.yy>l?fvδ"▗fk[:οKۛ \Ջ[>o[SΙ~qB}{WVf|#j>ߟᯩ<^Z3jQm\?Zb#i2yohw^ZssZk}TKUje(zCݪ ӁtaߙN@j>czZ4:12ڃ. brW=ope|xzS#uV=_97=50ab,9,k3#k:bV5>n-5fy$1xS:"䖢Obf<؋l"F"/z\)kv1׾s=T2s\KH#$Y伙 +Œ^vNgT;]\\x⡷Y nl=OLŵU<ÌQ!7 R{Ǫ |ۚ]d[5 +*`\QϼN?`nOHq|[侗?w#.y[,Re&robwOg_?۸~7>wuN'ݪe&ޙ eRAxˬQ4"^MSY^gK{]N/9X+ra^_1|݌7/#Αס̙yݳ/u^𷦫X/5ߋv}fKx m$m뒺o>,~᫨)3M e5 oƪܶx;KN76>ߣ[=10؅)7$nG_w+śaC#G'QY}o/Ÿ{So% 5_?;_/͗OGS>zyw_[qUdvb1`<&6F8'~1x|<7-S''7un 161t2gE~^+_$kDY6PݭXHocab'xTJe޸PgY]֖(*Ru;3{ڍhكUKqކs\[W'㋏_f[`+ٶFt ϯ}; ƕ>7+!ߺꉆDxl1X$o,O'(~KE++9G7tM{uXU\2#/=g9FhۣZ#z%󋃿^~WIޏ\gm=zzca/*Gj/֧9v[Er]AtwGqz\:z[?qqLal0`}esӘn;;ebh ,/aqvH1j|3D읙R]u3,+k#0뮬ϫpՅEx)r\R??kCA׸1+I.K ($YJ`wo|[[[\ljf2q+F}8'!DH=ȧ#Dv;Ir Ym[OG_!D D0kf)<G?#!skzu(\4"2?wwpcmlu4Jl & 9ܾE&gޖy{'ܻ;y;7OK^qd{x(2#Ƴ~~;=&5#Tp]bhNm3^ZoRgES8g\4@:&sTa1Ň/F~,@!:/D˅U yě˺m9XY&IeM=^GKU:M.!xDE17A4ZrZ!&MbfnS3ݘT-^, ߝG&01gfI^['.ayek\[VɅe&ukUEA">f^7BfK>o(f%N\w(vp}|ɇxQ(m" ^٩ j+g+ih2r1r70j7OCl!M#6Uj(n\65)Ir| H1̊b ):^{1&><³gk_G3w8)R.dx "Hgd`\Z5HDcRȜy+U}>FknY2tf:5do>ſo 9&/~ ~)uܟ\Z~W9u"!L1&υ9T#t2*jV|OK`' _*@Lϔ<8kuHtclxi̿%jPsu9}@/:}?>V{_W tFZ!}~-糃&ǂ\'5C9߫F"!a/^/OTڢ܍ѱa,.\+DG?U;--k%=?nG]+zF^Y6Mjkvsik(ni6t[MUM6j9t'oj3]>ߏbG`tլKw _\nAWj}uSS[$qyx;qrdLLv`rx +c#src9,ȕW$Umasrj$rF"5f5n[PolNw7ĭ۷p8vDzh<0랬3\$ZK2{*8&AwE*N^zD@ӵ3"r ypb6xޱwJبqrp@$&ZI!=-vȅ&o=y|O?zQ,Q)HY88oWA{=.):x`'wU&:m6W޸8Txwqœgٯu7 ,Ya11kF*E6eۓAƉUß%Odgk޶ -ø"¸ԮZBzLsg5zmm=44s8 uO^DV'B#ů O5|nƜBzWij9j>ˈ|Kczfɝt}mn˘s߬v>+vN/G ޵4znWu^Q?vϋ⛺/<ȕg111>^#.^B|vQ\owCwuc鏝*-ZV|hm2z׫bEJ uNIpUߛgG2£dҙvȬZW =F9VlMt.;?k=c|WX[sJ05%zX7ݡ0:ڏ^Mvbb3S] Y&O`zyKKS2nUr^r`VZ_)duBkgO?m|ܾN+#6\Jگ,[[p&qz:nF9ߊtCQMy?OeRgỴRCP& +Fmo筧pBY-bM}775 QL'Q~2 '`2zPSePD%F1CO,Sw{'7ptP"2Ex'r\wɯco'"'O=`yA1T%^Ԉի7hQ{%O<-Ē +I>+X!+Fy8:5bu5z/7X%Cu/ MML4;Y+ +Vnۉ,-tw&xm[QkVȨhǤ>MP]+yi/96w Kd xǹx:^v|SX nƁfBy#Dke!~g#yV;?3^>WKxnjus%~?Ϳy>go1#m14{uEϡW&N5]GkejX1^UZU^-FdF0=#1ɱVɻơ-5mx܍0Zۨin^&fF^Yt5ut:˟7SZrD ЎZ*6@yh?wgwTy !ՂNcѽ$>[< ;%Gݩud9[>{zȃ%<9 `xhPWpwh8gC %'3ˣzlSðü2X_2jNyn,kX_Qo\xEĞ-)w^W+}EGMP=8^/qسN$=9+׸aȁ|v^nD $G\VR&#HuPW}EoT`S|(u/>y?SbC>FL:@69h" +&NZ~N!+=T1r`ΒnLw6حgoqm8Yk<{M1XzN*bnĭ]<&׺wcɃw"NIzTi#f՚&B6r(vGˢ<`nX7RD>F/zHVg~oieoD>=+#B<~pζiEBQ-/GOl^\!7cӼ2$Y_{!/)/}>o!(XxQ?v2⪁J:x.ߋAju΃J聋7ͩf6W(-+ {//\W߆\mnjg 9W0W1*6/9W__yhbY: 9tj 7u[8w^krEK7w)F4W{_xz3;μ݃AL~<${17bÝ_\POv_:7lvv5id{~~jӮ?~ hdd2rRS*mmoךm[{W\Us-|e{_#ǝMrӊ uaV'0?JUcYNL,܏uY;lU?),ҜT'׺2KˋZɟa^~d.YrцÓG{t==Ɠ{GN>ظny>SqU0rE<(UAL/?3jG9WHȭ_{j1MLp(r^< LN1UMAX[0>̪ j۶|老zg"}/`g%3Dq;{7GN-3<ޠ̩')! +6=jW0UhxȅynS>jmbǚYA] Ѷu+c o˽05ٖ|mu\Կ|%m6xR]fF: pۅ;_֚kkXWݦj-9lIV'[Mz֠Zg_==;*|^V-?.yNk1h}݄vXk3Ɉ?0L-5 M 7k<!)78;G{n1WWE^_#w}{$-ǭ['H; O@&c.t#XB*ah+H;JoE:,kj\韖mQz#|[8=26/*\VS~r<>K$UyQK$.JOIz|P$uDu'o( +x;oT>1m2~!_)]T(ǠxTHd },,rbԬe.MnhɨG&by2"i$O†`n@NuJY=,|t<^Xt{1ſ]^/nӞz#InK D1E. y<@qED#+$|o( 1D7hDÂ[=ZdzM'NVVb_-@l΋f'cyHOg: +/%|({liGxYW24}~5g}<>ƕlSͧ/@i|$/`[{.#߭{g~E _zy]ys._5}mFL,yA -x߭9`nYӨ=/JdcSӻװ6dc6],v-Qε0|hQ_8mi]&->vO ֊Dj|xgC+;z-RߕmSzI,2cy;ǥ3xw>$*X'ޒam[Ue>iivK+#֚6a.3O^-hJZ76E Xs.ᘂ5I.[%r{S8f2s1 +7y/䇟F`u&*&#JhЮ}Q%$T6〘x>>y \[䔉0 02eXK~#J޵}(1\k1V9r15e"NfUߧ=b_j[s{H丛vErg!Z.m!_ުMY+A(Oyi99cra#1PFr."51MܑF[}ĸpDzwΏdЅ0cxz'3]@7E;EGkc}fhxLt`M'Vgs͚oqyLjsXeb5c)aF']a| ^0+ +eqjg(D,kmZslX=DG0kןxǠ2 h7)3J۫.+aqM8y%ƻ#qp藿\! +ל*|Q.lͯ%{}멗|/y Ϗl͆e+\,"*ú^wv+/\˚WbE{/azC]K[ߋg胉c/׉ՙf65ikh_Zi'ZۉG& : 2?7U &}U-7RsgYMZz\eyUh.u1fG cnmF{W3T|k'cCAN%5^=V %o{{yk>d^_ R1b@;9y' O-#plv7f'{ЯkX#Z{Ҵra7+k5vg2Axd%I.[n+3 6fh˚kf8XEumin%v.k !!D"8A˔~U7>"u⎟NQv ՞Y7i%! ! lH!b 4+3EU ->O[,1Gw69r8J~w)2Z_&bAJ3D9>qs !#%[ +* +hF!1)ҟW@縏Tćԓcܯh<jQqyVxlV-0}ijxJ]a$f]k$W{\e!=.2%~Ekv7$!o>"v_WxdqR8R,ϋGďo+gU)tV':ȡ*|Y|. ?=hܧbYS. v׼;o{Bg߲V+o ^֏saiyκuW |%35|G΁4}|lZl˃V=Wp^^o|G#KN㮺yҋ/W1ijiI\ʽ_=4~/T :ZϠxIKn84Lh2{é$@}kyK͵{|ԮA1CT>S<[:Xix:HZWz115K;D,n'vk붷Ro62cܩ3y]= q}3c]P{zxx*ܓ8l<&g'Jk^2vak+ .gc|eu KX[1XrNՉSZ" ~,X_[cW`3Ƚ:$څf792S'/8l+=8;N BJF]\qSʐуks6nnVc\@|1/ )X" fL۴Zr" ",Jµ/[K'җ]gq߹TRTPoʧS-8Gt.5Fdri#ߗLS"j!f RՕNQLQHX>JNl Zܭ('Y^yo:7H\  WOˆJIK(I7g0&)GZF&ɜvR_$ g10&ZϾ5ج+5xi/Wq5W!_$DZfRJ1yP y1H\t\1BZ3NexċpǴDN/񂗸še} 7ooaɽEHZ72oWgHwk4z^1\ +ekk}M g}Lk׸& 9]K_ܮOKE=و^:ކe 6Sh:Wj5jFڕ8Cq{i"n:C# 3ʧcmm\Ѫ!+Gn5<՝Mn_?gk%cYڤ?$xR}ܜ nb oS+=\%l}@+h9h$.v)[1?C9A wubtj +C#}"7[0x3s(zS FBq{$OO܉1qwqf^9nʵxW,6uEK]ºK :&2''{9b823A±?H)TP*qS;;{۪sqp\V" JbN9> Y%ϲ\C|[bhc0qԋXīQ3s{'6qqz =_- GCךjp"N^&f񣐉x0">ކ]'v顚&xE~a֪/았z7ʿ>5/\F}^z^q%~coauErZ_*dsL"!$ %L&Tbd2mȧYTH\YnxwxH6 +(oW3\S^&o* +Wri OVpW| uі`OfolV -T5R)"U ! &nSı䵉Kʼnx4Hnʿ,N\%}nbN4;;a 1t<8*|r8/שs] i>&xM<xk.:vQCs|4x1>cJK!<Mݖ^3@.`w[W9[g(y;K%fOF.m~\n'lRp"ץ8}.(E,qVx[)'Rcg|.wg[G %GE3+3GkvX37>0,ymRNh[ 궦՗WgU~|fJn']Ks/]:n?unfG\4Q%k58 귍Ɔ]>2cel/_=3zلn;Sg;t]m00.e.c|[9pey#W}GKՅ=uV_\i[ hP˿״SjydchΆtuGgMtIQNoMkr~t h撻6ukO`o7qݽ=z1D'O1 gucfr#z;7=EaC~/c]R/iȋ<+ZyeKX3/0N],C WpU_?䟱1 +ȖR%pH>xՏVq:zMn])_w76m1&or@|wuE#l +QR$ WTmHG-rJ)I_IBxU\ $[E SIb_WzR!|O1,Jn #hy՗/Dz{ 'Q'bQ8q, zi=fUmo;lz \+/_ә`m-k5+ګoaC{Vmp^__~ې{]{^5SsxtQp F7i1`fTksS]7r_8qƤsv'-L`wŘ-5~ܤf[՗U>f޶V#RvYEc@"+=n^a2z%fmSbbx6ޭmaWJw{ԖU + @`u:ȕ;|M\wcqo LJ0grlXl]x^2yy+õ01439<2~bJc^˵PqG{ js:NnhE"u>jS͉9cGJok3XSPIc-?:k?P-oMzk&.k<!nSM Ƀw7#|K s]pҙ$ 9{ց|MHPH8T7Jw;WU!ZL/8ԯ2grrUv7鍇%Wfv6p6=][~W}^ѓ^1)=jd\=O:,3~'_c3Zj:@.ף.kj#||݈5|ϛZƾ;2?zbvosjcn y|SKS{|W:|"g:';2v=]sѺN,-uir~8VGiR&K[pN=ӜpKSKhruܼ I/rd1smnkFmN͘5?OT>z-mu~M:Ԏ^rnI^ [͇7`P7dщvGKw薭@h(1τ6 NY{W枞1r)=A soE`0>9QN``jfK3  9;0c!w29|/1yx,cQWup T! C>'KFrM*tgTNJ5I[7p|@qw8znp$׷pL s a#ONu\}\Mv&omr`Ѭ{y|!́|1j+M/#]Qol<4D9dB||^SYՉq$`d>F bO59O.9oc'Z) rBy21^g+ˬΪ?h*Q0Hħ=]3Q@'{EW\pb.>mLq?Y>oË~gQy#ó~+VAW0T-xti[PG$Ǧ6Ʀ106aBm_5\PWAA]W/KTB,xyvpν/qRĐr:wz177b@3nS.Q/v4]闒xhU)g^I~߭K;kj3Ts?`>I<}˭x:tw _یެ>t.,ĥ5d>J>%OK',;pk3O* s_kOgo/K?{f_8{evh#ܶR01=JE'[݉)A&qsa4(Grer4yaX"|F4ڿT2#9F.4A4=c[[:SX75|7m5MwQHQ"J}I~%{uګvmۉ3e܉{ǁ1dA&||_r(6 Jn=966t5nn }E~zJ\xEb[+.RKN%,̾V?m.KO[|66p>\-VTCj\ p#I*T,) ,XߙgfC 6~6I\"-7֤my1EZY~"m*.{Mz}}S:Csg"ߠ'TZ+M6;irJ 0E8MI/tz)S,ndF c)z [˲VRUϚMѼ89UDd-bg!< kO/cz|km:?ޤ w4w}pGfU+3I w{mWŪ`ok{yFfX"*0s9z"}W_54ӥKWQga6 ~Np0U{^w/Hs=W/ߛ{Iw0|[f4hzxk_~A?ON>kGn`1laKkywkN \[Yw&t[\"VZhٷ~z]7ח&WV{.-~HȚL +-X㳦*+5jW%c\*ߢĜyMLW\~)B..D >UJ)47CK-j<ޖ_g];҇p}I.Ê50-Z)EV\5 + +>(rC1U,^kzr߲V`(4aR wUfڝt-=U׏w/y/'+9lg/柯Ʋ^,/2TJj8f$5#)䳒3(WYls1G{qmi)=W۫/xtq#8(}K~ TrdK 泟D!^ /\,tGu3\$vUMx-9l~Y?WY2_o§y]c6LᰟvjnٮMu}~j#EoO*+}5]x9\}n(mқ$`x3(ZX&ɥpΖb}||I,zIdܵM@-ueOw~Fӯ_{M{uMS lN% ^N][ikk[t +|rJ;Kq\ +<[t}{r;H0Dfs+P +[ɳFA4뜷^We819*RK^</(j ߫PwK95;\\-R!BM6aYj#/^ j +h_qSx8`5X-`oWjKoFK`}33kz%`k&1NP6>y9+d\ +ϊJ2w {V"8vg5:D;`У=8ݐ:.mcq؛KT,g)` c,XAb=_G6| Ecp~OeY7ۋ223cPJ>X'>1oE47;E4?۩S-u\ψnV ~%*p-͋奬ho/&jKj~Oo?O/gߧO>}A/N8gvC:.߽%}g=Uwζ]/+Kxsu_,{ݱ"+X>0'$uZƣZ +?LQ{x¹xw%|kTq=v[2gͧ>,+6jV.?2Gy~e/ITDLq\88nV{-M+KЖ^v5h +vV٫୵Y4ϜUg%STVeͫ*LqY=4fô)2m:98ݡ}~xEw>*] +7c*≔cLH +9gˣdTu9Gmu[gU7(F3E{?^{껱шNgR +oVWT%>Q-_;ƃYR18q;/klU/'DZk'\ qZ;e''Ds ݊8 lz49i}ץn}]=VIzjQ1Vb.8+|cP}fd x~>1'?Es)\f/)} ztX{<>c_YFMWu5G1Rewԇq ^z22ď:n(k+s{.{*~.H 2^JWZNV8;{6Qp$8`XNw_P~HO7fػ#BR摄bLA=|N?Y,axd!Dny^E|#3sQx +˜utH/h5_ ٟ|&IPt-7 a7h.:qz:Z6 r{8iO< Y.1={9~ASG7쌶)\6=}r.3Ύ kOwiwmVyɷapY*ge +uykC.6S؍XZ#|q|GR1T1_ +<4ѳ.xߝc:_3OQs= $]&z1p{m.=󴚖4/˱.q?7l6 +xp,yjsUe8bl+$gx~5Yq%~rq -QUez}[컈^~@oy_vt<:}-2ɄqIKF3>["q R#chGa?>܈_7o?y勯MМj& P5gxҨˏOq*vUp\3?UoW~yOm=n&=y{zʸoݜ=r#4]+unΠ\m塽cdw&4WnlN DHVN#߰tfpi2&{tH: >v La; \H,,iբ}>-z :ƺhAWem9ܦ5Zf|ܫ‡ZYC\^DuK9nre։nUwX)>_T(AmS}5 j5c{[t臿uw?>x@o~A#޸sfKr'<;w":em2Qd+$1x(-fpB{g u2o{kZ\GVz,#||sʚ6Mʷ[w>zyG݈O0Psooǁz=r]~Q=q5t|!{0i`,rFJVbޮnOVHs.& +aw3K=b,r}MFh3*zJ4XX.K.g5.*8:k׿hwfX4>pQ~V}A81Y:5z:~<;js zcwGسDF̾v:43ύ}]}&fиo&|ā =%3F +N9(L㽓1}Q?EX2[^)잍(|7!VX]ߥ:;qp7蛟KO!f8ޖ`oM;Ͷc/јtrW61Ĕ 2+7 K ټ.i]E#$)}l|\w x ,;9N2Xa8*~Nֆz6 +,y_\\6p`bu47FNtgNh:4F JX_qtیw]e%#kx] + 5?޵1/VW+x:prkoߧ}?~D?yA}7N?[~{M:<8%~pMO/طC{tށh':o?gϞ>m.h>_eʕX8'TW-Dg\PB p91Wa,L\Lڒ6qy!3|l1ϵ,~N( pS>Y4鰶~{J/^zFg%|Iãlokؗ-UA:3818"^̯83^x {e7Z5]c\3# \C\s_-"c-S~g[:@I+R >>^=~FcYxO]Eب(#.bCIf~6D3<3ppSA oMMOS$F_p8ka׊7(_%w o5ogwr@ypn_w{5ʼjkܚ+weZ/^+k>OA}Ft {>U|MG=j`U0ߠ g]7:_W#RC1t=d6;d~52Gkx=]Uk Mtvպ1o9Nu/?r +w5[< sY>O&E^zmV8I8/?Z&6q-ۍ_3@v\.y\FrX.z|Z p #gУ'!>$/ljJ%_+QQ Y-x6s+FGڧt~IF5,~vQюvz?xoz߼J?ZK"EMonDr'y 8'9J^7_vݞ+[KSf3ʯGU˜A/|-5ɾm7 q2ۤ3.9$2\֨lՑlW|6ۨ䣝N3ΏqKn%׍Xd8qdx%I1y'ݢWcc㦉q-S||^,JRD|| MO)wu6X02S5QM#ы7&Uhi-OU)Zg*ww:/f\V1V:l>SSOӻݣOxx Q5xr;%E]6:ep;}X|{^G_wvS< +Ж%%uELi#:K-`G?]zszoD'MK׿?^?!&Xf![K7V۴Ԣ*0.#@Q1#c(o?["ʳg| +\ϧUPYJu85~+㙳Zv buZ[}~ӛo% +L~bEvgYJaOg WQJħ(10!b  )\oyoW}W[~5ta^݈}k,y.Isu}|h~um/G4=νQ2[ݤEڗn_FhÞWMɯ^ #j۴ + 7\c4ګ\SXzf&Nѹ Ϸq>]xh5ך`>FDkf`xFfo늞zm ·QoڠskܤSz.c~4*}`kUS<V^ z_*3^;sQ. z$.v8hG2١ x +(:ѬcoBipH>z +!Z7,~Kb͛ÚϓM.yL̂{F)\ XkD&OX=iT4k x -RR! +l?/~^_Q=?>=:>Z?8?Cҳy.ا*ޜhpW`z\v3z ';zF?~@xC_t.\&MdRiE#.*L)'([ j7֡у{w${?!ݿ['4 .KŸmח[۴XKn,.R M2$!U}DبOc?{,[5|Ќ٥XsaiI;t3:DܴXkM8x;Wk .C2CP454m>d)ݚpFw{(<={-cIlm!?0w޾korO_%}U?6ep!;0?{"֏\Mn>߫?"sWJN^?WeF];1qWQA6+LV-F қśl +sz\vc~082?R>σ9]4 zz|XMNX&&)P0{bx)#\cQ:\e*I8-؇63>̕86Qu/+z 13x/#- 3T+M3_=Jthshoou;҇uvMtrgW4%3o~J~7}g[}>x?9^>Qg-\րZ/H.L,b<0jYjP419!&8Y_D Q`~?T 2t=2)2\/Ykzm/4 OIj!Z[YN㺭1uku-;x?Fγͳ%y ߠ篿N|L\`F>q\`$Sʜyh ?$k)b_Zqsr 'ͽvđffk~__QݠVP}IykOYrAگanA_^{|=~2Fn~#oh<}9Y5QJxK+OߢG ߸q.B>'L&}ao+BS.n&2(xMR|El% +i(HQjnםG6KJku2Պ)`nyp +7is\a\'G=zH{k:_^={~_ӿх ŧOw0cz <{ߘ.Z ^Tjiex9_B;CJ!(n׋Kآݽ?rWǷ}ߵU|˰qPn6<~~wRC)nq(!ZPf~@|~^ڝV{h5jX46e[5V08ZX3Ugz.痵~~܅k͹g?Mǚt3z?;dt$?.}7j|؏Ųu|%ԼS&ld^KP;-jlݬ/֫Qݜ5iltn뭢Kzfqq$>.fE!M4M.S4-& "k:Sc a]:1kKYM,a3“Y'48rfas1u;IYHg)` 8f؟Rs=Cf:/j I'{>xqكMџ|S;txLzӿ? Ѵ/7o}|yAw?ŕ5j-- Qr>"N,{2" \<"; m+uAz9%G<úM.`ۃpō{}oE.#5Ёc^+bG;-06;b^ 節kW߯mק⯦^k3Uu '/k#=RfA|ox`67ޤ?yL~No*;|B/yJTbr8\<|F_|3z3pf2^>}|D??~OFgOڢ +xzXXD<pLP$% +tN6IXg͈<'nuƹI7\Q~*Ď[WkeW^#spL nK.j{-z_{ UWs}a;0Z.2e;;25׳{ṙ_;>m^ z"-COt^kFEL8ޭk/W9˺tw51z]+2+}] >^Pz~2:ʖzQ6溬Ux<`Ө䢝v/EX&l4p{l`liZ)xmkp`Ur_7qbM&sޙo< +Δ)C 6M+ T^ifgDY<5*\mq|\.Wۤ%` V{[Zק7%m>-/cꔟKBݼOkx.%9\&~u<Ƴ^=GToe.ӫ9K=䯄VՃɗQy)j!ןf_]:GZ#Eb+ď^7NM"=x6F1(}6 +ze6Z]j>M] J|^ͫ䳻koosT}+C[|(?g7,&p`=޲jC`}> h8z ovz?>#׏mh1)#TP\W='z_ ɭw{'[P, a)ϺQE;^cUf \dn25zCF֖Q8fhu=z^oB:a~z_L:`^feF;/׀y5&ZjVirX0.ZE~ +/bfOIM_ )'w+KԨ~ m,W%mZ[ܪW+ijG<Úd<.i62C9Ρ&8;-^ [[uݓ:l 8d)FxH%8g*Fy`L3rlY9ψ\*c5"%Fv΋"HS +E"^˭s5Z3*ؿ\|.=XΖ>k΃Bث}uA}z_WsGhoﻊ7]?2F2f15{N:şxZH}uݙ_LqtJ>mYs' %2XBn6:Is1(梒C>J1M+Z[Y`elX >br ޓ-_|&sȹtDJEJRv= xv8J|/PܸIae^A\PЁLNQ:=eTx_pE3~{z5]M^~SoM񷯗gX}~ű~]aCcwWkwueX&UpE\`V%r?X`AvmcÓf +ҏJ81*/A:^RuՉ׃^fIr~]Zg +Q91`GUtgiQJ"Ic|D)'X1ϯFjTt85?Ho^Xό6 q?&X}ȚJis0_;ma9vs_Q:<u1fuy^-|Xô όy7Ԗk/Bk 믇&eߒ:j&?7*{UPhߏo5,<[+\/k-{'3@jfA8cJKy,M_ 8܍ݟv=kCw{g>UYO}醝WS匑-F?}I9^L'x%ɠ5w=3 ZC0s9Q32)9$m2-5hh{gx1Qo2%אmv~-è-opxjK4!Zό{Mpa/FNl^.{{x5)^.9,8//p%Z 43G mJkO<J3+Qv66Cᙠ<,Q<Sg,YBbT0|E_Z΂#xNfg3 T,|jڝ +l흽-w'{|DۛeZmӨн]z< pwiV;tb.ݝuz!I?7D#sX)8)0E"S ok?'41B)|N;9dV|:1`! ]^/c.3\ nrj9I;mǹcb]JKK2\pgK`6R\dSCjg?FkP9jeukN0e|Ms;9Ux_Ƿw5U祯L.FZ5/%?~K7`s;3n\헾y~"/W)þ[\v C&F'WgQjoC|CU{ޫm(ߩMQѧ/v'̣uď<3teQvXgh}>QtW7b7Mfl/͜_gG ߭6ͣ:yFţhۃhMs/8zqփ{Æ},>;{]o .\Rrm =?H+u>dcu H=5jsĚ:"cIo;4q.f+[|!=)4ycf[ra`l8^YoMOGXj&ccl;M^hE,M&ī|D^N!Gv.6+X٠R%MD(± /xm$"UZR8)l.y]qX'9Y]`^xlV9^z^T5H(hOy0\cfO#*l&^*^͢ge-ZNlT=]j{ky0r.beʽ.&`a*u2槙gmnqq]|=w.}O\-FcX}kg]E[t}z΁ȁՊx+#hU?*}:*nV =O>KkeSտI'3a1huk1ꅤĺګ#˵Fd/\qxt-qwTG~^JL˛[')17zE5׵ `LY&25Ijt ">W^b:2sM`ԅ7=!wIm.3nrr5b3P'̀ Oc:;~)ĸMk8S,9lt aJgeiPvEAf^\̀+Bb\|r.kdNG]wG=yGfOKV:1Adv\ff3e1q +(O\H}2Ghskb1/͉X٬_]IlUӷm6g΋؉QJ 2A0 C X{:]zώGUnig[ͷ*%W '+G1;E-l^mѶTυx-2UcoynOlgTQ`iĒ~Ej17[ݯvE~MUoVGi?ˤ6|B9H|ێ{ߨĒ7{*OΥuX FͰcxbY?\; cm^?ٛ~ \.CNVOsebʮyvluooN183j +hi' &f}C?/C۲ AQNC Soྸ7&O+Mm(u(jYR,u&}` _mV4P;uHϸi^Gt s=Z3霱ٜfƭ3֫ORq\1v x}@1\.tjm:=y"ݿ8d3nk/!joP87o!sm;t}ļ|-:[Wtz6C/1n!XjzD[5M`c3d1d}$1_}WWޒb|c{{E__)sٹLg矾ONͯOS;t!,g|M}:d~K6|yړ~NK^Q34.l΀mD4Nki3񜀖pvwSF{qtJzx\[Ee7)/B?wͷߢ.hmZ= 7?o[ `Sh羗'wO|oxxbǥ|]Ruz%k(ūv33GzՎkr|,jG?sck״:z3>b/ڇyo1g{2V֨MTAӸbkPU/u\7k)Ud+݋|=ѳlj7"SB3|rWHGLuQZFoaҎ'`(p%hUy8<@5͔\ވ'oҳ X~oƒCZAM\ArDs9б*VW3Q+nc|0hf+]R>4ֺ!.1=]ͼRr;h|l7u{^\J-zۈּN=[4[Ni1R=h/sW3:?d;ߥӋszp>#bJ1oM- 3/Rtرߟ]V׫ s͐.Sz[G.>{iWz\3ғkWInO/c)>m& +OO~FF''<.c>NOMh1XRo8dB'Ӈ|Q|y1>I;w\UۀU"|5uD 2?-4f1nn>'^e /s>̆s#]hƮf]m? NA?uQ|Ԯp= ö>R}/%[g?SSknd.&g"=iJ1˓w`;q{埆|9bd\zo E`c]^ާ33>M 0oyl#uE KF+W!;|Ns1wǏ%./ɱAbɿ9TzнУ;#C>nDySt5_tܗ}Z,%'Oz/]r1cۨI _VAX_诅q6$1.؆ /xv֔ԯCK}6aC,:8'~o߂=^#Cߠ7|H/=yXێV}%,v6<<#>=Z +]Y]L`l<>q[|NǒW;,͘3OFv*9D?TC +晫. ~iV#ĤhB׀?a{`N>{tzvAgl/=pt=,&!vy.:C:ZV˜y~kZuN+3c/ho;3;#陱?=;E.j͵߳43FlKI<;wZkT%Yb9vGg~[ ?I]~Bzp=#EzЍM.-_GbKK9.oS҆W=-W]ub ]IS?˶C*=8 AcYik\kbv֜%Rtp>ѯ9}Ry١ғ.=vvouoK鏤 @իĶAD#=kA;ׇߞRz^q,EV6z#b4/Mǎo COQ#0E~uVvhőԌ-MhI=pɑpxUz-#/2OM5bu"%мR̘; i1"ΙoSx: {?f0L; rb2>|Sz7/ 1n1Oiѐ4>[liǘda2s4S?d<%]2e>Kz=苜8loLP#6Q:}.;c[ݹEMzGҽ}:0Wf>?e>6l8v]*cY:&/QܞQr"_/lI#brih6ޣv*؆-Z |*˶)GMh}mC}\_E<ЏKQrQ)=a11QwOxsK6{ǥ-qJZ4؎ +<|>+粟 02?)o-_ؖVۺ5Y5ƣZWӿ̉+zL.^ӿo<1/I۵dLպ6;/ӏ_3G捥İd\u9YU*mz@dcxv76=&gFgE''G46}]Pr-U?e^t opiI<_uQ~wQ+>E-Ck3u#WuM/MO?&}K/ҫ/jv(="nj c;cp1C斌/3ƍW#ІmۛGח%'tq +MtΜyB=kʵ KzsϾ;]_y.rZ8!6Q@ib|=A|Gއ+~ +ݥ%-ɵj_ b'mWy>R9rNžw\¹7 +[}t>S V6jxM[8gs]ռaQ[\o >|=ּ%δqr+Xl[ǝ5=woR<7JS:nyb ޓ[77 ggא򶳹|eK壡Xj-qĖgh] UYUhNe׃d7Bw }K"t="o`08} D~|T4e6S/w#(1F4(Q8f,Iֈ)#[Ƞ] 5sƬ@n5#~ M^/=8u߁UM8wWǢkd~|tDgWg;+ZLW|c^1`t#8~g*97/K' -ɳ:Z"=>.1~,v3wvA_OOy;3 BSt˸Vl C@rCj[b[ u嘻R>ЙCM;ȵUy2G曲Լ d^=cswP|o +Q*d-n[a[L0wa%^Rſv]] 7.n'?{9g[kPϼ)W>vW/lmdW^x&R#dmϲ=Tu~5/#e_{#L5SUxsY--d Twc{|mGnbR^&klr+%Z^1τ_;z99yUm_jT\ώ 6~՚%3KNv -wCO`Zw#=)耢F9HУsL>bJ{+?C!=NuM8qmȸ= qc. sѰ#}m}9Ԩ!~E31 ##i){Hj븊EYWǒ?kSρ/5z̕9N'jAzڶPPBܡ {^hḴa#Ƚ7;[ǥv"6)_ܫ>[zq[-Cp9OU" lOh~^6N5~㍮(i8J\VNߪ?Ic +r/=tJSV8^3\['<.c9;tzDw-i1DrN)qvi$4H6̇ 0@LI +7Eb + +TQ5D4|2Zl|/5m}ocw.v"_Q~ly񷺞oM(e~fxx[aW_ZLjxnCK#eN|cgc7 HwZC5+LOaƍ0Y=yS +7cilܱT&eJձѺym>7m\4 k7;W'K'w͊r6#bBuo/6ir^+<-lîem0񝻱l5l mTc-8YL͵'fH ٕhD^ۢߢhsR9=]D~e17c>i6%L/<R1r;Tj3 +51m|kZEbFqSb{"ߝ.Q@q< [ PƼf@m4JT,,*PcX 0N3m]3f +b{pwo1n}#;P~n>f;5C)O$B/tQ킓7gA34 % Ix <8}wSgdPom{N}U.I("vbgĈU;M,{ڈv\A=̾ìm*w|o:|꾞 +{]C3soO,Qo\5ӏۡ[q3BBOE5[{ +/x_*Y'3#S6GMv3'9%vnh>>+,!EW/I=T,m+ؘgM~ܪWՇۂ󀆳ҖDS(:& +UYUsDnk".v}ӭ;/~_?ߴ5J:ǂ{{Tq#os#=6"{J!@Fb "WL?^v?rĜ׎lJd'zB""26Q#-ᾃX7eeP;p1o/0c2 ƖP)1Y.^QW&1Tʆ)c&з>Ըз%f |ߨ}GO=ā_A- 0W^_hs-{.'  Qq_lCP 9*/2+V[؇:w/^Y+,HS,k׷Ri/e|;.?sUʿn zeV7*+Uk{֧a0|*<G?ca|)baY柍6go\~aU7XɎV\8H8g+~֨ƚg/k;Wun|U\9| IcyҝTyFsVNm\v9VӐesCyU>+4GSNdýQ` ZY򠑂$>FA8`cYx{ZN_/鋿kKɒ9k_'pPxYE^Kb_ ?8^! +)0#+9OWB)gfL2F 7 1^9i/lȍB.vI̺˸@o$/8}พxlj˰e3Dgq:̀H-A O xqq7ix=؇#g$ K۲A3'QK|Q.)67c; *_+1]%9d>9ʨx>Wz|/[64jխIJP¿m.Z'kOa7tW:ϕx@IߨZ96{QKR'iz~?ׯ{Ro&{L[[k+NmVj秗†hxPmW >QYzT6BWped̦>͘tQ[Ji1謟8ELʤ!AGe},n +򴄯  +]E8gtt<]p%>cAAjD_F0WJSf:ޯ_@p}*JolΐeG|Ld &\![מ&7s8" .urό>Q1iUmM, +/{keMa$RbhB!כ/c=P4=$⣱#^>ůj#M6r͡_;x,xį&ߍcOL/[=U~S$k+]zئِOJ_u?Nps9_W<{/z AAM~G/Gم~6-Bn:|ίϊj?)?)Vi}Bva1wWvɷo+1Q]Ѯߕ_-\/*Y-k\8SC>V_|K,;gc\KIStݕE8ɭ9e> u&κNߏ]lvﶥfH݅N MYniza 3uNM]U]]U=wzpNiwmiӖжelpB$ĶpP 7F1Hd!A +׿s R.>{ZynnX NY㴵;e봡KNϣ3vÿgg7}v?}??;L?gVNu)=u^>qںag;5gƏ?qgϸ?./qjok֛[{7ngǵ[ +vV0~:~)>V?o8۟\۬;;lauΝ{]n~s4W??k7nn3רY?{g:c[wfiuۃ=ǩ.Yy{]nqTvv:sBQzҞ0=S~a=iCs:{>Y{ߵF +YRdž{-si:vn ).|.|~/+^Wy|?g/udr;x_>z^9+q~5nxNw3p^qaL0~^gqnZϯ^o:Gi|tT=7MﵾG3Y*Sws ޓlW^6蝟=|\j?k~>ݟܝy|^]\g=x[MX깧r1eyG-b/0$bp?cJA{LM8;q 8`ujY.qy;s's/ySwu[O?g{O{ /oFL^?㝿7\|=zu7s>7;ğ{ԇƄb_X!~?~Wk3pXwƃu:0k;7gp.3_ƀt?{1u>x}s`ޣaa 6SLn}A_P,K=@M[' v{gߝy(7k8/'ceU𷒫?(_ io'th<cY^# (E _c;7wG07їT S4ff~wPw G^X_z彬}~ ^7x2b^ U1~ k.=;?/]S3m< PπOO{w6؎0G<-#✭YO~KG>1[^8E;SY1s8i^g;ۓ3Sy/S=Ns1?cB;xlE7ޞ6w+'83&ޏzpo`s%ն겫%2yM}dL2QϞ4 }ckgq~_E^1DZҞ^"n} mc6q^^X<#+/[:SqKП@(Z`SmD }'K~n<Ϭp ,|msy&9~ߚd: s{uVe]/#𬵷3hGh +m8# +t(gd`wQƯ[p{ܫ}=|x@6e_@obf)1pcf߃Ǧ6yp;<({VN݀]ǫv 6Mo[7l@m$ 00c vz 1qӸs\2{s}ѽ o=^x={ݿuݾu c۔cc@Oz~mQD@F:ڟ` zA]Ŀө׍F + +p>rȇt4GBB)+O cg'aS|'zVxf5},31Qu78d,SA7 *26b ޫ[/#>9UvۄҀV8of8O}s0G3\x9xp6(p2 ;a0_0oY GǢUr ?,֬MK>0rv;ۜamy~r>r+?ݽEX=``!rޖ6n..#ݲ]uc!؃)g#D>l +o \/p`Oy~ڰ??#)yZwwaWT?006a/ Zs$ P[}PooOڀ^0 k&\Y+ 2S{ē>wNN>%#{pw +x;7M~[Knke߮7,=Z5G-"9;Mq rUbYKL8mq 7~^ݏs}2IKֽg#ث1 rs OqtI.!i_9 R=sZݛoa)oW{}lcXx?s>G [_|g +v/< '1 d"6&?e&ڷyfR(7{ponyzNo\ߵǾwOM#?C,7@3x[w[ w~v~@}׫MCW ೋ-r [?7[o{3+) +pV~!_9*1NƀcH +[Uxz1S:ZC'}˽Ȯo'ڍoHgM_GНz7=IZOŶl q~cJfú4۪on؃>M(h6!D.3*#*.ZA>2)"{gGoN_kzܺ7>x^}=wO=~k~pnpЛs]xuzo(GC|p%;+6 `cݸ}ϻ/|u|xKw楷}/P#Yx(\GG>pI)͂ +_Nx-r8VؓYvj vCcgY#+|=?-z ?i~#ߢ!ɲ03ܫo|:[/kezmG5 +Ͼ3VQSHop_ jET,g3x7}Ebc;0kg5ZUu>=a"ޠ C|;wֽϹ Q&LֺeWi'xO۪ qJ;?U֥zC+O1 Ng {؂"tÖ΢|\[GG%_ X'W|~nI|zV$2ğBNBV#w8[̝{=;twNܛouG+/޽n޾v;ⁿn7nx[5ڒP'_-񋚮% 2wO6ԜA2pp7_u__rʷ]{ՑfW 5^_6+A#_[oiENc=x]s`9r?rWfǬ2,0&ycO g~\_cc)!Lݦu7&ښuS3<~ UR@6"\q噂V _`=7A7ٞ232#ǞT~r|sbR,ՄxFy0?lFÚd0Nۨgun]Lރ}/Зq~~?w mgA w)Wm pw}@@'b#ތtSot?=s=w֙{MnRy/eB/1b><`_{KuE~Q_#ՆX6{ѳ 2c>3C(bs77B 6;, {.&7=Po??C?v}L0x&5ĀKmaW)[_!F.OJe7壀,5tƐ<>~M[D(c$w_(IgO.f|__kU*;^wqD9R^9r*_0Oﭷky?<[Dzd? [k< fy%5@{|B +b|Svu3qVlR56AM| o{ ? W~{/o؟v|#w܉yǞ vC L0u>ijvgEc_4uAc +szv~LmoSo͛[w~:cosvF.BA \OX?O4%ٯ`3GCwX%a| 'DV!.b7pm_D{u'Vϴ@ȵ1w)??08ueRV;qk:!ߒz[']ι ʟC,¹{t{Ct{oyx vn_hwK=7FLj9V ?Y1eb9o gcAճ~Ҟ4^7g1vju`]wS%,.7kggB-۲b Vtv+Yحb% +zkYχ11Z?|owwC Ϲw>ϻ;|䷾}ϹNo0g>XK=ƨw1m~rv ] XWKqR2Ր`#A 3cJ6A)`n]r'#;l^v%-QNȇYr/?ty?wWڿ~!uפk`NT~ a!)W%8=be.^rxq^(C[EZgBn3loee +wK4,ﱿcuu%;}۷4@>h6`0q| gWš'[wyqKz}'~Ϻ|-=qp ^A쀱ǐ Pܘ!%="9w?S!="6vXL;߃+#bq 'kǙ׭-"jN$7pFUg^{G +{ +ڢ@18 dgulPp z⦻VkeI>'wǀ?>O~~=S?^=6E^S3}-Y]e\62&ڳ)~^߈/,{u"+Kj#\i?dq[Go׼Bՙ=z +UNt#zt2&0xYkzD~,?󴌽^Kw߁ɻ=?;}o;x>P#q˻ EL.K+;,m€Ey(uŵ!6E:w5֒>q-:m&wد[ZSk?(F3.fNjkˠ..1/ƊΏK!ÿ/v_/ ܱwȽܘ/:'j.X˺#VfC_$ %;+9P½t_ϻǯ|M\/]V;;@>Կq 2ufqsÆlA$^^{FS1gA.7Ȥ' ++Z|dz~1BF}Ogp._pWe~y4Jϰ˖mG\ ?'oZ]}gk:Qids/>?up\9c|ܟQU}P+ +Y}y}m[H\52 O ȆZUov<"a~w_r?_p_owޗ̷DCU3 >?'OrP B) !W4;FzT?,/=+X7qꅀ ߭9Ԗ_}Ti Y56I|scՙ;?YPpN3X%5F-֙4:Ę)]ȓ $#)W 2?{ [T; -O#fZƃ$ϋl; N]_~$KO }aʵX-YZ矧yPO'SȇjfHNmyj}3W{Lz8~\p$a՟/ߛU+8ܔ| {լFu%>IYe88ؗ//8w@aB7ٻyoPNzolfe X#,A,fbarjZ?몂^Q\Í_K?̎Qi`ڲcPjN^޳-oX(:%r1A=m8/NzC'kJ6su9o̰i/OKgol&ƻX?Humѩu=za_[C9Gzy+BV ܩާ#;d1_MƳOif)_9a1?zBUXz[ګ߲`Zo]eoXH,u2A1w~YޖK4t+A4*hV~+"soT_c^w+_cfz;iB{t=zmچgR.7S]o)VT~Fӑ`7c^|$w[]n[3`ذ"*1\/J)K ~ǚ*,Xj)ث`OvT+ҽkJW|kMþ}j=uzgjN#slo.]Y{~kc셳W3lOTE\gk}_qL?p'q|7W yRοR ŵKby-}-_-I'⣪svm;}E-r/yv +ϟq i~ 7(AG4~$q|Kq."΃'H\W5G)3q3#9Lzs ƣK݄1u!"25*N_/u7C8QoQvC׻;Ntv$ơ+g=ȼXKb>ď+)0B1upm + uL-5SKTgysnb|d>K[D=_T[5?w 3AMщ}5cW~Ӵ3`MCrR{'ߋuU f?[ϕ=/1ч|bʼrRsb˔c%0w=1¿ި]0O}iպJz=խ8gx/`U쨏=VЇ~X6Lq4iS?cu?(X|TKOqbY;4}_|f<{~szΙ4oHSzs&>Yfܘj? q?Kc_ʏSنE{ +)OmZC䪥q~7`#%5Mx_ 8iζCr A@e})wJtLn7^oOSC嚷mu?o!gαWw=')4քxn˩.plS{,; Y:AΕsG$OTRʿj͗c)C?bcګ\EC_#ųys~GE;3L}c]G!O׊Y8/mi +qS(XޓRAE;@z Ws,> CORG+ zz a5A$C(6נ\RCcޕEoj W +Osh~n87RdnO80G3?NM9]g+'gB~7R'/[2b}f9ݧ3w$QwI~}ߦڒ^ہmS|Ҟo&Af~Y|W[7;S]: W}~߼Y\'I~|B ~ȇ4sq P*¡g+߬ LXݵ{WIMdݪnߛcherKV0?/k/"v\PBSQVaT\ס+ ײ !!6 >\ו1tv;^8e/&Z`S}?P#Asx +ᅣ"K%(_#~'xc4}=V{q͍|n-FYqWiOD\24ۆW6_oo)̹vՀ9\onCev\qL vX_;dْ< $RInkh=֪X|ao}`ڋE~16pgW/ȳ{ 0^~XPYr-) rцFI{ɋuG9_7g8g'Jj $F}&R +C[ %eq1g[ؘ>Q~QGλ~ XD&C Oyݒco\lIo"^ޭqB Q.kx*cHJMT +OET{HP ">3 TǒbT%K݈~["yH8i5C$23Kky-[qzЛXyz~̈́k{1xpƥ6+I|e?ϥԃך6uoo-C"Un31_J-EK}~='A@qz /Ob!xNo1h/,[L?e2#R 9_.} +W 2,5#e˹: g v`{B>#>b퍵q(^zRǘ-pq?azC~Qۺ4o{,{`G}VB6,9N3X^428z9 Wۊz5vCߙܙ9Fߗ =U*Nerɿ)Tv6ق߬x?JNՉH|xSucӽ{g^%}S%=d/ZŹ| %yp`abpUMtp';s$gR)hT +;`OY]8?{q?d{ܕwGGMKNAx89錚8Q=(b=VIN%a}+sP~B]dqz9*3sGvpdE|Z4MqUZ¹pZoاdg]ύ㠋8+C[EzaZP dEVHSNua\Q\Iqa\5 o&9.ڛ lsWaR|,~U]s}p]L>k)] %>ֿ |s7-v֫Sb̘qd%fG,:%fC-|!$dMUa| D:ǐ?aooFRڿ)H<BK!pvmvY忈/†1CjEsCnœN '&#}P6jd2ٿY>Z׃-N|IOi|d'f섞]<Ϸ G\=ŏ#OKA{nw_g$o[!n[{Oh7[Nb]c8)~O ⯵}>7rF [1V)T993$M!7^He9ԯs{Lj;;Amt_cyd5尐_}i>! P_!Uyio EG$ cxoɟ^c %L.Ľޓgsy؂>??!ⴭ׃6;~#Pw~W8csbUGv)W.~93G-7# ~)yxb<%?-1`\hB*'b M64#*=7ULeZԯo s6([p "ӄ%ߝa!c*c5c^+d*$>|I~u; 2PpX\Ƕ{98]/L4NVl|ŬCLaa%!ŗ_9'C2B Bs^Oc{,}& 8WbnaSXwAz  m\~]%ٰǩ.uv}S$r,_ =*]r/G(cQvtmos]fo*OK|Fbgj/>/u.)+bؙw#8 ﳄ"q L33k G +K?^rt_ PhfR{;?b >T +?t^z5rd9{'& 39m Y@ɯ%66*,|-wUIxpM-ch{Mk2>4wVj\uz} _c C?+f;QdI܉?T|ӹߝ~x=؊^:_%2e"ZOէJm"I-˶Us-hZ"kQ\{ֽV Rˍ1R(*7佔֞K8x|IΡ7#B,8y{nwˑI} a\!u^}Oi&sν}ps7=R[kMu bu8Z6M +[ܬt$#xrEE>߻35ɉR3 ;&7SK[K~9/OU5>f7軪,۳_x?5Ws}s5y/qӺq"y|W_k^`=Qr{vS 5:`MR&"<#֤O_U]g(Ryņ8qfU["O(ӽFOG|nz3Z 5JlC9ZQ/FP}}}qS쏺8"m0ke\s)]!'MݛuUDԎW!.<zsa4^PͰ'3}=bac&.~skA|i +sxPk3xR~o ߯]c]m^Awx#=U[xxL+ƻX?E;Ln鷺N\rLU +s3(^vT6L-oTk r1E dWj<&fMIZS$'>ױ_Xv=(^&t{Щ=,ޙ{O>UXےy6Hk.iܯ9]EYZf)߶fNްuܴvZbM2FYSԷsYŇ/Fg(? gSٯ1&.ث=o.ۻ}Ro*Z|PZ_R`oHu)wO`/^zǢ ;; ]"3}ﳦ +M5!)~0kMcM*=wqUL&$BziXWQ-J>شswyL8aY N Rj<2/5?/7;,J@;хpwgxh|ԺmdZE=4?}4׉yAL:Ĥ w?˃uH5V|Y*7;1f[z\@ +U̱^:Y, q$K_)|&{iUggQ߹~-cqCs$)v~ 1K6ͮW6iv>4aгϽ}䣟tù#O:R;˧%-Kȏ: <%],CR"+W~M*糠|>sxko~)?qcqKY`[?D?Mn&9O>5MhqI.]a c-ZwbR4'Fk S?riubL܇Z\ & ^ 4{ҽCwgƭMZ3b{֑VV)HR:;{OA]Uw\pBkoi\cp%d7[[/Z[r)e)Ǫ^b \g}!z0e}Mfϳ[˺}vo|mC_wU+~g3?UJ1w;`:К"Ĝ<>XxJ†fLgR (ߡ4z< uk?N{blW'Zuǎ`+]gz)r?ᯋxs/}|_Z&?D'%|EU=Sx +cVfY\wxO枱NU;\ }6)'X3싖Z!ĐP]Y[o>v?叻zhE A, :鋔.$;Gw!'QA\ {Bt<>\Y߇åYwrr7[/q?.7-ߣzᓒժo()L +iQt칣 +W%-䙤/u o`[%ʮ*?'W1j}{Eoi274@/A~ֲ:U5_,Vzr;}JlR{3;ꖺG=zi_?!=i)5?> gI=I u]9Xi?>=O6;xpUu'#u _%zq;wW]j _ޣJF?(;.kړK ++G%L󴖡my܋]Ş7'B|{\[^Os}dz:FS'8ZSI]ÜlֿP⻛ߒ(BO&溃-)5Wb [koĩnC^ @19Ǿxv'd:^q|IR_]vl.MN07PjU.M{_fÜ}{/Ro-XQq<`ou8a丅{'=13,SOX$ʍwsoVO-4h +1WW?kW_<ЋO>#>v9pqbCzEL9Z_}WU0_L_ nSnn:̋SqUd1nKW1|o[f=n^vfaaOuBfs~B߂^^[f00P$CLt>eS47c4Cq%N&lԒ+89QO@=:`HNo!_:p R%Y>+ޱv,V8F1,b?/mۜ<͔h; zC_~z$8jL{sR?(uRvwsy_GcU|bw[okx//n`ڸJ?.Ɵ_h{~g7MKj]hPW&=ROtO!h"#Ybb_E>۸PAIN+/ aY=y>z^8BJqWV,}r9xmżP߉u~~SW:p]-/3noC/bօSP\ϬrpN7IOLK⍐F=Z!nJ~8oN0B:üKO%2ƜchlJF%Iޙ*6IJUK;"H}<IAU&UkfOsfI/SOǀc Z^L{-,i*v6ĝ)f?s}k_qc>W&4_/G'%!,'bp/a< 63p(O2W>SecpߘCk/g-}&ZdeL71b9ZU q&m6/S9STv7wO쯺3Xcc4<8|*i|Zߪ~$S~oub6 U[? +3,gdfk(>wg 4`TU?pzR"M~ T[E])wZe5Áx +֏ >D5bMDb[@;NZ^q/bxծ{/~w#r«~;XKJH +cucw3;V^O^MϮ5\ʋ>&ߴ&qgT*Wu-`W[OI~Vs\mn7$~ szp(7tW_@|`; ѿ9'q鍑Ƅ)[(-@V=Ԗ;.-8\Eppi}f᫥&Lqԅ>#S;ԓA_̭ feo#ұB=^&$q7s ;DW$c|c(~ܿ:̕ovgs_;i?ދk=ȑ0o8Dڋl%C-?8uJ pT +^[=/SřMט.b{Yr=_vDRý|:2O< N_h7P%ǀg}\00# +Mq2Vk)"{;ma=h-x9|eJx{Zd г@@s}89AV+)_wE{>og_kCԫ+0 3ރ&K߳v{Ew8=uIsif +$όs?c'n8Yߎ@ql'c%[_\n0Vs8_[v!ITVo|DA޳a.O[9.ߖ C,E˶[K[& +?YnӖٹ.3qOj Bâ~xM\9UI-n|;]/Uqf>AqA~M=vuC/aj]}7PϨ^wx D2?s q,9R,q/Q|i>S Fe +[0F[rw)wuZN_T/@}2(J?T%׳zYTRkibqM뭓~YJzp_ +N kuSAPdekzqϲ[FE_DzI_M^W5_ꀅ9+S[Zf}l_\~nO1$GR|:;[ȗr|x ٫vPگHǘsƟ[`f\+R^\ׄ8Ϡ( 'wms JVw[Ǥ5x|o"'V8әU~ԁ^h)쳜nyQcPo;pw>I (u6&xZ;AwS#,gZ5E5wȹt,VK̦b"K,_{~z9^QkEK:*"+a?`/{ekccһ?XSqO#t7?PgO.fe$YwL(?.rp3Xq<ˬskd-R^[+靈dM5ڟXGmص]k} [@l |*w7ߔoL^w6}|mi!7)}&'_ vN-~W;9;eCǜҚ\\wt[pSe=jhАkK\",Mxn%kڛ}J3&l2=ޒW=|]C|ϕyT?qܹ*[=.ƕ8n\Lj{WT_ +j,w+{Ωm9瘿kS(Տ:e5y=y6UJ|LYg kqnvy9dA2ѐqn }뀷AX[ʼ,cx':3ٔXr{~˲u=R!VV>s?Su)^;ioU|Fʭr$-|?b>) !d'kP ػyՀ_g翔UNEuL?]$ M\%&1,ˁl.׆9?}Kr=/|ݞ/DJ8 E +[IoG4xpyv'\|r.Z%_DV-6,I+䫟^fl3ZA,滱Ȉ?)g7\Vo)u).{7QΣ̳|dW 289|RJ|"&ҳ[Ylo`߶>̷qV&/{P +O ~@Eݬi;qsz`[E_{Zz +y} [Q&շ1.!رy-)7x%WE^h:a +oko_]N70 Skoq ߇9=ſ︟ǽxl;wăXb|} +:6ܦh@+*{7zқ9JCa./(Ϫn,r;ھq_k&l\xL'MI!My%/''&W_17/Dž=/&3qX=ǧr=w=]ƙF99?W޿SmCx˽[>gd )+W3)ױ}+iٿ2w\g ,9v5+b7\K?mOŭ[C`yQ(z%X:}wpםY=﬊kWƄUէL𗰬gƴ7x/dwS$jkƳy=yM{r?7ܭWX顧8#C!c^uM{ <5ٜ eM0럒 cNU{ytQ9~.Vq?|ki/fevrY|X~ݔ Y*yc%|߯6Nu +(97Zelb8ZjrA2GʜdUUz1}Nqb}ws.~GeԷ.27=C)U3}}a~?QX]Bݱ :;tq>o{w?wSwE9GW=Ko]SIp2фT.LhsI` #,MvQ%J=J% SƢl_3/PG!7m|<'dK9M̦||D^.\>K\e ɮc+us}k IjnuJC>MC[_UM8>a:d<-O<)aSpg-]\8k].%9RdK*{-73=,T^H -9. 槌̖Z [}l~GS µ%=B=N+-Xz{o?#?rS/xíַsq>y½#wv:[mCPq)`6kLȏfUoRwS wD 66+6xEK/|j.?rljEbʡ4|-/pVno.GdU00rjfa|ISQ pv𵄿xo5_Wt|{ÈQK d ֮; os_֯?ws]uw8nz۸1l NNgW$C1 }%QUi~2PP?'m~t{[G9H`Gu8bp?|)=x k>I|T֖u Ws9ܣo矫Pc#q#5J<\l!Ƈ~WE;¯5Mђou>}P^OI?c9#_^h2l~9`[o_›?'=.Ouzx]ȔXYvq%~= =Ȫ\~iurm c$B2WSͯ kxm=P7/1ڏA`=]Ȼqp`JyRq;!'z~w ?D,^)Ld^q/ן EuGO9 +vNK2]!L%oGj&Wx rf( Ls_ᬳz;3T,wg(6sffk"?Y }*ڨCHDG7Xj쫽{u=pߥ1ظu2vc=XxAQk,q:3)/,>u>a/bVq^c?%cY qi%]O[O5+9L,T2OtN)fGc]ߌ?s=)ߏW-xF\y+併\˙_BQ-,U?0cߟ1ez`k2- +pW"UtJ'Rk- 7ez,o⨪|5yC#:Qg1j1\M1g2,K{ďs1 ڛi䧩y^`mg~ku +3o^:ʳ]W܈Y,FgO_<x>A\^O +f3SMHwy[>kZwr.?>_WǕﷄXCMm8tgu'F_ [g^c^p~`OyeFsY/鯌-σUXUA:V+WD +NztX"Q_T6LJ2Ȱ֧7LNB H~c8Iowts}{FoQ=KY S=h2CWo-_E=T~n'W/ح +Y|!wÅV{zR#F Փ'bŀ}X;}>rzs,tc8ի+'{}ԟBra.Ϣ/*[T;`A c; .1֣#[ z}Bug`c6:uⅮEo"v)33"iO,1Ց_=.梾݇Ϲ])BnI㉢@`Ig*L>E:o!YfA|!!wT]NW@r#>ZI}U_rpK>ڱp'E^!HӕyVl蛆ܩP+Ur~GoWG3{C>k1?$q!Wb;*^q>'Exb~ax/5O7$~΅N +놑dt +2xNoʩ޼>v>3AvUU/ۿ ,絓Z}5tVk'wL]!ޡz_úKّ52.QƼwZYU\%vRaeO|ˇ2&%5$bϕ7>7K8`'|*+r-~[t_@t,'nmb=y3Y`<фW{XB>U{kJ%R9ҥ3Oe"Zsv xQ/*C9V'i4ZC,ʍV.W9Yܣ;?ᘭ%:1SoسnY9Kk z e\Wcl,úEQnGyeO{k1p{qy+մ:kU~5'/{ _g5P/ 98Zgk7jqlipߜ7{ɷ.~kؗ{؟&2.Wҏ(0_ zdW5~.1|B>XԏJ4Yԟv1 KsR8a_ M 䢋z>O?{oZMKPl;Z3~o?#NEЯM0k`mC&kdXO=z 3/C _dEǥ2[hᆃz ;qTRDŽ(w 9.%/w%uZcZ#~*M߹jASFJ%qݽ;v}Cnۓ_/*Y +y䇴7[/g?s/ΞSF}X.w<_$].# PJLz|j{qͥlֲ^(1ݎOUů$Y筫 eĮ5w/M9UsvM(1ր}n&U||YUH}="7Iҫaf|gXm?Cg%Ocl.KtnD=2K|{>h~}s99۱c;on&NHpn%DbmJJ %Q!" 5UHZ** (B 5kf[~={f[+)ximn {S; +n5qO82ǻ^ʏcj[/ȓ<3&P,+X D~ëgW mD|_:~_?~sO3'g.C߂j8y.^w27uTwKknpĀ;|g{L'~R$ͽq/QkphpF{P==^W%=MÏ|c*Ct+g!QX8tN*_=y]1 ׃yCX|Sf޺juE[zJGU }V +} M~1*џ.ߍKM~oozǏvzFˁ+<%BSYÆst򺸬ޮ]P2|z=Ѯ?~#Rdל[GqI 5|W懎 g^|O>t/}96Nyw+%%WxI?<˟J|W]H_pS }+[΍9-Gc;gcZ&Mk\jO51xBfe_t@jw5>كOI~Fߺp<.b>{7|.qraŬJsD7ZԚjgֳ*ha? ,C?^ͱK}]#SgOk7>ӹ_ۙ~.ltц!S]ԫ>üEIi͗tz꤈bbB_-^ sE~j<_TY_^n3JMCg=;?t3u=f4$/%uѲfWϵ_^\R9ңh>/7:h{SS4r-o +[-_yU9ykHl]GliUsO>x|uSN88.ǟ͸==*k1`e崮+ƃә{u.9_ ̉CW ΅M~7yHti.7>̅~{ +?臞nIԯEpE 03[.Z׌>3?=xU@!.st\rYmr2Gcw^|XǮ7{n W\GH~a_wW~YOjO7Mћ_dz[_Zj7 Q3U MƘyl%{+_:?$~7*iQh.3:2]D乴e +f5{O\g;XE\ooO9w֏ +7\~UGpeKs+_<֊/s }n'5r8':5,\fo\&ˆ)Qxa\,DiTi1UrvyQmU=zUiD&t71~E׎20ﭗŒ{_/E P*'-/a:wzk>]u8ޑup+5rhǫ1|?qC<y~+SoQEW72/ըg_?8 6ַ2>9ߒ2oIu#u.G4+gŵ6,tt}|<Ƀa(.__!$q{Yh` WGcNDŻsyVT=70ӒڏL݂PM|qq=^q :"p +U>?UlΫ-=۹['9=Y?}k_[}w*x/yӝF{W8k<Sú?YYi* 8bWWٷܳ+Ϲ}lWN\8/ӰSUXd_.u靌'y&^i7/o}_zXN^c_[Iר{nPG7*9c]a tc^'>^Cz6c}'h WniNS6TR9ڙ/cYVGpqc;kVZq~}l}o9νuuڐ63|#J?K|Yx~bC:F07}}kxN5ǽ \O<Ǿ/||׮މau +۩8D溍@z^r_7c?G Kg(C^CV]kH// |&-q̍/6=+J>Z}`ΜCL{1/Tye+\X e$'hs2g <ģ8^,c*GGxcA)ᅯ;*|?O8S%4q~7|15\s%ίCo1f|l%C{ew :87\;\6V˽z!,ǡ 틕pƧ{1dƿ~w/ħ_z7ؗ9> )Ԑ7Z}yuPS/].k~8S=d!N )c܀:_8zp4Op}B81g=# 1Hp_Z[wg<*1'4OSsy~ >o/K+ٯ}_GϕߙCezL{Y]aG/Co<[ohW|<[N2j+pTbgt=N닸\p2NgNesj KMoCr2/G}pSL=_yDA+ ىrWY\?=0W,~3_?^ZxB }rG3<[o`e {Zӡ1WDmE?C:DK >zv~^x[]|Zs 7'pWB_Sݮzf^ʫOl8< c zk?Fr|+s簤gGw_=>ϋ:yU|]W⑋|0 +U{s',9Aʓ>7gOD8w5~Xd?7rp֡y˼$mg*˶(? oTld1foC.\_{(V%(Wwc ]1=Lgq|_2 #gf;5챴fpFN0R{[eT 1ޔ3zY3R3 o;+c8`_2]hCZrz3&.suwmMe/ >T7x2_޳Gޫ*gw#z<>OٛF={/?_ߌ[Ư_o~{ZP2{C=Z|:C#7JQcΣDmUA|c[v+Hm3?/ڼڐ_=a F]l9嬼``x=XZ#E0*$O\y\&ӕf}13]'bȗu)۸ӄ{uimCx~1C kUZZEU}Y`c^PeL֝_ "Q){Z )`?mCc_^9_T9AͿ:cUxǺEҊ$j݇ʍb_X˙98g}A ʭu~Fغ{WeiWb75|<ԏ m]k7%~{?_S%*&Fǡ>z?/˰÷10،zK[FKH|#>{~yvqE*]sXCybutO ad0"sYЍ8}#ۺq5C9.Wqh/ ?_՘g |?}:Qi%~UߩOa!| vɜ;!\iAʓvr }=9@G/I]徰~K{1D\Xr5Gf1=gj{9}n5\{ƍ>}xqѸ[aU[O_RS|XVߊ2c\mZ|7 +Ÿ{{Ob@r՞lmC#h-~Rcn58gB! +G~yyOz.޸z7ȇ"f=/Śqv>u#!Yԇ/-[kMZ7:'ɽ T,|vTy+{w`ί?b+܆z12=#ohȽ;K e>2|ze85E߬sUʍ=\|>}nGxNq|QU/;dGcg? j(WU t2{ز+{sIvqoݸד]8il2׃3/__\⟓]R[t+[}BGv7J:mMk_a<_/E__2\-?c)Y*I]#0CwRŃ^J9#Uƫs}| _%suwiMlg0P`6/D2SkG{ +>ZϞ n+]O7䵆sus|y-}Ku4>v,$i!/Ym_X:3i,+~^+mxϱ.NV@7/տ{gf,y)&' &?#8X' Pp%ϯor5O Eochk=c1#sn7.[:#>@yy ?0<)2>/C(}^*{Ƌ6;?ݯ|z%8s;pY}T_P~ UjM51cz07x~Q~[oE5|쫔^#)w"M3Y*nPZ~\*o(gź2fkDtOή|NU(0FrufC .wT}u$Zk^<>nipLS:_)y࢛|-C= >M>)gq%%{se\|{5~_o׼8/0gU9"1xlwGbK:qߗpuK_ 3o+$\?$4n !@)o۴wcS&|}||vMbԀqbQOh%@_q';ao$ޕ`د=&=F4":/49.C|+ƹ1ޜSUkH?2(_]ߟgy__;(q} +\m^kC/Z$&>b?78/759V|"=bo/gfEY',xmV!~{?˿=^zΚL %k'yaXF<)q GY|͋_گ?˿_ a:Z?) @;Pa.21ZGw0xE?T}o +~-c9gVl/z3@ Pjͯ/.:k/+6* bD¶os%1pX;1{y*r2r'F#{?q":_-U?rmud~-k(,whoRQLP&m0Fw q򚃆cQWH ZEA/¾۬O v6ij(x#9mߞt~$?[>W{; +b )?I{PbO7mī7[#[y9zV'$D͏)r7yQ׹?'H ucc[F/P!"rȧ~[iM;eoj}3wYg n8@l}N^ԃ zLᯜ"Gx^&/@,^r}sZ<y_G6WSqϕ+|1(6>/5ބaCOb꒾UgI giO<˜- +AvUFuߚA_G/(S 6&?y{WiCn7k:Og'u,y^K4'dTrtf[⻍3$}!?AsX( %j:>0!Kׯ/> 33<ǟxqoc6?ŗ^8>Gof}va末wLʷgx ,ʕbg kՏ\PgxKbضz,]l,yjo~=#@ NX-{{M5QM#g;o-5X-eG'<4m[8<^#+ux l.u(~zr~|_#Ɛ؋揦wxD&%+~~3ƧVrpJp/Sxf$:!U_߰ +kCm\^)4xO@Oߏe=Kg#?o}sq<7[b27_i0fz=pR0ǍK[5#-3\Ξȸo {2Ia~)nx[{$({Kz`a9G{drAu{/'vA_"SKK_zս㯸쿪|He1mBOj~>gu|4g6瞍~0Gptx`IÌv}G[qI˖|~ b*VoTxzg'[?$yf5G0/{~CN?{;.>tѻ՞75!MUe< a7'񥗿#s?c׿t#ֳu=R;IcUܝ ن iᾡ槞Uj&M' m=+ׅ('5 +~_v5ZY}聏3njkrR_\ISTzqt)g|CwV00o{x_u?9|aqq |y\tk`8B[kn~ּ ;KNc|}&#_7ws{2y:ciY~Txe}֦&A&ފx*vwLe6#j:+zeZOC[Cʯ7|E+-{̪H26rPaRPGtm{0|Ye04f z/7SR3߅b$p +x;C͋z@p)^?.jL\+ȟ/CKu${l6gYz:}"?u]ƥ,#⟦gF3c)59>\έ$ >DzN6^+zE4Tck/4Mv-ɻȼN4p+Mr$ySGb?.%B= ݡVyL^-\m_3z5'cc1yc7'M}9ߥ1 3~<%ϲ/Oi}fEQ5w X#)/>{ϹN)3{e+Ǘ=GoĿ~9~O}->ԇ>.vms/t/gRE1wHkVsZfߔb 0L!;j_Q?mqj1ڲ]CO_Zr~vx8u3b?ng|2#X_;sQҞU3ŚCQ>X +e}5d735nW΃OƳۑ֞0gg -UӾ#_m,ﱗ?U^KCɑ/Bw>5dX[R~{{R4|-1Pbj!.}E?>,`v>'9o| +&b_E͵z' =k^b?6Ӽ ZGd9{\[f4{\Lu|cO[X#}JN6YXܹ4q;7oKxoϾyݟer?HUiA/-ƸUM *9O[uFta(.OaͦC;(.qr}nkzv'>W{C/?*k^hb~+9r3LMxGToVUrySƍǷ#W>o?b6n#&][ϔqZx$.siϺ\[n)օΗ**cYCǴ'|'/ۿQЇr9 xΫ<ʈި_K>\J.t8 c<5<'dt b3FmpDW->O-r>پ9rJAEOg=OŖ{|5=>*MNM^8g_Ǹ6W}8wǫ=b?Iˋ+oy%<9žq&"<~|@$@1wX..ƿvA<>:f}s? #.^/rx&} ?{ _6>t뜃^b(Efj^y،1}n_? ΃|>O-PxDŽ]5QC׫uBŞ"/Gu2O7S_3t?-]׳X4D^t3:xkso{_:͕R{7O9Z:!0=%&| rq.?'ᳩF:wq-ĭa#Q}:(;4ϞP}"j.7F]CuKWF:pK@V^XL?Oey{qzgo`^ .R_;}-#_7\tqFloO{;xIX}_Z\>|?)_#?<6$Sjέ8gO..xae-௴jm{ waKr565_V( -Mf>TկF >x~X"ϥ+uF4 >?8Vz>d9iq&âǻ%8X`b|_ng[r¡6Ʋ9x&r" +5N3 i/?Ouʧ]dxDY?[T0ϢSu}~|*#wýo Ch 3usTߞ n,_3uf׌=Rs^:r+'1#Շj!'C0l֞B{8>v?0}nl|9dV޳MYrf~\rž29'hl'>4^ʣu3|_γVVܮ$cJCwOF>g*hۄ^t_.D0(Y1t^?NPVȳ̏,WOdlo&OF׽?CށxNɉ+ R_U{g3˰q4NlZaGVgq\b닽ga!5g߷NgBs3tAkB} qw@r5;-ls' !ծ&ԃvC]gYS1Mu7:Ls +ӳBD_9kЏe͆'N?O?~aMΓ3ջ_ncf)YKXՑ/&om'1ybW#ftix}H+~[Jl+ǣeΏȽ/9s8lK}oW{Ҟ ,=IU1af^? 9{<^B ۋ+XS(|So٘=u+o$bo8̅CuWsvpxo]?:sPg0=+}Aͭj,(h5_X?ɞײ{_5]x>_rk?ǯWWgfsG@=ϡ`B=0yYs5_s<E8F|~L;(ao y;ۧJ|_3z9#?؂lZ#JwuO)㟝c7;w3~gՙ)>{?3GHK6ާa/ ӳ7%[eR);}~iӳr~$cL9OVkbsMoxZo]B"&^wY!9=${&KW.DZ_^Z?U}C[1BIMKdM{L|_cИaMY5Ǽ|[xF1Ʒ}}{4G!zwƚ@#??sKʯZUlH-c;wq.d~k،8G!5Y<_ s6<Ӈnǖ3,r +?rJ{NV0~׵@φ^7ܮG<56J[Pk\/8ٕ25C*%k`cR{~>G1WctxQt^/W>Pe9 duG&z6{|{wu$MW$~=Ho;J1WxOeosy C| +,hgOG7gOU&w!}{}~8Or}_^_s{jܬ6w!=8b/O7f֋[#94v%ɏwXĞHy@᯶oYoA2o~rf43YI~}Ww)g\Сy}r} 1ZҌa$+vr௧6d֫!}te}^{ͣ:NY~3eu;0>Me'Y"}{e,nOo!>g>NCx_ſ<#k9hi+z!^?kyz5"wg'3_e,orBm)ֳ0kӎ_GG/N=S៕=D?Sz"^p>_0>Ey4_~qSwc= zY <&{#@ϩC}5'uY>Ga)MeotS\ḣ̃XY\3֨bRT\ۿ +ZsVi +dh[R^rI霒Vg%{K +œP[?/Gϟc_\ANv?󟫜ZxB}.d%_n0VZJlN}9C\6\o9*^L~aaDْ:td:j'̜D]WiHcOg!WI|)oE~]UOoy5DUx{Bށb>xdA]3yexpoI,Zk? /?@>N XgYKo|𕿵 ɯKmc[2ן E7_QwcLk7eĒc'ZјcEBwp{bե[:NWS.ofm_)ݙ5[ͯØӜxR,+_:A=ύ:5 8X;\@;-wyOI)#.yZS[)p(e0Ӟ|O$>gxq-m՟c69E (zeO?$jaܰMEG/ѡo[Ǵ_"я.|RʹR,jk7?^ţWR,/_o>4za?tan/vK/JȆa/N s-58m~ +_CהkIwC >amAD<>JV'#$VCJoRoqÓ9ý5`( skb""x>:~o~3Oo\1 -nT퇅xAXw>k?;/zR~_?>K>}C]?ŷj!˹1I#=u8_]Ɠ+p"Ĝ\&Gu뜼7݀ }tDg*lrO|VZn<2>Odcd 2V |.*./,f m+sGgf"W!t瓇x_S$qoD{_i+:W&!bX k3~Ӛ}k_zOg_lϹố|j~~b֞cC#ߨ:utMxpO5_:c>uZ_; A=| _;,=c@Md`2.ځs>Ks{ ßod&lL ȼN">El}CϏHc5D򒜪/6.a8aC[|۬@{/&1KyU̪q1XK|Yp;.9>د[L~| *`{sT{&,nO}6U{VH@c^ݙRGzgc '{t=/Ӂ hoௐCwn%rWK%7)o*9Lx1L%vG8*u9gF^WDiY}/q{]gSH /YGSO__e_ >]v63gc|':˻e/%>fߖ[Ϯ6챽IO6i>3L錈3gqKPUza7E^ߞ=~4=(zn;Da";k%(Vٿδu7{KK| Q$~w֜~/]eαXl83u{+!GO]T|5Uc2_ߜKSOT=-گy [a_?-Ry37sV.m ^ + 6BycKn_g r\/4!JƸ+JN7/=A{ )T/WO#swVL1 qYߏDwYK(bt^2&(cϡrs;xslunO[m*\2=[Eb18^_87 #7s yJv#U3QxZX˸o=kƎ_6K5ƲqɎ .^|uRz'Cx{y<`L>Oyn#phP7#ZI}u}}ZƷg<v\k~1x^?஼.ɝ/~*`X~ ^Bݮ䊦ݮzio'h,'SnQrfD`ŋ& ?$_dTOʿOUr[DO&6ۂM!u>_x O}>bϞ{|rpbˇñt[/1*y ܴf>KS*+(LWp;t u3N|,b&f +^M/o~_l~/ފPZ?z_}/s\b?# c:I,YW_M<@v-`]+/=?!ˡLZzړ7 ȴ'B%Iz"}׉ OeeZ?ԏ}Y|NMQVy z>#2^qbzY:k_^XO_-}<9ϼ׿'7&f1%Ɔ5?A/7?kv䯇j{+m~3—r]y%Ʃѕ_l.6dXE+a"'=_8xi%+MM|W/W/vQFcgCbnPʟ5u8ߠz>3V?әA=_j/8wh%>7>,D=,%p\bf]ojT9{_?^x0 +0Z>|#{y-v bsrn:zϮy/A ]o%㯊7Zºl45s>qKq`kIPR,C#Z؆3WꙆ^4s`^PIq#qQv~j́+10eçrIkXIM5)gl5C;ah߳m=}LkC9/bN{_Ow[ie~Cpm|H/s|dW|Ycg10څ LcIz.d;v'쑓⿢=Qƕ|c9a!DR&l.q\߯g?V%ڷlO7M)w,q1oG(gھ]M]RY#V>z_)Ҟ^^/no3TwyF|'nw@_GF1հ'y⟇O }s+:<ݖȈk"@,={/reI}}jws --Y&6>_8qɇo;_.å6i/o7n܉oܮF5|N0]Q1Rm-+poW k` X|1V xҠdVcf6&s2|P69>$N]l4 Ÿafޅ짎Q:=GVy,g"._h#{{׻y'+o3g+{~ H}yL:7ۙSl?'دX^53@ymu}⯜:W-}gsd[0P[fBʶoѶq܎Әj.oeؗh7O}{L'{5Kw23ނ9HDX< n UpoʣZoN^ƶ, +,GOdx|_|lM׵?d2-}~FASӥ>/^lQ1_$~=3skco5?3.~yevN?rhE/-,|\JoWy}ʝƒ;KHMHpEqk+CT—6>‡buSC<.r^]=Γ?*ݫoQsU3z+IRM>_E|?{8;=;Qg2/-u6ryEe4U~s rngZ +B_ X]!|YOC=wD?k_7孧bY+xoVcߖ-'9n_ѕ>e9kǨNJ'4y&}/'ף,fK1䓅#/F\|DZG)̍Ꟙg[xm8Gu$>x^d!xփ5?ѷV zZx'O;j\L=DP\&nwo䋳~^cu۾pbLyn|2끰k]|com9Ⱦh/^=߰ԲK9eo@{Oz$zRl^5rkD˒}-ϔF7g| z̋Og[ ZVS3Ͻc??/ӫN!FꊾI5seOa*=!C3<&7k%,U uugmi5&-gK((3-D9{__/g[-_?O{@{Ev9~Wu Ze9%//+똕=rNHbgC9ZƖ.1g=gC =ߛ N8j2cq*ļ8;N^.yy쏦^YehQ!ύu>.EE5MbX$/͈<@ ?3뷟ۋGK TƓޏ|(Lj=o1ʇg? #e~fc |/`vd}U@r+}WK^7-y\`_!*/}"7{n?k u=Y>B<9?[]௩} ٣^=Ud?#o 9&y7XZ߈q%Rtt'O0wff|crȈ%6Q5F?\aߩm+N>ȓ/!ֳjow_Q'K'iu#z=@iN33lA@rRNoky=؈nbNÒV~ɿ-LjZj\b9 5sS])Ӹ1濏~t:N{ V:Df_Z,D-,%]|(A +)~,xY/>Xۆl~U1/o~R뙬%x m + %|+j +/;,[9,9F?Z;TϹcꓔ85ǂ>eC./ѿ "7/ewKz?Ճd%쩯u;5[R)Lmgrv7aj 6<!Ɖ}6?F /_kՆئo,˂2'V/{œ_ Eo9|q'tDIy߬'YhL"Շ>_޿#m7ptT[_Zc)?V,ʗ3v*qȾkPR^\ţ9Ea}AVZ^:{ԡ*  *jN`>Bj-eP!j۔1P՜q=ץ'cۯ7H +:VxSc̟4ѭŁ|zU|xw_B׶.w?lrn/ /ĜM3PKoy>~&&%2֌'EeZ?"{fߏ̣ۜ =/גK PpgsBo +g+z^#ꡦ= +w]P Od7A1˱OKKKػ$;n~؃&9{Ɓ>dO)"c)_-,oA(y申\9H&/!o<9jM}o7^^Zh319&G엘Lj} ׺ˇrϩ>[_|5&3:Pqryfc}uX)C'%{}:/#|Ҽc,{xU<\?_KQr]kmO)xm3=1 ?3_qgX-sѽV_k-s=%(18GĿAKK{gfo=:c9*?!GWߎߥgg[C8Z!eU>kK)EsÕ=L/ d5:$nW>,\+ 81{y1w +YD|[EWsdmE罢() +s/em}*I3K>Z6=GWDsyߊ6C=|t0?EU]Bq=o7ԼOfĨ|+%_-ܸ~?sW#޻܎[|#Yu'.j| ?(̿8.HsO"Hşaǡıj\sl ʕ9,5~s] zJ;vOo_SOu9?ǪBīz,v'm\# ^sI?T +ށ煕g&_%CҿQ/еH'eG|޵Yמ[X!xm|r~}U ?+1q$GxqV:;9xx# |z3El#zPζa5i<uǰ}0sYӋ,{_FOS/ћƿS偯/Y/Hx&[#/3W;?'ْ;KSq|/';^Lc=⽩[W\?jCG;Oξ._嫒x&o.s.˥+kG|q`.w{cWci2 a^PFWSsைWUσ\Y7'{Tc=/c>On(^au, +X/mσro'gW^TqlV}f_iC|!mcˣ$^~Hчi-28?f &|Wq x _?c+OH7츃W^c*瞧#U k8ޝ+%=SOX=b!/ջ;OW[}y4(a!x_UN͓cΗ@l.aҡr{|:X6WY7R kf\/T}>z)c= M>qߐ1YF +nLyLWe?,̗_]_O9PGƃ+)orBc1Zc=.9KUXc0oqdm|?Q_\vM R|iOKgÞFd=\%^zg 5=H*TO+XƏ2 d/Y]o~cwn>:cp՜ \Fzl֗嬿Q\BKtWf(ƋVv~Hዬ{I\O!/0e7Ƈ5{}7j|=]||e~ͯ/C:Z[JL=-KӍy!}TgZ +{t {1j orގ ⧗Sc]EzU[6վ|mO`bgs(2o_>Nm\m(9}ku鵸΢-"mMa vd84?b~LNȫgNި_9ac'/Ur:@a5w-pfχ.w9LJbM_5o:H/?97iݡN M̽mqװo<ߙ3Kr$E"V4XDK2lI$; v8FbXɃ@$@$ $ #UU]սC)o{XWwuj.xh\?vki:3s{hkAQYAkLyhٚ0~M;=>cxN,wm9=w]е] 6kF{d;^ f̉zٵkm<~|9^uͯ?Oo(/kOuoǽ:& {T_?^R\lڴemlM:{MnIDW}_~2zm\YU|BaGiy3[ pvqw\-}F<+tXbe zԮK赙8NѼ.r}~f{+EiOFzq|uW vaKċ* |IZWY/5ߨ!4+?Z'Ӂo- f\C,z<$~-g2E8&>;/mvЍ8׍sݾw5~vP}߸^qeuqԳ_O8-U$jȳ(ק1Üoe}V7ÄΩE,S'+/wCWY_.>Ԛ|ZIXA}gP^Fx\k/|W/ +ya1ajgow7(u>wL"YL'4Oe͂l;k7 +O<`&~#o+拭 \!SFѠy $6w&qVÚ͹o4MKX8/{^䃰>]G{ e`Ѱq_TO?XF.nv\=.ׯ2B賂q}*ߛw?+nbӺGf*5yJnu9~'zs-nnlO4s{s #Lu.5oG-C蒼溒v:|->ypA7ω'9[|ZK?t˚Z}}g'CLF'ƅr}E0/2}]:J@/mcs^vJ6m1gn{w291e6ZS#+Ɯrd^ujyf;^LpQ5l[UǦYu:sȠo!%5)L=q=GN|iyn~$لx WoӷmpyrK>}!]oW덍Fe8Ϻ./oq9^lK[YmH b>_fsԶkuT/՚Ls{HfS|v;QJ.ýlO3dVTxmƏ|5ѷ&)r;qi^-&d[/2F9R<H-? U\o7kߛ&V9%Ml`5^s}ה?kx\/]w}~~JB=?QKs[jo9K{*SKd˵9;!"o0mSQ<8pƽ쳇aqfYb?GE>MِX3S>A"٪sٺў^2zU7gWNG5Z8GI~z~31oVX0}; Z7#ϱMSY.^/yASQOonѹhOڻ =839}v9}Y6.XyХ=u3m"\38}SsGxSed wm^AVƿm?ؗϲ<` "Ah^\Wئϖǧ:Nߨ0kϦxcϳʽ:]{$/MJ7kݾ?YkT,yIz a6;W.G_[ 8t_r_3g!p Pa<tB/ā4~'`%`y!'dOps~#In7a`HuO熶uds^m#t^,+a5؝v3#ijNox3e eu|DU^WokC<./:$"'q" [|g#,7TmGsA=Cr|>k]jOS"!lv*ЀaJv_FfG/lZ,oWolr;_]<ƞ^;ۀ|+ݫK|6'/o>F;$7Va?_9N +- +U؛d1V/ҺmX֛V;[?UL<:52ofm:xZ_/Ԙ֩az(p3}[aso޵5r885/QnOvb'O/hFpq1m8'[ᇙVx_BA{R<`v=mARl#~io>)ٟ?5__ ⍊t !?w( c/$ KlH9הoϜ뱧al}ڥueEh677{*!Y_<*9Ok[GJwt֬#x,oH?帠4gSmo z߉<yB~YESzd6\gݬ?$n2[6Ů Qgi{,ѽ|tՇ[_S.&!:Gt/IYHkaR0_֥'L%"/{2 {<7-ƴA~:e8ojLO>ٟ_-,j}i9%z`Uw,o+~5EIJ'/Q [sHq_c/m[o 5x o\6O c\J׊\ ++rj1ϵE_ϋwc型l%t56WWN*\ ?|8wdc{vi,oI x/~w?W9CZd1XG%ņz`ׂ.kA(tG6~E}n;7˒;wbS[g>m%g݌Z:~f{f_өYoyO 8"DT:osl(jٚkV[üH)cylX'=ۊ~94uWCoAoo˔>+cu|&e,D+ez<5'/*;p(l}=_@ >ɉ:zsvpgMx-Z탼l(ixU9ܾkoBZ^ր͗*ߞ)G\勞OT]9֥DvI@?)2vr-+SJBn޹~n|<29(3[eџU|(-Oݍgn@yqp_u~?eW|Z-=d,ŗg9EUeɖ4/o7gD y$^0zz9Wes6;;\?/otY{gekhAڼy8-Ucm^xԽ f{±H{C3K|s5-Χ"8Z<0[CӤ[ 򋽾6sYد&3{{[/rͻ6_<tbJo/sSx#xGu]deF;[2}]_-:꒗A⯍ )h_JP^ ~jZ[oUGIff?|q]kסŌ8C }swV[+ł.آM?C8cח^8j=>kK})[^\.czؘcoi7J/%*ߗOϐ>K]߃;_vlU׷qE>!zC.᯺/&u0WSDO>~u1Cdî"&sѯۻZ۽/Le쇜xo%_;'r-o%+_U{B7{}QGO~׿p+]I5(7{,7Ů்]x +cO[KyhɟkQl te:w| +؂PW뜾fs +=XBܞɹhѿA6??3蒽ֈ6B-#>?mkRهMb `1?.{ %J\O_v^Ќ |f^OwgW_^^zauiI@K{oQhQ*3)v XOidw?ZK!_=-֏I1Tfw$JzxMGkME?fMæ]bw`Ӻiߦx2}7ǯ TPG'y7ʩ}9D~q[:c:d/b_ةW|Ü`We Kgݰ.CͥQG&M>:OvU-䑚yܮWӬQP \[ᓺ^b:պ`8KkOQ,| B=TCN+=zkX7eO2`+YpʞߕW1I $duJ,D7܍:1vh y|ZA#$x{`\moN&~煽X՜'Z_0$+Y_wP/?Gow\&ث霛u=]gxE:Ѷ6cϱ6v*{|gg_ϰ|> ;W5ވO - PrsMv'G^}삵:b9ϙ?wB])x:#Wq?(L>//{]73# LלYP?gotZc +"^',~yWj08^f7/z V'q?6ޚ?"{5IpmoYC zlK[ڟs=P[oRٟIW婿NPX%kL~З1ߊ%,ߙН񗵲_S&O#N=Eoj +>6(n[_x=]/79e2zSZO\e?ׯ7> ozꗘy.ߌ'(9q5k?hG +69}Us:F.hKs}[$[նCy Z}}Ms~K2kjR`מ18W>Kku "8eCoVl`ɡoչ8dc41I~'$jߍrRw~~G(OLuOkuҙWhwg 7G:3j}FDŽ5JWrw%s k2[v8ސۋ +>]V筐4~6X~2Lȏ+{ǫPϒ;}K9|I"Wd'׫^䧅6|KOu3F|\KE#ҩ5=<#'׾|82bwn[+oI^-D5%q|洠=OcrHyn9SR:9N{duʗYfM=)a&~F|)[zsS꿙}B$1N(g&;ʽ] _I>po H,<܊OV#WAXfmh}uVA5\o[{#@] k..o }0{:ADCWc~7ɪVgUzZW㓓Џjlv.Q \V}J̜uW'ql /gϳɟ xB_1z"ͮ[mn31IbPXެw{,O U pݝ3ү%c/鸘 ]VzLz|N=ky6%~.Sycf$l Xmf,:_>}9 >dK!:4}1h~g}-g N#I׊}߲?}7h$Ƥ u嫅\;{&?Ig߃;"dk6_ +RňiA{5{z+i]tUp~%|8%M5~7Q +kpz+|9Sn;,%VPre!?g'XSIl6yNř=Z=ٟF{]#\?\m6Zx6Wem{8 H~ <c}~sQ_׊z)l6G?o0 ?N?>AkӔ>׷[/ܠ<#/|~%tZdB#VZNqϛ.co%= ;y0<9+J4:J-n>#X҅N/kyϾxۿ$8?>WP=}7Ü+=7Z]?d~[氩vJ~-3K9@<_z}rc豆4aRq[oǪ<\KwH~J5gK1┰B ]5RZgyx sjIXy꭯S2fM-ޖ1ja{2'۽Lz ox +s,F>ƵYX^Ei l{H}j Qvrj!?:WoI& %ysX4-L9JNC%0q1^z%f=Lc|kӺm8+c=~_ٿr \:ØjDMZR|\ XЃ~z4=I{=I]CZ)U{Kߒ_[{ I3hP:ak~.o"nᯑ_]𭴛[pퟆ{78Mo\uZyC ;3bjE`]S~(9X f\Lø/>~ʾ|.EQQ.gy3 -Yς(T7]m%5aQZW/>/Ѧ4K+=*o!p+gKwj#li{7kTS7 \\Z䧫S_+SAT>}V7J6/4 +4s*.gfϲZ{RcAӑ;χc匕!_ί(G>u +77;OW'޳Iv1Ln{/a +r#8SA?Ya]>5gnG.௓`dmb/_6z9[o >&W45̒u1o ?6fq_[_]ehuIy3 XẊO߅goCH9& ߚ+ϱUT[DŚuMj[3}DH}temc9B<$S-3cc^5zΩB"gúF[<34VCe/:u~c*s;3|zuVɊ嶦znNg>.o 3?gշ:>վz=gZZGi1_[CubmKPK6rS?]o1>[|4I`2~&y96oS#O> sl{+ 2,"ugTJFc%k!DNV3תoߊ~%\NEƾe+gohz[3"ߦ|xpru>/7_,q6kKĸp3,d#`IzuQ۫UW~3u2u"ٝAqRhouslxe_՗B_GLClj{JhCmQi7nrXGqy=T RrD_}ѻ~OO29έ|"Z y%9wxvS?$xi1³jkxq,.t<݃PüC//்P/'aWr\;}]k^wZ_ +&Iܪ lbϫ9!Wfѣsabd w~)2xpwY/bmozVR/W_dk[nm,6o[좣xnyGߚR`Gz4:k,w9&ؚ AvסfVԛdǿPΥ:NοOL*_Zz6E7|z[V뾮*|kWOWz-c0S_N=[l +DFpo5w}SRon~m&,n/o_u=zodoXc/@ kUoQE`+%g3]SY̎;kOfOL(ceÞ/o܄:D"K~:_؟'F>Z woYo㍤|0'H?`)}{u.Vy@;nq~ +6 fTzQfօ\T8Rޒmoxߊ.czu؇,uP?Xo唔6K +o(~/h.yׯس55me_Ze>M=v_Qry*+_'+BWLQ+OÞS/{99YpA 2:)OmIC|mx\^"[:Ox[Ƌc9!Z]fψ>,WY[u!Uȣ)' +~k[EB^d@\U~5XyXZc\_8E{V!/c=EC_IرjXw_-*dO^z[cn~Fg]oN{WS9%7Olݞr\Ͼj?j5s; +Ηq>}oc v3Si.kajMոI>5[z jԵ˫Mg?ۣ0b܈W69q5խlw֜El?asإ3}cu Þϱ驪˕GT ]o+H3&6/@=.E{Ʊ[~ʽв؋$7>)irf;_^o6xA]z_x74`|l1~:ƿ&| _$v46_k?\xҝlMa͹:K$>XI_9g3>{|~D)|qmI!|6I__?ԃ0J$'~^'1'@IW%CyGͳ5lwd__*f]ğI~a=<[_ӫ?G3+}\͞[O'9kk+٨IW;'O2%?^A˾0^ֿpT}$Zt{=},O-熶4!Ǻ3O}[9TK u:=̆-WKg_ħY'*+c]er}ZEvkh>zyu? +\\܌q]eo| /欎!6 [ ֕]ɼ5N }qNw=ה +M^#akc +{;b=e}w"\|HUm/I(և)(ݏ>5yI {q4U+EuȎ kì']/Wozv;ELlgn5VK_cs!܏g}- ⯖!<{:=V/ NO^wf4-Nj=2]7÷Xdny^?->wMK jηJ+&`sAw\=#LIk>~rTkxPKv(|sMrs}kG7 -酫Pukn7 |W?_M؟mj<{{_^àku6.]ەE_/i3ݿ[p + +[C/we>UND"~4x(۪XRc?o~>mv^}/Ȧ̳8% =z%j3?b7,[?kgb}}z;< +=<5{k.P=]SG; )NKs>zI> eu\{_LI>W#?x:_oןy?7e h]O&^6_HOc_coZ;[ٿB3TV&bEZ)L:7H ٻ?q| 1 Z1&odƗQbXc(eoْ##ոLkm\14M!~9v5Y~xY_CGe?>»r<~hUBߞ_-TK;/؟y89 |]!E^jd!N&I`,:0b*9D^4_c5^o5; Մ$8}aѮd[{yu: +i|:KrNjȪa;i_ZϵiC3kǵX!}83|ݜa'ӸC>a( XUqi~:WH1/I(iE~ nnmߊ2˓^V^;j߯gYhX~cTY [I_rڈgMW[e1g7ZM…(E+O\%_PR8KV{@^kƧ }^ 2ä^$rsoܰo':h?L<@oFyg>X;j5?K~uVyyn߶;>io5;In8N}7&|\S'/^߉|B]n3kQyċL#~:f{!]߆/|[WPv`F_K4t(_}rvCEY,hQ"dM+-Jozfe>:_Fy%<57eiٱ,^K/m8nS^uOp|GinϡGO7+?C!Py%2^KC+`F3`Q+5>\U~Nb +]6s : +k!$r}-9>77SXGdC9 깸lZ͒+%#B^uȧG[(?4>ʣ'_W}.82b}O܄aO A&` _/_kn>x|OY~ܯݟ#8ֽ/Wn"$pci?t%1c'3tnH7W|ׯ`/k\Oz/z,zq]BϷk>7T?2#_ujoB8;VF)BG58O*Gj:pܓ`uVpM&;n]k7ӲZO--61#[/]4c^S)~Sulk>z I\]oF$h=^< -ܫQ=1 ܺqJD~Gyڣr}m!`m]8sHy^Þvxst(uFǡߎ4繱o/`q[%+k05밆W|4b?]~7^#[ĕ}VOݠZ(Ƹ +&/\Y8z$bS1IN{W%ݫz>VR"w3s[i9%wa >귲'!6GL=+/ys&F\w>?\vB2V6ןW=s&]6{u j'ݧ{M Dz Z&8};XtAd4b=bAMR5i|7Ķ~Ԕ$u#?ޭ? +W7DÈ.`X2olrWow,;ՈC8a< ώs >, lL- '&]MύgW9Y3|>Ç?CΟwg_~\γOEj,(}P'?W5{etH^ ^/>Ļpgcb,LTu Uci/wqS|\Wo-n oWy-bfɯr>C /7>o>\|Dg{)yY-x~s +yEu|^s"x<.gz)~|*w%%+K>t%IשP{L&A>̝nr jG5.< ނg^G]٫g/׾8<qxmYfߏj gUboCa ̯0?B5kh(~Pg=8Ӽ=@Z_ oڍڳO½OK|w" 7fMgw?.B6y"tz,Z~3K7rJr[GڟkVy-2.gUsU}Wx1+I1yjKŞuIh~2O}8ݯW;_:5oMOmWj&x!H[ꋴd>ڵ#XS^3p/~X cO{msb]0Xȱrf\$ 㿇ŧz?(rr%(6հf_WxЏy +䉚/h,?gO=̅Y .jKlԙ?=H=Xߦ umO5_4 *ESmwl?jM$=ǜ қqmn}!kߔ@|׀L xg[+Ys{yf<ߎ=-Qw6)\\6_Zn~Q^Q/|5BL<%꧄6o_D2 c tUq< %X™Hi߈GGo8.ǯNm6*˔~mԁ[y9{ozM]si 6ǴgJ>;e^R?~X\Tňhn=poԅ-oi[3"3#6{g{ +Wyu/`/BLʟ >_iԧʔx!5 XeiYǟ5RU~ۤkh /xVag 4u Օ 4{ +dx7=F]_2 vk݉P]!; ̍z䰥x;ֳG|9P[}GQ_8ݍ/G}&\½҄c 5-k<ѯrպ= sMFwpԃ|$?ShMe͇G_pOn_~f׷+}Ţhҏu7ָ'__G_G^˂>+⢿Zӄ{Kܛ# K ^ x}v,R-ӞxwYP_+oÁ}KM2g c;fFد^u~)~H-?YQzR.CǁHg/漰M9YIs&R/lkցR^ kIh z7\`w 9CvXC]9 6ea>/WLuϡ偦?n/rÓG.OT eՀq]w/ݎA_[#Y;r|ys7)c_ZprT[sx^u7i7///~pqq2^jICqkߪ!W);^BNq)=xbLD*K13sJ:Kz9^OQ~G|6ϟŇk92{(o$bڍB~)jXN|/D] [#Wa/l֨F7ugp?ltv}tvƱ5H,Ѥѱ?,O:ll\]u';S)z e +|L {ìYEi/gu>{Z{mžN nKvp]s8]A&Ц$Jhb3^5l:xMq6Pݞ+<yW?'Ƴx_ zKGS2jN~vX)ߤVdJ_mv>y2(lO\Agc-)ߡju u `!x=1?oG\S/NN`ÿgk=\`=X7kcyxm9kM7ғ! ؄c"(Vd]⯕gc_\(lv;8=翭NGgEzj-VQߥN/`}QK[dn+O'T U]uk!'2ȹ!_Տ=TKc흑wꃳ@Dv:Mqńϖ7&ߩifx^7o?*Ul +Ja;ǹSU臬0bq55ֆG{GL]{Nރ_=^pu(Gz!}=Zd-b?7@9Ŏ†0 }V/c;hyagk]ϊ~% ?'4qt \]{^KT,V%WBa|:ǟ|#O~hEo`MR+ SƸ}9UkU~kBMDy<)LW?#sa-C|R۫~i Agҽw q\B/-u| "y1g)fuNE Al19R]f1 zb ':'g/8Ox}B} n7Wp~n.G| .}>;q'XG_Pn5LÊgڟB Vx͋C;##rZ C /㟇o-Mߣ!Ko[Kp&;F#0Ȣ|4K>!Krj:󟅟ọ.W諺NV֘1*߯.}Q_Ks0C/Gs`{_jc"9uE1+E<տi֝x|xlM~_*CTholEۣ x㰗|]^9/0My>sqϔZθC=-|}KsJ/"Ƴ +zf%Jۑ'5pD{8[(Gspxdۓ8N`;)&Mk{W7pjzaw݈}ÞJ(;c[ApaHt1z>{bPhI ZW]KTGDgk8%}N 9k_~K/l [n"mR +*Gͤ&u(?,a|dyKE;KkFZn+:i\G+GUmpgZk?^{y?73O*1ꁈXWP +l9b=ȶ V1w'9y֑~:t7È8!8p\SwkQ[؟{×>s|} y aqr3/^1./X"/LZG@8~O/!)|i khX0 Фx{gxx,]p}[":ߊUW%Fi)N/{Uϔn߹ݷ^5 F~#5bDլW>5x[o%u3v3_ã~vmvZgOW~}խo}.os9υl`OgNՁL"0혇x';pwὯ*r=1^:neqiFhQO_5!?#ϣdE-[W }oggI?YMմѿ>3^GPWo~;*|~nO8vZg?yVc-ٿDd^)f^Ӳz +`^opP9cWp5~\;Ѵ\:a9r~[(ETggk;a'+^=囆xFk.* Oy{57]'ZoܮN_  >$lWR]f3v  )/tވK֗pv.=kpq \1|,ux|3pz( lG ҁQnwd/ǚcgP`^c +}ކ6J+I}\)s^{{5(&uk2o/T ^"V[Oռ8H֞qA1bSnz⯕חޝW'>p0lN"M(Uo9Cd룂FzܝMW=q6':v{SV麼.,CHnO@=|a^ոUB.s u#{bY++s<Ű]|č}h׷G<~E{ɑϚ{0,A^~W? ??o~7~=nB~Q`;Xv]LkunՎas>Q89bq1ܾn܃k3y|>mxɧǰg֧wx6q>!_3b-G&&csF5.] T%-ՙ_oW o꫰=Q&>y~/5ɮ$~]S=W8f;3%~v,7^ {(ME}.y'4?_γQ@b}8lvt1jw+57TD&dz9d١@hGjtg`]__=rs~2KZ/r|#}gGv3Wzӹo[Lcn|X|,dao];saUzHYy?.\%{|Z ҭ: ]'X>`plh;V䁡s zc:q ~ +_5+~k3oRXO$ь,kuV֫]7¸8;{'prܼq>݇g߁O< w_&o/ow'Ex;߅ӫ+ys\= ; F^A~38+W-?,amh~У\s,W4-g}vvo>~E'~_#P9= |x(=Iy|S1AiKΉWG7 Axgp=)JgM [g8uh'l|/}`zCPO5>Zފ28e0hlHhH0f_W-T=ue-%uׇ‡]AK2ƶ9p.!^A&6ӱmnoSOvӖ\پܺ.S;Η2Uϲ>HX~1pܻ q./n;F}ރgހW^}O~n>yܾ_]m؟_r<;g$`-Ρېԓ?1ڊW? u4;}Aub- 8LI~V9"|7`8H_',~s?UޏDž1==.̯e_{©9-f Y%G_]o |֗>w!wG֌ U'{>޺dA`)Fw1~'m[KԆ[_"QW\c?l3˘!U]Q,ԺuOtw%z'6BC6Tmԑ>{/{ ߄v} 6{y{GߟcO..F;+vyn^7޻{ނϼE +_K }y#xշWpvga{M!m-QQ ٸ󧺒*EFL5Z`7םy pOiH %^KknG@[|]?/+tw50Ogx[kit Z^f+{8[뢬ee _qzs74s,\Wan?0\[KaJΞE./rTI|W3H {[S|=Yd\cs*:Sck5۲mZ7X&:˱/j .X}KާuwƹmW#ۢ&;9{O _)xp~~.5vq2WkO EG-'~ ~#O߅_׾mïg?pQWwnކay6sk5q0ǫ\fK{?=0SފcC+{I^W6=)$]Ob)ߟƇ<%e"sV>ֵj{I?+I ? FKӉ,u97'gpu1 ]SwwWWϋ9M>"< ن3XCsmp+gF<@5~ tD]k~>[}\h鍑ρ&ie*arzt|`z^tBO -ZBw(Ι1u菎@*t\o>Y#/\^KQVdq?; $çj#T\ P3Qg=|C댿L QE-W< xxp2땯x11!;)>o6M>xħٗ8;"ȺFeg +SQ}];| +ҏ*N{WC =iXLgaH\oW^,+ 1?r;| rW/W\ +]X' 9Yޟ+st +[axb^ +|YZbe {s"lA(~YG"d9eHN=mPtWfx~v:t=wvpG^8fg~ ?Wo2  +>g>7\y=Y8x)X}1<0X1{D H{+#+PYy@{K{ErE+i*ןSo=`>w{s5 q |7R.J sMd_ID_)( q((s|BM~{x֯ U/`qB{ejXn =GLF"eʐo}jG=Gs9+Fً yۇ⯉['/W`|E7 8)-p8$Ʒ$2!dOɮ(;7c_DT̏ϳyυ9lh>N88Mۗ|z˷խ(҄/XO[GF3A}!&rcoKQ/( 6\.0Y˺Ig_{nTrȗdOFqPk*\un( `7{o33(ro.|Z*}Kz?1c>Q +;kRyb391W4OWrBXٻ=VU?t]:U9W'a3t&;0oP۹IX[=GnpccCbJؾ_/|G>C [_s'/c^{\o97]8g՝y/ +7Yi)ǭ \PRrEtǩxρ OCB60\`>6G,5b_ϒǜKY}DSHYW7˸ơu r,S~MX!r3atLJ?; '_ +wOk~?|{? gC`iK_4|+_>xOË}>ppp:xt8? ,;w`w6VddЅ:L% xzߎaJ$khi(֑X=d3qtm\'MWbl-/G_5+D6(>yN/5eZ\̓e9qU9ڗn9qY R|,ӟ^|LU`u~ VӮϩ/&?[~$`6ޘk1=0܄}yI{b(^kuXoSc]>T`Nz%$^i9E}Ɂ|jFV*RvJ%^mJӿ9"5~VO*Rت׎>PJez M__K떋 ׁ'Bd=O=sd+;05,>h_s8Wf~sf7SXhV+k1w{wcΈ7XĆIi;gk;&Xͳ*LC +ΞMzjE,/`ߋFcC-nA>#76#z|~G? ߀׿ ?#|x[Oep=WKKpA܄_ZxݮG[L_{e9D^N2f)}="s2%{R)^>:oS*;CqJrx;O}?k{@6=_rS[-2VQǟ97Gj^s <ИlK <3y|/;T /'/6}0.s_ŗ?P^$gm'ϗ=/;A齛_s_Ň !u9CF7.ӸEɚ|!+Y_Sdu֤bs.snl׹Tp4VO5ܗ&:&g oU TUc5Q`心^zCa8wzM߀ +;x$8y3\~O>$MoO|Ww'g~?G>ax +O?_9Uȋ*MΜ ^xp#Fb#ozB=" t͈#C)ߙ(xA7wju%ǎ5?EƁArmayfS)td.VG`]1\ck񺘛XXXuط{O҅[7%my>Û1xoz y-pY8yl?l_ ٘2tFbBOYco2CBE6U€=) zzu{8./'5QD}@e%osKq>M7B/7$ʶE7h;~)]'KGKrjZTVhA]`_ThREIwE&`WPon1X̳7~ B 9.k_[ߋXZT_U3[yQ\ȿǦk(rs@c|W5ՅJ|<a /@\~wI1s.BonƣM`ÁÇp)Ó?xKx3;}nNV> 'NuZY͍tGv ]šd*C!18+ra +X= +f2Yc]b<qDCuLEgj8؁U~|G;͊Mx \5㯵m+t<tZ}f7vjMpN8vZדG;^hNPs239 KgߦH0ц5qCf>qWoJc5d oT}UtBwrb Tӷ9(fwӟ~,^+%8l| +HGVyVC>CV%i݄`[Onker :ŢxE`2&LࡃbÍN}W|o~Ӱ# Ks <`>an +,]"?V2vɉc,m2[PsJ1^j(R ZXj756T4<*c +3TmГU^/i7^x7e +llZ}r}e@Y7 93p՗)WH%2Wǧ/lJ ]?[~4l[þXWcWyǍ6=&!359Pau{ɣ%X. I_c>8Kw >)ߪ~:=>r'_9p\2Ip-v/4c1.qquz5.,Q}TY j2|JNKv]X\ֶ TإYpƋ0L`47xVW}p |)xS73z]~%:y^#.#os Va +.E(m;c)efT^k +*QoL!DZgbTY/ϻsS@fRWbz8pHc/~#5&i{`k@~rċ6&ѧ={G>e'm;s}{ϧ_>np'tk~MD-w4߷yB?` 置{Ÿj"t#_Vݤz%P%JL]NwRgym竉+~Γ*]SsBzȫIF0پ>{!؏ `p\?!`Y.>ˬ pE>%=aiD"T7ѽܵmD +K䴲76|qn+O!l˔OCw ?R4.^^T`N9ȺKv%.dh !9cX__}p~1x3#/7 kassa29ՋsUv ٳ_< wdL.ڽB=+vKρ9CҮqǞi|[/ߛ?u +}SSg@Asqe~fdTbq+ј.CVWŒdܷlM}i$hĢܽ=wI߬12,~i;])Nѷ5/Bg- F ?\UX[[Cp٣n~vN<pa(EX\:&  /iBȟt Mq;{%X!,&d3͍sSa-հy:߾L9$n UCF^Ī[cd/vЊ\7͌ #;}S/yE`@"{d9<&ėW}Y`n3FzAm1h*޿*<ܵbFʞW9iI/ o؁9rKPH~lA`>[\v֑c0\؀G޻7?CngO30^XT|gq\1O1#,A7RbzP.(sRΑ P +5oSMe;RY*>1{'5I^ o\}u!㼺o={0퍩19N<|y;sg_kJze6M{d/Fƀ :Syn+ϳëʯkX_cxO Ky#ww=73'a0__bb)U_w5 ,{ )֋J?v<o>Qqu_W6g|$h/ƪ+Mt]Lm|u%T1|v3jȉw*;؄XC2G#0Xg,.‰[p3p}|:xO@[N˫kC>s UO_=!csSB<k8ؾRtL^*_^NLN>_V6N!/Fͻ6GK*`}Fǽ KKB9 tn%Wj$x2QEgamH_L/$v:&hv}d/}Jfג$/Ơ8s:h[\VWTVxD ~$9I\<-ϻ Reܯnl|}/6sj>_Sozz句:k\Y7cH=P$'uD&{=8(; 'aqU8{{G۟?O>?~G_Q8~0Y> cu.|}A\Z(Su ǾY'K<.;|2VcȦϷs}bb_XJ`O®n7ixzϼvmgmS5Cony9^]1a2ڄU8;pwnKpmK_7reM?ځ%a/]>RUNPyvR䥖IN~j-\߁ԋQ#90I^L6#X>$kI^Ẍ́!}|Gc70:4r$ٺr\"B5b?!-]}?g 9ᣟ%w,l.xSo7pa~yWؑv؂FtU8zEՑ&j.S7izfUSkoiߪRoj蘔H)luL14gk:gIGd! JڿWџ^< kb.Ё prƛ!y_<"lW .C$`E1j po+ȼݩwY- ̗Ú*)7!8KcVNvyΣi_Q8z2̯WAcn|4Isa7F1?_Sϲ  z/sǬN7`qm{_Z##WmnWԮ&ІS.ZV,z#R7SׂTx~){(ܚ]i؅\^õ ?)cľ<h񠰭Vι"0h +P~_;)\{~W7.huսL quH-9&!v'?dAG`pIL`\}ڒ9vkZA<$ck%. + +Q֠/lL/H֩>(hrnȱ4zjqU^4ׂ&C/6s2^+{FSqkekZH)y>d4 Re|bg>g!+&gmtVZ,_6ui}+|d=ų11Bx~u8 }|U:? uL]cu_2ׂ]q.Ϙeb*u)>X?z<[<ŧxb 'Ϟ׼M_} N^s6qDs_,7f EcgiIgu-gfG9F1T}&Q~TX]&]ȻxaNwC~IYC,>_at Yxz6tmawg C>9蔰w!ex]՚aLޔJIrvklKuNz8=:?hJum={Չ)&+?E-?#kiw/%8|^iͰ/ʦ 㮭> ۑc&VZxGʏa +a'O!;|NbθƉcϨ>ۓ=1e|!|5T~!Un=gķ$~<I؏{tW3"YuYeD o\BVg_*zo?'$qS_ 5Xc8-L`nGw?n{>S.Iۿ{ů׾p ^#Sc͛lryP12ugCK^Zm'wɼ$VaYbmLCx=7l{ ޽/7K;:i-PEPXkG5،{__qg8VuU͆yu_]}ܹ ܶoS\n~o=GDϮ_mK]7߳^MZZo3MYׇ+H59 =K7@6<(+l+T9إݣz vDH6^>E_9OWq)~G/?v?2\f| Iߕ||v9+UKqU@s:j]ChNkj,v߃a},AwĽS[/!*W5_s.\ɾQ)3QR`t +%sg3-]_t<_u -&O0Z'І2 V[8~m^_/~%c| SY߮kU-0V Ϧ_7v-Zʗ&"'s%պoK<$}W;eSIdx9V)OMk^#}sNeo>]X?r~ ;:kdH@4gB[/;TQouTwiÝ [Й߄isUOuL셥 xpY[Xe[~F_=9D۰V?WrUJ +~J7$F/N\R69ir>DNCL1HϦnltw +{en&lMʃP+No G`*Oq[ouJU^lOAܖ )Ty{{^ +{?S9woϒy>8yNȳ< :%:&Aυo?~iXZRes_lԙWXJee@T Can2"61낿D2 ^Vs}Th[_wsk= [$P/IX\>1k O=zyT;o=s;{yM_ڞUMǻPVlG7R9|_X +m@!u.qp4wݷ~~+Xe^oreH곞cs{fKllx6Pw"B*cs4?K69PRƖw}aW=V;0A; +yqY;, OM\_d©~[ȯ7|* M,-+gɯ~}/XЅխ[șp1֟Z.zʿ?''z`c2ޗb$f9_m%QWحoUEq~RǑ$/=gt45ݙ287br}›_U;䄊]>,m}nY\;12YةG0x+9;2H,_E7z^̧V\~d^fޖQ{3V>Db2V,Wx,m͎>oV +C|JA< 4SoƮe}3oco䜖O\{7X?}3o(ؕ/&C)L 1uƛUT R7' Mz؛3`zs,'ڬo᯲eez[Z5r$R{nehKK`8êޭDפ.|/|# Sm/$r~ $2rUQU|w7}L^_N$ۧOV1wb7?!㭉̌?k?}k42_Gbc7)ΑPyhϖҷ+{=9FFh]C "]a~]NSi. dU{8+Sy/E[Vb>3/Ǭʯj}ipO|kߤ+yLs1zZ'X?//{nƒ6Z꼙{*oP>6G_=_7̛<%:o+e?;tFiŭG5W!\rho,F]~`}l_\UP=._>W6Jum}?廞h Z{1ӻ])YΖ{2{/~X*^H׼Dfؤݛ`jt k+aS_<6ҍ} s_Y!qu)Sh5),K`)2S5uX4LJ/0#w +XXȳEq3Jf~Ը,|T5\^D_4ixSu.

    &&gZ]hǙٌ3Mb>{uD]kS-i=9Ϡo_}`0Cb'NZWkռvXrK2!Xn].7e[@oŽЛܢEFF/pwHi33Jn(9rgn({X/x"PJuIk_}Og_]CWyʟQ`\*'JL:Q7Tv7t5ﴮ(_pxot9gFma'?2~^^ y2:d,ӑ}~{oIT\Fj_<'<7v@K;`2pu|N=!Ac56u,G=e.za.WrO_7st&uqzrߺ^_}<ƧW# \):?4-AƝ8y$i~ Ԟɴ<r;E:0L?<ӧMYsb-[]y&c:U"}|8:k~6kcFyI5*_2qDl3ɯS5:u>7zs p]`g?%$emaPtɹ)5Mg2UX=8c7^R?|nf<U>4]͵`zq U]~bA>hWbg +䑷Jmxq}/}FΙ](Fl˺]d y&ȣa8!L\Na,B0D=\r(~ bF~#5e6`0i^3e*`=~=ޑJ gגG>/c]GQ9/'j|G6vwmyߺ|?~6ږܨsxk-2Hk|>甭IbƟ-VZ pTk68$%y6q,P-Ğj^4Fg#=f_MSryٳXEz37d͋؜i) X7T}e.Tyw4_zά}){Vʫ&7R5lf_W?0ieLS_a=7>5p;'5yCȺowʎL*9o ?IވJc]s4עojI0$1:LlCwÐ|L3X/\ĒA{ijSoo.*/J(a_3*7,|N3iޱ=ݫ e#-'Eʾ缕x/"L~%;F|st| ]W>^yj쳤fVhGƎOXoFX/9$k%7K|2Y%ߚ[RPwT[Ls[YkՊ! 9MR]OV^B\ڔ`gxZ׌˶Zz{˭/sx_lOw6VK'֘] &X}=bR<5ct :sS~j}c=3RΣLou/WI "+4*uTy蔫8'aO,DbgSb_N =ΏGga񞪦(Kϵz߆8U?MEطNẕC7T5;_j[[쫼dKMu5_x,bjg]9E myEҩ(|~䃴M]mV {ku90+ψ*Gox1.BfLgҊq{ᯣHZsi#yP04i_}qfeM7K_]o?}*lG<8a}^U_<&_߿p>1@q67kBL˓=/Yor6/&_Q-G+No{TᓐpFR4y>[RmOa/Vh[k/`Ln?Qy +gU,A7>҆׵B!75_<ub#"skS0x''2VWje rWY? dou9~&5϶^J/2qJM::z/Y'O'|?~VmD.WN?88z9>.\*V4;]t7ORߏз"[[5B+^m/>X2B{j{>e׬Gb0ky w?TYS#.+<<'U_@SYUG%'I=e#><؋KZ8|ht$:cp(W*[4ޥ*@sS䎕 +dq5~{D~ OXd*w7˴?IZ1o JPr0r]۟km`uR`7^ =MIu_~;^V'{_9Ar›)rxN|vEެfnq7ڴ\&8fs-O]6V~^$>a7wLoM }PO|کN$ߨ~3> 3T/{E^?6 j_v~C^ar-ώMe+?b6`%z} Sc~*6zџ1oaEF嫬i;:gWqex!8ut.9}Ubͳm.S%xз3 LV|,~tC9,eARV{4R6_點|s-"輄.aώ&W~>Cg΂3/48خ|ě[ar&{ 5+,6>O~zoZWO>T׀/<fZ,}^`3d߃wzNȻi9l޽']]֞xOk޻9\ŃgyGo?x'z'R/2??*Hؘ>/g$gksZ]^pLF9 |'~.sZ!Gz%?nDGf8 Ϸv Lޘ*H~XlbqO`þi@>4/tÞ se>دΚ%xC=.-mE<>6#_W_`gPy8~p+>g_FH@$x7jܪw:r +>/c_ڄD-G9<#zG%Nyi-=9GSz}G/&G,?|V59L~=a{y|+1dֆC 4K{74UuUuF0{;6ȹO-b:h׮n׼|,pec=Jy\㦩aLxu>mb)i<`'j^ۄOqRNx ;O[k_o`>I6`];xU?k`$fߧ|3׀|mBj_Zu+XMrSu}߱y&ѳS/_[24Az(f̞KlVr Ѧtr:#+ +ė~ׄs!}q֋mx1gȎ\ΟZ ɞIO MDA y`ׂZo{Zչɝ!} fi5&ӤKI/?s3W>Y>2=bbߎد'dYt3#ͽ!?q##:㷗L͉#/r׬9$?e}4ƇfyPiHPF~Y I (|>Cgy"U~S͉79FItMڃDR|Mga~ë)7&G@啅C(YwHڞf߄r BRjH hGW_]ן߼9 A~RFk?=0WГ{Vg y7껣>(yB6/GKRp|$,^G_l*F +l˂<fS?)ɺ\Mo>gt,Ҹ9so1ҷ΂ol](7sW_Ԇ2A!]G'ʖ2F4:{yF7^O_OߡoGnj{[/c;M}$n|xތf=7{?p& ɵSm3m=^}lr8ZݐjNW{b?M^E`+f_f/6>oڜ$KX_5|VsH^ ;(rf_ b=mj~2G! H?ս_$,x#On`n~5/|3'[Y2!'CM\71T\qϵf_o||o#?c[O/ (N8x|_ ++-&RG!/l~h? +qck,K(ֽGo]. cj8"M7jKNwt3">W'Cia]>+翯vV:&`ssxgϧgwӬq<Ϭ/$.)#uiٟx~R_g_%U +OwSw.8>$goƟyZ),15 lDm?>!㯱(׆^{х2b@'0G>ן@sU!c= 6:NsFz\TF^U|rh +=A&@X9Ga˿E_Ϣ=z2^yz6g +wʗo0-חIsP@NaԽZG`IcFqdgwt5 z'}}}ބ(Y74!}kmǓ?ٔ/)qS o-#1Qr̥U/j8 G꼿\ ?Vܢ>u񪕯ͷ(e<[vemq'v|j?,7(~ _V8y#+$q;OAR$gYI>K- kn \wgR0 kA[颟 dOwZοxM3^(?>}_#b3oY;{_g>K5 7~r{e>k_oM_p݉o_ |%^uCp;}q;5sD{W6&ZƳK>d@jѐr-"+vq +(>(_"3\%=SiMx#f0z ~hLF}<5l#}=⯧o`u?352fl&7͌fl] ;m__W1Ծ;v?+\xu_}.Wr*=І{?oV9s8뀿MKrju?ŚlKC/K\>SNBK&~W4'˙e_ǜ_?oV3~/WSI2{hBVe5%[Z/f-?Խ~mFOr>)mRfu$w %"3܇ +s(9;ھMcwDp֋;9:[:P‹=P\?3Ahdié~3TOGZeV҈}Ѫ6ks5>xC-ΞP~L#+EosAVȞJ=;W\u4 74.?Er>j_*T=;}͊R$Ib_,k~Ä_/Onp5,߫zDsf#pMk%Z.>v;1 +g/n5oyiឞɼL8crǭZ媰6S^t= p-gOlCsݥ>U{-Ϧs̷;k,gs +&oIX8jqlߨ$}|#w/ey}u3?6f-Uo#Ak ﰘ'?#}[{Ecӓ!eo,t.{;߀ekڞTWj9v)^GgA 썳.NY0ya漵;n=|S J6P~xw̆N=6_J`ئ? sjśy{o|q끿 ⯍Y}b^~ffUgqi{8%Bْ%ڿ* :=6ٿDE{iM<3b:I=ӷ=x9Rw.^z!t卑Mwǥ+uEgb?SLw(1~sf3EפtDqZڦ!F_ܿi~͙h + ;ˬ]+xeB;5vV_3(>Lam} s`|.Or$}~Z_b>Ok7JsS.߳}n~6)'kOo~zѶߌXgxThlRN/~I09(ustQM|@taiu zy?N񩦤6ݷs+u.[DX$)Sk[sؤ?x,~?[?h^<7ׯ'^7V!}_\omoψߢi?5{=|oCsy}ӗ_6y!e6D&XoB]˙'ܬߘL]??t}N֥~_[Tx%ڹfپ خίb=~ÃaZ_3&+߫jL-rez1}g͛8kC|1qR?_3Ҋ,ɜʉiA{9ʓd\R3oeYzgƎ͟oPs7v>3aъ _u0w`lfg?@+/:^Ҧ/0=[Zn+_w]YT 6_+gA]_jO/ɷKWsl3N MĽinUv7nu%[R7HBjdMaIHHB%l 2c1l3L +.p@UB\?J2P WRS& +gkZmc9{Xk=kf`0a, A 0qD~6}?sz˜g0q3fV3n3c=Ź熁kVaaLy6?Å5gAدE̾k{͘1g#[&/u\g7m״.sb2e ?+_l<&9/7|F~=z3h"O;Y|``]yܫ|qM|Ht16'&wO2(<;e-vh+b~|O!{-6;}&ܸcKYGbkzhұ_Q>2F+ 8ȏ@~(³VPǯ?W{ǩy>|"m:Oӛo~uga=ϯGQ$|nc񴹉2*o@_*ti|Px͒y%>#>^yl#d=$h"_Z eoi{O6uҥJς^l깙}u_wt~-GCuGoDO̟߬:"Sk?M=ܷ2O_#]OWթ&kptzyO G #NEֲ},3B˅U~n +RZ9]FDom +t}hoL#e^w']'+~V}u*y6~\O3{q<=xk>O9\yfnC,LA7.gq"> CYgk@q +G^¾ =g +ႍm{/fʾϹa oJ{,}w?1XoOvC #y/_j:o8o=NOD~@o=H|46AȳIJ^.ߨ׮VGޜ$_k1q>v:Ƀ9 1ՏO%Ӱ1Eۿo;Ql['L+eؚ>*dL+~-xх 8mgUݥg?w{ <^Kﰢ%Gn_jy2o +t:7ƇџK-xpw + ~v>OOd8-VKW|G2;Z%ߐS`sܥ=Q tI^&V2?YBuQqQx|!.QN.\ z^`U0-qd+R7;N==YQW9ߒZaK}C LK''-ofW}&:W]zaPwoMx=bFfw.]`/$b , @3˰ \x z-pcƦ|?5t7| +^xݟៃ`{y.3!3F&ߩ~ ۂCǟks]l_.漹M{LƳľ&|t$=1ܯ^wM߭V+瞃[f~q?;[/;. +ylqy{7x>XXv\uDO~' +)xO/[_?8U {GOyg>s}~xl4e+]>"U?1XC佄pzz=^ @}Nȱ ճ>جzuM+ Wn3y u+aN711ў!ds\)E1[h?WiwWI-Ze2Buvy^ @ci&wїEŗ>^3.h'ۙ='yǂ>`DOw + cաT|ðw +7F_i_> Z vL''g}!&RAb:kwQޓgD>0^x&}AE" ">}g\w8g3ߎ5ZdY !(i+O㢿mțQǵW0b,H9US# yBNEy٧1p,rgEq+o;L&M[ߺ]nIs qiXEe^0~^'9J>R'D?E 1rFdݥw1O)!;=xGt4-C߅z]Շ(4%g۔l⯜ͅ]^Ӣ?KE9eO"cX+Xb;uQ\¯qc~̫ϵ:2=ucc>~G׋uC]iH`HZhkgOAM#Myr&W_C,> yFQ~YMcHT|;yoz<8)|uAX&v_b1x_2z5ej~τj zY%y?g /ӕ[@7fszmo檂xP]{x!rڿ;'gۿR~*Xl/}O=o#NPAKU=|TQί[iwx;x4tџeЏ9igr2=/v_Ɯrz3-;d`kxxf7~xuWGKqnT_+~&8g];P:c81qckG賎q$7}|K߄dS6{K.'WePUIytoÞޭᢩi*w|[=?^X]y.ڣV׾ ? )|w\-ȸqO5%2߱ض\>ϴ_ +6C9>釰q(Y5<ﮟC;4sZ>*ϯW#Q1D%^Ǡ󸉺hȊ}+C#qSц~wxNy#;ӋV*E-*`ؤK$E{{l?%<4>s %=9cEPzTH97Y@۔Ia/!xs=\FMmFv#Oq]za 5}={c\@ KR:Oq}21^_SoÏ#|xOM?rt쮽45&]7r-JdBt<@WQ6yٷM}襃Y3g;9E~Dfzx"l7GqX1G~ + + (CH-ow(4}\ߧgX($*e-i E<"|$ǬN)rF>tm poqguF}`gK wu9ħS}QW&q{K>y<`5z淽z*s_qo捤V[?װ^yS??J%Gxߍ=kY8垔Kc[WW\#:ydYkCb5M߫tAB7w^v7Co#䍬wfgx(샿Z^s+wB+2~?/S +r +&ܓW\dsO|*Y_5_{>G$\ÎwqבOB\蹣 z!ϑ.z=A >Ϸ(\|{^ҧ?jvI>*̩Ncɡ=FˇF3z8HS;^WtO48`ח"C^9>Āp_aMہS./KOuVɫ:kŬm{5ϧ&16M}kTUכ\̉6Vg +j^M3yI&Hi.=q.]U +h3e3^y?_㳏8Ӵ"2Ϗ7k<쳰"?#W_ eTBWvgPa't8^jmM֏8\m_0ƜHg}] "[ +ngqo׹G`{2^(*7m,{O{ m(PZ%9[y~&O`Ͳ"NnjuQ>K/Y =Eٓm+@{|ҹ~Sم+ȣôsȘǾ*)fg; -zg0|Nh+|kN @uRO!Xg%^8:2no]W5;;,dcGisU7?ŞwVGZ_)#lDJQf8W0N'1Mi@d=xUM=yծ7P$]?o[!/ z5hӫ`{0js^x‡?9WAj,/j=}uME/ٓ%~HO4K:<Ӱٞxq3f'(X~6޳`I'~́>=۟32_;g|OX +fe_-uI셿T'Vu1+Lboرyu|7@Ux {^텟oz"TQ_=mQyS顏cNβmwn3j201D5]٥?ʿm l{=HwܻUG&SeK?ߑw&ў|huǐޛC)m5-!_x1G|9|'>eڿĞ4wXuc\SV?n1?v^]VZ%R$c=7'|6n= +\+?O;l7^ǟ3_Yo2k4/Kk_`XDONa5x6dFJv`+KͧȺl|/s$b#mߊw0\h'C/ 2KbLĈɽ!Ck {=KXF`C#F1}^klkcG\Fo0|NDgFmjr}vU{5:ziWCuWŻj?П:'?2l>mg{ _zie鬽.,~wɫnm.`>ʿGBmr@8\T" ;!Bq܌^`m3ca xOdo)ޟzZ4?%_/Z7CǼVFpk?ɏYy}ȫ7QS_7r1?pP{c~'F_YMU΋c*KEGo0)g+Zwhy.΋(#I|!l{m9_6v寶yTu&}NX׽?_">K<_Z~uߪiΒ̫rn+Q>b_Yb\[~ M|Xޗ7H\w~u|vw>Ewi\=m ѐnDwiug׵ SiGlvN_,G ieg>MG(S) +{No3J>M ;?4S-oV#?e5W 紊ߥumR>^uK,zw2>m!Ϝҙ>nxwTMD x>G9P%"(yBפJJ·ǔo{Hw )oq>ba}$}`Cĵ~0i &L% +}K&>c/Fw);:+Km3!K4[o? +yK碛h-C{Oȟ>|ihwS=GxFGHm<5b,޹AwGb\z}_b//>کe!&Y4j +jǧb)-0o/n]d[ߘP(9|пeّW{ٿTϧVWƆP˂{[Ms5:jfGC/ nopm/li-Pg'Hy)"ֆs?A_aW)Nr$U>,P q7y^V[b$7V۠_㿷 ?UNf<tܗqIJva ud_~$Uf<5Kg~+xy^%^~H{Fs.]s\r9,ʗ4|BQs%Uz=5j::׳Mp׋˧NP8eGh2'gDZ>o3ohpFo[8}y?8j|"gPasw|?-9w='xi mO;AG2zҪxg;2 . +`/}.E<:bpA_8~>U/}^H[b~k^hS?_L.r!ȅƳP$ |eKi~3ػcc~To +%zaosb7_ɣayÌGBSf`;OW6X5Q梞-1[͵#NO]=r9 i;Vzۇ:KC8) ϫ$\c=3v-$GPl~.:bϫW lNgv,۬47>$~f=ҦZ V rEbi=M~ogԾqZ~"M犚+9p6BM<"7%h + +>D1ػƅ$'C ۆi4P湭'ݞҳ9J}uIYV4@?3?.}& ߛR(m|Z>91稖*%ikҞ43aMSfaaM;`»prQx ֿyGчq9oG6 9;e^Cџ¿%tg4{ʄku;F;ɢWSZZƓ:ޤ_cLf9*= +Qzaf`nw͟_{4|*.ϏC & cW ˃kg?O~\XY+|}V0oKWaO=_;`Latp}A!Veialxzc/(z_7mp]8>/c|Yg(?6q՞g:Ȗ63w +yuxjok, +6+:PE[eo ?ҋGhiC΋ 9X<>DkZD㻄 g彾[!Ccb0*D,q~5^5*l. ؛rl5~c-bg龜H|f <KYr+5J*\@{^yA/R6_i-v؇4ϛRx}zJ>iuK/nI?VU+MS/LªϬu.JXRe id#Ä[!"/k>:S5?*okωBpp~M}} cZ΃4?Iz0?]U%㯼ٱ+ޥ(;[D?n{}O="7"2VlPa($}f{އޞ񗟍7بمuö=jؓ}vإ~8`gMJ>%!q/z9weӒW&` +=%6xzi]GĉTz_d[m .O㨯ޥ{oG^?|Q7n*=f)na[g_)weOp +''O`>^[!? ~SYW'?)?+5N|3B ѯ'3P ~΁ +yޟ٬&Oxqo}wA&GDl@32w3Wa2r/%Pzak#5ϊk z+1WL_GPx?q1(Xb*?{'_*MDBCSG/.ا~uJ|[./vYKr 鼕>;7 >>.=h)8>o,LIgZd&aCyn7[Рn}1G^=rQr +j=yt⯊= q2:eoz'7㏂-9ċ<Ƙӂ뎺\ 2L;x[>:~Ά؊YJ|qwEy\`#ͣkI I{}g|x?moug}kÅ sim'_ǔ@g=nfsؑ퉿Uri.{^?5|ϏŬ_Oo/xK/۪~>vn} ۿ??}!MpWc[I=;#'a/yu]j♦f߸m\9߈%TCآ{15j0}G̤zau٦|tn [缔K1r_at΢_k޸ ]]? z՞[CQ?`{.Q [>"Mp97aW=y#&xK=MC+_/ xY=v, 7ߧ34p!CxnGq[+t![f6P(xy}z\K=8|gaܘqsv=Cq}(T~;sOk5}-}Zusy]%yVzgw}zNe4CaN=WV;%n[Gb[ +tPH&üȆҒ1n\ >cq)շ$ɞ{UpM-cEk?nDK-6Ek.KΟ{9/Uy =/+cwmW[y(%?ʦTx/|#&6<_[h/x4]^x_VSoPms;PGI i}q }vO٤=Ei9Gyk?s_xq}ۿ|>.|웿{Ɩ`i`\^:,l4;2=͵NW[7ޮ{cLW)үG/4wP 7}k\w.t_p<,pF4ir=ǺߟHij/ ?Ț}]O{/<´_~IG7gz!_J~_\\6^fAޟ7QL婦 +e"'vRHùy + 0*,QCHߕD}7 f];[Fw㐞a}:=Bf_H ?C|FߵIC1<|iGj?o}]~|ɑO|xRqiꝃɺ\] Рߎ`4DX}.Lwg,?Efo37Cem]ysa>1V&T6 +X8y]3Y,ǢwQ/>}va%-65:b2:X&.Cx?R8/mѧzýuu +F~FoiO)KԢ>&RYmN/D >WgqbOو؋8#j\#OV竼_ࢨϏ!1:x1f=;Q7S/_|_~ D&ptt~p|X6'aXXz>WnW7tx-1׵+:I_'ϯ'ܸm-()yt2|Y_݇!~6d:rTcuJ}%=8Nܓ)[ h +֛Lg{--b /-o_?#~ߑ99 /f1D?JzwмYkRyu>IOr0w4Wh* kyH|0ܮ2SxGCpi~Bfg֯ut?G>OU(gETæJS5xB_io!:={Ee>#1Rmg$2fظOorr,,ɑ#&b.~=wm9>%R}k_2zW?O-%F{񿳱:_}Ցiix{Jnk 3?s1)5s~cE_KxTWEzwimXڟ{ž#Y}Wݙ!U\ +=&!Iz wo0*Kú|Q͞s;7ζ;^}׎#2]up%8oGĖO4=$^1obNKb>Lw/Μ~;IӟhqzZb̝rU>7G s9rK1%:cOI4\-$b/ >~H0yU3?>Yx׀oT_W#>_{ smQx^H7KE !I0#\z nr8:~(ZF2C`uf_|`31XY'xe _i|mpgC&jl7C_<^ t1>&E13lG=*K0ێiW܅?3wp]Ks&iC:!Vsg6۝_&;_󵫜j9 +wd,8f/z}.oCuSrbΉ&΃^:_/ zkҝ, +^u^ORj*i-Go8|/IYƏ;{"=<ֳ~p57~?K6o[Bߓy1|ϊ-LT^fb]hݜwZ6_gr|)?; ?hXg$MoiJMau81ķ a{8GQS,ϭsB|VmuED[9yN:/;DFyBa4sLUXau~7F @xlƻ*EzB!r$sZKp`\ѓiV>[ ` %fF89 ^˞w~qT>Q~_OQ^ϊx +x:_]IGe+jZWy8pO}ML쎏ȼ($\?YЏ:F~Yd_9ƖlO=|H}uGV!|awW {[v+|HU'.x&ˡ'Iz#nT?c- yu'jZO1XCOJDSM_G a> +G68ۼ*mC:Jb21h$֛ܫ7sźЯ'ߘ?vf:ۭ_]OUt[e+ְ1Ԣ>?AOJ/Y>2i #q=n:l{4k01z8fmN/3J݋vTkS_p>6*#9N^㶣ߕ3~"_N/bm_KEQ埧Pc]q׏1ߏ!r7Bo4/KHI<`eU7cL +}9ؼ&Fׯ)t<^?~qoh/O#gQ7=. i8;Ie]]l-jx+"όПyV,0!TqB&Z)IsMi_Ӳ/8|PkMTڿ,ÐgMo1ڂe3 ^giO&ؼ7f^g ]# 3yj3أ*&$,!!~B}>|&n7s1p`{+?UikUŞ EGU8`gQ:~=Ʌ :luf\>5c%y{}:I'$c~cZ}w4G{N/`{l7^+ir~\1ȨKvvXӯܫ 7W]/d4%Y#ܥO^/-\9_U^w׸8T?8/xSc^ܡ+_5죟hz~}^UUUY4aҟX+jS5g5Iř3I꟔Z=gQߢu >ߋK#PA8;J}&ɷs١ɻsᯰaoH/ +&kg}i:μ[ߏc zϓc0XgWzֲvJVɳ[ݔza_u0K (߿7_0kOvϿC~kv_(lo%([<}VOow'E`GKE]ƞuCgs;yOO *uds}3oZ_ϙ/p1Ok?S}"\ ֳ^ZDd 5f;b5Nb}=s~,wwcN#_5=>;xDVĉҫ/e]y?y>h#_#>?֣bGʍu1Gzߝ\}XInoU'e0Uߦ|Bo(' fSŁOc+WTeWBƫ=4Jf˦g8WX~U>ڏw +4j{wy|>?tfM?r+}+Dˋ׵~8 Mx^i>.k_$|,<󺏀\ u2 bZIs繭GpvJgyz/E2Zk>4*q +mZ>U>OkezL F(} zRw<~Zk_ "ג5/oJ\rtoG`!zAZLupTzҪ~@TF3qXԓL~Hy8ScUŐjD~VJJ1 |*sIϺ.Ĉ#bx=g9hzkk=Z>As` y^/[7W<36P.߭e>z>xN<W}3=:6-ܥpO| 7oێc}m$F&R˓E7+?o} x +z{^W5=?wjϐ#N?j/L٤E p:X>Yr*-rEū +yՐ?%;AeT/R)ԑt? +n?1Cǘ=#cFw?"7_wQ۪ яeb~@]_S?oU?=3텿~ec1yWW[|Z^>>hlϘCgL_^ST?3a\[XE](/Mc_?59?dWsh؀㸃4HͨU$Η/G/AW!F[iҝI`m1y*L*8u:^§ux>|\xw~:m wt.q^_wcryX/,t|&FRO7K//'t/8\~ xo'?W?i6=杼 Կ> "_`oWupϭRάxf\b?UuljO}(] q"5t?sXls ._'$g!5fV7o~s=x)|N!JÎ|W3'%5|"{~8]Ohq]ð^b.V=6x|mSw&r +O+'u㴗HUD ?|߾/4ϱh%(ډ>z^5&R1Ab<KMuaJߴd虅B|;}9+dyௐ_6ryH?g!BO)܂S˖| 6 /0D16sַ +]9Mh ~n9xqzN݂n|7l)vq/6'r> f?2yP[lYk}45%е}viB+RXs-2@/0^Twguz?sPC̦zIצuG./'|n1#{s4ƚ|SΉy{ (x03̍yǺ5[|JOU2r߱Lk_`<἞5L= ._}_^❘{d}w|_h{Ї/ܗi/>?4$ޝe#n.+7g0[bzW7% +y4>ھоϭg_,HAɷ(c~x'Z>놝$oė M,gP?1e|;ӥ}F1S"' +ME:$ H%xW, U9@|ib-|9M1xGw~&"]{c&Ecuu?OO5vXo9S7y= +ioA?)ϓtg[|SKEtW htgL1=6ܫZK_h,~nn:g20tnBn^({KNr!<5Z/A-8G̓`93nƋ޾Ѯ(ɴt;.=m}w~g?r#xqƾY$7=$٬oܠ. Xc.ތ+SKG_~߇}p?1NowCỏsLEj-s VQ_sz֣ogSghn׳AxOI8l{(~ߨ=' [΋G16PIBƹ"cݸMXx~-R牺uշUT_3w*aE{3g0xG^ Og1~&r}c⇔_߄~^7}"wg>.Wo O>k+_og'[oys7#8>k߭MLqAú4_t3C.41q?-IRKt σN5j[W/e[み_-uFjL+ooVsCZ]4zGW/ ؛E `_ArqWQQk/w;?eǔ8|{j1Ϡg<؇ǟYͲf'lO~| tkS?C+{wTu|ZkAʇڿWi7~|_#e?y} J*#~L=O<6)71֟$:睎=z +zVW0.Y2e<(?l}W=};XU\S%%*zĸ$!cIkT>+h_*W[þT͉=#PqsMR~WξǦYOʽu&n\;|2\zSd1Tc3KgiOs3lx../s<m<7Nd&_a="w _זiV-ߋz.7{wC> Gw `_ Dh=_o/%~uz-r'uex'ti-7?ɾwg , +}#p{_y#ox2ѽl.ys嗨7cc S>u97;}i:ZG4ŻЦc +~ +jcZ_yfy +|^Ty;m`5czXǟzߢ'ӳr`h'la c)4V5`<Oi-sߟWjP{A w3<0v~?7=y(~#7}c^9lːKɿJ\y D@ۿ?T__{/wa}}Zl`A=ŷxqx7_x$vxeSA&/**׿D-?csR}{mzr0D~byy1>HuOJ\:aG5mw>-ѥAc ߈㭾U}̩ <:U>U+_ X6y2a KbIWd }Zb`G^R'.+'sQb;s&d.U(rjs؄Qeo2'魍|}| iP5߀٬r=ؤSϏoW 3Lci'~ .u.3 |LF:ߺ.r!ڿ\-=W7 zvLdJI_(`ڣoX߿t8.pkpzN,[ol_>}6?o&#*L|Vk4r2(9^Ǘ[pt|p IL//UWg;|;>ussS|8ޕ y: Rx^/$~\tϼ`gO: G*g(=$wL!??kl.<4~k0ƆzT|ߪψ-lпv<]iO}>1>$#tu)A_y+T-)&kOKAg4^= +vya8B[sߋ6+.u,Ƿ=mHr95qW!}*-է/y>â:"皙 Px\m] •K'L ׀a>zj;1o3I5yM]O%8_Ck,~?/'fawn h>;ClguX<^ =9coYUň 7ӖP]C/7[pk7-{6cT8S97]/jIOaS`|Q_&~Lk }|C^Qx^XIx.pϫ",!pZVo, M]zr]'o$[/)Ysj9GSx_np۱ZywI 4C+G[ 3޳nO)o*S9& 5KNf;<9^Kql׹^`ou f?rO}Ɇ[YٜK{wO.wJ6X?Ms{-~>Ag&_yѺxhbKhZ à~F?Ό>~ol}R_ˉ'sQgs M*[e.@'3-θp6۾`wj~YOxs=$[fk-O>gL%ְ]b/$z:/>CWül4%tO .Aߵ \Jư*frC |B\β餛 sV$݂GgjQGN?iHαAx幑X FC_1Q9?olWEs^o^zKq.(˘,m@Z$su<\{Nj9K[r}:8ܗ +Z*_* .B~&`gNfFta|N.yxك7fځ[kwp{V}~~ +s%+L^ 43Jf i鄼j F|GlʉR _G2}td*w. +n6 z^g9, |l rJ̧_X7;(Ϋ7y{ˈn } TRL5qݨ"/[ы4{'iڒܿݛ?,ɯ <Ax^{`J8őCG3.pZD:Őium%#=z^zx +vs͸R{@|i>+@e?m].˓.}𷮟⺓*>P1ii^ƥN>kZ?:ɷ/_ӣXOo\?Έwz>-acF"\ʹڥNd̤oݻ}=t1x~^O_e]z3M~XKtH7-~e+?W|0YR {Am{o k|팯r^3p顷+YxG?oۋpnעk ]Pݒs+1Omen__f#mR~L3sxz2<G_^j/{5 +m L#M_k O5E%_M\_O5?&[U΃ h13n 1{wDLkkQ k868Sc 1?q~_+%1j?:}Ɯϸeph]3 ?:ޒ2d%_}/%7Ck"j w<u =WD:/OK7nx~;Q_0asXN7U kMa9qKkОG* Yȋ}C@ -u{3G +̼0 v"p?v{ MZWJCw{oԯԾv7 z~w]wn p ىx}q~ ;hT-wxKpl`ȋDCAċj?\%v%o8<ڿ"uEYsU*ߍתY/~AK~xorbg`" (_]z}z^-260WQ +FQΔA{qKifwwW/Kp ~~W+_Nxݟ|O_[#qw8{m~IH71?+}ڇ]Ϧ|2ueb_=PT>+ίO}`gZ=.m-NZVq@.^sEzF}i1ҵfoO27J8 L3y\_α9Ngr&*/9ZK~X?kg V[DnH=x?5R}wId}o;7h}}cGնAqz%{2gS6;lO +ů6??WoG~op•'`$lo0<boB.S0\>flli ~{mbkחՆ5tj򹎿ɶ3|ϣoNG(QO2w:>0F~S{ cۆY~I_c"?,b/33}+r7m2aO|tb'TG(dg#NGue:۟L9s=0-fK:7~%>#SG{g*QYu^/HhoWM4Sِsrg Ǟvg.xno< /\o|/~ ?>o7W_"?_we86Xlp<^öpi^gkX{kz-kJqW{=f1cBk}ܚVp5x>'O=Z5~Yu3ZX%9[?+FάQeI,+_/;yc9a>M35d_;%ybƼNg(؏5d| 4)]=I␏ znK}$Ziww14bB.L7,ӻv6M~l/ϴky Ոd(Whލ~fMt&cӐIU_72p$%

    VA֭<ԛM ض /= ׿;/}_??Oc_ 'nw58|a 5էӥ0Y`Uͫ\Gyel'{^\|\ZcO~ptzgJYL.>aw~oweL1:/ٿ:_O>oHٴ 㱺gL>F_sI9+VɧV3OBzy,#Vx#/o? +/O6--YEcuyeև: +ī8t鋙TQZ_ wC 3_K:}NWt>-c߉Z`[KOpow> +՟i> O'ϼEƹ9'!10o*ؗo~__agҧP^n YVOqgao+bx7 QF/Wo  Y(*KAϢg =T_qdU?-5I'zMώ P(f?+`^Z(6%iģb|ί͞WߺA{Yf>,M+_^ ֧+78y F>9ر|y.(<5x{? +lw]mpn2(G1D1ޒ7Hfos?S?M?SKz]Mvi.] +\8/M#Ip5p}8 {^=cb`ϼZH3ZYWY6eyݷfŝȄmҿ%~L_?B˾oJrWSa&A7~Fẜhga9<~8ڛ`{`&h,\^pl-98O)X +CO땊,A$,a6ſ)Ǯf g+Oct7/"IS\ i|^1哆(oaꍡ&O y.Aq:1/!zJ9)}@xnL-c59|ZAߊ5hm&ޟOo囮qה0>ؗYߥM#B.3&ܿ"7H79Й< ؞6`hjw=Ifaҟx=XE+m_KcrO?yc܋?;'_7m/~F"Aeo|탫tvd} u-8yl._<}6 +4So^?qcgv>_77.g6rہjKN=obMyUI'؉9c02F%U IkB>$/˟,_ik'Y%q \VÄ _xmGsLɶ'kX"&bs +3:eguvQ_[y={Jebz1d??i˘F*ՌH?z 8"u}HC؛x)z/蓦3^Sg~ N_~v~.?o<]Wu}ي (/ ?7N1IKfz_t7|-8w(,_c>gzAǡ(:5pJ>e)z)sA/d34yۃ;s"94_^S :1ps g#SJzfM94*~7ϿRkΝ^ _JxOcDEqo+} R 1_+Z>frPѕ'tdMy }QCI}~\+<6=eGn=.|m߀[_gR?syYEzZj_y {m\5En-ۓ+.tW +dwn7|# X.`q:q/ϹEGo\$YQ_+y0_$}~2>md=Z[n%P_zIs L.#yg}:i [4=V*k6!gb ?a_Q~e$os6B|/ͫ,K-dF:q8&1n8c6C~ͷ_Sߦi]% X&w4?$U_a7fhEbr.bkL]oN;Wr^&<#]6gE]BcϜ1??y/yqeȣۢmߢʟ׮*'ԯ)wx'ܼ݁ +6a_d}`Z?\;߷kg޳Dg~ҖQ![5ױO9{0a]}4wbh o{?1H>{Ku/ylX=Qox'a[{誜ߗ:wӔjV~ ΀KCOV4״F(?mv=8 @QT>ʯpz<"[Xœle{kxcC쌈!!?nϵr mQKZL +뚅m>=~F.9g`?ロs_~ ~u܅C0~MN9}xVOs}u#`o}Gz ʀ)<+^YirM/DR|{0z!#sZg +K-_\}M?Cz|ȏc;.:Upb=>ĭ=Au<}}Syڰ7g%Nȼq{%.eN|}aM3y55VmW7=uc5"3g+88~N&l*K7sXr{ )7]I?h Tm %{TOj!]ir?g[(U+/ :)_*|':;}з0C-~ .[3O]}l_md2zU|+S|z c#=t'%lOnal.|^mWҰŒdGDq|^mWje1zvo}/ëe38qۆ=p,DߣSθx:>FyosU/H5U2CGg_h63~)&)+K-{a=+}߯Z(]MF3-|җ/z5w9|-۞ _K`Kɼ@?Agx<*od>IK#k/r9HG_|E߫~fG>_9)Z>ש-h_2ోFsl`|aO@-PǬWo}h rEyQF0v;?%}Gaym'd6F08pXܭ~`geqU=L!~"u7ื> dg.n!~wׯGWՃwz/ٸz]U&s0?oڎ$@ŘZX<9ڨ6r _ /)?b Y;9_f-oso{#? ?7^/߄__gǡ__x<_䙢jd}. c4̇5-8DyWv DbMcxMJ7_Ă?ۊΗXL<ѢnV;Wak`+CG夗mu[re{1/fnppSsр+ 4l:&\Eo__>[㨏Iy9y o+>Dȼ֮'wmsu|f8o @+Vx.\G[NYNnŒ#sLk{OyʝD{xqךރp֭1iV>uU֗mx=#?Oֆ|޷WaùGw~ϫ.>|NE60=?\{6p'=_}o's,ٹsϓxs'K~띈j M)EVϐ=H4鱀G_n|4ί0ab k'py8w>\OBKLw Z~{I|c`@[#re5^*~ovuB׭ZdƺA*zy_ |ukF?%v9+kOa> g"z߯— 1,C~7wqkXzK_>{t;ߜ^F7:1t`;~W +ů/fޜ4(+e!hKr3 5௸>z#aw74hy=ZWtLZ)0gQ}4Mݯ)u1OV=±R'U kZīQy;DꩶlB.U12\S^O3< / :Kkts`MFjz#>jo+gX < ?h}ʊ z.||ޞ_ Xc|OD0#>\.rT{~=}g; [l+&-hh6U軳&z?'_I ?`F!ՔMj9u#7`X ?W==> wu:P7kPy*s[ycJ稟+Glf"Z?KsMq?'u>h:T1C,f]>`{ݵR{&h|x{o[9 +^K!%2<-`"%}H"h>kF kJ>e3*i=d13EzE;9n~s7a =|?/|sp/ tخØ- 'y\#kj&,3ݷ_C~o1f* ›% +}J!^Lο U?bNXGKV_{5&k=P?SV̓ޟ8?f?nv'cI?i5L{L)tsu*t\f򻈿kSA9Jٞw=Oc"~˭םzwhJX6Y1S%?G|-hȿ!O8[ϙ(+)3ӭ(0._X`޹5! N3=^ձ<1牞3 y$ +Z c | sta0UB}U($mdqoIYU8kUoVtx @G4oywǹ^I}b!R͑{4aMbZZގocy_ƒҼxR߱xɱ]K:KX>O72R'hM\oH߃rMJu~cE(y.Am +577BR uMDZ׫1<>-(6L&N%ډni +{IZKoj'r=(cmi*~p:%B?iPn1o~Q { =rAſU?U_m-Ʒ GzqsgP},cZ_ FSDù缌ᮖ?PVCˀWr??|_uOcy={~"_SI?./; +flF\֢l]Az%ω, +}:gzB%,Fwp9ȼ޴3eM_z7>g]uɆ`leY|6esg|[6uϟ#U9#,_PӾxWԵ*gKu&r:$s08X0%F2pV;J*FKy&2y.'"1 ]V+/j 9c?[FofRW>*2_!^=\+߇wԼe{7ǿ\/ry&!,&O)ᥜFY ) ֯>_gW{#Pe}#8- @Z_=;c[qث3>Wc/s<ǕM܎sIޒx=w"oM: \7A3&ȃL`~?c:#9>NkAv:[6[3c] V"N!܌ x?At1-j$.gvinf8~:W$<,I]:lx*UGbZCu=0-籌?z!Kح7~&ݻ q:g{kzz?-m_cS= +uܔg^c:R n}_8^VymQO'}ᮻK'1w)sVع8Xe:h:} +O8[/&OXӹi{|K<=~yt;WnG9}J{  8=gl/up31EI'~"~R6yB_^/kxn"3D-|&'Q3L>jgQ__߫ÝAw3O^zQӷ:߉nfWW k+|kؿ|R%c= 2_1sj$YޒBE22dJsQY~_evpC4}>uRku㿥~([wMi{ll>gz{[H_\61[Cߵ-֌G_1%q>ƮϣL?!fz.Gg5/k6E2:4g=oEHv1LE7%է38ݷs_ۨostoF?Zv௠ ? sFsfKzfc-/6S y77`NN%f$~ y=]G+Ÿw9FLW 6K`ǂF7_5 2z - 3*m^Nڟs!+[OFښr}KĎPgUryfLؿU tzP4"?VI3-}ciϢwنb~'mpF|f%HKc 3\<1?*e(cQ-?+O>D?:gFWW MR%h! gB~ȣ/ѿ?lu7,~s1USĻyiJF;66[%p07Dds1I'7liZӌ?gޟWmgM>  _giq}S(LIvfF?>#vי+7 ~mς?S%AUᗲ? ;3H1Ѥqy|]1^_W|SgnĘު>];t.kߛO)=r$cHgDکoy7)[W`zL+/$$f*79BdnZ):Z_nR]߄>UЎPvh"ZVySgj4 NE: 8˒>){穈zQCV*Y?.K 3n+ Gv8piU1>Kg[6({# sf9Md&`>eKx]G]-ͦ>Qqj5W6J5\Ys$nf?0sz?8CV3BYxkx )>N2kc;Z g`ssU7o"MzPeum _ԟossݹ\w;ם5/1Tp#N M1 7}V$Trt#N M1 7}V$Trt#N M1 7}V$Trt]6N MEƄ"*b"# +EE%*`ވ"A% " fFgIg;s{ϾޛLuWw?SOUZVժU; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>?N:go'e)n]G'Ԏdq +Noӎ&<Ψ +ή3ߩΧ(i+[WSU>*rқ\!Ӆ2r۲:y)}pn+ _y;]! *KAl|z]ƽusxxј O_ǒ_} PYJWtpAILZ.+l=xizW?nZ +F #u*y}7+*WUodْ=ƽǺwi#T œg'|~**q{,yҜ+!HRE\9]L. E얬i]+k+[*ITjYݐg$x +Tp[eQPSz]-{U}u 饫7Tp~'hcTGU0F||U+u}TNrƩx(:आ)J.\]}t`d>_̹{+] +fUpOS%{GIt!ߟeyV}(*;+^ tt-{]}lixv}Fӣ:>Ju믺({h~E|&ܡoxf1\1QmCfVpLһypOWAAA}u3%˘^)yK:Ƀ#sDZg*](ٺ%,`EOWT!Ѣe 9Y<W>%젎f }ŽhY>?8vhMyYZፈd3.;<Y=T?l)MPװ]4׫eHAAAP'}yǮ\?$Y0':q>v#؇_:W_=gI +6{uT1l0%S[yP߂^|e{T]nw8Wfr^cv_l֧Au6nַu5~j7ogqLeP,؁&Gdr~zFWX ~NWk&*bї`C+x9g0G{'3MR^u;dIΔP.ϳ.FZz\L֋c}l}F}wO|E kWv5еJ{ިs]w3|PeEm*0vzy|.~~/S9E*6D]X;g\2RVUW;;Tuk[N Oyx>q?AIA)9nņ8٨=}Nvɂ%ثO7$^kS^䳻`;%ڣjFN]it7jF= r}mTuG "y,y|M$?T=IiM)qQ59O(Hr~L>jc`cj5oU36Cߠ3_;.Q}):=w~cfl`(Hx"w{TW:װ\g}Zy,!*s^OA#]6W$kTHkN'nU3t$ɛznb$ۉzk_wyAM=w6׈cTsD+0[t"T*-zh-zN(uCٮ}EܡTƬv0k#ۍ'שmHvd D?ؒ>2hS{3̙h,+͡ONOye +>oSQ#٪ۣ:_-ٳBry4]2jb$}r +2} +~[UпKOwP$j:=5ɸrՏ6Jgzb|I t=S2Eg㝥*/4|R_}lKnsÓ/t~ZzjgX$*mQ-"ͣ7 TT޵ 2t}?X;ẼS߫AjCD<;O4Dϩ=vghA;N4'FßWIf$~TgҌ׵g6ث;VYুHs$ C-d3Caen[Z͔z.K/ҜyYwJn![,F|S*s h$ߘ7Gu:1sal%U뒻%G*3وm[kJjnxQqt}J6+RsE~{s1;GG.RYܾVzHe]Լn?0^ӑC7UoOBi;"bWmzny2<,s1"| zlBuWb|+^;㚐=j5jhfK#w/ڱʃ|]oAA/@T/sA싣$CdgdAɌlhۢ*d7m|Mi>KHr~]$%W:;:*௕'zB Y;\q*jR|˜n\ެ"AܗZCǢ2rhD3Z, 5ֻD7UNOU<@eCJGY=B}_lmj/޹I"]Y)2F/3~tgT/Hk\O04٦ ^S}gغ1=̶Ƶx|G$+V:ݎH㕝,6^ QEY7Z;HmZdmȪÒ؏3NtZK)Ei̩(+c%d+y&Un6{g|y՝/N-o-zw;~S}3]D7'g@}kUD7l P_0LjF0FC{7G/|ֽq +|?QmV */G7^1Fzfh*+}E-a=o?<O﫝=sD#Ƴ=ZezPuت4ܗy,}>V{FZ7GOiRPPQg3-ggH{e+%!+?Td:]=Ey\'oAnb3R չ>Vy:iꃌfl21-'~KWDu])->ndwx;j;<\(n?e٤gn,P_擗D;,;:%}i]]@o.Ξ%滿Swю1:1XБE=Q%HFhEYoN}WtP{>SSylux}݋>}V/ar~]R΃j{Z4GT/vY^gi9j~ojɳ-Qž47%}zB_\c~G;ɸ-̉XSr82V/3Gٌd8RՃ2RRuB=oYA++:o=_'yoWM#c_(ocM6k(7/yc\kmQQSV N݋X7/Ÿ=/jW>T'hlozWؼWe^-Lt/{UjSǡcEt+:Y=S)Q;ю#S8Xf9i\cToAA/@TKF.Y!q$kE5gٶ\Ec["ω}g3W|B|'|!y-=q>Y$"鼝|Syx#?<Uf J5Efoy${eu{JNTyr`$+P[cfžs+w^RPlwnC?)qؿVר^&x϶̱1 7o }2Ҝc+}~.z4}G+7;lд! L}US%?ƎE"Dsyuk#_[}2,:uo*-il/.22ϽmCgLRF 'PH&z}Nwc_@oM{ۗ&׿#wy9n\~ʤ:AiCQ Y@wPߛTveEۋ/3v RDTmΘs$vIا>^fl^{3f[}0AO3~sn0JtՏߍơ[z0~x&|չ/f\mf_2DJKr>e"mW—{_*ي=tRxWInZ2י:mӷ:7?wu{{B?<<׉5GyWZ3@z?u|1-E#vZ2OS_2|]|=}Ƒwf$]ϡoWZ:=o"͡>}!t^*ƒؿE՟VUeFeDza_,{e٣Sܗ<ʾ@f؞nWx$oAAC_/H~"'V7r]dz1Şzfd2iOHp}<gڴPfWT ?|kܛ2`{F?ԽkTNt<0:"i;1p|-xO[S HkJXi>F6/||8ajg~Y)xLyGc$Rl *'6=xe}m;56ϐHqBT|{ ~3f^s +]FGXfő2W}ͣ(ѝ1yq_4& j]#9zzɛw"ykI Gqx|#UiMk%7ڢjgc>yQԹG7>5ٟj}vFҁK{ѹ5\_F*^<汝asVG>iHMpRY}y<~χ O}}fb(<`ga1Ҟ tsBx['/tmϔehj$dy\ԩP)VɦHtf|~ =1_Ǘ=ع߂^HR6Ea"l|/RXx(K$GjyeS"Ŕ*xHG/ϋX{w΍oc~?^C7Eb9Y9'txh-Xd[ҞsmV++F}ֽ'ukeoV_ږ]=AZڧ{롌/lY>6c]8̻^s,Ձ/}F7+{}u8oWKmnVD:Hㆹb9cT|K#KEܠfϡK^?Ց`GmL9Ζ\x>J?X4YXWt hϳ +EU/t';`es>VH wVN Hm)9?˔bt@זlϨ􎅪K+=jٺb5 +d.U~籃} vG^R ;&94<6E`tCdzz$lTT&>˱T_P[P?tCrL院c/#D:|6d[Tz3}#-a5Mad}lұ(ݞt}Q{vc8~Hgܬ{~<륛uڧ26@ldv f/t ꬜Q{89\1Hg?LW|i 4MgE:g~8U->PwD:Hg["uJ +y)E:O|n}=6}jwƺd ~M4uȼk"n2kDy]ɜᒓؓܽ'W|v [k몃:ׯ^.yh9@]x\׸wd YFSt=A顓 ='y|1V>;";U)+ե"NT=}XϣU:l\}>3jjy9o>ه7EYpe:^uܙ]'8.edhSYƟscz|1su5>ҺcmQwzv${x3\)((h X7\1qmJgHxs̑GFc=@C)$ +tmR[3-'WwHLTqh}, D;rKG?,9D|#/3Z&е*ueMggGgO}Fiq q;7G:7ȼM}\ `( s3_O:|#qM$?t>1pƛؗ7Fy16sUށ6B4"l}Δ>KTͪs,sAA/Bn+1H2Y^-ҵ~gK^\*dP2Hn'_$\k]A}uP>~ S/vqr6H +s: EsD3{P`եJZyYGޝF_,st#k>g}ğ"[3Lsy +9P_gI7PmC9D-P/M0}@?z.W_e[PK}2a~!oΖ̰KѺZ߮}qһ'vz쳎5y#ܶz:4iMkػ&ݝ:*mNG$; =דC|F%]GZuj2٘Y]Fi X]g^3">Fu?NBtJ:*ow۝+(w~Á>?I[_s=&˃uᶨ]g㨵?V=s_=Jt)(((((8VЧmDn^0~E?Tz5 ̏nvT"ŒJΈ:scB-(((((#lf?Z:b!zn؅<!z }G};+lB׽ osOxF N.t0>8Ƃއz-F^G{ :v辑|ݼ>uPw!=]S>Κ?nٷD}6ߦw>`P[//yqI5jD胝y?ڛdk+r4GS.%6(>2,((iN p|A74\2d@$mW>ku2xua6ǎ 6,1 AG1CRPPFB9 :Ǹ=RHD:;BuC;kdkOW-1aT?}HX>4վ|oHG~Es?Z@Cʞ[sKzǽt%02ZXX| r}d4Bbӌ[Gf}1ϓQs\xQ|{vmҍ:wVQ (w>-Z}Bs?ϟ9Oyy Fx%Gmm5]uU u騞Lv~֨_Gt;mwqL{Tk11}f9{sIo>:x2}u~}exkU%QIl v(簭=x"nȬFǣ'cHF;3Ofhh:>򼹂&O+##  i//ʵ˔n~{_{EFEwq Njms'xGxxJ)vkw3ۢ9J3p7 >Kob3"o}u@vLTZxQhj9j^xujY8vb"<>tiVQ=Dk5%*OS|FffyvFS=yUSzTϓl];)]<8~x<︳Yy {C#ɩu [Q?B3'f| ~ٹS}cԞG_g> #2`k?12Y#z|v5ٽWIq7A>Hg'HgM\wܜq.]:,w1L!ʯ=MڨG|M#Qב/Et-|FH>n3lo1-c^ J{{P?7W46>LQy=J?ƫ~З& 7F:8cd~%zϘ,_Y)Rߵou(Օ~θQ˙Ue͙͜|0gewf}/+{ĠwH;VMu|߷1dzvQm |ϮҵE'Lz{!1'mK=֥L5:~?ˏuz BӔg:s")?2EרUu鮍RhS*? '(c&5i5$ўd|Ιy,agNtVX :s|Ԭ-ᡙq"n؊#ӄk :MeFy4DmHڻznzKqHgcx2?OJ;SΏ#mM=wʌ78/ +ޏ+e,Zˬj?ږHgz0oǭoo$sP:jh@]t>~mAI~&>VxIx^{><^gsN9g1cgΉtŧcϋ~I3f\^w]e'jӵ-d}Nt~$jV >rޝ#d~q5_SPڏ2I.F8axB̓t9e}DvvN]`ڃ̃c*q'f6 Q/m_`wT_#Ox.AF7ڣʷO }t-Z,]GZ'}^])~%J?So>7:GD{#r5 ՗no;u'WgD"!Ҿ~#9^|Q-{Xu*6NTm^|wF:70]`xyUڤ4KOmaOZckcP)w'f}}q*5~qe(yh6effͶq-ymY.n-hd]!MrhvZ:GdžN Oj?b|0zi7ޱ][9+uQ]%-~V;ѷao+]] ^i ġ ҏ;c!iWƢ0|T)^yPT^HOtvג&-t%yljG>"oV߈$wW}g)OxG5GIUA2?/~X"CV|RQ҇/xfr*z-;z~]C'p&+C~|1޴WymP_춉#,s9l]J}Pt_ +_ oa+d꘿~n.ݩ4>uyg!x]]m OM{,4[Gu, VAFUR봌=od$zX.E}[!T4] ~yC?T]Hn_P[SGU#gEүڄw,3E|GiW)/;^ߌ#Wo[UIb0[mXMK6_l"}z"*G\:{gKd> u>Q=VY(}cYFkeI_ƒW^_SCC> u%ި 4/D~wɏ|O2w􁯣{y,uCGUV=޵_H~{-OuoUUeJ\ʚmf0BD?^Y!Ҹ-г)m߭xQĻF5y o[/O. a7^y27_XԳU(cOrЧMjk~EGdW#ۼ_v +TdU$b,鹜u[wsO~7}**׆,-fd-#tg>h%\/x]-_16xYeV]g/໨wދ\#׳F +zX},> nbMˮRl~εF+&:gosK#O>{~mQ1^iύ*+>kݱiڢvEy_*U]ʶG4b9}݆lc\B$o$W=ɇmO'1Hu'Q_:mh6?Z$hkxﰮzbA>ѽ|ٗ]_Bajgx-O"B}l'?i>7_ˋ#郶7SG _7(f;+lTGSyx /죎c/Y*z ,M&E-1eGtf=їv,k6F؛)*ύ}HhD}WHdcnd|~.ZWyr#Q?Ĉdf>FW|@'[g߉П>~W@i-^ +UDUZyJe0x@$mv:xe*Xd=ru CkT}]WwbCޡ4SѷK③y/쉤~f^#3?irrxTrxhߨ=CHgϼ.gg ~C֣gCVa;X@[ZÛiLۉwE#v13hjy>~N"}轼g4sHnQ^Pٽ>N2&Ɓo*g^Twr z_Gcߪk>rCbgπ Dž Y#=k=q5mÞOFmC? s4۶އBעWU/EZEG:aߩ>d~㵯32k~ӇsY`O ?/Qz ʹ#Ҟ UeOI#_5+ z  EZ|Oߴ53/E/ :u>ed51Qu3KoFZ~7:G?~zZg;Dğڦ疞m_۱ģ76xAJ:o)//#_ƯO|8rY84/zvFgtyz׻5vMseT2C>{C4x~AgicJ|gn3Fsēo?~/8MzBcNlDvEZC.o\"ouPxǍܿؑ; N۶?;L|Ie^#l8E{ۊsoӿ衁|+u+~~*GAާ<<+ﵾ%jؖXgn$wu{Gz? +x`$~F/w^7uBTws#Zga yg2ط߂zy_8)\#ރ+ "СƜ |+j}ئCz~k#5{5~8^8_rS9,mrnj${6[ԏC}3r=ֿ'Eާ}7~r=)|1nյ6J c~MmG3:;uu;F]U#ZYG7N2ϰ#jm +~9A"ϭ_O"˘ӡ U11eB_ثeh582e}ěD'Gt/7^+YaxC}m|F}H{p[X#:}EaU7erkdq#e1Xok#ſrrj#ŕۣ:mRW}O~=޷HkTfǢ <=?T]t6 +_:!vG"mz7}zm1t?V>#~6_8Uj{ޱDv=^e83ҹ'3"Oi>W>H|0;ԐAtek;]`ld^T[#Z9#ty2 _/6VfXi Qb92mώ>yM3"7M}tīOSZ:k_O78cѿOy3|rqM'YŲ)rn_o#C~ ^g__tOO1 CuqSyx z2R'Oei' =ikK#l.ya$~{083~>5:?c~$Rsڄ934 #޽A3nN8k}gMM2KL鷶zBGܕgg} ɰ ì# {Z,"JѠH^|H|bFG:H+ݢ,=zӢH.$3ET__}yz>e۱&oT^ ,G(‡d Ѻ߬y 3M~IF^-:v0M|&ӞwgiGvsLsз*%/cs\)^/ T7D:wJ4g1Fg͍ЦW gtO;=s:ӪRx/0sS$jrsެKH-Cn7a\J_ƥGFZq?ϦǑQc<ץQ{M".;o7F:c-N} +:J_x:I^t>?I}_LIY)b~ؖ}*r;c=3> }23viq•dǼR0Jt%E;Ϣ7/;FyQvHv}㿋"ttm/ɟV=DSP7|I酑dVg\u's^y:r.A]u g1Ѻ6X}|rslI\Aڳ#p=?.oc8l8a$xU>ʿY/{ϋlk`ڃ;J>=ǑmEe%um80ltǴ)5.srSǓm6^`{ʀHs3߯Ooy<=Tǜ;8Q=pܝVo#Z9yB$j}cu wA=#.u=2?'"m|iOژ}7y~ǹZI 4fznnψ4L':e^VgwpgvܱL[7Dqlwˎ5??9v9vv> P4yu:7ogzl) @/wfԞI;I:S3_kjo=ruڞn੎ڪkp6kS8VkSrwJpN{o^Ѫ^x3LR悂űx2xdF4;s,Usm?X' :O'|@vQ^ZZ?:^=!vZu tN|zc{#o >~1ᲨݻВzɌ4/륢߹1mUNGS5$38j'n̽7 + +NdΉWW:5P=Wp{@O{Of4(b8}{cPbB|Qj+ Kާf̽7 + +N//Wv8&]y Aa}Og̨`VtHGUۣ63QFdylOueQpr#hB+>heAKE8^udcv8v^~nP׳~ݸ+汒x١-ŏUb?.V>"ŅGF8>Wa:ڛOh6V|pWTcqXJTbEؾumܕ~بOSѤjҩ@]3ՙrFOyO u_ޛܿ{ٍ<@S Vw! - }+b\-baz>@ڰsR4kGT . s< b֜ӤMOsCHgsVؤ:NPKrNH%nX~n?ut|`:U~lf83o78fE}sΕϸZnh`ԝ~pwTe|{TwE3 #bwf! Kcp;i/V'P""o~d?j13gፑQ-Ȑ}W\IF/cӿ&ɑ&tZ##qN$pkQ=3KxYXHhJ/QޕQ=CQQ=a/nqF=CύQz'z/+,3(:̌x4^+Q|Hc=Y nks"ό S-3Ch>^y cuҸ7?ϫ\=œ~b?syHgd>]/}V }k>g+"ͫv;5jLݛ鬰t/: +ꥷj3+cOlƑg=f̼]M*ϛw)oک{Ky:Ok`D:>%ic(+uoUxhhoꄜwI?`.2y.ehf);: 0?HžD;A1Z/^[<"zܬwuHy{F3')=U,jk51ҹ3}B=#[q3=nִz/:ܪd#e[P/kغwx)]ɦ.zL|J,EyM[^OWr2|F|ީO(רKub ?<|*%ľ?7kAwVpH[S߯zyۧIsW)T +QnDłi wy 6UYۊ߶}]ghgmH¾]6c d@@>l+ޯ΅.ٲkc#g(mHtblE$(OZ/<,ijj-\ɪdz>^{DyNy!̘/ܦY(Dž7tgnWY|V9HU[E[ڕ&:}bt7oA8@N^}i[T5eG~(1BިzdzQ\m;w0oC7檜{ʏt5UTV_Du:zJQ }kR5ւO软^s?U}y,oTVhzTF^s0h4xRT{ 㑕Dw̏U˩C]4)-yޅgauU*Zz|c|uߥvXҤ̓UfۄOZv(-竽g{l2:y{m}yFmC^T^ Hu`[\HxZ kėlSCwQXi:#^fot^)x' Vzv_ +_=oDZ˾8 +oٯ_z~V[T^cwOJبe?w4+ʓEcm0ѿ^UW]V3w PKD^:BWTGDZ6?G[+Ţ +EzfjtsOs]޹Cy+վ;n3E7ʍ.aD't} 㐰W}4zՖEЇ!c@Q-^"5:+m(3cّtඨ=veyҳO~+ϼg0J. 8/kT \1ߡOxE@W;xo +< A`F.Mx_<_!}=Uӆ_ Ul4\G-%S'nR;EJޥ27<5aMYϩ>`3RE/'*b|pڇoWd c#=7E+,yz$_fWxC<9oV*%z9<Z,Nwz7|Kuu*u)y$k)г#ٻ\"b7cȏ9*?sdMʌ,ڣ(!H:>`׻2ݯ2AӨ|s]*Zn#א: sE4K_ʎ<itk#)gD7^DE:cvalolF"ox aʘA~܍q嶨{>79{ &?a>L^{F7џAy iIնu?I~z^9ҺfMt|UgaAO\~rlRZϢsɵ^.@.RK?;g?.s/:y0zyy:R޵WeXZݡgTK~:+'*q?>z'{~]+[1I~`c{W·#٩ R@[W9_p5c;!VKA?h{s*0:=F?R[gz.tὺKmml)L\q izrmJ2ωNy[OҾؘ-׾n{Hg{QQy{?1&jㇴ݊Spr#Կ ŧzv\4E"GZ)ָ7Sa)˧z/1ݨ~e 3:9(>F{PmBF^V3ɶHc2W"jYBo_,_䱜Yyk)cˇs +e08A}q2>S~H{:v|4GcVG?PGTn5+H{Ker=ozu"q"wXgt?:4eB.nВOT_uu j/{er;#G#q,i#At(J8|]tQuH(?s3eDݏ^`A}~1iܣV}eeLbfu`įT +21 "YRk?=6}Ӥ./}szJ`.-kWuNSw]<~6mU~ByZ߮hAmJۢ?=S/+Oisc[\cַ#o^} 1 \}~=uN,ZQ+Eյ?F&}|y +{O^3/~ψ~kT\y^)dJ{t-s>'آQVQ?vvE9N +xRs_wcն̋Zc/<=Q|?7-s?-Y$m:WZ$>纣>qd|GH+7+>*~V﵎m7[ε Jűj]_~D_E?#'X.߂Fu}W"퓁m8a;ǭۨ9'Ҽ{Po:$zɾ'+oKAVG'+sC=EK#pi3iay!:+ x:~Ƿ#skhXOn9mhMkvfe>cS`xKՉi$[c񯎢/ +l +_֜CzX¯nVUC'qKdȭ:oMz^f?7;t#mU-3<y!eGoLV}7jG Q_i7|d:_ ã49U}xH!?2z|:S8U ]wC GT6X; >o +MK4ketXhiBFEUnOgEumʸ[cܩ&sPm@H:m[_k"iHzu)hyXϠo2}"I|moT}OEzvp|U/u>Viܸ?Mx@CtT ?|Ǽ25?=Ww7ؒ7 &fYz^#1ޥ<}3cUy@͏߳{Nqw 6cݝ]i9e~>ܵK]pO:΋~~zgp4w&i95w{ڔH[ 襲1AszZ"m~=Mmv97,Qo7 cEuQ. _^5BmYx{X }3s:>/\~w ߏ$QDc)z >X4qNñzξfH=?TsPmRݿZ59GW e+ipD{zDרo7:0{ܨi=[;L}2~oP6ׁzsg$guǼUs+56׶Nw;\wGfW.W?O]rܷ4=#>hz\Dyt=vo6~]ښG>ދ ;XZ{DWGB^őto?gJ8!}~vV{j=^<ڥ߯vOg]垚7W/ֻdpQVNBoKVNu͏//v(>cYZ5*: 앵ð_XWc?4뙋tZTq٧M=ϣۻyϻ˲ `vk6xkesHsm9?sQnwsxv}|BşRO#_恡SDb=?evWo?m88 ;x0dqb6!YON2nxKVCGyiT:k֨!೗F;t}ͥ|8:E.Su`6k6_G͉k_4uyc2'K8cQglڿ-*x:5S+`M9ӖE>vhќ+t{PG OѯSi}?h0g۲/-A/ܢw)ö/2[8ʘoKv)Gy75Ǟs}<N'deU}o{+Oqf6Z/׌>8Pԋ}p^dyL5zkv'ƭ=qgdiUSwN~>?&揞yc=D*Ǥ>"Cz >G= g;4bFNh,beH#ncZKOnG U3N'}' 3ө|pOwD ?x30."YtRnwvz  Fe`=yvJr+E}n_yكoH~]ǣ/|g!4m\z]VFGg}Һ>9vOCNg^@x{Sh/;x.B^½sxxLOF)Kh =d6#:H9mt;пz20?5R ")v bk᝹\ɐwޤ?ذEH>蓊M`9l׶xA{xFG!5'sqB8~#i_|Nʏz?^ !A "6x2Rwx.>|BP y*(/| VO# [D..΋Oөx΋՜{zVʨ>3֣s]=r9iw1F.@r*s:@竽1tB ?M{T&I[Чi W}d0ԣfb5|6O +?`I$ߑwDmkzg3F貭<'r==xR8AcL<t$.vpKєvMg cλW)uA=.|  ̀k͹"W^׬oH\qIg^::f=vs f1}G5V<=hU9K.)/x[_xP08\ӹr\pl+R=)e]Yb<]|&&e}'r;9]C#Q]E:+ +HM)Gs8&wMWE'~ GmC\> [wms tAtI_˴І2G;u46w΁ٷp~ft,g16s[H(3%atA-WW[FoWϗPQ&sZyكO,WpB:4ҹ@>FѬylzKW}yՑ^қgFE: [yGx?L#|ܥzѼjf/`[cSz =;Rus=?g6LE{/o~+΃cZzs^'Bx_+ʸ h끎Gp#3H+6r[t5*RL 3 FD˫wHtMjcӘ8Lڹ?JszvAQs˗ʱ K# >Pwv~ gD\un^5 F!?7s:>6ҹi>~W-ˡ³>为_8!)c28@mw\нQ{0c{sZ*,wۢۢVYn7isD ųK/|O4ջVnu33x}u}[* +Rйx*)y7C~W4V[oǕ.Wp! e~fD#jjr\ycpW߯\AYgܼj=Oe<[^,5\x3>n|/s +/sקRm +]LZ~TcO{N'֭cmR휥y{tm?{ _XO)}%| qb\4l>\X ik[yG7*/yVsHFX:^ZEy.sjI$=uI]}tMxc'mCAwL 82g|o=~7uƒ~zDX\ DNڨwmёnxQmz,#IsOyQT89[O?]ݢ2+ +?XoQhdz~77o $XZiMNWNh>XG~L)\Sڵ;_Hq>ƈ~y.R[k6}$^y$ڦH"^Xp uUv1FUEghV*Ec]*I:rLa}"67{=!p/Jq@ԏ [ϋ ۾,Fx6<;[5gSE:GHrw9ypXm٥3珨Ԗ/WA35*눀 ^3Ac.Y ? jꚋhwVvfHHHr-ǷiNk~~Ds}OuHbۄ+^︞#3Kȧ]NQS_ko~;x}+ۣ+d2~={Uz}=ػ7do_n COؼ$P]ϩ_wh:EoдO 6G- +wV&|U87@qZI4fyLϛbK8߫L|EW"ZS׮H~2S1Fշ綾?tͱ G5$ڰ&晌 6 m:- NWÃ8#ŌK̿F}3f۾!떇#G4{3GM_n<ڦvQ}oDahE:w>kw"Ń` <GwHx~Ħ{TWlKd2q$^m_Er +?/j#8~]U~g;}/ɮ*S@Oݯ~[g<KT}I'g_ſ8Pv z j{bsKLUHk^EAsfte9i? +f3ؾxۖ>mi`Hy.Ց/Dc/\Gn@>'};WjہHGB}BW9:Bos^ ? GI;[~@pb$8͚_k;r-ދa$^wLo /=_zO$?{#4#W+9 z_ogQxI1BnVCn_ߣ*G4LӃ}^eq7ǰ6_R WG/߻5^38h=x};^5O + +_u/jY~)Ѳyޅ<UUX]26K3qе z2Y_F@_.;~J.vY> ]UбY{>"sR?hZ7@T&a*U6ΩY&렇"m_9m*ۡgx-ze^or^ߢ*?#ȎDmG},Z*j#>j{*͜g>!go^w]pASuQ|nŜ֩^+GwЧ-{8~Sd_c?[s\=̉i*gm\nP{G8.hu^1'ވZ߮;׺8\Ͼ^6:dMb|TyAoRg6{!`yX-*~+R< JۉlS?xMxh3rƃ.iyT~*{peHr~[o߰F@nnZEz㟽i6$2{ќJd[7OGoD%h6F-#`ѱHN96ɵS,R"|4F9νط nSڿ%ԅ8{[׸\p~x %ba5y5t]s>M;"y|h_/$xA^WVF|4}/EZ*sG`_1Wi?|/_E<뇟3WbS; +Oh_s8Go)+-9or\: 89˧>-tWfn`tBCڼ^#<#_κ߄/4M>$e弩2_Sl!}/QOaЍ}iH1֭T:/N[=c;)Gj6F؛y COutw,>,1֨Am?Q[z6}Z:n(Kv}l_D+[la+=mP.uQSo)qRZr 쫳-bY;{UOYy8Spb['ھFid6kD9(?KGO׿(8'}n㐮IzO#|Mx$<!>Td}Vx=HXO;j:O 1iOi \D_R}lm0כs3ph?ȑy+˗HcI];#H~"ICX4 ů4sj\mF)˾ft}V_=h_|_.I8#_YuR1C'նc=(_*`W/G/nĸa,A·sGc[0{M~nes DweTDͶGܢg^c6op.usHKE l]g]n%߶oHzt_,qWy;߱]u vOHvObmb# q W,Ca*H]e%K"_m$s[ MUyN"_ +ǟDZ'(8F}gTizcOCpuBK@r+_mk,_'bN'8{ב%Ѳ}C^W;f96޺;a=(>N Fb3x'*?eWn|vhmEso=kg:9Rlzގ_/AIx-t~Jx+xx$~|;WkHVP{މ$Wľoc3"{<'*"&qLs6³efy>]uvWG}33tdh93y֖cHs#vSm]&@FڃK-3+r\|%S\g"ͬc^Vզ_.wMqԣ+F:9M^FB76m6dq^'X{k;2W?\ P$RQ.iZEWO<}޿)lڵ6ur5#>W^Ư뵣-y;Ցr3%y=Whrud%iy@mv)KDwhth޳=s{0yE#Du72()9ҹO><){n圭9f;?t$}f_ oC6Hzx{GkVό'w ^sI,oK#fρK/d  +ƼWp,˚ezD{͞iBUFxVD]/~r o؆5t)۲%o(]qS36+Nb_m6]Է;϶zwac8u޻!R>5t^|=~*R(ḟp80Ǜ8ik8|_Э酢ͥ}rhi9\wP}~d8쳇=?Y`1\cU e^MtHzBxװ=gNN>-wzGSh| ^,5?.9koVG6ޖ9n;ԎQ)Es,mɻ#4n`De5-!"9=&| T<5y\3tmp0S03Y|x46Nda'3¹qjZH9n4wa#Il=)7N}_z yE:63Es o80jmvF׀Z;~r[Z;|~K#g'gN.1iop\HgAFEkMy9~bsܡ5f#}Ks !N{Zѥz swK?G$ة0X_;vu6vt^Y'Я_y%z-s?w x"zOEگXǹ2stѻ/bf`nhO4vA6.F_7zHcUd-kw7=!o9hLAş->U|˄)jǭwZ/i>^"ڎ߽Tݳz[9]ٯa</tvQ;k^WL,Әl=1CW&R-2ZvS.Z6\lz.%*HqKź#R-aˉˣVNv1:EApiI]Z.rX{a&x+?/6q==.4y8Mfz .7Ǒn"4#':=[u =k*O6.P_M褐+wsqx < Z9cPޑt\B O;O85K6i#zƺP'd01Kb.yYoΎYE< [O~p:&^9p$u,>L +&;bP8 r\RcO׎>O8VnɹTosmZcX4Džo]N8{9#_cZ>J[YjoWxf㵳օio`_uQkfuyߘS!g Z"]`o +=ZzoSKex$GxC8x޹$pZ_\)&S9%[T//Q!i+nq|bۼ'ϛ}>>{zkGm |Ͷ{y3ΰ=ҳy^])|P+54.~Pc@]g%PLs~O }ßc]`x#oM I; }q#%W<%޷A>ϛ gt{g=D`Hy]qe|O %}ioע^ ]}*D՜.%H{/ڌmzkt94g5PoC +^Pllq,S[}:PoH|ޞF{ʊLyY΄/{vW{aZ{ﴵ$bx]J942\Η +}ߡw +Qo)s2T\p~|-ȃ|17{\AD57e!;9U9+3ˣĮrhG p~} 92_6?rkt9FW'ih#㔯/zO*^gj|C1/ӐH-\Vz9B^./=[nj;6sz7x{K5?4M3x砻`2!5+Mu/& ?3N̷ueZbEl/OOs~wT{zҨ^~⑝Fkte6oyH.<缂i~zEj'jsO#it`}/33dV?Tm +\oŇymOOΎ{G9}.뮍ꝛKȤ*gCq9s rg\.S>p1M} T&m<I[nטW?!|N~d=a^Ϯ|ޙ|Fe\&F:G5NQ'OJׯ9Px汗 g|yK %4W/&1Cdxed||??}x׺c GF6.Ñ;QThq{ dnc|ǃOcm$;dcQ{~ѣO;}& k{:}>pRѱ϶jP>~>yc`k~I}25dҨ]ZjlZ#u8KX~*jMҽ{[8;N[M۟;_zDMyp> -Z*p s~ޟ6#_ՎTv;z%]B g9DUu*~xn׋?&~/]-~L$9o/*ave|fd$ eYr=Mu]פţWQ&gXGQϤ_[eR߷_D&!als k׻ Zձ0ҹs"پ Gvڟq_>N~R?>C2>_\QH>Qd2y\oj^FW>wH$lA}NY:|]ߠx޺m׻4DZ%@Ǧhpe*mQJciΎdcuۧğKmW|h#D՗jk_Wwm*s[6ğ֨^E)fmpxHެ +W3_.s7h|gh;)`ƼY7](< oY@v/#юl5J3NV]yX@[vO~)^GV!yw=z^лڽX}ޮ>7V}s6=.7v % jT/<-^>f}oFQ}owJ>WwwAm;ij^ƞ!fhډ|SUyAE#OW{_&Av;߶ ?[9LDTHhPdF"n{p*o|Djl7K4&;#ـ[ czeT>A*\9cC"ܨ16>cHr`$9|X?Io_u@c~D@^/t5xl;vڀ~E #[\)W Zy]'E]pyK %=@jW$f}cX?zO_W9.Ṃ[ē9Kįv'Ώ#>e(Z zl|vo*oI_#{ Wć ݩ}IN#cYG#IMޮ;w_mzF炇5MnEU * +ȵO1})Һ#*:دEk1sB ИF87i'Hhͪ;4,߮}8iMlo$x?N=!4Ԏ#*ߺKk.kݢ^(<3R}a}`|-%PBsOFoZGċWw|~fx_x 즑§-+zMn^,gldH  +>r Ϻk=Y8oP}r^wV_M펡vǚ{ _Oo ERY/D@&[и)j_H9;6ڌ|]mrHgݫz;dΨI>zo~yZ={6Sc5HczMZ +~<_T}=>RPYQ /Ӝ?#^p@<Q~_ +ߟExO6nvM2>J,u{}[8p\ҐH?_yaz3oj,Q; +EE/ר.~WF2E}v/v|5.f8}+AY' zϱ'4u4Gx|Op̕^Wd9k[S̸m߷"?ŵpn x~,~)K(.ZN$)>5Pb nL<10Wmσ;772}xxؾyNd&cz/' +?\x gbۢg|~? }lH>x|M{W@79'_d sJ ZYVW!_d/z)?e+|~kzH5Ѷm[4NS#^^<im}i*gm\nOnv|ձHqG#~C_\8^`Z'ھ'W;Ƒxo %C9 /GNCC- ?r{sOxs>11ωǽ ^u`x>|:[&ȺY7k2:>rc |8owkLy=_Uܿ}Y/DZv +F6GaT=mپΕNiׇ,vp%pN>^'4= +%2^ V|'R(|x>3? [kA|^̳TӍ/)όTdq*\ߎ8mTΛ=~;H>5efORםOIϨm|d /#ӑpӾSiNV3:+R QFm^#0fN]) +>}#xc*H{漧5w{3-=Я +/U?jپΕ eQAG``kD\B )h<4vd}ϯb).:}>(a?Ou#OK)lTH9st#R^b9eydޣAE<aP]ϩoϿ|)>Hߣ6:ᛑ!\:V}@M'zWL[LՑ]#?=jz%zyCx{T'}@~9'ٖH{3l{ʪH{-=ZW>*E4WӎuzeQΦhζ/75ZxۻCc`-P9Q?Q=kǾ,Rl^ny1R,voS#Zw&3B~†Uȸ=6eJ8 6Ȟj5c s<VOF\~&.ggX2=;5v}0zջLi+td"zyUՔ-3e_cidMfQeJ8g  ~8"ɿՑ|O{uƋtN9ŏ\-^HitSj}oqnos]m/T.O|W.waԞ;e63dKٿ0j%$$AF%1|\mfG@G9UlѧU-O%W ޽IM8^~^Cx< +ڹ]8w6dHg?^Y}]Xz4՝A9UY"{8_)چHB7aiܞ+ڜH~Px."Off;Uu;}03q}VMo#{3ۖh >p1S+pE\*e缬=eS#ŢP;'qwzMwdyILtͺ^cC#COE:c;1~ըOSη鳌ܦ*?o#ِjHȴ1F:koӢqᳺ5cXԞ}x6QgmZ + BCU)r:Gyo %3^@ FE: ~tg#g6W,/"6HuW "[u,so۳rF:GUóW>GǾZn$^ WH2nHsFefʛҤީUQ}8;Bucof6ݠr3sQ#סYnB3}E,W\O7mOZq1Ti!~ާ-3t6@=cŗ ۅHm\B d + &~ecA~x! /-~I` yx=W|k]] ě̫[_]:>=_/5ⷃ{m\/VPGoᥧ'F-M}ϵ|}#S]g ڵUt.eY{`M&)ջٺ@. 9ui$켌&X;tSg46V_ᪧޑtNh~gb1Ax\&n/[B xW]6rGW{Kc^şgߞ]޻8RNY_K +>xFϝ#,];_Zno{ ,w,W͗ #wI$}K,fQQ_z/H4(cv^OvlԾ ըy{]S>9]_Z.UHpeZNYv~]H 2[ +_5gPB cX~~5ZgJl d0~1t="ӹfuNx8;…s$^^ m[:tF}]21'.5vlZvibkD'Cy1hF#;j#^6Ax߹J( tvxl?%{9m>;?֋ob9O~ Jd?qkz{tlc/6K< D6YsxӞGէvV8q|i> Qm%9ckUCG/3=ٹٓw!o8uIcT| +D~tOGnϫ|1ֹ"±Xy>2EHg +\?$-yiy>Z<Tԁ[ssWcd6'gE3w®wˢ6F1F[7 .9noR/x~⸣KUN>>zxǹ",_xj=SFcU5$RK>s9A)ENgmpU_ 1ᆜlODlHya,ji:oqa(*3罪ڃ΄|n;}yC %pz!:|~{:j3p^sëkrx%rz(qިͷ4UϘGMo:XH38wsb:qVk>YO !r(yQj,Թ.;iY|ZyJ).,WFf}#ㄯH']B ?fZDofq7=u%jc%51:tk뗓 ;oƆ^sgr0g +s\w^impfF:shmƉZVR#ȕpOΎ}v90J^;vsk.xU[i}FY~Cnx [G-D9Ln56R 7mgsF!GFb.A|c3/Z".jy0,jiE?B;*|A 59Ng?PB 6Nj3Vޖ>c(]Y7>|a[o{ħ}lkԞ-s; +9w-㸥yj磪 }/-hug~3/oR9w"|gZ惿[զiEN+uFNdCF:_ ?g ј ,CW{xYO?%\Ln׸͏t2!߯O>* I.t. #$:I>Ssƴ0UXW-~CjH4{C %p@7n,q1p+ql7^E{-Obx*?Z}x~;M}d q|;Q}{TmC9+Կ=_T?-uѳ;dc%.Q]NG?CA^!g77n;Ju\dY%a뤷]9nPWF:yƙw,OZ"g=2wiTu jHg'_p;#s|#\&p̞iq +|R_VW_}O(^GlvGcs \rDu3?wKJt %!#Y_~U:Mމ:쁨T6ݪvww^{NO_-HUc}K?k޳\U}E^Gv"Ge8D@>>w)ysc|pcw[zN3 ]>N~R?-eŹ=;?g#=.}7i̷ +wPߴyܠz$U_$'m2݃-++uda=Q}SF Qo8"Һ .zS8<~os +B/|z434ϫ_DU}Uyĵrڊܽ].׽}G@ؾ>s! ^NY;#a[u6}ou lr8]z_j`$]`G$V=y5:GUHn3hy~P甔PB hj],Xx}[1w}TzwꝢv8VgxWpb r6CӽQ; * ?SUyK5ؽŋȮHK[9Ll@7x>~+m;Yh;Qg7 T8D"~+FU[?1hLvFUF3.[ c͇?x^o|P;?Q]A]~kmR}{6W ֵ]j~g}3zố1Zi)٣KG; +:'Ny\!Vs%g?PB =@ca_7{{$af/{S|xH1_3B +^Qmz%O{ۗX|-(Wczl}o*oI_# WӇ ة}INïY#JE߮}_mz4Wi,EE(\ ?oHޏ `ycҘ8uF cqF.U= szg-D?kДmC-|IF7컺5[^S9v =bU'hl{k }>Q_xC %p {?Ͻ"o -ܷx&;niO*ٲZϼ7W嶯s+9sLs'`a[#WFȽ?WQK_K"!_}_xڣ\εL\wVFO +.FavP;cMeٱ^q'W[cS2;ɇ44.`_Uz|_zUaޯ>ZCse߿;kuUyH8s|Yo齪EX+rU&cGTCh}M-0D&D>!i#>x@x/~){{U6Hy]}Ml;@پޗi[TJ} YE.}-@6Al""nǃmD<Mz:UDU-ئr\S_ƯI5V_|YWpHv<m]P_U])A/GGmBv.x?&_xFm 팪? }=E'ywlH&?Qepi_c#*"λ MBy:-՗~|؆WGyR$5Pb nL3Ŀk_ݳC}R`y:Bis._?i}|ƨ=Q_qܴEam:޲UG"ů9.{^ rY$?chuCt>RHsK(GQ]ˑSVD- ?r{s:'?Rb^CGޠ>9n tm$_ +UVi"7PlYuCΖޏqcx,\- 9'= +%2^ T{G}DZ3P߄-5 U/g)>6?R 7B|Ms};~;U6 zFBV׹>5efORם' OIϨmd /#CXgc?iN:ѽ#_-n|XMΡ_uQ~DmGnz-cKz벎9v/g:YC#ǻ- #}y +/1@'_l)9%J>W8hxQ߈.l@Z#s kSئϩLsN=_})/1yrYɼGuoH{Qy\)F!~!0R|'z6>#ܽg??'X> ߦ +^&D[L>R[CZ ;eC:H[4޳XFM=G~qTe67)1]#+@oTm "Pٌ~qrla ovPPyro %ޟ9r@YX*~b$\xr{ +y4]uQG"oM_rH6%>)ֹuj͋# nu.Ey,KԗE򗼨2m^u,Q<_ރuiM~=ýz,y3.u?1.x-.>>+ E /]b#Ðe#_- mzS]}Ȍ?tSm^1n(_3~j9c@A>ϗ5tb!klyH)_P2>z"6R-=r`:t|9N|o/>f?_㹮AgƫIrR oWO|W.waԞ;]hߛ"+3v \LKKϭlPﳪwڸPec|S,&G:j^7{Z9@׫ʥ,>|ߞ#^ FFܲNsE8_cE<2cW|Rk$xGs2S5R~L;<*k +׿8o(N/d|紊/Y≝~PxZLO+vT_ᾑ·^"~sdgͶ??,~T3\F:s~p܄K۲3? w9FELvb'MR3ꝩ=3lwͺ |Y5}5^ޛ\(`={mFWr^r *iQ#|%(/x~opZ +: %P酌upnώY>t~O9Wl")ÃTc"~xkCğ}1xg}ΏQଯc~f8qŷ +s3#͛gm +6^I tԨ]> ~O'WHǨvV,;7O[ii3y *weEF1Ȭgdz]r>_Gks>kj0B{ ~& +7 ~G|lUߎXu}-Q^?,mm-}%#ȸ+e7ZqC_9[nl?:/u"%lD[pF.W]OWϞ/B?^0By>0U9[xo kҤ^˟AHK]WToOw1&=ʘcWsv3|l) Re*k{zU.X#Ӻ]W߃aiiGow/_FA?_yF\g}pw 2fCuf|sLCiP +|.Q?9ϣ\̫]tHr>xչ{=ԅGp׺K +>?w#g,Ɖppy$1߿R,WyGUej.ufuݣݣ^'EP6 qv^Ov\;K:2'M$ǹU14r]NjtZ"53'RֽeMLÙ %PA>נSk ql7SslCfug<߁e_x]H29/…[ b={y:yx׹%sX@onh"6z9Ԝwkt\0 ͋O5sv]sZi|p}M^ٻ'%PB ҹ~~)Hg)}-: rYdkg#D!#?qY^I0RbBuxDžs~㡄J8w 7J:>}t&sO.ubFرSgtHdD}tJA.V{EZw:|?»g%P¹,u4yݯ&AY'.xPGl֜{HxM +tl]1[scuˢvGRPB ' 흉ŕ "" +3ڂ@T4L"! +$tkIݫ/wTOXk{wNgש}Ϸ<;UǺy^';3_jd;}^gժg[kr)((8skv&n m3 ~ogzEvTu<Á/)\ς}9#Q'gV_4rp/E޾_FUW}YT}R؇E>j団ߎ||T\CL;<+sX%]>& ʪ:i˞pAyxi nua<߽"ϸty;}{.k}9'qgp|4d_"ќ>&;>"]qT?7q{y.+bPc>Wg1ɘpF?H~%[9ܯH S".Mtw\:˴ssuEDXn&git}uF/((=:@82qnW};6]Κ |:F,y}WXO%R뢿ߜ[s=2"i" P[GU4GqCyn)fōn*,c/鞫}DfDӢyƃ+pRYwe|)\^3^4WDӘ)f.5?~$[ 'E=^[6J;EjHn^#[:CO 䐻$Nq D;IfջDmlƑ8ϩ5ޢ:~5H"WDϸP7ckvv YV:}ow +_a?1%=#^F #blC ßҏ,mʇر̀3?7}]pE#;kҫ#%HtH>:=.8g,l6={k}({sb:Rγ*/97E2ǎep7vvL~\-昒3WK{UU=zV> Hdr +q=(̓2b!6nɗ/=| +yi?Wj&ޭFOs+xH8cfľB߂_֞Kg82'pvV鼵ҳ%k >W}{+=CyL۠?/+(*T.3N}tAvn\غO.mÓ7:Ȋ9$k^C5LZiD + +~Dc_h> 8QA?^({ݞ26?ESt/V)tkJң Ɩ|YuEodֳפ o +m'jgn*Y/{DznWJT:=MxthխK9/kHNWnko" lHܸW^Pm!vkFW`=5W^e̹+EuT}˜_X_9$w;Ȋy lTw/kșw'ޗ(((8 e}Gz G%6\tfS弗ckͧ7c7:}gs հ.̙_,\iwrݬPiג5MuF{vV}?wEܙ?"G:]L^ˬ,T;{ՇGNocyF7uu|@zg^I _s*r<@4{\K{7^;)WwqAIԟ=sP~I/Xo.KEFMp8W:[,ͫy˜)-@=ƣY[۵zwG$|sX!gtX);kʜXl,>^\C3D}y?6z]5d=~;v=*K6xHg9Ww IT\撌VW;#ui/~:g0zI4Xo߂s19p svx YdN3uW{k#{]V:D#;Eg.Bg k|ttHg/IzUMzҵ_Kխ]{gE3TWwot$?mÿ4IFZ+˾Jmx@u! l raN=xxG#٠|o)/~c;&Py3FZ[SRgbZa2.{"NdS䱻xq՗ur&H.g + +qDsq=tPmj4Hrfc.X 썿HgbJkeʋca #6Ii]z}z>R|6LxH'|WfFa^eu˿}g}3٧#8wudH_s_8m4z}==!9R}T/M`v;t92{NϿsOJF>JY^c[qw l}$/8ݺ"ҧGOH}O%{u+X>D+nRJ]b;G:ۃ+mu#7Wzqvu\v"}emvPt,vl+:9_cΞ<{%$Sz~Z|g̽[`ןs*S9?yՉy ̜?Y.}^} kG\g] (((8_1>p%Îb}3"ә^ב\>HfH~݁OqS*JGuo #k-68kF>q^O3R=Hg} {xo^P9cg;e#~B~}A}y2dsHgvޥo-(8q^u)_Wa;5coK!Bskp}(J=X:}PaگVo?Vl5nk:*+ݚN'%k0;Xfoi`T!=My"s71v7E2тHf9vJ Y>^s#2ÕʿȟqVS-3L-(8Oo¶J6]~>ϑB籾 `ۑgXA:jH b7WbP q;,Lrx6k'c3ܶ9݂oGv_2CۿzObv߂1$˾7Rf\'+vi_I?wG䳢w9v&ym])R$Dϙ~_N=(}~ί7em}R6moY͖v|N~/E YSt6~I#ۃ/SDT^7oJ"p/5..@nE>O9l}>w;# $+vu$]EJ:R9\Pp _<HzDGGst-vL vH~g35w!+ JEyr\!LxYzz|Al;k61w\)Cۼn/8Ίtgq +m3i&'-q5'c.c_:SQ.E^Ƭ\IKH<<;uu<~݂Z:;czM{i]/((8DgGջC#û>/-6qH8>'4Wy,H,}S$ FzHqyGTIخD縇mGuj"{qDa;K^+g1}z3y;#+{ޙqbwVd5B9F.Ȳf+_d_Ͼ6 +`bM|&J^1YkG*2Hatc?ji]K#q?6if}\~N>ݺB+F3m?]w!WzH>L "^g5)R¨mN#GW좿B2&希lGHUd9Lu24Koz1*ҚEPwl cHp{x ?P9?6-bX<fvgMۢ~Cc`=թ,gz4VcX?i@hu=|F9}[[t׹lGƽ<YPPPPPP0:{is+]-((((((|u>kAAAAAA@DgO|P + + + + + +G TѰhzIpc7hp ~|kg%Ago$V}$_ 󈝀]d<Ӎ +w4ŏ}O3~+.ɿϯ/N6oq_4zzY#G 2mAAAAAD>/o,.}dH'YI7_io1 vʼJ<xb8<1xp[%xt˲րUN MUUM3PTf + CfT % " DPD.q€a5y'wN939oyk;?kbsEsUs]3Rso`y^͢fAo6h6fWgͭfyPxBy9Ѭz_h>Q?޿%\Ln'*lh4皭ϲ|WYlHr'͖litvrdd,1x_ S1TNminjv}swjΣCg=lL2mSfOs r~7'W&+wkLf}yun>~6_FQ^Uh>,[zͩx/m/&/RwLVk'S$z_i%XgRyݠԾ$dokklrp2MfK}3MpeKJW|hHͱp(E+_ordݿ4o|ϿK6J-Z\x_k++';%S\=>T gO$~/y}0T!+)oNLrljޏϩOݓjk;}}-熲Xrjp[?0Rt{pW'K>0*'MnN =umꏻ-_NoNZmu-dP ܿW5zoZ\7 +mni{2r7Cu~ )5Iؐzd,α[c+ޘWn0޸!YsYu `'VRVwӕ&kh^}Ժǻ{N&s8zY0/ )n0@HnO{wrM'5dl[UoEK\&ˇcJ{wtk捞]xp91X҇[Y8y)9[Z}oԻ\V7cMږiۘ?J^~!sxIێlSsV<5g, ȫsPV?diEoRx\[ +/K^ߕ{xii[:y]W%K9 +_L\獮=,9{:-̥R|0WjO Տ}[´Uu/_p2ߵ.m+]{>{9&o k 󒧕$3K=U5ZK{ߜ볯Y1,ƞ?|̃Y$[dykN7 [ 9oJ..Hݒ̬d֫71+m ermmggyn=/Y 톬sMw` &$˦0Ws]/F-Og3۔?WC͖1+Dosڲԕ00}uǡՔ/Ͳ~|Rd|2;n(loc[GQ甭 +}LB."^1|{oW}{v&11*4C_iE+3Z15kcEsL9&oG[gsE\)Ǖ}+X,筗اL1h`dݿ?庪)䯭*sEmǘ?xC%74,qs5?\,2KۊQ[lSL߷y_[nEjM9[3( \}_uƞm?㯚>mEz[WVruTؾo-fhqvxY)'2˿>&-+_;]2+*:/"Wf|?t)o^ߝٔ/0-r׶)\HߺzL=㶾z ﻄgM=}j4\WI3aJ(253tM~X3jdEl ˽wxWfU}_OfO7)eVMߘZ99VH 1ec}43߇/S~Fy[~p}(ov%+~ap=H7ӱ}`Bςoe<00maCw':S|%/*عn;ke3`U怇2+>>Y ]|~q#>Z=~h7vm ׍vv ?G=0\7t6g7#ZDFD:8quNoU?fvӰXK-l +m\==[7/,c,m){1s9mem{zf3bCTfG_m֙мf{Wru-o]*[ 2W@|poy!_>y圶~,_2}Fω}X~m&klcx͵Ô-Q},rIoW._t-Կ33v[7fۏZg΋S_}aoZEGW 5sr[Ɵc7glqm}mvF} ߡǟSs1]Ǝ0|2562BkT*&7WI]}6 <b-׶8"9$%ڊEW@__m7j.>w7|Y̭;/*u|-'4_W:ڸu~S7cr04>)&9Q)[J.]r l&[2_Wƹ2EAԔTY]oUS.-lSmPQخז-MP;55S[hr!۷Oj^]l}*MeKoNe[ ǕKxwЌsh^O*/7Wu~P_}*S_q~ 2/n+lcrM1X.dOkSɗe)u6s̪>mjK&[Ws6~Ddq=GqP/s|4lVE;$e3W긿fva>Yu }M\۵Y9u}'+Crxo,ڮ1̟PRgS6΍WPrf^cc0fjkǏC㟡SX}B.}!վ279eSoPxf E%f\Jj5KoN帵;ʐZ,i +rkzRǟ#S6ήgHs`'1sIn!k/w6o/9eS{ϩ{}{>}=zٰܠӭ7cvcu͆źUoG돗?s5[i7} 4L#Ww_mkMڊ:S>4ͿJ|`y||6l-ny|f}2=[SgKܪ}]R;6v)s%߮{ݳf V ͂`τƪCS7nBM/902 sb(@'?>a=t}嫃O^Z?~_߹翭T`_~Ƚg =:۸[<4ƽКW_vSt/qL>ޘr3mG_ O5U/wK>Fiک~~|uId=@N>^mz_vcsZ>/F=gO:x}x )>{L[w.'5W:/\_C{aߔ~f/E\9mmw7[ʾ,9V^*K?Q{V;!uC;Ûs̟묶)5Hq`,}n9wܺ`Xvg!cߜ5GߍҴC PK+n|+T_;~+ܳ)9!ܖ=sB[xl=us9W)u(^ogvrR?X3īɼ CnֱtcQg[_Ơ }6ו<ۥ˛:kdKY-߂gY{tFWjߜ~c-LURgאߝg*D9rs,mU4sVWξ]y-^YK~Z73uʹ_9MW:^ƿcr~ _EƇϳR$7% <,#bsoset\wWi_9^og͙:7))3Q1cU.<Ŀt~>)!7+,lU6*X)o\wЗSsߜ_C,4|FNY^>3Y?g3ϵsy\c w + EnZz(#eUgR#rς>4K ׾[WYc>~5߫V߇9%W9Kzvh ƾ_79_vu:W=s4V9s+Js0CjW84e_*F~z30gY HW-oN|[7)kNSAs|>Nϲ!JmMſҿ%{?~7&쁱ֻf/Tr02˾vLWqߜֿ)S4v34 畩c'-ӿ%ҿ%{m'oJ} RЂ!kp [T!\a\R=5m oI;(8%JW)ߍ})|+I"egi T9q_i>x1%}{~BX( |g3-&f x<^#R{ s1+ɝkϻ-:g[ 'v~ʕ;oX[ +p4~<]]g\eJq:C$\/V> sqĔ[נ۰0f*޳V5='9Wֿ_x>sgV;`\_miT\okO:kQ<ަ?hڳmGc}Lڔ~ѓ,<+JW|<߾c| Y:/}zY?\OJe??10Yvkoys}ľ;n]v(^,[W\YA֣εғ[j({YƾkZRXoe3ߞg}'sb_ 10t1f*֕ŸȈae..ڍ/zP{ŋcQKιjÍB>yʕPsoC?\,Ɣ!XEtzeSە.}6l_`c{~ʗJU {)aw_SKmiGJLk_^yk}RQ9^c_ttuَ_ MS^dLo~Y2|O>oJ}B@ϩrų} cN[2_xN.<omkƛֿYǬ"9uaT]-oZ߬N#omkEO~ļvlc1q>u,ey(ۿC?r+YҿOŸg/6-obL+kR:&GzxZ[[n =_QFڥF՜5a_1`zT{Ҿwap&_=ƾ/G_\5@%^}7vΒ^G¿b_L9=^zpV9ľ1v}VV-Y*Fgnsu¿M9p0юί +ǰ) 9U_r}ƿ }ioɹCltUm<7 2,9[A=7ƫ۩5C:0ֿC/{?+r?s } ;/t?4~ۗ5Gv|d>4a3狉nq0}aL=街+a0yn`܇+= kx`=,=Ci(_<3 +k[Cː5' cb*ޏ~ksr~/<4_x>s<ofl?[G>{s铃NZT7O/ľポ>[9jP5;Z{Ͻ?^aeZ={p\הkXovIÖg=ߞE3c=_iD]}u^kVg} ]y?΍_uxlXn]se[7;JOo_s/›Fƾk\9GCXo \1\R~UtHN 'rZwy׷3QMJLk_^yk}RQ9^c_ttuَ_ MS^dLoioJMx$}g~^{>zN^kh_t|,gku~rߔҝ{5_<ƛֿYǬ"9uaT]-g:gڞ_ݞ--54y^pcM]ǫ}X˜P͙C4Vrg[˜/`=}x7>{qlcZy]^18r sGкr[W\2:l7+8ڔw=b_1$2>՞]5k W:zőC78u PI-k(GuZC_8;V2_:KRN#17ծʱW×ʿ%{?W7WGcRV ڱU6ſ!Kԃt\1-9wƿw=41̻Ŀ)ѱr=7 2,9[A=7ƫ۩5snBȿΝ͙,aƾ Ϳi!rJ=Rq_7w^~u99mĚ#w~n*}͒%%yR2z'|1qTƀKq/Ln^ //̍_J{^|?lLe:7z;i+FR߼ח!5͟]:/y/k:%9fiothVi`^|c-!w\66ޯO\ߦ髝C_)[+-G.'M^U,D׽7⣠ ~h:oUuZ%G/;~z'OV+ulK?tn+|szֿ:0m~eNUuZ%Go|ۿ{I/)9 E_> / ~^^SVi`hC7~cԏspW\bic㾚NZVi`fj=DoZ#18v`3Z6e 6w_VM>ʙ74Γz]cCgֿm*-̋ 5)/+9ڱY7i]VGZ>u>*ǭ'Ah-É{1vyXT´2l>*V{M bknb^͝%*a߂GR84vД}T=uŚ&.u,]߸Zt3c#0'(O.Ӧ΁j,o<#y\%; -v`h/re]}{!]6eV[^Rm>=_H&_CoGN xJ,XJ "1 0we]#N MU0aVp{tvM+ESH*k߅6%i%iu~Rٞ-K#v6}t\>-3 9޷f~ 14yhP/N;v @͆֨aҴzl[t3Wf%{X#ޚdn3ا7|_S sbs&{wxロ/5(n;BF۬Ԭ- @JzvyuI]w>#58G>x5p \ No newline at end of file diff --git a/Hosts/Silverlight/Iron7/splashscreen.png b/Hosts/Silverlight/Iron7/splashscreen.png new file mode 100644 index 0000000000..cc800d1424 Binary files /dev/null and b/Hosts/Silverlight/Iron7/splashscreen.png differ diff --git a/Util/Iron7Libs/BingMaps/Microsoft.Phone.Controls.Maps.dll b/Util/Iron7Libs/BingMaps/Microsoft.Phone.Controls.Maps.dll new file mode 100644 index 0000000000..8b7ec1e5cf Binary files /dev/null and b/Util/Iron7Libs/BingMaps/Microsoft.Phone.Controls.Maps.dll differ diff --git a/Util/Iron7Libs/BingMaps/Microsoft.Phone.Controls.Maps.xml b/Util/Iron7Libs/BingMaps/Microsoft.Phone.Controls.Maps.xml new file mode 100644 index 0000000000..8ba3e2cf4c --- /dev/null +++ b/Util/Iron7Libs/BingMaps/Microsoft.Phone.Controls.Maps.xml @@ -0,0 +1,2074 @@ + + + + + Microsoft.Phone.Controls.Maps + + + +

    Represents the Aerial map mode. + + + Initializes a new instance of the class. + + + Initializes a new instance of the class. + + + + Notifies a mode that it is being activated by the map. + The previous map mode. + The map mode layer. + The foreground layer. + + + Gets the map as a UIElement. + Returns . + + When overridden in a derived class, retrieves the valid zoom range for the specified location. + + Gets a bool indicating whether map data is still downloading. + Returns . + + + Gets whether the mode is idle. + + + Raises the CultureChanged event. + + + Gets or sets whether or not to fade map labels when the map pans or zooms. + + + Gets or sets whether to show map labels. + + + Gets or sets whether the maximum zoom level is restricted by a coverage map. + + Gets or sets the map tile layers sample level delta. + Returns . + + + Determines when map navigation is animated. + + + The map navigates to a new view by snapping to it instead of using animation. + + + Map navigation is only animated in response to user input. + + + Map navigation is always animated. + + + Provides credentials in the form of an application ID. + + + Initializes a new instance of the class. + + + Initializes a new instance of the class using the specified application ID. + The application ID to use. + + + Gets or sets the application ID. + Returns . + + + Retrieves the credentials. + The function to call when credentials are retrieved. + + + Occurs when a property of this class changes. + + Contains a collection of Microsoft.Maps.MapControl.Core.AttributionInfo objects. + Initializes a new instance of the class. + + Occurs when any item in the attribution collection changes. + Raises the CollectionChanged event. + Contains map data and image attribution information. + Initializes a new instance of the class. + Determines whether the specified object is equal to this attribution info object. + Retrieves the hash code for this attribution info object. + Gets or sets the text which contains the map data and image attribution. + + Contains information used to authenticate requests. + + + Initializes a new instance of the class. + + + Gets or sets the application ID for these credentials. + Returns . + + + Determines whether the credentials are equal to the specified object. + Returns . + The object to compare to. + + + Retrieves the hash code for the credentials. + Returns . + + + Converts the credentials to a string. + Returns . + + + Provides credentials used to authenticate map tile requests. + + Initializes a new instance of the class. + + When overridden in a derived class, retrieves the credentials. + + + Provides data for a map event. + + + Gets the exception that caused the map loading error. + Returns . + + + Contains a collection of items. + + + Initializes a new instance of the class. + + + Represents a rectangle on the map. + + + Initializes a new instance of the class. + + + Initializes a new instance of the class. + The location rectangle to use. + + Initializes a new instance of the LocationRect class. + + Initializes a new instance of the class using the specified borders. + The latitude of the northern border of the rectangle. + The longitude of the western border of the rectangle. + The latitude of the southern border of the rectangle. + The longitude of the eastern border of the rectangle. + + + Gets the location of the center of the rectangle. + Returns . + + + + + Gets or sets the longitude of the eastern border of the rectangle. + Returns . + + + Determines whether this location rectangle is equal to the specified location rectangle. + Returns . + The location rectangle to use. + + + Determines whether this location rectangle is equal to the specified object. + Returns . + The object to use. + + + Retrieves the hash code for this location rectangle. + Returns . + + + Gets the height of the location rectangle. + Returns . + + + Retrieves the intersection rectangle of this location rectangle and the specified location rectangle. + Returns . + The location rectangle to use. + + + Determines whether this location rectangle intersects with the specified location rectangle. + Returns . + The location rectangle to use. + + + Gets or sets the latitude of the northern border of the rectangle. + Returns . + + + Gets or sets the location of the northeast corner of the rectangle. + Returns . + + + Gets the location or the northwest corner of the rectangle. + Returns . + + + Gets or sets the latitude of the southern border of the rectangle. + Returns . + + + Gets the location of the southeast corner of the rectangle. + Returns . + + + Gets or sets the location of the southwest corner of the rectangle. + Returns . + + + Converts the location rectangle to a formatted string containing the latitude, longitude, and altitude of each corner using an explicitly defined IFormattable.ToString implementation. + Returns . + The format type of the converted string (for example “c” for currency or “d” for decimal). + An implementation that supplies culture-specific formatting information. + + + Converts the location rectangle to a formatted string containing the latitude, longitude, and altitude values of its corners. + Returns . + + + Converts the location rectangle to a formatted string containing the latitude, longitude, and altitude values of its corners using a given format provider. + Returns . + An implementation that supplies culture-specific formatting information. + + + Gets or sets the longitude of the western border of the rectangle. + Returns . + + + Gets or sets the width of the rectangle. + Returns . + + + Represents map tiles contained with a location rectangle that are available on servers. + + + Initializes a new instance of the class. + + + Initializes a new instance of the class with the specified tile source URI, the bounding rectangle within which tiles can be displayed, and the valid zoom range. + The URI of the tile source. + The area on the map within which tiles can be displayed. + The zoom range where tiles are visible. + + + Gets or sets the rectangle which contains the tile source. + Returns . + + + Retrieves the URI of a tile at the given point and zoom level. + Returns . + The horizontal position of the tile. + The vertical position of the tile. + The zoom level of the tile. + + + Gets or sets the zoom range of the tile source. + Returns . + + + Represents the default map class. + + + Initializes a new instance of the class. + + + Gets or sets the credentials provider. + Returns . + + + Gets the map foreground. + Returns . + + + Gets or sets the map mode. + Returns . + + + Provides data for the MapCore.MapPan event. This class inherits from the MapInputEventArgs class. + + + Gets the screen amount from the current viewport point that was touched. + + + Provides data for events. + + + Gets or sets whether the event is handled. + Returns . + + + Provides data for the flick events. This class inherits from the MapInputEventArgs class. + + + Gets the screen amount from the current viewport point that was touched. + + + Provides data for map events. + + + Gets or sets whether the event is handled. + + + Gets the viewport point on the screen where the map event occurred. + + + Represents an the class that uses a MapLayer as an ItemsPanel. This enables data binding using an ItemsSource and an ItemTemplate. This class inherits from the ItemsControl class. + + + Initializes a new instance of the class. + + + Arranges the elements so that they fit into the control. + Returns . + + + + Measures the map items control to see if it will fit into the available size. + Returns that is the size needed to fit the map items control. + + + + Executes whenever application code or internal processes (such as a rebuilding layout pass) call System.Windows.Controls.Control.ApplyTemplate(). In simplest terms, this means the method is called just before the UI element displays in an application. + + + Executes the CreateAutomationPeer method. + Returns . + + + Gets the map that owns this map items control. + Returns . + + Updates the elements if the projection is updated. + + Represents a map layer, which positions its child objects using geographic coordinates. + + + Initializes a new instance of the class. + + + Adds a UIElement to the map layer at the specified location rectangle. + The UIElement to add. + The location rectangle at which to add the UIElement. + + Adds a UIElement to the map layer. + Adds a UIElement to the map layer. + Adds a UIElement to the map layer. + + Gets the parent map of the map layer. + Returns . + + + Defines the offset of a layer from the position calculated by the map projection, in pixels. + + + Specifies the position of the origin of the map. + + + Specifies the map position as a location. + + + Identifies a that objects are fit into (also known as “arranging”). + + Updates the map layer elements if the map projection is updated. + + Represents the base map layer. + + + Initializes a new instance of the class. + + + When overridden in a derived class, adds a UIElement to the map layer at the specified location rectangle. + The UIElement to add. + The location rectangle at which to add the UIElement. + + When overridden in a derived class, adds a UIElement to the map layer. + When overridden in a derived class, adds a UIElement to the map layer. + When overridden in a derived class, adds a UIElement to the map layer. + + Represents a polygon on the map. + + + Initializes a new instance of the class. + + + Gets or sets the fill rule for the polygon. + Returns . + + + Gets or sets the vertices of the polygon. + Returns . + + + Sets the new embedded shape object. + + + Represents a polyline on the map. + + + Initializes a new instance of the class. + + + Gets or sets the fill rule for the polyline. + Returns . + + + Gets or sets the points that define the polyline. + Returns . + + + Sets the new embedded shape object. + + + Represents a layer of image tiles on the map. + + + Initializes a new instance of the class. + + + Executes whenever application code or internal processes (such as a rebuilding layout pass) call System.Windows.Controls.Control.ApplyTemplate(). + + + Gets the parent map of the map tile layer. + Returns . + + Executes when the map projection is updated. + + Gets or sets the height, in pixels, of a tile in the map tile layer. + Returns . + + + Gets or sets a collection of tile sources associated with the map tile layer. + Returns . + + + Gets or sets the width, in pixels, of a tile in the map tile layer. + Returns . + + + Gets or sets the sample level delta. + Returns . + + + Specifies the sample level delta. + + Provides data for zoom events. + Gets the direction and extent of the zoom movement. This property returns, as a multiplier, the amount the touch manipulation has resized the map. + + Represents the anchor point of the Position property on a UIElement. + + + Initializes a new instance of the class. + The position of the origin along the x-axis. + The position of the origin along the y-axis. + + + Specifies the bottom center of the position. + + + Specifies the bottom left of the position. + + + Specifies the bottom right of the position. + + + Specifies the center of the position. + + + Specifies the center left of the position. + + + Specifies the center right of the position. + + + Determines whether the specified position origin is equal to this position origin. + Returns . + The position origin to compare to. + + + Determines whether the specified object is equal to this position origin. + Returns . + The object to compare to. + + + Retrieves the hash code for this position origin. + Returns . + + + Determines whether two position origin objects are equal. + Returns . + The first position origin to compare. + The second position origin to compare. + + + Determines whether two position origins are not equal. + Returns . + The first position origin to compare. + The second position origin to compare. + + + Specifies the top center of the position. + + + Specifies the top left of the position. + + + Specifies the top right of the position. + + + Gets or sets the x-axis position of the position origin. + Returns . + + + Gets or sets the y-axis position of the position origin. + Returns . + + + Provides data for the ProjectionChanged event. + + + Gets the new projection update level. + Returns . + + Contains projection update levels based on the difference between LocationToViewportPoint and ViewportPointToLocation. + + + + + + + + + + + Represents a pushpin on the map. + + + Initializes a new instance of the class. + + + Gets or sets the location of the pushpin on the map. + Returns . + + + Specifies the location of the pushpin on the map. + + + Gets or sets the position origin of the pushpin, which defines the position on the pushpin to anchor to the map. + Returns . + + + Specifies the position origin of the pushpin, which defines the position on the pushpin to anchor to the map. + + Represents a Bing Maps quad key. + Initializes a new instance of the Quadkey structure. + Initializes a new instance of the Quadkey structure. + Determines whether this quadkey is equal to the specified object. + Retrieves the hash code for this quad key. + Gets the quadkey string of the tile. + Determines whether two tiles are equal. + Determines whether two tiles are not equal. + Gets the x-axis position of the tile. + Gets the y-axis position of the tile. + Gets or sets the zoom level of the tile. + + Defines a generic range class for a given minimum and maximum value. + + + + Initializes a new instance of the class. + The minimum value of the range. + The maximum value of the range. + + + Gets the minimum value of the range. + + + Gets the maximum value of the range. + + + Represents the Road map mode. + + + Initializes a new instance of the class. + + + Notifies a mode that it is being activated by the map. + + + + + + Gets the map mode as a UIElement. + Returns . + + Retrieves the valid zoom range for the specified location in the Road map mode. + + Gets a bool indicating whether map data is downloading. + Returns . + + + Gets whether the mode is idle. + + + Gets the background style. + Returns . + + + Raises the CultureChanged event. + + + Gets or sets whether the maximum zoom level is restricted by a coverage map. + + Gets or sets the sample level delta. + Returns . + + + Retrieves the URI for a tile based on its zoom level and tile position. + + + Initializes a new instance of the class. + + + Initializes a new instance of the class. + The format used to create the URI. + + Retrieves the sub-domain for the tile identified by the specified quad key. + + Retrieves the URI for the tile specified by the given x, y coordinates and zoom level. + Returns a for the tile. + The horizontal position of the tile. + The vertical position of the tile. + The zoom level of the tile. + + + Raises the PropertyChanged event. + The name of the changed property. + + + Occurs when a property of the tile changes. + + + Specifies the piece of the URI that is the quad key. + + + Sets a matrix that represents sub-domains. + A string matrix that specifies the sub-domains. + + + Specifies the piece of the URI that is the sub-domain. + + + Gets or sets the format used to create the URI. + Returns a .The following markers may be present in the string.{quadkey} The string in the UriFormat that will be replaced with a quad key.{UriScheme} The string in the UriFormat that will be replaced with a URI scheme.{subdomain} The string in the UriFormat that will be replaced with the sub domain. + + + Specifies the pieces of the URI that is the URI scheme. + + + Represents the base automation peer class. + + + Initializes a new instance of the class. + The element for which to create the automation peer. + The name of the class. + + + Retrieves the type of the control. + Returns . + + + Retrieves the name of the class. + Returns . + + + Retrieves the name of the element. + Returns . + + + Contains methods used to automate the testing of a . + + + Initializes a new instance of the MapAutomationPeer class. + + + Simulates a double tap on the phone screen. + + + Simulates a pan drag on the phone screen. + + + Simulates a touch on the phone screen. + + + Simulates a pinch stretch on the phone screen. + + + Simulates a zoom on the phone screen. + + Contains methods used to automate the testing of a Map. + Initializes a new instance of the MapBaseAutomationPeer class. + Initializes a new instance of the MapBaseAutomationPeer class. + Retrieves the value pattern that is supported. + + + + Gets a string with map properties embedded in it. + + Contains methods used to automate the testing of a map tile layer. + + + Initializes a new instance of the class. + The element. + + + Returns . + + + + Returns . + + + + + + Gets a string with map tile layer properties embedded in it. + Returns . + + + The exception that is thrown when a configuration has not loaded. + + + Initializes a new instance of the class. + + + Initializes a new instance of the class. + The error message that explains the reason for the exception. + + + Initializes a new instance of the class. + The error message that explains the reason for the exception. + The error type that is the cause of the current exception or null. + + + Manages the copyright string. + + + Returns an instance of the copyright manager. + Returns . + + + The exception that is thrown when the specified credentials are invalid. + + + Initializes a new instance of the class. + + + Initializes a new instance of the class. + The error message. + + + Initializes a new instance of the class. + The error message. + The inner exception. + + + Represents a flattened map projection of the Earth. This class must be inherited. + + + Initializes a new instance of the class. + The size of the available screen space at the most zoomed out level. + + + Gets or sets the location of the center of the map view. + Returns . + + When overridden in a derived class, limits the values to valid margins. + + Converts a object to a . + Returns . + The rectangle to convert. + + Converts a collection of locations to a collection of points on the map. + Converts a location to a point on the screen. + + Converts a location rectangle to a viewport point. + Returns . + The location rectangle to convert. + + Converts a collection of locations to viewport points. + Converts a location to a viewport point. + + When overridden in a derived class, converts a logical point to a location. + Returns . + The logical point to convert. + + + Converts a point on the screen to a point in the map view. + Returns . + The point on the screen to convert. + + Executes when the map is dragged by the user. + Executes when the map is flicked by the user. + Executes when the map is zoomed by the user. + + Sets the map view to the specified location rectangle. + The bounding rectangle to use to set the map view. + A bool indicating whether to animate map navigation. + + Sets the map view. + + Gets the location of the center of the map view towards which the map is animating. + Returns . + + + Gets the zoom level of the map view towards which the map is animating. + Returns . + + Converts the location to a viewport point. + Converts the location to a viewport point. + + Converts a viewport point to a location. + Returns . + The viewport point to convert. + + + Converts a viewport point to a point on the screen. + Returns . + The viewport point to convert. + + + Executes when the size of the viewport changes. + The new viewport size. + + + Gets or sets the zoom level of the map view. + Returns . + + + Defines the base interface for all objects that use a projection to render themselves on a map. + + + Gets the parent map of this projectable. + Returns . + + Executes when the projection is changed and the conversion methods (ViewportPointToLocation and LocationToViewPortPoint) return different results. + + Represents the abstract base map class. This class must be inherited. + + + Initializes a new instance of the class. + + + When overridden in a derived class, gets or sets the animation level of map navigation. + Returns . + + Identifies the AnimationLevel dependency property. + + When overridden in a derived class, gets the bounding rectangle that defines the boundaries of the map view. + Returns . + + + When overridden in a derived class, gets the center location of the map view. + Returns . + + + Specifies the location of the center of the map. + + + Gets the child elements of the map. + Returns . + + + Gets or sets the visibility of the copyright on the map. + Returns . + + + Specifies whether the copyright is visible. + + + Gets or sets the type of credentials that are being provided. + Returns . + + + Specifies the type of credentials that are being provided. + + + Gets or sets the culture used by the map. + Returns . + + + Specifies the map culture to use. + + + When overridden in a derived class, gets or sets the directional heading of the map. + Returns . + + + Specifies the directional heading, in degrees, of the map. + + + When overridden in a derived class, gets a bool indicating whether map data is downloading. + Returns . + + + Gets whether the map is idle. + + + When overridden in a derived class, occurs when there is an error loading the map. + + Converts a location to a viewport point. + + Gets or sets the visibility of the logo on the map. + Returns . + + + Specifies whether the logo is visible. + + + When overridden in a derived class, occurs when the screen is touched and dragged to pan the map. This event occurs every frame. + + + When overridden in a derived class, occurs when the map tiles are fully downloaded, regardless of whether the user is still interacting with the map. + + + When overridden in a derived class, occurs when the map is touched to zoom the map. + + + When overridden in a derived class, gets or sets the map mode. + Returns . + + + When overridden in a derived class, occurs when the map mode changes. + + Identifies the Mode dependency property. + Executes when the animation level has changed. + + When overridden in a derived class, executes when the location of the center of the map changes. + + + + Creates an automation peer for the map object. + Returns . + + + When overridden in a derived class, raises an event when the CredentialsProvider property changes. + + + + When overridden in a derived class, raises an event when the Culture property changes. + + + + When overridden in a derived class, executes when the directional heading of the map changes. + + + Executes when the map mode has changed. + + When overridden in a derived class, raises an event when the OverlayVisibility property changes. + + + + When overridden in a derived class, executes when the pitch of the map changes. + + + + When overridden in a derived class, executes when the zoom level of the map changes. + + + + When overridden in a derived class, gets or sets the pitch of the map. + Returns . + + + Specifies the pitch of the map. + + When overridden in a derived class, executes when the projection update level changes. + + Gets the base map layer. + Returns . + + + Gets or sets the visibility of the scale bar. + Returns . + + + Specifies whether the scale bar is visible. + + + When overridden in a derived class, sets the map mode. + + + + + When overridden in a derived class, sets the map view to the specified location rectangle. + + + Sets the map view. + Sets the map view. + Sets the map view. + + When overridden in a derived class, gets the bounding rectangle that defines the view towards which the map is animating. + Returns . + + + When overridden in a derived class, gets the center location of the view towards which the map is animating. + Returns . + + + When overridden in a derived class, gets the heading of the view towards which the map is animating. + Returns . + + + When overridden in a derived class, gets the pitch of the view towards which the map is animating. + Returns . + + + When overridden in a derived class, occurs when the view towards which the map is animating changes. + + + When overridden in a derived class, gets the zoom level of the view towards which the map is animating. + Returns . + + Converts a location to a viewport point. + Converts a location to a viewport point. + + When overridden in a derived class, occurs when the view is done changing. + + + When overridden in a derived class, occurs when the map view is changing. + + + When overridden in a derived class, occurs when the map view starts changing. + + + When overridden in a derived class, converts a viewport point to a location. + Returns . + + + + When overridden in a derived class, gets the size of the viewport. + Returns . + + + Gets or sets whether the map zoom level bar displays. + + + Identifies the ZoomBarVisibility dependency property. + + + When overridden in a derived class, gets or sets the zoom level of the map. + Returns . + + + Specifies the zoom level of the map. + + + Contains asynchronous configuration settings. The configuration settings are loaded when an event handler is added to AsynchronousConfigurationLoaded. + + + Loads the configuration. If configuration is already loaded, this method calls the callback function without trying to load the configuration. + The version string of the configuration. + The name of the configuration section to load. + The culture of the configuration. + The callback function to call after the configuration is loaded. + + + Loads the configuration. If configuration is already loaded, this method calls the callback function without trying to load the configuration. + The version string of the configuration. + The name of the configuration section. + The culture of the configuration. + The callback function to call after the configuration is loaded. + An object that stores user defined information. This object is returned in the callback function. + + + Loads the configuration from the configuration service. + The URI that contains the configuration. + + + Loads the configuration using the xml provided. + The configuration xml to use. + + + Occurs when the configuration is loaded. + + + Sets the URI of the configuration service. + The URI of the configuration service. + + + Represents the callback function to call when the map configuration has loaded. + + + Provides data for the event. + + + Initializes a new instance of the class. + The error that occurred when the configuration was loaded. + + + Gets the error that occurred when the configuration was loaded. + Returns . + + + Represents a section of the map configuration. + + + Returns a bool indicating if the configuration setting identified by the specified key is in the map configuration. + Returns . + The key to use. + + + Gets the configuration setting identified by the specified key. + Returns . + The configuration setting key. + + + Represents the map. + + + Initializes a new instance of the class. + + + Arranges the elements to fit the given . + Returns . + + + + Gets the rectangle that defines the boundaries of the map view. + Returns . + + + Gets a bool indicating whether map data is downloading. + Returns . + + + Gets whether the map is idle. + + + Occurs when there is an error loading the map. + + + Gets the exception that occurred when loading the map. + Returns . + + Converts a location to a viewport point. + + Occurs when the screen is touched and dragged to pan the map. This event occurs every frame. + + + Occurs when the map tiles are fully downloaded, regardless of whether the user is still interacting with the map. + + + Occurs when the map is touched to zoom the map. + + + Measures the map elements so that they fit within the map . + Returns . + + + + Occurs when the map mode changes. + + Executes when the animation level has changed. + + Executes when the location of the center of the map changes. + + + + Raises an event when the CredentialsProvider property changes. + + + + Raises an event when the Culture property changes. + + + + When overridden in a derived class, executes during the first frame. + + + Executes when the directional heading of the map changes. + + + Executes when the map mode has changed. + + Executes when the pitch of the map changes. + + + + Executes when the zoom level of the map changes. + + + Executes when the projection is updated. + + Sets the map mode. + + + + + Sets the map view using the specified location rectangle. + + + Sets the map view. + Sets the map view. + Sets the map view. + + Gets the bounding rectangle that defines the boundaries of the map view towards which the map is animating. + Returns . + + + Gets the center location of the map view towards which the map is animating. + Returns . + + + Gets the heading of the map view towards which the map is animating. + Returns . + + + Gets the pitch of the map view towards which the map is animating. + Returns . + + + Occurs when the view towards which the map is animating changes. + + + Gets the zoom level of the map view towards which the map is animating. + Returns . + + Converts a location to a viewpoint point. + Converts a location to a viewpoint point. + + Occurs when the view is done changing. + + + Occurs when the view is done changing. + + + Occurs when the view starts changing. + + + Converts the specified viewport point to a location. + Returns . + + + + Gets the size of the viewport. + Returns . + + + Defines the base class for a map mode. This class must be inherited. + + + Initializes a new instance of the class. + + + When overridden in a derived class, executes when the map mode is activated. + + + + + When overridden in a derived class, executes when the map mode is activating. + + + + + + Gets or sets the animation level. + Returns . + + + When overridden in a derived class, gets a collection of attributions. + Returns . + + + When overridden in a derived class, gets the rectangle that defines the boundaries of the map view. + Returns . + + + When overridden in a derived class, gets or sets the center of the map view. + Returns . + + + When overridden in a derived class, gets the UIElement that represents the map mode. + Returns . + + + When overridden in a derived class, gets or sets the type of credentials provided. + Returns . + + + Gets or sets the culture of the map mode. + Returns . + + + When overridden in a derived class, executes when the map mode is deactivating. + + + When overridden in a derived class, gets the map foreground content as a UIElement. + Returns . + + + When overridden in a derived class, gets or sets the heading of the map view. + Returns . + + + When overridden in a derived class, gets the range of valid heading values. + Returns . + + + Gets or sets whether the map mode is downloading. + Returns . + + + Gets whether the mode is idle. + + + When overridden in a derived class, converts a location rectangle to a viewport point. + Returns . + + + Converts a location collection to viewpoint points. + Converts a location to a viewpoint point. + + When overridden in a derived class, gets the mode of the background. + Returns . + + + When overridden in a derived class, executes when the culture property changes. + + + When overridden in a derived class, executes when the map is dragged by the user. + + + When overridden in a derived class, executes when the map is flicked by the user. + + + When overridden in a derived class, executes when the map is zoomed by the user. + + Executes when the projection has changed. + Executes when the target view has changed. + + When overridden in a derived class, gets or sets the pitch of the map view. + Returns . + + + When overridden in a derived class, gets the range of valid pitch values. + Returns . + + + Occurs when the map projection changes. + + + When overridden in a derived class, gets or sets the scale of the map. + Returns . + + + When overridden in a derived class, sets the map view to the specified location rectangle. + + + + Sets the map view. + + When overridden in a derived class, gets the location rectangle that defines the boundaries of the map view towards which the map is animating. + Returns . + + + When overridden in a derived class, gets the center location of the map view towards which the map is animating. + Returns . + + + When overridden in a derived class, gets the heading of the map view towards which the map is animating. + Returns . + + + When overridden in a derived class, gets the pitch of the map view towards which the map is animating. + Returns . + + + When overridden in a derived class, gets the target scale towards which the map is navigating. + Returns . + + + When overridden in derived class, occurs when the map view towards which the map is animating changes. + + + When overridden in a derived class, gets the zoom level of the map view towards which the map is animating. + Returns . + + Converts a location to a viewpoint point. + Converts a location to a viewpoint point. + + When overridden in a derived class, converts a viewport point to a location in this map mode. + Returns . + + + + Gets or sets the size of the viewport. + Returns . + + + When overridden in a derived class, executes when the size of the viewport changes. + + + + When overridden in a derived class, gets or sets the zoom level. + Returns . + + + When overridden in a derived class, gets the range of valid zoom levels. + Returns . + + + Represents the base class for a shape on the map. This class must be inherited. + + Initializes a new instance of the MapShapeBase class. + + Arranges the shape to fit on the map. + Returns the actual used. + The final size of the shape to fit. + + Gets the shape. + + Gets or sets the fill of the shape. + Returns . + + + Gets or sets the locations that define the vertices of the shape. + Returns . + + + Measures the shape to fit the available size. + Returns the actual needed by the shape and its children. + The available size of the shape. + + + Executes whenever application code or internal processes (such as a rebuilding layout pass) call System.Windows.Controls.Control.ApplyTemplate(). + + + Raises the CreateAutomationPeer event. + Returns . + + + Gets the parent map of the shape. + Returns . + + + When overridden in a derived class, gets or sets the projected points of the shape. Use this property to arrange the points of derived classes. + Returns . + + Executes when the projection is updated. + + Sets the new embedded shape object. + + + Gets or sets the stroke color of the shape. + Returns . + + + Gets or sets the StrokeDashArray value of the shape. + Returns . + + + Gets or sets the StrokeDashCap value for the shape. + Returns . + + + Gets or sets the StrokeDashOffset value for the shape. + Returns . + + + Gets or sets the StrokeEndLineCap value for the shape. + Returns . + + + Gets or sets the StrokeLineJoin value for the shape. + Returns . + + + Gets or sets the StrokeMiterLimit value for the shape. + Returns . + + + Gets or sets the StrokeStartLineCap value for the shape. + Returns . + + + Gets or sets the StrokeThickness value for the shape. + Returns . + + + Contains helper methods for the System.Windows.Media.Matrix class. + + + Rotates the given matrix to the specified angle. + + + Rotates the given matrix to the specified angle around the specified center. + + + Translates the given matrix based on the given X and Y values. + + + Represents a Mercator map mode. + + + Initializes a new instance of the class. + + + Gets the map mode attributions. + Returns . + + + Gets the location rectangle that defines the current map view. + Returns . + + Modifies the given parameters to valid values. + When overridden in a derived class, gets the zoom range of the specified location in this mode. + + Gets the directional heading of the map view. + Returns . + + + Gets the heading range supported for the Mercator map mode. + Returns . + + + Converts a location rectangle to a rectangle in logical space. + Returns . + The location rectangle to convert. + + Converts a location collection to logical points. + Converts a location to a logical point. + + Converts a logical point to a location in the Mercator map mode. + Returns . + The logical point to convert. + + Executes when the projection has changed. + + Gets the pitch of the current map view. + Returns . + + + Gets the valid range of the pitch. + Returns . + + + Gets or sets the scale of the map. + Returns . + + + Gets the rectangle that defines the boundaries of the map view towards which the map is animating. + Returns . + + + Gets the scale of the map towards which the map is animating. + Returns . + + + Gets the range of the valid zoom levels. + Returns . + + + Contains helper methods for Mercator projection calculations. + + + Converts degrees to radians. + Returns . + The degrees to convert. + + + Gets the circumference of the Earth, in meters. + + + Gets the radius of the Earth, in meters. + + Converts a location to a logical point. + + Converts a to a in a Mercator projection. + Returns . + The logical point to convert. + + + Gets the maximum allowed latitude in a Mercator projection. + + + Modifies a given latitude to an equivalent latitude within the valid latitude range (-85.051128 to 85.051128). + + + Modifies the latitude and longitude values of a given location to be within valid latitude (-85.051128 to 85.051128) and longitude (-180 to 180) ranges. + + + Modifies the logical point to be within the range (-0.5 to 0.5) of the center logical point. + + + Modifies a given longitude to an equivalent longitude within the valid longitude range (-180 to 180). + + Calculates the zoom level of the specified location. + Calculates the scale based on the specified location and zoom level. + + Contains background styles that allow the foreground controls to choose appropriate foreground styles. + + + The background is more typically light (white). + + + The background is more typically dark (black). + + + Represents the null map mode. + + + Initializes a new instance of the class. + + Converts a location to a viewport point. + + Sets the map view to the specified location rectangle. + The location rectangle that defines the boundaries of the map view. + A bool indicating whether to animate map navigation. + + Sets the map view. + Converts a location to a viewport point. + Converts a viewport point to a location. + + Converts the specified viewport point to a location. + Returns . + The viewport point to convert. + + + Contains a collection of objects. + + + Initializes a new instance of the class. + + Occurs when a property of one of the collection item has changed. + Provides data for the TileSourcePropertyChanged event. + Initializes a new instance of the TileSourcePropertyChangedEventArgs class. + Gets the name of the changed property. + Gets the name of the tile source. + + Represents the exception that is thrown when the URI scheme used to host the map control is not supported. + + + Initializes a new instance of the class. + + + Initializes a new instance of the class. + The error message. + + + Initializes a new instance of the class. + The error message. + The inner exception that caused the error. + + + Contains methods that parse a string into an . + + + Initializes a new instance of the class. + + + Determines whether the given type can be converted to a . + Returns . + The context of the conversion. + The type of the object to convert. + + + Converts the specified object to an . + Returns . + The context to use in the conversion. + The culture to use in the conversion. + The object to convert. + + + Contains methods that parse a string containing locations into a . + + + Initializes a new instance of the class. + + + Determines whether the given type can be converted to a . + Returns . + The format context provider of the type. + The object type from which to convert. + + + Converts the given object to a . + Returns . + The format context provider of the type. + The culture to use in the conversion. + The object to convert. + + + Contains methods that parse a string into a . + + + Initializes a new instance of the class. + + + Determines whether or not the given type can be converted to a . + Returns . + The format context provider of the type. + The object type from which to convert. + + + Converts a given object to a . + Returns . + The format context provider of the type. + The culture to use in the conversion. + The object to convert. + + + Contains methods that parse a string into a . + + + Initializes a new instance of the class. + + + Determines whether the given type can be converted to a . + Returns . + The format context provider of the type. + The object type from which to convert. + + + Converts the given object to a . + Returns . + The format context provider of the type. + The culture to use in the conversion. + The object to convert. + + + Contains methods that parse a string into a . + + + Initializes a new instance of the class. + + + Determines whether the given type can be converted to a . + Returns . + The format context provider of the type. + The object type from which to convert. + + + Converts the given object to a . + Returns . + The format context provider of the type. + The culture to use in the conversion. + The object to convert. + + + Contains methods that parse a string into a . + + + Initializes a new instance of the class. + + + Determines whether the specified object can be converted to a . + Returns . + The context to use in the conversion. + The type of the object to convert. + + + Converts the specified object to a . + Returns . + The context to use in the conversion. + The culture to use in the conversion. + The object to convert. + + + Contains methods that parse a string into a . + + + Initializes a new instance of the class. + + + Determines whether or not the given type can be converted to a . + Returns . + The format context provider of the type. + The object type from which to convert. + + + Converts a given object to a . + Returns . + The format context provider of the type. + The culture to use in the conversion. + The object to convert. + + + Represents the copyright control on the map. + + + Initializes a new instance of the class. + + + Gets the copyright attributions. + Returns . + + + Executes whenever application code or internal processes (such as a rebuilding layout pass) call System.Windows.Controls.Control.ApplyTemplate(). + + + Creates an automation peer. + Returns . + + + Represents the units of distance used by the scale bar. + + + The scale bar uses the default unit of distance of the current country or region. + + + The scale bar uses miles for long distances and feet for short distances. + + + The scale bar uses miles for long distances and yards for short distances. + + + The scale bar uses kilometers for long distances and meters for shorter distances. + + + Represents a Silverlight control that shows an error message. + + + Initializes a new instance of the class. + + + Executes whenever application code or internal processes (such as a rebuilding layout pass) call System.Windows.Controls.Control.ApplyTemplate(). + + + Creates an automation peer. + Returns . + + Displays a configuration loading error. + Displays an error if credentials are invalid. + Displays an error for a bad URL. + + Represents the logo. + + + Initializes a new instance of the class. + + + Executes whenever application code or internal processes (such as a rebuilding layout pass) call System.Windows.Controls.Control.ApplyTemplate(). + + + Creates an automation peer. + Returns . + + Represents the command contract between the map control and the buttons on it. + Initializes a new instance of the MapCommandBase class. + When overridden in a derived class, executes when a button on the map is invoked. + Provides data for the map button command events. + Initializes a new instance of the MapCommandEventArgs class. + Gets the command associated with the event. + + Represents the control that displays the scale bar, the logo, the copyright, and map navigation bar on the map. + + + Gets the current copyright control. + Returns . + + + Gets or sets the culture of the map foreground controls. + Returns . + + + Specifies the culture to use. + + + Gets the current logo control. + Returns . + + + Executes whenever application code or internal processes (such as a rebuilding layout pass) call System.Windows.Controls.Control.ApplyTemplate(). + + + Creates an automation peer. + Returns . + + Executes when the culture has changed. + + Gets the current scale bar control. + Returns . + + + Gets the zoom bar overlay object on the map. + + + Represents the abstract base class for a control on top of the map. + + + Initializes a new instance of the Overlay class. + + + Raises the TemplateApplied event. + + + Occurs when the template is applied to the overlay after OnApplyTemplate executes. + + + Represents the scale bar control on a map. + + + Initializes a new instance of the class. + + + Gets or sets the culture of the scale bar, which determines the language and default units used by the scale bar. + Returns . + + + Specifies the culture of the scale bar. + + + Gets or sets the distance unit used by the scale bar. + Returns . + + + Specifies the unit of distance used by the scale bar. + + + Gets or sets the meters per pixel to display on the scale bar. + Returns . + + + Executes whenever application code or internal processes (such as a rebuilding layout pass) call System.Windows.Controls.Control.ApplyTemplate(). + + + Creates an automation peer. + Returns . + + + Raises the CultureChanged event. + + + Raises the MetersPerPixelChanged event. + + + Raises the DistanceUnitChanged event. + + + Represents a shadowed text label that can be added to a map. + + + Initializes a new instance of the class. + + + Gets or sets the color of the shadow behind the text. + Returns . + + + Specifies the shadow color to use behind the text. + + + Gets or sets the color of the text. + Returns . + + + Specifies the text color. + + + Executes whenever application code or internal processes (such as a rebuilding layout pass) call System.Windows.Controls.Control.ApplyTemplate(). + + + Creates an automation peer. + Returns . + + + Sets the color of the text and its shadow. + The color of the text. + The color of the shadow behind the text. + + + Sets colors that display best on a dark background. + + + Sets colors that display best on a light background. + + + Gets or sets the text of the shadow text control on the map. + Returns . + + + Specifies the text to use. + + + Represents the zoom control. + + + Initializes a new instance of the ZoomBar class. + + + + Executes whenever application code or internal processes (such as a rebuilding layout pass) call System.Windows.Controls.Control.ApplyTemplate(). + + Executes when the automation peer is created. + + Raises the ZoomMap event. + + + Occurs when the map zooms in or out. + + + Represents the command that adjusts the position of the slider on the ZoomBar. + + + Initializes a new instance of the ZoomMapCommand. + + + Executes a zoom in or zoom out command on the specified map. + + The Microsoft.Phone.Controls.Maps.Platform namespace contains types that allow users to easily cast a Bing Maps SOAP Services Location class to a GeoCoordinate class and a Bing Maps SOAP Services Rectangle class to a LocationRect class. + + Represents a geographical location determined by latitude and longitude coordinates. This class contains the same members as the System.Device.Location.GeoCoordinate class. + + + Initializes a new instance of the Location class. + + + Gets or sets the altitude of the location. + + + Gets or sets the latitude of the location. + + + Gets or sets the longitude of the location. + + + Converts a Location to a GeoCoordinate. + + + Converts a GeoCoordinate to a Location. + + + + Converts the Location to a string. + + + Converts the Location to a string based on the specified format provider. + + + Represents a location rectangle defined by northeast and southwest corners. + + + Initializes a new instance of the Rectangle class. + + + Gets or sets the northeast corner of the rectangle. + + + Converts a LocationRect to a Rectangle. + + + Converts a Rectangle to a LocationRect. + + + Gets or sets the southwest corner of the rectangle. + + + Represents the base shape class. This class is used for serialization. + + + Initializes a new instance of the ShapeBase class. + + + \ No newline at end of file diff --git a/Util/Iron7Libs/BingMaps/nolicense.txt b/Util/Iron7Libs/BingMaps/nolicense.txt new file mode 100644 index 0000000000..5e6b4527c4 --- /dev/null +++ b/Util/Iron7Libs/BingMaps/nolicense.txt @@ -0,0 +1 @@ +No specific license found for this - redistribution as a sample only \ No newline at end of file diff --git a/Util/Iron7Libs/JSON.Net/Silverlight/Newtonsoft.Json.Silverlight.dll b/Util/Iron7Libs/JSON.Net/Silverlight/Newtonsoft.Json.Silverlight.dll new file mode 100644 index 0000000000..5bbb778d75 Binary files /dev/null and b/Util/Iron7Libs/JSON.Net/Silverlight/Newtonsoft.Json.Silverlight.dll differ diff --git a/Util/Iron7Libs/JSON.Net/Silverlight/Newtonsoft.Json.Silverlight.pdb b/Util/Iron7Libs/JSON.Net/Silverlight/Newtonsoft.Json.Silverlight.pdb new file mode 100644 index 0000000000..ba470270a1 Binary files /dev/null and b/Util/Iron7Libs/JSON.Net/Silverlight/Newtonsoft.Json.Silverlight.pdb differ diff --git a/Util/Iron7Libs/JSON.Net/Silverlight/Newtonsoft.Json.Silverlight.xml b/Util/Iron7Libs/JSON.Net/Silverlight/Newtonsoft.Json.Silverlight.xml new file mode 100644 index 0000000000..ecfee1db9b --- /dev/null +++ b/Util/Iron7Libs/JSON.Net/Silverlight/Newtonsoft.Json.Silverlight.xml @@ -0,0 +1,5918 @@ + + + + Newtonsoft.Json.Silverlight + + + + + Represents a BSON Oid (object id). + + + + + Initializes a new instance of the class. + + The Oid value. + + + + Gets or sets the value of the Oid. + + The value of the Oid. + + + + Represents a reader that provides fast, non-cached, forward-only access to serialized Json data. + + + + + Represents a reader that provides fast, non-cached, forward-only access to serialized Json data. + + + + + Initializes a new instance of the class with the specified . + + + + + Reads the next JSON token from the stream. + + true if the next token was read successfully; false if there are no more tokens to read. + + + + Reads the next JSON token from the stream as a . + + A or a null reference if the next JSON token is null. + + + + Skips the children of the current token. + + + + + Sets the current token. + + The new token. + + + + Sets the current token and value. + + The new token. + The value. + + + + Sets the state based on current token type. + + + + + Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + + + + + Releases unmanaged and - optionally - managed resources + + true to release both managed and unmanaged resources; false to release only unmanaged resources. + + + + Changes the to Closed. + + + + + Gets the current reader state. + + The current reader state. + + + + Gets the quotation mark character used to enclose the value of a string. + + + + + Gets the type of the current Json token. + + + + + Gets the text value of the current Json token. + + + + + Gets The Common Language Runtime (CLR) type for the current Json token. + + + + + Gets the depth of the current token in the JSON document. + + The depth of the current token in the JSON document. + + + + Specifies the state of the reader. + + + + + The Read method has not been called. + + + + + The end of the file has been reached successfully. + + + + + Reader is at a property. + + + + + Reader is at the start of an object. + + + + + Reader is in an object. + + + + + Reader is at the start of an array. + + + + + Reader is in an array. + + + + + The Close method has been called. + + + + + Reader has just read a value. + + + + + Reader is at the start of a constructor. + + + + + Reader in a constructor. + + + + + An error occurred that prevents the read operation from continuing. + + + + + The end of the file has been reached successfully. + + + + + Initializes a new instance of the class. + + The stream. + + + + Initializes a new instance of the class. + + The stream. + if set to true the root object will be read as a JSON array. + The used when reading values from BSON. + + + + Reads the next JSON token from the stream as a . + + + A or a null reference if the next JSON token is null. + + + + + Reads the next JSON token from the stream. + + + true if the next token was read successfully; false if there are no more tokens to read. + + + + + Gets or sets a value indicating whether the root object will be read as a JSON array. + + + true if the root object will be read as a JSON array; otherwise, false. + + + + + Gets or sets the used when reading values from BSON. + + The used when reading values from BSON. + + + + Represents a writer that provides a fast, non-cached, forward-only way of generating Json data. + + + + + Represents a writer that provides a fast, non-cached, forward-only way of generating Json data. + + + + + Creates an instance of the JsonWriter class. + + + + + Flushes whatever is in the buffer to the underlying streams and also flushes the underlying stream. + + + + + Closes this stream and the underlying stream. + + + + + Writes the beginning of a Json object. + + + + + Writes the end of a Json object. + + + + + Writes the beginning of a Json array. + + + + + Writes the end of an array. + + + + + Writes the start of a constructor with the given name. + + The name of the constructor. + + + + Writes the end constructor. + + + + + Writes the property name of a name/value pair on a Json object. + + The name of the property. + + + + Writes the end of the current Json object or array. + + + + + Writes the current token. + + The to read the token from. + + + + Writes the specified end token. + + The end token to write. + + + + Writes indent characters. + + + + + Writes the JSON value delimiter. + + + + + Writes an indent space. + + + + + Writes a null value. + + + + + Writes an undefined value. + + + + + Writes raw JSON without changing the writer's state. + + The raw JSON to write. + + + + Writes raw JSON where a value is expected and updates the writer's state. + + The raw JSON to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + An error will raised if the value cannot be written as a single JSON token. + + The value to write. + + + + Writes out a comment /*...*/ containing the specified text. + + Text to place inside the comment. + + + + Writes out the given white space. + + The string of white space characters. + + + + Gets the top. + + The top. + + + + Gets the state of the writer. + + + + + Indicates how the output is formatted. + + + + + Initializes a new instance of the class. + + The stream. + + + + Flushes whatever is in the buffer to the underlying streams and also flushes the underlying stream. + + + + + Writes the end. + + The token. + + + + Writes out a comment /*...*/ containing the specified text. + + Text to place inside the comment. + + + + Writes the start of a constructor with the given name. + + The name of the constructor. + + + + Writes raw JSON. + + The raw JSON to write. + + + + Writes raw JSON where a value is expected and updates the writer's state. + + The raw JSON to write. + + + + Writes the beginning of a Json array. + + + + + Writes the beginning of a Json object. + + + + + Writes the property name of a name/value pair on a Json object. + + The name of the property. + + + + Writes a null value. + + + + + Writes an undefined value. + + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value that represents a BSON object id. + + + + + + Writes a BSON regex. + + The regex pattern. + The regex options. + + + + Gets or sets the used when writing values to BSON. + When set to no conversion will occur. + + The used when writing values to BSON. + + + + Specifies how constructors are used when initializing objects during deserialization by the . + + + + + First attempt to use the public default constructor then fall back to single paramatized constructor. + + + + + Allow Json.NET to use a non-public default constructor. + + + + + Converts a binary value to and from a base 64 string value. + + + + + Converts an object to and from JSON. + + + + + Writes the JSON representation of the object. + + The to write to. + The value. + The calling serializer. + + + + Reads the JSON representation of the object. + + The to read from. + Type of the object. + The existing value of object being read. + The calling serializer. + The object value. + + + + Determines whether this instance can convert the specified object type. + + Type of the object. + + true if this instance can convert the specified object type; otherwise, false. + + + + + Gets the of the JSON produced by the JsonConverter. + + The of the JSON produced by the JsonConverter. + + + + Gets a value indicating whether this can read JSON. + + true if this can read JSON; otherwise, false. + + + + Gets a value indicating whether this can write JSON. + + true if this can write JSON; otherwise, false. + + + + Writes the JSON representation of the object. + + The to write to. + The value. + The calling serializer. + + + + Reads the JSON representation of the object. + + The to read from. + Type of the object. + The existing value of object being read. + The calling serializer. + The object value. + + + + Determines whether this instance can convert the specified object type. + + Type of the object. + + true if this instance can convert the specified object type; otherwise, false. + + + + + Converts a to and from JSON and BSON. + + + + + Writes the JSON representation of the object. + + The to write to. + The value. + The calling serializer. + + + + Reads the JSON representation of the object. + + The to read from. + Type of the object. + The existing value of object being read. + The calling serializer. + The object value. + + + + Determines whether this instance can convert the specified object type. + + Type of the object. + + true if this instance can convert the specified object type; otherwise, false. + + + + + Create a custom object + + + + + + Writes the JSON representation of the object. + + The to write to. + The value. + The calling serializer. + + + + Reads the JSON representation of the object. + + The to read from. + Type of the object. + The existing value of object being read. + The calling serializer. + The object value. + + + + Creates an object which will then be populated by the serializer. + + Type of the object. + + + + + Determines whether this instance can convert the specified object type. + + Type of the object. + + true if this instance can convert the specified object type; otherwise, false. + + + + + Gets a value indicating whether this can write JSON. + + + true if this can write JSON; otherwise, false. + + + + + Provides a base class for converting a to and from JSON. + + + + + Determines whether this instance can convert the specified object type. + + Type of the object. + + true if this instance can convert the specified object type; otherwise, false. + + + + + Converts a to and from the ISO 8601 date format (e.g. 2008-04-12T12:53Z). + + + + + Writes the JSON representation of the object. + + The to write to. + The value. + The calling serializer. + + + + Reads the JSON representation of the object. + + The to read from. + Type of the object. + The existing value of object being read. + The calling serializer. + The object value. + + + + Gets or sets the date time styles used when converting a date to and from JSON. + + The date time styles used when converting a date to and from JSON. + + + + Gets or sets the date time format used when converting a date to and from JSON. + + The date time format used when converting a date to and from JSON. + + + + Gets or sets the culture used when converting a date to and from JSON. + + The culture used when converting a date to and from JSON. + + + + Converts a to and from a JavaScript date constructor (e.g. new Date(52231943)). + + + + + Writes the JSON representation of the object. + + The to write to. + The value. + The calling serializer. + + + + Reads the JSON representation of the object. + + The to read from. + Type of the object. + The existing property value of the JSON that is being converted. + The calling serializer. + The object value. + + + + Specifies whether a DateTime object represents a local time, a Coordinated Universal Time (UTC), or is not specified as either local time or UTC. + + + + + The time represented is local time. + + + + + The time represented is UTC. + + + + + The time represented is not specified as either local time or Coordinated Universal Time (UTC). + + + + + Preserves the DateTimeKind field of a date when a DateTime object is converted to a string and the string is then converted back to a DateTime object. + + + + + Converts a to and from JSON. + + + + + Writes the JSON representation of the object. + + The to write to. + The value. + The calling serializer. + + + + Reads the JSON representation of the object. + + The to read from. + Type of the object. + The existing value of object being read. + The calling serializer. + The object value. + + + + Determines whether this instance can convert the specified object type. + + Type of the object. + + true if this instance can convert the specified object type; otherwise, false. + + + + + Converts a to and from JSON and BSON. + + + + + Writes the JSON representation of the object. + + The to write to. + The value. + The calling serializer. + + + + Reads the JSON representation of the object. + + The to read from. + Type of the object. + The existing value of object being read. + The calling serializer. + The object value. + + + + Determines whether this instance can convert the specified object type. + + Type of the object. + + true if this instance can convert the specified object type; otherwise, false. + + + + + Converts an to and from its name string value. + + + + + Writes the JSON representation of the object. + + The to write to. + The value. + The calling serializer. + + + + Reads the JSON representation of the object. + + The to read from. + Type of the object. + The existing value of object being read. + The calling serializer. + The object value. + + + + Determines whether this instance can convert the specified object type. + + Type of the object. + + true if this instance can convert the specified object type; otherwise, false. + + + + + Indicates the method that will be used during deserialization for locating and loading assemblies. + + + + + In simple mode, the assembly used during deserialization need not match exactly the assembly used during serialization. Specifically, the version numbers need not match as the LoadWithPartialName method is used to load the assembly. + + + + + In full mode, the assembly used during deserialization must match exactly the assembly used during serialization. The Load method of the Assembly class is used to load the assembly. + + + + + Specifies default value handling options for the . + + + + + Include default values when serializing and deserializing objects. + + + + + Ignore default values when serializing and deserializing objects. + + + + + Provides an interface to enable a class to return line and position information. + + + + + Gets a value indicating whether the class can return line information. + + + true if LineNumber and LinePosition can be provided; otherwise, false. + + + + + Gets the current line number. + + The current line number or 0 if no line information is available (for example, HasLineInfo returns false). + + + + Gets the current line position. + + The current line position or 0 if no line information is available (for example, HasLineInfo returns false). + + + + Instructs the how to serialize the collection. + + + + + Instructs the how to serialize the object. + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class with the specified container Id. + + The container Id. + + + + Gets or sets the id. + + The id. + + + + Gets or sets the title. + + The title. + + + + Gets or sets the description. + + The description. + + + + Gets or sets a value that indicates whether to preserve object reference data. + + + true to keep object reference; otherwise, false. The default is false. + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class with a flag indicating whether the array can contain null items + + A flag indicating whether the array can contain null items. + + + + Initializes a new instance of the class with the specified container Id. + + The container Id. + + + + Gets or sets a value indicating whether null items are allowed in the collection. + + true if null items are allowed in the collection; otherwise, false. + + + + Provides methods for converting between common language runtime types and JSON types. + + + + + Represents JavaScript's boolean value true as a string. This field is read-only. + + + + + Represents JavaScript's boolean value false as a string. This field is read-only. + + + + + Represents JavaScript's null as a string. This field is read-only. + + + + + Represents JavaScript's undefined as a string. This field is read-only. + + + + + Represents JavaScript's positive infinity as a string. This field is read-only. + + + + + Represents JavaScript's negative infinity as a string. This field is read-only. + + + + + Represents JavaScript's NaN as a string. This field is read-only. + + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + The string delimiter character. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Serializes the specified object to a JSON string. + + The object to serialize. + A JSON string representation of the object. + + + + Serializes the specified object to a JSON string. + + The object to serialize. + Indicates how the output is formatted. + + A JSON string representation of the object. + + + + + Serializes the specified object to a JSON string using a collection of . + + The object to serialize. + A collection converters used while serializing. + A JSON string representation of the object. + + + + Serializes the specified object to a JSON string using a collection of . + + The object to serialize. + Indicates how the output is formatted. + A collection converters used while serializing. + A JSON string representation of the object. + + + + Serializes the specified object to a JSON string using a collection of . + + The object to serialize. + Indicates how the output is formatted. + The used to serialize the object. + If this is null, default serialization settings will be is used. + + A JSON string representation of the object. + + + + + Deserializes the specified object to a Json object. + + The object to deserialize. + The deserialized object from the Json string. + + + + Deserializes the specified object to a Json object. + + The object to deserialize. + The of object being deserialized. + The deserialized object from the Json string. + + + + Deserializes the specified object to a Json object. + + The type of the object to deserialize. + The object to deserialize. + The deserialized object from the Json string. + + + + Deserializes the specified JSON to the given anonymous type. + + + The anonymous type to deserialize to. This can't be specified + traditionally and must be infered from the anonymous type passed + as a parameter. + + The object to deserialize. + The anonymous type object. + The deserialized anonymous type from the JSON string. + + + + Deserializes the JSON string to the specified type. + + The type of the object to deserialize. + The object to deserialize. + Converters to use while deserializing. + The deserialized object from the JSON string. + + + + Deserializes the JSON string to the specified type. + + The type of the object to deserialize. + The object to deserialize. + + The used to deserialize the object. + If this is null, default serialization settings will be is used. + + The deserialized object from the JSON string. + + + + Deserializes the JSON string to the specified type. + + The object to deserialize. + The type of the object to deserialize. + Converters to use while deserializing. + The deserialized object from the JSON string. + + + + Deserializes the JSON string to the specified type. + + The JSON to deserialize. + The type of the object to deserialize. + + The used to deserialize the object. + If this is null, default serialization settings will be is used. + + The deserialized object from the JSON string. + + + + Populates the object with values from the JSON string. + + The JSON to populate values from. + The target object to populate values onto. + + + + Populates the object with values from the JSON string. + + The JSON to populate values from. + The target object to populate values onto. + + The used to deserialize the object. + If this is null, default serialization settings will be is used. + + + + + Instructs the to use the specified when serializing the member or class. + + + + + Initializes a new instance of the class. + + Type of the converter. + + + + Gets the type of the converter. + + The type of the converter. + + + + Represents a collection of . + + + + + Instructs the not to serialize the public field or public read/write property value. + + + + + Instructs the how to serialize the object. + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class with the specified member serialization. + + The member serialization. + + + + Initializes a new instance of the class with the specified container Id. + + The container Id. + + + + Gets or sets the member serialization. + + The member serialization. + + + + Instructs the to always serialize the member with the specified name. + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class with the specified name. + + Name of the property. + + + + Gets or sets the null value handling used when serializing this property. + + The null value handling. + + + + Gets or sets the default value handling used when serializing this property. + + The default value handling. + + + + Gets or sets the reference loop handling used when serializing this property. + + The reference loop handling. + + + + Gets or sets the object creation handling used when deserializing this property. + + The object creation handling. + + + + Gets or sets the type name handling used when serializing this property. + + The type name handling. + + + + Gets or sets whether this property's value is serialized as a reference. + + Whether this property's value is serialized as a reference. + + + + Gets or sets the name of the property. + + The name of the property. + + + + Gets or sets a value indicating whether this property is required. + + + A value indicating whether this property is required. + + + + + The exception thrown when an error occurs while reading Json text. + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class + with a specified error message. + + The error message that explains the reason for the exception. + + + + Initializes a new instance of the class + with a specified error message and a reference to the inner exception that is the cause of this exception. + + The error message that explains the reason for the exception. + The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified. + + + + Gets the line number indicating where the error occurred. + + The line number indicating where the error occurred. + + + + Gets the line position indicating where the error occurred. + + The line position indicating where the error occurred. + + + + The exception thrown when an error occurs during Json serialization or deserialization. + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class + with a specified error message. + + The error message that explains the reason for the exception. + + + + Initializes a new instance of the class + with a specified error message and a reference to the inner exception that is the cause of this exception. + + The error message that explains the reason for the exception. + The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified. + + + + Serializes and deserializes objects into and from the JSON format. + The enables you to control how objects are encoded into JSON. + + + + + Initializes a new instance of the class. + + + + + Creates a new instance using the specified . + + The settings to be applied to the . + A new instance using the specified . + + + + Populates the JSON values onto the target object. + + The that contains the JSON structure to reader values from. + The target object to populate values onto. + + + + Populates the JSON values onto the target object. + + The that contains the JSON structure to reader values from. + The target object to populate values onto. + + + + Deserializes the Json structure contained by the specified . + + The that contains the JSON structure to deserialize. + The being deserialized. + + + + Deserializes the Json structure contained by the specified + into an instance of the specified type. + + The containing the object. + The of object being deserialized. + The instance of being deserialized. + + + + Deserializes the Json structure contained by the specified + into an instance of the specified type. + + The containing the object. + The type of the object to deserialize. + The instance of being deserialized. + + + + Deserializes the Json structure contained by the specified + into an instance of the specified type. + + The containing the object. + The of object being deserialized. + The instance of being deserialized. + + + + Serializes the specified and writes the Json structure + to a Stream using the specified . + + The used to write the Json structure. + The to serialize. + + + + Serializes the specified and writes the Json structure + to a Stream using the specified . + + The used to write the Json structure. + The to serialize. + + + + Occurs when the errors during serialization and deserialization. + + + + + Gets or sets the used by the serializer when resolving references. + + + + + Gets or sets the used by the serializer when resolving type names. + + + + + Gets or sets how type name writing and reading is handled by the serializer. + + + + + Gets or sets how a type name assembly is written and resolved by the serializer. + + The type name assembly format. + + + + Gets or sets how object references are preserved by the serializer. + + + + + Get or set how reference loops (e.g. a class referencing itself) is handled. + + + + + Get or set how missing members (e.g. JSON contains a property that isn't a member on the object) are handled during deserialization. + + + + + Get or set how null values are handled during serialization and deserialization. + + + + + Get or set how null default are handled during serialization and deserialization. + + + + + Gets or sets how objects are created during deserialization. + + The object creation handling. + + + + Gets or sets how constructors are used during deserialization. + + The constructor handling. + + + + Gets a collection that will be used during serialization. + + Collection that will be used during serialization. + + + + Gets or sets the contract resolver used by the serializer when + serializing .NET objects to JSON and vice versa. + + + + + Gets or sets the used by the serializer when invoking serialization callback methods. + + The context. + + + + Specifies the settings on a object. + + + + + Initializes a new instance of the class. + + + + + Gets or sets how reference loops (e.g. a class referencing itself) is handled. + + Reference loop handling. + + + + Gets or sets how missing members (e.g. JSON contains a property that isn't a member on the object) are handled during deserialization. + + Missing member handling. + + + + Gets or sets how objects are created during deserialization. + + The object creation handling. + + + + Gets or sets how null values are handled during serialization and deserialization. + + Null value handling. + + + + Gets or sets how null default are handled during serialization and deserialization. + + The default value handling. + + + + Gets or sets a collection that will be used during serialization. + + The converters. + + + + Gets or sets how object references are preserved by the serializer. + + The preserve references handling. + + + + Gets or sets how type name writing and reading is handled by the serializer. + + The type name handling. + + + + Gets or sets how a type name assembly is written and resolved by the serializer. + + The type name assembly format. + + + + Gets or sets how constructors are used during deserialization. + + The constructor handling. + + + + Gets or sets the contract resolver used by the serializer when + serializing .NET objects to JSON and vice versa. + + The contract resolver. + + + + Gets or sets the used by the serializer when resolving references. + + The reference resolver. + + + + Gets or sets the used by the serializer when resolving type names. + + The binder. + + + + Gets or sets the error handler called during serialization and deserialization. + + The error handler called during serialization and deserialization. + + + + Gets or sets the used by the serializer when invoking serialization callback methods. + + The context. + + + + Represents a reader that provides fast, non-cached, forward-only access to serialized Json data. + + + + + Initializes a new instance of the class with the specified . + + The TextReader containing the XML data to read. + + + + Reads the next JSON token from the stream. + + + true if the next token was read successfully; false if there are no more tokens to read. + + + + + Reads the next JSON token from the stream as a . + + + A or a null reference if the next JSON token is null. + + + + + Changes the state to closed. + + + + + Gets a value indicating whether the class can return line information. + + + true if LineNumber and LinePosition can be provided; otherwise, false. + + + + + Gets the current line number. + + + The current line number or 0 if no line information is available (for example, HasLineInfo returns false). + + + + + Gets the current line position. + + + The current line position or 0 if no line information is available (for example, HasLineInfo returns false). + + + + + Represents a writer that provides a fast, non-cached, forward-only way of generating Json data. + + + + + Creates an instance of the JsonWriter class using the specified . + + The TextWriter to write to. + + + + Flushes whatever is in the buffer to the underlying streams and also flushes the underlying stream. + + + + + Closes this stream and the underlying stream. + + + + + Writes the beginning of a Json object. + + + + + Writes the beginning of a Json array. + + + + + Writes the start of a constructor with the given name. + + The name of the constructor. + + + + Writes the specified end token. + + The end token to write. + + + + Writes the property name of a name/value pair on a Json object. + + The name of the property. + + + + Writes indent characters. + + + + + Writes the JSON value delimiter. + + + + + Writes an indent space. + + + + + Writes a null value. + + + + + Writes an undefined value. + + + + + Writes raw JSON. + + The raw JSON to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes out a comment /*...*/ containing the specified text. + + Text to place inside the comment. + + + + Writes out the given white space. + + The string of white space characters. + + + + Gets or sets how many IndentChars to write for each level in the hierarchy when is set to Formatting.Indented. + + + + + Gets or sets which character to use to quote attribute values. + + + + + Gets or sets which character to use for indenting when is set to Formatting.Indented. + + + + + Gets or sets a value indicating whether object names will be surrounded with quotes. + + + + + Specifies the type of Json token. + + + + + This is returned by the if a method has not been called. + + + + + An object start token. + + + + + An array start token. + + + + + A constructor start token. + + + + + An object property name. + + + + + A comment. + + + + + Raw JSON. + + + + + An interger. + + + + + A float. + + + + + A string. + + + + + A boolean. + + + + + A null token. + + + + + An undefined token. + + + + + An object end token. + + + + + An array end token. + + + + + A constructor end token. + + + + + A Date. + + + + + Byte data. + + + + + Represents a reader that provides validation. + + + + + Initializes a new instance of the class that + validates the content returned from the given . + + The to read from while validating. + + + + Reads the next JSON token from the stream as a . + + + A or a null reference if the next JSON token is null. + + + + + Reads the next JSON token from the stream. + + + true if the next token was read successfully; false if there are no more tokens to read. + + + + + Sets an event handler for receiving schema validation errors. + + + + + Gets the text value of the current Json token. + + + + + + Gets the depth of the current token in the JSON document. + + The depth of the current token in the JSON document. + + + + Gets the quotation mark character used to enclose the value of a string. + + + + + + Gets the type of the current Json token. + + + + + + Gets The Common Language Runtime (CLR) type for the current Json token. + + + + + + Gets or sets the schema. + + The schema. + + + + Gets the used to construct this . + + The specified in the constructor. + + + + Specifies the state of the . + + + + + An exception has been thrown, which has left the in an invalid state. + You may call the method to put the in the Closed state. + Any other method calls results in an being thrown. + + + + + The method has been called. + + + + + An object is being written. + + + + + A array is being written. + + + + + A constructor is being written. + + + + + A property is being written. + + + + + A write method has not been called. + + + + + Specifies formatting options for the . + + + + + No special formatting is applied. This is the default. + + + + + Causes child objects to be indented according to the and settings. + + + + + The exception thrown when an error occurs while reading Json text. + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class + with a specified error message. + + The error message that explains the reason for the exception. + + + + Initializes a new instance of the class + with a specified error message and a reference to the inner exception that is the cause of this exception. + + The error message that explains the reason for the exception. + The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified. + + + + Contains the LINQ to JSON extension methods. + + + + + Returns a collection of tokens that contains the ancestors of every token in the source collection. + + The type of the objects in source, constrained to . + An of that contains the source collection. + An of that contains the ancestors of every node in the source collection. + + + + Returns a collection of tokens that contains the descendants of every token in the source collection. + + The type of the objects in source, constrained to . + An of that contains the source collection. + An of that contains the descendants of every node in the source collection. + + + + Returns a collection of child properties of every object in the source collection. + + An of that contains the source collection. + An of that contains the properties of every object in the source collection. + + + + Returns a collection of child values of every object in the source collection with the given key. + + An of that contains the source collection. + The token key. + An of that contains the values of every node in the source collection with the given key. + + + + Returns a collection of child values of every object in the source collection. + + An of that contains the source collection. + An of that contains the values of every node in the source collection. + + + + Returns a collection of converted child values of every object in the source collection with the given key. + + The type to convert the values to. + An of that contains the source collection. + The token key. + An that contains the converted values of every node in the source collection with the given key. + + + + Returns a collection of converted child values of every object in the source collection. + + The type to convert the values to. + An of that contains the source collection. + An that contains the converted values of every node in the source collection. + + + + Converts the value. + + The type to convert the value to. + A cast as a of . + A converted value. + + + + Converts the value. + + The source collection type. + The type to convert the value to. + A cast as a of . + A converted value. + + + + Returns a collection of child tokens of every array in the source collection. + + The source collection type. + An of that contains the source collection. + An of that contains the values of every node in the source collection. + + + + Returns a collection of converted child tokens of every array in the source collection. + + An of that contains the source collection. + The type to convert the values to. + The source collection type. + An that contains the converted values of every node in the source collection. + + + + Returns the input typed as . + + An of that contains the source collection. + The input typed as . + + + + Returns the input typed as . + + The source collection type. + An of that contains the source collection. + The input typed as . + + + + Represents a collection of objects. + + The type of token + + + + Gets the with the specified key. + + + + + + Represents a JSON array. + + + + + Represents a token that can contain other tokens. + + + + + Represents an abstract JSON token. + + + + + Compares the values of two tokens, including the values of all descendant tokens. + + The first to compare. + The second to compare. + true if the tokens are equal; otherwise false. + + + + Adds the specified content immediately after this token. + + A content object that contains simple content or a collection of content objects to be added after this token. + + + + Adds the specified content immediately before this token. + + A content object that contains simple content or a collection of content objects to be added before this token. + + + + Returns a collection of the ancestor tokens of this token. + + A collection of the ancestor tokens of this token. + + + + Returns a collection of the sibling tokens after this token, in document order. + + A collection of the sibling tokens after this tokens, in document order. + + + + Returns a collection of the sibling tokens before this token, in document order. + + A collection of the sibling tokens before this token, in document order. + + + + Gets the with the specified key converted to the specified type. + + The type to convert the token to. + The token key. + The converted token value. + + + + Returns a collection of the child tokens of this token, in document order. + + An of containing the child tokens of this , in document order. + + + + Returns a collection of the child tokens of this token, in document order, filtered by the specified type. + + The type to filter the child tokens on. + A containing the child tokens of this , in document order. + + + + Returns a collection of the child values of this token, in document order. + + The type to convert the values to. + A containing the child values of this , in document order. + + + + Removes this token from its parent. + + + + + Replaces this token with the specified token. + + The value. + + + + Writes this token to a . + + A into which this method will write. + A collection of which will be used when writing the token. + + + + Returns the indented JSON for this token. + + + The indented JSON for this token. + + + + + Returns the JSON for this token using the given formatting and converters. + + Indicates how the output is formatted. + A collection of which will be used when writing the token. + The JSON for this token using the given formatting and converters. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Creates an for this token. + + An that can be used to read this token and its descendants. + + + + Creates a from an object. + + The object that will be used to create . + A with the value of the specified object + + + + Creates a from an object using the specified . + + The object that will be used to create . + The that will be used when reading the object. + A with the value of the specified object + + + + Creates a from a . + + An positioned at the token to read into this . + + An that contains the token and its descendant tokens + that were read from the reader. The runtime type of the token is determined + by the token type of the first token encountered in the reader. + + + + + Selects the token that matches the object path. + + + The object path from the current to the + to be returned. This must be a string of property names or array indexes separated + by periods, such as Tables[0].DefaultView[0].Price in C# or + Tables(0).DefaultView(0).Price in Visual Basic. + + The that matches the object path or a null reference if no matching token is found. + + + + Selects the token that matches the object path. + + + The object path from the current to the + to be returned. This must be a string of property names or array indexes separated + by periods, such as Tables[0].DefaultView[0].Price in C# or + Tables(0).DefaultView(0).Price in Visual Basic. + + A flag to indicate whether an error should be thrown if no token is found. + The that matches the object path. + + + + Gets a comparer that can compare two tokens for value equality. + + A that can compare two nodes for value equality. + + + + Gets or sets the parent. + + The parent. + + + + Gets the root of this . + + The root of this . + + + + Gets the node type for this . + + The type. + + + + Gets a value indicating whether this token has childen tokens. + + + true if this token has child values; otherwise, false. + + + + + Gets the next sibling token of this node. + + The that contains the next sibling token. + + + + Gets the previous sibling token of this node. + + The that contains the previous sibling token. + + + + Gets the with the specified key. + + The with the specified key. + + + + Get the first child token of this token. + + A containing the first child token of the . + + + + Get the last child token of this token. + + A containing the last child token of the . + + + + Raises the event. + + The instance containing the event data. + + + + Returns a collection of the child tokens of this token, in document order. + + + An of containing the child tokens of this , in document order. + + + + + Returns a collection of the child values of this token, in document order. + + The type to convert the values to. + + A containing the child values of this , in document order. + + + + + Returns a collection of the descendant tokens for this token in document order. + + An containing the descendant tokens of the . + + + + Adds the specified content as children of this . + + The content to be added. + + + + Adds the specified content as the first children of this . + + The content to be added. + + + + Creates an that can be used to add tokens to the . + + An that is ready to have content written to it. + + + + Replaces the children nodes of this token with the specified content. + + The content. + + + + Removes the child nodes from this token. + + + + + Occurs when the items list of the collection has changed, or the collection is reset. + + + + + Gets a value indicating whether this token has childen tokens. + + + true if this token has child values; otherwise, false. + + + + + Get the first child token of this token. + + + A containing the first child token of the . + + + + + Get the last child token of this token. + + + A containing the last child token of the . + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class from another object. + + A object to copy from. + + + + Initializes a new instance of the class with the specified content. + + The contents of the array. + + + + Initializes a new instance of the class with the specified content. + + The contents of the array. + + + + Loads an from a . + + A that will be read for the content of the . + A that contains the JSON that was read from the specified . + + + + Load a from a string that contains JSON. + + A that contains JSON. + A populated from the string that contains JSON. + + + + Creates a from an object. + + The object that will be used to create . + A with the values of the specified object + + + + Creates a from an object. + + The object that will be used to create . + The that will be used to read the object. + A with the values of the specified object + + + + Writes this token to a . + + A into which this method will write. + A collection of which will be used when writing the token. + + + + Determines the index of a specific item in the . + + The object to locate in the . + + The index of if found in the list; otherwise, -1. + + + + + Inserts an item to the at the specified index. + + The zero-based index at which should be inserted. + The object to insert into the . + + is not a valid index in the . + The is read-only. + + + + Removes the item at the specified index. + + The zero-based index of the item to remove. + + is not a valid index in the . + The is read-only. + + + + Adds an item to the . + + The object to add to the . + The is read-only. + + + + Removes all items from the . + + The is read-only. + + + + Determines whether the contains a specific value. + + The object to locate in the . + + true if is found in the ; otherwise, false. + + + + + Removes the first occurrence of a specific object from the . + + The object to remove from the . + + true if was successfully removed from the ; otherwise, false. This method also returns false if is not found in the original . + + The is read-only. + + + + Gets the node type for this . + + The type. + + + + Gets the with the specified key. + + The with the specified key. + + + + Gets or sets the at the specified index. + + + + + + Gets the number of elements contained in the . + + + The number of elements contained in the . + + + + Represents a JSON constructor. + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class from another object. + + A object to copy from. + + + + Initializes a new instance of the class with the specified name and content. + + The constructor name. + The contents of the constructor. + + + + Initializes a new instance of the class with the specified name and content. + + The constructor name. + The contents of the constructor. + + + + Initializes a new instance of the class with the specified name. + + The constructor name. + + + + Writes this token to a . + + A into which this method will write. + A collection of which will be used when writing the token. + + + + Loads an from a . + + A that will be read for the content of the . + A that contains the JSON that was read from the specified . + + + + Gets or sets the name of this constructor. + + The constructor name. + + + + Gets the node type for this . + + The type. + + + + Gets the with the specified key. + + The with the specified key. + + + + Represents a collection of objects. + + The type of token + + + + An empty collection of objects. + + + + + Initializes a new instance of the struct. + + The enumerable. + + + + Returns an enumerator that iterates through the collection. + + + A that can be used to iterate through the collection. + + + + + Returns an enumerator that iterates through a collection. + + + An object that can be used to iterate through the collection. + + + + + Determines whether the specified is equal to this instance. + + The to compare with this instance. + + true if the specified is equal to this instance; otherwise, false. + + + + + Returns a hash code for this instance. + + + A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + + + + + Gets the with the specified key. + + + + + + Represents a JSON object. + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class from another object. + + A object to copy from. + + + + Initializes a new instance of the class with the specified content. + + The contents of the object. + + + + Initializes a new instance of the class with the specified content. + + The contents of the object. + + + + Gets an of this object's properties. + + An of this object's properties. + + + + Gets a the specified name. + + The property name. + A with the specified name or null. + + + + Gets an of this object's property values. + + An of this object's property values. + + + + Loads an from a . + + A that will be read for the content of the . + A that contains the JSON that was read from the specified . + + + + Load a from a string that contains JSON. + + A that contains JSON. + A populated from the string that contains JSON. + + + + Creates a from an object. + + The object that will be used to create . + A with the values of the specified object + + + + Creates a from an object. + + The object that will be used to create . + The that will be used to read the object. + A with the values of the specified object + + + + Writes this token to a . + + A into which this method will write. + A collection of which will be used when writing the token. + + + + Adds the specified property name. + + Name of the property. + The value. + + + + Removes the property with the specified name. + + Name of the property. + true if item was successfully removed; otherwise, false. + + + + Tries the get value. + + Name of the property. + The value. + true if a value was successfully retrieved; otherwise, false. + + + + Returns an enumerator that iterates through the collection. + + + A that can be used to iterate through the collection. + + + + + Raises the event with the provided arguments. + + Name of the property. + + + + Occurs when a property value changes. + + + + + Gets the node type for this . + + The type. + + + + Gets the with the specified key. + + The with the specified key. + + + + Gets or sets the with the specified property name. + + + + + + Gets the number of elements contained in the . + + + The number of elements contained in the . + + + + Represents a JSON property. + + + + + Initializes a new instance of the class from another object. + + A object to copy from. + + + + Returns a collection of the child tokens of this token, in document order. + + + An of containing the child tokens of this , in document order. + + + + + Initializes a new instance of the class. + + The property name. + The property content. + + + + Initializes a new instance of the class. + + The property name. + The property content. + + + + Writes this token to a . + + A into which this method will write. + A collection of which will be used when writing the token. + + + + Loads an from a . + + A that will be read for the content of the . + A that contains the JSON that was read from the specified . + + + + Gets the property name. + + The property name. + + + + Gets or sets the property value. + + The property value. + + + + Gets the node type for this . + + The type. + + + + Represents a raw JSON string. + + + + + Represents a value in JSON (string, integer, date, etc). + + + + + Initializes a new instance of the class from another object. + + A object to copy from. + + + + Initializes a new instance of the class with the given value. + + The value. + + + + Initializes a new instance of the class with the given value. + + The value. + + + + Initializes a new instance of the class with the given value. + + The value. + + + + Initializes a new instance of the class with the given value. + + The value. + + + + Initializes a new instance of the class with the given value. + + The value. + + + + Initializes a new instance of the class with the given value. + + The value. + + + + Initializes a new instance of the class with the given value. + + The value. + + + + Creates a comment with the given value. + + The value. + A comment with the given value. + + + + Creates a string with the given value. + + The value. + A string with the given value. + + + + Writes this token to a . + + A into which this method will write. + A collection of which will be used when writing the token. + + + + Indicates whether the current object is equal to another object of the same type. + + + true if the current object is equal to the parameter; otherwise, false. + + An object to compare with this object. + + + + Determines whether the specified is equal to the current . + + The to compare with the current . + + true if the specified is equal to the current ; otherwise, false. + + + The parameter is null. + + + + + Serves as a hash function for a particular type. + + + A hash code for the current . + + + + + Gets a value indicating whether this token has childen tokens. + + + true if this token has child values; otherwise, false. + + + + + Gets the node type for this . + + The type. + + + + Gets or sets the underlying token value. + + The underlying token value. + + + + Initializes a new instance of the class from another object. + + A object to copy from. + + + + Initializes a new instance of the class. + + The raw json. + + + + Creates an instance of with the content of the reader's current token. + + The reader. + An instance of with the content of the reader's current token. + + + + Compares tokens to determine whether they are equal. + + + + + Determines whether the specified objects are equal. + + The first object of type to compare. + The second object of type to compare. + + true if the specified objects are equal; otherwise, false. + + + + + Returns a hash code for the specified object. + + The for which a hash code is to be returned. + A hash code for the specified object. + The type of is a reference type and is null. + + + + Represents a reader that provides fast, non-cached, forward-only access to serialized Json data. + + + + + Initializes a new instance of the class. + + The token to read from. + + + + Reads the next JSON token from the stream as a . + + + A or a null reference if the next JSON token is null. + + + + + Reads the next JSON token from the stream. + + + true if the next token was read successfully; false if there are no more tokens to read. + + + + + Specifies the type of token. + + + + + No token type has been set. + + + + + A JSON object. + + + + + A JSON array. + + + + + A JSON constructor. + + + + + A JSON object property. + + + + + A comment. + + + + + An integer value. + + + + + A float value. + + + + + A string value. + + + + + A boolean value. + + + + + A null value. + + + + + An undefined value. + + + + + A date value. + + + + + A raw JSON value. + + + + + A collection of bytes value. + + + + + Represents a writer that provides a fast, non-cached, forward-only way of generating Json data. + + + + + Initializes a new instance of the class writing to the given . + + The container being written to. + + + + Initializes a new instance of the class. + + + + + Flushes whatever is in the buffer to the underlying streams and also flushes the underlying stream. + + + + + Closes this stream and the underlying stream. + + + + + Writes the beginning of a Json object. + + + + + Writes the beginning of a Json array. + + + + + Writes the start of a constructor with the given name. + + The name of the constructor. + + + + Writes the end. + + The token. + + + + Writes the property name of a name/value pair on a Json object. + + The name of the property. + + + + Writes a null value. + + + + + Writes an undefined value. + + + + + Writes raw JSON. + + The raw JSON to write. + + + + Writes out a comment /*...*/ containing the specified text. + + Text to place inside the comment. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Gets the token being writen. + + The token being writen. + + + + Specifies the member serialization options for the . + + + + + All members are serialized by default. Members can be excluded using the . + + + + + Only members must be marked with the are serialized. + + + + + Specifies missing member handling options for the . + + + + + Ignore a missing member and do not attempt to deserialize it. + + + + + Throw a when a missing member is encountered during deserialization. + + + + + Specifies null value handling options for the . + + + + + Include null values when serializing and deserializing objects. + + + + + Ignore null values when serializing and deserializing objects. + + + + + Specifies how object creation is handled by the . + + + + + Reuse existing objects, create new objects when needed. + + + + + Only reuse existing objects. + + + + + Always create new objects. + + + + + Specifies reference handling options for the . + + + + + Do not preserve references when serializing types. + + + + + Preserve references when serializing into a JSON object structure. + + + + + Preserve references when serializing into a JSON array structure. + + + + + Preserve references when serializing. + + + + + Specifies reference loop handling options for the . + + + + + Throw a when a loop is encountered. + + + + + Ignore loop references and do not serialize. + + + + + Serialize loop references. + + + + + Indicating whether a property is required. + + + + + The property is not required. The default state. + + + + + The property must be defined in JSON but can be a null value. + + + + + The property must be defined in JSON and cannot be a null value. + + + + + Allows users to control class loading and mandate what class to load. + + + + + When overridden in a derived class, controls the binding of a serialized object to a type. + + Specifies the name of the serialized object. + Specifies the name of the serialized object + + + + + Resolves member mappings for a type, camel casing property names. + + + + + Used by to resolves a for a given . + + + + + Used by to resolves a for a given . + + + + + Resolves the contract for a given type. + + The type to resolve a contract for. + The contract for a given type. + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + + If set to true the will use a cached shared with other resolvers of the same type. + Sharing the cache will significantly performance because expensive reflection will only happen once but could cause unexpected + behavior if different instances of the resolver are suppose to produce different results. When set to false it is highly + recommended to reuse instances with the . + + + + + Resolves the contract for a given type. + + The type to resolve a contract for. + The contract for a given type. + + + + Gets the serializable members for the type. + + The type to get serializable members for. + The serializable members for the type. + + + + Creates a for the given type. + + Type of the object. + A for the given type. + + + + Resolves the default for the contract. + + Type of the object. + + + + + Creates a for the given type. + + Type of the object. + A for the given type. + + + + Creates a for the given type. + + Type of the object. + A for the given type. + + + + Creates a for the given type. + + Type of the object. + A for the given type. + + + + Creates a for the given type. + + Type of the object. + A for the given type. + + + + Creates a for the given type. + + Type of the object. + A for the given type. + + + + Determines which contract type is created for the given type. + + Type of the object. + A for the given type. + + + + Creates properties for the given . + + The contract to create properties for. + Properties for the given . + + + + Creates the used by the serializer to get and set values from a member. + + The member. + The used by the serializer to get and set values from a member. + + + + Creates a for the given . + + The member's declaring types . + The member to create a for. + A created for the given . + + + + Resolves the name of the property. + + Name of the property. + Name of the property. + + + + Gets a value indicating whether members are being get and set using dynamic code generation. + This value is determined by the runtime permissions available. + + + true if using dynamic code generation; otherwise, false. + + + + + Gets or sets the default members search flags. + + The default members search flags. + + + + Gets or sets a value indicating whether compiler generated members should be serialized. + + + true if serialized compiler generated members; otherwise, false. + + + + + Initializes a new instance of the class. + + + + + Resolves the name of the property. + + Name of the property. + The property name camel cased. + + + + Used to resolve references when serializing and deserializing JSON by the . + + + + + Resolves a reference to its object. + + The reference to resolve. + The object that + + + + Gets the reference for the sepecified object. + + The object to get a reference for. + The reference to the object. + + + + Determines whether the specified object is referenced. + + The object to test for a reference. + + true if the specified object is referenced; otherwise, false. + + + + + Adds a reference to the specified object. + + The reference. + The object to reference. + + + + The default serialization binder used when resolving and loading classes from type names. + + + + + When overridden in a derived class, controls the binding of a serialized object to a type. + + Specifies the name of the serialized object. + Specifies the name of the serialized object. + + The type of the object the formatter creates a new instance of. + + + + + Provides information surrounding an error. + + + + + Gets or sets the error. + + The error. + + + + Gets the original object that caused the error. + + The original object that caused the error. + + + + Gets the member that caused the error. + + The member that caused the error. + + + + Gets or sets a value indicating whether this is handled. + + true if handled; otherwise, false. + + + + Provides data for the Error event. + + + + + Initializes a new instance of the class. + + The current object. + The error context. + + + + Gets the current object the error event is being raised against. + + The current object the error event is being raised against. + + + + Gets the error context. + + The error context. + + + + Provides methods to get and set values. + + + + + Sets the value. + + The target to set the value on. + The value to set on the target. + + + + Gets the value. + + The target to get the value from. + The value. + + + + Contract details for a used by the . + + + + + Contract details for a used by the . + + + + + Gets the underlying type for the contract. + + The underlying type for the contract. + + + + Gets or sets the type created during deserialization. + + The type created during deserialization. + + + + Gets or sets whether this type contract is serialized as a reference. + + Whether this type contract is serialized as a reference. + + + + Gets or sets the default for this contract. + + The converter. + + + + Gets or sets the method called immediately after deserialization of the object. + + The method called immediately after deserialization of the object. + + + + Gets or sets the method called during deserialization of the object. + + The method called during deserialization of the object. + + + + Gets or sets the method called after serialization of the object graph. + + The method called after serialization of the object graph. + + + + Gets or sets the method called before serialization of the object. + + The method called before serialization of the object. + + + + Gets or sets the default creator method used to create the object. + + The default creator method used to create the object. + + + + Gets or sets a value indicating whether [default creator non public]. + + true if the default object creator is non-public; otherwise, false. + + + + Gets or sets the method called when an error is thrown during the serialization of the object. + + The method called when an error is thrown during the serialization of the object. + + + + Initializes a new instance of the class. + + The underlying type for the contract. + + + + Contract details for a used by the . + + + + + Initializes a new instance of the class. + + The underlying type for the contract. + + + + Contract details for a used by the . + + + + + Initializes a new instance of the class. + + The underlying type for the contract. + + + + Contract details for a used by the . + + + + + Initializes a new instance of the class. + + The underlying type for the contract. + + + + Gets or sets the object member serialization. + + The member object serialization. + + + + Gets the object's properties. + + The object's properties. + + + + Gets or sets the parametrized constructor used to create the object. + + The parametrized constructor. + + + + Contract details for a used by the . + + + + + Initializes a new instance of the class. + + The underlying type for the contract. + + + + Maps a JSON property to a .NET member. + + + + + Returns a that represents this instance. + + + A that represents this instance. + + + + + Gets the name of the property. + + The name of the property. + + + + Gets the that will get and set the during serialization. + + The that will get and set the during serialization. + + + + Gets or sets the type of the property. + + The type of the property. + + + + Gets or sets the for the property. + If set this converter takes presidence over the contract converter for the property type. + + The converter. + + + + Gets a value indicating whether this is ignored. + + true if ignored; otherwise, false. + + + + Gets a value indicating whether this is readable. + + true if readable; otherwise, false. + + + + Gets a value indicating whether this is writable. + + true if writable; otherwise, false. + + + + Gets the member converter. + + The member converter. + + + + Gets the default value. + + The default value. + + + + Gets a value indicating whether this is required. + + A value indicating whether this is required. + + + + Gets a value indicating whether this property preserves object references. + + + true if this instance is reference; otherwise, false. + + + + + Gets the property null value handling. + + The null value handling. + + + + Gets the property default value handling. + + The default value handling. + + + + Gets the property reference loop handling. + + The reference loop handling. + + + + Gets the property object creation handling. + + The object creation handling. + + + + Gets or sets the type name handling. + + The type name handling. + + + + Gets or sets a predicate used to determine whether the property should be serialize. + + A predicate used to determine whether the property should be serialize. + + + + A collection of objects. + + + + + Initializes a new instance of the class. + + The contract. + + + + When implemented in a derived class, extracts the key from the specified element. + + The element from which to extract the key. + The key for the specified element. + + + + Adds a object. + + The property to add to the collection. + + + + Gets the closest matching object. + First attempts to get an exact case match of propertyName and then + a case insensitive match. + + Name of the property. + A matching property if found. + + + + Gets a property by property name. + + The name of the property to get. + Type property name string comparison. + A matching property if found. + + + + Contract details for a used by the . + + + + + Initializes a new instance of the class. + + The underlying type for the contract. + + + + Represents a method that constructs an object. + + + + + When applied to a method, specifies that the method is called when an error occurs serializing an object. + + + + + Get and set values for a using reflection. + + + + + Initializes a new instance of the class. + + The member info. + + + + Sets the value. + + The target to set the value on. + The value to set on the target. + + + + Gets the value. + + The target to get the value from. + The value. + + + + Specifies type name handling options for the . + + + + + Do not include the .NET type name when serializing types. + + + + + Include the .NET type name when serializing into a JSON object structure. + + + + + Include the .NET type name when serializing into a JSON array structure. + + + + + Include the .NET type name when the type of the object being serialized is not the same as its declared type. + + + + + Always include the .NET type name when serializing. + + + + + Determines whether the collection is null or empty. + + The collection. + + true if the collection is null or empty; otherwise, false. + + + + + Determines whether the collection is null or empty. + + The collection. + + true if the collection is null or empty; otherwise, false. + + + + + Determines whether the collection is null, empty or its contents are uninitialized values. + + The list. + + true if the collection is null or empty or its contents are uninitialized values; otherwise, false. + + + + + Makes a slice of the specified list in between the start and end indexes. + + The list. + The start index. + The end index. + A slice of the list. + + + + Makes a slice of the specified list in between the start and end indexes, + getting every so many items based upon the step. + + The list. + The start index. + The end index. + The step. + A slice of the list. + + + + Group the collection using a function which returns the key. + + The source collection to group. + The key selector. + A Dictionary with each key relating to a list of objects in a list grouped under it. + + + + Adds the elements of the specified collection to the specified generic IList. + + The list to add to. + The collection of elements to add. + + + + Converts the value to the specified type. + + The type to convert the value to. + The value to convert. + The converted type. + + + + Converts the value to the specified type. + + The type to convert the value to. + The value to convert. + The culture to use when converting. + The converted type. + + + + Converts the value to the specified type. + + The value to convert. + The culture to use when converting. + The type to convert the value to. + The converted type. + + + + Converts the value to the specified type. + + The type to convert the value to. + The value to convert. + The converted value if the conversion was successful or the default value of T if it failed. + + true if initialValue was converted successfully; otherwise, false. + + + + + Converts the value to the specified type. + + The type to convert the value to. + The value to convert. + The culture to use when converting. + The converted value if the conversion was successful or the default value of T if it failed. + + true if initialValue was converted successfully; otherwise, false. + + + + + Converts the value to the specified type. + + The value to convert. + The culture to use when converting. + The type to convert the value to. + The converted value if the conversion was successful or the default value of T if it failed. + + true if initialValue was converted successfully; otherwise, false. + + + + + Converts the value to the specified type. If the value is unable to be converted, the + value is checked whether it assignable to the specified type. + + The type to convert or cast the value to. + The value to convert. + The converted type. If conversion was unsuccessful, the initial value is returned if assignable to the target type + + + + Converts the value to the specified type. If the value is unable to be converted, the + value is checked whether it assignable to the specified type. + + The type to convert or cast the value to. + The value to convert. + The culture to use when converting. + The converted type. If conversion was unsuccessful, the initial value is returned if assignable to the target type + + + + Converts the value to the specified type. If the value is unable to be converted, the + value is checked whether it assignable to the specified type. + + The value to convert. + The culture to use when converting. + The type to convert or cast the value to. + + The converted type. If conversion was unsuccessful, the initial value + is returned if assignable to the target type. + + + + + Converts the value to the specified type. If the value is unable to be converted, the + value is checked whether it assignable to the specified type. + + The type to convert the value to. + The value to convert. + The converted value if the conversion was successful or the default value of T if it failed. + + true if initialValue was converted successfully or is assignable; otherwise, false. + + + + + Converts the value to the specified type. If the value is unable to be converted, the + value is checked whether it assignable to the specified type. + + The type to convert the value to. + The value to convert. + The culture to use when converting. + The converted value if the conversion was successful or the default value of T if it failed. + + true if initialValue was converted successfully or is assignable; otherwise, false. + + + + + Converts the value to the specified type. If the value is unable to be converted, the + value is checked whether it assignable to the specified type. + + The value to convert. + The culture to use when converting. + The type to convert the value to. + The converted value if the conversion was successful or the default value of T if it failed. + + true if initialValue was converted successfully or is assignable; otherwise, false. + + + + + Parses the specified enum member name, returning it's value. + + Name of the enum member. + + + + + Parses the specified enum member name, returning it's value. + + Name of the enum member. + If set to true ignore case. + + + + + Gets a dictionary of the names and values of an Enum type. + + + + + + Gets a dictionary of the names and values of an Enum type. + + + + + + Gets a dictionary of the names and values of an Enum type. + + The enum type to get names and values for. + + + + + Gets the maximum valid value of an Enum type. Flags enums are ORed. + + The type of the returned value. Must be assignable from the enum's underlying value type. + The enum type to get the maximum value for. + + + + + Gets the type of the typed collection's items. + + The type. + The type of the typed collection's items. + + + + Tests whether the list's items are their unitialized value. + + The list. + Whether the list's items are their unitialized value + + + + Gets the member's underlying type. + + The member. + The underlying type of the member. + + + + Determines whether the member is an indexed property. + + The member. + + true if the member is an indexed property; otherwise, false. + + + + + Determines whether the property is an indexed property. + + The property. + + true if the property is an indexed property; otherwise, false. + + + + + Gets the member's value on the object. + + The member. + The target object. + The member's value on the object. + + + + Sets the member's value on the target object. + + The member. + The target. + The value. + + + + Determines whether the specified MemberInfo can be read. + + The MemberInfo to determine whether can be read. + /// if set to true then allow the member to be gotten non-publicly. + + true if the specified MemberInfo can be read; otherwise, false. + + + + + Determines whether the specified MemberInfo can be set. + + The MemberInfo to determine whether can be set. + if set to true then allow the member to be set non-publicly. + + true if the specified MemberInfo can be set; otherwise, false. + + + + + Builds a string. Unlike StringBuilder this class lets you reuse it's internal buffer. + + + + + Determines whether the string contains white space. + + The string to test for white space. + + true if the string contains white space; otherwise, false. + + + + + Determines whether the string is all white space. Empty string will return false. + + The string to test whether it is all white space. + + true if the string is all white space; otherwise, false. + + + + + Ensures the target string ends with the specified string. + + The target. + The value. + The target string with the value string at the end. + + + + Perform an action if the string is not null or empty. + + The value. + The action to perform. + + + + Indents the specified string. + + The string to indent. + The number of characters to indent by. + + + + + Indents the specified string. + + The string to indent. + The number of characters to indent by. + The indent character. + + + + + Numbers the lines. + + The string to number. + + + + + Nulls an empty string. + + The string. + Null if the string was null, otherwise the string unchanged. + + + + Contains the JSON schema extension methods. + + + + + Determines whether the is valid. + + The source to test. + The schema to test with. + + true if the specified is valid; otherwise, false. + + + + + Validates the specified . + + The source to test. + The schema to test with. + + + + Validates the specified . + + The source to test. + The schema to test with. + The validation event handler. + + + + Returns detailed information about the schema exception. + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class + with a specified error message. + + The error message that explains the reason for the exception. + + + + Initializes a new instance of the class + with a specified error message and a reference to the inner exception that is the cause of this exception. + + The error message that explains the reason for the exception. + The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified. + + + + Gets the line number indicating where the error occurred. + + The line number indicating where the error occurred. + + + + Gets the line position indicating where the error occurred. + + The line position indicating where the error occurred. + + + + Specifies undefined schema Id handling options for the . + + + + + Do not infer a schema Id. + + + + + Use the .NET type name as the schema Id. + + + + + Use the assembly qualified .NET type name as the schema Id. + + + + + Returns detailed information related to the . + + + + + Gets the associated with the validation event. + + The JsonSchemaException associated with the validation event. + + + + Gets the text description corresponding to the validation event. + + The text description. + + + + Represents the callback method that will handle JSON schema validation events and the . + + + + + An in-memory representation of a JSON Schema. + + + + + Initializes a new instance of the class. + + + + + Reads a from the specified . + + The containing the JSON Schema to read. + The object representing the JSON Schema. + + + + Reads a from the specified . + + The containing the JSON Schema to read. + The to use when resolving schema references. + The object representing the JSON Schema. + + + + Load a from a string that contains schema JSON. + + A that contains JSON. + A populated from the string that contains JSON. + + + + Parses the specified json. + + The json. + The resolver. + A populated from the string that contains JSON. + + + + Writes this schema to a . + + A into which this method will write. + + + + Writes this schema to a using the specified . + + A into which this method will write. + The resolver used. + + + + Returns a that represents the current . + + + A that represents the current . + + + + + Gets or sets the id. + + + + + Gets or sets the title. + + + + + Gets or sets whether the object is optional. + + + + + Gets or sets whether the object is read only. + + + + + Gets or sets whether the object is visible to users. + + + + + Gets or sets whether the object is transient. + + + + + Gets or sets the description of the object. + + + + + Gets or sets the types of values allowed by the object. + + The type. + + + + Gets or sets the pattern. + + The pattern. + + + + Gets or sets the minimum length. + + The minimum length. + + + + Gets or sets the maximum length. + + The maximum length. + + + + Gets or sets the maximum decimals. + + The maximum decimals. + + + + Gets or sets the minimum. + + The minimum. + + + + Gets or sets the maximum. + + The maximum. + + + + Gets or sets the minimum number of items. + + The minimum number of items. + + + + Gets or sets the maximum number of items. + + The maximum number of items. + + + + Gets or sets the of items. + + The of items. + + + + Gets or sets the of properties. + + The of properties. + + + + Gets or sets the of additional properties. + + The of additional properties. + + + + Gets or sets a value indicating whether additional properties are allowed. + + + true if additional properties are allowed; otherwise, false. + + + + + Gets or sets the required property if this property is present. + + The required property if this property is present. + + + + Gets or sets the identity. + + The identity. + + + + Gets or sets the a collection of valid enum values allowed. + + A collection of valid enum values allowed. + + + + Gets or sets a collection of options. + + A collection of options. + + + + Gets or sets disallowed types. + + The disallow types. + + + + Gets or sets the default value. + + The default value. + + + + Gets or sets the extend . + + The extended . + + + + Gets or sets the format. + + The format. + + + + Generates a from a specified . + + + + + Generate a from the specified type. + + The type to generate a from. + A generated from the specified type. + + + + Generate a from the specified type. + + The type to generate a from. + The used to resolve schema references. + A generated from the specified type. + + + + Generate a from the specified type. + + The type to generate a from. + Specify whether the generated root will be nullable. + A generated from the specified type. + + + + Generate a from the specified type. + + The type to generate a from. + The used to resolve schema references. + Specify whether the generated root will be nullable. + A generated from the specified type. + + + + Gets or sets how undefined schemas are handled by the serializer. + + + + + Gets or sets the contract resolver. + + The contract resolver. + + + + Resolves from an id. + + + + + Initializes a new instance of the class. + + + + + Gets a for the specified id. + + The id. + A for the specified id. + + + + Gets or sets the loaded schemas. + + The loaded schemas. + + + + The value types allowed by the . + + + + + No type specified. + + + + + String type. + + + + + Float type. + + + + + Integer type. + + + + + Boolean type. + + + + + Object type. + + + + + Array type. + + + + + Null type. + + + + + Any type. + + + + diff --git a/Util/Iron7Libs/JSON.Net/readme.txt b/Util/Iron7Libs/JSON.Net/readme.txt new file mode 100644 index 0000000000..8f8fd8d244 --- /dev/null +++ b/Util/Iron7Libs/JSON.Net/readme.txt @@ -0,0 +1,52 @@ +Json.NET + +http://james.newtonking.com/projects/json-net.aspx +http://www.codeplex.com/json/ + + +Description: + +Json.NET makes working with JSON formatted data in .NET simple. Quickly read and write JSON using LINQ to JSON or serialize your .NET objects with a single method call using the JsonSerializer. + +-Flexible JSON serializer to convert .NET objects to JSON and back again +-LINQ to JSON for reading and writing JSON +-Writes indented, easy to read JSON +-Convert JSON to and from XML +-Supports Silverlight and the Compact Framework + + + +Versions: + +Json.NET comes in different versions for the various .NET frameworks. + +-DotNet: + .NET latest (3.5 SP1) + +-DotNet20: + .NET 2.0 + +-Silverlight: + Silverlight 3.0 + +-Compact: + Compact Framework 3.5 + + + +Instructions: + + 1. Extract Newtonsoft.Json.dll and Newtonsoft.Json.xml from the archive's /bin directory into your own applications. + 2. Add a reference to Newtonsoft.Json.dll within Visual Studio.NET to your project. + + + +License: + +Copyright (c) 2007 James Newton-King + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/Util/Iron7Libs/Silverlight/System.Windows.Controls.dll b/Util/Iron7Libs/Silverlight/System.Windows.Controls.dll new file mode 100644 index 0000000000..d16d25819e Binary files /dev/null and b/Util/Iron7Libs/Silverlight/System.Windows.Controls.dll differ