@ditdot-dev/vue-flow-form
Version:
Create conversational conditional-logic forms with Vue.js.
3,741 lines • 117 kB
JavaScript
import { openBlock, createElementBlock, createElementVNode, createCommentVNode, Fragment, renderList, toDisplayString, normalizeClass, resolveDirective, withDirectives, resolveComponent, createBlock, withKeys, withModifiers, normalizeStyle, vModelText, createVNode, vModelRadio, vModelCheckbox, createTextVNode, resolveDynamicComponent, renderSlot } from 'vue';
/*!
Copyright (c) 2020 - present, DITDOT Ltd. - MIT Licence
https://github.com/ditdot-dev/vue-flow-form
https://www.ditdot.hr/en
*/
// Language data store
var LanguageModel = function LanguageModel(options) {
this.enterKey = 'Enter';
this.shiftKey = 'Shift';
this.ok = 'OK';
this.continue = 'Continue';
this.skip = 'Skip';
this.pressEnter = 'Press :enterKey';
this.multipleChoiceHelpText = 'Choose as many as you like';
this.multipleChoiceHelpTextSingle = 'Choose only one answer';
this.otherPrompt = 'Other';
this.placeholder = 'Type your answer here...';
this.submitText = 'Submit';
this.longTextHelpText = ':shiftKey + :enterKey to make a line break.';
this.prev = 'Prev';
this.next = 'Next';
this.percentCompleted = ':percent% completed';
this.invalidPrompt = 'Please fill out the field correctly';
this.thankYouText = 'Thank you!';
this.successText = 'Your submission has been sent.';
this.ariaOk = 'Press to continue';
this.ariaRequired = 'This step is required';
this.ariaPrev = 'Previous step';
this.ariaNext = 'Next step';
this.ariaSubmitText = 'Press to submit';
this.ariaMultipleChoice = 'Press :letter to select';
this.ariaTypeAnswer = 'Type your answer here';
this.errorAllowedFileTypes = 'Invalid file type. Allowed file types: :fileTypes.';
this.errorMaxFileSize = 'File(s) too large. Maximum allowed file size: :size.';
this.errorMinFiles = 'Too few files added. Minimum allowed files: :min.';
this.errorMaxFiles = 'Too many files added. Maximum allowed files: :max.';
Object.assign(this, options || {});
};
/**
* Inserts a new CSS class into the language model string to format the :string
* Use it in a component's v-html directive: v-html="language.formatString(language.languageString)"
*/
LanguageModel.prototype.formatString = function formatString (string, replacements) {
var this$1$1 = this;
return string.replace(/:(\w+)/g, function (match, word) {
if (this$1$1[word]) {
return '<span class="f-string-em">' + this$1$1[word] + '</span>'
} else if (replacements && replacements[word]) {
return replacements[word]
}
return match
})
};
LanguageModel.prototype.formatFileSize = function formatFileSize (bytes) {
var
units = ['B', 'kB', 'MB', 'GB', 'TB'],
i = bytes > 0 ? Math.floor(Math.log(bytes) / Math.log(1024)) : 0;
return (bytes / Math.pow(1024, i)).toFixed(2) * 1 + ' ' + units[i];
};
/*
Copyright (c) 2020 - present, DITDOT Ltd. - MIT Licence
https://github.com/ditdot-dev/vue-flow-form
https://www.ditdot.hr/en
*/
// Global data store
var QuestionType = Object.freeze({
Date: 'FlowFormDateType',
Dropdown: 'FlowFormDropdownType',
Email: 'FlowFormEmailType',
File: 'FlowFormFileType',
LongText: 'FlowFormLongTextType',
MultipleChoice: 'FlowFormMultipleChoiceType',
MultiplePictureChoice: 'FlowFormMultiplePictureChoiceType',
Number: 'FlowFormNumberType',
Password: 'FlowFormPasswordType',
Phone: 'FlowFormPhoneType',
SectionBreak: 'FlowFormSectionBreakType',
Text: 'FlowFormTextType',
Url: 'FlowFormUrlType',
Matrix: 'FlowFormMatrixType',
OpinionScale: 'FlowFormOpinionScaleType',
IconRate: 'FlowFormIconRateType',
});
Object.freeze({
label: '',
value: '',
disabled: true
});
var MaskPresets = Object.freeze({
Date: '##/##/####',
DateIso: '####-##-##',
PhoneUs: '(###) ###-####'
});
var ChoiceOption = function ChoiceOption(options) {
this.label = '';
this.value = null;
this.selected = false;
this.imageSrc = null;
this.imageAlt = null;
Object.assign(this, options);
};
ChoiceOption.prototype.choiceLabel = function choiceLabel () {
return this.label || this.value
};
ChoiceOption.prototype.choiceValue = function choiceValue () {
// Returns the value if it's anything other than the default (null).
if (this.value !== null) {
return this.value
}
// Returns any other non-empty property if the value has not been set.
return this.label || this.imageAlt || this.imageSrc
};
ChoiceOption.prototype.toggle = function toggle () {
this.selected = !this.selected;
};
var LinkOption = function LinkOption(options) {
this.url = '';
this.text = '';
this.target = '_blank';
Object.assign(this, options);
};
var MatrixColumn = function MatrixColumn(options) {
this.value = '';
this.label = '';
Object.assign(this, options);
};
var MatrixRow = function MatrixRow(options) {
this.id = '';
this.label = '';
Object.assign(this, options);
};
var QuestionModel = function QuestionModel(options) {
// Make sure the options variable is an object
options = options || {};
this.id = null;
this.answer = null;
this.answered = false;
this.index = 0;
this.options = [];
this.description = '';
this.className = '';
this.type = null;
this.html = null;
this.required = false;
this.jump = null;
this.placeholder = null;
this.mask = '';
this.multiple = false;
this.allowOther = false;
this.other = null;
this.language = null;
this.tagline = null;
this.title = null;
this.subtitle = null;
this.content = null;
this.inline = false;
this.helpText = null;
this.helpTextShow = true;
this.descriptionLink = [];
this.min = null;
this.max = null;
this.maxLength = null;
this.nextStepOnAnswer = false;
this.accept = null;
this.maxSize = null;
this.rows = [];
this.columns = [];
this.labelLeft = null;
this.labelRight = null;
Object.assign(this, options);
// Sets default mask and placeholder value on PhoneType question
if (this.type === QuestionType.Phone) {
if (!this.mask) {
this.mask = MaskPresets.Phone;
}
if (!this.placeholder) {
this.placeholder = this.mask;
}
}
if (this.type === QuestionType.Url) {
this.mask = null;
}
if (this.type === QuestionType.Date && !this.placeholder) {
this.placeholder = 'yyyy-mm-dd';
}
if (this.type !== QuestionType.Matrix && this.multiple && !Array.isArray(this.answer)) {
this.answer = this.answer ? [this.answer] : [];
}
// Check if we have an answer already (when we have a pre-filled form)
// and set the answered property accordingly
if (!this.required && typeof options.answer !== 'undefined') {
this.answered = true;
} else if (this.answer && (!this.multiple || this.answer.length)) {
this.answered = true;
}
this.resetOptions();
};
QuestionModel.prototype.getJumpId = function getJumpId () {
var nextId = null;
if (typeof this.jump === 'function') {
nextId = this.jump.call(this);
} else if (this.jump[this.answer]) {
nextId = this.jump[this.answer];
} else if (this.jump['_other']) {
nextId = this.jump['_other'];
}
return nextId
};
QuestionModel.prototype.setAnswer = function setAnswer (answer) {
if (this.type === QuestionType.Number && answer !== '' && !isNaN(+answer)) {
answer = +answer;
}
this.answer = answer;
};
QuestionModel.prototype.setIndex = function setIndex (index) {
if (!this.id) {
this.id = 'q_' + index;
}
this.index = index;
};
QuestionModel.prototype.resetOptions = function resetOptions () {
var this$1$1 = this;
if (this.options) {
var isArray = Array.isArray(this.answer);
var numSelected = 0;
this.options.forEach(function (o) {
var optionValue = o.choiceValue();
if (this$1$1.answer === optionValue || (isArray && this$1$1.answer.indexOf(optionValue) !== -1)) {
o.selected = true;
++numSelected;
} else {
o.selected = false;
}
});
if (this.allowOther) {
var otherAnswer = null;
if (isArray) {
if (this.answer.length && this.answer.length !== numSelected) {
otherAnswer = this.answer[this.answer.length - 1];
}
} else if (this.options.map(function (o) { return o.choiceValue(); }).indexOf(this.answer) === -1) {
otherAnswer = this.answer;
}
if (otherAnswer !== null) {
this.other = otherAnswer;
}
}
}
};
QuestionModel.prototype.resetAnswer = function resetAnswer () {
this.answered = false;
this.answer = this.multiple ? [] : null;
this.other = null;
this.resetOptions();
};
QuestionModel.prototype.isMultipleChoiceType = function isMultipleChoiceType () {
return [QuestionType.MultipleChoice, QuestionType.MultiplePictureChoice].includes(this.type)
};
/*
Copyright (c) 2020 - present, DITDOT Ltd. - MIT Licence
https://github.com/ditdot-dev/vue-flow-form
https://www.ditdot.hr/en
*/
var
isIos = false,
isMobile = false;
if (typeof navigator !== 'undefined' && typeof document !== 'undefined') {
// Simple mobile device/tablet detection
isIos = navigator.userAgent.match(/iphone|ipad|ipod/i) || (navigator.userAgent.indexOf('Mac') !== -1 && 'ontouchend' in document);
isMobile = isIos || navigator.userAgent.match(/android/i);
}
// Mixin that adds `isMobile` and `isIos` data variables
var IsMobile = {
data: function data() {
return {
isIos: isIos,
isMobile: isMobile
}
}
};
var script$l = {
name: 'FlowFormBaseType',
props: {
language: LanguageModel,
question: QuestionModel,
active: Boolean,
disabled: Boolean,
modelValue: [String, Array, Boolean, Number, Object]
},
mixins: [
IsMobile ],
data: function data() {
return {
dirty: false,
dataValue: null,
answer: null,
enterPressed: false,
allowedChars: null,
alwaysAllowedKeys: ['ArrowLeft', 'ArrowRight', 'Delete', 'Backspace'],
focused: false,
canReceiveFocus: false,
errorMessage: null
}
},
mounted: function mounted() {
if (this.question.answer) {
this.dataValue = this.answer = this.question.answer;
} else if (this.question.multiple) {
this.dataValue = [];
}
},
methods: {
/**
* This method can be overriden in custom components to
* change the answer before going through validation.
*/
fixAnswer: function fixAnswer(answer) {
return answer
},
getElement: function getElement() {
var el = this.$refs.input;
// Sometimes the input is nested so we need to find it
while (el && el.$el) {
el = el.$el;
}
return el
},
setFocus: function setFocus() {
this.focused = true;
},
// eslint-disable-next-line no-unused-vars
unsetFocus: function unsetFocus($event) {
this.focused = false;
},
focus: function focus() {
if (!this.focused) {
var el = this.getElement();
el && el.focus();
}
},
blur: function blur() {
var el = this.getElement();
el && el.blur();
},
onKeyDown: function onKeyDown($event) {
this.enterPressed = false;
clearTimeout(this.timeoutId);
if ($event) {
if ($event.key === 'Enter' && !$event.shiftKey) {
this.unsetFocus();
}
if (this.allowedChars !== null) {
// Check if the entered character is allowed.
// We always allow keys from the alwaysAllowedKeys array.
if (this.alwaysAllowedKeys.indexOf($event.key) === -1 && this.allowedChars.indexOf($event.key) === -1) {
$event.preventDefault();
}
}
}
},
onChange: function onChange($event) {
this.dirty = true;
this.dataValue = $event.target.value;
this.onKeyDown();
this.setAnswer(this.dataValue);
},
onEnter: function onEnter() {
this._onEnter();
},
_onEnter: function _onEnter() {
this.enterPressed = true;
this.dataValue = this.fixAnswer(this.dataValue);
this.setAnswer(this.dataValue);
this.isValid() ? this.blur() : this.focus();
},
setAnswer: function setAnswer(answer) {
this.question.setAnswer(answer);
this.answer = this.question.answer;
this.question.answered = this.isValid();
this.$emit('update:modelValue', this.answer);
},
showInvalid: function showInvalid() {
return this.dirty && this.enterPressed && !this.isValid()
},
isValid: function isValid() {
if (!this.question.required && !this.hasValue && this.dirty) {
return true
}
if (this.validate()) {
return true
}
return false
},
/**
* This method validates the input and is meant to be overriden
* in custom question types.
*/
validate: function validate() {
return !this.question.required || this.hasValue
}
},
computed: {
placeholder: function placeholder() {
return this.question.placeholder || this.language.placeholder
},
hasValue: function hasValue() {
if (this.dataValue !== null) {
var v = this.dataValue;
if (v.trim) {
// Don't allow empty strings
return v.trim().length > 0
}
if (Array.isArray(v)) {
// Don't allow empty arrays
return v.length > 0
}
// All other non-null values are allowed to pass through
return true
}
return false
}
}
};
script$l.__file = "src/components/QuestionTypes/BaseType.vue";
var script$k = {
extends: script$l,
name: QuestionType.Dropdown,
computed: {
answerLabel: function answerLabel() {
for (var i = 0; i < this.question.options.length; i++) {
var option = this.question.options[i];
if (option.choiceValue() === this.dataValue) {
return option.choiceLabel()
}
}
return this.question.placeholder
}
},
methods: {
onKeyDownListener: function onKeyDownListener($event) {
if ($event.key === 'ArrowDown' || $event.key === 'ArrowUp') {
this.setAnswer(this.dataValue);
} else if ($event.key === 'Enter' && this.hasValue) {
this.focused = false;
this.blur();
}
},
onKeyUpListener: function onKeyUpListener($event) {
if ($event.key === 'Enter' && this.isValid() && !this.disabled) {
$event.stopPropagation();
this._onEnter();
this.$emit('next');
}
}
}
};
var _hoisted_1$9 = { class: "faux-form" };
var _hoisted_2$7 = ["value", "required"];
var _hoisted_3$5 = {
key: 0,
label: " ",
value: "",
disabled: "",
selected: "",
hidden: ""
};
var _hoisted_4$5 = ["disabled", "value"];
var _hoisted_5$5 = /*#__PURE__*/createElementVNode("span", { class: "f-arrow-down" }, [
/*#__PURE__*/createElementVNode("svg", {
version: "1.1",
id: "Capa_1",
xmlns: "http://www.w3.org/2000/svg",
"xmlns:xlink": "http://www.w3.org/1999/xlink",
x: "0px",
y: "0px",
viewBox: "-163 254.1 284.9 284.9",
style: "",
"xml:space": "preserve"
}, [
/*#__PURE__*/createElementVNode("g", null, [
/*#__PURE__*/createElementVNode("path", { d: "M119.1,330.6l-14.3-14.3c-1.9-1.9-4.1-2.9-6.6-2.9c-2.5,0-4.7,1-6.6,2.9L-20.5,428.5l-112.2-112.2c-1.9-1.9-4.1-2.9-6.6-2.9c-2.5,0-4.7,0.9-6.6,2.9l-14.3,14.3c-1.9,1.9-2.9,4.1-2.9,6.6c0,2.5,1,4.7,2.9,6.6l133,133c1.9,1.9,4.1,2.9,6.6,2.9s4.7-1,6.6-2.9l133.1-133c1.9-1.9,2.8-4.1,2.8-6.6C121.9,334.7,121,332.5,119.1,330.6z" })
])
])
], -1 /* HOISTED */);
function render$b(_ctx, _cache, $props, $setup, $data, $options) {
return (openBlock(), createElementBlock("span", _hoisted_1$9, [
createElementVNode("select", {
ref: "input",
class: "",
value: _ctx.dataValue,
onChange: _cache[0] || (_cache[0] = function () {
var args = [], len = arguments.length;
while ( len-- ) args[ len ] = arguments[ len ];
return (_ctx.onChange && _ctx.onChange.apply(_ctx, args));
}),
onKeydown: _cache[1] || (_cache[1] = function () {
var args = [], len = arguments.length;
while ( len-- ) args[ len ] = arguments[ len ];
return ($options.onKeyDownListener && $options.onKeyDownListener.apply($options, args));
}),
onKeyup: _cache[2] || (_cache[2] = function () {
var args = [], len = arguments.length;
while ( len-- ) args[ len ] = arguments[ len ];
return ($options.onKeyUpListener && $options.onKeyUpListener.apply($options, args));
}),
required: _ctx.question.required
}, [
(_ctx.question.required)
? (openBlock(), createElementBlock("option", _hoisted_3$5, " "))
: createCommentVNode("v-if", true),
(openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.question.options, function (option, index) {
return (openBlock(), createElementBlock("option", {
disabled: option.disabled,
value: option.choiceValue(),
key: 'o' + index
}, toDisplayString(option.choiceLabel()), 9 /* TEXT, PROPS */, _hoisted_4$5))
}), 128 /* KEYED_FRAGMENT */))
], 40 /* PROPS, HYDRATE_EVENTS */, _hoisted_2$7),
createElementVNode("span", null, [
createElementVNode("span", {
class: normalizeClass(["f-empty", {'f-answered': this.question.answer && this.question.answered}])
}, toDisplayString($options.answerLabel), 3 /* TEXT, CLASS */),
_hoisted_5$5
])
]))
}
script$k.render = render$b;
script$k.__file = "src/components/QuestionTypes/DropdownType.vue";
function maskit (value, mask, masked, tokens) {
if ( masked === void 0 ) masked = true;
value = value || '';
mask = mask || '';
var iMask = 0;
var iValue = 0;
var output = '';
while (iMask < mask.length && iValue < value.length) {
var cMask = mask[iMask];
var masker = tokens[cMask];
var cValue = value[iValue];
if (masker && !masker.escape) {
if (masker.pattern.test(cValue)) {
output += masker.transform ? masker.transform(cValue) : cValue;
iMask++;
}
iValue++;
} else {
if (masker && masker.escape) {
iMask++; // take the next mask char and treat it as char
cMask = mask[iMask];
}
if (masked) { output += cMask; }
if (cValue === cMask) { iValue++; } // user typed the same char
iMask++;
}
}
// fix mask that ends with a char: (#)
var restOutput = '';
while (iMask < mask.length && masked) {
var cMask = mask[iMask];
if (tokens[cMask]) {
restOutput = '';
break
}
restOutput += cMask;
iMask++;
}
return output + restOutput
}
function dynamicMask (maskit, masks, tokens) {
masks = masks.sort(function (a, b) { return a.length - b.length; });
return function (value, mask, masked) {
if ( masked === void 0 ) masked = true;
var i = 0;
while (i < masks.length) {
var currentMask = masks[i];
i++;
var nextMask = masks[i];
if (! (nextMask && maskit(value, nextMask, true, tokens).length > currentMask.length) ) {
return maskit(value, currentMask, masked, tokens)
}
}
return '' // empty masks
}
}
// Facade to maskit/dynamicMask when mask is String or Array
function masker (value, mask, masked, tokens) {
if ( masked === void 0 ) masked = true;
return Array.isArray(mask)
? dynamicMask(maskit, mask, tokens)(value, mask, masked, tokens)
: maskit(value, mask, masked, tokens)
}
var tokens = {
'#': {pattern: /\d/},
'X': {pattern: /[0-9a-zA-Z]/},
'S': {pattern: /[a-zA-Z]/},
'A': {pattern: /[a-zA-Z]/, transform: function (v) { return v.toLocaleUpperCase(); }},
'a': {pattern: /[a-zA-Z]/, transform: function (v) { return v.toLocaleLowerCase(); }},
'!': {escape: true}
};
// https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events#The_old-fashioned_way
function event (name) {
var evt = document.createEvent('Event');
evt.initEvent(name, true, true);
return evt
}
function mask (el, binding) {
var config = binding.value;
if (Array.isArray(config) || typeof config === 'string') {
config = {
mask: config,
tokens: tokens
};
}
if (el.tagName.toLocaleUpperCase() !== 'INPUT') {
var els = el.getElementsByTagName('input');
if (els.length !== 1) {
throw new Error("v-mask directive requires 1 input, found " + els.length)
} else {
el = els[0];
}
}
el.oninput = function (evt) {
if (!evt.isTrusted) { return } // avoid infinite loop
/* other properties to try to diferentiate InputEvent of Event (custom)
InputEvent (native)
cancelable: false
isTrusted: true
composed: true
isComposing: false
which: 0
Event (custom)
cancelable: true
isTrusted: false
*/
// by default, keep cursor at same position as before the mask
var position = el.selectionEnd;
// save the character just inserted
var digit = el.value[position-1];
el.value = masker(el.value, config.mask, true, config.tokens);
// if the digit was changed, increment position until find the digit again
while (position < el.value.length && el.value.charAt(position-1) !== digit) {
position++;
}
if (el === document.activeElement) {
el.setSelectionRange(position, position);
setTimeout(function () {
el.setSelectionRange(position, position);
}, 0);
}
el.dispatchEvent(event('input'));
};
var newDisplay = masker(el.value, config.mask, true, config.tokens);
if (newDisplay !== el.value) {
el.value = newDisplay;
el.dispatchEvent(event('input'));
}
}
var script$j = {
name: 'TheMask',
props: {
value: [String, Number],
mask: {
type: [String, Array],
required: true
},
masked: { // by default emits the value unformatted, change to true to format with the mask
type: Boolean,
default: false // raw
},
tokens: {
type: Object,
default: function () { return tokens; }
}
},
directives: {mask: mask},
data: function data () {
return {
lastValue: null, // avoid unecessary emit when has no change
display: this.value
}
},
watch : {
value: function value (newValue) {
if (newValue !== this.lastValue) {
this.display = newValue;
}
},
masked: function masked () {
this.refresh(this.display);
}
},
computed: {
config: function config () {
return {
mask: this.mask,
tokens: this.tokens,
masked: this.masked
}
}
},
methods: {
onInput: function onInput (e) {
if (e.isTrusted) { return } // ignore native event
this.refresh(e.target.value);
},
refresh: function refresh (value) {
this.display = value;
var value = masker(value, this.mask, this.masked, this.tokens);
if (value !== this.lastValue) {
this.lastValue = value;
this.$emit('input', value);
}
}
}
};
var _hoisted_1$8 = ["value"];
function render$a(_ctx, _cache, $props, $setup, $data, $options) {
var _directive_mask = resolveDirective("mask");
return withDirectives((openBlock(), createElementBlock("input", {
type: "text",
value: $data.display,
onInput: _cache[0] || (_cache[0] = function () {
var args = [], len = arguments.length;
while ( len-- ) args[ len ] = arguments[ len ];
return ($options.onInput && $options.onInput.apply($options, args));
})
}, null, 40 /* PROPS, HYDRATE_EVENTS */, _hoisted_1$8)), [
[_directive_mask, $options.config]
])
}
script$j.render = render$a;
script$j.__file = "node_modules/vue-the-mask/src/component.vue";
var script$i = {
extends: script$l,
name: QuestionType.Text,
components: {
TheMask: script$j
},
data: function data() {
return {
inputType: 'text',
canReceiveFocus: true
}
},
methods: {
validate: function validate() {
if (this.question.mask && this.hasValue) {
return this.validateMask()
}
return !this.question.required || this.hasValue
},
validateMask: function validateMask() {
var this$1$1 = this;
if (Array.isArray(this.question.mask)) {
return this.question.mask.some(function (mask) { return mask.length === this$1$1.dataValue.length; })
}
return this.dataValue.length === this.question.mask.length
}
}
};
var _hoisted_1$7 = ["data-placeholder"];
var _hoisted_2$6 = ["type", "value", "required", "min", "max", "placeholder", "maxlength"];
function render$9(_ctx, _cache, $props, $setup, $data, $options) {
var _component_the_mask = resolveComponent("the-mask");
return (openBlock(), createElementBlock("span", {
"data-placeholder": $data.inputType === 'date' ? _ctx.placeholder : null
}, [
(_ctx.question.mask)
? (openBlock(), createBlock(_component_the_mask, {
key: 0,
ref: "input",
mask: _ctx.question.mask,
masked: false,
type: $data.inputType,
value: _ctx.modelValue,
required: _ctx.question.required,
onKeydown: _ctx.onKeyDown,
onKeyup: [
_ctx.onChange,
withKeys(withModifiers(_ctx.onEnter, ["prevent"]), ["enter"]),
withKeys(withModifiers(_ctx.onEnter, ["prevent"]), ["tab"])
],
onFocus: _ctx.setFocus,
onBlur: _ctx.unsetFocus,
placeholder: _ctx.placeholder,
min: _ctx.question.min,
max: _ctx.question.max,
onChange: _ctx.onChange
}, null, 8 /* PROPS */, ["mask", "type", "value", "required", "onKeydown", "onKeyup", "onFocus", "onBlur", "placeholder", "min", "max", "onChange"]))
: (openBlock(), createElementBlock("input", {
key: 1,
ref: "input",
type: $data.inputType,
value: _ctx.modelValue,
required: _ctx.question.required,
onKeydown: _cache[0] || (_cache[0] = function () {
var args = [], len = arguments.length;
while ( len-- ) args[ len ] = arguments[ len ];
return (_ctx.onKeyDown && _ctx.onKeyDown.apply(_ctx, args));
}),
onKeyup: [
_cache[1] || (_cache[1] = function () {
var args = [], len = arguments.length;
while ( len-- ) args[ len ] = arguments[ len ];
return (_ctx.onChange && _ctx.onChange.apply(_ctx, args));
}),
_cache[2] || (_cache[2] = withKeys(withModifiers(function () {
var args = [], len = arguments.length;
while ( len-- ) args[ len ] = arguments[ len ];
return (_ctx.onEnter && _ctx.onEnter.apply(_ctx, args));
}, ["prevent"]), ["enter"])),
_cache[3] || (_cache[3] = withKeys(withModifiers(function () {
var args = [], len = arguments.length;
while ( len-- ) args[ len ] = arguments[ len ];
return (_ctx.onEnter && _ctx.onEnter.apply(_ctx, args));
}, ["prevent"]), ["tab"]))
],
onFocus: _cache[4] || (_cache[4] = function () {
var args = [], len = arguments.length;
while ( len-- ) args[ len ] = arguments[ len ];
return (_ctx.setFocus && _ctx.setFocus.apply(_ctx, args));
}),
onBlur: _cache[5] || (_cache[5] = function () {
var args = [], len = arguments.length;
while ( len-- ) args[ len ] = arguments[ len ];
return (_ctx.unsetFocus && _ctx.unsetFocus.apply(_ctx, args));
}),
min: _ctx.question.min,
max: _ctx.question.max,
onChange: _cache[6] || (_cache[6] = function () {
var args = [], len = arguments.length;
while ( len-- ) args[ len ] = arguments[ len ];
return (_ctx.onChange && _ctx.onChange.apply(_ctx, args));
}),
placeholder: _ctx.placeholder,
maxlength: _ctx.question.maxLength
}, null, 40 /* PROPS, HYDRATE_EVENTS */, _hoisted_2$6))
], 8 /* PROPS */, _hoisted_1$7))
}
script$i.render = render$9;
script$i.__file = "src/components/QuestionTypes/TextType.vue";
var script$h = {
extends: script$i,
name: QuestionType.Email,
data: function data() {
return {
inputType: 'email'
}
},
methods: {
validate: function validate() {
if (this.hasValue) {
return /^[^@]+@.+[^.]$/.test(this.dataValue)
}
return !this.question.required
}
}
};
script$h.__file = "src/components/QuestionTypes/EmailType.vue";
var script$g = {
name: 'TextareaAutosize',
props: {
value: {
type: [String, Number],
default: ''
},
autosize: {
type: Boolean,
default: true
},
minHeight: {
type: [Number],
'default': null
},
maxHeight: {
type: [Number],
'default': null
},
/*
* Force !important for style properties
*/
important: {
type: [Boolean, Array],
default: false
}
},
data: function data () {
return {
// data property for v-model binding with real textarea tag
val: null,
// works when content height becomes more then value of the maxHeight property
maxHeightScroll: false,
height: 'auto'
}
},
computed: {
computedStyles: function computedStyles () {
if (!this.autosize) { return {} }
return {
resize: !this.isResizeImportant ? 'none' : 'none !important',
height: this.height,
overflow: this.maxHeightScroll ? 'auto' : (!this.isOverflowImportant ? 'hidden' : 'hidden !important')
}
},
isResizeImportant: function isResizeImportant () {
var imp = this.important;
return imp === true || (Array.isArray(imp) && imp.includes('resize'))
},
isOverflowImportant: function isOverflowImportant () {
var imp = this.important;
return imp === true || (Array.isArray(imp) && imp.includes('overflow'))
},
isHeightImportant: function isHeightImportant () {
var imp = this.important;
return imp === true || (Array.isArray(imp) && imp.includes('height'))
}
},
watch: {
value: function value (val) {
this.val = val;
},
val: function val (val$1) {
this.$nextTick(this.resize);
this.$emit('input', val$1);
},
minHeight: function minHeight () {
this.$nextTick(this.resize);
},
maxHeight: function maxHeight () {
this.$nextTick(this.resize);
},
autosize: function autosize (val) {
if (val) { this.resize(); }
}
},
methods: {
resize: function resize () {
var this$1$1 = this;
var important = this.isHeightImportant ? 'important' : '';
this.height = "auto" + (important ? ' !important' : '');
this.$nextTick(function () {
var contentHeight = this$1$1.$el.scrollHeight + 1;
if (this$1$1.minHeight) {
contentHeight = contentHeight < this$1$1.minHeight ? this$1$1.minHeight : contentHeight;
}
if (this$1$1.maxHeight) {
if (contentHeight > this$1$1.maxHeight) {
contentHeight = this$1$1.maxHeight;
this$1$1.maxHeightScroll = true;
} else {
this$1$1.maxHeightScroll = false;
}
}
var heightVal = contentHeight + 'px';
this$1$1.height = "" + heightVal + (important ? ' !important' : '');
});
return this
}
},
created: function created () {
this.val = this.value;
},
mounted: function mounted () {
this.resize();
}
};
function render$8(_ctx, _cache, $props, $setup, $data, $options) {
return withDirectives((openBlock(), createElementBlock("textarea", {
style: normalizeStyle($options.computedStyles),
"onUpdate:modelValue": _cache[0] || (_cache[0] = function ($event) { return (($data.val) = $event); }),
onFocus: _cache[1] || (_cache[1] = function () {
var args = [], len = arguments.length;
while ( len-- ) args[ len ] = arguments[ len ];
return ($options.resize && $options.resize.apply($options, args));
})
}, null, 36 /* STYLE, HYDRATE_EVENTS */)), [
[vModelText, $data.val]
])
}
script$g.render = render$8;
script$g.__file = "node_modules/vue-textarea-autosize/src/components/TextareaAutosize.vue";
var script$f = {
extends: script$l,
name: QuestionType.LongText,
components: {
TextareaAutosize: script$g
},
data: function data () {
return {
canReceiveFocus: true
}
},
mounted: function mounted() {
window.addEventListener('resize', this.onResizeListener);
},
beforeUnmount: function beforeUnmount() {
window.removeEventListener('resize', this.onResizeListener);
},
methods: {
onResizeListener: function onResizeListener() {
this.$refs.input.resize();
},
unsetFocus: function unsetFocus($event) {
if ($event || !this.isMobile) {
this.focused = false;
}
},
onEnterDown: function onEnterDown($event) {
if (!this.isMobile) {
$event.preventDefault();
}
},
onEnter: function onEnter() {
this._onEnter();
if (this.isMobile) {
this.focus();
}
}
}
};
function render$7(_ctx, _cache, $props, $setup, $data, $options) {
var _component_textarea_autosize = resolveComponent("textarea-autosize");
return (openBlock(), createElementBlock("span", null, [
createVNode(_component_textarea_autosize, {
ref: "input",
rows: "1",
value: _ctx.modelValue,
required: _ctx.question.required,
onKeydown: [
_ctx.onKeyDown,
withKeys(withModifiers($options.onEnterDown, ["exact"]), ["enter"])
],
onKeyup: [
_ctx.onChange,
withKeys(withModifiers($options.onEnter, ["exact","prevent"]), ["enter"]),
withKeys(withModifiers($options.onEnter, ["prevent"]), ["tab"])
],
onFocus: _ctx.setFocus,
onBlur: $options.unsetFocus,
placeholder: _ctx.placeholder,
maxlength: _ctx.question.maxLength
}, null, 8 /* PROPS */, ["value", "required", "onKeydown", "onKeyup", "onFocus", "onBlur", "placeholder", "maxlength"])
]))
}
script$f.render = render$7;
script$f.__file = "src/components/QuestionTypes/LongTextType.vue";
var script$e = {
extends: script$l,
name: QuestionType.MultipleChoice,
data: function data() {
return {
editingOther: false,
hasImages: false
}
},
mounted: function mounted() {
this.addKeyListener();
},
beforeUnmount: function beforeUnmount() {
this.removeKeyListener();
},
watch: {
active: function active(value) {
if (value) {
this.addKeyListener();
if (this.question.multiple && this.question.answered) {
this.enterPressed = false;
}
} else {
this.removeKeyListener();
}
}
},
methods: {
addKeyListener: function addKeyListener() {
this.removeKeyListener();
document.addEventListener('keyup', this.onKeyListener);
},
removeKeyListener: function removeKeyListener() {
document.removeEventListener('keyup', this.onKeyListener);
},
/**
* Listens for keyboard events (A, B, C, ...)
*/
onKeyListener: function onKeyListener($event) {
if (this.active && !this.editingOther && $event.key && $event.key.length === 1) {
var keyCode = $event.key.toUpperCase().charCodeAt(0);
if (keyCode >= 65 && keyCode <= 90) {
var index = keyCode - 65;
if (index > -1) {
var option = this.question.options[index];
if (option) {
this.toggleAnswer(option);
} else if (this.question.allowOther && index === this.question.options.length) {
this.startEditOther();
}
}
}
}
},
getLabel: function getLabel(index) {
return this.language.ariaMultipleChoice.replace(':letter', this.getToggleKey(index))
},
getToggleKey: function getToggleKey(index) {
var key = 65 + index;
if (key <= 90) {
return String.fromCharCode(key)
}
return ''
},
toggleAnswer: function toggleAnswer(option) {
if (!this.question.multiple) {
if (this.question.allowOther) {
this.question.other = this.dataValue = null;
this.setAnswer(this.dataValue);
}
for (var i = 0; i < this.question.options.length; i++) {
var o = this.question.options[i];
if (o.selected) {
this._toggleAnswer(o);
}
}
}
this._toggleAnswer(option);
},
_toggleAnswer: function _toggleAnswer(option) {
var optionValue = option.choiceValue();
option.toggle();
if (this.question.multiple) {
this.enterPressed = false;
if (!option.selected) {
this._removeAnswer(optionValue);
} else if (this.dataValue.indexOf(optionValue) === -1) {
this.dataValue.push(optionValue);
}
} else {
this.dataValue = option.selected ? optionValue : null;
}
if (this.isValid() && this.question.nextStepOnAnswer && !this.question.multiple && !this.disabled) {
this.$emit('next');
}
this.setAnswer(this.dataValue);
},
_removeAnswer: function _removeAnswer(value) {
var index = this.dataValue.indexOf(value);
if (index !== -1) {
this.dataValue.splice(index, 1);
}
},
startEditOther: function startEditOther() {
var this$1$1 = this;
this.editingOther = true;
this.enterPressed = false;
this.$nextTick(function () {
this$1$1.$refs.otherInput.focus();
});
},
onChangeOther: function onChangeOther() {
if (this.editingOther) {
var
value = [],
self = this;
this.question.options.forEach(function(option) {
if (option.selected) {
if (self.question.multiple) {
value.push(option.choiceValue());
} else {
option.toggle();
}
}
});
if (this.question.other && this.question.multiple) {
value.push(this.question.other);
} else if (!this.question.multiple) {
value = this.question.other;
}
this.dataValue = value;
this.setAnswer(this.dataValue);
}
},
stopEditOther: function stopEditOther() {
this.editingOther = false;
}
},
computed: {
hasValue: function hasValue() {
if (this.question.options.filter(function (o) { return o.selected; }).length) {
return true
}
if (this.question.allowOther) {
return this.question.other && this.question.other.trim().length > 0
}
return false
}
}
};
var _hoisted_1$6 = { class: "f-radios-wrap" };
var _hoisted_2$5 = ["onClick", "aria-label"];
var _hoisted_3$4 = {
key: 0,
class: "f-image"
};
var _hoisted_4$4 = ["src", "alt"];
var _hoisted_5$4 = { class: "f-label-wrap" };
var _hoisted_6$4 = { class: "f-key" };
var _hoisted_7$4 = {
key: 0,
class: "f-label"
};
var _hoisted_8$4 = ["aria-label"];
var _hoisted_9$4 = { class: "f-label-wrap" };
var _hoisted_10$4 = {
key: 0,
class: "f-key"
};
var _hoisted_11$4 = {
key: 2,
class: "f-selected"
};
var _hoisted_12$4 = { class: "f-label" };
var _hoisted_13$4 = {
key: 3,
class: "f-label"
};
function render$6(_ctx, _cache, $props, $setup, $data, $options) {
return (openBlock(), createElementBlock("div", _hoisted_1$6, [
createElementVNode("ul", {
class: normalizeClass(["f-radios", {'f-multiple': _ctx.question.multiple}]),
role: "listbox"
}, [
(openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.question.options, function (option, index) {
return (openBlock(), createElementBlock("li", {
onClick: withModifiers(function ($event) { return ($options.toggleAnswer(option)); }, ["prevent"]),
class: normalizeClass({'f-selected': option.selected}),
key: 'm' + index,
"aria-label": $options.getLabel(index),
role: "option"
}, [
($data.hasImages && option.imageSrc)
? (openBlock(), createElementBlock("span", _hoisted_3$4, [
createElementVNode("img", {
src: option.imageSrc,
alt: option.imageAlt
}, null, 8 /* PROPS */, _hoisted_4$4)
]))
: createCommentVNode("v-if", true),
createElementVNode("div", _hoisted_5$4, [
createElementVNode("span", _hoisted_6$4, toDisplayString($options.getToggleKey(index)), 1 /* TEXT */),
(option.choiceLabel())
? (openBlock(), createElementBlock("span", _hoisted_7$4, toDisplayString(option.choiceLabel()), 1 /* TEXT */))
: createCommentVNode("v-if", true)
])
], 10 /* CLASS, PROPS */, _hoisted_2$5))
}), 128 /* KEYED_FRAGMENT */)),
(!$data.hasImages && _ctx.question.allowOther)
? (openBlock(), createElementBlock("li", {
key: 0,
class: normalizeClass(["f-other", {'f-selected': _ctx.question.other, 'f-focus': $data.editingOther}]),
onClick: _cache[5] || (_cache[5] = withModifiers(function () {
var args = [], len = arguments.length;
while ( len-- ) args[ len ] = arguments[ len ];
return ($options.startEditOther && $options.startEditOther.apply($options, args));
}, ["prevent"])),
"aria-label": _ctx.language.ariaTypeAnswer,
role: "option"
}, [
createElementVNode("div", _hoisted_9$4, [
(!$data.editingOther)
? (openBlock(), createElementBlock("span", _hoisted_10$4, toDisplayString($options.getToggleKey(_ctx.question.options.length)), 1 /* TEXT */))
: createCommentVNode("v-if", true),
($data.editingOther)
? withDirectives((openBlock(), createElementBlock("input", {
key: 1,
"onUpdate:modelValue": _cache[0] || (_cache[0] = function ($event) { return ((_ctx.question.other) = $event); }),
type: "text",
ref: "otherInput",
onBlur: _cache[1] || (_cache[1] = function () {
var args = [], len = arguments.length;
while ( len-- ) args[ len ] = arguments[ len ];
return ($options.stopEditOther && $options.stopEditOther.apply($options, args));
}),
onKeyup: [
_cache[2] || (_cache[2] = withKeys(withModifiers(function () {
var args = [], len = arguments.length;
while ( len-- ) args[ len ] = arguments[ len ];
return ($options.stopEditOther && $options.stopEditOther.apply($options, args));
}, ["prevent"]), ["enter"])),
_cache[3] || (_cache[3] = function () {
var args = [], len = arguments.length;
while ( len-- ) args[ len ] = arguments[ len ];
return ($options.onChangeOther && $options.onChangeOther.apply($options, args));
})
],
onChange: _cache[4] || (_cache[4] = function () {
var args = [], len = arguments.length;
while ( len-- ) args[ len ] = arguments[ len ];
return ($options.onChangeOther && $options.onChangeOther.apply($options, args));
}),
maxlength: "256"
}, null, 544 /* HYDRATE_EVENTS, NEED_PATCH */)), [
[vModelText, _ctx.question.other]
])
: (_ctx.question.other)
? (openBlock(), createElementBlock("span", _hoisted_11$4, [
createElementVNode("span", _hoisted_12$4, toDisplayString(_ctx.question.other), 1 /* TEXT */)
]))
: (openBlock(), createElementBlock("span", _hoisted_13$4, toDisplayString(_ctx.language.otherPrompt), 1 /* TEXT */))
])
], 10 /* CLASS, PROPS */, _hoisted_8$4))
: createCommentVNode("v-if", true)
], 2 /* CLASS */)
]))
}
script$e.render = render$6;
script$e.__file = "src/components/QuestionTypes/MultipleChoiceType.vue";
var script$d = {
extends: script$e,
name: QuestionType.MultiplePictureChoice,
data: function data() {
return {
hasImages: true
}
}
};
script$d.__file = "src/components/QuestionTypes/MultiplePictureChoiceType.vue";
var script$c = {
extends: script$i,
name: QuestionType.Number,
data: function data() {
return {
inputType: 'tel',
allowedChars: '-0123456789.'
}
},
methods: {
validate: function validate() {
if (this.question.min !== null && !isNaN(this.question.min) && +this.dataValue < +this.question.min) {
return false
}
if (this.question.max !== null && !isNaN(this.question.max) && +this.dataValue > +this.question.max) {
return false
}
if (this.hasValue) {
if (this.question.mask) {
return this.validateMask()
}
return !isNaN(+this.dataValue)
}
return !this.question.required || this.hasValue
}
}
};
script$c.__file = "src/components/QuestionTypes/NumberType.vue";
var script$b = {
extends: script$i,
name: QuestionType.Password,
data: function data() {
return {
inputType: 'password'
}
}
};
script$b.__file = "src/components/QuestionTypes/PasswordType.vue";
var script$a = {
extends: script$i,
name: QuestionType.Phone,
data: function data() {
return {
inputType: 'tel',
canReceiveFocus: true
}
}
};
script$a.__file = "src/components/QuestionTypes/PhoneType.vue";
var script$9 = {
extends: script$l,
name: QuestionType.SectionBreak,
methods: {
onEnter: function onEnter() {
this.dirty = true;
this._onEnter();
},
isValid: function isValid() {
return true
}
}
};
var _hoisted_1$5 = {
key: 0,
class: "f-content"
};
var _hoisted_2$4 = { class: "f-section-text" };
function render$5(_ctx, _cache, $props, $setup, $data, $options) {
return (_ctx.question.content)
? (openBlock(), createElementBlock("div", _hoisted_1$5, [
createElementVNode("span", _hoisted_2$4, toDisplayString(_ctx.question.content), 1 /* TEXT */)
]))
: createCommentVNode("v-if", true)
}
script$9.render = render$5;
script$9.__file = "src/components/QuestionTypes/SectionBreakType.vue";
var script$8 = {
extends: script$i,
name: QuestionType.Url,
data: function data() {
return {
inputType: 'url'
}
},
methods: {
fixAnswer: function fixAnswer(answer) {
if (answer && answer.indexOf('://') === -1) {
// Insert https protocol to make it easier to enter
// correct URLs
answer = 'https://' + answer;
}
return answer
},
validate: function validate() {
if (this.hasValue) {
try {
new URL(this.fixAnswer(this.dataValue));
return true
} catch(_) {
// Invalid URL
return false
}
}
return !this.question.required
}
}
};
script$8.__file = "src/components/QuestionTypes/UrlType.vue";
var script$7 = {
extends: script$i,
name: QuestionType.Date,
data: function data() {
return {
inputType: 'date'
}
},
methods: {
validate: function validate() {
if (this.question.min && this.dataValue < this.question.min) {
return false
}
if (this.question.max && this.dataValue > this.question.max) {
return false
}
return !this.question.required || this.hasValue
}
}
};
script$7.__file = "src/components/QuestionTypes/DateType.vue";
var script$6 = {
extends: script$i,
name: QuestionType.File,
methods: {
setAnswer: function setAnswer(answer) {
this.question.setAnswer(this.$refs.input.files);
this.answer = answer;
this.question.answered = this.isValid();
this.$emit('update:modelValue', answer);
},
showInvalid: function showInvalid() {
return this.errorMessage !== null
},
checkFileAccept: function checkFileAccept(file) {
var extension = '.' + file.name.split('.').pop();
if (this.acceptedFileExtensionsRegex && this.acceptedFileExtensionsRegex.test(extension)) {
return true
}
if (this.acceptedFileMimesRegex && this.acceptedFileMimesRegex.test(file.type)) {
return true
}
return false
},
validate: function validate() {
var this$1$1 = this;
if (this.question.required && !this.hasValue) {
return false
}
var
files = this.$refs.input.files,
numFiles = files.length;
if (this.question.accept) {
if (!Array.from(files).every(function (file) { return this$1$1.checkFileAccept(file); })) {
this.errorMessage = this.language.formatString(this.language.errorAllowedFileTypes, {
fileTypes: this.question.accept
});
return false
}
}
if (this.question.multiple) {
if (this.question.min !== null && numFiles < +this.question.min) {
this.errorMessage = this.language.formatString(this.language.errorMinFiles, {
min: this.question.min
});
return false
}
if (this.question.max !== null && numFiles > +this.question.max) {
this.errorMessage = this.language.formatString(this.language.errorMaxFiles, {
max: this.question.max
});
return false
}
}
if (this.question.maxSize !== null) {
var fileSize =
Array.from(files).reduce(function (current, file) { return current + file.size; }, 0);
if (fileSize > +this.question.maxSize) {
this.errorMessage = this.language.formatString(this.language.errorMaxFileSize, {
size: this.language.formatFileSize(this.question.maxSize)
});
return false
}
}
this.errorMessage = null;
return this.$refs.input.checkValidity()
}
},
computed: {
acceptArray: function acceptArray() {
if (this.question.accept) {
return this.question.accept.split(',')
}
return []
},
acceptedFileMimes: function acceptedFileMimes() {
return this.acceptArray.filter(function (accept) { return accept[0] !== '.'; })
},
acceptedFileMimesRegex: function acceptedFileMimesRegex() {
if (this.acceptedFileMimes.length) {
return new RegExp(this.acceptedFileMimes.join('|').replace(/\*/g, '.\*'))
}
return null
},
acceptedFileExtensions: function acceptedFileExtensions() {
return this.acceptArray.filter(function (accept) { return accept[0] === '.'; })
},
acceptedFileExtensionsRegex: function acceptedFileExtensionsRegex() {
if (this.acceptedFileExtensions.length) {
return new RegExp(this.acceptedFileExtensions.join('|'))
}
return null
}
}
};
var _hoisted_1$4 = ["accept", "multiple", "value", "required"];
function render$4(_ctx, _cache, $props, $setup, $data, $options) {
return (openBlock(), createElementBlock("input", {
ref: "input",
type: "file",
accept: _ctx.question.accept,
multiple: _ctx.question.multiple,
value: _ctx.modelValue,
required: _ctx.question.required,
onKeyup: [
_cache[0] || (_cache[0] = withKeys(withModifiers(function () {
var args = [], len = arguments.length;
while ( len-- ) args[ len ] = arguments[ len ];
return (_ctx.onEnter && _ctx.onEnter.apply(_ctx, args));
}, ["prevent"]), ["enter"])),
_cache[1] || (_cache[1] = withKeys(withModifiers(function () {
var args = [], len = arguments.length;
while ( len-- ) args[ len ] = arguments[ len ];
return (_ctx.onEnter && _ctx.onEnter.apply(_ctx, args));
}, ["prevent"]), ["tab"]))
],
onFocus: _cache[2] || (_cache[2] = function () {
var args = [], len = arguments.length;
while ( len-- ) args[ len ] = arguments[ len ];
return (_ctx.setFocus && _ctx.setFocus.apply(_ctx, args));
}),
onBlur: _cache[3] || (_cache[3] = function () {
var args = [], len = arguments.length;
while ( len-- ) args[ len ] = arguments[ len ];
return (_ctx.unsetFocus && _ctx.unsetFocus.apply(_ctx, args));
}),
onChange: _cache[4] || (_cache[4] = function () {
var args = [], len = arguments.length;
while ( len-- ) args[ len ] = arguments[ len ];
return (_ctx.onChange && _ctx.onChange.apply(_ctx, args));
})
}, null, 40 /* PROPS, HYDRATE_EVENTS */, _hoisted_1$4))
}
script$6.render = render$4;
script$6.__file = "src/components/QuestionTypes/FileType.vue";
var script$5 = {
extends: script$l,
name: QuestionType.Matrix,
data: function data() {
return {
selected: {},
inputList: []
}
},
beforeMount: function beforeMount() {
// Pre-fill the form if there is a predefined answer
if (this.question.multiple) {
for (var i = 0, list = this.question.rows; i < list.length; i += 1) {
var row = list[i];
this.selected[row.id] = this.question.answer && this.question.answer[row.id] ? [].concat( this.question.answer[row.id] ) : [];
}
} else if (this.question.answer) {
this.selected = Object.assign({}, this.question.answer);
}
},
beforeUpdate: function beforeUpdate() {
this.inputList = [];
},
methods: {
onChange: function onChange($event) {
this.dirty = true;
this.dataValue = this.selected;
this.onKeyDown();
this.setAnswer(this.dataValue);
},
validate: function validate() {
if (!this.question.required) {
return true
}
var checked = function (inputs) { return inputs.some(function (input) { return input.checked; }); };
if (!Object.values(this.inputGroups).every(function (value) { return checked(value); })) {
return false
}
return true
},
getElement: function getElement() {
return this.inputList[0]
},
},
computed: {
inputGroups: function inputGroups() {
var inputGroups = {};
// Setting input list for validation
for (var i = 0, list = this.question.rows; i < list.length; i += 1) {
var row = list[i];
inputGroups[row.id] = [];
}
this.inputList.forEach(function (input) {
inputGroups[input.dataset.id].push(input);
});
return inputGroups
}
}
};
var _hoisted_1$3 = { class: "f-matrix-wrap" };
var _hoisted_2$3 = /*#__PURE__*/createElementVNode("td", null, null, -1 /* HOISTED */);
var _hoisted_3$3 = { class: "f-table-string" };
var _hoisted_4$3 = { class: "f-table-cell f-row-cell" };
var _hoisted_5$3 = { class: "f-table-string" };
var _hoisted_6$3 = ["title"];
var _hoisted_7$3 = {
key: 0,
class: "f-field-wrap"
};
var _hoisted_8$3 = { class: "f-matrix-field f-matrix-radio" };
var _hoisted_9$3 = ["name", "id", "aria-label", "data-id", "value", "onUpdate:modelValue"];
var _hoisted_10$3 = /*#__PURE__*/createElementVNode("span", { class: "f-field-mask f-radio-mask" }, [
/*#__PURE__*/createElementVNode("svg", {
viewBox: "0 0 24 24",
class: "f-field-svg f-radio-svg"
}, [
/*#__PURE__*/createElementVNode("circle", {
r: "6",
cx: "12",
cy: "12"
})
])
], -1 /* HOISTED */);
var _hoisted_11$3 = {
key: 1,
class: "f-field-wrap"
};
var _hoisted_12$3 = { class: "f-matrix-field f-matrix-checkbox" };
var _hoisted_13$3 = ["id", "aria-label", "data-id", "value", "onUpdate:modelValue"];
var _hoisted_14$2 = /*#__PURE__*/createElementVNode("span", { class: "f-field-mask f-checkbox-mask" }, [
/*#__PURE__*/createElementVNode("svg", {
viewBox: "0 0 24 24",
class: "f-field-svg f-checkbox-svg"
}, [
/*#__PURE__*/createElementVNode("rect", {
width: "12",
height: "12",
x: "6",
y: "6"
})
])
], -1 /* HOISTED */);
function render$3(_ctx, _cache, $props, $setup, $data, $options) {
return (openBlock(), createElementBlock("div", _hoisted_1$3, [
createElementVNode("table", {
class: normalizeClass(["f-matrix-table", { 'f-matrix-multiple': _ctx.question.multiple }])
}, [
createElementVNode("thead", null, [
createElementVNode("tr", null, [
_hoisted_2$3,
(openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.question.columns, function (column, index) {
return (openBlock(), createElementBlock("th", {
key: 'c' + index,
class: "f-table-cell f-column-cell"
}, [
createElementVNode("span", _hoisted_3$3, toDisplayString(column.label), 1 /* TEXT */)
]))
}), 128 /* KEYED_FRAGMENT */))
])
]),
createElementVNode("tbody", null, [
(openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.question.rows, function (row, index) {
return (openBlock(), createElementBlock("tr", {
key: 'r' + index,
class: "f-table-row"
}, [
createElementVNode("td", _hoisted_4$3, [
createElementVNode("span", _hoisted_5$3, toDisplayString(row.label), 1 /* TEXT */)
]),
(openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.question.columns, function (column, index) {
return (openBlock(), createElementBlock("td", {
key: 'l' + index,
title: column.label,
class: "f-table-cell f-matrix-cell"
}, [
(!_ctx.question.multiple)
? (openBlock(), createElementBlock("div", _hoisted_7$3, [
createElementVNode("label", _hoisted_8$3, [
withDirectives(createElementVNode("input", {
type: "radio",
ref: function (el) { return $data.inputList.push(el); },
name: row.id,
id: 'c' + index + '-' + row.id,
"aria-label": row.label,
"data-id": row.id,
value: column.value,
"onUpdate:modelValue": function ($event) { return (($data.selected[row.id]) = $event); },
class: "f-field-control f-radio-control",
onChange: _cache[0] || (_cache[0] = function () {
var args = [], len = arguments.length;
while ( len-- ) args[ len ] = arguments[ len ];
return ($options.onChange && $options.onChange.apply($options, args));
})
}, null, 40 /* PROPS, HYDRATE_EVENTS */, _hoisted_9$3), [
[vModelRadio, $data.selected[row.id]]
]),
_hoisted_10$3
])
]))
: (openBlock(), createElementBlock("div", _hoisted_11$3, [
createElementVNode("label", _hoisted_12$3, [
withDirectives(createElementVNode("input", {
type: "checkbox",
ref: function (el) { return $data.inputList.push(el); },
id: 'c' + index + '-' + row.id,
"aria-label": row.label,
"data-id": row.id,
value: column.value,
class: "f-field-control f-checkbox-control",
"onUpdate:modelValue": function ($event) { return (($data.selected[row.id]) = $event); },
onChange: _cache[1] || (_cache[1] = function () {
var args = [], len = arguments.length;
while ( len-- ) args[ len ] = arguments[ len ];
return ($options.onChange && $options.onChange.apply($options, args));
})
}, null, 40 /* PROPS, HYDRATE_EVENTS */, _hoisted_13$3), [
[vModelCheckbox, $data.selected[row.id]]
]),
_hoisted_14$2
])
]))
], 8 /* PROPS */, _hoisted_6$3))
}), 128 /* KEYED_FRAGMENT */))
]))
}), 128 /* KEYED_FRAGMENT */))
])
], 2 /* CLASS */)
]))
}
script$5.render = render$3;
script$5.__file = "src/components/QuestionTypes/MatrixType.vue";
var script$4 = {
extends: script$l,
name: QuestionType.OpinionScale,
data: function data() {
return {
isIconScale: false,
hoveredIndex: null,
activeIndex: null
}
},
mixins: [
IsMobile
],
beforeMount: function beforeMount() {
var
size = this.question.max || 5,
numOptions = Math.min(Math.max(size, 1), 10);
if (!this.question.options.length) {
for (var i = 1; i <= numOptions; i++) {
this.question.options.push(new ChoiceOption({value: i.toString()}));
}
}
},
mounted: function mounted() {
this.addKeyListener();
},
beforeUnmount: function beforeUnmount() {
this.removeKeyListener();
},
watch: {
active: function active(value) {
if (value) {
this.addKeyListener();
} else {
this.removeKeyListener();
}
}
},
methods: {
addKeyListener: function addKeyListener() {
this.removeKeyListener();
document.addEventListener('keyup', this.onKeyListener);
},
removeKeyListener: function removeKeyListener() {
document.removeEventListener('keyup', this.onKeyListener);
},
onMouseover: function onMouseover(index) {
this.hoveredIndex = index;
},
onMouseleave: function onMouseleave() {
this.hoveredIndex = null;
},
/**
* Listens for keyboard events (1, 2, 3, ...)
*/
onKeyListener: function onKeyListener($event) {
if (this.active && $event.key && $event.key.length === 1) {
var keyCode = $event.key.toUpperCase().charCodeAt(0);
if (keyCode >= 48 && keyCode <= 57) {
var index = keyCode - 49;
if (index > -1) {
var option = this.question.options[index];
if (option) {
this.toggleAnswer(option);
}
}
}
}
},
getLabel: function getLabel(index) {
return this.language.ariaMultipleChoice.replace(':letter', this.getToggleKey(index))
},
getToggleKey: function getToggleKey(num) {
return num
},
toggleAnswer: function toggleAnswer(option) {
for (var i = 0; i < this.question.options.length; i++) {
var o = this.question.options[i];
if (o.selected) {
this._toggleAnswer(o);
}
}
this._toggleAnswer(option);
},
_toggleAnswer: function _toggleAnswer(option) {
var optionValue = option.choiceValue();
option.toggle();
this.activeIndex = option.selected ? this.question.options.indexOf(option) : null;
this.dataValue = option.selected ? optionValue : null;
if (this.isValid() && this.question.nextStepOnAnswer && !this.disabled) {
this.$emit('next');
}
this.setAnswer(this.dataValue);
},
_removeAnswer: function _removeAnswer(value) {
var index = this.dataValue.indexOf(value);
if (index !== -1) {
this.dataValue.splice(index, 1);
}
},
iconScaleClasses: function iconScaleClasses(index) {
var classes = {};
if (this.isIconScale) {
if (!this.isMobile) {
classes['f-hovered'] = this.hoveredIndex && index < this.hoveredIndex;
}
classes['f-previous'] = this.activeIndex && index < this.activeIndex;
}
return classes
}
},
computed: {
hasValue: function hasValue() {
if (this.question.options.filter(function (o) { return o.selected; }).length) {
return true
}
return false
}
}
};
var _hoisted_1$2 = { class: "f-radios-wrap" };
var _hoisted_2$2 = {
class: "f-radios",
role: "listbox"
};
var _hoisted_3$2 = ["onClick", "onMouseover", "aria-label"];
var _hoisted_4$2 = {
key: 0,
class: "f-label-wrap"
};
var _hoisted_5$2 = {
key: 0,
class: "f-label"
};
var _hoisted_6$2 = {
key: 1,
class: "f-icon-wrap"
};
var _hoisted_7$2 = /*#__PURE__*/createElementVNode("div", { class: "f-icon" }, [
/*#__PURE__*/createElementVNode("svg", {
xmlns: "http://www.w3.org/2000/svg",
width: "24",
height: "24",
viewBox: "0 0 24 24"
}, [
/*#__PURE__*/createElementVNode("path", {
d: "M12 .587l3.668 7.568 8.332 1.151-6.064 5.828 1.48 8.279-7.416-3.967-7.417 3.967 1.481-8.279-6.064-5.828 8.332-1.151z",
"stroke-width": ".5"
})
])
], -1 /* HOISTED */);
var _hoisted_8$2 = { class: "f-key" };
var _hoisted_9$2 = {
key: 0,
class: "f-label-scale-wrap"
};
var _hoisted_10$2 = {
key: 0,
class: "f-label-scale"
};
var _hoisted_11$2 = /*#__PURE__*/createElementVNode("span", { class: "f-label-scale-num" }, "1 - ", -1 /* HOISTED */);
var _hoisted_12$2 = {
key: 1,
class: "f-label-scale"
};
var _hoisted_13$2 = { class: "f-label-scale-num" };
function render$2(_ctx, _cache, $props, $setup, $data, $options) {
return (openBlock(), createElementBlock("div", _hoisted_1$2, [
createElementVNode("ul", _hoisted_2$2, [
(openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.question.options, function (option, index) {
return (openBlock(), createElementBlock("li", {
onClick: withModifiers(function ($event) { return ($options.toggleAnswer(option)); }, ["prevent"]),
onMouseover: function ($event) { return ($data.isIconScale && $options.onMouseover(index)); },
onMouseleave: _cache[0] || (_cache[0] = function ($event) { return ($data.isIconScale && $options.onMouseleave()); }),
class: normalizeClass(Object.assign({}, {'f-selected': option.selected}, $options.iconScaleClasses(index))),
key: 'm' + index,
"aria-label": $options.getLabel(option.value),
role: "option"
}, [
(!$data.isIconScale)
? (openBlock(), createElementBlock("div", _hoisted_4$2, [
(option.choiceLabel())
? (openBlock(), createElementBlock("span", _hoisted_5$2, toDisplayString(option.choiceLabel()), 1 /* TEXT */))
: createCommentVNode("v-if", true)
]))
: (openBlock(), createElementBlock("div", _hoisted_6$2, [
_hoisted_7$2,
createElementVNode("div", _hoisted_8$2, toDisplayString($options.getToggleKey(option.value)), 1 /* TEXT */)
]))
], 42 /* CLASS, PROPS, HYDRATE_EVENTS */, _hoisted_3$2))
}), 128 /* KEYED_FRAGMENT */))
]),
(!$data.isIconScale && (_ctx.question.labelLeft || _ctx.question.labelRight))
? (openBlock(), createElementBlock("div", _hoisted_9$2, [
(_ctx.question.labelLeft)
? (openBlock(), createElementBlock("span", _hoisted_10$2, [
_hoisted_11$2,
createTextVNode(" " + toDisplayString(_ctx.question.labelLeft), 1 /* TEXT */)
]))
: createCommentVNode("v-if", true),
(_ctx.question.labelRight)
? (openBlock(), createElementBlock("span", _hoisted_12$2, [
createElementVNode("span", _hoisted_13$2, toDisplayString(_ctx.question.options.length) + " - ", 1 /* TEXT */),
createTextVNode(" " + toDisplayString(_ctx.question.labelRight), 1 /* TEXT */)
]))
: createCommentVNode("v-if", true)
]))
: createCommentVNode("v-if", true)
]))
}
script$4.render = render$2;
script$4.__file = "src/components/QuestionTypes/OpinionScaleType.vue";
var script$3 = {
extends: script$4,
name: QuestionType.IconRate,
data: function data() {
return {
isIconScale: true
}
},
};
script$3.__file = "src/components/QuestionTypes/IconRateType.vue";
var script$2 = {
name: 'FlowFormQuestion',
components: {
FlowFormDateType: script$7,
FlowFormDropdownType: script$k,
FlowFormEmailType: script$h,
FlowFormLongTextType: script$f,
FlowFormMultipleChoiceType: script$e,
FlowFormMultiplePictureChoiceType: script$d,
FlowFormNumberType: script$c,
FlowFormPasswordType: script$b,
FlowFormPhoneType: script$a,
FlowFormSectionBreakType: script$9,
FlowFormTextType: script$i,
FlowFormFileType: script$6,
FlowFormUrlType: script$8,
FlowFormMatrixType: script$5,
FlowFormOpinionScaleType: script$4,
FlowFormIconRateType: script$3,
},
props: {
question: QuestionModel,
language: LanguageModel,
value: [String, Array, Boolean, Number, Object],
active: {
type: Boolean,
default: false
},
reverse: {
type: Boolean,
default: false
},
disabled: {
type: Boolean,
default: false
},
autofocus: {
type: Boolean,
default: true
}
},
mixins: [
IsMobile
],
data: function data() {
return {
QuestionType: QuestionType,
dataValue: null,
debounced: false
}
},
mounted: function mounted() {
this.autofocus && this.focusField();
this.dataValue = this.question.answer;
this.$refs.qanimate.addEventListener('animationend', this.onAnimationEnd);
},
beforeUnmount: function beforeUnmount() {
this.$refs.qanimate.removeEventListener('animationend', this.onAnimationEnd);
},
methods: {
/**
* Autofocus the input box of the current question
*/
focusField: function focusField() {
var el = this.$refs.questionComponent;
el && el.focus();
},
onAnimationEnd: function onAnimationEnd() {
this.autofocus && this.focusField();
},
shouldFocus: function shouldFocus() {
var q = this.$refs.questionComponent;
return q && q.canReceiveFocus && !q.focused
},
returnFocus: function returnFocus() {
this.$refs.questionComponent;
if (this.shouldFocus()) {
this.focusField();
}
},
/**
* Emits "answer" event and calls "onEnter" method on Enter press
*/
onEnter: function onEnter($event) {
this.checkAnswer(this.emitAnswer);
},
onTab: function onTab($event) {
this.checkAnswer(this.emitAnswerTab);
},
checkAnswer: function checkAnswer(fn) {
var this$1$1 = this;
var q = this.$refs.questionComponent;
if (q.isValid() && this.question.isMultipleChoiceType() && this.question.nextStepOnAnswer && !this.question.multiple) {
this.$emit('disable', true);
this.debounce(function () {
fn(q);
this$1$1.$emit('disable', false);
}, 350);
} else {
fn(q);
}
},
emitAnswer: function emitAnswer(q) {
if (q) {
if (!q.focused) {
this.$emit('answer', q);
}
q.onEnter();
}
},
emitAnswerTab: function emitAnswerTab(q) {
if (q && this.question.type !== QuestionType.Date) {
this.returnFocus();
this.$emit('answer', q);
q.onEnter();
}
},
debounce: function debounce(fn, delay) {
var debounceTimer;
this.debounced = true;
return (function () {
clearTimeout(debounceTimer);
debounceTimer = setTimeout(fn, delay);
})()
},
/**
* Check if the "OK" button should be shown.
*/
showOkButton: function showOkButton() {
var q = this.$refs.questionComponent;
if (this.question.type === QuestionType.SectionBreak) {
return this.active
}
if (!this.question.required) {
return true
}
if (this.question.allowOther && this.question.other) {
return true
}
if (this.question.isMultipleChoiceType() && !this.question.multiple && this.question.nextStepOnAnswer) {
return false
}
// If there is no question referenced, or dataValue is still set to its default (null).
// This allows a ChoiceOption value of false, but will not allow you to use null as a value.
if (!q || this.dataValue === null) {
return false
}
return q.hasValue && q.isValid()
},
showSkip: function showSkip() {
var q = this.$refs.questionComponent;
// We might not have a reference to the question component at first
// but we know that if we don't, it's definitely empty
return !this.question.required && (!q || !q.hasValue)
},
/**
* Determins if the invalid message should be shown.
*/
showInvalid: function showInvalid() {
var q = this.$refs.questionComponent;
if (!q || this.dataValue === null) {
return false
}
return q.showInvalid()
}
},
computed: {
mainClasses: {
cache: false,
get: function get() {
var classes = {
'q-is-active': this.active,
'q-is-inactive': !this.active,
'f-fade-in-down': this.reverse,
'f-fade-in-up': !this.reverse,
'f-focused': this.$refs.questionComponent && this.$refs.questionComponent.focused,
'f-has-value': this.$refs.questionComponent && this.$refs.questionComponent.hasValue
};
classes['field-' + this.question.type.toLowerCase().substring(8, this.question.type.length - 4)] = true;
return classes
}
},
showHelperText: function showHelperText() {
if (this.question.subtitle) {
return true
}
if (this.question.type === QuestionType.LongText || this.question.type === QuestionType.MultipleChoice) {
return this.question.helpTextShow
}
return false
},
errorMessage: function errorMessage() {
var q = this.$refs.questionComponent;
if (q && q.errorMessage) {
return q.errorMessage
}
return this.language.invalidPrompt
}
}
};
var _hoisted_1$1 = { class: "q-inner" };
var _hoisted_2$1 = {
key: 0,
class: "f-tagline"
};
var _hoisted_3$1 = {
key: 0,
class: "fh2"
};
var _hoisted_4$1 = {
key: 1,
class: "f-text"
};
var _hoisted_5$1 = ["aria-label"];
var _hoisted_6$1 = /*#__PURE__*/createElementVNode("span", { "aria-hidden": "true" }, "*", -1 /* HOISTED */);
var _hoisted_7$1 = [
_hoisted_6$1
];
var _hoisted_8$1 = {
key: 1,
class: "f-answer"
};
var _hoisted_9$1 = {
key: 2,
class: "f-sub"
};
var _hoisted_10$1 = { key: 0 };
var _hoisted_11$1 = ["innerHTML"];
var _hoisted_12$1 = {
key: 2,
class: "f-help"
};
var _hoisted_13$1 = {
key: 3,
class: "f-help"
};
var _hoisted_14$1 = {
key: 3,
class: "f-answer f-full-width"
};
var _hoisted_15$1 = {
key: 0,
class: "f-description"
};
var _hoisted_16$1 = { key: 0 };
var _hoisted_17$1 = ["href", "target"];
var _hoisted_18$1 = {
key: 0,
class: "vff-animate f-fade-in f-enter"
};
var _hoisted_19$1 = ["aria-label"];
var _hoisted_20 = { key: 0 };
var _hoisted_21 = { key: 1 };
var _hoisted_22 = { key: 2 };
var _hoisted_23 = ["innerHTML"];
var _hoisted_24 = {
key: 1,
class: "f-invalid",
role: "alert",
"aria-live": "assertive"
};
function render$1(_ctx, _cache, $props, $setup, $data, $options) {
return (openBlock(), createElementBlock("div", {
class: normalizeClass(["vff-animate q-form", $options.mainClasses]),
ref: "qanimate"
}, [
createElementVNode("div", _hoisted_1$1, [
createElementVNode("div", {
class: normalizeClass({'f-section-wrap': $props.question.type === $data.QuestionType.SectionBreak})
}, [
createElementVNode("div", {
class: normalizeClass({'fh2': $props.question.type !== $data.QuestionType.SectionBreak})
}, [
($props.question.tagline)
? (openBlock(), createElementBlock("span", _hoisted_2$1, toDisplayString($props.question.tagline), 1 /* TEXT */))
: createCommentVNode("v-if", true),
($props.question.title)
? (openBlock(), createElementBlock(Fragment, { key: 1 }, [
($props.question.type === $data.QuestionType.SectionBreak)
? (openBlock(), createElementBlock("span", _hoisted_3$1, toDisplayString($props.question.title), 1 /* TEXT */))
: (openBlock(), createElementBlock("span", _hoisted_4$1, [
createTextVNode(toDisplayString($props.question.title) + " ", 1 /* TEXT */),
createCommentVNode(" Required questions are marked by an asterisk (*) "),
($props.question.required)
? (openBlock(), createElementBlock("span", {
key: 0,
class: "f-required",
"aria-label": $props.language.ariaRequired,
role: "note"
}, _hoisted_7$1, 8 /* PROPS */, _hoisted_5$1))
: createCommentVNode("v-if", true),
($props.question.inline)
? (openBlock(), createElementBlock("span", _hoisted_8$1, [
(openBlock(), createBlock(resolveDynamicComponent($props.question.type), {
ref: "questionComponent",
question: $props.question,
language: $props.language,
modelValue: $data.dataValue,
"onUpdate:modelValue": _cache[0] || (_cache[0] = function ($event) { return (($data.dataValue) = $event); }),
active: $props.active,
disabled: $props.disabled,
onNext: $options.onEnter
}, null, 8 /* PROPS */, ["question", "language", "modelValue", "active", "disabled", "onNext"]))
]))
: createCommentVNode("v-if", true)
]))
], 2112 /* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */))
: createCommentVNode("v-if", true),
($options.showHelperText)
? (openBlock(), createElementBlock("span", _hoisted_9$1, [
($props.question.subtitle)
? (openBlock(), createElementBlock("span", _hoisted_10$1, toDisplayString($props.question.subtitle), 1 /* TEXT */))
: createCommentVNode("v-if", true),
($props.question.type === $data.QuestionType.LongText && !_ctx.isMobile)
? (openBlock(), createElementBlock("span", {
key: 1,
class: "f-help",
innerHTML: $props.question.helpText || $props.language.formatString($props.language.longTextHelpText)
}, null, 8 /* PROPS */, _hoisted_11$1))
: createCommentVNode("v-if", true),
($props.question.type === $data.QuestionType.MultipleChoice && $props.question.multiple)
? (openBlock(), createElementBlock("span", _hoisted_12$1, toDisplayString($props.question.helpText || $props.language.multipleChoiceHelpText), 1 /* TEXT */))
: ($props.question.type === $data.QuestionType.MultipleChoice)
? (openBlock(), createElementBlock("span", _hoisted_13$1, toDisplayString($props.question.helpText || $props.language.multipleChoiceHelpTextSingle), 1 /* TEXT */))
: createCommentVNode("v-if", true)
]))
: createCommentVNode("v-if", true),
(!$props.question.inline)
? (openBlock(), createElementBlock("div", _hoisted_14$1, [
(openBlock(), createBlock(resolveDynamicComponent($props.question.type), {
ref: "questionComponent",
question: $props.question,
language: $props.language,
modelValue: $data.dataValue,
"onUpdate:modelValue": _cache[1] || (_cache[1] = function ($event) { return (($data.dataValue) = $event); }),
active: $props.active,
disabled: $props.disabled,
onNext: $options.onEnter
}, null, 8 /* PROPS */, ["question", "language", "modelValue", "active", "disabled", "onNext"]))
]))
: createCommentVNode("v-if", true)
], 2 /* CLASS */),
($props.question.description || $props.question.descriptionLink.length !== 0)
? (openBlock(), createElementBlock("p", _hoisted_15$1, [
($props.question.description)
? (openBlock(), createElementBlock("span", _hoisted_16$1, toDisplayString($props.question.description), 1 /* TEXT */))
: createCommentVNode("v-if", true),
(openBlock(true), createElementBlock(Fragment, null, renderList($props.question.descriptionLink, function (link, index) {
return (openBlock(), createElementBlock("a", {
class: "f-link",
key: 'm' + index,
href: link.url,
target: link.target
}, toDisplayString(link.text || link.url), 9 /* TEXT, PROPS */, _hoisted_17$1))
}), 128 /* KEYED_FRAGMENT */))
]))
: createCommentVNode("v-if", true)
], 2 /* CLASS */),
($options.showOkButton())
? (openBlock(), createElementBlock("div", _hoisted_18$1, [
createElementVNode("button", {
class: "o-btn-action",
type: "button",
ref: "button",
href: "#",
onClick: _cache[2] || (_cache[2] = withModifiers(function () {
var args = [], len = arguments.length;
while ( len-- ) args[ len ] = arguments[ len ];
return ($options.onEnter && $options.onEnter.apply($options, args));
}, ["prevent"])),
"aria-label": $props.language.ariaOk
}, [
($props.question.type === $data.QuestionType.SectionBreak)
? (openBlock(), createElementBlock("span", _hoisted_20, toDisplayString($props.language.continue), 1 /* TEXT */))
: ($options.showSkip())
? (openBlock(), createElementBlock("span", _hoisted_21, toDisplayString($props.language.skip), 1 /* TEXT */))
: (openBlock(), createElementBlock("span", _hoisted_22, toDisplayString($props.language.ok), 1 /* TEXT */))
], 8 /* PROPS */, _hoisted_19$1),
($props.question.type !== $data.QuestionType.LongText || !_ctx.isMobile)
? (openBlock(), createElementBlock("a", {
key: 0,
class: "f-enter-desc",
href: "#",
onClick: _cache[3] || (_cache[3] = withModifiers(function () {
var args = [], len = arguments.length;
while ( len-- ) args[ len ] = arguments[ len ];
return ($options.onEnter && $options.onEnter.apply($options, args));
}, ["prevent"])),
innerHTML: $props.language.formatString($props.language.pressEnter)
}, null, 8 /* PROPS */, _hoisted_23))
: createCommentVNode("v-if", true)
]))
: createCommentVNode("v-if", true),
($options.showInvalid())
? (openBlock(), createElementBlock("div", _hoisted_24, toDisplayString($options.errorMessage), 1 /* TEXT */))
: createCommentVNode("v-if", true)
])
], 2 /* CLASS */))
}
script$2.render = render$1;
script$2.__file = "src/components/FlowFormQuestion.vue";
/*
Copyright (c) 2020 - present, DITDOT Ltd. - MIT Licence
https://github.com/ditdot-dev/vue-flow-form
https://www.ditdot.hr/en
*/
var instances = {};
var ComponentInstance = {
methods: {
getInstance: function getInstance(id) {
return instances[id]
},
setInstance: function setInstance() {
instances[this.id] = this;
}
}
};
var script$1 = {
name: 'FlowForm',
components: {
FlowFormQuestion: script$2
},
props: {
questions: {
type: Array,
validator: function (value) { return value.every(function (q) { return q instanceof QuestionModel; }); }
},
language: {
type: LanguageModel,
default: function () { return new LanguageModel(); }
},
progressbar: {
type: Boolean,
default: true
},
standalone: {
type: Boolean,
default: true
},
navigation: {
type: Boolean,
default: true
},
timer: {
type: Boolean,
default: false
},
timerStartStep: [String, Number],
timerStopStep: [String, Number],
autofocus: {
type: Boolean,
default: true
}
},
mixins: [
IsMobile,
ComponentInstance
],
data: function data() {
return {
questionRefs: [],
completed: false,
submitted: false,
activeQuestionIndex: 0,
questionList: [],
questionListActivePath: [],
reverse: false,
timerOn: false,
timerInterval: null,
time: 0,
disabled: false
}
},
mounted: function mounted() {
document.addEventListener('keydown', this.onKeyDownListener);
document.addEventListener('keyup', this.onKeyUpListener, true);
window.addEventListener('beforeunload', this.onBeforeUnload);
this.setQuestions();
this.checkTimer();
},
beforeUnmount: function beforeUnmount() {
document.removeEventListener('keydown', this.onKeyDownListener);
document.removeEventListener('keyup', this.onKeyUpListener, true);
window.removeEventListener('beforeunload', this.onBeforeUnload);
this.stopTimer();
},
beforeUpdate: function beforeUpdate() {
this.questionRefs = [];
},
computed: {
numActiveQuestions: function numActiveQuestions() {
return this.questionListActivePath.length
},
activeQuestion: function activeQuestion() {
return this.questionListActivePath[this.activeQuestionIndex]
},
activeQuestionId: function activeQuestionId() {
var question = this.questionModels[this.activeQuestionIndex];
if (this.isOnLastStep) {
return '_submit'
}
if (question && question.id) {
return question.id
}
return null
},
numCompletedQuestions: function numCompletedQuestions() {
var num = 0;
this.questionListActivePath.forEach(function (question) {
if (question.answered) {
++num;
}
});
return num
},
percentCompleted: function percentCompleted() {
if (!this.numActiveQuestions) {
return 0
}
return Math.floor((this.numCompletedQuestions / this.numActiveQuestions) * 100)
},
isOnLastStep: function isOnLastStep() {
return this.numActiveQuestions > 0 && this.activeQuestionIndex === this.questionListActivePath.length
},
isOnTimerStartStep: function isOnTimerStartStep() {
if (this.activeQuestionId === this.timerStartStep) {
return true
}
if (!this.timerOn && !this.timerStartStep && this.activeQuestionIndex === 0) {
return true
}
return false
},
isOnTimerStopStep: function isOnTimerStopStep() {
if (this.submitted) {
return true
}
if (this.activeQuestionId === this.timerStopStep) {
return true
}
return false
},
questionModels: {
cache: false,
get: function get() {
var this$1$1 = this;
if (this.questions && this.questions.length) {
return this.questions
}
var questions = [];
if (!this.questions) {
var classMap = {
options: ChoiceOption,
descriptionLink: LinkOption
};
var defaultSlot = this.$slots.default && this.$slots.default();
var children = null;
if (defaultSlot && defaultSlot.length) {
children = defaultSlot[0].children;
if (!children) {
children = defaultSlot;
}
}
if (children) {
children
.filter(function (q) { return q.type && typeof q.type === 'object' && q.type.name.indexOf('Question') !== -1; })
.forEach(function (q) {
var props = q.props;
var componentInstance = this$1$1.getInstance(props.id);
var model = new QuestionModel();
if (componentInstance.question !== null) {
model = componentInstance.question;
}
if (props.modelValue) {
model.answer = props.modelValue;
}
Object.keys(model).forEach(function (key) {
if (props[key] !== undefined) {
if (typeof model[key] === 'boolean') {
model[key] = props[key] !== false;
} else if (key in classMap) {
var
classReference = classMap[key],
options = [];
props[key].forEach(function (option) {
var instance = new classReference();
Object.keys(instance).forEach(function (instanceKey) {
if (option[instanceKey] !== undefined) {
instance[instanceKey] = option[instanceKey];
}
});
options.push(instance);
});
model[key] = options;
} else {
switch(key) {
case 'type':
if (Object.values(QuestionType).indexOf(props[key]) !== -1) {
model[key] = props[key];
} else {
for (var questionTypeKey in QuestionType) {
if (questionTypeKey.toLowerCase() === props[key].toLowerCase()) {
model[key] = QuestionType[questionTypeKey];
break
}
}
}
break
default:
model[key] = props[key];
break
}
}
}
});
componentInstance.question = model;
model.resetOptions();
questions.push(model);
});
}
}
return questions
}
}
},
methods: {
setQuestionRef: function setQuestionRef(el) {
this.questionRefs.push(el);
},
/**
* Returns currently active question component (if any).
*/
activeQuestionComponent: function activeQuestionComponent() {
return this.questionRefs[this.activeQuestionIndex]
},
setQuestions: function setQuestions() {
this.setQuestionListActivePath();
this.setQuestionList();
},
/**
* This method goes through all questions and sets the ones
* that are in the current path (taking note of logic jumps)
*/
setQuestionListActivePath: function setQuestionListActivePath() {
var this$1$1 = this;
var questions = [];
if (!this.questionModels.length) {
return
}
var
index = 0,
serialIndex = 0,
nextId,
activeIndex = this.activeQuestionIndex;
var loop = function () {
var question = this$1$1.questionModels[index];
if (questions.some(function (q) { return q === question; })) {
return 'break'
}
question.setIndex(serialIndex);
question.language = this$1$1.language;
questions.push(question);
if (!question.jump) {
++index;
} else if (question.answered) {
nextId = question.getJumpId();
if (nextId) {
if (nextId === '_submit') {
index = this$1$1.questionModels.length;
} else {
var loop$1 = function ( i ) {
if (this$1$1.questionModels[i].id === nextId) {
if (i < index && questions.some(function (q) { return q === this$1$1.questionModels[i]; })) {
question.answered = false;
activeIndex = i;
++index;
} else {
index = i;
}
return 'break'
}
};
for (var i = 0; i < this$1$1.questionModels.length; i++) {
var returned$1 = loop$1( i );
if ( returned$1 === 'break' ) break;
}
}
} else {
++index;
}
} else {
index = this$1$1.questionModels.length;
}
++serialIndex;
};
do {
var returned = loop();
if ( returned === 'break' ) break;
} while (index < this$1$1.questionModels.length)
this.questionListActivePath = questions;
this.goToQuestion(activeIndex);
},
/**
* Sets the question list array
* (all questions up to, and including, the current one)
*/
setQuestionList: function setQuestionList() {
var questions = [];
for (var index = 0; index < this.questionListActivePath.length; index++) {
var question = this.questionListActivePath[index];
questions.push(question);
if (!question.answered) {
if (this.completed) {
// The "completed" status changed - user probably changed an
// already entered answer.
this.completed = false;
}
break
}
}
this.questionList = questions;
},
/**
* If we have any answered questions, notify user before leaving
* the page.
*/
onBeforeUnload: function onBeforeUnload(event) {
if (this.activeQuestionIndex > 0 && !this.submitted) {
event.preventDefault();
event.returnValue = '';
}
},
/**
* Global key listeners, listen for Enter or Tab key events.
*/
onKeyDownListener: function onKeyDownListener(e) {
if (e.key !== 'Tab' || this.submitted) {
return
}
if (e.shiftKey) {
e.stopPropagation();
e.preventDefault();
if (this.navigation) {
this.goToPreviousQuestion();
}
} else {
var q = this.activeQuestionComponent();
if (q.shouldFocus()) {
e.preventDefault();
q.focusField();
} else {
e.stopPropagation();
this.emitTab();
this.reverse = false;
}
}
},
onKeyUpListener: function onKeyUpListener(e) {
if (e.shiftKey || ['Tab', 'Enter'].indexOf(e.key) === -1 || this.submitted) {
return
}
var q = this.activeQuestionComponent();
if (e.key === 'Tab' && q.shouldFocus()) {
q.focusField();
} else {
if (e.key === 'Enter') {
this.emitEnter();
}
e.stopPropagation();
this.reverse = false;
}
},
emitEnter: function emitEnter() {
if (this.disabled) {
return
}
var q = this.activeQuestionComponent();
if (q) {
// Send enter event to the current question component
q.onEnter();
} else if (this.completed && this.isOnLastStep) {
// We're finished - submit form
this.submit();
}
},
emitTab: function emitTab() {
var q = this.activeQuestionComponent();
if (q) {
// Send tab event to the current question component
q.onTab();
} else {
this.emitEnter();
}
},
submit: function submit() {
this.emitSubmit();
this.submitted = true;
},
emitComplete: function emitComplete() {
this.$emit('complete', this.completed, this.questionList);
},
emitSubmit: function emitSubmit() {
this.$emit('submit', this.questionList);
},
/**
* Checks if we have another question and if we
* can jump to it.
*/
isNextQuestionAvailable: function isNextQuestionAvailable() {
if (this.submitted) {
return false
}
var q = this.activeQuestion;
if (q && !q.required) {
return true
}
if (this.completed && !this.isOnLastStep) {
return true
}
return this.activeQuestionIndex < this.questionList.length - 1
},
/**
* Triggered by the "answer" event in the Question component
*/
onQuestionAnswered: function onQuestionAnswered(question) {
var this$1$1 = this;
if (question.isValid()) {
this.$emit('answer', question.question);
if (this.activeQuestionIndex < this.questionListActivePath.length) {
++this.activeQuestionIndex;
}
this.$nextTick(function () {
this$1$1.reverse = false;
this$1$1.setQuestions();
this$1$1.checkTimer();
// Nested $nextTick so we're 100% sure that setQuestions
// actually updated the question array
this$1$1.$nextTick(function () {
var q = this$1$1.activeQuestionComponent();
if (q) {
this$1$1.autofocus && q.focusField();
this$1$1.activeQuestionIndex = q.question.index;
} else if (this$1$1.isOnLastStep) {
// No more questions left - set "completed" to true
this$1$1.completed = true;
this$1$1.activeQuestionIndex = this$1$1.questionListActivePath.length;
this$1$1.$refs.button && this$1$1.$refs.button.focus();
}
this$1$1.$emit('step', this$1$1.activeQuestionId, this$1$1.activeQuestion);
});
});
} else if (this.completed) {
this.completed = false;
}
},
/**
* Jumps to previous question.
*/
goToPreviousQuestion: function goToPreviousQuestion() {
this.blurFocus();
if (this.activeQuestionIndex > 0 && !this.submitted) {
if (this.isOnTimerStopStep) {
this.startTimer();
}
--this.activeQuestionIndex;
this.reverse = true;
this.checkTimer();
}
},
/**
* Jumps to next question.
*/
goToNextQuestion: function goToNextQuestion() {
this.blurFocus();
if (this.isNextQuestionAvailable()) {
this.emitEnter();
}
this.reverse = false;
},
/**
* Jumps to question with specific index.
*/
goToQuestion: function goToQuestion(index) {
if (isNaN(+index)) {
var questionIndex = this.activeQuestionIndex;
this.questionListActivePath.forEach(function (question, _index) {
if (question.id === index) {
questionIndex = _index;
}
});
index = questionIndex;
}
if (index !== this.activeQuestionIndex) {
this.blurFocus();
if (!this.submitted && index <= this.questionListActivePath.length - 1) {
// Check if we can actually jump to the wanted question.
do {
var previousQuestionsAnswered =
this
.questionListActivePath
.slice(0, index)
.every(function (q) { return q.answered; });
if (previousQuestionsAnswered) {
break
}
--index;
} while (index > 0)
this.reverse = index < this.activeQuestionIndex;
this.activeQuestionIndex = index;
this.checkTimer();
}
}
},
/**
* Removes focus from the currently focused DOM element.
*/
blurFocus: function blurFocus() {
document.activeElement && document.activeElement.blur && document.activeElement.blur();
},
checkTimer: function checkTimer() {
if (this.timer) {
if (this.isOnTimerStartStep) {
this.startTimer();
} else if (this.isOnTimerStopStep) {
this.stopTimer();
}
}
},
startTimer: function startTimer() {
if (this.timer && !this.timerOn) {
this.timerInterval = setInterval(this.incrementTime, 1000);
this.timerOn = true;
}
},
stopTimer: function stopTimer() {
if (this.timerOn) {
clearInterval(this.timerInterval);
}
this.timerOn = false;
},
incrementTime: function incrementTime() {
++this.time;
this.$emit('timer', this.time, this.formatTime(this.time));
},
formatTime: function formatTime(seconds) {
var
startIndex = 14,
length = 5;
if (seconds >= 60 * 60) {
startIndex = 11;
length = 8;
}
return new Date(1000 * seconds).toISOString().substr(startIndex, length)
},
setDisabled: function setDisabled(state) {
this.disabled = state;
},
reset: function reset() {
this.questionModels.forEach(function (question) { return question.resetAnswer(); });
this.goToQuestion(0);
}
},
watch: {
completed: function completed() {
this.emitComplete();
},
submitted: function submitted() {
this.stopTimer();
}
}
};
var _hoisted_1 = { class: "f-container" };
var _hoisted_2 = { class: "f-form-wrap" };
var _hoisted_3 = {
key: 0,
class: "vff-animate f-fade-in-up field-submittype"
};
var _hoisted_4 = { class: "f-section-wrap" };
var _hoisted_5 = { class: "fh2" };
var _hoisted_6 = ["aria-label"];
var _hoisted_7 = ["innerHTML"];
var _hoisted_8 = {
key: 2,
class: "text-success"
};
var _hoisted_9 = { class: "vff-footer" };
var _hoisted_10 = { class: "footer-inner-wrap" };
var _hoisted_11 = { class: "f-progress-bar" };
var _hoisted_12 = {
key: 1,
class: "f-nav"
};
var _hoisted_13 = ["aria-label"];
var _hoisted_14 = /*#__PURE__*/createElementVNode("svg", {
version: "1.1",
xmlns: "http://www.w3.org/2000/svg",
"xmlns:xlink": "http://www.w3.org/1999/xlink",
x: "0px",
y: "0px",
width: "42.333px",
height: "28.334px",
viewBox: "78.833 5.5 42.333 28.334",
"aria-hidden": "true"
}, [
/*#__PURE__*/createElementVNode("path", { d: "M82.039,31.971L100,11.442l17.959,20.529L120,30.187L101.02,8.492c-0.258-0.295-0.629-0.463-1.02-0.463c-0.39,0-0.764,0.168-1.02,0.463L80,30.187L82.039,31.971z" })
], -1 /* HOISTED */);
var _hoisted_15 = {
class: "f-nav-text",
"aria-hidden": "true"
};
var _hoisted_16 = ["aria-label"];
var _hoisted_17 = /*#__PURE__*/createElementVNode("svg", {
version: "1.1",
xmlns: "http://www.w3.org/2000/svg",
"xmlns:xlink": "http://www.w3.org/1999/xlink",
x: "0px",
y: "0px",
width: "42.333px",
height: "28.334px",
viewBox: "78.833 5.5 42.333 28.334",
"aria-hidden": "true"
}, [
/*#__PURE__*/createElementVNode("path", { d: "M117.963,8.031l-17.961,20.529L82.042,8.031l-2.041,1.784l18.98,21.695c0.258,0.295,0.629,0.463,1.02,0.463c0.39,0,0.764-0.168,1.02-0.463l18.98-21.695L117.963,8.031z" })
], -1 /* HOISTED */);
var _hoisted_18 = {
class: "f-nav-text",
"aria-hidden": "true"
};
var _hoisted_19 = {
key: 2,
class: "f-timer"
};
function render(_ctx, _cache, $props, $setup, $data, $options) {
var _component_flow_form_question = resolveComponent("flow-form-question");
return (openBlock(), createElementBlock("div", {
class: normalizeClass(["vff", {'vff-not-standalone': !$props.standalone, 'vff-is-mobile': _ctx.isMobile, 'vff-is-ios': _ctx.isIos}])
}, [
createElementVNode("div", _hoisted_1, [
createElementVNode("div", _hoisted_2, [
(openBlock(true), createElementBlock(Fragment, null, renderList($data.questionList, function (q, index) {
return (openBlock(), createBlock(_component_flow_form_question, {
ref: $options.setQuestionRef,
question: q,
language: $props.language,
key: 'q' + index,
active: q.index === $data.activeQuestionIndex,
modelValue: q.answer,
"onUpdate:modelValue": function ($event) { return ((q.answer) = $event); },
onAnswer: $options.onQuestionAnswered,
reverse: $data.reverse,
disabled: $data.disabled,
onDisable: $options.setDisabled,
autofocus: $props.autofocus
}, null, 8 /* PROPS */, ["question", "language", "active", "modelValue", "onUpdate:modelValue", "onAnswer", "reverse", "disabled", "onDisable", "autofocus"]))
}), 128 /* KEYED_FRAGMENT */)),
renderSlot(_ctx.$slots, "default"),
createCommentVNode(" Complete/Submit screen slots "),
($options.isOnLastStep)
? (openBlock(), createElementBlock("div", _hoisted_3, [
renderSlot(_ctx.$slots, "complete", {}, function () { return [
createCommentVNode(" Default content for the \"complete\" slot "),
createElementVNode("div", _hoisted_4, [
createElementVNode("p", null, [
createElementVNode("span", _hoisted_5, toDisplayString($props.language.thankYouText), 1 /* TEXT */)
])
])
]; }),
renderSlot(_ctx.$slots, "completeButton", {}, function () { return [
createCommentVNode(" Default content for the \"completeButton\" slot "),
(!$data.submitted)
? (openBlock(), createElementBlock("button", {
key: 0,
class: "o-btn-action",
ref: "button",
type: "button",
href: "#",
onClick: _cache[0] || (_cache[0] = withModifiers(function ($event) { return ($options.submit()); }, ["prevent"])),
"aria-label": $props.language.ariaSubmitText
}, [
createElementVNode("span", null, toDisplayString($props.language.submitText), 1 /* TEXT */)
], 8 /* PROPS */, _hoisted_6))
: createCommentVNode("v-if", true),
(!$data.submitted)
? (openBlock(), createElementBlock("a", {
key: 1,
class: "f-enter-desc",
href: "#",
onClick: _cache[1] || (_cache[1] = withModifiers(function ($event) { return ($options.submit()); }, ["prevent"])),
innerHTML: $props.language.formatString($props.language.pressEnter)
}, null, 8 /* PROPS */, _hoisted_7))
: createCommentVNode("v-if", true),
($data.submitted)
? (openBlock(), createElementBlock("p", _hoisted_8, toDisplayString($props.language.successText), 1 /* TEXT */))
: createCommentVNode("v-if", true)
]; })
]))
: createCommentVNode("v-if", true)
])
]),
createElementVNode("div", _hoisted_9, [
createElementVNode("div", _hoisted_10, [
($props.progressbar)
? (openBlock(), createElementBlock("div", {
key: 0,
class: normalizeClass(["f-progress", {'not-started': $options.percentCompleted === 0, 'completed': $options.percentCompleted === 100}])
}, [
createElementVNode("div", _hoisted_11, [
createElementVNode("div", {
class: "f-progress-bar-inner",
style: normalizeStyle('width: ' + $options.percentCompleted + '%;')
}, null, 4 /* STYLE */)
]),
createTextVNode(" " + toDisplayString($props.language.percentCompleted.replace(':percent', $options.percentCompleted)), 1 /* TEXT */)
], 2 /* CLASS */))
: createCommentVNode("v-if", true),
($props.navigation)
? (openBlock(), createElementBlock("div", _hoisted_12, [
createElementVNode("a", {
class: normalizeClass(["f-prev", {'f-disabled': $data.activeQuestionIndex === 0 || $data.submitted}]),
href: "#",
onClick: _cache[2] || (_cache[2] = withModifiers(function ($event) { return ($options.goToPreviousQuestion()); }, ["prevent"])),
role: "button",
"aria-label": $props.language.ariaPrev
}, [
_hoisted_14,
createElementVNode("span", _hoisted_15, toDisplayString($props.language.prev), 1 /* TEXT */)
], 10 /* CLASS, PROPS */, _hoisted_13),
createElementVNode("a", {
class: normalizeClass(["f-next", {'f-disabled': !$options.isNextQuestionAvailable()}]),
href: "#",
onClick: _cache[3] || (_cache[3] = withModifiers(function ($event) { return ($options.goToNextQuestion()); }, ["prevent"])),
role: "button",
"aria-label": $props.language.ariaNext
}, [
_hoisted_17,
createElementVNode("span", _hoisted_18, toDisplayString($props.language.next), 1 /* TEXT */)
], 10 /* CLASS, PROPS */, _hoisted_16)
]))
: createCommentVNode("v-if", true),
($props.timer)
? (openBlock(), createElementBlock("div", _hoisted_19, [
createElementVNode("span", null, toDisplayString($options.formatTime($data.time)), 1 /* TEXT */)
]))
: createCommentVNode("v-if", true)
])
])
], 2 /* CLASS */))
}
script$1.render = render;
script$1.__file = "src/components/FlowForm.vue";
var script = {
name: 'Question',
mixins: [
ComponentInstance
],
props: {
id: null,
modelValue: [String, Array, Boolean, Number, Object]
},
data: function data() {
return {
question: null
}
},
mounted: function mounted() {
this.setInstance();
},
render: function render() {
return null
},
watch: {
'question.answer': function question_answer(val) {
this.$emit('update:modelValue', val);
}
}
};
script.__file = "src/components/Question.vue";
export { ChoiceOption, script$1 as FlowForm, LanguageModel, LinkOption, MaskPresets, MatrixColumn, MatrixRow, script as Question, QuestionModel, QuestionType };