<commit_before>/**
 * GridView
 *
 * Created by GUY on 2016/1/11.
 * @class BI.GridView
 * @extends BI.Widget
 */
BI.GridView = BI.inherit(BI.Widget, {
    _defaultConfig: function () {
        return BI.extend(BI.GridView.superclass._defaultConfig.apply(this, arguments), {
            baseCls: "bi-grid-view",
            // width: 400, //必设
            // height: 300, //必设
            overflowX: true,
            overflowY: true,
            overscanColumnCount: 0,
            overscanRowCount: 0,
            rowHeightGetter: BI.emptyFn, // number类型或function类型
            columnWidthGetter: BI.emptyFn, // number类型或function类型
            // estimatedColumnSize: 100, //columnWidthGetter为function时必设
            // estimatedRowSize: 30, //rowHeightGetter为function时必设
            scrollLeft: 0,
            scrollTop: 0,
            items: []
        });
    },

    render: function () {
        var self = this, o = this.options;
        this.renderedCells = [];
        this.renderedKeys = [];
        this.renderRange = {};
        this._scrollLock = false;
        this._debounceRelease = BI.debounce(function () {
            self._scrollLock = false;
        }, 1000 / 60);
        this.container = BI._lazyCreateWidget({
            type: "bi.absolute"
        });
        this.element.scroll(function () {
            if (self._scrollLock === true) {
                return;
            }
            o.scrollLeft = self.element.scrollLeft();
            o.scrollTop = self.element.scrollTop();
            self._calculateChildrenToRender();
            self.fireEvent(BI.GridView.EVENT_SCROLL, {
                scrollLeft: o.scrollLeft,
                scrollTop: o.scrollTop
            });
        });
        BI._lazyCreateWidget({
            type: "bi.vertical",
            element: this,
            scrollable: o.overflowX === true && o.overflowY === true,
            scrolly: o.overflowX === false && o.overflowY === true,
            scrollx: o.overflowX === true && o.overflowY === false,
            items: [this.container]
        });
        if (o.items.length > 0) {
            this._calculateSizeAndPositionData();
            this._populate();
        }
    },

    // mounted之后绑定事件
    mounted: function () {
        var o = this.options;
        if (o.scrollLeft !== 0 || o.scrollTop !== 0) {
            this.element.scrollTop(o.scrollTop);
            this.element.scrollLeft(o.scrollLeft);
        }
    },

    _calculateSizeAndPositionData: function () {
        var o = this.options;
        this.rowCount = 0;
        if (BI.isNumber(o.columnCount)) {
        } else if (o.items.length > 0) {
            this.options.columnCount = o.items[0].length;
        }
        if (BI.isNumber(o.rowCount)) {
        } else {
            this.options.rowCount = o.items.length;
        }
        this._columnSizeAndPositionManager = new BI.ScalingCellSizeAndPositionManager(o.columnCount, o.columnWidthGetter, o.estimatedColumnSize);
        this._rowSizeAndPositionManager = new BI.ScalingCellSizeAndPositionManager(o.rowCount, o.rowHeightGetter, o.estimatedRowSize);
    },

    _getOverscanIndices: function (cellCount, overscanCellsCount, startIndex, stopIndex) {
        return {
            overscanStartIndex: Math.max(0, startIndex - overscanCellsCount),
            overscanStopIndex: Math.min(cellCount - 1, stopIndex + overscanCellsCount)
        };
    },

    _calculateChildrenToRender: function () {
        var self = this, o = this.options;

        var width = o.width, height = o.height, scrollLeft = BI.clamp(o.scrollLeft, 0, this._getMaxScrollLeft()),
            scrollTop = BI.clamp(o.scrollTop, 0, this._getMaxScrollTop()),
            overscanColumnCount = o.overscanColumnCount, overscanRowCount = o.overscanRowCount;

        if (height > 0 && width > 0) {
            var visibleColumnIndices = this._columnSizeAndPositionManager.getVisibleCellRange(width, scrollLeft);
            var visibleRowIndices = this._rowSizeAndPositionManager.getVisibleCellRange(height, scrollTop);

            if (BI.isEmpty(visibleColumnIndices) || BI.isEmpty(visibleRowIndices)) {
                return;
            }
            var horizontalOffsetAdjustment = this._columnSizeAndPositionManager.getOffsetAdjustment(width, scrollLeft);
            var verticalOffsetAdjustment = this._rowSizeAndPositionManager.getOffsetAdjustment(height, scrollTop);

            this._renderedColumnStartIndex = visibleColumnIndices.start;
            this._renderedColumnStopIndex = visibleColumnIndices.stop;
            this._renderedRowStartIndex = visibleRowIndices.start;
            this._renderedRowStopIndex = visibleRowIndices.stop;

            var overscanColumnIndices = this._getOverscanIndices(o.columnCount, overscanColumnCount, this._renderedColumnStartIndex, this._renderedColumnStopIndex);

            var overscanRowIndices = this._getOverscanIndices(o.rowCount, overscanRowCount, this._renderedRowStartIndex, this._renderedRowStopIndex);

            var columnStartIndex = overscanColumnIndices.overscanStartIndex;
            var columnStopIndex = overscanColumnIndices.overscanStopIndex;
            var rowStartIndex = overscanRowIndices.overscanStartIndex;
            var rowStopIndex = overscanRowIndices.overscanStopIndex;

            // 算区间size
            var minRowDatum = this._rowSizeAndPositionManager.getSizeAndPositionOfCell(rowStartIndex);
            var minColumnDatum = this._columnSizeAndPositionManager.getSizeAndPositionOfCell(columnStartIndex);
            var maxRowDatum = this._rowSizeAndPositionManager.getSizeAndPositionOfCell(rowStopIndex);
            var maxColumnDatum = this._columnSizeAndPositionManager.getSizeAndPositionOfCell(columnStopIndex);
            var top = minRowDatum.offset + verticalOffsetAdjustment;
            var left = minColumnDatum.offset + horizontalOffsetAdjustment;
            var bottom = maxRowDatum.offset + verticalOffsetAdjustment + maxRowDatum.size;
            var right = maxColumnDatum.offset + horizontalOffsetAdjustment + maxColumnDatum.size;
            // 如果滚动的区间并没有超出渲染的范围
            if (top >= this.renderRange.minY && bottom <= this.renderRange.maxY && left >= this.renderRange.minX && right <= this.renderRange.maxX) {
                return;
            }

            var renderedCells = [], renderedKeys = {}, renderedWidgets = {};
            var minX = this._getMaxScrollLeft(), minY = this._getMaxScrollTop(), maxX = 0, maxY = 0;
            var count = 0;
            for (var rowIndex = rowStartIndex; rowIndex <= rowStopIndex; rowIndex++) {
                var rowDatum = this._rowSizeAndPositionManager.getSizeAndPositionOfCell(rowIndex);

                for (var columnIndex = columnStartIndex; columnIndex <= columnStopIndex; columnIndex++) {
                    var key = rowIndex + "-" + columnIndex;
                    var columnDatum = this._columnSizeAndPositionManager.getSizeAndPositionOfCell(columnIndex);

                    var index = this.renderedKeys[key] && this.renderedKeys[key][2];
                    var child;
                    if (index >= 0) {
                        // if (columnDatum.size !== this.renderedCells[index]._width) {
                        // this.renderedCells[index]._width = columnDatum.size;
                        this.renderedCells[index].el.setWidth(columnDatum.size);
                        // }
                        // if (rowDatum.size !== this.renderedCells[index]._height) {
                        // this.renderedCells[index]._height = rowDatum.size;
                        this.renderedCells[index].el.setHeight(rowDatum.size);
                        // }
                        // if (this.renderedCells[index]._left !== columnDatum.offset + horizontalOffsetAdjustment) {
                        this.renderedCells[index].el.element.css("left", (columnDatum.offset + horizontalOffsetAdjustment) / BI.pixRatio + BI.pixUnit);
                        // }
                        // if (this.renderedCells[index]._top !== rowDatum.offset + verticalOffsetAdjustment) {
                        this.renderedCells[index].el.element.css("top", (rowDatum.offset + verticalOffsetAdjustment) / BI.pixRatio + BI.pixUnit);
                        // }
                        child = this.renderedCells[index].el;
                        renderedCells.push(this.renderedCells[index]);
                    } else {
                        child = BI._lazyCreateWidget(BI.extend({
                            type: "bi.label",
                            width: columnDatum.size,
                            height: rowDatum.size
                        }, o.items[rowIndex][columnIndex], {
                            cls: (o.items[rowIndex][columnIndex].cls || "") + " grid-cell" + (rowIndex === 0 ? " first-row" : "") + (columnIndex === 0 ? " first-col" : ""),
                            _rowIndex: rowIndex,
                            _columnIndex: columnIndex,
                            _left: columnDatum.offset + horizontalOffsetAdjustment,
                            _top: rowDatum.offset + verticalOffsetAdjustment
                        }), this);
                        renderedCells.push({
                            el: child,
                            left: columnDatum.offset + horizontalOffsetAdjustment,
                            top: rowDatum.offset + verticalOffsetAdjustment,
                            _left: columnDatum.offset + horizontalOffsetAdjustment,
                            _top: rowDatum.offset + verticalOffsetAdjustment
                            // _width: columnDatum.size,
                            // _height: rowDatum.size
                        });
                    }
                    minX = Math.min(minX, columnDatum.offset + horizontalOffsetAdjustment);
                    maxX = Math.max(maxX, columnDatum.offset + horizontalOffsetAdjustment + columnDatum.size);
                    minY = Math.min(minY, rowDatum.offset + verticalOffsetAdjustment);
                    maxY = Math.max(maxY, rowDatum.offset + verticalOffsetAdjustment + rowDatum.size);
                    renderedKeys[key] = [rowIndex, columnIndex, count];
                    renderedWidgets[count] = child;
                    count++;
                }
            }
            // 已存在的， 需要添加的和需要删除的
            var existSet = {}, addSet = {}, deleteArray = [];
            BI.each(renderedKeys, function (i, key) {
                if (self.renderedKeys[i]) {
                    existSet[i] = key;
                } else {
                    addSet[i] = key;
                }
            });
            BI.each(this.renderedKeys, function (i, key) {
                if (existSet[i]) {
                    return;
                }
                if (addSet[i]) {
                    return;
                }
                deleteArray.push(key[2]);
            });
            BI.each(deleteArray, function (i, index) {
                // 性能优化，不调用destroy方法防止触发destroy事件
                self.renderedCells[index].el._destroy();
            });
            var addedItems = [];
            BI.each(addSet, function (index, key) {
                addedItems.push(renderedCells[key[2]]);
            });
            // 与listview一样, 给上下文
            this.container.addItems(addedItems, this);
            // 拦截父子级关系
            this.container._children = renderedWidgets;
            this.container.attr("items", renderedCells);
            this.renderedCells = renderedCells;
            this.renderedKeys = renderedKeys;
            this.renderRange = {minX: minX, minY: minY, maxX: maxX, maxY: maxY};
        }
    },

    _getMaxScrollLeft: function () {
        return Math.max(0, this._getContainerWidth() - this.options.width + (this.options.overflowX ? BI.DOM.getScrollWidth() : 0));
    },

    _getMaxScrollTop: function () {
        return Math.max(0, this._getContainerHeight() - this.options.height + (this.options.overflowY ? BI.DOM.getScrollWidth() : 0));
    },

    _getContainerWidth: function () {
        return this.options.columnCount * this.options.estimatedColumnSize;
    },

    _getContainerHeight: function () {
        return this.options.rowCount * this.options.estimatedRowSize;
    },

    _populate: function (items) {
        var self = this, o = this.options;
        this._reRange();
        if (items && items !== this.options.items) {
            this.options.items = items;
            this._calculateSizeAndPositionData();
        }
        if (o.items.length > 0) {
            this.container.setWidth(this._getContainerWidth());
            this.container.setHeight(this._getContainerHeight());

            // 元素未挂载时不能设置scrollTop
            this._debounceRelease();
            try {
                this.element.scrollTop(o.scrollTop);
                this.element.scrollLeft(o.scrollLeft);
            } catch (e) {
            }
            this._calculateChildrenToRender();
        }
    },

    setScrollLeft: function (scrollLeft) {
        if (this.options.scrollLeft === scrollLeft) {
            return;
        }
        this._scrollLock = true;
        this.options.scrollLeft = BI.clamp(scrollLeft || 0, 0, this._getMaxScrollLeft());
        this._debounceRelease();
        this.element.scrollLeft(this.options.scrollLeft);
        this._calculateChildrenToRender();
    },

    setScrollTop: function (scrollTop) {
        if (this.options.scrollTop === scrollTop) {
            return;
        }
        this._scrollLock = true;
        this.options.scrollTop = BI.clamp(scrollTop || 0, 0, this._getMaxScrollTop());
        this._debounceRelease();
        this.element.scrollTop(this.options.scrollTop);
        this._calculateChildrenToRender();
    },

    setColumnCount: function (columnCount) {
        this.options.columnCount = columnCount;
    },

    setRowCount: function (rowCount) {
        this.options.rowCount = rowCount;
    },

    setOverflowX: function (b) {
        var self = this;
        if (this.options.overflowX !== !!b) {
            this.options.overflowX = !!b;
            BI.nextTick(function () {
                self.element.css({overflowX: b ? "auto" : "hidden"});
            });
        }
    },

    setOverflowY: function (b) {
        var self = this;
        if (this.options.overflowY !== !!b) {
            this.options.overflowY = !!b;
            BI.nextTick(function () {
                self.element.css({overflowY: b ? "auto" : "hidden"});
            });
        }
    },

    getScrollLeft: function () {
        return this.options.scrollLeft;
    },

    getScrollTop: function () {
        return this.options.scrollTop;
    },

    getMaxScrollLeft: function () {
        return this._getMaxScrollLeft();
    },

    getMaxScrollTop: function () {
        return this._getMaxScrollTop();
    },

    setEstimatedColumnSize: function (width) {
        this.options.estimatedColumnSize = width;
    },

    setEstimatedRowSize: function (height) {
        this.options.estimatedRowSize = height;
    },

    // 重新计算children
    _reRange: function () {
        this.renderRange = {};
    },

    _clearChildren: function () {
        this.container._children = {};
        this.container.attr("items", []);
    },

    restore: function () {
        BI.each(this.renderedCells, function (i, cell) {
            cell.el._destroy();
        });
        this._clearChildren();
        this.renderedCells = [];
        this.renderedKeys = [];
        this.renderRange = {};
        this._scrollLock = false;
    },

    populate: function (items) {
        if (items && items !== this.options.items) {
            this.restore();
        }
        this._populate(items);
    }
});
BI.GridView.EVENT_SCROLL = "EVENT_SCROLL";
BI.shortcut("bi.grid_view", BI.GridView);
<commit_msg>BI-94391 fix: grid populate空数组滚动条仍然存在<commit_after>/**
 * GridView
 *
 * Created by GUY on 2016/1/11.
 * @class BI.GridView
 * @extends BI.Widget
 */
