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.
Files changed (116) hide show
  1. package/CHANGELOG.md +261 -0
  2. package/README.md +97 -14
  3. package/changed-features.md +99 -0
  4. package/dist/lforms/LICENSE.md +70 -0
  5. package/dist/lforms/README.md +100 -0
  6. package/dist/lforms/fhir/R4/lformsFHIR.min.js +2 -0
  7. package/dist/lforms/fhir/R4/lformsFHIR.min.js.map +1 -0
  8. package/dist/lforms/fhir/STU3/lformsFHIR.min.js +2 -0
  9. package/dist/lforms/fhir/STU3/lformsFHIR.min.js.map +1 -0
  10. package/dist/lforms/fhir/lformsFHIRAll.min.js +2 -0
  11. package/dist/lforms/fhir/lformsFHIRAll.min.js.map +1 -0
  12. package/dist/lforms/webcomponent/assets/lib/zone.min.js +134 -0
  13. package/dist/lforms/webcomponent/down_arrow_gray_10_10.png +0 -0
  14. package/dist/lforms/webcomponent/favicon.ico +0 -0
  15. package/dist/lforms/webcomponent/index.html +9 -0
  16. package/dist/lforms/webcomponent/lhc-forms.js +2471 -0
  17. package/dist/lforms/webcomponent/magnifying_glass.png +0 -0
  18. package/dist/lforms/webcomponent/main.js +2464 -0
  19. package/dist/lforms/webcomponent/main.js.map +1 -0
  20. package/dist/lforms/webcomponent/polyfills.js +2 -0
  21. package/dist/lforms/webcomponent/polyfills.js.map +1 -0
  22. package/dist/lforms/webcomponent/runtime.js +2 -0
  23. package/dist/lforms/webcomponent/runtime.js.map +1 -0
  24. package/dist/lforms/webcomponent/scripts.js +3 -0
  25. package/dist/lforms/webcomponent/scripts.js.map +1 -0
  26. package/dist/lforms/webcomponent/styles.css +3 -0
  27. package/dist/lforms/webcomponent/styles.css.map +1 -0
  28. package/form_definition.md +382 -0
  29. package/lforms-33.2.0.tgz +0 -0
  30. package/package.json +94 -88
  31. package/r4-support.md +147 -0
  32. package/sdc-support.md +127 -0
  33. package/app/.npmignore +0 -3
  34. package/app/images/blank.gif +0 -0
  35. package/app/images/signal-stop.png +0 -0
  36. package/app/index.html +0 -10
  37. package/app/lforms.js +0 -56
  38. package/app/lforms.tpl.js +0 -964
  39. package/app/scripts/angular-packages.js +0 -11
  40. package/app/scripts/bootstrap-decorators.js +0 -31
  41. package/app/scripts/bower-index.js +0 -14
  42. package/app/scripts/fhir/R4/export.js +0 -33
  43. package/app/scripts/fhir/R4/fhirRequire.js +0 -36
  44. package/app/scripts/fhir/R4/lformsFHIR.js +0 -27260
  45. package/app/scripts/fhir/R4/sdc-export.js +0 -455
  46. package/app/scripts/fhir/R4/sdc-import.js +0 -483
  47. package/app/scripts/fhir/STU3/export.js +0 -27
  48. package/app/scripts/fhir/STU3/fhirRequire.js +0 -36
  49. package/app/scripts/fhir/STU3/lformsFHIR.js +0 -27291
  50. package/app/scripts/fhir/STU3/sdc-export.js +0 -493
  51. package/app/scripts/fhir/STU3/sdc-import.js +0 -494
  52. package/app/scripts/fhir/diagnostic-report.js +0 -707
  53. package/app/scripts/fhir/export-common.js +0 -209
  54. package/app/scripts/fhir/expression-processor.js +0 -670
  55. package/app/scripts/fhir/extensions/rendering-style.js +0 -23
  56. package/app/scripts/fhir/fhir-common.js +0 -3
  57. package/app/scripts/fhir/obs-prepop.mjs +0 -97
  58. package/app/scripts/fhir/runtime-common.js +0 -33
  59. package/app/scripts/fhir/sdc-common.js +0 -334
  60. package/app/scripts/fhir/sdc-export-common.js +0 -1046
  61. package/app/scripts/fhir/sdc-import-common.js +0 -1343
  62. package/app/scripts/fhir/versions.js +0 -8
  63. package/app/scripts/index.js +0 -20
  64. package/app/scripts/lforms-config.js +0 -12
  65. package/app/scripts/lforms-constants.js +0 -10
  66. package/app/scripts/lforms-controllers.js +0 -1196
  67. package/app/scripts/lforms-directives.js +0 -424
  68. package/app/scripts/lforms-index.js +0 -20
  69. package/app/scripts/lib/cross_browser.js +0 -21
  70. package/app/scripts/lib/lforms-data.js +0 -3591
  71. package/app/scripts/lib/lforms-hl7.js +0 -525
  72. package/app/scripts/lib/lforms-util.js +0 -1122
  73. package/app/scripts/lib/lforms-validate.js +0 -277
  74. package/app/scripts/lib/polyfill.js +0 -81
  75. package/app/scripts/themeList.js +0 -17
  76. package/app/styles/lforms.css +0 -1219
  77. package/app/styles/redmond-theme/README.txt +0 -2
  78. package/app/styles/redmond-theme/images/ui-bg_glass_75_d0e5f5_1x400.png +0 -0
  79. package/app/styles/redmond-theme/images/ui-bg_glass_85_dfeffc_1x400.png +0 -0
  80. package/app/styles/redmond-theme/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  81. package/app/styles/redmond-theme/images/ui-bg_gloss-wave_55_5c9ccc_500x100.png +0 -0
  82. package/app/styles/redmond-theme/images/ui-bg_inset-hard_100_f5f8f9_1x100.png +0 -0
  83. package/app/styles/redmond-theme/images/ui-bg_inset-hard_100_fcfdfd_1x100.png +0 -0
  84. package/app/styles/redmond-theme/images/ui-icons_217bc0_256x240.png +0 -0
  85. package/app/styles/redmond-theme/images/ui-icons_2e83ff_256x240.png +0 -0
  86. package/app/styles/redmond-theme/images/ui-icons_469bdd_256x240.png +0 -0
  87. package/app/styles/redmond-theme/images/ui-icons_6da8d5_256x240.png +0 -0
  88. package/app/styles/redmond-theme/images/ui-icons_cd0a0a_256x240.png +0 -0
  89. package/app/styles/redmond-theme/images/ui-icons_d8e7f3_256x240.png +0 -0
  90. package/app/styles/redmond-theme/images/ui-icons_f9bd01_256x240.png +0 -0
  91. package/app/styles/redmond-theme/jquery-ui.css +0 -1311
  92. package/app/styles/redmond-theme/jquery-ui.min.css +0 -7
  93. package/app/styles/redmond-theme/theme.css +0 -443
  94. package/app/styles/themes.css +0 -1541
  95. package/app/styles/themes.css.map +0 -9
  96. package/app/styles/themes.scss +0 -411
  97. package/app/version.json +0 -1
  98. package/app/views/partials/attachment.html +0 -38
  99. package/app/views/partials/field-answers.html +0 -67
  100. package/app/views/partials/field-units.html +0 -24
  101. package/app/views/partials/form-controls.html +0 -19
  102. package/app/views/partials/form-header.html +0 -35
  103. package/app/views/partials/form-options.html +0 -50
  104. package/app/views/partials/form-title.html +0 -20
  105. package/app/views/partials/form-view.html +0 -13
  106. package/app/views/partials/item-options.html +0 -48
  107. package/app/views/partials/item.html +0 -126
  108. package/app/views/partials/itemPrefixAndText.html +0 -4
  109. package/app/views/partials/layout-horizontal.html +0 -156
  110. package/app/views/partials/layout-matrix.html +0 -145
  111. package/app/views/partials/popover-content.html +0 -1
  112. package/app/views/partials/repeating-button.html +0 -16
  113. package/app/views/partials/table-item.html +0 -30
  114. package/app/views/partials/template-table.html +0 -69
  115. package/app/views/partials/uib-popover-templates/uib-popover-template.html +0 -13
  116. package/app/views/partials/uib-popover-templates/uib-popover.html +0 -11
