Vue.component('click-by-click-assistant', { props: ['pages', 'allowclose', 'pagination'], data: function(){ return { activePage: 0, currentTransition:'', dataStorage: [] } }, template: '
' + '
' + '' + /** DEFAULT TEXT PAGE **/ '
' + '' + '
' + '
' + '

'+ '

'+ '

'+ '
'+ '' + '
'+ ''+ ''+ ''+ ''+ '' + '
'+ '
' + /** FORM PAGE **/ '
' + '' + '
' + '
' + '

'+ '

'+ '' + '' + '
' + '
' + '
' + '
', mounted: function(){ var self = this; self.saveDataRequiredForSubmit(); self.$on('completeStep', function(){ if(WizardContainer !== undefined){ WizardContainer.completeStep(); } self.$emit('close'); }); }, methods: { /** * @param {string} direction */ changePage: function(direction){ if(direction !== 'back' && direction !== 'next'){ return; } this.activePage = direction === 'next' ? this.activePage + 1 : this.activePage - 1; this.currentTransition = direction; }, link: function(link) { window.location.href = link; }, /** * saves all data that was defined on the building JSON file in order to submit it later */ saveDataRequiredForSubmit: function(){ var current; for(var i = 0; i < this.pages.length; i++){ current = this.pages[i].dataRequiredForSubmit; if(current === undefined || current.length === 0){ return; } this.setToStorage(current); } }, /** * @param {Object} object */ setToStorage: function(object){ this.dataStorage.push(object); }, /** * * @returns {Object} */ getStorage: function(){ return this.dataStorage; }, /** * clear storage, but keeps vue listener on this.dataStorage */ clearStorage: function(){ for (var member in this.dataStorage) { delete this.dataStorage[member]; } } } }); Vue.component('app-form',{ props: ['page'], data: function () { return { rowId: 0, surveyChoice: [], showSurveyError: false, formValid: true, formWasValidated: false, loading: false, errorMsg: 'Bitte überprüfe die Eingabefelder' } }, template: '
' + '
' + '' + ''+ ''+ '' + '' + '
' + '
'+ '' + '
'+ '' + '
{{ page.errorMsg }}
'+ '
{{ errorMsg }}
'+ '
' + '' + '
', methods:{ /** * @param {Object} row */ addInputRow: function(row){ this.rowId++ row.id = this.rowId; for(var k = 0; k < row.inputs.length; k++){ row.inputs[k].name += this.rowId; } this.page.form.push(row); for(var i = 0; i < this.page.form.length -1; i++){ this.page.form[i].add.allow = false; } if(row.add.maximum === this.page.form.length){ this.allowAddOnLastRow(false); } }, /** * @param {number} index */ removeInputRow: function(index){ if(this.page.form.length <= 1){ return; } this.page.form.splice(index, 1); this.allowAddOnLastRow(true); }, /** * @param {string} decision */ allowAddOnLastRow: function(decision){ var lastIndex = this.page.form.length - 1; this.page.form[lastIndex].add.allow = decision; }, /** * @param {Object} e */ processForm: function(e){ this.validateForm(); if(!this.formValid){ return; } if(!this.page.submitType){ throw new Error("Please define submitType in your JSON"); } if(this.page.submitType === "save"){ this.$parent.setToStorage(this.filterDataFromSubmitEvent(e)); this.$parent.changePage("next"); return; } this.submitForm(e); }, validateForm: function(){ if(this.page.submitType === 'survey'){ this.formValid = this.surveyChoice.length !== 0; this.formWasValidated = true; if(!this.formValid){ this.showSurveyError = true; } } else { this.formValid = this.requiredRowsValid(); } }, /** * Checks if the required rows are valid * @returns {boolean} */ requiredRowsValid: function(){ if(this.$refs === undefined){ console.error("Please define ref on child component"); return false; } if(this.$refs.row === undefined) { return true; } var current; for(var i = 0; i < this.$refs.row.length; i++){ current = this.$refs.row[i]; // case 1: if row has not been validated (no user input), form is valid in regard of this row if(!current.rowWasValidated){ this.formWasValidated = false; return true; } // case 2: if row is invalid, form is not valid // rowValid only includes required inputs (filtered out on row component) if(!current.rowValid){ this.formWasValidated = true; return false; } } return true; // if case1 or case2 didn't match, form valid }, /** * @param {Event} e */ submitForm: function(e){ var request = new XMLHttpRequest(), self = this, data, responseJson; data = this.prepareSubmitData(e); request.open("POST", this.page.submitUrl + '', true); request.addEventListener('load', function(event) { if (request.status >= 200 && request.status < 300) { console.log("POST " + request.statusText + " status: " + request.status); responseJson = JSON.parse(request.responseText); if(responseJson.page !== undefined) { self.$parent.pages.push(responseJson.page); } self.$parent.clearStorage(); if(responseJson.dataRequiredForSubmit !== undefined){ self.$parent.setToStorage(responseJson.dataRequiredForSubmit); } self.$parent.changePage("next"); self.loading = false; } else { console.warn(request.statusText, request.responseText); self.loading = false; self.formValid = false; self.formWasValidated = true; responseJson = JSON.parse(request.responseText); if(responseJson.error !== undefined) { self.errorMsg = responseJson.error; } else { self.errorMsg = 'Ooops, da ist etwas schief gelaufen. Bitte versuche es erneut.'; } if(responseJson.dataRequiredForSubmit !== undefined){ self.$parent.setToStorage(responseJson.dataRequiredForSubmit); } } }); self.loading = true; request.send(data); }, /** * Combines all available data of all not-submitted pages * * @param {Object} e * * @returns {FormData} */ prepareSubmitData: function(e){ var submitData = new FormData(), filteredEventData, storageData; filteredEventData = this.filterDataFromSubmitEvent(e); storageData = JSON.parse(JSON.stringify(this.$parent.getStorage())); if(storageData !== undefined && storageData.length > 0){ for(var i = 0; i < storageData.length; i++){ for(var key in storageData[i]){ submitData.append(key, storageData[i][key]); } } } if(filteredEventData !== undefined){ for(var filteredEventDataKey in filteredEventData){ submitData.append(filteredEventDataKey, filteredEventData[filteredEventDataKey]); } } return submitData; }, /** * Serializes all data from a form submit event * * @param e * * @returns {Object} */ filterDataFromSubmitEvent: function(e){ var data = {}, checkedInSurvey = [], current; for(var i = 0; i < e.target.length; i++){ current = e.target[i]; if(current.tagName === "button" || current.tagName === "BUTTON"){ continue; } if(!current.name){ throw new Error("Please define names for all inputs"); } if(this.page.type === "survey" && (current.tagName === "input" || current.tagName === "INPUT")){ if(current.checked){ checkedInSurvey.push(current.value); } data[current.name] = checkedInSurvey; } else { if(current.type === 'checkbox') { if(current.checked){ data[current.name] = current.value; } } else { data[current.name] = current.value; } } } return data; } } }); Vue.component('app-input-row',{ props: ['row', 'hasSiblings'], data: function(){ return { rowValid: true, rowWasValidated: false } }, template: '
' + '
' + '
' + '' + '
' + '
' + '
{{ row.add.text }}
' + '
{{ row.link.title }}
' + '
', methods:{ addRow: function(){ var newRow = JSON.parse(JSON.stringify(this.row)); // removes vue observable and makes it possible to change this.$parent.addInputRow(newRow); }, validateRow: function(){ this.rowValid = this.requiredInputsValid(); this.rowWasValidated = true; if(this.rowValid){ this.$parent.validateForm(); } }, /** * Checks if the required Inputs are valid * * @returns {boolean} */ requiredInputsValid: function(){ if(this.$refs === undefined){ throw new Error("Please define ref on child component"); } var valid = true, current; for(var i = 0; i < this.$refs.input.length; i++){ current = this.$refs.input[i]; if(!current.validation){ valid = true; continue; } valid = current.validation && current.valid && current.wasValidated; // row is invalid after first invalid input if(!valid){ return false; } } return valid; } } }); Vue.component('app-input', { props: ['type', 'validation', 'name', 'label', 'customErrorMsg', 'options', 'value', 'connectedTo'], data: function () { return { inputValue: this.value ? this.value : '', inputType: this.type, inputErrorMsg: undefined, valid: true, wasValidated: false } }, template: '
' + '' + '' + '
'+ '
' + //'' + '' + '
' + '' + '' + '
{{ inputErrorMsg }}
' + '
'+ '
', mounted: function(){ var self = this; // listens to compare request "broadcast" from other component self.$root.$on('compareConnected', function(data){ if(self.name !== data.connectedTo) { return; } // "broadcasts" to every component listening self.$root.$emit('comparisonResult', { requestingInput: data.requestingInput.name, valid: self.inputValue === data.requestingInput.inputValue && self.valid }) }); self.$root.$on('comparisonResult', function(result){ if(self.name === result.requestingInput){ self.valid = result.valid; } }); }, methods: { validateInput: function(){ if((this.inputValue.length === 0 && !this.wasValidated) || !this.validation){ // input is valid if it has a value and wasn't validated before (inputs do not get validated on page render) // or if it's not necessary to validate this.valid = true; return; } switch(this.type){ case "email": var regex = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; this.valid = regex.test(this.inputValue); this.inputErrorMsg = this.customErrorMsg || "Adresse nicht gültig"; break; case "text": this.valid = this.inputValue.length >= 2; this.inputErrorMsg = this.customErrorMsg || "Mindestens zwei Zeichen"; break; case "password": // "broadcasting" event to listening components // In this case we compare if passwords match in connected fields -> "connectedTo" option in JSON if(this.connectedTo !== undefined){ this.$root.$emit('compareConnected', { connectedTo: this.connectedTo, requestingInput: { name: this.name, inputValue: this.inputValue } }); this.inputErrorMsg = this.customErrorMsg || "Bitte wiederholen Sie das Passwort"; } else { this.valid = this.inputValue.length >= 4; this.inputErrorMsg = this.customErrorMsg || "Mindestens vier Zeichen"; } break; case "select": // it's "selected/changed" (event) so it always has a valid value break; default: break; } this.wasValidated = true; this.$parent.validateRow(); }, togglePasswordVisibility: function(){ this.inputType = this.inputType === 'password' ? 'text' : 'password'; } } }); Vue.component('app-pagination', { props: ["pages", "index"], template: '' + '
' + '
' + '
' }); Vue.component('app-spinner',{ template: '
' }); Vue.component('app-media',{ props: ["media"], template: '
' + '' + '' + '' + '
' });