BI.GridView = BI.inherit(BI.Widget, {
    _defaultConfig: function () {
        return BI.extend(BI.GridView.superclass._defaultConfig.apply(this, arguments), {
            baseCls: "bi-grid-view",
            // width: 400, //必设
            // height: 300, //必设
            overflowX: true,
            overflowY: true,
            overscanColumnCount: 0,
            overscanRowCount: 0,
            rowHeightGetter: BI.emptyFn, // number类型或function类型
            columnWidthGetter: BI.emptyFn, // number类型或function类型
            // estimatedColumnSize: 100, //columnWidthGetter为function时必设
            // estimatedRowSize: 30, //rowHeightGetter为function时必设
            scrollLeft: 0,
            scrollTop: 0,
            items: []
        });
    },

    render: function () {
        var self = this, o = this.options;
        this.renderedCells = [];
        this.renderedKeys = [];
        this.renderRange = {};
        this._scrollLock = false;
        this._debounceRelease = BI.debounce(function () {
            self._scrollLock = false;
        }, 1000 / 60);
        this.container = BI._lazyCreateWidget({
            type: "bi.absolute"
        });
        this.element.scroll(function () {
            if (self._scrollLock === true) {
                return;
            }
            o.scrollLeft = self.element.scrollLeft();
            o.scrollTop = self.element.scrollTop();
            self._calculateChildrenToRender();
            self.fireEvent(BI.GridView.EVENT_SCROLL, {
                scrollLeft: o.scrollLeft,
                scrollTop: o.scrollTop
            });
        });
        BI._lazyCreateWidget({
            type: "bi.vertical",
            element: this,
            scrollable: o.overflowX === true && o.overflowY === true,
            scrolly: o.overflowX === false && o.overflowY === true,
            scrollx: o.overflowX === true && o.overflowY === false,
            items: [this.container]
        });
        if (o.items.length > 0) {
            this._calculateSizeAndPositionData();
            this._populate();
        }
    },

    // mounted之后绑定事件
    mounted: function () {
        var o = this.options;
        if (o.scrollLeft !== 0 || o.scrollTop !== 0) {
            this.element.scrollTop(o.scrollTop);
            this.element.scrollLeft(o.scrollLeft);
        }
    },

    _calculateSizeAndPositionData: function () {
        var o = this.options;
        this.rowCount = 0;
        if (BI.isNumber(o.columnCount)) {
        } else if (o.items.length > 0) {
            this.options.columnCount = o.items[0].length;
        }
        if (BI.isNumber(o.rowCount)) {
        } else {
            this.options.rowCount = o.items.length;
        }
        this._columnSizeAndPositionManager = new BI.ScalingCellSizeAndPositionManager(o.columnCount, o.columnWidthGetter, o.estimatedColumnSize);
        this._rowSizeAndPositionManager = new BI.ScalingCellSizeAndPositionManager(o.rowCount, o.rowHeightGetter, o.estimatedRowSize);
    },

    _getOverscanIndices: function (cellCount, overscanCellsCount, startIndex, stopIndex) {
        return {
            overscanStartIndex: Math.max(0, startIndex - overscanCellsCount),
            overscanStopIndex: Math.min(cellCount - 1, stopIndex + overscanCellsCount)
        };
    },

    _calculateChildrenToRender: function () {
        var self = this, o = this.options;

        var width = o.width, height = o.height, scrollLeft = BI.clamp(o.scrollLeft, 0, this._getMaxScrollLeft()),
            scrollTop = BI.clamp(o.scrollTop, 0, this._getMaxScrollTop()),
            overscanColumnCount = o.overscanColumnCount, overscanRowCount = o.overscanRowCount;

        if (height > 0 && width > 0) {
            var visibleColumnIndices = this._columnSizeAndPositionManager.getVisibleCellRange(width, scrollLeft);
            var visibleRowIndices = this._rowSizeAndPositionManager.getVisibleCellRange(height, scrollTop);

            if (BI.isEmpty(visibleColumnIndices) || BI.isEmpty(visibleRowIndices)) {
                return;
            }
            var horizontalOffsetAdjustment = this._columnSizeAndPositionManager.getOffsetAdjustment(width, scrollLeft);
            var verticalOffsetAdjustment = this._rowSizeAndPositionManager.getOffsetAdjustment(height, scrollTop);

            this._renderedColumnStartIndex = visibleColumnIndices.start;
            this._renderedColumnStopIndex = visibleColumnIndices.stop;
            this._renderedRowStartIndex = visibleRowIndices.start;
            this._renderedRowStopIndex = visibleRowIndices.stop;

            var overscanColumnIndices = this._getOverscanIndices(o.columnCount, overscanColumnCount, this._renderedColumnStartIndex, this._renderedColumnStopIndex);

            var overscanRowIndices = this._getOverscanIndices(o.rowCount, overscanRowCount, this._renderedRowStartIndex, this._renderedRowStopIndex);

            var columnStartIndex = overscanColumnIndices.overscanStartIndex;
            var columnStopIndex = overscanColumnIndices.overscanStopIndex;
            var rowStartIndex = overscanRowIndices.overscanStartIndex;
            var rowStopIndex = overscanRowIndices.overscanStopIndex;

            // 算区间size
            var minRowDatum = this._rowSizeAndPositionManager.getSizeAndPositionOfCell(rowStartIndex);
            var minColumnDatum = this._columnSizeAndPositionManager.getSizeAndPositionOfCell(columnStartIndex);
            var maxRowDatum = this._rowSizeAndPositionManager.getSizeAndPositionOfCell(rowStopIndex);
            var maxColumnDatum = this._columnSizeAndPositionManager.getSizeAndPositionOfCell(columnStopIndex);
            var top = minRowDatum.offset + verticalOffsetAdjustment;
            var left = minColumnDatum.offset + horizontalOffsetAdjustment;
            var bottom = maxRowDatum.offset + verticalOffsetAdjustment + maxRowDatum.size;
            var right = maxColumnDatum.offset + horizontalOffsetAdjustment + maxColumnDatum.size;
            // 如果滚动的区间并没有超出渲染的范围
            if (top >= this.renderRange.minY && bottom <= this.renderRange.maxY && left >= this.renderRange.minX && right <= this.renderRange.maxX) {
                return;
            }

            var renderedCells = [], renderedKeys = {}, renderedWidgets = {};
            var minX = this._getMaxScrollLeft(), minY = this._getMaxScrollTop(), maxX = 0, maxY = 0;
            var count = 0;
            for (var rowIndex = rowStartIndex; rowIndex <= rowStopIndex; rowIndex++) {
                var rowDatum = this._rowSizeAndPositionManager.getSizeAndPositionOfCell(rowIndex);

                for (var columnIndex = columnStartIndex; columnIndex <= columnStopIndex; columnIndex++) {
                    var key = rowIndex + "-" + columnIndex;
                    var columnDatum = this._columnSizeAndPositionManager.getSizeAndPositionOfCell(columnIndex);

                    var index = this.renderedKeys[key] && this.renderedKeys[key][2];
                    var child;
                    if (index >= 0) {
                        // if (columnDatum.size !== this.renderedCells[index]._width) {
                        // this.renderedCells[index]._width = columnDatum.size;
                        this.renderedCells[index].el.setWidth(columnDatum.size);
                        // }
                        // if (rowDatum.size !== this.renderedCells[index]._height) {
                        // this.renderedCells[index]._height = rowDatum.size;
                        this.renderedCells[index].el.setHeight(rowDatum.size);
                        // }
                        // if (this.renderedCells[index]._left !== columnDatum.offset + horizontalOffsetAdjustment) {
                        this.renderedCells[index].el.element.css("left", (columnDatum.offset + horizontalOffsetAdjustment) / BI.pixRatio + BI.pixUnit);
                        // }
                        // if (this.renderedCells[index]._top !== rowDatum.offset + verticalOffsetAdjustment) {
                        this.renderedCells[index].el.element.css("top", (rowDatum.offset + verticalOffsetAdjustment) / BI.pixRatio + BI.pixUnit);
                        // }
                        child = this.renderedCells[index].el;
                        renderedCells.push(this.renderedCells[index]);
                    } else {
                        child = BI._lazyCreateWidget(BI.extend({
                            type: "bi.label",
                            width: columnDatum.size,
                            height: rowDatum.size
                        }, o.items[rowIndex][columnIndex], {
                            cls: (o.items[rowIndex][columnIndex].cls || "") + " grid-cell" + (rowIndex === 0 ? " first-row" : "") + (columnIndex === 0 ? " first-col" : ""),
                            _rowIndex: rowIndex,
                            _columnIndex: columnIndex,
                            _left: columnDatum.offset + horizontalOffsetAdjustment,
                            _top: rowDatum.offset + verticalOffsetAdjustment
                        }), this);
                        renderedCells.push({
                            el: child,
                            left: columnDatum.offset + horizontalOffsetAdjustment,
                            top: rowDatum.offset + verticalOffsetAdjustment,
                            _left: columnDatum.offset + horizontalOffsetAdjustment,
                            _top: rowDatum.offset + verticalOffsetAdjustment
                            // _width: columnDatum.size,
                            // _height: rowDatum.size
                        });
                    }
                    minX = Math.min(minX, columnDatum.offset + horizontalOffsetAdjustment);
                    maxX = Math.max(maxX, columnDatum.offset + horizontalOffsetAdjustment + columnDatum.size);
                    minY = Math.min(minY, rowDatum.offset + verticalOffsetAdjustment);
                    maxY = Math.max(maxY, rowDatum.offset + verticalOffsetAdjustment + rowDatum.size);
                    renderedKeys[key] = [rowIndex, columnIndex, count];
                    renderedWidgets[count] = child;
                    count++;
                }
            }
            // 已存在的， 需要添加的和需要删除的
            var existSet = {}, addSet = {}, deleteArray = [];
            BI.each(renderedKeys, function (i, key) {
                if (self.renderedKeys[i]) {
                    existSet[i] = key;
                } else {
                    addSet[i] = key;
                }
            });
            BI.each(this.renderedKeys, function (i, key) {
                if (existSet[i]) {
                    return;
                }
                if (addSet[i]) {
                    return;
                }
                deleteArray.push(key[2]);
            });
            BI.each(deleteArray, function (i, index) {
                // 性能优化，不调用destroy方法防止触发destroy事件
                self.renderedCells[index].el._destroy();
            });
            var addedItems = [];
            BI.each(addSet, function (index, key) {
                addedItems.push(renderedCells[key[2]]);
            });
            // 与listview一样, 给上下文
            this.container.addItems(addedItems, this);
            // 拦截父子级关系
            this.container._children = renderedWidgets;
            this.container.attr("items", renderedCells);
            this.renderedCells = renderedCells;
            this.renderedKeys = renderedKeys;
            this.renderRange = {minX: minX, minY: minY, maxX: maxX, maxY: maxY};
        }
    },

    _getMaxScrollLeft: function () {
        return Math.max(0, this._getContainerWidth() - this.options.width + (this.options.overflowX ? BI.DOM.getScrollWidth() : 0));
    },

    _getMaxScrollTop: function () {
        return Math.max(0, this._getContainerHeight() - this.options.height + (this.options.overflowY ? BI.DOM.getScrollWidth() : 0));
    },

    _getContainerWidth: function () {
        return this.options.columnCount * this.options.estimatedColumnSize;
    },

    _getContainerHeight: function () {
        return this.options.rowCount * this.options.estimatedRowSize;
    },

    _populate: function (items) {
        var self = this, o = this.options;
        this._reRange();
        if (items && items !== this.options.items) {
            this.options.items = items;
            this._calculateSizeAndPositionData();
        }
        if (o.items.length > 0) {
            this.container.setWidth(this._getContainerWidth());
            this.container.setHeight(this._getContainerHeight());

            // 元素未挂载时不能设置scrollTop
            this._debounceRelease();
            try {
                this.element.scrollTop(o.scrollTop);
                this.element.scrollLeft(o.scrollLeft);
            } catch (e) {
            }
            this._calculateChildrenToRender();
        } else {
            this.container.setWidth("auto");
            this.container.setHeight("auto");
        }
    },

    setScrollLeft: function (scrollLeft) {
        if (this.options.scrollLeft === scrollLeft) {
            return;
        }
        this._scrollLock = true;
        this.options.scrollLeft = BI.clamp(scrollLeft || 0, 0, this._getMaxScrollLeft());
        this._debounceRelease();
        this.element.scrollLeft(this.options.scrollLeft);
        this._calculateChildrenToRender();
    },

    setScrollTop: function (scrollTop) {
        if (this.options.scrollTop === scrollTop) {
            return;
        }
        this._scrollLock = true;
        this.options.scrollTop = BI.clamp(scrollTop || 0, 0, this._getMaxScrollTop());
        this._debounceRelease();
        this.element.scrollTop(this.options.scrollTop);
        this._calculateChildrenToRender();
    },

    setColumnCount: function (columnCount) {
        this.options.columnCount = columnCount;
    },

    setRowCount: function (rowCount) {
        this.options.rowCount = rowCount;
    },

    setOverflowX: function (b) {
        var self = this;
        if (this.options.overflowX !== !!b) {
            this.options.overflowX = !!b;
            BI.nextTick(function () {
                self.element.css({overflowX: b ? "auto" : "hidden"});
            });
        }
    },

    setOverflowY: function (b) {
        var self = this;
        if (this.options.overflowY !== !!b) {
            this.options.overflowY = !!b;
            BI.nextTick(function () {
                self.element.css({overflowY: b ? "auto" : "hidden"});
            });
        }
    },

    getScrollLeft: function () {
        return this.options.scrollLeft;
    },

    getScrollTop: function () {
        return this.options.scrollTop;
    },

    getMaxScrollLeft: function () {
        return this._getMaxScrollLeft();
    },

    getMaxScrollTop: function () {
        return this._getMaxScrollTop();
    },

    setEstimatedColumnSize: function (width) {
        this.options.estimatedColumnSize = width;
    },

    setEstimatedRowSize: function (height) {
        this.options.estimatedRowSize = height;
    },

    // 重新计算children
    _reRange: function () {
        this.renderRange = {};
    },

    _clearChildren: function () {
        this.container._children = {};
        this.container.attr("items", []);
    },

    restore: function () {
        BI.each(this.renderedCells, function (i, cell) {
            cell.el._destroy();
        });
        this._clearChildren();
        this.renderedCells = [];
        this.renderedKeys = [];
        this.renderRange = {};
        this._scrollLock = false;
    },

    populate: function (items) {
        if (items && items !== this.options.items) {
            this.restore();
        }
        this._populate(items);
    }
});
BI.GridView.EVENT_SCROLL = "EVENT_SCROLL";
BI.shortcut("bi.grid_view", BI.GridView);
<|endoftext|>


