<commit_before>define(function (require) {
    'use strict';
    var FloatingHeaderPlugin,
        $ = require('jquery'),
        _ = require('underscore'),
        BasePlugin = require('oroui/js/app/plugins/base/plugin'),
        Backbone = require('backbone'),
        mediator = require('oroui/js/mediator'),
        layout = require('oroui/js/layout'),
        tools = require('oroui/js/tools');

    FloatingHeaderPlugin = BasePlugin.extend({
        initialize: function (grid) {
            this.grid = grid;
            this.grid.on('shown', _.bind(this.onGridShown, this));

            this.selectMode = _.bind(this.selectMode, this);
            this.checkLayout = _.bind(this.checkLayout, this);
            this.fixHeaderCellWidth = _.bind(this.fixHeaderCellWidth, this);
        },

        onGridShown: function () {
            if (this.enabled && !this.connected) {
                this.enable();
            }
        },

        enable: function () {
            if (!this.grid.rendered) {
                // not ready to apply floatingHeader
                FloatingHeaderPlugin.__super__.enable.call(this);
                return;
            }

            this.setupCache();
            this.rescrollCb = this.enableOtherScroll();
            this.headerHeight = this.domCache.theadTr.height();
            this.fixHeaderCellWidth();
            this.$grid.on('click.float-thead', 'thead:first .dropdown', _.bind(function () {
                this.setFloatTheadMode(this.scrollVisible ? 'relative' : 'default');
            }, this));
            this.domCache.gridContainer.parents().add(document).on('scroll', this.checkLayout);

            this.listenTo(mediator, 'layout:headerStateChange', this.selectMode);
            this.listenTo(this.grid, 'content:update', this.fixHeaderCellWidth);
            this.listenTo(this.grid, 'layout:update', this.fixHeaderCellWidth);
            this.checkLayoutIntervalId = setInterval(this.checkLayout, 400);
            this.connected = true;
            FloatingHeaderPlugin.__super__.enable.call(this);
        },

        disable: function () {
            this.connected = false;
            clearInterval(this.checkLayoutIntervalId);

            this.setFloatTheadMode('default');
            this.disableOtherScroll();
            this.$grid.off('click.float-thead');
            this.domCache.gridContainer.parents().add(document).off('scroll', this.checkLayout);
            // remove css
            this.domCache.headerCells.attr('style', '');
            this.domCache.firstRowCells.attr('style', '');
            FloatingHeaderPlugin.__super__.disable.call(this);
        },

        setupCache: function () {
            this.$grid = this.grid.$grid;
            this.$el = this.grid.$el;
            this.documentHeight = $(document).height();
            this.domCache = {
                body: $(document.body),
                gridContainer: this.$grid.parent(),
                headerCells: this.$grid.find('th:first').parent().find('th'),
                firstRowCells: this.$grid.find('tbody tr:not(.thead-sizing-row):first td'),
                otherScrollContainer: this.$grid.parents('.other-scroll-container:first'),
                gridScrollableContainer: this.$grid.parents('.grid-scrollable-container:first'),
                otherScroll: this.$el.find('.other-scroll'),
                otherScrollInner: this.$el.find('.other-scroll > div'),
                thead: this.$grid.find('thead:first'),
                theadTr: this.$grid.find('thead:first tr:first')
            };
        },

        fixHeaderCellWidth: function () {
            this.setupCache();
            var headerCells = this.domCache.headerCells,
                firstRowCells = this.domCache.firstRowCells,
                totalWidth = 0,
                widths = [],
                self = this,
                scrollBarWidth = mediator.execute('layout:scrollbarWidth');
            // remove style
            headerCells.attr('style', '');
            firstRowCells.attr('style', '');
            this.$grid.css({width: ''});
            this.domCache.gridContainer.css({width: ''});
            if (this.scrollVisible) {
                this.$grid.css({borderRight: scrollBarWidth + 'px solid transparent'});
            }
            this.$el.removeClass('floatThead');

            // copy widths
            headerCells.each(function (i, headerCell) {
                widths.push(headerCell.offsetWidth);
            });
            headerCells.each(function (i, headerCell) {
                var cellWidth = widths[i];
                if (self.scrollVisible && i === headerCells.length - 1) {
                    cellWidth += scrollBarWidth;
                }
                totalWidth += cellWidth;
                headerCell.style.width = cellWidth + 'px';
                headerCell.style.minWidth = cellWidth + 'px';
                headerCell.style.boxSizing = 'border-box';
                if (firstRowCells[i]) {
                    firstRowCells[i].style.width = cellWidth + 'px';
                    firstRowCells[i].style.minWidth = cellWidth + 'px';
                    firstRowCells[i].style.boxSizing = 'border-box';
                }
            });

            this.$grid.css({borderRight: 'none'});
            this.$el.addClass('floatThead');
            this.$grid.css({
                width: totalWidth
            });
            this.domCache.gridContainer.css({
                width: totalWidth
            });

            this.selectMode();
        },

        /**
         * Selects floating header mode
         */
        selectMode: function () {
            // get gridRect
            var tableRect = this.domCache.gridContainer[0].getBoundingClientRect(),
                visibleRect = this.getVisibleRect(this.domCache.gridContainer[0]),
                mode = 'default';
            if (visibleRect.top !== tableRect.top || this.grid.layout === 'fullscreen') {
                mode = 'fixed';
            }
            this.setFloatTheadMode(mode, visibleRect, tableRect);

            // update tracked values to prevent calling this function again
            this._lastClientRect = this.domCache.otherScrollContainer[0].getBoundingClientRect();
            this._lastScrollLeft = this.domCache.gridScrollableContainer.scrollLeft();
            if (this.rescrollCb) {
                this.rescrollCb();
            }
        },

        /**
         * Setups floating header mode
         */
        setFloatTheadMode: function (mode, visibleRect, tableRect) {
            var theadRect, sizingThead;
            // pass this argument to avoid expensive calculations
            if (!visibleRect) {
                visibleRect = this.getVisibleRect(this.domCache.gridContainer[0]);
            }
            if (!tableRect) {
                tableRect = this.domCache.gridContainer[0].getBoundingClientRect();
            }
            switch (mode) {
                case 'relative':
                    // works well with dropdowns, but causes jumps while scrolling
                    if (this.currentFloatTheadMode !== mode) {
                        this.$el.removeClass('floatThead-fixed');
                        this.$el.addClass('floatThead-relative');
                        if (!this.$grid.find('.thead-sizing').length) {
                            sizingThead = this.domCache.thead.clone().addClass('thead-sizing');
                            sizingThead.find('th').attr('style', '');
                            sizingThead.insertAfter(this.domCache.thead);
                        }
                    }
                    theadRect = this.domCache.thead[0].getBoundingClientRect();
                    this.domCache.thead.css({
                        width: '',
                        top: visibleRect.top - tableRect.top
                    });
                    this.domCache.theadTr.css({
                        marginLeft: tableRect.left - theadRect.left
                    });
                    if (mode === 'relative') {
                        this._lastScrollTop = this.domCache.gridScrollableContainer.scrollTop();
                    }
                    break;
                case 'fixed':
                    // provides good scroll experience
                    if (this.currentFloatTheadMode !== mode) {
                        this.$el.removeClass('floatThead-relative');
                        this.$el.addClass('floatThead-fixed');
                        this.$grid.find('thead:first .dropdown.open').removeClass('open');
                        if (!this.$grid.find('.thead-sizing').length) {
                            sizingThead = this.domCache.thead.clone().addClass('thead-sizing');
                            sizingThead.find('th').attr('style', '');
                            sizingThead.insertAfter(this.domCache.thead);
                        }
                    }
                    this.domCache.thead.css({
                        // show only visible part
                        top: visibleRect.top,
                        width: visibleRect.right - visibleRect.left,
                        height: Math.min(this.headerHeight, visibleRect.bottom - visibleRect.top),

                        // left side should be also tracked
                        // gives incorrect rendering when "document" scrolled horizontally
                        left: visibleRect.left
                    });
                    theadRect = this.domCache.thead[0].getBoundingClientRect();
                    this.domCache.theadTr.css({
                        // possible solution set scrollLeft instead
                        // could be more fast for rendering
                        marginLeft: tableRect.left - theadRect.left
                    });
                    break;
                default:
                    if (this.currentFloatTheadMode !== mode) {
                        this.$grid.find('.thead-sizing').remove();
                        this.$el.removeClass('floatThead-relative floatThead-fixed');
                        // remove extra styles
                        this.domCache.thead.attr('style', '');
                        this.domCache.theadTr.attr('style', '');
                        // cleanup
                    }
                    break;
            }
            this.currentFloatTheadMode = mode;
        },

        /**
         * Enables other scroll functionality
         */
        enableOtherScroll: function () {
            var self = this,
                scrollContainer = this.domCache.gridScrollableContainer,
                otherScroll = this.domCache.otherScroll,
                otherScrollInner = this.domCache.otherScrollInner,
                scrollBarWidth = mediator.execute('layout:scrollbarWidth'),
                scrollStateModel = new Backbone.Model(),
                heightDec;

            this.scrollStateModel = scrollStateModel;

            if (scrollBarWidth === 0) {
                // nothing to do
                return _.noop;
            }

            scrollStateModel.on('change:headerHeight', function (model, val) {
                heightDec = val + 1; // compensate border
                otherScroll.css({
                    width: scrollBarWidth,
                    marginTop: heightDec
                });
                scrollStateModel.trigger('change:scrollHeight', scrollStateModel, scrollContainer[0].scrollHeight);
                scrollStateModel.trigger('change:clientHeight', scrollStateModel, scrollContainer[0].clientHeight);
            }, this);
            scrollStateModel.on('change:visible', function (model, val) {
                scrollContainer.css({
                    width: 'calc(100% + ' + (val ? scrollBarWidth : 0) + 'px)'
                });
                otherScroll.css({
                    display: val ? 'block' : 'none'
                });
                this.fixHeaderCellWidth();
            }, this);
            scrollStateModel.on('change:clientHeight', function (model, val) {
                otherScroll.css({
                    height: val - heightDec
                });
            }, this);
            scrollStateModel.on('change:clientWidth', function (model, val) {
                otherScroll.css({
                    marginLeft: val - scrollBarWidth
                });
            }, this);
            scrollStateModel.on('change:scrollHeight', function (model, val) {
                otherScrollInner.css({
                    height: val - heightDec
                });
            });
            scrollStateModel.on('change:scrollTop', function (model, val) {
                otherScroll.scrollTop(val);
            }, this);
            function setup() {
                scrollStateModel.set({
                    headerHeight: self.headerHeight
                });
                self.scrollVisible = scrollContainer[0].clientHeight + 1 /*IE fix*/ < scrollContainer[0].scrollHeight;
                scrollStateModel.set({
                    visible: self.scrollVisible,
                    scrollHeight:  scrollContainer[0].scrollHeight,
                    clientHeight:  scrollContainer[0].clientHeight,
                    clientWidth:   scrollContainer[0].clientWidth,
                    scrollTop:     scrollContainer[0].scrollTop
                });
            }
            scrollContainer.on('scroll', setup);
            otherScroll.on('scroll', function () {
                var mainScrollTop = scrollContainer.scrollTop(),
                    otherScrollTop = otherScroll.scrollTop();
                if (mainScrollTop !== otherScrollTop) {
                    scrollContainer.scrollTop(otherScroll.scrollTop());
                    self.checkLayout();
                }
            });
            setup();
            return setup;
        },

        /**
         * Disables other scroll functionality
         */
        disableOtherScroll: function () {
            this.domCache.gridScrollableContainer.off('scroll', this.rescrollCb);
            this.domCache.otherScroll.off('scroll');
            this.domCache.otherScroll.css({display: 'none'});
            this.domCache.gridScrollableContainer.css({width: ''});
            this.scrollStateModel.destroy();
            delete this.scrollStateModel;
            delete this.rescrollCb;
        },

        /**
         * Checks and performs required actions
         */
        checkLayout: function () {
            var scrollContainerRect, scrollLeft;
            if (this.currentFloatTheadMode === 'default') {
                if (this.grid.layout === 'fullscreen' &&
                        this.currentFloatTheadMode === 'default' &&
                        this.domCache.gridScrollableContainer.scrollTop() !== 0) {
                    this.selectMode();
                    return;
                }
            }
            if (this.currentFloatTheadMode === 'relative' &&
                    this.domCache.gridScrollableContainer.scrollTop() !== this._lastScrollTop) {
                this.selectMode();
                return;
            }
            scrollContainerRect = this.domCache.otherScrollContainer[0].getBoundingClientRect();
            if (!this._lastClientRect || (this._lastClientRect.top !== scrollContainerRect.top ||
                    this._lastClientRect.left !== scrollContainerRect.left ||
                    this._lastClientRect.right !== scrollContainerRect.right)) {
                if (!this._lastClientRect || (this._lastClientRect.left !== scrollContainerRect.left ||
                    this._lastClientRect.right !== scrollContainerRect.right)) {
                    this.fixHeaderCellWidth();
                } else {
                    this.selectMode();
                }
            } else {
                scrollLeft = this.domCache.gridScrollableContainer.scrollLeft();
                if (this._lastScrollLeft !== scrollLeft) {
                    this.selectMode();
                    this._lastScrollLeft = scrollLeft;
                }
            }
            this._lastClientRect = scrollContainerRect;
        },

        /**
         * Returns visible rect of DOM element
         *
         * @param el
         * @returns {{top: number, left: Number, bottom: Number, right: Number}}
         */
        getVisibleRect: function (el) {
            var current = el,
                tableRect = current.getBoundingClientRect(),
                midRect = tableRect,
                borders,
                resultRect = {
                    top: midRect.top - this.headerHeight,
                    left: midRect.left,
                    bottom: midRect.bottom,
                    right: midRect.right
                };
            if (
                (resultRect.top === 0 && resultRect.bottom === 0) || // no-data block is shown
                (resultRect.top > this.documentHeight && this.currentFloatTheadMode === 'default') // grid is invisible
                ) {
                // no need to calculate anything
                return resultRect;
            }
            current = current.parentNode;
            while (current && current.getBoundingClientRect) {
                midRect = current.getBoundingClientRect();
                borders = $.fn.getBorders(current);

                if (tools.isMobile()) {
                    /**
                     * Equals header height. Cannot calculate dynamically due to issues on ipad
                     */
                    if (resultRect.top < layout.MOBILE_HEADER_HEIGHT && current.id === 'top-page' &&
                        !this.domCache.body.hasClass('input-focused')) {
                        resultRect.top = layout.MOBILE_HEADER_HEIGHT;
                    } else if (resultRect.top < layout.MOBILE_POPUP_HEADER_HEIGHT && current.className === 'widget-content') {
                        resultRect.top = layout.MOBILE_POPUP_HEADER_HEIGHT;
                    }
                }

                if (resultRect.top < midRect.top + borders.top) {
                    resultRect.top = midRect.top + borders.top;
                }
                if (resultRect.bottom > midRect.bottom - borders.bottom) {
                    resultRect.bottom = midRect.bottom - borders.bottom;
                }
                if (resultRect.left < midRect.left + borders.left) {
                    resultRect.left = midRect.left + borders.left;
                }
                if (resultRect.right > midRect.right - borders.right) {
                    resultRect.right = midRect.right - borders.right;
                }
                current = current.parentNode;
            }

            if (resultRect.top < 0) {
                resultRect.top = 0;
            }

            return resultRect;
        }
    });

    return FloatingHeaderPlugin;
});
<commit_msg>BAP-7769 extra horizontal scroll in FF<commit_after>define(function (require) {
    'use strict';
    var FloatingHeaderPlugin,
        $ = require('jquery'),
        _ = require('underscore'),
        BasePlugin = require('oroui/js/app/plugins/base/plugin'),
        Backbone = require('backbone'),
        mediator = require('oroui/js/mediator'),
        layout = require('oroui/js/layout'),
        tools = require('oroui/js/tools');

    FloatingHeaderPlugin = BasePlugin.extend({
        initialize: function (grid) {
            this.grid = grid;
            this.grid.on('shown', _.bind(this.onGridShown, this));

            this.selectMode = _.bind(this.selectMode, this);
            this.checkLayout = _.bind(this.checkLayout, this);
            this.fixHeaderCellWidth = _.bind(this.fixHeaderCellWidth, this);
        },

        onGridShown: function () {
            if (this.enabled && !this.connected) {
                this.enable();
            }
        },

        enable: function () {
            if (!this.grid.rendered) {
                // not ready to apply floatingHeader
                FloatingHeaderPlugin.__super__.enable.call(this);
                return;
            }

            this.setupCache();
            this.rescrollCb = this.enableOtherScroll();
            this.headerHeight = this.domCache.theadTr.height();
            this.fixHeaderCellWidth();
            this.$grid.on('click.float-thead', 'thead:first .dropdown', _.bind(function () {
                this.setFloatTheadMode(this.scrollVisible ? 'relative' : 'default');
            }, this));
            this.domCache.gridContainer.parents().add(document).on('scroll', this.checkLayout);

            this.listenTo(mediator, 'layout:headerStateChange', this.selectMode);
            this.listenTo(this.grid, 'content:update', this.fixHeaderCellWidth);
            this.listenTo(this.grid, 'layout:update', this.fixHeaderCellWidth);
            this.checkLayoutIntervalId = setInterval(this.checkLayout, 400);
            this.connected = true;
            FloatingHeaderPlugin.__super__.enable.call(this);
        },

        disable: function () {
            this.connected = false;
            clearInterval(this.checkLayoutIntervalId);

            this.setFloatTheadMode('default');
            this.disableOtherScroll();
            this.$grid.off('click.float-thead');
            this.domCache.gridContainer.parents().add(document).off('scroll', this.checkLayout);
            // remove css
            this.domCache.headerCells.attr('style', '');
            this.domCache.firstRowCells.attr('style', '');
            FloatingHeaderPlugin.__super__.disable.call(this);
        },

        setupCache: function () {
            this.$grid = this.grid.$grid;
            this.$el = this.grid.$el;
            this.documentHeight = $(document).height();
            this.domCache = {
                body: $(document.body),
                gridContainer: this.$grid.parent(),
                headerCells: this.$grid.find('th:first').parent().find('th'),
                firstRowCells: this.$grid.find('tbody tr:not(.thead-sizing-row):first td'),
                otherScrollContainer: this.$grid.parents('.other-scroll-container:first'),
                gridScrollableContainer: this.$grid.parents('.grid-scrollable-container:first'),
                otherScroll: this.$el.find('.other-scroll'),
                otherScrollInner: this.$el.find('.other-scroll > div'),
                thead: this.$grid.find('thead:first'),
                theadTr: this.$grid.find('thead:first tr:first')
            };
        },

        fixHeaderCellWidth: function () {
            this.setupCache();
            var headerCells = this.domCache.headerCells,
                firstRowCells = this.domCache.firstRowCells,
                totalWidth,
                sumWidth,
                widthDecrement = 0,
                widths = [],
                self = this,
                scrollBarWidth = mediator.execute('layout:scrollbarWidth');
            // remove style
            headerCells.attr('style', '');
            firstRowCells.attr('style', '');
            this.$grid.css({width: ''});
            this.domCache.gridContainer.css({width: ''});
            this.$el.removeClass('floatThead');

            // compensate scroll bar
            if (this.scrollVisible) {
                this.$grid.css({borderRight: scrollBarWidth + 'px solid transparent'});
            }

            totalWidth = this.$grid[0].offsetWidth;

            // save widths
            headerCells.each(function (i, headerCell) {
                widths.push(headerCell.offsetWidth);
            });

            // FF sometimes gives wrong values, need to check
            sumWidth = _.reduce(widths, function (a,b){return a+b});
            if (sumWidth > totalWidth) {
                widthDecrement = (sumWidth - totalWidth) / widths.length + 0.001
            }

            // add scroll bar width to last cell if scroll is visible
            if (self.scrollVisible) {
                widths[widths.length - 1] += scrollBarWidth;
                totalWidth += scrollBarWidth;
            }

            // set exact sizes to header cells and cells in first row
            headerCells.each(function (i, headerCell) {
                var cellWidth = widths[i] - widthDecrement;
                headerCell.style.width = cellWidth + 'px';
                headerCell.style.minWidth = cellWidth + 'px';
                headerCell.style.boxSizing = 'border-box';
                if (firstRowCells[i]) {
                    firstRowCells[i].style.width = cellWidth + 'px';
                    firstRowCells[i].style.minWidth = cellWidth + 'px';
                    firstRowCells[i].style.boxSizing = 'border-box';
                }
            });

            if (this.scrollVisible) {
                this.$grid.css({borderRight: 'none'});
            }
            this.$el.addClass('floatThead');
            this.$grid.css({
                width: totalWidth
            });
            this.domCache.gridContainer.css({
                width: totalWidth
            });

            this.selectMode();
        },

        /**
         * Selects floating header mode
         */
        selectMode: function () {
            // get gridRect
            var tableRect = this.domCache.gridContainer[0].getBoundingClientRect(),
                visibleRect = this.getVisibleRect(this.domCache.gridContainer[0]),
                mode = 'default';
            if (visibleRect.top !== tableRect.top || this.grid.layout === 'fullscreen') {
                mode = 'fixed';
            }
            this.setFloatTheadMode(mode, visibleRect, tableRect);

            // update tracked values to prevent calling this function again
            this._lastClientRect = this.domCache.otherScrollContainer[0].getBoundingClientRect();
            this._lastScrollLeft = this.domCache.gridScrollableContainer.scrollLeft();
            if (this.rescrollCb) {
                this.rescrollCb();
            }
        },

        /**
         * Setups floating header mode
         */
        setFloatTheadMode: function (mode, visibleRect, tableRect) {
            var theadRect, sizingThead;
            // pass this argument to avoid expensive calculations
            if (!visibleRect) {
                visibleRect = this.getVisibleRect(this.domCache.gridContainer[0]);
            }
            if (!tableRect) {
                tableRect = this.domCache.gridContainer[0].getBoundingClientRect();
            }
            switch (mode) {
                case 'relative':
                    // works well with dropdowns, but causes jumps while scrolling
                    if (this.currentFloatTheadMode !== mode) {
                        this.$el.removeClass('floatThead-fixed');
                        this.$el.addClass('floatThead-relative');
                        if (!this.$grid.find('.thead-sizing').length) {
                            sizingThead = this.domCache.thead.clone().addClass('thead-sizing');
                            sizingThead.find('th').attr('style', '');
                            sizingThead.insertAfter(this.domCache.thead);
                        }
                    }
                    theadRect = this.domCache.thead[0].getBoundingClientRect();
                    this.domCache.thead.css({
                        width: '',
                        top: visibleRect.top - tableRect.top
                    });
                    this.domCache.theadTr.css({
                        marginLeft: tableRect.left - theadRect.left
                    });
                    if (mode === 'relative') {
                        this._lastScrollTop = this.domCache.gridScrollableContainer.scrollTop();
                    }
                    break;
                case 'fixed':
                    // provides good scroll experience
                    if (this.currentFloatTheadMode !== mode) {
                        this.$el.removeClass('floatThead-relative');
                        this.$el.addClass('floatThead-fixed');
                        this.$grid.find('thead:first .dropdown.open').removeClass('open');
                        if (!this.$grid.find('.thead-sizing').length) {
                            sizingThead = this.domCache.thead.clone().addClass('thead-sizing');
                            sizingThead.find('th').attr('style', '');
                            sizingThead.insertAfter(this.domCache.thead);
                        }
                    }
                    this.domCache.thead.css({
                        // show only visible part
                        top: visibleRect.top,
                        width: visibleRect.right - visibleRect.left,
                        height: Math.min(this.headerHeight, visibleRect.bottom - visibleRect.top),

                        // left side should be also tracked
                        // gives incorrect rendering when "document" scrolled horizontally
                        left: visibleRect.left
                    });
                    theadRect = this.domCache.thead[0].getBoundingClientRect();
                    this.domCache.theadTr.css({
                        // possible solution set scrollLeft instead
                        // could be more fast for rendering
                        marginLeft: tableRect.left - theadRect.left
                    });
                    break;
                default:
                    if (this.currentFloatTheadMode !== mode) {
                        this.$grid.find('.thead-sizing').remove();
                        this.$el.removeClass('floatThead-relative floatThead-fixed');
                        // remove extra styles
                        this.domCache.thead.attr('style', '');
                        this.domCache.theadTr.attr('style', '');
                        // cleanup
                    }
                    break;
            }
            this.currentFloatTheadMode = mode;
        },

        /**
         * Enables other scroll functionality
         */
        enableOtherScroll: function () {
            var self = this,
                scrollContainer = this.domCache.gridScrollableContainer,
                otherScroll = this.domCache.otherScroll,
                otherScrollInner = this.domCache.otherScrollInner,
                scrollBarWidth = mediator.execute('layout:scrollbarWidth'),
                scrollStateModel = new Backbone.Model(),
                heightDec;

            this.scrollStateModel = scrollStateModel;

            if (scrollBarWidth === 0) {
                // nothing to do
                return _.noop;
            }

            scrollStateModel.on('change:headerHeight', function (model, val) {
                heightDec = val + 1; // compensate border
                otherScroll.css({
                    width: scrollBarWidth,
                    marginTop: heightDec
                });
                scrollStateModel.trigger('change:scrollHeight', scrollStateModel, scrollContainer[0].scrollHeight);
                scrollStateModel.trigger('change:clientHeight', scrollStateModel, scrollContainer[0].clientHeight);
            }, this);
            scrollStateModel.on('change:visible', function (model, val) {
                scrollContainer.css({
                    width: 'calc(100% + ' + (val ? scrollBarWidth : 0) + 'px)'
                });
                otherScroll.css({
                    display: val ? 'block' : 'none'
                });
                this.fixHeaderCellWidth();
            }, this);
            scrollStateModel.on('change:clientHeight', function (model, val) {
                otherScroll.css({
                    height: val - heightDec
                });
            }, this);
            scrollStateModel.on('change:clientWidth', function (model, val) {
                otherScroll.css({
                    marginLeft: val - scrollBarWidth
                });
            }, this);
            scrollStateModel.on('change:scrollHeight', function (model, val) {
                otherScrollInner.css({
                    height: val - heightDec
                });
            });
            scrollStateModel.on('change:scrollTop', function (model, val) {
                otherScroll.scrollTop(val);
            }, this);
            function setup() {
                scrollStateModel.set({
                    headerHeight: self.headerHeight
                });
                self.scrollVisible = scrollContainer[0].clientHeight + 1 /*IE fix*/ < scrollContainer[0].scrollHeight;
                scrollStateModel.set({
                    visible: self.scrollVisible,
                    scrollHeight:  scrollContainer[0].scrollHeight,
                    clientHeight:  scrollContainer[0].clientHeight,
                    clientWidth:   scrollContainer[0].clientWidth,
                    scrollTop:     scrollContainer[0].scrollTop
                });
            }
            scrollContainer.on('scroll', setup);
            otherScroll.on('scroll', function () {
                var mainScrollTop = scrollContainer.scrollTop(),
                    otherScrollTop = otherScroll.scrollTop();
                if (mainScrollTop !== otherScrollTop) {
                    scrollContainer.scrollTop(otherScroll.scrollTop());
                    self.checkLayout();
                }
            });
            setup();
            return setup;
        },

        /**
         * Disables other scroll functionality
         */
        disableOtherScroll: function () {
            this.domCache.gridScrollableContainer.off('scroll', this.rescrollCb);
            this.domCache.otherScroll.off('scroll');
            this.domCache.otherScroll.css({display: 'none'});
            this.domCache.gridScrollableContainer.css({width: ''});
            this.scrollStateModel.destroy();
            delete this.scrollStateModel;
            delete this.rescrollCb;
        },

        /**
         * Checks and performs required actions
         */
        checkLayout: function () {
            var scrollContainerRect, scrollLeft;
            if (this.currentFloatTheadMode === 'default') {
                if (this.grid.layout === 'fullscreen' &&
                        this.currentFloatTheadMode === 'default' &&
                        this.domCache.gridScrollableContainer.scrollTop() !== 0) {
                    this.selectMode();
                    return;
                }
            }
            if (this.currentFloatTheadMode === 'relative' &&
                    this.domCache.gridScrollableContainer.scrollTop() !== this._lastScrollTop) {
                this.selectMode();
                return;
            }
            scrollContainerRect = this.domCache.otherScrollContainer[0].getBoundingClientRect();
            if (!this._lastClientRect || (this._lastClientRect.top !== scrollContainerRect.top ||
                    this._lastClientRect.left !== scrollContainerRect.left ||
                    this._lastClientRect.right !== scrollContainerRect.right)) {
                if (!this._lastClientRect || (this._lastClientRect.left !== scrollContainerRect.left ||
                    this._lastClientRect.right !== scrollContainerRect.right)) {
                    this.fixHeaderCellWidth();
                } else {
                    this.selectMode();
                }
            } else {
                scrollLeft = this.domCache.gridScrollableContainer.scrollLeft();
                if (this._lastScrollLeft !== scrollLeft) {
                    this.selectMode();
                    this._lastScrollLeft = scrollLeft;
                }
            }
            this._lastClientRect = scrollContainerRect;
        },

        /**
         * Returns visible rect of DOM element
         *
         * @param el
         * @returns {{top: number, left: Number, bottom: Number, right: Number}}
         */
        getVisibleRect: function (el) {
            var current = el,
                tableRect = current.getBoundingClientRect(),
                midRect = tableRect,
                borders,
                resultRect = {
                    top: midRect.top - this.headerHeight,
                    left: midRect.left,
                    bottom: midRect.bottom,
                    right: midRect.right
                };
            if (
                (resultRect.top === 0 && resultRect.bottom === 0) || // no-data block is shown
                (resultRect.top > this.documentHeight && this.currentFloatTheadMode === 'default') // grid is invisible
                ) {
                // no need to calculate anything
                return resultRect;
            }
            current = current.parentNode;
            while (current && current.getBoundingClientRect) {
                midRect = current.getBoundingClientRect();
                borders = $.fn.getBorders(current);

                if (tools.isMobile()) {
                    /**
                     * Equals header height. Cannot calculate dynamically due to issues on ipad
                     */
                    if (resultRect.top < layout.MOBILE_HEADER_HEIGHT && current.id === 'top-page' &&
                        !this.domCache.body.hasClass('input-focused')) {
                        resultRect.top = layout.MOBILE_HEADER_HEIGHT;
                    } else if (resultRect.top < layout.MOBILE_POPUP_HEADER_HEIGHT && current.className === 'widget-content') {
                        resultRect.top = layout.MOBILE_POPUP_HEADER_HEIGHT;
                    }
                }

                if (resultRect.top < midRect.top + borders.top) {
                    resultRect.top = midRect.top + borders.top;
                }
                if (resultRect.bottom > midRect.bottom - borders.bottom) {
                    resultRect.bottom = midRect.bottom - borders.bottom;
                }
                if (resultRect.left < midRect.left + borders.left) {
                    resultRect.left = midRect.left + borders.left;
                }
                if (resultRect.right > midRect.right - borders.right) {
                    resultRect.right = midRect.right - borders.right;
                }
                current = current.parentNode;
            }

            if (resultRect.top < 0) {
                resultRect.top = 0;
            }

            return resultRect;
        }
    });

    return FloatingHeaderPlugin;
});
<|endoftext|>


