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

    var SlidingRender = Backbone.Marionette.Behavior.extend({

        collectionEvents: {
            'reset': '_reset',

            'remove': function (item, collection, options) {
                //  When a rendered view is lost - render the next one since there's a spot in the viewport
                if (this._indexWithinRenderRange(options.index)) {
                    var rendered = this._renderElementAtIndex(this.maxRenderIndex);

                    //  If failed to render next item and there are previous items waiting to be rendered, slide view back 1 item
                    if (!rendered && this.minRenderIndex > 0) {
                        this.ui.list.scrollTop(this.lastScrollTop - this.itemViewHeight);
                    }
                }

                this._setHeightPaddingTop();
            },

            'add': function (item, collection) {
                var index = collection.indexOf(item);
                var indexWithinRenderRange = this._indexWithinRenderRange(index);
                var viewportFull = collection.length >= this.maxRenderIndex;

                //  If a view has been rendered and it pushes another view outside of maxRenderIndex, remove that view.
                if (indexWithinRenderRange && viewportFull) {
                    //  Adding one because I want to grab the item which is outside maxRenderIndex. maxRenderIndex is inclusive.
                    this._removeItemsByIndex(this.maxRenderIndex + 1, 1);
                }

                this._setHeightPaddingTop();
            },

            'change:active': function (item, active) {
                if (active) {
                    this._scrollToItem(item);
                }
            }
        },

        ui: {
            list: '.list'
        },

        //  Enables progressive rendering of children by keeping track of indices which are currently rendered.
        minRenderIndex: 0,
        maxRenderIndex: 0,

        //  The height of a rendered itemView in px. Including padding/margin.
        itemViewHeight: 40,
        viewportHeight: -1,

        //  Keep track of where user is scrolling from to determine direction and amount changed.
        lastScrollTop: 0,

        onShow: function () {
            //  If the collection implements getActiveItem - scroll to the active item.
            if (this.view.collection.getActiveItem) {
                if (this.view.collection.length > 0) {
                    this._scrollToItem(this.view.collection.getActiveItem());
                }
            }
        },

        onFullyVisible: function () {
            var self = this;
            //  Throttle the scroll event because scrolls can happen a lot and don't need to re-calculate very often.
            this.ui.list.scroll(_.throttle(function () {
                self._setRenderedElements(this.scrollTop);
            }, 20));
        },

        initialize: function (options) {
            if (_.isUndefined(options) || _.isUndefined(options.viewportHeight)) throw "SlidingRender expects to be initialized with a viewportHeight";
            this.viewportHeight = options.viewportHeight;

            //  Allow N items to be rendered initially where N is how many items need to cover the viewport.
            this.minRenderIndex = this._getMinRenderIndex(0);
            this.maxRenderIndex = this._getMaxRenderIndex(0);

            //  IMPORTANT: Stub out the view's implementation of addItemView with the slidingRender version.
            this.view.addItemView = this._addItemView.bind(this);
            //  Since slidingRender appends before/after depending on scrollDirection it is important to use the indexed version of appendHtml.
            this.view.appendHtml = this._appendHtml;
        },

        onRender: function () {
            this._setHeightPaddingTop();
        },

        //  jQuery UI's sortable needs to be able to know the minimum rendered index. Whenever an external
        //  event requests the min render index -- return it!
        onGetMinRenderIndex: function() {
            this.view.triggerMethod('GetMinRenderIndexReponse', {
                minRenderIndex: this.minRenderIndex
            });
        },

        //  Whenever the viewport height is changed -- adjust the items which are currently rendered to match
        onSetViewportHeight: function (request) {
            this.viewportHeight = request.viewportHeight;

            //  Unload or load N items where N is the difference in viewport height.
            var currentMaxRenderIndex = this.maxRenderIndex;

            var newMaxRenderIndex = this._getMaxRenderIndex(this.lastScrollTop);
            var indexDifference = currentMaxRenderIndex - newMaxRenderIndex;

            //  Be sure to update before potentially adding items or else they won't render.
            this.maxRenderIndex = newMaxRenderIndex;
            if (indexDifference > 0) {
                //  Unload N Items.
                //  Only remove items if need be -- collection's length might be so small that the viewport's height isn't affecting rendered count.
                if (this.view.collection.length > currentMaxRenderIndex) {
                    this._removeItemsByIndex(currentMaxRenderIndex, indexDifference);
                }
            }
            else if (indexDifference < 0) {
                //  Load N items
                for (var count = 0; count < Math.abs(indexDifference) ; count++) {
                    this._renderElementAtIndex(currentMaxRenderIndex + 1 + count);
                }
            }

            this._setHeightPaddingTop();
        },

        //  When deleting an element from a list it's important to render the next element (if any) since
        //  usually this only happens during scroll, but positions change when removing.
        _renderElementAtIndex: function (index) {
            var rendered = false;

            if (this.view.collection.length >= index) {
                var item = this.view.collection.at(index);
                var ItemView = this.view.getItemView(item);

                //  Adjust the itemView's index to account for where it is actually being added in the list
                this._addItemView(item, ItemView, index);
                rendered = true;
            }

            return rendered;
        },

        _setRenderedElements: function (scrollTop) {
            //  Figure out the range of items currently rendered:
            var currentMinRenderIndex = this.minRenderIndex;
            var currentMaxRenderIndex = this.maxRenderIndex;

            //  Figure out the range of items which need to be rendered:
            var minRenderIndex = this._getMinRenderIndex(scrollTop);
            var maxRenderIndex = this._getMaxRenderIndex(scrollTop);

            var itemsToAdd = [];
            var itemsToRemove = [];

            //  Append items in the direction being scrolled and remove items being scrolled away from.
            var direction = scrollTop > this.lastScrollTop ? 'down' : 'up';

            if (direction === 'down') {
                //  Need to remove items which are less than the new minRenderIndex
                if (minRenderIndex > currentMinRenderIndex) {
                    itemsToRemove = this.view.collection.slice(currentMinRenderIndex, minRenderIndex);
                }

                //  Need to add items which are greater than oldMaxRenderIndex and ltoe maxRenderIndex
                if (maxRenderIndex > currentMaxRenderIndex) {
                    itemsToAdd = this.view.collection.slice(currentMaxRenderIndex + 1, maxRenderIndex + 1);
                }
            } else {
                //  Need to add items which are greater than currentMinRenderIndex and ltoe minRenderIndex
                if (minRenderIndex < currentMinRenderIndex) {
                    itemsToAdd = this.view.collection.slice(minRenderIndex, currentMinRenderIndex);
                }

                //  Need to remove items which are greater than the new maxRenderIndex
                if (maxRenderIndex < currentMaxRenderIndex) {
                    itemsToRemove = this.view.collection.slice(maxRenderIndex + 1, currentMaxRenderIndex + 1);
                }
            }

            if (itemsToAdd.length > 0 || itemsToRemove.length > 0) {
                this.minRenderIndex = minRenderIndex;
                this.maxRenderIndex = maxRenderIndex;

                if (direction === 'down') {
                    //  Items will be appended after oldMaxRenderIndex.
                    this._addItems(itemsToAdd, currentMaxRenderIndex + 1, true);
                } else {
                    this._addItems(itemsToAdd, minRenderIndex, false);
                }

                this._removeItems(itemsToRemove);
                this._setHeightPaddingTop();
            }

            this.lastScrollTop = scrollTop;
        },

        //  Reset min/max, scrollTop, paddingTop and height to their default values.
        _reset: function () {
            this.ui.list.scrollTop(0);
            this.lastScrollTop = 0;

            this.minRenderIndex = this._getMinRenderIndex(0);
            this.maxRenderIndex = this._getMaxRenderIndex(0);

            this._setHeightPaddingTop();
        },

        _setHeightPaddingTop: function() {
            this._setPaddingTop();
            this._setHeight();
        },

        //  Adjust padding-top to properly position relative items inside of list since not all items are rendered.
        _setPaddingTop: function () {
            this.view.ui.itemContainer.css('padding-top', this._getPaddingTop());
        },

        _getPaddingTop: function () {
            return this.minRenderIndex * this.itemViewHeight;
        },

        //  Set the elements height calculated from the number of potential items rendered into it.
        //  Necessary because items are lazy-appended for performance, but scrollbar size changing not desired.
        _setHeight: function () {
            //  Subtracting minRenderIndex is important because of how CSS renders the element. If you don't subtract minRenderIndex
            //  then the rendered items will push up the height of the element by minRenderIndex * itemViewHeight.
            var height = (this.view.collection.length - this.minRenderIndex) * this.itemViewHeight;

            //  Keep height set to at least the viewport height to allow for proper drag-and-drop target - can't drop if height is too small.
            if (height < this.viewportHeight) {
                height = this.viewportHeight;
            }

            this.view.ui.itemContainer.height(height);
        },

        _addItems: function (models, indexOffset, isAddingToEnd) {
            //  Leverage Marionette's style of rendering for performance.
            this.view.initRenderBuffer();
            this.view.startBuffering();

            var ItemView;
            _.each(models, function (model, index) {
                ItemView = this.view.getItemView(model);

                if (isAddingToEnd) {
                    //  Adjust the itemView's index to account for where it is actually being added in the list
                    this._addItemView(model, ItemView, index + indexOffset);
                } else {
                    //  Adjust the itemView's index to account for where it is actually being added in the list, but
                    //  also provide the unmodified index because this is the location in the rendered itemViewList in which it will be added.
                    this._addItemView(model, ItemView, index, index + indexOffset);
                }
            }, this);

            this.view.endBuffering();
        },

        //  Remove N items from the end of the render item list.
        _removeItemsByIndex: function (startIndex, countToRemove) {
            for (var index = 0; index < countToRemove; index++) {
                var item = this.view.collection.at(startIndex - index);
                var childView = this.view.children.findByModel(item);
                this.view.removeChildView(childView);
            }
        },

        _removeItems: function (models) {
            _.each(models, function (model) {
                var childView = this.view.children.findByModel(model);

                this.view.removeChildView(childView);
            }, this);
        },

        _addItemView: function (item, ItemView, index, indexOverride) {
            //  indexOverride is necessary because the 'actual' index of an item is different from its rendered position's index.
            var shouldAdd;
            if (_.isUndefined(indexOverride)) {
                shouldAdd = this._indexWithinRenderRange(index);
            } else {
                shouldAdd = this._indexWithinRenderRange(indexOverride);
            }

            if (shouldAdd) {
                Backbone.Marionette.CompositeView.prototype.addItemView.apply(this.view, arguments);
            }
        },

        _appendHtml: function (collectionView, itemView, index) {
            var childrenContainer = collectionView.itemViewContainer ? collectionView.$(collectionView.itemViewContainer) : collectionView.$el;
            var children = childrenContainer.children();
            if (children.size() <= index) {
                childrenContainer.append(itemView.el);
            } else {
                children.eq(index).before(itemView.el);
            }
        },

        _getMinRenderIndex: function (scrollTop) {
            var minRenderIndex = Math.floor(scrollTop / this.itemViewHeight);
            return minRenderIndex;
        },

        _getMaxRenderIndex: function (scrollTop) {
            var maxRenderIndex = Math.ceil((scrollTop / this.itemViewHeight) + (this.viewportHeight / this.itemViewHeight)) - 1;
            return maxRenderIndex;
        },

        //  Returns true if an itemView at the given index would not be fully visible -- part of it rendering out of the top of the viewport.
        _indexOverflowsTop: function (index) {
            var position = index * this.itemViewHeight;
            var scrollPosition = this.ui.list.scrollTop();

            var overflowsTop = position < scrollPosition;

            return overflowsTop;
        },

        _indexOverflowsBottom: function (index) {
            //  Add one to index because want to get the bottom of the element and not the top.
            var position = (index + 1) * this.itemViewHeight;
            var scrollPosition = this.ui.list.scrollTop() + this.viewportHeight;

            var overflowsBottom = position > scrollPosition;

            return overflowsBottom;
        },

        _indexWithinRenderRange: function (index) {
            return index >= this.minRenderIndex && index <= this.maxRenderIndex;
        },

        //  Ensure that the active item is visible by setting the container's scrollTop to a position which allows it to be seen.
        _scrollToItem: function (item) {
            var itemIndex = this.view.collection.indexOf(item);

            var overflowsTop = this._indexOverflowsTop(itemIndex);
            var overflowsBottom = this._indexOverflowsBottom(itemIndex);

            //  Only scroll to the item if it isn't in the viewport.
            if (overflowsTop || overflowsBottom) {
                var scrollTop = 0;

                //  If the item needs to be made visible from the bottom, offset the viewport's height:
                if (overflowsBottom) {
                    //  Add 1 to index because want the bottom of the element and not the top.
                    scrollTop = (itemIndex + 1) * this.itemViewHeight - this.viewportHeight;
                }
                else if (overflowsTop) {
                    scrollTop = itemIndex * this.itemViewHeight;
                }

                this.ui.list.scrollTop(scrollTop);
            }
        }
    });

    return SlidingRender;
});<commit_msg>Finished refactoring sliding render.<commit_after>define(function () {
    'use strict';

    var SlidingRender = Backbone.Marionette.Behavior.extend({

        collectionEvents: {
            'reset': '_reset',

            'remove': function (item, collection, options) {
                //  When a rendered view is lost - render the next one since there's a spot in the viewport
                if (this._indexWithinRenderRange(options.index)) {
                    var rendered = this._renderElementAtIndex(this.maxRenderIndex);

                    //  If failed to render next item and there are previous items waiting to be rendered, slide view back 1 item
                    if (!rendered && this.minRenderIndex > 0) {
                        this.ui.list.scrollTop(this.lastScrollTop - this.itemViewHeight);
                    }
                }

                this._setHeightPaddingTop();
            },

            'add': function (item, collection) {
                var index = collection.indexOf(item);
                var indexWithinRenderRange = this._indexWithinRenderRange(index);
                var viewportFull = collection.length >= this.maxRenderIndex;

                //  If a view has been rendered and it pushes another view outside of maxRenderIndex, remove that view.
                if (indexWithinRenderRange && viewportFull) {
                    //  Adding one because I want to grab the item which is outside maxRenderIndex. maxRenderIndex is inclusive.
                    this._removeItemsByIndex(this.maxRenderIndex + 1, 1);
                }

                this._setHeightPaddingTop();
            },

            'change:active': function (item, active) {
                if (active) {
                    this._scrollToItem(item);
                }
            }
        },

        ui: {
            list: '.list'
        },

        //  Enables progressive rendering of children by keeping track of indices which are currently rendered.
        minRenderIndex: 0,
        maxRenderIndex: 0,

        //  The height of a rendered itemView in px. Including padding/margin.
        itemViewHeight: 40,
        viewportHeight: -1,

        //  The number of items to render outside of the viewport. Helps with flickering because if
        //  only views which would be visible are rendered then they'd be visible while loading.
        threshold: 10,

        //  Keep track of where user is scrolling from to determine direction and amount changed.
        lastScrollTop: 0,

        onShow: function () {
            //  If the collection implements getActiveItem - scroll to the active item.
            if (this.view.collection.getActiveItem) {
                if (this.view.collection.length > 0) {
                    this._scrollToItem(this.view.collection.getActiveItem());
                }
            }
        },

        onFullyVisible: function () {
            var self = this;
            //  Throttle the scroll event because scrolls can happen a lot and don't need to re-calculate very often.
            this.ui.list.scroll(_.throttle(function () {
                self._setRenderedElements(this.scrollTop);
            }, 20));
        },

        initialize: function (options) {
            if (_.isUndefined(options) || _.isUndefined(options.viewportHeight)) throw "SlidingRender expects to be initialized with a viewportHeight";
            this.viewportHeight = options.viewportHeight;

            //  Allow N items to be rendered initially where N is how many items need to cover the viewport.
            this.minRenderIndex = this._getMinRenderIndex(0);
            this.maxRenderIndex = this._getMaxRenderIndex(0);

            //  IMPORTANT: Stub out the view's implementation of addItemView with the slidingRender version.
            this.view.addItemView = this._addItemView.bind(this);
            //  Since slidingRender appends before/after depending on scrollDirection it is important to use the indexed version of appendHtml.
            this.view.appendHtml = this._appendHtml;
        },

        onRender: function () {
            this._setHeightPaddingTop();
        },

        //  jQuery UI's sortable needs to be able to know the minimum rendered index. Whenever an external
        //  event requests the min render index -- return it!
        onGetMinRenderIndex: function() {
            this.view.triggerMethod('GetMinRenderIndexReponse', {
                minRenderIndex: this.minRenderIndex
            });
        },

        //  Whenever the viewport height is changed -- adjust the items which are currently rendered to match
        onSetViewportHeight: function (request) {
            this.viewportHeight = request.viewportHeight;

            //  Unload or load N items where N is the difference in viewport height.
            var currentMaxRenderIndex = this.maxRenderIndex;

            var newMaxRenderIndex = this._getMaxRenderIndex(this.lastScrollTop);
            var indexDifference = currentMaxRenderIndex - newMaxRenderIndex;

            //  Be sure to update before potentially adding items or else they won't render.
            this.maxRenderIndex = newMaxRenderIndex;
            if (indexDifference > 0) {
                //  Unload N Items.
                //  Only remove items if need be -- collection's length might be so small that the viewport's height isn't affecting rendered count.
                if (this.view.collection.length > currentMaxRenderIndex) {
                    this._removeItemsByIndex(currentMaxRenderIndex, indexDifference);
                }
            }
            else if (indexDifference < 0) {
                //  Load N items
                for (var count = 0; count < Math.abs(indexDifference) ; count++) {
                    this._renderElementAtIndex(currentMaxRenderIndex + 1 + count);
                }
            }

            this._setHeightPaddingTop();
        },

        //  When deleting an element from a list it's important to render the next element (if any) since
        //  usually this only happens during scroll, but positions change when removing.
        _renderElementAtIndex: function (index) {
            var rendered = false;

            if (this.view.collection.length >= index) {
                var item = this.view.collection.at(index);
                var ItemView = this.view.getItemView(item);

                //  Adjust the itemView's index to account for where it is actually being added in the list
                this._addItemView(item, ItemView, index);
                rendered = true;
            }

            return rendered;
        },

        _setRenderedElements: function (scrollTop) {
            //  Figure out the range of items currently rendered:
            var currentMinRenderIndex = this.minRenderIndex;
            var currentMaxRenderIndex = this.maxRenderIndex;

            //  Figure out the range of items which need to be rendered:
            var minRenderIndex = this._getMinRenderIndex(scrollTop);
            var maxRenderIndex = this._getMaxRenderIndex(scrollTop);

            var itemsToAdd = [];
            var itemsToRemove = [];

            //  Append items in the direction being scrolled and remove items being scrolled away from.
            var direction = scrollTop > this.lastScrollTop ? 'down' : 'up';

            if (direction === 'down') {
                //  Need to remove items which are less than the new minRenderIndex
                if (minRenderIndex > currentMinRenderIndex) {
                    itemsToRemove = this.view.collection.slice(currentMinRenderIndex, minRenderIndex);
                }

                //  Need to add items which are greater than oldMaxRenderIndex and ltoe maxRenderIndex
                if (maxRenderIndex > currentMaxRenderIndex) {
                    itemsToAdd = this.view.collection.slice(currentMaxRenderIndex + 1, maxRenderIndex + 1);
                }
            } else {
                //  Need to add items which are greater than currentMinRenderIndex and ltoe minRenderIndex
                if (minRenderIndex < currentMinRenderIndex) {
                    itemsToAdd = this.view.collection.slice(minRenderIndex, currentMinRenderIndex);
                }

                //  Need to remove items which are greater than the new maxRenderIndex
                if (maxRenderIndex < currentMaxRenderIndex) {
                    itemsToRemove = this.view.collection.slice(maxRenderIndex + 1, currentMaxRenderIndex + 1);
                }
            }

            if (itemsToAdd.length > 0 || itemsToRemove.length > 0) {
                this.minRenderIndex = minRenderIndex;
                this.maxRenderIndex = maxRenderIndex;

                if (direction === 'down') {
                    //  Items will be appended after oldMaxRenderIndex.
                    this._addItems(itemsToAdd, currentMaxRenderIndex + 1, true);
                } else {
                    this._addItems(itemsToAdd, minRenderIndex, false);
                }

                this._removeItems(itemsToRemove);
                this._setHeightPaddingTop();
            }

            this.lastScrollTop = scrollTop;
        },

        //  Reset min/max, scrollTop, paddingTop and height to their default values.
        _reset: function () {
            this.ui.list.scrollTop(0);
            this.lastScrollTop = 0;

            this.minRenderIndex = this._getMinRenderIndex(0);
            this.maxRenderIndex = this._getMaxRenderIndex(0);

            this._setHeightPaddingTop();
        },

        _setHeightPaddingTop: function() {
            this._setPaddingTop();
            this._setHeight();
        },

        //  Adjust padding-top to properly position relative items inside of list since not all items are rendered.
        _setPaddingTop: function () {
            this.view.ui.itemContainer.css('padding-top', this._getPaddingTop());
        },

        _getPaddingTop: function () {
            return this.minRenderIndex * this.itemViewHeight;
        },

        //  Set the elements height calculated from the number of potential items rendered into it.
        //  Necessary because items are lazy-appended for performance, but scrollbar size changing not desired.
        _setHeight: function () {
            //  Subtracting minRenderIndex is important because of how CSS renders the element. If you don't subtract minRenderIndex
            //  then the rendered items will push up the height of the element by minRenderIndex * itemViewHeight.
            var height = (this.view.collection.length - this.minRenderIndex) * this.itemViewHeight;

            //  Keep height set to at least the viewport height to allow for proper drag-and-drop target - can't drop if height is too small.
            if (height < this.viewportHeight) {
                height = this.viewportHeight;
            }

            this.view.ui.itemContainer.height(height);
        },

        _addItems: function (models, indexOffset, isAddingToEnd) {
            //  Leverage Marionette's style of rendering for performance.
            this.view.initRenderBuffer();
            this.view.startBuffering();

            var ItemView;
            _.each(models, function (model, index) {
                ItemView = this.view.getItemView(model);

                if (isAddingToEnd) {
                    //  Adjust the itemView's index to account for where it is actually being added in the list
                    this._addItemView(model, ItemView, index + indexOffset);
                } else {
                    //  Adjust the itemView's index to account for where it is actually being added in the list, but
                    //  also provide the unmodified index because this is the location in the rendered itemViewList in which it will be added.
                    this._addItemView(model, ItemView, index, index + indexOffset);
                }
            }, this);

            this.view.endBuffering();
        },

        //  Remove N items from the end of the render item list.
        _removeItemsByIndex: function (startIndex, countToRemove) {
            for (var index = 0; index < countToRemove; index++) {
                var item = this.view.collection.at(startIndex - index);
                var childView = this.view.children.findByModel(item);
                this.view.removeChildView(childView);
            }
        },

        _removeItems: function (models) {
            _.each(models, function (model) {
                var childView = this.view.children.findByModel(model);

                this.view.removeChildView(childView);
            }, this);
        },

        _addItemView: function (item, ItemView, index, indexOverride) {
            //  indexOverride is necessary because the 'actual' index of an item is different from its rendered position's index.
            var shouldAdd;
            if (_.isUndefined(indexOverride)) {
                shouldAdd = this._indexWithinRenderRange(index);
            } else {
                shouldAdd = this._indexWithinRenderRange(indexOverride);
            }

            if (shouldAdd) {
                Backbone.Marionette.CompositeView.prototype.addItemView.apply(this.view, arguments);
            }
        },

        _appendHtml: function (collectionView, itemView, index) {
            var childrenContainer = collectionView.itemViewContainer ? collectionView.$(collectionView.itemViewContainer) : collectionView.$el;
            var children = childrenContainer.children();
            if (children.size() <= index) {
                childrenContainer.append(itemView.el);
            } else {
                children.eq(index).before(itemView.el);
            }
        },

        _getMinRenderIndex: function (scrollTop) {
            var minRenderIndex = Math.floor(scrollTop / this.itemViewHeight) - this.threshold;

            if (minRenderIndex < 0) {
                minRenderIndex = 0;
            }

            return minRenderIndex;
        },

        _getMaxRenderIndex: function (scrollTop) {
            //  Subtract 1 to make math 'inclusive' instead of 'exclusive'
            var maxRenderIndex = Math.ceil((scrollTop / this.itemViewHeight) + (this.viewportHeight / this.itemViewHeight)) - 1 + this.threshold;

            return maxRenderIndex;
        },

        //  Returns true if an itemView at the given index would not be fully visible -- part of it rendering out of the top of the viewport.
        _indexOverflowsTop: function (index) {
            var position = index * this.itemViewHeight;
            var scrollPosition = this.ui.list.scrollTop();

            var overflowsTop = position < scrollPosition;

            return overflowsTop;
        },

        _indexOverflowsBottom: function (index) {
            //  Add one to index because want to get the bottom of the element and not the top.
            var position = (index + 1) * this.itemViewHeight;
            var scrollPosition = this.ui.list.scrollTop() + this.viewportHeight;

            var overflowsBottom = position > scrollPosition;

            return overflowsBottom;
        },

        _indexWithinRenderRange: function (index) {
            return index >= this.minRenderIndex && index <= this.maxRenderIndex;
        },

        //  Ensure that the active item is visible by setting the container's scrollTop to a position which allows it to be seen.
        _scrollToItem: function (item) {
            var itemIndex = this.view.collection.indexOf(item);

            var overflowsTop = this._indexOverflowsTop(itemIndex);
            var overflowsBottom = this._indexOverflowsBottom(itemIndex);

            //  Only scroll to the item if it isn't in the viewport.
            if (overflowsTop || overflowsBottom) {
                var scrollTop = 0;

                //  If the item needs to be made visible from the bottom, offset the viewport's height:
                if (overflowsBottom) {
                    //  Add 1 to index because want the bottom of the element and not the top.
                    scrollTop = (itemIndex + 1) * this.itemViewHeight - this.viewportHeight;
                }
                else if (overflowsTop) {
                    scrollTop = itemIndex * this.itemViewHeight;
                }

                this.ui.list.scrollTop(scrollTop);
            }
        }
    });

    return SlidingRender;
});<|endoftext|>


