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

var igv = (function (igv) {

    var dragged,
        dragDestination;

    igv.TrackView = function (browser, $container, track) {

        var self = this,
            width,
            $track,
            config;

        this.browser = browser;

        $track = $('
');
        this.trackDiv = $track.get(0);
        $container.append($track);

        this.track = track;
        track.trackView = this;

        if (this.track instanceof igv.RulerTrack) {
            this.trackDiv.dataset.rulerTrack = "rulerTrack";
        }

        if (track.height) {
            this.trackDiv.style.height = track.height + "px";
        }

        if (typeof track.paintAxis === 'function') {
            appendLeftHandGutter.call(this, $(this.trackDiv));
        }

        this.$viewportContainer = $('
');
        $(this.trackDiv).append(this.$viewportContainer);

        this.viewports = [];
        width = this.browser.viewportContainerWidth() / this.browser.genomicStateList.length;
        browser.genomicStateList.forEach(function (genomicState) {

            var viewport;
            viewport = new igv.Viewport(self, self.$viewportContainer, genomicState, width);
            self.viewports.push(viewport);

        });

        this.decorateViewports();

        this.configureViewportContainer(this.$viewportContainer, this.viewports);

        if (true === this.track.ignoreTrackMenu) {
            // do nothing
        } else {
            appendRightHandGutter.call(this, $(this.trackDiv));
        }

        if (this.track instanceof igv.RulerTrack) {
            // do nuthin
        } else {
            attachDragWidget.call(this, $(this.trackDiv), this.$viewportContainer);
        }

        if (igv.doProvideColoSwatchWidget(this.track)) {

            config =
            {
                // width = (29 * swatch-width) + border-width + border-width
                width: ((29 * 24) + 1 + 1),
                classes: ['igv-position-absolute']
            };

            this.$colorpicker_container = igv.genericContainer($track, config, function () {
                self.$colorpicker_container.toggle();
            });

            igv.createColorSwatchSelector(this.$colorpicker_container, function (rgb) {
                self.setColor(rgb);
            });

            this.$colorpicker_container.hide();
        }

    };

    igv.TrackView.prototype.configureViewportContainer = function ($viewportContainer, viewports) {

        if ("hidden" === $viewportContainer.css("overflow-y")) {

            // TODO - Handle replacement for contentDiv. Use height calculating closure?
            this.scrollbar = new igv.TrackScrollbar($viewportContainer, viewports);
            // this.scrollbar.update();
            $viewportContainer.append(this.scrollbar.$outerScroll);
        }

        return $viewportContainer;
    };

    igv.TrackView.prototype.removeViewportWithLocusIndex = function (index) {

        if (this.track instanceof igv.RulerTrack) {
            this.track.removeRulerSweeperWithLocusIndex(index);
        }

        this.viewports[index].$viewport.remove();
        this.viewports.splice(index, 1);

        this.decorateViewports();
    };

    igv.TrackView.prototype.viewportWithGenomicState = function (genomicState) {
        var i, viewport;
        for(i=0; i<this.viewports.length; i++) {
            viewport = this.viewports[i];
            if (viewport.genomicState === genomicState) {
                return viewport;
            }
        }
        return undefined;
    };

    igv.TrackView.prototype.decorateViewports = function () {
        var self = this;

        this.viewports.forEach(function (viewport, index) {
            var $viewport;

            $viewport = viewport.$viewport;

            if (self.viewports.length > 1) {
                $viewport.find('.igv-viewport-fa-close').show();
                $viewport.find('.igv-viewport-content-ruler-div').show();
            } else {
                $viewport.find('.igv-viewport-fa-close').hide();
                $viewport.find('.igv-viewport-content-ruler-div').hide();
            }

            if (index < self.viewports.length && (1 + index) !== self.viewports.length) {
                $viewport.addClass('igv-viewport-div-border-right');
            } else {
                $viewport.removeClass('igv-viewport-div-border-right');
            }

        });

    };

    function appendLeftHandGutter($parent) {

        var self = this,
            $leftHandGutter,
            $canvas;

        $leftHandGutter = $('
');
        $parent.append($leftHandGutter);

        if (this.track.dataRange) {

            $leftHandGutter.click(function (e) {
                // igv.dataRangeDialog.configureWithTrackView(self);
                igv.dataRangeDialog.configure({ trackView: self });
                igv.dataRangeDialog.present($(self.trackDiv));
            });

            $leftHandGutter.addClass('igv-clickable');
        }

        $canvas = $('<canvas class ="igv-track-control-canvas">');
        $leftHandGutter.append($canvas);

        $canvas.attr('width', $leftHandGutter.outerWidth());
        $canvas.attr('height', $leftHandGutter.outerHeight());

        this.controlCanvas = $canvas.get(0);
        this.controlCtx = this.controlCanvas.getContext("2d");

    }

    function appendRightHandGutter($parent) {

        var self = this,
            $gearButton, $fa;

        this.rightHandGutter = $('
')[0];
        $parent.append($(this.rightHandGutter));

        $gearButton = igv.createWrappedIcon("cog");
        $(this.rightHandGutter).append($gearButton);

        $gearButton.click(function (e) {
            igv.popover.presentTrackGearMenu(e.pageX, e.pageY, self);
        });

    }

    function attachDragWidget($track, $viewportContainer) {

        var self = this,
            indexDestination,
            indexDragged;

        this.$trackDragScrim = $('
');
        $viewportContainer.append(this.$trackDragScrim);
        this.$trackDragScrim.hide();

        self.$trackManipulationHandle = $('
');
        $track.append(self.$trackManipulationHandle);

        self.$trackManipulationHandle.on('mousedown.trackview', function (e) {
            e.preventDefault();
            self.isMouseDown = true;
            dragged = self;
        });

        self.$trackManipulationHandle.on('mouseup.trackview', function (e) {
            e.preventDefault();
            self.isMouseDown = undefined;
        });

        self.$trackManipulationHandle.on('mouseenter.trackview', function (e) {
            e.preventDefault();
            self.isMouseIn = true;
            dragDestination = self;

            if (undefined === dragged) {
                self.$trackDragScrim.show();
            } else if (self === dragged) {
                self.$trackDragScrim.show();
            }

            if ((dragDestination && dragged) && (dragDestination !== dragged)) {

                indexDestination = igv.browser.trackViews.indexOf(dragDestination);
                indexDragged = igv.browser.trackViews.indexOf(dragged);

                igv.browser.trackViews[indexDestination] = dragged;
                igv.browser.trackViews[indexDragged] = dragDestination;

                if (indexDestination < indexDragged) {
                    $(dragged.trackDiv).insertBefore($(dragDestination.trackDiv));
                } else {
                    $(dragged.trackDiv).insertAfter($(dragDestination.trackDiv));
                }

            }

        });

        self.$trackManipulationHandle.on('mouseleave.trackview', function (e) {
            e.preventDefault();
            self.isMouseIn = undefined;
            dragDestination = undefined;

            if (self !== dragged) {
                self.$trackDragScrim.hide();
            }

        });

        $(document).on('mouseup.document.trackview', function (e) {

            if (dragged) {
                dragged.$trackDragScrim.hide();
            }

            dragged = undefined;
        });

    }

    igv.TrackView.prototype.dataRange = function () {
        return this.track.dataRange ? this.track.dataRange : undefined;
    };

    igv.TrackView.prototype.setDataRange = function (min, max, autoscale) {

        if (min !== undefined) {
            this.track.dataRange.min = min;
        }

        if (max !== undefined) {
            this.track.dataRange.max = max;
        }

        this.track.autoscale = autoscale;

        this.repaint(true);
    };

    igv.TrackView.prototype.setColor = function (color) {
        this.track.color = color;
        this.repaint(true);
    };

    igv.TrackView.prototype.setTrackHeight = function (newHeight, update, force) {

        if (!force) {
            if (this.track.minHeight) {
                newHeight = Math.max(this.track.minHeight, newHeight);
            }

            if (this.track.maxHeight) {
                newHeight = Math.min(this.track.maxHeight, newHeight);
            }
        }

        this.track.height = newHeight;
        $(this.trackDiv).height(newHeight);

        // If the track does not manage its own content height set it here
        if (typeof this.track.computePixelHeight !== "function") {
            this.viewports.forEach(function (vp) {
                vp.setContentHeight(newHeight);
                vp.tile.invalidate = true;
            });
            self.repaint(true);
        }

        if (this.track.paintAxis) {
            $(this.controlCanvas).height(newHeight);
            this.controlCanvas.setAttribute('height', $(this.trackDiv).height());
        }

        if (this.scrollbar) {
            this.scrollbar.update();
        }
    }

    igv.TrackView.prototype.isLoading = function () {

        var anyViewportIsLoading;

        anyViewportIsLoading = false;
        this.viewports.forEach(function (v) {
            if (false === anyViewportIsLoading) {
                anyViewportIsLoading = v.isLoading();
            }
        });

        return anyViewportIsLoading;
    };

    igv.TrackView.prototype.resize = function () {
        var width;

        width = igv.browser.viewportContainerWidth() / igv.browser.genomicStateList.length;

        if (width === 0) return;
        this.viewports.forEach(function (viewport) {
            viewport.setWidth(width);
        });

        this.update();

    };

    /**
     * Call update when the content of the track has changed,  e.g. by loading or changing location.
     */
    igv.TrackView.prototype.update = function () {

        var maxContentHeight, w, h,
            updatePromises = [],
            self = this;

        this.viewports.forEach(function (viewport) {
            updatePromises.push(viewport.update());
        });

        Promise.all(updatePromises)

            .then(function (ignore) {

                var contentHeights;
                contentHeights = self.viewports.map(function (vp) {
                    return vp.getContentHeight();
                })

                maxContentHeight = contentHeights.reduce(function (accumulator, currentValue) {
                    return Math.max(accumulator, currentValue);
                });

                if (self.track.autoHeight) {
                    self.setTrackHeight(maxContentHeight, false);
                } else if (self.track.paintAxis) {
                    $(self.controlCanvas).height(maxContentHeight);
                    self.controlCanvas.setAttribute("height", maxContentHeight);
                }

                if (self.scrollbar) {
                    self.scrollbar.update();
                }
            })
    };

    /**
     * Repaint existing features (e.g. a color, resort, or display mode change).
     *
     * viewport.repaint returns a promise.
     */
    igv.TrackView.prototype.repaint = function (force) {

        var promises = [];

        this.viewports.forEach(function (viewport) {
            promises.push(viewport.repaint(force));
        });

        Promise.all(promises)
            .then(function (ignore) {

            });

    };

    /**
     * Do any cleanup here
     */
    igv.TrackView.prototype.dispose = function () {

        if(this.$trackManipulationHandle) {
            this.$trackManipulationHandle.off();
        }

        if(this.$innerScroll) {
            this.$innerScroll.off();
        }

        $(window).off("mousemove.igv");
        $(window).off("mouseup.igv");

        if (typeof this.track.dispose === "function") {
            this.track.dispose();
        }

        var track = this.track;
        Object.keys(track).forEach(function (key) {
            track[key] = undefined;
        })

        this.viewports.forEach(function (viewport) {
            viewport.dispose();
        })

        if(dragged === this) {
            dragged = undefined;
        }

        if(dragDestination === this) {
            dragDestination = undefined;
        }

        Object.keys(this).forEach(function (key) {
            this[key] = undefined;
        })

    }

    igv.TrackScrollbar = function ($viewportContainer, viewports) {

        var self = this,
            offY,
            contentDivHeight;

        contentDivHeight = maxContentHeightWithViewports(viewports);

        this.$outerScroll = $('
');
        this.$innerScroll = $('
');

        this.$outerScroll.append(this.$innerScroll);

        this.$viewportContainer = $viewportContainer;
        this.viewports = viewports;

        this.$innerScroll.on("mousedown", function (event) {

            event.preventDefault();

            offY = event.pageY - $(this).position().top;

            $(window).on("mousemove.igv", null, null, mouseMove);

            $(window).on("mouseup.igv", null, null, mouseUp);

            // <= prevents start of horizontal track panning)
            event.stopPropagation();
        });

        this.$innerScroll.on("click", function (event) {
            event.stopPropagation();
        });

        this.$outerScroll.on("click", function (event) {
            moveScrollerTo(event.offsetY - self.$innerScroll.height() / 2);
            event.stopPropagation();

        });

        function mouseMove(event) {

            event.preventDefault();
            moveScrollerTo(event.pageY - offY);
            event.stopPropagation();
        }

        function mouseUp(event) {
            $(window).off("mousemove.igv", null, mouseMove);
            $(window).off("mouseup.igv", null, mouseUp);
        }

        function moveScrollerTo(y) {

            var newTop,
                contentTop,
                contentDivHeight,
                outerScrollHeight,
                innerScrollHeight;

            outerScrollHeight = self.$outerScroll.height();
            innerScrollHeight = self.$innerScroll.height();

            newTop = Math.min(Math.max(0, y), outerScrollHeight - innerScrollHeight);

            contentDivHeight = maxContentHeightWithViewports(viewports);

            contentTop = -Math.round(newTop * ( contentDivHeight / self.$viewportContainer.height() ));

            self.$innerScroll.css("top", newTop + "px");

            viewports.forEach(function (viewport) {
                $(viewport.contentDiv).css("top", contentTop + "px");
            });

        }
    };

    igv.TrackScrollbar.prototype.update = function () {

        var viewportContainerHeight,
            contentHeight,
            newInnerHeight;

        viewportContainerHeight = this.$viewportContainer.height();

        contentHeight = maxContentHeightWithViewports(this.viewports);

        newInnerHeight = Math.round((viewportContainerHeight / contentHeight) * viewportContainerHeight);

        if (contentHeight > viewportContainerHeight) {
            this.$outerScroll.show();
            this.$innerScroll.height(newInnerHeight);
        } else {
            this.$outerScroll.hide();
        }
    };

    function maxContentHeightWithViewports(viewports) {
        var height = 0;
        viewports.forEach(function (viewport) {
            var hgt = $(viewport.contentDiv).height();
            height = Math.max(hgt, height);
        });

        return height;
    }

    return igv;

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

var igv = (function (igv) {

    var dragged,
        dragDestination;

    igv.TrackView = function (browser, $container, track) {

        var self = this,
            width,
            $track,
            config;

        this.browser = browser;

        $track = $('
');
        this.trackDiv = $track.get(0);
        $container.append($track);

        this.track = track;
        track.trackView = this;

        if (this.track instanceof igv.RulerTrack) {
            this.trackDiv.dataset.rulerTrack = "rulerTrack";
        }

        if (track.height) {
            this.trackDiv.style.height = track.height + "px";
        }

        if (typeof track.paintAxis === 'function') {
            appendLeftHandGutter.call(this, $(this.trackDiv));
        }

        this.$viewportContainer = $('
');
        $(this.trackDiv).append(this.$viewportContainer);

        this.viewports = [];
        width = this.browser.viewportContainerWidth() / this.browser.genomicStateList.length;
        browser.genomicStateList.forEach(function (genomicState) {

            var viewport;
            viewport = new igv.Viewport(self, self.$viewportContainer, genomicState, width);
            self.viewports.push(viewport);

        });

        this.decorateViewports();

        this.configureViewportContainer(this.$viewportContainer, this.viewports);

        if (true === this.track.ignoreTrackMenu) {
            // do nothing
        } else {
            appendRightHandGutter.call(this, $(this.trackDiv));
        }

        if (this.track instanceof igv.RulerTrack) {
            // do nuthin
        } else {
            attachDragWidget.call(this, $(this.trackDiv), this.$viewportContainer);
        }

        if (igv.doProvideColoSwatchWidget(this.track)) {

            config =
            {
                // width = (29 * swatch-width) + border-width + border-width
                width: ((29 * 24) + 1 + 1),
                classes: ['igv-position-absolute']
            };

            this.$colorpicker_container = igv.genericContainer($track, config, function () {
                self.$colorpicker_container.toggle();
            });

            igv.createColorSwatchSelector(this.$colorpicker_container, function (rgb) {
                self.setColor(rgb);
            });

            this.$colorpicker_container.hide();
        }

    };

    igv.TrackView.prototype.configureViewportContainer = function ($viewportContainer, viewports) {

        if ("hidden" === $viewportContainer.css("overflow-y")) {

            // TODO - Handle replacement for contentDiv. Use height calculating closure?
            this.scrollbar = new igv.TrackScrollbar($viewportContainer, viewports);
            // this.scrollbar.update();
            $viewportContainer.append(this.scrollbar.$outerScroll);
        }

        return $viewportContainer;
    };

    igv.TrackView.prototype.removeViewportWithLocusIndex = function (index) {

        if (this.track instanceof igv.RulerTrack) {
            this.track.removeRulerSweeperWithLocusIndex(index);
        }

        this.viewports[index].$viewport.remove();
        this.viewports.splice(index, 1);

        this.decorateViewports();
    };

    igv.TrackView.prototype.viewportWithGenomicState = function (genomicState) {
        var i, viewport;
        for(i=0; i<this.viewports.length; i++) {
            viewport = this.viewports[i];
            if (viewport.genomicState === genomicState) {
                return viewport;
            }
        }
        return undefined;
    };

    igv.TrackView.prototype.decorateViewports = function () {
        var self = this;

        this.viewports.forEach(function (viewport, index) {
            var $viewport;

            $viewport = viewport.$viewport;

            if (self.viewports.length > 1) {
                $viewport.find('.igv-viewport-fa-close').show();
                $viewport.find('.igv-viewport-content-ruler-div').show();
            } else {
                $viewport.find('.igv-viewport-fa-close').hide();
                $viewport.find('.igv-viewport-content-ruler-div').hide();
            }

            if (index < self.viewports.length && (1 + index) !== self.viewports.length) {
                $viewport.addClass('igv-viewport-div-border-right');
            } else {
                $viewport.removeClass('igv-viewport-div-border-right');
            }

        });

    };

    function appendLeftHandGutter($parent) {

        var self = this,
            $leftHandGutter,
            $canvas;

        $leftHandGutter = $('
');
        $parent.append($leftHandGutter);

        if (this.track.dataRange) {

            $leftHandGutter.click(function (e) {
                // igv.dataRangeDialog.configureWithTrackView(self);
                igv.dataRangeDialog.configure({ trackView: self });
                igv.dataRangeDialog.present($(self.trackDiv));
            });

            $leftHandGutter.addClass('igv-clickable');
        }

        $canvas = $('<canvas class ="igv-track-control-canvas">');
        $leftHandGutter.append($canvas);

        $canvas.attr('width', $leftHandGutter.outerWidth());
        $canvas.attr('height', $leftHandGutter.outerHeight());

        this.controlCanvas = $canvas.get(0);
        this.controlCtx = this.controlCanvas.getContext("2d");

    }

    function appendRightHandGutter($parent) {

        var self = this,
            $gearButton, $fa;

        this.rightHandGutter = $('
')[0];
        $parent.append($(this.rightHandGutter));

        $gearButton = igv.createWrappedIcon("cog");
        $(this.rightHandGutter).append($gearButton);

        $gearButton.click(function (e) {
            igv.popover.presentTrackGearMenu(e.pageX, e.pageY, self);
        });

    }

    function attachDragWidget($track, $viewportContainer) {

        var self = this,
            indexDestination,
            indexDragged;

        this.$trackDragScrim = $('
');
        $viewportContainer.append(this.$trackDragScrim);
        this.$trackDragScrim.hide();

        self.$trackManipulationHandle = $('
');
        $track.append(self.$trackManipulationHandle);

        self.$trackManipulationHandle.on('mousedown.trackview', function (e) {
            e.preventDefault();
            self.isMouseDown = true;
            dragged = self;
        });

        self.$trackManipulationHandle.on('mouseup.trackview', function (e) {
            e.preventDefault();
            self.isMouseDown = undefined;
        });

        self.$trackManipulationHandle.on('mouseenter.trackview', function (e) {
            e.preventDefault();
            self.isMouseIn = true;
            dragDestination = self;

            if (undefined === dragged) {
                self.$trackDragScrim.show();
            } else if (self === dragged) {
                self.$trackDragScrim.show();
            }

            if ((dragDestination && dragged) && (dragDestination !== dragged)) {

                indexDestination = igv.browser.trackViews.indexOf(dragDestination);
                indexDragged = igv.browser.trackViews.indexOf(dragged);

                igv.browser.trackViews[indexDestination] = dragged;
                igv.browser.trackViews[indexDragged] = dragDestination;

                if (indexDestination < indexDragged) {
                    $(dragged.trackDiv).insertBefore($(dragDestination.trackDiv));
                } else {
                    $(dragged.trackDiv).insertAfter($(dragDestination.trackDiv));
                }

            }

        });

        self.$trackManipulationHandle.on('mouseleave.trackview', function (e) {
            e.preventDefault();
            self.isMouseIn = undefined;
            dragDestination = undefined;

            if (self !== dragged) {
                self.$trackDragScrim.hide();
            }

        });

        $(document).on('mouseup.document.trackview', function (e) {

            if (dragged) {
                dragged.$trackDragScrim.hide();
            }

            dragged = undefined;
        });

    }

    igv.TrackView.prototype.dataRange = function () {
        return this.track.dataRange ? this.track.dataRange : undefined;
    };

    igv.TrackView.prototype.setDataRange = function (min, max, autoscale) {

        if (min !== undefined) {
            this.track.dataRange.min = min;
        }

        if (max !== undefined) {
            this.track.dataRange.max = max;
        }

        this.track.autoscale = autoscale;

        this.repaint(true);
    };

    igv.TrackView.prototype.setColor = function (color) {
        this.track.color = color;
        this.repaint(true);
    };

    igv.TrackView.prototype.setTrackHeight = function (newHeight, update, force) {

        if (!force) {
            if (this.track.minHeight) {
                newHeight = Math.max(this.track.minHeight, newHeight);
            }

            if (this.track.maxHeight) {
                newHeight = Math.min(this.track.maxHeight, newHeight);
            }
        }

        this.track.height = newHeight;
        $(this.trackDiv).height(newHeight);

        // If the track does not manage its own content height set it here
        if (typeof this.track.computePixelHeight !== "function") {
            this.viewports.forEach(function (vp) {
                vp.setContentHeight(newHeight);
                vp.tile.invalidate = true;
            });
            this.repaint(true);
        }

        if (this.track.paintAxis) {
            $(this.controlCanvas).height(newHeight);
            this.controlCanvas.setAttribute('height', $(this.trackDiv).height());
        }

        if (this.scrollbar) {
            this.scrollbar.update();
        }
    }

    igv.TrackView.prototype.isLoading = function () {

        var anyViewportIsLoading;

        anyViewportIsLoading = false;
        this.viewports.forEach(function (v) {
            if (false === anyViewportIsLoading) {
                anyViewportIsLoading = v.isLoading();
            }
        });

        return anyViewportIsLoading;
    };

    igv.TrackView.prototype.resize = function () {
        var width;

        width = igv.browser.viewportContainerWidth() / igv.browser.genomicStateList.length;

        if (width === 0) return;
        this.viewports.forEach(function (viewport) {
            viewport.setWidth(width);
        });

        this.update();

    };

    /**
     * Call update when the content of the track has changed,  e.g. by loading or changing location.
     */
    igv.TrackView.prototype.update = function () {

        var maxContentHeight, w, h,
            updatePromises = [],
            self = this;

        this.viewports.forEach(function (viewport) {
            updatePromises.push(viewport.update());
        });

        Promise.all(updatePromises)

            .then(function (ignore) {

                var contentHeights;
                contentHeights = self.viewports.map(function (vp) {
                    return vp.getContentHeight();
                })

                maxContentHeight = contentHeights.reduce(function (accumulator, currentValue) {
                    return Math.max(accumulator, currentValue);
                });

                if (self.track.autoHeight) {
                    self.setTrackHeight(maxContentHeight, false);
                } else if (self.track.paintAxis) {
                    $(self.controlCanvas).height(maxContentHeight);
                    self.controlCanvas.setAttribute("height", maxContentHeight);
                }

                if (self.scrollbar) {
                    self.scrollbar.update();
                }
            })
    };

    /**
     * Repaint existing features (e.g. a color, resort, or display mode change).
     *
     * viewport.repaint returns a promise.
     */
    igv.TrackView.prototype.repaint = function (force) {

        var promises = [];

        this.viewports.forEach(function (viewport) {
            promises.push(viewport.repaint(force));
        });

        Promise.all(promises)
            .then(function (ignore) {

            });

    };

    /**
     * Do any cleanup here
     */
    igv.TrackView.prototype.dispose = function () {

        if(this.$trackManipulationHandle) {
            this.$trackManipulationHandle.off();
        }

        if(this.$innerScroll) {
            this.$innerScroll.off();
        }

        $(window).off("mousemove.igv");
        $(window).off("mouseup.igv");

        if (typeof this.track.dispose === "function") {
            this.track.dispose();
        }

        var track = this.track;
        Object.keys(track).forEach(function (key) {
            track[key] = undefined;
        })

        this.viewports.forEach(function (viewport) {
            viewport.dispose();
        })

        if(dragged === this) {
            dragged = undefined;
        }

        if(dragDestination === this) {
            dragDestination = undefined;
        }

        Object.keys(this).forEach(function (key) {
            this[key] = undefined;
        })

    }

    igv.TrackScrollbar = function ($viewportContainer, viewports) {

        var self = this,
            offY,
            contentDivHeight;

        contentDivHeight = maxContentHeightWithViewports(viewports);

        this.$outerScroll = $('
');
        this.$innerScroll = $('
');

        this.$outerScroll.append(this.$innerScroll);

        this.$viewportContainer = $viewportContainer;
        this.viewports = viewports;

        this.$innerScroll.on("mousedown", function (event) {

            event.preventDefault();

            offY = event.pageY - $(this).position().top;

            $(window).on("mousemove.igv", null, null, mouseMove);

            $(window).on("mouseup.igv", null, null, mouseUp);

            // <= prevents start of horizontal track panning)
            event.stopPropagation();
        });

        this.$innerScroll.on("click", function (event) {
            event.stopPropagation();
        });

        this.$outerScroll.on("click", function (event) {
            moveScrollerTo(event.offsetY - self.$innerScroll.height() / 2);
            event.stopPropagation();

        });

        function mouseMove(event) {

            event.preventDefault();
            moveScrollerTo(event.pageY - offY);
            event.stopPropagation();
        }

        function mouseUp(event) {
            $(window).off("mousemove.igv", null, mouseMove);
            $(window).off("mouseup.igv", null, mouseUp);
        }

        function moveScrollerTo(y) {

            var newTop,
                contentTop,
                contentDivHeight,
                outerScrollHeight,
                innerScrollHeight;

            outerScrollHeight = self.$outerScroll.height();
            innerScrollHeight = self.$innerScroll.height();

            newTop = Math.min(Math.max(0, y), outerScrollHeight - innerScrollHeight);

            contentDivHeight = maxContentHeightWithViewports(viewports);

            contentTop = -Math.round(newTop * ( contentDivHeight / self.$viewportContainer.height() ));

            self.$innerScroll.css("top", newTop + "px");

            viewports.forEach(function (viewport) {
                $(viewport.contentDiv).css("top", contentTop + "px");
            });

        }
    };

    igv.TrackScrollbar.prototype.update = function () {

        var viewportContainerHeight,
            contentHeight,
            newInnerHeight;

        viewportContainerHeight = this.$viewportContainer.height();

        contentHeight = maxContentHeightWithViewports(this.viewports);

        newInnerHeight = Math.round((viewportContainerHeight / contentHeight) * viewportContainerHeight);

        if (contentHeight > viewportContainerHeight) {
            this.$outerScroll.show();
            this.$innerScroll.height(newInnerHeight);
        } else {
            this.$outerScroll.hide();
        }
    };

    function maxContentHeightWithViewports(viewports) {
        var height = 0;
        viewports.forEach(function (viewport) {
            var hgt = $(viewport.contentDiv).height();
            height = Math.max(hgt, height);
        });

        return height;
    }

    return igv;

})(igv || {});
<|endoftext|>


<commit_before>/* jshint esversion:6, node: true */

/*
 * Loblaws Messenger - search.js
 *
 * Product search hander.
 */

const index = require('../index.js');
const request = require('request');

function requestProduct(sender, query) {
    console.log("[EVENT] " + sender + ": Requested search for product with query: \'" + query + "\'");

    // Produces URL to make search request with Loblaws
    let url = 'https://www.loblaws.ca/ecommerce/v2/loblaw/products/search?q=';
    url = url.concat(query.trim(), '&page=0&pageSize=10&sort=recommended');

    // Array to be sent with request
    let options = {
        url: url,
        headers: {
            'Authorization': 'bearer fd887ce5-7b15-4c1e-93a0-dc91ce883ec6'
        }
    };

    // Send the search request
    request(options, function callback(error, response, body) {
        if (!error && response.statusCode == 200) {
            let results = JSON.parse(body).searchResults;

            console.log("[INFO] " + "Received response from product query with: " + results.pagination.totalNumberOfResults + " results");
            //console.log(results[0]);
            //console.log(body);

            if (results.pagination.totalNumberOfResults !== 0) {
                sendProductMessage(sender, results);
            } else {
                action_handler.sendTextMessage(sender, "There are no results for that search.");
            }
        }
    });

    action_handler.currentState = action_handler.State.IDLE;
}

/*
 * Results is the api response
 * Makes a carouel displaying tile, description, and image
 */
function sendProductMessage(sender, results) {
	let messageData = {
		"attachment": {
			"type": "template",
			"payload": {
				"template_type": "generic",
				"elements": loadElements(results.products), //Load the products into an object that messenger api can handle
				/*"elements": [
					{
						"title": results.products[0].productName,
						"subtitle": results.products[0].description,
						"image_url": results.products[0].primaryImage,
						"buttons": [{
							"type": "web_url",
							"url": "https://www.messenger.com",
							"title": "web url"
						}, {
							"type": "postback",
							"title": "Postback",
							"payload": "Payload for first element in a generic bubble",
						}],
					}, {
						"title": "Second card",
						"subtitle": "Element #2 of an hscroll",
						"image_url": "http://messengerdemo.parseapp.com/img/gearvr.png",
						"buttons": [{
							"type": "postback",
							"title": "Postback",
							"payload": "Payload for second element in a generic bubble",
						}],
					}
				] */
			}
		}
	};

	//Send message
	request({
		url: 'https://graph.facebook.com/v2.6/me/messages',
		qs: { access_token: index.AUTH_TOKEN },
		method: 'POST',
		json: {
			recipient: { id: sender },
			message: messageData,
		}
	}, function(error, response, body) {
		if (error) {
			console.log('Error sending messages: ', error);
		} else if (response.body.error) {
			console.log('Error: ', response.body.error);
		}
	});
}

/*
 * Load products into an array that can be handled by messenger api
 * Makes carousel as long as number of responses
 */
function loadElements(products) {
	let elements = []; // Create blank array

	console.log("Length: " + products.length);

	for (let i = 0; i < products.length; i++){
		let element = {
			"title": products[i].productName, // Sets title
			"subtitle": products[i].description, // Sets description
			"image_url": products[i].primaryImage, // Sets image
		};

		elements.push(element); //Add the element to the array
	}

	return elements;
}

module.exports.requestProduct = requestProduct;
<commit_msg>Fix missing require<commit_after>/* jshint esversion:6, node: true */

/*
 * Loblaws Messenger - search.js
 *
 * Product search hander.
 */

const index = require('../index.js');
const request = require('request');
const action_handler = require('../action_handler.js');

function requestProduct(sender, query) {
    console.log("[EVENT] " + sender + ": Requested search for product with query: \'" + query + "\'");

    // Produces URL to make search request with Loblaws
    let url = 'https://www.loblaws.ca/ecommerce/v2/loblaw/products/search?q=';
    url = url.concat(query.trim(), '&page=0&pageSize=10&sort=recommended');

    // Array to be sent with request
    let options = {
        url: url,
        headers: {
            'Authorization': 'bearer fd887ce5-7b15-4c1e-93a0-dc91ce883ec6'
        }
    };

    // Send the search request
    request(options, function callback(error, response, body) {
        if (!error && response.statusCode == 200) {
            let results = JSON.parse(body).searchResults;

            console.log("[INFO] " + "Received response from product query with: " + results.pagination.totalNumberOfResults + " results");
            //console.log(results[0]);
            //console.log(body);

            if (results.pagination.totalNumberOfResults !== 0) {
                sendProductMessage(sender, results);
            } else {
                action_handler.sendTextMessage(sender, "There are no results for that search.");
            }
        }
    });

    action_handler.currentState = action_handler.State.IDLE;
}

/*
 * Results is the api response
 * Makes a carouel displaying tile, description, and image
 */
function sendProductMessage(sender, results) {
	let messageData = {
		"attachment": {
			"type": "template",
			"payload": {
				"template_type": "generic",
				"elements": loadElements(results.products), //Load the products into an object that messenger api can handle
				/*"elements": [
					{
						"title": results.products[0].productName,
						"subtitle": results.products[0].description,
						"image_url": results.products[0].primaryImage,
						"buttons": [{
							"type": "web_url",
							"url": "https://www.messenger.com",
							"title": "web url"
						}, {
							"type": "postback",
							"title": "Postback",
							"payload": "Payload for first element in a generic bubble",
						}],
					}, {
						"title": "Second card",
						"subtitle": "Element #2 of an hscroll",
						"image_url": "http://messengerdemo.parseapp.com/img/gearvr.png",
						"buttons": [{
							"type": "postback",
							"title": "Postback",
							"payload": "Payload for second element in a generic bubble",
						}],
					}
				] */
			}
		}
	};

	//Send message
	request({
		url: 'https://graph.facebook.com/v2.6/me/messages',
		qs: { access_token: index.AUTH_TOKEN },
		method: 'POST',
		json: {
			recipient: { id: sender },
			message: messageData,
		}
	}, function(error, response, body) {
		if (error) {
			console.log('Error sending messages: ', error);
		} else if (response.body.error) {
			console.log('Error: ', response.body.error);
		}
	});
}

/*
 * Load products into an array that can be handled by messenger api
 * Makes carousel as long as number of responses
 */
function loadElements(products) {
	let elements = []; // Create blank array

	console.log("Length: " + products.length);

	for (let i = 0; i < products.length; i++){
		let element = {
			"title": products[i].productName, // Sets title
			"subtitle": products[i].description, // Sets description
			"image_url": products[i].primaryImage, // Sets image
		};

		elements.push(element); //Add the element to the array
	}

	return elements;
}

module.exports.requestProduct = requestProduct;
<|endoftext|>


<commit_before>// Karma configuration
// Generated on Tue Sep 26 2017 15:12:48 GMT-0700 (PDT)

module.exports = function(config) {
  config.set({

    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: '',

    // frameworks to use
    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
    frameworks: ['jasmine'],

    // list of files / patterns to load in the browser
    files: [
      './node_modules/angular/angular.js',                             // angular
      './node_modules/angular-ui-router/release/angular-ui-router.js', // ui-router
      './node_modules/angular-mocks/angular-mocks.js',                 // loads our modules for tests
      './node_modules/angular-aria/angular-aria.js',                 // loads our modules for tests
      './node_modules/angular-route/angular-route.js',                 // loads our modules for tests
      './node_modules/angular-animate/angular-animate.js',                 // loads our modules for tests
      './node_modules/angular-messages/angular-messages.js',                 // loads our modules for tests
      './node_modules/angular-material/angular-material.js',                 // loads our modules for tests
      './node_modules/angular-sanitize/angular-sanitize.js',                 // loads our modules for tests
      './node_modules/angular-ui-bootstrap/dist/ui-bootstrap.js',                 // loads our modules for tests
      './node_modules/angular-ui-select/select.js',                 // loads our modules for tests
      './node_modules/ngStorage/ngStorage.js',                 // loads our modules for tests
      './public/scripts/gridster/angular-gridster.js',
      './public/scripts/angular-prompt.js',
      './public/scripts/datetimepicker.js',
      './public/scripts/angular-marked.min.js',
      './public/scripts/marked.js',
      './public/scripts/angular-modal-service.min.js',
      './app/app.js',
      './app/components/**/*.js',
      './app/services/*.js',
      './app/services/tests/*.js',
      './app/directives/**/*.js',
      // './app/services/userService.js',
      // './app/components/grid/grid.js',
      // './app/services/tests/userService-spec.js',
      // './app/components/grid/grid.spec.js',
      // './app/components/saveMenu/saveMenu.js',
      // './app/components/saveMenu/saveMenu.spec.js',
      // './app/directives/clock/clock.js',
      // './app/directives/clock/clock.spec.js',
      // './app/directives/clock/clocksettings.js',
      // './app/directives/clock/clocksettings.spec.js'
    ],

    // list of files to exclude
    exclude: [
    ],

    // preprocess matching files before serving them to the browser
    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
    preprocessors: {
    },

    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['spec'],

    // web server port
    port: 9876,

    // enable / disable colors in the output (reporters and logs)
    colors: true,

    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,

    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: true,

    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: ['Chrome'],

    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: false,

    // Concurrency level
    // how many browser should be started simultaneous
    concurrency: Infinity
  })
}
<commit_msg>updated karma configuration to use PhantomJS browser<commit_after>// Karma configuration
// Generated on Tue Sep 26 2017 15:12:48 GMT-0700 (PDT)

module.exports = function(config) {
  config.set({

    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: '',

    // frameworks to use
    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
    frameworks: ['jasmine'],

    // list of files / patterns to load in the browser
    files: [
      './node_modules/angular/angular.js',                             // angular
      './node_modules/angular-ui-router/release/angular-ui-router.js', // ui-router
      './node_modules/angular-mocks/angular-mocks.js',                 // loads our modules for tests
      './node_modules/angular-aria/angular-aria.js',                 // loads our modules for tests
      './node_modules/angular-route/angular-route.js',                 // loads our modules for tests
      './node_modules/angular-animate/angular-animate.js',                 // loads our modules for tests
      './node_modules/angular-messages/angular-messages.js',                 // loads our modules for tests
      './node_modules/angular-material/angular-material.js',                 // loads our modules for tests
      './node_modules/angular-sanitize/angular-sanitize.js',                 // loads our modules for tests
      './node_modules/angular-ui-bootstrap/dist/ui-bootstrap.js',                 // loads our modules for tests
      './node_modules/angular-ui-select/select.js',                 // loads our modules for tests
      './node_modules/ngstorage/ngStorage.js',                 // loads our modules for tests
      './public/scripts/gridster/angular-gridster.js',
      './public/scripts/angular-prompt.js',
      './public/scripts/datetimepicker.js',
      './public/scripts/angular-marked.min.js',
      './public/scripts/marked.js',
      './public/scripts/angular-modal-service.min.js',
      './app/app.js',
      './app/components/**/*.js',
      './app/services/*.js',
      './app/services/tests/*.js',
      './app/directives/**/*.js',
      // './app/services/userService.js',
      // './app/components/grid/grid.js',
      // './app/services/tests/userService-spec.js',
      // './app/components/grid/grid.spec.js',
      // './app/components/saveMenu/saveMenu.js',
      // './app/components/saveMenu/saveMenu.spec.js',
      // './app/directives/clock/clock.js',
      // './app/directives/clock/clock.spec.js',
      // './app/directives/clock/clocksettings.js',
      // './app/directives/clock/clocksettings.spec.js'
    ],

    // list of files to exclude
    exclude: [
    ],

    // preprocess matching files before serving them to the browser
    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
    preprocessors: {
    },

    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['spec'],

    // web server port
    port: 9876,

    // enable / disable colors in the output (reporters and logs)
    colors: true,

    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,

    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: true,

    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    //browsers: ['Chrome'],
    browsers: ['PhantomJS', 'PhantomJS_custom'],
    // you can define custom flags
    customLaunchers: {
      'PhantomJS_custom': {
        base: 'PhantomJS',
        options: {
          windowName: 'my-window',
          settings: {
            webSecurityEnabled: false
          },
        },
        flags: ['--load-images=true'],
        debug: true
      }
    },
    phantomjsLauncher: {
      // Have phantomjs exit if a ResourceError is encountered (useful if karma exits without killing phantom)
      exitOnResourceError: true
    },

    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: false,

    // Concurrency level
    // how many browser should be started simultaneous
    concurrency: Infinity
  })
}
<|endoftext|>


<commit_before>'use strict';
const Promise = require('bluebird'),
  chalk = require('chalk'),
  apps = require('../../db/apps'),
  parse = require('../../parse'),
  log = require('../../logger'),
  _ = require('lodash'),
  domNormalMatcher = require('../dom/normal-matcher.js'),
  domStrictMatcher = require('../dom/strict-matcher.js'),
  maxUIResults = require('config').results.maxUI;

exports.init = function init(env) {
  return apps
    .init('ui')
    .then((ret) => {
      const newEnv = env;
      newEnv.ids.ui = ret;
      return newEnv;
    });
};

exports.find = function find(env, query, options) {
  // Get all file paths
  const filePaths = _.map(options.scope, (scope) => {
      return env.apps.getPath(scope);
    }),
    uiQuery = parse(query),
    // array to collect results
    results = [];
  let count = 0,
    found = 0;
  // TODO: .settle is deprecated. Replace it wit .reflect
  return Promise.settle(filePaths)
    .then((promises) => {
      for (let i = 0; i < promises.length; i++) {
        if (promises[i].isFulfilled()) {
          const appPathsObj = promises[i].value(),
            currentApp = env.apps.loadData(appPathsObj, {
              dom: true
            });
          if (currentApp.dom === undefined || currentApp.dom === null) {
            continue;
          }
          let mResult = {};
          if (options.mode === 'strict') {
            mResult = domStrictMatcher(uiQuery, currentApp, currentApp.dom);
          }
          else {
            mResult = domNormalMatcher(uiQuery, currentApp, currentApp.dom);
          }
          if (mResult.matched) {
            results.push({
              id: currentApp.id,
              returnAttributes: mResult.returnAttributes
            });
            found++;
          }
          log.info('%d: %s %s', count, chalk.grey(currentApp.id),
            mResult.matched ? chalk.green('yes') : chalk.red('no'));
          count++;
          if (found === maxUIResults) {
            // the maximum number of UI results have been reached.
            break;
          }
        }
        else if (promises[i].isRejected()) {
          continue;
        }
      }
      return Promise.resolve(results);
    })
    .catch((e) => {
      log.error('UI Search failed while executing query %s.', query, e);
      return Promise.reject(e);
    });
};
<commit_msg>refactor and ignore max result limit<commit_after>const Promise = require('bluebird'),
  chalk = require('chalk'),
  apps = require('../../db/apps'),
  parse = require('../../parse'),
  log = require('../../logger'),
  _ = require('lodash'),
  domNormalMatcher = require('../dom/normal-matcher.js'),
  domStrictMatcher = require('../dom/strict-matcher.js');

exports.init = function init(env) {
  return apps
    .init('ui')
    .then((ret) => {
      const newEnv = env;
      newEnv.ids.ui = ret;
      return newEnv;
    });
};

exports.find = function find(env, query, options) {
  if (_.isEmpty(query)) {
    return Promise.resolve({ id: options.scope });
  }
  const uiQuery = parse(query),
  // Get all file paths
    results = _.map(options.scope, (appId, count) => {
      return env.apps.getPath(appId)
      .then((appPathObj) => {
        const currentApp = env.apps.loadData(appPathObj, { dom: true }),
          result = match(options.mode, currentApp, uiQuery);
        log.info('%d: %s %s', count, chalk.grey(currentApp.id),
            result.matched ? chalk.green('yes') : chalk.red('no'));
        if (result.matched) {
          return {
            id: currentApp.id,
            returnAttributes: result.returnAttributes
          };
        }
        return undefined;
      })
      .catch((e) => {
        if (e.type === 'NotFoundError') {
          log.warn('UI file for %s was not found.', appId);
          return undefined;
        }
        log.error('UI Search failed while executing query %s.', query, e);
        return Promise.reject(e);
      });
    });
  return Promise.all(results).then((uiResults) => {
    return _.compact(uiResults);
  });
};

function match(mode, app, query) {
  if (!app || !app.dom) {
    return {};
  }
  if (mode === 'strict') {
    return domStrictMatcher(query, app, app.dom);
  }
  return domNormalMatcher(query, app, app.dom);
}
<|endoftext|>


<commit_before>// Karma configuration
// Generated on Mon Jan 18 2016 13:44:53 GMT-0800 (PST)

module.exports = function (config) {
  var configuration = {

    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: '',

    // frameworks to use
    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
    frameworks: ['jasmine'],

    // list of files / patterns to load in the browser
    files: [
    // angular-files
      'node_modules/angular/angular.js',
      'node_modules/angular-mocks/angular-mocks.js',
      'node_modules/socket.io-client/socket.io.js',

      'node_modules/angular-ui-router/release/angular-ui-router.js',
      'node_modules/fastclick/lib/fastclick.js',
      'node_modules/viewport-units-buggyfill/viewport-units-buggyfill.js',
      'node_modules/ng-file-upload/dist/ng-file-upload-shim.min.js',
      'node_modules/ng-file-upload/dist/ng-file-upload.min.js',
      'node_modules/angular-drag-and-drop-lists/angular-drag-and-drop-lists.min.js',
      'node_modules/angular-foundation/mm-foundation.js',
      'node_modules/angular-foundation/mm-foundation-tpls.js',
      'node_modules/lodash/lodash.js',

    // client source
      'client/**/*.js',

    // client test
      'spec/client/**/*.js'
    ],

    // list of files to exclude
    exclude: [
      'client/build/**/*.js',
    ],

    // preprocess matching files before serving them to the browser
    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
    preprocessors: {
    },

    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['progress', 'nyan', 'html'],

    // web server port
    port: 9876,

    // enable / disable colors in the output (reporters and logs)
    colors: true,

    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,

    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: true,

    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: ['Chrome'],

    // Custom Chrome settings for Travis CI
    customLaunchers: {
      ChromeTravisCi: {
        base: 'Chrome',
        flags: ['--no-sandbox']
      }
    },

    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: false,

    // Concurrency level
    // how many browser should be started simultaneous
    concurrency: Infinity
  };

  if (process.env.TRAVIS) {
    configuration.browsers = ['ChromeTravisCi'];
  }

  config.set(configuration);
};
<commit_msg>[test]: add google maps dependency to karma config<commit_after>// Karma configuration
// Generated on Mon Jan 18 2016 13:44:53 GMT-0800 (PST)

module.exports = function (config) {
  var configuration = {

    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: '',

    // frameworks to use
    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
    frameworks: ['jasmine'],

    // list of files / patterns to load in the browser
    files: [
    // angular-files
      'node_modules/angular/angular.js',
      'node_modules/angular-mocks/angular-mocks.js',
      'node_modules/socket.io-client/socket.io.js',

      'node_modules/angular-ui-router/release/angular-ui-router.js',
      'node_modules/fastclick/lib/fastclick.js',
      'node_modules/viewport-units-buggyfill/viewport-units-buggyfill.js',
      'node_modules/ng-file-upload/dist/ng-file-upload-shim.min.js',
      'node_modules/ng-file-upload/dist/ng-file-upload.min.js',
      'node_modules/angular-drag-and-drop-lists/angular-drag-and-drop-lists.min.js',
      'node_modules/angular-foundation/mm-foundation.js',
      'node_modules/angular-foundation/mm-foundation-tpls.js',
      'node_modules/angular-simple-logger/dist/angular-simple-logger.js',
      'node_modules/angular-google-maps/dist/angular-google-maps.js',
      'node_modules/lodash/lodash.js',

    // client source
      'client/**/*.js',

    // client test
      'spec/client/**/*.js'
    ],

    // list of files to exclude
    exclude: [
      'client/build/**/*.js',
    ],

    // preprocess matching files before serving them to the browser
    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
    preprocessors: {
    },

    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['progress', 'nyan', 'html'],

    // web server port
    port: 9876,

    // enable / disable colors in the output (reporters and logs)
    colors: true,

    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,

    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: true,

    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: ['Chrome'],

    // Custom Chrome settings for Travis CI
    customLaunchers: {
      ChromeTravisCi: {
        base: 'Chrome',
        flags: ['--no-sandbox']
      }
    },

    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: false,

    // Concurrency level
    // how many browser should be started simultaneous
    concurrency: Infinity
  };

  if (process.env.TRAVIS) {
    configuration.browsers = ['ChromeTravisCi'];
  }

  config.set(configuration);
};
<|endoftext|>


<commit_before>import React from 'react';
import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs';
import Markdown from 'docs/src/pages/components/material-icons/material-icons.md';
import SearchIcons from 'docs/src/pages/components/material-icons/SearchIcons';

const req = (name) => {
  const map = {
    'material-icons.md': Markdown,
    'SearchIcons.js': {
      default: SearchIcons,
    },
  };
  return map[name];
};
req.keys = () => ['material-icons.md', 'SearchIcons.js'];
const reqPrefix = 'pages/components/material-icons';

export default function Page() {
  return <MarkdownDocs disableToc req={req} reqSource={() => {}} reqPrefix={reqPrefix} />;
}
<commit_msg>[docs] Fix icons + locale (#20213)<commit_after>import React from 'react';
import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs';

const req = require.context('docs/src/pages/components/material-icons', false, /\.(md|js|tsx)$/);
const reqSource = require.context(
  '!raw-loader!../../src/pages/components/material-icons',
  false,
  /\.(js|tsx)$/,
);
const reqPrefix = 'pages/components/material-icons';

export default function Page() {
  return <MarkdownDocs req={req} reqSource={reqSource} reqPrefix={reqPrefix} />;
}
<|endoftext|>


<commit_before>var tipos_cuenta_destino = {
    "CC en Pesos": "00",
    "CA en Pesos": "01",
    "Cuenta Única en pesos": "09",
    "Cuenta Única en dólares": "10",
    "CC en Dólares": "03",
    "CA en Dólares": "04"
};

var tipos_cuenta_destino_otros_bancos = [
    "CUENTA CORRIENTE PROPIA",
    "CUENTA CORRIENTE DE TERCEROS",
    "OTRA CUENTA"
];

var codigos_concepto_transferencia = [
    "ALQ", "VAR", "EXP", "CUO", "FAC", "PRE", "SEG", "HON"
];

var br = new BancoRioTransfer(
    "0072",
    "205",
    "00",
    "80268"
);
br.addRow(
    new SantanderOtrasCuentas(
        374,
        09,
        3584422,
        518.40,
        "Horacio Oliva",
        "VAR"
    )
);
br.addRow(
    new OtrosBancos(
        1200.00,
        "Alguien",
        "2850401240017000480910",
        "HON",
        "",
        20139114923,
        3
    )
)

console.log(br.getHeader());
console.log(br.getBody().join("\n"));
console.log(br.getFooter());
<commit_msg>Removed unnecesary file<commit_after><|endoftext|>


<commit_before>const request = require('request')
const URI = require('urijs')
const Promise = require('bluebird')
const { pick, get, omit } = require('lodash')
const { strRightBack } = require('underscore.string')
const parseContentDisposition = require('content-disposition').parse
const fs = require('fs')
const { exec } = require('child_process')
const findit = require('findit')
const crypto = require('crypto')
const rimraf = require('rimraf')
const through2 = require('through2')
const os = require('os')

const execAsync = Promise.promisify(exec)
const rimrafAsync = Promise.promisify(rimraf)

function mkTempDir() {
  return new Promise((resolve, reject) => {
    fs.mkdtemp(os.tmpdir() + '/plunger_', (err, path) => {
      if (err) return reject(err)
      resolve(path)
    })
  })
}

const FIELDS = [
  'statusCode',
  'headers',
  'contentDisposition',
  'fileName',
  'fileExtension',
  'binary',
  'archive',
]

const getExtension = fileName => {
  if (fileName && fileName.includes('.')) {
    return strRightBack(fileName, '.')
  } else {
    return undefined
  }
}

const BINARY_CONTENT_TYPES = [
  'application/octet-stream',
  'application/binary',
]

const ARCHIVE_EXTENSIONS = [
  'zip',
  'tar',
  'rar',
  'gz',
  'bz2',
  '7z',
  'xz',
]

class Analyzer {

  constructor(location) {
    this.rawLocation = location
    this.location = new URI(location)
  }

  executeRequest() {
    return new Promise((resolve, reject) => {
      this.req = request
      .get(this.rawLocation)
      .on('error', reject)
      .on('response', response => {
        this.response = response
        this.response.pause()
        resolve(this)
      })
    })
  }

  extractDataFromResponse() {
    const headers = omit(this.response.headers, 'set-cookie', 'connection')
    this.statusCode = this.response.statusCode
    this.headers = headers
    if (headers['content-disposition']) {
      this.contentDisposition = parseContentDisposition(headers['content-disposition'])
    }
    return this
  }

  closeConnection(force = false) {
    if (force) this.response.destroy()
    return this
  }

  inspect() {
    return this.executeRequest()
    .then(() => this.extractDataFromResponse())
    .then(() => this.closeConnection())
  }

  get fileName() {
    return get(this, 'contentDisposition.parameters.filename') || this.location.filename(true)
  }

  get fileExtension() {
    let attachmentExt = getExtension(get(this, 'contentDisposition.parameters.filename'))
    let urlExt = getExtension(this.location.filename(true))
    return attachmentExt || urlExt
  }

  get binary() {
    let contentType = this.headers['content-type']
    if (contentType && BINARY_CONTENT_TYPES.includes(contentType)) return true
  }

  get archive() {
    if (ARCHIVE_EXTENSIONS.includes(this.fileExtension)) {
      return this.fileExtension
    } else {
      return false
    }
  }

  toObject() {
    return pick(this, ...FIELDS)
  }

  isArchive() {
    return (this.archive === 'zip' && this.fileExtension === 'zip') || (this.archive === 'rar' && this.fileExtension === 'rar')
  }

  createTempDirectory() {
    return mkTempDir()
    .then(path => {
      this.tempDirectoryPath = path
      return this.tempDirectoryPath
    })
  }

  saveArchive() {
    return this.createTempDirectory()
    .then(path => {
      return new Promise((resolve, reject) => {
        this.archivePath = path + '/archive.' + this.archive

        const hash = crypto.createHash('sha1')
        let readBytes = 0

        this.response
        .pipe(through2.obj((chunk, enc, cb) => {
          readBytes += chunk.length
          if (readBytes > 100 * 1024 * 1204) {
            this.closeConnection(true)
            reject(new Error('Archive is too large'))
          }
          cb()
        }))
        .once('finish', () => this.readBytes = readBytes)

        this.response
        .pipe(hash)
        .once('finish', () => this.digest = 'sha1-' + hash.read())

        this.response
        .pipe(fs.createWriteStream(this.archivePath))
        .once('finish', () => resolve(this.archivePath))
        .on('error', reject)
      })
    })
  }

  decompressArchive() {
    if (this.decompressedDirectoryPath) return Promise.resolve(this.decompressedDirectoryPath)
    if (!this.archivePath) return Promise.reject(new Error('`archivePath` is not defined'))
    let decompressProcess
    if (this.archive === 'zip') {
      decompressProcess = new Promise((resolve, reject) => {
        exec('unzip -d decompressed archive.zip', { cwd: this.tempDirectoryPath }, err => {
          if ((err && err.code === 1) || !err) return resolve()
          reject(err)
        })
      })
    }
    if (this.archive === 'rar') decompressProcess = execAsync('unrar x archive.rar decompressed/', { cwd: this.tempDirectoryPath })
    if (decompressProcess) {
      return decompressProcess.then(() => {
        this.decompressedDirectoryPath = this.tempDirectoryPath + '/decompressed'
        return this.decompressedDirectoryPath
      })
    } else {
      return Promise.reject('Archive type not supported: ' + this.archive)
    }
  }

  listFiles() {
    if (!this.decompressedDirectoryPath) return Promise.reject(new Error('No iterable path found'))
    const startPoint = this.decompressedDirectoryPath.length + 1
    const paths = []
    const datasets = []
    return new Promise((resolve, reject) => {
      findit(this.decompressedDirectoryPath)
      .on('file', file => {
        let shortFileName = file.substring(startPoint)
        paths.push(shortFileName)
        if (shortFileName.match(/\.(shp|tab|mif)$/i)) datasets.push(shortFileName)
      })
      .on('end', () => resolve({ all: paths, datasets: datasets }))
      .on('error', reject)
    })
  }

  cleanup() {
    if (this.tempDirectoryPath) {
      return rimrafAsync(this.tempDirectoryPath)
      .then(() => this.tempDirectoryPath = undefined)
    }
  }

}

module.exports = Analyzer
<commit_msg>Switch digest to base64<commit_after>const request = require('request')
const URI = require('urijs')
const Promise = require('bluebird')
const { pick, get, omit } = require('lodash')
const { strRightBack } = require('underscore.string')
const parseContentDisposition = require('content-disposition').parse
const fs = require('fs')
const { exec } = require('child_process')
const findit = require('findit')
const crypto = require('crypto')
const rimraf = require('rimraf')
const through2 = require('through2')
const os = require('os')

const execAsync = Promise.promisify(exec)
const rimrafAsync = Promise.promisify(rimraf)

function mkTempDir() {
  return new Promise((resolve, reject) => {
    fs.mkdtemp(os.tmpdir() + '/plunger_', (err, path) => {
      if (err) return reject(err)
      resolve(path)
    })
  })
}

const FIELDS = [
  'statusCode',
  'headers',
  'contentDisposition',
  'fileName',
  'fileExtension',
  'binary',
  'archive',
]

const getExtension = fileName => {
  if (fileName && fileName.includes('.')) {
    return strRightBack(fileName, '.')
  } else {
    return undefined
  }
}

const BINARY_CONTENT_TYPES = [
  'application/octet-stream',
  'application/binary',
]

const ARCHIVE_EXTENSIONS = [
  'zip',
  'tar',
  'rar',
  'gz',
  'bz2',
  '7z',
  'xz',
]

class Analyzer {

  constructor(location) {
    this.rawLocation = location
    this.location = new URI(location)
  }

  executeRequest() {
    return new Promise((resolve, reject) => {
      this.req = request
      .get(this.rawLocation)
      .on('error', reject)
      .on('response', response => {
        this.response = response
        this.response.pause()
        resolve(this)
      })
    })
  }

  extractDataFromResponse() {
    const headers = omit(this.response.headers, 'set-cookie', 'connection')
    this.statusCode = this.response.statusCode
    this.headers = headers
    if (headers['content-disposition']) {
      this.contentDisposition = parseContentDisposition(headers['content-disposition'])
    }
    return this
  }

  closeConnection(force = false) {
    if (force) this.response.destroy()
    return this
  }

  inspect() {
    return this.executeRequest()
    .then(() => this.extractDataFromResponse())
    .then(() => this.closeConnection())
  }

  get fileName() {
    return get(this, 'contentDisposition.parameters.filename') || this.location.filename(true)
  }

  get fileExtension() {
    let attachmentExt = getExtension(get(this, 'contentDisposition.parameters.filename'))
    let urlExt = getExtension(this.location.filename(true))
    return attachmentExt || urlExt
  }

  get binary() {
    let contentType = this.headers['content-type']
    if (contentType && BINARY_CONTENT_TYPES.includes(contentType)) return true
  }

  get archive() {
    if (ARCHIVE_EXTENSIONS.includes(this.fileExtension)) {
      return this.fileExtension
    } else {
      return false
    }
  }

  toObject() {
    return pick(this, ...FIELDS)
  }

  isArchive() {
    return (this.archive === 'zip' && this.fileExtension === 'zip') || (this.archive === 'rar' && this.fileExtension === 'rar')
  }

  createTempDirectory() {
    return mkTempDir()
    .then(path => {
      this.tempDirectoryPath = path
      return this.tempDirectoryPath
    })
  }

  saveArchive() {
    return this.createTempDirectory()
    .then(path => {
      return new Promise((resolve, reject) => {
        this.archivePath = path + '/archive.' + this.archive

        const hash = crypto.createHash('sha1')
        let readBytes = 0

        this.response
        .pipe(through2.obj((chunk, enc, cb) => {
          readBytes += chunk.length
          if (readBytes > 100 * 1024 * 1204) {
            this.closeConnection(true)
            reject(new Error('Archive is too large'))
          }
          cb()
        }))
        .once('finish', () => this.readBytes = readBytes)

        this.response
        .pipe(hash)
        .once('finish', () => this.digest = 'sha1-' + hash.digest('base64'))

        this.response
        .pipe(fs.createWriteStream(this.archivePath))
        .once('finish', () => resolve(this.archivePath))
        .on('error', reject)
      })
    })
  }

  decompressArchive() {
    if (this.decompressedDirectoryPath) return Promise.resolve(this.decompressedDirectoryPath)
    if (!this.archivePath) return Promise.reject(new Error('`archivePath` is not defined'))
    let decompressProcess
    if (this.archive === 'zip') {
      decompressProcess = new Promise((resolve, reject) => {
        exec('unzip -d decompressed archive.zip', { cwd: this.tempDirectoryPath }, err => {
          if ((err && err.code === 1) || !err) return resolve()
          reject(err)
        })
      })
    }
    if (this.archive === 'rar') decompressProcess = execAsync('unrar x archive.rar decompressed/', { cwd: this.tempDirectoryPath })
    if (decompressProcess) {
      return decompressProcess.then(() => {
        this.decompressedDirectoryPath = this.tempDirectoryPath + '/decompressed'
        return this.decompressedDirectoryPath
      })
    } else {
      return Promise.reject('Archive type not supported: ' + this.archive)
    }
  }

  listFiles() {
    if (!this.decompressedDirectoryPath) return Promise.reject(new Error('No iterable path found'))
    const startPoint = this.decompressedDirectoryPath.length + 1
    const paths = []
    const datasets = []
    return new Promise((resolve, reject) => {
      findit(this.decompressedDirectoryPath)
      .on('file', file => {
        let shortFileName = file.substring(startPoint)
        paths.push(shortFileName)
        if (shortFileName.match(/\.(shp|tab|mif)$/i)) datasets.push(shortFileName)
      })
      .on('end', () => resolve({ all: paths, datasets: datasets }))
      .on('error', reject)
    })
  }

  cleanup() {
    if (this.tempDirectoryPath) {
      return rimrafAsync(this.tempDirectoryPath)
      .then(() => this.tempDirectoryPath = undefined)
    }
  }

}

module.exports = Analyzer
<|endoftext|>


<commit_before>var customLaunchers = {
	sl_chrome: {
		base: 'SauceLabs',
		browserName: 'chrome'
	},
	sl_firefox: {
		base: 'SauceLabs',
		browserName: 'firefox',
		version: '30'
	},
	sl_safari: {
		base: 'SauceLabs',
		browserName: 'safari',
		platform: 'OS X 10.9',
		version: '7'
	},
	sl_ios_safari: {
		base: 'SauceLabs',
		browserName: 'iphone',
		platform: 'OS X 10.9',
		version: '7.1'
	},
	sl_ie_9: {
		base: 'SauceLabs',
		browserName: 'internet explorer',
		platform: 'Windows 2008',
		version: '9'
	},
	sl_ie_10: {
		base: 'SauceLabs',
		browserName: 'internet explorer',
		platform: 'Windows 2012',
		version: '10'
	},
	sl_ie_11: {
		base: 'SauceLabs',
		browserName: 'internet explorer',
		platform: 'Windows 8.1',
		version: '11'
	}
};

module.exports = function(config) {
	config.set(module.exports.conf);

	if(process.env.TRAVIS) {
		config.sauceUser = process.env.SAUCE_USERNAME;
		config.sauceKey = process.env.SAUCE_ACCESS_KEY;

		var buildLabel = 'TRAVIS #' + process.env.TRAVIS_BUILD_NUMBER + ' (' + process.env.TRAVIS_BUILD_ID + ')';

		config.logLevel = config.LOG_DEBUG;
		config.transports = ['websocket', 'xhr-polling'];
		config.captureTimeout = 0; // rely on SL timeout

		config.sauceLabs.build = buildLabel;
		config.sauceLabs.startConnect = false;
		config.sauceLabs.tunnelIdentifier = process.env.TRAVIS_JOB_NUMBER;
		config.sauceLabs.recordScreenshots = true;

		// TODO(vojta): remove once SauceLabs supports websockets.
		// This speeds up the capturing a bit, as browsers don't even try to use websocket.
		config.transports = ['xhr-polling'];

		// Debug logging into a file, that we print out at the end of the build.
		config.loggers.push({
			type: 'file',
			filename: process.env.LOGS_DIR + '/karma.log'
		});
	}
};

module.exports.conf = {
	frameworks: ['jasmine'],
	files: [
		'node_modules/jasmine-as-promised/src/jasmine-as-promised.js',
		'bower_components/angular/angular.js',
		'bower_components/angular-mocks/angular-mocks.js',
		'bower_components/localforage/dist/localforage.js',
		'src/angular-localForage.js',
		'tests/angular-localForage.js'
	],
	autoWatch: false,
	singleRun: true,
	sauceLabs: {
		testName: 'angular-localforage unit tests',
		recordScreenshots: false,
		startConnect: true,
		options: {
			'selenium-version': '2.41.0'
		},
		connectOptions: {
			port: 5757,
			logfile: 'sauce_connect.log'
		}
	},
	// Increase timeout in case connection in CI is slow
	captureTimeout: 120000,
	browserDisconnectTimeout: 10000,
	browserDisconnectTolerance: 2,
	browserNoActivityTimeout: 30000,
	browsers: Object.keys(customLaunchers),
	customLaunchers: customLaunchers,
	reporters: ['progress', 'saucelabs'],
	port: 9876,
	colors: true
}<commit_msg>chore: don't try to log to a file for travis<commit_after>var customLaunchers = {
	sl_chrome: {
		base: 'SauceLabs',
		browserName: 'chrome'
	},
	sl_firefox: {
		base: 'SauceLabs',
		browserName: 'firefox',
		version: '30'
	},
	sl_safari: {
		base: 'SauceLabs',
		browserName: 'safari',
		platform: 'OS X 10.9',
		version: '7'
	},
	sl_ios_safari: {
		base: 'SauceLabs',
		browserName: 'iphone',
		platform: 'OS X 10.9',
		version: '7.1'
	},
	sl_ie_9: {
		base: 'SauceLabs',
		browserName: 'internet explorer',
		platform: 'Windows 2008',
		version: '9'
	},
	sl_ie_10: {
		base: 'SauceLabs',
		browserName: 'internet explorer',
		platform: 'Windows 2012',
		version: '10'
	},
	sl_ie_11: {
		base: 'SauceLabs',
		browserName: 'internet explorer',
		platform: 'Windows 8.1',
		version: '11'
	}
};

module.exports = function(config) {
	config.set(module.exports.conf);

	if(process.env.TRAVIS) {
		config.sauceUser = process.env.SAUCE_USERNAME;
		config.sauceKey = process.env.SAUCE_ACCESS_KEY;

		var buildLabel = 'TRAVIS #' + process.env.TRAVIS_BUILD_NUMBER + ' (' + process.env.TRAVIS_BUILD_ID + ')';

		config.logLevel = config.LOG_DEBUG;
		config.transports = ['websocket', 'xhr-polling'];
		config.captureTimeout = 0; // rely on SL timeout

		config.sauceLabs.build = buildLabel;
		config.sauceLabs.startConnect = false;
		config.sauceLabs.tunnelIdentifier = process.env.TRAVIS_JOB_NUMBER;
		config.sauceLabs.recordScreenshots = true;

		// TODO(vojta): remove once SauceLabs supports websockets.
		// This speeds up the capturing a bit, as browsers don't even try to use websocket.
		config.transports = ['xhr-polling'];
	}
};

module.exports.conf = {
	frameworks: ['jasmine'],
	files: [
		'node_modules/jasmine-as-promised/src/jasmine-as-promised.js',
		'bower_components/angular/angular.js',
		'bower_components/angular-mocks/angular-mocks.js',
		'bower_components/localforage/dist/localforage.js',
		'src/angular-localForage.js',
		'tests/angular-localForage.js'
	],
	autoWatch: false,
	singleRun: true,
	sauceLabs: {
		testName: 'angular-localforage unit tests',
		recordScreenshots: false,
		startConnect: true,
		options: {
			'selenium-version': '2.41.0'
		},
		connectOptions: {
			port: 5757,
			logfile: 'sauce_connect.log'
		}
	},
	// Increase timeout in case connection in CI is slow
	captureTimeout: 120000,
	browserDisconnectTimeout: 10000,
	browserDisconnectTolerance: 2,
	browserNoActivityTimeout: 30000,
	browsers: Object.keys(customLaunchers),
	customLaunchers: customLaunchers,
	reporters: ['progress', 'saucelabs'],
	port: 9876,
	colors: true
}<|endoftext|>