<commit_before>"use strict";
var px = require("../px");
var dom = require("../dom");
var mkAi = require("../mkAi");
var sock = require("../sock");
var Decks = require("../Decks.json");
var startMenu = require("./MainMenu");
function mkDaily(type) {
	if (type < 3) {
		return function() {
			var dataNext = type == 1 ?
				{ goldreward: 200, endurance: 2, cost: 0, daily: 1, cardreward: "", noheal: true} :
				{ goldreward: 750, endurance: 2, cost: 0, daily: 2, cardreward: "" };
			var game = mkAi.mkAi(type == 1 ? 0 : 2, type)();
			if (game){
				game.addData(dataNext);
				game.dataNext = dataNext;
			}
		}
	}
	else {
		return function() {
			var game = mkAi.mkPremade(type == 3 ? 1 : 3, type)();
			if (game){
				game.addonreward = type == 3 ? 90 : 200;
				sock.userExec("donedaily", { daily: type });
			}
		}
	}
}
module.exports = function(){
	var magename = Decks.mage[sock.user.dailymage][0];
	var dgname = Decks.demigod[sock.user.dailydg][0];
	var events = [
		"Novice Endurance Fight 3 Commoners in a row without healing in between. May try until you win.",
		"Expert Endurance: Fight 3 Champions in a row. May try until you win.",
		"Novice Duel: Fight " + magename + ". Only one attempt allowed.",
		"Expert Duel: Fight " + dgname + ". Only one attempt allowed."];
	var div = dom.div([50, 50, ["Exit", startMenu]]);
	for (var i = 1;i < 5;i++) {
		var active = !(sock.user.daily & (1 << i));
		if (active) {
			dom.add(div, [50, 100 + 30 * i, ["Fight!", mkDaily(i)]]);
		}
		dom.add(div, [130, 100 + 30 * i, active ? events[i-1] : i > 2 ? (sock.user.daily&(i==3?1:32) ? "You defeated this already today." : "You failed this today. Better luck tomorrow!") : "Completed."]);
	}
	if (sock.user.daily == 191){
		dom.add(div, [50, 280, ["Nymph!", function(){
			var etg = require("../etg");
			var nymph = etg.NymphList[etg.PlayerRng.uptoceil(12)];
			sock.userExec("donedaily", {daily: 6, c: nymph});
			startMenu(nymph);
		}]], [130, 280, "You successfully completed all tasks."]);
	}
	px.view({dom:div});
}<commit_msg>Make champ endurance easier<commit_after>"use strict";
var px = require("../px");
var dom = require("../dom");
var mkAi = require("../mkAi");
var sock = require("../sock");
var Decks = require("../Decks.json");
var startMenu = require("./MainMenu");
function mkDaily(type) {
	if (type < 3) {
		return function() {
			var dataNext = type == 1 ?
				{ goldreward: 200, endurance: 2, cost: 0, daily: 1, cardreward: "", noheal: true} :
				{ goldreward: 500, endurance: 1, cost: 0, daily: 2, cardreward: "" };
			var game = mkAi.mkAi(type == 1 ? 0 : 2, type)();
			if (game){
				game.addData(dataNext);
				game.dataNext = dataNext;
			}
		}
	}
	else {
		return function() {
			var game = mkAi.mkPremade(type == 3 ? 1 : 3, type)();
			if (game){
				game.addonreward = type == 3 ? 90 : 200;
				sock.userExec("donedaily", { daily: type });
			}
		}
	}
}
module.exports = function(){
	var magename = Decks.mage[sock.user.dailymage][0];
	var dgname = Decks.demigod[sock.user.dailydg][0];
	var events = [
		"Novice Endurance Fight 3 Commoners in a row without healing in between. May try until you win.",
		"Expert Endurance: Fight 2 Champions in a row. May try until you win.",
		"Novice Duel: Fight " + magename + ". Only one attempt allowed.",
		"Expert Duel: Fight " + dgname + ". Only one attempt allowed."];
	var div = dom.div([50, 50, ["Exit", startMenu]]);
	for (var i = 1;i < 5;i++) {
		var active = !(sock.user.daily & (1 << i));
		if (active) {
			dom.add(div, [50, 100 + 30 * i, ["Fight!", mkDaily(i)]]);
		}
		dom.add(div, [130, 100 + 30 * i, active ? events[i-1] : i > 2 ? (sock.user.daily&(i==3?1:32) ? "You defeated this already today." : "You failed this today. Better luck tomorrow!") : "Completed."]);
	}
	if (sock.user.daily == 191){
		dom.add(div, [50, 280, ["Nymph!", function(){
			var etg = require("../etg");
			var nymph = etg.NymphList[etg.PlayerRng.uptoceil(12)];
			sock.userExec("donedaily", {daily: 6, c: nymph});
			startMenu(nymph);
		}]], [130, 280, "You successfully completed all tasks."]);
	}
	px.view({dom:div});
}<|endoftext|>