<commit_before>// Copyright 2014 Globo.com Player authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

var $ = require('jquery');
var _ = require('underscore');
var extend = require('./utils').extend;
var BaseObject = require('./base_object');

var UIObject = function(options) {
  this.id = _.uniqueId('c');
  options || (options = {});
  _.extend(this, _.pick(options, viewOptions));
  this._ensureElement();
  this.initialize.apply(this, arguments);
  this.delegateEvents();
};

var delegateEventSplitter = /^(\S+)\s*(.*)$/;

var viewOptions = ['container', 'el', 'id', 'attributes', 'className', 'tagName', 'events'];

_.extend(UIObject.prototype, BaseObject.prototype, {

  tagName: 'div',

  $: function(selector) {
    return this.$el.find(selector);
  },

  initialize: function(){},

  render: function() {
    return this;
  },

  remove: function() {
    this.$el.remove();
    this.stopListening();
    return this;
  },

  setElement: function(element, delegate) {
    if (this.$el) this.undelegateEvents();
    this.$el = element instanceof $ ? element : $(element);
    this.el = this.$el[0];
    if (delegate !== false) this.delegateEvents();
    return this;
  },

  delegateEvents: function(events) {
    if (!(events || (events = _.result(this, 'events')))) return this;
    this.undelegateEvents();
    for (var key in events) {
      var method = events[key];
      if (!_.isFunction(method)) method = this[events[key]];
      if (!method) continue;

      var match = key.match(delegateEventSplitter);
      var eventName = match[1], selector = match[2];
      method = _.bind(method, this);
      eventName += '.delegateEvents' + this.cid;
      if (selector === '') {
        this.$el.on(eventName, method);
      } else {
        this.$el.on(eventName, selector, method);
      }
    }
    return this;
  },

  undelegateEvents: function() {
    this.$el.off('.delegateEvents' + this.cid);
    return this;
  },

  _ensureElement: function() {
    if (!this.el) {
      var attrs = _.extend({}, _.result(this, 'attributes'));
      if (this.id) attrs.id = _.result(this, 'id');
      if (this.className) attrs['class'] = _.result(this, 'className');
      var $el = $('<' + _.result(this, 'tagName') + '>').attr(attrs);
      this.setElement($el, false);
    } else {
      this.setElement(_.result(this, 'el'), false);
    }
  }
});

UIObject.extend = extend;

module.exports = UIObject;
<commit_msg>ui object: back to cid, id conflicts with tag's id<commit_after>// Copyright 2014 Globo.com Player authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

var $ = require('jquery');
var _ = require('underscore');
var extend = require('./utils').extend;
var BaseObject = require('./base_object');

var UIObject = function(options) {
  this.cid = _.uniqueId('c');
  options || (options = {});
  _.extend(this, _.pick(options, viewOptions));
  this._ensureElement();
  this.initialize.apply(this, arguments);
  this.delegateEvents();
};

var delegateEventSplitter = /^(\S+)\s*(.*)$/;

var viewOptions = ['container', 'el', 'id', 'attributes', 'className', 'tagName', 'events'];

_.extend(UIObject.prototype, BaseObject.prototype, {

  tagName: 'div',

  $: function(selector) {
    return this.$el.find(selector);
  },

  initialize: function(){},

  render: function() {
    return this;
  },

  remove: function() {
    this.$el.remove();
    this.stopListening();
    return this;
  },

  setElement: function(element, delegate) {
    if (this.$el) this.undelegateEvents();
    this.$el = element instanceof $ ? element : $(element);
    this.el = this.$el[0];
    if (delegate !== false) this.delegateEvents();
    return this;
  },

  delegateEvents: function(events) {
    if (!(events || (events = _.result(this, 'events')))) return this;
    this.undelegateEvents();
    for (var key in events) {
      var method = events[key];
      if (!_.isFunction(method)) method = this[events[key]];
      if (!method) continue;

      var match = key.match(delegateEventSplitter);
      var eventName = match[1], selector = match[2];
      method = _.bind(method, this);
      eventName += '.delegateEvents' + this.cid;
      if (selector === '') {
        this.$el.on(eventName, method);
      } else {
        this.$el.on(eventName, selector, method);
      }
    }
    return this;
  },

  undelegateEvents: function() {
    this.$el.off('.delegateEvents' + this.cid);
    return this;
  },

  _ensureElement: function() {
    if (!this.el) {
      var attrs = _.extend({}, _.result(this, 'attributes'));
      if (this.id) attrs.id = _.result(this, 'id');
      if (this.className) attrs['class'] = _.result(this, 'className');
      var $el = $('<' + _.result(this, 'tagName') + '>').attr(attrs);
      this.setElement($el, false);
    } else {
      this.setElement(_.result(this, 'el'), false);
    }
  }
});

UIObject.extend = extend;

module.exports = UIObject;
<|endoftext|>


