588 lines
20 KiB
JavaScript

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);
}
}
//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 parsed = JSON.parse(localStorage.getItem(RULE_STORAGE_KEY)) | [];
var rules = parsed.length > 0 ?
parsed.map((e, i) => {
var r = Rule.thaw(e);
r.id = i;
return r;
}) :
Rule.defaults();
ruleStorage.uid = rules.length;
return rules;
},
save: function (rules) {
'use strict';
var json = JSON.stringify(rules);
localStorage.setItem(RULE_STORAGE_KEY, json);
}
};
Vue.component('ask-format-modal', {
template: `
<div id="ask-format-modal" class="modal">
<div class="modal-content">
<h4>Dienstplanformat</h4>
<p>Welches Format soll eingelesen werden?</p>
<label v-for="option in options" >
<input name="dpFormat" type="radio" :value="option" v-model="picked"/>
<span>{{ option }}</span>
</label>
<p>Ausgewählt: {{ picked }}</p>
</div>
<div class="modal-footer">
<a href="#!" class="modal-close waves-effect waves-green btn-flat" @click.stop="submitPick">Bastätigen</a>
</div>
</div>
`,
data() {
return {
picked: null
}
},
props: ["options"],
methods: {
submitPick() {
this.$emit('picked-format', this.picked);
$('#ask-format-modal').modal('close');
}
}
});
Vue.component('chip-input', {
template: `
<div class="chips no-autoinit" :id="name"></div>
`,
data() {
return {
instance: null,
chips: []
}
},
computed: {
chipsData() {
return this.instance.chipsData;
}
},
watch: {
initData: {
deep: true,
handler(n, o) {
if (n !== o) {
this.initialize();
this.$emit('init');
}
}
}
},
props: {
name: String,
initData: Array
},
methods: {
initialize() {
this.chips = this.initData.map(e => e);
var el = $('#' + this.name)[0];
this.instance = M.Chips.init(el, {
data: this.chips,
onChipAdd: () => {
this.$emit("change", this.chipsData);
},
onChipDelete: () => {
this.$emit("change", this.chipsData);
}
});
}
},
mounted() {
this.initialize();
}
});
var app = new Vue({
el: '#app',
data: {
shifts: shiftStorage.fetch(),
rules: ruleStorage.fetch(),
icsFile: null,
blob: null,
dp_sheet: '',
deletedShift: '',
format: '',
remaining: shiftStorage.count(),
selectedShift: new Shift({}),
selectedShiftIndex: -1,
selectedRule: new Rule({}),
selectedRuleIndex: -1,
saveto: 'dienstplan.ics',
uploadFileName: "",
availableFormats: ["Erfurt", "Stuttgart", "X"],
stepper: null,
timepickers: null,
config: {
moment: {
parse_formats: [
"ddd, DD/ MMM. 'YY HH:mm",
"ddd, DD/ MMM. YYYY HH:mm"
],
parse_language: 'en',
display_language: 'de'
},
stepper: {
firstActive: 0,
},
toast: {
displayLength: 3000
},
timepicker: {
twelveHour: false,
}
},
workbook: null,
},
watch: {
shifts: {
handler: function (shifts) {
'use strict';
shiftStorage.save(shifts);
this.remaining = shifts.length;
this.makeToast("Änderungen gespeichert.");
},
deep: true
},
rules: {
handler: function (rules) {
'use strict';
ruleStorage.save(rules);
this.makeToast("Änderungen gespeichert.");
},
deep: true
}
},
computed: {
groupedTermine() {
return _.chain(this.shifts).sortBy(e => e.Datum).groupBy(e => e.Datum).value();
}
},
methods: {
updateArten(value) {
this.selectedRule.Arten = value;
},
updateTitel(value) {
this.selectedRule.Titel = value;
},
makeToast(message) {
var toastOptions = this.config.toast;
toastOptions.html = message;
M.toast(toastOptions);
},
openModal(elementID) {
var element = document.getElementById(elementID);
var modal = M.Modal.getInstance(element);
modal.open();
},
closeModal(elementID) {
var element = document.getElementById(elementID);
var modal = M.Modal.getInstance(element);
modal.close();
},
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]";
this.makeToast(this.uploadFileName + " ausgewählt");
},
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,
});
var isErfurterDienstplan = workbook.SheetNames.indexOf("Dienstplan") > -1;
if (isErfurterDienstplan) {
vm.parseForErfurt(workbook.Sheets["Dienstplan"]);
} else {
this.workbook = workbook;
this.askForDienstplanFormat();
}
};
reader.readAsBinaryString(file);
},
askForDienstplanFormat: function () {
this.makeToast("Dienstplanformat nicht erkannt.");
this.openModal('ask-format-modal');
},
parseForErfurt: function (dp) {
var arr = XLSX.utils.sheet_to_row_object_array(dp, {
range: 1
});
this.format = "Erfurt";
var vm = this;
var day;
this.makeToast("Erfurter Dienstplan erkannt.");
arr.forEach(element => {
moment.locale(vm.config.moment.parse_language);
if (element.hasOwnProperty('Datum')) {
day = moment(element.Datum);
}
if (element.hasOwnProperty('Dienst')) {
let times = [];
let art, beschreibung, name = "";
if (element.Zeit.toString().indexOf(' + ') > 0) {
// in der Zeitspalte stehen mehrere Uhrzeiten.
let tempTimes = element.Zeit.toString().split(' + ');
tempTimes.forEach(function(time) {
times.push(moment(time, 'HH:mm'));
})
} else {
// übrliches Format
times.push(element.Zeit ? moment(element.Zeit) : day);
}
if (element.hasOwnProperty('Bemerkung')) {
if (element.Bemerkung.toString().search(/\d\d:\d\d\s/) >= 0) {
// prüfe ob eine Uhrzeit drinnen steht
let sonderzeit = moment(element.Bemerkung, 'HH:mm');
let termin = {
datum: day.clone().hour(sonderzeit.hour()).minute(sonderzeit.minute()),
art: '',
beschreibung: '',
name: element.Bemerkung.toString().replace(/\d\d:\d\d\s/, '').trim()
}
if (!termin.datum.isDST()) {
termin.datum.add(1, 'hours')
}
vm.addShift(new Shift(termin));
beschreibung = '';
} else {
beschreibung = element.Bemerkung;
}
}
if (element.hasOwnProperty('Dienst')) {
art = element.Dienst.trim();
}
if (element.hasOwnProperty('__EMPTY')) {
name = element.__EMPTY.trim();
}
times.forEach(time => {
var termin = {
datum: day.clone().hour(time.hour()).minute(time.minute()),
art: art,
beschreibung: beschreibung,
name: name
}
if (!termin.datum.isDST()) {
termin.datum.add(1, 'hours')
}
vm.addShift(new Shift(termin));
});
}
});
},
parseForStuttgart: function (dp) {
var arr = XLSX.utils.sheet_to_json(dp, {
header: "A",
blankrows: false,
});
var vm = this;
var day;
this.makeToast("Stuttgarter Dienstplan erkannt.");
arr.forEach(element => {
moment.locale(vm.config.moment.parse_language);
if (element.hasOwnProperty('C')) {
day = moment(element.C);
}
if (element.hasOwnProperty('D') && moment(element.D, "HH:mm").isValid()) {
var termin = {
ort: element.H ? element.H.trim() : "",
art: element.E ? element.E.trim() : "",
beschreibung: element.H ? element.H.trim() : "",
name: element.F ? element.F.trim() : ""
}
var time = day.clone();
if (typeof (element.D) === "object") {
time = moment(element.D);
termin.datum = day.clone().hour(time.hour()).minute(time.minute());
} else if (element.D.indexOf("-") > -1) {
var tArray = element.D.split(" - ");
time = moment(tArray[0], "HH:mm");
termin.datum = day.clone().hour(time.hour()).minute(time.minute());
termin.ende = moment(tArray[1], "HH:mm").format("HH:mm");
}
vm.addShift(new Shift(termin));
}
});
},
parseForX: function (dp, sheetName) {
moment.locale(this.config.moment.parse_language);
var arr = XLSX.utils.sheet_to_json(dp, {
header: "A",
blankrows: false,
});
var vm = this;
var month = moment(sheetName.substr(sheetName.indexOf(" ") + 1), "MMMM YYYY", "de");
var day;
arr.forEach(element => {
var art, name, beschreibung = null;
var time = moment().hour(0).minute(0);
try {
if (element.hasOwnProperty('A')) {
day = moment(element.A, "D.", "de");
day = Number(element.A);
}
if ((!element.F && !element.H && !element.J) || element.I == "Spielort/ Extras" || element.J == "Spielort/Extras") {
} else {
if (element.hasOwnProperty("D") && element.hasOwnProperty("E")) {
// Probe
time = moment(element.D);
art = element.E.trim();
name = element.F ? element.F.trim() : "";
beschreibung = typeof (element.I) != 'undefined' ? element.I.trim() :
typeof (element.J) != 'undefined' ? element.J.trim() :
"";
} else if (element.hasOwnProperty("G") && element.hasOwnProperty("H")) {
// Vorstellung
time = moment(element.G);
art = "VS";
name = element.H.trim();
beschreibung = typeof (element.I) != 'undefined' ? element.I.trim() :
typeof (element.J) != 'undefined' ? element.J.trim() :
"";
} else if (element.hasOwnProperty("I")) {
// Spielort/Extras
beschreibung = element.I.trim();
name = name ? name : "Spielort/Extras";
} else if (element.hasOwnProperty("J")) {
// Spielort/Extras Fallback
beschreibung = element.J.trim();
name = name ? name : "Spielort/Extras";
}
var datumStr = day + '.' + month.clone().format("MM.YY") + time.format(" HH:mm");
var termin = {
ort: "",
art: art,
beschreibung: beschreibung,
name: name,
datum: moment(datumStr, "D.MM.YY HH:mm")
}
vm.addShift(new Shift(termin));
}
} catch (error) {
console.error("Fehler beim Parsen", element, error);
}
});
},
parsePickedFormat: function (format) {
this.makeToast(`Versuche ${format} zu lesen.`);
switch (format) {
case "Erfurt":
this.parseForErfurt(this.workbook.Sheets["Dienstplan"]);
break;
case "Stuttgart":
var sheetName = this.workbook.SheetNames[0];
this.parseForStuttgart(this.workbook.Sheets[sheetName]);
break;
case "X":
var sheetName = this.workbook.SheetNames[0];
this.parseForX(this.workbook.Sheets[sheetName], sheetName);
break;
default:
break;
}
this.workbook = null;
},
addShift: function (shift) {
this.shifts.push(shift);
},
removeShift: function (shift) {
this.shifts.splice(this.shifts.indexOf(shift), 1);
this.makeToast(shift.VEventTitle + " gelöscht");
},
cleanStorage: function () {
this.shifts = [];
this.makeToast("Alle Einträge gelöscht");
},
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);
this.makeToast(this.saveto + " erstellt.");
},
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();
this.openModal('shiftModal');
},
saveChanges: function (changedShift) {
this.shifts.splice(this.shifts.indexOf(this.keepShift), 1, changedShift);
this.closeModal('shiftModal');
this.keepShift = '';
this.selectedShift = '';
},
discardChanges: function (changedShift) {
this.closeModal('shiftModal');
this.keepShift = '';
this.selectedShift = '';
},
editRule: function (rule) {
this.selectedRule = Rule.thaw(rule);
this.selectedRuleIndex = this.rules.indexOf(rule);
this.openModal('ruleModal');
},
saveRule: function () {
this.rules[this.selectedRuleIndex] = this.selectedRule;
this.closeModal('ruleModal');
this.selectedRule = new Rule();
},
discardRule: function () {
this.closeModal('ruleModal');
this.selectedRule = new Rule();
},
applyRules: function () {
var shifts = this.shifts;
shifts.forEach(function (shift) {
Shift.setDurationFromRules(shift, this.rules);
});
this.shifts = shifts;
}
},
directives: {
'edit-focus': function (el, value) {
if (value) {
el.focus();
}
}
},
mounted: function () {
M.AutoInit();
var els = document.querySelectorAll('.timepicker');
this.timepickers = M.Timepicker.init(els, this.config.timepicker);
var el = document.querySelector(".stepper");
this.stepper = new MStepper(el, this.config.stepper);
}
})