Version Changes

This commit is contained in:
Christian Seyfferth 2019-01-14 09:57:57 +01:00
commit df89539710
5 changed files with 855 additions and 0 deletions

259
app.js Normal file
View File

@ -0,0 +1,259 @@
if (!String.prototype.includes) {
String.prototype.includes = function () {
'use strict';
return String.prototype.indexOf.apply(this, arguments) !== -1;
};
}
//extend FileReader
if (!FileReader.prototype.readAsBinaryString) {
FileReader.prototype.readAsBinaryString = function (fileData) {
var binary = "";
var pt = this;
var reader = new FileReader();
reader.onload = function (e) {
var bytes = new Uint8Array(reader.result);
var length = bytes.byteLength;
for (var i = 0; i < length; i++) {
binary += String.fromCharCode(bytes[i]);
}
//pt.result - readonly so assign binary
pt.content = binary;
$(pt).trigger('onload');
}
reader.readAsArrayBuffer(file);
}
}
document.addEventListener('DOMContentLoaded', function() {
M.AutoInit();
});
/* $(document).ready(function () {
$('.modal').modal();
$('.tabs').tabs();
$('.fixed-action-btn').floatingActionButton();
}); */
//localStorage persistence
var SHIFT_STORAGE_KEY = "dienstplan_chrosey";
var RULE_STORAGE_KEY = "regeln_chrosey";
var shiftStorage = {
fetch: function () {
'use strict';
var parsed = JSON.parse(localStorage.getItem(SHIFT_STORAGE_KEY) || '[]'),
shifts = [];
parsed.forEach(function (el, index) {
var shift = Shift.thaw(el);
shift.id = index;
shifts.push(shift);
});
shiftStorage.uid = shifts.length;
return shifts;
},
save: function (shifts) {
'use strict';
var json = JSON.stringify(shifts)
localStorage.setItem(SHIFT_STORAGE_KEY, json);
},
count: function () {
return JSON.parse(localStorage.getItem(SHIFT_STORAGE_KEY) || '[]').length;
}
};
var ruleStorage = {
fetch: function () {
'use strict';
var rules = JSON.parse(localStorage.getItem(RULE_STORAGE_KEY)) || Rule.defaults();
ruleStorage.uid = rules.length;
return rules;
},
save: function (rules) {
'use strict';
var json = JSON.stringify(rules);
localStorage.setItem(RULE_STORAGE_KEY, json);
}
};
var app = new Vue({
el: '#app',
data: {
shifts: shiftStorage.fetch(),
rules: ruleStorage.fetch(),
icsFile: null,
blob: null,
dp_sheet: '',
deletedShift: '',
remaining: shiftStorage.count(),
selectedShift: '',
selectedRule: '',
saveto: 'dienstplan.ics',
uploadFileName: "",
config: {
moment: {
parse_formats: [
"ddd, DD/ MMM. 'YY HH:mm",
"ddd, DD/ MMM. YYYY HH:mm"
],
parse_language: 'en',
display_language: 'de'
},
toast_length: 3000
}
},
watch: {
shifts: {
handler: function (shifts) {
'use strict';
shiftStorage.save(shifts);
this.remaining = shifts.length;
M.toast({html:"Änderungen gespeichert.", displayLength:this.config.toast_length});
},
deep: true
},
rules: {
handler: function (rules) {
'use strict';
ruleStorage.save(rules);
M.toast({html:"Änderungen gespeichert.", displayLength:this.config.toast_length});
},
deep: true
}
},
methods: {
onFileChange: function (event) {
var files = event.target.files || event.dataTransfer.files;
this.handleInputFile(files[0]);
this.uploadFileName = files[0].name + " [" + Math.round(files[0].size / 1024) + " kB]";
M.toast({html:this.uploadFileName + " ausgewählt", displayLength:this.config.toast_length});
},
handleInputFile: function (file) {
var reader = new FileReader();
var vm = this;
reader.onload = (e) => {
var data = !e ? reader.content : e.target.result;
var workbook = XLSX.read(data, {
type: 'binary',
cellDates: true,
});
if(workbook.Sheets["Dienstplan"] == null){
file.status = "error";
} else {
vm.parseWorksheet(workbook.Sheets["Dienstplan"]);
file.status = "success";
}
};
reader.readAsBinaryString(file);
M.toast({html:this.uploadFileName + " wird eingelesen",displayLength: this.config.toast_length});
},
parseWorksheet: function (dp) {
var arr = XLSX.utils.sheet_to_row_object_array(dp, {
range: 1
});
var vm = this;
var day;
arr.forEach(element => {
moment.locale(vm.config.moment.parse_language);
if (element.hasOwnProperty('Datum')) {
day = moment(element.Datum);
}
if (element.hasOwnProperty('Dienst')) {
var time = moment(element.Zeit);
var date = day.hour(time.hour()).minute(time.minute());
var bemerkung = !element.Bemerkung ? "" : element.Bemerkung.trim();
var art = !element.Dienst ? "" : element.Dienst.trim();
var name = !element.__EMPTY ? "" : element.__EMPTY.trim();
console.log({day, element});
vm.addShift(new Shift(art, name, date, bemerkung));
}
});
},
addShift: function (shift) {
this.shifts.push(shift);
},
removeShift: function (shift) {
this.shifts.splice(this.shifts.indexOf(shift), 1);
M.toast({html:shift.VEventTitle + " gelöscht",displayLength: this.config.toast_length});
},
cleanStorage: function () {
this.shifts = [];
M.toast({html:"Alle Einträge gelöscht",displayLength: this.config.toast_length});
},
createDownloadFile: function () {
var vCal = new VCalendar("Dienstplan Kalender");
this.shifts.forEach(function (shift) {
vCal.add(shift.toVEvent());
});
var calString = vCal.toString();
this.blob = new Blob([calString], {
type: 'text/plain'
});
if (this.icsFile !== null) {
window.URL.revokeObjectURL(this.icsFile);
}
this.icsFile = window.URL.createObjectURL(this.blob);
M.toast({html:this.saveto + " erstellt.",displayLength: this.config.toast_length});
},
downloadFile: function () {
if (window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveOrOpenBlob(this.blob, this.saveto);
}
},
selectShift: function (shift) {
this.selectedShift = Shift.thaw(shift);
this.keepShift = shift;
M.updateTextFields();
$('#shiftModal').modal('open');
},
saveChanges: function (changedShift) {
this.shifts.splice(this.shifts.indexOf(this.keepShift), 1, changedShift);
$('#shiftModal').modal('close');
this.keepShift = '';
this.selectedShift = '';
},
discardChanges: function (changedShift) {
$('#shiftModal').modal('close');
this.keepShift = '';
this.selectedShift = '';
},
selectRule: function (rule) {
this.selectedRule = new Rule(rule.duration, rule.art, rule.title);
this.keepRule = rule;
$('#rule_arten').chips({
data: this.selectedRule.Arten
});
$('#rule_titel').chips({
data: this.selectedRule.Titel
});
$('#ruleModal').modal('open');
},
saveRule: function (changedRule) {
changedRule.Arten = $('#rule_arten').chips('data');
changedRule.Titel = $('#rule_titel').chips('data');
this.rules.splice(this.rules.indexOf(this.keepRule), 1, changedRule);
$('#ruleModal').modal('close');
this.keepRule = '';
this.selectedRule = '';
},
discardRule: function (changedRule) {
this.rules.splice(this.rules.indexOf(this.keepRule), 1, this.keepRule);
$('#ruleModal').modal('close');
this.keepRule = '';
this.selectedRule = '';
}
},
directives: {
'edit-focus': function (el, value) {
if (value) {
el.focus();
}
}
}
})