<commit_before>(function() {

    /**
     * A Moveable implementation.
     * @constructor
     * @implements{Moveable}
     */
    function ClientMoveable(object, transform, constraint) {
    	/**
		 * Object which shall be moveable
		 * @protected
		 * @type {Object}
		 */
		this.object = object;
		/**
		 * Transform coords of the object and the Moveable
		 * @protected
		 * @type {Object}
		 */
		this.transform = transform;
		/**
		 * Constraint of the movement
		 * @protected
		 * @type {Constraint}
		 */
		this.constraint = constraint;
		/**
		 * Queue of movements
		 * @private
		 * @type {Array.<{tween: tween, startPosition:Array.<number>, endPosition:Array.<number>, startOrientation:Array.<number>, endOrientation:Array.<number>}>}
		 */
		this.motionQueue = new Array();
    };

    var p = ClientMoveable.prototype;

    /** @inheritDoc */
    p.setPosition = function(position){
		if(this.constraint.constrainTranslation(position, this))
			this.transform.translation.set(position[0],position[1],position[2]);
		return this;
    };

    /** @inheritDoc */
	p.setOrientation = function(orientation){
		if(this.constraint.constrainRotation(orientation, this)){
			this.transform.rotation.setQuaternion( new XML3DVec3(orientation[0],orientation[1],orientation[2]), orientation[3] );
		}
		return this;
    };

    /** @inheritDoc */
    p.setScale = function(scale){
    	this.transform.scale.set(new XML3DVec3(scale[0], scale[1], scale[2]));
    };

    /** @inheritDoc */
    p.getPosition = function(){
    	return [this.transform.translation.x, this.transform.translation.y, this.transform.translation.z];
    };

    /** @inheritDoc */
    p.getOrientation = function(){
    	var axis = this.transform.rotation.axis;
    	var angle = this.transform.rotation.angle;
    	return XMOT.axisAngleToQuaternion([axis.x, axis.y, axis.z], angle);
    };

    /** @inheritDoc */
    p.getScale = function(){
    	return this.transform.scale;
    };

    /** @inheritDoc */
    p.translate = function(translation){
    	var currentPos = this.getPosition();
    	return this.setPosition([currentPos[0]+translation[0], currentPos[1]+translation[1], currentPos[2]+translation[2]]);
    };

    /** @inheritDoc */
    p.rotate = function(orientation){
		var modifier = new XML3DRotation();
		modifier.setQuaternion( new XML3DVec3(orientation[0],orientation[1],orientation[2]), orientation[3] );
		var destination = this.transform.rotation.multiply( modifier );
		if(this.constraint.constrainRotation(orientation, this))
			this.transform.rotation.set(destination);
		return this;
    };

    /** @inheritDoc */
    p.scale = function(factor){
    	this.transform.scale.multiply(new XML3DVec3(factor[0], factor[1], factor[2]));
    	return this;
    };

    /** @inheritDoc */
    p.moveTo = function(position, orientation, time, opt){
    	opt = opt || {};
    	//no movement needed
    	var queueingAllowed = opt.queueing || true;
		if( (position == undefined && orientation == undefined) || //nowhere to moveto
				( !queueingAllowed && this.movementInProgress()) ) //queuing forbiden, but something in progress
					return this;

		//create new queue entry of the new given data:
		var newEntry = {};
		var tween = new TWEEN.Tween({t:0}).to({t:time}, time);
		if(opt.delay != undefined) tween.delay(opt.delay);
		var that = this;
		var easing = opt.easing;
		//update callback
		tween.onUpdate( function() {
			//this is the data interpolated by the tween
			that.movement(this.t, 0, time, easing);
		} );
		//callback on complete
		tween.onComplete( function(){
			//this is the data interpolated by the tween

			//start next tween (beginning of the queue), if there is any in the queue
			if(that.motionQueue.length > 1){ //we did not remove the finished one yet
				//set startpos / ori of the following moveTo, instead of setting at definition
				var followingMovement = that.motionQueue[1];
				var endedMovement = that.motionQueue[0];
				followingMovement.startPosition = endedMovement.endPosition || that.getPosition();
				followingMovement.startOrientation = endedMovement.endOrientation || that.getOrientation();
				followingMovement.tween.start();
			}
			//remove finished tween from the beginning of the queue
			that.motionQueue.shift();
			//callback after the movement finished
			if(opt.callback && typeof(opt.callback) === "function")
				opt.callback();
		});
		newEntry.tween = tween;
		newEntry.endPosition = position;
		newEntry.endOrientation = orientation;
		//default start values, are the current values
		//those are overwritten if a tween ends before us, see the onComplete callback
		newEntry.startPosition = this.getPosition();
		newEntry.startOrientation = this.getOrientation();

		//push tween to the end of the queue and start if queue was empty
		this.motionQueue.push(newEntry);
		if( this.motionQueue.length-1 == 0){
			newEntry.tween.start();
			if(!XMOT.animating) {
				XMOT.animate();
				XMOT.animating = true;
			}
		}
		return this;
    };

    /**
     * Applies one movement step to the moveable
     * @private
     * @param {number}currentTime
     * @param {number} startTime
     * @param {number} endTime
     * @param {Function} easing
     */
    p.movement = function(currentTime, startTime, endTime, easing){
		var t = (currentTime - startTime) / (endTime - startTime);
		if(easing && typeof(easing) === "function") t = easing(t); //otherwise its linear
		var pos = this.interpolatePosition(t);
		var ori = this.interpolateOrientation(t);
		this.setValue(pos, ori);
    };

    /**
     * Interpolates the position of the current movement
     * @private
     * @param {number} t interpolation parameter
     * @return {Array.<number>|undefined} position
     */
    p.interpolatePosition = function(t){
		var end = this.motionQueue[0].endPosition;
		if(end == undefined) return undefined;
		var start = this.motionQueue[0].startPosition;
		var ret = [];
		var i = 0;
		for(i=0; i<start.length; i++ ){
			ret[i] = start[i] + ( end[i] - start[i] ) * t;
		}
		return ret;
    };

    /**
     * interpoaltes the orientation of the current movement
     * @private
     * @param {number} t interpolation paramater
     * @return {Array.<number>|undefined} orientation
     */
    p.interpolateOrientation = function(t){
		var end = this.motionQueue[0].endOrientation;
		if(end == undefined) return undefined;
		var start = this.motionQueue[0].startOrientation;
		return XMOT.slerp(start, end, t);
    };

    /**
	 * Set position and animation of the moveable
	 * @private
	 * @param {Array.<number>|undefined} position
	 * @param {Array.<number>|undefined} orientation
	 */
	p.setValue = function(position, orientation){
		if(position != undefined)
			this.setPosition(position);
		if(orientation != undefined)
			this.setOrientation(orientation);
	};

	/** @inheritDoc */
	p.movementInProgress = function(){
		return this.motionQueue.length > 0;
	};

    /**@inheritDoc */
    p.stop = function(){
		this.motionQueue.shift().tween.stop();
		this.motionQueue = []; //clear array
    };

    /** @inheritDoc */
    p.setConstraint = function(constraint){
		this.constraint = constraint;
    };

    //export
    XMOT.ClientMoveable = ClientMoveable;

}());<commit_msg>Small adoption for the native version<commit_after>(function() {

    /**
     * A Moveable implementation.
     * @constructor
     * @implements{Moveable}
     */
    function ClientMoveable(object, transform, constraint) {
    	/**
		 * Object which shall be moveable
		 * @protected
		 * @type {Object}
		 */
		this.object = object;
		/**
		 * Transform coords of the object and the Moveable
		 * @protected
		 * @type {Object}
		 */
		this.transform = transform;
		/**
		 * Constraint of the movement
		 * @protected
		 * @type {Constraint}
		 */
		this.constraint = constraint;
		/**
		 * Queue of movements
		 * @private
		 * @type {Array.<{tween: tween, startPosition:Array.<number>, endPosition:Array.<number>, startOrientation:Array.<number>, endOrientation:Array.<number>}>}
		 */
		this.motionQueue = new Array();
    };

    var p = ClientMoveable.prototype;

    /** @inheritDoc */
    p.setPosition = function(position){
		if(this.constraint.constrainTranslation(position, this))
			this.transform.translation.set(new XML3DVec3(position[0],position[1],position[2]));
		return this;
    };

    /** @inheritDoc */
	p.setOrientation = function(orientation){
		if(this.constraint.constrainRotation(orientation, this)){
			this.transform.rotation.setQuaternion( new XML3DVec3(orientation[0],orientation[1],orientation[2]), orientation[3] );
		}
		return this;
    };

    /** @inheritDoc */
    p.setScale = function(scale){
    	this.transform.scale.set(new XML3DVec3(scale[0], scale[1], scale[2]));
    };

    /** @inheritDoc */
    p.getPosition = function(){
    	return [this.transform.translation.x, this.transform.translation.y, this.transform.translation.z];
    };

    /** @inheritDoc */
    p.getOrientation = function(){
    	var axis = this.transform.rotation.axis;
    	var angle = this.transform.rotation.angle;
    	return XMOT.axisAngleToQuaternion([axis.x, axis.y, axis.z], angle);
    };

    /** @inheritDoc */
    p.getScale = function(){
    	return this.transform.scale;
    };

    /** @inheritDoc */
    p.translate = function(translation){
    	var currentPos = this.getPosition();
    	return this.setPosition([currentPos[0]+translation[0], currentPos[1]+translation[1], currentPos[2]+translation[2]]);
    };

    /** @inheritDoc */
    p.rotate = function(orientation){
		var modifier = new XML3DRotation();
		modifier.setQuaternion( new XML3DVec3(orientation[0],orientation[1],orientation[2]), orientation[3] );
		var destination = this.transform.rotation.multiply( modifier );
		if(this.constraint.constrainRotation(orientation, this))
			this.transform.rotation.set(destination);
		return this;
    };

    /** @inheritDoc */
    p.scale = function(factor){
    	this.transform.scale.multiply(new XML3DVec3(factor[0], factor[1], factor[2]));
    	return this;
    };

    /** @inheritDoc */
    p.moveTo = function(position, orientation, time, opt){
    	opt = opt || {};
    	//no movement needed
    	var queueingAllowed = opt.queueing || true;
		if( (position == undefined && orientation == undefined) || //nowhere to moveto
				( !queueingAllowed && this.movementInProgress()) ) //queuing forbiden, but something in progress
					return this;

		//create new queue entry of the new given data:
		var newEntry = {};
		var tween = new TWEEN.Tween({t:0}).to({t:time}, time);
		if(opt.delay != undefined) tween.delay(opt.delay);
		var that = this;
		var easing = opt.easing;
		//update callback
		tween.onUpdate( function() {
			//this is the data interpolated by the tween
			that.movement(this.t, 0, time, easing);
		} );
		//callback on complete
		tween.onComplete( function(){
			//this is the data interpolated by the tween

			//start next tween (beginning of the queue), if there is any in the queue
			if(that.motionQueue.length > 1){ //we did not remove the finished one yet
				//set startpos / ori of the following moveTo, instead of setting at definition
				var followingMovement = that.motionQueue[1];
				var endedMovement = that.motionQueue[0];
				followingMovement.startPosition = endedMovement.endPosition || that.getPosition();
				followingMovement.startOrientation = endedMovement.endOrientation || that.getOrientation();
				followingMovement.tween.start();
			}
			//remove finished tween from the beginning of the queue
			that.motionQueue.shift();
			//callback after the movement finished
			if(opt.callback && typeof(opt.callback) === "function")
				opt.callback();
		});
		newEntry.tween = tween;
		newEntry.endPosition = position;
		newEntry.endOrientation = orientation;
		//default start values, are the current values
		//those are overwritten if a tween ends before us, see the onComplete callback
		newEntry.startPosition = this.getPosition();
		newEntry.startOrientation = this.getOrientation();

		//push tween to the end of the queue and start if queue was empty
		this.motionQueue.push(newEntry);
		if( this.motionQueue.length-1 == 0){
			newEntry.tween.start();
			if(!XMOT.animating) {
				XMOT.animate();
				XMOT.animating = true;
			}
		}
		return this;
    };

    /**
     * Applies one movement step to the moveable
     * @private
     * @param {number}currentTime
     * @param {number} startTime
     * @param {number} endTime
     * @param {Function} easing
     */
    p.movement = function(currentTime, startTime, endTime, easing){
		var t = (currentTime - startTime) / (endTime - startTime);
		if(easing && typeof(easing) === "function") t = easing(t); //otherwise its linear
		var pos = this.interpolatePosition(t);
		var ori = this.interpolateOrientation(t);
		this.setValue(pos, ori);
    };

    /**
     * Interpolates the position of the current movement
     * @private
     * @param {number} t interpolation parameter
     * @return {Array.<number>|undefined} position
     */
    p.interpolatePosition = function(t){
		var end = this.motionQueue[0].endPosition;
		if(end == undefined) return undefined;
		var start = this.motionQueue[0].startPosition;
		var ret = [];
		var i = 0;
		for(i=0; i<start.length; i++ ){
			ret[i] = start[i] + ( end[i] - start[i] ) * t;
		}
		return ret;
    };

    /**
     * interpoaltes the orientation of the current movement
     * @private
     * @param {number} t interpolation paramater
     * @return {Array.<number>|undefined} orientation
     */
    p.interpolateOrientation = function(t){
		var end = this.motionQueue[0].endOrientation;
		if(end == undefined) return undefined;
		var start = this.motionQueue[0].startOrientation;
		return XMOT.slerp(start, end, t);
    };

    /**
	 * Set position and animation of the moveable
	 * @private
	 * @param {Array.<number>|undefined} position
	 * @param {Array.<number>|undefined} orientation
	 */
	p.setValue = function(position, orientation){
		if(position != undefined)
			this.setPosition(position);
		if(orientation != undefined)
			this.setOrientation(orientation);
	};

	/** @inheritDoc */
	p.movementInProgress = function(){
		return this.motionQueue.length > 0;
	};

    /**@inheritDoc */
    p.stop = function(){
		this.motionQueue.shift().tween.stop();
		this.motionQueue = []; //clear array
    };

    /** @inheritDoc */
    p.setConstraint = function(constraint){
		this.constraint = constraint;
    };

    //export
    XMOT.ClientMoveable = ClientMoveable;

}());<|endoftext|>


