Fixes #7497 datetimepicker invalid date format (#7499)
[openemr.git] / library / options.js.php
blobb869cc5a0c14c56de68ebd79b3607691cc59240e
1 <?php
3 /**
4 * This is the place to put JavaScript functions that are needed to support
5 * options.inc.php. Include this in the <head> section of relevant modules.
6 * It's a .php module so that translation can be supported.
8 * @package OpenEMR
9 * @link https://www.open-emr.org
10 * @author Rod Roark <rod@sunsetsystems.com>
11 * @author Brady Miller <brady.g.miller@gmail.com>
12 * @author Jerry Padgett <sjpadgett@gmail.com>
13 * @copyright Copyright (c) 2014-2021 Rod Roark <rod@sunsetsystems.com>
14 * @copyright Copyright (c) 2018 Brady Miller <brady.g.miller@gmail.com>
15 * @copyright Copyright (c) 2021 Jerry Padgett <sjpadgett@gmail.com>
16 * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
19 use OpenEMR\Common\Csrf\CsrfUtils;
22 <script>
24 // JavaScript support for date types when the A or B edit option is used.
25 // Called to recompute displayed age dynamically when the corresponding date is
26 // changed. Must generate the same age formats as the oeFormatAge() function.
28 function updateAgeString(fieldid, asof, format, description) {
29 var datefld = document.getElementById('form_' + fieldid);
30 var f = datefld.form;
31 var age = '';
32 var date1 = new Date(datefld.value);
33 var date2 = asof ? new Date(asof) : new Date();
34 if (format == 3) {
35 // Gestational age.
36 var msecs = date2.getTime() - date1.getTime();
37 var days = Math.round(msecs / (24 * 60 * 60 * 1000));
38 var weeks = Math.floor(days / 7);
39 days = days % 7;
40 if (description == '') description = <?php echo xlj('Gest age') ?>;
41 age = description + ' ' +
42 weeks + (weeks == 1 ? ' ' + <?php echo xlj('week') ?> : ' ' + <?php echo xlj('weeks') ?>) + ' ' +
43 days + (days == 1 ? ' ' + <?php echo xlj('day') ?> : ' ' + <?php echo xlj('days') ?>);
45 else {
46 // Years or months.
47 var dayDiff = date2.getDate() - date1.getDate();
48 var monthDiff = date2.getMonth() - date1.getMonth();
49 var yearDiff = date2.getFullYear() - date1.getFullYear();
50 var ageInMonths = yearDiff * 12 + monthDiff;
51 if (dayDiff < 0) --ageInMonths;
52 if (format == 1 || (format == 0 && ageInMonths >= 24)) {
53 age = yearDiff;
54 if (monthDiff < 0 || (monthDiff == 0 && dayDiff < 0)) --age;
55 age = '' + age;
57 else {
58 age = '' + ageInMonths;
59 if (format == 0) {
60 age = age + ' ' + (ageInMonths == 1 ? <?php echo xlj('month') ?> : <?php echo xlj('months') ?>);
63 if (description == '') description = <?php echo xlj('Age') ?>;
64 if (age != '') age = description + ' ' + age;
66 document.getElementById('span_' + fieldid).innerHTML = age;
69 // Function to support datatype 46 - single-selection list with comment support
70 function processCommentField(fieldId) {
71 if (document.getElementById("form_" + fieldId) != null) {
72 if (document.getElementById("form_" + fieldId).options[document.getElementById("form_" + fieldId).selectedIndex].value.match(/^comment_/)) {
73 if (document.getElementById("form_text_" + fieldId).style.display == "none") {
74 document.getElementById("form_text_" + fieldId).style.display = "inline-block";
76 } else {
77 document.getElementById("form_text_" + fieldId).value = "";
78 document.getElementById("form_text_" + fieldId).style.display = "none";
83 function myHideOrShow(elem, hide) {
84 // elem is a td or bootstrap column div.
85 for (var inp = elem.firstChild; inp; inp = inp.nextSibling) {
86 if (inp.style) {
87 inp.style.display = hide ? 'none' : '';
88 } else {
89 // This must be a text node with no tag, so hide/show the parent elem instead.
90 elem.style.display = hide ? 'none' : '';
95 // Function to show or hide form fields (and their labels) depending on "skip conditions"
96 // defined in the layout.
98 var cskerror = false; // to avoid repeating error messages
99 function checkSkipConditions() {
100 var myerror = cskerror;
101 var prevandor = '';
102 var prevcond = false;
103 if (typeof skipArray === 'undefined') {
104 return '';
106 for (var i = 0; i < skipArray.length; ++i) {
107 var target = skipArray[i].target;
108 var id = skipArray[i].id;
109 var itemid = skipArray[i].itemid;
110 var operator = skipArray[i].operator;
111 var value = skipArray[i].value;
112 var is_radio = false;
113 var action = skipArray[i].action;
114 var tofind = id;
116 if (itemid) tofind += '[' + itemid + ']';
117 // Some different source IDs are possible depending on the data type.
118 var srcelem = document.getElementById('check_' + tofind);
119 if (srcelem == null) srcelem = document.getElementById('radio_' + tofind);
120 if (srcelem == null) srcelem = document.getElementById('form_' + tofind) ;
121 if (srcelem == null) srcelem = document.getElementById('text_' + tofind);
123 if (srcelem == null) {
124 // This caters to radio buttons which we treat like droplists.
125 var tmp = document.getElementById('form_' + tofind + '[' + value + ']');
126 if (tmp != null) {
127 srcelem = tmp;
128 if (operator == 'eq') operator = 'se';
129 if (operator == 'ne') operator = 'ns';
130 is_radio = true;
134 if (srcelem == null) {
135 if (!cskerror) alert(<?php echo xlj('Cannot find a skip source field for'); ?> + ' "' + tofind + '"');
136 myerror = true;
137 continue;
140 var condition = false;
141 var is_multiple = false;
142 var elem_val;
143 if ( is_radio){
144 for (var k = 0; k < document.getElementsByName('form_' + tofind).length; k++){
145 if (document.getElementsByName('form_' + tofind)[k].checked){
146 elem_val= document.getElementsByName('form_' + tofind)[k].value;
149 }else if( typeof srcelem.options!=="undefined" && srcelem.type == 'select-one' ){
150 elem_val=srcelem.options[srcelem.selectedIndex].value;
152 }else if( srcelem.type == 'select-multiple' ) {
153 elem_val = new Array();
154 is_multiple = true;
155 for (var k = 0; k < srcelem.length; k++) {
156 if (srcelem.options[k].selected) {
157 if( elem_val.indexOf(srcelem.options[k].value)<0) {
158 elem_val.push(srcelem.options[k].value);
162 } else {
163 elem_val=srcelem.value;
165 if(elem_val == null) {
166 elem_val = srcelem.getAttribute("data-value");
167 if( elem_val !== null && elem_val.indexOf("|") !== -1 ) {
168 elem_val = elem_val.split("|");
169 is_multiple = true;
172 if(elem_val == null) elem_val = srcelem.innerText;
174 //this is a feature fix for the multiple select list option
175 //collect all the multiselect control values:
176 if( is_multiple ) {
177 switch(operator) {
178 case 'eq':
179 condition = (-1 !== elem_val.indexOf(value));break;
180 case 'ne':
181 condition = (-1 == elem_val.indexOf(value)); break;
182 case 'se':
183 condition = srcelem.checked ; break; // doesn't make sense?
184 case 'ns':
185 condition = !srcelem.checked; break;
188 } else {
189 if (operator == 'eq') condition = elem_val == value; else
190 if (operator == 'ne') condition = elem_val != value; else
191 if (operator == 'se') condition = srcelem.checked ; else
192 if (operator == 'ns') condition = !srcelem.checked;
195 // Logic to accumulate multiple conditions for the same target.
196 // alert('target = ' + target + ' prevandor = ' + prevandor + ' prevcond = ' + prevcond); // debugging
197 if (prevandor == 'and') condition = condition && prevcond; else
198 if (prevandor == 'or' ) condition = condition || prevcond;
199 prevandor = skipArray[i].andor;
200 prevcond = condition;
201 var j = i + 1;
202 if (j < skipArray.length && skipArray[j].target == target) continue;
204 // At this point condition indicates the target should be hidden or have its value set.
206 var skip = condition;
208 if (action.substring(0, 5) == 'value') {
209 skip = false;
211 else if (action.substring(0, 5) == 'hsval') {
212 // This action means hide if true, set value if false.
213 if (!condition) {
214 action = 'value=' + action.substring(6);
215 skip = false;
219 if (true) {
220 var trgelem1 = document.getElementById('label_id_' + target);
221 var trgelem2 = document.getElementById('value_id_text_' + target);
222 if (trgelem2 == null) {
223 trgelem2 = document.getElementById('value_id_' + target);
225 if (trgelem1 == null && trgelem2 == null) {
226 var trgelem1 = document.getElementById('label_' + target);
227 var trgelem2 = document.getElementById('text_' + target);
228 if(trgelem2 == null){
229 trgelem2 = document.getElementById('form_' + target);
231 if (trgelem1 == null && trgelem2 == null) {
232 if (!cskerror) alert(<?php echo xlj('Cannot find a skip target field for'); ?> + ' "' + target + '"');
233 myerror = true;
234 continue;
237 if (trgelem1) myHideOrShow(trgelem1, skip);
238 if (trgelem2) myHideOrShow(trgelem2, skip);
241 if (action.substring(0, 5) == 'value') {
242 var trgelem = document.forms[0]['form_' + target];
243 if (trgelem == null) {
244 if (!cskerror) alert('Cannot find a value target field for "' + target + '"');
245 myerror = true;
246 continue;
248 var action_value = action.substring(6);
249 if (trgelem.type == 'checkbox') {
250 trgelem.checked = !(action_value == '0' || action_value == '');
252 else {
253 trgelem.value = action_value;
254 // Handle billing code descriptions.
255 var valelem = document.forms[0]['form_' + target + '__desc'];
256 if (skipArray[i].valdesc && valelem) {
257 // alert('Setting ' + valelem.name + ' value to: ' + skipArray[i].valdesc); // debugging
258 valelem.value = skipArray[i].valdesc;
263 // If any errors, all show in the first pass and none in subsequent passes.
264 cskerror = cskerror || myerror;
267 ///////////////////////////////////////////////////////////////////////
268 // Image canvas support starts here.
269 ///////////////////////////////////////////////////////////////////////
271 var lbfCanvases = {}; // contains the LC instance for each canvas.
273 // Initialize the drawing widget.
274 // canid is the id of the div that will contain the canvas, and the image
275 // element used for initialization should have an id of canid + '_img'.
277 function lbfCanvasSetup(canid, canWidth, canHeight) {
278 LC.localize({
279 "stroke": <?php echo xlj('stroke'); ?>,
280 "fill": <?php echo xlj('fill'); ?>,
281 "bg": <?php echo xlj('bg{{image canvas label}}'); ?>,
282 "Clear": <?php echo xlj('Clear'); ?>,
283 // The following are tooltip translations, however they do not work due to
284 // a bug in LiterallyCanvas 0.4.13. We'll leave them here pending a fix.
285 "Eraser": <?php echo xlj('Eraser'); ?>,
286 "Pencil": <?php echo xlj('Pencil'); ?>,
287 "Line": <?php echo xlj('Line'); ?>,
288 "Rectangle": <?php echo xlj('Rectangle'); ?>,
289 "Ellipse": <?php echo xlj('Ellipse'); ?>,
290 "Text": <?php echo xlj('Text'); ?>,
291 "Polygon": <?php echo xlj('Polygon'); ?>,
292 "Pan": <?php echo xlj('Pan'); ?>,
293 "Eyedropper": <?php echo xlj('Eyedropper'); ?>,
294 "Undo": <?php echo xlj('Undo'); ?>,
295 "Redo": <?php echo xlj('Redo'); ?>,
296 "Zoom out": <?php echo xlj('Zoom out'); ?>,
297 "Zoom in": <?php echo xlj('Zoom in'); ?>,
299 var tmpImage = document.getElementById(canid + '_img');
300 var shape = LC.createShape('Image', {x: 0, y: 0, image: tmpImage});
301 var lc = LC.init(document.getElementById(canid), {
302 imageSize: {width: canWidth, height: canHeight},
303 strokeWidths: [1, 2, 3, 5, 8, 12],
304 defaultStrokeWidth: 2,
305 backgroundShapes: [shape],
306 imageURLPrefix: '<?php echo $GLOBALS['assets_static_relative'] ?>/literallycanvas/img'
308 if (canHeight > 261) {
309 // TBD: Do something to make the widget bigger?
310 // Look for some help with this in the next LC release.
312 // lc.saveShape(shape); // alternative to the above backgroundShapes
313 lbfCanvases[canid] = lc;
316 // This returns a standard "Data URL" string representing the image data.
317 // It will typically be a few kilobytes. Here's a truncated example:
318 // data:image/png;base64,iVBORw0K ...
320 function lbfCanvasGetData(canid) {
321 return lbfCanvases[canid].getImage().toDataURL();
324 // set signture to hidden element for this img
325 function lbfSetSignature(el) {
326 let imgel = el + "_img";
327 let sign = $("#"+ imgel).attr('src');
328 $("#"+ el).val(sign);
331 // This is invoked when a field with edit option M is changed.
332 // Its purpose is to make the corresponding change to the member fields (edit option m).
334 function checkGroupMembers(elem, groupnumber) {
335 var i = elem.id.indexOf('[');
336 if (i < 0) {
337 alert(<?php echo xlj('Field not suitable for edit option M') ?> + ': ' + elem.name);
338 return;
340 var suffix = elem.id.substring(i);
341 var members = document.getElementsByClassName('lbf_memgroup_' + groupnumber);
342 if (members.length == 0) {
343 alert(<?php echo xlj('No member fields found for') ?> + ': ' + elem.name);
344 return;
346 for (var i = 0; i < members.length; ++i) {
347 if (members[i].id.indexOf(suffix) > 1) {
348 members[i].checked = true;
353 // Support for patient finder. References to the input elements.
354 var elem_patient_name;
355 var elem_patient_id;
357 // This is for callback by the find-patient popup.
358 function setpatient(pid, lname, fname, dob) {
359 elem_patient_name.value = lname + ', ' + fname + ' (' + pid + ')';
360 elem_patient_id.value = pid;
363 // This invokes the find-patient popup.
364 function sel_patient(ename, epid) {
365 elem_patient_name = ename;
366 elem_patient_id = epid;
367 dlgopen('<?php echo $GLOBALS['webroot']; ?>/interface/main/calendar/find_patient_popup.php', '_blank', 500, 400);
370 // This is a wrapper for specialty forms dialog
371 // ajax mode allows calling script to be in the same scope as options.inc.php.
372 function specialtyFormDialog(mode = 'iframe', size = 'modal-sm', formHandler = 'name_history') {
373 event.preventDefault();
374 let url = '<?php echo $GLOBALS['webroot']; ?>/library/specialty_forms.php?';
375 url += "form_handler=" + encodeURIComponent(formHandler);
376 url += "&csrf_token_form=" + <?php echo js_escape(CsrfUtils::collectCsrfToken()); ?>;
377 let title = xl("Add to History");
378 dlgopen('', '', size, 500, '', '', {
379 allowResize: true,
380 allowDrag: true,
381 sizeHeight: 'auto',
382 dialogId: '',
383 type: mode,
384 url: url
387 </script>