PageSourceSearch

https://secure.yourirvine.org/public/js/catalog/widget.item.registration.dailyPriceDateSelection.js?1790122021

js yourirvine.org collected 2026-09-24 12:02:45 UTC 29,837 bytes, 804 lines download raw bytes

1/**
2 * Widget for Daily Pricing Date Selection
3 * @author Ryan Schingeck
4 * @created 2017-12-18
5 */
6
7(function ($, undefined) {
8    "use strict";
9
10    $.widget("rec1.catalogItemRegistrationDailyPricingDateSelection", {
11
12        options: {
13            selectedValues: null,
14            item: null,
15            requireOne: false,
16        },
17
18        _create: function () {
19            var _this = this;
20            this.$modal = this.element;
21            this.$contents = this.$modal.find(".modal-body");
22            this.components = {};
23            this.registrations = [];
24            this.registrationsByDay = [];
25            this.selectedUserIds = [];
26            this.selectedUsers = {};
27            this.scheduleAvailability = {};
28            this.participantCounts = {};
29            this.dailyPriceRates = [];
30
31            this.element.on("shown.bs.modal", function () {
32                _this.components.calendar.$element.fullCalendar('render');
33                _this.components.calendar.rendered = true;
34            });
35
36        },
37
38        load: function () {
39            var _this = this,
40                $modal = this.$modal;
41            $modal.loadingmask();
42            var promise = this.options.item.dailyPricingDateSelection(null, null, null, this.options.selectedValues);
43            promise
44                .done(function (data) {
45                    _this.setData(data);
46                })
47                .always(function () {
48                    $modal.loadingmask('remove');
49                });
50        },
51
52        show: function () {
53            this.$modal.modal('show');
54        },
55
56        setData: function (data) {
57            this.setUsers(data.users);
58            this.setDefaultSelectedUsers(data.users);
59            this.render(data.html);
60            this.components.usersPanel.refresh();
61            this.refresh();
62        },
63
64        setDefaultSelectedUsers: function (users) {
65            var selectedUserIds = Object.keys(users);
66            for (var i in selectedUserIds) {
67                this.setSelectedUser(selectedUserIds[i], true);
68            }
69        },
70
71        setUsers: function (users) {
72            this.users = users;
73        },
74
75        render: function (html) {
76            this.$contents.html(html);
77            this.initComponents();
78        },
79
80        initComponents: function () {
81            var element = this.element,
82                _this = this,
83                $footer = element.find(".modal-footer");
84
85            this.components = {
86                calendar: {
87                    $element: element.find(".dps-calendar"),
88                    initialized: false,
89                    dirty: true,
90                    initialize: this.initCalendar.bind(this),
91                    refresh: this.refreshCalendar.bind(this),
92                    availabilityDirty: true
93                },
94
95                usersPanel: {
96                    $element: element.find(".dps-users-panel"),
97                    initialized: false,
98                    dirty: false,
99                    initialize: this.initUsersPanel.bind(this),
100                    refresh: this.refreshUsersPanel.bind(this)
101                },
102
103                selectionsPanel: {
104                    $element: element.find(".dps-selections-panel"),
105                    initialized: false,
106                    dirty: true,
107                    initialize: this.initSelectionsPanel.bind(this),
108                    refresh: this.refreshSelectionsPanel.bind(this)
109                },
110
111                controlButtons: {
112                    $element: $footer,
113                    $addToCart: $footer.find(".add-to-cart"),
114                    initialized: false,
115                    dirty: true,
116                    initialize: this.initControlButtons.bind(this),
117                    refresh: this.refreshControlButtons.bind(this)
118                }
119            };
120        },
121
122        initCalendar: function () {
123            this.initEventSources();
124
125            this.components.calendar.$element.fullCalendar({
126                selectable: true,
127                selectHelper: true,
128                eventLimit: true,
129                displayEventEnd: true,
130                select: this._selectedDateRange.bind(this),
131                loading: this._calendarLoading.bind(this),
132                viewRender: this._calendarViewRender.bind(this),
133                eventClick: this._calendarEventClicked.bind(this),
134                eventRender: this._calendarEventRender.bind(this),
135                header: {
136                    left: 'title',
137                    center: '',
138                    right: 'today prev,next'
139                },
140                eventSources: this.calendarEventSources,
141                selectConstraint: null,
142                height: 600,
143                longPressDelay: 0
144            });
145        },
146
147        initEventSources: function () {
148            this.calendarEventSources = [
149                {
150                    name: 'dateSelections',
151                    events: this.getDateSelectionEvents.bind(this),
152                    editable: false,
153                    className: ''
154                }
155            ];
156        },
157
158        _calendarLoading: function () {
159
160        },
161
162        getDateSelectionEvents: function (start, end, timezone, callback) {
163            var registrations = this.registrations,
164                events = [];
165
166            $.each(registrations, function (index, event) {
167                if (event.start > end) {
168                    return true;
169                }
170                events.push(event);
171            });
172
173            callback(events);
174        },
175
176        _selectedDateRange: function (start, end, jsEvent, view) {
177            var selectedUsers = this.selectedUsers,
178                selectedDates = this._createDateListFromRange(start, end),
179                registrations = [],
180                unavailableDateCount = 0;
181
182            if (this.selectedUserIds.length === 0) {
183                alert('No registrants are selected. Click the checkbox next to one or more registrant(s) on the left-hand panel.');
184                return;
185            }
186
187            for (var d in selectedDates) {
188                var eventStart, eventEnd,
189                    selectedDate = selectedDates[d];
190
191
192                eventStart = $.fullCalendar.moment(selectedDate, 'YYYY-MM-DD');
193                eventEnd = $.fullCalendar.moment(selectedDate, 'YYYY-MM-DD');
194
195                if (eventStart > eventEnd) {
196                    eventEnd.add(1, 'day');
197                }
198
199                eventStart.stripZone();
200                eventEnd.stripZone();
201
202
203                if (!this.isAvailableDate(eventStart)) {
204                    unavailableDateCount++;
205                    continue;
206                }
207
208
209                for (var u in selectedUsers) {
210                    var user = selectedUsers[u];
211
212                    var weekDescription = this.getWeekDescription(eventStart);
213
214                    var registration = {
215                        user: user,
216                        date: selectedDate,
217                        allDay: true,
218                        start: eventStart,
219                        end: eventEnd,
220                        title: user.fullName,
221                        weekDescription: weekDescription,
222                        weekKey: eventStart.clone().week() + '-' + user.id,
223                        description: selectedDate,
224                        className: [
225                            'bg-color-' + user.colorCode,
226                            'border-color-' + user.colorCode
227                        ],
228                        registrations: [],
229                        weekdays: [],
230                        warning: null,
231                        error: null
232                    };
233                    registrations.push(registration);
234                }
235            }
236
237            // Hide FullCalendar's selection helper
238            this.components.calendar.$element.fullCalendar('unselect');
239            this.addRegistrations(registrations);
240            this.detectAllRegistrationWarnings();
241            this.components.calendar.dirty = true;
242            this.components.selectionsPanel.dirty = true;
243            this.components.controlButtons.dirty = true;
244            this.refresh();
245
246            if (unavailableDateCount === 1) {
247                alert("There was an unavailable date selected. Only the days highlighted in green/yellow are available.");
248            } else if (unavailableDateCount > 1) {
249                alert("There were " + unavailableDateCount + " unavailable dates selected. Only the days highlighted in green/yellow are available.");
250            }
251        },
252
253        _createDateListFromRange: function (start, end) {
254            var dateList = [];
255            for (var currentDate = moment(start); currentDate.isBefore(end); currentDate.add(1, 'days')) {
256                dateList.push(currentDate.format('YYYY-MM-DD'));
257            }
258            return dateList;
259        },
260
261        isAvailableDate: function (date) {
262            var year = date.year(),
263                month = date.month() + 1,
264                mDate = date.date();
265
266            return this.scheduleAvailability[year]
267                && this.scheduleAvailability[year][month]
268                && this.scheduleAvailability[year][month][mDate];
269
270        },
271
272        getParticipantCountForDate: function (date) {
273            var dateFormat = date.format("MM/DD/YYYY");
274            if (this.participantCounts.hasOwnProperty(dateFormat)) {
275                return this.participantCounts[dateFormat];
276            }
277            return null;
278        },
279
280        getWeekDescription: function (momentInstance) {
281            var firstDay = momentInstance.clone(),
282                lastDay = momentInstance.clone();
283            return 'Week of ' + firstDay.day(0).format("M/D") + ' - '
284                + lastDay.day(6).format("M/D");
285        },
286
287        addRegistrations: function (registrations) {
288            for (var i = 0; i < registrations.length; i++) {
289                this._addRegistration(registrations[i]);
290            }
291        },
292
293        _calendarEventClicked: function () {
294
295        },
296
297        _findRegistrationByDateAndUser: function (date, userId) {
298            var registrations = this.registrations,
299                registrationFound = null;
300
301            $.each(registrations, function (index, registration) {
302                if (registration.date === date && registration.user.id === userId) {
303                    registrationFound = registration;
304                    return true;
305                }
306            });
307
308            return registrationFound;
309        },
310
311        _addRegistration: function (registration) {
312
313
314            // already found for date, do not add again
315            if (this._findRegistrationByDateAndUser(registration.date, registration.user.id)) {
316                return;
317            }
318
319            this.registrations.push(registration);
320
321            var value = {
322                description: moment(registration.date).format("MM/DD/YYYY"),
323                date: registration.date,
324                rate: this._getRateByDate(moment(registration.date)),
325                registrationsByUser: {}
326            };
327            value.registrationsByUser[registration.user.id] = registration;
328            if (value.rate !== null) {
329                value.description += " - " + value.rate.title;
330            }
331
332            var index = -1;
333            $.each(this.registrationsByDay, function (i, registrationByDay) {
334                if (registrationByDay.date === registration.date) {
335                    index = i;
336                    return;
337                }
338            });
339
340            if (index === -1) {
341                this.registrationsByDay.push(value);
342            } else {
343                var day = this.registrationsByDay[index];
344                // User is already in the date
345                if (!day.registrationsByUser[registration.user.id]) {
346                    day.registrationsByUser[registration.user.id] = registration;
347                }
348            }
349        },
350
351        getDaysDescription: function (days) {
352            var dayDescriptions = [];
353            days.sort();
354            for (var i in days) {
355                dayDescriptions.push(moment.weekdaysMin(days[i]));
356            }
357            return dayDescriptions.join('/');
358        },
359
360
361        detectAllRegistrationWarnings: function () {
362            // Detect if restrictions apply on the # of days selected
363            var _this = this,
364                hasWarnings = false;
365            this._iterateRegistrationsByUser(function (day, userId, registration) {
366                var thisHasWarnings = _this.detectRegistrationWarning(registration);
367                hasWarnings = hasWarnings || thisHasWarnings;
368            });
369            this.hasWarnings = hasWarnings;
370        },
371
372        detectRegistrationWarning: function (registration) {
373            //  rate = this._getRate(dayCount),
374            //  validRate = rate || this._getValidRate(dayCount);
375
376            // No rate
377            // if(!validRate) {
378            //   registration.warning = this._getWarningForInvalidDayCount(registration, dayCount);
379            //}
380            // else if(this._registrationHasRestrictedWeekdays(registration, validRate)) {
381            //    registration.warning = this._getWarningForRestrictedWeekdays(registration, validRate);
382            //}
383            // else {
384            //    registration.warning = null;
385            // }
386
387            return !!registration.warning; // bool
388        },
389
390        _iterateRegistrationsByUser: function (callback) {
391            $.each(this.registrationsByDay, function (dayIndex, day) {
392                $.each(day.registrationsByUser, function (userId, registration) {
393                    callback.apply(this, [day, userId, registration]);
394                });
395            });
396        },
397
398        _calendarViewRender: function () {
399            // Render cached availability
400            this._renderCalendarAvailability();
401            this._renderCalendarParticipantCounts();
402            this.refreshCalendarAvailability();
403        },
404
405        _calendarEventRender: function (event, element, view) {
406            var _this = this,
407                $close = $('<div class="dps-remove-registration anchor-right-xs"><i class="flaticon-close"></i></div>'),
408                $content = element.children('.fc-content');
409            $close.on('click', function (e) {
410                _this.removeRegistration(event);
411                _this.detectAllRegistrationWarnings();
412                _this.components.calendar.dirty = true;
413                _this.components.selectionsPanel.dirty = true;
414                _this.components.controlButtons.dirty = true;
415                _this.refresh();
416            });
417            $content.append($close);
418        },
419
420        removeRegistration: function (registration) {
421            if (registration === null) {
422                return;
423            }
424            var _this = this;
425
426            var reservationToRemoveIndex = -1;
427            $.each(this.registrations, function (index, saved) {
428
429                if (saved.date === registration.date
430                    && saved.user.id === registration.user.id) {
431                    _this.registrations.splice(index, 1);
432                    return false;
433                }
434            });
435
436            $.each(this.registrationsByDay, function (index, registrationDay) {
437                if (registrationDay.date === registration.date
438                    && registrationDay.registrationsByUser.hasOwnProperty(registration.user.id)) {
439                    delete _this.registrationsByDay[index].registrationsByUser[registration.user.id];
440
441                    if (Object.keys(registrationDay.registrationsByUser).length === 0) {
442                        _this.registrationsByDay.splice(index, 1);
443                    }
444                    return false
445                }
446            });
447
448        },
449
450        initUsersPanel: function () {
451            var _this = this,
452                component = this.components.usersPanel,
453                $element = component.$element;
454
455            $element.on("change", "input", function (event) {
456                _this.setSelectedUser(
457                    this.value,
458                    this.checked
459                );
460            });
461        },
462
463        setSelectedUser: function (userId, selected) {
464            if (selected) {
465                this.selectedUserIds.push(userId);
466                this.selectedUsers[userId] = this.users[userId];
467            } else {
468                this.selectedUserIds.splice(
469                    this.selectedUserIds.indexOf(userId),
470                    1
471                );
472                delete this.selectedUsers[userId];
473            }
474        },
475
476        initSelectionsPanel: function () {
477            var component = this.components.selectionsPanel,
478                $element = component.$element;
479
480            this._on($element, {
481                "click .dps-remove-registration": function (event) {
482                    var $target = $(event.currentTarget),
483                        $li = $target.closest('li'),
484                        userId = $li.data('user'),
485                        date = $li.data('date');
486
487                    this.removeRegistration(this._findRegistrationByDateAndUser(date, userId.toString()));
488                    this.detectAllRegistrationWarnings();
489                    this.components.selectionsPanel.dirty = true;
490                    this.components.calendar.dirty = true;
491                    this.components.controlButtons.dirty = true;
492                    this.refresh();
493                }
494            });
495        },
496
497        initControlButtons: function () {
498            this._on(this.components.controlButtons.$addToCart, {
499                "click": "addToCart"
500            });
501        },
502
503        addToCart: function (event) {
504            var _this = this,
505                $modal = this.$modal,
506                $contents = this.$contents,
507                users = {};
508            for (var i in this.users) {
509                users[i] = {
510                    dates: [],
511                    override: false
512                };
513            }
514
515            $.each(this.registrations, function (index, registration) {
516                users[registration.user.id].dates.push(registration.start.format('M/D/YYYY'));
517                users[registration.user.id].override = $.inArray(registration.user.id.toString(), _this.options.selectedValues.override) !== -1;
518            });
519
520            var userDateMapping = {
521                userDateMapping: users
522            };
523
524
525            $contents.loadingmask();
526            if (!_this.options.requireOne) {
527                this.options.item.registerDailyPriceDateSelections(userDateMapping).done(function (data) {
528                    $modal.modal('hide');
529
530
531                    if (data.warnings && data.warnings.length > 0 && data.newItemCount > 0) {
532                        alert('Only ' + data.newItemCount + ' ' + (data.newItemCount == 1 ? 'item was' : 'items were') +
533                            ' added to your cart.\n\nThe following warning(s) occurred:\n - ' +
534                            data.warnings.join('\n - '));
535                    }
536
537                }).always(function () {
538                    $contents.loadingmask('remove');
539                });
540            } else {
541                this.options.item.selectSessionPrerequisites(null, null, userDateMapping, _this.options.selectedValues);
542                $modal.modal('hide');
543            }
544        },
545
546        refreshCalendar: function () {
547            if (this.components.calendar.rendered) {
548                this.components.calendar.$element.fullCalendar('refetchEvents');
549                this.components.calendar.availabilityDirty && this.refreshCalendarAvailability();
550            }
551        },
552
553        refreshCalendarAvailability: function () {
554
555            var _this = this,
556                $calendar = this.components.calendar.$element,
557                view = $calendar.fullCalendar('getView'),
558                start = view.start.format('YYYY-MM-DD'),
559                end = view.end.format('YYYY-MM-DD');
560
561            this.components.calendar.availabilityDirty = false;
562
563            var promise = this.options.item.getScheduleAvailability(start, end);
564
565            promise.done(function (data) {
566                _this.setScheduleAvailability(data.availability);
567                _this.setParticipantCounts(data.participantCounts);
568                _this.setDailyPriceRates(data.dailyPriceRates);
569                _this._renderCalendarAvailability();
570                _this._renderCalendarParticipantCounts();
571            });
572        },
573
574        setScheduleAvailability: function (availability) {
575            $.extend(true, this.scheduleAvailability, availability);
576        },
577
578        setParticipantCounts: function (participantCounts) {
579            $.extend(true, this.participantCounts, participantCounts);
580        },
581
582        setDailyPriceRates: function (dailyPriceRates) {
583            var _this = this;
584            $.each(dailyPriceRates, function (index, rate) {
585                var found = false;
586                $.each(_this.dailyPriceRates, function (savedIndex, savedRate) {
587                    if (rate.date === savedRate.date) {
588                        found = true;
589                        return;
590                    }
591                });
592                if (!found) {
593                    _this.dailyPriceRates.push(rate);
594                }
595            });
596        },
597
598        _renderCalendarAvailability: function () {
599
600            if (typeof this.scheduleAvailability !== "object") {
601                return;
602            }
603
604            var _this = this,
605                $calendar = this.components.calendar.$element,
606                $days = $calendar.find(".fc-day");
607
608            var availability = this.scheduleAvailability,
609                classNames = "day-partial day-available day-unavailable bg-warning bg-success bg-muted";
610
611            $days.each(function (index) {
612                var $day = $(this),
613                    date = moment($day.data('date')),
614                    month = date.month() + 1,
615                    mDate = date.date(),
616                    year = date.year(),
617                    className = 'day-unavailable bg-muted';
618
619                if (_this.isAvailableDate(date)) {
620                    var thisAvailability = availability[year][month][mDate];
621                    switch (thisAvailability) {
622                        case 1:
623                            className = "day-partial bg-warning";
624                            break;
625                        case 2:
626                            className = "day-available bg-success";
627                            break;
628                    }
629
630                    $day.closest("td").attr("data-container", ".dps-calendar-panel");
631                    var rate = _this._getRateByDate(date);
632                    if (rate !== null) {
633                        if (!$day.find(".day-label").length) {
634                            var $tdCell = $day.closest("td");
635
636                            $tdCell
637                                .attr("title", rate.title)
638                                .attr("tabindex", 0)
639                                .attr("role", "button")
640                                .attr("aria-label", rate.title)
641                                .on('keydown', function (event) {
642                                    if (event.key === "Enter" || event.keyCode === 13) {
643                                        event.preventDefault();
644
645                                        // Create the start and end moment objects for selection
646                                        var cellDate = moment($day.data('date'));
647                                        var endDate = cellDate.clone().add(1, 'days');
648
649                                        // Use the public select method to trigger the selection
650                                        $calendar.fullCalendar('select', cellDate, endDate);
651
652                                        // Focus back on the cell for continued navigation
653                                        setTimeout(function () {
654                                            $tdCell.focus();
655                                        }, 100);
656                                    }
657                                });
658
659                            $day.closest("td").tooltip();
660
661                            var $title = $('<div class="day-label"></div>');
662                            $title.text(rate.title);
663                            $day.attr("title", rate.title).prepend($title);
664                        }
665                    }
666                }
667
668                $day.removeClass(classNames).addClass(className);
669            });
670        },
671
672        _renderCalendarParticipantCounts: function () {
673
674            if (typeof this.participantCounts !== "object") {
675                return;
676            }
677
678            var _this = this,
679                $calendar = this.components.calendar.$element,
680                $days = $calendar.find(".fc-day");
681
682            $days.each(function (index) {
683                var $day = $(this),
684                    date = moment($day.data('date'));
685
686                if (_this.getParticipantCountForDate(date)) {
687                    var count = _this.getParticipantCountForDate(date);
688
689                    var $countLabel = $('<div></div>');
690                    $countLabel[0].className = "dropin-participant-count-label fc-event";
691                    $countLabel.html(count + " Enrolled");
692                    $countLabel.attr("title", $countLabel.text());
693
694                    $day.closest("td.fc-day").append($countLabel);
695                }
696            });
697        },
698
699        _getRateByDate: function (date) {
700            for (var i in this.dailyPriceRates) {
701                if (this.dailyPriceRates[i].date === date.format("YYYY-MM-DD")) {
702                    return this.dailyPriceRates[i];
703                }
704            }
705            return null;
706        },
707
708        refreshUsersPanel: function () {
709            var selectedUsers = this.selectedUserIds;
710
711            this.components.usersPanel.$element.find("input[type='checkbox']").each(function () {
712                if (selectedUsers.indexOf($(this).val()) != -1) {
713                    $(this).prop("checked", "checked");
714                }
715            });
716        },
717
718        refreshSelectionsPanel: function () {
719            // We're going to just re-render the selections panel
720            var _this = this,
721                html = '';
722
723            html = '<ul class="ui-listview"><li class="ui-listview-heading">Selected Dates</li>';
724            if (this.registrationsByDay.length === 0) {
725                html += '<p class="ui-voffset-lg text-center text-muted">No dates selected</p>';
726            }
727
728            var registrations = this.registrationsByDay;
729            // sort by days
730            registrations.sort(function (reg1, reg2) {
731                var reg1Date = moment(reg1.date),
732                    reg2Date = moment(reg2.date);
733
734                if (reg1Date < reg2Date) {
735                    return -1;
736                } else if (reg1Date > reg2Date) {
737                    return 1;
738                }
739                return 0;
740            });
741
742            $.each(registrations, function (index, registrationDay) {
743                html += _this._renderRegistrationGroup(registrationDay.date,
744                    registrationDay.description,
745                    registrationDay.registrationsByUser);
746            });
747            html += '</ul>';
748
749            this.components.selectionsPanel.$element.html(html);
750        },
751
752        refreshControlButtons: function () {
753            var $addToCart = this.components.controlButtons.$addToCart,
754                disabled = this.hasWarnings || this.registrations.length === 0;
755            $addToCart.prop('disabled', disabled);
756        },
757
758        _renderRegistrationGroup: function (date, description, registrations) {
759
760            var _this = this,
761                rate = _this._getRateByDate(moment(date));
762
763            var html = '<li class="heading-muted ui-voffset-lg"><div class="pull-left">' + description + '</div>' +
764                '<div class="pull-right"><span class="badge">' + Object.keys(registrations).length + ' </span></div>' +
765                '</li>';
766
767            $.each(registrations, function (userId, registration) {
768                var warnings = _this._renderRegistrationWarning(registration);
769                html += '<li class="color-' + registration.user.colorCode + '" ' +
770                    'data-user="' + userId + '" data-date="' + date + '"> $' + rate.price + " - " +
771                    registration.user.fullName +
772                    '<div class="pull-right">' +
773                    ' <a href="javascript:void(0);" class="flaticon-close dps-remove-registration ui-offset-sm text-muted"></a>' +
774                    '</div>' +
775                    warnings +
776                    '</li>';
777            });
778            return html;
779        },
780
781        _renderRegistrationWarning: function (registration) {
782            if (registration.warning) {
783                var html = '<div class="text-muted">' +
784                    '<i class="flaticon-warning2 text-warning"></i> ' + registration.warning +
785                    '</div>';
786                return html;
787            }
788            return '';
789        },
790
791        refresh: function () {
792            $.each(this.components, function (name, component) {
793                component.initialized || component.initialize();
794                component.dirty && component.refresh();
795
796                component.initialized = true;
797                component.dirty = false;
798            });
799        }
800
801    });
802
803
804})(jQuery);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.