<commit_before>var mainObj = [],
    currentPos = new Array(7).fill(0),
    currentPlayer = 'X',
    time = 200;

function init() {
    mainObj = [];
    for(var i =0; i<7; i++) {
        mainObj.push(new Array(7).fill(' '));
    }
    plotPos();
    printPlayer();
}

function display(str, style) {
    console.log("%c"+str, style);
}

function plotPos() {
    var logit = '\n%c+---+---+---+---+---+---+---+\n';
    var style = [];
    var boardColor = "color:" + (currentPlayer === 'X' ? 'blue' : 'red');
    style.push(boardColor);
    for(var i=6; i>=0; i--) {
        logit += '%c|';
        style.push(boardColor);
        for(var j=0; j<7; j++) {
            logit = "" + logit + "" + ("%c " +mainObj[j][i]) + ' %c|';
            if(mainObj[j][i] === 'X'){
                style.push("background:blue;color:blue;display:block");
            }else if(mainObj[j][i] === 'O'){
                style.push("background:red;color:red;display:block");
            }
            else{
                style.push("background:white;");
            }
            style.push(boardColor);
        }
        logit+='\n' + '+---+---+---+---+---+---+---+\n';

    }
    logit+='  1   2   3   4   5   6   7 ' + '\n';
    // display(logit);
    style.unshift(logit);
    console.log.apply(null,style);
}