<commit_before>'use strict';

/**
  * Defines to methods to access and manipulate profiles in
  * a mongodb instance.  The profile structure is a collection of the
  * following structure:
  *
    {
      "accountId":"idValue",
      "primary":"y",
      "profileName":"nameValue",
      "ageRange":"rangeValue",
      "gender":"Male",
      "medications":[
          "med1",
          "med2",
          "med3"
      ],
      "conditions":[
          "cond1"
      ],
      "allergies":[
          "aller1",
          "aller2"
      ]
    }

  code based on http://www.qat.com/rest-service-node-js-mongodb-express/
  */

var profilesTable = 'profiles';
var MongoClient = require('mongodb').MongoClient;
var BSON = require('bson').BSONPure;

//setup connection URL
var dbName = 'fda-anywhere';
var dbConnectUrl = 'mongodb://localhost:27017/';
if(process.env.OPENSHIFT_MONGODB_DB_URL){
  dbConnectUrl = process.env.OPENSHIFT_MONGODB_DB_URL;
}
dbConnectUrl += dbName;
console.log('mongo URL: ' + dbConnectUrl);

var _db;

var ProfileProvider = function(host, port) {
  var self = this;

  //create DB connection and store it
  MongoClient.connect(dbConnectUrl, function(err, db) {
      if(err) {
          console.error('Error connecting to Mongo!', err);
      } else {
          console.log("Connected correctly to mongo server");
          _db = db;
      }
  });

  /**
    * Retrieves all the profiles in the database with the input accountId
    * @param   {String}  myAccountId the accountId of the the profiles
    * @return  {Array}   an array of profile documents
    */
  this.fetchAllProfilesByAccountId = function(myAccountId, cb) {
    _db.collection(profilesTable, function(error, profiles) {
      if (error) {
        cb(error, null);
      } else {
        //finds all profile documents with the accountId and returns in an array
        profiles.find({accountId:myAccountId}).toArray(function(error, results) {
          cb(error, results);
        });
      }
    });
  };

  /**
    * Retrieves a profile by its DB generated _id field
    * @param   {String}  myAccountId the accountId of the the profiles
    * @return  {String}   the profile document
    */
  this.fetchProfileById = function(id, cb) {
    _db.collection(profilesTable, function(error, profiles) {
      if (error) {
        cb(error, null);
      } else {
        //finds a single document with the input id.  The input id must be
        //converted to a ObjectId when dealing with the db generated id
        //when searching
        profiles.findOne({_id:new BSON.ObjectID(id)
        }, function(error, result) {
          cb(error, result);
        });
      }
    });
  };

  /**
    * Adds a profile document to the profiles collection
    * @param   {String}  profile the profile document to add
    */
  this.insertProfile = function(profile, cb) {

    _db.collection(profilesTable, function(error, profiles) {
      if (error) {
        cb(error, null);
        console.error(error);
      } else {

        //verify the accountId field is present in profile
        if (profile.accountId === null) {
          throw "illegal profile document. It does not contain the required" +
          " 'accountId' field at the root";
        }

        profiles.insert([profile], function() {
          cb(null, profile);
        });
      }
    });
  };

  /**
    * Updates a profile document to the profiles collection only if
    * the profile exists based on DB generated _id field.  Does nothing
    * (no error nor does it add) if the profile does not exist.
    * @param   {String}  profile the profile document to update
    */
  this.updateProfile = function(id, profile, cb) {
    _db.collection(profilesTable, function(error, profiles) {
      if (error) {
        cb(error, null);
      } else {

        //verify the accountId field is present in profile
        if (profile.accountId === null) {
          throw "illegal profile document. It does not contain the required" +
          " 'accountId' field at the root";
        }

        //setup the BSON id
        var bsonId = new BSON.ObjectID(id);
        profile._id = bsonId;

        //finds a single document with the DB generated _id in the input
        //profile.  The db generated _id must be converted to a ObjectId when
        //searching
        profiles.update({_id: bsonId}, profile,
          function(error, result) {
            cb(error, result);
        });

      }
    });
  };

  /**
    * Deletes the profile whose DB generated _id field matches the input id
    * @param   {String}  the db generated _id of the profile to be deleted
    */
  this.deleteProfile = function(id, cb) {
    _db.collection(profilesTable, function(error, profiles) {
      if (error) {
        cb(error, null);
      } else {

        //removes the document with the input id.  The input id must be
        //converted to a ObjectId when dealing with the db generated id
        //when searching
       profiles.remove({_id:new BSON.ObjectID(id)},
          function(error, result) {
            cb(error, result);
        });

      }
    });
  };

};