44
css/page.css Normal file
View File

@ -0,0 +1,44 @@
.custom-file-control:lang(de)::after {
content: "Datei wählen...";
}
.custom-file-control:lang(de)::before {
content: "Durchsuchen";
}
#drop {
padding: 3em;
outline: 2px dashed black;
outline-offset: -10px;
}
.edit {
display: none;
}
footer {
padding-bottom: 15px;
}
.click-me {
cursor: pointer;
}
#body {
display: flex;
min-height: calc(100vh - 64px);
flex-direction: column;
padding-top: 56px;
}
main {
flex: 1 0 auto;
}
.deleteMe {
cursor: pointer;
}
.modal {
max-height: 70%;
}

217
index.html Normal file
View File

@ -0,0 +1,217 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<title>Dienstplan Converter</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<link rel="apple-touch-icon" sizes="180x180" href="/favicons/apple-touch-icon.png">
<link rel="icon" type="image/png" href="/favicons/favicon-32x32.png" sizes="32x32">
<link rel="icon" type="image/png" href="/favicons/favicon-16x16.png" sizes="16x16">
<link rel="mask-icon" href="/favicons/safari-pinned-tab.svg" color="#e6b300">
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
<link rel="stylesheet" href="css/page.css">
</head>
<body>
<div id="app">
<div class="navbar-fixed">
<nav class="nav-extended indigo">
<div class="nav-wrapper container ">
<span class="brand-logo">Dienstplan Converter</span>
<a href="#" data-activates="mobile-demo" class="button-collapse"><i class="material-icons">menu</i></a>
<ul id="nav-mobile" class="right hide-on-med-and-down">
<li>&nbsp;</li>
</ul>
<ul class="side-nav" id="mobile-demo">
</ul>
<ul class="tabs tabs-transparent">
<li class="tab"><a class="active" href="#Dienste">Dienste</a></li>
<li class="tab"><a href="#Einstellungen">Regeln</a></li>
</ul>
</div>
</nav>
</div>
<div id="body">
<main class="container">
<div class="card" id="Dienste">
<div class="card-content" v-if="shifts.length > 0">
<h3 class="card-title">Dienste</h3>
<table class="highlight" >
<thead class="">
<tr>
<th>Datum</th>
<th>Wochentag</th>
<th>Titel</th>
<th>Uhrzeit</th>
<th></th>
</tr>
</thead>
<tbody>
<tr v-for="s in shifts" :key="s.id" @click="selectShift(s)">
<td class="text-xs-right align-middle">
{{s.FormattedDatum}}
</td>
<td class="">
{{s.Wochentag}}
</td>
<td class="">
{{s.VEventTitle}}
</td>
<td class="">
{{s.Beginn}} - {{s.Ende}}
</td>
<td class="">
<a class="btn-flat red-text wave" @click.stop="removeShift(s)"><i class="material-icons">delete</i></a>
</td>
</tr>
</tbody>
<tfoot>
<tr>
<th colspan="5" class="text-right"> {{shifts.length }} Dienste</th>
</tr>
</tfoot>
</table>
</div>
<div class="card-content" v-if="shifts.length < 1">
Noch keine Dienste da.
</div>
<div class="card-action" v-if="shifts.length < 1">
<label class="btn green tooltipped" data-position="left" data-tooltip="Dienstplan einlesen" for="fileInput">
<i class="material-icons">publish</i>
</label>
</div>
</div>
<div class="fixed-action-btn">
<a class="btn-floating btn-large orange darken-2 waves-effect">
<i class="large material-icons">work</i>
</a>
<ul>
<li>
<label class="btn-floating green tooltipped" data-position="left" data-tooltip="Dienstplan einlesen" for="fileInput">
<i class="material-icons">publish</i>
</label>
</li>
<li>
<button class="btn-floating blue tooltipped" data-position="left" data-tooltip="Kalenderdatei erstellen" @click="createDownloadFile" :disabled=" (remaining > 0) ? null : 'disabled'">
<i class="material-icons">description</i>
</button>
</li>
<li>
<a class="btn-floating yellow tooltipped" data-position="left" data-tooltip="Kalenderdatei herunterladen" :href="icsFile" v-bind:class="[ icsFile ? '' : 'disabled']" download="dienstplan.ics" @click="downloadFile">
<i class="material-icons">get_app</i>
</a>
</li>
<li>
<a class="btn-floating red tooltipped" data-position="left" data-tooltip="Tabelle leeren" @click="cleanStorage" :disabled=" (remaining > 0) ? null : 'disabled'"><i class="material-icons">clear_all</i></a>
</li>
</ul>
</div>
<div class="card" id="Einstellungen">
<div class="card-content">
<h3 class="card-title">Regeln für die Dauer</h3>
<div>
<span class="chip orange">Titel der Veranstaltung</span>
<span class="chip indigo">Art des Dienstes</span>
</div>
<ul class="collection highlight">
<li class="collection-item" v-for="r in rules" :key="rules.indexOf(r)" @click="selectRule(r)">
<span v-if="rules.indexOf(r) != 0" class="title">sonst </span>
<span class="title" v-if="rules.indexOf(r) < rules.length -1">wenn: </span>
<span class="secondary-content">{{ r.duration }} min</span>
<div>
<span v-if="Array.isArray(r.art)">
<span class="chip indigo" :class="{'lighten-3' : r.art.length > 1, 'lighten-2' : r.art.length == 1}" v-for="a in r.art" :key="r.art.indexOf(a)">{{a}}</span>
</span>
<span v-if="Array.isArray(r.title)">
<span class="chip orange" :class="{'lighten-3' : r.title.length > 1, 'lighten-2' : r.title.length == 1}" v-for="t in r.title" :key="r.title.indexOf(t)">{{t}}</span>
</span>
</div>
</li>
</ul>
</div>
</div>
</main>
<div id="ruleModal" class="modal bottom-sheet active">
<div class="modal-content">
<h4>Regel anpassen</h4>
<div class="row">
<div class="range-field col m6 s12">
<label class="active" for="rule_duration">Dauer in Minuten: {{selectedRule.Dauer}}</label>
<input id="rule_duration" type="range" v-model="selectedRule.duration" min="30" max="300" step="10">
</div>
<div class="input-field col s12 m8 l6">
<label class="active" for="shift_kind">Arten</label>
<div id="rule_arten" class="chips-initial"></div>
</div>
<div class="input-field col m8 l6 s12">
<label class="active" for="rule_titel">Titel</label>
<div id="rule_titel" class="chips-inital"></div>
</div>
</div>
</div>
<div class="modal-footer">
<button class="waves-effect btn-flat waves-green" @click="saveRule(selectedRule)">Speichern</button>
<button class="waves-effect btn-flat waves-red" @click="discardRule(selectedRule)">Verwerfen</button>
</div>
</div>
<div id="shiftModal" class="modal bottom-sheet">
<div class="modal-content">
<h4>{{ selectedShift.VEventTitle || 'kein Titel'}}</h4>
<div class="row">
<div class="input-field col s6 ">
<input id="shift_name" type="text" v-model="selectedShift.Name" placeholder="kein Titel">
<label class="active" for="shift_name">Titel</label>
</div>
<div class="input-field col s6 ">
<input id="shift_kind" type="text" v-model="selectedShift.Art" placeholder="keine Art">
<label class="active" for="shift_kind">Titel</label>
</div>
<div class="input-field col s6 ">
<input id="shift_date" type="date" class="datepicker" v-model="selectedShift.Datum" placeholder="Datum">
<label class="active" for="shift_date">Datum</label>
</div>
<div class="input-field col s6">
<input id="shift_begin" type="time" v-model="selectedShift.Beginn" placeholder="Uhrzeit" class="timepicker" :data-default="selectedShift.Beginn">
<label class="active" for="shift_begin">Anfang</label>
</div>
<div class="input-field col s6 ">
<input id="shift_end" type="time" v-model="selectedShift.Ende" placeholder="Ende" class="timepicker">
<label class="active" for="shift_end">Ende</label>
</div>
<div class="input-field col s6 ">
<input id="shift_desc" type="text" v-model="selectedShift.Beschreibung" placeholder="keine Beschreibung">
<label class="active" for="shift_desc">Titel</label>
</div>
</div>
</div>
<div class="modal-footer">
<button class="waves-effect btn-flat waves-green" @click="saveChanges(selectedShift)">Speichern</button>
<button class="waves-effect btn-flat waves-red" @click="discardChanges(selectedShift)">Verwerfen</button>
</div>
</div>
</div>
<input type="file"
name="fileInput"
id="fileInput"
@change="onFileChange"
accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
style="display:none;">
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.14.1/xlsx.full.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.23.0/moment-with-locales.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.22/dist/vue.js"></script>
<script src="js/shift.js"></script>
<script src="js/vcal.js"></script>
<script src="app.js"></script>
</body>
</html>