function playAt(stack) {
    if(stack < 1 || stack > 7) {
        display("Kindly enter correct stack(1-7)!!", "color:#a94442;font-size:13px;background:#f2dede;");
        return;
    }

    var currPos = currentPos[stack-1], position = 7;

    function showThePlot() {
        console.clear();
        position--;
        loopPlot();
        plotPos();
    }

    function loopPlot() {
        if(position >= currPos) {
            setTimeout(function() {
                if(position < 6) {
                    mainObj[stack-1][position+1] = ' ';
                }
                mainObj[stack-1][position] = currentPlayer;
                showThePlot();
            }, time);
        } else {
            currentPos[stack-1] = currPos+1;
            togglePlayer();
        }
    }

    showThePlot();

}

function togglePlayer() {
    currentPlayer = currentPlayer == 'X' ? 'O' : 'X';
    printPlayer();
//    display('Its your turn ' + currentPlayer);
}
function printPlayer() {
    if(currentPlayer === 'X'){
        console.log("Its your turn %cX ", "color:blue;background:blue");
    }else{
        console.log("Its your turn %cO ", "color:red;background:red");
    }
}
init();
<commit_msg>Update connect4.js<commit_after>/*
 Libraries Start
*/

/*!
 * CallWithName v1.0
 *
 * Copyright 2017, Himanshu Tanwar
 * Released under the MIT license
 * https://github.com/webXtended/callWithName
 * Date: 2017-03-16
 */
var callWithName=function(t,n,o){var i,a,e=typeof t,s=typeof n;o=o||{},"string"===e&&"function"===s?(i=o.context||window,i[t]=n,i[t].toString=function(){var t=n.call(i);return t||o.returnValue||void 0}):(a=[],a.push("cannot assign"),a.push(e),a.push("to a"),a.push(s),console.error(a.join(" ")),console.info("Pass a string for name and a function to associate it with the name"),console.info("for more details visit https://github.com/webXtended/callWithName"))};
/*
 Libraries End
*/

/*!
 * Copyright 2017, Anusha Balodi
 * Released under the MIT license
 * https://github.com/AnushaBalodi/connect4-console
 * Date: 2017-03-17
 */

var mainObj = [],
    currentPos = new Array(7).fill(0),
    currentPlayer = 'X',
    time = 200;

function init() {
    mainObj = [];
    for(var i =0; i<7; i++) {
        mainObj.push(new Array(7).fill(' '));
    }
    plotPos();
    printPlayer();
}

function display(str, style) {
    console.log("%c"+str, style);
}

function plotPos() {
    var logit = '\n%c+---+---+---+---+---+---+---+\n';
    var style = [];
    var boardColor = "color:" + (currentPlayer === 'X' ? 'blue' : 'red');
    style.push(boardColor);
    for(var i=6; i>=0; i--) {
        logit += '%c|';
        style.push(boardColor);
        for(var j=0; j<7; j++) {
            logit = "" + logit + "" + ("%c " +mainObj[j][i]) + ' %c|';
            if(mainObj[j][i] === 'X'){
                style.push("background:blue;color:blue;display:block");
            }else if(mainObj[j][i] === 'O'){
                style.push("background:red;color:red;display:block");
            }
            else{
                style.push("background:white;");
            }
            style.push(boardColor);
        }
        logit+='\n' + '+---+---+---+---+---+---+---+\n';

    }
    logit+='  a   b   c   d   e   f   g ' + '\n';
    // display(logit);
    style.unshift(logit);
    console.log.apply(null,style);
}

function playAt(stack) {
    if(stack < 1 || stack > 7) {
        display("Kindly enter correct stack(1-7)!!", "color:#a94442;font-size:13px;background:#f2dede;");
        return;
    }

    var currPos = currentPos[stack-1], position = 7;

    function showThePlot() {
        console.clear();
        position--;
        loopPlot();
        plotPos();
    }

    function loopPlot() {
        if(position >= currPos) {
            setTimeout(function() {
                if(position < 6) {
                    mainObj[stack-1][position+1] = ' ';
                }
                mainObj[stack-1][position] = currentPlayer;
                showThePlot();
            }, time);
        } else {
            currentPos[stack-1] = currPos+1;
            togglePlayer();
        }
    }

    showThePlot();

}

['a','b','c','d','e','f','g'].forEach(function(item, index){
    callWithName(item, function(){
        playAt(index+1);
    })
});

function togglePlayer() {
    currentPlayer = currentPlayer == 'X' ? 'O' : 'X';
    printPlayer();
//    display('Its your turn ' + currentPlayer);
}
function printPlayer() {
    if(currentPlayer === 'X'){
        console.log("Its your turn %cX ", "color:blue;background:blue");
    }else{
        console.log("Its your turn %cO ", "color:red;background:red");
    }
}
init();

<|endoftext|>


<commit_before>var gobble = require( 'gobble' ),
	sander = require( 'sander' ),
	Promise = sander.Promise,
	path = require( 'path' ),
	esperanto = require( 'esperanto' ),
	sandbox = gobble( 'sandbox' ).moveTo( 'sandbox' ),
	version = require( './package.json' ).version,
	es5, lib, test;

var babelTransformWhitelist = [
	'es6.arrowFunctions',
	'es6.blockScoping',
	'es6.constants',
	'es6.destructuring',
	'es6.parameters.default',
	'es6.parameters.rest',
	'es6.properties.shorthand',
	'es6.templateLiterals'
];

es5 = gobble( 'src' ).transform( 'babel', {
	whitelist: babelTransformWhitelist
});

lib = (function () {
	var banner = sander.readFileSync( __dirname, 'src/banner.js' ).toString()
		.replace( '${version}', version )
		.replace( '${time}', new Date() )
		.replace( '${commitHash}', process.env.COMMIT_HASH || 'unknown' );

	var bundleTransform = function ( src, path ) {
		if ( /(Ractive\.js|utils\/log\.js)$/.test( path ) ) {
			return src.replace( '<@version@>', version );
		}

		return src;
	};

	var lib = [
		es5.transform( 'esperanto-bundle', {
			type: 'umd',
			transform: bundleTransform,
			banner: banner,
			entry: 'Ractive.js',
			name: 'Ractive',
			dest: 'ractive-legacy.js'
		})
	];

	if ( gobble.env() === 'production' ) {
		// Add non-legacy and runtime-only builds
		lib.push(
			es5.transform( 'esperanto-bundle', {
				type: 'umd',
				transform: bundleTransform,
				banner: banner,
				entry: 'Ractive.js',
				name: 'Ractive',
				dest: 'ractive.js',
				skip: [ 'legacy' ]
			}),

			es5.transform( 'esperanto-bundle', {
				type: 'umd',
				transform: bundleTransform,
				banner: banner,
				entry: 'Ractive.js',
				name: 'Ractive',
				dest: 'ractive.runtime.js',
				skip: [ 'legacy', 'parse/_parse' ]
			}),

			es5.transform( 'esperanto-bundle', {
				type: 'umd',
				transform: bundleTransform,
				banner: banner,
				entry: 'Ractive.js',
				name: 'Ractive',
				dest: 'ractive-legacy.runtime.js',
				skip: [ 'parse/_parse' ]
			})
		);
	}

	else {
		lib.push( sandbox );
	}

	// Combine sourcemaps from babel and esperanto
	lib = lib.map( function ( node ) {
		return node.transform( 'sorcery' );
	});

	return lib;
})();

test = (function () {
	function compileTemplate ( str ) {
		return function ( data ) {
			return str.replace( /\${([^}]+)}/g, function ( match, $1 ) {
				return $1 in data ? data[ $1 ] : match;
			});
		};
	}

	var templates = {
		testpage: compileTemplate( sander.readFileSync( 'test/templates/testpage.html' ).toString() ),
		index: compileTemplate( sander.readFileSync( 'test/templates/index.html' ).toString() )
	};

	var testModules = gobble([
		gobble([
			gobble( 'test/__tests' ).moveTo( '__tests' ),
			gobble( 'test/testdeps' )
		]).transform( 'babel', {
			whitelist: babelTransformWhitelist,
			sourceMap: false
		}),
		es5
	]).transform( function bundleTests ( inputdir, outputdir, options ) {
		return sander.lsr( inputdir, '__tests' ).then( function ( testModules ) {
			var promises = testModules.sort().map( function ( mod ) {
				return esperanto.bundle({
					base: inputdir,
					entry: '__tests/' + mod
				}).then( function ( bundle ) {
					return sander.writeFile( outputdir, mod, bundle.concat({}).code );
				});
			});

			return Promise.all( promises ).then( function () {
				// index page
				var index = templates.index({
					scriptBlock: testModules.map( function ( src ) {
						return '<script src="' + src + '"></script>';
					}).join( '\n\t' )
				});

				return sander.writeFile( outputdir, 'index.html', index );
			});
		});
	});

	var testPages = testModules.transform( function () {
		return templates.testpage({
			testModule: path.basename( this.filename ),
			name: this.filename.replace( /\.js$/, '' )
		});
	}, { accept: '.js', ext: '.html' });

	return gobble([
		testModules,
		testPages,
		gobble( 'test/root' ),
		gobble( 'test/__nodetests' ).moveTo( '__nodetests' ),
		gobble( 'test/testdeps/samples' )
			.include( '*.js' )
			.transform( 'babel', { whitelist: babelTransformWhitelist, sourceMap: false })
			.transform( 'esperanto', { type: 'cjs', sourceMap: false })
			.moveTo( '__nodetests/samples' )
	]).moveTo( 'test' );
})();

module.exports = gobble([
	lib,
	test
]);
<commit_msg>transform .catch() into ["catch"]()<commit_after>var gobble = require( 'gobble' ),
	sander = require( 'sander' ),
	Promise = sander.Promise,
	path = require( 'path' ),
	esperanto = require( 'esperanto' ),
	sandbox = gobble( 'sandbox' ).moveTo( 'sandbox' ),
	version = require( './package.json' ).version,
	es5, lib, test;