exports.ProfileProvider = ProfileProvider;<commit_msg>removing db name to avoid auth issues with openshify<commit_after>'use strict';

/**
  * Defines to methods to access and manipulate profiles in
  * a mongodb instance.  The profile structure is a collection of the
  * following structure:
  *
    {
      "accountId":"idValue",
      "primary":"y",
      "profileName":"nameValue",
      "ageRange":"rangeValue",
      "gender":"Male",
      "medications":[
          "med1",
          "med2",
          "med3"
      ],
      "conditions":[
          "cond1"
      ],
      "allergies":[
          "aller1",
          "aller2"
      ]
    }

  code based on http://www.qat.com/rest-service-node-js-mongodb-express/
  */

var profilesTable = 'profiles';
var MongoClient = require('mongodb').MongoClient;
var BSON = require('bson').BSONPure;

//setup connection URL
var dbConnectUrl = 'mongodb://localhost:27017/';
if(process.env.OPENSHIFT_MONGODB_DB_URL){
  dbConnectUrl = process.env.OPENSHIFT_MONGODB_DB_URL;
}
console.log('mongo URL: ' + dbConnectUrl);

var _db;

var ProfileProvider = function(host, port) {
  var self = this;

  //create DB connection and store it
  MongoClient.connect(dbConnectUrl, function(err, db) {
      if(err) {
          console.error('Error connecting to Mongo!', err);
      } else {
          console.log("Connected correctly to mongo server");
          _db = db;
      }
  });

  /**
    * Retrieves all the profiles in the database with the input accountId
    * @param   {String}  myAccountId the accountId of the the profiles
    * @return  {Array}   an array of profile documents
    */
  this.fetchAllProfilesByAccountId = function(myAccountId, cb) {
    _db.collection(profilesTable, function(error, profiles) {
      if (error) {
        cb(error, null);
      } else {
        //finds all profile documents with the accountId and returns in an array
        profiles.find({accountId:myAccountId}).toArray(function(error, results) {
          cb(error, results);
        });
      }
    });
  };

  /**
    * Retrieves a profile by its DB generated _id field
    * @param   {String}  myAccountId the accountId of the the profiles
    * @return  {String}   the profile document
    */
  this.fetchProfileById = function(id, cb) {
    _db.collection(profilesTable, function(error, profiles) {
      if (error) {
        cb(error, null);
      } else {
        //finds a single document with the input id.  The input id must be
        //converted to a ObjectId when dealing with the db generated id
        //when searching
        profiles.findOne({_id:new BSON.ObjectID(id)
        }, function(error, result) {
          cb(error, result);
        });
      }
    });
  };

  /**
    * Adds a profile document to the profiles collection
    * @param   {String}  profile the profile document to add
    */
  this.insertProfile = function(profile, cb) {

    _db.collection(profilesTable, function(error, profiles) {
      if (error) {
        cb(error, null);
        console.error(error);
      } else {

        //verify the accountId field is present in profile
        if (profile.accountId === null) {
          throw "illegal profile document. It does not contain the required" +
          " 'accountId' field at the root";
        }

        profiles.insert([profile], function() {
          cb(null, profile);
        });
      }
    });
  };

  /**
    * Updates a profile document to the profiles collection only if
    * the profile exists based on DB generated _id field.  Does nothing
    * (no error nor does it add) if the profile does not exist.
    * @param   {String}  profile the profile document to update
    */
  this.updateProfile = function(id, profile, cb) {
    _db.collection(profilesTable, function(error, profiles) {
      if (error) {
        cb(error, null);
      } else {

        //verify the accountId field is present in profile
        if (profile.accountId === null) {
          throw "illegal profile document. It does not contain the required" +
          " 'accountId' field at the root";
        }

        //setup the BSON id
        var bsonId = new BSON.ObjectID(id);
        profile._id = bsonId;

        //finds a single document with the DB generated _id in the input
        //profile.  The db generated _id must be converted to a ObjectId when
        //searching
        profiles.update({_id: bsonId}, profile,
          function(error, result) {
            cb(error, result);
        });

      }
    });
  };

  /**
    * Deletes the profile whose DB generated _id field matches the input id
    * @param   {String}  the db generated _id of the profile to be deleted
    */
  this.deleteProfile = function(id, cb) {
    _db.collection(profilesTable, function(error, profiles) {
      if (error) {
        cb(error, null);
      } else {

        //removes the document with the input id.  The input id must be
        //converted to a ObjectId when dealing with the db generated id
        //when searching
       profiles.remove({_id:new BSON.ObjectID(id)},
          function(error, result) {
            cb(error, result);
        });

      }
    });
  };

};