<commit_before>const { body, param, query, validationResult } = require('express-validator/check')
const { matchedData, sanitize } = require('express-validator/filter')
//
// API BASE CLASS
//
exports.BaseController = class {
  constructor (Model) {
    // table model
    this.Model = Model.orm
    this.modelId = Model.id
    // check method
    this.check = {body, param, query}

    this.ValidateIdParams = [
      param('id', 'id should be a int').custom(id => {
        if (!Number.isInteger(Number(id))) {
          throw new Error('id format illegal')
        }
        return id
      }),
      sanitize('id').toInt()
    ]

    this.ValidateCreateKeys = []
    // Bind Main Function
    this.getAll = this.getAll.bind(this)
    this.getById = this.getById.bind(this)
    this.create = this.create.bind(this)
    this.update = this.update.bind(this)
    this.delete = this.delete.bind(this)
  }

  _notEmpty (value) {
    if (value && Array.isArray(value)) {
      return !value.includes('')
    } else if (value) {
      return true
    } else {
      return false
    }
  }

  _validateRequest (req, res) {
    return new Promise((resolve, reject) => {
      const errors = validationResult(req)
      // check id param valid
      if (errors.mapped().id) {
        throw new Error('404')
      } else if (!errors.isEmpty()) {
        const badRequest = new Error('400')
        badRequest.payload = errors.mapped()
        throw badRequest
      }
      resolve()
    })
  }

  // use in update & delete request
  // generate different {'item_id': req.params.id}
  _setIdFilter (req) {
    const filterRules = {}
    filterRules[this.modelId] = req.params.id
    return filterRules
  }

  _returnItemOrNotFound (Item, res) {
    if (Item) {
      res.json(Item)
    } else {
      throw new Error('404')
    }
  }

  _sendErrorResponse (err, res) {
    (err.name === 'SequelizeForeignKeyConstraintError') ? res.status(400).json({error: 'This foreign id is not exist'})
    : (err.message === '400') ? res.status(400).json({error: err.payload})
    : (err.message === '404') ? res.status(404).json({error: 'not found'})
                              : res.status(500).json({error: err.message})
  }

  async getAll (req, res) {
    try {
      const Items = await this.Model.findAll({where: req.query, oreder: [['created_at', 'DESC']]})
      if (Items.length) {
        res.json(Items)
      } else {
        res.status(404).json({error: 'not found'})
      }
    } catch (err) {
      res.status(500).json({error: err})
    }
  }

  async getById (req, res) {
    try {
      await this._validateRequest(req, res)

      const params = await matchedData(req)
      const singleItem = await this.Model.findById(params.id)
      this._returnItemOrNotFound(singleItem, res)
    } catch (err) {
      this._sendErrorResponse(err, res)
    }
  }

  async create (req, res) {
    try {
      await this._validateRequest(req, res)

      const newItem = await this.Model.create(req.body)
      res.set('location', `${req.path}/${newItem[this.modelId]}`)
      res.status(201).json(newItem)
    } catch (err) {
      this._sendErrorResponse(err, res)
    }
  }

  async update (req, res) {
    try {
      const filterRules = this._setIdFilter(req)
      const updateItem = await this.Model.update(req.body, {where: filterRules, returning: true})
      if (updateItem[0]) {
        res.status(200).json(updateItem[1][0])
      } else {
        res.status(404).json({error: 'not found'})
      }
    } catch (err) {
      this._sendErrorResponse(err, res)
    }
  }

  async delete (req, res) {
    try {
      const filterRules = this._setIdFilter(req)
      const deleteResult = await this.Model.destroy({where: filterRules})
      if (deleteResult) {
        res.status(204).json(deleteResult)
      } else {
        res.status(404).json(deleteResult)
      }
    } catch (err) {
      res.status(500).json({error: err})
    }
  }
}
<commit_msg>[Update] foreign key not exist error msg<commit_after>const { body, param, query, validationResult } = require('express-validator/check')
const { matchedData, sanitize } = require('express-validator/filter')
//
// API BASE CLASS
//
exports.BaseController = class {
  constructor (Model) {
    // table model
    this.Model = Model.orm
    this.modelId = Model.id
    // check method
    this.check = {body, param, query}

    this.ValidateIdParams = [
      param('id', 'id should be a int').custom(id => {
        if (!Number.isInteger(Number(id))) {
          throw new Error('id format illegal')
        }
        return id
      }),
      sanitize('id').toInt()
    ]

    this.ValidateCreateKeys = []
    // Bind Main Function
    this.getAll = this.getAll.bind(this)
    this.getById = this.getById.bind(this)
    this.create = this.create.bind(this)
    this.update = this.update.bind(this)
    this.delete = this.delete.bind(this)
  }

  _notEmpty (value) {
    if (value && Array.isArray(value)) {
      return !value.includes('')
    } else if (value) {
      return true
    } else {
      return false
    }
  }

  _validateRequest (req, res) {
    return new Promise((resolve, reject) => {
      const errors = validationResult(req)
      // check id param valid
      if (errors.mapped().id) {
        throw new Error('404')
      } else if (!errors.isEmpty()) {
        const badRequest = new Error('400')
        badRequest.payload = errors.mapped()
        throw badRequest
      }
      resolve()
    })
  }

  // use in update & delete request
  // generate different {'item_id': req.params.id}
  _setIdFilter (req) {
    const filterRules = {}
    filterRules[this.modelId] = req.params.id
    return filterRules
  }

  _returnItemOrNotFound (Item, res) {
    if (Item) {
      res.json(Item)
    } else {
      throw new Error('404')
    }
  }

  _sendErrorResponse (err, res) {
    (err.name === 'SequelizeForeignKeyConstraintError') ? res.status(400).json({error: 'The foreign id is not exist'})
    : (err.message === '400') ? res.status(400).json({error: err.payload})
    : (err.message === '404') ? res.status(404).json({error: 'not found'})
                              : res.status(500).json({error: err.message})
  }

  async getAll (req, res) {
    try {
      const Items = await this.Model.findAll({where: req.query, oreder: [['created_at', 'DESC']]})
      if (Items.length) {
        res.json(Items)
      } else {
        res.status(404).json({error: 'not found'})
      }
    } catch (err) {
      res.status(500).json({error: err})
    }
  }

  async getById (req, res) {
    try {
      await this._validateRequest(req, res)

      const params = await matchedData(req)
      const singleItem = await this.Model.findById(params.id)
      this._returnItemOrNotFound(singleItem, res)
    } catch (err) {
      this._sendErrorResponse(err, res)
    }
  }

  async create (req, res) {
    try {
      await this._validateRequest(req, res)

      const newItem = await this.Model.create(req.body)
      res.set('location', `${req.path}/${newItem[this.modelId]}`)
      res.status(201).json(newItem)
    } catch (err) {
      this._sendErrorResponse(err, res)
    }
  }

  async update (req, res) {
    try {
      const filterRules = this._setIdFilter(req)
      const updateItem = await this.Model.update(req.body, {where: filterRules, returning: true})
      if (updateItem[0]) {
        res.status(200).json(updateItem[1][0])
      } else {
        res.status(404).json({error: 'not found'})
      }
    } catch (err) {
      this._sendErrorResponse(err, res)
    }
  }

  async delete (req, res) {
    try {
      const filterRules = this._setIdFilter(req)
      const deleteResult = await this.Model.destroy({where: filterRules})
      if (deleteResult) {
        res.status(204).json(deleteResult)
      } else {
        res.status(404).json(deleteResult)
      }
    } catch (err) {
      res.status(500).json({error: err})
    }
  }
}
<|endoftext|>


<commit_before>#!/usr/bin/env node
/* @flow */
import * as path from 'path'
import {
  createGitHubRepository,
  getAuthorName,
  getAuthorEmail,
  getGitHubRepositoryName,
  enableTravis,
  writeTemplates,
  installPackages,
  commitChanges
} from 'create-npm/src/actions'
import templates from 'create-npm/src/templates'

async function run () {
  const packageName = process.argv[2]
  const localPath = path.resolve(packageName)

  await createGitHubRepository(localPath)
  const repositoryName = await getGitHubRepositoryName(localPath)

  await enableTravis(repositoryName)

  const authorName = await getAuthorName()
  const authorEmail = await getAuthorEmail()
  await writeTemplates(localPath, templates, {
    packageName,
    repositoryName,
    authorName,
    authorEmail
  })

  await installPackages(
    localPath,
    [],
    [
      'ava',
      'babel-plugin-package-name-import',
      'babel-preset-env',
      'babel-preset-flow',
      'babel-preset-stage-0',
      'babel-register',
      'build-esm',
      'flow-bin',
      'prettier',
      'standard-esnext'
    ]
  )

  await commitChanges(localPath, 'Create npm package')
}
run()
<commit_msg>Add progress logs<commit_after>#!/usr/bin/env node
/* @flow */
import * as path from 'path'
import {
  createGitHubRepository,
  getAuthorName,
  getAuthorEmail,
  getGitHubRepositoryName,
  enableTravis,
  writeTemplates,
  installPackages,
  commitChanges
} from 'create-npm/src/actions'
import templates from 'create-npm/src/templates'

async function run () {
  const packageName = process.argv[2]
  const localPath = path.resolve(packageName)

  await createGitHubRepository(localPath)
  const repositoryName = await getGitHubRepositoryName(localPath)
  console.log(`Created GitHub repository ${repositoryName}`)

  await enableTravis(repositoryName)
  console.log(`Enabled Travis CI for ${repositoryName}`)

  const authorName = await getAuthorName()
  const authorEmail = await getAuthorEmail()
  await writeTemplates(localPath, templates, {
    packageName,
    repositoryName,
    authorName,
    authorEmail
  })
  console.log('Wrote boilerplate files')

  await installPackages(
    localPath,
    [],
    [
      'ava',
      'babel-plugin-package-name-import',
      'babel-preset-env',
      'babel-preset-flow',
      'babel-preset-stage-0',
      'babel-register',
      'build-esm',
      'flow-bin',
      'prettier',
      'standard-esnext'
    ]
  )
  console.log('Installed npm packages')

  await commitChanges(localPath, 'Create npm package')
  console.log('Committed changes')
}
run()
<|endoftext|>


<commit_before>function captureUncaughtExceptions(window, handler, shim) {
  if (!window) { return; }
  var oldOnError;

  if (typeof handler._rollbarOldOnError === 'function') {
    oldOnError = handler._rollbarOldOnError;
  } else if (window.onerror && !window.onerror.belongsToShim) {
    oldOnError = window.onerror;
    handler._rollbarOldOnError = oldOnError;
  }

  var fn = function() {
    var args = Array.prototype.slice.call(arguments, 0);
    _rollbarWindowOnError(window, handler, oldOnError, args);
  };
  fn.belongsToShim = shim;
  window.onerror = fn;
}

