forked from FOSS/BangleApps
Merge branch 'espruino:master' into master
commit
ef29ff3709
|
@ -13,3 +13,4 @@
|
|||
0.12: Mark dated events on a day
|
||||
0.13: Switch to swipe left/right for month and up/down for year selection
|
||||
Display events for current month on touch
|
||||
0.14: Add support for holidays
|
||||
|
|
|
@ -10,6 +10,7 @@ Basic calendar
|
|||
- Swipe down (Bangle.js 2 only) to go to the next year
|
||||
- Touch to display events for current month
|
||||
- Press the button (button 3 on Bangle.js 1) to exit
|
||||
- Holidays have same color as weekends and can be edited with the 'Download'-interface, e.g. by uploading an iCalendar file.
|
||||
|
||||
## Settings
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ const white = "#ffffff";
|
|||
const red = "#d41706";
|
||||
const blue = "#0000ff";
|
||||
const yellow = "#ffff00";
|
||||
const cyan = "#00ffff";
|
||||
let bgColor = color4;
|
||||
let bgColorMonth = color1;
|
||||
let bgColorDow = color2;
|
||||
|
@ -23,6 +24,7 @@ let bgColorWeekend = color3;
|
|||
let fgOtherMonth = gray1;
|
||||
let fgSameMonth = white;
|
||||
let bgEvent = blue;
|
||||
let bgOtherEvent = "#ff8800";
|
||||
const eventsPerDay=6; // how much different events per day we can display
|
||||
const date = new Date();
|
||||
|
||||
|
@ -36,9 +38,17 @@ const events = (require("Storage").readJSON("sched.json",1) || []).filter(a => a
|
|||
date.setHours(time.h);
|
||||
date.setMinutes(time.m);
|
||||
date.setSeconds(time.s);
|
||||
return {date: date, msg: a.msg};
|
||||
return {date: date, msg: a.msg, type: "e"};
|
||||
});
|
||||
// add holidays & other events
|
||||
(require("Storage").readJSON("calendar.days.json",1) || []).forEach(d => {
|
||||
const date = new Date(d.date);
|
||||
const o = {date: date, msg: d.name, type: d.type};
|
||||
if (d.repeat) {
|
||||
o.repeat = d.repeat;
|
||||
}
|
||||
events.push(o);
|
||||
});
|
||||
events.sort((a,b) => a.date - b.date);
|
||||
|
||||
if (settings.ndColors === undefined) {
|
||||
settings.ndColors = !g.theme.dark;
|
||||
|
@ -52,68 +62,16 @@ if (settings.ndColors === true) {
|
|||
fgOtherMonth = blue;
|
||||
fgSameMonth = black;
|
||||
bgEvent = color2;
|
||||
bgOtherEvent = cyan;
|
||||
}
|
||||
|
||||
function getDowLbls(locale) {
|
||||
let dowLbls;
|
||||
//TODO: Find some clever way to generate this programmatically from locale lib
|
||||
switch (locale) {
|
||||
case "de_AT":
|
||||
case "de_CH":
|
||||
case "de_DE":
|
||||
if (startOnSun) {
|
||||
dowLbls = ["So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"];
|
||||
} else {
|
||||
dowLbls = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"];
|
||||
}
|
||||
break;
|
||||
case "nl_NL":
|
||||
if (startOnSun) {
|
||||
dowLbls = ["zo", "ma", "di", "wo", "do", "vr", "za"];
|
||||
} else {
|
||||
dowLbls = ["ma", "di", "wo", "do", "vr", "za", "zo"];
|
||||
}
|
||||
break;
|
||||
case "fr_BE":
|
||||
case "fr_CH":
|
||||
case "fr_FR":
|
||||
if (startOnSun) {
|
||||
dowLbls = ["Di", "Lu", "Ma", "Me", "Je", "Ve", "Sa"];
|
||||
} else {
|
||||
dowLbls = ["Lu", "Ma", "Me", "Je", "Ve", "Sa", "Di"];
|
||||
}
|
||||
break;
|
||||
case "sv_SE":
|
||||
if (startOnSun) {
|
||||
dowLbls = ["Di", "Lu", "Ma", "Me", "Je", "Ve", "Sa"];
|
||||
} else {
|
||||
dowLbls = ["Lu", "Ma", "Me", "Je", "Ve", "Sa", "Di"];
|
||||
}
|
||||
break;
|
||||
case "it_CH":
|
||||
case "it_IT":
|
||||
if (startOnSun) {
|
||||
dowLbls = ["Do", "Lu", "Ma", "Me", "Gi", "Ve", "Sa"];
|
||||
} else {
|
||||
dowLbls = ["Lu", "Ma", "Me", "Gi", "Ve", "Sa", "Do"];
|
||||
}
|
||||
break;
|
||||
case "oc_FR":
|
||||
if (startOnSun) {
|
||||
dowLbls = ["dg", "dl", "dm", "dc", "dj", "dv", "ds"];
|
||||
} else {
|
||||
dowLbls = ["dl", "dm", "dc", "dj", "dv", "ds", "dg"];
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (startOnSun) {
|
||||
dowLbls = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
|
||||
} else {
|
||||
dowLbls = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"];
|
||||
}
|
||||
break;
|
||||
}
|
||||
return dowLbls;
|
||||
let days = startOnSun ? [0, 1, 2, 3, 4, 5, 6] : [1, 2, 3, 4, 5, 6, 0];
|
||||
const d = new Date();
|
||||
return days.map(i => {
|
||||
d.setDate(d.getDate() + (i + 7 - d.getDay()) % 7);
|
||||
return require("locale").dow(d, 1);
|
||||
});
|
||||
}
|
||||
|
||||
function sameDay(d1, d2) {
|
||||
|
@ -206,8 +164,13 @@ function drawCalendar(date) {
|
|||
weekBeforeMonth.setDate(weekBeforeMonth.getDate() - 7);
|
||||
const week2AfterMonth = new Date(date.getFullYear(), date.getMonth()+1, 0);
|
||||
week2AfterMonth.setDate(week2AfterMonth.getDate() + 14);
|
||||
events.forEach(ev => {
|
||||
if (ev.repeat === "y") {
|
||||
ev.date.setFullYear(ev.date.getMonth() < 6 ? week2AfterMonth.getFullYear() : weekBeforeMonth.getFullYear());
|
||||
}
|
||||
});
|
||||
const eventsThisMonth = events.filter(ev => ev.date > weekBeforeMonth && ev.date < week2AfterMonth);
|
||||
|
||||
eventsThisMonth.sort((a,b) => a.date - b.date);
|
||||
let i = 0;
|
||||
for (y = 0; y < rowN - 1; y++) {
|
||||
for (x = 0; x < colN; x++) {
|
||||
|
@ -220,6 +183,33 @@ function drawCalendar(date) {
|
|||
const y1 = y * rowH + headerH + rowH;
|
||||
const x2 = x * colW + colW;
|
||||
const y2 = y * rowH + headerH + rowH + rowH;
|
||||
|
||||
if (eventsThisMonth.length > 0) {
|
||||
// Display events for this day
|
||||
eventsThisMonth.forEach((ev, idx) => {
|
||||
if (sameDay(ev.date, curDay)) {
|
||||
switch(ev.type) {
|
||||
case "e": // alarm/event
|
||||
const hour = ev.date.getHours() + ev.date.getMinutes()/60.0;
|
||||
const slice = hour/24*(eventsPerDay-1); // slice 0 for 0:00 up to eventsPerDay for 23:59
|
||||
const height = (y2-2) - (y1+2); // height of a cell
|
||||
const sliceHeight = height/eventsPerDay;
|
||||
const ystart = (y1+2) + slice*sliceHeight;
|
||||
g.setColor(bgEvent).fillRect(x1+1, ystart, x2-2, ystart+sliceHeight);
|
||||
break;
|
||||
case "h": // holiday
|
||||
g.setColor(bgColorWeekend).fillRect(x1+1, y1+1, x2-1, y2-1);
|
||||
break;
|
||||
case "o": // other
|
||||
g.setColor(bgOtherEvent).fillRect(x1+1, y1+1, x2-1, y2-1);
|
||||
break;
|
||||
}
|
||||
|
||||
eventsThisMonth.splice(idx, 1); // this event is no longer needed
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (isToday) {
|
||||
g.setColor(red);
|
||||
g.drawRect(x1, y1, x2, y2);
|
||||
|
@ -231,23 +221,6 @@ function drawCalendar(date) {
|
|||
);
|
||||
}
|
||||
|
||||
if (eventsThisMonth.length > 0) {
|
||||
// Display events for this day
|
||||
g.setColor(bgEvent);
|
||||
eventsThisMonth.forEach((ev, idx) => {
|
||||
if (sameDay(ev.date, curDay)) {
|
||||
const hour = ev.date.getHours() + ev.date.getMinutes()/60.0;
|
||||
const slice = hour/24*(eventsPerDay-1); // slice 0 for 0:00 up to eventsPerDay for 23:59
|
||||
const height = (y2-2) - (y1+2); // height of a cell
|
||||
const sliceHeight = height/eventsPerDay;
|
||||
const ystart = (y1+2) + slice*sliceHeight;
|
||||
g.fillRect(x1+1, ystart, x2-2, ystart+sliceHeight);
|
||||
|
||||
eventsThisMonth.splice(idx, 1); // this event is no longer needed
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
require("Font8x12").add(Graphics);
|
||||
g.setFont("8x12", fontSize);
|
||||
g.setColor(day < 50 ? fgOtherMonth : fgSameMonth);
|
||||
|
@ -286,10 +259,11 @@ function setUI() {
|
|||
},
|
||||
btn: (n) => n === (process.env.HWVERSION === 2 ? 1 : 3) && load(),
|
||||
touch: (n,e) => {
|
||||
events.sort((a,b) => a.date - b.date);
|
||||
const menu = events.filter(ev => ev.date.getFullYear() === date.getFullYear() && ev.date.getMonth() === date.getMonth()).map(e => {
|
||||
const dateStr = require("locale").date(e.date, 1);
|
||||
const timeStr = require("locale").time(e.date, 1);
|
||||
return { title: `${dateStr} ${timeStr}` + (e.msg ? " " + e.msg : "") };
|
||||
return { title: `${dateStr} ${e.type === "e" ? timeStr : ""}` + (e.msg ? " " + e.msg : "") };
|
||||
});
|
||||
if (menu.length === 0) {
|
||||
menu.push({title: /*LANG*/"No events"});
|
||||
|
|
|
@ -0,0 +1,234 @@
|
|||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="../../css/spectre.min.css">
|
||||
<link rel="stylesheet" href="../../css/spectre-icons.min.css">
|
||||
<script src="../../core/lib/interface.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/ical.js/1.5.0/ical.min.js"></script>
|
||||
<script>
|
||||
let dataElement = document.getElementById("data");
|
||||
let holidays;
|
||||
|
||||
function sameDay(d1, d2) {
|
||||
return d1.getFullYear() === d2.getFullYear() &&
|
||||
d1.getMonth() === d2.getMonth() &&
|
||||
d1.getDate() === d2.getDate();
|
||||
}
|
||||
|
||||
function render() {
|
||||
holidays.sort((a,b) => new Date(a.date) - new Date(b.date));
|
||||
document.getElementById('events').innerHTML = "";
|
||||
holidays.forEach(holiday => {
|
||||
renderHoliday(holiday);
|
||||
});
|
||||
}
|
||||
|
||||
function readFile(input) {
|
||||
document.getElementById('upload').disabled = true;
|
||||
|
||||
for(let i=0; i<input.files.length; i++) {
|
||||
const reader = new FileReader();
|
||||
reader.addEventListener("load", () => {
|
||||
const jCalData = ICAL.parse(reader.result);
|
||||
const comp = new ICAL.Component(jCalData);
|
||||
// Fetch the VEVENT part
|
||||
comp.getAllSubcomponents('vevent').forEach(vevent => {
|
||||
event = new ICAL.Event(vevent);
|
||||
holidays = holidays.filter(holiday => !sameDay(new Date(holiday.date), event.startDate.toJSDate())); // remove if already exists
|
||||
|
||||
const holiday = eventToHoliday(event);
|
||||
|
||||
holidays.push(holiday);
|
||||
});
|
||||
|
||||
render();
|
||||
}, false);
|
||||
|
||||
reader.readAsText(input.files[i], "UTF-8");
|
||||
}
|
||||
}
|
||||
|
||||
function eventToHoliday(event) {
|
||||
const date = event.startDate.toJSDate();
|
||||
|
||||
const holiday = {
|
||||
date: formatDate(date),
|
||||
name: event.summary,
|
||||
type: 'h',
|
||||
};
|
||||
return holiday;
|
||||
}
|
||||
|
||||
function formatDate(d) {
|
||||
return d.getFullYear() + "-" + (d.getMonth() + 1).toString().padStart(2, '0') + "-" + d.getDate().toString().padStart(2, '0');
|
||||
}
|
||||
|
||||
function upload() {
|
||||
Util.showModal("Saving...");
|
||||
Util.writeStorage("calendar.days.json", JSON.stringify(holidays), () => {
|
||||
location.reload(); // reload so we see current data
|
||||
});
|
||||
}
|
||||
|
||||
function renderHoliday(holiday) {
|
||||
const localDate = new Date(holiday.date);
|
||||
|
||||
const tr = document.createElement('tr');
|
||||
tr.classList.add('event-row');
|
||||
const tdTime = document.createElement('td');
|
||||
tr.appendChild(tdTime);
|
||||
const inputTime = document.createElement('input');
|
||||
inputTime.type = "date";
|
||||
inputTime.classList.add('event-date');
|
||||
inputTime.classList.add('form-input');
|
||||
inputTime.value = formatDate(localDate)
|
||||
inputTime.onchange = (e => {
|
||||
const date = new Date(inputTime.value);
|
||||
holiday.date = formatDate(date);
|
||||
});
|
||||
tdTime.appendChild(inputTime);
|
||||
|
||||
const tdSummary = document.createElement('td');
|
||||
tr.appendChild(tdSummary);
|
||||
const inputSummary = document.createElement('input');
|
||||
inputSummary.type = "text";
|
||||
inputSummary.classList.add('event-summary');
|
||||
inputSummary.classList.add('form-input');
|
||||
inputSummary.maxLength=40;
|
||||
const summary = (holiday.name?.substring(0, inputSummary.maxLength) || "");
|
||||
inputSummary.value = summary;
|
||||
inputSummary.onchange = (e => {
|
||||
holiday.name = inputSummary.value;
|
||||
});
|
||||
tdSummary.appendChild(inputSummary);
|
||||
inputSummary.onchange();
|
||||
|
||||
const tdType = document.createElement('td');
|
||||
tr.appendChild(tdType);
|
||||
const selectType = document.createElement("select");
|
||||
selectType.classList.add('form-select');
|
||||
tdType.prepend(selectType);
|
||||
const optionHoliday = document.createElement("option");
|
||||
optionHoliday.text = "Holiday";
|
||||
optionHoliday.value = "h";
|
||||
optionHoliday.selected = holiday.type === "h";
|
||||
selectType.add(optionHoliday);
|
||||
const optionOther = document.createElement("option");
|
||||
optionOther.text = "Other";
|
||||
optionOther.value = "o";
|
||||
optionOther.selected = holiday.type === "o";
|
||||
selectType.add(optionOther);
|
||||
selectType.onchange = (e => {
|
||||
holiday.type = e.target.value;
|
||||
});
|
||||
|
||||
const tdRepeat = document.createElement('td');
|
||||
tr.appendChild(tdRepeat);
|
||||
const selectRepeat = document.createElement("select");
|
||||
selectRepeat.classList.add('form-select');
|
||||
tdRepeat.prepend(selectRepeat);
|
||||
const optionNever = document.createElement("option");
|
||||
optionNever.text = "Never";
|
||||
optionNever.selected = !holiday.repeat;
|
||||
selectRepeat.add(optionNever);
|
||||
const optionYearly = document.createElement("option");
|
||||
optionYearly.text = "Yearly";
|
||||
optionYearly.value = "y";
|
||||
optionYearly.selected = holiday.repeat === "y";
|
||||
selectRepeat.add(optionYearly);
|
||||
selectRepeat.onchange = (e => {
|
||||
holiday.repeat = e.target.value;
|
||||
});
|
||||
|
||||
const tdAction = document.createElement('td');
|
||||
tr.appendChild(tdAction);
|
||||
|
||||
const buttonDelete = document.createElement('button');
|
||||
buttonDelete.classList.add('btn');
|
||||
buttonDelete.classList.add('btn-action');
|
||||
tdAction.prepend(buttonDelete);
|
||||
const iconDelete = document.createElement('i');
|
||||
iconDelete.classList.add('icon');
|
||||
iconDelete.classList.add('icon-delete');
|
||||
buttonDelete.appendChild(iconDelete);
|
||||
buttonDelete.onclick = (e => {
|
||||
holidays = holidays.filter(a => a !== holiday);
|
||||
document.getElementById('events').removeChild(tr);
|
||||
});
|
||||
|
||||
document.getElementById('events').appendChild(tr);
|
||||
document.getElementById('upload').disabled = false;
|
||||
}
|
||||
|
||||
function addHoliday() {
|
||||
const holiday = {date: formatDate(new Date()), type: 'h'};
|
||||
renderHoliday(holiday);
|
||||
holidays.push(holiday);
|
||||
render();
|
||||
}
|
||||
|
||||
function getData() {
|
||||
Util.showModal("Loading...");
|
||||
Puck.write(`\x10(function() {
|
||||
Bluetooth.print(JSON.stringify(require("Storage").list("calendar.days.json").sort()));
|
||||
})()\n`, contents => {
|
||||
const fileNames = JSON.parse(contents);
|
||||
if (fileNames.length > 0) {
|
||||
Util.readStorage('calendar.days.json',data=>{
|
||||
holidays = JSON.parse(data || "[]") || [];
|
||||
|
||||
Util.hideModal();
|
||||
render();
|
||||
});
|
||||
} else {
|
||||
holidays = [];
|
||||
Util.hideModal();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Called when app starts
|
||||
function onInit() {
|
||||
getData();
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h4 class="float-left">Holidays</h4>
|
||||
|
||||
<div class="float-right">
|
||||
<button class="btn" onclick="addHoliday();">
|
||||
<i class="icon icon-plus"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<table class="table table-scroll" style="clear:both;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th>Holiday</th>
|
||||
<th>Type</th>
|
||||
<th>Repeat</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="events">
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="divider"></div>
|
||||
<div class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<div class="col-5 col-xs-12">
|
||||
<label class="form-label" for="fileinput">Add from iCalendar file</label>
|
||||
</div>
|
||||
<div class="col-7 col-xs-12">
|
||||
<input id="fileinput" class="form-input" type="file" onchange="readFile(this)" accept=".ics,.ifb,.ical,.ifbf" multiple/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="divider"></div>
|
||||
|
||||
<button id="upload" class="btn btn-primary" onClick="upload()" disabled>Upload</button>
|
||||
<button id="reload" class="btn" onClick="location.reload()">Reload</button>
|
||||
</body>
|
||||
</html>
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "calendar",
|
||||
"name": "Calendar",
|
||||
"version": "0.13",
|
||||
"version": "0.14",
|
||||
"description": "Simple calendar",
|
||||
"icon": "calendar.png",
|
||||
"screenshots": [{"url":"screenshot_calendar.png"}],
|
||||
|
@ -9,10 +9,11 @@
|
|||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"allow_emulator": true,
|
||||
"interface": "interface.html",
|
||||
"storage": [
|
||||
{"name":"calendar.app.js","url":"calendar.js"},
|
||||
{"name":"calendar.settings.js","url":"settings.js"},
|
||||
{"name":"calendar.img","url":"calendar-icon.js","evaluate":true}
|
||||
],
|
||||
"data": [{"name":"calendar.json"}]
|
||||
"data": [{"name":"calendar.json"}, {"name":"calendar.days.json"}]
|
||||
}
|
||||
|
|
|
@ -9,4 +9,4 @@
|
|||
0.07: Convert Yes/No On/Off in settings to checkboxes
|
||||
0.08: Fix the wrapping of intervals/timeouts with parameters
|
||||
Fix the widget drawing if widgets are hidden and Bangle.setLCDBrightness is called
|
||||
0.09: Cache the app-launch info
|
||||
0.09: Accidental version bump
|
|
@ -1 +1,2 @@
|
|||
0.01: Initial release
|
||||
0.02: Cache the app-launch info
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"id": "ratchet_launch",
|
||||
"name": "Ratchet Launcher",
|
||||
"shortName": "Ratchet",
|
||||
"version": "0.01",
|
||||
"version": "0.02",
|
||||
"description": "Launcher with discrete scrolling for quicker app selection",
|
||||
"icon": "app.png",
|
||||
"type": "launch",
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<link rel="stylesheet" href="../../css/spectre.min.css">
|
||||
<link rel="stylesheet" href="../../css/spectre-icons.min.css">
|
||||
<script src="../../core/lib/interface.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/ical.js/0.0.3/ical.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/ical.js/1.5.0/ical.min.js"></script>
|
||||
<script>
|
||||
let dataElement = document.getElementById("data");
|
||||
let alarms;
|
||||
|
@ -17,7 +17,7 @@ function readFile(input) {
|
|||
const reader = new FileReader();
|
||||
reader.addEventListener("load", () => {
|
||||
const jCalData = ICAL.parse(reader.result);
|
||||
const comp = new ICAL.Component(jCalData[1]);
|
||||
const comp = new ICAL.Component(jCalData);
|
||||
// Fetch the VEVENT part
|
||||
comp.getAllSubcomponents('vevent').forEach(vevent => {
|
||||
event = new ICAL.Event(vevent);
|
||||
|
@ -50,13 +50,17 @@ function dateFromAlarm(alarm) {
|
|||
return new Date(date.getTime() + alarm.t);
|
||||
}
|
||||
|
||||
function formatDate(d) {
|
||||
return d.getFullYear() + "-" + (d.getMonth() + 1).toString().padStart(2, '0') + "-" + d.getDate().toString().padStart(2, '0');
|
||||
}
|
||||
|
||||
function getAlarmDefaults() {
|
||||
const date = new Date();
|
||||
return {
|
||||
on: true,
|
||||
t: dateToMsSinceMidnight(date),
|
||||
dow: 127,
|
||||
date: date.toISOString().substring(0,10),
|
||||
date: formatDate(date),
|
||||
last: 0,
|
||||
rp: "defaultRepeat" in schedSettings ? schedSettings.defaultRepeat : false,
|
||||
vibrate: "defaultAlarmPattern" in schedSettings ? schedSettings.defaultAlarmPattern : "::",
|
||||
|
@ -72,7 +76,7 @@ function eventToAlarm(event, offsetMs) {
|
|||
id: event.uid,
|
||||
msg: event.summary,
|
||||
t: dateToMsSinceMidnight(date),
|
||||
date: date.toISOString().substring(0,10),
|
||||
date: formatDate(date),
|
||||
data: {end: event.endDate.toJSDate().toISOString()}
|
||||
}};
|
||||
if (offsetMs) { // Alarm time is not real event time, so do a backup
|
||||
|
@ -105,7 +109,7 @@ function renderAlarm(alarm, exists) {
|
|||
inputTime.onchange = (e => {
|
||||
const date = new Date(inputTime.value);
|
||||
alarm.t = dateToMsSinceMidnight(date);
|
||||
alarm.date = date.toISOString().substring(0,10);
|
||||
alarm.date = formatDate(date);
|
||||
});
|
||||
tdTime.appendChild(inputTime);
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
0.01: New app!
|
||||
0.02: Bug fixes
|
||||
0.03: Submitted to the app loader
|
||||
0.04: Reduce battery consumption when the user's not looking
|
|
@ -8,7 +8,7 @@ const BUTTON_ICONS = {
|
|||
reset: heatshrink.decompress(atob("jEYwMA/4BB/+BAQPDAQPnAQIAKv///0///8j///EP//wAQQICBwQUCEhgyCHAQ+CIgI="))
|
||||
};
|
||||
|
||||
let state = storage.readJSON(STATE_PATH);
|
||||
let state = storage.readJSON(STATE_PATH, 1);
|
||||
const STATE_DEFAULT = {
|
||||
wasRunning: false, //If the stopwatch was ever running since being reset
|
||||
sessionStart: 0, //When the stopwatch was first started
|
||||
|
@ -157,7 +157,7 @@ function firstTimeStart(now, time) {
|
|||
elapsedTime: 0,
|
||||
};
|
||||
lapFile = 'stlap-' + state.sessionStart + '.json';
|
||||
setupTimerInterval();
|
||||
setupTimerIntervalFast();
|
||||
Bangle.buzz(200);
|
||||
drawButtons();
|
||||
}
|
||||
|
@ -201,13 +201,15 @@ function start(now, time) {
|
|||
state.elapsedTime += (state.pausedTime - state.startTime);
|
||||
state.startTime = now;
|
||||
state.running = true;
|
||||
setupTimerInterval();
|
||||
setupTimerIntervalFast();
|
||||
Bangle.buzz(200);
|
||||
drawTime();
|
||||
drawButtons();
|
||||
}
|
||||
|
||||
Bangle.on("touch", (button, xy) => {
|
||||
setupTimerIntervalFast();
|
||||
|
||||
//In gesture mode, just turn on the light and then return
|
||||
if (gestureMode) {
|
||||
Bangle.setLCDPower(true);
|
||||
|
@ -242,6 +244,8 @@ Bangle.on("touch", (button, xy) => {
|
|||
});
|
||||
|
||||
Bangle.on('swipe', direction => {
|
||||
setupTimerIntervalFast();
|
||||
|
||||
let now = (new Date()).getTime();
|
||||
let time = getTime();
|
||||
|
||||
|
@ -272,12 +276,23 @@ setWatch(() => {
|
|||
}, BTN1, { repeat: true });
|
||||
|
||||
let timerInterval;
|
||||
let userWatching = false;
|
||||
|
||||
function setupTimerIntervalFast() {
|
||||
userWatching = true;
|
||||
setupTimerInterval();
|
||||
|
||||
setTimeout(() => {
|
||||
userWatching = false;
|
||||
setupTimerInterval();
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
function setupTimerInterval() {
|
||||
if (timerInterval !== undefined) {
|
||||
clearInterval(timerInterval);
|
||||
}
|
||||
timerInterval = setInterval(drawTime, 10);
|
||||
timerInterval = setInterval(drawTime, userWatching ? 10 : 1000);
|
||||
}
|
||||
|
||||
function stopTimerInterval() {
|
||||
|
@ -289,7 +304,7 @@ function stopTimerInterval() {
|
|||
|
||||
drawTime();
|
||||
if (state.running) {
|
||||
setupTimerInterval();
|
||||
setupTimerIntervalFast();
|
||||
}
|
||||
|
||||
//Save our state when the app is closed
|
||||
|
@ -300,5 +315,8 @@ E.on('kill', () => {
|
|||
}
|
||||
});
|
||||
|
||||
// change interval depending of whether the user's looking
|
||||
Bangle.on("twist", setupTimerIntervalFast);
|
||||
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "stlap",
|
||||
"name": "Stopwatch",
|
||||
"version": "0.03",
|
||||
"version": "0.04",
|
||||
"description": "A stopwatch that remembers its state, with a lap timer and a gesture mode (enable by swiping)",
|
||||
"icon": "icon.png",
|
||||
"type": "app",
|
||||
|
@ -20,5 +20,9 @@
|
|||
"url": "icon.js",
|
||||
"evaluate": true
|
||||
}
|
||||
],
|
||||
"data": [
|
||||
{"name":"stlap.state.json"},
|
||||
{"wildcard":"stlap-*.json"}
|
||||
]
|
||||
}
|
|
@ -1 +1,2 @@
|
|||
0.01: Initial release
|
||||
0.02: Disallow emulator
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "widtemp",
|
||||
"name": "Temperature widget",
|
||||
"version": "0.01",
|
||||
"version": "0.02",
|
||||
"description": "A temperature widget, showing the current internal temperature",
|
||||
"readme": "README.md",
|
||||
"icon": "widtemp.png",
|
||||
|
@ -10,7 +10,7 @@
|
|||
"tags": "widget,health",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"dependencies" : {},
|
||||
"allow_emulator":true,
|
||||
"allow_emulator":false,
|
||||
"storage": [
|
||||
{"name":"widtemp.wid.js","url":"widtemp.wid.js"}
|
||||
]
|
||||
|
|
Loading…
Reference in New Issue