angular.module('FredrikSandell.worker-pool', []).service('WorkerService', [ '$q', function ($q) { var that = {}; //this should be configured from the app in the future var urlToAngular = 'http://localhost:9876/base/bower_components/angular/angular.js'; var serviceToUrlMap = {}; var storage = {}; var scriptsToLoad = []; that.setAngularUrl = function (urlToAngularJs) { urlToAngular = urlToAngularJs; }; function createAngularWorkerTemplate() { /*jshint laxcomma:true */ /*jshint quotmark: false */ var workerTemplate = [ '', '//try {', 'var window = self;', 'self.history = {};', 'var Node = function() {};', 'var app', 'var localStorage = {storage: , getItem: function(key) {return this.storage[key]}, setItem: function(key, value) {this.storage[key]=value}}', 'var document = {', ' readyState: \'complete\',', ' cookie: \'\',', ' querySelector: function () {},', ' createElement: function () {', ' return {', ' pathname: \'\',', ' setAttribute: function () {}', ' };', ' }', '};', 'importScripts(\'\');', '', 'angular = window.angular;', 'var workerApp = angular.module(\'WorkerApp\', []);', 'workerApp.run([\'$q\', function ($q) {', ' self.addEventListener(\'message\', function(e) {', ' var input = e.data;', ' var output = $q.defer();', ' var promise = output.promise;', ' promise.then(function(success) {', ' self.postMessage({event:\'success\', data : success});', ' }, function(reason) {', ' self.postMessage({event:\'failure\', data : reason});', ' }, function(update) {', ' self.postMessage({event:\'update\', data : update});', ' });', ' ;', ' });', ' self.postMessage({event:\'initDone\'});', '}]);', 'angular.bootstrap(null, [\'WorkerApp\']);', '//} catch(e) {self.postMessage(JSON.stringify(e));}' ]; return workerTemplate.join('\n'); } var workerTemplate = createAngularWorkerTemplate(); that.addDependency = function (serviceName, moduleName, url) { serviceToUrlMap[serviceName] = { url: url, moduleName: moduleName }; return that; }; that.includeScripts = function(url) { scriptsToLoad.push(url); }; that.addToLocalStorage = function(key, value) { storage[key] = value; }; function createIncludeStatements(listOfServiceNames) { var includeString = ''; angular.forEach(scriptsToLoad, function(script) { includeString += 'importScripts(\'' + script + '\');'; }); angular.forEach(listOfServiceNames, function (serviceName) { if (serviceToUrlMap[serviceName]) { includeString += 'importScripts(\'' + serviceToUrlMap[serviceName].url + '\');'; } }); return includeString; } function createModuleList(listOfServiceNames) { var moduleNameList = []; angular.forEach(listOfServiceNames, function (serviceName) { if (serviceToUrlMap[serviceName]) { moduleNameList.push('\'' + serviceToUrlMap[serviceName].moduleName + '\''); } }); return moduleNameList.join(','); } function createDependencyMetaData(dependencyList) { var dependencyServiceNames = dependencyList.filter(function (dep) { return dep !== 'input' && dep !== 'output' && dep !== '$q'; }); var depMetaData = { dependencies: dependencyServiceNames, moduleList: createModuleList(dependencyServiceNames), angularDepsAsStrings: dependencyServiceNames.length > 0 ? ',' + dependencyServiceNames.map(function (dep) { return '\'' + dep + '\''; }).join(',') : '', angularDepsAsParamList: dependencyServiceNames.length > 0 ? ',' + dependencyServiceNames.join(',') : '', servicesIncludeStatements: createIncludeStatements(dependencyServiceNames) }; depMetaData.workerFuncParamList = 'input,output' + depMetaData.angularDepsAsParamList; return depMetaData; } function populateWorkerTemplate(workerFunc, dependencyMetaData) { return workerTemplate .replace('', urlToAngular) .replace('', dependencyMetaData.servicesIncludeStatements) .replace('', dependencyMetaData.moduleList) .replace('', dependencyMetaData.angularDepsAsStrings) .replace('', dependencyMetaData.angularDepsAsParamList) .replace('', JSON.stringify(storage)) .replace('', workerFunc.toString()); } var buildAngularWorker = function (initializedWorker) { var that = {}; that.worker = initializedWorker; that.run = function (input) { var deferred = $q.defer(); initializedWorker.addEventListener('message', function (e) { var eventId = e.data.event; //console.log(e.data); if (eventId === 'initDone') { throw 'Received worker initialization in run method. This should already have occurred!'; } else if (eventId === 'success') { deferred.resolve(e.data.data); } else if (eventId === 'failure') { deferred.reject(e.data.data); } else if (eventId === 'update') { deferred.notify(e.data.data); } else { deferred.reject(e); } }); initializedWorker.postMessage(input); return deferred.promise; }; that.terminate = function () { initializedWorker.terminate(); }; return that; }; var extractDependencyList = function (depFuncList) { return depFuncList.slice(0, depFuncList.length - 1); }; var workerFunctionToString = function (func, paramList) { return '(' + func.toString() + ')(' + paramList + ')'; }; /** * example call: * WorkerService.createAngularWorker(['input', 'output', '$http', function(input, output, $http) * {body of function}]); * Parameters "input" and "output" is required. Not defining them will cause a runtime error. * Declaring services to be injected, as '$http' is above, requires the web worker to be able to resolve them. * '$http' service is a part of the standard angular package which means it will resolve without additional information * since angular source is always loaded in the web worker. * But if a custom service was to be injected the WorkerService would need be be informed on how to resolve the. * @param depFuncList */ that.createAngularWorker = function (depFuncList) { //validate the input if (!Array.isArray(depFuncList) || depFuncList.length < 3 || typeof depFuncList[depFuncList.length - 1] !== 'function') { throw 'Input needs to be: [\'workerInput\',\'deferredOutput\'/*optional additional dependencies*/,\n' + ' function(workerInput, deferredOutput /*optional additional dependencies*/)\n' + ' {/*worker body*/}' + ']'; } var deferred = $q.defer(); var dependencyMetaData = createDependencyMetaData(extractDependencyList(depFuncList)); var blobURL = (window.webkitURL ? webkitURL : URL).createObjectURL(new Blob([populateWorkerTemplate(workerFunctionToString(depFuncList[depFuncList.length - 1], dependencyMetaData.workerFuncParamList), dependencyMetaData)], { type: 'application/javascript' })); var worker = new Worker(blobURL); //wait for the worker to load resources worker.addEventListener('message', function (e) { var eventId = e.data.event; console.log(e.data); if (eventId === 'initDone') { deferred.resolve(buildAngularWorker(worker)); } else { deferred.reject(e); } }); return deferred.promise; }; return that; } ]); 'use strict'; var Bahmni = Bahmni || {}; Bahmni.Common = Bahmni.Common || {}; Bahmni.Common.Util = Bahmni.Common.Util || {}; angular.module('bahmni.common.util', []) .provider('$bahmniCookieStore', [function () { var self = this; self.defaultOptions = {}; var fixedEncodeURIComponent = function (str) { return encodeURIComponent(str).replace(/[!'()*]/g, function (c) { return '%' + c.charCodeAt(0).toString(16); }); }; self.setDefaultOptions = function (options) { self.defaultOptions = options; }; self.$get = function () { return { get: function (name) { var jsonCookie = $.cookie(name); if (jsonCookie) { return angular.fromJson(decodeURIComponent(jsonCookie)); } return null; }, put: function (name, value, options) { options = $.extend({}, self.defaultOptions, options); $.cookie.raw = true; $.cookie(name, fixedEncodeURIComponent(angular.toJson(value)), options); }, remove: function (name, options) { options = $.extend({}, self.defaultOptions, options); $.removeCookie(name, options); } }; }; }]) ; var Bahmni = Bahmni || {}; Bahmni.Common = Bahmni.Common || {}; Bahmni.Common.Models = Bahmni.Common.Models || {}; angular.module('bahmni.common.models', []); 'use strict'; angular.module('bahmni.common.models') .factory('age', [function () { var dateUtil = Bahmni.Common.Util.DateUtil; var fromBirthDate = function (birthDate) { var today = dateUtil.now(); var period = dateUtil.diffInYearsMonthsDays(birthDate, today); return create(period.years, period.months, period.days); }; var create = function (years, months, days) { var isEmpty = function () { return !(this.years || this.months || this.days); }; return { years: years, months: months, days: days, isEmpty: isEmpty }; }; var calculateBirthDate = function (age) { var birthDate = dateUtil.now(); birthDate = dateUtil.subtractYears(birthDate, age.years); birthDate = dateUtil.subtractMonths(birthDate, age.months); birthDate = dateUtil.subtractDays(birthDate, age.days); return birthDate; }; return { fromBirthDate: fromBirthDate, create: create, calculateBirthDate: calculateBirthDate }; }] ); Bahmni.Common.AuditLogEventDetails = { "USER_LOGIN_SUCCESS": {eventType: "USER_LOGIN_SUCCESS", message: "USER_LOGIN_SUCCESS_MESSAGE"}, "USER_LOGIN_FAILED": {eventType: "USER_LOGIN_FAILED", message: "USER_LOGIN_FAILED_MESSAGE"}, "USER_LOGOUT_SUCCESS": {eventType: "USER_LOGOUT_SUCCESS", message: "USER_LOGOUT_SUCCESS_MESSAGE"}, "OPEN_VISIT": {eventType: "OPEN_VISIT", message: "OPEN_VISIT_MESSAGE"}, "EDIT_VISIT": {eventType: "EDIT_VISIT", message: "EDIT_VISIT_MESSAGE"}, "CLOSE_VISIT": {eventType: "CLOSE_VISIT", message: "CLOSE_VISIT_MESSAGE"}, "CLOSE_VISIT_FAILED": {eventType: "CLOSE_VISIT_FAILED", message: "CLOSE_VISIT_FAILED_MESSAGE"}, "EDIT_ENCOUNTER": {eventType: "EDIT_ENCOUNTER", message: "EDIT_ENCOUNTER_MESSAGE"}, "VIEWED_REGISTRATION_PATIENT_SEARCH": {eventType: "VIEWED_REGISTRATION_PATIENT_SEARCH", message: "VIEWED_REGISTRATION_PATIENT_SEARCH_MESSAGE"}, "VIEWED_NEW_PATIENT_PAGE": {eventType: "VIEWED_NEW_PATIENT_PAGE", message: "VIEWED_NEW_PATIENT_PAGE_MESSAGE"}, "REGISTER_NEW_PATIENT": {eventType: "REGISTER_NEW_PATIENT", message: "REGISTER_NEW_PATIENT_MESSAGE"}, "EDIT_PATIENT_DETAILS": {eventType: "EDIT_PATIENT_DETAILS", message: "EDIT_PATIENT_DETAILS_MESSAGE"}, "ACCESSED_REGISTRATION_SECOND_PAGE": {eventType: "ACCESSED_REGISTRATION_SECOND_PAGE", message: "ACCESSED_REGISTRATION_SECOND_PAGE_MESSAGE"}, "VIEWED_PATIENT_DETAILS": {eventType: "VIEWED_PATIENT_DETAILS", message: "VIEWED_PATIENT_DETAILS_MESSAGE"}, "PRINT_PATIENT_STICKER": {eventType: "PRINT_PATIENT_STICKER", message: "PRINT_PATIENT_STICKER_MESSAGE"}, "VIEWED_CLINICAL_PATIENT_SEARCH": {eventType: "VIEWED_CLINICAL_PATIENT_SEARCH", message: "VIEWED_PATIENT_SEARCH_MESSAGE"}, "VIEWED_CLINICAL_DASHBOARD": {eventType: "VIEWED_CLINICAL_DASHBOARD", message: "VIEWED_CLINICAL_DASHBOARD_MESSAGE"}, "VIEWED_OBSERVATIONS_TAB": {eventType: "VIEWED_OBSERVATIONS_TAB", message: "VIEWED_OBSERVATIONS_TAB_MESSAGE"}, "VIEWED_DIAGNOSIS_TAB": {eventType: "VIEWED_DIAGNOSIS_TAB", message: "VIEWED_DIAGNOSIS_TAB_MESSAGE"}, "VIEWED_TREATMENT_TAB": {eventType: "VIEWED_TREATMENT_TAB", message: "VIEWED_TREATMENT_TAB_MESSAGE"}, "VIEWED_DISPOSITION_TAB": {eventType: "VIEWED_DISPOSITION_TAB", message: "VIEWED_DISPOSITION_TAB_MESSAGE"}, "VIEWED_DASHBOARD_SUMMARY": {eventType: "VIEWED_DASHBOARD_SUMMARY", message: "VIEWED_DASHBOARD_SUMMARY_MESSAGE"}, "VIEWED_ORDERS_TAB": {eventType: "VIEWED_ORDERS_TAB", message: "VIEWED_ORDERS_TAB_MESSAGE"}, "VIEWED_BACTERIOLOGY_TAB": {eventType: "VIEWED_BACTERIOLOGY_TAB", message: "VIEWED_BACTERIOLOGY_TAB_MESSAGE"}, "VIEWED_INVESTIGATION_TAB": {eventType: "VIEWED_INVESTIGATION_TAB", message: "VIEWED_INVESTIGATION_TAB_MESSAGE"}, "VIEWED_SUMMARY_PRINT": {eventType: "VIEWED_SUMMARY_PRINT", message: "VIEWED_SUMMARY_PRINT_MESSAGE"}, "VIEWED_VISIT_DASHBOARD": {eventType: "VIEWED_VISIT_DASHBOARD", message: "VIEWED_VISIT_DASHBOARD_MESSAGE"}, "VIEWED_VISIT_PRINT": {eventType: "VIEWED_VISIT_PRINT", message: "VIEWED_VISIT_PRINT_MESSAGE"}, "VIEWED_DASHBOARD_OBSERVATION": {eventType: "VIEWED_DASHBOARD_OBSERVATION", message: "VIEWED_DASHBOARD_OBSERVATION_MESSAGE"}, "VIEWED_PATIENTPROGRAM": {eventType: "VIEWED_PATIENTPROGRAM", message: "VIEWED_PATIENTPROGRAM_MESSAGE"}, "RUN_REPORT": {eventType: "RUN_REPORT", message: "RUN_REPORT_MESSAGE"} }; 'use strict'; var Bahmni = Bahmni || {}; Bahmni.Common = Bahmni.Common || {}; (function () { var hostUrl = localStorage.getItem('host') ? ("https://" + localStorage.getItem('host')) : ""; var rootDir = localStorage.getItem('rootDir') || ""; var RESTWS = hostUrl + "/openmrs/ws/rest"; var RESTWS_V1 = hostUrl + "/openmrs/ws/rest/v1"; var BAHMNI_CORE = RESTWS_V1 + "/bahmnicore"; var EMRAPI = RESTWS + "/emrapi"; var BACTERIOLOGY = RESTWS_V1; var BASE_URL = hostUrl + "/bahmni_config/openmrs/apps/"; var CUSTOM_URL = hostUrl + "/implementation_config/openmrs/apps/"; var serverErrorMessages = [ { serverMessage: "Cannot have more than one active order for the same orderable and care setting at same time", clientMessage: "One or more drugs you are trying to order are already active. Please change the start date of the conflicting drug or remove them from the new prescription." }, { serverMessage: "[Order.cannot.have.more.than.one]", clientMessage: "One or more drugs you are trying to order are already active. Please change the start date of the conflicting drug or remove them from the new prescription." } ]; var representation = "custom:(uuid,name,names,conceptClass," + "setMembers:(uuid,name,names,conceptClass," + "setMembers:(uuid,name,names,conceptClass," + "setMembers:(uuid,name,names,conceptClass))))"; var unAuthenticatedReferenceDataMap = { "/openmrs/ws/rest/v1/location?tags=Login+Location&s=byTags&v=default": "LoginLocations", "/openmrs/ws/rest/v1/bahmnicore/sql/globalproperty?property=locale.allowed.list": "LocaleList" }; var authenticatedReferenceDataMap = { "/openmrs/ws/rest/v1/idgen/identifiertype": "IdentifierTypes", "/openmrs/module/addresshierarchy/ajax/getOrderedAddressHierarchyLevels.form": "AddressHierarchyLevels", "/openmrs/ws/rest/v1/bahmnicore/sql/globalproperty?property=mrs.genders": "Genders", "/openmrs/ws/rest/v1/bahmnicore/sql/globalproperty?property=bahmni.encountersession.duration": "encounterSessionDuration", "/openmrs/ws/rest/v1/bahmnicore/sql/globalproperty?property=bahmni.relationshipTypeMap": "RelationshipTypeMap", "/openmrs/ws/rest/v1/bahmnicore/config/bahmniencounter?callerContext=REGISTRATION_CONCEPTS": "RegistrationConcepts", "/openmrs/ws/rest/v1/relationshiptype?v=custom:(aIsToB,bIsToA,uuid)": "RelationshipType", "/openmrs/ws/rest/v1/personattributetype?v=custom:(uuid,name,sortWeight,description,format,concept)": "PersonAttributeType", "/openmrs/ws/rest/v1/entitymapping?mappingType=loginlocation_visittype&s=byEntityAndMappingType": "LoginLocationToVisitTypeMapping", "/openmrs/ws/rest/v1/bahmnicore/config/patient": "PatientConfig", "/openmrs/ws/rest/v1/concept?s=byFullySpecifiedName&name=Consultation+Note&v=custom:(uuid,name,answers)": "ConsultationNote", "/openmrs/ws/rest/v1/concept?s=byFullySpecifiedName&name=Lab+Order+Notes&v=custom:(uuid,name)": "LabOrderNotes", "/openmrs/ws/rest/v1/concept?s=byFullySpecifiedName&name=Impression&v=custom:(uuid,name)": "RadiologyImpressionConfig", "/openmrs/ws/rest/v1/concept?s=byFullySpecifiedName&name=All_Tests_and_Panels&v=custom:(uuid,name:(uuid,name),setMembers:(uuid,name:(uuid,name)))": "AllTestsAndPanelsConcept", "/openmrs/ws/rest/v1/concept?s=byFullySpecifiedName&name=Dosage+Frequency&v=custom:(uuid,name,answers)": "DosageFrequencyConfig", "/openmrs/ws/rest/v1/concept?s=byFullySpecifiedName&name=Dosage+Instructions&v=custom:(uuid,name,answers)": "DosageInstructionConfig", "/openmrs/ws/rest/v1/bahmnicore/sql/globalproperty?property=bahmni.encounterType.default": "DefaultEncounterType", "/openmrs/ws/rest/v1/concept?s=byFullySpecifiedName&name=Stopped+Order+Reason&v=custom:(uuid,name,answers)": "StoppedOrderReasonConfig", "/openmrs/ws/rest/v1/ordertype": "OrderType", "/openmrs/ws/rest/v1/bahmnicore/config/drugOrders": "DrugOrderConfig", "/openmrs/ws/rest/v1/bahmnicore/sql/globalproperty?property=drugOrder.drugOther": "NonCodedDrugConcept" }; authenticatedReferenceDataMap["/openmrs/ws/rest/v1/entitymapping?mappingType=location_encountertype&s=byEntityAndMappingType&entityUuid=" + (localStorage.getItem("LoginInformation") ? JSON.parse(localStorage.getItem("LoginInformation")).currentLocation.uuid : "")] = "LoginLocationToEncounterTypeMapping"; Bahmni.Common.Constants = { hostURL: hostUrl, dateFormat: "dd/mm/yyyy", dateDisplayFormat: "DD-MMM-YYYY", timeDisplayFormat: "hh:mm", emrapiDiagnosisUrl: EMRAPI + "/diagnosis", bahmniDiagnosisUrl: BAHMNI_CORE + "/diagnosis/search", bahmniDeleteDiagnosisUrl: BAHMNI_CORE + "/diagnosis/delete", diseaseTemplateUrl: BAHMNI_CORE + "/diseaseTemplates", AllDiseaseTemplateUrl: BAHMNI_CORE + "/diseaseTemplate", emrapiConceptUrl: EMRAPI + "/concept", encounterConfigurationUrl: BAHMNI_CORE + "/config/bahmniencounter", patientConfigurationUrl: BAHMNI_CORE + "/config/patient", drugOrderConfigurationUrl: BAHMNI_CORE + "/config/drugOrders", emrEncounterUrl: EMRAPI + "/encounter", encounterUrl: RESTWS_V1 + "/encounter", locationUrl: RESTWS_V1 + "/location", bahmniVisitLocationUrl: BAHMNI_CORE + "/visitLocation", bahmniOrderUrl: BAHMNI_CORE + "/orders", bahmniDrugOrderUrl: BAHMNI_CORE + "/drugOrders", bahmniDispositionByVisitUrl: BAHMNI_CORE + "/disposition/visit", bahmniDispositionByPatientUrl: BAHMNI_CORE + "/disposition/patient", bahmniSearchUrl: BAHMNI_CORE + "/search", bahmniLabOrderResultsUrl: BAHMNI_CORE + "/labOrderResults", bahmniEncounterUrl: BAHMNI_CORE + "/bahmniencounter", conceptUrl: RESTWS_V1 + "/concept", bahmniConceptAnswerUrl: RESTWS_V1 + "/bahmniconceptanswer", conceptSearchByFullNameUrl: RESTWS_V1 + "/concept?s=byFullySpecifiedName", visitUrl: RESTWS_V1 + "/visit", endVisitUrl: BAHMNI_CORE + "/visit/endVisit", endVisitAndCreateEncounterUrl: BAHMNI_CORE + "/visit/endVisitAndCreateEncounter", visitTypeUrl: RESTWS_V1 + "/visittype", patientImageUrlByPatientUuid: RESTWS_V1 + "/patientImage?patientUuid=", labResultUploadedFileNameUrl: "/uploaded_results/", visitSummaryUrl: BAHMNI_CORE + "/visit/summary", encounterModifierUrl: BAHMNI_CORE + "/bahmniencountermodifier", openmrsUrl: hostUrl + "/openmrs", loggingUrl: hostUrl + "/log/", idgenConfigurationURL: RESTWS_V1 + "/idgen/identifiertype", bahmniRESTBaseURL: BAHMNI_CORE + "", observationsUrl: BAHMNI_CORE + "/observations", obsRelationshipUrl: BAHMNI_CORE + "/obsrelationships", encounterImportUrl: BAHMNI_CORE + "/admin/upload/encounter", programImportUrl: BAHMNI_CORE + "/admin/upload/program", conceptImportUrl: BAHMNI_CORE + "/admin/upload/concept", conceptSetImportUrl: BAHMNI_CORE + "/admin/upload/conceptset", drugImportUrl: BAHMNI_CORE + "/admin/upload/drug", labResultsImportUrl: BAHMNI_CORE + "/admin/upload/labResults", referenceTermsImportUrl: BAHMNI_CORE + "/admin/upload/referenceterms", relationshipImportUrl: BAHMNI_CORE + "/admin/upload/relationship", conceptSetExportUrl: BAHMNI_CORE + "/admin/export/conceptset?conceptName=:conceptName", patientImportUrl: BAHMNI_CORE + "/admin/upload/patient", adminImportStatusUrl: BAHMNI_CORE + "/admin/upload/status", programUrl: RESTWS_V1 + "/program", programEnrollPatientUrl: RESTWS_V1 + "/bahmniprogramenrollment", programStateDeletionUrl: RESTWS_V1 + "/programenrollment", programEnrollmentDefaultInformation: "default", programEnrollmentFullInformation: "full", programAttributeTypes: RESTWS_V1 + "/programattributetype", relationshipTypesUrl: RESTWS_V1 + "/relationshiptype", personAttributeTypeUrl: RESTWS_V1 + "/personattributetype", diseaseSummaryPivotUrl: BAHMNI_CORE + "/diseaseSummaryData", allTestsAndPanelsConceptName: 'All_Tests_and_Panels', dosageFrequencyConceptName: 'Dosage Frequency', dosageInstructionConceptName: 'Dosage Instructions', stoppedOrderReasonConceptName: 'Stopped Order Reason', consultationNoteConceptName: 'Consultation Note', diagnosisConceptSet: 'Diagnosis Concept Set', radiologyOrderType: 'Radiology Order', radiologyResultConceptName: "Radiology Result", investigationEncounterType: "INVESTIGATION", validationNotesEncounterType: "VALIDATION NOTES", labOrderNotesConcept: "Lab Order Notes", impressionConcept: "Impression", qualifiedByRelationshipType: "qualified-by", dispositionConcept: "Disposition", dispositionGroupConcept: "Disposition Set", dispositionNoteConcept: "Disposition Note", ruledOutDiagnosisConceptName: 'Ruled Out Diagnosis', emrapiConceptMappingSource: "org.openmrs.module.emrapi", abbreviationConceptMappingSource: "Abbreviation", includeAllObservations: false, openmrsObsUrl: RESTWS_V1 + "/obs", openmrsObsRepresentation: "custom:(uuid,obsDatetime,value:(uuid,name:(uuid,name)))", admissionCode: 'ADMIT', dischargeCode: 'DISCHARGE', transferCode: 'TRANSFER', undoDischargeCode: 'UNDO_DISCHARGE', vitalsConceptName: "Vitals", heightConceptName: "HEIGHT", weightConceptName: "WEIGHT", bmiConceptName: "BMI", // TODO : shruthi : revove this when this logic moved to server side bmiStatusConceptName: "BMI STATUS", // TODO : shruthi : revove this when this logic moved to server side abnormalObservationConceptName: "IS_ABNORMAL", documentsPath: '/document_images', documentsConceptName: 'Document', miscConceptClassName: 'Misc', abnormalConceptClassName: 'Abnormal', unknownConceptClassName: 'Unknown', durationConceptClassName: 'Duration', conceptDetailsClassName: 'Concept Details', admissionEncounterTypeName: 'ADMISSION', dischargeEncounterTypeName: 'DISCHARGE', imageClassName: 'Image', videoClassName: 'Video', locationCookieName: 'bahmni.user.location', retrospectiveEntryEncounterDateCookieName: 'bahmni.clinical.retrospectiveEncounterDate', JSESSIONID: "JSESSIONID", rootScopeRetrospectiveEntry: 'retrospectiveEntry.encounterDate', patientFileConceptName: 'Patient file', serverErrorMessages: serverErrorMessages, currentUser: 'bahmni.user', retrospectivePrivilege: 'app:clinical:retrospective', locationPickerPrivilege: 'app:clinical:locationpicker', onBehalfOfPrivilege: 'app:clinical:onbehalf', nutritionalConceptName: 'Nutritional Values', messageForNoObservation: "NO_OBSERVATIONS_CAPTURED", messageForNoDisposition: "NO_DISPOSTIONS_AVAILABLE_MESSAGE_KEY", messageForNoFulfillment: "NO_FULFILMENT_MESSAGE", reportsUrl: "/bahmnireports", uploadReportTemplateUrl: "/bahmnireports/upload", ruledOutdiagnosisStatus: "Ruled Out Diagnosis", registartionConsultationPrivilege: 'app:common:registration_consultation_link', manageIdentifierSequencePrivilege: "Manage Identifier Sequence", closeVisitPrivilege: 'app:common:closeVisit', deleteDiagnosisPrivilege: 'app:clinical:deleteDiagnosis', viewPatientsPrivilege: 'View Patients', editPatientsPrivilege: 'Edit Patients', addVisitsPrivilege: 'Add Visits', deleteVisitsPrivilege: 'Delete Visits', grantProviderAccess: "app:clinical:grantProviderAccess", grantProviderAccessDataCookieName: "app.clinical.grantProviderAccessData", globalPropertyUrl: BAHMNI_CORE + "/sql/globalproperty", passwordPolicyUrl: BAHMNI_CORE + "/globalProperty/passwordPolicyProperties", fulfillmentConfiguration: "fulfillment", fulfillmentFormSuffix: " Fulfillment Form", noNavigationLinksMessage: "NO_NAVIGATION_LINKS_AVAILABLE_MESSAGE", conceptSetRepresentationForOrderFulfillmentConfig: representation, entityMappingUrl: RESTWS_V1 + "/entitymapping", encounterTypeUrl: RESTWS_V1 + "/encountertype", defaultExtensionName: "default", orderSetMemberAttributeTypeUrl: RESTWS_V1 + "/ordersetmemberattributetype", orderSetUrl: RESTWS_V1 + "/bahmniorderset", primaryOrderSetMemberAttributeTypeName: "Primary", bahmniBacteriologyResultsUrl: BACTERIOLOGY + "/specimen", bedFromVisit: RESTWS_V1 + "/beds", ordersUrl: RESTWS_V1 + "/order", formDataUrl: RESTWS_V1 + "/obs", providerUrl: RESTWS_V1 + "/provider", drugUrl: RESTWS_V1 + "/drug", orderTypeUrl: RESTWS_V1 + "/ordertype", userUrl: RESTWS_V1 + "/user", passwordUrl: RESTWS_V1 + "/password", formUrl: RESTWS_V1 + "/form", allFormsUrl: RESTWS_V1 + "/bahmniie/form/allForms", latestPublishedForms: RESTWS_V1 + "/bahmniie/form/latestPublishedForms", formTranslationsUrl: RESTWS_V1 + "/bahmniie/form/translations", sqlUrl: BAHMNI_CORE + "/sql", patientAttributeDateFieldFormat: "org.openmrs.util.AttributableDate", platform: "user.platform", RESTWS_V1: RESTWS_V1, baseUrl: BASE_URL, customUrl: CUSTOM_URL, faviconUrl: hostUrl + "/bahmni/favicon.ico", platformType: { other: 'other' }, numericDataType: "Numeric", encryptionType: { SHA3: 'SHA3' }, LoginInformation: 'LoginInformation', // orderSetSpecialUnits:["mg/kg","mg/m2"], ServerDateTimeFormat: 'YYYY-MM-DDTHH:mm:ssZZ', calculateDose: BAHMNI_CORE + "/calculateDose", unAuthenticatedReferenceDataMap: unAuthenticatedReferenceDataMap, authenticatedReferenceDataMap: authenticatedReferenceDataMap, rootDir: rootDir, dischargeUrl: BAHMNI_CORE + "/discharge", uuidRegex: "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}", eventlogFilterUrl: hostUrl + "/openmrs/ws/rest/v1/eventlog/filter", bahmniConnectMetaDataDb: "metaData", serverDateTimeUrl: "/cgi-bin/systemdate", loginText: "/bahmni_config/openmrs/apps/home/whiteLabel.json", auditLogUrl: RESTWS_V1 + "/auditlog", appointmentServiceUrl: RESTWS_V1 + "/appointmentService", conditionUrl: EMRAPI + '/condition', conditionHistoryUrl: EMRAPI + '/conditionhistory', followUpConditionConcept: 'Follow-up Condition', localeLangs: "/bahmni_config/openmrs/apps/home/locale_languages.json", privilegeRequiredErrorMessage: "PRIVILEGE_REQUIRED", defaultPossibleRelativeSearchLimit: 10 }; })(); 'use strict'; angular.module('bahmni.common.routeErrorHandler', ['ui.router']) .run(['$rootScope', function ($rootScope) { $rootScope.$on('$stateChangeError', function (event) { event.preventDefault(); }); }]); 'use strict'; angular.module('httpErrorInterceptor', []) .config(['$httpProvider', function ($httpProvider) { var interceptor = ['$rootScope', '$q', function ($rootScope, $q) { var serverErrorMessages = Bahmni.Common.Constants.serverErrorMessages; var showError = function (errorMessage) { var result = _.find(serverErrorMessages, function (listItem) { return listItem.serverMessage === errorMessage; }); if (_.isEmpty(result)) { $rootScope.$broadcast('event:serverError', errorMessage); } }; function stringAfter (value, searchString) { var indexOfFirstColon = value.indexOf(searchString); return value.substr(indexOfFirstColon + 1).trim(); } function getServerError (message) { return stringAfter(message, ':'); } function success (response) { return response; } function shouldRedirectToLogin (response) { var errorMessage = response.data.error ? response.data.error.message : response.data; if (errorMessage.search("HTTP Status 403 - Session timed out") > 0) { return true; } } function error (response) { var data = response.data; var unexpectedError = "There was an unexpected issue on the server. Please try again"; if (response.status === 500) { var errorMessage = data.error && data.error.message ? getServerError(data.error.message) : unexpectedError; showError(errorMessage); } else if (response.status === 409) { var errorMessage = data.error && data.error.message ? getServerError(data.error.message) : "Duplicate entry error"; showError(errorMessage); } else if (response.status === 0) { showError("Could not connect to the server. Please check your connection and try again"); } else if (response.status === 405) { showError(unexpectedError); } else if (response.status === 400) { var errorMessage = data.error && data.error.message ? data.error.message : (data.localizedMessage || "Could not connect to the server. Please check your connection and try again"); showError(errorMessage); } else if (response.status === 403) { var errorMessage = data.error && data.error.message ? data.error.message : unexpectedError; if (shouldRedirectToLogin(response)) { $rootScope.$broadcast('event:auth-loginRequired'); } else { showError(errorMessage); } } else if (response.status === 404) { if (!_.includes(response.config.url, "implementation_config") && !_.includes(response.config.url, "locale_") && !_.includes(response.config.url, "offlineMetadata")) { showError("The requested information does not exist"); } } return $q.reject(response); } return { response: success, responseError: error }; }]; $httpProvider.interceptors.push(interceptor); }]); 'use strict'; Bahmni.Common.Util.ArrayUtil = { chunk: function (array, chunkSize) { var chunks = []; for (var i = 0; i < array.length; i += chunkSize) { chunks.push(array.slice(i, i + chunkSize)); } return chunks; }, groupByPreservingOrder: function (records, groupingFunction, keyName, valueName) { var groups = []; records.forEach(function (record) { var recordKey = groupingFunction(record); var existingGroup = _.find(groups, function (group) { return group[keyName] === recordKey; }); if (existingGroup) { existingGroup[valueName].push(record); } else { var newGroup = {}; newGroup[keyName] = recordKey; newGroup[valueName] = [record]; groups.push(newGroup); } }); return groups; } }; 'use strict'; var Bahmni = Bahmni || {}; Bahmni.Auth = Bahmni.Auth || {}; angular.module('authentication', ['ui.router']); 'use strict'; Bahmni.Auth.User = function (user) { angular.extend(this, user); this.userProperties = user.userProperties || {}; this.favouriteObsTemplates = this.userProperties.favouriteObsTemplates ? this.userProperties.favouriteObsTemplates.split("###") : []; this.favouriteWards = this.userProperties.favouriteWards ? this.userProperties.favouriteWards.split("###") : []; this.recentlyViewedPatients = this.userProperties.recentlyViewedPatients ? JSON.parse(this.userProperties.recentlyViewedPatients) : []; this.toContract = function () { var user = angular.copy(this); user.userProperties.favouriteObsTemplates = this.favouriteObsTemplates.join("###"); user.userProperties.favouriteWards = this.favouriteWards.join("###"); user.userProperties.recentlyViewedPatients = JSON.stringify(this.recentlyViewedPatients); delete user.favouriteObsTemplates; delete user.favouriteWards; delete user.recentlyViewedPatients; return user; }; this.addDefaultLocale = function (locale) { this.userProperties['defaultLocale'] = locale; }; this.addToRecentlyViewed = function (patient, maxPatients) { if (!_.some(this.recentlyViewedPatients, {'uuid': patient.uuid})) { this.recentlyViewedPatients.unshift({ uuid: patient.uuid, name: patient.name, identifier: patient.identifier }); if (_.size(this.recentlyViewedPatients) >= maxPatients) { this.recentlyViewedPatients = _.take(this.recentlyViewedPatients, maxPatients); } } }; this.isFavouriteObsTemplate = function (conceptName) { return _.includes(this.favouriteObsTemplates, conceptName); }; this.toggleFavoriteObsTemplate = function (conceptName) { if (this.isFavouriteObsTemplate(conceptName)) { this.favouriteObsTemplates = _.without(this.favouriteObsTemplates, conceptName); } else { this.favouriteObsTemplates.push(conceptName); } }; this.isFavouriteWard = function (wardName) { return _.includes(this.favouriteWards, wardName); }; this.toggleFavoriteWard = function (wardName) { if (this.isFavouriteWard(wardName)) { this.favouriteWards = _.without(this.favouriteWards, wardName); } else { this.favouriteWards.push(wardName); } }; }; 'use strict'; angular.module('authentication') .service('userService', ['$rootScope', '$http', '$q', function ($rootScope, $http, $q) { var getUserFromServer = function (userName) { return $http.get(Bahmni.Common.Constants.userUrl, { method: "GET", params: { username: userName, v: "custom:(username,uuid,person:(uuid,),privileges:(name,retired),userProperties)" }, cache: false }); }; this.getUser = function (userName) { var deferrable = $q.defer(); getUserFromServer(userName).success(function (data) { deferrable.resolve(data); }).error(function () { deferrable.reject('Unable to get user data'); }); return deferrable.promise; }; this.savePreferences = function () { var deferrable = $q.defer(); var user = $rootScope.currentUser.toContract(); $http.post(Bahmni.Common.Constants.userUrl + "/" + user.uuid, {"uuid": user.uuid, "userProperties": user.userProperties}, { withCredentials: true }).then(function (response) { $rootScope.currentUser.userProperties = response.data.userProperties; deferrable.resolve(); }); return deferrable.promise; }; var getProviderFromServer = function (uuid) { return $http.get(Bahmni.Common.Constants.providerUrl, { method: "GET", params: { user: uuid }, cache: false }); }; this.getProviderForUser = function (uuid) { var deferrable = $q.defer(); getProviderFromServer(uuid).success(function (data) { if (data.results.length > 0) { var providerName = data.results[0].display.split("-")[1]; data.results[0].name = providerName ? providerName.trim() : providerName; deferrable.resolve(data); } else { deferrable.reject("UNABLE_TO_GET_PROVIDER_DATA"); } }).error(function () { deferrable.reject("UNABLE_TO_GET_PROVIDER_DATA"); }); return deferrable.promise; }; this.getPasswordPolicies = function () { return $http.get(Bahmni.Common.Constants.passwordPolicyUrl, { method: "GET", withCredentials: true }); }; }]); 'use strict'; angular.module('authentication') .config(['$httpProvider', function ($httpProvider) { var interceptor = ['$rootScope', '$q', function ($rootScope, $q) { function success (response) { return response; } function error (response) { if (response.status === 401) { $rootScope.$broadcast('event:auth-loginRequired'); } return $q.reject(response); } return { response: success, responseError: error }; }]; $httpProvider.interceptors.push(interceptor); }]).run(['$rootScope', '$window', '$timeout', function ($rootScope, $window, $timeout) { $rootScope.$on('event:auth-loginRequired', function () { $timeout(function () { $window.location = "../home/index.html#/login"; }); }); }]).service('sessionService', ['$rootScope', '$http', '$q', '$bahmniCookieStore', 'userService', function ($rootScope, $http, $q, $bahmniCookieStore, userService) { var sessionResourcePath = Bahmni.Common.Constants.RESTWS_V1 + '/session?v=custom:(uuid)'; var getAuthFromServer = function (username, password, otp) { var btoa = otp ? username + ':' + password + ':' + otp : username + ':' + password; return $http.get(sessionResourcePath, { headers: {'Authorization': 'Basic ' + window.btoa(btoa)}, cache: false }); }; this.resendOTP = function (username, password) { var btoa = username + ':' + password; return $http.get(sessionResourcePath + '&resendOTP=true', { headers: {'Authorization': 'Basic ' + window.btoa(btoa)}, cache: false }); }; var createSession = function (username, password, otp) { var deferrable = $q.defer(); destroySessionFromServer().success(function () { getAuthFromServer(username, password, otp).then(function (response) { if (response.status == 204) { deferrable.resolve({"firstFactAuthorization": true}); } deferrable.resolve(response.data); }, function (response) { if (response.status == 401) { deferrable.reject('LOGIN_LABEL_WRONG_OTP_MESSAGE_KEY'); } else if (response.status == 410) { deferrable.reject('LOGIN_LABEL_OTP_EXPIRED'); } else if (response.status == 429) { // Too many requests deferrable.reject('LOGIN_LABEL_MAX_FAILED_ATTEMPTS'); } deferrable.reject('LOGIN_LABEL_LOGIN_ERROR_MESSAGE_KEY'); }); }).error(function () { deferrable.reject('LOGIN_LABEL_LOGIN_ERROR_MESSAGE_KEY'); }); return deferrable.promise; }; var hasAnyActiveProvider = function (providers) { return _.filter(providers, function (provider) { return (provider.retired == undefined || provider.retired == "false"); }).length > 0; }; var self = this; var destroySessionFromServer = function () { return $http.delete(sessionResourcePath); }; var sessionCleanup = function () { delete $.cookie(Bahmni.Common.Constants.currentUser, null, {path: "/"}); delete $.cookie(Bahmni.Common.Constants.currentUser, null, {path: "/"}); delete $.cookie(Bahmni.Common.Constants.retrospectiveEntryEncounterDateCookieName, null, {path: "/"}); delete $.cookie(Bahmni.Common.Constants.grantProviderAccessDataCookieName, null, {path: "/"}); $rootScope.currentUser = undefined; }; this.destroy = function () { var deferrable = $q.defer(); destroySessionFromServer().then(function () { sessionCleanup(); deferrable.resolve(); }); return deferrable.promise; }; this.loginUser = function (username, password, location, otp) { var deferrable = $q.defer(); createSession(username, password, otp).then(function (data) { if (data.authenticated) { $bahmniCookieStore.put(Bahmni.Common.Constants.currentUser, username, {path: '/', expires: 7}); if (location != undefined) { $bahmniCookieStore.remove(Bahmni.Common.Constants.locationCookieName); $bahmniCookieStore.put(Bahmni.Common.Constants.locationCookieName, {name: location.display, uuid: location.uuid}, {path: '/', expires: 7}); } deferrable.resolve(data); } else if (data.firstFactAuthorization) { deferrable.resolve(data); } else { deferrable.reject('LOGIN_LABEL_LOGIN_ERROR_MESSAGE_KEY'); } }, function (errorInfo) { deferrable.reject(errorInfo); }); return deferrable.promise; }; this.get = function () { return $http.get(sessionResourcePath, { cache: false }); }; this.loadCredentials = function () { var deferrable = $q.defer(); var currentUser = $bahmniCookieStore.get(Bahmni.Common.Constants.currentUser); if (!currentUser) { this.destroy().finally(function () { $rootScope.$broadcast('event:auth-loginRequired'); deferrable.reject("No User in session. Please login again."); }); return deferrable.promise; } userService.getUser(currentUser).then(function (data) { userService.getProviderForUser(data.results[0].uuid).then(function (providers) { if (!_.isEmpty(providers.results) && hasAnyActiveProvider(providers.results)) { $rootScope.currentUser = new Bahmni.Auth.User(data.results[0]); $rootScope.currentUser.currentLocation = $bahmniCookieStore.get(Bahmni.Common.Constants.locationCookieName).name; $rootScope.$broadcast('event:user-credentialsLoaded', data.results[0]); deferrable.resolve(data.results[0]); } else { self.destroy(); deferrable.reject("YOU_HAVE_NOT_BEEN_SETUP_PROVIDER"); } }, function () { self.destroy(); deferrable.reject("COULD_NOT_GET_PROVIDER"); }); }, function () { self.destroy(); deferrable.reject('Could not get roles for the current user.'); }); return deferrable.promise; }; this.getLoginLocationUuid = function () { return $bahmniCookieStore.get(Bahmni.Common.Constants.locationCookieName) ? $bahmniCookieStore.get(Bahmni.Common.Constants.locationCookieName).uuid : null; }; this.changePassword = function (currentUserUuid, oldPassword, newPassword) { return $http({ method: 'POST', url: Bahmni.Common.Constants.passwordUrl, data: { "oldPassword": oldPassword, "newPassword": newPassword }, headers: {'Content-Type': 'application/json'} }); }; this.loadProviders = function (userInfo) { return $http.get(Bahmni.Common.Constants.providerUrl, { method: "GET", params: { user: userInfo.uuid }, cache: false }).success(function (data) { var providerUuid = (data.results.length > 0) ? data.results[0].uuid : undefined; $rootScope.currentProvider = { uuid: providerUuid }; }); }; }]).factory('authenticator', ['$rootScope', '$q', '$window', 'sessionService', function ($rootScope, $q, $window, sessionService) { var authenticateUser = function () { var defer = $q.defer(); var sessionDetails = sessionService.get(); sessionDetails.then(function (response) { if (response.data.authenticated) { defer.resolve(); } else { defer.reject('User not authenticated'); $rootScope.$broadcast('event:auth-loginRequired'); } }); return defer.promise; }; return { authenticateUser: authenticateUser }; }]).directive('logOut', ['sessionService', '$window', 'configurationService', 'auditLogService', function (sessionService, $window, configurationService, auditLogService) { return { link: function (scope, element) { element.bind('click', function () { scope.$apply(function () { auditLogService.log(undefined, 'USER_LOGOUT_SUCCESS', undefined, 'MODULE_LABEL_LOGOUT_KEY').then(function () { sessionService.destroy().then( function () { $window.location = "../home/index.html#/login"; }); }); }); }); } }; }]) .directive('btnUserInfo', [function () { return { restrict: 'CA', link: function (scope, elem) { elem.bind('click', function (event) { $(this).next().toggleClass('active'); event.stopPropagation(); }); $(document).find('body').bind('click', function () { $(elem).next().removeClass('active'); }); } }; } ]); angular.module('bahmni.common.config', []); 'use strict'; angular.module('bahmni.common.config') .service('configurations', ['configurationService', function (configurationService) { this.configs = {}; this.load = function (configNames) { var self = this; return configurationService.getConfigurations(_.difference(configNames, Object.keys(this.configs))).then(function (configurations) { angular.extend(self.configs, configurations); }); }; this.dosageInstructionConfig = function () { return this.configs.dosageInstructionConfig || []; }; this.stoppedOrderReasonConfig = function () { return this.configs.stoppedOrderReasonConfig || []; }; this.dosageFrequencyConfig = function () { return this.configs.dosageFrequencyConfig || []; }; this.allTestsAndPanelsConcept = function () { return this.configs.allTestsAndPanelsConcept.results[0] || []; }; this.impressionConcept = function () { return this.configs.radiologyImpressionConfig.results[0] || []; }; this.labOrderNotesConcept = function () { return this.configs.labOrderNotesConfig.results[0] || []; }; this.consultationNoteConcept = function () { return this.configs.consultationNoteConfig.results[0] || []; }; this.patientConfig = function () { return this.configs.patientConfig || {}; }; this.encounterConfig = function () { return angular.extend(new EncounterConfig(), this.configs.encounterConfig || []); }; this.patientAttributesConfig = function () { return this.configs.patientAttributesConfig.results; }; this.identifierTypesConfig = function () { return this.configs.identifierTypesConfig; }; this.genderMap = function () { return this.configs.genderMap; }; this.addressLevels = function () { return this.configs.addressLevels; }; this.relationshipTypes = function () { return this.configs.relationshipTypeConfig.results || []; }; this.relationshipTypeMap = function () { return this.configs.relationshipTypeMap || {}; }; this.loginLocationToVisitTypeMapping = function () { return this.configs.loginLocationToVisitTypeMapping || {}; }; this.defaultEncounterType = function () { return this.configs.defaultEncounterType; }; }]); 'use strict'; var Bahmni = Bahmni || {}; Bahmni.Common = Bahmni.Common || {}; Bahmni.Common.Domain = Bahmni.Common.Domain || {}; Bahmni.Common.Domain.Helper = Bahmni.Common.Domain.Helper || {}; angular.module('bahmni.common.domain', []); 'use strict'; (function () { var nameFor = { "Date": function (obs) { return moment(obs.value).format('D-MMM-YYYY'); }, "Datetime": function (obs) { var date = Bahmni.Common.Util.DateUtil.parseDatetime(obs.value); return date != null ? Bahmni.Common.Util.DateUtil.formatDateWithTime(date) : ""; }, "Boolean": function (obs) { return obs.value === true ? "Yes" : obs.value === false ? "No" : obs.value; }, "Coded": function (obs) { return obs.value.shortName || obs.value.name || obs.value; }, "Object": function (obs) { return nameFor.Coded(obs); }, "MultiSelect": function (obs) { return obs.getValues().join(", "); }, "Default": function (obs) { return obs.value; } }; Bahmni.Common.Domain.ObservationValueMapper = { getNameFor: nameFor, map: function (obs) { var type = (obs.concept && obs.concept.dataType) || obs.type; if (!(type in nameFor)) { type = (typeof obs.value === "object" && "Object") || (obs.isMultiSelect && "MultiSelect") || "Default"; } return (nameFor[type])(obs); } }; })(); angular.module('bahmni.common.appFramework', ['authentication']); var Bahmni = Bahmni || {}; Bahmni.Common = Bahmni.Common || {}; Bahmni.Common.AppFramework = Bahmni.Common.AppFramework || {}; 'use strict'; Bahmni.Common.AppFramework.AppDescriptor = function (context, inheritContext, retrieveUserCallback, mergeService) { this.id = null; this.instanceOf = null; this.description = null; this.contextModel = null; this.baseExtensionPoints = []; this.customExtensionPoints = []; this.baseExtensions = {}; this.customExtensions = {}; this.customConfigs = {}; this.baseConfigs = {}; this.extensionPath = context; this.contextPath = inheritContext ? context.split("/")[0] : context; var self = this; var setExtensionPointsFromExtensions = function (currentExtensions, currentExtensionPoints) { _.values(currentExtensions).forEach(function (extn) { if (extn) { var existing = self[currentExtensionPoints].filter(function (ep) { return ep.id === extn.extensionPointId; }); if (existing.length === 0) { self[currentExtensionPoints].push({ id: extn.extensionPointId, description: extn.description }); } } }); }; this.setExtensions = function (baseExtensions, customExtensions) { if (customExtensions) { setExtensionPointsFromExtensions(customExtensions, "customExtensionPoints"); self.customExtensions = customExtensions; } self.baseExtensions = baseExtensions; setExtensionPointsFromExtensions(baseExtensions, "baseExtensionPoints"); }; this.setTemplate = function (template) { self.instanceOf = template.id; self.description = self.description || template.description; self.contextModel = self.contextModel || template.contextModel; if (template.configOptions) { _.values(template.configOptions).forEach(function (opt) { var existing = self.configs.filter(function (cfg) { return cfg.name === opt.name; }); if (existing.length > 0) { existing[0].description = opt.description; } else { self.configs.push({ name: opt.name, description: opt.description, value: opt.defaultValue }); } }); } }; var setConfig = function (instance, currentConfig) { for (var configName in instance.config) { var existingConfig = getConfig(self[currentConfig], configName); if (existingConfig) { existingConfig.value = instance.config[configName]; } else { self[currentConfig][configName] = { name: configName, value: instance.config[configName] }; } } }; var setDefinitionExtensionPoints = function (extensionPoints, currentExtensionPoints) { if (extensionPoints) { extensionPoints.forEach(function (iep) { if (iep) { var existing = self[currentExtensionPoints].filter(function (ep) { return ep.id === iep.id; }); if (existing.length === 0) { self[currentExtensionPoints].push(iep); } } }); } }; this.setDefinition = function (baseInstance, customInstance) { self.instanceOf = (customInstance && customInstance.instanceOf) ? customInstance.instanceOf : baseInstance.instanceOf; self.id = (customInstance && customInstance.id) ? customInstance.id : baseInstance.id; self.description = (customInstance && customInstance.description) ? customInstance.description : baseInstance.description; self.contextModel = (customInstance && customInstance.contextModel) ? customInstance.contextModel : baseInstance.contextModel; setDefinitionExtensionPoints(baseInstance.extensionPoints, "baseExtensionPoints"); setConfig(baseInstance, "baseConfigs"); if (customInstance) { setDefinitionExtensionPoints(customInstance.extensionPoints, "customExtensionPoints"); setConfig(customInstance, "customConfigs"); } }; var getExtensions = function (extPointId, type, extensions) { var currentUser = retrieveUserCallback(); var currentExtensions = _.values(extensions); if (currentUser && currentExtensions) { var extnType = type || 'all'; var userPrivileges = currentUser.privileges.map(function (priv) { return priv.retired ? "" : priv.name; }); var appsExtns = currentExtensions.filter(function (extn) { return ((extnType === 'all') || (extn.type === extnType)) && (extn.extensionPointId === extPointId) && (!extn.requiredPrivilege || (userPrivileges.indexOf(extn.requiredPrivilege) >= 0)); }); appsExtns.sort(function (extn1, extn2) { return extn1.order - extn2.order; }); return appsExtns; } }; this.getExtensions = function (extPointId, type, shouldMerge) { if (shouldMerge || shouldMerge === undefined) { var mergedExtensions = mergeService.merge(self.baseExtensions, self.customExtensions); return getExtensions(extPointId, type, mergedExtensions); } return [getExtensions(extPointId, type, self.baseExtensions), getExtensions(extPointId, type, self.customExtensions)]; }; this.getExtensionById = function (id, shouldMerge) { if (shouldMerge || shouldMerge === undefined) { var mergedExtensions = _.values(mergeService.merge(self.baseExtensions, self.customExtensions)); return mergedExtensions.filter(function (extn) { return extn.id === id; })[0]; } else { return [self.baseExtensions.filter(function (extn) { return extn.id === id; })[0], self.customExtensions.filter(function (extn) { return extn.id === id; })[0]]; } }; var getConfig = function (config, configName) { var cfgList = _.values(config).filter(function (cfg) { return cfg.name === configName; }); return (cfgList.length > 0) ? cfgList[0] : null; }; this.getConfig = function (configName, shouldMerge) { if (shouldMerge || shouldMerge === undefined) { return getConfig(mergeService.merge(self.baseConfigs, self.customConfigs), configName); } else { return [getConfig(self.baseConfigs, configName), getConfig(self.customConfigs, configName)]; } }; this.getConfigValue = function (configName, shouldMerge) { var config = this.getConfig(configName, shouldMerge); if (shouldMerge || shouldMerge === undefined) { return config ? config.value : null; } return config; }; this.formatUrl = function (url, options, useQueryParams) { var pattern = /{{([^}]*)}}/g, matches = url.match(pattern), replacedString = url, checkQueryParams = useQueryParams || false, queryParameters = this.parseQueryParams(); if (matches) { matches.forEach(function (el) { var key = el.replace("{{", '').replace("}}", ''); var value = options[key]; if (!value && (checkQueryParams === true)) { value = queryParameters[key] || null; } replacedString = replacedString.replace(el, value); }); } return replacedString.trim(); }; this.parseQueryParams = function (locationSearchString) { var urlParams; var match, pl = /\+/g, // Regex for replacing addition symbol with a space search = /([^&=]+)=?([^&]*)/g, decode = function (s) { return decodeURIComponent(s.replace(pl, " ")); }, queryString = locationSearchString || window.location.search.substring(1); urlParams = {}; while (match = search.exec(queryString)) { // eslint-disable-line no-cond-assign urlParams[decode(match[1])] = decode(match[2]); } return urlParams; }; this.addConfigForPage = function (pageName, baseConfig, customConfig) { self.basePageConfigs = self.basePageConfigs || {}; self.basePageConfigs[pageName] = baseConfig; self.customPageConfigs = self.customPageConfigs || {}; self.customPageConfigs[pageName] = customConfig; }; this.getConfigForPage = function (pageName, shouldMerge) { if (shouldMerge || shouldMerge === undefined) { return mergeService.merge(self.basePageConfigs[pageName], self.customPageConfigs[pageName]); } return [_.values(self.basePageConfigs[pageName]), _.values(self.customPageConfigs[pageName])]; }; }; 'use strict'; angular.module('bahmni.common.appFramework') .config(['$compileProvider', function ($compileProvider) { $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|chrome-extension|file):/); }]) .service('appService', ['$http', '$q', 'sessionService', '$rootScope', 'mergeService', 'loadConfigService', 'messagingService', '$translate', function ($http, $q, sessionService, $rootScope, mergeService, loadConfigService, messagingService, $translate) { var currentUser = null; var baseUrl = Bahmni.Common.Constants.baseUrl; var customUrl = Bahmni.Common.Constants.customUrl; var appDescriptor = null; var loadConfig = function (url) { return loadConfigService.loadConfig(url, appDescriptor.contextPath); }; var loadTemplate = function (appDescriptor) { var deferrable = $q.defer(); loadConfig(baseUrl + appDescriptor.contextPath + "/appTemplate.json").then( function (result) { if (_.keys(result.data).length > 0) { appDescriptor.setTemplate(result.data); } deferrable.resolve(appDescriptor); }, function (error) { if (error.status !== 404) { deferrable.reject(error); } else { deferrable.resolve(appDescriptor); } } ); return deferrable.promise; }; var setDefinition = function (baseResultData, customResultData) { if (customResultData && (_.keys(baseResultData).length > 0 || _.keys(customResultData.length > 0))) { appDescriptor.setDefinition(baseResultData, customResultData); } else if (_.keys(baseResultData).length > 0) { appDescriptor.setDefinition(baseResultData); } }; var loadDefinition = function (appDescriptor) { var deferrable = $q.defer(); loadConfig(baseUrl + appDescriptor.contextPath + "/app.json").then( function (baseResult) { if (baseResult.data.shouldOverRideConfig) { loadConfig(customUrl + appDescriptor.contextPath + "/app.json").then(function (customResult) { setDefinition(baseResult.data, customResult.data); deferrable.resolve(appDescriptor); }, function () { setDefinition(baseResult.data); deferrable.resolve(appDescriptor); }); } else { setDefinition(baseResult.data); deferrable.resolve(appDescriptor); } }, function (error) { if (error.status !== 404) { deferrable.reject(error); } else { deferrable.resolve(appDescriptor); } }); return deferrable.promise; }; var setExtensions = function (baseResultData, customResultData) { if (customResultData) { appDescriptor.setExtensions(baseResultData, customResultData); } else { appDescriptor.setExtensions(baseResultData); } }; var loadExtensions = function (appDescriptor, extensionFileName) { var deferrable = $q.defer(); loadConfig(baseUrl + appDescriptor.extensionPath + extensionFileName).then(function (baseResult) { if (baseResult.data.shouldOverRideConfig) { loadConfig(customUrl + appDescriptor.extensionPath + extensionFileName).then( function (customResult) { setExtensions(baseResult.data, customResult.data); deferrable.resolve(appDescriptor); }, function () { setExtensions(baseResult.data); deferrable.resolve(appDescriptor); }); } else { setExtensions(baseResult.data); deferrable.resolve(appDescriptor); } }, function (error) { if (error.status !== 404) { deferrable.reject(error); } else { deferrable.resolve(appDescriptor); } }); return deferrable.promise; }; var setDefaultPageConfig = function (pageName, baseResultData, customResultData) { if (customResultData && (_.keys(customResultData).length > 0 || _.keys(baseResultData).length > 0)) { appDescriptor.addConfigForPage(pageName, baseResultData, customResultData); } else if (_.keys(baseResultData).length > 0) { appDescriptor.addConfigForPage(pageName, baseResultData); } }; var hasPrivilegeOf = function (privilegeName) { return _.some(currentUser.privileges, {name: privilegeName}); }; var loadPageConfig = function (pageName, appDescriptor) { var deferrable = $q.defer(); loadConfig(baseUrl + appDescriptor.contextPath + "/" + pageName + ".json").then( function (baseResult) { if (baseResult.data.shouldOverRideConfig) { loadConfig(customUrl + appDescriptor.contextPath + "/" + pageName + ".json").then( function (customResult) { setDefaultPageConfig(pageName, baseResult.data, customResult.data); deferrable.resolve(appDescriptor); }, function () { setDefaultPageConfig(pageName, baseResult.data); deferrable.resolve(appDescriptor); }); } else { setDefaultPageConfig(pageName, baseResult.data); deferrable.resolve(appDescriptor); } }, function (error) { if (error.status !== 404) { messagingService.showMessage('error', "Incorrect Configuration: " + error.message); deferrable.reject(error); } else { deferrable.resolve(appDescriptor); } }); return deferrable.promise; }; this.getAppDescriptor = function () { return appDescriptor; }; this.configBaseUrl = function () { return baseUrl; }; this.loadCsvFileFromConfig = function (name) { return loadConfig(baseUrl + appDescriptor.contextPath + "/" + name); }; this.loadConfig = function (name, shouldMerge) { return loadConfig(baseUrl + appDescriptor.contextPath + "/" + name).then( function (baseResponse) { if (baseResponse.data.shouldOverRideConfig) { return loadConfig(customUrl + appDescriptor.contextPath + "/" + name).then(function (customResponse) { if (shouldMerge || shouldMerge === undefined) { return mergeService.merge(baseResponse.data, customResponse.data); } return [baseResponse.data, customResponse.data]; }, function () { return baseResponse.data; }); } else { return baseResponse.data; } } ); }; this.loadMandatoryConfig = function (path) { return $http.get(path); }; this.getAppName = function () { return this.appName; }; this.checkPrivilege = function (privilegeName) { if (hasPrivilegeOf(privilegeName)) { return $q.when(true); } messagingService.showMessage("error", $translate.instant(Bahmni.Common.Constants.privilegeRequiredErrorMessage) + " [Privileges required: " + privilegeName + "]"); return $q.reject(); }; this.initApp = function (appName, options, extensionFileSuffix, configPages) { this.appName = appName; var appLoader = $q.defer(); var extensionFileName = (extensionFileSuffix && extensionFileSuffix.toLowerCase() !== 'default') ? "/extension-" + extensionFileSuffix + ".json" : "/extension.json"; var promises = []; var opts = options || {'app': true, 'extension': true}; var inheritAppContext = (!opts.inherit) ? true : opts.inherit; appDescriptor = new Bahmni.Common.AppFramework.AppDescriptor(appName, inheritAppContext, function () { return currentUser; }, mergeService); var loadCredentialsPromise = sessionService.loadCredentials(); var loadProviderPromise = loadCredentialsPromise.then(sessionService.loadProviders); promises.push(loadCredentialsPromise); promises.push(loadProviderPromise); if (opts.extension) { promises.push(loadExtensions(appDescriptor, extensionFileName)); } if (opts.template) { promises.push(loadTemplate(appDescriptor)); } if (opts.app) { promises.push(loadDefinition(appDescriptor)); } if (!_.isEmpty(configPages)) { configPages.forEach(function (configPage) { promises.push(loadPageConfig(configPage, appDescriptor)); }); } $q.all(promises).then(function (results) { currentUser = results[0]; appLoader.resolve(appDescriptor); $rootScope.$broadcast('event:appExtensions-loaded'); }, function (errors) { appLoader.reject(errors); }); return appLoader.promise; }; }]); 'use strict'; angular.module('bahmni.common.appFramework') .service('mergeService', [function () { this.merge = function (base, custom) { var mergeResult = $.extend(true, {}, base, custom); return deleteNullValuedKeys(mergeResult); }; var deleteNullValuedKeys = function (currentObject) { _.forOwn(currentObject, function (value, key) { if (_.isUndefined(value) || _.isNull(value) || _.isNaN(value) || (_.isObject(value) && _.isNull(deleteNullValuedKeys(value)))) { delete currentObject[key]; } }); return currentObject; }; }]); angular.module('bahmni.common.uiHelper', ['ngClipboard']); 'use strict'; angular.module('bahmni.common.uiHelper') .service('backlinkService', ['$window', function ($window) { var self = this; var urls = []; self.reset = function () { urls = []; }; self.setUrls = function (backLinks) { self.reset(); angular.forEach(backLinks, function (backLink) { self.addUrl(backLink); }); }; self.addUrl = function (backLink) { urls.push(backLink); }; self.addBackUrl = function (label) { var backLabel = label || "Back"; urls.push({label: backLabel, action: $window.history.back}); }; self.getUrlByLabel = function (label) { return urls.filter(function (url) { return url.label === label; }); }; self.getAllUrls = function () { return urls; }; }]); 'use strict'; angular.module('bahmni.common.uiHelper') .filter('days', function () { return function (startDate, endDate) { return Bahmni.Common.Util.DateUtil.diffInDays(startDate, endDate); }; }).filter('bahmniDateTime', function () { return function (date) { return Bahmni.Common.Util.DateUtil.formatDateWithTime(date); }; }).filter('bahmniDate', function () { return function (date) { return Bahmni.Common.Util.DateUtil.formatDateWithoutTime(date); }; }).filter('bahmniTime', function () { return function (date) { return Bahmni.Common.Util.DateUtil.formatTime(date); }; }).filter('bahmniDateInStrictMode', function () { return function (date) { return Bahmni.Common.Util.DateUtil.formatDateInStrictMode(date); }; }); 'use strict'; angular.module('bahmni.common.uiHelper') .factory('spinner', ['messagingService', '$timeout', function (messagingService, $timeout) { var tokens = []; var topLevelDiv = function (element) { return $(element).find("div").eq(0); }; var showSpinnerForElement = function (element) { if ($(element).find(".dashboard-section-loader").length === 0) { topLevelDiv(element) .addClass('spinnable') .append('
'); } return { element: $(element).find(".dashboard-section-loader") }; }; var showSpinnerForOverlay = function () { var token = Math.random(); tokens.push(token); if ($('#overlay').length === 0) { $('body').prepend('
'); } var spinnerElement = $('#overlay'); spinnerElement.stop().show(); return { element: spinnerElement, token: token }; }; var show = function (element) { if (element !== undefined) { return showSpinnerForElement(element); } return showSpinnerForOverlay(); }; var hide = function (spinner, parentElement) { var spinnerElement = spinner.element; if (spinner.token) { _.pull(tokens, spinner.token); if (tokens.length === 0) { spinnerElement.fadeOut(300); } } else { topLevelDiv(parentElement).removeClass('spinnable'); spinnerElement && spinnerElement.remove(); } }; var forPromise = function (promise, element) { return $timeout(function () { // Added timeout to push a new event into event queue. So that its callback will be invoked once DOM is completely rendered var spinner = show(element); // Don't inline this element promise['finally'](function () { hide(spinner, element); }); return promise; }); }; var forAjaxPromise = function (promise, element) { var spinner = show(element); promise.always(function () { hide(spinner, element); }); return promise; }; return { forPromise: forPromise, forAjaxPromise: forAjaxPromise, show: show, hide: hide }; }]); 'use strict'; angular.module('bahmni.common.uiHelper') .directive('bmBackLinks', function () { return { template: '', controller: function ($scope, backlinkService) { $scope.backLinks = backlinkService.getAllUrls(); $scope.$on('$stateChangeSuccess', function (event, state) { if (state.data && state.data.backLinks) { backlinkService.setUrls(state.data.backLinks); $scope.backLinks = backlinkService.getAllUrls(); } }); $scope.$on("$destroy", function () { window.onbeforeunload = undefined; }); }, restrict: 'E' }; }); 'use strict'; angular.module('bahmni.common.uiHelper') .directive('nonBlank', function () { return function ($scope, element, attrs) { var addNonBlankAttrs = function () { element.attr({'required': 'required'}); }; var removeNonBlankAttrs = function () { element.removeAttr('required'); }; if (!attrs.nonBlank) { return addNonBlankAttrs(element); } $scope.$watch(attrs.nonBlank, function (value) { return value ? addNonBlankAttrs() : removeNonBlankAttrs(); }); }; }) .directive('datepicker', function () { var link = function ($scope, element, attrs, ngModel) { var maxDate = attrs.maxDate; var minDate = attrs.minDate || "-120y"; var format = attrs.dateFormat || 'dd-mm-yy'; element.datepicker({ changeYear: true, changeMonth: true, maxDate: maxDate, minDate: minDate, yearRange: 'c-120:c+120', dateFormat: format, onSelect: function (dateText) { $scope.$apply(function () { ngModel.$setViewValue(dateText); }); } }); }; return { require: 'ngModel', link: link }; }) .directive('myAutocomplete', ['$parse', function ($parse) { var link = function (scope, element, attrs, ngModelCtrl) { var ngModel = $parse(attrs.ngModel); var source = scope.source(); var responseMap = scope.responseMap(); var onSelect = scope.onSelect(); element.autocomplete({ autofocus: true, minLength: 2, source: function (request, response) { source(attrs.id, request.term, attrs.itemType).then(function (data) { var results = responseMap ? responseMap(data.data) : data.data; response(results); }); }, select: function (event, ui) { scope.$apply(function (scope) { ngModelCtrl.$setViewValue(ui.item.value); scope.$eval(attrs.ngChange); if (onSelect != null) { onSelect(ui.item); } }); return true; }, search: function (event) { var searchTerm = $.trim(element.val()); if (searchTerm.length < 2) { event.preventDefault(); } } }); }; return { link: link, require: 'ngModel', scope: { source: '&', responseMap: '&', onSelect: '&' } }; }]) .directive('bmForm', ['$timeout', function ($timeout) { var link = function (scope, elem, attrs) { $timeout(function () { $(elem).unbind('submit').submit(function (e) { var formScope = scope.$parent; var formName = attrs.name; e.preventDefault(); if (scope.autofillable) { $(elem).find('input').trigger('change'); } if (formScope[formName].$valid) { formScope.$apply(attrs.ngSubmit); $(elem).removeClass('submitted-with-error'); } else { $(elem).addClass('submitted-with-error'); } }); }, 0); }; return { link: link, require: 'form', scope: { autofillable: "=" } }; }]) .directive('patternValidate', ['$timeout', function ($timeout) { return function ($scope, element, attrs) { var addPatternToElement = function () { if ($scope.fieldValidation && $scope.fieldValidation[attrs.id]) { element.attr({"pattern": $scope.fieldValidation[attrs.id].pattern, "title": $scope.fieldValidation[attrs.id].errorMessage, "type": "text"}); } }; $timeout(addPatternToElement); }; }]) .directive('validateOn', function () { var link = function (scope, element, attrs, ngModelCtrl) { var validationMessage = attrs.validationMessage || 'Please enter a valid detail'; var setValidity = function (value) { var valid = value ? true : false; ngModelCtrl.$setValidity('blank', valid); element[0].setCustomValidity(!valid ? validationMessage : ''); }; scope.$watch(attrs.validateOn, setValidity, true); }; return { link: link, require: 'ngModel' }; }); 'use strict'; angular.module('bahmni.common.uiHelper') .controller('MessageController', ['$scope', 'messagingService', function ($scope, messagingService) { $scope.messages = messagingService.messages; $scope.getMessageText = function (level) { var string = ""; $scope.messages[level].forEach(function (message) { string = string.concat(message.value); }); return string; }; $scope.hideMessage = function (level) { messagingService.hideMessages(level); }; $scope.isErrorMessagePresent = function () { return $scope.messages.error.length > 0; }; $scope.isInfoMessagePresent = function () { return $scope.messages.info.length > 0; }; }]); 'use strict'; angular.module('bahmni.common.uiHelper') .service('messagingService', ['$rootScope', '$timeout', function ($rootScope, $timeout) { this.messages = {error: [], info: []}; var self = this; $rootScope.$on('event:serverError', function (event, errorMessage) { self.showMessage('error', errorMessage, 'serverError'); }); this.showMessage = function (level, message, errorEvent) { var messageObject = {'value': '', 'isServerError': false}; messageObject.value = message; if (errorEvent) { messageObject.isServerError = true; } else if (level == 'info') { this.createTimeout('info', 4000); } var index = _.findIndex(this.messages[level], function (msg) { return msg.value == messageObject.value; }); if (index >= 0) { this.messages[level].splice(index, 1); } this.messages[level].push(messageObject); }; this.createTimeout = function (level, time) { $timeout(function () { self.messages[level] = []; }, time, true); }; this.hideMessages = function (level) { self.messages[level].length = 0; }; this.clearAll = function () { self.messages["error"] = []; self.messages["info"] = []; }; }]); 'use strict'; Bahmni.Common.Util.DateUtil = { diffInDays: function (dateFrom, dateTo) { return Math.floor((this.parse(dateTo) - this.parse(dateFrom)) / (60 * 1000 * 60 * 24)); }, diffInMinutes: function (dateFrom, dateTo) { return moment(dateTo).diff(moment(dateFrom), 'minutes'); }, diffInSeconds: function (dateFrom, dateTo) { return moment(dateFrom).diff(moment(dateTo), 'seconds'); }, isInvalid: function (date) { return date == "Invalid Date"; }, diffInDaysRegardlessOfTime: function (dateFrom, dateTo) { var from = new Date(dateFrom); var to = new Date(dateTo); from.setHours(0, 0, 0, 0); to.setHours(0, 0, 0, 0); return Math.floor((to - from) / (60 * 1000 * 60 * 24)); }, addSeconds: function (date, seconds) { return moment(date).add(seconds, 'seconds').toDate(); }, addMinutes: function (date, minutes) { return this.addSeconds(date, minutes * 60); }, addDays: function (date, days) { return moment(date).add(days, 'day').toDate(); }, addMonths: function (date, months) { return moment(date).add(months, 'month').toDate(); }, addYears: function (date, years) { return moment(date).add(years, 'year').toDate(); }, subtractSeconds: function (date, seconds) { return moment(date).subtract(seconds, 'seconds').toDate(); }, subtractDays: function (date, days) { return this.addDays(date, -1 * days); }, subtractMonths: function (date, months) { return this.addMonths(date, -1 * months); }, subtractYears: function (date, years) { return this.addYears(date, -1 * years); }, createDays: function (startDate, endDate) { var startDate = this.getDate(startDate); var endDate = this.getDate(endDate); var numberOfDays = this.diffInDays(startDate, endDate); var days = []; for (var i = 0; i <= numberOfDays; i++) { days.push({dayNumber: i + 1, date: this.addDays(startDate, i)}); } return days; }, getDayNumber: function (referenceDate, date) { return this.diffInDays(this.getDate(referenceDate), this.getDate(date)) + 1; }, getDateWithoutTime: function (datetime) { return datetime ? moment(datetime).format("YYYY-MM-DD") : null; }, getDateWitTime: function (datetime) { return datetime ? moment(datetime).format("YYYY-MM-DD HH:mm:ss") : null; }, getDateInMonthsAndYears: function (date, format) { var format = format || "MMM YY"; var dateRepresentation = isNaN(Number(date)) ? date : Number(date); if (!moment(dateRepresentation).isValid()) { return date; } return dateRepresentation ? moment(dateRepresentation).format(format) : null; }, formatDateWithTime: function (datetime) { var dateRepresentation = isNaN(Number(datetime)) ? datetime : Number(datetime); if (!moment(dateRepresentation).isValid()) { return datetime; } return dateRepresentation ? moment(dateRepresentation).format("DD MMM YY h:mm a") : null; }, formatDateWithoutTime: function (date) { var dateRepresentation = isNaN(Number(date)) ? date : Number(date); if (!moment(dateRepresentation).isValid()) { return date; } return dateRepresentation ? moment(dateRepresentation).format("DD MMM YY") : null; }, formatDateInStrictMode: function (date) { var dateRepresentation = isNaN(Number(date)) ? date : Number(date); if (moment(dateRepresentation, 'YYYY-MM-DD', true).isValid()) { return moment(dateRepresentation).format("DD MMM YY"); } if (moment(dateRepresentation, 'YYYY-MM-DDTHH:mm:ss.SSSZZ', true).isValid()) { return moment(dateRepresentation).format("DD MMM YY"); } return date; }, formatTime: function (date) { var dateRepresentation = isNaN(Number(date)) ? date : Number(date); if (!moment(dateRepresentation).isValid()) { return date; } return dateRepresentation ? moment(dateRepresentation).format("h:mm a") : null; }, getDate: function (dateTime) { return moment(this.parse(dateTime)).startOf('day').toDate(); }, parse: function (dateString) { return dateString ? moment(dateString).toDate() : null; }, parseDatetime: function (dateTimeString) { return dateTimeString ? moment(dateTimeString) : null; }, now: function () { return new Date(); }, today: function () { return this.getDate(this.now()); }, endOfToday: function () { return moment(this.parse(this.now())).endOf('day').toDate(); }, getDateWithoutHours: function (dateString) { return moment(dateString).toDate().setHours(0, 0, 0, 0); }, getDateTimeWithoutSeconds: function (dateString) { return moment(dateString).toDate().setSeconds(0, 0); }, isSameDateTime: function (date1, date2) { if (date1 == null || date2 == null) { return false; } var dateOne = this.parse(date1); var dateTwo = this.parse(date2); return dateOne.getTime() == dateTwo.getTime(); }, isBeforeDate: function (date1, date2) { return moment(date1).isBefore(moment(date2)); }, isSameDate: function (date1, date2) { if (date1 == null || date2 == null) { return false; } var dateOne = this.parse(date1); var dateTwo = this.parse(date2); return dateOne.getFullYear() === dateTwo.getFullYear() && dateOne.getMonth() === dateTwo.getMonth() && dateOne.getDate() === dateTwo.getDate(); }, diffInYearsMonthsDays: function (dateFrom, dateTo) { dateFrom = this.parse(dateFrom); dateTo = this.parse(dateTo); var from = { d: dateFrom.getDate(), m: dateFrom.getMonth(), y: dateFrom.getFullYear() }; var to = { d: dateTo.getDate(), m: dateTo.getMonth(), y: dateTo.getFullYear() }; var age = { d: 0, m: 0, y: 0 }; var daysFebruary = to.y % 4 != 0 || (to.y % 100 == 0 && to.y % 400 != 0) ? 28 : 29; var daysInMonths = [31, daysFebruary, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; age.y = to.y - from.y; age.m = to.m - from.m; if (from.m > to.m) { age.y = age.y - 1; age.m = to.m - from.m + 12; } age.d = to.d - from.d; if (from.d > to.d) { age.m = age.m - 1; if (from.m == to.m) { age.y = age.y - 1; age.m = age.m + 12; } age.d = to.d - from.d + daysInMonths[parseInt(from.m)]; } return { days: age.d, months: age.m, years: age.y }; }, convertToUnits: function (minutes) { var allUnits = {"Years": 365 * 24 * 60, "Months": 30 * 24 * 60, "Weeks": 7 * 24 * 60, "Days": 24 * 60, "Hours": 60, "Minutes": 1}; var durationRepresentation = function (value, unitName, unitValueInMinutes) { return {"value": value, "unitName": unitName, "unitValueInMinutes": unitValueInMinutes, "allUnits": allUnits }; }; for (var unitName in allUnits) { var unitValueInMinutes = allUnits[unitName]; if (minutes || minutes !== 0) { if (minutes >= unitValueInMinutes && minutes % unitValueInMinutes === 0) { return durationRepresentation(minutes / unitValueInMinutes, unitName, unitValueInMinutes); } } } return durationRepresentation(undefined, undefined, undefined); }, getEndDateFromDuration: function (dateFrom, value, unit) { dateFrom = this.parse(dateFrom); var from = { h: dateFrom.getHours(), d: dateFrom.getDate(), m: dateFrom.getMonth(), y: dateFrom.getFullYear() }; var to = new Date(from.y, from.m, from.d, from.h); if (unit === "Months") { to.setMonth(from.m + value); } else if (unit === "Weeks") { to.setDate(from.d + (value * 7)); } else if (unit === "Days") { to.setDate(from.d + value); } else if (unit === "Hours") { to.setHours(from.h + value); } return to; }, parseLongDateToServerFormat: function (longDate) { return longDate ? moment(longDate).format("YYYY-MM-DDTHH:mm:ss.SSS") : null; }, parseServerDateToDate: function (longDate) { return longDate ? moment(longDate, "YYYY-MM-DDTHH:mm:ss.SSSZZ").toDate() : null; }, getDateTimeInSpecifiedFormat: function (date, format) { return date ? moment(date).format(format) : null; }, getISOString: function (date) { return date ? moment(date).toDate().toISOString() : null; }, isBeforeTime: function (time, otherTime) { return moment(time, 'hh:mm a').format('YYYY-MM-DD'); } }; 'use strict'; angular.module('bahmni.common.uiHelper') .directive('toggle', function () { var link = function ($scope, element) { $scope.toggle = $scope.toggle === undefined ? false : $scope.toggle; $(element).click(function () { $scope.$apply(function () { $scope.toggle = !$scope.toggle; }); }); $scope.$watch('toggle', function () { $(element).toggleClass('active', $scope.toggle); }); $scope.$on("$destroy", function () { element.off('click'); }); }; return { scope: { toggle: "=" }, link: link }; }); 'use strict'; var Bahmni = Bahmni || {}; Bahmni.Common = Bahmni.Common || {}; Bahmni.Common.Logging = Bahmni.Common.Logging || {}; angular.module('bahmni.common.logging', []); 'use strict'; angular.module('bahmni.common.logging') .config(['$provide', function ($provide) { $provide.decorator("$exceptionHandler", function ($delegate, $injector, $window, $log) { var logError = function (exception, cause) { try { var messagingService = $injector.get('messagingService'); var loggingService = $injector.get('loggingService'); var errorMessage = exception.toString(); var stackTrace = printStackTrace({ e: exception }); var errorDetails = { timestamp: new Date(), browser: $window.navigator.userAgent, errorUrl: $window.location.href, errorMessage: errorMessage, stackTrace: stackTrace, cause: (cause || "") }; loggingService.log(errorDetails); messagingService.showMessage('error', errorMessage); exposeException(errorDetails); } catch (loggingError) { $log.warn("Error logging failed"); $log.log(loggingError); } }; var exposeException = function (exceptionDetails) { window.angular_exception = window.angular_exception || []; window.angular_exception.push(exceptionDetails); }; return function (exception, cause) { $delegate(exception, cause); logError(exception, cause); }; }); }]); 'use strict'; var Bahmni = Bahmni || {}; Bahmni.Common = Bahmni.Common || {}; Bahmni.Common.I18n = Bahmni.Common.I18n || {}; angular.module('bahmni.common.i18n', []); 'use strict'; angular.module('bahmni.common.i18n', ['pascalprecht.translate']) .provider('$bahmniTranslate', ['$translateProvider', function ($translateProvider) { this.init = function (options) { var preferredLanguage = window.localStorage["NG_TRANSLATE_LANG_KEY"] || "en"; $translateProvider.useLoader('mergeLocaleFilesService', options); $translateProvider.useSanitizeValueStrategy('escaped'); $translateProvider.preferredLanguage(preferredLanguage); $translateProvider.useLocalStorage(); }; this.$get = [function () { return $translateProvider; }]; } ]) .filter('titleTranslate', ['$translate', function ($translate) { return function (input) { if (!input) { return input; } if (input.translationKey) { return $translate.instant(input.translationKey); } if (input.dashboardName) { return input.dashboardName; } if (input.title) { return input.title; } if (input.label) { return input.label; } if (input.display) { return input.display; } return $translate.instant(input); }; }]); 'use strict'; angular.module('bahmni.common.i18n') .service('mergeLocaleFilesService', ['$http', '$q', 'mergeService', function ($http, $q, mergeService) { return function (options) { var baseLocaleUrl = '../i18n/'; var customLocaleUrl = Bahmni.Common.Constants.rootDir + '/bahmni_config/openmrs/i18n/'; var loadFile = function (url) { return $http.get(url, {withCredentials: true}); }; var mergeLocaleFile = function (options) { var fileURL = options.app + "/locale_" + options.key + ".json"; var loadBahmniTranslations = function () { return loadFile(baseLocaleUrl + fileURL).then(function (result) { return result; }, function () { return; }); }; var loadCustomTranslations = function () { return loadFile(customLocaleUrl + fileURL).then(function (result) { return result; }, function () { return; }); }; var mergeTranslations = function (result) { var baseFileData = result[0] ? result[0].data : undefined; var customFileData = result[1] ? result[1].data : undefined; if (options.shouldMerge || options.shouldMerge === undefined) { return mergeService.merge(baseFileData, customFileData); } return [baseFileData, customFileData]; }; return $q.all([loadBahmniTranslations(), loadCustomTranslations()]) .then(mergeTranslations); }; return mergeLocaleFile(options); }; }]); 'use strict'; angular.module('bahmni.home', ['ui.router', 'httpErrorInterceptor', 'bahmni.common.domain', 'bahmni.common.i18n', 'bahmni.common.uiHelper', 'bahmni.common.util', 'bahmni.common.appFramework', 'bahmni.common.logging', 'bahmni.common.routeErrorHandler', 'pascalprecht.translate', 'ngCookies', 'bahmni.common.models']) .config(['$urlRouterProvider', '$stateProvider', '$httpProvider', '$bahmniTranslateProvider', '$compileProvider', function ($urlRouterProvider, $stateProvider, $httpProvider, $bahmniTranslateProvider, $compileProvider) { $urlRouterProvider.otherwise('/dashboard'); $compileProvider.debugInfoEnabled(false); $stateProvider .state('dashboard', { url: '/dashboard', templateUrl: 'views/dashboard.html', controller: 'DashboardController', data: {extensionPointId: 'org.bahmni.home.dashboard'}, resolve: { initialize: function (initialization) { return initialization(); } } }).state('changePassword', { url: '/changePassword', templateUrl: 'views/changePassword.html', controller: 'ChangePasswordController' }).state('login', { url: '/login?showLoginMessage', templateUrl: 'views/login.html', controller: 'LoginController', resolve: { initialData: function (loginInitialization) { return loginInitialization(); } } }) .state('errorLog', { url: '/errorLog', controller: 'ErrorLogController', templateUrl: 'views/errorLog.html', data: { backLinks: [ {label: "Home", state: "dashboard", accessKey: "h", icon: "fa-home"} ] }, resolve: { } }); $httpProvider.defaults.headers.common['Disable-WWW-Authenticate'] = true; $bahmniTranslateProvider.init({app: 'home', shouldMerge: true}); }]).run(['$rootScope', '$templateCache', '$window', function ($rootScope, $templateCache, $window) { moment.locale($window.localStorage["NG_TRANSLATE_LANG_KEY"] || "en"); // Disable caching view template partials $rootScope.$on('$viewContentLoaded', function () { $templateCache.removeAll(); }); }]); 'use strict'; angular.module('bahmni.home') .factory('initialization', ['$rootScope', 'appService', 'spinner', function ($rootScope, appService, spinner) { var initApp = function () { return appService.initApp('home'); }; return function () { return spinner.forPromise(initApp()); }; } ]); 'use strict'; angular.module('bahmni.home') .factory('loginInitialization', ['$rootScope', '$q', 'locationService', 'spinner', 'messagingService', function ($rootScope, $q, locationService, spinner, messagingService) { var init = function () { var deferrable = $q.defer(); locationService.getAllByTag("Login Location").then( function (response) { deferrable.resolve({locations: response.data.results}); }, function (response) { deferrable.reject(); if (response.status) { response = 'MESSAGE_START_OPENMRS'; } messagingService.showMessage('error', response); } ); return deferrable.promise; }; return function () { return spinner.forPromise(init()); }; } ]); 'use strict'; angular.module("bahmni.home") .controller("LoginController", ["$rootScope", "$http", "$scope", "messagingService", "$window", "$location", "sessionService", "initialData", "spinner", "$q", "$stateParams", "$bahmniCookieStore", "localeService", "$translate", "userService", "auditLogService", function ($rootScope, $http, $scope, messagingService, $window, $location, sessionService, initialData, spinner, $q, $stateParams, $bahmniCookieStore, localeService, $translate, userService, auditLogService) { var redirectUrl = $location.search()['from']; var landingPagePath = "/dashboard"; var loginPagePath = "/login"; $scope.locations = initialData.locations; $scope.loginInfo = {}; var localeLanguages = []; var hostName = $window.location.hostname; var baseUrl = "https://" + hostName; var getLocalTimeZone = function () { var currentLocalTime = new Date().toString(); var localTimeZoneList = currentLocalTime.split(" "); var localTimeZone = localTimeZoneList[localTimeZoneList.length - 1]; localTimeZone = localTimeZone.substring(1, localTimeZone.length - 1); return localTimeZone; }; var findLanguageByLocale = function (localeCode) { return _.find(localeLanguages, function (localeLanguage) { return localeLanguage.code == localeCode; }); }; var logAuditForLoginAttempts = function (eventType, isFailedEvent) { if ($scope.loginInfo.username) { var messageParams = isFailedEvent ? {userName: $scope.loginInfo.username} : undefined; auditLogService.log(undefined, eventType, messageParams, 'MODULE_LABEL_LOGIN_KEY'); } }; var promise = localeService.allowedLocalesList(); localeService.serverDateTime().then(function (response) { var serverTime = response.data.date; var offset = response.data.offset; var localTime = new Date().toLocaleString(); var localtimeZone = getLocalTimeZone(); var localeTimeZone = localTime + " " + localtimeZone; $scope.timeZoneObject = {serverTime: serverTime, localeTimeZone: localeTimeZone}; if (offset && !new Date().toString().includes(offset)) { $scope.warning = "Warning"; $scope.warningMessage = "WARNING_SERVER_TIME_ZONE_MISMATCH"; } }); localeService.getLoginText().then(function (response) { $scope.logo = response.data.loginPage.logo; $scope.bottomLogos = response.data.loginPage.bottomLogos; $scope.headerText = response.data.loginPage.showHeaderText; $scope.titleText = response.data.loginPage.showTitleText; $scope.helpLink = response.data.helpLink.url; }); localeService.getLocalesLangs().then(function (response) { localeLanguages = response.data.locales; }).finally(function () { promise.then(function (response) { var localeList = response.data.replace(/\s+/g, '').split(','); $scope.locales = []; _.forEach(localeList, function (locale) { var localeLanguage = findLanguageByLocale(locale); if (_.isUndefined(localeLanguage)) { $scope.locales.push({"code": locale, "nativeName": locale}); } else { $scope.locales.push(localeLanguage); } }); $scope.selectedLocale = $translate.use() ? $translate.use() : $scope.locales[0].code; }); }); $scope.isChrome = function () { if ($window.navigator.userAgent.indexOf("Chrome") != -1) { return true; } return false; }; $scope.$watch("selectedLocale", function () { $translate.use($scope.selectedLocale); }); var getLoginLocationUuid = function () { return $bahmniCookieStore.get(Bahmni.Common.Constants.locationCookieName) ? $bahmniCookieStore.get(Bahmni.Common.Constants.locationCookieName).uuid : null; }; var getLastLoggedinLocation = function () { return _.find(initialData.locations, function (location) { return location.uuid === getLoginLocationUuid(); }); }; $scope.loginInfo.currentLocation = getLastLoggedinLocation(); if ($stateParams.showLoginMessage) { $scope.errorMessageTranslateKey = "LOGIN_LABEL_LOGIN_ERROR_MESSAGE_KEY"; } var redirectToLandingPageIfAlreadyAuthenticated = function () { sessionService.get().then(function (data) { if (data.authenticated) { $location.path(landingPagePath); } }); }; if ($location.path() === loginPagePath) { redirectToLandingPageIfAlreadyAuthenticated(); } var onSuccessfulAuthentication = function () { $bahmniCookieStore.remove(Bahmni.Common.Constants.retrospectiveEntryEncounterDateCookieName, { path: '/', expires: 1 }); $rootScope.$broadcast('event:auth-loggedin'); $scope.loginInfo.currentLocation = getLastLoggedinLocation(); }; $scope.login = async function () { $scope.errorMessageTranslateKey = null; var deferrable = $q.defer(); const userHeaders = new Headers(); userHeaders.append("Content-Type", "application/json"); userHeaders.append("Authorization", "Basic YWRtaW46dGVzdA=="); const headers = new Headers(); headers.append("Content-Type", "application/json"); var ensureNoSessionIdInRoot = function () { $bahmniCookieStore.remove(Bahmni.Common.Constants.JSESSIONID, { path: '/', expires: 1 }); }; const checkAndFormatPassword = (password) => { const formattedPassword = (password.search(/[A-Z]/) === -1 || password.search(/[a-z]/) === -1) ? password.charAt(0).toUpperCase() + password.charAt(1).toLowerCase() + password.slice(2) : password; if (formattedPassword !== password) { return formattedPassword + (/\d/.test(formattedPassword) ? '' : '123'); } else { return password + (/\d/.test(password) ? '' : '123'); } }; const updateUserPassword = async (userPayload, uuid) => { return await fetch( baseUrl + "/openmrs/ws/rest/v1/password/" + uuid, { method: "POST", body: JSON.stringify(userPayload), headers: userHeaders } ); }; const loginBahmni = (hrisLoggedIn, uuid) => { sessionService.loginUser($scope.loginInfo.username, checkAndFormatPassword($scope.loginInfo.password), $scope.loginInfo.currentLocation, $scope.loginInfo.otp).then( function (data) { ensureNoSessionIdInRoot(); if (data && data.firstFactAuthorization) { $scope.showOTP = true; deferrable.resolve(data); return; } sessionService.loadCredentials().then(function () { onSuccessfulAuthentication(); $rootScope.currentUser.addDefaultLocale($scope.selectedLocale); fetch("/openmrs/ws/rest/v1/provider?user=${$rootScope.currentUser.uuid}") .then(res => res.json()) .then(data => { localStorage.setItem('providerName', data.results[0].display); }); userService.savePreferences().then( function () { deferrable.resolve(); }, function (error) { deferrable.reject(error); } ); logAuditForLoginAttempts("USER_LOGIN_SUCCESS"); }, function (error) { $scope.errorMessageTranslateKey = error; deferrable.reject(error); logAuditForLoginAttempts("USER_LOGIN_FAILED", true); } ); }, function (error) { if (error === 'LOGIN_LABEL_LOGIN_ERROR_MESSAGE_KEY' && hrisLoggedIn) { // check if user exists in the system, if exists already and is not able to login, const userData = { "newPassword": checkAndFormatPassword($scope.loginInfo.password) }; const updateBahmniUserPassword = updateUserPassword(userData, uuid); if (updateBahmniUserPassword) { return loginBahmni(false); } } else { $scope.errorMessageTranslateKey = error; if (error === "LOGIN_LABEL_MAX_FAILED_ATTEMPTS" || error === "LOGIN_LABEL_OTP_EXPIRED") { deleteUserCredentialsAndShowLoginPage(); } else if (error === "LOGIN_LABEL_WRONG_OTP_MESSAGE_KEY") { delete $scope.loginInfo.otp; } deferrable.reject(error); logAuditForLoginAttempts("USER_LOGIN_FAILED", true); } } ); }; var deleteUserCredentialsAndShowLoginPage = function () { $scope.showOTP = false; delete $scope.loginInfo.otp; delete $scope.loginInfo.username; delete $scope.loginInfo.password; }; $scope.resendOTP = function () { var promise = sessionService.resendOTP($scope.loginInfo.username, $scope.loginInfo.password); spinner.forPromise(promise); promise.then(function () { $scope.errorMessageTranslateKey = "LOGIN_LABEL_RESEND_SUCCESS"; }, function (response) { if (response.status === 429) { $scope.errorMessageTranslateKey = "LOGIN_LABEL_MAX_RESEND_ATTEMPTS"; deleteUserCredentialsAndShowLoginPage(); } }); }; spinner.forPromise(deferrable.promise).then( function (data) { if (data) return; if (redirectUrl) { $window.location = redirectUrl; } else { $location.path(landingPagePath); } } ); const checkInternet = async () => { return await fetch("/openmrs/ws/rest/v1/angtcore/ajax/checkInternet.htm") .then((response) => { return response.json(); }); }; const userPayloadData = async (inputData) => { const transformedData = { username: inputData.userName, password: inputData.password, person: { names: [ { givenName: inputData.name.trim(), preferred: true } ], gender: inputData.gender.charAt(0).toUpperCase(), age: 0, birthdate: new Date(inputData.birthDate).toISOString(), birthdateEstimated: false, dead: false, addresses: [ { preferred: true, address1: inputData.address.text } ], deathdateEstimated: false }, systemId: inputData.systemId, roles: inputData.roles }; return transformedData; }; const createUser = async (userPayload) => { return await fetch( // "https://${$window.location.hostname}/openmrs/ws/rest/v1/user", "https://${$window.location.hostname}/openmrs/ws/rest/v1/user", { method: "POST", body: JSON.stringify(userPayload), headers: userHeaders } ) .then((response) => { return response.json(); }); }; const getDesignationUuid = async () => { return await fetch( "https://${$window.location.hostname}/openmrs/ws/rest/v1/providerattributetype?q=Designation", { method: "GET", headers: userHeaders } ) .then((response) => { return response.json(); }); }; const createProvider = async (providerData) => { var hrisProvider = { "name": providerData.name, "personUuid": providerData.person, "identifier": providerData.userPayload.systemId } return await fetch( "/openmrs/ws/rest/v1/angtcore/hris/saveProvider.htm", { method: "POST", body: JSON.stringify(hrisProvider), headers: userHeaders } ) .then((response) => { return response.json(); }); }; const createBahmniHRISUser = async (userPayload) => { return await fetch( "https://${$window.location.hostname}:6062/api/v1/hris-user", { method: "POST", body: JSON.stringify(userPayload), headers: headers } ) .then((response) => { return response.json(); }); }; const hrisLogin = async (dataBody) => { console.log(dataBody); return await fetch(baseUrl+"/openmrs/ws/rest/v1/angtcore/hris/signin.htm", { method: "POST", body: JSON.stringify(dataBody), headers: headers } ) .then((response) => { return response.json(); }); }; const getUserRolesUuid = async (roles) => { return await fetch("/openmrs/ws/rest/v1/angtcore/hris/openMRSRoles.htm", { method: "POST", body: JSON.stringify(roles), headers: headers }) .then((response) => { return response.json(); }); }; const checkUserName = async (username) => { return await fetch("/openmrs/ws/rest/v1/angtcore/hris/openmrsUser.htm?username=" + username) .then((response) => { return response.json(); }); }; const getProviderFromHRIS = async (token) => { return await fetch("/openmrs/ws/rest/v1/angtcore/hris/hrisInfoGetByToken.htm?token=" + token) .then((response) => { return response.json(); }); }; const getProviderDataFromHRIS = async (id) => { return await fetch("/openmrs/ws/rest/v1/angtcore/hris/hrisProviderInfoGetByProviderId.htm?providerId=" + id) .then((response) => { return response.json(); }); }; const generateUsername = async (name) => { const generateRandomUsername = () => { return `HRM-${name.split(' ')[1]}-${Math.floor(Math.random() * 9000) + 1000}`; }; const username = generateRandomUsername(); const userAvailable = await checkUserName(username); if (userAvailable.statusCode === 404) { return username; } else { return generateUsername(name); } }; try { const dataBody = { "email": $scope.loginInfo.username, "password": $scope.loginInfo.password }; // is ok status === 200 const internetAvailability = await checkInternet(); if (internetAvailability.content) { const hrisRes = await hrisLogin(dataBody); if (hrisRes.error) { return loginBahmni(false); } else { const userAvailable = await checkUserName($scope.loginInfo.username); if (userAvailable.statusCode === 404) { const accessToken = hrisRes.access_token; if (accessToken) { const providerRes = await getProviderFromHRIS(accessToken); const checkProvider = providerRes.profiles.find(p => p.name === 'provider'); if (!checkProvider) { alert("You do not have privileges to access this system"); return loginBahmni(false); } else { const getUserData = await getProviderDataFromHRIS(checkProvider.id); if (getUserData.telecom.length > 0) { const userEmailData = getUserData.telecom.filter(data => data.system === 'email'); if (userEmailData.length > 0) { const userEmail = userEmailData[0].value; const roles = { names: [ "Admin-App", "InPatient-App", "Reports-App", "Doctor" ] }; const roleDataRes = await getUserRolesUuid(roles); const roleData = roleDataRes.content.uuidList .map(item => ({ "uuid": item })); const userData = {...getUserData}; const userName = await generateUsername(userData.name); userData.userName = userName; userData.systemId = userEmail; userData.roles = roleData; userData.password = checkAndFormatPassword($scope.loginInfo.password); const userPayload = await userPayloadData(userData); const createBahmniUser = await createUser(userPayload); if (createBahmniUser) { const designationUuid = await getDesignationUuid(); if (designationUuid) { const providerData = { "name": createBahmniUser.person.display, "description": null, "person": createBahmniUser.person.uuid, "identifier": null, "attributes": [{ "attributeType": designationUuid.results[0].uuid, "value": getUserData.properties.designation }], "retired": false, "userPayload": userPayload }; const createBahmniProvider = await createProvider(providerData); if (createBahmniProvider) { const bahmniUserPayload = { userUuid: createBahmniUser.uuid, personUuid: createBahmniUser.person.uuid, providerUuid: createBahmniProvider.uuid, object: JSON.stringify(getUserData), activeStatus: getUserData.active, url: getUserData.url, providerId: getUserData.id }; const createBahmniHRIS = await createBahmniHRISUser(bahmniUserPayload); if (createBahmniHRIS) { return loginBahmni(false); } else { return loginBahmni(false); } } else { return loginBahmni(false); } } } else { return loginBahmni(false); } } else { return loginBahmni(false); } } } } else { return loginBahmni(false); } } else { return loginBahmni(true, userAvailable.content); } } } else { return loginBahmni(false); } } catch (err) { console.log(err); return loginBahmni(false); } }; }]); 'use strict'; angular.module('bahmni.home') .controller('DashboardController', ['$scope', '$state', 'appService', 'locationService', 'spinner', '$bahmniCookieStore', '$window', '$q', function ($scope, $state, appService, locationService, spinner, $bahmniCookieStore, $window, $q) { $scope.appExtensions = appService.getAppDescriptor().getExtensions($state.current.data.extensionPointId, "link") || []; $scope.selectedLocationUuid = {}; var isOnline = function () { return $window.navigator.onLine; }; $scope.isVisibleExtension = function (extension) { return extension.exclusiveOnlineModule ? isOnline() : extension.exclusiveOfflineModule ? !isOnline() : true; }; var getCurrentLocation = function () { return $bahmniCookieStore.get(Bahmni.Common.Constants.locationCookieName) ? $bahmniCookieStore.get(Bahmni.Common.Constants.locationCookieName) : null; }; var init = function () { return locationService.getAllByTag("Login Location").then(function (response) { $scope.locations = response.data.results; $scope.selectedLocationUuid = getCurrentLocation().uuid; } ); }; var getLocationFor = function (uuid) { return _.find($scope.locations, function (location) { return location.uuid === uuid; }); }; $scope.isCurrentLocation = function (location) { return getCurrentLocation().uuid === location.uuid; }; $scope.onLocationChange = function () { var selectedLocation = getLocationFor($scope.selectedLocationUuid); $bahmniCookieStore.remove(Bahmni.Common.Constants.locationCookieName); $bahmniCookieStore.put(Bahmni.Common.Constants.locationCookieName, { name: selectedLocation.display, uuid: selectedLocation.uuid }, {path: '/', expires: 7}); $window.location.reload(); }; $scope.changePassword = function () { $state.go('changePassword'); }; return spinner.forPromise($q.all(init())); }]); 'use strict'; angular.module('bahmni.home') .controller('ChangePasswordController', ['$scope', '$state', 'sessionService', '$rootScope', 'authenticator', '$window', 'userService', 'messagingService', function ($scope, $state, sessionService, $rootScope, authenticator, $window, userService, messagingService) { $scope.loginInfo = {}; $scope.passwordDoesNotMatch = false; $scope.passwordPolicies = []; $scope.redirectToHomePage = function () { $state.go('dashboard'); }; var checkPasswordMatches = function () { return $scope.loginInfo.newPassword == $scope.loginInfo.confirmPassword; }; $scope.changePassword = function () { if (_.isEmpty($scope.loginInfo.oldPassword) || _.isEmpty($scope.loginInfo.newPassword) || _.isEmpty($scope.loginInfo.confirmPassword)) { return; } if (checkPasswordMatches()) { $scope.passwordDoesNotMatch = false; sessionService.changePassword($rootScope.currentUser.uuid, $scope.loginInfo.oldPassword, $scope.loginInfo.newPassword).then(function (data) { messagingService.showMessage("info", 'CHANGE_PASSWORD_SUCCESSFUL_MESSAGE'); clearPasswordFields(); }); } else { $scope.passwordDoesNotMatch = true; } }; var clearPasswordFields = function () { $scope.loginInfo.newPassword = undefined; $scope.loginInfo.oldPassword = undefined; $scope.loginInfo.confirmPassword = undefined; }; var convertPasswordPolicies = function (policies) { _.forEach(policies, function (value, key) { switch (key) { case "security.passwordCannotMatchUsername" : value == "true" ? $scope.passwordPolicies.splice(0, 0, 'PASSWORD_SHOULD_NOT_MATCH_USER_NAME') : ''; break; case "security.passwordMinimumLength" : $scope.passwordLength = value; $scope.passwordPolicies.splice(1, 0, 'PASSWORD_SHOULD_BE_MINIMUM_CHARACTERS'); break; case "security.passwordRequiresUpperAndLowerCase": value == "true" ? $scope.passwordPolicies.splice(2, 0, 'PASSWORD_SHOULD_BE_A_MIX_OF_BOTH_UPPER_CASE_AND_LOWER_CASE') : ''; break; case "security.passwordRequiresDigit" : value == "true" ? $scope.passwordPolicies.splice(3, 0, 'PASSWORD_SHOULD_CONTAIN_DIGITS') : ''; break; case "security.passwordRequiresNonDigit" : value == "true" ? $scope.passwordPolicies.splice(4, 0, 'PASSWORD_SHOULD_HAVE_ATLEAST_ONE_NON_DIGIT') : ''; break; case "security.passwordCustomRegex": if (!_.isEmpty(value)) { $scope.passwordPolicies.splice(5, 0, 'PASSWORD_SHOULD_MATCH_THE_REGEX'); $scope.passwordRegex = value; } } }); }; var init = function () { authenticator.authenticateUser().then(function () { userService.getPasswordPolicies().then(function (response) { convertPasswordPolicies(response.data); }); sessionService.loadCredentials().then(function (data) { }); }, function () { $window.location = "../home/index.html#/login"; }); }; init(); }]); 'use strict'; angular.module('bahmni.home') .controller('ErrorLogController', ['$q', 'spinner', '$scope', function ($q, spinner, $scope) { $scope.errorLogs = []; $scope.showErrorLog = true; }]); (function(window, undefined) { "use strict"; var version = '0.2.0'; var internal_db_version = 5; var indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.oIndexedDB || window.msIndexedDB; var IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange if (!indexedDB) throw 'IndexedDB required'; // define error(s) used by tcrypt var extend_error = function(extend, errname) { var err = function() { var tmp = extend.apply(this, arguments); tmp.name = this.name = errname; this.stack = tmp.stack this.message = tmp.message return this; }; err.prototype = Object.create(extend.prototype, { constructor: { value: err } }); return err; } var HustleError = extend_error(window.Error, 'HustleError'); var HustleDBClosed = extend_error(HustleError, 'HustleDBClosed'); var HustleDBOpened = extend_error(HustleError, 'HustleDBOpened'); var HustleBadTube = extend_error(HustleError, 'HustleBadTube'); var HustleBadID = extend_error(HustleError, 'HustleBadID'); var HustleNotice = extend_error(window.Error, 'HustleNotice'); var HustleNotFound = extend_error(HustleNotice, 'HustleNotFound'); var Hustle = function(qoptions) { qoptions || (qoptions = {}); if (!qoptions.tubes) qoptions.tubes = []; // database version. should change every time the tubes change var db_version = qoptions.db_version ? qoptions.db_version : 1; // how often we do DB maintenance var maintenance_delay = qoptions.maintenance_delay ? qoptions.maintenance_delay : 1000; // define some system db vars var db_name = qoptions.db_name ? qoptions.db_name : 'hustle'; // our reserved tables var tbl = { ids: '_ids', reserved: '_reserved', delayed: '_delayed', buried: '_buried' }; // always add a default tube if (qoptions.tubes.indexOf('default') < 0) qoptions.tubes.push('default'); var db = null; // --------------------------------------------------------------------- // database-related functions // --------------------------------------------------------------------- var check_db = function() { if (!db) throw new HustleDBClosed('Closed database being operated on. Did you call Hustle.open()?'); return true; }; // will be filled in later var do_maintenance; /** * helper function, creates a table if it doesn't exist, otherwise grabs * it. returns the store. */ var update_table_schema = function(e, tablename, options) { options || (options = {}); var store = null; var udb = e.target.result; var keypath = options.keypath ? options.keypath : 'id'; var autoinc = options.autoincrement ? options.autoincrement : false; // grab an existing object store or create a new one if (udb.objectStoreNames.contains(tablename)) { store = e.currentTarget.transaction.objectStore(tablename); } else { store = udb.createObjectStore(tablename, { keyPath: keypath, autoIncrement: autoinc }); } if (options.indexes) { var keys = Object.keys(options.indexes); for (var i = 0; i < keys.length; i++) { (function(key, idx) { var index_val = idx.index ? idx.index : key; var unique = idx.unique ? true : false; try { store.createIndex(key, index_val, { unique: unique }); } // index probably exists already // TODO: check store.indexNames catch (e) {} })(keys[i], options.indexes[keys[i]]); } } return store; }; /** * open the queue database and make sure the schema is ship-shape */ var open = function(options) { options || (options = {}); if (db) throw new HustleDBOpened('db is already open'); var version = (internal_db_version * 1000) + db_version; var req = indexedDB.open(db_name, version); req.onerror = function(e) { if (options.error) options.error(e); } req.onsuccess = function(e) { db = req.result; if (options.success) options.success(e); do_maintenance(); }; req.onupgradeneeded = function(e) { var store = null; var tubes = qoptions.tubes; update_table_schema(e, tbl.ids); update_table_schema(e, tbl.reserved, { indexes: { expire: { index: 'expire', unique: false } } }); update_table_schema(e, tbl.delayed, { indexes: { activate: { index: 'activate', unique: false } } }); update_table_schema(e, tbl.buried, { indexes: { id: { unique: false } } }); for (var i = 0; i < tubes.length; i++) { if ([tbl.reserved, tbl.buried].indexOf(tubes[i]) >= 0) continue; update_table_schema(e, tubes[i], { indexes: { priority: { index: ['priority', 'id'], unique: false } } }); } }; }; /** * close the queue database */ var close = function() { if (!db) return false; db.close(); db = null; return true; }; /** * convenience function to obliterate the queue */ var wipe = function() { close(); indexedDB.deleteDatabase(db_name); return true; }; /** * generate a unique, auto-incrementing ID */ var new_id = function(options) { check_db(); options || (options = {}); var id = null; var trx = db.transaction([tbl.ids], 'readwrite'); trx.oncomplete = function(e) { if (!id) { if (options.error) options.error('bad id'); return; } if (options.success) options.success(id, e); }; trx.onerror = function(e) { if (options.error) options.error(e); } // upsert the ID record var store = trx.objectStore(tbl.ids); var req = store.get('id'); req.onsuccess = function(e) { var item = req.result; if (!item) { id = 1; store.put({ id: 'id', value: 1 }); } else { item.value++; id = item.value; store.put(item); } }; }; /** * generic function to move a queue item from one table to another. */ var move_item = function(id, from, to, options) { options || (options = {}); var trx = db.transaction([from, to], 'readwrite'); trx.oncomplete = function(e) { if (options.success) options.success(e); }; trx.onerror = function(e) { if (options.error) options.error(e); } var do_move_item = function(item, success) { if (options.transform) { item = options.transform(item); } var store = trx.objectStore(to); var req = store.add(item); req.onsuccess = success; }; var store = trx.objectStore(from); var req; if (from == tbl.buried) { // if we're looking up the buried table, we use the "id" index var index = store.index('id'); req = index.get(id); } else { req = store.get(id); } req.onsuccess = function(e) { var item = req.result; if (!item) { if (options.error) options.error(new HustleNotFound('item ' + id + ' wasn\'t found')); return; } var item_id = item.id; // account for the buried table's IDs if (from == tbl.buried) item_id = item._id; do_move_item(item, function(e) { var req = store.delete(item_id); req.onerror = options.error; }); }; }; /** * wrapper to create a new queue item for storage in the DB * * valid option values are 'priority' */ var create_queue_item = function(data, options) { var item = { data: data }; var fields = [{ name: 'priority', type: 'int', default: 1024 }, { name: 'delay', type: 'int', default: 0 }, { name: 'ttr', type: 'int', default: 0 }]; // loop over our fields, making sure they are the correct type and // format. for (var i = 0; i < fields.length; i++) { var field = fields[i]; if (options[field.name]) { item[field.name] = options[field.name]; switch (field.type) { case 'int': item[field.name] = parseInt(item[field.name]); break; case 'float': item[field.name] = parseFloat(item[field.name]); break; } } if (field.default && typeof item[field.name] == 'undefined') { item[field.name] = field.default; } } // some defaults item.age = 0; item.reserves = 0; item.releases = 0; item.timeouts = 0; item.buries = 0; item.kicks = 0; item.created = new Date().getTime(); return item; }; // --------------------------------------------------------------------- // queue interface functions // --------------------------------------------------------------------- /** * grab an item by id from the queue */ var peek = function(id, options) { check_db(); options || (options = {}); if (!id) { if (options.error) options.error(new HustleBadId('bad id given')); return false; } var item = null; var tables = [tbl.reserved, tbl.delayed, tbl.buried].concat(qoptions.tubes); var trx = db.transaction(tables, 'readonly'); trx.oncomplete = function(e) { if (!item && options.not_found_error) { if (options.error) options.error(new HustleNotFound('item ' + id + ' not found')); return; } if (options.success) options.success(item, e); }; trx.onerror = function(e) { if (options.error) options.error(e); }; // scan all tables for this id tables.forEach(function(table) { var req; if (table == tbl.buried) { var index = trx.objectStore(table).index('id'); req = index.get(id); } else { req = trx.objectStore(table).get(id); } req.onsuccess = function(e) { var res = e && e.target && e.target.result; if (item || !res) return false; item = res; item.age = Math.round((new Date().getTime() - item.expire) / 1000); if (table == tbl.reserved) { item.state = 'reserved'; if (item.ttr > 0) { item.time_left = Math.round((item.expire - new Date().getTime()) / 1000); } } else if (table == tbl.buried) { item.state = 'buried'; } else { item.state = 'ready'; if (!item.tube) item.tube = table; } } }); }; /** * put a new item in the queue in the specified tube (or the "default" * tube) */ var put = function(data, options) { var getAllItems = function (tube, options) { var tr = db.transaction([tube], 'readonly'); var store = tr.objectStore(tube); if (store.getAll) { store.getAll().onsuccess = function (e) { if (options.success) options.success(e.target.result); }; } else { var items = []; store.openCursor().onsuccess = function (e) { var cursor = e.target.result; if (cursor) { items.push(cursor.value); cursor.continue(); } else { options.success(items); } } } }; check_db(); options || (options = {}); if (!data) return false; var tube = options.tube ? options.tube : 'default'; if (qoptions.tubes.indexOf(tube) < 0) throw new HustleBadTube('tube ' + tube + ' doesn\'t exist'); var item = create_queue_item(data, options); if (item.delay && item.delay > 0) { item.tube = tube; item.activate = new Date().getTime() + (1000 * item.delay); tube = tbl.delayed; delete item.delayed; } var comparator = options.comparator; var createItem = function () { // grab a unique ID for this item new_id({ success: function(id) { item.id = id; var trx = db.transaction([tube], 'readwrite'); trx.oncomplete = function(e) { if (options.success) options.success(item, e); }; trx.onerror = function(e) { if (options.error) options.error(e); }; var store = trx.objectStore(tube); store.openCursor().onsuccess = function (e) { }; var req = store.add(item); req.onsuccess = function(e) { item.id = e.target.result; }; }, error: function(e) { if (options.error) options.error(new HustleBadID('error generating id')); } }); }; var addUniqueItemToQueue = function (items) { var existingItem = items.find(function (currentItem) { return comparator(item.data, currentItem.data); }); existingItem ? options.success(existingItem) : createItem(); }; comparator ? getAllItems(tube, {success: addUniqueItemToQueue}) : createItem(); }; /** * grab one item off of the given tube (or "default" tube) and move it * onto the reserved table. */ var reserve = function(options) { check_db(); options || (options = {}); var tube = options.tube ? options.tube : 'default'; if (qoptions.tubes.indexOf(tube) < 0) throw new HustleBadTube('tube ' + tube + ' doesn\'t exist'); var item = null; var trx = db.transaction([tbl.reserved, tube], 'readwrite'); trx.oncomplete = function(e) { if (options.success) options.success(item, e); }; trx.onerror = function(e) { if (options.error) options.error(e); } // called once we have an item, puts the item in the reserved table var put_in_reserved = function(citem, success) { item = citem; item.reserves++; item.tube = tube; if (item.ttr > 0) { item.expire = new Date().getTime() + (1000 * item.ttr); } var store = trx.objectStore(tbl.reserved); var req = store.add(item); req.onsuccess = success; }; // grab one item from the tube, and put it in reserved var store = trx.objectStore(tube); var index = store.index('priority'); index.openCursor().onsuccess = function(e) { var cursor = e.target.result; if (cursor) { put_in_reserved(cursor.value, function(e) { // remove the item from the tube once we know it's reserved var req = store.delete(cursor.value.id); req.onerror = options.error; }); } }; }; /** * delete a queue item by id. checks all tubes and the reserved/buried * tables as well. */ var del = function(id, options) { check_db(); options || (options = {}); peek(id, { success: function(item) { if (!item) { if (options.success) options.success(null); return; } var table = item.tube; var item_id = item.id; if (item.state == 'reserved') { table = tbl.reserved; } else if (item.state == 'buried') { table = tbl.buried; // be mindful of the buried table's own IDs item_id = item._id; } var trx = db.transaction(table, 'readwrite'); trx.oncomplete = function(e) { if (options.success) options.success(item, e); }; trx.onerror = function(e) { if (options.error) options.error(e); } trx.objectStore(table).delete(item_id); }, error: options.error }); }; /** * release a reserved item back into the queue (from the tube it came * from). */ var release = function(id, options) { check_db(); options || (options = {}); peek(id, { not_found_error: true, success: function(item) { if (item.state != 'reserved') { if (options.error) options.error(new HustleNotFound('item ' + id + ' isn\'t reserved')); return; } var tube = item.tube; if (options.delay) { var delay = parseInt(options.delay); if (delay) { item.activate = new Date().getTime() + (1000 * delay); tube = tbl.delayed; } } move_item(id, tbl.reserved, tube, { transform: function(item) { item.releases++; if (options.priority) { var pri = parseInt(options.priority); if (pri) item.priority = pri; } delete item.tube; return item; }, success: options.success, error: options.error }); }, error: options.error }); }; /** * move an item to the buried table. this is a great way to track items * that fail a lot and can't be ignored, allowing you to look over them * later on and see what jobs are failing. */ var bury = function(id, options) { check_db(); options || (options = {}); peek(id, { not_found_error: true, success: function(item) { if (item.state == 'buried') { if (options.success) options.success(); return; } var table = item.tube; if (item.state == 'reserved') { table = tbl.reserved; } move_item(id, table, tbl.buried, { transform: function(titem) { titem.buries++; titem.tube = item.tube; if (options.priority) { var pri = parseInt(options.priority); if (pri) titem.priority = pri; } return titem; }, success: options.success, error: options.error }); }, error: options.error }); }; /** * kick N many buried items back into their tubes */ var kick = function(num, options) { check_db(); options || (options = {}); var records = 0; // open all tables since we may get a range of tubes when kicking var tables = [tbl.buried].concat(qoptions.tubes); var trx = db.transaction(tables, 'readwrite'); trx.oncomplete = function(e) { if (options.success) options.success(records, e); }; trx.onerror = function(e) { if (options.error) options.error(e); } var put_in_tube = function(item, success) { item.kicks++; var tube = item.tube; // remove the buried table's ID delete item._id; delete item.tube; var store = trx.objectStore(tube) var req = store.add(item); req.onsuccess = success; }; // grab one item from the tube, and put it in reserved var store = trx.objectStore(tbl.buried); store.openCursor().onsuccess = function(e) { var cursor = e.target.result; if (cursor) { put_in_tube(cursor.value, function(e) { // remove the item from the tube once we know it's reserved var req = store.delete(cursor.key); req.onerror = options.error; }); records++; if (records < num) cursor.continue(); } }; }; /** * kick a job from the buried table by its id */ var kick_job = function(id, options) { check_db(); options || (options = {}); peek(id, { not_found_error: true, success: function(item) { if (item.state != 'buried') { if (options.error) options.error(new HustleNotFound('item ' + id + ' isn\'t buried')); return; } move_item(id, tbl.buried, item.tube, { transform: function(item) { item.kicks++; delete item._id; delete item.tube; return item; }, success: options.success, error: options.error }); }, error: options.error }); }; /** * reset a job's ttr */ var touch = function(id, options) { check_db(); options || (options = {}); peek(id, { not_found_error: true, success: function(item) { if (item.state != 'reserved') { console.log('item.state: ', item.state); if (options.error) options.error(new HustleNotFound('item ' + id + ' isn\'t reserved')); return; } if (item.ttr <= 0) { if (options.success) options.success(); return; } var trx = db.transaction(tbl.reserved, 'readwrite'); trx.oncomplete = function(e) { if (options.success) options.success(e); }; trx.onerror = function(e) { if (options.error) options.error(e); } var store = trx.objectStore(tbl.reserved); var req = store.get(id); req.onsuccess = function(e) { var item = req.result; item.expire = new Date().getTime() + (item.ttr * 1000); store.put(item); }; }, error: options.error }); }; var count_reserved = function(options) { check_db(); options || (options = {}); var count = null; var trx = db.transaction(tbl.reserved, 'readonly'); trx.oncomplete = function(e) { if (options.success) options.success(count, e); }; trx.onerror = function(e) { if (options.error) options.error(e); }; var store = trx.objectStore(tbl.reserved); var req = store.count(); req.onsuccess = function(e) { count = req.result; }; }; var count_ready = function(tube, options) { check_db(); options || (options = {}); if (qoptions.tubes.indexOf(tube) < 0) throw new HustleBadTube('tube ' + tube + ' doesn\'t exist'); var count = null; var trx = db.transaction(tube, 'readonly'); trx.oncomplete = function(e) { if (options.success) options.success(count, e); }; trx.onerror = function(e) { if (options.error) options.error(e); } var store = trx.objectStore(tube); var req = store.count(); req.onsuccess = function(e) { count = req.result; }; }; /** * A class that makes consumption of a tube more manageable. For each * reserved item, calls the given handler function. * * Has two public methods: start and stop. The consumer is started by * default on instantiation. */ var Consumer = function(fn, coptions) { coptions || (coptions = {}); var tube = coptions.tube ? coptions.tube : 'default'; var delay = coptions.delay ? coptions.delay : 100; var do_stop = false; var poll = function(options) { options || (options = {}); if (do_stop || !db) return; if (coptions.enable_fn) { var res = coptions.enable_fn(); if (!res) { do_stop = true; return false; } } // grab an item from the tube reserve({ tube: tube, success: function(item) { if (!item) return; fn(item); // immediately poll for new items setTimeout(function() { poll({ skip_recurse: true }); }, 0); } }); // poll again if (!options.skip_recurse) setTimeout(poll, delay); }; var start = function() { if (!do_stop) return false; do_stop = false; setTimeout(poll, delay); return true; }; var stop = function() { if (do_stop) return false; do_stop = true; return true; }; setTimeout(poll, delay); this.start = start; this.stop = stop; return this; }; // --------------------------------------------------------------------- // maintenance/cleanup // --------------------------------------------------------------------- /** * move jobs in the delayed state into their respective tubes */ var move_delayed_jobs_to_ready = function(options) { options || (options = {}); var move_items = []; var trx = db.transaction(tbl.delayed, 'readonly'); trx.oncomplete = function(e) { move_items.forEach(function(item) { move_item(item.id, tbl.delayed, item.tube, { error: function(e) { console.error('Hustle: delayed move: ', e); } }); }); }; trx.onerror = function(e) { console.error('Hustle: delayed move: ', e); if (options.error) options.error(e); } var store = trx.objectStore(tbl.delayed); var index = store.index('activate'); var bound = new Date().getTime(); var range = IDBKeyRange.upperBound(bound); index.openCursor(range).onsuccess = function(e) { var cursor = e.target.result; if (cursor) { move_items.push(cursor.value); cursor.continue(); } } }; /** * move expired jobs to their ready tube */ var move_expired_jobs_to_ready = function(options) { options || (options = {}); var move_items = []; var trx = db.transaction(tbl.reserved, 'readonly'); trx.oncomplete = function(e) { move_items.forEach(function(item) { move_item(item.id, tbl.reserved, item.tube, { transform: function(item) { delete item.expire; item.timeouts++; return item; }, error: function(e) { if (e instanceof HustleNotFound) { move_items.erase(item); } console.error('Hustle: ttr move: ', e); } }); }); }; trx.onerror = function(e) { console.error('Hustle: ttr move: ', e); if (options.error) options.error(e); } var store = trx.objectStore(tbl.reserved); var index = store.index('expire'); var bound = new Date().getTime(); var range = IDBKeyRange.upperBound(bound); index.openCursor(range).onsuccess = function(e) { var cursor = e.target.result; if (cursor) { move_items.push(cursor.value); cursor.continue(); } } }; /** * bury any items in the reserved queue */ var cleanup_abandoned_items = function(options) { options || (options = {}); var abandoned_items = []; var number_of_buried_items = 0; var move_items_to_buried = function() { abandoned_items.forEach(function(item) { move_item(item.id, tbl.reserved, tbl.buried, { transform: function(transform_item) { transform_item.abandoned = true; return transform_item; }, success: exit_after_processing_all_items, error: function(e) { if (options.error) options.error(e); } }); }); }; var exit_after_processing_all_items = function() { number_of_buried_items++; if(number_of_buried_items == abandoned_items.length) { if (options.success) options.success(); } }; var trx = db.transaction(tbl.reserved, 'readonly'); trx.oncomplete = function() { if (abandoned_items.length == 0) { if (options.success) options.success(); } else { move_items_to_buried(); } }; trx.onerror = function(e) { if (options.error) options.error(e); }; var store = trx.objectStore(tbl.reserved); store.openCursor().onsuccess = function(e) { var cursor = e.target.result; if (cursor) { abandoned_items.push(cursor.value); cursor.continue(); } } }; /** * rescues any items in the reserved queue by moving it to its tube * or moving it to buried queue if it exceeds a maximum rescue limit */ var rescue_reserved_items = function (options) { var items_in_reserved = []; var number_of_rescued_items = 0; var DEFAULT_RESCUE_LIMIT = 5, DEFAULT_RESCUE_TIME_LIMIT_IN_SECONDS = 60; var maxRescueLimit = options.maxRescueLimit || DEFAULT_RESCUE_LIMIT; var rescueTimeLimitInSeconds = options.rescueTimeLimitInSeconds || DEFAULT_RESCUE_TIME_LIMIT_IN_SECONDS; var exit_after_processing_all_items = function () { number_of_rescued_items++; if(number_of_rescued_items == items_in_reserved.length) { if(options.success) options.success(); } }; var move_items_in_reserved = function () { var move_item_to_tube = function (item) { /** * increase number of times it has been rescued based on timestamp it was rescued last */ var should_increase_abandon_count = function (item) { if(!item.lastAbandonedTime) { return true; } var timeInSecondsAfterLastAbandoned = (new Date().getTime() - item.lastAbandonedTime)/1000; return timeInSecondsAfterLastAbandoned > rescueTimeLimitInSeconds; }; move_item(item.id, tbl.reserved, item.tube, { transform: function (item) { if(should_increase_abandon_count(item)) { item.lastAbandonedTime = new Date(); item.abandon_count = item.abandon_count || 0; item.abandon_count++; } return item; }, success: exit_after_processing_all_items, error: function (e) { if (options.error) options.error(e); } }); }; var move_item_to_buried = function (item) { move_item(item.id, tbl.reserved, tbl.buried, { success: exit_after_processing_all_items, error: function (e) { if (options.error) options.error(e); } }); }; items_in_reserved.forEach(function (item) { var shouldRescue = !item.abandon_count || maxRescueLimit > item.abandon_count; if(shouldRescue) { move_item_to_tube(item); } else { move_item_to_buried(item); } }); }; var trx = db.transaction(tbl.reserved, 'readonly'); trx.oncomplete = function () { if (items_in_reserved.length == 0) { if(options.success) options.success(); } else { move_items_in_reserved(); } }; trx.onerror = function(e) { if (options.error) options.error(e); }; var store = trx.objectStore(tbl.reserved); store.openCursor().onsuccess = function (e) { var cursor = e.target.result; if(cursor) { items_in_reserved.push(cursor.value); cursor.continue(); } }; }; /** * this function does database cleanup. only runs while db is open. */ do_maintenance = function() { var run_maintenance = function() { if (!db) return false; move_delayed_jobs_to_ready(); move_expired_jobs_to_ready(); setTimeout(run_maintenance, maintenance_delay); }; setTimeout(run_maintenance, maintenance_delay); }; // --------------------------------------------------------------------- // exports // --------------------------------------------------------------------- var Queue = { peek: peek, put: put, reserve: reserve, delete: del, release: release, bury: bury, kick: kick, kick_job: kick_job, touch: touch, count_ready: count_ready, count_reserved: count_reserved, cleanup_abandoned_items: cleanup_abandoned_items, rescue_reserved_items: rescue_reserved_items, Consumer: Consumer }; this.open = open; this.close = close; this.is_open = function() { return !!db; }; this.wipe = wipe; this.Error = Error; this.Queue = Queue; this.promisify = function() { var _self = this; var do_promisify = function(fn, opts_idx) { return function() { var args = Array.prototype.slice.call(arguments, 0); if (!args[opts_idx]) args[opts_idx] = {}; return new Promise(function(success, error) { args[opts_idx].success = success; args[opts_idx].error = error; fn.apply(_self, args); }); }; }; this.open = do_promisify(this.open, 0); this.Queue.peek = do_promisify(this.Queue.peek, 1); this.Queue.put = do_promisify(this.Queue.put, 1); this.Queue.reserve = do_promisify(this.Queue.reserve, 0); this.Queue.delete = do_promisify(this.Queue.delete, 1); this.Queue.release = do_promisify(this.Queue.release, 1); this.Queue.bury = do_promisify(this.Queue.bury, 1); this.Queue.kick = do_promisify(this.Queue.kick, 1); this.Queue.kick_job = do_promisify(this.Queue.kick_job, 1); this.Queue.touch = do_promisify(this.Queue.touch, 1); this.Queue.count_ready = do_promisify(this.Queue.count_ready, 1); this.Queue.count_reserved = do_promisify(this.Queue.count_reserved, 0); this.Queue.cleanup_abandoned_items = do_promisify(this.Queue.cleanup_abandoned_items, 0); this.Queue.rescue_reserved_items = do_promisify(this.Queue.rescue_reserved_items, 0); return this; }.bind(this); this.debug = { get_db: function() { return db; } }; return this; }; Hustle.Error = { DBClosed: HustleDBClosed, DBOpened: HustleDBOpened, BadTube: HustleBadTube, BadID: HustleBadID, NotFound: HustleNotFound }; window.Hustle = Hustle; })(window);