function _rollbarWindowOnError(window, r, old, args) {
  if (window._rollbarWrappedError) {
    if (!args[4]) {
      args[4] = window._rollbarWrappedError;
    }
    if (!args[5]) {
      args[5] = window._rollbarWrappedError._rollbarContext;
    }
    window._rollbarWrappedError = null;
  }

  r.handleUncaughtException.apply(r, args);
  if (old) {
    old.apply(window, args);
  }
}

function captureUnhandledRejections(window, handler, shim) {
  if (!window) { return; }

  if (typeof window._rollbarURH === 'function' && window._rollbaarURH.belongsToShim) {
    window.removeEventListener('unhandledrejection', window._rollbarURH);
  }

  var rejectionHandler = function (event) {
    var reason = event.reason;
    var promise = event.promise;
    var detail = event.detail;

    if (!reason && detail) {
      reason = detail.reason;
      promise = detail.promise;
    }

    if (handler && handler.handleUnhandledRejection) {
      handler.handleUnhandledRejection(reason, promise);
    }
  };
  rejectionHandler.belongsToShim = shim;
  window._rollbarURH = rejectionHandler;
  window.addEventListener('unhandledrejection', rejectionHandler);
}

function wrapGlobals(window, handler, shim) {
  if (!window) { return; }
  // Adapted from https://github.com/bugsnag/bugsnag-js
  var globals = 'EventTarget,Window,Node,ApplicationCache,AudioTrackList,ChannelMergerNode,CryptoOperation,EventSource,FileReader,HTMLUnknownElement,IDBDatabase,IDBRequest,IDBTransaction,KeyOperation,MediaController,MessagePort,ModalWindow,Notification,SVGElementInstance,Screen,TextTrack,TextTrackCue,TextTrackList,WebSocket,WebSocketWorker,Worker,XMLHttpRequest,XMLHttpRequestEventTarget,XMLHttpRequestUpload'.split(',');
  var i, global;
  for (i = 0; i < globals.length; ++i) {
    global = globals[i];

    if (window[global] && window[global].prototype) {
      _extendListenerPrototype(handler, window[global].prototype, shim);
    }
  }
}

function _extendListenerPrototype(handler, prototype, shim) {
  if (prototype.hasOwnProperty && prototype.hasOwnProperty('addEventListener')) {
    var oldAddEventListener = prototype.addEventListener;
    while (oldAddEventListener._rollbarOldAdd && oldAddEventListener.belongsToShim) {
      oldAddEventListener = oldAddEventListener._rollbarOldAdd;
    }
    var addFn = function(event, callback, bubble) {
      oldAddEventListener.call(this, event, handler.wrap(callback), bubble);
    };
    addFn._rollbarOldAdd = oldAddEventListener;
    addFn.belongsToShim = shim;
    prototype.addEventListener = addFn;

    var oldRemoveEventListener = prototype.removeEventListener;
    while (oldRemoveEventListener._rollbarOldRemove && oldRemoveEventListener.belongsToShim) {
      oldRemoveEventListener = oldRemoveEventListener._rollbarOldRemove;
    }
    var removeFn = function(event, callback, bubble) {
      oldRemoveEventListener.call(this, event, callback && callback._wrapped || callback, bubble);
    };
    removeFn._rollbarOldRemove = oldRemoveEventListener;
    removeFn.belongsToShim = shim;
    prototype.removeEventListener = removeFn;
  }
}

module.exports = {
  captureUncaughtExceptions: captureUncaughtExceptions,
  captureUnhandledRejections: captureUnhandledRejections,
  wrapGlobals: wrapGlobals
};
<commit_msg>Fix a small typo @rokob<commit_after>function captureUncaughtExceptions(window, handler, shim) {
  if (!window) { return; }
  var oldOnError;

  if (typeof handler._rollbarOldOnError === 'function') {
    oldOnError = handler._rollbarOldOnError;
  } else if (window.onerror && !window.onerror.belongsToShim) {
    oldOnError = window.onerror;
    handler._rollbarOldOnError = oldOnError;
  }

  var fn = function() {
    var args = Array.prototype.slice.call(arguments, 0);
    _rollbarWindowOnError(window, handler, oldOnError, args);
  };
  fn.belongsToShim = shim;
  window.onerror = fn;
}

function _rollbarWindowOnError(window, r, old, args) {
  if (window._rollbarWrappedError) {
    if (!args[4]) {
      args[4] = window._rollbarWrappedError;
    }
    if (!args[5]) {
      args[5] = window._rollbarWrappedError._rollbarContext;
    }
    window._rollbarWrappedError = null;
  }

  r.handleUncaughtException.apply(r, args);
  if (old) {
    old.apply(window, args);
  }
}

function captureUnhandledRejections(window, handler, shim) {
  if (!window) { return; }

  if (typeof window._rollbarURH === 'function' && window._rollbarURH.belongsToShim) {
    window.removeEventListener('unhandledrejection', window._rollbarURH);
  }

  var rejectionHandler = function (event) {
    var reason = event.reason;
    var promise = event.promise;
    var detail = event.detail;

    if (!reason && detail) {
      reason = detail.reason;
      promise = detail.promise;
    }

    if (handler && handler.handleUnhandledRejection) {
      handler.handleUnhandledRejection(reason, promise);
    }
  };
  rejectionHandler.belongsToShim = shim;
  window._rollbarURH = rejectionHandler;
  window.addEventListener('unhandledrejection', rejectionHandler);
}

function wrapGlobals(window, handler, shim) {
  if (!window) { return; }
  // Adapted from https://github.com/bugsnag/bugsnag-js
  var globals = 'EventTarget,Window,Node,ApplicationCache,AudioTrackList,ChannelMergerNode,CryptoOperation,EventSource,FileReader,HTMLUnknownElement,IDBDatabase,IDBRequest,IDBTransaction,KeyOperation,MediaController,MessagePort,ModalWindow,Notification,SVGElementInstance,Screen,TextTrack,TextTrackCue,TextTrackList,WebSocket,WebSocketWorker,Worker,XMLHttpRequest,XMLHttpRequestEventTarget,XMLHttpRequestUpload'.split(',');
  var i, global;
  for (i = 0; i < globals.length; ++i) {
    global = globals[i];

    if (window[global] && window[global].prototype) {
      _extendListenerPrototype(handler, window[global].prototype, shim);
    }
  }
}

function _extendListenerPrototype(handler, prototype, shim) {
  if (prototype.hasOwnProperty && prototype.hasOwnProperty('addEventListener')) {
    var oldAddEventListener = prototype.addEventListener;
    while (oldAddEventListener._rollbarOldAdd && oldAddEventListener.belongsToShim) {
      oldAddEventListener = oldAddEventListener._rollbarOldAdd;
    }
    var addFn = function(event, callback, bubble) {
      oldAddEventListener.call(this, event, handler.wrap(callback), bubble);
    };
    addFn._rollbarOldAdd = oldAddEventListener;
    addFn.belongsToShim = shim;
    prototype.addEventListener = addFn;

    var oldRemoveEventListener = prototype.removeEventListener;
    while (oldRemoveEventListener._rollbarOldRemove && oldRemoveEventListener.belongsToShim) {
      oldRemoveEventListener = oldRemoveEventListener._rollbarOldRemove;
    }
    var removeFn = function(event, callback, bubble) {
      oldRemoveEventListener.call(this, event, callback && callback._wrapped || callback, bubble);
    };
    removeFn._rollbarOldRemove = oldRemoveEventListener;
    removeFn.belongsToShim = shim;
    prototype.removeEventListener = removeFn;
  }
}

module.exports = {
  captureUncaughtExceptions: captureUncaughtExceptions,
  captureUnhandledRejections: captureUnhandledRejections,
  wrapGlobals: wrapGlobals
};
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2013, Twitter Inc. and other contributors
 * Released under the MIT License
 */

var vows        = require('vows'),
    assert      = require('assert'),
    WebPageTest = require('../lib/webpagetest');

var wptServer = 'https://www.example.com:1234/foo/bar/';

vows.describe('Edge Cases').addBatch({
  'An HTTPS WebPageTest Server with API key': {
    topic: new WebPageTest(wptServer, '1234567890'),

    'gets a simple test request with another HTTP server and API key': {
      topic: function (wpt) {
        wpt.runTest('http://foobar.com', {
          server: 'http://wpt.com',
          key: '0987654321',
          dryRun: true
        }, this.callback);
      },
      'then returns the API url': function (err, data) {
        if (err) throw err;
        assert.equal(data.url, 'http://wpt.com/runtest.php?url=http%3A%2F%2Ffoobar.com&k=0987654321&f=json');
      }
    }

  },

  'An HTTP WebPageTest Server with API key': {
    topic: new WebPageTest(wptServer, '1234567890'),

    'gets a simple test request with another HTTPS server and API key': {
      topic: function (wpt) {
        wpt.runTest('http://foobar.com', {
            server: 'https://wpt.com:4321/baz/',
          key: '0987654321',
          dryRun: true
        }, this.callback);
      },
      'then returns the API url': function (err, data) {
        if (err) throw err;
        assert.equal(data.url, 'https://wpt.com:4321/baz/runtest.php?url=http%3A%2F%2Ffoobar.com&k=0987654321&f=json');
      }
    }

  },

  'An Example WebPageTest Server': {
    topic: new WebPageTest(wptServer),

    'gets a test with blocking urls and spof array request': {
      topic: function (wpt) {
        wpt.runTest('http://foobar.com', {
          dryRun: true,
          block: ['foo.com', 'bar.com'],
          spof: ['baz.com', 'qux.com']
        }, this.callback);
      },
      'then returns the API url': function (err, data) {
        if (err) throw err;
        assert.equal(data.url, wptServer + 'runtest.php?url=http%3A%2F%2Ffoobar.com&block=foo.com%20bar.com&spof=baz.com%20qux.com&f=json');
      }
    },

    'gets a test with blocking urls and spof strings request': {
      topic: function (wpt) {
        wpt.runTest('http://foobar.com', {
          dryRun: true,
          block: 'foo.com bar.com',
          spof: ['baz.com qux.com']
        }, this.callback);
      },
      'then returns the API url': function (err, data) {
        if (err) throw err;
        assert.equal(data.url, wptServer + 'runtest.php?url=http%3A%2F%2Ffoobar.com&block=foo.com%20bar.com&spof=baz.com%20qux.com&f=json');
      }
    },

    'gets a test with dial up connectivity and location': {
      topic: function (wpt) {
        wpt.runTest('http://foobar.com', {
          dryRun: true,
          connectivity: 'Dial',
          location: 'somelocation'
        }, this.callback);
      },
      'then returns the API url with connectivity appended to location': function (err, data) {
        if (err) throw err;
        assert.equal(data.url, wptServer + 'runtest.php?url=http%3A%2F%2Ffoobar.com&location=somelocation.Dial&connectivity=Dial&f=json');
      }
    }

  }
}).export(module);
<commit_msg>added version test<commit_after>/**
 * Copyright (c) 2013, Twitter Inc. and other contributors
 * Released under the MIT License
 */

var vows        = require('vows'),
    assert      = require('assert'),
    WebPageTest = require('../lib/webpagetest'),
    packageJson = require('../package.json');

var wptServer = 'https://www.example.com:1234/foo/bar/';