exports.ProfileProvider = ProfileProvider;<|endoftext|>


<commit_before>define(
  'ephox.robin.api.general.LeftBlock',

  [
    'ephox.compass.Arr',
    'ephox.phoenix.api.general.Gather',
    'ephox.robin.leftblock.Walks'
  ],

  function (Arr, Gather, Walks) {
    var walkers = Gather.walkers();

    var goLeft = function (universe, item, mode, strategy) {
      var next = Gather.walk(universe, item, mode, walkers.left(), strategy.rules);
      return next.map(function (n) {
        var isBlock = universe.property().isEmptyTag(n.item()) || universe.property().isBoundary(n.item());
        if (isBlock) return [];
        var inclusions = strategy.inclusion(universe, n, item) ? [ n.item() ] : [];
        return inclusions.concat(goLeft(universe, n.item(), n.mode(), strategy));
      }).getOr([]);
    };

    var top = function (universe, item) {
      var lefts = goLeft(universe, item, Gather.sidestep, Walks.top);
      return Arr.reverse(lefts).concat([ item ]);
    };

    /**
     * Gather leaves left to the edge of the block item is in
     */
    var all = function (universe, item) {
      var lefts = goLeft(universe, item, Gather.sidestep, Walks.all);
      return Arr.reverse(lefts).concat([ item ]);
    };

    return {
      top: top,
      all: all
    };
  }
);
<commit_msg>TBIO-179-b: removing dupe<commit_after>define(
  'ephox.robin.api.general.LeftBlock',

  [
    'ephox.compass.Arr',
    'ephox.peanut.Fun',
    'ephox.phoenix.api.general.Gather',
    'ephox.robin.leftblock.Walks'
  ],

  function (Arr, Fun, Gather, Walks) {
    var walkers = Gather.walkers();

    var goLeft = function (universe, item, mode, strategy) {
      var next = Gather.walk(universe, item, mode, walkers.left(), strategy.rules);
      return next.map(function (n) {
        var isBlock = universe.property().isEmptyTag(n.item()) || universe.property().isBoundary(n.item());
        if (isBlock) return [];
        var inclusions = strategy.inclusion(universe, n, item) ? [ n.item() ] : [];
        return inclusions.concat(goLeft(universe, n.item(), n.mode(), strategy));
      }).getOr([]);
    };

    var run = function (strategy, universe, item) {
      var lefts = goLeft(universe, item, Gather.sidestep, strategy);
      return Arr.reverse(lefts).concat([ item ]);
    };

    return {
      top: Fun.curry(run, Walks.top),
      all: Fun.curry(run, Walks.all)
    };
  }
);
<|endoftext|>


<commit_before>define(
  'ephox.snooker.api.TableOperations',

  [
    'ephox.compass.Arr',
    'ephox.lid.Jam',
    'ephox.peanut.Fun',
    'ephox.perhaps.Option',
    'ephox.snooker.api.Generators',
    'ephox.snooker.api.TableLookup',
    'ephox.snooker.model.RunOperation',
    'ephox.snooker.operate.MergingOperations',
    'ephox.snooker.operate.ModificationOperations',
    'ephox.snooker.operate.TransformOperations',
    'ephox.snooker.resize.Adjustments',
    'ephox.sugar.api.Attr',
    'ephox.sugar.api.Compare',
    'ephox.sugar.api.Element',
    'ephox.sugar.api.InsertAll',
    'ephox.sugar.api.Node',
    'ephox.sugar.api.Remove',
    'ephox.sugar.api.Traverse',
    'global!parseInt'
  ],

  function (Arr, Jam, Fun, Option, Generators, TableLookup, RunOperation, MergingOperations, ModificationOperations, TransformOperations, Adjustments, Attr, Compare, Element, InsertAll, Node, Remove, Traverse, parseInt) {
    var prune = function (table) {
      var cells = TableLookup.cells(table);
      if (cells.length === 0) Remove.remove(table);
    };

    /* HACKING */
    var insertRowBefore = function (grid, detail, comparator, genWrappers) {
      var example = detail.row();
      var targetIndex = detail.row();
      return ModificationOperations.insertRowAt(grid, targetIndex, example, comparator, genWrappers);
    };

    var insertRowAfter = function (grid, detail, comparator, genWrappers) {
      var example = detail.row();
      var targetIndex = detail.row() + detail.rowspan();
      return ModificationOperations.insertRowAt(grid, targetIndex, example, comparator, genWrappers);
    };

    var insertColumnAfter = function (grid, detail, comparator, genWrappers) {
      var example = detail.column();
      var targetIndex = detail.column() + detail.colspan();
      return ModificationOperations.insertColumnAt(grid, targetIndex, example, comparator, genWrappers);
    };

    var insertColumnBefore = function (grid, detail, comparator, genWrappers) {
      var example = detail.column();
      var targetIndex = detail.column();
      return ModificationOperations.insertColumnAt(grid, targetIndex, example, comparator, genWrappers);
    };

    var headerGenerators = function (comparator, scope, tag, generators) {

      var list = [];

      var find = function (element) {
        var raw = Arr.find(list, function (x) { return comparator(x.item, element); });
        return Option.from(raw);
      };

      var makeNew = function (element) {
        var cell = generators.replace(element, tag, {
          scope: scope
        });
        list.push({ item: element, sub: cell });
        return cell;
      };

      var replaceOrInit = function (element, comparator) {
        return find(element).fold(function () {
          return makeNew(element);
        }, function (p) {
          return comparator(element, p.item) ? p.sub : makeNew(element);
        });
      };

      return {
        replaceOrInit: replaceOrInit
      };
    };

    var makeRowHeader = function (grid, detail, comparator, genWrappers) {
      return TransformOperations.replaceRow(grid, detail.row(), comparator, genWrappers);
    };

    var makeColumnHeader = function (grid, detail, comparator, genWrappers) {
      return TransformOperations.replaceColumn(grid, detail.column(), comparator, genWrappers);
    };

    var unmakeRowHeader = function (grid, detail, comparator, genWrappers) {
      return TransformOperations.replaceRow(grid, detail.row(), comparator, genWrappers);
    };

    var unmakeColumnHeader = function (grid, detail, comparator, genWrappers) {
      return TransformOperations.replaceColumn(grid, detail.column(), comparator, genWrappers);
    };

    var eraseColumn = function (grid, detail, comparator, generators) {
      return ModificationOperations.deleteColumnAt(grid, detail.column());
    };

    var eraseRow = function (grid, detail, comparator, generators) {
      return ModificationOperations.deleteRowAt(grid, detail.row());
    };

    var mergeContent = function (cells) {
      var readContent = function () {
        var kin = Arr.bind(cells, function (cell) {
          var children = Traverse.children(cell);
          // Exclude empty cells.
          return children.length > 1 || (children.length == 1 && Node.name(children[0]) !== 'br') ? [ children ] : [];
        });
        return Jam.intersperseThunk(kin, function () {
          return [ Element.fromTag('br') ];
        });
      };

      var contents = Arr.flatten(readContent());
      Remove.empty(cells[0]);
      InsertAll.append(cells[0], contents);
    };

    var mergeCells = function (grid, mergable, comparator, _generators) {
      var cells = mergable.cells();
      mergeContent(cells);
      return MergingOperations.merge(grid, mergable, comparator, Fun.constant(cells[0]));
    };

    var unmergeCells = function (grid, unmergable, comparator, generators) {
      console.log('unmergable', unmergable[0]);

      return Arr.foldr(unmergable, function (b, cell) {
        return MergingOperations.unmerge(b, cell, comparator, generators(cell));
      }, grid);
    };

    /* END HACKING */

    // Only column modifications force a resizing. Everything else just tries to preserve the table as is.
    var resize = Adjustments.adjustTo;

    return {
      //operation, adjustment, postAction, genWrappers
      insertRowBefore: RunOperation.run(insertRowBefore, RunOperation.onCell, Fun.noop, Fun.noop, Generators.modification),
      insertRowAfter:  RunOperation.run(insertRowAfter, RunOperation.onCell, Fun.noop, Fun.noop, Generators.modification),
      insertColumnBefore:  RunOperation.run(insertColumnBefore, RunOperation.onCell, resize, Fun.noop, Generators.modification),
      insertColumnAfter:  RunOperation.run(insertColumnAfter, RunOperation.onCell, resize, Fun.noop, Generators.modification),
      eraseColumn:  RunOperation.run(eraseColumn, RunOperation.onCell, resize, prune, Generators.modification),
      eraseRow:  RunOperation.run(eraseRow, RunOperation.onCell, Fun.noop, prune, Generators.modification),
      makeColumnHeader:  RunOperation.run(makeColumnHeader, RunOperation.onCell, Fun.noop, Fun.noop, Generators.transform('row', 'th')),
      unmakeColumnHeader:  RunOperation.run(unmakeColumnHeader, RunOperation.onCell, Fun.noop, Fun.noop, Generators.transform(null, 'td')),
      makeRowHeader:  RunOperation.run(makeRowHeader, RunOperation.onCell, Fun.noop, Fun.noop, Generators.transform('col', 'th')),
      unmakeRowHeader:  RunOperation.run(unmakeRowHeader, RunOperation.onCell, Fun.noop, Fun.noop, Generators.transform(null, 'td')),
      mergeCells: RunOperation.run(mergeCells, RunOperation.onMergable, Fun.noop, Fun.noop, Generators.merging),
      unmergeCells: RunOperation.run(unmergeCells, RunOperation.onUnmergable, Fun.noop, Fun.noop, Generators.merging)
    };
  }
);
<commit_msg>TBIO-405: TBIO-405: table operations<commit_after>define(
  'ephox.snooker.api.TableOperations',

  [
    'ephox.compass.Arr',
    'ephox.lid.Jam',
    'ephox.peanut.Fun',
    'ephox.snooker.api.Generators',
    'ephox.snooker.api.TableLookup',
    'ephox.snooker.model.RunOperation',
    'ephox.snooker.operate.MergingOperations',
    'ephox.snooker.operate.ModificationOperations',
    'ephox.snooker.operate.TransformOperations',
    'ephox.snooker.resize.Adjustments',
    'ephox.sugar.api.Element',
    'ephox.sugar.api.InsertAll',
    'ephox.sugar.api.Node',
    'ephox.sugar.api.Remove',
    'ephox.sugar.api.Traverse',
    'global!parseInt'
  ],

  function (Arr, Jam, Fun, Generators, TableLookup, RunOperation, MergingOperations, ModificationOperations, TransformOperations, Adjustments, Element, InsertAll, Node, Remove, Traverse, parseInt) {
    var prune = function (table) {
      var cells = TableLookup.cells(table);
      if (cells.length === 0) Remove.remove(table);
    };

    var insertRowBefore = function (grid, detail, comparator, genWrappers) {
      var example = detail.row();
      var targetIndex = detail.row();
      return ModificationOperations.insertRowAt(grid, targetIndex, example, comparator, genWrappers);
    };

    var insertRowAfter = function (grid, detail, comparator, genWrappers) {
      var example = detail.row();
      var targetIndex = detail.row() + detail.rowspan();
      return ModificationOperations.insertRowAt(grid, targetIndex, example, comparator, genWrappers);
    };

    var insertColumnAfter = function (grid, detail, comparator, genWrappers) {
      var example = detail.column();
      var targetIndex = detail.column() + detail.colspan();
      return ModificationOperations.insertColumnAt(grid, targetIndex, example, comparator, genWrappers);
    };

    var insertColumnBefore = function (grid, detail, comparator, genWrappers) {
      var example = detail.column();
      var targetIndex = detail.column();
      return ModificationOperations.insertColumnAt(grid, targetIndex, example, comparator, genWrappers);
    };

    var makeRowHeader = function (grid, detail, comparator, genWrappers) {
      return TransformOperations.replaceRow(grid, detail.row(), comparator, genWrappers);
    };

    var makeColumnHeader = function (grid, detail, comparator, genWrappers) {
      return TransformOperations.replaceColumn(grid, detail.column(), comparator, genWrappers);
    };

    var unmakeRowHeader = function (grid, detail, comparator, genWrappers) {
      return TransformOperations.replaceRow(grid, detail.row(), comparator, genWrappers);
    };

    var unmakeColumnHeader = function (grid, detail, comparator, genWrappers) {
      return TransformOperations.replaceColumn(grid, detail.column(), comparator, genWrappers);
    };

    var eraseColumn = function (grid, detail, comparator, generators) {
      return ModificationOperations.deleteColumnAt(grid, detail.column());
    };

    var eraseRow = function (grid, detail, comparator, generators) {
      return ModificationOperations.deleteRowAt(grid, detail.row());
    };

    var mergeContent = function (cells) {
      var readContent = function () {
        var kin = Arr.bind(cells, function (cell) {
          var children = Traverse.children(cell);
          // Exclude empty cells.
          return children.length > 1 || (children.length == 1 && Node.name(children[0]) !== 'br') ? [ children ] : [];
        });
        return Jam.intersperseThunk(kin, function () {
          return [ Element.fromTag('br') ];
        });
      };

      var contents = Arr.flatten(readContent());
      Remove.empty(cells[0]);
      InsertAll.append(cells[0], contents);
    };

    var mergeCells = function (grid, mergable, comparator, _generators) {
      var cells = mergable.cells();
      mergeContent(cells);
      return MergingOperations.merge(grid, mergable, comparator, Fun.constant(cells[0]));
    };

    var unmergeCells = function (grid, unmergable, comparator, generators) {
      console.log('unmergable', unmergable[0]);

      return Arr.foldr(unmergable, function (b, cell) {
        return MergingOperations.unmerge(b, cell, comparator, generators(cell));
      }, grid);
    };

    // Only column modifications force a resizing. Everything else just tries to preserve the table as is.
    var resize = Adjustments.adjustTo;

    return {
      insertRowBefore: RunOperation.run(insertRowBefore, RunOperation.onCell, Fun.noop, Fun.noop, Generators.modification),
      insertRowAfter:  RunOperation.run(insertRowAfter, RunOperation.onCell, Fun.noop, Fun.noop, Generators.modification),
      insertColumnBefore:  RunOperation.run(insertColumnBefore, RunOperation.onCell, resize, Fun.noop, Generators.modification),
      insertColumnAfter:  RunOperation.run(insertColumnAfter, RunOperation.onCell, resize, Fun.noop, Generators.modification),
      eraseColumn:  RunOperation.run(eraseColumn, RunOperation.onCell, resize, prune, Generators.modification),
      eraseRow:  RunOperation.run(eraseRow, RunOperation.onCell, Fun.noop, prune, Generators.modification),
      makeColumnHeader:  RunOperation.run(makeColumnHeader, RunOperation.onCell, Fun.noop, Fun.noop, Generators.transform('row', 'th')),
      unmakeColumnHeader:  RunOperation.run(unmakeColumnHeader, RunOperation.onCell, Fun.noop, Fun.noop, Generators.transform(null, 'td')),
      makeRowHeader:  RunOperation.run(makeRowHeader, RunOperation.onCell, Fun.noop, Fun.noop, Generators.transform('col', 'th')),
      unmakeRowHeader:  RunOperation.run(unmakeRowHeader, RunOperation.onCell, Fun.noop, Fun.noop, Generators.transform(null, 'td')),
      mergeCells: RunOperation.run(mergeCells, RunOperation.onMergable, Fun.noop, Fun.noop, Generators.merging),
      unmergeCells: RunOperation.run(unmergeCells, RunOperation.onUnmergable, Fun.noop, Fun.noop, Generators.merging)
    };
  }
);
<|endoftext|>


