<commit_before>/*-------------------------------------------------------+
| ICA Event Registration Module                          |
| Copyright (C) 2016 SYSTOPIA                            |
+--------------------------------------------------------+
| This program is released as free software under the    |
| Affero GPL license. You can redistribute it and/or     |
| modify it under the terms of this license which you    |
| can read by viewing the included agpl.txt or online    |
| at www.gnu.org/licenses/agpl.html. Removal of this     |
| copyright header is strictly prohibited without        |
| written permission from the original author(s).        |
+--------------------------------------------------------*/

// Groupmatrix validation
(function($) {
  $(document).ready(function() {
    var partner = 11;
    var partner_prefix = 12;
    var partner_first_name = 13;
    var partner_last_name = 14;
    var partner_badge_name = 15;

    function validateForm() {
      // SELECT select[name='submitted[group_registration_table][*][<partner>]']
      $("select[name^='submitted[group_registration_table][']")
        .filter(function() {return this.name.endsWith("][" + partner + "]");})
        .each(function() {
          var row = parseInt(this.name.substr(36));
          console.log(row);
          var partner = $("select[name='submitted[group_registration_table][" + row + "][" + partner + "]']")
          console.log(partner.val());

          if (parseInt(partner.val())) {
            // partner is ACTIVE: show fields
            $("select[name='submitted[group_registration_table][" + row + "][" + partner_prefix + "]']").parent().parent().show();
            $("input[name='submitted[group_registration_table][" + row + "][" + partner_first_name + "]']").parent().parent().show();
            $("input[name='submitted[group_registration_table][" + row + "][" + partner_last_name + "]']").parent().parent().show();
            $("input[name='submitted[group_registration_table][" + row + "][" + partner_badge_name + "]']").parent().parent().show();

          } else {
            // partner is INACTIVE: hide fields
            $("select[name='submitted[group_registration_table][" + row + "][" + partner_prefix + "]']").parent().parent().hide();
            $("input[name='submitted[group_registration_table][" + row + "][" + partner_first_name + "]']").parent().parent().hide();
            $("input[name='submitted[group_registration_table][" + row + "][" + partner_last_name + "]']").parent().parent().hide();
            $("input[name='submitted[group_registration_table][" + row + "][" + partner_badge_name + "]']").parent().parent().hide();
          }
        });
    }

    // // CATCH select[name='submitted[group_registration_table][*][<partner>]']
    //   $("select[name^='submitted[group_registration_table][']")
    //     .filter(function() {return this.name.endsWith("][" + partner + "]");})
    //     .change(validateForm);

    // catch ALL change events on table for processing
    $("body").change(validateForm);
  });
})(jQuery);

<commit_msg>JS group matrix validation WIP<commit_after>/*-------------------------------------------------------+
| ICA Event Registration Module                          |
| Copyright (C) 2016 SYSTOPIA                            |
+--------------------------------------------------------+
| This program is released as free software under the    |
| Affero GPL license. You can redistribute it and/or     |
| modify it under the terms of this license which you    |
| can read by viewing the included agpl.txt or online    |
| at www.gnu.org/licenses/agpl.html. Removal of this     |
| copyright header is strictly prohibited without        |
| written permission from the original author(s).        |
+--------------------------------------------------------*/

// Groupmatrix validation
(function($) {
  $(document).ready(function() {
    var partner = 11;
    var partner_prefix = 12;
    var partner_first_name = 13;
    var partner_last_name = 14;
    var partner_badge_name = 15;

    function validateForm() {
      // SELECT select[name='submitted[group_registration_table][*][<partner>]']
      $("select[name^='submitted[group_registration_table][']")
        .filter(function() {return this.name.endsWith("][" + partner + "]");})
        .each(function() {
          var row = parseInt(this.name.substr(36));
          console.log(row);
          var partner = $("select[name='submitted[group_registration_table][" + row + "][" + partner + "]']");
          console.log(partner.val());

          if (parseInt(partner.val())) {
            // partner is ACTIVE: show fields
            $("select[name='submitted[group_registration_table][" + row + "][" + partner_prefix + "]']").parent().parent().show();
            $("input[name='submitted[group_registration_table][" + row + "][" + partner_first_name + "]']").parent().parent().show();
            $("input[name='submitted[group_registration_table][" + row + "][" + partner_last_name + "]']").parent().parent().show();
            $("input[name='submitted[group_registration_table][" + row + "][" + partner_badge_name + "]']").parent().parent().show();

          } else {
            // partner is INACTIVE: hide fields
            $("select[name='submitted[group_registration_table][" + row + "][" + partner_prefix + "]']").parent().parent().hide();
            $("input[name='submitted[group_registration_table][" + row + "][" + partner_first_name + "]']").parent().parent().hide();
            $("input[name='submitted[group_registration_table][" + row + "][" + partner_last_name + "]']").parent().parent().hide();
            $("input[name='submitted[group_registration_table][" + row + "][" + partner_badge_name + "]']").parent().parent().hide();
          }
        });
    }

    // // CATCH select[name='submitted[group_registration_table][*][<partner>]']
    //   $("select[name^='submitted[group_registration_table][']")
    //     .filter(function() {return this.name.endsWith("][" + partner + "]");})
    //     .change(validateForm);

    // catch ALL change events on table for processing
    $("body").change(validateForm);
  });
})(jQuery);

<|endoftext|>


<commit_before>/*
 * _____/\\\\\\\\\_____/\\\\\\\\\\\\\\\__/\\\\\_____/\\\__/\\\\\\\\\\\\\_______/\\\\\\\\\\\\_______/\\\\\______
 *  ___/\\\\\\\\\\\\\__\///////\\\/////__\/\\\\\\___\/\\\_\/\\\/////////\\\___/\\\//////////______/\\\///\\\____
 *   __/\\\/////////\\\_______\/\\\_______\/\\\/\\\__\/\\\_\/\\\_______\/\\\__/\\\_______________/\\\/__\///\\\__
 *    _\/\\\_______\/\\\_______\/\\\_______\/\\\//\\\_\/\\\_\/\\\\\\\\\\\\\/__\/\\\____/\\\\\\\__/\\\______\//\\\_
 *     _\/\\\\\\\\\\\\\\\_______\/\\\_______\/\\\\//\\\\/\\\_\/\\\/////////____\/\\\___\/////\\\_\/\\\_______\/\\\_
 *      _\/\\\/////////\\\_______\/\\\_______\/\\\_\//\\\/\\\_\/\\\_____________\/\\\_______\/\\\_\//\\\______/\\\__
 *       _\/\\\_______\/\\\_______\/\\\_______\/\\\__\//\\\\\\_\/\\\_____________\/\\\_______\/\\\__\///\\\__/\\\____
 *        _\/\\\_______\/\\\_______\/\\\_______\/\\\___\//\\\\\_\/\\\_____________\//\\\\\\\\\\\\/_____\///\\\\\/_____
 *         _\///________\///________\///________\///_____\/////__\///_______________\////////////_________\/////_______
 *  © Oproma inc. All rights reserved.
 */

(function ($) {
    $.fn.MobileScheduler = function (options) {
        var settings = $.extend({
            // These are the defaults.
            date: new Date(),
            use24HourClock: true,
            events: [],
            prevClass: 'icon-arrow-left5',
            nextClass: 'icon-arrow-right5',
            onEventClick: function (e, event) {},
            labels: {
                allday: "all-day",
                months: [
                    'January',
                    'February',
                    'March',
                    'April',
                    'May',
                    'June',
                    'July',
                    'August',
                    'September',
                    'October',
                    'November',
                    'December'
                ],
                days: [
                    'Sun',
                    'Mon',
                    'Tue',
                    'Wed',
                    'Thu',
                    'Fri',
                    'Sat'
                ]
            }
        }, options);

        var $this = this;

        var bindCalendar = function (cssClass) {
            var today = new Date();
            var nextMonth = function () {
                $this.find('.jqms-month-view').on('webkitAnimationEnd animationend', function () {
                    bindCalendar('animated slideInRight');
                }).addClass('animated slideOutLeft');
                if (settings.date.getMonth() === 11) {
                    // set january of next year.
                    settings.date.setMonth(0);
                    settings.date.setFullYear(settings.date.getFullYear() + 1);
                } else {
                    settings.date.setMonth(settings.date.getMonth() + 1);
                }
            };
            var prevMonth = function () {
                $this.find('.jqms-month-view').on('webkitAnimationEnd animationend', function () {
                    bindCalendar('animated slideInLeft');
                }).addClass('animated slideOutRight');
                if (settings.date.getMonth() === 0) {
                    // set december of previous year.
                    settings.date.setMonth(11);
                    settings.date.setFullYear(settings.date.getFullYear() - 1);
                } else {
                    settings.date.setMonth(settings.date.getMonth() - 1);
                }
            };
            $this.empty();
            $this.addClass('jqms-calendar');
            var header = $('

' + settings.labels.months[settings.date.getMonth()] + ', ' + settings.date.getFullYear()
                    + '

');
            var days = header.find('.jqms-days');
            _.each(settings.labels.days, function (day) {
                days.append('
' + day + '
');
            });

            header.find('.jqms-previous').click(function (e) {
                e.preventDefault();
                e.stopPropagation();
                prevMonth();
            });
            header.find('.jqms-next').click(function (e) {
                e.preventDefault();
                e.stopPropagation();
                nextMonth();
            });
            header.find('.jqms-month-picker').click(function (e) {
                e.preventDefault();
                e.stopPropagation();
                var month = settings.date.getMonth() % 11 + 1;
                var $picker = $('<input type="month" style="height:0px;width:0px;position:absolute;top:-200px;left:-200px;display:inline" value="' + settings.date.getFullYear() + (month < 10 ? '-0' : '-') + month + '">');
                $this.append($picker);
                $picker.bind("keypress", function (e) {
                    if (e.which === 13) {
                        var parts = $(this).val().split('-');
                        if (parts.length === 2) {
                            var inClass = 'animated ', outClass = 'animated ', rebind = true;
                            var newYear = parseInt(parts[0]);
                            var newMonth = parseInt(parts[1]) - 1;
                            if (newYear < settings.date.getFullYear()) {
                                inClass += 'slideInLeft';
                                outClass += 'slideOutRight';
                            } else if (newYear > settings.date.getFullYear()) {
                                inClass += 'slideInRight';
                                outClass += 'slideOutLeft';
                            } else {
                                if (newMonth < settings.date.getMonth()) {
                                    inClass += 'slideInLeft';
                                    outClass += 'slideOutRight';
                                } else if (newMonth > settings.date.getMonth()) {
                                    inClass += 'slideInRight';
                                    outClass += 'slideOutLeft';
                                } else {
                                    rebind = false;
                                }
                            }
                            if (rebind) {
                                settings.date.setFullYear(newYear);
                                settings.date.setMonth(newMonth);
                                $this.find('.jqms-month-view').on('webkitAnimationEnd animationend', function () {
                                    bindCalendar(inClass);
                                }).addClass(outClass);
                            }
                        }
                        $picker.remove();
                    }
                });
                $picker.focus();
            });

            $this.append(header);

            var content = $('

');
            var monthView = content.find('.jqms-month-view');

            var monthStart = new Date(settings.date.getFullYear(), settings.date.getMonth(), 1);
            var fillerCellCount = 0;
            for (; fillerCellCount < monthStart.getDay(); fillerCellCount++) {
                monthView.append('

');
            }

            var monthEvents = _.filter(settings.events, function (event) {
                if (event && event.start && settings && settings.date) {
                    if (event.allday) {
                        return event.start.getUTCFullYear() === settings.date.getFullYear() && event.start.getUTCMonth() === settings.date.getMonth();
                    } else {
                        return event.start.getFullYear() === settings.date.getFullYear() && event.start.getMonth() === settings.date.getMonth();
                    }
                }
                return false;
            });

            var daysInMonth = new Date(settings.date.getFullYear(), settings.date.getMonth() + 1, 0).getDate();

            // add day cells.
            for (var i = 1; i <= daysInMonth; i++) {
                var cell = $('

');
                cell.append('' + i + '');
                cell.data('day', i);
                cell.click(function () {
                    settings.date.setDate($(this).data('day'));
                    bindCalendar();
                });

                if (today.getFullYear() === settings.date.getFullYear() && today.getMonth() === settings.date.getMonth() && today.getDate() === i) {
                    cell.addClass('jqms-today');
                }

                if (settings.date.getDate() === i) {
                    cell.addClass('jqms-active');
                }

                if (_.filter(monthEvents, function (event) {
                    return event && event.start && ((!event.allday && event.start.getDate() === i) || (event.allday && event.start.getUTCDate() === i));
                }).length > 0) {
                    cell.append('●');
                }
                monthView.append(cell);
            }

            // Fix layouts for months that are either less or more than 5 "weeks"/rows.
            if ((daysInMonth + fillerCellCount) > 35) {
                monthView.addClass('jqms-long');
            } else if ((daysInMonth + fillerCellCount) === 28) {
                monthView.addClass('jqms-short');
            }

            monthView.on('webkitAnimationEnd animationend', function () {
                monthView.removeClass('animated slideInRight slideInLeft');
            });
            monthView.click(function (e) {
                e.preventDefault();
                e.stopPropagation();
            });

            // bind day list.
            var dayEvents = _.filter(monthEvents, function (event) {
                return event && event.start && ((!event.allday && event.start.getDate() === settings.date.getDate()) || (event.allday && event.start.getUTCDate() === settings.date.getDate()));
            });

            var listItems = content.find('.jqms-list-items');
            _.each(_.sortBy(dayEvents, function (event) {
                return event.start.getTime();
            }), function (event) {
                var startTime = settings.labels.allday;
                var endTime = settings.labels.allday;
                if (!event.allday) {
                    if (settings.use24HourClock) {
                        startTime = (event.start.getHours() < 10 ? '0' : '') + event.start.getHours() + ':' + (event.start.getMinutes() < 10 ? '0' : '') + event.start.getMinutes();
                        endTime = (event.end.getHours() < 10 ? '0' : '') + event.end.getHours() + ':' + (event.end.getMinutes() < 10 ? '0' : '') + event.end.getMinutes();
                    } else {
                        var starthours = event.start.getHours() % 12;
                        var endhours = event.end.getHours() % 12;
                        if (starthours === 0) {
                            starthours = 12;
                        }
                        if (endhours === 0) {
                            endhours = 12;
                        }
                        startTime = (starthours < 10 ? '0' : '') + starthours + ':' + (event.start.getMinutes() < 10 ? '0' : '') + event.start.getMinutes() + (event.start.getHours() > 12 ? ' PM' : ' AM');
                        endTime = (endhours < 10 ? '0' : '') + endhours + ':' + (event.end.getMinutes() < 10 ? '0' : '') + event.end.getMinutes() + (event.end.getHours() > 12 ? ' PM' : ' AM');
                    }
                }
                var listItem = $('

' + startTime + '' + endTime + '
' + event.title + '
');

                listItem.data('event', JSON.stringify(event));

                listItem.click(function (e) {
                    settings.onEventClick(e, JSON.parse($(this).data('event')));
                });
                listItems.append(listItem);
            });

            $this.append(content);

            if (window.IScroll) {
                var myScroll = new IScroll($this.find('.jqms-month-view-container')[0], {
                    disableMouse: true,
                    disablePointer: true,
                    bindToWrapper: true,
                    scrollX: true,
                    probeType: 3,
                    eventPassthrough: true
                });
                var goToPrev = false, goToNext = false;

                myScroll.on('scroll', function (e) {
                    if (this.x > ($(window).width() * 0.05)) {
                        goToPrev = true;
                    } else if (this.x < -($(window).width() * 0.05)) {
                        goToNext = true;
                    } else {
                        goToPrev = false;
                        goToNext = false;
                    }
                });
                $this.find('.jqms-month-view').on('touchend', function () {
                    if (goToNext) {
                        nextMonth();
                    } else if (goToPrev) {
                        prevMonth();
                    }
                });
            }
        };
        bindCalendar();
        return this;
    };

}(jQuery));<commit_msg>iOS focus fix.<commit_after>/*
 * _____/\\\\\\\\\_____/\\\\\\\\\\\\\\\__/\\\\\_____/\\\__/\\\\\\\\\\\\\_______/\\\\\\\\\\\\_______/\\\\\______
 *  ___/\\\\\\\\\\\\\__\///////\\\/////__\/\\\\\\___\/\\\_\/\\\/////////\\\___/\\\//////////______/\\\///\\\____
 *   __/\\\/////////\\\_______\/\\\_______\/\\\/\\\__\/\\\_\/\\\_______\/\\\__/\\\_______________/\\\/__\///\\\__
 *    _\/\\\_______\/\\\_______\/\\\_______\/\\\//\\\_\/\\\_\/\\\\\\\\\\\\\/__\/\\\____/\\\\\\\__/\\\______\//\\\_
 *     _\/\\\\\\\\\\\\\\\_______\/\\\_______\/\\\\//\\\\/\\\_\/\\\/////////____\/\\\___\/////\\\_\/\\\_______\/\\\_
 *      _\/\\\/////////\\\_______\/\\\_______\/\\\_\//\\\/\\\_\/\\\_____________\/\\\_______\/\\\_\//\\\______/\\\__
 *       _\/\\\_______\/\\\_______\/\\\_______\/\\\__\//\\\\\\_\/\\\_____________\/\\\_______\/\\\__\///\\\__/\\\____
 *        _\/\\\_______\/\\\_______\/\\\_______\/\\\___\//\\\\\_\/\\\_____________\//\\\\\\\\\\\\/_____\///\\\\\/_____
 *         _\///________\///________\///________\///_____\/////__\///_______________\////////////_________\/////_______
 *  © Oproma inc. All rights reserved.
 */

(function ($) {
    $.fn.MobileScheduler = function (options) {
        var settings = $.extend({
            // These are the defaults.
            date: new Date(),
            use24HourClock: true,
            events: [],
            prevClass: 'icon-arrow-left5',
            nextClass: 'icon-arrow-right5',
            onEventClick: function (e, event) {},
            labels: {
                allday: "all-day",
                months: [
                    'January',
                    'February',
                    'March',
                    'April',
                    'May',
                    'June',
                    'July',
                    'August',
                    'September',
                    'October',
                    'November',
                    'December'
                ],
                days: [
                    'Sun',
                    'Mon',
                    'Tue',
                    'Wed',
                    'Thu',
                    'Fri',
                    'Sat'
                ]
            }
        }, options);

        var $this = this;

        var bindCalendar = function (cssClass) {
            var today = new Date();
            var nextMonth = function () {
                $this.find('.jqms-month-view').on('webkitAnimationEnd animationend', function () {
                    bindCalendar('animated slideInRight');
                }).addClass('animated slideOutLeft');
                if (settings.date.getMonth() === 11) {
                    // set january of next year.
                    settings.date.setMonth(0);
                    settings.date.setFullYear(settings.date.getFullYear() + 1);
                } else {
                    settings.date.setMonth(settings.date.getMonth() + 1);
                }
            };
            var prevMonth = function () {
                $this.find('.jqms-month-view').on('webkitAnimationEnd animationend', function () {
                    bindCalendar('animated slideInLeft');
                }).addClass('animated slideOutRight');
                if (settings.date.getMonth() === 0) {
                    // set december of previous year.
                    settings.date.setMonth(11);
                    settings.date.setFullYear(settings.date.getFullYear() - 1);
                } else {
                    settings.date.setMonth(settings.date.getMonth() - 1);
                }
            };
            $this.empty();
            $this.addClass('jqms-calendar');
            var header = $('

' + settings.labels.months[settings.date.getMonth()] + ', ' + settings.date.getFullYear()
                    + '

');
            var days = header.find('.jqms-days');
            _.each(settings.labels.days, function (day) {
                days.append('
' + day + '
');
            });

            header.find('.jqms-previous').click(function (e) {
                e.preventDefault();
                e.stopPropagation();
                prevMonth();
            });
            header.find('.jqms-next').click(function (e) {
                e.preventDefault();
                e.stopPropagation();
                nextMonth();
            });
            header.find('.jqms-month-picker').click(function (e) {
                e.preventDefault();
                e.stopPropagation();
                var month = settings.date.getMonth() % 11 + 1;
                var $picker = $('<input type="month" style="height:0px;width:0px;position:absolute;top:-200px;left:-200px;display:inline" value="' + settings.date.getFullYear() + (month < 10 ? '-0' : '-') + month + '">');
                $this.append($picker);
                $picker.on("change focusout blur", function (e) {

                    var parts = $(this).val().split('-');
                    if (parts.length === 2) {
                        var inClass = 'animated ', outClass = 'animated ', rebind = true;
                        var newYear = parseInt(parts[0]);
                        var newMonth = parseInt(parts[1]) - 1;
                        if (newYear < settings.date.getFullYear()) {
                            inClass += 'slideInLeft';
                            outClass += 'slideOutRight';
                        } else if (newYear > settings.date.getFullYear()) {
                            inClass += 'slideInRight';
                            outClass += 'slideOutLeft';
                        } else {
                            if (newMonth < settings.date.getMonth()) {
                                inClass += 'slideInLeft';
                                outClass += 'slideOutRight';
                            } else if (newMonth > settings.date.getMonth()) {
                                inClass += 'slideInRight';
                                outClass += 'slideOutLeft';
                            } else {
                                rebind = false;
                            }
                        }
                        if (rebind) {
                            settings.date.setFullYear(newYear);
                            settings.date.setMonth(newMonth);
                            $this.find('.jqms-month-view').on('webkitAnimationEnd animationend', function () {
                                bindCalendar(inClass);
                            }).addClass(outClass);
                        }
                    }
                    $picker.remove();
                });
                $picker.focus();
            });

            $this.append(header);

            var content = $('

');
            var monthView = content.find('.jqms-month-view');

            var monthStart = new Date(settings.date.getFullYear(), settings.date.getMonth(), 1);
            var fillerCellCount = 0;
            for (; fillerCellCount < monthStart.getDay(); fillerCellCount++) {
                monthView.append('

');
            }

            var monthEvents = _.filter(settings.events, function (event) {
                if (event && event.start && settings && settings.date) {
                    if (event.allday) {
                        return event.start.getUTCFullYear() === settings.date.getFullYear() && event.start.getUTCMonth() === settings.date.getMonth();
                    } else {
                        return event.start.getFullYear() === settings.date.getFullYear() && event.start.getMonth() === settings.date.getMonth();
                    }
                }
                return false;
            });

            var daysInMonth = new Date(settings.date.getFullYear(), settings.date.getMonth() + 1, 0).getDate();

            // add day cells.
            for (var i = 1; i <= daysInMonth; i++) {
                var cell = $('

');
                cell.append('' + i + '');
                cell.data('day', i);
                cell.click(function () {
                    settings.date.setDate($(this).data('day'));
                    bindCalendar();
                });

                if (today.getFullYear() === settings.date.getFullYear() && today.getMonth() === settings.date.getMonth() && today.getDate() === i) {
                    cell.addClass('jqms-today');
                }

                if (settings.date.getDate() === i) {
                    cell.addClass('jqms-active');
                }

                if (_.filter(monthEvents, function (event) {
                    return event && event.start && ((!event.allday && event.start.getDate() === i) || (event.allday && event.start.getUTCDate() === i));
                }).length > 0) {
                    cell.append('●');
                }
                monthView.append(cell);
            }

            // Fix layouts for months that are either less or more than 5 "weeks"/rows.
            if ((daysInMonth + fillerCellCount) > 35) {
                monthView.addClass('jqms-long');
            } else if ((daysInMonth + fillerCellCount) === 28) {
                monthView.addClass('jqms-short');
            }

            monthView.on('webkitAnimationEnd animationend', function () {
                monthView.removeClass('animated slideInRight slideInLeft');
            });
            monthView.click(function (e) {
                e.preventDefault();
                e.stopPropagation();
            });

            // bind day list.
            var dayEvents = _.filter(monthEvents, function (event) {
                return event && event.start && ((!event.allday && event.start.getDate() === settings.date.getDate()) || (event.allday && event.start.getUTCDate() === settings.date.getDate()));
            });

            var listItems = content.find('.jqms-list-items');
            _.each(_.sortBy(dayEvents, function (event) {
                return event.start.getTime();
            }), function (event) {
                var startTime = settings.labels.allday;
                var endTime = settings.labels.allday;
                if (!event.allday) {
                    if (settings.use24HourClock) {
                        startTime = (event.start.getHours() < 10 ? '0' : '') + event.start.getHours() + ':' + (event.start.getMinutes() < 10 ? '0' : '') + event.start.getMinutes();
                        endTime = (event.end.getHours() < 10 ? '0' : '') + event.end.getHours() + ':' + (event.end.getMinutes() < 10 ? '0' : '') + event.end.getMinutes();
                    } else {
                        var starthours = event.start.getHours() % 12;
                        var endhours = event.end.getHours() % 12;
                        if (starthours === 0) {
                            starthours = 12;
                        }
                        if (endhours === 0) {
                            endhours = 12;
                        }
                        startTime = (starthours < 10 ? '0' : '') + starthours + ':' + (event.start.getMinutes() < 10 ? '0' : '') + event.start.getMinutes() + (event.start.getHours() > 12 ? ' PM' : ' AM');
                        endTime = (endhours < 10 ? '0' : '') + endhours + ':' + (event.end.getMinutes() < 10 ? '0' : '') + event.end.getMinutes() + (event.end.getHours() > 12 ? ' PM' : ' AM');
                    }
                }
                var listItem = $('

' + startTime + '' + endTime + '
' + event.title + '
');

                listItem.data('event', JSON.stringify(event));

                listItem.click(function (e) {
                    settings.onEventClick(e, JSON.parse($(this).data('event')));
                });
                listItems.append(listItem);
            });

            $this.append(content);

            if (window.IScroll) {
                var myScroll = new IScroll($this.find('.jqms-month-view-container')[0], {
                    disableMouse: true,
                    disablePointer: true,
                    bindToWrapper: true,
                    scrollX: true,
                    probeType: 3,
                    eventPassthrough: true
                });
                var goToPrev = false, goToNext = false;

                myScroll.on('scroll', function (e) {
                    if (this.x > ($(window).width() * 0.05)) {
                        goToPrev = true;
                    } else if (this.x < -($(window).width() * 0.05)) {
                        goToNext = true;
                    } else {
                        goToPrev = false;
                        goToNext = false;
                    }
                });
                $this.find('.jqms-month-view').on('touchend', function () {
                    if (goToNext) {
                        nextMonth();
                    } else if (goToPrev) {
                        prevMonth();
                    }
                });
            }
        };
        bindCalendar();
        return this;
    };

}(jQuery));<|endoftext|>


<commit_before>// MIT License:
//
// Copyright (c) 2010-2012, Joe Walnes
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

/**
 * This behaves like a WebSocket in every way, except if it fails to connect,
 * or it gets disconnected, it will repeatedly poll until it successfully connects
 * again.
 *
 * It is API compatible, so when you have:
 *   ws = new WebSocket('ws://....');
 * you can replace with:
 *   ws = new ReconnectingWebSocket('ws://....');
 *
 * The event stream will typically look like:
 *  onconnecting
 *  onopen
 *  onmessage
 *  onmessage
 *  onclose // lost connection
 *  onconnecting
 *  onopen  // sometime later...
 *  onmessage
 *  onmessage
 *  etc...
 *
 * It is API compatible with the standard WebSocket API, apart from the following members:
 *
 * - `bufferedAmount`
 * - `extensions`
 * - `binaryType`
 *
 * Latest version: https://github.com/joewalnes/reconnecting-websocket/
 * - Joe Walnes
 *
 * Syntax
 * ======
 * var socket = new ReconnectingWebSocket(url, protocols, options);
 *
 * Parameters
 * ==========
 * url - The url you are connecting to.
 * protocols - Optional string or array of protocols.
 * options - See below
 *
 * Options
 * =======
 * Options can either be passed upon instantiation or set after instantiation:
 *
 * var socket = new ReconnectingWebSocket(url, null, { debug: true, reconnectInterval: 4000 });
 *
 * or
 *
 * var socket = new ReconnectingWebSocket(url);
 * socket.debug = true;
 * socket.reconnectInterval = 4000;
 *
 * debug
 * - Whether this instance should log debug messages. Accepts true or false. Default: false.
 *
 * automaticOpen
 * - Whether or not the websocket should attempt to connect immediately upon instantiation. The socket can be manually opened or closed at any time using ws.open() and ws.close().
 *
 * reconnectInterval
 * - The number of milliseconds to delay before attempting to reconnect. Accepts integer. Default: 1000.
 *
 * maxReconnectInterval
 * - The maximum number of milliseconds to delay a reconnection attempt. Accepts integer. Default: 30000.
 *
 * reconnectDecay
 * - The rate of increase of the reconnect delay. Allows reconnect attempts to back off when problems persist. Accepts integer or float. Default: 1.5.
 *
 * timeoutInterval
 * - The maximum time in milliseconds to wait for a connection to succeed before closing and retrying. Accepts integer. Default: 2000.
 *
 */
(function (global, factory) {
    if (typeof define === 'function' && define.amd) {
        define([], factory);
    } else if (typeof module !== 'undefined' && module.exports){
        module.exports = factory();
    } else {
        global.ReconnectingWebSocket = factory();
    }
})(this, function () {

    if (!('WebSocket' in window)) {
        return;
    }

    function ReconnectingWebSocket(url, protocols, options) {

        // Default settings
        var settings = {

            /** Whether this instance should log debug messages. */
            debug: false,

            /** Whether or not the websocket should attempt to connect immediately upon instantiation. */
            automaticOpen: true,

            /** The number of milliseconds to delay before attempting to reconnect. */
            reconnectInterval: 1000,
            /** The maximum number of milliseconds to delay a reconnection attempt. */
            maxReconnectInterval: 30000,
            /** The rate of increase of the reconnect delay. Allows reconnect attempts to back off when problems persist. */
            reconnectDecay: 1.5,

            /** The maximum time in milliseconds to wait for a connection to succeed before closing and retrying. */
            timeoutInterval: 2000,

            /** The maximum number of reconnection attempts to make. Unlimited if null. */
            maxReconnectAttempts: null,

            /** The binary type, possible values 'blob' or 'arraybuffer', default 'blob'. */
            binaryType: 'blob'
        }
        if (!options) { options = {}; }

        // Overwrite and define settings with options if they exist.
        for (var key in settings) {
            if (typeof options[key] !== 'undefined') {
                this[key] = options[key];
            } else {
                this[key] = settings[key];
            }
        }

        // These should be treated as read-only properties

        /** The URL as resolved by the constructor. This is always an absolute URL. Read only. */
        this.url = url;

        /** The number of attempted reconnects since starting, or the last successful connection. Read only. */
        this.reconnectAttempts = 0;

        /**
         * The current state of the connection.
         * Can be one of: WebSocket.CONNECTING, WebSocket.OPEN, WebSocket.CLOSING, WebSocket.CLOSED
         * Read only.
         */
        this.readyState = WebSocket.CONNECTING;

        /**
         * A string indicating the name of the sub-protocol the server selected; this will be one of
         * the strings specified in the protocols parameter when creating the WebSocket object.
         * Read only.
         */
        this.protocol = null;

        // Private state variables

        var self = this;
        var ws;
        var forcedClose = false;
        var timedOut = false;
        var eventTarget = document.createElement('div');

        // Wire up "on*" properties as event handlers

        eventTarget.addEventListener('open',       function(event) { self.onopen(event); });
        eventTarget.addEventListener('close',      function(event) { self.onclose(event); });
        eventTarget.addEventListener('connecting', function(event) { self.onconnecting(event); });
        eventTarget.addEventListener('message',    function(event) { self.onmessage(event); });
        eventTarget.addEventListener('error',      function(event) { self.onerror(event); });

        // Expose the API required by EventTarget

        this.addEventListener = eventTarget.addEventListener.bind(eventTarget);
        this.removeEventListener = eventTarget.removeEventListener.bind(eventTarget);
        this.dispatchEvent = eventTarget.dispatchEvent.bind(eventTarget);

        /**
         * This function generates an event that is compatible with standard
         * compliant browsers and IE9 - IE11
         *
         * This will prevent the error:
         * Object doesn't support this action
         *
         * http://stackoverflow.com/questions/19345392/why-arent-my-parameters-getting-passed-through-to-a-dispatched-event/19345563#19345563
         * @param s String The name that the event should use
         * @param args Object an optional object that the event will use
         */
        function generateEvent(s, args) {
        	var evt = document.createEvent("CustomEvent");
        	evt.initCustomEvent(s, false, false, args);
        	return evt;
        };

        this.open = function (reconnectAttempt) {
            if (reconnectAttempt && this.maxReconnectAttempts && this.reconnectAttempts > this.maxReconnectAttempts) {
                return;
            }

            ws = new WebSocket(self.url, protocols || []);
            ws.binaryType = this.binaryType;

            if (!reconnectAttempt) {
                eventTarget.dispatchEvent(generateEvent('connecting'));
                this.reconnectAttempts = 0;
            }

            if (self.debug || ReconnectingWebSocket.debugAll) {
                console.debug('ReconnectingWebSocket', 'attempt-connect', self.url);
            }

            var localWs = ws;
            var timeout = setTimeout(function() {
                if (self.debug || ReconnectingWebSocket.debugAll) {
                    console.debug('ReconnectingWebSocket', 'connection-timeout', self.url);
                }
                timedOut = true;
                localWs.close();
                timedOut = false;
            }, self.timeoutInterval);

            ws.onopen = function(event) {
                clearTimeout(timeout);
                if (self.debug || ReconnectingWebSocket.debugAll) {
                    console.debug('ReconnectingWebSocket', 'onopen', self.url);
                }
                self.protocol = ws.protocol;
                self.readyState = WebSocket.OPEN;
                self.reconnectAttempts = 0;
                var e = generateEvent('open');
                e.isReconnect = reconnectAttempt;
                reconnectAttempt = false;
                eventTarget.dispatchEvent(e);
            };

            ws.onclose = function(event) {
                clearTimeout(timeout);
                ws = null;
                if (forcedClose) {
                    self.readyState = WebSocket.CLOSED;
                    eventTarget.dispatchEvent(generateEvent('close'));
                } else {
                    self.readyState = WebSocket.CONNECTING;
                    var e = generateEvent('connecting');
                    e.code = event.code;
                    e.reason = event.reason;
                    e.wasClean = event.wasClean;
                    eventTarget.dispatchEvent(e);
                    if (!reconnectAttempt && !timedOut) {
                        if (self.debug || ReconnectingWebSocket.debugAll) {
                            console.debug('ReconnectingWebSocket', 'onclose', self.url);
                        }
                        eventTarget.dispatchEvent(generateEvent('close'));
                    }

                    var timeout = self.reconnectInterval * Math.pow(self.reconnectDecay, self.reconnectAttempts);
                    setTimeout(function() {
                        self.reconnectAttempts++;
                        self.open(true);
                    }, timeout > self.maxReconnectInterval ? self.maxReconnectInterval : timeout);
                }
            };
            ws.onmessage = function(event) {
                if (self.debug || ReconnectingWebSocket.debugAll) {
                    console.debug('ReconnectingWebSocket', 'onmessage', self.url, event.data);
                }
                var e = generateEvent('message');
                e.data = event.data;
                eventTarget.dispatchEvent(e);
            };
            ws.onerror = function(event) {
                if (self.debug || ReconnectingWebSocket.debugAll) {
                    console.debug('ReconnectingWebSocket', 'onerror', self.url, event);
                }
                eventTarget.dispatchEvent(generateEvent('error'));
            };
        }

        // Whether or not to create a websocket upon instantiation
        if (this.automaticOpen == true) {
            this.open(false);
        }

        /**
         * Transmits data to the server over the WebSocket connection.
         *
         * @param data a text string, ArrayBuffer or Blob to send to the server.
         */
        this.send = function(data) {
            if (ws) {
                if (self.debug || ReconnectingWebSocket.debugAll) {
                    console.debug('ReconnectingWebSocket', 'send', self.url, data);
                }
                return ws.send(data);
            } else {
                throw 'INVALID_STATE_ERR : Pausing to reconnect websocket';
            }
        };

        /**
         * Closes the WebSocket connection or connection attempt, if any.
         * If the connection is already CLOSED, this method does nothing.
         */
        this.close = function(code, reason) {
            // Default CLOSE_NORMAL code
            if (typeof code == 'undefined') {
                code = 1000;
            }
            forcedClose = true;
            if (ws) {
                ws.close(code, reason);
            }
        };

        /**
         * Additional public API method to refresh the connection if still open (close, re-open).
         * For example, if the app suspects bad data / missed heart beats, it can try to refresh.
         */
        this.refresh = function() {
            if (ws) {
                ws.close();
            }
        };
    }

    /**
     * An event listener to be called when the WebSocket connection's readyState changes to OPEN;
     * this indicates that the connection is ready to send and receive data.
     */
    ReconnectingWebSocket.prototype.onopen = function(event) {};
    /** An event listener to be called when the WebSocket connection's readyState changes to CLOSED. */
    ReconnectingWebSocket.prototype.onclose = function(event) {};
    /** An event listener to be called when a connection begins being attempted. */
    ReconnectingWebSocket.prototype.onconnecting = function(event) {};
    /** An event listener to be called when a message is received from the server. */
    ReconnectingWebSocket.prototype.onmessage = function(event) {};
    /** An event listener to be called when an error occurs. */
    ReconnectingWebSocket.prototype.onerror = function(event) {};

    /**
     * Whether all instances of ReconnectingWebSocket should log debug messages.
     * Setting this to true is the equivalent of setting all instances of ReconnectingWebSocket.debug to true.
     */
    ReconnectingWebSocket.debugAll = false;

    ReconnectingWebSocket.CONNECTING = WebSocket.CONNECTING;
    ReconnectingWebSocket.OPEN = WebSocket.OPEN;
    ReconnectingWebSocket.CLOSING = WebSocket.CLOSING;
    ReconnectingWebSocket.CLOSED = WebSocket.CLOSED;

    return ReconnectingWebSocket;
});
<commit_msg>Stop reconnecting if reconnectAttempts > maxReconnectAttempts<commit_after>// MIT License:
//
// Copyright (c) 2010-2012, Joe Walnes
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

/**
 * This behaves like a WebSocket in every way, except if it fails to connect,
 * or it gets disconnected, it will repeatedly poll until it successfully connects
 * again.
 *
 * It is API compatible, so when you have:
 *   ws = new WebSocket('ws://....');
 * you can replace with:
 *   ws = new ReconnectingWebSocket('ws://....');
 *
 * The event stream will typically look like:
 *  onconnecting
 *  onopen
 *  onmessage
 *  onmessage
 *  onclose // lost connection
 *  onconnecting
 *  onopen  // sometime later...
 *  onmessage
 *  onmessage
 *  etc...
 *
 * It is API compatible with the standard WebSocket API, apart from the following members:
 *
 * - `bufferedAmount`
 * - `extensions`
 * - `binaryType`
 *
 * Latest version: https://github.com/joewalnes/reconnecting-websocket/
 * - Joe Walnes
 *
 * Syntax
 * ======
 * var socket = new ReconnectingWebSocket(url, protocols, options);
 *
 * Parameters
 * ==========
 * url - The url you are connecting to.
 * protocols - Optional string or array of protocols.
 * options - See below
 *
 * Options
 * =======
 * Options can either be passed upon instantiation or set after instantiation:
 *
 * var socket = new ReconnectingWebSocket(url, null, { debug: true, reconnectInterval: 4000 });
 *
 * or
 *
 * var socket = new ReconnectingWebSocket(url);
 * socket.debug = true;
 * socket.reconnectInterval = 4000;
 *
 * debug
 * - Whether this instance should log debug messages. Accepts true or false. Default: false.
 *
 * automaticOpen
 * - Whether or not the websocket should attempt to connect immediately upon instantiation. The socket can be manually opened or closed at any time using ws.open() and ws.close().
 *
 * reconnectInterval
 * - The number of milliseconds to delay before attempting to reconnect. Accepts integer. Default: 1000.
 *
 * maxReconnectInterval
 * - The maximum number of milliseconds to delay a reconnection attempt. Accepts integer. Default: 30000.
 *
 * reconnectDecay
 * - The rate of increase of the reconnect delay. Allows reconnect attempts to back off when problems persist. Accepts integer or float. Default: 1.5.
 *
 * timeoutInterval
 * - The maximum time in milliseconds to wait for a connection to succeed before closing and retrying. Accepts integer. Default: 2000.
 *
 */
(function (global, factory) {
    if (typeof define === 'function' && define.amd) {
        define([], factory);
    } else if (typeof module !== 'undefined' && module.exports){
        module.exports = factory();
    } else {
        global.ReconnectingWebSocket = factory();
    }
})(this, function () {

    if (!('WebSocket' in window)) {
        return;
    }

    function ReconnectingWebSocket(url, protocols, options) {

        // Default settings
        var settings = {

            /** Whether this instance should log debug messages. */
            debug: false,

            /** Whether or not the websocket should attempt to connect immediately upon instantiation. */
            automaticOpen: true,

            /** The number of milliseconds to delay before attempting to reconnect. */
            reconnectInterval: 1000,
            /** The maximum number of milliseconds to delay a reconnection attempt. */
            maxReconnectInterval: 30000,
            /** The rate of increase of the reconnect delay. Allows reconnect attempts to back off when problems persist. */
            reconnectDecay: 1.5,

            /** The maximum time in milliseconds to wait for a connection to succeed before closing and retrying. */
            timeoutInterval: 2000,

            /** The maximum number of reconnection attempts to make. Unlimited if null. */
            maxReconnectAttempts: null,

            /** The binary type, possible values 'blob' or 'arraybuffer', default 'blob'. */
            binaryType: 'blob'
        }
        if (!options) { options = {}; }

        // Overwrite and define settings with options if they exist.
        for (var key in settings) {
            if (typeof options[key] !== 'undefined') {
                this[key] = options[key];
            } else {
                this[key] = settings[key];
            }
        }

        // These should be treated as read-only properties

        /** The URL as resolved by the constructor. This is always an absolute URL. Read only. */
        this.url = url;

        /** The number of attempted reconnects since starting, or the last successful connection. Read only. */
        this.reconnectAttempts = 0;

        /**
         * The current state of the connection.
         * Can be one of: WebSocket.CONNECTING, WebSocket.OPEN, WebSocket.CLOSING, WebSocket.CLOSED
         * Read only.
         */
        this.readyState = WebSocket.CONNECTING;

        /**
         * A string indicating the name of the sub-protocol the server selected; this will be one of
         * the strings specified in the protocols parameter when creating the WebSocket object.
         * Read only.
         */
        this.protocol = null;

        // Private state variables

        var self = this;
        var ws;
        var forcedClose = false;
        var timedOut = false;
        var eventTarget = document.createElement('div');

        // Wire up "on*" properties as event handlers

        eventTarget.addEventListener('open',       function(event) { self.onopen(event); });
        eventTarget.addEventListener('close',      function(event) { self.onclose(event); });
        eventTarget.addEventListener('connecting', function(event) { self.onconnecting(event); });
        eventTarget.addEventListener('message',    function(event) { self.onmessage(event); });
        eventTarget.addEventListener('error',      function(event) { self.onerror(event); });

        // Expose the API required by EventTarget

        this.addEventListener = eventTarget.addEventListener.bind(eventTarget);
        this.removeEventListener = eventTarget.removeEventListener.bind(eventTarget);
        this.dispatchEvent = eventTarget.dispatchEvent.bind(eventTarget);

        /**
         * This function generates an event that is compatible with standard
         * compliant browsers and IE9 - IE11
         *
         * This will prevent the error:
         * Object doesn't support this action
         *
         * http://stackoverflow.com/questions/19345392/why-arent-my-parameters-getting-passed-through-to-a-dispatched-event/19345563#19345563
         * @param s String The name that the event should use
         * @param args Object an optional object that the event will use
         */
        function generateEvent(s, args) {
        	var evt = document.createEvent("CustomEvent");
        	evt.initCustomEvent(s, false, false, args);
        	return evt;
        };

        this.open = function (reconnectAttempt) {
            if (reconnectAttempt && this.reconnectAttempts > this.maxReconnectAttempts) {
                return;
            }

            ws = new WebSocket(self.url, protocols || []);
            ws.binaryType = this.binaryType;

            if (!reconnectAttempt) {
                eventTarget.dispatchEvent(generateEvent('connecting'));
                this.reconnectAttempts = 0;
            }

            if (self.debug || ReconnectingWebSocket.debugAll) {
                console.debug('ReconnectingWebSocket', 'attempt-connect', self.url);
            }

            var localWs = ws;
            var timeout = setTimeout(function() {
                if (self.debug || ReconnectingWebSocket.debugAll) {
                    console.debug('ReconnectingWebSocket', 'connection-timeout', self.url);
                }
                timedOut = true;
                localWs.close();
                timedOut = false;
            }, self.timeoutInterval);

            ws.onopen = function(event) {
                clearTimeout(timeout);
                if (self.debug || ReconnectingWebSocket.debugAll) {
                    console.debug('ReconnectingWebSocket', 'onopen', self.url);
                }
                self.protocol = ws.protocol;
                self.readyState = WebSocket.OPEN;
                self.reconnectAttempts = 0;
                var e = generateEvent('open');
                e.isReconnect = reconnectAttempt;
                reconnectAttempt = false;
                eventTarget.dispatchEvent(e);
            };

            ws.onclose = function(event) {
                clearTimeout(timeout);
                ws = null;
                if (forcedClose) {
                    self.readyState = WebSocket.CLOSED;
                    eventTarget.dispatchEvent(generateEvent('close'));
                } else {
                    self.readyState = WebSocket.CONNECTING;
                    var e = generateEvent('connecting');
                    e.code = event.code;
                    e.reason = event.reason;
                    e.wasClean = event.wasClean;
                    eventTarget.dispatchEvent(e);
                    if (!reconnectAttempt && !timedOut) {
                        if (self.debug || ReconnectingWebSocket.debugAll) {
                            console.debug('ReconnectingWebSocket', 'onclose', self.url);
                        }
                        eventTarget.dispatchEvent(generateEvent('close'));
                    }

                    var timeout = self.reconnectInterval * Math.pow(self.reconnectDecay, self.reconnectAttempts);
                    setTimeout(function() {
                        self.reconnectAttempts++;
                        self.open(true);
                    }, timeout > self.maxReconnectInterval ? self.maxReconnectInterval : timeout);
                }
            };
            ws.onmessage = function(event) {
                if (self.debug || ReconnectingWebSocket.debugAll) {
                    console.debug('ReconnectingWebSocket', 'onmessage', self.url, event.data);
                }
                var e = generateEvent('message');
                e.data = event.data;
                eventTarget.dispatchEvent(e);
            };
            ws.onerror = function(event) {
                if (self.debug || ReconnectingWebSocket.debugAll) {
                    console.debug('ReconnectingWebSocket', 'onerror', self.url, event);
                }
                eventTarget.dispatchEvent(generateEvent('error'));
            };
        }

        // Whether or not to create a websocket upon instantiation
        if (this.automaticOpen == true) {
            this.open(false);
        }

        /**
         * Transmits data to the server over the WebSocket connection.
         *
         * @param data a text string, ArrayBuffer or Blob to send to the server.
         */
        this.send = function(data) {
            if (ws) {
                if (self.debug || ReconnectingWebSocket.debugAll) {
                    console.debug('ReconnectingWebSocket', 'send', self.url, data);
                }
                return ws.send(data);
            } else {
                throw 'INVALID_STATE_ERR : Pausing to reconnect websocket';
            }
        };

        /**
         * Closes the WebSocket connection or connection attempt, if any.
         * If the connection is already CLOSED, this method does nothing.
         */
        this.close = function(code, reason) {
            // Default CLOSE_NORMAL code
            if (typeof code == 'undefined') {
                code = 1000;
            }
            forcedClose = true;
            if (ws) {
                ws.close(code, reason);
            }
        };

        /**
         * Additional public API method to refresh the connection if still open (close, re-open).
         * For example, if the app suspects bad data / missed heart beats, it can try to refresh.
         */
        this.refresh = function() {
            if (ws) {
                ws.close();
            }
        };
    }

    /**
     * An event listener to be called when the WebSocket connection's readyState changes to OPEN;
     * this indicates that the connection is ready to send and receive data.
     */
    ReconnectingWebSocket.prototype.onopen = function(event) {};
    /** An event listener to be called when the WebSocket connection's readyState changes to CLOSED. */
    ReconnectingWebSocket.prototype.onclose = function(event) {};
    /** An event listener to be called when a connection begins being attempted. */
    ReconnectingWebSocket.prototype.onconnecting = function(event) {};
    /** An event listener to be called when a message is received from the server. */
    ReconnectingWebSocket.prototype.onmessage = function(event) {};
    /** An event listener to be called when an error occurs. */
    ReconnectingWebSocket.prototype.onerror = function(event) {};

    /**
     * Whether all instances of ReconnectingWebSocket should log debug messages.
     * Setting this to true is the equivalent of setting all instances of ReconnectingWebSocket.debug to true.
     */
    ReconnectingWebSocket.debugAll = false;

    ReconnectingWebSocket.CONNECTING = WebSocket.CONNECTING;
    ReconnectingWebSocket.OPEN = WebSocket.OPEN;
    ReconnectingWebSocket.CLOSING = WebSocket.CLOSING;
    ReconnectingWebSocket.CLOSED = WebSocket.CLOSED;

    return ReconnectingWebSocket;
});
<|endoftext|>


<commit_before>function MageSplit()
{
    this._name = null;

    // Ten year cookie duration is what optimizely uses.
    this._cookieDuration = 3650;

    this.run = function(name, callback)
    {
        this._name = name;

        if (this._isTriggered()) {
            _gaq.push(['_trackEvent', 'MageSplit', this._name + ': Enabled']);
            this._log("Enabled (" + this._getRandomNumber() + ")");
            callback();
        } else {
            _gaq.push(['_trackEvent', 'MageSplit', this._name + ': Disabled']);
            this._log("Disabled (" + this._getRandomNumber() + ")");
        }
    };

    this._isTriggered = function()
    {
        if (this._getUrlOverride() !== null) {
            this._log("Overriden by URL");
            return this._getUrlOverride();
        }

        return (this._getRandomNumber() > 0.5);
    };

    this._getRandomNumber = function()
    {
        if (typeof(jQuery.cookie(this._getCookieName())) == 'undefined') {
            var random = Math.random(0, 1);
            jQuery.cookie(this._getCookieName(), random, {expires: this._cookieDuration});
        }

        return jQuery.cookie(this._getCookieName());
    }

    this._getCookieName = function()
    {
        return 'magesplit_' + this._name + '_random_number';
    };

    this._getUrlOverride = function()
    {
        param = window.location.search.substring(1);
        testEnabledParamName = 'magesplit_' + this._name + '=1';
        testDisabledParamName = 'magesplit_' + this._name + '=0';

        if (param == testEnabledParamName) {
            return true;
        } else if (param == testDisabledParamName) {
            return false;
        }

        return null;
    };

    this._log = function(message)
    {
        console.log("Split Test: " + this._name + ": " + message);
    };
}<commit_msg>Public methods to check if the cookie is set and if the test is enabled or not.<commit_after>function MageSplit()
{
    this._name = null;

    // Ten year cookie duration is what optimizely uses.
    this._cookieDuration = 3650;

    this.run = function(name, callback)
    {
        this.setName(name);

        if (this.isTriggered()) {
            _gaq.push(['_trackEvent', 'MageSplit', this._name + ': Enabled']);
            this._log("Enabled (" + this._getRandomNumber() + ")");
            callback();
        } else {
            _gaq.push(['_trackEvent', 'MageSplit', this._name + ': Disabled']);
            this._log("Disabled (" + this._getRandomNumber() + ")");
        }
    };

    this.setName = function()
    {
        this._name = name;
        return this;
    }

    this.isCookieSet = function()
    {
        return (typeof(jQuery.cookie(this._getCookieName())) != 'undefined');
    };

    this.isTriggered = function()
    {
        if (this._getUrlOverride() !== null) {
            this._log("Overriden by URL");
            return this._getUrlOverride();
        }

        return (this._getRandomNumber() > 0.5);
    };

    this._getRandomNumber = function()
    {
        if (! this.isCookieSet()) {
            var random = Math.random(0, 1);
            jQuery.cookie(this._getCookieName(), random, {expires: this._cookieDuration});
        }

        return jQuery.cookie(this._getCookieName());
    };

    this._getCookieName = function()
    {
        return 'magesplit_' + this._name + '_random_number';
    };

    this._getUrlOverride = function()
    {
        param = window.location.search.substring(1);
        testEnabledParamName = 'magesplit_' + this._name + '=1';
        testDisabledParamName = 'magesplit_' + this._name + '=0';

        if (param == testEnabledParamName) {
            return true;
        } else if (param == testDisabledParamName) {
            return false;
        }

        return null;
    };

    this._log = function(message)
    {
        console.log("Split Test: " + this._name + ": " + message);
    };
}<|endoftext|>


<commit_before>(function (state, module, define) {
  'use strict';

  function DataState () {
    var self = this;

    //////////////////////////////
    // Gets the current data-state
    //
    // Returns an array of states, or null
    //////////////////////////////
    this.get = function (el) {
      var current = el.getAttribute('data-state');
      if (current !== null) {
        current = current.split(' ');
      }
      return current;
    };

    //////////////////////////////
    // Has the given state
    //
    // Returns true if state is applied
    //////////////////////////////
    this.has = function (el, has) {
      var states = self.get(el);
      if (states === null) {
	return false
      }
      else if (states.indexOf(has) !== -1) {
        return true;
      }
      else {
        return false;
      }
    };

    //////////////////////////////
    // Adds the given state
    //
    // Adds the current state to `data-state` attribute
    //////////////////////////////
    this.add = function (el, add) {
      var current = self.get(el);

      if (current === null) {
        el.setAttribute('data-state', add);
      }
      else {
        if (current.indexOf(add) === -1) {
          current.push(add);
          el.setAttribute('data-state', current.join(' '));
        }
      }
    };
    this.set = this.add;

    //////////////////////////////
    // Removes the given state
    //
    // Removes the current state from `data-state` attribute, if it exists
    //////////////////////////////
    this.remove = function (el, remove) {
      var current = self.get(el),
          index,
          length;

      if (current !== null) {
        index = current.indexOf(remove);
        length = current.length;
        if (index !== -1) {
          current = current.slice(0, index).concat(current.slice(index + 1, length));
          if (current.length === 0) {
            el.removeAttribute('data-state');
          }
          else {
            el.setAttribute('data-state', current.join(' '));
          }
        }
      }
    };
  }

  //////////////////////////////
  // We only ever want one instance of state
  //////////////////////////////
  state = state || new DataState();

  //////////////////////////////
  // All of the various exports!
  //////////////////////////////
  if (typeof module !== 'undefined' && module.exports) {
    module.exports = state;
  } else if (typeof define === 'function' && define.amd) {
    define(function () {
      return state;
    });
  } else {
    window.state = state;
  }

})(window.state, window.module, window.define);<commit_msg>Toggle<commit_after>(function (state, module, define) {
  'use strict';

  function DataState () {
    var self = this;

    //////////////////////////////
    // Gets the current data-state
    //
    // Returns an array of states, or null
    //////////////////////////////
    this.get = function (el) {
      var current = el.getAttribute('data-state');
      if (current !== null) {
        current = current.split(' ');
      }
      return current;
    };

    //////////////////////////////
    // Has the given state
    //
    // Returns true if state is applied
    //////////////////////////////
    this.has = function (el, has) {
      var states = self.get(el);
      if (states === null) {
        return false;
      }
      else if (states.indexOf(has) !== -1) {
        return true;
      }
      else {
        return false;
      }
    };

    //////////////////////////////
    // Adds the given state
    //
    // Adds the current state to `data-state` attribute
    //////////////////////////////
    this.add = function (el, add) {
      var current = self.get(el);

      if (current === null) {
        el.setAttribute('data-state', add);
      }
      else {
        if (current.indexOf(add) === -1) {
          current.push(add);
          el.setAttribute('data-state', current.join(' '));
        }
      }
    };
    this.set = this.add;

    //////////////////////////////
    // Removes the given state
    //
    // Removes the current state from `data-state` attribute, if it exists
    //////////////////////////////
    this.remove = function (el, remove) {
      var current = self.get(el),
          index,
          length;

      if (current !== null) {
        index = current.indexOf(remove);
        length = current.length;
        if (index !== -1) {
          current = current.slice(0, index).concat(current.slice(index + 1, length));
          if (current.length === 0) {
            el.removeAttribute('data-state');
          }
          else {
            el.setAttribute('data-state', current.join(' '));
          }
        }
      }
    };

    this.toggle = function (el, toggle) {
      if (self.has(el, toggle)) {
        self.remove(el, toggle);
      }
      else {
        self.add(el.toggle);
      }
    };
  }

  //////////////////////////////
  // We only ever want one instance of state
  //////////////////////////////
  state = state || new DataState();

  //////////////////////////////
  // All of the various exports!
  //////////////////////////////
  if (typeof module !== 'undefined' && module.exports) {
    module.exports = state;
  } else if (typeof define === 'function' && define.amd) {
    define(function () {
      return state;
    });
  } else {
    window.state = state;
  }

})(window.state, window.module, window.define);<|endoftext|>


<commit_before>// Copyright 2013-2017, University of Colorado Boulder

/**
 * A listener for common button usage, providing the fire() method/callback and helpful properties.
 *
 * TODO: name (because ButtonListener was taken). Can we rename the old ButtonListener and have this be ButtonListener?
 *
 * TODO: unit tests
 *
 * TODO: add example usage
 *
 * @author Jonathan Olson <jonathan.olson@colorado.edu>
 */

define( function( require ) {
  'use strict';

  var inherit = require( 'PHET_CORE/inherit' );
  var scenery = require( 'SCENERY/scenery' );
  var Property = require( 'AXON/Property' );
  var ObservableArray = require( 'AXON/ObservableArray' );
  var PressListener = require( 'SCENERY/listeners/PressListener' );

  /**
   * @constructor
   * @extends PressListener
   *
   * @param {Object} [options] - See the constructor body (below) and in PressListener for documented options.
   */
  function FireListener( options ) {
    options = _.extend( {
      // {Function|null} - Called as fire() when the button is fired.
      fire: null,

      // {boolean} - If true, the button will fire when the button is pressed. If false, the button will fire when the
      // button is released while the pointer is over the button.
      fireOnDown: false,

      // {Property.<boolean>} - A property that will be controlled by this listener. It will be set to true when at
      // least one pointer is over the button.
      // A custom property may be passed in here, as it may be useful for hooking up to existing button models.
      isOverProperty: new Property( false ),

      // {Property.<boolean>} - A property that will be controlled by this listener. It will be set to true when either:
      //   1. The button is pressed and the pointer that is pressing is over the button.
      //   2. There is at least one unpressed pointer that is over the button.
      // A custom property may be passed in here, as it may be useful for hooking up to existing button models.
      isHoveringProperty: new Property( false ),

      // {Property.<boolean>} - A property that will be controlled by this listener. It will be set to true when either:
      //   1. The button is pressed.
      //   2. There is at least one unpressed pointer that is over the button.
      // This is essentially true when ( isPressed || isHovering ).
      // A custom property may be passed in here, as it may be useful for hooking up to existing button models.
      isHighlightedProperty: new Property( false )
    }, options );

    assert && assert( options.fire === null || typeof options.fire === 'function',
      'The fire callback, if provided, should be a function' );
    assert && assert( typeof options.fireOnDown === 'boolean', 'fireOnDown should be a boolean' );
    assert && assert( options.isOverProperty instanceof Property && options.isOverProperty.value === false,
      'If a custom isOverProperty is provided, it must be a Property that is false initially' );
    assert && assert( options.isHoveringProperty instanceof Property && options.isHoveringProperty.value === false,
      'If a custom isHoveringProperty is provided, it must be a Property that is false initially' );
    assert && assert( options.isHighlightedProperty instanceof Property && options.isHighlightedProperty.value === false,
      'If a custom isHighlightedProperty is provided, it must be a Property that is false initially' );

    PressListener.call( this, options );

    // @public {ObservableArray.<Pointer>} - Contains all pointers that are over our button. Tracked by adding with
    // 'enter' events and removin with 'exit' events.
    this.overPointers = new ObservableArray();

    // @public {Property.<boolean>} - See options for documentation.
    this.isOverProperty = options.isOverProperty;
    this.isHoveringProperty = options.isHoveringProperty;
    this.isHighlightedProperty = options.isHighlightedProperty;

    // @private - See options for documentation.
    this._fireOnDown = options.fireOnDown;
    this._fireCallback = options.fire;

    // isOverProperty updates (not a DerivedProperty because we need to hook to passed-in properties)
    this.overPointers.lengthProperty.link( this.invalidateOver.bind( this ) );

    // isHoveringProperty updates (not a DerivedProperty because we need to hook to passed-in properties)
    var isHoveringListener = this.invalidateHovering.bind( this );
    this.overPointers.lengthProperty.link( isHoveringListener );
    this.isPressedProperty.link( isHoveringListener );
    // overPointers will be cleared on disposal, so we should release references then.
    this.overPointers.addItemAddedListener( function( pointer ) {
      pointer.isDownProperty.link( isHoveringListener );
    } );
    this.overPointers.addItemRemovedListener( function( pointer ) {
      pointer.isDownProperty.unlink( isHoveringListener );
    } );

    // isHighlightedProperty updates (not a DerivedProperty because we need to hook to passed-in properties)
    var isHighlightedListener = this.invalidateHighlighted.bind( this );
    this.isHoveringProperty.link( isHighlightedListener );
    this.isPressedProperty.link( isHighlightedListener );
  }

  scenery.register( 'FireListener', FireListener );

  inherit( PressListener, FireListener, {

    /**
     * Fires any associated button fire callback.
     * @public
     *
     * NOTE: This is safe to call on the listener externally.
     */
    fire: function() {
      sceneryLog && sceneryLog.InputListener && sceneryLog.InputListener( 'FireListener fire' );
      sceneryLog && sceneryLog.InputListener && sceneryLog.push();

      this._fireCallback && this._fireCallback();

      sceneryLog && sceneryLog.InputListener && sceneryLog.pop();
    },

    /**
     * Called with 'enter' events (part of the listener API).
     * @public (scenery-internal)
     *
     * NOTE: Do not call directly.
     *
     * @param {Event} event
     */
    enter: function( event ) {
      sceneryLog && sceneryLog.InputListener && sceneryLog.InputListener( 'FireListener enter' );
      sceneryLog && sceneryLog.InputListener && sceneryLog.push();

      this.overPointers.push( event.pointer );

      sceneryLog && sceneryLog.InputListener && sceneryLog.pop();
    },

    /**
     * Called with 'exit' events (part of the listener API).
     * @public (scenery-internal)
     *
     * NOTE: Do not call directly.
     *
     * @param {Event} event
     */
    exit: function( event ) {
      sceneryLog && sceneryLog.InputListener && sceneryLog.InputListener( 'FireListener exit' );
      sceneryLog && sceneryLog.InputListener && sceneryLog.push();

      assert && assert( this.overPointers.contains( event.pointer ), 'Exit event not matched by an enter event' );

      this.overPointers.remove( event.pointer );

      sceneryLog && sceneryLog.InputListener && sceneryLog.pop();
    },

    /**
     * Recomputes the value for isOverProperty. Separate to reduce anonymous function closures.
     * @private
     */
    invalidateOver: function() {
      this.isOverProperty.value = this.overPointers.length > 0;
    },

    /**
     * Recomputes the value for isHoveringProperty. Separate to reduce anonymous function closures.
     * @private
     */
    invalidateHovering: function() {
      var pointers = this.overPointers.getArray();
      for ( var i = 0; i < pointers.length; i++ ) {
        var pointer = pointers[ i ];
        if ( !pointer.isDown || pointer === this.pointer ) {
          this.isHoveringProperty.value = true;
          return;
        }
      }
      this.isHoveringProperty.value = false;
    },

    /**
     * Recomputes the value for isHighlightedProperty. Separate to reduce anonymous function closures.
     * @private
     */
    invalidateHighlighted: function() {
      this.isHighlightedProperty.value = this.isHoveringProperty.value || this.isPressedProperty.value;
    },

    /**
     * Presses the button.
     * @public
     * @override
     *
     * NOTE: This is safe to call externally in order to attempt to start a press. fireListener.canPress( event ) can
     * be used to determine whether this will actually start a press.
     *
     * @param {Event} event
     * @returns {boolean} success - Returns whether the press was actually started
     */
    press: function( event ) {
      var success = PressListener.prototype.press.call( this, event );

      if ( success ) {
        if ( this._fireOnDown ) {
          this.fire( event );
        }
      }

      return success;
    },

    /**
     * Releases the button.
     * @public
     * @override
     *
     * NOTE: This can be safely called externally in order to force a release of this button (no actual 'up' event is
     * needed). If the cancel/interrupt behavior is more preferable (will not fire the button), then call interrupt()
     * on this listener instead.
     */
    release: function() {
      PressListener.prototype.release.call( this, event );

      // Notify after the rest of release is called in order to prevent it from triggering interrupt().
      // TODO: Is this a problem that we can't access things like this.pointer here?
      if ( !this._fireOnDown && this.isHoveringProperty.value && !this.interrupted ) {
        this.fire();
      }
    },

    /**
     * Disposes the listener, releasing references. It should not be used after this.
     * @public
     */
    dispose: function() {
      sceneryLog && sceneryLog.InputListener && sceneryLog.InputListener( 'FireListener dispose' );
      sceneryLog && sceneryLog.InputListener && sceneryLog.push();

      // We need to release references to any pointers that are over us.
      this.overPointers.clear();

      PressListener.prototype.dispose.call( this );

      sceneryLog && sceneryLog.InputListener && sceneryLog.pop();
    }
  } );

  return FireListener;
} );
<commit_msg>FireListener release fix<commit_after>// Copyright 2013-2017, University of Colorado Boulder

/**
 * A listener for common button usage, providing the fire() method/callback and helpful properties.
 *
 * TODO: name (because ButtonListener was taken). Can we rename the old ButtonListener and have this be ButtonListener?
 *
 * TODO: unit tests
 *
 * TODO: add example usage
 *
 * @author Jonathan Olson <jonathan.olson@colorado.edu>
 */

define( function( require ) {
  'use strict';

  var inherit = require( 'PHET_CORE/inherit' );
  var scenery = require( 'SCENERY/scenery' );
  var Property = require( 'AXON/Property' );
  var ObservableArray = require( 'AXON/ObservableArray' );
  var PressListener = require( 'SCENERY/listeners/PressListener' );

  /**
   * @constructor
   * @extends PressListener
   *
   * @param {Object} [options] - See the constructor body (below) and in PressListener for documented options.
   */
  function FireListener( options ) {
    options = _.extend( {
      // {Function|null} - Called as fire() when the button is fired.
      fire: null,

      // {boolean} - If true, the button will fire when the button is pressed. If false, the button will fire when the
      // button is released while the pointer is over the button.
      fireOnDown: false,

      // {Property.<boolean>} - A property that will be controlled by this listener. It will be set to true when at
      // least one pointer is over the button.
      // A custom property may be passed in here, as it may be useful for hooking up to existing button models.
      isOverProperty: new Property( false ),

      // {Property.<boolean>} - A property that will be controlled by this listener. It will be set to true when either:
      //   1. The button is pressed and the pointer that is pressing is over the button.
      //   2. There is at least one unpressed pointer that is over the button.
      // A custom property may be passed in here, as it may be useful for hooking up to existing button models.
      isHoveringProperty: new Property( false ),

      // {Property.<boolean>} - A property that will be controlled by this listener. It will be set to true when either:
      //   1. The button is pressed.
      //   2. There is at least one unpressed pointer that is over the button.
      // This is essentially true when ( isPressed || isHovering ).
      // A custom property may be passed in here, as it may be useful for hooking up to existing button models.
      isHighlightedProperty: new Property( false )
    }, options );

    assert && assert( options.fire === null || typeof options.fire === 'function',
      'The fire callback, if provided, should be a function' );
    assert && assert( typeof options.fireOnDown === 'boolean', 'fireOnDown should be a boolean' );
    assert && assert( options.isOverProperty instanceof Property && options.isOverProperty.value === false,
      'If a custom isOverProperty is provided, it must be a Property that is false initially' );
    assert && assert( options.isHoveringProperty instanceof Property && options.isHoveringProperty.value === false,
      'If a custom isHoveringProperty is provided, it must be a Property that is false initially' );
    assert && assert( options.isHighlightedProperty instanceof Property && options.isHighlightedProperty.value === false,
      'If a custom isHighlightedProperty is provided, it must be a Property that is false initially' );

    PressListener.call( this, options );

    // @public {ObservableArray.<Pointer>} - Contains all pointers that are over our button. Tracked by adding with
    // 'enter' events and removin with 'exit' events.
    this.overPointers = new ObservableArray();

    // @public {Property.<boolean>} - See options for documentation.
    this.isOverProperty = options.isOverProperty;
    this.isHoveringProperty = options.isHoveringProperty;
    this.isHighlightedProperty = options.isHighlightedProperty;

    // @private - See options for documentation.
    this._fireOnDown = options.fireOnDown;
    this._fireCallback = options.fire;

    // isOverProperty updates (not a DerivedProperty because we need to hook to passed-in properties)
    this.overPointers.lengthProperty.link( this.invalidateOver.bind( this ) );

    // isHoveringProperty updates (not a DerivedProperty because we need to hook to passed-in properties)
    var isHoveringListener = this.invalidateHovering.bind( this );
    this.overPointers.lengthProperty.link( isHoveringListener );
    this.isPressedProperty.link( isHoveringListener );
    // overPointers will be cleared on disposal, so we should release references then.
    this.overPointers.addItemAddedListener( function( pointer ) {
      pointer.isDownProperty.link( isHoveringListener );
    } );
    this.overPointers.addItemRemovedListener( function( pointer ) {
      pointer.isDownProperty.unlink( isHoveringListener );
    } );

    // isHighlightedProperty updates (not a DerivedProperty because we need to hook to passed-in properties)
    var isHighlightedListener = this.invalidateHighlighted.bind( this );
    this.isHoveringProperty.link( isHighlightedListener );
    this.isPressedProperty.link( isHighlightedListener );
  }

  scenery.register( 'FireListener', FireListener );

  inherit( PressListener, FireListener, {

    /**
     * Fires any associated button fire callback.
     * @public
     *
     * NOTE: This is safe to call on the listener externally.
     */
    fire: function() {
      sceneryLog && sceneryLog.InputListener && sceneryLog.InputListener( 'FireListener fire' );
      sceneryLog && sceneryLog.InputListener && sceneryLog.push();

      this._fireCallback && this._fireCallback();

      sceneryLog && sceneryLog.InputListener && sceneryLog.pop();
    },

    /**
     * Called with 'enter' events (part of the listener API).
     * @public (scenery-internal)
     *
     * NOTE: Do not call directly.
     *
     * @param {Event} event
     */
    enter: function( event ) {
      sceneryLog && sceneryLog.InputListener && sceneryLog.InputListener( 'FireListener enter' );
      sceneryLog && sceneryLog.InputListener && sceneryLog.push();

      this.overPointers.push( event.pointer );

      sceneryLog && sceneryLog.InputListener && sceneryLog.pop();
    },

    /**
     * Called with 'exit' events (part of the listener API).
     * @public (scenery-internal)
     *
     * NOTE: Do not call directly.
     *
     * @param {Event} event
     */
    exit: function( event ) {
      sceneryLog && sceneryLog.InputListener && sceneryLog.InputListener( 'FireListener exit' );
      sceneryLog && sceneryLog.InputListener && sceneryLog.push();

      assert && assert( this.overPointers.contains( event.pointer ), 'Exit event not matched by an enter event' );

      this.overPointers.remove( event.pointer );

      sceneryLog && sceneryLog.InputListener && sceneryLog.pop();
    },

    /**
     * Recomputes the value for isOverProperty. Separate to reduce anonymous function closures.
     * @private
     */
    invalidateOver: function() {
      this.isOverProperty.value = this.overPointers.length > 0;
    },

    /**
     * Recomputes the value for isHoveringProperty. Separate to reduce anonymous function closures.
     * @private
     */
    invalidateHovering: function() {
      var pointers = this.overPointers.getArray();
      for ( var i = 0; i < pointers.length; i++ ) {
        var pointer = pointers[ i ];
        if ( !pointer.isDown || pointer === this.pointer ) {
          this.isHoveringProperty.value = true;
          return;
        }
      }
      this.isHoveringProperty.value = false;
    },

    /**
     * Recomputes the value for isHighlightedProperty. Separate to reduce anonymous function closures.
     * @private
     */
    invalidateHighlighted: function() {
      this.isHighlightedProperty.value = this.isHoveringProperty.value || this.isPressedProperty.value;
    },

    /**
     * Presses the button.
     * @public
     * @override
     *
     * NOTE: This is safe to call externally in order to attempt to start a press. fireListener.canPress( event ) can
     * be used to determine whether this will actually start a press.
     *
     * @param {Event} event
     * @returns {boolean} success - Returns whether the press was actually started
     */
    press: function( event ) {
      var success = PressListener.prototype.press.call( this, event );

      if ( success ) {
        if ( this._fireOnDown ) {
          this.fire( event );
        }
      }

      return success;
    },

    /**
     * Releases the button.
     * @public
     * @override
     *
     * NOTE: This can be safely called externally in order to force a release of this button (no actual 'up' event is
     * needed). If the cancel/interrupt behavior is more preferable (will not fire the button), then call interrupt()
     * on this listener instead.
     */
    release: function() {
      PressListener.prototype.release.call( this );

      // Notify after the rest of release is called in order to prevent it from triggering interrupt().
      // TODO: Is this a problem that we can't access things like this.pointer here?
      if ( !this._fireOnDown && this.isHoveringProperty.value && !this.interrupted ) {
        this.fire();
      }
    },

    /**
     * Disposes the listener, releasing references. It should not be used after this.
     * @public
     */
    dispose: function() {
      sceneryLog && sceneryLog.InputListener && sceneryLog.InputListener( 'FireListener dispose' );
      sceneryLog && sceneryLog.InputListener && sceneryLog.push();

      // We need to release references to any pointers that are over us.
      this.overPointers.clear();

      PressListener.prototype.dispose.call( this );

      sceneryLog && sceneryLog.InputListener && sceneryLog.pop();
    }
  } );

  return FireListener;
} );
<|endoftext|>