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: ` `, data() { return { picked: null } }, props : ["options"], methods: { submitPick() { this.$emit('picked-format', this.picked); $('#ask-format-modal').modal('close'); } } }); Vue.component('chip-input', { template: `
`, 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, } }, }, watch: { shifts: { handler: function (shifts) { 'use strict'; shiftStorage.save(shifts); this.remaining = shifts.length; this.icsFile = null; this.blob = null; 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('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 name = element.Bemerkung.toString().replace(/\d\d:\d\d\s/, '').trim(); let splittedName = name.split(' '); let terminArt = ''; splittedName.forEach(function (item) { vm.rules.forEach(function (rule) { rule.Arten.forEach(function (art) { if (art.tag == item) { terminArt = art.tag; name.replace(art.tag, ''); } }) }) }); let termin = { datum : day.clone().hour(sonderzeit.hour()).minute(sonderzeit.minute()), art : terminArt, beschreibung: '', name : name.trim() } vm.addShift(new Shift(termin)); beschreibung = ''; } } 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) { let mom = moment(time, 'HH:mm'); times.push([mom.hour(), mom.minute()]); }) } else if (element.Zeit.toString().indexOf(' - ') > 0) { // in der Zeitspalte stehen mehrere Uhrzeiten als Zeitspanne let tempTimes = element.Zeit.toString().split(' - '); let mom = moment(tempTimes[0], 'HH:mm'); let momEnd = moment(tempTimes[1], 'HH:mm'); times.push([mom.hour(), mom.minute(), momEnd.hour(), momEnd.minute()]); } else { if (element.Zeit) { let mom = moment(element.Zeit); times.push([mom.hour(), mom.minute()]); } else { times.push([0, 0]); } } art = element.Dienst.trim(); if (element.hasOwnProperty('__EMPTY')) { name = element.__EMPTY.trim(); } if (element.hasOwnProperty('Bemerkung') && element.Bemerkung.toString().search(/\d\d:\d\d\s/) == -1) { beschreibung = element.Bemerkung; } times.forEach(time => { var termin = { datum : day.clone().hour(time[0]).minute(time[1]), art : art, beschreibung: beschreibung, name : name } if (time.length === 4) { // Wenn die Zeit mehr Werte hat, dann behandle die nächsten 2 als ende termin.end = day.clone().hour(time[2]).minute(time[3]); termin.dontSetDurationFromRules = true; } vm.addShift(new Shift(termin)); }); } }); }, changeTime: function (hours) { let temp = this.shifts; this.shifts = []; temp.forEach(shift => { shift.updateBeginn(hours); this.addShift(shift); }); }, updateBeginn: function (shift, hours) { shift.updateBeginn(hours); this.shifts.splice(this.shifts.indexOf(shift), 1, shift); }, 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); } })