<commit_before>/* eslint no-console: 0 */

"use strict";

var colors = require("colors");
var getBrowsers = require("get-saucelabs-browsers");
var http = require("https");
var url = require("url");

var username = process.env.SAUCE_USERNAME;
var accessKey = process.env.SAUCE_ACCESS_KEY;
var tunnelId = process.env.SAUCE_TUNNEL || process.env.TRAVIS_JOB_NUMBER;
var build = process.env.TRAVIS_BUILD_NUMBER || null;
var testUrl = process.argv[2];
if (!username || !accessKey) {
  console.log("Missing username and access key for SauceLabs. Please provide ".red +
              "the %s and %s environment variables.".red,
              "SAUCE_USERNAME".underline,
              "SAUCE_ACCESS_KEY".underline);
  process.exit(1);
}

if (!tunnelId) {
  console.log("Missing tunnel identifier. Please start Sauce Connect and provide ".red +
              "the %s (or %s) environment variable.".red,
              "SAUCE_TUNNEL".underline,
              "TRAVIS_JOB_NUMBER".underline);
  process.exit(1);
}

if (!testUrl) {
  console.log("Missing test URL. Please provide it as the first argument, such as:\n".red +
              "    $ node bin/sauce-jasmine.js %s",
              "http://localhost:9999/test/SpecRunner.html".underline);
  process.exit(1);
}

function handleError(err) {
  if (!err) return;
  console.log("Unexpected error occured:".red);
  console.log.apply(console, arguments);
  process.exit(1);
}

function request(options, callback) {
  var req = http.request(options, function (res) {
    if (res.statusCode !== 200) {
      handleError("Unexpected status code %s when requesting %s",
                  colors.underline(res.statusCode),
                  options.path.underline);
    }

    var body = "";
    res.on("data", function (chunk) {
      body += chunk.toString();
    });

    res.on("end", function () {
      callback(JSON.parse(body));
    });
  });

  req.on("error", handleError);
  req.write(options.body);
  req.end();
}

var browserFilter = [
  "internet explorer/9..latest",
  "microsoftedge/13..latest",
  "firefox/31..latest/linux",
  "chrome/31..latest/linux",
  "safari/7..latest",
  "android/4.4..latest",
  "iphone/5.1..latest"
];

getBrowsers(browserFilter, function (err, result) {
  handleError(err);

  startBrowsers(result);
});

function startBrowsers(browsers) {
  var sauceBrowsers = formatBrowsers(browsers);

  var body = {
    platforms: sauceBrowsers,
    url: testUrl,
    tunnelIdentifier: tunnelId,
    build: build,
    name: "tus-js-client – Jasmine"
  };

  console.log("Requesting %s browser tests…".yellow, sauceBrowsers.length);

  var options = url.parse("https://saucelabs.com/rest/v1/" + username + "/js-tests");
  options.method = "POST";
  options.auth = username + ":" + accessKey;
  options.body = JSON.stringify(body);
  request(options, function (res) {
    var testIds = res["js tests"];
    startedTests = testIds.length;
    console.log("Queued %s browser tests…".yellow, startedTests);

    pollTestStatus(testIds);
  });
}

function formatBrowsers(browsers) {
  return browsers.filter(function (browser) {
    // One entry seems to always be undefined, so we filter it out.
    return browser.browserName !== undefined;
  }).map(function (browser) {
    return [
      browser.platform,
      browser.browserName,
      browser.version
    ];
  });
}

var startedTests = 0;
var failedTests = 0;
var passedTests = 0;
function pollTestStatus(testIds) {
  var body = {
    "js tests": testIds
  };

  var options = url.parse("https://saucelabs.com/rest/v1/" + username + "/js-tests/status");
  options.method = "POST";
  options.auth = username + ":" + accessKey;
  options.body = JSON.stringify(body);
  request(options, function (res) {
    var tests = res["js tests"];
    var remainingTestIds = [];

    tests.forEach(function (test) {
      if (test.status === "test error") {
        console.log("Browser %s errored!".red, test.platform.join(" "));
        failedTests++;
        return;
      }

      if (!("result" in test)) {
        // No results are available yet, so we still need to wait for it.
        remainingTestIds.push(test.id);
        return;
      }

      if (test.result == null) {
        console.log("Browser %s completed without results!".red, test.platform.join(" "));
        failedTests++;
        return;
      }

      if (test.result.passed === test.result.total) {
        // Test passed succesfully. No need to worry about it any more.
        console.log("Browser %s passed!".green, test.platform.join(" "));
        passedTests++;
        return;
      }

      console.log("Browser %s failed!".red, test.platform.join(" "));
      failedTests++;
      printFailedTests(test.result.tests);
    });

    if (remainingTestIds.length === 0) {
      console.log("Started: %s Passed: %s Failed: %s".yellow,
                  startedTests,
                  passedTests,
                  failedTests);

      process.exit(failedTests > 0 ? 1 : 0);
    } else {
      console.log("%s browser remaining…".gray, remainingTestIds.length);
    }

    setTimeout(pollTestStatus.bind({}, remainingTestIds), 3000);
  });
}

function printFailedTests(tests) {
  // The format of the tests array is defined in test/reporter.js
  tests
    .forEach(function (test, index) {
      if (test.result === true) return;

      console.log("  %s) %s".red, index+1, test.name);
    });
}
<commit_msg>Remove Safari 7 from tests due to problems with testing<commit_after>/* eslint no-console: 0 */

"use strict";

var colors = require("colors");
var getBrowsers = require("get-saucelabs-browsers");
var http = require("https");
var url = require("url");

var username = process.env.SAUCE_USERNAME;
var accessKey = process.env.SAUCE_ACCESS_KEY;
var tunnelId = process.env.SAUCE_TUNNEL || process.env.TRAVIS_JOB_NUMBER;
var build = process.env.TRAVIS_BUILD_NUMBER || null;
var testUrl = process.argv[2];
if (!username || !accessKey) {
  console.log("Missing username and access key for SauceLabs. Please provide ".red +
              "the %s and %s environment variables.".red,
              "SAUCE_USERNAME".underline,
              "SAUCE_ACCESS_KEY".underline);
  process.exit(1);
}

if (!tunnelId) {
  console.log("Missing tunnel identifier. Please start Sauce Connect and provide ".red +
              "the %s (or %s) environment variable.".red,
              "SAUCE_TUNNEL".underline,
              "TRAVIS_JOB_NUMBER".underline);
  process.exit(1);
}

if (!testUrl) {
  console.log("Missing test URL. Please provide it as the first argument, such as:\n".red +
              "    $ node bin/sauce-jasmine.js %s",
              "http://localhost:9999/test/SpecRunner.html".underline);
  process.exit(1);
}

function handleError(err) {
  if (!err) return;
  console.log("Unexpected error occured:".red);
  console.log.apply(console, arguments);
  process.exit(1);
}

function request(options, callback) {
  var req = http.request(options, function (res) {
    if (res.statusCode !== 200) {
      handleError("Unexpected status code %s when requesting %s",
                  colors.underline(res.statusCode),
                  options.path.underline);
    }

    var body = "";
    res.on("data", function (chunk) {
      body += chunk.toString();
    });

    res.on("end", function () {
      callback(JSON.parse(body));
    });
  });

  req.on("error", handleError);
  req.write(options.body);
  req.end();
}

var browserFilter = [
  "internet explorer/9..latest",
  "microsoftedge/13..latest",
  "firefox/31..latest/linux",
  "chrome/31..latest/linux",
  "safari/8..latest",
  "android/4.4..latest",
  "iphone/5.1..latest"
];

getBrowsers(browserFilter, function (err, result) {
  handleError(err);

  startBrowsers(result);
});

