You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
273 lines
10 KiB
273 lines
10 KiB
/** |
|
* reasonCodeWidget is used with observation,vitals, and procedures to toggle and select reason codes and their accompanying |
|
* statii / text descriptions. It requires a launch button to be present with the selector attribute of .reason-code-btn |
|
* |
|
* The launch button must have a data-toggle-container property with the id of the DOM node containing the reason window |
|
* that you want to be displayed on the screen. The widget will setup the code selector widget on the input that has |
|
* a class of .code-selector-popup. It will also populate the following fields if they are present in the DOM: |
|
* |
|
* .code-selector-popup -> text code that will be sent to the server. In the format of CODE_SYSTEM:CODE |
|
* .code-selector-text-display -> text description of the selected code or empty if code is deleted |
|
* .code-selector-text -> text descriptoin of the selected code or empty (intended to be a hidden attribute to be sent to the server) |
|
* |
|
* If the reasonCodeWidget is toggled off it will clear all of the reason code values inside the container. |
|
* |
|
* Widgets can be instantiated by calling the init() method, cleanup is done via the destroy() method. If you are manipulating |
|
* the DOM (copying, moving, etc) and want to clear event handlers and reinstantiate you can call the reload() method. |
|
* |
|
* Note only one code selector window can be instantiated at a time. The widget will override the default set_related callback |
|
* in the document window and restore it once the find code popup window closes. |
|
* @package openemr |
|
* @link http://www.open-emr.org |
|
* @author Stephen Nielson <snielson@discoverandchange.com> |
|
* @copyright Copyright (c) 2022 Discover and Change <snielson@discoverandchange.com> |
|
* @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3 |
|
*/ |
|
|
|
(function(window, oeUI) { |
|
|
|
let widgetPtrs = []; |
|
let previousSelCodeFunction = window.set_related || null; |
|
let webroot = null; |
|
let reasonCodeTypes = ""; |
|
let currentWindowOpenWidget = null; |
|
|
|
function getNegationContainerFromWidget(widget) { |
|
if (!(widget && widget.dataset)) { |
|
window.console.error("Failed to find reason code button"); |
|
return false; |
|
} |
|
let containerId = widget.dataset['toggleContainer']; |
|
if (!containerId) { |
|
window.console.error("Missing data-toggle-container"); |
|
return false; |
|
} |
|
let node = document.getElementById(containerId); |
|
if (!node) { |
|
console.error("Failed to find reason widget container node with id " + containerId); |
|
} |
|
return node; |
|
} |
|
|
|
function noop() { |
|
// do nothing here. |
|
} |
|
|
|
/** |
|
* Represents a single instance of a ReasonCode Widget. Tracks the dom nodes and event callbacks associated |
|
* with this instance. |
|
* @param btnToggleNode |
|
* @constructor |
|
*/ |
|
function Widget(btnToggleNode) { |
|
var _this = this; |
|
|
|
this.btnToggle = btnToggleNode; |
|
this.reasonCodeContainer = getNegationContainerFromWidget(btnToggleNode); |
|
this.reasonCodeInput = null; |
|
this.reasonCodeText = null; |
|
this.reasonCodeTextInput = null; |
|
this.reasonStatusNode = null; |
|
|
|
this.handleCodeSelected = function(codetype, code, selector, codedesc) { |
|
// restore this first thing. |
|
_this.restoreCallback(); // if there is any error we want to restore our window callback first thing. |
|
if (typeof codetype != "string") { |
|
// called externally so we have to check this |
|
console.error("codetype was invalid data type"); |
|
} |
|
if (typeof code != "string") { |
|
console.error("code was invalid data type"); |
|
} |
|
if (typeof codedesc != "string") { |
|
console.error("codedesc was invalid data type"); |
|
} |
|
|
|
if (_this.reasonCodeInput) { |
|
if (codetype.trim() !== "") { |
|
_this.reasonCodeInput.value = (codetype + ":" + code); |
|
} else { |
|
_this.reasonCodeInput.value = ""; |
|
} |
|
} |
|
if (_this.reasonCodeTextInput) { |
|
_this.reasonCodeTextInput.value = (codedesc || "").trim(); |
|
} |
|
// hide out our text if we arempty |
|
if (_this.reasonCodeText) { |
|
_this.reasonCodeText.innerText = (codedesc || "").trim(); |
|
if (codedesc.trim() != "") { |
|
_this.reasonCodeText.classList.remove("d-none"); |
|
} else { |
|
_this.reasonCodeText.classList.add("d-none"); |
|
} |
|
} |
|
}; |
|
|
|
this.restoreCallback = function() { |
|
// could be undefined but we need this for the code selector anyways |
|
window.set_related = previousSelCodeFunction; |
|
}; |
|
|
|
this.launchCodeSelector = function() { |
|
let opts = { |
|
callBack: { |
|
call: _this.restoreCallback |
|
} |
|
}; |
|
// we are going to replace our global function for the time, and it will get setback in the callback |
|
previousSelCodeFunction = window.set_related; |
|
window.set_related = _this.handleCodeSelected; |
|
window.top.restoreSession(); |
|
let urlFragment = "?default=SNOMED-CT"; |
|
if (reasonCodeTypes) { |
|
urlFragment = "?codetype=" + reasonCodeTypes; |
|
} |
|
window.dlgopen(webroot + "/interface/patient_file/encounter/find_code_popup.php" + urlFragment |
|
, '_blank', 700, 400, false, undefined, opts); |
|
}; |
|
|
|
this.destroy = function() { |
|
if (_this.reasonCodeInput) { |
|
_this.reasonCodeInput.removeEventListener('click', _this.launchCodeSelector); |
|
} |
|
if (_this.btnToggle) { |
|
_this.btnToggle.removeEventListener('click', toggleReasonCode); |
|
} |
|
_this.reasonCodeInput = null; |
|
_this.reasonCodeText = null; |
|
_this.reasonCodeTextInput = null; |
|
_this.reasonStatusNode = null; |
|
_this.btnToggle = null; |
|
_this.reasonCodeContainer = null; |
|
}; |
|
|
|
this.show = function() { |
|
toggleDisplay(true); |
|
}; |
|
|
|
this.hide = function() { |
|
toggleDisplay(false); |
|
}; |
|
|
|
function toggleDisplay(shouldDisplay) { |
|
if (shouldDisplay) { |
|
_this.reasonCodeContainer.classList.remove("d-none"); |
|
} else { |
|
_this.reasonCodeContainer.classList.add("d-none"); |
|
resetValues(); |
|
} |
|
} |
|
|
|
function resetValues() { |
|
// reset our values here |
|
if (_this.reasonCodeInput) { |
|
_this.reasonCodeInput.value = ""; |
|
|
|
} |
|
if (_this.reasonCodeText) { |
|
_this.reasonCodeText.innerText = ""; |
|
_this.reasonCodeText.classList.add("d-none"); |
|
} |
|
if (_this.reasonCodeTextInput) { |
|
_this.reasonCodeTextInput.value = ""; |
|
} |
|
if (_this.reasonStatusNode) { |
|
_this.reasonStatusNode.selectedIndex = 0; |
|
} |
|
} |
|
|
|
function toggleReasonCode(event) { |
|
event.preventDefault(); |
|
var target = event.currentTarget; |
|
if (!(target && target.dataset)) { |
|
window.console.error("Failed to find reason code button"); |
|
return false; |
|
} |
|
toggleDisplay(_this.reasonCodeContainer.classList.contains("d-none")) |
|
return false; |
|
} |
|
|
|
function init() { |
|
|
|
if (_this.reasonCodeContainer) { |
|
_this.reasonCodeInput = _this.reasonCodeContainer.querySelector('.code-selector-popup'); |
|
if (_this.reasonCodeInput) { |
|
_this.reasonCodeInput.addEventListener('click', _this.launchCodeSelector); |
|
} else { |
|
console.error("Failed to find input node with selector .code-selector-popup"); |
|
} |
|
_this.reasonCodeText = _this.reasonCodeContainer.querySelector('.code-selector-text-display'); |
|
_this.reasonCodeTextInput = _this.reasonCodeContainer.querySelector('.code-selector-text'); |
|
_this.reasonStatusNode = _this.reasonCodeContainer.querySelector('select'); |
|
} else { |
|
window.console.error("Failed to find reasonCodeContainer ") |
|
} |
|
|
|
if (_this.btnToggle && btnToggleNode.addEventListener) { |
|
_this.btnToggle.addEventListener('click', toggleReasonCode); |
|
} |
|
else { |
|
window.console.error("Widget called to be setup but was null"); |
|
} |
|
|
|
} |
|
init(); |
|
} |
|
|
|
function destroyWidget(widget) { |
|
if (widget && widget.removeEventListener) { |
|
widget.removeEventListener('click', toggleNegationRationale); |
|
} else { |
|
window.console.error("Widget called to be destroyed but was not a valid widget", widget); |
|
} |
|
} |
|
|
|
function destroyWidgetList() { |
|
if (widgetPtrs && widgetPtrs.length) { |
|
widgetPtrs.forEach(function(widget) { |
|
widget.destroy(); |
|
}); |
|
} |
|
widgetPtrs = []; |
|
} |
|
|
|
function reload() { |
|
destroy(); |
|
init(webroot); // make sure we keep the same webroot here. |
|
} |
|
|
|
function init(webRootValue, reasonCodeTypes) { |
|
widgetPtrs = []; |
|
// destroy if we have anything setup right now |
|
widgetPtrNodes = document.querySelectorAll('.reason-code-btn'); |
|
if (!(widgetPtrNodes && widgetPtrNodes.forEach)) { |
|
return; // nothing to do here |
|
} |
|
widgetPtrNodes.forEach(function(node) { |
|
let widget = new Widget(node); |
|
widgetPtrs.push(widget); |
|
}); |
|
webroot = webRootValue; |
|
} |
|
function destroy() { |
|
destroyWidgetList(); |
|
} |
|
|
|
function getWidgetForNode(node) { |
|
return widgetPtrs.filter(function(widget) { |
|
return widget.btnToggle == node; |
|
}); |
|
} |
|
|
|
// any other functions can go here |
|
oeUI.reasonCodeWidget = { |
|
init: init |
|
,destroy: destroy |
|
,reload: reload |
|
,getWidget: getWidgetForNode |
|
}; |
|
window.oeUI = oeUI; |
|
} |
|
// we wrap everything into an oeUI so we don't get namespace clashes. |
|
)(window, window.oeUI || {}); |