/*
Development Team: Ascension Technologies WebBased - IT
Developed By: Budry, Derek
Developed: 11 / 29 / 2018
Developer Instructions:
To use this library, you must add the "PriceTransparency.js" script within the node or element that you want the content to dynamical load.
The source will automatically identify the parent elemnent even if it is only the html < body > tag.
This script will inject two select drop down menus as well as a data list and will be in the following order whereever the script tag is applied:
This script will also inject a style tag in the header with custom css3 styles for the modal window that must appear for the dislclaimer. It will also inject a modal window within the body of the document that will appear as:
*/
//================= VANILLA MODAL CODE BEGIN ==================================================================
var vanillaCss = " \
.vanilla-overlay \
{ \
position: fixed; \
z-index: 9998; \
top: 0; \
left: 0; \
opacity: 0; \
width: 100%; \
height: 100%; \
-webkit-transition: 1ms opacity ease; \
-moz-transition: 1ms opacity ease; \
-ms-transition: 1ms opacity ease; \
-o-transition: 1ms opacity ease; \
transition: 1ms opacity ease; \
background: rgba(0,0,0,.6); \
} \
\
.vanilla-modal \
{ \
position: absolute; \
z-index: 9999; \
top: 50%; \
left: 50%; \
opacity: 0; \
width: 94%; \
padding: 24px 20px; \
-webkit-transition: 1ms opacity ease; \
-moz-transition: 1ms opacity ease; \
-ms-transition: 1ms opacity ease; \
-o-transition: 1ms opacity ease; \
transition: 1ms opacity ease; \
-webkit-transform: translate(-50%, -50%); \
-moz-transform: translate(-50%, -50%); \
-ms-transform: translate(-50%, -50%); \
-o-transform: translate(-50%, -50%); \
transform: translate(-50%, -50%); \
border-radius: 2px; \
background: #fff; \
} \
\
.vanilla-modal.vanilla-open.vanilla-anchored \
{ \
top: 20px; \
-webkit-transform: translate(-50%, 0); \
-moz-transform: translate(-50%, 0); \
-ms-transform: translate(-50%, 0); \
-o-transform: translate(-50%, 0); \
transform: translate(-50%, 0); \
} \
\
.vanilla-modal.vanilla-open \
{ \
opacity: 1; \
} \
\
.vanilla-overlay.vanilla-open \
{ \
opacity: 1; \
\
} \
\
.vanilla-close \
{ \
font-family: Helvetica, Arial, sans-serif; \
font-size: 24px; \
font-weight: 700; \
line-height: 12px; \
position: absolute; \
top: 5px; \
right: 5px; \
padding: 5px 7px 7px; \
cursor: pointer; \
color: #fff; \
border: 0; \
outline: none; \
background: #e74c3c; \
} \
\
.vanilla-close: hover \
{ \
background: #c0392b; \
} \
\
.vanilla-overlay.zoom-and-spin \
{ \
display: block; \
height: 0px; \
opacity: 0; \
} \
\
.vanilla-modal.zoom-and-spin \
{ \
-webkit-transition: 500ms-webkit-transform ease; \
-moz-transition: 500ms-moz-transform ease; \
-ms-transition: 500ms-ms-transform ease; \
-o-transition: 500ms-o-transform ease; \
transition: 500ms transform ease; \
-webkit-transform: translate(-50 %, -50 %) scale(0) rotateX(0deg); \
-moz-transform: translate(-50 %, -50 %) scale(0) rotateX(0deg); \
-ms-transform: translate(-50 %, -50 %) scale(0) rotateX(0deg); \
-o-transform: translate(-50 %, -50 %) scale(0) rotateX(0deg); \
transform: translate(-50 %, -50 %) scale(0) rotateX(0deg); \
opacity: 1; \
display: block; \
} \
\
.vanilla-modal.zoom-and-spin.vanilla-open \
{ \
-webkit-transition: 500ms-webkit-transform 500ms ease; \
-moz-transition: 500ms-moz-transform 500ms ease; \
-ms-transition: 500ms-ms-transform 500ms ease; \
-o-transition: 500ms-o-transform 500ms ease; \
transition: 500ms transform 500ms ease; \
-webkit-transform: translate(-50 %, -50 %) scale(1) rotateX(360deg);\
-moz-transform: translate(-50 %, -50 %) scale(1) rotateX(360deg); \
-ms-transform: translate(-50 %, -50 %) scale(1) rotateX(360deg); \
-o-transform: translate(-50 %, -50 %) scale(1) rotateX(360deg); \
transform: translate(-50 %, -50 %) scale(1) rotateX(360deg); \
} \
\
.vanilla-modal.zoom-and-spin.vanilla-open.vanilla-anchored \
{ \
-webkit-transition: 500ms-webkit-transform 500ms ease; \
-moz-transition: 500ms-moz-transform 500ms ease; \
-ms-transition: 500ms-ms-transform 500ms ease; \
-o-transition: 500ms-o-transform 500ms ease; \
transition: 500ms transform 500ms ease; \
} \
\
.vanilla-overlay.zoom-and-spin.vanilla-open \
{ \
top: 0; \
height: 100 %; \
-webkit-transition: 500ms all ease; \
-moz-transition: 500ms all ease; \
-ms-transition: 500ms all ease; \
-o-transition: 500ms all ease; \
transition: 500ms all ease; \
opacity: 1; \
} \
\
.vanilla-overlay.zoom-and-spin \
{ \
-webkit-transition: 500ms all 500ms ease; \
-moz-transition: 500ms all 500ms ease; \
-ms-transition: 500ms all 500ms ease; \
-o-transition: 500ms all 500ms ease; \
transition: 500ms all 500ms ease; \
} \
\
.vanilla-footer \
{ \
padding: 20px 0; \
} \
.vanilla-footer button \
{ \
border:1px solid #cccccc; \
width: 100px; \
padding: 10px; \
margin: 0 10px; \
float: right; \
} ";
// Create an immediately invoked functional expression to wrap our code
(function () {
// Define our constructor
this.VanillaModal = function () {
// Create global element references
this.closeButton = null;
this.modal = null;
this.overlay = null;
// Determine proper prefix
this.transitionEnd = transitionSelect();
// Define option defaults
var defaults = {
autoOpen: false,
autoInjectStyle: true,
className: 'fade-and-drop',
closeButton: true,
headerRender: true,
header: "",
content: "",
footerRender: true,
footer: "",
maxWidth: 600,
minWidth: 280,
overlay: true,
overlayAllowDismiss: true
};
// Create options by extending defaults with the passed in arugments
if (arguments[0] && typeof arguments[0] === "object") {
this.options = extendDefaults(defaults, arguments[0]);
}
if (this.options.autoOpen === true) this.open();
};
// Public Methods
VanillaModal.prototype.close = function () {
var _ = this;
this.modal.className = this.modal.className.replace(" vanilla-open", "");
this.overlay.className = this.overlay.className.replace(" vanilla-open",
"");
this.modal.addEventListener(this.transitionEnd, function () {
_.modal.parentNode.removeChild(_.modal);
});
this.overlay.addEventListener(this.transitionEnd, function () {
if (_.overlay.parentNode) _.overlay.parentNode.removeChild(_.overlay);
});
};
VanillaModal.prototype.open = function () {
buildOut.call(this);
initializeEvents.call(this);
window.getComputedStyle(this.modal).height;
this.modal.className = this.modal.className +
(this.modal.offsetHeight > window.innerHeight ?
" vanilla-open vanilla-anchored" : " vanilla-open");
this.overlay.className = this.overlay.className + " vanilla-open";
};
// Private Methods
function buildOut() {
var header,content,footer, contentHolder, docFrag;
/*
* If content is an HTML string, append the HTML string.
* If content is a domNode, append its content.
*/
if (typeof this.options.header === "string") {
header = this.options.header;
} else {
header = this.options.header.innerHTML;
}
if (typeof this.options.content === "string") {
content = this.options.content;
} else {
content = this.options.content.innerHTML;
}
if (typeof this.options.footer === "string") {
footer = this.options.footer;
} else {
footer = this.options.footer.innerHTML;
}
// Create a DocumentFragment to build with
docFrag = document.createDocumentFragment();
// Create modal element
this.modal = document.createElement("div");
this.modal.className = "vanilla-modal " + this.options.className;
this.modal.style.minWidth = this.options.minWidth + "px";
this.modal.style.maxWidth = this.options.maxWidth + "px";
// If closeButton option is true, add a close button
if (this.options.closeButton === true) {
this.closeButton = document.createElement("button");
this.closeButton.className = "vanilla-close close-button";
this.closeButton.innerHTML = "×";
this.modal.appendChild(this.closeButton);
}
// If overlay is true, add one
if (this.options.overlay === true) {
this.overlay = document.createElement("div");
this.overlay.className = "vanilla-overlay " + this.options.className;
docFrag.appendChild(this.overlay);
}
// Create Header area and append to modal
if (this.options.headerRender) {
contentHolder = document.createElement("div");
contentHolder.className = "vanilla-header";
contentHolder.innerHTML = header;
this.modal.appendChild(contentHolder);
}
// Create content area and append to modal
contentHolder = document.createElement("div");
contentHolder.className = "vanilla-content";
contentHolder.innerHTML = content;
this.modal.appendChild(contentHolder);
// Create Footer area and append to modal
if (this.options.footerRender) {
contentHolder = document.createElement("div");
contentHolder.className = "vanilla-footer";
contentHolder.innerHTML = footer;
this.modal.appendChild(contentHolder);
}
// Append modal to DocumentFragment
docFrag.appendChild(this.modal);
// Append DocumentFragment to body
document.body.appendChild(docFrag);
if (this.options.autoInjectStyle) {
var css = vanillaCss,
head = document.head || document.getElementsByTagName('head')[0],
style = document.createElement('style');
style.type = 'text/css';
if (style.styleSheet) {
// This is required for IE8 and below.
style.styleSheet.cssText = css;
} else {
style.appendChild(document.createTextNode(css));
}
head.appendChild(style);
}
}
function extendDefaults(source, properties) {
var property;
for (property in properties) {
if (properties.hasOwnProperty(property)) {
source[property] = properties[property];
}
}
return source;
}
function initializeEvents() {
if (this.closeButton) {
this.closeButton.addEventListener('click', this.close.bind(this));
}
if (this.overlay && this.overlayAllowDismiss) {
this.overlay.addEventListener('click', this.close.bind(this));
}
}
function transitionSelect() {
var el = document.createElement("div");
if (el.style.WebkitTransition) return "webkitTransitionEnd";
if (el.style.OTransition) return "oTransitionEnd";
return 'transitionend';
}
}());
//================= VANILLA MODAL CODE END ==================================================================
//================= PRICE TRANSPARENCY CODE BEGIN ===========================================================
var priceTransparency = {
details: {
ajaxConnections: 0,
confirmDisclaimer: true,
allowQueryParam: false,
parentElement: null,
hospital: {
url: 'https://pricetransparency.pub.cloud-03.pcf.ascension.org/api/Hospital',
id: "ddlHospitals"
},
department: {
url: 'https://pricetransparency.pub.cloud-03.pcf.ascension.org/api/Department',
id: "ddlDepartments"
},
procedure: {
url: 'https://pricetransparency.pub.cloud-03.pcf.ascension.org/api/Procedure',
id: "dlProcedures"
}
},
initialize: function () {
// Identify Script Element Position on document
scripts = document.getElementsByTagName('script');
for (var i=0, end = scripts.length; i < end; i++) {
if (scripts[i].src.indexOf("PriceTransparency") > -1) { // ControllerName >> No Index
parentElement = scripts[i].parentNode;
}
}
if (parentElement !== null) {
// Create Housing Elements
this.builder.initialize(this.details);
// Add Event Listeners
this.listeners.hospital(this);
this.listeners.department(this);
// does page contain querystring of pthospital?
var pthospital = this.allowQueryParam ? this.utilities.getQuerystring("pthid") :"";
// Call First Ajax pull
this.utilities.getAjax(this.details.hospital.url + "/" + encodeURIComponent(pthospital), this.loadHospital, this);
} else {
//console.log("Unable to identify parent node for Price Transparency");
}
},
builder: {
initialize: function (details) {
// Get Parent Container Element
// Build Hospital Select
this.buildElement(details.hospital.id, "select", parentElement);
this.resetElement(document.getElementById(details.hospital.id));
// Build Department Select
this.buildElement(details.department.id, "select", parentElement);
this.resetElement(document.getElementById(details.department.id));
// Build Procedure Data List
this.buildElement(details.procedure.id, "dl", parentElement);
},
buildElement: function (id, elementType, parentElement) {
var element = document.createElement(elementType);
element.setAttribute("id", id);
parentElement.appendChild(element);
},
buildSelectOption: function (element, text, value) {
var option = document.createElement('option');
option.setAttribute('value', value);
var option_content = document.createTextNode(text);
option.appendChild(option_content);
element.appendChild(option);
},
buildDlItem: function (element, dtText, ddText) {
var dt = document.createElement('dt');
var dt_content = document.createTextNode(dtText);
dt.appendChild(dt_content);
var dd = document.createElement("dd");
var dd_content = document.createTextNode(ddText);
dd.appendChild(dd_content);
element.appendChild(dt);
element.appendChild(dd);
},
resetElement: function (element) {
element.innerHTML = "";
element.disabled = true;
}
},
loadHospital: function (data, objThis) {
try {
hospitalElement = document.getElementById(objThis.details.hospital.id);
hospitalElement.disabled = false;
objThis.builder.buildSelectOption(hospitalElement, "Select Hospital", "");
// Can have two data results. Direct hit or the database table. Check which is which
var directhit = data.cdm !== undefined ? true : false;
if (!directhit) {
for (var i in data) {
objThis.builder.buildSelectOption(hospitalElement, data[i].value, data[i].key);
}
} else {
objThis.builder.buildSelectOption(hospitalElement, data.name, data.cdm);
hospitalElement.selectedIndex = 1;
hospitalElement.dispatchEvent(new Event('change'));
}
} catch (err) {
//console.log(err.message + " in loading hospital drop down list");
}
},
loadHospitalDisclaimer: function (data, objThis) {
try {
var modalHeader = "
" + data.name + " Disclaimer:
";
var modalFooter = "";
var modalContent = data.disclaimer;
var vmodal = new VanillaModal({
header: modalHeader,
content: modalContent,
footer: modalFooter,
allowOverlayClose: false,
closeButton: false
});
vmodal.open();
document.getElementById("vanilla-button-ok").addEventListener("click", function () {
var departmentUrl = objThis.details.department.url + "?hospital=" + encodeURIComponent(data.cdm);
objThis.utilities.getAjax(departmentUrl, objThis.loadDepartment, objThis);
vmodal.close();
});
document.getElementById("vanilla-button-cancel").addEventListener("click", function () {
document.getElementById(objThis.details.hospital.id).selectedIndex = 0;
vmodal.close();
});
} catch (err) {
//console.log(err.message + " in loading hospital price disclaimer");
}
},
loadDepartment: function (data, objThis) {
try {
departmentElement = document.getElementById(objThis.details.department.id);
departmentElement.disabled = false;
objThis.builder.buildSelectOption(departmentElement, "Select Department", "");
for (var i in data) {
objThis.builder.buildSelectOption(departmentElement, data[i].value, data[i].key);
}
departmentElement.focus();
} catch (err) {
//console.log(err.message + " in loading departments drop down list");
}
},
loadProcedures: function (data, objThis) {
try {
procedureElement = document.getElementById(objThis.details.procedure.id);
for (var i in data) {
try {
var primary_formated = objThis.utilities.formatMoney(data[i].primary);
objThis.builder.buildDlItem(procedureElement, data[i].descr, "$" + primary_formated);
}
catch (err) {
//console.log(err.message + " in formating procdure amount into currency");
}
}
} catch (err) {
//console.log(err.message + " in loading departments drop down list");
}
},
listeners: {
hospital: function (objThis) {
var hospitalElement = document.getElementById(objThis.details.hospital.id);
var departmentElement = document.getElementById(objThis.details.department.id);
var procedureElement = document.getElementById(objThis.details.procedure.id);
hospitalElement.addEventListener("change", function () {
// Reset Departments
objThis.builder.resetElement(departmentElement);
objThis.builder.resetElement(procedureElement);
if (this.value.length) {
if (objThis.details.confirmDisclaimer) {
// If value is not null, get hospital disclaimer
var hospitalUrl = objThis.details.hospital.url + "/" + encodeURIComponent(this.value);
objThis.utilities.getAjax(hospitalUrl, objThis.loadHospitalDisclaimer, objThis);
} else {
// If value is not null, get departments
var departmentUrl = objThis.details.department.url + "?hospital=" + encodeURIComponent(this.value);
objThis.utilities.getAjax(departmentUrl, objThis.loadDepartment, objThis);
}
}
});
},
department: function (objThis) {
var hospitalElement = document.getElementById(objThis.details.hospital.id);
var departmentElement = document.getElementById(objThis.details.department.id);
var procedureElement = document.getElementById(objThis.details.procedure.id);
departmentElement.addEventListener("change", function () {
// Reset Procedures
objThis.builder.resetElement(procedureElement);
// If value is not null, get procedures
if (this.value.length > 0) {
var procedureUrl = objThis.details.procedure.url + "?hospital=" + encodeURIComponent(hospitalElement.options[hospitalElement.selectedIndex].value);
procedureUrl += "&department=" + encodeURIComponent(this.value);
objThis.utilities.getAjax(procedureUrl, objThis.loadProcedures, objThis);
}
});
}
},
utilities: {
formatMoney: function (n, c, d, t) {
c = isNaN(c = Math.abs(c)) ? 2 : c,
d = d == undefined ? "." : d,
t = t == undefined ? "," : t,
s = n < 0 ? "-" : "",
i = String(parseInt(n = Math.abs(Number(n) || 0).toFixed(c))),
j = (j = i.length) > 3 ? j % 3 : 0;
return s + (j ? i.substr(0, j) + t : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) + (c ? d + Math.abs(n - i).toFixed(c).slice(2) : "");
},
getQuerystring: function (name) {
name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
var regex = new RegExp('[\\?&]' + name + '=([^]*)');
var results = regex.exec(location.search);
return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
},
getAjax: function (url, callback, objThis) {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState === 4 && xmlhttp.status === 200) {
try {
var data = JSON.parse(xmlhttp.responseText);
} catch (err) {
//console.log(err.message + " in " + xmlhttp.responseText);
return;
}
objThis.details.ajaxConnections--;
callback(data, objThis);
}
};
if (objThis.details.ajaxConnections === 0) {
objThis.details.ajaxConnections++;
xmlhttp.open("GET", url, true);
xmlhttp.send();
}
}
}
};
priceTransparency.initialize();
//================= PRICE TRANSPARENCY CODE END ===========================================================