function startBrowsers(browsers) {
  var sauceBrowsers = formatBrowsers(browsers);

  var body = {
    platforms: sauceBrowsers,
    url: testUrl,
    tunnelIdentifier: tunnelId,
    build: build,
    name: "tus-js-client – Jasmine"
  };

  console.log("Requesting %s browser tests…".yellow, sauceBrowsers.length);

  var options = url.parse("https://saucelabs.com/rest/v1/" + username + "/js-tests");
  options.method = "POST";
  options.auth = username + ":" + accessKey;
  options.body = JSON.stringify(body);
  request(options, function (res) {
    var testIds = res["js tests"];
    startedTests = testIds.length;
    console.log("Queued %s browser tests…".yellow, startedTests);

    pollTestStatus(testIds);
  });
}

function formatBrowsers(browsers) {
  return browsers.filter(function (browser) {
    // One entry seems to always be undefined, so we filter it out.
    return browser.browserName !== undefined;
  }).map(function (browser) {
    return [
      browser.platform,
      browser.browserName,
      browser.version
    ];
  });
}

var startedTests = 0;
var failedTests = 0;
var passedTests = 0;
function pollTestStatus(testIds) {
  var body = {
    "js tests": testIds
  };

  var options = url.parse("https://saucelabs.com/rest/v1/" + username + "/js-tests/status");
  options.method = "POST";
  options.auth = username + ":" + accessKey;
  options.body = JSON.stringify(body);
  request(options, function (res) {
    var tests = res["js tests"];
    var remainingTestIds = [];

    tests.forEach(function (test) {
      if (test.status === "test error") {
        console.log("Browser %s errored!".red, test.platform.join(" "));
        failedTests++;
        return;
      }

      if (!("result" in test)) {
        // No results are available yet, so we still need to wait for it.
        remainingTestIds.push(test.id);
        return;
      }

      if (test.result == null) {
        console.log("Browser %s completed without results!".red, test.platform.join(" "));
        failedTests++;
        return;
      }

      if (test.result.passed === test.result.total) {
        // Test passed succesfully. No need to worry about it any more.
        console.log("Browser %s passed!".green, test.platform.join(" "));
        passedTests++;
        return;
      }

      console.log("Browser %s failed!".red, test.platform.join(" "));
      failedTests++;
      printFailedTests(test.result.tests);
    });

    if (remainingTestIds.length === 0) {
      console.log("Started: %s Passed: %s Failed: %s".yellow,
                  startedTests,
                  passedTests,
                  failedTests);

      process.exit(failedTests > 0 ? 1 : 0);
    } else {
      console.log("%s browser remaining…".gray, remainingTestIds.length);
    }

    setTimeout(pollTestStatus.bind({}, remainingTestIds), 3000);
  });
}

function printFailedTests(tests) {
  // The format of the tests array is defined in test/reporter.js
  tests
    .forEach(function (test, index) {
      if (test.result === true) return;

      console.log("  %s) %s".red, index+1, test.name);
    });
}
<|endoftext|>


<commit_before>// Copyright (c) 2015, Evan Summers (twitter.com/evanxsummers)
// ISC license, see http://github.com/evanx/redexutil/LICENSE

import assert from 'assert';

import Loggers from './Loggers';

const logger = Loggers.create(module.filename, 'info');

export function props(object) {
   return Object.keys(object).map(key => {
      let value = object[key];
      return {key, value};
   });
}

export function joinProps(object) {
   if (object) {
      return Object.keys(object).join(' ');
   } else {
      return 'empty';
   }
}
<commit_msg>join props<commit_after>// Copyright (c) 2015, Evan Summers (twitter.com/evanxsummers)
// ISC license, see http://github.com/evanx/redexutil/LICENSE

import assert from 'assert';

import Loggers from './Loggers';

const logger = Loggers.create(module.filename, 'info');

export function props(object) {
   return Object.keys(object).map(key => {
      let value = object[key];
      return {key, value};
   });
}

export function formatKeys(object) {
   if (object) {
      return Object.keys(object).join(' ');
   } else {
      return 'empty';
   }
}
<|endoftext|>


<commit_before>import APIQuery from './APIQuery.js'

describe('APIQuery.queries', () => {

    it('should query at least name, code, platform, and location of stop', () => {
        var stopFields = ["name", "code", "platformCode", "lat", "lon"];
        for (var field in stopFields) {
            expect(APIQuery.queries.nearestDepartures()).toMatch(field);
        }
    });
    it('should query at least route, route type, destination, arrival time, departure time, real time, destination, and service day of stop', () => {
        var stopFields =
            [/trip.*{.*route.*{.*shortName.*}.*}/, /trip.*{.*route.*{.*mode.*}.*}/,
            "realtimeArrival", "realtimeDeparture", "realtime", "stopHeadsign", "serviceDay"];
        for (var field in stopFields) {
            expect(APIQuery.queries.nearestDepartures()).toMatch(field);
        }
    });
});<commit_msg>Add another expect.<commit_after>import APIQuery from './APIQuery.js'

describe('APIQuery.queries', () => {

    it('should query at least name, code, platform, and location of stop', () => {
        var stopFields = ["name", "code", "platformCode", "lat", "lon"];
        for (var field in stopFields) {
            expect(APIQuery.queries.nearestDepartures()).toMatch(field);
        }
    });
    it('should query at least route, route type, destination, arrival time, departure time, real time, destination, and service day of stop', () => {
        var stopFields =
            [/trip.*{.*route.*{.*shortName.*}.*}/, /trip.*{.*route.*{.*mode.*}.*}/,
            "realtimeArrival", "realtimeDeparture", "realtime", "stopHeadsign", "serviceDay"];
        for (var field in stopFields) {
            expect(APIQuery.queries.nearestDepartures()).toMatch(field);
            expect(APIQuery.queries.stopDepartures()).toMatch(field);
        }
    });
});<|endoftext|>


<commit_before>/* eslint-disable no-undef */
/* eslint-disable import/no-unresolved */
// @generated by expo-yarn-workspaces

import 'expo/build/Expo.fx';
import { activateKeepAwake } from 'expo-keep-awake';
import registerRootComponent from 'expo/build/launch/registerRootComponent';

import App from '../App';

if (__DEV__) {
    activateKeepAwake();
}

registerRootComponent(App);
<commit_msg>auto build [skip ci]<commit_after>// @generated by expo-yarn-workspaces

import 'expo/build/Expo.fx';
import { activateKeepAwake } from 'expo-keep-awake';
import registerRootComponent from 'expo/build/launch/registerRootComponent';

import App from '../App';

if (__DEV__) {
  activateKeepAwake();
}

registerRootComponent(App);
<|endoftext|>


<commit_before>// subclipse.js

var subclipse = {};

subclipse.isManagedBySubclipse = function(pProject) {

	var SVNWorkspaceRoot = org.tigris.subversion.subclipse.core.resources.SVNWorkspaceRoot;
	// java.lang.System.err.println(pProject.getName() + " => isManagedBySubclipse: " + SVNWorkspaceRoot.isManagedBySubclipse(pProject));
	return SVNWorkspaceRoot.isManagedBySubclipse(pProject);

}

subclipse.repositoryRoot = function(pProject) {

	var SVNWorkspaceRoot = org.tigris.subversion.subclipse.core.resources.SVNWorkspaceRoot;
	var repositoryRoot = SVNWorkspaceRoot.getSVNResourceFor(pProject).getRepository().getRepositoryRoot();
	// java.lang.System.err.println(pProject.getName() + " => repoRoot: " + repositoryRoot.toString());
	return repositoryRoot.toString();

}

var module = {
	name : 'subclipse',
	exports : {
		subclipse : subclipse,
	},
};
<commit_msg>added new function to retrieve the SVNUrl of a given project<commit_after>// subclipse.js

var subclipse = {};

subclipse.isManagedBySubclipse = function(pProject) {

	var SVNWorkspaceRoot = org.tigris.subversion.subclipse.core.resources.SVNWorkspaceRoot;
	// java.lang.System.err.println(pProject.getName() + " => isManagedBySubclipse: " + SVNWorkspaceRoot.isManagedBySubclipse(pProject));
	return SVNWorkspaceRoot.isManagedBySubclipse(pProject);

}

subclipse.repositoryRoot = function(pProject) {

	var SVNWorkspaceRoot = org.tigris.subversion.subclipse.core.resources.SVNWorkspaceRoot;
	var repositoryRoot = SVNWorkspaceRoot.getSVNResourceFor(pProject).getRepository().getRepositoryRoot();
	// java.lang.System.err.println(pProject.getName() + " => repoRoot: " + repositoryRoot.toString());
	return repositoryRoot.toString();

}

subclipse.url = function(pProject) {

	var SVNWorkspaceRoot = org.tigris.subversion.subclipse.core.resources.SVNWorkspaceRoot;
	var url = SVNWorkspaceRoot.getSVNResourceFor(pProject).getUrl();
	// java.lang.System.err.println(pProject.getName() + " => url: " + url.toString());
	return url.toString();

}

var module = {
	name : 'subclipse',
	exports : {
		subclipse : subclipse,
	},
};
<|endoftext|>


<commit_before>exports.init = function init(){
    var router  = require('./router')
        , middleware  = require('./middleware')
        , express = require('express')
        , app     = express()
        , conf    = require('./conf').get(process.env.NODE_ENV)
/*        , crypto  = require('crypto')
        , auth = express.basicAuth(function(username, password) {
            var cipher = crypto.createCipher('aes-256-cbc', conf.application.salt);
            cipher.update(password, 'utf8', 'base64');
            return ((cipher.final('base64') === conf.application.password) && username === conf.application.username );
        }, conf.application.realm);*/
        , auth = require('./authenticate').basicAuth(conf);

    middleware.setup(app, conf);
    router.run(auth, app, conf.application.routes);

    app.listen(conf.server.port);
    console.log('node-rest-demo pid %s listening on %d in %s', process.pid, conf.server.port, process.env.NODE_ENV);
};<commit_msg>Bugfix<commit_after>exports.init = function init(){
    var router  = require('./router')
        , middleware  = require('./middleware')
        , express = require('express')
        , app     = express()
        , conf    = require('./conf').get(process.env.NODE_ENV)
/*        , crypto  = require('crypto')
        , auth = express.basicAuth(function(username, password) {
            var cipher = crypto.createCipher('aes-256-cbc', conf.application.salt);
            cipher.update(password, 'utf8', 'base64');
            return ((cipher.final('base64') === conf.application.password) && username === conf.application.username );
        }, conf.application.realm);*/
        , auth = require('./authentication').basicAuth(conf);

    middleware.setup(app, conf);
    router.run(auth, app, conf.application.routes);

    app.listen(conf.server.port);
    console.log('node-rest-demo pid %s listening on %d in %s', process.pid, conf.server.port, process.env.NODE_ENV);
};<|endoftext|>


<commit_before>export { Comparator, Asserter_Equal };

/**
 *
 */
class Comparator {

  /** @return Return true, if two array of tensor are equal by value. */
  static isTensorArrayEqual( tensorArray1, tensorArray2 ) {

    if ( tensorArray1 === tensorArray2 )
      return true;

    if ( tensorArray1 == null || tensorArray2 == null )
      return false;

    if ( tensorArray1.length !== tensorArray2.length )
      return false;

    for ( let i = 0; i < tensorArray1.length; ++i ) {  // Compare every element of each tensor.
      let allElementEqual = tf.tidy( "TensorTools.Comparator.isTensorArrayEqual", () => {
        let everyElementEqualTensor1d = tensorArray1[ i ].equal( tensorArray2[ i ] );
        let allElementEqualTensor1d = everyElementEqualTensor1d.all();
        return allElementEqualTensor1d.arraySync(); // 0: false. 1: true.
      });

      if ( !allElementEqual )
        return false;
    }

    return true;
  }

}