var babelTransformWhitelist = [
	'es3.memberExpressionLiterals',
	'es3.propertyLiterals',
	'es6.arrowFunctions',
	'es6.blockScoping',
	'es6.constants',
	'es6.destructuring',
	'es6.parameters.default',
	'es6.parameters.rest',
	'es6.properties.shorthand',
	'es6.templateLiterals'
];

es5 = gobble( 'src' ).transform( 'babel', {
	whitelist: babelTransformWhitelist
});

lib = (function () {
	var banner = sander.readFileSync( __dirname, 'src/banner.js' ).toString()
		.replace( '${version}', version )
		.replace( '${time}', new Date() )
		.replace( '${commitHash}', process.env.COMMIT_HASH || 'unknown' );

	var bundleTransform = function ( src, path ) {
		if ( /(Ractive\.js|utils\/log\.js)$/.test( path ) ) {
			return src.replace( '<@version@>', version );
		}

		return src;
	};

	var lib = [
		es5.transform( 'esperanto-bundle', {
			type: 'umd',
			transform: bundleTransform,
			banner: banner,
			entry: 'Ractive.js',
			name: 'Ractive',
			dest: 'ractive-legacy.js'
		})
	];

	if ( gobble.env() === 'production' ) {
		// Add non-legacy and runtime-only builds
		lib.push(
			es5.transform( 'esperanto-bundle', {
				type: 'umd',
				transform: bundleTransform,
				banner: banner,
				entry: 'Ractive.js',
				name: 'Ractive',
				dest: 'ractive.js',
				skip: [ 'legacy' ]
			}),

			es5.transform( 'esperanto-bundle', {
				type: 'umd',
				transform: bundleTransform,
				banner: banner,
				entry: 'Ractive.js',
				name: 'Ractive',
				dest: 'ractive.runtime.js',
				skip: [ 'legacy', 'parse/_parse' ]
			}),

			es5.transform( 'esperanto-bundle', {
				type: 'umd',
				transform: bundleTransform,
				banner: banner,
				entry: 'Ractive.js',
				name: 'Ractive',
				dest: 'ractive-legacy.runtime.js',
				skip: [ 'parse/_parse' ]
			})
		);
	}

	else {
		lib.push( sandbox );
	}

	// Combine sourcemaps from babel and esperanto
	lib = lib.map( function ( node ) {
		return node.transform( 'sorcery' );
	});

	return lib;
})();

test = (function () {
	function compileTemplate ( str ) {
		return function ( data ) {
			return str.replace( /\${([^}]+)}/g, function ( match, $1 ) {
				return $1 in data ? data[ $1 ] : match;
			});
		};
	}

	var templates = {
		testpage: compileTemplate( sander.readFileSync( 'test/templates/testpage.html' ).toString() ),
		index: compileTemplate( sander.readFileSync( 'test/templates/index.html' ).toString() )
	};

	var testModules = gobble([
		gobble([
			gobble( 'test/__tests' ).moveTo( '__tests' ),
			gobble( 'test/testdeps' )
		]).transform( 'babel', {
			whitelist: babelTransformWhitelist,
			sourceMap: false
		}),
		es5
	]).transform( function bundleTests ( inputdir, outputdir, options ) {
		return sander.lsr( inputdir, '__tests' ).then( function ( testModules ) {
			var promises = testModules.sort().map( function ( mod ) {
				return esperanto.bundle({
					base: inputdir,
					entry: '__tests/' + mod
				}).then( function ( bundle ) {
					return sander.writeFile( outputdir, mod, bundle.concat({}).code );
				});
			});

			return Promise.all( promises ).then( function () {
				// index page
				var index = templates.index({
					scriptBlock: testModules.map( function ( src ) {
						return '<script src="' + src + '"></script>';
					}).join( '\n\t' )
				});

				return sander.writeFile( outputdir, 'index.html', index );
			});
		});
	});

	var testPages = testModules.transform( function () {
		return templates.testpage({
			testModule: path.basename( this.filename ),
			name: this.filename.replace( /\.js$/, '' )
		});
	}, { accept: '.js', ext: '.html' });

	return gobble([
		testModules,
		testPages,
		gobble( 'test/root' ),
		gobble( 'test/__nodetests' ).moveTo( '__nodetests' ),
		gobble( 'test/testdeps/samples' )
			.include( '*.js' )
			.transform( 'babel', { whitelist: babelTransformWhitelist, sourceMap: false })
			.transform( 'esperanto', { type: 'cjs', sourceMap: false })
			.moveTo( '__nodetests/samples' )
	]).moveTo( 'test' );
})();

module.exports = gobble([
	lib,
	test
]);
<|endoftext|>


<commit_before>/**
 * this is the configuration file. message object has the message body in
 * message.body field.
 * callback function should be called with the result. this result is sent
 * back to the request
 */

var SAMPLE_NO = '1';
var logger = require('../lib/logger');
var samples = require('../samples/sample_' + SAMPLE_NO);
var loadtest;
//sample load testing scenarios
//loadtest = require('../samples/loadtest/direct_proxy');
//loadtest = require('../samples/loadtest/cbr_transportheader_proxy');
//loadtest = require('../samples/loadtest/cbr_soapheader_proxy');

function mediate(message, callback){

	//run the pre defined sample. see ./samples dir
	samples.executeSample(message, callback);

	//run the load testing scenario
	//loadtest.executeTest(message, callback);

}
exports.mediate = mediate;
<commit_msg>change config<commit_after>/**
 * this is the configuration file. message object has the message body in
 * message.body field.
 * callback function should be called with the result. this result is sent
 * back to the request
 */

var SAMPLE_NO = '4';
var logger = require('../lib/logger');
var samples = require('../samples/sample_' + SAMPLE_NO);
var loadtest;
//sample load testing scenarios
//loadtest = require('../samples/loadtest/direct_proxy');
//loadtest = require('../samples/loadtest/cbr_transportheader_proxy');
//loadtest = require('../samples/loadtest/cbr_soapheader_proxy');

function mediate(message, callback){

	//run the pre defined sample. see ./samples dir
	samples.executeSample(message, callback);

	//run the load testing scenario
	//loadtest.executeTest(message, callback);

}
exports.mediate = mediate;
<|endoftext|>


