Merge pull request #2011 from pebl-hank/master
Added note to configure position in "my location" if not done yet. Small fixes.master
commit
9808daa1c0
|
|
@ -6,3 +6,4 @@
|
|||
0.20: Add theme support
|
||||
0.21: Add Settings
|
||||
0.22: Use default Bangle formatter for booleans
|
||||
0.23: Added note to configure position in "my location" if not done yet. Small fixes.
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ If watch is locked, seconds get refreshed every 10 seconds.
|
|||
|
||||
## Usage
|
||||
|
||||
Location for sun set / rise set with mylocation app.
|
||||
|
||||
Provide names and the UTC offsets for up to three other timezones in the app store. These are stored in a json file on your watch. UTC offsets can be decimal (e.g., 5.5 for India).
|
||||
|
||||
The clock does not handle summer time / daylight saving time changes automatically. If one of your three locations changes its UTC offset, you can simply change the setting in the app store and update. Currently the clock only supports 24 hour time format for the additional time zones.
|
||||
|
|
@ -21,11 +23,5 @@ Please use [the Espruino Forum](http://forum.espruino.com/microcosms/1424/) if y
|
|||
|
||||
Created by Hank.
|
||||
|
||||
Based on the great work of
|
||||
=================
|
||||
World Clock - 4 time zones
|
||||
Made by [Scott Hale](https://www.github.com/computermacgyver), based upon the [Simple Clock](https://github.com/espruino/BangleApps/tree/master/apps/sclock).
|
||||
===== a n d =====
|
||||
Sun Clock
|
||||
[Sun Clock](https://github.com/espruino/BangleApps/tree/master/apps/sunclock)
|
||||
=================
|
||||
Based on the great work of "World Clock - 4 time zones". Made by [Scott Hale](https://www.github.com/computermacgyver), based upon the [Simple Clock](https://github.com/espruino/BangleApps/tree/master/apps/sclock).
|
||||
And Sun Clock [Sun Clock](https://github.com/espruino/BangleApps/tree/master/apps/sunclock)
|
||||
|
|
@ -46,10 +46,10 @@ setting = require("Storage").readJSON("setting.json",1);
|
|||
E.setTimeZone(setting.timezone); // timezone = 1 for MEZ, = 2 for MESZ
|
||||
SunCalc = require("hsuncalc.js");
|
||||
const LOCATION_FILE = "mylocation.json";
|
||||
var rise = "07:00";
|
||||
var set = "20:00";
|
||||
var pos = {altitude: 20, azimuth: 135};
|
||||
var noonpos = {altitude: 37, azimuth: 180};
|
||||
var rise = "read";
|
||||
var set = "...";
|
||||
//var pos = {altitude: 20, azimuth: 135};
|
||||
//var noonpos = {altitude: 37, azimuth: 180};
|
||||
//=======Sun
|
||||
|
||||
var ampm = "AM";
|
||||
|
|
@ -113,19 +113,19 @@ g.setBgColor(g.theme.bg);
|
|||
function queueDraw() {
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = setTimeout(function() {
|
||||
drawTimeout = undefined;
|
||||
draw();
|
||||
}, 60000 - (Date.now() % 60000));
|
||||
drawTimeout = undefined;
|
||||
draw();
|
||||
}, 60000 - (Date.now() % 60000));
|
||||
}
|
||||
|
||||
// schedule a draw for the next second
|
||||
function queueDrawSeconds() {
|
||||
if (drawTimeoutSeconds) clearTimeout(drawTimeoutSeconds);
|
||||
drawTimeoutSeconds = setTimeout(function() {
|
||||
drawTimeoutSeconds = undefined;
|
||||
drawSeconds();
|
||||
//console.log("TO: " + secondsTimeout);
|
||||
}, secondsTimeout - (Date.now() % secondsTimeout));
|
||||
drawTimeoutSeconds = undefined;
|
||||
drawSeconds();
|
||||
//console.log("TO: " + secondsTimeout);
|
||||
}, secondsTimeout - (Date.now() % secondsTimeout));
|
||||
}
|
||||
|
||||
function doublenum(x) {
|
||||
|
|
@ -137,12 +137,17 @@ function getCurrentTimeFromOffset(dt, offset) {
|
|||
}
|
||||
|
||||
function updatePos() {
|
||||
coord = require("Storage").readJSON(LOCATION_FILE,1)|| {"lat":53.3,"lon":10.1,"location":"Pattensen"};
|
||||
pos = SunCalc.getPosition(Date.now(), coord.lat, coord.lon);
|
||||
coord = require("Storage").readJSON(LOCATION_FILE,1)|| {"lat":0,"lon":0,"location":"-"}; //{"lat":53.3,"lon":10.1,"location":"Pattensen"};
|
||||
if (coord.lat != 0 && coord.lon != 0) {
|
||||
//pos = SunCalc.getPosition(Date.now(), coord.lat, coord.lon);
|
||||
times = SunCalc.getTimes(Date.now(), coord.lat, coord.lon);
|
||||
rise = times.sunrise.toString().split(" ")[4].substr(0,5);
|
||||
set = times.sunset.toString().split(" ")[4].substr(0,5);
|
||||
noonpos = SunCalc.getPosition(times.solarNoon, coord.lat, coord.lon);
|
||||
rise = "^" + times.sunrise.toString().split(" ")[4].substr(0,5);
|
||||
set = "v" + times.sunset.toString().split(" ")[4].substr(0,5);
|
||||
//noonpos = SunCalc.getPosition(times.solarNoon, coord.lat, coord.lon);
|
||||
} else {
|
||||
rise = null;
|
||||
set = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -152,11 +157,7 @@ function drawSeconds() {
|
|||
var da = d.toString().split(" ");
|
||||
|
||||
// default draw styles
|
||||
g.reset();
|
||||
g.setBgColor(g.theme.bg);
|
||||
|
||||
// drawSting centered
|
||||
g.setFontAlign(0, 0);
|
||||
g.reset().setBgColor(g.theme.bg).setFontAlign(0, 0);
|
||||
|
||||
// draw time
|
||||
var time = da[4].split(":");
|
||||
|
|
@ -187,11 +188,7 @@ function draw() {
|
|||
var da = d.toString().split(" ");
|
||||
|
||||
// default draw styles
|
||||
g.reset();
|
||||
g.setBgColor(g.theme.bg);
|
||||
|
||||
// drawSting centered
|
||||
g.setFontAlign(0, 0);
|
||||
g.reset().setBgColor(g.theme.bg).setFontAlign(0, 0);
|
||||
|
||||
// draw time
|
||||
var time = da[4].split(":");
|
||||
|
|
@ -255,35 +252,31 @@ function draw() {
|
|||
var date = [require("locale").dow(new Date(), 1), require("locale").date(new Date(), 1)];
|
||||
// For a single secondary timezone, draw it bigger and drop time zone to second line
|
||||
const xOffset = 30;
|
||||
g.setFont(font, secondaryTimeFontSize);
|
||||
g.drawString(`${hours}:${minutes}`, xyCenter, yposTime2, true);
|
||||
g.setFont(font, secondaryTimeZoneFontSize);
|
||||
g.drawString(offset[OFFSET_TIME_ZONE], xyCenter, yposTime2 + 30, true);
|
||||
g.setFont(font, secondaryTimeFontSize).drawString(`${hours}:${minutes}`, xyCenter, yposTime2, true);
|
||||
g.setFont(font, secondaryTimeZoneFontSize).drawString(offset[OFFSET_TIME_ZONE], xyCenter, yposTime2 + 30, true);
|
||||
|
||||
// draw Day, name of month, Date
|
||||
g.setFont(font, secondaryTimeZoneFontSize);
|
||||
g.drawString(date, xyCenter, yposDate, true);
|
||||
g.setFont(font, secondaryTimeZoneFontSize).drawString(date, xyCenter, yposDate, true);
|
||||
} else if (index < 3) {
|
||||
// For > 1 extra timezones, render as columns / rows
|
||||
g.setFont(font, secondaryRowColFontSize);
|
||||
g.setFontAlign(-1, 0);
|
||||
g.setFont(font, secondaryRowColFontSize).setFontAlign(-1, 0);
|
||||
g.drawString(
|
||||
offset[OFFSET_TIME_ZONE],
|
||||
xcol1,
|
||||
yposWorld + index * 15,
|
||||
true
|
||||
);
|
||||
g.setFontAlign(1, 0);
|
||||
g.drawString(`${hours}:${minutes}`, xcol2, yposWorld + index * 15, true);
|
||||
g.setFontAlign(1, 0).drawString(`${hours}:${minutes}`, xcol2, yposWorld + index * 15, true);
|
||||
}
|
||||
});
|
||||
|
||||
if (showSunInfo) {
|
||||
g.setFontAlign(-1, 0);
|
||||
g.setFont("Vector",12);
|
||||
g.drawString(`^${rise}`, 10, 3 + yposWorld + 3 * 15, true); // draw riseset
|
||||
g.setFontAlign(1, 0);
|
||||
g.drawString(`v${set}`, xcol2, 3 + yposWorld + 3 * 15, true); // draw riseset
|
||||
if (rise != null){
|
||||
g.setFontAlign(-1, 0).setFont("Vector",12).drawString(`${rise}`, 10, 3 + yposWorld + 3 * 15, true); // draw rise
|
||||
g.setFontAlign(1, 0).drawString(`${set}`, xcol2, 3 + yposWorld + 3 * 15, true); // draw set
|
||||
} else {
|
||||
g.setFontAlign(-1, 0).setFont("Vector",11).drawString("set city in \'my location\' app!", 10, 3 + yposWorld + 3 * 15, true);
|
||||
}
|
||||
}
|
||||
//debug settings
|
||||
//g.setFontAlign(1, 0);
|
||||
|
|
@ -291,7 +284,6 @@ function draw() {
|
|||
//g.drawString(showSunInfo, xcol2, 3 + yposWorld + 3 * 15, true);
|
||||
//g.drawString(colorWhenDark, xcol2, 3 + yposWorld + 3 * 15, true);
|
||||
|
||||
|
||||
queueDraw();
|
||||
|
||||
if (secondsMode != "none") queueDrawSeconds();
|
||||
|
|
@ -317,6 +309,7 @@ if (!Bangle.isLocked()) { // Initial state
|
|||
if (showSunInfo) {
|
||||
if (PosInterval != 0) clearInterval(PosInterval);
|
||||
PosInterval = setInterval(updatePos, 60*10E3); // refesh every 10 mins
|
||||
updatePos();
|
||||
}
|
||||
|
||||
secondsTimeout = 1000;
|
||||
|
|
@ -326,9 +319,8 @@ if (!Bangle.isLocked()) { // Initial state
|
|||
}
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = undefined;
|
||||
|
||||
draw(); // draw immediately, queue redraw
|
||||
if (showSunInfo) updatePos();
|
||||
|
||||
}else{
|
||||
if (secondsMode == "always") secondsTimeout = 1000;
|
||||
if (secondsMode == "when unlocked") secondsTimeout = 10 * 1000;
|
||||
|
|
@ -343,20 +335,19 @@ if (!Bangle.isLocked()) { // Initial state
|
|||
if (showSunInfo) {
|
||||
if (PosInterval != 0) clearInterval(PosInterval);
|
||||
PosInterval = setInterval(updatePos, 60*60E3); // refesh every 60 mins
|
||||
updatePos();
|
||||
}
|
||||
draw(); // draw immediately, queue redraw
|
||||
if (showSunInfo) updatePos();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Bangle.on('lock',on=>{
|
||||
if (!on) { // UNlocked
|
||||
if (showSunInfo) {
|
||||
if (PosInterval != 0) clearInterval(PosInterval);
|
||||
PosInterval = setInterval(updatePos, 60*10E3); // refesh every 10 mins
|
||||
updatePos();
|
||||
}
|
||||
|
||||
secondsTimeout = 1000;
|
||||
|
|
@ -368,7 +359,6 @@ Bangle.on('lock',on=>{
|
|||
drawTimeout = undefined;
|
||||
|
||||
draw(); // draw immediately, queue redraw
|
||||
if (showSunInfo) updatePos();
|
||||
}else{ // locked
|
||||
|
||||
if (secondsMode == "always") secondsTimeout = 1000;
|
||||
|
|
@ -381,9 +371,11 @@ Bangle.on('lock',on=>{
|
|||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = undefined;
|
||||
|
||||
if (PosInterval != 0) clearInterval(PosInterval);
|
||||
PosInterval = setInterval(updatePos, 60*60E3); // refesh every 60 mins
|
||||
if (showSunInfo) {
|
||||
if (PosInterval != 0) clearInterval(PosInterval);
|
||||
PosInterval = setInterval(updatePos, 60*60E3); // refesh every 60 mins
|
||||
updatePos();
|
||||
}
|
||||
draw(); // draw immediately, queue redraw
|
||||
if (showSunInfo) updatePos();
|
||||
}
|
||||
});
|
||||
|
|
@ -30,8 +30,8 @@
|
|||
}
|
||||
} catch(e){
|
||||
offsets=[
|
||||
[true,"London",0],
|
||||
[true,"NY",-5],
|
||||
[true,"London",1],
|
||||
[true,"NY",-4],
|
||||
[true, "Denver",-6],
|
||||
|
||||
];
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"id": "hworldclock",
|
||||
"name": "Hanks World Clock",
|
||||
"shortName": "Hanks World Clock",
|
||||
"version": "0.22",
|
||||
"version": "0.23",
|
||||
"description": "Current time zone plus up to three others",
|
||||
"allow_emulator":true,
|
||||
"icon": "app.png",
|
||||
|
|
|
|||
|
|
@ -9,3 +9,6 @@
|
|||
0.10: Bug fix
|
||||
0.11: Avoid too many notifications. Change disconnected colour to red.
|
||||
0.12: Prevent repeated execution of `draw()` from the current app.
|
||||
0.13: Added "connection restored" notification. Fixed restoring of the watchface.
|
||||
0.14: Added configuration option
|
||||
0.15: Added option to hide widget when connected
|
||||
|
|
@ -1,13 +1,17 @@
|
|||
{
|
||||
"id": "widbt_notify",
|
||||
"name": "Bluetooth Widget with Notification",
|
||||
"version": "0.12",
|
||||
"version": "0.15",
|
||||
"description": "Show the current Bluetooth connection status in the top right of the clock and vibrate when disconnected.",
|
||||
"icon": "widget.png",
|
||||
"type": "widget",
|
||||
"tags": "widget,bluetooth",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"storage": [
|
||||
{"name":"widbt_notify.wid.js","url":"widget.js"}
|
||||
]
|
||||
{"name":"widbt_notify.wid.js","url":"widget.js"},
|
||||
{"name":"widbt_notify.settings.js","url":"settings.js"}
|
||||
],
|
||||
"data": [
|
||||
{"name":"widbt_notify.json"}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,69 @@
|
|||
(function(back) {
|
||||
var FILE = "widbt_notify.json";
|
||||
var settings = Object.assign({
|
||||
secondsOnUnlock: false,
|
||||
}, require('Storage').readJSON(FILE, true) || {});
|
||||
|
||||
function writeSettings() {
|
||||
require('Storage').writeJSON(FILE, settings);
|
||||
}
|
||||
|
||||
// Helper method which uses int-based menu item for set of string values
|
||||
function stringItems(startvalue, writer, values) {
|
||||
return {
|
||||
value: (startvalue === undefined ? 0 : values.indexOf(startvalue)),
|
||||
format: v => values[v],
|
||||
min: 0,
|
||||
max: values.length - 1,
|
||||
wrap: true,
|
||||
step: 1,
|
||||
onchange: v => {
|
||||
writer(values[v]);
|
||||
writeSettings();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Helper method which breaks string set settings down to local settings object
|
||||
function stringInSettings(name, values) {
|
||||
return stringItems(settings[name], v => settings[name] = v, values);
|
||||
}
|
||||
|
||||
var mainmenu = {
|
||||
"": {
|
||||
"title": "Bluetooth Widget WN"
|
||||
},
|
||||
"< Back": () => back(),
|
||||
"Show Widget": {
|
||||
value: (settings.showWidget !== undefined ? settings.showWidget : true),
|
||||
onchange: v => {
|
||||
settings.showWidget = v;
|
||||
writeSettings();
|
||||
}
|
||||
},
|
||||
"Buzz on Connect": {
|
||||
value: (settings.buzzOnConnect !== undefined ? settings.buzzOnConnect : true),
|
||||
onchange: v => {
|
||||
settings.buzzOnConnect = v;
|
||||
writeSettings();
|
||||
}
|
||||
},
|
||||
"Buzz on loss": {
|
||||
value: (settings.buzzOnLoss !== undefined ? settings.buzzOnLoss : true),
|
||||
onchange: v => {
|
||||
settings.buzzOnLoss = v;
|
||||
writeSettings();
|
||||
}
|
||||
},
|
||||
"Hide connected": {
|
||||
value: (settings.hideConnected !== undefined ? settings.hideConnected : false),
|
||||
onchange: v => {
|
||||
settings.hideConnected = v;
|
||||
writeSettings();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
E.showMenu(mainmenu);
|
||||
|
||||
});
|
||||
|
|
@ -2,42 +2,106 @@ WIDGETS.bluetooth_notify = {
|
|||
area: "tr",
|
||||
width: 15,
|
||||
warningEnabled: 1,
|
||||
|
||||
// ------------ Settings -------- very lame - need to improve
|
||||
readshowWidget: function() {
|
||||
var showWidget;
|
||||
const SETTINGSFILE = "widbt_notify.json";
|
||||
function def (value, def) {return value !== undefined ? value : def;}
|
||||
var settings = require('Storage').readJSON(SETTINGSFILE, true) || {};
|
||||
showWidget = def(settings.showWidget, true);
|
||||
return showWidget;
|
||||
},
|
||||
|
||||
readBuzzOnConnect: function() {
|
||||
var buzzOnConnect;
|
||||
const SETTINGSFILE = "widbt_notify.json";
|
||||
function def (value, def) {return value !== undefined ? value : def;}
|
||||
var settings = require('Storage').readJSON(SETTINGSFILE, true) || {};
|
||||
buzzOnConnect = def(settings.buzzOnConnect, true);
|
||||
return buzzOnConnect;
|
||||
},
|
||||
|
||||
readBuzzOnLoss: function() {
|
||||
var buzzOnLoss;
|
||||
const SETTINGSFILE = "widbt_notify.json";
|
||||
function def (value, def) {return value !== undefined ? value : def;}
|
||||
var settings = require('Storage').readJSON(SETTINGSFILE, true) || {};
|
||||
buzzOnLoss = def(settings.buzzOnLoss, true);
|
||||
return buzzOnLoss;
|
||||
},
|
||||
|
||||
readHideConnected: function() {
|
||||
var hideConnected;
|
||||
const SETTINGSFILE = "widbt_notify.json";
|
||||
function def (value, def) {return value !== undefined ? value : def;}
|
||||
var settings = require('Storage').readJSON(SETTINGSFILE, true) || {};
|
||||
hideConnected = def(settings.hideConnected, true);
|
||||
return hideConnected;
|
||||
},
|
||||
|
||||
|
||||
// ------------ Settings --------
|
||||
|
||||
draw: function() {
|
||||
g.reset();
|
||||
if (NRF.getSecurityStatus().connected) {
|
||||
g.setColor((g.getBPP() > 8) ? "#07f" : (g.theme.dark ? "#0ff" : "#00f"));
|
||||
} else {
|
||||
// g.setColor(g.theme.dark ? "#666" : "#999");
|
||||
g.setColor("#f00"); // red is easier to distinguish from blue
|
||||
if (WIDGETS.bluetooth_notify.readshowWidget()){
|
||||
g.reset();
|
||||
if (NRF.getSecurityStatus().connected) {
|
||||
if (!WIDGETS.bluetooth_notify.readHideConnected()) {
|
||||
g.setColor((g.getBPP() > 8) ? "#07f" : (g.theme.dark ? "#0ff" : "#00f"));
|
||||
g.drawImage(atob("CxQBBgDgFgJgR4jZMawfAcA4D4NYybEYIwTAsBwDAA=="), 2 + this.x, 2 + this.y);
|
||||
}
|
||||
} else {
|
||||
// g.setColor(g.theme.dark ? "#666" : "#999");
|
||||
g.setColor("#f00"); // red is easier to distinguish from blue
|
||||
g.drawImage(atob("CxQBBgDgFgJgR4jZMawfAcA4D4NYybEYIwTAsBwDAA=="), 2 + this.x, 2 + this.y);
|
||||
}
|
||||
}
|
||||
g.drawImage(atob("CxQBBgDgFgJgR4jZMawfAcA4D4NYybEYIwTAsBwDAA=="), 2 + this.x, 2 + this.y);
|
||||
},
|
||||
|
||||
redrawCurrentApp: function(){
|
||||
if(typeof(draw)=='function'){
|
||||
g.clear();
|
||||
draw();
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
}else{
|
||||
load(); // fallback. This might reset some variables
|
||||
}
|
||||
},
|
||||
|
||||
connect: function() {
|
||||
WIDGETS.bluetooth_notify.draw();
|
||||
|
||||
if(WIDGETS.bluetooth_notify.warningEnabled == 1){
|
||||
E.showMessage(/*LANG*/'Connection\nrestored.', 'Bluetooth');
|
||||
setTimeout(()=>{WIDGETS.bluetooth_notify.redrawCurrentApp();}, 3000); // clear message - this will reload the widget, resetting 'warningEnabled'.
|
||||
|
||||
WIDGETS.bluetooth_notify.warningEnabled = 0;
|
||||
setTimeout('WIDGETS.bluetooth_notify.warningEnabled = 1;', 30000); // don't buzz for the next 30 seconds.
|
||||
|
||||
var quiet = (require('Storage').readJSON('setting.json',1)||{}).quiet;
|
||||
if(!quiet && WIDGETS.bluetooth_notify.readBuzzOnConnect()){
|
||||
Bangle.buzz(700, 1); // buzz on connection resume
|
||||
}
|
||||
}
|
||||
WIDGETS.bluetooth_notify.draw();
|
||||
|
||||
},
|
||||
|
||||
disconnect: function() {
|
||||
if(WIDGETS.bluetooth_notify.warningEnabled == 1){
|
||||
E.showMessage(/*LANG*/'Connection\nlost.', 'Bluetooth');
|
||||
setTimeout(()=>{WIDGETS.bluetooth_notify.redrawCurrentApp();}, 3000); // clear message - this will reload the widget, resetting 'warningEnabled'.
|
||||
|
||||
WIDGETS.bluetooth_notify.warningEnabled = 0;
|
||||
setTimeout('WIDGETS.bluetooth_notify.warningEnabled = 1;', 30000); // don't buzz for the next 30 seconds.
|
||||
|
||||
var quiet = (require('Storage').readJSON('setting.json',1)||{}).quiet;
|
||||
if(!quiet){
|
||||
Bangle.buzz(700, 1); // buzz on connection loss
|
||||
if(WIDGETS.bluetooth_notify.warningEnabled == 1){
|
||||
E.showMessage(/*LANG*/ 'Connection\nlost.', 'Bluetooth');
|
||||
setTimeout(()=>{WIDGETS.bluetooth_notify.redrawCurrentApp();}, 3000); // clear message - this will reload the widget, resetting 'warningEnabled'.
|
||||
|
||||
WIDGETS.bluetooth_notify.warningEnabled = 0;
|
||||
setTimeout('WIDGETS.bluetooth_notify.warningEnabled = 1;', 30000); // don't buzz for the next 30 seconds.
|
||||
|
||||
var quiet = (require('Storage').readJSON('setting.json',1)||{}).quiet;
|
||||
if(!quiet && WIDGETS.bluetooth_notify.readBuzzOnLoss()){
|
||||
Bangle.buzz(700, 1); // buzz on connection loss
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WIDGETS.bluetooth_notify.draw();
|
||||
}
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue