<commit_before>/*
 * Copyright 2012 The Toolkitchen Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style
 * license that can be found in the LICENSE file.
 */

// SideTable is a weak map where possible. If WeakMap is not available the
// association is stored as an expando property.
(function(scope) {
  var SideTable;
  // TODO(dfreedman): WeakMap does not allow for Events to be keys in Firefox
  if (typeof WeakMap !== 'undefined' && navigator.userAgent.indexOf('Firefox/') < 0) {
    SideTable = WeakMap;
  } else {
    var defineProperty = Object.defineProperty;
    var hasOwnProperty = Object.hasOwnProperty;
    var counter = new Date().getTime() % 1e9;

    SideTable = function() {
      this.name = '__st' + (Math.random() * 1e9 >>> 0) + (counter++ + '__');
    };

    SideTable.prototype = {
      set: function(key, value) {
        defineProperty(key, this.name, {value: value, writable: true});
      },
      get: function(key) {
        return hasOwnProperty.call(key, this.name) ? key[this.name] : undefined;
      },
      delete: function(key) {
        this.set(key, undefined);
      }
    }
  scope.SideTable = SideTable;
})(window.PointerEventsPolyfill);
<commit_msg>fix syntax error in sidetable<commit_after>/*
 * Copyright 2012 The Toolkitchen Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style
 * license that can be found in the LICENSE file.
 */

// SideTable is a weak map where possible. If WeakMap is not available the
// association is stored as an expando property.
(function(scope) {
  var SideTable;
  // TODO(dfreedman): WeakMap does not allow for Events to be keys in Firefox
  if (typeof WeakMap !== 'undefined' && navigator.userAgent.indexOf('Firefox/') < 0) {
    SideTable = WeakMap;
  } else {
    var defineProperty = Object.defineProperty;
    var hasOwnProperty = Object.hasOwnProperty;
    var counter = new Date().getTime() % 1e9;

    SideTable = function() {
      this.name = '__st' + (Math.random() * 1e9 >>> 0) + (counter++ + '__');
    };

    SideTable.prototype = {
      set: function(key, value) {
        defineProperty(key, this.name, {value: value, writable: true});
      },
      get: function(key) {
        return hasOwnProperty.call(key, this.name) ? key[this.name] : undefined;
      },
      delete: function(key) {
        this.set(key, undefined);
      }
    };
  }
  scope.SideTable = SideTable;
})(window.PointerEventsPolyfill);
<|endoftext|>


<commit_before>(function($) {
    "use strict";

    function Impl()
    {
        var currentDate = new Date();

        ///////// Const /////////

        var YEAR_DATA = "setYear";
        var MONTH_DATA = "setMonth";

        var CALENDAR_HEADER_CLASS = "calendar-header";
        var CALENDAR_HEADER_TITLE_CLASS = "calendar-header-title";
        var CALENDAR_HEADER_TITLE_DATE_CLASS = "calendar-header-title-date";

        var CALENDAR_EVENT_LIST_CLASS = "calendar-event-list";
        var CALENDAR_EVENT_LIST_DATE_CLASS = "calendar-event-list-date";

        var M_D_CLASS = "m-d";

        var MONTH_DAY_CLASS = "month-day";
        var MONTH_DAY_BLANK_CLASS = "month-day-blank";
        var MONTH_DAY_EVENT_CLASS = "month-day-event";
        var MONTH_DAY_WRAP_CLASS = "month-day-wrap";
        var MONTH_DAY_NUMBER_CLASS = "month-day-number";
        var MONTH_DAY_INDICATOR_WRAP_CLASS = "month-day-indicator-wrap";
        var MONTH_DAY_TODAY_CLASS = "month-day-today";
        var MONTH_PAST_DAY_CLASS = "month-past-day";
        var MONTH_RESET_CLASS = "month-reset";
        var MONTH_WEEK_CLASS = "month-week";
        var MONTH_NEXT_CLASS = "month-next";
        var MONTH_PREV_CLASS = "month-prev";
        var MONTH_EVENT_LIST_ITEM_CLASS = "month-event-list-item";
        var MONTH_VIEW_BUTTON_CLASS = "month-view-button";

        var WEEK_TITLE_WRAP_CLASS = "week-title-wrap";

        ///////// Global Variable /////////

        var _uniqueId = $(this).attr("id");
        var _parent = "#" + _uniqueId;

        var _currentMonth = currentDate.getMonth() + 1;
        var _currentYear = currentDate.getFullYear();
        var _currentDay = currentDate.getDate();

        var _weekNames = _getWeekNames();
        var _monthNames = _getMonthNames();

        ///////// Init UI /////////

        _appendMonthBody();
        _addCalendarHeader();
        _appendEventList();
        _initMonth();
        _registerHandler();

        ///////// Utilities /////////

        function _getClassID(id)
        {
            return _parent + " ." + id;
        }

        function _getClassDiv(id, element)
        {
            if (element == undefined) {
                element = "";
            }

            return '
' + element + '
';
        }

        function _getClassHref(id, event, element)
        {
            if (event == undefined) {
                event = "";
            } else {
                event = " " + event;
            }

            if (element == undefined) {
                element = "";
            }

            return '' + element + '';
        }

        function _getBlankDayMarkup()
        {
            return _getClassDiv(M_D_CLASS + ' ' + MONTH_DAY_BLANK_CLASS, _getClassDiv(MONTH_DAY_NUMBER_CLASS));
        }

        function _getWeekNames()
        {
            return ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
        }

        function _getMonthNames()
        {
            return ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
        }

        function _isLeapYear(year)
        {
            return !(year & 3) && ((year % 25) || !(year & 15))
        }

        function _getDaysOfMonth(year, month)
        {
            if (month === 2) {
                return _isLeapYear(year) ? 29 : 28;
            }

            return 30 + ((month + (month >> 3)) & 1);
        }

        function _isPastDay(year, month, day)
        {
            if (year < _currentYear) {
                return true;
            } else if (year > _currentYear) {
                return false;
            }

            if (month < _currentMonth) {
                return true;
            } else if (month > _currentMonth) {
                return false;
            }

            return day < _currentDay;
        }

        function _createAttr(name, value)
        {
            var attrMap = {
                "'": "'",
                "\"": """,
                "<": "<",
                ">": ">"
            };

            var parseValue = String(value);
            var newValue = "";
            for (var index = 0; index < parseValue.length; index++) {
                if (parseValue[index] in attrMap) {
                    newValue += attrMap[parseValue[index]];
                } else {
                    newValue += parseValue[index];
                }
            }

            return " " + name + "=\"" + newValue + "\"";
        }

        function _setData(name, data)
        {
            $(_parent).data(name, data);
        }

        function _getData(name)
        {
            return $(_parent).data(name);
        }

        ///////// Element Operators /////////

        function _initMonth()
        {
            _setMonth(_currentMonth, _currentYear);
        }

        function _appendMonthBody()
        {
            var weekNameElement = "";
            _weekNames.forEach(name => {
                weekNameElement += "
" + name + "
";
            });

            $(_parent).append(_getClassDiv(WEEK_TITLE_WRAP_CLASS, weekNameElement) + _getClassDiv(MONTH_DAY_WRAP_CLASS));
        }

        function _addCalendarHeader()
        {
            $(_parent).prepend(_getClassDiv(CALENDAR_HEADER_CLASS,
                                            _getClassDiv(CALENDAR_HEADER_TITLE_CLASS, _getClassHref(CALENDAR_HEADER_TITLE_DATE_CLASS, 'onclick="return false"')) +
                                            _getClassDiv(MONTH_PREV_CLASS) +
                                            _getClassDiv(MONTH_NEXT_CLASS)));
        }

        function _appendEventList()
        {
            $(_parent).append(_getClassDiv(CALENDAR_EVENT_LIST_CLASS));
        }

        function _prependBlankDays(count)
        {
            var wrapperElement = $(_getClassID(MONTH_DAY_WRAP_CLASS));
            for (var index = 0; index < count; ++index) {
                wrapperElement.prepend(_getBlankDayMarkup());
            }
        }

        function _appendBlankDays(count)
        {
            var wrapperElement = $(_getClassID(MONTH_DAY_WRAP_CLASS));
            for (var index = 0; index < count; ++index) {
                wrapperElement.append(_getBlankDayMarkup());
            }
        }

        function _cleanMonth()
        {
            $(_getClassID(CALENDAR_EVENT_LIST_CLASS)).empty();
            $(_getClassID(MONTH_DAY_WRAP_CLASS)).empty();
        }

        function _fillBlankDays(year, month)
        {
            var theFirstDayOfMonth = new Date(year, month - 1, 1, 0, 0, 0, 0).getDay();
            var blankDaysAhead = theFirstDayOfMonth;
            _prependBlankDays(blankDaysAhead);

            var totalDays = blankDaysAhead + _getDaysOfMonth(year, month);
            if (totalDays % 7 !== 0) {
                var blankDaysAfter = Math.ceil((totalDays) / 7) * 7 - totalDays;
                _appendBlankDays(blankDaysAfter);
            }
        }

        function _groupDaysIntoWeeks()
        {
            var divs = $(_getClassID(M_D_CLASS));
            for (var index = 0; index < divs.length; index += 7) {
                divs.slice(index, index + 7).wrapAll(_getClassDiv(MONTH_WEEK_CLASS));
            }
        }

        function _createMonthHeader(year, month)
        {
            var isCurrentMonth = (month === _currentMonth) && (year === _currentYear);
            if (isCurrentMonth) {
                $(_parent + ' *[data-number="' + _currentDay + '"]').addClass(MONTH_DAY_TODAY_CLASS);
            }

            var resetMarkup = isCurrentMonth ? "" : _getClassHref(MONTH_RESET_CLASS);
            $(_getClassID(CALENDAR_HEADER_TITLE_CLASS)).html(_getClassHref(CALENDAR_HEADER_TITLE_DATE_CLASS,
                                                                           'onclick="return false"',
                                                                           _monthNames[month - 1] + " " + year) +
                                                             resetMarkup);
        }

        function _createDayInMonth(year, month, day)
        {
            var innerMarkup = _getClassDiv(MONTH_DAY_NUMBER_CLASS, day) + _getClassDiv(MONTH_DAY_INDICATOR_WRAP_CLASS);
            var pastDayMarkup = _isPastDay(year, month, day) ? " " + MONTH_PAST_DAY_CLASS : "";
            var thisDate = new Date(year, month - 1, day, 0, 0, 0, 0);
            var dateStr = thisDate.toISOString().slice(0, 10);
            $(_getClassID(MONTH_DAY_WRAP_CLASS)).append("
" +
                                                        innerMarkup +
                                                        "
");
            $(_getClassID(CALENDAR_EVENT_LIST_CLASS)).append("
" +
                                                             _getClassDiv(CALENDAR_EVENT_LIST_DATE_CLASS, _weekNames[thisDate.getDay()] + "
" + day) +
                                                             "
");
        }

        function _setMonth(month, year)
        {
            _setData(MONTH_DATA, month);
            _setData(YEAR_DATA, year);

            _cleanMonth();

            var dayNumbersInMonth = _getDaysOfMonth(month, year);
            for (var dayIter = 1; dayIter <= dayNumbersInMonth; ++dayIter) {
                _createDayInMonth(year, month, dayIter);
            }

            _createMonthHeader(year, month);
            _fillBlankDays(year, month);
            _groupDaysIntoWeeks();
        }

        ///////// Handlers /////////

        function _registerHandler()
        {
            $(document.body).on("click", _getClassID(MONTH_NEXT_CLASS), function (event) {
                _hideEventList();
                _setNextMonth();
                _updateMonthViewButton();
                event.preventDefault();
            });

            $(document.body).on("click", _getClassID(MONTH_PREV_CLASS), function (event) {
                _hideEventList();
                _setPreviousMonth();
                _updateMonthViewButton();
                event.preventDefault();
            });

            $(document.body).on("click", _getClassID(MONTH_RESET_CLASS), function (event) {
                _hideEventList();
                _updateMonthViewButton();
                _setMonth(_currentMonth, _currentYear);
                event.preventDefault();
                event.stopPropagation();
            });

            $(document.body).on("click", _getClassID(MONTH_VIEW_BUTTON_CLASS), function (event) {
                _hideEventList();
                _updateMonthViewButton();
                event.preventDefault();
            });

            $(document.body).on("click touchstart", _getClassID(MONTH_DAY_CLASS), function (event) {
                // Return data-number attribute in element
                var selectedDay = $(this).data("number");

                _showEventList(selectedDay);
                _updateMonthViewButton();

                event.preventDefault();
            });
        }

        function _showEventList(selectedDay)
        {
            var eventListElement = $(_getClassID(CALENDAR_EVENT_LIST_CLASS));
            if (eventListElement.is(":visible")) {
                return;
            }

            var monthDayWrapElement = $(_getClassID(MONTH_DAY_WRAP_CLASS));
            var width = monthDayWrapElement.width();
            var height = monthDayWrapElement.height();
            var offset = monthDayWrapElement.offset();

            eventListElement.show();
            eventListElement.css("transform", "scale(1)");
            eventListElement.css("width", width);
            eventListElement.css("height", height);
            eventListElement.css("top", offset.top);
            eventListElement.css("left", offset.left);
            $(_parent + ' .' + MONTH_EVENT_LIST_ITEM_CLASS + '[data-number="' + selectedDay + '"]').show();
        }

        function _hideEventList()
        {
            var eventListElement = $(_getClassID(CALENDAR_EVENT_LIST_CLASS));
            if (!eventListElement.is(":visible")) {
                return;
            }

            eventListElement.css("transform", "scale(0)");
            eventListElement.hide();
            $(_getClassID(CALENDAR_EVENT_LIST_CLASS) + ' .' + MONTH_EVENT_LIST_ITEM_CLASS + '[style="display: block;"]').hide();
        }

        function _setNextMonth()
        {
            var    month = _getData(MONTH_DATA);
            var year = _getData(YEAR_DATA);
            var newMonth = month === 12 ? 1 : month + 1;
            var newYear = month === 12 ? year + 1 : year;
            _setMonth(newMonth, newYear);
        }

        function _setPreviousMonth()
        {
            var    month = _getData(MONTH_DATA);
            var year = _getData(YEAR_DATA);
            var newMonth = month === 1 ? 12 : month - 1;
            var newYear = month === 1 ? year - 1 : year;
            _setMonth(newMonth, newYear);
        }

        function _updateMonthViewButton()
        {
            $(_getClassID(MONTH_VIEW_BUTTON_CLASS)).remove();

            if ($(_getClassID(CALENDAR_EVENT_LIST_CLASS)).is(":visible")) {
                $(_getClassID(CALENDAR_HEADER_TITLE_CLASS)).prepend(_getClassHref(MONTH_VIEW_BUTTON_CLASS));
            }
        }
    };

    $.fn.extend({
        calendar: Impl
    });
} (jQuery));
<commit_msg>Introduce calendar UI control<commit_after>(function($) {
    "use strict";

    function Impl()
    {
        var currentDate = new Date();

        ///////// Const /////////

        var YEAR_DATA = "setYear";
        var MONTH_DATA = "setMonth";

        var CALENDAR_HEADER_CLASS = "calendar-header";
        var CALENDAR_HEADER_TITLE_CLASS = "calendar-header-title";
        var CALENDAR_HEADER_TITLE_DATE_CLASS = "calendar-header-title-date";

        var CALENDAR_EVENT_LIST_CLASS = "calendar-event-list";
        var CALENDAR_EVENT_LIST_DATE_CLASS = "calendar-event-list-date";

        var M_D_CLASS = "m-d";

        var MONTH_DAY_CLASS = "month-day";
        var MONTH_DAY_BLANK_CLASS = "month-day-blank";
        var MONTH_DAY_EVENT_CLASS = "month-day-event";
        var MONTH_DAY_WRAP_CLASS = "month-day-wrap";
        var MONTH_DAY_NUMBER_CLASS = "month-day-number";
        var MONTH_DAY_INDICATOR_WRAP_CLASS = "month-day-indicator-wrap";
        var MONTH_DAY_TODAY_CLASS = "month-day-today";
        var MONTH_PAST_DAY_CLASS = "month-past-day";
        var MONTH_RESET_CLASS = "month-reset";
        var MONTH_WEEK_CLASS = "month-week";
        var MONTH_NEXT_CLASS = "month-next";
        var MONTH_PREV_CLASS = "month-prev";
        var MONTH_EVENT_LIST_ITEM_CLASS = "month-event-list-item";
        var MONTH_VIEW_BUTTON_CLASS = "month-view-button";

        var WEEK_TITLE_WRAP_CLASS = "week-title-wrap";

        ///////// Global Variable /////////

        var _uniqueId = $(this).attr("id");
        var _parent = "#" + _uniqueId;

        var _currentMonth = currentDate.getMonth() + 1;
        var _currentYear = currentDate.getFullYear();
        var _currentDay = currentDate.getDate();

        var _weekNames = _getWeekNames();
        var _monthNames = _getMonthNames();

        ///////// Init UI /////////

        _appendMonthBody();
        _addCalendarHeader();
        _appendEventList();
        _initMonth();
        _registerHandler();

        ///////// Utilities /////////

        function _getClassID(id)
        {
            return _parent + " ." + id;
        }

        function _getClassDiv(id, element)
        {
            if (element == undefined) {
                element = "";
            }

            return '
' + element + '
';
        }

        function _getClassHref(id, event, element)
        {
            if (event == undefined) {
                event = "";
            } else {
                event = " " + event;
            }

            if (element == undefined) {
                element = "";
            }

            return '' + element + '';
        }

        function _getBlankDayMarkup()
        {
            return _getClassDiv(M_D_CLASS + ' ' + MONTH_DAY_BLANK_CLASS, _getClassDiv(MONTH_DAY_NUMBER_CLASS));
        }

        function _getWeekNames()
        {
            return ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
        }

        function _getMonthNames()
        {
            return ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
        }

        function _isLeapYear(year)
        {
            return !(year & 3) && ((year % 25) || !(year & 15))
        }

        function _getDaysOfMonth(year, month)
        {
            if (month === 2) {
                return _isLeapYear(year) ? 29 : 28;
            }

            return 30 + ((month + (month >> 3)) & 1);
        }

        function _isPastDay(year, month, day)
        {
            if (year < _currentYear) {
                return true;
            } else if (year > _currentYear) {
                return false;
            }

            if (month < _currentMonth) {
                return true;
            } else if (month > _currentMonth) {
                return false;
            }

            return day < _currentDay;
        }

        function _createAttr(name, value)
        {
            var attrMap = {
                "'": "'",
                "\"": """,
                "<": "<",
                ">": ">"
            };

            var parseValue = String(value);
            var newValue = "";
            for (var index = 0; index < parseValue.length; index++) {
                if (parseValue[index] in attrMap) {
                    newValue += attrMap[parseValue[index]];
                } else {
                    newValue += parseValue[index];
                }
            }

            return " " + name + "=\"" + newValue + "\"";
        }

        function _setData(name, data)
        {
            $(_parent).data(name, data);
        }

        function _getData(name)
        {
            return $(_parent).data(name);
        }

        ///////// Element Operators /////////

        function _initMonth()
        {
            _setMonth(_currentYear, _currentMonth);
        }

        function _appendMonthBody()
        {
            var weekNameElement = "";
            _weekNames.forEach(name => {
                weekNameElement += "
" + name + "
";
            });

            $(_parent).append(_getClassDiv(WEEK_TITLE_WRAP_CLASS, weekNameElement) + _getClassDiv(MONTH_DAY_WRAP_CLASS));
        }

        function _addCalendarHeader()
        {
            $(_parent).prepend(_getClassDiv(CALENDAR_HEADER_CLASS,
                                            _getClassDiv(CALENDAR_HEADER_TITLE_CLASS, _getClassHref(CALENDAR_HEADER_TITLE_DATE_CLASS, 'onclick="return false"')) +
                                            _getClassDiv(MONTH_PREV_CLASS) +
                                            _getClassDiv(MONTH_NEXT_CLASS)));
        }

        function _appendEventList()
        {
            $(_parent).append(_getClassDiv(CALENDAR_EVENT_LIST_CLASS));
        }

        function _prependBlankDays(count)
        {
            var wrapperElement = $(_getClassID(MONTH_DAY_WRAP_CLASS));
            for (var index = 0; index < count; ++index) {
                wrapperElement.prepend(_getBlankDayMarkup());
            }
        }

        function _appendBlankDays(count)
        {
            var wrapperElement = $(_getClassID(MONTH_DAY_WRAP_CLASS));
            for (var index = 0; index < count; ++index) {
                wrapperElement.append(_getBlankDayMarkup());
            }
        }

        function _cleanMonth()
        {
            $(_getClassID(CALENDAR_EVENT_LIST_CLASS)).empty();
            $(_getClassID(MONTH_DAY_WRAP_CLASS)).empty();
        }

        function _fillBlankDays(year, month)
        {
            var theFirstDayOfMonth = new Date(year, month - 1, 1, 0, 0, 0, 0).getDay();
            var blankDaysAhead = theFirstDayOfMonth;
            _prependBlankDays(blankDaysAhead);

            var totalDays = blankDaysAhead + _getDaysOfMonth(year, month);
            if (totalDays % 7 !== 0) {
                var blankDaysAfter = Math.ceil((totalDays) / 7) * 7 - totalDays;
                _appendBlankDays(blankDaysAfter);
            }
        }

        function _groupDaysIntoWeeks()
        {
            var divs = $(_getClassID(M_D_CLASS));
            for (var index = 0; index < divs.length; index += 7) {
                divs.slice(index, index + 7).wrapAll(_getClassDiv(MONTH_WEEK_CLASS));
            }
        }

        function _createMonthHeader(year, month)
        {
            var isCurrentMonth = (month === _currentMonth) && (year === _currentYear);
            if (isCurrentMonth) {
                $(_parent + ' *[data-number="' + _currentDay + '"]').addClass(MONTH_DAY_TODAY_CLASS);
            }

            var resetMarkup = isCurrentMonth ? "" : _getClassHref(MONTH_RESET_CLASS);
            $(_getClassID(CALENDAR_HEADER_TITLE_CLASS)).html(_getClassHref(CALENDAR_HEADER_TITLE_DATE_CLASS,
                                                                           'onclick="return false"',
                                                                           _monthNames[month - 1] + " " + year) +
                                                             resetMarkup);
        }

        function _createDayInMonth(year, month, day)
        {
            var innerMarkup = _getClassDiv(MONTH_DAY_NUMBER_CLASS, day) + _getClassDiv(MONTH_DAY_INDICATOR_WRAP_CLASS);
            var pastDayMarkup = _isPastDay(year, month, day) ? " " + MONTH_PAST_DAY_CLASS : "";
            var thisDate = new Date(year, month - 1, day, 0, 0, 0, 0);
            var dateStr = thisDate.toISOString().slice(0, 10);
            $(_getClassID(MONTH_DAY_WRAP_CLASS)).append("
" +
                                                        innerMarkup +
                                                        "
");
            $(_getClassID(CALENDAR_EVENT_LIST_CLASS)).append("
" +
                                                             _getClassDiv(CALENDAR_EVENT_LIST_DATE_CLASS, _weekNames[thisDate.getDay()] + "
" + day) +
                                                             "
");
        }

        function _setMonth(year, month)
        {
            _setData(MONTH_DATA, month);
            _setData(YEAR_DATA, year);

            _cleanMonth();

            var dayNumbersInMonth = _getDaysOfMonth(year, month);
            for (var dayIter = 1; dayIter <= dayNumbersInMonth; ++dayIter) {
                _createDayInMonth(year, month, dayIter);
            }

            _createMonthHeader(year, month);
            _fillBlankDays(year, month);
            _groupDaysIntoWeeks();
        }

        ///////// Handlers /////////

        function _registerHandler()
        {
            $(document.body).on("click", _getClassID(MONTH_NEXT_CLASS), function (event) {
                _hideEventList();
                _setNextMonth();
                _updateMonthViewButton();
                event.preventDefault();
            });

            $(document.body).on("click", _getClassID(MONTH_PREV_CLASS), function (event) {
                _hideEventList();
                _setPreviousMonth();
                _updateMonthViewButton();
                event.preventDefault();
            });

            $(document.body).on("click", _getClassID(MONTH_RESET_CLASS), function (event) {
                _hideEventList();
                _updateMonthViewButton();
                _setMonth(_currentYear, _currentMonth);
                event.preventDefault();
                event.stopPropagation();
            });

            $(document.body).on("click", _getClassID(MONTH_VIEW_BUTTON_CLASS), function (event) {
                _hideEventList();
                _updateMonthViewButton();
                event.preventDefault();
            });

            $(document.body).on("click touchstart", _getClassID(MONTH_DAY_CLASS), function (event) {
                // Return data-number attribute in element
                var selectedDay = $(this).data("number");

                _showEventList(selectedDay);
                _updateMonthViewButton();

                event.preventDefault();
            });
        }

        function _showEventList(selectedDay)
        {
            var eventListElement = $(_getClassID(CALENDAR_EVENT_LIST_CLASS));
            if (eventListElement.is(":visible")) {
                return;
            }

            var monthDayWrapElement = $(_getClassID(MONTH_DAY_WRAP_CLASS));
            var width = monthDayWrapElement.width();
            var height = monthDayWrapElement.height();
            var offset = monthDayWrapElement.offset();

            eventListElement.show();
            eventListElement.css("transform", "scale(1)");
            eventListElement.css("width", width);
            eventListElement.css("height", height);
            eventListElement.css("top", offset.top);
            eventListElement.css("left", offset.left);
            $(_parent + ' .' + MONTH_EVENT_LIST_ITEM_CLASS + '[data-number="' + selectedDay + '"]').show();
        }

        function _hideEventList()
        {
            var eventListElement = $(_getClassID(CALENDAR_EVENT_LIST_CLASS));
            if (!eventListElement.is(":visible")) {
                return;
            }

            eventListElement.css("transform", "scale(0)");
            eventListElement.hide();
            $(_getClassID(CALENDAR_EVENT_LIST_CLASS) + ' .' + MONTH_EVENT_LIST_ITEM_CLASS + '[style="display: block;"]').hide();
        }

        function _setNextMonth()
        {
            var    month = _getData(MONTH_DATA);
            var year = _getData(YEAR_DATA);
            var newMonth = month === 12 ? 1 : month + 1;
            var newYear = month === 12 ? year + 1 : year;
            _setMonth(newYear, newMonth);
        }

        function _setPreviousMonth()
        {
            var    month = _getData(MONTH_DATA);
            var year = _getData(YEAR_DATA);
            var newMonth = month === 1 ? 12 : month - 1;
            var newYear = month === 1 ? year - 1 : year;
            _setMonth(newYear, newMonth);
        }

        function _updateMonthViewButton()
        {
            $(_getClassID(MONTH_VIEW_BUTTON_CLASS)).remove();

            if ($(_getClassID(CALENDAR_EVENT_LIST_CLASS)).is(":visible")) {
                $(_getClassID(CALENDAR_HEADER_TITLE_CLASS)).prepend(_getClassHref(MONTH_VIEW_BUTTON_CLASS));
            }
        }
    };

    $.fn.extend({
        calendar: Impl
    });
} (jQuery));
<|endoftext|>


<commit_before>TXE.Views.HomeView = Backbone.View.extend({

  template: _.template(TXE.Templates.projectsTopNavListTemplate),

  collection: new TXE.Collections.Projects(),

  initialize: function(){
    this.collection.bind('reset', this.render, this).fetch();
  },

  render: function(){
    console.log(this.collection);
    var template = this.template;
    _.each(this.collection.models,function(model){
      var project = template(model.toJSON());
      $('.dropdown').append(project);
    });
  }
});
<commit_msg>removed console<commit_after>TXE.Views.HomeView = Backbone.View.extend({

  template: _.template(TXE.Templates.projectsTopNavListTemplate),

  collection: new TXE.Collections.Projects(),

  initialize: function(){
    this.collection.bind('reset', this.render, this).fetch();
  },

  render: function(){
    var template = this.template;
    _.each(this.collection.models,function(model){
      var project = template(model.toJSON());
      $('.dropdown').append(project);
    });
  }
});
<|endoftext|>


<commit_before>function update () {
    var out = {
        "score":"",
        "overs":"",
        "lead":"",
        "striker_name":"",
        "striker_stats":"",
        "nonstriker_name":"",
        "nonstriker_stats":"",
        "bowler_name":"",
        "bowler_stats":""
    };
    var matchId = localStorage.getItem("matchId");
    if (!matchId) {
        out.striker_name = "No match selected";
        out.nonstriker_name = "in config.";
        Pebble.sendAppMessage(out);
        return;
    }
    var req = new XMLHttpRequest();
    req.open('GET', "http://www.espncricinfo.com/ci/engine/match/"+matchId+".json");
    req.onload = function(e) {
        if (req.readyState == 4) {
            if(req.status == 200) {
                console.log("got new data, updating");

                var data = JSON.parse(req.responseText);
                if (data) {

                    var teams = {};
                    var players = {};
                    data.team.forEach(function (team) {
                        teams[team.team_id] = team;
                        var playerList = team.player || team.squad;
                        playerList.forEach(function (player) {
                            players[player.player_id] = player;
                        });
                    });

                    var match_status = data.match.match_status || "complete";

                    console.log("match status: "+match_status);

                    switch (match_status) {
                        case "dormant":
                            out.score = data.match.team1_filename+" v "+data.match.team2_filename;
                            out.lead = "Match starts in "+data.match.match_clock;

                            if (data.match.toss_decision && data.match.toss_decision !== "") {
                                out.striker_name = data.match["team"+data.match.toss_winner_team_id+"_short_name"]+" won toss,";
                                out.nonstriker_name = "will "+data.match.toss_decision_name;
                            }
                            break;

                        case "complete": {
                            out.score = data.match.team1_filename+" v "+data.match.team2_filename;

                            if (data.match.winner_team_id == "0") {
                                out.striker_name = "Match drawn";
                            }
                            else {
                                out.striker_name = teams[data.match.winner_team_id].team_short_name+" won by";
                                if (data.match.amount_name === "innings") {
                                    out.nonstriker_name = "innings and "+data.match.amount+" runs";
                                }
                                else {
                                    out.nonstriker_name = data.match.amount+" "+data.match.amount_name;
                                }
                            }
                            break;
                        }

                        case "current": {
                            var innings = data.live.innings;

                            out.score = [
                                teams[innings.batting_team_id].team_filename + " " + innings.runs,
                                (innings.wickets < 10) ? "/"+innings.wickets : "",
                                ((innings.event && innings.event == "declared") ? "d" : "")
                            ].join('');

                            out.overs = innings.overs;

                            if (+data.match.scheduled_overs > 0) {
                                switch (+innings.innings_number) {
                                    case 1:
                                        out.lead = "Run rate: "+innings.run_rate;
                                        break;
                                    default:
                                        out.lead =
                                            "Need "+(-innings.lead)+" in "+
                                            (innings.remaining_overs <= 10.0 ? innings.remaining_balls+" b" : innings.remaining_overs+" ov");
                                        break;
                                }
                            }
                            else {
                                switch (+innings.innings_number) {
                                    case 1:
                                        out.lead = "First innings";
                                        break;
                                    case 4:
                                        out.lead = "Target "+(innings.target);
                                        break;
                                    default:
                                        out.lead =
                                            innings.lead < 0 ? "Trail by "+(-innings.lead) :
                                            innings.lead > 0 ? "Lead by "+innings.lead :
                                                            "Scores level";
                                }
                            }

                            var striker = data.live.batting.filter(function (player) { return player.live_current_name == "striker"; })[0];
                            var nonstriker = data.live.batting.filter(function (player) { return player.live_current_name == "non-striker"; })[0];

                            if (!striker) {
                                striker = nonstriker;
                                nonstriker = null;
                            }

                            if (striker) {
                                var striker_name = players[striker.player_id].card_short;
                                if (striker_name.length > 10) striker_name = players[striker.player_id].popular_name;
                                out.striker_name = striker_name + (nonstriker ? "*" : "");
                                out.striker_stats = striker.runs+" ("+striker.balls_faced+")";
                            }

                            if (nonstriker) {
                                var nonstriker_name = players[nonstriker.player_id].card_short;
                                if (nonstriker_name.length > 10) nonstriker_name = players[nonstriker.player_id].popular_name;
                                out.nonstriker_name = nonstriker_name;
                                out.nonstriker_stats = nonstriker.runs+" ("+nonstriker.balls_faced+")";
                            }

                            var bowler = data.live.bowling.filter(function (player) { return player.live_current_name == "current bowler"; })[0];
                            if (bowler) {
                                var bowler_name = players[bowler.player_id].card_short;
                                if (bowler_name.length > 10) bowler_name = players[bowler.player_id].popular_name;
                                out.bowler_name = bowler_name;
                                out.bowler_stats = bowler.overs+"-"+bowler.maidens+"-"+bowler.conceded+"-"+bowler.wickets;
                            }

                            break;
                        }
                    }

                    console.log(JSON.stringify(out));
                    Pebble.sendAppMessage(out);
                }

            } else {
                console.log("Error");
            }
        }
    };
    req.send(null);
}

Pebble.addEventListener("ready", function (e) {
    console.log("connected, doing first update");
    update();
});

Pebble.addEventListener("appmessage", function (e) {
    console.log("tick, doing update");
    update();
});

Pebble.addEventListener("showConfiguration", function (e) {
    console.log("config requested");
    Pebble.openURL("http://eatenbyagrue.org/a/sixfour.psgi");
});

Pebble.addEventListener("webviewclosed", function (e) {
    console.log("config closed, response: "+e.response);
    if (e.response !== "CANCELLED") {
        localStorage.setItem("matchId", e.response);
        update();
    }
});
<commit_msg>fix off-by-one error in chase target output<commit_after>function update () {
    var out = {
        "score":"",
        "overs":"",
        "lead":"",
        "striker_name":"",
        "striker_stats":"",
        "nonstriker_name":"",
        "nonstriker_stats":"",
        "bowler_name":"",
        "bowler_stats":""
    };
    var matchId = localStorage.getItem("matchId");
    if (!matchId) {
        out.striker_name = "No match selected";
        out.nonstriker_name = "in config.";
        Pebble.sendAppMessage(out);
        return;
    }
    var req = new XMLHttpRequest();
    req.open('GET', "http://www.espncricinfo.com/ci/engine/match/"+matchId+".json");
    req.onload = function(e) {
        if (req.readyState == 4) {
            if(req.status == 200) {
                console.log("got new data, updating");

                var data = JSON.parse(req.responseText);
                if (data) {

                    var teams = {};
                    var players = {};
                    data.team.forEach(function (team) {
                        teams[team.team_id] = team;
                        var playerList = team.player || team.squad;
                        playerList.forEach(function (player) {
                            players[player.player_id] = player;
                        });
                    });

                    var match_status = data.match.match_status || "complete";

                    console.log("match status: "+match_status);

                    switch (match_status) {
                        case "dormant":
                            out.score = data.match.team1_filename+" v "+data.match.team2_filename;
                            out.lead = "Match starts in "+data.match.match_clock;

                            if (data.match.toss_decision && data.match.toss_decision !== "") {
                                out.striker_name = data.match["team"+data.match.toss_winner_team_id+"_short_name"]+" won toss,";
                                out.nonstriker_name = "will "+data.match.toss_decision_name;
                            }
                            break;

                        case "complete": {
                            out.score = data.match.team1_filename+" v "+data.match.team2_filename;

                            if (data.match.winner_team_id == "0") {
                                out.striker_name = "Match drawn";
                            }
                            else {
                                out.striker_name = teams[data.match.winner_team_id].team_short_name+" won by";
                                if (data.match.amount_name === "innings") {
                                    out.nonstriker_name = "innings and "+data.match.amount+" runs";
                                }
                                else {
                                    out.nonstriker_name = data.match.amount+" "+data.match.amount_name;
                                }
                            }
                            break;
                        }

                        case "current": {
                            var innings = data.live.innings;

                            out.score = [
                                teams[innings.batting_team_id].team_filename + " " + innings.runs,
                                (innings.wickets < 10) ? "/"+innings.wickets : "",
                                ((innings.event && innings.event == "declared") ? "d" : "")
                            ].join('');

                            out.overs = innings.overs;

                            if (+data.match.scheduled_overs > 0) {
                                switch (+innings.innings_number) {
                                    case 1:
                                        out.lead = "Run rate: "+innings.run_rate;
                                        break;
                                    default:
                                        out.lead =
                                            "Need "+(1-innings.lead)+" in "+
                                            (innings.remaining_overs <= 10.0 ? innings.remaining_balls+" b" : innings.remaining_overs+" ov");
                                        break;
                                }
                            }
                            else {
                                switch (+innings.innings_number) {
                                    case 1:
                                        out.lead = "First innings";
                                        break;
                                    case 4:
                                        out.lead = "Target "+(innings.target);
                                        break;
                                    default:
                                        out.lead =
                                            innings.lead < 0 ? "Trail by "+(-innings.lead) :
                                            innings.lead > 0 ? "Lead by "+innings.lead :
                                                            "Scores level";
                                }
                            }

                            var striker = data.live.batting.filter(function (player) { return player.live_current_name == "striker"; })[0];
                            var nonstriker = data.live.batting.filter(function (player) { return player.live_current_name == "non-striker"; })[0];

                            if (!striker) {
                                striker = nonstriker;
                                nonstriker = null;
                            }

                            if (striker) {
                                var striker_name = players[striker.player_id].card_short;
                                if (striker_name.length > 10) striker_name = players[striker.player_id].popular_name;
                                out.striker_name = striker_name + (nonstriker ? "*" : "");
                                out.striker_stats = striker.runs+" ("+striker.balls_faced+")";
                            }

                            if (nonstriker) {
                                var nonstriker_name = players[nonstriker.player_id].card_short;
                                if (nonstriker_name.length > 10) nonstriker_name = players[nonstriker.player_id].popular_name;
                                out.nonstriker_name = nonstriker_name;
                                out.nonstriker_stats = nonstriker.runs+" ("+nonstriker.balls_faced+")";
                            }

                            var bowler = data.live.bowling.filter(function (player) { return player.live_current_name == "current bowler"; })[0];
                            if (bowler) {
                                var bowler_name = players[bowler.player_id].card_short;
                                if (bowler_name.length > 10) bowler_name = players[bowler.player_id].popular_name;
                                out.bowler_name = bowler_name;
                                out.bowler_stats = bowler.overs+"-"+bowler.maidens+"-"+bowler.conceded+"-"+bowler.wickets;
                            }

                            break;
                        }
                    }

                    console.log(JSON.stringify(out));
                    Pebble.sendAppMessage(out);
                }

            } else {
                console.log("Error");
            }
        }
    };
    req.send(null);
}

Pebble.addEventListener("ready", function (e) {
    console.log("connected, doing first update");
    update();
});

Pebble.addEventListener("appmessage", function (e) {
    console.log("tick, doing update");
    update();
});

Pebble.addEventListener("showConfiguration", function (e) {
    console.log("config requested");
    Pebble.openURL("http://eatenbyagrue.org/a/sixfour.psgi");
});

Pebble.addEventListener("webviewclosed", function (e) {
    console.log("config closed, response: "+e.response);
    if (e.response !== "CANCELLED") {
        localStorage.setItem("matchId", e.response);
        update();
    }
});
<|endoftext|>


<commit_before>/**
 * @constructor
 */
function CubeRenderLayer(layer) {
  this.config = layer.config;

/**Comment yo shit, ya'll
*/
  this.scene = new THREE.Scene();
  this.camera = new THREE.PerspectiveCamera(45, 16 / 9, 1, 100);

  this.top_material = new THREE.ShaderMaterial(SHADERS.topshader);
  this.wall_material = new THREE.ShaderMaterial(SHADERS.wallshader);
  this.ball_material = new THREE.ShaderMaterial(SHADERS.ballshader);
  this.ball_material2 = new THREE.ShaderMaterial(SHADERS.ballshader2);
  this.ball_material3 = new THREE.ShaderMaterial(SHADERS.ballshader3);

  var geometry = new THREE.BoxGeometry(3,3,3);
  var ball_material = new THREE.ShaderMaterial(SHADERS.topshader);
  var ball = new THREE.Mesh(geometry, ball_material);
  ball.position.set(4,0,6);

  this.ball = ball;

  this.generate_cubes();

  this.renderPass = new THREE.RenderPass(this.scene, this.camera);
}

CubeRenderLayer.prototype.generate_cubes = function() {
  var geometry = new THREE.BoxGeometry(3,3,3);
  var ball_material = new THREE.ShaderMaterial(SHADERS.topshader);
  this.cubes = [];

  this.cube_count = 2000;

  for(var i = 0; i < this.cube_count; i++) {
    var position = i/(3*16) - 10;
    var np = this.cubePositionFunction2(position);
    this.cubes.push(new THREE.Mesh(geometry, ball_material));
    this.cubes[i].position.set(np.x, np.y, np.z);
    this.scene.add(this.cubes[i]);

    // Draw derived helper lines
    var dnp = this.cubePositionDerived(position);
    this.cubes[i].rotation.set(dnp.x, dnp.y, dnp.z);
/*
    var linematerial = new THREE.LineBasicMaterial({
      color: 0x00ddff
    });

    var linegeometry = new THREE.Geometry();
    linegeometry.vertices.push(
      new THREE.Vector3( np.x, np.y, np.z ),
      new THREE.Vector3( np.x + dnp.x / 1, np.y + dnp.y / 1, np.z + dnp.z / 1 )
    );

    var line = new THREE.Line( linegeometry,linematerial );
    this.scene.add( line );

// Draw derived helper lines
    var x = 10;
    var y = 0;

    var pvnp = this.cubePositionPlaneVector(position, x, y);
    var linematerial2 = new THREE.LineBasicMaterial({
      color: 0xdd0000
    });

    var v90d = this.vector90degree(dnp);
    var linegeometry2 = new THREE.Geometry();
    linegeometry2.vertices.push(
      new THREE.Vector3( np.x, np.y, np.z ),
      //new THREE.Vector3( np.x + x / 3, np.y + y / 3, np.z + pvnp / 3 )
      new THREE.Vector3( np.x + v90d.x, np.y + v90d.y, np.z + v90d.z)
    );

    var line2 = new THREE.Line( linegeometry2, linematerial2 );
    this.scene.add( line2 );

    console.log(np);
    console.log(this.vector90degree(dnp)); */
  }
}

// Returns a point (x,y,z) at based on a position forming a continous line for continous input
CubeRenderLayer.prototype.cubePositionFunction2 = function(position, scale) {

  var core_potition = new THREE.Vector3(3 * Math.cos(position), 3 * Math.sin(position), position * 3);
  var cube_position = new THREE.Vector3(Math.cos(position * 16), Math.sin(position * 16), 0);
  cube_position.multiply(new THREE.Vector3(scale, scale, scale));
  return core_potition.add(cube_position);
  //return new THREE.Vector3(Math.cos(position), 0, position);
}

// Returns a point (x,y,z) at based on a position forming a continous line for continous input
CubeRenderLayer.prototype.cubePositionFunction = function(position) {
  return new THREE.Vector3(Math.cos(position), Math.sin(position), position);
  //return new THREE.Vector3(Math.cos(position), 0, position);
}

// Returns the derivate of cubePositionFunction.
CubeRenderLayer.prototype.cubePositionDerived = function(position) {
  return new THREE.Vector3(-Math.sin(position), Math.cos(position), 1);
}

CubeRenderLayer.prototype.vector90degree = function(vector) {
  var new_vector = new THREE.Vector3(vector.z, 0, -vector.x);
  var size = Math.sqrt(new_vector.x*new_vector.x + new_vector.y*new_vector.y + new_vector.z*new_vector.z);
  return new THREE.Vector3(new_vector.x / size, new_vector.y / size, new_vector.z / size);
}

// Returns a vector perpendicular to the line.
CubeRenderLayer.prototype.cubePositionPlaneVector = function(position, x, y) {

  var p = this.cubePositionFunction(position);
  var d = this.cubePositionDerived(position);

  var v = (p.x * d.x + p.y * d.y + p.z * d.z) - (d.x * x) - (d.y * y)

  return v;
}

CubeRenderLayer.prototype.getEffectComposerPass = function() {
  return this.renderPass;
};

CubeRenderLayer.prototype.start = function() {
};

CubeRenderLayer.prototype.end = function() {
};

CubeRenderLayer.prototype.update = function(frame) {
  this.camera.position.x = 30 * Math.sin(frame/200);
  this.camera.position.y = 0;
  this.camera.position.z = 30 * Math.cos(frame/200);;

  this.camera.lookAt(new THREE.Vector3(0,0,0));

  var scale = Math.sin(frame/100)+1
  this.ball.scale.set(scale, scale, scale);

  for(var i = 0; i < this.cube_count; i++) {
    var scale = Math.max( Math.pow(Math.sin(frame/100 + i/(this.cube_count * 0.3) * Math.PI * 2) + 1, 3) - 2.5, 0);
    scale /= 15;
    this.cubes[i].scale.set(scale, scale, scale);

    var position = i/(3*16) - 10;
    var np = this.cubePositionFunction2(position, scale * 3);
    this.cubes[i].position.set(np.x, np.y, np.z);
  }

  this.wall_material.uniforms.colorA.value = new THREE.Color(19 / 255, 18 / 255, 94 / 255);
  this.wall_material.uniforms.colorB.value = new THREE.Color(208 / 255, 225 / 255, 255 / 255);
  this.wall_material.uniforms.time.value = frame;

  this.ball_material.uniforms.time.value = frame;
  this.ball_material2.uniforms.time.value = frame;
  this.ball_material3.uniforms.time.value = frame;

  this.top_material.uniforms.tiles.value = 4;
  this.top_material.uniforms.time.value = frame;
};
<commit_msg>Improved resolution of cube renderer<commit_after>/**
 * @constructor
 */
function CubeRenderLayer(layer) {
  this.config = layer.config;

/**Comment yo shit, ya'll
*/
  this.scene = new THREE.Scene();
  this.camera = new THREE.PerspectiveCamera(45, 16 / 9, 1, 100);

  this.top_material = new THREE.ShaderMaterial(SHADERS.topshader);
  this.wall_material = new THREE.ShaderMaterial(SHADERS.wallshader);
  this.ball_material = new THREE.ShaderMaterial(SHADERS.ballshader);
  this.ball_material2 = new THREE.ShaderMaterial(SHADERS.ballshader2);
  this.ball_material3 = new THREE.ShaderMaterial(SHADERS.ballshader3);

  var geometry = new THREE.BoxGeometry(3,3,3);
  var ball_material = new THREE.ShaderMaterial(SHADERS.topshader);
  var ball = new THREE.Mesh(geometry, ball_material);
  ball.position.set(4,0,6);

  this.ball = ball;

  this.generate_cubes();

  this.renderPass = new THREE.RenderPass(this.scene, this.camera);
}

CubeRenderLayer.prototype.generate_cubes = function() {
  var geometry = new THREE.BoxGeometry(3,3,3);
  var ball_material = new THREE.ShaderMaterial(SHADERS.topshader);
  this.cubes = [];

  this.cube_count = 1000;

  for(var i = 0; i < this.cube_count; i++) {
    var position = i/(3*64) - 10;
    var np = this.cubePositionFunction2(position);
    this.cubes.push(new THREE.Mesh(geometry, ball_material));
    this.cubes[i].position.set(np.x, np.y, np.z);
    this.scene.add(this.cubes[i]);

    // Draw derived helper lines
    var dnp = this.cubePositionDerived(position);
    this.cubes[i].rotation.set(dnp.x, dnp.y, dnp.z);
/*
    var linematerial = new THREE.LineBasicMaterial({
      color: 0x00ddff
    });

    var linegeometry = new THREE.Geometry();
    linegeometry.vertices.push(
      new THREE.Vector3( np.x, np.y, np.z ),
      new THREE.Vector3( np.x + dnp.x / 1, np.y + dnp.y / 1, np.z + dnp.z / 1 )
    );

    var line = new THREE.Line( linegeometry,linematerial );
    this.scene.add( line );

// Draw derived helper lines
    var x = 10;
    var y = 0;

    var pvnp = this.cubePositionPlaneVector(position, x, y);
    var linematerial2 = new THREE.LineBasicMaterial({
      color: 0xdd0000
    });

    var v90d = this.vector90degree(dnp);
    var linegeometry2 = new THREE.Geometry();
    linegeometry2.vertices.push(
      new THREE.Vector3( np.x, np.y, np.z ),
      //new THREE.Vector3( np.x + x / 3, np.y + y / 3, np.z + pvnp / 3 )
      new THREE.Vector3( np.x + v90d.x, np.y + v90d.y, np.z + v90d.z)
    );

    var line2 = new THREE.Line( linegeometry2, linematerial2 );
    this.scene.add( line2 );

    console.log(np);
    console.log(this.vector90degree(dnp)); */
  }
}

// Returns a point (x,y,z) at based on a position forming a continous line for continous input
CubeRenderLayer.prototype.cubePositionFunction2 = function(position, scale) {

  var core_potition = new THREE.Vector3(3 * Math.cos(position), 3 * Math.sin(position), position * 3);
  var cube_position = new THREE.Vector3(Math.cos(position * 64), Math.sin(position * 64), 0);
  cube_position.multiply(new THREE.Vector3(scale, scale, scale));
  return core_potition.add(cube_position);
  //return new THREE.Vector3(Math.cos(position), 0, position);
}

// Returns a point (x,y,z) at based on a position forming a continous line for continous input
CubeRenderLayer.prototype.cubePositionFunction = function(position) {
  return new THREE.Vector3(Math.cos(position), Math.sin(position), position);
  //return new THREE.Vector3(Math.cos(position), 0, position);
}

// Returns the derivate of cubePositionFunction.
CubeRenderLayer.prototype.cubePositionDerived = function(position) {
  return new THREE.Vector3(-Math.sin(position), Math.cos(position), 1);
}

CubeRenderLayer.prototype.vector90degree = function(vector) {
  var new_vector = new THREE.Vector3(vector.z, 0, -vector.x);
  var size = Math.sqrt(new_vector.x*new_vector.x + new_vector.y*new_vector.y + new_vector.z*new_vector.z);
  return new THREE.Vector3(new_vector.x / size, new_vector.y / size, new_vector.z / size);
}

// Returns a vector perpendicular to the line.
CubeRenderLayer.prototype.cubePositionPlaneVector = function(position, x, y) {

  var p = this.cubePositionFunction(position);
  var d = this.cubePositionDerived(position);

  var v = (p.x * d.x + p.y * d.y + p.z * d.z) - (d.x * x) - (d.y * y)

  return v;
}

CubeRenderLayer.prototype.getEffectComposerPass = function() {
  return this.renderPass;
};

CubeRenderLayer.prototype.start = function() {
};

CubeRenderLayer.prototype.end = function() {
};

CubeRenderLayer.prototype.update = function(frame) {
  /*this.camera.position.x = 30 * Math.sin(frame/200);
  this.camera.position.y = 0;
  this.camera.position.z = 30 * Math.cos(frame/200);;

  this.camera.lookAt(new THREE.Vector3(0,0,0));
*/
  var scale = Math.sin(frame/100)+1
  this.ball.scale.set(scale, scale, scale);

  for(var i = 0; i < this.cube_count; i++) {
    var scale = Math.max( Math.pow(Math.sin(frame/100 + i/(this.cube_count * 1.3) * Math.PI * 2) + 1, 3) - 0.5, 0);
    scale /= 15 * 3;
    this.cubes[i].scale.set(scale, scale, scale);

    var position = i/(3*64) - 10;
    var np = this.cubePositionFunction2(position, scale * 3 * 3 );
    this.cubes[i].position.set(np.x, np.y, np.z);
  }

  this.wall_material.uniforms.colorA.value = new THREE.Color(19 / 255, 18 / 255, 94 / 255);
  this.wall_material.uniforms.colorB.value = new THREE.Color(208 / 255, 225 / 255, 255 / 255);
  this.wall_material.uniforms.time.value = frame;

  this.ball_material.uniforms.time.value = frame;
  this.ball_material2.uniforms.time.value = frame;
  this.ball_material3.uniforms.time.value = frame;

  this.top_material.uniforms.tiles.value = 4;
  this.top_material.uniforms.time.value = frame;
};
<|endoftext|>


<commit_before>'use strict';

import React, { Component } from 'react';
import {
  StyleSheet,
  Text,
  TouchableOpacity
} from 'react-native';
import {
  View,
  Icon,
  Button
} from 'native-base';

export class LetterCard extends Component {
  constructor(props) {
    super(props);

    this.editItem = props.editItem.bind(this);
  };

  editItem(idx) {
    alert(idx);
  };

  dateToStr(d, t) {
    let nd = new Date(d);
    if (t) {
      return '' + nd.getHours() + ' : ' + nd.getMinutes() + ' on ' + nd.getDate() + ' / ' + (nd.getMonth() + 1) + ' / ' + nd.getFullYear();
    }
    return '' + nd.getDate() + ' / ' + (nd.getMonth() + 1) + ' / ' + nd.getFullYear();
  };

  render() {
    let { letterId,
      serialNo,
      counterNo,
      sentTo,
      sentOn,
      subject,
      replyBy,
      important,
      status,
      updatedAt } = this.props.item;

    let replyByStr = new Date()
    return (
      <TouchableOpacity
        onPress={() => this.editItem(letterId)}
        activeOpacity={0.7}
        style={styles.touchContainer}>
        <View style={[styles.content, {borderLeftColor: important ? '#E74C3C' : '#FFFFFF'}]}>
          <View style={{flexDirection: 'row'}}>
            <View style={styles.label}>
              <Text style={styles.labelText}>Serial No.</Text>
            </View>
            <View style={styles.value}>
              <Text>{serialNo}</Text>
            </View>
          </View>
          <View style={{flexDirection: 'row'}}>
            <View style={styles.label}>
              <Text style={styles.labelText}>Counter No.</Text>
            </View>
            <View style={styles.value}>
              <Text>{counterNo}</Text>
            </View>
          </View>
          <View style={{flexDirection: 'row'}}>
            <View style={styles.label}>
              <Text style={styles.labelText}>Sent To</Text>
            </View>
            <View style={styles.value}>
              <Text>{sentTo}</Text>
            </View>
          </View>
          <View style={{flexDirection: 'row'}}>
            <View style={styles.label}>
              <Text style={styles.labelText}>Sent On</Text>
            </View>
            <View style={styles.value}>
              <Text>{this.dateToStr(sentOn)}</Text>
            </View>
          </View>
          <View style={{flexDirection: 'row'}}>
            <View style={styles.label}>
              <Text style={styles.labelText}>Reply By</Text>
            </View>
            <View style={styles.value}>
              <Text>{this.dateToStr(replyBy)}</Text>
            </View>
          </View>
          <View style={{flexDirection: 'row'}}>
            <View style={styles.label}>
              <Text style={styles.labelText}>Subject</Text>
            </View>
            <View style={styles.value}>
              <Text>{subject}</Text>
            </View>
          </View>
          <View style={{flexDirection: 'row'}}>
            <View style={styles.label}>
              <Text style={styles.labelText}>Updated At</Text>
            </View>
            <View style={styles.value}>
              <Text>{this.dateToStr(updatedAt, true)}</Text>
            </View>
          </View>
        </View>
      </TouchableOpacity>
    );
  };
};

const styles = StyleSheet.create({
  touchContainer: {
    backgroundColor: '#fff',
    borderBottomWidth: 1,
    borderBottomColor: '#00000022',
  },
  content: {
    flex: 1,
    flexDirection: 'column',
    justifyContent: 'space-between',
    borderWidth: 5,
    borderColor: '#FFFFFF',
    padding: 5,
  },
  label: {
    width: 90,
    paddingBottom: 5,
  },
  labelText: {
    fontSize: 15
  },
  value: {
    flex: 1,
    paddingBottom: 5,
  }
});
<commit_msg>minor changes to letter display<commit_after>'use strict';

import React, { Component } from 'react';
import {
  StyleSheet,
  Text,
  TouchableOpacity
} from 'react-native';
import {
  View,
  Icon,
  Button
} from 'native-base';

export class LetterCard extends Component {
  constructor(props) {
    super(props);

    this.editItem = props.editItem.bind(this);
  };

  editItem(idx) {
    alert(idx);
  };

  dateToStr(d, t) {
    let nd = new Date(d);
    if (t) {
      return '' + nd.getHours() + ' : ' + nd.getMinutes() + '  On  ' + nd.getDate() + ' / ' + (nd.getMonth() + 1) + ' / ' + nd.getFullYear();
    }
    return '' + nd.getDate() + ' / ' + (nd.getMonth() + 1) + ' / ' + nd.getFullYear();
  };

  render() {
    let { letterId,
      serialNo,
      counterNo,
      sentTo,
      sentOn,
      subject,
      replyBy,
      important,
      status,
      updatedAt } = this.props.item;

    let replyByStr = new Date()
    return (
      <TouchableOpacity
        onPress={() => this.editItem(letterId)}
        activeOpacity={0.7}
        style={styles.touchContainer}>
        <View style={[styles.content, {borderLeftColor: important ? '#E74C3C' : '#FFFFFF'}]}>
          <View style={{flexDirection: 'row', borderBottomWidth: 0.5, borderBottomColor: '#00000011'}}>
            <View style={styles.label}>
              <Text style={styles.labelText}>Serial No.</Text>
            </View>
            <View style={styles.value}>
              <Text style={styles.labelValue}>{serialNo}</Text>
            </View>
          </View>
          <View style={{flexDirection: 'row', borderBottomWidth: 0.5, borderBottomColor: '#00000011'}}>
            <View style={styles.label}>
              <Text style={styles.labelText}>Counter No.</Text>
            </View>
            <View style={styles.value}>
              <Text style={styles.labelValue}>{counterNo}</Text>
            </View>
          </View>
          <View style={{flexDirection: 'row', borderBottomWidth: 0.5, borderBottomColor: '#00000011'}}>
            <View style={styles.label}>
              <Text style={styles.labelText}>Sent To</Text>
            </View>
            <View style={styles.value}>
              <Text style={styles.labelValue}>{sentTo}</Text>
            </View>
          </View>
          <View style={{flexDirection: 'row', borderBottomWidth: 0.5, borderBottomColor: '#00000011'}}>
            <View style={styles.label}>
              <Text style={styles.labelText}>Sent On</Text>
            </View>
            <View style={styles.value}>
              <Text style={styles.labelValue}>{this.dateToStr(sentOn)}</Text>
            </View>
          </View>
          <View style={{flexDirection: 'row', borderBottomWidth: 0.5, borderBottomColor: '#00000011'}}>
            <View style={styles.label}>
              <Text style={styles.labelText}>Reply By</Text>
            </View>
            <View style={styles.value}>
              <Text style={styles.labelValue}>{this.dateToStr(replyBy)}</Text>
            </View>
          </View>
          <View style={{flexDirection: 'row', borderBottomWidth: 0.5, borderBottomColor: '#00000011'}}>
            <View style={styles.label}>
              <Text style={styles.labelText}>Subject</Text>
            </View>
            <View style={styles.value}>
              <Text style={styles.labelValue}>{subject}</Text>
            </View>
          </View>
          <View style={{flexDirection: 'row'}}>
            <View style={styles.label}>
              <Text style={styles.labelText}>Updated At</Text>
            </View>
            <View style={styles.value}>
              <Text style={styles.labelValue}>{this.dateToStr(updatedAt, true)}</Text>
            </View>
          </View>
        </View>
      </TouchableOpacity>
    );
  };
};

const styles = StyleSheet.create({
  touchContainer: {
    backgroundColor: '#FFFFFF',
    borderBottomWidth: 1,
    borderBottomColor: '#00000022',
  },
  content: {
    flex: 1,
    flexDirection: 'column',
    justifyContent: 'space-between',
    borderWidth: 5,
    borderColor: '#FFFFFF',
    padding: 5,
  },
  label: {
    width: 105,
    marginTop: 10,
    marginBottom: 10,
    borderRightWidth: 0.5,
    borderRightColor: '#00000011'
  },
  labelText: {
    fontSize: 18,
    color: '#000000',
  },
  value: {
    flex: 1,
    justifyContent: 'center',
    marginLeft: 10,
  },
  labelValue: {
    fontSize: 18,
  },
});
<|endoftext|>


<commit_before>angular.module('ngGooglemaps', [])
  .directive('ngGooglemaps', function() {

    var isValidPosition = function(pos) {
      var isValid = true;
      if(!pos) isValid = false;
      if(!pos.lat || !angular.isNumber(pos.lat)) isValid = false;
      if(!pos.lng || !angular.isNumber(pos.lng)) isValid = false;
      return isValid;
    };

    return {
      scope: {
        startLat   : '=',
        startLng   : '=',
        zoom       : '=',
        positions  : '=',
        disableDefaultUi : '='
      },
      link : function($scope, $element, $attrs) {
        var start_lat         = $scope.startLat;
        var start_lng         = $scope.startLng;
        var zoom              = $scope.zoom;
        var positions         = $scope.positions;
        var disableDefaultUi  = $scope.disableDefaultUi
        var markers           = [];

        $element.addClass('map-canvas');

        var mapElement = $element[0];
        var mapOptions = {
          center: new google.maps.LatLng(start_lat, start_lng),
          zoom: zoom,
          disableDefaultUI: disableDefaultUi || false
        };

        var map = new google.maps.Map(mapElement, mapOptions);

        var setMap = function(marker) {
          markers.push(marker);
          marker.setMap(map);
        };

        var createMarker = function(pos) {
          var lat_lng = new google.maps.LatLng(pos.lat, pos.lng);
          var mark = new google.maps.Marker({
            position: lat_lng
          });
          setMap(mark);
          return mark;
        };

        var updateMarkers = function(positions) {

          cleanMarkers();

          var isArray   =   angular.isArray(positions);
          var multiple  =   isArray && positions.length > 1

          if(multiple) {
            angular.forEach(positions, function(position){
              createMarker(position);
            });

            var bounds = new google.maps.LatLngBounds();
            angular.forEach(markers, function(marker) {
              bounds.extend(marker.getPosition());
            });
            map.setCenter(bounds.getCenter());
            map.fitBounds(bounds);

          } else {
            var marker;
            var position = isArray ? positions[0] : positions;
            if(isValidPosition(position)) {
              var mark = createMarker(position);
              map.setCenter(mark.position);
              map.setZoom(zoom);
            }
          }
        };

        var cleanMarkers = function() {
          angular.forEach(markers, function(marker, i) {
            marker.setMap(null);
          });
          markers = [];
        };

        var watch = function(next, error) {
          return function(n,o) {
            if(n) next(n);
            else error();
          };
        };

        $scope.$watch('positions', watch(updateMarkers, cleanMarkers), true);
      }
    };
  })
  .factory('GoogleApi', function($injector) {
    var modules = ['GoogleGeolocation'];
    var api = {};
    angular.forEach(modules, function(m) {
      if($injector.has(m)) {
        var module = $injector.get(m);
        angular.extend(api, module);
      }
    });
    return api;
  });<commit_msg>1. removed console log<commit_after>angular.module('ngGooglemaps', [])
  .directive('ngGooglemaps', function() {

    var isValidPosition = function(pos) {
      var isValid = true;
      if(!pos) isValid = false;
      if(!pos.lat || !angular.isNumber(pos.lat)) isValid = false;
      if(!pos.lng || !angular.isNumber(pos.lng)) isValid = false;
      return isValid;
    };

    return {
      scope: {
        startLat   : '=',
        startLng   : '=',
        zoom       : '=',
        positions  : '=',
        disableDefaultUi : '='
      },
      link : function($scope, $element, $attrs) {
        var start_lat         = $scope.startLat;
        var start_lng         = $scope.startLng;
        var zoom              = $scope.zoom;
        var positions         = $scope.positions;
        var disableDefaultUi  = $scope.disableDefaultUi
        var markers           = [];

        $element.addClass('map-canvas');

        var mapElement = $element[0];
        var mapOptions = {
          center: new google.maps.LatLng(start_lat, start_lng),
          zoom: zoom,
          disableDefaultUI: disableDefaultUi || false
        };

        var map = new google.maps.Map(mapElement, mapOptions);

        var setMap = function(marker) {
          markers.push(marker);
          marker.setMap(map);
        };

        var createMarker = function(pos) {
          var lat_lng = new google.maps.LatLng(pos.lat, pos.lng);
          var mark = new google.maps.Marker({
            position: lat_lng
          });
          setMap(mark);
          return mark;
        };

        var updateMarkers = function(positions) {

          cleanMarkers();

          var isArray   =   angular.isArray(positions);
          var multiple  =   isArray && positions.length > 1

          if(multiple) {
            console.log(multiple);
            angular.forEach(positions, function(position){
              createMarker(position);
            });

            var bounds = new google.maps.LatLngBounds();
            angular.forEach(markers, function(marker) {
              bounds.extend(marker.getPosition());
            });
            map.setCenter(bounds.getCenter());
            map.fitBounds(bounds);

          } else {
            var marker;
            var position = isArray ? positions[0] : positions;
            if(isValidPosition(position)) {
              var mark = createMarker(position);
              map.setCenter(mark.position);
              map.setZoom(zoom);
            }
          }
        };

        var cleanMarkers = function() {
          angular.forEach(markers, function(marker, i) {
            marker.setMap(null);
          });
          markers = [];
        };

        var watch = function(next, error) {
          return function(n,o) {
            if(n) next(n);
            else error();
          };
        };

        $scope.$watch('positions', watch(updateMarkers, cleanMarkers), true);
      }
    };
  })
  .factory('GoogleApi', function($injector) {
    var modules = ['GoogleGeolocation'];
    var api = {};
    angular.forEach(modules, function(m) {
      if($injector.has(m)) {
        var module = $injector.get(m);
        angular.extend(api, module);
      }
    });
    return api;
  });<|endoftext|>


<commit_before>// ==UserScript==
// @name          Stack Exchange Global Flag Summary
// @namespace     http://floern.com/
// @version       0.5
// @description   Stack Exchange network wide flag summary available in your network profile
// @author        Floern
// @include       *://stackexchange.com/users/*/*
// @match         *://*.stackexchange.com/users/flag-summary/*
// @match         *://*.stackoverflow.com/users/flag-summary/*
// @match         *://*.superuser.com/users/flag-summary/*
// @match         *://*.serverfault.com/users/flag-summary/*
// @match         *://*.askubuntu.com/users/flag-summary/*
// @match         *://*.stackapps.com/users/flag-summary/*
// @match         *://*.mathoverflow.net/users/flag-summary/*
// @grant         GM_xmlhttpRequest
// @grant         GM_addStyle
// @run-at        document-end
// ==/UserScript==

let flagSummaryTable, flagSummaryTableBody;

let sortedColIndex = 2;
let sortedColAsc = false;

let flagGlobalSummaryStats = {
    sumFlagsTotal: 0,
    sumFlagsDeclined: 0,
    sumFlagsDisputed: 0,
    sumFlagsRetracted: 0,
    sumFlagsExpired: 0,
    sumFlagsPending: 0,
    sumFlagsHelpful: 0
};

// init
(function () {
    if (window.location.href.match(/\/users\/flag-summary\/\d+/i)) {
        showGlobalFlagSummaryLink();
        return;
    }

    if (!window.location.href.match(/:\/\/stackexchange\.com\/users\/\d+/i)) {
        return;
    }

    let navigation = document.querySelector('#content .contentWrapper .subheader');
    if (!navigation) {
        return;
    }

    let tabbar = navigation.querySelector('.tabs');

    // verify that we are in the profile of the logged in user
    let tabs = tabbar.getElementsByTagName('a');
    let loggedIn = false;
    for (var i = 0; i < tabs.length; i++) {
        if (tabs[i].textContent.trim().toLowerCase() == 'inbox') {
            loggedIn = true;
            break;
        }
    }
    if (!loggedIn) {
        return;
    }

    // add navigation tab for flags
    let flagTab = document.createElement('a');
    flagTab.setAttribute('href', '?tab=flags');
    flagTab.textContent = 'flags';
    tabs[4].parentNode.insertBefore(flagTab, tabs[4]);

    if (!window.location.href.match(/:\/\/stackexchange\.com\/users\/\d+\/.+?\?tab=flags/i)) {
        return;
    }

    // unselect default tab
    let selectedTab = navigation.querySelector('.youarehere');
    selectedTab.className = '';

    // set selected tab to flags
    flagTab.className = 'youarehere';

    // remove default content
    while (navigation.nextSibling) {
        navigation.parentNode.removeChild(navigation.nextSibling);
    }

    document.querySelector('title').textContent = 'Flag Summary - Stack Exchange';

    let container = document.createElement('div');
    navigation.parentNode.appendChild(container);

    // setup summary table
    flagSummaryTable = document.createElement('table');
    flagSummaryTable.id = 'flag-summary-table';
    flagSummaryTable.style.width = '100%';
    flagSummaryTable.style.textAlign = 'right';
    flagSummaryTable.style.borderCollapse = 'separate';
    flagSummaryTable.style.borderSpacing = '0 5px';
    flagSummaryTable.innerHTML = `

                Site
                helpful
                declined
                disputed
                expired
                retracted
                pending
                total
                helpful %
                last flag

    `;
    container.appendChild(flagSummaryTable);

    // make columns sortable
    let tableLabelNodes = flagSummaryTable.querySelectorAll('#flag-summary-heading-labels th');
    for (var i = 0; i < tableLabelNodes.length; i++) {
        (function(col) {
            tableLabelNodes[i].onclick = function() {
                sortedColAsc = col == sortedColIndex ? !sortedColAsc : false;
                sortTable(col, sortedColAsc);
            }
        })(i + 1);
    }

    flagSummaryTableBody = flagSummaryTable.getElementsByTagName('tbody')[0];

	// some table CSS
    GM_addStyle("#flag-summary-table tbody tr:hover { background: rgba(127, 127, 127, .15); }");
    GM_addStyle("#flag-summary-global-stats th { border-bottom: 1px #ddd solid; }");

	// init global flag summary
    updateGlobalFlagStats();

	// create loading view
    let loadingView = document.createElement("div");
    loadingView.id = 'flag-summary-loading';
    loadingView.style.textAlign = 'center';
    loadingView.innerHTML = '';
    container.appendChild(loadingView);

    // load data
    loadAccountList();
})();

/**
 * Add a link to the flag summary page.
 */
function showGlobalFlagSummaryLink() {
    let header = document.querySelector('#content .subheader');
    if (!header) {
        return;
    }

    // add link to header
    let segfsLink = document.createElement('a');
    segfsLink.setAttribute('href', '//stackexchange.com/users/current?tab=flags');
    segfsLink.textContent = 'Global Flag Summary';
    segfsLink.style.float = 'right';
    header.insertBefore(segfsLink, header.firstChild);
}

/**
 * Update global flag summary in header.
 */
function updateGlobalFlagStats() {
    let realTotal = flagGlobalSummaryStats.sumFlagsTotal - flagGlobalSummaryStats.sumFlagsPending -
        flagGlobalSummaryStats.sumFlagsRetracted - flagGlobalSummaryStats.sumFlagsExpired;
    let helpfulFraction = realTotal == 0 ? 0 : (flagGlobalSummaryStats.sumFlagsHelpful / realTotal);

    document.getElementById('flag-summary-global-stats').innerHTML = `

        ` + formatFlagCount(flagGlobalSummaryStats.sumFlagsHelpful) + `
        ` + formatFlagCount(flagGlobalSummaryStats.sumFlagsDeclined) + `
        ` + formatFlagCount(flagGlobalSummaryStats.sumFlagsDisputed) + `
        ` + formatFlagCount(flagGlobalSummaryStats.sumFlagsExpired) + `
        ` + formatFlagCount(flagGlobalSummaryStats.sumFlagsRetracted) + `
        ` + formatFlagCount(flagGlobalSummaryStats.sumFlagsPending) + `
        ` + formatFlagCount(flagGlobalSummaryStats.sumFlagsTotal) + `
        ` + formatFlagPercentage(helpfulFraction) + `%

    `;
}

/**
 * Load the network account list.
 */
function loadAccountList() {
    GM_xmlhttpRequest({
        method: 'GET',
        url: '//stackexchange.com/users/current?tab=accounts',
        onload: function(response) {
            parseNetworkAccounts(response.response);
        },
        onerror: function(response) {
            console.error('loadAccountList: ' + JSON.stringify(response));
        }
    });
}

/**
 * Parse the network account list.
 */
function parseNetworkAccounts(html) {
    let pageNode = document.createElement('div');
    pageNode.innerHTML = html;

    // iterate all accounts
    let accountNodes = pageNode.querySelectorAll('.account-container .account-site a');
    let i = -1;
    function loadNextSite() {
        i++;

        if (i >= accountNodes.length) {
            // end of list
            document.getElementById('flag-summary-loading').style.visibility = 'hidden';
            return;
        }

        if (accountNodes[i].href.indexOf('area51.stackexchange.com/') != -1) {
            // skip this weird abomination
            loadNextSite();
            return;
        }

        let siteName = accountNodes[i].textContent.trim();
        let siteUserFlagSummaryUrl = accountNodes[i].href.replace(/users\/(\d+)\/.*$/i, 'users/flag-summary/$1');

        loadSiteFlagSummary(siteName, siteUserFlagSummaryUrl, loadNextSite);
    };

    // start 3 'threads' in parallel
    loadNextSite();
    loadNextSite();
    loadNextSite();
}

/**
 * Load the flag summary of the specified site.
 */
function loadSiteFlagSummary(siteName, siteUserFlagSummaryUrl, finishedCallback) {
    GM_xmlhttpRequest({
        method: 'GET',
        url: siteUserFlagSummaryUrl,
        onload: function(response) {
            finishedCallback();
            parseSiteFlagSummary(siteName, siteUserFlagSummaryUrl, response.response);
        },
        onerror: function(response) {
            console.error('loadSiteFlagSummary: ' + JSON.stringify(response));
            finishedCallback();
        }
    });
}

/**
 * Parses the flag summary site and extracts the stats.
 */
function parseSiteFlagSummary(siteName, siteUserFlagSummaryUrl, html) {
    let pageNode = document.createElement('div');
    pageNode.innerHTML = html;

    let sumFlagsTotal = 0;
    let sumFlagsDeclined = 0;
    let sumFlagsDisputed = 0;
    let sumFlagsRetracted = 0;
    let sumFlagsExpired = 0;
    let sumFlagsPending = 0;
    let sumFlagsHelpful = 0;

    // search for flag stats
    let flagCountNodes = pageNode.querySelectorAll('#flag-stat-info-table > tbody > tr > td.col2 > a[href*="status="]');
    for (var i = 0; i < flagCountNodes.length; i++) {
        let flagType = parseInt(flagCountNodes[i].href.replace(/^.+?\bstatus=(\d+).*$/, '$1'));
        let flagCount = parseInt(previousElementSibling(flagCountNodes[i].parentElement).textContent.replace(/\D/g, ''));

        sumFlagsTotal += flagCount;

        switch (flagType) {
            case 1: // pending
                sumFlagsPending += flagCount; break;
            case 2: // helpful
                sumFlagsHelpful += flagCount; break;
            case 3: // declined
                sumFlagsDeclined += flagCount; break;
            case 4: // disputed
                sumFlagsDisputed += flagCount; break;
            case 5: // expired
                sumFlagsExpired += flagCount; break;
            case 6: // retracted
                sumFlagsRetracted += flagCount; break;
            default:
                console.error('parseSiteFlagSummary: unknown flag type #' + flagType);
                break;
        }
    }

    if (sumFlagsTotal == 0) {
        // skip site with no flags
        return;
    }

    // update global summary
    flagGlobalSummaryStats.sumFlagsTotal += sumFlagsTotal;
    flagGlobalSummaryStats.sumFlagsDeclined += sumFlagsDeclined;
    flagGlobalSummaryStats.sumFlagsDisputed += sumFlagsDisputed;
    flagGlobalSummaryStats.sumFlagsRetracted += sumFlagsRetracted;
    flagGlobalSummaryStats.sumFlagsExpired += sumFlagsExpired;
    flagGlobalSummaryStats.sumFlagsPending += sumFlagsPending;
    flagGlobalSummaryStats.sumFlagsHelpful += sumFlagsHelpful;

    updateGlobalFlagStats();

    // compute helpful percentage
    let realTotal = sumFlagsTotal - sumFlagsPending - sumFlagsRetracted - sumFlagsExpired;
    let helpfulFraction = realTotal == 0 ? 0 : (sumFlagsHelpful / realTotal);

	// get most recent flag date
    let flagHistoryDates = pageNode.querySelectorAll('.user-flag-history .mod-flag .relativetime');
	let mostRecentflagHistoryDateNode = flagHistoryDates[0];
	let lastFlagTimestamp = mostRecentflagHistoryDateNode.title;
	let lastFlagTimeDisplay = mostRecentflagHistoryDateNode.textContent.split('at')[0].trim();

    // get site icon
    let siteFaviconURL = pageNode.querySelector('link[rel*="icon"]').href;

    // create table row for this site
    let siteFlagSummaryTr = document.createElement('tr');
    siteFlagSummaryTr.innerHTML = `
        <img src="` + siteFaviconURL + `"
            style="width:16px;height:16px;vertical-align:middle" />
        <a href="` + siteUserFlagSummaryUrl + `">` + siteName + `
        ` + formatFlagCount(sumFlagsHelpful) + `
        ` + formatFlagCount(sumFlagsDeclined) + `
        ` + formatFlagCount(sumFlagsDisputed) + `
        ` + formatFlagCount(sumFlagsExpired) + `
        ` + formatFlagCount(sumFlagsRetracted) + `
        ` + formatFlagCount(sumFlagsPending) + `
        ` + formatFlagCount(sumFlagsTotal) + `
        ` + formatFlagPercentage(helpfulFraction) + `%
        <td style="color:#999" title="` + lastFlagTimestamp + `">` + lastFlagTimeDisplay + `
    `;
    flagSummaryTableBody.appendChild(siteFlagSummaryTr);

	// keep order
    sortTable(sortedColIndex, sortedColAsc);
}

/**
 * Format flag count, empty if 0.
 */
function formatFlagCount(flagCount) {
    if (flagCount == 0)
        return '';
    else
        return flagCount;
}

/**
 * Format helpful flag percentage.
 */
function formatFlagPercentage(fraction) {
    return (fraction * 100).toFixed(2);
}

/**
 * Find the previous non-text sibling node.
 */
function previousElementSibling(node) {
    do {
        node = node.previousSibling;
    } while (node && node.nodeType !== 1);
    return node;
}

/**
 * Sort the table.
 */
function sortTable(col, asc) {
    sortedColIndex = col;
    var trs = Array.prototype.slice.call(flagSummaryTableBody.rows, 0);
    asc = -((+asc) || -1);
    if (col == 1) {
        trs = trs.sort(function (a, b) {
            return asc * (a.cells[col].textContent.trim().localeCompare(b.cells[col].textContent.trim()));
        });
    }
	else if (col == 10) {
        trs = trs.sort(function (a, b) {
            return asc * (a.cells[col].title.localeCompare(b.cells[col].title));
        });
    }
    else {
        trs = trs.sort(function (a, b) {
            return asc
                * ((parseInt(b.cells[col].textContent.replace(/\D/g, '')) || 0) -
                (parseInt(a.cells[col].textContent.replace(/\D/g, '')) || 0) );
        });
    }
    for(var i = 0; i < trs.length; ++i) flagSummaryTableBody.appendChild(trs[i]);
}

<commit_msg>Add @connect statements to make Tampermonkey happy<commit_after>// ==UserScript==
// @name          Stack Exchange Global Flag Summary
// @namespace     http://floern.com/
// @version       0.5
// @description   Stack Exchange network wide flag summary available in your network profile
// @author        Floern
// @include       *://stackexchange.com/users/*/*
// @match         *://*.stackexchange.com/users/flag-summary/*
// @match         *://*.stackoverflow.com/users/flag-summary/*
// @match         *://*.superuser.com/users/flag-summary/*
// @match         *://*.serverfault.com/users/flag-summary/*
// @match         *://*.askubuntu.com/users/flag-summary/*
// @match         *://*.stackapps.com/users/flag-summary/*
// @match         *://*.mathoverflow.net/users/flag-summary/*
// @connect       stackexchange.com
// @connect       stackoverflow.com
// @connect       superuser.com
// @connect       serverfault.com
// @connect       askubuntu.com
// @connect       stackapps.com
// @connect       mathoverflow.net
// @grant         GM_xmlhttpRequest
// @grant         GM_addStyle
// @run-at        document-end
// ==/UserScript==

let flagSummaryTable, flagSummaryTableBody;

let sortedColIndex = 2;
let sortedColAsc = false;

let flagGlobalSummaryStats = {
    sumFlagsTotal: 0,
    sumFlagsDeclined: 0,
    sumFlagsDisputed: 0,
    sumFlagsRetracted: 0,
    sumFlagsExpired: 0,
    sumFlagsPending: 0,
    sumFlagsHelpful: 0
};

// init
(function () {
    if (window.location.href.match(/\/users\/flag-summary\/\d+/i)) {
        showGlobalFlagSummaryLink();
        return;
    }

    if (!window.location.href.match(/:\/\/stackexchange\.com\/users\/\d+/i)) {
        return;
    }

    let navigation = document.querySelector('#content .contentWrapper .subheader');
    if (!navigation) {
        return;
    }

    let tabbar = navigation.querySelector('.tabs');

    // verify that we are in the profile of the logged in user
    let tabs = tabbar.getElementsByTagName('a');
    let loggedIn = false;
    for (var i = 0; i < tabs.length; i++) {
        if (tabs[i].textContent.trim().toLowerCase() == 'inbox') {
            loggedIn = true;
            break;
        }
    }
    if (!loggedIn) {
        return;
    }

    // add navigation tab for flags
    let flagTab = document.createElement('a');
    flagTab.setAttribute('href', '?tab=flags');
    flagTab.textContent = 'flags';
    tabs[4].parentNode.insertBefore(flagTab, tabs[4]);

    if (!window.location.href.match(/:\/\/stackexchange\.com\/users\/\d+\/.+?\?tab=flags/i)) {
        return;
    }

    // unselect default tab
    let selectedTab = navigation.querySelector('.youarehere');
    selectedTab.className = '';

    // set selected tab to flags
    flagTab.className = 'youarehere';

    // remove default content
    while (navigation.nextSibling) {
        navigation.parentNode.removeChild(navigation.nextSibling);
    }

    document.querySelector('title').textContent = 'Flag Summary - Stack Exchange';

    let container = document.createElement('div');
    navigation.parentNode.appendChild(container);

    // setup summary table
    flagSummaryTable = document.createElement('table');
    flagSummaryTable.id = 'flag-summary-table';
    flagSummaryTable.style.width = '100%';
    flagSummaryTable.style.textAlign = 'right';
    flagSummaryTable.style.borderCollapse = 'separate';
    flagSummaryTable.style.borderSpacing = '0 5px';
    flagSummaryTable.innerHTML = `

                Site
                helpful
                declined
                disputed
                expired
                retracted
                pending
                total
                helpful %
                last flag

    `;
    container.appendChild(flagSummaryTable);

    // make columns sortable
    let tableLabelNodes = flagSummaryTable.querySelectorAll('#flag-summary-heading-labels th');
    for (var i = 0; i < tableLabelNodes.length; i++) {
        (function(col) {
            tableLabelNodes[i].onclick = function() {
                sortedColAsc = col == sortedColIndex ? !sortedColAsc : false;
                sortTable(col, sortedColAsc);
            }
        })(i + 1);
    }

    flagSummaryTableBody = flagSummaryTable.getElementsByTagName('tbody')[0];

	// some table CSS
    GM_addStyle("#flag-summary-table tbody tr:hover { background: rgba(127, 127, 127, .15); }");
    GM_addStyle("#flag-summary-global-stats th { border-bottom: 1px #ddd solid; }");

	// init global flag summary
    updateGlobalFlagStats();

	// create loading view
    let loadingView = document.createElement("div");
    loadingView.id = 'flag-summary-loading';
    loadingView.style.textAlign = 'center';
    loadingView.innerHTML = '';
    container.appendChild(loadingView);

    // load data
    loadAccountList();
})();

/**
 * Add a link to the flag summary page.
 */
function showGlobalFlagSummaryLink() {
    let header = document.querySelector('#content .subheader');
    if (!header) {
        return;
    }

    // add link to header
    let segfsLink = document.createElement('a');
    segfsLink.setAttribute('href', '//stackexchange.com/users/current?tab=flags');
    segfsLink.textContent = 'Global Flag Summary';
    segfsLink.style.float = 'right';
    header.insertBefore(segfsLink, header.firstChild);
}

/**
 * Update global flag summary in header.
 */
function updateGlobalFlagStats() {
    let realTotal = flagGlobalSummaryStats.sumFlagsTotal - flagGlobalSummaryStats.sumFlagsPending -
        flagGlobalSummaryStats.sumFlagsRetracted - flagGlobalSummaryStats.sumFlagsExpired;
    let helpfulFraction = realTotal == 0 ? 0 : (flagGlobalSummaryStats.sumFlagsHelpful / realTotal);

    document.getElementById('flag-summary-global-stats').innerHTML = `

        ` + formatFlagCount(flagGlobalSummaryStats.sumFlagsHelpful) + `
        ` + formatFlagCount(flagGlobalSummaryStats.sumFlagsDeclined) + `
        ` + formatFlagCount(flagGlobalSummaryStats.sumFlagsDisputed) + `
        ` + formatFlagCount(flagGlobalSummaryStats.sumFlagsExpired) + `
        ` + formatFlagCount(flagGlobalSummaryStats.sumFlagsRetracted) + `
        ` + formatFlagCount(flagGlobalSummaryStats.sumFlagsPending) + `
        ` + formatFlagCount(flagGlobalSummaryStats.sumFlagsTotal) + `
        ` + formatFlagPercentage(helpfulFraction) + `%

    `;
}

/**
 * Load the network account list.
 */
function loadAccountList() {
    GM_xmlhttpRequest({
        method: 'GET',
        url: '//stackexchange.com/users/current?tab=accounts',
        onload: function(response) {
            parseNetworkAccounts(response.response);
        },
        onerror: function(response) {
            console.error('loadAccountList: ' + JSON.stringify(response));
        }
    });
}

/**
 * Parse the network account list.
 */
function parseNetworkAccounts(html) {
    let pageNode = document.createElement('div');
    pageNode.innerHTML = html;

    // iterate all accounts
    let accountNodes = pageNode.querySelectorAll('.account-container .account-site a');
    let i = -1;
    function loadNextSite() {
        i++;

        if (i >= accountNodes.length) {
            // end of list
            document.getElementById('flag-summary-loading').style.visibility = 'hidden';
            return;
        }

        if (accountNodes[i].href.indexOf('area51.stackexchange.com/') != -1) {
            // skip this weird abomination
            loadNextSite();
            return;
        }

        let siteName = accountNodes[i].textContent.trim();
        let siteUserFlagSummaryUrl = accountNodes[i].href.replace(/users\/(\d+)\/.*$/i, 'users/flag-summary/$1');

        loadSiteFlagSummary(siteName, siteUserFlagSummaryUrl, loadNextSite);
    };

    // start 3 'threads' in parallel
    loadNextSite();
    loadNextSite();
    loadNextSite();
}

/**
 * Load the flag summary of the specified site.
 */
function loadSiteFlagSummary(siteName, siteUserFlagSummaryUrl, finishedCallback) {
    GM_xmlhttpRequest({
        method: 'GET',
        url: siteUserFlagSummaryUrl,
        onload: function(response) {
            finishedCallback();
            parseSiteFlagSummary(siteName, siteUserFlagSummaryUrl, response.response);
        },
        onerror: function(response) {
            console.error('loadSiteFlagSummary: ' + JSON.stringify(response));
            finishedCallback();
        }
    });
}

/**
 * Parses the flag summary site and extracts the stats.
 */
function parseSiteFlagSummary(siteName, siteUserFlagSummaryUrl, html) {
    let pageNode = document.createElement('div');
    pageNode.innerHTML = html;

    let sumFlagsTotal = 0;
    let sumFlagsDeclined = 0;
    let sumFlagsDisputed = 0;
    let sumFlagsRetracted = 0;
    let sumFlagsExpired = 0;
    let sumFlagsPending = 0;
    let sumFlagsHelpful = 0;

    // search for flag stats
    let flagCountNodes = pageNode.querySelectorAll('#flag-stat-info-table > tbody > tr > td.col2 > a[href*="status="]');
    for (var i = 0; i < flagCountNodes.length; i++) {
        let flagType = parseInt(flagCountNodes[i].href.replace(/^.+?\bstatus=(\d+).*$/, '$1'));
        let flagCount = parseInt(previousElementSibling(flagCountNodes[i].parentElement).textContent.replace(/\D/g, ''));

        sumFlagsTotal += flagCount;

        switch (flagType) {
            case 1: // pending
                sumFlagsPending += flagCount; break;
            case 2: // helpful
                sumFlagsHelpful += flagCount; break;
            case 3: // declined
                sumFlagsDeclined += flagCount; break;
            case 4: // disputed
                sumFlagsDisputed += flagCount; break;
            case 5: // expired
                sumFlagsExpired += flagCount; break;
            case 6: // retracted
                sumFlagsRetracted += flagCount; break;
            default:
                console.error('parseSiteFlagSummary: unknown flag type #' + flagType);
                break;
        }
    }

    if (sumFlagsTotal == 0) {
        // skip site with no flags
        return;
    }

    // update global summary
    flagGlobalSummaryStats.sumFlagsTotal += sumFlagsTotal;
    flagGlobalSummaryStats.sumFlagsDeclined += sumFlagsDeclined;
    flagGlobalSummaryStats.sumFlagsDisputed += sumFlagsDisputed;
    flagGlobalSummaryStats.sumFlagsRetracted += sumFlagsRetracted;
    flagGlobalSummaryStats.sumFlagsExpired += sumFlagsExpired;
    flagGlobalSummaryStats.sumFlagsPending += sumFlagsPending;
    flagGlobalSummaryStats.sumFlagsHelpful += sumFlagsHelpful;

    updateGlobalFlagStats();

    // compute helpful percentage
    let realTotal = sumFlagsTotal - sumFlagsPending - sumFlagsRetracted - sumFlagsExpired;
    let helpfulFraction = realTotal == 0 ? 0 : (sumFlagsHelpful / realTotal);

	// get most recent flag date
    let flagHistoryDates = pageNode.querySelectorAll('.user-flag-history .mod-flag .relativetime');
	let mostRecentflagHistoryDateNode = flagHistoryDates[0];
	let lastFlagTimestamp = mostRecentflagHistoryDateNode.title;
	let lastFlagTimeDisplay = mostRecentflagHistoryDateNode.textContent.split('at')[0].trim();

    // get site icon
    let siteFaviconURL = pageNode.querySelector('link[rel*="icon"]').href;

    // create table row for this site
    let siteFlagSummaryTr = document.createElement('tr');
    siteFlagSummaryTr.innerHTML = `
        <img src="` + siteFaviconURL + `"
            style="width:16px;height:16px;vertical-align:middle" />
        <a href="` + siteUserFlagSummaryUrl + `">` + siteName + `
        ` + formatFlagCount(sumFlagsHelpful) + `
        ` + formatFlagCount(sumFlagsDeclined) + `
        ` + formatFlagCount(sumFlagsDisputed) + `
        ` + formatFlagCount(sumFlagsExpired) + `
        ` + formatFlagCount(sumFlagsRetracted) + `
        ` + formatFlagCount(sumFlagsPending) + `
        ` + formatFlagCount(sumFlagsTotal) + `
        ` + formatFlagPercentage(helpfulFraction) + `%
        <td style="color:#999" title="` + lastFlagTimestamp + `">` + lastFlagTimeDisplay + `
    `;
    flagSummaryTableBody.appendChild(siteFlagSummaryTr);

	// keep order
    sortTable(sortedColIndex, sortedColAsc);
}

/**
 * Format flag count, empty if 0.
 */
function formatFlagCount(flagCount) {
    if (flagCount == 0)
        return '';
    else
        return flagCount;
}

/**
 * Format helpful flag percentage.
 */
function formatFlagPercentage(fraction) {
    return (fraction * 100).toFixed(2);
}

/**
 * Find the previous non-text sibling node.
 */
function previousElementSibling(node) {
    do {
        node = node.previousSibling;
    } while (node && node.nodeType !== 1);
    return node;
}

/**
 * Sort the table.
 */
function sortTable(col, asc) {
    sortedColIndex = col;
    var trs = Array.prototype.slice.call(flagSummaryTableBody.rows, 0);
    asc = -((+asc) || -1);
    if (col == 1) {
        trs = trs.sort(function (a, b) {
            return asc * (a.cells[col].textContent.trim().localeCompare(b.cells[col].textContent.trim()));
        });
    }
	else if (col == 10) {
        trs = trs.sort(function (a, b) {
            return asc * (a.cells[col].title.localeCompare(b.cells[col].title));
        });
    }
    else {
        trs = trs.sort(function (a, b) {
            return asc
                * ((parseInt(b.cells[col].textContent.replace(/\D/g, '')) || 0) -
                (parseInt(a.cells[col].textContent.replace(/\D/g, '')) || 0) );
        });
    }
    for(var i = 0; i < trs.length; ++i) flagSummaryTableBody.appendChild(trs[i]);
}

<|endoftext|>