207
js/shift.js Normal file
View File

@ -0,0 +1,207 @@
var DATE_LOCALE_FORMAT = "D.M.YY"; //10.2.16
var DATE_INPUT_FORMAT = "YYYY-MM-DD"; //2016-02-10
var TIME_FORMAT = "HH:mm"; // 17:30
var WEEKDAY_FORMAT = "dddd"; //Monday
var MOMENT_LOCALE = "de";
var TIMEZONE_NAME = "Europe/Berlin";
//requires moment.js
Array.prototype.asChipData = function () {
var chips = [];
this.forEach(function (el, index, array) {
chips.push({
tag: el,
id: index
});
});
return chips;
}
Array.prototype.fromChipData = function () {
var strings = [];
this.forEach(function (el, id, array) {
strings.push(el.tag);
});
return strings;
}
function Rule(duration, arten, titel) {
this.duration = duration;
this.art = arten;
this.title = titel;
}
Rule.prototype = {
get Dauer() {
return this.duration;
},
set Dauer(value) {
this.duration = value;
},
get Arten() {
return this.art.asChipData();
},
set Arten(value) {
this.art = value.fromChipData();
},
get Titel() {
return this.title.asChipData();
},
set Titel(value) {
this.title = value.fromChipData();
}
};
Rule.prototype.fits = function (art, title) {
var artMatch = false;
var nameMatch = false;
if (this.art.length == 0) artMatch = true;
else {
this.art.forEach(function (el, i) {
if (art.includes(el.toLowerCase())) artMatch = true;
});
}
if (this.title.length == 0) nameMatch = true;
else {
this.title.forEach(function (el, i) {
if (name.includes(el.toLowerCase())) nameMatch = true;
});
}
return artMatch && nameMatch;
}
Rule.defaults = function () {
var rules = [];
rules.push(new Rule(60, ['VS'], ['Kinderkonzert']));
rules.push(new Rule(120, ['VS'], ["expeditionskonzert", "sinfoniekonzert"]));
rules.push(new Rule(150, ['Oa', 'GP'], ['Expeditionskonzert']));
rules.push(new Rule(60, ['Oa', 'GP'], ['Expeditionskonzert']));
rules.push(new Rule(150, ['Oa', 'OSP'], []));
rules.push(new Rule(180, ["VS", "BO", "OHP", "HP", "GP", "Prem", "WA"], []));
rules.push(new Rule(60, [], []));
rules.push(new Rule(60, [], []));
rules.push(new Rule(60, [], []));
rules.push(new Rule(60, [], []));
rules.push(new Rule(60, [], []));
return rules;
}
var DURATION_RULES = Rule.defaults();
function Shift(kind, name, date, info) {
'use strict';
this.Datum = date.format(DATE_INPUT_FORMAT);
this.Beginn = date.format(TIME_FORMAT);
this.Art = kind;
this.Beschreibung = info;
this.Name = name;
Shift.setDurationFromRules(this);
}
Shift.prototype = {
get Wochentag() {
return this._date.clone().locale(MOMENT_LOCALE).format(WEEKDAY_FORMAT);
},
set Wochentag(value) {
throw "kann nicht gesetzt werden.";
},
get FormattedDatum() {
return this._date.clone().format(DATE_LOCALE_FORMAT);
},
set FormattedDatum(value) {
var dateMoment = moment(value, DATE_LOCALE_FORMAT);
this._date = dateMoment;
},
get Datum() {
return this._date.clone().format(DATE_INPUT_FORMAT);
},
set Datum(value) {
var dateMoment = moment(value).startOf('day');
this._date = dateMoment.clone();
},
get Beginn() {
return this._begin.clone().format(TIME_FORMAT);
},
set Beginn(value) {
var dateMoment = moment(this.Datum + " " + value, DATE_INPUT_FORMAT + " " + TIME_FORMAT);
this._begin = dateMoment.clone();
},
get Ende() {
return this._begin.clone().add(this._duration).format(TIME_FORMAT);
},
set Ende(value) {
var dateMoment = moment(this.Datum + " " + value, DATE_INPUT_FORMAT + " " + TIME_FORMAT);
if (this._begin > dateMoment) {
dateMoment.add(1, 'd');
}
this._duration = moment.duration(dateMoment.diff(this._begin));
},
get Dauer() {
return this._duration;
},
set Dauer(duration) {
this._duration = duration;
},
get Name() {
return this._name;
},
set Name(value) {
this._name = value ? value.trim() : "";
},
get VEventTitle() {
return this.Name + " " + this.Art;
},
set VEventTitle(value) {
throw "kann nicht gesetzt werden.";
},
get Art() {
return this._kind;
},
set Art(value) {
this._kind = value ? value.trim() : "";
},
get Beschreibung() {
return this._description;
},
set Beschreibung(value) {
this._description = value ? value.trim() : "";
}
}
Shift.setDurationFromRules = function (shift) {
'use strict';
var art = shift.Art.toLowerCase();
var name = shift.Name.toLowerCase();
var duration = 60;
for (var rIndex in DURATION_RULES) {
var rule = DURATION_RULES[rIndex];
if (rule.fits(art, name)) {
duration = rule.Dauer;
break;
}
}
shift.Dauer = moment.duration(duration, 'm').locale(MOMENT_LOCALE);
}
Shift.prototype.toVEvent = function () {
'use strict';
var end = this._begin.clone().add(this._duration);
return new VEvent(TIMEZONE_NAME, this._begin, end, this.VEventTitle, this.Beschreibung);
};
Shift.thaw = function (jsonShift) {
'use strict';
moment.locale(MOMENT_LOCALE);
var begin = moment(jsonShift._begin);
var shift = new Shift(jsonShift._kind, jsonShift._name, begin, jsonShift._description);
shift.id = jsonShift.id;
shift.Dauer = moment.duration(jsonShift._duration);
return shift;
};

