UNPKG

@kbss-cvut/s-forms

Version:

Semantic forms generator and processor

1 lines 272 kB
{"version":3,"file":"s-forms.cjs","sources":["../src/util/Logger.js","../src/contexts/FormGenContext.js","../src/constants/Constants.js","../src/util/Utils.js","../src/util/JsonLdObjectMap.js","../src/util/JsonLdObjectUtils.js","../src/util/FormUtils.js","../src/contexts/FormQuestionsContext.js","../src/components/DefaultInput.jsx","../src/contexts/ConfigurationContext.js","../src/util/JsonLdFramingUtils.js","../src/contexts/IntlContextProvider.tsx","../src/model/DefaultFormGenerator.js","../src/model/FormGenerator.js","../src/model/QuestionAnswerProcessor.js","../src/components/answer/CheckboxAnswer.jsx","../src/components/answer/DateTimeAnswer.jsx","../src/components/answer/InputAnswer.jsx","../src/util/MaskMapper.js","../src/components/MaskedInput.jsx","../src/components/answer/MaskedInputAnswer.jsx","../src/components/answer/SelectAnswer.jsx","../src/components/answer/TypeaheadAnswer.jsx","../src/styles/icons/CaretSquareUp.jsx","../src/styles/icons/CaretSquareDown.jsx","../src/styles/icons/InfoCircle.jsx","../src/styles/icons/QuestionCircle.jsx","../src/components/IconOverlay.jsx","../src/components/HelpIcon.jsx","../src/styles/icons/ExternalLink.jsx","../src/components/LinkIcon.jsx","../src/styles/icons/CommentBubble.jsx","../src/styles/icons/RecycleBin.jsx","../src/components/comment/CommentView.jsx","../src/components/comment/CommentList.tsx","../src/styles/icons/ArrowRight.jsx","../src/components/comment/CommentForm.jsx","../src/styles/icons/Close.jsx","../src/components/QuestionStatic.jsx","../src/components/comment/QuestionCommentIcon.jsx","../src/components/Answer.jsx","../src/model/ValidatorFactory.js","../src/components/PrefixIcon.jsx","../src/components/MediaContent.tsx","../src/components/Question.jsx","../src/components/wizard/WizardStep.jsx","../src/components/wizard/HorizontalWizardNav.jsx","../src/components/wizard/VerticalWizardNav.jsx","../src/components/wizard/Wizard.jsx","../src/components/FormWindow.jsx","../src/model/ValidationProcessor.js","../src/components/FormManager.jsx","../src/components/ErrorBoundary.jsx","../src/components/SForms.jsx"],"sourcesContent":["export default class Logger {\n static log(msg) {\n console.log(msg);\n }\n\n static warn(msg) {\n if (console.warn) {\n console.warn(msg);\n } else {\n console.log(\"WARNING: \" + msg);\n }\n }\n\n static error(msg) {\n if (console.error) {\n console.error(msg);\n } else {\n console.log(\"ERROR: \" + msg);\n }\n }\n}\n","import React, { useMemo } from \"react\";\nimport PropTypes from \"prop-types\";\nimport * as jsonld from \"jsonld\";\nimport Logger from \"../util/Logger\";\n\nconst FormGenContext = React.createContext({});\n\nconst FormGenContextProvider = ({ children, ...props }) => {\n let options = [];\n\n const loadFormOptions = async (id, query) => {\n const option = options[id];\n\n if (option && option.length) {\n return option;\n }\n\n const data = await props.fetchTypeAheadValues(query);\n\n if (data.length) {\n return jsonld.frame(data, {}, {}).then((framedData) => {\n const option = framedData[\"@graph\"];\n options.push({ ...option, [id]: option });\n return option;\n });\n }\n\n Logger.warn(`No data received when loading options using id ${id}`);\n\n return [];\n };\n\n const getOptions = (id) => options[id] || [];\n\n const values = useMemo(\n () => ({\n loadFormOptions,\n getOptions,\n }),\n [loadFormOptions, getOptions]\n );\n\n return (\n <FormGenContext.Provider value={values} {...props}>\n {children}\n </FormGenContext.Provider>\n );\n};\n\nFormGenContextProvider.propTypes = {\n children: PropTypes.element.isRequired,\n fetchTypeAheadValues: PropTypes.func,\n};\n\nexport { FormGenContext, FormGenContextProvider };\n","import * as JsonLdUtils from \"jsonld-utils\";\n\nexport default class Constants {\n // Default bootstrap column count\n static COLUMN_COUNT = 12;\n static INPUT_LENGTH_THRESHOLD = 50;\n static DATETIME_NUMBER_FORMAT = \"x\";\n static EPOCH_MILLISECONDS = \"epoch-milliseconds\";\n\n /**\n * Contains mainly definition of constants used to parse the form declaration.\n */\n static FORM = \"http://onto.fel.cvut.cz/ontologies/documentation/form\";\n static HAS_SUBQUESTION =\n \"http://onto.fel.cvut.cz/ontologies/documentation/has_related_question\";\n static HAS_ANSWER =\n \"http://onto.fel.cvut.cz/ontologies/documentation/has_answer\";\n static HAS_OPTION =\n \"http://onto.fel.cvut.cz/ontologies/form/has-possible-value\";\n static HAS_OPTIONS_QUERY =\n \"http://onto.fel.cvut.cz/ontologies/form/has-possible-values-query\";\n static HAS_VALUE_TYPE =\n \"http://onto.fel.cvut.cz/ontologies/form/has-value-type\";\n static IS_DISABLED =\n \"http://onto.fel.cvut.cz/ontologies/aviation/form-376/is-disabled\";\n static INPUT_MASK = \"http://onto.fel.cvut.cz/ontologies/form/has-input-mask\";\n static LAYOUT_CLASS =\n \"http://onto.fel.cvut.cz/ontologies/form-layout/has-layout-class\";\n static LAYOUT = {\n FORM: \"form\",\n QUESTION_SECTION: \"section\",\n WIZARD_STEP: \"wizard-step\",\n DATE: \"date\",\n TIME: \"time\",\n DATETIME: \"datetime\",\n TEXT: \"text\",\n TEXTAREA: \"textarea\",\n CHECKBOX: \"checkbox\",\n QUESTION_TYPEAHEAD: \"type-ahead\",\n MASKED_INPUT: \"masked-input\",\n ANSWERABLE: \"answerable\",\n SPARQL: \"sparql\",\n TURTLE: \"ttl\",\n DISABLED: \"disabled\",\n HIDDEN: \"hidden\",\n COLLAPSED: \"collapsed\",\n EMPHASISED: \"emphasised\",\n EMPHASISE_ON_RELEVANT: \"emphasise-on-relevant\",\n CATEGORY: [\n \"category-1\",\n \"category-2\",\n \"category-3\",\n \"category-4\",\n \"category-5\",\n ],\n };\n static HAS_INITIAL_INPUT_LENGTH =\n \"http://onto.fel.cvut.cz/ontologies/form-layout/has-initial-input-length\";\n static VALUE_TYPE_CODE = \"code\";\n static VALUE_TYPE_TEXT = \"text\";\n static GENERATED_ROW_SIZE = 1;\n static HAS_QUESTION_ORIGIN =\n \"http://onto.fel.cvut.cz/ontologies/form/has-question-origin\";\n static HAS_ANSWER_ORIGIN =\n \"http://onto.fel.cvut.cz/ontologies/form/has-answer-origin\";\n static HAS_DATA_VALUE =\n \"http://onto.fel.cvut.cz/ontologies/documentation/has_data_value\";\n static HAS_OBJECT_VALUE =\n \"http://onto.fel.cvut.cz/ontologies/documentation/has_object_value\";\n static HELP_DESCRIPTION = \"http://purl.org/dc/elements/1.1/description\";\n static SOURCE = \"http://purl.org/dc/elements/1.1/source\";\n static XSD = {\n MAX_EXCLUSIVE: \"http://www.w3.org/2001/XMLSchema#maxExclusive\",\n MAX_INCLUSIVE: \"http://www.w3.org/2001/XMLSchema#maxInclusive\",\n MIN_EXCLUSIVE: \"http://www.w3.org/2001/XMLSchema#minExclusive\",\n MIN_INCLUSIVE: \"http://www.w3.org/2001/XMLSchema#minInclusive\",\n\n INT: \"http://www.w3.org/2001/XMLSchema#int\",\n INTEGER: \"http://www.w3.org/2001/XMLSchema#integer\",\n NEGATIVE_INTEGER: \"http://www.w3.org/2001/XMLSchema#negativeInteger\",\n NON_NEGATIVE_INTEGER: \"http://www.w3.org/2001/XMLSchema#nonNegativeInteger\",\n NON_POSITIVE_INTEGER: \"http://www.w3.org/2001/XMLSchema#nonPositiveInteger\",\n POSITIVE_INTEGER: \"http://www.w3.org/2001/XMLSchema#positiveInteger\",\n FLOAT: \"http://www.w3.org/2001/XMLSchema#float\",\n POSITIVE_FLOAT: \"http://www.w3.org/2001/XMLSchema#positiveFloat\",\n NEGATIVE_FLOAT: \"http://www.w3.org/2001/XMLSchema#negativeFloat\",\n\n BOOLEAN: \"http://www.w3.org/2001/XMLSchema#boolean\",\n };\n static STEP = \"http://onto.fel.cvut.cz/ontologies/form/step\";\n static PATTERN = \"http://onto.fel.cvut.cz/ontologies/form/pattern\";\n static ACCEPTS_ANSWER_VALUE =\n \"http://onto.fel.cvut.cz/ontologies/form/accepts-answer-value\";\n static ACCEPTS = \"http://onto.fel.cvut.cz/ontologies/form/accepts\";\n static HAS_DATATYPE = \"http://onto.fel.cvut.cz/ontologies/form/has-datatype\";\n static HAS_LAYOUT_CLASS =\n \"http://onto.fel.cvut.cz/ontologies/form/has-layout-class\";\n static HAS_POSSIBLE_VALUES_QUERY =\n \"http://onto.fel.cvut.cz/ontologies/form/has-possible-values-query\";\n static HAS_REQUIRED_VALUE =\n \"http://onto.fel.cvut.cz/ontologies/form/has-required-value\";\n static HAS_TESTED_QUESTION =\n \"http://onto.fel.cvut.cz/ontologies/form/has-tested-question\";\n static HAS_TESTED_FORM =\n \"http://onto.fel.cvut.cz/ontologies/form/has-tested-form\";\n static HAS_UNIT = \"http://onto.fel.cvut.cz/ontologies/form/has-unit\";\n static HAS_VALID_ANSWER =\n \"http://onto.fel.cvut.cz/ontologies/form/has-valid-answer\";\n static IS_RELEVANT_IF =\n \"http://onto.fel.cvut.cz/ontologies/form/is-relevant-if\";\n static ACCEPTS_VALIDATION_VALUE =\n \"http://onto.fel.cvut.cz/ontologies/form/accepts-validation-value\";\n static HAS_VALIDATION_MESSAGE =\n \"http://onto.fel.cvut.cz/ontologies/form/has-validation-message\";\n static NEGATIVE_CONDITION =\n \"http://onto.fel.cvut.cz/ontologies/form/negative-condition\";\n static REQUIRES_ANSWER =\n \"http://onto.fel.cvut.cz/ontologies/form/requires-answer\";\n static USED_ONLY_FOR_COMPLETENESS =\n \"http://onto.fel.cvut.cz/ontologies/form/used-only-for-completeness\";\n static REQUIRES_ANSWER_DESCRIPTION_IF =\n \"http://onto.fel.cvut.cz/ontologies/form/requires-answer-description-if\";\n static REQUIRES_ANSWER_IF =\n \"http://onto.fel.cvut.cz/ontologies/form/requires-answer-if\";\n static HAS_PRECEDING_QUESTION =\n \"http://onto.fel.cvut.cz/ontologies/form/has-preceding-question\";\n static HAS_PRECEDING_VALUE =\n \"http://onto.fel.cvut.cz/ontologies/form/has-preceding-value\";\n static HAS_MEDIA_CONTENT =\n \"http://onto.fel.cvut.cz/ontologies/form/has-media-content\";\n static CONDITION = \"http://onto.fel.cvut.cz/ontologies/form/condition\";\n static OR_CONDITION = \"http://onto.fel.cvut.cz/ontologies/form/or-condition\";\n static HAS_SUB_CONDITION =\n \"http://onto.fel.cvut.cz/ontologies/form/has-sub-condition\";\n static HAS_PATTERN = \"http://onto.fel.cvut.cz/ontologies/form-lt/has-pattern\";\n static HAS_DECLARED_PREFIX =\n \"http://onto.fel.cvut.cz/ontologies/form-spin/has-declared-prefix\";\n static PREFIX = \"http://www.w3.org/ns/shacl#prefix\";\n static NAMESPACE = \"http://www.w3.org/ns/shacl#namespace\";\n static HAS_COMMENT = \"http://onto.fel.cvut.cz/ontologies/form/has-comment\";\n static HAS_COMMENT_VALUE =\n \"http://onto.fel.cvut.cz/ontologies/form/has-comment-value\";\n static HAS_AUTHOR = \"http://onto.fel.cvut.cz/ontologies/form/has-author\";\n static HAS_TIMESTAMP =\n \"http://onto.fel.cvut.cz/ontologies/form/has-timestamp\";\n static TIME_FORMAT = \"http://onto.fel.cvut.cz/ontologies/form/time-format\";\n static DATE_FORMAT = \"http://onto.fel.cvut.cz/ontologies/form/date-format\";\n static DATETIME_FORMAT =\n \"http://onto.fel.cvut.cz/ontologies/form/datetime-format\";\n static TIMESTAMP_FORMAT =\n \"http://onto.fel.cvut.cz/ontologies/form/timestamp-format\";\n\n static NOT_ANSWERED_QUESTION =\n \"http://onto.fel.cvut.cz/ontologies/form/not-answered-question\";\n static ANSWERED_QUESTION =\n \"http://onto.fel.cvut.cz/ontologies/form/answered-question\";\n static HAS_VALIDATION_SEVERITY =\n \"http://onto.fel.cvut.cz/ontologies/form/has-validation-severity\";\n\n static RDFS_LABEL = JsonLdUtils.RDFS_LABEL;\n static RDFS_COMMENT = JsonLdUtils.RDFS_COMMENT;\n static DEFAULT_HAS_CHILD = JsonLdUtils.DEFAULT_HAS_CHILD;\n static BROADER = \"http://www.w3.org/2004/02/skos/core#broader\";\n static HAS_NON_SELECTABLE_VALUE =\n \"http://onto.fel.cvut.cz/ontologies/form/has-non-selectable-value\";\n static PROVIDES_DEREFERENCEABLE_ANSWER_VALUES =\n \"http://onto.fel.cvut.cz/ontologies/form/provides-dereferenceable-answer-values\";\n\n static ICONS = {\n QUESTION_COMMENTS: \"questionComments\",\n QUESTION_HELP: \"questionHelp\",\n QUESTION_LINK: \"questionLink\",\n };\n\n static ICON_BEHAVIOR = {\n ENABLE: \"enable\",\n DISABLE: \"disable\",\n ON_HOVER: \"onHover\",\n };\n\n // Default form options\n static DEFAULT_OPTIONS = {\n icons: [\n {\n id: Constants.ICONS.QUESTION_HELP,\n behavior: Constants.ICON_BEHAVIOR.ENABLE,\n },\n {\n id: Constants.ICONS.QUESTION_COMMENTS,\n behavior: Constants.ICON_BEHAVIOR.ON_HOVER,\n },\n {\n id: Constants.ICONS.QUESTION_LINK,\n behavior: Constants.ICON_BEHAVIOR.ON_HOVER,\n },\n ],\n };\n\n static LANG = {\n cs: {\n locale: \"cs\",\n label: \"Čestina\",\n },\n en: {\n locale: \"en\",\n label: \"English\",\n },\n };\n\n static VALIDATION = {\n SEVERITY: {\n ERROR: \"error\",\n WARNING: \"warning\",\n },\n DEFAULT_MESSAGE: {\n REQUIRED: \"This field is required.\",\n REQUIRED_ONLY_FOR_COMPLETENESS:\n \"This field is required to complete the form.\",\n INVALID: \"This field is invalid.\",\n CHECK: \"This field must be checked.\",\n LOWER_OR_EQUAL: \"This field should be a number equal or lower to \",\n GREATER_OR_EQUAL: \"This field must be greater or equal to \",\n },\n CLASSNAME: {\n ERROR: \"is-invalid\",\n WARNING: \"is-warning\",\n },\n };\n}\n","import Constants from \"../constants/Constants.js\";\n\nexport default class Utils {\n /**\n * Calculates a simple hash of the specified string, much like usual Java implementations.\n * @param str The string to compute has for\n * @return {number}\n */\n static getStringHash(str) {\n let hash = 0;\n const strlen = str ? str.length : 0;\n\n if (strlen === 0) {\n return hash;\n }\n for (let i = 0; i < strlen; i++) {\n hash = (hash << 5) - hash + str.charCodeAt(i);\n hash = hash & hash; // Convert to 32bit integer\n }\n\n return hash;\n }\n\n /**\n * Wraps passed object into new array if it is not array already.\n * @param object_or_array An object or array.\n * @returns {*} New array containing passed object or passed array.\n */\n static asArray(object_or_array) {\n if (!object_or_array) {\n return [];\n }\n if (object_or_array.constructor === Array) {\n return object_or_array;\n }\n return [object_or_array];\n }\n\n static findQuestionById(id, question, reflexive, asserted, transitive) {\n if (reflexive) {\n if (question[\"@id\"] === id) {\n return question;\n }\n }\n\n const subQuestions = Utils.asArray(question[Constants.HAS_SUBQUESTION]); // we have such method in some kind of json-ld utils\n if (asserted) {\n for (let q of subQuestions) {\n let foundQ = Utils.findQuestionById(id, q, true, false, false);\n if (foundQ) return foundQ;\n }\n }\n\n if (transitive) {\n for (let q of subQuestions) {\n let foundQ = Utils.findQuestionById(id, q, false, true, true);\n if (foundQ) return foundQ;\n }\n }\n\n return null;\n }\n\n /**\n * Finds the first key in an array of objects that matches any of the specified keys.\n *\n * @param {Object[]} objectList - The array of objects to search through.\n * @param {string[]} keysToCheck - An array of keys to check in each object.\n * @returns {?string} - The first key that exists in any of the objects, or null if none are found.\n */\n static findKeyInObjects(objectList, keysToCheck) {\n for (let i = 0; i < objectList.length; i++) {\n for (let j = 0; j < keysToCheck.length; j++) {\n if (objectList[i].hasOwnProperty(keysToCheck[j])) {\n return keysToCheck[j];\n }\n }\n }\n return null;\n }\n}\n","export default class JsonLdObjectMap {\n static objectMap = {};\n\n static putObject(id, question) {\n JsonLdObjectMap.objectMap[id] = question;\n }\n\n static getObject(id) {\n return JsonLdObjectMap.objectMap[id];\n }\n}\n","import * as jsonld from \"jsonld\";\nimport * as JsonLdUtils from \"jsonld-utils\";\nimport Utils from \"./Utils\";\nimport tsort from \"@kbss-cvut/tsort\";\n\nexport default class JsonLdObjectUtils {\n static getFirstObject(subject, predicate) {\n const values = jsonld.getValues(subject, predicate);\n\n if (values.length === 0) {\n //throw \"Subject \" + subject[@id] + \" does not have any value of property \" + predicate;\n return null;\n }\n return values[0];\n }\n\n static compareValues(jsonLdValue1, jsonLdValue2) {\n jsonLdValue1 =\n typeof jsonLdValue1 === \"object\"\n ? jsonLdValue1\n : { \"@value\": jsonLdValue1 };\n jsonLdValue2 =\n typeof jsonLdValue2 === \"object\"\n ? jsonLdValue2\n : { \"@value\": jsonLdValue2 };\n\n // TODO remove: workaround for bad persistance of boolean values -- { @value: \"true\" } instead of { @value: true }\n if (\n jsonLdValue1 &&\n jsonLdValue1[\"@value\"] &&\n jsonLdValue2 &&\n jsonLdValue2[\"@value\"]\n ) {\n const strValue1 =\n typeof jsonLdValue1[\"@value\"] === \"string\"\n ? jsonLdValue1[\"@value\"]\n : JSON.stringify(jsonLdValue1[\"@value\"]);\n const strValue2 =\n typeof jsonLdValue2[\"@value\"] === \"string\"\n ? jsonLdValue2[\"@value\"]\n : JSON.stringify(jsonLdValue2[\"@value\"]);\n\n if (strValue1 === strValue2) {\n return true;\n }\n }\n return jsonld.compareValues(jsonLdValue1, jsonLdValue2);\n }\n\n /**\n * Sorts the specified JSON-LD data using a topological sort over partially ordered set defined by gtProperty.\n *\n * This is useful for situations where each item only knows its immediate neighbour in the list.\n * @param data The data to sort, should be an array\n * @param gtProperty Property specifying that an item is greater than another item. It is used for comparison.\n *\n */\n static toplogicalSort(data, gtProperty) {\n let swapped;\n do {\n swapped = false;\n for (let i = 0; i < data.length; i++) {\n for (let j = i; j < data.length; j++) {\n if (data[i][gtProperty]) {\n let gtId =\n typeof data[i][gtProperty] === \"object\"\n ? data[i][gtProperty][\"@id\"]\n : data[i][gtProperty];\n if (gtId === data[j][\"@id\"]) {\n const tmp = data[i];\n data[i] = data[j];\n data[j] = tmp;\n swapped = true;\n break;\n }\n }\n }\n }\n } while (swapped);\n\n return data;\n }\n\n /**\n * Sorts the specified JSON-LD data using a topological sort over partially ordered set defined by gtProperty,\n * while preserving original order.\n *\n * This is useful for situations where each item only knows its immediate neighbour in the list.\n * @param data The data to sort, should be an array\n * @param gtProperty Property specifying that an item is greater than another item. It is used for comparison.\n *\n */\n static orderPreservingToplogicalSort(data, gtProperty) {\n const graph = tsort();\n const id2ObjectMap = {};\n\n for (let i = 0; i < data.length; i++) {\n let currentId = data[i][\"@id\"];\n graph.add(currentId);\n id2ObjectMap[currentId] = data[i];\n\n Utils.asArray(data[i][gtProperty])\n .map((val) => (typeof val === \"object\" ? val[\"@id\"] : val))\n .map((val) => [val, currentId])\n .forEach((edge) => graph.add(edge));\n }\n\n let sortedIds = graph.sort();\n for (let i = 0; i < sortedIds.length; i++) {\n data[i] = id2ObjectMap[sortedIds[i]];\n }\n return data;\n }\n\n static getCompareLocalizedLabelFunction(intl) {\n return (a, b) => {\n const aLabel = JsonLdUtils.getLocalized(a[JsonLdUtils.RDFS_LABEL], intl);\n const bLabel = JsonLdUtils.getLocalized(b[JsonLdUtils.RDFS_LABEL], intl);\n if (aLabel < bLabel) {\n return -1;\n } else if (aLabel > bLabel) {\n return 1;\n }\n return 0;\n };\n }\n\n static orderByLocalizedLabels(data, intl) {\n return data.sort(JsonLdObjectUtils.getCompareLocalizedLabelFunction(intl));\n }\n\n /**\n * Evaluates if jsonLdObject has provided id.\n * @param jsonLdObject\n * @param id\n * @returns {boolean}\n */\n static checkId(jsonLdObject, id) {\n if (jsonLdObject === undefined) {\n return;\n }\n if (jsonLdObject.constructor === Array) {\n return !!jsonLdObject.find((o) => o[\"@id\"] === id);\n }\n if (jsonLdObject.constructor === Object) {\n return jsonLdObject[\"@id\"] === id;\n }\n }\n}\n","import * as JsonLdUtils from \"jsonld-utils\";\nimport * as jsonld from \"jsonld\";\n\nimport Constants from \"../constants/Constants\";\nimport Utils from \"./Utils\";\nimport JsonLdObjectMap from \"./JsonLdObjectMap\";\nimport JsonLdObjectUtils from \"./JsonLdObjectUtils\";\n\nexport default class FormUtils {\n static isForm(structure) {\n return JsonLdUtils.hasValue(\n structure,\n Constants.LAYOUT_CLASS,\n Constants.LAYOUT.FORM\n );\n }\n\n static isWizardStep(structure) {\n return JsonLdUtils.hasValue(\n structure,\n Constants.LAYOUT_CLASS,\n Constants.LAYOUT.WIZARD_STEP\n );\n }\n\n static isSection(question) {\n return JsonLdUtils.hasValue(\n question,\n Constants.LAYOUT_CLASS,\n Constants.LAYOUT.QUESTION_SECTION\n );\n }\n\n static isAnswerable(question) {\n return JsonLdUtils.hasValue(\n question,\n Constants.LAYOUT_CLASS,\n Constants.LAYOUT.ANSWERABLE\n );\n }\n\n static isTypeahead(question) {\n return JsonLdUtils.hasValue(\n question,\n Constants.LAYOUT_CLASS,\n Constants.LAYOUT.QUESTION_TYPEAHEAD\n );\n }\n\n static getPossibleValuesQuery(question) {\n return JsonLdUtils.getJsonAttValue(question, Constants.HAS_OPTIONS_QUERY);\n }\n\n static isDisabled(question) {\n return JsonLdUtils.hasValue(\n question,\n Constants.LAYOUT_CLASS,\n Constants.LAYOUT.DISABLED\n );\n }\n\n static isHidden(question) {\n return JsonLdUtils.hasValue(\n question,\n Constants.LAYOUT_CLASS,\n Constants.LAYOUT.HIDDEN\n );\n }\n\n static isTextarea(question, answerValue) {\n return (\n (answerValue &&\n answerValue.length > Constants.INPUT_LENGTH_THRESHOLD &&\n !FormUtils.isTypeahead(question)) ||\n JsonLdUtils.hasValue(\n question,\n Constants.LAYOUT_CLASS,\n Constants.LAYOUT.TEXTAREA\n )\n );\n }\n\n static isText(question) {\n return JsonLdUtils.hasValue(\n question,\n Constants.LAYOUT_CLASS,\n Constants.LAYOUT.TEXT\n );\n }\n\n static isCalendar(question) {\n return (\n FormUtils.isDate(question) ||\n FormUtils.isTime(question) ||\n FormUtils.isDateTime(question)\n );\n }\n\n static isDate(question) {\n return JsonLdUtils.hasValue(\n question,\n Constants.LAYOUT_CLASS,\n Constants.LAYOUT.DATE\n );\n }\n\n static isTime(question) {\n return JsonLdUtils.hasValue(\n question,\n Constants.LAYOUT_CLASS,\n Constants.LAYOUT.TIME\n );\n }\n\n static isDateTime(question) {\n return JsonLdUtils.hasValue(\n question,\n Constants.LAYOUT_CLASS,\n Constants.LAYOUT.DATETIME\n );\n }\n\n static isTimestamp(question) {\n return JsonLdUtils.hasValue(\n question,\n Constants.TIMESTAMP_FORMAT,\n Constants.EPOCH_MILLISECONDS\n );\n }\n\n static isCheckbox(question) {\n return JsonLdUtils.hasValue(\n question,\n Constants.LAYOUT_CLASS,\n Constants.LAYOUT.CHECKBOX\n );\n }\n\n static isMaskedInput(question) {\n return JsonLdUtils.hasValue(\n question,\n Constants.LAYOUT_CLASS,\n Constants.LAYOUT.MASKED_INPUT\n );\n }\n\n static isSparqlInput(question) {\n return JsonLdUtils.hasValue(\n question,\n Constants.LAYOUT_CLASS,\n Constants.LAYOUT.SPARQL\n );\n }\n\n static isTurtleInput(question) {\n return JsonLdUtils.hasValue(\n question,\n Constants.LAYOUT_CLASS,\n Constants.LAYOUT.TURTLE\n );\n }\n\n static isCollapsed(question) {\n return JsonLdUtils.hasValue(\n question,\n Constants.LAYOUT_CLASS,\n Constants.LAYOUT.COLLAPSED\n );\n }\n\n static isEmphasised(question) {\n return JsonLdUtils.hasValue(\n question,\n Constants.LAYOUT_CLASS,\n Constants.LAYOUT.EMPHASISED\n );\n }\n\n static getCategory(question) {\n return Constants.LAYOUT.CATEGORY.find((c) =>\n JsonLdUtils.hasValue(question, Constants.LAYOUT_CLASS, c)\n );\n }\n\n static resolveValue(answer) {\n if (!answer) {\n return null;\n }\n if (answer[Constants.HAS_OBJECT_VALUE]) {\n return answer[Constants.HAS_OBJECT_VALUE][\"@id\"];\n } else {\n return JsonLdUtils.getJsonAttValue(answer, Constants.HAS_DATA_VALUE);\n }\n }\n\n static resolveValueObject(answer) {\n if (!answer) {\n return null;\n }\n if (answer[Constants.HAS_OBJECT_VALUE]) {\n return Utils.asArray(answer[Constants.HAS_OBJECT_VALUE])[0];\n }\n if (answer[Constants.HAS_DATA_VALUE]) {\n return Utils.asArray(answer[Constants.HAS_DATA_VALUE])[0];\n }\n return null;\n }\n\n static isRelevant(question) {\n if (!question[Constants.IS_RELEVANT_IF]) {\n return true;\n }\n\n for (let cond of Utils.asArray(question[Constants.IS_RELEVANT_IF])) {\n if (!FormUtils.testCondition(cond)) {\n return false;\n }\n }\n\n return true;\n }\n\n static hasValidationLogic(question) {\n if (question[Constants.REQUIRES_ANSWER]) {\n return true;\n }\n if (question[Constants.REQUIRES_ANSWER_IF]) {\n return true;\n }\n if (question[Constants.PATTERN]) {\n return true;\n }\n if (this.containsXSDProperty(question)) {\n return true;\n }\n return false;\n }\n\n static isValid(question) {\n if (question[Constants.HAS_VALID_ANSWER] === false) {\n return false;\n }\n for (const subQ of Utils.asArray(question[Constants.HAS_SUBQUESTION])) {\n if (this.isValid(subQ) === false) {\n return false;\n }\n }\n\n return true;\n }\n\n static testOrCondition(condition) {\n const hasSubCondition = condition[Constants.HAS_SUB_CONDITION];\n if (!hasSubCondition) {\n console.warn(\"Or condition does not have any sub-condition !\");\n }\n for (const subC of this._getMappedObjectsArray(hasSubCondition)) {\n if (this.testCondition(subC)) {\n return true;\n }\n }\n return false;\n }\n\n static testCondition(condition) {\n const isOrCondition = condition[Constants.HAS_SUB_CONDITION];\n const acceptedValidationsValues =\n condition[Constants.ACCEPTS_VALIDATION_VALUE];\n const acceptedAnswerValues = condition[Constants.ACCEPTS_ANSWER_VALUE];\n const accepts = condition[Constants.ACCEPTS];\n const testedQuestions = condition[Constants.HAS_TESTED_QUESTION];\n let question;\n\n if (isOrCondition) {\n return this.testOrCondition(condition);\n }\n\n if (acceptedValidationsValues && acceptedAnswerValues) {\n console.warn(\n \"Support for validation and requirement constraints at same time is not implemented !\"\n );\n }\n\n // any answer within all subquestions\n if (accepts && testedQuestions) {\n const arr = Utils.asArray(accepts);\n if (arr.length !== 1) {\n console.warn(\n \"Support for multiple accepts values is not implemented !\"\n );\n }\n if (arr[0][\"@id\"] === Constants.ANSWERED_QUESTION) {\n if (acceptedAnswerValues || acceptedValidationsValues) {\n console.warn(\n \"Support for accepted answer/validations values is not implemented !\"\n );\n }\n for (const q of this._getMappedObjectsArray(testedQuestions)) {\n if (!this.hasAnswer(q)) {\n return false;\n }\n }\n return true;\n }\n console.warn(\n \"No support to accept question of type \" + arr[0][\"@id\"] + \" !\"\n );\n }\n\n // valid answers\n if (acceptedValidationsValues && testedQuestions) {\n const arr = Utils.asArray(acceptedValidationsValues);\n if (arr.length !== 1 || (arr[0] !== true && arr[0] !== \"true\")) {\n console.warn(\n 'Validation values other than \"true\" are not implemented !'\n );\n }\n for (const q of Utils.asArray(testedQuestions)) {\n question = JsonLdObjectMap.getObject(q[\"@id\"]);\n if (question === undefined) {\n console.warn(\"Questions is not loaded in an object map.\");\n return true;\n }\n if (this.isValid(question) === false) {\n return false;\n }\n }\n return true;\n }\n\n // concrete values\n if (acceptedAnswerValues && testedQuestions) {\n question = JsonLdObjectMap.getObject(testedQuestions[\"@id\"]);\n for (const expValue of Utils.asArray(acceptedAnswerValues)) {\n if (!question) {\n console.warn(\"Question is not defined.\");\n return true;\n }\n if (!question.hasOwnProperty(Constants.HAS_ANSWER)) {\n //console.warn('Question does not have answer value defined.');\n return false;\n }\n const answers = jsonld.getValues(question, Constants.HAS_ANSWER);\n\n if (answers.length === 0) {\n return false;\n }\n const qValue = FormUtils.resolveValueObject(answers[0]);\n\n if (\n qValue &&\n expValue &&\n qValue.hasOwnProperty(\"@value\") &&\n expValue.hasOwnProperty(\"@id\") &&\n qValue[\"@value\"] == expValue[\"@id\"]\n ) {\n // TODO remove !, this is temporary fix as type-ahead component returns data-value instead of\n // code-value\n return true;\n }\n\n if (JsonLdObjectUtils.compareValues(qValue, expValue)) {\n return true;\n }\n }\n }\n return false;\n }\n\n //returns array of valid object while ignoring\n static _getMappedObjectsArray(jsonObjects, objectType) {\n return Utils.asArray(jsonObjects)\n .map((o) => {\n const obj = JsonLdObjectMap.getObject(o[\"@id\"]);\n if (obj === undefined) {\n const ot = objectType ? objectType : \"Object\";\n console.warn(\n (objectType ? objectType : \"Object\") +\n ' \"' +\n o[\"@id\"] +\n '\"' +\n \" is not loaded in an object map.\"\n );\n return null;\n }\n return obj;\n })\n .filter(function (o) {\n return o !== null;\n });\n }\n\n static hasAnswer(question) {\n if (!question) {\n return false;\n }\n\n if (question.hasOwnProperty(Constants.HAS_ANSWER)) {\n const answers = jsonld.getValues(question, Constants.HAS_ANSWER);\n if (answers.length) {\n const qValue = FormUtils.resolveValueObject(answers[0]);\n if (qValue) {\n if (qValue[\"@value\"] || qValue[\"@id\"]) {\n return true;\n }\n }\n }\n }\n\n for (const subQ of Utils.asArray(question[Constants.HAS_SUBQUESTION])) {\n if (FormUtils.hasAnswer(subQ)) {\n return true;\n }\n }\n\n return false;\n }\n\n static getAnswerValue(question) {\n if (!question) {\n return null;\n }\n\n if (question.hasOwnProperty(Constants.HAS_ANSWER)) {\n const answers = jsonld.getValues(question, Constants.HAS_ANSWER);\n if (answers.length) {\n const qValue = FormUtils.resolveValueObject(answers[0]);\n if (qValue) {\n if (qValue[\"@value\"] || qValue[\"@id\"]) {\n return qValue[\"@value\"] || qValue[\"@id\"];\n }\n }\n }\n }\n\n for (const subQ of Utils.asArray(question[Constants.HAS_SUBQUESTION])) {\n const subQAnswer = FormUtils.getAnswerValue(subQ);\n if (subQAnswer) {\n return subQAnswer;\n }\n }\n\n return null;\n }\n\n /**\n * Resolves which format of date/time/datetime value should be used in the datetime picker.\n * @param question Question with format info\n * @param originalValue Value read from answer, not processed by the rendered component\n * @param options Object with datetime formats\n * @return {*} Format from Configuration\n */\n static resolveDateTimeFormat(question, originalValue, options) {\n const isDate = FormUtils.isDate(question);\n const isTime = FormUtils.isTime(question);\n const isDatetime = FormUtils.isDateTime(question);\n\n switch (true) {\n case isDate:\n return question[Constants.DATE_FORMAT] || options.dateFormat;\n\n case isTime:\n return question[Constants.TIME_FORMAT] || options.timeFormat;\n\n case isDatetime:\n return question[Constants.DATETIME_FORMAT] || options.dateTimeFormat;\n default:\n return null;\n }\n }\n\n /**\n * Traverse provided array of questions recursively by DFS algorithm\n * and execute on each traversed question provided function.\n * @param {Array<Object>} questions - The array of questions to be traversed.\n * @param {Function} onEnterQuestion - The callback function to execute when entering a question.\n * @param {Function} [onLeaveQuestion = () => {}] - The callback function to execute when leaving a question.\n */\n static dfsTraverseQuestionTree(\n questions,\n onEnterQuestion,\n onLeaveQuestion = () => {}\n ) {\n function recursiveTraverse(question) {\n onEnterQuestion(question);\n Utils.asArray(question[Constants.HAS_SUBQUESTION]).forEach((q) => {\n recursiveTraverse(q);\n });\n onLeaveQuestion(question);\n }\n\n questions.forEach((q) => {\n recursiveTraverse(q);\n });\n }\n\n static getFormSpecification(questions) {\n let level = -1;\n const indentation = \" \";\n const propertyIndentation = \"..\";\n let output = \"\";\n\n function onEnterQuestion(q) {\n level += 1;\n const ind = indentation.repeat(level);\n let requiredValue = \"\";\n if (\n q[Constants.REQUIRES_ANSWER] &&\n !q[Constants.USED_ONLY_FOR_COMPLETENESS]\n ) {\n requiredValue =\n ind +\n propertyIndentation +\n \"required: \" +\n q[Constants.REQUIRES_ANSWER] +\n \"\\n\";\n } else if (q[Constants.USED_ONLY_FOR_COMPLETENESS]) {\n requiredValue =\n ind +\n propertyIndentation +\n \"required only for completeness: \" +\n q[Constants.USED_ONLY_FOR_COMPLETENESS] +\n \"\\n\";\n }\n\n let description = q[Constants.HELP_DESCRIPTION]\n ? ind +\n propertyIndentation +\n \"description: \" +\n q[Constants.HELP_DESCRIPTION] +\n \"\\n\"\n : \"\";\n\n let pattern = q[Constants.PATTERN]\n ? ind + propertyIndentation + \"pattern: \" + q[Constants.PATTERN] + \"\\n\"\n : \"\";\n\n let mask = q[Constants.INPUT_MASK]\n ? ind + propertyIndentation + \"mask: \" + q[Constants.INPUT_MASK] + \"\\n\"\n : \"\";\n\n let validationMessage = q[Constants.HAS_VALIDATION_MESSAGE]\n ? ind +\n propertyIndentation +\n \"validation-message: \" +\n q[Constants.HAS_VALIDATION_MESSAGE] +\n \"\\n\"\n : \"\";\n\n let minInclusive = q[Constants.XSD.MIN_INCLUSIVE]\n ? ind +\n propertyIndentation +\n \"This field must be greater or equal to \" +\n q[Constants.XSD.MIN_INCLUSIVE] +\n \"\\n\"\n : \"\";\n\n let maxInclusive = q[Constants.XSD.MAX_INCLUSIVE]\n ? ind +\n propertyIndentation +\n \"This field should be a number equal or lower to \" +\n q[Constants.XSD.MAX_INCLUSIVE] +\n \"\\n\"\n : \"\";\n\n output += ind + q[\"http://www.w3.org/2000/01/rdf-schema#label\"] + \"\\n\";\n output += description;\n output += requiredValue;\n output += pattern;\n output += mask;\n output += maxInclusive;\n output += minInclusive;\n output += validationMessage;\n output += \"\\n\";\n }\n\n function onLeaveQuestion() {\n level -= 1;\n }\n\n FormUtils.dfsTraverseQuestionTree(\n questions,\n onEnterQuestion,\n onLeaveQuestion\n );\n\n return output;\n }\n\n /**\n * Checks if the keys of a question contain any of the values from the XSD object.\n * @param {Object} question - The question to check.\n * @returns {boolean} - True if any XSD value is found among the keys, otherwise false.\n */\n static containsXSDProperty(question) {\n const objKeys = Object.keys(question);\n const xsdValues = Object.values(Constants.XSD);\n\n for (const value of xsdValues) {\n if (objKeys.includes(value)) {\n return true;\n }\n }\n return false;\n }\n\n static getInputMask(question) {\n if (question[Constants.INPUT_MASK]) {\n return question[Constants.INPUT_MASK];\n }\n }\n}\n","import React, { useMemo, useState, useEffect } from \"react\";\nimport PropTypes from \"prop-types\";\nimport FormUtils from \"../util/FormUtils\";\n\nconst INITIAL_DATA = {};\nconst INITIAL_FORM_QUESTIONS_DATA = [];\n\nconst FormQuestionsContext = React.createContext({});\n\nconst FormQuestionsProvider = (props) => {\n const [data, setData] = useState(props.data || INITIAL_DATA);\n const [formQuestionsData, setFormQuestionsData] = useState(\n props.formQuestions || INITIAL_FORM_QUESTIONS_DATA\n );\n\n useEffect(() => {\n if (props.isFormValid) {\n const isValid = FormUtils.isValid(data);\n props.isFormValid(isValid);\n }\n }, []);\n\n useEffect(() => {\n setData(props.data || INITIAL_DATA);\n }, [formQuestionsData]);\n\n const updateData = (update) => {\n if (!update) return;\n\n setData({ ...data, ...update });\n };\n\n const updateFormQuestionsData = (index, update) => {\n if (!update || index < 0 || index >= formQuestionsData.length) return;\n\n if (typeof update === \"object\") {\n const newFormQuestionsData = [...formQuestionsData];\n newFormQuestionsData[index] = {\n ...newFormQuestionsData[index],\n ...update,\n };\n setFormQuestionsData(newFormQuestionsData);\n }\n\n if (Array.isArray(update)) {\n setFormQuestionsData(update);\n }\n\n if (props.isFormValid) {\n const isValid = FormUtils.isValid(data);\n props.isFormValid(isValid);\n }\n };\n\n const getData = () => {\n return data;\n };\n\n const getFormQuestionsData = (index) => {\n return index === null || index === undefined\n ? formQuestionsData\n : formQuestionsData[index];\n };\n\n const values = useMemo(\n () => ({\n updateData,\n updateFormQuestionsData,\n getData,\n getFormQuestionsData,\n }),\n [getFormQuestionsData, getData]\n );\n\n return (\n <FormQuestionsContext.Provider value={values} {...props}>\n {props.children}\n </FormQuestionsContext.Provider>\n );\n};\n\nFormQuestionsProvider.propTypes = {\n children: PropTypes.element.isRequired,\n data: PropTypes.object.isRequired,\n formQuestions: PropTypes.array.isRequired,\n isFormValid: PropTypes.func,\n};\n\nexport { FormQuestionsContext, FormQuestionsProvider };\n","import React from \"react\";\nimport ReactDOM from \"react-dom\";\nimport PropTypes from \"prop-types\";\nimport { FormControl, FormGroup, Form } from \"react-bootstrap\";\n\nexport default class DefaultInput extends React.Component {\n constructor(props) {\n super(props);\n\n this.state = {\n cursorPosition: 0,\n };\n }\n\n focus() {\n ReactDOM.findDOMNode(this.input).focus();\n }\n\n getInputDOMNode() {\n return ReactDOM.findDOMNode(this.input);\n }\n\n componentDidUpdate(prevProps, prevState, snapshot) {\n if (this.fieldDidShrink(prevProps) || this.fieldDidExpand(prevProps)) {\n this.updateFieldCursorPosition();\n }\n }\n\n fieldDidExpand(prevProps) {\n return this.props.type === \"textarea\" && prevProps.type !== \"textarea\";\n }\n\n fieldDidShrink(prevProps) {\n return this.props.type === \"text\" && prevProps.type !== \"text\";\n }\n\n updateFieldCursorPosition() {\n this.focus();\n this.getInputDOMNode().setSelectionRange(\n this.state.cursorPosition,\n this.state.cursorPosition\n );\n }\n\n saveCursorPosition(e) {\n this.props.onChange(e);\n this.setState({\n cursorPosition: e.target.selectionStart,\n });\n }\n\n render() {\n switch (this.props.type) {\n case \"radio\":\n return this._renderRadio();\n case \"checkbox\":\n return this._renderCheckbox();\n case \"select\":\n return this._renderSelect();\n case \"textarea\":\n return this._renderTextArea();\n default:\n return this._renderInput();\n }\n }\n\n _renderCheckbox() {\n // TODO change control id to hash of label\n return (\n <Form.Group\n size=\"small\"\n controlId={Math.floor(Math.random() * Number.MAX_SAFE_INTEGER)}\n className=\"mb-3\"\n >\n <Form.Check\n type=\"checkbox\"\n ref={(c) => (this.input = c)}\n {...this.props}\n label={this.props.label}\n className={this.props.validation.classname}\n />\n {this.props.validation.message}\n </Form.Group>\n );\n }\n\n _renderRadio() {\n return (\n <FormGroup size=\"small\" className=\"mb-3\">\n <Form.Check\n type=\"radio\"\n ref={(c) => (this.input = c)}\n {...this.props}\n label={this.props.label}\n className={this.props.validation.classname}\n />\n {this.props.validation.message}\n </FormGroup>\n );\n }\n\n _renderSelect() {\n return (\n <FormGroup size=\"small\" className=\"mb-3\">\n {this._renderLabel()}\n <FormControl\n as=\"select\"\n className={this.props.validation.classname}\n ref={(c) => (this.input = c)}\n {...this.props}\n >\n {this.props.children}\n </FormControl>\n {this.props.validation.message}\n </FormGroup>\n );\n }\n\n _renderLabel() {\n return this.props.label ? (\n <Form.Label>{this.props.label}</Form.Label>\n ) : null;\n }\n\n _renderTextArea() {\n // TODO validation\n return (\n <FormGroup size=\"small\" className=\"mb-3\">\n {this._renderLabel()}\n <FormControl\n ref={(c) => (this.input = c)}\n as=\"textarea\"\n {...this.props}\n className={this.props.validation.classname}\n onChange={(e) => this.saveCursorPosition(e)}\n />\n {this.props.validation.message}\n </FormGroup>\n );\n }\n\n _renderInput() {\n return (\n <FormGroup size=\"small\" className=\"mb-3\">\n {this._renderLabel()}\n <FormControl\n className={this.props.validation.classname}\n ref={(c) => (this.input = c)}\n as=\"input\"\n placeholder={this.props.placeholder}\n {...this.props}\n onChange={(e) => this.saveCursorPosition(e)}\n />\n {this.props.validation.message}\n </FormGroup>\n );\n }\n}\n\nDefaultInput.propTypes = {\n type: PropTypes.string,\n label: PropTypes.object,\n value: PropTypes.any,\n onChange: PropTypes.func,\n help: PropTypes.string,\n validation: PropTypes.object,\n placeholder: PropTypes.string,\n};\n\nDefaultInput.defaultProps = {\n type: \"text\",\n};\n","import React, { useMemo } from \"react\";\nimport PropTypes from \"prop-types\";\nimport DefaultInput from \"../components/DefaultInput\";\n\nconst ConfigurationContext = React.createContext({});\n\nconst defaultProps = {\n components: {\n inputComponent: DefaultInput,\n },\n componentsOptions: {\n dateTimeAnswer: {\n dateFormat: \"yyyy-MM-dd\",\n timeFormat: \"HH:mm\",\n dateTimeFormat: \"yyyy-MM-dd HH:mm:ss\",\n },\n readOnly: false,\n },\n options: {\n intl: { locale: \"en\" },\n modalView: false,\n modalProps: {},\n horizontalWizardNav: true,\n wizardStepButtons: true,\n enableWizardStepSkip: true,\n startingStep: 0,\n },\n};\n\nconst ConfigurationContextProvider = ({ children, ...props }) => {\n const values = useMemo(\n () => ({\n inputComponent:\n (props.components && props.components.inputComponent) ||\n defaultProps.components.inputComponent,\n componentsOptions: {\n ...defaultProps.componentsOptions,\n ...props.componentsOptions,\n },\n options: { ...defaultProps.options, ...props.options },\n mapComponent: props.mapComponent,\n }),\n [props]\n );\n\n return (\n <ConfigurationContext.Provider value={values}>\n {children}\n </ConfigurationContext.Provider>\n );\n};\n\nConfigurationContextProvider.propTypes = {\n children: PropTypes.element.isRequired,\n components: PropTypes.object,\n mapComponent: PropTypes.func,\n options: PropTypes.object,\n};\n\nexport { ConfigurationContext, ConfigurationContextProvider };\n","import * as jsonld from \"jsonld\";\nimport Constants from \"../constants/Constants\";\nimport FormUtils from \"../util/FormUtils\";\nimport Utils from \"./Utils\";\n\nconst formShape = {\n root: {\n [Constants.HAS_LAYOUT_CLASS]: Constants.FORM,\n },\n expandProperties: [\n Constants.HAS_SUBQUESTION,\n Constants.IS_RELEVANT_IF,\n Constants.HAS_ANSWER,\n Constants.HAS_DECLARED_PREFIX,\n Constants.HAS_OPTION,\n Constants.HAS_COMMENT,\n ],\n};\n\nexport default class JsonLdFramingUtils {\n //TODO revise\n\n /**\n * Performs JSON-LD custom framing using shapes. Shape is declaration of the JSON-LD framing\n * in a language that is incompatible with JSON-LD specification.\n *\n * @param input the JSON-LD input to framing by a shape.\n * @param shape the JSON-LD custom frame configuration using shapes.\n * @param callback(err, framed) called once the operation completes.\n */\n static customFrame(input, shape, callback) {\n if (arguments.length < 2) {\n return jsonld.nextTick(function () {\n callback(new TypeError(\"Could not frame, too few arguments.\"));\n });\n }\n\n if (shape === null) {\n //TODO remove\n shape = formShape;\n }\n\n let flattened = null;\n let err = null;\n const flattenedCallback = (e, f) => {\n flattened = f;\n err = e;\n };\n\n jsonld.flatten(input, null, null, flattenedCallback);\n }\n\n static expandStructure(structure) {\n let root;\n const id2objectMap = {}; // mapping @id -> object\n\n structure[\"@graph\"].forEach((item) => {\n id2objectMap[item[\"@id\"]] = item;\n\n if (FormUtils.isForm(item)) {\n root = item;\n }\n });\n\n try {\n this._expandGraph(root, formShape, id2objectMap);\n } catch (e) {\n console.error(\n \"Error '\" +\n e +\n \"' occured, while trying to apply frame-ing with custom shape.\"\n );\n }\n\n return id2objectMap;\n }\n\n static _expandGraph(parentNode, shape, id2ObjectMap) {\n let childArray;\n let child;\n let childId;\n\n shape.expandProperties.forEach((prop) => {\n if (parentNode.hasOwnProperty(prop)) {\n parentNode[prop] = Utils.asArray(parentNode[prop]);\n childArray = parentNode[prop];\n\n for (let i = 0; i < childArray.length; i++) {\n childId = this._getId(childArray[i]);\n child = id2ObjectMap[childId];\n\n if (child !== undefined) {\n childArray[i] = child;\n //console.log(childId + \" expanded.\");\n this._expandGraph(child, shape, id2ObjectMap);\n } else {\n console.warn(\n \"object with @id \" + childId + \" was not defined in input data.\"\n );\n }\n }\n }\n });\n }\n\n static _getId(jsonObject) {\n if (typeof jsonObject === \"string\") {\n return jsonObject;\n }\n return jsonObject[\"@id\"];\n }\n\n static compressStructure = (rootNode) => {\n let object2IdMap = []; // mapping object -> id\n let idIncluded = new Set();\n\n object2IdMap = this._compressGraph(rootNode, object2IdMap, idIncluded);\n\n object2IdMap = object2IdMap.sort((a, b) => {\n if (a[\"@id\"] && b[\"@id\"]) {\n return a[\"@id\"].localeCompare(b[\"@id\"]);\n }\n return 0;\n });\n\n return object2IdMap;\n };\n\n static _compressGraph = (parentNode, object2IdMap, idIncluded) => {\n if (!idIncluded.has(parentNode[\"@id\"])) {\n object2IdMap.push(parentNode);\n idIncluded.add(parentNode[\"@id\"]);\n }\n\n formShape.expandProperties.forEach((prop) => {\n if (parentNode.hasOwnProperty(prop)) {\n const childArray = parentNode[prop];\n\n for (let i = 0; i < childArray.length; i++) {\n const child = childArray[i];\n\n if (child !== undefined) {\n childArray[i] = child;\n\n object2IdMap = this._compressGraph(child, object2IdMap, idIncluded);\n\n parentNode[prop][i] = { \"@id\": child[\"@id\"] };\n }\n }\n }\n });\n\n return object2IdMap;\n };\n}\n","import React, { ReactNode } from \"react\";\nimport Constants from \"../constants/Constants\";\nimport cs from \"../i18n/cs.json\";\nimport en from