<commit_before>module.exports = function(config) {
  var customLaunchers = {
    sl_chrome: {
      base: 'SauceLabs',
      browserName: 'chrome',
      platform: 'Windows 7',
      version: '35'
    },
    sl_firefox: {
      base: 'SauceLabs',
      browserName: 'firefox',
      version: '30'
    },
    sl_ie_11: {
      base: 'SauceLabs',
      browserName: 'internet explorer',
      platform: 'Windows 8.1',
      version: '11'
    }
  };
  config.set({
    basePath: '',
    frameworks: ['mocha', 'chai-jquery', 'jquery-1.11.0', 'sinon-chai', 'chai', 'fixture'],
    files: [
      'public/javascripts/vendor/d3.v3.min.js',
      'public/javascripts/vendor/raphael-min.js',
      'public/javascripts/vendor/morris.min.js',
      'public/javascripts/helpers/helper.js',
      'public/javascripts/traffic.js',
      'tests/**/*Spec.js',
      'tests/fixtures/**/*'
    ],
    exclude: [
      '**/*.swp'
    ],
    preprocessors: {
      'tests/**/*.json'   : ['json_fixtures']
    },
    jsonFixturesPreprocessor: {
      variableName: '__json__'
    },
    reporters: ['progress', 'saucelabs'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['PhantomJS'],
    singleRun: true,
    sauceLabs: {
      testName: 'Web App Unit Tests',
      tunnelIdentifier: process.env.TRAVIS_JOB_NUMBER,
      username: process.env.SAUCE_USERNAME,
      accessKey: process.env.SAUCE_ACCESS_KEY,
      startConnect: true,
      recordVideo: true,
      recordScreenshots: true
    },
    customLaunchers: customLaunchers,
    browsers: Object.keys(customLaunchers),
    browserDisconnectTimeout : 10000,
    browserDisconnectTolerance : 1,
    browserNoActivityTimeout : 4*60*1000,
    captureTimeout : 4*60*1000
  });
};
<commit_msg>Use IE 10 for now<commit_after>module.exports = function(config) {
  var customLaunchers = {
    sl_chrome: {
      base: 'SauceLabs',
      browserName: 'chrome',
      platform: 'Windows 7',
      version: '35'
    },
    sl_firefox: {
      base: 'SauceLabs',
      browserName: 'firefox',
      version: '30'
    },
    sl_ie_11: {
      base: 'SauceLabs',
      browserName: 'internet explorer',
      platform: 'Windows 8',
      version: '10'
    }
  };
  config.set({
    basePath: '',
    frameworks: ['mocha', 'chai-jquery', 'jquery-1.11.0', 'sinon-chai', 'chai', 'fixture'],
    files: [
      'public/javascripts/vendor/d3.v3.min.js',
      'public/javascripts/vendor/raphael-min.js',
      'public/javascripts/vendor/morris.min.js',
      'public/javascripts/helpers/helper.js',
      'public/javascripts/traffic.js',
      'tests/**/*Spec.js',
      'tests/fixtures/**/*'
    ],
    exclude: [
      '**/*.swp'
    ],
    preprocessors: {
      'tests/**/*.json'   : ['json_fixtures']
    },
    jsonFixturesPreprocessor: {
      variableName: '__json__'
    },
    reporters: ['progress', 'saucelabs'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['PhantomJS'],
    singleRun: true,
    sauceLabs: {
      testName: 'Web App Unit Tests',
      tunnelIdentifier: process.env.TRAVIS_JOB_NUMBER,
      username: process.env.SAUCE_USERNAME,
      accessKey: process.env.SAUCE_ACCESS_KEY,
      startConnect: true,
      recordVideo: true,
      recordScreenshots: true
    },
    customLaunchers: customLaunchers,
    browsers: Object.keys(customLaunchers),
    browserDisconnectTimeout : 10000,
    browserDisconnectTolerance : 1,
    browserNoActivityTimeout : 4*60*1000,
    captureTimeout : 4*60*1000
  });
};
<|endoftext|>


<commit_before>import trimStart from 'lodash/trimStart';
import semaphore from "semaphore";
import AuthenticationPage from "./AuthenticationPage";
import API from "./API";
import { fileExtension } from 'Lib/pathHelper';
import { CURSOR_COMPATIBILITY_SYMBOL } from 'ValueObjects/Cursor';
import { EDITORIAL_WORKFLOW } from "Constants/publishModes";

const MAX_CONCURRENT_DOWNLOADS = 10;

export default class GitLab {
  constructor(config, options={}) {
    this.config = config;
    this.options = {
      proxied: false,
      API: null,
      ...options,
    };

    if (config.getIn(["publish_mode"]) === EDITORIAL_WORKFLOW) {
      throw new Error("The GitLab backend does not support the Editorial Workflow.")
    }

    if (!this.options.proxied && config.getIn(["backend", "repo"]) == null) {
      throw new Error("The GitLab backend needs a \"repo\" in the backend configuration.");
    }

    this.api = this.options.API || null;

    this.repo = config.getIn(["backend", "repo"], "");
    this.branch = config.getIn(["backend", "branch"], "master");
    this.api_root = config.getIn(["backend", "api_root"], "https://gitlab.com/api/v4");
    this.token = '';
  }

  authComponent() {
    return AuthenticationPage;
  }

  restoreUser(user) {
    return this.authenticate(user);
  }

  authenticate(state) {
    this.token = state.token;
    this.api = new API({ token: this.token, branch: this.branch, repo: this.repo, api_root: this.api_root });
    return this.api.user().then(user =>
      this.api.hasWriteAccess(user).then((isCollab) => {
        // Unauthorized user
        if (!isCollab) throw new Error("Your GitLab user account does not have access to this repo.");
        // Authorized user
        return Object.assign({}, user, { token: state.token });
      })
    );
  }

  logout() {
    this.token = null;
    return;
  }

  getToken() {
    return Promise.resolve(this.token);
  }

  entriesByFolder(collection, extension) {
    return this.api.listFiles(collection.get("folder"))
    .then(({ files, cursor }) =>
      this.fetchFiles(files.filter(file => fileExtension(file.name) === extension))
      .then(fetchedFiles => {
        const returnedFiles = fetchedFiles;
        returnedFiles[CURSOR_COMPATIBILITY_SYMBOL] = cursor;
        return returnedFiles;
      })
    );
  }

  allEntriesByFolder(collection, extension) {
    return this.api.listAllFiles(collection.get("folder"))
    .then(files => this.fetchFiles(files.filter(file => fileExtension(file.name) === extension)));
  }

  entriesByFiles(collection) {
    const files = collection.get("files").map(collectionFile => ({
      path: collectionFile.get("file"),
      label: collectionFile.get("label"),
    }));
    return this.fetchFiles(files).then(fetchedFiles => {
      const returnedFiles = fetchedFiles;
      return returnedFiles;
    });
  }

  fetchFiles = (files) => {
    const sem = semaphore(MAX_CONCURRENT_DOWNLOADS);
    const promises = [];
    files.forEach((file) => {
      promises.push(new Promise((resolve, reject) => (
        sem.take(() => this.api.readFile(file.path, file.id).then((data) => {
          resolve({ file, data });
          sem.leave();
        }).catch((error = true) => {
          sem.leave();
          console.error(`failed to load file from GitLab: ${ file.path }`);
          resolve({ error });
        }))
      )));
    });
    return Promise.all(promises)
      .then(loadedEntries => loadedEntries.filter(loadedEntry => !loadedEntry.error));
  };

  // Fetches a single entry.
  getEntry(collection, slug, path) {
    return this.api.readFile(path).then(data => ({
      file: { path },
      data,
    }));
  }

  getMedia() {
    return this.api.listAllFiles(this.config.get('media_folder'))
      .then(files => files.map(({ id, name, path }) => {
        const url = new URL(this.api.fileDownloadURL(path));
        if (url.pathname.match(/.svg$/)) {
          url.search += (url.search.slice(1) === '' ? '?' : '&') + 'sanitize=true';
        }
        return { id, name, url: url.href, path };
      }));
  }

  async persistEntry(entry, mediaFiles, options = {}) {
    return this.api.persistFiles([entry], options);
  }

  async persistMedia(mediaFile, options = {}) {
    await this.api.persistFiles([mediaFile], options);
    const { value, path, fileObj } = mediaFile;
    const url = this.api.fileDownloadURL(path);
    return { name: value, size: fileObj.size, url, path: trimStart(path, '/') };
  }

  deleteFile(path, commitMessage, options) {
    return this.api.deleteFile(path, commitMessage, options);
  }

  traverseCursor(cursor, action) {
    return this.api.traverseCursor(cursor, action)
      .then(async ({ entries, cursor: newCursor }) => ({
        entries: await Promise.all(entries.map(file => this.api.readFile(file.path, file.id).then(data => ({ file, data })))),
        cursor: newCursor,
      }));
  }
}
<commit_msg>Support extensions with multiple parts for gitlab (i.e. en.md)<commit_after>import trimStart from 'lodash/trimStart';
import semaphore from "semaphore";
import AuthenticationPage from "./AuthenticationPage";
import API from "./API";
import { CURSOR_COMPATIBILITY_SYMBOL } from 'ValueObjects/Cursor';
import { EDITORIAL_WORKFLOW } from "Constants/publishModes";

const MAX_CONCURRENT_DOWNLOADS = 10;

export default class GitLab {
  constructor(config, options={}) {
    this.config = config;
    this.options = {
      proxied: false,
      API: null,
      ...options,
    };

    if (config.getIn(["publish_mode"]) === EDITORIAL_WORKFLOW) {
      throw new Error("The GitLab backend does not support the Editorial Workflow.")
    }

    if (!this.options.proxied && config.getIn(["backend", "repo"]) == null) {
      throw new Error("The GitLab backend needs a \"repo\" in the backend configuration.");
    }

    this.api = this.options.API || null;

    this.repo = config.getIn(["backend", "repo"], "");
    this.branch = config.getIn(["backend", "branch"], "master");
    this.api_root = config.getIn(["backend", "api_root"], "https://gitlab.com/api/v4");
    this.token = '';
  }

  authComponent() {
    return AuthenticationPage;
  }

  restoreUser(user) {
    return this.authenticate(user);
  }

  authenticate(state) {
    this.token = state.token;
    this.api = new API({ token: this.token, branch: this.branch, repo: this.repo, api_root: this.api_root });
    return this.api.user().then(user =>
      this.api.hasWriteAccess(user).then((isCollab) => {
        // Unauthorized user
        if (!isCollab) throw new Error("Your GitLab user account does not have access to this repo.");
        // Authorized user
        return Object.assign({}, user, { token: state.token });
      })
    );
  }

  logout() {
    this.token = null;
    return;
  }

  getToken() {
    return Promise.resolve(this.token);
  }

  entriesByFolder(collection, extension) {
    return this.api.listFiles(collection.get("folder"))
    .then(({ files, cursor }) =>
      this.fetchFiles(
        files.filter(file => file.name.endsWith('.' + extension))
      )
      .then(fetchedFiles => {
        const returnedFiles = fetchedFiles;
        returnedFiles[CURSOR_COMPATIBILITY_SYMBOL] = cursor;
        return returnedFiles;
      })
    );
  }

  allEntriesByFolder(collection, extension) {
    return this.api.listAllFiles(collection.get("folder"))
    .then(files =>
      this.fetchFiles(
        files.filter(file => file.name.endsWith('.' + extension))
      )
    );
  }

  entriesByFiles(collection) {
    const files = collection.get("files").map(collectionFile => ({
      path: collectionFile.get("file"),
      label: collectionFile.get("label"),
    }));
    return this.fetchFiles(files).then(fetchedFiles => {
      const returnedFiles = fetchedFiles;
      return returnedFiles;
    });
  }

  fetchFiles = (files) => {
    const sem = semaphore(MAX_CONCURRENT_DOWNLOADS);
    const promises = [];
    files.forEach((file) => {
      promises.push(new Promise((resolve, reject) => (
        sem.take(() => this.api.readFile(file.path, file.id).then((data) => {
          resolve({ file, data });
          sem.leave();
        }).catch((error = true) => {
          sem.leave();
          console.error(`failed to load file from GitLab: ${ file.path }`);
          resolve({ error });
        }))
      )));
    });
    return Promise.all(promises)
      .then(loadedEntries => loadedEntries.filter(loadedEntry => !loadedEntry.error));
  };

  // Fetches a single entry.
  getEntry(collection, slug, path) {
    return this.api.readFile(path).then(data => ({
      file: { path },
      data,
    }));
  }

  getMedia() {
    return this.api.listAllFiles(this.config.get('media_folder'))
      .then(files => files.map(({ id, name, path }) => {
        const url = new URL(this.api.fileDownloadURL(path));
        if (url.pathname.match(/.svg$/)) {
          url.search += (url.search.slice(1) === '' ? '?' : '&') + 'sanitize=true';
        }
        return { id, name, url: url.href, path };
      }));
  }

  async persistEntry(entry, mediaFiles, options = {}) {
    return this.api.persistFiles([entry], options);
  }

  async persistMedia(mediaFile, options = {}) {
    await this.api.persistFiles([mediaFile], options);
    const { value, path, fileObj } = mediaFile;
    const url = this.api.fileDownloadURL(path);
    return { name: value, size: fileObj.size, url, path: trimStart(path, '/') };
  }

  deleteFile(path, commitMessage, options) {
    return this.api.deleteFile(path, commitMessage, options);
  }

  traverseCursor(cursor, action) {
    return this.api.traverseCursor(cursor, action)
      .then(async ({ entries, cursor: newCursor }) => ({
        entries: await Promise.all(entries.map(file => this.api.readFile(file.path, file.id).then(data => ({ file, data })))),
        cursor: newCursor,
      }));
  }
}
<|endoftext|>


<commit_before>const { Router } = require('express');
const userCtrl = require('./userCtrl');

const userRoute = module.exports = new Router();

userRoute.post('/login');
userRoute.post('/signup');
<commit_msg>added test logging for user service routing endpoints<commit_after>const { Router } = require('express');
const userCtrl = require('./userCtrl');

const userRoute = module.exports = new Router();

userRoute.post('/login', (req, res) => {
  console.log('/login route was hit!');
});

userRoute.post('/signup', (req, res) => {
  console.log('/signup route was hit!');
});
<|endoftext|>


<commit_before>// ==UserScript==
// @name         GitHub Collapse In Comment
// @version      1.0.5
// @description  A userscript that adds a header that can toggle long code and quote blocks in comments
// @license      https://creativecommons.org/licenses/by-sa/4.0/
// @namespace    https://github.com/Mottie
// @include      https://github.com/*
// @include      https://gist.github.com/*
// @run-at       document-idle
// @grant        GM_addStyle
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// @author       Rob Garrison
// @updateURL    https://raw.githubusercontent.com/Mottie/GitHub-userscripts/master/github-collapse-in-comment.user.js
// @downloadURL  https://raw.githubusercontent.com/Mottie/GitHub-userscripts/master/github-collapse-in-comment.user.js
// ==/UserScript==
/* global GM_addStyle, GM_getValue, GM_setValue, GM_registerMenuCommand */
/* jshint esnext:true, unused:true */
(() => {
	"use strict";
	/*
	Idea from: https://github.com/dear-github/dear-github/issues/166 & https://github.com/isaacs/github/issues/208
	examples:
		https://github.com/Mottie/tablesorter/issues/569
		https://github.com/jquery/jquery/issues/3195
	*/
	let timer,
		busy = false,

		// hide code/quotes longer than this number of lines
		minLines = GM_getValue("gcic-max-lines", 10),
		startCollapsed = GM_getValue("gcic-start-collapsed", true);

	// syntax highlight class name lookup table
	const syntaxClass = {
		basic: "HTML",
		cs: "C#",
		fsharp: "F#",
		gfm: "Markdown",
		jq: "JSONiq",
		shell: "Bash (shell)",
		tcl: "Glyph",
		tex: "LaTex"
	};

	GM_addStyle(`
		.gcic-block {
			border:#eee 1px solid;
			padding:2px 8px 2px 10px;
			border-radius:5px 5px 0 0;
			position:relative;
			top:1px;
			cursor:pointer;
			font-weight:bold;
			display:block;
		}
		.gcic-block + .highlight {
			border-top:none;
		}
		.gcic-block + .email-signature-reply {
			margin-top:0;
		}
		.gcic-block:after {
			content:"\u25bc ";
			float:right;
		}
		.gcic-block-closed {
			border-radius:5px;
			margin-bottom:10px;
		}
		.gcic-block-closed:after {
			transform: rotate(90deg);
		}
		.gcic-block-closed + .highlight, .gcic-block-closed + .email-signature-reply,
		.gcic-block-closed + pre {
			display:none;
		}
	`);

	function makeToggle(name, lines) {
		/* full list of class names from (look at "tm_scope" value)
		https://github.com/github/linguist/blob/master/lib/linguist/languages.yml
		here are some example syntax highlighted class names:
			highlight-text-html-markdown-source-gfm-apib
			highlight-text-html-basic
			highlight-source-fortran-modern
			highlight-text-tex
		*/
		let n = (name || "").replace(
			/(highlight[-\s]|(source-)|(text-)|(html-)|(markdown-)|(-modern))/g, ""
		);
		n = (syntaxClass[n] || n).toUpperCase().trim();
		return `${n || "Block"} (${lines} lines)`;
	}

	function addToggles() {
		busy = true;
		// issue comments
		if ($("#discussion_bucket")) {
			let indx = 0;
			const block = document.createElement("a"),
				els = $$(".markdown-body pre, .email-signature-reply"),
				len = els.length;

			// "flash" = blue box styling
			block.className = `gcic-block border flash${
				startCollapsed ? " gcic-block-closed" : ""
			}`;
			block.href = "#";

			// loop with delay to allow user interaction
			const loop = () => {
				let el, wrap, node, syntaxClass, numberOfLines,
					// max number of DOM insertions per loop
					max = 0;
				while (max < 20 && indx < len) {
					if (indx >= len) {
						return;
					}
					el = els[indx];
					if (el && !el.classList.contains("gcic-has-toggle")) {
						numberOfLines = el.innerHTML.split("\n").length;
						if (numberOfLines > minLines) {
							syntaxClass = "";
							wrap = closest(".highlight", el);
							if (wrap && wrap.classList.contains("highlight")) {
								syntaxClass = wrap.className;
							} else {
								// no syntax highlighter defined (not wrapped)
								wrap = el;
							}
							node = block.cloneNode();
							node.innerHTML = makeToggle(syntaxClass, numberOfLines);
							wrap.parentNode.insertBefore(node, wrap);
							el.classList.add("gcic-has-toggle");
							if (startCollapsed) {
								el.display = "none";
							}
							max++;
						}
					}
					indx++;
				}
				if (indx < len) {
					setTimeout(() => {
						loop();
					}, 200);
				}
			};
			loop();
		}
		busy = false;
	}

	function addBindings() {
		document.addEventListener("click", event => {
			let els, indx, flag;
			const el = event.target;
			if (el && el.classList.contains("gcic-block")) {
				event.preventDefault();
				// shift + click = toggle all blocks in a single comment
				// shift + ctrl + click = toggle all blocks on page
				if (event.shiftKey) {
					els = $$(".gcic-block", event.ctrlKey ? "" : (el, ".markdown-body"));
					indx = els.length;
					flag = el.classList.contains("gcic-block-closed");
					while (indx--) {
						els[indx].classList.toggle("gcic-block-closed", !flag);
					}
				} else {
					el.classList.toggle("gcic-block-closed");
				}
				removeSelection();
			}
		});
	}

	function update() {
		busy = true;
		let toggles = $$(".gcic-block"),
			indx = toggles.length;
		while (indx--) {
			toggles[indx].parentNode.removeChild(toggles[indx]);
		}
		toggles = $$(".gcic-has-toggle");
		indx = toggles.length;
		while (indx--) {
			toggles[indx].classList.remove("gcic-has-toggle");
		}
		addToggles();
	}

	function $(selector, el) {
		return (el || document).querySelector(selector);
	}

	function $$(selector, el) {
		return Array.from((el || document).querySelectorAll(selector));
	}

	function closest(selector, el) {
		while (el && el.nodeType === 1) {
			if (el.matches(selector)) {
				return el;
			}
			el = el.parentNode;
		}
		return null;
	}

	function removeSelection() {
		// remove text selection - http://stackoverflow.com/a/3171348/145346
		const sel = window.getSelection ? window.getSelection() : document.selection;
		if (sel) {
			if (sel.removeAllRanges) {
				sel.removeAllRanges();
			} else if (sel.empty) {
				sel.empty();
			}
		}
	}

	GM_registerMenuCommand("Set GitHub Collapse In Comment Max Lines", () => {
		let val = prompt("Minimum number of lines before adding a toggle:",
			minLines);
		val = parseInt(val, 10);
		if (val) {
			minLines = val;
			GM_setValue("gcic-max-lines", val);
			update();
		}
	});

	GM_registerMenuCommand("Set GitHub Collapse In Comment Initial State", () => {
		let val = prompt(
			"Start with blocks (c)ollapsed or (e)xpanded (first letter necessary):",
			startCollapsed ? "collapsed" : "expanded"
		);
		if (val) {
			val = /^c/.test(val || "");
			startCollapsed = val;
			GM_setValue("gcic-start-collapsed", val);
			update();
		}
	});

	$$("#js-repo-pjax-container, #js-pjax-container").forEach(target => {
		new MutationObserver(mutations => {
			mutations.forEach(mutation => {
				const mtarget = mutation.target;
				// preform checks before adding code wrap to minimize function calls
				// update after comments are edited
				if (!busy && (mtarget === target || mtarget.matches(
						".js-comment-body, .js-preview-body"))) {
					clearTimeout(timer);
					timer = setTimeout(() => {
						addToggles();
					}, 100);
				}
			});
		}).observe(target, {
			childList: true,
			subtree: true
		});
	});

	addBindings();
	addToggles();
})();
<commit_msg>Collapse-in-comment: use pjax & preview events<commit_after>// ==UserScript==
// @name         GitHub Collapse In Comment
// @version      1.0.6
// @description  A userscript that adds a header that can toggle long code and quote blocks in comments
// @license      https://creativecommons.org/licenses/by-sa/4.0/
// @namespace    https://github.com/Mottie
// @include      https://github.com/*
// @include      https://gist.github.com/*
// @run-at       document-idle
// @grant        GM_addStyle
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// @author       Rob Garrison
// @updateURL    https://raw.githubusercontent.com/Mottie/GitHub-userscripts/master/github-collapse-in-comment.user.js
// @downloadURL  https://raw.githubusercontent.com/Mottie/GitHub-userscripts/master/github-collapse-in-comment.user.js
// ==/UserScript==
(() => {
	"use strict";
	/*
	Idea from: https://github.com/dear-github/dear-github/issues/166 &
		https://github.com/isaacs/github/issues/208
	examples:
		https://github.com/Mottie/tablesorter/issues/569
		https://github.com/jquery/jquery/issues/3195
	*/
	// hide code/quotes longer than this number of lines
	let minLines = GM_getValue("gcic-max-lines", 10),
		startCollapsed = GM_getValue("gcic-start-collapsed", true);

	// syntax highlight class name lookup table
	const syntaxClass = {
		basic: "HTML",
		cs: "C#",
		fsharp: "F#",
		gfm: "Markdown",
		jq: "JSONiq",
		shell: "Bash (shell)",
		tcl: "Glyph",
		tex: "LaTex"
	};

	GM_addStyle(`
		.gcic-block {
			border:#eee 1px solid;
			padding:2px 8px 2px 10px;
			border-radius:5px 5px 0 0;
			position:relative;
			top:1px;
			cursor:pointer;
			font-weight:bold;
			display:block;
		}
		.gcic-block + .highlight {
			border-top:none;
		}
		.gcic-block + .email-signature-reply {
			margin-top:0;
		}
		.gcic-block:after {
			content:"\u25bc ";
			float:right;
		}
		.gcic-block-closed {
			border-radius:5px;
			margin-bottom:10px;
		}
		.gcic-block-closed:after {
			transform: rotate(90deg);
		}
		.gcic-block-closed + .highlight, .gcic-block-closed + .email-signature-reply,
		.gcic-block-closed + pre {
			display:none;
		}
	`);

	function makeToggle(name, lines) {
		/* full list of class names from (look at "tm_scope" value)
		https://github.com/github/linguist/blob/master/lib/linguist/languages.yml
		here are some example syntax highlighted class names:
			highlight-text-html-markdown-source-gfm-apib
			highlight-text-html-basic
			highlight-source-fortran-modern
			highlight-text-tex
		*/
		let n = (name || "").replace(
			/(highlight[-\s]|(source-)|(text-)|(html-)|(markdown-)|(-modern))/g, ""
		);
		n = (syntaxClass[n] || n).toUpperCase().trim();
		return `${n || "Block"} (${lines} lines)`;
	}

	function addToggles() {
		// issue comments
		if ($("#discussion_bucket")) {
			let indx = 0;
			const block = document.createElement("a"),
				els = $$(".markdown-body pre, .email-signature-reply"),
				len = els.length;

			// "flash" = blue box styling
			block.className = `gcic-block border flash${
				startCollapsed ? " gcic-block-closed" : ""
			}`;
			block.href = "#";

			// loop with delay to allow user interaction
			const loop = () => {
				let el, wrap, node, syntaxClass, numberOfLines,
					// max number of DOM insertions per loop
					max = 0;
				while (max < 20 && indx < len) {
					if (indx >= len) {
						return;
					}
					el = els[indx];
					if (el && !el.classList.contains("gcic-has-toggle")) {
						numberOfLines = el.innerHTML.split("\n").length;
						if (numberOfLines > minLines) {
							syntaxClass = "";
							wrap = closest(".highlight", el);
							if (wrap && wrap.classList.contains("highlight")) {
								syntaxClass = wrap.className;
							} else {
								// no syntax highlighter defined (not wrapped)
								wrap = el;
							}
							node = block.cloneNode();
							node.innerHTML = makeToggle(syntaxClass, numberOfLines);
							wrap.parentNode.insertBefore(node, wrap);
							el.classList.add("gcic-has-toggle");
							if (startCollapsed) {
								el.display = "none";
							}
							max++;
						}
					}
					indx++;
				}
				if (indx < len) {
					setTimeout(() => {
						loop();
					}, 200);
				}
			};
			loop();
		}
	}

	function addBindings() {
		document.addEventListener("click", event => {
			let els, indx, flag;
			const el = event.target;
			if (el && el.classList.contains("gcic-block")) {
				event.preventDefault();
				// shift + click = toggle all blocks in a single comment
				// shift + ctrl + click = toggle all blocks on page
				if (event.shiftKey) {
					els = $$(".gcic-block", event.ctrlKey ? "" : (el, ".markdown-body"));
					indx = els.length;
					flag = el.classList.contains("gcic-block-closed");
					while (indx--) {
						els[indx].classList.toggle("gcic-block-closed", !flag);
					}
				} else {
					el.classList.toggle("gcic-block-closed");
				}
				removeSelection();
			}
		});
	}

	function update() {
		let toggles = $$(".gcic-block"),
			indx = toggles.length;
		while (indx--) {
			toggles[indx].parentNode.removeChild(toggles[indx]);
		}
		toggles = $$(".gcic-has-toggle");
		indx = toggles.length;
		while (indx--) {
			toggles[indx].classList.remove("gcic-has-toggle");
		}
		addToggles();
	}

	function $(selector, el) {
		return (el || document).querySelector(selector);
	}

	function $$(selector, el) {
		return Array.from((el || document).querySelectorAll(selector));
	}

	function closest(selector, el) {
		while (el && el.nodeType === 1) {
			if (el.matches(selector)) {
				return el;
			}
			el = el.parentNode;
		}
		return null;
	}

	function removeSelection() {
		// remove text selection - https://stackoverflow.com/a/3171348/145346
		const sel = window.getSelection ? window.getSelection() : document.selection;
		if (sel) {
			if (sel.removeAllRanges) {
				sel.removeAllRanges();
			} else if (sel.empty) {
				sel.empty();
			}
		}
	}

	GM_registerMenuCommand("Set GitHub Collapse In Comment Max Lines", () => {
		let val = prompt("Minimum number of lines before adding a toggle:",
			minLines);
		val = parseInt(val, 10);
		if (val) {
			minLines = val;
			GM_setValue("gcic-max-lines", val);
			update();
		}
	});

	GM_registerMenuCommand("Set GitHub Collapse In Comment Initial State", () => {
		let val = prompt(
			"Start with blocks (c)ollapsed or (e)xpanded (first letter necessary):",
			startCollapsed ? "collapsed" : "expanded"
		);
		if (val) {
			val = /^c/.test(val || "");
			startCollapsed = val;
			GM_setValue("gcic-start-collapsed", val);
			update();
		}
	});

	document.addEventListener("pjax:end", addToggles);
	// "preview:render" only fires when using the hotkey :(
	// "preview:setup" fires on hover & click of comment preview tab
	document.addEventListener("preview:setup", () => {
		setTimeout(() => {
			// must include some rendering time...
			// 200 ms seems to be enough for a 1100+ line markdown file
			addToggles();
		}, 500);
	});
	addBindings();
	addToggles();
})();
<|endoftext|>


<commit_before>var app;

/**
 * Get or set agreements
 *
 * @callback callback
 * @param [agreementId] {string} - agreement ID to add to account
 * @param callback {function} - `function (err, data) {}`
 * @returns {void}
 */

function agreements (agreementId, callback) {
  var options = {
    method: 'GET',
    path: '/api/toon/v1/agreements',
    params: {}
  };

  if (typeof agreementId === 'function') {
    callback = agreementId;
    agreementId = null;
  } else {
    options.method = 'POST';
    options.params.agreementId = agreementId;
  }

  app.httpRequest (options, callback);
}

/**
 * Set up lib
 *
 * @param parent {object} - Main module utils
 * @returns {object} - Interface methods
 */

function agreementsSetup (parent) {
  app = parent;

  return agreements;
}

module.exports = agreementsSetup;
<commit_msg>Style(methods): Method .agreements clean up<commit_after>var app;

/**
 * Get or set agreements
 *
 * @callback callback
 * @param [agreementId] {string} - agreement ID to add to account
 * @param callback {function} - `function (err, data) {}`
 * @returns {void}
 */

function agreements (agreementId, callback) {
  var options = {
    method: 'GET',
    path: '/api/toon/v1/agreements'
  };

  if (typeof agreementId === 'function') {
    callback = agreementId;
    agreementId = null;
  } else {
    options.method = 'POST';
    options.params = {
      agreementId
    };
  }

  app.httpRequest (options, callback);
}

/**
 * Set up lib
 *
 * @param parent {object} - Main module utils
 * @returns {object} - Interface methods
 */

function agreementsSetup (parent) {
  app = parent;

  return agreements;
}

module.exports = agreementsSetup;
<|endoftext|>


<commit_before>const global_declare_cache = {__proto__:null};
const global_read_cache = {__proto__:null};
const global_write_cache = {__proto__:null};

aran.object = (prototype, ... properties) => {
  const object = {__proto__:null};
  if (properties.length % 2 !== 0) {
    throw new Error("Properties length mismatch");
  }
  const length = properties.length / 2;
  for (const index = 0; index < length; index += 1) {
    object[properties[2 * index]] = properties[2 * index + 1];
  }
  setPrototypeOf(object, prototype);
  return object;
};
aran.throw = (error) => { throw error; };
aran.unary = (operator, argument) => {};
aran.binary = (operator, left, right) => {};
aran.declareGlobal = (kind, variable, value) => {
  global.__HIDDEN__ = value;
  global.eval(`${kind} ${variable} = __HIDDEN__;`);
};
aran.readGlobal = (variable) => {
  if (variable in global_typeof_cache) {
    return global_typeof_cache[variable]();
  } else {
    assert(variable !== "arguments");
    const typeofVariable = new global.Function(`return ${variable};`);
    global_typeof_cache[variable] = typeofVariable;
    return typeofVariable();
  }
};
aran.typeofGlobal = (variable) => {
  if (variable in global_typeof_cache) {
    return global_typeof_cache[variable]();
  } else {
    assert(variable !== "arguments");
    const typeofVariable = new Function(`return typeof ${variable};`);
    global_typeof_cache[variable] = typeofVariable;
    return typeofVariable();
  }
};
aran.writeGlobal = (variable, value) => {
  if (variable in global_write_cache) {
    global_write_cache[variable](value);
  } else {
    assert(variable !== "__HIDDEN__");
    assert(variable !== "arguments");
    const writeVariable = new Function("__HIDDEN__", `${variable} = __HIDDEN__;`);
    global_write_cache[variable] = writeVariable;
    writeVariable(value)
  }
};
aran.get = (object, key) => object[key];
aran.setSloppy = (object, key, value) => {
  Reflect.set(Object(object), key, value);
  return value;
};
aran.setStrict = (object, key, value) => object[key] = value;

// Global Code //

ScriptDeclareStatement = ["kind", "variable", "expression"];

// Module //
input = {
  __proto__: null,
  "this": this,
  "import": (source) => import(source),
  "import.meta": import.meta,
};

// Script
input = {
  __proto__: null,
  "this": this,
  "import": (source) => import("source"),
};

// LocalEval [Identifier]
input = {__proto__:null};

// LocalEval //
const input = {__proto__:null};

// GlobalEval //
const input = {
  __proto__: null,
  this: this,
  import: (source) => import(source),
};

// EnclaveEval //
const cache_write = {__proto__:null};
const input = {
  __proto__: null,
  "this": this,
  "import": (source) => import(source),
  "new.target": [enclaves.includes("new.target") ? "new.target" : "null"],
  "super.set": [enclaves.includes("super.set")
    ? "(key, value) => { super[key] = value; }"
    : "() => { throw new SyntaxError('Cannot access super property'); }"
  ],
  "super.get": [enclaves.includes("super.get")
    ? "(key) => super[key]"
    : "() => { throw new SyntaxError('Cannot access super property'); }"
  ],
  "super.call": [enclaves.includes("super.get")
    ? "(values) => super(... values)"
    : "() => { throw new SyntaxError('Cannot call super'); }"
  ],
  "scope.typeof": (variable) => eval(`(typeof ${variable});`),
  "scope.read": (variable) => eval(`(${variable});`),
  "scope.write": (variable, value) => {
    if (variable in cache_write) {
      return cache_write[variable](value);
    } else {
      assert(isIdentifier(variable));
      assert(variable !== "__HIDDEN__");
      const writeVariable = eval(`((__HIDDEN__) => ${variable} = __HIDDEN__);`);
      cache_write[variable] = writeVariable;
      return writeVariable(value);
    }
  },
};

// Local Eval //

const input = {__proto__: null};

// Invoke //

InvokeExpression: (context, expression1, expression2, expressions, serial) => {
  if (
    cut(context.trap.pointcut, "apply", null, null, repeat(null, expressions.length), serial)
  ) {
    const this_variable = makeMetaVariable(`this_${String(incrementCounter(context.counter))}`);
    declareScopeVariable(context.scope, {
      variable: makeOldVariable(this_variable),
      value: null,
      duplicable: false,
      initialized: true,
    });
    return visitExpression(
      context,
      makeSequenceExpression(
        makeWriteEffect(
          this_variable,
          expression1,
        ),
        makeApplyExpression(
          makeApplyExpression(
            makeInstrinsicExpression("aran.get"),
            makeLiteralExpression({undefined:null}),
            [makeReadExpression(this_variable), expression2],
          ),
          makeReadExpression(this_variable),
          expressions,
        ),
      ),
    );
  } else {
    return makeInvokeExpression(
      visitExpression(context, expression1),
      visitExpression(context, expression2),
      map(expressions, (expression) => visitExpression(context, expression)),
    );
  }
}
<commit_msg>doc: minor changes in the roadmap<commit_after>const global_declare_cache = {__proto__:null};
const global_read_cache = {__proto__:null};
const global_write_cache = {__proto__:null};

aran.object = (prototype, ... properties) => {
  const object = {__proto__:null};
  if (properties.length % 2 !== 0) {
    throw new Error("Properties length mismatch");
  }
  const length = properties.length / 2;
  for (const index = 0; index < length; index += 1) {
    object[properties[2 * index]] = properties[2 * index + 1];
  }
  setPrototypeOf(object, prototype);
  return object;
};
aran.throw = (error) => { throw error; };
aran.unary = (operator, argument) => {};
aran.binary = (operator, left, right) => {};
aran.declareGlobal = (kind, variable, value) => {
  global.__HIDDEN__ = value;
  global.eval(`${kind} ${variable} = __HIDDEN__;`);
};
aran.readGlobal = (variable) => {
  if (variable in global_typeof_cache) {
    return global_typeof_cache[variable]();
  } else {
    assert(variable !== "arguments");
    const typeofVariable = new global.Function(`return ${variable};`);
    global_typeof_cache[variable] = typeofVariable;
    return typeofVariable();
  }
};
aran.typeofGlobal = (variable) => {
  if (variable in global_typeof_cache) {
    return global_typeof_cache[variable]();
  } else {
    assert(variable !== "arguments");
    const typeofVariable = new Function(`return typeof ${variable};`);
    global_typeof_cache[variable] = typeofVariable;
    return typeofVariable();
  }
};
aran.writeGlobal = (variable, value) => {
  if (variable in global_write_cache) {
    global_write_cache[variable](value);
  } else {
    assert(variable !== "__HIDDEN__");
    assert(variable !== "arguments");
    const writeVariable = new Function("__HIDDEN__", `${variable} = __HIDDEN__;`);
    global_write_cache[variable] = writeVariable;
    writeVariable(value)
  }
};
aran.get = (object, key) => object[key];
aran.setSloppy = (object, key, value) => {
  Reflect.set(Object(object), key, value);
  return value;
};
aran.setStrict = (object, key, value) => object[key] = value;

// Global Code //

ScriptDeclareStatement = ["kind", "variable", "expression"];

// Module //
input = {
  __proto__: null,
  "this": this,
  "import": (source) => import(source),
  "import.meta": import.meta,
};

// Script //
input = {
  __proto__: null,
  "this": this,
  "import": (source) => import("source"),
};

// LocalEval //
const input = {__proto__:null};

// GlobalEval //
const input = {
  __proto__: null,
  this: this,
  import: (source) => import(source),
};

// EnclaveEval //
const cache_write = {__proto__:null};
const input = {
  __proto__: null,
  "this": this,
  "import": (source) => import(source),
  "import.meta": [enclaves.includes("import.meta") ? "import.meta" : null],
  "new.target": [enclaves.includes("new.target") ? "new.target" : null],
  "super.set": [enclaves.includes("super.set")
    ? "(key, value) => { super[key] = value; }"
    : "() => { throw new SyntaxError('Cannot access super property'); }"
  ],
  "super.get": [enclaves.includes("super.get")
    ? "(key) => super[key]"
    : "() => { throw new SyntaxError('Cannot access super property'); }"
  ],
  "super.call": [enclaves.includes("super.get")
    ? "(values) => super(... values)"
    : "() => { throw new SyntaxError('Cannot call super'); }"
  ],
  "scope.delete": (variable) => eval(`(delete ${variable});`),
  "scope.typeof": (variable) => eval(`(typeof ${variable});`),
  "scope.read": (variable) => eval(`(${variable});`),
  "scope.write": (variable, value) => {
    if (variable in cache_write) {
      return cache_write[variable](value);
    } else {
      assert(isIdentifier(variable));
      assert(variable !== "__HIDDEN__");
      const writeVariable = eval(`((__HIDDEN__) => ${variable} = __HIDDEN__);`);
      cache_write[variable] = writeVariable;
      return writeVariable(value);
    }
  },
};

// Invoke //

InvokeExpression: (context, expression1, expression2, expressions, serial) => {
  if (
    cut(context.trap.pointcut, "apply", null, null, repeat(null, expressions.length), serial)
  ) {
    const this_variable = makeMetaVariable(`this_${String(incrementCounter(context.counter))}`);
    declareScopeVariable(context.scope, {
      variable: makeOldVariable(this_variable),
      value: null,
      duplicable: false,
      initialized: true,
    });
    return visitExpression(
      context,
      makeSequenceExpression(
        makeWriteEffect(
          this_variable,
          expression1,
        ),
        makeApplyExpression(
          makeApplyExpression(
            makeInstrinsicExpression("aran.get"),
            makeLiteralExpression({undefined:null}),
            [makeReadExpression(this_variable), expression2],
          ),
          makeReadExpression(this_variable),
          expressions,
        ),
      ),
    );
  } else {
    return makeInvokeExpression(
      visitExpression(context, expression1),
      visitExpression(context, expression2),
      map(expressions, (expression) => visitExpression(context, expression)),
    );
  }
}
<|endoftext|>