@ditdot-dev/vue-flow-form
Version:
Create conversational conditional-logic forms with Vue.js.
1,705 lines (1,453 loc) • 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
}
}