128
js/vcal.js Normal file
View File

@ -0,0 +1,128 @@
var VCAL_DATETIME_FORMAT = "YMMDD[T]HHmmss"; //20160216T130500
var UID_FORMAT = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx';
var NEW_LINE = "\r\n";
function VMeta() {
this.properties = new Map();
this.children = [];
this.tag = "VMETA";
}
VMeta.formatDate = function (aMoment) {
'use strict';
return aMoment.format(VCAL_DATETIME_FORMAT);
};
VMeta.generateUID = function () {
'use strict';
return UID_FORMAT.replace(/[xy]/g, function (c) {
var r = Math.random() * 16 | 0,
v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
};
VMeta.prototype.set = function (key, value) {
'use strict';
this.properties.set(key, value);
};
VMeta.prototype.get = function (key) {
'use strict';
this.properties.get(key);
};
VMeta.prototype.add = function (child) {
'use strict';
this.children.push(child);
};
VMeta.prototype.mapToString = function () {
'use strict';
var output = "";
this.properties.forEach(function (value, key, map) {
output += (NEW_LINE + key + ":" + value);
});
return output;
};
VMeta.prototype.childrenToString = function () {
'use strict';
var output = "";
this.children.forEach(function (child) {
output += (NEW_LINE + child.toString());
});
return output;
};
VMeta.prototype.toString = function () {
'use strict';
var output = "BEGIN:" + this.tag;
output += this.mapToString();
output += this.childrenToString();
output += (NEW_LINE + "END:" + this.tag);
return output;
};
function VCalendar(calendarName) {
'use strict';
VMeta.call(this);
this.add(VTimeZone.Berlin());
this.tag = "VCALENDAR";
this.set('X-WR-CALNAME', calendarName);
this.set('VERSION', '2.0');
this.set('PRODID', window.location.href);
this.set('METHOD', "PUBLISH");
this.set('X-WR-TIMEZONE', "Europe/Berlin");
this.set('CALSCALE', "GREGORIAN");
};
VCalendar.prototype = Object.create(VMeta.prototype);
VCalendar.prototype.constructor = VCalendar;
function VEvent(timezoneName, startMoment, endMoment, title, description) {
VMeta.call(this);
this.tag = "VEVENT";
this.set('DTSTART;TZID=' + timezoneName, VMeta.formatDate(startMoment));
this.set('DTEND;TZID=' + timezoneName, VMeta.formatDate(endMoment));
this.set('DTSTAMP', VMeta.formatDate(moment()) + "Z");
this.set('UID', VMeta.generateUID());
this.set('SUMMARY', title);
this.set('DESCRIPTION', description);
this.set('CLASS', "PUBLIC");
this.set('TRANSP', "OPAQUE");
this.set('STATUS', "CONFIRMED");
this.set('ORGANIZER', "");
}
VEvent.prototype = Object.create(VMeta.prototype);
VEvent.prototype.constructor = VEvent;
function VTimeZone() {
VMeta.call(this);
this.tag = "VTIMEZONE";
}
VTimeZone.prototype = Object.create(VMeta.prototype);
VTimeZone.prototype.constructor = VTimeZone;
VTimeZone.Berlin = function () {
var tz = new VTimeZone();
tz.children = [VTimeDef.MESZ(), VTimeDef.MEZ()];
tz.set("TZID", "Europe/Berlin");
return tz;
}
function VTimeDef(name) {
VMeta.call(this);
this.tag = name;
}
VTimeDef.prototype = Object.create(VMeta.prototype);
VTimeDef.prototype.constructor = VTimeDef;
VTimeDef.MESZ = function () {
var timedef = new VTimeDef("DAYLIGHT");
timedef.set("TZOFFSETFROM", "+0100");
timedef.set("RRULE", "FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU");
timedef.set("DTSTART", "19819329T020000");
timedef.set("TZNAME", "MESZ");
timedef.set("TZOFFSETTO", "+0200");
return timedef;
}
VTimeDef.MEZ = function () {
var timedef = new VTimeDef("STANDARD");
timedef.set("TZOFFSETFROM", "+0200");
timedef.set("RRULE", "FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU");
timedef.set("DTSTART", "19961027T030000");
timedef.set("TZNAME", "MEZ");
timedef.set("TZOFFSETTO", "+0100");
return timedef;
}