pax_global_header00006660000000000000000000000064150464126510014516gustar00rootroot0000000000000052 comment=ed500db7c41ab384032671eb303f78d276d76e3c pixl-webapp-2.0.3/000077500000000000000000000000001504641265100137505ustar00rootroot00000000000000pixl-webapp-2.0.3/README.md000066400000000000000000001162101504641265100152300ustar00rootroot00000000000000# Overview The **pixl-webapp** package is a client-side JavaScript framework, designed to be a starting point for a simple web application. It consists of a number of JavaScript classes, utility functions, and basic CSS elements (header, tabs, dialogs, progress bars, form elements, etc.). [jQuery](http://jquery.com/) is required for all features to work properly. It ships with a demo application, which you can see here: [Demo App](http://pixlcore.com/demos/pixl-webapp/demo/index.html) # Table of Contents | Document | Description | |---------------|-------------| | Main Docs | (You're reading them) | [OOP Docs](https://github.com/jhuckaby/pixl-webapp/blob/master/docs/oop.md) | Documentation on the object-oriented class generation system. | [XML Docs](https://github.com/jhuckaby/pixl-webapp/blob/master/docs/xml.md) | Documentation on the XML parser and serializer class. | [Tools Docs](https://github.com/jhuckaby/pixl-webapp/blob/master/docs/tools.md) | Documentation on the tools library (misc utility functions). | [Date/Time Docs](https://github.com/jhuckaby/pixl-webapp/blob/master/docs/datetime.md) | Documentation on the date/time utility library. # Usage You can use [npm](https://www.npmjs.com/) to install the module: ``` npm install pixl-webapp ``` Or just download the files from the [GitHub repo](https://github.com/jhuckaby/pixl-webapp). There is no installation script. This is basically just a collection of JavaScript files (and a CSS file) that you must include manually. It is important that you include the JavaScript files in the proper order. You can of course use tools such as [UglifyJS](https://www.npmjs.com/package/uglify-js) to compact them all together into a single blob for distribution. But for development, it is best to include them separately, in this order: ```html ``` ## File Overview Here is a quick overview of each file in the package: ### base.css This file contains all the CSS used by the framework. It contains some content from [HTML5 Boilerplate](http://html5boilerplate.com/), as well as CSS classes for the pages, tabs, dialogs and buttons. The base font family is Helvetica, falling back to generic `sans-serif`. The color scheme is mainly shades of light gray, with a light blue highlight. Here are all the blue theme colors used in the CSS, from darkest to lightest: ``` #3f7ed5, #5890db, #7cafda, #9ccffa ``` ### md5.js This is a 3rd party library ([BSD](https://en.wikipedia.org/wiki/BSD_licenses) licensed) that implements the [MD5](https://en.wikipedia.org/wiki/MD5) algorithm in JavaScript. This is used only for generating unique IDs, and constructing [Gravatar](https://en.gravatar.com/) image URLs. If you don't require either in your app, you can omit this file. Note that MD5 should not be used for any secure cryptography, as it now generally considered to be a weak algorithm. Here is the copyright snippet from the source file: ``` Version 2.2 Copyright (C) Paul Johnston 1999 - 2009 Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet Distributed under the BSD License See http://pajhome.org.uk/crypt/md5 for more info. ``` ### oop.js This is a simple object-oriented programming framework, offering "classes" in JavaScript. See the [Pages](#pages) section below for details on this. The file also comes with its own standalone docs: [OOP Documentation](https://github.com/jhuckaby/pixl-webapp/blob/master/docs/oop.md). ### xml.js The `xml.js` file provides a simple XML parser in pure JavaScript. This can be used to convert XML to a simple JavaScript object hash/array tree, for example if you are sending AJAX requests to a server API that returns XML. It also contains a number of static utility functions. The file comes with its own standalone docs: [XML Documentation](https://github.com/jhuckaby/pixl-webapp/blob/master/docs/xml.md). ### tools.js The `tools.js` file contains a number of misc. utility functions, from generating unique IDs, to manipulating strings, to measuring the browser window. The file comes with its own standalone docs: [Tools Documentation](https://github.com/jhuckaby/pixl-webapp/blob/master/docs/tools.md). ### datetime.js The `datetime.js` file contains a number of functions for dealing with dates and times. Specifically, parsing dates and constructing various localized date/time strings for display purposes. The file comes with its own standalone docs: [Date/Time Documentation](https://github.com/jhuckaby/pixl-webapp/blob/master/docs/datetime.md). ### page.js This is the virtual page navigation and management system in the web app framework. It manages a collection of virtual pages, provides a base class for them, and handles listening for URL hash changes and page state changes. See [Pages](#pages) below for details. ### dialog.js This file handles basic dialog display and rendering. See the [Dialogs](#dialogs) section below for details. ### base.js This file contains the base application object for your web app. It's a single, global `app` object that you can extend with your own variables and functions if you like. See [Main Application](#main-application) below for details. ## HTML Markup To get started, include the webapp CSS and JS from your HTML page: ```html ``` ### Header The page header section contains a custom logo, title, and widgets on the right side. It should live in your HTML `` element, and not inside any other containers: ```html
My Application
``` Please style the `#d_header_logo` element to contain your own app's logo image, using `background-image`. It should be 32x32 pixels (or 64x64 for retina). Enter your app's title in the `#d_header_title` element, optionally wrapped in a `` tag. The `#d_header_user_container` can contain a number of widgets (`#header_option`), such as login/logout buttons, user avatar, etc. These should be floated right, and separated by `#d_header_divider` elements. Example: ```html
  Logout
``` ### Main Content For the main content of the page, see the [Pages](#pages) and [Tabs](#tabs) sections below. ### Footer The page footer contains left and right aligned sections for you to add a copyright, version, or anything else you want. Example: ```html ``` ## Configuration The app framework requires a configuration object to be loaded into `window.config` and copied in `app.config`. This can be loaded however you like (inline script tag, etc.). An simple example is shown here (taken from the demo app): ```javascript window.config = app.config = { // Define all app's pages in 'Page' array Page: [ { ID: 'Home' }, { ID: 'MoreDemos' } ], // Which page to load by default (if not in URL hash) DefaultPage: 'Home' }; ``` The only required properties in the configuration object are the `Page` array, which contains objects for each of your pages (more on this below in [Pages](#pages)), and the `DefaultPage` string, which declares which page loads by default. Everything else is optional, and this structure can be extended for your own uses. ## Main Application Your main application object is located in the global scope under the name `app`. It is a plain object with variables and classes you can override, and of course add your own. You can use the built-in `extend()` method to add your own properties and methods, if you like. Example: ```javascript app.extend({ // This name will appear in the window title name: 'My App', // init() is called on page load init: function() { // initialize application // Setup page manager for tabs this.page_manager = new PageManager( config.Page ); // start monitoring URL hash changes for page transitions Nav.init(); } }); ``` The first thing you need to do is add a `name` property, set to the name of your application. This is used in a number of places (such as window titles). Also, an `init()` method, which is called when the DOM is ready. The only other requirement is the `init()` method, in which you need to construct a `PageManager()` instance, passing it your `Page` array from the configuration object, and assigning it to `this.page_manager` (must be exact). Then call `Nav.init()` to start the page navigation system. You can also add your own application startup tasks here. Feel free to extend this object with whatever properties or methods your app requires. ## Pages Each "page" in your web application is virtual. It's basically a DIV that is shown when the page is activated, hidden when deactivated, and a JavaScript class upon which methods are called when the page state changes (activated, deactivated, etc.). Each page has a unique ID, which must first be defined in your configuration: ```javascript { Page: [ { ID: 'Home' }, { ID: 'MoreDemos' } ], DefaultPage: 'Home' } ``` In this example your HTML should be setup like this: ```html
``` Finally, each virtual page in your app should inherit from the `Page` base class. It contains placeholders for all the methods you can override (described below), as well as a few utility methods for rendering tables and tabs. See the [OOP Documentation](https://github.com/jhuckaby/pixl-webapp/blob/master/docs/oop.md) for details on creating a subclass. Here is an example class, showing the bare minimum you'll need to add: ```javascript Class.subclass( Page, "Page.Home", { onInit: function() { // called once at page load var html = ''; // include initial HTML here, if you want this.div.html( html ); }, onActivate: function(args) { // page activation if (!args) args = {}; this.args = args; app.setWindowTitle('Home'); app.showTabBar(true); // activate page here (show live / updated content) var html = 'Hello there!'; this.div.html( html ); return true; }, onDeactivate: function() { // called when page is deactivated return true; } } ); ``` You have the choice of including your HTML markup in the `index.html` file, or building the HTML as a string in the `onInit()` method (called *only once*), or building the HTML as a string in the `onActivate()` method (called *every time* your page is activated). ### onInit The `onInit()` method is called on your page only once, at load time. This allows you to setup things like initial HTML markup (or you can just put this in the `index.html` file), and any other initialization tasks your page might need. A `div` property points to your page's DIV element (jQuery wrapped). The function takes no arguments, and there is no return value. ### onActivate The `onActivate()` method is called *every time* your page is activated. Your DIV is automatically shown, but you can use this method to update the page contents if you want. Your function may be passed an `args` object, if the URL hash contains a query string. For example: ``` http://myapp.com/#Home?foo=bar&baz=1234 ``` This URL would load the `Home` virtual page, and the `onActivate()` method would be passed an `args` object containing: ```javascript { foo: "bar", baz: 1234 } ``` Another typical thing to do in your `onActivate()` method is to call `app.setWindowTitle()` to set the browser window / tab title, and `app.showTabBar()` to show the tab bar: ```javascript app.setWindowTitle('Home'); app.showTabBar(true); ``` The window title will contain the page name, *and* your application name, taken from `app.name`. Showing the tab bar is typical for most web apps and pages, but there are exceptions. For example, a "login" page may not want to show the tab bar. See [Tabs](#tabs) below for more on this. Your `onActivate()` method *must* return either `true` or `false`. Returning `true` means that the page accepted the activation, and the app can proceed. Returning `false` means that something went wrong (error or other), and the page should *not* be activated. In this case the page remains hidden and the *previous* page's DIV is still displayed. ### onDeactivate The `onDeactivate()` method is called when your page is deactivated. Meaning, the user is navigating to another virtual page in the app. Your method is passed the ID of the new page being activated: ```javascript onDeactivate: function(new_id) { // called when page is deactivated return true; } ``` You can use this method to shut down or cleanup things happening in the page. For example: timers, IFRAMEs, or anything else that should be stopped. You may want to clear your entire DIV element (if your `onActivate()` redraws everything, for example). Your `onDeactivate()` method *must* return either `true` or `false`. Returning `true` means that the page accepted the deactivation, and the app can proceed. Returning `false` means that something went wrong (error or other), and the page should *not* be deactivated. In this case the current page remains displayed. ### Accessing Pages You can access any page by looking it up by its ID. This is done by calling the `page_manager` object in the `app` global. It provides a `find()` method that accepts an ID string. Example: ```javascript var page = app.page_manager.find('Home'); ``` There is a global shortcut for this, available by calling `$P()`. It also accepts a Page ID, but if omitted, it defaults to the current page. This is very useful for getting back into the context of the page from an inline HTML callback. ```javascript var page = $P('Home'); var cur_page = $P(); ``` ## Navigation The built-in navigation system listens for URL hash change events, and switches virtual pages based on the anchor tag present in the URL. Example: ``` http://myapp.com/#Home ``` This would activate the virtual page with ID `Home`. It also supports URL query string params after the page ID, which are passed to the page class `onActivate()` method. Example: ``` http://myapp.com/#Home?foo=bar&baz=1234 ``` So a typical way of triggering a page change event is to simply redirect the browser to a new hash anchor tag. However, some convenience methods are also provided: ### Nav.go This forces a page change event, and accepts a new anchor tag, optionally with a query string at the end. ```javascript Nav.go('SomePage'); Nav.go('SomePage?foo=bar'); ``` ### Nav.refresh This refreshes the current page (calls `onDeactivate()`, then `onActivate()`). ```javascript Nav.refresh(); ``` ### Nav.prev This jumps back to the previous page. ```javascript Nav.prev(); ``` ### Nav.currentAnchor This returns the name of the current anchor, including query string if present. ```javascript var loc = Nav.currentAnchor(); ``` ## Tabs Tabs are optional, and if present, work hand-in-hand with the page system. If you create tab HTML markup as shown below, they will automatically be attached to their respective pages, and handle clicks, and changing state for you. ```html ``` The `tab_bar` DIV is initially hidden, as you may have a login page that shouldn't show any tabs. Each individual page can hide or show the tab bar by calling `app.showTabBar();` and passing `true` or `false`. If you have no login page, or you are sure the tab bar will *always* be shown, you can omit the `display:none`, and never have to call `app.showTabBar(true);`. Each tab should have a DIV with an ID following this format: `tab_ID`, and should the CSS classes `tab inactive` applied. The page navigation system will handle activating tabs when their respective pages are shown. ## Forms The library comes with a simple HTML form builder system, which is really just a table structure with two columns: Form element labels on the left, and the form elements themselves on the right. Functions are provided to build up the table HTML row by row, and can generate captions and spacers. Start with a simple table declaration with your own styles added (margins, etc.), and then call these functions: | Function Name | Description | |---------------|-------------| | `get_form_table_row()` | Returns HTML for one form element row (2 arguments, label and content). | | `get_form_table_caption()` | Returns HTML for a caption row (one argument, the caption text). | | `get_form_table_spacer()` | Returns HTML for a spacer row (no arguments). | Here is an example: ```javascript var html = ''; // Name (Text Field) html += get_form_table_row( 'Name', '' ); html += get_form_table_caption( "Enter a title for the vegetable, which will be displayed on the main salad." ); html += get_form_table_spacer(); // Quality (Checkbox) html += get_form_table_row( 'Quality', '' ); html += get_form_table_caption( "Select whether the vegetable should be farm fresh or not." ); html += get_form_table_spacer(); html += '
'; $('#my_element').html( html ); ``` If you would prefer to simply create the HTML markup yourself, and not use the functions, that is fine. For form labels, use a `` element with CSS class `table_label`, and for the form elements, use a `` with class `table_value`. For captions, simply use a `
` set to class `caption`. ## Tables The library provides CSS styles and JavaScript functions for creating data tables, optionally with pagination. The HTML markup is simple; just use CSS class `data_table`, then provide your headers in `` elements, and your data in `` elements. HTML example of a simple table: ```html
Username Full Name Status Created Modified
jhuckaby Joseph Huckaby Administrator Jan 3, 2014 Oct 5, 2015
fsmith Fred Smith Standard User Oct 5, 2015 Oct 5, 2015
``` In addition to the CSS, a pagination system is provided, to assist you with generating tables from a large dataset that have pagination links built-in. The function to call is `this.getPaginatedTable()` and is available in the `Page` base class. It returns the final rendered HTML for the page. To use it, you'll need to provide an object containing the following pieces of information: | Property Name | Description | |---------------|-------------| | `cols` | An array of header column labels, displayed in bold at the top of the table. | | `rows` | The current page of data (array). Each element is passed to your callback for each visible row of the table. | | `data_type` | A string identifying the type of data, e.g. `user`. Used in strings such as `No users found`. | | `offset` | The current offset into the full dataset. This should be `0` for the first page. | | `limit` | The number of items shown on each page. This should equal the length of the `rows` array. | | `total` | The total number of items in the dataset. This is used to render proper pagination links. | | `callback` | A user callback which is fired for each row, so you can provide your own `` elements. | Here is an example: ```javascript var cols = [ 'Name', 'Color', 'Size', 'Quantity', 'Price', 'Created' ]; var rows = [ { name: 'Celery', color: 'Green', size: '1ft', quantity: 450, price: '$2.75', created: 1442984544 }, { name: 'Beets', color: 'Purple', size: '4in', quantity: 30, price: '$3.50', created: 1442380043 }, { name: 'Lettuce', color: 'Green', size: '1ft', quantity: 1000, price: '$2.50', created: 1442264863 }, { name: 'Carrots', color: 'Orange', size: '8in', quantity: 60, price: '$4.00', created: 1442084869 }, { name: 'Rhubarb', color: 'Purple', size: '2ft', quantity: 190, price: '$3.99', created: 1441724876 } ]; var html = this.getPaginatedTable({ cols: cols, rows: rows, data_type: 'vegetable', offset: 0, limit: 5, total: 10, callback: function(row, idx) { return [ row.name, row.color, row.size, commify( row.quantity ), row.price, get_nice_date_time( row.created ) ]; } }); ``` So the idea here is, we have a dataset of 10 items total, but we are only showing 5 items per page. So we have an array of 5 items in `rows`, but we're specifying the `total` as 10, and `offset` as 0 (first page). Based on this, the `getPaginatedTable()` will generate the proper pagination links. Your callback is fired once per row, and is passed the current row (array element from `rows`), and the localized index in `idx` (starts from `0` regardless of `offset`). Your function should return an array of values which should match up with the `cols`, and each will be stuffed into a `` element. The pagination links work by constructing self-referencing URL to the current page, but adding or modifying an `offset` query parameter, set to the appropriate value. For example, in this case there would be a `Next Page` link, which would be set to: ``` http://myapp.com/#Home?foo=bar&baz=1234&offset=5 ``` Since the `limit` is set to 5 items per page, and `offset` starts at `0`, then the next page (page 2) will be at offset `5`. This link is simply a hashtag anchor tag, which doesn't reload the browser page, but will instead be caught by the navigation system, and call your page's `onDeactivate()` then its `onActivate()` with the new values. It is up to your page code to redraw the table with the new data chunk and new `offset` value. Instead of generating hashtag anchor links, you can optionally provide a custom JavaScript function in a `pagination_link` property, which will be written into the HTML as an `onMouseUp` handler on each link, and called instead of a standard link. Note that it must be a string and globally accessible, so remember the `$P()` shortcut to get access to the current page. Example: ```javascript pagination_link: '$P().tableNavClick' ``` In this case your custom page `tableNavClick()` method will be called for each table pagination click, and passed the new offset value. ## Dialogs The library comes with a simple "dialog" system, which is just an auto-centered floating DIV with a drop shadow, and an invisible overlay making it behave like a modal window. To show a dialog, call `app.showDialog()` and pass in a title, HTML content, and HTML for buttons at the bottom. The DIV will automatically size and position itself to fit your content. Example: ```javascript var html = 'This is the main dialog content.'; var buttons = '
Close
'; app.showDialog( "My Dialog", html, buttons ); ``` As you can see in the button markup above, you can call `app.hideDialog()` to close the dialog box. This destroys the content, so make sure you grab any user input before calling it, if applicable. If you need a quick, simple "confirmation" style dialog, meaning exactly two buttons ("Cancel" and a custom button), there is a convenience method called `app.confirm()` which takes care of some of the dialog logistics for you. It allows you to provide a callback function, which is called with either `true` or `false` depending on which button was clicked. Example: ```javascript app.confirm( 'Add Vegetable', "Are you sure you want to add the vegetable celery?", "OK", function(result) { if (result) { // User clicked "OK", close dialog ourselves app.hideDialog(); } else { // User clicked "Cancel", dialog is closed automatically } } ); ``` As you can see, this method takes 4 arguments: The dialog title, HTML content, button title, and a callback function that accepts a result (Boolean). The result will be `true` if the user clicked your custom button ("OK" in this case), or `false` if the user clicked "Cancel". Note that it is up to you to hide the dialog when the user clicks your button (`true` result). This allows you to "interrupt" the closing of the dialog and show an error notification or something. There is no way to intercept the cancel button, however. ## Progress To show a simple "progress" style dialog, which has a title and graphical progress bar, you can call `app.showProgress()` and pass in a counter (floating point decimal between `0.0` and `1.0`), and a title string. Example: ```javascript app.showProgress( 0.5, "Processing files..." ); ``` This would show the progress bar at half width (50%). You can call the same function multiple times to update the bar width. You can omit the title for subsequent calls (unless you want to change it too). Example: ```javascript app.showProgress( 0.7 ); app.showProgress( 0.8 ); app.showProgress( 0.9, "Finishing up..." ); ``` If you pass in `1.0` as the counter, the progress bar is displayed at full width but partial transparency, denoting an "indeterminate" length. So if you don't know the length of a job, and just want to show a generic progress bar, pass in `1.0` as the counter. Call `app.hideProgress()` to hide the progress bar. ## Notification Notification messages are shown in a fixed bar at the top of the screen, regardless of the scroll position. Messages can have one of three styles (highlight color), and custom HTML. They can remain in place until clicked, or disappear after N seconds. Only one notification may be shown at a time. To use the notification system in your app, make sure this markup is in your main HTML page: ```html ``` Then, call `app.showMessage()` and pass in a style name (see below), a text or HTML message string, and optionally a lifetime (number of seconds before it auto-hides). Here are the three supported message styles: | Style | Description | |-------|-------------| | `success` | Highlighted in green, used for successful completion messages. By default, these automatically hide after 8 seconds. | | `warning` | Highlighted in yellow, used for warning messages. By default these are persistent until user click. | | `error` | Highlighted in red, used for error messages. By default these are persistent until user click. | Example use: ```javascript app.showMessage( 'success', "The user was saved successfully.", 8 ); ``` To programmatically hide the notification message, call `app.hideMessage()`. You can optionally pass in a number of milliseconds to animate the hide, if you want (uses jQuery's animation system). For form field validation errors, you can call `app.badField()` and pass in the DOM ID (or CSS selector) of the form field containing an invalid value, and an error message. The form field will be focused, highlighted in red (background color, works well for text fields), and an error message notification will be displayed. To clear an error, call `app.clearError()`. ```javascript app.badField( '#my_username', "Usernames must contain alphanumeric characters only." ); ``` If you include [Font Awesome Icons](https://fortawesome.github.io/Font-Awesome/icons/) in your HTML page, the notification messages will also contain an appropriate icon matching the style: ```html ``` ## API The library contains a simple JSON REST API wrapper built around jQuery's [$.ajax()](http://api.jquery.com/jquery.ajax/) call, designed to support JSON API backends. API calls can be sent to the server using HTTP GET or POST, and JSON responses are parsed for you. Errors are handled automatically, but you can specify custom handlers as well. By default, API calls are sent to the same hostname as the one hosting the page, using the URI `/api/COMMAND`, where `COMMAND` is a custom command passed in, e.g. `/api/user_login`. You can change the base API URL by calling `app.setAPIBaseURL()`. Example: ```javascript app.setAPIBaseURL( '/myapp/API.php' ); app.setAPIBaseURL( 'http://myotherserver.com/myapp/API.php' ); ``` To send an API call, use `app.api.get()` or `app.api.post()` depending on whether you want an HTTP GET or HTTP POST. Pass in a command name (is appended to the base URI), a params object (serialized to JSON or a query string), and a callback. Example: ```javascript app.api.post( 'user_login', { username: 'joe', password: '12345' }, function(resp) { // successfully logged user in // 'resp' is response JSON from server } ); ``` So this example would send an HTTP POST to `/api/user_login`, and serialize the params into JSON, sent as the body of the post. The response is expected to be in JSON, and is parsed and sent to the callback. Sending an HTTP GET is similar. Just call `app.api.get()` instead, and note that the params object is serialized into a URL query string, not a JSON POST body. The response and callback are handled the same. Example: ```javascript app.api.get( 'user_get_info', { username: 'joe' }, function(resp) { // successfully fetched user info // 'resp' is response JSON from server } ); ``` API errors are handled automatically by default, meaning your callback is *not* fired, and instead an error notification is displayed. This includes HTTP related errors, as well as errors specified inside the response JSON. The API system expects the response to include a `code` property, and if this is non-zero, it is considered an error, and it looks for a `description` property for the error message. To set a custom error handler, specify a second callback after the first one: ```javascript app.api.post( 'user_login', { username: 'joe', password: '12345' }, function(resp) { // successfully logged user in // 'resp' is response JSON from server }, function(err) { // an error occurred // see err.code and err.description } ); ``` ### User Login When implementing your own user login system, note that the API calls will automatically include a Session ID if you store it in [localStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) using key `session_id`. It will be sent to the server along with all API calls as a custom HTTP request header `X-Session-ID`. Example: ```javascript localStorage['session_id'] = "d2691d948880cea8426078b0879ce733"; ``` ## Misc ### Page Resize If your pages need to take special action when the browser is resized, you can define an `onResize()` method in your page classes. This is fired for every browser resize event, and your method is passed an object containing the new inner window `width` and `height` in pixels. Example: ```javascript onResize: function(size) { // window was resized // see 'size.width' and 'size.height' } ``` ### Page Unload If you need to intercept the user navigating away from the app entirely or closing the browser tab/window, you can define an `onBeforeUnload()` method in your page classes. This method should return a text message to be displayed, if you want to intercept the event and alert the user, or return `false` to allow the app to shut down without any intervention. Example: ```javascript onBeforeUnload: function() { // if dirty, warn user before navigating away from app if (this.dirty) return "There are unsaved changes in this document. If you leave now they will be abandoned."; else return false; } ``` This example assumes your page class has a `dirty` property, which is `true` when the user has made changes which are unsaved. Please note that alerting the user in this way is very jarring and disruptive, and should *only* be done when there really is a good reason to keep the user on the page, i.e. unsaved changes that will be lost forever. ## Additional UI Features ### Side Tabs The webapp framework comes with a system for handling "side tabs", a.k.a. tabs within tabs. These exist inside the context of a single page, but allow you to further subdivide your page into sub-pages, controlled by a set of tabs displayed on the left side. This requires you to design your page class in a specific way. Namely, you must generate your HTML on-the-fly when `onActivate()` is called. The idea is that the side tabs and sub-page are redrawn every time. The sub-page is chosen via a hash anchor query string parameter named `sub`. Example URL: ``` http://myapp.com/#MyPage?sub=form ``` This will activate the `MyPage` page, and pass in `{sub:"form"}` as the `args` object. It is up to your `onActivate()` method to react to this, by rendering a different sub-page for the value of `args.sub`. For example, you can call a method based on the value like this: ```javascript onActivate: function(args) { // page activation this.args = args; // jump to sub-page based on has query this['gosub_'+args.sub](args); return true; } ``` So in this example you'd need to provide a method for each sub-page, i.e. `gosub_form()`. Then, as you are building the HTML to render the sub-page, call `this.getSidebarTabs()` to render the sidebar tabs. Pass in the current `args.sub` value, and an array describing the side tabs. Example: ```javascript var html = ''; html += this.getSidebarTabs( args.sub, [ ['form', "Form Demo"], ['table', "Table Demo"] ] ); html += "(Sub-page content here)"; html += '
'; // close sidebar tabs this.div.html( html ); ``` This would render two side tabs, one with ID `form` and another with ID `table`. The value of the `args.sub` must match one of these for the correct tab to be highlighted. Clicks are handled automatically -- the user is navigated back to your page, but with a different value for the `sub` parameter. ### Fieldsets HTML `
` tags are styled with a light gray vertical gradient, gray border, and a legend. Inside the fieldset, feel free to use DIVs with class `info_label` for labels, and `info_value` for values. Example: ```html
Some Info
Section 1
Lorem ipsum dolor sit amet.
``` See the demo application HTML source for a better example of this. ### Buttons Buttons are provided in the webapp library by setting a assigning CSS class `button` to a `
` element. They come with rollover and click effects, and are 111px wide and 24px tall (including padding and border) by default. Example: ```html
Something
``` There are a few variants available: | CSS Classes | Description | |---------------|-------------| | `button mini` | Smaller font, only 101px wide by 21px tall. | `button ellip` | Add ellipsis text overflow. | `button left` | Float button to the left. | `button right` | Float button to the right. | `button center` | Horizontally center the button. | `button disabled` | Grayed out, no hover or click effects. ### Subtitles Subtitles are designed to be displayed at the top of your page content, usually in conjunction with [Side Tabs](#side-tabs), or just above a [Table](#tables). They are styled in 16px bold text, with a light gray bottom border that extends the full width of the container. To use, create a `
` and apply CSS class `subtitle`. Example: ```html
New Vegetable
``` You can also include subtitle "widgets" which float on the right side of the subtitle. These are styled in smaller text (16px), and go *inside* of the subtitle DIV. These should be inner `
` elements with CSS class `subtitle_widget`. Also, don't forget to clear the float at the end. Example: ```html
New Vegetable
Widget 1
Widget 2
``` ### Color Labels Color labels are designed to be applied to `` elements, and provide an opaque colored background, with white shadowed bold text. These are useful for decorating one-word table elements, such as account status (e.g. "Active", "Suspended"). To use, create a `` element apply CSS class `color_label` plus your desired color. Example: ```html Active ``` The colors available are `green`, `yellow`, `red`, `purple` and `gray`. To add more, just set a custom `background-color` CSS property. ### Avatars For displaying user avatars, a function is provided in the `app` global to generate a URL to the [Gravatar](https://en.gravatar.com/) service, using an e-mail address. Basically it just constructs a URL to an avatar image for the user, if one exists, at a specified pixel size, and falls back to a generic user icon. Example: ```javascript var url = app.getUserAvatarURL( 'email@server.com', 64 ); ``` This would generate a URL to a 64x64 user icon image. If the user's e-mail address is registered at [Gravatar.com](https://en.gravatar.com/), it will be their custom avatar icon. If not, it will be a generic icon. This function requires the [md5.js](#md5js) library, as the e-mail address is hashed using MD5 on the URL. # License The MIT License (MIT) Copyright (c) 2013 - 2015 Joseph Huckaby 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. pixl-webapp-2.0.3/css/000077500000000000000000000000001504641265100145405ustar00rootroot00000000000000pixl-webapp-2.0.3/css/base.css000066400000000000000000000600241504641265100161660ustar00rootroot00000000000000/* Base styles for Web App Framework v2 */ /* Author: Joseph Huckaby */ /* Lots of content from: http://html5boilerplate.com/ */ /* Theme colors: #3f7ed5, #5890db, #7cafda, #9ccffa */ /* Lato Font: Copyright (c) 2010-2011 by tyPoland Lukasz Dziedzic (team@latofonts.com) with Reserved Font Name "Lato". Licensed under the SIL Open Font License, Version 1.1. */ @font-face { font-family: 'Lato'; font-style: normal; font-weight: 400; src: local('Lato Regular'), local('Lato-Regular'), url('../fonts/lato-v11-latin-regular.woff2') format('woff2'), /* Chrome 26+, Opera 23+ */ url('../fonts/lato-v11-latin-regular.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ } @font-face { font-family: 'Lato'; font-style: normal; font-weight: 700; src: local('Lato Bold'), local('Lato-Bold'), url('../fonts/lato-v11-latin-700.woff2') format('woff2'), /* Chrome 26+, Opera 23+ */ url('../fonts/lato-v11-latin-700.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ } @font-face { font-family: 'LatoBold'; font-style: normal; font-weight: normal; src: local('Lato Bold'), local('Lato-Bold'), url('../fonts/lato-v11-latin-700.woff2') format('woff2'), /* Chrome 26+, Opera 23+ */ url('../fonts/lato-v11-latin-700.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ } /* Light Theme by default */ body { --body-background-color: rgb(255, 255, 255); --background-color: rgb(255, 255, 255); --border-color: rgb(210, 210, 210); --dialog-background-color: rgb(255, 255, 255); --box-background-color: rgb(248, 248, 248); --header-text-color: rgb(150, 150, 150); --body-text-color: rgb(84, 84, 84); --label-color: rgb(120, 120, 120); --highlight-color: rgb(88, 144, 219); } html, body, div, span, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, abbr, address, cite, code, del, dfn, em, img, ins, kbd, q, samp, small, strong, sub, sup, var, b, i, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, figure, footer, header, hgroup, menu, nav, section, menu, time, mark, audio, video { margin:0; padding:0; border:0; outline:0; font-size:100%; vertical-align:baseline; background:transparent; } article, aside, figure, footer, header, hgroup, nav, section { display:block; } nav ul { list-style:none; } blockquote, q { quotes:none; } blockquote:before, blockquote:after, q:before, q:after { content:''; content:none; } a { margin:0; padding:0; font-size:100%; vertical-align:baseline; background:transparent; } del { text-decoration: line-through; } table { border-collapse:collapse; border-spacing:0; } hr { display:block; height:1px; border:0; border-top:1px solid var(--border-color); margin:1em 0; padding:0; } input, select { vertical-align:middle; } body { font:12px sans-serif; line-height:1.22; } table { font-size:inherit; font:100%; } select, input, textarea { font:99% sans-serif; } textarea { outline: 0; } pre, code, kbd, samp { font-family: monospace, sans-serif; } body, select, input, textarea { color: var(--body-text-color); } h1,h2,h3,h4,h5,h6 { font-weight: bold; text-rendering: optimizeLegibility; } a:hover, a:active { outline: none; } a, a:active, a:visited, .link { color: var(--highlight-color); } a:hover, .link:hover { color: var(--body-text-color); } .link { text-decoration: underline; cursor: pointer; } ul { margin-left:30px; } ol { margin-left:30px; list-style-type: decimal; } small { font-size:85%; } strong, th { font-weight: bold; } td, td img { vertical-align: middle; } sub { vertical-align: sub; font-size: smaller; } sup { vertical-align: super; font-size: smaller; } pre { padding: 15px; white-space: pre; /* CSS2 */ white-space: pre-wrap; /* CSS 2.1 */ word-wrap: break-word; /* IE */ } input[type="radio"] { vertical-align: text-bottom; } input[type="checkbox"] { vertical-align: bottom; } .ie6 input { vertical-align: text-bottom; } label, input[type=button], input[type=submit], button { cursor: pointer; } label { padding-left: 2px; font-weight: bold; color: var(--label-color); font-size: 13px; user-select: none; -moz-user-select: none; -webkit-user-select: none; } label:hover { text-decoration: underline; } input[type="text"] { outline: 0; } input[type="search"] { outline: 0; } input[type="password"] { outline: 0; } button { width: auto; overflow: visible; } .ie7 img { -ms-interpolation-mode: bicubic; } .ir { display:block; text-indent:-999em; overflow:hidden; background-repeat: no-repeat; } .hidden { display:none; visibility:hidden; } .visuallyhidden { position:absolute !important; clip: rect(1px 1px 1px 1px); /* IE6, IE7 */ clip: rect(1px, 1px, 1px, 1px); } .invisible { visibility: hidden; } .clear { clear: both } .left { float: left; } .right { float: right; } .clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; } .clearfix { display: inline-block; } * html .clearfix { height: 1%; } /* Hides from IE-mac \*/ .clearfix { display: block; } @media print { * { background: transparent !important; color: #444 !important; text-shadow: none; } a, a:visited { color: #444 !important; text-decoration: underline; } a:after { content: " (" attr(href) ")"; } abbr:after { content: " (" attr(title) ")"; } .ir a:after { content: ""; } /* Don't show links for images */ pre, blockquote { border: 1px solid #999; page-break-inside: avoid; } img { page-break-inside: avoid; } @page { margin: 0.5cm; } p, h2, h3 { orphans: 3; widows: 3; } h2, h3{ page-break-after: avoid; } } @media screen and (max-device-width: 480px) { html { -webkit-text-size-adjust:none; -ms-text-size-adjust:none; } } /* Dialog */ #dialog_overlay { position: fixed; z-index:10001; top: 0px; left: 0px; background-color: var(--background-color); height:100%; width:100%; } #dialog_container { position: fixed; z-index:10002; } #dialog_main { text-align: left; border: none; padding: 0px; background: transparent; box-shadow: none; } div.dialog_subtitle { margin-bottom: 10px; text-align: center; font-size: 14px; font-weight: bold; } div.confirm_container { width: 450px; font-size: 13px; color: var(--body-text-color); } /* Misc */ .loading { background-image: url(../images/loading.gif); background-repeat: no-repeat; background-position: center center; min-width: 32px; min-height: 32px; } .invalid { background-color: #fbb; } .strong { font-weight: bold; } body { font-family: "Lato", helvetica, sans-serif; margin: 0; padding: 0; background: var(--body-background-color); } input { font-family: "Lato", helvetica, sans-serif; } .container { position: relative; width: 90%; margin: 0 auto 0 auto; } /* Header */ #d_header { position: relative; margin-top: 5px; margin-bottom: 5px; height: 40px; background: transparent; /* background: linear-gradient(to top, #e8e8e8 0%, #f8f8f8 100%); */ /* box-shadow: rgba(0,0,0,0.1) 0px 1px 1px; */ } #d_header_logo { height: 40px; } #d_header_title { margin-left: 4px; height: 40px; line-height: 40px; font-size: 22px; color: var(--header-text-color); opacity: 0.9; cursor: default; /* text-shadow: #fff 1px 1px; */ /* text-shadow: 1px 1px white, -1px -1px #444; */ letter-spacing: -1px; } #d_header_user_bar { font-size: 12px; font-weight: bold; color: var(--header-text-color); /* text-shadow: #fff 1px 1px; */ text-align: right; height: 40px; line-height: 40px; cursor: pointer; padding-left: 40px; /* room for avatar as bkgnd */ background-repeat: no-repeat; background-position: 0px center; background-size: 32px 32px; } #d_header_user_bar:hover { color: var(--highlight-color); } .header_option { font-size: 12px; font-weight: bold; color: var(--header-text-color); /* text-shadow: #fff 1px 1px; */ text-align: right; height: 40px; line-height: 40px; cursor: pointer; /* padding-left: 26px; background-repeat: no-repeat; background-position: 0px center; */ } .header_option.logout { margin-right:2px; } .header_option:hover { color: var(--highlight-color); } #d_header_divider, .header_divider { height: 32px; width: 0px; margin-left: 9px; margin-right: 7px; margin-top: 4px; border-left: 1px solid var(--border-color); border-right: 1px solid var(--background-color); } /* Tabs */ .tab_bar { position: relative; height: 27px; top: 1px; /* covers border of main box below tabs */ z-index: 2; overflow: hidden; } .side_tab_bar { width: 150px; height: 100%; border-right: 1px solid var(--border-color); } .tab { float: left; margin-left: 10px; padding:0; height: 27px; background-repeat: repeat-x; border-left: 1px solid var(--border-color); border-top: 1px solid var(--border-color); border-right: 1px solid var(--border-color); border-top-left-radius: 4px; border-top-right-radius: 4px; -moz-border-radius-topleft: 4px; -moz-border-radius-topright: 4px; -webkit-border-top-left-radius: 4px; -webkit-border-top-right-radius: 4px; } .tab.side { position: relative; left: 1px; float: right; clear: both; margin-top: 10px; border-right: none; border-left: 1px solid var(--border-color); border-top: 1px solid var(--border-color); border-bottom: 1px solid var(--border-color); border-bottom-left-radius: 4px; border-top-right-radius: 0px; -moz-border-radius-bottomleft: 4px; -moz-border-radius-topright: 0px; -webkit-border-bottom-left-radius: 4px; -webkit-border-top-right-radius: 0px; } .tab.side.inactive { left: 0px; } .tab.side.active, .tab.side.active:hover { box-shadow: none; } .tab .content { display: inline-block; height: 27px; line-height: 27px; margin-left: 10px; margin-right: 10px; font-size: 13px; font-weight: bold; /* text-shadow: #fff 1px 1px; */ position: relative; z-index: 2; max-width: 200px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; user-select: none; -moz-user-select: none; -webkit-user-select: none; } /* .tab.icononly .content { margin-left:1px; margin-right:3px; } */ .tab .content.icon { padding-left: 21px; background-position: 0px center; background-repeat: no-repeat; } /* .tab.icononly .content.icon { background-position: 5px 4px; } */ .tab .content .badge { position: relative; top: -2px; margin-left: 4px; margin-right: 0px; padding-left: 4px; padding-right: 4px; background-color: #f00; border-radius: 50%; color: #fff; font-size: 11px; font-weight: bold; /* text-shadow: none; */ /* text-shadow: rgba(0,0,0,0.25) 1px 1px; */ } .tab.active { background: var(--background-color); /* box-shadow: rgba(0,0,0,0.1) 2px 2px 3px; */ cursor: default; } .tab.active .content { color: var(--highlight-color); /* text-shadow: #7D571C 0px -1px; */ } .tab.inactive { background: var(--box-background-color); /* background: linear-gradient(to top, rgba(232, 232, 232, 1.0) 0%, rgba(255, 255, 255, 1.0) 100%); */ cursor: pointer; height: 25px; border-bottom: 1px solid var(--border-color); } .tab.side.inactive { height: 27px; border-bottom: 1px solid var(--border-color); } .tab.inactive:hover .content { color: var(--highlight-color); } .tab.inactive .content { color: var(--label-color); } .tab_widget { float: right; padding: 0; height: 27px; line-height: 27px; margin-left: 15px; margin-right: 2px; font-size: 12px; font-weight: bold; color: var(--label-color); /* text-shadow: #fff 1px 1px; */ position: relative; z-index: 2; } /* Main content area below tabs */ .master_content_container { margin-right: 0px; margin-left: 0px; } .main { background: var(--background-color); border: 1px solid var(--border-color); border-radius: 2px; padding: 10px; min-height: 400px; overflow: hidden; /* box-shadow: rgba(0,0,0,0.2) 2px 2px 3px; */ } .main > div { min-height: 400px; } /* Subtitle */ div.subtitle { height: 20px; font-size: 16px; font-weight: bold; line-height: 20px; color: var(--header-text-color); /* color: #3f7ed5; */ /* text-shadow: 0px 1px 0px white; */ padding-left: 0px; padding-right: 3px; margin-bottom: 10px; border-bottom: 1px solid var(--border-color); } div.subtitle_widget { float: right; height: 20px; font-size: 12px; line-height: 20px; margin-left: 10px; margin-right: 0px; padding-left: 10px; /* text-shadow: 0px 1px 0px white; */ color: var(--header-text-color); font-weight: normal; } /* Fieldsets and labels */ fieldset { background: var(--box-background-color); /* background: linear-gradient(to top, #efefef 0%, #fff 100%); */ border:1px solid var(--border-color); border-radius: 2px; padding:4px 8px 8px 8px; /* box-shadow: rgba(0,0,0,0.1) 3px 3px 4px; */ } legend { padding-left: 5px; padding-right: 5px; font-size: 13px; cursor: default; font-weight: bold; color: var(--label-color); } .label { font-weight: bold; color: var(--highlight-color); } /* Data Table */ .data_table tr th { font-size: 13px; height: 20px; line-height: 20px; cursor: default; font-weight: bold; text-align: left; padding-left: 5px; padding-right: 10px; /* color:#1840F0; */ color: var(--label-color); border-top: 1px solid var(--border-color); background: var(--box-background-color); /* background: linear-gradient(to top, #ddd 0%, #fff 100%); */ } .data_table tr td { padding-left: 5px; padding-right: 10px; padding-top: 4px; padding-bottom: 4px; border-bottom: 1px solid var(--border-color); background: var(--background-color); /* background: linear-gradient(to top, #efefef 0%, #fff 100%); */ } .data_table tr.highlight td { } .data_table.extra_padding tr td { padding-top: 10px; padding-bottom: 10px; } div.td_big { line-height: 24px; font-size: 13px; font-weight: bold; } div.ellip { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } /* Buttons */ .button { width: 100px; height: 18px; padding: 4px 5px 1px 5px; background: var(--box-background-color); /* background: linear-gradient(to top, #e8e8e8 0%, #fff 100%); */ border-left: 1px solid var(--border-color); border-top: 1px solid var(--border-color); border-right: 1px solid var(--border-color); border-bottom: 1px solid var(--border-color); cursor: pointer; font-size: 10pt; font-weight: bold; /* text-shadow: rgba(255, 255, 255, 0.5) 1px 1px; */ text-align: center; color: var(--body-text-color); border-radius: 5px; /* box-shadow: rgba(0,0,0,0.1) 1px 1px 1px, rgba(255,255,255,0.5) -1px -1px 0px; */ user-select: none; -moz-user-select: none; -webkit-user-select: none; } .button.mini { width: 90px; height: 15px; font-size: 9pt; border-radius: 4px; } .button.center { margin-left: auto; margin-right: auto; } .button:hover, .button.hover { color: var(--highlight-color); } .button.disabled:hover, .button.disabled.hover { color: var(--body-text-color); } .button:active, .button.active { padding: 5px 4px 0px 6px; text-shadow: none; color: var(--highlight-color); background: var(--background-color); } .button.disabled:active, .button.disabled.active { padding: 4px 5px 1px 5px; background: var(--box-background-color); } /* Menus */ select { color: var(--body-text-color); border: 1px solid var(--border-color); outline: 0; font-size: 14px; } select.small { font-size: 12px; } /* Message Box */ div.message { position: fixed; box-sizing: border-box; left: 0px; top: 0; width: 100%; z-index: 10003; cursor: pointer; overflow: hidden; border-style: solid; border-width: 1px; } div.message.inline { position: relative; left: 0px; cursor: default; z-index: 0; } div.message_inner { font-size: 10pt; font-weight: bold; cursor: pointer; padding: 12px 20px 12px 20px; } div.message.inline > div.message_inner { cursor: default; } div.message.success { color:#0a0; background-color:#cfc; } div.message.warning { color:#990; background-color:#ffc; } div.message.error { color:#c00; background-color:#fee; } /* Footer */ #d_footer { margin: 7px 0px 10px 0px; font-size: 11px; cursor: default; color: var(--header-text-color); } #d_footer a { color: var(--label-color); } #d_footer a:hover { color: var(--body-text-color); } /* Misc */ .table_label { font-size: 14px; font-weight: bold; color: var(--highlight-color); padding-right: 10px; } .caption { font-size: 11px; cursor: default; color: var(--label-color); } div.caption { margin: 2px; } .table_spacer { height:1px; background:var(--border-color); margin-top:12px; margin-bottom:12px; } .table_spacer.short { margin-top:4px; margin-bottom:4px; } .table_spacer.transparent { background:transparent; } .pagination { margin-top:10px; margin-bottom:0px; font-weight:bold; color:var(--label-color); } .pagination.hide { display:none; } .h1 { font-size:20px; font-weight:bold; color:var(--highlight-color); } .disabled { opacity: 0.5; cursor: default; } label.disabled:hover { text-decoration: none; } @keyframes invalidFade { 0% { background-color: #fbb; } 50% { background-color: #fbb; } 100% { background-color: #fff; } } .invalid { animation-name: invalidFade; animation-duration: 4s; animation-fill-mode: both; } h1, h2, div.h1, div.h2 { color: var(--header-text-color); font-weight: bold; /* text-shadow: white 0px 1px; */ padding-left: 4px; /* background: #eee; background: linear-gradient(to top, #eee 0%, #fff 100%); */ } h1, div.h1 { font-size: 15px; line-height: 30px; } h2, div.h2 { font-size: 13px; line-height: 20px; } textarea { font-family: courier,monospace; /* resize: none; */ box-sizing: border-box; /* cursor: text; */ outline: 0; border: 1px solid var(--border-color); tab-size: 4; } .inline_dialog_container { width: 500px; margin-left: auto; margin-right: auto; } td.table_value > div > input[type="text"], td.table_value > div > input[type="password"] { font-size:14px; } td.table_value > div > textarea { border: 1px solid var(--border-color); color: var(--body-text-color); font-family: monospace; font-size: 12px; } td.table_value > div > label { line-height:18px; font-size:13px; } /* Color Labels */ span.color_label { border-radius: 5px; padding: 2px 5px 2px 5px; color: #fff; font-size: 12px; font-weight: bold; text-shadow: 0px 1px 0px rgba(0, 0, 0, 0.5); cursor: default; white-space: nowrap; user-select: none; -moz-user-select: none; -webkit-user-select: none; } span.color_label.blue { background: #5890db; } span.color_label.green { background: #44bb44; } span.color_label.yellow { background: #bbbb44; } span.color_label.red { background: #bb4444; } span.color_label.purple { background: #bb44bb; } span.color_label.gray { background: #aaaaaa; } span.color_label.checkbox { cursor: pointer; background: var(--border-color); transition: all 0.5s ease; } span.color_label.checkbox:hover { background: var(--box-background-color); /* box-shadow: 0px 0px 1px 1px #7cafda; */ } span.color_label.checkbox:active { color: var(--label-color); } span.color_label.checkbox.checked { /* background: #3f7ed5; */ background: var(--highlight-color); } span.color_label.checkbox.checked:hover { } span.color_label.checkbox > i { width: 14px; } span.color_label.checkbox.plain { font-weight: normal !important; } .info_value .color_label { padding: 1px 5px 1px 5px; } /* Dialog Stuff */ .dialog_title { background: var(--box-background-color); padding: 15px 20px 15px 20px; font-size: 18px; font-weight: bold; border-left: 1px solid var(--border-color); border-right: 1px solid var(--border-color); border-top: 1px solid var(--border-color); margin: 0; /* color: #888; */ /* color: #008BD6; */ color: var(--highlight-color); text-align: center; /* text-shadow: 0 1px 0 rgba(255, 255, 255, 1); */ -webkit-border-top-left-radius: 5px; -webkit-border-top-right-radius: 5px; -moz-border-radius-topleft: 5px; -moz-border-radius-topright: 5px; border-top-left-radius: 5px; border-top-right-radius: 5px; -webkit-border-bottom-left-radius: 0px; -webkit-border-bottom-right-radius: 0px; -moz-border-radius-bottomleft: 0px; -moz-border-radius-bottomright: 0px; border-bottom-left-radius: 0px; border-bottom-right-radius: 0px; } .dialog_content { background: var(--dialog-background-color); border-left: 1px solid var(--border-color); border-right: 1px solid var(--border-color); padding: 30px; } .dialog_content fieldset { background: var(--box-background-color); } .dialog_buttons { background: var(--background-color); padding: 15px; border-left: 1px solid var(--border-color); border-right: 1px solid var(--border-color); border-bottom: 1px solid var(--border-color); border-top: 1px solid var(--border-color); -webkit-border-top-left-radius: 0px; -webkit-border-top-right-radius: 0px; -moz-border-radius-topleft: 0px; -moz-border-radius-topright: 0px; border-top-left-radius: 0px; border-top-right-radius: 0px; -webkit-border-bottom-left-radius: 5px; -webkit-border-bottom-right-radius: 5px; -moz-border-radius-bottomleft: 5px; -moz-border-radius-bottomright: 5px; border-bottom-left-radius: 5px; border-bottom-right-radius: 5px; } .dialog_shadow { /* box-shadow: rgba(0,0,0,0.2) 5px 5px 8px; -webkit-box-shadow: rgba(0,0,0,0.2) 5px 5px 8px; -moz-box-shadow: rgba(0,0,0,0.2) 5px 5px 8px; */ } .dialog_simple { background: var(--dialog-background-color); border: 1px solid var(--border-color); border-radius: 5px; padding: 30px; } /* Progress Bar Stuff */ @keyframes progress { to { background-position: 30px 0; } } div.progress_bar_container { position: relative; height: 18px; border-radius: 10px; background-color: var(--border-color); /* box-shadow: inset 2px 2px 4px #999; */ overflow: hidden; } div.progress_bar_container.indeterminate { background-color: var(--background-color); box-shadow: none; } div.progress_bar_inner { height: 18px; border-radius: 10px; background-color: var(--highlight-color); box-shadow: inset 0px 0px 6px 2px rgba(255,255,255,.3); animation: progress 1s linear infinite; background-repeat: repeat-x; background-size: 30px 30px; background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.25) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.25) 50%, rgba(255, 255, 255, 0.25) 75%, transparent 75%, transparent); } div.progress_bar_container.indeterminate > div.progress_bar_inner { opacity: 0.5; } /* Info Boxes */ div.info_label { font-size: 11px; font-weight: bold; color: var(--label-color); cursor: default; /* text-shadow: 0px 1px 0px white; */ } div.info_value { margin-top: 1px; margin-bottom: 10px; font-weight: bold; color: var(--body-text-color); } /* Dark Theme */ body.dark { --body-background-color: rgb(29, 29, 29); --background-color: rgb(23, 23, 23); --border-color: rgb(48, 48, 48); --dialog-background-color: rgb(24, 24, 24); --box-background-color: rgb(36, 36, 36); --header-text-color: rgb(128, 128, 128); --body-text-color: rgb(170, 170, 170); --label-color: rgb(110, 110, 110); --highlight-color: #3f7ed5; } body.dark input { background-color: var(--box-background-color); } body.dark input[type='text'], body.dark input[type='password'] { border: 1px solid var(--border-color); } body.dark input::placeholder { color: rgba(128, 128, 128, 0.4); } body.dark textarea { background-color: var(--box-background-color); } body.dark select { background-color: var(--box-background-color); color: var(--body-text-color); } @keyframes invalidFadeDark { 0% { background-color: #600; } 50% { background-color: #600; } 100% { background-color: var(--box-background-color); } } body.dark .invalid { background-color: #800; animation-name: invalidFadeDark; } body.dark div.message.success { color:#0c0; background-color:#050; } body.dark div.message.warning { color:#cc0; background-color:#550; } body.dark div.message.error { color:#f33; background-color:#400; } body.dark span.color_label.blue { background: rgb(8, 64, 140); } body.dark span.color_label.green { background: rgb(0, 108, 0); } body.dark span.color_label.yellow { background: rgb(108, 108, 0); } body.dark span.color_label.red { background: rgb(108, 0, 0); } body.dark span.color_label.purple { background: rgb(108, 0, 108); } body.dark span.color_label.gray { background: rgb(90, 90, 90); } body.dark div.progress_bar_inner { box-shadow: inset 0px 0px 6px 2px rgba(0,0,0,.3); background-image: linear-gradient(-45deg, rgba(0, 0, 0, 0.25) 25%, transparent 25%, transparent 50%, rgba(0, 0, 0, 0.25) 50%, rgba(0, 0, 0, 0.25) 75%, transparent 75%, transparent); } pixl-webapp-2.0.3/demo/000077500000000000000000000000001504641265100146745ustar00rootroot00000000000000pixl-webapp-2.0.3/demo/app.js000077500000000000000000000021261504641265100160160ustar00rootroot00000000000000// Demo Web App // Author: Joseph Huckaby // Copyright (c) 2015 Joseph Huckaby and PixlCore.com // Released under The MIT License // base.js must be loaded first if (!window.app) throw new Error("App Framework is not present."); // You app's configuration can be loaded from the server via a script tag // Shown inline here just as an example / for simplicity. window.config = app.config = { Version: "1.0.0", // Define all app's pages in 'Page' array Page: [ { ID: 'Home' }, { ID: 'MoreDemos' } ], // Which page to load by default (if not in URL hash) DefaultPage: 'Home' }; // Extend the global 'app' object with our custom functions app.extend({ // This name will appear in the window title name: 'My App', // init() is called on page load init: function() { // initialize application this.initTheme(); // pop version into footer $('#d_footer_version').html( "Version " + config.Version || 0 ); // Setup page manager for tabs this.page_manager = new PageManager( config.Page ); // start monitoring URL hash changes for page transitions Nav.init(); } }); pixl-webapp-2.0.3/demo/index.html000077500000000000000000000050751504641265100167030ustar00rootroot00000000000000 Loading...
My Application
 Logout
pixl-webapp-2.0.3/demo/logo.png000066400000000000000000000142701504641265100163460ustar00rootroot00000000000000‰PNG  IHDRii9‚ :gAMA± üa pHYs  šœÆiTXtXML:com.adobe.xmp 2 5 72 1 72 Pixelmator 3.3.2 2015:09:22 21:09:65 105 1 105 xmp.did:D71F355C0E2068119ECF899A1EC23618 xmp.iid:3441161F117711E28653B48E4E0E9B71 xmp.did:34411620117711E28653B48E4E0E9B71 xmp.iid:D71F355C0E2068119ECF899A1EC23618 xmp.did:D71F355C0E2068119ECF899A1EC23618 d:yˆIDATxík¨ÕÇ'ÆÄw¢Ñ˜Qአ½1 FmÕj?•*S4mhDÓÒBÁ/-jKÅ´_üЗ†b ­ D„B±X´ÕD‚As© ¢ŒÑhb|åaº~ûÎî:ûÎ93sΜs_gÁ¾kïµ×^{ïÿšý˜™3ûÎH&-_¾|±5÷–̘1c&]8qây³‰1ùDL~ÜØëÞVرcGȳô¤ ¹•×\sÍ¥ÇÿîI't}­µu^Mí=`N|ñ믿~aæÌ™Ï¼ôÒKoÕd·+f&œ“–-[6`À­±Þ®¶0`¡´Ç*ÙdÄÆ;wŸP4!œ´råÊ™ï¾ûîj»ºbè,o†Ð¬Y³ÂÉ'ŸÂìٚݒ ·ò¡¨ºäèÑ£™™#GŽ$ÇŽ ¹ÏË”F#;¬üc^x᦭[·2UŽ;«“-Z4{îܹk ”{ ‰1£ÆFTrÚi§%§žzj6íÕ˜MsÉ—_~Â_|‘Øʳ»ÇœþðÁƒ7îÞ½ûHžB¯dãæ¤«¯¾z°Î:º0îìgœ‘pN/‡}öÙg!äÔ7lÑo^~ùå9y=õÜIW]uÕ G­ãl2bÔØ¨JÎ<óÌL6‘Ç'6zÆŒ.» ^´6ÿ|ûOÖ`ÖŸ@tpþüùaK-ÙdãŒ|vœŒ,¶ñ)ÝpÑE-°Ýê?‡††NHX'¯ÝIllZØlü±zúé§'çŸ~¸%›¬œ‹QņÂÝ_i›‰AÛmÛ¿îMW'ý­ÕIŒ ÔA·ªQtˆ)Îæo‰&=§/\xÜ3ªR´µö6Å?U÷ˆªÕIé— ¶Öl¦*ñ4rŠÁO?ýt¾=âz¦Î>׿¤t“­A8ˆ0ÕIOEœ£®¬{3Q‹“Òm6»¸@LqSy©Ÿâ8*šúØLì¶5$NxÇ Eúja—5" æêóÎ;¯“6MÚ²~øaòù矫ýÍqKë¸áíè±2;9»‡Ø"±óa“0]‰¾ó*%¥¹†ÇV0’ ]Þ‘“lÍYo/SåSm§~•åìúxÔ%â1XŠ‘Dmñ¶§;[‡pÎv a]› ÏáÚB¨B<œýè£T’û¦öô|§Uy[#‰û!«èQ ÁA<‹c³Ð§x¢&)¬RÌ$«ÄÛrÒðððVËjbˆO§\YtÁÄÝÀ/±? h‹ÂH¨R’…Ю’§¬Ìʱ馮Щ®ËCY6Rz k[bëÓÛylTy$YEk àðÊ›FL´—uÉù`F)-L±Sº4¯ä$æUÛ±ð£‘@V©¢}Þص³6Ur’­E«¬-´‡áÜEM<ãÄ`V) ¤*]ŠWr’] ?“U…HÖçùx¬<†ùÚc¥¥d÷Eü;ûábË=Ìf’+ƒ2`ÙL}Œ¼´“¬äj•îOsB¢<0˰,c¡ï¤2(Õ Óu'Ùðä³’làw(5´}Ú˜3¿H1-Õÿ²#é:Yë߸ ‰ê<Â.ôÈR)'ÙÝòwd¨?Š„Duî±ó˜Y*tRz›ýnÛWTd¼Ÿßˆ€Çζâ×–½±-tÒÞ½{/µªÂK^hÕõùIcó§G ìÜKÁy)¶…/t’ Kî¹çPõyE<†ÛVf²§Í”Ì#)» $j›ócIèƒ>(eƒ-ìÅ_tßyçfß5Ø¢Ž ÙoË\U¿¡²’ 0tOÆ3l[/ã¤A›?ƒ:´~ýú`óÎ;y5Õœìç»ÉªU«Â/`½Î}â‰'û¸Ë‹Cüæ›oNnºé¦†2¼-E—2Ä=¡Ë-·Œy‰c7oÞœpQÔE†ÜÚR¡“ÌBW¦»èæ.·¡7Þxc²víÚPrŽcTÝ}÷ÝɆ ’çž{.+>å ô)ÇcÊ H|ðÁÌQ­ô/^êA¿.GEÓ]¸÷Ìß$Rè$EóU¶—›œÈ‚¸úŸ~úi5#ĹúÉ'à F ÊA÷Ýw_°ØC˜½HŸ‹çÞ~ûíÁ±Y:ˆx =¶­Ln¬pöówÇÜÊf-y€ƒ£˜Ö¼ƒdyè  ‰3²ò®|Fw^‘>Ž…p¦ÖÐ èàO„a†m+“eœ4òƒg³b›ˆV¶jÍÓ‚¯).ϸò¤ËTµÚ$xçésH_›–¼vT‘Efض²Q8ÝYáìg@Q­ìvœ'ù²)OºÒo ]vÙe &Þ|óÍ~ž~ƒ¢%d«®‘a˜a×ëÓeœÄ·áW˜ìò¢J¼­ gJcýòÄHc30Y¨Œ“¸”ÃÜÙK'íÙ³'¬Ñ ³\•‡.ÄÔÄèð;ÇmÛ¶eÓ#N›‘fúÁû#[L}u:j>M8¥2kÒAé»O%êß·o_°­Å=¯"åIWÎb¡1]1rÊWžÒ^_yp¦8M‰Z›|~;ñÈIÙ¸­l•qRæí¨‚Vv;ÎÓ¶ üÕ/ÃÈÈà Ú@ˆ³ ¸Ò‡kí’ÌëËáÊcé ×5’¢#rÏPå/3Ýe†º1’¶lᣌQŒuëÖPtoÚÂâÏ‚—ØVkqL¶ß8顇j¸™õetóëõ¹'"M`·¨m7¶©£.òÚú¾¿ŒÝ2NÚ+C|Ìë~ã,q[\ëG\XkrÀÄi8 Ðäò“{%¸'eúã±£C#°±ÇåGú´…ÇBÔá§¾<}_W;qŠÍL#‹i¡Â{ÍË'–¿ÇŽ;“Øw±&»—-'UY}¶¢Ö¡“½#‹ÊTÉÿä“OûR]E-V¢/Iæí·´ívŸÄ7³×Uyç¨!ñH“¼¯ªßÌN3¹Çаj¦çå…3ô¶ ø¡*YŸWCÀcè±me¥ÐIöî[f l¸ üÂ×Êp?o,`çFÒÛ±Š‘¤ÐIqiÓÝ‹*Ç~}j»e-tͱaù/5+ªHâ>/@„Ý?K *¥œdš/È ^ý*Ýç到Ë0-²PÊIé‘–aOÏstEÕÑÏ7ÀÌ=mØSå˜ÐRNJQÞ$´ËÞsH¿ÏG_yÄX–Á¦ï¤2(Õ ]ØÙ_Æti'Ùðä~i‡Œê…›Ò}Þ+ƒ2`Ù¼@”SÚI”³­ø_Tž“~ûT•ǰ\é$©ä¤… òËŒl á²uN+=0ò†ÃJ̬¢Í‰ˆv—Ì‹ªïQŽ»ç³Î:«Š‰i§ËÉ]zJc÷›¿~öÙg+_Si$° ÝÆ†‰óª?š@"ŸÀÆ=«¶S%ÿ–¯ÙZZÙIœ,oWDö+6wç¶®m傉?ôÌÚ=•¿²“ÀÙŽ¯|ÌÿÝ‹GFá`sâ}E€ÃÞÁ&¥×SÌ”®Ä _ú5³f/ûG©5‡i®Î£Ô*m|›ìTùaÛDpnM8ÛçR|Éæîõ§Kœsí§¬»¶å~Äî‹ÏmDÚšîTm"îµx¶[áêqC\jÓ†Ów¿™ƒ^M1ꃎœÄBh÷·Y Â-;7Ì;jØd,LßÝK½ƒ†ÇÊv7 ¾ÿ9 Céi½wÈ(§ùú«Iò©Îé³;ɘ§3wÕq’1¸µ½&yÐm}ú–›ìäÚ’û¯­½þT‹óØÇî|·î·u({„æ3Ú‰×â$*æßÓØFbE¯$­秺£pAij9sЯ”®ƒw<ÝùFØs©_XúIÉh<÷ S•è›w}O1¨µËmß'5kg´Ú(ÿaù·J‡S÷§Ò™áìâØ$ø5Èúú¤ýèñ‡ul„›x­# £4ò‚ .`ÇÇS‰@tæý÷ßϦ@É'#g§/‘ƒ£ÏÝpÕ>’<ðöTâKó/ã2bDùß{g“ =IP‹gkÐýJtƒwÕI4xÅŠ·ÚôÀwöé!?úçÜìIþÛ&áŽ^üãÅ®; GÙˆºÄ:´Åœu9iÑdø× ¼úŽïû¬/¯Z_n³”ý[}ê¯m Þªq¶=?`›‰¶ç?þò`6¬…<ïc álƒ‰6ªh/ìàŽø5ï#¶£[õÚk¯> s ݈öd$ù†Û¨Zlý³]‰Ùñläs¾§þê;X_¦—qFÛj÷Ê[Õó#œŸÚèiü J¹]ä=w’úbw­1g­³tÿÔ ”&|ÜœD{¸§²Ñ³ÆFÕ=–ˆÛÈkžX(ø#_bÝ*i~sÀ/JÜ+nof]DÛ¨ÚØ­­µ¯¬U|<œ4¦Î믿~¦­O«Í k­±ÙÙãqÃ9ኵ Îô¨¯ Ì,®2<6ç‡$q¦/8ÏÝ“j©{¾Ãœ¸ÁÎKÝôüóÏ÷i|ÄhNF·DcëBEqqš*3ÙW\q‰ ðw˜|ÌTØ…öarØú¸¨¿¿òÊ+~Ç–çX§kobNí–G÷u(^ÄCs–.]ºØFË·m„}Óœö-Ω©‡Ì)ÿ±ó_UÿÞµkW¼ðEœæÄ:55qÔŒÀ•t“Mωû@-yiÉs¹}téÚH@ÖePö¼¸'RZqñ<}éÒ&ŸïÓÄ!多ÿ€ÒP:#ó2Jó_鉋ÇqÒøHªÃ¿…_ŸW°/À2Ž( ÒmÆ©ž<ì@ÄE>Ëò@ò2Å6呵 èæÚ!¹Eƒ dªYGT§“h#È9<Ñ §.Å•—¾ÊŠ+Ïç›z ò ò ñ‘Ôè_%˜"d¤áq<–É pv|ðciÜ×¢|mT·“h&ÈÔáƒwœ!îËGWúʃCpŸ&.}\ÀÁSÐ J+Oiq9ŽƒT7qtÈàµR7œDi,À dê™e.™À—ƒ<'.}ÊÈ–ìÂ!äÒâJÜ_ár‚@dÂQ ä)ßsâèÔN‹fΡtºBu9IàÀh0q:åCž“Ôq¸òå$q9Uö=·bArŽâ¤}è8Gm£¬A>r¸ê±h ì¨ \uJŽŽ¸E;#*é©ñݲ?‘ìvµ¯u$åø+–8W£\­"éú+—8:¾,S# 0üÕ«‹L ‰SÇ&$›pê@^uº£ŒØŠƒ‰²º‰wDu9)n„À€óàÑAä~úðè'p#ª©Oùâ–•Ù•í¸Úy•¦ ¾ñE¡2±žÚ.ÇRŽ =ì×NÝp’ï ÀÑ!ɼsä 9F ‹Ë!>_‘‰¤FÿR/$˜"d7Ž“ö2ôäú¥¸·câz©n'Ñ1ˆF‹|‡åq9¤Çy±S$‡{’³ÔŸçeŠ \ôÔÎf\NjÅUÖ×Ûq\êØPj@öà L\<ŽSTºžKžÇ‘Aè—!9EºJçq-NÅåTÒŠ‹KGúðZ¨l'«T&›ž÷{yiÉ[qò<©/óqÀË#ÉËðØJ‹c?¶“Wg[²¢¶e4-$Ûâˆ/âªWz¾¬òĽŽdž îó$oÆót‘ÕJE¬£²¸Ž8M±,NçétÚ6ïíIJ8n,‹ÓÞ^-ñ<0j1ÜÂHQEù-L×’UzQ~-ðF¸éS#=wBcõcSÿ„aP€ªÅújIEND®B`‚pixl-webapp-2.0.3/demo/pages.js000066400000000000000000000234621504641265100163400ustar00rootroot00000000000000// Demo Web App // Author: Joseph Huckaby // Copyright (c) 2015 Joseph Huckaby and PixlCore.com // Released under The MIT License // // Home Page/Tab // Class.subclass( Page, "Page.Home", { onInit: function() { // called once at page load var html = ''; // include initial HTML here, if you want this.div.html( html ); }, onActivate: function(args) { // page activation if (!args) args = {}; this.args = args; app.setWindowTitle('Home'); app.showTabBar(true); // activate page here (show live / updated content) var html = ''; // fieldset html += '
Some Info'; html += '
Section 1
'; html += '
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
'; html += '
Section 2
'; html += '
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
'; html += '
'; // body text html += '
'; html += 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'; html += '
'; // buttons html += '
'; html += '
Demo Success
'; html += '
Demo Warning
'; html += '
Demo Error
'; html += '
Demo Progress
'; html += '
Demo Dialog
'; html += '
'; this.div.html( html ); return true; }, demoSuccess: function() { app.showMessage('success', "This is a successful message."); }, demoWarning: function() { app.showMessage('warning', "This is a warning message."); }, demoError: function() { app.showMessage('error', "This is an error message."); }, demoProgress: function() { app.showProgress( 0, "Doing something..." ); var progress = 0; var timer = setInterval( function() { progress += 0.03; app.showProgress( progress ); if (progress > 1.0) { app.hideProgress(); clearTimeout( timer ); } }, 100 ); }, demoDialog: function() { app.confirm( 'Add Vegetable', "Are you sure you want to add the vegetable celery?", "Add", function(result) { if (result) { app.hideDialog(); app.showMessage('success', "Celery added successfully."); } } ); }, onDeactivate: function() { // called when page is deactivated // this.div.html( '' ); return true; } } ); // // MoreDemos Page/Tab // Class.subclass( Page, "Page.MoreDemos", { defaultSubPage: 'form', onInit: function() { // called once at page load }, onActivate: function(args) { // page activation if (!args) args = {}; if (!args.sub) args.sub = this.defaultSubPage; this.args = args; app.showTabBar(true); // save current nav anchor for returning to sub-tab later this.tab[0]._page_id = Nav.currentAnchor(); // jump to sub-page based on has query this['gosub_'+args.sub](args); return true; }, gosub_form: function(args) { // show form demo sub-page app.setWindowTitle( "Form Demo" ); var html = ''; html += this.getSidebarTabs( args.sub, [ ['form', "Form Demo"], ['table', "Table Demo"] ] ); html += '
New Vegetable
'; html += '
'; html += '
'; var veg = { name: '', fresh: true, type: '' }; // Vegetable Name html += get_form_table_row( 'Name', '' ); html += get_form_table_caption( "Enter a title for the vegetable, which will be displayed on the main salad." ); html += get_form_table_spacer(); // Quality html += get_form_table_row( 'Quality', '' ); html += get_form_table_caption( "Select whether the vegetable should be farm fresh or not." ); html += get_form_table_spacer(); // Type var veg_types = ['Carrot', 'Celery', 'Beet', 'Lettuce']; html += get_form_table_row( 'Type', '' ); html += get_form_table_caption( "Select a category for the vegetable (this may limit the maximum available, etc.)" ); html += get_form_table_spacer(); // notes html += get_form_table_row( 'Notes', '' ); html += get_form_table_caption( "Optionally enter notes for the veg, which will be included in all e-mail notifications." ); html += get_form_table_spacer(); // buttons at bottom html += ''; html += '
'; html += '
'; html += ''; html += ''; html += ''; html += ''; html += '
Cancel
 
Save Changes
'; html += '
'; html += '
'; // table wrapper div html += '
'; // sidebar tabs this.div.html( html ); }, cancelEdit: function() { // cancel edit, return home Nav.go('Home'); }, saveChanges: function() { // simulate save changes app.showMessage('success', "Changes saved successfully."); }, gosub_table: function(args) { // show table demo sub-page app.setWindowTitle( "Table Demo" ); var html = ''; html += this.getSidebarTabs( args.sub, [ ['form', "Form Demo"], ['table', "Table Demo"] ] ); var cols = ['Vegetable Name', 'Color', 'Size', 'Quantity', 'Price', 'Date Added', 'Date Modified', 'Actions']; html += '
'; html += '
'; html += 'All Vegetables'; // html += '
'; html += '
'; // sort by title ascending this.vegetables = vegetables.sort( function(a, b) { return (b.name < a.name) ? 1 : -1; } ); var offset = args.offset || 0; var limit = args.limit || 5; // render table html += this.getPaginatedTable({ cols: cols, rows: this.vegetables.slice( offset, offset + limit ), data_type: 'vegetable', offset: offset, limit: limit, total: vegetables.length, callback: function(veg, idx) { var actions = [ 'Edit', 'Delete' ]; var tds = [ '
' + veg.name + '
', '' + veg.color + '', veg.size, commify( veg.quantity ), veg.price, get_short_date_time( veg.created ), get_short_date_time( veg.created + 86400 ), actions.join(' | ') ]; return tds; } }); html += '
'; html += '
'; html += ''; html += '
Add Vegetable...
'; html += '
'; // padding html += '
'; // sidebar tabs this.div.html( html ); }, editVeg: function(idx) { // add or edit vegetable Nav.go( 'MoreDemos?sub=form' ); }, deleteVeg: function(idx) { // delete vegetable }, onDeactivate: function() { // called when page is deactivated // this.div.html( '' ); return true; } } ); // // Sample data for table // window.vegetables = [ { name: 'Celery', color: 'Blue', size: '1ft', quantity: 450, price: '$2.75', created: 1442984544 }, { name: 'Beets', color: 'Green', size: '4in', quantity: 30, price: '$3.50', created: 1442380043 }, { name: 'Lettuce', color: 'Yellow', size: '1ft', quantity: 1000, price: '$2.50', created: 1442264863 }, { name: 'Carrots', color: 'Red', size: '8in', quantity: 60, price: '$4.00', created: 1442084869 }, { name: 'Rhubarb', color: 'Purple', size: '2ft', quantity: 190, price: '$3.99', created: 1441724876 }, { name: 'Amaranth', color: 'Gray', size: '1in', quantity: 270, price: '$0.50', created: 1441184886 }, { name: 'Arugula', color: 'Blue', size: '1ft', quantity: 0, price: '$4.30', created: 1439384893 }, { name: 'Brussels sprout', color: 'Green', size: '2in', quantity: 910, price: '$9.50', created: 1438934904 }, { name: 'Cabbage', color: 'Yellow', size: '1ft', quantity: 620, price: '$4.75', created: 1435784914 }, { name: 'Watercress', color: 'Red', size: '3in', quantity: 1300, price: '$11.00', created: 1370984920 } ]; pixl-webapp-2.0.3/demo/style.css000066400000000000000000000002611504641265100165450ustar00rootroot00000000000000/* Styles for Demo App */ #d_header_logo { position: relative; width: 40px; height: 40px; background: url(logo.png) no-repeat center center; background-size: 32px 32px; } pixl-webapp-2.0.3/docs/000077500000000000000000000000001504641265100147005ustar00rootroot00000000000000pixl-webapp-2.0.3/docs/datetime.md000066400000000000000000000245341504641265100170260ustar00rootroot00000000000000# Overview This module contains a set of static functions for dealing with dates and times. Note that some of the functions require [jQuery](http://jquery.com/) to work properly. # Usage The tools library is provided as a JavaScript file that you must include in your web page: ```html ``` That's it! The library is now available to your page. Make sure you include the library near the top of your file, above all your other code. Example usage: ```javascript var nice = get_nice_date_time( time_now() ); ``` # Function List Here are all the functions included in the tools library, with links to full descriptions and examples: | Function Name | Description | |---------------|-------------| | [time_now()](#time_now) | Get integer Epoch timestamp of current date/time. | | [hires_time_now()](#hires_time_now) | Get high-resolution Epoch timestamp of current date/time. | | [get_date_args()](#get_date_args) | Parse date/time into individual components suitable for display. | | [get_time_from_args()](#get_time_from_args) | Recalculate Epoch seconds given object from `get_date_args()`. | | [yyyy()](#yyyy) | Return the year in `YYYY` format given Epoch (defaults to current). | | [yyyy_mm_dd()](#yyyy_mm_dd) | Return date in `YYYY/MM/DD` format given Epoch (defaults to today). | | [mm_dd_yyyy()](#mm_dd_yyyy) | Return date in `MM/DD/YYYY` format given Epoch (defaults to today). | | [normalize_time()](#normalize_time) | Normalize (quantize) time by zeroing specified units. | | [get_nice_date()](#get_nice_date) | Return formatted date suitable for display. | | [get_nice_time()](#get_nice_time) | Return formatted time suitable for display. | | [get_nice_date_time()](#get_nice_date_time) | Return formatted date/time suitable for display. | | [get_short_date_time()](#get_short_date_time) | Return short (abbreviated) date/time suitable for display. | | [parse_date()](#parse_date) | Parse any local date/time string into Epoch seconds. | | [check_valid_date()](#check_valid_date) | Return `true` if date is a valid string, `false` otherwise. | ## time_now ``` NUMBER time_now( VOID ) ``` This function returns the current time expressed as [Epoch Seconds](http://en.wikipedia.org/wiki/Unix_time), floored to the nearest second. ```javascript var epoch = time_now(); // --> 1443319066 ``` ## hires_time_now ``` NUMBER hires_time_now( VOID ) ``` This function returns the current high-resolution time expressed as [Epoch Seconds](http://en.wikipedia.org/wiki/Unix_time), with floating point decimal milliseconds. ```javascript var epoch = hires_time_now(); // --> 1443319066.124 ``` ## get_date_args ``` OBJECT get_date_args( MIXED ) ``` This function parses any date string, Epoch timestamp or Date object, and produces a hash with the following keys (all localized to the current timezone): | Key | Sample Value | Description | | --- | ------------ | ----------- | | `year` | 2015 | Full year as integer. | | `mon` | 3 | Month of year as integer (1 - 12). | | `mday` | 6 | Day of month as integer (1 - 31). | | `wday` | 4 | Day of week as integer (0 - 6). | | `hour` | 9 | Hour of day as integer (0 - 23). | | `min` | 2 | Minute of hour as integer (0 - 59). | | `sec` | 10 | Second of minute as integer (0 - 59). | | `msec` | 999 | Millisecond of second as integer (0 - 999). | | `yyyy` | "2015" | 4-digit year as string. | | `mm` | "03" | 2-digit month as string with padded zeros if needed. | | `dd` | "06" | 2-digit day as string with padded zeros if needed. | | `hh` | "09" | 2-digit hour as string with padded zeros if needed. | | `mi` | "02" | 2-digit minute as string with padded zeros if needed. | | `ss` | "10" | 2-digit second as string with padded zeros if needed. | | `hour12` | 9 | Hour expressed in 12-hour time (i.e. 1 PM = 1.) | | `ampm` | "am" | String representing ante meridiem (`am`) or post meridiem (`pm`). | | `yyyy_mm_dd` | "2015/03/06" | Formatted string representing date in `YYYY/MM/DD` format. | | `hh_mi_ss` | "09:02:10" | Formatted string representing local time in `HH:MI:SS` format. | | `epoch` | 1425661330 | Epoch seconds used to generate all the date args. | | `offset` | -28800 | Local offset from GMT/UTC in seconds. | | `tz` | "GMT-8" | Formatted GMT hour offset string. | Example usage: ```javascript var args = get_date_args( new Date() ); var date_str = args.yyyy + '/' + args.mm + '/' + args.dd; ``` ## get_time_from_args ``` INTEGER get_time_from_args( OBJECT ) ``` This function will recalculate a date given an `args` object as returned from [get_date_args()](#get_date_args). It allows you to manipulate the `year`, `mon`, `mday`, `hour`, `min` and/or `sec` properties, and will return the computed Epoch seconds from the new set of values. Example: ```javascript var args = get_date_args( new Date() ); args.mday = 15; var epoch = get_time_from_args(args); ``` This example would return the Epoch seconds from the 15th day of the current month, in the current year, and using the current time of day. ## normalize_time ``` INTEGER normalize_time( INTEGER, OBJECT ) ``` This function will "normalize" (i.e. quantize) an Epoch value to the nearest minute, hour, day, month, or year. Meaning, you can pass in an Epoch time value, and have it return a value of the start of the current hour, midnight on the current day, the 1st of the month, etc. To do this, pass in an object containing any keys you wish to change, e.g. `year`, `mon`, `mday`, `hour`, `min` and/or `sec`. Example: ```javascript var midnight = normalize_time( time_now(), { hour: 0, min: 0, sec: 0 } ); ``` You can actually set the values to non-zero. For example, to return the Epoch time of exactly noon today: ```javascript var noon = normalize_time( time_now(), { hour: 12, min: 0, sec: 0 } ); ``` ## yyyy ``` STRING yyyy( EPOCH ) ``` Returns the date year in `YYYY` format, given any Epoch timestamp (defaults to current year). Example: ```javascript var year = yyyy( time_now() ); // --> "2015" ``` ## yyyy_mm_dd ``` STRING yyyy_mm_dd( EPOCH, SEPARATOR ) ``` Returns the date in `YYYY/MM/DD` format, given any Epoch timestamp (defaults to current day). You can customize the separator by passing it as the 2nd argument (defaults to slash). Example: ```javascript var today = yyyy_mm_dd( time_now(), '-' ); // --> "2015-09-26" ``` ## mm_dd_yyyy ``` STRING mm_dd_yyyy( EPOCH, SEPARATOR ) ``` Returns the date in `MM/DD/YYYY` format, given any Epoch timestamp (defaults to current day). You can customize the separator by passing it as the 2nd argument (defaults to slash). Example: ```javascript var today = mm_dd_yyyy( time_now(), '/' ); // --> "09/26/2015" ``` ## get_nice_date ``` STRING get_nice_date( EPOCH, ABBREVIATE ) ``` This function returns a "nice" (human-friendly) date string in the format `MONTH DD, YYYY`, given an Epoch timestamp. If you pass `true` for the second argument, the month name is abbreviated to the first 3 characters. Example: ```javascript var today = get_nice_date( time_now() ); // --> "September 26, 2015" var today = get_nice_date( time_now(), true ); // --> "Sep 26, 2015" ``` ## get_nice_time ``` STRING get_nice_time( EPOCH, SECONDS ) ``` This function returns a "nice" (human-friendly) time string in the format `HH:MM[:SS] AM/PM`, given an Epoch timestamp. If you pass `true` for the second argument, seconds are included. Example: ```javascript var nice_time = get_nice_time( time_now() ); // --> "11:37 PM" var nice_time = get_nice_time( time_now(), true ); // --> "11:37:08 PM" ``` ## get_nice_date_time ``` STRING get_nice_date_time( EPOCH, SECONDS, ABBREVIATE ) ``` This function returns a "nice" (human-friendly) date/time string in the format `MONTH DD, YYYY HH:MM[:SS] AM/PM`, given an Epoch timestamp. The 2rd argument is a Boolean indicating whether seconds should be included in the time, and the 3rd argument indicates whether the month name should be abbreviated or not. Example: ```javascript var str = get_nice_date_time( time_now() ); // --> "September 26, 2015 11:37 PM" var str = get_nice_date_time( time_now(), true ); // --> "September 26, 2015 11:37:08 PM" var str = get_nice_date_time( time_now(), true, true ); // --> "Sep 26, 2015 11:37:08 PM" ``` ## get_short_date_time ``` STRING get_short_date_time( EPOCH ) ``` The function returns a short date/time stamp in the format `MMM DD, YYYY HH:MM AM/PM`, given an Epoch timestamp. This is the same as calling [get_nice_date_time()](#get_nice_date_time) with `false` for seconds, and `true` for abbreviate. Example: ``javascript var str = get_short_date_time( time_now() ); // --> "Sep 26, 2015 11:37 PM" ``` ## parse_date ``` INTEGER parse_date( STRING ) ``` This function parses a local date/time string and returns Epoch seconds. Note that this may throw an error if the date/time format cannot be parsed (depending on the user's browser). Example use: ``javascript var when = parse_date( "2015/09/26 23:48:00" ); // --> 1443336480 ``` ## check_valid_date ``` BOOLEAN check_valid_date( STRING ) ``` This function returns `true` if a date/time string is well-formed (can be parsed), or `false` otherwise. This will not throw an error on malformed date strings. Example: ``javascript var valid = check_valid_date( "2015/09/26 23:48:00" ); // --> true var valid = check_valid_date( "015/0926 2-3::" ); // --> false ``` # License The MIT License Copyright (c) 2004 - 2015 Joseph Huckaby 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. pixl-webapp-2.0.3/docs/oop.md000066400000000000000000000226211504641265100160220ustar00rootroot00000000000000# Overview The JavaScript language already supports object orientated programming, but the syntax is strange and inheritance is wonky. This library is provided as a means to create classes in a more classical sort of way, including support for static class members, proper constructors, inheritance, and namespaces. # Usage This section describes how to use the framework. ## The oop.js File The framework is provided as a JavaScript library file that you must include in your web page: ```html ``` That's it! The library is now available to your page. Make sure you include the library near the top of your file, above all your other code. ## Creating Classes Here is how you create a class using the framework. You'll notice this is dramatically different than the traditional JavaScript class syntax. ```javascript Class.create( 'Animal', { // class member variables nickname: '', color: '', // class constructor __construct: function(new_name, new_color) { this.nickname = new_name; this.color = new_color; }, // methods getInfo: function() { return("Nickname: " + this.nickname + "\nColor: " + this.color); } } ); ``` This defines a class called `Animal`, with two member variables, `nickname` and `color`, a constructor and a `getInfo()` method which returns the nickname and color. You'll notice that to define the constructor method you use the keyword `__construct` which is exactly the same in PHP. Usage of this class is probably what you would expect: ```javascript var dog = new Animal('Spot', 'Green'); console.log( dog.getInfo() ); ``` Of course, you can also access the class member variables, as all members are public. ```javascript dog.nickname = 'Skippy'; dog.color = 'Blue'; console.log( dog.getInfo() ); ``` ## Creating Subclasses To create a subclass that inherits from a base class, use the following syntax: ```javascript Class.subclass( Animal, 'Bear', { // define a new member variable wants: 'Honey', // and a new method roar: function() { console.log("Roar! Give me " + this.wants + "!"); } } ); ``` This defines a `Bear` class which inherits from the base `Animal` class, including the constructor. Notice that we passed the parent class as a reference, not a string. What we did is extend the base class by introducing a new member variable `wants`, and a new method `roar()`. Everything else from the base class will be present in subclass instances: ```javascript var grizzly = new Bear('Fred', 'Brown'); console.log( grizzly.getInfo() ); grizzly.wants = 'blood'; grizzly.roar(); ``` ## Inheritance The framework provides an easy way to override methods defined in a base class. Simply redeclare them in the subclass: ```javascript Class.add( Bear, { // override base class method getInfo: function() { return "Bear has overridden this!"; } } ); ``` This also demonstrates `Class.add()` which allows you to add/replace methods or member variables in a class after it is defined. Note that the class name is passed in as a reference, not a string. Alternatively you could just have defined the method to be overridden in the original subclass definition. ### Calling Superclass methods You can also explicitly invoke the superclass method, in order to extend its functionality: ```javascript Class.add( Bear, { // override base class method getInfo: function() { // first, get info from base class var info = this.__parent.getInfo.call(this); // append bear info and return combined info info += "\nWants: " + this.wants; return info; } } ); ``` So here we are overriding the base class `getInfo()` method, but the first thing we do is call the superclass method of the same name. This is done with the `__parent` keyword, which points to the parent class prototype object. The JavaScript `call()` method allows you to call a function in object context (hence we are passing in the `this` keyword to it). Invoking a superclass constructor is just as easy: ```javascript Class.subclass( Animal, 'Bear', { // define a new member variable wants: 'Honey', // override base class constructor __construct: function(new_name, new_color, new_wants) { // invoke superclass constructor to set name and color this.__parent.__construct.call(this, new_name, new_color); this.wants = new_wants; } // and a new method roar: function() { console.log("Roar! Give me " + this.wants + "!"); } } ); ``` **Note:** You cannot use `Class.add()` to redeclare a class constructor. ## Static Class Members You can define static class members (variables or methods) by using the `__static` keyword. These members do not become part of class instances, but instead live inside the class reference object, and must be accessed that way too. Example class definition: ```javascript Class.create( 'Beer', { // static members __static: { types: ['Lager', 'Ale', 'Stout', 'Barleywine'] }, // class member variables nickname: '', type: '', // class constructor __construct: function(new_name, new_type) { this.nickname = new_name; if (!Beer.types[new_type]) throw new Error("TYPE NOT KNOWN: " + new_type); this.type = new_type; }, // methods getInfo: function() { return("Nickname: " + this.nickname + "\nType: " + this.type); } } ); ``` Here we define a `Beer` class which has a static member defined in the `__static` element. Anything placed there will *not* be propagated to class instances, and must be accessed using the class reference instead. As you can see in the constructor, we are checking the new type against the `types` array which is declared static, so we are getting to the list by using the syntax: `Beer.types` rather than `this.types`. If you were to change `Beer.types` later on, then *all* classes would see the changes instantly. The content is effectively shared. ## Namespaces The OOP framework also offers "namespaces". That is, named containers for your classes to live in, to prevent name collision with built-in classes or 3rd party libraries. You declare namespaces simply by adding periods (.) to your class name as passed to `Class.create()` and `Class.subclass()`: ```javascript Class.create( 'Beverage.Beer', { } ); ``` You can nest namespaces any number of levels deep. The library will automatically create any parent namespaces as needed. Now you can create instances of the class using `Beverage.Beer` instead of just `Beer`: ```javascript var mybeer = new Beverage.Beer(); ``` You can explicity create namespaces (without creating classes) using the `Namespace.create()` function. Example: ```javascript Namespace.create( 'Beverage' ); ``` All this does is create empty objects, ready to receive classes (although creating classes automatically creates the parent namespaces, so this is typically not required). **Note**: Do not use a class name as a namespace, or visa-versa. For example, if you create a class named `Beverage`, do not also use it as a namespace for a subclass, like `Beverage.Beer`. The results are undefined. ## Require Classes If your class requires other classes (possibly those defined in other include files), you can use the `Class.require()` function to make sure they are available. This is sort of like a class "assert" (see below), which will throw an alert if the required classes are not defined. Example: ```javascript Class.require( 'Animal' ); ``` This would throw an alert if the `Animal` class was not defined. You can also specify multiple classes on the same call: ```javascript Class.require( 'Animal', 'Bear' ); ``` ## Assert Macro The framework comes with a very simple `assert()` macro for testing that things you know should be true, are in fact true. You can pass it an optional message to be alerted if the assert fails, so you can tell them apart. Example: ```javascript assert( 1 + 1 == 2, "Math doesn't work" ); ``` If the fact turns out to be false, then the text is displayed in an alert so you can immediately see it and respond. This is a good way to check for objects that should exist in a parent container, by using the double-bang operator: ```javascript assert( !!window.something, "window.something does not exist"); ``` Using the double-bang operator simply converts anything to a Boolean -- a simple way to check for existence of an object. # License The MIT License Copyright (c) 2004 - 2015 Joseph Huckaby 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. pixl-webapp-2.0.3/docs/tools.md000066400000000000000000000464341504641265100163750ustar00rootroot00000000000000# Overview This module contains a set of miscellaneous utility functions that don't fit into any particular category. # Usage The tools library is provided as a JavaScript file that you must include in your web page: ```html ``` That's it! The library is now available to your page. Make sure you include the library near the top of your file, above all your other code. Example usage: ```javascript var id = generate_unique_id(); ``` # Function List Here are all the functions included in the tools library, with links to full descriptions and examples: | Function Name | Description | |---------------|-------------| | [parse_query_string()](#parse_query_string) | Parses a URL query string into key/value pairs. | | [compose_query_string()](#compose_query_string) | Takes an object and serializes it into a URL query string. | | [get_text_from_bytes()](#get_text_from_bytes) | Get a human-readable string from a number of bytes (e.g. `150 MB`). | | [get_bytes_from_text()](#get_bytes_from_text) | Parse a human-readable size string into raw bytes. | | [ucfirst()](#ucfirst) | Upper-case the first character of a string, lower-case the rest. | | [commify()](#commify) | Add commas to a positive integer in U.S. style, e.g. `1,000,000`. | | [short_float()](#short_float) | Shorten floating-point decimal to 2 places, unless they are zeros. | | [pct()](#pct) | Return percentage given a number along a sliding scale from 0 to 'max' | | [get_text_from_seconds()](#get_text_from_seconds) | Convert raw seconds to human-readable relative time, e.g. `4 hours`. | | [get_text_from_seconds_round()](#get_text_from_seconds_round) | Convert raw seconds to human-readable relative time, but round instead of floor. | | [get_seconds_from_text()](#get_seconds_from_text) | Parse a human-readable time string into raw seconds. | | [get_inner_window_size()](#get_inner_window_size) | Get width and height of inner browser window, in pixels. | | [get_scroll_xy()](#get_scroll_xy) | Get page scroll offset (X and Y) in pixels. | | [get_scroll_max()](#get_scroll_max) | Get maximum page scroll width/height in pixels. | | [hires_time_now()](#hires_time_now) | Get high-resolution Epoch timestamp of current date/time. | | [str_value()](#str_value) | Get friendly string value for display purposes. | | [pluralize()](#pluralize) | Pluralize a word using English language rules. | | [render_menu_options()](#render_menu_options) | Return HTML for a set of menu options. | | [dirname()](#dirname) | Return path excluding file at end (same as POSIX function of same name). | | [basename()](#basename) | Return filename, strip path (same as POSIX function of same name). | | [strip_ext()](#strip_ext) | Strip extension from filename or URL. | | [load_script()](#load_script) | Dynamically load script into DOM. | | [compose_attribs()](#compose_attribs) | Convert object into `Key="Value"` formatted attributes for HTML elements. | | [compose_style()](#compose_style) | Convert object into `key:value;` formatted pairs for style (inline CSS) attribute. | | [truncate_ellipsis()](#truncate_ellipsis) | Simple truncate string with ellipsis if beyond specified max length. | | [escape_text_field_value()](#escape_text_field_value) | Escape text field string value, with stupid IE support. | | [expando_text()](#expando_text) | If text is longer than max chars, chop with ellipsis and include link to show all. | | [get_int_version()](#get_int_version) | Convert 3-part version string into integer for comparison with another. | | [get_unique_id()](#get_unique_id) | Get unique ID using MD5, hires time, pseudo-random number and static counter. | | [escape_regexp()](#escape_regexp) | Escape text for use in a regular expression. | ## parse_query_string ``` OBJECT parse_query_string( URL ) ``` This function parses a standard URL query string, and returns a hash with key/value pairs for every query parameter. Duplicate params are clobbered, the latter prevails. Values are URL-unescaped, and all of them are strings. The function accepts a full URL, or just the query string portion. ```javascript var url = 'http://something.com/hello.html?foo=bar&baz=12345'; var query = parse_query_string( url ); var foo = query.foo; // "bar" var baz = query.baz; // "12345" ``` ## compose_query_string ``` STRING compose_query_string( OBJECT ) ``` This function takes a hash of key/value pairs, and constructs a URL query string out of it. Values are URL-escaped. ```javascript var my_hash = { foo: "bar", baz: 12345 }; var qs = compose_query_string( my_hash ); // --> "?foo=bar&baz=12345" ``` ## get_text_from_bytes ``` STRING get_text_from_bytes( BYTES, PRECISION ) ``` This function generates a human-friendly text string given a number of bytes. It reduces the units to K, MB, GB or TB as needed, and allows a configurable amount of precision after the decimal point. The default is one decimal of precision (specify as `1`, `10`, `100`, etc.). ```javascript var str = get_text_from_bytes( 0 ); // "0 bytes" var str = get_text_from_bytes( 1023 ); // "1023 bytes" var str = get_text_from_bytes( 1024 ); // "1 K" var str = get_text_from_bytes( 1126 ); // "1.1 K" var str = get_text_from_bytes( 1599078, 1 ); // "1 MB" var str = get_text_from_bytes( 1599078, 10 ); // "1.5 MB" var str = get_text_from_bytes( 1599078, 100 ); // "1.52 MB" var str = get_text_from_bytes( 1599078, 1000 ); // "1.525 MB" ``` ## get_bytes_from_text ``` INTEGER get_bytes_from_text( STRING ) ``` This function parses a string containing a human-friendly size count (e.g. `45 bytes` or `1.5 MB`) and converts it to raw bytes. ```javascript var bytes = get_bytes_from_text( "0 bytes" ); // 0 var bytes = get_bytes_from_text( "1023 bytes" ); // 1023 var bytes = get_bytes_from_text( "1 K" ); // 1024 var bytes = get_bytes_from_text( "1.1k" ); // 1126 var bytes = get_bytes_from_text( "1.525 MB" ); // 1599078 ``` ## ucfirst ``` STRING ucfirst( STRING ) ``` The function upper-cases the first character of a string, and lower-cases the rest. This is very similar to the Perl core function of the same name. Example: ```javascript var first_name = ucfirst( 'george' ); // --> "George" ``` ## commify ``` STRING commify( INTEGER ) ``` This function adds commas to long numbers following US-style formatting rules (add comma every 3 digits counting from right side). Only positive integers are supported. ```javascript var c = commify( 123 ); // "123" var c = commify( 1234 ); // "1,234" var c = commify( 1234567890 ); // "1,234,567,890" ``` ## short_float ``` NUMBER short_float( NUMBER ) ``` This function "shortens" a floating point number by only allowing two digits after the decimal point, *unless they are zeros*. ```javascript var short = short_float( 0.12345 ); // 0.12 var short = short_float( 0.00001 ); // 0.00001 var short = short_float( 0.00123 ); // 0.0012 ``` ## pct ``` STRING pct( AMOUNT, MAX, FLOOR ) ``` This function calculates a percentage given an arbitrary numerical amount and a maximum value, and returns a formatted string with a '%' symbol. Pass `true` as the 3rd argument to floor the percentage to the nearest integer. Otherwise the value is shortened with `shortFloat()`. ```javascript var p = pct( 5, 10 ); // "50%" var p = pct( 0, 1 ); // "0%" var p = pct( 751, 1000 ); // "75.1%" var p = pct( 751, 1000, true ); // "75%" ``` ## get_text_from_seconds ``` STRING get_text_from_seconds( NUMBER, ABBREVIATE, SHORTEN ) ``` This function generates a human-friendly time string given a number of seconds. It reduces the units to minutes, hours or days as needed. You can also abbreviate the output, and shorten the extra precision. ```javascript var str = get_text_from_seconds( 0 ); // "0 seconds" var str = get_text_from_seconds( 86400 ); // "1 day" var str = get_text_from_seconds( 90 ); // "1 minute, 30 seconds" var str = get_text_from_seconds( 90, true ); // "1 min, 30 sec" var str = get_text_from_seconds( 90, false, true ); // "1 minute" var str = get_text_from_seconds( 90, true, true ); // "1 min" ``` ## get_text_from_seconds_round ``` STRING get_text_from_seconds_round( NUMBER, ABBREVIATE ) ``` This function generates a human-friendly time string given a number of seconds. It reduces the units to minutes, hours or days as needed, but rounds to the nearest whole unit. You can also abbreviate the output if desired. ```javascript var str = get_text_from_seconds_round( 0 ); // "0 seconds" var str = get_text_from_seconds_round( 85000 ); // "1 day" var str = get_text_from_seconds_round( 89 ); // "1 minute" var str = get_text_from_seconds_round( 90 ); // "2 minutes" var str = get_text_from_seconds_round( 90, true ); // "2 min" ``` ## get_seconds_from_text ``` INTEGER get_seconds_from_text( STRING ) ``` This function parses a string containing a human-friendly time (e.g. `45 minutes` or `7 days`) and converts it to raw seconds. It accepts seconds, minutes, hours, days and/or weeks. It does not interpret "months" or "years" because those are non-exact measurements. ```javascript var sec = get_seconds_from_text( "1 second" ); // 1 var sec = get_seconds_from_text( "2min" ); // 120 var sec = get_seconds_from_text( "30m" ); // 1800 var sec = get_seconds_from_text( "12 HOURS" ); // 43200 var sec = get_seconds_from_text( "1day" ); // 86400 ``` ## get_inner_window_size ``` OBJECT get_inner_window_size( VOID ) ``` This function measures the browser's inner window size (the total amount of pixel space available), and returns an object with `width` and `height` properties. Guaranteed to work in all modern browsers, including IE6 and up. Example: ```javascript var size = get_inner_window_size(); // --> { width: 1120, height: 950 } ``` ## get_scroll_xy ``` OBJECT get_scroll_xy( VOID ) ``` This function returns the current window scroll offset, and returns an object with `x` and `y` properties. Guaranteed to work in all modern browsers, including IE6 and up. Example: ```javascript var offset = get_scroll_xy(); // --> { x: 0, y: 175 } ``` ## get_scroll_max ``` OBJECT get_scroll_max( VOID ) ``` This function measures the total window scroll area. That is, if the page HTML content stretches beyond the window bounds and scrollbars are present, this returns an object representing the maximum scroll width and height. Guaranteed to work in all modern browsers, including IE6 and up. Example: ```javascript var size = get_scroll_max(); // --> { width: 1120, height: 3784 } ``` ## hires_time_now ``` NUMBER hires_time_now( VOID ) ``` This function returns the current high-resolution time expressed as [Epoch Seconds](http://en.wikipedia.org/wiki/Unix_time), with floating point decimal milliseconds. ```javascript var epoch = hires_time_now(); // --> 1443319066.124 ``` ## str_value ``` STRING str_value( MIXED ) ``` This function provides a nice way to coerce a value into a string. For example, if the value passed in is `undefined` or `null`, an empty string is returned. If a number or other non-string is passed in, it is converted to a string, and returned. Example: ```javascript var str = str_value( undefined ); // "" var str = str_value( null ); // "" var str = str_value( 123 ); // "123" var str = str_value( "Z" ); // "Z" ``` ## pluralize ``` STRING pluralize( STRING, NUMBER ) ``` This function pluralizes a string using US-English rules, given an arbitrary number. This is useful when constructing human-friendly sentences containing a quantity of things, and you wish to say either "thing" or "things" depending on the number. ```javascript var list = ['apple', 'orange', 'banana']; var text = "You have " + list.length + pluralize(" item", list.length) + " in your list."; // --> "You have 3 items in your list."; ``` ## render_menu_options ``` HTML render_menu_options( ITEMS, SELECTED, AUTO_ADD ) ``` This functions composes HTML for a set of menu options, i.e. ` ``` If you need to vary the option values and display text, you can use one of three different formats. A nested array with the first sub-element set to the value, and the 2nd the display label, an array of hashes with `data` and `label` properties, or an array of hashes with `id` and `title` properties. Examples: ```javascript var list = [ ['AAPL','Apple'], ['ORNG','Orange'], ['BANA','Banana'] ]; var list = [ { data: 'AAPL', label: 'Apple' }, { data: 'ORNG', label: 'Orange' }, { data: 'BANA', label: 'Banana' } ]; var list = [ { id: 'AAPL', title: 'Apple' }, { id: 'ORNG', title: 'Orange' }, { id: 'BANA', title: 'Banana' } ]; ``` In each of the three cases, the HTML output would be the same: ```html ``` ## dirname ``` STRING dirname( STRING ) ``` This function returns the directory path portion of a string, sans the final slash and filename. It tries to emulate the POSIX function of the same name. It should also work on URLs. Example: ```javascript var path = '/var/www/html/index.html'; var dir = dirname( path ); // --> "/var/www/html" ``` ## basename ``` STRING basename( STRING ) ``` This function returns the filename portion of a string, sans the parent directory path and slash. It tries to emulate the POSIX function of the same name. It should also work on URLs. Example: ```javascript var path = '/var/www/html/index.html'; var filename = basename( path ); // --> "index.html" ``` ## strip_ext ``` STRING strip_ext( STRING ) ``` This function strips the extension from the end of a filename, file path or URL. Example: ```javascript var path = '/var/www/html/index.html'; var filename = strip_ext( basename( path ) ); // --> "index" ``` ## load_script ``` VOID load_script( URL ) ``` This function dynamically loads a JavaScript file given a URL (can be partial, relative to the page). The script is loaded by appending a `SCRIPT` element to the document `HEAD`. Example: ```javascript load_script( 'js/myscript/js' ); ``` ## compose_attribs ``` STRING compose_attribs( OBJECT ) ``` This function takes an object containing key/value pairs, and serializes them into HTML/XML attributes, returning the resulting string. Example: ```javascript var attribs = { type: 'text', name: 'username', size: 20, value: 'frank' }; var html = ""; // --> '' ``` ## compose_style ``` STRING compose_style( OBJECT ) ``` This function takes an object containing key/value pairs, and serializes them into CSS style rules, returning the resulting string. Example: ```javascript var style = { margin:0, padding:0, 'font-size':'12px', color:'red' }; var html = '
'; // --> '
' ``` ## escape_text_field_value ``` STRING escape_text_field_value( STRING ) ``` This function escapes a string for insertion into raw HTML markup, specifically into the `value` attribute of a form element. For example, if a string contains special characters such as angle brackets or quotes, these will be converted into HTML entities. Also, undefined / null values will become empty strings. Example: ```javascript var tricky_username = "frank"; var html = ''; // --> '' ``` ## truncate_ellipsis ``` STRING truncate_ellipsis( STRING, LENGTH ) ``` This function will truncate (chop) a string at a specified length, and append an ellipsis (...) to indicate the string was chopped. Note that the string is chopped 3 characters shy of the specified amount, to make room for the ellipsis. Example: ```javascript var text = "The quick brown fox jumped over the lazy, sleeping dog."; var short_text = truncate_ellipsis( text, 20 ); // --> "The quick brown f..." ``` ## expando_text ``` STRING expando_text( STRING, LENGTH, LINK ) ``` This function will truncate (chop) a string at a specified length, and append an ellipsis (...) to indicate the string was chopped. It will also add an HTML link (customizable text) that when clicked, will fully expand the text and show the original full string, removing the ellipsis and link. Example: ```javascript var text = "The quick brown fox jumped over the lazy, sleeping dog."; var html = expando_text( text, 20, "More" ); ``` The HTML link will contain an `onMouseUp` event and use [jQuery](http://jquery.com/) to expand the text. ## get_int_version ``` INTEGER get_int_version( STRING, PADDING ) ``` This function takes a 3-part version string (e.g. `1.25.3`), and converts it to an integer for comparison with other versions. Basically, it pads each number with zeroes, and combines them into one single integer. Example: ```javascript var version = '2.5.1'; var int_ver = get_int_version(version); // --> 2005001 ``` ## get_unique_id ``` STRING get_unique_id( LENGTH, SALT ) ``` This function generates a pseudo-random alphanumeric (hexadecimal) ID by combining various bits of local entropy, and hashing it together with [MD5](http://en.wikipedia.org/wiki/MD5). The default length is 32 characters, but you can pass in any lesser length to chop it. If you want to add your own entropy (salt string), pass it as the 2nd argument. ```javascript var id = get_unique_id(); var id = get_unique_id( 16 ); var id = get_unique_id( 32, "my extra entropy!" ); ``` ## escape_regexp ``` STRING escape_regexp( STRING ) ``` This function escapes a string so that it can be used inside a regular expression. Meaning, any regular expression metacharacters are prefixed with a backslash, so they are interpreted literally. It was taken from the [MDN Regular Expression Guide](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions). # License The MIT License Copyright (c) 2004 - 2015 Joseph Huckaby 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. pixl-webapp-2.0.3/docs/xml.md000066400000000000000000000363771504641265100160420ustar00rootroot00000000000000# Overview This module provides a lightweight, fast, easy-to-use XML parser which generates a simplified object / array tree. This can be very useful for parsing XML server API responses and the like. It is 100% pure JavaScript and has no dependencies. * Pure JavaScript, no dependencies * Fully synchronous operation, no callbacks * Can preserve or flatten attributes * Can serialize objects back to pretty-printed XML # Usage The XML library is provided as a JavaScript file that you must include in your web page: ```html ``` That's it! The library is now available to your page. Make sure you include the library near the top of your file, above all your other code. Usage is as follows: ```javascript var myxml = '' + 'Hello' + 'Content' + ''; var parser = new XML({ text: myxml, preserveAttributes: true }); var tree = parser.getTree(); console.log(tree); ``` You can also manipulate the XML tree in memory, then have the library output XML again: ```javascript tree.Simple = "Hello2"; tree.Node._Attribs.Key = "Value2"; tree.Node._Data = "Content2"; tree.New = "I added this"; console.log( parser.compose() ); ``` ## Options The `XML` constructor accepts a plain XML string, or it can be an object containing any of the following properties: ### text The XML text to be parsed (string). ### preserveAttributes This optional property, when set to `true`, will cause all XML attributes to be kept separate in their own sub-object called `_Attribs` for each element. ### attribsKey This is the key used to identify XML attributes, when [preserveAttributes](#preserveattributes) is set to `true`. It defaults to `_Attribs`. ### dataKey This is the key used to identify string values of complex XML elements that contain both attributes and text. It defaults to `_Data`. ## Composing XML To compose XML back to a string, call the `compose()` method on your XML object. It helps to parse using the [preserveAttributes](#preserveattributes) option for this, as it will honor the `_Attribs` sub-objects and convert them back into real XML attributes. ```javascript var parser = new XML({ text: myxml, preserveAttributes: true }); var tree = parser.getTree(); var output = parser.compose(); console.log(output); ``` Note that elements and attributes may lose their original ordering, as hashes have an undefined key order. However, to keep things consistent, they are both alphabetically sorted when serialized. ## Utility Functions There are also a number of static utility functions provided in the `xml.js` file: | Function Name | Description | |---------------|-------------| | [trim()](#trim) | Strip whitespace from beginning and end of string. | | [encode_entities()](#encode_entities) | Encode basic HTML entities `<`, `>` and `&`. | | [encode_attrib_entities()](#encode_attrib_entities) | Encode basic entities, plus quotes. | | [decode_entities()](#decode_entities) | Decode basic HTML entities, and quotes. | | [find_object()](#find_object) | Walk array looking for nested object matching criteria object. | | [find_objects()](#find_objects) | Walk array gathering all nested objects that match criteria object. | | [find_object_idx()](#find_object_idx) | Walk array looking for nested object matching criteria object, return index in outer array, not object itself. | | [delete_object()](#delete_object) | Walk array looking for nested object matching criteria object, delete first object found. | | [delete_objects()](#delete_objects) | Delete all objects in obj array matching criteria. | | [always_array()](#always_array) | Wrap variable in array, unless it is already an array. | | [hash_keys_to_array()](#hash_keys_to_array) | Creates an array out of all object keys (undefined order). | | [hash_values_to_array()](#hash_values_to_array) | Creates an array out of all object values (undefined order). | | [sort_array()](#sort_array) | Performs a custom sort on an array, using a specified nested key and direction. | | [merge_objects()](#merge_objects) | Non-destructive shallow merge of two objects, return the combined one. | | [copy_object()](#copy_object) | Makes a shallow copy of an object. | | [deep_copy_object()](#deep_copy_object) | Makes a deep copy of an object. | | [copy_into_object()](#copy_into_object) | Merge one hash into another (destructive). | | [lookup_path()](#lookup_path) | Perform a `/filesystem/path/style` lookup in an object tree. | | [isa_hash()](#isa_hash) | Determines if a variable is a hash (object) or not. | | [isa_array()](#isa_array) | Determines if a variable is an array (or array-like) or not. | | [first_key()](#first_key) | Returns the "first" key in an object (undefined order). | | [num_keys()](#num_keys) | Returns the number of keys in an object. | | [reverse_hash()](#reverse_hash) | Reverse the keys and values of a hash. | | [rand_array()](#rand_array) | Return random element from array. | | [find_in_array()](#find_in_array) | Return `true` if element is found in array, `false` otherwise. | ### trim ``` STRING trim( STRING ) ``` This function trims whitespace from the left and right sides of a string, returning the new string. Example: ```javascript var text = " Hello\n\t "; console.log( trim(text) ); // Would output: "Hello" ``` ### encode_entities ``` STRING encode_entities( STRING ) ``` This function will take a string, and encode the three standard XML entities, ampersand (`&`), left-angle-bracket (`<`) and right-angle-bracket (`>`), into their XML-safe counterparts. It returns the result. Example: ```javascript var text = '&'; console.log( encode_entities(text) ); // Would output: <Hello>&<There> ``` ### encode_attrib_entities ``` STRING encode_attrib_entities( STRING ) ``` This function does basically the same thing as [encode_entities](#encode_entities), but it also includes encoding for single-quotes (`'`) and double-quotes (`"`). It is used for encoding an XML string for composing into an attribute value. It returns the result. Example: ```javascript var text = '"&"'; console.log( encode_attrib_entities(text) ); // Would output: <Hello>"&"<There> ``` ### decode_entities ``` STRING decode_entities( STRING ) ``` This function decodes all the standard XML entities back into their original characters. This includes ampersand (`&`), left-angle-bracket (`<`), right-angle-bracket (`>`), single-quote (`'`) and double-quote (`"`). It is used when parsing XML element and attribute values. Example: ```javascript var text = '<Hello>"&"<There>'; console.log( decode_entities(text) ); // Would output: "&" ``` ### find_object ``` OBJECT find_object( ARRAY, CRITERIA ) ``` This function iterates over an array of hashes, and returns the first item whose object has keys which match a given criteria hash. If no objects match, `null` is returned. ```javascript var list = [ { id: 12345, name: "Joe", eyes: "blue" }, { id: 12346, name: "Frank", eyes: "brown" }, { id: 12347, name: "Cynthia", eyes: "blue" } ]; var criteria = { eyes: "blue" }; var obj = find_object( list, criteria ); // --> { id: 12345, name: "Joe", eyes: "blue" } ``` ### find_objects ``` ARRAY find_objects( ARRAY, CRITERIA ) ``` This function iterates over an array of hashes, and returns all the items whose objects have keys which match a given criteria hash. ```javascript var list = [ { id: 12345, name: "Joe", eyes: "blue" }, { id: 12346, name: "Frank", eyes: "brown" }, { id: 12347, name: "Cynthia", eyes: "blue" } ]; var criteria = { eyes: "blue" }; var objs = find_objects( list, criteria ); // --> [{ id: 12345, name: "Joe", eyes: "blue" }, { id: 12347, name: "Cynthia", eyes: "blue" }] ``` ### find_object_idx ``` INTEGER find_object_idx( ARRAY, CRITERIA ) ``` This function iterates over an array of hashes, and returns the first array index whose object has keys which match a given criteria hash. If no objects match, `-1` is returned. ```javascript var list = [ { id: 12345, name: "Joe", eyes: "blue" }, { id: 12346, name: "Frank", eyes: "brown" }, { id: 12347, name: "Cynthia", eyes: "blue" } ]; var criteria = { eyes: "blue" }; var idx = find_object_idx( list, criteria ); // --> 0 ``` ### delete_object ``` BOOLEAN delete_object( ARRAY, CRITERIA ) ``` This function iterates over an array of hashes, and deletes the first item whose object has keys which match a given criteria hash. It returns `true` for success or `false` if no matching object could be found. ```javascript var list = [ { id: 12345, name: "Joe", eyes: "blue" }, { id: 12346, name: "Frank", eyes: "brown" }, { id: 12347, name: "Cynthia", eyes: "blue" } ]; var criteria = { eyes: "blue" }; delete_object( list, criteria ); // list will now contain only Frank and Cynthia ``` ### delete_objects ``` INTEGER delete_objects( ARRAY, CRITERIA ) ``` This function iterates over an array of hashes, and deletes all items whose objects have keys which match a given criteria hash. It returns the number of objects deleted. ```javascript var list = [ { id: 12345, name: "Joe", eyes: "blue" }, { id: 12346, name: "Frank", eyes: "brown" }, { id: 12347, name: "Cynthia", eyes: "blue" } ]; var criteria = { eyes: "blue" }; var count = delete_objects( list, criteria ); // list will now contain only Frank ``` ### always_array ``` ARRAY always_array( MIXED ) ``` This function will wrap anything passed to it into an array and return the array, unless the item passed is already an array, in which case it is simply returned verbatim. ```javascript var arr = always_array( maybe_array ); ``` ### hash_keys_to_array ``` ARRAY hash_keys_to_array( OBJECT ) ``` This function returns all the hash keys as an array. Useful for sorting and then iterating over the sorted list. ```javascript var my_hash = { foo: "bar", baz: 12345 }; var keys = hash_keys_to_array( my_hash ).sort(); for (var idx = 0, len = keys.length; idx < len; idx++) { var key = keys[idx]; // do something with key and my_hash[key] } ``` ### hash_values_to_array ``` ARRAY hash_values_to_array( OBJECT ) ``` This function returns all the hash values as an array. The keys are discarded. ```javascript var my_hash = { foo: "bar", baz: 12345 }; var values = hash_values_to_array( my_hash ); for (var idx = 0, len = values.length; idx < len; idx++) { var value = values[idx]; // do something with value } ``` ### merge_objects ``` OBJECT merge_objects( OBJECT_A, OBJECT_B ) ``` This function merges two objects together, and returns a new object which contains the combination of the two keys and values (shallow copy). The 2nd object takes precedence over the first, in the event of duplicate keys. ```javascript var hash1 = { foo: "bar" }; var hash2 = { baz: 12345 }; var combo = merge_objects( hash1, hash2 ); ``` ### copy_object ``` OBJECT copy_object( OBJECT ) ``` This function performs a shallow copy of the specified hash, and returns the copy. ```javascript var my_hash = { foo: "bar", baz: 12345 }; var my_copy = copy_object( my_hash ); ``` ### deep_copy_object ``` OBJECT deep_copy_object( OBJECT ) ``` This function performs a deep copy of the specified hash (uses `JSON.stringify()` and `JSON.parse()`), and returns the copy. ```javascript var my_hash = { foo: "bar", baz: 12345 }; var my_copy = deep_copy_object( my_hash ); ``` ### copy_into_object ``` VOID copy_into_object( OBJECT_A, OBJECT_B ) ``` This function shallow-merges {OBJECT_B} into {OBJECT_A}. There is no return value. Existing keys are replaced in {OBJECT_A}. ```javascript var hash1 = { foo: "bar" }; var hash2 = { baz: 12345 }; copy_into_object( hash1, hash2 ); ``` ### lookup_path ``` MIXED lookup_path( PATH, ARGS ) ``` This function will perform a directory-style path lookup on a hash/array tree, returning whatever object or value is pointed to, or `null` if not found. ```javascript var tree = { folder1: { file1: "foo", folder2: { file2: "bar" } } }; var file = lookup_path( "/folder1/folder2/file2", tree ); // --> "bar" ``` For walking into arrays, simply provide the index number of the element you want. ### isa_hash ``` BOOLEAN isa_hash( MIXED ) ``` This function returns `true` if the provided argument is a hash (object), `false` otherwise. ```javascript var my_hash = { foo: "bar", baz: 12345 }; var is_hash = isa_hash( my_hash ); ``` ### isa_array ``` BOOLEAN isa_array( MIXED ) ``` This function returns `true` if the provided argument is an array (or is array-like), `false` otherwise. ```javascript var my_arr = [ "foo", "bar", 12345 ]; var is_arr = isa_array( my_arr ); ``` ### num_keys ``` INTEGER num_keys( OBJECT ) ``` This function returns the number of keys in the specified hash. ```javascript var my_hash = { foo: "bar", baz: 12345 }; var num = num_keys( my_hash ); // 2 ``` ### first_key ``` STRING first_key( OBJECT ) ``` This function returns the first key of the hash when iterating over it. Note that hash keys are stored in an undefined order. ```javascript var my_hash = { foo: "bar", baz: 12345 }; var key = first_key( my_hash ); // foo or baz ``` ### reverse_hash ``` OBJECT reverse_hash( OBJECT ) ``` This function shallow-copies an object, but swaps the keys and values. It returns the new object. Example: ```javascript var my_hash = { foo: "bar", baz: 12345 }; var rev = reverse_hash( my_hash ); // --> { "bar": "foo", "12345": "baz" }; ``` ### rand_array ``` MIXED rand_array( ARRAY ) ``` This function picks a random element from the given array, and returns it. ```javascript var fruits = ['apple', 'orange', 'banana']; var rand = rand_array( fruits ); ``` ### find_in_array ``` BOOLEAN find_in_array( ARRAY, MIXED ) ``` This functions returns `true` if the specified element exists in the array, `false` otherwise. Unlike [find_object()](#find_object) and the related hash-in-array functions above, this is a much simpler, direct compare by value. Example: ```javascript var fruits = ['apple', 'orange', 'banana']; console.log( find_in_array( fruits, 'orange' ) ); // --> true ``` # Known Issues * Serialized XML doesn't exactly match parsed XML. * Unicode XML entities are not decoded when parsed. # License The MIT License Copyright (c) 2004 - 2015 Joseph Huckaby 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. pixl-webapp-2.0.3/fonts/000077500000000000000000000000001504641265100151015ustar00rootroot00000000000000pixl-webapp-2.0.3/fonts/lato-v11-latin-700.woff000066400000000000000000001060701504641265100207450ustar00rootroot00000000000000wOFFŒ8AÀGPOS€ OU×0GSUB ÐSpŽ8ŽzOS/2 $[`Úç­cmap €´Œcvt *.È fpgm 0årZr@gasp´ glyfÀU‰½ À¯ headeL66üÝòGhheae„$ö¤hmtxe¤n[AÕkerng¸÷^Dª¹Ãloca‡°¾¾Õè©êmaxp‰p Èöname‰Ä`5postŠT–AVø ^prep‹ìKK¦•xLÏÌ\AÀñÿÃññÛ4¨mÛ¶mÛ¶mAí6¨mÅÉž ˆm\gÏù­9ƒ$iE?Ì~†Ã_6{Ý ª`d2P séü5+ðs-`芩{âRÿ`+ÂEá"§Z¸¨bêV±'÷ß9i÷ƒÿÎøâþ‹¿2÷ùï¬;þ;{˜}ÃýÇÄ_yu{œjN:ªP%’©`@~v"8•ü$óSN5ÿ]¸HuSÝtïÜpn¨nÎ ½â¤KôOåÜ9^ÿ]0 $Tz\œÊUýO¸Hg‘ke.0©GD@OX(lêR8 hH’ÆÂ¥9-ñhC{B:Ò“Jô£?ÕÄ`j2TÔf„¨Ã(ÆR—ñ¢!“™F#fˆf,e9ÍYÉ*Z±F´aëiËfÑž­l§{8Hg‹žœ½8Ç%zs…k à÷Î3ާ¼boÄ >ò™™|søEй´"‘YNð¿Ïº€©,»~¾¯N†2d£ëKVè*aÝw'ðÖwe·î-#ËàÔ˸áPyè¢ËðšàVw§§Ñ=ýÕ½I~ùÿϹÇï½ïÝø|H+¿&fµÜ® yÊwħCQ|#“äe×^¡D¾WÜ'îqŒ:}êi ‘&ši¡_Ûb˜4#úŠcŒ3Á$SL»6Ã,s\!£~Íz7I†·½ù›E69á¡°;¾âq#Ü¿n&Ÿ ¹?¾ŒO†‡bix$^—Å…b’”°ÒØ^Õþ½b™Xnü cWR¥\­|8ÞŽP£î„X«.%ou½v:褋“œ3g¿ñdˆaÒŒ2Æ8L2ż5,°¨FyÉˬ˜o•5û]7Ø”o¹¶Í·¹Ç 9oþ>$È•çq‡]ï‰vþ«PL’öQçz= 4ÒD3-\Ôç—馇^úè×w€A†&Í(cŒ3Á$SdX³¦M®r¯¶ÂÍäS@!÷Ç¥ð@\eò*Zi£:éâ$§´;Í⨮ôoÏ÷îzÆ?®å¦˜yÊ7‹ùÜb!{â.gSŠIòl|.¼¬Ï+”(ï÷‰ûÅñp8$–ÿUùkñXx¯ü}±%”«B^I•ü˜v5òÿë]iÕ¶v:褋“\´¶K\¦›zé£ßšdˆaÒŒ˜wTcœ &™bÚµf™cÞÚ¸"_4_F›%åeÖØd‹mvø¤_ÇÛôÁ%f“q⟠1Wý5ñL¸–Ã-N})ä©»YÌçŽøÑP ²'6†"ŠIòŒ'äÙx4¼¬ï+”¨Û+î÷‹b[8wÂ!yiü–“Ÿ ïß»C™ñÊ­¥Â:*©R®V>î G8¦OúòZõ)yuÕÓ@#M4ÓB«þm´ÓA']œä¢õ\â2ÝôÐKý 0Èä1ÿ¨8Æ8L2Å´k3Ì2Ǽ=-pE¾h¾Œ6Kֻ̊µ¯²æ¬×Å 6å[®m³ÃÓ¹CŸù‡;”r‡~b®úÃõ!O¼#~9ì1KQü~(&ɳñ|(÷q ®þùTõ­äO'Êj”Oˆµ¤äuÆ«§Fšh¦…‹æ¸Äeºé¡—>úÍ3À C “f”1Æ™`’)2,™o™¿ŸFêï§!ßrm› ì|.ÜäÙº™| (¤(~7“äç\"îã ç©4Nx»×Â{Å2m+ŒQI•¼FLÑ*o£:éâ$ýÆ`!†I3ÊãL0ÉóæY`Qߌò’ñ—Ùb›7‚{g7›áfò) ¢øëPL’öq þÖN~ÞK™6úWR%¯S´ÊÛh§ƒNº8I¿1dˆaÒŒ2Æ8L2żyXÔ7£¼düe¶Øf‡„§ó‰E69±!$(—Ws8|4áµÊ+¬²¦Íº¸Á¦|—žÇC‚=æ¹È%.ÓM½ô±¦Í&oÕãõ`MrY&“¤„}ô3À C “f”1Æ™`’)2ÜàŽü ÜL>Räé+&I û(S_E+m´ÓA']œ¤_»b˜4£Œ1ΓL±¨OFÌñÎßrÉãAÏËC<â[óq±N]= 4ÒD3-œÓæþð6_ Yd³;Îú¦Z°¯õp3ùÜücŠ…Ü+ÂqÍ·ÕAßV'}[=——zwÞK™6審0N%UÊÕʇÍv„u'ÄZu)y«ëm´ÓA']œä”yNs†³œ3×¼9Xt}IÿeVŒµÊ:l©Ûf‡,;ª TRCŠ%–Ùb›Þí×<+!A®<:êi ‘&šiaM»M>îÉÎÓûÙ%f“KÍûÁsÕ_SáZþüÃÈÏþí‹õOß#b!{be(Ò¯˜$ÏúŠ}Y¿W(QÞ+î÷‹â‘pH,§ýb¯yÖß+ÿÓW¬5ThWI•rµòawñÇô©QB^«>%¯³žzh¤‰fZø__¹­÷—馇^úè7îƒ 1LšóŠcŒ3Á$SL»6Ã,sÌÛÏWäú*Öf‰eV¬}•5g¼.n°)ßrm›nôlÏYYâ.ïK¶˜ãÞ%ÈúqL{ž—ÂÃ~ƒ1Ë£~­W~BûR®O5µÔi_O4ÑL çô]Ñf•5㯋lÊs­d4d‘M©·öÕø»ð^±\¹šÃáµÌ»¶ÀŠ|•u6¸Þ~ºÿö+˜+Ï£(þ<“¤„}Ô©¯§Fšh¦…~×dˆaÒŒ2Æ8L2E†¿þ¢^e/çC»¬/[|06ú5ø~x\|B]©Ö¯ÅLx¯øAårmª©e^Ý+òUÖ٠Ⱦž´¯÷xs†„˜«|“óËoó) Ãíá!q×÷ĹPd”b’”°2m+ŒWI•¼FLQgÌzh¤‰fZhÕ®v:褋“œ3ßEs]â2ÝôÐKýæ`!†I3ÊãL0É‹ÆÌˆKÖ´Ìš}o²%ßf‡;ÃÛÔd‘ÍîXëTÖÂÍäS@!÷ûF‡bÊS\——Æãž³×Ã{Å2mÊõ¯Ð¿’*åjåy­˜[ÕµÑNtq’sÆœ7΋ÊKÚ/³¢ï*ël°¥n›®·òƒî«ßa1›µ rÕåñçUΪâý›³òzÿ¶Âãêʵ¯¦–:mëi ‘&šiáœ>+Ú¬²fìuqƒÍXû))Ùxc`d``àb0`pa`rqó aàËI,Écb`a‚ÿÿAò62åd¦'2ðAH 0#³!ˆæb)0ÍÄÀÆÀÃp Hû0\’þ`Ýžh Äxc`fQaÚÃÀÊÀÀZÁ*ÂÀÀ(¡™w1,`üÂÁÌÄÏÁÄÄÄÂÌļ€a}C‚7”Tø0(00üfbóúWÅ8}ã* w2HŽ…‰uR``ž.óxc```bf ’Œ`š…aVaP²X€,^†:†ÿŒ†ŒÁLǘn1ÝQQRSPR°RpQX£$ôÿ?ðÕ/ª ‚ªVPª²„«büÿõÿãÿ‡þOü_ø÷ÿß7_?Øú`ÓƒÖ=˜ñ ÿƽà û º¦-xc@€ÿ {Dîc]ÆÀÀz†…‰áßFÖmÿ?ÙBÿ?þ›ýã~x•TWväF dORÎY”½ aÅirsÎä„·þÚ¼¤ýÓT:‡¿yPò|4(jÆÙÞ‰tu!4šâY:rç,ÞÛô, ÅÀAòë[iíý(­þOiÀ_¤$oߦ¼Ê|’GŠeÉT??•˜@$·ÜÛS:§¢ÈI¦ß¦ÒµiE=p¾Ë²Ì¯›euloX™àX:øzúM¤“¼Ke"é^xÆK\,ëg;à5-Ö!ý1ýc× ¥Uû Ée§µë¨_pN§ulÏ×ü„|y5Š(Í]Î{ØÛ¶b¡žä]à8“iµÞÚ†JGHc7®s)§€ËiüˆYg‚ôÁhíÆEAˆ-í£€Åäר¯÷ã[Ùâ eèäçãq-¥ç­pOL"Þ‹ c B…2a=rкœ™1^\¸²Óè†2iáœq” ŒSm|Þ¥LÏ`²,ŸíøA€Ì§¬Lveò(”iK4¤q,Î1ŸÒ?;}Ý6c=T&ÓGbÖn…2ké>tC™ƒ¿yÅE³¥1†Þú/ž¾z/øQ9kÖqÞÒ3ŽÅуüQ( 6ÚxÊâ߬¢ò0–lÙðÖw)¢¡*{SCr~,†ã‹EcÖV`Ù",šŽOµ*¦{VDLô¬€ÊÊx•¢j¤Í1H$NOúÕ»ô²I-ò/›{­í,Ž™d2¡‚+2œ´Ó¹ºšèfâNYZI~ú6…‘ûÀNg ´qqsx€2tH•–£ZŠU‹a8üK[‡A÷b_NÈSÕ›øÅÔúdcEɪ–C¥½º~†*×*·LrŒµU_»¼®~ͬQ÷Æû˜FôŒ1Š‘bãæuva½©&ÔôOø¸žˆºŸ¬c±QK®êô‚®{¾i™"­|€Ëó,‹Ê)³ŠaݹßþÞ½ýGößr|„\»šƒ OÏ}%’yœýÎ?ø¿±¥gVWdø[+Køc+ËÝ¿ÍkÏÊJ·@î:Å£¿áà¨"™õûÑ4]·_ DŸQÄjµÀ–“x|ýÔ ¿nÐ4E½¼Ï—õw‡du"duäàwZûu¹»ZnÀu½üèÞÐ ûè ¨Õ{ö­¬ŽÔM Ã mé65¶at­¬ØVŠ*ªØ‘¢ŠzÓÊúˆzKŠz[QE½£¨¢Þµ²1¢ÞS£¢ÞWTQ(ª¨-~Yyxõäú „æ ÿÿxœU”#Û=ç¢*Õ¨°’~§’Jkbv÷¤kú ÚoÖ¬±mãÛ¶mÛ^ø¶­¥Åo,>eÞ½ÑÃx¢«}öªsö¾'À` €…ø‡À„*LÁ 4mÿLcõôÔd½V*Œ$bá ÛÅHŸ:¾ø™}·Û÷ÀP‚„bd9 ‚%(¹ƒq9Ó4 ŠÒ‡C•sUPcZpñ3GÕšëS) ¢X×%í9Õ˜#h/܇ª:Ì—âÚŽŸ©{X,DˆáәʹÕ`å’e&tb&ÒÞRƒtÅ2C0îŽWÅ÷?õ×Z~e44VȘÜÿQ·-®Šæ2É|2èL¸6ÉB8”OF* ’Fó£ôÿék艇ÞÌžž™±Ö˜ªÞ75–Jú÷_L•ÇS£EÓœÐ]zøát¨ à ¡p>iÉ<{Ù;×ñ0øåÕÿºi†¿Û:ÀðK+3.µÒDƒ€"C†ˆBB(Št)õиo1Ú¡[³ì»5‹ûqR)Êc4h'wF©˜™=Œâl‡| }ÐíST3ƒe7}¢ö8?ìôXÑZ,Z©EcµÑÿÐÃßÙõéƒ?½‹L?ð×ÞIu$©‚ô<Ÿ¾šüHÔ² 8b{úûú~Ãí;} SºÞú4d€!ØN˜SFEÊ ³H÷Nån`¶bš$òW¦ÒNCÕÑLXYtÇâ΀šKŽ)~²¡;ãjþùÙìÄ;0öè¬ýÜivN<÷4ØÈl ÀWW¬¦«åR!›M[©d"¾g(àq9û4Šn™Ëz‘Ë*}€0× ¥ EÝ5TÕþ¨³Ÿp.omŸƒ(Š•y@Œwt—ÛÃĺE7¤«û¸ØUÔÒMeˆS†Øµ6ºU«òµ1ˆLÔTÎd\²E!gq¥ã* Y.Uª^“ê8ŽÞÖr‹^³ÚÀ)ôÊZô ~çҢ*A´µrbJ èÌò…ÈâÔO'5;}féR,A¿VúWÍUÎ5“éóK?Y>kN`ãÇ^5_>\|U,NþÈO‹?Á·4çL®q,4¿•÷D“ºç>ô³R8;2Ñ<‰o‰öñæ7ÄFóŸÑ”îYùú€Âº«q6©œƒ:ÌÂ2¼Î¨WSfh8àw(¼ë½Ê|,,²¤@mñe@#@”€" …Èb²ˆÚÈ‹ƒèf1%hÏÜv RiŠH& ˜⪬+Ï`ºÚîŒo†–ÅÕ›AYK5‚–}3$M#TÞE'Ê¢bIî4}2D§øó+?[ê‹{ü{SÍÚðòskW_þØÙ~’ ïrŸ|[}Zùœ£e;mνòÂúÆ“?u~@áëƒùÙtâ䙿ðpÖ¶¢Õ•B@síßpÁéŽõá7|•/9ºôÚ‹ üYCÖwÝ[+»|>o0¤5úË›Ÿ¹wÏûŸ¹âx} MžeÕ'bƒÍ“ú%òkr•©ãE5ž›ñâ°ú꿼†<¬ƒ¸Ûöoݼ´0k×*ùìÄX2!»«ÂZÿ‚!¡F(€JÁšê3BÛMÐM8$ä|€Çqb"®të8—ÁŠEñ(1µsùj×e¬?Ú!€ëÇ;:7±.%ÈHA0*¦`]—‘sçc ùöº^,×D©”[qˆ†`öhäwt{x§c—;\^‘±†/"Ö ÒrVËE·B’?Vw?mvîi; …O››{úÎÂ×ýér$R² Ã*E"å´Ÿ¼g ûüƒ?H …?÷‰ð¯Zð”ߟjÁlnÓ ö {^°ióKö‹{_²yr%g¹•ÉÚbÆçË,â9çÐÏv¸Y‹Åë—$p ØWÿËß¿$nõ NÛ>„ù …ÜhÚŒ{\}*Ìâ¬Cú)"´™BŠK‹Vˆ)4ƒâ'H(AZÚ‚ÐVE!.¥L,82&DV”Þ‚Å—ØwÄ".çœ{xÏ-òH‚òc=Êâ| ‹Á[–¹-…qE…ã’ËêÑÈ!®v#ª$D;Ogh×a”V[×Qmµ ¬˜uíRÍ hË)•ª8!oÞþòCuþNªRwС…F*æØlfh0´*ŽÏ‰(d8ðv\X‡Ùÿ{ÃÃCƒ‡÷ÿ㥛ßó¬å‰•Sv9_=ñ–‡¬Ùmùí¯n¾`ã<ÿReçåF½¾á¬i„ók’‘Zm*Ѽ0óй@èÿ=Òüë÷èÇV¶×O¿uÿ½O=¾9²øê óðQ\×Þ÷Ü;³EÛ´]»ÚÞ$íª±jHBÒ"!zïˆb¢7Slp£¹a§÷`§`§8†¸Çi<§9Nï½'þ‚&þžýb£á»gÊÎjòµ÷’ ;÷œ;÷ž9çÜÿ)£dž»ßú{ÇÒžxÇ88Úz!€Ø†dì1¯PAˆ‚8˜æõ}f ˆ(T|EA1u€‚ 4pˆ àÆF04 à”›¥sÔfH1OX¸yºüðz P\}‡üÓh P^i=öíÓ7öG£ý§o·{l+†éî—¤]Ù|fOÀ4°ëÏ/¿zðà…—ÿ´«ßp»ƒI;åÅÏÎÁ+ÚsÌn|N+­Áú”ƒÃXÓxv·» jx…môcDãJ|ÜÿXÐí˜úwýéå ¾úòŸw ˜³ÏöœŸ3“ Òï‹'-d#Ê“_[ªƒ²<Ñÿ&šësµ©dÔçrÚÌ&ÁD!‚’#6@ÙæÆ "D&Jê/¯ª˜qÕvpÈg$×»&@‰ù¡Z¸½Ð'Ÿ’F_p“5¾0ÀBÑ`‰8?Ò}Ò•t»“®“]'Q‹QÜ+-—r;‘áw]« >íÉ8í>ËŠá;£•–•ë,~»3ãyöioÓîç#Ë-fð­^iñÙcÙgpËÉ#BNx7±¾‚‹~µT˜MFƒ(0ʱZ®6€H¨È÷%a” Ã3‹¢@(^€IqoÜ™tÆÛâNz ŽH7]:w ï–Ö?- Çž†pÖ¹ÒaDI„ÜP0T ÕôØåÀ A=¦¬(gÅÏ”8ä7P­Ü”…œ,â%.È¡£üe-êèeÜôçz3í±ÌøŒ+<~~[Îe1Yb±jC´5í}F`ÕM=‰æÍ›‡³5ŸÉc²&Ú"þÚvdþ‹n¦ÏFÒC÷Äåã/{íDyë E.ajy“>ÿN»ôØÏ÷î&· ˆ¨…Ùõݹ F1ð}׫ܭ“vÝ´ÿزaêÛQM+‰Ž—‡Apé•K±°ø±†‚ÁbVמ?ÝJŒ›üì |X+!$GñY~;Ë|bÿÜJÕº7Ò©ÎBÃ&æOø­â³ §ä/½*,Ÿ’ñÕ¾B…†¦M;‚£ cŠ#.=ЍÒñ†>î•=a|4a(/ªÆ€‡Ê‡ðÓÁ%CYp•à añ¼ûŸß¹ó쉹sOœÝ¹óùûç]jœ5ÜÞ¶vfCÃ̵míóé¾(ýí©eËžÿ¿>ü—ôò—>üÒqã¼ôaõŠû!BƒøßÄ; V—³Òa·™@Šx>i(PÀCOB<-Qœ‹Ð‘tQÝ|L%U@;J¿„Ît…ìX$‚€ª«Óâ%n¥‘Ž;[;´ÃZкÁiö][µÍ7͆g¿ÂÅß>ËfŒkÿ¬Åä´l}o9Ÿ%@"´òwÙLž*TÉÖ¦w¥4Cs1Nn¨Y€aX‘W_‰¨€Å¿P+´@ñr&¤ÆpHFgqPÜpë5¨e…J¹ð¢n4·\ âyî‹ãè> —µÉê`ÅÒÄÓ¡áS{û›ælî’>ß¼" »k›.Ƽ~xºcIw42~Vó²Ý~K}ÂáºÃ±øö‡Å§º·¾oåÜã·ì™ =nw4¤ß5?Ðî‡gƯÞÐÙ:§½zhf&Îü?¼© õcˆÛD?bC̸™ÐÕÞÚØPŸKÆ«n'bCs1ãÖ FC¢eÂý·Á(r…˜@YžˆD ¢ PÆ& àéŽÄ¤eÜ®>rŒÄÀçÒgÕ'òâºéÿÖh˜Å¹ð7~ÐüUMG;Éý†F)¿•6|;™Q/gçðC»z¢Ý‹Úï¸mîñLJ·}tÿÂȶP8Xgqå&Ìß·ª/ê›|çöåû§„nZ½tßšMTjoªsÝñ9ƒ›f·»ê¹uë©]ù5'V6X}Þ䤶x]ÿ¼ºÖE§!Ö9·yæ•Óô×F(ñ" ‹“Jâ#' vŸ·ÒaÁ3URÔÿˆQ;Sã4+U|šN…vÙZJªè¸Àº„U¸Mõ8 iŠóàUÓlD9ÀcZ1k†zgŸ}*¸ó§# dÄ}ôåŸ ¢ÔÏÐøýž°tVGýÙ;¥±éµ#ï& †âºiç˜4G~[¨ÌekÒQˆ]•&<´=v׉T„Z`¢¨À"„‰ˆà10ÈiVBôü¯ PÍ×EcF- ½ÂȨÊ}2}/ŬFÿuñR* öisà%nTC2ÎÚÕÓR×>å@uÎX㎳_ Ì–vµ®™·ëѽæ=ðÕ=“lYžšYUh™4T˜´q0yבM›?°Ÿ§{Úú¥ï{&íhåî/›â€cS"–‰ÕÏÙVØ{$SO¿¤äî–pOã2“qä$zÒÆúTÂﵘIÂEMJUƒ@dM"#‚’,°(ÆMãVŽ –PB[KtZ/ÒÆ$ÓMT#g%ŠU"ô•Z€nÄüŽ&)å(¥™ÿÁ½S§ýâ®=ÏÜ1¸` ,s 6üRZ`X³«¥ßÄ'ÞØ×½vJmfòšîîÓìXû¶n]wæðÌéGžÛµþ'{à;U‰‘ל‘Jo•Ô7aíäšÚ©«;{צj§¬–uóFB„©\nN²‘ ¿:íV³±ˆ%<ªÏg s‘:Öd2Â¥ h§ƒ ž‰Èð† —¡ ƒ„ñJF¢½ƒWS½#¿rö¶=N›“¹‘™þ„xjKs·ç­÷%Ç ó&Kø&rno6Ï]ÜÏGI ç ÁqÍñîÎæqu5±¦x“ÓA¢-úûÆ*eDL F¦ädŠøÈJoDHÅã³^åBµdµ/á/åñÔ‚ökËz²„¯ª:ˆŠ¨¶b:Õ}×hVc@På.Ñ üM'Ýôìái‡ŽlÞ4°ÿááá‡÷lÞtäдÃÏÞÔ³¢/vxϞñ¾=æç½ÁÖ¹sÛÞüüÊ Û?tãmgrõÝ5ÿĦîîM'æïúh}îÌm7~hû„³ÉÞÅù}ìË/îMÒeúæåê¦w&Óëróú2Š­ÝAˆ0W~ãȇ[ˤ~‹å®ÛZXu·"*QGŸSñÝE[K¨´@˜(2K÷ß *%´­”’Õ©ˆHÐE ²huô®(æ9ìEAúäÜ—kÆ;'M½ã©í;Îðç&¤¥'¹?ÀYgĞߎTÿòöñC}‰ƒ»'¬,ÄéÄÖMܸúS·MŸ|è4ÿŽMmRmUœŽóÖzbq8Ó½²HsŽ·Ýšé"”ìäñ̃ì¯2fÿmf7˘1Õ#Û£¥Q‹RZº2…W¡¨ºæ™kΡD”0BQ²£Æ12Ї®KŽ09Ë~ï, ®PÑ¿]VhB™òøe!yòH¡¢©!¬ª2ÅÝF0ì{ÇV(aBH5Ö®­ºT®Hã@šB‡¯%qÙÕCíòÜþ+†ÞÆrq]-§?£Fäò}ö1a'qaþœ`ɹe¯,Úå"„0À=¬Êã—Þío ‡[ü°Åï¹øWúCøðiOÜúy«ÓhtZŸ³Æ=§á#Š_8ïbû aÄHZ jb„ð÷QÔB2cç5€¡Óq¶÷âÝø_:óZõ´Lø'¾Þ¾^'®×Aøz;Ptit›5iÌ ÷O¾Èúîp ®Ùê¾ }Îgçëü<®SZ;,¯3}鬖û¯,é…5{w縦ºšTÂí2Í›Æ.„+þÏIÄ`zš×ˆFzl^…­êß{Ú ÿÞÓP›®Ùs 3!8l¿&½(¢ .ò Z®Yí`£zZ¹Gð»B@:1oÕöÂ’ûׯ˜=¹EôÒ õý3ûë«›j:çtd©Ê[.+K'#íµþæe‡fÏÜ1B[KìbýÄÎö¾¹í©Î:_ ˆ¼õâ˜õêKÃ8q=Y@ÖB!¶h!•Ë®]´vÆ´¾žÎŽ|sm&r;"Y l¨!Ý\Š ¼PA‡€U0 ¬Œ¢!"ƒ˜'&"P“/I£ü_V3e`¡Ä ÔvD£QD‘‰¢EfÄKãÊY|ê!ejó5§6+Sƒ™¹>tÍ'˜« ËõÉá_›œŸo2 \}v%« Å€‘'3xËp­öcƒ(„d"•QÉù¤qÔQOháĤ-ÞîÃ3ûVöEÃ]Kö¾cÙ¬í-7V:)ovÆøø¼·sÿ¿ûÄÊõŸ»pﬣ7­Hyìakb£ôòçž”^üÝ­ë8ýí›N€øý};ß/™‚m͵¶i±DïÖ9ϦLí­Ÿµ©·ó?¶Ý0¥®Ê'ÝWÈEj«Ìm7ž}Ç GÖ~æåcÿçà á´×[6iñ®AþË[¾yæ]›»o8séÁ-ÏÝ=çÁʺI-4´¯ØW(L!ã&ñ.Ž«mÄGެ<]eåY@‘ê«°&‡£rÙž"<*+÷ëDL`­:¥^à¯ÕÇÕ*?Q«úD@Ò¼¨p(Žg2ŠCœqßiçp×w“ [G>¹ÕÔ”ønéøöikØn¯¶%ƒP#ýÌŸO]¼~[ÒÒ²4Ny!Âòý¹IŒÔ‘g >‡¬Åybž¨ßËﻘ´½Ö[ÉXÐ:WT±Þj±¬eå¼~ÿÀ¥ÚY ¤¥© sšf´…>?¼mçxŽõò¦•Ÿ= äÛšt¸ºÊçtEÒ Ål@«ÊDНcʼä­5ë‘»\œÖ1žæÝóWá¥ü'FE@^@ßý¯1 Ñ™EÐÿR3vµ§ÐìåäR™½÷‚°ÇW¹y©t鉇¤KO­ÞzœŸxèÂâ!SÀçož¾sÉúOß<8¸÷ÁËf Ä+|°È¹{Ûžíç ò¡ãܶ ‹Z]©ljÊá/î=ð¥;­ÁºˆÙ"ûqÔIñ=\'­$€yqŸÇí4Ç΋[JòâÖkäÅ×™çbˆs%|.µ(.+”øžÍ_‘þññ‘ŸÒÚƒý+›¥ó…½ŸÜ,ý›?¹·@pRzý+›yÌûeéõ“û¾xç䋆Éw~‘€lg ¾' ypÌš“²³´º8¥CO$Dn”7)R–ÿNò1Bð¿$£óé¤=Ký¨’,Rã%.*[Gô¦þWHŒÜ ÿ”Œôö‹7‰§~,íøŽ´á'|„”ìÉLî)T¯Åé9¿Q;ˆR˜—wde:.pÙVLn!Ãv·¶Q4”)Kd—-_Y< üAº€K_ø“‘ÇÅSšíöȹú?a÷Am&­8fƒ€ùú¢·ëƒ=X€ash ‘扞,1Ô:?æÑ;‹¹¼+L!.'Lë“•LàehË×ëèpRŸ/ªM§y¡ß™ÄôLÜig¥¶-ø†d¢¤w€e—ëy2ßuì;öe‘^ÿÏm›Ÿߣg€ Íögª±yëú·:ãô§ö}noûÆN; ‡œÙΥݰQzàÜÝÓmö}ÕöøÔÝóÙ§"nàuƒùFÁ ‡ªƒUèíiIÕÀŒÈ€Q¹Š% _€QUƒØXTTnSòN*$Ï*t"(–““¯q.ŠfuRäfr)eÑeҒדÁ…J!'ë<Ò—É»àþ@Gö¯RÀŸ­Š·àõWš›*¥ ž|ã"é5°±©^?ü3y8”±Zë#Ë«ƒÔôÖl‰ýžë‹H† &ÅŠŠÝ1n%Žd´¬­À-ç·õ!¥¯ ¤ÜÕze/ªÅ¤e 'éÀÅÓ`ûõ¯ÅSÊ ŸãÿªCdœJÄcŸƒëm‡˜o*n*‰µÜQ™Èå4 §©µœLíeTNÊ”PÐ?—v/`Õ² >˜”GŒâ€—ß‘{5mtrPqÂgLNoÐõ­óŒI3ͱt¬Ù! ìü·\A¯Ë§éÒØ`259Æâ—‰¾9ÄN{c_ E£!Ö﫾È{”¨É司«ÿVEÖ¬U~Ÿ×nµT”Ô¶PE¨ºEÇá”o£á€ !¼G)¾ŽÒQ¼Æ™ÞûØñÝâL–좮*·ËBß໸'–õŒX;ŸxŠ™ö‹É0Ûñæ'Èžõ/r_'ÇͯŠ#ÿÿûFÃN„]eA¦8"c¯KÎnÞ|WØßû8Ûü)°íûÔfŽÂú…]xaÇŽÀqò$8^ØqÓW¥ ž<ùð÷!*CLFÑ÷ã°“jÁ à¿lâhÃD „©xŽBœ!òr(ß úÎfVtlß‹{éUö_\—zÈ ˜mÍÖb7>¯1Ò¢­´ôˆ1 Å—Y¯¼\ßÁ.zPs‘α«Þã®Ì*¢ `Œjw×µy®^åØôJ·ÒÓ/§  Xx’³_ï·„=žü¤å½ãt†:Wí?¸UçÀíŸß?xôæ-ÙÙ–¨;Ð5gˌ݇øÐ-|¨ïm§wÞöÓ™ÂIŸ+š‰ò¤gmˤæL®sÉ¡¡5Ÿ¾uZu~rîv»»._·m}çÔq‰ÚñCÇÖÎyÏžÉs§ U„Ük#Ù}nüª‚å¢ÝzQkAô`¯ìœ-˜-CîQãDÚøÌ˜ÆÇà•qq›pŸ4KzMš!žúÕ[Â7~¥`øÛùYÑ,ÛÆÎ‚ˆÇm· $}]LÅg„èoY[h-ôâ`!ªÝW@fR'À‹BtéT^“òÏ¥?}ó¢°-øÐ¯ï¹ç×\¶†…7ÿ¬uÕÑlÍÅÜþ‹“Ë–üÅíüß'½AF¡â1¹V¾¬`â-h6Z‚L2ÜPZV}íú%ˆšòÊewsLI–£Æ Ñ`RøY²Þ Q»? ̉Ÿ<óšx*Q}ñDnCçî¥l—?óæ®ÏJˆ).ãªMk]m, 9í%GÊgbL©‚'À`ZYgù0#\¨Zíc¤t½n=á‚qù>üåV–JŽ’Öß'›L¯›F“Û!žiœ“l4CÐWit˜^76'~ õ½&3Ø ü?ÒY¾ÃxèâÉØŒlvFŒ­ögðWavÍÔ([Hq@õKw} Pリ"DÝ»ØÏ÷âúU :6 ¡¨[vBië8Jß·>XЦƒè#Šzé$¥úe†8î%ÐÞç¿Ð‘ÚA-6Ÿ—ZÀ TÀ4Ñj‚_¨Ô¿‘^«Ž[¤3B…(}Ù«ºHÿJ¿äpŽœ·lð‹ôȬ[ÈE³V¯Y"ΰ¼ÇKÿ$DLò=zx?°Éã2Xq‡VF˜ÚÁåú l-Etá¾ì~ŽP4Q³Ì+½6rá|²ÎfÂn‹ûÁÈûõ^¡ù‘—hžíI¥pnV:3+ ðe0_BˆðAÄI·¯< Žî¨¨‘Õ„RÝÉ/²¼ôàcˆ”LèñGB»¬¨÷›!îU¹‡ú`p$H_yëI»…þu¤Q<µ¶1óÖ_²Ù“®Â?Vôi·ô"µ,ÄF†píüj«0EÕ¦paHG‰|V軨dòÂô1ýL¯J§»ÛWZÌZ°or7| ¾ûîpZzÑØ·:˜~ÒÁÕøÞÿÈ{‰ÝâKÄŒ½Ä| Dï%–³¼<¥—˜i%J/1ãÓ*ÇzhsýD'²±f†S|©!ûf¶±Qþ†àEøÓè=Z-á±’=–6ø”ïQS³H: Ñ÷È…Þ Zi†²Cø€ô"ß¡x‰ïð¿Ïâª0³€ØÉEz &Þßkí[ U]eƨš%Tm¼@ÕÅÇÉÛUëJ£Ežô‡+N:&“Ïñ!s$ø%ø™¹Ê ñP>l©–~㬢&ŠæH?gžK#(N~eª•Ua$VòqC¥,ò@É#eélíá:Ç<ÿ~ôÁ \º$œ€rdÆê8ƒ@Ì`.îÒ&¿K<œòÌ £Þ¦;ï‹*»ûs]ãþûBI×âSÕÁ×9KKÉ<޳¼¤–´CoÁ—­knªk϶§‘PÀïq[Ì”˜µ/¤&ĉÀÕ KzØôK™ ç(jTâ1xy¢&PZaA¦Zù¹p=s G’'º¼¶÷ óˆ„ "â\}Ft]Ú,zíŒ+L@ ` 0æD&ÁO¥ôÉ J­V)Â?Ò>[ÇÛJ{#äñ¶ñê${dG²¶ÂW3cîPÇÄísê{Nüý±% &ÌíÎG)KÙ7‡î[Ó"Å2…ζê­o_šžåMK¶¥\ÉÉ»v.Ÿ Lذ6Ä1±{ÎG»7½c‰ä²‡êBŠÕÙññ‰ã•ÚÆ±K„î$ w|ÕAB±`¶:‹ñÖ¨Rms"$fE…©(xŠ,‚¦5B1Øò`áˆb<À(¢,åsÊëfD,­jB[ #“Ñ€XÙuø¦!ø–rfàœ¢îâ<ÈêÐYµç¢ÿË_…U$9ÒÈ®}Ä0Êœp5&F±Â…š£¥ê´çŠZ¦N­¶8{aTŸ+–»€·T;# ÅÞÇÞèùÀ†…÷¬ëèÝý±uM³ ‹!€žYÛš²k»°›,Ü>K|i䩉³ÿç¡Ýç˜çNŒ‹ }lþÄn©µµ.»øÎeCgË^¥ïÒö à"õ@ ölm,Rås;‹q”EY A ¥ FKâ(ÂSµüà4c¾nV¯ ‡`WfÕéRF Áþ%9öJyùEÿ†ñòâ"¶è&+‡^‹,^;•l™¸fb,Ù¿ª»{Z>í8¦Ôݸa{û œ6ñÐçß¶ûÑAVg¯ô‡ýMKΙÛ’†p2ìjžXS9åøWí<{ßÜ郊öðwpžÛa©ƒé_0$ Ôëüò» UP…v¨˜“DžÈ¦²ï‘›ª™­D=²…È~Õ@ѱêæt-FpúRó‚nNåÌ”ˆwl“Y£Yusº"+§Wò.iƒ=âdrÞ¿çZL˜­2È1¥r¯,‹£øÛDSÙ«ãö%ן¡XcZÜìðÄ&Ï[Ù¾ð^nQ{>ºnñæ7¤Ø¬­Íٵ㗣1ÍnX›êÊú'9wh7¦ŽFy3ÿd¡^lÉf¡)ÍA”Be[ú«à"iÒ•…ªš l]¦±¦1\íqñàÁLÒ6kvÕêA©m ŠbW<ð-ÉLj(j_mX×Ï[bYùѼœ’3 WŸ§Ü´º¯ÅtmÛÒÜZ\veø>J-ËÏ\œ{7Z¿úc{ûE›Ï9rÔò®–Lç罇SëÖïØÝ¾å™ã³'Ýúôþ­œðÝ`û|ÁÕ»ïÑ¡´×ü‘w…!«E1¯{¾}lÓ³wÍ=sà¦æE…´‚Áü„ÐÇÅ/“09ƒY4~ÅúNÀãœA1‹–sB.“8¥yfA rzŸ{Tÿs²8kJXLÊÙe¤D"¨_j:Ḑµè”ü¦ ¶éôe_‚¡ìðRì'©‘?TUÔXi°Ó0`äô²pòýmkO,›Ò#Ò|¼~°9i„Mµ‹‘êu3—Þ»¦Õ}KØînšÕ9éôŧeô“ÞËþ§à!]d6Y7BsçYºxΪ¹«fL›Ø—Jª\•#]ÐeÑþšÂ Œ¦7¥Æþ‹T€‘PfTs颉J ÊŽHUÐ,« ^–ö)À÷ðɦ–OÆé9)?ö¤Wï\ÜõÍg N’.Ø„˜oΕæA“‘˜8÷¦~ÅÕáEÃ}˜¾À˜ÌÚçS5=ÅWÚ§¯^ µQ¨±ƒvÁ¨~}øKÿžf¨© ¯¨°ôlyç⮎æÎ•{oÙ»²sàÖgöþÇgvŽ_:;ÓXÏ'f/ëÙönØê]°rmc¶¥º{Õ¤Áá‰éBã¤ÿrþþ†AÁS\áŠÎ`WlæÍ‹›,áT4leö•Ó&Ý6ÜÓ0cMÛÔ5aïäñþqÍ9göÞU3o^ÒøÖ—"o…(ü}uÕM ·7ÝB×ê{Ó3—-›™êm(6w˜òsÍK¾‰ ~õº*mVÄ•zG?,9(Oïç÷DEŽîè/¡ùd×àRj•Õó8ëX”2‘†Ú”îÿÒ‡Ž%GML91r¥££ ¤a‰KùàÍyø ¬^xÇ’†7–,)Ü”çèë«“;7Ü¿xd7½oÕ†®üHP²‡8-~‹0â'btìqU˜ø¯ÒLj@ÿÃK¶¨ö²âNu9^çG’ŸbLPãG’.!–Ü$vLƒ\Òˆ’Âè SPµ–ï®üo{À=o@(˜ëŠÇ:³@¶3ïÊú–$PÈÅÆã½ñü^»½ô‚ô>y¿XËû1jVóø/ÌÓŠ² 3¨©0Sµ¡@µlþ>An­ @qs>Ǥ4r ‡Ó—ñ) óÓ’©±¤…›ÎÁ¤à÷q—Qêö dq5x)–×%=h§cÈ,9ëàPrBÔÄ‚étKÔ6¦1°wY‹¹âˆÇâˆ·× éËÅI€#DèâöáÒt¯ؤ@-bz%(i/ë>Àˆª’£ *}EÒ’nQµ©Z…øòƤÓÚÃl¢,M…Ô×Û¬àÄn÷˜Ý ¨Ž‡¡ÊKÎLô÷ÒYgÊëUÂâß„k+¥·‘Èø7Ä—.>jwC·Ç/ÝéMÚµ~Éî©‚#~»”_R|Ò»{øFvè¹=_‚´­¬™F¿éÀ›Š4jNP»VÖF7¨{ø"ßäþAó†4Æ^ä•‚~­çß $yf…W÷,fÅÌJ-ö›å€ 4bÂäS Ix‘aÚ½øýI}=A*dP–ìÑy4 SÎÃÉ8AOÔ)<ÙbÚ.{„ çë®À¦dï šŰªWÎ%ö²R_jåUßÛ쨬î˜Õ¸ú`´jêâÕãjzcñ‚ÌÄq¡ÿ^0¯me­@fFò)ï¦%-Ó}­Ã¬D»fmpÜôfü׆Um#Ní,ⲯ£'â×€Ïã¬D)4)ÇD`¤J9Ó’Söâ9$h.¨„ǯvf•ÒŽu´hß¡éd²À5O¢R)FÊ¥æŒÎZÚªH\Lób­OÙ‘¤ÄžB+—‹›Dɧ .¯÷(QoÔb&np³!U"‚ŒÂÐÏŽþ솫Д~  èFS`×ÜèA¥ÅZ¼ìkO=œÐ]­;ß®a$íÚýÜá©S?·{÷ŽL›vä »o»ùæÛî¸åÁ5ýø¹·aç×ô»ÏxÛ¹ãÓ/~ø3§N}ö±O|âQBÉaé —…—øI-+øU„ð|Hm –ßñ+^Vχ@ ¡j* >9­…1¨®1z\í’SM•Š™¯ƒÑ«3*çd[#ª^y"­œ¹0v”1_Oë™±3=f,e/O§”Ô|zT6 sbeLš‡(K¨1‘ê®BKω¹r XšËÔ8Ý£Sb‡ß[•=½mÕÒbKÞÖü7̇i!ü,éŒ8Ïç¿aÊÒÉGž—SbmMÒ á¡²|Úƒt†ç:PMêáxÁÉõáz~'ˆÙ˜Òœ¨D‹ÊY X¤4‘é)}5®¨’ÛWtà:½:£¢œ‘•0âÛÒ•flf]ʘ¯Æ† Qžœè:PÆ®çd jŸ¤G§€0‘sù‚Eœ§«®}°iÚÂÜ”¡UCSríß¿¶mÝÊ9‘NKÄï¯kŸ6¾¾¯Î›²ü†åSrùÕo¿aóg»XW¥Íðôæã ‰êX]ßÒÞé—·¸Õ«œ®`$èM4T¥ùP®ÿÆI}»µLî$@jx/øïÅ $€ß'à—Enö©~/!ûB¢G3‹e} ­Tª)]%eF€ÊYI—ʵ”ñN1?Š—”²êoé ¬(v00Yà:£—Èoé_á!k5:/^µ¦˜È´)Eg%F‘?íðøôl&ÀŽ<°ÑþD«öÝvé͵þ¨ße¶ØMÓÛï­|ïÚ ®©†*Oúâé‘6·[¬‹eC#úš\¯Ï ”È75ŒêqY+Ì&‘éÕì@ ˆ•Óz‡I)F½œŽ#±þaˆ†Q3e´ªn;J¨½H]hÐ ¡òaÎôžÚQß{ˆÅÙ‡Q¶ë y'¬—éVc“f-kYxûÒŽìYµ¡3Oÿü–Ô0?Æ=tçÆûÓ{ ;!BX Ä…}R<;9ªÇ¼´.{Y—Š>Tڣ⬀±:TâIÐ;”WLþVÚøíê´ñ§ßlö»¾eLžhÐ3òËæ+šiÊÄwg$ÄP#¤‘wïZë±hxtŸT¿¤OÊ^Þ'…ÃcôIáð7ø°wŒáßH@»Op'Šc‚ŠžÊ÷Sö“»bcƒ!Æs¡ŒøN«Ãh Ø?eHUm«N±y+ŒNÛûÄLà9鹯ÅìÏš¢Án~Î^ûEE ‹s¹Å 4ã ò_ßÍÍ«Ë-nD±¼%ÕÓyáîp¤;2r¦^í#Œ ”„È>ì§rUÚ­%ýT~¯Ã@­iÊ^ÞQЇ/ë©Jêc×ꪒ¢m߬ô>Ô®Ž}Òc¯T+~":,Zƒî?HŸ…_áU?7W~…·SýŠ:á‚Ã)}Ò™p‚Ýí’‚# ¹`]êÕö€mWÚ3ÛÑ ÑH€ AI°ÁN°WR’IY–l™îr“ÜÎrbåbç£HnE>¹¦'îݧô^]Ò«;½8g‘à¿oXîB (ɺÄöî>̼]ÎÌÎÎ{óÞ÷yÒ?²Ã퇎A.taJr9‹‰Íz¹fžÑ R+¤A4ó[Û¡¢‹•à§<¢ä #0ÚÇ ]üC°üÉÓg¿ê/•Þ0X©ÀðM)äþbú·E^|û¸Ï>ÿvQ³¯¸ÉÇÈ©£^„ØŽAè «ÒFÞ+³rÞ°*Z2ª*€N.¨Ê!¨ƒªìL)íÁïÎýÎjÃo§­¬ž½·¨Á6¿çùóluNæýÏ%År\•E0 >tuÊÜÞšˆ¹íVP²1Gaš³¦%“uêqi€ ¬ÔwµtÑü˜yÑdÛ3&(]¢¶GS—=…Ncá [§m`º+ ¸iéêj1ÙLÕ›÷M% 6»¨1šŒÎÊ–rbÀ)ŸŽ èD»Í˜Ú·¹Z.ˆïîXqÇŠŽº³¦¦ÆvO6\l*0 W$wLo© ÷%«ô†Êæ¾PÝ–éɱ‚Ñaº¨aÓé©)h«¯§?ƒ? ‹Ú•<2:BÍ‹}f…Ý%‘5éÄb ~ýÅÿ3ù—Ãr´eû ·§;•ˆ×F ¬Ýb”`ž7/ÀÐeÙ0e{P²È¬ˆôÁ’EµÞXåE }€3MÅ´ »#I@riü¹ÜìŠ1 8=Î2¿’§*ZËË[+òtt¾»nŠ´ÿ¦†‹L¹ýÇHû‡úš+ úªd_˜´ÿ ¹ý L7L’ö?«:a´  ³6äEƒ¯íqÙm“^âXäÅ^À|­A˜Çà°J8ëâåž8iµñÈñ†4%âtÙHV‘ÁŒ™Äü¾¹óK•+.Jœ½a¬Tuμo¨{ÅîMõ¾€O9A8+ÒŸag'œ»ópVÀHQáß8JT´ÖEWLõ4*¤*¬Äø) 6šÆaõ\PSqÈ$Â9`ï‰ô_ÝÍ¥¥ÉJ§2YZÚ\á–ÝIi$æßâ¾–ãK°–¿Ïù¹7¶Ál69Òü4_ì÷87V¶FœÜ‡=æg{ÿ»˜¹ÇúiÒ#‚£ø8jA?M9Zšêª*‚qñNsŒ‚†ÖbÁ ‹0¢@VÖ0`cœ!ƒÑ%"ΰÌ(«ùd‹V¤F•RÝ"Bã§N¡¦(­¢Aû«ÂIׂ’†,À;€ˆQS<@ÃçÒŒTU^Ø|ùcW®¾áœá¢sJ CÅ¡hZQÓ4°¥}ü–ŠÐ¶>­{§¯½¢µ&TØÚ³jw(rÁX×eëeÏüUu4Ô9:Ê«;bÕEžÄ,¼Ëíeþƒ?@öíûµ9¸„—ÈÊ`j©ì*²ùYÄfMk¹”Ø|t"|Î56»"qŸ·!ìr…¼¾xÄ…g]á¸Ïژϻx£lùŠãP.^œsM¶ ãßåGQ4ˆ¦ñ»)×䯕c=©Æàmº!#}‘mÐ9†Ó@Ô ãj–xœaKÃ-×FØÒè‡ÈNÙÒlgH“ªóœÙ§›>³OS`ÿÒê¤c(É£X­ èáVž¶ˆl‚Š>)¦D]•¨¸F–À x0?@ròoœêsÅÉå8ö va¢ù‘+¶üŠÎëÜvé5u ›Í_•Š·Ou—;ןªËæ&[o{àÙsÏ}îÛ¶&àü¹sÏ}Îo;ëðo¸á§LEW^²÷ÞuëîÙ{ÑÊ*é¤ ƒ£ƒ»>çÜG®ëiÛ$YGdxgwßÎÁÕåzM­iÇs‡éùágϽù—nÝúà/oÞð±}—WW_¶ïc§Áv3óO£J”@ÿH¹¢UuµU‰h"Ôæi”¹Hm¨9AŽ…DòD×Ù™”U·)¢`é܈܌ˆ=JzEÍ™J¯€þ>QvEfû6ŒhvsñO2zªkª¾oÙÚ^XWSV]æw‰%Í{WB }úáVÛX=Ä›Üå¿CïŠvUú›ClaSÙnv˜ZâuåÝÓõ3f§×ZZˆÀÖ@{Ø6–âèR‚+Â0†hA¢ €iÄgùd×a4î°›n7Û­ÖÛMáñáœkF_Š?ÖùlN×[EV³ïm—«ÌÆÈ'¥öùûp%›fŽ!Mh0:`,&F•(ò 7@4Ø€HÌàDþŸéwV"±ùùÍ̱t-¬ß3_§Ìå^GÃh=þrª¨±8¿‡‡úzZ’M ð´‚§Íý[:;±(Aè'2"MøÑI<™C$=9IÔàBÐ`möí½iqR-ž3ò,ÓgäY`¶nÏÕ‚݉ ç×§ÖLQ§X=¿Ñ lê/3ëò0œb.ºÀrP'µ}"qJ“-~뇸û™On9Ûí·|á›5ö;õ……ÉMñžÙh«½€ùÞ)LµßN?ÃÞ´w÷¦[KL‘¢ô7X4[ÚÊb¥%u%„‘ƒ{ž™á·œÒ˜w0ëçá·Ü/V’µ0oóO’ycMüw«ÎF—àËÙlôUÍñ¥äÚgÉÄY{†‡o˜jjšºaxxÏY‰ÇŠêºB¡Tû…B]uEÜ9ƒ»·$[vö_VSÓY×÷Ê¿z½ò¯áîzŸ¯¾›L›Ñj.ʽ† È‚Æ º üÉZ¤9Ü2µjR# ó`ÅPÆZGÔZGÔZ‡À§š#°ƒTTGéÝì·çê¹×Ò÷?™~¯{o›«}aÔÏþ³M¸ Õ SVÀX++>@G$¶E ºè ÉJ·é)µIô4¨MÔaKâîÉ2›ta wù`á©ÖÍ]¥…•MÅ7úšÜ7ùJæe²~ÇUá³E‚ “¡ÐÍÄslw —•µ®ª­ìëlõß,éü…ç·ØÂÖoºÜå5»cË“7Â^Ïl—Û¡}øL’ £¸".õºmÔ;Nw  s¼óD0Îl:( ‰¢ã½“(-'äR’&J@ÃXË@‚w¶lì,mli﬙ªoZß+Xᲇ̂ÕSꬩ7Zªª’ò› Vt‡Á²Yª˜. 4õ‡º6xÃm›Ú|κÍ%’Ïà)0Æ‚Þr—îJ“»ÌŠŠ¬¥e]³—~cŸå>ÈÌæâ B‰gU¹¸AÃ$ÎäâjXš0Ù„+’9øNAÐw×-2¢tú˜Ãò÷"/Ù&±I *Á£$þ·•øŠ²;Äb&CŒîæºÌ ìÒ üq[Äõ`…øjÓWeJ³šÒ™Ž” +5Èâ¯vúU5ò… Žj¢u¦s¦òÔà³Ñ¨ùkÂÓ2R˜Á*®¡XÞâP1f蹚4¶§—Ü Gª\>j÷©KÚ;{Ãm[CñÚÆÍ^õš’¨×X¨žªüK"ÆýìÞ…:~’`ÙžGúªÙó`lgÐc`5ÇÁ¬æqg“@ðD4¿ 8ž2`)p ð h£ÿÏš<üä_}ëÕíêîÞõÐNþÈ·ÞÝÈ?ôîFv°û’»ÖlÞa;ðÏáƒøû¬ Â‰¶$wYžïÇfp×eoÄ]·FvÞƒó®޽™\ôaFOЭþX-n§É(p°ß¡0aÌ1…˜t!ÀŒU`ŽñÅP7Ù[0,[ÐÅ*áV¥Ú‚Lcžrð”gËáL9.›%¯”%ÑK#“UP)% Ñu …#ɸ2yÉMOˆ–·5­¹Û±i(˜šlª º…_qWh/.ÔßÕïšÂN£a zMËß±ÕÂùÈ X,Vÿ,òL· h=˰ZŠ1uæa!‹B™“¥!£.»¤(oR2n‹;‡öñ+¬v‹‡Oþþ#W¤ÇÙáñ£Q}yþ?/Ïä³²Ì×ȦmÍSLs]xœ³˜á0¬fN:x¹@`¼wèêõuu믹f}}ýúkFz»»{»»ù'«Ç¯Ùµ¦ºzÍ®‘á«Æ«ÓÎu›6­]»iÓ:ú7܉ö°«í·Ã†C4Dø/»pSRn0œagA‰¹fµÚÍ`®a4œs½Ì4‡ëmŸÙZô–ËYn§¯,M_™OŠHÝBü^þA¤CE(ŠðXÊ''dUÁ÷»¬<ÛNË$S‘cLÙN…üŬ„ë0c`üHÂ"–D’g`DbÂFÌð8³§£nn@e`Hëí:Ôzä‚nD’(5.¡J¥ÄB•ÜåÀJ`@°Ö[V’¼²P+¼‹FtšÊtô% ™q† ±€òs9ƒ‰ ?W(wücÙ±ËÆñ—ŠÚÂ3l»³æüÂ)»µý®ôÏ<^Ö‚W=zì\í ,÷ÕW+ýüºpôØ•MŽC‡œÖ1ü½Uê7g¹›ýé·àÛEúúBÒ×Mh_›òôƒý}½=íÍIຖ÷àÜú&C“¦¯G»[[X¹°À;‚Wéoè+KûÏ6 @øò˜Ét»êý3"ÅhÊU©êµkUé³P}0ÆO_Ÿ®¬D©Öåê¤â½éÕçŽ v¹u»Y¶?^î_){N°¤[¬Qº¢ŸÛ¯@™¡3?x‚…~I œ¬óµ³1‡’dÞø™76¢)üãTØç:kbõ؈< ºÛZcÕUÞ¾ÚÄ£*!wÙôZl6±ÁXõº-˜‘8? .™ÌÆÒô>†ŒÈ 3àíÒ#‰ÑC–¬EýΙL0i[Eæø FŸY žsnC´›L"Ü ‹¢-çV†Ì ÞžçVX”«™b¹·”Õ “Y4öåî®™òôÞÔEïõ6‚D&²ånÇ€aÙyìôÌ':×}uq»´sÝ\ÍÉÛWzwÖ¾b/_bFd¾zr†W©7kxÍd§MÄ úôÇÙF®8xÐSy8x:(½ ŦæY|žàƒð™tPš d*¹Õù«1Aáª>e·É_\‰bÒÀXýàÙïå6×…ãn—¼d‹p$îÜh¶1¸Èm³JËmÓC¸m¬Akz"—ÜækþD¥»výu+ÇfFkëJ?t˜×ñµõÑÄ`#pÛÈ[†%ìÏç¶YX <$Â&Þ'½Š¯Î+]#S‘¿ß§’ÿ^‘ÿD-õŠü5|B‘ÿ¿ú}á× ÿ 8_ôÝ”Ãó˜Ó]ñk$œ‰4‡U¢°þâY‰‡3ÐH¶¥ Yæ}dW¨AP 2Ã"‚*yÔ¨ªZHxPûòµ´´6³Ùp¿jš3…ÌÄð|ÚcZæ~U=ÿïšË¿ÎüŒk8fÀYo Ú¸oS–¼=ýª7t+̳ñI‚£—>L8C†¸])F}=­-‰8 òú}n§À¡!<¤ì’ŽbŽIb‘o3X$` q°W'p¬#|=âE޵L"¢è,Á:! Ƹ$Y%²yhxO*í•.ªÒtÆžRQi;óOi?óOé8óOé;óOé?óOYvæŸ>Ç©ÄK©ÔÁ{-";N7ÂXѬ¬pVŽfŒ$PÒÞB½ ¿âtÔ*»éÊ ”ÝøÉ“×‡DĉÔbázµVIˆÒp€5µò?òSyFjÙe~H$$¶`µ…¹Zñ „þóç÷­]ŽöÇä²Î¬þÕ=Æ|¾–ë,×¼´°ÿl@¶+¶_¦¯T9d(ç ùÞUd¾ƒEå‘¿®‘¿©ÈßÀU‹rQ¯È_£ò…ûÂ?&zª Qó«A¤üM1/¨ä¯SùÂwdù£D?•¿±@å?—å÷ŠzEþÈ î%âÿÈAV@‡Sv? sÊž%# 7…{& {ô˜‹r`çE‘ KmÆ;ˆbtiëeÊ*ïKc`³?¨±è â”É™áC †2\HÎ%Øú|†)C‹ôyî.Jˆt~ú£„©‹-gn&|H„‰Ù7PëéµÓ£€·Nû‚_OÖ$ ™5ÉQrÂK@Ú<–éëïç•¿®‘¿©ÈßÀ¿VÉAŒêWËE½" ä£ôe®Û‰ ÀQ©ã±!ªcDŽèpR‡9]ú¼î–ôcéGoÁëÓOÝŒqûëô3éÇnÂkÒÞ„×ãÁ›ÓOaˆ‰då{|ˆŸçßDAT‹Z°=enjŒV–úÝNàwQâô“B†cⱊÇsf‰A$ßáEŒÓR…)ô ÊJ9ou–a©Ž¥«ÓÑ“8quqMØbõö"‹ø¯íé·Ž\ñx[Õ´ç=”šûe­ÒÖ׉l‰u]xÇêÚÉ5#þV½ßí®Š¥jºV6O7UutU¹c«’·ÜöÎðm/ó/ô;o謭ª)Ô×6ŽÎ¬X'ç†.F²Ž¶×u6ÔuMö·L¤âMU¶hÑ¡{æêÙWg>{óðÂÅ,æg¨ŒÝ^ô+„òÈg5ò#Šü¨F~H‘I#? Èg4òcŠü‹æpäB-çD½hM²£©ðš ŒÖ¯›˜\3¹b´¿¯«£¹)ÞP[ *8‘½¸×šE™íncDT‚0Á­+ ¢$IJXŒ&,èqÑbdD‘`Ú™À`1` ÅIÿvŠ—ç:ƒ*T%ÄnP«4œšJ¤ÒX‚ †EõÊêy»¬Þx²ê±Ñ¨Ö~²÷2f^®-ËÜKÖ/"#FA¢R”D<©`™¿çù[ßû= kÓÀq8*·c@Ÿñ¸ï\ò>zF/ßÇŒ½¨ÔÉ7Ò‰p†õp“ |C¤gýò÷3xS矱[8n¡úh 4xÄÙ¹iPÑpä‘ @%!—Êô'pz”Û¿aÿE¼Ñi›ļïâ¾uÍa§Ý#õTn»àâÆ‹žëðoœ¹ºct×Úš(îM/Í^yW¤å‘ô]€Ã½²¯a]g9þþùW˜ðg< cœ½wÏç/ŒÒï+*-2ÖFé¶Mc1wxø¢ÁÖ«¶vqÁófJÊKd¬îññ+F7Ì?Ë—Ô¶úSþ®íýø¥[¯iØ2XE÷ѻ҇ ^s?~bxº»bõ*¯M?î×e½6½`É57 â{±ˆØãÍ8À(U™ÆŽ,Áhà ¸Á¬"y¹Ë΀*—¨@kUá¥TIÔªÔ¨D+ •¨öSUH¬`MíàÀò]}ŠŠÔö®(ÂVž]~;wéXô|6.þˆbã†Wï;·5ƒ,Ý‚ÚkìlK^ûvyÍ­_¸”‚NOJ=>*Ukm[‚eL¾•C™oèßÊ#ŸÕÈ(ò£ùE>Cå ?B?Fô¼LlÒÞWÈŸ¹L%Ÿ¥ò…çe¹ÌúªÈfäß•å[ˆ~*ŸÉäõȸfUüÔˆ†`/ )‘êL 5 ÕV——ñÙ/kM=fq+]kŒÚmf^Ä>uö`>ÎÊ“¯D),“Ë–§[‹¤ÐiÑx‚Zœ 1ðcwÒí"P²4\Ý — À[²È ŠoÖ—Ž–F RñŽgÖ|ºAo1·lé öÞüâõ;?{ó(ÄMßßáܾî‹7´nв–ºÔšÆÔÙ=e‘ÞI.lµùíQ“Ë –û­Eël —¯¶™Í©6¾{ÿgÏûðï­íÝó…+¶Ù=Òß3s°åƹTu×L¨§¡¸jd[Ëð%cè#нGÖQãt…YSì9Ò×™16—W>«‘QäG5òCŠüKùE>rÄ¢Í迹(w7Áa¢î”rƒ¾åMEÕ)ÊämGKeÿó™ÞÜ][nœ„¦âÕî Ø…”eØ…©pç^3ÇžÀ/älºÌoÌÀß]ø7Éí@AEØ›²×TW7Ö4B†.D ëD1;Ê›}ˆXÆ‘0Çs6Ã’9 Àç1èO¯¾sÑ¢oÒÖ_Ò2×Ö†ÜÞ“¯¨„Åå3ê•ù”šõª´l¾qŸÕ’¾=Éîî~à ûê"ïô¿t € ú}£öB«8ﮕ8]ö…4úü‘¶­á4_º°@±~Èߘûï ”G>«‘QäG5òŠ|†Ê) ѳ%£çe1ùo"Šà;S.¡’bwÄ‘%.-çeàð-OÝ¡Æ9„nïX¾ò2p…9•—®§&âP†œæÞZ¸Â%++„ñü•µp…ù @ÔµßÝ©ƒæ€u¨fÿ¨élôäŠÅjê«™š=éP5F(cdýa#—ƒ)pÓR¹f F®–ÄȲ ® —‚GÞ׿J^Ê Šž*BVµò•B·r YOKÜ‹‘`À²LÅêNbÊ1*׋ ¤-ê0ÝNUPòh€t—\U\ª*‚Šp!"YECŽÅâ=u5 q>\dó᜼båz±*uj•H’H­©ô!N«:¥W¥N@²øõ‹KõkUA´ÝÉkÑq¼Ž8‘ǃ°¢ JþšÕ?AÖÁ<­Ô4K°9«’W¦ý7-È´ÿwnxëÿÝõ÷ONOòïw}èí %Í3¦·Þ=ÓÒ2s÷Öé3Í@­Oö»Ÿšš¹äh–Zufú©ùš]/Ü64tÛ »v½°whhï 0ªa} ÜÉÙQ=jGýŒ;UÞÙk ¾½þÎþ榆öX{u•]YD½2ÀÇoÎÎݵX/ù1úˆEaâÐ#Ìê1ë:†Ò âx0ŠŒÆsUÅ{Vä\äY¹¬"¬Ó©ô iUöòk•uIúšT—ô„añVùž‘ž¶6Ê,¢Ö GˆÝ Ò)ž´NĈT#9N¤'bF3º¥} Š‹A“]܉±*ôXq*ü[ãTh^ÝQï.²ŸÂ…Ïu´¿tã¹^ß?°ûSç_ÿL[òÉùOÌ^~ùìÕ—_Ʀò9|e>J!töγwf1ŒÏ›ž¼0‹c|ä1æ³w^ø-w's€p‹vÏ-*æå¿—à¿«£”›Š5güwIÁù¼—ÿ¹@òZýÍ›Üöi^/Šz~Z4`’ulå¶3%ü1¾;¥÷û<.%— <Ëg¬‘‡üIo¦“MzËŸõæŠg;kkyÇêꚉÎP¨s¢¦zuGùÞºêŠúúŠê:¾¦v¬Éïo«­I'FjÂñx¸ª¡ àæFþò¢¿åÁp‚¿p%`¦ q.¤òÿ(²ß&`ôV— »#ʇÁ@36“–Â<’Å1­>mAèÖŠlÁLB5T@yÊóÞT<·¨jŠË 5E^‰¥á¦ð#þ²ý%M£5åMuŽâÀÁ’æÑêò®¦Z~U Ú¼¹;({ïƒUòI¹|BýY‡Ä~“ùÔ)åKf£s?`>õ©L}þ/§ZŸ_ñîç2õ·±ßÁ{òÀþ*°!Zì7³!^B0¹Å#};¶®ÐKo*öX]Eºrö;—\¶ý‰±:Á&鬂¥¢®¹´lE•Ù\j½ëÙ/àkø9ª ²^L5S>Iâ†#aù '‚Ó²f“¬Ø"+ñsÍ—6o|h.0Y"DqÔŠe½éãkpðtôâà’zh滤 ßÁ_È#fɶpŸd=~òmÖºdc2ЖÌQ~.ûl…%H`–ü»C'Ý¿X¦áÃõJµ.õØTÐ'ÌQü?><çÃètˆ}‡ãPjWr¼èta^DÞ3CRâ’™_ù&Ñ;úgüØ,ÄØLNÞ?ÛÏŒOÞ¿«¯o×ý“›î›í뛽aÀAÀƒ<¯LÎpݲøÎ `ÈËaĬ>Âùsr°x2ù6Àƒ» !özþÒ¡ž”‘ïJÛa:Öš9 Spd 7îb'´PR‰¿n\ ™ÎÄ9˜ÆÂ߸«øÏ£A¦4åHuuv´$Á¬2²²ÛI˜q02g¿c:Ìâ–t*t3(‰: ÖLYl3# XÔ b.Øš$éá\¯·ëiPã™Ôé¤:=ÿÏ9}染ôßÈÒ:Õ.ÙüÚÕÁ0ZóÞ”éõ€^£(…Ã⢳ ÞÉ.np”O¡è‡Ó‡Ýp®Ýó¢˜ÁD<ܘhÇÉ`èV÷ö;¿`ß{7è]³ÛWV—ªžýx)þW¨4݉1~(T’~È÷_³C’!—Á&]0ðêW=sûFû Wb|%û±kÞ>zÁæ)ƒÍêñy¬¿Ì±lFaÔyìáH}DÔo»ðŠUw~}Ïû £üSg„霑SùpÈÜí²ç3ÃÓÇyùï¢Rôç”!ù˜n#ï† Ä¤ÕØ¬°Îï¦ê#Œ–‹T}¢ÀhññU•XLQ† «êCÿœ*ðÏ%ò×d)Îñ¥5$äá‚D4xâþÉÃòçRÑüÚ%úëÜóâ-UK%¦¶ìì[ƃÌõï¤oÉPý¦Íöêô7ºue½;z çDvèþ²mÚÌÿ*ÁAÂ3T‚H“z PÞ­ {†Ä­$ÛË~´Hd"Y4ÄŸ2 1 iéZÆsÑZhÉJÔ=§T‚Rž¦*Å#•,ôN×ChÍñ•(…CªúÁg …3ð±Au¨¡R]u†záòöðŒ’ ¡ÇAÚñ™_²ùðø¾%v/ýû[™>ˆNì¿¢ßøîod„ÝWD)ß"^ø¤`Âo È„Š£[' 6±bTCÇMظýL }‘C/:|n‡ñè+f§ÝnHÿHX`uVãW1¯—^'9¿sŸ¨'ù©Õœa±ÁM–bû£+óÕ²Z]‘/váOÆš·!ät†¼€¹&Üâ­/w8Êë½²Ôå’¥9×£ &æ[Ù¿™ðÀE>ó7+M0%x3 ¨lœùVúUCAÓüòÿ.ŸCXxUÒ ÌWV‹H;~WÖù˜ð&²£€ŠÏ=i»¥2Z“qæ±g,农È1¿ÃPnyæoÿL’ÔˬüO¯$½A÷/åÄXÅ b·Æ5vëIØ›ËX›—–5TFÇZËÊZÇ¢•#Íe³YkSøpt æóÅ¢Uý1¯7Ö_‰Å"Uõõä6 ,³[ø=ò¢Oç·7 6CH±áx gXmÄe Ç@Ö¨¬VŒÊ%1†•:§m/Rk1©²EÅZ|ÚÀ{‹êjÊ{;No)~_Ql º¼¯£Q8\R›h ¸Š\Ò†5íp‚úÿºy =„_<õÐÊ“^pÊß.€ÿOþ—îR xc`d``ßö/ŒSû¿ÿOŽw@Tp‘ì­xm’ A„çVµmÛ¶mÛjÛ¶Ô¶mÛ¶Ý 6·ó×zÉ—Ÿw·;óLq¤Æ÷_ÐP§pJ—Å3)H1WùìD ®b€ª‰æ¤ŒNˆ²œÕ v£¢:ަŒ¥´óÙËL摪¤6‰Ad·iHÊ“þ*2Ú ÌG¡äž.…d®ŠØ‘p6ÆÛ|Èo/`¼éFš°žÏú!Æ«ŽX£›#¹íˆ)&4Ƈ(Êû®Š˜Û˜"ÑÆç,’šùˆiW¢ßiB¦CX[žÄóïÌiäç=:èpþ®Ü‡ßﮣª·÷&*™Ž¢7£c>“Ô6Ä0aдÃ@Ul¿ßTÀæÝD dŸp·#ŸÑ™0gL­R°ßƤAx—!LNÄdB/A~79JJñûhϼÇwí[“0²£Ÿ£>ÏöÞe@6µ•ôQÈÎPÑþk¯7¢ê$È!ž¨É(FJ³·U¾m»!‘Ê€:¬²ß@‡D>>?ÖŽEÙ¯ŒCjŸQtÿ!Êú»â…øðôÁO§ýO‘›.²ýðáox®æÅ‹?/è™y•¢ûÿpx_ñ"ÝŸ¨øþ‚èϸœ6mQø‡ÿ@]Óˆ@/Ä3‰ò®xÞsÈG­ïS¯†$àÞz<‡ÿñƤº Ã£1‘Xï£ú£«ø ¬“‰7f5b˜ÓˆÁw-÷·0ΟF[„ÿ=4Ç2xÒƒn®KÅñÙ6oaÛ¶Q786â¤nƒÃÚ?¦¶mÛ¶ïeöÊ?øe噾3Í·cÌc~oœk»oÌ©»ÍsÂ~çì-yÞþ //2¿d7åeòUòM|ˆñ…¹-_âksY¾#¶ƒÒ×K?ôǯñ[ å†pÞÄ(&ÿc1‰×“1S1 Ó13ùÖ‰.t£½˜‹Eæ²,µƒ² ˱+± «±†ÿ¶ë°°Û±;± »±Çx}œÄǨŽäKTGòYIu$ýпÆoQIu$#QÉȱ¨Žd2¦`*¦a:f :’Nt¡=èÅ\TGRÉ2,Ç ¬Ä*¬Fu$k±ë±[±;°»°{QÉq²:’3¨Žäª#¹‚«ò”ZØ–Wñ&>ÄÇèk÷¥úã×ø-†ò7Q˜‰Nt¡=èÅ\¬áÛZ¬ÃzlÀVlÇìÄ.ìÆ^ã¶qå3F›&µiR›&/µiR›&/“¯’oâC|ŒÚ4ùµiòY›&µiÒýñküµiR›ŠÉ?äXÔ¦ÉdLÁTLÃtÌ@mšt¢ ÝèA/æ¢6MjÓd–cVbV£6MÖbÖc¶b;v`'va7ö¢6MŽ“µirµir —™¬àªÅgøœÝ{‰¯H^3Ï7¼câ˜`g’˜â†¤©3˜%däæd±þ þ*¿²´Ì§a¿ËêBKI:ãÂùÖ%ZL.¶¿å»/—Ú ¹œ|}N`ݼXƒ>úø1€A a#ØIço%1Å|ÒÔÌ*qi-+e>`‘=•ãí¥,¶´œ€SpzÞ馞ƒó­\. ÛBÛ/Y‹\b›är’ƒvEÁRÞ*ã·Êù­ ô{ÉÏÛo´t‘O·Õr½ëqî`äNÜE²›zõ^êx }tócƒÂ0Fðnâ-¼w° »éÙCÝ‹}Øø3ò >Ågø ß0&Å>dåtíOJZF:ÐIâBú1€A a#˜âݬœ¡»÷LÛßrNA7ÎÁuö‹\p£Åä.ê=¸ß®ÈƒÖ/YJ¡.¥C9¿R’JêZŒ’Ôc6b6c Þà·nâ-¼w° {°û°ð>gn/ñ5ßǘOqsrfÞgþ•ùX€ú;WÙ6é oYl÷圂nœƒóm‡\h+å";(—Ø(¹œdý)×ãÜ…{ð Eå!ú¡.¥.c>å̤=ä^òó6]^ÀJžVS×ð´–$ÊøzlÀFlÂflÁNf{ƒùÜÄ[xï`ö`/öa?à#|Î*^âkú¿!1«8&˜gSìsš:ƒY’AFaNº‡¿RßF™O­o£, Ö·Q:ÐÉêÛ(õm”ú6J}¥¾r9ù úœÀ2ºy±}ôñcƒÂ0F°“Î ÞJbŠù¤©3˜UòµVqV~øO:ÐIâÂé¶R®±Œ\gËõ¸wáô1Þ bÃÁïéöþˆWð*^ÃëxƒÎ7ñÞÆ;Ø…=Ø‹}Øøß`ŠÕeåœá”:A™O­”Ô:Aé@'c\¨”:A©”:A©”ËÉWÐç–ÑÍ‹5è£Ä†1‚tNðVSÌ'MÁ¬’¹ú& Ê 8Ý8ç[L.°´,%ñ`ë±± ›±[y· Û±_ëé|ía¹Ôÿ­²è$e'äh,¶.é"Ÿ@=§Û馞ƒkl„\gsäzÜ€›l³ÜA·¸‹|7õê½Ôûì¼<@}ù"9l—ä’£‘¥üb9IzH.ñn%I-ú˜¹Ä†1‚Q:Ôc6b6cËÿ×E]QÁXH4DÄo{Ã:†™™™ÙÐ@mú1ÜÝ)´Ò)ÖÕ9׫[Ô%]ÖÝÔ-ÿ¼mGwuO÷õPÜy¬'zªgVz¡—Î\ùÏkOÝ;ÿ Ïú¦ïú¡Ÿ?˜(&ŠÁŸ¿úÏ™ÿ:¤Ã:¢£:¦ã:¡Ïž}ûiLSÇÔ1uLSÇÔ1uLSÇÔ1uLSÇÔ1uLSÇÔ1uLSÇÔ1uLSÇÔ1uLSÇÔ1uLSÇÔ1uLSÇÔ1uLSÇÔ1uLSÇÔ1uLSÇÔ1uLSÇÔ1uLSÇÔ1uLSÇÔ1uLSÇÔ1uLSÇÔ1uLSÇÔ1uLSÇÔ1uLSÇÔ1uLSÇÔ1uLSÇÔ1uLSÇÔ1uLSÇÔ1uLSÇÔ1õ7Çõ i…q<åZõÚ¶QÅiÖfÛ«x¶Z›á°‰mÛvêíË›wþÍïyŸ3÷;÷Œ¿{Ñýy“+Í¹Š¼×›çrƒ™–É›X³Óc»ÜM>bÞI/óKú˜1é‹~`þÊä[xÇtÉ`ú‡Ð9 #©Dc &PO"'ãs*)웊i˜Ž˜‰Y˜ÍT9˜‹y˜hE³ÙÑNta!c –b–c%6c;sv`'“wa7¯a¹û¨ô³rå%÷™BêL!÷âA<‰§Qg é‹~¨3…¼A¾…:SH)¤Îò9ˆ!ì†áTÈÏÑBå#~ÂÏø¿âw´±—èDb1–`)–a9Vb³5`›©·3OöãÊËúôî–+M€\E^‹ëÍ}¹Á<“É›X³Å<—[QŸd©³†ÜÍ£{ÉQÿ×ò$ù4z™0éCO_ôÃsM^£çu¼Aý&ùù6ùމ‘÷È÷ÍOùÀü•M¼|D%ˆ½‚y!\†áÔ#©G{œ—1G·Ö$QIfÍs*)<¯TLÃtÌÀLÌB ý?â'üŒ_ð+~Çl^‡ÌÅ<ÌÇ´¢ìè@'º°‹˜¿˜\‚¥X†åX‰U¬¬Æ¬Å:^·l¤ÒÄœÍ\ÕŽØÉ+Ó…Ý|*zȽØG¥Ÿ•8(¯èST/õi‘»LŸÜ‹ñ$žF³$}Ñoà-¼cþËûfV> ® ¡†SI ?G •ø ?ãüŠßÑÆ.vt ]XˆÅX‚¥X†åX‰ËØ%T@…áŒõІ»kÅ]2îNC3îDÜÝÝÝ^èmß}ùΜµ™³ñî1Ûñò1ëçÌs÷ø€2D?ó.Ð =Ð cÌ·ŒÅ8LÀ$,àL)v`vcöbàw'q §qqWp×p7ñˆ×ŽU‡ZSYi‹¨TV*••Je¥º¡RYéAí…Je¥RY©TV*••Jee(+Jeep«^Eô*ÁRÖËYW*+«°ŽÝêFv›Xéà|vcöbà ³ á0Žà(î1Õqþœ÷/ð’ŽWxƒ·xÏî>Ê(ýä²´EL7ß2ÃüË,êBÖ˱«°q“xÉÊÞà­ŒV¯1i‹öæ[:Pû›`Þd(u»éæOfšc™EÇz!·Ê±÷,Ö5žÛ@@ñÔfû ¹G˜™¡N™JáDM’¥[,33t†Ê#¸ÁâA¼O¯ùOó›oÁ£míûMÐT3¼_Ô±¤Ô‘:RGêH©#u¤ŽÔ‘:RÇHc¥Žm¥ŽASÍð¯cI«Z aS?ûM¤_¯|ÂoúGc¿ïkÐÄ«RßgšóÍ£b7±¤Ue7‘ÝDvÙMŒô«~Ó?+»‰me71hª>åwEïé}}©¯uQ—tYWtU·tGwuO÷õ@´‡Ï‹uaI«Êºu!ëBÖ…‘~ÕoúGce]ØVÖ…ASÍð­Â’V•ÒBZH #ýªßôÆJ ÛJ ƒ¦šáKZXRf™Id&‘™Df™Id&‘™Df™Id&1ÒX™Il+3‰ASÍðõÂ’RGêH©#u¤ŽÔ‘:RGêH#•:¶•:M5Ã7ÌÿOlêíÁNé´Îè¬Îé¼.hâU9~à¿ÊYänØÔ–ß\Õ»ƒ¼§÷õ¥¾Öžó_‡tXGtTÇtÑó—tYWtU·tGwuO÷õ@´§2WãzÜŒÛq7.cùŧƩq^•5Îë²Æ¹/kœ§Æ©qjœ§Æ©q>5¾’5ÎÕ¸7ãvÜKÖ85ÎÓYãÔ8Ï5þr/ÃëÇ}ÙËð®ìeèeèe¸×ãfÜŽ»q{™W*ŽŠ£â¨8*ŽŠ£â¨8ÉŠã-Yq¼++ŽŠ£â¨8eÅñxVÇdzâ¨8¾šÇÕ¸7ãvÜKVßÉŠã{Yqü`ü(+ŽŠã×Yqü6+Žß?dÅñ§¬8þ’Çß²âøgVÿΊãYqTÇ“Yq<ŸÇ‹cŽêzÙõÒõòªìzy]v½Ü—]/]/]/]/]/]/]/]/Ÿ_É®—«q=nÆí¸—ìzézy:»^º^žË¹Þ·ÚÙxùxU¶³ÑÎF;ŒG²v6Þ’íl´³ÑÎF;íl¼¿ßigãƒÙÎF;íl|<ÛÙøT¿ßÎÆg³v6¾ÒŸ¶³ÑÎÆwÆwÇ÷Æ÷ÇƲŸŒŸŽŸe;ílü?ÛÙhgãÉlgãéñìx.ÛÙxqÜãj^ò‡vŠ, aô.˜Á2ª$Ý\û‹+zÃ1ísÜW•!9$‡ä’CrHÉ!9$‡ä’CrHÉ!9$‡ä’CrHÉ!9$‡ä’CrHÉ!9$‡ä’CrHÉ!9$‡ä’CrHÉ!9$‡ä’CrI.É%¹$—ä’\’KrI.É%¹$—ä’\’KrI.É%¹$—ä’\’KrI.É%¹$—ä’\’KrI.É%¹$—ä’\’KrI.É%¹$—ä’\’KrI.É%¹$—ä’\’GòHÉ#y$ä‘<’GòHÉ#y$ä‘<’GòHÉ#y$ä‘<’GòHÉ#y$ä‘<’GòHÉ#y$ä‘<’GòHÉ#y$ä‘<’GòHÉ#y$ä‘ É É É É É É É É É É É É É É É É É É É É É É É É É É É É’,É’,É’,É’,É’,É’,É’,É’,É’,É’,É’,É’,É’,É’,É’,É’,É’,É’,É’,É’,É’,É’,É’,É’,É’,É’,É’,É’üôoÿùñ£ýÙþjÿ´ÿØíöûýßþ)Ó§LŸ2}Êô)Ó§LŸ2}Êô)Ó§LŸ2}Êô)Ó§LŸ2}Êô)Ó§LŸ2}Êô)Ó÷1víÙØÚ¿ì÷2}Êô)Ó§LŸ2}Êô)Ó¿Ýò•Cû("(ŠЀR²€NÕ»3ŠðDx— ~Õ»w°K°K°K°K°K°K°K°K°K°K°K°K°K°K°K°K°$K²$K²$K²$K²$K²$K²$K²$K²$K²$K²$K²$K²$K²$K²$K²$K²$K²K±K±K±K±K±K±K±K±K±K±K±K±K±K±K±K±K±4K³4K³4K³4K³4K³4K³4K³4K³4K³4K³4K³4K³4K³4K³4K³4K³4K³ ˰ ˰ ˰ ˰ ˰ ˰ ˰ ˰ ˰ ˰ ˰ ˰ ˰ ˰ ˰ ˰ ˰,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲,˲<¦XS¬Ï‡žŸ¬|öüâùÕó›çwÏž?=yþþ×ë«ùj¾š¯æ«ùj¾š¯æ«ùj¾š¯æ«ùjÍ£y4æÑ<šGóhÍ£y4æÑ<šWój^Í«y5¯æÕ¼šWój^Í«y5¯æ_í›J `Îû©,¯7OŠI‹I‹I‹I‹I‹I‹I‹I‹I‹I‹2ŠI‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹I‹Iþ3ügøÏðŸá?Æÿ ÿþ3ügøÏðŸá?Æÿ ÿþ3ügøÏðŸá?Æÿ ÿþ3ügøÏðŸá?Æÿ ÿþ3üçã">.âã">.âã">.âã">.âã">.âã">.âã">.âã">.âã">.bjM­©5µ¦ÖÔšZSkjM­©5µ¦ÖÔšZSkjM­¥µ´–ÖÒZZKki-­¥µ´–ÖÒZZKki-­¥µ´¶ÖÖÚZ[kkm­­µµ¶ÖÖÚZ[kkm­­µµ¶ÖÖ:ZGëh­£u´ŽÖÑ:ZGëh­£u´ŽÖÑ:ZGëj]­«uµ®ÖÕºZWëj]­«uµ®ÖÕºZWëj]­G­hÔ¤YÿТU›v:uë?>çÿŸº¬·¬·¬·¬·¬·¬·¬·¬·¬·¬·¬·¬·¬·¬·¬·¬·¬·¬·¬·¬·¬wµÞÕzWë]­wµÞÕz×›7úV¿ÒoôÖ3wz¯ú¨Oú¬ï=4jÒ¬hѪM»ºõu½«õ¶õ¶õ¶õ¶õ¶õ¶õ¶õ¶õ¶õ¶õ¶õ¶õ¶õ¶õ¶õ¶õ¶õ¶õ¶õ¶õ¶õŽ[Úã–ö¸¥=ni[Úã–ö¸¥=ni[Úã–ö¸¥=ni[Úã–ö¸¥=niÏÍwúNoý„;½×}Ô'}ÖŸý´¿ü¯¿õŸä_¿ÿO_oi?rp Q „{ÿ›½$í•Úþ<À Â1gÿÙöŸýgÿÙöŸýgÿÙöŸýgÿÙöŸýgÿÙöŸýgÿÙöŸýgÿÙöŸýgÿÙöŸýa2„É&C˜ a2„É&C˜ a2„É&C˜ a2„É&C˜ a2„É&C˜ a2„É&C˜ a2„É&C˜ a2„É&C˜ a2„É&C˜ a2„É&C˜ a2„É&C˜ a2„É&C˜ a2„É&C˜ a2„É&C˜ a2„É&C˜ a2„É&C˜ a2„É&C˜ a2„ÉPŒŠQ1*FŨ£bTŒŠQ1*FŨ£bTŒŠQ1*FŨ£bTŒŠQ1*FŨ£bTŒŠQ1*FŨ£bTŒŠQ1*FŨ£bTŒŠQ1*FŨ£bTŒŠQ1*FŨ£bTŒŠQ1*FŨMûnÚwÓ¾›öÝ´ï¦}7í»ißMûn~ƒißMûnÚwÓ¾›öÝ´ï¦}7í»ißMûnÚwÓ¾›öÝ´ï¦}7í»ißMûnÚwÓ¾›öÝ´ï¦}7í»ißMûnÚwÓ¾ÛëÜx¹îÛý¸OûnÚwÓ¾ûuX/4@! Cý‹Û¸`è}Žƒ†6 ÑmtÝF·ÑmtÝF·ÑmtÝF·ÑmtÝF·ÑmtÝF·ÑmtÝF·ÑmtÝF·ÑmtÝF·ÑmtÝF·ÑmtÝF·ÑmtÝF·ÑmtÝF·ÑmtÝF·ÑmtÝF·ÑmtÝF·ÑmtÝF·ÑmtÝF·Ñmt[ŽÊQ9*G娕£rTŽÊQ9*G娕£rTŽÊQ9*G娕£rTŽÊQ9*G娕£rTŽÊQ9*G娕£rTŽÊQ9*G娕£rTŽÊQ9*G娕£rTŽÊQ9*G娕£rTŽÊQ9*Gå¨mÛ2¶elËØ–±-c[ƶŒmÛ2¶elËØ–±-c[ƶŒmÛ2¶elËØ–±-c[ƶŒmÛ2¶elËØ–±-c[ƶŒmÛ2¶elËØ–±-c[ƶŒmÛ2¶elËØ–±-Ïßõü]Ïßõü]Ïßõü]Ïßõü]Ï5ž¿ëù»ž¿ëù»ž¿ëù»ž¿ëù»ž¿ëù»ž¿ëù»ž¿ëù»ž¿ëù»ž¿ëù»ž¿ëù»ž¿ëù»ž¿kñ/þÅ¿øÿâ_ü‹ñ/þÅ¿øÿâ_ü‹ñ/þÅ¿øÿâ_ü‹ñ/þÅ¿øÿâ_ü‹ÿðþÃøÿá?ü‡ÿðþÃøÿá?ü‡ÿðþÃøÿá?ü‡ÿðþÃøÿá?ü÷çÿ7ßµ ÜÜÜ܂ނ®À4hî&„¤2¬  ¼ ˜ ¼ X ° r ¶T~¬ÒÚt¾(’îª8´P’vòdþŠ@Œî>¼.†ä*Vœà> ,!.!ð"Ô#¢$(%\%ä&\''¦'ì(¢).)´*¶+¾,ˆ->-ô.‚.Ò/d/Ô0401&1R1è26262¤3<3ú4š545r686–88Ú9"9\9|:Ö:ö;Z; <$<Ô==Ú>.>b??X?º@@øBC”DHDTD`DlDxD„DE*GÌGØGäGðGüHHH H,H¶HÂHÎHÚHæHòHþI$J JJ"J.J:JFJÂK˜K¤K°K¼KÈKÔKàNO:OFORO^OjOvO‚OŽOšP`PlPxP„PPœP¨Q QêQöRRRR&S,S8SrT¬VV\VÊWnWŽW®WÚXX2X€XÐY Y^Y†Y®Yä[V[v\ \à] ]Z]„]¸^^†Þ‚_"-9åxe޵aAÇÌXÁÅfŠÎ‘11&fff”ªS ªHãî£@‡´ÐÔÚEáfqg„[Ød0­ sá6¶yp»z-ÂýÌQg ¾ycÑÿ™kqżXýU¾ÎOâ/ª,û?p£êÍ!AR$T™aŠiß²»ký§Ýä¯êÓLøO1Ǽl½oÌèŽF›<¨Ýˆßu^eoü˜¸’EO&ÜË¿M†Lú~™úäAþ-žÀÉ<{¿©Þéo±Êz 34xlÁú•ÀùÏõ=Ù¶mÛ¶mÛ¶í6Ù¶móË\g×¶^ !àïüÏ[‚‘¢D‹+N¼°J$±$’J&¹RJ%µ4ÒJ'½ 2Ê$³,²Ê&»rÊ%·<òÊ'¿ *¤°"Š*¦¸J*¥´2Ê*§¼ *ª¤²*ªª¦ºjª¥¶:ꪧ¾j¤±&šj¦¹Zj¥µ6Új§½:ꤳ.º6›a¦³Vyg–Eæ[k§-A¤y^šn¹~Zhµ9.{ë»uvùí—?6Ùë¦ëö馻%z¸­§n¹ïŽ»îy¯—Gxh¿Þ¾Yê©Çžèã£Ïæê§¯þ` jˆa†i„QFû`ŒqÆo¢ NØh²I¦˜ê“/Ny怃ž{ã…C;æ¸+Ž8êªÙÎ9ïLå« .ÑALÄñAøßXüB}|ØKó2   ´ ‹kiQ>WzQbYª^rbq*oJfjQjqf1˜Ç•˜\Z‘àOÎ,J.ÍMËI­ó9‹2óÒ!ŠJ2sR ŠazƒA¹c °#D °#p°E °(`f ŠUX°%a°Ec#b°#D³ +³ +³+Y²(ERD³ +pixl-webapp-2.0.3/fonts/lato-v11-latin-700.woff2000066400000000000000000000620041504641265100210250ustar00rootroot00000000000000wOF2dAÀc  ¦p`4. e ‚ú ‚ß6$†n¼D ƒ> ‚`„A K[O1±–ç[ݼٿŠ'…r¸”rú·Ÿ†X´BÙö¦Þì@;ôäâ‚éæån%É|‘L™ýÿÿÿÿÿÿÿÿÿÿÿÿÿ$75`Òv÷^#eúrZ¢SMW2½Ú×Ðþn3ý}ƒjiz29”nW¤éˑюí6·‹±1'ûÊ:ƒwë¸+õaÂÖêJžLud&Ý©3b‡5 ÙjeåO£¡¢z3eë,ºq‘LbNóV‘ÑèB`#rZ|z B¦©#'%ù‹r‚dåÈte—ÐEo”Nb4'¬ÆÌ¸«âËüXpfå×o.Üú&üþ…Ñ`—i5²Æ¾Î²%ûýM³E¿-òúé]ìí›æþsÈÝõ ;ïE6WIÇ";õ˜ý„«Ù œž‰ýܬ˜“x—`)pMÆKþ+þkܨd+y˜â¿åÇâÿ=ÿ)×WðÁß2—¦1?ñc™Ïü/ü¯ü¯e „¹Àý;ÿÿgÙ¡’Àÿ7ÿÿ/ÿÿ{×ü“¯°q ¤¡¥œç´Í„ØÀ†öi/p•˜à†b' Ra6k]ºŸ&Ë,Öy´Í|§ï;' ¤  ’!˜Ä%(ˆ0£fÔŒ¿éJnnssÿssí"òkµ.–æþ½´œÜµýÛ´ùØ>øÈ²s±‰0Sn HΡ2ãáÿïýÜ÷ÜsßoèÎ ¥tÐ*áP, £‹Å:–®ÿÀÿû­\Æ%®V£çÿÕ׃ÿ§w«ÿµU%A†ôxU.ýDóLU8ÕàˆÌ%yüÿ’®ÞÿÂ4Žñ“rœÑ,~‚ ±/+& ±ýKcÙâÒ* šÑa.¥]C[}Ü.ty bàÐ Xrú’‘Î| PúöþÜO›w7‰Ä«‘Š”EaËù$Ç?ø·çG­€¢þ€g7Ô¶,§ –7¯†´ú¤ü sÚ¹¬)uƒHÎ…† øÿækÆ¢³3[\æ«pJ8!åjÕ}ùJ]AW´¶PWUÕ-pÀ‚•Æ ÈÙ#EáÇ›F×ÿþl'&`üI$Ëöÿ¯I¶Ì¹,“(.ô©òZ`µV·–Æû[«ÌùuúÆ«Z€¢²[Ëú,—f…½[p›5·6ª~$“ðÜŽ\É•,ÕN~—Ýñºüñ}T᎙Kõ‘Ý]Ãå‡}`¨Š‚cc\Sš7Óˆ@È!…ˆø™RÓaë°wYúö{]¶Û-K "‚br-„#¸A*m,½6ùú¾å-¼ƒãiùÝ,AlÇvZ ®®‘–’œæ‰IJ(TÃC½ɅK¼”ÌLÜ$MÍM‚âˆFNðÔOìDOùüaalåšW°[í8 8÷ûÙ‰ÝrvvB‚U—:~ã(¾¾ŒÁ„h(ÃÃN”¤¬T–—áÜœžŸâ³FI»ÔôUª®²æ-®nüf00q!'‡r,s4×3tEKϧóu½“}† Ø’aû³fY©=Yd½/…å ’’Êúaÿ‚J„³4¤ʸОvؼõu:Žý[;¬c·þÿªú®DÊrªJ-SV»§Ö-cßõî+0*M‚4CP²MJn²\HµHnï ‚EÉåÈJ“Kò%¥ýßõêþKKoËÞÚ”,C–9Ù´Œá¡\¾R?§C¸ :(ÀL¡¾û¦4Ô‡oúA±”J¡àACO蟸˜ÞÍ—°‡ æZÔ,àQÆ?o÷¤vr¤úR©.ˆMÆ&—Ê ôS,6 Q›Nzø±œº‰)þæ„§|ãI®UÓdñÄD÷zÙö®o²ftÍkH°@)}góØZ‡ÁQÍø¿üÓnc¦k€€LÓ±~ü5¿÷´÷VÛÎײ½ç’MEAHHBB`†¢vð°@OÔQ€·Ç€F ü›¢d°S>1ªÁ…IÆVs:PÅ ½¸EÎEôŠÒ‰OSgª÷%‹ì¹õv¬‰9õDTºÝîÕýõÞÕ¶\[æÛ£e»»›éR”½kË{²ÒVηo¼M+·Õý]äž¹èÓw‘Ë‹9a¡ÍÝu,÷ŸŸ¸cå¶Ý½/áS+v%{½äãÔŽ¯Åú¾@‡­-Å÷v· ApèÜìx%`iÖdaiôeŽjð¥´à5„íå0ö˜8_:ÿ†!quܶFs˜ÂæpŸNÈ3:¼b[ïxI|RÚ€ Ùs·†+¨p5´Fê£mTjLåÖ¸jN¶aOD$Y¤ãÀæW?•Uô[%ÿGªºö žÄ©cè-9Ú?û“éeþjæào~Mé**Ãz ¯×'¥qÏѸGVt|_ÿíËÚY½Ê·\EäÕ¨˜¸„¤TpO ’´«Swééç€!9nê[wî=´Ój5Û.iŠ?VAF4‚ú46FÍV»Ö¡gbÞ~zÖ’së Û,òÍÁ92ŸŸ­]Åž¯ÞÓ¾ò¯ëâäeUðt¨’}žáYéu¶ää•Úò îO ’´uõô É1ú|\“õ4kvz.ß+—j…°¹‘ÛY»Ó{³_Pº‚þ«W¤·Ä͉}KŽT 98#òJTL\BR*ªÛšº†¦–ëàFž@$iëêé’MsõóäK펞‰y|Ú<ëÜ•~Ò²rò ŠJñ®{'Ìhÿ«op´¿”î}¶ÆÇL¿˜[ûqò! Ý~ùü÷á‡r<ôrpz¯ßy[ÌìlÆùû®öŸ¹ñ¦/Ó/®B0Ã~z;©ô/Yrò ŠJQ=^S×ÐÔrÜC§mðT'õÊø²Iš…}A¢Ðó/Æ/ßû~ùC”cñ§WãëŸÞ\y;Gæâyé²ý‡•»ñ0ð~嫹¿ 0¸ð—QWËà^†*YV‘4ÚÅÄ%$¥"Ýdåä•¢º¨©khj¹Æá D’vu–]zÙg`HŽ›å­;÷1º8ÎóI3]̾š‹ƒe­žZ³±ÍÝ~îÓáÑïºsøñõ Gð17~o_’•èú²pH§·¯/5>Âf2¬ ‚1T'„^xщw]×u]U—µ®®¡©å:¸'IÚºzú†dÓ˜ÅùW8Ìq»™‰z£uAÏÄäu ]_4ŽJ§ù›óçYë2ºGïÆ§ücP†EzÌÊÉ+(*w'IÚºzú†äý9~0ÙK®×g›9;{‡÷¿¡®úVÚ=s‡šƒ³}ý?s×ÕºwÞÊWþ]pdX¤wY9yE¥ày‘¤­«§o`HŽÑÝñ|²›ΪyØÙ;,1ŠN© šÃÒ33 .üÝ¥P°N-,kunÍÆv}ê&ì‡U5u M-×±ú`»üq¨ß˜•í5…æàÄá D’¶®ž¾!Ùtù3Ú:z&æ #…æà ÷ ¿´¬œ¼‚¢Rp‘'IÚºzú†äšÜ˜Z£)ð°ø ¢¬~Φ"D‰‰KHJeùý마 d®.5´.èŸ-¹îÿž¾ú¬µ¤ÄyëÔ–2+×¾xÏÝIÈ®Ûs·“ÛìT° b“ð¬t—M9yE¥x×¾÷ÁǶ<­Æí¤›•óX$Kk›Ø•{‡¥*õ^>Aa3s;{‡ÙOR²¨‹‰—”ŠÕv»õOŠpJ©*#*WlŒ0=ÎOœl¿Ÿk|:­žÌ"'kç:À,Žàîâú„ûì8¸·á·=Ûõñ*†Ð .\‘i”˜¸„¤ÔOO¸U}½F]CSËup'IÚÕÉ»¥—}†ä¸ÉoݹÑþ8œLÄìÁmø._¬ævecÛ0 ¯äT#Žl …²X?çxœ·¿Xšç£uý¢Šµ9½ñç OÀF¤‹Š‰KHJµåõ¢_Öª]§Íým™…oª„l3ñìÈ=¿Â…K!lŒâ8iiŸQªö€ 3ñ ò…æàŒÈ**&.!)Ü=ž@$iëêé’MÏö¦ëŸR*u‘- b°ùidî×®xðçõ/«F÷Æ=3—¬m¶HJ•¨S¬ZH´=N—zLÌaa@”Îf´oÈ‚ò€žbˆ®u÷ÞÔ—þ)(\‘$JL\BR*Ò}VN^AQ©-ÃU75u M-×Á(íyåxÀb˜D"9ªÃØ£WT¹ÞDz¢;‰I¸IÜIÅ^máó5¼8°eyÒ@>`{E’$I’]¼^Þt¤¸u4´MjÛYÝ÷„&3“-/Ëú¸árõÂ(PÁ®æD®¥—9Ê\Bî»Q(ƒý*îdl±ºÛ§9†ÚÓÑÆã6€uƒu!ÿàÝø2^‡ŸA:W©c¶µ­<ñN¥˜^@Œ²W1Q'èžã ÓB.VÂêȨ–GãfÜ>± ƒð&&üzµ7“$9Ž(a%vgN„[pÒžù¹ÕÁê¬ûMþþö/«ÚÝÎV5Pð݉éS¬XHÂGäYØK&J9†?×¹µÞ==3ëýÙü31ОE<¸‡àÇn‹o`ÂOLýkðæ]ûM|w;xŠ÷Èů-®Ç="!&¢4Jß.¨£ÚWÉu¨_ú‚éÉ]9“Î6ÕÒ¶9 +5‹¦£„«LpTðÍ‘€›7'XLj¦×]óHš¥ïªRï`½­Ó€ù¾8ÔzKÕ¬¹ä 6›Âȵ‹‘ûÌX *6Nå$;†Q"æºÝúV¹¼Éw9qòíˆd7’=~&{rÚ ½I b$·¯¹Ù#°{o0=-'‹Žø]+¦´…9‹ÊÿwÒaΦàpbCz~ˆ@ˆ3U˜îÒšÜþëA¦%cÒ¨‰Áˆ‹&»7ƒ)½Ž2:ݵóÞi)Þéa±} ´™ó´ñƒ©ý<”èNä›OqO²‡îE ƒÞÂÈ™Yù¶Á£ÿŨ}µ=³Î˜ThiªÂ? pü(¥@«¡¹k¼T]ÌÃgŠZô„EVí«µå‚ã®ò©e±»¯|TÀcä€.õÐöVRj¡.8Šóœ°H}Tñ_’?úa3Üéüªvë=„+Ur6Q͵,±yŲkú«€fó”uoŽûToÔMsO½æj3z4 l>Í0ôy::7Œ“©šèš½hõšÂöC2Ú¸ÖYÓs§9Þ™/hŨ|Ù¨Ö ´DP©‘ËÉU®vç¶À†=âÈ)KâR‚ç•~Ì$OX0¸À%y3VÙ¹—õζÙ@c‰léô5Ù ¦^y ëY¼û0EÒÚ|à\+•Øc[Þ†e«ÜFŒ8ý›ûÇ.R'¤¼p/R¤½‘IÕè):luòY˜öErÇî¨K% èjsz¢ç3*Á2·KøLb¡OB…¹¤˜ëé¶¼DrÉ×ÄE/û`à&“ T¾Ã3¾X‘Z ’Ìúm櫲Êx›R—UrʼtfŠš7$:“Ö•°¾ñ’dpoÛ”‡ÆVPÐHIÛ‰šSß ÆîÀØØIûËzúÃ`ŽŒ#Ž—õõ†Á8g<Ç©÷Ôø„ßZ‡o[ ‘¤ ýî(‰ì>Jhçu‡××tg²9­~ûï Ýç0 œ££W8Z/û"rkFXS€j¡™Ø] 8«n°QŽ#ài8„‡É¶Q$x5~‚/¸]‰ìjRŒæ¹@„mô4ë²”‚`´ë’–£Á=¼Úß}O*.ü4¡}Äé;3xM¡æ‚P+ü¢JïÑÂ>Žcea+Ó’a\åõlžë" ’3žJ6:ò=ИÐò%#®²œ%OÇEB?ÉgîÒ'V«‡Ž + °å©6-›ÈP⽄ÎG OnÆäœÙ¤wIfb.ÛôO¦ Â\>tö/FîçÀ™]ïvô6Ê2"Á©Rà‹á î‚4u@C,×9‡òE£ª!„ÜEB–‚ÁQ+WÛ¿ÍA±‘¶][Hµ¨“« Áá ¶úD›ìD¯¦½,ÌäH(ø«ã|âxO“ÒAÃBj_!¾ö¡°zµƒâ“ 04vžÙ«E§púv G·ö>ÐPZTxЂ0ZPð—µñüÞsZ65iQm¸/"X¸*h]Òxp·¾‚+a¼EÎG§zG‹”Å ᣓÝÓwòxáÈÎTw;ŒÝµˆm²n:Q.Þ¨Ãn¢ýAhx6¼å'Æ&4ʹYº–\ø…4ªÕ’Mar÷}­gÙ ê1L¿Í £Á“æ®3/mÚån×"ù^í?¬Âö<‘÷¦Ü~;y JL¦«™¤e»({R”U½¾;OKwÝâB•±¶\\Rʱ»TÁó·æ^m”Øô™« –‚ó ¦BÚw1OZbþ­‹™.§R9=Ùw4rMŸfW%^Î#ä‰nÍöo?Áoü_èàiD#¢8²$MÛ¨”›¸°Õ‘ˆ «à:ædì–3 ‚UA]•{hÞ„Laa)\ˆ”;dcùj-M_ÇyŽr©W£I=Ö†/q Æ8‰ú÷Ó¨¡‡½Clâ‡UºbNdÚâ¦ýJâ!nñ€X¸0G8Ð]%,¬ð² ïE 1­ü= È^/¨6„g)!ã¶#„¢n眧-O"”¬õw 3ñq‘¯Xæ÷Ît ÁÇ‘”~¨ö;ÓÀžï0Ö;¯'êÿM2!Æo%þöËÿ *’ÿ %Þ+È·ÿÒ^”ÔÝôÊ;שL¹I³À+%N©—rJóÍèlE,Y»Ê7ó͇T³˜Ðä<[8›óšQ;Àr¤Ðîö½Bß 6U¹Ý×ÿ“)½Ä:)þˆ-Þ»åe†»#ÚšxÌ0šÐºÆJ;$2*³Cÿ£YäA ã^@»Gg9]àß×?e¸NÓ§R©rˆ‚:啯ùÙ¨xÒiv¶¼šÑZl]Çh<Þcæ™q>çCBÙO"äŠNndéaæº2_R@Y›OrÝŒFÒÕ'yVÚ:öÍ•¡ ëÌ‘!Ð]׺5·ÛðŒ‘¾ÃÁ õË |눇hA›×±r,Â4ƒR6õŸ•2:îÀæ:XdÚ6J[ Z —§ô«ñ` º4U(1«³G»Lß—|g·’£%LxíÊÉîZJ¯q,çè©Ù§67ld‰<4†sY,¥ñƒ=å¶]—"¬Sà|åk¯]™(.ÁO6ÒvQ ­ªZc´ÄL¸¥'Ó«ÝiäEðtó” þHXj+ZeIù ? ÛVß—es‡»÷a)üB¦ºeȲϗËRÙ±tÒdîf!>úùƪ»Y•YV ¹h!œOI‚NtiƒÕu®&JB¼afÂmŒ^Ëj•´¢¦[úg¡æ¯å Ðâ½w'“U‘i NåDÞ’)eÿ;<ï}ÆëÄßJÝ!fÿø«KÕNí7Èy2,}e™†œ$"œÁKŃÔë‘'´ø‚½ù+d4á÷ðÓœâ|Ì÷H±ýKþ-Ô`ÄBŸ`Ûbv},O(¢äà,êO¡[Ië!l=ë&<åB=d!Ý11]Q÷“þ%ÔìŠ\-xo 2³Ãšì²2rܲ‰6ÍJM¨‡L¿|¤÷¨’ÞS䬯¯+>¡¹±É Ž1(‘ÁÌHûö<µhÍ«º‰§¨Š¹œaò îã¤v“[©LJ3"ÕÁ1j [hRÞRPlа’Ì3œsÚdoœ¤3> ‹ °P»6¨œ®I9*u?Uˆ‹ËºÅ¼Å¥–f”"µx‰âJ³­ÈËÓ¦óï"VÛÐ*kJ¤ð1Ç|V_Õ 6®øqZoÐ¥~ÍtµMúRä%ªëj=§#e#õÉRWZ}ø1;‡àVÄL.ú·¶POàÅ »HûÜîýÅu`àÓ .×$ÈÞÚíVDEîÏ¥9è1Þ5¿÷p$æ¤7 Áà„Á…ÅQNÉç˜ÚòOœ Sfð ê-b¿îOízö¨ a܉„Oþ`Œ›¿Ó‰MYüvmÝi©IÉ|rñɰmPò7-ä]$ý´..…˨ƒñx‘OÞD¨²rV_UðX-úêLK~H/&tœ [îä3g1☦û'伓¼KÁ3,0Ùr˜yJ?÷ƒ®aèNŽ¥éµˆu,ýç}µ5mf¥…åH×gë’úëÇõIèE]¤MO‰ÿÝÙ šò–:‚¨¯ë­ H¾ÔGþX Aíi·\nó{Û1YHv*_Îa«ôú&ºð°Nü¡|a 2*n 0ÿX•[EU©/±OÅ{eyÐTš}Z­›–ÁÚqBIëy¢—‚Ê@Çí¦¶‚Iät#XÊÆ)j‚GcÍx6ÕÝÊ™z5%ù îÕx°d‘Л©¸—Ž´œZyÊRÔ¼W"Æ >±uúh©´¢‘ml­òÌëWÏ,HäwÏWíæöu³¢‡ì¼Ýå`“¢PpÍ΀·¡²¿ó§œ»ž8µ$¥3îÙ.P2{§Ð0IÕ#»K:i}¦Ú\iPgÍÈyn¿²AÛ6>Á.P¬Ë®fÎ~®7TVñîËѾfiΆZÁ!x 9#1áRœ6«-*Mº%*vÅ´ñέ#oczA]lA‹%h±”™NÒƒVåÀqóIÕÜÌ r+fìæ­TØÙêl¶6UCÂRk½û}‘”‡Ôe®ÿÚ]—3;‹_ÀW£RÞsï9¶´x³5ÅÐôÁÈ®w‹-õÎÄC“™¦fß o°fÀE&Ûež)ÙåÑY 3G¹Ò~–X­ Å”×V£`Vèm1që=D¤­XèÙÓ½pÉOlÙ“@’W*¸…î?n ó2](lZkêF´.yMYá|Ú-wðîïÌ=§LX˜pÅ3s%n¡¯å7 ¡¥ùŠ\_¾è9åV†ÿH_ çƒQC©2)ô~˜Iï¶Þbi£ÈåÇ×A›{i\ý´WòÄ’¬¥ê)}±ìï“B_ä°°8 ŒAí, J)fljÞ5줬Ófšãñ˜CåŸ$ [­ˆ°/KôûhQÂ%˜PƒÁ•›:¼‹°9qœüL¿¿güg ûc³E¥%œˆ‡ÝÄ ¦iÏòdsþì8ŸOAx÷€Ïqw‘a 0 éÄç!Q\Ó1 ˜Î…Tõ¼k~ X¶£Ü-öo è£Ó¼v‚2·HÚ¦E;JÑþy6Œ¿­iþ«Íúß÷Ñ™û20…®.Œ'á×¥AVcÄOžåú‹.ÅÊrôQœ3Ý)2ì‡7ð! X¾²ƒÆƒ¼–Ä¡ûÜö.Êy=¾4†g[T&½(-Áíó_ŒÇÄ]HÌêÄ4‡ûôc@~ /·íîKþ.Ú]bt£ÇÅ<±H^¶ÎÑŠþ@¬!ùô0Kt‚Î\Eô`eù´´Øiqƒ/•æyÒâ9¦iJ½ñ#Ø£Iö©$`­²œQ–$¬O7Æl°ÖÏ(K]܆ ,TÚPCàV7žvÔâ^Tñ÷ÃÀ¥?ä Ës•ÿ‚!êˆ$‘Õšf¨ ãKº !¬519¬Sg¬ —HjÃõÆ+7G’"!ÊRf˜ÀÊH6Xf¤ÇV Š!²õ%G"0? -Gø¼Î+9 >ØppØ « -!rå9¨Ün"VïÕõ0³ôìJå ÆóÚPÙ}¤"Gâ!¯ú†âÑgöGêûAA½ÎbèjßÕÍ{˜iœ—¬ãå—²sMʇÚîäõì†BŪ y©2¯ éËXYh¿XÎêÈÏ^éq˜= åE§`Âtƒ¬Ùºv]ÒbëßJˆ«¸œÝgØY$t³ïó¥åyóþG¿mh@?¹óþ’–óî°Y>çÒvf÷»øÉ÷ŸÔs,Vyð/ø>ƒ~Ø®‚"ÃÎì>ˆ@¸,TÏÜbü›™½. N À†À×8·‹ÿY“!ï·îƒˆ2fn»ì=F*œÑÀjÈ¿uøÊÐíù$óèxû¾ö5gÿiÝ×:§Z‰ä&'ckÓ‘¡HK„%![:…†~×gÛþò×&£?K,4eÿö,ÕOÒ‰ÛÅj¹‡¶ífÕùäãÛo[ýâZ¨Â¬‡ÕÐþ¹ãéåå º¾9ôðdÐ…çîëúçÔ¯{ÖR/)j²CY̬Py åÒÚµ$™ÿ2YÙ¡Šê%Û’y‚<Œpeæü2w°C­²`Þzá™ÓÌ0aó^óæÈêBÞJ£šÖ_g©\-T|É‘*ƒ>H„ s÷îËKɉÌFçr¨øj”¡ˆroßäMìQcØqÆàXZMd@’…Q˰%³©6¡ Í+ÍÏÔU ’¹ÁÏBþiÙ¿éý»u¼Öšy73 –.ò«'Ý~xmf‘P¼P0ßs+fŸßO§fú$TìTÛ¢&Œ•êÃ'…U@ ¢€–&Ï/†…z.GâЏ֮¶v‹…0F¶>Ÿø¨3qœY]$ž,ʑζ·’WTí“¶´*ro§Tê.ûò±ÔŸw,¹:çèH†/f!šÈâ MqQYA"qP'–%2qQQ¦ @„ÏX´O†Ê†ùŽWÇmÂ9%&öøzùn)x”ÍGµKVg47̶‘,äüÉŠD~Ÿ-*Òl.Ù–Ÿ;Xa™Ì'·‘f›3$«[‹ç£Jb²žø§„ÛZkªªÈ¶p®ž¤D¯ŽN  Áj2&e«°ªY‰Ÿ”H«Ñ „“^¡òzY¾`¾‹Z.­{°™rý:”È·„«ÛËNÄÕk¶zÉ͸ ð8jJöZŒ{ì¿3£/»¸¼ÉÔõÏ1O}.«Ð9Tî™tPÍÈ÷˜ÜX:qÈS¹yòy‘&Y__×õ}%$–á´¹ÛDµ­À!k-p¸ºe=`ˆÐ¹É`ü`÷®ÔKú¬ë-®Gº[6—Çeð&÷‚£G,®Þëlq‡ª—mÀ>:lä-y˜ÃÆ'LÝn\ñá܇¥-w:Ž'V†¨;¨g{Ã'Ngu`.ºlÿ éú]˜gì Œyë×¹;QT¶ò[<Ž‚(fv;æë“”5æ@¿—á5ÂìÅ/Ü!®iç¬johŸý­Épð¨˜Zš¡É£dªÄ£ñhùª%Üù¦*ꄵ{·ÔÊÊ[Òç¾réXLâh1‘bTEâ6÷x«$. í?/Ÿôß?ºÂÑ:7yË›þ§#_|è‚p 4M½»C.s?÷ß/¿ª×Ýͬe:$pýJà?­ûF¼ÂûÂN¯OÌpj"‰ñè+AÀ|}‰ØJCL=qwã³SìÕˆXäÓåõÆì]ì9Ry& G•X[/f˜"8_ú4Ù—eºÞº9@GOÙª7m•,œ²€ÖGdPhç^zô¤Ñg-öPuk7kÓBkkÀv»j]6½ˆ­A¬ºåóï2—Ê|§óFDw o:Ç"ÜÙÛpMØwîÂyY1^ à²b"qF1¯8@v¾7·_)™A³²±Âf ´dàב"ØËKÿSåî‘ywßÂ`¾£ö\ŸÚV»Ê %µ¨-V•Ü—"´@^ú¸Y…\vm ®Xت(#Hø¸´ˆ’ÁêÑ8#ë7c³ÑB*:–@ðƒél1ÄF¨'ÙØÆvCj[sã~ÉRa;¼6¬@8®lžùƒå¼*~ît°¼²¡(—ÃCmغ [„QÏîÈVÅŽ.­Ü%,o¾£âÓ’[S¿l:g{bË/š;wãkéÉFJ{R–{VKâýÕë~IïííòøºÔZ²Ü3“Ÿ­ôZz¹ƒpêžÏ†mAÅbtî]¨«…Ð+:nyßÀ·N½Êp8eþÃç~GvæHf©ñ–íò:‘I½9âpW{½6fš<”¹ qã>]þ+ïÈAþk²NüœxA§»èóêö+Ùã/^‚ì”x+æë)÷“qŽ6þúÁáÖOŒ‘píŸ>ëÂÃÃÝGc^ŸàïÑÊ±Š›§0"Ë· Ì<5I禲i£4³ßÓ;òðÍ]´6ÔõÛt—ñL8€êq Šà™©ßœ:[@áR\}«oçɨî\}EǶS¹×9ß3ƒë0Zjj®Æ4ßqÙ[µf"&ŒÛ1ž€rö þï9Ióy‹Ó·–âªÖC$Y2† §÷Š ¦OñBrž02º¬/õQÔÚEÑy)YIÑ*ÄÆÙ@‰Ÿü˜¹<23?|…ö·'¿gI¶Ï: µD1–§ˆ°ó:(g¿FEsÇyÉ=€¹•u¬±2æDÅÀuNˆÒûE~ßb@ ƺ‘¨,â>¼8œÉÖc5ØeúTd=S©ÀÙ;˜IÄ؇… t^&«õ¯W%4ñRŒ”¨( Ò[á‹þG¤0qs‹=X¦-¢úNÉÉÆrîBÃòýªšÊR°;óÐÇ>…¯5Ñe%÷ Ím¬1'*&Õ9)5¬/ï*ù¨÷ö( ã>žÊ"rØ©ÄØä2ÌWØ ûIï‚ 1ÕVÞŽR$pRçU»xíðpmt ‚‹uãxdÕŠõ9Ü’À Vá>Qgòb§E0ߨµWe›-ª©ñ,½êœlJÀÛ}ÖKtî'¹®9c £7+³®wªF¢°Àû«“‚¸Í.’îFWù•ðßš“ižcÓEtmfˆ–YðÙ×¹Á­Ú±ÎµÚÏÀd¤¿©1{_´µz;¥L‘É”h3•ÈdªíûÜî©$êzÓ/,nQÊZÅG3o&ºœ²œn-¦;'Þ€0ÌÅ_Ii¿ ÅEª.-­×Uà‹/~öì¶Á+ú”ùafr ;%—ÒŸÒ÷nNzJ®–[Êñúç¼ßfGÇw(‚„Y9½ ‹éëÄ`»ÇºŽÉUÓcÉ?v‚  Z¡«OÅÍÿOkÁ´§‚ýN-ËmΓËI4Øá—"¥úÿ[㮈÷EóâÊðoûçzâíJ]õÛr¹Dì¶$š6Ç•¡=¢g&ù8˜–YŸN©K4Ô H˜¦•®Ï¦ ˺ÅYš¬¤@.Ì! í{?'/јÀ40ž=½ðCôŒÐ­£vögqýÞ•¤­â5µ%·vO›ÊʃҶnɉÚr[§ò¸Ð³›k·OMÕÎ4/«Û¹ysÝŽí­ñÎ +ý "ŸôÑ„¶(¹‡Øj“ýJÚ¥Œ¯ý ‘I‚çpm 쉽éxþøîü€¡ÄkÁtrýŠ…Æ{òýëVŸ„ø‡•7 Oo›ãŠÀŸx :Vúp±Aáí?õòà‘•ÅÉ„„&»’–¹Þàåݪ|B¤I1áØ"¬nô׋bIÚX³>/uUx6ÓìmÀÆSï³s&®q8ÝF ŒMù«…⇟aSáéþ©$dâÂÊ3’ÃM3ÂU§/{´|ß“>/ܧûÒOA 7îoeè›@߇…pWëûUR›¡—jwÊ4;.ïlÀßõˆ1Á.`(ðºZ'›ÙÚ<àbùù¶Î-Ž—\…Ù§pöƒÓúúú«Üÿ±Ãý‡>&ümB+ÞeÕ¶_b¤¾§‹X×À1 ÿ¶|Ü ËóúŠ?2=ß³Ø62ÃVÄ ÄŸ•˜%ÙžÆ%éa r}ª¾'4Yâ¾Á âb%’j•n’%éni!`AG7'²«, „¸eŽ›#R­ÈÕâÖç^ÌLï MÕ‘ëaKr³Éb\ºä´õ~pÉ\ˆ>8òpRÚŒ®u±ûÆ€ú±­ŠäŒYHÙ²×½$¨Ÿn©övéì1ïŒ2¸^¡+ ‘03)9³ãÚâ·±Ëщ!ž¯OaŠlVÌvF¬y9l¶Ü¸Kú’•êÒ¶Ø=óu5÷Jªqr_c‰)™Ï_ñîMü„¸”µ1`Ðmñ«¯œ @„¤jÁè×Úà$NZ`6˜î6ß÷¸wÊØ¯m™›R‘ìñÛ®®À©€AbÑãôˆ­åÛ¥UêÆà,¡$ýû¬X;ÌoÀŸ¦bór:–>ËòËóO:ÎÍÝ.õ”Êò½ÿ>’åwtµïÏ÷ýzq‚ Oï^µ²¾ftt:Éb˜àÖº²¶öûïÇ‘™,&ܘL®çŽe"´K‡­ZÅ×a‹9f  ‚‚T  F” ¬l[ öû“z·þÀýgÑyUTu÷ß©=Wü>rÁ=q¦¸î8—¯4n7×Ä=Ú§†ìwݸ?™¾ç—ÿ^>”¹§¤—Ý…0Ì3%)+(’ÅT iiÑ ²e‰A'Ù€ŒA£3àÀc%jP¢SÑÁâ(f|>Ñ®†˜øªÄúdümŽ\Z–© Õgf§°‹AZc­¤tWFã)õú°2rÍ™¥+=ÿ^â¦ÎÆ#NIj$*QãŒ@r«‘Huþ8,ØB¼f{5Ç 9X;iR"DÓ²J󤨢>fkv®pº®vJU¶NšÊÕiã¼¼n¬|5&ÚÈ•`óx@@^Œ(ölùà^.ãicGÇFÓAFnî!ÆFSG‡i#ãPnΆÙùeã`GÎ2Ãï¦~ÊkjúÅ´qÊøû2-i½²§¤¬]½š¬ÑŒ“ÇŸFEeÊâú¯Ðü)Mò;dý±òð !–$€,^KÄb¤ˆ>|¡#Œõ˜ê}Øëk¸è¿ŠvZ<[‹åbµ Èüç„I¹ûIŒäu•+ÆÞ«&°^SÝoøÿþÙýèÔ;AMô®–wmÿXvýb ÑÒô—d\¯_Cÿðœðv ïàL{ƒ>­-]tûÿ=á—©߮퇱VééûäGxô šD2ßËjŸoלմηfùѯ_ÝwUù§<2û¿,è'èÓ_jjÙ§ómA>ÉP¯Sö«—4e³|[vy²Å×¹;# äY1\Ü í(×ߪ·[þ|B=¥óÇIT,U'4"BY0Ÿ¦R µÞ„ŸRßé? ¢¾ðAÜp6‘ªHEÿƒøáš§[+Àn3AKiÉÂ=üâèÞ·8ކé{ù¼6˜.Xv9ñéûánQ&wû·Fâó*F[øC{¼fóÔ•«ÎËïn…G·›7- ×ZÂNJãŽZ ·Dåç¥&òîÈ `Ù.(Å!ƒòÊ'ìýƒöÈOì‰ýމûÉ\)Ãã>€"–ѱ„7`QäF‚ÊSž,f½•ÏWDbzT‹Y²ÿŽVVVì²øåÌKldšV%•ѬšDY"[Jv¢«LR¥QO;éC7Y¯ç& Äu$g|Û@rìðèçoÿ{Ý@|ÿÜ){/FÒ›øåz4rÕL`ƒ É¹O•}3²<]çîã=92‰õÛŠŽ¿¾]:¡ªÜûâf:øÅœ²«Öžq4Ì÷ú‘é^ ƒWÄšgY{Ã¹Š Î|Å„ŸÇ–Q¶ g͉À=©Ó²¿ÑC/PãÆ`[UwjŽ&¢v _ORiÌ ³«3í0/DNâ1È"dÂÆ V¬÷Ä}uùóÁAc)puoØà—s…uqî †e×t>Ô÷"õ³9L•Ô6ª9øÒ[ï|½s>©ñµËA¿U¬YHúºØª#¸Ï =Uöeã®óÄ·Òܶ( ¿g™­§výüOºwŒ+±pó>ÇÙa³ÌÁæ²läÿÅ’“–5'%&'¯:F$jié—.ŽíÉÙMTÉóϨý ޻鳱³Ö†}/YK‰L8¦ï¿=xæoгËÀ²²©JÎ\ƒT»Üú°aN¨-¶åŠo:Z“sÚYæä<q•Ýߟ´”y ùÔÑêÍ‚¼œjzd`ÂŒ€:]„ÂúK©ZºTfQjb› µý5M ËÐàµWéê7N)‹1ªC4u,S/äžIÃÿ^KÂÀãa­«¯~½^ ÙÝÑrlú ¡Ù[;Ê/jˆù± ì‘x¥¸õô”ɰ&=Ä»®–—4É-0IKÁ(>”wK+·„Ù 1r+!IB)GFWõ™Å®]„ËK±èÔ­ÐÑ9uêµµjX¨`˦S ÁÁ­¥Ê±Be–Œú'ÞX°T´-Yº;Ì7-T$'~Áä#¤ó~ *÷žÄœ6Ξ2Mø¢põºt} €z»,Íå Ѽ{íÛüðë½Ð/¢HŸ#!‹Ðž5Èd³ç˜ÄÐ]塪Aèßkjê²´zÞhÀî¼aþ?-G“êówó[d{* ÜÍÖú-Ò|ÓZ š¶d¯JfŸjry5,2“(6†-Ñž'"Ü "OŠÍäHKør}àþÔÂ%wÄ?Ï6²˜N¦óŠl¹[ã+ó"_ÕÆÀa›±öè’Fd!ú´˜6v5 .Ôèm¿s*%M¨´Ù1ÝÀíÁUá˜mþJÛ]bkÏw{ìùq*Ê#ïïê;º9ÞÁÅÝŠñ`S¤¿N®šÓåâÃ:rBß27{ýxvŠñ¡ehä¥zædîŒéòÌ©éjdÅ#YJú~ôu[ÄKÇJ±)cL<æ³Øl)[ÚSÎ0='EöoÐþ§ª ïLlâØ'j‹˜FŠe8Ee,ä5çn“Öu&«7á‰úÎRKýÜÈt–~_!žm-7^»•ÏX—–ßçTÙv†ãFÜz•u'¶ nöÇæ§Ó"Ñ4PnÝæ±ý¯A–*…JÃÉO©Pjê>šFÅKo& ©ò$NØN üƒøÒŸô<ò ¾Y…ö$ÏF§2¹²*þžñ°»ãб ÂGÁÿo¼÷²w'y™°&“¯:ìÚV˜°X|MPRn6ï5ɴѲï\›„—Ï4¾# б>&Ú…¥ù€)e›Ís銃Gó¹xùtã¿04¾ü&(\ðde~áç8áºæ>©¦ ÿ€Îȵj§Ø¹öýêT`mµu­ 5u½`iLÕkª«×?kùjg­´†!>gÒEu8Ëü ð“&Ú¡5Ù'ºçd…½§äüÔ㸠ÒìCük§”MNú¯}+òaŽ;`½–8-^.¯’úëÛ×mµJRKEP7jMA¦°ðNÅ@õøîí¯Ú¦îªÅ~K±U_ýUäyû¬„y±§›3ÑP}”ßJÒ~÷Ö½P° «æ˜°ú_¥ ÒgǽVɉᣖŠI 4mƒ[B¯eû[°6<³3®¬º¾’üjý#¡=Іj·1?îàÉ'@¡`Ø–à{(»«ÍS’þ'=à2þ†»A’Õ»à}‘bQÄŽ"ΤóX.…ò–tm ‘À?tî J÷E:í¾Ë#b‹™–îUÍ.·Dxìzqó­— u˜Œ–KD²±âdK&ar8™(…—/51èwJà‹’=xø3ºëÔFȾ#–ðA€¡âùsBâ¹}W,!ÄPðÈ9æú^õ‡ç4¸Þ*À¿9ò­fÂïà7ZÞn3¤`=« øMÂ:tR®E…ë¿.jÞáVùnHaÕøŽHá­— ~“°>¿ƒnHlëLð›„u)è†ä ÷—Žð¼ ?‚ÃåSªJ’èY? ^†Arñx³ÎñþaͲšG|òxB5&%´þZàeøü’K:ëWð2ü’K†$ë yƧ©÷Ê/„Éý£PfLä²øŸ71F/Ñ|€{O¤ßbš1Èÿr{ï(è>aò]÷U“¥î'þ™1RJê×Ô"êùø-ƒA– 1öci#a¤ÖšW~ &¿SŸ»××—­‚̨uŠ«÷Ýò1@fcW]Èêê¶ :¥öâµ”ºˆÏfÐpôkÅ«÷9V&ƒÌQ»h¾'}-ƒAf¢;íÏuxާˆ]WƒC‚yëE“‡û»Û›‹3=·u‘1JÊØ>þí%àv0>bpÁ-vÇuAaŠoqD4¶GFzÏ7 ª‚aÿEL0P¯ÂI°¿ù{®+ù´Öc±Ëf.‡ËÝKC_0z@ƒˆ‰3’0D$"íÍ\ý¹ý]œY²Þ?ŸEˆ0”Ÿ7„ì÷æ @S{ºçš«ánÂ1¸¸Ë¸@ò@*ÝA:/O8„‘l©¨ô^V¨üUµ[ YÃç:ø ½W`s}og¬44ðK›Ð/–åÇ—gÇGË4}[J¦ §á9®ƒVyö[v‹Ãâf[]fÐÚtÞï–YÀ‹UíÎd,2\º]Í7;*‘7â´= ŸfW|âÕ®B0VÊôÚàÄ^Õ9å/“¸ûžðâ™eùªøå‡.WIl ï]Ô>3Óƒª?[yE×ãn]0ÙtŽÀó tˆw•k_(%ŸÊ®,„'.β9swÌ/—›ÕhÐ8;=ÞtßövgÄð–Åo¡¡CÑÐú½×LÊIépÁaHÙ«’¹I‡ô¿ý/¬x2ˆåJÕ±Þµ¬ßX9ùϼ÷“µz2ﶉ)ì‚—m|Ѧgî™>kÏ®S¹[)‡²äQýÎA8—ÍÏûú–hÆç{å`™º†Swòħè\–ïbÔfÕß×Ú„ ÿ¢Ó7µ s“_0ƒøøx÷>Eøš­z1b ±jÞÅb"ç~2³Æz+³ý!`+ï9W¹ð=Td ‡cÒ㋪€ ÊH·. ø5ÔîE&f3“Éf3³2¥y£¹âæA<¿zv#Õhº<$¾· p¼8•jè¯ßFî`[羫bÎök@»ˆ< —žzv‹‡Bȹ´bð—I¤¤ãÐ[e¸©µ«H$ ¨d4%åiÀc—Ô2D‡…ÇÌfäû¨’/ÈHn´"ióªTöéU(饠c¸Æ…ZáÑî°šP=ÕÊz`r/_j8pÛD_ -ìø±¹>Ä[ÓGš³wõl…ºÃé»üÿ8@N‘¶„—Ã@xl[›aü˨åûŒ´{«hA‡ÿ×ÀÎ.u»ŠÈ¾ü†¶Zz¾E©Åká ¯¶C½ú•Ϛɴ CÄfÓÙƒtA#„ngÐ0çž-xýRöÇ\­ó’™8<@0Ѽ_‚±õ®oAÐ}è{}7în>Éd áòÐåzÌì%ÒRÌÛÆzs,"}JÐ`j©½Us<&~ GˆUøîÆÍÏ®.Ž·µk2)ÇL òæ{œ5*ò¾ª­_¨UVË+†‡ù…ÄXö(2vï÷¸&ïã²Ó ¸\†ê—4kMÞ¬>}FjB$@ .`±ôf}g;BŸGÈ£%!+ QÿăbÌ'0W4üD¤[gwc²»éðC”½r‚j=…m= ªÙ)§ökðhoîJêjÞª„/xFz\Ö•¢ÏQiÿ‰¦³£ x 5É_¤ Ž‘zæÏ^å:¢aÂ}‚ †vŽŽ½O¯’DÞÊT¯;”±A]£ÐàÁ¸Å_(ÄMˆÅ›‡!Wd”ù¸V…o1SïýUqm»chq°èÆq•™2À +ƒHY(áË€œ:£Qira¾þð¤©q\ÿôä½ül®×ÇÍ1 c®·/ݽvñÐÒjáÒ“Ú§¦-r†S>ãe9¥JÈÓ ©šO õG3[ }ݘ½Oc/o­´ eÚâo4…Ø:¼>ñ´rxeBCy ö Y.Âi†Üae@#£ýhØ”Ìc@jC3î-zòîÊ SÜÅsRNˆªT/u’årlòˆ…ç䊓*®—^åƒlfÏðÜxw’Ô^Ü¿\u4›io*«¨k)Rö6á qA<Ån¥(0ÎãÔ³˜¥Ìö —Ã8ÃT.¥gÉV›;íoÀà š“½e[—•C8Ô¡Xœg+œæ|Èe¸¥N®¹á0 Í Â§€œ —hÞçfê•.Ø.=?M¤LîrˆD‹^…‰ûC‘ë„S{jÑ0C®ò`Ø„c·¢^†áÄôŸÑºŽnŠQP qz ‚XCá—Šƒð΀6q§݉ï‘ÙZNŽ—+}Õ·e.ÞjÄ“Äå˜ü¢ÑR/nV ,ƒZãÑ^µ÷t¶¹zÞý *׸ÛÓ'F³¾aã îÛhœš†L^ú¬ÚŽ*"šgYËj™õæI8ȰaÙÑíau¥šƒ®&¤Tä¾RÒqÆÕ%@@ò^èü¿—ÌKÞî ®8¶ÞGÓQ‘úÃVú<´¨^¯›Þ{I“Û…‚^âÄtx. hRÕ¥ã÷È5œá÷RͳQ5¡Þ—QSLwWWí¿¯]2I¶èlOˆ7è@qöÉ‚„uô!МÃ0‰ñ?IíSU±´úrÞá >1´ã›¿Ûã·_öχçõrØ÷­¼v<œT®ôgò)û®§([ÿážQÚoméî˨!ÞÆy[~.+íÎôñÃSQ ]æmè“Ü\0ú "Ø/ ƒ× JVÜAa*ÄŠ©ÞÅ\Cû,`!jD ;)¶ W1@êP÷»5ó‘î–ºóeå:©¸‰ÎÁ¹Àé¼köÎã jrá‚8!\ ®ò†ØBÜ¡BÎ]4 :Ó¢ï[˜<™Ø[ÝEÙR•`ä0/Öží9<qô)œåLv°’_3Шg­X!b¥ïº<|Ÿ›h¦p!Å…¢–avÄE÷Ð׌|lÛ®'«™ùhËéÙž“{ó´i ×ƒÛ *.hfÚkòP¯lµÜ|N¹éB€íè27)ß-ZC*HµŸ•Q3fuÙñ!·*L•/Ì|› g{~©E0ž©h¼YÐÊöcˆÞ¾ÁÌöïló†ÚI-VA¥VzDm-¼Ü‘X U '‚ ǵUïéP¿ `gAWµN^Gö™NtÝ·aÛªaËVèi‰öåf@qî3]+fð(פß&ÂqŽ‘Nøª_…äŒbí¦Y<Ñ*3ð»(ð3ôÕ…B¦rÕ«Œ†Ú“[ —»<‹4"ÌGÁêŒïÒ’Æî¥éî²VO³áG5yfšanņóÈyÂ?‚}ÜíÙYÙUÄkN5#gIUˆVž1 °²/\Üwz„¡šxA`jìæ;ORUµ·Êâ9Vºh>¥½I‰ 72èˆ ›âŸAˆSdT¥h0 ”‚V¼r6fÔjf‹‡„QþC<ä!„WæÂ*’ΡwLû„¨Æ Ðî7Ã<Ü>ð,‚nWâ/b“Èæ¾ùゥ5ú1JFÔkÄŽIN0.[nœþe\ÿt»F¥&ûuXÖö´¾— µ¸4㽺xÇõ˜³‚5}讜8؉ɟ)lÝ ±k´T¢ å¡òBõ,ªŠ‡”R•ºZRõÖƒ@÷·”Ë«…95·^1~„sP:¸UuB*Žs'î_SoI‡g„àN¢;›IÎÌ.¼ùà C¬ÓïxFixô1†÷ŸÊܲºHc@â6ªCP `ùôh熸s7' í«ÛqÇ…Cy‚n ¾÷©­«Â9ñêëÉê áœäkJíûè¹r§F"x5_l G¯_¼;§Á1Œ­Ãy_mQËóø¬ÉUð™ël×v­åtºèV¯±”ÆbNÓI|ßÛP†÷™Yƒ·¬œÎ'’¹nãëÌ8>ˆôdd‘^îæg—g*át×¼/7|û •éC§á«hÞŒ6£ù)F•7 ´j”=|Ïâ]bÓÓÑÏ£áÀ?>Z…,ÍÁç¸ÇvGÂý½«0‚P…Èi‘FãÇ}êó!Ib >´„>dù=÷pÆÊ6?®.”ºwE‰–SɈšPl bbÛºª›Ü>=i'Ôã7e „WˆªcGðÞåjmrxÖ¥øþç®Iãê'†úòåA ǯ 1fÈðʬpÁã¯õÍ'»P™{+ÆGu1‘$DÌä%éË›Óc=wMY(Ý/&¿¢YÚsykÌêÛo5ŽÂì'„ž§«¼7á“ÐñV¯N̉u‘JšÚ·:5k@•iÍÀD¨©á‚ðYï9¹r—¸1W›Št–³üùÈ$mÝ›ëé¶“sbxŒ_B+¯Ô³RÛ{eš¤FOÀµ¦®J;(¼1& Bl´§1û»v$ŠÄ oÙU- 4®[|ŸãYc Ž@DäÞJYSÇ\ˆVí½¤*»ƒKо«[µFù€sòÂXЭšÀåj“Öd#°ŒÒ;ïÓÒÞº´µ,·¼þyÈ¿ö½!©4óc¼d,3T©ð* ‡šŽoHU™tæãWŸåî"Ä búœMÉ6ó*N¦pÑâm>–ùãv· ¼£÷~k’¿ï³‡HÂíotN³YÅÖ!킽¤î0Ô*_uX} M±ãR YÅæ%+¶„‚ |ƒSx‘"¶|™ Œ®Ÿžë.‹ÞÖº–\ˆ©MLçþmëã#}¹^3Þõ¯7ÁÚ~>¤+‰>ÄÚvÂýFÄÌíévöÌ‚Mb/‰Ï€7%Ü{kÜÛîáX~˜™é=|Ô°e²ûÊD®"dº;W¥_—?¹ø€‹xŸÞÇdÔ<¿º”Az´§R\¾w‡g×R¢Y›òRÜs¸ñ>ªEoáö\}>0d±yâa56|”Ù40p4Ðd%ÐÃGæ«J@8ª3¥+~Ð>~ŸõXÑhBÁ3—ò¿‡Ç¿4LZ îRw‘º!ÊGŒVÎãëÙxÓÖqËÞÕ¹~üööœbΚCf ¯÷mš&Ïï>ãuíyëR&,Œ[ý‡åæܛ5ÏøÅ©ìÓoúŠ>Ȭaæúx¯˜(ýJ95ÒA>G³ä¤æœŸ(‚ÝBt ¸·»â¾öêýg"nlX ÁXæªNo…6ð^yvÀ­WÀg’~eá³Æès°Ó9ôˆŒ5DfÄÀ-¥~Nq‘¸Á ‹§$²»Ý<  Ë1ÒÅ’â®xqÒXE»`¿ÛZýJÂ{ÅQßQÅNÊË* ÁVK þ¥Ûúóþ°“±rÅ¢ƒ²ÅnÈCÝØ• È;5*“7AC_ ){ëÖÔˆ K®)à†­@\vø¾qx0o7ô„CÄŠ8xkž[WK¥òùÙ¤š¯’ „CèQPûrÀ•NA4gqÍÍa[gx}áÕ‰!Áß'ž¬šé¤ÚÚÄÑ#û&|nfG Û¾}[uV,úª2ÙBWSo WL­MÈ› KéÒæ ñì©‚V–Pf¶¬éfñ§2PוÞúõ'ÿ+áCÌà«FMÀ1‚[ýùww‰7ô¥4\I¬¡e\\N·?ͦ“ñ¨wssýÝFHSŸŸ¸?¦5ùÄõ°uyôU#g…zm¦Ò³¼VÛs8¶,e)÷¸ˆ÷È;±NóYÅJèÞ‰:qF:lÏ<äzuÍž÷»ÚYZc­Ö&Ùÿ§óÃfuó‡¶//Î6ýkËÊAö¿©àò¸Ø¾Ô¾ù¦møÿ±ôXÓßÌv·¹0Lã4Œ6¤ÃfŠJ‰lì÷5û§ÿål·þÆ*MÝÅÇD¿Ïgìn·­˜3áÙ©5=üz ©vΔÛK~(>ò]ÛF,¾|×ë˜ëÏ<’Õ—'—ܶ<ŸœG^ñï±.‰l{fzÊ·YºÉd¬`Ÿ:Î3&ÞpH5 Ð^Ó²Èr_TÎô)Ì‹YÂ&Œìªá©Qˆíq¿_ãÃS.Þe ¡c½ç}¬1hO h™5ñÇYbÂ} °Åƒz³àÈŒ½ù±  nþ*¡lñ°Ñ…Œ—§Öååùg˜®QæId6>øaY¾æ~ÐkæÏ7»þñ8Â,xTx]¨ŒÊoq†Ùb—¿Öæ3^ D²Wô~ÓÍc·ê1v»T⤰ÄHXbÔf‰Ñ›%&n–˜¼Yb¹ ðžèsX+ÿ€P ^²†¤MßÃyŠH]öš=gkú–%gGS™3;\“î³*£aiÎcÀo©Ô‰¢Mêèê/X6gæw-R¼õMØý¡4‹@ÞP"83­ÕæUa`¡"ŠG“@(#Ìšp¨á6«3ĹTKÉÙu]êR²v•ï¼¾”F³.óDzìºa}Að.´Þcnþ«BãKÚëæWúÀ‹qø°Ã0¹ 7·æôhø CÉÏgúX0tþé©áltYêÛPIð¡þ…˜[ Ø÷@1º×ÓÐÖ2Aä[ħa¤5Å\ËoÄ‘WS+»eÓ¬\hȹ¦¸cãuC…áâ-¡8ªc˜¥B…µß¤Šj䀞œê)´ñ±×°ñMj?žã—ŸÏï/î·ëé¤{su~zô5JjÜÃÓžùL|Ïëa[&s¿¯f¾Ð[µ^—Ø‚½[5*¢à;ØÖÄWª³çÃI³!ä¾Tv±fækn`=Na”Î>£ibÍ8±ö¬—ã›MZ•b¬MKÏ# ¬42á¶a?E4¹Wr+òï ~“½3…¼Û3EL¿±<d–£þØØ—"®mܲÃÏ\Ãà)Ã(4–„Ë-Gâ9Vöñÿ1åú5ŸüÉžãkéiš‚„G#êtÑ 2ïN¤à§½òUÆ”Ÿ\ý=°¿;Ñ+s§3^ „²W´vÓÚöDçØdoÛ¬•ˆ%¬•ì.¤yvÓ´cS„MÁ|ªÉ(ú‰ŠOêz`ÂËoT ™Ö8œÝ½ôNj_ºhï¸Z‰{€«~/[®ç’³ 7= Yð]!¥JJ}bk—Œ9"aiA\Æ>ØÛL¦XíåèÎ) þBzÂrjwÛ./Þ/×]¶¬­Ýx¾1AB}ù4ßaR%¬Sࢧ*R}ÚP½G Tð8 ¨I"Þ¤í£Žü£˜A‚˜?M¦tB·ÜÆï*þøø‘àín7oéjú„¬{A Î©eà],ÁYüeÃU`I€¹¼âµ¦é‘XU xI³º0/~J¼uyÀœ‹è O¾q…3ÂØ“”7´:ÆLòÇ¢kj]¬ðã´·3´:DOÄlíÏ«?Æ’éxð¤#dÁQk ¦º4} tÍ2ü7n%D¦®ç—ôV¶Ò{ZƒÒ0é C ]þ""˜EÈÛ¼×Hó`¶ö*e0kˆ–kÇÿG³Ä»÷(wg§|îµ÷ús»Î6}ÃRŠìÓô}W2,pë ŒÝ²ˆ\yTÆ®íjñ”ní¶:ÕŸ¦¾Âfb &¡96x|;˜nÒÆQ†lŒßÀ P6©N‡â}G4“/ÍÇ·zØ ð±ÉˆD YâïÙ=×›ìÖOëÂÚ®_‹h¿[-ÓÉ-|y¼ ©®Ç±áé³'i¼ln…ÞÔ¶æΰ^ò`þ²¹¿½ì²=”`dÈ;}êSÔI¾«‘/R7í|æ45j<\ÉMQbÅQVHÐb¶,Ìèlà·^ú¡qA½kŽêùŽTŽG,ÿÚB+‚_{³‡;çááÕïê1ШÏã(þƒòˆÜËá¡mÎ=na+ýµàˆðúðÖ•ßg¿S_<›:\?á‰>iâ$Q ðQ4ÂÅ7(]wZOÛÒ¡õ6ô‹×ž—oÔ¤6a¿ê}Ü9ŽÇÑjýß‘]v=ÿ«ª¸Ô‚ Hƒ+=˜d,dv=ö\/eÆÑ;‘0Ä¿UÞø‡Ð ‚•qÃI”‘ô÷pÛÔ(’"WnÀ {p0CaƤ]e !OæF˜EýHîÔÀ•fà«)®ù€K†A±z"u†°É„ACõôU6=Ü(èELòÒBD3ªéCªÚrkŸÝŒk‡"{¬»„Àj …;L p4:xOãÄÜ"ÿªcn‡çšƒ-tèæÕ+“>`fiŽT-"aJ5N_‰ÄE®­¸”g8^à°h¦œïÍÉZ2Qû.å¯üÖ¸ò§•ŠÁ€;x#WB,ÙÔ¥Ø>¶l [Ù 4Ï„ál„ò~,$>ªÃcî6æÙOý›'_vWßɇ¯Œû³¤¿™%BÀ P€­÷IÛzæ€Õ°Š#¥ô7~îçÝÈHCI6‘&€^p¡‹4§I@ßL‘xc¡Ðv[s¤“GEw‚bÌjÅÇY’QÂN%ß<ÏÑe@²Lœ£´L#_ìݦ`*ß1‹i®îÊ“.ä7©DŒÙ¨Ž¡Á;®™Ÿ•€XæÇÀ’%Œ6u9ÿaôw2B™ñ“Ý«3…äC_…†‡cŽ‚O3§±^ã×3ât®%”؞Ͻ¾çEÁ‘ÎÐmÉÇkñœ&UÍ­+{r$¤Q{ß)¤"¼äzŽ=8i Dê´„‡ªp©£§}¦G¦…ZBlWy©Höã“}”¶ŸOÚ©ÀY“À¨A\ð‰üÊì‚­éÜ;<½D˜&…mgSª†ª Öñdÿ ýçZ×Çifl—ÐO#˜ÚÎùý×awZVüEÐiawYèä#ö¢uZ´¼NÞç.ìD/ŽÙiBÙòúHÑDõW¸¬.„RëŠ6z­–<˜¸˜&#©4‘®kH öÿµ ¿˜my}&48s:\Ú…G]  ×Þ¥S+¼aÿ&lŠÃú°†aqýbGLG™~Võ Þß™ÿ§‡åS¤föM µo2©í»2©¥É¼hR­uıŠ{,ú³q#N^Ä©•w~ ³.Òþ¾îȽÄ~Ó8X]‚3X‘a@{r˜3$A*ÂÜ*@(mg:z;ÓÌYF°ÄØ$û&²@Éè9æäÎæ-É'üÞ‚ ‹z1q I©Òye‘“WPTª\cÕêªQÝhhj¹Ž«4‘¤­«§o`HŽ2)lêÌVfn¬Úm‹gÏy°Qý9P&:}StàÖµz® à¡¶âCd“oi,ÞÔgš·5AL0H’$I’$I’$I’$ih@f?Ïùÿÿÿÿÿëwäà‘Ü)¼Ć:•ú+ ÞñÒ‰¤ñyÚ¡3ô’ñe&˜É¾9jt_Œ/ù"€CûåµþNKæ:²1$”…Y‡ÓÓ_²4=³®{Q±…œjø³ð( 0V„EÅX|‰%—RVµÈ°«Z¬û2r@ÙÑ}YpÀI¯/½#t^&@Õð ô\&€á²`H[Ø+R²¨—”ªZQ5Ô54µ\ÇÕHÒÖÕÓ70$›6«±9§ˆE\î€XÄñ¶“´»mS‘ù'[çøóq= @’$H@P— ‘”¬ è{dTQUUàY”¾­TaâœåryBÄ…K!l£¤Æ´ÀÒzmžßS¨î‘-â²,"†ÙœFbîu^sáÏ!ž ;Ö(bŒ:[Zß.½Âû1lÇ>Ž) ý%•¢ºœìÎpÆÙ{îYuR’$I’$IÏ«¬ …æàÄá D’¶®ž¾!yM»XZ33333333[^ Ï8’$I’$I’UX’$I’$I’ú_–Ùáç°쇫ªšº†¦–ëVwëÅPªŽf ¼p†¼‡Bsp‰EL\BR*.5‘¤­«§o`H6=µc{ZuÎ4¬5kÖ¶SÚ§²$I’$I’$I’$Iê‰Æ¶mÛ¶mÛ¶mÛ¶m{y3ïö¹oë±)‹BIR9¡Ø0ÓÓY–¯"bÏ7YC/Øàѱ",*ÆâK,¹”²’E†¥XUam,p›ßmöV¯vI’$I’$I’$I’´v}m×ÙŽ;Û:ÀØ6 €mÛ0ðüãH¹ÞHX¦u¶Ñ.‘Nq¹=^ßï?¢*""""""’í¨MQUUUUUUËÖ@WUUUUUUUì}lÀ'¥ïU‡N¸€ªªªªªªªªªªªªªŠ¨ªªªªªªªªªªªªªñó¹ö’$Iò$I’$I’$I’$I’¬uH’$I’$I’$I’$ɶmÛ¶mÛ¶mÛ¶mÛ†€ªªªªªªªª’I’$I’$I’$I’$éý;s‹×ŠÍîpºÜž¾¼ûË8z’$I’$%I’$Iú{z¦ÏG€tÙ÷ðœ…oE&ÎÙQ.—'T@\¸Â6JjL ,­×¦¯OTUUU I’$I’$%I’$I’¤õMP$Õ=Ê-â²,"†ÙœFbîu^sáÏ!ž ;Ö(bŒ–Ž¥õÝè—+ I’$I’$I²mÛ¶mÛ¶mãÖ]y¼óÃ3ÜÎ9çœsÎ9çH’$I’$I’’$I’$I’$3333333333C@’$I’$I’ÔbÛ¶mÛ¶mÛ‰ö d“$I’$I’$Û¶mÛ¶mÛöEÈ7õZhíªªªªªªªj¨Ïb¡ú2ú}ï@¨‘¡„¶_ 3—ªªªªªªª@퀪ªªªªªª:ÐÚÿ1š-ß3ÓMÊ›; œÔôbàR& ’Ã:pEôÞ˜X½qÌÜÞñ´½©˜ûU;c+—H#pJ-+Å;P“)¥Í  ¿/”‘’\-›LäÖwbmôÈQK=s*)=YRõ¹:*¨›GŽB¤Õ,cœ†U¡É”P5´çT}Õ('&UrÕÆEÔ~w4¢˜žŒ–‘¡wDPÑGW RJ¯Ø*‘D?WãiÒíéawòÏ#)„¨@:X¬ÿ‹¯;°^¼ùƒ€òå ÍV<‚@A‚‘QPÑ„´ª’áè˜X"°qDŠ-F¬8\aÂEàˆ4í¿¾£¢"14 ñ&k©Ò¤Ÿ3eÉ–sþjÈ“¯ aЧE«=³h3×,ÿZ¡¿á0ÓMÍøà•>K˜r×{ë¬ôÙkocsÖ8ᘵ ‰u8EúéóêœÓÎ8ë¹"—œwÁ:ÅÞYáªË®(ñÒk3ÈÉ(¨(©uÓÐÑŽA¡RFeʽP¡J墫l5¶ëQ¯NƒF¯¼±Ó5ëmpÝ7l´ÉVÛ¼3ßG´Ûk5v‹ÂÛ÷ŽLVýrÛ¢A£¦øgŠHD+UËF¾xäÕðqA©^Ã(Ö”Iƒv©­D&ÕK 2Ãb£@\j|å|{±LµPU¤”VÜ[d¥—©‹_£“Q¦”;6Aø}øG½úh×CM¼IlÄ¢ÿ*ÍjÿFrZ›“Ãpû¸)u°á¶7el$Ô:‘ÿ¤í¶CŒJ+ŠáY’ŽÃMÞ>Åð-(†ÿ…D:l…JÂMòÈ=ñ•U_¨jGdW}!ÔX¾ ·SÈjÓÞ©éCŠaí7ESM;#F/ȾsAÒ±Kò kæÅ%ê8§Ë¾Cö¶ãj+¶-‘QÑйÂ}ðˆÇ<å%¯xÍ;Þó|æ _ùÆw~ð“_ü&Žx’H&• ²È&‡\ò)¦„Rʨ v:颇>úf”1Æ™9!€íí¹s¶­C®C¶¾Ô&($,"*.)-#+'¯¤¬¢ª¦®©¥­£«§ohdlbfnaiemkïìîáéåíãëç_i]ÀT–|?ßW'KØø:Ò5ʺï ë˼™[¶î…Ð0Jxk8uÁ2Ü+xÆ*¼4Èóº{ãIãÞÛ_Ý-ä—ÿ¹Ç¯¼Ë͆\ÈÛõë¢x(ŠÅ”(_%•”†+C¹ã£»CMt6ÔRÇÓÚž¡^y¯ŒÉ}òGé0¦“.ºé¡—>fõcžYbÙ¸¹Êëlp–sÚÎs‹$Ô%í5CuxÕo~ (¤(ÜöØm±,Ò¡,úB¸– *©âŽ(îŠ wGï÷Fo(×D?µÔQOŒ†h <«£l2w³y[hU>ÝÓæ¸]ÆÕ=¯Ü¯ÿƒ 1ÌÇ9c­YóÎ1Ï‹,±Â*k¬³ÁY6KÈ-so³c]’Î/%Ód”³Úrä¹Áý<â~¾6S¢\Îβ:J;ÓŸ…Zê¨'F‡öNºè¦‡^ú3fœ &™bšfcžYb…UÖXgƒ³$HÚS†KÝ›|¸– *©âŽ(ú•dˆaF8Î í'9Åi6ÕÅþü ïù›çøíáJÊ¢Ó¡Üñµ²‚£;C¥¬¢:zµkrS¨¥ŽG£êð´1ÏPïx¯ŒÉ}rôÖp@6˜ÿÙèPxKôáШü\Ôše Gµ·Éçùw¿ƒ~k0ÈÃŒpœ1{g‚I¦˜f†Y{™cžYbÙº+r•5ÖÙà,ç´çÙ´FBý–=n“$C–yÞå]w‹«z,ÈBŠ¢]ÝO…bY¢þŠh&\I©¶2÷­\ݵ²‚ý¶*eÕÑÇB µÔñ&OÁ£ú>mì3Ô«Û+crŸÜ …7»¯”¢¬«œ ò¹ès¡ÉZÍöÐB«ò!ïØÃÕ·M]»r\ýóÊÖ褋nzè¥~s0ÈÃŒpœ1ûg‚I¦˜f†Yæ˜gE–X¶þŠ\eu68Ë9mç¹ÀE6­‘P¿eÛìØï.I×5%Ód”³Úräyäwcì¯îF»ñ“P,KÔ—ú+—7FÇCµUj´ÕRÇ£îw½Œ±?Êÿá**·ðû«ÈaÚ·Ë8Ï+w˜¯“.ºé¡—>Ƭ1ΓL1Í ³ÆÎ1Ï‹,±Â*k¬³ÁYl±Í_®FÏ_®†rV[Ž<79ó¯†2ÏѵTPIõ÷œ%ÿô.÷<}γtѯv+4Êfã[hãyú`!†á8ÿû;Û›Nn™w›,9òÜhçgƒ{e÷_ ×RA%UÔDß µÔQOŒýÑ/ìü;¡‘fc[hãyú`!†á8³ÆÎ1Ï‹,±Â*k¬³ÁY6IÈ-ón“%GžbO_u( ¢è…PL“r+‡Â»ÃaÚ‰;Þa—¤>)™&£|‰‘q#õ±ÎãL0ÉÓÌÔ'Ã+øj(&I†×(j©£ž³Ì1Ï‹,±Â*k¬³ÁY\í×ô¹?ï¨D¹œšèW¡–:ê‰Ñ¡¾“.ºé¡—>fµÏ1Ï‹,±Â*k¬³ÁYüéì® eîéµTPIý»­'F¿úb˜Žó¿þÖ6IÈ"ç_J(Ç·êïÿî ׇd‡ºNºè¦‡^ú8£Ïë|OPÈ¿´R¿¬2oík©àÆP*ew8Û;µßåÍ{w´à««1< Üà 6ÒdŽfs´Ðª|È*‡isÜ.ãêžWî7׃ 1ÌÇ9aþ“œâ4g¬±©~˸mv̱KŠ4Yu9ò8ƒ†fZhãy¶Ø&KŽ<¯wÕ¶<5éPL‰r9tÒE7=ôÒGR¿ ïðºÒèÆP )ŠÞoÝ·‡bY¢þ o¹+)õç»ÕÌþvýý‹¬¢:z)ÔWKúž}Ú¸g¨w¼WÆä>¹_ß²!:éÝ7ái<4*7Y¿YŸZ•¹[‡9ªo›ºvå¸úç•;죓.ºé¡—>þÝ÷Ǚ`’)¦™aÖ¼s̳À"K,[E®²Æ:œåœ¶ó\à"¿ÿ6V¿Å6;ö»KÒõLÉ4嬶yJ=·ùð:ÏE¼Äo¡P¹OÅ”¨/çÿ/ïò\ÜãM~¯Uîó|Àñƒú¿&cZ‰Ó¡']tÓC/}œ1vGŸ]’æOÉ4å=v² (¤!ú‰ûóëÐ(›·rˆÃ´g‡]R¤¹Ô ¡€KŒ*”wùª¸[ùù lˆ~åŽ'B£|»ã&}Z‰³Ã.)ÒT»*º—{&Ÿ ŲÄq™Ý–Ë¿ÿN¿Ë廹×5~@VG_5f©¥Žzb4›«…6ž§Ã\tÑM½ôñï¾ùÏXgÌãL0ÉÓÌ0k­9æY`‘%VXeu68˦9rËž¶I:ß Yåy*«ÔPÈï«2÷íZ*¨¤Š;|+ßÅÝžÈ{=m(7Dqwó«¡Q6Ûll ­Êm2.Ÿ—ýÆ0ÈÃŒpœ3æÚ”[úm³cÌ.)ÒdÕåÈsµ>éþy“ÉBŠÔS¢®œ?ìòSÁÓá©þª¾ÝS¨kÒ¿•8úvÒE7=ôÒÇcvôÙ%iî”L“‰Žýq‘‰,xc`d``àb0`pa`rqó aàËI,Écb`a‚ÿÿAò62åd¦'2ðAH 0#³!ˆæb)0ÍÄÀÆÀÃp Hû0\’þ`Ýžh Äxc`fbœÀÀÊÀÀZÁ*ÂÀÀ(¡™w1T0~á`fâgebbbafb^ÀÀ°>€!Á› J*|~3±yý«bœÀ¾q•ÃdóWÖ™@J ˆhŽxc```bf ’Œ`š…aVaP²X€,^†:†ÿŒ†ŒÁLǘn1ÝQQRSPR°RpQX£$ôÿ?ðÕ/ª ‚ªVPª²„«büÿõÿãÿ‡þOü_ø÷ÿß7_?Øú`ÓƒÖ=˜ñ ÿƽà û º¦-xc@€ Ý@ "{Xg20°îbþÊÀðo%ëÊÿXw±°þÿôo Þɯx•TWväF dORÎY”½ aÅirsÎä„·þÚ¼¤ýÓT:‡¿yPò|4(jÆÙÞ‰tu!4šâY:rç,ÞÛô, ÅÀAòë[iíý(­þOiÀ_¤$oߦ¼Ê|’GŠeÉT??•˜@$·ÜÛS:§¢ÈI¦ß¦ÒµiE=p¾Ë²Ì¯›euloX™àX:øzúM¤“¼Ke"é^xÆK\,ëg;à5-Ö!ý1ýc× ¥Uû Ée§µë¨_pN§ulÏ×ü„|y5Š(Í]Î{ØÛ¶b¡žä]à8“iµÞÚ†JGHc7®s)§€ËiüˆYg‚ôÁhíÆEAˆ-í£€Åäר¯÷ã[Ùâ eèäçãq-¥ç­pOL"Þ‹ c B…2a=rкœ™1^\¸²Óè†2iáœq” ŒSm|Þ¥LÏ`²,ŸíøA€Ì§¬Lveò(”iK4¤q,Î1ŸÒ?;}Ý6c=T&ÓGbÖn…2ké>tC™ƒ¿yÅE³¥1†Þú/ž¾z/øQ9kÖqÞÒ3ŽÅуüQ( 6ÚxÊâ߬¢ò0–lÙðÖw)¢¡*{SCr~,†ã‹EcÖV`Ù",šŽOµ*¦{VDLô¬€ÊÊx•¢j¤Í1H$NOúÕ»ô²I-ò/›{­í,Ž™d2¡‚+2œ´Ó¹ºšèfâNYZI~ú6…‘ûÀNg ´qqsx€2tH•–£ZŠU‹a8üK[‡A÷b_NÈSÕ›øÅÔúdcEɪ–C¥½º~†*×*·LrŒµU_»¼®~ͬQ÷Æû˜FôŒ1Š‘bãæuva½©&ÔôOø¸žˆºŸ¬c±QK®êô‚®{¾i™"­|€Ëó,‹Ê)³ŠaݹßþÞ½ýGößr|„\»šƒ OÏ}%’yœýÎ?ø¿±¥gVWdø[+Køc+ËÝ¿ÍkÏÊJ·@î:Å£¿áà¨"™õûÑ4]·_ DŸQÄjµÀ–“x|ýÔ ¿nÐ4E½¼Ï—õw‡du"duäàwZûu¹»ZnÀu½üèÞÐ ûè ¨Õ{ö­¬ŽÔM Ã mé65¶at­¬ØVŠ*ªØ‘¢ŠzÓÊúˆzKŠz[QE½£¨¢Þµ²1¢ÞS£¢ÞWTQ(ª¨-~Yyxõäú „æÿÿx¤X@KžY¹‹»@B.F” „ ¬B_Ky” ÔÝý¹»ûûÝÝÝÝÝÝÝݽé¿{‘¿¾ؽݙoofgg¾=` çO…ô@† ¬†ú‹}…Þ|÷’ÖæD4âõ(Ì¢¶O½fëô} j@ ŠÝ€Œ Ë¡@ÒÀ€›—=³YhE±FÁdRe_UmjÌžzÍ!UZJ3(f€ô¢ u §3…õ•ÿ†ªš’u,ÙÄÍ7¾¦¯Ý;LsÝñû̉ɖô0ëɧ“ I&Z¼ùÞÊœ|JcÜ/ˆß¿5Ç×tŒì]Ѧez³Iæ¾ÆÆB™B6¶¢ØÖ×Údm´íÎZ#nw¤5.?—~êÝÝtç?žÉNÒwRŸï+L´§SÁý§¹öÖŽþÖÜ 'à üãïº[k [5÷{Áßööò¥ÒøÚÙßâçÅžÙ!7è­ÉçOvUáµ}  ë" ÿBQ˜J©‡ÆXÕÿ ¤o¤Ÿê~£a=qÁ¥¢—¬ËÈ&Î ¡[xCMvÐ7Í“sþé ø*ª˜•«Å/m÷Æ;B¡Ž„×›mÜËŸz晹õóùüüú¹ üÛŬÏ'fÃ)•1ì| }*¹IØi?<]÷X­V¿ÕïvŠ‹Â”º­>› j+VsÊ(¢bTØú!ÒR‘S©dE „MDþ•F…õŒ!I(j ‘VäDK€Ê(bXQ€Š<¯:Au ‰NtÇÏu1ç¢MÑPDqáOj=þÔò·žK&cOÇĹž´µ€­¶Ã(õ4ÀhIOÃ}½=¹lç’Ö–t*Ó¡ÆËi·˜)ºkög6Â\vJ¤Í¨ªÖ¨ÓJ8—'Ðb"ŠâˆJߨ Æ«»ß.¤5/'¥$S8ôE@RÅ 1Vub_EÅpVÏ¥Šˆ ){R¹ÙÀ‘½¸Ö·jVó`VÌyúº &pZMN°r‹EO *ÏaG‡<¨6Ú©Í.‡PŠrÒ!W”Mܶ±²Tø¾'ß[ð&©ÛÑ[yÁœ7YÆôÊ?4çÍuü>•æT)E'ʼn)!F³kƒ1Äe^fÊDÖ¬{$B4}¢ô;onyÖr¥]›žýóìšÆ8®ø‚§Ò÷”^ß $ÌgžñçÒŸñÅå‰0S9n*?3Å›¸M)âäÊHîÑåMø"­ÁÄËÏœŠ„Ëo F¸U)ÞŠ“œõ°fe/ôÁ¬Æuº£O$œæDS(0)F´›Å.,o@$+[cFqÅp›±QÊ@x–3®E@*ýËXa2`óDs”}D7©a=OÏ)ž ¬‰ÿ#GHÌb"dÖü¯8”£‘¥€q‰””¸Íø¸Z‰Þ-"d¢ zEÚIkeP¨UE&®ªQ™Äœ(ƒórd˜¢Oj8(þîê/ïQCovrûxjò΃¥âáçì³P`Ô±ÿ)ƒí¾£­UW|ù}Ç&O¾ø „ý£ø””>ו(ížHžùbbpºS+mÒlÖíS3VGº«ÃD¼þžu7o_qï¡ öWû}¦TkŒÇÕrS ?sí¦g]5¥¼Æ!·w/ϧ]e‡’™ÜL>“]ÖÛêÂ÷+­ƒS–Ÿý5ÿ:1x¡º`6ₘ›]µrTïëíê\º¤9jp9ª½IìW(€JAzL“Y5É!ˆ•êᎂÙ,© ç6SÄ>'„^þ?ë y©¬ A›¿}vlvᎹхPh`a´4_ll,Îã•nïßn³»pfö6)xÛìÜRðιÑÍý¡PÿæÑÒ&)¸ Ìœý ¿Š¿F` u݇09Þ™iM'bn§Y…1ÉxÒ$C䈤)®XÞž s…R P‚´‘ÅSE±»”ÊóΑ1±ËŠR`1S5LJÿT‘*§ž{x=^Fþ5D X¡ˆuçy0~.£fò¿CPWd°P`\‚¥ë8ÊyIeö‘É&ÝÒAkAÄa4j”Õ …p`-` (4b¥· fÈcknÙZ`G9³¹íšèèž.F݉Þ4>#1Ÿç™Å¥¯ˆ4ÚòÇG§ŸvõÊÎËOŒõ÷ìz°ü‰ìê]ùc¯/oˆ³Êëî,Ïà«îÄ—KØRþ-NÃ/@ƒ­ºnT©Å½Ç‰–2•²fÕYéÒ§±cáÊ ±)Éú$`ýÊU8ʨ+ Ó‚L[ÜÑ¥!­3ê e—e4»Ã…{hIÔ}m°-ê ´ä´Ìö­›Ò›Ëî°¦²ýqOPHé %âÝõàEa•(¡ò’,\² Uöç?YNn÷ѵÂv/\®+.Çy¶;ÎYçÁ‘v_8ê—£zpq{é%öJ37\|™¯Ë9Ì)õ{y'2jE‚DF‘2ì®n'¯ÐÄJº!6Sj÷òK•8†È[ï9'ÎÒóÿAÚÆd]K6q¥Dµ‚"‚âÝÆeîÇaÚS ¥ZÁ¦dãü³NŽfg–ïlIãw"‰Œ;Ýøñâ–±æÖÉ-½½ ˺,¦Xˆ[ÕÍŽékŸÍ_*ØÂ–é»®?=T¾ÁjO„ y[ÝøÉþƒ‡ŽŽŒlŽÆŠ«;#!D׎'ïé‘þ}¤×SÖò‹‚Õã7ÏïÞ6Tfw†Ð$µÑˆÙ”Ùk/Ù퇩•./ Ý’Q¼Ð«Jba.“1 ` È †1Ý`0÷Åi¨Ž3¤Û‘„™ÙȲ«=7Ç|—˜fÀA;„ÎÔí2#tbɃ{æöþäöm/Ýܽh9ÜjŽåO&,µ7'~T2g_ÿžÑhʼnîþ=#åxGóö‡7®ö@ÏÁOL¬úÁ>øk»gæd¸81Ô³ëšhå5]½×—ÕŒn—}Ú„6²_v°…FIäÓn!ž^mœŠÁm…“y,‹Y ,@c« µÈã S>yሇ³4;ÓB%êj$ª5 mÝÌK¦Êâƒh™Ég›qÚ|âÙ…–K£Þˆ0é)søl7 Öó-">#jAx-î­¯ åCÐ1§®¯¾¯¼4¿6Tk³€ ª¾£2W‡°:(Œd™c˜A-”Q ¤‹W]r…ÂEÅ^`bŸÂŸÊ#zú W!ghA8…U Dåøå|_Vâ Š™ð0Ï‘ñuo=¨÷®c×ïnßõðºµçvµï™8vWßÁó[{wÎ/­ÝÖÚºm´¦tþÎÞÞõíþPçÚx|mgÈß¾ÞÚ¼åc«N\(*{jÏð‰--O ïyª¬è‰UÛÒ|º¸o}ÛÀæ®`°kó@Ûú¾bôryߊšØXWQQ×X¬fE_9Õµû”³xˆêZ5ñÝ·QO÷ŸëZžb{EH­ošêØdÛ¯êZB  ;)Ε´þñ¨B„ªO¥@±JJ´%ð-†l‹yNÁ…Ï a&(¹{Ggï¯ÛôôÞNwÅœâÄc¦êàãðW‘­ñô”mîèÚ>¿¬xÞæÎþC%h¨aóÙõ«ß×;wß³›ÚwnÙÜ”Ð9ܨ5¯Î[Q ëÞ>TZ>´¥cîÖ¢ªe;uˆäO7ád9Å™Œœ¢å+ÚL˨(Ë¥õ)€ØÚù8Ï4³ŽKå|èJYgÖ4Ì–ñýPfZvµ4 ë³Õü%ä½€x:n¨ŠFÂ>OÊ^hzœ±<³•ýðS*¾°4[ÌFc¡4ñ¾Úވ㳧ä™5ÑcWLÑ5™Û5KÎŽ¦3wýû ~PX ì“‚6‚I9˜e^ã0Âû°ÆêLt8£OÔ §œÖé÷Ñðîƒ6ágF‹F´~jðZÁ{d¹|~ ÿŸhAmÜ €.€œ…*5eÇ ' iÕxùôô¿¨ývø£ÛáÈûþ5ù¾V@Þ·‘Ú½QC,E(æ¥?Xé[–{<åô­­XüÍ!«—¼£U$¥ÇŸ<¶ƒ‰ò»|ðgô.±?%  ¼777VE‹#ù{J¸–ÆwN1ÊlPÐdÀ2APd5žªá¬³´>2&qTˇâEj©T^ÁÄÙzˆÍsϺ2D« 88¾©møöuMÑžkújû¾œhÏpO´ e8:gi[$Çc¾ÿ²2¼'¯®Ä]¹hïàÐŽÑ9 ¡³OË:››{ÆÚÊ{k¼Â€ûÒ¹LYC`É6_#`-lŽç^ÁòñkÖŽ®íëim®¯¥–ßïµY´"#&ºûÝd : a„ PÔà­ó: wFò¯=ÂЈ€F.ƈZ­H7Iµt“´ô#D“¸7ɼK•yõ³Î«—ç…z X5çªÓë}ñ±ÿÜÌ’©™K3ÂÙ§–ñ‚þs„Ÿ&xX“ô;Ü­Û þ4lB.(,¢ä9yž|„7:ŠdZ—pç‚u¦–ã ÚÆZó:}é–îk£+ö`©»f¬»xüãßßwì7/nØú™÷î<óëÅ–ÉàÙšøã /$~ôO·4n¸ãù7¶­xù¡£«ëçl½;‚­5¶¡âÊÁ[–Õv}vpÁHoé¼ -‹_89&9¬)¬òš6Þ¹ôÄwNöl|åí»ŸM|ðâXW­>·«ÿš߆_Üþµ—îÞÔ\²`÷ýŸÚºíSdžö:cK:a±¾cãÑžþ¥Lg ˆóX‹¾Ï!°RŽQ¯G–òìPÀ,Wc ˆ† F¥“A1äœ ¸ŽSfô.”p"¥( @ ô… P†“*9iZ³BŽ&…h1‡0‹%jaÓÃÕŠóðæëŽÍÜrL[àyÍy=qrÿØ%%ƒ× —%ž´ûųÓûQ_ÃHi—‚|/ œ#{âAP>wIe%ùRP äºÈs†ºäþTä@Ìâ)-äá¬bÉÔZªR¿§F³e ¢äŽss Ëdjg%†F²œ ñ|†ài@´…˜d…#É„^„_=ú¥ý­¡îkç>t®ûðçö&Þ„ £7Î/|èáÄ×!¸fïpÑ™s‰÷ijukN.­_·¨K >~lùÛZÑ©„©¨guóÎÃ' ãËÜ@}Ê2R£ÔP ¾·C@së\³C•°RÍ®ëöýˆÑ#¦¹YLÙòªy&ÌŠÏj¥ºØ,¼ˆ|EBZF!AêZ?„ÔR¨Ì"ä‰4Ñ}ø6R[¡e„Ô`µ±A8g5 ]úÛOŸ_·ù‹PnòçãZ»Í”WÒ0°.¾ûK'æÏ¿í“›ã—Ù`‰itã®­SÐüè£Ð|qëºEã“ßïw ßó½[}ïî!K~M *“âq"“9ÀC1o—ÓaÓk³cÞÆÌ;ç*˜·å?‰yó"2$¸d°;ÄäI<¾ñ³‰éÉ™_¡ÜI(|vã›ýG?¿;ñ\¼ûóGûÑ7I¼ûùMâÙM_H¼ûÈÑïÜ5té‘¡;¿dz"k2‚sÙëJleååä2‘ä&¶H€ l;LÆ ÿEœ“ZWV\J Å¨°JM>Ôö2E)ÿB3 QtæûèåéSâÙÉDᙄ{’,¤®INÆ­OuEwK[‘äš;[Qæ±±É.[Š…uÓÀú4„ Èš3^?,¿üÐÌ+¯Ò77NΜ¡/®èmœèmøyÜI±ôH8Ïç°åuPët*.‚¬ŒM¢iDxÐh”>Íåà=*.v…)@ö‰‚´|²” $Lõ¸û?ÅËd8Ì'XÕgæ´ma u„lf¬8u¦ØJ_@J[޶¬]2Tô¯7ütÃGn;3þ7‰éÏoÞô%hyzñ=·l©l´ù¬OûøM£7½vb`þ_>et°ÍeÝ1óÅ-ŦáCóÔOUwÙ˜Ïh.‰x‡ïýî­Çxß‚¤ÜˆMLÎÇÍ\ RÐÚܤ_E"ä.XÍ/½éã´Íü/oÀ,$Ï@E<+)r¶œÙE… 1* 7_fX)9 jî Û¨lÕ’ÿ­›^ž~õU,¼Šî˜ÙCšL£ƒ‚ïï#ëÃ`D­oÊëÊ&‘,^Tkè2ŠåJ_‚:@?B8ùçká}/&u°íƒ?¡¯’/nðBÜà&ÞÇœ£XJ/Ý'-d-òB˜^¤o"¬”†©ÒÅ%ÏNì¼3 bˆë( VÇ©dªø2yJª¸od*f-(Ä¿° ÔV‹¾Úºûéík7#²m’do‹umê‹LÁ?îýâ‘^^³ß&ÙKY†_dk¿€ØBÖ^¾7„òƒ?õ(õ§ªÃ ŒX…JdíÉLGý³Q!ÖV–†ú—Ét"”µ.œ•~2Ø_™Š*­0/ËPù’™ãá°,‰ÆÊ©4d@6¬Ä¬P Ì+Š:ῸJóŸ˜ùW)"ùËܨåi‡×aÕ%lF‡'”;65…#7ò{¤™¹ó úBß̸Æ`Õ£æ[Ž83!žMê!þgòolˆëd T»g ”fd(£ÀÁpf>$÷øÙSžœ+£ôCq©j•káÚ6ý½‹=&žUÞAs˜ü3 ¾ÏÉäù]•«¹³†{ÅÔŨ†ÒZo²¸œ2ؘ¾È/£B aáäô+;»*B*À€ZΩ÷ppìŸ9¬vÈÎŽžÕ5ý+&Ï%òFQï4KƒË nò{ï|¡„ä‰ú£ž„ãgïôúMðH“[ïó×zñõnÃû›„<©Ú3}(ÅG¼1éÒOų÷ôifG]àžxŽKr:rˆ-M©“[ˆØv1qW Ž=¦’‡eùFÊùéBôüRIègˆzaæ}Kdß+cÌeȱ¨ì]n%$JM=º ÐîõXq;FÏ®s-Ó¯"ôìñ¬Éyé+Þ7~çýMV‡Ðè©vNc*¬øàÏâÿÿû4Bª×\—õ?ˆ?&qá¥ÉÉÄôg7nü,&'¡(G‡{vþ¶þþÛ>¿{!þõâÖ­¡é‘G éÕmÛ^Müõ‘£ß»{Á‚»¿wôèwïž?ÿîm’-0]yÜ6 ùfÓ£+,B°ñ 8-öSˆ¡@‰8½Ó¿WŸJƒ²ˆ¡Ùˆ|ò»a5Ó ×ʉU9„G¦àSÇ¿z´³÷ö¯þÅ/F.©xæ¹_ Ü™_‰g;ö<~í†'÷v?X1¼£ë£ÀÎ5ñ÷ÚÏàÕàR<'ZyÜ“N#&û΋Œ"‚”k:e€Ï”h–)¥Rg'¤;bŒzõjnV…K£F€¾á¼ w%2H·5¬’ rÖÊý ²dÞ ¯*c¥ &záĹÂ&÷tóþî« ÛµŸ|èPUb·ÓívçìB§ÈÜωÌå‚0ø7š¥„C>ùævØÄ´,Ŭ ˆAFQ˘yï#,óâgËÄL*eRžÕ§2`¬l fy?¦Ÿ9©SæF`4¹*›vü29Öº¨“%.fHôýÇß8Ü^½üÈh¡>ú|½'q‡­¤äÞ¯/>¼´â™§~€–Ì<+žm¼îÞåCG78¤’™7KÈ•N•^×qèVÙF·úû?9ŸCQâÖæBø½´?Œ£Äs PÀˆJ†ÇX]I¨V/ˆ¶ìU÷š+³Št#`–j{ËÕyf¯²S1UåR£ÜNcwx¥]£BŒwèV[´gmOç†î‚Öµûï_Ûßa×ÉŸ4éÜf©ad÷h϶Á’Ö5dhMkëÄ[÷¾1 |Æb)(-·Tµ 7•V4.Ú7¾ê‰}½×.Ya´D¢E=kZ;G›ŠK–^=tzG×P?€À€p€È¯<Äzþ´@I,T(Q‰ÃôšË*2âò±;rlfªg$”Ö“qÆ.O$"¬8Ê’„¬\¡26î•Oá@¢æb¢J<ûØ%ƒðöcL^ΑûYƒ‡®§Ãœ£Õôð5`%†€K„²6`]Ê D•U¨ƒ,"s*úAW³n6œŠ»÷ˆ°Û”jûg¡¿cmíÔð½?8zô÷ OÕ®=õ`Û¶–áMÓgoþÁÇÇÆ>þƒ›É¿\öÀ¶9²~èûXÀqéõ3£”TÇ"6p1‡¯“!@3 _¥-c¨œ¸8‰<ƒ²eV“—r¥Ðè rš»Á°îãE…÷›I9Y2Ã{ –ú73Ÿ~I<ëuN› –—/,Àµå½¿ @`@ûKòþ%àÞxNI1ÍAlæDÙvé0M‹ l™ÃÔÑa V×"e¦ë ÑçJ_‘€ê(ŽSæ k£_])+… 6†aó#Þ ,Ñ™4Z³Žhž&oH ‡ífAKŰçc‰Î÷“È\/&î}‘í…ß×^PÐáÅ?³åÑouÝ¡6òÅî'QkO°Ô±\úò o“=òGãŸÇf11Ty6$UùþðA±ò³ñaYX9*Ó%„C–âfdmYãV=d{¦ä`zæša²cäR¢xJrêà Z3\«—,ŸIDào¿ìò致ÆÄ ½[zý;úŒÙ4sQò ³mfáŒÛmFã’cæÛf·,ïÂÏÞ~0®sÚ5‚X݉ °r3f6¼ðA¦Ä íp\6H¥ÄÍžR)QÒqÄFîa¦| MÍüõYoÀû>£Ñë„}ú çyÔ2ójÁ«½Ž™ï‡ÛÁöµæÑ³]€@±“|3˜|’ôšÝ”›Á¥˜\Ê”Y÷ƒ.Œ€ç•|,{é—÷õh!É0I1ÓzŸ4ýC»ô®˜&úÙ_¸tjr(àn˜dܧÿâ 0¦ä{tZ‘ùŠxÚšÇ/#«‹@8Yn¶…ä½ZÚÍzNø\Ï<ãò%þMèuyÞû¦ÇÝ XoøïHoø“½áZÞÎÜûó¬7\Qg(÷†cz:LC¡jÌk2‰Sùï„)ŽIÖð5ºZ-'ÛnЊ’«å¢T€A û=€AòõÝu ­0‰ †–É/'wý“Ó¯©õ9{ßý aœÄü/ä|m`y\g³iÊ—@”E7Âò[•ff„ r.ÛeˆØB•ë•CàJX¬Ñ²švÇm¹º×L­Ù}Qçv’’ÀÌ>§®q—Kå‰sf'=þÄO±óƒåƒrº}t}L£âž´ë=©8m{"r‚Ó¿{bï^Z6N $FÓÓ}„@#=Ô««4±3B„ׯɈ`Ú)9b® üößå›ïß(yÍ¿Ÿ‘Ü1Y ’sî´ 4ÃXÜU^VS]Ö\Þ ü·ÓaÔ#À:Æ2`,à*i´¬ÃaÎZ ÒÊ ¬’[(÷Co«Wç|è ác³\ިܚm`An\åÓ`Q§P[”û³q íåÌ:‹ŽŽÐkl|&úÒñRg4cõršò ì±vJ0FŸ›1C°÷ {óEgÉТÕ-­›†¢]÷þáùk×wŽwV¹í>]þ¢‹7,>º¬2«ZÐÓØÿü¶oE›Õ»ª°¹Ø꺶§mç’Faø†›B¥!‡¹ydaÓ†Ó‹g~îÇò¿/csËJZ"69‡=óÁ_ÑkˆÀÕqW®€`ž;’!O$ZDP—ÌfË}^Y¸\Û4µPM´ô‡h†‚‹åØ…×ÿ‹„Ô¼¶j <£ò<"ÊÙ á @g`L–t&O¶]Y™D€(m„2&o†aÖ¥˜#‘Y' Tœ²ú·Ä$(ªÔwkmÊY+©[@ÒÏkc ´áÌ…ŽÇ6ÞÐÐvýc×}´V¯+‚#K4V¤ÿô™p×qjæÑyã]G¦LLš»¢߸p^ÂÕÕðµÏ­?½¼L¾k÷Á_ðAAeÅÍ%E®ËaSó5z’µeŠ^ˆÒ(1âùU.~ ^Ptó?Ï* <Õ»"+OÛ2o´·|(–ã…U^$¨I2Ïõ8AûB2ºHztV‹%Ü0¿i`×üⲡís;5ÚÝÆX×;V?¶·§ã¦ó»·<Þ;9ž€§~Ãé±w®­%-ԶΑÁÞÛ.Þ|ý—OU”uªŸìÿëŠNµR‚ŠNINVYwAÓ)ª •("ŸcADø1¥ª“ ?³@Ç~b³ri !NU‰Wã«Ó8$Ê6ÕÄø,é|ª¦Teç#Ä2Ñ(Y)‰ÕRZfå Ðœ†%ªìYV\)TP•qND"¤´`«$O5ÌN./2X;ŸÚ¶”©Ì£›V­;Ÿ˜C¦æ#}§Ït­‰ì^Ý}ëE¢.§G:šQìýŽ[΃¿éª'úrŠé búò¦ ‚B…ÖxnQ‚Ò’H´(ê÷:Y779€B}Rwê\±VHÅvøC²æD1y¿:»òü§x3µ'–ÎK( “†5˜ðy2Õ§õjLW×¹Ÿ: `UwT7æ&Æ+DÌ /p¢ƒ4ʯBÕ‹öôw¯í­²¹rb¡›'7ÿíáþÎ}/ï:|Ÿt­Ä9»ŸÙVæ_szuU°(PIk‘£ï£_;²åÓ·Ï?rÿÑöMƒ¥r^íÝ'¾0Âú €ÔV|^ÉNñ†äŨ‹±Ð¼Š ¤áè‚XEsÿ ;†RÎBǰRçVT°˜3š“3¸=…•’[D¬åädDë9SÆ•< _°cH`3çQWËäÎÞò UýK1»w,k‹›ýN 1{y­>;!ùÎÕ,?´pn‹ó󂕃 y0’ø1^i%ç¼uKo_YíwêrÆ:Wž>0ˆ%îÅÿ*h@3˜VÂ5qÿð–.^°rxå@¼¢ÝV³€A3l6&oX÷è V³!¤í,C"ÆÔ„µJ}BÔ`QÎZÍô®¨Èë™Èó6—ÀB¸yd²þÌÉXò u±¬“Î>=÷žÿÜ| ÐI"©ëhp8|¥y(‰N t„û S  Õ·£!¯6‘lÌ]’\X6CŽN¦^qP>`]¥@i¯eÔW1Œð—]»« £lwAóµ'Gû:òC-köÜ·¦…¨âÎOíh\=Z^k Uç.nÞx ÞUÖ¿êºm5Í}áþíý»†Ë¯5Œ6ù¯ß¶õú¼¦kMž¿+¤·v.¸iI¥ÍYé*ŒX½+6ÖÛ¹ousÅÀÚúÁÍAgw“«ª²ÌZvråÀM‹+/½ZTéÉ5:ß‚†‚Ör·º -4 FWîØ±2:Ø`:~$7›x‹õßH@²[M9Z1¥BrÒjŽŒÕpPPÚõ‡`’²A )™Z?ÅæË$cÉP/¥X®þaHGÃió"BNù€rG®’ ” ¨^iò óû¼sHÖ÷ËcB‚e°ãÓÚî9ß9¹iôàâŠó›· ´…}Ï÷Œ6^{zÉÌ~tÛîƒC3.ú÷ <‰np‘"N»A'gjÊÃ¥Ì Õ;^X[N`Ê$`›êË àê†æ Jž ")ĈBa†Þ°b]’(,¤'³Â8s¿øÇÎÿ£&×ѰÓŽ’ì°„ßš6â·`»ú¬Š~V±Øëƒ¯$îc{â%R‰¢w!È7§È”F­Ëé“=%jï“Ù͵¿Ä%G[ó²Q›²S+7»j¶Xj=2Ùdy=Å'¦ÕIºE±l<,ñ¨Î$äW±( µ(i?ÿ¡Q·4µ´á¢IF–-ÎïÙ>jðéÙ¢³›teÙñK_ž³yAÔ`XcÒÂy#B<ËîCpü.Ñå´¥øk•JÌ+oasF§ už¦ s LÓçÈ•Hyg ßÌ…ø²ÆJ—lC4‰êuù™ž<…t8|9#Hkˆ¡.:vùß’/–½#†ŸD–ž˜"Z¯“AÌÁJ§× ã–€çÑÄI³ÏfÏ3Á;ι‚æÄK » NM?d´Á¥Vgb·ÍkÈ :96;|X2%üâ”/= #_0ØÉq$ŽÑà”ѸÅÒ¾-e'-)?7Ã@½Y€(®ºDUÅ©÷;Ä©¤]ÿÈrP_Ü(eän ©Òþ.½C- •E*î’ì ˆhÌFL¬6X²’m’ÐaJ 5„NN¬ˆ'&ã¨t(¢LlÁ¼hËgÒx%&€e[§ ZÊ j1 =gµìÒ:SE  ÒÜ(h‘2‰–Nµô3¤ãÖ?„L7Ýí8Õ ˜ÓüÁ–"ƒ©éäŠMýRÏ’µ±…·,­<ÝúŠá¶ðùu«º®¯ÂoE®ëÛµ¶qa]níº;WP?qãþ`Ûò6ú¯}{ûæÌ˜÷·‚Üà¯Ìߺ›ä8fz"šä‰ä¹DÙíaœç̪¯U›î’Dtp§Ì ³9Ï´Ö4…鼚·u‰ÜÛ25„ÂÛòýd뱭Üå’ Ý1oY†ËUr{Á,ˆÀÂ๸]rB@,\X õÀª‡ñ+›'Р‘Ùý̈³ÐH¼¬à jÒ(¨s-LT®ˆ™7†³Ü•nGŠ7©×`+v^¸¥»û– ;w^80wî ;¸ï¾†&#‘Á A8>uãÞ/Ÿ7ïø—÷Þ8u|`zïW_ùÄ×êÖݹlÙéµul_îI<#X-Ã<>’#2ô s±Òç±(H‰(@s.eư3ÜΊ™_õ~X.‰sɾ;–Ê(q™ÉÉsõTÎY™”ÀÌ[¦«RyÓðH dF$’GŠ$ùKãPÌP(‰EÄí‘2G\¶U|Ñ–Ž.R9·t>µ¡%m\u[-´1°¤ÿEψC‘½Ãkº\døb{s".|+_¤:x¿®œõ ³cÆøG|¸•µ·Éû¡¹$Î%Ÿ5áÂI.z0\2²sò³NåœGéÂÁfÀÏšó¦â\¥ÑDÒ5ŠŒe¼§Èºö2 2 @yÞ|Á›9ÜthŒ\wƒéÐØëçãmNcë>Úc(J 6[‰ÇÏœ‹·[ìÛâòvnûˆŽ¼Žßnðn2þ´[iÏ·ˆyüiÖ"–î ˆÁ¼m&5þÌ "ƒ”’__âÍ›)„ŠzXRH%ÌAM!1ÿKXxs5¿”¤\Ž Ä 0ÿ Krl<éI¹‹&ƒ´«œW8l°bËÖ'ZγXôñ-¤ý¸¿5³o÷.ôÛicdû¼ñÆ §— clíàßã·®Ösfþ?í9³ß#ù5š­ZÝ Ïy2±þb*-3{ ûÃáþBt‡Uš6ÊýTâñ; |"žSYQÌKï9+v§ôœ™3{ÎøpfÏ»ß.e.O.Ô“d?{‡ J ¦4:& eŒ;—7¥ñÕ;èê5ÉøJ•µ‚˜¡kÊ¡ù—_¼¨3‹:§ñG¢Ç1ßé¿k´éD«þ Ñï8–øô)¯áßuQ4è~j Sö¬»  »Ýeq:-3ṡPPÞÁôÍ}|GöÿÌ,‰É“ÈŠ%³e¡Ù²cJœÄ˜8iÓ&q,…Ó6åæš2÷ßK¹)·‡…cfæ»Ï5í1󵇽ÆòßÎj½’%ÇN~|w«}šùŽ3š7óÞ÷û^W£ËvÍŒU DãF™ïˆã¡ =Ÿ'>Íe7ñˆUEn„Z ýºpŒZ@)°P”ZTjÉqjE"õü\œ#Ïà«Ó/¼à´kþ­µžÔÙÍO¦_À[_£ÔfÓu.û£DÀ6ÓGËËqÊdM—ÏüÅe·¹]ég¤5“¸Fý«ØNt$¥ax–HϬí&.CžK§ úÙ¤|'ÀK°Ò 6¥ž*@Í Æ‚ñi¡>ž÷¤|T[V~ôÃéóp•òج5r±(ðnëíéïÛ‹ñÝÌG\¦™ëœu.W‹\iqÐw^?BÌZñßd†,Èüx.;‹Dy£å Q+F#Ôühqjv^PK@„šŸYkÒϼNªg8£„f~BþÊŒv¥>\·Ífn€>œžšŠ¤F.¥ÿæøÖÆ T†€Ÿ Øi§6†eP.Äw=ÂŽÁ™uœÇ™#NüÀÑ}Ù¬Àoø7ƒ—•wB`}÷ÁÉ¿ºWà;+û¶§bÛ7­[Fo¶uà ٶ¶ùž‘:_È?1 ~¨÷‡ü )#þÎßc9ISæÊ<š2ð‹«x—ì^•¾ŒEMD…D |S©*L%e2¥ÜÇR®¡HÊÀoGý|I^‘”ù~Q…%UÔ4õ5Mú9â®_;¹1Û8¹¶~æìOÀ ¥dÏró¬ƒ%ì¯QJâã"c]u• òD€¦Šñôûôáhy‘…%($ [ƒϱò›”HÜ‘‰Ö K!|D¢ý“ZyÅNÅ ôg f›¶é¿ò/ƒI´g)`ñˆÇ¨RFU˜ñW- „ð0¨3§È&o*«—¹uû¸·«çª–Ô6,cG ³Éà¯ïª‰u­m Zô6.Üþa*Æñ›c—ÅVí_Òë†~`ß%½y”yÉîþ¡«©S¯· 3<ë­(× ]ƒ+…÷¹É¯N|ÊEþÍ·l½Ñå#bÏåžE¸1eïìHÄ$®)?DØèµ,QØ!‚IÖ'JN«D­äpßñ¼€e…,ÅONæ˜sü³ ”êfAqÁRK¨.p–¯ÀÀ±Á!Bi y¶0”F^iÂÌœaŽ£?ª" Eoà`$–žy?°ÜZ1Ÿû†ÄÅO^¼æ²óûŠu©Do¯ŽöÔwmNùð‡=Îô‘–ˆ»ÅCžÔÛËm§Ú+:ÃîÏ’ÇJ—aîü§Ž „zÏ}{No*i 8jÖ\Ü›~kÐæùÛÐEõœ°ÊTæ2í×UDû0̃GÑOY–ÝŒ(‰îKé@­¼T§QV&ÍR>?ƒü3䊔< /FŽ rŸ×Åà2ßB”Ì#ÇH¼ª§¯J—˜¹Ü:Êþ· ÷a2”tѤK¢×á—¢½)„’ 8™Ã#õ¿ûø¹÷×ílmÙ^{|ÃýÞ ¿ûç¾³ngK˶:ñÞSq4Ð9ÚØ8Þ±lYÇxcãhg€<_óN±¨ç¸XtT½ã;=bUzß0 ¥GÇ:Î1X!ÄŽCþ?¶¥Š¥üw9V¹ ÚfTŸéÇZ´Tb0Òa)ðˆµ˜Ó`^ͪ œqOƒâüBeÅZ¨¢ô(NpR]†b<§ÏÃq©S¥,®0ðWêpÎJjƒ¨‹tb]Yá>qZ i‘†×JECL'}ÿ¼¥3¬>м@Ü7ޤwÞWêÕa»É.6.ÒøKþ_zަ߅'Òïz‰^È^¦ªÄ>ó•@«ÛÝVAÖòS?ܾkGúó¸mÇ.DTk6MdñöÑ÷xñœäÁÜ:ÛÂе³ê+ÖÐ¥M´È¬æ¬tEkºS¿NÿìÔ¯èûußìõÌ1î4ˆÎA÷§Lý½õµUA¿7kç09ˆ1·~MG1Ë ¥ÎÙÑcéÖ]¥*ãR¢Rêέ¹ä½À|[@°ZhCPÅÅêtIP8Nƒ•˜&y“0¼:^Þ4¼}×öá¦5M‘-wl7 «5.«­ªumWd8QÛyñαȺó"]òfáÌû/:Ú™{býÃU= ö Ø<,©iö[k’;F¯VNõÍm!ÊKê;ƒõ=Ñšºhÿy©5W‚»†Z/ w&gžŒ´Ä½¥CËš‚€Ý³‚þ&ö²Ž;"åë_’“¯ï Á”ÑRÊbDJ†e)ƒå•6ƒ³5òhž>CWáŠ0^özœ®är£3¸œû¿Ø|Õ.g•×fóV9]Õ>Þdu®jÍæ«×Æi]UpWårÕ@¹WÎ=B ê}“{‹{5 •h Ÿ“rn:o|´oy2|Ï奎"x+ë2k»f‰EÇ…æ³q!¬¡ÃHQ²BŸü’µQËMÿEpŽ9Q̾Âp<Ò^ ù€U` Œ¹æŒq½ (xpñkéFrÎ…ùQéxŽ¢š2ñ}ÄÊ åX^£çõ¾£åéSïïê»ü™© ßÙÒÀY &odU›HK]+Òäv†{•§Æb›n8ñ‘mÛ>zâ†M1øüÑmÛ>Ÿ§ëÆöÝzbýÑß¼°µ~lÿ­O¬?úëç§ð79çS=+z/vzÛs—-ïMvóñѪi.W_6Vk°cí¶>ñŽÍ±Øæw<ñÑmÓ=A?‹lxò‘@{úÝ¿»aÃÓwoØöÞ?ÈüL™àžGÕ(Žþ9Cbt`¼6>/g(As†®ÞHÀ™'ëG#åÓðrtTf†c”°«T~%g'7S'VåCYg›ö¿ý¢²~4éÇI&Úm6¡¸!Ò\5É‚ëﺠµ%©t[LvλlEb¢Õ“þYtå²þ-í¥1fPokrø\â²®½ºby<@*»Úm⬮¦©¡¢}"šþA0°‹XËÄ\…äÇ¡kÌœ@vT´)ÞUMìDä¼Q'†¤=LAÍk¼ù­V“I÷¨Éd6>Êqå­9÷„wáw” N£Õ’~»Øhr¦ßÖØ4åÆrüŽ_Pÿ^ÜÆü•üqh,‹Ãf·D“VdN9$«•Ãj#^àkº†€¥Â½ÌÆûgÚÈ/Òë±½~«ÌŸe(ŠV uøöTi,š±±ÁÞž–d"nXV;úÌ¿¥³ )ÎI ÍFÓj8iVÑè°Àj„,~‰Ü;;¯mÓÙ¡8æ²ãÚsQ¢çÐÁüxj A~ßõ-#ßú\#>wñ+ðà÷Z¸ ¿š6³=šÉ”“¨3œtã*’6ëæŒR‹{—6þú!ì{×Óì¶–èy¯ßòemIéÈäêFGQmì(ùÚæÎŸ¦a®¸òòó–è<öô‹%¸ÁhÚç¨j VŠIÒÀ´³ß$c\ï’Æ®tÎ|†ë=H?"ßáž—Ö7ãyøˆ\yÜ1«²Ô]ÐÛ¢\ÎT®gÕY¸¯ïðúHæzµ'±¢¦f0áñ$kjV$<ìÎå‡&#‘ÉCË{NF£“»E«× ¥V&½ÞäJéAŸF#lû"Ò#3º¢ aO<›•R›Q;kP‹œiह²ë¨ë¨ëMÌÙLY‹¬Èçꟿ¢Ò@K_À|âTûbúÆÓwâ=7âKOUƒlŒ9JFùm¨]J )i#Õãz^ñÿj.RQ•9ª=™#u¨˜Yr¸2*G]¸“¡éŠtÆçÄÖ¶ûANä„«ÑñY‡+u†•ÔùŠÚëZLš"+«åÌ7óÛ<‰ÕU}­ž§xÓ¾ºÖXnx­$—E“á“cÃîÝÒÀ`® cb?t AÓ( 7ÔTWI¬´êÀÒ/:BD-öIXξ!ŒåS_"R0Î^ˆHé¹\¢l"œÓ‰ žŠŽµù÷ôÕôn™è¨5'Í&·ž·yj}ÞFŸÅênœh ‡[ ¼®å– uÝü6w´·êšú¢5ɉwIÓ`c1ï2ŠL´vo•ÃS[nšÄí±H¬±ÊbÆØŽSuóϱ·µ™ºî¶mÍ£+ðÇñ3ä·¨U*š‹uó¼Ç®pU'<Þæªââªf¯'Qí"£ê;ñ[ºž=š~ÿé}øDN8±-äÆn%+Ço1DÊè¸!‘*ÌnDÞVbeJ8_vAËS¨e™rX.Çf”²½[¡bÒ\PŠÃ%ç{@Ž %£êmŸÄ»x‹Ó稩uV·k÷Ÿ3êk W¯ôZù ZÁ_ZTj3p·Øû¾è²"«)I×/—2GI/¿ ™Ñ ™Á^.—Ñ+ñkÂd‹ª³9çk¡à¢%ÃPziš‡tß.)ððœ%Åø@‡ÿÝ÷°L.mèÇ^—ÞżŠ?T»^kjþÄÌåk¥ØX¦¼_âzô¢ÍJ|¸×áUâÃå´+ç,²X «¬E‡u/ÔÍ‹Þ$¾ªïH]ÝÈÞþ}£uu£û&ÆÇ'&ÇǹçjGö€ÖUíèÁý#µ33»¦§wÞ%k˜ˆ~¢‘9l¨üÄJ‡}mRiƒ5‚!FüÔ…‹DâV&×øÇÖrÝ£F“Áü¨®¼õ-\õ–úþZÑå³Z1WlЗbÎjoÓG\.WúÈü/‹¶#Ä]Á=á(ˆÂø•Ty¸¡¶:XIwÉá,xN%ϘéÙT©½ˆá…ei øœÀsX¸i§E$¨S ݰ (jpzx§Ì\v¨q °NˆÁît¬” ÄLAÖˆ ý@«•”ôN Wíø~¥g pH0ˆhÞ°H’ R›Óp,§©A°~Ô“H)ÐH'Bëz‰õy$ ²4¸¬X™óDqâ~1åYÎ|‘É8vQú6ÑáuW¼}kÖ#Ç „yã3–¢:÷*&bkð­Æún½ù}ï³Zð÷ÚTÏãg‹ê\éÿÀëä6º}ˆ‘ÆÓyâxªGhÿ:åÁh ¯'ÕÑÞÖÚÒ66¨WÝÊxJ6…^Ȭ¦eLÉ«j:¶©W s l=ZtÕ “†–â|æBêÕ#c!Tžðè(=c<Å Pp1ò€[+!ëç‘ õ¼þô褺®,5¥ÆÕåÁÕpÆŸ  ªŽDPþVty\~KófrÇe‘2"Ùí‹ósN=”5Rÿ07DÉÏãEUƒwæÒV×')£ô¬%Wmv–jZñÿ$A=% â P>ûÉ,ûëŠý5|HeÿŽbÿQ–ýMÅþªÚ.Ô+ö×ñ~‰Cà·ñ'%>×bìIÙ!ÃhPk|(ÑJqƈ¯jƒèÄÂjxø¤%è3òx‚D*£,0£reew1Œªª™SöÚW•㤠@€+j,—« ,Ç \ät0‚ƒh.R‚“`{³HþoFÉ .ù33SÔNg~ÙA¶œ:Õ4ó½—ô•¥÷â“øÛçÜ&Eˆߘ~Ê^Bìäê>/¢Ú6éÇ%Mª!rEªÖhí͉¦ÆP¥ÏSââY4„‡”“é¡2̰­˜ã;MF +°±pz˳Œ’ú’#Q•9óõb-AHwE#'Ï Ò–Òñ_ÿWÂ46σÔb$`áH!l„±‚¬¼Ç‡—„Œ‘0*ó6¡>¥_½$Xå˜]i@ §õ“gˆÇ!q‚„¬ÑÀºQé¸Ð$:iª7>ÈitÄhn”«÷GÈõª ’e·üüÑu§U³ˆjdã¿;aÉ¿—rÈpð“_H{Ì8¾eŸÌÞr™¥:WÒ\Z%ϱɼö“Yö×ûk¸GeS±¿Jí³_CL©“¢^øoÚî‘›Uö“Ô>ûÑ~ðeûk µ¿)Ú/|Ùþ*¡{‰ñmIß/f¢Ú<4HNTÉ7`Ü)zWAC޵€”#'!0>³õ’a·ÒÑe׃²¹zùõ…ʈ(uY<'>W$0j‡Ð‚õÎk*򾆥²z›ƒ*¸E>O\’^‚$Ø1ó»Ï³?á„ô øÊôõx-'0r›$á&ɹ‘" å–¶ìK3¢š›2&¸fé}Ù$¿/O‚ê•H¿YD+¿Èk?™e]±¿†ÿ¬²~„âgÙßT쯂atzeØõHú¶ZëkµDÐâ'µ˜y+ý,žÚ”~$ýà&<~fÞ€×3ÿL?›~t3žJ?´Oã ›ÓOãõÐÇb÷ˆš+'‘Õ£flK™âÑš*O¹ËaÔó¬’i˜,Á,‰4+½Çš4q—ŽSNÅì) Águ†hþê abc¡ê0úâ W'ˆå¤)K©LsES‹®GwÞ•úpQxý`hIÁ¯RŽRpnzKJäNg—Ôo¤ãòX0„*˜ ?6<ílu¯ùäž#’xÇþ/Ù}¾éwíØóv÷P¹§ýÞIñ`þÝÝ>³÷ÀC*ݘ¦ðîRÌNaßK’Gkâ’2צҿáØwW0××Ô‚ ÇU_2ûWöȹ8ˆÞ€|›å=m•Xw¡A<(dž›š2Œ•¸Ëfäü­™\~+ú3›$ö̬ÁqZ-W;´.»g ’Ÿ 3ÔÉR mÎiD¼a8œUáros¨.©kÄæme3‚Ò AO'!︪±65”ªœ7o™>6¼å9”59¯U¡›'¦&šë—¯Y^_Ò¼¡«÷’áÚ—ú®y™{OûyGÛ×õDÂQ_(Vߨ¿}ÍšË×ÖÏ%¿§*›û£5­ã=ñá®Dû@Ø×ó×L\5q*Èütçó—÷Ðy\âææž#AÔ+ßåèGy퇲ìÇûç³ì7)ö/fÙ(öÝYö+ö/ ‹Wì¼™…¿cmdN¤‚ v½~rlãøÆÕC}½]b`SC=(VÊüªËñrKfE?ØÝF4‚×E$Î_À >’!05b^‡eÚR³‚DÌh"@ [ï,¦d6JúXþ_…ç xp=q¦xH›\sØÊÒ{Ë¢±±Á †^lCùÁÚ°PC"¸€ $Óþ•½—ÞíO¼;ý೟mÙº¢¬cs_µ€OyÛ'Y.uù{÷T»Ï=vN{™[«éâà½:×7—úw¶ØØÆF]¨ Hœ÷ÛÏ]¿cæg\ y°frdÙÊ‹Wâ¿€ôX÷%#°’_™~\âHÄ; Ψ·'i¬§o:ž…wV¨¥¾{f ˆÙ·ŸÉõc!ï.Eq¨\x»k`Ôa«EP(mWÍCÃ…Ð4­ÎBE+˜J8ýŠ3ÀÄH¤J8 ^õøÒ±Ô¾º À€WþJ¸øµùÜi¦p$<˜çûÒøN•/]9~ÛöV™“½1/u»ÞÊGÚžßbÍïG_¦³÷RÎöT.¥»VÓ=¸RÓªr¡gg)G¸ô.”ß±G(ýP–ý¸bÿ|–ýˆbßMí³o!„op>#ù½Ë¿‹¤ò÷CpˆÊ~ˆÚg¿#Ú€/Û?ÿjÿ™h¯|Ù¾Ê#‚VÍþå¸P B®r"žêŒ&ê–UpB­¬rß–øbÑ’"›™p¹:ó1G*7¶paº=W .rxa†@¢<Ø•„l73¦Iûê4 ±PÛ+sÚÎøq–sí¨ßÖb+áì›_}& qnißÚê<òÁK§ž¿r°óÀS;§ßßm?xþ‡6 ìm`,±¡­C{W…Fv±Õ‚‘wší&®Ìe6uZÚ×·õËò¦+üðÅwüò±u=G^Üsþ3W­\7yã'RÇNÝš¸è©KjG»*#“‡{G¯^Wý,óÃZi”®•0;å”~¯1yœ¼×~(Ë~\±>Ëøc?Ë~D±ï;bÐ4ú8[Á^#q×¢nªûÌ«²&uªVJ.=¸¨Ã'Ç ç Xsî§!–OùÕYåþ¢Ü{ò‹cøéœmÿ™ñüR³/³õì äGµ(ŽkS¶úººx}rá BY+(¹æíå˜%±h%än°z °sØÏù=µ÷MãèÏÔƒüÖÓc(¯ß,>í]ReuâÌ+Ûðt¦TÜy9ÿNñæ±Ý…áí-“„‡°“¹³£øW]ŸH:ÛÜËïêéÚÇácͽ¢Ω/·á—W\²êÀpNí®Š¥‡û™½Ÿ<<¾•î’Ïõ}æ ƒ Î-sͼÿ±-dMsbú®ô²øôçÜ¿yæƒa¯ß“4î×ËÏÃ?P>û¡,ûqÅþù,ûž›Ú)Ï‹„³QÆùå%÷idG|UÊét ä.¥è¢ÅžÃQ))fŽ-îÅÂ:5jNJÞæùj.ž]R]sÁJŠâ+­æ°K¬©¨x„ ÕTØ%çiÞ´dU¡Ê’K&¦´.LKùbד»2œ”›ŽærR Å3¯T4oúºÒÑ\NJeQÆÁnôË9Nd1™A q"›r8‘ÍDMr¾0ý±šý˜ò’KZYojBT˜DR¥2°xWêÀsLjúÌnJ´ c¤Ç !™;LJ5 ,:@â£SJp…‘ÊÇt-TAE¸ˆñH¹00ÕÑó©¥Ã@7ˆ(p“É£sp’†Ei>H¤Ñ(ˆhñ€4itI€H…‡´Úl8ø•—«àx8í̈6 ø/ ªÚP ÌYP°\^¹x- )LL< *Xp¡4Š´ªŸþ7.-Ž1ìÍR—+Îäè)kB3#¤~æ»ä}§žœH?~ûïŸÝ¸ñÙßßþxz•:üÜÎÏ]ÚÝ})\§”#׿^zßÎè(»ì¯3ѣ߽wxøÞï=úÝ{Ö¬¹ç»ˆuPòiRhˆL§–õtGš0èïêjmnJERu5>E!'ŒÃ&%Ï­ë4§äÀ*R7:„?_K¨ž¯—v+ ÉÚ·Y.qv(Ž9Å¡…Q°V«A Bj3#'¤¤AðóJu5:ã`®(%FîXR.èCŒïÔOÉ×Éõ¹o,µ>ùÏ7äú­Ìßqˆ{ Ù-°¾¶¨M—öÁ»ö¹Ë6Œ$5œÎâ)6¹„2æïá-ásžê«à ¼Ig¯kJx|½A½¡X'Å1?ÁýÜ/).âE\LI{BŸ”¢w¥Ülv—nVs¿ oŸ›.ª—õÅzÀMÿ÷£7T¸•‹Å}£ ,"ÐdRê‡&È™ð–ðˆì ×"»è³‹ï³º‚I /É!èKú·•xO ö碻?¼øŽ¯+ÜuˆÀoB¡7rþ¾Êÿò¿ï3ùóFßB?e>È| ¡v%olè}.‰ÏQQ2AÂdÁl²|Óä·Ú¦­Z}ã¶¶¶m7®^ulºŒgîV‹wâ·g~8`¼;.À×pób©Â߃ù=€i#ðlÍOäÁ¿ÊJÝ!îüY:  Ý‹àŽ#-êIñ 3J2o;ƒr®¸W`S´¿%½è¹ Sè¥.¬Šð˱¹U̾ÁîãžAø¶”g ?%Ò`¶$#áÚj`“++q:Ä [,3L™¿a•&Ž5Z›VÐZMÒ†2tnÌë± å…\’9FŸu:›NY®,Œ©Ú-„® :t>üò?;Dhd¸ÀŠmëbPámÏ!c6#éƒl!iWyýEí¢Â€~Uél0B¢ÒµÍIQ%ÖPI[‚ŠX‰·œ/“‡­@cÂh-T°’´|É3Âsë«Û¡jØó«¨[£ôtR%3­ô$œÿäiÊ@áJUý¹Ü­!SÔ’ †ÒÑaЃ±Õ ƒ,¢ï€ö;2qH&)̦ÔÓl(0Ú°5@5PT²§±í?>8ýb⢪ënÅštZ‰‰û÷ôÿ³«!ü}7$@öáÙïñ:ü9þŸH‹ÜYZúY²ùjÕüʇ}>ûwoq[Ó¯òÿ4Ú¿*^EDŒ‚q’Cü›Ïʺ,ž•ÿ¾¸EÐÅ} Há!  9€$?/Á9EB8«U$„sŠÄpüuÎÜ…œ¹œ{ žàuän±OôÐ'‰WÄ‘Ež G"˜(¹;}ÒZê¸ù»:«ÍiåÿùCƒæ«:ƒ†AÒšá"Þ¥üëÈ‚ºRˆâ¥>2u„…<ް×VH¶‚!žúÂÉ(¹ô˜¡\{†Õ°×iË Çþü>^'ð›X–'›xá;ô<ô÷uü†P/ùÄÑ,ŸxNïi|ÞCÞho0Ô÷xâ}¡`oÔ»!ãóòGB=¢dbO(˜‚œ™T°¶¥¥6ãó–ò²Žÿ*CïËÃu ½sîÿIvRmüB O9k:«©¶XqE%žvÕv…©Öuueí`Ü]ê-­]V»"æ? „þ?eL÷ç $пL_<õÐÊ“^pÊß.…ÿDþ“¹- xc`d``ßö/Œ“é¿ËgŽ@Tpˆ>xm’°1Eïû^mÛ¶mÛ¶mÛ¶mcPcPÛî ¶í¦7©ÿÿ;sænŒ•™ðëó†b3®Ëø˜«Æ"7iï§DY=Õ½“˜+êb4)/“¢)ÛZzkÑXlwYW<6oYW'mIG’Öæ/:;n¦÷C-öŸÌ&ÏeB$÷;¢¶®Šð:26ëú¨¯oc³O:²¼—凨,Zã¼ì‹‚º:ö+…ͬlc½_ Õaì·©ƒlK‰üj!ê­XÊ9ƒ÷ˆ¬}DVT7Q›ç˜,ž›§Ìú\±Œ pîÔò=çiŒ¹r5Ê3˪¸(/–#¾üŽìª.¦ `¸÷Õeý\þ¯öÛc:ë ûVerŒLé²% ‹ÇÈÈúþ ˆ®¿"²|‰„ Ì…(Íu3’û¤.×ïùûîù?žä$#‰ë#Ÿ£;÷™g+)6¢¡\ìúÌ´wïêZ#&•¶NÄ\T'5YwЮ­ó#½HŽ,d}_ñe9~¶žŠÖŽé(É»Ïhï=,ÅÍCëÂzøz0'9ç|æ òÚOì¿=„„ûlÓºøç‚ÎÔ~ì¶÷~S¦u÷Ì'Ìaž'·TuTüã!$¼fQçâ_èÂ9cZ—Á©\ï0Êò\ íItö[HÇ ùÆó“úqù Ó½³¢/Ɠټ‡’,'wïº'â«•ˆÏº‹æ%–›§èø?Ý¡ÑÉxÒ³–QGñÛéb£‰mÛz‰ ›õض&¶o•2orf¯ÝüÖå§ý!Ì áÙÚ0kyöþ–ìšæ…Eñ&®ˆëqe¼‹«|½ÚÏ·Ån÷õN_ïÕƒzXO„ýxRO‡xÎ×—ã¼[ñš^ׇúX_xÆWš¬éšãs5Oóµ@ µH‹½†R-Ór­ÐJ­Ö¯­Ù«jÑVmÓvíÐNíò »µG{µO‡uTÇt\'tR§õ½gÿàë/ÞÝWýásþ­üä¯&pq  Òé‚tAº ].HÜ«õ°ÒO*].H¤ ^ÓëúP+]ð•&kºÒs5Oóµ@ µHé‚¥Z¦åZ¡•Z­tAº`‹¶j›¶k‡v*]°[{´WûtXGuLÇuB'uZé‚tAºàW¥ þVºà_Màžü?Ü©{õ Ö«ñ?^ÓëúPk±¿)Õ2-× ­Ôjíò÷ÝÚ£½Ú§Ã:ªc:®:©ÓúÞ£}àõ²À¢E!‹B…, Y²(dQ¸WêaeQxRY²(dQÈ¢ðš^ׇúXY¾ÒdMW…¹š§ùZ …Z¤, KµL˵B+µZY²(lÑVmÓvíÐNeQØ­=Ú«}:¬£:¦ã:¡“:­, ?øšEáWeQø[YþÕ®óâz\¬Ëf8ª ÅÖ3ŽãSoIÉ Ä“ëÒk”õÝ0µ±úŽ[3Ñ„†«d!‚S,cëî¾{ý®â0u·?¤Oó|òËó¾çá9ç=sìM鱚܂>ôcwÙ×äÜkY¹ß¾'ƒTf-.çìž\ ‡è¦s£Tbä89A.пˆ%,c«¸Œ‡ùÅûä×Ö±A·&¶±ƒ=V7ð·r@÷þ ùkIÙ‰ý¬ºÐͪu×rýRîµË}vOî·%yÀ~+ƒ¬†èÅ8&éÂ4f0‹9Ìãaº5¸ª‰-&i“;ØUŵyvÒN ®³“[Ї~  îBîAÔÙÉ ÔÙÉrˆÎa:G0J%FŽ“äý‹XÂ2V°ŠË¨³“÷É5®­cƒnMlc{¬nào¥[ÿ©^’Ÿ!;ÈNì·A9`¿”.²›=OÚù>ÇNµ¤—Õ-d>oß“~r¿l?•C8Œ#8jÿ•ctø¿Y'O'ÉSV–ÓöG9CeÖzrž_Y /Ú%bÂ0wÁ(•WûöÊ×ð :ÄX=D%Ξ•$S¥0ÌbóXà׋XÂ2V°ŠËxŒ{9Ž'ð$žÂÓxÏâ9<ð"^Ãë̃|oám¼ƒoáÛì|ßÅ÷ð>S=`O»®cƒçÐÄï@›ÜÁ.•;7ð·Ò£¿¦ßÊÏØ¥ƒüYû³t’û­&]èfwÙºÜc-¹Ïþ+÷ÛyÀÞ’AêƒôYÂÝ¢Ç$}R˜Æ f1‡yßç—~rwY\îfÿ{Sîµ r¿-È •Y»$0į„ù•F©¼j#ò5ŒQ?D޳š Rà‹XÂ2V°ŠË¸Â<«¸†ëx˜©î³§FÏ:6ø•&¶±ƒ=V7ð·r‹žÏd#ÃÖ°Ž=ÜÀßÊ­›oŽÔ›#d½9ÒIÖ›#]èfõæH½9RoŽÔ›#õæÈ õAú,aˆnQŒc’>)Lc³˜Ã<¦sƒ«šØbž6¹ƒ]U¶ë.â²ß¾&]è¦ò¤=Oás}OI/õçm¯²·ä0Žà·Øùm`µÅèÊçõdj²ß:Ò…n*^Lb Ó˜Á,æ0-®íÊôÖÝ“û¯Ü‚>ôc‡ìïrGpÔZò y§í’œµ÷äœÕä9LÿÆ0:±„e¬`—ñ,ýÏáy¼€ñÞÀ›x oã| ïÓí¹Æ uìáþV¾Ø÷‰ÿýS:Љý};å€í•.òsÖ‘»"· ýÀ]6!÷ؘÜkß“ûís2HEOUãÄ œµ¢œ£Ï9Ä$afˆ`”Ê«¶S¾†1ê‡ÈqVT ô)b ËXÁ*.ãa&<Ë çð<^À‹x oàM¼…·ñ¾…÷éù€\c’:6˜­‰-žg›ÜÁ.•;7ð·Ò¿ù•“úÊIY_9é$ë+']èfõ•“úÊI}夾rR_9¤>HŸ% Ñ-ŠqLÒ'…iÌ`s˜ÇÃtnpU[ÌÓ&w°«Êçu¯Ë~û´t¡›ŠŸ·1ùeëÈ!û¯Æ<ˆ˜d Ó˜Á,æ0ÇèvOàI<…§ñ ž¥ó9<ð"^Ãxoám¼ƒoálqw]Ø=÷Ì™¿ºç&'ê¦NTµÜFÞ‰»-Cî±_r/y=GÝŽÊãä V,=¬QzÑé>ègåòù>1—üÂ|*B% Ã1šz,9㩤ðS1 Ó131 «¹«¬Å:¬ÇlÄ&&7c ¶bvc/öa?à ã,.â.sç+¸Ê;\#¯ãNtÉ[›»½Ôn/OâY¼Š×Q[¨ôFÔn/¡v{©Ý^j·—¯ÈþÌÄhŒÇl&çbæcb161¿[°Û°{±ûqqg˜6K^ä–ÐNtÉÛ:¥åV{%·‘wânû ÷Ø;¹—¼ž–!¢N¬ÔÎ/sõ$ù,ê·U^%_GK’^ÌôFô³Gò3ïãêÉÈÉO˜ðŒüÜJå «•/-N¾¢ò…û÷ç¯1„J˜ÛUŽ‘Lˆæj,•8z⩤ð,©˜†é˜™˜…Ù`!c5w^ƒµX‡õØ€ØÄ=4c ¶bvc÷ßKîÃ~ÀAÆ:Gq Çq†»š¥g—p™÷°‚«|÷5ò:nPqÐéD—¼£s2,uä1›—'ñ,^Åëèe?¤7úà|„OìŸ|nßä+ôgr Fc'‚Gà?èý¦?”ÏsLÔ$MÑ4m÷4=Ú«}Ú¯:¤³ž~NçuAuM7tS·t[wtO¼´CväK¥Ä×úN©”H¥D*%~Ð/J¥ÄoîJ¥D*%R)‘J‰TJŒô*%æ)•©”X®UB¥ÄZmôðf7•[=„J‰=Ú«}Ú¯:¤TJÑQS*%R)‘J‰gJ¥Ä ½Òk¥RâÞcؾÖwš„0'xÂÆ¾~õ@Ã[_0ÒX‡¾Ÿj¦¹¿*ü¿ÔŠwžÔ3… m+3…Ì2SÈLa ¡FëPSʹП³^_â}¨¯õ­®èª®éºnè®îëê‘ë©^â˺/ØÐ¶Ò¤/H_¾` ¡FëPSÍ´Ð_Q¿¶¡m¥>R©ÔÇ@C4Ö¡¦ši¡%¾®—1l(˶ýŸe YÆe YÆe YÆe YÆe jª™ZâkJ"’ˆ$"‰H"’ˆ$"‰H"’ˆ$b CM5ÓBK|Ç2<ľÞ_á_ý§ÿuAuI—5÷W~âØl©†}øÎm½?¿ÆúP_ë[ýæw¾ëý©¿ô·þÑ¿¿ªkº®º«ûz ‡z¤Çzª—Jû±ÂÏ´üûšk…7Ú5’QÐÞŒ13ìAJ²éšü_¯Ê‘™Ä±™ÙFqhfÚà$GÜ}Õ wÅú²RTXå"—¹Î nr‹Û<`—=ö9àc~œYš†¥iXš†¥iXš†¥iXV¹ÈeÞôël°ÉÛ\÷ý nr‹Û<`—=ö9àc~œk–¦áª#ŠÕâ(Oò4ÏrvD1³Â*¹ÌŽïÜæÞå=ÞçC®ûþ7¹Åm°ËûpÈ1?̇æˆ"i%-£¤e”´Œ’–QrÍ0i%WSq’§yv®–QÒ2JZFIË(i%-£TTXå"—™1xWxÓk¬³Á&[l³#ÃmÞá]Þã}>ä3Ù^JõНù†oùŽk\—pƒ›Üâ6Øe}8䘌ý£ç_ý»oüi:ÿá_ïüãÿ™YÓ&kÚdM›¬i“5m²¦MÖ´Éš6YG%kÚd[ˬi“5m²¦MÖ´ÉE2œs¼Ì›†Pc 6Ùb›Ï í»_ýàOI~yþ{®¦MÈò‡ü!Èò‡ü!Èò‡ü!Èò‡ü!Èò‡üQÔXgƒM¶Øž+Èò‡ü!ÈòO9°ok)(Š¢?…éLÂÀGպȷ¸Vë,~<Ș¨ÚÎiQZlç-&ÿ‹Éÿbò¿˜ü/&ÿË6ù_ûŸî¬sg«Éÿbò¿˜ü/&ÿË6ù_ûÂÿ/íçµ¾µ·huo?=êIÏzÑ«ÞÚóO[ýÒßÎçï·šü‡ñ0ÆÃxãa<Œ‡ñ0ÆÃxãa<Œ‡ñ0ÆÃxãa<Œ‡ñ0ÆÃxãa<Œ‡ñ0ÆÃxãa<Œ‡ñ0ÆÃxãa<Œ‡ñ0ÆÃxãa<Œ‡ñ0ÆÃxãa<Œ‡ñ0ÆÃø§õ¹¼Z»Ó;úÈ4ð±>½y°ö…ï{KzÔ“žõ¢W½]·ú¼íyíNïè#ÆÇúÔ:/|ß[zУžô¬½nµçáz¸®‡ëáz¸®‡ëáz¸®‡ëáz¸®‡ë¹‰V÷öpУžô¬½nåz¸®‡ëáz¸®¿owfíNïè#óÖǺޙµ/|ß[zУžô¬½êvgJY)+e¥¬”•²RVÊJY)+e¥¬”•²RVÊJY)+e¥¬”•²RVÊJY)+e¥¬”•²RVÊJY)+e¥¬”•²RVÊJY)+e¥¬”•²RVÊJY)+e¥¬”•²RVÊJY)+e¥¬”•²RVÊJY)+e¥ìvN@Ä0 ù6Ixß# W>ÅžbO±§ØSì)ö{Š=ÅžbO±§ØSì)ö{Š=ÅžbO±§ØSì)ö{Š=ÅžbO±§ØSì)ö{ŠçÏ'žO<Ÿx>ñ|âùÄó‰çÏ'žO<Ÿx>ñ|âùÄó‰çÏ'žO<Ÿx>ñ|âùÄó‰çÏ'žO<Ÿx>ñ|âùÄó‰çÏ'žO<Ÿx>ñ|âùÄó‰çÏ'žO<Ÿx>ñ|âùÄó‰çÏ'žO‰•X‰•X‰•X‰•X‰•X‰•X‰•X‰•X‰•X‰•X‰•X‰•X‰•X‰•X‰•X‰•X‰•X‰•X‰•X‰•X‰•X‰•X‰•X‰•X‰•X‰ØˆØˆØˆØˆØˆØˆØˆØˆØˆØˆØˆØˆØˆØˆØˆØˆØˆØˆØˆØˆØˆØˆØˆØˆØˆíåÐŽ±eÀŒÎ‚¡*’˜ö_lï6YÀí¾sB,ÄB,ÄB,ÄB,ÄB,ÄB,ÄB,ÄB,ÄB,ÄB,ÄB,ÄB,ÄB,ÄB,ÄB,ÄB,ÄB,ÄB,ÄB,ÄB,ÄB,ÄB,ÄB,ÄB,ÄB,ÄB,ÄJ¬ÄJ¬ÄJ¬ÄJ¬ÄJ¬ÄJ¬ÄJ¬ÄJ¬ÄJ¬ÄJ¬ÄJ¬ÄJ¬ÄJ¬ÄJ¬ÄJ¬ÄJ¬ÄJ¬ÄJ¬ÄJ¬ÄJ¬ÄJ¬ÄJ¬ÄJ¬ÄJ¬ÄJ¬ÄJlÄFlÄFlÄFlÄFlÄFlÄFlÄFlÄFlÄFlÄFlÄFlÄFlÄFlÄFlÄFlÄFlÄFlÄFlÄFlÄFlÄFlÄFlÄFlÄFlÄFlÄž>ÉÏÇkûÖ¾·ŸíwûÃþ²¿íŸÿ{Šïß)¾S|§øNñâ;ÅwŠïß)¾S|§øNñâ;ÅwŠïß)¾S|§øîñÅ~µ±µ³ßìKñâ;ÅwŠïÇuNÃŒ»“Äf{ ¨Ô3¾g|Ïø†eX†eX†eX†eX†eX†eX†eX†eX†eX†eX†eX†eX†eX†eX†eX†eX†eX–eY–eY–eY–eY–eY–eY–eY–eY–eY–eY–eY–eY–eY–eY–eY–eY–eYŽåXŽåXŽåXŽåXŽåXŽåXŽåXŽåXŽåXŽåXŽåXŽåXŽåXŽåXŽåXŽåXŽåX–°„%,a KX–°„%,a KX–°„%,a KX–°„%,a KX–²”¥,e)KYÊR–²”¥,e)KYÊR–²”¥,e)KYÊR–²”¥,e)KYÊR–²”åË¡}@Eóoá7žÓ+1ÀÈ”MßmúnÓw›¾Ûôݦï6}·é»MßmúnÓw›¾ÛôÝ¦ïŽæÑ<šGóhÍ£y4æÑ<šGóhÍ«y5¯æÕ¼šWój^Í«y5¯æÕ¼šWói>ͧù4ŸæÓ|šOói>ͧù4ŸæÓŒÀ‰À‰À‰À‰À‰À‰À‰À‰À‰À‰¸ˆÀ‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‰À‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡‡ùrd¦„Pûoc3«’Ö„$ûúa8ÃIÞÍú¼YŸ7ëóf}ެϛõy³>oÖçÍú¼YŸ7ëóf}ެϛõy³>oÖç]×ßõwý]×ßõwý]×ßõwý]×ßõwýCÿÐ?ôýCÿÐ?ôýCÿÐ?ôýCÿÐ?ôýSÿÔ?õOýSÿÔ?õOýSÿÔ?õOýSÿÔ?õOýKÿÒ¿ô/ýKÿÒ¿ô/ýKÿÒ¿ô/ýKÿÒ¿ô/ý[ÿÖ¿õoý[ÿÖ¿õoý[ÿÖ¿õoý[ÿÖ¿õoýÇÖÿØú[ÿcëlý?·ÇÖÿØú[ÿcëlý­ÿ±õ?¶þÇÖÿØú[ÿcëlý/ñ%¾Ä—ø_âK|‰/ñ%¾Ä—ø_âK|‰/ñ%¾Ä@ Ä@ Ä@ Ä@ Ä@ Ä@ Ä@ Ä@ Ä@ Ä@ŒÄHŒÄHŒÄHŒÄHŒÄHŒÄHŒÄHŒÄHŒÄHŒÄDLÄDLÄDLÄDLÄDLÄDLÄDLÄDLÄDLÄDü¼ÌÏËü¼ÌÏËü¼ÌÏËü¼ÌÏËü¼ÌÏËü¼ÌÏËü¼ÌÏËü¼ÌÏËÌ.Ê.Ê.Ê.Ê.Ê.Ê.Ê.Ê.Ê.Ê.Ê.Ê.Ê.Ê.Ê.Ê.Ê.Ê.*ÄB,ÄB,ÄB,ÄB,ÄB,ÄB,ÄB,ÄB,ÄB,ÄJ¬ÄJ¬ÄJ¬ÄJ¬ÄJ¬ÄJ¬ÄJ¬ÄJ¬ÄJ¬ÄJlÄFlÄFlÄFlÄFlÄFlÄFlÄFlÄFlÄFlÄNìÄNìÄNìÄNìÄNìÄNìÄNìÄNìÄNìÄNÄAÄAÄAÄAÄAÄAÄAÄAÄAÄIœÄIœÄIœÄIœÄIœÄIœÄIœÄIœÄIœÄI\ÄE\ÄE\ÄE\ÄE\ÄE\ÄE\ÄE\ÄE\ÄE\Ä_îZ9Ž(£plöî+ˆ•™ÙwSAè70;2ÓÒL,fffÍËðD:s’¯ºî–îüUÝŠºêþv7ÈîÏ[_Rv7Ó¸¿6+»dwƒäAv7ÈîÙÝà}î=oõ“þð†”¦5£YÍi SÞVð¯ŠZ2IÙº¢UNòQ~$?ž·¾¤äǘ’›•üH~$?’ÉäGò#ùñ­~RòcJӚѬæ4Pò#ù±¨äDzuE£ü…h§‰ì4‘&Ƭ㞳ÓÄfëVëN½®7•&ÞWvšøÄš&²ÓÄ„&µ[{õ…_|¥ïõ³þð‹)MkF³šÓ@C3ôë€êë¨N™mÖTs:¯ º¨Kº¬+&\Õ5]× ÝÕ}=ÐC=Òc=Õ_~ý·5;MÌ+;M¬jÍ“º6°õé ÒŒYÇ=§/ØlÝjÝ©×õ¦Ò¼¯ôŸXÓ¤/˜Ð¤vk¯Ò|¥ïõ³ÒLiZ3šÕœJ_°_tP‡tXG•¾ }Á9×]Ô%]Vú‚«º¦ëº¡»º¯z¨Gz¬§J_ð·5}Á¼Ò¬jÍ“º6°½/„çõ’ò¾ò¾¶j§^×›Ê=ÞSÞBÞÂ'žð¾ò¾>µ~îÍ/¼ù•¾õä½õ'ëÏÖ¡÷÷ë€êë¨NùÅ_ÖÿýÛ¼¼­¨e­hÍ_ëÚÀr4“ÈL"3‰1ë¸çÌ$6[·Zwêu½©Ì$ÞWfŸX3“ÈLbB“Ú­½ÊLâ+}¯Ÿ•™Ä”¦5£YÍi ¡úu@uH‡uT§ÌÆLâœÎë‚.ê’.+3‰«º¦ëº¡»º¯z¨Gz¬§ÊLâokfóÊLbUkžÔ#Ï:¬€†¡þÍm€¡<%%Íüdx /á%¼„—ð^ÂKx /á%¼„—ð^ÂKx /á%¼„—ð^ÂKx /á%¼„—ð^ÂKx /á%¼„—ð^ÂKx /á%¼„—ð^ÂKx /á%¼„—ð^ÂKx /á%¼„—ð^ÂKx /á%¼T+ªÕŠjEµ¢ZQ­¨VT+ªÕŠjEµ¢ZQ­¨VT+ªÕŠjEµ¢ZQ­¨VT+ªÕŠjEµ¢ZQ­¨VT+ªÕŠjEµ¢ZQ­¨VT+ªc]4vÑØEc]4vÑØEã걋Æ.»h좱‹Æ.»h좱‹Æ.»h좱‹Æ.»h좱‹Æ.»h좱‹Æ.»hñ/þÅ¿øÿâ_ü‹ñ/þÅ¿øÿâ_ü‹ñ/þÅ¿øÿâ_ü‹ñ/þÅ¿øÿâ_ü‹ÿðþÃøÿá?ü‡ÿðþÃøÿá?ü‡ÿðþÃøÿá?ü‡ÿðþÃøÿá?ü÷ó¿ ÆxÚÚÚÚÚNÄÆè&fšÎP¤"l˜  œ D * ¸ n  ^$ÂHä<d>Èf|æBr¨&Ʀ”Îx.hú€èDz¦Ø ( J z!f"P##è$´%t&¦'D'Î(Œ)L)’*^*þ+Š,€-v. .Â/ž0B0¢1J1Ò2<2˜33<3¶444x5š6ˆ7&7ê8(8ð9b:”;\;¨;â<=@=`=Ä>*>®?^?Ž@j@ÜAA BBpB´CÜE8FðG¨G´GÀGÌGØGäGðH°JZJfJrJ~JŠJ–J¢J®JºKdKpK|KˆK”K K¬KÒL¸LÄLÐLÜLèLôMnNHNTN`NlNxN„NPâQúRRRR*R6RBRNRZSSSS&S2S>SJS¬T–T¢T®TºTÆTÒUÊUÖVWrYYXY²ZFZfZ†Z²ZÞ[ [X[¨[ø\6\`\ˆ\¾]æ^^ð_ð``z`¢`âa,a°Þ‚b"-9åxuŽ%BDaFî4$¾Œ["Rp—„øۋ,‘e° N¸ûõ“kÀ·ôÑÓ?Bø—I†÷qÎtà~fÉà˜÷Àƒê'Ùà“}îiPæ”g^iR×Tº«Ê(ñľN^\'eÛ›åYÕŸG2$D’uôXc…U÷1—²u²”)‘¹ä]aƒMÙÏz Œ¡Ä|L•U{#WxV¯‹šÔx”Eôy£á®aÙ]çQ-+oˆ—¼Y þeÕWý#vÙÿD8ÞxlÁƒaÀë×¶mÛ¶í6Î á´Y%Y w ˜–WNŠ@ R•j5jÕ©× Q“f-ZµiסS—n=zõé7`Ða#F7aÒ”i3fÍ™·`Ñ’e+V­Y·aÓ–m;víÙwàБc'N9wáÒ•k7nݹ÷àÑ“g/^½y÷áÓ—o?~ýù—,Ëa@÷MÝþemÛ:v.±mÛæÄ<ÇNÎÙ äúå·v)6ü#Rºbyá¢Ë~JtäX´TÿôZw(C‰S'Îä(7lP…›n‰sÛ¨;†Œ˜4fÜ„Mw͘2­Ò=âÍ›5ç¾m»þ{èGžxì©,ϼðÜK¯¼ñÚ[ïlyï£>ùâ³&Ù¾ùê»vìi± JµEk–Ô¨Õ QŸ:õúýÕ¡S[¸d_—îp9\ Wõp=Ü8‹_¨{i^¦£”vaq--ÊçJ/J,KÕKN,NåMÉL-J-Î,ó¸“KK üÉ™EÉ¥¹i9©`>gQf^:DQIfN DÜ›ƒ¹c °#D °#p°E °(`f ŠUX°%a°Ec#b°#D³ +³ +³+Y²(ERD³ +pixl-webapp-2.0.3/fonts/lato-v11-latin-regular.woff2000066400000000000000000000630401504641265100221610ustar00rootroot00000000000000wOF2f @<e¼ ¤Jp`4. e ƒ†`‚ë|6$†n® ƒ> ‚x„A K[ì/±‘øwÛ;>ˆÃ9›C2 ïï¨ÛÛçêH>Z¢ÛQ:o°òÞæñ¶?ÂípS ¿7Íþÿÿÿÿÿÿÿÿÿÿÿ¿ƒ¤".“ÈqÚ¦íyð@Â’tOŠåî0”ŽäVqQuäšg‘ëÖ(³p”ÞÌYxËPG[é «zO5SÍT3ÍD©Lm¶ &ëÖ‡I—&MÀ‘ôaœš6¶°1 }aú (Åê÷­2îêÄ‚KÓª9)6­W³,1B’Ž j²C7¡*<;ª3Ñ÷zZ™” }<çÇbiä+u\0¬úš>56ØŠ a0BWnôº^ ±Ó¡™i¡ƒá~yHTgjéGþNÕ9èLÞ¾\•‚ùF9iCÂZHºËvx&pÔõÿ…þéÙ¢6c.B>°>>ʦ^5>ñæÿÂÿU‹YÆLýoô/üâ…ÿ•¿x­xÕˆïaGòïüèbm(ã•ßÑøÁÿ3NA‘Ò SÈÉÛä¿øóÿáïòÿåÇ?òšøO¾„rl'‘†¢ì<:¸ê=ò­’ HéÊlAR¦×í v³Y!„—uDC‘¯Êþ“¢üsKuCyµ÷Vô77n“RÓò<ð "¢àAð >î•+D¾<çfZš•–f–½ugµýÞö;çÌÖ.Ð?Ÿêëœ[ ‰Ÿ°;äÚSém™U(êº,ªf¹ç¡¾{··÷[Kâãc®UáJ•H‹Ä¨8Kwƒ.uÀ÷µ­AÔúrHþ0c¡ÌâÛ)†Oí^ØÇní¢hëÔš5-t Œå¸ëäâæ÷žxo*Ô†E%+µ–‘•“G9)ú.ƒÅá5´´utõô׃µá]¡‰¥•õVI”„´än*Ì*âw‡â•`36Éôº™y:m$§”y|·hJÊ*ªjê™’Û¯’Ë•¦[GçäâæAc°8¼†–¶Ž®ž¾áË;Ä–³‰*i9ž9¹¸y ¾Œ@ap²èg ‡×ÐÒÖÑÕÓ7<›Ùë·ink¡c`ü¬gÏúµS·3*+'ròCûìîàv¯½.OìœJAwMÌÛ¶¸Ï ?‡(G S¸þúwD„Òµ"Ý‹šA+týˆÁbäßžW/¢©4_´ì·–Þ˃E(…²#ÏÒѧE²SCY9y”“:gÎ]¬ ûƒFRãbbf^Ë´²ÞJR ‹JŒ,­¬W²ÄPnM)Q0 ƒ#5½¶XýB”šX¿$"e–KAɬڿY«9Y^fÐþ±õdX9]wÜè£i@ÚmÚG“T} RƒÛ²# }>ª£!N%¨%Þ…ÁÈou»øv eU5õ¢ƒÅá5ºY·L;tõôë²¾ríæd,@Õ¨-§S@ ¹¢E Ë`8¨Ã="^,H¬Þ†m©(‡JKV P«JGLiˤ–Ùv‹”^ÞþÙ(ˆ,Qð€@apr,ÌÆý¤§q™³ZD‘ªDHgoöý®DŠK›˜™oo™”ô–2š>4g«—£÷™¿ ÜøYÕäíÉÆ&fæ²r Š¥ÜUãy ¾8¢·‰žÙÛBͪ³íÇI'\Ü<Z ‹J\ Püæq.P+þQBYEUM½è“ ‡×ÐÒÖÑÕÓïA?´VFÔtÃ"ËÌ ëÛ:¢Y$ÜäõüAðú1°*¹¸RfBuI+£Ìœ³ke–sI¥|¶,³Æ¢ÞLƒÞxw…B–.©“,+¥”ReɲÔC-¨oí"¹;ò\AÞ°>#9÷m†ìpc¨§Ö’´*^×eÎÅ’ªÍâlCmp 'œp‰‚ÙÞ_Õª6]*£ú^Ñ•,ÜU${Ì‹.í6ÐÆ“ªj6ù)¼Ä!šëBM?+['Ò_5Ú§ ãÆà½)){U»©iÌÔjO§ÕÅèÈS8ì°á. à¼ÿ«º5!ü ‡ý¿® §²÷&Ù¤Dõm,@Zw`+”°±‚y„µ!#-Çý‹aì«~Çåø-nž¡`( ƒ TèZX|»€Ð´¾$²êùB ÁUS*®p¥žÔášYÿÙÿ}û÷æaèçÈÏៃ?›ÖÿDÝ^N]ha§~‡†˜W³ÛVþ_„ù/ÿ3pw²¿ÿ¯ŸLΠ=•4pë9@“r‹wÁy?àÜÏÞ›wõ7ñÝuïy/;q¹è.ÿœIvÞÉs ›ØÜ2{§ª£êÛA(·¾Ì˜ê£\4'ìd.¹$9 MŸÜ»1@´ Á5LtÓ#êÕ¢%$—[0V€êû6ȸä¶eàÜâ!Ö„ ÍjÜ:­ãܪñÏoH&e¦_ÃøNÈ—uíg§Me‚?pÅÞzNyðMN¾)½õL7{»Cá8Õ·:¶ø7(°œ‹NOQ> -˜nQø; æŽM!û‘ øž2Dœ©@ÁI-“9×ÿ𒀄…NT_ƒ|]p'nzS7t3œäÒyÿSc²Y’ny»€i¡4+iÃ;Sý¹ ãÞ$pgÐCÙAÝg z ùýÓa @^èýÇÆmúÙÞ5º‡aêî#ñVÞLÄ]Àìî®–d£1¦÷DQ‹w„Œ ¯êÛ…UÕa Ù4ZDÝ»7wszpÜÑ‚$‡º²RWwP¸ˆQ|$~''‰ø¯Ê®ïmú»¸Y¹¶ ïf’S2!©joF‰Í ¼öMJaVaµì N‡=«ì ¹W¦ˆÈâ>˜çÙœ|ÈP0svP'M³¢€1€ì *±ž`9P '†#öF1wæ32Gxm1ªÄÃ{€·®ƒàÌÛ”n›Ûõ¥ ëAÇU¬L†.ðê£:eÆbB‰Á.-ð;}%œëÈíl+ „–jS†aE–S+ÚCm<¥÷XSìtr.9ërl2u®qRŸë$§÷çkS€®dF°|ôNÄ`{+YÚæ9èøæëhìHjÏÝœ& À•Š7p…Ú=õxÈÙR QÒ§!ˆùNQÔ1¢{s&e8 \\ÍÉMw&©|;>7"SAÒ”/ZÓmÚV¥w¼m άsJ9qfŠšWD$”Æ”WX›¼I=÷LåXŸEåT9‘æ)?P[A_^ú ðRZ/ ´Í+}Ë}€´_j‡Wú‘ú €ô}õbM^´ö "‰ï~hߨh÷)-öº$IÞ$Ò¯rwBlÍ8ùðDE—ۼ´p <ÁÞT¡ÔÁq *ùð 25)ub}>Œ"JaZGÐKê-’†xÂM¼˜B…BÞõÄöãHͳp¾flIƒà3óÎÑѸtÍÌ”q‘àUë:íYx³\ÄoEî¨RþÒ¯=*BJƒ#UH!üÊ$Ü÷œ±«#øhÔ¶àiœ_g¢w±ÆðfÈGž Ý@%%špÀ¹H6ö ø½;|k’ r UäA®€ZжÁQy‰%ûüù›œ«òág-h"üõ+®‚däRøÕ4ÊÃåWAœ?ÒÁn_ ñ+©8‰Â#$æwî|>)ô-ßÇïÀѼþ’ãüÓWm݃«cüfX$#ÎUø¿~ãi {ß¡a74úëMoêXKµ©G rþ|œäˆ¶2³”QÕ})¡ªcš pªQ¤úÓ~á}è“G\¹ÊŸì´¤¸šKO&±E““¯'žv7ƒÒ‰”[ˆ*ö•§$¼A¹Ë¦Œ¢î,J¿ërg$…x˜ÀÜ4Ì`LÀNR€J0(7¨$¨5`‹ºí¹{ÓëFÄ=딩ùäzÑai«Êxh` Ötu°ÿÛSÚ¦ Y»g”ª° TdÒú\A;.ÌÒ¸ªÞ丛ƒ¦Ãrm”(r®0/4Õö—=Wòö‚Ü $ÀÚHÉÂÀß~„¿‹>Sçtã ƒ­6*<Aãd옦F2¾\XU”|>™¸ám` ZNͰýçÇ­^Ê[1€ò-ê×÷Æò”Ý¿Dñ¥µ½áÑ“[xabp ,Ö²gŒ Ëìï „A´Wg¶€È´™eBÅ2ìJ,={c\çÕ²#߇’íyzèºLy¨ÇÜ…Q¤(št)Ï›…¥-‡ ŠKÛ.af¦Šµ¶&ލ¼LR.홪t94>.#Ý $C"JkèÕz2!¶¨\&NÊîZv¨õLzâÉÖ¨'÷õðHÔÇìÓ?{ÃSb÷DÓ{öA­IŸ)*×ÍcCŽÍñêÏì=€FÁèÎSÔ»7ƒ¨%¨‹Híç}½å¥‘R–PÛ›Q·Š„K a"7’·‡Ö|¸úCÔ®EUÀ…Ö½C‡n|Y™ž%U€? M'¡äÁ swâäæˆ¸S²¿RF­ +¦}È‘™çë[¤éPEä+ÂÂz¼ÎLÞœBmüCrõ½,ÙÖ¢5ÀÓ½:½–9%§³œ`Õ aó?Ò)\ÍÒ¢åèÎÝF÷¶ë¨zå& ÷ï¡_ ðØæðR :7«#¡NLe¬™øÕþèðžÔÄ× dy‡YéêrAqm¶)RßÇ" ŒŒËS1?Œ@Ͷúçà@¨%¾ ïÓƒ ˜Za<—.} †[ËãÛ½I~á>D"ÖÓ &÷Ö­G6ü¢àŒ² 3ËÍÜ}á`"ôOS: jYÇÖª©%áA#~ûáĬ1·ŒO‰¨éxS6µƒµE±Åô™µýÀJj9‰¤Ø²þùôÐKÞprh ^–æã)![A-ÑËC1ë—0:Y)¹¥¶Šà[ÕÚÌò‘þÐç׈‰ñü'+mmÈ)òŒÙ¦/YdIƒ2n™â Pm_.C,D•Â"D9¹9p £éäP+˜gP …F<Óô{p÷è2qrh”Q 긔õ„Loµ¤mû…W‰ø |©%ä:vkŒ)ÂÌ- kÐÞBJ>ÄÀ~;R\e¿Ñj_½Œî·ªb1£mYa ‰©^¡"g $ìǸ"Ì­:Mëîrmíû ¼BÖþy˜Í¬i3Ù‰å ùwâÃþ$ЬÙå‹+N3™1äù)™Þoh ~QHä¸GZ`ºih#PGe%JœÉ8Õ žê½Ô…ÙïcuýáË¡²·‹µ„µÇ3Mª¡VÄEaØb®T‡÷hü¼aûœ…ŒÀÜ@XGý"äà®§ìúÕŽÓ—Î|žÍ¥‰î.q…µSºm¤w©§r$_׳ÿ, ÂÒSâm8_ôà r$:M‹`%RõÐ*yÇ~^§r“®©î!±Vâm«Ÿü”êieáªdâ”T0fÚ†‹ÕCá˜pQðú²Š5à=­ªÒ‡ÑËHí(iôæJ+{Ûã•Ì ïybp"”Gܦ‚lA±‹áT­úšHþ´ù1¯¶ÐQÿ˜žŠIV$¿r0<ûÛ¶›J‹ÄÝÁ£B-ä¬IÞi]lÐB½|:@TžXÀ¢ V Ùd™ в oi»íËl´¡„Tò`#„ÖSdBÛEöH4ŒO,ÝÕ[ݱoÙõ^}°Ï™ƒäx=µég= ÑÏ{’ aòÉÕo:j•Œ•J¸„QgüÁt[ôXÓð•Ûb‚”í«P—‰=õ yÁs+‹+0)í8‡—J:3di3šÑù6dÍdQ|Ô’ê©WWÓ§TX[mƒ«Ùó*ÓÖ€c¢Ù`[jX„50¯2BYµ7xìOѬjõ'ÿP•¯„©uñïˉUZg*>%®+¼Æ•æúrÏ|×#ݦßW,& nÏt;Уķ1’¾c1­­ ]’×PÕë:§~®? uÈÌ#ì›Cyaµûeg«ÕH_÷{2SIIuøÆË(‡_K,“þØú(}{³,í&ÔmMq ±F+¡Ù•]?ô•-þôHSOh@MCÌ“ñ¹¼*`e¡w£ùÈzþÎöE0`-®Õ¡ÂV€–¦7WÒ“}NÅF ŽOã[:H-¹£i1¬Æ)”îøtF¦¿ËÞ¡ä íiçiŽÞ—Ü™ ÒˆAdPïïý2)™ÕµàMV‰ªŸúz[’°Î@©ÇË©›TÃÛ”„&}ò+žÜKˆóBŒ™eè«4ƒðC*ߨ ¥˜Ù®Ð@9öfƒÍ²$A‰- K(&vÇãØªÃI!Á¸Ø4-DâÊ"RlF!_iѱA¶‰û‰²ûå'1d"«d+êc<3H‰ÖY¾ûLÖp¯œç™=á{È2Ü?X/Ô9­Ð±ÒÝÖ›zuša ¯£Tò80ãõþææƒ…¹hq!ž_©ß¯%‚ýUétÆŽiè “Âädf×NIKä5ß½&š8xg)Îó¹6ô¯Ý&†KKŠry µ7¼#‚œÎ—8>R]Á7l[pÓ´EØf×ÙNpb2[zn ªKÔîç±§ìî@9_´ öêP²OþK¸»ò|þU#hPrÔz᱇l]PLÔ¢ÈD=¼|Gª—°ŸqÐËA5Ž ²ÑÍ”ÌæÒmvûAäñlãƒîöŽè¤ERi°Ü˜+˜0úqAä›%¢S~ä7!l»™)ÁuÈ”Vt&Õ‚–*ѺSšs·TŠ -ÉÁtöIÉA9ëˆp¸«ü Î!“ÀÊOĀ˳p R6W-,´*¶]ô틲$¸v™Ò‚o×v%YÑÒC½ VîÚпÀ¹U³¿ÛeÛUÑ´yA+6_œT–Z":º—’LOÜ©!qXAh„‹ ÷ #&R¥[gàdäˆ3Ë'ѧl•…v‹T‹Þ’.]?œ§üÃ4*œH¬Ñ³Fóèq Y¦ 1%$›²ÏJhÖä%ùRCHèBEK(?,¢PæöšðESv ÓK)‚Ú~*1œPfºx\›ã£Ùã=âƒÇuµ!Oˆ÷Õ7ÛÊÇÌ]óð1/u޶ýÔ¨‹eÄ3_(†m½ëý'µu!‰B-|t)]2•†¥#Î~áªK¾$3Þa_a,{Ÿ&´_ûº¹ýšpU«xYq)úöÁ]ë>ɶÎÕÛ{ݺíEøøgo#/£Ž íï?8rpS÷¦}•yuþ.¯ƒ!¿‡  ÐùôìK7¿ñr¿µå¡}~׼ᮛJ3ózü5\^Êï•%Þ¼®áÀ7ýzå]E]úöœÐ§ÿèyiÙ9yÿQϹsßþc™TVzAÞ…¾§ÎÎ&ÝÎí®%ÉnÎéÖÔÙÙ¤[9ï͹Ýü?¨†t'\C"í¿eÒZÏE2w¾|@”ܘdÚ4Ÿ3™hÑ“Fó…ènQC‘í\MkÈ q7Ä;¨ä\ºï@{¶7¯ˆ\^–™™OKσ$%å„ÒñÞTÃ_`§¹žîjÁXn*áâÒ|ήL•Þ-¦ÂŠˆÂœä–þˆíödkÙ~‚Æ27k=N(¼[á‹ÞŒdR2µ±N9†Íƒ«ÐˆJHŠ,oUZFD¤ˆ|"‡’šR£Í?OüÛe_…·8ýª÷‚ÔNÖÖ/c¼•±£“sÛ¨àXË竾J‡soS–HÕfUù^’ÕJ˜Ì³Nöµ¥•fZ4ÑÒD÷DN Ä7Ž/€P8Õh‰UÏ£ ŠùœÊhA’Õû†jÜ}7jg™lo-¡Lé5Œù–ÆãŒÒ’ƒÔú&ò±ò|ñ emÑÓkЩ·6X="ú(-²‚+*C2˜VT–i&ÓcË|s4…iųbJ¶S.Ma/šàÀ_ýÙ Ÿúr~õKï½ÞµEdÏâÃøfm,¿oý‘Nxy´eÎ&Hï­ÐM¥ª·§¥•#°YçÌÑðõGúòécM¦Ãø¢Az‚K*LccŒY¤bHjªS*ÅÙiQ…Üô@hŸÑ cÒéA9ñX¨œH*€‰k#)El|PNúºôŸGpDÉ¥FâfM>mgcíšÑ°›R]Kœ7dø•ÅšÒ>¿ysÿ\ú:{”+^[Ü‹ ˜¡Ä”p8ƨLrI —mʤ"Š˜,#œÄ,‹ãð"u“͢ݫµ÷C¸¹S]åôO!ß{F/aÔš½?¥,< |ßÙq%üÝOùË«£qÞ”žÓóÓ5îÕ³¯¿R/Â>ëèÅ•-}שu]ÏtŽjV6ì-œr}’¯ßü”$Á>uŠ^¢¢Kû¾!öBiÒWšƒýÃûô—ð%¹ŸEnãµ[ò7&œ(.I;QÔ9Ìù¯:ôÁ4FêEä Œqœ†Œl¶Æ$Éìi’a@NøËÚ™ÝÞª  ÿqâ’߃Nÿ»t)P¸H¸xÐMŸ·_u\ÀÕ©}òD¨š,Ö’(Àmí;»rÈ›ZŠÏjð’ÄUp.5}ÖA?€“9`ê¼§Ïéœ/ïè`\i_–c•ª¤Ö“¹}hè§š š˜:tµõìД5#»ã"YáNqËës¶{uP°N â@Äi·}u^e§2[JbÜ'{›CÑÙ£û̬Þý‚‡itC@Cœ‰ük’|]WÌàÏŠˆš@qí2¿w„uµ­‡ûCÿèEÞ#q `0Åq­Ûr³cóò‘K/gÑ’èË×5„ó–¶Ý´Rµ1(XëEùÆ’õ‘tqD›òà✅XJº¤è€î 3Ò¬S£E)s{|7EÛk^ã÷AûË2/~mž)ÙÓ¿6éýšŸ©[¶fý W\> ^ŸØÞPhähË*5ªi…ì=ìØzðéË–oøxàî!>ÚËÿÏmq…þoîã~Hþ÷Ö®É÷¡­«ÛéEÇ¢©)‡¢ ’™~¯[r(6;+$sÆkíÃgy6«›;†3©£ƒq5’Îr­65·«£ºÍ?Ï ìîù2£ÝJÜ©ÓÒvuUAn}4ñXÜ+åÇ–’{÷Lì{Ü_Ñg]ÔqÞ6s3-Î<°#ÂjüÇ»µÎ—Rë5ÿ´Ie?Ë›z.%µÒGr¸èv¥±•å“pr¿?±Çjw¶E |#m‰’±c ™-{Zwÿpÿùé¡ /ïEjTA¤‘SÄè4¿ ¹.ª%¶.©@L¨ÍÎÏœ­«=B³ô?Ô ¥É9ue \a¢)¦ÃÙÔHU*‰¥J®C´DÕ%ª¤„—0h.%×\â°—&v^ù¸ôýÚ’Éß&²ûS б›”†ECÿÆÈ–òŽht ÿFƒaQ¯Än*(H›`û[•òL ¸–X°·(¸ˆûP¸-¢sâ³jÀ£…Óà@±OûúïDêÛ½ ߇Bd ¼Vüð0;Y¸µÖgô!)6pɦCô–ÆWíEÄ“µ]Ÿq+ÚZG7z·Gf„éIìü(»ï™<³uÈÚQ(Çt)s›ÑBꨃÂáÏÍÐ8¶¿4XNʃ·†Ø£ ¶5dKð _rØfËPÿ¨ Ùø­´%fÜýàî±QåYëÐäR RŧÈé$bmmþgx³y.¶”•TÈÉ7fAvbÉx¿ÿÝÿúÿu´¤/¯èÐIPŽéˆIF@ž«Á±ØÉ¹ÎK‚Å0bJØÌŠ@>:š”ÊKÊ © b£òÊKKŠ]%Ì®…Ø5#´ Ú ‘:™e7%I´±ƒ²Î#VcS‰ ‡“B|¾j_Yñ¯üZ Ì…Ùå–˰´^—[¶ß€ñ—Ò?úÄôP#•W‚ä†eÏ;«‚òtñr瓘"bî ÙÇ5qÀ-o3eonÎØ®Î¯ ú~¹ÿÿ»×¸SoB°6Çi‚:›®A0¬!6º’©}ò\CSEP*â ü±Â¬LÊÅV­ÚP§Aò!gaÊ´w%Õ©«óÉåô—ìê‚ÀËš4s&»¸s¾>À>w2a5t“ªkÈ#éÓlv—ÆnÊÅšjÊA8ÂZóýòØñѯƸf[1¤TF ² ̸}d§šÈyjñ«¡*Õ¶1fþï.¿#=jÁøý—ᙃ¼o¯I¯”]Ø#¯2Y3Ù&î]_aMåÊ߈ZW#Ú+“‚ßàÚ‘}ˆ”à:‰3m®2ÄP|ü:Œ# y ÅÑúÎ{( ÿx §´ˆã—€bøEΆF.³àyÁ…мHV*R–N¯RËãóŠöQGSò||ê[…Aé jOM-Êš%®Ce% €‡Ý€<…V†w7xº«±±Mëâ¢mWÁ±Cðq_ðr…†t; &fÕÅe‰£¬´ÏBŸBO5ÃC÷_à›†}i3{R ±G[Eâ,ÖD!¨£‘Ûàªö mˆˆ—ÊZ\p J$ŽFšÆ9ÕX"M€íÓ2>jIL €6L‰0SºXb‚.4!…F˜b=E§E’4ÐŒ 'Yjµ¤Y\ræ–ð‰â.¹38ϰ^òNëËœî½ ZöF5bÖå¬ÝÂ/oÏ8n1“Î4öžV­v=â;S³O<ŸŠ\Û3„·êž{¸¾x1†ûëACÈtŽ—kýÿTÆ)VÑ׃3:çéã‡3]küprœ,x^!¢Û¾­´ü!¤Ë)?q¶´n'½Œ_ƒÈ£P³­ Ô&ê:¾ø! ˆÊTEgòBK©Î§™>·ü:?pð%ÁŸwAf¹{“Îû|n ¼ö>˜¶lÇÞÞžÕ~³y³2{lü‚!Œ¢ƒUS(u/Âô›‡ê¾S挟7„¥¤h¡çұ߷³=%&¼) G£³Â¿ Æ¡ÃEßeÁ¦v &Ú´¡Øµ ütÔ7ñzJI øšOb¹kàXàhN/1I ’Ô£©7‡Ib±wà¾Ð¾ > øN9ˈá°N†"\øqº#J—ħcrÒE¼&1Ü1]M“Óú²¡ø\,ªZ©÷¾‘ebù¹ç¨x’kˆ.5`E´ø\"_Ú e9"¨Ìô1fŽF/Á·'ÜÎF³Ñ?§Óà|˜f%…$2Ç=VÄþþÿµúûU¼ò/®¯+üü[ÿÔ·–þ¯ZÝÒz+¹ý|¿4ÕWlsk\+¥Wð¤]L[እ6c·VGÝS[3G.,§t¡ˆ´Ü<9~©ôr$_SÍáÅÔpD¦˜ Éõƒ“po­w&¦³cZu§ÓÁM«%ù3¸3:mšf?£=ý³0;­ÑfÇî UXû³¦‘ŠŽÇš½û Ÿve#w‰†,•ýY3(EÇ#íüAõs{œÂLóW²Ýñ«ƒ!ˆ¾ŸöEÃàdyÂ0¬hàm ûôe®A° Â–Ì>a!à‘;iÞ ö A5£2î¨5Râ+岪xÍÍrYÚ)äxÛ{‹c}Úšˆ1!L)ÀHC˜'{KÆ8„ÈBXâ¨cGjÆ{ÆËGÊ7õlòÝ~ ~¿Ð}¡4§K„‡rÑQÔÉì[Ó³º' eȳǺo={󷞣DáN âÖÿÚˆövl!Ò]=L½¦?„' ž ¥DµT!I>T^Fr´˜ŠWcRÄ 1Œ×†G!s¹¡¿Ï@Cüÿ' YWLtGËezjvƒ:Ùìá +p²«6–OLƒÀ¥óOWÝÞ*}÷“†âlÎGJ‹«M¥"3(r¶[jr’!ü³ÝƒH71ñû…?5ye@qiòŒ»ÒlÆŒA‘,cÀ¦Ý•lœ©†ºåÐêÒ>RYSÕŸ©²|ßÇ5>‚bv€}³[ÏJbÊýÙ¾Bjô}»ÀÍòzµF´¼îƯ£§EòSþƒ†[çKºÚ¤ã3þæéå1A¬…ÁŒµ „å1tFEŒP˜a˜ŒX‹Pà¾Z’eÔj³ ¹ƒØ—±àEËÖsÁÞÃSŽ_ü~æYㇽ9Îù©Ã?ÒåS?ôk…ï\KI®·Ý³•Âú‚6Ko40ˆ©x|ž'–/û!ÞÒ_¾ù1îxÏ ¾åÙOM ïõìÈ“ˆ8ô<ï…;ŠgeU³~D©÷¿à‡8|øl—H/Äÿ è T—È# õbÕ¢äÊ;™¨Q" Å&¼Î¹!¿£Æó㨩HA×<Œz\¥<7×¢€?¼Ö_—P?Nü7õ*õwôæà´§ê)GL7òˆr'¼ñWßkyoOSoÛÃÚDåÕ<¼²Ïõžmê‚Ô>±Ãx--|VÁSó´ÓG+áÿ6‡p·KƒþSŽ‹;ìôœæ˜ÃË%^ v®§K›ôm£¥ñ?©m¾ÇǽÌÑr¹º _¥Vk5í*F¡˜^‘-—;ÏjìAÉY ­éÎá_p0«ëBÎ2£ERÈÜ8-AÇçãk5’.¤@=é&ƒªf%"i`÷íp|2MŒUG‹S’³È‰H>9ƒ—HNEÒ1ТUͤ–EÛª³·QòÛÑ»Fê|síQÒ;æC§¬L}-á`yô¿V%ª‹›mŒ%Ñr‚—s½<ä/e„B¸É'¡òBy’õ€LnJ® H¥UÀ²hH3dÉyEØç€­Ò6N­¯‡£ï‹TÔlÐA•‰òýz¤Îq¬8È‘k¢”Âp&+º”}öuº*ŠÊ‹2¯¡ýK‚”¥ dÈÿfÖ Ñ ‰¨ÉZ7À×µ§¯ðŒûäͤúEwu‘Ç ŒyVóZá †½ ŒNâ®ñ+Ó3–µaƒÀ>O^ºÊóhª=WÕà RýÊë³°°ž¶qªl}úƒºí'¤5…ó¤†:Æáòüô9kã,˨=$%Ê|»mçó2Ov9}ˆ0,‰ÂÔì겿qBvFaD§<ž#CTQE¨&ÔˆlÔÜw)¼z—r…Y/{§·Ló^êKôêÀ}·¤[…t¡jʶen[˜*‰•»'„2¼L¢Ò¼7©YзP”|°gì å1“Í\k"_9œô;Ç’ðàr§:Ĥ¡íqdç¤GT¬Š Y䂆Æ<›ªvjÜwþœ©Õ[.»­réT=÷­¸ú¿Îé¡gE»rå(ßÎÝKÏŠ‡¦ÿë×¼õû¶#ózÕè¾Ñ(aªG‰×;:ìÕ#{xïW¯j$óúÿxUø6®€|ø>>è–¸ò[}Á’T•h^¨íÕîdÚº3ý×Rg.TuϱôíçkJãgá£=ýÝ_S† QÔafª:³*õ|™$vâ´¦£+åÿ"uC5ÕÜx«Q¢p«¿í<—³fWxL(EÍGE3ÁsL´›¹):Š6»h¢r4ʀ삀BËÅ`*ÂäZ‚à?!B¢Â¿ýëÏ\%7cùŸHÈ÷./ol‚¶AáÍ.†F]j¦ÿãJòŒR1+ ªmœ5~ïý#êCѹÌ;{­<¼¨Âä(fùŸÕys±»ìuöxö`/Åûdmѽ-ôÁÚf¤ ¯+Yw¤|/»q”¹tgûþEË LµÉÇq“¸®}ï6Eù @§Û"â¦ô}{×Úo“¨^ê žÜ¬ÏÈîDE÷sëÓx„ 7 Rø÷vÔ¿’q“"ÙôÓØ™}ìƒÆ<D/Q/½]Ÿû‰-2 &¤õõ @Œ[k? ³Rý‹¯WxL|ù…²í!ލ{{™TÏØ2LL$¡^Ý‘è2LLPÔóøao#P‰ƒçŸ÷˜çã~»^ΧÃn6éš2w!çþö|³î´7 È7)ëDaŠŸuT¬aD^66M•ªå…1ú‘J÷Úu:ß<³¿Î}è‡/:âDž#¿±mêgµ{GÝÊF{¸LÛï›ÚÔ{¸ñçàÂÔ•w¬*j uäj3×mÜovãϋ稀µÛ'45ã|Gî&ÊFÖ%ZÎÔ †½^µ3­O¬¨Ö×çi]èPºaÏß!  ç›ÿ6~õ”ÛŒN£c¬Œ!T»A5Eµ,Q¼êÈ·pÛ—Öm°6òº6uÀ7§^¬ïç×§ã~³žMÆ¡o«"MâȱÌež\Ï×úγÅÓ.\°Nt//ÛÉ2 êÚ¥ÔzêSMLˆñxÆ`»Œ ý‚™!çE+»à8‘•ëE×pkÖ'Y|½ UêZs°Åõ*¨(^ÖM™ö|y¹ßuM–„Tk%‡?êGØ'UÎ…þóšê‰ýi žcyàBœ§àÂZ©ñ*·™z.=‡›¤‡}Έ`6´s ÷+‚R¹ÞKý&ªíˆ Œž—mV¤•·³ô}-Ì/‡«Å W;m׋Y×$‘eh(Yz8$GXiòÔã¹j&õ_éùèiÊ Eæš8æÏ7Æi–>‹š ÊõkªnñáG×>í]AŸ­«™öQuÄžv"ÔegOaó3·£¶Wžkc¶{(§š²ØÕÇ«&QŽ /c{®/ ’ýpÉ/'CSÙfo>éeåÅï‘ì×úÏgé>å«ûÌå‰ýÄ ¡6ÐÅ'pQ“ˆ®æª~Óã ˆÕÆ>“5–„‘¼…§Öb—‹‘t.~”Ç’[f´kÄù*²DÁñôøÑTjY€æîõ2#c¡ÁÚ1¶NÓNíº3»z¬kˆ×³D‡ÈoVc€ÇÕ~'ä-óO`,§“®*S×2Zk:®BØT`R5QiÄ¡(]ZŽÐâË€ažFW†›º.и/ÑH4¡˜d¬ “Ó b²1Œ„˜G™ó¤zþàFhzYH{fcÀDïïqß±W¹¹ˆgÎ*[¶‰'ª"ä~PCâZk(kv·ÆÞ¶ÁZw˜Ö~€hAº™Ú6FÙ 7÷6ã^‘;¬–o÷‚áÒ…n_y÷Lu¨)³šý‘uWŽN <š¸ÁN/éõ Ñ5nÊKš;Œçr0ÜÙYæz™Z„ÞZ ÇX”‚[ š%yãÂñPWdŸþpø´üizZíë½mÚ¬ëáú,¹Hr™À,=à<íÒ,âÌ(àmcf&"B(žUøÌ Ðl*ØÛ…¥CyR£$ÙcCSŽCµcͽH3W|õ¢^Ä÷b>¼x0 2C¾åÈÐsZqC_‰2b€ƒ~e.V°ÇrÜz×ë‹U6= Ü˜ƒah/e—Rf%½ » ð§ýxh3†­ôí …uIWÀ»[ƪðt‰À«íÜ*=æï²²umv“gãYDèÙ[$¡Û2vƒqÜý% ˆE©-‰ë«õrì›jþ§·”ÒQ{á^rÁê¥ãt:"üôxj±HÇgiè·´_ó¨Ö w2U³uDj«§Ò~Üâ¯ÿþÍÁÍtܼ.[¨Â¾§,±Í–üJßë;yø.w)íÿ,ã󯤔˜'·¡ÍžÞmëÚ͵1®zÚìNQÈlQéH[l­ü§y"rðSlª09iY}¼¯ÅIíThÙþ*ÄŠ/"zT ÞκL•v¨É¥úîK'q9¹åm‚‰ÊV‚aRªÑ ú{E›r¢„/o4+¼>ßw¿j*¿ô‹(À7θ‰bFÃ`΋–^&ŠËÒu»è™ñh¦È×B¬$ÇÈë›åj½„t¤ ¡šý*²Ñƒò9òPOõÙ>¯ •èî<-D~rõÁçbΑ7iÕc«î…ö­ím¾Z“‘$SBDÞÝ{×–["³äò͌ߺ­ÄUYf™* @¨ $4I3=×NV†“¶«ãj‚J„Ó5LͥΠã¼]d·XJnu˜$ñ\Ý®IßJÍwèÂÍ_/_©™H9RYêyÁûßômžº¶6â¹>H±YSf㯒‹;ȲJ;ñ—pY§@ÄX¦²âÌcõCÿAÌ(ƉÿÎì¶iß-oà8@De0HÈ[XœaºŸ4üæûIM9ù²S5ÆÉ®½Ø¸êñSYU"gl5`¨IàÔ€Ò¼‹9¬!Áí=6‚8>ꆾ±ÿÛÜ~ÏÀý^HpùmÛ´ SÚ„aOMÍÍ#ÂvŠ.ó, Â{‚#¥nó\ ¼¸ì˜uB¶ë%æ6Ú·|y¯­ãÐ\ƨžî¨ÅйTú\°!Ç&šÐjÊ 8Ú· íl¶*õhD4ÄÈHñÀÆ‘R\Ńy¿kù8ño¿±ùÓ¥ #š]¦2MÉ¢ržÆÍ6r¸Š‚½†vÜ»({¡ð†Ã†¿Z9×EC½âߎ¸¿™ m%r ‹é¾ðIä!ù9ˆ ;ÙCNÇßÎC>—¤Z,ÒÝ¥D*E;Aî·µHØs+×{¡À ÆÖÍ´nÃá&ÄÃ!Ôpl­BÈ«ãŒçRÂ~ï`Ãô”fgk+ïoEìßs—uve&k«°a®àÁw#Ü®>FJ…!5½‹ ‚¦ý‘™ëcýN†ù; wFÛIrIOÇjÀN—”ç™z=añªö­©Ñ÷† †¾u Ÿ™Ü²çólÃaŒm)€¡šÞvBܸ‰MÇÏs›ÿ| ÐMOëV8ìAx˜GÛˆtšº°·(yU`w› Dm^'3Ÿm7³ëüÚ·E‡ž«v£ÙõšÒ ^máæ‘ä’ÊÚqwÕû,1ÂdÈÚÑåíiSW£‰©Èæg5±˜xáxí[z<%9c¼ËD"@ýz;H‰"Ĭ )´y:& ÁüaØG=Þb_Œ1,ƒ$‡'§ÓÄe33`zIø{½ÏÈ4Q+LÇÓå% Áçv ŠZÍÃz÷•JgKr Ií ÷ 4ègƒz@R¢3ÕΉ¦‰c9º"“<øÀJŽz"š¶3”æe¡Í(1Y‹?Ó|<â‡(ayö½ï‰K¶¤uÞçåP)—¿†ÒuìÀøÜæ6;AîD)ÌW ŽB’®Œï…Ît (}Î¥bï_a#ÒÜ/oL¿+LQÕ†`¼Z÷n £ö asiš²ßê—ïþ¶TfZÈC©‰°zù±Ð¡¶_J­7ˆG)ø¶$ν}ôgà†žìÎWýrXf‰7Þ·žª‡N†^¡»\&·“B¼Ôãqž÷ê4 Beã¿B<È'(`‘‰pU¢64W¾¿&À Êiâ;O­/²—H›w,)ÃŽö¤¸]6/Ëà õ$~gÛ·‚”ä€\DΈöR]Ê&o(ø6¹ åiÅ1Z©¹ ¿ÚTÍÈÃqP¾G¦„;·~«v.è@™Òªg›ýÞc¿Ë=;! üXpñJLÍjÀ#õá¹Ì+9DqLÀAÊ„Œ]<íÕÑ[âÂqùVq2£Ø¡Ï†·Éô®=K‰º®mù£ <·ñUs¾E!ÉT÷7Äà¸K†œKñ@r‹55ßÒ>E¸ð)c1¹KÂÄGÔM÷§!Œ>´G`XrG\½~ã÷k|æÆÄVø&'åûy~e%²±ÄɬÒuŽ1Iµ„.–û!ê*á ”³¹'^"|g,Ã9Z5…¼]Ã8”‡Ùa³ ‰!ÂÿîA?á'ÜPÔCÀ<ÜâÔJ˜œ®¼AÐ7t†„qô óA·2“sŸ 6âöú§ºÍ'¥êÚ­Ö‘ŠÇ¤È}¹ç“.ŸPpòü‚É®¿?£27K‹nsx÷’ŒüE,lÉíA)Uy:RjNKo,bñ­âTU{§HÇ㻄rÍŸŠ¤ŠªPx =@'ÓÇ hËŽôÔ0^áÇI­¦  £¨P½Í*£ò'—ô²¶×r³~pº¿t¸Éh†Ä‡´ Ý0«’C€†þ1GÆc¤Ð¥WåV÷·ÛÿŸÐC' ŽP²GN,çžùB~Îd”|Ç@ ž2´Â–¹_¿cDŸÙÍÇϱºßTeþcØÿFhråO/°¾íšÅÏDÈCi,»š~<<Ì ó®SH~6·ÃzFÅ–[ÝÌ7yÕMfí¯ÖS«óÁ^Ú"×µÅ>Í«sŠ;=¿„)u-X¸‰$Ið—”i•t®¤Sp-ŽçÒyã .Jeߦ7Q0b”O7(µb7å\o˜ì²PýèERT,xÈÄ- >±¿ÜË7\¾ùø|ðT×LmcšÀÎÉc¾ÞÑpnzL>¦¨š¿ô–ó$Ûlá\•`bËB©ÇÇ•ñŽË©dHÍÿ¶Pñí=sF·°îõ`wCÖ—q ‚Ç‚˜ßî†Hç„]ƒšØŒ\-¦“¶.°­ê}šz¼öZÇB~¾æZœóô &â¬2^vyÃUòTê6ùÓjû”}Fæ>sªÀžI;7KÜ>3ë[Ïå¶ `-D¢‡ƒ:D±ªí[YG¬¢Ðdˆ€ÜòœßÊëy:`ýµ:¾Jía,ð¹Îô_ÝcZÞ¤)‚Ó3$[…#éµÇ@QUmn$&Úaº¹­íêÓí™Ä6`CÖgú ($9»™A¨°c† ¤1Ve‰2€^£U>…³ìf„ùq)`Ùí{*ƵÊå}lÚ8bÃì½3³êÔãštÚ¸Ò[éa¼ÿ…8 ét_éXœ‹á½¦½"qô‹ŽªÎ‚•N€j-HqÜÄûÒÂ5MÕÕÑÕ¯&ªÊa‹,¨ƒRPA -0ê:À0ù ßÞ5abr²…‰\S¥E82¡¢æ*òd¾D´q ŽìÑóXŸ¬7ñüçvî¶€CE´ä+üä£ì|2Ôå¡zõ³.þÿM;₯“¹›bˆ®§‰ ‘öö}XÛRì²3½9†Eg5…ää­æ²4m²Ú¶‹ÖÝCò3Z…È@ÐxÒëÕì8?j+/Uä÷R;Eôm7ƒ’ˆtZ<›Ï½wzÚ‚„,-BÖÅ”$µ¥¾Ëj ÊÔAÃ;Ì¡NXixm'Cfú\/ñ/l¤¸[$Èôú_ bü„ÃANäiüÏýÏFƒúùtܬºf˜l-7bßµ¡#mMG9¥9Ýñ˜Õ1šòÜ¥ü Ä–9\;ìô˜ÐdÁ›ÓÂêJ07SÐÖ1xv9Õ¡¶t±ª£+NÂÑà~l]ZÓö{Š|vÛ¹¦v­ºÿ¾CÓ—¨!kJyÑ”“ºF­-$Ĉ=ß‘•”õiY• ÉWPú‚èñ¼§ç⬖ÑÌÙ¥Þü¹QS_>6«ÙtÂC›böAA^.î×ån×ûšæaù€†›û:[õ39]´:ÏB…s‘Ù˜׉ÓÇž«Gºú.ޱxwÖ¾ñÄ“·‚‰]‹TÕ8QÀfæÔX‡DͼN§´¦ÜN(ÄWzööR”†ŒR6jóJ!s’“Š …~7´| ;xÎj5X¸3‘į3Ó¹TV³]3(]w .pãÞ™rŒÔNCúY¦À‚…ov #¹V !dg/¯ñÐ0(†ÛÔkšÌõG˜ñ"3ÄCJ¼øKÚB0ÜjôƒZh×u¹¤?]·tGß<÷o _I˜li:û!D+tnúPðD³,ˆËrÊ&w0|A/„T) ;ŽWƒ¼Ãs'ÈÖér˜§Táq@œPKDºß<ª`³ž\XæûîÒ/!_ˆçi̇‚uæç`3†ö ÊéÒf5Ÿýä¼(œØHïô¿ï žËÖþL¬³ƒêìÊ3“t„ ý¢O²j¥;I‡±„öhÞÔv¡zXªTÖã3o!¶ÉÛƒ™ú’/qFð!¯q'.UÍÅÌÓfÔ[·êæsß×,ÊÀ%f¾ä‚i T”ȸ@i¯ä‰­'¹A=v†÷àì·dˆÞNË´ŠõœØ!$u™ Ⱦ=·ï º7ŽCü›>këú©”ð ˜\ØTé"s ÛîÉÕË¥TNF ‘¿x Éhñs̈± ö'ŰÚxÁFô”øœ‡÷ÇPŒ#ó¥úûwÿòàrk9uŸóþ¶íj)l£ ºá[úÐÚ³Y¿É ýr‘q·ƒÑÕ£NöMêDÍâÜìÂ6[7BVƒý‹J“Ä Y³$§‹Þ•v‡¥~rJ(8òôtkhµûÎ ŒÀºªB9 7Ö~oŸ°œ¥EYWKƒIz”\{¿×/¯¬›–hªVKIJ7¸‡Ðl™ñC½ñÈ×ló;Í%Q.½×\·\$ööcàèË7°Í¯L›AMW²D.IÔñ%H\àʵÇÏ"]¾©Kòr¹èØ ¬cû½“œÌ}wöwÉÉðLm’>Òäè´_)¿ûaáŸ$^?ñdü³‡ûLƒA’]çžRÔUøÊÑ tÇ$‡ó mè,d»çp¿"©eŸ[†øÝÜð§{8‚o!ìØüáÞ‰Ó‹÷:ÇÏ*Ÿå]òƒ×áhê¶aòßåx¾]Ǭ­nbM³ð âK΀$nbŒ^Ä É›€.--fyµg¥äd°\,ŽËãü:,îäz„þi[¥=žõ2cÁ}8TÐïщ\NçßÎóܳl{%¸|¸[m¤É‡½¹¡G|ì |pý}¿ Ú¸-ü^±‘ñ&`PSŸ[ÛæFqÔ,Šœ‚K‰<ø3¢ìãÖ @Wîqë!õ»—ókÁO|T×…bŒ’´ùæB*@z©Œø@~‰»Ø·–Ñ›\º]¹™\àè>—ëÜŒöÐ)ó ©h¹«§±?$úu³¹‘™¤6øŠ$WDà$^¡‹5 -«‡½ÆPí‚‚ªhß‚(òî \•Âú"²ßýÈCºØ7,jƒËM‘ÓÛM ¿7°ÃÈ=|ýF­úÞ–ìÇk9X6.Û÷¼÷2³¦bÅŸñX/›éã8ëóit»<+séÒ-N¸P“}Ü]k6Ô…ÏsXqØYö³XÂ|¦Iµ{VWC^ ZÉ F”'òŒÃФ Ï[ã°9ضƒ#ô‹è3[„ió¡ÍÖ«µtD̶¬ôUMjG«åló+µ\Ý€²],ZH\Pˆ8•=Zy\0ÿøŸu< ˆá Ñ< ƒ8C $@’c0Ò·ldwþ "J—¯è<}”sÛ ¸ µL'ºÚ’ãË®”£Î%#š§‚z_8ß¼p+áæÄSê5=DiQŠ™u¢³Ý0JLýŠˆÀûÉt"î*hyYX$½4žQö†(=,‹åžÅæ­O@æ~†Ceïƒâ+¥iŽÄUnï‚֣ʧ“š~Òõ¼Û̧Tå§qà»¶1wŠÓû[…ÉüU†éó™zô4N‡ -[tÌ (f÷ÿaSa$ŒjÿÖIîþaX>)2L`¦PÁvsGàÖµ2â9$þê@üñ/õVpHÚ=@Hǧ)²[ÙA¢>ðÔ-ïDÍzNšQì'·OÝ0‰vÃë;ØDÔÆf²I`>Oë6RóÎu¬‰5ñ7æW“ÇÆ0ú½q„V=X¦äy‘Ã%×Ë:Ð5ð$ ¾ÎÚ胥}ždç±,ÙCÓuð$àÿåÈ%gJž'ó“‚€uèI]€ð/žùŠ®“~/³pk ð7Êë‹t½@H$€å³á£’/c†¾ P‘Š>©°íÿ+nk{ ¸Fyuü‚ ¶x5jô©8úª8fa--µç,àùú®ÖG3õ[p?n¢É1ÁØ­ De™væ4e‰‡AM’îÕXšÛ–Žq™“‹›§”\²rò('Ñ¡1ÀâðZÚ:ºzú ”Ö°•ÔLÀ=Œ[Mf•³£ÓåöػԷ?¡z–)«3o5ß\À ªf€‹JpI)m.[®|T' É ˜gÄÆJ&hÖÜR°ÂY¿D*#K r¦@xf|÷)o.³­ù,aÛ=« ¨P 0B  ˆTP2Ž0!Ó,̘Ëü]”‡R7Môa"_ðØ0Q³Ø¶‘ŸŠ'Ÿ¥#hfb +ÅËNyàc£›4m¶ùhmWÕÙØ;Úñ×;œbg7\$pI~’ €ˆˆˆ€Z@óËkü ™Ý†í¤“CJC%ƒ°çGfJÛg™m»eõy©ágTPŒ(ÂÂ#"T#LÈ43kNß""’$"¹'L3Ð"Ý100#ö¼ò"t¦r拳ìguæ½,@‚ àEžsQŸ8I’J&cÊ–+Õ‰Ó\œ¡ó.22 zFlÌ&hÖÜR°ÂY¿´‡ù„„E% F–Vk]˜M€ À]¸"ùÅ–ŠÎ䎂–oÙÉ&­)šÙ ²ûìÀvØ‘Sêu²°¡aá‘h`_CSOËjSºõê» r…®»1„éìïB_Ī“k§¤r@ ˆ4» ~$4ØÔë pËPUUUUUUU•Ê™3oc9vK(f»6Çø8¹¸y‚CC…ÁÈŠJ• ¬¢ª¦­,¯¡¥­£«§oØtfgA— z´Õ‚¶N`fwæ€lB¨:5è†4­"íPÝÞý`»U IIàæã«ªªš’Ï€6÷L§OS§éñsÞ<÷û£p&ˆ" ‚€ÿªæe‰FD@D`L@ÝOŠWUUUUUUÕ,Á %poí­6ãyÊóü¯¶Êׂ¬¬mÔZmͳ{ÝÙ;Ù`£lL;þЉt¹§g{ézõéSEXbj­Îví’d„B!„B!„|ÿL10žé›æUN ÌʯµMµ¯ºú[»sÚ³ÞTÉÌÌÌÌÌÌÌî9fœ\Üm'†#{´ê¤ªªªªªªªÉîo@@@UPUUUUUUàFĤQ*1­2c–û¬,@È×…ÁÈ·€;UI’$I’ÒÓ@ŶmÛ¶íÔè0 ÍªªªªªªªªÆ²ÞÀ×Zý¼nÒ¾],I’$ "A ’$I’$I’$I’$ € ضmÛ¶m˜ ƒÙ¶mÛ¶mÛ¶mÛ¶m,4â ¾Y’$I¶mÛ¶mÛ¶mÛ¶mÛvó@UUUUUUUUa ªªªªªªªª@  €'±9ÊTNÀ( Ž@6; Z϶mÛ¶M_’$I’$wwwwww÷|ñ @’Íâ€44ÜÝsÞ Œç ϳÿ½Ú*_ ²²¶Qku¶k—jSJu”RJ)¥”1Æ<óŒ1ÆcÌwÎ9çœsÎ9çœ !„B!„B(Š¢(Š¢(Š¢(Š¢Ô<ÿw’ec»ã8@—{z¶—®WŸ>U„…±µVg»ve2)¥”RJ)¥”RJ)}£@UUUUe€uç<žo~¼›Ò9çœsÎ9G’$I’$IRDDDDDDDDDTUUUUUUUUÕÌÌÌÌÌÌÌÌÌH$‘$I’$I’fffffffff&I’$I’$©Ú¹[Ñ=Èk}ØO¦GI%€ð|ÚÆK0G Á™@ 33³ÔÈè§3UUUUUUUUUUUUUUUUÕ05ÀÿejÜMìrvtºÜžÝñ«_Ý’$I’$I’$I’$õ3í€05@”e@úG"@¹ªªªªªªªêÀ£ÿïevþ°mœiÏIzYufLYýð®.Í9 ôÚx,’p)òk¤Iê©F†ºÚ³FüUöØô?.5³,:•J¨YØÔ>À‰{-®JÅ&ZÕ¡º`¢3xJ× Ðz}ý¢§ Xó%»ËèXX™•= E9w/ð5iNˆµ²É\lF¢‰•áyå¨ì1Ô¬2QC—âžP‚‘­Ö+GßÔË4,LÊÙXÅ¥KÄy^»6@ÛAÇýâ‹HhScÓú>€øbgdSƒƒ‹G‘DDÇc Dc°8X°8L­¡I³–Øü|)—ë\YfÂ`H˜¸TGZ¥Å¼Î`QWéâ4j«Î]kÒYtV“õ]Ê:µ¦Ò–X’§ÆdÑT–êKt5»”µS™!Ãu6S‰ÖÛé®I¦ d§}FÖþÃs:Û&þñ¿mÆãÿé…ºg§'¹”NÉImܪÛ=· 1), base_api_url: '/api', plain_text_post: false, prefs: {}, init: function() { // override this in your app.js }, extend: function(obj) { // extend app object with another for (var key in obj) this[key] = obj[key]; }, setAPIBaseURL: function(url) { // set the API base URL (commands are appended to this) this.base_api_url = url; }, setWindowTitle: function(title) { // set the current window title, includes app name document.title = title + ' | ' + this.name; }, showTabBar: function(visible) { // show or hide tab bar if (visible) $('.tab_bar').show(); else $('.tab_bar').hide(); }, updateHeaderInfo: function() { // update top-right display // override this function in app }, getUserAvatarURL: function() { // get URL to user's avatar using Gravatar.com service var size = 0; var email = ''; if (arguments.length == 2) { email = arguments[0]; size = arguments[1]; } else if (arguments.length == 1) { email = this.user.email; size = arguments[0]; } // user may have custom avatar if (this.user && this.user.avatar) { // convert to protocol-less URL return this.user.avatar.replace(/^\w+\:/, ''); } return '//en.gravatar.com/avatar/' + hex_md5( email.toLowerCase() ) + '.jpg?s=' + size + '&d=mm'; }, doMyAccount: function() { // nav to the my account page Nav.go('MyAccount'); }, doUserLogin: function(resp) { // user login, called from login page, or session recover app.username = resp.username; app.user = resp.user; app.setPref('username', resp.username); app.setPref('session_id', resp.session_id); this.updateHeaderInfo(); if (this.isAdmin()) $('#tab_Admin').show(); else $('#tab_Admin').hide(); }, doUserLogout: function(bad_cookie) { // log user out and redirect to login screen if (!bad_cookie) { // user explicitly logging out app.showProgress(1.0, "Logging out..."); app.setPref('username', ''); } app.api.post( 'user/logout', { session_id: app.getPref('session_id') }, function(resp, tx) { app.hideProgress(); delete app.user; delete app.username; delete app.user_info; app.setPref('session_id', ''); $('#d_header_user_container').html( '' ); Debug.trace("User session cookie was deleted, redirecting to login page"); Nav.go('Login'); setTimeout( function() { if (bad_cookie) app.showMessage('error', "Your session has expired. Please log in again."); else app.showMessage('success', "You were logged out successfully."); }, 150 ); $('#tab_Admin').hide(); } ); }, isAdmin: function() { // return true if user is logged in and admin, false otherwise return( app.user && app.user.privileges && app.user.privileges.admin ); }, handleResize: function() { // called when window resizes if (this.page_manager && this.page_manager.current_page_id) { var id = this.page_manager.current_page_id; var page = this.page_manager.find(id); if (page && page.onResize) page.onResize( get_inner_window_size() ); } // also handle sending resize events at a 250ms delay // so some pages can perform a more expensive refresh at a slower interval if (!this.resize_timer) { this.resize_timer = setTimeout( this.handleResizeDelay.bind(this), 250 ); } }, handleResizeDelay: function() { // called 250ms after latest resize event this.resize_timer = null; if (this.page_manager && this.page_manager.current_page_id) { var id = this.page_manager.current_page_id; var page = this.page_manager.find(id); if (page && page.onResizeDelay) page.onResizeDelay( get_inner_window_size() ); } }, handleUnload: function() { // called just before user navs off if (this.page_manager && this.page_manager.current_page_id && $P && $P() && $P().onBeforeUnload) { var result = $P().onBeforeUnload(); if (result) { (e || window.event).returnValue = result; //Gecko + IE return result; // Webkit, Safari, Chrome etc. } } }, doError: function(msg, lifetime) { // show an error message at the top of the screen // and hide the progress dialog if applicable Debug.trace("ERROR: " + msg); this.showMessage( 'error', msg, lifetime ); if (this.progress) this.hideProgress(); return null; }, badField: function(id, msg) { // mark field as bad if (id.match(/^\w+$/)) id = '#' + id; $(id).removeClass('invalid').width(); // trigger reflow to reset css animation $(id).addClass('invalid'); try { $(id).focus(); } catch (e) {;} if (msg) return this.doError(msg); else return false; }, clearError: function(animate) { // clear last error app.hideMessage(animate); $('.invalid').removeClass('invalid'); }, showMessage: function(type, msg, lifetime) { // show success, warning or error message // Dialog.hide(); var icon = ''; msg = escape_text_field_value(msg); // escape any html chars switch (type) { case 'success': icon = 'check-circle'; break; case 'warning': icon = 'exclamation-circle'; break; case 'error': icon = 'exclamation-triangle'; break; } if (icon) { msg = '   ' + msg; } $('#d_message_inner').html( msg ); $('#d_message').hide().removeClass().addClass('message').addClass(type).show(250); if (this.messageTimer) clearTimeout( this.messageTimer ); if ((type == 'success') || lifetime) { if (!lifetime) lifetime = 8; this.messageTimer = setTimeout( function() { app.hideMessage(500); }, lifetime * 1000 ); } }, hideMessage: function(animate) { if (animate) $('#d_message').hide(animate); else $('#d_message').hide(); }, api: { request: function(url, args, callback, errorCallback) { // send AJAX request to server using jQuery var headers = {}; // inject session id into headers, unless app is using plain_text_post if (app.getPref('session_id') && !app.plain_text_post) { headers['X-Session-ID'] = app.getPref('session_id'); } args.context = this; args.url = url; args.dataType = 'text'; // so we can parse the response json ourselves args.timeout = 1000 * 10; // 10 seconds args.headers = headers; $.ajax(args).done( function(text) { // parse JSON and fire callback Debug.trace( 'api', "Received response from server: " + text ); var resp = null; try { resp = JSON.parse(text); } catch (e) { // JSON parse error var desc = "JSON Error: " + e.toString(); if (errorCallback) errorCallback({ code: 500, description: desc }); else app.doError(desc); } // success, but check json for server error code if (resp) { if (('code' in resp) && (resp.code != 0)) { // an error occurred within the JSON response // session errors are handled specially if (resp.code == 'session') app.doUserLogout(true); else if (errorCallback) errorCallback(resp); else app.doError("Error: " + resp.description); } else if (callback) callback(resp); } } ) .fail( function(xhr, status, err) { // XHR or HTTP error var code = xhr.status || 500; var desc = err.toString() || status.toString(); switch (desc) { case 'timeout': desc = "The request timed out. Please try again."; break; case 'error': desc = "An unknown network error occurred. Please try again."; break; } Debug.trace( 'api', "Network Error: " + code + ": " + desc ); if (errorCallback) errorCallback({ code: code, description: desc }); else app.doError( "Network Error: " + code + ": " + desc ); } ); }, post: function(cmd, params, callback, errorCallback) { // send AJAX POST request to server using jQuery var url = cmd; if (!url.match(/^(\w+\:\/\/|\/)/)) url = app.base_api_url + "/" + cmd; if (!params) params = {}; // inject session in into json if submitting as plain text (cors preflight workaround) if (app.getPref('session_id') && app.plain_text_post) { params['session_id'] = app.getPref('session_id'); } var json_raw = JSON.stringify(params); Debug.trace( 'api', "Sending HTTP POST to: " + url + ": " + json_raw ); this.request(url, { type: "POST", data: json_raw, contentType: app.plain_text_post ? 'text/plain' : 'application/json' }, callback, errorCallback); }, get: function(cmd, query, callback, errorCallback) { // send AJAX GET request to server using jQuery var url = cmd; if (!url.match(/^(\w+\:\/\/|\/)/)) url = app.base_api_url + "/" + cmd; if (!query) query = {}; query.cachebust = app.cacheBust; url += compose_query_string(query); Debug.trace( 'api', "Sending HTTP GET to: " + url ); this.request(url, { type: "GET" }, callback, errorCallback); } }, // api getPref: function(key) { // get pref using html5 localStorage if (window.localStorage) return localStorage[key]; else return this.prefs[key]; }, setPref: function(key, value) { if (window.localStorage) localStorage[key] = value; else prefs[key] = value; }, hideProgress: function() { // hide progress dialog Dialog.hide(); delete app.progress; }, showProgress: function(counter, title) { // show or update progress bar if (!$('#d_progress_bar').length) { // no progress dialog is active, so set it up if (!counter) counter = 0; if (counter < 0) counter = 0; if (counter > 1) counter = 1; var cx = Math.floor( counter * 196 ); var html = ''; html += '
'; // html += '
'; // html += '
'; html += '
' + title + '
'; var extra_classes = ''; if (counter == 1.0) extra_classes = 'indeterminate'; html += '
'; html += '
'; html += '
'; // html += '
'; html += '
'; app.hideMessage(); Dialog.show(275, 100, "", html, true); app.progress = { start_counter: counter, counter: counter, counter_max: 1, start_time: hires_time_now(), last_update: hires_time_now(), title: title }; } else if (app.progress) { // dialog is active, so update existing elements var now = hires_time_now(); var cx = Math.floor( counter * 196 ); $('#d_progress_bar').css( 'width', '' + cx + 'px' ); var prog_cont = $('#d_progress_bar_cont'); if ((counter == 1.0) && !prog_cont.hasClass('indeterminate')) prog_cont.addClass('indeterminate'); else if ((counter < 1.0) && prog_cont.hasClass('indeterminate')) prog_cont.removeClass('indeterminate'); if (title) app.progress.title = title; $('#d_progress_title').html( app.progress.title ); app.progress.last_update = now; app.progress.counter = counter; } }, showDialog: function(title, inner_html, buttons_html) { // show dialog using our own look & feel var html = ''; html += '
' + title + '
'; html += '
' + inner_html + '
'; html += '
' + buttons_html + '
'; Dialog.showAuto( "", html ); }, hideDialog: function() { Dialog.hide(); }, confirm: function(title, html, ok_btn_label, callback) { // show simple OK / Cancel dialog with custom text // fires callback with true (OK) or false (Cancel) if (!ok_btn_label) ok_btn_label = "OK"; this.confirm_callback = callback; var inner_html = ""; inner_html += '
'+html+'
'; var buttons_html = ""; buttons_html += '
'; buttons_html += ''; buttons_html += ''; buttons_html += ''; buttons_html += '
Cancel
 
'+ok_btn_label+'
'; this.showDialog( title, inner_html, buttons_html ); // special mode for key capture Dialog.active = 'confirmation'; }, confirm_click: function(result) { // user clicked OK or Cancel in confirmation dialog, fire callback // caller MUST deal with Dialog.hide() if result is true if (this.confirm_callback) { this.confirm_callback(result); if (!result) Dialog.hide(); } }, confirm_key: function(event) { // handle keydown with active confirmation dialog if (Dialog.active !== 'confirmation') return; if ((event.keyCode != 13) && (event.keyCode != 27)) return; // skip enter check if textarea is active if (document.activeElement && (event.keyCode == 13)) { if ($(document.activeElement).prop('type') == 'textarea') return; } event.stopPropagation(); event.preventDefault(); if (event.keyCode == 13) this.confirm_click(true); else if (event.keyCode == 27) this.confirm_click(false); }, get_base_url: function() { return app.proto + location.hostname + '/'; }, setTheme: function(theme) { // toggle light/dark theme if (theme == 'dark') { $('body').addClass('dark'); $('#d_theme_ctrl').html( ' Dark' ); this.setPref('theme', 'dark'); } else { $('body').removeClass('dark'); $('#d_theme_ctrl').html( ' Light' ); this.setPref('theme', 'light'); } if (this.onThemeChange) this.onThemeChange(theme); }, initTheme: function() { // set theme to user's preference if (!this.getPref('theme')) { // brand new user: try to guess theme using media query if (window.matchMedia('(prefers-color-scheme: dark)').matches) { this.setPref('theme', 'dark'); } } this.setTheme( this.getPref('theme') || 'light' ); }, toggleTheme: function() { // toggle light/dark theme if (this.getPref('theme') == 'dark') this.setTheme('light'); else this.setTheme('dark'); } }; // app object function get_form_table_row() { // Get HTML for formatted form table row (label and content). var tr_class = ''; var left = ''; var right = ''; if (arguments.length == 3) { tr_class = arguments[0]; left = arguments[1]; right = arguments[2]; } else { left = arguments[0]; right = arguments[1]; } left = left.replace(/\s/g, ' ').replace(/\:$/, ''); if (left) left += ':'; else left = ' '; var html = ''; html += ''; html += ''+left+''; html += ''; html += '
'+right+'
'; html += ''; html += ''; return html; }; function get_form_table_caption() { // Get HTML for form table caption (takes up a row). var tr_class = ''; var cap = ''; if (arguments.length == 2) { tr_class = arguments[0]; cap = arguments[1]; } else { cap = arguments[0]; } var html = ''; html += ''; html += ' '; html += ''; html += '
'+cap+'
'; html += ''; html += ''; return html; }; function get_form_table_spacer() { // Get HTML for form table spacer (takes up a row). var tr_class = ''; var extra_classes = ''; if (arguments.length == 2) { tr_class = arguments[0]; extra_classes = arguments[1]; } else { extra_classes = arguments[0]; } var html = ''; html += '
'; return html; }; function $P(id) { // shortcut for page_manager.find(), also defaults to current page if (!id) id = app.page_manager.current_page_id; var page = app.page_manager.find(id); assert( !!page, "Failed to locate page: " + id ); return page; }; var Debug = { backlog: [], dump: function() { // dump backlog to console for (var idx = 0, len = this.backlog.length; idx < len; idx++) { console.log( this.backlog[idx] ); } }, trace: function(cat, msg) { // trace one line to console, or store in backlog if (arguments.length == 1) { msg = cat; cat = 'debug'; } if (window.console && console.log && window.config && config.debug) { console.log( cat + ': ' + msg ); } else { this.backlog.push( hires_time_now() + ': ' + cat + ': ' + msg ); if (this.backlog.length > 100) this.backlog.shift(); } } }; $(document).ready(function() { app.init(); }); window.addEventListener( "keydown", function(event) { app.confirm_key(event); }, false ); window.addEventListener( "resize", function() { app.handleResize(); }, false ); window.addEventListener("beforeunload", function (e) { return app.handleUnload(); }, false ); pixl-webapp-2.0.3/js/datetime.js000066400000000000000000000134571504641265100165300ustar00rootroot00000000000000// Joe's Date/Time Tools // Copyright (c) 2004 - 2015 Joseph Huckaby // Released under the MIT License var _months = [ [ 1, 'January' ], [ 2, 'February' ], [ 3, 'March' ], [ 4, 'April' ], [ 5, 'May' ], [ 6, 'June' ], [ 7, 'July' ], [ 8, 'August' ], [ 9, 'September' ], [ 10, 'October' ], [ 11, 'November' ], [ 12, 'December' ] ]; var _days = [ [1,1], [2,2], [3,3], [4,4], [5,5], [6,6], [7,7], [8,8], [9,9], [10,10], [11,11], [12,12], [13,13], [14,14], [15,15], [16,16], [17,17], [18,18], [19,19], [20,20], [21,21], [22,22], [23,23], [24,24], [25,25], [26,26], [27,27], [28,28], [29,29], [30,30], [31,31] ]; var _short_month_names = [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'June', 'July', 'Aug', 'Sept', 'Oct', 'Nov', 'Dec' ]; var _day_names = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; var _short_day_names = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; var _number_suffixes = ['th', 'st', 'nd', 'rd', 'th', 'th', 'th', 'th', 'th', 'th']; var _hour_names = ['12am', '1am', '2am', '3am', '4am', '5am', '6am', '7am', '8am', '9am', '10am', '11am', '12pm', '1pm', '2pm', '3pm', '4pm', '5pm', '6pm', '7pm', '8pm', '9pm', '10pm', '11pm']; function time_now() { // return the Epoch seconds for like right now var now = new Date(); return Math.floor( now.getTime() / 1000 ); } function hires_time_now() { // return the Epoch seconds for like right now var now = new Date(); return ( now.getTime() / 1000 ); } function format_date(thingy, template) { // format date using get_date_args // e.g. '[yyyy]/[mm]/[dd]' or '[dddd], [mmmm] [mday], [yyyy]' or '[hour12]:[mi] [ampm]' if (!thingy) return false; var dargs = thingy.yyyy_mm_dd ? thingy : get_date_args(thingy); return template.replace(/\[(\w+)\]/g, function(m_all, m_g1) { return (m_g1 in dargs) ? dargs[m_g1] : ''; }); } function get_date_args(thingy) { // return hash containing year, mon, mday, hour, min, sec // given epoch seconds var date = (typeof(thingy) == 'object') ? thingy : (new Date( (typeof(thingy) == 'number') ? (thingy * 1000) : thingy )); var args = { epoch: Math.floor( date.getTime() / 1000 ), year: date.getFullYear(), mon: date.getMonth() + 1, mday: date.getDate(), hour: date.getHours(), min: date.getMinutes(), sec: date.getSeconds(), msec: date.getMilliseconds(), wday: date.getDay(), offset: 0 - (date.getTimezoneOffset() / 60) }; args.yyyy = '' + args.year; if (args.mon < 10) args.mm = "0" + args.mon; else args.mm = '' + args.mon; if (args.mday < 10) args.dd = "0" + args.mday; else args.dd = '' + args.mday; if (args.hour < 10) args.hh = "0" + args.hour; else args.hh = '' + args.hour; if (args.min < 10) args.mi = "0" + args.min; else args.mi = '' + args.min; if (args.sec < 10) args.ss = "0" + args.sec; else args.ss = '' + args.sec; if (args.hour >= 12) { args.ampm = 'pm'; args.hour12 = args.hour - 12; if (!args.hour12) args.hour12 = 12; } else { args.ampm = 'am'; args.hour12 = args.hour; if (!args.hour12) args.hour12 = 12; } args.AMPM = args.ampm.toUpperCase(); args.yyyy_mm_dd = args.yyyy + '/' + args.mm + '/' + args.dd; args.hh_mi_ss = args.hh + ':' + args.mi + ':' + args.ss; args.tz = 'GMT' + (args.offset > 0 ? '+' : '') + args.offset; // add formatted month and weekdays args.mmm = _short_month_names[ args.mon - 1 ]; args.mmmm = _months[ args.mon - 1] ? _months[ args.mon - 1][1] : ''; args.ddd = _short_day_names[ args.wday ]; args.dddd = _day_names[ args.wday ]; return args; } function get_time_from_args(args) { // return epoch given args like those returned from get_date_args() var then = new Date( args.year, args.mon - 1, args.mday, args.hour, args.min, args.sec, 0 ); return parseInt( then.getTime() / 1000, 10 ); } function yyyy(epoch) { // return current year (or epoch) in YYYY format if (!epoch) epoch = time_now(); var args = get_date_args(epoch); return args.year; } function yyyy_mm_dd(epoch, ch) { // return current date (or custom epoch) in YYYY/MM/DD format if (!epoch) epoch = time_now(); if (!ch) ch = '/'; var args = get_date_args(epoch); return args.yyyy + ch + args.mm + ch + args.dd; } function mm_dd_yyyy(epoch, ch) { // return current date (or custom epoch) in YYYY/MM/DD format if (!epoch) epoch = time_now(); if (!ch) ch = '/'; var args = get_date_args(epoch); return args.mm + ch + args.dd + ch + args.yyyy; } function normalize_time(epoch, zero_args) { // quantize time into any given precision // example hourly: { min:0, sec:0 } // daily: { hour:0, min:0, sec:0 } var args = get_date_args(epoch); for (key in zero_args) args[key] = zero_args[key]; // mday is 1-based if (!args['mday']) args['mday'] = 1; return get_time_from_args(args); } function get_nice_date(epoch, abbrev) { var dargs = get_date_args(epoch); var month = window._months[dargs.mon - 1][1]; if (abbrev) month = month.substring(0, 3); return month + ' ' + dargs.mday + ', ' + dargs.year; } function get_nice_time(epoch, secs) { // return time in HH12:MM format var dargs = get_date_args(epoch); if (dargs.min < 10) dargs.min = '0' + dargs.min; if (dargs.sec < 10) dargs.sec = '0' + dargs.sec; var output = dargs.hour12 + ':' + dargs.min; if (secs) output += ':' + dargs.sec; output += ' ' + dargs.ampm.toUpperCase(); return output; } function get_nice_date_time(epoch, secs, abbrev_date) { return get_nice_date(epoch, abbrev_date) + ' ' + get_nice_time(epoch, secs); } function get_short_date_time(epoch) { return get_nice_date(epoch, true) + ' ' + get_nice_time(epoch, false); } function parse_date(str) { // parse date into epoch return Math.floor( ((new Date(str)).getTime() / 1000) ); }; function check_valid_date(str) { // return true if a date is valid, false otherwise // returns false for Jan 1, 1970 00:00:00 GMT var epoch = 0; try { epoch = parse_date(str); } catch (e) { epoch = 0; } return (epoch >= 86400); }; pixl-webapp-2.0.3/js/dialog.js000066400000000000000000000064171504641265100161710ustar00rootroot00000000000000// Dialog Tools // Author: Joseph Huckaby // Released under the MIT License. var Dialog = { active: false, clickBlock: false, showAuto: function(title, inner_html, click_block) { // measure size of HTML to create correctly positioned dialog var temp = $('
').css({ position: 'absolute', visibility: 'hidden' }).html(inner_html).appendTo('body'); var width = temp.width(); var height = temp.height(); temp.remove(); this.show( width, height, title, inner_html, click_block ); }, autoResize: function() { // automatically resize dialog to match changed content size var temp = $('
').css({ position: 'absolute', visibility: 'hidden' }).html( $('#dialog_main').html() ).appendTo('body'); var width = temp.width(); var height = temp.height(); temp.remove(); var size = get_inner_window_size(); var x = Math.floor( (size.width / 2) - ((width + 0) / 2) ); var y = Math.floor( ((size.height / 2) - (height / 2)) * 0.75 ); $('#dialog_main').css({ width: '' + width + 'px', height: '' + height + 'px' }); $('#dialog_container').css({ left: '' + x + 'px', top: '' + y + 'px' }); }, show: function(width, height, title, inner_html, click_block) { // show dialog this.clickBlock = click_block || false; var body = document.getElementsByTagName('body')[0]; // build html for dialog var html = ''; if (title) { html += '
'; html += '
'+title+'
'; html += '
'; } html += '
'; html += inner_html; html += '
'; var size = get_inner_window_size(); var x = Math.floor( (size.width / 2) - ((width + 0) / 2) ); var y = Math.floor( ((size.height / 2) - (height / 2)) * 0.75 ); if ($('#dialog_overlay').length) { $('#dialog_overlay').stop().remove(); } var overlay = document.createElement('div'); overlay.id = 'dialog_overlay'; overlay.style.opacity = 0; body.appendChild(overlay); $(overlay).fadeTo( 500, 0.75 ).click(function() { if (!Dialog.clickBlock) Dialog.hide(); }); if ($('#dialog_container').length) { $('#dialog_container').stop().remove(); } var container = document.createElement('div'); container.id = 'dialog_container'; container.style.opacity = 0; container.style.left = '' + x + 'px'; container.style.top = '' + y + 'px'; container.innerHTML = html; body.appendChild(container); $(container).fadeTo( 250, 1.0 ); this.active = true; }, hide: function() { // hide dialog if (this.active) { $('#dialog_container').stop().fadeOut( 250, function() { $(this).remove(); } ); $('#dialog_overlay').stop().fadeOut( 500, function() { $(this).remove(); } ); this.active = false; } }, showProgress: function(msg) { // show simple progress dialog (unspecified duration) var html = ''; html += '
'; html += '

'; html += '' + msg + ''; html += '
'; this.show( 300, 120, '', html ); } }; pixl-webapp-2.0.3/js/md5.js000077500000000000000000000272161504641265100154220ustar00rootroot00000000000000/* * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message * Digest Algorithm, as defined in RFC 1321. * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet * Distributed under the BSD License * See http://pajhome.org.uk/crypt/md5 for more info. */ /* * Configurable variables. You may need to tweak these to be compatible with * the server-side, but the defaults work in most cases. */ var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */ var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */ /* * These are the functions you'll usually want to call * They take string arguments and return either hex or base-64 encoded strings */ function hex_md5(s) { return rstr2hex(rstr_md5(str2rstr_utf8(s))); } function b64_md5(s) { return rstr2b64(rstr_md5(str2rstr_utf8(s))); } function any_md5(s, e) { return rstr2any(rstr_md5(str2rstr_utf8(s)), e); } function hex_hmac_md5(k, d) { return rstr2hex(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d))); } function b64_hmac_md5(k, d) { return rstr2b64(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d))); } function any_hmac_md5(k, d, e) { return rstr2any(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d)), e); } /* * Perform a simple self-test to see if the VM is working */ function md5_vm_test() { return hex_md5("abc").toLowerCase() == "900150983cd24fb0d6963f7d28e17f72"; } /* * Calculate the MD5 of a raw string */ function rstr_md5(s) { return binl2rstr(binl_md5(rstr2binl(s), s.length * 8)); } /* * Calculate the HMAC-MD5, of a key and some data (raw strings) */ function rstr_hmac_md5(key, data) { var bkey = rstr2binl(key); if(bkey.length > 16) bkey = binl_md5(bkey, key.length * 8); var ipad = Array(16), opad = Array(16); for(var i = 0; i < 16; i++) { ipad[i] = bkey[i] ^ 0x36363636; opad[i] = bkey[i] ^ 0x5C5C5C5C; } var hash = binl_md5(ipad.concat(rstr2binl(data)), 512 + data.length * 8); return binl2rstr(binl_md5(opad.concat(hash), 512 + 128)); } /* * Convert a raw string to a hex string */ function rstr2hex(input) { try { hexcase } catch(e) { hexcase=0; } var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef"; var output = ""; var x; for(var i = 0; i < input.length; i++) { x = input.charCodeAt(i); output += hex_tab.charAt((x >>> 4) & 0x0F) + hex_tab.charAt( x & 0x0F); } return output; } /* * Convert a raw string to a base-64 string */ function rstr2b64(input) { try { b64pad } catch(e) { b64pad=''; } var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; var output = ""; var len = input.length; for(var i = 0; i < len; i += 3) { var triplet = (input.charCodeAt(i) << 16) | (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0) | (i + 2 < len ? input.charCodeAt(i+2) : 0); for(var j = 0; j < 4; j++) { if(i * 8 + j * 6 > input.length * 8) output += b64pad; else output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F); } } return output; } /* * Convert a raw string to an arbitrary string encoding */ function rstr2any(input, encoding) { var divisor = encoding.length; var i, j, q, x, quotient; /* Convert to an array of 16-bit big-endian values, forming the dividend */ var dividend = Array(Math.ceil(input.length / 2)); for(i = 0; i < dividend.length; i++) { dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1); } /* * Repeatedly perform a long division. The binary array forms the dividend, * the length of the encoding is the divisor. Once computed, the quotient * forms the dividend for the next step. All remainders are stored for later * use. */ var full_length = Math.ceil(input.length * 8 / (Math.log(encoding.length) / Math.log(2))); var remainders = Array(full_length); for(j = 0; j < full_length; j++) { quotient = Array(); x = 0; for(i = 0; i < dividend.length; i++) { x = (x << 16) + dividend[i]; q = Math.floor(x / divisor); x -= q * divisor; if(quotient.length > 0 || q > 0) quotient[quotient.length] = q; } remainders[j] = x; dividend = quotient; } /* Convert the remainders to the output string */ var output = ""; for(i = remainders.length - 1; i >= 0; i--) output += encoding.charAt(remainders[i]); return output; } /* * Encode a string as utf-8. * For efficiency, this assumes the input is valid utf-16. */ function str2rstr_utf8(input) { var output = ""; var i = -1; var x, y; while(++i < input.length) { /* Decode utf-16 surrogate pairs */ x = input.charCodeAt(i); y = i + 1 < input.length ? input.charCodeAt(i + 1) : 0; if(0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF) { x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF); i++; } /* Encode output as utf-8 */ if(x <= 0x7F) output += String.fromCharCode(x); else if(x <= 0x7FF) output += String.fromCharCode(0xC0 | ((x >>> 6 ) & 0x1F), 0x80 | ( x & 0x3F)); else if(x <= 0xFFFF) output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F), 0x80 | ((x >>> 6 ) & 0x3F), 0x80 | ( x & 0x3F)); else if(x <= 0x1FFFFF) output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07), 0x80 | ((x >>> 12) & 0x3F), 0x80 | ((x >>> 6 ) & 0x3F), 0x80 | ( x & 0x3F)); } return output; } /* * Encode a string as utf-16 */ function str2rstr_utf16le(input) { var output = ""; for(var i = 0; i < input.length; i++) output += String.fromCharCode( input.charCodeAt(i) & 0xFF, (input.charCodeAt(i) >>> 8) & 0xFF); return output; } function str2rstr_utf16be(input) { var output = ""; for(var i = 0; i < input.length; i++) output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF, input.charCodeAt(i) & 0xFF); return output; } /* * Convert a raw string to an array of little-endian words * Characters >255 have their high-byte silently ignored. */ function rstr2binl(input) { var output = Array(input.length >> 2); for(var i = 0; i < output.length; i++) output[i] = 0; for(var i = 0; i < input.length * 8; i += 8) output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (i%32); return output; } /* * Convert an array of little-endian words to a string */ function binl2rstr(input) { var output = ""; for(var i = 0; i < input.length * 32; i += 8) output += String.fromCharCode((input[i>>5] >>> (i % 32)) & 0xFF); return output; } /* * Calculate the MD5 of an array of little-endian words, and a bit length. */ function binl_md5(x, len) { /* append padding */ x[len >> 5] |= 0x80 << ((len) % 32); x[(((len + 64) >>> 9) << 4) + 14] = len; var a = 1732584193; var b = -271733879; var c = -1732584194; var d = 271733878; for(var i = 0; i < x.length; i += 16) { var olda = a; var oldb = b; var oldc = c; var oldd = d; a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936); d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586); c = md5_ff(c, d, a, b, x[i+ 2], 17, 606105819); b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330); a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897); d = md5_ff(d, a, b, c, x[i+ 5], 12, 1200080426); c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341); b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983); a = md5_ff(a, b, c, d, x[i+ 8], 7 , 1770035416); d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417); c = md5_ff(c, d, a, b, x[i+10], 17, -42063); b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162); a = md5_ff(a, b, c, d, x[i+12], 7 , 1804603682); d = md5_ff(d, a, b, c, x[i+13], 12, -40341101); c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290); b = md5_ff(b, c, d, a, x[i+15], 22, 1236535329); a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510); d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632); c = md5_gg(c, d, a, b, x[i+11], 14, 643717713); b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302); a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691); d = md5_gg(d, a, b, c, x[i+10], 9 , 38016083); c = md5_gg(c, d, a, b, x[i+15], 14, -660478335); b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848); a = md5_gg(a, b, c, d, x[i+ 9], 5 , 568446438); d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690); c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961); b = md5_gg(b, c, d, a, x[i+ 8], 20, 1163531501); a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467); d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784); c = md5_gg(c, d, a, b, x[i+ 7], 14, 1735328473); b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734); a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558); d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463); c = md5_hh(c, d, a, b, x[i+11], 16, 1839030562); b = md5_hh(b, c, d, a, x[i+14], 23, -35309556); a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060); d = md5_hh(d, a, b, c, x[i+ 4], 11, 1272893353); c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632); b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640); a = md5_hh(a, b, c, d, x[i+13], 4 , 681279174); d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222); c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979); b = md5_hh(b, c, d, a, x[i+ 6], 23, 76029189); a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487); d = md5_hh(d, a, b, c, x[i+12], 11, -421815835); c = md5_hh(c, d, a, b, x[i+15], 16, 530742520); b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651); a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844); d = md5_ii(d, a, b, c, x[i+ 7], 10, 1126891415); c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905); b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055); a = md5_ii(a, b, c, d, x[i+12], 6 , 1700485571); d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606); c = md5_ii(c, d, a, b, x[i+10], 15, -1051523); b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799); a = md5_ii(a, b, c, d, x[i+ 8], 6 , 1873313359); d = md5_ii(d, a, b, c, x[i+15], 10, -30611744); c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380); b = md5_ii(b, c, d, a, x[i+13], 21, 1309151649); a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070); d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379); c = md5_ii(c, d, a, b, x[i+ 2], 15, 718787259); b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551); a = safe_add(a, olda); b = safe_add(b, oldb); c = safe_add(c, oldc); d = safe_add(d, oldd); } return Array(a, b, c, d); } /* * These functions implement the four basic operations the algorithm uses. */ function md5_cmn(q, a, b, x, s, t) { return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b); } function md5_ff(a, b, c, d, x, s, t) { return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t); } function md5_gg(a, b, c, d, x, s, t) { return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t); } function md5_hh(a, b, c, d, x, s, t) { return md5_cmn(b ^ c ^ d, a, b, x, s, t); } function md5_ii(a, b, c, d, x, s, t) { return md5_cmn(c ^ (b | (~d)), a, b, x, s, t); } /* * Add integers, wrapping at 2^32. This uses 16-bit operations internally * to work around bugs in some JS interpreters. */ function safe_add(x, y) { var lsw = (x & 0xFFFF) + (y & 0xFFFF); var msw = (x >> 16) + (y >> 16) + (lsw >> 16); return (msw << 16) | (lsw & 0xFFFF); } /* * Bitwise rotate a 32-bit number to the left. */ function bit_rol(num, cnt) { return (num << cnt) | (num >>> (32 - cnt)); } pixl-webapp-2.0.3/js/oop.js000066400000000000000000000061611504641265100155230ustar00rootroot00000000000000/** * JavaScript Object Oriented Programming Framework * Author: Joseph Huckaby **/ var Namespace = { // simple namespace support for classes create: function(path, container) { // create namespace for class if (!container) container = window; while (path.match(/^(\w+)\.?/)) { var key = RegExp.$1; path = path.replace(/^(\w+)\.?/, ""); if (!container[key]) container[key] = {}; container = container[key]; } return container; }, prep: function(name, container) { // prep namespace for new class if (!container) container = window; if (name.match(/^(.+)\.(\w+)$/)) { var path = RegExp.$1; name = RegExp.$2; container = Namespace.create(path, container); } return { container: container, name: name }; } }; var Class = { // simple class factory create: function(name, members) { // generate new class with optional namespace assert(name, "Must pass name to Class.create"); if (!members) members = {}; members.__name = name; members.__parent = null; var ns = Namespace.prep(name); var container = ns.container; name = ns.name; if (!members.__construct) members.__construct = function() {}; container[name] = members.__construct; var static_members = members.__static; if (static_members) { for (var key in static_members) { container[name][key] = static_members[key]; } } container[name].prototype = members; }, subclass: function(parent, name, members) { // subclass an existing class assert(parent, "Must pass parent class to Class.subclass"); assert(name, "Must pass name to Class.subclass"); if (!members) members = {}; members.__name = name; members.__parent = parent.prototype; var ns = Namespace.prep(name); var container = ns.container; var subname = ns.name; if (members.__construct) { // explicit subclass constructor container[subname] = members.__construct; } else { // inherit parent's constructor var code = parent.toString(); var args = code.substring( code.indexOf("(")+1, code.indexOf(")") ); var inner_code = code.substring( code.indexOf("{")+1, code.lastIndexOf("}") ); eval('members.__construct = container[subname] = function ('+args+') {'+inner_code+'};'); } var static_members = members.__static; if (static_members) { for (var key in static_members) { container[subname][key] = static_members[key]; } } container[subname].prototype = {}; for (var key in parent.prototype) container[subname].prototype[key] = parent.prototype[key]; for (var key in members) container[subname].prototype[key] = members[key]; }, add: function(obj, members) { // add members to an existing class for (var key in members) obj.prototype[key] = members[key]; }, require: function() { // make sure classes are loaded for (var idx = 0, len = arguments.length; idx < len; idx++) { assert( !!eval('window.' + arguments[idx]) ); } return true; } }; Class.extend = Class.subclass; Class.set = Class.add; if (!window.assert) window.assert = function(fact, msg) { // very simple assert if (!fact) { console.log("ASSERT FAILURE: " + msg); return alert("ASSERT FAILED! " + msg); } return fact; } pixl-webapp-2.0.3/js/page.js000066400000000000000000000342311504641265100156410ustar00rootroot00000000000000/** * WebApp 1.0 Page Manager * Author: Joseph Huckaby * Copyright (c) 2010 Joseph Huckaby * Released under the MIT License. **/ var Nav = { /** * Virtual Page Navigation System **/ loc: '', old_loc: '', inited: false, nodes: [], init: function() { // initialize nav system assert( window.config, "window.config not present."); if (!this.inited) { this.inited = true; this.loc = 'init'; this.monitor(); if (window.addEventListener) { window.addEventListener("hashchange", function(event) { Nav.monitor(); }, false); } else { window.onhashchange = function() { Nav.monitor(); }; } } }, monitor: function() { // monitor browser location and activate handlers as needed var parts = window.location.href.split(/\#/); var anchor = parts[1]; if (!anchor) anchor = config.DefaultPage || 'Main'; var full_anchor = '' + anchor; var sub_anchor = ''; anchor = anchor.replace(/\%7C/, '|'); if (anchor.match(/\|(\w+)$/)) { // inline section anchor after article name, pipe delimited sub_anchor = RegExp.$1.toLowerCase(); anchor = anchor.replace(/\|(\w+)$/, ''); } if ((anchor != this.loc) && !anchor.match(/^_/)) { // ignore doxter anchors Debug.trace('nav', "Caught navigation anchor: " + full_anchor); var page_name = ''; var page_args = {}; if (full_anchor.match(/^\w+\?.+/)) { parts = full_anchor.split(/\?/); page_name = parts[0]; page_args = parse_query_string( parts[1] ); } else { parts = full_anchor.split(/\//); page_name = parts[0]; page_args = {}; } Debug.trace('nav', "Calling page: " + page_name + ": " + JSON.stringify(page_args)); Dialog.hide(); // app.hideMessage(); var result = app.page_manager.click( page_name, page_args ); if (result) { this.old_loc = this.loc; if (this.old_loc == 'init') this.old_loc = config.DefaultPage || 'Main'; this.loc = anchor; } else { // current page aborted navigation -- recover current page without refresh this.go( this.loc ); } } else if (sub_anchor != this.sub_anchor) { Debug.trace('nav', "Caught sub-anchor: " + sub_anchor); $P().gosub( sub_anchor ); } // sub-anchor changed this.sub_anchor = sub_anchor; }, go: function(anchor, force) { // navigate to page anchor = anchor.replace(/^\#/, ''); if (force) { if (anchor == this.loc) { this.loc = 'init'; this.monitor(); } else { this.loc = 'init'; window.location.href = '#' + anchor; } } else { window.location.href = '#' + anchor; } }, prev: function() { // return to previous page this.go( this.old_loc || config.DefaultPage || 'Main' ); }, refresh: function() { // re-nav to current page this.loc = 'refresh'; this.monitor(); }, currentAnchor: function() { // return current page anchor var parts = window.location.href.split(/\#/); var anchor = parts[1] || ''; var sub_anchor = ''; anchor = anchor.replace(/\%7C/, '|'); if (anchor.match(/\|(\w+)$/)) { // inline section anchor after article name, pipe delimited sub_anchor = RegExp.$1.toLowerCase(); anchor = anchor.replace(/\|(\w+)$/, ''); } return anchor; } }; // Nav // // Page Base Class // Class.create( 'Page', { // 'Page' class is the abstract base class for all pages // Each web component calls this class daddy // member variables ID: '', // ID of DIV for component data: null, // holds all data for freezing active: false, // whether page is active or not sidebar: true, // whether to show sidebar or not // methods __construct: function(config, div) { if (!config) return; // class constructor, import config into self this.data = {}; if (!config) config = {}; for (var key in config) this[key] = config[key]; this.div = div || $('#page_' + this.ID); assert(this.div, "Cannot find page div: page_" + this.ID); this.tab = $('#tab_' + this.ID); }, onInit: function() { // called with the page is initialized }, onActivate: function() { // called when page is activated return true; }, onDeactivate: function() { // called when page is deactivated return true; }, show: function() { // show page this.div.show(); }, hide: function() { this.div.hide(); }, gosub: function(anchor) { // go to sub-anchor (article section link) }, getSidebarTabs: function(current, tabs) { // get html for sidebar tabs var html = ''; html += '
'; html += '
'; html += '
'; for (var idx = 0, len = tabs.length; idx < len; idx++) { var tab = tabs[idx]; if (typeof(tab) == 'string') html += tab; else { var class_name = 'inactive'; var link = 'Nav.go(\''+this.ID+'?sub='+tab[0]+'\')'; if (tab[0] == current) { class_name = 'active'; link = ''; } html += '
'+tab[1]+'
'; } } html += '
'; return html; }, getPaginatedTable: function(resp, cols, data_type, callback) { // get html for paginated table // dual-calling convention: (resp, cols, data_type, callback) or (args) var args = null; if (arguments.length == 1) { // custom args calling convention args = arguments[0]; // V2 API if (!args.resp && args.rows && args.total) { args.resp = { rows: args.rows, list: { length: args.total } }; } } else { // classic calling convention args = { resp: arguments[0], cols: arguments[1], data_type: arguments[2], callback: arguments[3], limit: this.args.limit, offset: this.args.offset || 0 }; } var resp = args.resp; var cols = args.cols; var data_type = args.data_type; var callback = args.callback; var cpl = args.pagination_link || ''; var html = ''; // pagination header html += ''; html += '
'; html += ''; html += ''; for (var idx = 0, len = resp.rows.length; idx < len; idx++) { var row = resp.rows[idx]; var tds = callback(row, idx); if (tds) { html += ''; html += ''; html += ''; } } // foreach row if (!resp.rows.length) { html += ''; } html += '
' + cols.join('').replace(/\s+/g, ' ') + '
' + tds.join('') + '
'; html += 'No '+pluralize(data_type)+' found.'; html += '
'; html += '
'; return html; }, getBasicTable: function(rows, cols, data_type, callback) { // get html for sorted table (fake pagination, for looks only) var html = ''; // pagination html += ''; html += '
'; html += ''; html += ''; for (var idx = 0, len = rows.length; idx < len; idx++) { var row = rows[idx]; var tds = callback(row, idx); if (tds.insertAbove) html += tds.insertAbove; html += ''; html += ''; html += ''; } // foreach row if (!rows.length) { html += ''; } html += '
' + cols.join('') + '
' + tds.join('') + '
'; html += 'No '+pluralize(data_type)+' found.'; html += '
'; html += '
'; return html; } } ); // class Page // // Page Manager // Class.create( 'PageManager', { // 'PageManager' class handles all virtual pages in the application // member variables pages: null, // array of pages current_page_id: '', // current page ID // methods __construct: function(page_list) { // class constructor, create all pages // page_list should be array of components from master config // each one should have at least a 'ID' parameter // anything else is copied into object verbatim this.pages = []; this.page_list = page_list; for (var idx = 0, len = page_list.length; idx < len; idx++) { Debug.trace( 'page', "Initializing page: " + page_list[idx].ID ); assert(Page[ page_list[idx].ID ], "Page class not found: Page." + page_list[idx].ID); var page = new Page[ page_list[idx].ID ]( page_list[idx] ); page.args = {}; page.onInit(); this.pages.push(page); $('#tab_'+page.ID).click( function(event) { // console.log( this ); // app.page_manager.click( this._page_id ); Nav.go( this._page_id ); } )[0]._page_id = page.ID; } }, find: function(id) { // locate page by ID (i.e. Plugin Name) var page = find_object( this.pages, { ID: id } ); if (!page) Debug.trace('PageManager', "Could not find page: " + id); return page; }, activate: function(id, old_id, args) { // send activate event to page by id (i.e. Plugin Name) $('#page_'+id).show(); $('#tab_'+id).removeClass('inactive').addClass('active'); var page = this.find(id); page.active = true; if (!args) args = {}; // if we are navigating here from a different page, AND the new sub mismatches the old sub, clear the page html var new_sub = args.sub || ''; if (old_id && (id != old_id) && (typeof(page._old_sub) != 'undefined') && (new_sub != page._old_sub) && page.div) { page.div.html(''); } var result = page.onActivate.apply(page, [args]); if (typeof(result) == 'boolean') return result; else throw("Page " + id + " onActivate did not return a boolean!"); }, deactivate: function(id, new_id) { // send deactivate event to page by id (i.e. Plugin Name) var page = this.find(id); var result = page.onDeactivate(new_id); if (result) { $('#page_'+id).hide(); $('#tab_'+id).removeClass('active').addClass('inactive'); // $('#d_message').hide(); page.active = false; // if page has args.sub, save it for clearing html on reactivate, if page AND sub are different if (page.args) page._old_sub = page.args.sub || ''; } return result; }, click: function(id, args) { // exit current page and enter specified page Debug.trace('page', "Switching pages to: " + id); var old_id = this.current_page_id; if (this.current_page_id) { var result = this.deactivate( this.current_page_id, id ); if (!result) return false; // current page said no } this.current_page_id = id; this.old_page_id = old_id; window.scrollTo( 0, 0 ); var result = this.activate(id, old_id, args); if (!result) { // new page has rejected activation, probably because a login is required // un-hide previous page div, but don't call activate on it $('#page_'+id).hide(); this.current_page_id = ''; // if (old_id) { // $('page_'+old_id).show(); // this.current_page_id = old_id; // } } return true; } } ); // class PageManager pixl-webapp-2.0.3/js/tools.js000066400000000000000000000353211504641265100160660ustar00rootroot00000000000000//// // Joe's Misc JavaScript Tools // Copyright (c) 2004 - 2015 Joseph Huckaby // Released under the MIT License //// var months = [ [ 1, 'January' ], [ 2, 'February' ], [ 3, 'March' ], [ 4, 'April' ], [ 5, 'May' ], [ 6, 'June' ], [ 7, 'July' ], [ 8, 'August' ], [ 9, 'September' ], [ 10, 'October' ], [ 11, 'November' ], [ 12, 'December' ] ]; function parse_query_string(url) { // parse query string into key/value pairs and return as object var query = {}; url.replace(/^.*\?/, '').replace(/([^\=]+)\=([^\&]*)\&?/g, function(match, key, value) { query[key] = decodeURIComponent(value); if (query[key].match(/^\-?\d+$/)) query[key] = parseInt(query[key]); else if (query[key].match(/^\-?\d*\.\d+$/)) query[key] = parseFloat(query[key]); return ''; } ); return query; }; function compose_query_string(queryObj) { // compose key/value pairs into query string // supports duplicate keys (i.e. arrays) var qs = ''; for (var key in queryObj) { var values = always_array(queryObj[key]); for (var idx = 0, len = values.length; idx < len; idx++) { qs += (qs.length ? '&' : '?') + escape(key) + '=' + escape(values[idx]); } } return qs; } function get_text_from_bytes(bytes, precision) { // convert raw bytes to english-readable format // set precision to 1 for ints, 10 for 1 decimal point (default), 100 for 2, etc. bytes = Math.floor(bytes); if (!precision) precision = 10; if (bytes >= 1024) { bytes = Math.floor( (bytes / 1024) * precision ) / precision; if (bytes >= 1024) { bytes = Math.floor( (bytes / 1024) * precision ) / precision; if (bytes >= 1024) { bytes = Math.floor( (bytes / 1024) * precision ) / precision; if (bytes >= 1024) { bytes = Math.floor( (bytes / 1024) * precision ) / precision; return bytes + ' TB'; } else return bytes + ' GB'; } else return bytes + ' MB'; } else return bytes + ' K'; } else return bytes + pluralize(' byte', bytes); }; function get_bytes_from_text(text) { // parse text into raw bytes, e.g. "1 K" --> 1024 if (text.toString().match(/^\d+$/)) return parseInt(text); // already in bytes var multipliers = { b: 1, k: 1024, m: 1024 * 1024, g: 1024 * 1024 * 1024, t: 1024 * 1024 * 1024 * 1024 }; var bytes = 0; text = text.toString().replace(/([\d\.]+)\s*(\w)\w*\s*/g, function(m_all, m_g1, m_g2) { var mult = multipliers[ m_g2.toLowerCase() ] || 0; bytes += (parseFloat(m_g1) * mult); return ''; } ); return Math.floor(bytes); }; function ucfirst(text) { // capitalize first character only, lower-case rest return text.substring(0, 1).toUpperCase() + text.substring(1, text.length).toLowerCase(); } function commify(number) { // add commas to integer, like 1,234,567 if (!number) number = 0; number = '' + number; if (number.length > 3) { var mod = number.length % 3; var output = (mod > 0 ? (number.substring(0,mod)) : ''); for (i=0 ; i < Math.floor(number.length / 3); i++) { if ((mod == 0) && (i == 0)) output += number.substring(mod+ 3 * i, mod + 3 * i + 3); else output+= ',' + number.substring(mod + 3 * i, mod + 3 * i + 3); } return (output); } else return number; } function short_float(value, places) { // Shorten floating-point decimal to N places max if (!places) places = 2; var mult = Math.pow(10, places); return( Math.floor(parseFloat(value || 0) * mult) / mult ); } function pct(count, max, floor) { // Return formatted percentage given a number along a sliding scale from 0 to 'max' var pct = (count * 100) / (max || 1); if (!pct.toString().match(/^\d+(\.\d+)?$/)) { pct = 0; } return '' + (floor ? Math.floor(pct) : short_float(pct)) + '%'; }; function get_text_from_seconds(sec, abbrev, no_secondary) { // convert raw seconds to human-readable relative time var neg = ''; sec = parseInt(sec, 10); if (sec<0) { sec =- sec; neg = '-'; } var p_text = abbrev ? "sec" : "second"; var p_amt = sec; var s_text = ""; var s_amt = 0; if (sec > 59) { var min = parseInt(sec / 60, 10); sec = sec % 60; s_text = abbrev ? "sec" : "second"; s_amt = sec; p_text = abbrev ? "min" : "minute"; p_amt = min; if (min > 59) { var hour = parseInt(min / 60, 10); min = min % 60; s_text = abbrev ? "min" : "minute"; s_amt = min; p_text = abbrev ? "hr" : "hour"; p_amt = hour; if (hour > 23) { var day = parseInt(hour / 24, 10); hour = hour % 24; s_text = abbrev ? "hr" : "hour"; s_amt = hour; p_text = "day"; p_amt = day; if (day > 29) { var month = parseInt(day / 30, 10); s_text = "day"; s_amt = day % 30; p_text = abbrev ? "mon" : "month"; p_amt = month; if (day >= 365) { var year = parseInt(day / 365, 10); month = month % 12; s_text = abbrev ? "mon" : "month"; s_amt = month; p_text = abbrev ? "yr" : "year"; p_amt = year; } // day>=365 } // day>29 } // hour>23 } // min>59 } // sec>59 var text = p_amt + " " + p_text; if ((p_amt != 1) && !abbrev) text += "s"; if (s_amt && !no_secondary) { text += ", " + s_amt + " " + s_text; if ((s_amt != 1) && !abbrev) text += "s"; } return(neg + text); } function get_text_from_seconds_round(sec, abbrev) { // convert raw seconds to human-readable relative time // round to nearest instead of floor var neg = ''; sec = Math.round(sec); if (sec < 0) { sec =- sec; neg = '-'; } var text = abbrev ? "sec" : "second"; var amt = sec; if (sec > 59) { var min = Math.round(sec / 60); text = abbrev ? "min" : "minute"; amt = min; if (min > 59) { var hour = Math.round(min / 60); text = abbrev ? "hr" : "hour"; amt = hour; if (hour > 23) { var day = Math.round(hour / 24); text = "day"; amt = day; } // hour>23 } // min>59 } // sec>59 var text = "" + amt + " " + text; if ((amt != 1) && !abbrev) text += "s"; return(neg + text); }; function get_seconds_from_text(text) { // parse text into raw seconds, e.g. "1 minute" --> 60 if (text.toString().match(/^\d+$/)) return parseInt(text); // already in seconds var multipliers = { s: 1, m: 60, h: 60 * 60, d: 60 * 60 * 24, w: 60 * 60 * 24 * 7 }; var seconds = 0; text = text.toString().replace(/([\d\.]+)\s*(\w)\w*\s*/g, function(m_all, m_g1, m_g2) { var mult = multipliers[ m_g2.toLowerCase() ] || 0; seconds += (parseFloat(m_g1) * mult); return ''; } ); return Math.floor(seconds); }; function get_inner_window_size(dom) { // get size of inner window if (!dom) dom = window; var myWidth = 0, myHeight = 0; if( typeof( dom.innerWidth ) == 'number' ) { // Non-IE myWidth = dom.innerWidth; myHeight = dom.innerHeight; } else if( dom.document.documentElement && ( dom.document.documentElement.clientWidth || dom.document.documentElement.clientHeight ) ) { // IE 6+ in 'standards compliant mode' myWidth = dom.document.documentElement.clientWidth; myHeight = dom.document.documentElement.clientHeight; } else if( dom.document.body && ( dom.document.body.clientWidth || dom.document.body.clientHeight ) ) { // IE 4 compatible myWidth = dom.document.body.clientWidth; myHeight = dom.document.body.clientHeight; } return { width: myWidth, height: myHeight }; } function get_scroll_xy(dom) { // get page scroll X, Y if (!dom) dom = window; var scrOfX = 0, scrOfY = 0; if( typeof( dom.pageYOffset ) == 'number' ) { //Netscape compliant scrOfY = dom.pageYOffset; scrOfX = dom.pageXOffset; } else if( dom.document.body && ( dom.document.body.scrollLeft || dom.document.body.scrollTop ) ) { //DOM compliant scrOfY = dom.document.body.scrollTop; scrOfX = dom.document.body.scrollLeft; } else if( dom.document.documentElement && ( dom.document.documentElement.scrollLeft || dom.document.documentElement.scrollTop ) ) { //IE6 standards compliant mode scrOfY = dom.document.documentElement.scrollTop; scrOfX = dom.document.documentElement.scrollLeft; } return { x: scrOfX, y: scrOfY }; } function get_scroll_max(dom) { // get maximum scroll width/height if (!dom) dom = window; var myWidth = 0, myHeight = 0; if (dom.document.body.scrollHeight) { myWidth = dom.document.body.scrollWidth; myHeight = dom.document.body.scrollHeight; } else if (dom.document.documentElement.scrollHeight) { myWidth = dom.document.documentElement.scrollWidth; myHeight = dom.document.documentElement.scrollHeight; } return { width: myWidth, height: myHeight }; } function hires_time_now() { // return the Epoch seconds for like right now var now = new Date(); return ( now.getTime() / 1000 ); } function str_value(str) { // Get friendly string value for display purposes. if (typeof(str) == 'undefined') str = ''; else if (str === null) str = ''; return '' + str; } function pluralize(word, num) { // Pluralize a word using simplified English language rules. if (num != 1) { if (word.match(/[^e]y$/)) return word.replace(/y$/, '') + 'ies'; else if (word.match(/s$/)) return word + 'es'; // processes else return word + 's'; } else return word; } function render_menu_options(items, sel_value, auto_add) { // return HTML for menu options var html = ''; var found = false; for (var idx = 0, len = items.length; idx < len; idx++) { var item = items[idx]; var item_name = ''; var item_value = ''; if (isa_hash(item)) { if (('label' in item) && ('data' in item)) { item_name = item.label; item_value = item.data; } else { item_name = item.title; item_value = item.id; } } else if (isa_array(item)) { item_value = item[0]; item_name = item[1]; } else { item_name = item_value = item; } html += ''; if (item_value == sel_value) found = true; } if (!found && (str_value(sel_value) != '') && auto_add) { html += ''; } return html; } function dirname(path) { // return path excluding file at end (same as POSIX function of same name) return path.toString().replace(/\/$/, "").replace(/\/[^\/]+$/, ""); } function basename(path) { // return filename, strip path (same as POSIX function of same name) return path.toString().replace(/\/$/, "").replace(/^(.*)\/([^\/]+)$/, "$2"); } function strip_ext(path) { // strip extension from filename return path.toString().replace(/\.\w+$/, ""); } function load_script(url) { // Dynamically load script into DOM. Debug.trace( "Loading script: " + url ); var scr = document.createElement('SCRIPT'); scr.type = 'text/javascript'; scr.src = url; document.getElementsByTagName('HEAD')[0].appendChild(scr); } function compose_attribs(attribs) { // compose Key="Value" style attributes for HTML elements var html = ''; if (attribs) { for (var key in attribs) { html += " " + key + "=\"" + attribs[key] + "\""; } } return html; } function compose_style(attribs) { // compose key:value; pairs for style (CSS) elements var html = ''; if (attribs) { for (var key in attribs) { html += " " + key + ":" + attribs[key] + ";"; } } return html; } function truncate_ellipsis(str, len) { // simple truncate string with ellipsis if too long str = str_value(str); if (str.length > len) { str = str.substring(0, len - 3) + '...'; } return str; } function escape_text_field_value(text) { // escape text field value, with stupid IE support text = encode_attrib_entities( str_value(text) ); if (navigator.userAgent.match(/MSIE/) && text.replace) text = text.replace(/\&apos\;/g, "'"); return text; } function expando_text(text, max, link) { // if text is longer than max chars, chop with ellipsis and include link to show all if (!link) link = 'More'; text = str_value(text); if (text.length <= max) return text; var before = text.substring(0, max); var after = text.substring(max); return before + '... '+link+'' + '' + after + ''; }; function get_int_version(str, pad) { // Joe's Fun Multi-Decimal Comparision Trick // Example: convert 2.5.1 to 2005001 for numerical comparison against other similar "numbers". if (!pad) pad = 3; str = str_value(str).replace(/[^\d\.]+/g, ''); if (!str.match(/\./)) return parseInt(str, 10); var parts = str.split(/\./); var output = ''; for (var idx = 0, len = parts.length; idx < len; idx++) { var part = '' + parts[idx]; while (part.length < pad) part = '0' + part; output += part; } return parseInt( output.replace(/^0+/, ''), 10 ); }; function get_unique_id(len, salt) { // Get unique ID using MD5, hires time, pseudo-random number and static counter. if (this.__unique_id_counter) this.__unique_id_counter = 0; this.__unique_id_counter++; return hex_md5( '' + hires_time_now() + Math.random() + this.__unique_id_counter + (salt || '') ).substring(0, len || 32); }; function escape_regexp(text) { // Escape text for use in a regular expression. return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); }; function setPath(target, path, value) { // set path using dir/slash/syntax or dot.path.syntax // preserve dots and slashes if escaped var parts = path.replace(/\\\./g, '__PXDOT__').replace(/\\\//g, '__PXSLASH__').split(/[\.\/]/).map( function(elem) { return elem.replace(/__PXDOT__/g, '.').replace(/__PXSLASH__/g, '/'); } ); var key = parts.pop(); // traverse path while (parts.length) { var part = parts.shift(); if (part) { if (!(part in target)) { // auto-create nodes target[part] = {}; } if (typeof(target[part]) != 'object') { // path runs into non-object return false; } target = target[part]; } } target[key] = value; return true; }; function getPath(target, path) { // get path using dir/slash/syntax or dot.path.syntax // preserve dots and slashes if escaped var parts = path.replace(/\\\./g, '__PXDOT__').replace(/\\\//g, '__PXSLASH__').split(/[\.\/]/).map( function(elem) { return elem.replace(/__PXDOT__/g, '.').replace(/__PXSLASH__/g, '/'); } ); var key = parts.pop(); // traverse path while (parts.length) { var part = parts.shift(); if (part) { if (typeof(target[part]) != 'object') { // path runs into non-object return undefined; } target = target[part]; } } return target[key]; }; function substitute(text, args, fatal) { // perform simple [placeholder] substitution using supplied // args object and return transformed text var self = this; var result = true; var value = ''; if (typeof(text) == 'undefined') text = ''; text = '' + text; if (!args) args = {}; text = text.replace(/\[([^\]]+)\]/g, function(m_all, name) { value = getPath(args, name); if (value === undefined) { result = false; return m_all; } else return value; } ); if (!result && fatal) return null; else return text; }; pixl-webapp-2.0.3/js/xml.js000066400000000000000000000500401504641265100155210ustar00rootroot00000000000000/* JavaScript XML Library Plus a bunch of object utility functions Usage: var myxml = '' + 'Hello' + 'Content' + ''; var parser = new XML({ text: myxml, preserveAttributes: true }); var tree = parser.getTree(); tree.Simple = "Hello2"; tree.Node._Attribs.Key = "Value2"; tree.Node._Data = "Content2"; tree.New = "I added this"; alert( parser.compose() ); Copyright (c) 2004 - 2007 Joseph Huckaby */ var indent_string = "\t"; var xml_header = ''; var sort_args = null; var re_valid_tag_name = /^\w[\w\-\:]*$/; function XML(args) { // class constructor for XML parser class // pass in args hash or text to parse if (!args) args = ''; if (isa_hash(args)) { for (var key in args) this[key] = args[key]; } else this.text = args || ''; this.tree = {}; this.errors = []; this.piNodeList = []; this.dtdNodeList = []; this.documentNodeName = ''; this.patTag.lastIndex = 0; if (this.text) this.parse(); } XML.prototype.preserveAttributes = false; XML.prototype.patTag = /([^<]*?)<([^>]+)>/g; XML.prototype.patSpecialTag = /^\s*([\!\?])/; XML.prototype.patPITag = /^\s*\?/; XML.prototype.patCommentTag = /^\s*\!--/; XML.prototype.patDTDTag = /^\s*\!DOCTYPE/; XML.prototype.patCDATATag = /^\s*\!\s*\[\s*CDATA/; XML.prototype.patStandardTag = /^\s*(\/?)([\w\-\:\.]+)\s*(.*)$/; XML.prototype.patSelfClosing = /\/\s*$/; XML.prototype.patAttrib = new RegExp("([\\w\\-\\:\\.]+)\\s*=\\s*([\\\"\\'])([^\\2]*?)\\2", "g"); XML.prototype.patPINode = /^\s*\?\s*([\w\-\:]+)\s*(.*)$/; XML.prototype.patEndComment = /--$/; XML.prototype.patNextClose = /([^>]*?)>/g; XML.prototype.patExternalDTDNode = new RegExp("^\\s*\\!DOCTYPE\\s+([\\w\\-\\:]+)\\s+(SYSTEM|PUBLIC)\\s+\\\"([^\\\"]+)\\\""); XML.prototype.patInlineDTDNode = /^\s*\!DOCTYPE\s+([\w\-\:]+)\s+\[/; XML.prototype.patEndDTD = /\]$/; XML.prototype.patDTDNode = /^\s*\!DOCTYPE\s+([\w\-\:]+)\s+\[(.*)\]/; XML.prototype.patEndCDATA = /\]\]$/; XML.prototype.patCDATANode = /^\s*\!\s*\[\s*CDATA\s*\[(.*)\]\]/; XML.prototype.attribsKey = '_Attribs'; XML.prototype.dataKey = '_Data'; XML.prototype.parse = function(branch, name) { // parse text into XML tree, recurse for nested nodes if (!branch) branch = this.tree; if (!name) name = null; var foundClosing = false; var matches = null; // match each tag, plus preceding text while ( matches = this.patTag.exec(this.text) ) { var before = matches[1]; var tag = matches[2]; // text leading up to tag = content of parent node if (before.match(/\S/)) { if (typeof(branch[this.dataKey]) != 'undefined') branch[this.dataKey] += ' '; else branch[this.dataKey] = ''; branch[this.dataKey] += trim(decode_entities(before)); } // parse based on tag type if (tag.match(this.patSpecialTag)) { // special tag if (tag.match(this.patPITag)) tag = this.parsePINode(tag); else if (tag.match(this.patCommentTag)) tag = this.parseCommentNode(tag); else if (tag.match(this.patDTDTag)) tag = this.parseDTDNode(tag); else if (tag.match(this.patCDATATag)) { tag = this.parseCDATANode(tag); if (typeof(branch[this.dataKey]) != 'undefined') branch[this.dataKey] += ' '; else branch[this.dataKey] = ''; branch[this.dataKey] += trim(decode_entities(tag)); } // cdata else { this.throwParseError( "Malformed special tag", tag ); break; } // error if (tag == null) break; continue; } // special tag else { // Tag is standard, so parse name and attributes (if any) var matches = tag.match(this.patStandardTag); if (!matches) { this.throwParseError( "Malformed tag", tag ); break; } var closing = matches[1]; var nodeName = matches[2]; var attribsRaw = matches[3]; // If this is a closing tag, make sure it matches its opening tag if (closing) { if (nodeName == (name || '')) { foundClosing = 1; break; } else { this.throwParseError( "Mismatched closing tag (expected )", tag ); break; } } // closing tag else { // Not a closing tag, so parse attributes into hash. If tag // is self-closing, no recursive parsing is needed. var selfClosing = !!attribsRaw.match(this.patSelfClosing); var leaf = {}; var attribs = leaf; // preserve attributes means they go into a sub-hash named "_Attribs" // the XML composer honors this for restoring the tree back into XML if (this.preserveAttributes) { leaf[this.attribsKey] = {}; attribs = leaf[this.attribsKey]; } // parse attributes this.patAttrib.lastIndex = 0; while ( matches = this.patAttrib.exec(attribsRaw) ) { attribs[ matches[1] ] = decode_entities( matches[3] ); } // foreach attrib // if no attribs found, but we created the _Attribs subhash, clean it up now if (this.preserveAttributes && !num_keys(attribs)) { delete leaf[this.attribsKey]; } // Recurse for nested nodes if (!selfClosing) { this.parse( leaf, nodeName ); if (this.error()) break; } // Compress into simple node if text only var num_leaf_keys = num_keys(leaf); if ((typeof(leaf[this.dataKey]) != 'undefined') && (num_leaf_keys == 1)) { leaf = leaf[this.dataKey]; } else if (!num_leaf_keys) { leaf = ''; } // Add leaf to parent branch if (typeof(branch[nodeName]) != 'undefined') { if (isa_array(branch[nodeName])) { array_push( branch[nodeName], leaf ); } else { var temp = branch[nodeName]; branch[nodeName] = [ temp, leaf ]; } } else { branch[nodeName] = leaf; } if (this.error() || (branch == this.tree)) break; } // not closing } // standard tag } // main reg exp // Make sure we found the closing tag if (name && !foundClosing) { this.throwParseError( "Missing closing tag (expected )", name ); } // If we are the master node, finish parsing and setup our doc node if (branch == this.tree) { if (typeof(this.tree[this.dataKey]) != 'undefined') delete this.tree[this.dataKey]; if (num_keys(this.tree) > 1) { this.throwParseError( 'Only one top-level node is allowed in document', first_key(this.tree) ); return; } this.documentNodeName = first_key(this.tree); if (this.documentNodeName) { this.tree = this.tree[this.documentNodeName]; } } }; XML.prototype.throwParseError = function(key, tag) { // log error and locate current line number in source XML document var parsedSource = this.text.substring(0, this.patTag.lastIndex); var eolMatch = parsedSource.match(/\n/g); var lineNum = (eolMatch ? eolMatch.length : 0) + 1; lineNum -= tag.match(/\n/) ? tag.match(/\n/g).length : 0; array_push(this.errors, { type: 'Parse', key: key, text: '<' + tag + '>', line: lineNum }); }; XML.prototype.error = function() { // return number of errors return this.errors.length; }; XML.prototype.getError = function(error) { // get formatted error var text = ''; if (!error) return ''; text = (error.type || 'General') + ' Error'; if (error.code) text += ' ' + error.code; text += ': ' + error.key; if (error.line) text += ' on line ' + error.line; if (error.text) text += ': ' + error.text; return text; }; XML.prototype.getLastError = function() { // Get most recently thrown error in plain text format if (!this.error()) return ''; return this.getError( this.errors[this.errors.length - 1] ); }; XML.prototype.parsePINode = function(tag) { // Parse Processor Instruction Node, e.g. if (!tag.match(this.patPINode)) { this.throwParseError( "Malformed processor instruction", tag ); return null; } array_push( this.piNodeList, tag ); return tag; }; XML.prototype.parseCommentNode = function(tag) { // Parse Comment Node, e.g. var matches = null; this.patNextClose.lastIndex = this.patTag.lastIndex; while (!tag.match(this.patEndComment)) { if (matches = this.patNextClose.exec(this.text)) { tag += '>' + matches[1]; } else { this.throwParseError( "Unclosed comment tag", tag ); return null; } } this.patTag.lastIndex = this.patNextClose.lastIndex; return tag; }; XML.prototype.parseDTDNode = function(tag) { // Parse Document Type Descriptor Node, e.g. var matches = null; if (tag.match(this.patExternalDTDNode)) { // tag is external, and thus self-closing array_push( this.dtdNodeList, tag ); } else if (tag.match(this.patInlineDTDNode)) { // Tag is inline, so check for nested nodes. this.patNextClose.lastIndex = this.patTag.lastIndex; while (!tag.match(this.patEndDTD)) { if (matches = this.patNextClose.exec(this.text)) { tag += '>' + matches[1]; } else { this.throwParseError( "Unclosed DTD tag", tag ); return null; } } this.patTag.lastIndex = this.patNextClose.lastIndex; // Make sure complete tag is well-formed, and push onto DTD stack. if (tag.match(this.patDTDNode)) { array_push( this.dtdNodeList, tag ); } else { this.throwParseError( "Malformed DTD tag", tag ); return null; } } else { this.throwParseError( "Malformed DTD tag", tag ); return null; } return tag; }; XML.prototype.parseCDATANode = function(tag) { // Parse CDATA Node, e.g. var matches = null; this.patNextClose.lastIndex = this.patTag.lastIndex; while (!tag.match(this.patEndCDATA)) { if (matches = this.patNextClose.exec(this.text)) { tag += '>' + matches[1]; } else { this.throwParseError( "Unclosed CDATA tag", tag ); return null; } } this.patTag.lastIndex = this.patNextClose.lastIndex; if (matches = tag.match(this.patCDATANode)) { return matches[1]; } else { this.throwParseError( "Malformed CDATA tag", tag ); return null; } }; XML.prototype.getTree = function() { // get reference to parsed XML tree return this.tree; }; XML.prototype.compose = function() { // compose tree back into XML var raw = compose_xml( this.documentNodeName, this.tree ); var body = raw.substring( raw.indexOf("\n") + 1, raw.length ); var xml = ''; if (this.piNodeList.length) { for (var idx = 0, len = this.piNodeList.length; idx < len; idx++) { xml += '<' + this.piNodeList[idx] + '>' + "\n"; } } else { xml += xml_header + "\n"; } if (this.dtdNodeList.length) { for (var idx = 0, len = this.dtdNodeList.length; idx < len; idx++) { xml += '<' + this.dtdNodeList[idx] + '>' + "\n"; } } xml += body; return xml; }; // // Static Utility Functions: // function parse_xml(text) { // turn text into XML tree quickly var parser = new XML(text); return parser.error() ? parser.getLastError() : parser.getTree(); } function trim(text) { // strip whitespace from beginning and end of string if (text == null) return ''; if (text && text.replace) { text = text.replace(/^\s+/, ""); text = text.replace(/\s+$/, ""); } return text; } function encode_entities(text) { // Simple entitize function for composing XML if (text == null) return ''; if (text && text.replace) { text = text.replace(/\&/g, "&"); // MUST BE FIRST text = text.replace(//g, ">"); } return text; } function encode_attrib_entities(text) { // Simple entitize function for composing XML attributes if (text == null) return ''; if (text && text.replace) { text = text.replace(/\&/g, "&"); // MUST BE FIRST text = text.replace(//g, ">"); text = text.replace(/\"/g, """); text = text.replace(/\'/g, "'"); } return text; } function decode_entities(text) { // Decode XML entities into raw ASCII if (text == null) return ''; if (text && text.replace) { text = text.replace(/\<\;/g, "<"); text = text.replace(/\>\;/g, ">"); text = text.replace(/\"\;/g, '"'); text = text.replace(/\&apos\;/g, "'"); text = text.replace(/\&\;/g, "&"); // MUST BE LAST } return text; } function compose_xml(name, node, indent) { // Compose node into XML including attributes // Recurse for child nodes var xml = ""; // If this is the root node, set the indent to 0 // and setup the XML header (PI node) if (!indent) { indent = 0; xml = xml_header + "\n"; } // Setup the indent text var indent_text = ""; for (var k = 0; k < indent; k++) indent_text += indent_string; if ((typeof(node) == 'object') && (node != null)) { // node is object -- now see if it is an array or hash if (!node.length) { // what about zero-length array? // node is hash xml += indent_text + "<" + name; var num_keys = 0; var has_attribs = 0; for (var key in node) num_keys++; // there must be a better way... if (node["_Attribs"]) { has_attribs = 1; var sorted_keys = hash_keys_to_array(node["_Attribs"]).sort(); for (var idx = 0, len = sorted_keys.length; idx < len; idx++) { var key = sorted_keys[idx]; xml += " " + key + "=\"" + encode_attrib_entities(node["_Attribs"][key]) + "\""; } } // has attribs if (num_keys > has_attribs) { // has child elements xml += ">"; if (node["_Data"]) { // simple text child node xml += encode_entities(node["_Data"]) + "\n"; } // just text else { xml += "\n"; var sorted_keys = hash_keys_to_array(node).sort(); for (var idx = 0, len = sorted_keys.length; idx < len; idx++) { var key = sorted_keys[idx]; if ((key != "_Attribs") && key.match(re_valid_tag_name)) { // recurse for node, with incremented indent value xml += compose_xml( key, node[key], indent + 1 ); } // not _Attribs key } // foreach key xml += indent_text + "\n"; } // real children } else { // no child elements, so self-close xml += "/>\n"; } } // standard node else { // node is array for (var idx = 0; idx < node.length; idx++) { // recurse for node in array with same indent xml += compose_xml( name, node[idx], indent ); } } // array of nodes } // complex node else { // node is simple string xml += indent_text + "<" + name + ">" + encode_entities(node) + "\n"; } // simple text node return xml; } function find_object(obj, criteria) { // walk array looking for nested object matching criteria object if (isa_hash(obj)) obj = hash_values_to_array(obj); var criteria_length = 0; for (var a in criteria) criteria_length++; obj = always_array(obj); for (var a = 0; a < obj.length; a++) { var matches = 0; for (var b in criteria) { if (obj[a][b] && (obj[a][b] == criteria[b])) matches++; else if (obj[a]["_Attribs"] && obj[a]["_Attribs"][b] && (obj[a]["_Attribs"][b] == criteria[b])) matches++; } if (matches >= criteria_length) return obj[a]; } return null; } function find_objects(obj, criteria) { // walk array gathering all nested objects that match criteria object if (isa_hash(obj)) obj = hash_values_to_array(obj); var objs = new Array(); var criteria_length = 0; for (var a in criteria) criteria_length++; obj = always_array(obj); for (var a = 0; a < obj.length; a++) { var matches = 0; for (var b in criteria) { if (obj[a][b] && obj[a][b] == criteria[b]) matches++; else if (obj[a]["_Attribs"] && obj[a]["_Attribs"][b] && (obj[a]["_Attribs"][b] == criteria[b])) matches++; } if (matches >= criteria_length) array_push( objs, obj[a] ); } return objs; } function find_object_idx(obj, criteria) { // walk array looking for nested object matching criteria object // return index in outer array, not object itself if (isa_hash(obj)) obj = hash_values_to_array(obj); var criteria_length = 0; for (var a in criteria) criteria_length++; obj = always_array(obj); for (var idx = 0; idx < obj.length; idx++) { var matches = 0; for (var b in criteria) { if (obj[idx][b] && (obj[idx][b] == criteria[b])) matches++; else if (obj[idx]["_Attribs"] && obj[idx]["_Attribs"][b] && (obj[idx]["_Attribs"][b] == criteria[b])) matches++; } if (matches >= criteria_length) return idx; } return -1; } function delete_object(obj, criteria) { // walk array looking for nested object matching criteria object // delete first object found var idx = find_object_idx(obj, criteria); if (idx > -1) { obj.splice( idx, 1 ); return true; } return false; } function delete_objects(obj, criteria) { // delete all objects in obj array matching criteria while (delete_object(obj, criteria)) ; } function always_array(obj, key) { // if object is not array, return array containing object // if key is passed, work like XMLalwaysarray() instead // apparently MSIE has weird issues with obj = always_array(obj); if (key) { if ((typeof(obj[key]) != 'object') || (typeof(obj[key].length) == 'undefined')) { var temp = obj[key]; delete obj[key]; obj[key] = new Array(); obj[key][0] = temp; } return null; } else { if ((typeof(obj) != 'object') || (typeof(obj.length) == 'undefined')) { return [ obj ]; } else return obj; } } function hash_keys_to_array(hash) { // convert hash keys to array (discard values) var array = []; for (var key in hash) array_push(array, key); return array; } function hash_values_to_array(hash) { // convert hash values to array (discard keys) var arr = []; for (var key in hash) arr.push( hash[key] ); return arr; }; function merge_objects(a, b) { // merge keys from a and b into c and return c // b has precedence over a if (!a) a = {}; if (!b) b = {}; var c = {}; // also handle serialized objects for a and b if (typeof(a) != 'object') eval( "a = " + a ); if (typeof(b) != 'object') eval( "b = " + b ); for (var key in a) c[key] = a[key]; for (var key in b) c[key] = b[key]; return c; } function copy_object(obj) { // return copy of object (NOT DEEP) var new_obj = {}; for (var key in obj) new_obj[key] = obj[key]; return new_obj; } function deep_copy_object(obj) { // recursively copy object and nested objects // return new object return JSON.parse( JSON.stringify(obj) ); } function copy_into_object(a, b) { // copy b in to a (NOT DEEP) // no return value for (var key in b) a[key] = b[key]; } function num_keys(hash) { // count the number of keys in a hash var count = 0; for (var a in hash) count++; return count; } function reverse_hash(a) { // reverse hash keys/values var c = {}; for (var key in a) { c[ a[key] ] = key; } return c; } function lookup_path(path, obj) { // walk through object tree, psuedo-XPath-style // supports arrays as well as objects // return final object or value // always start query with a slash, i.e. /something/or/other path = path.replace(/\/$/, ""); // strip trailing slash while (/\/[^\/]+/.test(path) && (typeof(obj) == 'object')) { // find first slash and strip everything up to and including it var slash = path.indexOf('/'); path = path.substring( slash + 1 ); // find next slash (or end of string) and get branch name slash = path.indexOf('/'); if (slash == -1) slash = path.length; var name = path.substring(0, slash); // advance obj using branch if (typeof(obj.length) == 'undefined') { // obj is hash if (typeof(obj[name]) != 'undefined') obj = obj[name]; else return null; } else { // obj is array var idx = parseInt(name, 10); if (isNaN(idx)) return null; if (typeof(obj[idx]) != 'undefined') obj = obj[idx]; else return null; } } // while path contains branch return obj; } function isa_hash(arg) { // determine if arg is a hash return( !!arg && (typeof(arg) == 'object') && (typeof(arg.length) == 'undefined') ); } function isa_array(arg) { // determine if arg is an array or is array-like return( !!arg && (typeof(arg) == 'object') && (typeof(arg.length) != 'undefined') ); } function first_key(hash) { // return first key from hash (unordered) for (var key in hash) return key; return null; // no keys in hash } function array_push(array, item) { // push item onto end of array array[ array.length ] = item; } function rand_array(arr) { // return random element from array return arr[ parseInt(Math.random() * arr.length, 10) ]; } function find_in_array(arr, elem) { // return true if elem is found in arr, false otherwise for (var idx = 0, len = arr.length; idx < len; idx++) { if (arr[idx] == elem) return true; } return false; } pixl-webapp-2.0.3/package.json000066400000000000000000000011101504641265100162270ustar00rootroot00000000000000{ "name": "pixl-webapp", "version": "2.0.3", "description": "A client-side JavaScript framework, designed to be a base for web applications.", "author": "Joseph Huckaby ", "homepage": "https://github.com/jhuckaby/pixl-webapp", "license": "MIT", "repository": { "type": "git", "url": "https://github.com/jhuckaby/pixl-webapp" }, "bugs": { "url": "https://github.com/jhuckaby/pixl-webapp/issues" }, "keywords": [ "webapp", "frontend", "boilerplate", "starterkit" ], "dependencies": {}, "devDependencies": {} }