@@ -1,1122 +0,0 @@
1
- /**
2
- * LForms Utility tools
3
- */
4
-
5
- var moment = require('moment');
6
- // Acceptable date formats
7
-
8
- // Strict parsing -
9
- var parseDateFormats = [
10
- 'M/D/YYYY',
11
- 'M/D/YY',
12
- 'M/D',
13
- 'M-D-YYYY',
14
- 'M-D-YY',
15
- 'M-D',
16
- 'YYYY',
17
- 'YYYY-M-D',
18
- 'YYYY/M/D',
19
- moment.ISO_8601,
20
- 'M/D/YYYY HH:mm',
21
- 'M/D/YY HH:mm',
22
- 'M/D HH:mm',
23
- 'M-D-YYYY HH:mm',
24
- 'M-D-YY HH:mm',
25
- 'M-D HH:mm',
26
- ];
27
-
28
-
29
- var LForms = require('../../lforms');
30
-
31
- LForms.Util = {
32
- /**
33
- * Adds an LForms form to the page.
34
- * @param formDataDef A form definiton (either JSON or a parsed object). Also,
35
- * for backward compatibility, this can be the name of a global-scope variable
36
- * (on "window") containing that form definition object. A FHIR Questionnaire can be also be
37
- * used as a form definition.
38
- * @param formContainer The ID of a DOM element to contain the form, or the
39
- * element itself. The contents of this element will be replaced by the form.
40
- * This element should be outside the scope of any existing AngularJS app on
41
- * the page.
42
- * @param {Object} [options] A hash of options
43
- * @param {boolean} [options.prepopulate] Set to true if you want FHIR prepopulation to happen (if
44
- * the form was an imported FHIR Questionnaire).
45
- * @param {string} [options.fhirVersion] Optional, version of FHIR used if formDataDef is a FHIR
46
- * Questionnaire. options are: `R4` or `STU3`. If not provided an attempt will be made to determine
47
- * the version from the Questionnaire content.
48
- * @return a Promise that will resolve after any needed external FHIR
49
- * resources have been loaded (if the form was imported from a FHIR
50
- * Questionnaire).
51
- */
52
- addFormToPage: function(formDataDef, formContainer, options) {
53
- var formContainer = typeof formContainer === 'string' ?
54
- $('#'+formContainer) : $(formContainer);
55
- if (typeof formDataDef === 'string') {
56
- if (formDataDef.indexOf('{') >= 0) // test for JSON
57
- formDataDef = JSON.parse(formDataDef);
58
- else // backward compatibility
59
- formDataDef = window[formDataDef];
60
- }
61
-
62
- // If resourceType is specified assume formDataDef is FHIR
63
- var resourceType = formDataDef.resourceType;
64
- var fhirVersion = options && options.fhirVersion;
65
-
66
- if (resourceType === 'Questionnaire') {
67
- formDataDef = this.convertFHIRQuestionnaireToLForms(formDataDef, fhirVersion);
68
- } else if (resourceType) {
69
- throw new Error('Only Questionnaire FHIR content is supported in addFormToPage.')
70
- }
71
-
72
- if (!this.pageFormID_)
73
- this.pageFormID_ = 0;
74
- var appName = 'LFormsApp' + ++this.pageFormID_;
75
- var controller = 'LFormsAppController'+ this.pageFormID_;
76
- if (!LForms.addedFormDefs)
77
- LForms.addedFormDefs = [];
78
- var formIndex = LForms.addedFormDefs.length;
79
- LForms.addedFormDefs.push(formDataDef);
80
- var prepop = options && options.prepopulate===true;
81
- formContainer.html(
82
- '<div ng-controller="'+controller+'">'+
83
- '<lforms lf-data="myFormData"></lforms>'+
84
- '</div>'
85
- );
86
- var rtnPromise = new Promise(function(resolve, reject) {
87
- angular.module(appName, ["lformsWidget"])
88
- .controller(controller, ["$scope", function ($scope) {
89
- try {
90
- var myFormData = new LForms.LFormsData(LForms.addedFormDefs[formIndex]);
91
- if (LForms.fhirContext) {
92
- myFormData.loadFHIRResources(prepop).then(function() {
93
- $scope.$apply(function() {
94
- $scope.myFormData = myFormData;
95
- resolve();
96
- })
97
- }, function failed(e) {
98
- reject(e);
99
- });
100
- }
101
- else {
102
- $scope.myFormData = myFormData;
103
- resolve();
104
- }
105
- }
106
- catch(e) {
107
- reject(e);
108
- }
109
- }]);
110
- }).catch(function(e) {
111
- var errMsg = (e instanceof Error) ? e.message : e.toString();
112
- formContainer.html('<div id=lformsErrors style="color: red"><b>Unable to '+
113
- 'display the form, due to the following error:</b><p id=lformsErrorContent></p>');
114
- $('#lformsErrorContent').text(errMsg);
115
- throw e;
116
- });
117
-
118
- // Bootstrap the element if needed
119
- // Following http://stackoverflow.com/a/34501500/360782
120
- var isInitialized = formContainer.injector && formContainer.injector();
121
- if (!isInitialized)
122
- angular.bootstrap(formContainer.children(':first'), [appName]);
123
-
124
- return rtnPromise;
125
- },
126
-
127
-
128
- /**
129
- * Get user input data from the form, with or without form definition data.
130
- * @param element the containing HTML element that includes the LForm's rendered form.
131
- * @param noFormDefData optional, to include form definition data, the default is false.
132
- * @param noEmptyValue optional, to remove items that have an empty value, the default is false.
133
- * @param noDisabledItem optional, to remove items that are disabled by skip logic, the default is false.
134
- * @returns {{itemsData: (*|Array), templateData: (*|Array)}} form data and template data
135
- */
136
- getUserData: function(element, noFormDefData, noEmptyValue, noDisabledItem) {
137
- var formObj = this._getFormObjectInScope(element);
138
- return formObj ? formObj.getUserData(noFormDefData, noEmptyValue, noDisabledItem) : null;
139
- },
140
-
141
-
142
- /**
143
- * Get the complete form definition data, including the user input data from the form.
144
- * The returned data could be fed into a LForms widget directly to render the form.
145
- * @param element optional, the containing HTML element that includes the LForm's rendered form.
146
- * It could either be the DOM element or its id.
147
- * @param noEmptyValue optional, to remove items that have an empty value, the default is false.
148
- * @param noDisabledItem optional, to remove items that are disabled by skip logic, the default is false.
149
- * @returns {{}} Form definition data
150
- */
151
- getFormData: function(element, noEmptyValue, noDisabledItem) {
152
- var formObj = this._getFormObjectInScope(element);
153
- return formObj ? formObj.getFormData(noEmptyValue, noDisabledItem) : null;
154
- },
155
-
156
-
157
- /**
158
- * Get HL7 v2 OBR and OBX segment data from the form.
159
- * Empty or hidden questions are not included.
160
- * @param element optional, the containing HTML element that includes the LForm's rendered form.
161
- * It could either be the DOM element or its id
162
- * @returns {*} a string that contains HL7 v2 OBR and OBX segments
163
- */
164
- getFormHL7Data: function(element) {
165
- var formObj = this._getFormObjectInScope(element);
166
- return formObj ? LForms.HL7.toHL7Segments(formObj) : null;
167
- },
168
-
169
-
170
- /**
171
- * Get FHIR data from the form.
172
- * @param resourceType a FHIR resource type. it currently supports "DiagnosticReport",
173
- * "Questionnaire" (both standard Questionnaire and SDC Questionnaire profile)
174
- * and "QuestionnaireResponse" (SDC profile).
175
- * @param fhirVersion the version of FHIR being used (e.g., 'STU3')
176
- * @param formDataSource Optional. Either the containing HTML element that
177
- * includes the LForm's rendered form, a CSS selector for that element, an
178
- * LFormsData object, or an LForms form definition (parsed). If not
179
- * provided, the first form found in the page will be used.
180
- * @param options A hash of other options. See convertLFormsToFHIRData for
181
- * the allowed values.
182
- * @returns {*} the requested FHIR resource. For Questionnaire, the full form definition
183
- * will be returned, but or DiagnosticReport and QuestionnaireResponse, empty
184
- * or hidden questions will not be included.
185
- */
186
- getFormFHIRData: function(resourceType, fhirVersion, formDataSource, options) {
187
- if (!formDataSource || formDataSource instanceof HTMLElement || typeof formDataSource === 'string')
188
- formDataSource = this._getFormObjectInScope(formDataSource);
189
- return this._convertLFormsToFHIRData(resourceType, fhirVersion, formDataSource, options);
190
- },
191
-
192
-
193
- /**
194
- * Get a list of errors preventing the form from being valid.
195
- * @param [element] optional, the containing HTML element that includes the LForm's rendered form.
196
- * @return {Array<string>} list of errors or null if form is valid
197
- */
198
- checkValidity: function (element) {
199
- var formObj = this._getFormObjectInScope(element);
200
- return formObj ? formObj.checkValidity() : null;
201
- },
202
-
203
-
204
- /**
205
- * Convert LForms data into a FHIR resource
206
- * @param resourceType a FHIR resource type. it currently supports "DiagnosticReport",
207
- * "Questionnaire" (both standard Questionnaire and SDC Questionnaire profile)
208
- * and "QuestionnaireResponse" (SDC profile).
209
- * @param fhirVersion the version of FHIR to be used (e.g., 'STU3')
210
- * @param formData an LFormsData object or an LForms form definition (parsed).
211
- * @param options A hash of other options, with the following optional keys:
212
- * * bundleType: optional, maybe be either "transaction" or "collection".
213
- * This is used when resourceType is set to "DiagnosticReport", and requests
214
- * that the DiagnosticReport and associated Observation resources be placed
215
- * together in a bundle. When this is not present, a bundle will not be
216
- * used.
217
- * * noExtensions: a flag that a standard FHIR Questionnaire or QuestionnaireResponse is to be created
218
- * without any extensions, when resourceType is Questionnaire or QuestionnaireResponse.
219
- * The default is false.
220
- * * extract: a flag for QuestionnaireReponse that data should be extracted
221
- * (using the observationExtract extension). In this case the returned
222
- * resource will be an array consisting of the QuestionnaireResponse and any
223
- * extracted Observations.
224
- * * subject: A local FHIR resource that is the subject of the output resource.
225
- * If provided, a reference to this resource will be added to the output FHIR
226
- * resource when applicable.
227
- * @returns {*} a FHIR resource, or (if extract is true) an array of
228
- * resources.
229
- */
230
- _convertLFormsToFHIRData: function(resourceType, fhirVersion, formData, options) {
231
- if (!options)
232
- options = {};
233
- if (!(formData instanceof LForms.LFormsData))
234
- formData = new LForms.LFormsData(formData);
235
- var version = this.validateFHIRVersion(fhirVersion);
236
- var fhir = LForms.FHIR[version];
237
- var fhirData = null;
238
- if (formData) {
239
- switch (resourceType) {
240
- case "DiagnosticReport":
241
- var bundleType = options ? options.bundleType : undefined;
242
- var inBundle = bundleType != undefined;
243
- fhirData = fhir.DiagnosticReport.createDiagnosticReport(formData,
244
- options.subject, inBundle, bundleType);
245
- break;
246
- case "Questionnaire":
247
- fhirData = fhir.SDC.convertLFormsToQuestionnaire(formData,
248
- options.noExtensions);
249
- break;
250
- case "QuestionnaireResponse":
251
- if (options.extract)
252
- fhirData = fhir.SDC.convertLFormsToFHIRData(formData,
253
- options.noExtensions, options.subject);
254
- else
255
- fhirData = fhir.SDC.convertLFormsToQuestionnaireResponse(formData,
256
- options.noExtensions, options.subject);
257
- break;
258
- }
259
- }
260
- return fhirData;
261
- },
262
-
263
-
264
- /**
265
- * Convert FHIR SQC Questionnaire to the LForms internal definition
266
- *
267
- * @param fhirData - a FHIR Questionnaire resource, which should be generated through
268
- * the function "getFormFHIRData('Questionnaire', ...)"
269
- * @param fhirVersion - the version of FHIR in which the Questionnaire is
270
- * written. This maybe be omitted if the Questionnaire resource (in
271
- * fhirData) contains a meta.profile attibute specifying the FHIR version.
272
- * (See http://build.fhir.org/versioning.html#mp-version)
273
- * If both are provided, this takes precedence.
274
- * @returns {*} - an LForms json object
275
- */
276
- convertFHIRQuestionnaireToLForms: function(fhirData, fhirVersion) {
277
- var rtn = null;
278
- if (fhirData) {
279
- fhirVersion = this._requireValidFHIRVersion(fhirVersion, fhirData);
280
- var fhir = LForms.FHIR[fhirVersion];
281
- rtn = fhir.SDC.convertQuestionnaireToLForms(fhirData);
282
- }
283
- return rtn;
284
- },
285
-
286
-
287
- /**
288
- * Merge a FHIR resource into an LForms form object
289
- * @param resourceType a FHIR resource type. it currently supports "DiagnosticReport" and
290
- * "QuestionnaireResponse" (SDC profile)
291
- * @param fhirData a QuestionnaireResponse resource, a DiagnosticReport resource with "contained" Observation
292
- * resources,or a Bundle with DiagnosticReport and Observation resources
293
- * @param formData an LForms form definition or LFormsData object.
294
- * @param fhirVersion - the version of FHIR in which the fhirData is
295
- * written. This maybe be omitted if the Questionnaire resource (in
296
- * fhirData) contains a meta.profile attibute specifying the FHIR version.
297
- * (See http://build.fhir.org/versioning.html#mp-version)
298
- * If both are provided, this takes precedence.
299
- * @returns {{}} an updated LForms form definition, with answer data
300
- */
301
- mergeFHIRDataIntoLForms: function(resourceType, fhirData, formData, fhirVersion) {
302
- if (fhirData) {
303
- fhirVersion = this._requireValidFHIRVersion(fhirVersion, fhirData);
304
- var fhir = LForms.FHIR[fhirVersion];
305
- switch (resourceType) {
306
- case "DiagnosticReport":
307
- formData = fhir.DiagnosticReport.mergeDiagnosticReportToLForms(formData, fhirData);
308
- formData.hasSavedData = true;
309
- break;
310
- case "QuestionnaireResponse":
311
- formData = fhir.SDC.mergeQuestionnaireResponseToLForms(formData, fhirData);
312
- formData.hasSavedData = true; // will be used to determine whether to update or save
313
- break;
314
- }
315
- }
316
- return formData;
317
- },
318
-
319
-
320
- /**
321
- * Ensures that either the given FHIR version is valid and supported, or
322
- * that a valid version can be determined from the given FHIR resource.
323
- */
324
- _requireValidFHIRVersion: function(fhirVersion, fhirResource) {
325
- if (!fhirVersion)
326
- fhirVersion = this.detectFHIRVersion(fhirResource) || this.guessFHIRVersion(fhirResource);
327
- if (!fhirVersion) {
328
- throw new Error('Could not determine the FHIR version for this resource. '+
329
- 'Please make sure it is specified via meta.profile (see '+
330
- 'http://build.fhir.org/versioning.html#mp-version and '+
331
- 'https://www.hl7.org/fhir/references.html#canonical). '+
332
- 'Example 1: http://hl7.org/fhir/4.0/StructureDefinition/Questionnaire'+
333
- ' (for Questionnaire version 4.0, a.k.a. R4).'+
334
- 'Example 2: http://hl7.org/fhir/3.0/StructureDefinition/Questionnaire'+
335
- ' (for Questionnaire version 3.0, a.k.a. STU3).'+
336
- 'Example 3: http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire|2.7 '+
337
- ' (for SDC Questionnaire version 2.7).');
338
- }
339
- else
340
- fhirVersion = this.validateFHIRVersion(fhirVersion);
341
- return fhirVersion;
342
- },
343
-
344
-
345
- /**
346
- * For FHIR applications, provides FHIR context information that might be
347
- * needed in rendering a Quesitonnaire.
348
- * Priort to calling this, the LHC-Forms FHIR support files should be loaded.
349
- * @param fhirContext an optional object for accessing a FHIR context and
350
- * a FHIR API. It should be an instance of 'client-js', a.k.a. npm package fhirclient,
351
- * version 2. (See http://docs.smarthealthit.org/client-js).
352
- */
353
- setFHIRContext: function(fhirContext) {
354
- if (!LForms.FHIR) {
355
- throw new Error('LHC-Forms FHIR support files have not been loaded.' +
356
- 'See http://lhncbc.github.io/lforms/#fhirScripts');
357
- }
358
- LForms.fhirContext = fhirContext;
359
- LForms.fhirCapabilities = {}; // our own flags about what the server can do
360
- delete LForms._serverFHIRReleaseID; // in case the version changed
361
- },
362
-
363
-
364
- /**
365
- * Converts a FHIR version string (e.g. 3.0.1) to a release ID (e.g. 'STU3').
366
- * @param versionStr the version string to be converted to a release ID.
367
- * @return the release ID for the given version string, or versionStr if the
368
- * version string cannot be mapped to a release ID.
369
- */
370
- _fhirVersionToRelease: function(versionStr) {
371
- var releaseID = versionStr; // default
372
- var matchData = versionStr.match(/^\d+(\.\d+)/);
373
- if (matchData) {
374
- var versionNum = parseFloat(matchData[0]);
375
- // Following http://www.hl7.org/fhir/directory.cfml
376
- var releaseID = versionNum > 3.0 && versionNum <= 4.0 ?
377
- 'R4' : versionNum >= 1.1 && versionNum <= 3.0 ? 'STU3' : versionStr;
378
- }
379
- return releaseID;
380
- },
381
-
382
-
383
- /**
384
- * Gets the release identifier for the version of FHIR being used by the
385
- * server providing the FHIR context set via setFHIRContext (which must have
386
- * been called first).
387
- * @param callback Because asking the FHIR server for its version is an
388
- * asynchronous call, this callback function will be used to return the
389
- * version when found. The callback will be called asynchronously with a
390
- * release string, like 'STU3' or 'R4'. This string can then be passed to
391
- * validateFHIRVersion to check that the needed support files have been loaded.
392
- * If the release ID cannot be determined because the server's fhir
393
- * version is not known, the version number will passed to the callback. If
394
- * communication with the FHIR server is not succesful, the callback will be
395
- * called without an argument.
396
- */
397
- getServerFHIRReleaseID: function(callback) {
398
- if (!LForms.fhirContext)
399
- throw new Error('setFHIRContext needs to be called before getFHIRReleaseID');
400
- if (!LForms._serverFHIRReleaseID) {
401
- // Retrieve the fhir version
402
- try {
403
- var fhirAPI = LForms.fhirContext;
404
- //fhirAPI.request('metadata?_elements=fhirVersion').then(function(res) // causes an error on lforms-smart-fhir (TBD)
405
- fhirAPI.getFhirVersion().then(function(fhirVersion) {
406
- LForms._serverFHIRReleaseID = LForms.Util._fhirVersionToRelease(fhirVersion);
407
- console.log('Server FHIR version is '+LForms._serverFHIRReleaseID+' ('+
408
- fhirVersion+')');
409
- callback(LForms._serverFHIRReleaseID);
410
- }, function(err) {
411
- console.log("Error retrieving server's CompatibilityStatement:");
412
- console.log(err);
413
- callback();
414
- });
415
- }
416
- catch(e) {
417
- setTimeout(function(){callback()});
418
- throw e;
419
- }
420
- }
421
- else { // preserve the asynchronous nature of the return
422
- setTimeout(function(){callback(LForms._serverFHIRReleaseID)});
423
- }
424
- },
425
-
426
-
427
- /**
428
- * Checks to see if the given value is a valid FHIR version. If the
429
- * version is unsupported, an exception is thrown. Also, if the version is
430
- * supported but the support file is not loaded, an exception will be thrown.
431
- * @version the version of FHIR that was requested
432
- * @return the version passed in
433
- */
434
- validateFHIRVersion: function(version) {
435
- if (LForms.Util.FHIRSupport[version]) {
436
- if (!LForms.FHIR) {
437
- throw new Error('The FHIR support files for LHC-Forms do not appear to '+
438
- 'have been loaded. Please consult the documentation at '+
439
- 'http://lhncbc.github.io/lforms/#fhirSupport.');
440
- }
441
- else if (!LForms.FHIR[version])
442
- throw new Error('Version '+version+
443
- ' of FHIR is supported, but the supporting code was not loaded.');
444
- }
445
- else
446
- throw new Error('Version '+version+' of FHIR is not supported.');
447
- return version;
448
- },
449
-
450
-
451
- /**
452
- * Attempts to detect the version of FHIR specified in the given resource.
453
- * @param fhirData a FHIR resource. Supported resource types are currently
454
- * just Questionnaire and QuestionnaireResponse.
455
- * @return the FHIR version, or null if the FHIR version was not explicity
456
- * specified in the resource.
457
- */
458
- detectFHIRVersion: function(fhirData) {
459
- var fhirVersion;
460
- if (fhirData.meta && fhirData.meta.profile) {
461
- var profiles = fhirData.meta.profile;
462
- // See http://build.fhir.org/versioning.html#mp-version
463
- var questionnairePattern =
464
- new RegExp('http://hl7.org/fhir/(\\d+\.\\d+)([\\.\\d]+)?/StructureDefinition/Questionnaire');
465
- var sdcPattern =
466
- new RegExp('http://hl7.org/fhir/u./sdc/StructureDefinition/sdc-questionnaire\\|(\\d+\.\\d+)(\.\\d+)?');
467
- for (var i=0, len=profiles.length && !fhirVersion; i<len; ++i) {
468
- var match = profiles[i].match(questionnairePattern);
469
- if (match)
470
- fhirVersion = match[1];
471
- else {
472
- match = profiles[i].match(sdcPattern);
473
- if (match) {
474
- fhirVersion = match[1];
475
- // See http://www.hl7.org/fhir/uv/sdc/history.cfml
476
- // Use FHIR 3.0 for SDC 2.0; There was no SDC 3.0
477
- if (fhirVersion == '2.0') {
478
- fhirVersion = '3.0';
479
- }
480
- // use FHIR 4.0 for SDC version >= 2.1
481
- else if (parseFloat(fhirVersion) >= 2.1) {
482
- fhirVersion = '4.0';
483
- }
484
- }
485
- }
486
- }
487
- }
488
- if (fhirVersion)
489
- fhirVersion = this._fhirVersionToRelease(fhirVersion);
490
- return fhirVersion;
491
- },
492
-
493
-
494
- /**
495
- * Looks at the structure of the given FHIR resource to determine the version
496
- * of FHIR, if possible.
497
- * @param fhirData a FHIR resource. Supported resource types are currently
498
- * just Questionnaire and QuestionnaireResponse.
499
- * @return the FHIR version number (e.g. STU3), or null if the type cannot be
500
- * determined.
501
- */
502
- guessFHIRVersion: function(fhirData) {
503
- var version = null;
504
- if (fhirData.resourceType == 'Questionnaire') {
505
- // See if any items have a property deleted from R4.
506
- var items = [];
507
- var foundSTU3 = this._testValues(fhirData, 'item', function(item) {
508
- return !!(item.option || item.options ||
509
- (item.enableWhen && 'hasAnswer' in item.enableWhen));
510
- });
511
- version = foundSTU3 ? 'STU3' : 'R4';
512
- }
513
- else if (fhirData.resourceType == 'QuestionnaireResponse') {
514
- if (fhirData.parent)
515
- version = 'STU3';
516
- else {
517
- // See if any items have a property deleted from R4.
518
- var foundSTU3 = this._testValues(fhirData, 'item', function(item) {
519
- return !!item.subject;
520
- });
521
- version = foundSTU3 ? 'STU3' : 'R4';
522
- }
523
- }
524
- return version;
525
- },
526
-
527
-
528
- /**
529
- * Searches the properties and sub-properties of "obj" for the given property
530
- * name, testing their values to see if valTest returns true.
531
- * @param obj the object to be searched. This can be an array.
532
- * @param property the property name to look for
533
- * @param testVal the function to use to test the property values. This
534
- * should return true if the value passes the test.
535
- * @return true if at least one value was found found whose key was "property"
536
- * and for which testVal returned true.
537
- */
538
- _testValues: function(obj, property, valTest) {
539
- var rtn = false;
540
- if (obj instanceof Array) {
541
- for (var j=0, jLen=obj.length; !rtn && j<jLen; ++j)
542
- rtn = this._testValues(obj[j], property, valTest);
543
- }
544
- else if (typeof obj === "object") {
545
- var keys = Object.keys(obj);
546
- for (var i=0, len=keys.length; !rtn && i<len; ++i) {
547
- var key = keys[i];
548
- var val = obj[key];
549
- if (key === property) {
550
- if (val instanceof Array) {
551
- for (var k=0, kLen=val.length; !rtn && k<kLen; ++k)
552
- rtn = valTest(val[k]);
553
- }
554
- else
555
- rtn = valTest(val);
556
- }
557
- if (!rtn)
558
- rtn = this._testValues(val, property, valTest); // search sub-objects
559
- }
560
- }
561
- return rtn;
562
- },
563
-
564
-
565
- /**
566
- * Find the form object in the scope based the form dom element
567
- * @param element element the containing HTML element that includes the LForm's rendered form.
568
- * @returns {*}
569
- * @private
570
- */
571
- _getFormObjectInScope: function(element) {
572
- // find the scope that has the LForms data
573
- var formObj;
574
- if (!element) element = jQuery("body");
575
-
576
- // class="lf-form"> is the element that contains rendered form.
577
- var lfForms = jQuery(element).find(".lf-form");
578
- angular.forEach(lfForms, function(ele, index) {
579
- var lfForm = angular.element(ele);
580
- if (lfForm.scope() && lfForm.scope().lfData) {
581
- formObj = lfForm.scope().lfData;
582
- return false; // break the loop
583
- }
584
- });
585
-
586
- return formObj;
587
- },
588
-
589
- /**
590
- * This function and stringToDTDateISO are meant to work as a pair on DT (or FHIR date) data type.
591
- * The idea is that DT/date type does not have timezone info, as a result, the string value could be
592
- * off by a day during either way of conversion depending on the time zone the code is executed.
593
- * The solution here is to keep the literal values of the year, month, and date components remain
594
- * unchanged regardless of the time zones.
595
- * Convert the given date object into a DT type date string, in "yyyy-mm-dd" format, where the
596
- * year, month, and date are based on the "local time zone" as the users can see on the display.
597
- * @param dateObj the date object to be converted.
598
- * @return date string in yyyy-mm-dd format with year, month, and date values corresponding to that
599
- * at the local timezone.
600
- */
601
- dateToDTStringISO: function (dateObj) {
602
- return (! dateObj || !(dateObj instanceof Date) || isNaN(dateObj.getTime()))? undefined: [
603
- (10000 + dateObj.getFullYear()).toString().substr(1),
604
- (101 + dateObj.getMonth()).toString().substr(1),
605
- (100 + dateObj.getDate()).toString().substr(1) ].join('-');
606
- },
607
-
608
- /**
609
- * Parse the given iso date string, that is, a string of format "yyyy[-mm[-dd]]", into a Date object,
610
- * and then, adjust the year, month, and day so that when displayed (as local date) the literal values of
611
- * the year, month, and date components remain unchanged.
612
- * See the comments/docs for function dateToDTStringISO().
613
- * @param isoDateString
614
- */
615
- stringToDTDateISO: function(isoDateString) {
616
- var d = new Date(isoDateString);
617
- return isNaN(d.getTime())? undefined: new Date(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate());
618
- },
619
-
620
- /**
621
- * Get a formatted date string from a date object
622
- * for example: "2016-10-31T14:42:12Z"
623
- * @param objDate a date object, or a valid string representation of date.
624
- * @returns a formatted date string
625
- */
626
- dateToDTMString: function(objDate) {
627
- if(typeof objDate === 'string') {
628
- objDate = LForms.Util.stringToDate(objDate, true);
629
- }
630
- return objDate.toISOString();
631
- },
632
-
633
- /**
634
- * Parse a formatted date string and create a date object
635
- * @param strDate a formatted date string
636
- * @param looseParsing {boolean} - Do default parsing. Intended to parse output
637
- * from native date object, typically from programmatic output from widgets. Default is false.
638
- * @returns a date object
639
- */
640
- stringToDate: function(strDate, looseParsing) {
641
- if(! strDate || (typeof strDate) != 'string') { // maybe already a date object.
642
- return strDate;
643
- }
644
-
645
- if(strDate.trim() === 't') {
646
- return new Date();
647
- }
648
-
649
- let m = moment(strDate, parseDateFormats, true);
650
- if(looseParsing && !m.isValid()) { // Make another attempt for loose parsing.
651
- m = moment(strDate);
652
- }
653
- return m.isValid() ? m.toDate() : null;
654
- },
655
-
656
-
657
- /**
658
- * Validate date object or date string. If string, check to see if it is in acceptable formats.
659
- * See stringToDate() for acceptable formats.
660
- * @param date {Date | string} - Potential date object or date string
661
- * @returns boolean
662
- */
663
- isValidDate: function(date) {
664
- return !!(LForms.Util.stringToDate(date));
665
- },
666
-
667
-
668
- /**
669
- * Format a date object with given format. Refer to momentjs documentation for
670
- * format specification.
671
- *
672
- * @param date - Date object
673
- * @param format - Format string
674
- * @returns {string}
675
- */
676
- formatDate: function(date, format) {
677
- return moment(date).format(format);
678
- },
679
-
680
-
681
- /**
682
- * Check if an item's value is empty, where the data has no meaningful use.
683
- * @param value the value to be tested
684
- * @returns {boolean}
685
- * @private
686
- */
687
- isItemValueEmpty: function(value) {
688
- var empty = true;
689
- if(value !== null && value !== undefined && value !== '' && typeof value !== 'function') {
690
- if(typeof value === 'string' || value instanceof String) {
691
- empty = value.trim() === '';
692
- }
693
- else if(Array.isArray(value)) {
694
- for(var i=0; i < value.length; ++i) {
695
- if(! this.isItemValueEmpty(value[i])) {
696
- empty = false;
697
- break;
698
- }
699
- }
700
- }
701
- else if(typeof value === 'object') {
702
- var keys = Object.keys(value);
703
- if(keys.length > 0) {
704
- for(var i=0, iLen=keys.length; i<iLen; i++) {
705
- if(! this.isItemValueEmpty(value[keys[i]])) {
706
- empty = false;
707
- break;
708
- }
709
- }
710
- }
711
- else if(value.constructor !== Object) { // e.g., a Date object has zero length keys
712
- empty = false;
713
- }
714
- }
715
- else {
716
- empty = false;
717
- }
718
- }
719
-
720
- return empty;
721
- },
722
-
723
-
724
- /**
725
- * Get the letter (or letters) indicator for the next repeating instance
726
- * The letters returned are in the pattern of:
727
- * 'a','b',...,'z','aa','ab',...,'az','ba','bb',...
728
- * for index that is:
729
- * 1, 2, ..., 26, 27, 28, ..., 52, 53, 54, ...
730
- * @param index the index for the current repeating instance, starting with 1
731
- */
732
- getNextLetter: function(index) {
733
- var letters = "abcdefghijklmnopqrstuvqxyz";
734
- var positions = [];
735
- var wkIndex = index;
736
- while (wkIndex > 0) {
737
- var letterIndex = wkIndex % 26;
738
- letterIndex = letterIndex === 0 ? 25 : letterIndex - 1;
739
- positions.push(letterIndex);
740
- wkIndex = Math.floor((wkIndex-1) / 26);
741
- }
742
- var nextLetter = "";
743
- for(var i=positions.length-1; i>=0; i--) {
744
- nextLetter += letters.charAt(positions[i]);
745
- }
746
- return nextLetter;
747
- },
748
-
749
-
750
- /**
751
- * Finds an object from an array using key/value pair with an optional start index.
752
- * The matching value should be a primitive type. If start index is not specified,
753
- * it is assumed to be 0.
754
- *
755
- * Only returns the first matched object in the array.
756
- *
757
- * @param targetObjects - Array of objects to search using key and value
758
- * @param key - Key of the target object to match the value.
759
- * @param matchingValue - Matching value of the specified key.
760
- * @param starting_index - Optional start index to lookup. Negative number indicates index from end.
761
- * The absolute value should be less than the length of items in the array. If not
762
- * the starting index is assumed to be 0.
763
- * @param all - If true, then an array will be returned containing all
764
- * matches.
765
- *
766
- * @returns {*} - If "all" is false (default), then this returns the first matched
767
- * object, or null if none matched. If "all" is true, then this will return
768
- * an array containing any matched objects.
769
- */
770
- findObjectInArray: function(targetObjects, key, matchingValue, starting_index, all) {
771
- var ret = all ? [] : null;
772
- if(Array.isArray(targetObjects)) {
773
- var start = 0;
774
- // Figure out start index.
775
- if(starting_index && Math.abs(starting_index) < targetObjects.length) {
776
- if(starting_index < 0) {
777
- start = targetObjects.length + starting_index;
778
- }
779
- else {
780
- start = starting_index;
781
- }
782
- }
783
- var len = targetObjects.length;
784
- for(var i = start; i < len; i++) {
785
- if(targetObjects[i][key] === matchingValue) {
786
- var match = targetObjects[i];
787
- if (all)
788
- ret.push(match);
789
- else {
790
- ret = match;
791
- break;
792
- }
793
- }
794
- }
795
- }
796
-
797
- return ret;
798
- },
799
-
800
- /**
801
- * Recursively find the first occurrence of an item, depth first, that matches the
802
- * given field value for the given field
803
- * @param items an array of LForms items, where an item may have its own sub-items.
804
- * @param key
805
- * @param matchingValue
806
- * @return {*}
807
- */
808
- findItem: function(items, key, matchingValue) {
809
- var ret = null;
810
- if(items) {
811
- for(var i=0; !ret && i < items.length; ++i) {
812
- var item = items[i];
813
- if(item[key] === matchingValue) {
814
- ret = item;
815
- }
816
- else if(Array.isArray(item.items)) {
817
- ret = this.findItem(item.items, key, matchingValue);
818
- }
819
- }
820
- }
821
-
822
- return ret;
823
- },
824
-
825
- /**
826
- * Remove key/values from an object based on a regular expression of key.
827
- *
828
- * @param obj {object} - Object to prune
829
- * @param keyRegex {regex} - A regular expression to match the keys for deletion
830
- * @param recursiveKey {optional|string} - Key of the recursive field. The value
831
- * of this should be an object or an Array of objects.
832
- * @private
833
- */
834
- _pruneObject: function (keyRegex, obj, recursiveKey) {
835
- if(typeof obj === 'object') {
836
- for(var k in obj) {
837
- if(k.match(keyRegex)) {
838
- delete obj[k];
839
- }
840
- else if(recursiveKey && k === recursiveKey) {
841
- var val = obj[k];
842
- if(Array.isArray(val)) {
843
- var len = val.length;
844
- for(var i = 0; i < len; i++) {
845
- this._pruneObject(keyRegex, val[i], recursiveKey);
846
- }
847
- }
848
- else {
849
- this._pruneObject(keyRegex, val, recursiveKey);
850
- }
851
- }
852
- }
853
- }
854
- },
855
-
856
-
857
- /**
858
- * Utility to walkthrough recurively through each element in a collection
859
- *
860
- * @param collectionObj
861
- */
862
- pruneNulls: function (collectionObj) {
863
- if (Array.isArray(collectionObj)) {
864
- for(var i = collectionObj.length - 1; i >= 0; i--) {
865
- if(collectionObj[i] === null || collectionObj[i] === undefined ) {
866
- collectionObj.splice(i, 1);
867
- }
868
- else if(typeof collectionObj[i] === 'object') {
869
- LForms.Util.pruneNulls(collectionObj[i]);
870
- }
871
- }
872
- }
873
- else if (collectionObj && typeof collectionObj === 'object') {
874
- var keys = Object.keys(collectionObj);
875
- keys.forEach(function (key) {
876
- if(collectionObj[key] === null || collectionObj[key] === undefined) {
877
- delete collectionObj[key];
878
- }
879
- else if (typeof collectionObj[key] === 'object') {
880
- LForms.Util.pruneNulls(collectionObj[key]);
881
- }
882
- });
883
- }
884
- },
885
-
886
-
887
- /**
888
- * We are transitioning lforms fields representing code (form.code, items[x].questionCode
889
- * and items[x].questionCodeSystem) to FHIR definition of Coding type.
890
- * In lforms, these fields are string type and FHIR Coding is an array of
891
- * objects encapsulating multiple codes
892
- * .
893
- * To preserve compatibility with existing lforms code, we preserve
894
- * both lforms code and FHIR Coding. FHIR Coding is preserved in codeList.
895
- *
896
- * This function adopts the following rules.
897
- *
898
- * . If codeList is not present create it making the first item representing lforms code.
899
- * . If lforms code is not present, create it as appropriate (form.code or item[x].questionCode) from
900
- * first item in codeList.
901
- * . Always make sure the first item in codeList represents lforms code.
902
- *
903
- * @param formOrItem - lforms form or items[x]
904
- */
905
- initializeCodes: function (formOrItem) {
906
-
907
- var isItem = (formOrItem.question || formOrItem.questionCode);
908
- var code = isItem ? formOrItem.questionCode : formOrItem.code;
909
- var codeSystem = isItem ? formOrItem.questionCodeSystem : formOrItem.codeSystem;
910
- var display = isItem ? formOrItem.question : formOrItem.name;
911
- var codeSystemUrl = LForms.Util.getCodeSystem(codeSystem);
912
-
913
- // if there is code
914
- if(code) {
915
- if(!formOrItem.codeList) {
916
- formOrItem.codeList = [];
917
- }
918
- var codeList = formOrItem.codeList;
919
- var found = false;
920
- for(var i = 0; i < codeList.length; i++) {
921
- if(code === codeList[i].code && (!codeSystemUrl && !codeList[i].system || codeSystemUrl === codeList[i].system)) {
922
- found = true;
923
- break;
924
- }
925
- }
926
-
927
- // if form data is converted from a FHIR Questionnaire that has no 'code' on items,
928
- // don't create a 'code' when converting it back to Questionnaire.
929
- if(!found && codeSystemUrl !== 'LinkId') {
930
- var code = {
931
- code: code,
932
- display: display
933
- };
934
- if (codeSystemUrl) {
935
- code.system = codeSystemUrl;
936
- }
937
- codeList.unshift(code);
938
- }
939
- }
940
- // if there is a codeList
941
- else {
942
- if(formOrItem.codeList && formOrItem.codeList.length > 0) {
943
- if(isItem) {
944
- // questionCode is required, so this shouldn't happen??
945
- formOrItem.questionCode = formOrItem.codeList[0].code;
946
- formOrItem.questionCodeSystem = formOrItem.codeList[0].system;
947
- }
948
- else {
949
- formOrItem.code = formOrItem.codeList[0].code;
950
- formOrItem.codeSystem = formOrItem.codeList[0].system;
951
- }
952
- }
953
- }
954
-
955
- return formOrItem;
956
- },
957
-
958
-
959
- /**
960
- * Creates a Reference to the given FHIR resource, to be used an a subject in
961
- * another resource.
962
- * @param fhirRes the FHIR resource for which to create a Reference.
963
- * @return a FHIR Reference, with a local reference to fhirRes.
964
- */
965
- createLocalFHIRReference: function(fhirRes) {
966
- var ref = {"reference": fhirRes.resourceType+"/" + fhirRes.id};
967
- if (fhirRes.resourceType === "Patient") {
968
- if (fhirRes.name && fhirRes.name.length > 0) {
969
- var name = fhirRes.name[0];
970
- if (name.text)
971
- ref.display = name.text;
972
- else {
973
- if (name.given && name.given.length > 0)
974
- ref.display = name.given[0];
975
- if (name.family) {
976
- if (ref.display)
977
- ref.display = ref.display+' '+name.family;
978
- else
979
- ref.display = name.family;
980
- }
981
- }
982
- }
983
- }
984
-
985
- // Not sure what to put for display for something other than patient, but it
986
- // is optional, so for now I will just leave it blank.
987
- return ref;
988
- },
989
-
990
-
991
- /**
992
- * Get a code system based on the code system value used in LForms
993
- * @param codeSystemInLForms code system value used in LForms
994
- * @private
995
- */
996
- getCodeSystem: function(codeSystemInLForms) {
997
-
998
- var codeSystem;
999
- switch (codeSystemInLForms) {
1000
- case "LOINC":
1001
- codeSystem = "http://loinc.org";
1002
- break;
1003
- default:
1004
- codeSystem = codeSystemInLForms;
1005
-
1006
- }
1007
-
1008
- return codeSystem;
1009
- },
1010
-
1011
-
1012
- /**
1013
- * Removes an object(s) from an array searching it using key/value pair with an optional start index.
1014
- * The matching value should be a primitive type. If start index is not specified,
1015
- * it is assumed to be 0.
1016
- *
1017
- * @param targetObjects - Array of objects to search using key and value
1018
- * @param key - key of the object to match the value
1019
- * @param matchingValue - Matching value of the specified key.
1020
- * @param starting_index - Optional start index to lookup. Negative number indicates index from end.
1021
- * The absolute value should be less than the length of items in the array. If not
1022
- * the starting index is assumed to be 0.
1023
- * @param all - if false, removes the first matched object otherwise removes all matched objects.
1024
- * Default is false.
1025
- *
1026
- * @returns {Object|Array} - Returns removed object or array of objects.
1027
- */
1028
- removeObjectsFromArray: function(targetObjects, key, matchingValue, starting_index, all) {
1029
- var ind = all ? [] : null;
1030
- var ret = all ? [] : null;
1031
- if(Array.isArray(targetObjects)) {
1032
- var start = 0;
1033
- // Figure out start index.
1034
- if(starting_index && Math.abs(starting_index) < targetObjects.length) {
1035
- if(starting_index < 0) {
1036
- start = targetObjects.length + starting_index;
1037
- }
1038
- else {
1039
- start = starting_index;
1040
- }
1041
- }
1042
- var len = targetObjects.length;
1043
-
1044
- for(var i = start; i < len; i++) {
1045
- if(targetObjects[i][key] === matchingValue) {
1046
- var match = targetObjects[i];
1047
- if (all) {
1048
- ind.push(i);
1049
- ret.push(match);
1050
- }
1051
- else {
1052
- ind = i;
1053
- ret = match;
1054
- break;
1055
- }
1056
- }
1057
- }
1058
- if(Array.isArray(ind)) {
1059
- for(var i = ind.length - 1; i >= 0; i--) {
1060
- targetObjects.splice(ind[i], 1);
1061
- }
1062
- }
1063
- else {
1064
- if(ind !== null) {
1065
- targetObjects.splice(ind, 1);
1066
- }
1067
- }
1068
- }
1069
-
1070
- return ret;
1071
- },
1072
-
1073
-
1074
- /**
1075
- * Returns the part of an LForms form definition that all form definitions
1076
- * should have.
1077
- */
1078
- baseFormDef: function() {
1079
- return {lformsVersion: LForms.lformsVersion};
1080
- },
1081
-
1082
-
1083
- /**
1084
- * Get a list of warning messages about answer lists, which should have been
1085
- * loaded from the URL in answerValueSet but were not.
1086
- *
1087
- * @param {*} formDataSource Optional. Either the containing HTML element that
1088
- * includes the LForm's rendered form, a CSS selector for that element, an
1089
- * LFormsData object, or an LForms form definition (parsed). If not
1090
- * provided, the first form found in the page will be used. */
1091
- getAnswersResourceStatus : function(formDataSource) {
1092
- if (!formDataSource || formDataSource instanceof HTMLElement || typeof formDataSource === 'string')
1093
- formDataSource = this._getFormObjectInScope(formDataSource);
1094
-
1095
- return formDataSource.checkAnswersResourceStatus();
1096
- },
1097
-
1098
-
1099
- /**
1100
- * A wrapper function to deep copy an object so that FHIR lib does not use "angular" directly
1101
- * @param {*} object
1102
- */
1103
- deepCopy: function(object) {
1104
- return angular.copy(object);
1105
- },
1106
-
1107
-
1108
- /**
1109
- * Shows a warning message, typically about some problem with the form
1110
- * definition.
1111
- * @param msg the message to show
1112
- * @param item (optional) the item in the form to which the message applies.
1113
- */
1114
- showWarning: function(msg, item) {
1115
- if (item)
1116
- msg = 'The question "'+item.text+'" produced the following warning: '+msg;
1117
- console.log(msg);
1118
- // TBD: add a warning visible on the page.
1119
- }
1120
- };
1121
-
1122
-