OTRS API Reference JavaScript

Source: Core.Agent.AppointmentCalendar.js

  1. // --
  2. // Copyright (C) 2001-2020 OTRS AG, https://otrs.com/
  3. // --
  4. // This software comes with ABSOLUTELY NO WARRANTY. For details, see
  5. // the enclosed file COPYING for license information (GPL). If you
  6. // did not receive this file, see https://www.gnu.org/licenses/gpl-3.0.txt.
  7. // --
  8. /*global Clipboard */
  9. "use strict";
  10. var Core = Core || {};
  11. Core.Agent = Core.Agent || {};
  12. /**
  13. * @namespace Core.Agent.AppointmentCalendar
  14. * @memberof Core.Agent
  15. * @author OTRS AG
  16. * @description
  17. * This namespace contains the appointment calendar functions.
  18. */
  19. Core.Agent.AppointmentCalendar = (function (TargetNS) {
  20. // Appointment days cache and ready flag
  21. var AppointmentDaysCache,
  22. AppointmentDaysCacheRefreshed = false,
  23. AJAXCounter = 0,
  24. CurrentView,
  25. CalendarSources = {};
  26. /**
  27. * @name Init
  28. * @memberof Core.Agent.AppointmentCalendar
  29. * @description
  30. * Initializes the appointment calendar control.
  31. */
  32. TargetNS.Init = function () {
  33. var CalendarConfig = Core.Config.Get('CalendarConfig'),
  34. ResourceConfig,
  35. $CalendarObj = $('#calendar'),
  36. $DatepickerObj,
  37. ClipboardJS,
  38. CurrentAppointment = [];
  39. if (Core.Config.Get('Action') == 'AgentAppointmentAgendaOverview') {
  40. TargetNS.AgentAppointmentAgendaOverview();
  41. return;
  42. }
  43. if (!CalendarConfig) {
  44. return;
  45. }
  46. $DatepickerObj = $('<div />')
  47. .prop('id', 'Datepicker')
  48. .addClass('Hidden')
  49. .insertAfter($CalendarObj),
  50. ClipboardJS = new Clipboard('.CopyToClipboard');
  51. if (Core.Config.Get('TeamID')) {
  52. ResourceConfig = {
  53. url: Core.Config.Get('CGIHandle'),
  54. type: 'POST',
  55. data: {
  56. ChallengeToken: $('#ChallengeToken').val(),
  57. Action: 'AgentAppointmentTeamList',
  58. Subaction: 'ListResources',
  59. TeamID: Core.Config.Get('TeamID')
  60. }
  61. };
  62. }
  63. // Initialize calendar
  64. $CalendarObj.fullCalendar({
  65. header: {
  66. left: 'month,agendaWeek,agendaDay timelineMonth,timelineWeek,timelineDay',
  67. center: 'title',
  68. right: 'jump,today prev,next'
  69. },
  70. customButtons: {
  71. jump: {
  72. text: Core.Language.Translate('Jump'),
  73. click: function() {
  74. ShowDatepicker($CalendarObj, $DatepickerObj, $(this));
  75. }
  76. }
  77. },
  78. defaultView: Core.Config.Get('DefaultView'),
  79. allDayText: Core.Language.Translate('All-day'),
  80. isRTL: Core.Config.Get('IsRTLLanguage'),
  81. columnFormat: 'ddd, D MMM',
  82. timeFormat: 'HH:mm',
  83. slotLabelFormat: 'HH:mm',
  84. titleFormat: 'D MMM YYYY',
  85. weekNumbers: true,
  86. weekNumberTitle: '#',
  87. weekNumberCalculation: 'ISO',
  88. eventLimit: true,
  89. eventLimitText: Core.Language.Translate('more'),
  90. height: 600,
  91. editable: true,
  92. selectable: true,
  93. firstDay: Core.Config.Get('CalendarWeekDayStart'),
  94. monthNames: [
  95. Core.Language.Translate('January'),
  96. Core.Language.Translate('February'),
  97. Core.Language.Translate('March'),
  98. Core.Language.Translate('April'),
  99. Core.Language.Translate('May_long'),
  100. Core.Language.Translate('June'),
  101. Core.Language.Translate('July'),
  102. Core.Language.Translate('August'),
  103. Core.Language.Translate('September'),
  104. Core.Language.Translate('October'),
  105. Core.Language.Translate('November'),
  106. Core.Language.Translate('December')
  107. ],
  108. monthNamesShort: [
  109. Core.Language.Translate('Jan'),
  110. Core.Language.Translate('Feb'),
  111. Core.Language.Translate('Mar'),
  112. Core.Language.Translate('Apr'),
  113. Core.Language.Translate('May'),
  114. Core.Language.Translate('Jun'),
  115. Core.Language.Translate('Jul'),
  116. Core.Language.Translate('Aug'),
  117. Core.Language.Translate('Sep'),
  118. Core.Language.Translate('Oct'),
  119. Core.Language.Translate('Nov'),
  120. Core.Language.Translate('Dec')
  121. ],
  122. dayNames: [
  123. Core.Language.Translate('Sunday'),
  124. Core.Language.Translate('Monday'),
  125. Core.Language.Translate('Tuesday'),
  126. Core.Language.Translate('Wednesday'),
  127. Core.Language.Translate('Thursday'),
  128. Core.Language.Translate('Friday'),
  129. Core.Language.Translate('Saturday')
  130. ],
  131. dayNamesShort: [
  132. Core.Language.Translate('Sun'),
  133. Core.Language.Translate('Mon'),
  134. Core.Language.Translate('Tue'),
  135. Core.Language.Translate('Wed'),
  136. Core.Language.Translate('Thu'),
  137. Core.Language.Translate('Fri'),
  138. Core.Language.Translate('Sat')
  139. ],
  140. buttonText: {
  141. today: Core.Language.Translate('Today'),
  142. month: Core.Language.Translate('Month'),
  143. week: Core.Language.Translate('Week'),
  144. day: Core.Language.Translate('Day'),
  145. timelineMonth: Core.Language.Translate('Timeline Month'),
  146. timelineWeek: Core.Language.Translate('Timeline Week'),
  147. timelineDay: Core.Language.Translate('Timeline Day'),
  148. jump: Core.Language.Translate('Jump'),
  149. prevDatepicker: Core.Language.Translate('Previous'),
  150. nextDatepicker: Core.Language.Translate('Next')
  151. },
  152. schedulerLicenseKey: 'GPL-My-Project-Is-Open-Source',
  153. slotDuration: '00:30:00',
  154. forceEventDuration: true,
  155. nowIndicator: true,
  156. timezone: 'local',
  157. resourceAreaWidth: '21%',
  158. views: {
  159. month: {
  160. titleFormat: 'MMMM YYYY',
  161. columnFormat: 'dddd'
  162. },
  163. agendaDay: {
  164. titleFormat: 'D MMM YYYY',
  165. resources: false
  166. },
  167. timelineMonth: {
  168. slotDuration: '24:00:00',
  169. duration: {
  170. months: 1
  171. },
  172. slotLabelFormat: [
  173. 'D'
  174. ]
  175. },
  176. timelineWeek: {
  177. slotDuration: '02:00:00',
  178. duration: {
  179. week: 1
  180. },
  181. slotLabelFormat: [
  182. 'ddd, D MMM',
  183. 'HH'
  184. ]
  185. },
  186. timelineDay: {
  187. slotDuration: '00:30:00',
  188. duration: {
  189. days: 1
  190. },
  191. slotLabelFormat: [
  192. 'ddd, D MMM',
  193. 'HH:mm'
  194. ]
  195. }
  196. },
  197. loading: function(IsLoading) {
  198. if (IsLoading) {
  199. $('.CalendarWidget').addClass('Loading');
  200. } else {
  201. $('.CalendarWidget').removeClass('Loading');
  202. }
  203. },
  204. viewRender: function(View) {
  205. // Check if we are on a timeline view.
  206. if (View.name === 'timelineWeek' || View.name === 'timelineDay') {
  207. // Add calendar week number to timeline view titles.
  208. window.setTimeout(function () {
  209. $CalendarObj.find('.fc-toolbar > div > h2').append(
  210. $('<span />').addClass('fc-week-number')
  211. .text(View.start.format(' #W'))
  212. );
  213. }, 0);
  214. // Initialize restore settings button.
  215. RestoreDefaultSettingsInit();
  216. }
  217. // Remember view selection
  218. if (CurrentView !== undefined && CurrentView !== View.name) {
  219. Core.AJAX.FunctionCall(
  220. Core.Config.Get('CGIHandle'),
  221. {
  222. ChallengeToken: $('#ChallengeToken').val(),
  223. Action: 'AgentAppointmentEdit',
  224. Subaction: 'UpdatePreferences',
  225. OverviewScreen: Core.Config.Get('OverviewScreen') ? Core.Config.Get('OverviewScreen') : 'CalendarOverview',
  226. DefaultView: View.name
  227. },
  228. function (Response) {
  229. if (!Response.Success) {
  230. Core.Debug.Log('Error updating user preferences!');
  231. }
  232. }
  233. );
  234. }
  235. CurrentView = View.name;
  236. },
  237. select: function(Start, End, JSEvent, View, Resource) {
  238. var Data = {
  239. Start: Start,
  240. End: End,
  241. JSEvent: JSEvent,
  242. View: View,
  243. Resource: Resource
  244. };
  245. TargetNS.OpenEditDialog(Data);
  246. $CalendarObj.fullCalendar('unselect');
  247. },
  248. eventClick: function(CalEvent, JSEvent) {
  249. var Data = {
  250. Start: CalEvent.start,
  251. End: CalEvent.end,
  252. CalEvent: CalEvent
  253. };
  254. TargetNS.OpenEditDialog(Data);
  255. JSEvent.stopPropagation();
  256. return false;
  257. },
  258. eventDrop: function(CalEvent, Delta, RevertFunc) {
  259. var Data = {
  260. CalEvent: CalEvent,
  261. PreviousAppointment: CurrentAppointment,
  262. Delta: Delta,
  263. RevertFunc: RevertFunc
  264. };
  265. UpdateAppointment(Data);
  266. },
  267. eventResize: function(CalEvent, Delta, RevertFunc) {
  268. var Data = {
  269. CalEvent: CalEvent,
  270. PreviousAppointment: CurrentAppointment,
  271. Delta: Delta,
  272. RevertFunc: RevertFunc
  273. };
  274. UpdateAppointment(Data);
  275. },
  276. eventRender: function(CalEvent, $Element) {
  277. var $IconContainer,
  278. $Icon;
  279. if (CalEvent.allDay
  280. || CalEvent.recurring
  281. || CalEvent.parentId
  282. || CalEvent.notification
  283. || CalEvent.ticketAppointmentType) {
  284. // Create container and icon element
  285. $IconContainer = $('<div />').addClass('Icons');
  286. $Icon = $('<i />').addClass('fa');
  287. // Mark appointment with appropriate icon(s)
  288. if (CalEvent.allDay) {
  289. $Icon.clone()
  290. .addClass('fa-sun-o')
  291. .appendTo($IconContainer);
  292. }
  293. if (CalEvent.recurring) {
  294. $Icon.clone()
  295. .addClass('fa-repeat')
  296. .appendTo($IconContainer);
  297. }
  298. if (CalEvent.parentId) {
  299. $Icon.clone()
  300. .addClass('fa-link')
  301. .appendTo($IconContainer);
  302. }
  303. if (CalEvent.notification) {
  304. $Icon.clone()
  305. .addClass('fa-bell')
  306. .appendTo($IconContainer);
  307. }
  308. if (CalEvent.ticketAppointmentType) {
  309. $Icon.clone()
  310. .addClass('fa-char-' + Core.Config.Get('TicketAppointmentConfig')[CalEvent.ticketAppointmentType].Mark)
  311. .appendTo($IconContainer);
  312. }
  313. // Prepend container to the appointment
  314. $Element.find('.fc-content')
  315. .prepend($IconContainer);
  316. }
  317. },
  318. eventResizeStart: function(CalEvent) {
  319. CurrentAppointment.start = CalEvent.start;
  320. CurrentAppointment.end = CalEvent.end;
  321. },
  322. eventDragStart: function(CalEvent) {
  323. CurrentAppointment.start = CalEvent.start;
  324. CurrentAppointment.end = CalEvent.end;
  325. CurrentAppointment.resourceIds = CalEvent.resourceIds;
  326. },
  327. eventMouseover: function(CalEvent, JSEvent) {
  328. var $TooltipObj,
  329. PosX = 0,
  330. PosY = 0,
  331. TooltipHTML = Core.Template.Render('Agent/AppointmentCalendar/AppointmentTooltip', {
  332. 'PluginList': Core.Config.Get('PluginList'),
  333. 'CalEvent': CalEvent,
  334. 'TooltipTemplateResource': Core.Config.Get('TooltipTemplateResource') || 0,
  335. 'TicketAppointmentConfig': Core.Config.Get('TicketAppointmentConfig')
  336. }),
  337. DocumentVisibleLeft = $(document).scrollLeft() + $(window).width(),
  338. DocumentVisibleTop = $(document).scrollTop() + $(window).height(),
  339. LastXPosition,
  340. LastYPosition;
  341. if (!JSEvent) {
  342. JSEvent = window.event;
  343. }
  344. if (JSEvent.pageX || JSEvent.pageY) {
  345. PosX = JSEvent.pageX;
  346. PosY = JSEvent.pageY;
  347. } else if (JSEvent.clientX || JSEvent.clientY) {
  348. PosX = JSEvent.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
  349. PosY = JSEvent.clientY + document.body.scrollTop + document.documentElement.scrollTop;
  350. }
  351. // Increase positions so the tooltip do not overlap with mouse pointer
  352. PosX += 10;
  353. PosY += 10;
  354. if (TooltipHTML.length > 0) {
  355. // Create tooltip object
  356. $TooltipObj = $(TooltipHTML)
  357. .offset({
  358. top: PosY,
  359. left: PosX
  360. })
  361. .appendTo('body');
  362. // Re-calculate top position if needed
  363. LastYPosition = PosY + $TooltipObj.height();
  364. if (LastYPosition > DocumentVisibleTop) {
  365. PosY = PosY - $TooltipObj.height();
  366. $TooltipObj.css('top', PosY + 'px');
  367. }
  368. // Re-calculate left position if needed
  369. LastXPosition = PosX + $TooltipObj.width();
  370. if (LastXPosition > DocumentVisibleLeft) {
  371. PosX = PosX - $TooltipObj.width() - 30;
  372. $TooltipObj.css('left', PosX + 'px');
  373. }
  374. // Show the tooltip
  375. $TooltipObj.fadeIn('fast');
  376. }
  377. },
  378. eventMouseout: function() {
  379. $('.AppointmentTooltip').fadeOut("fast").remove();
  380. },
  381. eventAfterAllRender: function () {
  382. // If the first day in timelineMonth view is not Saturday or Sunday, the first div.fc-bgevent should be removed.
  383. // There is a bug from fullcalendar and it can be solved here. See bug#14764.
  384. if (!$('.fc-timelineMonth-view .fc-slats td.fc-widget-content:eq(0)').hasClass('fc-sat')
  385. && !$('.fc-timelineMonth-view .fc-slats td.fc-widget-content:eq(0)').hasClass('fc-sun')) {
  386. $('.fc-timelineMonth-view .fc-bgevent-container .fc-bgevent:eq(0)').remove();
  387. }
  388. },
  389. events: Core.Config.Get('WorkingHoursConfig'),
  390. resources: ResourceConfig,
  391. resourceColumns: [
  392. {
  393. labelText: Core.Language.Translate('Name'),
  394. field: 'title'
  395. }
  396. ],
  397. resourceLabelText: Core.Language.Translate('Resources')
  398. });
  399. // Initialize datepicker
  400. $DatepickerObj.datepicker({
  401. showOn: 'button',
  402. buttonText: Core.Language.Translate('Jump'),
  403. constrainInput: true,
  404. prevText: Core.Language.Translate('Previous'),
  405. nextText: Core.Language.Translate('Next'),
  406. firstDay: Core.Config.Get('CalendarWeekDayStart'),
  407. showMonthAfterYear: 0,
  408. monthNames: [
  409. Core.Language.Translate('January'),
  410. Core.Language.Translate('February'),
  411. Core.Language.Translate('March'),
  412. Core.Language.Translate('April'),
  413. Core.Language.Translate('May_long'),
  414. Core.Language.Translate('June'),
  415. Core.Language.Translate('July'),
  416. Core.Language.Translate('August'),
  417. Core.Language.Translate('September'),
  418. Core.Language.Translate('October'),
  419. Core.Language.Translate('November'),
  420. Core.Language.Translate('December')
  421. ],
  422. monthNamesShort: [
  423. Core.Language.Translate('Jan'),
  424. Core.Language.Translate('Feb'),
  425. Core.Language.Translate('Mar'),
  426. Core.Language.Translate('Apr'),
  427. Core.Language.Translate('May'),
  428. Core.Language.Translate('Jun'),
  429. Core.Language.Translate('Jul'),
  430. Core.Language.Translate('Aug'),
  431. Core.Language.Translate('Sep'),
  432. Core.Language.Translate('Oct'),
  433. Core.Language.Translate('Nov'),
  434. Core.Language.Translate('Dec')
  435. ],
  436. dayNames: [
  437. Core.Language.Translate('Sunday'),
  438. Core.Language.Translate('Monday'),
  439. Core.Language.Translate('Tuesday'),
  440. Core.Language.Translate('Wednesday'),
  441. Core.Language.Translate('Thursday'),
  442. Core.Language.Translate('Friday'),
  443. Core.Language.Translate('Saturday')
  444. ],
  445. dayNamesShort: [
  446. Core.Language.Translate('Sun'),
  447. Core.Language.Translate('Mon'),
  448. Core.Language.Translate('Tue'),
  449. Core.Language.Translate('Wed'),
  450. Core.Language.Translate('Thu'),
  451. Core.Language.Translate('Fri'),
  452. Core.Language.Translate('Sat')
  453. ],
  454. dayNamesMin: [
  455. Core.Language.Translate('Su'),
  456. Core.Language.Translate('Mo'),
  457. Core.Language.Translate('Tu'),
  458. Core.Language.Translate('We'),
  459. Core.Language.Translate('Th'),
  460. Core.Language.Translate('Fr'),
  461. Core.Language.Translate('Sa')
  462. ],
  463. isRTL: Core.Config.Get('IsRTLLanguage'),
  464. onSelect: function(DateText) {
  465. $CalendarObj.fullCalendar('gotoDate', new Date(DateText));
  466. $('#DatepickerOverlay').remove();
  467. $DatepickerObj.hide();
  468. },
  469. beforeShowDay: function(DateObject) {
  470. if (AppointmentDaysCacheRefreshed) {
  471. return CheckDate(DateObject);
  472. } else {
  473. return [true];
  474. }
  475. },
  476. onChangeMonthYear: function(Year, Month) {
  477. AppointmentDays($DatepickerObj, Year, Month);
  478. }
  479. });
  480. $.each(CalendarConfig, function (Index, Calendar) {
  481. CalendarSources[Calendar.CalendarID] = {
  482. url: Core.Config.Get('CGIHandle'),
  483. type: 'POST',
  484. data: {
  485. ChallengeToken: $("#ChallengeToken").val(),
  486. Action: 'AgentAppointmentList',
  487. Subaction: 'ListAppointments',
  488. CalendarID: Calendar.CalendarID,
  489. ResourceID: Core.Config.Get('ResourceID'),
  490. TeamID: Core.Config.Get('TeamID')
  491. },
  492. color: Calendar.Color,
  493. textColor: Calendar.TextColor,
  494. borderColor: 'rgba(0, 0, 0, 0.2)',
  495. startParam: 'StartTime',
  496. endParam: 'EndTime',
  497. // Workaround for removeEventSource.
  498. googleCalendarId: Calendar.CalendarID,
  499. eventDataTransform: function(AppointmentData) {
  500. var TicketAppointmentConfig = Core.Config.Get('TicketAppointmentConfig');
  501. return {
  502. id: AppointmentData.AppointmentID,
  503. parentId: AppointmentData.ParentID,
  504. start: AppointmentData.StartTime,
  505. startDate: AppointmentData.StartDate,
  506. end: AppointmentData.EndTime,
  507. endDate: AppointmentData.EndDate,
  508. title: AppointmentData.Title,
  509. description: AppointmentData.Description,
  510. location: AppointmentData.Location,
  511. calendarId: AppointmentData.CalendarID,
  512. allDay: parseInt(AppointmentData.AllDay, 10) ? true : false,
  513. recurring: parseInt(AppointmentData.Recurring, 10) ? true : false,
  514. teamIds: AppointmentData.TeamID,
  515. teamNames: AppointmentData.TeamNames,
  516. resourceIds: AppointmentData.ResourceID,
  517. resourceNames: AppointmentData.ResourceNames,
  518. pluginData: AppointmentData.PluginData,
  519. calendarName: Calendar.CalendarName,
  520. calendarColor: Calendar.Color,
  521. notification: AppointmentData.NotificationDate.length ? true : false,
  522. notificationDate: AppointmentData.NotificationDate,
  523. ticketAppointmentType: AppointmentData.TicketAppointmentStartDate,
  524. startEditable: typeof AppointmentData.TicketAppointmentStartDate === 'undefined' ? true :
  525. typeof TicketAppointmentConfig[AppointmentData.TicketAppointmentStartDate].NoDrag !== 'undefined' ? false : true,
  526. durationEditable: typeof AppointmentData.TicketAppointmentEndDate === 'undefined' ? true :
  527. typeof TicketAppointmentConfig[AppointmentData.TicketAppointmentStartDate].NoDrag !== 'undefined' ? true : false
  528. };
  529. }
  530. };
  531. });
  532. $('.CalendarSwitch input[type="checkbox"]').each(function () {
  533. CalendarSwitchInit($(this));
  534. });
  535. // Auto open appointment create screen.
  536. if (Core.Config.Get('AppointmentCreate')) {
  537. TargetNS.OpenEditDialog({
  538. Start: Core.Config.Get('AppointmentCreate').Start ? $.fullCalendar.moment(Core.Config.Get('AppointmentCreate').Start) : $.fullCalendar.moment().add(1, 'hours').startOf('hour'),
  539. End: Core.Config.Get('AppointmentCreate').End ? $.fullCalendar.moment(Core.Config.Get('AppointmentCreate').End) : $.fullCalendar.moment().add(2, 'hours').startOf('hour'),
  540. PluginKey: Core.Config.Get('AppointmentCreate').PluginKey ? Core.Config.Get('AppointmentCreate').PluginKey : null,
  541. Search: Core.Config.Get('AppointmentCreate').Search ? Core.Config.Get('AppointmentCreate').Search : null,
  542. ObjectID: Core.Config.Get('AppointmentCreate').ObjectID ? Core.Config.Get('AppointmentCreate').ObjectID : null
  543. });
  544. }
  545. // Auto open appointment edit screen
  546. else if (Core.Config.Get('AppointmentID')) {
  547. TargetNS.OpenEditDialog({ CalEvent: { id: Core.Config.Get('AppointmentID') } });
  548. }
  549. $('#AppointmentCreateButton')
  550. .off('click.AppointmentCalendar')
  551. .on('click.AppointmentCalendar', function () {
  552. TargetNS.OpenEditDialog({
  553. Start: $.fullCalendar.moment().add(1, 'hours').startOf('hour'),
  554. End: $.fullCalendar.moment().add(2, 'hours').startOf('hour')
  555. });
  556. return false;
  557. });
  558. CalendarSettingsInit();
  559. ResourceSettingsInit();
  560. $('#Team').on('change', function() {
  561. $('#ChangeTeamForm').submit();
  562. });
  563. Core.UI.Table.InitTableFilter($('#FilterCalendars'), $('#Calendars'));
  564. ClipboardJS.on('success', function (Event) {
  565. $(Event.trigger).hide()
  566. .fadeIn();
  567. Event.clearSelection();
  568. });
  569. ClipboardJS.on('error', function(Event) {
  570. Core.Form.ErrorTooltips.InitTooltip($(Event.trigger), Core.Language.Translate('Press Ctrl+C (Cmd+C) to copy to clipboard'));
  571. $(Event.trigger).focus();
  572. });
  573. };
  574. /**
  575. * @private
  576. * @name ShowDatepicker
  577. * @memberof Core.Agent.AppointmentCalendar
  578. * @param {jQueryObject} $CalendarObj - Calendar control object.
  579. * @param {jQueryObject} $DatepickerObj - Datepicker control object.
  580. * @param {jQueryObject} $JumpButton - Datepicker button object.
  581. * @description
  582. * Show date picker control.
  583. */
  584. function ShowDatepicker($CalendarObj, $DatepickerObj, $JumpButton) {
  585. var CurrentDate = $CalendarObj.fullCalendar('getDate'),
  586. Year = CurrentDate.format('YYYY'),
  587. Month = CurrentDate.format('M');
  588. $('<div />').prop('id', 'DatepickerOverlay')
  589. .appendTo($('body'))
  590. .on('click.AppointmentCalendar', function () {
  591. $(this).remove();
  592. $DatepickerObj.hide();
  593. });
  594. AppointmentDays($DatepickerObj, Year, Month);
  595. $DatepickerObj.datepicker('setDate', CurrentDate.toDate())
  596. .css({
  597. left: parseInt($JumpButton.offset().left - $DatepickerObj.outerWidth() + $JumpButton.outerWidth(), 10),
  598. top: parseInt($JumpButton.offset().top - $DatepickerObj.outerHeight() + $JumpButton.outerHeight(), 10)
  599. }).fadeIn('fast');
  600. }
  601. /**
  602. * @private
  603. * @name CheckDate
  604. * @memberof Core.Agent.AppointmentCalendar
  605. * @function
  606. * @param {DateObject} DateObject - A JS date object to check.
  607. * @returns {Array} First element is always true, second element contains the name of a CSS
  608. * class, third element a description for the date.
  609. * @description
  610. * Check if date has an appointment.
  611. */
  612. function CheckDate(DateObject) {
  613. var DateMoment = $.fullCalendar.moment(DateObject),
  614. DayAppointments = AppointmentDaysCache[DateMoment.format('YYYY-MM-DD')],
  615. DayClass = DayAppointments ? 'Highlight' : '',
  616. DayDescription = DayAppointments ? DayAppointments.toString() : '';
  617. return [true, DayClass, DayDescription];
  618. }
  619. /**
  620. * @private
  621. * @name AppointmentDays
  622. * @memberof Core.Agent.AppointmentCalendar
  623. * @param {jQueryObject} $DatepickerObj - Datepicker control object.
  624. * @param {Integer} Year - Selected year.
  625. * @param {Integer} Month - Selected month (1-12).
  626. * @description
  627. * Caches the list of appointment days for later use.
  628. */
  629. function AppointmentDays($DatepickerObj, Year, Month) {
  630. var StartTime = $.fullCalendar.moment(Year + '-' + Month, 'YYYY-M').startOf('month'),
  631. EndTime = $.fullCalendar.moment(Year + '-' + Month, 'YYYY-M').add(1, 'months').startOf('month'),
  632. Data = {
  633. ChallengeToken: $('#ChallengeToken').val(),
  634. Action: 'AgentAppointmentList',
  635. Subaction: 'AppointmentDays',
  636. StartTime: StartTime.format('YYYY-MM-DD'),
  637. EndTime: EndTime.format('YYYY-MM-DD')
  638. };
  639. $DatepickerObj.addClass('AJAXLoading');
  640. AppointmentDaysCacheRefreshed = false;
  641. Core.AJAX.FunctionCall(
  642. Core.Config.Get('CGIHandle'),
  643. Data,
  644. function (Response) {
  645. if (Response) {
  646. AppointmentDaysCache = Response;
  647. AppointmentDaysCacheRefreshed = true;
  648. $DatepickerObj.removeClass('AJAXLoading');
  649. // Refresh the date picker because this call is asynchronous
  650. $DatepickerObj.datepicker('refresh');
  651. }
  652. }
  653. );
  654. }
  655. /**
  656. * @public
  657. * @name ShowWaitingDialog
  658. * @memberof Core.Agent.AppointmentCalendar
  659. * @description
  660. * Shows waiting dialog.
  661. */
  662. TargetNS.ShowWaitingDialog = function () {
  663. Core.UI.Dialog.ShowContentDialog('<div class="Spacing Center"><span class="AJAXLoader" title="' + Core.Language.Translate('Loading...') + '"></span></div>', Core.Language.Translate('Loading...'), '10px', 'Center', true);
  664. }
  665. /**
  666. * @public
  667. * @name OpenEditDialog
  668. * @memberof Core.Agent.AppointmentCalendar
  669. * @param {Object} AppointmentData - Hash with appointment data.
  670. * @param {Moment} AppointmentData.Start - Moment object with start date/time.
  671. * @param {Moment} AppointmentData.End - Moment object with end date/time.
  672. * @param {Object} AppointmentData.CalEvent - Calendar event object (FullCalendar).
  673. * @param {Object} AppointmentData.Resource - Calendar resource object (FullCalendar).
  674. * @description
  675. * This method opens the appointment dialog after selecting a time period or an appointment.
  676. */
  677. TargetNS.OpenEditDialog = function (AppointmentData) {
  678. var Data = {
  679. ChallengeToken: $('#ChallengeToken').val(),
  680. Action: 'AgentAppointmentEdit',
  681. Subaction: 'EditMask',
  682. AppointmentID: AppointmentData.CalEvent ? AppointmentData.CalEvent.id : null,
  683. StartYear: !AppointmentData.CalEvent ? AppointmentData.Start.year() : null,
  684. StartMonth: !AppointmentData.CalEvent ? AppointmentData.Start.month() + 1 : null,
  685. StartDay: !AppointmentData.CalEvent ? AppointmentData.Start.date() : null,
  686. StartHour: !AppointmentData.CalEvent ? AppointmentData.Start.hour() : null,
  687. StartMinute: !AppointmentData.CalEvent ? AppointmentData.Start.minute() : null,
  688. EndYear: !AppointmentData.CalEvent ? AppointmentData.End.year() : null,
  689. EndMonth: !AppointmentData.CalEvent ? AppointmentData.End.month() + 1 : null,
  690. EndDay: !AppointmentData.CalEvent ? AppointmentData.End.date() : null,
  691. EndHour: !AppointmentData.CalEvent ? AppointmentData.End.hour() : null,
  692. EndMinute: !AppointmentData.CalEvent ? AppointmentData.End.minute() : null,
  693. AllDay: !AppointmentData.CalEvent ? (AppointmentData.End.hasTime() ? '0' : '1') : null,
  694. TeamID: AppointmentData.Resource ? [ AppointmentData.Resource.TeamID ] : null,
  695. ResourceID: AppointmentData.Resource ? [ AppointmentData.Resource.id ] : null,
  696. PluginKey: AppointmentData.PluginKey ? AppointmentData.PluginKey : null,
  697. Search: AppointmentData.Search ? AppointmentData.Search : null,
  698. ObjectID: AppointmentData.ObjectID ? AppointmentData.ObjectID : null
  699. };
  700. // Make end time for all day appointments inclusive
  701. if (Data.AllDay && Data.AllDay === '1') {
  702. AppointmentData.End.subtract(1, 'day');
  703. Data.EndYear = AppointmentData.End.year();
  704. Data.EndMonth = AppointmentData.End.month() + 1;
  705. Data.EndDay = AppointmentData.End.date();
  706. Data.EndHour = AppointmentData.End.hour();
  707. Data.EndMinute = AppointmentData.End.minute();
  708. }
  709. function EditDialog() {
  710. TargetNS.ShowWaitingDialog();
  711. Core.AJAX.FunctionCall(
  712. Core.Config.Get('CGIHandle'),
  713. Data,
  714. function (HTML) {
  715. Core.UI.Dialog.ShowContentDialog(HTML, Core.Language.Translate('Appointment'), '10px', 'Center', true, undefined, true);
  716. Core.UI.InputFields.Activate($('.Dialog:visible'));
  717. TargetNS.AgentAppointmentEdit();
  718. }, 'html'
  719. );
  720. }
  721. // Repeating event
  722. if (AppointmentData.CalEvent && AppointmentData.CalEvent.parentId) {
  723. Core.UI.Dialog.ShowDialog({
  724. Title: Core.Language.Translate('This is a repeating appointment'),
  725. HTML: Core.Language.Translate('Would you like to edit just this occurrence or all occurrences?'),
  726. Modal: true,
  727. CloseOnClickOutside: true,
  728. CloseOnEscape: true,
  729. PositionTop: '20%',
  730. PositionLeft: 'Center',
  731. Buttons: [
  732. {
  733. Label: Core.Language.Translate('All occurrences'),
  734. Class: 'Primary CallForAction',
  735. Function: function() {
  736. Data.AppointmentID = AppointmentData.CalEvent.parentId;
  737. EditDialog();
  738. }
  739. },
  740. {
  741. Label: Core.Language.Translate('Just this occurrence'),
  742. Class: 'CallForAction',
  743. Function: EditDialog
  744. },
  745. {
  746. Type: 'Close',
  747. Label: Core.Language.Translate('Close this dialog')
  748. }
  749. ]
  750. });
  751. } else {
  752. EditDialog();
  753. }
  754. }
  755. /**
  756. * @private
  757. * @name UpdateAppointment
  758. * @memberof Core.Agent.AppointmentCalendar
  759. * @param {Object} AppointmentData - Hash with appointment data.
  760. * @param {Object} AppointmentData.CalEvent - Calendar event object (FullCalendar).
  761. * @description
  762. * This method updates the appointment with supplied data.
  763. */
  764. function UpdateAppointment(AppointmentData) {
  765. var Data = {
  766. ChallengeToken: $('#ChallengeToken').val(),
  767. Action: 'AgentAppointmentEdit',
  768. Subaction: 'EditAppointment',
  769. AppointmentID: AppointmentData.CalEvent.id,
  770. StartYear: AppointmentData.CalEvent.start.year(),
  771. StartMonth: AppointmentData.CalEvent.start.month() + 1,
  772. StartDay: AppointmentData.CalEvent.start.date(),
  773. StartHour: AppointmentData.CalEvent.start.hour(),
  774. StartMinute: AppointmentData.CalEvent.start.minute(),
  775. EndYear: AppointmentData.CalEvent.end.year(),
  776. EndMonth: AppointmentData.CalEvent.end.month() + 1,
  777. EndDay: AppointmentData.CalEvent.end.date(),
  778. EndHour: AppointmentData.CalEvent.end.hour(),
  779. EndMinute: AppointmentData.CalEvent.end.minute(),
  780. AllDay: AppointmentData.CalEvent.end.hasTime() ? '0' : '1',
  781. Recurring: AppointmentData.CalEvent.recurring ? '1' : '0',
  782. TeamID: AppointmentData.CalEvent.teamIds ? AppointmentData.CalEvent.teamIds : undefined,
  783. ResourceID: AppointmentData.CalEvent.resourceIds ? AppointmentData.CalEvent.resourceIds :
  784. AppointmentData.CalEvent.resourceId ? [ AppointmentData.CalEvent.resourceId ] : undefined
  785. };
  786. // Assigned resource didn't change
  787. if (
  788. AppointmentData.CalEvent.resourceId
  789. && AppointmentData.PreviousAppointment.resourceIds
  790. && $.inArray(
  791. AppointmentData.CalEvent.resourceId,
  792. AppointmentData.PreviousAppointment.resourceIds
  793. ) !== -1
  794. ) {
  795. Data.ResourceID = AppointmentData.PreviousAppointment.resourceIds;
  796. }
  797. function Update() {
  798. Core.UI.Dialog.CloseDialog($('.Dialog:visible'));
  799. Core.AJAX.FunctionCall(
  800. Core.Config.Get('CGIHandle'),
  801. Data,
  802. function (Response) {
  803. if (Response.Success) {
  804. $('#calendar').fullCalendar('refetchEvents');
  805. } else {
  806. AppointmentData.RevertFunc();
  807. }
  808. // Close the dialog
  809. Core.UI.Dialog.CloseDialog($('.Dialog:visible'));
  810. }
  811. );
  812. }
  813. // Make end time for all day appointments inclusive
  814. if (AppointmentData.CalEvent.allDay) {
  815. AppointmentData.CalEvent.end.subtract(1, 'day');
  816. Data.EndYear = AppointmentData.CalEvent.end.year();
  817. Data.EndMonth = AppointmentData.CalEvent.end.month() + 1;
  818. Data.EndDay = AppointmentData.CalEvent.end.date();
  819. Data.EndHour = AppointmentData.CalEvent.end.hour();
  820. Data.EndMinute = AppointmentData.CalEvent.end.minute();
  821. }
  822. // Repeating event
  823. if (AppointmentData.CalEvent.parentId) {
  824. Core.UI.Dialog.ShowDialog({
  825. Title: Core.Language.Translate('This is a repeating appointment'),
  826. HTML: Core.Language.Translate('Would you like to edit just this occurrence or all occurrences?'),
  827. Modal: true,
  828. CloseOnClickOutside: true,
  829. CloseOnEscape: true,
  830. PositionTop: '20%',
  831. PositionLeft: 'Center',
  832. Buttons: [
  833. {
  834. Label: Core.Language.Translate('All occurrences'),
  835. Class: 'Primary CallForAction',
  836. Function: function() {
  837. Data.AppointmentID = AppointmentData.CalEvent.parentId;
  838. Data.Recurring = '1';
  839. Data.UpdateDelta = AppointmentData.Delta.asSeconds();
  840. if (
  841. AppointmentData.CalEvent.start.diff(AppointmentData.PreviousAppointment.start, 'seconds')
  842. === Data.UpdateDelta
  843. &&
  844. AppointmentData.CalEvent.end.diff(AppointmentData.PreviousAppointment.end, 'seconds')
  845. === Data.UpdateDelta
  846. ) {
  847. Data.UpdateType = 'Both';
  848. }
  849. else if (
  850. AppointmentData.PreviousAppointment.start.diff(AppointmentData.CalEvent.start, 'seconds')
  851. === Data.UpdateDelta
  852. ) {
  853. Data.UpdateType = 'StartTime';
  854. Data.UpdateDelta = Data.UpdateDelta * -1;
  855. }
  856. else if (
  857. AppointmentData.CalEvent.end.diff(AppointmentData.PreviousAppointment.end, 'seconds')
  858. === Data.UpdateDelta
  859. ) {
  860. Data.UpdateType = 'EndTime';
  861. }
  862. Update();
  863. }
  864. },
  865. {
  866. Label: Core.Language.Translate('Just this occurrence'),
  867. Class: 'CallForAction',
  868. Function: function() {
  869. AppointmentData.CalEvent.parentId = null;
  870. Update();
  871. }
  872. },
  873. {
  874. Type: 'Close',
  875. Label: Core.Language.Translate('Close this dialog'),
  876. Function: function() {
  877. Core.UI.Dialog.CloseDialog($('.Dialog:visible'));
  878. AppointmentData.RevertFunc();
  879. }
  880. }
  881. ]
  882. });
  883. } else {
  884. Update();
  885. }
  886. }
  887. /**
  888. * @private
  889. * @name CalendarSwitchInit
  890. * @memberof Core.Agent.AppointmentCalendar
  891. * @param {jQueryObject} $CalendarSwitch - calendar checkbox element.
  892. * @description
  893. * This method initializes calendar checkbox behavior and loads multiple calendars to the
  894. * FullCalendar control.
  895. */
  896. function CalendarSwitchInit($CalendarSwitch) {
  897. // Initialize enabled sources
  898. if ($CalendarSwitch.prop('checked')) {
  899. $('#calendar').fullCalendar('addEventSource', CalendarSources[$CalendarSwitch.data('id')]);
  900. }
  901. // Register change event handler
  902. $CalendarSwitch.off('change.AppointmentCalendar').on('change.AppointmentCalendar', function() {
  903. if ($('.CalendarSwitch input:checked').length > Core.Config.Get('CalendarLimit')) {
  904. $CalendarSwitch.prop('checked', false);
  905. Core.UI.Dialog.ShowAlert(Core.Language.Translate('Too many active calendars'), Core.Language.Translate('Please either turn some off first or increase the limit in configuration.'));
  906. } else {
  907. CalendarSwitchSource($CalendarSwitch);
  908. }
  909. });
  910. }
  911. /**
  912. * @private
  913. * @name CalendarSwitchSource
  914. * @memberof Core.Agent.AppointmentCalendar
  915. * @param {jQueryObject} $CalendarSwitch - calendar checkbox element.
  916. * @description
  917. * This method enables/disables calendar source in FullCalendar control and stores
  918. * selection to user preferences.
  919. */
  920. function CalendarSwitchSource($CalendarSwitch) {
  921. var CalendarSelection = [];
  922. // Show/hide the calendar appointments
  923. if ($CalendarSwitch.prop('checked')) {
  924. $('#calendar').fullCalendar('addEventSource', CalendarSources[$CalendarSwitch.data('id')]);
  925. } else {
  926. $('#calendar').fullCalendar('removeEventSource', CalendarSources[$CalendarSwitch.data('id')]);
  927. }
  928. // Get all checked calendars
  929. $.each($('.CalendarSwitch input:checked'), function (Index, Element) {
  930. CalendarSelection.push($(Element).data('id'));
  931. });
  932. // Store selection in user preferences
  933. Core.AJAX.FunctionCall(
  934. Core.Config.Get('CGIHandle'),
  935. {
  936. ChallengeToken: $('#ChallengeToken').val(),
  937. Action: 'AgentAppointmentEdit',
  938. Subaction: 'UpdatePreferences',
  939. OverviewScreen: Core.Config.Get('OverviewScreen') ? Core.Config.Get('OverviewScreen') : 'CalendarOverview',
  940. CalendarSelection: JSON.stringify(CalendarSelection)
  941. },
  942. function (Response) {
  943. if (!Response.Success) {
  944. Core.Debug.Log('Error updating user preferences!');
  945. }
  946. }
  947. );
  948. }
  949. /**
  950. * @name LocationLinkInit
  951. * @memberof Core.Agent.AppointmentCalendar
  952. * @param {jQueryObject} $LocationObj - location field.
  953. * @param {jQueryObject} $LocationLinks - location links.
  954. * @description
  955. * This method initializes location link behavior.
  956. */
  957. TargetNS.LocationLinkInit = function ($LocationObj, $LocationLinks) {
  958. var LocationValue = $LocationObj.val() || $LocationObj.text();
  959. $LocationLinks.each(function (Index, Element) {
  960. var $LinkObj = $(Element),
  961. BaseURL = $LinkObj.data('baseUrl');
  962. // Update link URL and show it if location is set
  963. if (LocationValue.length > 0 && LocationValue !== '-') {
  964. $LinkObj.attr('href', BaseURL + encodeURIComponent(LocationValue))
  965. .fadeIn('fast');
  966. }
  967. // Otherwise, hide the link
  968. else {
  969. $LinkObj.fadeOut('fast');
  970. }
  971. });
  972. // Register key up event handler
  973. $LocationObj.off('keyup.AppointmentCalendar').on('keyup.AppointmentCalendar', function() {
  974. TargetNS.LocationLinkInit($LocationObj, $LocationLinks);
  975. });
  976. }
  977. /**
  978. * @name AllDayInit
  979. * @memberof Core.Agent.AppointmentCalendar
  980. * @param {jQueryObject} $AllDay - all day checkbox element.
  981. * @description
  982. * This method initializes all day checkbox behavior.
  983. */
  984. TargetNS.AllDayInit = function ($AllDay) {
  985. if ($('#StartMonth:disabled').length > 0) {
  986. return;
  987. }
  988. // Show/hide the start hour/minute and complete end time
  989. if ($AllDay.prop('checked')) {
  990. $('#StartHour,#StartMinute,#EndHour,#EndMinute').val('0')
  991. .prop('disabled', true)
  992. .prop('readonly', true);
  993. } else {
  994. $('#StartHour,#StartMinute,#EndHour,#EndMinute')
  995. .prop('disabled', false)
  996. .prop('readonly', false);
  997. }
  998. // Register change event handler
  999. $AllDay.off('change.AppointmentCalendar').on('change.AppointmentCalendar', function() {
  1000. TargetNS.AllDayInit($AllDay);
  1001. });
  1002. }
  1003. /**
  1004. * @name RecurringInit
  1005. * @memberof Core.Agent.AppointmentCalendar
  1006. * @param {Object} Fields - Array with references to recurring fields.
  1007. * @param {jQueryObject} Fields.$Recurring - field with recurring flag.
  1008. * @param {jQueryObject} Fields.$RecurrenceType - drop down with recurrence type
  1009. * @param {jQueryObject} Fields.$RecurrenceCustomType - Recurrence pattern
  1010. * @param {jQueryObject} Fields.$RecurrenceCustomTypeStringDiv - Custom type drop-down
  1011. * @param {jQueryObject} Fields.$RecurrenceIntervalText - interval text
  1012. * @param {jQueryObject} Fields.$RecurrenceCustomWeeklyDiv - contains week days table
  1013. * @param {jQueryObject} Fields.$RecurrenceCustomMonthlyDiv - contains month days table
  1014. * @param {jQueryObject} Fields.$RecurrenceCustomYearlyDiv - contains months table
  1015. * @param {jQueryObject} Fields.$RecurrenceLimitDiv - layer with recurrence limit fields.
  1016. * @param {jQueryObject} Fields.$RecurrenceLimit - drop down with recurrence limit field.
  1017. * @param {jQueryObject} Fields.$RecurrenceCountDiv - layer with reccurence count field.
  1018. * @param {jQueryObject} Fields.$RecurrenceUntilDiv - layer with reccurence until fields.
  1019. * @param {jQueryObject} Fields.$RecurrenceUntilDay - select field for reccurence until day.
  1020. * @description
  1021. * This method initializes recurrence fields behavior.
  1022. */
  1023. TargetNS.RecurringInit = function (Fields) {
  1024. function RecInit(Fields) {
  1025. var Days = [],
  1026. MonthDays = [],
  1027. Months = [],
  1028. Index;
  1029. if ($('input#Days').length) {
  1030. Days = $('input#Days').val().split(',');
  1031. }
  1032. if ($('input#MonthDays').length) {
  1033. MonthDays = $('input#MonthDays').val().split(',');
  1034. }
  1035. if ($('input#MonthDays').length) {
  1036. Months = $('input#Months').val().split(',');
  1037. }
  1038. for (Index = 0; Index < Days.length; Index++) {
  1039. if (Days[Index] != "") {
  1040. $("#RecurrenceCustomWeeklyDiv button[value=" + Days[Index] + "]")
  1041. .addClass("fc-state-active");
  1042. }
  1043. }
  1044. for (Index = 0; Index < MonthDays.length; Index++) {
  1045. if (MonthDays[Index] != "") {
  1046. $("#RecurrenceCustomMonthlyDiv button[value=" + MonthDays[Index] + "]")
  1047. .addClass("fc-state-active");
  1048. }
  1049. }
  1050. for (Index = 0; Index < Months.length; Index++) {
  1051. if (Months[Index] != "") {
  1052. $("#RecurrenceCustomYearlyDiv button[value=" + Months[Index] + "]")
  1053. .addClass("fc-state-active");
  1054. }
  1055. }
  1056. if (Fields.$RecurrenceType.val() == 0) {
  1057. Fields.$Recurring.val(0);
  1058. Fields.$RecurrenceCustomTypeStringDiv.hide();
  1059. Fields.$RecurrenceCustomWeeklyDiv.hide();
  1060. Fields.$RecurrenceCustomMonthlyDiv.hide();
  1061. Fields.$RecurrenceCustomYearlyDiv.hide();
  1062. Fields.$RecurrenceLimitDiv.hide();
  1063. Fields.$RecurrenceCountDiv.hide();
  1064. Fields.$RecurrenceUntilDiv.hide();
  1065. // Skip validation of RecurrenceUntil fields
  1066. Fields.$RecurrenceUntilDay.addClass('ValidationIgnore');
  1067. }
  1068. else if (Fields.$RecurrenceType.val() == 'Custom') {
  1069. Fields.$Recurring.val(1);
  1070. Fields.$RecurrenceCustomTypeStringDiv.show();
  1071. if (Fields.$RecurrenceCustomType.val()=="CustomDaily") {
  1072. Fields.$RecurrenceCustomWeeklyDiv.hide();
  1073. Fields.$RecurrenceCustomMonthlyDiv.hide();
  1074. Fields.$RecurrenceCustomYearlyDiv.hide();
  1075. Fields.$RecurrenceIntervalText.find('span')
  1076. .hide()
  1077. .end()
  1078. .find('.TextDay')
  1079. .show();
  1080. }
  1081. else if (Fields.$RecurrenceCustomType.val()=="CustomWeekly") {
  1082. Fields.$RecurrenceCustomWeeklyDiv.show();
  1083. Fields.$RecurrenceCustomMonthlyDiv.hide();
  1084. Fields.$RecurrenceCustomYearlyDiv.hide();
  1085. Fields.$RecurrenceIntervalText.find('span')
  1086. .hide()
  1087. .end()
  1088. .find('.TextWeek')
  1089. .show();
  1090. }
  1091. else if (Fields.$RecurrenceCustomType.val()=="CustomMonthly") {
  1092. Fields.$RecurrenceCustomMonthlyDiv.show();
  1093. Fields.$RecurrenceCustomWeeklyDiv.hide();
  1094. Fields.$RecurrenceCustomYearlyDiv.hide();
  1095. Fields.$RecurrenceIntervalText.find('span')
  1096. .hide()
  1097. .end()
  1098. .find('.TextMonth')
  1099. .show();
  1100. }
  1101. else if (Fields.$RecurrenceCustomType.val()=="CustomYearly") {
  1102. Fields.$RecurrenceCustomYearlyDiv.show();
  1103. Fields.$RecurrenceCustomWeeklyDiv.hide();
  1104. Fields.$RecurrenceCustomMonthlyDiv.hide();
  1105. Fields.$RecurrenceIntervalText.find('span')
  1106. .hide()
  1107. .end()
  1108. .find('.TextYear')
  1109. .show();
  1110. }
  1111. Fields.$RecurrenceLimitDiv.show();
  1112. Fields.$RecurrenceLimit.off('change.AppointmentCalendar').on('change.AppointmentCalendar', function() {
  1113. if ($(this).val() == 1) {
  1114. Fields.$RecurrenceCountDiv.hide();
  1115. Fields.$RecurrenceUntilDiv.show();
  1116. // Resume validation of RecurrenceUntil fields
  1117. Fields.$RecurrenceUntilDay.removeClass('ValidationIgnore');
  1118. } else {
  1119. Fields.$RecurrenceUntilDiv.hide();
  1120. Fields.$RecurrenceCountDiv.show();
  1121. // Skip validation of RecurrenceUntil fields
  1122. Fields.$RecurrenceUntilDay.addClass('ValidationIgnore');
  1123. }
  1124. }).trigger('change.AppointmentCalendar');
  1125. Core.UI.InputFields.Activate(Fields.$RecurrenceCustomTypeStringDiv);
  1126. Core.UI.InputFields.Activate(Fields.$RecurrenceLimitDiv);
  1127. }
  1128. else {
  1129. Fields.$Recurring.val(1);
  1130. Fields.$RecurrenceLimitDiv.show();
  1131. Fields.$RecurrenceCustomTypeStringDiv.hide();
  1132. Fields.$RecurrenceCustomWeeklyDiv.hide();
  1133. Fields.$RecurrenceCustomMonthlyDiv.hide();
  1134. Fields.$RecurrenceCustomYearlyDiv.hide();
  1135. Fields.$RecurrenceLimit.off('change.AppointmentCalendar').on('change.AppointmentCalendar', function() {
  1136. if ($(this).val() == 1) {
  1137. Fields.$RecurrenceCountDiv.hide();
  1138. Fields.$RecurrenceUntilDiv.show();
  1139. // Resume validation of RecurrenceUntil fields
  1140. Fields.$RecurrenceUntilDay.removeClass('ValidationIgnore');
  1141. } else {
  1142. Fields.$RecurrenceUntilDiv.hide();
  1143. Fields.$RecurrenceCountDiv.show();
  1144. // Skip validation of RecurrenceUntil fields
  1145. Fields.$RecurrenceUntilDay.addClass('ValidationIgnore');
  1146. }
  1147. }).trigger('change.AppointmentCalendar');
  1148. Core.UI.InputFields.Activate(Fields.$RecurrenceLimitDiv);
  1149. }
  1150. Fields.$RecurrenceCustomType.off('change.AppointmentCalendar').on('change.AppointmentCalendar', function() {
  1151. TargetNS.RecurringInit(Fields);
  1152. });
  1153. }
  1154. Fields.$RecurrenceType.off('change.AppointmentCalendar').on('change.AppointmentCalendar', function() {
  1155. RecInit(Fields);
  1156. }).trigger('change.AppointmentCalendar');
  1157. }
  1158. /**
  1159. * @name NotificationInit
  1160. * @memberof Core.Agent.AppointmentCalendar
  1161. * @param {Object} Fields - Array with references to reminder fields.
  1162. * @param {jQueryObject} Fields.$Notification - drop down with system notification selection.
  1163. * @param {jQueryObject} Fields.$NotificationCustomStringDiv - custom selection of system notification date
  1164. * @description
  1165. * This method initializes the reminder section behavior.
  1166. */
  1167. TargetNS.NotificationInit = function (Fields) {
  1168. var NotificationCustomStringDiv = Fields.$NotificationCustomStringDiv.attr('id');
  1169. if (Fields.$NotificationTemplate.val() !== 'Custom') {
  1170. // hide the custom fields
  1171. Fields.$NotificationCustomStringDiv.hide();
  1172. }
  1173. else {
  1174. // custom field is needed
  1175. Fields.$NotificationCustomStringDiv.show();
  1176. // initialize modern fields on custom selection
  1177. Core.UI.InputFields.InitSelect($('select.Modernize'));
  1178. }
  1179. // disable enable the different custom fields
  1180. if (Fields.$NotificationCustomRelativeInput.prop('checked')) {
  1181. // enable relative date fields
  1182. Fields.$NotificationCustomRelativeInput.val(1);
  1183. Fields.$NotificationCustomRelativeUnitCount.prop('disabled', false).prop('readonly', false);
  1184. Fields.$NotificationCustomRelativeUnit.prop('disabled', false);
  1185. Fields.$NotificationCustomRelativePointOfTime.prop('disabled', false);
  1186. // disable the custom date time fields
  1187. Fields.$NotificationCustomDateTimeInput.val('');
  1188. Fields.$NotificationCustomDateTimeDay.prop('disabled', true);
  1189. Fields.$NotificationCustomDateTimeMonth.prop('disabled', true);
  1190. Fields.$NotificationCustomDateTimeYear.prop('disabled', true);
  1191. Fields.$NotificationCustomDateTimeHour.prop('disabled', true);
  1192. Fields.$NotificationCustomDateTimeMinute.prop('disabled', true);
  1193. }
  1194. else {
  1195. // enable the custom date time input
  1196. Fields.$NotificationCustomDateTimeInput.val(1);
  1197. Fields.$NotificationCustomDateTimeInput.prop('checked', true);
  1198. Fields.$NotificationCustomDateTimeDay.prop('disabled', false);
  1199. Fields.$NotificationCustomDateTimeMonth.prop('disabled', false);
  1200. Fields.$NotificationCustomDateTimeYear.prop('disabled', false);
  1201. Fields.$NotificationCustomDateTimeHour.prop('disabled', false);
  1202. Fields.$NotificationCustomDateTimeMinute.prop('disabled', false);
  1203. // disable relative date fields
  1204. Fields.$NotificationCustomRelativeInput.val('');
  1205. Fields.$NotificationCustomRelativeInput.prop('checked', false);
  1206. Fields.$NotificationCustomRelativeUnitCount.prop('disabled', true).prop('readonly', true);
  1207. Fields.$NotificationCustomRelativeUnit.prop('disabled', true);
  1208. Fields.$NotificationCustomRelativePointOfTime.prop('disabled', true);
  1209. }
  1210. // TODO: Workaround for InputFields bug in the framework (disabled attribute not checked after initialization)
  1211. Core.UI.InputFields.Deactivate('#' + Core.App.EscapeSelector(NotificationCustomStringDiv));
  1212. Core.UI.InputFields.Activate('#' + Core.App.EscapeSelector(NotificationCustomStringDiv));
  1213. // Register change event handler
  1214. Fields.$NotificationTemplate.off('change.AppointmentCalendar').on('change.AppointmentCalendar', function() {
  1215. TargetNS.NotificationInit(Fields);
  1216. });
  1217. Fields.$NotificationCustomRelativeInput.off('change.AppointmentCalendar').on('change.AppointmentCalendar', function() {
  1218. // handle radio buttons
  1219. Fields.$NotificationCustomRelativeInput.prop('checked', true);
  1220. Fields.$NotificationCustomDateTimeInput.prop('checked', false);
  1221. // process changes
  1222. TargetNS.NotificationInit(Fields);
  1223. });
  1224. Fields.$NotificationCustomDateTimeInput.off('change.AppointmentCalendar').on('change.AppointmentCalendar', function() {
  1225. // handle radio buttons
  1226. Fields.$NotificationCustomDateTimeInput.prop('checked', true);
  1227. Fields.$NotificationCustomRelativeInput.prop('checked', false);
  1228. // process changes
  1229. TargetNS.NotificationInit(Fields);
  1230. });
  1231. }
  1232. /**
  1233. * @name TeamInit
  1234. * @memberof Core.Agent.AppointmentCalendar
  1235. * @param {jQueryObject} $TeamIDObj - field with list of teams.
  1236. * @param {jQueryObject} $ResourceIDObj - field with list of resources.
  1237. * @param {jQueryObject} $TeamValueObj - field with read only values for team.
  1238. * @param {jQueryObject} $ResourceValueObj - field with read only values for resources.
  1239. * @description
  1240. * This method initializes team fields behavior.
  1241. */
  1242. TargetNS.TeamInit = function ($TeamIDObj, $ResourceIDObj, $TeamValueObj, $ResourceValueObj) {
  1243. $TeamIDObj.off('change.AppointmentCalendar').on('change.AppointmentCalendar', function () {
  1244. var Data = {
  1245. ChallengeToken: $('#ChallengeToken').val(),
  1246. Action: 'AgentAppointmentEdit',
  1247. Subaction: 'TeamUserList',
  1248. TeamID: $TeamIDObj.val()
  1249. };
  1250. ToggleAJAXLoader($ResourceIDObj, true);
  1251. Core.AJAX.FunctionCall(
  1252. Core.Config.Get('CGIHandle'),
  1253. Data,
  1254. function (Response) {
  1255. var SelectedID = $ResourceIDObj.val();
  1256. if (Response.TeamUserList) {
  1257. $ResourceIDObj.empty();
  1258. $.each(Response.TeamUserList, function (Index, Value) {
  1259. var NewOption = new Option(Value, Index);
  1260. // Overwrite option text, because of wrong html quoting of text content.
  1261. // (This is needed for IE.)
  1262. NewOption.innerHTML = Core.App.EscapeHTML(Value);
  1263. // Restore selection
  1264. if (SelectedID && SelectedID.indexOf(Index) > -1) {
  1265. NewOption.selected = true;
  1266. }
  1267. $ResourceIDObj.append(NewOption);
  1268. });
  1269. // Trigger custom redraw event for InputFields
  1270. $ResourceIDObj.trigger('redraw.InputField');
  1271. }
  1272. ToggleAJAXLoader($ResourceIDObj, false);
  1273. }
  1274. );
  1275. });
  1276. function CollapseValues($ValueObj, Values) {
  1277. $ValueObj.html('');
  1278. $.each(Values, function (Index, Value) {
  1279. var Count = Values.length,
  1280. TooltipIndex = $('.Dialog').css('z-index');
  1281. if (Index < 2) {
  1282. $ValueObj.html($ValueObj.html() + Value + '<br>');
  1283. } else {
  1284. Values.splice(-Count, 2);
  1285. $('<a />').attr('href', '#')
  1286. .addClass('DialogTooltipLink')
  1287. .text(Core.Language.Translate('+%s more', Count-2))
  1288. .off('click.AppointmentCalendar')
  1289. .on('click.AppointmentCalendar', function (Event) {
  1290. var $TooltipObj,
  1291. PosX = 0,
  1292. PosY = 0,
  1293. TooltipHTML = '<p>' + Values.join('<br>') + '</p>',
  1294. DocumentVisibleLeft = $(document).scrollLeft() + $(window).width(),
  1295. DocumentVisibleTop = $(document).scrollTop() + $(window).height(),
  1296. LastXPosition,
  1297. LastYPosition;
  1298. // Close existing tooltips
  1299. $('.DialogTooltip').remove();
  1300. if (Event.pageX || Event.pageY) {
  1301. PosX = Event.pageX;
  1302. PosY = Event.pageY;
  1303. } else if (Event.clientX || Event.clientY) {
  1304. PosX = Event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
  1305. PosY = Event.clientY + document.body.scrollTop + document.documentElement.scrollTop;
  1306. }
  1307. // Increase positions so the tooltip do not overlap with mouse pointer
  1308. PosX += 10;
  1309. PosY += 5;
  1310. if (typeof TooltipIndex !== 'undefined') {
  1311. TooltipIndex = parseInt(TooltipIndex, 10) + 500;
  1312. }
  1313. else{
  1314. TooltipIndex = 4000;
  1315. }
  1316. // Create tooltip object
  1317. $TooltipObj = $('<div/>').addClass('AppointmentTooltip DialogTooltip Hidden')
  1318. .offset({
  1319. top: PosY,
  1320. left: PosX
  1321. })
  1322. .html(TooltipHTML)
  1323. .css('z-index', TooltipIndex)
  1324. .css('width', 'auto')
  1325. .off('click.AppointmentCalendar')
  1326. .on('click.AppointmentCalendar', function (Event) {
  1327. Event.stopPropagation();
  1328. })
  1329. .appendTo('body');
  1330. // Re-calculate top position if needed
  1331. LastYPosition = PosY + $TooltipObj.height();
  1332. if (LastYPosition > DocumentVisibleTop) {
  1333. PosY = PosY - $TooltipObj.height();
  1334. $TooltipObj.css('top', PosY + 'px');
  1335. }
  1336. // Re-calculate left position if needed
  1337. LastXPosition = PosX + $TooltipObj.width();
  1338. if (LastXPosition > DocumentVisibleLeft) {
  1339. PosX = PosX - $TooltipObj.width() - 30;
  1340. $TooltipObj.css('left', PosX + 'px');
  1341. }
  1342. // Show the tooltip
  1343. $TooltipObj.fadeIn('fast');
  1344. // Close tooltip on any outside click
  1345. $(document).off('click.AppointmentCalendar')
  1346. .on('click.AppointmentCalendar', function (Event) {
  1347. $('.Close').on('click', function(){
  1348. $('.DialogTooltip').remove();
  1349. });
  1350. if (!$(Event.target).closest('.DialogTooltipLink').length) {
  1351. $('.DialogTooltip').remove();
  1352. $(document).off('click.AppointmentCalendar');
  1353. }
  1354. });
  1355. })
  1356. .appendTo($ValueObj);
  1357. return false;
  1358. }
  1359. });
  1360. }
  1361. // Collapse read only values
  1362. if ($TeamValueObj.length) {
  1363. CollapseValues($TeamValueObj, $TeamValueObj.html().split('<br>'));
  1364. }
  1365. if ($ResourceValueObj.length) {
  1366. CollapseValues($ResourceValueObj, $ResourceValueObj.html().split('<br>'));
  1367. }
  1368. }
  1369. /**
  1370. * @private
  1371. * @name CalendarSettingsInit
  1372. * @memberof Core.Agent.AppointmentCalendar
  1373. * @description
  1374. * This method initializes calendar settings behavior.
  1375. */
  1376. function CalendarSettingsInit() {
  1377. var $CalendarSettingsObj = $('#CalendarSettingsButton'),
  1378. CalendarSettingsDialogHTML;
  1379. if (!$CalendarSettingsObj.length) {
  1380. return;
  1381. }
  1382. CalendarSettingsDialogHTML = Core.Template.Render('Agent/AppointmentCalendar/CalendarSettingsDialog', {
  1383. 'ShownAppointmentsStrg': Core.Config.Get('ShownAppointmentsStrg')
  1384. });
  1385. // Calendar settings button
  1386. $CalendarSettingsObj.off('click.AppointmentCalendar').on('click.AppointmentCalendar', function (Event) {
  1387. Core.UI.Dialog.ShowContentDialog(CalendarSettingsDialogHTML, Core.Language.Translate('Settings'), '10px', 'Center', true,
  1388. [
  1389. {
  1390. Label: Core.Language.Translate('Save'),
  1391. Class: 'Primary',
  1392. Function: function () {
  1393. var $ShownAppointments = $('#ShownAppointments'),
  1394. Data = {
  1395. ChallengeToken: $('#ChallengeToken').val(),
  1396. Action: 'AgentAppointmentEdit',
  1397. Subaction: 'UpdatePreferences',
  1398. OverviewScreen: Core.Config.Get('OverviewScreen') ? Core.Config.Get('OverviewScreen') : 'CalendarOverview',
  1399. ShownAppointments: $ShownAppointments.val()
  1400. };
  1401. Core.Agent.AppointmentCalendar.ShowWaitingDialog();
  1402. Core.AJAX.FunctionCall(
  1403. Core.Config.Get('CGIHandle'),
  1404. Data,
  1405. function (Response) {
  1406. if (!Response.Success) {
  1407. Core.Debug.Log('Error updating user preferences!');
  1408. }
  1409. window.location.href = Core.Config.Get('Baselink') + 'Action=AgentAppointment' + (Core.Config.Get('OverviewScreen') ? Core.Config.Get('OverviewScreen') : 'CalendarOverview');
  1410. }
  1411. );
  1412. }
  1413. }
  1414. ], true);
  1415. Event.preventDefault();
  1416. Event.stopPropagation();
  1417. return false;
  1418. });
  1419. // Show settings dialog immediately
  1420. if (Core.Config.Get('CalendarSettingsShow')) {
  1421. $CalendarSettingsObj.trigger('click.AppointmentCalendar');
  1422. }
  1423. }
  1424. /**
  1425. * @private
  1426. * @name ResourceSettingsInit
  1427. * @memberof Core.Agent.AppointmentCalendar
  1428. * @description
  1429. * This method initializes resource settings behavior.
  1430. */
  1431. function ResourceSettingsInit() {
  1432. var $ResourceSettingsObj = $('#ResourceSettingsButton'),
  1433. ResourceSettingsDialogHTML;
  1434. if (!$ResourceSettingsObj.length) {
  1435. return;
  1436. }
  1437. ResourceSettingsDialogHTML = Core.Template.Render('Agent/AppointmentCalendar/ResourceSettingsDialog', Core.Config.Get('ShownResourceSettings'));
  1438. // Resource settings button
  1439. $ResourceSettingsObj.off('click.AppointmentCalendar').on('click.AppointmentCalendar', function (Event) {
  1440. Core.UI.Dialog.ShowContentDialog(ResourceSettingsDialogHTML, Core.Language.Translate('Settings'), '10px', 'Center', true,
  1441. [
  1442. {
  1443. Label: Core.Language.Translate('Save'),
  1444. Class: 'Primary',
  1445. Function: function () {
  1446. var $ListContainer = $('.AllocationListContainer').find('.AssignedFields'),
  1447. ShownResources = [],
  1448. Data = {
  1449. ChallengeToken: $('#ChallengeToken').val(),
  1450. Action: 'AgentAppointmentEdit',
  1451. Subaction: 'UpdatePreferences',
  1452. OverviewScreen: Core.Config.Get('OverviewScreen') ? Core.Config.Get('OverviewScreen') : 'ResourceOverview',
  1453. TeamID: $('#Team').val()
  1454. };
  1455. if (isJQueryObject($ListContainer) && $ListContainer.length) {
  1456. $.each($ListContainer.find('li'), function() {
  1457. ShownResources.push($(this).attr('data-fieldname'));
  1458. });
  1459. }
  1460. Data.ShownResources = JSON.stringify(ShownResources);
  1461. Core.Agent.AppointmentCalendar.ShowWaitingDialog();
  1462. Core.AJAX.FunctionCall(
  1463. Core.Config.Get('CGIHandle'),
  1464. Data,
  1465. function (Response) {
  1466. if (!Response.Success) {
  1467. Core.Debug.Log('Error updating user preferences!');
  1468. }
  1469. location.reload();
  1470. }
  1471. );
  1472. }
  1473. }
  1474. ], true);
  1475. Event.preventDefault();
  1476. Event.stopPropagation();
  1477. Core.Agent.TableFilters.SetAllocationList();
  1478. return false;
  1479. });
  1480. // Initialize restore settings button.
  1481. RestoreDefaultSettingsInit();
  1482. }
  1483. /**
  1484. * @private
  1485. * @name ResourceSettingsInit
  1486. * @memberof Core.Agent.AppointmentCalendar
  1487. * @description
  1488. * Adds restore default settings button (trash can) to view and initializes its behavior.
  1489. */
  1490. function RestoreDefaultSettingsInit() {
  1491. var $RestoreSettingsObj;
  1492. // Skip if button is not needed or we already have one.
  1493. if (
  1494. !Core.Config.Get('RestoreDefaultSettings')
  1495. || $('#RestoreDefaultSettings').length > 0
  1496. )
  1497. {
  1498. return;
  1499. }
  1500. // Create the button.
  1501. $RestoreSettingsObj = $('<a />').attr('id', 'RestoreDefaultSettings')
  1502. .attr('href', '#')
  1503. .attr('title', Core.Language.Translate('Restore default settings'))
  1504. .append($('<i />').addClass('fa fa-trash'));
  1505. // Add it to the column header.
  1506. $('tr.fc-super + tr .fc-cell-content').append($RestoreSettingsObj);
  1507. // Register click handler.
  1508. $RestoreSettingsObj.off('click.AppointmentCalendar').on('click.AppointmentCalendar', function (Event) {
  1509. Core.AJAX.FunctionCall(
  1510. Core.Config.Get('CGIHandle'),
  1511. {
  1512. ChallengeToken: $('#ChallengeToken').val(),
  1513. Action: 'AgentAppointmentEdit',
  1514. Subaction: 'UpdatePreferences',
  1515. OverviewScreen: Core.Config.Get('OverviewScreen') ? Core.Config.Get('OverviewScreen') : 'ResourceOverview',
  1516. RestoreDefaultSettings: 'ShownResources',
  1517. TeamID: $('#Team').val()
  1518. },
  1519. function (Response) {
  1520. if (!Response.Success) {
  1521. Core.Debug.Log('Error updating user preferences!');
  1522. }
  1523. location.reload();
  1524. }
  1525. );
  1526. Event.preventDefault();
  1527. Event.stopPropagation();
  1528. return false;
  1529. });
  1530. }
  1531. /**
  1532. * @private
  1533. * @name ToggleAJAXLoader
  1534. * @memberof Core.Agent.AppointmentCalendar
  1535. * @function
  1536. * @param {jQueryObject} $Element - Object reference of the field which is updated
  1537. * @param {Boolean} Show - Show or hide the AJAX loader image
  1538. * @description
  1539. * Shows and hides an ajax loader for every element which is updates via ajax.
  1540. */
  1541. function ToggleAJAXLoader($Element, Show) {
  1542. var $Loader = $('#AJAXLoader' + $Element.attr('id')),
  1543. LoaderHTML = '<span id="AJAXLoader' + $Element.attr('id') + '" class="AJAXLoader"></span>';
  1544. // Element not present
  1545. if (!$Element.length) {
  1546. return;
  1547. }
  1548. // Show or hide the loader
  1549. if (Show) {
  1550. if (!$Loader.length) {
  1551. $Element.after(LoaderHTML);
  1552. } else {
  1553. $Loader.show();
  1554. }
  1555. } else {
  1556. if ($Loader.length) {
  1557. $Loader.hide();
  1558. }
  1559. }
  1560. }
  1561. /**
  1562. * @name PluginInit
  1563. * @memberof Core.Agent.AppointmentCalendar
  1564. * @param {jQueryObject} $PluginFields - fields with different plugin searches.
  1565. * @description
  1566. * This method initializes plugin fields behavior.
  1567. */
  1568. TargetNS.PluginInit = function ($PluginFields) {
  1569. function InitRemoveButtons() {
  1570. $('.RemoveButton').off('click.AppointmentCalendar').on('click.AppointmentCalendar', function () {
  1571. var $RemoveObj = $(this),
  1572. PluginKey = $RemoveObj.data('pluginKey'),
  1573. $PluginDataObj = $('#Plugin_' + Core.App.EscapeSelector(PluginKey)),
  1574. PluginData = JSON.parse($PluginDataObj.val()),
  1575. LinkID = $RemoveObj.data('linkId').toString(),
  1576. $Parent = $RemoveObj.parent();
  1577. PluginData.splice(PluginData.indexOf(LinkID), 1);
  1578. $PluginDataObj.val(JSON.stringify(PluginData));
  1579. $Parent.remove();
  1580. return false;
  1581. });
  1582. }
  1583. function AddLink(PluginKey, PluginURL, LinkID, LinkName) {
  1584. var $PluginContainerObj = $('#PluginContainer_' + Core.App.EscapeSelector(PluginKey)),
  1585. $PluginDataObj = $('#Plugin_' + Core.App.EscapeSelector(PluginKey)),
  1586. PluginData = JSON.parse($PluginDataObj.val()),
  1587. $ExistingLinks = $PluginContainerObj.find('.Link_' + Core.App.EscapeSelector(LinkID)),
  1588. $LinkContainerObj = $('<div />'),
  1589. $URLObj = $('<a />'),
  1590. $RemoveObj = $('<a />'),
  1591. LinkURL = PluginURL.replace('%s', LinkID);
  1592. if ($ExistingLinks.length > 0) {
  1593. return;
  1594. }
  1595. PluginData.push(LinkID);
  1596. $PluginDataObj.val(JSON.stringify(PluginData));
  1597. $LinkContainerObj.addClass('Link_' + Core.App.EscapeSelector(LinkID));
  1598. $URLObj.attr('href', LinkURL)
  1599. .attr('target', '_blank')
  1600. .text(LinkName)
  1601. .appendTo($LinkContainerObj);
  1602. $RemoveObj.attr('href', '#')
  1603. .addClass('RemoveButton')
  1604. .data('pluginKey', PluginKey)
  1605. .data('linkId', LinkID)
  1606. .append(
  1607. $('<i />').addClass('fa fa-minus-square-o')
  1608. )
  1609. .appendTo($LinkContainerObj);
  1610. $LinkContainerObj.appendTo($PluginContainerObj);
  1611. InitRemoveButtons();
  1612. }
  1613. $PluginFields.each(function () {
  1614. var $Element = $(this),
  1615. PluginKey = $Element.data('pluginKey'),
  1616. PluginURL = $Element.data('pluginUrl');
  1617. // Skip already initialized fields
  1618. if ($Element.hasClass('ui-autocomplete-input')) {
  1619. return true;
  1620. }
  1621. Core.UI.Autocomplete.Init($Element, function (Request, Response) {
  1622. var URL = Core.Config.Get('CGIHandle'),
  1623. CurrentAJAXNumber = ++AJAXCounter,
  1624. Data = {
  1625. ChallengeToken: $('#ChallengeToken').val(),
  1626. Action: 'AgentAppointmentPluginSearch',
  1627. PluginKey: PluginKey,
  1628. Term: Request.term + '*',
  1629. MaxResults: 20
  1630. };
  1631. $Element.data('AutoCompleteXHR', Core.AJAX.FunctionCall(URL, Data, function (Result) {
  1632. var Data = [];
  1633. // Check if the result is from the latest ajax request
  1634. if (AJAXCounter !== CurrentAJAXNumber) {
  1635. return false;
  1636. }
  1637. $.each(Result, function () {
  1638. Data.push({
  1639. label: this.Value,
  1640. key: this.Key,
  1641. value: this.Value
  1642. });
  1643. });
  1644. Response(Data);
  1645. }));
  1646. }, function (Event, UI) {
  1647. Event.stopPropagation();
  1648. $Element.val('').trigger('select.Autocomplete');
  1649. AddLink(PluginKey, PluginURL, UI.item.key, UI.item.label);
  1650. return false;
  1651. }, PluginKey);
  1652. });
  1653. InitRemoveButtons();
  1654. }
  1655. /**
  1656. * @name EditAppointment
  1657. * @param {Object} Data - Hash with call and appointment data.
  1658. * @param {Integer} Data.AppointmentID - Appointment ID.
  1659. * @param {Integer} Data.Action - Edit action.
  1660. * @param {Integer} Data.Subaction - Edit subaction.
  1661. * @param {Integer} Data.Title - Appointment title.
  1662. * @param {Integer} Data.Description - Appointment description.
  1663. * @param {Integer} Data.Location - Appointment location.
  1664. * @param {Integer} Data.StartYear - Appointment start year.
  1665. * @param {Integer} Data.StartMonth - Appointment start month.
  1666. * @param {Integer} Data.StartDay - Appointment start day.
  1667. * @param {Integer} Data.StartHour - Appointment start hour.
  1668. * @param {Integer} Data.StartMinute - Appointment start minute.
  1669. * @param {Integer} Data.EndYear - Appointment end year.
  1670. * @param {Integer} Data.EndMonth - Appointment end month.
  1671. * @param {Integer} Data.EndDay - Appointment end day.
  1672. * @param {Integer} Data.EndHour - Appointment end hour.
  1673. * @param {Integer} Data.EndMinute - Appointment end minute.
  1674. * @param {Integer} Data.AllDay - Is appointment an all-day appointment (0|1)?
  1675. * @description
  1676. * This method submits an edit appointment call to the backend and refreshes the view.
  1677. */
  1678. TargetNS.EditAppointment = function (Data) {
  1679. if (Core.Config.Get('Action') === 'AgentAppointmentAgendaOverview') {
  1680. $('.OverviewControl').addClass('Loading');
  1681. }
  1682. Core.AJAX.FunctionCall(
  1683. Core.Config.Get('CGIHandle'),
  1684. Data,
  1685. function (Response) {
  1686. if (Response.Success) {
  1687. $('#calendar').fullCalendar('refetchEvents');
  1688. // Close the dialog
  1689. Core.UI.Dialog.CloseDialog($('.Dialog:visible'));
  1690. // Reload page if necessary
  1691. if (Core.Config.Get('Action') === 'AgentAppointmentAgendaOverview') {
  1692. window.location.reload();
  1693. }
  1694. }
  1695. else {
  1696. if (Response.Error) {
  1697. alert(Response.Error);
  1698. }
  1699. }
  1700. }
  1701. );
  1702. };
  1703. /**
  1704. * @name AgentAppointmentAgendaOverview
  1705. * @memberof Core.Agent.AppointmentCalendar
  1706. * @description
  1707. * This method initializes functionalities for AgentAppointmentAgentOverview screen.
  1708. */
  1709. TargetNS.AgentAppointmentAgendaOverview = function () {
  1710. var Config = Core.Config.Get('AppointmentAgendaOverview');
  1711. $('#AppointmentCreateButton').off('click').on('click', function () {
  1712. Core.Agent.AppointmentCalendar.OpenEditDialog({
  1713. Start: $.fullCalendar.moment(Config.Start).add(1, 'hours').startOf('hour'),
  1714. End: $.fullCalendar.moment(Config.Start).add(2, 'hours').startOf('hour')
  1715. });
  1716. return false;
  1717. });
  1718. $('.MasterAction').bind('click', function () {
  1719. var $MasterActionLink = $(this).find('.MasterActionLink'),
  1720. AppointmentID = $MasterActionLink.data('appointmentId');
  1721. Core.Agent.AppointmentCalendar.OpenEditDialog({
  1722. CalEvent: {
  1723. id: AppointmentID
  1724. }
  1725. });
  1726. return false;
  1727. });
  1728. }
  1729. /**
  1730. * @name AgentAppointmentAgendaOverview
  1731. * @memberof Core.Agent.AppointmentCalendar
  1732. * @description
  1733. * This method initializes functionalities for AgentAppointmentEdit screen.
  1734. */
  1735. TargetNS.AgentAppointmentEdit = function () {
  1736. Core.Form.Validate.Init();
  1737. TargetNS.LocationLinkInit($('#Location'), $('.LocationLink'));
  1738. TargetNS.AllDayInit($('#AllDay'));
  1739. TargetNS.RecurringInit({
  1740. $Recurring: $('#Recurring'),
  1741. $RecurrenceType: $('#RecurrenceType'),
  1742. $RecurrenceCustomType: $('#RecurrenceCustomType'),
  1743. $RecurrenceCustomTypeStringDiv: $('#RecurrenceCustomTypeStringDiv'),
  1744. $RecurrenceIntervalText: $('#RecurrenceIntervalText'),
  1745. $RecurrenceCustomWeeklyDiv: $('#RecurrenceCustomWeeklyDiv'),
  1746. $RecurrenceCustomMonthlyDiv: $('#RecurrenceCustomMonthlyDiv'),
  1747. $RecurrenceCustomYearlyDiv: $('#RecurrenceCustomYearlyDiv'),
  1748. $RecurrenceLimitDiv: $('#RecurrenceLimitDiv'),
  1749. $RecurrenceLimit: $('#RecurrenceLimit'),
  1750. $RecurrenceUntilDiv: $('#RecurrenceUntilDiv'),
  1751. $RecurrenceUntilDay: $('#RecurrenceUntilDay'),
  1752. $RecurrenceCountDiv: $('#RecurrenceCountDiv')
  1753. });
  1754. TargetNS.NotificationInit({
  1755. $NotificationTemplate: $('#NotificationTemplate'),
  1756. $NotificationCustomStringDiv: $('#NotificationCustomStringDiv'),
  1757. $NotificationCustomRelativeInput: $('#NotificationCustomRelativeInput'),
  1758. $NotificationCustomRelativeUnitCount: $('#NotificationCustomRelativeUnitCount'),
  1759. $NotificationCustomRelativeUnit: $('#NotificationCustomRelativeUnit'),
  1760. $NotificationCustomRelativePointOfTime: $('#NotificationCustomRelativePointOfTime'),
  1761. $NotificationCustomDateTimeInput: $('#NotificationCustomDateTimeInput'),
  1762. $NotificationCustomDateTimeDay: $('#NotificationCustomDateTimeDay'),
  1763. $NotificationCustomDateTimeMonth: $('#NotificationCustomDateTimeMonth'),
  1764. $NotificationCustomDateTimeYear: $('#NotificationCustomDateTimeYear'),
  1765. $NotificationCustomDateTimeHour: $('#NotificationCustomDateTimeHour'),
  1766. $NotificationCustomDateTimeMinute: $('#NotificationCustomDateTimeMinute')
  1767. });
  1768. TargetNS.TeamInit($('#TeamID'), $('#ResourceID'), $('label[for="TeamID"] + div.Field p.ReadOnlyValue'), $('label[for="ResourceID"] + div.Field p.ReadOnlyValue'));
  1769. if (Core.Config.Get('CalendarPermissionLevel') > 1) {
  1770. TargetNS.PluginInit($('.PluginField'), $('#ChallengeToken').val());
  1771. }
  1772. Core.Form.Validate.SetSubmitFunction($('#EditAppointmentForm'), function (Form) {
  1773. var Data = new Object(),
  1774. Days = new Array(),
  1775. MonthDays = new Array(),
  1776. Months = new Array();
  1777. // serialize days
  1778. $("#RecurrenceCustomWeeklyDiv button.fc-state-active").each(function(){
  1779. Days.push($(this).val());
  1780. });
  1781. $("input#Days").val(Days.join());
  1782. // serialize month days
  1783. $("#RecurrenceCustomMonthlyDiv button.fc-state-active").each(function(){
  1784. MonthDays.push($(this).val());
  1785. });
  1786. $("input#MonthDays").val(MonthDays.join());
  1787. // serialize months
  1788. $("#RecurrenceCustomYearlyDiv button.fc-state-active").each(function(){
  1789. Months.push($(this).val());
  1790. });
  1791. $("input#Months").val(Months.join());
  1792. $(Form).find('input,textarea,select').each(function (Index, Element) {
  1793. if (Element.type == 'checkbox') {
  1794. Data[Element.id] = $(Element).prop('checked') ? '1' : '0';
  1795. } else {
  1796. Data[Element.id] = $(Element).val();
  1797. }
  1798. });
  1799. TargetNS.ShowWaitingDialog();
  1800. TargetNS.EditAppointment(Data);
  1801. });
  1802. $(".ButtonTable button").on("click", function() {
  1803. $(this).toggleClass("fc-state-active");
  1804. });
  1805. // Duplicate appointment
  1806. $('#EditFormCopy').on('click', function() {
  1807. // Reset AppointmentID, remove Copy & Delete buttons
  1808. $("#EditAppointmentForm input#AppointmentID").val("");
  1809. $('#EditAppointmentForm').submit();
  1810. });
  1811. // Save the appointment
  1812. $('#EditFormSubmit').on('click', function() {
  1813. $('#EditAppointmentForm').submit();
  1814. });
  1815. // Delete the appointment
  1816. if (Core.Config.Get('EditAppointmentID')) {
  1817. $('#EditFormDelete').on('click', function() {
  1818. if (window.confirm(Core.Language.Translate('Are you sure you want to delete this appointment? This operation cannot be undone.'))) {
  1819. Core.Agent.AppointmentCalendar.ShowWaitingDialog();
  1820. Core.Agent.AppointmentCalendar.EditAppointment({
  1821. AppointmentID: Core.Config.Get('EditAppointmentID'),
  1822. Action: 'AgentAppointmentEdit',
  1823. Subaction: 'DeleteAppointment'
  1824. });
  1825. }
  1826. });
  1827. }
  1828. // Edit parent appointment
  1829. if (Core.Config.Get('EditParentID')) {
  1830. $('#EditParent').on('click', function() {
  1831. var Data = {
  1832. ChallengeToken: $("#ChallengeToken").val(),
  1833. Action: 'AgentAppointmentEdit',
  1834. Subaction: 'EditMask',
  1835. AppointmentID: Core.Config.Get('EditParentID')
  1836. };
  1837. Core.Agent.AppointmentCalendar.ShowWaitingDialog();
  1838. Core.AJAX.FunctionCall(
  1839. Core.Config.Get('CGIHandle'),
  1840. Data,
  1841. function (HTML) {
  1842. Core.UI.Dialog.ShowContentDialog(HTML, Core.Language.Translate('Appointment'), '10px', 'Center', true, undefined, true);
  1843. Core.UI.InputFields.Activate($('.Dialog:visible'));
  1844. TargetNS.AgentAppointmentEdit();
  1845. }, 'html'
  1846. );
  1847. });
  1848. }
  1849. // Close the dialog
  1850. $('#EditFormCancel').on('click', function() {
  1851. Core.UI.Dialog.CloseDialog($('.Dialog:visible'));
  1852. });
  1853. // Initialize datepicker buttons.
  1854. if (Core.Config.Get('CalendarPermissionLevel') > 2) {
  1855. $.each(['Start', 'End', 'RecurrenceUntil', 'NotificationCustomDateTime'], function (Index, Prefix) {
  1856. Core.UI.Datepicker.Init({
  1857. Day: $('#' + Core.App.EscapeSelector(Prefix) + 'Day'),
  1858. Month: $('#' + Core.App.EscapeSelector(Prefix) + 'Month'),
  1859. Year: $('#' + Core.App.EscapeSelector(Prefix) + 'Year'),
  1860. Hour: $('#' + Core.App.EscapeSelector(Prefix) + 'Hour'),
  1861. Minute: $('#' + Core.App.EscapeSelector(Prefix) + 'Minute'),
  1862. WeekDayStart: Core.Config.Get('CalendarWeekDayStart')
  1863. });
  1864. });
  1865. }
  1866. }
  1867. Core.Init.RegisterNamespace(TargetNS, 'APP_MODULE');
  1868. return TargetNS;
  1869. }(Core.Agent.AppointmentCalendar || {}));

^ Use Elevator