vows.describe('Edge Cases').addBatch({
  'An HTTPS WebPageTest Server with API key': {
    topic: new WebPageTest(wptServer, '1234567890'),

    'gets a simple test request with another HTTP server and API key': {
      topic: function (wpt) {
        wpt.runTest('http://foobar.com', {
          server: 'http://wpt.com',
          key: '0987654321',
          dryRun: true
        }, this.callback);
      },
      'then returns the API url': function (err, data) {
        if (err) throw err;
        assert.equal(data.url, 'http://wpt.com/runtest.php?url=http%3A%2F%2Ffoobar.com&k=0987654321&f=json');
      }
    }

  },

  'An HTTP WebPageTest Server with API key': {
    topic: new WebPageTest(wptServer, '1234567890'),

    'gets a simple test request with another HTTPS server and API key': {
      topic: function (wpt) {
        wpt.runTest('http://foobar.com', {
            server: 'https://wpt.com:4321/baz/',
          key: '0987654321',
          dryRun: true
        }, this.callback);
      },
      'then returns the API url': function (err, data) {
        if (err) throw err;
        assert.equal(data.url, 'https://wpt.com:4321/baz/runtest.php?url=http%3A%2F%2Ffoobar.com&k=0987654321&f=json');
      }
    }

  },

  'An Example WebPageTest Server': {
    topic: new WebPageTest(wptServer),

    'gets a test with blocking urls and spof array request': {
      topic: function (wpt) {
        wpt.runTest('http://foobar.com', {
          dryRun: true,
          block: ['foo.com', 'bar.com'],
          spof: ['baz.com', 'qux.com']
        }, this.callback);
      },
      'then returns the API url': function (err, data) {
        if (err) throw err;
        assert.equal(data.url, wptServer + 'runtest.php?url=http%3A%2F%2Ffoobar.com&block=foo.com%20bar.com&spof=baz.com%20qux.com&f=json');
      }
    },

    'gets a test with blocking urls and spof strings request': {
      topic: function (wpt) {
        wpt.runTest('http://foobar.com', {
          dryRun: true,
          block: 'foo.com bar.com',
          spof: ['baz.com qux.com']
        }, this.callback);
      },
      'then returns the API url': function (err, data) {
        if (err) throw err;
        assert.equal(data.url, wptServer + 'runtest.php?url=http%3A%2F%2Ffoobar.com&block=foo.com%20bar.com&spof=baz.com%20qux.com&f=json');
      }
    },

    'gets a test with dial up connectivity and location': {
      topic: function (wpt) {
        wpt.runTest('http://foobar.com', {
          dryRun: true,
          connectivity: 'Dial',
          location: 'somelocation'
        }, this.callback);
      },
      'then returns the API url with connectivity appended to location': function (err, data) {
        if (err) throw err;
        assert.equal(data.url, wptServer + 'runtest.php?url=http%3A%2F%2Ffoobar.com&location=somelocation.Dial&connectivity=Dial&f=json');
      }
    },

    'get current version': {
      topic: function (wpt) {
        return wpt.version;
      },
      'then compares to package.json version': function (version) {
        assert.equal(version, packageJson.version);
      }
    }

  }
}).export(module);
<|endoftext|>


<commit_before>var assert = require("chai").assert;
require("./helpers/aboutEqual");

var Evaluascii = require("../evaluascii.js");

var formulae = [
    ["2", 2],
    ["3 * x", 12, { x: 4 }],
    ["- 2 ^ 4", -16],
    ["2 * 4 + 1 / 4 - 3 / -2^2 * -cos(PI)", 9],
    ["sin(PI / 2)", 1],
    ["sqrt(LN2 + x)", Math.sqrt(Math.LN2 + 5), { x: 5 }],
    ["hypot 10", 10],
    ["hypot 10^2", 100],
    ["hypot 10 * 2", 20],
    ["hypot(3, 4)", 5],
    ["(-b + sqrt(b^2 - 4 * a * c)) / (2 * a)", 3, { a: 2, b : -4, c: -6 }],
    ["(-b + sqrt(b^2 - 4a * c)) / (2 a)", 3, { a: 2, b : -4, c: -6 }],

    // Test implicit multiplication
    ["-2a + - 2 a + (2)(a a)", 6, { a: 3 }],

    // Test absolute value
    ["2 + -|6 - 2^4| * 9", -88],

    // Test custom functions
    ["incr 2 + incr(3)", 7, { incr: function(a) { return a + 1; } }],

    // Test factorial -40320
    ["-(2^3)!", -40320],

    // Test parens
    ["{1 + [2 - {3 + 4}])", -4]
];

describe("Evaluascii.js", function() {
    it("Calculates some formulae", function() {
        for (i in formulae) {
            var formula = formulae[i];
            var expression = formula[0];
            var result = formula[1];
            var locals = formula[2] || {};

            // console.log(expression); // For debugging

            assert.aboutEqual(Evaluascii.evaluate(expression, locals), result);
        }
    });
});<commit_msg>Added a new test for factorial rounding<commit_after>var assert = require("chai").assert;
require("./helpers/aboutEqual");

var Evaluascii = require("../evaluascii.js");

var formulae = [
    ["2", 2],
    ["3 * x", 12, { x: 4 }],
    ["- 2 ^ 4", -16],
    ["2 * 4 + 1 / 4 - 3 / -2^2 * -cos(PI)", 9],
    ["sin(PI / 2)", 1],
    ["sqrt(LN2 + x)", Math.sqrt(Math.LN2 + 5), { x: 5 }],
    ["hypot 10", 10],
    ["hypot 10^2", 100],
    ["hypot 10 * 2", 20],
    ["hypot(3, 4)", 5],
    ["(-b + sqrt(b^2 - 4 * a * c)) / (2 * a)", 3, { a: 2, b : -4, c: -6 }],
    ["(-b + sqrt(b^2 - 4a * c)) / (2 a)", 3, { a: 2, b : -4, c: -6 }],

    // Test implicit multiplication
    ["-2a + - 2 a + (2)(a a)", 6, { a: 3 }],

    // Test absolute value
    ["2 + -|6 - 2^4| * 9", -88],

    // Test custom functions
    ["incr 2 + incr(3)", 7, { incr: function(a) { return a + 1; } }],

    // Test factorial -40320
    ["-(2^3)!", -40320],
    ["4.5!", 5*4*3*2],

    // Test parens
    ["{1 + [2 - {3 + 4}])", -4]
];

describe("Evaluascii.js", function() {
    it("Calculates some formulae", function() {
        for (i in formulae) {
            var formula = formulae[i];
            var expression = formula[0];
            var result = formula[1];
            var locals = formula[2] || {};

            // console.log(expression); // For debugging

            assert.aboutEqual(Evaluascii.evaluate(expression, locals), result);
        }
    });
});<|endoftext|>


<commit_before>var assert = require('assert');
var expect = require('expect.js');
var fs = require('fs');
var Cookies = require('cookies');
var FakeMinder = require('../lib/fakeminder.js');

describe('FakeMinder', function() {
  var subject;
  var emptySession;

  beforeEach(function() {
    subject = new FakeMinder();
    emptySession = { 'user':'' };
    subject.config['siteminder'] = {
      'session_expiry_minutes':20
    };
    subject.config['target_site'] = {
      'root':'http://localhost:8000',
      'urls':{
        'logoff':'/system/logout',
        'not_authenticated':'/system/error/notauthenticated',
        'protected':'/protected'
      }
    };
  });

  it('it has an empty session', function() {
    // Act
    var emptySession = subject.emptySession;

    // Assert
    expect(emptySession).to.eql({'user':''});
  });

  it('parses the config.json file and writes it to the config property', function() {
    // Arrange
    var file = __dirname + '/../config.json';
    var json;
    var json = fs.readFileSync(file, 'utf8');
    json = JSON.parse(json);

    // Stub out the fs.readFile function.
    /*
    var rf = fs.readFileSync;
    fs.readFileSync = function(file, encoding) {
      this.readFileSync = rf;
      if (file.indexOf('/../config.json') > 0) {
        return json;
      } else {
        return;
      }
    };
    */

    // Act
    subject = new FakeMinder();

    // Assert
    expect(subject.config).to.eql(json);
  });

  describe('#handleRequest()', function() {
    var request;
    var response;

    beforeEach(function() {
      request = {};
      response = {};
      request['method'] = 'GET';
      request['url'] = 'http://localhost:8000/';
      response['setHeader'] = function(header, value) {
        this.headers = this.headers || {};
        this.headers[header] = value;
      };
      // Stubs for supporting cookie.js
      request['connection'] = { 'encrypted':false };
      request['setHeader'] = function(header, value) {
        this.headers = this.headers || {};
        this.headers[header] = value;
      };
      request['headers'] = {};
      response['getHeader'] = function(header) {
        this.headers = this.headers || {};
        return this.headers[header];
      };
      response['end'] = function() {

      };
    });

    it('adds a "x-proxied-by" header value with the host/port value of the proxy', function() {
      // Arrange

      // Act
      subject.handleRequest(request, response);

      // Assert
      expect(response.headers).to.be.ok();
      expect(response.headers).to.have.key('x-proxied-by');
      expect(response.headers['x-proxied-by']).to.equal('localhost:8000');
    });

    describe('when the request is not for a protected URI', function() {
      it('proxies the request', function() {
        // Arrange
        request.url = 'http://localhost:8000/public/home';

        // Act
        var proxied = subject.handleRequest(request, response);

        // Assert
        expect(proxied).to.be.ok();
      });

      it('does not set an SMSESSION cookie in the response', function() {
        // Arrange
        request.url = 'http://localhost:8000/public/home';

        // Act
        subject.handleRequest(request, response);

        // Assert
        console.log(response.headers);
        expect(response.headers['set-cookie']).to.not.be.ok();
      });
    });

    describe('when the request is for a protected URI', function() {

      describe('and the request has no SMSESSION cookie', function() {
        it('redirects the user to the Not Authenticated URI', function() {
          // Arrange
          request.url = 'http://localhost:8000/protected/home';

          // Act
          subject.handleRequest(request, response);

          // Assert
          expect(response.statusCode).to.be(302);
          expect(response.headers['Location']).to.be('http://localhost:8000/system/error/notauthenticated');
        });
      });

      describe('and the request has an SMSESSION cookie that does not exist', function() {
        it('redirects the user to the Not Authenticated URI', function() {
          // Arrange
          request.url = 'http://localhost:8000/protected/home';
          request.headers['cookie'] = 'SMSESSION=abc';

          // Act
          subject.handleRequest(request, response);

          // Assert
          expect(response.statusCode).to.be(302);
          expect(response.headers['Location']).to.be('http://localhost:8000/system/error/notauthenticated');
        });
      });

      describe('and the request has an SMSESSION cookie related to an expired session', function() {
        it('redirects the user to the Not Authenticated URI', function() {
          // Arrange
          request.url = 'http://localhost:8000/protected/home';
          request.headers['cookie'] = 'SMSESSION=xyz';
          var now = new Date();
          var sessionExpiry = new Date(now.getTime() - 30 * 60000);
          subject.sessions = {
            'xyz' : {
              'name' : 'bob',
              'session_expires' : sessionExpiry.toJSON()
            }
          };

          // Act
          subject.handleRequest(request, response);

          // Assert
          expect(response.statusCode).to.be(302);
          expect(response.headers['Location']).to.be('http://localhost:8000/system/error/notauthenticated');
        });
      });

      describe('and the request has an SMSESSION cookie related to a valid session', function() {
        beforeEach(function() {
          // Arrange
          request.url = 'http://localhost:8000/protected/home';
          request.headers['cookie'] = 'SMSESSION=xyz';
          var now = new Date();
          var sessionExpiry = new Date(now.getTime() - 10 * 60000);
          subject.sessions = {
            'xyz' : {
              'name' : 'bob',
              'session_expires' : sessionExpiry.toJSON()
            }
          };
          subject.config.users = {
            'bob' : {
              'auth_headers' : {
                'header1' : 'auth1',
                'header2' : 'auth2',
                'header3' : 'auth3'
              }
            }
          }
        });

        it('resets the expiration of the session', function() {
          // Arrange
          var now = new Date();
          var expected_expiry = new Date(now.getTime() + subject.config.siteminder.session_expiry_minutes * 60000);

          // Act
          var forward_to_proxy = subject.handleRequest(request, response);
          var session_expired_date = new Date(subject.sessions['xyz'].session_expires);

          // Assert
          expect(session_expired_date.getFullYear()).to.equal(expected_expiry.getFullYear());
          expect(session_expired_date.getMonth()).to.equal(expected_expiry.getMonth());
          expect(session_expired_date.getDay()).to.equal(expected_expiry.getDay());
          expect(session_expired_date.getHours()).to.equal(expected_expiry.getHours());
          expect(session_expired_date.getMinutes()).to.equal(expected_expiry.getMinutes());
          expect(session_expired_date.getSeconds()).to.equal(expected_expiry.getSeconds());
        });

        it('adds identity headers to the forwarded request', function() {
          // Act
          var forward_to_proxy = subject.handleRequest(request, response);

          // Assert
          expect(response.headers).to.have.keys(['header1', 'header2', 'header3']);
          expect(response.headers['header1']).to.equal('auth1');
          expect(response.headers['header2']).to.equal('auth2');
          expect(response.headers['header3']).to.equal('auth3');
        });

        it('forwards the request to the proxy', function() {
          // Act
          var forward_to_proxy = subject.handleRequest(request, response);

          // Assert
          expect(forward_to_proxy).to.be.ok();
          expect(response['statusCode']).to.be(undefined);
        });

        it('Sets the SMSESSION cookie to the session ID, domain being proxied and sets HttpOnly to true', function() {
          // Act
          subject.handleRequest(request, response);
          var cookie_jar = new Cookies(request, response);
          var session_cookie = cookie_jar.get('SMSESSION');

          // Assert
          expect(session_cookie).to.equal('xyz');
        });
      });
    });

    describe('when the logoff_url is requested', function() {
      it('adds an SMSESSION cookie with a value of LOGGEDOFF to the response', function() {
        // Arrange
        request.url = 'http://localhost:8000/system/logout';

        // Act
        subject.handleRequest(request, response);
        var cookies = response.headers['set-cookie'];

        // Assert
        expect(cookies).to.be.ok();
        expect(cookies).to.not.be.empty();
        expect(cookies[0]).to.contain('SMSESSION=LOGGEDOFF');
      });

      it('removes the existing session corresponding to the SMSESSION cookie value', function() {
        // Arrange
        request.url = 'http://localhost:8000/system/logout';
        subject.sessions = {'session1':{}, 'session2':{}, 'session3':{}};
        request.headers = {'cookie':'SMSESSION=session2'};

        // Act
        subject.handleRequest(request, response);

        // Assert
        expect(subject.sessions).to.not.have.key('session2');
      });
    });

    describe('when the request is a post to the login_url', function() {
      describe('when the credentials are valid', function() {
        it('destroys any existing session for the user');
        it('creates a new session for the user');
        it('adds an SMSESSION cookie with the session ID to the response');
        it('responds with a redirect to the TARGET URI');
      });

      describe('when the USER is not valid', function() {
        it('responds with a redirect to the bad login URI');
      });

      describe('when the PASSWORD is not valid', function() {
        it('responds with a redirect to the bad password URI');
        it('increments the number of login attempts associated with the user');
      });

      describe('when the number of login attempts has been exceeded', function() {
        it('responds with a redirect to the account locked URI');
      });
    });
  })

  describe('#beginSession()', function(req, res) {
  });
});<commit_msg>Removed redundant test<commit_after>var assert = require('assert');
var expect = require('expect.js');
var fs = require('fs');
var Cookies = require('cookies');
var FakeMinder = require('../lib/fakeminder.js');

