Repository: jorgehpo/notebookJS Branch: main Commit: 5a930661420b Files: 33 Total size: 216.3 KB Directory structure: gitextract_k0oa75uh/ ├── .gitignore ├── Examples/ │ ├── 1_HelloWorld/ │ │ └── HelloWorld.ipynb │ ├── 2_SimpleD3_Circle/ │ │ ├── SimpleD3_Circle.ipynb │ │ └── draw_circle_lib.js │ ├── 3_RadialBarChart/ │ │ ├── RadialBarChart.ipynb │ │ ├── energy.csv │ │ ├── radial_bar.css │ │ └── radial_bar_lib.js │ ├── 4_Bidirectional_Comm/ │ │ ├── Bar_Chart_Bidirectional_Comm.ipynb │ │ └── bar_chart_lib.js │ ├── 5_Webpack_BaseballAnnotator_Bidirectional/ │ │ ├── BaseballAnnotator.ipynb │ │ └── BaseballVisualizer/ │ │ ├── js/ │ │ │ ├── DraggableTimeline.js │ │ │ ├── PlayDiagram.js │ │ │ ├── TrajectoryAnnotator.js │ │ │ ├── helpers.js │ │ │ └── index.js │ │ ├── package.json │ │ ├── play_annotated.csv │ │ └── webpack.config.js │ ├── 6_HelloWorld_Bidirectional/ │ │ └── HelloWorld_Bidirectional.ipynb │ └── 7_D3_scatterplot/ │ ├── D3_Scatter.ipynb │ ├── Prices.csv │ └── scatterplot_lib.js ├── Images/ │ └── notebookJS.ai ├── LICENSE ├── MANIFEST.in ├── README.md ├── notebookjs/ │ ├── __init__.py │ ├── _comm.py │ ├── _display.py │ └── resources/ │ ├── CommAPI.js │ └── template.html └── setup.py ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ *.egg-info .DS_Store __pycache__ build dist node_modules .ipynb_checkpoints ================================================ FILE: Examples/1_HelloWorld/HelloWorld.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Hello World\n", "\n", "Adding a text element to a Notebook output cell using plain JavaScript" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from notebookjs import execute_js" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Writing the js function" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "helloworld_js = \"\"\"\n", "function helloworld(div_id, data){\n", " document.querySelector(div_id).textContent=data.text;\n", "}\n", "\"\"\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Running the code" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "execute_js(helloworld_js, \"helloworld\", {\"text\": \"Hello World\"})" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.5" } }, "nbformat": 4, "nbformat_minor": 4 } ================================================ FILE: Examples/2_SimpleD3_Circle/SimpleD3_Circle.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Simple D3 Circle\n", "\n", "This notebook shows how to load D3js from an URL and draw a simple circle in a notebook" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from notebookjs import execute_js" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Setting up the JavaScript libraries\n", "\n", "We are using two JavaScript libraries: D3 and a local \"draw_circle_lib\" library.\n", "\n", "- D3 is loaded from the web (notebookJS takes care of downloading files from URLs). \n", "\n", "- draw_circle_lib is loaded from a local file.\n", "\n", "**Note that we are using D3 V3.**\n", "More recent versions of D3 use ES6 and cannot be directly loaded in the notebook script tag. We recommend using a javascript build tool such as babel + webpack to use more modern libraries." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "with open(\"./draw_circle_lib.js\", \"r\") as f:\n", " draw_circle_lib = f.read()\n", " \n", "d3_lib_url = \"https://d3js.org/d3.v3.min.js\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is the D3 function to draw a circle: " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(draw_circle_lib)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Running the code" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "execute_js([d3_lib_url, draw_circle_lib], \"draw_circle\", {\"color\": \"#4682B4\"})" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.5" } }, "nbformat": 4, "nbformat_minor": 4 } ================================================ FILE: Examples/2_SimpleD3_Circle/draw_circle_lib.js ================================================ function draw_circle(id, data){ d3.select(id) .append("div") .style("width", "50px") .style("height", "50px") .style("background-color", data.color) .style("border-radius", "50px") } ================================================ FILE: Examples/3_RadialBarChart/RadialBarChart.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Radial Bar Chart\n", "\n", "We port the Radial Bar Chart implemented in D3 to a Jupyter Notebook.\n", "Code adapted from https://bl.ocks.org/AntonOrlov/6b42d8676943cc933f48a43a7c7e5b6c" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Loading the radial bar chart code. \n", "\n", "We load both js and css scripts" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "d3_lib_url = \"https://d3js.org/d3.v3.min.js\"\n", "\n", "with open(\"radial_bar.css\", \"r\") as f:\n", " radial_bar_css = f.read()\n", " \n", "with open (\"radial_bar_lib.js\", \"r\") as f:\n", " radial_bar_lib = f.read()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Loading data" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import pandas as pd\n", "energy = pd.read_csv(\"energy.csv\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Plotting the bar chart \n", "\n", "Radial Bar Chart of energy consumption over five months" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from notebookjs import execute_js\n", "execute_js(library_list=[d3_lib_url, radial_bar_lib], main_function=\"radial_bar\", \n", " data_dict=energy.to_dict(orient=\"records\"), css_list=[radial_bar_css])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.5" } }, "nbformat": 4, "nbformat_minor": 4 } ================================================ FILE: Examples/3_RadialBarChart/energy.csv ================================================ name,value Jan,432 Feb,340 Mar,382 Apr,398 May,410 ================================================ FILE: Examples/3_RadialBarChart/radial_bar.css ================================================ body { font: 12px sans-serif; } svg { margin: 0px auto; display: block; } path.arc { opacity: 0.9; transition: opacity 0.5s; } path.arc:hover { opacity: 0.7; } .axis line, .axis circle { stroke: #cccccc; stroke-width: 1px } .axis circle { fill: none; } .r.axis text { text-anchor: end } .tooltip { position: absolute; display: none; background: rgba(0, 0, 0, 0.6); border-radius: 3px; box-shadow: -3px 3px 15px #888; color: white; padding: 6px; } ================================================ FILE: Examples/3_RadialBarChart/radial_bar_lib.js ================================================ function radial_bar(div_id, data){ // Radial Bar Chart // Code adapted from: https://bl.ocks.org/AntonOrlov/6b42d8676943cc933f48a43a7c7e5b6c // We have changed the data loading lines (to receive the parameter data) and ported the code to D3 V3. const width = 960, height = 500, chartRadius = height / 2 - 40; const color = d3.scale.category10(); let svg = d3.select(div_id).append('svg') .attr('width', width) .attr('height', height) .append('g') .attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')'); let tooltip = d3.select('body').append('div') .attr('class', 'tooltip'); const PI = Math.PI, arcMinRadius = 10, arcPadding = 10, labelPadding = -5, numTicks = 10; let scale = d3.scale.linear() .domain([0, d3.max(data, d => d.value) * 1.1]) .range([0, 2 * PI]); let ticks = scale.ticks(numTicks).slice(0, -1); let keys = data.map((d, i) => d.name); //number of arcs const numArcs = keys.length; const arcWidth = (chartRadius - arcMinRadius - numArcs * arcPadding) / numArcs; let arc = d3.svg.arc() .innerRadius((d, i) => getInnerRadius(i)) .outerRadius((d, i) => getOuterRadius(i)) .startAngle(0) .endAngle((d, i) => scale(d)) let radialAxis = svg.append('g') .attr('class', 'r axis') .selectAll('g') .data(data) .enter().append('g'); radialAxis.append('circle') .attr('r', (d, i) => getOuterRadius(i) + arcPadding); radialAxis.append('text') .attr('x', labelPadding) .attr('y', (d, i) => -getOuterRadius(i) + arcPadding) .text(d => d.name); let axialAxis = svg.append('g') .attr('class', 'a axis') .selectAll('g') .data(ticks) .enter().append('g') .attr('transform', d => 'rotate(' + (rad2deg(scale(d)) - 90) + ')'); axialAxis.append('line') .attr('x2', chartRadius); axialAxis.append('text') .attr('x', chartRadius + 10) .style('text-anchor', d => (scale(d) >= PI && scale(d) < 2 * PI ? 'end' : null)) .attr('transform', d => 'rotate(' + (90 - rad2deg(scale(d))) + ',' + (chartRadius + 10) + ',0)') .text(d => d); //data arcs let arcs = svg.append('g') .attr('class', 'data') .selectAll('path') .data(data) .enter().append('path') .attr('class', 'arc') .style('fill', (d, i) => color(i)) arcs.transition() .delay((d, i) => i * 200) .duration(1000) .attrTween('d', arcTween); arcs.on('mousemove', showTooltip) arcs.on('mouseout', hideTooltip) function arcTween(d, i) { let interpolate = d3.interpolate(0, d.value); return t => arc(interpolate(t), i); } function showTooltip(d) { tooltip.style('left', (d3.event.pageX + 10) + 'px') .style('top', (d3.event.pageY - 25) + 'px') .style('display', 'inline-block') .html(d.value); } function hideTooltip() { tooltip.style('display', 'none'); } function rad2deg(angle) { return angle * 180 / PI; } function getInnerRadius(index) { return arcMinRadius + (numArcs - (index + 1)) * (arcWidth + arcPadding); } function getOuterRadius(index) { return getInnerRadius(index) + arcWidth; } } ================================================ FILE: Examples/4_Bidirectional_Comm/Bar_Chart_Bidirectional_Comm.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Bar chart example with bidirectional communication" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from notebookjs import execute_js" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Loading libraries" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "d3_lib_url = \"https://d3js.org/d3.v3.min.js\"\n", "\n", "with open(\"bar_chart_lib.js\", \"r\") as f:\n", " bar_chart_lib = f.read()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### The bar chart displays the array data in order" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "data_dict = {\n", " \"array\": [1,2,3]\n", "}\n", "execute_js(library_list=[d3_lib_url, bar_chart_lib], main_function=\"bar_chart\", data_dict=data_dict)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Set up data update callback" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import random\n", "\n", "def random_array(data):\n", " # Makes an array of random numbers\n", " n = data[\"n\"]\n", " return {'array': [random.random() for x in range(n)]}\n", "\n", "callbacks = {'random_array': random_array}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Update the bar chart every 2 seconds using callback" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "update_func_js = \"\"\"\n", "function set_update(div_id, _){ \n", " comm = new CommAPI(\"random_array\", (data) => {\n", " d3.select(div_id).selectAll(\"*\").remove();\n", " bar_chart(div_id, data);\n", " });\n", " comm.call({n: 5})\n", " setInterval(function(){ comm.call({n: 5}) }, 2000);\n", "}\n", "\"\"\"\n", "\n", "execute_js(library_list=[d3_lib_url, bar_chart_lib, update_func_js], main_function=\"set_update\", callbacks=callbacks)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.5" } }, "nbformat": 4, "nbformat_minor": 4 } ================================================ FILE: Examples/4_Bidirectional_Comm/bar_chart_lib.js ================================================ function bar_chart(div_id, data_dict){ const data = data_dict.array; const div = d3.select(div_id) const height = data.length * 20; const scaleY = d3.scale.ordinal() .domain(d3.range(data.length)) .rangeRoundBands([0, height], .1); const scaleX = d3.scale.linear() .domain(d3.extent(data)) .range([1, 120]); const svg = div.append("svg") .style("width", "120px") .style("height", height+"px"); svg.selectAll("rect") .data(data) .enter() .append("rect") .attr("x", 0) .attr("y", (d, i) => scaleY(i)) .attr("width", d => scaleX(d)) .attr("height", scaleY.rangeBand()); } ================================================ FILE: Examples/5_Webpack_BaseballAnnotator_Bidirectional/BaseballAnnotator.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Baseball Visualizer and Annotator - Bidirectional communication" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This visualization shows an interactive chart that displays baseball game trajectories. The user can control the progress of the play using a slider. Furthermore, the user can select a player or the ball to edit its trajectory (either clicking on the field, or using the button \"Clear trajectory\"). Visualization based on the paper HistoryTracker (Ono et al, 2019)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Loading code bundle (built with Webpack)\n", "\n", "Source code for the bundle is in BaseballVisualizer/js.\n", "\n", "To build the library from scratch, run\n", "\n", "```\n", "cd BaseballVisualizer\n", "npm install\n", "npm run build\n", "```" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "with open(\"BaseballVisualizer/build/baseballvisualizer.js\", \"r\") as f:\n", " code_bundle = f.read()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Loading the data (baseball play trajectory in CSV)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import pandas as pd\n", "play_csv = pd.read_csv(\"./BaseballVisualizer/play_annotated.csv\")\n", "data_dict = {'tracking': play_csv.to_json(orient=\"records\")}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Setting up callback\n", "\n", "The callback function will set the received_trajectory variable when the user clicks the button \"Submit\"" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "received_trajectory = None\n", "\n", "def receive_trajectory(data):\n", " global received_trajectory \n", " received_trajectory = data['trajectory']\n", " return {\"received\": True}\n", " \n", "callbacks = {'submit_trajectory': receive_trajectory}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In javascript, the callback call is done in BaseballVisualizer/js/TrajectoryAnnotator.js\n", "\n", "```Javascript\n", "function submitTrajectoryToServer(tracking){\n", " const alert_sent = ()=>{alert(\"Trajectory sent to Jupyter Notebook.\")};\n", " let comm = new CommAPI(\"submit_trajectory\", alert_sent)\n", "\n", " // Send data\n", " comm.call({'trajectory': tracking})\n", "}\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Rendering Visualization" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from notebookjs import execute_js\n", "\n", "execute_js(library_list=code_bundle, \n", " main_function=\"baseballvisualizer.renderBaseballAnnotator\", \n", " data_dict=data_dict, \n", " callbacks=callbacks)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "received_trajectory" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.5" } }, "nbformat": 4, "nbformat_minor": 4 } ================================================ FILE: Examples/5_Webpack_BaseballAnnotator_Bidirectional/BaseballVisualizer/js/DraggableTimeline.js ================================================ import React from "react"; import PropTypes from 'prop-types'; import * as d3 from 'd3'; import Slider from 'rc-slider'; import 'rc-slider/assets/index.css'; export default class DraggableTimeline extends React.Component { shouldComponentUpdate(nextProps, nextState) { if (this.props.tracking === nextProps.tracking) { return false; } return true; } render () { const tracking = this.props.tracking; const annotatedPoints = {}; tracking.forEach(({x, y, t}, idx) => { annotatedPoints[t] = {}; }); const createSliderWithTooltip = Slider.createSliderWithTooltip; const SliderTP = createSliderWithTooltip(Slider); return (
{this.props.onDragTime(value)}} marks={annotatedPoints} activeDotStyle={{ borderColor: 'steelblue' }} dotStyle={{borderColor: 'steelblue' }} trackStyle={{ backgroundColor: 'steelblue' }} railStyle={{ backgroundColor: 'steelblue' }} width={this.props.width} />
); } } DraggableTimeline.propTypes = { width: PropTypes.number.isRequired, height: PropTypes.number.isRequired, tracking: PropTypes.array, playerHead: PropTypes.number, margin: PropTypes.object, onDragTime: PropTypes.func.isRequired, trackingDuration: PropTypes.number.isRequired, }; DraggableTimeline.defaultProps = { width: 400, height: 10, margin: {top:10, bottom: 10, left: 10, right:10} }; ================================================ FILE: Examples/5_Webpack_BaseballAnnotator_Bidirectional/BaseballVisualizer/js/PlayDiagram.js ================================================ import React, { Component } from 'react'; import PropTypes from "prop-types"; import { mapFieldSVG, mapSVGField, constants, mapFieldSVGY, mapFieldSVGX, interpolatePositions } from './helpers'; import $ from "jquery"; import * as d3 from "d3"; import fieldsvg from "./field.svg"; export default class PlayDiagram extends Component { render() { let ball = null; let line = d3.line() .x((d) => { return mapFieldSVGX(d.x); }) .y((d) => { return mapFieldSVGY(d.y); }); const lineElems = []; const endElems = []; constants.gameElements.forEach( elem => { if (this.props.tracking && this.props.tracking[elem] && this.props.tracking[elem].length > 0){ const tracking = this.props.tracking; const newTrackingElem = []; for (let i = 0; i < tracking[elem].length; ++i){ if (tracking[elem][i].t > this.props.playUpTo){ break; } newTrackingElem.push(tracking[elem][i]); } const lastPos = interpolatePositions(this.props.playUpTo, tracking[elem]); newTrackingElem.push( {...lastPos, t: this.props.playUpTo} ); lineElems.push( ); if (elem === this.props.annotationElem) { endElems.push(); } else { endElems.push( ); } } }); return ( { let container = $("#playDiagramBallSVG").get(0).getBoundingClientRect(); let x = evt.clientX - container.left; let y = evt.clientY - container.top; const scaleX = 250/this.props.width; const scaleY = 250/this.props.height; let mapped = mapSVGField([x * scaleX, y * scaleY]); this.props.onClick(mapped); }}> {lineElems} {endElems} {ball} ); } } PlayDiagram.propTypes = { width: PropTypes.number.isRequired, height: PropTypes.number.isRequired, onClick: PropTypes.func, playUpTo: PropTypes.number, tracking: PropTypes.object, annotationElem: PropTypes.string }; PlayDiagram.defaultProps = { width: 500, height: 500 }; ================================================ FILE: Examples/5_Webpack_BaseballAnnotator_Bidirectional/BaseballVisualizer/js/TrajectoryAnnotator.js ================================================ import React, { Component } from 'react'; import PlayDiagram from './PlayDiagram'; import {interpolatePositions, constants, convert_data_format} from './helpers'; import {Container, Row, Col, Button} from 'react-bootstrap'; import 'rc-select/assets/index.less'; import DraggableTimeline from "./DraggableTimeline"; import PropTypes from 'prop-types'; function submitTrajectoryToServer(tracking){ const alert_sent = ()=>{alert("Trajectory sent to Jupyter Notebook.")}; let comm = new CommAPI("submit_trajectory", alert_sent) // Send data comm.call({'trajectory': tracking}) } function convertRawTrackingToTrajectory(rawTracking) { if (rawTracking === null) return []; let tracking = Object.create(null); constants.gameElements.forEach(element => { tracking[element] = []; for (let i = 0; i < rawTracking.length; ++i) { if (rawTracking[i][element + "_x"] !== null) { tracking[element].push({ x: rawTracking[i][element + "_x"], y: rawTracking[i][element + "_y"], t: rawTracking[i]["time_elapsed_sec"] }); } } }); return tracking; } export default class TrajectoryAnnotator extends Component { constructor(props){ super(props); this.state = { tracking: convertRawTrackingToTrajectory(this.props.tracking), trackingDuration: this.props.tracking[this.props.tracking.length-1].time_elapsed_sec, playerHead: 0, curTracking: "B" }; this.clickPlayDiagram = this.clickPlayDiagram.bind(this); } clickPlayDiagram(position) { const curTracking = this.state.curTracking; let newTracking = this.state.tracking[curTracking].filter(annotation => { return Math.abs(annotation.t - this.state.playerHead) > 1e-1; }); newTracking.push({ x: position[0], y: position[1], t: this.state.playerHead }); const allTrackings = { ...this.state.tracking, }; allTrackings[curTracking] = newTracking; this.setState({tracking: allTrackings}); } render() { return (
Selected Position
{ constants.gameElements.map((elem) => { return }) }
{this.setState({playerHead: time})}} trackingDuration={this.state.trackingDuration} />
); } } TrajectoryAnnotator.propTypes = { tracking: PropTypes.arrayOf(PropTypes.object).isRequired }; ================================================ FILE: Examples/5_Webpack_BaseballAnnotator_Bidirectional/BaseballVisualizer/js/helpers.js ================================================ import {linear} from 'everpolate'; export const constants = { gameElements: ["P", "B", "R@1", "R@2", "R@3", "1B", "2B", "3B", "SS", "LF", "CF", "RF", "C", "BALL"], gameElementsDesc: { "P": "Pitcher", "B": "Batter", "R@1": "Runner at first", "R@2": "Runner at second", "R@3": "Runner at third", "1B": "First baseman", "2B": "Second baseman", "3B": "Third baseman", "SS": "Shortstop", "LF": "Left fielder", "CF": "Center fielder", "RF": "Right fielder", "C": "Catcher", "BALL": "Ball" }, }; const input2ndBase = [0.0, 127.2792206], outputHomePlate = [125.2, 203.5], output2ndBase = [125.2, 150.8], inputScale = 1.0 / input2ndBase[1], outputScale = 1.0 / (output2ndBase[1] - outputHomePlate[1]); export function mapFieldSVGX(x){ const x2 = (((-x) * inputScale) / outputScale) + outputHomePlate[0]; return x2; } export function mapFieldSVGY(y){ const y2 = (y * inputScale) / outputScale + outputHomePlate[1]; return y2; } export function mapFieldSVG([x, y]){ const x2 = (((-x) * inputScale) / outputScale) + outputHomePlate[0]; const y2 = (y * inputScale) / outputScale + outputHomePlate[1]; return [x2, y2]; } export function mapSVGField([x2, y2]){ const x = -((x2 - outputHomePlate[0]) * outputScale) / inputScale; const y = ((y2 - outputHomePlate[1]) * outputScale) / inputScale; return [x, y]; } export function interpolatePositions(tQuery, positions) { //positions: array of [{x, y, t}, ...] if (positions.length === 1){ return {x: positions[0].x, y:positions[0].y}; } positions.sort((a,b) => a.t - b.t); if (tQuery <= positions[0].t) { return {x: positions[0].x, y: positions[0].y} } if (tQuery >= positions[positions.length-1].t) { return {x: positions[positions.length-1].x, y: positions[positions.length-1].y} } const t = positions.map(p=>p.t); const x = positions.map(p=>p.x); const y = positions.map(p=>p.y); const mappedX = linear(tQuery, t, x); const mappedY = linear(tQuery, t, y); return {x: mappedX, y: mappedY}; } export function convert_data_format(tracking){ let csvObj = {}; constants.gameElements.forEach(elem => { tracking[elem].forEach( ({x, y, t}) => { if (!csvObj[t]) { csvObj[t] = {"time_elapsed_sec": t}; constants.gameElements.forEach(ename => { csvObj[t][ename + "_x"] = null; csvObj[t][ename + "_y"] = null; }) } csvObj[t][elem + "_x"] = x; csvObj[t][elem + "_y"] = y; }); }); const timeKeys = Object.keys(csvObj); timeKeys.sort((a, b) => a - b); const csvArray = []; timeKeys.forEach(key => { csvArray.push(csvObj[key]); }); return csvArray; } ================================================ FILE: Examples/5_Webpack_BaseballAnnotator_Bidirectional/BaseballVisualizer/js/index.js ================================================ import React from "react"; import ReactDOM from "react-dom"; import { select } from "d3-selection"; import TrajectoryAnnotator from "./TrajectoryAnnotator"; export function renderBaseballAnnotator(divName, data){ ReactDOM.render( , select(divName).node()); } ================================================ FILE: Examples/5_Webpack_BaseballAnnotator_Bidirectional/BaseballVisualizer/package.json ================================================ { "name": "baseballvisualizer", "version": "1.0.0", "description": "library to plot and edit baseball trajectories", "main": "./js/index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack", "start": "webpack-dev-server --progress --colors --content-base dist/" }, "author": "Jorge Piazentin Ono", "license": "UNLICENSED", "devDependencies": { "@babel/core": "^7.12.9", "@babel/preset-env": "^7.12.7", "@babel/preset-react": "^7.12.7", "babel-loader": "^8.2.2", "css-loader": "^2.1.1", "file-loader": "^3.0.1", "less": "^4.0.0", "less-loader": "^7.1.0", "prop-types": "^15.6.2", "react": "^16.14.0", "react-dom": "^16.14.0", "style-loader": "^0.23.1", "url-loader": "^4.1.1", "webpack": "^4.44.2", "webpack-cli": "^3.3.11", "webpack-dev-server": "^3.11.0", "worker-loader": "^2.0.0" }, "dependencies": { "@babel/polyfill": "^7.12.1", "@material-ui/core": "^4.11.2", "@material-ui/icons": "^4.11.2", "crossfilter2": "^1.5.4", "d3": "^6.3.1", "d3-array": "^2.9.0", "d3-axis": "^1.0.12", "d3-fetch": "^1.1.2", "d3-scale": "^2.2.2", "d3-scale-chromatic": "^1.3.3", "d3-selection": "^1.4.0", "d3-shape": "^1.3.7", "d3-transition": "^1.3.2", "d3-zoom": "^1.8.3", "dagre": "^0.8.4", "everpolate": "0.0.3", "install": "^0.12.2", "jquery": "^3.5.1", "lodash": "^4.17.20", "npm": "^7.3.0", "rc-checkbox": "^2.3.1", "rc-select": "^11.5.3", "rc-slider": "^9.6.5", "react-bootstrap": "^1.4.0", "react-redux": "^6.0.0", "react-select": "^2.4.1", "redux": "^4.0.1", "redux-thunk": "^2.3.0", "styled-components": "^5.2.1" } } ================================================ FILE: Examples/5_Webpack_BaseballAnnotator_Bidirectional/BaseballVisualizer/play_annotated.csv ================================================ time_elapsed_sec,P_x,P_y,B_x,B_y,R@1_x,R@1_y,R@2_x,R@2_y,R@3_x,R@3_y,1B_x,1B_y,2B_x,2B_y,3B_x,3B_y,SS_x,SS_y,LF_x,LF_y,CF_x,CF_y,RF_x,RF_y,C_x,C_y,BALL_x,BALL_y 0.0,-1.0465815337,62.2709968754,-2.2541642719,-2.9384709841,,,,,,,,,,,-72.29396308400649,91.01148815468284,-59.010552964461894,136.89963220401873,,,,,,,-2.2541642719,-6.5612191986,, 3.243,,,,,,,,,,,,,,,,,,,,,,,,,,,0.1207582738,62.7943023833 3.443,,,,,,,,,,,,,,,,,,,,,,,,,,,-1.0868244643,8.453079167 3.68,,,,,,,,,,,55.7098071589,96.0833135434,,,,,,,,,,,,,,,, 3.98,,,-1.0465815337,-2.9384709841,,,,,,,,,,,,,,,,,,,,,,,, 4.03,,,,,,,,,,,,,,,,,,,,,,,139.03301609056658,260.0730714943413,,,, 4.44,,,,,,,,,,,,,,,,,,,,,-16.74515712954728,316.82946018694093,,,,,, 4.47,,,,,,,,,,,,,,,,,,,-129.0503517766061,243.16691316037546,,,,,,,, 4.88,,,,,,,,,,,,,,,,,-42.10439463049605,136.89963220401873,,,,,,,,,, 5.493,,,,,,,,,,,,,,,,,,,,,,,,,,,31.5179094655,156.9857559583 5.81,,,,,,,,,,,64.1628863259,81.5923206857,,,,,,,,,,,,,,,, 6.34,,,,,,,,,,,,,,,,,,,,,,,128.16477144730283,217.8076756594267,,,, 6.57,,,,,,,,,,,,,,,,,,,-102.48353153751694,233.50625125525212,,,,,,,, 7.25,,,,,,,,,,,,,,,,,,,,,,,,,3.7837494188,-0.5233055079,, 7.383,,,,,,,,,,,,,,,,,,,,,,,,,,,61.707477919,65.2094678596 7.503,,,,,,,,,,,,,30.3103267273,156.9857559583,,,,,,,,,,,,,, 8.8,,,,,,,,,,,,,,,,,,,,,-33.65131546351312,249.20482685107754,,,,,, 8.81,,,,,,,,,,,,,,,,,-48.142308321198136,126.03138756075496,,,,,,,,,, 8.85,3.7837494188,52.6103349703,,,,,,,,,,,,,,,,,-102.48353153751694,214.18492744500543,,,,,,,, 8.89,,,62.9553035877,65.8937450899,,,,,,,64.1628863259,61.0634141373,,,,,,,,,,,131.78751966172408,180.37261077707376,,,, 8.893,,,,,,,,,,,,,38.7634058943,153.3630077438,,,,,,,,,,,,,, ================================================ FILE: Examples/5_Webpack_BaseballAnnotator_Bidirectional/BaseballVisualizer/webpack.config.js ================================================ const config = { entry: ['./js/index.js'], output: { path: __dirname + '/build', filename: 'baseballvisualizer.js', library: 'baseballvisualizer' }, module: { rules: [ { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader', options: { presets: ['@babel/preset-env', '@babel/react'] } }, { test: /\.css$/, use: [ 'style-loader', 'css-loader' ] }, { test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'], }, { test: /\.(jpg|png|svg)$/, loader: "url-loader", options: { limit: Infinity // everything } } ] }, resolve: { extensions: ['.js'] }, devServer:{ writeToDisk:true, hot:false, inline: false, }, mode: 'development' }; module.exports = config; ================================================ FILE: Examples/6_HelloWorld_Bidirectional/HelloWorld_Bidirectional.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": { "id": "IaxAGrTGttuV" }, "source": [ "# notebookJS Colab HelloWorld\n", "\n", "In this notebook, we show how to use notebookJS to run custom Javascript code. We also show how to send messages between Python and Javascript." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "vYD-yjPtn_ik" }, "outputs": [], "source": [ "!pip install notebookjs" ] }, { "cell_type": "markdown", "metadata": { "id": "BjePMHoyt9TF" }, "source": [ "## Defining the JavaScript drawing function.\n", "\n", "The function requests a message to python every 1 second. The callback identifier is called *get_hello*" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "10am7Nm9oCc9" }, "outputs": [], "source": [ "helloworld_js = \"\"\"\n", "function helloworld(div_id, data){\n", " comm = new CommAPI(\"get_hello\", (ret) => {\n", " document.querySelector(div_id).textContent = ret.text;\n", " });\n", " setInterval(() => {comm.call({})}, 1000);\n", " comm.call({});\n", "}\n", "\"\"\"" ] }, { "cell_type": "markdown", "metadata": { "id": "zsXaw6XxuJF2" }, "source": [ "## Defining the Python callback" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "L0ucikc4oIOW" }, "outputs": [], "source": [ "import random\n", "def hello_world_random(data):\n", " hello_world_languages = [\n", " \"Ola Mundo\", # Portuguese\n", " \"Hello World\", # English\n", " \"Hola Mundo\", # Spanish\n", " \"Geiá sou Kósme\", # Greek\n", " \"Kon'nichiwa sekai\", # Japanese\n", " \"Hallo Welt\", # German\n", " \"namaste duniya\" #Hindi\n", " ]\n", " i = random.randint(0, len(hello_world_languages)-1)\n", " return {'text': hello_world_languages[i]}" ] }, { "cell_type": "markdown", "metadata": { "id": "g-rkBdq7uXSL" }, "source": [ "## Running the javascript function with Python callbacks" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "HaxtaaB-pj0z" }, "outputs": [], "source": [ "from notebookjs import execute_js\n", "execute_js(helloworld_js, \"helloworld\", callbacks={\"get_hello\": hello_world_random})" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "848VKX8jqGbW" }, "outputs": [], "source": [] } ], "metadata": { "colab": { "collapsed_sections": [], "name": "HelloWorld_notebookJS_Colab.ipynb", "provenance": [] }, "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.5" } }, "nbformat": 4, "nbformat_minor": 1 } ================================================ FILE: Examples/7_D3_scatterplot/D3_Scatter.ipynb ================================================ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# D3 Scatter Plot" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "D3 V3 scatter plot. Code adapted from https://www.d3-graph-gallery.com/graph/scatter_animation_start.html" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Loading libraries" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "with open(\"scatterplot_lib.js\", \"r\") as f:\n", " scatterplot_lib = f.read()\n", " \n", "d3_lib_url = \"https://d3js.org/d3.v3.min.js\"\n", "\n", "css = \"\"\"\n", ".axis path,\n", ".axis line {\n", " fill: none;\n", " stroke: slategray;\n", " shape-rendering: crispEdges;\n", "}\n", "\"\"\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Loading data" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import pandas as pd\n", "data = pd.read_csv(\"Prices.csv\").to_dict(orient=\"records\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Plotting Scatter Plot" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from notebookjs import execute_js\n", "execute_js(library_list=[d3_lib_url, scatterplot_lib], main_function=\"scatter\", data_dict=data, css_list=css)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.5" } }, "nbformat": 4, "nbformat_minor": 4 } ================================================ FILE: Examples/7_D3_scatterplot/Prices.csv ================================================ GrLivArea,SalePrice 1710,208500 1262,181500 1786,223500 1717,140000 2198,250000 1362,143000 1694,307000 2090,200000 1774,129900 1077,118000 1040,129500 2324,345000 912,144000 1494,279500 1253,157000 854,132000 1004,149000 1296,90000 1114,159000 1339,139000 2376,325300 1108,139400 1795,230000 1060,129900 1060,154000 1600,256300 900,134800 1704,306000 1600,207500 520,68500 1317,40000 1228,149350 1234,179900 1700,165500 1561,277500 2452,309000 1097,145000 1297,153000 1057,109000 1152,82000 1324,160000 1328,170000 884,144000 938,130250 1150,141000 1752,319900 2149,239686 1656,249700 1452,113000 955,127000 1470,177000 1176,114500 816,110000 1842,385000 1360,130000 1425,180500 1739,172500 1720,196500 2945,438780 780,124900 1158,158000 1111,101000 1370,202500 1710,140000 2034,219500 2473,317000 2207,180000 1479,226000 747,80000 2287,225000 2223,244000 845,129500 1718,185000 1086,144900 1605,107400 988,91000 952,135750 1285,127000 1768,136500 1230,110000 2142,193500 1337,153500 1563,245000 1065,126500 1474,168500 2417,260000 1560,174000 1224,164500 1526,85000 990,123600 1040,109900 1235,98600 964,163500 2291,133900 1786,204750 1470,185000 1588,214000 960,94750 835,83000 1225,128950 1610,205000 1732,178000 1535,118964 1226,198900 1818,169500 1992,250000 1047,100000 789,115000 1517,115000 1844,190000 1855,136900 1430,180000 2696,383970 2259,217000 2320,259500 1458,176000 1092,139000 1125,155000 3222,320000 1456,163990 988,180000 1123,100000 1080,136000 1199,153900 1586,181000 754,84500 958,128000 840,87000 1348,155000 1053,150000 2157,226000 2054,244000 1327,150750 1296,220000 1721,180000 1682,174000 1214,143000 1959,171000 1852,230000 1764,231500 864,115000 1734,260000 1385,166000 1501,204000 1728,125000 1709,130000 875,105000 2035,222500 1080,141000 1344,115000 969,122000 1710,372402 1993,190000 1252,235000 1200,125000 1096,79000 1040,109500 1968,269500 1947,254900 2462,320000 1232,162500 2668,412500 1541,220000 882,103200 1616,152000 1355,127500 1867,190000 2161,325624 1720,183500 1707,228000 1382,128500 1656,215000 1767,239000 1362,163000 1651,184000 2158,243000 2060,211000 1920,172500 2234,501837 968,100000 1525,177000 1802,200100 1340,120000 2082,200000 1252,127000 3608,475000 1217,173000 1656,135000 1224,153337 1593,286000 2727,315000 1479,184000 1431,192000 1709,130000 864,127000 1456,148500 1726,311872 3112,235000 2229,104000 1713,274900 1121,140000 1279,171500 1310,112000 848,149000 1284,110000 1442,180500 1696,143900 1100,141000 2062,277000 1092,145000 864,98000 1212,186000 1852,252678 990,156000 1392,161750 1236,134450 1436,210000 1328,107000 1954,311500 1248,167240 1498,204900 2267,200000 1552,179900 864,97000 2392,386250 1302,112000 2520,290000 987,106000 912,125000 1555,192500 1194,148000 2794,403000 987,94500 894,128200 1960,216500 987,89500 1414,185500 1744,194500 1694,318000 1487,113000 1566,262500 866,110500 1440,79000 1217,120000 2110,205000 1872,241500 1928,137000 1375,140000 1668,180000 2144,277000 1306,76500 1625,235000 1640,173000 1302,158000 1314,145000 2291,230000 1728,207500 1604,220000 1792,231500 882,97000 1382,176000 2574,276000 1212,151000 1316,130000 764,73000 1422,175500 1511,185000 2192,179500 778,120500 1113,148000 1939,266000 1363,241500 2270,290000 1632,139000 816,124500 1548,205000 1560,201000 864,141000 2121,415298 2022,192000 1982,228500 1262,185000 1314,207500 1468,244600 1575,179200 1250,164700 1734,159000 858,88000 900,122000 1396,153575 1919,233230 1716,135900 1716,131000 2263,235000 1644,167000 1003,142500 1558,152000 1950,239000 1743,175000 1152,158500 1336,157000 2452,267000 1541,205000 894,149900 3493,295000 2000,305900 2243,225000 1406,89500 861,82500 1944,360000 1501,165600 972,132000 1118,119900 2036,375000 1641,178000 1432,188500 2353,260000 1959,270000 2646,260000 1472,187500 2596,342643 2468,354000 2730,301000 1163,126175 2978,242000 803,87000 1719,324000 1383,145250 2134,214500 1192,78000 1728,119000 1056,139000 1629,284000 1358,207000 1638,192000 1786,228950 1922,377426 1536,214000 1621,202500 1215,155000 1908,202900 841,82000 1040,87500 1684,266000 1112,85000 1577,140200 958,151500 1478,157500 1626,154000 2728,437154 1869,318061 1453,190000 1111,95000 720,105900 1595,140000 1200,177500 1167,173000 1142,134000 1352,130000 1924,280000 912,156000 1505,145000 1922,198500 987,118000 1574,190000 1344,147000 1394,159000 1431,165000 1268,132000 1287,162000 1664,172400 1588,134432 752,125000 1319,123000 1928,219500 904,61000 914,148000 2466,340000 1856,394432 1800,179000 1691,127000 1301,187750 1797,213500 784,76000 1953,240000 1269,192000 1184,81000 1125,125000 1479,191000 2332,426000 1367,119000 1961,215000 882,106500 788,100000 1034,109000 1144,129000 894,123000 1812,169500 1077,67000 1550,241000 1288,245500 1310,164990 672,108000 2263,258000 1572,168000 1620,150000 1639,115000 1680,177000 2172,280000 2078,339750 1276,60000 1056,145000 1478,222000 1028,115000 2097,228000 1340,181134 1400,149500 2624,239000 1134,126000 1056,142000 1344,206300 1602,215000 988,113000 2630,315000 1196,139000 1389,135000 1644,275000 907,109008 1208,195400 1412,175000 987,85400 1198,79900 1365,122500 1604,181000 630,81000 1661,212000 1118,116000 904,119000 694,90350 1196,110000 2402,555000 1440,118000 1573,162900 1258,172500 1908,210000 1689,127500 1888,190000 1886,199900 1376,119500 1183,120000 813,110000 1533,280000 1756,204000 1590,210000 1728,188000 1242,175500 1344,98000 1663,256000 1666,161000 1203,110000 1935,263435 1135,155000 864,62383 1660,188700 1040,124000 1414,178740 1277,167000 1644,146500 1634,250000 1710,187000 1502,212000 1969,190000 1072,148000 1976,440000 1652,251000 970,132500 1493,208900 2643,380000 1718,297000 1131,89471 1850,326000 1792,374000 1826,155000 1216,164000 999,132500 1113,147000 1073,156000 1484,175000 2414,160000 630,86000 1304,115000 1578,133000 1456,172785 1269,155000 886,91300 720,34900 3228,430000 1820,184000 899,130000 912,120000 1218,113000 1768,226700 1214,140000 1801,289000 1322,147000 1960,124500 1911,215000 1218,208300 1378,161000 1041,124500 1363,164900 1368,202665 864,129900 1080,134000 789,96500 2020,402861 2119,158000 2344,265000 1796,211000 2080,234000 1294,106250 1244,150000 1664,159000 4676,184750 2398,315750 1266,176000 928,132000 2713,446261 605,86000 2515,200624 1509,175000 1362,128000 827,107500 334,39300 1414,178000 1347,107500 1724,188000 864,111250 1159,158000 1601,272000 1838,315000 2285,248000 1680,213250 767,133000 1496,179665 2183,229000 1635,210000 768,129500 825,125000 2094,263000 1069,140000 928,112500 1717,255500 1126,108000 2046,284000 1048,113000 1092,141000 1336,108000 1446,175000 1557,234000 1392,121500 1389,170000 996,108000 1674,185000 2295,268000 1647,128000 2504,325000 1535,214000 2132,316600 943,135960 1728,142600 864,120000 1692,224500 1430,170000 1109,139000 1216,118500 1477,145000 1320,164500 1392,146000 1795,131500 1429,181900 2042,253293 816,118500 2775,325000 1573,133000 2028,369900 838,130000 860,137000 1473,143000 935,79500 1582,185900 2296,451950 816,138000 848,140000 924,110000 1826,319000 1368,114504 1402,194201 1647,217500 1556,151000 1904,275000 1375,141000 1915,220000 1200,151000 1494,221000 1986,205000 1040,152000 2008,225000 3194,359100 1029,118500 2153,313000 1032,148000 1872,261500 1120,147000 630,75500 1054,137500 1509,183200 832,105500 1828,314813 2262,305000 864,67000 2614,240000 980,135000 1512,168500 1790,165150 1116,160000 1422,139900 1520,153000 2080,135000 1350,168500 1750,124000 1554,209500 1411,82500 1056,139400 1056,144000 3395,200000 800,60000 1387,93000 796,85000 1567,264561 1518,274000 1929,226000 2704,345000 1620,152000 1766,370878 981,143250 1048,98300 1094,155000 1839,155000 630,84500 1665,205950 1510,108000 1716,191000 1469,135000 2113,350000 1092,88000 1053,145500 1502,149000 1458,97500 1486,167000 1935,197900 2448,402000 1392,110000 1181,137500 2097,423000 1936,230500 2380,129000 1679,193500 1437,168000 1180,137500 1476,173500 1369,103600 1208,165000 1839,257500 1136,140000 1441,148500 1774,87000 792,109500 2046,372500 988,128500 923,143000 1520,159434 1291,173000 1668,285000 1839,221000 2090,207500 1761,227875 1102,148800 1419,392000 1362,194700 848,141000 4316,755000 2519,335000 1073,108480 1539,141500 1137,176000 616,89000 1148,123500 894,138500 1391,196000 1800,312500 1164,140000 2576,361919 1812,140000 1484,213000 1092,55000 1824,302000 1324,254000 1456,179540 904,109900 729,52000 1178,102776 1228,189000 960,129000 1479,130500 1350,165000 2554,159500 1178,157000 2418,341000 971,128500 1742,275000 848,143000 864,124500 1470,135000 1698,320000 864,120500 1680,222000 1232,194500 1776,110000 1208,103000 1616,236500 1146,187500 2031,222500 1144,131400 948,108000 1768,163000 1040,93500 1801,239900 1200,179000 1728,190000 1432,132000 912,142000 1349,179000 1464,175000 1337,180000 2715,299800 2256,236000 2640,265979 1720,260400 1529,98000 1140,96500 1320,162000 1494,217000 2098,275500 1026,156000 1471,172500 1768,212000 1386,158900 1501,179400 2531,290000 864,127500 1301,100000 1547,215200 2365,337000 1494,270000 1506,264132 1714,196500 1750,160000 1836,216837 3279,538000 858,134900 1220,102000 1117,107000 912,114500 1973,395000 1204,162000 1614,221500 894,142500 2020,144000 1004,135000 1253,176000 1603,175900 1430,187100 1110,165500 1484,128000 1342,161500 1652,139000 2084,233000 901,107900 2087,187500 1145,160200 1062,146800 2013,269790 1496,225000 1895,194500 1564,171000 1285,143500 773,110000 3140,485000 1768,175000 1688,200000 1196,109900 1456,189000 2822,582933 1128,118000 1428,227680 980,135500 1576,223500 1086,159950 2138,106000 1309,181000 848,144500 1044,55993 1442,157900 1250,116000 1661,224900 1008,137000 1689,271000 1052,155000 1358,224000 1640,183000 936,93000 1733,225000 1489,139500 1489,232600 2084,385000 784,109500 1434,189000 2126,185000 1223,147400 1392,166000 1200,151000 1829,237000 1516,167000 1144,139950 1067,128000 1559,153500 987,100000 1099,144000 1200,130500 1482,140000 1539,157500 1165,174900 1800,141000 1416,153900 1701,171000 1775,213000 864,133500 2358,240000 1855,187000 848,131500 1456,215000 1646,164000 1445,158000 1779,170000 1040,127000 1026,147000 1481,174000 1370,152000 2654,250000 1426,189950 1039,131500 1097,152000 1148,132500 1372,250580 1002,148500 1646,248900 1120,129000 2320,169000 1949,236000 894,109500 1682,200500 910,116000 1268,133000 1131,66500 2610,303477 1040,132250 2224,350000 1155,148000 864,136500 1090,157000 1717,187500 1593,178000 2230,118500 892,100000 1709,328900 1712,145000 1393,135500 2217,268000 1505,149500 924,122900 1683,172500 1068,154500 1383,165000 1535,118858 1796,140000 951,106500 2240,142953 2364,611657 1236,135000 858,110000 1306,153000 1509,180000 1670,240000 902,125500 1063,128000 1636,255000 2057,250000 902,131000 1484,174000 2274,154300 1268,143500 1015,88000 2002,145000 1224,173733 1092,75000 480,35311 1229,135000 2127,238000 1414,176500 1721,201000 2200,145900 1316,169990 1617,193000 1686,207500 1126,175000 2374,285000 1978,176000 1788,236500 2236,222000 1466,201000 925,117500 1905,320000 1500,190000 2069,242000 747,79900 1200,184900 1971,253000 1962,239799 2403,244400 1728,150900 2060,214000 1440,150000 1632,143000 1344,137500 1869,124900 1144,143000 1629,270000 1776,192500 1381,197500 864,129000 965,119900 768,133900 1968,172000 980,127500 1958,145000 1229,124000 1057,132000 1337,185000 1416,155000 858,116500 2872,272000 1548,155000 1800,239000 1894,214900 1484,178900 1308,160000 1098,135000 968,37900 1095,140000 1192,135000 1626,173000 918,99500 1428,182000 2019,167500 1382,165000 869,85500 1241,199900 894,110000 1121,139000 999,178400 2612,336000 1266,159895 2290,255900 1734,126000 1164,125000 1635,117000 1940,395192 2030,195000 1576,197000 2392,348000 1742,168000 1851,187000 1500,173900 1718,337500 1230,121600 1050,136500 1442,185000 1077,91000 1208,206000 944,82000 691,86000 1574,232000 1680,136905 1504,181000 985,149900 1657,163500 1092,88000 1710,240000 1522,102000 1271,135000 1664,100000 1502,165000 1022,85000 1082,119200 1665,227000 1504,203000 1360,187500 1472,160000 1506,213490 1132,176000 1220,194000 1248,87000 1504,191000 2898,287000 882,112500 1264,167500 1646,293077 1376,105000 1218,118000 1928,160000 3082,197000 2520,310000 1654,230000 954,119750 845,84000 1620,315500 2263,287000 1344,97000 630,80000 1803,155000 1632,173000 1306,196000 2329,262280 2524,278000 1733,139600 2868,556581 990,145000 1771,115000 930,84900 1302,176485 1316,200141 1977,165000 1526,144500 1989,255000 1523,180000 1364,185850 1850,248000 2184,335000 1991,220000 1338,213500 894,81000 2337,90000 1103,110500 1154,154000 2260,328000 1571,178000 1611,167900 2521,151400 893,135000 1048,135000 1556,154000 1456,91500 1426,159500 1240,194000 1740,219500 1466,170000 1096,138800 848,155900 990,126000 1258,145000 1040,133000 1459,192000 1251,160000 1498,187500 996,147000 1092,83500 1953,252000 1709,137500 1247,197000 1040,92900 1252,160000 1694,136500 1200,146000 936,129000 1314,176432 1355,127000 1088,170000 1324,128000 1601,157000 438,60000 950,119500 1134,135000 1194,159500 1302,106000 2622,325000 1442,179900 2021,274725 1690,181000 1836,280000 1658,188000 1964,205000 816,129900 1008,134500 833,117000 1734,318000 1419,184100 894,130000 1601,140000 1040,133700 1012,118400 1552,212900 960,112000 698,118000 1482,163900 1005,115000 1555,174000 1530,259000 1959,215000 936,140000 1981,135000 974,93500 2210,117500 2020,239500 1600,169000 986,102000 1252,119000 1020,94000 1567,196000 1167,144000 952,139000 1868,197500 2828,424870 1006,80000 924,80000 1576,149000 1298,180000 1564,174500 1111,116900 1482,143000 932,124000 1466,149900 1811,230000 816,120500 1820,201800 1437,218000 1265,179900 1314,230000 1580,235128 1876,185000 1456,146000 1640,224000 894,129000 1258,108959 1432,194000 1502,233170 1694,245350 1671,173000 2108,235000 3627,625000 1118,171000 1261,163000 1250,171900 3086,200500 2345,239000 2872,285000 923,119500 1224,115000 1343,154900 1124,93000 2514,250000 1652,392500 4476,745000 1130,120000 1572,186700 1221,104900 1699,95000 1624,262000 1660,195000 1804,189000 1622,168000 1441,174000 1472,125000 1224,165000 1352,158000 1456,176000 1863,219210 1690,144000 1212,178000 1382,148000 864,116050 1779,197900 1348,117000 1630,213000 1074,153500 2196,271900 1056,107000 1700,200000 1283,140000 1660,290000 1845,189000 1752,164000 672,113000 960,145000 999,134500 894,125000 1902,112000 1314,229456 912,80500 1218,91500 912,115000 1211,134000 1846,143000 2136,137900 1490,184000 1138,145000 1933,214000 912,147000 1702,367294 1507,127000 2620,190000 1190,132500 1224,101800 1188,142000 1964,130000 1784,138887 1626,175500 1948,195000 1141,142500 1484,265900 1768,224900 1689,248328 1173,170000 2076,465000 1517,230000 1868,178000 1553,186500 1034,169900 2058,129500 988,119000 2110,244000 1405,171750 874,130000 2167,294000 1656,165400 1367,127500 1987,301500 864,99900 1166,190000 1054,151000 1675,181000 1050,128900 1788,161500 1824,180500 1337,181000 1452,183900 1889,122000 2018,378500 3447,381000 1524,144000 1524,260000 1489,185750 935,137000 1357,177000 1250,139000 1920,137000 1395,162000 1724,197900 2031,237000 1128,68400 1573,227000 1339,180000 1040,150500 1824,139000 2447,169000 1412,132500 1328,143000 1582,190000 1659,278000 1970,281000 1152,180500 1302,119500 2372,107500 1664,162900 864,115000 1052,138500 1128,155000 1072,140000 5642,160000 1246,154000 1983,225000 1494,177500 2526,290000 1616,232000 1708,130000 1652,325000 1368,202500 990,138000 1122,147000 1294,179200 1902,335000 1274,203000 2810,302000 2599,333168 948,119000 2112,206900 1630,295493 1352,208900 1787,275000 948,111000 1478,156500 720,72500 1923,190000 708,82500 1795,147000 796,55000 774,79000 816,130500 2792,256000 1632,176500 1588,227000 954,132500 816,100000 1360,125500 1365,125000 1334,167900 1656,135000 693,52500 1861,200000 864,128500 872,123000 1114,155000 2169,228500 1913,177000 1456,155835 960,108500 2156,262500 1776,283463 1494,215000 2358,122000 2634,200000 1716,171000 1176,134900 3238,410000 1865,235000 1920,170000 892,110000 1078,149900 1573,177500 1980,315000 2601,189000 1530,260000 1738,104900 1412,156932 1200,144152 1674,216000 1790,193000 1475,127000 848,144000 1668,232000 1374,105000 1661,165500 2097,274300 2633,466500 1958,250000 1571,239000 790,91000 1604,117000 987,83000 1394,167500 864,58500 2117,237500 1762,157000 1416,112000 1258,105000 1154,125500 2784,250000 2526,136000 1746,377500 1218,131000 1525,235000 1584,124000 900,123000 1912,163000 1500,246578 2482,281213 1687,160000 1513,137500 1904,138000 1608,137450 1158,120000 1593,193000 1294,193879 1464,282922 1214,105000 1646,275000 768,133000 833,112000 1363,125500 2093,215000 1840,230000 1668,140000 1040,90000 1844,257000 1848,207000 1569,175900 2290,122500 2450,340000 1144,124000 1844,223000 1416,179900 1069,127500 848,136500 2201,274970 1344,144000 1252,142000 2127,271000 1558,140000 804,119000 1440,182900 1838,192140 958,143750 968,64500 1792,186500 1126,160000 1537,174000 864,120500 1932,394617 1236,149700 1725,197000 2555,191000 848,149300 2007,310000 952,121000 1422,179600 913,129000 1188,157900 2090,240000 1346,112000 630,92000 1792,136000 1578,287090 1072,145000 1140,84500 1221,185000 1647,175000 2073,210000 2340,266500 1078,142125 1256,147500 ================================================ FILE: Examples/7_D3_scatterplot/scatterplot_lib.js ================================================ function scatter(div_id, data){ // Code adapted from https://www.d3-graph-gallery.com/graph/scatter_animation_start.html // Dataset https://raw.githubusercontent.com/holtzy/data_to_viz/master/Example_dataset/2_TwoNum.csv // set the dimensions and margins of the graph var margin = {top: 10, right: 30, bottom: 30, left: 60}, width = 460 - margin.left - margin.right, height = 400 - margin.top - margin.bottom; // append the svg object to the body of the page var svg = d3.select(div_id) .append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")") // Add X axis var x = d3.scale.linear() .domain([0, 0]) .range([ 0, width ]); svg.append("g") .attr("class", "myXaxis axis") // Note that here we give a class to the X axis, to be able to call it later and modify it .attr("transform", "translate(0," + height + ")") .call(d3.svg.axis().scale(x)) .attr("opacity", "0") // Add Y axis var y = d3.scale.linear() .domain([0, 500000]) .range([ height, 0]); svg.append("g") .attr("class", "axis") .call(d3.svg.axis() .orient('left') .scale(y)); // Add dots svg.append('g') .selectAll("dot") .data(data) .enter() .append("circle") .attr("cx", function (d) { return x(d.GrLivArea); } ) .attr("cy", function (d) { return y(d.SalePrice); } ) .attr("r", 1.5) .style("fill", "#69b3a2") // new X axis x.domain([0, 4000]) svg.select(".myXaxis") .transition() .duration(2000) .attr("opacity", "1") .call(d3.svg.axis().scale(x)); svg.selectAll("circle") .transition() .delay(function(d,i){return(i*3)}) .duration(2000) .attr("cx", function (d) { return x(d.GrLivArea); } ) .attr("cy", function (d) { return y(d.SalePrice); } ) } ================================================ FILE: Images/notebookJS.ai ================================================ %PDF-1.5 % 1 0 obj <>/OCGs[23 0 R]>>/Pages 3 0 R/Type/Catalog>> endobj 2 0 obj <>stream application/pdf notebookJS Adobe Illustrator 24.1 (Macintosh) 2021-04-12T12:26:13-04:00 2021-04-12T12:26:14-04:00 2021-04-12T12:26:14-04:00 212 256 JPEG /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgBAADUAwER AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE 1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp 0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo +DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4qx/8AMP8A5QDzN/2y r7/qGfFXxh+SX/OO/wDys/Qb/Vv8Qfoj6jdfVfR+p/Wef7tZOXL14KfbpSmKvRf+hGP+/wBv+5X/ ANnmKu/6EY/7/b/uV/8AZ5irv+hGP+/2/wC5X/2eYq7/AKEY/wC/2/7lf/Z5irv+hGP+/wBv+5X/ ANnmKu/6EY/7/b/uV/8AZ5irv+hGP+/2/wC5X/2eYq7/AKEY/wC/2/7lf/Z5irv+hGP+/wBv+5X/ ANnmKu/6EY/7/b/uV/8AZ5irv+hGP+/2/wC5X/2eYq7/AKEY/wC/2/7lf/Z5irv+hGP+/wBv+5X/ ANnmKu/6EY/7/b/uV/8AZ5irv+hGP+/2/wC5X/2eYq7/AKEY/wC/2/7lf/Z5irv+hGP+/wBv+5X/ ANnmKu/6EY/7/b/uV/8AZ5irv+hGP+/2/wC5X/2eYq7/AKEY/wC/2/7lf/Z5irv+hGP+/wBv+5X/ ANnmKu/6EY/7/b/uV/8AZ5irv+hGP+/2/wC5X/2eYq+Xvqf+5L6lz/3d6PqU/wArjWlf44q/UvFW P/mH/wAoB5m/7ZV9/wBQz4q8U/5wl/5QPXv+2p/2Lx4q+i8VdirsVdirsVdirsVdirsVdirsVdir sVdirsVdirsVdirsVdirsVdirsVfl9/00n/R5/zNxV9L/wDK3v8AnLj/AKkr/uWXP/VXFUv8w/mr /wA5TXWganban5P9DTZ7SeO+n/RtwnCB42WVuRkIXihJrirCPyU88fnZ5f0K/tvIGgnVtOmuvUu5 hZy3XGf01XjyjZQPgANMVei/8rf/AOcuP+pMb/uFXP8A1UxV9A/lZrHm7WPIemal5vsv0f5in9f6 9ZmJoOHC4kSL925Zl5RKrde9cVZXirEL78z9Asr24s5be7aW2leFyqRlS0bFTSsgNKjFVD/lbXlz /lmvP+Ai/wCqmKu/5W15c/5Zrz/gIv8Aqpirv+VteXP+Wa8/4CL/AKqYq7/lbXlz/lmvP+Ai/wCq mKu/5W15c/5Zrz/gIv8Aqpirv+VteXP+Wa8/4CL/AKqYq7/lbXlz/lmvP+Ai/wCqmKu/5W15c/5Z rz/gIv8Aqpirv+VteXP+Wa8/4CL/AKqYq7/lbXlz/lmvP+Ai/wCqmKu/5W15c/5Zrz/gIv8Aqpir v+VteXP+Wa8/4CL/AKqYq7/lbXlz/lmvP+Ai/wCqmKu/5W15c/5Zrz/gIv8Aqpirv+VteXP+Wa8/ 4CL/AKqYq7/lbXlz/lmvP+Ai/wCqmKqtr+aXl+5uobaO3uw87rGhZI6AuQorSQ+OKsxxV2Kvy+/6 aT/o8/5m4q/UHFWP/mH/AMoB5m/7ZV9/1DPirxT/AJwl/wCUD17/ALan/YvHir6LxV2KuxV4D5l/ 5SPVf+Yy4/5OtiqW4q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FUdof/AB2tP/5iYf8Ak4MV fQWKuxV+X3/TSf8AR5/zNxV+oOKsf/MP/lAPM3/bKvv+oZ8VeKf84S/8oHr3/bU/7F48VfReKuxV 2KvCfMen37+YdUZbaVla7nKsEYggytuNsVS79G6j/wAss3/Itv6Yq79G6j/yyzf8i2/pirv0bqP/ ACyzf8i2/pirv0bqP/LLN/yLb+mKu/Ruo/8ALLN/yLb+mKu/Ruo/8ss3/Itv6Yq79G6j/wAss3/I tv6Yq79G6j/yyzf8i2/pirv0bqP/ACyzf8i2/pirv0bqP/LLN/yLb+mKu/Ruo/8ALLN/yLb+mKu/ Ruo/8ss3/Itv6Yq79G6j/wAss3/Itv6Yq79G6j/yyzf8i2/pirv0bqP/ACyzf8i2/pirv0bqP/LL N/yLb+mKo3RdPv11mwZraUKLiIklGAADj2xV71irsVfl9/00n/R5/wAzcVfS/wCn/wDnNr/q2/8A JHTP64ql/mHXP+cxn0DU01fT+OlNaTjUG9HThS3MbCU1U8vsV6b4qwj8lNT/AOcgbTQr9Py0tjPp bXVb1hFaSUuPTUUrcfF9jj02xV6L/iD/AJza/wCre3/SPpn9cVfQP5WXHne48h6ZN54j9LzQ3r/p CMrElKXEgi+GH93/AHITp+vFWV4q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX Yq/L7/ppP+jz/mbir9QcVY/+Yf8AygHmb/tlX3/UM+KvFP8AnCX/AJQPXv8Atqf9i8eKvovFXYq7 FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX5ff9NJ/0ef8AM3FX6g4qx/8A MP8A5QDzN/2yr7/qGfFXin/OEv8Aygevf9tT/sXjxV9F4q7FXYq8R806vq0XmPUo4724SNLiQKiy uAAGOwAOKpX+nNa/6uFz/wAjpP64q79Oa1/1cLn/AJHSf1xV36c1r/q4XP8AyOk/rirv05rX/Vwu f+R0n9cVd+nNa/6uFz/yOk/rirv05rX/AFcLn/kdJ/XFXfpzWv8Aq4XP/I6T+uKu/Tmtf9XC5/5H Sf1xV36c1r/q4XP/ACOk/rirv05rX/Vwuf8AkdJ/XFXfpzWv+rhc/wDI6T+uKu/Tmtf9XC5/5HSf 1xV36c1r/q4XP/I6T+uKu/Tmtf8AVwuf+R0n9cVd+nNa/wCrhc/8jpP64q79Oa1/1cLn/kdJ/XFV S31vWTcRA39yQXWo9aTx+eKvfsVdir8vv+mk/wCjz/mbir6X/QH/ADm1/wBXL/ktpn9MVS/zDof/ ADmMmgam+r6hy0pbSc6gvraca24jYyiijl9ivTfFWEfkppn/ADkDd6Ffv+WlyYNLW6peqJbSOtx6 amtLj4vscem2KvRf8P8A/ObX/Vwb/pI0z+mKvoH8rLfzvb+Q9Mh88Ser5oX1/wBISFonrW4kMXxQ /u/7kp0/XirK8VeDebf+Un1T/mJk/wCJHFUoxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2Kq tt/vRF/rr+vFX0XirsVfl9/00n/R5/zNxV+oOKsf/MP/AJQDzN/2yr7/AKhnxV4p/wA4S/8AKB69 /wBtT/sXjxV9F4q7FXYqxLUfyz0G/v572ae6WW4dpHVHjCgsamlYyfxxVDf8ql8uf8tN5/wcX/VP FXf8ql8uf8tN5/wcX/VPFXf8ql8uf8tN5/wcX/VPFXf8ql8uf8tN5/wcX/VPFXf8ql8uf8tN5/wc X/VPFXf8ql8uf8tN5/wcX/VPFXf8ql8uf8tN5/wcX/VPFXf8ql8uf8tN5/wcX/VPFXf8ql8uf8tN 5/wcX/VPFXf8ql8uf8tN5/wcX/VPFXf8ql8uf8tN5/wcX/VPFXf8ql8uf8tN5/wcX/VPFXf8ql8u f8tN5/wcX/VPFXf8ql8uf8tN5/wcX/VPFXf8ql8uf8tN5/wcX/VPFXf8ql8uf8tN5/wcX/VPFVyf lP5dR1cXN5VSCKvF2/554qzTFXYq/L7/AKaT/o8/5m4q/UHFWP8A5h/8oB5m/wC2Vff9Qz4q8U/5 wl/5QPXv+2p/2Lx4q+i8VdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsV fl9/00n/AEef8zcVfS//ACqH/nLj/qdf+5nc/wDVLFUv8w/lV/zlNa6BqdzqfnD19NgtJ5L6D9JX D84EjZpV4mMBuSAimKsI/JTyP+dnmDQr+58ga8dJ06G69O7hF5La8p/TVuXGNWB+AgVxV6L/AMqg /wCcuP8Aqc2/7itz/wBU8VfQP5WaP5u0fyHpmm+b739IeYoPX+vXhlafnzuJHi/eOFZuMTKvTtTF WV4q881T81biy1O7shpyOLaaSEOZSOXpuVrTj3pnO5+3JQnKPD9JI59z02n9n45McZ8f1AHl3j3o X/lcN1/1bE/5Gn/mnKv9EEv5g+f7G3/Q3H+efl+13/K4br/q2J/yNP8AzTj/AKIJfzB8/wBi/wCh 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 fwxV/9k= proof:pdf uuid:65E6390686CF11DBA6E2D887CEACB407 xmp.did:70590589-948d-4756-b92d-228cc83a97cb uuid:8609d199-ee6e-8340-9c4a-35637fac439b uuid:27f584af-a488-9349-a64a-b29a9af20ed9 xmp.did:8d08eb9c-7571-4e78-aade-d88bdc677e5e uuid:65E6390686CF11DBA6E2D887CEACB407 proof:pdf saved xmp.iid:70590589-948d-4756-b92d-228cc83a97cb 2021-04-12T10:58:21-04:00 Adobe Illustrator 24.1 (Macintosh) / Web Document AIRobin 1 False False 461.000000 443.000000 Pixels MyriadPro-Regular Myriad Pro Regular Open Type Version 2.106;PS 2.000;hotconv 1.0.70;makeotf.lib2.5.58329 False MyriadPro-Regular.otf Cyan Magenta Yellow Black Default Swatch Group 0 White RGB PROCESS 255 255 255 Black RGB PROCESS 0 0 0 RGB Red RGB PROCESS 255 0 0 RGB Yellow RGB PROCESS 255 255 0 RGB Green RGB PROCESS 0 255 0 RGB Cyan RGB PROCESS 0 255 255 RGB Blue RGB PROCESS 0 0 255 RGB Magenta RGB PROCESS 255 0 255 R=193 G=39 B=45 RGB PROCESS 193 39 45 R=237 G=28 B=36 RGB PROCESS 237 28 36 R=241 G=90 B=36 RGB PROCESS 241 90 36 R=247 G=147 B=30 RGB PROCESS 247 147 30 R=251 G=176 B=59 RGB PROCESS 251 176 59 R=252 G=238 B=33 RGB PROCESS 252 238 33 R=217 G=224 B=33 RGB PROCESS 217 224 33 R=140 G=198 B=63 RGB PROCESS 140 198 63 R=57 G=181 B=74 RGB PROCESS 57 181 74 R=0 G=146 B=69 RGB PROCESS 0 146 69 R=0 G=104 B=55 RGB PROCESS 0 104 55 R=34 G=181 B=115 RGB PROCESS 34 181 115 R=0 G=169 B=157 RGB PROCESS 0 169 157 R=41 G=171 B=226 RGB PROCESS 41 171 226 R=0 G=113 B=188 RGB PROCESS 0 113 188 R=46 G=49 B=146 RGB PROCESS 46 49 146 R=27 G=20 B=100 RGB PROCESS 27 20 100 R=102 G=45 B=145 RGB PROCESS 102 45 145 R=147 G=39 B=143 RGB PROCESS 147 39 143 R=158 G=0 B=93 RGB PROCESS 158 0 93 R=212 G=20 B=90 RGB PROCESS 212 20 90 R=237 G=30 B=121 RGB PROCESS 237 30 121 R=199 G=178 B=153 RGB PROCESS 199 178 153 R=153 G=134 B=117 RGB PROCESS 153 134 117 R=115 G=99 B=87 RGB PROCESS 115 99 87 R=83 G=71 B=65 RGB PROCESS 83 71 65 R=198 G=156 B=109 RGB PROCESS 198 156 109 R=166 G=124 B=82 RGB PROCESS 166 124 82 R=140 G=98 B=57 RGB PROCESS 140 98 57 R=117 G=76 B=36 RGB PROCESS 117 76 36 R=96 G=56 B=19 RGB PROCESS 96 56 19 R=66 G=33 B=11 RGB PROCESS 66 33 11 Grays 1 R=0 G=0 B=0 RGB PROCESS 0 0 0 R=26 G=26 B=26 RGB PROCESS 26 26 26 R=51 G=51 B=51 RGB PROCESS 51 51 51 R=77 G=77 B=77 RGB PROCESS 77 77 77 R=102 G=102 B=102 RGB PROCESS 102 102 102 R=128 G=128 B=128 RGB PROCESS 128 128 128 R=153 G=153 B=153 RGB PROCESS 153 153 153 R=179 G=179 B=179 RGB PROCESS 179 179 179 R=204 G=204 B=204 RGB PROCESS 204 204 204 R=230 G=230 B=230 RGB PROCESS 230 230 230 R=242 G=242 B=242 RGB PROCESS 242 242 242 Web Color Group 1 R=63 G=169 B=245 RGB PROCESS 63 169 245 R=122 G=201 B=67 RGB PROCESS 122 201 67 R=255 G=147 B=30 RGB PROCESS 255 147 30 R=255 G=29 B=37 RGB PROCESS 255 29 37 R=255 G=123 B=172 RGB PROCESS 255 123 172 R=189 G=204 B=212 RGB PROCESS 189 204 212 Adobe PDF library 15.00 21.0.0 endstream endobj 3 0 obj <> endobj 5 0 obj <>/Resources<>/ExtGState<>/Font<>/ProcSet[/PDF/Text]/Properties<>>>/Thumb 28 0 R/TrimBox[0.0 0.0 461.0 443.0]/Type/Page>> endobj 25 0 obj <>stream HTMK$1WQN}\TapvGp+Ǒ: ^W/]Gn7?Fp8ۀ ?!9LaB.@-X#F&bg 651ˡ\7CKp¶?}Xdx"LK/9&p+)%R 9(ww('K#,5)̝ c Ͼ;䯨RJKO+{xT3"zʹFdzm9 u g#)ٿ6=+ǴX>fi:?X/(#Z{3$ÌUǼwͲ)*YE~^MΗtI "i7 endstream endobj 28 0 obj <>stream 8;Z]!d1(^^$q,#+p0j!j'7XSHXOG+.>#fUJ*D60**1]J]Nq8/0;r@B_6?_ (H7AmrBiT(gTKfs Pq^r^S^tZW/?(W&)TQ PP@8^_<3m[VIHq;QuHYtNq`('1%MEsjWEPb[gW4WmeUc`"os!:[[@~> endstream endobj 29 0 obj [/Indexed/DeviceRGB 255 30 0 R] endobj 30 0 obj <>stream 8;X]O>EqN@%''O_@%e@?J;%+8(9e>X=MR6S?i^YgA3=].HDXF.R$lIL@"pJ+EP(%0 b]6ajmNZn*!='OQZeQ^Y*,=]?C.B+\Ulg9dhD*"iC[;*=3`oP1[!S^)?1)IZ4dup` E1r!/,*0[*9.aFIR2&b-C#soRZ7Dl%MLY\.?d>Mn 6%Q2oYfNRF$$+ON<+]RUJmC0InDZ4OTs0S!saG>GGKUlQ*Q?45:CI&4J'_2j$XKrcYp0n+Xl_nU*O( l[$6Nn+Z_Nq0]s7hs]`XX1nZ8&94a\~> endstream endobj 23 0 obj <> endobj 31 0 obj [/View/Design] endobj 32 0 obj <>>> endobj 22 0 obj <> endobj 33 0 obj <> endobj 34 0 obj <>stream Hbd`ab`dd ,LL ( JM/I,If!CG_~~? B L;g(ZZH0i`d``&MSR+KRs< KRSsrF+!R,VH,H-RHJg(%&e+dixRS RmI/+)L-w ,HUPHIMcmp113gѽǛ@Re/s7ˏ:ؾ>*d/~+> 3[7c(prYg}-aW8qR\ kM-s<>]G ;ߏ?v_*:{[׈TqF񍧟Ͷ]^o}ҪSR;z~bGyB9/ ߘ9ML)2֧H_"cVWʅq?/@* endstream endobj 27 0 obj <> endobj 26 0 obj [/ICCBased 35 0 R] endobj 35 0 obj <>stream HyTSwoɞc [5laQIBHADED2mtFOE.c}08׎8GNg9w߽'0 ֠Jb  2y.-;!KZ ^i"L0- @8(r;q7Ly&Qq4j|9 V)gB0iW8#8wթ8_٥ʨQQj@&A)/g>'Kt;\ ӥ$պFZUn(4T%)뫔0C&Zi8bxEB;Pӓ̹A om?W= x-[0}y)7ta>jT7@tܛ`q2ʀ&6ZLĄ?_yxg)˔zçLU*uSkSeO4?׸c. R ߁-25 S>ӣVd`rn~Y&+`;A4 A9=-tl`;~p Gp| [`L`< "A YA+Cb(R,*T2B- ꇆnQt}MA0alSx k&^>0|>_',G!"F$H:R!zFQd?r 9\A&G rQ hE]a4zBgE#H *B=0HIpp0MxJ$D1D, VĭKĻYdE"EI2EBGt4MzNr!YK ?%_&#(0J:EAiQ(()ӔWT6U@P+!~mD eԴ!hӦh/']B/ҏӿ?a0nhF!X8܌kc&5S6lIa2cKMA!E#ƒdV(kel }}Cq9 N')].uJr  wG xR^[oƜchg`>b$*~ :Eb~,m,-ݖ,Y¬*6X[ݱF=3뭷Y~dó ti zf6~`{v.Ng#{}}jc1X6fm;'_9 r:8q:˜O:ϸ8uJqnv=MmR 4 n3ܣkGݯz=[==<=GTB(/S,]6*-W:#7*e^YDY}UjAyT`#D="b{ų+ʯ:!kJ4Gmt}uC%K7YVfFY .=b?SƕƩȺy چ k5%4m7lqlioZlG+Zz͹mzy]?uuw|"űNwW&e֥ﺱ*|j5kyݭǯg^ykEklD_p߶7Dmo꿻1ml{Mś nLl<9O[$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)Km endstream endobj 7 0 obj <> endobj 16 0 obj <> endobj 17 0 obj <>stream %!PS-Adobe-3.0 %%Creator: Adobe Illustrator(R) 24.0 %%AI8_CreatorVersion: 24.1.2 %%For: (Jorge Henrique Piazentin Ono) () %%Title: (notebookJS.ai) %%CreationDate: 4/12/21 12:26 PM %%Canvassize: 16383 %%BoundingBox: 148 -486 419 -160 %%HiResBoundingBox: 148.5 -486 419 -160 %%DocumentProcessColors: Cyan Magenta Yellow Black %AI5_FileFormat 14.0 %AI12_BuildNumber: 408 %AI3_ColorUsage: Color %AI7_ImageSettings: 0 %%RGBProcessColor: 0 0 0 ([Registration]) %AI3_Cropmarks: 46.9999999999991 -550 508 -106.999999999999 %AI3_TemplateBox: 400.5 -400.5 400.5 -400.5 %AI3_TileBox: -100.5 -616.5 633.5 -40.5 %AI3_DocumentPreview: None %AI5_ArtSize: 14400 14400 %AI5_RulerUnits: 6 %AI9_ColorModel: 1 %AI5_ArtFlags: 0 0 0 1 0 0 1 0 0 %AI5_TargetResolution: 800 %AI5_NumLayers: 1 %AI9_OpenToView: -956.013772651195 492.014707619762 1.00000201502252 2774 1678 18 1 0 408 191 0 0 0 1 1 0 1 1 0 1 %AI5_OpenViewLayers: 7 %%PageOrigin:0 -700 %AI7_GridSettings: 72 8 72 8 1 0 0.800000011920929 0.800000011920929 0.800000011920929 0.899999976158142 0.899999976158142 0.899999976158142 %AI9_Flatten: 1 %AI12_CMSettings: 00.MS %%EndComments endstream endobj 18 0 obj <>stream %AI24_ZStandard_Data(/X"> ,%Y%jKi)-X9<@  h@C@  SBR2d&1  ` q3_RȥN)fDʶH|P0a $$@*ccTCq"8c!aqk!Q 8*h,A(8|xkViEh7S<&Mͩɨa.}TˑUU'f8FUc8d_88yWaa1"} vݚreMwԱ95bH(K"oll25i2#8΁ .0Dҏ9}vm Bz9' #1F/1+%bQGn1t;޸1{x Sv -{e ,,cq0 awƱ;|`1]P,jłA cQo(Z8a;qidcXFb@Ɓ>Ίf(BuQǕ5xƑ@wh  qVGC6Gk)./G"Ġ}QHGP03sLP M}FKJsI2|iVޠU9GBEt4E 磎 <D ,`xxLksIBxX8Z;j{GWW l`q G8ʎ3cJ1EuQ|8{faP$`(OCA x&Ɠd$Cq ,N5ְ%[-~GZ ő8ǝY,T#xV H;(h sW2k#=*m[N5,2\;]>aQr/e;-t2IWlٱSޙ+jpuḏX0ӣO(P1^3O:+ s`^qL?8.8ʉ:L C(PDb4BCPz$G+^D^%y|8F3B-J}P,$Y[8 /B` X,ܢq+13F"D /$㉖o `dA B0*6bҙ9Bn9cr UMCb.D@p@xh0xY*ɫ }ʐV5HUıK*,5 q#.%fM"2GF/T"9CrJ-C2oа 9ck+ʼ{Uhdob>.tsi9brec  2IL`x$'.h^h, BHTXa)Lai8h4ڣT82_.4h0qhQ S[Zh j0ZZ)% bЂ\ Yta F#Hj4CF-.ZUհ+(cBJDEFG&S 5hZTUVW.#(E-QZYٙ -0-a []ݝ/8-q[c\泍N +0 /xHXhxhDL<&P d`pIYiy<2T"ȅ/x D%.&2D"H,D#8FQ)JaSXT\P( CP,(f-ja ƢXXL1q 3q$CX0c16Q k\ ldc@L,( ,Tv^E1P0!?lh(cJe8ΘO(И@eg̅/O(ac bAXH,$ [[.,4X476q kPF`(g$fV0bX$.na:ł`(T(EXH4"H(%,Q&&Ww{Td"KK2p <&" fxaN8>X8-nqz 0-,a KC;3+5QR+ eU 2޴vV*cA J JP>m EhײAYi1 ($F9NN΍sT8S"&mу袊D'{=+괱041 ,F1 E9Ί:: ƒC C<(K@xh8  .` XhPa$ƒ>@! #ph &tx@A88H<@! ( *D(HD ,h0hBC,@A"0@AB(<80D4$AB.0h`P@"$,PpC ƒ  (0A4h$x  4@`Ѐ"8D 4D4D48&H ""4 ".h@A h@AÄ$D4PЀCà  4hBG @@@@@48 A, Chp@A РB  $8@hxh!"`ÄI% #`' "<8LhQA ^ *Ph@ xpР`@  p@q dͫ% !8@$ "8D@!C08 xhKr,K]bD:433q:nޞ :wX @'D4Tx@R bB(4D`@$,T ä * f&@!"R F*+ PHp .S#sG`B","<@88Dh0$1 (Ph@C `p "0LR@cXP$8TP8* QB  N   B """LR  !x AAH!ABC   &X &D@HXL"",qeB1.ki i%BCGe8L0"@LPI (00 (p`(X@ :@! (AA NPA " RA#RÄ 8H$08 $ &T ATA!bA:!A D Xd{=Yfկg7TtbUݝ1뤑.fk'kV0|+m,U$F5ʦޖ;;h89"z^߬W*}uCxWg6A*:Ty*Κ+;y?[v3α펈sd=:witgB+9D9}-۫6|JAM$7}U(IY!!N OWG3 ]AQ억BDwUff9DhO9Ee8vg4b"r|{vlGQZU=ޱ%#bsY FɏleoM[WfǪK%L_Ŗ4u>ETj,wy0M49FtwC;}~yXƲ9{N)6H=̦z6hYDJmXnv6iTSīrt;"Z-k/"d1LQ휒-''{L/f\z!s-m6E&^Dp%VfZ&|]a*XT 8Y˛ҨrϖSO_ꔪ)ZlŲK4*DDW٩7ň irư$d;BCCJwWJo>>eA ʒtU+X}\\c U08v5XO=^n9=JBo7UV,2oiTL%Us<"gӥVm.Ft"GC-7T3BDwP =Sj˒ݩXb}?JakgL gʸ}Gj˭yuf!!֔ VuvF;e)lb,&ݟV[\nu*Ύ N-YkWދb. V^V,^gBlnF.s-)}5+*~пuSbkug^Z>Z+>MzNh/U[T$W%BJy,7"H3)C=g㔦^.ׇG<ͦIfRnuNO*D:ɼ ޕ2IC-ON鶤2' 7Xme aU kCOkŭ%ѥewߨi~FGwK: b_s)Jeu-#sJG+!񘾂wg-QjRlJ']`UVU)ſ:zKr3+˧|BS!*!,M+~jMm]rhe,nRb,m  ?詼ѝUVee/fEj~L!+ݴx2?Ԛw宔Ww{^lOԃtk,v>BߩSXrh4rɅ%yoArFL<òMF~TPZk5iirMُQ[S\[ %]RS{e^IwNz{ZTQ{o+dJ0KN^H-8Dgxg]fJLVZK۔bipJĚv2Cz/%WZҝƪ >Z[*Cj ),xu;rV.+SXUeNeYQ낄IG#cD W^ڸMɒNͦ\;m kxe`IϬWWe.72 eTD2GWU{ư|,%r/{A$[dd!+kH$ή’-+^cF[NfT*ω$є)rI*bT-SYrUld[% s^?œlt ?i,ؕoUl]S4u:K6jɤO-W$~L'VIJSMF? I%o3D WbF?2ҚoF5*ęm 6#izt4$WJNҧsN^Ŧy.׉GlrfSaߞlS7^=9j6vnʙؔF^ϑy-|KmT.f#=TB7妛&,dbeY9N*|yIkR?MR&ODYUyVyLO_~V3;$T4g_9UO?/ "Rt!e9^GHNba6hCFzD-NA#V ݒ&Z9 zJ !|M!lfbh7IG#2CcL4Q)2sȨ =㜽S[:*eL]WR{gIO>CA;ڬ|Z-eHwsp:s'l;3Ú*6g˰\\D;\vf*SMb9I`e]PNrsj9:vٍOE 3d2Cւ'Gckizrը @ | CC$Ѐ"WzXp`8p $  Ѐ^Wu08`bY*Δ&c^.`ݩ9`p< Njq}\PK.-4xO.bWu^bkPRIR>4+f+TeX>3uŨ\eiW~Lv aa<'S9:`p<(֕I,M,S%o\x`SnjcEwYʻrٛ KeZu$B4{,3YQ̰z?7秥z%Kո+/Y,,#*QZ]BbG?,4zrWTj*RRa&5 q4Ő%3K,3MȨTjKHvfQ%|K:\sVL2"J|\7.$MφWάS\[%{[jF)I'JMҘ\YEJڞa)YצJ:>3+]zޚ^2zhT*&kE,V̏}WF*jȐ3;5t8vCCw;ryzb!|`cj D"wR=)LS3d'#+TRR>e-5[1tn,2,jUthXrR y/b˜)i5vw*c|Pң>K]w hʜUߖ#Y[Four6KgGD*.]dvh߄kl8ǒ]m1<]R2w4 aS1^:ٔ<;]+SfٕIB*` <£[:2)|-{)IF3|,-Ωy{VS+3шY,ٟY$kFGҲ"fh$FB*mT⚥yI$k3ǚ Kn)p5f~U/<4s!N=!LZ.Ua|>{ߐ3Ia*Wٹ"fޛH.:kWgxv%JKDS3CxJySD7y6zf_4zz;MULu~.~pdGgSsb̖A5E9Z /Xz)3UIg6aPb\WI0OB#>)}ҘݠUc߿L:B5EgMӋ&""!`)KS6⫓wmkYsʂlӔ~T*lբWɴlU6+sH>YAkm6r^^uF%<9%#^)[uUL׋}tKս+;z?[ 2 1֠9d/σǼф&o.nYnNY,Dʰ&Yy7UlΕXb",K0k\ v& uƈZ]xS%w-"eHЮ]rEf̵!U9|v]NS#wN1 cS+vU[MX""U?gU "|^٥4iM2C~I4ry󥕥bb%&V֊ 1OǜFB;n׹4f%B+a[GeCI>BϮݮ/SSrS|jZ=k-kRwL2+KMƮjJY[5WMQ+W1h$2L<ߔj;髆>vygU35.QVZxyuج>#g\E&+eV}\ ʌGxX2"K<*KBT٥L&ƪ.jL=_5/}d61!̩$IfuXYiv9דŲUFʺ*1ڹܡ ͱGInrWu\uI_.E,<+2$xG:U),˾)g,c)ًov9VEcVc9f59&*ъ݌ّLUr:uDE)홐xW5hB&wCiVCS*'j/h\Z ak.4*!YVOh)Еi -Y5m숈2,8") XPB! Wk)G9`Q%f7BDʼnWUWTVD.;NsS(+]t.#yf}BvzALGYc;2OcԴۏi7dٴB7v53E.eLZv:<ٵMh:&J{:yYg)<̫U~M!:V${KinY/l%segc6 bo3Jo|y*WqfgjI<"6"4f *_+؞/<*KO+R7*{M]|!"Ol%Ջ%D0~7^9,3TLG.xd3NyrUzZh3iRmJ9VG^Hn&{CYZ"ǻ{W63Gd"2zͨ̆Ȋ`[:*:+%Z؉nhi^1+UKX˜[:#vWL*u$Y*QM>a͹c2d~:SYcž⊦H_fヲ+cfuJURRDTZzTvƛmK), F,<a+ ڇnǤ+3h6y˿Xl[{V%omJSU\ګy[lK»=¹Rr&;Cgg1֎:UXeb68eSt1"r}YшavJÖ'&:xǫHLF7}35U }sY9Lsy 1ujюvG:cH#b陕4Rܬ$騵4,mϬ,ct7g,J&GdETnn2[썼EG}e1ؽ.P .8?̖VAKrWo kfu#|6w>32LXǞTg)bs!42M_usWleѝURuUYIuc+eUYǮXr]ջY`{Ӝ3aBWmyXh.JJhOel*Oȿ{z-H/RkCSXUv%|Uej!2V.&_>o/ {:ۜ eyx;SYKmo uu7\O h^,wӴ7bFW4IHꕶz0hrKefM֗U%o ]ɣ]Ix(Xwx'K_XcE.f^ M%4ŨɆm~ϓY^Sh~R>H VlfJ&a݊{i'Бޞ\qP`Jl.B#s ˈ^:FlWRM _Mch"xY+}~ogV1֊yʥFE/(n ֒f!@RK]63ǧP2]F 1g P[񪝘.{80*j9/z/0& (}N@BWw#n֭S+d[dR@;mκdׇ"ri/,g(<;e $ƹKX^?ը&wqD7 Ģ DA|Wϋޤ˳Cn:n`ȼ5^):.cxwł--eu4eyK4xv>VlHkfXW6Y liןe@\v /ĨvAGR5-(Y۱n4{'<俢.8 ?H8ғ@^~x<=V 1} UٲߘQWN3Fܻx, 'ؔ?ٓz:yXdXScaIAfPS8lǙ B#m걉k99W>Hn5&qpqTEN7o\c2,B* ,'Dӫ 񁢬r6Yh̋HljaΏKTojLfbN %т hmqFuf9-3Uq!憌bamyE`H>t f@26PTވ٨#>3W"D ޶jA=7+J|!S@ 4V0|t)hMwi4OQeڛW 4LC tRuE]VD#:V%$~ u+g)[_]6%#.R7W,UyrˀMUMtv:q ϲ:zb*?eÀ+G HhR9BiEZ-t GIPjC I v.$sT>}4t6A5d hm3$ucPgO&+P:)NVO=̰fG6x@N61(-: u,#rA{N\Zun\}Uȓ^1RJ% "!{`-xȔ']YRof~=֋vض]^f9!j}[N3^~巄JPFnĖkCp:3]LA7X(T\ Wر}M+u!,?B2  M|UUY Q9hE"m}__?,29휉b;5PXxD s,vD+)L|;eIv`-`Y#1)8Ϗ( ]Ɂp&{* W?]lkthӷ ÅSeQd/Sl1:YCx噗s+ ,}vv>TxvwKZn. ijXaYTAU~ws-d*uCk<Fcp4W#joΜ"D0+? 7JC>Ro&Pv" \t )hþѿ#lC6$P "vC'p&5PG1cӹM{g_YY8n5xBld|C_V25}P[LT3}(& rv7|57f1Y5bQa&j@#~cI؎l7#?BcjeqY0zLy 1ޔ&IO45ɖ:ڄMQ$1/jwmny`ywyiI{YHwIlk%i: OjY)D[6T9s[+iCٻgl#ՄCk0Ь?c se7x%HQdPCJnJ'X- &2iBL /ܸ%%I!c3mgNaW+u=NJm!T4:yC}~-]|z^ ;v/jQ+np] "Soª bbᓗ-Ŏt '렼ω[/xu)MWk @$ȱ=e$&Xðk("ʚnJhRf%PƧMŃ l EF~YG0-{M[rTK x'\E]JH9" Gq]ɼ:nk29~fY"{7x" ǰw_rz0nC;xQY*eyYF3o^`w:RB ̴y|^Lqώ#WX(DAdHʏj+"ԴG~X68oC"%|9O.{٪., dH&P"ȁMcr`jn᭜lMB:˅>GᱭHIQ+Qo֦Bv)- 3q0ɐ#oO,':pK_ g4DΫPL͹^ Hqa@KMup3vs-6 kO;z&raV##$NKT]zcp-Tǽ*!>'ݔA>Uu W=ەQHS|~%RH j'a$ t-9>~#~Sb}T,o[~1?`0 ;h+Kw9C%' /С, ]a?};:XxN䔍k9}f쫯sW&r% X#Y}]-N8~B5mG,*r.v!y6vˡz!yqr ް-h@ҥэU Xj˩D@#Uv/_|7gƵZSKgב,jh z@M0 |x ˅ٷRs-ZF֪D*hv];ߪлUn8f7#Tc5Jݜ|ft6eFV0O{o9w@C;~gb rN-=bΥD% `"NŽtj^?LiɆ6Jn8Tu 0~ЗW$Q~2x}Iզ2U?§>ey6$ W:4O:d^+b8_ AQ8Р/P^Y\Lnr90-'ж䆞rOX̸0?DwZ"a68W61PY=:C1QdqRr~B~MOd 0|y -t3dvi餜JNT-0nF{މN\Pꌿo)MN}xL&t0\8#q<'M͚F!8C/,$AȼG $.>F>]au_A]z1&#\QgPeI g$Q~M KqS7c]F|<-nNIpi3)n+Dm*NAE?yHe iʼa||b'ژߔjy.S5m W8L](,! rp,|fѴk i@זc8@ AG \ }MU,RЩ^/+Iջ 3I1}[Y3 _8Y!HÀ+XpOh;&qjUMHs{?: FJ°pUS{Xl".%ݴ9F{aM2D80o/61rƗ*xWcڵt%@п.@$! g.@h0?vMPU!myċya ȥ(w$4u{<1 |X|z<](* Iec[O$ lk[Mq*h"Fi1- As*r-g8A\+fƏs,e(^R #8(dNG!6w!r:82Te/} ERRa%Vj=뛝WG!dO?3ifGUj> x)BFK[-ivQ Vk>CLA;-"B[`ARˤ!(SYpej,=sfw"3{X(򿾩ss<"T ap T3ao0ԖPc^_ȹZk!Gd)aÖbDL]u$m.GPz@$>F؁ )nj`zRơu}rEIR;,y^q[:=T(7!<>=3C57Bn?qD@}CA A @蓼 JàG 2RYdΠ͗ƝHзFk=>K +R`jqG3UѲUPCȦKqSyZW _Q(ᒀfp:9Wլ^xwܭl30X[XqR~8(:2t3VG S*\dM7BqYyDo栛'Ka y6LFhp(R,JDz گ򈆇-8"+cz˚i7%aU(8+o\~|! 4S6O۴rZ7Vд!c bL5jDW;Q1g퓂AD>; cLUgs$)Uxr)זRuC+ T=(\Z!FVZ5Ox2˵hNM ƷuMX \Nf$M2pEn?q쟳FrpCdquD3~ $چխx5+GmɅ̗Ӆ}Ȑg>\F9y$?`̌gA%nOL[24#2;K7#Ń٣cou"oj-MY>|ǬA"1PM1`tkZ`_ Bu \0kd%_=_xS"=36:1 ,F.ׇ3<7 6!DiӁtWqE&MV`HTU vv8a#JD3'YQ3 Bpd7ӿIn=,5H\Q>w]c`yF|QIO^Mt{:\4M V,@bk\:{iXƍ&,d[H%W nxi ճR!9hۉ&Q0%'šbI6pLgIIGoAJv-&'`WuD專~բ$3CX,!iwj?ip27]b8QJGfP+Vmɲ*G#Ju Mvp.T$zŮ*O&L&Yl1hAW`e+WIJ`yJb\R f Y]Yش%>KE eZ2 MW[*0X_L&[bW]%s.jU_wѼV]?6,x>k >K-a=b^:9̵bQ3|q1e.调nvUH 7B#`ø,aצo bzf{$`/zȃ6wLfYw˧ M$ęO"eέhclD ~Sf$e~N̄,3a! VL wq&Ƅh{DpTdyfqi=͙t4৆bj0BbPt ,|i㫰رY/u~6@AOm}OCQd5>eG\aߗGNjX0oAG&t ƯZkQ'T҆@Y& {zyW- Mu1eͮDqM#Ư7莙 @lPbm_y]s&"Ӏ E+X/ /ٱ=f|/OZ9]Oײ{Jsj81qbK-{Q}.fru?{c%s}tԹ9QfR}v牢b+r)3"7a`Vrb#' u-$],d+W}y5:LzG;' @xK<#nk7+SxLںmW >*biO]odv+}ڇGg}Vโ,}fF³<Ѣ쿗bU% aށ%ɜ?gM G,.4 ֠c<=(/K3k@6qjSU%6*Ѷ~L8PĐvVT, E-ߧ6IbybY5Or]3̲mqʂ8ފIlLanEixM!4 BEb8*ʚ m[ËZKeNmY?hhpAн56> qtQ'^:InL)g]k 'J=qb*4Dbnaex 5(4xۆP^ðAG{ĂS\%B>鉢3 <귷璌6' &@Y/gizXbn8ͥ|Ԗzh@=(D"" HA둈i=(?iz hTU*x ΃r,Y&[ 5:G.$HI4pDZƝaW\Y`e=D]9Ӂdm[מ'K @=C7SLI pyy\zzz ) ӪF44ZZsma=l] ]-88 <ǑCMPC*<)9Z,[^[sϳ¢ẁb4 B։%S5:¢GyDBt=x}=>xբ6 V%(\ OoGS<["65OLW̝ n>ʻ32#wF:-mKEV]?NSvȒ; ^ƅi}Y@e_ݤgy"WmAUGH Adx cPf ; PE{bV<PK?n@Cu %pJn82L$#McH"A~_e*<-ۡZwh-zlL )g_zxMFilζ9Dd:*0e:T1ɣbLO/K',}bM$c[NCE6Oq TT0 U#3lf &uH
p^)oܘA wI1OQD6܇q4BWѲڒ[V 60 +d R}ZE=;~=޻@lO|̫hzdkM Q|wZgQzB 2Y}NLxÏ}>Wʧ2j42RS"G#lOЋOp^8 oLKp1riU.;-/o|?P/EqUy&~%(L[kQ0a *.t}HbAgH&^bԉIՍ¿L8bD=7'5F eXQ)q'Ұބb [7t!tM͜h| "Yp6h^(~}WA,C8Yuמ58,?O& h %CŌP~M%oa1hڗ@eps3`{]M*1 dݒ^^Vf 4{@BbZM-΂Jho>?;^;!2NSqn ;,x8E=P(9kj VsbCe^ED(IL$v,?G8c8[Tѡ_R4R]Wu'+6R_={R*_xXL4E(0rM-f|eyQ n=DJg­A/FG?O:g9ͣƮJwu^?p<;[嵲ɡWW,lMY3P> 5նN"/T B¯~l zg-6xL4Q6hG(_L.ҁTX0X e1hiA:)[ÕYayrAp͌\\o1]s~[-2l"ŪhE}/ީx#5 nA0\\eD惣Qʦ8#҃F E''yݾMG!FFa-\aK 7[x^ܰ-}Ry`Xiٿ_vD@n9ݔJ6vV[7y"o*YDP;&Hu: a20Ξ+w7QPm@r ~dGIgu C=kaj5^1l'ҁ)ĪDe) FvjV+ <>ám,t8<_G;+aG:[ 'snA(&`*wOOTнl ?=U; ~V%.{NiD ҷtKa/ BWx3T-MS^ dXT'|@egdkp=&ə;3gqў|Ι?' ޛY8-<;&L%rֵy<%SZ !c[TQn I%)ҺԱt/(8ZJs.uqforOlB:QC:%vgNodoluXľ %KܿȻʼJ)TgeGO^FTwKmKژXPm'9:.clB+%:GY9 k<3RsǕV!69 ^UZՄGm\J49sIG]F 4Πjk;EO?͔EI=+Oq)A뽶ykч4 34J (hER|6QQlY[QQ%JTC C_ *24VQ94pI1GysNdQlt%$~ l_ڦ%$H:L03A2`'adAdg}l3/Xa[ ,=V&$GNJ4vdR7iv6͌.i=R#R'̤R@R~eK A*@#X.]-28!ҭ2[o/dnGZ,筊)x~7.1Y G8V~ӥ^wgX;wPMzMƶ7uBҋ}5U\][ڢ^S`6VH/^mPg=!Xt%B䦽O.1msdo_{g^s?iP9 amB%%) Lsgі6eaӪ]%+ |ۍ6K:L=JJ_6sHeMZgdaTI>7 {i @ y*0'*)]b]9iԂYf2y@&xMcdƫ,,M [!|&c3VC T `*sMkHK2+l.BUX#P~Ր26IA Mkɡ@:Sp[ZF5*nqbKG%{tv6ڟ =^'b\tCCꍙX^ğgr Cro=, DX3UԌ;Z;߄B}˵p t<7k3KEiY][kr>"1 YK1#`N)#fvSl& gtm`Mw_hVWTpb^?T _rф(1+|RIMz-\VAo3- {_%\(' I? %lIFS! vcYʼnx |@B_dL_]Oh?)iE ԛ?uiT}iϰ*mqa?6pSE[ϣ ?cAA= 1hJMbg*fnckryI]vM >rߋts܊(O-B cy/ġIq5V%ݎ mkIj>G)f kf6z-cJJo8-<ŒaVR`W̫ `Ph?EΓlTv> yCOXA[E^_ xJ Ո]Z*}S};K{4y} zGӏnpӸ pph_<.k4s|sP(!2y{C/<d xyқ4y&EIx@j9 N=SBgҧ Z2jrRެڈXmhÄOYg4}S37Gaz'4ul;v|Pυ~NnF $D@5>Ǎz"]v},$Wa8I{Gt%oDN$R8LX!`•Յ'$M,yU,M8FQzG#ExQU̹WE';Jl$'T๾Ș m7"X' ] >:I[\0E6'n){R'L+{ȭ_όY`b"i%C}N&>uJ>9s}f̥|*NxQ}-+?9=Op@u@L)iVxrX8V0+҂mR"GfpdعNCn\nAAoʺr>l+UԔAEK!r4XAoQU:)[7Rd}n*m]Hb25'w`wU~ .(f mJl\醼w`vz;iA&ڍp10\~;SĻ3o 9L9 3M1e).&P"'?vR4׵ꗗ*RFX4m>,d@ vhߋ5P fVꯦ {H.b8FJ _ҳ 6|r_%Y[n+f^QaXa)3l)2)vӸ0$@ bf pOV*!>;6țY~J&;eŜ\ $ czA׀A>mx %­ğ=I>Ei8mJZ@^^ތ}0>hdWx7sk]kU$2!+0k:QJxR3z 3ȨVRD@-`r8:e ۳9=8٪G#wiOxI=KE/^ٟ:4EuYcފwN uc g)|c3 5ˈ'%|uT7MXL+($V/(5DbWk< tjƠH<#ɷ5aPQnNK䆓Ϝ(d0S3LJb4֪%+P0ԢnO+*ӿL[I]v#m՝K KN1SOׇ?DQSEtQ ;+{`4~b#RW.R[S+.J3񬓿v Y\|_́Ĵ}&PޖXq8pMƙL]p.7[uw^ 4q{q=$qEɉ;HϐYJcVRxzgU; Ubi/$2xQg;.J A]DE!muq.%"^K9^/5T^|`"&C}&X 4婕g$"bcpVwiaBI^CT[`L9Mv;vJO/BuZN+wƓWR 9>а(V$3JX.Ⓘ[~~1];B:7wx6iBf2 OPhٍo_HMka$) @zt Ri^:U/F,}4HmLr^@Ez:`:\LՕqD q4͈v<52Z<?$ŀD%τ׾i,u`)9c+}p%W,a`AɩD:顚LS|J9 i+h(K͜V"p~ Yeh'[qXFQ2%#1daed{8b*ӄLѸA3d_+t=A("k!Q緿XvOFuoo5}oUd>}UɾbO{9Ķ8r\soL&-,DJ xٿ#z-'dO,O,TxTKE < v~ļ( M.gh׋Av~{M|2dX/ӛƺu H?9ws=WBp[H,ٞa1LEJ] Y%aEyI=w;q>`Է+:VLԷ,siSYWH尤O[}g*a_?A>"' r"uV_Y6yk'j1c_b-V:;EH`$ N|j^%P1*ŬQ?#Ly3M3< S-W‰!Cpք ʾ>Mު}.{B@Z7O ?X4h/]B m`Vq_"40b(a.9v&fX*)ez ~QԨL3ruxK(]K]I㭲Y}Ҭ(!N 5={|VIRSỶ8fb:Ҽn. @ǯQ"6\6;{&#e]Z`47OTPoVLjM@?eH"+HBψNNTӷH}LsB+*!&C)C4{a!J!Z09&F,CU!?ۻk?p%4"XPkJ-1OjűP^[v]-䁳H |Q u&dA!lVS[ z1c-m&9^ٞ+e-D!qlCjҵ~j(U?U5:U51U@*(EAjWOU˜ T LcP*eIņTQ坘`'' Հ̓û^O!?5885--t<½߾Xo.5v.ܖW~?=9xUel7,%UL?sO#kY5 $nO3ШX`P֥sweڭbv+d D."j־vA&h\->RvIvi>P ,ZXe]z+]cNI+iwm߭S/vLc#_Uup./6maF-t-|af$14"T€|,-}mŬ\k7e,Q&d&~-#Kn E2#8(݊W:{)p:`%lmϞ9; 8S9L炊p ?(*WP\k~rI.m}-@JɇE8{"\ǴfBePp=Q Roir`sUb`yDSirmJ.Ɠ$ GJirJ >SV D.(JdB,P3FQ: و D35,i^ߐ%h.o'-cQS:`"4xb1bˢzo?Ut]f;% A9%DSRV`[e*NQXLoW*Օ6@^G9ژL WL6&u+uD=CэDBFÂПmn ]0Z\$܇Q^GZkDYQW^K[c-Z17~4z8hrĨeZT5ZB,d9;pm SK0w=ox7[!߳` qre {zf6PF66jԆQ@ޓ;[1l4{ly O'~{ чx=w!s@@avK .ъbl\ ;)em$xX;"5SIEy6.8,zVr'#$LXFo`놁Ng)vT-(N{0n %s=V6ހ d }E5 ٪)??ĭ&(7Ab=vBtQdmR˚>HGV$_|8&Z2(o .(R4'Ŏp5jZ%J)CT+RR hx>(7ȁ#<wY>ro> q~5^!P OWlgY%Q=TN6$L5 )2R/.A)rerLEhd`( x ZCx eAk}AciAdC.X/7 *dJvoT#SI^NP#KC]{)FNTAdg6hYi0w}v~] Fׅ2E3l?N)zqn[؛0; 56Y-rZ"D7R V{1u.6P6.CՖ=vpo 2bMD"hK%`n 0죇Ae%r)&lkLCTT5-JRRw%qԡ.:6gY?P󔑞@N@(=2ܭ@QUj7YS=xw+~eIQ ':0k\>z)pdMH-(2||;mYc;(1UVp Tk7gMpm f34!12W30Ktmy?`%*@keU;isG?X<ԍ"egا5xnFj'LdؑXP1G͓րKB(?1G#.(}C 3_C6M 鸡h|0|} ۂ>=pOVt@dNTx(c~7>>ed(A$"^i#c3bd~h|/r<.SJTA. p%@;MBL5IZOCgB`FV~:Cd'|FuSUO# uS_CPQ ,ΊN 5(!FErrw-\"3d AETȖ]*Ĭ' T(`h_TIhg6sn/I߬6X=`¯΃S8gfN.p[*WrPQ:3 0qVR&B,€vYhl,Kr0}hô"R~h)kW{ 4_&;}a6]QPCxi!o]Yc%'4;+'O ޮh"~z. ˣ'$Us0F5;x9Sp$hgv0YndjuC?G%;2C Es)܆ =p XmP 4m)9IEM!$M`5z@\psl1JaH/tFJA#+T`/4z<MR$e<9+<$̈́7 HNˊI9{8Y~)+WA2Y 3 -VrbhLL)q3R?)Vz "&䉑<"t)6b&"4qL)O՟PgJQaLm4a+PBs@5}G"T6J]w?:L͐m7&]Z[^I'hhhF$PP"M$~G :ܥ:ŠJ$ L֐.j m66`Q_!%$13y6T(bj Ou͋zEQ6'smMAv34T1!"Rll15~!KxBH%!4QS8&2Z КjBBe `ksNx-v4&3-k-5LAF$X5ŪQBECDCAFEU40vqWH1-$ |Ev"0v؛' :QtiDIBQE7♹d)ouS(ױtMב5$'FL$҆T bLeVTalхAGC9CF-WM?4J|Nd,3K V$DIй ۊFC_r쮊!ZZ VI،Ih =*)G\> MD5Aj*WFXE}'C~B6hmAFVg'xw[z䌪}D&rqXi/fLq! CSSS)ljFR]z3%dr5a) æ,"RR( J1'TeE(]tl\HMhL$<&" A !bB kB*B4#Hb$BPB9= `p")!B4!!bB$ 1Dp}YxG*A =JppJ&J>3BC6a:Nu vmVXB۲&Ԭ&e+e)-tdXNXmWj-^(V0-@061>T{CUT§j[FTK\ pWNnF7N.4U3P;q8EE-V eLw)RDDZ943"' RE"=+=쟨 (VPNp'©'H2m0} i+CB .N.g" 51$ZIc8;`V0iL^9vNogv|)qoy)A*RwRglwr$XZe$6ƾ_' _#1?u VּAꤒR]eH!9Tpe(u8 \8HKlj<( %'!SH %G3n#XQ _%!=. 6.gari*Đk7ɡWn&$¹*u9 ,Y <)HsKA$[U&%T(%CI"zB!#Cř|&%*'9zq7"',sS7i&5ɐhhTTdJ[V*oig5Y4Ģ?Ɯr/UߚW+wHb'E e#*#c#ZN*1􋎔]zi_F-z9Qk"'B+Yc'o(kթ%.9OsJ=b 2#sՅ>TYg"Bt2DF6~Ya*v*L{-GmYBٯ+Re&] <Ho S3yJUjOpW\R "^s M'c̜Y8Wj2U&DM\F۴ 4UHJF̈́&Bt+$>d2i#zvF)⋶h)<"Bb4Lp|ʩA!QEqgi1ٝ5jk"hs#x,ϼ5 !.Y[թM2Hf!(5sSIi>U 1q5zNVKb֍#zqdҕ[Ӹh(b(#e![Yj.p媳 8'Nx6!4m 8P:',\.U1NyL˫XSWMZ-*D,I(0D0Q90T=~_+Cۗ?_4),"B8FXP3:AeH%Lf&4D8XjJ@4C-TPj`hv aB[.T 0ak$/e0Oʄ%𣩅 %P uA*!멙\LчS0Or:4St-D:eEpIL7 <3)\$&HDD.TR3,txa0MUI.YejU A UP_N11u(&}Jz^QI/h Vň&FbA AY gaK3/2Xwvm>*+L-6'ndjچJNpF&Vo>' [ΎN+G9Rq=x-=x4H=iHOUfJ `>G]@# @ P2 !pf.!H1(Xw A8(@cP @@@@)  B\SF1UiMqrr5%m4!(%qHT$F"s;#'d̩ddRAAeƕ Sk12BF~Q HLB4+ҏUmP|fR>ӯIee@vto&!GKvc'2q栭_IbK3%VeJf~.~Ik~#U\$)_xu4u:si BIBRVPjQK"(;B*(U.F f5Q1 -*PP~Tv&5s *H(d 9=!-# $@d@к8F;7vB_?r+}ISK&QDX:Hn}BK)[1C[kvک?uwBj^2-N(mZZms84i9* 50ԸEUCi㩽GaDLus2EVW"TIV,'I #EEb&(ЁS2py y;T$l#xVJŔc*H\QuFGğY2 Q  wV(%"hPAZS7фZVe%+]2P/Qxo sG%*ø>d( c:dy r&с $D-PBE(̘*l"/(r(:UM(S AMhڨªdR U&2LUF(0U2轶%%C%UU24*(J%rj(T9TU^ū«dO4$H#"H%wj%?a.%K h> vr%P29MbYSw&h:L\0q)4 D" qPи *6 "i<)n! q uDI/]*2SQVqx9fFbh UX8^ $T:\ B?*F#L@_?zW<@iTJ颥R3"U3v&JPPF(TTJE , l%I`CI&-|Wi[|%6}pƬ "C5)9ah #XJQ]],{|֢CKίF|dʟ|nWxMQ89I)/B)M(#3Xjb"!d`P05$L"ԻC4Bp8 Cvp6DCBAPTX6H/ѢcA)̿~6OxIܤk=ih%CW Xe*Vi3["V9&LIPKuxNCB/hpbJ^'aΈy?Q5:Y\ ?t_! ȋ$t9mxNAFkLgqK;;W]/Ȧ;E.FZ ֿ3;cC[P@Plu cY2!Ul`oB=ٷb;dkIEٌd_:qA 6ɜSy~rS R/1kɧMl/6^0?R2(9 YEDlk#C՚ofɀϮF(fbG Kh?/8IⓅ22ii3tA˗m*ãaJϳ΃SUdxj.( 7./xU]8B -Td3م .[F LRd ( =' e6D9"fq!REmf!Swx|Xc30=K D.^xZA ;hC> -FhBJBk{c;)z-d&{.Bvvм":+lx`ơUc^AnaQ[G7QZQ\1.6E0A(DEI Ѕⵡ<&xmɌj|"}!$"ax3"=jq {]za9ȪB|>c_B8JKK E8?5[fcE_'0y=L@66ݔ57F&`0ZU38# ';9{^~ >Fepdʪ];Faz2"\GWTd˳o O5$k¹n:%ٻ;ًnKM7as~LhC⑷,?4TN:5*0(71% rZ|` rQ;_<`&p6D,q?׋MrԚ/s؄X'W}[ڜ~+jk9ZWE#7ՠX# 2kh^`Y?iJO12E"DGBo50x $8$B 6z_riنsGA|N) :sȡgU]CP(a>BG3@1c&if3}Fo?Kq ۈ~7 茡?EpnEm]a5uX<:}G6Kp0Ԕ~ Hz/6LY$ 8+F "PdGm.߲b$S=J1>Crd mDxN͒["p{b[bl 'c9bS+-Emq"N}v <0mWVT,8fz("0swD C?f.S%/CU ֣j^A ?۳zdZm!#zeZR Ol 2I|^akkcTǵANY誥GHG=9k8喙*l'X!9=w`(~<(Xo6m8$|N?Pm +٤kQyO11^i)"]WM*`XJx !HY&!``2f,.Mh9N%0w#噿mJ:68#ʋ0S Fp+ِfc Ѱ^e/dcsؑ3y\=ʪ,"Du:0;N3ʌ³cxưV7R/ ߜ\Cs0^ Kd у}S40x$',*&6_s;=Ԛ n֙F%#ϭǜSNbug o1Pg{z ~zŷwl ՟|=-ͪX2ljtAMeU3{@V>ا O ^UFwxmUmC"Jʱf-Jg۝=8):AtmaXz}t[8jkyPb8Q%:_qѿ>^f6LM86KQ~'ƫ8W^3f)K@VhݞH"@urxsfl Ρu*b6Vb5' 6tPh\b"_-C<& 㸻ތ/L.3rԀ4B]ACkw]:OP1F hnAe\Dt؁U t3WUrzItVR#(]yy UAȘ:#>v\b,nU,#m9G?&70 7JS)7G+\w'|vJ`>H7wAuؿ, c2 InE%NUx  mM(8Q7K*.}G>TB[ H.?@XPgSZpCe_-"1:,[F)}QMYX,$fx{:lC@S+[4M$m6V{իP7Dg(^͑JUr8{:%㻺-lPdu)(;J$c {l6c=fkxAv&G6!d.d >xzIT^bx8S,`~[LdB n5}9@@v~O6c"Fh<$9alYlЄHAƎ>&Q+^&}PD˫~!"aLĪ^2_0lg 運ĥMxw Q\s<#Qȏwߣ ~ -hBwV cwwC%'lm8 մ$5-X"@̳AmؓKo ¡,rB*G0̜A>渽G i r" uC}mʣkӊ q3bf(LppXM#S~7Hs`mc6z~7Mx_/IyUjf5V'z1li_/SJ7Le?ڦv6Gy`5r. +8}dC(rƉ/&=oSA4ؓY0zRt=FoW{gd‹C[l"aq#_\:Blr`y7SL"Y1_ˇC$lXvwh=fXʨbYb}WB$p1-_M+DD_2KbNTV3wGY3e5މINoSwj# Fq4k9NyњCi~UTk$b^p2lyoe<4gT v 6ёB/N `$ Tr4"0@E܆ERgL/2Fz?V`Ru8 7aR4&`MDiuhll 16@dWN"6(#=ӿ(lYlj㤻a&si;l+$ #1)/}qGeIr@eF"^tqrG=B!-jg/ύOXrl qxDE`3yiϬ3M/9n(1g0a]L ;ee>);B8d rpa,<-n MA&,P >{ܻw?m삗 ߘfF\?%ո~AqbUg%ص A>/Hpf{88O29>A߰M ciYTC$nXwJYWtpq$xz'kj;XG* Z&| @e!jUPSya*vwF`L,*DgZ-+vЏ%> "=F}9MS( i݂°(&0 P#ԌkYu: tudI` Z^Ij&+Vz XoY3YVyh73;huӓ]/JNTV<8`..EIYHq+:e?J.h4HO*ME} m鐝-*ޜ4#9G3|f\lm]eN E6MY} !021hC:8=ނX[ '\Ldh+~tŽޠY,%kL ]6S`9 G`[:psNTD[yA .(m 笴Fd .o`= 4J+{9C6j5"F顐f(u2#zn6AZDxOs*0%n?m70"Bm% 48{x &9Fa6) S@pҗͨ\|a' ǻ<.\i&]c<.Q.~h|8Ad،:ЭrrqdgnjeQ0ZE& b-tIr5:n )ܗL6-b䷖tM:q\9?,DEa:-j (RR;~,Q+u߼W$紳ei Œc{Q K"4$*Vk4K6^ tt @f/v=^!)YAg6tЅ~2ȑpXy?@Ԕ]ZeB82>6] s2{tu;q%m-&wݔXK0hTYOvħ@1&3ڇW<shZCnfaqjdVQ}OۨeTr4'[iR¨Q6x%g%q`<&( CS 1zwR걅>[^G*K CdKo}\.[Ì3~P&Ywʑ,z15%m^}ők@$Y} Hxƞ EjʭG2҃03#-)3~!Ueh Ef&?Z2H,iUh͘?XM@y]M1B%ys {'I1O(M  AHS!A>SDI.4I|z:}FٻC918*LE * U洿GjX$H_ M٩^%X7!Ϊl'ξ;?[ X2@h9% l~6FW7۰Fb)Y[2Lj&X-/pg%Wl/3fyvh ex,YUMҳ.lLSF[7iH*FZԡ1cJ~Sjw-=4g (E/7JLT J,߈n>d=!AW>BN|9 b׵/^O6'eA2]6b+̡§ 69L#^6 ҵp^M'̈h@6ܝdJi<HZDRrԱ`!N׆ϑ՞!߶ b[g#S_f2i1}>wjRPl|"g2Y*#+lI)$R(aGm32rθf]]p~HU>y:֠'8VhI= SLFÀ5$Z<c)t0 ( 7M3T49\@9r;#H:4RD%ccP=BT v#lYJ$ZPT8M j >#D]"T@+@c[aɥGC0?IU!^1ͣF}i0H1(x ?BԸl*ʍ`Yر1M}ûG(G'o.'9eO7*r.}ݛH6ψ{޻j!A~L-XH c(7}D@pe<7 8ʭ;58b1S B 4t )qsNr,7A<[Ud>j^LAhq=^AYq&WrmXRTEG.]VA46=;\|v&DN3l51BNK>s|r!^W x( WסЕŸEJl IKmS`E\^>+d?e ΅APĆWEfi(d]ALk|iC0L"@{%/{sHĿ˿*- })сٖcp`G =Ҿ&vEmty(XՓoiBR#$19s~Y(5sыp 0E+#m%Ba!FRp{ħ ׿|23)&RJo ;K26Rn[jBY_7]?ɵ8S18\ XmO> ֠_DO?tSn@} !t{JڑPfߡtjVXw MxDb*<ȹB7=B㥁2=_X  zYGN^BRl#Wr cHT@]2|ՅnϻY)|^/D2!Prlb`賷#}/Xq ~)J)Az`djȲavn3 Hi Ettq*6 @u6| e<пyЮ![p(ކE`}=!Y1Y݈΁x$brFD52@juFtv`׌y{x?45cD7zjE[UC "KS- &E+DH7},#Dn.N$@b!x!l2VE"'>݃fD~4"(cI`-)*%ZUa D.rR͈FtNX4Iwwtt!i:4w9")9>o%Aʆ$!V' ^D"W%5蕈]y % lK{~ 7LT1 91IOh` yLAMEBt FMP4!fĀNpz'T Ϟ'`{vAZ A0 2=m{.Q`\(H S.IAArJpJ,R #$!)F` ?@#S~ QA2TX@h*:TљPĩV,q >V @|hE7Ǩl7"` ǒ^\+H78X0W cɌlh?D~̕Cf~{m(h~8=-ȏ3ւ;-h/DEV-Reo1}&\pZ} a-ģ' Arh|EccvQr|L yTxG/Ƞ(_7/#?f80 bQ=!F|F"=8xC9A`G֑Sp,ֱ>8Opо8bu4*W7}c#lo!7YyQ޸SuXZ7裎V+'Vqn%66F[q[q:_m6. JƳܥuM6kc#/ae7`LoT|5<kThI[#ʰhJ#ka{ƴ<3ӎX-rYSr r6vv+ΌdLX̐ĂVypYLʸcS0exCN (c2t%C.;Ii&~ cJ;6;+eT(m7cD;le$:n1љ.Ee!;."k)}!J@G:4.0b鰠i=,A u)jpgbo!W)5NE(i+ʸbn3VܤmՊphPf7x;"V**~# *~qRVqHWE9"NUHFGv 3;#Mv" I`=uTRTS,T`H Hs> OGJ25"{dL9S\.)řyRbGKlF X Q\Q@C (ؑFDA`P2_ u0 ֦O:b^P8( (x?a5?Q>eԌO)ܞܤ' #Vs #Dٯ#NP^Nirb Ɖ:8"M<#4nz6# % $MA2/$E QEBL3\ G_L$' 0~_<>@r&/MErN{9FldVBJ*GY iDsJ33KTU֒(Pb·dLP$%0`B@0i+$IdL$cv6t2H,1E΄&  `:JV IK A&٤G|&TyxfD!2D.NVN,##3t«F$%E#넗w!#aU0"ӓb]O{ұE'<[?"> @qr7"j"p%Oh#+ a$zW*Ja{Q(Dl Q*!1XP@FF  QTRH:*EƂ D" ɔ 8 b̻ dHlx 0 Ad /BR/ VqiP8(LYz@<`<K !S @{cpcJۦR@:C;JIS+Rn;/E2)*La8[ڦ4JSnV6vloR]1NqZ4BqJ~ fSʆ66Az>BS> N_rbíGp{p*{GSiȔCߗY=xk)Ԧ/Y1R̝6^[\!.xCx\x-eEwx|p%[RBg^X֡#աPyY:47:Y)gRyρ[ΡKs(ZRza9JRH)3pz&(nR6*_ 6U͹I$R qR6iR(YdDzrl26)F!BiʵR>3;~Q1 *OFcT4K镊 M@మ"*>7ƨR/8>U(WzV uf$*`~uy: yVypiGX CuIZ?JPWURȪuN,8ķ*sU:!TT!b3Fb+nU`Ve}pqRe{'p*|üe*n2yXq BxFkUf݆ يmx[Q+ "+ˆ8v`^!6ܥ^!k@W+崆 ,j]Xhz "b4x uaca4,48& 4XY__94r,zLP:hΚ^i;3zZ Zʼ a-,ôkr B[vka =dK#{h macH٬T[8o- 4GpT @.K Böqa`U.:Pu(c.< a΅]Β.P7.ŭK&k.ºPɀe _0yxC^Nd^j`䫗6#wv!?Z]^jх\ 8pٗ~ ◺gK5BZ(/%¤ *Cf! ~FdAn AVX>sGj{ PuD0lnmìʇl!fr_^bոC1OJ"b !*Vi1$ԋO*BQ3 T1}

J I,t$ f T} Ap~dp(PR' /u@ kahi0:>|PaD @a6t ]p/7pn@w$vW;8t7txxS`1oJO ސ=co0T oYGFbnjA8 i8 (M`p˧s؀5:S&&48`+ӤDhP@6 Ncf_2pT>Q kbp8}!?rzLNM(*rV/Ԙ g4 0.ϱ :90zC V:N*4" :Sbu\"X`3SrAu(18n0 o x۩v ҫy]@t Nܝ .mP^l>]L0B.pol xS!-XzA=|;+ <ŅMQX`R+c]m+?AVyCyRMzD`.M,/S!Mӣ`%'Jnl]㯠\>^nVe+vPbu(%dƶ &֭؄_xy"SƇIA7+$5)g90)!GXL \v2CN\0L N&TRP0VK̴ ijlo2XI' ()`Mr ,Q6T)PJ%(НQD kB~(=[D<ť[(VVBA0O(!$Gѫů֡}m =SrinXDAnA+pXZ -7“[ UMk<( ]!;AZ` $;h@h9}VC A9&?~)8A \$NԃͮS PX=Sc;AWx8 B K>/@&Mqr @) A[h_J-YSK|SHpsdEU0JY m?N $H5 ۞|MgB(Oǟ``>$dLW YQ!+5sM( {Ӯ@,aj*F(vAV2#wO VUw%"EjnDe6|9aZ Gr!3ea4.E5O@f<9&إvelu <_ 6N,z?p H4_(@$d~sg=29yX@/915WBGJGfn_Ձzx||79bo m+p j[)  Y 8Om@3s6~Qc `:?uЗ4Rr>I-3ph- Q*Xkdu[|0@><(h33 Ӯ-B5{GpYVߋ#!^f/( ohy^00*w ;@:jy2Vet7 йf6Dx lhK 36 !IlTc&-Yv}!] ,cSM -N\C;hZ 3zH *DqLNg0N/@Dtқm xh ~;z=N/vImAJI LNu/+L` C6RWeJ G W|~a#pOX=LLp-@i BNx-$g$e% ( ФB~@"OUgZw\еGξ,1s(ACZNٙ7 c+XKCrw=8`E1|V?^QR=+%LHHT2 GN͸9Y#T-0+a(np" BXJ਴9PT"Qn}z8)nM>K|v>l1&, RjBy6@1I GUalXԺQ0FYA"jX;Tyo^D(8F8 -o/^(@4fxwF5VA#Wمx#[mmhiHe,ܞ=,Wp^T*C3>9g97}Ä_(7wܑ,nUDq7`NS-CPuQ_p¯N p%/Fa|l2vJo2( gؼy1l: ӱ]ۭ"kY~Н#u|e2 WUح(}( ÂPaAkP\=Q!l ,,.VCf^olxdI8M-t"/3M@/6uKȓ!8lLc$O p6ߣ]3% 7E ͠U➛Pl' W{eޏXGK>UNdG/"F|O򟥀wxC-YӫToBu·7QyDt+s,O~'BwpѹEk4DId@?r&g?!="_B+V>4*2I`?BEP"3]vugM bLWR;*qڙM؛?@~yC2O5>w￧{,ҷfȔ!cP&$9a@)aȟa_9!~aƩ(&~C+z;T"vQؐ]A$43W/J~K,;EF*|TCqqmP܇?F.4f^&c>k~]K+CTS0,q(`[2kXAwrqt{IiߔIUj~ψ I~e@}ƽi Tj=1/eB&[5 Ym˝ƒoHO(j}IAG+:~4pTsW|%,ZArN`_"H3>D9(.U, k%z4}R>@1@d}{}꒫f nTc0SA X~RDhUTD,O2էor=]ͩ s7ɪPEP8RnhMא.}IK-r'do)O>dbn vG\ C,g(}仳y qrGh.߶b翢M8v3AzcqD$N҈oF7ߢhp~. ^x͗Jw3|zt^Ėb>eϩ]#:G,7k я*Kn(}<lS{ LFVIY)Ca\}& oHOƘF.{| 1'wẁ?BsqT @אo/uiIX^=pl7)Æoփ_UQS?eaFWjz?$N'e9 "B࣓$GYJXwP\o d3+->4ĶQsҌsi8\ S!10ѾهR;5Q)}{MI[Q_±ֻ%ޛؓ3~SK'/'/S B`]Lx#"lݎZz{!*z+ :O8Xf=n T S}LB~н:M;Vk(BU(:N!˧pHDgo, QlԻQ$aΦ=F?s+wpNޥ H&5*_0 m6L>q{2fM;ǯ? t{SN&d;,zW%7zMCRvϝ.oU8}h.Wۿ^nJ '[ fݝ8UWb[:+ QG] /{;tpq]jC˪ %Kxە>0~Y AMsR{@}уz;*kǵ͑Pڋyf7Ծv8a81UԎ%Y_dT5_9zγ®ץakx9̥>; !$AqQ͌Fb-@sΈ9B4oޤpsFg`}TA&e&vc">AAiWzr7>C^KnTlH^x4]>sk}up^FDy1Z{0^DuK EPk\*Vsf \ee ]f~!O)di55".ZAԾ^:tY'{ֶI{V Ȓz]TĄ1D!|uX}Z@+X{RjumRzoЉ7XbU/^%.DyKmu,Ng\:D+,SR3inҤ>hm*:#ߌx91.sJ24]u=#8ѧH/جr9>5Mu@%400L?1er)xXGOz2gM./f붕c_(2P2 O~ԯЮlK"J-dN<` e$[HGb a z8=o.Bz,1I֦`t>TDrEP&xcӬD?1 n\ 7G)NǥMړ0#05C\ :!r$U+{]"9s)ׅt\My2~yOg o. An-Ob[i*8W8w7$GEͯܠE#t4%DwL4[ q]a#C?}V8yKT,Q$' \l̍0UuCD8Xf%H>s(uʼnG6a(8#0˞ \|*L\.n6R>8xA/~g8^E:.ͭ)*_8։a1\{]Wȋj2['Ù3OkE3~7hehq~qqJr w*h- +TyE~+nά5W|OU1[Y9zsi}5CqT%^:qY{> V1.4:%]Xȅw57(ZWGʿ!F97E\Ko.0^߇qP3Uń tFfzX9XJyy;g8e* ?ƊTp'ZL(J Tw\,Px:%Y 3J! endstream endobj 19 0 obj <>stream 1!b>7LQ nS`ZUG7 OS;p4G)aW'x/){9Ω=q⁷voƑ3p%!զo%I)By;b뺀c5wH Y>~Θ~m^gTIKH'P32x{~m he9}hM/~z[Yq`y߆"gAo-عo6Ɓx^4VTaS 0mٽ{uWՍa@cCIpi=G Yj^Y8B[ W񽗴jb{!nܜ%Fޓk#_"}OӜٴSXh)DYsRYƴw-R˴~,s 0kJDїGz`@-j'xy)-}roR }\}+ĽRgroT7JNq6ɽ S)? r`xLkro poֈ"@>֘PmE)NqF镧lr@-.Ȫ-7_t6ݳ r1qs6ɀEWKr m˶rnh$Ms؊y70*p;ܽ_͕;!yMF~ wrCzJ^Cȝmm.Y";eP1u)Z*5 q n`?y{n5RlZGB㿖g.F{;*睎4"o*no1ov?Pœ$vnw.܎wʓx>O8A5bZY`7տ:mKjayK])* lQ]U׶l:OL\_W=km`Qz\smI<5z̢~"VՊ]2cXa.ܑh={5jЬz֏ApUdaSm8TvJg7tݳ' :By-U2ڬbpT>w6muϮF4iWe46lL3#1?$gP1ǐIJ/ oCKWGfO4{3CfٲYPG")Hف".z-͇,vk([I%iN,dr@vv TKlj>{epAVbfvC"d "s<cj{%oMN.'-lA)HUa(7x !j=-d+9Lg'#R|=Ou>rgH/J;%v,Zo G^D5o6yy7YZCI)uAI}Yv0^"oևfiV;?6xJ$f,5Mk;P8acUpP<5ʿ25 $vL.h/׺7Pag5ǚ&ˍPO(~@%kg}i#7F8V$o*k8¸Y dpW`.n^0^n~53jּzTV%\=Iݺ[VH{8rfmО]5-gYuW4PU5CFU¨8qN`VQB녜|6nF霺+w<N*WR .2υR+gi~,x-ҤcFj&EU=j[@c#6DQ粷"[9ء\:v`1lފ0C<[M<1ppz*sz଑MUJwS9MeMa+biW6kQ$#KZ!z,>gbӂXL(Na`.ץ7̵ܪSv2t^.yD1HW+I{6uZҹdytB$L·1@IǕg td)$tW@oa0mZC wЀSC}φKS }֏r/(2C:ĐA&K/|v7#spT g{+;_#+$HmzVgY8NF,Q3CN{%Oxz>sY׺I=ل:s 8ϻ R|x4Wns:D< OR^Lj·Л4%*i/XwV'@%#]Pteb*ūk6ՠ2wZb4aG;"PH旆uhg{ͩ܁җ^:yh.4 *h<Ղ[.ޙRټVq:a K g''<ׅp ,sv ,U㗞YpgxXGI/Jw`<ٰxcL5Ҟ"f \o^w+垈Hj|JסyMSgi`!F>:iy9oFjKZ[Yek d8Y0blÄ3.#R{RɌAdB7Wd:%eo )ج 7٢a\PX6 267B+nD|yICk]~>M#=Q>:`6=2~r$FqNuS44`ӂRiC:a`‹˄f)939 =tnWh:i ud)/xdB*GS=P<)Suy )6=9_O yA{^G(r)RwC 铵χA~v;,us=1OPJ`Y!Ng\Cl瀚г ̥+Z[&nPT =CdncbC%ah^B5;:^TAv8 =eF#7l%aUsPxɢW[\!4̈́&P.*xè] -5*n})n%DԠ}3(?G942d &"ه>I7.KZ BT6\;+~RHiRgc;G0WVJiޖTdKLLKShwon'0nЇLgnT}9.j8Txw+[k 6NΪTcT)R°x69z!~5rlP?鏿m~Px8izn^U٧V~py ,+RsK޺jR/NM:EJMpAF5`X%>UEe>VXW(Ue?Y*DT$^[ .ji$Rc(=Uܑ堘:>VѫUkEy78J-X'kf b֛JXi}JW תSQ>[^עR2}AbrxUE/((JjZܮvE ^HHc i,lՁ|-UI׶WD խDjO0`bW(aM>9tjrA-{xWqau@sC6]B1F¢U&keRH[/ Ⱦ1S= XZ`Y鬛aPu 6<"hF4׆yU%^hecۤ$mz=-bmZ?4;#vDjO#ւ/>_k]{H.Mi. ցF˶wۿ/n[?v:[w"[nuYY\, . An;ܕ2q+۠텍xI/3@\-Ύ ;Tvxfffy>(sG:nݟ3/bNWT2N*^2B>o,E25$Fc/%i qPB˺''4FF5xQeKxEb5yEĕxb3cԼrP{܌a>K]\]^P{ kuqbuV0frBZfVU|vT~Sz_`7g ~U^{A@ ׼WŇ*oOd_[=j Ʊ` Mi He_-0/~fu4X 7rFZj#zl$\ NP<1A9 >hICɟlXj]%8:}27 =?HF zJ]ưVx@pZGxYI[ؓ`Y ܾ,JLFRv 85ɊA tTC$GܑR\0β9JFݩ>Z}fbrO6CҮ/j0O Hp2RI1i\<ؐcBA*i֠C̙#]-Q#4Hq} A-0 dA,3QT!9SPzន/  MZΒn S`=h8?^"`͊ Pg C@#f X!@iq ɀS5M'+V,5oOLU !ȉhLՊ'f3zfkS04c:00O520Xz"bKNfk4P wj@h`KunW V_{׺[wFh &~aȁ50 m [ }Bb)`bZ8QNP*!k` * i8;}">C5QbŢ(CU߬2=_9UC_M7ם\]d[?f>Ep,^6Ԝ|BOM/r`$1 I01[%{Q %2] c B`תT`o;.J !I)FfG%I~Jxb̨ | +Dr6'f'XB$lX2OR7Ajx-Ȣ'8]+\%H ^c_֢O֜_q`3, ݲ-5霉CD5l(0X` g)A9uC`6Y\ MYIJGW>@Z0'&qukY"v9y,l' "<d' " M/{jr/x7b5X5[jK/L@ɛaQ _^ej) [3pΡ/,X<>7C.njYT24# uG8g g־I`0Ôg2UAp`}P(FĄJQ0l~\oZ)y~{e*ՅeY(އZc\-Br5ߥ,we)a|vՉ|ytZWۦ4h40a$o1CRL6M3IQvEۏlfC̀@ij[4eӹP煵=`)Tƌ!k=\͟ &5wTk9&M¸b&9zvg͟"+qt X3 `ּY:p G<԰Gߡ5/pfw<'#5 Ji:՚|ɚKÇeDL}4:{z4Uh>mmGUͻ7iVfp}Q |fkd4EeZ "1u_&xfƖ?G>9}4+rw΢مS Q]EtO. iFq%"@y&EsY?<^x33#_Nv:3gΐ0ڪ3nef2 DO]5О;Rfka/w-\dNin,I=oEO>WTrqzod^)7k.)牚\yfnoV8~b# }{PTa9ةsw-2.33)⠖D7K{/r-al5깠' A)B0PC"Vײ$Nl}EkH hީwd}ɛ1cK gIima' .&Kե+ȀӈDnK(twԢl1pLn?a2C6%R ª$^S.;!Fʸ6V c5[˼TWkWk k{&j!;%̬`9G!vBIc[3: dsʜ{Yw +]g{u~ ^إ}j6pp_50 >\w㙱hlK%? t]Hي}JҰ'@(N_5 ;贫LuJ mO׸s^"䮱TP5@Ǟ%~lofӚH A{X2KB5l$[IhP>+ٻHȀd| %[u>9/$$i0&P lXQz3+eJLfSnd5VhQ6DeôEMQv@91$^I U< dvN5tKsRSŚL%{dÇ@(/aX5@&C`+G6(B,wYG0hgx1;PMmax} W?)a"L4Z cg* sm~4AQ;rՃH' %6cb{Ǧkc+$=X< ;_b -$xBHO(\q"NV3aŗK;X;&c *@Bq;aəigX[L}TPQ-&nAIlۆ7l0k^e$ͮ*a#>_`V7gH5Z<%Cwzḵ,_îFwbӰ|T봍x {^נavaAѿ2A$aG:GԳcJGr`;!T;MmqKa$k\BH b4SDnra?YV uN}ožbِ߲8F1hWh>/)@W b(j8QdșuӶqŗ yXmǻ¸ld!%WA6 Oɏ9˨55eJ΢/=th >;w(Sx$dxT>܊g&dM|` x@q289nb=~z<ߍr z R^A>z_BL=vڪ.$ยn3K _ƻT U G#}))[/ҳfjA;wN@ƭz3Rֻ7r+p ?t ]椱ye]]/d~/3>wV<^/ZE F8^?0Yx~B):?Dj[$oo"=tg3O^a{L(62jgh[Eksf/OaW|H$oh|z$+^I`.aCS%Vi`K\Kٍ7`7ПnrKD~/:Lc!9el}Hh}IP#;|w@ޯx}&SjJϥ:/ACqdk8oF J?<8\E?wy A.99 4'Zgc"$$\?Hh9?\E&kaS<(?'Wd>vޑSj)K)!AѴR)E3!:U0斸zL*1ڇ ѱZ' j2Ûx^2dGF.$t"><1װ?GYIgw݅];g!# #X:5.'U3&k: m-}VqREJ5jJi+ ="?;m12ses#_?b&3um9[k^ kN{pmb9_nlcZA7Э1<, 2 ߙTEA,{ j?Yl{mؼ]Di}zǛp=y2[嘤V4"=PcQ2Vz?jx7zij_DٷIef;ZgpGdO_ɮ-wxn{m0hگkV HP27$ $m@Cxg|:Z-C&5T%-!<51u.hƺqY+`XU8~q^;SgNbFn(ώ5J8/g7q7o4 p~ܿk5zlNQy=sxgbKFީ|̝u9 cw- <|qWPޯPs:*)opL푥`pd28LΦ;epG:qn3ڶmg4Z, gckB浜Q˂i{j"N\M$A{ı}t xCo(+z Ics3}e'+phU0$}iml#A԰xlYI20;S(HɚNZL:OE"@FF.$WD~OmihȻf׎-I <\i:soD|!KȐoFx>u_[Csql<3RþGSx-)XpJ\?Ý`DINЂG0J?ӕ>y=Q7o5N \8o%veYp\K!G-ՈEEgO.Ch_}<}}nZԳG<H1*#$K1syƯL6n4 ןFö69{eb:sS3XOHP$&W9.i.V.?oc3u~4B:e]"I7}oLw;ynJ{wv#Z.TxS#$o' ٜ5$ -,xMĘij3:25X?ʽgj~M9]8;iS4{i$_g :s F<IL[QH %dmPKGgO"ǯtX b8ѰN5G>0gc5zk}nm~ RƏ3}xvS(+X;Ygќ%ќhO? %>^WS$m)T*_~Fhw&( >Oίb&P Fl0=wQEvf <ܗ;6\^UJA }⇠R!*hqp|!3&?aJ5*nHx3mv7kZ8sq>Fn3My"L=1M$ͿҦay[S.|AH@C$֫sr4NuE=Ϳ߳XW5|njFC}9q?myohj1zfΌ|6mkȥޠҦ E}&ѯW7x!Y7H!"|:C-,+,餷l~bi 6]Q~"5lj18 bTG^h3X ^0v Ǽ.Sg&th?JX7٩L$6; uvݧpR|Pc_P0 E>-"QOe"7h١LwOp<"G ~ Q>a7̻kR7}q>`R6PJh# x# xC*$Sh }W7;{}bȲCt.گ/(.JY@8e&ϰ{~_>`=#l*? Eh2F n;Y&nl6h' Y>uЭe̴:߸#GjF:O׉,%94gLLwFu;;!Y_:kɤL `=s6e+=+&c/5mr_ۜ:oހi?%ќj5T)7H!~I&mŽ.Al 5x$)hIF~7m$9,vS'KOEB#8:6ޝ3k:!G>lur=ỿ㭳kU@ T){'?z~m# b@J~]X͂m:ڈr_C풢! %ӵ@{?cݨX8*;'!f>mF1;a\V3{h2wdZ1OmCcc+ۍu20M:Fe3/Dza֊ٲ ]#FD}i'mr}ݕkcض2^ZQ8ݷ~UګYٙ>2항gWC}L^[;$RGBob:I?\)pL 0:-^D(.ytWNQ. B~Pzܳ"lF?[q:5/]=`=Sl Ľo K߭&Ns6_ӧmzN15|l=nL+W&iŮh4X0<9,Gth[+^4-M_ܣxWpj:4 ,VFԑ7\zP,uI)x V(AzIG? l|u0k{2wcLޙӥ7nfoSuu3&Z-&RC8 OR;^:;+ׁXMD=ﭾt8oy>7 G(9:ṺOmWcs[,MsXWIu|Y:lfM87} H[ QCu`m)X?TC Ըׅ&|Ob݃xשT C8i, u6FP݀jǰ|H,@ o)ľ$ %c꽁w4N 9F$(SOg-%̯MUb3(gtzż;h l#qJ;^:;+s:a=v? wu~(Iz#˿MiXQ?(Ͼჳ} (Rᆸ>PQ5zj> L];ʔp h$ Ƽ0@6/cG6F~4DþgM7[,c\ "-4٬HFq A/k,֐L F_$wxzO_4P>*7qo+YBShk9m}"?H1U~%ͯYīgf3FM!ī<jТݝdٵJ ma2RԳ}ywPbM$g= FN:[aC|xl"MD,mOD*A[)kEB|2Hq|p#-5h'e5yj^fI::spVgj6V(ra}=ɵ>cO)o =DV*{3|iLޙlj#?:tum{V5xl.]{m|mg6oi̺ -3W6 yllM=$kazclYLuƼojP!E>»sx׃|#_)4{k[]DDۥ[*vG"~? UrNx%ϯgⱖR5K\I4w*4=AF`7@ā-y"JK@ǐ#GNBEā?hlDwFЩ8~qqfT%+)ٵ4wyt h'm S% mRܳmܺ ZRK^;)0>Q|C~*Fц=M[}ݷk8~ot9h/a~m@Mژ8[#qk6ܿ2`@x'8^7Ԩusa:s MD<3|ds-m9qG4iZ082L\VCm| Z;kvkgf>:fk6>qu2se4dž`j8LZ]uqP$2pduv} Žå:]~{yHRM%NW9߹g;t}HUQ:=F :J]í8r?iSІ:n6o%N$P ND*-\[J5|K&SDrԳw뼎Wt!ĨwA?|0MMA@)`Ģ-ZhU?;g0ԡ6~oa^!8p^;y |iik[ylݝ J~#l= `h149Lo :qLߙ\3Vؙimw|+Y/jaʴbKce V{}:ѻW5;p2t¸gԾ Í~޷s>y{M_\(}z~){-Lm+[^a Ԙă@ ƺªòCЮupUST)gL_9qrn+0xI?zO! 3ui쓈}͟[cqἐ#ɽ,6!ݿTNSo}5CkJOsm?fmك=vbߝx5B{w꽓:mHujkV+׉$w$98cuqwNͦSenZFo,{mrL0dr{ bbW/v:8y +y3ǻfgyƸm FM v>3-aSFtdlW;gδ1q?{ea\]"!Lu93ü`ʰa0jb`IkwU]66xl.)ijE $ ~bGs#LAO$:\3Wlkd_?x5a9؟N n7lި:=3sD7vc7kj=gaȴ٫ژ'LgyO[if;icdem+إ6OSVԡ5yi_ͬ}f;ka,_cXshD<gwՊG(ky@ {L٬󅳍&&M"Y L{ k0:\9f>vo;wxl>swkkZ8 \<źļdȰ54 Y>K)t|24 mquWZyxmq]=4,MFPM[36pӲ0lZ,^]ˡ^`D}`W3/F]0NEl48 Ꝭ&qprrPTV;U9=$!]|N^]Ć64vc 5p~e 2ax 7|p.gx <܇;5Iq T A\ʸ8i0 ϶#kx_VoJtXƎzG,/ RACQ+%SJ!:\!2. {Z>H%P[|"il1DBydSLĄEH$H5 W11 |V1 ʃrP=2?RA!ãy$"(m|:Ӆaq +%AQN2FÉbvG<+Lq„­k  dq'đxWDNJ #:`'}TRHII1_#; {C#fLK/CH5 v*Jrz:T11E-[&*LG#<$;4/{PZuDAZ9RI`0nP~fr 8X*!n᪊oEx5hE&\. p#CR+ø/=tɊ}|)ü߱Ԑ 1|@2OL/O\Y/ G5L&a>&@X xc釄w5uRP17 M#bZ(p\q% 3TACbBEbJœ*q@k_zi8a/]V*KY*W ń@LCHńBF3 >" [ vk,H/#MZ_Xx}|`2"e^l=w261$t٪:%.E5J#_ dVЇXPgN\]l(٪f,gFl5 餯#%NM$IO lH|@*M߈$- Oȩ'PK%+)&QB*F+øEV8n ȅIIyٍ v6"wc.^=bl|EXbX. ljHj^ye0"f %94 K/qMT+|5YXYTR^*6"&$ |,[j?h1khA,SE9QTV;Uֳ`bF @ (vR厄rCVI%@0SK0D$I!gt!7^.bmc-a,3UdY|":He^#JLT"!dߓLxE?#O:+j'ЍxT81Ih (qVu jEueTa3'ke0RLC E3OLasCf:ļZОF3B>5%x5%1,^2i<"v\ Tb{21!bM(RQ #YW5\"*.BjhvQ9*R J$&({lj2: w:9UXi6#tAQydjztz^]Ȃv.d;UByB \(()1l-kKKʏ_)J#'d`!DZ}gXXf]^ 5V" 2ǫ`<f6"1$4z-BzE"j%20I I?&;${CJɾ'=& &YqVYMX2 Ȓd}Y4SVT\}V $Bdɿ$}JD-!2 x_ {PV%dB5rZi҉%CM1@Q<S,gPAL1mQ2;#AvIʈ:nkaQ!R]RjR_Ȃv)D aXXF:osCK`B/*rhTm5mxo%ŀ/Zӭ+zۓ77ܓw.D-3L'|G-4^.A1A9$UMrI3 b䓯8H2)MhD!QnQG&$LŸL ==U~3\R;ZreF6*[BCv0DeRv RA2-3$`JOxj=,:yyR_⸂ņD<14U/7fh+%xe K/"&bZ"}pO2f"Kh&#!F 3SBܸw+~w$vqG2!:cO\|1(nEךByˆFJ %ŔVΚLpUX@4 [_e &9aְ؃IxǔTؾ#TWk4#]]L{UCй$5k mX˗:rT)oP.7mz:m<1 YȼJZ{!Ko7<VTg ' )q*URuU9bI5 Y*3*2~\_- ޱԐUft{J,$-cOTiG\l1De9AHǤ D͂r~d=΄EM2/>Ѐ*t[Gj "HDUЍP؉\|",?2 md,[C'S ؘ+)hbZ0f'`)!aqq9a]AYtLpyC!$dI~HwRV>u~B״nQdlEzAqg2*kXUGW2޳„fdr;Fbd!lQe "F /G[ĩR Wa.^jD.OX|`wB) D m֒Miхsq9d!=l1z1*ذ b:-Ɋ29 ?Al--fi/3Z/P8]޻!Cmψo@ ,%ءG?;_h `>bS2fzEqR}+ CVa1-6fJ򢶽ؘ  tY4젷\n3rYr0@sٔ J ‹BÂoEhl [;c &or*{IImSi+,>Ep@la K*բ.R@XWBN\ra)Rc Ў)cN#@ 5ݖ!u]"Zt;j)<|nQa.4hm1P{rvcr㓱QR]RJ;QSzXeZ%0we1pZJ4gyˉζ6{ S˕U®o"/ȖQT|'8NnYvx3ml̅Hܝ7\!tbeX%l41MB /W.4.u_\K+]#.6hƬXy# CR jun%ݐEQewQ!XB$; A+#V$v˿ZޕeA]vzRRO儽eYc U-O#,w&g0峓 !av gEY\ۀsHae d}g`AhAE[$*n 4 DjIY6d2Ю̐B+- b(0Y+ {䅟> ovgӒؘ{ٖ}EF f@Y'^R"ApQ9|ՠTPeT*)c`?dXEKJkv^zrzWN.)S:9KJGdm  T$Tہ iMƿr mɋne=eMSQż 0Q@ e\_iQSVt,=ÕR-'s/LWrï5ͨ>ާb]L`TJl||dX7,:()x R6IHf4`|)XAgF$Mf́5JycnϔcBc ĆmwA/3h-6#6᪑] K^=x@Jll$kv!Z5xe! jfEܤJH/ziϾza5e5jɤP\2p#K/F/Ig (ݠOW;NSb 1 4dX7Wk5DP"I{Z%ERZxG:m- uSNS)-)ZZNiyɈ{Ÿ)yq-q+/h b%<ɔHVD$JNŗ=keMǖrRZk58+>] ,K) [Frc[O]ZTi UJ TD !$r%%r%'VQ:ēSƚ?XGg UJC.i4&ioE##P-sX"3~ \TNg ]朵)_`rY)1V0I[X9[X9kZsq!v+Y=*{%!3κʆd9̻4͏]-}#es(ƣ?HH3zd 9q[1 A2,m%F-E]$ǽJbAѹ@P{;1r_pHVrӈ/m)8LD~~\\4LS ª~tJ '@xk(:`8f 3)284y 4i7${m$I\-(QFQXT^ٵ6@cI7UG6,#b#LA9,nD/`' $wt?&,{2ycr&`U~C^ v\M&8r2^37K̝iijƻm4~hs[)q"PU vMJ=P F6#O T)=mg#Z%aI/}zZI DF? ?xP^gk) !ظL4ZшT(Si *)kٜhNd~pt`Maez` Z2i)]r(C~D:|!ɨ`dD% ^D ^H T8#Rf6.Xh*Qh(qhK.rSi3x'yaR]IH ʫ=E{InI;iS*Ob+m~.]< E%(؆"5> S5j̞ٜG@(;?|8~qHQz+H&ݨ0>CwFȕ2qe]f d:`8]2LV@B/@ |>&L;بpCe߼D J\cZZKI1}O j8{WP% {.Zh'u L(z$>_C&͋PP&ja$֫TSZu@%T~B5LHiH>$6!ԩ"KT%= LH La{h V8zRi9qqgJ9;pTןJ_AF`DWN+Q}kjLeh(gĮ;໺賯np^P2$=cFټO8, s9M_oCEg0x4@M4yh5_״2Z_JR`O0CZ9/xA9O@ -0餷XC-$]**-@)k%C~vT)㲎SPJUM`{ Ѧ:m r d(Bb>n^"m T% DB[SLI Ch );AIIy/]18ܸ:+׏āF#XO ODg ;>.%kÍh:-ƆPo3hIlxKHF<C>w1!Ƚ }${xd~$֑c!żίt)$@J \4RSfx˔Ih3mv~6 DJFe#S~`:ؓ8| x G:x'eZmBNqosc{T EC\ A$<5fb2tgٝB;Ṇ:h4ij.SZX%ظB~B~A{C]tPt =|1x$)  5)uUəNyDWO%]! A (H(a5o8+餥NmܧSo~`UIDw$T$xDo;[isЮTrH߀d>6m ۼQ=SCL R/0بNN:Z G%`Ģ-g;@ $Πdtps8FRo 2qePb5`%Z, OC[IoVNmJuO"ZKףJ74 "l826m+uFߴ2o0ݗm?{UDcY>g86AL i;g1 BXռ`\+5c:qXkH 5 JXj6Y%RB%^s03*:)5<Z1 IL3[~ȍąGCKb F%k2!S u"\6p2N7nQ{h&oʨuT LBNml3LZ k^3~dYLp{rd?2앍bwfJ,*/-* VLQ(ts K F+r4 P^6ͿA ɹ#d/&f %O0]$B} О*]SDc?rLoؙ::ɲk[p/|T$m Fuvwye2-uc9^a΁!Tca⑼]iaB\f@ FNpۂ:;d)bЧ H+ I T8"= `B[Ah[Aoy1HXq9;}BT}"'hYU؛B@ F4\ariyC; !Ji}!ɨ6êȨPjЙW: CVK/uU볨7s Z|km#au Ǭ 1Ok۾4Xk\ l|#Jq0IbrU*,#f6(e*>Hq E݆6;GV 0i>GrO; ESlbR4, vdZF-B"vW/-~ER V)$6@EU0(ZI(((/~?zuǝZƹll%D`4<aj7 lHTڗ.$-y8@5\%JHhWs|/.\C-us<8 oȲ6w Y('" Q1vU6e6% C,. bPj̈'Lv , ?2/u*Z S8.ݬqA/, \<@~2O",EQљZ]"v PBe[1ML.ܖifpu sؓk N)hi6p, \D.>u6FmסŞT:*i4`_dGUZ <@ CZ54풂 d؃I$7ZvT(0k š־bc6A ȍKmvi'SCBPPf_C8W1STqN %DFY 3gke蚿YkW geiM ]1sfb1tg_49N4 Cxo11/q!NDxw0ạ՘D(B#8Wue I,֑Ol/#^Jf*@9o: sZS2(`VNP#C,XT^dNdx2V6#֧[gSŜd|KW %.2U E^ 0pvf{V,PO8EX| $]V@ȍ 7.@v] ֱ *g6)PbEXJ4N hHpN4C}~2OXwԳV`b}@"MQZՔ84׊,< (ȠR jϹ0|,+;S(G؍eBsڝB=&7lk\Bve&4 k0f`UT)3q!.SXcxvFb!TpWPe,e"B"dBdQ17%4n^T%oę0)- [Ne,Z12+<4V=2Il6\D_Al(G+H F~=(So6xNТ vd, Eeؒhؓj C p)-\-<\paEK `&ͮxٖA6Cյkk z(FlՐKlņdXd`XRgڴ3zgY3<1ebgZlt%/`PRS2vEK&]X FVm+bW5,kCA?5:93ზ'%=HNx ~6.@UkLBaUX20[sRIr R?rP3mk&L*5- "G:mqB}1w`EAINe;V ?K7`ܘ;` \V@= DOwF 2o⁙VhnMr2Ё#!* )cC|1-S37+9Vg7|d!@ p"KK>ҥ6F@؀/z1 5'*p:|C&!nJˀ ,MT+erLll 񱥛UXS6ha7jdj\VvdjF ;"A0 vʉ%ykˋO -Hm y :K|E򆗲E')@ ɆcY.34n%pmH'suDTб '8%4v$j O8Po mee B+ƅ\S1(@)a~}seCѫ]~|ŀ4PM>Q(u P--f辸V!?}%3 o-a5ظK1b۱^H8܂YZ\`7tQ#t fɜ~"pO?%Á8L9r7`-Y_|8YG(k_>{ٕ@ En;ȋ/dᆧlE$3ۿĠ? :c1:P `!S}c^-\4^e[Rc0Eal㫏hNDE1_tō3Cո)cWcEq!ux Ia8yfNic^^x/P/gRV*V9( PFe C2ι:tq oc_0D VI?*Q4m l a\1~k 2!/QWLxD[dۘ"Y lMj t m,H4C'PY0#.#b,)U}8 Dxu:`4BlL3oh5噂(<V6KLyvBadHt$`ڸHk#ͣ8/%XDP/#l@1X SB xD8 ;.@c~PjxQY<.AGHJQ9AF#ï <}܀x$z32 8,Jw+=„nbā *] >剶l|KWE\Kx-ڵ@8`)@05 e-"M6t0Q 8@jdIi#pa@EŹfbpS. )xX'&?pF;Qj(<6%2 l_,#H_@1)PBć`L$22~ $Xq>p=̭(xVfHiXn8wI#cH4`lQo`XVYC  xfPaD ;GjvE 2DK !̍A23C" 7S: O)@Xv tHI# cOjaH4ά猀svKjxt8$uķx#"`/P5AÍ< ěbRLOq ~tP eadä"DM@'SBI2]:RpUeׇ/%X2dmmlW Tc~@jŬ@]x܆RdTJx"^k q?DoTq$~DC8=Wiz=0*u eB%SM%]2x"C0GNBB::l bXQ (gic._Uvp+pLHHh5qyʈO߮ p+\N~3#NsčCGƆ_>6C|+䛖Xbn\ >,>X"aI#P(OqXHN D<0-mPrmTd؉2HKz1%is%#:aPKK*_P:WDJ*(7Af858l9H5A\8RK%7n +4>|hv{1aDۣwG,,qhrudgDdipX0vP?dq7A|AMM3?<Á89"4S{ 06A+'wuSIk(wR/."ZLA~P)-J !Ge;4%M96F5!&u,=[DHU;@AwP#īv8*!$LjK^9̕'ƈ]vH/ ⲲRJ>Va =H` \4, {KYoxk\5Ɣ4DiCS؃cKw05mbH?7?*Gp tIN×b9.jW$ ,hb0L1R#F-+ {Zz{(+d5G/:n`@qhē41"Ƨ`3o Vץ!o40?r`Y J胵 fHLo!;X>K!$Eb'"JZ8DnV&u8p+kR$ߖ=%vLSooɿ$F*CFhJGLiô\HD&&#EMI#K'tR,O]. y#Kc'e*Ρ(Gp#Ãx62\&Ns13 :j#4ppSBaaCgewՄx KKD#Gb 90 q b dF&^40:R\.$X WOס2`KpactWa<x,ܘKX[ 6(RL'^24,@Z=p\s 0DiY؃)&aL45]VY@G$TZtc\CR3p 8\ps100;p!~wb8 >CXCx`VJȼ*I6!p 7F DfY(DS #&dBZ4'ܠÒ0H%)eGTMQ[ic&X7%Jbȁa5 ~`vY1%k͑e{@dE'xE+蜍H)WXv{{}QBaÒp 0S=4-`Z*+[2)n!SpYNb;r;\VXaee1rzР. 8ƪ@lQ9QpB0v5V5F49A\Y A42z  ǺaYK8Ci&g(]DUUP#`[3n@2J+mz*j0g3 gSEhrCz*<7䟝e$B}'qH9ĢtfD""aC0bўP #MIVD%9c$d~GMZͺJF%mU@% F%ݢ_@:t?yhXV1$aDv)Uo490V1ݎ.ispJYyJ \,z)/gsr\y'Q0&r#IEZhK*U Lx˴\Ίq )h+8xsp^ɳF }P9s>ӧ;zopޭӍA{_?U؛@JyA7o {uK"@ _#Д'LDN= 6ƭ=w=+_Y['Tbw -FH"'RB 4`\]VQu+m)2#بT u>aݯk{ |RGP7G'av?c9.2njՍn%gFcYwt=HRK$}ޟ;1iez\- A Eㅋ@zveזRE@C\G^阬i'&l-^tz9u#4T^Fàb״ YYSp FxJ0v'OC4~i5gi%ϯ_;a)cМT<7OM R<&sp5My em4l'az?䟧2h* DR C8!u#53vf `-}גQ(ʹI.oI A0k,)T~ġ꽗8_DN#ez;$uCWX X8z$̮o V@J"( ߟ+enf6H]c 23xj_\C8Wj'Tr97hCP H[M&u;ͷ,v}1VkspgbN_H1WmOhS' w[pFm/ }s^V2 ZRZV3/܀(*9{} `0lt`V?e*'9ɉ!,uX>ОJ5J=j#Z<:,5@j$} DL̚D>ý)Vү'iд4~jZ&SoJ⩭Ps!4nT:8A$nM#*nۉ z ȾF~7%ko$Dsm/?VI?1=uS% mJ7PJh+a=y݊dE.hgOYPa"M`CwrF  p[uugpYăR%~LnLN `_zC{WqzNWΨ;$1qdZ~b֊&vM-I@ -8}o󭳋"m.yl+g_XE)g`%E,1`PFYI [ģS20'4` \<xtVCg.$~kS*2mr?x =[4KȁٱfTRvS =U.$ LZˇ&-e&!;0"KСYcŰT$R='GR|l(6I԰$TV3&;wnpBx>TE =2 L>,] sSeh38 E]іMKݭw4* {u3& }#@}UWP";m~FvRfpі E+SكYU{Y)X@(VQ̮9tA{cX5k !Fe_BѾ%T0s~P-y'P JB94 AkjYkAxqm/+m,CnvOM> >yڼlrW VLaf, BĜ6yq6l>n;m52'S2r> 6}nuM^[N7.2PFE㌞Y}eZYq#ke#8r\[GBiY<.k,C*d@~';p G8=wУߏ"UT#f 9pYNܘĘwyNi# Rf;y0c5}57Tpee8(;-%-VRيdWТK8z0H/Y11k[cBp:^&s8~LV;$k#>W&s;QagE\<+sj)UC~@C\K"~WS(`ZP}. ĤA2&r Hmyk5-D?i<Ox )F~QH=2 o&ίM4w95yk]&n[G-Ox#g^P#2yc2Q&Aɨאt[ij3'1^B!Fy'|'wba,ڬ>w)S`Lz(?S!`+@yM{u#[6zR]lHdl58~%,Z"M|wFs4(gO F!i7rp1J7Sg^{oE=/ hu"=5p{eC3 nmӌpl@Ε5{k5vВJ{5I(s8}GFcI3H2lmH[bOZx0%bS&Hc?pnL(rܳ n\&av\qk{ *-eꙸc6Fǰ\ASW$N;+a~/'zdd,|jE~ =6gG4xh/n;gԱ٫MǼ6~mdX(d׵1L^hqI/p477N8`MN.gg.WH&OZC(Ahs%W4*:\>hAW[6KzK6o޻&0p:fo %M?\k^jl ̡spiC{qAمrd n-fCXsUY'caPFx}'kҰٵ2y92--E!l-)ԩMB}7WlQo,|MnHZ`Psf>{ݧQ (D-Wi~oo6kgҴ2zgY0PmWzc~IDj_x왻~ؖDI:OcݜCHGx~q\x E>Ah-9srtOP(zMoؐ7|)e[qٰq:TFbRX/HO0~LxR' }ҧ9RYCКK iݕ ƢqƳgk,&jVgwu E JX%)Ted HeZrx'e6n=!u S%J}80lZ83ܬH'HFtv[0)#)4aR) )2tXUu#`q0xiFaʆdOzRpOf\ƫ}y7)ܫwzuCRzͺ 鯉mXB|v۶aKPmJ3d ؗ8 k3 {W)R~8wujܖbضl+uǽ:>|Znr8*Ig1Ҧ`eTvdV=g+5 -9;^TN`ҧ I J>'a4}lYǽGD;# LJh3r =H;Zǐ!tcȰCgV1}Q@rQY7I'mr{]:\;/fFsirr].mS8ܹyH2N]}Ua  Bk! ܣWslX<6H> j8w1IὬ8öOa_ӭ$nč%rVz FZܙ׵9;w6 5N =;Fn;Ôiھߊu "a \\-B3moL 8$=vhܣcƴ1ed__P@c̨fx0,%+Z%NozZoa#-OGf%$l7ĵ H+=PҚUcXL{`u3gk<M f]|X_|7_>xɮ'bQAF/ETF ^ξ[w?,=_u>wԣK7:2oAf6sr$ߓ F2j1\sE*ĝQ̂G_>ITtQMhW>v{s!|DcfcV :crqԓ;3 &n=u?k?n)VX 9hSf=?ça:r0nUiO(\|7D(xa{-^{l74fzlPVOYY''3t΋9}.<ۖ~0^yxTjY?SxgG|뚻fAzđv.@g5v8Y\)C٢=ob!3å~ցP]Ę`YWؕjۭWgW֙\{jt Z$_ۨuFXK:*%Ϲ\_|aV=uƟUӫF~V˼lI:}$mi۶3.x݉}L[4KIs)OhofCܞwV ,ldQo7,|\KnFg4֝ȍ"tH_?B lR3Kv$My 5aa$CgT*)_]-y-(_^ČrG0=q}zufNq׼˭$|_^_4lH&}Nb@Vw&A$YoyށUOo`^w`i&~C>_w:/ܒe5d;Yh#NKBp^Ybh :@ q־N!T5(5iW?Rz7G0.If3*: wݻ@Bߴ.aw;#N P0HWCa>q+իR޿)DߔS Ɉtɞڪ" Ni_"-[ I W{k~?'eH+Rw|ޖWdۨ O(WV\Da낅HRTDk*zQ=|=5gsoX# ٵuލ{Wr.4DA Q/v+S<Z4 &C3":zxp_y?IpF$r 7Kɲi}`c \KA<܆HX>̆^JsT3Ac1 &^sA [gwt܍u 3xWlqGF9}-G [ŶT|\%U8$}n1Kȳ_3@ynmoh='ᖭۿnX:8XLaJ N4Bjdwk Y*(rx&q_>yF<ώ "hC&sXQy/sS\[u-LfH9e{ݹ7j^cEGl|AH_SVqŒi{^;`?^nYxC4]tL;>CMz6,cZA딴 + E;Cι[R[ڼ~{y*HActBL  ckw$3ܨg}-`#ޕQ=nZR%sJl[98VQ<6O?t5v7sK^=[ǽ wTs  1ONSľv"4HԖl|lWb}.p<[nL5B% !(xg] \DCgm~MVP2]s+Hf$JAٕl!X{|M\H a4DSˈ-ڑdCJvd4{.@x.*=Od螦= "nB~)Ā![ 0 zDMC᪁e:=4s*DE` X́_ʢIZ{ fCoQWeoZ~,ΡoEdJ#gl<ʕiG:oN3kx|3 vHjSro#jNǯu*gztoY㼘$b}KIPMVChQ1q|wIW Q ͕MIqm=:[ʉLcgqbd+jP锈j4X*+*uX ɡC2Lc#/.j)|IadiFa vEb*">.JV\` |H[Lg/Hv5S=C4@pj(>/e;MA Q6ɈMahXolCcnc'_[5@ ʋYe%%7ʺл'|mtʼn@9~7 ͆m/ F2X`S]m{ו >θǝ9-#%cZP覃p`Q~hDWyv9Y( mz##ǎ6Hb[LȒBnXAXմDXtAF]w}i=-=s7ˌΟ(NY?Yzost,G;3n]6nOUn?/pto`ļ5)Dȕؕ50`"SOrd~ЕRY:ٲzEi^>U(Z}Fz[Uu6*zI'z^_YdO,8kӝ' W$r S*;bZȘ2,Yg^Yߓlj #?4SAZ+N,gDAӜ@10+=HfMǀX? k Ūox塌%n.<\ ۣ!] ] -9 aj֫*2Egk9W`Ip[LH|7$990 678.2523825.4612513* !#17|MFb|S{k;[QTbzżz-&ydMudjߞhpQ"Ӗ?f9խˢ,.9Fkaп<9sڂRrW VTq1Wi>=jcwˊzS jv$Is_| a?3 OxB:0,џ?54cZ['Fal-Wc_JɒOuhfliE5̕r̒ayzpHE`YE3^C 34آ_/"͢jɱH3,ڛr,1Ș3[4RCGbDƸͨ֯;_5Z _!{R~jglO͘'(@yE$sj~Z(ݯ<GFq*@CY4J>y\K S#. "\|-$:ƸǾ2,Z6SJ  C 6Jƒ °8@a0  P `B2)Fƀ8KAAnjD2pqZ6hό(޺4udOe.GaEI7ʷ|9釽L:S%48 RiOY7Z>Ru_0j*#5k*sM&RȂ(rgAVA#QPlXAq_ƿ~Z{i.T?\Lv3mO{ڮxy}YW\,?)!l@A\ k3V2pTg׀{ =Uqz$1Iᖥ6:q%kQeq fVhhK'0GcD]g\TvTdzfXO ӗvE+z4NHTY_e,tr 4bufѬ%WI7;dI6nxar-K3Nƀ;me0Vp+ݾg7R8oŷ ׆[m#1Dbw:IC.br&{\R)9Gj|F)yރVʔ=CU1틪3JE ݗcԎ;5v(꠼d9#\qx`$r)XsXER4B4@4YBSb"0X+_h`LyYҥ>U\h̊ABؖ`i[يNs~TڝO`NKj򱝨r䝒y E8m{W# PőkZd ~ܻgh33/ _C"BxDž}4=\b.)5h=[X5DI|H'b94PbäҺ3YpYE iD.x@+ iMD・*؀>$k'5V׈c)B(*U.AIvSkQ TXyKh823w%L)bv Ty +uXb,IAk<(SZ1UΏ,8[잋?8.㌮\@qHvYl<%\ '^6kU3ӌn2r#fD\*d_.s C[+)\tc"d=TE/\+^5*Iqs_2Dւ$>l{$s }cuaf18fFdj@+9/G^0D3HiT1f NC];71.Z5{QMUH5,Ӟ#/|Y@i@omC$c OÀ /㡈[ Jׄ-fm[Kڸn+xM4!Ŭ;J/qZ_3uJr3_%я)R=3Cō *(yr@S%C J+0?%MӶ§ 3ADҩPZʹ LKf/?yc97HYЃFY /%smAu+-Bt84N\ ]Ws悇8rwaL:,}yyLȂiiՁl^V_^P0\Ia7":>er])aW1W5f2D<L33= 2b(Uz-/GKFbC,tv՝HA}ב=tm4.u̷砗!]=uv+Y@bnBd.MqgtQ˳`vaû1Oy~cB 8tE񫁥w) I찍qDYk<䊇\ qbضx (p3)Na]jZ`:(tGB|咠f)Ci ^_<9oc>F$ԟgXpw󁚄*D"IHp X𷤽V8Ɉ V+^ÊEYg|߸xNJd?&?dt$dn nXRF[ڮ|z.HXu{-z}:q U_lc f+8^L-l0 g6 WM v3;)NmN~%6_93TYpӦ^8~Gb,KC$_OMϩnޗD A 'Gl!DgݒCDS,eφV͸q-z%~N5gg  G`R POM{Y%BJ<X"mLӼԻ::m?HΓQ+|@;\dHZb&`. 6/KRJ<C" 5ή~Lh@ fW rS2}ՒF<4yp:3!wl]ediKog,dn8H*.ۚ.k I24^ăʍk0;S87>-+KS9H??8/I %irnoWp2E4PB!e0XΎp|NUs/Q־[q$kǂ-7C%) 'mpAҨNF4rzV#rD^"BcHGQRE;esJ+'&O%iAU-k3rWRNƛg8$REHNr9]EB`=wse'>Ҍ6ʛ:76UT)">]H+F$&Kj%j0иBbz]n eajj*[)e"ڲظL9{2E괯'kE=jiעmr^<'H[:*6oDt/Oq3P[oCҺֵ݇TaD'z4|مri.:c7lDTTb.KO![h[Ԑkeq9`̓;uJ UjOx̞U,43GA&oN#8.0Ht;˔KSWIP>'5%PA'HLj3D21LxT Rkr mO5A% fi] VEɀNx@ϺzhHqlm[ن$gp'0WF H-m4uKA3nr&)2 7K]$bF!R>sA;& p _CA @"((((@Xt3B!lu`Ѣ5jڪTv4F(= [ k~ڿw|us~fߍ/x㎫v[KŻ}jiyf#P119sk ;G}i3B9C )pPPУT 9!ɯ^ a1K׷Zj|::HڿvMr>.߬Ü9./~wg]TӜUߪ1oi_ۚ79fn9Ywƞ9޽N/MN^6/{cuTk7ڱݻuްToMWyTm2Le{Ys3ǫTsw$(kw&zi]o^Ew}{{f=4 ue7s^}Ƿqzks~QQ|/bJkE19Ywz{bsΜb墳g.cQ{9esQu>s8^f,jd9ջ\KGͼ=G=W+ξ8<9{~_ޟ;yG9~kgj:Su}~^hGy@s>z\W[\5=-=UY3j1r4c_bszϞ9ߊq:h5zu6;m-vN[杶ޞӖ$f1{Ye]eyyYo{^{/r̳{ϼۚo_y֞yW}e_󚱯9lY7gujƾڋ_:+:z=~e]?W[yss﹬_e^uR{V+[qocm0(A x0􃁜LsNi;m 9mjt\ $P`2?9w;Z)c}9z7n;;拹}w(\W~sZ{qko7l_9jj;<[u{տ<<\W7cQ+y!zb6rzob߹{ss_gy{o[~c{23T&SĜs*8mW L7@US(T3LڷڟLw흣+Cz>|w6Q{s{Dou{ZkZk杽c۽z-Zmfk|os|os;ӭ&0Tc9{z9G=Gg^j.{ѻyk]j;wQqtg 9S{QQιyoV=oǘ]JCx~{ikʯr.yrL-J6cRt 0 K!L,SXfι=~wO[r2ڏ)D}载# 4Q1Y~%Y$xE7?+kLQ2S+jYeTIa(:²cib@QTЂ3F-]z @O3'M OV`'怜(RT,Z@ i¾9C1D}gГ{\EO04i ISRLFY~m5$URNp9feD~;k{^jzc1k_;{uޘo￸fmj9Ez5d(WF\Pk3}w{̹<]Ljzj/[w{-r_coe^}ov7W:xoZ{ݱ}]л+#H%C9[gX/{JZ^!鄥6%^ BqbϽfo]Jo?wk;}_][矯{c=m=;ۛoW>j3ҡ_9^kܫw_?5{my/Y}V1 ҡ/_g-j<7[}%Ct]go]-]{+thm=~띱voܵ{cy{g={/j3ϻom_ 捹s9˯^:s{k|8_nu[o%Ct(;8zoݭ [8o/ǹr+ujqyk ͗c\oeV2s1ckomks쵾9sl׽n1{W2T[Ҏ?טzskv_un5W2[о3v3J+j[k^_~lָ9nW[+7W1g|W2̫qoͷ͕ Zoorb߭c/V+z5[/9o߫{>۾8ﭿe7ֻkV8J}b}&HDY6 Hۜ;qoj"IM-3ʸͯxwmҮk"f (X+0CU$`CPGf,RL &OUG%(%R1dR=Z ~+\\-w,Z'VW-vۮ:ߑ &)VĨYOʖkL]5]$JP$U+Vs˻͇f:#}OXX_$`YRUYIb*r$_x¾J@bVcq AG)PDP\*\V׋EXbxR)T)L2rlUVU)2W00,Ǫ*qIsLj)Pe~(hc&Ѣ5@JPT +^$"EP5TdOx() eC*,QP%HMD!6(Hbg~ 惡B:j( "fKNei`Z>R/ Adʼn$"!I 5:dx>0V,% !S^$eb{@H 1K%:aq-{x 0a!,H,]bZF:9H!'+*frRt"m4(TM-,X\}T5I-+ >5BIJ*DQjA}$P:|:#(*\U>:R1iR~!Ӧ$=XAh!4E 0"P(2G&,$JB": -UHXP VNE6/` VAx1jQ 2 60LhYDZ y[PzwD%A,`0b !LA"e[|TD"|, (yy,*EO𭧌L.:6ٲBӡT"Z^D2&Ƌq2`qH'CH0 >`( ؂8bh*y$<0tA:Cy0y(xfl t3YFfr {&S p42(C61$-Ġw䢠01.Ak&AnBJF;5":M#!h ;XE !X,ZwpT-;SBk < :Zy]h p 5˚ͫ-^V,PUHpZ4e $Hhu,C۲847 5fL8C4m4MY`"r|#4Mh&¤C4DhZWn4M;h4£bhq7 @MVF<&\y1d&--FŒ д&kr?W&wԶL̖=>&bsfxH,jPÍxu=Y%P\  b/&W߲S ; Cih .ʀ@ o3@ vTǀc/̅ˡڲЀ24:6E3`UȖ MqQ0#LV/L}v iV@]Q$<4Dд[vhb44-.ɪ!i 0`&WUGyNYr+HDR\p"Dq 2m(.Q|"d&, f8U3hu(U t@_#9R+":%pa<[ QNARXYp&CH64| &\' vMFy8H3u#̀PS51Xi26(264r`m92vD(U]0wAd|qWxvj"J[V2L0&.y&.cMᒵK d@B mkSDpl–CK ebd ,;5crn@.:2¦0epf](\NiR2tXuTU] U~Ty`Ҝ,-ηe%6nكryC(? Axt,$4Cd!{:܃e`$v9Vnٖ5@өBQnI92R髈UlRM`O ([mCVDT!+[L7Z[*y221 'p0]H\H\H\H8(wc&w e2% .+/NJ|LH`,XKc`DbFrsci,M endstream endobj 20 0 obj <>stream `햁2%XHRFPᱝ3ڼu,-$l @4\c@6& ,l`e^#D2A?3 !dWDL |j ce?oK䓵 b(]rN@.ȅ9c9Y"*[j ɖe霌/{X'SfeQrZ&i\NKɖLPYk k3J](\$vlYwY&[9:ҩL"Ei8A{` lè30-CLt,Fbs r-\QLlo( ?bwY&Gvʁ"ʖdsbsG -@ оDR6/@ oylrhBa܆09Ab,!]MD[$ آlEfF߂@fPAH˶m}$!HbcE ohDcsL"#.HulNu[6d؏߲F')0u-kKP+ V^.˘BQ!#xI/m"VVNfc : 's,CeG0Q &byOLJ+/*2K)KM3H(S($$c {[ \ j"IiGa(*6l@0pQ [fyPEQӖGt<> VGz얕c RH Y  \-!!9vA0D r:DQd"l"VnY0"F38onYisPc@evUtDIS(6(ij ?"YH[f0 [1hk-80pBdI,ǚ4H_$[:U0A,ʂ0#4-ĆG),)R4Qn4CX&1z &P6:TY8Ȧ-#iMDY2 4;24.MJ0,yLh[ #Lh@Zh+<,xL:FrSLXrS#:x  UucU6;S@8g"t.ͶL@&6B6Bd9Mc h$D [ڇPh.D۲уiOh->L4_e:ɜ \  4M(6˄M(*]&b[&Ip ',`br ?4C`zo肅gdx1(GG#Ђj,SqLL> G#$ 8sCo0,@8dXLPSa0,Fp5RЌQi`j~4$36 )OA#),X $@+G#}((+ (  & 'hR N M,pY(C@A ?k1W0sÄ*?P *KN4A 6v’aDa@CqNш`nH~ j`ԤF:q4{?`AX`i~&6w\>F Y(~8\ODˬтR =aa)JЌUh:M)򞟸:A$*IQēDIJh0p(dehcN0|PB,GP0( (eQjlXQ],B.Rg(B(HYBB-ZFHlfxQ׉|Z`|f: Y,cIQtKG]HoJRE]%]n?]Xh&6@u(Eeq-3Of4@1X/oWy<[.iaFПQWVOҢ˳.G6t 'xuO!(T߄<"x{\.kyԽ)u)m쬝'ꦍ O .U0XQW<]]Eb7&yW.㲔R{(Qfjm\J" Va407E+VVԑ#)PS =o) G톋l9q22 @g$%YroOz| mr/+3,Aq{Rtv4lObuvc=9NvR +XaI̦:DYz=(-?r]#vN0,)XX*L^cir>y˷&ꋧ޻DއeWڻ^|a540RGrjiCu)r3ԡbyDi,+Hiʌ#9 m!NZNл>5Ccc]ܔ!8o"˴)ig)%ҁ8AFXR$l9ps;ZɌsp} [Z'VU?:\F)w^.Yߪ1U&Uv̰;\bRpN\=f)D=;qpqvȩ g5r,ё":)kYRbf[Tlʘ rXv̒m7Z+[!o0.7NA;Kp,0۴+3YH=H-5ꝅɊh ų ӧb^5bfő<$hzkeFHRYCe@(8Lq>K'5^Ԣ#LR܀TaK߉)Zhş>i@Ml2(΅R8q|PBFH$e s,&y1ݤmb 0,c1qWB?ɭy_ˢ:b:Jh4vUQE-'fk#E%{QgXj?سZ.T1v]K*%D3XChϥw?D&‘NƖl]_+50"آ)4gv2G[u nWa.D'}0b) oHfL *4W + >@֋3HW@k2}Ҷ>qێ+*`70[I͕Q1ĜOT%&n5H ћN:=j#Y#|%rpniBr^)I9ZR4P*p}g"IwHLB 8@D񿁎:zf]G˴$0*9rmTM&z< Kg(? $hhf*s#g,}G#El+a5;sB8sq:k!1#$G/=[LyȦW` o>A^<i/!t;=/ B~@._!͏z΁/ߑ;Y z~]K i?vYap~t|߂|9YԪet^$2 ;gr߰&T[B -kđ\Ҝ.ؘm?] ^zXڀG y]ᏇDe)q0mbe}j SmWӄyqvF'3 cҭ 2e1wڙ"2W"ڠ։ceC)頁+͠sJRxжkbΈoLnJ5cox"C&ΣC W5 c(K$-kˈb4ؐX^BATv6]э{jc ZtAjoߛnLP(#ψS%lbEPxt{וE佚-U/l^'4-~]muuWwB#)1Ÿ(-09ボV$x@O) dvPzjބco8cr W4)4Qq~-\(R7eOVJԎB7Ön's:IvƦ6\z{VFVIYS,@bd=,ogkDpw pY,f5[BJ|9|/F ((sapwj6.9YjKzȢnX›^LasD|šHɍr|5%QA?3 heT _ ^cRgfX+!gYޭGR=r%0-˗ZB%Cb:ǂC;t1h7YBdAȵKd<3͙Tq0 pzRhr`W|k5qC!k8WeS66 ~ECq~6 FpxY{є0f Ƴg7=6 Gy4D0/ Y8L@Z_ĢpXWRvV^#3[- )FiP '*JP+A "d8 c9ҥ]pVKֆ?!YG;k In^8MUlO@:O9~vxey@#̈W55G?糉 xK:=Ad>$@*fgk!7qMOd"M0yw?o&)?.uϊM]s/KE U:B'2<$=|n^\ qY>l*3mEQMđcj)MqŇѓV6! aSpi3lCnb~8rR `qRљ7X{x .E1^Tg-@^'"2hW@*#"Og*wz>cB (ܯw@4rk{h>e??j?wH?4OZxIZ\wa$2|HX#D\ _POh9nq՚:u"8-K h8zs O\8=£3"G$70] i4[W5"d_ch_!61dtd - LRs&a9bׇDk9cFnN_tU"`N%qE27jkz~"-mDg"D?T>El@:za RiMuY mmK'XfUQ懑8%>1QeB[Ưvcn4Kpі+NV0S,ŊSa@>r%iՃG$_2YS:ڴgBYh bdB襥=f~#r|-N-l9 CG;@S;e%Vtzc $ׯJ+s&ӣ]3X?z)xOD "M3^óFЅ1dH5,!߁ͼt (=T0gG4߯C* m J+gp%]p Ni΍{BHDŘmYMK~^Fsryk:IB|qce8>4Qr+v*. <ma(JLovԏ[%FVM |?HPı\<+(˦umdgS wQ"2=AHR`T瀺B(M3?M'Ȋr:rh1NT#UP{p[[3"(aЅI(T^Peona=_=Ƶסu<ETP5B4 ,KMZ-@Ňgֹ1EF@]>T;U˗c\#d@r4&&Y$#InuH֤([Ek\Q2[SWLGz&RL%8GGIMX-4؝ؑO}|/AGǁz1bn>}ӂyQeu%,Qj tV2 nU ?rB\ZH`8b58^lR Bc97̢t熽)pQv2@h}6>‰ǿ%Ŝ|z^XX/ՄB/G+0;OhT(ʹh"T R;gwlѕ{2,$_NIV"8WŤlOj+%rOPLGݙrCTQʻy>/"2Usp .|iRyMZ:!﹉VN,4+5r J>~^*geiR P7b V0POЍ)dmKeKoenz+- ig7VofVb"ܔAoH9+QڲW)WIhZU: ǎQF2Ħ.rҵ]Qe0ך {@oOJ!H}.%d?WrEOFiXD5B\t1;'1s HԶX̳m>6HQ5='}Zaޏ=ač&(,ڂxKf$pOoRwsE⋝qy?c_~ea H+L\AO Æ g9STDnp# JM-d)@,C+9kz9>w#Gvyx Hp_;j29 Zm-jşBT4G շ58"T51Z6B私_E&ϑA5|&~nTG\K(cî5y4sry.}Ky6D<#]Hk"28 l*xT#Zuo>|>U D{z|#Q[ Em ])el W,&󞹄Ph4S^@mZҖ͂P]?9  AODcWCv앯ܭZ*_Qi.n6t։2g8/Ӽ4BָSp0i3"߃?3ɒ¹gzT ն8Bvt>ֹPǣ8I"$.> fɂ~`‖@aE7AF2)nY![LRd{*ꂛ~ + *[FJ 6leӉٍ(*b{E1+!%]SID!IsR71z&UsNaGX_׎W6*5s%Oe'ub:aN\+s ևX5V5om3pZzf/@* dQt:A>_/Pa !;/(,"v'mƅ|KŇ :@x5wZSHlOJL߯`/cM-ݹ1؎GHJ AIB &}ZZ|! Bn  ]GF鐸 ʊ#Senn{H񮽺 >rd@:݅Sb__.Xh Jn }Cy|ln ["7'70E{*$>KD"B%T(&Hg|dIQ?DtַShL$ZRWs8;kqU3ͶTP1|/h HIٌ7j v 1HYL*q=%#͠rk?YCک|q=2GCRˆ>],ai E|TݩJz!tP ?:U+\(Z*@Ճz 5Θ#+bg\{BoÃ_e0.B] (}s/0vqYJw4r ̭@7)ˮ|ZrH/Bscf)zT5-;΍ZoUx KqF9@b`u2G.d@RȰLѻ5&sy.=n9@M:9P(Nf<HB脇wg̘ěJdwz@Y\'uȝ8/=DAɄqGPw(Isohz y9E:Nz`)Lrk^01Bѳ˜vNY,0IDP!lKz8pێM0~gYm)X2V΍ t*x{@rQX}0蒟[9C}_Yeb]֒F_Ɵ_̃:OY7\8$D15nb7_MZ6K^JIarY&L~691u;0 U׭uӾ/ou-uu-ڧn?W mC[O'&CՃh1TUIʅP$:me1lFRcyjVVܡc "9TNK~Ɨ,uMay·7;Hu,InZHT>Ԯ=_ۚwZfX1hH$PjE('x؞BJFium ȓSyXׄ^j8઒Ml]`NS8%v^C&Z7>Zʺ>sF"$2> x^N{gslxT#_UEow/Fte Q06"0O"%Ɍ HJ',J>uL,auwškj6'˗PDxMxz跲WR"(A~9<;dۻ^zhza>ł/_!g(7;wp)pݒUr  %&̈pTS3F4ٯl@6Ԥ^L+XqUXzC68ؾ+v;~=UMDQ bm hfLqxueRe |47!߭Lq$cz SR;)8YC[ ն~yAK9$#fdkiR0dD9!xVkRo{pIЊຖ~˼XR 7U(3]#d|',&&6 åk~fs,|B[{wY$Bm.\I*d8o\!`\$m"oamx++>Y\O[iVj7 nCx |8zAFuᕷ^Z>S+,$hsu[9F0"f5`5hmIRtS_F9 js76]Vld1}y5/4hN=xO 2ipItUQ<ӈy w >4h) L:!g&=*ȹ:Tǵ)nmm]mDj=|,/2o뫓pf"wDޥ%XJd\n0Y:Xpm܊ {!-6 #z./Л2~(^Zi⡿>-P ntxM:®tO%jj`˩ |;Vs\Q+R qBJkj@9b~K).EN@"𶲝tEFqC?$\\mrt yĔG)Wʑ_/G 5Իz8[{ `ܳJ-ʫ@;t2 >űf &4Or`F c_c sļ ,+nxtQHr"sfhݖU8\|R@QEBKEfW3! 3%VL<(@P2i,r_ yց8DǓdě3eC[)O*z0v_}A]z;Pr(}Z>&Y,>y2(o᥌*l71~#[嬟7f! O[5ֺ/&)4 cs$<TTrg#L7c06x1 2XƳn\Eq.¸Yͦ挋j\@YE]j ZmtP ݲѹ0_CJpoQ™-Rn3`3cN{[jYXUp Q[NA>B\X` "L D#bpO.:Lpeh=Z&R+71 G+%Q!;`xY\26f1|FJ`B"q4 0iDkgOc_ wUVr^YؠVbdV0flCW&3|HfE3+=QǴ1kqXi^AjV vNFT+*/,bYr c*b֦Z3U_?Z')(=aVĘ*w(h"s$̧h 8$^bs0_$w*'JT^% `"A'#бpX`K"4JL7>%h%9 @IȆ^;aY'lsth.~𬦀~}E+C$UvL#)|Nۋ@ŕHN!:99]or1y#,䦪1qb o? MBn,pZl$Ϳbw nD\z`tF5,v6)+f-[,us'n;RһS;vWine{NhJ x`- 5O&Dx_BG]Uw}MLReѥ;d)2-xp7Ue9V b"7ssW *o@JQNɫl|a@Ԅ#H=AjCw0=F*]Z34 C.x'cEh!5fƒ KM~R t5r}!b3KWҷ *ǻ Er”j7qaWёnbh·ImChhrL0Z;%wbE̪6QMS<ϸ OynPIYVR$4\-nr0僘eQ:Ԗw//iOT.Xh]mhdf@[x==^J.Iu!KZ H{Dv|-YY!:u/y7:;d9iu'( yZڐԮ=`0<4Xl^uyܠm[ZW VWª,4{R 50) /4vzor b=u9(e uv I0u,/:)xdB#I[@F& ! Z^5LF5z9%"J̦WZ6+6,{kKn^67}XoyPu&aܚ(\qjV#7M/W2U8r%TU~_/gP~f<`5E!ZjAp5dz"X~@pSC `)۬=B GY<.%xa2ߚO2x)Cs:|Q5J7xؾIE5x Ӂ8$n ZqCB3½k× Hf=_b®eag"J[|fl2,p`e2^pu7UwSj”ej~T_bRyZCg;@m$Mq&۫5͊QM5rin/!df6:k2hfh wlVKDM2m5lvݭ #L!g;V*3 x"0#(qDH)R.!͊K(A^@I'(W%w'hNaM^MHu 39o˟5O&op͆_g¯=&YknMIs]m 14U<,Y# = S61lAIu/ EJ eYǑW*82&8V~Ƥ4\+0K5~r*X; L|rˉA/УPR/Bhs]dq;pL8}Y@V񁵽37GkPC6o]88l#+(jtmNKQ hԳq^^ zU^}vE/r*ag"7/ VZ߮$HR5* f7l#r+j?U|D]Q8 0Ww0) t , k4c5 |?eiCn[LQ( CApR!ml*& Grl}@SOzoX$pDKjK 9d0X4p mXbMn"*fLnD[k~' X(0Lp\K6'k pV xX29Ut5yznɋz!⒒׾YIMQ+kB a;J)dƒ<~{6~ czyqw ~ 0 oo+ ^93ۼ_*&uI!gq1ǥAP*k'3 {+'U^ByRC mCS Hȡklk"`{>Mez ] i291 5i SZo F"fCCPJxy_COO4z[I$UP==IK_1=qj @V' dВ*:|O6["C*{$jv)gUϡ_^M].VBT1p:G.h7?4$V[9aO9 k;N3cT0Tt DKÁ)]YЇ\J;rq-,㣮KP!Æ_5Ig0'J`ZHfk-jt E1ܐѶAy,iȑeePnGUԅ҅&ʙ=˅ ֌KO6*G +B(+ybY) `df A mA{X3 HwAOV:ot(ؠHm>l yWXϓYBg]æ;FGU'.*S4+dpKR!8Owapfׅ,6ҵ' or|ވ8N#WS@м?`\J7^Iypݵ5];((ïNoYZ?d'1 1eWG&[>x@Sdžlݑ2VQCM ?q k'`P4-{l'aPl>` #Q|2VqObyY^=mJqw,pK!avS ,U~j e$iEn30x?њ5huya'-.Z V j[jy%-sڶSn44!!xB+\_@Z!;@X/sk4 [I 7Xiv^y ;<>Ż[<>ݸn]#E]N;I/uQ5"]ȕcb [΁Nl$[YT Ļ) }$'vs{t@t>#oV<%=dL/3.n ZVmՀj3Հ&+aW&]H믆%&{j0 %+`j $]tn]O^ t~ߧ[>)JDKP=5ؠ}atöQaX 83"`~AM@1 a5 aЫqRCJX 3j5DuȎBW6"yVC:x“r5L6gLj'6/50KW0t53 Za2^/nfkaC!|+75$j1CWC6aj` 1]#j(aj H<@UL 6e51W,;2JK~JoRG@!=IPM{x*+^Z$FX޵OEVsCP) @j6/-4$WM昒 AK0m^r eWM}^;Q!Y4w GZ~@mKP\QT BP+dRuR~ .DE+;L]nLGt7Ƞpctg[7KdF,* G W]d?GTRFEJ>TvDk#=[NQD!Hs.KQ!"agR9g ;^5=[%[XaWsп^$=@Ծ R'~DG%4qS?W 7pEsqvvCI/&0wC"iR7I;.8O3#;ԆIRt YjKm;i6}OKknG{yTq!?)U5-^t.߶ƭ+' OCLq>懵Յ%'Qf)q=Ejd$Lt!V"7xc;״zl 6 qآ@0<Lk"5w'qUEN؎ܴB:w^{un -"=Fm:ܯ8fG_6/sJDieɠ7J X̐C_MzZv MQU ʻ4NS*΢@mTz 6&xsvr c_خMXplk4gyqbRJYu>)LEfY5m&0DokOژX!IPLl`vV׆X7 POvWiE146";/!\\-udfGS''`tp_mNf T`MgеsCNİF8ƻp[ `mZ :cܐ)>2F'*V^pg}mX #/\ iY"ҍNT'ٺ(&2t ܵ޷;`.σ) \R1JsK"E#a67WQסǦ枖SJ$‡P}cv)O@hx9ɘ ݯxNݚL1r9V.y5XrS2NUN|lj bx˂e!+\}'U7ѠA'}{'C)*CԝC=#vι1Y?oȉ!FD$Y9".L|M1peZ3w()vn_9I`Bbk5w_HkbN) 6T k~L]gdld67]6ZՑ!?(!!-Jn"37Ll F"d|BX\.)Y/<4 y][=U|-~]o=cEdɵ.X#V~ݍU|߷פrZz>|J;el čp\Į~OC'&i_2@@NE_AUYpq.`¥6 :TdlT0``RrV_㞐i]~Gw{9ZۯvRѢhpR1!qj酇VK(k (2/~C̑UE2KU'RJXL½>̍>4_FғaHu:^D8"D/Ґf,K~nl19g.o4jŴۯk*;!ޓ/Ʉ;r<$ |fۯd6\i$ʴ%.o~1I7ꥀfW4 o՝QHV]gf=ɨ=#/K i*3nꖶsaRߜ.a < BgC=;AZPy*/t.(H5{HwFA-g~)*ٯfk^Kfd; _^|T}]sghYY]g?# (#o )lyՓ&?rz Xibjt0,z fc|$Dc(eMD5ܧ}ɷȓqCzpVбk7ۓތD`^:_Wܷ\ l$wyxt0%s:J&s^gkwK{6 rДN j5e:)(l4M6H8s.;ɚ4tA.#)ޯl:+&Tngb[e’[h!q#2"%d^M`|'Њ)iUH*xз=# 4Vd7W򬄨X5ƬFJ Z *QrMu{6ԩ߉8vl a]58RVWP=ai~/)d#ĐbQ&dRsɝXD "$d!J}L)2G^]!22L&9%2eaB$z$AȤ0Qnʅ¢otZe6SKȶȨrZs%! FAېhgVdg@Ruߥm G߻l rpD^zCe iWP\JO$Iv[4Ʉ:'+V;ϙF}=j릫 Ub@+ZݹSBm B1xsvR Ȧk='O\!zUV%7A"xMVpYHETsAPYb y*:εzl0O 4f^^}񥣼9>rTlq2+BjN\o2 `[~*SP_i_ܗb,ﷳMɐ 'uQ;/_gAnOUvl>r1bv^bi!w~lDm2׉XOk($fv4%RL%-$ t>^- <ٶD5n =I/dQ1ѸVm+=tD3}ܶ$[;h, Ɵ\*ҙ~IDo%Zub}$QPfV/; LiRw|ҹM [\rwqwY{ )>ŶW!R_%5uM_)w2^n-w#ߒr(&G[V1x@j`2dx(1RS& -\U j)Fދk"\+@H!62ǓD޸W)+Z,kz.PeiqA8 kvnR_0*M9HsRTy:zI͵C C ڦL IS'0ۏ0>Q]q(Qf P=ƌ>- h9NKK:% /Be-6T4l laq\:AW9dd_A-7   veYfmhEIqL7\sh?/!8]QHfwt*4tn&Uyp$ s8++ Ǭ~f:ʮHe.3ǪA90+vWEzF6T5G*=Dn ?Yݥ}pYm<ae<U(l*\GZ>!,W:ep cʍIca uY- d"(O0}EGnsl=ew(Ԭ4!tIo׮UAR  b}Ӛ i&]KJŔ^Q!2;0/tE]bE]O=)n 6ڒF;V~5lf[M{h_dtͯx€+ endstream endobj 24 0 obj [23 0 R] endobj 36 0 obj <> endobj xref 0 37 0000000004 65535 f 0000000016 00000 n 0000000147 00000 n 0000050080 00000 n 0000000000 00000 f 0000050131 00000 n 0000000000 00000 f 0000056163 00000 n 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000056236 00000 n 0000056432 00000 n 0000057604 00000 n 0000123192 00000 n 0000188780 00000 n 0000000000 00000 f 0000052164 00000 n 0000051977 00000 n 0000218617 00000 n 0000050553 00000 n 0000053480 00000 n 0000053367 00000 n 0000051022 00000 n 0000051416 00000 n 0000051464 00000 n 0000052048 00000 n 0000052079 00000 n 0000052404 00000 n 0000052680 00000 n 0000053515 00000 n 0000218642 00000 n trailer <<3B62822D93F749AE93EA2DBD804B1028>]>> startxref 218855 %%EOF ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2021 notebookJS Developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: MANIFEST.in ================================================ include notebookjs/resources/CommAPI.js include notebookjs/resources/template.html ================================================ FILE: README.md ================================================ # notebookJS: seamless JavaScript integration in Python Notebooks [![made-with-python](https://img.shields.io/badge/Made%20with-Python-1f425f.svg)](https://www.python.org/) [![Open In Collab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/1g8WOn9oZ5G_3-Y8DYmpV1MIj59dnd81u?usp=sharing) [![PyPI version](https://badge.fury.io/py/notebookjs.svg)](https://pypi.org/project/notebookjs) *notebookJS* enables the execution of custom JavaScript code in Python Notebooks (Jupyter Notebook and Google Colab). This Python library can be useful for implementing and reusing interactive Data Visualizations in the Notebook environment. *notebookJS* takes care of downloading and handling Javascript libraries and CSS stylesheets from the web. Furthermore, it supports bidirectional communication between Python and JavaScript. User interactions in HTML/JavaScript can trigger Python callbacks that process data on demand and send the results back to the front-end code. Implementation details in [our paper](https://ieeexplore.ieee.org/iel7/5992/9387473/09391750.pdf?casa_token=v05VFeWM3gwAAAAA:ra4uhd2Xpsd6lllS62Woz1IOjaSOZJGGhh4jpF_ZXOlm1Kq4HTBFHJU7Z-Ez6DDQOUE_djlI5Gk). See our [blog post](https://jorgehpo.medium.com/introducing-notebookjs-seamless-integration-between-python-and-javascript-in-computational-e654ec3fbd18). [![ScatterPlot](https://user-images.githubusercontent.com/14821895/114492279-478ae380-9be6-11eb-8750-30ec3a206816.gif)](https://github.com/jorgehpo/notebookJS/tree/main/Examples/7_D3_scatterplot) ## Install To install, run: `pip install notebookjs` Or clone this repository and run: `python setup.py install` ## API ### execute_js This method executes a javascript function and sets up the infrastructure for bidirectional communication between Python and Javascript using callbacks. ```python execute_js( library_list, main_function, data_dict={}, callbacks={}, css_list=[], ) ``` **Parameters** - library_list : list of str. List of strings containing either 1) URL to a javascript library, 2) javascript code, 3) javascript [bundle](https://github.com/jorgehpo/notebookJS/tree/main/Examples/5_Webpack_BaseballAnnotator_Bidirectional) (Plain JS only - No support for ES6 Modules) - main_function : str. Name of the main function to be called. The function will be called with two parameters: , for example "#my_div", and . - data_dict : dict. Dictionary containing the data to be passed to - callbacks : dict. Dictionary of the form { : }. The javascript library can use callbacks to talk to python. - css_list : list of str. List of strings containing either 1) URL to a CSS stylesheet or 2) CSS styles **Main Function** *main_function* is the javascript function that will be run when execute_js is called. It has the following signature: ```Javascript function main_function(div_id, data_dict) ``` **Example of Main Function** As a simple example, we can use D3 to add a circular div to the output cell: ```Javascript function draw_circle(div_id, data){ // Function that draws a circle of color inside the div using D3 d3.select(div_id) .append("div") .style("width", "50px") .style("height", "50px") .style("background-color", data.color) .style("border-radius", "50px") } ``` **Callbacks** *callbacks* contains a dictionary that maps an identifier string to a Python function. Data is passed to/from callbacks using json/dicts. For example, the following callback computes the number to the power of 2. ``` Python def compute_power_2(data){ n = data.n n2 = n**2 return {"power2": n2} } callbacks = { "compute_power_2": compute_power_2 } execute_js(..., callbacks=callbacks) ``` In Javascript, we can call this callback with the class *CommAPI*. *CommAPI* is automatically injected in the Javascript by *notebookJS*. ``` Javascript let comm = new CommAPI("compute_power_2", (ret)=>{alert("The returned value is " + ret.power2)}) comm.call({n: 3}) // An alert will be shown with the message: "The returned value is 9" ``` Jupyter Notebook and Google Colab have different APIs for sending data to/from Javascript/Python. *CommAPI* abstracts the different APIs in a single convenient class. **Warning**: Callbacks between Python and JS are only available in Jupyter and Colab notebooks. Jupyter Lab is not supported currently. ### save_html This method creates a standalone HTML bundle (containing all data, JS and CSS resources) and saves it to disk. It accepts all parameters of execute_js, with the addition of *html_dest*, the path to the output file. For example, *html_dest="./output.html"* ```python save_html(, html_dest, library_list, main_function, data_dict={}, callbacks=None, css_list=[], ) ``` **Warning:** callbacks do not work in standalone HTML files. This parameter only exists to make *execute_js* and *save_html* interoperable. ## Examples ### Hello World - Python Callbacks In this example, we show how to display "hello world" in multiple languages using Javascript and Python. The Javascript is responsible for updating the front end and requesting a new message from Python. Python returns a random message every time the callback is invoked. ![Hello World Output Gif](https://user-images.githubusercontent.com/14821895/114482788-2d94d500-9bd5-11eb-9ec3-7ee5c5d62a86.gif) **Javascript to update the div with a hello world message** ```Python helloworld_js = """ function helloworld(div_id, data){ comm = new CommAPI("get_hello", (ret) => { document.querySelector(div_id).textContent = ret.text; }); setInterval(() => {comm.call({})}, 1000); comm.call({}); } """ ``` **Defining the Python Callback** ```Python import random def hello_world_random(data): hello_world_languages = [ "Ola Mundo", # Portuguese "Hello World", # English "Hola Mundo", # Spanish "Geiá sou Kósme", # Greek "Kon'nichiwa sekai", # Japanese "Hallo Welt", # German "Namaste duniya", # Hindi "Ni hao, shijiè" # Chinese ] i = random.randint(0, len(hello_world_languages)-1) return {'text': hello_world_languages[i]} ``` **Invoking the function helloworld in notebook** ```Python from notebookjs import execute_js execute_js(helloworld_js, "helloworld", callbacks={"get_hello": hello_world_random}) ``` See this [colab notebook](https://colab.research.google.com/drive/1g8WOn9oZ5G_3-Y8DYmpV1MIj59dnd81u?usp=sharing) for a live demo. ### Radial Bar Chart - Running D3 code in the Notebook Plotting a Radial Bar Chart with data loaded from Python. Adapted from this [bl.ock](https://bl.ocks.org/AntonOrlov/6b42d8676943cc933f48a43a7c7e5b6c). See [Examples/3_RadialBarChart](https://github.com/jorgehpo/notebookJS/blob/main/Examples/3_RadialBarChart/). ```Python # Loading libraries d3_lib_url = "https://d3js.org/d3.v3.min.js" with open("radial_bar.css", "r") as f: radial_bar_css = f.read() with open ("radial_bar_lib.js", "r") as f: radial_bar_lib = f.read() # Loading data import pandas as pd energy = pd.read_csv("energy.csv") # Plotting the Radial Bar Chart from notebookjs import execute_js execute_js(library_list=[d3_lib_url, radial_bar_lib], main_function="radial_bar", data_dict=energy.to_dict(orient="records"), css_list=[radial_bar_css]) ``` ![Radial Bar Chart](https://user-images.githubusercontent.com/14821895/114483438-536ea980-9bd6-11eb-8502-77f7a8864322.gif) ### More examples Please see the [Examples/](https://github.com/jorgehpo/notebookJS/blob/main/Examples/) folder for more examples. ## Reference If you use *notebookJS*, please reference our related work: "*Interactive Data Visualization in Jupyter Notebooks*. JP Ono, J Freire, CT Silva - Computing in Science & Engineering, 2021" Bibtex: ``` @article{ono2021interactive, title={Interactive Data Visualization in Jupyter Notebooks}, author={Ono, Jorge Piazentin and Freire, Juliana and Silva, Claudio T}, journal={Computing in Science \& Engineering}, volume={23}, number={2}, pages={99--106}, year={2021}, publisher={IEEE} } ``` ================================================ FILE: notebookjs/__init__.py ================================================ from ._display import execute_js, save_html ================================================ FILE: notebookjs/_comm.py ================================================ from IPython.core.display import display, HTML, Javascript from IPython import get_ipython def setup_comm_colab(api_call_id, callback): """Function that connects javascript call with a Colab Notebook""" from google.colab import output from IPython import display def _recv(msg): return display.JSON(callback(msg)) # Use display.JSON to transfer an object output.register_callback(api_call_id, _recv) def setup_comm_jupyter(api_call_id, callback): """Function that connects javascript call with a Jupyter Notebook""" def _comm_api(comm, open_msg): @comm.on_msg def _recv(msg): ret = callback(msg['content']['data']) comm.send(ret) get_ipython().kernel.comm_manager.register_target(api_call_id, _comm_api) def setup_comm_api(api_call_id, callback): """Function that abstracts notebook connection (Jupyter or Colab) to javascript""" try: jupyter_setup = True setup_comm_jupyter(api_call_id, callback) except Exception: jupyter_setup = False try: colab_setup = True setup_comm_colab(api_call_id, callback) except Exception: colab_setup = False if not jupyter_setup and not colab_setup: print("Error: Cannot find Jupyter/Colab namespace for Python") ================================================ FILE: notebookjs/_display.py ================================================ from IPython.core.display import display, HTML, Javascript from string import Template, ascii_uppercase import pkg_resources import random import re import json from ._comm import setup_comm_api def id_generator(size=15): """Helper function to generate random div ids.""" chars = list(ascii_uppercase) return ''.join(random.choice(chars) for i in range(size)) def make_html(library_list, main_function, parameter_dict, css_list): """Makes the HTML that will be added to the Notebook""" # Loading Python CommAPI comm_api_path = pkg_resources.resource_filename(__name__, "resources/CommAPI.js") with open(comm_api_path, "r") as f: comm_api_js = f.read() # Making sure library_list and css_list are lists. if type(library_list) is not list: library_list = [library_list] if type(css_list) is not list: css_list = [css_list] # Downloading web resources for idx in range(len(library_list)): if check_url(library_list[idx]): library_list[idx] = download_url(library_list[idx]) for idx in range(len(css_list)): if check_url(css_list[idx]): css_list[idx] = download_url(css_list[idx]) # Adding CommAPI to library_list library_list.insert(0, comm_api_js) # Generating HTML div_id = id_generator() library_bundle = '\n\n'.join(library_list) css_bundle = '\n'.join(css_list) template_path = pkg_resources.resource_filename(__name__, "resources/template.html") with open(template_path, "r") as f: html_all_template = f.read() html_all_template = Template(html_all_template) return html_all_template.substitute(div_id=div_id, library_bundle=library_bundle, main_function=main_function, parameter_dict=json.dumps(parameter_dict), css_bundle=css_bundle) # Regex expression to test if a string is a URL regex_url = re.compile( r'^(?:http|ftp)s?://' # http:// or https:// r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|' #domain... r'localhost|' #localhost... r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' # ...or ip r'(?::\d+)?' # optional port r'(?:/?|[/?]\S+)$', re.IGNORECASE) def check_url(string): """Checks if the string argument is a URL""" return re.match(regex_url, string) is not None def download_url(url): """Downloads a URL file as a browser.""" import requests headers = {'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0'} r = requests.get(url, headers=headers, stream=False) return r.content.decode("utf-8") def save_html(html_dest, library_list, main_function, data_dict = {}, callbacks = None, css_list=[]): """Saves the bundled code (output of execute_js) to an HTML file Parameters ---------- html_dest : str Path to the output HTML dest file. Example: "./output.html" library_list : list of str List of strings containing either 1) URL to a javascript library, 2) javascript code main_function : str Name of the main function to be called. The function will be called with two parameters: , for example "#my_div", and . data_dict : dict Dictionary containing the data to be passed to callbacks : dict Dictionary of the form { : }. The javascript library can use callbacks to talk to python. css_list : list of str List of strings containing either 1) URL to a CSS stylesheet or 2) CSS styles """ if callbacks is not None: print ("Warning: Python callbacks do not work in standalone HTML file.") print ("Saving file...") html_all = make_html(library_list, main_function, data_dict, css_list) with open(html_dest, "w") as f: f.write(html_all) def execute_js(library_list, main_function, data_dict = {}, callbacks = {}, css_list=[]): """Executes a javascript function that can add content to an output div Parameters ---------- library_list : list of str List of strings containing either 1) URL to a javascript library, 2) javascript code main_function : str Name of the main function to be called. The function will be called with two parameters: , for example "#my_div", and . data_dict : dict Dictionary containing the data to be passed to callbacks : dict Dictionary of the form { : }. The javascript library can use callbacks to talk to python. css_list : list of str List of strings containing either 1) URL to a CSS stylesheet or 2) CSS styles """ html_all = make_html(library_list, main_function, data_dict, css_list) for callback_id in callbacks.keys(): setup_comm_api(callback_id, callbacks[callback_id]) display(HTML(html_all)) ================================================ FILE: notebookjs/resources/CommAPI.js ================================================ const COMM_TYPES = { JUPYTER: 'JUPYTER', COLAB: 'COLAB' }; class CommAPI{ constructor(api_call_id, callback) { this.callback = callback; this.mode = null; if (window.Jupyter !== undefined) { this.mode = COMM_TYPES.JUPYTER; this.comm = window.Jupyter.notebook.kernel.comm_manager.new_comm(api_call_id, {}); this.comm.on_msg(msg => { const data = msg.content.data; callback(data); }); } else if (window.google !== undefined) { this.mode = COMM_TYPES.COLAB; this.comm = async function(msg){ const result = await google.colab.kernel.invokeFunction( api_call_id, [msg], // The argument {}); // kwargs callback(result.data['application/json']); }; } else { console.error(new Error("Cannot find Jupyter/Colab namespace from javascript")); } } call(msg) { if (this.comm){ if (this.mode === COMM_TYPES.JUPYTER){ this.comm.send(msg); } else if (this.mode === COMM_TYPES.COLAB){ this.comm(msg); } } } } ================================================ FILE: notebookjs/resources/template.html ================================================

================================================ FILE: setup.py ================================================ from setuptools import setup, find_packages with open("README.md", "r") as fh: long_description = fh.read() setup( name="notebookjs", version="0.1.4", author="Jorge Piazentin Ono, Juliana Freire, Claudio Silva", author_email="jorgehpo@nyu.edu", description="notebookJS library - Seamless JavaScript integration in Python Notebooks", long_description=long_description, long_description_content_type="text/markdown", url="https://github.com/jorgehpo/notebookJS", packages=find_packages(exclude=['resources']), include_package_data=True, license="MIT", classifiers=[ "Programming Language :: Python :: 3", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", ], python_requires='>=3.6', install_requires=[ "notebook" ] )