/**
 * Assert a tensor whether equals to a number array.
 */
class Asserter_Equal {

  /**
   *
   *
   * @param {number} acceptableDifferenceRate
   *   How many difference (in ratio) between the tensor and numberArray (per element) is acceptable. Because floating-point
   * accumulated error of float32 (GPU) and float64 (CPU) is different, a little difference should be allowed. Otherwise,
   * the comparison may hardly to pass this check. Default is 0.4 (i.e. 40% difference is allowed).
   */
  constructor( acceptableDifferenceRate = 0.4 ) {
    this.acceptableDifferenceRate = acceptableDifferenceRate;
    this.comparator = Asserter_Equal.ElementComparator.bind( this );

    // Used by assert_Number_Number().
    this.lhsNumberArrayDefault = [ 0 ];
    this.rhsNumberArrayDefault = [ 0 ];
  }

  /**
   *
   * @param {number[]} lhsNumberArray
   *   The number array to be checked.
   *
   * @param {number[]} rhsNumberArray
   *   The number array to be compared against lhsNumberArray.
   *
   * @param {string} prefixMsg
   *   The text to be displayed at the beginning when comparison failed.
   *
   * @param {string} lhsNumberArrayName
   *   The text to be displayed for the lhsNumberArray when comparison failed.
   *
   * @param {string} rhsNumberArrayName
   *   The text to be displayed for the rhsNumberArray when comparison failed.
   *
   * @param {string} postfixMsg
   *   The text to be displayed at the tail when comparison failed.
   */
  assert_NumberArray_NumberArray( lhsNumberArray, rhsNumberArray, prefixMsg, lhsNumberArrayName, rhsNumberArrayName, postfixMsg ) {

    // Check both null or non-null.
    tf.util.assert( ( lhsNumberArray == null ) == ( rhsNumberArray == null ),
      `${prefixMsg} ${lhsNumberArrayName} ( ${lhsNumberArray} ) and ${rhsNumberArrayName} ( ${rhsNumberArray} ) `
        + `should be both null or non-null. ${postfixMsg}` );

    if ( !lhsNumberArray )
      return; // Since null, no element need to be compared futher.

    // Check both length.
    tf.util.assert( lhsNumberArray.length == rhsNumberArray.length,
    `${prefixMsg} ${lhsNumberArrayName} length ( ${lhsNumberArray.length} ) should be ( ${rhsNumberArray.length} ). ${postfixMsg}` );

    this.rhsNumberArray = rhsNumberArray; // For ElementComparator() to access.

    // Check both elements.
    //
    // Note: Array.every() seems faster than for-loop.
    tf.util.assert( lhsNumberArray.every( this.comparator ),
      `${prefixMsg} ${lhsNumberArrayName}[ ${this.elementIndex} ] `
        + `( ${lhsNumberArray[ this.elementIndex ]} ) should be ( ${rhsNumberArray[ this.elementIndex ]} ) `
        + `( ${lhsNumberArray} ) should be ( ${rhsNumberArray} ). `
        + `${postfixMsg}` );

//!!! (2021/08/10 Remarked) Old Codes.
//         `PointDepthPoint output${i}[ ${elementIndex} ] ( ${outputArray[ elementIndex ]} ) should be ( ${outputArrayRef[ elementIndex ]} ) `
//           +`( ${outputArray} ) should be ( ${outputArrayRef} ). `
//           + `${parametersDescription}` );

  }

  /**
   *
   * @param {number} lhsNumber
   *   The number to be checked.
   *
   * @param {number} rhsNumber
   *   The number to be compared against lhsNumber.
   *
   * @param {string} prefixMsg
   *   The text to be displayed at the beginning when comparison failed.
   *
   * @param {string} lhsNumberName
   *   The text to be displayed for the lhsNumber when comparison failed.
   *
   * @param {string} rhsNumberName
   *   The text to be displayed for the rhsNumber when comparison failed.
   *
   * @param {string} postfixMsg
   *   The text to be displayed at the tail when comparison failed.
   */
  assert_Number_Number( lhsNumber, rhsNumber, prefixMsg, lhsNumberName, rhsNumberName, postfixMsg ) {
    this.lhsNumberArrayDefault[ 0 ] = lhsNumber;
    this.rhsNumberArrayDefault[ 0 ] = rhsNumber;

    this.assert_NumberArray_NumberArray(
      this.lhsNumberArrayDefault, this.rhsNumberArrayDefault, prefixMsg, lhsNumberName, rhsNumberName, postfixMsg );
  }

  /**
   *
   * @param {tf.tensor} tensor
   *   The tf.tensor to be checked.
   *
   * @param {number[]} numberArray
   *   The number to be compared against tensor's data.
   *
   * @param {string} prefixMsg
   *   The text to be displayed at the beginning when comparison failed.
   *
   * @param {string} tensorName
   *   The text to be displayed for the tensor when comparison failed.
   *
   * @param {string} numberArrayName
   *   The text to be displayed for the numberArray when comparison failed.
   *
   * @param {string} postfixMsg
   *   The text to be displayed at the tail when comparison failed.
   */
  assert_Tensor_NumberArray( tensor, rhsNumberArray, prefixMsg, tensorName, rhsNumberArrayName, postfixMsg ) {

    let tensorDataArray = null;
    if ( tensor ) {
      tensorDataArray = tensor.dataSync();
    }

    this.assert_NumberArray_NumberArray( tensorDataArray, rhsNumberArray, prefixMsg, tensorName, rhsNumberArrayName, postfixMsg );

//!!! (2021/12/31 Remarked) use assert_NumberArray_NumberArray() instead.
//     // Check both null or non-null.
//     tf.util.assert( ( tensorDataArray == null ) == ( numberArray == null ),
//       `${prefixMsg} ${tensorName} ( ${tensorDataArray} ) and ${numberArrayName} ( ${numberArray} ) should be both null or non-null. ${postfixMsg}` );
//
//     if ( !tensorDataArray )
//       return; // Since null, no element need to be compared futher.
//
//     // Check both length.
//     tf.util.assert( tensorDataArray.length == numberArray.length,
//     `${prefixMsg} ${tensorName} length ( ${tensorDataArray.length} ) should be ( ${numberArray.length} ). ${postfixMsg}` );
//
//     this.numberArray = numberArray; // For ElementComparator() to access.
//
//     // Check both elements.
//     //
//     // Note: Array.every() seems faster than for-loop.
//     tf.util.assert( tensorDataArray.every( this.comparator ),
//       `${prefixMsg} ${tensorName}[ ${this.elementIndex} ] `
//         + `( ${tensorDataArray[ this.elementIndex ]} ) should be ( ${numberArray[ this.elementIndex ]} ) `
//         + `( ${tensorDataArray} ) should be ( ${numberArray} ). `
//         + `${postfixMsg}` );
//
// //!!! (2021/08/10 Remarked) Old Codes.
// //         `PointDepthPoint output${i}[ ${elementIndex} ] ( ${outputArray[ elementIndex ]} ) should be ( ${outputArrayRef[ elementIndex ]} ) `
// //           +`( ${outputArray} ) should be ( ${outputArrayRef} ). `
// //           + `${parametersDescription}` );

  }

  /**
   * @param {Asserter_Tensor_NumberArray} this
   *   - The this.rhsNumberArray[] and this.acceptableDifferenceRate will be read by this method.
   *   - The this.elementIndex will be set by this method.
   */
  static ElementComparator( value, index ) {

    let valueRef = this.rhsNumberArray[ this.elementIndex = index ];
    let delta = Math.abs( value - valueRef );

    let valueAbs = Math.abs( value );
    let valueRefAbs = Math.abs( valueRef );

    // Ratio to the smaller one.
    //
    // When one of two compared values is zero, it will always be failed if compare to the larger value (got 100% delteRate).
    let deltaRateBase = Math.min( valueAbs, valueRefAbs );

    let deltaRate;
    if ( deltaRateBase > 0 ) // Avoid divided by zero.
      deltaRate = delta / deltaRateBase; // Using ratio so that the difference will not to large even if value is large.
    else
      deltaRate = delta;

    if ( deltaRate <= this.acceptableDifferenceRate )
      return true;
    return false;
  }

}
<commit_msg>Update TensorTools.js<commit_after>export { Comparator, Asserter_Equal };

/**
 *
 */
class Comparator {

  /** @return Return true, if two array of tensor are equal by value. */
  static isTensorArrayEqual( tensorArray1, tensorArray2 ) {

    if ( tensorArray1 === tensorArray2 )
      return true;

    if ( tensorArray1 == null || tensorArray2 == null )
      return false;

    if ( tensorArray1.length !== tensorArray2.length )
      return false;

    for ( let i = 0; i < tensorArray1.length; ++i ) {  // Compare every element of each tensor.
      let allElementEqual = tf.tidy( "TensorTools.Comparator.isTensorArrayEqual", () => {
        let everyElementEqualTensor1d = tensorArray1[ i ].equal( tensorArray2[ i ] );
        let allElementEqualTensor1d = everyElementEqualTensor1d.all();
        return allElementEqualTensor1d.arraySync(); // 0: false. 1: true.
      });

      if ( !allElementEqual )
        return false;
    }

    return true;
  }

}

/**
 * Assert a tensor whether equals to a number array.
 */
class Asserter_Equal {

  /**
   *
   *
   * @param {number} acceptableDifferenceRate
   *   How many difference (in ratio) between the tensor and numberArray (per element) is acceptable. Because floating-point
   * accumulated error of float32 (GPU) and float64 (CPU) is different, a little difference should be allowed. Otherwise,
   * the comparison may hardly to pass this check. Default is 0.4 (i.e. 40% difference is allowed).
   */
  constructor( acceptableDifferenceRate = 0.4 ) {
    this.acceptableDifferenceRate = acceptableDifferenceRate;
    this.comparator = Asserter_Equal.ElementComparator.bind( this );

    // Used by assert_Number_Number().
    this.lhsNumberArrayDefault = [ 0 ];
    this.rhsNumberArrayDefault = [ 0 ];
  }

  /**
   *
   * @param {number[]} lhsNumberArray
   *   The number array to be checked.
   *
   * @param {number[]} rhsNumberArray
   *   The number array to be compared against lhsNumberArray.
   *
   * @param {string} prefixMsg
   *   The text to be displayed at the beginning when comparison failed.
   *
   * @param {string} lhsNumberArrayName
   *   The text to be displayed for the lhsNumberArray when comparison failed.
   *
   * @param {string} rhsNumberArrayName
   *   The text to be displayed for the rhsNumberArray when comparison failed.
   *
   * @param {string} postfixMsg
   *   The text to be displayed at the tail when comparison failed.
   */
  assert_NumberArray_NumberArray( lhsNumberArray, rhsNumberArray, prefixMsg, lhsNumberArrayName, rhsNumberArrayName, postfixMsg ) {

    // Check both null or non-null.
    tf.util.assert( ( lhsNumberArray == null ) == ( rhsNumberArray == null ),
      `${prefixMsg} ${lhsNumberArrayName} ( ${lhsNumberArray} ) and ${rhsNumberArrayName} ( ${rhsNumberArray} ) `
        + `should be both null or non-null. ${postfixMsg}` );

    if ( !lhsNumberArray )
      return; // Since null, no element need to be compared futher.

    // Check both length.
    tf.util.assert( lhsNumberArray.length == rhsNumberArray.length,
    `${prefixMsg} ${lhsNumberArrayName} length ( ${lhsNumberArray.length} ) should be ( ${rhsNumberArray.length} ). ${postfixMsg}` );

    this.rhsNumberArray = rhsNumberArray; // For ElementComparator() to access.

    // Check both elements.
    //
    // Note: Array.every() seems faster than for-loop.
    tf.util.assert( lhsNumberArray.every( this.comparator ),
      `${prefixMsg} ${lhsNumberArrayName}[ ${this.elementIndex} ] `
        + `( ${lhsNumberArray[ this.elementIndex ]} ) should be ( ${rhsNumberArray[ this.elementIndex ]} ) `
        + `( ${lhsNumberArray} ) should be ( ${rhsNumberArray} ). `
        + `${postfixMsg}` );
  }