describe('FakeMinder', function() {
  var subject;
  var emptySession;

  beforeEach(function() {
    subject = new FakeMinder();
    emptySession = { 'user':'' };
    subject.config['siteminder'] = {
      'session_expiry_minutes':20
    };
    subject.config['target_site'] = {
      'root':'http://localhost:8000',
      'urls':{
        'logoff':'/system/logout',
        'not_authenticated':'/system/error/notauthenticated',
        'protected':'/protected'
      }
    };
  });

  it('it has an empty session', function() {
    // Act
    var emptySession = subject.emptySession;

    // Assert
    expect(emptySession).to.eql({'user':''});
  });

  it('parses the config.json file and writes it to the config property', function() {
    // Arrange
    var file = __dirname + '/../config.json';
    var json;
    var json = fs.readFileSync(file, 'utf8');
    json = JSON.parse(json);

    // Stub out the fs.readFile function.
    /*
    var rf = fs.readFileSync;
    fs.readFileSync = function(file, encoding) {
      this.readFileSync = rf;
      if (file.indexOf('/../config.json') > 0) {
        return json;
      } else {
        return;
      }
    };
    */

    // Act
    subject = new FakeMinder();

    // Assert
    expect(subject.config).to.eql(json);
  });

  describe('#handleRequest()', function() {
    var request;
    var response;

    beforeEach(function() {
      request = {};
      response = {};
      request['method'] = 'GET';
      request['url'] = 'http://localhost:8000/';
      response['setHeader'] = function(header, value) {
        this.headers = this.headers || {};
        this.headers[header] = value;
      };
      // Stubs for supporting cookie.js
      request['connection'] = { 'encrypted':false };
      request['setHeader'] = function(header, value) {
        this.headers = this.headers || {};
        this.headers[header] = value;
      };
      request['headers'] = {};
      response['getHeader'] = function(header) {
        this.headers = this.headers || {};
        return this.headers[header];
      };
      response['end'] = function() {

      };
    });

    it('adds a "x-proxied-by" header value with the host/port value of the proxy', function() {
      // Arrange

      // Act
      subject.handleRequest(request, response);

      // Assert
      expect(response.headers).to.be.ok();
      expect(response.headers).to.have.key('x-proxied-by');
      expect(response.headers['x-proxied-by']).to.equal('localhost:8000');
    });

    describe('when the request is not for a protected URI', function() {
      it('proxies the request', function() {
        // Arrange
        request.url = 'http://localhost:8000/public/home';

        // Act
        var proxied = subject.handleRequest(request, response);

        // Assert
        expect(proxied).to.be.ok();
      });

      it('does not set an SMSESSION cookie in the response', function() {
        // Arrange
        request.url = 'http://localhost:8000/public/home';

        // Act
        subject.handleRequest(request, response);

        // Assert
        console.log(response.headers);
        expect(response.headers['set-cookie']).to.not.be.ok();
      });
    });

    describe('when the request is for a protected URI', function() {

      describe('and the request has no SMSESSION cookie', function() {
        it('redirects the user to the Not Authenticated URI', function() {
          // Arrange
          request.url = 'http://localhost:8000/protected/home';

          // Act
          subject.handleRequest(request, response);

          // Assert
          expect(response.statusCode).to.be(302);
          expect(response.headers['Location']).to.be('http://localhost:8000/system/error/notauthenticated');
        });
      });

      describe('and the request has an SMSESSION cookie that does not exist', function() {
        it('redirects the user to the Not Authenticated URI', function() {
          // Arrange
          request.url = 'http://localhost:8000/protected/home';
          request.headers['cookie'] = 'SMSESSION=abc';

          // Act
          subject.handleRequest(request, response);

          // Assert
          expect(response.statusCode).to.be(302);
          expect(response.headers['Location']).to.be('http://localhost:8000/system/error/notauthenticated');
        });
      });

      describe('and the request has an SMSESSION cookie related to an expired session', function() {
        it('redirects the user to the Not Authenticated URI', function() {
          // Arrange
          request.url = 'http://localhost:8000/protected/home';
          request.headers['cookie'] = 'SMSESSION=xyz';
          var now = new Date();
          var sessionExpiry = new Date(now.getTime() - 30 * 60000);
          subject.sessions = {
            'xyz' : {
              'name' : 'bob',
              'session_expires' : sessionExpiry.toJSON()
            }
          };

          // Act
          subject.handleRequest(request, response);

          // Assert
          expect(response.statusCode).to.be(302);
          expect(response.headers['Location']).to.be('http://localhost:8000/system/error/notauthenticated');
        });
      });

      describe('and the request has an SMSESSION cookie related to a valid session', function() {
        beforeEach(function() {
          // Arrange
          request.url = 'http://localhost:8000/protected/home';
          request.headers['cookie'] = 'SMSESSION=xyz';
          var now = new Date();
          var sessionExpiry = new Date(now.getTime() - 10 * 60000);
          subject.sessions = {
            'xyz' : {
              'name' : 'bob',
              'session_expires' : sessionExpiry.toJSON()
            }
          };
          subject.config.users = {
            'bob' : {
              'auth_headers' : {
                'header1' : 'auth1',
                'header2' : 'auth2',
                'header3' : 'auth3'
              }
            }
          }
        });

        it('resets the expiration of the session', function() {
          // Arrange
          var now = new Date();
          var expected_expiry = new Date(now.getTime() + subject.config.siteminder.session_expiry_minutes * 60000);

          // Act
          var forward_to_proxy = subject.handleRequest(request, response);
          var session_expired_date = new Date(subject.sessions['xyz'].session_expires);

          // Assert
          expect(session_expired_date.getFullYear()).to.equal(expected_expiry.getFullYear());
          expect(session_expired_date.getMonth()).to.equal(expected_expiry.getMonth());
          expect(session_expired_date.getDay()).to.equal(expected_expiry.getDay());
          expect(session_expired_date.getHours()).to.equal(expected_expiry.getHours());
          expect(session_expired_date.getMinutes()).to.equal(expected_expiry.getMinutes());
          expect(session_expired_date.getSeconds()).to.equal(expected_expiry.getSeconds());
        });

        it('adds identity headers to the forwarded request', function() {
          // Act
          var forward_to_proxy = subject.handleRequest(request, response);

          // Assert
          expect(response.headers).to.have.keys(['header1', 'header2', 'header3']);
          expect(response.headers['header1']).to.equal('auth1');
          expect(response.headers['header2']).to.equal('auth2');
          expect(response.headers['header3']).to.equal('auth3');
        });

        it('forwards the request to the proxy', function() {
          // Act
          var forward_to_proxy = subject.handleRequest(request, response);

          // Assert
          expect(forward_to_proxy).to.be.ok();
          expect(response['statusCode']).to.be(undefined);
        });

        it('Sets the SMSESSION cookie to the session ID, domain being proxied and sets HttpOnly to true', function() {
          // Act
          subject.handleRequest(request, response);
          var cookie_jar = new Cookies(request, response);
          var session_cookie = cookie_jar.get('SMSESSION');

          // Assert
          expect(session_cookie).to.equal('xyz');
        });
      });
    });

    describe('when the logoff_url is requested', function() {
      it('adds an SMSESSION cookie with a value of LOGGEDOFF to the response', function() {
        // Arrange
        request.url = 'http://localhost:8000/system/logout';

        // Act
        subject.handleRequest(request, response);
        var cookies = response.headers['set-cookie'];

        // Assert
        expect(cookies).to.be.ok();
        expect(cookies).to.not.be.empty();
        expect(cookies[0]).to.contain('SMSESSION=LOGGEDOFF');
      });

      it('removes the existing session corresponding to the SMSESSION cookie value', function() {
        // Arrange
        request.url = 'http://localhost:8000/system/logout';
        subject.sessions = {'session1':{}, 'session2':{}, 'session3':{}};
        request.headers = {'cookie':'SMSESSION=session2'};

        // Act
        subject.handleRequest(request, response);

        // Assert
        expect(subject.sessions).to.not.have.key('session2');
      });
    });

    describe('when the request is a post to the login_url', function() {
      describe('when the credentials are valid', function() {
        it('destroys any existing session for the user');
        it('creates a new session for the user');
        it('adds an SMSESSION cookie with the session ID to the response');
        it('responds with a redirect to the TARGET URI');
      });

      describe('when the USER is not valid', function() {
        it('responds with a redirect to the bad login URI');
      });

      describe('when the PASSWORD is not valid', function() {
        it('responds with a redirect to the bad password URI');
        it('increments the number of login attempts associated with the user');
      });

      describe('when the number of login attempts has been exceeded', function() {
        it('responds with a redirect to the account locked URI');
      });
    });
  })
});<|endoftext|>