946 lines
24 KiB
JavaScript
946 lines
24 KiB
JavaScript
const obsidian = require('obsidian');
|
|
const Plugin = obsidian.Plugin;
|
|
const ItemView = obsidian.ItemView;
|
|
const getIcon = obsidian.getIcon;
|
|
|
|
const DateFormat = {
|
|
months: ["januarie", "februarie", "maart", "april", "mei", "juni", "juli", "augustis", "september", "oktober", "november", "december"],
|
|
daysLong: ["Maandag", "Dinsdag", "Woensdag", "Donderdag", "Vrijdag", "Zaterdag", "Zondag"],
|
|
week: "y[0-9]{2}w[0-9]{2}",
|
|
days: ["zo", "ma", "di", "wo", "do", "vrij", "za"],
|
|
time: "[0-9]{2}:[0-9]{2}[ap]m",
|
|
date: ""
|
|
}
|
|
DateFormat.date = "(?:" + DateFormat.week + ")?(?: ?(?:" + DateFormat.days.join('|') + ") ?)?(?:" + DateFormat.time + ")?";
|
|
|
|
function parseSingleDate(d, defal)
|
|
{
|
|
let year = -1, week = -1, dow = -1, time = moment(defal);
|
|
|
|
d = d.toLowerCase();
|
|
|
|
// year
|
|
let res = d.match(/y([0-9]{2})/);
|
|
if (res !== null)
|
|
{
|
|
year = parseInt(res[1]) + 2000;
|
|
}
|
|
else
|
|
{
|
|
year = defal.isoWeekYear();
|
|
}
|
|
|
|
// week
|
|
res = d.match(/w([0-9]{2})/);
|
|
if (res !== null)
|
|
{
|
|
week = parseInt(res[1]);
|
|
}
|
|
else
|
|
{
|
|
week = defal.isoWeek();
|
|
}
|
|
|
|
// day of week
|
|
res = d.split(' ');
|
|
for (const p in res)
|
|
{
|
|
let i = DateFormat.days.indexOf(res[p]);
|
|
if (i != -1){
|
|
dow = i;
|
|
break;
|
|
}
|
|
}
|
|
if (dow == -1)
|
|
{
|
|
dow = defal.day()
|
|
}
|
|
|
|
// time
|
|
res = d.match(/([0-9]{2}:[0-9]{2}[ap]m)/);
|
|
if (res !== null)
|
|
{
|
|
time = moment(res[1], "hh:mma");
|
|
}
|
|
|
|
// combine
|
|
time.day(dow);
|
|
time.isoWeek(week);
|
|
time.isoWeekYear(year);
|
|
return time;
|
|
}
|
|
|
|
function stringifySingleDate(date)
|
|
{
|
|
let week = date.format("[y]GG[w]WW");
|
|
let dow = DateFormat.days[date.day()];
|
|
let time = date.format("hh:mma");
|
|
return [week, dow, time].join(' ');
|
|
}
|
|
|
|
function stringifyDate(date)
|
|
{
|
|
let res = stringifySingleDate(date[0]);
|
|
if (date[1].diff(date[0]) > 1000)
|
|
{
|
|
res += "-" + stringifySingleDate(date[1])
|
|
}
|
|
return res;
|
|
}
|
|
|
|
function parseDate(d, defal)
|
|
{
|
|
let start, end;
|
|
let res = d.match(/([^-]+)-([^-]+)/);
|
|
if (res !== null)
|
|
{
|
|
start = parseSingleDate(res[1], defal);
|
|
end = parseSingleDate(res[2], start);
|
|
}
|
|
else
|
|
{
|
|
start = parseSingleDate(d, defal);
|
|
end = start;
|
|
}
|
|
return [start, end]
|
|
}
|
|
|
|
function scanEditor(editor, data)
|
|
{
|
|
let lineNum = 0, lineCount = editor.lineCount();
|
|
let defal = parseSingleDate("00:00am", moment());
|
|
|
|
while ((lineNum < lineCount))
|
|
{
|
|
let res, line = editor.getLine(lineNum);
|
|
|
|
res = line.match("weekboek: \"?(" + DateFormat.date + ")\"?");
|
|
if (res != null)
|
|
{
|
|
defal = parseSingleDate(res[1], defal);
|
|
}
|
|
|
|
res = [...line.matchAll("\\[(" + DateFormat.date + "(?:-" + DateFormat.date + ")?)\\]")];
|
|
if (res.length > 0)
|
|
{
|
|
if (res[0][0] == "[-]" && res.length > 1)
|
|
{
|
|
res[0] = res[1];
|
|
}
|
|
if (res[0][0] != "[-]")
|
|
{
|
|
res = res[0]
|
|
res[1] = parseDate(res[1], defal);
|
|
let procesed = processLine(line, line.replace(res[0], ''), res[1], data);
|
|
data = procesed['data'];
|
|
editor.setLine(lineNum, procesed['line']);
|
|
}
|
|
}
|
|
|
|
lineNum++;
|
|
}
|
|
return data;
|
|
}
|
|
|
|
function scanFile(content, data)
|
|
{
|
|
let lines = content.split('\n');
|
|
let defal = parseSingleDate("00:00am", moment());
|
|
|
|
for (let lineNum in lines)
|
|
{
|
|
let res = lines[lineNum].match("weekboek: \"?(" + DateFormat.date + ")\"?");
|
|
if (res != null)
|
|
{
|
|
defal = parseSingleDate(res[1], defal);
|
|
}
|
|
|
|
res = [...lines[lineNum].matchAll("\\[(" + DateFormat.date + "(?:-" + DateFormat.date + ")?)\\]")];
|
|
if (res.length > 0)
|
|
{
|
|
if (res[0][0] == "[-]" && res.length > 1)
|
|
{
|
|
res[0] = res[1];
|
|
}
|
|
if (res[0][0] != "[-]")
|
|
{
|
|
res = res[0]
|
|
res[1] = parseDate(res[1], defal);
|
|
let procesed = processLine(lines[lineNum], lines[lineNum].replace(res[0], ''), res[1], data);
|
|
data = procesed['data'];
|
|
lines[lineNum] = procesed['line'];
|
|
}
|
|
}
|
|
}
|
|
return { data: data, content: lines.join('\n') };
|
|
}
|
|
|
|
function processLine(line, noDate, date, data)
|
|
{
|
|
let res;
|
|
let item = {
|
|
"id": null,
|
|
"date": stringifyDate(date),
|
|
"title": "Untitled",
|
|
"group": "default",
|
|
"location": "",
|
|
"state": "reminder",
|
|
"type": "reminder"
|
|
};
|
|
|
|
// id
|
|
res = noDate.match(/<([a-z0-9]+)\/>/);
|
|
if (res != null)
|
|
{
|
|
item["id"] = res[1];
|
|
noDate = noDate.replace(res[0], '');
|
|
}
|
|
else
|
|
{
|
|
item["id"] = generateId();
|
|
line += " <" + item["id"] + "/>";
|
|
}
|
|
|
|
// location
|
|
res = noDate.match(/\{([^\}\{]+)\}/);
|
|
if (res != null)
|
|
{
|
|
item["location"] = res[1];
|
|
noDate = noDate.replace(res[0], '');
|
|
}
|
|
|
|
// state (checkbox)
|
|
res = noDate.match(/^[ \t]*- \[(.)\] /);
|
|
if (res != null)
|
|
{
|
|
item['type'] = "task";
|
|
item['state'] = res[1];
|
|
noDate = noDate.replace('[' + res[1] + '] ', '');
|
|
}
|
|
|
|
|
|
noDate = noDate.replace(/[ \t][ \t]+/g, ' '); // remve multeple spaces/tabs in a row
|
|
|
|
// title and group
|
|
res = noDate.match(/^ ?- (.*) - (.*)/);
|
|
if (res != null)
|
|
{
|
|
item["group"] = res[1];
|
|
item["title"] = res[2];
|
|
}
|
|
else
|
|
{
|
|
// no group look for only title
|
|
res = noDate.match(/^ ?- (.*)/);
|
|
if (res != null)
|
|
{
|
|
item["title"] = res[1];
|
|
}
|
|
else
|
|
{
|
|
item["title"] = noDate;
|
|
}
|
|
}
|
|
// remove leading and ending spaces
|
|
item["title"] = item['title'].replaceAll(/(^ +)?( +$)?/g, '')
|
|
item["group"] = item['group'].replaceAll(/(^ +)?( +$)?/g, '')
|
|
|
|
// add/update to database
|
|
if (data == null) data = {};
|
|
data[item["id"]] = item;
|
|
|
|
return { line: line, data: data};
|
|
}
|
|
|
|
function generateId()
|
|
{
|
|
let id = '';
|
|
const chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
|
let l = 0;
|
|
while (l < 10) {
|
|
id += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
l += 1;
|
|
}
|
|
return id;
|
|
}
|
|
|
|
const VIEW_TYPE_CALENDAR = "fr-calendar"
|
|
|
|
class CalendarView extends ItemView
|
|
{
|
|
constructor(leaf, plugin)
|
|
{
|
|
super(leaf);
|
|
this.plugin = plugin;
|
|
this.week = stringifySingleDate(moment()).match(DateFormat.week)[0];
|
|
this.zoom = 30/60; // in px/min
|
|
this.dayViewIndex = -1;
|
|
}
|
|
|
|
getViewType()
|
|
{
|
|
return VIEW_TYPE_CALENDAR;
|
|
}
|
|
|
|
getDisplayText()
|
|
{
|
|
return "Calender " + this.week;
|
|
}
|
|
|
|
async onOpen()
|
|
{
|
|
const container = this.containerEl.children[1];
|
|
container.empty();
|
|
this.eventsEls = {};
|
|
container.style = "--frcal-zoom: " + this.zoom + "px";
|
|
|
|
// mount view
|
|
this.mountView = container.createEl('div', { attr: { style: "height:0" } }).createEl("div", { cls: "frcal__mountView" });
|
|
|
|
// month
|
|
this.monthEl = container.createEl("div", { cls: "frcal__month" });
|
|
|
|
let today = this.monthEl.createEl("div", { cls: "frcal__time" });
|
|
today.append(getIcon('calendar-days'));
|
|
today.addEventListener('click', function(e) {
|
|
this.gotoThisWeek();
|
|
}.bind(this));
|
|
this.mountFirstEl = this.monthEl.createEl("div", { cls: "frcal__month_first", attr: { style: "flex: 70" } });
|
|
this.mountFirstSpanEl = this.mountFirstEl.createEl("span");
|
|
this.mountSecondEl = this.monthEl.createEl("div", { cls: "frcal__month_second", attr: { style: "flex: 0" } });
|
|
this.mountSecondSpanEl = this.mountSecondEl.createEl("span");
|
|
|
|
// day header
|
|
this.headEl = container.createEl("div", { cls: "frcal__days frcal__day_head" });
|
|
|
|
this.weekNumEl = this.headEl.createEl("div", { cls: "frcal__time" });
|
|
this.weekNumEl.addEventListener('click', function(e){
|
|
this.openMountView();
|
|
}.bind(this));
|
|
this.dayHeads = [null, null, null, null, null, null, null];
|
|
for (let i = 0; i < 7; i++)
|
|
{
|
|
this.dayHeads[i] = this.headEl.createEl("div", { cls: "frcal__day frcal__day_head_item" });
|
|
this.dayHeads[i].createEl("span");
|
|
}
|
|
|
|
// all day events
|
|
this.alldayEl = container.createEl("div", { cls: "frcal__days frcal__day_allday" });
|
|
|
|
this.alldayEl.createEl("div", { cls: "frcal__time" });
|
|
this.allday = [null, null, null, null, null, null, null];
|
|
for (let i = 0; i < 7; i++)
|
|
{
|
|
this.allday[i] = this.alldayEl.createEl("div", { cls: "frcal__day frcal__day_allday_item" });
|
|
}
|
|
|
|
// timed events
|
|
this.timedEl = container.createEl("div", { cls: "frcal__days frcal__day_timed" });
|
|
|
|
let timed = this.timedEl.createEl("div", { cls: "frcal__time" });
|
|
timed.createEl("div", { cls: "frcal__hour am", text: '12' });
|
|
for (let i = 1; i <= 12; i++)
|
|
{
|
|
timed.createEl("div", { cls: "frcal__hour " + ((i == 12) ? "pm" : "am"), text: i.toString() });
|
|
}
|
|
for (let i = 1; i <= 11; i++)
|
|
{
|
|
timed.createEl("div", { cls: "frcal__hour pm", text: i.toString() });
|
|
}
|
|
this.timed = [null, null, null, null, null, null, null];
|
|
for (let i = 0; i < 7; i++)
|
|
{
|
|
this.timed[i] = this.timedEl.createEl("div", { cls: "frcal__day frcal__timed_item" });
|
|
}
|
|
|
|
let btn;
|
|
btn = container.createEl('button', { cls: "frcal__button"});
|
|
btn.innerText = 'week-';
|
|
btn.addEventListener('click', () => {
|
|
this.gotoPreviusWeek();
|
|
})
|
|
|
|
btn = container.createEl('button', { cls: "frcal__button"})
|
|
btn.innerText = 'week+';
|
|
btn.addEventListener('click', () => {
|
|
this.gotoNextWeek();
|
|
})
|
|
|
|
btn = container.createEl('button', { cls: "frcal__button"});
|
|
btn.innerText = 'day-';
|
|
btn.addEventListener('click', () => {
|
|
this.dayViewIndex--;
|
|
if (this.dayViewIndex < -1)
|
|
this.dayViewIndex = 6;
|
|
this.dayView();
|
|
})
|
|
|
|
btn = container.createEl('button', { cls: "frcal__button"})
|
|
btn.innerText = 'day+';
|
|
btn.addEventListener('click', () => {
|
|
this.dayViewIndex++;
|
|
if (this.dayViewIndex >= 7)
|
|
this.dayViewIndex = -1;
|
|
this.dayView();
|
|
})
|
|
|
|
this.addEvents();
|
|
this.updateWeek();
|
|
}
|
|
|
|
openMountView(startMonth = null, endMonth = null)
|
|
{
|
|
this.mountView.empty();
|
|
|
|
if (startMonth == null)
|
|
{
|
|
startMonth = moment().subtract(1, "years");
|
|
}
|
|
if (endMonth == null)
|
|
{
|
|
endMonth = moment().add(1, "years");
|
|
}
|
|
|
|
if (startMonth.day() != 1)
|
|
{
|
|
if (startMonth.day() == 0)
|
|
{
|
|
startMonth.day(-6);
|
|
}
|
|
else
|
|
{
|
|
startMonth.day(1);
|
|
}
|
|
}
|
|
|
|
if (endMonth.day() != 1)
|
|
{
|
|
if (endMonth.day() == 0)
|
|
{
|
|
endMonth.day(-6);
|
|
}
|
|
else
|
|
{
|
|
endMonth.day(1);
|
|
}
|
|
}
|
|
|
|
console.log("mount view: start = ", startMonth, " end = ", endMonth);
|
|
|
|
let lastMonth = moment(startMonth).day(4).year() * 12 + moment(startMonth).day(4).month() - 1;
|
|
|
|
let table = this.mountView.createEl('table');
|
|
for (let week = moment(startMonth); week < endMonth; week.add(1, "weeks"))
|
|
{
|
|
let date = stringifySingleDate(week).match(DateFormat.week);
|
|
let row = table.createEl("tr", {
|
|
attr: {
|
|
'id': "frcal_mountview_" + date,
|
|
'onclick': "app.workspace.getLeavesOfType('" + VIEW_TYPE_CALENDAR + "')[0].view.mountViewClick('" + date + "')"
|
|
}
|
|
});
|
|
|
|
let thursday = moment(week).day(4);
|
|
|
|
if (lastMonth != thursday.year() * 12 + thursday.month())
|
|
{
|
|
lastMonth += 1;
|
|
let lastDayOfMount = moment(thursday);
|
|
let span = 0;
|
|
while (lastDayOfMount.month() == thursday.month())
|
|
{
|
|
span++;
|
|
lastDayOfMount.add(1, "weeks");
|
|
}
|
|
// let span = moment(week).add(1, "months").date(4).isoWeek() - week.isoWeek();
|
|
row.createEl("td", { attr: { rowspan: span }, cls: 'frcal__month_month' }).innerText = (thursday.month() + 1).toString() + " - " + DateFormat.months[thursday.month()];
|
|
}
|
|
|
|
row.createEl('td', { cls: 'frcal__weekinmonth' }).innerText = week.isoWeek();
|
|
|
|
let day=moment(week)
|
|
for (let i = 7; i > 0; i--)
|
|
{
|
|
let classes = 'frcal__dayinmonth';
|
|
classes += ((day.month()%2 == 1) ? ' frcal__evenMonth' : ' frcal__oddMonth');
|
|
classes += (day.format("y-MM-D") == moment().format("y-MM-D")) ? ' frcal__today' : ''
|
|
row.createEl('td', { cls: classes }).innerText = day.date().toString();
|
|
day.add(1, "day");
|
|
}
|
|
}
|
|
this.mountView.style.display = "block";
|
|
setTimeout(() => {
|
|
document.getElementById('frcal_mountview_' + stringifySingleDate(moment()).match(DateFormat.week)).scrollIntoView({
|
|
behavior: 'instant',
|
|
block: 'center',
|
|
inline: 'center'
|
|
});
|
|
}, 10);
|
|
}
|
|
|
|
mountViewClick(week)
|
|
{
|
|
this.mountView.style.display = "none";
|
|
this.gotoThisWeek(week);
|
|
}
|
|
|
|
async onClose()
|
|
{
|
|
// Nothing to clean up.
|
|
}
|
|
|
|
addEvents()
|
|
{
|
|
this.backButtonEl.ariaDisabled = "false";
|
|
this.backButtonEl.addEventListener('click', this.gotoPreviusWeek.bind(this));
|
|
this.forwardButtonEl.ariaDisabled = "false";
|
|
this.forwardButtonEl.addEventListener('click', this.gotoNextWeek.bind(this));
|
|
}
|
|
|
|
gotoPreviusWeek()
|
|
{
|
|
let date = parseSingleDate(this.week + ' ma 12:00am', null).subtract(7, 'days');
|
|
this.week = stringifySingleDate(date).match(DateFormat.week)[0];
|
|
this.updateWeek();
|
|
}
|
|
|
|
gotoNextWeek()
|
|
{
|
|
let date = parseSingleDate(this.week + ' ma 12:00am', null).add(7, 'days');
|
|
this.week = stringifySingleDate(date).match(DateFormat.week)[0];
|
|
this.updateWeek();
|
|
}
|
|
|
|
gotoThisWeek(week = '')
|
|
{
|
|
if (week == '')
|
|
{
|
|
this.week = stringifySingleDate(moment()).match(DateFormat.week)[0];
|
|
}
|
|
else if (typeof week == 'string')
|
|
{
|
|
// format of this.week is very strikt, this way every format the parser suports will work.
|
|
this.week = stringifySingleDate(parseSingleDate(week, moment())).match(DateFormat.week)[0];
|
|
}
|
|
else
|
|
{
|
|
this.week = stringifySingleDate(week).match(DateFormat.week)[0];
|
|
}
|
|
this.updateWeek();
|
|
}
|
|
|
|
updateWeek()
|
|
{
|
|
let monthF = parseSingleDate(this.week + " ma 00:00am",).month();
|
|
this.mountFirstSpanEl.innerText = (monthF+1).toString() + " - " + DateFormat.months[monthF];
|
|
|
|
this.weekNumEl.innerHTML = this.week.substring(0, 3) + '<br/>' + this.week.substring(3, 6);
|
|
|
|
let zoDate = parseSingleDate(this.week + " zo 00:00am");
|
|
let monthS = zoDate.month();
|
|
if (monthF != monthS)
|
|
{
|
|
this.mountSecondSpanEl.innerText = (monthS+1).toString() + " - " + DateFormat.months[monthS];
|
|
|
|
let dow = zoDate.date();
|
|
|
|
this.mountFirstEl.setAttr("style", "flex: " + ((7-dow)*10).toString());
|
|
this.mountSecondEl.setAttr("style", "flex: " + ((dow)*10).toString())
|
|
}
|
|
else
|
|
{
|
|
this.mountSecondSpanEl.innerText = "";
|
|
this.mountFirstEl.setAttr("style", "flex: 70")
|
|
this.mountSecondEl.setAttr("style", "flex: 0")
|
|
}
|
|
|
|
let today = parseSingleDate("12:00pm", moment());
|
|
for (let day = 0; day < 7; day++)
|
|
{
|
|
let date = parseSingleDate(this.week + " " + DateFormat.days[(day + 1) % 7] + " 12:00pm");
|
|
this.dayHeads[day].children[0].innerHTML = DateFormat.daysLong[day] + "<br/>" + date.date().toString();
|
|
if (date.diff(today, "minutes") == 0)
|
|
{
|
|
this.dayHeads[day].children[0].style.color = "var(--color-accent)";
|
|
}
|
|
else
|
|
{
|
|
this.dayHeads[day].children[0].style.color = "";
|
|
}
|
|
}
|
|
|
|
this.updateEvents();
|
|
}
|
|
|
|
updateEvents()
|
|
{
|
|
// let item = {
|
|
// "id": null,
|
|
// "date": stringifyDate(date),
|
|
// "allDay": false,
|
|
// "title": "Untitled",
|
|
// "group": "default",
|
|
// "location": null,
|
|
// "state": "reminder"
|
|
// };
|
|
|
|
// clear events
|
|
let today = parseSingleDate("12:00pm", moment());
|
|
for (let day = 0; day < 7; day++)
|
|
{
|
|
this.allday[day].innerHTML = "";
|
|
this.timed[day].innerHTML = "";
|
|
// add hour lines
|
|
let hourlinesEl = this.timed[day].createEl("div", { cls: "frcal__hourlines" });
|
|
hourlinesEl.createEl("div", { cls: "frcal__hourline", attr: { style: "margin-top: 0px"} })
|
|
for (let hour = 0; hour < 11; hour++)
|
|
{
|
|
hourlinesEl.createEl("div", { cls: "frcal__hourline", attr: { style: "margin-top: calc(120 * var(--frcal-zoom, 0.5px) - 1px)"} })
|
|
}
|
|
// add current time line
|
|
if (parseSingleDate(this.week + " " + DateFormat.days[(day+1) % 7] + " 12:00pm").diff(today, "minutes") == 0)
|
|
{
|
|
let time = moment();
|
|
time = time.hour()*60 + time.minute();
|
|
this.timed[day].createEl("div", { cls: "frcal__hourlines" })
|
|
.createEl("div", { cls: "frcal__hourline frcal__now", attr: { style: "top: calc(" + time + " * var(--frcal-zoom, 0.5px) - 1px)"} })
|
|
}
|
|
}
|
|
|
|
let start = parseSingleDate(this.week + " ma 00:00am");
|
|
let end = parseSingleDate(this.week + " zo 11:59pm");
|
|
for (let event in this.plugin.data)
|
|
{
|
|
event = this.plugin.data[event];
|
|
let date = parseDate(event['date'])
|
|
let startInWeek = (date[0] >= start && date[0] <= end);
|
|
let endInWeek = (date[1] >= start && date[1] <= end);
|
|
if (startInWeek && endInWeek)
|
|
{
|
|
let res = [...event.date.matchAll("(?:" + DateFormat.days.join('|') + ")")];
|
|
if ((res.length <= 1) || (res[0][0] == res[1][0]))
|
|
{
|
|
this.renderEvent(event);
|
|
}
|
|
else
|
|
{
|
|
let date_split = event.date.split('-');
|
|
let day_start = DateFormat.days.indexOf(res[0][0]), day_end = DateFormat.days.indexOf(res[1][0]);
|
|
let part = JSON.parse(JSON.stringify(event));
|
|
// first day
|
|
part.date = date_split[0].match(DateFormat.week)[0] + ' ' + DateFormat.days[day_start] + ' ' + date_split[0].match(DateFormat.time)[0];
|
|
part.date += '-' + date_split[1].match(DateFormat.week)[0] + ' ' + DateFormat.days[day_start] + ' 23:59pm';
|
|
this.renderEvent(part);
|
|
|
|
// middel days
|
|
for (let day = day_start+1; day < ((day_end == 0) ? 7 : day_end); day++)
|
|
{
|
|
part.date = date_split[0].match(DateFormat.week)[0] + ' ' + DateFormat.days[day] + ' 12:00am';
|
|
part.date += '-' + date_split[1].match(DateFormat.week)[0] + ' ' + DateFormat.days[day] + ' 23:59pm';
|
|
this.renderEvent(part);
|
|
}
|
|
|
|
// last day
|
|
part.date = date_split[0].match(DateFormat.week)[0] + ' ' + DateFormat.days[day_end] + ' 12:00am';
|
|
part.date += '-' + date_split[1].match(DateFormat.week)[0] + ' ' + DateFormat.days[day_end] + ' ' + date_split[0].match(DateFormat.time)[0];
|
|
this.renderEvent(part);
|
|
}
|
|
}
|
|
else if (startInWeek || endInWeek)
|
|
{
|
|
console.warn('multi week event not suported', event);
|
|
console.log(date, start, end);
|
|
}
|
|
}
|
|
}
|
|
|
|
renderEvent(event)
|
|
{
|
|
let time = parseDate(event.date);
|
|
let container = {}, allDay;
|
|
let day = time[0].day() - 1;
|
|
day = (day == -1) ? 6 : day;
|
|
if (event.date.match(DateFormat.time)[0] == '12:00am')
|
|
{
|
|
container = this.allday[day];
|
|
allDay = true;
|
|
}
|
|
else
|
|
{
|
|
container = this.timed[day];
|
|
allDay = false;
|
|
}
|
|
|
|
time[0] = time[0].hour()*60 + time[0].minute();
|
|
time[1] = time[1].hour()*60 + time[1].minute();
|
|
|
|
// check for concurrent events
|
|
let classes = "frcal__event";
|
|
let concurrentClash = "";
|
|
for (let i in container.children)
|
|
{
|
|
let child = container.children[i];
|
|
if ((child instanceof HTMLElement) && child.hasClass("frcal__event"))
|
|
{
|
|
if (
|
|
(time[1] > parseInt(child.dataset.start))
|
|
&& (time[0] < parseInt(child.dataset.end))
|
|
)
|
|
{
|
|
if (child.dataset.concurrentClash != "")
|
|
{
|
|
let third = document.getElementById("fr_event_" + child.dataset.concurrentClash);
|
|
if (
|
|
(time[1] > parseInt(third.dataset.start))
|
|
&& (time[0] < parseInt(third.dataset.end))
|
|
)
|
|
{
|
|
console.warn("triple concurrent clashes are not suported with " + event.id + ", " + child.dataset.id + " and " + child.dataset.concurrentClash);
|
|
}
|
|
else
|
|
{
|
|
classes += (child.hasClass("frcal__event_concurrent2")) ? " frcal__event_concurrent1" : " frcal__event_concurrent2";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (time[0] < parseInt(child.dataset.start))
|
|
{
|
|
classes += " frcal__event_concurrent1";
|
|
child.addClass("frcal__event_concurrent2");
|
|
}
|
|
else
|
|
{
|
|
child.addClass("frcal__event_concurrent1");
|
|
classes += " frcal__event_concurrent2";
|
|
}
|
|
child.dataset.concurrentClash = event.id;
|
|
concurrentClash = child.dataset.id;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
let el = container.createEl('div', {
|
|
cls: classes,
|
|
attr: {
|
|
'id': "fr_event_" + event.id,
|
|
'data-id': event.id,
|
|
'data-group': event.group,
|
|
'data-state': event.state,
|
|
'data-type': event.type,
|
|
'data-start': time[0].toString(),
|
|
'data-end': time[1].toString(),
|
|
'data-concurrent-clash': concurrentClash,
|
|
'style': ((allDay) ? '' : 'top: calc(' + time[0] + ' * var(--frcal-zoom, 0.5px))')
|
|
},
|
|
});
|
|
let eventEl = el.createEl('span', {
|
|
attr: {
|
|
'style': ((allDay) ? '' : 'min-height: calc(' + (time[1] - time[0]) + ' * var(--frcal-zoom, 0.5px))')
|
|
}
|
|
});
|
|
eventEl.addEventListener('mousedown', (e) => {
|
|
if (e.button == 2)
|
|
{
|
|
this.openMenu(event, {x: e.clientX, y: e.clientY});
|
|
}
|
|
});
|
|
eventEl.addEventListener('touchstart', (e) => {
|
|
if (e.touches.length == 1)
|
|
{
|
|
this.touchEvent = { "event": event, "location": {x: e.touches[0].clientX, y: e.touches[0].clientY}, "time": new Date().getTime() };
|
|
setTimeout(() => {
|
|
if (this.touchEvent != null && ((new Date().getTime() - this.touchEvent.time) > 700))
|
|
{
|
|
this.openMenu(this.touchEvent.event, this.touchEvent.location);
|
|
this.touchEvent = null;
|
|
}
|
|
}, 750);
|
|
}
|
|
else
|
|
{
|
|
this.touchEvent = null;
|
|
}
|
|
});
|
|
eventEl.addEventListener('touchend', (e) => {
|
|
this.touchEvent = null;
|
|
});
|
|
eventEl.addEventListener('touchmove', (e) => {
|
|
this.touchEvent = null;
|
|
});
|
|
switch (event.type)
|
|
{
|
|
case 'task':
|
|
if (event.state == ' ')
|
|
{
|
|
eventEl.createEl('input', {
|
|
cls: 'task-list-item-checkbox',
|
|
attr: {
|
|
'data-task': event.state,
|
|
'type': 'checkbox'
|
|
}
|
|
});
|
|
}
|
|
else
|
|
{
|
|
eventEl.createEl('input', {
|
|
cls: 'task-list-item-checkbox is-checked',
|
|
attr: {
|
|
'data-task': event.state,
|
|
'type': 'checkbox',
|
|
'checked': ''
|
|
}
|
|
});
|
|
}
|
|
default:
|
|
eventEl.appendText(event.title);
|
|
break;
|
|
}
|
|
return el;
|
|
}
|
|
|
|
openMenu(event, location)
|
|
{
|
|
let time = [...event.date.matchAll(DateFormat.time)];
|
|
if (time.length == 1)
|
|
{
|
|
time = time[0];
|
|
}
|
|
else if (time.length == 2)
|
|
{
|
|
time = time[0] + "-" + time[1];
|
|
}
|
|
else
|
|
{
|
|
time = "<??>";
|
|
}
|
|
let loc = event.location;
|
|
if (loc != "")
|
|
{
|
|
loc = " {" + loc + "}";
|
|
}
|
|
let text = event.group + " " + time + loc;
|
|
|
|
let menu = new obsidian.Menu();
|
|
menu.addItem((item) => {
|
|
item.setIsLabel(true);
|
|
item.setTitle(text);
|
|
});
|
|
|
|
menu.showAtPosition(location);
|
|
}
|
|
|
|
dayView()
|
|
{
|
|
let containers = [this.dayHeads, this.allday, this.timed];
|
|
if (this.dayViewIndex == -1)
|
|
{
|
|
this.containerEl.children[1].removeClass('frcal__dayView');
|
|
}
|
|
else
|
|
{
|
|
this.containerEl.children[1].addClass('frcal__dayView');
|
|
}
|
|
for (let i in containers)
|
|
{
|
|
for (let ii=0; ii<7; ii++)
|
|
{
|
|
containers[i][ii].style.display = ((this.dayViewIndex == -1) || (ii == this.dayViewIndex)) ? "" : "none";
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
class FRCalander extends Plugin
|
|
{
|
|
async onload()
|
|
{
|
|
this.registerView(VIEW_TYPE_CALENDAR, (leaf) => { return new CalendarView(leaf, this) });
|
|
|
|
this.addCommand({
|
|
id: 'fr-calendar-scan',
|
|
name: 'scan active file',
|
|
repeatable: false,
|
|
callback: async () => {
|
|
let file = this.app.workspace.getActiveFile();
|
|
if (file !== null)
|
|
{
|
|
let content = await this.app.vault.read(file);
|
|
let res = scanFile(content, this.data);
|
|
this.data = res.data;
|
|
this.app.vault.modify(file, res.content);
|
|
}
|
|
this.saveData(this.data);
|
|
}
|
|
});
|
|
this.addCommand({
|
|
id: 'fr-calendar-scan-all',
|
|
name: 'scan all files conaining the tag',
|
|
repeatable: false,
|
|
callback: async () => {
|
|
var events = {};
|
|
const notes = this.app.vault.getMarkdownFiles();
|
|
for (const noteFile of notes) {
|
|
const fileCachedData = this.app.metadataCache.getFileCache(noteFile) || {};
|
|
const tags = obsidian.getAllTags(fileCachedData);
|
|
if (tags.contains('#fr-calendar'))
|
|
{
|
|
let content = await this.app.vault.read(noteFile);
|
|
let res = scanFile(content, events);
|
|
events = res.data;
|
|
this.app.vault.modify(noteFile, res.content);
|
|
}
|
|
}
|
|
this.data = events;
|
|
this.saveData(this.data);
|
|
}
|
|
});
|
|
//TODO: scan folder; with DataAdapter
|
|
// this.addCommand({
|
|
// id: 'calnder-save',
|
|
// name: 'save the calander',
|
|
// editorCallback: () => {
|
|
// this.saveData(data);
|
|
// }
|
|
// });
|
|
// this.addCommand({
|
|
// id: 'calnder-load',
|
|
// name: 'save the calander',
|
|
// editorCallback: () => {
|
|
// data = this.loadData();
|
|
// }
|
|
// });
|
|
|
|
|
|
this.addRibbonIcon("calendar", "show fr calender", () => {
|
|
let leaf = app.workspace.getLeavesOfType(VIEW_TYPE_CALENDAR);
|
|
if (leaf.length > 0)
|
|
{
|
|
leaf = leaf[0];
|
|
}
|
|
else
|
|
{
|
|
leaf = app.workspace.getLeavesOfType('empty');
|
|
if (leaf.length > 0)
|
|
{
|
|
leaf = leaf[0];
|
|
}
|
|
else
|
|
{
|
|
leaf = app.workspace.getLeaf('tab');
|
|
}
|
|
}
|
|
|
|
leaf.setViewState({ type: VIEW_TYPE_CALENDAR });
|
|
app.workspace.setActiveLeaf(leaf);
|
|
});
|
|
|
|
this.data = await this.loadData();
|
|
}
|
|
}
|
|
|
|
module.exports = FRCalander;
|