lforms 29.3.0 → 33.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +261 -0
- package/README.md +97 -14
- package/changed-features.md +99 -0
- package/dist/lforms/LICENSE.md +70 -0
- package/dist/lforms/README.md +100 -0
- package/dist/lforms/fhir/R4/lformsFHIR.min.js +2 -0
- package/dist/lforms/fhir/R4/lformsFHIR.min.js.map +1 -0
- package/dist/lforms/fhir/STU3/lformsFHIR.min.js +2 -0
- package/dist/lforms/fhir/STU3/lformsFHIR.min.js.map +1 -0
- package/dist/lforms/fhir/lformsFHIRAll.min.js +2 -0
- package/dist/lforms/fhir/lformsFHIRAll.min.js.map +1 -0
- package/dist/lforms/webcomponent/assets/lib/zone.min.js +134 -0
- package/dist/lforms/webcomponent/down_arrow_gray_10_10.png +0 -0
- package/dist/lforms/webcomponent/favicon.ico +0 -0
- package/dist/lforms/webcomponent/index.html +9 -0
- package/dist/lforms/webcomponent/lhc-forms.js +2471 -0
- package/dist/lforms/webcomponent/magnifying_glass.png +0 -0
- package/dist/lforms/webcomponent/main.js +2464 -0
- package/dist/lforms/webcomponent/main.js.map +1 -0
- package/dist/lforms/webcomponent/polyfills.js +2 -0
- package/dist/lforms/webcomponent/polyfills.js.map +1 -0
- package/dist/lforms/webcomponent/runtime.js +2 -0
- package/dist/lforms/webcomponent/runtime.js.map +1 -0
- package/dist/lforms/webcomponent/scripts.js +3 -0
- package/dist/lforms/webcomponent/scripts.js.map +1 -0
- package/dist/lforms/webcomponent/styles.css +3 -0
- package/dist/lforms/webcomponent/styles.css.map +1 -0
- package/form_definition.md +382 -0
- package/lforms-33.2.0.tgz +0 -0
- package/package.json +94 -88
- package/r4-support.md +147 -0
- package/sdc-support.md +127 -0
- package/app/.npmignore +0 -3
- package/app/images/blank.gif +0 -0
- package/app/images/signal-stop.png +0 -0
- package/app/index.html +0 -10
- package/app/lforms.js +0 -56
- package/app/lforms.tpl.js +0 -964
- package/app/scripts/angular-packages.js +0 -11
- package/app/scripts/bootstrap-decorators.js +0 -31
- package/app/scripts/bower-index.js +0 -14
- package/app/scripts/fhir/R4/export.js +0 -33
- package/app/scripts/fhir/R4/fhirRequire.js +0 -36
- package/app/scripts/fhir/R4/lformsFHIR.js +0 -27260
- package/app/scripts/fhir/R4/sdc-export.js +0 -455
- package/app/scripts/fhir/R4/sdc-import.js +0 -483
- package/app/scripts/fhir/STU3/export.js +0 -27
- package/app/scripts/fhir/STU3/fhirRequire.js +0 -36
- package/app/scripts/fhir/STU3/lformsFHIR.js +0 -27291
- package/app/scripts/fhir/STU3/sdc-export.js +0 -493
- package/app/scripts/fhir/STU3/sdc-import.js +0 -494
- package/app/scripts/fhir/diagnostic-report.js +0 -707
- package/app/scripts/fhir/export-common.js +0 -209
- package/app/scripts/fhir/expression-processor.js +0 -670
- package/app/scripts/fhir/extensions/rendering-style.js +0 -23
- package/app/scripts/fhir/fhir-common.js +0 -3
- package/app/scripts/fhir/obs-prepop.mjs +0 -97
- package/app/scripts/fhir/runtime-common.js +0 -33
- package/app/scripts/fhir/sdc-common.js +0 -334
- package/app/scripts/fhir/sdc-export-common.js +0 -1046
- package/app/scripts/fhir/sdc-import-common.js +0 -1343
- package/app/scripts/fhir/versions.js +0 -8
- package/app/scripts/index.js +0 -20
- package/app/scripts/lforms-config.js +0 -12
- package/app/scripts/lforms-constants.js +0 -10
- package/app/scripts/lforms-controllers.js +0 -1196
- package/app/scripts/lforms-directives.js +0 -424
- package/app/scripts/lforms-index.js +0 -20
- package/app/scripts/lib/cross_browser.js +0 -21
- package/app/scripts/lib/lforms-data.js +0 -3591
- package/app/scripts/lib/lforms-hl7.js +0 -525
- package/app/scripts/lib/lforms-util.js +0 -1122
- package/app/scripts/lib/lforms-validate.js +0 -277
- package/app/scripts/lib/polyfill.js +0 -81
- package/app/scripts/themeList.js +0 -17
- package/app/styles/lforms.css +0 -1219
- package/app/styles/redmond-theme/README.txt +0 -2
- package/app/styles/redmond-theme/images/ui-bg_glass_75_d0e5f5_1x400.png +0 -0
- package/app/styles/redmond-theme/images/ui-bg_glass_85_dfeffc_1x400.png +0 -0
- package/app/styles/redmond-theme/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- package/app/styles/redmond-theme/images/ui-bg_gloss-wave_55_5c9ccc_500x100.png +0 -0
- package/app/styles/redmond-theme/images/ui-bg_inset-hard_100_f5f8f9_1x100.png +0 -0
- package/app/styles/redmond-theme/images/ui-bg_inset-hard_100_fcfdfd_1x100.png +0 -0
- package/app/styles/redmond-theme/images/ui-icons_217bc0_256x240.png +0 -0
- package/app/styles/redmond-theme/images/ui-icons_2e83ff_256x240.png +0 -0
- package/app/styles/redmond-theme/images/ui-icons_469bdd_256x240.png +0 -0
- package/app/styles/redmond-theme/images/ui-icons_6da8d5_256x240.png +0 -0
- package/app/styles/redmond-theme/images/ui-icons_cd0a0a_256x240.png +0 -0
- package/app/styles/redmond-theme/images/ui-icons_d8e7f3_256x240.png +0 -0
- package/app/styles/redmond-theme/images/ui-icons_f9bd01_256x240.png +0 -0
- package/app/styles/redmond-theme/jquery-ui.css +0 -1311
- package/app/styles/redmond-theme/jquery-ui.min.css +0 -7
- package/app/styles/redmond-theme/theme.css +0 -443
- package/app/styles/themes.css +0 -1541
- package/app/styles/themes.css.map +0 -9
- package/app/styles/themes.scss +0 -411
- package/app/version.json +0 -1
- package/app/views/partials/attachment.html +0 -38
- package/app/views/partials/field-answers.html +0 -67
- package/app/views/partials/field-units.html +0 -24
- package/app/views/partials/form-controls.html +0 -19
- package/app/views/partials/form-header.html +0 -35
- package/app/views/partials/form-options.html +0 -50
- package/app/views/partials/form-title.html +0 -20
- package/app/views/partials/form-view.html +0 -13
- package/app/views/partials/item-options.html +0 -48
- package/app/views/partials/item.html +0 -126
- package/app/views/partials/itemPrefixAndText.html +0 -4
- package/app/views/partials/layout-horizontal.html +0 -156
- package/app/views/partials/layout-matrix.html +0 -145
- package/app/views/partials/popover-content.html +0 -1
- package/app/views/partials/repeating-button.html +0 -16
- package/app/views/partials/table-item.html +0 -30
- package/app/views/partials/template-table.html +0 -69
- package/app/views/partials/uib-popover-templates/uib-popover-template.html +0 -13
- package/app/views/partials/uib-popover-templates/uib-popover.html +0 -11
|
@@ -1,1196 +0,0 @@
|
|
|
1
|
-
var LForms = require('../lforms');
|
|
2
|
-
|
|
3
|
-
angular.module('lformsWidget')
|
|
4
|
-
.controller('LFormsCtrl',
|
|
5
|
-
['$window','$scope', '$element', '$timeout', '$interval', '$sce', 'smoothScroll', 'LF_CONSTANTS', 'lformsConfig',
|
|
6
|
-
function ($window, $scope, $element, $timeout, $interval, $sce, smoothScroll, LF_CONSTANTS, lformsConfig) {
|
|
7
|
-
'use strict';
|
|
8
|
-
|
|
9
|
-
$scope.debug = false;
|
|
10
|
-
|
|
11
|
-
$scope.hasUnused = false;
|
|
12
|
-
$scope.repeatingSectionStatus = {};
|
|
13
|
-
|
|
14
|
-
$scope.validationInitialShowTime = LF_CONSTANTS.VALIDATION_MESSAGE_INITIAL_SHOW_TIME;
|
|
15
|
-
|
|
16
|
-
// Provide blank image to satisfy img tag. Bower packaging forces us to
|
|
17
|
-
// avoid using image files from html templates, therefore using base64
|
|
18
|
-
// encoding for a 1x1 blank gif file.
|
|
19
|
-
$scope.blankGifDataUrl = LF_CONSTANTS.BLANK_GIF_DATAURL;
|
|
20
|
-
|
|
21
|
-
// Default option for calendar - for the JQuery datepicker
|
|
22
|
-
$scope.dateOptions = {
|
|
23
|
-
changeYear: true,
|
|
24
|
-
changeMonth: true,
|
|
25
|
-
yearRange: '1800:-0',
|
|
26
|
-
showOn: 'button',
|
|
27
|
-
constrainInput: false,
|
|
28
|
-
showOtherMonths: true,
|
|
29
|
-
selectOtherMonths: true,
|
|
30
|
-
showMonthAfterYear: true,
|
|
31
|
-
buttonText: "Show date picker"
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
// uib booststrap datetime picker, https://github.com/Gillardo/bootstrap-ui-datetime-picker
|
|
35
|
-
$scope.uibDateTimePickerFormat = "MM/dd/yyyy HH:mm";
|
|
36
|
-
$scope.uibDatePickerAltFormats = [
|
|
37
|
-
'yyyy',
|
|
38
|
-
'M/yyyy', 'MM/yyyy', 'yyyy/M', 'yyyy/MM',
|
|
39
|
-
'M/d/yyyy', 'MM/d/yyyy', 'M/dd/yyyy', 'MM/dd/yyyy',
|
|
40
|
-
"M/d/yyyy HH:mm", "MM/d/yyyy HH:mm", "M/dd/yyyy HH:mm",
|
|
41
|
-
"yyyy-MM", "yyyy-MM-dd", "yyyy-MM-dd HH:mm"
|
|
42
|
-
];
|
|
43
|
-
|
|
44
|
-
$scope.uibDatePickerOptions = {
|
|
45
|
-
showWeeks: false
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
$scope.uibTimePickerOptions = {
|
|
49
|
-
arrowkeys: true,
|
|
50
|
-
//showSeconds: false
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
// open the uib bootstrap datetime picker
|
|
54
|
-
$scope.openUibDtmPicker = function(e) {
|
|
55
|
-
this.isOpen = true;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
$scope.uibDtmPickerButtonBar = {
|
|
59
|
-
// specify the class for the close button so that we can locate the element.
|
|
60
|
-
// must specify each button but each button's conf fall back to system default if not specified.
|
|
61
|
-
show: true,
|
|
62
|
-
now: {},
|
|
63
|
-
today: {},
|
|
64
|
-
clear: {},
|
|
65
|
-
date: {},
|
|
66
|
-
time: {},
|
|
67
|
-
close: {
|
|
68
|
-
cls: 'btn-sm btn-default lf-dtmpk-close'
|
|
69
|
-
},
|
|
70
|
-
cancel: {}
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
// Close the uib bootstrap datetime picker if the input field loses focus -
|
|
74
|
-
// if the input loses focus due to clicking on the calendar icon, it will open fine.
|
|
75
|
-
// not intended to use on other fields but having the eleName param make it useful for troubleshooting
|
|
76
|
-
$scope.uibDtmPickerOnBlur = function(eleName) {
|
|
77
|
-
if(eleName === 'input' && this.isOpen) {
|
|
78
|
-
setTimeout(function(){
|
|
79
|
-
document.getElementsByClassName('lf-dtmpk-close')[0].click()
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Check the current view's width
|
|
86
|
-
*/
|
|
87
|
-
$scope.checkViewWidth = function() {
|
|
88
|
-
// the $element is where the controller is set on
|
|
89
|
-
var width = $element.width(); //$window.innerWidth;
|
|
90
|
-
$scope._viewMode = "";
|
|
91
|
-
$scope._inputFieldWidth = "";
|
|
92
|
-
|
|
93
|
-
// small screen
|
|
94
|
-
if (width <= 400) //480
|
|
95
|
-
$scope._viewMode = "sm";
|
|
96
|
-
// medium screen
|
|
97
|
-
else if (width <= 600) //800
|
|
98
|
-
$scope._viewMode = "md";
|
|
99
|
-
// large screen
|
|
100
|
-
else {
|
|
101
|
-
$scope._viewMode = "lg";
|
|
102
|
-
}
|
|
103
|
-
$scope._inputFieldWidth = {'width': width / 2};
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
// initialize an element resize detector
|
|
108
|
-
var erd = LForms._elementResizeDetectorMaker({
|
|
109
|
-
strategy: "scroll" //<- For ultra performance.
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
// check the width when the containing div changes its size
|
|
114
|
-
erd.listenTo($element, function(element) {
|
|
115
|
-
$scope.$apply($scope.checkViewWidth());
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
// initial values
|
|
120
|
-
$scope.checkViewWidth();
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* get the CSS class the form's view mode
|
|
125
|
-
* @returns {string}
|
|
126
|
-
*/
|
|
127
|
-
$scope.getViewModeClass = function() {
|
|
128
|
-
var viewModeClass = "";
|
|
129
|
-
if ($scope.lfData) {
|
|
130
|
-
var viewMode = $scope.lfData.templateOptions.viewMode;
|
|
131
|
-
// responsive to the screen/container's size
|
|
132
|
-
if (! viewMode || viewMode === "auto") {
|
|
133
|
-
viewMode = $scope._viewMode;
|
|
134
|
-
}
|
|
135
|
-
switch (viewMode) {
|
|
136
|
-
// fixed to be the large layout
|
|
137
|
-
case "lg":
|
|
138
|
-
viewModeClass = "lf-view-lg";
|
|
139
|
-
break;
|
|
140
|
-
// fixed to be the large layout
|
|
141
|
-
case "md":
|
|
142
|
-
viewModeClass = "lf-view-md";
|
|
143
|
-
break;
|
|
144
|
-
// fixed to be the large layout
|
|
145
|
-
case "sm":
|
|
146
|
-
viewModeClass = "lf-view-sm";
|
|
147
|
-
break;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
return viewModeClass;
|
|
152
|
-
};
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* get the CSS class for an item's view mode
|
|
157
|
-
* @param item a form item
|
|
158
|
-
* @returns {string}
|
|
159
|
-
*/
|
|
160
|
-
$scope.getItemViewModeClass = function(item) {
|
|
161
|
-
var viewMode;
|
|
162
|
-
var viewModeClass = "";
|
|
163
|
-
if ($scope.lfData) {
|
|
164
|
-
// if viewMode is specified on the item
|
|
165
|
-
if (item.displayControl && item.displayControl.viewMode) {
|
|
166
|
-
viewMode = item.displayControl.viewMode;
|
|
167
|
-
}
|
|
168
|
-
// otherwise use the default viewMode of the form
|
|
169
|
-
else {
|
|
170
|
-
viewMode = $scope.lfData.templateOptions.viewMode;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// responsive to the screen/container's size
|
|
174
|
-
if (!viewMode || viewMode === "auto") {
|
|
175
|
-
viewMode = $scope._viewMode;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
switch (viewMode) {
|
|
179
|
-
// fixed to be the large layout
|
|
180
|
-
case "lg":
|
|
181
|
-
viewModeClass = "lf-item-view-lg";
|
|
182
|
-
break;
|
|
183
|
-
// fixed to be the large layout
|
|
184
|
-
case "md":
|
|
185
|
-
viewModeClass = "lf-item-view-md";
|
|
186
|
-
break;
|
|
187
|
-
// fixed to be the large layout
|
|
188
|
-
case "sm":
|
|
189
|
-
viewModeClass = "lf-item-view-sm";
|
|
190
|
-
break;
|
|
191
|
-
default:
|
|
192
|
-
viewModeClass = "lf-item-view-lg";
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
return viewModeClass;
|
|
197
|
-
};
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* Set the active row in table
|
|
202
|
-
* @param index index of an item in the lforms form items array
|
|
203
|
-
*/
|
|
204
|
-
$scope.setActiveRow = function(item) {
|
|
205
|
-
$scope.lfData.setActiveRow(item);
|
|
206
|
-
};
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* Get the inline width for the input and unit part of a form item
|
|
211
|
-
* @item a form item
|
|
212
|
-
* @returns {*}
|
|
213
|
-
*/
|
|
214
|
-
$scope.getFieldWidth = function(item) {
|
|
215
|
-
return $scope.getItemViewModeClass(item) === 'lf-item-view-lg' ? $scope._inputFieldWidth : null;
|
|
216
|
-
};
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* Set the time that a validation message shows for the first time.
|
|
221
|
-
* @param ms time in ms
|
|
222
|
-
*/
|
|
223
|
-
$scope.setValidationInitialShowTime = function(ms) {
|
|
224
|
-
$scope.validationInitialShowTime = ms;
|
|
225
|
-
};
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
/**
|
|
229
|
-
* Set up a timer to make validation messages disappear in 2 seconds when the input field loses focus
|
|
230
|
-
* @param item the item which onBlur event happens on its input field
|
|
231
|
-
*/
|
|
232
|
-
$scope.activeRowOnBlur = function(item) {
|
|
233
|
-
|
|
234
|
-
// the first visit to the field (and leaving the field), show validation messages for a certain time
|
|
235
|
-
if (!item._visitedBefore) {
|
|
236
|
-
item._showValidation = true;
|
|
237
|
-
|
|
238
|
-
// use $interval instead of $timeout so that protractor will not wait on $timeout
|
|
239
|
-
var intervalCanceller = $interval(function() {
|
|
240
|
-
// not to show validation messages after 2 seconds
|
|
241
|
-
item._showValidation = false;
|
|
242
|
-
item._visitedBefore = true;
|
|
243
|
-
$interval.cancel(intervalCanceller);
|
|
244
|
-
}, $scope.validationInitialShowTime);
|
|
245
|
-
}
|
|
246
|
-
// the following visits (and leaving the field), not to show validation messages
|
|
247
|
-
else {
|
|
248
|
-
item._showValidation = false;
|
|
249
|
-
}
|
|
250
|
-
};
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
/**
|
|
254
|
-
* Get the css class for the active row
|
|
255
|
-
* @param item an item
|
|
256
|
-
* @returns {string}
|
|
257
|
-
*/
|
|
258
|
-
$scope.getActiveRowClass = function(item) {
|
|
259
|
-
return $scope.lfData.getActiveRowClass(item);
|
|
260
|
-
};
|
|
261
|
-
|
|
262
|
-
/**
|
|
263
|
-
* Reset the lfData
|
|
264
|
-
*/
|
|
265
|
-
$scope.setFormData = function(formData) {
|
|
266
|
-
$scope.lfData = formData;
|
|
267
|
-
};
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
* Check if the form is finished
|
|
271
|
-
* @returns {boolean|*|*}
|
|
272
|
-
*/
|
|
273
|
-
$scope.isFormDone = function() {
|
|
274
|
-
return $scope.lfData.isFormDone();
|
|
275
|
-
};
|
|
276
|
-
|
|
277
|
-
/**
|
|
278
|
-
* Check if there's a unit list
|
|
279
|
-
* @param item an item in the lforms form items array
|
|
280
|
-
* @returns {boolean}
|
|
281
|
-
*/
|
|
282
|
-
$scope.checkUnits = function(item) {
|
|
283
|
-
return !!(item._unitAutocompOptions ||
|
|
284
|
-
(item._unitReadonly && item.unit && item.unit._displayUnit));
|
|
285
|
-
};
|
|
286
|
-
|
|
287
|
-
/**
|
|
288
|
-
* Get an item's skip logic status
|
|
289
|
-
* @param item an item
|
|
290
|
-
* @returns {*|string}
|
|
291
|
-
*/
|
|
292
|
-
$scope.getSkipLogicClass = function(item) {
|
|
293
|
-
return $scope.lfData.getSkipLogicClass(item);
|
|
294
|
-
};
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
/**
|
|
298
|
-
* Get the CSS styles on an item in formHeaderItems
|
|
299
|
-
* @param col an item in formHeaderItems
|
|
300
|
-
* @returns {{}} CSS style object
|
|
301
|
-
*/
|
|
302
|
-
$scope.getHeaderItemStyle = function(col) {
|
|
303
|
-
var ret = {};
|
|
304
|
-
if (col.displayControl &&
|
|
305
|
-
angular.isArray(col.displayControl.colCSS)) {
|
|
306
|
-
// only when the view mode is lg, i.e. the items are listed like table columns
|
|
307
|
-
var viewModeClass = $scope.getViewModeClass();
|
|
308
|
-
if (viewModeClass === 'lf-view-lg') {
|
|
309
|
-
var colCSS = col.displayControl.colCSS;
|
|
310
|
-
for (var i = 0, iLen = colCSS.length; i < iLen; i++) {
|
|
311
|
-
var css = colCSS[i];
|
|
312
|
-
ret[css.name] = css.value;
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
return ret;
|
|
317
|
-
};
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
/**
|
|
321
|
-
* Get the CSS styles on a table column
|
|
322
|
-
* @param col a column in a HTML table
|
|
323
|
-
* @returns {{}} CSS style object
|
|
324
|
-
*/
|
|
325
|
-
$scope.getTableColumnStyle = function(col) {
|
|
326
|
-
var ret = {};
|
|
327
|
-
if (col.displayControl && angular.isArray(col.displayControl.colCSS)) {
|
|
328
|
-
var colCSS = col.displayControl.colCSS;
|
|
329
|
-
for (var i= 0, iLen= colCSS.length; i<iLen; i++) {
|
|
330
|
-
var css = colCSS[i];
|
|
331
|
-
ret[ css.name ] = css.value;
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
return ret;
|
|
335
|
-
};
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
/**
|
|
339
|
-
* Get the CSS styles on an item itself
|
|
340
|
-
* @param item an item in a form
|
|
341
|
-
* @returns {{}} CSS style object
|
|
342
|
-
*/
|
|
343
|
-
$scope.getItemStyle = function(item) {
|
|
344
|
-
var ret = {};
|
|
345
|
-
if (item.displayControl && angular.isArray(item.displayControl.css)) {
|
|
346
|
-
for (var i= 0, iLen= item.displayControl.css.length; i<iLen; i++) {
|
|
347
|
-
var css = item.displayControl.css[i];
|
|
348
|
-
ret[ css.name ] = css.value;
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
return ret;
|
|
352
|
-
};
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
/**
|
|
356
|
-
* Check an item's skip logic status to decide if the item should be shown
|
|
357
|
-
* @param item an item
|
|
358
|
-
* @returns {boolean}
|
|
359
|
-
*/
|
|
360
|
-
$scope.targetShown = function(item) {
|
|
361
|
-
return item._enableWhenExpVal !== false &&
|
|
362
|
-
$scope.lfData.getSkipLogicClass(item) !== 'target-disabled';
|
|
363
|
-
};
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
/**
|
|
367
|
-
* Hide/show the form option panel
|
|
368
|
-
*/
|
|
369
|
-
$scope.hideShowFormOptionPanel = function() {
|
|
370
|
-
$scope.lfData.templateOptions.showFormOptionPanel = !$scope.lfData.templateOptions.showFormOptionPanel;
|
|
371
|
-
};
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
/**
|
|
375
|
-
* Check if the button for item option panel should be shown
|
|
376
|
-
* @param item a form item
|
|
377
|
-
*/
|
|
378
|
-
$scope.isItemOptionPanelButtonShown = function(item) {
|
|
379
|
-
var buttonShown = $scope.lfData.templateOptions.showItemOptionPanelButton &&
|
|
380
|
-
(item.dataType === "SECTION" || item.answers && (item.dataType === "CWE" || item.dataType === "CNE" ))
|
|
381
|
-
if (!buttonShown)
|
|
382
|
-
item._showItemOptionPanel = false;
|
|
383
|
-
|
|
384
|
-
return buttonShown;
|
|
385
|
-
};
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
/**
|
|
389
|
-
* Hide/show the item's option panel
|
|
390
|
-
* @param item a form item
|
|
391
|
-
*/
|
|
392
|
-
$scope.hideShowItemOptionPanel = function(item) {
|
|
393
|
-
if ($scope.isItemOptionPanelButtonShown(item)) {
|
|
394
|
-
item._showItemOptionPanel = !item._showItemOptionPanel;
|
|
395
|
-
}
|
|
396
|
-
};
|
|
397
|
-
|
|
398
|
-
/**
|
|
399
|
-
* Check if a particular layout is allowed for a section item
|
|
400
|
-
* @param item a form item
|
|
401
|
-
* @param layout a type of layout. permissible values are 'horizontal' and 'matrix'
|
|
402
|
-
*/
|
|
403
|
-
$scope.isQuestionLayoutAllowed = function(item, layout) {
|
|
404
|
-
|
|
405
|
-
var allowed = false;
|
|
406
|
-
if (layout === 'matrix' || layout === 'horizontal') {
|
|
407
|
-
allowed = true;
|
|
408
|
-
//for both horizontal and matrix
|
|
409
|
-
//the item has children but no grand children
|
|
410
|
-
if (item.dataType === "SECTION" && item.items && item.items.length>0) {
|
|
411
|
-
var firstItemAnswers = item.items[0].answers;
|
|
412
|
-
var firstItemDataType = item.items[0].dataType;
|
|
413
|
-
var firstItemAnswerCardinality = item.items[0].answerCardinality;
|
|
414
|
-
for (var i = 0, iLen = item.items.length; i<iLen; i++) {
|
|
415
|
-
var subItem = item.items[i];
|
|
416
|
-
if (subItem.dataType === "SECTION" || subItem.dataType === "TITLE" || subItem.items && subItem.items.length > 0) {
|
|
417
|
-
allowed = false;
|
|
418
|
-
break;
|
|
419
|
-
}
|
|
420
|
-
// addition requirement for matrix layout: all answers are same
|
|
421
|
-
if (layout === "matrix") {
|
|
422
|
-
if (subItem.dataType !== "CWE" && subItem.dataType !== "CNE") {
|
|
423
|
-
allowed = false;
|
|
424
|
-
break;
|
|
425
|
-
}
|
|
426
|
-
else if (i>0 && firstItemDataType !== item.items[i].dataType ||
|
|
427
|
-
!angular.equals(firstItemAnswerCardinality, subItem.answerCardinality) ||
|
|
428
|
-
!angular.equals(firstItemAnswers, subItem.answers)) {
|
|
429
|
-
allowed = false;
|
|
430
|
-
break;
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
else {
|
|
436
|
-
allowed = false;
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
return allowed;
|
|
441
|
-
};
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
/**
|
|
445
|
-
* Check the display type of the coding instructions
|
|
446
|
-
* @param item an item in the lforms form items array
|
|
447
|
-
* @returns {string}
|
|
448
|
-
*/
|
|
449
|
-
$scope.getCodingInstructionsDisplayType = function(item) {
|
|
450
|
-
var ret ='';
|
|
451
|
-
if (item.codingInstructions && item.codingInstructions.length > 0) {
|
|
452
|
-
var position = $scope.lfData.templateOptions.showCodingInstruction ? "inline" : "popover";
|
|
453
|
-
if ($scope.lfData.templateOptions.allowHTMLInInstructions && item.codingInstructionsFormat === "html") {
|
|
454
|
-
var format = "html";
|
|
455
|
-
}
|
|
456
|
-
else {
|
|
457
|
-
format = "escaped";
|
|
458
|
-
}
|
|
459
|
-
ret = position + "-" + format;
|
|
460
|
-
}
|
|
461
|
-
return ret;
|
|
462
|
-
};
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
/**
|
|
466
|
-
* Get coding instructions with assumed safe HTML content
|
|
467
|
-
* @param item an item in the lforms form items array
|
|
468
|
-
* @returns {string}
|
|
469
|
-
*/
|
|
470
|
-
$scope.getTrustedCodingInstructions = function(item) {
|
|
471
|
-
return item.codingInstructions ? $sce.trustAsHtml(item.codingInstructions) : ''
|
|
472
|
-
};
|
|
473
|
-
|
|
474
|
-
/**
|
|
475
|
-
* Get the sequence number for the current repeating item
|
|
476
|
-
* @param item an item in the lforms form items array
|
|
477
|
-
* @returns {string}
|
|
478
|
-
*/
|
|
479
|
-
$scope.getRepeatingSN = function(item) {
|
|
480
|
-
var ret = '';
|
|
481
|
-
if (item._questionRepeatable) {
|
|
482
|
-
var sn = item._idPath.slice(1);
|
|
483
|
-
ret = sn.replace(/\//g,'.');
|
|
484
|
-
}
|
|
485
|
-
return ret;
|
|
486
|
-
};
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
/**
|
|
490
|
-
* Watch on value and unit changes of controlling/source items for skip logic
|
|
491
|
-
* formulas and data controls.
|
|
492
|
-
*/
|
|
493
|
-
$scope.$watch(
|
|
494
|
-
function () {
|
|
495
|
-
var watchedSourceItems = null;
|
|
496
|
-
if ($scope.lfData && $scope.lfData.itemList) {
|
|
497
|
-
watchedSourceItems = [];
|
|
498
|
-
for (var i= 0, iLen=$scope.lfData.itemList.length; i<iLen; i++) {
|
|
499
|
-
var item = $scope.lfData.itemList[i];
|
|
500
|
-
if (item._formulaTargets || item._dataControlTargets || item._skipLogicTargets) {
|
|
501
|
-
watchedSourceItems.push({value: item.value, unit: item.unit,
|
|
502
|
-
id: item._elementId, enableWhenExp: item._enableWhenExpVal});
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
return watchedSourceItems;
|
|
507
|
-
},
|
|
508
|
-
function(newValues, oldValues) {
|
|
509
|
-
var lastUpdated = [];
|
|
510
|
-
if (newValues) {
|
|
511
|
-
// no oldValues, initial loading
|
|
512
|
-
if (!oldValues) {
|
|
513
|
-
for (var i = 0, iLen = newValues.length; i < iLen; i++) {
|
|
514
|
-
lastUpdated.push(newValues[i].id);
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
// adding a new repeating item/section
|
|
518
|
-
else if (oldValues.length < newValues.length) {
|
|
519
|
-
//// find out which one is added, solution 1
|
|
520
|
-
//for (var m= 0, mLen=newValues.length; m<mLen; m++) {
|
|
521
|
-
// var newVal = newValues[m];
|
|
522
|
-
// var isNew = true;
|
|
523
|
-
// for (var n= 0, nLen=oldValues.length; n<nLen; n++) {
|
|
524
|
-
// var oldVal = oldValues[n];
|
|
525
|
-
// if (newVal.id === oldVal.id) {
|
|
526
|
-
// isNew = false;
|
|
527
|
-
// break;
|
|
528
|
-
// }
|
|
529
|
-
// }
|
|
530
|
-
// if (isNew) {
|
|
531
|
-
// lastUpdated.push(newVal.id)
|
|
532
|
-
// }
|
|
533
|
-
//}
|
|
534
|
-
// find out which one is added, solution 2
|
|
535
|
-
// it is always the last one in current design
|
|
536
|
-
lastUpdated.push(newValues[newValues.length-1].id);
|
|
537
|
-
}
|
|
538
|
-
// removing a repeating item/section
|
|
539
|
-
else if (oldValues.length > newValues.length) {
|
|
540
|
-
// rules run at the remove event, TBD
|
|
541
|
-
}
|
|
542
|
-
// data changes only
|
|
543
|
-
else {
|
|
544
|
-
for (var i = 0, iLen = newValues.length; i < iLen; i++) {
|
|
545
|
-
if (!angular.equals(newValues[i], oldValues[i])) {
|
|
546
|
-
lastUpdated.push(newValues[i].id);
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
// do something
|
|
551
|
-
for (var j = 0, jLen = lastUpdated.length; j < jLen; j++) {
|
|
552
|
-
var item = $scope.lfData.itemHash[lastUpdated[j]];
|
|
553
|
-
$scope.updateOnSourceItemChange(item);
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
},
|
|
557
|
-
true
|
|
558
|
-
);
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
/**
|
|
562
|
-
* Watch on form changes (shallow watch on the form object)
|
|
563
|
-
* Disable animation and validations before a form is loaded,
|
|
564
|
-
* then re-enable animation and validations when the form is loaded
|
|
565
|
-
*/
|
|
566
|
-
$scope.$watch("lfData", function() { // or watch on function() {return $scope.lfData;}
|
|
567
|
-
var lfData = $scope.lfData;
|
|
568
|
-
|
|
569
|
-
// disable animation
|
|
570
|
-
lformsConfig.disableAnimate();
|
|
571
|
-
// re-enable animation after the form is loaded
|
|
572
|
-
if (lfData && lfData.templateOptions && lfData.templateOptions.useAnimation) {
|
|
573
|
-
$timeout(function () {
|
|
574
|
-
// the templateOptions might be changed again after the $timeout was set
|
|
575
|
-
if (lfData && lfData.templateOptions && lfData.templateOptions.useAnimation) {
|
|
576
|
-
lformsConfig.enableAnimate();
|
|
577
|
-
}
|
|
578
|
-
}, 1);
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
// Only when lfData changes as a whole do we check whether the new
|
|
582
|
-
// form has FHIRPath or not. This is to avoid the expensive (for
|
|
583
|
-
// large forms) check on the whole of the form data for the need to
|
|
584
|
-
// run FHIRPath.
|
|
585
|
-
if (LForms.FHIR) {
|
|
586
|
-
if (lfData) { // sometimes set to null to clear the page
|
|
587
|
-
if (lfData._hasResponsiveExpr || lfData._hasInitialExpr) {
|
|
588
|
-
// Watch for changes that require FHIRPath to run
|
|
589
|
-
if (lfData._hasResponsiveExpr) {
|
|
590
|
-
if ($scope.unwatchFHIRPath)
|
|
591
|
-
$scope.unwatchFHIRPath();
|
|
592
|
-
$scope.unwatchFHIRPath = $scope.$watch(function() {
|
|
593
|
-
return JSON.stringify(lfData, function(key, val) {
|
|
594
|
-
// In Safari, "key" is a number (not a string) for arrays
|
|
595
|
-
key = ""+key; // a little faster than checking the type
|
|
596
|
-
// Ignore changes to internal variables and $$hashKey
|
|
597
|
-
return (key.indexOf('_') === 0 || key.indexOf('$$')===0) ? undefined : val;
|
|
598
|
-
});
|
|
599
|
-
}, function() {
|
|
600
|
-
if (lfData) {
|
|
601
|
-
lfData._expressionProcessor.runCalculations(false).then(()=>{
|
|
602
|
-
// We might or might not be in a digest cycle.
|
|
603
|
-
$scope.$evalAsync(); // will trigger a digest cycle
|
|
604
|
-
}); // pick up asynchronous model changes
|
|
605
|
-
}
|
|
606
|
-
});
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
if (!lfData._controllerInit && (lfData._hasResponsiveExpr || lfData._hasInitialExpr)) {
|
|
610
|
-
lfData._expressionProcessor.runCalculations(true).then(() => {
|
|
611
|
-
// We might or might not be in a digest cycle.
|
|
612
|
-
$scope.$evalAsync(); // will trigger a digest cycle
|
|
613
|
-
});
|
|
614
|
-
}
|
|
615
|
-
// Angular calls this twice for the same lfData. Set a flag.
|
|
616
|
-
// Note: For some reason the watches still need to be set up both times.
|
|
617
|
-
lfData._controllerInit = true;
|
|
618
|
-
}
|
|
619
|
-
else {
|
|
620
|
-
if ($scope.unwatchFHIRPath) {
|
|
621
|
-
$scope.unwatchFHIRPath(); // stop watching because it is no longer needed
|
|
622
|
-
$scope.unwatchFHIRPath = null;
|
|
623
|
-
}
|
|
624
|
-
if ($scope.unwatchAsync) {
|
|
625
|
-
$scope.unwatchAsync();
|
|
626
|
-
$scope.unwatchAsync = null;
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
});
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
/**
|
|
634
|
-
* Check skip logic, formulas and data controls when the source item changes.
|
|
635
|
-
* @param item the controlling/source item
|
|
636
|
-
*/
|
|
637
|
-
$scope.updateOnSourceItemChange = function(item) {
|
|
638
|
-
var widgetData = $scope.lfData;
|
|
639
|
-
if (widgetData) {
|
|
640
|
-
widgetData.updateOnSourceItemChange(item);
|
|
641
|
-
$scope.sendActionsToScreenReader();
|
|
642
|
-
}
|
|
643
|
-
};
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
/**
|
|
647
|
-
* Adjust the height of a textarea
|
|
648
|
-
* @param e a textarea DOM element or a ID of a textarea element
|
|
649
|
-
*/
|
|
650
|
-
$scope.autoExpand = function(e) {
|
|
651
|
-
var element = typeof e === 'object' ? e.target : document.getElementById(e);
|
|
652
|
-
var scrollHeight = element.scrollHeight +2;
|
|
653
|
-
element.style.height = scrollHeight + "px";
|
|
654
|
-
};
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
/**
|
|
658
|
-
* Get total number of questions on the form, not including section headers or titles
|
|
659
|
-
* @returns {number}
|
|
660
|
-
*/
|
|
661
|
-
$scope.getNumberOfQuestions = function() {
|
|
662
|
-
var ret = 0;
|
|
663
|
-
var widgetData = $scope.lfData;
|
|
664
|
-
if (widgetData && widgetData.itemList) {
|
|
665
|
-
for (var i = 0, iLen = widgetData.itemList.length; i < iLen; i++) {
|
|
666
|
-
if (!widgetData.itemList[i].header)
|
|
667
|
-
ret++;
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
return ret;
|
|
671
|
-
};
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
/**
|
|
675
|
-
* Get CSS classes for the sibling status (whether it is the first or the last sibling)
|
|
676
|
-
* @param item a form item
|
|
677
|
-
* @returns {string}
|
|
678
|
-
*/
|
|
679
|
-
$scope.getSiblingStatus = function(item) {
|
|
680
|
-
var status = "";
|
|
681
|
-
if (item._lastSibling)
|
|
682
|
-
status += 'lf-last-item';
|
|
683
|
-
if (item._firstSibling)
|
|
684
|
-
status += ' lf-first-item'
|
|
685
|
-
return status;
|
|
686
|
-
};
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
/**
|
|
690
|
-
* Get the indentation style of the form
|
|
691
|
-
* @returns {string}
|
|
692
|
-
*/
|
|
693
|
-
$scope.getIndentationStyle = function () {
|
|
694
|
-
return $scope.lfData.templateOptions.useTreeLineStyle ? "lf-indentation-tree" : "lf-indentation-bar";
|
|
695
|
-
};
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
/**
|
|
699
|
-
* Get the CSS class on each item row
|
|
700
|
-
* @param item an item in the lforms form items array
|
|
701
|
-
* @returns {string}
|
|
702
|
-
*/
|
|
703
|
-
$scope.getRowClass = function(item) {
|
|
704
|
-
var eleClass = 'level' + item._displayLevel;
|
|
705
|
-
eleClass += ' lf-datatype-' + item.dataType;
|
|
706
|
-
if (item._answerRequired) {
|
|
707
|
-
eleClass += ' lf-answer-required';
|
|
708
|
-
}
|
|
709
|
-
if (item.header) {
|
|
710
|
-
eleClass += ' lf-section-header';
|
|
711
|
-
}
|
|
712
|
-
else {
|
|
713
|
-
eleClass += ' lf-question';
|
|
714
|
-
}
|
|
715
|
-
if (!item.question || item.question.length === 0) {
|
|
716
|
-
eleClass += ' lf-empty-question';
|
|
717
|
-
}
|
|
718
|
-
if (item._visitedBefore) {
|
|
719
|
-
eleClass += ' lf-visited-before';
|
|
720
|
-
}
|
|
721
|
-
if (item._showValidation) {
|
|
722
|
-
eleClass += ' lf-show-validation';
|
|
723
|
-
}
|
|
724
|
-
if (item._isHiddenFromView) {
|
|
725
|
-
eleClass += ' lf-hidden-from-view';
|
|
726
|
-
}
|
|
727
|
-
if (item.dataType === 'TITLE') {
|
|
728
|
-
eleClass += ' lf-title-row';
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
return eleClass;
|
|
732
|
-
};
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
/**
|
|
736
|
-
* Add a repeating item or a repeating group
|
|
737
|
-
* @param item an item in the lforms form items array
|
|
738
|
-
* @param append an optional flag indicate if the new item is added to the end of the repeating group
|
|
739
|
-
*/
|
|
740
|
-
$scope.addOneRepeatingItem = function(item, append) {
|
|
741
|
-
var widgetData = $scope.lfData;
|
|
742
|
-
var anyEmpty = false;
|
|
743
|
-
if ($scope.lfData && !$scope.lfData.templateOptions.allowMultipleEmptyRepeatingItems) {
|
|
744
|
-
anyEmpty = widgetData.areAnyRepeatingItemsEmpty(item);
|
|
745
|
-
if (anyEmpty && item._showUnusedItemWarning) {
|
|
746
|
-
if (!item._unusedItemWarning)
|
|
747
|
-
item._unusedItemWarning = 'Please enter info in the blank "' +
|
|
748
|
-
item._text+'"';
|
|
749
|
-
$scope.sendMsgToScreenReader(item._unusedItemWarning);
|
|
750
|
-
}
|
|
751
|
-
}
|
|
752
|
-
if (!anyEmpty) {
|
|
753
|
-
var newItem = append ? widgetData.appendRepeatingItems(item) : widgetData.addRepeatingItems(item);
|
|
754
|
-
$scope.sendActionsToScreenReader();
|
|
755
|
-
|
|
756
|
-
// broadcast the event
|
|
757
|
-
$scope.$emit(LF_CONSTANTS.EVENT_REPEATING_ITEM_ADDED,
|
|
758
|
-
{
|
|
759
|
-
"event": LF_CONSTANTS.EVENT_REPEATING_ITEM_ADDED,
|
|
760
|
-
"formId": $scope.lfData.code,
|
|
761
|
-
"itemId": newItem._elementId,
|
|
762
|
-
"time": new Date()
|
|
763
|
-
});
|
|
764
|
-
|
|
765
|
-
setTimeout(function() {
|
|
766
|
-
var viewportHeight = window.innerHeight || document.documentElement.clientHeight;
|
|
767
|
-
|
|
768
|
-
var headerItem = jQuery("label[for='" + newItem._elementId + "']")[0];
|
|
769
|
-
var btnDel = document.getElementById("del-" + newItem._elementId);
|
|
770
|
-
// vertical table, find the header item
|
|
771
|
-
if (headerItem) {
|
|
772
|
-
var anchorItem = headerItem;
|
|
773
|
-
}
|
|
774
|
-
// horizontal table, find the '-' button
|
|
775
|
-
else if (btnDel) {
|
|
776
|
-
var anchorItem = btnDel;
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
if (anchorItem) {
|
|
780
|
-
var anchorPosition = anchorItem.getBoundingClientRect();
|
|
781
|
-
// scroll down to show about 2 rows of the newly added section
|
|
782
|
-
// if the new header item is close enough to the bottom so that the first 2 questions are not visible
|
|
783
|
-
if (anchorPosition && anchorPosition.bottom > viewportHeight - 70) {
|
|
784
|
-
smoothScroll(anchorItem, {
|
|
785
|
-
duration: 500,
|
|
786
|
-
easing: 'easeInQuad',
|
|
787
|
-
offset: viewportHeight - 105
|
|
788
|
-
});
|
|
789
|
-
}
|
|
790
|
-
// move the focus to the '-' button of the newly added item/section
|
|
791
|
-
// a table from the '-' button moves the focus to the next input field
|
|
792
|
-
if (btnDel)
|
|
793
|
-
btnDel.focus();
|
|
794
|
-
}
|
|
795
|
-
}, 1);
|
|
796
|
-
|
|
797
|
-
}
|
|
798
|
-
};
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
/**
|
|
802
|
-
* Unset the flag to hide the warning about unused repeating items
|
|
803
|
-
* @param item a repeating item
|
|
804
|
-
*/
|
|
805
|
-
$scope.hideUnusedItemWarning = function(item) {
|
|
806
|
-
if ($scope.lfData && !$scope.lfData.templateOptions.allowMultipleEmptyRepeatingItems) {
|
|
807
|
-
item._showUnusedItemWarning = false;
|
|
808
|
-
}
|
|
809
|
-
};
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
/**
|
|
813
|
-
* Writes a single message to the reader_log element on the page
|
|
814
|
-
* so that screen readers can read it.
|
|
815
|
-
* @param msg the message to be read
|
|
816
|
-
*/
|
|
817
|
-
$scope.sendMsgToScreenReader = function(msg) {
|
|
818
|
-
LForms.Def.Autocompleter.screenReaderLog(msg);
|
|
819
|
-
};
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
/**
|
|
823
|
-
* Write action logs from the lforms to reader_log element on the page
|
|
824
|
-
* so that screen readers can read.
|
|
825
|
-
*/
|
|
826
|
-
$scope.sendActionsToScreenReader = function() {
|
|
827
|
-
var widgetData = $scope.lfData;
|
|
828
|
-
if (widgetData._actionLogs.length > 0) {
|
|
829
|
-
widgetData._actionLogs.forEach(function(log) {
|
|
830
|
-
LForms.Def.Autocompleter.screenReaderLog(log);
|
|
831
|
-
});
|
|
832
|
-
// clean up logs
|
|
833
|
-
widgetData._actionLogs = [];
|
|
834
|
-
}
|
|
835
|
-
};
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
/**
|
|
839
|
-
* Remove one repeating item in a group
|
|
840
|
-
* @param item an item in the lforms form items array
|
|
841
|
-
*/
|
|
842
|
-
$scope.removeOneRepeatingItem = function(item) {
|
|
843
|
-
|
|
844
|
-
var widgetData = $scope.lfData;
|
|
845
|
-
var nextItem = widgetData.getNextRepeatingItem(item);
|
|
846
|
-
|
|
847
|
-
var btnId = '';
|
|
848
|
-
// move the focus to the next '-' button if there's one displayed
|
|
849
|
-
// ('-' buttons are shown only when there are two repeating items shown).
|
|
850
|
-
if (nextItem) {
|
|
851
|
-
if (widgetData.getRepeatingItemCount(item) === 2) {
|
|
852
|
-
btnId = 'add-' + nextItem._elementId;
|
|
853
|
-
}
|
|
854
|
-
else {
|
|
855
|
-
btnId = 'del-' + nextItem._elementId;
|
|
856
|
-
}
|
|
857
|
-
}
|
|
858
|
-
// otherwise move the focus to the add button of the previous item
|
|
859
|
-
else {
|
|
860
|
-
var prevItem = widgetData.getPrevRepeatingItem(item);
|
|
861
|
-
if (prevItem) {
|
|
862
|
-
btnId = 'add-' + prevItem._elementId;
|
|
863
|
-
}
|
|
864
|
-
}
|
|
865
|
-
|
|
866
|
-
// remove the items
|
|
867
|
-
$scope.lfData.removeRepeatingItems(item);
|
|
868
|
-
|
|
869
|
-
$scope.sendActionsToScreenReader();
|
|
870
|
-
|
|
871
|
-
// broadcast the event
|
|
872
|
-
$scope.$emit(LF_CONSTANTS.EVENT_REPEATING_ITEM_DELETED,
|
|
873
|
-
{
|
|
874
|
-
"event": LF_CONSTANTS.EVENT_REPEATING_ITEM_DELETED,
|
|
875
|
-
"formId": $scope.lfData.code,
|
|
876
|
-
"itemId": item._elementId,
|
|
877
|
-
"time": new Date()
|
|
878
|
-
});
|
|
879
|
-
|
|
880
|
-
// set the focus
|
|
881
|
-
setTimeout(function() {
|
|
882
|
-
var btn = document.getElementById(btnId);
|
|
883
|
-
if (btn) btn.focus();
|
|
884
|
-
}, 1);
|
|
885
|
-
};
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
/**
|
|
889
|
-
* Check if there's only one repeating item in a group
|
|
890
|
-
* (so that the 'remove' button won't show on this item)
|
|
891
|
-
* @param item an item in the lforms form items array
|
|
892
|
-
* @returns {boolean}
|
|
893
|
-
*/
|
|
894
|
-
$scope.hasOneRepeatingItem =function(item) {
|
|
895
|
-
var recCount = $scope.lfData.getRepeatingItemCount(item);
|
|
896
|
-
return recCount > 1 ? false : true;
|
|
897
|
-
};
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
/**
|
|
901
|
-
* Check if the current horizontal table has one row only
|
|
902
|
-
* @param item an item in the lforms form items array
|
|
903
|
-
* @returns {boolean}
|
|
904
|
-
*/
|
|
905
|
-
$scope.hasOneRepeatingRow = function(item) {
|
|
906
|
-
var ret = false;
|
|
907
|
-
var tableInfo = $scope.lfData._horizontalTableInfo[item._codePath + item._parentIdPath_];
|
|
908
|
-
if (tableInfo && tableInfo.tableRows && tableInfo.tableRows.length === 1) {
|
|
909
|
-
ret = true;
|
|
910
|
-
}
|
|
911
|
-
return ret;
|
|
912
|
-
};
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
/**
|
|
916
|
-
* Check if the question needs an extra input
|
|
917
|
-
* @param item an item in the lforms form items array
|
|
918
|
-
* @returns {*}
|
|
919
|
-
*/
|
|
920
|
-
$scope.needExtra = function(item) {
|
|
921
|
-
var widgetData = $scope.lfData;
|
|
922
|
-
var extra = widgetData.needExtra(item);
|
|
923
|
-
return extra;
|
|
924
|
-
};
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
/**
|
|
928
|
-
* Get user input data from the form, with or without form definition data.
|
|
929
|
-
* @param noFormDefData optional, to not include form definition data, the default is false.
|
|
930
|
-
* @param noEmptyValue optional, to remove items that have an empty value, the default is false.
|
|
931
|
-
* @param noDisabledItem optional, to remove items that are disabled by skip logic, the default is false.
|
|
932
|
-
* @returns {{itemsData: (*|Array), templateData: (*|Array)}} form data and template data
|
|
933
|
-
*/
|
|
934
|
-
$scope.getUserData = function(noFormDefData, noEmptyValue, noDisabledItem) {
|
|
935
|
-
var formData = $scope.lfData.getUserData(noFormDefData, noEmptyValue, noDisabledItem);
|
|
936
|
-
return formData;
|
|
937
|
-
};
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
/**
|
|
941
|
-
* Get the complete form definition data, including the user input data from the form.
|
|
942
|
-
* The returned data could be fed into a LForms widget directly to render the form.
|
|
943
|
-
* @return {{}} form definition JSON object
|
|
944
|
-
*/
|
|
945
|
-
$scope.getFormData = function() {
|
|
946
|
-
var formData = $scope.lfData.getFormData();
|
|
947
|
-
return formData;
|
|
948
|
-
};
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
// for debug only. to be removed.
|
|
952
|
-
$scope.onclick = function() {
|
|
953
|
-
debugger
|
|
954
|
-
var ele = $element;
|
|
955
|
-
var i = 1;
|
|
956
|
-
};
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
/**
|
|
960
|
-
* Get the display layout for each answer in a RADIO_CHECKBOX layout of an item's answers
|
|
961
|
-
* @param item a form item
|
|
962
|
-
* @returns {string}
|
|
963
|
-
*/
|
|
964
|
-
$scope.getAnswerLayoutColumnClass = function(item) {
|
|
965
|
-
var ret = "";
|
|
966
|
-
if (item && item.displayControl && item.displayControl.answerLayout &&
|
|
967
|
-
item.displayControl.answerLayout.type === "RADIO_CHECKBOX") {
|
|
968
|
-
var colNum = parseInt(item.displayControl.answerLayout.columns);
|
|
969
|
-
if (isNaN(colNum) || colNum >6 || colNum <0 )
|
|
970
|
-
colNum = 0;
|
|
971
|
-
ret = "lf-" + colNum + "-col";
|
|
972
|
-
}
|
|
973
|
-
|
|
974
|
-
return ret;
|
|
975
|
-
};
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
/**
|
|
979
|
-
* Updates the value for an item whose answers are displayed as a list of checkboxes,
|
|
980
|
-
* one of which has just been selected or deselected
|
|
981
|
-
* @param item a form item that has an answer list and supports multiple selections
|
|
982
|
-
* @param answer an answer object in the answer list
|
|
983
|
-
*/
|
|
984
|
-
$scope.updateCheckboxList = function(item, answer) {
|
|
985
|
-
if (item.value && angular.isArray(item.value)) {
|
|
986
|
-
var index, selected = false;
|
|
987
|
-
for(var i= 0,iLen=item.value.length; i<iLen; i++) {
|
|
988
|
-
if (angular.equals(item.value[i],answer)) {
|
|
989
|
-
selected = true;
|
|
990
|
-
index = i;
|
|
991
|
-
break;
|
|
992
|
-
}
|
|
993
|
-
}
|
|
994
|
-
// if answer is currently selected
|
|
995
|
-
if (selected) {
|
|
996
|
-
// remove the answer from the selected list
|
|
997
|
-
item.value.splice(index, 1);
|
|
998
|
-
}
|
|
999
|
-
// if answer is not currently selected
|
|
1000
|
-
else {
|
|
1001
|
-
// add the answer to the selected list
|
|
1002
|
-
item.value.push(answer);
|
|
1003
|
-
}
|
|
1004
|
-
}
|
|
1005
|
-
// the value is not accessed before
|
|
1006
|
-
else {
|
|
1007
|
-
// add the answer to the selected list
|
|
1008
|
-
item.value = [answer];
|
|
1009
|
-
}
|
|
1010
|
-
};
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
/**
|
|
1014
|
-
* Updates the value for an item with the user typed data.
|
|
1015
|
-
* The item's answers are displayed as a list of checkboxes, and users have an option to type their own answer.
|
|
1016
|
-
* Update the item.value based on selection of extra data input by users
|
|
1017
|
-
* Note: In the checkbox display layout only one "OTHER" value is allowed, while in autocomplete multiple
|
|
1018
|
-
* "OTHER" values are allowed.
|
|
1019
|
-
* @param item a form item that has an answer list and supports multiple selections and user typed data.
|
|
1020
|
-
* @param otherValue the user typed string value for the "OTHER" checkbox
|
|
1021
|
-
*/
|
|
1022
|
-
$scope.updateCheckboxListForOther = function(item, otherValue) {
|
|
1023
|
-
var other = {"text": otherValue, "_notOnList": true};
|
|
1024
|
-
|
|
1025
|
-
// add/update the other value
|
|
1026
|
-
if (item._otherValueChecked) {
|
|
1027
|
-
// the list is not empty
|
|
1028
|
-
if (item.value && angular.isArray(item.value)) {
|
|
1029
|
-
var found = false;
|
|
1030
|
-
for(var i= 0,iLen=item.value.length; i<iLen; i++) {
|
|
1031
|
-
if (item.value[i]._notOnList) {
|
|
1032
|
-
item.value[i] = other;
|
|
1033
|
-
found = true;
|
|
1034
|
-
break;
|
|
1035
|
-
}
|
|
1036
|
-
}
|
|
1037
|
-
// if the other value is not already in the list
|
|
1038
|
-
if (!found) {
|
|
1039
|
-
// add the other value to the list
|
|
1040
|
-
item.value.push(other);
|
|
1041
|
-
}
|
|
1042
|
-
}
|
|
1043
|
-
// the list is empty
|
|
1044
|
-
else {
|
|
1045
|
-
// add the other value to the list
|
|
1046
|
-
item.value = [other];
|
|
1047
|
-
}
|
|
1048
|
-
}
|
|
1049
|
-
// remove other value
|
|
1050
|
-
else {
|
|
1051
|
-
if (item.value && angular.isArray(item.value)) {
|
|
1052
|
-
var index, found = false;
|
|
1053
|
-
for(var i= 0,iLen=item.value.length; i<iLen; i++) {
|
|
1054
|
-
if (item.value[i]._notOnList) {
|
|
1055
|
-
found = true;
|
|
1056
|
-
index = i;
|
|
1057
|
-
break;
|
|
1058
|
-
}
|
|
1059
|
-
}
|
|
1060
|
-
if (found) {
|
|
1061
|
-
item.value.splice(index, 1);
|
|
1062
|
-
}
|
|
1063
|
-
}
|
|
1064
|
-
}
|
|
1065
|
-
};
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
/**
|
|
1069
|
-
*
|
|
1070
|
-
* Update the item.value based on selection of an answer by users
|
|
1071
|
-
* @param item a form item that has an answer list and support single selections
|
|
1072
|
-
*/
|
|
1073
|
-
$scope.updateRadioList = function(item) {
|
|
1074
|
-
item._otherValueChecked = false;
|
|
1075
|
-
};
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
/**
|
|
1079
|
-
* Update the item.value based on selection of extra data input by users
|
|
1080
|
-
* @param item a form item that has an answer list and support single selections
|
|
1081
|
-
* @param otherValue the user typed string value for the "OTHER" radio button
|
|
1082
|
-
*/
|
|
1083
|
-
$scope.updateRadioListForOther = function(item, otherValue) {
|
|
1084
|
-
|
|
1085
|
-
// add/update the other value
|
|
1086
|
-
if (item._otherValueChecked) {
|
|
1087
|
-
item.value = { "text": otherValue, "_notOnList": true};
|
|
1088
|
-
}
|
|
1089
|
-
};
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
/**
|
|
1093
|
-
* Check if a checkbox should be checked based on the value of a form item
|
|
1094
|
-
* @param item a form item
|
|
1095
|
-
* @param answer an answer in the items' answer list
|
|
1096
|
-
* @returns {boolean}
|
|
1097
|
-
*/
|
|
1098
|
-
$scope.checkAnswer = function(item,answer) {
|
|
1099
|
-
var checked = false;
|
|
1100
|
-
if (item && item.value) {
|
|
1101
|
-
if (angular.isArray(item.value)) {
|
|
1102
|
-
for(var i=0, iLen=item.value.length; i<iLen; i++) {
|
|
1103
|
-
var selectedAnswer = item.value[i];
|
|
1104
|
-
if (selectedAnswer.text === answer.text) {
|
|
1105
|
-
checked = true;
|
|
1106
|
-
break;
|
|
1107
|
-
}
|
|
1108
|
-
}
|
|
1109
|
-
}
|
|
1110
|
-
else {
|
|
1111
|
-
if (item.value.text === answer.text) {
|
|
1112
|
-
checked = true;
|
|
1113
|
-
}
|
|
1114
|
-
}
|
|
1115
|
-
}
|
|
1116
|
-
return checked;
|
|
1117
|
-
};
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
/**
|
|
1121
|
-
* Handle navigation keys using TAB/ SHIFT+TAB keys
|
|
1122
|
-
* @param event keypress event
|
|
1123
|
-
*/
|
|
1124
|
-
$scope.handleNavigationKeyEventByTab = function(event) {
|
|
1125
|
-
|
|
1126
|
-
var widgetData = $scope.lfData;
|
|
1127
|
-
if (widgetData.templateOptions.tabOnInputFieldsOnly && event.keyCode === widgetData.Navigation.TAB) {
|
|
1128
|
-
if (event.shiftKey) {
|
|
1129
|
-
var simArrowCode = widgetData.Navigation.ARROW.LEFT;
|
|
1130
|
-
}
|
|
1131
|
-
else {
|
|
1132
|
-
var simArrowCode = widgetData.Navigation.ARROW.RIGHT;
|
|
1133
|
-
}
|
|
1134
|
-
|
|
1135
|
-
var nextId = event.target['id'], nextElement;
|
|
1136
|
-
// find the next element, bypass the invisible elements
|
|
1137
|
-
do {
|
|
1138
|
-
// get the DOM element id of next field
|
|
1139
|
-
nextId = widgetData.Navigation.getNextFieldId(simArrowCode, nextId);
|
|
1140
|
-
// get the next DOM element by ID
|
|
1141
|
-
nextElement = document.getElementById(nextId);
|
|
1142
|
-
} while (nextId && (!nextElement || !jQuery(nextElement).is(":visible")));
|
|
1143
|
-
|
|
1144
|
-
// set the focus
|
|
1145
|
-
var currentElement = event.target;
|
|
1146
|
-
if (nextElement && nextElement.id !== currentElement.id) {
|
|
1147
|
-
event.preventDefault();
|
|
1148
|
-
event.stopPropagation();
|
|
1149
|
-
setTimeout(function() {
|
|
1150
|
-
nextElement.focus();
|
|
1151
|
-
nextElement.select();
|
|
1152
|
-
}, 1);
|
|
1153
|
-
currentElement.blur();
|
|
1154
|
-
}
|
|
1155
|
-
}
|
|
1156
|
-
};
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
/**
|
|
1160
|
-
* Handle navigation keys using CTRL+arrow keys
|
|
1161
|
-
* @param event keypress event
|
|
1162
|
-
*/
|
|
1163
|
-
$scope.handleNavigationKeyEventByArrowKeys = function(event) {
|
|
1164
|
-
|
|
1165
|
-
var widgetData = $scope.lfData;
|
|
1166
|
-
// supported arrow keys
|
|
1167
|
-
var arrow = widgetData.Navigation.ARROW;
|
|
1168
|
-
|
|
1169
|
-
// only when control key is also pressed
|
|
1170
|
-
if (event.ctrlKey &&
|
|
1171
|
-
jQuery.inArray(event.keyCode, [arrow.LEFT, arrow.UP, arrow.RIGHT, arrow.DOWN]) >= 0 ) {
|
|
1172
|
-
|
|
1173
|
-
var nextId = event.target['id'], nextElement;
|
|
1174
|
-
// find the next element, bypass the invisible elements
|
|
1175
|
-
do {
|
|
1176
|
-
// get the DOM element id of next field
|
|
1177
|
-
nextId = widgetData.Navigation.getNextFieldId(event.keyCode, nextId);
|
|
1178
|
-
// get the next DOM element by ID
|
|
1179
|
-
nextElement = document.getElementById(nextId);
|
|
1180
|
-
} while (nextId && (!nextElement || !jQuery(nextElement).is(":visible")));
|
|
1181
|
-
|
|
1182
|
-
// set the focus
|
|
1183
|
-
var currentElement = event.target;
|
|
1184
|
-
if (nextElement && nextElement.id !== currentElement.id) {
|
|
1185
|
-
event.preventDefault();
|
|
1186
|
-
event.stopPropagation();
|
|
1187
|
-
setTimeout(function() {
|
|
1188
|
-
nextElement.focus();
|
|
1189
|
-
}, 1);
|
|
1190
|
-
currentElement.blur();
|
|
1191
|
-
}
|
|
1192
|
-
}
|
|
1193
|
-
};
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
}]);
|