  /**
   *
   * @param {number} lhsNumber
   *   The number to be checked.
   *
   * @param {number} rhsNumber
   *   The number to be compared against lhsNumber.
   *
   * @param {string} prefixMsg
   *   The text to be displayed at the beginning when comparison failed.
   *
   * @param {string} lhsNumberName
   *   The text to be displayed for the lhsNumber when comparison failed.
   *
   * @param {string} rhsNumberName
   *   The text to be displayed for the rhsNumber when comparison failed.
   *
   * @param {string} postfixMsg
   *   The text to be displayed at the tail when comparison failed.
   */
  assert_Number_Number( lhsNumber, rhsNumber, prefixMsg, lhsNumberName, rhsNumberName, postfixMsg ) {
    this.lhsNumberArrayDefault[ 0 ] = lhsNumber;
    this.rhsNumberArrayDefault[ 0 ] = rhsNumber;

    this.assert_NumberArray_NumberArray(
      this.lhsNumberArrayDefault, this.rhsNumberArrayDefault, prefixMsg, lhsNumberName, rhsNumberName, postfixMsg );
  }

  /**
   *
   * @param {tf.tensor} tensor
   *   The tf.tensor to be checked.
   *
   * @param {number[]} numberArray
   *   The number to be compared against tensor's data.
   *
   * @param {string} prefixMsg
   *   The text to be displayed at the beginning when comparison failed.
   *
   * @param {string} tensorName
   *   The text to be displayed for the tensor when comparison failed.
   *
   * @param {string} numberArrayName
   *   The text to be displayed for the numberArray when comparison failed.
   *
   * @param {string} postfixMsg
   *   The text to be displayed at the tail when comparison failed.
   */
  assert_Tensor_NumberArray( tensor, rhsNumberArray, prefixMsg, tensorName, rhsNumberArrayName, postfixMsg ) {

    let tensorDataArray = null;
    if ( tensor ) {
      tensorDataArray = tensor.dataSync();
    }

    this.assert_NumberArray_NumberArray( tensorDataArray, rhsNumberArray, prefixMsg, tensorName, rhsNumberArrayName, postfixMsg );
  }

  /**
   * @param {Asserter_Tensor_NumberArray} this
   *   - The this.rhsNumberArray[] and this.acceptableDifferenceRate will be read by this method.
   *   - The this.elementIndex will be set by this method.
   */
  static ElementComparator( value, index ) {

    let valueRef = this.rhsNumberArray[ this.elementIndex = index ];
    let delta = Math.abs( value - valueRef );

    let valueAbs = Math.abs( value );
    let valueRefAbs = Math.abs( valueRef );

    // Ratio to the smaller one.
    //
    // When one of two compared values is zero, it will always be failed if compare to the larger value (got 100% delteRate).
    let deltaRateBase = Math.min( valueAbs, valueRefAbs );

    let deltaRate;
    if ( deltaRateBase > 0 ) // Avoid divided by zero.
      deltaRate = delta / deltaRateBase; // Using ratio so that the difference will not to large even if value is large.
    else
      deltaRate = delta;

    if ( deltaRate <= this.acceptableDifferenceRate )
      return true;
    return false;
  }

}
<|endoftext|>


<commit_before>import Ember from 'ember';
import module from '../helpers/module-for-db';
import { test } from '../helpers/qunit';

const {
  run
} = Ember;

module('filter', {
  beforeEach() {
    this.owner = Ember.Object.create({ type: null });
    this.opts = {
      owner: [ 'type' ],
      document: [ 'type' ],
      matches(doc, owner) {
        return doc.get('type') === owner.get('type');
      }
    };
    this.filter = () => this.db._createInternalFilter(this.owner, this.opts).model(true);
  }
});

test('create filter', function(assert) {
  let filter = this.filter();
  assert.ok(filter);
  assert.equal(filter.get('values.length'), 0);
  assert.equal(filter.get('value'), undefined);
});

test('create filter with existing matched values', function(assert) {
  let duck = this.db.doc({ id: 'duck:yellow', type: 'duck' });
  this.db.doc({ id: 'house:big', type: 'house' });
  this.owner.set('type', 'duck');
  let filter = this.filter();
  assert.equal(filter.get('values.length'), 1);
  assert.equal(filter.get('value'), duck);
});

test('updated doc is removed from values', function(assert) {
  let duck = this.db.doc({ id: 'duck:yellow', type: 'duck' });
  this.owner.set('type', 'duck');
  let filter = this.filter();
  assert.equal(filter.get('values.length'), 1);
  assert.equal(filter.get('value'), duck);
  duck.set('type', 'ducky');
  assert.equal(filter.get('values.length'), 0);
  assert.equal(filter.get('value'), undefined);
});

test('added doc is added to values', function(assert) {
  this.owner.set('type', 'duck');
  let filter = this.filter();
  assert.equal(filter.get('value'), undefined);
  let duck = this.db.doc({ id: 'duck:yellow', type: 'duck' });
  assert.equal(filter.get('value'), duck);
});

test('destroy stops observing', function(assert) {
  let duck = this.db.doc({ id: 'duck:yellow', type: 'duck' });

  let events = [];
  let wrap = (name, func, obj) => {
    let fn = obj[func];
    obj[func] = function(...args) {
      events.push({ name, func });
      return fn.call(obj, ...args);
    }
  };

  wrap('owner', 'removeObserver', this.owner);
  wrap('duck', 'removeObserver', duck);
  wrap('identity', 'removeEnumerableObserver', this.db.get('identity'));

  this.owner.set('type', 'duck');
  let filter = this.filter();
  assert.equal(filter.get('value'), duck);

  run(() => filter.destroy());

  assert.deepEqual(events, [
    {
      "func": "removeObserver",
      "name": "owner"
    },
    {
      "func": "removeEnumerableObserver",
      "name": "identity"
    },
    {
      "func": "removeObserver",
      "name": "duck"
    }
  ]);
});

test('destroyed new doc is removed', function(assert) {
  let duck = this.db.doc({ id: 'duck:yellow', type: 'duck' });
  this.owner.set('type', 'duck');

  let filter = this.filter();

  assert.equal(filter.get('values.length'), 1);
  assert.equal(filter.get('value'), duck);

  run(() => duck.destroy());

  assert.equal(filter.get('values.length'), 0);
  assert.equal(filter.get('value'), undefined);
});
<commit_msg>destroy filter in tests<commit_after>import Ember from 'ember';
import module from '../helpers/module-for-db';
import { test } from '../helpers/qunit';

const {
  run
} = Ember;

module('filter', {
  beforeEach() {
    this.owner = Ember.Object.create({ type: null });
    this.opts = {
      owner: [ 'type' ],
      document: [ 'type' ],
      matches(doc, owner) {
        return doc.get('type') === owner.get('type');
      }
    };
    this.filter = () => this.db._createInternalFilter(this.owner, this.opts).model(true);
  }
});

test('create filter', function(assert) {
  let filter = this.filter();
  assert.ok(filter);
  assert.equal(filter.get('values.length'), 0);
  assert.equal(filter.get('value'), undefined);
  run(() => filter.destroy());
});

test('create filter with existing matched values', function(assert) {
  let duck = this.db.doc({ id: 'duck:yellow', type: 'duck' });
  this.db.doc({ id: 'house:big', type: 'house' });
  this.owner.set('type', 'duck');
  let filter = this.filter();
  assert.equal(filter.get('values.length'), 1);
  assert.equal(filter.get('value'), duck);
  run(() => filter.destroy());
});

test('updated doc is removed from values', function(assert) {
  let duck = this.db.doc({ id: 'duck:yellow', type: 'duck' });
  this.owner.set('type', 'duck');
  let filter = this.filter();
  assert.equal(filter.get('values.length'), 1);
  assert.equal(filter.get('value'), duck);
  duck.set('type', 'ducky');
  assert.equal(filter.get('values.length'), 0);
  assert.equal(filter.get('value'), undefined);
  run(() => filter.destroy());
});

test('added doc is added to values', function(assert) {
  this.owner.set('type', 'duck');
  let filter = this.filter();
  assert.equal(filter.get('value'), undefined);
  let duck = this.db.doc({ id: 'duck:yellow', type: 'duck' });
  assert.equal(filter.get('value'), duck);
  run(() => filter.destroy());
});

test('destroy stops observing', function(assert) {
  let duck = this.db.doc({ id: 'duck:yellow', type: 'duck' });

  let events = [];
  let wrap = (name, func, obj) => {
    let fn = obj[func];
    obj[func] = function(...args) {
      events.push({ name, func });
      return fn.call(obj, ...args);
    }
  };

  wrap('owner', 'removeObserver', this.owner);
  wrap('duck', 'removeObserver', duck);
  wrap('identity', 'removeEnumerableObserver', this.db.get('identity'));

  this.owner.set('type', 'duck');
  let filter = this.filter();
  assert.equal(filter.get('value'), duck);

  run(() => filter.destroy());

  assert.deepEqual(events, [
    {
      "func": "removeObserver",
      "name": "owner"
    },
    {
      "func": "removeEnumerableObserver",
      "name": "identity"
    },
    {
      "func": "removeObserver",
      "name": "duck"
    }
  ]);
});

test('destroyed new doc is removed', function(assert) {
  let duck = this.db.doc({ id: 'duck:yellow', type: 'duck' });
  this.owner.set('type', 'duck');

  let filter = this.filter();

  assert.equal(filter.get('values.length'), 1);
  assert.equal(filter.get('value'), duck);

  run(() => duck.destroy());

  assert.equal(filter.get('values.length'), 0);
  assert.equal(filter.get('value'), undefined);

  run(() => filter.destroy());
});
<|endoftext|>


<commit_before>// test deps
var test = require('tape');
var td = require('testdouble');

// require and stub oled.js
var oledjs = require('../../build/oled.js');
oledjs.prototype._setUpI2C = td.function();
oledjs.prototype._transfer = td.function();

// require and stub johnny-five
var five = td.function();
var board = new td.constructor();

// import framebuffer snapshots
var drawPixelBuffer = require('../buffers/drawPixel');

test('drawPixel outputs correct buffer', function (t) {
  t.plan(1);

  var options = {
    width: 128,
    height: 64,
    address: 0x3D
  };

  var oled = new oledjs(board, five, options);

  oled.drawPixel([
    [127, 0, 1],
    [127, 31, 1],
    [127, 16, 1],
    [64, 16, 1]
  ], false);

  t.ok(oled.buffer.equals(Buffer.from(drawPixelBuffer)));
});
<commit_msg>Fixing test require statement<commit_after>// test deps
var test = require('tape');
var td = require('testdouble');

// require and stub oled.js
var oledjs = require('../../build/oled.js').default;
oledjs.prototype._setUpI2C = td.function();
oledjs.prototype._transfer = td.function();

// require and stub johnny-five
var five = td.function();
var board = new td.constructor();

// import framebuffer snapshots
var drawPixelBuffer = require('../buffers/drawPixel');

test('drawPixel outputs correct buffer', function (t) {
  t.plan(1);

  var options = {
    width: 128,
    height: 64,
    address: 0x3D
  };

  var oled = new oledjs(board, five, options);

  oled.drawPixel([
    [127, 0, 1],
    [127, 31, 1],
    [127, 16, 1],
    [64, 16, 1]
  ], false);

  t.ok(oled.buffer.equals(Buffer.from(drawPixelBuffer)));
});
<|endoftext|>