|
var appo = { |
|
// (A) PROPERTIES |
|
// (A1) INDEXED DB |
|
iName : "jsAppo", // idb name |
|
iDB : null, iTX : null, // idb objects |
|
|
|
// (A2) HTML ELEMENTS |
|
hNow : null, // html current year month |
|
hList : null, // html appointment list |
|
hForm : null, // html appointment form |
|
fID : null, fFrom : null, fTo : null, // html form |
|
fTxt : null, fTC : null, fBC : null, // html form |
|
|
|
// (A3) PERIOD |
|
month : null, // current month |
|
year : null, // current year |
|
days : null, // days in month for current month/year |
|
|
|
// (B) INIT |
|
init : () => { |
|
// (B1) IDB SUPPORT CHECK |
|
window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB; |
|
if (!window.indexedDB) { |
|
alert("Your browser does not support indexed database."); |
|
return; |
|
} |
|
|
|
// (B2) OPEN "MYLIB" DATABASE |
|
let req = window.indexedDB.open(appo.iName, 1); |
|
|
|
// (B3) ON DATABASE ERROR |
|
req.onerror = evt => { |
|
alert("Indexed DB init error - " + evt.message); |
|
console.error(evt); |
|
}; |
|
|
|
// (B4) UPGRADE NEEDED |
|
req.onupgradeneeded = evt => { |
|
// (B4-1) INIT UPGRADE |
|
appo.iDB = evt.target.result; |
|
appo.iDB.onerror = evt => { |
|
alert("Indexed DB upgrade error - " + evt.message); |
|
console.error(evt); |
|
}; |
|
|
|
// (B4-2) VERSION 1 |
|
if (evt.oldVersion < 1) { |
|
let store = appo.iDB.createObjectStore(appo.iName, { |
|
keyPath: "id", |
|
autoIncrement: true |
|
}); |
|
store.createIndex("from", "from"); |
|
store.createIndex("to", "to"); |
|
} |
|
}; |
|
|
|
// (B5) OPEN DATABASE OK |
|
req.onsuccess = evt => { |
|
// (B5-1) REGISTER IDB OBJECTS |
|
appo.iDB = evt.target.result; |
|
appo.iTX = () => { |
|
return appo.iDB |
|
.transaction(appo.iName, "readwrite") |
|
.objectStore(appo.iName); |
|
}; |
|
|
|
// (B5-2) GET HTML ELEMENTS |
|
appo.hNow = document.getElementById("appoNow"); |
|
appo.hList = document.getElementById("appoList"); |
|
appo.hForm = document.getElementById("appoFormWrap"); |
|
appo.fID = document.getElementById("formID"); |
|
appo.fFrom = document.getElementById("formFrom"); |
|
appo.fTo = document.getElementById("formTo"); |
|
appo.fTxt = document.getElementById("formTxt"); |
|
appo.fTC = document.getElementById("formTC"); |
|
appo.fBC = document.getElementById("formBC"); |
|
|
|
// (B5-3) CURRENT DATE |
|
let today = new Date(); |
|
appo.month = today.getMonth() + 1; // jan is 0! |
|
appo.year = today.getFullYear(); |
|
|
|
// (B5-4) DRAW APPOINTMENTS |
|
appo.draw(); |
|
}; |
|
}, |
|
|
|
// (C) HELPER FUNCTION - DATE TO UNIX TIMESTAMP |
|
dtu : date => Math.floor((+new Date(date)) / 1000), |
|
|
|
// (D) HELPER FUNCTION - UNIX TIMESTAMP TO DATE |
|
utd : unix => new Date(unix*1000).toISOString().substring(0,16), |
|
|
|
// (E) DRAW APPOINTMENTS FOR SELECTED MONTH |
|
draw : () => { |
|
// (E1) CURRENT MONTH YEAR |
|
let months = ["", "Jan", "Feb", "Mar", "Apr", "May", "Jun", |
|
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; |
|
appo.hNow.innerHTML = `${months[appo.month]} ${appo.year}`; |
|
delete months; |
|
|
|
// (E2) FIRST ROW - DAYS IN MONTH |
|
let cell; |
|
appo.days = new Date(appo.year, appo.month, 0).getDate(); |
|
appo.hList.style.gridTemplateColumns = `repeat(${appo.days}, 100px)`; |
|
appo.hList.innerHTML = ""; |
|
for (let d=1; d<=appo.days; d++) { |
|
cell = document.createElement("div"); |
|
cell.className = "cell head"; |
|
cell.innerHTML = d; |
|
appo.hList.append(cell); |
|
} |
|
|
|
// (E3) FOLLOWING ROWS - APPOINTMENT EVENTS |
|
// (E3-1) FLAGS & VARS |
|
let first = appo.dtu(`${appo.year}-${appo.month<10?"0"+appo.month:appo.month}-01T00:00`), |
|
last = appo.dtu(`${appo.year}-${appo.month<10?"0"+appo.month:appo.month}-${appo.days}T00:00`), |
|
idx = null, more = true, drawn = {}, row = 2; |
|
|
|
// (E3-2) HELPER FUNCTION - DRAW APPOINTMENT EVENT |
|
let rower = evt => { |
|
const cursor = evt.target.result; |
|
if (cursor) { |
|
if (drawn[cursor.value.id]==undefined) { |
|
let cx = cursor.value.from < first ? 1 : new Date(cursor.value.from * 1000).getDate(), |
|
cy = cursor.value.to > last ? appo.days + 1 : new Date(cursor.value.to * 1000).getDate() + 1; |
|
|
|
cell = document.createElement("div"); |
|
cell.className = "row"; |
|
cell.style.gridRow = row; |
|
cell.style.gridColumn = `${cx}/${cy}`; |
|
cell.style.color = cursor.value.tc; |
|
cell.style.background = cursor.value.bc; |
|
cell.innerHTML = `<div class="mi del" onclick="appo.del(${cursor.value.id});">clear</div> |
|
<div class="txt" onclick="appo.toggle(${cursor.value.id});">${cursor.value.txt}</div>`; |
|
appo.hList.append(cell); |
|
drawn[cursor.value.id] = 1; row++; |
|
} |
|
cursor.continue(); |
|
} else { if (more) { |
|
more = false; |
|
idx = appo.iTX().index("to").openCursor(range = IDBKeyRange.bound(first, last)); |
|
idx.onsuccess = rower; |
|
}} |
|
}; |
|
|
|
// (E3-3) GET & DRAW ENTRIES |
|
idx = appo.iTX().index("from").openCursor(range = IDBKeyRange.bound(first, last)); |
|
idx.onsuccess = rower; |
|
}, |
|
|
|
// (F) CYCLE MONTH |
|
cycle : next => { |
|
if (next) { appo.month++; } else { appo.month--; } |
|
if (appo.month==13) { appo.month = 1; appo.year++; } |
|
if (appo.month==0) { appo.month = 12; appo.year--; } |
|
appo.draw(); |
|
}, |
|
|
|
// (G) TOGGLE APPOINTMENT FORM |
|
toggle : id => { |
|
// (G1) HIDE |
|
if (id == false) { |
|
appo.hForm.classList.remove("show"); |
|
appo.fID.value = ""; |
|
appo.fFrom.value = ""; |
|
appo.fTo.value = ""; |
|
appo.fTxt.value = ""; |
|
appo.fTC.value = "#ffffff"; |
|
appo.fBC.value = "#000000"; |
|
} |
|
|
|
// (G2) SHOW |
|
else { |
|
if (Number.isInteger(id)) { |
|
let req = appo.iTX().get(id); |
|
req.onsuccess = evt => { |
|
appo.fID.value = req.result.id; |
|
appo.fFrom.value = appo.utd(req.result.from); |
|
appo.fTo.value = appo.utd(req.result.to); |
|
appo.fTxt.value = req.result.txt; |
|
appo.fTC.value = req.result.tc; |
|
appo.fBC.value = req.result.bc; |
|
}; |
|
} |
|
appo.hForm.classList.add("show"); |
|
} |
|
}, |
|
|
|
// (H) SAVE APPOINTMENT EVENT |
|
save : () => { |
|
// (H1) FORM DATA |
|
let data = { |
|
id : appo.fID.value, |
|
from : appo.dtu(appo.fFrom.value), |
|
to : appo.dtu(appo.fTo.value), |
|
txt : appo.fTxt.value, |
|
tc : appo.fTC.value, |
|
bc : appo.fBC.value |
|
}; |
|
|
|
// (H2) DATE/TIME CHECK |
|
if (data.to < data.from) { |
|
alert("'Date From' cannot be later than 'Date To'"); |
|
return false; |
|
} |
|
|
|
// (H3) SAVE EVENT |
|
if (data.id == "") { delete data.id; } |
|
else { data.id = parseInt(data.id); } |
|
appo.iTX().put(data); |
|
|
|
// (H4) UPDATE HTML INTERFACE |
|
appo.toggle(false); |
|
appo.draw(); |
|
return false; |
|
}, |
|
|
|
// (I) DELETE APPOINTMENT EVENT |
|
del : id => { if (confirm("Delete event?")) { |
|
appo.iTX().delete(id); |
|
appo.draw(); |
|
}} |
|
}; |
|
window.onload = appo.init; |