From ad7decddb62142981116a55b5b998190f9d47117 Mon Sep 17 00:00:00 2001 From: Marco H Date: Wed, 2 Nov 2022 19:42:50 +0100 Subject: [PATCH] Initial version of Slope Clock ++ --- apps/slopeclockpp/ChangeLog | 1 + apps/slopeclockpp/app-icon.js | 1 + apps/slopeclockpp/app.js | 220 +++++++++++++++++++++++++++++++ apps/slopeclockpp/app.png | Bin 0 -> 10685 bytes apps/slopeclockpp/metadata.json | 14 ++ apps/slopeclockpp/screenshot.png | Bin 0 -> 15562 bytes 6 files changed, 236 insertions(+) create mode 100644 apps/slopeclockpp/ChangeLog create mode 100644 apps/slopeclockpp/app-icon.js create mode 100644 apps/slopeclockpp/app.js create mode 100644 apps/slopeclockpp/app.png create mode 100644 apps/slopeclockpp/metadata.json create mode 100644 apps/slopeclockpp/screenshot.png diff --git a/apps/slopeclockpp/ChangeLog b/apps/slopeclockpp/ChangeLog new file mode 100644 index 0000000000..5560f00bce --- /dev/null +++ b/apps/slopeclockpp/ChangeLog @@ -0,0 +1 @@ +0.01: New App! diff --git a/apps/slopeclockpp/app-icon.js b/apps/slopeclockpp/app-icon.js new file mode 100644 index 0000000000..bd62b928d5 --- /dev/null +++ b/apps/slopeclockpp/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEw4P/AAMA/Ayq8EH8AEBgfgj4zCj/gn/8Aod//wFDvk/gEEAoP4AoMAEIP4j4FFwAFC/gFEv//ApM/74FDg4XBgZLCFIMzAoU4g8BK4dwgMP+Ewg+AgMfK4PhAoXwh+B/0Bj0B/4FBgYnB/8B/kDgf/+ED/kHn//HgIFBW4IFB/AFDgf4h4FB+EBFgLKCAoInBAAOAAoqkBAgPAWAIuBAoXAn+zCAMB4F/8YFBgYFB4YFBRgY7BYwIoCABX4zkY74FB/mMiALC/3mug6CAAgA==")) diff --git a/apps/slopeclockpp/app.js b/apps/slopeclockpp/app.js new file mode 100644 index 0000000000..25fd307ebd --- /dev/null +++ b/apps/slopeclockpp/app.js @@ -0,0 +1,220 @@ +Graphics.prototype.setFontPaytoneOne = function(scale) { + // Actual height 81 (91 - 11) + this.setFontCustom( + E.toString(require('heatshrink').decompress(atob('AH8AgP/BpcD//gBpn4Bpn+Bpn/wANMHBRTB//wBphGLBoJGLv4OBBpU/KhkfBoPABpMPMRkHMRh+CMRRwC/hwmMQQNKMQTTNBpRGCRhSpCBpY4BFJY4BBpcAjgMLAHUwBpl4BhcBd5Z/Bd5abCBpa3BTZd/YpcBcIPgBpMHBoPwIhf//BEL/5wKIgP/OBJECAAJELAAJwIIgQABOBBECOBRECOBJEEOBBEEOBBEEOBBEEOBBEEOA5EFBo5EFFI5EFKY5EGN4woGTIpEpj5EMDYzeGG4xEFgEDWZhhFbo59FfI7QFIgynGIgxwGBg5wEIhBwE+ANIOAZEIOAhEIOAgMJOAREJOAZEJOAZEJOAZEKOAQMKOAJELOAJELAAJELAH0EBhaQBSJa6BZJbkCDhMDBof4XJIADBpvAKRIqKBov+Bo0fBogqHBozpGBoyAGBoxjGBo44FBo44FMIpxHBo5xFBo7HFU4pGHBpBGEBpB/EdohGIgINHIwgNJIwgWEn4EC8ANGQ4SNHv4VEQgRUEEgQxCHwRUEYgRNDEQQNKFQRUDAwQNDQoRUDTQQUDHASpDCgR3EHAJiDCgR3ELYJiEBow/BMQgiBbQ4iFSYg/CLYZwBGAg/COAwNGOAwiDJoRwUKggNBOAwGEBoJwEcIT2GaYw4DAoINEMQQ/CHwRbEMQQHCLQTaHI4QvCNIoHCAArMEJoQAFO4gkDBpJUCAAraHBpRUDAAihEIxANFIw4NFIw7EEIxANFRo4NGcQQNKHAwNGHAwNGHAwNHHAoNHf4YNJVQqLFFQ7DEFRDtEKpHgBpCADwANIDgRSHKwvABpQA/AFp7BZwkfXIyXFVoLVFv//bArxFBoLBDga6GfgK0DHwIiEH4TrEcgw/BJogwBa4g/BJogwBEQgNGOAxNBAAwUEJoQAFOAoNHOAoNHOApbBAAxwEBpBwENIIAGOAgNIOAh3BOBYNIOAi2BOBYNIOAgNJOAbEBOBbEIOAjEIOAoNIOAioIOAiaIOAiMIOH5wLAAw/BOAgAGH4JwEAAw/CBpQ/COAYAHWAJwDAA6wBOAYAHWAJwEAAywBODIA/ABsDUBYNBOwpwGZgIcEcIwNBDggNBcIraFBoQjEbQK+DBoThEBoIqDBoThEdAJNDBoThEBpBNEewJbDBoRwEewINGOAiFBNIYNCOAgNJO5INDOAaaBAwYNDOAgGEBoZwEBpBwEVAgNDOAiMBCgQNDOAiMBCgRnCOAqMEBohwDPwgNEOAZ+EBohwDPwQGBFwJwJAwINEOAxUBLAP/+5wHIwIDC/ZwHHAInC/JwHAAn4OBAAD/g/BOAwNEHYJwGBog/BOAgiBAAf+H4JwELwQNDH4JwEMQQNDH4JwEMQv+H4QNDKgoYBOApUGJoRwDKgxNCOAZUGJoRwEIwoGCOAhGFWARwEIwoUCOAhGEBIJwGRogXCOAriEBoRwGHAZBCOAxxDBoRwGFQZrCOAxADEgRwGCwZOCOA4A/AEMBXggAISQ0AjCZFZYgjBTQt/AwqgBBoraFfozgBbQgNBGIgNGEQIGEewJVECgIGEHwJGEAxr9BKggGBewImBfoRUEAwQ7CBIJUFgINCFoIJBO4oNCwAtBBIJ3JFoIJBFoJNEEQQfBBIJNDRgwJCJoaMGBIQ/DPwgNBFoJiHRgYtBMQ4+DFoJiHHwYfBMQbFDPwoJBXww+CFoZwGHwQtDOAz2CFoZwGUIQJCTwRwGGAIJBTwRwGEQICBKAIRDOAngAQJCBJoJwGAAfhD4ZwEAAxwGBpZiBAA4NDMQIAHPwZiCAAx+DMQQNKKhKMDKhKMDKhINEKgf7BoaaDIwn5BpCpD/A8DVAhGD/g8DBooJC/g8DBoqNC/A8DWwg4DIAINIe4k/BpA0BPAI4CBowmBWAI4CBo4uFKYoAFM4KLEAAxZBWogA/ADSMBRZaaCBpTlCwANMXYIAIaQXgBpioKBoTEKaILgLBoRwKn4NBOBQNDOBINDOBN/BoRwJBoZwJBgRwKBoZwJBoZwIgILCOBINDJAJwHfQX8OQJwHBoaqBOA4NC/DUBOA8HBoQDBOA4NC+AfBOA76C8BXBOA4NDQIQNJLwJwILoINCOBANCC4JwIfQQNBOBAbCMwZwGIoQAGJAZ9CAAxIDU4QAGJAbfCAAxIEBpBIEQ4IAGXIhwCAAq5EOAQAGOH5w/OH5wvBoYAELIInEAA4ZKLIiYDAA5ZBTAYAHLIKYDAA5ZBTAgAGZQKYEAAzKBTAhwjAH4A8U4LRCh7xGS4LRCcYwGBAATDBAwLjEBojDBeILVEAwIADwA7Baoj4BAAfAcYLVECgIADGgIRCfAgAD/EAn5UFBohUIv4OEKg4iBKghNBKghwEGgJNCOBJCBD4RwIIQI/BMQZwHH4JUDOArFDOgJwHBIJiGOAQtBBoJiGSYQNBC4JiGSYTPDH4RiDGAP4Z4jFFGAImBBoY/BYoYmDEoZwIRAhwIwDrDBoJwG4AXDJoJwHRAbMCOAzICZgZwGRAXADYRwGK4X4EQLhGOAYADPwZwFcopwHcopwHBpBwEAAaMEOAoACRgjhFBo7hFAAYNDOAZiFBoZwDKgqoDOAZUFBohwCW4QNHfQYNEWwZwDCIQNHGgINBIwgNEOAIDDBo8DLAoNGAAg4DBpJxDMIgAEXAYNJFQYMJXgTtEAA8HIhIA/ACp9BN5SZD8B7JBoX+YZjSJb4f//ANMYpF/BogqHBovwBowMEKpANF/+ABpiAGBoxjGBoyrGBoxxGBo5xFBo5xFPopGHBo5/FBo5GFYYpGHBpCNEj5UMBpCNEh4ICw//g5UGA4X8AYOAHwQNG/EDBoIGCcQYJBH4IDB4EBKgoGCBoQJBQoJUDBoYDBBIJbBVIgNGHAJiEEQIUBAQQtBMQhbBBoQXBGISMFBQN/C4RiFRgIKBD4IxDYoY+BBoIfBC4IRBOAZ+CBoQJBAYJwGwAtBBIIDBOA3AFoIJBOBHgNgY/DOAiMCHYLFCOAp+CFoZwGPwQRBAwINEGAb6CAAR+DGgYtBAAZ+DGgYmCBo5iCIQQACRgZiGAASMEKgYNJKgYtBAASaEYoZiEBohUIVAhUIBoomB/BUEBopUIBoipIBogmBDYJGEBogmBO4JmCBo8/V4QNJh7nCHAYNFgxYEMIxKGBpYqCU4oAFOoLtEAA8PBhYA/AB9///AQ5jFCABEfQ47MCYAbvBXQgiEUYKxFg4iEgbNGh4UEbgRNFCgoNBH4hpBOBYUBAwhwFHwJ3FOApaBNIpwFCYJpFOAovBNIpwFBgJbFOAgECKgwUDIgQABTYhwDJQIACKghwDKQRGGOAYfBAAZwHBghUEOASXCAAaiF/xSEKgprCIgibGAwO/BopUEKApwJAAyMEGoyoGSwhvHWQqLHOARgKbgpSHfAqYGOBJSEOBAMFOAyXEOBBEGOAyXEOBBEGOAyXEOA5EHOAqXFOA5EHOAqXGOAxEIOAgMIOAZEJOAaXHMQpEJAH4AOn6QJbIaDKQgYcKUATXJVxwNCZQ8fCwIND4C4H4ANDHAzUCBoY4GBAP+MIQEBBo//4IDCOIoXD+ANDewozDBoZGFBIZXBIw4NDAAZGFBo6NFEoYAERogNIKgk/Bo5UEBpBUEj5UMh5UMBpKpDg4KFAwRUDbgP4JARCBKgrEB/AsC/BNCAYINEfYQJBCQJiEBIQpDCQJiEv4JBHAT2DRggTBQIReBWAJiDBQJlDYIIgBYoY+BwBGCLwIVBOAYYBCYJUFOAYYBCYIzBHgIVBOAoTBKgYVBOA6NCwAVBOA6zEOAwlDSIhwF4ANCEAJKBOAvwcgYNCOAv/TQQYBGILhFAAn4DYJwDHwQAGBogUBAAx+ERIQAFPwiJCAAwNDL4YNJPYQAGRgZUJRgZUJBoiKC/wNETQZGEMwiaDIwhmEBohGDMwgNFEwS7EVAiNDLAgNFDARYDBowqBWAJGDBo0DH4JYDaQgAFDZKRGBpRxCBpQqCPooAFKoLDEAA8cBhYA/ACM/8AMKcQYAJaASXKWYTdDgwNI/+AawSyHAAJHCn64FBobeCHgwND/xLCeAoNDHAIFBCIINI8BnCKZA0BQYRGEBohxBv5YDBow0Bn5UFGIRGFSIYNG4AiBKgg/CKhQNFPYJUGBohUIBohUICgIADSYSpECgJiEKgwNCKAXAKg0fCgRCCLYWAYggNBCIJiHGAYDBBoJiFGAINBEwJwBMQowCOgQtFPwh0DH4TFEJgYYBOA4XBJgIYBaYRwEHwJMBBQLTDOAYlBJgIKBPwZwFHwIKB+ANCOA5KBD4INBOAwwBTQhwGGAN/BpBiBEQM/HYINBPwhiBS4X8GAR+EMQI4BBoJvCPwiFC/kPAIINGCof//oEDRgYxCAAwNDKgQAGTQZUCBpZUCAAqoDKgYNKKggADWwapDBpZGHBopGHBopGHBoqNHBoqNHBow4GBow4GBow4GBow4GTIgACfIYNJFQrREFRD7EKo/+Bg7HE/ANJDgQ2IeYZRHAH4AmgaYDn50HRgKLCv/8BpD6CZQINIC4QNBVgy2CBoYgCIojEDBoI4GBoRQBn7yHgLuDBoJGGBoQlBj7zIBAIlBh4uDAAhBBEoJYCKgwzCwBKCHgIAEGYY8EAAgzEHgaMHGYI8DPw5wEwBwTEoJwLUgatEMQ4uDPwzhNC4RPBEAKMGC4QNBEAINHC4INBEAIpGKAQgDBo8AnASDRYoAnA='))), + 46, + atob("ITZOMzs7SDxHNUdGIQ=="), + 113+(scale<<8)+(1<<16) + ); + return this; +}; + +{ // must be inside our own scope here so that when we are unloaded everything disappears + // we also define functions using 'let fn = function() {..}' for the same reason. function decls are global +let drawTimeout; + +let g2 = Graphics.createArrayBuffer(g.getWidth(),90,1,{msb:true}); +let g2img = { + width:g2.getWidth(), height:g2.getHeight(), bpp:1, + buffer:g2.buffer, transparent:0 +}; +const slope = 20; +const offsy = 20; // offset of numbers from middle +const fontBorder = 4; // offset from left/right +const slopeBorder = 10, slopeBorderUpper = 4; // fudge-factor to move minutes down from slope +let R,x,y; // middle of the clock face +let dateStr = ""; +let bgColors = g.theme.dark ? ["#ff0","#0ff","#f0f"] : ["#f00","#0f0","#00f"]; +let bgColor = bgColors[(Math.random()*bgColors.length)|0]; + + +// Draw the hour, and the minute into an offscreen buffer +let draw = function() { + R = Bangle.appRect; + x = R.w / 2; + y = R.y + R.h / 2 - 12; // 12 = room for date + var date = new Date(); + var hourStr = date.getHours(); + var minStr = date.getMinutes().toString().padStart(2,0); + dateStr = require("locale").dow(date, 1).toUpperCase()+ " "+ + require("locale").date(date, 0).toUpperCase(); + + // Draw hour + g.reset().clearRect(R); // clear whole background (w/o widgets) + g.setFontAlign(-1, 0).setFont("PaytoneOne"); + g.drawString(hourStr, fontBorder, y-offsy); + // add slope in background color + g.setColor(g.theme.bg).fillPoly([0,y+slope-slopeBorderUpper, R.w,y-slope-slopeBorderUpper, + R.w,y-slope, 0,y+slope]); + + // Draw minute to offscreen buffer + g2.setColor(0).fillRect(0,0,g2.getWidth(),g2.getHeight()).setFontAlign(1, 0).setFont("PaytoneOne"); + g2.setColor(1).drawString(minStr, g2.getWidth()-fontBorder, g2.getHeight()/2); + g2.setColor(0).fillPoly([0,0, g2.getWidth(),0, 0,slope*2]); + // start the animation *in* + animate(true); + + // queue next draw + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = setTimeout(function() { + drawTimeout = undefined; + animate(false, function() { + draw(); + }); + }, 60000 - (Date.now() % 60000)); +}; + +let isAnimIn = true; +let animInterval; +// Draw *just* the minute image +let drawMinute = function() { + var yo = slopeBorder + offsy + y - 2*slope*minuteX/R.w; + // draw over the slanty bit + g.setColor(bgColor).fillPoly([0,y+slope, R.w,y-slope, R.w,R.h+R.y, 0,R.h+R.y]); + // draw the minutes + g.setColor(g.theme.bg).drawImage(g2img, x+minuteX-(g2.getWidth()/2), yo-(g2.getHeight()/2)); +}; +let animate = function(isIn, callback) { + if (animInterval) clearInterval(animInterval); + isAnimIn = isIn; + minuteX = isAnimIn ? -g2.getWidth() : 0; + drawMinute(); + animInterval = setInterval(function() { + minuteX += 8; + let stop = false; + if (isAnimIn && minuteX>=0) { + minuteX=0; + stop = true; + } else if (!isAnimIn && minuteX>=R.w) + stop = true; + drawMinute(); + if (stop) { + clearInterval(animInterval); + animInterval=undefined; + if (isAnimIn) { + // draw the date + g.setColor(g.theme.bg).setFontAlign(0, 0).setFont("6x15").drawString(dateStr, R.x + R.w/2, R.y+R.h-9); + + // draw steps to bottom left + const steps = getSteps(); + if (steps > 0) + g.setFontAlign(-1, 0).drawString(shortValue(steps), 3, R.y+R.h-30); + + // draw weather to top right + const weather = getWeather(); + const tempString = weather ? require("locale").temp(weather.temp - 273.15) : undefined; + const code = weather ? weather.code : -1; + if (code > -1) { + g.setColor(g.theme.fg).setFontAlign(1, 0).drawString(tempString, R.w - 3, y-slope-slopeBorderUpper); + const icon = getWeatherIconByCode(code); + if (icon) g.drawImage(icon, R.w - 3 - 15, y-slope-slopeBorderUpper - 15 - 15); + } + } + if (callback) callback(); + } + }, 20); +}; + +let getSteps = function() { + if (Bangle.getHealthStatus) { + return Bangle.getHealthStatus("day").steps; + } + if (WIDGETS && WIDGETS.wpedom !== undefined) { + return WIDGETS.wpedom.getSteps(); + } + return 0; +}; + +let shortValue = function(v) { + if (isNaN(v)) return '-'; + if (v <= 999) return v; + if (v >= 1000 && v < 10000) { + v = Math.floor(v / 100) * 100; + return (v / 1000).toFixed(1).replace(/\.0$/, '') + 'k'; + } + if (v >= 10000) { + v = Math.floor(v / 1000) * 1000; + return (v / 1000).toFixed(1).replace(/\.0$/, '') + 'k'; + } +}; + +let getWeather = function() { + let jsonWeather = require("Storage").readJSON('weather.json'); + return jsonWeather && jsonWeather.weather ? jsonWeather.weather : undefined; +}; + +/* + * Choose weather icon to display based on weather conditition code + * https://openweathermap.org/weather-conditions#Weather-Condition-Codes-2 + */ +let getWeatherIconByCode = function(code) { + let codeGroup = Math.round(code / 100); + + // weather icons: + let weatherCloudy = atob("EBCBAAAAAAAAAAfgD/Af8H/4//7///////9//z/+AAAAAAAA"); + let weatherSunny = atob("EBCBAAAAAYAQCBAIA8AH4A/wb/YP8A/gB+ARiBAIAYABgAAA"); + let weatherMoon = atob("EBCBAAAAAYAP8B/4P/w//D/8f/5//j/8P/w//B/4D/ABgAAA"); + let weatherPartlyCloudy = atob("EBCBAAAAAAAYQAMAD8AIQBhoW+AOYBwwOBBgHGAGP/wf+AAA"); + let weatherRainy = atob("EBCBAAAAAYAH4AwwOBBgGEAOQAJBgjPOEkgGYAZgA8ABgAAA"); + let weatherPartlyRainy = atob("EBCBAAAAEEAQAAeADMAYaFvoTmAMMDgQIBxhhiGGG9wDwAGA"); + let weatherSnowy = atob("EBCBAAAAAAADwAGAEYg73C50BCAEIC50O9wRiAGAA8AAAAAA"); + let weatherFoggy = atob("EBCBAAAAAAADwAZgDDA4EGAcQAZAAgAAf74AAAAAd/4AAAAA"); + let weatherStormy = atob("EBCBAAAAAYAH4AwwOBBgGEAOQMJAgjmOGcgAgACAAAAAAAAA"); + let unknown = undefined; + + switch (codeGroup) { + case 2: + return weatherStormy; + case 3: + return weatherCloudy; + case 5: + switch (code) { + case 511: + return weatherSnowy; + case 520: + return weatherPartlyRainy; + case 521: + return weatherPartlyRainy; + case 522: + return weatherPartlyRainy; + case 531: + return weatherPartlyRainy; + default: + return weatherRainy; + } + case 6: + return weatherSnowy; + case 7: + return weatherFoggy; + case 8: + switch (code) { + case 800: + return weatherSunny; + case 801: + return weatherPartlyCloudy; + case 802: + return weatherPartlyCloudy; + default: + return weatherCloudy; + } + default: + return unknown; + } +} + +// Show launcher when middle button pressed +Bangle.setUI({ + mode : "clock", + remove : function() { + // Called to unload all of the clock app + if (animInterval) clearInterval(animInterval); + animInterval = undefined; + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = undefined; + delete Graphics.prototype.setFontPaytoneOne; + }}); +// Load widgets +Bangle.loadWidgets(); +draw(); +setTimeout(Bangle.drawWidgets,0); +} diff --git a/apps/slopeclockpp/app.png b/apps/slopeclockpp/app.png new file mode 100644 index 0000000000000000000000000000000000000000..2f5912fcf3c5dde259cd3f6ec1f7745cd3013d8e GIT binary patch literal 10685 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%0A^TaIdNAA1c|vojMSZS%LP3!g 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 ZwI=CXS3MV047Msb8n<;-%5L2c`9H>$Z?^yd literal 0 HcmV?d00001 diff --git a/apps/slopeclockpp/metadata.json b/apps/slopeclockpp/metadata.json new file mode 100644 index 0000000000..9e28424a84 --- /dev/null +++ b/apps/slopeclockpp/metadata.json @@ -0,0 +1,14 @@ +{ "id": "slopeclockpp", + "name": "Slope Clock ++", + "version":"0.01", + "description": "A clock where hours and minutes are divided by a sloping line. When the minute changes, the numbers slide off the screen. This is a clone of the original Slope Clock which shows weather and steps.", + "icon": "app.png", + "screenshots": [{"url":"screenshot.png"}], + "type": "clock", + "tags": "clock", + "supports" : ["BANGLEJS2"], + "storage": [ + {"name":"slopeclockpp.app.js","url":"app.js"}, + {"name":"slopeclockpp.img","url":"app-icon.js","evaluate":true} + ] +} diff --git a/apps/slopeclockpp/screenshot.png b/apps/slopeclockpp/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..93970956c17750f51c2281a8ee7f483b737b0468 GIT binary patch literal 15562 zcmeI0TZ$_;5QZHH_$4p%G@Ch3!C(kvFE$u@TUkdoHK~iL>Mvbv^~EP4OwT!PsV<*> zm85RZ{Ql?PKTdM}L7%_T|G((xm(#Co`;z}(a{Hp*7hfa$<@@@7Rp4=XERW0O`nW91 zW4T3PMFtIRi7{(noUR^ilF+B&HK2lPsS-+!jXw* zqaJZje=vkHsSKL^xSC$(s(BBWX`(l{SK_L9Lm?gyohboC7WHWa9fUk;-XPextd?aV zmsd~C>jf`Y)oa{E#H!x?TwXP01((WO%<7rm3KB>;<-GAR<9YLW6B60!7*bC?>$u<1 z-xb(V0ojYI^B22Dne+--ebIXdu3kDv^z2V>Mpl~rPp(spUo#3rNd8dezUY5ey4XX40JadwXNKK?%o0?U06vR z&UUp|>3JhF83gg@T*XB})*f(iJP7GGjRkkqg?jgLlK>bG>z-nI9n8}<%FW*mv!_Q3 zTgwd}x3CbqxK%Q8*A%$cTVa#lJ=|Oc>fylkpiOWXFpb*buoebv=eC(x;MQxSH+Pi7 z?Ir-Z#S7nNns77J+!l-xn#Gn^(lJZgO#()!vsg@8_l{XBxt*e=AyNg~V00(FEF*#1 zPMaGs=u=LbV^g=t=2oxhS*{)8pd8inj8h|gS{Iguc)%OxO}<0Tidd>SUWOn%7)4manxPEGT}I%K&d7{+Pqa9()d zz)O6{1&lUfpb_`HfCm9#1_}mfJHl+_wamHb+a1BtUm>Idn zhGYvf>~f>W2mQP}Z^*S|05@EV!8^qJ^8vWdfH=1yr*1>MbHV1}VEYeqL)x)=-_C`l zY#%$m2+)A}b=J-G{(D?H9OYsS_}<#5H*>KJ9j@0B;#`;MJ?J`0Z81=(#`Qa%E6i2fGfA>4;u!q6Mg>DsJ%m6I10GQ zL~ul}XeprK#-g^a74&{+JgEBqZ)>i={WJHs^=xnQitD1@Kemx_7H&a3-}gfVuCxJL+%zxeRqlorx@U*5sJf8LL;0~h7v4eQ3-`^OQ>M!#nBtA_va%bPxyk5B&G zdkKpF=0JzyXzq@^Rq05mz1!%sq0S(W1#ihiiS@xrmk5H9YFPwcH~% W@VlOq?|QCB6|8l{)67^u?fwsI8A4G2 literal 0 HcmV?d00001