· 8 years ago · May 07, 2018, 05:10 PM
1/* Scheduler Functions */
2
3
4//Global Shared variables
5var gAppointmentClickedOn; //Contains the appointment when a context menu is shown outside of the context menu event
6var gZoomScale = 1; //Default zoom of 100%
7var gLocProviderNodeList; //List of provider divs on location calendar
8var tsMainMenu; //Main menu tab strip
9var gShowOnlySelectedProviders = false; //State of 'Show only selected' checkbox on Prov Dock
10var gPressedKey = ''; //Key currently pressed down
11var gLongClick = false; //detects long click on a mobile device
12var gHideContextMenu = false; //flag for hiding/showing context menu on mobile view
13var gIsAptStyled = false;
14var gOpenShiftSelected = false; //flag used for detecting if ctrl + click was done on an open shift
15var gRcbPractAllText = "All"; //Text for rcbPractType on request calendar when all is selected
16var gRcbPractMinWidth = 100; // Min width for rcbPractType box
17var gRcbPractMaxWidth = 160; // Max width for rcbPractType box
18var gBoolSaveColor = true;
19var gCheckedProviders = []; //TJF 7/5/2016 - Once a provider is checked, there id becomes the key and checked status is the data
20var gProvColors = []; //TJF 7/5/2016 - Prov colors keyed by primary key
21var gSetManuallyAssignedMenuItemText = "Set Shifts as Manually Assigned"; //JF 9/1/2017 - Base string for menu item
22var gSetShiftStatusMenuItemText = "Set Shift Status"; //TJF 12/5/2017 - Base string for shift status text
23
24/**********************************************************/
25/* GENERAL */
26/**********************************************************/
27
28/*Application area constants mirror IEvents server constants
29MTE 7/29/2016 - Added Areas to keep in sync with IEvent.cs enum ApplicationArea
30MTE 10/4/2016 - Added TangierWebServices
31TJF 1/27/2017 - Added time and accounting
32*/
33var ApplicationArea = {
34 AdminCalendar: 0,
35 ProvCalendar: 1,
36 TangierEPS: 2,
37 TangierWeb: 3,
38 TangierMobile: 4,
39 TangierSky: 5,
40 TangierMobileAdmin: 6,
41 TangierSkyAdmin: 7,
42 ResetPassword: 8,
43 TangierNotification: 9,
44 TangierWebService: 10
45};
46
47/*Provider Schedule View constants mirror WebEnums.ProviderScheduleView server constants*/
48/*JCL 9/25/2017 - Added Swap Exchange*/
49var ProviderScheduleView = {
50 Personal: 0,
51 Location: 1,
52 Request: 2,
53 SwapExchange: 3
54};
55
56/*EventType constants mirror WebEnums.AdminMenuBarItem server constants*/
57var AdminMenuBarItem = {
58 AdminCalendar: 0,
59 RequestCalendar: 1,
60 Accounting: 2,
61 Reports: 3,
62 Logout: 4
63};
64
65/*Event view types for calendar*/
66var EventViewType = {
67 Simple: 0,
68 Standard: 1,
69 Detail: 2
70};
71
72function initMainMenu() {
73 //Set main menu tab strip as global variable
74 tsMainMenu = $find("tsMainMenu");
75}
76
77//Toggle Display Settings display and save state to cookie
78function toggleDisplaySettings() {
79
80 var cookieName = getDisplaySettingsHiddenCookieName();
81 var target = $("#calDisplaySettings");
82 var exdays = 30;
83
84 //Persist state of Display Settings in Cookie
85 if ($(target).is(':hidden')) {
86 setCookie(cookieName, "0", exdays);
87 } else {
88 setCookie(cookieName, "1", exdays);
89 }
90
91 $(target).slideToggle("fast");
92
93 toggleDisplaySettingsIcon();
94}
95
96//NMM 10/13/2015 - toggle the expand/collapse icon
97function toggleDisplaySettingsIcon() {
98
99 var icon = document.getElementById("lblDisplaySettings");
100
101 if ($('#lblDisplaySettings').hasClass("expanded")) {
102 icon.className = icon.className.replace("expanded", "");
103 icon.className += " collapsed";
104 } else {
105 icon.className = icon.className.replace("collapsed", "");
106 icon.className += " expanded";
107 }
108}
109
110//VJF 9/11/2015 - Get name of cookie that contains the visibility state of Display Settings.
111//The name is unique by application area and selected tab.
112function getDisplaySettingsHiddenCookieName() {
113 return 'DispSettingsHidden' + getAppArea() + '|' + tsMainMenu.get_selectedTab().get_value();
114}
115
116//Refresh calendar
117//This is requested by child windows that have changed data.
118//VJF 10/05/2015 - Added optional parameter sTargetControlId. This will cause the ajax refresh to occur
119//as if a change in the TargetControlId ctrl occurred. It allows custom control of the display panel and controls refreshed.
120//VJF 02/03/2017 - Added optional parameter indicating where to reload the data. The default is to reload.
121function refreshCalendar(sTargetControlId, bReloadData) {
122
123 bReloadData = (bReloadData == undefined || bReloadData != false);
124
125 if (bReloadData) {
126 //VJF 9/24/2015 - Pages need to know when the calendar needs to be refreshed
127 var hdnRefresh = document.getElementById("hdnRefreshCalendar");
128 if (hdnRefresh) {
129 hdnRefresh.value = "1";
130 }
131
132 //VJF 12/28/2015 - Pages need to know when the admin calendar needs to be refreshed
133 var hdnReload = document.getElementById("hdnReloadAdminCalendar");
134 if (hdnReload) {
135 hdnReload.value = "1";
136 }
137 }
138
139 var ajaxMgr = $find("ramScheduleAjaxMgr");
140
141 if (ajaxMgr) {
142
143 if (sTargetControlId != undefined
144 && typeof (sTargetControlId) === "string"
145 && sTargetControlId.trim.length > 0
146 && document.getElementById(sTargetControlId)) {
147 //Perform refresh as if initiated from provided control
148 ajaxMgr.ajaxRequestWithTarget(sTargetControlId, "Rebind");
149 } else {
150 ajaxMgr.ajaxRequest("Rebind");
151 }
152 }
153}
154
155/**********************************************************/
156/* CUSTOMIZATIONS */
157/**********************************************************/
158
159function restoreCustomState() {
160
161 //Determine whether to restore state of calDisplaySettings
162 if (document.forms.frmScheduler.hdnShowDisplaySettings.value == 'true') {
163 var isExpanded = true;
164 try {
165 if (getCookie) {
166 //Restore visibility state of Display Settings
167 var dispSettingState = getCookie(getDisplaySettingsHiddenCookieName());
168 var domElem = document.getElementById('calDisplaySettings');
169 var elem = $(domElem); //wrap object in jquery so we can use it's show/hide functions
170
171 if (dispSettingState == "0" || dispSettingState == null) { //The first time in there will be no cookie and settings will show
172 elem.show();
173 isExpanded = true;
174 } else {
175 isExpanded = false;
176 }
177 }
178 } catch (err) {
179 //REMOVE: For testing only
180 //alert("getCookie=" + typeof (getCookie) + " " + (typeof (getCookie) == "undefined") + " " + (getCookie == undefined));
181 }
182
183 //When "Display settings" is displayed and granular filtering is being used
184 //add a css class to the display settings label to visually alert user filtering is being applied
185 var lblDispSettings = document.getElementById('lblDisplaySettings');
186 if (lblDispSettings) {
187 var classSelection = ' selection';
188 if (document.getElementById('pnlEventFilter') && HasGranularEventFiltering()) {
189
190 //Add selection css class if it doesn't exist
191 if (lblDispSettings.className.indexOf(classSelection) == -1) {
192 lblDispSettings.className += classSelection;
193 }
194
195 } else {
196 //Remove selection css class
197 lblDispSettings.className = lblDispSettings.className.replace(classSelection, "");
198 }
199
200 if (isExpanded) {
201 lblDispSettings.className = lblDispSettings.className.replace("collapsed", "");
202 lblDispSettings.className += " expanded";
203 } else {
204 lblDispSettings.className = lblDispSettings.className.replace("expanded", "");
205 lblDispSettings.className += " collapsed";
206 }
207
208 }
209 }
210
211 //Determine zoom scale to apply from cookie for Calendar or Location
212 var nZoomScale;
213 if ((getAppArea() == ApplicationArea.ProvCalendar && tsMainMenu.get_selectedTab().get_value() == ProviderScheduleView.Location) ||
214 (getAppArea() == ApplicationArea.ProvCalendar && tsMainMenu.get_selectedTab().get_value() == ProviderScheduleView.SwapExchange) ||
215 (getAppArea() == ApplicationArea.AdminCalendar && tsMainMenu.get_selectedTab().get_value() == AdminMenuBarItem.AdminCalendar)) {
216 nZoomScale = getCookie('ZoomScaleLoc');
217 } else {
218 nZoomScale = getCookie('ZoomScaleCal');
219 }
220
221 //Store zoom scale globally
222 gZoomScale = setZoomView(nZoomScale);
223
224}
225
226
227/**********************************************************/
228/* CONTEXT MENUS */
229/**********************************************************/
230/*Appointment attribute constants that mirror WebConstants server constants*/
231var WebConstants = {
232 CAL_APPT_ATTR_TOKEN_APPT_TYPE: 'at', //Appointment type to distinguish between: shift, request, personal, meeting etc
233 CAL_APPT_ATTR_TOKEN_APPT_ID: 'ai', //The value will be the unique id info needed to process the appointment type, i.e. definition id
234 CAL_APPT_ATTR_TOKEN_TASK_ID: 'ti', //The task associated with the appointment, see WebEnums.ProviderTask
235 CAL_APPT_ATTR_TOKEN_PROV_ID: 'pi', //Provider Id
236 CAL_APPT_ATTR_TOKEN_ALLOW_REQUEST: 'ar', //Flag indicating whether to allow requests
237 CAL_APPT_ATTR_TOKEN_ALLOW_SWAP_AFTER_SWAP_DATE: 'sa', //Flag indicating whether to allow swap requests after swap date
238 CAL_APPT_ATTR_TOKEN_SHIFT_DURATION: 'sd', //VJF 10/26/2016 - Shift duration in minutes, applicable to Admin Schedule.
239 CAL_APPT_ATTR_TOKEN_COUNT_SHIFT: 'cs', //VJF 12/09/2016 - Flag indicating whether the shift gets counted toward provider totals.
240 CAL_APPT_ATTR_REASON_ID: 'rr', //TJF 2/22/2017 - ID of an off request's reason for filtering on request calendar
241 CAL_APPT_ATTR_TOKEN_SHIFT_STATUS_ID: 'ss', //MTE 3/7/2017 - Shift Status Id to be used to turn on/off context menu based on the shift current status.
242 CAL_APPT_ATTR_TOKEN_TEMPLATE_ASSIGNMENT: 'tp', //VJF 12/14/2017 - Flag indicating where the shift is a template assignment for color coding.
243
244
245 CM_NO_ACTION_ID: 'cmNoAct', //No Action Context menu
246 CM_ADMIN_SHIFT_ID: 'cmAdminShift', //Scheduler Shift Context menu
247 CM_ADMIN_SHIFT_OPEN_ID: 'cmAdminOpen', //Scheduler Open Shift Context menu
248 CM_ADMIN_TIME_SLOT: 'cmAdminTimeSlot', //Scheduler Time Slot Context menu
249
250 //context menu item labels
251 CMI_REMOVE_PROVIDER_LABEL: 'Remove Providers',
252 CMI_ADMIN_SWAP_PROVIDERS_LABEL: ' Swap Providers...',
253 CMI_ASSIGN_PROVIDER_LABEL: 'Assign Provider...',
254 CMI_REMOVE_PROVIDER_FROM_ALL_LABEL: 'Remove Provider From All Shifts In Selected Period...',
255 CMI_HELP_LABEL: "Help"
256
257};
258
259
260/*EventType constants mirror that TangierSchedule.EventType server constants*/
261var EventType = {
262 Personal: 0,
263 Request: 1,
264 Shift: 2,
265 Annotation: 3,
266 Meeting: 4,
267 SwapShift: 5,
268 SwapShiftSelected: 6,
269 Unknown: 7
270};
271
272var ProviderTask = {
273 OffRequest: 0,
274 ShiftRequest: 1,
275 PersonalEvent: 2,
276 SwapRequest1Way: 3,
277 TimeSheet: 4,
278 SwapResponse: 5,
279 ShiftDetails: 6,
280 SwapRequest2Way: 7,
281 SplitShifts: 8,
282 Meeting: 9,
283 SwapRequest: 10,
284 SwapShiftProcess: 11,
285 SwapShiftReview: 12
286};
287
288/*MTE 2/14/2017 - Added Change Shift Status*/
289/*MTE 7/12/2017 - Added Shift Comment*/
290/*VJF 4/25/2018 - Added Add and Copy shift*/
291var AdminTask = {
292 AssignProvider: 0,
293 ShiftDetails: 1,
294 AdjustPlanned: 2,
295 AdjustWorked: 3,
296 EligibleProviders: 4,
297 RemoveProviders: 5,
298 OffRequest: 6,
299 ShiftRequest: 7,
300 ShiftAuditTrail: 8,
301 SelectProvider: 9,
302 SwapProviders: 10,
303 AssignProviderReplaceExisting: 11,
304 ChangeShiftStatus: 12,
305 RemoveShiftStatus: 13,
306 RemoveProviderInPeriod: 14,
307 ShiftComment: 15,
308 SetManuallyAssigned: 16,
309 AddCompensationAdjustment: 17,
310 AddShift: 18,
311 CopyShift: 19
312};
313
314
315//Used for removing/assigning shift on the client. This describes the shift passed back from the server whether
316//they process of removing/assigning were successful, had errors, had broken rules so we can manipulate the html
317//to reflect the changes.
318var ShiftAssignmentStatus = {
319 CRITICAL: 'critical',
320 WARNING: 'warning',
321 NONE: 'none',
322 ERROR: 'error'
323};
324
325var ProvColorType = {
326 CLEAR: 'clear',
327 PERMANENT: 'permanent',
328 TEMPORARY: 'temporary'
329}
330
331//Event handler when time slot is clicked
332//Show the New Tasks context menu
333function TimeSlotClicked(sender, eventArgs) {
334 //Get the context menu from the scheduler.
335 //It should be first and only in array, NewTasksContextMenu
336 var contextMenu = sender.get_timeSlotContextMenus()[0];
337 if (contextMenu != undefined) {
338 contextMenu.show(eventArgs.get_domEvent());
339 //contextMenu.showAt(eventArgs.get_domEvent().clientX, eventArgs.get_domEvent().clientY); //Needed for iPad
340 positionContextMenu(contextMenu, eventArgs.get_domEvent());
341 }
342
343}
344
345//Event that fires with the Time Slot right-click context menu
346function TimeSlotContextMenu(sender, eventArgs) {
347
348 //Position context menu after context menu appears
349 setTimeout(function () { TimeSlotClicked(sender, eventArgs); }, 0);
350}
351
352//Show time slot context menu based on event, not scheduler built-in event
353function ShowTimeSlotContextMenu(event) {
354
355 var scheduler = $find('rsScheduler');
356 contextMenu = scheduler.get_timeSlotContextMenus()[0];
357 if (contextMenu != undefined) {
358 contextMenu.show(event);
359 positionContextMenu(contextMenu, event);
360 }
361 return false;
362}
363
364//Event that fires with the Time Slot right-click context menu
365function AppointmentContextMenu(sender, eventArgs) {
366
367 //NMM 2/10/2016 - If macOS we are gonna use the alt key to multi select.
368 if ($telerik.isSafari && eventArgs.get_domEvent().altKey) {
369 AppointmentClicked(sender, eventArgs);
370 return;
371 }
372
373
374 gAppointmentClickedOn = null; //Reset so we know context menu was not shown via a app click
375
376 //Position context menu after context menu appears
377 var contextMenu = eventArgs.get_appointment().get_contextMenu();
378 var apt = eventArgs.get_appointment();
379 gHideContextMenu = false;
380
381 //Hides the context menu when doing a long touch on a mobile device.
382 if ($telerik.isTouchDevice) {
383 gLongClick = true;
384
385 //flag for hiding the context menu on a long click
386 gHideContextMenu = true;
387
388 //hide the context menu
389 $('ul.rmActive').addClass("hideAptContextMenu");
390
391 //do not show the browser's context menu
392 window.oncontextmenu = function (event) {
393 event.preventDefault();
394 event.stopPropagation();
395 return false;
396 };
397 } else {
398 setTimeout(function () { positionContextMenu(contextMenu, eventArgs.get_domEvent()); }, 0);
399 }
400
401 //We do the appointment stylings on the AdminCalendar page ONLY.
402 if (getAppArea() == ApplicationArea.AdminCalendar && tsMainMenu.get_selectedTab().get_value() == AdminMenuBarItem.AdminCalendar) {
403 //NMM 11/17/2015 - Apply styling on selected appointment
404 var IsRightClickDetected = true;
405 ApplySelectedAppointmentStyling(sender, eventArgs, eventArgs.get_appointment(), IsRightClickDetected);
406 StructureContextMenuItems(apt, eventArgs);
407 }
408}
409
410//Event handler for appointment clicked
411//The default action is to show details of appointment
412function AppointmentClicked(sender, eventArgs) {
413 gAppointmentClickedOn = null; //Reset so we know when context menu is shown via a app click
414
415 var contextMenu = eventArgs.get_appointment().get_contextMenu();
416 var apt = eventArgs.get_appointment();
417
418 //NMM 12/14/2015 - We hide the context menu if a long click is pressed on a mobile device. This functionality is for the AdminCalendar page only
419 if (getAppArea() == ApplicationArea.AdminCalendar && tsMainMenu.get_selectedTab().get_value() == AdminMenuBarItem.AdminCalendar) {
420 if ($telerik.isTouchDevice) {
421 HideOrShowContextMenuOnMobile();
422 }
423 }
424
425 //Auto assign selected provider and bypass context menu when the ctrl or alt key is down and the appt clicked is for an open admin shift.
426 //Ctrl+Click doesn't work natively with Macs because that is defined for the context menu on a one-button mouse, so Alt and 'A' are include for auto select
427 var isAutoAssignProvider = (gPressedKey === 'A' && contextMenu.get_id().indexOf(WebConstants.CM_ADMIN_SHIFT_OPEN_ID) > -1);
428 var isAutoRemoveProvider = (gPressedKey === 'R' && contextMenu.get_id().indexOf(WebConstants.CM_ADMIN_SHIFT_ID) > -1);
429 var isAutoSelectProvider = ((gPressedKey === 'S' || eventArgs.get_domEvent().ctrlKey || eventArgs.get_domEvent().altKey) &&
430 (contextMenu.get_id().indexOf(WebConstants.CM_ADMIN_SHIFT_ID) > -1 || contextMenu.get_id().indexOf(WebConstants.CM_ADMIN_SHIFT_OPEN_ID) > -1));
431
432 //We do the appointment stylings on the AdminCalendar page ONLY.
433 if (getAppArea() == ApplicationArea.AdminCalendar && tsMainMenu.get_selectedTab().get_value() == AdminMenuBarItem.AdminCalendar) {
434 //Apply styling on selected appointment
435 var IsRightClickDetected = false;
436 ApplySelectedAppointmentStyling(sender, eventArgs, eventArgs.get_appointment(), IsRightClickDetected);
437 StructureContextMenuItems(apt, eventArgs);
438 }
439
440 //If this is a touch device show appointment context menu
441 //if ($telerik.isTouchDevice && contextMenu && contextMenu.get_items().get_count() > 1) {
442 //Don't bother if there is only one menu item to choose from, except if it the no action menu
443 //or it is an open admin shift and the ctrl key is down, indicating an assignment
444 if (contextMenu && (contextMenu.get_items().get_count() > 1 ||
445 contextMenu.get_items().get_count() == 1 && contextMenu.get_id().indexOf(WebConstants.CM_NO_ACTION_ID) > -1) &&
446 (!isAutoAssignProvider && !isAutoRemoveProvider && !isAutoSelectProvider)) {
447
448
449 contextMenu.show(eventArgs.get_domEvent());
450 //contextMenu.showAt(eventArgs.get_domEvent().clientX, eventArgs.get_domEvent().clientY);
451 positionContextMenu(contextMenu, eventArgs.get_domEvent());
452
453 //When showing a context menu manually the originating appointment
454 //needs to be saved for later user when item is selected in order to know context
455 gAppointmentClickedOn = eventArgs.get_appointment();
456
457 return;
458 }
459
460 var nTaskId;
461 var attribs = eventArgs.get_appointment().get_attributes();
462
463 //The appointment request type will determine what value to use for for the default task id
464 var apptType = attribs.getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_APPT_TYPE); //Appointment Type token
465
466 if (apptType == EventType.Shift) {
467
468 if (isAutoAssignProvider) {
469 //Assign provider to open shift
470 nTaskId = AdminTask.AssignProvider;
471 } else if (isAutoRemoveProvider) {
472 //Remove provider from shift assignment
473 nTaskId = AdminTask.RemoveProviders;
474 } else if (isAutoSelectProvider) {
475 //Select provider on shift assignment
476 nTaskId = AdminTask.SelectProvider;
477 } else if (contextMenu.get_id().indexOf(WebConstants.CM_ADMIN_SHIFT_ID) > -1
478 || contextMenu.get_id().indexOf(WebConstants.CM_ADMIN_SHIFT_OPEN_ID) > -1) {
479 //The default action for an admin shift is to view details
480 nTaskId = AdminTask.ShiftDetails;
481 } else {
482 //The default action for a provider shift is to view details
483 nTaskId = ProviderTask.ShiftDetails;
484 }
485
486 } else {
487 //Use task id attribute for a non-shift appointment
488 nTaskId = attribs.getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_TASK_ID);
489 }
490
491 //NMM 1/6/2016 - indicates whether the appointment clicked was an open shift or not
492 gOpenShiftSelected = contextMenu.get_id().indexOf(WebConstants.CM_ADMIN_SHIFT_OPEN_ID) > -1;
493 ProcessAppointmentAction(sender, eventArgs, nTaskId);
494}
495
496//Position context with adjustments for zoom
497function positionContextMenu(contextMenu, event) {
498
499 var origX = event.clientX;
500 var origY = event.clientY;
501 var scrollTop = getScrollTop();
502
503 //When zoom is applied the context menu needs to be adjusted proportionally
504 //and vertical scroll accounted for. Chrome and Safari appear to enforce a max x and y.
505 if (gZoomScale != 1 && !$telerik.isIE) {
506 var newX = origX / gZoomScale;
507 var newY = (origY + scrollTop) / gZoomScale;
508
509 contextMenu.showAt(newX, newY);
510 } else {
511 //iPad content menu is more consistent if always set
512 if ($telerik.isMobileSafari) {
513 contextMenu.showAt(origX, origY + scrollTop);
514 }
515 }
516}
517
518
519//VJF 4/14/2015 - Override calendar cell date header clicking on event. Default event is to show day view.
520function headerDateClickingOverride() {
521 //$telerik.$(".rsDateHeader").click(function (e) { return false; }); //Prevent showing Day View by disabling scheduler date links
522 $telerik.$(".rsDateHeader").click(function (e) { return ShowTimeSlotContextMenu(e); });
523}
524
525/**********************************************************/
526/* NAVIGATION */
527/**********************************************************/
528
529/*Navigation Constants mirror RadScheduler server constants. KEEP IN SYNCH: step into sender function and search resource for constants*/
530//VJF 11/30/2015 - Update and added new constants with release
531var SchedulerNavigationCommand = {
532 SwitchToDayView: 0,
533 SwitchToWeekView: 1,
534 SwitchToMonthView: 2,
535 SwitchToTimelineView: 3,
536 SwitchToMultiDayView: 4,
537 SwitchToAgendaView: 5,
538 SwitchToYearView: 6,
539 NavigateToNextPeriod: 7,
540 NavigateToPreviousPeriod: 8,
541 SwitchToSelectedDay: 9,
542 SwitchToSelectedMonth: 10,
543 SwitchFullTime: 11,
544 DisplayNextAppointmentSegment: 12,
545 DisplayPreviousAppointmentSegment: 13,
546 NavigateToSelectedDate: 14
547};
548
549var SchedulerViewType = {
550 DayView: 0,
551 WeekView: 1,
552 MonthView: 2,
553 TimelineView: 4,
554 MultiDayView: 5,
555 AgendaView: 6,
556 YearView: 7
557};
558
559var SchedulerViewCssClass = {
560 DayView: 'rsHeaderDay',
561 WeekView: 'rsHeaderWeek',
562 MonthView: 'rsHeaderMonth',
563 TimelineView: 'rsHeaderTimeline',
564 PeriodView: 'rsHeaderTimeline'
565};
566
567
568//Event handler when Navigation occurs
569//VJF 11/30/2015 - Enhanced to support multiple scheduler control display and Period view.
570function NavigationClicked(sender, eventArgs) {
571
572 var nNavCommand = eventArgs.get_command();
573 var nSelectedView = sender.get_selectedView();
574 var dtSelectedDate = sender.get_selectedDate();
575 var sDate = dateToSimpleString(dtSelectedDate);
576 var nCalView;
577 var dtSelectedSlotDate;
578 var isSlotSelected = false;
579 var nViewPeriodMultiplier = 1;
580
581 if (getAppArea() == ApplicationArea.AdminCalendar && tsMainMenu.get_selectedTab().get_value() == AdminMenuBarItem.AdminCalendar) {
582 //A custom Sched View control is being used, so don't use selected view of the sender/scheduler.
583 nSelectedView = document.forms.frmScheduler.hdnCalView.value;
584
585 //Get time period multiplier, if applicable.
586 var ddl = $find('ddlViewMultiplier');
587 if (ddl) {
588 nViewPeriodMultiplier = parseInt(ddl.get_selectedItem().get_value());
589 }
590 }
591
592 //Get date of first selected slots, if any.
593 //It will be the date navigated to in week or day view
594 if (sender.get_selectedSlots().length > 0 &&
595 ((getAppArea() == ApplicationArea.ProvCalendar && nSelectedView != SchedulerViewType.MonthView) ||
596 (getAppArea() == ApplicationArea.AdminCalendar))) {
597 dtSelectedSlotDate = sender.get_selectedSlots()[0].get_startTime();
598 isSlotSelected = true; //A specific date slot has been highlighted
599 } else {
600 dtSelectedSlotDate = dtSelectedDate;
601 }
602
603 //Viewable Dates are Changing
604 //Navigating Next
605 if (nNavCommand == SchedulerNavigationCommand.NavigateToNextPeriod) { //Next
606 if ((nSelectedView == SchedulerViewType.MonthView) ||
607 (nSelectedView == SchedulerViewType.AgendaView)) { //Month or Agenda
608 sDate = dateToSimpleString(addMonth(dtSelectedDate, nViewPeriodMultiplier));
609 } else if (nSelectedView == SchedulerViewType.WeekView) { //Week
610 sDate = dateToSimpleString(addWeek(dtSelectedDate, nViewPeriodMultiplier));
611
612 } else if (nSelectedView == SchedulerViewType.DayView) { //Day
613 sDate = dateToSimpleString(addDaysToDate(new Date(document.forms.frmScheduler.hdnSelectedDate.value), 1 * nViewPeriodMultiplier));
614
615 } else if (nSelectedView == SchedulerViewType.TimelineView) { //Timeline/Schedule Period
616 sDate = dateToSimpleString(addMonth(dtSelectedDate, 1));
617
618 //Get the start date from the next period in the drop down, otherwise use the day after the last period ended.
619 var ddl = $find('ddlSchedulePeriod');
620 if (ddl && ddl.get_selectedItem().get_index() < (ddl.get_items().get_count() - 1)) {
621 //Get the first day of the next period in the list
622 var dates = ddl.get_items().getItem(ddl.get_selectedItem().get_index() + 1).get_text().split(" - ");
623 if (dates.length == 2) {
624 sDate = dates[0];
625 }
626 } else if (ddl && ddl.get_selectedItem().get_text().indexOf(" - ") > -1) {
627 //Calculate the day after the selected period's end date
628 var dates = ddl.get_selectedItem().get_text().split(" - ");
629 if (dates.length == 2) {
630 sDate = dateToSimpleString(addDaysToDate(new Date(dates[1]), 1));
631 }
632 }
633 }
634
635 //Navigating Previous
636 } else if (nNavCommand == SchedulerNavigationCommand.NavigateToPreviousPeriod) { //Previous
637
638 if ((nSelectedView == SchedulerViewType.MonthView) ||
639 (nSelectedView == SchedulerViewType.AgendaView)) { //Month or Agenda
640 sDate = dateToSimpleString(subtractMonth(dtSelectedDate, nViewPeriodMultiplier));
641
642 } else if (nSelectedView == SchedulerViewType.WeekView) { //Week
643 sDate = dateToSimpleString(subtractWeek(dtSelectedDate, nViewPeriodMultiplier));
644
645 } else if (nSelectedView == SchedulerViewType.DayView) { //Day
646 sDate = dateToSimpleString(addDaysToDate(new Date(document.forms.frmScheduler.hdnSelectedDate.value), -1 * nViewPeriodMultiplier));
647
648 } else if (nSelectedView == SchedulerViewType.TimelineView) { //Timeline/Schedule Period
649
650 sDate = dateToSimpleString(subtractMonth(dtSelectedDate, 1));
651
652 //Get the start date from the previous period in the drop down, otherwise use the day before the first period started.
653 var ddl = $find('ddlSchedulePeriod');
654 if (ddl && ddl.get_selectedItem().get_index() > 0 && ddl.get_items().getItem(ddl.get_selectedItem().get_index() - 1).get_text().indexOf(" - ") > -1) {
655 //Get the first day of the previous period in the list
656 var dates = ddl.get_items().getItem(ddl.get_selectedItem().get_index() - 1).get_text().split(" - ");
657 if (dates.length == 2) {
658 sDate = dates[0];
659 }
660 } else if (ddl && ddl.get_selectedItem().get_text().indexOf(" - ") > -1) {
661 //Calculate the day before the selected period's start date
662 var dates = ddl.get_selectedItem().get_text().split(" - ");
663 if (dates.length == 2) {
664 sDate = dateToSimpleString(addDaysToDate(new Date(dates[0]), -1));
665 }
666 }
667 }
668
669 } else if (nNavCommand == SchedulerNavigationCommand.SwitchToSelectedDay) { //Today
670 sDate = dateToSimpleString(new Date());
671
672 } else if (nNavCommand == SchedulerNavigationCommand.NavigateToSelectedDate) { //Date Picker
673 sDate = dateToSimpleString(eventArgs.get_selectedDate());
674 isSlotSelected = true;
675
676 } else if (nNavCommand == SchedulerNavigationCommand.DisplayNextAppointmentSegment) { //Next Appt Segment
677 sDate = dateToSimpleString(addDaysToDate(dtSelectedDate, 1));
678 isSlotSelected = true;
679
680 } else if (nNavCommand == SchedulerNavigationCommand.DisplayPreviousAppointmentSegment) { //Prev Appt Segment
681 sDate = dateToSimpleString(addDaysToDate(dtSelectedDate, -1));
682 isSlotSelected = true;
683
684 } else {
685 sDate = dateToSimpleString(dtSelectedDate);
686 }
687
688 //View Type is Changing
689 if (nNavCommand == SchedulerNavigationCommand.SwitchToMonthView) { //Month
690
691 nCalView = SchedulerViewType.MonthView;
692
693 } else if (nNavCommand == SchedulerNavigationCommand.SwitchToWeekView) { //Week
694 nCalView = SchedulerViewType.WeekView;
695 if (isSlotSelected) {
696 //When a slot is selected go to its week
697 sDate = dateToSimpleString(dtSelectedSlotDate);
698 }
699
700 } else if (nNavCommand == SchedulerNavigationCommand.SwitchToDayView) { //Day
701 nCalView = SchedulerViewType.DayView;
702 if (isSlotSelected) {
703 //When a slot is selected go to its day
704 sDate = dateToSimpleString(dtSelectedSlotDate);
705 }
706
707 } else if (nNavCommand == SchedulerNavigationCommand.SwitchToAgendaView) { //Agenda
708 nCalView = SchedulerViewType.AgendaView;
709 if (isSlotSelected) {
710 //When a slot is selected start at it's date
711 sDate = dateToSimpleString(dtSelectedSlotDate);
712 }
713
714 } else if (nNavCommand == SchedulerNavigationCommand.SwitchToTimelineView) { //Timeline/Schedule Period
715 nCalView = SchedulerViewType.SwitchToTimelineView;
716 if (isSlotSelected) {
717 //When a slot is selected start at it's date
718 sDate = dateToSimpleString(dtSelectedSlotDate);
719 }
720
721 } else {
722 nCalView = nSelectedView;
723 }
724
725 document.forms.frmScheduler.hdnSelectedDate.value = sDate;
726 document.forms.frmScheduler.hdnIsSlotSelected.value = isSlotSelected;
727 document.forms.frmScheduler.hdnCalView.value = nCalView;
728 document.forms.frmScheduler.hdnRefreshCalendar.value = "1";
729}
730
731
732//VJF 02/05/2016 - Extract selected dates from ddlSchedulePeriod, returning an array with start and end dates.
733function GetSelectedSchedulePeriodDatesArray() {
734 var ddl = $find('ddlSchedulePeriod');
735 if (ddl && ddl.get_selectedItem().get_index() > 0) {
736 //Get the first day of the selected period in the list
737 var dates = ddl.get_selectedItem().get_text().split(" - ");
738 if (dates.length == 2) {
739 return dates;
740 }
741 }
742 return "";
743}
744
745//VJF 11/30/2015 - Configure Scheduler View buttons by overriding order and handlers
746function configureSchedulerViewButtons() {
747 //console.time('funct');
748
749 //Override tab click default handlers
750 var scheduler = $find('rsScheduler');
751 scheduler._eventMap.addHandlerForClassName("click", SchedulerViewCssClass.DayView, SwitchToDayView); //was scheduler._onDayViewTabClick
752 scheduler._eventMap.addHandlerForClassName("click", SchedulerViewCssClass.WeekView, SwitchToWeekView); //was scheduler._onWeekViewTabClick
753 scheduler._eventMap.addHandlerForClassName("click", SchedulerViewCssClass.PeriodView, SwitchToPeriodView); //was scheduler._onTimelineViewTabClick
754
755 reorderSchedulerViewButtons();
756
757 var hdnCalView = document.getElementById("hdnCalView");
758 if (hdnCalView) {
759 setSelectedSchedulerView(hdnCalView.value);
760 }
761
762 //console.timeEnd('funct');
763}
764
765//VJF 11/30/2015 - Set the passed scheduler view type as visually selected in the header
766function setSelectedSchedulerView(nViewId) {
767
768 switch (parseInt(nViewId)) {
769 case SchedulerViewType.DayView:
770 unSelectSchedulerView();
771 transformSchedulerViewItemToSelected(getScheduleViewLinkItemByClass(SchedulerViewCssClass.DayView));
772 break;
773 case SchedulerViewType.WeekView:
774 unSelectSchedulerView();
775 transformSchedulerViewItemToSelected(getScheduleViewLinkItemByClass(SchedulerViewCssClass.WeekView));
776 break;
777 case SchedulerViewType.MonthView:
778 unSelectSchedulerView();
779 transformSchedulerViewItemToSelected(getScheduleViewLinkItemByClass(SchedulerViewCssClass.MonthView));
780 break;
781 case SchedulerViewType.TimelineView:
782 unSelectSchedulerView();
783 transformSchedulerViewItemToSelected(getScheduleViewLinkItemByClass(SchedulerViewCssClass.PeriodView));
784 break;
785 default:
786
787 }
788}
789
790//VJF 11/30/2015 - Reorder the scheduler view buttons
791// Starts out as: Day, Week, Month, Period
792// Reorders to: Period, Month, Week, Day
793// TODO: DOM manipulation is expensive, can performance be improved by building html and inserting it?
794function reorderSchedulerViewButtons() {
795
796 //Get list of Views
797 var viewCollection = $(".rsHeader ul li");
798
799 var dayView = $(viewCollection[0]);
800 var weekView = $(viewCollection[1]);
801 var monthView = $(viewCollection[2]);
802 var periodView = $(viewCollection[3]);
803
804 if (periodView.length > 0) {
805 //Period View is available
806
807 //Remove first and last css classes
808 dayView.removeClass('rsFirst');
809 periodView.removeClass('rsLast');
810
811 //Move Week to end
812 weekView.remove();
813 periodView.after(weekView);
814
815 //Move Day to end
816 dayView.remove();
817 weekView.after(dayView);
818
819 //Move Period to beginning
820 periodView.remove();
821 monthView.before(periodView);
822
823 //Set first and last css classes
824 periodView.addClass('rsFirst');
825 dayView.addClass('rsLast');
826 } else {
827 //Period View is not available
828
829 //Remove first and last css classes
830 dayView.removeClass('rsFirst');
831 monthView.removeClass('rsLast');
832
833 //Move Week to end
834 weekView.remove();
835 monthView.after(weekView);
836
837 //Move Day to end
838 dayView.remove();
839 weekView.after(dayView);
840
841 //Set first and last css classes
842 monthView.addClass('rsFirst');
843 dayView.addClass('rsLast');
844 }
845}
846
847//VJF 11/30/2015 - Unselect selected Scheduler View by transform the html
848//From Selected: <li class="rsSelected"><em class="rsHeaderMonth">Month</em></li>
849//To Unselected: <li><a href="#" class="rsHeaderMonth"><span>Month</span></a></li>
850function unSelectSchedulerView() {
851
852 var views = $(".rsHeader ul", "#rsScheduler");
853 var selectedView = views.find('li.rsSelected');
854 if (selectedView.length > 0) {
855 var selectedViewClass = selectedView.find('em').attr('class');
856 var selectedViewLabel = selectedView.find('em').text();
857 selectedView.find('em').replaceWith('<a href="#" class="' + selectedViewClass + '"><span>' + selectedViewLabel + '</span></a>');
858 selectedView.removeClass('rsSelected');
859 }
860}
861
862//VJF 11/30/2015 - Get the scheduler view item, <li>, that contains the header view class name.
863//Note: The selected view doesn't have a link
864function getScheduleViewLinkItemByClass(className) {
865 var selector = '.rsHeader ul li a.' + className;
866 if (selector.length > 0) {
867 return $(selector, "#rsScheduler").parent();
868 } else {
869 return null;
870 }
871}
872
873//VJF 11/30/2015 - Set selected Scheduler View by transforming the html
874//From Unselected: <li><a href="#" class="rsHeaderMonth"><span>Month</span></a></li>
875// To Selected: <li class="rsSelected"><em class="rsHeaderMonth">Month</em></li>
876function transformSchedulerViewItemToSelected(viewListItem) {
877
878 var cssClass = viewListItem.find('a').attr('class');
879 var label = viewListItem.find('a').text();
880 viewListItem.find('a').replaceWith('<em class="' + cssClass + '">' + label + '</em>');
881 viewListItem.addClass('rsSelected');
882}
883
884//Overridden Day View Handler
885function SwitchToDayView(e, a) {
886 SchedulerViewOverrideBaseHandler(SchedulerViewType.DayView);
887}
888
889//Overridden Day View Handler
890function SwitchToWeekView(e) {
891 SchedulerViewOverrideBaseHandler(SchedulerViewType.WeekView);
892}
893
894//Overridden Period View Handler
895function SwitchToPeriodView(e) {
896
897 //Set the selected date to the first day in the period ddl
898 var arrSchedulePeriodDates = GetSelectedSchedulePeriodDatesArray();
899 if (arrSchedulePeriodDates.length == 2) {
900 var selectedDate = dateToSimpleString(new Date(arrSchedulePeriodDates[0]));
901 document.forms.frmScheduler.hdnSelectedDate.value = selectedDate;
902 }
903
904 SchedulerViewOverrideBaseHandler(SchedulerViewType.TimelineView);
905}
906
907//VJF 11/30/2015 - Base handler when Scheduler view is changed
908function SchedulerViewOverrideBaseHandler(nViewId) {
909
910 var hdnCalView = document.getElementById("hdnCalView");
911 if (hdnCalView) {
912 hdnCalView.value = nViewId;
913
914 unSelectSchedulerView();
915 setSelectedSchedulerView(nViewId);
916
917 var scheduler = $find('rsScheduler');
918
919 //Posting back using the scheduler instead of __doPostBack will use ajax
920 if (scheduler) {
921 var arg = { Command: "SwitchToMonthView" };
922 scheduler.postback(arg);
923 }
924 }
925}
926
927
928/*Override Date Picker events to use month selection instead of day.*/
929function calDatePickerFastNavOverrides() {
930
931 var scheduler = $find('rsScheduler');
932 var calendar = $find(scheduler.get_id() + "_SelectedDateCalendar");
933
934 //VJF 05/09/2017 - Fixes condition with multi-sched where first schedule is one row and pushes second sched down.
935 //Move from css to prevent calendar from showing briefly on every load.
936 calendar.get_element().style.position = "fixed";
937
938 //Month selection is only applicable for Month Views
939 if (document.forms.frmScheduler.hdnCalView.value != SchedulerViewType.MonthView) {
940 return false;
941 }
942
943 var fastNavigation = calendar._getFastNavigation();
944
945 $telerik.$(".rsDatePickerActivator").get(0).href = "javascript:void(0);";
946 $addHandler($telerik.$(".rsDatePickerActivator").get(0), "click", function () {
947
948 //Attempting to close the popup from the calendar button, so do nothing.
949 if (calendar._getFastNavigation().Popup && calendar._getFastNavigation().Popup.IsVisible()) {
950 return false;
951 }
952
953 $telerik.$(calendar.get_element()).hide();
954 //adjust where to show the popup table
955 var x, y;
956 var adjustElement = $telerik.$(".rsDatePickerActivator");
957 var offset = adjustElement.offset();
958 x = offset.left + 12;
959 y = offset.top + 12;
960
961 var e = {
962 clientX: x,
963 clientY: y - document.documentElement.scrollTop
964 };
965
966 $get(calendar._titleID).onclick(e);
967
968 return false;
969 });
970
971 fastNavigation.OnOK = function () {
972
973 var date = new Date(fastNavigation.Year, fastNavigation.Month, 1);
974
975 //Set date on form since NavigationClicked will not be called
976 document.forms.frmScheduler.hdnSelectedDate.value = dateToSimpleString(date);
977 document.forms.frmScheduler.hdnRefreshCalendar.value = "1";
978
979 //With Server side binding, we need to use RadAjaxManager:
980 $find("ramScheduleAjaxMgr").ajaxRequest('SelectedDate|' + date.format('yyyy/MM/dd'));
981 fastNavigation.Popup.Hide();
982 };
983
984 fastNavigation.OnToday = function () {
985 var date = new Date();
986
987 //Set date on form since NavigationClicked will not be called
988 document.forms.frmScheduler.hdnSelectedDate.value = dateToSimpleString(date);
989 document.forms.frmScheduler.hdnRefreshCalendar.value = "1";
990
991 $find("ramScheduleAjaxMgr").ajaxRequest('SelectedDate|' + date.format('yyyy/MM/dd'));
992 fastNavigation.Popup.Hide();
993 };
994}
995
996/**********************************************************/
997/* MENU / TOOLBAR */
998/**********************************************************/
999//Admin Header Menu Clicking
1000function OnAdminHeaderMenuClicking(sender, eventArgs) {
1001
1002 var selectedMenuItem = eventArgs.get_item();
1003
1004 //Prevent additional processing to navigateUrl when opening window
1005 eventArgs.set_cancel(true);
1006
1007 //Navigate to different page
1008 if (selectedMenuItem.get_value() == "miLogout" ||
1009 selectedMenuItem.get_value() == "miProvView") {
1010
1011 location.href = selectedMenuItem.get_navigateUrl();
1012
1013 } else if (selectedMenuItem.get_value() == "miEmail") {
1014
1015 var oManager = GetRadWindowManager();
1016
1017 //This window won't display properly on iPad unless content is shown during load
1018 if ($telerik.isTouchDevice) {
1019 oManager.set_showContentDuringLoad(true);
1020 }
1021
1022 OpenWindow(selectedMenuItem.get_navigateUrl(), 'SendMessageWindow');
1023 oManager.set_showContentDuringLoad(false);
1024
1025 } else if (selectedMenuItem.get_value() == "miProvider") {
1026 //No action taken - allow opening of dropdown menu
1027 eventArgs.set_cancel(false);
1028
1029 } else if (selectedMenuItem.get_value() == "miAdminReport") {
1030 //JCL 2/23/2017 - No action taken - allow opening of dropdown menu
1031 eventArgs.set_cancel(false);
1032
1033 //Close the dropdown menu
1034 sender.close(true);
1035 } else if (selectedMenuItem.get_value() == "miProfile") {
1036 OpenWindow(selectedMenuItem.get_navigateUrl());
1037 //Close the dropdown menu - for touch devices
1038 sender.close(true);
1039 } else {
1040 //Open new page in a window
1041 OpenWindow(selectedMenuItem.get_navigateUrl());
1042 }
1043}
1044
1045//Scheduler View Toolbar Button Clicked - shared by Schedule, Request and T&A
1046function OnSchedViewToolbarButtonClicked(sender, eventArgs) {
1047
1048 var button = eventArgs.get_item();
1049
1050 if (button.get_value() == "tbbPrint") {
1051
1052 //var PrintReportURL = "./Reports/ReportCriteria.aspx?date=" + document.forms.frmScheduler.hdnSelectedDate.value + "&AdminAutoPreview=true";
1053 var PrintReportURL = button._linkElement.href;
1054 if (CheckAdobeReader(false, false)) {
1055 PrintReportURL = PrintReportURL + "&UsePDFFormat=true";
1056 }
1057
1058 OpenWindow(PrintReportURL);
1059 eventArgs.set_cancel(true);
1060
1061 } else if (button.get_value() == "tbbClear") {
1062 OpenWindow("AdminClearSchedule.aspx?Date=" + document.forms.frmScheduler.hdnSelectedDate.value);
1063 eventArgs.set_cancel(true);
1064
1065 } else if (button.get_value() == "tbbReload") {
1066 ReloadAdminCalendar();
1067 eventArgs.set_cancel(true);
1068
1069 } else if (button.get_value() == "tbbFind" || button.get_value() == "Location") {
1070 GoToLocationSearch();
1071 eventArgs.set_cancel(true);
1072
1073 } else if (button.get_commandName() == "tbbScheduleView") {
1074
1075 if (document.forms.frmScheduler.hdnAdminScheduleView.value == button.get_commandArgument()) {
1076 //Selection didn't change, don't do anything
1077 eventArgs.set_cancel(true);
1078 } else {
1079 document.forms.frmScheduler.hdnAdminScheduleView.value = button.get_commandArgument();
1080 }
1081
1082 } else if (button.get_value() == "tbbMassUpdate") {
1083 GoToMassUpdate();
1084 eventArgs.set_cancel(true);
1085
1086 } else if (button.get_commandName() == "ToggleProviderList") {
1087 toggleProvDockVisibility();
1088 var bIsDockVisible = inViewport(document.getElementById('ProvidersDock'));
1089 if ($('#ProvidersDock').is(":visible") || bIsDockVisible) {
1090 AddShiftGroupColumnToMasterTable(selectPrimaryKeys, false);
1091 }
1092 eventArgs.set_cancel(true);
1093
1094 } else if (button.get_value() == "tbbFindProviders" || button.get_value() == "Provider") {
1095 GoToProviderSearch();
1096 eventArgs.set_cancel(true);
1097
1098 } else if (button.get_value() == "tbbFilter") {
1099 GoToShiftGroups();
1100 eventArgs.set_cancel(true);
1101
1102 } else if (button.get_value() == "tbbGenerate") {
1103 OpenWindow(GetShiftGroupBreakdownURL());
1104 eventArgs.set_cancel(true);
1105
1106 } else if (button.get_value() == "tbbRevisions") {
1107 var nSchedulePeriodId = GetAdminScheduleSelectedPeriodId();
1108 var nLocationId = GetAdminScheduleLocationId();
1109 var nScheduleGroupId = GetAdminScheduleSchedGroupId();
1110
1111 var params = {
1112 "LocationId": nLocationId,
1113 "ScheduleGroupId": nScheduleGroupId,
1114 "SchedulePeriodId": nSchedulePeriodId,
1115 "IsLocationContext": nLocationId > 0
1116 }
1117
1118 var appPath = '../config/schedule/revisions.aspx';
1119 var sUrl = appPath + '?' + $.param(params);
1120
1121 OpenWindow(sUrl);
1122 eventArgs.set_cancel(true);
1123 }
1124
1125
1126}
1127
1128/**********************************************************/
1129/* PROVIDER HIGHLIGHTING / FILTERING */
1130/**********************************************************/
1131//Provider highlight colors
1132var PROV_COLORS = ['#cb5165', '#D1B2EB', '#A4D88D', '#87a4ff', '#e4cf22', '#DEF1D5', '#C1C3C2', '#eb774d', '#5EA7A0', '#B74DAE', '#EAB2CB', '#FDE7CF', '#828685', '#E5E5E5', '#ef3660', '#F4AB98', '#FAC68C', '#36A598', '#d8da59', '#85CCD0', '#91d888', '#A8ADAC', '#d2daed', '#c39ce0', '#86a403', '#c9fdf4', '#88bea1', '#cbb316', '#b6b1a0', '#fb4f3d', '#c57792', '#A86959', '#9470ae', '#f18a74', '#8dba36', '#b7b2d4', '#e5492f', '#dc78e2', '#a990fb', '#d3ace8', '#91CEEA', '#a864e0', '#ccdac7', '#9975dd', '#e1e130', '#81cfde', '#FAD8CE', '#ae7c62', '#CDE9F5', '#ADD8E6'];
1133
1134//TJF 12/2/2015 - Track current color index
1135var provColorsIndex = 1;
1136var assignedColors = ['#cb5165']; //TJF 12/9/2015 - Track assigned colors for use after period change
1137var colorsInUse = [];
1138//Get the highlight color for a selected provider
1139function getHighlightColor(item) {
1140 //TJF 12/2/2015 - Check for already set prov color
1141 if ($(item._element).attr("data-prov-color") != undefined) {
1142 return $(item._element).attr("data-prov-color");
1143 }
1144
1145 //Get cached value
1146 if (assignedColors[item.get_value()]) {
1147 var provColor = assignedColors[item.get_value()];
1148 $(item._element).attr("data-prov-color", provColor);
1149 return provColor;
1150 }
1151
1152 //TJF 12/2/2015 - Use colors index instead of item index
1153 if (provColorsIndex < PROV_COLORS.length) {
1154
1155 var provColor = PROV_COLORS[provColorsIndex];
1156
1157 //This will be useful later when some colors are set by the server
1158 var isInUse = isColorInUse(provColor);
1159
1160 if (isInUse) {
1161 while (provColorsIndex < PROV_COLORS.length && isInUse) {
1162 provColorsIndex++;
1163 provColor = PROV_COLORS[provColorsIndex];
1164 isInUse = isColorInUse(provColor);
1165 }
1166 }
1167
1168 $(item._element).attr("data-prov-color", provColor);
1169 //Store assigned color
1170 assignedColors[item.get_value()] = provColor;
1171 colorsInUse.push(provColor);
1172 provColorsIndex++;
1173 if (!isInUse) {
1174 return provColor;
1175 }
1176 }
1177
1178 var hexcolor;
1179 //When provider's index exceeds the color array, generate a random color
1180 while (true) {
1181 //Generate random hex number
1182 hexcolor = "000000".replace(/0/g, function () { return (~~(Math.random() * 16)).toString(16); });
1183
1184 //Make sure the color contrast is in the lighter half of spectrum so text isn't obscured
1185 if (parseInt(hexcolor, 16) > 0xffffff / 2) {
1186 break;
1187 }
1188 }
1189
1190 var fullHexColor = "#" + hexcolor;
1191 assignedColors[item.get_value()] = fullHexColor;
1192 return fullHexColor;
1193}
1194
1195//Reset all color variables
1196function resetColorVariables() {
1197 assignedColors = ['#cb5165'];
1198 provColorsIndex = 1;
1199 colorsInUse = [];
1200}
1201
1202//Check if a color is already used
1203function isColorInUse(provColor) {
1204 var isInUse = false;
1205 for (var i = 0; i < colorsInUse.length; i++) {
1206 if (colorsInUse[i] == provColor) {
1207 isInUse = true;
1208 break;
1209 }
1210 }
1211 return isInUse;
1212}
1213
1214
1215//Highlight each provider checked in the lbProviderList when viewing Location Schedule or Admin Calendar
1216//Optional bClearAll parameter will uncheck and unselect all checked providers
1217function highlightCheckedProviders(bClearAll) {
1218 //Default bClearAll to false when it isn't supplied
1219 if (typeof bClearAll != 'boolean') bClearAll = false;
1220
1221 if ((getAppArea() == ApplicationArea.ProvCalendar && tsMainMenu.get_selectedTab().get_value() == ProviderScheduleView.Location) ||
1222 (getAppArea() == ApplicationArea.ProvCalendar && tsMainMenu.get_selectedTab().get_value() == ProviderScheduleView.SwapExchange) ||
1223 (getAppArea() == ApplicationArea.AdminCalendar && tsMainMenu.get_selectedTab().get_value() == AdminMenuBarItem.AdminCalendar)) {
1224 var listbox = $find("lbProviderList");
1225 if (listbox) {
1226 var items = listbox.get_checkedItems();
1227 for (var i = 0; i < items.length; i++) {
1228
1229 if (bClearAll) items[i].set_checked(false); //Uncheck provider item to cause selection to be cleared
1230
1231 highlightProvider(items[i]);
1232 }
1233 }
1234 }
1235}
1236
1237//TJF 11/23/2015 - Filter by a practitioner id
1238//function providerDockPractIndexChanged(sender) {
1239// var filterPractId = sender.options[sender.selectedIndex].value;
1240// var lbProviderList = $find("lbProviderList");
1241
1242// (lbProviderList.get_items()).forEach(function (item) {
1243// //get the pract ids data field from element
1244// var sPractTypeIds = $(item._element).data("practTypeIds");
1245// var visible = false;
1246
1247// if (sPractTypeIds != undefined && filterPractId != "-1") {
1248// var arrPractTypeIds;
1249// //Can be string or integer, if string convert to array
1250// if (typeof sPractTypeIds == "string") {
1251// arrPractTypeIds = sPractTypeIds.split(",");
1252// } else {
1253// arrPractTypeIds = [sPractTypeIds.toString()];
1254// }
1255
1256// //Check if provider has the practitioner type
1257// for (var i = 0; i < arrPractTypeIds.length; i++) {
1258
1259// if (arrPractTypeIds[i] == filterPractId) {
1260// visible = true;
1261// break;
1262// }
1263// }
1264// } else {
1265// //none is selected, all should be visible
1266// visible = true;
1267// }
1268
1269// if (visible) {
1270// $(item._element).show();
1271// } else {
1272// $(item._element).hide();
1273// //Since item is no longer visible, remove highlighting
1274// if (item.get_checked()) {
1275// item.uncheck();
1276// highlightProviderHandlerGeneric(item);
1277// }
1278// }
1279
1280// });
1281
1282//}
1283
1284//TJF 11/24/2015 - Called from eligible providers to select a prov dock item by id
1285function providerDockSelectId(id) {
1286 //Clear current highlighted items
1287 clearHighlightedProvidersHandler();
1288
1289 //Check the list element and highlight
1290 var masterTable = GetGrdProvInfoMasterTableView();
1291 var dataItem = FindDataItemById(id);
1292
1293 //TJF 4/5/2017 - In the case of a provider being not is session, dataitem will return null. The schedule will be reloaded when the operation completes.
1294 if (dataItem != null) {
1295 var rowId = dataItem._element.id;
1296 var chkSelected = $("#" + rowId).find("#chkProviderColor")[0];
1297
1298 //In some cases a provider will not be on the list
1299 if (!chkSelected) {
1300 return;
1301 }
1302 chkSelected.checked = true;
1303 highlightProviderHandlerGenericForGrid(chkSelected);
1304 selectedProviderCheckedOnGrid(chkSelected);
1305 }
1306}
1307
1308//TJF 11/25/2015 - Save the selected providers so it can be restored after closing eligible providers
1309var arrSelectedProviderIds = null;
1310function saveSelectedDockProviders() {
1311 if (arrSelectedProviderIds != null) {
1312 return;
1313 }
1314
1315 arrSelectedProviderIds = [];
1316 var masterTable = GetGrdProvInfoMasterTableView();
1317 for (var i = 0; i < masterTable.get_dataItems().length; i++) {
1318 var row = masterTable.get_dataItems()[i];
1319 var rowId = row._element.id;
1320 var chkHighlightOnSelectedRow = $("#" + rowId).find("#chkProviderColor")[0]; //.find() returns a list of elements, we need to get the first one.
1321 if (chkHighlightOnSelectedRow.checked == true) {
1322 var id = masterTable.getCellByColumnUniqueName(row, "PK").innerHTML;
1323 arrSelectedProviderIds.push(id);
1324 }
1325 }
1326}
1327
1328//TJF 11/25/2015 - Restore original selected providers after closing eligible providers
1329function restoreSelectedDockProviders() {
1330
1331 if (arrSelectedProviderIds == null) {
1332 return;
1333 }
1334
1335 //Clear currently selected provider
1336 clearHighlightedProvidersHandler();
1337
1338 var lbProviderList = $find("lbProviderList");
1339
1340 //Select previous ids in list
1341 for (var i = 0; i < arrSelectedProviderIds.length; i++) {
1342 var item = FindCheckboxFromGridById(arrSelectedProviderIds[i]);
1343 if (!item) {
1344 continue;
1345 }
1346 item.checked = true;
1347 highlightProviderHandlerGenericForGrid(item);
1348 }
1349
1350 arrSelectedProviderIds = null;
1351}
1352
1353//Provider selected from list - toggle checkbox and highlight
1354function providerSelectedHandler(sender, eventArgs) {
1355 var item = eventArgs.get_item();
1356 item.set_checked(!item.get_checked());
1357
1358 eventArgs.set_cancel(true);
1359 highlightProviderHandler(sender, eventArgs);
1360}
1361
1362//Provider list checkbox state changed - toggle checkbox and highlight
1363function highlightProviderHandler(sender, eventArgs) {
1364 //eventArgs will be null on init
1365 if (!eventArgs) return;
1366 var item = eventArgs.get_item();
1367
1368 highlightProviderHandlerGeneric(item);
1369}
1370
1371//TJF 11/23/2015 - This version only requires one parameter and is used in multiple places
1372function highlightProviderHandlerGeneric(item) {
1373 //Method only used on the Location Schedules view
1374
1375 var isProvViewLoc = (getAppArea() == ApplicationArea.ProvCalendar && tsMainMenu.get_selectedTab().get_value() == ProviderScheduleView.Location) ||
1376 (getAppArea() == ApplicationArea.ProvCalendar && tsMainMenu.get_selectedTab().get_value() == ProviderScheduleView.SwapExchange);
1377 var isSchdViewCal = (getAppArea() == ApplicationArea.AdminCalendar && tsMainMenu.get_selectedTab().get_value() == AdminMenuBarItem.AdminCalendar);
1378
1379 //Selected providers maybe be restored on load, so make sure the page supports it.
1380 if (isProvViewLoc || isSchdViewCal) {
1381 //highlighting is supported
1382 } else {
1383 //highlighting is not supported
1384 return;
1385 }
1386
1387 //When a Schedule View Calendar provider is checked select it
1388 if (isSchdViewCal && item.get_checked()) {
1389 item.set_selected(true);
1390 }
1391
1392 var bSetProvAsSelected = true;
1393 highlightProvider(item, bSetProvAsSelected); //VJF 12/21/2015 - Since it is a single provider highlight, set them as selected
1394}
1395
1396//Highlight the provider on the calendar using listbox item
1397function highlightProvider(item, bSetProvAsSelected) {
1398 var isSwapCandidate = false;
1399 var sBgColor = "transparent";
1400 var isChecked = item.get_checked();
1401 var provid = item.get_value();
1402
1403 //VJF 12/21/2015 - Added optional parameter flag to determine when the highlighted provider should be selected,
1404 //this typically only occurs when a single provider is being processed, as opposed to a restore which processes multiples.
1405 if (typeof bSetProvAsSelected != 'boolean') bSetProvAsSelected = false;
1406
1407 if (isChecked) {
1408 sBgColor = getHighlightColor(item);
1409 }
1410
1411 if (provid > -1) {
1412
1413 var cssClass = '.evtProv';
1414 var bIgnoreIdMatch = false;
1415 if (provid == 0) {
1416 cssClass = '.evtOpen';
1417 bIgnoreIdMatch = true;
1418 }
1419
1420 //Reuse the list of provider divs for subsequent calls when it has already been loaded
1421 if (!gLocProviderNodeList || true) {
1422 //Get all elements by class name
1423 gLocProviderNodeList = document.querySelectorAll(cssClass); //getElementsByClassName not supported in IE8 so querySelectorAll used instead
1424 }
1425
1426 var classHighlight = ' highlight';
1427
1428 for (i = 0; i < gLocProviderNodeList.length; i++) {
1429 if (bIgnoreIdMatch || gLocProviderNodeList[i].id == provid) {
1430
1431 if (isChecked) {
1432
1433 gLocProviderNodeList[i].parentNode.style.backgroundColor = sBgColor;
1434
1435 if (gLocProviderNodeList[i].parentNode.className.indexOf(classHighlight) == -1) {
1436 gLocProviderNodeList[i].parentNode.className += classHighlight;
1437 }
1438
1439 //Checked providers are always visible
1440 gLocProviderNodeList[i].parentNode.style.display = "table";
1441
1442 } else {
1443
1444 //remove style which contains the added bgcolor and class that contains the highlight
1445 //gLocProviderNodeList[i].parentNode.removeAttribute("style");
1446 gLocProviderNodeList[i].parentNode.style.backgroundColor = '';
1447 gLocProviderNodeList[i].parentNode.className = gLocProviderNodeList[i].parentNode.className.replace(classHighlight, "");
1448
1449 //Hide when only showing selected providers
1450 if (gShowOnlySelectedProviders) {
1451 gLocProviderNodeList[i].parentNode.style.display = "none";
1452 } else {
1453 gLocProviderNodeList[i].parentNode.style.display = "table";
1454 }
1455
1456 }
1457
1458 }
1459 }
1460 }
1461
1462 if (bSetProvAsSelected) {
1463 document.forms.frmScheduler.hdnSelectedProvider.value = provid;
1464 }
1465}
1466
1467function clearHighlightedProvidersHandler(sender, eventArgs) {
1468 //TJF 11/25/2015 - Reset selected practitioner type
1469 var sel = document.getElementById('ddlDockPractType');
1470 //In empty cases list will not appear
1471 if (sel != null) {
1472 sel.selectedIndex = 0;
1473 providerDockPractIndexChangedForGrid(sel);
1474 }
1475
1476 //Toggle all checked providers off, resetting the highlighting
1477 var bClearAll = true;
1478 highlightCheckedProvidersOnGrid(bClearAll);
1479
1480 //VJF 09/25/2017 - When providers are cleared unavailable time display needs to be reevaluated.
1481 var chkbox = document.getElementById('chkShowProvUnavailableTime');
1482 if ((chkbox && chkbox.checked)) {
1483 showProviderUnavailableTime(true);
1484 }
1485
1486 //TJF 10/3/2016 - Restore underlying shift group or pract type colors
1487 setTimeout(function () {
1488 if (gColorContext == COLORCONTEXT.PractType) {
1489 restorePractTypeColors();
1490 } else if (gColorContext == COLORCONTEXT.ShiftGroup) {
1491 restoreShiftGroupColors();
1492 } else if (gColorContext == COLORCONTEXT.ShiftStatus) {
1493 restoreShiftStatusHighlighting();
1494 }
1495 }, 0);
1496}
1497
1498//VJF 08/11/2015 - 'Show only selected' checkbox state changed.
1499// Show or hide the selected providers accordingly.
1500//DEPRECATED
1501function showOnlySelectedProvidersChecked(isChecked) {
1502
1503 gShowOnlySelectedProviders = isChecked;
1504
1505 //Show all unselected providers
1506 var item;
1507 var listbox = $find("lbProviderList");
1508 if (listbox) {
1509 var items = listbox.get_items();
1510 items.forEach(function (item) {
1511
1512 //Unchecked items need to be shown/hidden
1513 if (item.get_checked() == false) {
1514 highlightProvider(item);
1515 }
1516 });
1517 }
1518}
1519
1520//VJF 09/01/2015 - Restore the visibility of providers based on the state
1521//of the ProvDock provider filter checkbox. Typically called on page load.
1522//VJF 09/13/2017 - Deprecated - Functionality moved to grdProv_OnGridCreated. Call should be removed.
1523function restoreOnlySelectedProvidersChecked() {
1524
1525 //var chkbox = document.getElementById('chkFilterProv');
1526
1527 ////Since all providers are visible by default only need to call when hiding them (filter checked).
1528 //if (chkbox && chkbox.checked) {
1529 // showOnlySelectedProvidersCheckedOnGrid(chkbox.checked, false);
1530 //}
1531}
1532
1533
1534function showHiddenProvidersChecked(isChecked) {
1535 document.forms.frmScheduler.hdnShowHidden.value = isChecked;
1536}
1537
1538//VJF 12/18/2015 - Apply practitioner type filter to dock provider list
1539function restoreProvListPractTypeFilter() {
1540
1541 var ddlDockPractType = document.getElementById('ddlDockPractType');
1542
1543 if (ddlDockPractType) {
1544 providerDockPractIndexChangedForGrid(ddlDockPractType);
1545 }
1546}
1547
1548/**********************************************************/
1549/* DOCKS */
1550/**********************************************************/
1551
1552//Prov Dock cookie string constants
1553var ProvDockCookieConstants = {
1554 TopPos: 'ProvDockTop',
1555 LeftPos: 'ProvDockLeft',
1556 RestoreState: 'RestoreProvDock',
1557 IsCollapsed: 'ProvDockCollapsed',
1558 IsPinned: 'ProvDockPinned'
1559};
1560
1561//Get Prov Dock attributes stored in cookies to restore previous state, otherwise use defaults.
1562var gProvDockTop = (getCookie(ProvDockCookieConstants.TopPos) ? getCookie(ProvDockCookieConstants.TopPos) : 96); //Dock pixels from top
1563var gProvDockLeft = (getCookie(ProvDockCookieConstants.LeftPos) ? getCookie(ProvDockCookieConstants.LeftPos) : 628); //Dock pixels from left
1564var gProvDockIsCollapsed = (getCookie(ProvDockCookieConstants.IsCollapsed) == 'true'); //VJF 12/14/2015 - Dock collapsed state
1565var gProvDockIsPinned = (getCookie(ProvDockCookieConstants.IsPinned) == 'true'); //VJF 12/14/2015 - Dock pinned state
1566var gRestoreProvDock = (getCookie(ProvDockCookieConstants.RestoreState) == 'true'); //Indicates whether dock visibility should be restored
1567
1568//Dock the Dock window
1569function Dock(sender, eventArgs) {
1570
1571 //FYI: A closed dock can't be opened after a tab change ajax call
1572 if (eventArgs.Command.get_name() == "Dock") {
1573 //TJF 11/24/2015 - Reset all selections on close
1574 clearHighlightedProvidersHandler(sender, eventArgs);
1575
1576 //VJF 11/01/2016 - Logged in provider in Provider View Location should be highlighted when dock closes
1577 if ((getAppArea() == ApplicationArea.ProvCalendar && tsMainMenu.get_selectedTab().get_value() == ProviderScheduleView.Location) ||
1578 (getAppArea() == ApplicationArea.ProvCalendar && tsMainMenu.get_selectedTab().get_value() == ProviderScheduleView.SwapExchange)) {
1579 var provId = document.getElementById('hdnLoggedInProvider').value;
1580 var chkSelected = FindCheckboxFromGridById(provId);
1581
1582 //TJF 11/10/2016 - Sometimes the provider is not actually in the provider list for certain months/locations, which can cause an error
1583 if (chkSelected) {
1584 chkSelected.checked = true;
1585 ChkProvColorClicked(chkSelected, false);
1586 }
1587 }
1588
1589 //VJF 12/22/2015- Reset 'Show checked only' state to unchecked
1590 var chkbox = document.getElementById('chkFilterProv');
1591 if ((chkbox && chkbox.checked)) {
1592 chkbox.checked = false;
1593 showOnlySelectedProvidersCheckedOnGrid(false, false);
1594 }
1595
1596 //VJF 09/13/2017 - Reset 'Unavailable Time' state to unchecked
1597 chkbox = document.getElementById('chkShowProvUnavailableTime');
1598 if ((chkbox && chkbox.checked)) {
1599 chkbox.checked = false;
1600 showProviderUnavailableTime(false);
1601 }
1602
1603 //VJF 09/13/2017 - Clear selected providers
1604 circleSelectedProvider(-2, true);
1605
1606 //Dock in zone
1607 var zone = $find("RadDockZone1");
1608 gProvDockTop = sender.get_top();
1609 gProvDockLeft = sender.get_left();
1610
1611 gRestoreProvDock = false;
1612 setCookie(ProvDockCookieConstants.RestoreState, gRestoreProvDock, 30);
1613
1614 zone.dock(sender);
1615
1616 }
1617}
1618
1619//NMM 12/16/2015 - This sets the max-height of the ProvidersDock according to the viewport size of the screen.
1620//This will prevent the provider list from expanding it's height past the screen. It will show a scrollbar instead.
1621function AdjustProvDockHeight() {
1622 var provDockMaxHeight = $(window).height() - 200;
1623 $('#ProvidersDock .rdContent').css({ 'max-height': provDockMaxHeight });
1624}
1625
1626//Prov Dock has been repositioned
1627function OnProvDockPositionChanged(provDock) {
1628
1629 //Make sure dock has not been moved from visible screen
1630 KeepDockOnScreen(provDock);
1631
1632 //Save the position of the dock, so it can be restored on navigation
1633 gProvDockTop = provDock.get_top();
1634 gProvDockLeft = provDock.get_left();
1635
1636 //Store the dock position for use when restoring the Prov Dock state
1637 setCookie(ProvDockCookieConstants.TopPos, gProvDockTop, 30);
1638 setCookie(ProvDockCookieConstants.LeftPos, gProvDockLeft, 30);
1639}
1640
1641//VJF 12/14/2015 - Track Prov dock expand/collapse state
1642function OnProvDockExpandCollapse(provDock) {
1643
1644 //Save the collapsed state of the dock, so it can be restored on navigation
1645 gProvDockIsCollapsed = provDock.get_collapsed();
1646
1647 //Store the dock collapsed state for use when restoring. Expires with session.
1648 setCookie(ProvDockCookieConstants.IsCollapsed, gProvDockIsCollapsed);
1649}
1650
1651//VJF 12/14/2015 - Track Prov dock pin/unpin state
1652function OnProvDockPinUnpin(provDock) {
1653
1654 //Save the pin state of the dock, so it can be restored on navigation
1655 gProvDockIsPinned = provDock.get_pinned();
1656
1657 //Store the dock pinned state for use when restoring. Expires with session.
1658 setCookie(ProvDockCookieConstants.IsPinned, gProvDockIsPinned);
1659}
1660
1661function KeepDockOnScreen(dock) {
1662
1663 //Get browser window dimensions
1664 var width = window.innerWidth
1665 || document.documentElement.clientWidth
1666 || document.body.clientWidth;
1667
1668 var height = window.innerHeight
1669 || document.documentElement.clientHeight
1670 || document.body.clientHeight;
1671
1672 //Keep dock from going above page
1673 if (dock.get_top() < 1) {
1674 dock.set_top(1);
1675 }
1676
1677 //Keep dock from going past left page edge
1678 if (dock.get_left() < 1) {
1679 dock.set_left(1);
1680 }
1681
1682 //Keep dock from completely disappearing past right side of page
1683 if (dock.get_left() > width) {
1684 dock.set_left(width - 40);
1685 }
1686}
1687
1688//NMM 12/17/2015 - Positions element on the middle of the screen. Used for positioning the RadDock in the center of
1689//the viewport.
1690function CenterAlignProvDock(dock) {
1691 var wide = $(window).width();
1692 var high = $(window).height();
1693
1694 gProvDockTop = high / 6;
1695 gProvDockLeft = wide / 2;
1696
1697 dock.set_top(gProvDockTop);
1698 dock.set_left(gProvDockLeft);
1699
1700}
1701//Undock Prov Dock on Location Schedule otherwise hide
1702function restoreProvDock() {
1703
1704 var provDock = $find("ProvidersDock");
1705 if ((getAppArea() == ApplicationArea.ProvCalendar && tsMainMenu.get_selectedTab().get_value() == ProviderScheduleView.Location) ||
1706 (getAppArea() == ApplicationArea.ProvCalendar && tsMainMenu.get_selectedTab().get_value() == ProviderScheduleView.SwapExchange) ||
1707 (getAppArea() == ApplicationArea.AdminCalendar && tsMainMenu.get_selectedTab().get_value() == AdminMenuBarItem.AdminCalendar)) {
1708
1709 if (!provDock.get_dockZoneID() || gRestoreProvDock) { //undocked when there is no zone id
1710
1711 provDock.undock();
1712 provDock.set_top(gProvDockTop);
1713 provDock.set_left(gProvDockLeft);
1714 provDock.set_collapsed(gProvDockIsCollapsed);
1715 provDock.set_pinned(gProvDockIsPinned);
1716 provDock.set_closed(false);
1717 //gRestoreProvDock = false;
1718 gRestoreProvDock = true;
1719 setCookie(ProvDockCookieConstants.RestoreState, gRestoreProvDock, 30);
1720
1721 //Adjust the Providers dock height based on the viewport size
1722 AdjustProvDockHeight();
1723 }
1724
1725 } else {
1726 //Dock the Prov Dock when it is undocked and not relevant to current context
1727 if (!provDock.get_dockZoneID()) { //no zone id indicates it is undocked
1728 //Save position
1729 gProvDockTop = provDock.get_top();
1730 gProvDockLeft = provDock.get_left();
1731 var zone = $find("RadDockZone1");
1732 zone.dock(provDock);
1733
1734 gRestoreProvDock = true; //Indicate that dock should be restored when back on location cal
1735 setCookie(ProvDockCookieConstants.RestoreState, gRestoreProvDock, 30);
1736 }
1737 }
1738
1739 //NMM 10/28/2015 - Checks if browser is on IE8 or IE9 if it is, set the providersDock control resizable property to false
1740 //as it is not displaying correctly on those browsers with the setting set to true
1741 if ($telerik.isIE8 || $telerik.isIE9) {
1742 if (provDock != undefined || provDock != null) {
1743 provDock.set_resizable(false);
1744 }
1745 }
1746}
1747
1748//Toggle the visibility of the Provider dock
1749function toggleProvDockVisibility() {
1750 var provDock = $find("ProvidersDock");
1751
1752 if (provDock.get_dockZoneID()) { //docked
1753 provDock.undock();
1754 provDock.set_collapsed(false);
1755 provDock.set_pinned(provDock.get_pinned());
1756 provDock.set_closed(false);
1757 provDock.set_top(gProvDockTop);
1758 provDock.set_left(gProvDockLeft);
1759 gRestoreProvDock = true; //Indicate that dock was manually shown
1760
1761 //Make sure dock restored position is still on visible screen
1762 KeepDockOnScreen(provDock);
1763 } else {
1764 //Check if providersDock is inside viewport
1765 var bIsDockVisible = inViewport(document.getElementById('ProvidersDock'));
1766
1767 //If the providersDock is not in the viewport, instead of hiding we will show the providers list right away. Otherwise
1768 //just hide the providers list.
1769 if (bIsDockVisible) {
1770
1771 //toggle visibility
1772 //provDock.set_closed(!provDock.get_closed());
1773 gProvDockTop = provDock.get_top();
1774 gProvDockLeft = provDock.get_left();
1775 gRestoreProvDock = false; //Indicate that dock was hidden manually
1776
1777 var zone = $find("RadDockZone1");
1778 zone.dock(provDock);
1779 } else {
1780 CenterAlignProvDock(provDock);
1781 }
1782 }
1783
1784 //Store the dock state for restoring state
1785 setCookie(ProvDockCookieConstants.RestoreState, gRestoreProvDock, 30);
1786
1787 //Adjust the Providers dock height based on the viewport size
1788 AdjustProvDockHeight();
1789}
1790
1791//NMM 12/17/2015 - Checks if element is within the current viewport
1792function inViewport(el) {
1793
1794 var rect = el.getBoundingClientRect(),
1795 vWidth = window.innerWidth || doc.documentElement.clientWidth,
1796 vHeight = window.innerHeight || doc.documentElement.clientHeight,
1797 efp = function (x, y) { return document.elementFromPoint(x, y); };
1798
1799 // Return false if it's not in the viewport
1800 if (rect.right < 0 || rect.bottom < 0
1801 || rect.left > vWidth || rect.top > vHeight)
1802 return false;
1803
1804 // Return true if any of its four corners are visible
1805 return (
1806 el.contains(efp(rect.left, rect.top))
1807 || el.contains(efp(rect.right, rect.top))
1808 || el.contains(efp(rect.right, rect.bottom))
1809 || el.contains(efp(rect.left, rect.bottom))
1810 );
1811
1812}
1813/**********************************************************/
1814/* ZOOM */
1815/**********************************************************/
1816
1817//Zoom View Button Clicking - Increase or Decrease scale of the body content by 10%
1818function OnZoomClicking(sender, eventArgs) {
1819
1820 var nZoomScale = gZoomScale;
1821 var incr = 0.05; //zoom increment %
1822
1823 if (sender.get_value() == 0) { //Decrease size
1824 nZoomScale = parseFloat(nZoomScale) - parseFloat(incr);
1825 } else {
1826 nZoomScale = parseFloat(nZoomScale) + parseFloat(incr);
1827 }
1828
1829 nZoomScale = +(Math.round(nZoomScale + "e+2") + "e-2"); //Round to 2 decimal places
1830
1831 gZoomScale = setZoomView(nZoomScale);
1832
1833 //Save the zoom scale as a cookie for Calendar or Location
1834 if ((getAppArea() == ApplicationArea.ProvCalendar && tsMainMenu.get_selectedTab().get_value() == ProviderScheduleView.Location) ||
1835 (getAppArea() == ApplicationArea.ProvCalendar && tsMainMenu.get_selectedTab().get_value() == ProviderScheduleView.SwapExchange) ||
1836 (getAppArea() == ApplicationArea.AdminCalendar && tsMainMenu.get_selectedTab().get_value() == AdminMenuBarItem.AdminCalendar)) {
1837 setCookie("ZoomScaleLoc", gZoomScale, 30);
1838 } else {
1839 setCookie("ZoomScaleCal", gZoomScale, 30);
1840 }
1841
1842 eventArgs.set_cancel(true);
1843 return false;
1844}
1845
1846//Set body Zoom Scale to passed value. It will return the value used which may differ if param value is invalid.
1847function setZoomView(nZoomScale) {
1848
1849 var nZoomScaleLoLimit = 0.5;
1850 var nZoomScaleHiLimit = 2.0;
1851
1852 //Context menus position is always seen as (0,0) when zooming in on iPad
1853 if ($telerik.isMobileSafari) {
1854 nZoomScaleHiLimit = 1.0;
1855 }
1856
1857 if (!nZoomScale) {
1858 nZoomScale = 1; //Set default value when invalid
1859 }
1860
1861 //Limit Zoom Scale range
1862 if (nZoomScale < nZoomScaleLoLimit) {
1863 nZoomScale = nZoomScaleLoLimit;
1864 } else if (nZoomScale > nZoomScaleHiLimit) {
1865 nZoomScale = nZoomScaleHiLimit;
1866 }
1867
1868 //Only change zoom scale when it has changed
1869 if (nZoomScale != gZoomScale) {
1870
1871 //Change zoom style on body or equivalent for Mozilla
1872 var style = document.getElementsByTagName("body")[0].style;
1873
1874 //Determine if the browser supports the zoom style attribute.
1875 //VJF 11/09/2015 - Edge browser doesn't support the zoom style on sprites, so use transform instead.
1876 if (typeof (style.zoom) != "undefined" && !Telerik.Web.Browser.edge) {
1877
1878 style.zoom = nZoomScale;
1879
1880 //IE needs width adjusted
1881 if (!($telerik.isChrome || $telerik.isSafari)) {
1882 style.width = (100 / nZoomScale) + "%";
1883 style.height = (100 / nZoomScale) + "%";
1884 style.overflowX = "hidden";
1885
1886 if ($telerik.isIE8) {
1887 var doc = document.getElementsByTagName("body")[0];
1888 doc.offsetWidth;
1889 //IE8 requires scaling the width/height non-linearly to fill the screen
1890 //The correct widths were determined manually and entered into excel to create an equation using a Power trendline
1891 var size = 99.888 * Math.pow(nZoomScale, -2.0001);
1892 style.width = size + "%";
1893 style.height = size + "%";
1894 }
1895 }
1896
1897 } else {
1898 //Mozilla - Transform scale at same time adjusting width
1899
1900 style.width = (100 / nZoomScale) + "%";
1901 style.height = (100 / nZoomScale) + "%";
1902
1903 style.transform = "scale(" + nZoomScale + ")";
1904 style.transformOrigin = "0 0"; //position in right top corner
1905 }
1906
1907 //Reposition status notification
1908 var statusBar = $find("StatusNotification");
1909 if (statusBar) {
1910 statusBar.set_position(Telerik.Web.UI.NotificationPosition.MiddleRight);
1911 statusBar.set_offsetX(0);
1912 statusBar.set_offsetY(0);
1913 //statusBar.calculateOffset = true;
1914 }
1915
1916 }
1917
1918 //The following gets executed regardless of zoom change
1919 //Show magnifying glass as active when a zoom is applied
1920 var btnIncView = $find("btnViewIncrease");
1921 var btnDecView = $find("btnViewDecrease");
1922
1923 //Limit Zoom Scale range
1924 btnIncView.set_toolTip("Increase zoom from " + Math.round(nZoomScale * 100) + "%");
1925 btnDecView.set_toolTip("Decrease zoom from " + Math.round(nZoomScale * 100) + "%");
1926
1927 if (nZoomScale == nZoomScaleLoLimit) {
1928 btnDecView.set_toolTip("Minimum zoom of " + Math.round(nZoomScale * 100) + "%");
1929 } else if (nZoomScale == nZoomScaleHiLimit) {
1930 btnIncView.set_toolTip("Maximum zoom of " + Math.round(nZoomScale * 100) + "%");
1931 } else {
1932
1933 }
1934
1935
1936 if (!$telerik.isTouchDevice) { //Selected index gets overridden by default action on iPad
1937 //Select zoom button based on the effect being applied value
1938 if (nZoomScale == 1) {
1939 //No Zoom
1940 btnIncView.set_selectedToggleStateIndex(1);
1941 btnDecView.set_selectedToggleStateIndex(1);
1942 } else if (nZoomScale > 1) {
1943 //Increase Zoom
1944 btnIncView.set_selectedToggleStateIndex(0);
1945 btnDecView.set_selectedToggleStateIndex(1);
1946 } else {
1947 //Decrease Zoom
1948 btnIncView.set_selectedToggleStateIndex(1);
1949 btnDecView.set_selectedToggleStateIndex(0);
1950 }
1951 }
1952
1953 return nZoomScale;
1954
1955}
1956
1957/**********************************************************/
1958/* MISC */
1959/**********************************************************/
1960//Actions that can be run after login
1961//Call added to page on server side
1962function postLogin(appPath) {
1963 //setTimeout(function () { OpenWindow(appPath + "/HelpCenterAdmin.aspx"); }, 0);
1964
1965 if (getAppArea() == ApplicationArea.AdminCalendar && tsMainMenu.get_selectedTab().get_value() == AdminMenuBarItem.AdminCalendar) {
1966 startTour(appPath);
1967 }
1968}
1969
1970//Key Pressed Handlers
1971//VJF 10/15/2015 - Store the key that is being held down so it can be used by hotkey actions.
1972//To use on a page, have it called by Sys.Application.add_load
1973function addKeyboardEventHandlers() {
1974 $(document).ready(function () {
1975
1976 //TJF 11/23/2015 - This is required because after an ajax request these get double registered
1977 $(document).off("keyup");
1978 $(document).off("keydown");
1979
1980 //Key has been pressed down
1981 $(document).on('keydown', function (e) {
1982 //Capture and store depressed key
1983 var key = e.which || e.keyCode || 0;
1984 gPressedKey = String.fromCharCode(key);
1985
1986 //TJF 11/23/2015 - When down arrow is pressed check for hotkey use
1987 if (key == 40 && !$telerik.isIE8) {
1988 handleKeydownOnProviderListOnGrid(e, true);
1989 }
1990 //Up arrow
1991 else if (key == 38 && !$telerik.isIE8) {
1992 handleKeydownOnProviderListOnGrid(e, false);
1993 }
1994 });
1995
1996 //Key has been let up
1997 $(document).on('keyup', function (e) {
1998 var key = e.which || e.keyCode || 0;
1999 gPressedKey = '';
2000 });
2001 });
2002}
2003
2004/**********************************************************/
2005/* Selected Appointment CSS styling */
2006/**********************************************************/
2007
2008//NMM 11/2/2015 - Applies css styling to selected appointment/s
2009function ApplySelectedAppointmentStyling(sender, eventArgs, apt, IsRightClickDetected) {
2010
2011 //Get the appointment DOM Element ID
2012 var selectedAptDOMElementId = apt.get_element().id;
2013 var hdnSelectedAptDomIdElem = $("#hdnSelectedAptDOMElementIds");
2014 var hdnSelectedShiftKeyElem = $("#hdnSelectedShiftPrimaryKeys");
2015 var attribs = apt.get_attributes();
2016 var selectedShiftPrimaryKey = attribs.getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_APPT_ID);
2017 var selectedProviderId = attribs.getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_PROV_ID);
2018
2019 //Check if selecting multiple appointments
2020 if (eventArgs.get_domEvent().ctrlKey || eventArgs.get_domEvent().altKey || gLongClick == true) {
2021 ToggleAppointmentSelection(selectedAptDOMElementId, selectedShiftPrimaryKey, selectedProviderId, IsRightClickDetected);
2022
2023 //TJF 2/8/2018 - Set provider as selected in status bar
2024 GoToShiftSky(null, hdnSelectedAptDomIdElem, true, selectedProviderId, false, AdminTask.SelectProvider, "");
2025
2026
2027 } else {
2028 var nAptItems = hdnSelectedAptDomIdElem.val().split(";").length - 1;
2029
2030 //If selected appointments are more than 1, when you click on a selected appointment again it should not remove the appointment styling
2031 if (nAptItems < 1) {
2032
2033 //Add the selected appointments - their DOM element and shift primary key id to each of their hidden fields
2034 hdnSelectedAptDomIdElem.val("");
2035 hdnSelectedAptDomIdElem.val(selectedAptDOMElementId + ";");
2036 hdnSelectedShiftKeyElem.val("");
2037 hdnSelectedShiftKeyElem.val(selectedShiftPrimaryKey + "|" + selectedProviderId + ";");
2038
2039 //Apply appointment class styling
2040 $("#" + selectedAptDOMElementId).find(".rsAptContent").addClass("selectedApt");
2041 }
2042 }
2043
2044 gLongClick = false;
2045}
2046
2047
2048//On selecting appointments while holding the ctrl key, determine if the appointment selected
2049//already has the css styling. Parameters:
2050//1. selectedAptDOMElementId - the DOM Element of the appointment clicked that we will use to apply styling
2051//2. IsRightClickDetected - determine if a right click is detected
2052//3. selectedShiftPrimaryKey - shift primary key
2053function ToggleAppointmentSelection(selectedAptDOMElementId, selectedShiftPrimaryKey, selectedProviderId, IsRightClickDetected) {
2054 var selectedAptDomElem = $("#" + selectedAptDOMElementId);
2055 var hdnSelectedShiftKeysElem = $("#hdnSelectedShiftPrimaryKeys");
2056 var hdnSelectedAptDomIdsElem = $("#hdnSelectedAptDOMElementIds");
2057
2058 //check if selected appointment is already styled, if it is we remove
2059 //it's highlight. If not we highlight it using a css class.
2060 if (selectedAptDomElem.find(".rsAptContent").hasClass("selectedApt")) {
2061
2062 //On a ctrl + click or long click(mobile) on an already selected appointment will remove css styling for that particular appointment.
2063 //Otherwise if it is on a non selected appointment, we will add css styling to that appointment
2064 if (IsRightClickDetected == false || gLongClick == true) {
2065
2066 //Remove styling
2067 selectedAptDomElem.find(".rsAptContent").removeClass("selectedApt");
2068
2069 //"rsAptSelected" - Telerik's class that gets added to the div when you select an appointment
2070 selectedAptDomElem.removeClass("rsAptSelected");
2071
2072 //remove the shift primary key from the hidden field which is used as
2073 //a flag for styling appointments.
2074 hdnSelectedShiftKeysElem.val(hdnSelectedShiftKeysElem.val().replace(selectedShiftPrimaryKey + "|" + selectedProviderId + ";", ""));
2075 hdnSelectedAptDomIdsElem.val(hdnSelectedAptDomIdsElem.val().replace(selectedAptDOMElementId + ";", ""));
2076
2077 if ($telerik.isTouchDevice) {
2078 gIsAptStyled = true;
2079 }
2080 }
2081 } else {
2082
2083 //Add a class that will style the appointment as selected
2084 selectedAptDomElem.find(".rsAptContent").addClass("selectedApt");
2085
2086 //Add the parameter id's to the hidden fields.
2087 hdnSelectedShiftKeysElem.val(hdnSelectedShiftKeysElem.val() + selectedShiftPrimaryKey + "|" + selectedProviderId + ";");
2088 hdnSelectedAptDomIdsElem.val(hdnSelectedAptDomIdsElem.val() + selectedAptDOMElementId + ";");
2089 }
2090}
2091
2092//NMM 11/2/2015 - Removes all CSS styling of selected appointments
2093function RemoveAppointmentSelections() {
2094
2095 //remove CSS styling class
2096 $('.selectedApt').removeClass("selectedApt");
2097
2098 //Remove appointment styling when the click is made not in the appointment
2099 $('.rsAptSelected').removeClass("rsAptSelected");
2100
2101 $("#hdnSelectedShiftPrimaryKeys").val("");
2102 $("#hdnSelectedAptDOMElementIds").val("");
2103
2104}
2105
2106/**********************************************************/
2107/* Admin Schedule Context Menu Format */
2108/**********************************************************/
2109
2110//NMM 12/08/2015 - Get the shift primary keys of the selected appointments
2111function GetSelectedShiftPrimaryKeys() {
2112
2113 //Get the selected appointments
2114 var strShiftPrimaryKeys = document.getElementById("hdnSelectedShiftPrimaryKeys").value;
2115 var arShiftPrimaryKeys = strShiftPrimaryKeys.split(";");
2116 arShiftPrimaryKeys.pop();
2117
2118 return arShiftPrimaryKeys;
2119}
2120
2121//NMM 1/18/2016 - Get the Apt Dom Ids of the selected appointments
2122function GetSelectedAptDomIds() {
2123
2124 //Get the selected appointments
2125 var strAptDomIds = document.getElementById("hdnSelectedAptDOMElementIds").value;
2126 var arAptDomIds = strAptDomIds.split(";");
2127 arAptDomIds.pop();
2128
2129 return arAptDomIds;
2130}
2131
2132//NMM 12/08/2015 - Formats each context menu items to be disabled, enabled, hide, text edited shown based on selected shifts.
2133function StructureContextMenuItems(apt, eventArgs) {
2134
2135 //Get Selected Shift Primary Keys
2136 var arShiftPrimaryKeys = GetSelectedShiftPrimaryKeys();
2137 var items = apt.get_contextMenu().get_items();
2138 var contextMenu = apt.get_contextMenu();
2139
2140 //Structures context menu items based on if there are multiple or single appointment selection.
2141 //We enable/disable/hide/show/edit text of the context menu items where it's relevant
2142 if (arShiftPrimaryKeys.length <= 1) {
2143 EnableContextMenuItems(contextMenu);
2144 for (var i = 0; i < items.get_count() ; i++) {
2145 var contextMenuItem = items.getItem(i);
2146 var contextMenuItemValue = items.getItem(i).get_value();
2147
2148 //Enable/Disable Assign Provider
2149 if (contextMenuItemValue == AdminTask.AssignProvider) {
2150 ToggleAssignProviderMenuItem(apt, contextMenuItem);
2151 continue;
2152 }
2153
2154 //Enable/Disable Remove Provider
2155 if (contextMenuItemValue == AdminTask.RemoveProviders) {
2156 ToggleRemoveProviderMenuItem(apt, contextMenuItem, eventArgs);
2157 continue;
2158 }
2159
2160 //TJF 4/10/2017 - Remove provider from all shifts in period/displayed date range
2161 if (contextMenuItemValue == AdminTask.RemoveProviderInPeriod) {
2162 ToggleRemoveProviderInRange(apt, contextMenuItem);
2163 continue;
2164 }
2165
2166 //JCL 8/23/2017 - Show\Hide Set Manually Assigned based on whether the user has Identify Manually Assigned selected.
2167 //VJF 12/14/2017 - Hide Set Manually Assigned option if it is a template assignment. This prevents the manually assigned flag on the shift from being removed.
2168 if (contextMenuItemValue == AdminTask.SetManuallyAssigned) {
2169 var chkIdentifyManuallyAssigned = document.getElementById("chkIdentifyManuallyAssigned");
2170 var isTemplateAssignment = apt.get_attributes().getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_TEMPLATE_ASSIGNMENT) === "1";
2171
2172 if (chkIdentifyManuallyAssigned) {
2173 contextMenuItem.set_visible(chkIdentifyManuallyAssigned.control._checked && !isTemplateAssignment);
2174 }
2175
2176 continue;
2177 }
2178
2179 //VJF 04/26/2018 - Customize the copy shift menu item to include the shift name
2180 if (contextMenuItemValue == AdminTask.CopyShift) {
2181
2182 var shiftName = $(".evtLocShift", apt._domElement)[0].textContent.trim();
2183 var itemText = "Copy '" + shiftName + "' Shift...";
2184
2185 //When the context menu item has more than 1 child, it is a submenu, update the text of the first child/copy item
2186 if (contextMenuItem.get_items()._array && contextMenuItem.get_items()._array.length > 1)
2187 contextMenuItem.get_items()._array[0].set_text(itemText);
2188 else
2189 contextMenuItem.set_text(itemText);
2190
2191 continue;
2192 }
2193
2194
2195 }
2196
2197 } else {
2198 DisableContextMenuItems(contextMenu);
2199 }
2200
2201
2202 ToggleSwapMenuItem(contextMenu);
2203}
2204
2205//NMM 12/08/2015 - Enable context menu items by default if it is a single appointment selection.
2206function EnableContextMenuItems(contextMenu) {
2207 var items = contextMenu.get_items();
2208 for (var i = 0; i < items.get_count() ; i++) {
2209 var contextMenuItem = items.getItem(i);
2210 contextMenuItem.set_enabled(true);
2211
2212 }
2213}
2214//NMM 12/08/2015 - Disable context menu items that aren't necessary when selecting multiple appointments.
2215//MTE 2/14/2017 - Allow multiple selected shifts for Change Shift Status
2216//MTE 7/12/2017 - Allow multiple selected shifts for Add Shift Comment
2217//JCL 8/21/2017 - Allow multiple selected shifts for Set Manually Assigned
2218//TJF 1/24/2018 - Allow multiple select on adjust planned/shift exception
2219function DisableContextMenuItems(contextMenu) {
2220 var items = contextMenu.get_items();
2221 for (var i = 0; i < items.get_count() ; i++) {
2222 var contextMenuItem = items.getItem(i);
2223 var contextMenuItemValue = items.getItem(i).get_value();
2224
2225 //MTE 2/22/2017 - Look for the Shift Status id i.e. "12|1" within the context menu Value
2226 var nChangeShiftStatusIndex = contextMenuItemValue.indexOf(AdminTask.ChangeShiftStatus);
2227
2228 if (contextMenuItemValue == WebConstants.CMI_HELP_LABEL
2229 || contextMenuItemValue == AdminTask.RemoveProviders
2230 || contextMenuItemValue == AdminTask.RemoveShiftStatus
2231 || contextMenuItemValue == AdminTask.ShiftComment
2232 || nChangeShiftStatusIndex != -1
2233 || contextMenuItemValue == AdminTask.SetManuallyAssigned
2234 || contextMenuItemValue == AdminTask.AddCompensationAdjustment
2235 || contextMenuItemValue == AdminTask.AdjustPlanned
2236 ) {
2237 contextMenuItem.set_enabled(true);
2238 } else {
2239 contextMenuItem.set_enabled(false);
2240 }
2241 }
2242
2243 ToggleRemoveProviderOnMultipleSelection(contextMenu);
2244}
2245
2246//Add provider name to "Assign Provider" context menu item. Enable or Disable it based on the shift selected.
2247function ToggleAssignProviderMenuItem(apt, contextMenuItem) {
2248 var selectedProviderId = document.forms.frmScheduler.hdnSelectedProvider.value;
2249 var selectedProvName = FindProviderNameFromGrdById(selectedProviderId);
2250
2251 //NMM 2/24/2016 - Only enable the Assign Provider context menu item when
2252 //you click on an open shift and there is a matching provider found on the provlistbox
2253 //TJF 3/21/2017 - Allow assignment on shifts with providers
2254 if (selectedProviderId > 0 && selectedProvName != null) {
2255 contextMenuItem.set_enabled(true);
2256 contextMenuItem.set_text("Assign " + selectedProvName);
2257 } else {
2258 contextMenuItem.set_enabled(false);
2259 contextMenuItem.set_text(WebConstants.CMI_ASSIGN_PROVIDER_LABEL);
2260 }
2261}
2262
2263//TJF 4/10/2017 - Add provider name and date range to remove in range context menu item
2264function ToggleRemoveProviderInRange(apt, contextMenuItem) {
2265 var aptAttribs = apt.get_attributes();
2266 var selectedProviderId = aptAttribs.getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_PROV_ID);
2267 var selectedProvName = FindProviderNameFromGrdById(selectedProviderId);
2268
2269 //TJF 4/10/2017 - Only enable in date range with a selected provider
2270 if (selectedProviderId > 0 && selectedProvName != null && isDateInActiveOrNotDefinedPeriod(apt.get_start())) {
2271 contextMenuItem.set_visible(true);
2272 contextMenuItem.set_text("Remove " + selectedProvName + " from " + document.forms.frmScheduler.hdnCountingDateLabel.value);
2273 } else {
2274 //VJF 04/11/2017 - Hide when not relevant
2275 contextMenuItem.set_visible(false);
2276 contextMenuItem.set_text(WebConstants.CMI_REMOVE_PROVIDER_FROM_ALL_LABEL);
2277 }
2278}
2279
2280//Hide or show the "Remove Provider" context menu on single appointment selection
2281function ToggleRemoveProviderMenuItem(apt, contextMenuItem, eventArgs) {
2282
2283 //If it is an open shift, we hide the "Remove Provider" menu item
2284 //If selected shift has a provider assigned, we show the "Remove Provider" with the provider name
2285 if (apt.get_contextMenuID() == WebConstants.CM_ADMIN_SHIFT_OPEN_ID) {
2286 contextMenuItem.set_visible(false);
2287 } else {
2288 apt = eventArgs.get_appointment();
2289 var attribs = apt.get_attributes();
2290 var selectedProviderId = attribs.getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_PROV_ID);
2291 var selectedProvName = FindProviderNameFromGrdById(selectedProviderId);
2292
2293 //Customize the context menu item with the provider name, when they exist in the provider list, hidden providers won't be there.
2294 if (selectedProvName) {
2295 contextMenuItem.set_text("Remove " + selectedProvName);
2296 }
2297
2298 contextMenuItem.set_visible(true);
2299 }
2300}
2301
2302//NMM 11/24/2015 - Display provider names when swapping 2 valid shifts. Enables Swap Providers
2303//ONLY when 2 shifts are selected.
2304function ToggleSwapMenuItem(contextMenu) {
2305
2306 //get the shift primary keys and store them in an array
2307 var arShiftPrimaryKeys = GetSelectedShiftPrimaryKeys();
2308 var OPEN_SHIFT = "-1";
2309 var contextMenuItem = contextMenu.findItemByValue(AdminTask.SwapProviders);
2310
2311 if (contextMenuItem != null) {
2312 contextMenuItem.set_text(WebConstants.CMI_ADMIN_SWAP_PROVIDERS_LABEL);
2313 contextMenuItem.set_enabled(false);
2314 }
2315
2316 if (arShiftPrimaryKeys.length === 2 && contextMenuItem != null) {
2317
2318 //extract the provider id from the shiftprimary key
2319 var provider1Id = arShiftPrimaryKeys[0].substring(arShiftPrimaryKeys[0].lastIndexOf("|") + 1);
2320 var provider2Id = arShiftPrimaryKeys[1].substring(arShiftPrimaryKeys[1].lastIndexOf("|") + 1);
2321 var bBothOpenShifts = provider1Id == OPEN_SHIFT && provider2Id == OPEN_SHIFT;
2322 var bBothSameProviders = provider1Id == provider2Id;
2323
2324 //Edit the Swap provider menu item
2325 if (!bBothOpenShifts && !bBothSameProviders) {
2326 //VJF 12/14/2016 - Get the name from the dom since we want to use the mnemonic for an open shift
2327 var arAptDom = GetSelectedAptDomIds();
2328
2329 var provider1Name = FindProviderNameFromGridByDomId(arAptDom[0]);
2330 var provider2Name = FindProviderNameFromGridByDomId(arAptDom[1]);
2331
2332 contextMenuItem.set_text("Swap " + provider1Name + " and " + provider2Name);
2333 contextMenuItem.set_enabled(true);
2334 }
2335 }
2336}
2337
2338//NMM 12/23/2015 - Edit the "Assign Provider" context menu item based on the type of multiple shifts selected
2339function ToggleAssignProviderOnMultipleSelection(contextMenu) {
2340
2341 //Get Selected Shift Primary Keys
2342 var arShiftDOM = GetCombinedShiftPrimaryKeysAndAptDomIds();
2343 arShiftDOM = arShiftDOM.split(",");
2344 var nProvCount = 0;
2345 var provId; var aptId;
2346
2347 //Check the selected shifts if they contain providers
2348 for (i = 0; i < arShiftDOM.length; i++) {
2349 extractedProvId = arShiftDOM[i].substring(arShiftDOM[i].lastIndexOf("|") + 1, arShiftDOM[i].indexOf("^"));
2350 aptId = arShiftDOM[i].substring(arShiftDOM[i].lastIndexOf("^") + 1);
2351
2352 var apt = GetAptObjByAptDomIdFromSelectedRadScheduler(aptId);
2353 if (apt.get_contextMenuID() == WebConstants.CM_ADMIN_SHIFT_ID) {
2354
2355 //Check if the same provider is selected
2356 if (extractedProvId != provId) {
2357 nProvCount++;
2358 }
2359
2360 if (nProvCount > 1) {
2361 break;
2362 }
2363
2364 provId = extractedProvId;
2365 }
2366 }
2367
2368 var contextMenuItem;
2369 var items = contextMenu.get_items();
2370 var bAssignProvFound = false;
2371 //get reference to the 'Assign Provider' context menu item so we can edit it.
2372 for (var i = 0; i < items.get_count() ; i++) {
2373 contextMenuItem = items.getItem(i);
2374 var contextMenuItemValue = items.getItem(i).get_value();
2375 if (contextMenuItemValue == AdminTask.AssignProvider) {
2376 bAssignProvFound = true;
2377 break;
2378 }
2379 }
2380
2381 if (bAssignProvFound) {
2382 var provId = document.forms.frmScheduler.hdnSelectedProvider.value;
2383 var selectedProvName = FindProviderNameFromGrdById(provId);
2384
2385 //Edit the 'Assign provider' context menu item according to the selection of multiple shifts
2386 //If a single provider is selected together with open shifts, we add the provider name e.g. "Assign A.Doctor"
2387 //TJF 3/22/2017 - Allow assignment on shifts with providers
2388 if (nProvCount == 1) {
2389 contextMenuItem.set_enabled(true);
2390 contextMenuItem.set_text("Assign " + selectedProvName);
2391
2392 //If there is no provider selected and there is an already reference provider via the "info selected prov box", we also add the provider name in
2393 } else if ((nProvCount == 0 || nProvCount > 1) && document.forms.frmScheduler.hdnSelectedProvider.value != "-1") {
2394 contextMenuItem.set_enabled(true);
2395 contextMenuItem.set_text("Assign " + selectedProvName);
2396
2397 //If there are multiple providers selected or if you select open shifts with no provider referenced we disable the "Assign Prov" context menu item
2398 } else {
2399 contextMenuItem.set_enabled(false);
2400 contextMenuItem.set_text(WebConstants.CMI_ASSIGN_PROVIDER_LABEL);
2401 }
2402 }
2403
2404}
2405
2406//NMM 12/8/2015 - Shows/Hides Remove Providers Menu item on multiple selection.
2407//We ONLY hide the "Remove Providers" menu item if the apt selections only contain
2408//open shifts
2409function ToggleRemoveProviderOnMultipleSelection(contextMenu) {
2410
2411 //Get Selected Shift Primary Keys
2412 var arAptDomIds = GetSelectedAptDomIds();
2413 var bAllOpenShifts = false;
2414 var provId;
2415 var openShift = "-1";
2416
2417 //Check if multiple appointment selection contains an open shift
2418 for (i = 0; i < arAptDomIds.length; i++) {
2419 var apt = GetAptObjByAptDomIdFromSelectedRadScheduler(arAptDomIds[i]);
2420 if (apt.get_contextMenuID() == WebConstants.CM_ADMIN_SHIFT_OPEN_ID) {
2421 bAllOpenShifts = true;
2422 } else {
2423 bAllOpenShifts = false;
2424 break;
2425 }
2426
2427 }
2428 var items = contextMenu.get_items();
2429
2430 //Hide the "Remove Providers" context menu if all the selected appointments are open shifts, otherwise show it
2431 //JCL 8/21/2017 - Hide 'Set Manually Assigned' if open shifts are selected.
2432 for (var i = 0; i < items.get_count() ; i++) {
2433 var contextMenuItem = items.getItem(i);
2434 var contextMenuItemValue = items.getItem(i).get_value();
2435 if (contextMenuItemValue == AdminTask.RemoveProviders
2436 || contextMenuItemValue == AdminTask.SetManuallyAssigned) {
2437
2438 //On multiple apt selection, we reset the "remove providers" label
2439 contextMenuItem.set_text(WebConstants.CMI_REMOVE_PROVIDER_LABEL);
2440 if (bAllOpenShifts) {
2441 contextMenuItem.set_visible(false);
2442 } else {
2443 contextMenuItem.set_visible(true);
2444 }
2445 break;
2446 }
2447 }
2448}
2449
2450//NMM 12/14/2015 - This function will set the context menu to be hidden or shown. This will only
2451//be called on a mobile device. Long clicking on the iPad has a different code flow from an Android device.
2452//*Note: When a long click is initiated, both IOS and Android will call the AppointmentContextMenu().
2453//After that call, the IOS will make the extra call to AppointmentClicked() while the Android will not.
2454function HideOrShowContextMenuOnMobile() {
2455 //Checks if using Android or IOS and show/hide the context menu accordingly.
2456 if ($telerik.isAndroid == false) {
2457 if (gHideContextMenu == false) {
2458
2459 //Show context menu
2460 $('ul.rmActive').removeClass('hideAptContextMenu');
2461 }
2462 } else {
2463 $('ul.rmActive').removeClass('hideAptContextMenu');
2464 }
2465
2466 //Reset flag for hiding/showing context menu
2467 if ($telerik.isAndroid == false) {
2468 gHideContextMenu = false;
2469 }
2470}
2471
2472//TJF 5/11/2016 - Filter Admin Request Calendar by practitioner type
2473function FilterByPractitionerType(sender) {
2474 //Set combobox text to desired text if all is checked instead of the default
2475 if (sender.get_checkAllCheckBox().checked) {
2476 sender.set_text(gRcbPractAllText);
2477 }
2478
2479 setWidthOfComboboxToTextLength(sender);
2480
2481 if (sender != null) {
2482 var arrVisibleProviderIds = getFilteredProviders(sender);
2483 var arrReasonCheckedComboBoxValues = [];
2484 var bReasonCheckAllChecked = true;
2485
2486 //TJF 2/28/2017 - Reason is now null when off times are not being displayed
2487 if (gRcbReason != null && document.getElementById("tbCalendarSubMenu_i8_rcbReason") != null) {
2488 arrReasonCheckedComboBoxValues = GetReasonValues(gRcbReason);
2489 bReasonCheckAllChecked = gRcbReason.get_items().get_count() > 0 ? gRcbReason.get_checkAllCheckBox().checked : true;
2490 }
2491
2492 FilterByPractTypeAndOffReason(arrVisibleProviderIds, arrReasonCheckedComboBoxValues, sender.get_checkAllCheckBox().checked, bReasonCheckAllChecked);
2493 }
2494}
2495
2496function FilterByPractTypeAndOffReason(arrPractVisibleProviderIds, arrReasonCheckedComboBoxValues, bAllPractChecked, bAllReasonChecked) {
2497 //TJF 3/3/2017 - Check if all providers is selected
2498 var bAllProvidersSelected = $("#hdnSelectedProvider").val() == -1;
2499
2500 //Support multiple schedulers
2501 $(".RadScheduler").each(function (index) {
2502 var scheduler = $find($(this).attr('id'));
2503 (scheduler.get_appointments()).forEach(function (item) {
2504 //TJF 5/13/2016 - Hide or show provider's appointment
2505 var nProvIdIndex = arrPractVisibleProviderIds.indexOf(item.get_attributes().getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_PROV_ID));
2506
2507 var nReasonId = item.get_attributes().getAttribute(WebConstants.CAL_APPT_ATTR_REASON_ID);
2508 var nReasonIdIndex = arrReasonCheckedComboBoxValues.indexOf(nReasonId);
2509
2510 //TJF 3/3/2017 - Get the reason name to set on appointment when filter is in use
2511 var sReasonName = "";
2512 if (gRcbReason != null && nReasonId != -1) {
2513 var cbiReason = gRcbReason.findItemByValue(nReasonId);
2514 if (cbiReason != null) {
2515 sReasonName = cbiReason.get_text();
2516 }
2517 }
2518
2519 var nTaskId = item.get_attributes().getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_TASK_ID);
2520
2521 var $appointment = $(item.get_element());
2522 var $rsAptElement = $($appointment.find(".rsAptSubject")[0]);
2523 //TJF 2/28/2017 - Never filter requests without a reason id unless they are meetings
2524 if ((nProvIdIndex > -1 || bAllPractChecked) &&
2525 (nReasonIdIndex > -1 || bAllReasonChecked || (nReasonId == -1 && nTaskId != ProviderTask.Meeting))) {
2526 //Show
2527 $appointment.show();
2528
2529 //TJF 3/3/2017 - Change text when reason filter is in use to include off request reason
2530 if (nReasonId > -1 && bAllProvidersSelected) {
2531 if (!bAllReasonChecked) {
2532 if ($rsAptElement.data("default-text") == undefined) {
2533 $rsAptElement.data("default-text", $rsAptElement.html());
2534 }
2535 $rsAptElement.html($rsAptElement.data("default-text").replace("Off", sReasonName));
2536 } else {
2537 $rsAptElement.html($rsAptElement.data("default-text"));
2538 }
2539 }
2540 } else {
2541 $appointment.hide();
2542 }
2543 });
2544 });
2545}
2546
2547function getFilteredProviders(rcbPractitioner) {
2548 if (rcbPractitioner == null) {
2549 return [];
2550 }
2551
2552 var checkedItems = rcbPractitioner.get_checkedItems();
2553
2554 var arrVisibleProviderIds = [];
2555 for (var i = 0; i < checkedItems.length; i++) {
2556 var arrProviderIds = checkedItems[i].get_value().split(",");
2557
2558 //TJF 5/12/2016 - Add visible provider ids to array
2559 for (var j = 0; j < arrProviderIds.length; j++) {
2560 if (arrVisibleProviderIds.indexOf(arrProviderIds[j]) == -1) {
2561 arrVisibleProviderIds.push(arrProviderIds[j]);
2562 }
2563 }
2564 }
2565
2566 return arrVisibleProviderIds;
2567}
2568
2569//TJF 5/16/2016 - Change default text to all and set combobox width. This runs on combobox load to default it to say "All"
2570var gRcbPractitioner = null;
2571function rcbPractitioner_OnClientLoadHandler(sender) {
2572 gRcbPractitioner = sender;
2573 //TJF 5/27/2016 - Load previous selections when page is ready.
2574 //This prevents null pointers being accessed while page is in loading
2575 $(document).ready(function () {
2576 FilterByPractitionerType(gRcbPractitioner);
2577 });
2578}
2579
2580function setWidthOfComboboxToTextLength(sender) {
2581 if (sender.get_checkAllCheckBox().checked) {
2582 sender.set_text(gRcbPractAllText);
2583 }
2584
2585 //TJF 5/16/2016 - Set correct width for combobox
2586 var widthElement = $(sender.get_element()).find(".rcbInputCellLeft");
2587 var newTextWidth = getTextWidth(sender.get_text());
2588 //if (gRcbPractMinWidth > newTextWidth) {
2589 // newTextWidth = gRcbPractMinWidth;
2590 //} else if (gRcbPractMaxWidth < newTextWidth) {
2591 // newTextWidth = gRcbPractMaxWidth;
2592 //}
2593
2594 //TJF 2/23/2017 - We have run out of space on the toolbar so the comboboxes must always be minimum width
2595 newTextWidth = gRcbPractMinWidth;
2596
2597 widthElement.width(newTextWidth);
2598
2599}
2600
2601//TJF 2/22/2017 - Client side filtering of off request reason on request calendar
2602function FilterByOffRequestReason(sender) {
2603
2604 if (sender.get_checkAllCheckBox().checked) {
2605 sender.set_text(gRcbPractAllText);
2606 }
2607
2608 setWidthOfComboboxToTextLength(sender);
2609
2610 var arrCheckedComboBoxValues = GetReasonValues(sender);
2611 var arrPractVisibleProviderIds = (gRcbPractitioner == null || document.getElementById("tbCalendarSubMenu_i7_rcbPractitioner") == null ? [] : getFilteredProviders(gRcbPractitioner));
2612 var bAllPractChecked = gRcbPractitioner == null ? true : gRcbPractitioner.get_checkAllCheckBox().checked;
2613 var bAllReasonChecked = sender.get_checkAllCheckBox().checked;
2614 FilterByPractTypeAndOffReason(arrPractVisibleProviderIds, arrCheckedComboBoxValues, bAllPractChecked, bAllReasonChecked);
2615
2616}
2617
2618function GetReasonValues(rcbReason) {
2619 var arrCheckedComboboxItems = rcbReason.get_checkedItems();
2620 var arrCheckedComboBoxValues = [];
2621
2622 for (var i = 0; i < arrCheckedComboboxItems.length; i++) {
2623 var comboBoxItem = arrCheckedComboboxItems[i];
2624 arrCheckedComboBoxValues.push(comboBoxItem.get_value());
2625 }
2626
2627 return arrCheckedComboBoxValues;
2628}
2629
2630var gRcbReason = null;
2631function rcbReason_OnClientLoadHandler(sender) {
2632 gRcbReason = sender;
2633 //TJF 5/27/2016 - Load previous selections when page is ready.
2634 //This prevents null pointers being accessed while page is in loading
2635 $(document).ready(function () {
2636 //TJF 2/24/2017 - If Practitioner filter is not on the page, set the filter from this function
2637 if (gRcbPractitioner == null) {
2638 FilterByOffRequestReason(gRcbReason);
2639 } else {
2640 setWidthOfComboboxToTextLength(gRcbReason);
2641 }
2642 });
2643}
2644
2645//TJF 5/16/2016 - Gets the width of text not yet in the dom
2646function getTextWidth(text) {
2647 //Create temp element
2648 var $testElement = $("<span>" + text + "</span>");
2649 //insert into dom
2650 $(document.body).append($testElement);
2651 //Now that element has been inserted, an accurate width can be measured
2652 var width = $testElement.width();
2653 //remove from dom now that width has been found
2654 $testElement.remove();
2655
2656 return width;
2657}
2658
2659
2660/**********************************************************/
2661/* Admin Assignment/Removal on Client Side */
2662/**********************************************************/
2663
2664function UpdateShiftDetails(JsonShifts) {
2665 JsonShifts = CleanJSONString(JsonShifts);
2666
2667 JSON.parse(JsonShifts).forEach(function (shiftInfo) {
2668 if (shiftInfo.Action == 0) {
2669 if (shiftInfo.isAdjustWorked == "false") {
2670 var aptDomId = FindSplitDomId(shiftInfo.ShiftKey, shiftInfo.AptDomId);
2671 if (aptDomId == "") {
2672 aptDomId = GetAptDomIdByShiftKey(shiftInfo.ShiftKey);
2673 }
2674 shiftInfo.AptDomId = aptDomId;
2675 }
2676
2677 if (aptDomId != "") {
2678 UpdateSingleShiftDetails(shiftInfo);
2679 }
2680 }
2681 });
2682}
2683
2684//TJF 2/29/2016 - Used by adjust worked and adjust planned to update the dom
2685function UpdateSingleShiftDetails(shiftInfo) {
2686
2687 var tooltip = shiftInfo.Tooltip;
2688 var aptDomId = shiftInfo.AptDomId;
2689 var providerHoursChange = parseFloat(shiftInfo.ProviderHoursChange);
2690 var providerId = parseInt(shiftInfo.ProviderId);
2691 var newShiftName = shiftInfo.ShiftName;
2692 var newShiftTime = shiftInfo.ShiftTime;
2693 var isAdjustWorked = shiftInfo.isAdjustWorked;
2694 var dayIndex = shiftInfo.DayIndex;
2695
2696 var bIsRemoval = (shiftInfo.isRemoval == "true");
2697 var bIsAdjustWorked = (shiftInfo.isAdjustWorked == "true");
2698
2699 //var arrShiftGroupIds = shiftInfo.ShiftGroupIds.split(",");
2700
2701 var $aptElem = $("#" + aptDomId);
2702
2703 //TJF 3/3/2016 - Get apt object
2704 var aptObj = GetAptObjByAptDomIdFromSelectedRadScheduler(aptDomId);
2705
2706 //TJF 10/28/2016 - Update appointment object
2707 var curDuration = parseFloat(aptObj.get_attributes().getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_SHIFT_DURATION));
2708
2709 curDuration += (providerHoursChange * 60); //TJF 11/23/2016 - Round to 1 decimal place
2710 aptObj.get_attributes().setAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_SHIFT_DURATION, curDuration);
2711
2712 //TJF 3/3/2016 - Add tooltip
2713 $aptElem.attr("title", tooltip);
2714
2715 //TJF 3/3/2016 - Add exception class
2716 //TJF 12/15/2016 - Add classes regardless of if date is in active period
2717 var $evtLocShift = $aptElem.find(".evtLocShift");
2718 if (!bIsAdjustWorked) {
2719
2720 $evtLocShift.text(newShiftName + " " + newShiftTime);
2721
2722 if (!bIsRemoval) {
2723 if (!$evtLocShift.hasClass("exception")) {
2724 $evtLocShift.addClass("exception");
2725 }
2726 } else {
2727 if ($evtLocShift.hasClass("exception")) {
2728 $evtLocShift.removeClass("exception");
2729 }
2730 }
2731
2732 } else {
2733 $evtLocShift.text(newShiftName + " " + newShiftTime);
2734
2735 //TJF 3/4/2016 - Handle adjust worked
2736 if (!bIsRemoval) {
2737 if (!$evtLocShift.hasClass("adjWorked")) {
2738 $evtLocShift.addClass("adjWorked");
2739 }
2740 } else {
2741 if ($evtLocShift.hasClass("adjWorked")) {
2742 $evtLocShift.removeClass("adjWorked");
2743 }
2744 }
2745 }
2746
2747 var assigmentElem = $(".evtOpen", $aptElem);
2748 if (assigmentElem.length) {
2749 assigmentElem[0].innerHTML = shiftInfo.OpenShiftMnemonic;
2750 }
2751
2752 if (isDateInActiveOrNotDefinedPeriod(aptObj.get_start())) {
2753 //TJF 3/3/2016 - Update affected provider's hours data attribute
2754 var masterTable = GetGrdProvInfoMasterTableView();
2755 if (providerId != -1) {
2756 var dataItem = FindDataItemById(providerId);
2757 var nOrigHours = parseFloat(masterTable.getCellByColumnUniqueName(dataItem, "ProviderHours").innerHTML);
2758 var totalHours = nOrigHours + providerHoursChange;
2759 masterTable.getCellByColumnUniqueName(dataItem, "ProviderHours").innerHTML = RoundValueToDecimalPlace(totalHours, 1);
2760 var chkSelected = FindCheckboxFromGridById(providerId);
2761 selectedProviderCheckedOnGrid(chkSelected);
2762 } else {
2763
2764 //NMM 4/1/2016 - Update the total open shifts row on the providers grid
2765 var openShiftRow = FindDataItemById(0);
2766 var nOldOpenHours = parseFloat(masterTable.getCellByColumnUniqueName(openShiftRow, "ProviderHours").innerHTML);
2767 var nNewOpenHours = nOldOpenHours + providerHoursChange;
2768 masterTable.getCellByColumnUniqueName(openShiftRow, "ProviderHours").innerHTML = RoundValueToDecimalPlace(nNewOpenHours, 1);
2769 }
2770
2771 //TJF 10/28/2016 - Reload tree json, only if grid is loaded
2772 if (gShiftGroupCountsLoaded) {
2773 LoadAndProcessShiftGroupTreeJsonInBothUnits(false);
2774 }
2775 }
2776
2777
2778 //Adjust worked does not change sort order
2779 if (!bIsAdjustWorked) {
2780 SortShiftDay($aptElem, dayIndex);
2781 }
2782}
2783
2784//TJF 6/30/2017 - For adding and removing split shifts
2785function UpdateSplitShift(JsonShifts) {
2786 JsonShifts = CleanJSONString(JsonShifts);
2787 var removeShifts = [];
2788 JSON.parse(JsonShifts).forEach(function (shiftInfo) {
2789 if (shiftInfo.Action == 0) {
2790 var sShiftKey = shiftInfo.ShiftKey;
2791
2792 var aptDomId = GetAptDomIdByShiftKey(sShiftKey);
2793 if (aptDomId == "" && shiftInfo.AptDomId.indexOf(",") == -1) {
2794 aptDomId = shiftInfo.AptDomId;
2795 } else if (aptDomId == "") {
2796 aptDomId = GetAptDomIdByShiftKey(sShiftKey.replace("|-1|", "|0|"));
2797 }
2798
2799 insertAppointment(shiftInfo, aptDomId, aptDomId, true);
2800
2801 } else {
2802 removeShifts.push(shiftInfo);
2803 }
2804
2805 });
2806
2807 removeShifts.forEach(function (shiftInfo) {
2808 var aptDomId = FindSplitDomId(shiftInfo.ShiftKey, shiftInfo.AptDomId);
2809 shiftInfo.AptDomId = aptDomId;
2810 });
2811
2812 removeShifts.forEach(function (shiftInfo) {
2813 RemoveShift(shiftInfo, false);
2814 });
2815
2816}
2817
2818//TJF 11/29/2017 - Mass update shift statuses client side
2819function UpdateShiftStatuses(JsonShifts) {
2820 JsonShifts = CleanJSONString(JsonShifts);
2821 JSON.parse(JsonShifts).forEach(function (shiftInfo) {
2822 var tooltip = shiftInfo.Tooltip;
2823 var aptDomId = shiftInfo.AptDomId; //This can be set to "B" in order to fetch it from shiftkey. In this case, shiftkey must be supplied
2824 var bIsRemoval = (shiftInfo.isRemoval == "true");
2825 var bNotCounted = (shiftInfo.bNotCounted == "true"); //Shift status is not counted
2826 var bShiftNotCounted = (shiftInfo.bShiftNotCounted == "true"); //Actual base shift is not counted(a responsibility shift)
2827 var sShiftKey = shiftInfo.ShiftKey;
2828 var bExceptionRemoved = (shiftInfo.bIsExceptionRemoved == "true");
2829 var bIsStatusCounted = shiftInfo.bIsStatusCounted;
2830 var nStatusId = shiftInfo.nShiftStatusId;
2831
2832 //TJF 12/4/2017 - Fetch dom id by shift key
2833 if (aptDomId == "B") {
2834 aptDomId = GetAptDomIdByShiftKey(sShiftKey);
2835 }
2836
2837 var aptObj = GetAptObjByAptDomIdFromSelectedRadScheduler(aptDomId);
2838 var $aptElem = $("#" + aptDomId);
2839
2840 //TJF 11/29/2017 - Add tooltip
2841 $aptElem.attr("title", tooltip);
2842
2843 var assigmentElem = $(".evtOpen", $aptElem);
2844 if (assigmentElem.length == 0) {
2845 assigmentElem = $(".evtProv", $aptElem);
2846 }
2847
2848 var bAssignedProvider = (shiftInfo.ProviderId != "-1");
2849 if (assigmentElem.length) {
2850
2851 //Only an open shifts name can be changed
2852 if (!bAssignedProvider) {
2853 assigmentElem[0].innerHTML = shiftInfo.OpenShiftMnemonic;
2854 }
2855 var $evtLocShift = $aptElem.find(".evtLocShift");
2856
2857 if (!bIsRemoval) {
2858
2859 //Add exception class
2860 if (!$evtLocShift.hasClass("exception")) {
2861 $evtLocShift.addClass("exception");
2862 }
2863
2864 //Apply proper css class if shift is not counted
2865 if (bNotCounted && !$aptElem.hasClass('not-counted')) {
2866 $aptElem.addClass('not-counted');
2867
2868 var bIsShiftCounted = !bShiftNotCounted;
2869
2870 if (isDateInActiveOrNotDefinedPeriod(aptObj.get_start())) {
2871 aptObj.get_attributes().setAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_COUNT_SHIFT, bIsStatusCounted ? 0 : 1);
2872
2873 if (bIsShiftCounted) {
2874 UpdateGlobalShiftCounts(true, bAssignedProvider);
2875 }
2876
2877 if (bAssignedProvider) {
2878 var bUpdateTotalsColumns = bIsShiftCounted;
2879 var hoursSpan = 0;
2880 //TJF 11/1/2016 - Use appointment object duration as session variables may not be updated if an adjust worked has taken place
2881 if (aptObj.get_attributes().getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_SHIFT_DURATION)) {
2882 hourSpan = ((parseFloat(aptObj.get_attributes().getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_SHIFT_DURATION)) / 60));
2883 }
2884
2885 //var arrShiftGroupIds = shiftInfo.ShiftGroupIds.split(",");
2886 var arrShiftGroupIds = [];
2887 UpdateStatusBarTotals(shiftInfo.ProviderId, true, hourSpan, arrShiftGroupIds, bUpdateTotalsColumns, true);
2888 } else if (bIsShiftCounted) {
2889 //TJF 12/11/2017 - Update total hours
2890 var masterTable = GetGrdProvInfoMasterTableView();
2891 var rowOpenShift = FindDataItemById(0);
2892 var openHours = parseFloat(masterTable.getCellByColumnUniqueName(rowOpenShift, "ProviderHours").innerHTML);
2893
2894 var hoursSpan = 0;
2895 //TJF 11/1/2016 - Use appointment object duration as session variables may not be updated if an adjust worked has taken place
2896 if (aptObj.get_attributes().getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_SHIFT_DURATION)) {
2897 hourSpan = ((parseFloat(aptObj.get_attributes().getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_SHIFT_DURATION)) / 60));
2898 }
2899
2900 openHours -= parseFloat(hourSpan);
2901 openHours = RoundValueToDecimalPlace(openHours, 1);
2902
2903 masterTable.getCellByColumnUniqueName(rowOpenShift, "ProviderHours").innerHTML = (parseFloat(openHours));
2904 }
2905 }
2906
2907 }
2908
2909 } else {
2910
2911 //Remove exception class
2912 if ($evtLocShift.hasClass("exception") && bExceptionRemoved) {
2913 $evtLocShift.removeClass("exception");
2914 }
2915
2916 //This class is only used for shift statuses, remove it
2917 if ($aptElem.hasClass('not-counted')) {
2918
2919 //Set counting status based on base shift's status
2920 aptObj.get_attributes().setAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_COUNT_SHIFT, bShiftNotCounted ? 1 : 0);
2921
2922
2923
2924 $aptElem.removeClass('not-counted');
2925
2926 var bIsShiftCounted = !bShiftNotCounted;
2927 if (isDateInActiveOrNotDefinedPeriod(aptObj.get_start())) {
2928
2929 if (bIsShiftCounted) {
2930 UpdateGlobalShiftCounts(false, bAssignedProvider);
2931 }
2932
2933 if (bAssignedProvider) {
2934
2935 var bUpdateTotalsColumns = bIsShiftCounted;
2936 var hourSpan = 0;
2937 //TJF 11/1/2016 - Use appointment object duration as session variables may not be updated if an adjust worked has taken place
2938 if (aptObj.get_attributes().getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_SHIFT_DURATION)) {
2939 hourSpan = ((parseFloat(aptObj.get_attributes().getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_SHIFT_DURATION)) / 60));
2940 }
2941
2942 //var arrShiftGroupIds = shiftInfo.ShiftGroupIds.split(",");
2943 UpdateStatusBarTotals(shiftInfo.ProviderId, false, hourSpan, [], bUpdateTotalsColumns, false);
2944 } else if (bIsShiftCounted) {
2945 //TJF 12/11/2017 - Update total hours
2946 var masterTable = GetGrdProvInfoMasterTableView();
2947 var rowOpenShift = FindDataItemById(0);
2948 var openHours = parseFloat(masterTable.getCellByColumnUniqueName(rowOpenShift, "ProviderHours").innerHTML);
2949
2950 var hoursSpan = 0;
2951 //TJF 11/1/2016 - Use appointment object duration as session variables may not be updated if an adjust worked has taken place
2952 if (aptObj.get_attributes().getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_SHIFT_DURATION)) {
2953 hourSpan = ((parseFloat(aptObj.get_attributes().getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_SHIFT_DURATION)) / 60));
2954 }
2955
2956 openHours += parseFloat(hourSpan);
2957 openHours = RoundValueToDecimalPlace(openHours, 1);
2958
2959 masterTable.getCellByColumnUniqueName(rowOpenShift, "ProviderHours").innerHTML = (parseFloat(openHours));
2960 }
2961
2962 }
2963 }
2964
2965 }
2966
2967 //TJF 3/26/2018 - Set shift status id
2968 if (nStatusId != null) {
2969 aptObj.get_attributes().setAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_SHIFT_STATUS_ID, nStatusId);
2970 }
2971
2972 }
2973 });
2974
2975
2976
2977 //TJF 3/26/2018 - Reapply status colors
2978 if (gColorContext == COLORCONTEXT.ShiftStatus && gApplyColorsChecked) {
2979 HighlightByShiftStatus(g_arrShiftStatusIds, false, false);
2980 highlightCheckedProvidersOnGrid();
2981 }
2982
2983}
2984
2985//TJF 1/22/2018 - Update shift tooltips. Used by AdminShiftComment.aspx
2986function UpdateTooltips(JsonShifts) {
2987
2988 //TJF 2/1/2018 - Filter out incorrect escape sequences in JSON
2989 JsonShifts = CleanJSONString(JsonShifts);
2990
2991 JSON.parse(JsonShifts).forEach(function (shiftInfo) {
2992 var sTooltip = shiftInfo.Tooltip;
2993 var sShiftKey = shiftInfo.ShiftKey;
2994 var aptDomId = GetAptDomIdByShiftKey(sShiftKey);
2995
2996 var $aptElem = $("#" + aptDomId);
2997
2998 //TJF 1/22/2018 - Set tooltip
2999 $aptElem.attr("title", sTooltip);
3000
3001 });
3002}
3003
3004//TJF 2/26/2018 - Remove illegal json characters from a json string
3005function CleanJSONString(sJSONString) {
3006 return sJSONString.replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t").replace(/\f/g, "\\f");
3007}
3008
3009//TJF 6/16/2017 - Use shift key of first split to find the next one
3010function FindSplitDomId(sShiftPrimaryKey, sDomId) {
3011 var scheduler = GetSchedulerByAptDomId(sDomId);
3012 if (scheduler == null) {
3013 return "";
3014 }
3015
3016 var apts = scheduler.get_appointments();
3017 var splitId = "";
3018 for (var i = 0; i < apts.get_count() ; i++) {
3019 var apt = apts.getAppointment(i);
3020 var attribs = apt.get_attributes();
3021 var selectedShiftPrimaryKey = attribs.getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_APPT_ID);
3022 if (selectedShiftPrimaryKey == sShiftPrimaryKey) {
3023 splitId = apt.get_element().id;
3024 break;
3025 }
3026 }
3027
3028 return splitId;
3029
3030}
3031
3032function SortShiftDay($aptElem, dayIndex) {
3033
3034 var $rsWrap = $($aptElem.parent());
3035 var $rsSlot = $($rsWrap.parent());
3036 var children = $rsSlot.children(":not(.rsDateWrap)");
3037
3038 children = children.filter(function (index) {
3039 return $(children[index].children).hasClass("rsApt");
3040 });
3041
3042 var currentIndex = 0;
3043
3044 //Find the
3045 children.each(function (i) {
3046 var child = children[i];
3047
3048 if ($rsWrap.is($(child))) {
3049 currentIndex = i;
3050 return false;
3051 }
3052 });
3053
3054 if (currentIndex > dayIndex) {
3055 $rsWrap.insertBefore(children[dayIndex]);
3056 } else if (currentIndex < dayIndex) {
3057 $rsWrap.insertAfter(children[dayIndex]);
3058 }
3059
3060}
3061
3062//TJF 2/12/2018 - For multiple select adjusted planned
3063function MultiRemoveShift(JsonShifts) {
3064 JSON.parse(JsonShifts).forEach(function (shiftInfo) {
3065 var aptDomId = GetAptDomIdByShiftKey(shiftInfo.ShiftKey);
3066 shiftInfo.AptDomId = aptDomId;
3067
3068 RemoveShift(shiftInfo, false);
3069 });
3070}
3071
3072//TJF 3/11/2016 - Remove a shift from the dom and update provider dock
3073//TJF 6/23/2017 - Added bPermanentRemoval to indicate this shift is being removed completely instead of being replace by a split
3074//TJF 2/12/2018 - Changed bPermanentRemoval to bSingleShift to indicate if a single shift is being passed or an Array
3075function RemoveShift(JsonShift, bSingleShift) {
3076
3077 var shiftInfo = {};
3078 if (bSingleShift) {
3079 shiftInfo = JSON.parse(JsonShift)[0];
3080 } else {
3081 shiftInfo = JsonShift;
3082 }
3083
3084 var aptDomId = shiftInfo.AptDomId;
3085 var $aptElem = $("#" + aptDomId);
3086 var aptObj = GetAptObjByAptDomIdFromSelectedRadScheduler(aptDomId);
3087
3088 //Get if shift is counted
3089 var bIsShiftCounted = aptObj.get_attributes().getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_COUNT_SHIFT) == 0 ? true : false;
3090
3091 if (isDateInActiveOrNotDefinedPeriod(aptObj.get_start())) {
3092 var bAssignedProvider = (shiftInfo.ProviderId != "-1");
3093
3094 var hourSpan = 0;
3095 //TJF 11/1/2016 - Use appointment object duration as session variables may not be updated if an adjust worked has taken place
3096 if (aptObj.get_attributes().getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_SHIFT_DURATION)) {
3097 hourSpan = ((parseFloat(aptObj.get_attributes().getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_SHIFT_DURATION)) / 60));
3098 }
3099
3100 if (bIsShiftCounted) {
3101 UpdateGlobalShiftCounts(true, bAssignedProvider, true, (-1 * hourSpan));
3102 }
3103
3104 if (bAssignedProvider) {
3105 var bUpdateTotalsColumns = bIsShiftCounted;
3106
3107
3108 var arrShiftGroupIds = shiftInfo.ShiftGroupIds.split(",");
3109
3110 UpdateStatusBarTotals(shiftInfo.ProviderId, true, hourSpan, arrShiftGroupIds, bUpdateTotalsColumns, false);
3111 }
3112 }
3113
3114 var scheduler = GetSchedulerByAptDomId(aptDomId);
3115 scheduler.get_appointments().remove(aptObj);
3116
3117 //TJF 3/11/2016 - Remove appointment from dom
3118 $aptElem.parent(".rsWrap").remove();
3119
3120}
3121
3122function UpdateGlobalShiftCounts(bIsRemoval, bHasAssignedProvider, bUpdateGlobalHours, dHoursChange) {
3123
3124 if (bUpdateGlobalHours == null) {
3125 bUpdateGlobalHours = false;
3126 dHoursChange = 0;
3127 }
3128
3129 var openShiftRow = FindDataItemById(OPEN_SHIFT_ID);
3130 var masterTable = GetGrdProvInfoMasterTableView();
3131
3132 var percentFilled = parseFloat(masterTable.getCellByColumnUniqueName(openShiftRow, "FilledPercent").innerHTML);
3133 var globalTotalOpens = parseFloat(masterTable.getCellByColumnUniqueName(openShiftRow, "ProviderShifts").innerHTML);
3134 var globalTotalOpenHours = parseFloat(masterTable.getCellByColumnUniqueName(openShiftRow, "ProviderHours").innerHTML);
3135
3136 var grid = GetGrdProvInfo();
3137 var grdProvId = "#" + grid.get_id();
3138
3139 //TJF 2/22/2016 - Determine total number of shifts for percent filled calculation
3140 var globalTotalShifts = parseInt(SumTotalShifts());
3141
3142 //TJF 3/11/2016 - A removed shift is always an empty shift
3143 if (bIsRemoval) {
3144 globalTotalShifts -= 1;
3145 if (!bHasAssignedProvider) {
3146 globalTotalOpens -= 1;
3147 }
3148
3149 } else {
3150 globalTotalShifts += 1;
3151 if (!bHasAssignedProvider) {
3152 globalTotalOpens += 1;
3153 }
3154 }
3155
3156 if (bUpdateGlobalHours && !bHasAssignedProvider) {
3157 globalTotalOpenHours += dHoursChange;
3158 }
3159
3160 var globalTotalFilledShifts = globalTotalShifts - globalTotalOpens;
3161
3162 percentFilled = globalTotalShifts == 0 ? 100 : parseFloat((globalTotalFilledShifts / globalTotalShifts) * 100).toFixed(0);
3163
3164 masterTable.getCellByColumnUniqueName(openShiftRow, "FilledPercent").innerHTML = percentFilled;
3165 masterTable.getCellByColumnUniqueName(openShiftRow, "ProviderShifts").innerHTML = globalTotalOpens;
3166
3167 if (bUpdateGlobalHours && !bHasAssignedProvider) {
3168 masterTable.getCellByColumnUniqueName(openShiftRow, "ProviderHours").innerHTML = globalTotalOpenHours;
3169 }
3170
3171 $(grdProvId + " .lblTotalShifts").text(globalTotalShifts);
3172
3173 var chkbox = FindCheckboxFromGridById(OPEN_SHIFT_ID);
3174 selectedProviderCheckedOnGrid(chkbox);
3175}
3176
3177
3178function UpdateShiftAssignments(JsonShifts) {
3179 var arrShifts = JSON.parse(JsonShifts);
3180
3181 for (var i in arrShifts) {
3182 var provId = arrShifts[i].ProviderId;
3183 if (provId == "-1") {
3184 RemoveProvOnClient(arrShifts[i]);
3185 } else {
3186 AssignProvOnClient(arrShifts[i]);
3187 }
3188 }
3189
3190 //TJF 9/7/2016 - Highlight providers
3191 highlightCheckedProvidersOnGrid(false);
3192}
3193
3194//NMM 1/13/2016 - Removes the provider on a shift. Takes in a JSON string to which we will convert into an array of objects
3195//Below is the property, format and description of the JSON object
3196//shiftKey : 1424721229|72|-1|20151110 : The shift primary key
3197//provId : 1147 : The provider Id
3198//aptDomId : 27_0 : The appointment html DOM id
3199//shiftStatus : "critical" : This determines the shift status of the shift whether it was successful, had an error removing or had a broken rule
3200//-----
3201//HTML MANIPULATIONS:
3202//Update the apt div class from "evtProv" to "evtOpen"
3203//Update the apt div text from "A.Doctor" to "Open",
3204//Remove ID attribute from "evtOpen"
3205//If shift date < today, add class "openShift expired" to lighten css color
3206//Update appointment ContextMenuID from "cmAdminShift" to "cmAdminOpen"
3207//Update appointment attribute key ("pi") to "-1"
3208function RemoveProvOnClient(shift) {
3209 var arShiftPrimaryKey = [];
3210 var shiftAssignment;
3211 var aptDomId = shift.AptDomId;
3212 var shiftKey = shift.ShiftKey;
3213 var provId = shift.ProviderId;
3214 var shiftStatus = shift.ShiftStatus;
3215 var toolTip = shift.Tooltip;
3216 var hourSpan = ((parseFloat(shift.ElapsedHours)));
3217 var removedProviderId = shift.PreviousProviderId;
3218 var aptElem = $("#" + aptDomId);
3219 var arrShiftGroupIds = shift.ShiftGroupIds.split(",");
3220 var sOpenShiftMnemonic = "Open";
3221 var bIncludeInGlobalTotals = shift.IsNowCounted;
3222
3223 //TJF 4/18/2017 - In case of a custom open shift mnemonic, set it here
3224 if (shift.OpenShiftMnemonic != null) {
3225 sOpenShiftMnemonic = shift.OpenShiftMnemonic;
3226 }
3227
3228 if (shiftStatus != ShiftAssignmentStatus.ERROR) {
3229 aptElem.find(".evtProv").attr('class', 'evtOpen');
3230 aptElem.find(".evtOpen").text(sOpenShiftMnemonic);
3231 aptElem.find(".evtOpen").removeAttr("id");
3232
3233 //VJF 10/27/2016 - Remove any provider highlighting that may have existed before remove.
3234 var rsAptContent = aptElem.find(".rsAptContent")[0];
3235 AddOrRemoveHighlight(true, '', rsAptContent, '');
3236
3237 //If shift date is before the current date, we add a class that makes the text color lighter
3238 var shiftDate = GetShiftDateFromShiftPrimaryKey(shiftKey);
3239 var today = new Date();
3240 if (shiftDate < today) {
3241 aptElem.addClass("openShift expired");
3242 } else {
3243 aptElem.addClass("openShift");
3244 }
3245
3246 //Update appointment obj properties to reflect an open shift
3247 var aptObj = GetAptObjByAptDomIdFromSelectedRadScheduler(aptDomId);
3248 aptObj.set_contextMenuID(WebConstants.CM_ADMIN_SHIFT_OPEN_ID);
3249
3250 var posScheduler = GetSchedulerByAptDomId(aptDomId);
3251 aptObj._contextMenu = $find(posScheduler._resolveContextMenuID(WebConstants.CM_ADMIN_SHIFT_OPEN_ID));
3252
3253 aptObj.get_attributes().setAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_PROV_ID, "-1");
3254
3255 //JCL 1/25/2016 - Clear Broken Rules to prevent residual color coding of broken rules (e.g. Swap). No need to check the shift status since there can't be any broken rules for
3256 //an open shift.
3257 aptElem.removeClass("evtBrokenRuleCritical");
3258 aptElem.removeClass("evtBrokenRuleWarning");
3259 aptElem.find(".evtLocShift").removeClass("adjWorked");
3260
3261 aptElem.attr("title", toolTip);
3262
3263 var bUpdateTotalsColumns = aptObj.get_attributes().getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_COUNT_SHIFT) == 0 ? true : false;
3264
3265 //TJF 11/1/2016 - Use appointment object duration as session variables may not be updated if an adjust worked has taken place
3266 if (aptObj.get_attributes().getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_SHIFT_DURATION)) {
3267 hourSpan = ((parseFloat(aptObj.get_attributes().getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_SHIFT_DURATION)) / 60));
3268 }
3269
3270 //VJF 02/25/2016 - Only update status bar totals for shifts in the selected period or when the period is not defined.
3271 if (isDateInActiveOrNotDefinedPeriod(aptObj.get_start())) {
3272 UpdateStatusBarTotals(removedProviderId, true, hourSpan, arrShiftGroupIds, bUpdateTotalsColumns, bIncludeInGlobalTotals);
3273 }
3274 }
3275
3276 //Find the old selected shift PK and replace it with the updated one without a providerId at the end
3277 //to reflect an assigned shift. (1424721414|72|-1|20160126|-1)
3278 var arOldHdnShiftKeyValues = GetSelectedShiftPrimaryKeys();
3279 for (var i = 0; i < arOldHdnShiftKeyValues.length; i++) {
3280 if (arOldHdnShiftKeyValues[i].indexOf(shiftKey) !== -1) {
3281 var shiftAssignment = shiftKey + "|" + provId;
3282 arOldHdnShiftKeyValues[i] = shiftAssignment;
3283 break;
3284 }
3285 }
3286
3287 //JF 9/21/2017 - Remove "assigned" tag from shift
3288 setManuallyAssigned(aptDomId, shift.IsManuallyAssigned);
3289
3290 //Store the updated shift primary keys in the #hdnSelectedShiftPrimaryKeys with the format:
3291 //1424721350|72|-1|20160127|-1;1424721414|72|-1|20160126|-1;
3292 $("#hdnSelectedShiftPrimaryKeys").val(arOldHdnShiftKeyValues.join(";") + ";");
3293}
3294
3295//NMM 1/8/2016 - Assigns a provider to an open shift. Uses the following parameters:
3296//Below is the property, format and description of the JSON object
3297//shiftKey : 1424721229|72|-1|20151110 : The shift primary key
3298//provId : 1147 : The provider Id
3299//aptDomId : 27_0 : The appointment html DOM id
3300//shiftStatus : "critical" : This determines the shift status of the shift whether it was successful, had an error removing or had a broken rule
3301//brokenRuleMessage : "Consecutive shifts..." : Tooltip message
3302function AssignProvOnClient(shift) {
3303 var aptDomId = shift.AptDomId;
3304 var shiftKey = shift.ShiftKey;
3305 var shiftStatus = shift.ShiftStatus;
3306 var provId = shift.ProviderId;
3307 var toolTip = shift.Tooltip;
3308 var assignedProvName = FindProviderNameFromGrdById(provId);
3309 var hourSpan = ((parseFloat(shift.ElapsedHours)));
3310 var aptElem = $("#" + aptDomId);
3311 var arrShiftGroupIds = shift.ShiftGroupIds.split(",");
3312 var bIsNowCounted = shift.IsNowCounted;
3313
3314 if (shiftStatus != ShiftAssignmentStatus.ERROR) {
3315
3316 aptElem.find(".evtOpen").attr('class', 'evtProv');
3317 //TJF 3/22/2017 - Slice the name to replicate the server side's name truncation
3318 aptElem.find(".evtProv").text(assignedProvName.slice(0, 10));
3319
3320 //Change the context menu Id from "cmAdminOpen" to "cmAdminSHift"
3321 var aptObj = GetAptObjByAptDomIdFromSelectedRadScheduler(aptDomId);
3322 aptObj.set_contextMenuID(WebConstants.CM_ADMIN_SHIFT_ID);
3323
3324 var posScheduler = GetSchedulerByAptDomId(aptDomId);
3325 aptObj._contextMenu = $find(posScheduler._resolveContextMenuID(WebConstants.CM_ADMIN_SHIFT_ID));
3326
3327 aptObj.get_attributes().setAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_PROV_ID, provId);
3328
3329 aptElem.find(".evtProv").attr("id", provId);
3330 aptElem.attr("title", toolTip);
3331
3332 //Check for broken rules and update the HTML to reflect a shift with a provider
3333 if (shiftStatus != ShiftAssignmentStatus.NONE) {
3334 if (shiftStatus == ShiftAssignmentStatus.CRITICAL) {
3335 aptElem.addClass("evtBrokenRuleCritical");
3336 } else {
3337 aptElem.addClass("evtBrokenRuleWarning");
3338 }
3339 } else {
3340 //JCL 1/25/2016 - Clear Broken Rules to prevent residual color coding of broken rules (e.g. Swap)
3341 aptElem.removeClass("evtBrokenRuleCritical");
3342 aptElem.removeClass("evtBrokenRuleWarning");
3343 }
3344
3345 //TJF 12/7/2017 - This is used when a shift status that is not counted gets an assignment. The totals should then be calculated.
3346 var bUpdateGlobalTotals = true;
3347 if (bIsNowCounted) {
3348
3349 if (aptObj.get_attributes().getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_COUNT_SHIFT) == 1) {
3350 //TJF 12/7/2017 - The shift is currently not counted in the totals columns, so do not update them
3351 bUpdateGlobalTotals = false;
3352 aptObj.get_attributes().setAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_COUNT_SHIFT, 0);
3353 }
3354 }
3355
3356 var bUpdateTotalsColumns = aptObj.get_attributes().getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_COUNT_SHIFT) == 0 ? true : false;
3357
3358 //TJF 11/1/2016 - Use appointment object duration as session variables may not be updated if an adjust worked has taken place
3359 if (aptObj.get_attributes().getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_SHIFT_DURATION)) {
3360 hourSpan = (parseFloat(aptObj.get_attributes().getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_SHIFT_DURATION)) / 60);
3361 }
3362
3363 //VJF 02/25/2016 - Only update status bar totals for shifts in the selected period or when the period is not defined.
3364 if (isDateInActiveOrNotDefinedPeriod(aptObj.get_start())) {
3365 UpdateStatusBarTotals(provId, false, hourSpan, arrShiftGroupIds, bUpdateTotalsColumns, bUpdateGlobalTotals);
3366 }
3367 }
3368
3369 //Find the old selected shift PK and replace it with the updated one with a providerId at the end
3370 //to reflect an assigned shift (1424721414|72|-1|20160126|1147)
3371 var arOldHdnShiftKeyValues = GetSelectedShiftPrimaryKeys();
3372 for (var i = 0; i < arOldHdnShiftKeyValues.length; i++) {
3373 if (arOldHdnShiftKeyValues[i].indexOf(shiftKey) !== -1) {
3374 var shiftAssignment = shiftKey + "|" + provId;
3375 arOldHdnShiftKeyValues[i] = shiftAssignment;
3376 break;
3377 }
3378 }
3379
3380 //JF 9/21/2017 - Apply "assigned" tag to shift
3381 setManuallyAssigned(aptDomId, shift.IsManuallyAssigned);
3382
3383 //Store the updated shift primary keys in the #hdnSelectedShiftPrimaryKeys with the format:
3384 //1424721350|72|-1|20160127|1147;1424721414|72|-1|20160126|1147;
3385 $("#hdnSelectedShiftPrimaryKeys").val(arOldHdnShiftKeyValues.join(";") + ";");
3386}
3387
3388//VJF 04/04/2017 - Insert an appointment on the calendar.
3389//Html is generated and an SchedulerAppointment object is created and inserted into the RadScheduler appointments array.
3390//The SchedulerAppointment contains details about the shift, in attributes, as well as the context menu id, dom details etc.
3391//Parameters-
3392// shiftInfo - object with all the details on the shift
3393// tmpltAptDomId - appointment dom id that will be used to model the html and appointment after
3394// posAptDomId - appointment dom id that while provide a positional reference on where to insert the new html/dom object
3395// bInsertAfter - whether to insert the new appointment before or after the positional appointment
3396//TODO: The shiftInfo object will need to be extended to include additional values, such as -
3397// - start date time
3398// - end date time
3399// - shift status
3400// - is shift counted flag
3401// - view for shift name formatting
3402// - addtl flags for css: is adjust worked, exception, expired, locked etc
3403function insertAppointment(shiftInfo, tmpltAptDomId, posAptDomId, bInsertAfter) {
3404
3405 //Since there can be multiple RadSchedulers that make up a calendar.
3406 //Get the schedulers that contain the template and positional appointments.
3407 var tmpltScheduler = GetSchedulerByAptDomId(tmpltAptDomId);
3408 var posScheduler = GetSchedulerByAptDomId(posAptDomId);
3409
3410 var bIsRemoval = (shiftInfo.isRemoval == "true");
3411
3412 if (!(tmpltScheduler && posScheduler))
3413 return null;
3414
3415 //Get the SchedulerAppointment objects corresponding to the template and positional dom ids
3416 var tmpltApt = GetAptByDomId(tmpltScheduler, tmpltAptDomId);
3417 var posApt = GetAptByDomId(posScheduler, posAptDomId);
3418
3419 if (!(tmpltApt && posApt))
3420 return null;
3421
3422 //Create a unique apt id by getting the id of the last apt on the positional schedule and incrementing by 1
3423 var newAptId = posScheduler.get_appointments().getAppointment(posScheduler.get_appointments().get_count() - 1).get_id() + 1;
3424
3425 //-----------DOM Appointment element and wrapper divs---------------
3426
3427 //Get references to the template html apt and apt wrapper elements to copy.
3428 //The wrapper element, .rsWrap, is the parent div of the .rsApt div.
3429 var tmpltElement = document.getElementById(tmpltApt.get_element().id);
3430 var tmpltElementWrapper = tmpltElement.parentElement;
3431
3432 //Deep copy the template appointment's wrapper element
3433 var newElementWrapper = $(tmpltElementWrapper).clone(true);
3434
3435 //Get a reference to the new appointment element within the wrapper
3436 //Assigned: <div id="14_0" title="7AM-3:13PM Armacost" class="rsApt evtLocation otherProv" style="height:28px;width:100%;">
3437 //Open: <div id="15_0" title="11PM-7AM" class="rsApt evtLocation openShift expired " style="height:28px;width:100%;">
3438
3439 var newElement = $(".rsApt", newElementWrapper[0])[0];
3440
3441 //The apt id and dom id need to be consistent with the existing format, id = 55 dom id = ..._55_0
3442 var newAptElemId = CreateAptDomId(posApt.get_element().id, newAptId);
3443
3444 newElement.id = newAptElemId;
3445 newElement.title = shiftInfo.Tooltip;
3446
3447 //-----------Appointment Subject---------------
3448 //An appointment subject contains two div elements: Shift and Assignment
3449 //<div class="evtLocShift adjWorked inactive"> Morn 7AM-3:09PM</div>
3450 //<div id="1193" class="evtProv">Armacost</div>
3451
3452 //Shift Name - <div class="evtLocShift adjWorked inactive exception">Morn</div>
3453 var shiftElem = $(".evtLocShift", newElement);
3454 if (shiftElem.length) {
3455 shiftElem[0].innerHTML = shiftInfo.ShiftName + " " + shiftInfo.ShiftTime;
3456 }
3457
3458 //Assignment - Prov Name and ID - <div id="53" class="evtProv">S.Lucas</div>
3459 var assigmentElem = $(".evtProv", newElement);
3460 if (assigmentElem.length) {
3461 if (shiftInfo.ProviderId != "-1") {
3462 assigmentElem[0].innerHTML = FindProviderNameFromGrdById(shiftInfo.ProviderId);
3463 assigmentElem[0].id = shiftInfo.ProviderId;
3464 } else {
3465 assigmentElem[0].innerHTML = shiftInfo.OpenShiftMnemonic;
3466 assigmentElem.removeClass("evtProv").addClass("evtOpen");
3467 $(newElement).removeClass("evtBrokenRuleCritical").removeClass("evtBrokenRuleWarning");
3468 }
3469 } else {
3470
3471 //No Assignment - <div class="evtOpen">Open</div>
3472 assigmentElem = $(".evtOpen", newElement);
3473 if (assigmentElem.length) {
3474 assigmentElem[0].innerHTML = shiftInfo.OpenShiftMnemonic;
3475 $(newElement).removeClass("evtBrokenRuleCritical").removeClass("evtBrokenRuleWarning");
3476 }
3477
3478 }
3479
3480 if (shiftInfo.isAdjustWorked == "false") {
3481
3482 if (!shiftElem.hasClass("exception") && !bIsRemoval) {
3483 shiftElem.addClass("exception");
3484 } else if (shiftElem.hasClass("exception") && bIsRemoval) {
3485 shiftElem.removeClass("exception");
3486 }
3487
3488 } else {
3489
3490 //TJF 3/4/2016 - Handle adjust worked
3491 if (!shiftElem.hasClass("adjWorked")) {
3492 shiftElem.addClass("adjWorked");
3493 }
3494 }
3495
3496 //Handle manually assigned shifts
3497 var chkIdentifyManuallyAssigned = document.getElementById("chkIdentifyManuallyAssigned");
3498 var bIsManuallyAssigned = (shiftInfo.isShiftLocked == "true");
3499
3500 if (chkIdentifyManuallyAssigned && bIsManuallyAssigned) {
3501 if (chkIdentifyManuallyAssigned.checked) {
3502 shiftElem.addClass("manual-show");
3503 } else {
3504 shiftElem.addClass("manual-hide");
3505 }
3506 }
3507
3508 //-----------Scheduler Appointment object---------------
3509
3510 //Create a new SchedulerAppointment object based on the template apt
3511 //There may not be much difference between cloning and starting with a new object since most values are changed
3512 var newApt = tmpltApt.clone();
3513
3514 //Set the apt div element as the only item in dom element array. apt.get_element() will get the first item.
3515 newApt._domElements = [newElement];
3516
3517 //var newApt = new Telerik.Web.UI.SchedulerAppointment();
3518 var start = new Date(shiftInfo.StartDate);
3519 var end = new Date(shiftInfo.EndDate);
3520
3521 newApt._id = newAptId; //Unique id we assign
3522 newApt._internalID = newApt.get_element().id; //Not sure how this is used
3523
3524 newApt.set_start(start);
3525 newApt.set_end(end);
3526 newApt.set_toolTip(shiftInfo.Tooltip);
3527
3528 //Set appropriate context menu and id
3529 if (shiftInfo.ProviderId == "-1") {
3530 newApt.set_contextMenuID(WebConstants.CM_ADMIN_SHIFT_OPEN_ID);
3531 newApt._contextMenu = $find(posScheduler._resolveContextMenuID(WebConstants.CM_ADMIN_SHIFT_OPEN_ID));
3532 } else {
3533 newApt.set_contextMenuID(WebConstants.CM_ADMIN_SHIFT_ID);
3534 newApt._contextMenu = $find(posScheduler._resolveContextMenuID(WebConstants.CM_ADMIN_SHIFT_ID))
3535 }
3536
3537 //TJF 6/26/2017 - Get duration minutes for shift
3538 var diffMs = (end - start);
3539
3540 var diffMins = (diffMs / 1000) / 60;
3541
3542 //TJF 4/17/2018 - Allow overriding duration for cases where duration is not calculated by end - start
3543 var nOverrideDuration = 0;
3544 if (shiftInfo.ProviderHoursChange != null) {
3545 nOverrideDuration = parseFloat(shiftInfo.ProviderHoursChange);
3546 diffMins = nOverrideDuration * 60;
3547 }
3548
3549 //Get if shift is counted
3550 var nIsShiftCounted = 1;
3551 var bIsShiftCounted = false;
3552 if (shiftInfo.isShiftCounted == "true") {
3553 nIsShiftCounted = 0;
3554 bIsShiftCounted = true;
3555 }
3556
3557 //Add the shift attributes
3558 var attributes = newApt.get_attributes();
3559 attributes.setAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_APPT_TYPE, EventType.Shift);
3560 attributes.setAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_APPT_ID, shiftInfo.ShiftKey);
3561 attributes.setAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_PROV_ID, shiftInfo.ProviderId);
3562 attributes.setAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_SHIFT_DURATION, diffMins); //Shift Minutes
3563 attributes.setAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_COUNT_SHIFT, nIsShiftCounted); //Is shift counted?
3564 attributes.setAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_SHIFT_STATUS_ID, shiftInfo.ShiftStatusId);
3565 attributes.setAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_TEMPLATE_ASSIGNMENT, shiftInfo.IsTemplateAssignment); //VJF 12/14/2017 - Shift assigned through template generation
3566
3567 //Insert the new Scheduler Appointment
3568 posScheduler.get_appointments().add(newApt);
3569
3570 //Insert the new HTML
3571 //Get references to the positional html apt and apt wrapper elements to determine where to insert new wrapper element
3572 var posElement = document.getElementById(posApt.get_element().id);
3573 var posElementWrapper = posElement.parentElement;
3574
3575 if (bInsertAfter) {
3576 //Insert After
3577 posElementWrapper.parentNode.insertBefore(newElementWrapper[0], posElementWrapper.nextSibling);
3578
3579 } else {
3580 //Insert Before
3581 posElementWrapper.parentNode.insertBefore(newElementWrapper[0], posElementWrapper);
3582 }
3583
3584 SortShiftDay($("#" + newAptId + "_0"), shiftInfo.DayIndex);
3585
3586 var bHasAssignedProvider = (shiftInfo.ProviderId != "-1");
3587 if (isDateInActiveOrNotDefinedPeriod(newApt.get_start())) {
3588
3589 if (bIsShiftCounted) {
3590 UpdateGlobalShiftCounts(false, bHasAssignedProvider, true, diffMins / 60);
3591 }
3592
3593 if (bHasAssignedProvider) {
3594
3595 var bUpdateTotalsColumns = bIsShiftCounted;
3596 var hourSpan = 0;
3597 //TJF 11/1/2016 - Use appointment object duration as session variables may not be updated if an adjust worked has taken place
3598 if (newApt.get_attributes().getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_SHIFT_DURATION)) {
3599 hourSpan = ((parseFloat(newApt.get_attributes().getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_SHIFT_DURATION)) / 60));
3600 }
3601
3602 var arrShiftGroupIds = shiftInfo.ShiftGroupIds.split(",");
3603 UpdateStatusBarTotals(shiftInfo.ProviderId, false, hourSpan, arrShiftGroupIds, bUpdateTotalsColumns, false);
3604 }
3605
3606 }
3607
3608 return false;
3609}
3610
3611//VJF 04/04/2017 - Generate an appointment dom id based of the format of a template id passed in and numerical id.
3612//Template of rsScheduler2_55_0 and id of 66 return rsScheduler2_66_0
3613function CreateAptDomId(tmpltDomId, newId) {
3614
3615 //The Appointment Dom Id can be of the format 55_0 or rsScheduler2_55_0. Replace the appointment Id with the new id, ie: 55.
3616 var aptId;
3617 var count = (tmpltDomId.match(/_/g) || []).length; //Count the number of underscores
3618 if (count > 1) {
3619 //Extract the Dom id between the underscores
3620 aptId = tmpltDomId.substring(tmpltDomId.indexOf("_") + 1, tmpltDomId.lastIndexOf("_"));
3621 } else {
3622 //Extract the Dom id up to the last 2 characters
3623 aptId = tmpltDomId.slice(0, -2);
3624 }
3625 return tmpltDomId.replace(aptId + "_", newId + "_");
3626}
3627
3628//VJF 04/04/2017 - Get the RadScheduler dom object that contains an appointment id
3629function GetSchedulerByAptDomId(aptDomId) {
3630
3631 //Get the id of the RadScheduler that contains the appointment dom id
3632 var schedulerDomId = $("#" + aptDomId).closest(".RadScheduler").attr('id');
3633 var scheduler = $find(schedulerDomId);
3634
3635 return scheduler;
3636}
3637
3638//VJF 04/04/2017 - Find a SchedulerAppointment object by dom id among a RadScheduler's appointments
3639function GetAptByDomId(scheduler, aptDomId) {
3640
3641 var apt = null;
3642 var apts = scheduler.get_appointments();
3643 for (var i = 0; i < apts.get_count() ; i++) {
3644 if (apts.getAppointment(i).get_element().id === aptDomId) {
3645 apt = apts.getAppointment(i);
3646 break;
3647 }
3648 }
3649
3650 return apt;
3651}
3652
3653
3654//VJF 11/03/2016 - Determine whether a date falls with the date range the user is interested in.
3655//The counting date range on the page identifies the valid range.
3656function isDateInActiveOrNotDefinedPeriod(dtDate) {
3657
3658 var isInRange = false;
3659 try {
3660 var nViewId = document.forms.frmScheduler.hdnCalView.value;
3661 switch (parseInt(nViewId)) {
3662 case SchedulerViewType.DayView:
3663 case SchedulerViewType.WeekView:
3664 //Always true because only countable days are displayed in these views
3665 isInRange = true;
3666 break;
3667 case SchedulerViewType.MonthView:
3668 case SchedulerViewType.TimelineView:
3669 case SchedulerViewType.AgendaView:
3670
3671 var sStartDate = document.forms.frmScheduler.hdnCountingStartDate.value;
3672 var sEndDate = document.forms.frmScheduler.hdnCountingEndDate.value;
3673
3674 if (sStartDate.length > 0 && sEndDate.length > 0) {
3675 var dtStartDate = new Date(sStartDate);
3676 var dtEndDate = new Date(sEndDate); //This should end at the very end of the day with a time of 23:59:59
3677
3678 //Check for date within period
3679 if (dtStartDate <= dtDate && dtEndDate >= dtDate) {
3680 //Date falls within Active/Selected period
3681 isInRange = true;
3682 } else {
3683 //Date is not in active period
3684 isInRange = false;
3685 }
3686 }
3687 break;
3688 default:
3689
3690 }
3691
3692 } catch (e) {
3693 isInRange = false;
3694 }
3695
3696 return isInRange;
3697}
3698
3699//TJF 2/19/2016 - Updates the status bar after an add or remove
3700// Updates both the affected provider and the open and filled counts
3701function UpdateStatusBarTotals(provId, bIsRemoval, hoursSpan, arrShiftGroupIds, bUpdateTotalsColumns, bUpdateGlobalTotals) {
3702 var rowSelectedProv = FindDataItemById(provId);
3703 if (rowSelectedProv == null) {
3704 return;
3705 }
3706
3707 var rowOpenShift = FindDataItemById(0);
3708
3709 var masterTable = GetGrdProvInfoMasterTableView();
3710 var totalHours = parseFloat(masterTable.getCellByColumnUniqueName(rowSelectedProv, "ProviderHours").innerHTML);
3711 var totalShifts = parseInt(masterTable.getCellByColumnUniqueName(rowSelectedProv, "ProviderShifts").innerHTML);
3712
3713 var openHours = parseFloat(masterTable.getCellByColumnUniqueName(rowOpenShift, "ProviderHours").innerHTML);
3714 var globalTotalOpens = parseFloat(masterTable.getCellByColumnUniqueName(rowOpenShift, "ProviderShifts").innerHTML);
3715
3716 //TJF 10/18/2016 - Get counts for open slots
3717 var arrOpenSlotCounts = [];
3718 for (var i = 0; i < gShiftGroupCountColumnNames.length; i++) {
3719 var sColName = gShiftGroupCountColumnNames[i];
3720 arrOpenSlotCounts.push(parseInt(masterTable.getCellByColumnUniqueName(rowOpenShift, sColName).innerHTML));
3721 }
3722
3723 //TJF 10/18/2016 - Get counts for non-open for prov
3724 var arrFilledSlotCounts = [];
3725 for (var i = 0; i < gShiftGroupCountColumnNames.length; i++) {
3726 var sColName = gShiftGroupCountColumnNames[i];
3727 arrFilledSlotCounts.push(parseInt(masterTable.getCellByColumnUniqueName(rowSelectedProv, sColName).innerHTML));
3728 }
3729
3730
3731 //TJF 2/22/2016 - Determine total number of shifts for percent filled calculation
3732 var grid = GetGrdProvInfo();
3733 var grdProvId = "#" + grid.get_id();
3734
3735 if (bIsRemoval) {
3736 totalHours -= parseFloat(hoursSpan);
3737 totalShifts -= 1;
3738 globalTotalOpens += 1;
3739 openHours += parseFloat(hoursSpan);
3740
3741 //TJF 11/23/2016 - Round hours counts to 1 decimal place
3742 totalHours = RoundValueToDecimalPlace(totalHours, 1);
3743 openHours = RoundValueToDecimalPlace(openHours, 1);
3744
3745 //TJF 2/26/2016 - This can happen due to rounding, so just set totalhours to 0 to fix it
3746 if (totalHours < 0 || totalShifts == 0) {
3747 totalHours = 0;
3748 }
3749
3750 //TJF 10/28/2016 - Nothing to update if counts are not loaded
3751 if (gShiftGroupCountsLoaded) {
3752
3753 for (var k = 0; k < arrShiftGroupIds.length; k++) {
3754 var sgId = arrShiftGroupIds[k];
3755 var idAndLocId = sgId.split("|");
3756
3757 var shiftGroupId = idAndLocId[0];
3758 var locId = idAndLocId[1];
3759
3760 var bIsSystem = (locId == "-1");
3761
3762 //TJF 10/28/2016 - Init if provider does not already have the shift group
3763 initShiftGroupsByProvIdColumn(0, shiftGroupId);
3764 initShiftGroupsByProvIdColumn(provId, shiftGroupId);
3765
3766 var newOpenCountShifts = gShiftGroupsByProvId[0][shiftGroupId][bIsSystem ? "System" : "Local"].shifts;
3767 var newProvCountShifts = gShiftGroupsByProvId[provId][shiftGroupId][bIsSystem ? "System" : "Local"].shifts;
3768
3769 var newOpenCountHours = gShiftGroupsByProvId[0][shiftGroupId][bIsSystem ? "System" : "Local"].hours;
3770 var newProvCountHours = gShiftGroupsByProvId[provId][shiftGroupId][bIsSystem ? "System" : "Local"].hours;
3771
3772 //TJF 11/9/2016 - Modify hours and shifts for updated counts in columns
3773 newProvCountHours -= parseFloat(hoursSpan);
3774 newOpenCountHours += parseFloat(hoursSpan);
3775
3776 newProvCountShifts -= 1;
3777 newOpenCountShifts += 1;
3778
3779 //Update provider
3780 gShiftGroupsByProvId[provId][shiftGroupId][bIsSystem ? "System" : "Local"].shifts = newProvCountShifts;
3781 //Update opens
3782 gShiftGroupsByProvId[0][shiftGroupId][bIsSystem ? "System" : "Local"].shifts = newOpenCountShifts;
3783
3784 gShiftGroupsByProvId[0][shiftGroupId][bIsSystem ? "System" : "Local"].hours = newOpenCountHours;
3785 gShiftGroupsByProvId[provId][shiftGroupId][bIsSystem ? "System" : "Local"].hours = newProvCountHours;
3786 }
3787
3788 //8/17/2016 - Update shift group totals
3789 //Update counts for provider columns and open columns on provgrid
3790 for (var i = 0; i < arrFilledSlotCounts.length; i++) {
3791 var bIncludes = false;
3792 //Check for inclusion in displayed primary keys
3793 for (var j = 0; j < arrShiftGroupIds.length; j++) {
3794 var sgId = arrShiftGroupIds[j];
3795 var idAndLocId = sgId.split("|");
3796
3797 if (gCurrentDisplayedPrimaryKeys[i] && gCurrentDisplayedPrimaryKeys[i].id.toString() == idAndLocId[0]) {
3798 bIncludes = true;
3799 break;
3800 }
3801 }
3802
3803 if (gCurrentDisplayedPrimaryKeys[i] && bIncludes) {
3804 arrFilledSlotCounts[i] = gShiftGroupsByProvId[provId][gCurrentDisplayedPrimaryKeys[i].id][gCurrentDisplayedPrimaryKeys[i].isSystem == "System" || gCurrentDisplayedPrimaryKeys[i].isSystem == true ? "System" : "Local"];
3805 arrOpenSlotCounts[i] = gShiftGroupsByProvId[0][gCurrentDisplayedPrimaryKeys[i].id][gCurrentDisplayedPrimaryKeys[i].isSystem == "System" || gCurrentDisplayedPrimaryKeys[i].isSystem == true ? "System" : "Local"];
3806 }
3807 }
3808 }
3809
3810
3811 } else {
3812 totalHours += parseFloat(hoursSpan);
3813 totalShifts += 1;
3814 globalTotalOpens -= 1;
3815 openHours -= parseFloat(hoursSpan);
3816
3817 //TJF 11/23/2016 - Round hours counts to 1 decimal place
3818 totalHours = RoundValueToDecimalPlace(totalHours, 1);
3819 openHours = RoundValueToDecimalPlace(openHours, 1);
3820
3821
3822 //TJF 10/28/2016 - Nothing to update if counts are not loaded
3823 if (gShiftGroupCountsLoaded) {
3824
3825 //8/17/2016 - Update shift group totals
3826 for (var k = 0; k < arrShiftGroupIds.length; k++) {
3827 var sgId = arrShiftGroupIds[k];
3828 var idAndLocId = sgId.split("|");
3829
3830 var shiftGroupId = idAndLocId[0];
3831 var locId = idAndLocId[1];
3832
3833 var bIsSystem = (locId == "-1");
3834
3835 //TJF 10/28/2016 - Init if provider does not already have the shift group
3836 initShiftGroupsByProvIdColumn(0, shiftGroupId);
3837 initShiftGroupsByProvIdColumn(provId, shiftGroupId);
3838
3839 var newOpenCountShifts = gShiftGroupsByProvId[0][shiftGroupId][bIsSystem ? "System" : "Local"].shifts;
3840 var newProvCountShifts = gShiftGroupsByProvId[provId][shiftGroupId][bIsSystem ? "System" : "Local"].shifts;
3841
3842 var newOpenCountHours = gShiftGroupsByProvId[0][shiftGroupId][bIsSystem ? "System" : "Local"].hours;
3843 var newProvCountHours = gShiftGroupsByProvId[provId][shiftGroupId][bIsSystem ? "System" : "Local"].hours;
3844
3845 //TJF 11/9/2016 - Modify hours and shifts for updated counts in columns
3846 newProvCountHours += parseFloat(hoursSpan);
3847 newOpenCountHours -= parseFloat(hoursSpan);
3848
3849 newProvCountShifts += 1;
3850 newOpenCountShifts -= 1;
3851
3852 //Update provider
3853 gShiftGroupsByProvId[provId][shiftGroupId][bIsSystem ? "System" : "Local"].shifts = newProvCountShifts;
3854 //Update opens
3855 gShiftGroupsByProvId[0][shiftGroupId][bIsSystem ? "System" : "Local"].shifts = newOpenCountShifts;
3856
3857 gShiftGroupsByProvId[0][shiftGroupId][bIsSystem ? "System" : "Local"].hours = newOpenCountHours;
3858 gShiftGroupsByProvId[provId][shiftGroupId][bIsSystem ? "System" : "Local"].hours = newProvCountHours;
3859
3860 }
3861
3862 //Update counts for provider columns and open columns on provgrid
3863 for (var i = 0; i < arrFilledSlotCounts.length; i++) {
3864 var bIncludes = false;
3865 //Check for inclusion in displayed primary keys
3866 for (var j = 0; j < arrShiftGroupIds.length; j++) {
3867 var sgId = arrShiftGroupIds[j];
3868 var idAndLocId = sgId.split("|");
3869
3870 if (gCurrentDisplayedPrimaryKeys[i] && gCurrentDisplayedPrimaryKeys[i].id.toString() == idAndLocId[0]) {
3871 bIncludes = true;
3872 break;
3873 }
3874 }
3875
3876 if (gCurrentDisplayedPrimaryKeys[i] && bIncludes) {
3877 arrFilledSlotCounts[i] = gShiftGroupsByProvId[provId][gCurrentDisplayedPrimaryKeys[i].id][gCurrentDisplayedPrimaryKeys[i].isSystem == "System" || gCurrentDisplayedPrimaryKeys[i].isSystem == true ? "System" : "Local"];
3878 arrOpenSlotCounts[i] = gShiftGroupsByProvId[0][gCurrentDisplayedPrimaryKeys[i].id][gCurrentDisplayedPrimaryKeys[i].isSystem == "System" || gCurrentDisplayedPrimaryKeys[i].isSystem == true ? "System" : "Local"];
3879
3880 }
3881 }
3882 }
3883 }
3884
3885 $(grdProvId + " .lblTotalShifts").html(SumTotalShifts().toString());
3886 var globalTotalShifts = $(grdProvId + " .lblTotalShifts").text();
3887 var globalTotalFilledShifts = globalTotalShifts - globalTotalOpens;
3888 percentFilled = parseFloat((globalTotalFilledShifts / globalTotalShifts) * 100).toFixed(0);
3889
3890 if (bUpdateTotalsColumns) {
3891 masterTable.getCellByColumnUniqueName(rowSelectedProv, "ProviderHours").innerHTML = (parseFloat(totalHours));
3892 masterTable.getCellByColumnUniqueName(rowSelectedProv, "ProviderShifts").innerHTML = totalShifts;
3893
3894 if (bUpdateGlobalTotals) {
3895 masterTable.getCellByColumnUniqueName(rowOpenShift, "ProviderHours").innerHTML = (parseFloat(openHours));//TJF 8/5/2016 - Now tracking open hours
3896 masterTable.getCellByColumnUniqueName(rowOpenShift, "ProviderShifts").innerHTML = globalTotalOpens;
3897 //TJF 6/16/2016 - Update percent filled
3898 masterTable.getCellByColumnUniqueName(rowOpenShift, "FilledPercent").innerHTML = percentFilled;
3899 }
3900 }
3901
3902
3903 //TJF 10/18/2016 - Set new columns values
3904 //TJF 10/28/2016 - Nothing to update if counts are not loaded
3905 if (gShiftGroupCountsLoaded) {
3906 var bIsHours = isUnitsHoursChecked();
3907 var bIsShifts = isUnitsShiftsChecked();
3908
3909 //TJF 11/9/2016 - Edit updated columns
3910 for (var i = 0; i < gShiftGroupCountColumnNames.length; i++) {
3911 var sColName = gShiftGroupCountColumnNames[i];
3912 if (!arrFilledSlotCounts[i] || !arrOpenSlotCounts[i] || arrFilledSlotCounts[i].shifts == undefined) {
3913 continue;
3914 }
3915
3916 var sOpenColString = "";
3917 var sProvColString = "";
3918 if (bIsHours && bIsShifts) {
3919 sOpenColString = arrOpenSlotCounts[i].shifts + "/" + RoundValueToDecimalPlace(arrOpenSlotCounts[i].hours, 1);
3920 sProvColString = arrFilledSlotCounts[i].shifts + "/" + RoundValueToDecimalPlace(arrFilledSlotCounts[i].hours, 1);
3921 } else if (bIsHours) {
3922 sOpenColString = RoundValueToDecimalPlace(arrOpenSlotCounts[i].hours, 1);
3923 sProvColString = RoundValueToDecimalPlace(arrFilledSlotCounts[i].hours, 1);
3924 } else {
3925 sOpenColString = arrOpenSlotCounts[i].shifts;
3926 sProvColString = arrFilledSlotCounts[i].shifts
3927 }
3928
3929 masterTable.getCellByColumnUniqueName(rowOpenShift, sColName).innerHTML = sOpenColString;
3930 masterTable.getCellByColumnUniqueName(rowSelectedProv, sColName).innerHTML = sProvColString;
3931 }
3932 }
3933
3934 //Update the provider status info bar provider
3935 var chkSelected = FindCheckboxFromGridById(provId);
3936 selectedProviderCheckedOnGrid(chkSelected);
3937
3938 //Get the totals of the Provider Shifts and Providers Hours to be displayed on the footer
3939 $(grdProvId + " .lblTotalShifts").html(SumTotalShifts().toString());
3940 $(grdProvId + " .lblTotalHours").html(SumTotalHours());
3941
3942
3943 //TJF 10/18/2016 - Set totals
3944 for (var i = 0; i < gShiftGroupCountColumnNames.length; i++) {
3945 var sName = gShiftGroupCountColumnNames[i];
3946 $(grdProvId + " .lbl" + sName).html(sumTotalShiftGroups(sName).toString());
3947 }
3948
3949}
3950
3951//TJF 12/2/2016 - Returns a float rounded to one decimal place
3952function RoundValueToDecimalPlace(value, decimals) {
3953 var rounded = Number(Math.round(value + 'e' + decimals) + 'e-' + decimals);
3954 if (isNaN(rounded)) {
3955 rounded = 0;
3956 }
3957 return rounded;
3958}
3959
3960//NMM 1/07/2016 - Takes in an appointment DOM id to retrieve the context menu ID("cmAdminOpen/cmAdminShift") from the correct scheduler instance.
3961//Used for Checking if selected shift is open for multiple remove/assign providers. Also for renaming the context menu id after shift
3962//assignment/removal on the client side. This returns an appointment object.
3963function GetAptObjByAptDomIdFromSelectedRadScheduler(aptDomId) {
3964 var aptId;
3965
3966 //Get the correct radscheduler if there are more than 1, based on the selected appointment
3967 var selectedRadScheduler = $("#" + aptDomId).closest(".RadScheduler").attr('id');
3968 var scheduler = $find(selectedRadScheduler);
3969
3970 //The Appointment Dom Id can be of the format 55_0 or rsScheduler2_55_0. Extract the appointment Id, ie: 55.
3971 var count = (aptDomId.match(/_/g) || []).length;
3972 if (count > 1) {
3973 //Extract the Dom id between the underscores
3974 aptId = aptDomId.substring(aptDomId.indexOf("_") + 1, aptDomId.lastIndexOf("_"));
3975 } else {
3976 //Extract the Dom id up to the last 2 characters
3977 aptId = aptDomId.slice(0, -2);
3978 //aptId = parseInt(aptId, 10);
3979 //aptId = aptId + 1;
3980 }
3981
3982 return scheduler.get_appointments().findByID(aptId);
3983}
3984
3985//TJF 12/4/2017 - Use the shift key to get the apt object
3986function GetAptDomIdByShiftKey(sShiftPrimaryKey) {
3987 var sAptDomId = "";
3988 $(".RadScheduler").each(function (index) {
3989
3990 var scheduler = $find($(this).attr('id'));
3991
3992 for (var i = 0; i < scheduler.get_appointments().get_count() ; i++) {
3993 var apt = scheduler.get_appointments().getAppointment(i);
3994 var attribs = apt.get_attributes();
3995 var sSelectedShiftPrimaryKey = attribs.getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_APPT_ID);
3996 if (sShiftPrimaryKey == sSelectedShiftPrimaryKey) {
3997 sAptDomId = apt.get_element().id;
3998 break
3999 }
4000
4001 }
4002
4003 if (sAptDomId != "") {
4004 return false;
4005 }
4006 });
4007
4008 return sAptDomId;
4009
4010}
4011
4012//NMM 1/6/2016 - Combines the selected shift primary keys and apt DOM id's, shift^domId, from the AdminCalendar.aspx. This combination
4013//is used by the AdminRemove/AdminAssign to Update the UI to show the changed removal/assignment without making a call back to the server.
4014function GetCombinedShiftPrimaryKeysAndAptDomIds(bExcludeTemplateAssignments) {
4015 var arSpk = GetSelectedShiftPrimaryKeys();
4016 var arAptDom = GetSelectedAptDomIds();
4017 var arShiftPrimaryKeyWithAptDom = [];
4018
4019 if (typeof bExcludeTemplateAssignments != 'boolean') bExcludeTemplateAssignments = false;
4020
4021 for (i = 0; i < arSpk.length; i++) {
4022
4023 if (bExcludeTemplateAssignments && GetAptObjByAptDomIdFromSelectedRadScheduler(arAptDom[i]).get_attributes().getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_TEMPLATE_ASSIGNMENT) === "1")
4024 continue;
4025
4026 arShiftPrimaryKeyWithAptDom.push(arSpk[i] + "^" + arAptDom[i]);
4027 }
4028
4029 //Removes the '#' signs from the apt DOM id's, as it cannot be passed in through the url request parameters
4030 var strShiftPrimaryKeyWithAptDomId = arShiftPrimaryKeyWithAptDom.toString().replace(/#/g, "");
4031 return strShiftPrimaryKeyWithAptDomId;
4032}
4033
4034//NMM 1/6/2015 - Extracts the Date string from the shift primary key and returns it as a date object
4035function GetShiftDateFromShiftPrimaryKey(shiftKey) {
4036 var delimiter = "|";
4037 var start = 3;
4038 var tokens;
4039 var result;
4040
4041 tokens = shiftKey.split(delimiter).slice(start);
4042 result = tokens.join(delimiter);
4043
4044 var shiftDate = result.slice(0, 4) + "/" + result.slice(4, 6) + "/" + result.slice(6, 8);
4045 return new Date(shiftDate);
4046}
4047
4048//*************************
4049//PROVIDERS LIST ON GRID
4050//*************************
4051
4052//TJF 7/28/2016 - This uses jquery to shorten the dragged row to just the name column
4053function grdProv_dragStarted(sender, args) {
4054 selectedProviderChangedOnGrid(sender, args);
4055 setTimeout(function () {
4056 var masterTable = GetGrdProvInfoMasterTableView();
4057 var draggedRow = sender._draggedRow;
4058 var $draggedRow = $(draggedRow);
4059 $draggedRow.find("table").hide();
4060 var name = masterTable.getCellByColumnUniqueName(args.get_gridDataItem(), "ProviderName").innerHTML;
4061 $draggedRow.append(getDragTemplate(name));
4062 $draggedRow.width(getTextWidth(name) + 60);
4063 });
4064}
4065
4066//TJF 7/28/2016 -
4067function getDragTemplate(name) {
4068 return $("<span style='padding-left:25px; padding-right: 25px; border: 1px solid black; font: 14px/1.42857 \"Helvetica Neue\",Helvetica,Arial,sans-serif;'>" + name + "</span>");
4069}
4070
4071
4072//TJF 7/5/2016 - Reset boolean to save color on reload
4073function grdProv_OnGridCreating(sender, eventArgs) {
4074 gGrdProvFinishedLoad = false;
4075}
4076
4077
4078//TJF 8/23/2016 - Object used as datasource for ShiftGroupListGrid
4079var ShiftGroupRowData =
4080 {
4081 LocId: null,
4082 ID: null,
4083 ShiftGroupName: null,
4084 IsSystem: null,
4085
4086 create: function (treeEntries) {
4087 var obj = new Object();
4088 obj.LocId = (treeEntries.Entries[0] && !treeEntries.IsSystem) ? treeEntries.Entries[0].LocId : -1;
4089 obj.ID = treeEntries.ShiftGroupID;
4090
4091 //TJF 11/11/2016 - Remove (system) from shift group name
4092 var sName = treeEntries.ShiftGroupName;
4093 if (sName.indexOf("(system)") > -1) {
4094 sName = sName.slice(0, sName.indexOf("(system)"));
4095 }
4096
4097 obj.ShiftGroupName = sName;
4098 obj.IsSystem = treeEntries.IsSystem ? "System" : "Location";
4099 return obj;
4100 }
4101 };
4102
4103//Enum to track which coloring system is being applied
4104var COLORCONTEXT = {
4105 ShiftGroup: 0,
4106 PractType: 1,
4107 ShiftStatus: 2
4108};
4109
4110//TJF 8/23/2016 - Used to populate shift group list on admincalendar. It is of the form [provId][shiftGroupId]["System"|"Local"].
4111//This is a count of all shifts groups a provider is assigned to and is built in the grdProv_OnGridCreated function
4112var gShiftGroupsByProvId = [];
4113var gColorContext = COLORCONTEXT.ShiftGroup;
4114var gGrdProvFinishedLoad = false;
4115
4116function get_gGrdProvFinishedLoad() {
4117 return gGrdProvFinishedLoad;
4118}
4119
4120//TJF 7/5/2016 - Restore checked items and colors
4121var rgShiftGroupListDatasource = [];
4122//"ShiftGroupSlot0", "ShiftGroupSlot1", "ShiftGroupSlot2", ect.
4123var gShiftGroupCountColumnNames = [];
4124var gShiftGroupTargetColumnNames = []; //TJF 9/11/2017 - Column names for shift group targets
4125//TJF 11/10/2016 - Number of columns in provGrid that are always present(if new ones are added this must be changed)
4126//TJF 10/26/2017 - Changed to 12 due to addition of SPP column
4127//TJF 1/4/2018 - Changed to 13 due to addition of OffRequestDeduction column
4128var nDefaultProviderCols = 13;
4129var nColumnsAfterDynamicColumns = 3; //TJF 1/4/2018 - Number of columns following the dynamic columns
4130function grdProv_OnGridCreated(sender, eventArgs) {
4131 var masterTable = sender.get_masterTableView();
4132 var bShowTargets = IsShowTargetsChecked();
4133
4134 //TJF 9/11/2017 - Divide by 2 because of target columns
4135 var nDynamicColumns = getDynamicColumnsCount();
4136
4137 gShiftGroupCountColumnNames = [];
4138 gShiftGroupTargetColumnNames = [];
4139 gCurrentGridSize = null; //TJF 11/11/2016 - Reset columns size
4140
4141 var arrGroupHeaders = $("#ProvidersDock_C .rgMultiHeaderRow:first th:not([id])");
4142
4143 for (var i = 0; i < nDynamicColumns; i++) {
4144 gShiftGroupCountColumnNames.push("ShiftGroupSlot" + i);
4145 gShiftGroupTargetColumnNames.push("ShiftGroupTargetSlot" + i);
4146 }
4147
4148 //TJF 9/19/2016 - Fix flashing of A B C in column headers by setting name initially outside of a timeout function
4149 if (getAppArea() == ApplicationArea.AdminCalendar) {
4150 for (var i = 0; i < gShiftGroupCountColumnNames.length; i++) {
4151 var sShiftGroupColName = gShiftGroupCountColumnNames[i];
4152 var col = masterTable.getColumnByUniqueName(sShiftGroupColName).get_element();
4153
4154 if (bShowTargets) {
4155 var sTargetColumnName = gShiftGroupTargetColumnNames[i];
4156 var targetCol = masterTable.getColumnByUniqueName(sTargetColumnName).get_element();
4157
4158 if (!gCurrentDisplayedPrimaryKeys[i]) {
4159 masterTable.hideColumn(GetAdjustedCellIndex(col.cellIndex));
4160 masterTable.hideColumn(GetAdjustedCellIndex(targetCol.cellIndex));
4161 }
4162
4163 col.innerHTML = "Actual";
4164 targetCol.innerHTML = gCurrentDisplayedPrimaryKeys[i] ? "Target" : "";
4165 if (gCurrentDisplayedPrimaryKeys[i]) {
4166 arrGroupHeaders[i].innerHTML = gCurrentDisplayedPrimaryKeys[i].name;
4167 }
4168 } else {
4169 if (!gCurrentDisplayedPrimaryKeys[i]) {
4170 masterTable.hideColumn(GetAdjustedCellIndex(col.cellIndex));
4171 } else {
4172 col.innerHTML = gCurrentDisplayedPrimaryKeys[i].name;
4173 }
4174 }
4175 }
4176
4177 //TJF 3/5/2018 - Restore pract type selection after reload on provider list
4178 var ddlDockPractType = document.getElementById('ddlDockPractType');
4179 if (ddlDockPractType != null) {
4180 providerDockPractIndexChangedForGrid(ddlDockPractType);
4181 }
4182 }
4183
4184 setTimeout(function () {
4185
4186 var remainingCheckedItems = []; //Checked providers that still exist after a reload. Exclude those that are no longer on the list.
4187 for (var i = 0; i < masterTable.get_dataItems().length; i++) {
4188 var row = masterTable.get_dataItems()[i];
4189 var rowId = row.get_element().id;
4190 var chkProvColor = $("#" + rowId).find("#chkProviderColor")[0]; //Gets the checkbox object
4191
4192 //Reset checkboxes or restore the checked box
4193 var PK = masterTable.getCellByColumnUniqueName(row, "PK").innerHTML;
4194 if (gCheckedProviders[PK]) {
4195 chkProvColor.checked = gCheckedProviders[PK];
4196 //Mark item as a still existing checked items
4197 remainingCheckedItems[PK] = gCheckedProviders[PK];
4198 } else {
4199 chkProvColor.checked = false;
4200 }
4201
4202 //TJF 11/8/2016 - Restore status bar totals
4203 if (getAppArea() == ApplicationArea.AdminCalendar && tsMainMenu.get_selectedTab().get_value() == AdminMenuBarItem.AdminCalendar) {
4204 if (gCurrentStatusMsgSelection && PK == gCurrentStatusMsgSelection) {
4205 UpdateProviderInfo(i, null);
4206 }
4207 }
4208
4209 //Restore color
4210 var colorPicker = row.findControl("cpProvList");
4211 var colorType = masterTable.getCellByColumnUniqueName(row, "ColorType").innerHTML;
4212 var DataProvColor = masterTable.getCellByColumnUniqueName(row, "DataProvColor").innerHTML;
4213 //TJF 7/5/2016 - Store permanent colors in array
4214 if (colorType == "permanent" && !gProvColors[PK]) {
4215 gProvColors[PK] = DataProvColor;
4216 }
4217
4218 var colorPickerIconElId = $(colorPicker._element).find("em")[0].id;
4219
4220 if (gProvColors[PK]) {
4221 SetDataProvColor(i, gProvColors[PK]);
4222 colorPicker.remove_colorChange(ProvRadColorPickerChanged);
4223 colorPicker.set_selectedColor(gProvColors[PK], true);
4224 colorPicker.add_colorChange(ProvRadColorPickerChanged);
4225
4226 if (colorType == "permanent") {
4227 SetPermanentColor(colorPickerIconElId);
4228 } else {
4229 masterTable.getCellByColumnUniqueName(row, "ColorType").innerHTML = "temporary";
4230 SetTemporaryColor(colorPickerIconElId);
4231 }
4232 } else {
4233 colorPicker._selectedColor = null;
4234 SetNoColor(colorPickerIconElId);
4235 }
4236
4237 }
4238
4239 //Set checked providers to those still existing
4240 gCheckedProviders = remainingCheckedItems;
4241
4242
4243 //Update shift group tree, but only on the admin calendar
4244 if (getAppArea() == ApplicationArea.AdminCalendar && tsMainMenu.get_selectedTab().get_value() == AdminMenuBarItem.AdminCalendar) {
4245 //Load the shift group tree and calculate shift group count columns
4246 var hdnProvGrdInfoExpandCol = document.getElementById("hdnProvGrdInfoExpandCol");
4247 if (hdnProvGrdInfoExpandCol.value == "true" || gApplyColorsChecked) {
4248 LoadAndProcessShiftGroupTreeJsonInBothUnits(false);
4249 gShiftGroupCountsLoaded = true;
4250 } else {
4251 highlightCheckedProvidersOnGrid();
4252 gShiftGroupCountsLoaded = false;
4253 SetProviderListTitle('');
4254 }
4255
4256 //VJF 11/03/2016 - Show the date range used for shift counting in provider dock when showing counts
4257 //if (gShiftGroupCountsLoaded) {
4258 // SetProviderListTitle(document.forms.frmScheduler.hdnCountingDateLabel.value);
4259 //} else {
4260 // SetProviderListTitle('');
4261 //}
4262
4263 //TJF 10/25/2016 - Used by admin shift groups to determine when processing can begin
4264 gGrdProvFinishedLoad = true;
4265 } else {
4266 //TJF 8/30/2016 - Sky for provider Provider grid only has to load colors
4267 //Highlight remaining providers
4268 highlightCheckedProvidersOnGrid(false);
4269 }
4270
4271 var chkFilterProv = document.getElementById('chkFilterProv');
4272 var bByPassUnavailableTimeCheck = false;
4273 //09/10/2017 - Settings that affect the providers shown or highlighted need to be called here
4274 //after the grid is finalized since it is in a setTimeout call, to assure calls are make in the proper sequence.
4275 if (chkFilterProv && chkFilterProv.checked) {
4276 showOnlySelectedProvidersCheckedOnGrid(chkFilterProv.checked, false);
4277
4278 //showOnlySelectedProvidersCheckedOnGrid call will apply unavailable time formatting, if applicable.
4279 bByPassUnavailableTimeCheck = true;
4280 }
4281
4282 //09/10/2017 - Apply unavailable time formatting, if applicable
4283 if (!bByPassUnavailableTimeCheck) {
4284 var chkUnvailTime = document.getElementById('chkShowProvUnavailableTime');
4285 if (chkUnvailTime && chkUnvailTime.checked) {
4286 showProviderUnavailableTime(true);
4287 }
4288 }
4289
4290 SetSortingClickHandlersForProvInfo(masterTable);
4291 }, 0);
4292
4293}
4294
4295function IsShowTargetsChecked() {
4296 return document.getElementById("ProvidersDock_C_chkShowTargets") && document.getElementById("ProvidersDock_C_chkShowTargets").checked;
4297}
4298
4299//TJF 9/18/2017 - To account for column headers being placed in different rows, get correct javascript cell index using modifier
4300function GetAdjustedCellIndex(col_cellIndex) {
4301 if (!IsShowTargetsChecked()) {
4302 return col_cellIndex;
4303 }
4304
4305 return col_cellIndex + (nDefaultProviderCols - nColumnsAfterDynamicColumns);
4306}
4307
4308function GetAdjustedCellIndexForTotals(col_cellIndex) {
4309 if (!IsShowTargetsChecked()) {
4310 return col_cellIndex;
4311 }
4312
4313 return col_cellIndex + gShiftGroupCountColumnNames.length;
4314}
4315
4316var gTargetsByProvId = null;
4317function LoadAndProcessShiftGroupTreeJsonInBothUnits(bToggleGrid) {
4318 //TJF 10/26/2016 - Show loading panel while ajax request and counting takes place
4319 var currentLoadingPanel = $find("RadAjaxLoadingPanel1");
4320 var grdProvInfoId = $('#ProvidersDock').attr("id");
4321 currentLoadingPanel.show(grdProvInfoId);
4322
4323 var bIsHours = false;
4324 var bIsShifts = false;
4325 if (isUnitsShiftsChecked()) {
4326 bIsShifts = true;
4327 }
4328 if (isUnitsHoursChecked()) {
4329 bIsHours = true;
4330 }
4331
4332 $.ajax(
4333 {
4334 type: "POST",
4335 url: "AdminCalendar.aspx/AjaxGetShiftGroupTreeJSON",
4336 data: JSON.stringify({ bIsHours: bIsHours, bIsShifts: bIsShifts, bTargetsChecked: IsShowTargetsChecked() }),
4337 contentType: "application/json; charset=utf-8",
4338 async: true,
4339 dataType: "json",
4340 success: function (json) {
4341
4342 //TJF 4/6/2018 - If json string is empty, adminschedule has not finished loading.
4343 if (json.d != "") {
4344
4345 var grdProv = GetGrdProvInfo();
4346 var fullJson = JSON.parse(json.d);
4347 treeJSON = fullJson[0];
4348 gTargetsByProvId = fullJson[1];
4349
4350 buildGrid();
4351
4352 var hdnProvGrdInfoExpandCol = document.getElementById("hdnProvGrdInfoExpandCol");
4353
4354 var bIsDockVisible = ($('#ProvidersDock').is(":visible"));
4355 if (bIsDockVisible) {
4356 AddShiftGroupColumnToMasterTable(selectPrimaryKeys, false);
4357 }
4358
4359 gShiftGroupCountsLoaded = true;
4360
4361 if (bToggleGrid) {
4362 SetProviderListTitle(document.forms.frmScheduler.hdnCountingDateLabel.value);
4363 }
4364
4365 RestoreShiftGroupHighlightingAfterCalReload(treeJSON, (gApplyColorsChecked && gColorContext == COLORCONTEXT.ShiftGroup));
4366
4367 if (gColorContext == COLORCONTEXT.PractType) {
4368 restorePractTypeColors();
4369 } else if (gColorContext == COLORCONTEXT.ShiftStatus) {
4370 HighlightByShiftStatus(g_arrShiftStatusIds, false, false);
4371
4372 }
4373
4374 highlightCheckedProvidersOnGrid();
4375 gGrdProvFinishedLoad = true;
4376 if (bToggleGrid) {
4377 ToggleGrdProvCol();
4378 } else {
4379 var masterTable = GetGrdProvInfoMasterTableView();
4380 var bProvGrdInfoExpandCol = (document.getElementById("hdnProvGrdInfoExpandCol").value == "true");
4381 //TJF 11/10/2016 - If units changed, show or hide totals columns
4382 if (bProvGrdInfoExpandCol) {
4383 var NEW_COL_SIZE = 90;
4384 var shiftsCol = masterTable.getColumnByUniqueName("ProviderShifts");
4385 var hoursCol = masterTable.getColumnByUniqueName("ProviderHours");
4386 var sppCol = masterTable.getColumnByUniqueName("SPP");
4387
4388 var bResizeGridLarger = (!shiftsCol.get_visible() || !hoursCol.get_visible()) && (bIsHours && bIsShifts);
4389 var bResizeGridSmaller = (shiftsCol.get_visible() && hoursCol.get_visible()) && (!bIsHours || !bIsShifts);
4390
4391 if (bIsHours) {
4392 masterTable.showColumn(GetAdjustedCellIndexForTotals(hoursCol.get_element().cellIndex));
4393 } else {
4394 masterTable.hideColumn(GetAdjustedCellIndexForTotals(hoursCol.get_element().cellIndex));
4395 }
4396
4397 if (bIsShifts) {
4398 masterTable.showColumn(GetAdjustedCellIndexForTotals(shiftsCol.get_element().cellIndex));
4399 } else {
4400 masterTable.hideColumn(GetAdjustedCellIndexForTotals(shiftsCol.get_element().cellIndex));
4401 }
4402
4403 //TJF 10/26/2017 - Show SPP targets
4404 if (IsShowTargetsChecked()) {
4405 masterTable.showColumn(GetAdjustedCellIndexForTotals(sppCol.get_element().cellIndex));
4406 } else {
4407 masterTable.hideColumn(GetAdjustedCellIndexForTotals(sppCol.get_element().cellIndex));
4408 }
4409
4410 //TJF 11/30/2016 - Resize grid due to new column additions
4411 if (bResizeGridLarger) {
4412 gCurrentGridSize += NEW_COL_SIZE;
4413 } else if (bResizeGridSmaller) {
4414 gCurrentGridSize -= NEW_COL_SIZE;
4415 }
4416 //TJF 10/26/2017 - Add SPP column to grid size
4417 if (IsShowTargetsChecked()) {
4418 gCurrentGridSize += NEW_COL_SIZE;
4419 }
4420
4421 grdProv.get_element().style.width = gCurrentGridSize + "px";
4422 ResizeProvDock();
4423 }
4424
4425 }
4426
4427 $("#" + grdProv.get_id() + " .lblTotalShifts").html(SumTotalShifts().toString());
4428 $("#" + grdProv.get_id() + " .lblTotalHours").html(SumTotalHours());
4429 currentLoadingPanel.hide(grdProvInfoId);
4430
4431 //TJF 12/7/2017 - Restore the pract type filter after a reload
4432 restoreProvListPractTypeFilter();
4433 } else {
4434 currentLoadingPanel.hide(grdProvInfoId);
4435
4436 }
4437 },
4438 error: function (xhr, ajaxOptions, thrownError) {
4439 //console.log(xhr.status);
4440 //console.log(xhr.responseText);
4441 //TJF 10/12/2016 - Catch 401 authentication errors
4442 if (xhr.status == 401) {
4443 var topWindow = GetTopAccessibleWindow(window);
4444 topWindow.location = gSLogoutUrl;
4445 } else {
4446 //TJF 10/12/2016 - Catch 500 internal server errors and others
4447 RadAlert(xhr.status + " " + xhr.responseText);
4448 }
4449 currentLoadingPanel.hide(grdProvInfoId);
4450 }
4451 }
4452 );
4453
4454 function buildGrid() {
4455 //TJF 8/23/2016 - Create list for datasource
4456 if (getAppArea() == ApplicationArea.AdminCalendar && tsMainMenu.get_selectedTab().get_value() == AdminMenuBarItem.AdminCalendar) {
4457
4458 selectPrimaryKeys = GetShiftGroupGridDatasourceAndSelectedPrimaryKeys(arrShiftGroupBreakdown, rgShiftGroupListDatasource);
4459
4460 gShiftGroupsByProvId = [];
4461 //This loops through every appointment on the page and counts the shift groups providers are assigned to
4462 $(".RadScheduler").each(function (index) {
4463 var scheduler = $find($(this).attr('id'));
4464 (scheduler.get_appointments()).forEach(function (apt) {
4465 if (isDateInActiveOrNotDefinedPeriod(apt.get_start())) {
4466 var attribs = apt.get_attributes();
4467 var selectedShiftPrimaryKey = attribs.getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_APPT_ID);
4468 var provId = attribs.getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_PROV_ID);
4469 var locationId = GetLocationIdFromPrimaryKey(selectedShiftPrimaryKey);
4470 var bIsException = ($(apt.get_element()).find(".exception").length > 0);
4471
4472 //Handle opens. The open should use an ID of 0
4473 if (provId == -1) {
4474 provId = 0;
4475 }
4476
4477 var typeId = GetTypeIdFromPrimaryKey(selectedShiftPrimaryKey);
4478 var aptDate = apt.get_start();
4479 for (var k = 0; k < treeJSON.length; k++) {
4480 var treeEntries = treeJSON[k];
4481 var shiftGroupId = treeEntries.ShiftGroupID;
4482 var provShiftGroupTarget = { ideal: -1, isShifts: "false" };
4483
4484 //TJF 9/7/2017 - Get targets
4485 var sSystemOrLocalKey = treeEntries.IsSystem ? "System" : "Local";
4486 if (objectContains(gTargetsByProvId, provId, shiftGroupId, sSystemOrLocalKey)) {
4487 provShiftGroupTarget = gTargetsByProvId[provId][shiftGroupId][sSystemOrLocalKey];
4488
4489 //TJF 2/7/2018 - Init when a target is found always
4490 initShiftGroupsByProvIdColumn(provId, shiftGroupId, parseInt(provShiftGroupTarget.ideal), (provShiftGroupTarget.isShifts == "true"), treeEntries.IsSystem);
4491
4492 }
4493
4494 //TJF 8/23/2016 - Loop through tree entries until a matching entry is found
4495 for (var j = 0; j < treeEntries.Entries.length; j++) {
4496 var treeEntry = treeEntries.Entries[j];
4497 var shiftTypeId = treeEntry.STId;
4498 //TJF 10/26/2016 - Increment by hours or shift count
4499 var nIncrement = ((parseFloat(attribs.getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_SHIFT_DURATION))) / 60);
4500
4501
4502 var exDate;
4503 //exDate is only set in the case of an exception, so date is not relevant for normal tree entries
4504 if (treeEntry.IsEx) {
4505 exDate = new Date(treeEntry.ExDate);
4506 }
4507
4508 //Count shifts for provider
4509 if (!treeEntry.IsEx && !bIsException && (typeId == shiftTypeId) && (locationId == treeEntry.LocId) && (GetBitmaskForDay(apt.get_start().getDay()) & treeEntry.DoW)) {
4510 //Add hours counts
4511 AddCountToShiftGroupsByProvIdAndUnits(provId, shiftGroupId, treeEntries.IsSystem, nIncrement, true,
4512 parseInt(provShiftGroupTarget.ideal),
4513 provShiftGroupTarget ? (provShiftGroupTarget.isShifts == "true") : false);
4514 //Add shift counts
4515 AddCountToShiftGroupsByProvIdAndUnits(provId, shiftGroupId, treeEntries.IsSystem, nIncrement, false,
4516 parseInt(provShiftGroupTarget.ideal),
4517 provShiftGroupTarget ? (provShiftGroupTarget.isShifts == "true") : false);
4518 break;
4519 } else if (treeEntry.IsEx && (typeId == shiftTypeId) && (locationId == treeEntry.LocId) &&
4520 (exDate.getDate() == aptDate.getDate() && exDate.getMonth() == aptDate.getMonth() && exDate.getFullYear() == aptDate.getFullYear())) {
4521 //TJF 7/29/2016 - Handle an exception
4522 //Add hours counts
4523 AddCountToShiftGroupsByProvIdAndUnits(provId, shiftGroupId, treeEntries.IsSystem, nIncrement, true,
4524 parseInt(provShiftGroupTarget.ideal),
4525 provShiftGroupTarget ? (provShiftGroupTarget.isShifts == "true") : false);
4526 //Add shift counts
4527 AddCountToShiftGroupsByProvIdAndUnits(provId, shiftGroupId, treeEntries.IsSystem, nIncrement, false,
4528 parseInt(provShiftGroupTarget.ideal),
4529 provShiftGroupTarget ? (provShiftGroupTarget.isShifts == "true") : false);
4530 break;
4531 }
4532 }
4533 }
4534 }
4535 });
4536
4537 });
4538 }
4539 }
4540}
4541
4542//TJF 10/18/2017 - Get the second value(the location id) from a shift primary key. Ex with primary key of "1434736625|78|-1|20170831", "78" would be returned
4543function GetLocationIdFromPrimaryKey(strPrimaryKey) {
4544 return strPrimaryKey.substring(strPrimaryKey.indexOf("|") + 1,
4545 strPrimaryKey.indexOf("|", strPrimaryKey.indexOf("|") + 1));
4546}
4547
4548//TJF 10/18/2017 - Get the first value(the shift type) from a shift primary key. Ex with primary key of "1434736625|78|-1|20170831", "1434736625" would be returned
4549function GetTypeIdFromPrimaryKey(strPrimaryKey) {
4550 return strPrimaryKey.substring(0, strPrimaryKey.indexOf("|"));
4551}
4552
4553//VJF 11/10/2016 - Move code to function for reuse.
4554//Build the datasource the shift group selection window will use to load the grid, rgSGListDatasourceOut. This output parameter will contain the datasource.
4555//The selected shift group PK array is constructed from the app pref shift group breakdown array.
4556function GetShiftGroupGridDatasourceAndSelectedPrimaryKeys(arrSGBreakdown, rgSGListDatasourceOut) {
4557 rgSGListDatasourceOut.length = 0; //Clear array, using = [] would create a new object and lose the original reference
4558 var arrSelectedShiftGroupPKs = [];
4559
4560 for (var k = 0; k < treeJSON.length; k++) {
4561
4562 //Build the datasource the grid will use to display the shift group list
4563 var rowData = ShiftGroupRowData.create(treeJSON[k]);
4564 //TJF 11/22/2016 - Undefined means a title has not been set for the shift group, so exclude it from the datasource
4565 if (rowData.ShiftGroupName.trim() == "Undefined") {
4566 continue;
4567 }
4568
4569 rgSGListDatasourceOut.push(rowData);
4570
4571 //Set the select primary keys. The arrSGBreakdown contains the values found in the SG Breakdown app preference
4572 for (var j = 0; j < arrSGBreakdown.length; j++) {
4573 var shiftGroupIds = arrSGBreakdown[j];
4574 if (shiftGroupIds[0] == rowData.ID && shiftGroupIds[1] == rowData.LocId) {
4575 arrSelectedShiftGroupPKs.push({ isSystem: rowData.IsSystem, id: rowData.ID, name: rowData.ShiftGroupName, locId: rowData.LocId });
4576 break;
4577 }
4578 }
4579 }
4580
4581 return arrSelectedShiftGroupPKs;
4582}
4583
4584//VJF 11/10/2016 - Convert Shift Group Breakdown string in format SG1,Loc1|SG2,Loc2,... to an array with a structure of [[SG1][Loc1]][[SG1][Loc2]]...
4585function ShiftGroupBreakdownToArray(sShiftGroupBreakdown) {
4586 var arrFirstLevel = sShiftGroupBreakdown.split("|");
4587 var arrShiftGroupBreakdown = [];
4588
4589 arrFirstLevel.forEach(function (commaString) {
4590 arrShiftGroupBreakdown.push(commaString.split(","));
4591 });
4592
4593 return arrShiftGroupBreakdown
4594}
4595
4596
4597
4598
4599//TJF 8/25/2016 - Enable sorting by clicking the grid column on grdProv.
4600//This adds jquery click handlers to every column header and sets the sorting function to use
4601function SetSortingClickHandlersForProvInfo(grdProv_masterTable) {
4602 //Set grdProv sorting click handlers
4603 if (grdProv_masterTable.getColumnByUniqueName("ProviderShifts")) {
4604 var bSortShiftCountAscending = false;
4605 $(grdProv_masterTable.getColumnByUniqueName("ProviderShifts").get_element()).click(function () {
4606 sortProvGridByShifts(grdProv_masterTable, bSortShiftCountAscending, "ProviderShifts");
4607 bSortShiftCountAscending = !bSortShiftCountAscending;
4608 });
4609 }
4610
4611 ////TJF 9/13/2017 - Sort target shifts
4612 //if (grdProv_masterTable.getColumnByUniqueName("TargetShifts")) {
4613 // var bSortShiftCountAscending = false;
4614 // $(grdProv_masterTable.getColumnByUniqueName("TargetShifts").get_element()).click(function () {
4615 // sortProvGridByShifts(grdProv_masterTable, bSortShiftCountAscending, "TargetShifts");
4616 // bSortShiftCountAscending = !bSortShiftCountAscending;
4617 // });
4618 //}
4619
4620 if (grdProv_masterTable.getColumnByUniqueName("ProviderHours")) {
4621 var bSortHoursCountAscending = false;
4622 $(grdProv_masterTable.getColumnByUniqueName("ProviderHours").get_element()).click(function () {
4623 sortProvGridByHours(grdProv_masterTable, bSortHoursCountAscending);
4624 bSortHoursCountAscending = !bSortHoursCountAscending;
4625 });
4626 }
4627
4628 if (grdProv_masterTable.getColumnByUniqueName("ProviderNameDisplay")) {
4629 var bSortByNameAscending = false;
4630 $(grdProv_masterTable.getColumnByUniqueName("ProviderNameDisplay").get_element()).click(function () {
4631 sortProvGridByName(grdProv_masterTable, bSortByNameAscending);
4632 bSortByNameAscending = !bSortByNameAscending;
4633 });
4634 }
4635
4636 //TJF 8/9/2016 - This col only displays on provider location screen
4637 if (grdProv_masterTable.getColumnByUniqueName("ProviderName")) {
4638 var bSortByProvNameAscending = false;
4639 $(grdProv_masterTable.getColumnByUniqueName("ProviderName").get_element()).click(function () {
4640 sortProvGridByName(grdProv_masterTable, bSortByProvNameAscending);
4641 bSortByProvNameAscending = !bSortByProvNameAscending;
4642 });
4643 }
4644
4645 if (grdProv_masterTable.getColumnByUniqueName("ProvColorPicker")) {
4646 var bSortByColorAscending = true;
4647 $(grdProv_masterTable.getColumnByUniqueName("ProvColorPicker").get_element()).click(function () {
4648 sortProvGridByColor(grdProv_masterTable, bSortByColorAscending);
4649 bSortByColorAscending = !bSortByColorAscending;
4650 });
4651 }
4652
4653 if (grdProv_masterTable.getColumnByUniqueName("ChkBoxColor")) {
4654 var bSortCheckedAscending = true;
4655 $(grdProv_masterTable.getColumnByUniqueName("ChkBoxColor").get_element()).click(function () {
4656 sortProvGridByChecked(grdProv_masterTable, bSortCheckedAscending);
4657 bSortCheckedAscending = !bSortCheckedAscending;
4658 });
4659 }
4660
4661 //The following three are generic columns that will hold selected shift group data.
4662 //They are not visible until a shift group is set in the shift group select window
4663 for (var i = 0; i < gShiftGroupCountColumnNames.length; i++) {
4664 var sColName = gShiftGroupCountColumnNames[i];
4665 (function (i) {
4666 var sColName = gShiftGroupCountColumnNames[i];
4667 if (grdProv_masterTable.getColumnByUniqueName(sColName)) {
4668 var bSortShiftGroupAscending = false;
4669 $(grdProv_masterTable.getColumnByUniqueName(sColName).get_element()).click(function () {
4670 sortProvGridByShiftGroupCount(grdProv_masterTable, bSortShiftGroupAscending, sColName);
4671 bSortShiftGroupAscending = !bSortShiftGroupAscending;
4672 });
4673 }
4674
4675 })(i);
4676 }
4677
4678}
4679
4680//TJF 8/22/2016 - Add a shift group tree entry count to the object
4681//Object keyed by [provId][shiftGroupId]["System"|"Local"]["hours"|"shifts"]
4682function AddCountToShiftGroupsByProvIdAndUnits(provId, shiftGroupId, bIsSystem, nIncrement, bIsHours, nTargetHours, bIsShifts) {
4683 initShiftGroupsByProvIdColumn(provId, shiftGroupId, nTargetHours, bIsShifts, bIsSystem);
4684
4685 var sSystemOrLocal = bIsSystem ? "System" : "Local";
4686 if (bIsHours) {
4687 gShiftGroupsByProvId[provId][shiftGroupId][sSystemOrLocal].hours += nIncrement; //Math.round(nIncrement * 100) / 100;
4688 } else {
4689 gShiftGroupsByProvId[provId][shiftGroupId][sSystemOrLocal].shifts += 1;
4690 }
4691}
4692
4693//TJF 11/9/2016 - Make sure columns are initialized before adding to counts
4694function initShiftGroupsByProvIdColumn(provId, shiftGroupId, nTargetHours, bIsShifts, bIsSystem) {
4695
4696 if (!gShiftGroupsByProvId[provId]) {
4697 gShiftGroupsByProvId[provId] = [];
4698 }
4699 if (!gShiftGroupsByProvId[provId][shiftGroupId]) {
4700
4701 //TJF 9/5/2017 - Add targets to data structure
4702 gShiftGroupsByProvId[provId][shiftGroupId] = [];
4703 gShiftGroupsByProvId[provId][shiftGroupId]["System"] =
4704 {
4705 hours: 0,
4706 shifts: 0,
4707 target:
4708 {
4709 ideal: -1,
4710 isShifts: bIsShifts
4711 }
4712 };
4713 gShiftGroupsByProvId[provId][shiftGroupId]["Local"] =
4714 {
4715 hours: 0,
4716 shifts: 0,
4717 target:
4718 {
4719 ideal: -1,
4720 isShifts: bIsShifts
4721 }
4722 };
4723
4724 }
4725
4726 gShiftGroupsByProvId[provId][shiftGroupId][bIsSystem ? "System" : "Local"].target = { ideal: nTargetHours, isShifts: bIsShifts };
4727
4728
4729}
4730
4731//TJF 8/12/2016 - Add column(s) to the prov grid by filling in a blank column
4732//This is used for the shift group counts, which are filled in dynamically here
4733//TJF 9/12/2016 - This is used to keep track of all the shift group counts being displayed on the provider grid in between page loads.
4734//It is also used when updating a count when removing or adding a provider to a shift
4735var gCurrentDisplayedPrimaryKeys = [];
4736var gCurrentGridSize = null; //TJF 11/10/2016 - Always set to current grid size when toggling
4737function AddShiftGroupColumnToMasterTable(selectPrimaryKeys, bSaveVal) {
4738 gCurrentDisplayedPrimaryKeys = selectPrimaryKeys;
4739
4740 //TJF 11/9/2016 - Check for hours or shifts
4741 var bIsHours = isUnitsHoursChecked();
4742 var bIsShifts = isUnitsShiftsChecked();
4743
4744 var masterTable = GetGrdProvInfoMasterTableView();
4745 for (var i = 0; i < masterTable.get_dataItems().length; i++) {
4746 var row = masterTable.get_dataItems()[i];
4747 var PK = masterTable.getCellByColumnUniqueName(row, "PK").innerHTML;
4748 for (var j = 0; j < selectPrimaryKeys.length; j++) {
4749 var objShfitPrimaryKey = selectPrimaryKeys[j];
4750 var ShiftGroupType = "System";
4751 if (!objShfitPrimaryKey["isSystem"] || objShfitPrimaryKey["isSystem"] == "Location") {
4752 ShiftGroupType = "Local";
4753 }
4754
4755 var colName = gShiftGroupCountColumnNames[j];
4756 var targetColName = gShiftGroupTargetColumnNames[j];
4757
4758 if (!colName) {
4759 continue;
4760 }
4761 if (bIsHours && bIsShifts) {
4762 masterTable.getCellByColumnUniqueName(row, colName).innerHTML = "0/0";
4763 } else {
4764 masterTable.getCellByColumnUniqueName(row, colName).innerHTML = 0;
4765 }
4766
4767 if (IsShowTargetsChecked()) {
4768 masterTable.getCellByColumnUniqueName(row, targetColName).innerHTML = "N/A";
4769 }
4770
4771 if (objectContains(gShiftGroupsByProvId, PK, objShfitPrimaryKey.id, ShiftGroupType)) {
4772
4773 if (bIsHours && bIsShifts) {
4774 masterTable.getCellByColumnUniqueName(row, colName).innerHTML = gShiftGroupsByProvId[PK][objShfitPrimaryKey.id][ShiftGroupType].shifts + "/" + RoundValueToDecimalPlace(gShiftGroupsByProvId[PK][objShfitPrimaryKey.id][ShiftGroupType].hours, 1);
4775 } else if (bIsHours) {
4776 masterTable.getCellByColumnUniqueName(row, colName).innerHTML = RoundValueToDecimalPlace(gShiftGroupsByProvId[PK][objShfitPrimaryKey.id][ShiftGroupType].hours, 1);
4777 } else if (bIsShifts) {
4778 masterTable.getCellByColumnUniqueName(row, colName).innerHTML = gShiftGroupsByProvId[PK][objShfitPrimaryKey.id][ShiftGroupType].shifts;
4779 }
4780
4781 //TJF 9/11/2017 - Add target data
4782 if (IsShowTargetsChecked() && gShiftGroupsByProvId[PK][objShfitPrimaryKey.id][ShiftGroupType].target.ideal > -1) {
4783 masterTable.getCellByColumnUniqueName(row, targetColName).innerHTML =
4784 gShiftGroupsByProvId[PK][objShfitPrimaryKey.id][ShiftGroupType].target.ideal + " " + (gShiftGroupsByProvId[PK][objShfitPrimaryKey.id][ShiftGroupType].target.isShifts ? "shifts" : "hours");
4785 }
4786
4787
4788 } else if (IsShowTargetsChecked() && objectContains(gTargetsByProvId, PK, objShfitPrimaryKey.id, ShiftGroupType)) {
4789 //TJF 9/14/2017 - Even if provider does not work in a shift group, check for targets
4790 var provShiftGroupTarget = gTargetsByProvId[PK][objShfitPrimaryKey.id][ShiftGroupType];
4791 if (parseInt(provShiftGroupTarget.ideal) > -1) {
4792 masterTable.getCellByColumnUniqueName(row, targetColName).innerHTML =
4793 parseInt(provShiftGroupTarget.ideal) + " " + ((provShiftGroupTarget.isShifts == "true") ? "shifts" : "hours");
4794 }
4795 }
4796 }
4797
4798 //TJF 10/26/2017 - Set SPP
4799 //TJF 11/27/2017 - SPP loaded outside loop so spp is set with no shift groups displayed
4800 //TJF 1/3/2017 - SPP is now loaded by the server side
4801 //if (objectContains(gTargetsByProvId, PK, "-1", "System")) {
4802 // var sppTarget = gTargetsByProvId[PK]["-1"]["System"];
4803 // if (parseInt(sppTarget.ideal) > -1) {
4804 // masterTable.getCellByColumnUniqueName(row, "SPP").innerHTML =
4805 // parseInt(sppTarget.ideal) + " " + ((sppTarget.isShifts == "true") ? "shifts" : "hours");
4806 // }
4807 //} else {
4808 // masterTable.getCellByColumnUniqueName(row, "SPP").innerHTML = "N/A"; TJF TEST
4809 //}
4810 }
4811
4812 //Get column element
4813 var arrShiftGroupCols = [];
4814 var arrTargetCols = [];
4815 for (var i = 0; i < gShiftGroupCountColumnNames.length; i++) {
4816 var sColName = gShiftGroupCountColumnNames[i];
4817 arrShiftGroupCols.push(masterTable.getColumnByUniqueName(sColName).get_element())
4818
4819 if (IsShowTargetsChecked()) {
4820 arrTargetCols.push(masterTable.getColumnByUniqueName(gShiftGroupTargetColumnNames[i]).get_element());
4821 }
4822 }
4823
4824 var bProvGrdInfoExpandCol = (document.getElementById("hdnProvGrdInfoExpandCol").value == "true");
4825
4826 var grid = GetGrdProvInfo();
4827 //grid.get_element().style.width = 340 + "px";
4828 //ResizeProvDock();
4829
4830 //Only show columns if extra columns have been toggled on
4831 var nGridSize = 420; //Grow grid if columns are shown
4832
4833 var arrGroupHeaders = $("#ProvidersDock_C .rgMultiHeaderRow:first th:not([id])");
4834
4835 //TJF 10/17/2016 - Show or hide shift group count columns
4836 for (var i = 0; i < arrShiftGroupCols.length; i++) {
4837 var sDisplayName = ""
4838 if (selectPrimaryKeys[i] && (selectPrimaryKeys[i].isSystem == "System" || selectPrimaryKeys[i].isSystem == true)) {
4839 sDisplayName = selectPrimaryKeys[i].name + "<br>(system)";
4840 } else if (selectPrimaryKeys[i]) {
4841 sDisplayName = selectPrimaryKeys[i].name;
4842 }
4843
4844 if (IsShowTargetsChecked()) {
4845 arrShiftGroupCols[i].innerHTML = "Actual";
4846 arrTargetCols[i].innerHTML = "Target";
4847 arrGroupHeaders[i].innerHTML = sDisplayName;
4848 } else {
4849 arrShiftGroupCols[i].innerHTML = sDisplayName;
4850 }
4851
4852 if (selectPrimaryKeys[i] && bProvGrdInfoExpandCol) {
4853 masterTable.showColumn(GetAdjustedCellIndex(arrShiftGroupCols[i].cellIndex));
4854 if (IsShowTargetsChecked()) {
4855 masterTable.showColumn(GetAdjustedCellIndex(arrTargetCols[i].cellIndex));
4856 }
4857
4858 //nGridSize += $(arrShiftGroupCols[i]).width();
4859 nGridSize += $(arrShiftGroupCols[i]).outerWidth(); //TJF 11/14/2016 - Include padding in width calculation
4860 if (IsShowTargetsChecked()) {
4861 nGridSize += $(arrTargetCols[i]).outerWidth();
4862 }
4863 } else {
4864 masterTable.hideColumn(GetAdjustedCellIndex(arrShiftGroupCols[i].cellIndex));
4865 if (IsShowTargetsChecked()) {
4866 masterTable.hideColumn(GetAdjustedCellIndex(arrTargetCols[i].cellIndex));
4867 }
4868 }
4869
4870 }
4871
4872 if (bProvGrdInfoExpandCol) {
4873 //TJF 11/27/2017 - Prevent sizing glitches by always resetting the height when expanded
4874 GetGrdProvInfo().get_element().style.width = nGridSize + "px";
4875 gCurrentGridSize = nGridSize;
4876 ResizeProvDock();
4877 }
4878
4879
4880 var grdProvId = "#" + grid.get_id();
4881
4882 for (var i = 0; i < gShiftGroupCountColumnNames.length; i++) {
4883 var sName = gShiftGroupCountColumnNames[i];
4884 $(grdProvId + " .lbl" + sName).html(sumTotalShiftGroups(sName).toString());
4885 }
4886
4887 //Save to server
4888 if (bSaveVal) {
4889 saveShiftGroupSelectionToServer(selectPrimaryKeys);
4890 }
4891}
4892
4893//TJF 9/14/2017 - Check if object contains the keys
4894function objectContains(obj, key1, key2, key3) {
4895 return (obj && obj[key1] && obj[key1][key2] && obj[key1][key2][key3]);
4896}
4897
4898function isUnitsShiftsChecked() {
4899 return document.getElementById("ProvidersDock_C_chkUnitsShifts").checked;
4900}
4901
4902function isUnitsHoursChecked() {
4903 return document.getElementById("ProvidersDock_C_chkUnitsHours").checked;
4904}
4905
4906//TJF 12/8/2017 - Get the number of loaded dynamic columns. This includes hidden columns not in use.
4907function getDynamicColumnsCount() {
4908 var masterTable = GetGrdProvInfoMasterTableView();
4909 var nDynamicColumns = masterTable.get_columns().length - nDefaultProviderCols;
4910
4911 if (IsShowTargetsChecked()) {
4912 nDynamicColumns = nDynamicColumns / 2;
4913 }
4914
4915 return nDynamicColumns;
4916}
4917
4918//VJF 11/10/2016 - Enhanced so that app pref shift groups not relevant to the current shift group tree do not get removed on save.
4919//This includes other location shift groups or system shift groups that may not be relevant to the current active date range.
4920function saveShiftGroupSelectionToServer(selectPrimaryKeys) {
4921 //Check selected primary keys to see which are being used
4922 //TJF 10/20/2016 - Check to see if more columns are needed from the server
4923 var nDynamicColumns = getDynamicColumnsCount();
4924
4925 var sSaveString = "";
4926 for (var i = 0; i < selectPrimaryKeys.length; i++) {
4927 if (selectPrimaryKeys[i]) {
4928 sSaveString += selectPrimaryKeys[i].id + "," + selectPrimaryKeys[i].locId + "|";
4929 } else {
4930 break;
4931 }
4932 }
4933
4934 $.ajax(
4935 {
4936 type: "POST",
4937 url: "AdminCalendar.aspx/SaveSelectedProvListShiftGroups",
4938 data: JSON.stringify({ sSelectedShiftGroupsKeys: sSaveString, sShiftGroupJson: JSON.stringify(treeJSON) }),
4939 contentType: "application/json; charset=utf-8",
4940 async: true,
4941 dataType: "json",
4942 success: function (msg) {
4943 if (msg.d === "Fail") {
4944 //RadAlert('Session timed out. Shift group selection could not be saved.');
4945 var topWindow = GetTopAccessibleWindow(window);
4946 topWindow.location = gSLogoutUrl;
4947 } else {
4948 //The updated shift group breakdown app pref is returned, to keep the client up to date.
4949 sShiftGroupBreakdown = msg.d;
4950 arrShiftGroupBreakdown = ShiftGroupBreakdownToArray(sShiftGroupBreakdown);
4951
4952 //Rebuild the Shift Group selected primary keys to keep them accurate.
4953 var rgSGListDatasource = [];
4954 selectPrimaryKeys = GetShiftGroupGridDatasourceAndSelectedPrimaryKeys(arrShiftGroupBreakdown, rgSGListDatasource);
4955
4956 if (selectPrimaryKeys.length > nDynamicColumns) {
4957 reloadProvList();
4958 }
4959 }
4960 },
4961 error: function (xhr, ajaxOptions, thrownError) {
4962 //TJF 10/12/2016 - Catch 401 authentication errors
4963 if (xhr.status == 401) {
4964 var topWindow = GetTopAccessibleWindow(window);
4965 topWindow.location = gSLogoutUrl;
4966 } else {
4967 //TJF 10/12/2016 - Catch 500 internal server errors and others
4968 RadAlert(xhr.status + " " + xhr.responseText);
4969 }
4970
4971 //console.log(xhr.status);
4972 //console.log(xhr.responseText);
4973 }
4974 }
4975 );
4976}
4977
4978//Sorting for grid prov
4979var OPEN_SHIFT_ID = "0";
4980//TJF 7/13/2016 - Sort by shift number
4981function sortProvGridByShifts(masterTable, bAscending, sColumnName) {
4982 var items = masterTable.get_dataItems();
4983
4984 items.sort(function (a, b) {
4985 //Sort open at top always
4986 var aPK = masterTable.getCellByColumnUniqueName(a, "PK").innerHTML;
4987 var bPK = masterTable.getCellByColumnUniqueName(b, "PK").innerHTML;
4988
4989 if (aPK == OPEN_SHIFT_ID) {
4990 return -1;
4991 } else if (bPK == OPEN_SHIFT_ID) {
4992 return 1;
4993 }
4994
4995 if (bAscending) {
4996 return parseInt(masterTable.getCellByColumnUniqueName(a, sColumnName).innerHTML) - parseInt(masterTable.getCellByColumnUniqueName(b, sColumnName).innerHTML);
4997 } else {
4998 return parseInt(masterTable.getCellByColumnUniqueName(b, sColumnName).innerHTML) - parseInt(masterTable.getCellByColumnUniqueName(a, sColumnName).innerHTML);
4999 }
5000 });
5001
5002 updateProvGrid(masterTable, items);
5003}
5004
5005//TJF 7/13/2016 - Sort by shift number
5006function sortProvGridByHours(masterTable, bAscending) {
5007 var items = masterTable.get_dataItems();
5008 items.sort(function (a, b) {
5009 //Sort open at top always
5010 var aPK = masterTable.getCellByColumnUniqueName(a, "PK").innerHTML;
5011 var bPK = masterTable.getCellByColumnUniqueName(b, "PK").innerHTML;
5012
5013 if (aPK == OPEN_SHIFT_ID) {
5014 return -1;
5015 } else if (bPK == OPEN_SHIFT_ID) {
5016 return 1;
5017 }
5018
5019 if (bAscending) {
5020 return parseFloat(masterTable.getCellByColumnUniqueName(a, "ProviderHours").innerHTML) - parseFloat(masterTable.getCellByColumnUniqueName(b, "ProviderHours").innerHTML);
5021 } else {
5022 return parseFloat(masterTable.getCellByColumnUniqueName(b, "ProviderHours").innerHTML) - parseFloat(masterTable.getCellByColumnUniqueName(a, "ProviderHours").innerHTML);
5023 }
5024 });
5025
5026 updateProvGrid(masterTable, items);
5027}
5028
5029//TJF 8/5/2016 - Sort by shift group count
5030function sortProvGridByShiftGroupCount(masterTable, bAscending, sColName) {
5031 var items = masterTable.get_dataItems();
5032 items.sort(function (a, b) {
5033 //Sort open at top always
5034 var aPK = masterTable.getCellByColumnUniqueName(a, "PK").innerHTML;
5035 var bPK = masterTable.getCellByColumnUniqueName(b, "PK").innerHTML;
5036
5037 if (aPK == OPEN_SHIFT_ID) {
5038 return -1;
5039 } else if (bPK == OPEN_SHIFT_ID) {
5040 return 1;
5041 }
5042
5043 if (bAscending) {
5044 return parseInt(masterTable.getCellByColumnUniqueName(a, sColName).innerHTML) - parseInt(masterTable.getCellByColumnUniqueName(b, sColName).innerHTML);
5045 } else {
5046 return parseInt(masterTable.getCellByColumnUniqueName(b, sColName).innerHTML) - parseInt(masterTable.getCellByColumnUniqueName(a, sColName).innerHTML);
5047 }
5048 });
5049
5050 updateProvGrid(masterTable, items);
5051}
5052
5053//TJF 9/12/2016 - The following are sorting functions for the provider grid. Sorting is activated by clicking a column header
5054//TJF 7/14/2016 - Sort provider grid by name
5055function sortProvGridByName(masterTable, bAscending) {
5056 var items = masterTable.get_dataItems();
5057 items.sort(function (a, b) {
5058 //Sort open at top always
5059 var aPK = masterTable.getCellByColumnUniqueName(a, "PK").innerHTML;
5060 var bPK = masterTable.getCellByColumnUniqueName(b, "PK").innerHTML;
5061
5062 if (aPK == OPEN_SHIFT_ID) {
5063 return -1;
5064 } else if (bPK == OPEN_SHIFT_ID) {
5065 return 1;
5066 }
5067
5068 //Sort the provider list without using first initial and trailing elipses
5069 var textA = masterTable.getCellByColumnUniqueName(a, "ProviderName").innerHTML.toUpperCase();
5070 var textB = masterTable.getCellByColumnUniqueName(b, "ProviderName").innerHTML.toUpperCase();
5071
5072 textA = textA.substring(textA.lastIndexOf(".") + 1);
5073 textB = textB.substring(textB.lastIndexOf(".") + 1);
5074
5075 if (bAscending) {
5076 return (textA < textB) ? -1 : (textA > textB) ? 1 : 0;
5077 } else {
5078 return (textA < textB) ? 1 : (textA > textB) ? -1 : 0;
5079 }
5080 });
5081
5082 updateProvGrid(masterTable, items);
5083}
5084
5085//TJF 9/12/2016 - Sort by hex value
5086function sortProvGridByColor(masterTable, bAscending) {
5087 var items = masterTable.get_dataItems();
5088 items.sort(function (a, b) {
5089 //Sort open at top always
5090 var aPK = masterTable.getCellByColumnUniqueName(a, "PK").innerHTML;
5091 var bPK = masterTable.getCellByColumnUniqueName(b, "PK").innerHTML;
5092
5093 if (aPK == OPEN_SHIFT_ID) {
5094 return -1;
5095 } else if (bPK == OPEN_SHIFT_ID) {
5096 return 1;
5097 }
5098
5099 var textA = masterTable.getCellByColumnUniqueName(a, "DataProvColor").innerHTML;
5100 var textB = masterTable.getCellByColumnUniqueName(b, "DataProvColor").innerHTML;
5101
5102 if (bAscending) {
5103 return (textA < textB) ? -1 : (textA > textB) ? 1 : 0;
5104 } else {
5105 return (textA < textB) ? 1 : (textA > textB) ? -1 : 0;
5106 }
5107 });
5108
5109 updateProvGrid(masterTable, items);
5110
5111}
5112
5113
5114//TJF 7/14/2016 - Sort by checked
5115function sortProvGridByChecked(masterTable, bAscending) {
5116 var items = masterTable.get_dataItems();
5117
5118 items.sort(function (a, b) {
5119 //Sort open at top always
5120 var aPK = masterTable.getCellByColumnUniqueName(a, "PK").innerHTML;
5121 var bPK = masterTable.getCellByColumnUniqueName(b, "PK").innerHTML;
5122
5123 if (aPK == OPEN_SHIFT_ID) {
5124 return -1;
5125 } else if (bPK == OPEN_SHIFT_ID) {
5126 return 1;
5127 }
5128
5129 var aChk = $("#" + a.get_element().id).find("#chkProviderColor")[0];
5130 var bChk = $("#" + b.get_element().id).find("#chkProviderColor")[0];
5131
5132 if (bAscending) {
5133 if (aChk.checked == bChk.checked) {
5134 return 0;
5135 } else if (aChk.checked) {
5136 return -1;
5137 } else {
5138 return 1;
5139 }
5140 } else {
5141 if (aChk.checked == bChk.checked) {
5142 return 0;
5143 } else if (aChk.checked) {
5144 return 1;
5145 } else {
5146 return -1;
5147 }
5148 }
5149 });
5150
5151 updateProvGrid(masterTable, items);
5152}
5153
5154
5155
5156//TJF 7/13/2016 - Delete old rows and replace them with the new rows
5157function updateProvGrid(masterTable, newItems) {
5158 var tbody = $(masterTable.get_element()).find("tbody")[0];
5159
5160 for (var i = 0; i < masterTable.get_dataItems().length; i++) {
5161 $(masterTable.get_dataItems()[i].get_element()).remove();
5162 }
5163
5164 for (var i = 0; i < newItems.length; i++) {
5165 $(tbody).append(newItems[i].get_element());
5166 }
5167}
5168
5169//VJF 08/11/2015 - 'Show only selected' checkbox state changed.
5170// Show or hide the selected providers accordingly.
5171// bIsProviderDockChk - Called from checkbox on provider dock
5172function showOnlySelectedProvidersCheckedOnGrid(isChecked, bIsProviderDockChk) {
5173 gShowOnlySelectedProviders = isChecked;
5174 var masterTable = GetGrdProvInfoMasterTableView();
5175
5176 for (var i = 0; i < masterTable.get_dataItems().length; i++) {
5177 var row = masterTable.get_dataItems()[i];
5178 var rowId = row._element.id;
5179 var chkProvColor = $("#" + rowId).find("#chkProviderColor")[0]; //Gets the checkbox object
5180 if (chkProvColor.checked == false) {
5181 HighlightProviderOnGrid(chkProvColor);
5182 }
5183 }
5184
5185 //VJF 10/31/2017 - Add/Remove css class on page body for styling when only showing checked only, ex. shifts with closed status.
5186 var body = null;
5187 addRemoveBodyCheckedOnlyCssClass(body, isChecked);
5188
5189 //VJF 09/13/2017 - Show provider unavailable time when checked
5190 var chkbox = document.getElementById('chkShowProvUnavailableTime');
5191 if (chkbox && chkbox.checked) {
5192 showProviderUnavailableTime(true);
5193 }
5194 else {
5195 //TJF 12/16/2016 - Restore highlighting for shift groups or pract types when all providers are now shown
5196 if (bIsProviderDockChk && !gShowOnlySelectedProviders) {
5197 if (gColorContext == COLORCONTEXT.PractType) {
5198 restorePractTypeColors();
5199 } else if (gColorContext == COLORCONTEXT.ShiftGroup) {
5200 restoreShiftGroupColors();
5201
5202 } else if (gColorContext == COLORCONTEXT.ShiftStatus) {
5203 restoreShiftStatusHighlighting();
5204
5205 }
5206 highlightCheckedProvidersOnGrid();
5207 }
5208 }
5209}
5210
5211//NMM 3/7/2016 - Gets called when you pick a color on the RadColorPicker.
5212//This saves the color to the hidden data prov column on the grid
5213function ProvRadColorPickerChanged(sender, eventArgs) {
5214 var selectedColor = sender._selectedColor;
5215 var row = $(sender._element).closest('tr')[0];
5216
5217 //Check the prov color checkbox when raddcolorpicker was changed
5218 var chkProvColor = $("#" + row.id).find("#chkProviderColor")[0];
5219
5220
5221 //Apply colorpicker styling based on the radcolorpicker selection.
5222 var rowIndex = $(sender._element).closest('tr').index();
5223 var masterTable = GetGrdProvInfoMasterTableView();
5224 var colorPickerId = masterTable.get_dataItems()[rowIndex].findControl("cpProvList")._iconElement.id;
5225
5226 //TJF 7/5/2016 - Save checked status
5227 var PK = masterTable.getCellByColumnUniqueName(masterTable.get_dataItems()[rowIndex], "PK").innerHTML;
5228
5229 if (gBoolSaveColor || selectedColor == null) {
5230 //We will save the selected color to the server via AJAX
5231 var provId = $(chkProvColor).closest('tr').children('td:first').text();
5232 if (selectedColor == null) {
5233 SaveColorOnServerAjax(provId, ProvColorType.CLEAR);
5234 } else {
5235 SaveColorOnServerAjax(provId, selectedColor);
5236 }
5237
5238 }
5239
5240 //Set prov color column based on the color selected on the Rad Picker
5241 SetDataProvColor(rowIndex, selectedColor);
5242
5243 //If colorpicker selected comes back as null, it means the 'clear' button was clicked so we will call
5244 //the SetNoColor() which will set the picker styling to transparent. If a valid color was picked we will call the
5245 //SetPermanentColor() which will apply a black border to the radcolorpicker icon square
5246 //TJF 7/14/2016 - This needs to be in a timeout function or border will be overridden by the color changing
5247 setTimeout(function () {
5248 var curColType = masterTable.getCellByColumnUniqueName(masterTable.get_dataItems()[rowIndex], "ColorType").innerHTML;
5249 if (selectedColor == null) {
5250 selectedColor = ProvColorType.CLEAR;
5251 SetNoColor(colorPickerId);
5252 chkProvColor.checked = false;
5253
5254 } /*else if (curColType == "temporary") {
5255 SetTemporaryColor(colorPickerId);
5256 chkProvColor.checked = true;
5257 }*/ else {
5258 SetPermanentColor(colorPickerId);
5259 chkProvColor.checked = true;
5260 }
5261
5262 gCheckedProviders[PK] = chkProvColor.checked;
5263
5264 //Highlight the provider selected on the calendar
5265 highlightProviderHandlerGenericForGrid(chkProvColor);
5266 }, 0);
5267}
5268
5269//NMM 3/7/2016 - Gets called when you check/uncheck the box on the providersGrid that highlights providers on the calendar
5270//VJF 09/13/2017 - Added param bIsProviderDockChk to know whether call is from checkbox on provider dock
5271function ChkProvColorClicked(chkProvColor, bIsProviderDockChk) {
5272 var masterTable = GetGrdProvInfoMasterTableView();
5273 var rowIndex = FindRowIndexByCheckboxChecked(chkProvColor);
5274 var selectedColor = masterTable.get_dataItems()[rowIndex].findControl("cpProvList").get_selectedColor();
5275
5276 //TJF 7/5/2016 - Get PK for setting checked in
5277 var PK = masterTable.getCellByColumnUniqueName(masterTable.get_dataItems()[rowIndex], "PK").innerHTML;
5278
5279 //Save the selected color to the hidden data-provider-col on the grid
5280 SetDataProvColor(rowIndex, selectedColor);
5281
5282 //Highlight the providers on the calendar that matches the checked row
5283 highlightProviderHandlerGenericForGrid(chkProvColor);
5284
5285 //Update the provider information status bar
5286 if (getAppArea() == ApplicationArea.AdminCalendar && tsMainMenu.get_selectedTab().get_value() == AdminMenuBarItem.AdminCalendar) {
5287 selectedProviderCheckedOnGrid(chkProvColor);
5288 }
5289
5290 //Apply blue highlight color on the selected row
5291 $("#" + chkProvColor.parentNode.parentNode.id).addClass("rgSelectedRow");
5292
5293 var rowIndex = FindRowIndexByCheckboxChecked(chkProvColor);
5294 var curColType = masterTable.getCellByColumnUniqueName(masterTable.get_dataItems()[rowIndex], "ColorType").innerHTML;
5295 if (curColType != "permanent") {
5296 masterTable.getCellByColumnUniqueName(masterTable.get_dataItems()[rowIndex], "ColorType").innerHTML = "temporary";
5297 }
5298
5299 //TJF 7/5/2016 - Store provider checked status
5300 gCheckedProviders[PK] = chkProvColor.checked;
5301
5302 //VJF 09/13/2017 - Show provider unavailable time when checked from the provider dock. Suppress running on indirect calls to this function.
5303 if (bIsProviderDockChk && getAppArea() == ApplicationArea.AdminCalendar && tsMainMenu.get_selectedTab().get_value() == AdminMenuBarItem.AdminCalendar) {
5304 var chkbox = document.getElementById('chkShowProvUnavailableTime');
5305 if (chkbox && chkbox.checked) {
5306 showProviderUnavailableTime(true);
5307 }
5308 }
5309}
5310
5311//NMM 3/7/2016 - This gets called on page load. This sets the styling/appearance of the RadColorPicker to
5312//distinguish that a permanent color was saved or no color was selected. Permanent color styling is a black border
5313//around the color square. No color is determined with a transparent square styling.
5314function SetProvGrdRadColorPickerStylingOnLoad() {
5315 var masterTable = GetGrdProvInfoMasterTableView();
5316 for (var i = 0; i < masterTable.get_dataItems().length; i++) {
5317
5318 //Get the selected color hex code of the RadColorPicker and it's Id
5319 var colorPicker = masterTable.get_dataItems()[i].findControl("cpProvList")
5320 var selectedColor = colorPicker._selectedColor;
5321 var colorPickerId = colorPicker._iconElement.id;
5322 var colorType = masterTable.getCellByColumnUniqueName(masterTable.get_dataItems()[i], "ColorType").innerHTML;
5323
5324 //Set the styling according to whether there is a selected color provided
5325 if (colorType == "permanent") {
5326 SetPermanentColor(colorPickerId);
5327 } else if (selectedColor == null) {
5328 SetNoColor(colorPickerId);
5329 } else {
5330 SetTemporaryColor(colorPickerId);
5331 }
5332
5333 }
5334}
5335
5336//NMM 3/7/2016 - Set the provider's selected color in a hidden column on the grid
5337function SetDataProvColor(rowIndex, selectedColor) {
5338 var masterTable = GetGrdProvInfoMasterTableView();
5339 var row = masterTable.get_dataItems()[rowIndex];
5340 var dataProvColorCol = masterTable.getCellByColumnUniqueName(row, "DataProvColor");
5341
5342 var PK = masterTable.getCellByColumnUniqueName(row, "PK").innerHTML;
5343
5344 dataProvColorCol.innerHTML = selectedColor;
5345 //TJF 7/5/2016 - Store selected color
5346 gProvColors[PK] = selectedColor;
5347}
5348
5349//NMM 3/7/2016 - Get the Data-Provider-Color from the hidden column on the Provider Grid
5350function GetDataProvColor(chkProvColor) {
5351 var masterTable = GetGrdProvInfoMasterTableView();
5352 var rowIndex = $(chkProvColor).closest('tr').index();
5353 var row = masterTable.get_dataItems()[rowIndex];
5354 var dataProvColorCol = masterTable.getCellByColumnUniqueName(row, "DataProvColor");
5355 var selectedColor = dataProvColorCol.innerHTML;
5356
5357 //On IE, an empty cell value is treated as "null" text. As opposed to chrome which is equivalent to "".
5358 //So we just manually set it as "".
5359 if (selectedColor == "null") {
5360 if (selectedColor == "null") {
5361 selectedColor = "";
5362 }
5363 }
5364 return selectedColor;
5365}
5366
5367//TJF 11/23/2015 - Highlight the next provider after the current checked provider
5368//VJF 09/13/2017 - Added support for selected row navigation when checkbox navigation is not relevant
5369function handleKeydownOnProviderListOnGrid(e, isDown) {
5370 if (gRestoreProvDock) {
5371
5372 //Prevent scrolling down when provider list is up, we use this hotkey
5373 e.preventDefault(); e.stopPropagation();
5374 var masterTable = GetGrdProvInfoMasterTableView();
5375 var nCountCheckedRows = 0; var checkedRow; var chkSelected; var rowIndex;
5376 var nSelectedRowIndex = -1;
5377
5378 var rowLength = masterTable.get_dataItems().length;
5379
5380 //Check how many checkboxes are selected
5381 for (var i = 0; i < rowLength; i++) {
5382 var row = masterTable.get_dataItems()[i];
5383
5384 //VJF 09/13/2017 - Keep track of selected row and clear
5385 if (row.get_selected()) {
5386 nSelectedRowIndex = i;
5387 row.set_selected(false);
5388 }
5389
5390 var rowId = row._element.id;
5391 var checkbox = $("#" + rowId).find("#chkProviderColor")[0]; //.find() returns a list of elements, we need to get the first one.
5392 if (checkbox.checked) {
5393 nCountCheckedRows++;
5394 checkedRow = row;
5395 chkSelected = checkbox;
5396 rowIndex = i;
5397 }
5398 }
5399
5400 //Determine navigations context checkbox vs. selected row
5401 var isCheckboxNavContext = (nCountCheckedRows == 1); //Single checkbox checked
5402 var isSelectNavContext = !isCheckboxNavContext && nSelectedRowIndex >= 0; //Not checkbox navigation and a row is selected
5403
5404 if (isSelectNavContext) {
5405 rowIndex = nSelectedRowIndex;
5406 }
5407
5408 if (isCheckboxNavContext || isSelectNavContext) {
5409 var oldChkBox;
5410
5411 //IE Automatically selects the next element, chrome does not
5412 oldChkBox = chkSelected;
5413
5414 if (isSelectNavContext || (chkSelected && chkSelected.checked)) {
5415 var newRowSelected;
5416
5417 if (isDown) {
5418 rowIndex += 1;
5419 newRowSelected = masterTable.get_dataItems()[rowIndex];
5420
5421 } else {
5422 rowIndex -= 1;
5423 newRowSelected = masterTable.get_dataItems()[rowIndex];
5424 }
5425
5426 //Go to next visible element in case of pract filter hiding
5427 if (newRowSelected && $(newRowSelected._element).is(":hidden")) {
5428 var i = 1;
5429 while (newRowSelected && $(newRowSelected._element).is(":hidden")) {
5430 if (isDown) {
5431 newRowSelected = masterTable.get_dataItems()[rowIndex + i];
5432 } else {
5433 newRowSelected = masterTable.get_dataItems()[rowIndex - i];
5434 }
5435 i++;
5436 }
5437 }
5438
5439 if (isSelectNavContext) {
5440 newRowSelected.set_selected(true);
5441 } else {
5442 var newChkBox;
5443 //Check to make sure that the new row selected is within the griditem length of the radgrid. If it is, we move to that position.
5444 if (!isSelectNavContext && rowLength > rowIndex && rowIndex > -1) {
5445 newChkBox = $("#" + newRowSelected._element.id).find("#chkProviderColor")[0]; //Get the checkbox object
5446 } else {
5447 checkedRow.set_selected(true);
5448 }
5449
5450 if (newChkBox) {
5451
5452 //TJF 8/10/2017 - Set new row selected
5453 newRowSelected.set_selected(true);
5454
5455 //TJF 7/7/2016 - Update checked array
5456 gCheckedProviders = [];
5457 var newPK = masterTable.getCellByColumnUniqueName(newRowSelected, "PK").innerHTML;
5458 gCheckedProviders[newPK] = true;
5459
5460 oldChkBox.checked = false;
5461 highlightProviderHandlerGenericForGrid(oldChkBox);
5462 newChkBox.checked = true;
5463 newChkBox.value = "";
5464
5465 highlightProviderHandlerGenericForGrid(newChkBox);
5466
5467 //VJF 09/25/2017 - When checkboxes change unavailable time needs to be evaluated, if applicable.
5468 var chkbox = document.getElementById('chkShowProvUnavailableTime');
5469 if ((chkbox && chkbox.checked)) {
5470 showProviderUnavailableTime(true);
5471 }
5472 }
5473 }
5474 }
5475 }
5476 }
5477}
5478
5479//TJF 11/23/2015 - This version only requires one parameter and is used in multiple places
5480function highlightProviderHandlerGenericForGrid(chkProvColor) {
5481 //Method only used on the Location Schedules view
5482
5483 var isProvViewLoc = (getAppArea() == ApplicationArea.ProvCalendar && tsMainMenu.get_selectedTab().get_value() == ProviderScheduleView.Location) ||
5484 (getAppArea() == ApplicationArea.ProvCalendar && tsMainMenu.get_selectedTab().get_value() == ProviderScheduleView.SwapExchange);
5485 var isSchdViewCal = (getAppArea() == ApplicationArea.AdminCalendar && tsMainMenu.get_selectedTab().get_value() == AdminMenuBarItem.AdminCalendar);
5486
5487 //Selected providers maybe be restored on load, so make sure the page supports it.
5488 if (isProvViewLoc || isSchdViewCal) {
5489 //highlighting is supported
5490 } else {
5491 //highlighting is not supported
5492 return;
5493 }
5494
5495 //When a Schedule View Calendar provider is checked select it
5496 if (isSchdViewCal && chkProvColor.checked) {
5497 chkProvColor.checked = true;
5498 }
5499
5500 var bSetProvAsSelected = true;
5501
5502 if (!chkProvColor.checked) {
5503 HighlightProviderOnGrid(chkProvColor, bSetProvAsSelected); //VJF 12/21/2015 - Since it is a single provider highlight, set them as selected
5504 }
5505
5506 //Apply highlighting for shift groups
5507 if (gColorContext == COLORCONTEXT.PractType) {
5508 restorePractTypeColors();
5509 } else if (gColorContext == COLORCONTEXT.ShiftGroup) {
5510 restoreShiftGroupColors();
5511 } else if (gColorContext == COLORCONTEXT.ShiftStatus) {
5512 restoreShiftStatusHighlighting();
5513 }
5514
5515
5516 //TJF 10/3/2016 - Restore highlighting for all providers last so provider is on top
5517 highlightCheckedProvidersOnGrid();
5518 //HighlightProviderOnGrid(chkProvColor, bSetProvAsSelected); //VJF 12/21/2015 - Since it is a single provider highlight, set them as selected
5519}
5520
5521//Highlight the provider on the calendar using the GridProvider
5522//VJF 09/22/2107 - Style display type of rsAptContent div changed from block to table since containing shift and assignment divs are of type table-cell.
5523function HighlightProviderOnGrid(chkProvColor, bSetProvAsSelected) {
5524
5525 //Get provider id(PK) of the selected row in radgrid
5526 var provId = $(chkProvColor).closest('tr').children('td:first').text();
5527 var isChecked = chkProvColor.checked;
5528 var selectedColor = GetDataProvColor(chkProvColor);
5529
5530 //VJF 12/21/2015 - Added optional parameter flag to determine when the highlighted provider should be selected,
5531 //this typically only occurs when a single provider is being processed, as opposed to a restore which processes multiples.
5532 if (typeof bSetProvAsSelected != 'boolean') bSetProvAsSelected = false;
5533
5534 //Set temporary color for the provider if the user did not pick from the radcolorpicker
5535 if (isChecked) {
5536 if (selectedColor == null || selectedColor == "" || selectedColor == "clear" || selectedColor == " ") {
5537
5538 //Get assigned color
5539 selectedColor = getHighlightColorOnGrid(chkProvColor);
5540
5541 //Change RadColorpicker's selected color
5542 var masterTable = GetGrdProvInfoMasterTableView();
5543 var rowIndex = $(chkProvColor).closest('tr').index();
5544
5545 var colorpicker = masterTable.get_dataItems()[rowIndex].findControl("cpProvList");
5546 colorpicker.remove_colorChange(ProvRadColorPickerChanged);
5547 colorpicker.set_selectedColor(selectedColor);
5548 colorpicker.add_colorChange(ProvRadColorPickerChanged);
5549
5550 //Set colorpicker icon border as temporary color
5551 var colorPickerId = masterTable.get_dataItems()[rowIndex].findControl("cpProvList")._iconElement.id;
5552 masterTable.getCellByColumnUniqueName(masterTable.get_dataItems()[rowIndex], "ColorType").innerHTML = "temporary";
5553 SetTemporaryColor(colorPickerId);
5554 }
5555 }
5556
5557 if (provId > -1) {
5558
5559 var cssClass = '.evtProv';
5560 var bIgnoreIdMatch = false;
5561 if (provId == 0) {
5562 cssClass = '.evtOpen';
5563 bIgnoreIdMatch = true;
5564 }
5565
5566 //Reuse the list of provider divs for subsequent calls when it has already been loaded
5567 if (!gLocProviderNodeList || true) {
5568 //Get all elements by class name
5569 gLocProviderNodeList = document.querySelectorAll(cssClass); //getElementsByClassName not supported in IE8 so querySelectorAll used instead
5570 }
5571
5572 var classHighlight = ' highlight';
5573
5574 for (i = 0; i < gLocProviderNodeList.length; i++) {
5575 if (bIgnoreIdMatch || gLocProviderNodeList[i].id == provId) {
5576
5577 var bShowHighlighting = true;
5578 //VJF 04/19/2017 - Don't highlight open shifts that aren't counted
5579 //VJF 09/20/2017 - Allow highlighting override while still allowing to hide/show
5580 if (bIgnoreIdMatch && $(gLocProviderNodeList[i]).closest('.rsApt').hasClass("not-counted")) {
5581 bShowHighlighting = false;
5582 }
5583
5584 if (isChecked) {
5585 if (bShowHighlighting) {
5586 gLocProviderNodeList[i].parentNode.style.backgroundColor = selectedColor;
5587
5588 if (gLocProviderNodeList[i].parentNode.className.indexOf(classHighlight) == -1) {
5589 gLocProviderNodeList[i].parentNode.className += classHighlight;
5590 }
5591 }
5592
5593 //Checked providers are always visible
5594 gLocProviderNodeList[i].parentNode.style.display = "table";
5595
5596 } else {
5597
5598 if (bShowHighlighting) {
5599 //remove style which contains the added bgcolor and class that contains the highlight
5600 //gLocProviderNodeList[i].parentNode.removeAttribute("style");
5601 gLocProviderNodeList[i].parentNode.style.backgroundColor = '';
5602 gLocProviderNodeList[i].parentNode.className = gLocProviderNodeList[i].parentNode.className.replace(classHighlight, "");
5603 }
5604
5605 //Hide when only showing selected providers
5606 if (gShowOnlySelectedProviders) {
5607 gLocProviderNodeList[i].parentNode.style.display = "none";
5608 } else {
5609 gLocProviderNodeList[i].parentNode.style.display = "table";
5610 }
5611
5612 }
5613 }
5614 }
5615 }
5616}
5617
5618//TJF 9/30/2016 - Runs when a calendar reload takes place to reapply coloring
5619function RestoreShiftGroupHighlightingAfterCalReload(arrShiftGroupTrees, bApplyColors) {
5620 var ShiftGroupTreeEntries = [];
5621 arrShiftGroupColorStacksByPK = [];
5622 //Add relevant entries to list
5623 for (var i = 0; i < arrShiftGroupTrees.length; i++) {
5624 var tree = arrShiftGroupTrees[i];
5625 //TJF 11/2/2016 - Trees with no entries are bit relevant as there is nothing to highlight
5626 if (tree.Entries.length == 0) {
5627 continue;
5628 }
5629 var LocId = "-1";
5630 if (!tree.IsSystem) {
5631 LocId = "" + tree.Entries[0].LocId;
5632 }
5633
5634 //TJF 3/29/2018 - Fix color keys to reflect key change on backend
5635 var colorsKey = tree.ShiftGroupID + "|" + LocId;
5636 if (gShiftGroupColors[colorsKey]) {
5637
5638 if (gShiftGroupColors[colorsKey].isChecked) {
5639 ShiftGroupTreeEntries.push(tree);
5640 }
5641 }
5642 }
5643
5644 if (ShiftGroupTreeEntries.length < 1) {
5645 return;
5646 }
5647
5648 $(".RadScheduler").each(function (index) {
5649 var scheduler = $find($(this).attr('id'));
5650 (scheduler.get_appointments()).forEach(function (apt) {
5651 var attribs = apt.get_attributes();
5652 var selectedShiftPrimaryKey = attribs.getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_APPT_ID);
5653 var typeId = selectedShiftPrimaryKey.substring(0, selectedShiftPrimaryKey.indexOf("|")); //Type ID is the first part of the primary key
5654 var $apt = $(apt.get_element());
5655 var rsAptContent = $apt.find(".rsAptContent")[0]; //This is the element highlights are applied to
5656 var aptDate = apt.get_start(); //Use start date for comparison with the exception date
5657 var locationId = selectedShiftPrimaryKey.substring(selectedShiftPrimaryKey.indexOf("|") + 1, selectedShiftPrimaryKey.indexOf("|", selectedShiftPrimaryKey.indexOf("|") + 1));
5658
5659 //Add or remove color from color stack
5660 if (!arrShiftGroupColorStacksByPK[selectedShiftPrimaryKey]) {
5661 arrShiftGroupColorStacksByPK[selectedShiftPrimaryKey] = [];
5662 }
5663
5664 for (var j = 0; j < ShiftGroupTreeEntries.length; j++) {
5665 var ShiftGroupTreeEntry = ShiftGroupTreeEntries[j];
5666
5667 //key to arrShiftGroupColors
5668 var sColorsKey = "" + ShiftGroupTreeEntry.ShiftGroupID;
5669 if (ShiftGroupTreeEntry.IsSystem) {
5670 sColorsKey += "|-1";
5671 } else {
5672 var locId = ShiftGroupTreeEntry.Entries[0].LocId;
5673 sColorsKey += + "|" + locId;
5674 }
5675
5676 (ShiftGroupTreeEntry.Entries).forEach(function (treeEntry) {
5677 var shiftTypeId = treeEntry.STId;
5678 var exDate;
5679
5680 //Date is only set in the case of an exception
5681 if (treeEntry.IsEx) {
5682 exDate = new Date(treeEntry.ExDate);
5683 }
5684
5685 if (!treeEntry.IsEx && (typeId == shiftTypeId) && (locationId == treeEntry.LocId) && (GetBitmaskForDay(apt.get_start().getDay()) & treeEntry.DoW)) {
5686 if (gShiftGroupColors[sColorsKey] && gShiftGroupColors[sColorsKey].isChecked) {
5687
5688 arrShiftGroupColorStacksByPK[selectedShiftPrimaryKey].push(gShiftGroupColors[sColorsKey].color);
5689
5690 arrShiftGroupColorStacksByPK[selectedShiftPrimaryKey].sort(function (a, b) {
5691 //Sort open at top always
5692 var aIndex = gShiftGroupColorOrder.indexOf(a);
5693 var bIndex = gShiftGroupColorOrder.indexOf(b);
5694 return (aIndex > bIndex) ? 1 : -1;
5695 });
5696
5697
5698 var arrStack = arrShiftGroupColorStacksByPK[selectedShiftPrimaryKey];
5699
5700 if (bApplyColors) {
5701 AddOrRemoveHighlight(false, arrStack[arrStack.length - 1], rsAptContent, "");
5702 }
5703
5704 }
5705
5706 } else if (treeEntry.IsEx && (typeId == shiftTypeId) && (locationId == treeEntry.LocId) &&
5707 (exDate.getDate() == aptDate.getDate() && exDate.getMonth() == aptDate.getMonth() && exDate.getFullYear() == aptDate.getFullYear())) {
5708 if (gShiftGroupColors[sColorsKey] && gShiftGroupColors[sColorsKey].isChecked) {
5709
5710 arrShiftGroupColorStacksByPK[selectedShiftPrimaryKey].push(gShiftGroupColors[sColorsKey].color);
5711
5712 arrShiftGroupColorStacksByPK[selectedShiftPrimaryKey].sort(function (a, b) {
5713 //Sort open at top always
5714 var aIndex = gShiftGroupColorOrder.indexOf(a);
5715 var bIndex = gShiftGroupColorOrder.indexOf(b);
5716 return (aIndex > bIndex) ? 1 : -1;
5717 });
5718
5719 var arrStack = arrShiftGroupColorStacksByPK[selectedShiftPrimaryKey];
5720
5721 if (bApplyColors) {
5722 AddOrRemoveHighlight(false, arrStack[arrStack.length - 1], rsAptContent, "");
5723 }
5724 }
5725 }
5726
5727 });
5728 }
5729
5730 });
5731 });
5732
5733}
5734
5735//TJF 7/26/2016 - Use shift group tree array to highlight shifts. Called from AdminShiftGroup.
5736//TJF 9/19/2016 - Adding highlight is optional, otherwise just add to stack
5737var arrShiftGroupColorStacksByPK = [];
5738var gShiftGroupColors = []; //Used by AdminShiftGroups to store current color for a shift group
5739var gShiftGroupColorOrder = []; //Order in which shift group colors are applied
5740function HighlightShiftsByShiftGroup(arrShiftGroupTrees, nShiftGroupId, bIsSystem, sBgColor, bRemoveAll, bHighlight) {
5741 //Store order in which colors are applied
5742 if (!bRemoveAll) {
5743 gShiftGroupColorOrder.push(sBgColor);
5744 } else {
5745 var index = gShiftGroupColorOrder.indexOf(sBgColor);
5746 gShiftGroupColorOrder.splice(index, 1);
5747 }
5748
5749 //Scan entries to find shift group in tree, multiple trees can occur for a single shift group id
5750 var ShiftGroupTreeEntries = [];
5751 for (var i = 0; i < arrShiftGroupTrees.length; i++) {
5752 if ((arrShiftGroupTrees[i].ShiftGroupID == nShiftGroupId) && (arrShiftGroupTrees[i].IsSystem == bIsSystem)) {
5753 ShiftGroupTreeEntries.push(arrShiftGroupTrees[i]);
5754 }
5755 }
5756
5757 //Support multiple schedulers(they are used when a mode other than by month is active)
5758 $(".RadScheduler").each(function (index) {
5759 var scheduler = $find($(this).attr('id'));
5760
5761 //Loop through each appointment and match it to a tree item if possible and highlight
5762 (scheduler.get_appointments()).forEach(function (apt) {
5763 var attribs = apt.get_attributes();
5764 var selectedShiftPrimaryKey = attribs.getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_APPT_ID);
5765 var typeId = selectedShiftPrimaryKey.substring(0, selectedShiftPrimaryKey.indexOf("|")); //Type ID is the first part of the primary key
5766 var $apt = $(apt.get_element());
5767 var rsAptContent = $apt.find(".rsAptContent")[0]; //This is the element highlights are applied to
5768 var aptDate = apt.get_start(); //Use start date for comparison with the exception date
5769 var locationId = selectedShiftPrimaryKey.substring(selectedShiftPrimaryKey.indexOf("|") + 1, selectedShiftPrimaryKey.indexOf("|", selectedShiftPrimaryKey.indexOf("|") + 1));
5770
5771 //Add or remove color from color stack
5772 if (!arrShiftGroupColorStacksByPK[selectedShiftPrimaryKey]) {
5773 arrShiftGroupColorStacksByPK[selectedShiftPrimaryKey] = [];
5774 }
5775
5776 //convert to hex for comparison. This allows removals to only affect the bgColor and not all appointment highlights
5777 var hexBackgroundColor = "";
5778 if (bRemoveAll && rsAptContent.style.backgroundColor != "") {
5779 hexBackgroundColor = rgb2hex(rsAptContent.style.backgroundColor);
5780 }
5781 //Iterate through each entry and see if appointment should be highlighted
5782 for (var j = 0; j < ShiftGroupTreeEntries.length; j++) {
5783 var ShiftGroupTreeEntry = ShiftGroupTreeEntries[j];
5784 (ShiftGroupTreeEntry.Entries).forEach(function (treeEntry) {
5785
5786 var shiftTypeId = treeEntry.STId;
5787 var exDate;
5788
5789 //Date is only set in the case of an exception
5790 if (treeEntry.IsEx) {
5791 exDate = new Date(treeEntry.ExDate);
5792 }
5793
5794 if (!treeEntry.IsEx && (typeId == shiftTypeId) && (locationId == treeEntry.LocId) && (GetBitmaskForDay(apt.get_start().getDay()) & treeEntry.DoW)) {
5795 //TJF 7/29/2016 - Handle a normal shift group
5796 if (bHighlight) {
5797 AddOrRemoveHighlight(bRemoveAll, sBgColor, rsAptContent, hexBackgroundColor);
5798 }
5799
5800 //Add color to stack
5801 if (!bRemoveAll) {
5802 arrShiftGroupColorStacksByPK[selectedShiftPrimaryKey].push(sBgColor);
5803 } else {
5804 //remove color from stack
5805 var index = arrShiftGroupColorStacksByPK[selectedShiftPrimaryKey].indexOf(sBgColor);
5806
5807 arrShiftGroupColorStacksByPK[selectedShiftPrimaryKey].splice(index, 1);
5808 }
5809
5810 //Restore previous color
5811 if (bRemoveAll && arrShiftGroupColorStacksByPK[selectedShiftPrimaryKey].length != 0) {
5812 var stack = arrShiftGroupColorStacksByPK[selectedShiftPrimaryKey];
5813 if (bHighlight) {
5814 AddOrRemoveHighlight(false, stack[stack.length - 1], rsAptContent, hexBackgroundColor);
5815 }
5816 }
5817
5818 //Time comparison done this way because aptDate is in Eastern Daylight while exception date is standard eastern time
5819 } else if (treeEntry.IsEx && (typeId == shiftTypeId) && (locationId == treeEntry.LocId) &&
5820 (exDate.getDate() == aptDate.getDate() && exDate.getMonth() == aptDate.getMonth() && exDate.getFullYear() == aptDate.getFullYear())) {
5821 //TJF 7/29/2016 - Handle an exception
5822 if (bHighlight) {
5823 AddOrRemoveHighlight(bRemoveAll, sBgColor, rsAptContent, hexBackgroundColor);
5824 }
5825 //Add color to stack
5826 if (!bRemoveAll) {
5827 arrShiftGroupColorStacksByPK[selectedShiftPrimaryKey].push(sBgColor);
5828 } else {
5829 //remove color from stack
5830 var index = arrShiftGroupColorStacksByPK[selectedShiftPrimaryKey].indexOf(sBgColor);
5831 arrShiftGroupColorStacksByPK[selectedShiftPrimaryKey].splice(index, 1);
5832 }
5833
5834 //Restore previous color
5835 if (bRemoveAll && arrShiftGroupColorStacksByPK[selectedShiftPrimaryKey].length != 0) {
5836 var stack = arrShiftGroupColorStacksByPK[selectedShiftPrimaryKey];
5837 if (bHighlight) {
5838 AddOrRemoveHighlight(false, stack[stack.length - 1], rsAptContent, hexBackgroundColor);
5839 }
5840 }
5841 }
5842 });
5843 }
5844 });
5845 });
5846}
5847
5848//Format: arrStatusColors[nStatusId] = color
5849var arrStatusColors = []; //All colors assigned by status id. They may not be selected yet.
5850var arrShiftStatusColorStack = []; //list of all colors assigned by shift in case a color overrides the existing color. The color can later be restored when the overriding color is removed.
5851var g_arrShiftStatusIds = []; //All shift statuses selected. Used for restoring the color when a reload happens.
5852
5853//TJF 3/27/2018 - Highlight the shifts by selected status and add to a color stack for tracking
5854function HighlightByShiftStatus(arrShiftStatusIds, bRemoveAll, bOnlySetColorStack) {
5855 arrShiftStatusColorStack = []; //reset color stack
5856 g_arrShiftStatusIds = arrShiftStatusIds;
5857
5858 $(".RadScheduler").each(function (index) {
5859 var scheduler = $find($(this).attr('id'));
5860
5861 //Loop through each appointment and highlight by status
5862 (scheduler.get_appointments()).forEach(function (apt) {
5863 var attribs = apt.get_attributes();
5864 var nShiftStatusId = parseInt(attribs.getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_SHIFT_STATUS_ID));
5865 var $apt = $(apt.get_element());
5866 var rsAptContent = $apt.find(".rsAptContent")[0];
5867 var hexBackgroundColor = "";
5868 var selectedShiftPrimaryKey = attribs.getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_APPT_ID);
5869
5870 if (bRemoveAll && rsAptContent.style.backgroundColor != "") {
5871 hexBackgroundColor = rgb2hex(rsAptContent.style.backgroundColor);
5872 }
5873
5874 //Init stack from shift primary key
5875 if (!arrShiftStatusColorStack[selectedShiftPrimaryKey]) {
5876 arrShiftStatusColorStack[selectedShiftPrimaryKey] = [];
5877 }
5878
5879
5880 //Highlight if shift is selected and has a color
5881 if (arrShiftStatusIds.indexOf(nShiftStatusId) > -1 && arrStatusColors[nShiftStatusId] != null) {
5882 if (!bOnlySetColorStack) {
5883 AddOrRemoveHighlight(bRemoveAll, arrStatusColors[nShiftStatusId], rsAptContent, hexBackgroundColor);
5884 }
5885
5886 if (!bRemoveAll) {
5887 arrShiftStatusColorStack[selectedShiftPrimaryKey].push(arrStatusColors[nShiftStatusId]);
5888 } else {
5889 var index = arrShiftStatusColorStack[selectedShiftPrimaryKey].indexOf(arrStatusColors[nShiftStatusId]);
5890 arrShiftStatusColorStack[selectedShiftPrimaryKey].splice(index, 1);
5891
5892 }
5893
5894 }
5895
5896 });
5897
5898 });
5899
5900}
5901
5902//TJF 3/27/2018 - Restore status highlighting after changes to the calendar
5903function restoreShiftStatusHighlighting() {
5904 if (!gApplyColorsChecked) {
5905 return;
5906 }
5907
5908 $(".RadScheduler").each(function (index) {
5909 var scheduler = $find($(this).attr('id'));
5910
5911 //Loop through each appointment and match it to a tree item if possible and highlight
5912 (scheduler.get_appointments()).forEach(function (apt) {
5913 var attribs = apt.get_attributes();
5914 var selectedShiftPrimaryKey = attribs.getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_APPT_ID);
5915 var typeId = selectedShiftPrimaryKey.substring(0, selectedShiftPrimaryKey.indexOf("|")); //Type ID is the first part of the primary key
5916 var $apt = $(apt.get_element());
5917 var rsAptContent = $apt.find(".rsAptContent")[0]; //This is the element highlights are applied to
5918 var aptDate = apt.get_start(); //Use start date for comparison with the exception date
5919 var locationId = selectedShiftPrimaryKey.substring(selectedShiftPrimaryKey.indexOf("|") + 1, selectedShiftPrimaryKey.indexOf("|", selectedShiftPrimaryKey.indexOf("|") + 1));
5920
5921 if (arrShiftStatusColorStack[selectedShiftPrimaryKey] && arrShiftStatusColorStack[selectedShiftPrimaryKey].length > 0) {
5922 var stack = arrShiftStatusColorStack[selectedShiftPrimaryKey];
5923 var sBgColor = stack[stack.length - 1];
5924 AddOrRemoveHighlight(false, sBgColor, rsAptContent, "");
5925 }
5926
5927 });
5928 });
5929
5930
5931}
5932
5933//TJF 9/16/2016 - Used to recolor shifts after apply colors has been checked on
5934var gApplyColorsChecked = false; //Keeping track off apply colors checkbox on admin shift groups
5935function setApplyColorsChecked(bChecked) { gApplyColorsChecked = bChecked; }
5936
5937function restoreShiftGroupColors() {
5938 if (!gApplyColorsChecked) {
5939 return;
5940 }
5941
5942 $(".RadScheduler").each(function (index) {
5943 var scheduler = $find($(this).attr('id'));
5944
5945 //Loop through each appointment and match it to a tree item if possible and highlight
5946 (scheduler.get_appointments()).forEach(function (apt) {
5947 var attribs = apt.get_attributes();
5948 var selectedShiftPrimaryKey = attribs.getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_APPT_ID);
5949 var typeId = selectedShiftPrimaryKey.substring(0, selectedShiftPrimaryKey.indexOf("|")); //Type ID is the first part of the primary key
5950 var $apt = $(apt.get_element());
5951 var rsAptContent = $apt.find(".rsAptContent")[0]; //This is the element highlights are applied to
5952 var aptDate = apt.get_start(); //Use start date for comparison with the exception date
5953 var locationId = selectedShiftPrimaryKey.substring(selectedShiftPrimaryKey.indexOf("|") + 1, selectedShiftPrimaryKey.indexOf("|", selectedShiftPrimaryKey.indexOf("|") + 1));
5954
5955 if (arrShiftGroupColorStacksByPK[selectedShiftPrimaryKey] && arrShiftGroupColorStacksByPK[selectedShiftPrimaryKey].length > 0) {
5956 var stack = arrShiftGroupColorStacksByPK[selectedShiftPrimaryKey];
5957 var sBgColor = stack[stack.length - 1];
5958 AddOrRemoveHighlight(false, sBgColor, rsAptContent, "");
5959 }
5960
5961 });
5962 });
5963}
5964
5965//TJF 9/19/2016 - Recolor shifts after apply colors has been checked
5966function restorePractTypeColors() {
5967 if (!gApplyColorsChecked) {
5968 return;
5969 }
5970 var masterTable = GetGrdProvInfoMasterTableView();
5971 sBgColor = "";
5972
5973 for (var i = 0; i < masterTable.get_dataItems().length; i++) {
5974
5975 var row = masterTable.get_dataItems()[i];
5976 //Provider primary key
5977 var prov_pk = masterTable.getCellByColumnUniqueName(row, "PK").innerHTML;
5978
5979 //If primary key is 0, It means it is an open shift. We will include this when filtering.
5980 if (prov_pk == OPEN_SHIFT_ID) {
5981 continue;
5982 }
5983
5984 //Check if provider has pract type and if so, highlight
5985 if (arrPractTypeColorStacksByPK[prov_pk]) {
5986 if (arrPractTypeColorStacksByPK[prov_pk].length > 0) {
5987 var colorStack = arrPractTypeColorStacksByPK[prov_pk];
5988 sBgColor = colorStack[colorStack.length - 1];
5989 } else {
5990 continue;
5991 }
5992 var cssClass = '.evtProv';
5993
5994 //Get all elements by class name
5995 gLocProviderNodeList = document.querySelectorAll(cssClass);
5996
5997 var classHighlight = ' highlight';
5998
5999 //Iterate through appointments with providers assigned. Highlight shifts if provider primary key is in list of provider pract types.
6000 for (var j = 0; j < gLocProviderNodeList.length; j++) {
6001 //Check if provider on shift has the practitioner type
6002 if (gLocProviderNodeList[j].id == prov_pk) {
6003 //Only add the highlight if highlighting is enabled
6004
6005 gLocProviderNodeList[j].parentNode.style.backgroundColor = sBgColor;
6006
6007 if (gLocProviderNodeList[j].parentNode.className.indexOf(classHighlight) == -1) {
6008 gLocProviderNodeList[j].parentNode.className += classHighlight;
6009 }
6010
6011
6012 }
6013
6014 }
6015 }
6016
6017 }
6018
6019}
6020
6021//TJF 8/1/2016 - Used for highlighting a single appointment
6022function AddOrRemoveHighlight(bRemoveAll, sBgColor, rsAptContent, hexBackgroundColor) {
6023 var classHighlight = ' highlight';
6024 if (!bRemoveAll) {
6025 rsAptContent.style.backgroundColor = sBgColor;
6026 if (rsAptContent.className.indexOf(classHighlight) == -1) {
6027 rsAptContent.className += classHighlight;
6028 }
6029 } else if (sBgColor.toLowerCase() == hexBackgroundColor) {
6030 //Remove color
6031 rsAptContent.style.backgroundColor = '';
6032 rsAptContent.className = rsAptContent.className.replace(classHighlight, "");
6033 }
6034}
6035
6036//TJF 7/28/2016 - This is used to turn a normal day from the get_day method into the corresponding bitmask value
6037function GetBitmaskForDay(dayOfWeek) {
6038 var bitMaskValue;
6039 switch (dayOfWeek) {
6040 case 0:
6041 bitMaskValue = 64;
6042 break;
6043 case 1:
6044 bitMaskValue = 1;
6045 break;
6046 case 2:
6047 bitMaskValue = 2;
6048 break;
6049 case 3:
6050 bitMaskValue = 4;
6051 break;
6052 case 4:
6053 bitMaskValue = 8;
6054 break;
6055 case 5:
6056 bitMaskValue = 16;
6057 break;
6058 case 6:
6059 bitMaskValue = 32;
6060 }
6061
6062 return bitMaskValue;
6063}
6064
6065//TJF 7/20/2016 - Called from AdminShiftGroups, this highlights providers on shifts by practitioner type
6066//TJF 9/19/2016 - Adding highlight is optional, otherwise just add to stack
6067var arrPractTypeColorStacksByPK = [];
6068function HighlightProvidersByPractType(practTypePrimaryKey, sBgColor, bRemoveAll, bHighlight) {
6069 var masterTable = GetGrdProvInfoMasterTableView();
6070 for (var i = 0; i < masterTable.get_dataItems().length; i++) {
6071 if (sBgColor == null) {
6072 sBgColor = "";
6073 }
6074
6075 var row = masterTable.get_dataItems()[i];
6076
6077 var sPractTypeIds = masterTable.getCellByColumnUniqueName(row, "DataPractTypeIds").innerHTML;
6078 //Provider primary key
6079 var prov_pk = masterTable.getCellByColumnUniqueName(row, "PK").innerHTML;
6080
6081 //Init stack fro prov id
6082 if (!arrPractTypeColorStacksByPK[prov_pk]) {
6083 arrPractTypeColorStacksByPK[prov_pk] = [];
6084 }
6085
6086 //TJF 10/7/2016 - Do not add same color twice
6087 if (arrPractTypeColorStacksByPK[prov_pk].indexOf(sBgColor) > -1 && !bRemoveAll) {
6088 continue;
6089 }
6090
6091 //If primary key is 0, It means it is an open shift. We will include this when filtering.
6092 if (prov_pk == OPEN_SHIFT_ID) {
6093 continue;
6094 }
6095
6096 //get array of provider pract types
6097 var arrPractTypeIds = [];
6098 if (sPractTypeIds != undefined) {
6099 //Can be string or integer, if string convert to array
6100 if (typeof sPractTypeIds == "string") {
6101 arrPractTypeIds = sPractTypeIds.split(",");
6102 } else {
6103 arrPractTypeIds = [sPractTypeIds.toString()];
6104 }
6105 }
6106 //Check if provider has pract type and if so, highlight
6107 if (arrPractTypeIds.indexOf(practTypePrimaryKey) != -1) {
6108 var cssClass = '.evtProv';
6109
6110 //Get all elements by class name
6111 gLocProviderNodeList = document.querySelectorAll(cssClass);
6112
6113 var classHighlight = ' highlight';
6114
6115 //Add color to stack
6116 if (!bRemoveAll) {
6117 arrPractTypeColorStacksByPK[prov_pk].push(sBgColor);
6118 } else {
6119 //remove color from stack
6120 var index = arrPractTypeColorStacksByPK[prov_pk].indexOf(sBgColor);
6121 arrPractTypeColorStacksByPK[prov_pk].splice(index, 1);
6122 }
6123
6124 //Iterate through appointments with providers assigned. Highlight shifts if provider primary key is in list of provider pract types.
6125 for (var j = 0; j < gLocProviderNodeList.length && bHighlight; j++) {
6126 var hexBackgroundColor = "";
6127 if (bRemoveAll && gLocProviderNodeList[j].parentNode.style.backgroundColor != "") {
6128 hexBackgroundColor = rgb2hex(gLocProviderNodeList[j].parentNode.style.backgroundColor);
6129 }
6130
6131 //Check if provider on shift has the practitioner type
6132 if (gLocProviderNodeList[j].id == prov_pk && !bRemoveAll) {
6133 //Only add the highlight if highlighting is enabled
6134
6135 gLocProviderNodeList[j].parentNode.style.backgroundColor = sBgColor;
6136
6137 if (gLocProviderNodeList[j].parentNode.className.indexOf(classHighlight) == -1) {
6138 gLocProviderNodeList[j].parentNode.className += classHighlight;
6139 }
6140
6141
6142 } else if (gLocProviderNodeList[j].id == prov_pk && sBgColor.toLowerCase() == hexBackgroundColor && bRemoveAll) {
6143
6144 //gLocProviderNodeList[j].parentNode.removeAttribute("style");
6145 gLocProviderNodeList[j].parentNode.style.backgroundColor = '';
6146 gLocProviderNodeList[j].parentNode.className = gLocProviderNodeList[j].parentNode.className.replace(classHighlight, "");
6147
6148 //Use previous color in stack
6149 if (arrPractTypeColorStacksByPK[prov_pk].length > 0) {
6150 var stack = arrPractTypeColorStacksByPK[prov_pk];
6151 gLocProviderNodeList[j].parentNode.style.backgroundColor = stack[stack.length - 1];
6152
6153 if (gLocProviderNodeList[j].parentNode.className.indexOf(classHighlight) == -1) {
6154 gLocProviderNodeList[j].parentNode.className += classHighlight;
6155 }
6156 }
6157
6158 }
6159
6160 }
6161 }
6162
6163 }
6164}
6165
6166//TJF 9/16/2016 - Reset color stacks on close. Called from admin shift groups.
6167function ResetColorStacks(bApplyColorsChecked) {
6168 arrPractTypeColorStacksByPK = [];
6169 arrShiftGroupColorStacksByPK = [];
6170 arrShiftStatusColorStack = [];
6171 arrStatusColors = [];
6172 setApplyColorsChecked(bApplyColorsChecked);
6173}
6174
6175function ResetShiftGroupColorStack() {
6176 arrShiftGroupColorStacksByPK = [];
6177}
6178
6179function ResetPractTypeColorStack() {
6180 arrPractTypeColorStacksByPK = [];
6181}
6182
6183function ResetShiftStatusColorStack() {
6184 arrShiftStatusColorStack = [];
6185}
6186
6187//TJF 10/10/2016 - Reset all global shift group color tracking
6188function ResetShiftGroupVars(bResetColorContext) {
6189 gShiftGroupColors = [];
6190 gShiftGroupColorOrder = [];
6191 if (bResetColorContext) {
6192 gColorContext = COLORCONTEXT.ShiftGroup;
6193 }
6194}
6195
6196//TJF 7/21/2016 - Used by shift group filter to clear all highlighting
6197function RemoveAllHighlighting() {
6198 var cssClass = '.rsAptContent';
6199 gLocProviderNodeList = document.querySelectorAll(cssClass);
6200 var classHighlight = ' highlight';
6201 for (var j = 0; j < gLocProviderNodeList.length; j++) {
6202
6203 $(gLocProviderNodeList[j]).css({ 'background-color': '' });
6204 //gLocProviderNodeList[j].removeAttribute("style");
6205 gLocProviderNodeList[j].className = gLocProviderNodeList[j].className.replace(classHighlight, "");
6206 }
6207
6208}
6209
6210//TJF 7/21/2016 - The following 3 functions convert rgb values into hex.
6211//This is used when checked if an rgb color is the same as a hex value when removing highlights
6212var hexDigits = new Array
6213 ("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f");
6214
6215function rgb2hex(rgb) {
6216 rgb = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
6217 return "#" + hex(rgb[1]) + hex(rgb[2]) + hex(rgb[3]);
6218}
6219
6220function hex(x) {
6221 return isNaN(x) ? "00" : hexDigits[(x - x % 16) / 16] + hexDigits[x % 16];
6222}
6223
6224//Get the highlight color for a selected provider
6225function getHighlightColorOnGrid(chkProvColor) {
6226
6227 //TJF 12/2/2015 - Check for already set prov color
6228 var selectedColor = GetDataProvColor(chkProvColor);
6229 if (selectedColor.length != 0 && selectedColor != "clear" && selectedColor != " ") {
6230 return selectedColor;
6231 }
6232
6233 //Gets the provider Id of the selected row
6234 var provId = $(chkProvColor).closest('tr').children('td:first').text();
6235 var rowIndex = FindRowIndexByCheckboxChecked(chkProvColor);
6236
6237 //Get cached value
6238 if (assignedColors[provId]) {
6239 var provColor = assignedColors[provId];
6240 SetDataProvColor(rowIndex, provColor);
6241 return provColor;
6242 }
6243
6244 //TJF 12/2/2015 - Use colors index instead of item index
6245 if (provColorsIndex < PROV_COLORS.length) {
6246
6247 var provColor = PROV_COLORS[provColorsIndex];
6248
6249 //This will be useful later when some colors are set by the server
6250 var isInUse = isColorInUse(provColor);
6251
6252 if (isInUse) {
6253 while (provColorsIndex < PROV_COLORS.length && isInUse) {
6254 provColorsIndex++;
6255 provColor = PROV_COLORS[provColorsIndex];
6256 isInUse = isColorInUse(provColor);
6257 }
6258 }
6259
6260
6261 //Store assigned color
6262 SetDataProvColor(rowIndex, provColor);
6263 assignedColors[provId] = provColor;
6264
6265 colorsInUse.push(provColor);
6266 provColorsIndex++;
6267
6268 var colorAlreadyExist = isColorAlreadyExist(provColor, provId);
6269 if (!isInUse && !colorAlreadyExist) {
6270 return provColor;
6271 }
6272 }
6273
6274 var hexcolor;
6275 //When provider's index exceeds the color array, generate a random color
6276 while (true) {
6277 //Generate random hex number
6278 hexcolor = "000000".replace(/0/g, function () { return (~~(Math.random() * 16)).toString(16); });
6279
6280 //Make sure the color contrast is in the lighter half of spectrum so text isn't obscured
6281 if (parseInt(hexcolor, 16) > 0xffffff / 2) {
6282 break;
6283 }
6284 }
6285
6286 var fullHexColor = "#" + hexcolor;
6287 assignedColors[provId] = fullHexColor;
6288
6289 return fullHexColor;
6290}
6291
6292
6293//Highlight each provider checked in the lbProviderList when viewing Location Schedule or Admin Calendar
6294//Optional bClearAll parameter will uncheck and unselect all checked providers
6295function highlightCheckedProvidersOnGrid(bClearAll) {
6296
6297 //Default bClearAll to false when it isn't supplied
6298 if (typeof bClearAll != 'boolean') bClearAll = false;
6299
6300 //JCL 9/25/2017 - Added Provider Swap Exchange
6301 if ((getAppArea() == ApplicationArea.ProvCalendar && tsMainMenu.get_selectedTab().get_value() == ProviderScheduleView.Location) ||
6302 (getAppArea() == ApplicationArea.ProvCalendar && tsMainMenu.get_selectedTab().get_value() == ProviderScheduleView.SwapExchange) ||
6303 (getAppArea() == ApplicationArea.AdminCalendar && tsMainMenu.get_selectedTab().get_value() == AdminMenuBarItem.AdminCalendar)) {
6304
6305 var grid = GetGrdProvInfo();
6306 var masterTable = GetGrdProvInfoMasterTableView();
6307
6308 if (grid) {
6309 for (var i = 0; i < masterTable.get_dataItems().length; i++) {
6310 var row = masterTable.get_dataItems()[i];
6311 var rowId = row._element.id;
6312 var checkbox = $("#" + rowId).find("#chkProviderColor")[0]; //Gets the checkbox object of the current row iteration
6313 if (checkbox.checked) {
6314 if (bClearAll) checkbox.checked = false;
6315 HighlightProviderOnGrid(checkbox);
6316 }
6317 }
6318 }
6319
6320 //TJF 7/6/2016 - Clear out checked list
6321 if (bClearAll) {
6322 gCheckedProviders = [];
6323 }
6324 }
6325}
6326
6327//TJF 1/19/2017 - Used by report criteria. Get a string list of checked providers delimited by comma
6328function getCheckedProvidersAsIdString() {
6329 //TJF 1/31/2017 - Admin accounting does not initialize tsMainMenu on load
6330 if (tsMainMenu == null) {
6331 initMainMenu();
6332 }
6333
6334 var checkString = "";
6335
6336 //VJF 03/24/2017 - Parent page must support getAppArea and provider selection
6337 if (typeof getAppArea === typeof (Function)) {
6338 if ((getAppArea() == ApplicationArea.ProvCalendar && tsMainMenu.get_selectedTab().get_value() == ProviderScheduleView.Location) ||
6339 (getAppArea() == ApplicationArea.ProvCalendar && tsMainMenu.get_selectedTab().get_value() == ProviderScheduleView.SwapExchange) ||
6340 (getAppArea() == ApplicationArea.AdminCalendar && tsMainMenu.get_selectedTab().get_value() == AdminMenuBarItem.AdminCalendar)) {
6341
6342 var grid = GetGrdProvInfo();
6343 var masterTable = GetGrdProvInfoMasterTableView();
6344
6345 if (grid) {
6346 for (var i = 0; i < masterTable.get_dataItems().length; i++) {
6347 var row = masterTable.get_dataItems()[i];
6348 var rowId = row._element.id;
6349 var checkbox = $("#" + rowId).find("#chkProviderColor")[0]; //Gets the checkbox object of the current row iteration
6350 if (checkbox.checked) {
6351 checkString += masterTable.getCellByColumnUniqueName(row, "PK").innerHTML + ",";
6352 }
6353 }
6354
6355 if (checkString.length > 0) {
6356 checkString = checkString.slice(0, -1);
6357 }
6358 }
6359 }
6360 }
6361
6362 return checkString;
6363}
6364
6365
6366
6367//NMM 3/31/2016 - This gets called when you click on the expand or collapsed icon above the providerInfo grid.
6368//This will hide or show the grid's additional columns accordingly and set a flag to remember the grid's column
6369//state on postback.
6370function GrdExpandCollapseIconClicked() {
6371 //var masterTable = GetGrdProvInfoMasterTableView();
6372 //var colorCol = masterTable.getColumnByUniqueName("ProvColorPicker");
6373 var hdnProvGrdInfoExpandCol = document.getElementById("hdnProvGrdInfoExpandCol");
6374
6375 //VJF 05/22/2017 - Changed use of color column visibility to determine current state
6376 if (hdnProvGrdInfoExpandCol.value === "true") {
6377 hdnProvGrdInfoExpandCol.value = "false";
6378 } else {
6379 hdnProvGrdInfoExpandCol.value = "true";
6380 }
6381
6382 //Expand or Collapse the additional columns on the radgrid
6383 ToggleGrdProvCol();
6384
6385 //VJF 11/03/2016 - When expanded - show the date range used for shift counting in admin provider dock
6386 //if (getAppArea() == ApplicationArea.AdminCalendar && tsMainMenu.get_selectedTab().get_value() == AdminMenuBarItem.AdminCalendar) {
6387 // if (hdnProvGrdInfoExpandCol.value === "true") {
6388 // SetProviderListTitle(document.forms.frmScheduler.hdnCountingDateLabel.value);
6389 // } else {
6390 // SetProviderListTitle('');
6391 // }
6392 //}
6393}
6394
6395//NMM 3/31/2016 - Resizes the provider dock and removes the horizontal scrollbar
6396function ResizeProvDock() {
6397
6398 var grid = GetGrdProvInfo();
6399 var provDock = $find("ProvidersDock");
6400 var gridWidth = grid.get_element().style.width;
6401 if (gridWidth != "") {
6402 var newGridWidth = parseInt(gridWidth.replace("px", "")) + "px";
6403
6404 provDock.set_width(newGridWidth);
6405 }
6406}
6407
6408//NMM 3/7/2016 - Saves the color picker selection on the server via Ajax
6409function SaveColorOnServerAjax(sProvId, sColor) {
6410 var postMethod;
6411 if (getAppArea() == ApplicationArea.AdminCalendar && tsMainMenu.get_selectedTab().get_value() == AdminMenuBarItem.AdminCalendar) {
6412 postMethod = "AdminCalendar.aspx/SaveColorPickerSelection";
6413 } else {
6414 postMethod = "ProvCalendar.aspx/SaveColorPickerSelection";
6415 }
6416
6417 if (sColor.length != 0) {
6418 $.ajax(
6419 {
6420 type: "POST",
6421 url: postMethod,
6422 data: JSON.stringify({ provId: sProvId, color: sColor }),
6423 contentType: "application/json; charset=utf-8",
6424 async: true,
6425 dataType: "json",
6426 success: function (msg) {
6427 if (!msg.d) {
6428 var topWindow = GetTopAccessibleWindow(window);
6429 topWindow.location = gSLogoutUrl;
6430 }
6431 },
6432 error: function (xhr, ajaxOptions, thrownError) {
6433 //TJF 10/12/2016 - Catch 401 authentication errors
6434 if (xhr.status == 401) {
6435 var topWindow = GetTopAccessibleWindow(window);
6436 topWindow.location = gSLogoutUrl;
6437 } else {
6438 //TJF 10/12/2016 - Catch 500 internal server errors and others
6439 RadAlert(xhr.status + " " + xhr.responseText);
6440 }
6441 //console.log(xhr.status);
6442 //console.log(xhr.responseText);
6443 }
6444 }
6445 );
6446 }
6447
6448}
6449
6450//NMM 3/7/2016 - Applies the Permanent color styling on the radcolorpicker - black border around the selected color
6451function SetPermanentColor(colorPickerId) {
6452 $("#" + colorPickerId).css("border", "1px solid #000000");
6453}
6454
6455//NMM 3/7/2016 - Applies the Temporary color styling on the radcolorpicker - no border around the selected color
6456function SetTemporaryColor(colorPickerId) {
6457 $("#" + colorPickerId).css("border", "1px solid #cdcdcd");
6458}
6459
6460//NMM 3/7/2016 - Applies the styling for the 'no color' selection on the colorpicker - set the selected color as transparent
6461function SetNoColor(colorPickerId) {
6462 $("#" + colorPickerId).css("border", "none");
6463 $("#" + colorPickerId).css("background-color", "transparent");
6464}
6465
6466//TJF 11/23/2015 - Filter by a practitioner id
6467function providerDockPractIndexChangedForGrid(sender) {
6468
6469 if (sender.options[sender.selectedIndex] != undefined) {
6470 var filterPractId = sender.options[sender.selectedIndex].value;
6471 var masterTable = GetGrdProvInfoMasterTableView();
6472 for (var i = 0; i < masterTable.get_dataItems().length; i++) {
6473 var sPractTypeIds = masterTable.getCellByColumnUniqueName(masterTable.get_dataItems()[i], "DataPractTypeIds").innerHTML;
6474 var pk = masterTable.getCellByColumnUniqueName(masterTable.get_dataItems()[i], "PK").innerHTML;
6475 var visible = false;
6476
6477 //If primary key is 0, It means it is an open shift. We will include this when filtering.
6478 if (pk == OPEN_SHIFT_ID) {
6479 sPractTypeIds = (function () { return; })();
6480 }
6481
6482 if (sPractTypeIds != undefined && filterPractId != "-1") {
6483 var arrPractTypeIds;
6484 //Can be string or integer, if string convert to array
6485 if (typeof sPractTypeIds == "string") {
6486 arrPractTypeIds = sPractTypeIds.split(",");
6487 } else {
6488 arrPractTypeIds = [sPractTypeIds.toString()];
6489 }
6490
6491 //Check if provider has the practitioner type
6492 for (var j = 0; j < arrPractTypeIds.length; j++) {
6493
6494 if (arrPractTypeIds[j] == filterPractId) {
6495 visible = true;
6496 break;
6497 }
6498
6499 }
6500 } else {
6501 //none is selected, all should be visible
6502 visible = true;
6503 }
6504
6505 var row = masterTable.get_dataItems()[i];
6506 var rowId = row._element.id;
6507 if (visible) {
6508 row.set_visible(true);
6509 } else {
6510 row.set_visible(false);
6511
6512 //If checkbox is checked on a row, we will uncheck it and remove it's highlight
6513 var chkHighlight = $("#" + rowId).find("#chkProviderColor")[0]; //.find() returns a list of elements, we need to get the first one.
6514 if (chkHighlight.checked) {
6515 //TJF 7/7/2016 - Update checked providers
6516 if (gCheckedProviders[pk]) {
6517 gCheckedProviders[pk] = false;
6518 }
6519 chkHighlight.checked = false;
6520 highlightProviderHandlerGenericForGrid(chkHighlight);
6521 }
6522 }
6523 }
6524
6525 //Get the total shifts and hours of the visible rows
6526 var grid = GetGrdProvInfo();
6527 var grdProvId = "#" + grid.get_id();
6528 $(grdProvId + " .lblTotalShifts").html(SumTotalShifts().toString());
6529 $(grdProvId + " .lblTotalHours").html(SumTotalHours());
6530
6531 //TJF 8/12/2016 - Sum shift group slots
6532 setTimeout(function () {
6533 for (var i = 0; i < gShiftGroupCountColumnNames.length; i++) {
6534 sColName = gShiftGroupCountColumnNames[i];
6535 $(grdProvId + " .lbl" + sColName).html(sumTotalShiftGroups(sColName).toString());
6536 }
6537 }, 0);
6538
6539 }
6540}
6541
6542//NMM 3/30/2016 - Returns the 'grdProviderInformation' Radgrid object (ProviderInfo) that is inside the RadDock
6543function GetGrdProvInfo() {
6544 var grdProvInfo = $('#ProvidersDock').find('.RadGrid').attr("id");
6545 return $find(grdProvInfo);
6546}
6547
6548//NMM 3/30/2016 - Returns the 'grdProviderInformation' mastertable object (ProviderInfo) that is inside the RadDock
6549function GetGrdProvInfoMasterTableView() {
6550 var grdProvInfo = $('#ProvidersDock').find('.RadGrid').attr("id");
6551 var masterTableView = $find(grdProvInfo).get_masterTableView();
6552 return masterTableView;
6553}
6554
6555//NMM 3/30/2016 - Returns the provider name text from the providers grid based on the providerId parameter
6556function FindProviderNameFromGrdById(id) {
6557 var provName = null;
6558 var masterTable = GetGrdProvInfoMasterTableView();
6559 for (var i = 0; i < masterTable.get_dataItems().length; i++) {
6560 var pk = masterTable.getCellByColumnUniqueName(masterTable.get_dataItems()[i], "PK").innerHTML;
6561 if (pk == id) {
6562 provName = masterTable.getCellByColumnUniqueName(masterTable.get_dataItems()[i], "ProviderName").innerHTML;
6563 break;
6564 }
6565 }
6566
6567 return provName;
6568}
6569
6570//VJF 12/14/2016 - Get the name on a shift assignment directly from the Dom Html using an appointment id.
6571//The div with the assigment name will have a class name of evtProv or evtOpen.
6572function FindProviderNameFromGridByDomId(aptDomId) {
6573 var name = "";
6574 var aptElem = $("#" + aptDomId);
6575
6576 if (aptElem.length) {
6577 //Look for provider assignment by class
6578 var assignmentDiv = aptElem.find(".evtProv");
6579 if (assignmentDiv.length) {
6580 name = assignmentDiv.text();
6581 return name;
6582 }
6583
6584 //Look for open shift assignment by class
6585 assignmentDiv = aptElem.find(".evtOpen");
6586 if (assignmentDiv.length) {
6587 name = assignmentDiv.text();
6588 return name;
6589 }
6590 }
6591 return name;
6592}
6593
6594
6595//NMM 3/30/2016 - Returns the checkbox object from the providers grid based on the providerId parameter
6596function FindCheckboxFromGridById(id) {
6597 var chkSelected;
6598 var masterTable = GetGrdProvInfoMasterTableView();
6599
6600 for (var i = 0; i < masterTable.get_dataItems().length; i++) {
6601 var pk = masterTable.getCellByColumnUniqueName(masterTable.get_dataItems()[i], "PK").innerHTML;
6602 if (pk == id) {
6603 var row = masterTable.get_dataItems()[i];
6604 var rowId = row._element.id;
6605 chkSelected = $("#" + rowId).find("#chkProviderColor")[0];
6606 break;
6607 }
6608 }
6609 return chkSelected;
6610}
6611
6612//NMM 3/30/2016 - This method returns the row dataItem from the providers Grid based on the providerId parameter
6613function FindDataItemById(id) {
6614 var dataItem;
6615 var masterTable = GetGrdProvInfoMasterTableView();
6616
6617 for (var i = 0; i < masterTable.get_dataItems().length; i++) {
6618 var pk = masterTable.getCellByColumnUniqueName(masterTable.get_dataItems()[i], "PK").innerHTML;
6619 if (pk == id) {
6620 dataItem = masterTable.get_dataItems()[i];
6621 break;
6622 }
6623 }
6624 return dataItem;
6625}
6626
6627//NMM 3/31/2016 - Gets the Row Index position by checkbox parameter passed in
6628function FindRowIndexByCheckboxChecked(chkHighlighted) {
6629 var rowIndex = chkHighlighted.parentNode.parentNode.parentNode.sectionRowIndex;
6630 return rowIndex;
6631}
6632
6633function isColorAlreadyExist(provColor, provId) {
6634 var bExist = false;
6635 var masterTable = GetGrdProvInfoMasterTableView();
6636 for (var i = 0; i < masterTable.get_dataItems().length; i++) {
6637 var provHexColor = masterTable.getCellByColumnUniqueName(masterTable.get_dataItems()[i], "DataProvColor").innerHTML;
6638 var pk = masterTable.getCellByColumnUniqueName(masterTable.get_dataItems()[i], "PK").innerHTML;
6639 if (provHexColor.toUpperCase() == provColor.toUpperCase() && provId != pk) {
6640 bExist = true;
6641 break;
6642 }
6643 }
6644 return bExist;
6645}
6646
6647//NMM 3/30/2016 - Returns the total of the shifts column of the providers grid that is used in the footer display
6648function SumTotalShifts() {
6649
6650 var masterTable = GetGrdProvInfoMasterTableView();
6651 var nTotal = 0;
6652 for (var i = 0; i < masterTable.get_dataItems().length; i++) {
6653 var nRow = masterTable.get_dataItems()[i];
6654
6655 //Add the visible rows only
6656 if (nRow.get_visible()) {
6657 var nProvShifts = masterTable.getCellByColumnUniqueName(nRow, "ProviderShifts").innerHTML;
6658 nTotal += parseInt(nProvShifts);
6659 }
6660 }
6661 return nTotal;
6662}
6663
6664//NMM 3/30/2016 - Returns the total of the hours column of the providers grid that is used in the footer display
6665function SumTotalHours() {
6666
6667 var masterTable = GetGrdProvInfoMasterTableView();
6668 var nTotal = 0;
6669 for (var i = 0; i < masterTable.get_dataItems().length; i++) {
6670 var nRow = masterTable.get_dataItems()[i];
6671
6672 //Add the visible rows only
6673 if (nRow.get_visible()) {
6674 var nProvHours = masterTable.getCellByColumnUniqueName(nRow, "ProviderHours").innerHTML;
6675 nTotal += parseFloat(nProvHours);
6676 }
6677 }
6678
6679 //TJF 11/23/2016 - Round final result to two decimal places
6680 return RoundValueToDecimalPlace(nTotal, 1);
6681}
6682
6683//TJF 8/5/2016 - Sum a shift group row
6684function sumTotalShiftGroups(sShiftGroupCol) {
6685 var masterTable = GetGrdProvInfoMasterTableView();
6686 var bIsBoth = false; //TJF 11/10/2016 - Track whether both hours and shifts are selected
6687 var nTotalShifts = 0;
6688 var nTotalHours = 0;
6689 for (var i = 0; i < masterTable.get_dataItems().length; i++) {
6690 var nRow = masterTable.get_dataItems()[i];
6691 //Add the visible rows only
6692 if (nRow.get_visible()) {
6693 var colNum = masterTable.getCellByColumnUniqueName(nRow, sShiftGroupCol).innerHTML;
6694 //TJF 11/10/2016 - Sum both shift group count and shift group hours
6695 if (colNum.indexOf("/") > -1) {
6696 bIsBoth = true;
6697 var arrShiftsAndHours = colNum.split("/"); //Index 0 is shifts, index 1 is hours
6698 nTotalShifts += parseFloat(arrShiftsAndHours[0]);
6699 nTotalHours += parseFloat(arrShiftsAndHours[1]);
6700 } else {
6701 nTotalShifts += parseFloat(colNum);
6702 }
6703
6704 }
6705 }
6706 if (bIsBoth) {
6707 return nTotalShifts + "/" + RoundValueToDecimalPlace(nTotalHours, 1); //TJF 11/23/2016 - Round final totals to 1 decimal place
6708 } else {
6709 return RoundValueToDecimalPlace(nTotalShifts, 1);
6710 }
6711}
6712
6713//NMM 3/29/2015 - Removes the opacity from the providers dock whenever we open the color palette window on the RadColorPicker
6714function OnColorPickerOpen(sender) {
6715 $("#ProvidersDock").css("opacity", "1");
6716
6717 //TJF 7/11/2016 - Add click handler for preview button
6718 $(sender._previewElement).click(function () {
6719 var selectedColor = sender._selectedColor;
6720 var row = $(sender._element).closest('tr')[0];
6721
6722 //Check the prov color checkbox when raddcolorpicker was changed
6723 var chkProvColor = $("#" + row.id).find("#chkProviderColor")[0];
6724 var colorPickerId = $(sender._element).find("em")[0].id;
6725
6726 var provId = $(chkProvColor).closest('tr').children('td:first').text();
6727 if (selectedColor != null) {
6728 SaveColorOnServerAjax(provId, selectedColor);
6729 SetPermanentColor(colorPickerId);
6730 }
6731 sender.HidePalette();
6732 });
6733}
6734
6735//NMM 3/29/2015 - After we close the color palette window, we set the opacity back to the Providers Dock
6736function RestoreOpacity() {
6737 $("#ProvidersDock").css("opacity", ".85");
6738}
6739
6740//NMM 3/29/2015 - Restores the provider grid expand/collapse column state
6741//when switching months on the calendar or changing view mode
6742var gShiftGroupCountsLoaded = false; //TJF 10/24/2016 - Load shift group counts if loaded is false
6743function ToggleGrdProvCol() {
6744 var icon = document.getElementById("pnlMoreColumnsOnProvGrd");
6745 var grid = GetGrdProvInfo();
6746 var masterTable = GetGrdProvInfoMasterTableView();
6747 //flag that specifies if the column is expanded or not
6748 var hdnProvGrdInfoExpandCol = document.getElementById("hdnProvGrdInfoExpandCol");
6749
6750 var colorCol = masterTable.getColumnByUniqueName("ProvColorPicker");
6751 var shiftsCol = masterTable.getColumnByUniqueName("ProviderShifts");
6752 //var shiftsTargetCol = masterTable.getColumnByUniqueName("TargetShifts");
6753 var hoursCol = masterTable.getColumnByUniqueName("ProviderHours");
6754 //TJF 10/26/2017 - Get spp column
6755 var sppCol = masterTable.getColumnByUniqueName("SPP");
6756
6757 var ShiftGroupColumns = [];
6758 var TargetColumns = [];
6759 for (var i = 0; i < gShiftGroupCountColumnNames.length; i++) {
6760 var sName = gShiftGroupCountColumnNames[i];
6761 ShiftGroupColumns.push(masterTable.getColumnByUniqueName(sName));
6762
6763 if (IsShowTargetsChecked()) {
6764 var sTargetName = gShiftGroupTargetColumnNames[i];
6765 TargetColumns.push(masterTable.getColumnByUniqueName(sTargetName));
6766 }
6767
6768 }
6769
6770 if (hdnProvGrdInfoExpandCol.value == "true") {
6771
6772 //TJF 10/24/2016 - Load counts for provider shift group
6773 if (getAppArea() == ApplicationArea.AdminCalendar && tsMainMenu.get_selectedTab().get_value() == AdminMenuBarItem.AdminCalendar && !gShiftGroupCountsLoaded) {
6774 LoadAndProcessShiftGroupTreeJsonInBothUnits(true);
6775 gShiftGroupCountsLoaded = true;
6776 } else {
6777 //Show the additional columns on the grid
6778 masterTable.showColumn(colorCol.get_element().cellIndex);
6779
6780 var nGridSize = 380;
6781 if (getAppArea() == ApplicationArea.AdminCalendar && tsMainMenu.get_selectedTab().get_value() == AdminMenuBarItem.AdminCalendar) {
6782 SetProviderListTitle(document.forms.frmScheduler.hdnCountingDateLabel.value);
6783
6784 //TJF 11/10/2016 - Only show if the relevant unit is selected
6785 var bIsHours = isUnitsHoursChecked();
6786 var bIsShifts = isUnitsShiftsChecked();
6787 if (bIsShifts) {
6788 masterTable.showColumn(GetAdjustedCellIndexForTotals(shiftsCol.get_element().cellIndex));
6789 }
6790 if (bIsHours) {
6791 masterTable.showColumn(GetAdjustedCellIndexForTotals(hoursCol.get_element().cellIndex));
6792 }
6793 //TJF 10/26/2017 - Show spp column if targets are being shown
6794 if (IsShowTargetsChecked()) {
6795 masterTable.showColumn(GetAdjustedCellIndexForTotals(sppCol.get_element().cellIndex));
6796 }
6797
6798 //TJF 8/16/2016 - Only show column if data is loaded into it
6799 for (var i = 0; i < ShiftGroupColumns.length; i++) {
6800 if (gCurrentDisplayedPrimaryKeys[i]) {
6801 masterTable.showColumn(GetAdjustedCellIndex(ShiftGroupColumns[i].get_element().cellIndex));
6802
6803 nGridSize += $(ShiftGroupColumns[i].get_element()).outerWidth(); //TJF 11/14/2016 - Include padding
6804
6805 if (IsShowTargetsChecked()) {
6806 masterTable.showColumn(GetAdjustedCellIndex(TargetColumns[i].get_element().cellIndex));
6807 nGridSize += $(TargetColumns[i].get_element()).outerWidth();
6808 }
6809
6810 }
6811 }
6812
6813 //Show the footer
6814 $("#" + grid.get_id() + " .rgFooter").css("display", "table-row");
6815
6816 $("#" + grid.get_id() + " .lblTotalShifts").html(SumTotalShifts().toString());
6817 $("#" + grid.get_id() + " .lblTotalHours").html(SumTotalHours());
6818 }
6819
6820 //change the icon panel class name to display the expanded icon
6821 icon.className = icon.className.replace("collapsed", "");
6822 icon.className += " expanded";
6823
6824 //Show link to select shift groups
6825 $("#ProvidersDock_C_ShiftGroupsDisplay").show();
6826
6827 if (gCurrentGridSize == null) {
6828 grid.get_element().style.width = nGridSize + "px";
6829 } else {
6830 //TJF 11/10/2016 - Use consistent width
6831 grid.get_element().style.width = gCurrentGridSize + "px";
6832 }
6833 ResizeProvDock();
6834
6835 }
6836
6837 } else {
6838 //Hide the additional columns on grid
6839 masterTable.hideColumn(colorCol.get_element().cellIndex);
6840
6841 if (getAppArea() == ApplicationArea.AdminCalendar && tsMainMenu.get_selectedTab().get_value() == AdminMenuBarItem.AdminCalendar) {
6842 SetProviderListTitle('');
6843
6844 if (shiftsCol && hoursCol) {
6845 masterTable.hideColumn(GetAdjustedCellIndexForTotals(shiftsCol.get_element().cellIndex));
6846 masterTable.hideColumn(GetAdjustedCellIndexForTotals(hoursCol.get_element().cellIndex));
6847 //TJF 10/26/2017 - hide spp column
6848 masterTable.hideColumn(GetAdjustedCellIndexForTotals(sppCol.get_element().cellIndex));
6849
6850 //TJF 10/17/2016 - Hide shift group columns
6851 for (var i = 0; i < ShiftGroupColumns.length; i++) {
6852 masterTable.hideColumn(GetAdjustedCellIndex(ShiftGroupColumns[i].get_element().cellIndex));
6853
6854 if (IsShowTargetsChecked()) {
6855 masterTable.hideColumn(GetAdjustedCellIndex(TargetColumns[i].get_element().cellIndex));
6856 }
6857 }
6858
6859 $("#" + grid.get_id() + " .rgFooter").css("display", "none");
6860 }
6861 }
6862
6863 //change the icon panel class name to display the collapsed icon
6864 icon.className = icon.className.replace("expanded", "");
6865 icon.className += " collapsed";
6866
6867 //Set the collapsed grid width.
6868 grid.get_element().style.width = "230px";
6869
6870 //Hide link to select shift groups
6871 $("#ProvidersDock_C_ShiftGroupsDisplay").hide();
6872 }
6873
6874 //Adjust the width of the Providers Dock
6875 ResizeProvDock();
6876}
6877
6878/*function RGBToHexColor(colorval) {
6879 var parts = colorval.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
6880 delete (parts[0]);
6881 for (var i = 1; i <= 3; ++i) {
6882 parts[i] = parseInt(parts[i]).toString(16);
6883 if (parts[i].length == 1) parts[i] = '0' + parts[i];
6884 }
6885 color = '#' + parts.join('');
6886
6887 return color;
6888}*/
6889
6890
6891function SetProviderListTitle(title) {
6892
6893 var provDock = $find("ProvidersDock");
6894 if (provDock) {
6895 provDock.set_title(title);
6896 }
6897}
6898
6899//VJF 05/31/2017 - Toggle the visibility of the provider dock color column
6900function ToggleGrdProvColorCol(bShow) {
6901
6902 var hdnProvGrdInfoExpandCol = document.getElementById("hdnProvGrdInfoExpandCol");
6903
6904 if (hdnProvGrdInfoExpandCol.value != "true") {
6905 //var grid = GetGrdProvInfo();
6906 var masterTable = GetGrdProvInfoMasterTableView();
6907 var colorCol = masterTable.getColumnByUniqueName("ProvColorPicker");
6908
6909 if (bShow) {
6910 masterTable.showColumn(colorCol.get_element().cellIndex);
6911 } else {
6912 masterTable.hideColumn(colorCol.get_element().cellIndex);
6913 }
6914 }
6915}
6916
6917//VJF 05/31/2017 - Set the checkbox state by provider id. Param bCheck is optional and will toggle state if omitted.
6918//Return flag indicating state change.
6919function setProvGrdCheckedById(provId, bCheck) {
6920 var chkbox = FindCheckboxFromGridById(provId);
6921 var bStateChanged = false;
6922
6923 if (chkbox) {
6924 //Toggle state when not supplied
6925 if (bCheck == undefined) {
6926 bCheck = !chkbox.checked;
6927 }
6928
6929 //Change the checked state if it is changing
6930 if (bCheck !== chkbox.checked) {
6931 chkbox.checked = bCheck;
6932 ChkProvColorClicked(chkbox, false);
6933 bStateChanged = true;
6934 }
6935 }
6936 return bStateChanged;
6937}
6938
6939//JCL 8/30/2017 - Set Manually Assigned Shift flag based on the current user filters (e.g. Shift Group, Practitioner Type, Provider IDs, or All shifts).
6940function SetManuallyAssignedForSelectedFilter(arrFilter) {
6941
6942 //Determine if a Shift Group Filter has been applied
6943 var bIsShiftGroupFilterApplied = false;
6944 for (var i in arrFilter) {
6945 var shiftGroupId = arrFilter[i].ShiftGroupFilterId;
6946 if (shiftGroupId != -1) {
6947 bIsShiftGroupFilterApplied = true;
6948 }
6949 }
6950
6951 //If Shift Group Filter in use, check if we have the Shift Group Tree loaded. If not, we need to load before processing further.
6952 if (bIsShiftGroupFilterApplied
6953 && !gShiftGroupCountsLoaded) {
6954
6955 //JF 11/30/2017 - Add bTargetsChecked parameter and Error handler to AjaxGetShiftGroupTreeJSON() call
6956 PageMethods.AjaxGetShiftGroupTreeJSON(true, true, false, ApplyShiftGroupFilter, ApplyShiftGroupFilterError);
6957
6958 function ApplyShiftGroupFilter(JsonShiftGroupTree) {
6959
6960 //JF 11/30/2017 - Rename treeJSON to treeJSONForFilter to avoid name clash with global treeJSON
6961 var treeJSONForFilter = JSON.parse(JsonShiftGroupTree);
6962
6963 UpdateManuallyAssignedShiftsForFilter(arrFilter, treeJSONForFilter);
6964 }
6965
6966 //JF 11/30/2017 - Add Error handler
6967 function ApplyShiftGroupFilterError(error) {
6968
6969 if (error.get_statusCode() === 401) {
6970 //Authorization Failed
6971 var topWindow = GetTopAccessibleWindow(window);
6972 topWindow.location = gSLogoutUrl;
6973 } else {
6974 //Catch 500 internal server errors and others
6975 RadAlert('Unable to apply Shift Group Filter.\n' +
6976 error.get_statusCode() + " " + error.get_message());
6977 }
6978 }
6979 }
6980
6981 else {
6982
6983 //No Filter applied, update all shifts
6984 UpdateManuallyAssignedShiftsForFilter(arrFilter, treeJSON);
6985
6986 }
6987}
6988
6989//JCL 8/30/2017 - Update Manually Assigned flag for shifts based on our current filter (e.g. Shift Group, Practitioner Type, Provider IDs, or All shifts).
6990function UpdateManuallyAssignedShiftsForFilter(arrFilter, arrShiftGroupTrees) {
6991
6992 //Iterate through applied Filters
6993 for (var i in arrFilter) {
6994
6995 //Get Filter settings
6996 var shiftGroupId = arrFilter[i].ShiftGroupFilterId;
6997 var shiftGroupLocId = arrFilter[i].ShiftGroupLocationFilterId;
6998 var practTypeFilterId = arrFilter[i].PractTypeFilterId;
6999 var providerIdFilter = arrFilter[i].ProviderIdFilter.split(",");
7000
7001 //Manual Assigned flag is a string in setManuallyAssigned()
7002 var bSetManuallyAssigned = arrFilter[i].bSetManuallyAssigned;
7003 var sSetManulFlag = "false";
7004 if (bSetManuallyAssigned) {
7005 sSetManulFlag = "true"
7006 }
7007
7008 //If Shift Group Filter, load Shift Group info
7009 var ShiftGroupTreeEntries = [];
7010 var bIsSystem = (shiftGroupLocId == -1);
7011 if (shiftGroupId > -1) {
7012 for (var j = 0; j < arrShiftGroupTrees.length; j++) { //JF 9/29/2017 - Change loop variable to avoid clash with outer loop
7013
7014 //JF 11/30/2017 - Loop through each ShiftGroupTree to see which ones are filtered
7015 for (var k = 0; k < arrShiftGroupTrees[j].length; k++) {
7016 if ((arrShiftGroupTrees[j][k].ShiftGroupID == shiftGroupId) && (arrShiftGroupTrees[j][k].IsSystem == bIsSystem)) {
7017 ShiftGroupTreeEntries.push(arrShiftGroupTrees[j][k]);
7018 }
7019 }
7020 }
7021 }
7022
7023 //JF 9/29/2017 - Get period start & end date from filter
7024 var PeriodDateRange = arrFilter[i].PeriodDateRange;
7025 var periodStartDate = PeriodDateRange.Starts;
7026 var periodEndDate = PeriodDateRange.Ends;
7027
7028 //Support multiple schedulers(they are used when a mode other than by month is active)
7029 $(".RadScheduler").each(function (index) {
7030 var scheduler = $find($(this).attr('id'));
7031
7032 //Loop through each appointment and match it to a tree item if possible and highlight
7033 (scheduler.get_appointments()).forEach(function (apt) {
7034 var attribs = apt.get_attributes();
7035 var selectedShiftPrimaryKey = attribs.getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_APPT_ID);
7036 var typeId = selectedShiftPrimaryKey.substring(0, selectedShiftPrimaryKey.indexOf("|")); //Type ID is the first part of the primary key
7037 var $apt = $(apt.get_element());
7038 var aptDate = apt.get_start(); //Use start date for comparison with the exception date
7039 var locationId = selectedShiftPrimaryKey.substring(selectedShiftPrimaryKey.indexOf("|") + 1, selectedShiftPrimaryKey.indexOf("|", selectedShiftPrimaryKey.indexOf("|") + 1));
7040 var providerId = attribs.getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_PROV_ID);
7041 var isTemplateAssignment = attribs.getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_TEMPLATE_ASSIGNMENT) === "1";
7042
7043 //JF 9/29/2017 - Filter out the dates that aren't in the period date range
7044 //VJF 12/14/2017 - Skip template assigned shifts. This prevents the manually assigned flag on the shift from being removed.
7045 if (aptDate.getTime() >= periodStartDate.getTime() && aptDate.getTime() <= periodEndDate.getTime()
7046 && !isTemplateAssignment) {
7047
7048 //Iterate through each entry and see if appointment should be Set to Manually or Flexibly Assigned
7049 if (shiftGroupId > -1) {
7050
7051 //Shift Group Filter
7052 for (var j = 0; j < ShiftGroupTreeEntries.length; j++) {
7053 var ShiftGroupTreeEntry = ShiftGroupTreeEntries[j];
7054 (ShiftGroupTreeEntry.Entries).forEach(function (treeEntry) {
7055
7056 var shiftTypeId = treeEntry.STId;
7057 var exDate;
7058
7059 //Date is only set in the case of an exception
7060 if (treeEntry.IsEx) {
7061 exDate = new Date(treeEntry.ExDate);
7062 }
7063
7064 //Shift Group filter applied, update shift if it belongs to the current Shift Group
7065 if (!treeEntry.IsEx && (typeId == shiftTypeId) && (locationId == treeEntry.LocId) && (GetBitmaskForDay(apt.get_start().getDay()) & treeEntry.DoW)) {
7066
7067 //Standard (non exception) Shifts
7068
7069 setManuallyAssigned($apt[0].id, sSetManulFlag);
7070
7071 }
7072 else if (treeEntry.IsEx && (typeId == shiftTypeId) && (locationId == treeEntry.LocId)
7073 && (exDate.getDate() == aptDate.getDate() && exDate.getMonth() == aptDate.getMonth() && exDate.getFullYear() == aptDate.getFullYear())) {
7074
7075 //Exception Shifts
7076 setManuallyAssigned($apt[0].id, sSetManulFlag);
7077
7078 }
7079
7080 });
7081 }
7082 }
7083 else if (practTypeFilterId > -1) {
7084
7085 //Practitioner Type Filter
7086 var masterTable = GetGrdProvInfoMasterTableView();
7087 for (var i = 0; i < masterTable.get_dataItems().length; i++) {
7088
7089 //Get row from Provider QuickList
7090 var row = masterTable.get_dataItems()[i];
7091
7092 //Provider Id from Provider QuickList
7093 var quickListProviderId = masterTable.getCellByColumnUniqueName(row, "PK").innerHTML;
7094
7095 //Find match for the provider assigned to the shift
7096 if (quickListProviderId == providerId) {
7097
7098 //Get the Provider's Practitioner Type Ids
7099 var sProviderPractTypeIds = masterTable.getCellByColumnUniqueName(row, "DataPractTypeIds").innerHTML;
7100
7101 //Convert to Practitioner Type Array
7102 var arrProviderPractTypeIds = [];
7103 if (sProviderPractTypeIds != undefined) {
7104 //Can be string or integer, if string convert to array
7105 if (typeof sProviderPractTypeIds == "string") {
7106 arrProviderPractTypeIds = sProviderPractTypeIds.split(",");
7107 } else {
7108 arrProviderPractTypeIds = [sProviderPractTypeIds.toString()];
7109 }
7110 }
7111
7112 //If the provider has the selected Practitioner Type filter, Set Manually Assigned flag.
7113 for (var j = 0; j < arrProviderPractTypeIds.length ; j++) {
7114
7115 var sProviderPratcTypeId = arrProviderPractTypeIds[j];
7116
7117 //Set flag if the provider assigned to shift is one of the selected providers
7118 if (sProviderPratcTypeId == practTypeFilterId.toString()) {
7119
7120 setManuallyAssigned($apt[0].id, sSetManulFlag);
7121 }
7122
7123 }
7124 }
7125 }
7126
7127 }
7128 else if (providerIdFilter.length > 0
7129 && providerIdFilter[0] != "") {
7130
7131 //Provider Id Filter
7132 for (i = 0; i < providerIdFilter.length ; i++) {
7133 var nSelProviderId = providerIdFilter[i];
7134
7135 //Set flag if the provider assigned to shift is one of the selected providers
7136 if (providerId == nSelProviderId) {
7137
7138 setManuallyAssigned($apt[0].id, sSetManulFlag);
7139 }
7140
7141 }
7142 }
7143 else if (providerId != "-1") {
7144
7145 //No Filter applied, update all shifts
7146 setManuallyAssigned($apt[0].id, sSetManulFlag);
7147 }
7148 }
7149 });
7150 });
7151 }
7152}
7153
7154//JCL 8/22/2017 - Toggle Manually Assigned
7155function setManuallyAssigned(domId, bLock) {
7156 apt = $("#" + domId + ".rsApt");
7157
7158 //Clear existing setting
7159 apt.removeClass("manual-show");
7160 apt.removeClass("manual-hide");
7161
7162 //VJF 12/14/2017 - Remove the template assignment class, since a manually assignment will negate that state.
7163 apt.removeClass("tmplt-assigned");
7164
7165 //JF 9/25/2017 - Set Manually Assigned flag based on whether the Identify Manually Assigned Shifts checkbox is checked
7166 var chkIdentifyManuallyAssigned = document.getElementById("chkIdentifyManuallyAssigned");
7167 if (chkIdentifyManuallyAssigned) {
7168
7169 //Get manually assigned css class name based on current visibility of manually assigned shifts
7170 var manualClassName = chkIdentifyManuallyAssigned.control.get_checked() ? "manual-show" : "manual-hide";
7171
7172 //Set\Unset Locked flag
7173 //JF 9/21/2017 - Add check for boolean true
7174 if (bLock == "true" || bLock == true) {
7175 apt.addClass(manualClassName);
7176 }
7177 else {
7178 apt.removeClass(manualClassName);
7179 }
7180 }
7181
7182 return true;
7183}
7184
7185//VJF 09/13/2017 - Identify a selected provider by circling it
7186function circleSelectedProvider(provId, bClearExistingSelections) {
7187
7188 //Default bClearExistingSeletions to true when it isn't supplied
7189 if (typeof bClearExistingSeletions != 'boolean') bClearExistingSeletions = true;
7190
7191 if (bClearExistingSeletions) {
7192 $(".selected-prov").removeClass("selected-prov");
7193 }
7194
7195 //Find all divs that have the provider id as their id
7196 var assignmentDivs = $("[id=" + provId + "]");
7197 assignmentDivs.addClass("selected-prov");
7198}
7199
7200//VJF 09/13/2017 - Get index of provider grid row by provider id
7201function getProviderRowIndexOnGrid(providerId) {
7202
7203 var masterTableView = GetGrdProvInfoMasterTableView();
7204 var rowIndex = -1;
7205 var dataItem = null;
7206 for (var i = 0; i < masterTableView.get_dataItems().length; i++) {
7207 dataItem = masterTableView.get_dataItems()[i];
7208 rowIndex = $(dataItem.get_element()).index();
7209 var pk = masterTableView.getCellByColumnUniqueName(dataItem, "PK").innerHTML;
7210 if (pk == providerId) {
7211 break;
7212 }
7213 }
7214
7215 return rowIndex;
7216}
7217
7218/**********************************************************/
7219/* PROVIDER UNAVAILABLE TIME - Start */
7220/**********************************************************/
7221var UNAVAILABLE_CONTENT_CSS_CLASS = "unavail-content";
7222
7223//VJF 09/13/2017 - Show unavailable time checkbox state has changed.
7224function checkedShowUnavailableTime(bShowUnavailTime) {
7225 //The Open shift checkbox should be checked by default when first displaying unavailable time.
7226 var nOpenProvId = 0;
7227 var bSuppressChangeEvent = true;
7228 var bCallShowProviderUnavailableTime = true;
7229 var nSelectedProvId = document.forms.frmScheduler.hdnSelectedProvider.value;
7230
7231 //Open shift check state mirrors Unavailable Time checkbox
7232 var bOpenStateChanged = setProvGrdCheckedById(nOpenProvId, bShowUnavailTime);
7233
7234 //VJF 10/09/2017 - Show checked only should not be automatically set when unavailable time is first shown
7235 var bDefaultToShowCheckedOnly = false;
7236 if (bDefaultToShowCheckedOnly) {
7237 //'Show checked only' checkbox should mirror the 'Show unavailable time' checkbox state, unless already in proper state.
7238 var chkShowCheckedOnly = document.getElementById('chkFilterProv');
7239 if (!chkShowCheckedOnly || bShowUnavailTime !== chkShowCheckedOnly.checked) {
7240 showOnlySelectedProvidersCheckedOnGrid(bShowUnavailTime, false);
7241
7242 chkShowCheckedOnly.checked = bShowUnavailTime;
7243
7244 //Calling 'showOnlySelectedProvidersCheckedOnGrid' will fire 'showProviderUnavailableTime' so it will not need to be called again.
7245 bCallShowProviderUnavailableTime = false;
7246 }
7247 }
7248
7249 if (bCallShowProviderUnavailableTime || !bShowUnavailTime) {
7250 //Show/hide unavailable time on schedule
7251 showProviderUnavailableTime(bShowUnavailTime);
7252 }
7253
7254 //Restore the selected provider before the 'Open Shifts' checkbox state changed and set itself as the selected provider
7255 if (bOpenStateChanged) {
7256 var nIndex = getProviderRowIndexOnGrid(nSelectedProvId);
7257 UpdateProviderInfo(nIndex, null);
7258 }
7259}
7260
7261//VJF 09/22/2017 - Add/Remove css class from body tag indicating page is showing checked providers only.
7262//This class is important for controlling the visibility of uncounted open shifts.
7263function addRemoveBodyCheckedOnlyCssClass(body, bAdd) {
7264 var SHOW_CHECKED_ONLY_CSS_CLASS = "show-checked-only";
7265 var bodyWrap;
7266 if (body == undefined) {
7267 bodyWrap = $("body");
7268 } else {
7269 bodyWrap = $(body);
7270 }
7271
7272 if (typeof bAdd != 'boolean') {
7273 var chkbox = document.getElementById('chkFilterProv');
7274 bAdd = chkbox && chkbox.checked;
7275 }
7276
7277 if (bAdd)
7278 bodyWrap.addClass(SHOW_CHECKED_ONLY_CSS_CLASS)
7279 else
7280 bodyWrap.removeClass(SHOW_CHECKED_ONLY_CSS_CLASS);
7281
7282}
7283
7284//VJF 09/13/2017 - Show/hide provider unavailable times coloring on schedule
7285function showProviderUnavailableTime(bShowUnavailTime) {
7286
7287 //Remove any existing unavailable time formatting from schedule
7288 if (!bShowUnavailTime) {
7289 configPageForUnavailableTime(false);
7290 removeUnavailableTimesHtml();
7291 ToggleGrdProvColorCol(false);
7292
7293 //Restore filter coloring, when applicable
7294 if (gApplyColorsChecked) {
7295 if (gColorContext == COLORCONTEXT.PractType) {
7296 restorePractTypeColors();
7297 } else if (gColorContext == COLORCONTEXT.ShiftGroup) {
7298 restoreShiftGroupColors();
7299 } else if (gColorContext == COLORCONTEXT.ShiftStatus) {
7300 restoreShiftStatusHighlighting();
7301 }
7302 }
7303 return;
7304 }
7305
7306 var provGridMasterTable = GetGrdProvInfoMasterTableView();
7307 var providerIdList = "";
7308
7309 //Build a comma delimited list of checked provider ids
7310 for (var i = 0; i < provGridMasterTable.get_dataItems().length; i++) {
7311 var row = provGridMasterTable.get_dataItems()[i];
7312 var rowId = row._element.id;
7313 var chkProvColor = $("#" + rowId).find("#chkProviderColor")[0]; //Gets the checkbox object
7314 if (chkProvColor.checked) {
7315
7316 var provId = $(chkProvColor).closest('tr').children('td:first').text();
7317 var selectedColor = GetDataProvColor(chkProvColor);
7318
7319 if (providerIdList.length > 0) providerIdList += ",";
7320
7321 //Add prov id to list, excluding the Open Shift provider id
7322 if (provId > 0)
7323 providerIdList += provId;
7324 }
7325 }
7326
7327 configPageForUnavailableTime(true);
7328 removeUnavailableTimesHtml();
7329
7330 //Show Provider grid colors column if not currently visible
7331 ToggleGrdProvColorCol(true);
7332
7333 //Call server using ajax call to get the unavailable time for each provider
7334 if (providerIdList.length > 0) {
7335 ShowLoadingPanel();
7336 PageMethods.GetProviderUnavailableTimeAsJson(providerIdList, UnavailableTimeSuccess, UnavailableTimeError);
7337 }
7338
7339 /*** Callback functions are imbedded in function to localize scope and have providerIdList available. ***/
7340 //Callback function on success returning json of each shift and provider off times.
7341 function UnavailableTimeSuccess(result) {
7342 var unavailableShiftList = JSON.parse(result);
7343
7344 modifyAppointmentHtmlToDisplayUnavailableProviders(unavailableShiftList, providerIdList);
7345 HideLoadingPanel();
7346 }
7347
7348 //Callback function on AjaxSetSessionKeys error
7349 function UnavailableTimeError(error) {
7350 if (error.get_statusCode() === 401) {
7351 //Authorization Failed
7352 var topWindow = GetTopAccessibleWindow(window);
7353 topWindow.location = gSLogoutUrl;
7354 } else {
7355 //Catch 500 internal server errors and others
7356 RadAlert('Unable to get provider unavailable time information.\n' +
7357 error.get_statusCode() + " " + error.get_message());
7358 }
7359 HideLoadingPanel();
7360 }
7361}
7362
7363//VJF 09/13/2017 - Remove all unavailable time html from the schedule
7364function removeUnavailableTimesHtml() {
7365 //Take the css class off the appointment div
7366 var dateWrapDivs = $(".rsDateWrap");
7367 var aptContentDivs = $(".rsAptContent");
7368
7369 dateWrapDivs.removeClass(UNAVAILABLE_CONTENT_CSS_CLASS);
7370 aptContentDivs.removeClass(UNAVAILABLE_CONTENT_CSS_CLASS);
7371 aptContentDivs.removeClass("hidden");
7372
7373 //Remove all added provider unavailable divs
7374 $(".unavailable", dateWrapDivs).remove();
7375 $(".unavailable", aptContentDivs).remove();
7376}
7377
7378//VJF 09/13/2017 - Add/Remove css class from body tag indicating page is showing unavailable time.
7379//VJF 09/22/2017 - Add/Remove css class from body tag indicating page is showing checked providers only.
7380function configPageForUnavailableTime(bShow) {
7381 var UNAVAILABLE_TIME_CSS_CLASS = "unavail-time";
7382
7383 var body = document.getElementsByTagName("body")[0];
7384
7385 if (bShow) {
7386 //Add if not already there
7387 if (!body.classList.contains(UNAVAILABLE_TIME_CSS_CLASS)) {
7388 body.classList.add(UNAVAILABLE_TIME_CSS_CLASS);
7389 }
7390
7391 } else {
7392 //Remove
7393 body.classList.remove(UNAVAILABLE_TIME_CSS_CLASS);
7394 }
7395}
7396
7397//VJF 09/13/2017 - Iterate through all the appointments on the schedule looking for a match by shift primary key
7398//against the list of shifts with unavailable providers. It will add html to the rsAptContent or rsDateWrap div to show a color coded overlay.
7399//Unavailable Time Shift Object structure:
7400// Shift PK: 1482252896|31|-1|20170625
7401// Prov Objects[]:
7402// Desc:"Blend, T. Approved Barney Meeting (06/05/2017 08:00 AM-09:00 AM) Blend, T. Scheduled on 06/04/2017 (COUNTY Day shift 07:00 AM-03:00 PM)"
7403// Id:51
7404// Type:4
7405function modifyAppointmentHtmlToDisplayUnavailableProviders(unavailableShiftList, providerIdList) {
7406
7407 if (unavailableShiftList.length > 0) {
7408
7409 //Create array of provider ids to easily check shift assignments.
7410 var arrSelectedProvIds = providerIdList.split(",");
7411
7412 //When showing opens, add the provider id -1 representing an unassigned open shift
7413 var showOpens = FindCheckboxFromGridById(0).checked;
7414 if (showOpens)
7415 arrSelectedProvIds.push("-1");
7416
7417 //'Show checked only' state will determine where to insert unavailable html, shift(rsAptContent)/header(rsDateWrap).
7418 //When it is not checked all unavailable time will be added to the shift.
7419 var showCheckedOnly = false;
7420 var chkShowCheckedOnly = document.getElementById('chkFilterProv');
7421 if (chkShowCheckedOnly && chkShowCheckedOnly.checked) {
7422 showCheckedOnly = chkShowCheckedOnly.checked;
7423 }
7424
7425 var aptDate, aptDatePrevious;
7426 var arrProvIdsInUnavailableHeader = [];
7427 var headerUnavailProvs = [];
7428
7429 //Loop through each scheduler control on the page
7430 $(".RadScheduler").each(function (index) {
7431 var scheduler = $find($(this).attr('id'));
7432
7433 //Loop through each appointment
7434 var apts = scheduler.get_appointments();
7435 for (var i = 0; i < apts.get_count() ; i++) {
7436 var apt = apts.getAppointment(i);
7437
7438 //Get appointment attributes
7439 var aptAttribs = apt.get_attributes();
7440
7441 //Get appointment assigned provider id
7442 var provId = aptAttribs.getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_PROV_ID);
7443
7444 //Get appointment shift primary key
7445 var aptShiftPK = aptAttribs.getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_APPT_ID);
7446
7447 //Get the appointment date from the primary key. It is the last delimited item in key, ex 1482252896|31|-1|20170625
7448 var aptDate = aptShiftPK.split("|").pop();
7449
7450 //When showing checked only some shifts with unavailable time will not be visible so the unavailable time needs to be displayed in the header.
7451 //In this case at the beginning of each day determine which selected providers have unavailable time that will go in the header.
7452 var bAptDateChanged = (aptDate !== aptDatePrevious);
7453 if (showCheckedOnly && bAptDateChanged) {
7454 arrProvIdsInUnavailableHeader = getProvIdsInUnavailableHeader(apts, i, unavailableShiftList, arrSelectedProvIds);
7455 }
7456
7457 var shiftUnavailProvs = [];
7458
7459 //Loop through unavailable shifts looking for a match
7460 for (var j = 0; j < unavailableShiftList.length; j++) {
7461 var unavailShift = unavailableShiftList[j];
7462
7463 var unavailShiftDate = unavailShift.PK.split("|").pop();
7464
7465 //Unavailable shift dates will be in chronological order and removed from the list as each is processed.
7466 //So if the date is different than the appointment date there will be no more matches in the list.
7467 if (unavailShiftDate === aptDate) {
7468
7469 if (aptShiftPK === unavailShift.PK) {
7470
7471 if (showCheckedOnly) {
7472
7473 //Divide the unavailable providers between those to appear on the the shift and those to appear on the header
7474 sortOutUnavailableProvsBetweenShiftAndHeader(provId, unavailShift, arrSelectedProvIds, arrProvIdsInUnavailableHeader, headerUnavailProvs, shiftUnavailProvs);
7475
7476 } else {
7477 //All unavailable providers are to be added on shift
7478 shiftUnavailProvs = unavailShift.Provs;
7479 }
7480
7481 //Insert provider unavailable times to shift
7482 if (shiftUnavailProvs.length > 0) {
7483 var aptContentDiv = $("#" + apt.get_element().id + " .rsAptContent"); //shift div
7484 insertUnavailableHtml(aptContentDiv, shiftUnavailProvs);
7485 shiftUnavailProvs.length = 0; //clear
7486 }
7487
7488 //Remove the added shift from list to speed processing since it is no longer relevant.
7489 //Decrement the index to adjust for the removed shift.
7490 unavailableShiftList.splice(j, 1);
7491 j--;
7492 }
7493 }
7494 }
7495
7496 //Header unavailable time gets inserted after the last appointment/shift of the day has been processed.
7497 if (headerUnavailProvs.length > 0 && isLastAptOfDay(apts, i)) {
7498 var dateWrapDiv = $("#" + apt.get_element().id).parent().prevAll(".rsDateWrap"); //header div
7499 insertUnavailableHtml(dateWrapDiv, headerUnavailProvs);
7500 headerUnavailProvs.length = 0; //clear
7501 }
7502
7503 //Stop examining appointments when all unavailable shifts have been displayed.
7504 if (unavailableShiftList.length === 0) {
7505 break;
7506 }
7507
7508 aptDatePrevious = aptDate;
7509 } //apt loop
7510 }); //schedule loop
7511 } //no unavailable times
7512}
7513
7514//Determine if the next schedule appointment is on a different day
7515function isLastAptOfDay(apts, aptIndex) {
7516 var isLastDay = true;
7517
7518 //Check that next day exists
7519 if (aptIndex < apts.get_count() - 1) {
7520
7521 //Get appointment shift primary keys
7522 var aptShiftPK = apts.getAppointment(aptIndex).get_attributes().getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_APPT_ID);
7523 var aptShiftPKNext = apts.getAppointment(aptIndex + 1).get_attributes().getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_APPT_ID);
7524
7525 //Get the appointment date from the primary key. It is the last delimited item in key, ex 1482252896|31|-1|20170625
7526 var aptDate = aptShiftPK.split("|").pop();
7527 var aptDateNext = aptShiftPKNext.split("|").pop();
7528
7529 isLastDay = (aptDate !== aptDateNext);
7530 }
7531
7532 return isLastDay;
7533}
7534
7535//Sort out the unavailable times for a shift between those that need to appear on the date header or the shift.
7536//This call is only relevant when showing only checked/selected providers, and the header is required to display unavailable times.
7537//It will put the unavailable shift providers in the appropriate arrays header/shift that gets returned.
7538//Params: arrProvIdsInUnavailableHeader - contains the provider ids, for the day the shift falls on, that will need to be displayed in the header.
7539function sortOutUnavailableProvsBetweenShiftAndHeader(assignedProvId, unavailShift, arrSelectedProvIds, arrProvIdsInUnavailableHeader, headerUnavailProvsOut, shiftUnavailProvsOut) {
7540
7541 //Are there providers that need to display in header
7542 if (arrProvIdsInUnavailableHeader.length > 0) {
7543
7544 //Loop through each provider with unavailable time for the shift
7545 for (var i = 0; i < unavailShift.Provs.length; i++) {
7546
7547 //Add unavailable time to the header when the provider id is in the header array
7548 if (arrProvIdsInUnavailableHeader.indexOf(unavailShift.Provs[i].Id) > -1) {
7549
7550 //Check for existence of provider in header list
7551 var bProviderExistsInHeader = false;
7552 for (var j = 0; j < headerUnavailProvsOut.length; j++) {
7553
7554 //Combine provider's unavailable time information
7555 if (headerUnavailProvsOut[j].Id === unavailShift.Provs[i].Id) {
7556
7557 headerUnavailProvsOut[j].Desc = combineUnavailableDescriptions(headerUnavailProvsOut[j].Desc, unavailShift.Provs[i].Desc, " ");
7558
7559 //'Working' type (4) has precedence
7560 if (unavailShift.Provs[i].Type === "4") {
7561 headerUnavailProvsOut[j].Type = unavailShift.Provs[i].Type;
7562 }
7563
7564 bProviderExistsInHeader = true;
7565 break;
7566 }
7567 }
7568
7569 if (!bProviderExistsInHeader) {
7570 //Add to header's unavailable providers
7571 headerUnavailProvsOut.push(unavailShift.Provs[i]);
7572 }
7573 } else {
7574 //Add to shift's unavailable providers.
7575 //It is only relevant when the shift will be displayed, which is when it has a selected provider assigned to it.
7576 if (arrSelectedProvIds.indexOf(assignedProvId) > -1)
7577 shiftUnavailProvsOut.push(unavailShift.Provs[i]);
7578 }
7579 }
7580 } else {
7581 //There are no providers that need to appear in the header - add them all to the shift array,
7582 //It is only relevant when the shift will be displayed, which is when it has a selected provider assigned to it.
7583 if (arrSelectedProvIds.indexOf(assignedProvId) > -1)
7584 shiftUnavailProvsOut.push.apply(shiftUnavailProvsOut, unavailShift.Provs);
7585 }
7586}
7587
7588//Combines two sets of unavailable time descriptions, eliminating duplicates.
7589//Unavailable descriptions may have multiple reasons when created on server, so breakout each reason,
7590//remove duplicates and recombine.
7591function combineUnavailableDescriptions(desc1, desc2, delimiter) {
7592
7593 var arrDesc1 = desc1.split(delimiter);
7594 var arrDesc2 = desc2.split(delimiter);
7595
7596 var arrResult = arrDesc1.concat(arrDesc2); //Contains duplicates
7597 var arrResultUnique = arrResult.filter(function (item, pos) { return arrResult.indexOf(item) == pos; }); //De-dupe
7598
7599 return arrResultUnique.join(delimiter);
7600}
7601
7602//Insert the provider unavailable time into a content div
7603function insertUnavailableHtml(contentDiv, unavailProviders) {
7604
7605 if (contentDiv && contentDiv.length) {
7606
7607 //Append unavailable html inside the content div at the end
7608 var unavailableHtml = getShiftUnavailableProviderHtml(unavailProviders);
7609 contentDiv.append(unavailableHtml);
7610
7611 //Content div gets css class to style container and inserted elements.
7612 contentDiv.addClass(UNAVAILABLE_CONTENT_CSS_CLASS);
7613 } else {
7614 alert("Error: Could not find insertUnavailableHtml:contentDiv. This should not happen.");
7615 }
7616}
7617
7618//Determine which of the selected providers have unavailable time on a single day, that will not appear on any
7619//visible shifts because the display is only showing selected providers.
7620//Provider ids in header = Providers ids with unavailable time on day - Provider ids with unavailable time on shifts that will display on day
7621function getProvIdsInUnavailableHeader(apts, nIndexOfFirstAptOfDay, unavailableShiftList, arrSelectedProvIds) {
7622
7623 var arrProvIdsUnavailableOnDate = [];
7624 var arrProvIdsUnavailableOnVisibleShifts = [];
7625 var aptDate, aptDatePrevious;
7626
7627 for (var i = nIndexOfFirstAptOfDay; i < apts.get_count() ; i++) {
7628 var apt = apts.getAppointment(i);
7629
7630 //Get appointment attributes
7631 var aptAttribs = apt.get_attributes();
7632
7633 //Get appointment shift primary key
7634 var aptShiftPK = aptAttribs.getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_APPT_ID);
7635
7636 //Get the appointment date from the primary key. It is the last delimited item in key, ex 1482252896|31|-1|20170625
7637 aptDate = aptShiftPK.split("|").pop();
7638
7639 //Only interested in appointments on a single day. Get out when date changes. Except first iteration.
7640 if (i !== nIndexOfFirstAptOfDay && aptDate !== aptDatePrevious) {
7641 break;
7642 }
7643
7644 //Get appointment assigned provider id
7645 var assignedProvId = aptAttribs.getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_PROV_ID);
7646
7647 //Determine if apt is an open 'not counted' shift, since they will not display.
7648 var bIsShiftCounted = aptAttribs.getAttribute(WebConstants.CAL_APPT_ATTR_TOKEN_COUNT_SHIFT) == 0 ? true : false;
7649 var bIsNotCountedOpenShift = !bIsShiftCounted && assignedProvId === "-1";
7650
7651 //Loop through unavailable shifts looking for a primary key match on apt key
7652 for (var j = 0; j < unavailableShiftList.length; j++) {
7653
7654 var unavailShift = unavailableShiftList[j];
7655 var unavailShiftDate = unavailShift.PK.split("|").pop();
7656
7657 //Unavailable shifts are in chronological order so if the unavailable date is past the apt date then
7658 //there are not more relevant unavailable times. The dates are in the format YYYYMMDD so a string comparison can be used.
7659 if (unavailShiftDate > aptDate)
7660 break;
7661
7662 //Find unavailable times for shift where PK and date match
7663 if (aptShiftPK === unavailShift.PK && aptDate === unavailShiftDate) {
7664
7665 //Loop through each provider detail in the unavailable shift
7666 for (var k = 0; k < unavailShift.Provs.length; k++) {
7667
7668 //Build list of providers id that have unavailable time, without duplicates.
7669 var provInfo = unavailShift.Provs[k];
7670 if (arrProvIdsUnavailableOnDate.indexOf(provInfo.Id) === -1) {
7671 arrProvIdsUnavailableOnDate.push(provInfo.Id);
7672 }
7673
7674 //Build list of providers id with unavailable time on DISPLAYED SHIFTS, without duplicates.
7675 //Any shift assigned to a selected provider will be displayed.
7676 //Not-counted open shifts will not displays, any unavailable time for the shift should not be considered visible.
7677 if ((arrSelectedProvIds.indexOf(assignedProvId) > -1 && !bIsNotCountedOpenShift) && arrProvIdsUnavailableOnVisibleShifts.indexOf(provInfo.Id) === -1) {
7678 arrProvIdsUnavailableOnVisibleShifts.push(provInfo.Id);
7679 }
7680 }
7681 }
7682 }
7683
7684 aptDatePrevious = aptDate;
7685 }
7686 //Remove the provider ids that have conflicts on visible days from all the providers that have a conflict,
7687 //to determine the providers with unavailable time that need to be represented in the header.
7688 return arrProvIdsUnavailableOnDate.filter(function (item) { return arrProvIdsUnavailableOnVisibleShifts.indexOf(parseInt(item)) < 0; });
7689}
7690
7691/*VJF 05/31/2017 - Generate the html representing the providers unavailable time to be inserted into the shift or header date container .
7692The html has two main container divs, one for color coding that will appear behind the containers content,
7693and one with the tooltips of off descriptions that appear in front of the containers content.
7694
7695<div class="rsAptContent/(rsDate rsDateWrap) unavail-content">
7696 <div class="evtLocShift inactive">S67</div><div id="1133" class="evtProv">Vince</div>
7697 <a class="rsAptDelete" href="#">delete</a>
7698
7699 <div class="unavailable">
7700 <div class="unavailable-prov" style="background-color: #9999FF;"></div>
7701 <div class="unavailable-prov" style="background-color: #87a4ff;"></div>
7702 </div>
7703 <div class="unavailable tooltip">
7704 <div class="unavailable-prov" title="Blend, T. Scheduled on 01/31/2017 (CH MD Night shift (11:00 PM-07:00 AM)"></div>
7705 <div class="unavailable-prov" title="Bland, M. Scheduled on 01/31/2017 (CH MD Mid shift (02:00 PM-11:30 PM)"></div>
7706 </div>
7707</div>*/
7708function getShiftUnavailableProviderHtml(unavailableProvInfoList) {
7709
7710 var unavailableColorHtml = "<div class=\"unavailable\">";
7711 var unavailableTooltipHtml = "<div class=\"unavailable tooltip\">";
7712 var provGridMasterTable = GetGrdProvInfoMasterTableView();
7713
7714 //Loop through each provider detail in the unavailable shift
7715 for (var i = 0; i < unavailableProvInfoList.length; i++) {
7716 var provInfo = unavailableProvInfoList[i];
7717
7718 //Get the provider's data item/row from the the provider grid, for the color
7719 var dataItem = FindDataItemById(provInfo.Id);
7720 if (dataItem != null) {
7721
7722 //Get the color associated with the provider. Since it is checked it will always have a color.
7723 var dataProvColorCol = provGridMasterTable.getCellByColumnUniqueName(dataItem, "DataProvColor");
7724 var color = dataProvColorCol.innerHTML;
7725
7726 //Create div with provider's background color
7727 unavailableColorHtml += "<div class=\"unavailable-prov type" + provInfo.Type + "\" style=\"background-color:" + color + ";\"></div>";
7728
7729 //Create div with provider's off time descriptions tooltip
7730 unavailableTooltipHtml += "<div class=\"unavailable-prov\" title=\"" + provInfo.Desc + "\"></div>";
7731 }
7732 }
7733
7734 unavailableColorHtml += "</div>";
7735 unavailableTooltipHtml += "</div>";
7736
7737 return unavailableColorHtml + unavailableTooltipHtml;
7738}
7739
7740/**********************************************************/
7741/* PROVIDER UNAVAILABLE TIME - End */
7742/**********************************************************/
7743
7744//TJF 9/25/2017 - Get shift group breakdown page url with params used to begin schedule generation
7745//VJF 5/02/2018 - Created reusable function to get the period, location id and schedule group id
7746function GetShiftGroupBreakdownURL() {
7747 var nSchedulePeriodId = GetAdminScheduleSelectedPeriodId();
7748 var nLocationId = GetAdminScheduleLocationId();
7749 var nScheduleGroupId = GetAdminScheduleSchedGroupId();
7750
7751 return "../config/schedule/generate/shiftbreakdown.aspx?schedulePeriodId=" + nSchedulePeriodId + "&locationId=" + nLocationId + "&scheduleGroupId=" + nScheduleGroupId;
7752}
7753
7754function GetAdminScheduleSelectedPeriodId() {
7755 var nSchedulePeriodId = $find("ddlSchedulePeriod").get_selectedItem().get_value();
7756 return nSchedulePeriodId;
7757}
7758
7759function GetAdminScheduleLocationId() {
7760 var tbbScheduleLocation = $find("tbCalendarSubMenu").findItemByValue("tbbScheduleLocation");
7761 var ddlScheduleLocation = tbbScheduleLocation.findControl("ddlScheduleLocation");
7762 var nLocationId = ddlScheduleLocation ? ddlScheduleLocation.get_selectedItem().get_value() : -1;
7763 return nLocationId;
7764}
7765
7766function GetAdminScheduleSchedGroupId() {
7767 var tbbScheduleGroup = $find("tbCalendarSubMenu").findItemByValue("tbbScheduleGroup");
7768 var ddlScheduleGroup = tbbScheduleGroup.findControl("ddlScheduleGroup");
7769 var nScheduleGroupId = ddlScheduleGroup ? ddlScheduleGroup.get_selectedItem().get_value() : -1;
7770 return nScheduleGroupId;
7771}
7772
7773//VJF 01/25/2018 - Fix for Chrome that changes viewport calculations used for RadMenu positioning,
7774//to allow context submenus to be positioned correctly when schedule zooming is being applied.
7775function ChromeContextMenuViewPortFix() {
7776 Telerik.Web.UI.RadMenu._getViewPortSize = function () {
7777 var viewPortSize = $telerik.getViewPortSize();
7778
7779 // The document scroll is not included in the viewport size
7780 // calculation under FF/quirks and Edge.
7781 var quirksMode = document.compatMode != "CSS1Compat";
7782 if (($telerik.isFirefox && quirksMode) || Telerik.Web.Browser.edge) {
7783 viewPortSize.height += document.body.scrollTop;
7784 viewPortSize.width += document.body.scrollLeft;
7785 }
7786 else if (Telerik.Web.Browser.chrome) {
7787 viewPortSize.height += Math.max(document.body.scrollTop, document.scrollingElement.scrollTop);
7788 viewPortSize.width += Math.max(document.body.scrollLeft, document.scrollingElement.scrollLeft);
7789 }
7790
7791 return viewPortSize;
7792 };
7793}