From e496f93b9e397efba058c3f9138a627e327dbc62 Mon Sep 17 00:00:00 2001 From: mhead <mtrxhead@protonmail.com> Date: Mon, 29 Aug 2022 17:55:47 +0530 Subject: [PATCH 1/7] refactor: Migrate the markdown feature from Dart to Rust --- .../src/codec/markdown/markdown_encoder.rs | 387 ++++++++++++++++++ shared-lib/lib-ot/src/codec/markdown/mod.rs | 2 +- shared-lib/lib-ot/src/rich_text/attributes.rs | 4 + 3 files changed, 392 insertions(+), 1 deletion(-) create mode 100644 shared-lib/lib-ot/src/codec/markdown/markdown_encoder.rs diff --git a/shared-lib/lib-ot/src/codec/markdown/markdown_encoder.rs b/shared-lib/lib-ot/src/codec/markdown/markdown_encoder.rs new file mode 100644 index 0000000000..f338a029a4 --- /dev/null +++ b/shared-lib/lib-ot/src/codec/markdown/markdown_encoder.rs @@ -0,0 +1,387 @@ +use crate::core::{Delta, DeltaIterator}; +use crate::rich_text::{is_block, RichTextAttributeKey, RichTextAttributeValue, RichTextAttributes}; +use std::collections::HashMap; + +const LINEFEEDASCIICODE: i32 = 0x0A; + +#[cfg(test)] +mod tests { + use crate::codec::markdown::markdown_encoder::markdown_encoder; + use crate::rich_text::RichTextDelta; + + #[test] + fn markdown_encoder_header_1_test() { + let json = r#"[{"insert":"header 1"},{"insert":"\n","attributes":{"header":1}}]"#; + let delta = RichTextDelta::from_json(json).unwrap(); + let md = markdown_encoder(&delta); + assert_eq!(md, "# header 1\n"); + } + + #[test] + fn markdown_encoder_header_2_test() { + let json = r#"[{"insert":"header 2"},{"insert":"\n","attributes":{"header":2}}]"#; + let delta = RichTextDelta::from_json(json).unwrap(); + let md = markdown_encoder(&delta); + assert_eq!(md, "## header 2\n"); + } + + #[test] + fn markdown_encoder_header_3_test() { + let json = r#"[{"insert":"header 3"},{"insert":"\n","attributes":{"header":3}}]"#; + let delta = RichTextDelta::from_json(json).unwrap(); + let md = markdown_encoder(&delta); + assert_eq!(md, "### header 3\n"); + } + + #[test] + fn markdown_encoder_bold_italics_underlined_test() { + let json = r#"[{"insert":"bold","attributes":{"bold":true}},{"insert":" "},{"insert":"italics","attributes":{"italic":true}},{"insert":" "},{"insert":"underlined","attributes":{"underline":true}},{"insert":" "},{"insert":"\n","attributes":{"header":3}}]"#; + let delta = RichTextDelta::from_json(json).unwrap(); + let md = markdown_encoder(&delta); + assert_eq!(md, "### **bold** _italics_ <u>underlined</u> \n"); + } + #[test] + fn markdown_encoder_strikethrough_highlight_test() { + let json = r##"[{"insert":"strikethrough","attributes":{"strike":true}},{"insert":" "},{"insert":"highlighted","attributes":{"background":"#ffefe3"}},{"insert":"\n"}]"##; + let delta = RichTextDelta::from_json(json).unwrap(); + let md = markdown_encoder(&delta); + assert_eq!(md, "~~strikethrough~~ <mark>highlighted</mark>\n"); + } + + #[test] + fn markdown_encoder_numbered_list_test() { + let json = r#"[{"insert":"numbered list\nitem 1"},{"insert":"\n","attributes":{"list":"ordered"}},{"insert":"item 2"},{"insert":"\n","attributes":{"list":"ordered"}},{"insert":"item3"},{"insert":"\n","attributes":{"list":"ordered"}}]"#; + let delta = RichTextDelta::from_json(json).unwrap(); + let md = markdown_encoder(&delta); + assert_eq!(md, "numbered list\n\n1. item 1\n1. item 2\n1. item3\n"); + } + + #[test] + fn markdown_encoder_bullet_list_test() { + let json = r#"[{"insert":"bullet list\nitem1"},{"insert":"\n","attributes":{"list":"bullet"}}]"#; + let delta = RichTextDelta::from_json(json).unwrap(); + let md = markdown_encoder(&delta); + assert_eq!(md, "bullet list\n\n* item1\n"); + } + + #[test] + fn markdown_encoder_check_list_test() { + let json = r#"[{"insert":"check list\nchecked"},{"insert":"\n","attributes":{"list":"checked"}},{"insert":"unchecked"},{"insert":"\n","attributes":{"list":"unchecked"}}]"#; + let delta = RichTextDelta::from_json(json).unwrap(); + let md = markdown_encoder(&delta); + assert_eq!(md, "check list\n\n- [x] checked\n\n- [ ] unchecked\n"); + } + + #[test] + fn markdown_encoder_code_test() { + let json = r#"[{"insert":"code this "},{"insert":"print(\"hello world\")","attributes":{"code":true}},{"insert":"\n"}]"#; + let delta = RichTextDelta::from_json(json).unwrap(); + let md = markdown_encoder(&delta); + assert_eq!(md, "code this `print(\"hello world\")`\n"); + } + + #[test] + fn markdown_encoder_quote_block_test() { + let json = r#"[{"insert":"this is a quote block"},{"insert":"\n","attributes":{"blockquote":true}}]"#; + let delta = RichTextDelta::from_json(json).unwrap(); + let md = markdown_encoder(&delta); + assert_eq!(md, "> this is a quote block\n"); + } + + #[test] + fn markdown_encoder_link_test() { + let json = r#"[{"insert":"appflowy","attributes":{"link":"https://www.appflowy.io/"}},{"insert":"\n"}]"#; + let delta = RichTextDelta::from_json(json).unwrap(); + let md = markdown_encoder(&delta); + assert_eq!(md, "[appflowy](https://www.appflowy.io/)\n"); + } +} + +struct Attribute { + key: RichTextAttributeKey, + value: RichTextAttributeValue, +} + +pub fn markdown_encoder(delta: &Delta<RichTextAttributes>) -> String { + let mut markdown_buffer = String::new(); + let mut line_buffer = String::new(); + let mut current_inline_style = RichTextAttributes::default(); + let mut current_block_lines: Vec<String> = Vec::new(); + let mut iterator = DeltaIterator::new(delta); + let mut current_block_style: Option<Attribute> = None; + + while iterator.has_next() { + let operation = iterator.next().unwrap(); + let operation_data = operation.get_data(); + if !operation_data.contains("\n") { + handle_inline( + &mut current_inline_style, + &mut line_buffer, + String::from(operation_data), + operation.get_attributes(), + ) + } else { + handle_line( + &mut line_buffer, + &mut markdown_buffer, + String::from(operation_data), + operation.get_attributes(), + &mut current_block_style, + &mut current_block_lines, + &mut current_inline_style, + ) + } + } + handle_block(&mut current_block_style, &mut current_block_lines, &mut markdown_buffer); + + markdown_buffer +} + +fn handle_inline( + current_inline_style: &mut RichTextAttributes, + buffer: &mut String, + mut text: String, + attributes: RichTextAttributes, +) { + let mut marked_for_removal: HashMap<RichTextAttributeKey, RichTextAttributeValue> = HashMap::new(); + + for key in current_inline_style + .clone() + .keys() + .collect::<Vec<&RichTextAttributeKey>>() + .into_iter() + .rev() + { + if is_block(key) { + continue; + } + + if attributes.contains_key(key) { + continue; + } + + let padding = trim_right(buffer); + write_attribute(buffer, key, current_inline_style.get(key).unwrap(), true); + if !padding.is_empty() { + buffer.push_str(&padding) + } + marked_for_removal.insert(key.clone(), current_inline_style.get(key).unwrap().clone()); + } + + for (marked_for_removal_key, marked_for_removal_value) in &marked_for_removal { + current_inline_style.retain(|inline_style_key, inline_style_value| { + inline_style_key != marked_for_removal_key && inline_style_value != marked_for_removal_value + }) + } + + for (key, value) in attributes.iter() { + if is_block(key) { + continue; + } + if current_inline_style.contains_key(key) { + continue; + } + let original_text = text.clone(); + text = text.trim_start().to_string(); + let padding = " ".repeat(original_text.len() - text.len()); + if !padding.is_empty() { + buffer.push_str(&padding) + } + write_attribute(buffer, key, value, false) + } + + buffer.push_str(&text); + *current_inline_style = attributes; +} + +fn trim_right(buffer: &mut String) -> String { + let text = buffer.clone(); + if !text.ends_with(" ") { + return String::from(""); + } + let result = text.trim_end(); + buffer.clear(); + buffer.push_str(result); + " ".repeat(text.len() - result.len()) +} + +fn write_attribute(buffer: &mut String, key: &RichTextAttributeKey, value: &RichTextAttributeValue, close: bool) { + match key { + RichTextAttributeKey::Bold => buffer.push_str("**"), + RichTextAttributeKey::Italic => buffer.push_str("_"), + RichTextAttributeKey::Underline => { + if close { + buffer.push_str("</u>") + } else { + buffer.push_str("<u>") + } + } + RichTextAttributeKey::StrikeThrough => { + if close { + buffer.push_str("~~") + } else { + buffer.push_str("~~") + } + } + RichTextAttributeKey::Link => { + if close { + buffer.push_str(format!("]({})", value.0.as_ref().unwrap()).as_str()) + } else { + buffer.push_str("[") + } + } + RichTextAttributeKey::Background => { + if close { + buffer.push_str("</mark>") + } else { + buffer.push_str("<mark>") + } + } + RichTextAttributeKey::CodeBlock => { + if close { + buffer.push_str("\n```") + } else { + buffer.push_str("```\n") + } + } + RichTextAttributeKey::InlineCode => { + if close { + buffer.push_str("`") + } else { + buffer.push_str("`") + } + } + _ => {} + } +} + +fn handle_line( + buffer: &mut String, + markdown_buffer: &mut String, + data: String, + attributes: RichTextAttributes, + current_block_style: &mut Option<Attribute>, + current_block_lines: &mut Vec<String>, + current_inline_style: &mut RichTextAttributes, +) { + let mut span = String::new(); + for c in data.chars() { + if (c as i32) == LINEFEEDASCIICODE { + if !span.is_empty() { + handle_inline(current_inline_style, buffer, span.clone(), attributes.clone()); + } + handle_inline( + current_inline_style, + buffer, + String::from(""), + RichTextAttributes::default(), + ); + + let line_block_key = attributes.keys().find(|key| { + if is_block(*key) { + return true; + } else { + return false; + } + }); + + match (line_block_key, ¤t_block_style) { + (Some(line_block_key), Some(current_block_style)) + if *line_block_key == current_block_style.key + && *attributes.get(line_block_key).unwrap() == current_block_style.value => + { + current_block_lines.push(buffer.clone()); + } + (None, None) => { + current_block_lines.push(buffer.clone()); + } + _ => { + handle_block(current_block_style, current_block_lines, markdown_buffer); + current_block_lines.clear(); + current_block_lines.push(buffer.clone()); + + match line_block_key { + None => *current_block_style = None, + Some(line_block_key) => { + *current_block_style = Some(Attribute { + key: line_block_key.clone(), + value: attributes.get(line_block_key).unwrap().clone(), + }) + } + } + } + } + buffer.clear(); + span.clear(); + } else { + span.push(c); + } + } + if !span.is_empty() { + handle_inline(current_inline_style, buffer, span.clone(), attributes) + } +} + +fn handle_block( + block_style: &mut Option<Attribute>, + current_block_lines: &mut Vec<String>, + markdown_buffer: &mut String, +) { + if current_block_lines.is_empty() { + return; + } + if !markdown_buffer.is_empty() { + markdown_buffer.push('\n') + } + + match block_style { + None => { + markdown_buffer.push_str(¤t_block_lines.join("\n")); + markdown_buffer.push('\n'); + } + Some(block_style) if block_style.key == RichTextAttributeKey::CodeBlock => { + write_attribute(markdown_buffer, &block_style.key, &block_style.value, false); + markdown_buffer.push_str(¤t_block_lines.join("\n")); + write_attribute(markdown_buffer, &block_style.key, &block_style.value, true); + markdown_buffer.push('\n'); + } + Some(block_style) => { + for line in current_block_lines { + write_block_tag(markdown_buffer, &block_style, false); + markdown_buffer.push_str(line); + markdown_buffer.push('\n'); + } + } + } +} + +fn write_block_tag(buffer: &mut String, block: &Attribute, close: bool) { + if close { + return; + } + + if block.key == RichTextAttributeKey::BlockQuote { + buffer.push_str("> "); + } else if block.key == RichTextAttributeKey::List { + if block.value.0.as_ref().unwrap().eq("bullet") { + buffer.push_str("* "); + } else if block.value.0.as_ref().unwrap().eq("checked") { + buffer.push_str("- [x] "); + } else if block.value.0.as_ref().unwrap().eq("unchecked") { + buffer.push_str("- [ ] "); + } else if block.value.0.as_ref().unwrap().eq("ordered") { + buffer.push_str("1. "); + } else { + buffer.push_str("* "); + } + } else if block.key == RichTextAttributeKey::Header { + if block.value.0.as_ref().unwrap().eq("1") { + buffer.push_str("# "); + } else if block.value.0.as_ref().unwrap().eq("2") { + buffer.push_str("## "); + } else if block.value.0.as_ref().unwrap().eq("3") { + buffer.push_str("### "); + } else if block.key == RichTextAttributeKey::List { + } + } +} diff --git a/shared-lib/lib-ot/src/codec/markdown/mod.rs b/shared-lib/lib-ot/src/codec/markdown/mod.rs index 8b13789179..5a276efda3 100644 --- a/shared-lib/lib-ot/src/codec/markdown/mod.rs +++ b/shared-lib/lib-ot/src/codec/markdown/mod.rs @@ -1 +1 @@ - +pub mod markdown_encoder; diff --git a/shared-lib/lib-ot/src/rich_text/attributes.rs b/shared-lib/lib-ot/src/rich_text/attributes.rs index 54826c49a0..be3b1bbf59 100644 --- a/shared-lib/lib-ot/src/rich_text/attributes.rs +++ b/shared-lib/lib-ot/src/rich_text/attributes.rs @@ -361,6 +361,10 @@ pub fn is_block_except_header(k: &RichTextAttributeKey) -> bool { BLOCK_KEYS.contains(k) } +pub fn is_block(k: &RichTextAttributeKey) -> bool { + BLOCK_KEYS.contains(k) +} + lazy_static! { static ref BLOCK_KEYS: HashSet<RichTextAttributeKey> = HashSet::from_iter(vec![ RichTextAttributeKey::Header, From 6384edf0e65c9f442a8d8e18c06f6f5509ec5c55 Mon Sep 17 00:00:00 2001 From: appflowy <annie@appflowy.io> Date: Sun, 4 Sep 2022 23:11:05 +0800 Subject: [PATCH 2/7] chore: support group by checkbox field --- .../src/services/cell/cell_operation.rs | 1 - .../flowy-grid/src/services/group/action.rs | 7 +- .../src/services/group/controller.rs | 10 +- .../controller_impls/checkbox_controller.rs | 94 ++++++++++++++----- .../multi_select_controller.rs | 4 +- .../single_select_controller.rs | 4 +- .../select_option_controller/util.rs | 6 +- 7 files changed, 90 insertions(+), 36 deletions(-) diff --git a/frontend/rust-lib/flowy-grid/src/services/cell/cell_operation.rs b/frontend/rust-lib/flowy-grid/src/services/cell/cell_operation.rs index 17c52dd3a2..20dd09a5ef 100644 --- a/frontend/rust-lib/flowy-grid/src/services/cell/cell_operation.rs +++ b/frontend/rust-lib/flowy-grid/src/services/cell/cell_operation.rs @@ -182,7 +182,6 @@ pub fn delete_select_option_cell(option_id: String, field_rev: &FieldRevision) - CellRevision::new(data) } -/// If the cell data is not String type, it should impl this trait. /// Deserialize the String into cell specific data type. pub trait FromCellString { fn from_cell_str(s: &str) -> FlowyResult<Self> diff --git a/frontend/rust-lib/flowy-grid/src/services/group/action.rs b/frontend/rust-lib/flowy-grid/src/services/group/action.rs index d19be8395e..b339722905 100644 --- a/frontend/rust-lib/flowy-grid/src/services/group/action.rs +++ b/frontend/rust-lib/flowy-grid/src/services/group/action.rs @@ -1,13 +1,16 @@ use crate::entities::GroupChangesetPB; use crate::services::group::controller::MoveGroupRowContext; -use flowy_grid_data_model::revision::RowRevision; +use flowy_grid_data_model::revision::{CellRevision, RowRevision}; pub trait GroupAction: Send + Sync { type CellDataType; + fn default_cell_rev(&self) -> Option<CellRevision> { + None + } + fn can_group(&self, content: &str, cell_data: &Self::CellDataType) -> bool; fn add_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec<GroupChangesetPB>; fn remove_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec<GroupChangesetPB>; - fn move_row(&mut self, cell_data: &Self::CellDataType, context: MoveGroupRowContext) -> Vec<GroupChangesetPB>; } diff --git a/frontend/rust-lib/flowy-grid/src/services/group/controller.rs b/frontend/rust-lib/flowy-grid/src/services/group/controller.rs index 54d001d4a7..5825596aaa 100644 --- a/frontend/rust-lib/flowy-grid/src/services/group/controller.rs +++ b/frontend/rust-lib/flowy-grid/src/services/group/controller.rs @@ -7,7 +7,6 @@ use flowy_error::FlowyResult; use flowy_grid_data_model::revision::{ FieldRevision, GroupConfigurationContentSerde, GroupRevision, RowChangeset, RowRevision, TypeOptionDataDeserializer, }; - use std::marker::PhantomData; use std::sync::Arc; @@ -193,9 +192,14 @@ where #[tracing::instrument(level = "trace", skip_all, fields(row_count=%row_revs.len(), group_result))] fn fill_groups(&mut self, row_revs: &[Arc<RowRevision>], field_rev: &FieldRevision) -> FlowyResult<()> { for row_rev in row_revs { - if let Some(cell_rev) = row_rev.cells.get(&self.field_id) { + let cell_rev = match row_rev.cells.get(&self.field_id) { + None => self.default_cell_rev(), + Some(cell_rev) => Some(cell_rev.clone()), + }; + + if let Some(cell_rev) = cell_rev { let mut grouped_rows: Vec<GroupedRow> = vec![]; - let cell_bytes = decode_any_cell_data(cell_rev.data.clone(), field_rev); + let cell_bytes = decode_any_cell_data(cell_rev.data, field_rev); let cell_data = cell_bytes.parser::<P>()?; for group in self.group_ctx.concrete_groups() { if self.can_group(&group.filter_content, &cell_data) { diff --git a/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/checkbox_controller.rs b/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/checkbox_controller.rs index 02ff4cb503..3f7943239e 100644 --- a/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/checkbox_controller.rs +++ b/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/checkbox_controller.rs @@ -1,4 +1,4 @@ -use crate::entities::GroupChangesetPB; +use crate::entities::{GroupChangesetPB, InsertedRowPB, RowPB}; use crate::services::field::{CheckboxCellData, CheckboxCellDataParser, CheckboxTypeOptionPB, CHECK, UNCHECK}; use crate::services::group::action::GroupAction; use crate::services::group::configuration::GroupContext; @@ -6,8 +6,11 @@ use crate::services::group::controller::{ GenericGroupController, GroupController, GroupGenerator, MoveGroupRowContext, }; -use crate::services::group::GeneratedGroup; -use flowy_grid_data_model::revision::{CheckboxGroupConfigurationRevision, FieldRevision, GroupRevision, RowRevision}; +use crate::services::cell::insert_checkbox_cell; +use crate::services::group::{move_group_row, GeneratedGroup}; +use flowy_grid_data_model::revision::{ + CellRevision, CheckboxGroupConfigurationRevision, FieldRevision, GroupRevision, RowRevision, +}; pub type CheckboxGroupController = GenericGroupController< CheckboxGroupConfigurationRevision, @@ -20,30 +23,79 @@ pub type CheckboxGroupContext = GroupContext<CheckboxGroupConfigurationRevision> impl GroupAction for CheckboxGroupController { type CellDataType = CheckboxCellData; - fn can_group(&self, _content: &str, _cell_data: &Self::CellDataType) -> bool { - false + fn default_cell_rev(&self) -> Option<CellRevision> { + Some(CellRevision::new(UNCHECK.to_string())) } - fn add_row_if_match(&mut self, _row_rev: &RowRevision, _cell_data: &Self::CellDataType) -> Vec<GroupChangesetPB> { - todo!() + fn can_group(&self, content: &str, cell_data: &Self::CellDataType) -> bool { + return if cell_data.is_check() { + content == CHECK + } else { + content == UNCHECK + }; } - fn remove_row_if_match( - &mut self, - _row_rev: &RowRevision, - _cell_data: &Self::CellDataType, - ) -> Vec<GroupChangesetPB> { - todo!() + fn add_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec<GroupChangesetPB> { + let mut changesets = vec![]; + self.group_ctx.iter_mut_groups(|group| { + let mut changeset = GroupChangesetPB::new(group.id.clone()); + let is_contained = group.contains_row(&row_rev.id); + if group.id == CHECK && cell_data.is_check() { + if !is_contained { + let row_pb = RowPB::from(row_rev); + changeset.inserted_rows.push(InsertedRowPB::new(row_pb.clone())); + group.add_row(row_pb); + } + } else { + if is_contained { + changeset.deleted_rows.push(row_rev.id.clone()); + group.remove_row(&row_rev.id); + } + } + if !changeset.is_empty() { + changesets.push(changeset); + } + }); + changesets } - fn move_row(&mut self, _cell_data: &Self::CellDataType, _context: MoveGroupRowContext) -> Vec<GroupChangesetPB> { - todo!() + fn remove_row_if_match(&mut self, row_rev: &RowRevision, _cell_data: &Self::CellDataType) -> Vec<GroupChangesetPB> { + let mut changesets = vec![]; + self.group_ctx.iter_mut_groups(|group| { + let mut changeset = GroupChangesetPB::new(group.id.clone()); + if group.contains_row(&row_rev.id) { + changeset.deleted_rows.push(row_rev.id.clone()); + group.remove_row(&row_rev.id); + } + + if !changeset.is_empty() { + changesets.push(changeset); + } + }); + changesets + } + + fn move_row(&mut self, _cell_data: &Self::CellDataType, mut context: MoveGroupRowContext) -> Vec<GroupChangesetPB> { + let mut group_changeset = vec![]; + self.group_ctx.iter_mut_groups(|group| { + if let Some(changeset) = move_group_row(group, &mut context) { + group_changeset.push(changeset); + } + }); + group_changeset } } impl GroupController for CheckboxGroupController { - fn will_create_row(&mut self, _row_rev: &mut RowRevision, _field_rev: &FieldRevision, _group_id: &str) { - todo!() + fn will_create_row(&mut self, row_rev: &mut RowRevision, field_rev: &FieldRevision, group_id: &str) { + match self.group_ctx.get_group(group_id) { + None => tracing::warn!("Can not find the group: {}", group_id), + Some((_, group)) => { + let is_check = group.id == CHECK; + let cell_rev = insert_checkbox_cell(is_check, field_rev); + row_rev.cells.insert(field_rev.id.clone(), cell_rev); + } + } } } @@ -58,13 +110,13 @@ impl GroupGenerator for CheckboxGroupGenerator { _type_option: &Option<Self::TypeOptionType>, ) -> Vec<GeneratedGroup> { let check_group = GeneratedGroup { - group_rev: GroupRevision::new("true".to_string(), CHECK.to_string()), - filter_content: "".to_string(), + group_rev: GroupRevision::new(CHECK.to_string(), "".to_string()), + filter_content: CHECK.to_string(), }; let uncheck_group = GeneratedGroup { - group_rev: GroupRevision::new("false".to_string(), UNCHECK.to_string()), - filter_content: "".to_string(), + group_rev: GroupRevision::new(UNCHECK.to_string(), "".to_string()), + filter_content: UNCHECK.to_string(), }; vec![check_group, uncheck_group] } diff --git a/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/multi_select_controller.rs b/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/multi_select_controller.rs index 026843bbcc..2d8139bc73 100644 --- a/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/multi_select_controller.rs +++ b/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/multi_select_controller.rs @@ -46,10 +46,10 @@ impl GroupAction for MultiSelectGroupController { changesets } - fn move_row(&mut self, cell_data: &Self::CellDataType, mut context: MoveGroupRowContext) -> Vec<GroupChangesetPB> { + fn move_row(&mut self, _cell_data: &Self::CellDataType, mut context: MoveGroupRowContext) -> Vec<GroupChangesetPB> { let mut group_changeset = vec![]; self.group_ctx.iter_mut_groups(|group| { - if let Some(changeset) = move_select_option_row(group, cell_data, &mut context) { + if let Some(changeset) = move_group_row(group, &mut context) { group_changeset.push(changeset); } }); diff --git a/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/single_select_controller.rs b/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/single_select_controller.rs index 96b686efe3..5d2ddb9bd9 100644 --- a/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/single_select_controller.rs +++ b/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/single_select_controller.rs @@ -46,10 +46,10 @@ impl GroupAction for SingleSelectGroupController { changesets } - fn move_row(&mut self, cell_data: &Self::CellDataType, mut context: MoveGroupRowContext) -> Vec<GroupChangesetPB> { + fn move_row(&mut self, _cell_data: &Self::CellDataType, mut context: MoveGroupRowContext) -> Vec<GroupChangesetPB> { let mut group_changeset = vec![]; self.group_ctx.iter_mut_groups(|group| { - if let Some(changeset) = move_select_option_row(group, cell_data, &mut context) { + if let Some(changeset) = move_group_row(group, &mut context) { group_changeset.push(changeset); } }); diff --git a/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/util.rs b/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/util.rs index 494cd41197..a1d81b0d5c 100644 --- a/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/util.rs +++ b/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/util.rs @@ -62,11 +62,7 @@ pub fn remove_select_option_row( } } -pub fn move_select_option_row( - group: &mut Group, - _cell_data: &SelectOptionCellDataPB, - context: &mut MoveGroupRowContext, -) -> Option<GroupChangesetPB> { +pub fn move_group_row(group: &mut Group, context: &mut MoveGroupRowContext) -> Option<GroupChangesetPB> { let mut changeset = GroupChangesetPB::new(group.id.clone()); let MoveGroupRowContext { row_rev, From 5b92805e93e80a3a900a06b73a92d164a9ed96b7 Mon Sep 17 00:00:00 2001 From: appflowy <annie@appflowy.io> Date: Mon, 5 Sep 2022 09:24:05 +0800 Subject: [PATCH 3/7] chore: config checkbox ui --- .../plugins/board/application/board_bloc.dart | 89 ++++++++++----- .../board/presentation/board_page.dart | 102 ++++++++++++------ .../application/field/field_controller.dart | 31 ++++-- .../widgets/board_column/board_column.dart | 2 +- .../src/entities/group_entities/group.rs | 4 +- 5 files changed, 157 insertions(+), 71 deletions(-) diff --git a/frontend/app_flowy/lib/plugins/board/application/board_bloc.dart b/frontend/app_flowy/lib/plugins/board/application/board_bloc.dart index 26dd15038a..11f4cf6910 100644 --- a/frontend/app_flowy/lib/plugins/board/application/board_bloc.dart +++ b/frontend/app_flowy/lib/plugins/board/application/board_bloc.dart @@ -1,4 +1,6 @@ import 'dart:async'; +import 'dart:collection'; + import 'package:app_flowy/plugins/grid/application/block/block_cache.dart'; import 'package:app_flowy/plugins/grid/application/field/field_controller.dart'; import 'package:app_flowy/plugins/grid/application/row/row_cache.dart'; @@ -12,7 +14,6 @@ import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/protobuf.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; -import 'dart:collection'; import 'board_data_controller.dart'; import 'group_controller.dart'; @@ -164,12 +165,17 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> { boardController.clear(); // - List<AFBoardColumnData> columns = groups.map((group) { + List<AFBoardColumnData> columns = groups + .where((group) => fieldController.getField(group.fieldId) != null) + .map((group) { return AFBoardColumnData( id: group.groupId, name: group.desc, items: _buildRows(group), - customData: group, + customData: BoardCustomData( + group: group, + fieldContext: fieldController.getField(group.fieldId)!, + ), ); }).toList(); boardController.addColumns(columns); @@ -177,6 +183,7 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> { for (final group in groups) { final delegate = GroupControllerDelegateImpl( controller: boardController, + fieldController: fieldController, onNewColumnItem: (groupId, row, index) { add(BoardEvent.didCreateRow(groupId, row, index)); }, @@ -238,10 +245,8 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> { List<AFColumnItem> _buildRows(GroupPB group) { final items = group.rows.map((row) { - return BoardColumnItem( - row: row, - fieldId: group.fieldId, - ); + final fieldContext = fieldController.getField(group.fieldId); + return BoardColumnItem(row: row, fieldContext: fieldContext!); }).toList(); return <AFColumnItem>[...items]; @@ -332,15 +337,11 @@ class GridFieldEquatable extends Equatable { class BoardColumnItem extends AFColumnItem { final RowPB row; - - final String fieldId; - - final bool requestFocus; + final GridFieldContext fieldContext; BoardColumnItem({ required this.row, - required this.fieldId, - this.requestFocus = false, + required this.fieldContext, }); @override @@ -348,24 +349,29 @@ class BoardColumnItem extends AFColumnItem { } class GroupControllerDelegateImpl extends GroupControllerDelegate { + final GridFieldController fieldController; final AFBoardDataController controller; final void Function(String, RowPB, int?) onNewColumnItem; GroupControllerDelegateImpl({ required this.controller, + required this.fieldController, required this.onNewColumnItem, }); @override void insertRow(GroupPB group, RowPB row, int? index) { + final fieldContext = fieldController.getField(group.fieldId); + if (fieldContext == null) { + Log.warn("FieldContext should not be null"); + return; + } + if (index != null) { - final item = BoardColumnItem(row: row, fieldId: group.fieldId); + final item = BoardColumnItem(row: row, fieldContext: fieldContext); controller.insertColumnItem(group.groupId, index, item); } else { - final item = BoardColumnItem( - row: row, - fieldId: group.fieldId, - ); + final item = BoardColumnItem(row: row, fieldContext: fieldContext); controller.addColumnItem(group.groupId, item); } } @@ -377,22 +383,25 @@ class GroupControllerDelegateImpl extends GroupControllerDelegate { @override void updateRow(GroupPB group, RowPB row) { + final fieldContext = fieldController.getField(group.fieldId); + if (fieldContext == null) { + Log.warn("FieldContext should not be null"); + return; + } controller.updateColumnItem( group.groupId, - BoardColumnItem( - row: row, - fieldId: group.fieldId, - ), + BoardColumnItem(row: row, fieldContext: fieldContext), ); } @override void addNewRow(GroupPB group, RowPB row, int? index) { - final item = BoardColumnItem( - row: row, - fieldId: group.fieldId, - requestFocus: true, - ); + final fieldContext = fieldController.getField(group.fieldId); + if (fieldContext == null) { + Log.warn("FieldContext should not be null"); + return; + } + final item = BoardColumnItem(row: row, fieldContext: fieldContext); if (index != null) { controller.insertColumnItem(group.groupId, index, item); @@ -414,3 +423,29 @@ class BoardEditingRow { required this.index, }); } + +class BoardCustomData { + final GroupPB group; + final GridFieldContext fieldContext; + BoardCustomData({ + required this.group, + required this.fieldContext, + }); + + CheckboxGroup? asCheckboxGroup() { + if (fieldType != FieldType.Checkbox) return null; + return CheckboxGroup(group); + } + + FieldType get fieldType => fieldContext.fieldType; +} + +class CheckboxGroup { + final GroupPB group; + + CheckboxGroup(this.group); + +// Hardcode value: "Yes" that equal to the value defined in Rust +// pub const CHECK: &str = "Yes"; + bool get isCheck => group.groupId == "Yes"; +} diff --git a/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart b/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart index d6c5dca609..0fd29867c6 100644 --- a/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart +++ b/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart @@ -17,7 +17,7 @@ import 'package:flowy_infra_ui/style_widget/text.dart'; import 'package:flowy_infra_ui/widget/error_page.dart'; import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart'; -import 'package:flowy_sdk/protobuf/flowy-grid/group.pbserver.dart'; +import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import '../../grid/application/row/row_cache.dart'; @@ -36,8 +36,7 @@ class BoardPage extends StatelessWidget { create: (context) => BoardBloc(view: view)..add(const BoardEvent.initial()), child: BlocBuilder<BoardBloc, BoardState>( - buildWhen: (previous, current) => - previous.loadingState != current.loadingState, + buildWhen: (p, c) => p.loadingState != c.loadingState, builder: (context, state) { return state.loadingState.map( loading: (_) => @@ -84,36 +83,15 @@ class _BoardContentState extends State<BoardContent> { child: BlocBuilder<BoardBloc, BoardState>( buildWhen: (previous, current) => previous.groupIds != current.groupIds, builder: (context, state) { - final theme = context.read<AppTheme>(); + final column = Column( + children: [const _ToolbarBlocAdaptor(), _buildBoard(context)], + ); + return Container( - color: theme.surface, + color: context.read<AppTheme>().surface, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 20), - child: Column( - children: [ - const _ToolbarBlocAdaptor(), - Expanded( - child: AFBoard( - key: UniqueKey(), - scrollManager: scrollManager, - scrollController: scrollController, - dataController: context.read<BoardBloc>().boardController, - headerBuilder: _buildHeader, - footBuilder: _buildFooter, - cardBuilder: (_, column, columnItem) => _buildCard( - context, - column, - columnItem, - ), - columnConstraints: - const BoxConstraints.tightFor(width: 300), - config: AFBoardConfig( - columnBackgroundColor: HexColor.fromHex('#F7F8FC'), - ), - ), - ), - ], - ), + child: column, ), ); }, @@ -121,6 +99,27 @@ class _BoardContentState extends State<BoardContent> { ); } + Expanded _buildBoard(BuildContext context) { + return Expanded( + child: AFBoard( + scrollManager: scrollManager, + scrollController: scrollController, + dataController: context.read<BoardBloc>().boardController, + headerBuilder: _buildHeader, + footBuilder: _buildFooter, + cardBuilder: (_, column, columnItem) => _buildCard( + context, + column, + columnItem, + ), + columnConstraints: const BoxConstraints.tightFor(width: 300), + config: AFBoardConfig( + columnBackgroundColor: HexColor.fromHex('#F7F8FC'), + ), + ), + ); + } + void _handleEditState(BoardState state, BuildContext context) { state.editingRow.fold( () => null, @@ -152,6 +151,7 @@ class _BoardContentState extends State<BoardContent> { BuildContext context, AFBoardColumnData columnData, ) { + final boardCustomData = columnData.customData as BoardCustomData; return AppFlowyColumnHeader( title: Flexible( fit: FlexFit.tight, @@ -162,6 +162,7 @@ class _BoardContentState extends State<BoardContent> { color: context.read<AppTheme>().textColor, ), ), + icon: _buildHeaderIcon(boardCustomData), addIcon: SizedBox( height: 20, width: 20, @@ -181,7 +182,9 @@ class _BoardContentState extends State<BoardContent> { } Widget _buildFooter(BuildContext context, AFBoardColumnData columnData) { - final group = columnData.customData as GroupPB; + final boardCustomData = columnData.customData as BoardCustomData; + final group = boardCustomData.group; + if (group.isDefault) { return const SizedBox(); } else { @@ -246,7 +249,7 @@ class _BoardContentState extends State<BoardContent> { child: BoardCard( gridId: gridId, groupId: column.id, - fieldId: boardColumnItem.fieldId, + fieldId: boardColumnItem.fieldContext.id, isEditing: isEditing, cellBuilder: cellBuilder, dataController: cardController, @@ -319,3 +322,38 @@ extension HexColor on Color { return Color(int.parse(buffer.toString(), radix: 16)); } } + +Widget? _buildHeaderIcon(BoardCustomData customData) { + Widget? widget; + switch (customData.fieldType) { + case FieldType.Checkbox: + final group = customData.asCheckboxGroup()!; + if (group.isCheck) { + widget = svgWidget('editor/editor_check'); + } else { + widget = svgWidget('editor/editor_uncheck'); + } + break; + case FieldType.DateTime: + break; + case FieldType.MultiSelect: + break; + case FieldType.Number: + break; + case FieldType.RichText: + break; + case FieldType.SingleSelect: + break; + case FieldType.URL: + break; + } + + if (widget != null) { + widget = SizedBox( + width: 20, + height: 20, + child: widget, + ); + } + return widget; +} diff --git a/frontend/app_flowy/lib/plugins/grid/application/field/field_controller.dart b/frontend/app_flowy/lib/plugins/grid/application/field/field_controller.dart index e9bda8eef5..7eb20a8a06 100644 --- a/frontend/app_flowy/lib/plugins/grid/application/field/field_controller.dart +++ b/frontend/app_flowy/lib/plugins/grid/application/field/field_controller.dart @@ -7,6 +7,7 @@ import 'package:dartz/dartz.dart'; import 'package:flowy_sdk/log.dart'; import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart'; +import 'package:flowy_sdk/protobuf/flowy-grid/group.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/setting_entities.pb.dart'; import 'package:flutter/foundation.dart'; import '../row/row_cache.dart'; @@ -35,12 +36,12 @@ class GridFieldController { final SettingListener _settingListener; final Map<OnReceiveFields, VoidCallback> _fieldCallbackMap = {}; final Map<OnChangeset, OnChangeset> _changesetCallbackMap = {}; - - _GridFieldNotifier? _fieldNotifier = _GridFieldNotifier(); - List<String> _groupFieldIds = []; final GridFFIService _gridFFIService; final SettingFFIService _settingFFIService; + _GridFieldNotifier? _fieldNotifier = _GridFieldNotifier(); + final Map<String, GridGroupConfigurationPB> _configurationByFieldId = {}; + List<GridFieldContext> get fieldContexts => [..._fieldNotifier?.fieldContexts ?? []]; @@ -67,31 +68,43 @@ class GridFieldController { //Listen on setting changes _settingListener.start(onSettingUpdated: (result) { result.fold( - (setting) => _updateFieldsWhenSettingChanged(setting), + (setting) => _updateGroupConfiguration(setting), (r) => Log.error(r), ); }); _settingFFIService.getSetting().then((result) { result.fold( - (setting) => _updateFieldsWhenSettingChanged(setting), + (setting) => _updateGroupConfiguration(setting), (err) => Log.error(err), ); }); } - void _updateFieldsWhenSettingChanged(GridSettingPB setting) { - _groupFieldIds = setting.groupConfigurations.items - .map((item) => item.groupFieldId) + GridFieldContext? getField(String fieldId) { + final fields = _fieldNotifier?.fieldContexts + .where( + (element) => element.id == fieldId, + ) .toList(); + if (fields?.isEmpty ?? true) { + return null; + } + return fields!.first; + } + void _updateGroupConfiguration(GridSettingPB setting) { + _configurationByFieldId.clear(); + for (final configuration in setting.groupConfigurations.items) { + _configurationByFieldId[configuration.fieldId] = configuration; + } _updateFieldContexts(); } void _updateFieldContexts() { if (_fieldNotifier != null) { for (var field in _fieldNotifier!.fieldContexts) { - if (_groupFieldIds.contains(field.id)) { + if (_configurationByFieldId[field.id] != null) { field._isGroupField = true; } else { field._isGroupField = false; diff --git a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_column/board_column.dart b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_column/board_column.dart index 79fe534941..ce998b365e 100644 --- a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_column/board_column.dart +++ b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_column/board_column.dart @@ -31,7 +31,7 @@ typedef AFBoardColumnCardBuilder = Widget Function( typedef AFBoardColumnHeaderBuilder = Widget? Function( BuildContext context, - AFBoardColumnData headerData, + AFBoardColumnData columnData, ); typedef AFBoardColumnFooterBuilder = Widget Function( diff --git a/frontend/rust-lib/flowy-grid/src/entities/group_entities/group.rs b/frontend/rust-lib/flowy-grid/src/entities/group_entities/group.rs index de6f920341..d2c6eb0efb 100644 --- a/frontend/rust-lib/flowy-grid/src/entities/group_entities/group.rs +++ b/frontend/rust-lib/flowy-grid/src/entities/group_entities/group.rs @@ -44,14 +44,14 @@ pub struct GridGroupConfigurationPB { pub id: String, #[pb(index = 2)] - pub group_field_id: String, + pub field_id: String, } impl std::convert::From<&GroupConfigurationRevision> for GridGroupConfigurationPB { fn from(rev: &GroupConfigurationRevision) -> Self { GridGroupConfigurationPB { id: rev.id.clone(), - group_field_id: rev.field_id.clone(), + field_id: rev.field_id.clone(), } } } From 4de0008e443f15c6039053e81992556526fecf66 Mon Sep 17 00:00:00 2001 From: appflowy <annie@appflowy.io> Date: Mon, 5 Sep 2022 17:13:52 +0800 Subject: [PATCH 4/7] chore: disable group field that does not support grouping --- .../application/field/field_controller.dart | 21 +++++++++++++++++++ .../widgets/toolbar/grid_group.dart | 7 ++++++- frontend/app_flowy/pubspec.lock | 2 +- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/frontend/app_flowy/lib/plugins/grid/application/field/field_controller.dart b/frontend/app_flowy/lib/plugins/grid/application/field/field_controller.dart index 7eb20a8a06..73127ed6df 100644 --- a/frontend/app_flowy/lib/plugins/grid/application/field/field_controller.dart +++ b/frontend/app_flowy/lib/plugins/grid/application/field/field_controller.dart @@ -290,5 +290,26 @@ class GridFieldContext { bool get isGroupField => _isGroupField; + bool get canGroup { + switch (_field.fieldType) { + case FieldType.Checkbox: + return true; + case FieldType.DateTime: + return false; + case FieldType.MultiSelect: + return true; + case FieldType.Number: + return false; + case FieldType.RichText: + return false; + case FieldType.SingleSelect: + return true; + case FieldType.URL: + return false; + } + + return false; + } + GridFieldContext({required FieldPB field}) : _field = field; } diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_group.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_group.dart index 0a82eef4bb..b400cda0fe 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_group.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_group.dart @@ -31,10 +31,15 @@ class GridGroupList extends StatelessWidget { child: BlocBuilder<GridGroupBloc, GridGroupState>( builder: (context, state) { final cells = state.fieldContexts.map((fieldContext) { - return _GridGroupCell( + Widget cell = _GridGroupCell( fieldContext: fieldContext, key: ValueKey(fieldContext.id), ); + + if (!fieldContext.canGroup) { + cell = IgnorePointer(child: Opacity(opacity: 0.3, child: cell)); + } + return cell; }).toList(); return ListView.separated( diff --git a/frontend/app_flowy/pubspec.lock b/frontend/app_flowy/pubspec.lock index bb8391eee9..8b8b9cfb05 100644 --- a/frontend/app_flowy/pubspec.lock +++ b/frontend/app_flowy/pubspec.lock @@ -35,7 +35,7 @@ packages: path: "packages/appflowy_editor" relative: true source: path - version: "0.0.3" + version: "0.0.4" args: dependency: transitive description: From e3d0e7c871698ade139a431b7e178e9998d7f00d Mon Sep 17 00:00:00 2001 From: annie <anqi.annie.wang@gmail.com> Date: Mon, 5 Sep 2022 17:58:53 +0800 Subject: [PATCH 5/7] docs: readme for appflowy_board --- .../board/presentation/board_page.dart | 2 +- .../packages/appflowy_board/README.md | 132 ++++++++---------- .../example/gifs/appflowy_board_builders.jpg | Bin 0 -> 88814 bytes .../example/lib/multi_board_list_example.dart | 2 +- .../appflowy_board/lib/src/widgets/board.dart | 6 +- 5 files changed, 63 insertions(+), 79 deletions(-) create mode 100644 frontend/app_flowy/packages/appflowy_board/example/gifs/appflowy_board_builders.jpg diff --git a/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart b/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart index 0fd29867c6..233917ec8c 100644 --- a/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart +++ b/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart @@ -106,7 +106,7 @@ class _BoardContentState extends State<BoardContent> { scrollController: scrollController, dataController: context.read<BoardBloc>().boardController, headerBuilder: _buildHeader, - footBuilder: _buildFooter, + footerBuilder: _buildFooter, cardBuilder: (_, column, columnItem) => _buildCard( context, column, diff --git a/frontend/app_flowy/packages/appflowy_board/README.md b/frontend/app_flowy/packages/appflowy_board/README.md index 38561d13e3..8da1064699 100644 --- a/frontend/app_flowy/packages/appflowy_board/README.md +++ b/frontend/app_flowy/packages/appflowy_board/README.md @@ -1,87 +1,71 @@ # appflowy_board -The **appflowy_board** is a package that is used in [AppFlowy](https://github.com/AppFlowy-IO/AppFlowy). For the moment, this package is iterated very fast. +<h1 align="center"><b>AppFlowy Board</b></h1> +<p align="center">A customizable and draggable Kanban Board widget for Flutter</p> -**appflowy_board** will be a standard git repository when it becomes stable. -## Getting Started +<p align="center"> + <a href="https://discord.gg/ZCCYN4Anzq"><b>Discord</b></a> • + <a href="https://twitter.com/appflowy"><b>Twitter</b></a> +</p> -<p> -<img src="https://github.com/AppFlowy-IO/AppFlowy/blob/main/frontend/app_flowy/packages/appflowy_board/example/gifs/appflowy_board_video_2.gif?raw=true" width="680" title="AppFlowyBoard"> +<p align="center"> <img src="https://github.com/AppFlowy-IO/AppFlowy/blob/main/frontend/app_flowy/packages/appflowy_board/example/gifs/appflowy_board_video_1.gif?raw=true" width="680" title="AppFlowyBoard"> </p> +## Intro + +appflowy_board is a customizable and draggable Kanban Board widget for Flutter. +You can use it to create a Kanban Board tool like those in Trello. + +Check out [AppFlowy](https://github.com/AppFlowy-IO/AppFlowy) to see how appflowy_board is used to build a BoardView database. +<p align="center"> +<img src="https://github.com/AppFlowy-IO/AppFlowy/blob/main/frontend/app_flowy/packages/appflowy_board/example/gifs/appflowy_board_video_2.gif?raw=true" width="680" title="AppFlowyBoard"> +</p> + + +## Getting Started +Add the AppFlowy Board [Flutter package](https://docs.flutter.dev/development/packages-and-plugins/using-packages) to your environment. + +With Flutter: ```dart -@override - void initState() { - final column1 = BoardColumnData(id: "To Do", items: [ - TextItem("Card 1"), - TextItem("Card 2"), - TextItem("Card 3"), - TextItem("Card 4"), - ]); - final column2 = BoardColumnData(id: "In Progress", items: [ - TextItem("Card 5"), - TextItem("Card 6"), - ]); +flutter pub add appflowy_board +``` - final column3 = BoardColumnData(id: "Done", items: []); +This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get): +```dart +dependencies: + appflowy_board: ^0.0.6 +``` + +Import the package in your Dart file: +```dart +import 'package:appflowy_board/appflowy_board.dart'; +``` + +## Usage Example +To quickly grasp how it can be used, look at the /example/lib folder. +First, run main.dart to play with the demo. + +Second, let's delve into multi_board_list_example.dart to understand a few key components: +* A Board widget is created via instantiating an AFBoard() object. +* In the AFBoard() object, you can find: + * AFBoardDataController, which is defined in board_data.dart, is feeded with prepopulated mock data. It also contains callback functions to materialize future user data. + * Three builders: AppFlowyColumnHeader, AppFlowyColumnFooter, AppFlowyColumnItemCard. See below image for what they are used for. +<p> +<img src="https://github.com/AppFlowy-IO/AppFlowy/blob/main/frontend/app_flowy/packages/appflowy_board/example/gifs/appflowy_board_builders.jpg?raw=true" width="100%" title="AppFlowyBoard"> +</p> + +## Glossary +Please refer to the API documentation. + +## Contributing +Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are greatly appreciated. + +Please look at [CONTRIBUTING.md](https://appflowy.gitbook.io/docs/essential-documentation/contribute-to-appflowy/contributing-to-appflowy) for details. + +## License +Distributed under the AGPLv3 License. See [LICENSE](https://github.com/AppFlowy-IO/AppFlowy-Docs/blob/main/LICENSE) for more information. - boardDataController.addColumn(column1); - boardDataController.addColumn(column2); - boardDataController.addColumn(column3); - super.initState(); - } - @override - Widget build(BuildContext context) { - final config = BoardConfig( - columnBackgroundColor: HexColor.fromHex('#F7F8FC'), - ); - return Container( - color: Colors.white, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 30, horizontal: 20), - child: Board( - dataController: boardDataController, - footBuilder: (context, columnData) { - return AppFlowyColumnFooter( - icon: const Icon(Icons.add, size: 20), - title: const Text('New'), - height: 50, - margin: config.columnItemPadding, - ); - }, - headerBuilder: (context, columnData) { - return AppFlowyColumnHeader( - icon: const Icon(Icons.lightbulb_circle), - title: Text(columnData.id), - addIcon: const Icon(Icons.add, size: 20), - moreIcon: const Icon(Icons.more_horiz, size: 20), - height: 50, - margin: config.columnItemPadding, - ); - }, - cardBuilder: (context, item) { - final textItem = item as TextItem; - return AppFlowyColumnItemCard( - key: ObjectKey(item), - child: Align( - alignment: Alignment.centerLeft, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 20), - child: Text(textItem.s), - ), - ), - ); - }, - columnConstraints: const BoxConstraints.tightFor(width: 240), - config: BoardConfig( - columnBackgroundColor: HexColor.fromHex('#F7F8FC'), - ), - ), - ), - ); - } -``` \ No newline at end of file diff --git a/frontend/app_flowy/packages/appflowy_board/example/gifs/appflowy_board_builders.jpg b/frontend/app_flowy/packages/appflowy_board/example/gifs/appflowy_board_builders.jpg new file mode 100644 index 0000000000000000000000000000000000000000..76a9fb67f83d4d468841178c2a1bc0aca1f128b2 GIT binary patch literal 88814 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�is#5)6Dh(nsan>XKe0$(l<P<{PUx7w({ zsx<sM{bo_(gFtkJ#r)A-WA(Rl`2Mt>dd08byT%INLGR@GMy5BCY;%Vo%{ySu17(J~ z*o$Fr*a<S3P=Ehvc2Q}y0h3zy{BD=nC!#ItB?2c7@05n#giQv3<`}!pe)hXhiNhP7 zp&7_2>fur*(Xyq_*WbRhRg<Fgp!nI2unIaX!3Tg^Qy|eqPE{BzHrm>Rt1K~vHoQH^ zmLBQ-l24zmbcVV+w?$X%{%5ZSt3LD`+k$P!d_WrUiOg5?OFvXhx~|Mv%+`#&94Bxa z50*w7XXW(dTUNrCBcJn{e(DnRdx33_lnFNgbyDHMO9pDN5AdR--a9-mmyA6(KV28x zwzktW5c=9)c3EGpq|=MFcPC{}mUUrm*#<HH2vOa8bUTtdcd0FD;o()jYNtC+)}K3Q zYG1B62|4)+mmJ7q`N5f82+YLTWFPj>NSg+O#i=h1nFl4q*PIg$p3F*oi55(pQ_&2$ zsYrL(S4%a`ti+*Eb~8xG_92%Pb@rs0Ppu1Xzi*^-QG!oDw=(g-t8jY)k%_}&G~0q9 z6-WYa@!X<9+dK0sU}_L|{3OTj{Gt+Vei8Ahp*1=_{d|d%x`lZ%t>;nvNOd3Y`2p{6 zqcZze*P@A*G0y4Ug%eM>1T!bP7`ddo#OE1tQ`iY&R3sB=I)}AtAW}50YTN13Y~akz zeu!J`o>Cp3qOADk^V5mzsWM^6E+o#+s-qER61(s-u1R}?*MarkD|_fxN|g@Vvdrh7 zF<fHh%T!;F1upt&NQT8U4|gnSBW)Aa2a|oD59bR`UiI5_Esh9JE5E|mAbhF3z&J-a zC3!?vtv*DnpTuAlrmzkA0h-IWI=oS+7ZWa2vP4TITAFc#*F*kV0&C`ifm6=L-7zrA zQojRJi+7RV$iTrI7~F>H$9_PzUgO0HBE&=zZ&54jL(2*|Gb8Ndsi`#9pu4{3&BfmX zLI7p)z2dXX$*{iMiXsB%hsfUCYFHa=(mAvh??d98P?*E3;h|e|&Uml+Pn|EZaaw&D z6Qoj=u_vix8Ge!Qc+!xmw_>V5;5*5e%dbYUCbAm`HCf&+26CmajqQ?Zsgq&$kTeKQ zgFyiSj2uzK&>k4Zx#ZU5(yZ-u{wv=GrzhLBj8kUy>=fRKuoxnzAyZ3>A<^2Lq)sS% zgYn|j$8D#N-xRjwE%lqTe(dtb&_557n7Oy~s!NLsell_nfi~KSovTSo@M=008URmk zo>hwCo6p)+r)lFgVm+!9cU*E5WleRq`udH6jtZiJgOTXN-s6&Od=TzCk0)!N*iL?3 z6h1Y2E%R0N$7JPay{n#a0(uEgNoHX>@%OCDbgW<8(#h0faf<Ia>z{w+g%m9Lipdbk zMj?1`@6Mg?X9LTtxIMVm15RqF%Dzcg3u=mi6WCfwmm!M=#MlD`qMGff8bPoUeCarS z5k`#OLhT!mtt)IP;AxY_Yv&<x+H|?xh!)VjF|fQ2h``Q$AU^KG>s49ewNTn#Q{kl( zvnnrcI7y}xI4iM~kPm7P!;KbvexU{M?+_lw><S{)wjeil!=In5^~u%VTGRN_S)d=* zmKiLX79ybPy*d159baMBtuTA1RGzr0_AONFSg0}m3go_#u|)xP^AM7OK>NiK;&U0H zn>*HEaYWwp$Y2Py451?iz?WtB9+MbBoI||}sl}!&m?->)Di`}>BpcF0u!t!F_~y}j zT|WFp!83;u{uBMTt=5Gb9Q5jSUTm4x9ehS+_3d4j508~=HhZ}-7*scs^J4GH*=pCI ziBCbA=<z#GJHQ0_ZpBLN%e10BFeRpb3|jCr(zo1$xoe;Szs@JogGVqYDTuESn~gt7 zFLF?q5zcU&)D2=P8E}U@f>%iqBq38MbdFIVh?+?Kc!de=p+IlHRbn1o#b*W|m(viK zj|E>Niwb6)P{ZEGJ-`fuc8wr$)=42gfFX#OzLgWyW9QU|zu0t3rS^y99lX`pn$XoW zE1TSkQiP{|g5!@Kn+wxD76qR^NqyV%t&0?8W$lB+nEP1dh18l?fm7#$f2VLS90q1$ zrr^sN17j`F53GdOQf;|dN=(RZ9dfjPGHKqISOC5-l0R=V0xd@@h*$BTymli8mKR2T zhTUD7`RSLqd4QzHQQ+AXKKGBOTqQn1EJ%`uN#8z<Y&}6@6~tVz$n3p{+^^w>k*+Yc z)y}Iq+Me=XeFH1_=8Lb4L{>7URpPjA&un`gIgkv3!sTt8gs(Nu-CBxJYnmI4{hW*T zffKS~LH%xdWs?EJZO@<eO$;FOcSx;ESQZ?tgrEr8Q!a@ExovNA0IMQC-qLo!-2~4s z*I+NgY-T<Nb*OjYCmX&h_bTGHHn|_S^kltQ3EUbPN!L%o4-BM!dnqJwHi-KTxRQd4 zs6*?@%ST|SE#!2#3VN>!G+pfJq^X^S{-h?kPIdH|M#rM54V4Sl&}Xipix|^;ZOY9u zil3$Ahg9)m#bBV*;57%Cdu&UZ%SC5w;T2)HX9(A|;2tU`G5~?Q@olam-a%)6B8yNS zI}-{EtlLTGdRX)vulyS~Ggy_4!<3?NiRL}VIMJK*-wj=ESj1lamQ>S<DWqe_bjipt z>Sk-bka*?Va2|1Ua^1dtV#Z!OVb?X)@?C73xkulye<Ie?7(QmcH}tFKAcbhw)UtSG zgDYw^E}mPjf5;Hv6+~O?aMJN9t(7ICjwLml&bl3R>pnV{#NdNY!N!guQO<n<W*E9Q zR@(LY7nPGf^;r{+59tdjZ`EE85zLUO=$?>T=?k4kpf4a3ZA}tpDbc9TZT<chmC7IG zJSp$*LNtz1I>=oy)XJgQ--ef<$`!Wg&{kMMbn5IgH2&*|>dWh{*tGIHyxW`)*9D>} zoZ^hcMY_6rRM?KWX1ivD-q_SxvnW4xN1x#mV?V-ewc2uUV3efi_#q|e2B}0Kc;=HD z8StLkbKM)jGe>^)M>IQVpA4N^qT3Ewiny|B!eZ>mlkF&TalyNxB~NivwAJT$-|MT& zZOPVb#^8cRRYkT#%44^j&~vR>kmhv-#DdzWDurx&2m%{xudKezvdsgS4ra8Rp}kbY zN^SLht-T*7N|Qv8H{OXS0~J<npe4pApX*F4cTyc~P#Lq9Y6%d?a(2{BZ!L~bw( zyOQX|7GRv=r|X6YCYV!Xzzj1(+~-w0F5XnBrsK6>LlLDJTxTiRQ=*lmnLBfjGAiI8 zFtlIhiIaga^H<ZBvTGkOMG`#)S?Mw>elRP~+6t`+7wgyy*Bpe;X5oJr%{iox63>Qc zJs>ga%%5`wkIxMt_Z<`l^WATvSF;!5eqaVfJ`T1~^b@N?$4E5r3OM>oe?kryZUQe5 z4#lv>crVnYUm1A6UwT3P(G1OYf)VrE4GrDY=~qv#K7T^5x|zdH%+)$kNb1c}*lJuF zNovbF^>U#H$}-FEq*%IaS+h<NaX+I2erq9Cf<<ux7+Xwx4LU#xOziyzr@QY+eQ<$l zIK8{T1IKMbre3!}ZvD{p>GP6QTHbo3YK&6v?u(&QIoA{0@ciZ_XP>Vwoz%aYJ#xAl zRyJ$!9*ui(F6dDKOW@-3)Is;M`tRWbV6mJb+!dW0Pm;v(&UeQCSg1##tE;zH-;}eh zGAQ(lN>3-%<4>HjDjPL35N(8zSXj|K<!UiaoZ+x2$%wNPK2<&G!W=ef{5Ox^tM61G z?m3Jt!Eqw2tzL+Q(Ps<&EI87}P@C^ef7pA^+WOGObW`_K%Dj$KqPtEP-hbwOmQgp| zNd>8f^EXk2pC-O);+PBP#Z}%=J;!SrE%wac>|!X|%zE5ESq$hl__oZ!iadU*Te&tx zt|`AnNacOekB>FQHX7plqr1;7ri>(HRZ>?nUR-Fu>-M((Ma?KakdQL;jgW)`Jr4Cl z&^+A;{y49E0{Aur5&8D}(M<ic&gnbZLas6Tg34tHjP*|$KZxAZ0}(YXSoH<bcv17t zl<q~GD|)U?prEr|o^jvAz3_c)$W;SBY7-T@RQX<r45hv8)v5%c<Fftz1UBSiQIrDm zbng8*;RAcB*4jay<e@KF8fW&2YZE{6p4{s6kITJ8q#=UL%x6Gop*NXIa`~<2V2en; z*|r<$)(wd_+4Vc+YS2-$rjcrCApr^ARHEg*dt0X!ad8Hkr8Y*YjP92d;D?7NWlyIi zU;1h<XU{Bbpf2_Bq1-`A<<z0sC=pqHn2XjWS<+x?iN4^=%Y*cuN4|s8rQ=rz?sFgB zp9d=pH_$!=E+#u-J!(M_55x8bNMWV0k^KsN_eJ_ABfo_>-P$=2E7No)<+!6TN4ZJ6 z$fPB?A*Ltq9;WexcXOR=u~l`~(=0V>{8Xx`GTe<mknYHPTN>7dG$=pZ1O58{)?)TL z>EDalYkU2Nf{v)|IpWFOx#(+60mAte2OZF}wbz0aaJz`s!?C@|Y5Xn!s;he5x4>FA z@b`rv4}rDlC*b=A$6+|c*~Y)e4_$wtVM_+IQ8?*2LN2@=j^{%{_rY4S&ZYm_OYCWe z9P%%sHs?swNlzE&=D~2p`yJs17H(jNO0Z}+_7qsB`L2pB2)6=lZkY_EaN#V#8uNB8 z>48HZUY|4oVL-S*qQG;G&YdR$dG5yr(JOTy>oV&?#J3Ie?4owNm&M&QZi!f}*noBY z1PuIO7ja~+okT}qL@dCcZI85uJ^)YoVGJ`7vF#3`L#Mm?kK4~oo1&Jtjv=z)1THv< zg^pN>@Pcr892daB-p>*gae)%fRfGFseT~Fkv3p~+e(YkE%xUpwtj}v2`m5zWjig63 zi=yTW;niwU`M5@O?!uMwlJApG)f6|>DzA?-RY!)8CvJVK>YmYkbz>$DDY|Bnw4X^V zggPSbfhpl!JV6ix>#&`Di(KGwKaCym_G)jKdh<kgl{dz#HSk7wLW3&`)=)xtv`+@O zNpn#2Wk@22+ZnZ4X`I0#UOA=bmV>d*Sx9|9d#{>ejk;Wg{`gKjX#q7K4?#Ob@M8Bq z)ey6w?it&G(8pKX#m1+n6jGg#dW`7-|AW2vj%#vD+eT4TI;iv-l`1HxfJl*Gp^FI8 zTf_na0xHr35(McT1XP45MU)mgQY3T`L<H%@P^Cjc34tV!XE}TJo|#?dn{VFpmUG_y zM}B)t;3=!z<+`u?T6{ORO||?>np8E7WrX$_C@p?F6W;4!yU_XWYE}3=e|7c4_m?ux z?|+ibnOCu1M3pja#2x?<p-do(9bj)VY{<c3C@ZgV62&4~Woa?exqI+Tx%8E`C|Vuu zbykn|_jEes@bEk?_<_eyvV&71;_rDZa#w99m_$m7<VX$Sz6FF!6v+O%k>E#D0qr<y z#0_GY$EOfi-{DKK9GiX!U42GsNt^Z2i3<|%*F0Bc)n)WWJ;qtH6(`@LiPxwJ5F^wE zXG2X`QMkT5?;kc-S*(6L(pg=d{L(wKZd7eTZI{slTCv)=^!GZ?zAAZ1-0I1fu@y9u z$y)h3=I*@UmM6+-JYZn8iUAKK)uFUIT;k>Le{OVec&+nDKfF1KR@=*A@XtRsUr<w< zaM+BIBN0T1u(zb|G?MzZ8SAqU6BtT~F%tNx%?=8BaXfb$RIYy0oB<WW$BSTVQMfSd z=4v)TVjG6B2>{p>#!@mPCX%VXj<6lqN(`<>33LpJ%YUbNhug6eZ?Q+Ooo+;pV0W&< zv5Y^LjWEW7x{wMQ54lr_6{<ro@;MAFp9OK4hJv1eQq2a$kmh(fmO!2ge)C*2n4;+7 zB5mhl{XyAKBKc5FaU}yEt9dBLuKx*JDC>S6D)(!0uEWEz$cJeWZk#iv8As(<MWRAV zjvj*cl}i0z?v)HUDCLrr_0M(J9x%U+E1PXx)-)qzWt3iBP!%|#?%+*Ndo!P-=V<uV zHup=fgzqo26tUXgM}P1AuQQ1hKM=nUpvqO>Pr-)S8n8tQA+D$AgjkCrtovSFfGSL| ztcMS>?btd6Uq%z~Lv*{bh5Em$$$h6$fsxL*d@(Nr>#WY#+Sy6u<Ib_z@)OMB+9eDz zFL_kQ2WC{xeWwvtF{S#Vp(kCL^(aszG@bSdq41-~bQD!;G2B@fC*XUnEaob{tAWf@ zmh|Pdfr!HM$35O2aUUb;Z(qraFW#_=GJa%Yi<pNCI2=4?AYlw7Jg(6wGk&Kzg(WGp zLX2$Vut5c4R~l|geR95*ffRJDO8ON_{J@}9W`M5cGyU6%6|INoBBa;u24lO6;12pj zJ?gA!aQuiAbAY_1Aw8VfQ}Z<Sl<E=iBDKhRH0yU7SHxg-vkmjW(j<a|^sr&p4FBo% z0bjBu+3aJ4&>F-3QRlVSHbItm`TMlK(@1sEkk(t^Ounv}zFH)oH=UVIRV<?lJATX6 z&PiXt?PKGAyJ<qR-iqObQeb#fEJ-qZX`L(zwIC%?bY2xY11fHlW2%c9_?6Ub>t;`x zVd)zu%RWZ1pKi-fq_o)8*)jUh1fi1qj<SlAnW1J>smn96HSz;QL(MFG*F@XABVV?G z$BYHf6?8leei%Ays^;_P`3~}jaRt<>Wi-ict05sb-l(>k=sSBiglw_S+f}=SDv9nR z-!r-Z7eS4`!s0bBL)j3cF@gI$=^3HAsaCpMrUw2x_0(4xHk;v*^8{WC>z?DyS@klN zZEd%BS_K!k%?7~rBM~Kbi6F=*?1qo0ltWU4IQ;3XiID!RTBk+CvhCgSFP&w#xUSQr zWEceQI@6{24g1*yo5aQgn#TImfY~qFtxr8%o!Negx`!GBACH0;%TNyDCSBf>b$k^F z=<tG>l*TO!V=^h*pg2LlqR8Q8RnqnACFK%r&dIY5ryn1W`y4IP48z|Wpd7Sp_$r%R zzvin}jnxu4sr|;>fxBdA-_-!){^LC|85()nyn7-|fsd^J0^_CF;~OD{M%(c&daDl( z+|f10w=x!7c_nRH&PYoS9}7^W6spH{KRF@1533Ki$AHfKrK@;*qID14t{cDDIQ;2} z++hBu_G;eomF(UEMkI04`<a5aaFLx!+aNtdnX%g51-~G?)0AlgYM6WCQwqUzJQ{01 z>B{0bXY=JsVXQX$@|jy_`}!W4YV3-8#Jp>dY4*SxpdRem{RPw)?OaC|iG@^uz<ZP* z2vCvA7rdYmV9uOAsO~2EuH(U}4=$fYT65)uU$dahIX=shocrM8XaoalJPU!pOSgO& ze|0MQ{iGnj_UDZ@_Y_Y&wfFS;v#A(8e<}V@X?DAzr?LA59(_8+HiY6vjgPK|;!0c} zyC5b00$wY5O{o#e!(%+1UoQ*?cw2;>dsin`8r^rpzJ(^~=nc1^yE(e75kwG_Cnkd) zH8hzM1XmY&q;|5hY|D|yQT6-5V+aA@D<Blv`8?g%zFb!(49n)4tVIxU;2vt4Y2=ai zMQ`8i*%K#niAFSx7-Y!d)3pq|&|XjKBdjeOhqyodHjH0HNYtzUM#g;k8t$tme`Kys zVeT>IV8$?Wrrq>=+z{a)Eq6xffsRE@vWM4V{UN=H#{`x27F*tEz2T&65e=1lrRi-~ zP!GbV;o~Eyq4kOEWjAMnC&VaA)}H4Ae8xuwq=qT|a6c1~8eQS(M(<;>niBW;m9285 zHqEB!gG}#UJBCt&7sXIG#fd`HT8#7td<fH0<oK}fv4&67ku}~suKkD9P92lY;T7KF z-*v#<Q0+Pc=MD>^HH5;|IPTdPGpI=<QJ;bwUVkW1yA+SC&Lw4#6|Bm8Zs?l1_9!~9 zT{^j!|8;;<s5B3A)hXJx4^ZRWegk!mrk}%P4g&;8PViw6kQEs~yR69vx>HN4mwj%M zxd$m9XHh#?;r1z)hraK|nrV<r28d`h{tBE0`fO3r9CdoO!DG1WTL@9B`RNx!^8?bf zg5<~MO^?R|G<3$9$ESL+4ykm+_!??_jkv9&Oo6*s65H}z-qzU;nItvb%*+d^8y{Ql z<4%qrq=8?)jroxzp?CC~JB24$#<0yitB1lc+F#PK%;IJqQ8+0|dr(QKfTwT(1G(6r zQ6e3DF#brT+o%fX)us`0Ou)lTuCM&JLY9iKL!P^Z5wfax?oeu{KJ46669S0yUNsR^ z1MPPj1UHWCipbrhtU-Olh-GsSqBMZ7SvdU!zimsyOMEk2*WRsBK5k2-A}_ggT~;0d zMhG7X7A}DG<`kSj)B>m2#dh^oNp(O>Bfh#Nq+3}d6x(MmDq6YXBRBO@%%1VvsT985 zIvq_zS?oO;`p+bAB>xs<Wtf)YNL2mSZ9^Xx7#jYs@P0NQf%p2?k}o86Vt^8-h@rLs zMD?Vqtjc$qxN?4I4CIT?sH9)wtcPzF5Co9Mgsfv`%PNT<XKts5kA9RQiG%hIK>xhs z=hFf&g$1FmyRv?D{;VQ+gR46Cd<fMgA1QEN1h&yIz?=%7nkZ)7i}K-wtg-mJW3NlV zknQy+RSoxRKie=reCHa^<peu+nA!=y`mEz2?4w}s0BQhyVrw~$>}W%pn%bN95FL!< z9&FAlPdycOxTaSup`$eV=CupwgKj1!XUI9`F_r+=tWBEIin|_mKF1$OU+o$+-3Csg zz7~$Zf}(-yvE{y?L6=X4TMw__B(=BOooV|_bi<6uL5tCLpNsjmifk9nJrf?wLs4O5 z9ptBYi3bFLL=BnUJdiUS3nGg0Cl~Mk+umAY2XOBZBq<z)^D5Ds8vmW<2AmTfSYw&x zl%_~<LAz&od0zffFj<|v%zg6d@Hh3al$$2)&<)vZvU}C;H()y!o`kS}@B1%|)n1_L z2hc@a*er@BK6`m){JpAqX_Uz)I4ddMEcw2rjb;3uZwUgARPKCUflHi7$A6Ng0tB{R z5kB3l%844Rwg)7!fo>F!>VDtTBr?9$k|a++X@^+j2SNcK^GeY>x|>DtcZU<7__r-w z0NBnsnt>TbFiJr`2W}bl@X@P)W=4r2#A4>x$2CC6x90_EbK=GuiV!gLNMCI*K}54~ zoIwqqh$&b&(R%;NL4gsgs#h*%N9$Y9g*SoUibd6z!^-TJrY7I(gNiDIb|WZHY(%b~ zb8-rkAEDpbzCS|4(q<D*oJjpn6E3$(N*G7Oki*!b+M+y?XPrfIq$;l?-_rTkt7T<Q zonlT;td_N}c;9LEJUtrK@otJ&kw8@1I_nBn7Acv`3^Jo;C9za8m#;vdDjVtsQ-|Nf zF5^DwvZDpko-ftZdwWX71Pk&q97HR5ISH!`@E}!X(k$VE8c7k~P`?S^@AZ6#MX6!< z<i~_h^`_z^Bl}~mL08Q1EPlr1)2RAWC|B5UmrX@?Eu-&=0axc)ca7eHWt$GY#{xG$ zc-}pD;L)eg&28iyeNO5um<s{;crcm`gh>4vQ!>q#9j;uyR@ibUZ<&{S1|3dxY)uq@ z%~|@SYA!8k{0wPqWH`}Oz%od^PmKfRk4|=?0rhDO1Nn>>Qaz1r-(30g8=rCut@IDF zDc$jq)v-B$)}l5nJ-m1~HU+^+VI+;Y0VGkHWIxr34J`Yh!s0p}c3hNjaDF}Ys5DDz zySJVPdmCHj6K9!f5oR|;k1@xtZChXkqNx65aDclk>E=@Q<Aa*GY9)JCuMPOZML&!% zA%>;06NTBTN-i8L+BcV1AA0y6Xd(t1eY^;cH<kf7%XnwV(mdd3tAVJkR*dA)b5@My zdjHo&GW?2{=}pgrOg@Tnp;umC*wZPH#}gbXAhTJmPFo3zQ>c2TAHD@!D!w#iOOPQ} zQcKR)6A)82Z>ia+>x^8*{vVDlfAhYXuB-B;q{dOFC5#V0@&u(OXP_1{x!3|bfO6I! zl!BN%3F}0#P{?wm#mRvX3gQM=jQ#1jY=rvlh3-KeyXXZe{x>s>{WJ6iy=nL9F(^_I zc<CR@cM=*_Wv?O~B_Bn$Ki{e!>AWY@Gu31xl5{((EZ|&7JcEpLDe2W+hQ>*ITB^nB zu;ylzx)kf|42p6PG{EG(0@Ot8t$KX=&W@m4^VdUFO4(uV5*O&js&3I2HVEunvprX) zOJg0O$b@x-4*H7XyOy`X{@F!MM;`WqPoV3^ZcN$N$p|iJ8ueH<hKVFAxy7INPP?SQ z8n4NEIPsQxv~}qGk<@lnag{`&6$Iaa1;Z;qh_OiACfO3m#AuT7TVEcMF#i5JS*Ppr z0B6tol}hs)sfiaq*B^U5GydqNEnhQ-M7?z;98TP&&QS+k-k!fj7=Huhe*PKK46N8o z51em+!0s_SS+d6oBR#o=O5|4^-RAkUtL3iKrc$^B4+vD~|Nr1UZvyX$fnPt4(j~=A z=#O;NKlCke;LCr36eNfgNl%y8REP|9iYe~)8hSN~yke_9@xq>*f;mm9#|KVSwBmx5 zaP^pool8oq!8PiR#D_sqb;_b))OzN;%zc`Mygd61>GvgExb&Tdmk~m%?r}okwYy?$ zxRhe*S4rZtO=uWEw?p$c`McO0cUCf(Jw<mN?kwxb;hG6}A@G1FPoH#@j>2zCHbH(; zmFlH<l40{(aQ0f5R+k)C`MU(`!Wc~Q@j{x>pgD{SFOHOP889nU83<d;xn%xDzOE48 zy>Go5Cz4(jG#ztSwlt~fv8z=7)SF0`^wHGKxoxyM`eeeh!Tc^uDi#uj8dc$h%VF8b zW;C6=ejskJVGU<f(&w(5uj3SvI=BCTyIej)h*Pe_u(peaI?{Jw3|9j5G{lJnYTo9e zAOCoEBZ~*|tEZ8(&Pv}>>EOAROUa(Q4)X{sGh4+}WfBkr3QJu8&rd~udg$3;d4O!` z)o9*Jag~*;K;xsVG@W19@i1TDO&gm|c40VG7~_E)Zxt=zF8bd8U*~Oq7c53N0t9&L zHvn*~fjZ}OD%et2{?k7{ho8oL?_UG7dI1S<PZ+#S0fr=?3DvHE6azh-wSc6a&EyDy zD))FaP)v!J2}W!Z-k|1dzhU#(NXIlO8J?4_RG<0pG&^_8F(8nH=vP7M{s;-+RS3Qi zC}qWIYn2r{9oC6jQGlL>5uVLbq6%@uRRG?sHvAHN=JshCey52Mr@&KDXY;$t?WNQ? zOupIv-7wI7ANV(BtcxTuE=*h`<<PfMj7(8GoXDy17%=GYP4JX1nr-)7@khVYL~OuI zfQ$b}f;ia_8?GOK##^F58DF!(m5m|*)FOSQ++LGh+LieoT$T%lJ);l%TV)L&b+wWZ z>RW&l_J=Ho0TrW)Kop*f3~B>T3G(=CI3@~X^Wt#F(GM8!J0rIT*qXc~3?GY(_2{|^ zlNr;oF$hxV1tJJ%URl;-Nlbo#R#1v+Iu9!sr!szJ0m_J$tB_s+8|kr%IwcnTv%6xa z^U(@4^)LTz26UM107?XomxC>yrmH5$EJ=n#XHk=f*+$r*AXkufk00OqP9xS0geK^b zgXj$@F9krmbH#3-j``)TpHX>(LaQtA!W=2m8XSKYwH${|Zh)9+q4zS_ZKVY02rIQ1 zHD`*qhj!smYwj=ty;sEcVIUOr@;i-r;aS`$%C^^(Fa_>Gha7@$NRecLxZ*1K(-=~y zGV#L?k;Zy7iSEbi_oA8(Ps&V(K>Grzc6D2*d(;pEL~9J?q97Sq*3X!6pcPmP`U)1{ zlZRGE#DVJDKGe7%cm;8T?=<^ELb35M5+B=U3hfxRl_;o=5&|>h`2!9(j}3(5S%1vT z8=wyrh`oc};iT{ap-4|0B~_DhUg!v<g&MyN@4v(5xdj_^=cIUjr)fHeE7zwoLd;<7 zl~<w5Ku_@hQ?L4V3S!+)ExNbvWm%sI_H@=C3=}sQDC}>R@NF<qkKuRpcQ|YK!9Z2u zC@H@kC}U2_LoiUkTC-rFz<uF!CU{q9H&_$SzqclgObLs>x9)ytOZ;k01c5byp<Fyn zzK;5wKaQj9{jns5;lH=6f4wF+|9sUyXZ7!Q`hU*?HeObM;8zKdF$JVozurA>uYn*k z2z$rL`{IWQ#J+=Po&REI@gbr4c))j{JqMLs^TED@0#W};`Awgig#vv4(ye7(yrwmY zX$r^YWz(TW*nK{k-GMmZ02I43Qa$97?;q;W*wrp1i+jEfx;YEQAx3q4+b;A*l;(FD zy)F42S5|z#g2=?NE~Xgm%Lcik_I`pQSh+4*!I-RwkA<5O@7cqF0mqJ`aefB9*ePJv z*=9|1eyPesR+i{jC5fPZ>2?BCQGb?aqy3@O{71EWN*NXOD#YT7(aQ*e((EMg*P{Io z1n*O?*nG&ay1?OiIefoLm>fF|S#8hj(J2x31jJh-)1oB1!H-oH6?J_NW}U26Q_&CZ zUq8{C+Ztbb)y95;j&cD<28r6|FQDOx5oB%ClJPPmg<PWAv!;8b>=yMH>e)dM?{FfQ z`+lCJRqTGRaNKGM1>QCVHP?No>B>azM>Q#<i%qG_Urzz=Pz{4F0B1W&jmOT}5t5<3 zKQ1ANIwxm>A{;mq0|nZXP;rPsFWB}*%<RZ-ei24som~Q;)&+$~EMB_xJ5B834nsO{ zk1xRBpZjrNcp)CXJ`d}vpj@aVi=sYvf~RNt@$^dYNx`lmDJa;L7d37j^PT4Kzy`ZI z?3cU7qHM=evpsOMD=EZ{r~{mBN(+L-4KSUy2fuj*%$!&_eDcUW7<FG4{yWVfsPQi^ zi&Dk?<^kt`ckqPY#O(0afPn9j2b>au_^ii)YPvWH-(f*dK<B6-0JB7EL4Md@KOqP= zfHKwx1Q5pR0KI-osBKp4%jXi{ieXKt4SskdC6LTU3pg5a6!LGL@pp$Ac>e?Jp#Zf# zjSAVOBMQ+$e!|-Ty!rJbYW`;Upn%)JTZtb4E8-4VvTJNhDr}S;&GqHqK4Z*pM$nzQ zr(2V9@v<-g#fBi*H0;kA{%H;VIm3VM;s23)_?ZlazmY6_K3H2tf9g;Mlm?6sBlczc z&pedp1h&vsi2N55#Quv3Ityyct5g=%AErJR2Au~sXq6g+nsfMJ>H)w7yv1s_1*%DH zQsytV?;_xZ{l)6i!zY2S8Qg+`(ZD<#|1i(SDf%RZY@*V;cV>uYn^QEiSz|HTRIh=R zvY+`l+N4KKCf{lNfDu(Iw)$a>CNPu>7&1s#J`YUk|77?1c2<etu>y3V4p@<AlQ=0K zPu=u$ZQ9KpFuZE<F8s#r_^4uUDdrPz?5*C~F^_1|_-nzl<xKa<MwN_O^V$-4KC~Y+ zaU>Uf8dFX&Y`^>9TQ;M%eBtkvhmtMRA`~qo(ko;c<0aDU544<W$;cgcr69AXC~wdD z!%JLIl{+zn4D#tMGWrdu#VowZ+aB_LGlTk_Z!%ItB;h~wSpUfB|K&cv;1X|scEUz! zjhe;)8rU23f1)-0N522P$i?aIUonZ_hfNmVq=(lP<|rjoyx|j+h0R|sq36Q<Q%&s; zDu05;90q1wKX6jiUz+KE36=hl!1%v%JpKux^MB=d{1Z0UzcwDGYb9@>K^)$&izJ0w zww%HMI7H8tYtZ)yNfzHunYYM}7BA5q=`<ne9_3m*a{9ulB(15FiJ=T4Pk0YX(m-$D zV5U1JTbJ2DnZq8K9bvDIMl3rh6T1poLf!CP@?_G3RQ)TKB1L_r-rq(qzC4mV!;#2# ztVqkK^=WF{$WA0dycr$zm6_y$<5qE)Jg%UQ&-3X1s)~6trs;4qY0ckI^SOzU@wLXi zp^Iulf$F{=@K=ew)L4{*pbLtbvWtYCaOHJO%IVPs+?w(MPboeD?vt9rsvqOlmq%7@ zf~F8LC|V@HSE~-mo50>;Ar^>PjP1IY?NAl`YH!)t=;<6A`{1L3HtJy<CB*?8yD!tc zWHV2%Q5eTAE0C3;?*5(kvp{=nE={iDv%RJBd1tO2sk!`_yXMAU#cq9|iR*G)$Lxb} zh{CNVcv#?gmp>%q1vevD>h#ZNnJ=0oSZklMe{ip*_~4o}>$Fv2eH+KF&G(!n;6nC| zQ0|gC$ZDwivzu&!z7Gf{`BQ=pDz|cmk3%Ki$2Z#o&g6${h&+21aMbRw+oObVY5)~j z(c2hWApcb|8v%D<3p+73I+wP;$Z7G-oh#aI5_=>w9Y5xn4<%}Rqp=rx)Jfwr!%2DM zs|+Rv)OXFHL#X=8wY%o9{0|<G)bSZpoMZDnHQb_QrNfVhonIVla2k63cuf6Rbi3MB zIwmNO?HLd!8U7C+;PY==^j}Qo-{p({lGXh|$Ngv3^M7S;2*fnOmIKF+-|_N+55uli zl20pbf3>{RqDUUZ%rZeNfcg4Z6B10!MKyr&h2iio9MQwi7K!K*^QG2`xEZ7(X1)NM z*l9_W0mGjRs$F7m$L}-(zRuvEc3z0fq%yqEr2BTCyzA~U>#W57WUrCIvJ%}RG@MU! zRFJkOPef~3c4({7Z61H)Q00;D*sqjd<9VW-A=-#FTOcxkeiBQM9e>B?Nntm!rk(KX zqHtVo{(7ja=Yh3_WhZ;RD(u=+lc}VzVaaeS?MFfjwwg7*E_ap_SJ_{yLc-w1Atr9H zX(I`mfN25sAsLFPFOd7@A3o-*#W=a<h(aZ~DfiU^-D_SANH$5tpE2Os&hET<5vB{? z#0X`J9v)sNz9I3Gkx+vwZ;#5od|!cb^d;AQ3l&G5_LLV(h0au*sxgpiDL&Knv>>nO zRLDrVKb#ijgo{QO=_N1Ik`*b}!I?Vtt9-u91o03GDzjd!;tk2m%SWeo$?mSu$OBVW z@x;2ueQGvbv9drj><t`m$u>0GfG+c-Z~`WP7HF&SzUSupl9be85qOo$P3~alt^Kn4 zG;#E|%%?Ph+G9Nw>pAP8VQdMS!nF!9>QCxFMjxz|@RjIMp_{<$_Uh8BMbHf`dYF2C zJ49ZQSI&JV9(^P>{n(XyE<07H_!qlTR{<bA#7+z*<%6N}q&|f^veA%G%g|nur&E6G zq@ez?N4H;8!Fb$Zp-b_3Yb$oNTL!ht<5%d~5THyz=L;$(0k)`m2^G@`)F7(S1kEx{ ziprZ$<A0C77}Mq%?4CGts$JVPjsE82w$2yYQA6?@c__RqXus{ypvGM%3BDw0Fv)Na z&WHDZJrk@EVHIN*Mk|W1GNf-B*Pda%NY~E62$~ofJ;|)|XuJX2U{^DuMDSC(oZkh5 z{a@!5K32!GobPOk<=w}yHX109qA?!b?A?q!Lt^_uXT(N9EcIk|(wB*l;2Iep(WjqX zky2KU{p)!q{Ju#wx@W}g<LA?7%nk|J?#+mKLF0kIJKg{wef<d;^H17@@;q($W<w7) zlf10jXjFds;gbWS=gqA$a|Atw_gxK)OT0#ZHgq2rocx5t4z3f!ejHd~D}@cfmCl0y zo8Tkx1Hxsm;g@BPfoElDQtnZd4sE+gmy;D8*t@Mj4CS5NF3J~hH$Bvh61<3ez#24F zUdLvI15R%xAk?f5O98P!PEVpaH47==<wMr2hqJPRiDKZ&AXeW-wi2H-Au5xMo?Ev< z-WsIUaBBS@u;c%THUB$5|GoN@{5#p7@T;GL-<+slLiQy&cOwJ!Yi7R_KfkWn_fyXb zr;i?}boEkDi3UGk;qBSr=$vqgJf<HkH~2p*P5vvU?ElF4|93;<f7?L+2gBljQuzB{ zIUfH6EB#+N9{(3<WHqlLe^&g3AYRBQtuDGe94MpRqJo4PFw<beCI}prjbw7#FG@P1 z*BG`eC9?{J{18bih5)9BYKTy+e_)dif-(nl6lw!Lp-D_k2DP*c5QjODDSeYuldRJL zYKyP_qMnwA#qPlH>0O1^+;4K=f4#QB-)_g{7clo;-Qbst16j47ZdbCzt-c@`^UJkS zf4!SJ`dXEYff1_orz>at?PkZwd%GY0-m4hc5B~Wk|BZ9<i~IPy$;$QfbCS>1<i8cI z46|@h4l6-}V#xK~n7=k+=E(oz9>fc~`v4o{k?qf&jC~8g;8Q?euUnn7^64)XB#oah zkV)iI%8^JfJobY}^-mB5hS`9Ax~eMoXo=pEY5~bN)A9N=yLtzLI>}d%U3YapV<R?t zjK-Iro|yU^8Avic3aGl)X3$6E(dRuHQNzl&KU@tg*L@ln^7-u{G9P6ZzBPz)VM^~l z8;h4ok1m;<`qk(FbM=y8=cA>U=mxCnl=SJQnKQ#?$BuI<>PTAPavemHt>NtRYtpcc zsK!t49ixvbJGg|M9;52dB}074L{NBtg$2wbESl;FpLmP1kJ;hhSS4?Nr|HKu#*l7d zhhb0<izY-xZO+e=rJ(LpCobZVncB-UsLjP$)Z`%o8x@cM*K|U%p}ZON1}CYB+5{-S zS=2Q6oa-0t>g;zKix`q4VrU8qrYb`aYBQI>1o0{m<G`I2u7W!ws1M;22`C5H4!b*? zya`I@phm#GAN3jgoksKxeDlUJ0Qh5C#Ftko@-j;VU<r_^fO`nv0Vh=%LS3*8g?Zf8 z`gfXqL?{CP0b3eFWvD_^0Sk5!0OQOlWG!HLQr>>2!C|2HFv9+Kfl$zQn(gCi-)T%v zb?q=xt|PyK<LUn4N}9jAQy&TZ$*(W<n|mE#3Wd)h0Fk|y?azn)bEJR06#rZve{RIT z8KOU}$DelkCpY$|bNNT-`(NwE<pGJCZCF)a8e;AB7X*hs{e6sZ4wcJmPu+_=33mn9 z%0EO$8NZI6C}%*}V@U;R$=sD*)an3e=l@mw^n>F#3EKex;dCM`)w}~fCk5!BVPsIK zWYHz7pg!ZWQMfLs%&HKao?w4_r5o46cb8UWyB4QI0Wr|8E}n)i#xAo9@6o4QxS00U z6x=R>K>ODK#E5Ov{;yHfYBEJWd1(wpO=&40YC`>pn#_@%xDCa5>=sC?@)3Z1`)gPA zt5C-t!h$ly27p{YIC4Yzx6m8xAk13?k5o)e!OppTL9MQWFfRuI^?j!)d7Km-Hg#Ms zAzB58%0jY%*h&oQfp3T{ptiuDV9X!lmbwV*jStU$!WNLOUMVXs4tt&EAl%T($6Ufb zM<)heLpA)H7Yp~69aHfAf%GQmB>W}YKFV#9K7r1_S-2i~sHoSaPj~x7J9ChxA@^I_ zO4{&^1;_z(+(>og!eY``(mrWKjhK~0CZ18g8E5cQ@C+qMt)7tzm$Wx?Omw<x@I=&E z#y)EYt%#o4-?7lbF+J?K6J%MGc(}@3<ctA&##jPVVjtj+_-#sgztrgdk;-R%KWB`; z<fokN-I_;O?f!{EimC*)0y{lWW3W*I8U)`tt=%Ak*7by!cx*oiV72Wq{iqQiNYAGk zrVFP^1!KkF*JZYq*qErU@Qox#idz(FI==(8!VRUyZF$pC!cHOp?FNdCnKS%3FZsK~ z2H8VY-TSw?=g&PN|8M`7^A}3F&CdYTe1833H6?zA?0%(skpq&k-3>Jth{@pX9|D1$ zN3N)itELqLp~xPPOt{*d26Au5SD|pn$&WyNeGwImNKiU~Zp~i6%5L{j(WvQak}TBd z0ozfO+Ofra^C9|yuzPj9i_9{Jnw!SV2V)iSG1LxrNPZh;+=6S(g1EPXv^n%;L#KFe z82nu3Hs1L%NQ#9z-$Szt@RpdR5!?zggW{BvUNU=YEhF)FzKHe^c=N|<4}VVYPogIO zJ=6PpYmMr#<ZJkMiTWQlfBrdj|I?@L4?BteoVx$%Q}>6Jh5yc}<D}#*z{|mbTqO`A zj%-3&1nn;Dmd)ib`dVhnDXfyo+o`an9^$%jjbXcbRa;c-$E^4>A8%dXtu4CmVmLNC zCKNtGS7$MZC!$JK>)nWV+}CT$ha%m!iV&Z;DSRk!NNNm4SqgB=9(zJ??8KQqc$w_L zGTg3YD8^N|o#Lh@qzpvGLD4P%1OxR4YHm&7!a#?-Ld<c%M?m{Or&5$IXTLcfZ?-V3 zvhH%^$^qTAaGj7OvrNewA1}8pQ#HI2Cc7F|_xUQjMx@CRxKh$dCd#*0VWzz<8KuR# z-fBmMJEbHg3vC0M+zY;3`SM`dqA$<g(rd-`4!gh`!OM!gdk!=s51{Jrf{tymyehf| zGMXfj9Z_@6DRZu>wBosVfUjzOYiNOyMwWe9G~KhQD3phw=p)llQdqZq8e}$wgIWf^ z2^-e5L^r}|km4lE7K^N$()gj<JNac}?JXgDKN_@km5BI~J;(#EyauLlb-3WKCwppz zd@mA`!d@=!Ov{cs47<E~bI#e*lO-ql*+jUeY20=7V@ofCXcGLTNLQ#Bq!P0EG@_Jk zX%=)SGf>er4tUw7G#CL&*z^2r#>B+Lveeb0XLUZ#-BxGPOlg^UcX?T;eLUBUd<h2i z4%{Bii0s4RL3lhQO!OtpG<VhpPFfy!WVvMi@w2Y{6SYp2-4_oh+DGwmOP(8?-E)O* zBNvY#rcgm{c6-s!W__G3YD1sTa!xkC{MG3QKh6!4wRgvIR@qNY`)kdXc@G4lTrq<_ zldz_GynP*ty9So$m7nGGpeJuLSGyqdnQYaWDRTyIZo2!@X4j?}DS)gPFhWwE0H>re zK@8_6u}n}S#8*mk6>oC3zl0xK+7UrssFBUX98EIR^~GNh5!1LcdCBDiChG?1Q}Bb2 zr&%a0MR3<G(>0th_o{8ZWKvFOIwfZ@{jf=kS0<<@RzrNZzQpxIs#jiKp*#EU@L`gv z8LDz~xRRJU8D5GXfhBV|2p#yqa_qXLE^2dN_b0Jfb(lfz`B&U3H`fu5(7#)}&dQ;I zjF7v$lp&@@P~+z!9fd8Iz4S$Xsb#(}xesV##soNTNJ!@=b(_a?*1U^j_SZ6sy{Vf} z8Zulth|NUc)xp^kbj_|Kjl}ze-N<ewzw%Uq_yi_YTIKlUf!n*)-Q<f8Tc$>=wS3ae zNEfW0vb&N~ZvrDp!6+PG*NMt_D1oeu?5uM0>LLb^5}}|8IGe?rXUmkytIGpNUmP=t z^(`DcsPtk|E-|48J01ta>vwdK#6y-+*%~qY3&IwJ)o|Bu1n!8k1Hw89juo~Sjwn+; z#vRFVIkV0q_mm$`12_C<4b=b2cmFnl$17wRWMX2QQ?BIQ0J)xfjd8{7r)Ghy@@tJV zQ0244Q91gW0n4beAAr_au?M#B82vt^{dz806=ZOpczbH_5`5#n=|&^s<JJ-=rHSI| z(M0}2zy$b!_`-s8n}Cs%#3@c|s<~0v7+s3YI^%CCqJN2<Gk>bXgMRW|f81K~Ut#2@ z5j?7@Ufe{#?qwCdsknQ`Jg@Q`4({fh@t`-0<&C{|V^2bFj0H7<J3J#eLvnLEgMQ^} z8_9yh-o!Cg-urCaMm{GfR4^e$Nlu{g#UBVPh9VE1@(VFAxoPfiekFtB*4-@sYjuGE zny;E4?zPt_mHZNA;X;VllkYqG?CRrJ;taX+8Gu$$dPr*9st<b|w7DJyA_F&w7(#0c zjwEIuw6Pk2psN)}q+T__AMYDBw|4N1dLLmaSA1xfd3Z+aH+FXV>7bg@Cn%!_SLoKV z2#9fg3YQGT#5)<oOp&4HP}pyg&rn!;HtFU4Z2ECVx>S49)6<XkEqY%lFb*{kxto4+ zUH^Gos47UQ0^UH4@UpVmWdpndkxG5+E1T&xWkFJOR(<-FrO(B%pTYU2>46THTVCm= zQ8(|QPg<K_zr|MJj5^=9zSW8v)(3}etO_N|#N4Qfb^K;aHzaI+a{A6#wcBe|Qme(c z=E=DU2c6m@jwdWDimNNWb>6=6*1|DW;;Vi-JQ+7)N^}E{01$_IZHhKNVbT<wQp>05 zPmv$j=bf+U7$=`~X<wl<!TegX-Q7e8O@<um;XFq^wszjX>aV{58c4}Uke_j!*l{3? zQ$n|VZXCu&#nxe2m)+k{F1)1{49gUijUE&c{t$1RX8o}axuDSM%eYel30VUSrK=n` zQ-4tSAuMd5A7jgL^|(k#sf^?^A>l59tueQdb=xiC6~<Jq)7F~);+~Bj>(O>6qbNzU zr<K~7@jeyB%8oZlgX*6iSm>H8-G$??Vh24hf!7H|0?jw-(-rpv84G-&!CTt3w?<<f zxm-m2+fL{wP4i3-E9~)*`XohYt^1+I6&OWv#k;T#qFd0+D)JLPL&sVIeFzp)CKajI z3rD8QCe4B9b+R6}WIfF~{c!=`sjd5s#J68$unvHPV^`LlWf|&;%HFPfHR2)-?_2KD zPhRyI=;xdtCv2S5ad})>DKfs*qw9`weK(B>eyf>iZ1PPTqgeuH!~UDJpnQa2SyzM* zFX{Lsc0Yx#`!GQ~>P~4&W$AJ0Z#OG~*A29ujx>uz6LO?Jl%BLd7c*&7wy<Gp`-q|g zwcvJP$7>M9xK;!|vR_D*n!JGlfee?*y}O<FCjH!+AL{5k+-}cUDlck974wi<Kg5hZ zj--hQq{<*c#}g0ei++XXK1R7JzD&Re>XG#21Bapx$SvE97?ba}iIUHuO1j|5Gn^6p z;VWP7z2KuV|In!V>qqJdp&}Y6bIdj-3mqrCr&xHT-l2OYmmQhrzbpQuk7R(pEn@c~ z8q8xrIrp+th67(2MQc#@PPnM;!mG@&n5}^4Yi`HLGIjC-x4i1#C*b-!EWK0TSyi)# z>3x1ZojkUM&MY<UK<xqSTUY>2P$n6i05e+7SF+org?iDjR#BPWPP1;$gr$dKi`0V7 zV*L#~sa>a>EB%E}HfnyG=lY4h+xkQO5fMubK;yJze|oJxDTZt`k7k8bZ^L%mcV|_l zSd*nknx9Ix65m>!d?KkmQXw9PFf$e6hg~?J^klAmC1oaQM|Btq-Yo1ps&Vj5US>hf zXHc;oy!MY!4nmm(Bc_nt2(**mFnG)J(VOmm9HU{wW{I|9{pa|XuT7($5)yeiI}yC+ zBVRF=Xm<`PERcmQ9!MG)J`r~3^qh`KmnhI}7E;BF2QV{D={YuPDOlFT`M3#UpOx^J zZ%0-a#l3jYR#09ZrKNNEXm0+1)7IezZL$7H;O~mh|DuyBHN6@ID-WrM1z6;z@%ICn zz0KQN2Z!e8z{dMdlNR|gG`r8!MJTZ~`s0Z~C7TgWgY#!cDn583wQy{~xi-j8kyhxq z&8S~^P>9r8A+QBgDD1tPdrJl$yf?iR_DO}PHKQ(+cVG*JlkD3Hj+(@=c+EE4^U7}E z?sio28x9Wlvb<l;N>4n5KUYr|*y(ob*$S=DaD9K|@Yr!^HovCxp0|fMa{@-M8D70A z1<L#@X<q-N&M(yjcR+0`VE9pkTmUvzm-pUkj2X_*BZU#x5hV6~QOEO!xrZxk<m!|3 zi`$A`h<(;r9$L6(6G+wtPWQdn2vk>r-8q`uk{|ObqVBq+?aasc7dGU~l``dt;I|jp zb>xjx?xDms;HikAEl3x1&@U<2ey7QZ$G+x+?6LN^ZRsDa@r7d{P_z#8)M>(3_&d#g zP->4tYIYMU2=k~Q$o6-dCa@MhFpj^cz5OAL=ReO+V};=kQkGrA_Qc9=B<<YRKIP-0 z%(CqnccX@U7efS#4;U*#hhrS6+Jvr%2dlfh6pRS3^HALz)eh2D_Iqo}L?pIu)NRRc zvy%__8j%nau3<uAr1yjbtAJMHUM7(v#vaF9cs|Hda_%KD(kU(?j9UhC<Q*qYJ^x}5 zr0u3x9zPvi?p#JVThl#tm&z9#<a_7H=*xV5DRK`$8%42i0eFqq!}2>SvHXzFx5u%g zrMI--`MgWN(O!Hacmf$d!FpC(ehb^`8fk&3Lx*_64#-C+y1uGNl88J}Xd-4Z%xW1e zwZ_zf<w5e3*uW_xq)vQ?F}Rj`?{u)<xmV0N0pmO_yO{S~rb~ecoLjRL4#h-Z=mphd zr};Zh(SN5Y2&l=6Ih_O(Y5AOCpyOrZ&MPH1l^eLUgNYk@dd=9Xk<BpEBFe?2zyIM; znZ?RR5|BC8$LDI}p=Um?<~<+2&PK7Niav44IbN*AAo7AT%iHj_PGy{64X<Ydp>zZ2 z=0%DH)<5ri0txnF1T^o6yC*T+RM~ToCR0&G;ucMsZ2Vr3Bn3S56~_+*A?eI0=gA`W z<^#hHH!H6(M=)x2XF5I%I_$Y$DQWg)D_}#^UxK(vD6O6ic%%hq@z8>y4K;Y-wnpR8 zn-0Qut)__Mg=2T`K4j*UWrl*ynHREdx1WwomDcqs7+!bnwD&AVNHLzVDt;|(?(0yx z(t}noMy6P1yR9IG;lv2Q6SHSyorjJL$jxtx^h)a$XENs)1s#vKoJYu$wtl>MW25K( zK`*#=6=Baej-(p`5gaE=-dul#p9!~TKEK>H{+!@ClG4nLJWWkP@_AveC>OZPl#G5v z&F3Xmq9|U^$rz#rNq+(p45Qtk%^KDedX+O+@8hTX`BuE;<AeJ<dmoRLX{yn^6{{9~ zX7`RVT);$Yel_zI!}^)G@*gobORGH>i!FGLiLtQ+1PdR~Yj-h>N+|X~WyiHcUFh<P zMp(>qEak}E3#TLUi%VJ^?zeZGFi4ouI{D83lu^)`4>bPw){-flme5&W76{6s<S#y= z*47-(_B}`%nm|Ot*p|OsuEg3^x<9_7QC0i^aF&@uu8gJdE?oABAxS<UjLz5Yu8c^G zT{x!t*z!3+_G1^fso2LgiqGYII{mLY?FLWMCzrcNe1xK{179&C+1Z2j&Zj5ESjs(j zoSid?!PAaktWwX5_YzmLyL~18!N`4WN%(xKdm4;%0grC5NOwx(#Mj2XwrS!KZO(df zqWx7cJ!>e>O#vZq&h59^!(jRik@a+gE6x<magUo0l_Sycke=F?)Mu}#MHvLubd&?{ z^@K}g39`gF2Q(azjL>)aS7tGso8~S~!TA=c5;JAjaKnir@#`)-LZ@Z+nxrb~O-_go z>6Js8?W(}qKY0yOn-K(6fsvd-^rz-PJW$tx3l5VZNn6HM`ltArt}tgQ?iwv(4ArU= zPx!Q}9uw>f(%qwa?*W8@-ErY;q8tFKyyT0>F{H!*#fNc$>*2+UWP?V9PF}8_{P$I- z(q0(_MnA@=+(ja2pHJTHU7RR1A@&uyUr*^%zdf4YKZc!%xxpToWnr{<UFO5(NPb<< z_i@X7a0(}P{Z+@4&699Ogt>W}*Yl%29wUo|dDkFjr)5_T3KAcjfY+Fs!Uh@FoXe~~ zcq!NMeD&d2VSA6$PuFyJ@8S{WEvTLhX~5A_O!4xwk9-a6Ir<0o7hlwg`mA~)h8N<x z8gOz0oIDN;1YZAu+Z2qRa*=dkav$8bx}4`iSJHZ3td8aY11aApzQv9taOV_7&)syS z#W!Kv;NFWHeuh;}H$EAiI1_%bsps{?(x`XF$KiV}!)I5JFDNXe#&lnIlA8m8{ak>q zR)I*8Sb!n#l-G;5d!iL-P7sgOm>N+_RT#36pTo%T4qIg_`B4)wlG64O??f#tzZJm% z7%}%7nk23eg`Hr*V`Ef>NJY>mUxmBw493_;=S$rndRr^1)A(7<MxhhnxI=l@;boBr zl|m9%Xy!0t^B;jyC7+yNW$Tyr7DDuCMLVVg=WOyYlFwy=(-OX9;V87l!RjGiHR?*w z&zWo56!)u^Yi<Y<A|``l@J<sg*#u$hu-S?)#th*=bG1jQB}cYJ_XH-<_X64Twv8a^ z>I5)z$u?ZQ{Fe#LTURdbi3^CjGoqpIM{>rl@DWx?65P!`C=SYQyd8RCO`jrms`1Sc z8PQkKheqx#)*4aNeY#p)hbH+!6KN-s!>maJi(g4$=`8P#A2124y5!SAZr*wC!yD}U z)U^5f{H+olxwjg+7grU1I^6pi`&U-(P86CsKDe#VEogtb!m--|UZpvR<Rk1$ne1XU z^UBafo%&)}5x&5+8GVa6drf{_vx#lEqY+6@n$M!_E$eY9)6+L_y6JG}0Rxj&#jZ!Q zGnW*<)7%^Yb=&&0ki-ZngXj&l^kp+C1lh$8o4EB1Ld4Um{=3`G#@BjKBjpxjqS0mC z5!l<E)=ZY>+J{TODw<1|2&kb3l-(u$PDw5jIPd5&S6;I)KFOkx?h$Fc;5u^MVdlZS zQM1bV8V7NgWM?wg`vcUg6cYvq)zsldeJWbcGJ`8Q>(cRJth<$Xiu*~yAr6i!sOm>i zt?+D!j+#<SPoiy%BiXCt#~v3+?v1y6bI|+snd!Eb%%ZdRK6}vILp%p*T(u|`9I4Q9 zU?J1H$7*r6z|J#IY4~!Q6Y+ZC1hc4s^(DJoq2Y<<#xGp@wr}u@a{6K0TPkO~yQ7#z z#cxZ9XPa!<Pj@XllR3T)I}W=xbq%3)e4N#+VoHb1ikpgxDcxQ4q8EL1CIVjEd`oj* zf9`YwftMnVzur2@-O^Ac*jyHA=onz-n=mfr&#ee5<M&KOrgwGv>_uv$>J4g)b5c<B zl(R+m4^{g_e7Hfk+Lj_5F%s3aJ#g7Rq!CXQuyGBt_#or9<Yq0hl-R$sINHi!Qedyd zBiUs~T`hC6&ZU$poppj3BgUV~A3Xvbs0=1=N~QDIJT2$uAlD_ITOkt&Gh`Py&dalr zZJ1ZI2;ZT5BiB;=nS9Xa-PSL5&l$zeG1LpkwWzgqWm6<crW5eTwY0@}h2T4r!90hJ zQUjvCe27$TySFYYkbr(U6;4S88lIiXL@(+)%JE58nuaRB*ZHDZqp^)^Eh^g<OplEP zY2Szoo*I2<DkR05I&(53veh(7=l+u$XWmGxxw%Is6?{eEN4j4nPg(2mJajpz{jS#O z>)3$qwqV6FphO&HB>8=(xr?Ss#@7y#rpSgQ&vDP_2W+&CJJy{W!^E-phJ{?A`8~eD z(`#<czWQW6RQvf_7;04fJB?0e2E@n&RH7L0v(OM~US}Zj>M6{lV5iVVW!;fxXFr&v z&$eJu!<QyqI-Q&<qBC{CesS5i48KZzjbCljkAApX*C8nV?d$4!vI#la{p1Itxrwm9 zBJc4ygyFz6+fp-lK6+BnCLkMZ6ey8ZwoYM!o5mfwJ&Yyqcj)zDY3n-*pDY?QU3M~S zS?r(vQY%fZ20;MZWLgD&pgBpwr17FQ?@FWav4T*6BVy-{O$S-U%6_Q(Af%8EcA9}I zY;5tZeSw$VPOH8r?XvEvOADD(ZklCYf_2yZWNrBA{8iutVQ`H6+d0*vkEHXC)FdsK ziSU{;b4|pVdGZtE+4e9CvG`S!eZ6EGEh?5e^BgNo?)hb_pY9oI+rRH|X?IJ#{xyTp zL-)2#KeWnr74i7GoQ^(nGKis9N1CBh!pUw&q}0UJWy`)4|IAtPov+Hgf@B;xz0u@; z&%FD<bhIacs3;t=6Wood>T<3O^)lfumvp7h=0ylrg<NS@Y2ZH|7{B|)1Cjme?zocd z<s$qR(SHC|zvv(xmVf8e1!v{F2&FfoH5D|so-ij$vMTb@Oo31}z>T7c{}h4e8fiUE zEK0p<@$Q}MDCftN!*S_GEWHxLm-i`()jE>0Upl1i2EQHSW#`J+kaXi}TKl9?*!co- zRJYFEGuw5d@3qCsyldTbqS@v1O{5bY&M7&<ne9sq-;Y$JW8<FJjf*Rhl<xZC4IQ0V zrfO3}{K^Pdi7j~X2DV5pETf9Ky_}PZfu8hB>x(BcQ!0g51)P^ZEF;b=An-=0J?C=e zluUzM3*<Jhr%%jkO$!fn3^$_nHm6W!*uXsGA!sa30B`cP^L1v`9d>^$F|oJ`f6YUQ zZFK!un^jc-5+AwV&{qkqQw}x7vz4UCN76s`s;g_=;~^ZY@o+!`#-3)!ljnTww3yt4 zj?2j!j+ABYA?cm>mlk?D3QKQa&KEy=`6$*q*L!E%`Bf){m()ET6J5)q&*#Nwf7|ov z>tN%NV+Z{iG<k*39139ABgBHdjqJd!#iBxCn_tWU<)btgoZ6yM*;8XZ)x}IV#(cha zojR9h)HTT_Z%MQPVNW0h@RAFjcR`6+-aVa;c5=mctZzOUGf6G4al!IiEts9LwU5cr zBj+%FjsCbzCeBk|Zp1_(kA2x-k=0A9r^uY8=H)9NJ(%>s>Wv`XN({n+YDj&7a?)o* zc3Y%nXE`XH?SJy%-9dYwWD^rst-6sfJeWxMcn*$09(Uc6ht0Yo9FZe7CwqN4$+Dzi zvQ})fn^)|R)Z2Qddq*;#3iqFCBxEIb@CJ64SQ%Ss$~j2rIK@f3FHKJ-)vihpm|_hk z6B$Xz1bGZgQsQH3s5oyiB7mVEfhEh18qY*CsR&~9fScYkpDZNm<RP(jK&#NQL<VuZ z&Eu}(c6rd*bHdLz*!-~-`VF|KVG5Gv`By(Cvv*x_EFDtKim--wW?yi~UU4Mx!(=TJ zH5Tq5Gt@}*!&}9BCFu8x5zcNzyA-^th*m4weeqsP=aJZIq}}+f7{xlwNopSQv@a(q zb+VIt5>~IQ+$n5T>fuzGM!(z?d@7$g@J9JfL$=PyxX84<cToji?OtBrWLv%@I4hh} z7fb)zZn-WBLQ8(LIjH`XQVf%TN<Z-p&Unn(4%r3)xUtkG1gQLa>Jzvsv-RJKy}Iw5 zc<7d4=FvOxIGt_RRv7*&JQ!qNQ(q`juSOSjgrjDkUMg&fTwsz+#b9lfwHpE&BP031 zD2-<*eep^jmw8lkaa;;9s(D{+j=6jWo9|aoR`NZLcMVY8J7**CCGXHl-27)fUvpZ& z*XC0!!m4iX4r<X%@2wMH+;Ida`|zij){WmgkbU{&<Al40$h9GH=behOQkAE+c%u_W zMLgjyhAujzoyTGGq%`GhVD{7?T5HP5<Vnun3a+ga?Z-!SB=bFb3PqfEl|MNILmy%d zq1#;((ul<y$<R0o9g5H=z=BuhMi{J(#*TF;AE_Y7*&2<zE9(k8pv&@IBrEtH#%pAe zA9%$yB$waoeC>6A*6>|fd7P&PGkfUd9wB<Y@yIO$#4T`V4GL_6s!#Mov0GVc5|@vx z;Fi|+^7o0_@*g_u5qnPfwb};eNRo3CPh@%d&D;i|)@NtlAkYbu&J`=TblvO=<zB~% z)1-M!FY6DR<DQS!jEGbLZDMwiq&T3X3@EbHv`tu;4SQv@`RbwYv!@;$?oI;-++Y2? zeEz0jwA3Wq;02;F9vx8wiYiQ9lkd|P*&W$G5Se^1U)9S`ORj6>r>=bBp&6oKUejuY z5z1Gu_JUb@A+)<PuEX22pU{Syf1>Ng$JWRQXQT>J<8PIu5bxlf24k-{6uoWT>Wu}p z0KEK2$b)rXVkE(!=J0vhNSoZ}&ekLO7}xkETVq|?<jZo_S(4GmH%lL)Y;OHH)B#q{ z7FG*OIoD4)d&{LqY;YxSTua(k_?0&2xhZnCq)&j<BxVpdVcLp&1lzBoKCaL0Py%Bd zV_-J*dD7r=_^xo`zDcztmFXMeGhI5=D?byn78e%rD+gbn4pwD~&r+zlQ<*S-TwXIm zUoM~d>?OJPbGq-x(C&kz#;J;BSA6TZ-r5{rMe5syrB}nBzU)mm;oYZkefMnPOwp&4 zoxYnBU8JL?k7V4<mQ}7!jC7vr*PvZK&|blBgs%Be#(LHx-ftI3bfU0Jp!ari1IMm< zc=Y{1*b2`mHLk*X*^d8$w;Jaqw#u4Fcfq~B%jBy_;Lmrd%2A{%@o6F12<_Zv5koVp zPFFsWV+GZa>xn`Zp$Y$Nao$Z#T$e1Rkwrya^Ql3$ghGe5sJKYEq0d{@4=z5>ke0!@ zd<JF?A9My%hqY_p-sxFEMpBGPGZW}w1Y<~ZTLw6LBBAXB^_l<r!EygQhAAFJ%#%$F zc$dca2ZtM0&wVb~lzN+fP@hSsFmrG5Zth`niogr$efM5Wv+)eLWLE*_cY9v{Zgac! zojvPo`Ahz6b$8F^F2)SXOoTjEQNTN?6Yg+bsm;whR>94E=~1SzPmY9mY|cw&R@sEF zRq%3I-&R3gmPfgKS5oPXott$NKP)<}Z0ediS+CsMWM<`NQ+gMw*e)-+*WPIa>l(LM zt4htQ<)@q^8Na0H5JHm_DeAXaX3|o29kha{^0jGP?k-cH`E>W}J}u65x)AvI8zGw7 zzKjY2JQ!0t!T`Hn6>a=(_^kQqS9L2KMXhQNO~ZV(x(|^6h`E;|1eoIRU1+T@nbEkm zPuy*#6|z-+LV9&mB}n-lD#jO%Hw~i*i~DcppBi=+(yBohdtcMYjbwSbs}zGztfz|$ zuMv|`0w;QlK5o)TRXX*cZ#=6@f&yA^C0(~4!jCw8uCqPbV?3{@<CLOpnRqsSW#HNN zIkK3Ol4&T`70$o7BQWoN%ZDplj$PwEhJUol_|e9pfqA-LS2#QL2Z2Mt2SS6j;&_|M zDecnUyha7XLzQ8>;8u26L$KHX0i3<jVECf=m{E?Dc6<JSeQ(XOx}BU6I3Vf9zs4SX zSMxV;Ri7UvluSZ=`<;g3+|BGFg4R!(G=EV?==me1o&PLs6^k9ufFHz8z(@|Z_^&OO zten=j6KK52@<l9x&_>Y7#nJ&+Jc`Ie;!<HqS0}KkkzGnebF_Vgtw#SJ_TD@m>b~t8 zA5lV)ExW0ZLL^zrGL`JrlwD#fWXqb}n30O?Arvx2I1!UQ>yRx=gpl1>%Q|Bj!z?|Y zuIoC_`@GKcI`8{>?(28`p4W3f=O4<eX1??N9-q$~$MHFi_wjyfBj>F;s|`)Ay?s%` zkY-;HxD`WZKuVCGPo=^H$kTfCPoM8{iiK#W+nSh<Rz*Kv2%8g6%k9>TYYs$#6X{<S z%qY!`2AnCk<+q~_8_LQ@tCW3gPbw&rupJpRSlQHYnd?etPZDA~u9ZES;!_=Xz1!Ir zb_#vh?MokCGNg`W+;1MQ6iIu5etnr|Ju%(CrsIyP@NEm>{aJ}?d)%`t-5#TPV0b5V zgGxb}YOY$x@~OGL%hmIeo#k;C5>|Si+N?Nl_E~@0z;b_0P8S-y#iwLOXmj)6wq=em zk!sgHw8kPVJUF+t97r$%WbKF{qZ@r10^f2jhfuB-CE2deJyXaJ=2*2Nwq`4g*ZDqH z37Q;`69|dArEM%J&@Ang;G`$vCYiULo^A1*;(H<VaHX*ccVxh~ex<Q47)kiYw-T_x zBcUvH#}Ls6=k@(Xd!3)WKVC3zBRAf{oA`|2&Keiu&)A6uUHRB_DDBlIjswuWpX@I1 z_f5Ak;Chl^rK|R?SN%)Zr;kZ3<9;f8>hj3kfWkqL+z*m3+0`l0ND)A$aw*(f2#Aue zBOo<)1`J)i#mrejP(nV*hAct2O<thy>JbZ}J;e@Z`=;OIV3O9gyrX0Ft~;QSBf;K$ zG%pl!wsb)7+M`*=z$=!L9d4nE&J&>FIlV^JIF&7RGP&%^b;pj6d9_>XMFbd?hvR+Z zBx0xozZH^L)4ddaxI`BAxWVNurYUN$fZhQshrfrDjjPNapu`r6zFzOT?;;XaH12AW zc!+_szbHiie&rSZlWI0hYZC<H*zKd%f}mxQ`4^BvKqToJnS+R~9~U8VL~V!%X>H^m z+A>{JuZewZw7PwlG0h{1>3)l2>;BlV15@edct4};N_&)PL3X}zf9ZC3f1`m+Mv)_> zH=)MGYjtSD7Y4Le9J)n^P{Yttcxat4RfcQKG0Ap>?M0R*3%_Dh{Yj=HM`CoDmcM<# zS^=k>o6%}Fsr%`cN!bC&5~Kp#gMNfy(BPu-hM3cZzV?{wxy#GUrdW)6H^g7~SfRGe zw2zbn0tYuCXWGfUl?c<P_X#>a89~t<d<4ot%fR?s+LCqUw`Q+Fys!(hT12q_2vH^M z;CLR(`Rw?fTHehHHzO(kanW>X_HWI3rxOfZ?K@wLlO}e)255%?zDG;g4jxJmkv7Z- z<zL1d_-TLEV$J--zS{uts@XyuS?%i!J5-|%&bq(UH0R$gu2KZ)Bf~A#)5j#Nm=VrN z+!-t1As1_(=!=}Aj2lPr2G6s)v(&@hD1lg7$8()(lrU{lmr&8vJMX=Uk?*oui(Tp1 zM&FCATpdgqBS#YQ@$=0E{0>k~ub&hRY$cxP@Adma&_Da+E8O$7+58$_7*U*i)>}-l zy=KuUpv=*7EGc}^#RV;mxl~f6MvR&qQ@(OCzIV&4AC;`(Le8PwL+&Lc-<RFQuq-}V zJp1~|+v2kFeVx5;w+&_>5iA|94fTh1`C(bWa@bH621?;9i#Q&|XDKS1tr6qhehq%h z9m!7uLVbog@=|srwP+8(bOw#v=&~V<JvAx1LdI8F!Qg<#*Y2{gl3mh^kp>2qZ01XE zY;<oux}YQM#A{-F6T%E(sNYp9d=&otwt}EVsPi#7+t;rIksqz~yDoQh<YW&FCe^93 z7_h0y?l+ile%?NEBHmz!24jLbjTAUfLYFzGs}USI$u85;C&F%UNNvYu-}ODJI|GeI zJ@SK3@?j`@vG0AtXt1k2!&{C}<q?p=>P11lgK{@`H#nL(h6X1GTU<;BEy;-)g*o1K z0-*wG+pnY^t_)wiazFHnKXXB#VP3|=bGz;bGecPCJN&eITBfDV;=S^YYqMArqekw* z4Nmx!1o$X6&pTy{K0KbCvnLJX%j>YY{1vT+DDm#9AdXBcHzFX;(shmRS;W#_G-QTY z$QF+WV`OCl7!aSf1ttf>ks7qLIyR~_(PSdJhA~4HlRMr*ynHgn@l^at9|l_=t&U@` zWBe`5!e%Z+p()8n8a!l;#a5Kki-OV-G5#bi?I#b!9wHJMBxUNo3JVXmIoW=Z!ipHm zULH5Bi;$1dU(N!O&6n;)Aw4!H-x3_}CR>(|3{%QcJV^b4vhE?}DYvH8M^(aiQ_vB2 zJC#!Xj6?B_nH*MeYCRrQlbtdO*AGGTM+l1gSJIZpw(oHL*{lI=_QhaU#!=qKhr|XI zx4WlZt~ZiaY5Ap=h}~Z<)j|7Sh6+_bH*!ujfAKmI!?ZkV-pVhT%N+Ft=4lh7Vq>0h zHSZ+^QV;RkRm0BOfmcIG3h3YXd}x-{s^IuN{qdM~8r%TZtgqs9`e=cno_^4VsTp+% zrGWZE=O898Q7GEqaAnLPH&0nueH1QEG%Aw4#^kAWSm1rPsQ-fAT~Fy1C?kCoQIZ^` zrh2d}5UZ6r>axe#;8UB7OVkr~)^{6HS5P%({eoSQndC?VjOO6-?b_l>!-Vr^S;V~6 zpPL`h8K^r0j>3}|Scz;2a_iGKGbPm#kC@>FtyqnQ$eV%t7=~*dXNIy7kp++2C0LB| zwzF8quETu~R&T0g#x_hj80Ewm8io`<b;<jh5)_;8Bj*``TalBe_RHaYmr8sC`f5LL zSr6yyNPK>HOYE_*QbWA?TY*D90%w8hNCR3#4xlY|lN*iSC7XRl$}^M?$m)%E4l=Mj zO=B#xGpyXWwTgHJWkuUVA0T$WD6E5iEwZ$#T6V6q$bNTdlVLRe<rZ6Tko2&tO99s9 zbxBRMU8u>iual>W-!_2roLI<yot=WH7Cg~F5gU*gucu9YGV7QxS(l5*UoB{-54{S@ z*IDpd=2y<IIE~G$wzfp;k{j``<qm>$bKS0z{u)G~W~Nioh5SHgF9CD?%(>&in)wIx zm)jZ|AVdVKljspxx&3lkrc)hji0LP<qvc0^4RK$S3<a)hGp}sNG+!0<zY*-o{PkcZ zWiP5@XC-^*b)5)B;e+of%Nv*XU8EeT>lbZ@V3A}GLMDFV{bk{=F!s`Cr>lJrwatcD zUr^aS7VaxnKILI#B;Hj-Z{<ia&RM!8aTNdYqlOjM+YVK}&AYFWA(WKp?vbpPV30Bp zy;W9*(wUR{O}!N8+S%R8|7xv3fHeg=M2?L3$GOruPs<kPC5*Ut!NxQachslcf+twj zY=|6ia{iRM<nmd1;%rdTV)t`3+~LTa4~1eXx8a>R>g2+{mUL!9aub~6G9E7Ujj(V% zs71N)<cAMm&kN_XckUG{%89_A-6U*3;=3k!-N`+m`ufLvj$>Z$ewsJ__PO!0R@oKV zQ|q-P07$Z#=cT;^EoU5A)2w;T?>-}cMe2bl*T!iYB007;UExLTdW`e>I*Eg5)e0mi z?n1f!h8JK*FI#tCtC62qRiiw{xcYrdGn+_}ToR+meGMpfB9QMb@8@RLU2(mWa!4mn zA@SByeeCtk2^K&gr!|QXouFPJWXF^TjK7w!jC_=0y6@=C6{fp$ioo?qMS1b_<Rr{L z>$K+PjCmm;c&hf*$HJ8AGMrsT{{m;L-REP2Sz%ElDP?1&Ydv(yd1xbcTd%8n6P&(! z?XV?iSNOIf`XJy6P({#=N7TZvkoOZXdgK=#!~}RR631obx-O@-9kaY}fWA~2Wv8uj zeW}J{63vFxB@|4AARV1aW2eYc-RxVje%kCBwsyBdulk8)`A)9w(-OvsJbaq@@w&+k zZy$Z9_MIRSynS8~+5VJh9`H@o=qlWqg0*zsx{Jh7An3Myav;45^eVqyDqC|~27&E3 zA3<jjK*oBoh+Gte9E9h1G|pQ=fZvy)H)dbwlh>S6tG*OvRp%)1Z1!0?{M(J3$0$bR zF``+MGc@unDS;Gwz_HG3sIX=`**@Mt^2cTn-!w|!<gXKLGHHM7$KS5D@#5&%80N^p zFrxRH|Cc<07sqU*rv#BDM&*NB<#jm!Z5(lgZm2%FTKEExEU2S!W=|Gz40YVVRcIwQ ze{|DZr)uVGqwTBL=d99A^-;?=v^^5~F9&P}oH=S@#%w8In19*ir@4Y4ZiB!qY!>ax zjYc6>!Yh8s6)mj^T-^3<P`?`m16Mzo9EL!0_YKIRg5ub5X7c^-5GzFtO<JEOMRp?y z;dS1tn_U~Ai#o!F2CtWEe5NCNx1ULbajEW}jUC(d9FGo0vmgbCY*)#-_yWnLVN%{5 zWixAJ_nc4|m)@Gfvz9psHBWwIR~>ry`)5@u$lb@UC}$cPC1H(6oW@snHY;+!O(~>s z=ZB|U=|7ZFJ|<CD4Nb!lOe}@65ll|{58va4ITd{Njh~0m&Z+DO?!c$!BWjo_R`jj_ z5Ps;K3)h?iiiaAI<>7H>)vgpX+1=cGXZWg5+akh(3?@Fi@6(U_`GY9RcsbJetq;d+ zM_k$9p;N_A+UvQLk25pHY&K9O0K?k>DhICSbDfYQ)<%))%+oo@^|6vCevkMa#)RQ& zX?N0zIrFNBYktT}#&=$P9+VL_lTBJ2+&CF(uXW3zAv>?vrPbBUHz)uD&Q(Q-)`{R0 zXt2)v?~o{;azWS3td8R$D?4Kr_Axs4Qe9V<y>4ssugS+e1kNLoVohc1r6RJ)(v!Fc zr$K$vdR!%{^y+KQaN*!^fs>V<o}TAe)!`~TxGC6DGzEq<9<U`sBToawN$FA5tas&c z%i|XUk~xd>xoLAul!4Xvt3#N{u!icsJTmk7h+%Rx&IugM>cyCy+dGZKe!9ds{?5Hp zG*CNQjmgQ-c-n6HjAd?n`Hce4@nPp6wav5Acg}b%7;xd0C9x-HBe>J6t~t$Ug3*Ug z1WV0vZtW+DpLN%!XN?Y2Fy62Ks=~V?M98P>p+iAUm(Tt?BqOOr<2X`)>`crtDDSB| z>WCZm8Ss4m3Vznyr*ikbXD?VLHzaw#p0$2%lkqKSSm`-5(Z!KVlgsu+E<bCgr1&KJ zyyAwrT6Tf(2zN}vjYKh~q>3%mir(=gw8!jV%X>d56)()`8>!1!n>8I0py~kic7Vu# zhodL9BCequE`Z_8P4vzVz>xNISX03E1a2B^&uc$#&w@Ex9&pptZuO>EussK5UjAi! zR>eL>u-6C?&EBv3DYTZO-TGt&M0Js^ktc-q7*#2m@{1K=m023Uq*!RnF&x)P&;O~f zx&l|Wfh$PZe&JoBZ`69N>Gk%ljv(yHstam*N-hk>%F%))OAaIw=%aa7qeE)tF|>nI zLSbHQOZ?HVNlb%X(4y95s~jX-c@>$H_))U=RZ(TaQeT@8x6{f{yuj7{4F>TIZ{78} z`4x9ICvkiHobmIImZ$=gi*7~V%2^Hf`j)2h#JA3QcKVC2UG~Q`yA730;#!_RT8%B$ zEtE6f&F7(@>2d!P-vu^?FG}Z@&2Ou?3Bebh)$G=7<<?Kh5npXnd0px=7-Up2W}|OW zz(R52e^#OI>1kSV+A@$da_^&BAmtvaE1mU|UB8Utht2Ajm7%>&eEvdjWzJm`&5C7w zDfwXCe#rvVykjQ>YHneXro_2?Kji6*i{)i^jtmvrJrOB7KKc51+>-w0T<xMJI5PyH zI;&(~R;)XkDO1=l`YIcq_TOSPgK5SsBCK>?J6>lGPEiVT;eC-+oTs(#m{>FqXE4YK zH4=LscHB!dRZ5an$mKW6$$O+M=f9N@m^DIYNxW;lCq~rE{fXz(il+q`YZaS2C^o8W z4f$3keLz$aJ#4Kqp==f+h6*+C@VwLhoF_!-Y1jMu?7bwh!S4{~)fgCLRYS0wFoNG* z`)GD{y~+MvQTfG)aegZV?dDaMJCOaA{9U@!u$O3VoIT<JhF^}`n79&=Y!>%2+SSH4 z)GG7k2_e}@Ro5<6GxZEq8@hz_01w;%%NKHhf^(jfM4wu5X|L4TcL-(Frqzend8a~j zj?{R+KT{^GA;u7v2(`cValK5=(a|fwvVCXBDRBt|@8uevh2Exqqq*qX&x5irOe@yQ zQ!u+0aBERC1v<|1JH#Lh`w9M~k$!pcJ#=$Xk{%WCHH*BPkc1BPpo%2xb%z~B%b!r& zerIzyS>w)kh{9?->N}+3igYg>>Zj8<&e$u16vG>!iya4!*@TEJQH*D%?<Lu^sq0|_ zCGewHI0OthO|N;xi1NHh#ht!h-7n``GVx*EV>?S~LsFk*tpDj|dG|bS^WIu4>1l!q zEfwZct2|vpDu%yafQxh)k;4*5fzLjt?Z@l6vuhBrHJ~Fsl0b6lp^DO_i-}CWtFvwq z?Y!vH4>Rtm0@{t~EO!|e5f{meplU%s>W6}}k=HMW8NGe7B7aGtER5l4)Ty^^jmMb+ z2D`0=X`#3~uzid2i%BS9KCbs8nFhJD=@McC%AN%qH?=%;j(#Mlq-nO#!-q^0QjO+n z#J8M=VLCH4<Ezm7P1jx*#U-op$yGb+oxldgc7dT065!WNB^3`+4c;7%Sv7o%tA2h( zThxE*-e7t*&J_3ebx8+5#H8eVw>`-7pEZt??ez{;k$#fkQ$FIK>i7HsZe^&`Iq53O zgn#)yXcm^@sHuaXe9jgK9Aip(+nUq%9r)wM+efqH#`PC=va7~U1k&$D`DmoeiRc#` zE3NGZ7>Rka-;Y8+U;Q1TbbA#<fYBn@G$L-n<9`D@`iE4}|E>b(e>T~EiyYRdAw)za z?SP0zaWQPa@Md7kLxw1C^h94pN>?pLrJ!9p<yuriRAS%hs1#qgk%D%QmmB-HCtuT{ zrJ*hDV?NDpZa`ttd5|>%!l)5a`ZXi%c1FB@8>35g$ulB6M2&Ac)qDx*{My3#zy-Rk z$Ao38`(tv;r1(CDh88WagXQG|N<Bjs+HV_KAI6+!Ffq}FK-?k15UBtu6P4p<UKef! znUBu&Mg{<~NK;)fCIk8Pk11a`QV!oO;LuC<ZB9DrWXhd*%5wB&ywb6TWT_$MeGH;g z6%M&+V#nUKS*d@TmOPjekXG?XZT}>0mxa=c$rnevbwe(&Wu>v~DiUF1Vw4;mG}7Y9 z?Tb3{YPW6Gi378ek{=~~07l&6@UH~k{%O6t{~jIM?_kn_LBE?<DwOG_bybk<wiPm2 zr~#<_^?w%gypH*0*KXc$p#~uE=zl=Xh8;+6Z5I3M3_8H1bNnmw`mrNk+!Q&#OwlCt zj>&!$FH)8qTSyfrj)gVJ?bl%%tnz(6o7G-&5&30U;hpqxHTjmkb2uI%Bkw+C?wcOG zD1IqVRK)94kRIYw4D+8(9oO_fBe5_UM?2U59~_A$WKbaHI6_|j4mr2X4GMSj-yzc* z=*ACqs_0LuAU~=12R%euP#{rN><7!mB#Z#(rd{h))meoFz<y6uNaX`U({V^2K%3?h z4yIw&!8FVc3_pVq!F?6UKu?0YDHR1c)~vODFu~UA&~xvoXM%Q6{eS;zvE*=nzI8ab zcaBX1`DG}M&Oqt;4;Dt~-&i!vCUI1?$mt3iV=0`<i~*4!w9bG3<V^-2a4Bf=UUZ&C ztOE_eAUVc=;cfhd$q@%mA-{R1Sh7D~tJ@B<1BT2211*aGct%Z{VP~KYDB1?2Hh$0B zK?beg-`~Y=?GGh#t;x+{=)0f`NjAeUG?j7dSA>H<uLHIL{kyvm)dJ_S`@@oS#%`bh z`4tS8CC8ybwoW(zs>mgxJruB}(AuAk^egsC6o66v?luT(axoJd>^(4oD4Grfia0fq z+n|FV9JZ77j1)To<o;-1f3ecfc9SL`=)b#--*1vBi5bL47?lauFVk8IVm_I9^%r^* zo}Uee2m!bLo16IEs&dL5`O98C{O9iab9epC1^v0Z{zh^8r|<gz*>~}p?(7UWo5m=0 z2>WQ1k-=H=zxMZRO`Wm7b*yz6xC7|_BI@J=|Bi|K4*B8Wq2wdL#ouG^p>&Td{4zw1 z1A|<@xk%OnI_ov!6wAfj90%K2OAzq4k0cOP#VeX&RAxGeglEDWX!EPlykyW3<TwM0 zQy^LR$o$u2VI~5GpNBy~B~IWa2qiz_dmK1GG+k<gr69;0R4!b<Ok?q1qD+nA6!h;8 z7C15V@7{c)A{t!h6<SCwXmZ)8L9h#xspFV`y~9yKld0p7o^h_!r6q=j=|izjvn}M< z1e3<kIRg4CC**vqHWeEQg&MrKloAu*Xqe_a$M23FbWrE9uZOpy_b>pty=o4ca3bxb znes#QiaCae{V?$lM$UhgD(wKqrl;Pe|A02SsAS3Uk9ngr|5&*~ZK7F}|5J@4ezBXC zoHFLRBsb+?A70ET9T;^$=j!<nYBA3EA>3pt5YLlHe0^EdGD==tYE<+7k^%$lx#A9k zmiycpQAF%hyQo(JWhKr=Ic(3S+}rYag^iX_K_GsY(|4mk!Eb>4>M?9v^mQbi1xrw} z=WNY&eiZ9gdLC#4ZtgJrJh^A~jI1DDL3yn)olgk_Eiz0e6|7sB9TMbp7}`B>4<J|E z7L6tQm^GlcLH!#>I0{03+~P)6L^loI2K&kd1+O{<j_V2>;Qz(ef5>To;E&$yrm|E3 zk^n%qJA?qa6G$?D+*2x=u7mAeIQAW4v<BZ{3LE_n>4g3H-al>i=id19yYQ#q_;312 zTO8riYRHpU@s~Aku&UCs|LUS!eqpN5ey1vq173cWe1tyM37e<JeuM(Cu6pba%OZx% z3F5jKI2p5#ka<CjRR7`j5B+f6G^UKw?-0W^*bheyCy{P_hrph1gQNTJUReU_1Wf&8 zPWAUqJLy#n-3E`<VT<E+lwY(^H1@Tfpnv0dcQEJ~x_b_J7+j<2hk(}k%Yoq2PJkdI z2!f1cJ#>fFd4kO82EtVTj)ImQJ;xTa!2*5P_j7of1L5g562$VC*`Nn_AM|9Y(I$Yp zWAZryGFjnhWIPzozv$t%2nb61ceH-4Dt&sV{KEh!yc*>%QI70}H(R<!u))hk9wKJP zk(Mv)3?3bw(2t2b!#|oA*TM$zJCWwiucElw&try5o_%&>Kg{M~_A8<BHt!YxcWG94 zFIFyoY}*Ecg;uR9f2*=F{J*8~@@v+i?|;a-_BV0KUy{-P38IgG_vHVoZVM^^H4Gz| zaC||K7-~vC{TL$<KA+|6OJrHzh}5;K`z(6&1J`M#ugk)BO+sp_0bGeoTY43KV9^I9 z_(`rt=chEwZ=|)+j@XrpS*aVm)J$|kH5h^1dWgGwoGu9l9xPx^`voVOaGYw+Q<M&? zqd`{`t75N|RKy%T;UuD~-HEd|W%ShAmEmG~vmROuEsCWcRHd`5Q~b%zbU`YF_%5M& z*U{&vgA4~;G*%ow*+m_(W0AkgVi!J^c>eaTb;M7+<PL!JQ34IKvq(8wwF~tOkq<9I z%XyED)Zp%S*%VQ2IqUt{IYD@zW!tlhgy)baZ;DivzK)@c%F>kcKpPb1gdI^PITD!f zQ&}itROu>m8Bw97f-Q$uYB3ti@$E8Ki+J^C@i4Z1S5_&dfseI6EzjLLSr7f`v)Q`w z1mPNnEY-RMBZ3AhC>2fS)UgpHR)g!L7oU)QMyv_OgU4kPf3hlmW8f}pYv*M(bn^f* zVoCgVwa@@~bv}k*ygK}VBt$Eqv(vkAB@_2cSE1<l9mSt{96fc8eU?=`giKYzKevxL zmZ5M~U|4s8GxW?2TL_AF4H15DqI_bxgu;nx!Eu599GomI8jE+<+i*OQ+E}F=GH$w_ zKEHKsxP7hWozIIW0*t3JTy37nBLo0z!z!>B%@M}3jB}ZYgzm24LlepkJl>4^nOzEu zMDuE9aLx%#S;&g)%V*)@AG;LAAZo~6&%Erm#7pr<0-S3|1oZ%BdbPg##PwFKihfrw zpID0!>A(c_yq2yDji(pihVwC_0%lk#*KsL0vf>pJ44ozRr%NxOt%BA2D2<MYPg_mh zEC@0M-})GgU^`U^@(z?`kC|)cUf=B;sazSJX)(2rHTiM*=IGuJ0xvl_++rpO)(EnM z@6sai>?)qQ2_C_zSLm3$XYgvQLEozPF&A-b0o(LpmLoC`dA*`v^xxn2GJ?iWW~5w1 za?*0^_`gu?fZUsb{S<b;lj=j|@=N-wR|G`(rCY(FmB&-6YHJrxpblC=<(#MQwIW=H z4#B6uxIk%O&>c$wMHg)=%PSP?k~a;$r(cO0SKRM>ti*3~PU%sUt+!+cdUCWoWL$-S z39C~@^&GSotR}$TcSmZ-2iez)iq?g@%vSU!8N?SqHpa**N0=Xp@{-<T=E>^=p+Efz zD60JW3;n<Q`+u8Q?H9tCf254~s}>6LzpAJ!AbP0hE>-X4grDy<feKtj((qMSrBtHZ zr@=KepzK?xf1>ZFMPW65I18X}800$g9tfn@@Bwd8a75kExHQtR^j(C6v{Ua%_MymS zspn(jMq!uR3(a;0vE36Rz*%@u_R%Mjl>%_fs*{#MHs*cP2S%I*`Yzl2B(5p0ZC~Lx ztlMx$S~&F+7#dqYCkhUiySc#*evjNkd$>$hpPXPBX(Vb+nMG_`x+q*C_(cD7F}Z-0 zbW9>K<m2pdr1t#;ubBE{^*#tNI67H4Q3iU>I<HZK2ng<GcPQq7OC94kC{ID5T#e@R zQO--BP9F~Yvb=IsKCgUcn1AokOp@@akaqM%oGpwjhNW^>kVJ_O(Of79Qaxm_`9MpL z&KoCXWx?pt^DX>MuC`u}9=9hw;*5xAX*g2%m{x$hg&TsCz7b5Y!DuJUo?*YjMtEXQ ziKD1xV5)`ittjP#tHM1i5LXlTg8T1;St0b3xXFCPZZ$ptt#>6e5~j9D7-DNHlz&0= zOfR<33wB`9#;~Z*<;e2`HzK`c%GY&Qi%(g~q~BqHq|h>fSsv>M;|K`Ymjc_X^%j0n zlTm9YW)IPsQz)qo2!f^?Spw4tI`-e{I0)VEQypIRmQ}+!&%1lGt^0Wk9hhPfucYXv zT#W4udaL@(vuUaAV^9u}gQP`EM@b`t2xiOYr&l?GaIFaT-Z>bD6WX~la>>~*>!|1@ z&o^8vrea$~bqX!}At*I;LpOAPjUADZ&VtBys^g3Zd`6X?5|O_C`R08AYR9q8CmICY z$rz;<5<dLH+~UD3*cxvRvJ~@@4D7Ze%(-YfnjOAZ<FV^N-}1GfS6T8Nw@AosjN=$3 zfqe}Bt{D~Kd|ftn>ayr+W8LAhh@JOzffFk(g$ML_KDB3AiSYB<*@>Tu3t``nx`I3l ztOA-B4aZTuRO@o`#N_J3B#%WO<qM=9SvNP=Hw7z<uIxz(U8L_2Y0n57sfW8>-n`1( z2&4|ps2Mx!^i~&309(Iq27GSoL<HD=On^tAV)|g}t5I?ig7uqnKn2Mvb@#h!OO;{M zh$HpUOL-TfJl$0XIVjSo4~UYArOSkN!~<LjGz^CqAj}N*Il=8o)v2RnH<&D-oz+Q> z8aE@9R)*#4g$H||GLTmZV$)$%u2wp02SE}}%c2J2(TvW?@n2FeU+HUB8ulK~&3tSX zUh!hyNZcu^MfVG@AvA!tG&q+`J)mGk=!&EG&_92NFym}rT$whdrEOSJ3}QKWpRaf= zI&Z&d%u0(g=bzvTZf`|;v^14#Li-R=p!#<nYz@BF*vDoF9yMP=`tS+F)pyRR1HG%O zr5{{vJq`Fm2BwbPzips!L|Xogcmg652Eo~*L!iYIjh3(nAZM5ly^B1KuRiD-(cY|- z8?TuZ_a%N!chuGQj?MjqW=Np=e^+Dh=lA`mF7M}>l!@Rk$zsA6hyPlF`_~TpKas}z ztH}8;m;N7@hyEc};-AgH&1Q3!Tc^?CbrQtQibmLjiuK{kc=e;xO;!0LmuysTH)y%M z<}%*j0B7zLzRJ8{#XD_%ko@tb^Te)0<i#T;=Ft+Hs*hhbo_!{E;<WR-U<(0@Gly)w z3t;<@mW07b4K5vG>Xp#h)44KsIL;4wlAKNaZ>9JPSD#M0w$?%d5_u<;V5Qhb0XnlL z<=leN<s1j)y~~258IhrOaT$9#1sZ(pR2X7~)Amg_y5c5ZLHBMHh-~6Gzgmm+;^P%l zI+lH;p(9U<bJzGgir%tMS3Y%NW=cQCxXLOG)BRkqSs*oNUb^o{b@<V9qbDsOx6<s> z1j*7Q5K2N5i2Gy1GYHXN3D^fUXR_5dk_nZRJDI0F1TN_shek6nm`B%cqpKjW^A1>6 z+Oskd9W#)}Kyw%R+Pv>+JZEf^>9iVqJX6A@r!g~F^@%|X8B5%%hY4!PAkP!F?yH?a z`@SR}Y8Kz=%FG!RS3ae;&U7E;rohAy&y&*~&;CjJJrbI+h%P}dyWz19G}ws;GvAPX zn|6Ylwggd`D(?bKA4%B7vGW|d{cLiV_N3>9Nk&k9r4A03!Z=X_xYFc^I;hj|EGc;s z%c!%vdc?8BpX<i>6_$>)1Ma5hKd4>^hRC#3;f5No5^?V_vEvf7XKL{L8;b##%Oae1 zPV_IO&g-%r;g1xBPqe5&ULAsbfk06`2#E611W|b!8;#VNLdRa8E6Q-}LDZaOPt6+F z;c;{n(73U}{ShI5U&Nhqzgy^B7zs`;nkrDJhaT8i6<)aK7t@O0q|KOCy(y?%DJbaw z61{!<3(Gq$^WaI_;A4y%2P3E%WH^Dj6;`_1=!nuFueY47jScrsewV6ashY6AAikwD zjzebGg;hgcZRR`dzFf|WKpS_eI~*m8<PEz<%=Ay`it|-U4m@6eN^egu|9z^Kh;P7K z$+}~V3&T<co6G5`c1oE)wLh^O$<&jHKk(Dz2Qr;-2;`I=w#Drr(uAB(G@9mkSf}(Y z<;6<kWXI}3bB6$}r=D$GV<XM7cIjHX&$EvTKluELk`|2W>9Iac9BF~_Q3dcc4a($v zm<>^4X{$%`-oDtkI<ECAx7KCPKwQNib?F|dkt5X-5wT@3PO!mHWVc+6INxc5%15(H z`%L{@PK1;!i!r(GP7Prhs!*{x2lePdB#$2_9IdACQFRIS5j7k@llUqD``BBhH0XA> z@w@e>dLCPLH>83aG&RmK@wDg{F}|ebVqK706g@vP{AxJTEC?_TH)mQo@oky<zHb${ zW4^ZhbhgLXPVY?p%qQk=`+14JOWTBMygDwNIR+&Jre>%JlJ1arS!I7_?S15@bEhu; z^d=|7%;$^%w$uTVffEDReOQPx2CAkyg%I#dZWWd4TUkbG`r8S9ane>P*Dg9+WTvB9 zJmX$}la;g@tZ|AQhsOv~wP-J>fd$=D%7n6Ft1nIot_s^`oNhzn597_<E}YV1QWl3m zn7{J!ZeIgIc#_;b_0hu{7K%QAb;U|nMpA4H($};TXM)dEJd>`Jbo%hXL1ZhvzYm0d zZMuSA<a=wuUX*|z`&!^-qib?MjdeI5jxm3;ToP6M<eYEY2iRI?%m%~GEiHd+Lu5y- z=H%AH)zw-5d5rekcs=pkT`Qjs%QUU&sm1#rt(E!@QqP~sJ%9fDXFW#j&w{^R1Cw2* z5yuKT-~R*MlYd6g@mDY`hCjv!{d10**A44(y5o4+i3?hxY+jDyab3Ked-NnfF(e@3 zFbtWrpEM4WBbx!958ik}p>f^9uWL>B06DnR?qE_=hWKr+Js;mim!v)0xp{A=c?!Yq zK$jzIZaqSt=S*+B7GmK1xPSN*QJ=MV<-(g&EX+)TSwFzM_J0BM@=cCt74^v+Xk}vU zY%m63-rMHDtnqqBgJH6DyKa#48!)?3a`X<A;>F9(jVZ3L?m6#swYomOHWYKDTY{0- z9S*A%n5#|2^F<?1fLbEcreN9ZhJ2&TE5vnOi~1&VA`=o**Llul!BhUT*%ve+PYt3W zhHEbCdruCrguiX`D*jMshuac8Id17CT%1rpf8QFMS@x+@WJP=s2hi`2^QFrB$cN7# z*n^yTbK!F2U@(NO<<s6h!5Rz$%eJv3cCyiwi+=xVsk%@Tn{VDohTHzTvaZHSOMEZV z2nG=(+(^ZAcRf~+-qq`eZoK|6v@+L;d&Iy-<V~bZ(yXw8?!uYKt-yqRyv5|!8$r&~ zxX^KNf?Rz$8BJ0CMq(W}{P3*nYr(nkDHeega|3<ajmksSE_Ci-PYpgIA2^Oe4|=g- zAJGqx6|<<SR=3TKOb0G~dTX8$XLsbVpqPuC;EUWEpL$9?<ECUiZ0(2xZW9mMZy5tM zTYpim@h9SF!K=6p1GBf^QoPIKvPZrt-F)nFTlXq=LK%cn@~c#Ac+Hs%a%1D*I1f=6 z=J59O%lP4`6P>A3PkHS|U!VM-exA8ESdD{pcd%4$KdJ+;GuP+@?!h>vSiN;Dv^CG% z9SV_YVo0c;t4oa0or}Kn=s{+%$^8$9z>s`1rnI6y70ZK^BA^?fp*vpT8CnHjnth5Y zZ@FYQ6^caaj-<L8$LNWh4L=SS+&j$xz=^_;-4Nt=2t||rNszhTja4V`wEfjVMJ0+x zVzy3ag;5$q{mhYgWno_kvWaya4MqVr2OpIgtVRMl-?C$ha47ldo!NLT+meTqkq&Ql zCn5YX8dvk{N5^xC1|$cv1Tn|*K@*z$G-6qL#9?=-XHfQ(NYS1<Y>!`SsTG|OQ`;;w zdygneUK*T4a1Hr2zONIa$Y$o~AFW7UPKFl^z8YRD#=HU?{NePj<YC`nP{4_egzfjv zMW!x$F6_5%M#P>~;?g^07?8N046zTM7*kY!Q0B}LjwW1Hzi0jFS+@-y%CTR`@^NMC zGW?hX8#{~8jPO>}j#)2^AAM&6q|_qf<u&B6tnIC&$k|5kebM_Co_CFX(4K7*-}8Yf z{-j=E4&J_DH4L%4Mq^xn?2(;~aS_{&f6=+7Pg7Q{lxgjgWPH0TSi_9=5_OuK+FExK zJrp~sIODVPW<k61O;Xc!yCQs?rI^A2cX!Xq`bI9B%~V)O-2sB{(qJL-AQ@3^lB!!< zG5$u>V#W2A-6>riQy(gXK?lOxHUPWTJ#-JzUsyI~*(mDLztpYPO&Td16OXcpVXEI3 ze+9CuRud1ahdsnG)`5NkYqcJ|gZ_D~u8*>vGcn^fM6aCSA0LxXTOSTqV<gFvb0-0Q z%a#T+?w7MEhqGyV?8(8+Qeg2*lW!M0?aXa*RTkqcCJ1m#%SFtCs_tmH+YV+;>4)k* zA7Z-6|Llk@d)ir*>AvAwwiZN@+!B5od+_75`n?O%zTSsZW?fb!LXVH8CE032xcnu2 zX4d@02m7j&qO<&b^~yh$O8YI#EKQrg43%Jw%h<R3stSY^LY^nhhfU2@5b6f&0oBUs z)LQ)$cV=|*6rXhFJ3PDnps!PnyAP7fB#(_iDI%{DKOe;Gqbfk(kLffjp@atS+V&O9 zrlN<_ckMbC*1-(A6n--aZEe`$1-zOgp|1gUV8tu4o8Ka+_%bU={X)}ryw2>6CQt0z zsg(x$!S?j2V70(jOsOo6K*5KFQPKHiIir*<i>ioW7ukuegyHAPK~sDQEj%|}#rH)J z5ySVcy(PO*u2Ch))rCcomnH(UM!QC}7}8HY?reS3oz{5;wZJ`$JD(pxzE4<f0IY)Y z$`5LSa$0j8`<{H{dCqid!_Mw-V%Qx+j!6f`Da=UzwO;Bif5O(}qlf9P`2~Ivy^9@@ za}LWk974KZW_4Ciab#4yD#T2Mg~9;jo}WID0+i6Khv%l7U|%|DnBDV9Uil9#Y^AE< zo$Ic0YZV{PeZ8Wv0O}4nJLvt1-QF!k*!_+8syNeVS62!DnP{O9ZvVrcD&qT_+^<NN zl8Xq+c>9)RrTq$1)(N67%inxtN=-R_Tjr!sn}8{VnAm1`?t=;s15qbJO>R;S4CGYE z1;5c?J@1x-STG&TEp#NAhmKnAjTBRJd}Mk^!KJ?Ah_LMp{h3)P^$0CZgPpoRKCnCN z`i0?ft3xr)xwif}-pneaGs{eqPg2hx;!@#VF=ikWDUwJj^2cc$i(jT8*`LVew)czN z<JDX7T_-eD3)UV!evWMMvXihdW6U1i!dB9G>D^~J>alys@eAq~7H<q}ewo1-eqI$i z)wO^vwULAzwE0{lK9de1LBr5&HEe{;OVvF!3^pV-=fz679JrF6@2EbnMTwZa0Bg-I zNb9cZ$b1JmFuPi5)zh9OY%c%?b7Gw!R3m~_^h6_WC?7AkZ1C#hbvG7-_4S2Ykg=nw zzJX^|&d|Tz&i`uL?KDjES&c%o(@)VqD+kxU=+t}lRkavWRGxBVh0%-aK+aG_1Wm8E zZXZ&i2gQeLh*mW)A@nt$-qZFvuBC10MGML1sDb>UmG<<9H*c%ZS*Rx?@s4EagGssg z1cAsU1DTh1<>sK{%<HERbCN!KFL#p#zC*;}4HG0rkZ^5WFv}e<eU#ytF4!YOJ(JNq zGxUw1<KiYdHF6~ULf&k+B!nSePkqt!|5zUNhvHiQTpk55^5_07KmYzB#~*4_{f~e0 z|EkScVI!sKRjlit32V>0rpcK1dth>6MrIsuXIDPucM9@_rV(cj+*A06?}@o6+_`}J zJ@$RpAv2Cy;VgDpT5gpdMm_e+=T%-mHgo>ZkNJPkXrS=0`DUO%*d<A2DaaX<@rX(G zW=4cm(?8&I%2f~j8&m#&a^n0&=HSO@$$!;rjz5-e&)IUSCG!{Dg?cco#0_1!6HCmb zC~Z+NKYfP?F8uUndkFc{Ecze(Pv(m-sWXVbNmDxfEJ<0FcWnMvdyLsHm-zXjg?SNY zzIgtjcIBTR@ju&8&rd{uK4u%|o;hk}`7_j}&&ZeWcsmfdcy;*ylZyTy3P}8q4|ab@ z@Ag0b$^YeE#{adc-RD1|^!wYI|C!+YJo)x#y7qr2`TVygIDdGiQ<IxoZCZwc8dES9 z>0fYhz16wQkiSdNLj17Fd9{5BZA+)z1I*@a&<#4&3+7J=1@8nVg|a1~Tg^ez6dxdo zgtI~qE@SBro!77=G#Tzl%$Yi^v8T2+aPMr01#;%xjK7UagBW%IcVb%YJ487%wYQ^q zIIX59Ix+Lj>*4-3jSu7-V9=peQ)HhkmuZz%nM>W9oh`a3Yznr+TYw`-;K*PsW||Hf zC$FxL08A@D96T-@p^G%&yLSX#(F6fBxeA6K6%__A$est2G|50cq7ac!99*ZL#(iiV z@_??RzcB+`3<s15rLhi7khvzPT2KP<JB0H)<Wm7C1>Xli*(Nja3;QHtp0-~ZM&5^` zT0sdb9PH>%efNNI01~gcXA^anj&4cbW-gwB0laK4yX3!mpP36(Zz>Rfl3U}jYeIkg z4tXsMr>dcw&>Pa0=t%=Kkq6z_O4a!exlmR?XQ}{qY*j&3eb9*6I6Vp=J77M9=!I^4 zr*`H$MDubtol)*PgioiRya+}{1Q9c$9gn|5QoloVRWM{fU^XyWWLgde0+PN%rof~~ zSS&e~{>a&l%Hl>pQtd|e1+=qY9V@{L?7$1Yf)}{^-J^%5VyJr%blKRSEvlXj%%)6# z;sN`e2|@Ge?~Lp-@mmxA*2HGh*m<fW`bnUy7~PF_XbSz?rOy0&6EEX_yVRL~zZEpF zt%z=NFCb<l-O!;<?3@SMTE8)&&fi&>cjR{l{H=w95HUCmF^hhF>0RtLZsP%%#{N6o z#s1E^(!k7rZ@u4IcLyhehD{q#AN;vL|J<K{{}umpfBw^c{!jn;r~mxFI==I&14X6e zrlfV2utzR2szJFR-2pQ;zYTKv2XTbU*!`nd-eP!fM9pK0u^`av;eQwC&)~@ZxNzK8 zLx8`k&j$P>AUpW@gU9_sB9^{e88FKgF49E+qGU(N6-VGjlV_2SD5j{+>-I}?RcwyK zHB~vJ*5L~OxV_~;nD&;FAJeumS^h(zVo~lY{<n)1PwX9D-E#AZ?~z{V^zin3uwUOK z|4NS~Rm7?a`evd;ZdoD=)q@hPtBq(b{itaWf4Zt-VlG>PDKr0;r;xL|_IZ(=!Jd?4 zsy_@*U0lnjvpk@ew0wtL26yHvy=i6$s6-^GuotN0_h+J!J|>#Hk9|{X#|KMOsBxRy zqO#eNj91F+{Z%JshH2~oIfBhoKM5GyA%FW}{Nq6(js3K0^vpydZjm$ytf5FyUXlRX zM!Ug$m9;NoXMfQ8-)FOvZeI-OS6KV#H7mFcL27I`Zsu(RdK5-Gj=_M`Z%w@sa9QC} zVeSqY5ws{ueMSV;9)M}J0HWr)D|#{)Mkq%0O)3&}sM3@IjV~^ryD~{dL8SC6E(O$D zjQzKDd*8dB3>5ia@aXd5G3PGb6%uy8r@todTz7#dARz+c&BK`f8@8ceL0<ljh3J3x z-~T`(m6`#b0)x&6<d<MEMgnM7*mgPc@vR*kQ;@eDtpXc9J+aMs8@=33la&kyxz(CX z0a!agoT(f3hpTCVZa9e^@SFGscI0w1DsKTnoB{dKdS)<eMkf^XC~+H=Y@x6j3j;c! z;Ou?=qsm2^FaN79?q49dx;V1*<!&mkB>f<K7`dlzFQ9!yd*_VouX_F&H6T~l*Y>dt zmt3+$)402`9CHqK=LdN8kB6uJZM(r40E8UehB@$ZO9g~`*U1!OpYd7cM`~(MYH3pq zC)&d86A!`1&c*8v#cLrFfs#c1(b|j}+%&8K{b-^T)~IeYInnY$kmCT^#?ri(0<RoB z^89w3-MJC{ijE`F`#3*fzjW8Y#Bft$Ze+1}8m}IXa6TU_0xwz-M{why@-&j<X%As| zFOc*ak_+Mc(eDvKncZ0~eXDZFjWUW`DyFzW>0_N28tr<o#odEug>H<6O+JNgT-<<B zR>1Ko)@*GH3@;3$8qmdj%YmT7-$55Nsvpph{SJ9}hoTjBiQ?Qrk4m6bcd`|0QO2*^ zreU5MFPMgHN>fgQ-EWVZOhcEgwx}yi@-5M(zbSU}E*^I$CAqwIQp(UO3!hKksyNuw zQf;H{_PoX8e!_i3E-wLEk0VQf2?7d0pFF=sOqhhfr%5-WB>N89mVG{vVk2YVA*6Lk zXvS_k)5oY)6M-_}`ms6C>Go7EG^vjawjk32Eu+SOSTc1%A0VzLn$V90F%P^)UN%ql zmgT!79~dj%xTK{aB;3LJ!TlpED?JuHIkVVtS(DDfJIzL6G4zxCydfYKue2{FI*@bU zYxncQA!i4hp@fULp)kDtavb;s0_`>6Jdle@!Z>}R>Ji~q_s7ny7i3#n9u1x1I?pC^ zYPXmAsgviK9sp^KoAlm^gJ^p|ET_{9mP&3p*}Z!j9a@zXDfd9p%#_pFTG-%2ioB4L zufIG#LqJ^f*@ykEL+OvyyO$FXwAXd)^fp+LbsTYhx&<p5iLYgA9QI1+Z1jp(JuleG zqpRU45%<_wYbN1~?cnTuBaW;xNYWw7Mb*HvkXH$ku|U$mzPW<M<+Wda)3CA6uv&}J zL;Dw!K|ci~TYb%(^UPy`?nXBRwKnbd#bcJ>gsn!O6hKkBc$Sv@!oJXz-umwILFv5( zVs%5^Lqm7oS-1m$BGGz_Vh1L<2v(R81CsL~`VJ-v#Yz@&#xLJG40R~2{2V{kwmf4N zW9gGPqdVp^snoa@FCU_HQkRxRdxdk0eF!UoE$=5Jki5y^IZoYy#FVwEM$LW~n@1a8 zR&K6^C96fB(k(cgA}{)3!sAw7ng_HTOXb-jdD0%xrI9De=ZP62hCv2X*)Q+PNc2sI z>dF{jX4*4F=s11pNobmIOR@Z3@{K6y2P|32ZE2mDiAPg8F_VM9NpUNdO*CPLQs&>M z^o@SewU05%-u5x%d6HLh_yo_&7v?ShuhP@2BL;Zu1TaDqMqZ>`p}G)Y&vPAcyX$zr z4xreRWG|-FkO!^sVzDZ(J%=?o^!A`1_IQ}n({OijBdvJlCB1uuEdz3Rw$sjd7E!q= zX`HX;u9x|%B;V?)L(=ck9gHu&`S8ZL#BPw)zjef$nuO#d&qIG`$Fkm{i2Fs4$!$zL z!m!PdA7FQ@X2v3L()QvO->RhFy^Oop#_A_LxS4Z_A2|8FzlP=iE5A#TKxN>s@*kN$ zHxvv5_luaI0D~$V*H0E(%tbHPq6!CR`KUY;8X`oaDw#eq*hE~VJ}!ch=IP1YO;hOY zgQmcNqc>h_f^SH}cZkI_IvZTLf_EoB7u}jbhj!tlaFfqqi)wBgeQ44Y&cs`eyoPSv z?x}~ai8Z1}CU$N-YJ{%IWPOLYp}$?41p$?EZ-kYI9u^`!ARADmk-`IY8dR@-_Gmnu zLtGB+_twDkw#BS2_tsVMcqQ97vsulk83GH|0V_xDCLr*>%V2=Vn5<09(SFdR!9Or? zx~^}<cW<`&Ym2K&y#;F@A{v=Ik1?*P(vK*CV5foZF$OoxB#8SCIc&CW37j7R__0ec z8|W8JIv<>>C>Jq97^F&U4Xf>#j%loIbsHecw<{?!+;>Ojb+$<JYWB>~{z0nBI-TV_ z7>9uaOVq8P$(v|7G{GfjiH~v^HHahB;P6>$`)#IH$=YtE=3d?9HMp;Bu#uYjYH>vE zY_?w8w$_y_2ecus1Wr8|NM~sUq`)?P8VsoJagBr<MC;;Y4#zvg+L=z`mgedH=s*t_ zVdwJGcec~)`2_$2ZybVw$_5_iI=FWj?oMoY5>!Bgi7MAyqdXNPf5*V;!cg|9rV7qd zLxwtSM2fPVnwR3%>@~?h?tT8-f+SdBkTNw2SrMUOHF7qzoGE8Y(Ag2638e*-N6I(T zTs`-=vrwhi0L6+PSeO#B8~F3fTjF>=VWgE$L#d1$tsad}akw3!lGL#*1X!O(RoqI2 z)4oIY7Uq*C1~#$bFdn2T0s5Y?`&}%z9>2<-sp*Gf--=IKYQ$P+Q6SXwL2mf|t;Sld z*x^#Pke+;Zt=a~eJsexM9GFWHWGakTyU<q+n=opoZ#B=8mN{~9Vr<UOt0YV9y}1U$ z*BIk3o{A7#&67?eA|k1ZlR*Mh&8d+uOEQ^urYS~`cE>=dmp#r-w7NYM75VfAFp82x zdWF=_?@Q{gzNa7j#Nyid+YJ04M4Z1Z+PH6|nmB2nkghI$Q(lcT*i?6%I)1+FOqPqc zv+Mku9&6`GL=<A5#!=PANf)P)t4Z>e8QRjv5Qr1+%hibyUx+zPa=VeL#Q?Tnm&KmC zyK&p2rj-FBP*2^>*@B<dS>(*+gKrlWY%Q^I>NO7M7FutaQo0|2(&2M3*aY_ghFv7P zO{2pm2+8R806WZ*9Eq(lA;rE$+tX5)i!`BeCEf2PO4_=gSKg}@(mq2(lcmk5oFjMy zl~=P-LnQU;AP&Emm>eo$TRJ#Z#J5B`HPhyNTEiRnER{gSoa7(z*(1_GJ3(}7#XcC{ z4Zy@kQ*4%ZDx>d2Gn~@3@kn%eED(HXhU-jWY6Fa`W;apv<7&<RypaXHgG?cO9oYe{ znxQvH9|V31g|VHjd#`wVo|*>&yboU3L;yb2;@DBvPoJK08-bAy(NfS(*deZN(iK{k z_ufM#<)syu<xK5?GD9!D{V<o6<#yD$n=I_l5687DGb?U008$^O81f*|tx*ZF>pR40 zbtJ)vSn)nk@0P;XgM^VGE5vz*Jt{dh&TY>_5{)SR&un)=ZtkTSfiD@z2EgNdhfLt; zSIu66GXw#S3E46|V5OJqZ23Cce9A#SUg0}LO?pmVV8vdomGgc*ep<pHe#Ov(oQWFG zi8TU>$Gm3yLtm$a$rg-wqgv5Z-48G&$<6Q~HxpX8ng}}kx}Uu<p@z;|T3nQVX1Mpm zaH<2fl;hnqqkTC(o-gOs23s)%ee{?7$s8&-41Xt|BJ_=}6xlL<yniz=+S0Hx0q%S- zwX-6eOYT^Q`zx^!`PP*45;1!`5DwH6v=}-+Rg9Qb5kplD%>C%^P%I_US#;V~{}KCz zrkqzQ^99JkSKlG+I&!e(9Qu=pKJFKHRGj?GA~N~AvK;yxl_fePssfz7zXC;jUTvPB zQm&^9$A-6hHoxW0P7n+@$YQu)++;Fck(@jY-AB%)8jh>JtFYvsc}o%KEu}b0#<@J$ zUVk2bs&HFgboIoQo{y+@voli|KC)%QStDnZ?B_#$1w7N8@7#ki=hI)47{vz@zCp`4 z>P7BdvyyFWJ{WSxCG+D1YXY4bkq=Ik1Dz+I9?dl^;BQ+>i$~wH&o|=L=DL=9I`!-D zrL8Jy3{ftEsyazyMR1c-)>PFs_GjzOz>cim%<rx|#8IHysBsLq{HP6Gq#oY+-Rc`x zW&e8@JqwB=(t`Lo$)@ySOeyI`NBUsdX8B+_31<X%6m>NybhaLI86S&YknWx*=Mxr2 zS|SN6LDpl6<x&})6|1K@YW4*r&Q(RD5k1HgnWKIWnv{<_+BsFvrf5FW>N<tyHWI!p za9m&Evi7%yQs+QYg=M%&cAt~M<;{`7q9QH7&B0{`pnR-TI=DjBx|8I)EM?hlz8v?e z-d%+*(g?f0`Qd2j9M8Loo5T;V5}(c-@Mj8gyw?2b&2ZK=y?k}S0Cm%`9bewF8*{@C z6E=J&+-i~{*1`BOkV5jt!#Rh^noR+<wb?m=ZyVgikgIb=7wflfp2OY;ni25GbOj<j z)&*_P5gL1>&HZ-FR=etTNBaidNcIa-PrAR%r!#aXrV;lD$u)MBAlEKyY0Cm}SLo62 z5ZS2d);X`YpR<gn9EUr6#y?QP%*o3H`*XD}s-{Hk=R99WtYM{@r`|5rbC=SbD+yZ> zNX4lU&OATMtL2{yx<&0u^QGqdq;$K>kZ155WJz43>WSS)hy}D+((47ap%Y`b!}&M& zvc{j=ygUEN3!ADTMLj`mUAi@W0yYJ0v@&##KC*&1=3ncX=@B|9%iG_g|AhI%IVMKy z-KfJ|?xzRjPBt`O?Msa}d&<2q&^3<mpELd_Pxi|r=dlfKmQ=@L(Q%aHTAt}FM4uPa zm_6U>6#ME7g7k+~6HV2!H`@#Y?5>>O;++%hUOrD?KL1(B;4|`?<Gh%Qn>4bWqgwk7 zQ1}eG(aNTu=G33h-(soM3-6t;R5Rh%x^hoXJ=GDhs=iROdV^d-SZzefmypo~3vnZh zf?>&|z&oZgbZ6d62eP!yIoo(Y`I)t7@Df{+swN&P?p2gFRarkVs>1wf`V~n<al3(X z9^FPHL+N<j&NT^tf6_e}(f`%ncLp`NcI|>F2qK6|N1}ovU8G|m8xar?QRyH=MWjTU z^p+?{5h;T7ssbV)M0zLm-a$Hs5Sp|kln_Yqd-nV7edcU^&zU)M=KMMH{$j!mVcpNO z?sc#GTGzT3SE3NRgZmwhts)UvA*34HSF#ntW1MT}tLbZw653;nz<>=2*P8^Hzd6Yn zaISqdvqEDx2)eE#Pd2dxrQ)rINs~Nf-BWSS*K_u)8)CHNg)U>c3(&n|SBlSQlQQPn zPx%z!n)##(j|~*rpav^6QiQfA#h>_}rZ4CxxL|VNmum4IwQ~iIURSms{DG^;zBS>` zCGvvPR|L$`xjqRK^*5WUbFSK4U#bC_<-&Fft!IkJewnW|eXL{2w*#0a<P(PE&}S@E zN2@hi%{h|sN9wL<WKKH6z7WvBtvVFJN~s`>m$~G1Uzv@q<4Q;xyBIXN-yyEFoUCk^ zU_TKYG`AxzZR&rKOO|bZxj;L?dFxA~UCzA+OfS_$DPU5Di{#*oJ$v~JjNosPZ-3N% zT71aE5FDf}e>sl;Y2s6S<3wRDaQv$N#mY&>qU^CYI-~k<exbr__Ie&)L5}aGkKQIO zrT*8(FTRUNO<nT8UI<B<8@6Wt3?GBgdGs5XTcXZdFb_vm_TzYjXsKfHz!6b6y<23d zH{r4B8OXJ8y-qKL{p`?J4^P!rwv%^2pirdfjS#c$qLDbSy~<|2Gg{ehd2-X=`(=|G zNGW)%eqEBmSb+McX0XKlTZ?tC&+)iw9GN=m%~*(NnIf431wW|-pmO;YC;=KDT*n|@ zsqv8}$d;tRW|~;)@(H85v0@F2(rldG3%cIy$bim@KpUX=Mj(j-;y{ux8jvRbr0WpJ z;vcA|V~y4xUH$4Bnaf{OPMvW2;-eHW2nxTS!6)OUUu5gls6FGSfcoGQ-M1zv*bqdU zl`**XT{A(EE&F-gHCf<#-9uxioW~C~Ls%fe6jAv;;>ZKlZrCT&cm5&wuZ0Km+?&oO zRCOA>g0dm+q+uoD;{&U#<-FH)-|x<b^M?9Aafh0-+Lg9yRjPKRt>dJO$X1g?EHo}I z^U~+W+8+oLzjn?QK}J#oesIAM3}+fIs$dr4PZf454cWUg8Q_oQT<z<LFdv{>d}n1O zbGA_3C|mwYWB#YcU8g%=#I#dS>w8<g(e#ljBi{|6AM#`z*#n)m@a>wh>_J}(-Wo2H zUbim7KzT@}oqQJS@Ka;w$BN9beO0dWAdTp-$@%fKvI3VJYK>r8_bqdU(X%NTU}L^G zSKwW(rat>|1=$1OWQN$?Y~x`$^eeb%CbJXYwyL@uIqVHAKk~{uhX-#put-1ZNxVUO zOc;bCFLSh+uc0wlvb3=0Yn*POD|6Q#QkJ33?6!$YBjf1@V+YR;k{bHb0vZmS>?Rjm zm*ZiN#U3qu!#WpQ7XJ7#wyPpO0QMw!WAa*WyvAlY&fJMpues<oCocV2vlolzDQILQ zeZv<n7&n*V@6<R2x@LRF@#IvWyQR(2#<s{NKIR(=$Y|CTf*dIIy=hww1E`{V#LV6d zcduI1T+N!CDL>XA+?s{tZOfL84b(lSa3X9Q>l@9WCi{}af~nUdYXxxNLpJ=pPz%@1 z@)9`O9BNHYoM+8_<v8>FxR95UaxPS+<(?C87*9{s$5jv;(AF=3<2ZwwGg9RhyXol< zpwTg76hy+kzsZz^MH?PM1;5~o-==?sB1xHmW@apx2o0!wk1Y748~miZ*n=kGXz}5Q ze|N#0STd!YVWQea(NbM#v!-jszztK-fP_#{VxZmhaUuHy0~Fp?+t4)QQJXlbXnN7v zdr)WpM}H!+&^REa(?gJifm{zUpM=JLg<#kSA^Q;kG7qUKsY%;R9G;=3Z9wd&xos3S z(e}zBAP>{^|AuPm{};L<5inP~W>l36C2enl3ZDjw@mh_5P6ujnz6i2D0D%7L$VP-S z`ap3Vc}NBD`=O|SlVoN@ce4dzB4dIU42>lyQWANj$zea~a%FcYXqt94g6zFxw{_BR z%=3h*h(VU|m@tRsjMhlc=I}jw34<Y)8t6IN48R(IV3<kbZXYewW(H5)l6c1y%l$o` z$`TbuJ)|1QceXMF7rx0HdILv(Jg5`eD5*}ytlp__>_Hs_t_(s-IWty(031WRLLz+m zFy%Z*3`93y8ZbT!I<#X+plIF$at2H$!dc`MZRHk0gUW4t&_$$|;uh=?OKL$+LfZs| z@t0E;YABWpQBs9RtmMC#v{y<5izc3t(z(xkpd}lDf!IT*K-MJH`7u%yclgqT+93FD zk08UDn=u*1{QYo-d7EfU`2&XRR7`2DQp&^B1ZA?fVWtY&-1_=dM|aWg7|G6nUyq3e z$%f<g*Osy^+Vi$tD?(^&vCm`y(=qP;q(c@$sXXGqG>Gd&3%pC43w<*|WFfV;KyujU znUjU-Eo>;4Xq<6EOdtP=jQ4C|U@x>F{Jb_ngLtWe@M(V3N<MYgrkv1q`p)%$`PdCj zznBdOHUrd#V)zoE=%T_;<yZ<@IOW=d6aqU%SBp&!-cUzoXGpNww2iG;iMAM>{g|2G zXy>E=Uw1~Ug%w_TRAOF#>7EF;zFcNMy2?rlsKOWoaF<h4!TKL4CM0#a%2N%^_c*)| zmp%Aju(p*670>sy?FzkhRh1<!5zeYzCGpRrxGami_LObvjCapi;yN*@RRDH%no#`q zyTGv8xtYE=s62Wm_qDJ6Fh{JnpR<(Pdc+a}*3XCoRA|Z!S_j$ISWFhWEL+7gM<gqG z;7sxh%e(oHGTlR(3-8_iG;OKnR5(*{h)mK5=0AS_kjlrWkto$`E1o@!vcpX=VJ5=W z1kHS~3+1QR{W2?@lm5^HhnHwx4CdkI)p1G|E>T)lao<^Kmp)##;YPsSBtTy}U*=QA z@32a>$HenU;7e$invdx$p1vNLHH@38>r{cnsIl5}A3pdYQJLsEHFq)7CiCd=rs$~- z?a${Ynh{m-1}7|_TV%~kdW1<iy>{3KzLP&Ng|p(k8#{aSX!KO^#=c}PzUCzBQx;JZ z>;4wlLVTKO-Q1kjj8$}GFLOwC;wUmqNB?<sMV(V|gI`&}pBK4P0Jdkl$OYWvj4C1c zyZ$qlJWePKV@7?saaTQf-UdRz86)PiU5hHy=IcBbwOr!uhK?VDC)=a9naY3C70UqI z_+nlDJYh2Wd>=e7f~a7G+|u&imm^hH*15QUJS!vb4mSN-w6WQZ@YJQ@vTT+?Q~Sis zYSNDd{!K;~Hofc<;%;us^~->hUbU*;9zEwiHqoC7qU1c^p-m%x97m3+Q0j+%(k%%k zWf4aKGLNR`%9zYqhoy3Yal;o{8M}Xi7Elb-{1keSpi+)~73G+|O)MGj_Fjz!S&mkK zrYDG{jd-szZwVEySEKK54bD|nJmNpgbY8BMBT?wQAn8bB7AcC%7I5xcJ-@rtye0QY zh5h5Fl__?Ke{5grGUuFn$?79ar{%MYOjl!YBxW%qo*_?@?Aq^37OXwDwrJT-KQhu& zrqTVQL0d5Q#X9t}wE`jZ^*o3L?NirgD8ZE|BNy4{7RSV*Y939;5U1~oUp7y|eogMv zvOV5Ym@ls=ZY}DezkW7xylUQk$?Ue&<tV@E>O4hJcg5POr6+I~S})&L5g}_{`VEb7 zy;Qy}(WLGfpGI8aRU5uVVzkjwKZfWRHp2j>ktHZawrf?jX;Cby)aTppZ*?mb4;f(d zdo(<VwFV@aj;;?^@TcK8Nh3=#te;MJK5BAcb`YSab%`HL#FFaTcHje0p2o{0XS9dw z`uG{i<a<qV8-*R(RCjY>hFQ~?M66v<^HNI3g+`+S!*~AG<N;8m$AJLP?VDeBuO=VX zT~dO`7sf35&`=A=Gi#93E(iyIVM!a8g)5Z2LE#|VD7By~O%My}S!!*)H_QpoHQstV zUWF9BO!o?L_BePGeY*he+3e##pO-~uoONh4I1F5rMj{1p_s_{}4V{N}G(H`o=qqj> zTVM}?u!ziw*%OkbngyQeFh9c4qFCJ5+#cNXiDcv|KJC0GQ;xYrQ|?WKO(g12qCM6y zp9fxLHH1e3_~6;xr;W>jssxvL$Q0`FreMo)nS3AT@2dJuAcd#Bv&bPA3B)YtuE7RS z>mwO79{q7<fV@>+q2fUuaKfKaTfX*+oO-B2>ligGNxO}T)H~MZbT)jBSKwjr9wUlB z-~@qXp9pOrrQyr&CdApk>`3izmfhm$nD;Ax{v~Wme4VfstpQYd((WZF-m(5_8ocN5 zJbSL~vu$;)G4T_oi~1M|O4IGk36Kb;apN|#3j_X0+MhNJ%W=4%dN&;6?h>Ql$C^FM zSBN(`zK5RaZA+Sd+m~pnlBQAjF5ZR(OQXA^TDXdwG{m4O7eP!+ueU~>(BYpHMTBCH z`rZl~q70~}Ha;L_`FoQUX;V4`u0v6_*n%Es^A}|NY;iXsx`;on*$>}XF>i2ELW+St z^>I?CmGNh?J4N!~l8e^hH6tql1%6)3O{0~hB!$7nFYtO}%Q|t%8`5RymUo@$o3R?K zF*ZiKlKxr*wRemSnDx&%iLJX}!<)?s@#L5>PtNV6O&PL@1Nvyxrx1)s<u}io^pwrH zZnKLFg`3K(ZBg%u-Mh#!cY#sn;Ffyp%MOO4R=B{T?=<bng_3ytd!7Zoa}?_JOi!{u z<<?jL_q>%f1|6EU@usN!(|r3jBD=XtCenj#KsNe4$584ToDbfLX4*v^vuMTkGJTVW zg&S`@b;ya-+PG#S9~DoR7x`AF!+)XComzE)058L^(@|~{PNq)H={v7`vIJK$=Q4|3 zI62_TbILy-2?U@cq~4pPml$z&ZW7!!bNcYoTTbQawt${MUW228>YM~00R`@>gM=9V zV`^rGM7Oxwd-2ZGZ+0qnhwB6x`4R+7Y@zEMcZS2nVA-aA5J)uzIqK1`LDZ%e?gH)5 ztKQFhKft1#bA^@XTs)R#Q12zrzIfZ3&i-7yhWZ}0NTRU+q;o}6uf)|LrrAw_j#%;| zA6QG~*;wa}v`e`;{k&alaph$Jt;KEN!&BN8YY=<s12qT(FeeEh8R!ZlQwy^1go>P0 zr!DXlIG#&D*C)l<#R`A8Qz3?Ejk4b+TC63#&#K&~B(c*2i9LjR#e#Yh*(*IWjV1Fh zWChi$d*pS6oOl=Ac)O<$LZ=DOC@#xsd@H{P$bDS~;4=QEn4XT!nOdh#<y~ZDmk4M2 z(-IeJN0+7UnqB25ah*12a3HEMrH`5ktfd|RVBG*tL3DjuJDP2YR78H}NIR-{&BD@p zPH{RX98({TG4%v)c}^^{Z<(2xIOk|R))z_$cAGC3P-h&RYR74kT0WLMeJ*aihQVuB zVTh)z?Y9kfwC|AJ3>YT=er`k)#-Z2DfQROQ-q9XtIaL&z%?sl-MaXSIsh@w+J;8;j zF_AR9u?Tt#oxJZ3moFZqESZn!aLHER>JL(U!r6J-*kH<?2uz;`Qoy5G5hx!0jvCL; z*VUcl1~0yfnA!g$QH!;6sj0h(&JAh|pM-8Z3j#P$vDZ(!!+V@I@HW)8GoJ|6qYfCw zw{1G2cfR*0-MIabk<bP;1|pdx9+xHNd|egfqB>~2lnmXpHJB=f+V#U6;4tf%XM<64 zI3`QlAhfb027_eM6Z=sc+*Wu-S){w}eh3c$W|=OzziSeSO)Vrpu;<GHvUAuu@s>lm zekb>QbsNXHVs}vbMsD@_`6D_jxqeKY><4PN;{b`{C!Llmh!?U*1&agR1<2u<i2<9d z@#Fkwf`TgK-$dwm82EdE3w9rW9&hcu<?j)<3Gb|%qqSrmS;W6e<;?g(Si3Bl|2bvx z8uK5R2<PoCk%G-1qvv3qkogSSg>TrHb~XAj;isV;x129VZVP!!#bMVge2DwVQ5H%q z708wsb9}J0DIjwIf}zaRQ*MTgrPAgRql1PVs942F$hS>Y;TVyiq^tC-_4ul~7NvVe zuh=>-)GWX{X+CSy94pPEnOg}!`S&WfPa9l`+S!`Rh#7Uc?Y$1wM6|Xd5vw<|e$p`j z`$T90LIwj!k<Sd3Y6^J23Cggr9}`(8TXdrwpNz9*hZ)Wh-4d(5<E-w0vra_j>pss8 zUyK2lFBu@c0+3jP7;oZFy3)PQjv}?bA5~RelT|Ng`egJZGIi4v9&8TKj69Hu5L@;s zL^Jvf<su+2p(Y_wTb`P}eQ>J2xZsA2Y3$Ukr%zqc?a|8J;6N+#6Cc0*Hb~icjbLW( zV)?dnPt2Ca#Ux136B=hPywzH9n4*bS8Eoa+xzIQ)oXU$C5RizI3!mJ*P?EVQxVQQk z|FzAH4aA&WabG6eC;yUzLF!0AoplfKhfP5R&$oWjyY1v|!`j;69-{M%)6jt-F^9yT z2OqFVd_(Y_-xzg>rGB~jXor`Q5|niROIwMF@xz|B=s7l~5AcaB!3sTKr_neohRzl7 zcPO;1;d+%Wa^95W^221{18>9*rOp8vV<-u&U4>Ujz#sC)R-8y{ulIS`D=Z`QS=ei~ zPi8$BXU#yqPhz*i$FzV#`4Yx7`8t}WXBh^Qt{3PQ1k!+k4=*PAR+5?q6<U`XH>Gr) zb+2pPkGo&`!>0;CVv0=$QuAJZJ?0$c$J3UVr|--aBi+}bp;=$|o2k;xCSOV0&3pP5 z3!MHbsSZLMb*u?dHnw!4SmD!dnIvR#`JC)sDo~qnq(WM1&&`&uT3{{nb=bTu=aeH| zR!?Z27rwf}FdIR=i<gcpHwYIz{gr?STQ=WBel({y%=n_+mp2@bS6}3SCWFcakq!hK zVhesE1T1=SE=oa9PbXFO?YT=1PY=SZ!hLtoM^|hTxXhF0U43rfu{_>UkghD?op7j7 zkubiGTJYm7K64`M!;Rw8qWZevB4^yeoB+W6!Qktfve@N|P8i`mUzBE<!+s!nbc7Cy z`7W}ZOO}>Gd(aP#pl(RQ1t_A_bg&4LL~fe?qtxG@_i{3G?z_HIqMp$2*KI^bISBx3 zF4l5WjqRCRCB=>q)VNJ*&*{ZNgT9|1l`}v$5R+u8Cf%21Zgeq1>fK_BU`Y>G@AvQc z4rL4aIi`ixmOH1XEsg3mY@!TrO9`gSO#UzoT8?-V{YGnnxao~@-^LOB@CL28s7B#& zwJWrt+P$*GYO%^B#m95Yu`eFC;Y>K*UobXhsG@gurj0;Z;im(n-sqE(@J!ZKL0i+C z#+y#aMDH8wwuz98tR*2<W}#SIDyUSX85IQOE#K7BB(x4v>tKp$0`~?UqaBU4{%Gzt z9oX>bLVi@c1n-BI{G@BqS2?NIIpJL?Guyq?b`&QZtu1mqy#M7amLdANW<VyBSmEn# z)zWND>&@}_>C}XM=V`e^YqakI25(rDMAl^STi+BY62{eejgG7^lOK}08!x~I;718x z3Qk*T*(MsFJKCtn#4vePiHN*Ix<uxpNH*ZPQWj#uhXxBBQ*-OR<D64(#Yrn`gd9=0 zuNxd>mYzP51fQt2dnJdM8f_w~(K}dbk#*wwKe3+sn5f`-2hX1|1prLDZ!n#dcl}JX z=5f@aC~WOoUva1+7zqv#x_>WM58)?Sy*8m;8Qod(s(G~Ssr;qa5oveBQtK>G5TUZa zA)$gO>rn)D4+2_*?N*iSb*&Ww)nJxD8nPK=1?*C%^*t}t71VqldX2)dH-;j9Zxo3^ zENZ?6hzF3}>P(R{fX;bo6%{yjaxa2Nm)Q>o`KfYfYBlnkl^zY8g{OHQ7z5`S0D=C- zSLnO<zb7XAt!4%Mg#1euQ91j}&0$3Xt5N)2rY1yHx&eC=fAu2I#lMJ#-2WsR!eapB z^^XLDjUQfZ&!vkHyJD10>WhVF#I)FevX=H_@t<_Vu-CRf>3HGHz+ey*z&NB&Lz1jo zfoudI96E^u)VUaR8Eckcy?M^MMP}o;3Oq@0utc&drM_(I?Q3xRwABsdoYG9DpXm&A zsuDU&H6RUB(`>1OV?d_~yfFEp>*Sg=dqkMrxinLlooAVq-?P9^;!Fk{z^qKu7$=kO zk&BN)#eMF7KXyD87`*zBPKnO+0^tmRL$$dutYtEN<A0)(QP+y%k9-DSe;FGW$v~g^ zvB8_H&`ZwB2O3WxWzSGGhgpQ;f|9)Z>G}HBl5!L8hO-?;4Bxy;jM3OOTmT-y2_O=p zOB1UwIu)qRH2<hO<I55=2ZHyW8b6|y-VQDooP=0>z^@Sh_)2<>&uh)%0CN^U@NqY* zDZhxxK8B4CwH@r~>bk|CBrM=DphLY1lvG?=vUuvb6<^ltUFvW6vaKzoT3FD->Qw!( zwiY|FR1_12Itj1>W|~tGFUz%3Vfrev0xqu2IS+MQl0O>{7}#<W*n{z9eGp$=lKsfN zh-L5l_g%E%w%OllmLM<*9$#X;3-Nd#_iavI9EZX#2FT#Ehyp_a=N7u^-3Uyk2o9+F z`P3`Zs<FP=Z@0`da?WqdzRY|-aUFnVfMg}k7XwH(9j~HAI8v|E`b%jmYlx0|ORU|- z&O9ReP%<-Qp&KXpxHNqr%J!lVmzXJ2pAO?ye>zy%HB9ZPZUQgdTwik#z%!iZrW&O$ zzjBp#oxGK^M5^0+MzTdbzB%p@cpb>mSiyR3m~KyTeqU#&rl(OE!#ZMJt3AxZo7Zc= z58bS5@Ipi-JhJ6vfY0ayRG_<AjQ2aC&LvG|=M!REkD*7S+V<xe`U9Ik`apGuJYf24 zL%^^AR@V#eiQ;@AN3je_0`mYV_Al#euLg($FfOa^`-U9Px$-Pe{_Q+?Z{K*RMsS|e zH=~JbJ6;C0mT;yHKs7CC16ck^X0{k^;vi9*sFS|30Z=7(z|^eW5wMM2qdQd3eCCbb zts?cgrHh{ey&_F*|A)_l{_^693hiT|x#5KodHhECy?tAIOC}c9J-rq6gEunS(!~bK zO&i3m){Dxmp>91Bz2lHF`%-H5C+d4dWtHy?YDjG`()oo3t*VbWx3Ai;zJc19k=}ym zQGj;*|9%^pvd6;ORh;Wxx-TiMeaorzQ}v!&<;2IYKlj&!o8(jt7o`wTor6<$&5(1} zs*e}!M)r~odo8=-l{|P`x?V*!*##X}BP}$#h7surT!n7OkdYL3)#WT_xLM+538vf9 zDdJ)R-qY}eL*(yki;c!S$m2F&5Fe_~BtAFY)5)RPW7?jWiS3=Uh~M*H`$oO*Wfx0g zYEt7S*<oNVOg=Zz8$^@$^Ji)@Vk9=^recI?3=Bsaov0rWPy9hKf>LFK=P>PCiQgwJ zbwADw?iib=+zDuF<_}->_cNVC{}vhicxDTay=nQv>1Uc#WM7z5RPklhM5Tn+lV!|A zFh}~N+>BFYNsGCIN?>R#SBTRouCF(~3fsjl6>Of^?d04BHK8YWbV-r~E`;ExQqN1< zwxQE=MfKV2sjV3_o2?7<&|2B_34q`G+dnoDmuOxP8!ocQSkn2<ZfuulXIDDw`Sq;h zK!$E?U1$l~V6F3l+|W$N(`wHs?n0R+&}*Q{YdTvWztRDkcGtxtTl3?#wus{Xdfo3I z2|u>#PwbjMaC?XX(?`FbSkG#Yc?Ca5s+-qC)4`ti!<_scOH0-qQD3@M%IeIjEFK&? zQjebR+(^K`n~&N}oe29_-8lBdDrjr3wVN;V!x1sDm6{H>4mWG|GfUN9hDVz|Yu>H5 z6|KVA&tZA?t1S1xvuJuK4<_uM^}vrBsFn0bC^BVDMJo0YhAKF!KJu=&qBP`u2opZX zkyCsAm@9+}4ivVrkkG+UiXHvKSLNIsZ_EBzA;pF(4Z5l`RsXQw|J^%A$3NVt;0`N1 zI{z&drYG2b8!W=4>!8#iIYfz1A~KR*e(05~3JQ4kuq@&Am0n@tU@C_HZRa^szaOd@ zH)+>QW0<%=u#0w?X;Z{sjk_`pk(zrVuw{EG@|B?0E#9@0@Cd}yDTMnT1glMSO4^4Q z(&TrNolkauHjc8tTNj<LtdqN%2HOD_Jh=WcPhM$s239$8px`SV17^m=ytd}5#UtJn zwf83s@rSzhv?kaJz3uU3I(%Z<WC(9kJ3r!VSA{o?mXi1)0&W|=`pl!1XVKU_nyd*g z1oSX}`M;}AQy8t1@5sv-AuoM&sIK#>w-_B;>#$XNW)xp|W7D(F0C$@-IuF_?nn%8T z-dM$oD_Fmlm*~87_vNWQj`t$Y{&8jiW9V#vIyFr@nx<@z;QdKg+1N{sBD(BC%5XdI zK_MMlS)Wy1mCcsG<Hp{<w|~rE@xU=rGf6`3s#~_Bo1@O<iTa8=Kju)D=+EQEp++|9 zH-_}(D&{=D<CX)y5eDb&LK?4=tW_DHv76444|-vqr;aT=zv=_N_O)ZFYk=u|1ZiKF z`Xb;DE>&)8LVH{4H3f_D<K`NyD|B*^5eCKMD;59l8ARK(np>-&#@*yxuA7%H7S~;u zM`V5K@cEL&9G<`F*s{InNUQV%gw=Sd@8nRyptDHYdHd9w)PXhTB239u&Z{}mUt~pN z-#DF!0d^h1(vN#%QqEwSYacsEoJb?0WT{LWWUqtRS3l`2mj$rA<e=Xl15DSP(NZ@< z#ps8{)vWS}x)G~=io&)S?;S3VNTCx~dQ%*C1A*Uw#j#8P0ofA7CJzbg*x3M0i}O*l zdw0BipNmuHdeb*Mzn^o=YL*UVm|to22Zgne$=VzGvVqyX+upvVvebScDtCTY?tra= zevQX<wA`>m^h6E9zqi{T6}n%6bUK_~G597*cKKq7u9DoF-mj>fC5?81${rH3Is-vi zJL1F?suVs*3n2#5?B4dF*TKzI%AK=H?Sj{+X-se6st!>8KfPg)nzD%a5SRrAq`BS! z&cHK(sCI(_S=X=L*ba`7&Qb(8ppMz&P`)V~$!YPCMBzy(Sf{V3DzvTApT;jx6i|l; z#2-Nn^*Y6*Ffxp6NQSv=(FPX}EX~u9$>c_&3R?ov?yrp{_xDb-s*dCmtZ+1oD5?K} zPMG`QY4_2~+p2#JBRZDPQf9p??X=q%gIo@P;&DWK!6PJW5#=dunhYg&*FtQ_ib+e< zPwFCvgECc49crtC)+(lB-@g6*-GA?5xvxqR3T$kdeBctN?zQho??P4uwv6M|_f7Px zCcGOr7B7eLCtH8V9F6VDH8*>56?{q^2nGS9R^std{<h~nZ!iYodHB`%cZ<*LIJVYK zYkE9v?DQT-=_CG1@&NgpztX;y{%etJHawlY25Cul1+d{ySqrw2z4njejIsVswXo$1 zh%$|f)@m()M@HBXkZt<b(9>v*^QlU6ESk>7i9=7mhx8@^bBDU?6tzh>y*JX-IKB zXkp#W-PVH<lYZ^%lKHL0Ynke_6wdYE^I8cu{TIzcnf&aUSM(MPtYQ1ko0Rs@-mAN! zJqcGXaic5`(eB$HT_%~@`1JdTG!{a8d%~-g<kZV2qmyj}B8uJ6_Q-5CdRotS781Ak zy6eDUahaI!(=FrU9PIb_j(DxqT#9-2GWOrPNDy#xGn+rYa<@Ej;<<z0<!Nm-(8cDv zp9@Cw3T8|;SJO(>tVm=$dn*bAR&>dBoVaE-XVzs~Q5&TZFLkvkU+8%yqn3J?z###+ z{v$VV3!s-t-u?NcM19BU<+Bm|?)+MBH-r}%gre0u=}f18U%c1g<)&V~GH!$Y-p$Js zdXDePMr$e2uWic(FFa01_|(qP#E1ylo$Q6I4Mhx!i}cBWkD~f*2|u^H&6{&mpWqwS z{bagpIL=o|*pJ>!?KCw=4oAB6k-**#J^jEZ{HFT0!ddq6MW)z{$^0$GQ5^IW>bFze zcLyVo>Y3sD=-W&KQp$3F2_=oONi=6<G*->dLHciPBJ^hPYTDEf5W6}=bSCF0SQ3_B z{F*BTk{aSfSrR+-{_Y3tG#%J|Cf6SsCKp4}rl>x8{~C<`I@9ig;`jD-<W}nLa7%yk zCo}QaUAi&S{3w4x?D2;aMyWb=4MtugM3ZgR;}3tdd@0oQA1QUQPV*Hga&|E3W1oKT z(B_1NsEq^f>}QCe<D8dnVDzh?f8Hihrm$b`n^>iN+D)@8vO7Q;c#R=IC~w3OGpNJ+ z7XV1;;ZM3R1k@<NYnX|kLQ`er^v@tpwZrfJv+Jp3WFB<D@d!Xce$r8;ZeRfB{Jnp* zK7Dd~-iEJ<Ru2+W*#y)$L#K=s$AQ}9UrU5Ke)6x+6W8?o{jCeEDx3EVTbceESF}~w zOrDpU8vm9>3=ae+Wc>t0VE*hIAd!hbhU5{a-~*{2?T1ubrwv@(FTS5`xq@DP#^(Bd z@sLg*&S>VS5}M>HwcEW|8GnSKt(~6<Da42>ydm3M{HUH|zVk6I$DB(<IL&9<U<pd! zE^tO-hFic&g%YJHZb4Sq%9wCpDk~Q7SzD`j+%7MwARZL2?3@Ew#+^}+RcU_!Zaa-2 z7$I8Rum*=GfYfmQW){U50X#^2u#X7s1rb7G2Lat1+LZI(fxGZ~)fgH>C!o|YF^FiB zX-5pX&~$+TXF@%|&`TI%NE;Bb=%YP>V3Y`X)MGdV{-Oa*EQWLk>;O<J{Lz%-?||wt z@&}4`%H=4)t`G#ii5@5@FW3W(P5jM}T_yl@Y4QQYR#PDhJPW8*pj>bXL3xYFuRyH| z;StM-Yao2O&nPWuA4TF2`ANsAO%V~41I%i`(T?f>`VgkOXn=yQ7=&15w*hXqz-A`M zAzDt(XB{$30&4>!DEc4*s~dC|IDz6&v|Es|Y4k1!39<u8VgJR<Y}%O^KE#mqK2UA} zNGJ=1hc3TciDASWQ0X@ictHs5attX{33+%DP~?CDM;d6&$>s1k1$2<r7xs&lfYbfs z@f%Zth5lmYua93tz?8}e;MPdMaP~cgD(X=<J7sWL;}<J|g2F3oN7gQP1MnnYLr4X* zG=LiNPb<~y0c_?Ee-MQU07718(=J4d(}EG(06N{xbeLvI3xY7w>~?@{^$#Qe&wXSV zC=YaU0&gC5AP!g_Ow}&}K7fLM(#amY{ngHY`V8t5-~_+g`McAE+e82_A5BRB`tQ_f zpT>a?G0i{eQYc-&IP@=W106;D;?IDszq-x;<lpE2<KF*q?_WdUf86^&(fiNC^FPs> zRPdkZ{r^pJVq%?J46_&*+<eo$MXBG|$#GCv;q6#(OJ|+fWBUD+{Ab!C>H`grD=$fc z0*$|KvyOdp9Q;>urHGhMQDH<nWRt4eysxU5TX);Cp8nd>h4*c5Io$kSb2zbPrQzh{ zWOrnGTC3z{FiZH6&w?-5j_E^lpD5q&bv^r4_rYw{1Z=Kych7FpHkmWHRf8=TZn?fT dL%jiLP&~k~{G%oWNDcnq^yU7)_}iaT{{_P!48s5b literal 0 HcmV?d00001 diff --git a/frontend/app_flowy/packages/appflowy_board/example/lib/multi_board_list_example.dart b/frontend/app_flowy/packages/appflowy_board/example/lib/multi_board_list_example.dart index 1decf21063..c0fc62f8e2 100644 --- a/frontend/app_flowy/packages/appflowy_board/example/lib/multi_board_list_example.dart +++ b/frontend/app_flowy/packages/appflowy_board/example/lib/multi_board_list_example.dart @@ -66,7 +66,7 @@ class _MultiBoardListExampleState extends State<MultiBoardListExample> { padding: const EdgeInsets.symmetric(vertical: 30, horizontal: 20), child: AFBoard( dataController: boardDataController, - footBuilder: (context, columnData) { + footerBuilder: (context, columnData) { return AppFlowyColumnFooter( icon: const Icon(Icons.add, size: 20), title: const Text('New'), diff --git a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board.dart b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board.dart index a565838da4..96874a1425 100644 --- a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board.dart +++ b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board.dart @@ -56,7 +56,7 @@ class AFBoard extends StatelessWidget { final AFBoardColumnHeaderBuilder? headerBuilder; /// - final AFBoardColumnFooterBuilder? footBuilder; + final AFBoardColumnFooterBuilder? footerBuilder; /// final AFBoardDataController dataController; @@ -78,7 +78,7 @@ class AFBoard extends StatelessWidget { required this.dataController, required this.cardBuilder, this.background, - this.footBuilder, + this.footerBuilder, this.headerBuilder, this.scrollController, this.scrollManager, @@ -112,7 +112,7 @@ class AFBoard extends StatelessWidget { delegate: phantomController, columnConstraints: columnConstraints, cardBuilder: cardBuilder, - footBuilder: footBuilder, + footBuilder: footerBuilder, headerBuilder: headerBuilder, phantomController: phantomController, onReorder: dataController.moveColumn, From bfebea7b31d2b1ccaaae8b5e4cf7d2acee249164 Mon Sep 17 00:00:00 2001 From: appflowy <annie@appflowy.io> Date: Mon, 5 Sep 2022 20:24:33 +0800 Subject: [PATCH 6/7] fix: add new created row --- .../presentation/card/board_text_cell.dart | 1 - .../flowy-grid/src/services/block_manager.rs | 2 +- .../src/services/grid_view_editor.rs | 2 ++ .../src/services/group/controller.rs | 1 + .../controller_impls/checkbox_controller.rs | 18 +++++++++++------- .../controller_impls/default_controller.rs | 2 ++ .../multi_select_controller.rs | 8 +++++++- .../single_select_controller.rs | 6 +++++- 8 files changed, 29 insertions(+), 11 deletions(-) diff --git a/frontend/app_flowy/lib/plugins/board/presentation/card/board_text_cell.dart b/frontend/app_flowy/lib/plugins/board/presentation/card/board_text_cell.dart index 02ab521222..99be8cdb3d 100644 --- a/frontend/app_flowy/lib/plugins/board/presentation/card/board_text_cell.dart +++ b/frontend/app_flowy/lib/plugins/board/presentation/card/board_text_cell.dart @@ -4,7 +4,6 @@ import 'package:app_flowy/plugins/grid/presentation/widgets/cell/cell_builder.da import 'package:flowy_infra_ui/style_widget/text.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; - import 'board_cell.dart'; import 'define.dart'; diff --git a/frontend/rust-lib/flowy-grid/src/services/block_manager.rs b/frontend/rust-lib/flowy-grid/src/services/block_manager.rs index 9619bf52ab..800348fb97 100644 --- a/frontend/rust-lib/flowy-grid/src/services/block_manager.rs +++ b/frontend/rust-lib/flowy-grid/src/services/block_manager.rs @@ -107,7 +107,7 @@ impl GridBlockManager { let editor = self.get_editor_from_row_id(&changeset.row_id).await?; let _ = editor.update_row(changeset.clone()).await?; match editor.get_row_rev(&changeset.row_id).await? { - None => tracing::error!("Internal error: can't find the row with id: {}", changeset.row_id), + None => tracing::error!("Update row failed, can't find the row with id: {}", changeset.row_id), Some(row_rev) => { let row_pb = make_row_from_row_rev(row_rev.clone()); let block_order_changeset = GridBlockChangesetPB::update(&editor.block_id, vec![row_pb]); diff --git a/frontend/rust-lib/flowy-grid/src/services/grid_view_editor.rs b/frontend/rust-lib/flowy-grid/src/services/grid_view_editor.rs index ab8e8bd41f..30ff689027 100644 --- a/frontend/rust-lib/flowy-grid/src/services/grid_view_editor.rs +++ b/frontend/rust-lib/flowy-grid/src/services/grid_view_editor.rs @@ -96,6 +96,8 @@ impl GridViewRevisionEditor { None => Some(0), Some(_) => None, }; + + self.group_controller.write().await.did_create_row(row_pb, group_id); let inserted_row = InsertedRowPB { row: row_pb.clone(), index, diff --git a/frontend/rust-lib/flowy-grid/src/services/group/controller.rs b/frontend/rust-lib/flowy-grid/src/services/group/controller.rs index 5825596aaa..75bc16f3b2 100644 --- a/frontend/rust-lib/flowy-grid/src/services/group/controller.rs +++ b/frontend/rust-lib/flowy-grid/src/services/group/controller.rs @@ -15,6 +15,7 @@ use std::sync::Arc; // a new row. pub trait GroupController: GroupControllerSharedOperation + Send + Sync { fn will_create_row(&mut self, row_rev: &mut RowRevision, field_rev: &FieldRevision, group_id: &str); + fn did_create_row(&mut self, row_pb: &RowPB, group_id: &str); } pub trait GroupGenerator { diff --git a/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/checkbox_controller.rs b/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/checkbox_controller.rs index 3f7943239e..c95a6e4af2 100644 --- a/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/checkbox_controller.rs +++ b/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/checkbox_controller.rs @@ -28,11 +28,11 @@ impl GroupAction for CheckboxGroupController { } fn can_group(&self, content: &str, cell_data: &Self::CellDataType) -> bool { - return if cell_data.is_check() { + if cell_data.is_check() { content == CHECK } else { content == UNCHECK - }; + } } fn add_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec<GroupChangesetPB> { @@ -46,11 +46,9 @@ impl GroupAction for CheckboxGroupController { changeset.inserted_rows.push(InsertedRowPB::new(row_pb.clone())); group.add_row(row_pb); } - } else { - if is_contained { - changeset.deleted_rows.push(row_rev.id.clone()); - group.remove_row(&row_rev.id); - } + } else if is_contained { + changeset.deleted_rows.push(row_rev.id.clone()); + group.remove_row(&row_rev.id); } if !changeset.is_empty() { changesets.push(changeset); @@ -97,6 +95,12 @@ impl GroupController for CheckboxGroupController { } } } + + fn did_create_row(&mut self, row_pb: &RowPB, group_id: &str) { + if let Some(group) = self.group_ctx.get_mut_group(group_id) { + group.add_row(row_pb.clone()) + } + } } pub struct CheckboxGroupGenerator(); diff --git a/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/default_controller.rs b/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/default_controller.rs index 938bdd127e..2489df8af2 100644 --- a/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/default_controller.rs +++ b/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/default_controller.rs @@ -77,4 +77,6 @@ impl GroupControllerSharedOperation for DefaultGroupController { impl GroupController for DefaultGroupController { fn will_create_row(&mut self, _row_rev: &mut RowRevision, _field_rev: &FieldRevision, _group_id: &str) {} + + fn did_create_row(&mut self, _row_rev: &RowPB, _group_id: &str) {} } diff --git a/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/multi_select_controller.rs b/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/multi_select_controller.rs index 2d8139bc73..8d18fa2a83 100644 --- a/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/multi_select_controller.rs +++ b/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/multi_select_controller.rs @@ -1,4 +1,4 @@ -use crate::entities::GroupChangesetPB; +use crate::entities::{GroupChangesetPB, RowPB}; use crate::services::cell::insert_select_option_cell; use crate::services::field::{MultiSelectTypeOptionPB, SelectOptionCellDataPB, SelectOptionCellDataParser}; use crate::services::group::action::GroupAction; @@ -67,6 +67,12 @@ impl GroupController for MultiSelectGroupController { } } } + + fn did_create_row(&mut self, row_pb: &RowPB, group_id: &str) { + if let Some(group) = self.group_ctx.get_mut_group(group_id) { + group.add_row(row_pb.clone()) + } + } } pub struct MultiSelectGroupGenerator(); diff --git a/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/single_select_controller.rs b/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/single_select_controller.rs index 5d2ddb9bd9..25da9eec17 100644 --- a/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/single_select_controller.rs +++ b/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/single_select_controller.rs @@ -65,10 +65,14 @@ impl GroupController for SingleSelectGroupController { Some(group) => { let cell_rev = insert_select_option_cell(group.id.clone(), field_rev); row_rev.cells.insert(field_rev.id.clone(), cell_rev); - group.add_row(RowPB::from(row_rev)); } } } + fn did_create_row(&mut self, row_pb: &RowPB, group_id: &str) { + if let Some(group) = self.group_ctx.get_mut_group(group_id) { + group.add_row(row_pb.clone()) + } + } } pub struct SingleSelectGroupGenerator(); From 4a21a10b948c87312d51ca5c2b3ca5005986285e Mon Sep 17 00:00:00 2001 From: appflowy <annie@appflowy.io> Date: Mon, 5 Sep 2022 21:20:10 +0800 Subject: [PATCH 7/7] fix: editing cell height when editing row contains multi text cell --- .../board/presentation/card/board_cell.dart | 16 ++++++++++------ .../plugins/board/presentation/card/card.dart | 4 ++-- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/frontend/app_flowy/lib/plugins/board/presentation/card/board_cell.dart b/frontend/app_flowy/lib/plugins/board/presentation/card/board_cell.dart index 580ebe6c5e..e8a0406e0f 100644 --- a/frontend/app_flowy/lib/plugins/board/presentation/card/board_cell.dart +++ b/frontend/app_flowy/lib/plugins/board/presentation/card/board_cell.dart @@ -14,33 +14,37 @@ class EditableCellNotifier { } class EditableRowNotifier { - Map<EditableCellId, EditableCellNotifier> cells = {}; + final Map<EditableCellId, EditableCellNotifier> _cells = {}; void insertCell( GridCellIdentifier cellIdentifier, EditableCellNotifier notifier, ) { - cells[EditableCellId.from(cellIdentifier)] = notifier; + _cells[EditableCellId.from(cellIdentifier)] = notifier; } void becomeFirstResponder() { - for (final notifier in cells.values) { + for (final notifier in _cells.values) { notifier.becomeFirstResponder.notify(); } } void resignFirstResponder() { - for (final notifier in cells.values) { + for (final notifier in _cells.values) { notifier.resignFirstResponder.notify(); } } + void clear() { + _cells.clear(); + } + void dispose() { - for (final notifier in cells.values) { + for (final notifier in _cells.values) { notifier.resignFirstResponder.notify(); } - cells.clear(); + _cells.clear(); } } diff --git a/frontend/app_flowy/lib/plugins/board/presentation/card/card.dart b/frontend/app_flowy/lib/plugins/board/presentation/card/card.dart index 924faef0a5..9d5aefd9bc 100644 --- a/frontend/app_flowy/lib/plugins/board/presentation/card/card.dart +++ b/frontend/app_flowy/lib/plugins/board/presentation/card/card.dart @@ -89,20 +89,20 @@ class _BoardCardState extends State<BoardCard> { List<GridCellIdentifier> cells, ) { final List<Widget> children = []; + rowNotifier.clear(); cells.asMap().forEach( (int index, GridCellIdentifier cellId) { final cellNotifier = EditableCellNotifier(); Widget child = widget.cellBuilder.buildCell( widget.groupId, cellId, - widget.isEditing, + index == 0 ? widget.isEditing : false, cellNotifier, ); if (index == 0) { rowNotifier.insertCell(cellId, cellNotifier); } - child = Padding( key: cellId.key(), padding: const EdgeInsets.only(left: 4, right: 4),