forked from episphere/quest
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathinitSurvey.js
114 lines (97 loc) · 5.15 KB
/
initSurvey.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
import { moduleParams } from './questionnaire.js';
import { initializeCustomMathJSFunctions, math } from './customMathJSImplementation.js';
import { initializeStateManager } from './stateManager.js';
import { QuestionProcessor } from './questionProcessor.js';
import { getStateManager } from './stateManager.js';
/**
* Initialize the survey: state manager, precalculated values, mathJS implementation, and questionProcessor.
* Normalize ids in asyncQuestionsMap for DOM manipulation: [D_761310265?] -> D_761310265.
* moduleParams.activate determines if the survey is embedded in an application or found in the included rendering tool.
* If activate is true, the survey is embedded and the retrieve function and CSS files are fetched.
* If activate is false, the survey is standalone in the renderer tool.
* @param {String} markdown - The markdown contents of the survey prior to transformation.
* @returns {Array} - An array containing the transformed contents, questName, and retrievedData.
*/
export async function initSurvey(markdown) {
initializeStateManager(moduleParams.store);
initializeCustomMathJSFunctions();
const precalculated_values = getPreCalculatedValues(markdown);
const questionProcessor = new QuestionProcessor(markdown, precalculated_values, moduleParams.i18n);
const stateManager = getStateManager();
stateManager.setQuestionProcessor(questionProcessor);
moduleParams.asyncQuestionsMap = Object.fromEntries(
Object.entries(moduleParams.asyncQuestionsMap).map(([key, value]) => {
const normalizedKey = key.replace(/[[\]]/g, '').replace(/[?!]$/, '');
return [normalizedKey, value];
})
);
return !moduleParams.isRenderer
? await fetchAndProcessResources()
: null;
}
/**
* Fetch and process the resources for the survey. This includes the retrieve function (existing user data) and CSS files.
* See moduleParams for the configuration (main.js).
* @returns {Object} - The retrieved data from the retrieve function or null.
*/
async function fetchAndProcessResources() {
// Helper function to unwrap the data from the retrieve function response. This format is necessary for fillForm().
function unwrapData(data) {
if (data && typeof data === 'object' && !Array.isArray(data)) {
const keys = Object.keys(data);
if (keys.length === 1) {
return data[keys[0]];
}
}
return data;
}
try {
const shouldFetchStylesheets = moduleParams.url && moduleParams.activate;
const [retrieveFunctionResponse, cssActiveLogic, cssStyle1] = await Promise.all([
moduleParams.retrieve && !moduleParams.surveyDataPrefetch ? moduleParams.retrieve() : Promise.resolve(),
shouldFetchStylesheets ? fetch(`${moduleParams.basePath}ActiveLogic.css`).then(response => response.text()) : Promise.resolve(),
shouldFetchStylesheets ? fetch(`${moduleParams.basePath}Style1.css`).then(response => response.text()) : Promise.resolve(),
]);
// retrievedData is the prefetched user data, the result of the retrieve function, or null (for the renderer or when no retrieve function is provided).
const retrievedData = moduleParams.surveyDataPrefetch || unwrapData(retrieveFunctionResponse?.data);
// Add the stylesheets to the document.
if (shouldFetchStylesheets) {
[cssActiveLogic, cssStyle1].forEach((css) => {
const cssTextBlob = new Blob([css], { type: 'text/css' });
const stylesheetLinkElement = document.createElement('link');
stylesheetLinkElement.rel = 'stylesheet';
stylesheetLinkElement.href = URL.createObjectURL(cssTextBlob);
document.head.appendChild(stylesheetLinkElement);
});
}
return retrievedData;
} catch (error) {
moduleParams.errorLogger('Error fetching retrieve function and css:', error);
return null;
}
}
/**
* Pre-calculate values for the survey that don't work with the service worker.
* Date operations and operations 'window' access are not compatible with the worker. Pre-calculate these values prior to worker access.
* @param {String} contents - The markdown contents of the survey prior to transformation.
* @returns {Object} - The precalculated values for the survey.
*/
function getPreCalculatedValues(contents) {
const dateToQuestFormat = (date) => {
return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`;
}
const current_date = new Date();
const precalculated_values = {
current_date: current_date,
current_day: current_date.getDate(),
current_month_str: moduleParams.i18n.months[current_date.getMonth()],
current_month: current_date.getMonth() + 1,
current_year: current_date.getFullYear(),
quest_format_date: dateToQuestFormat(current_date),
};
// Find all user variables in the questText and add them to precalculated_values.
[...contents.matchAll(/\{\$u:(\w+)}/g)].forEach(([, varName]) => {
precalculated_values[varName] = math._value(varName);
});
return precalculated_values;
}