<commit_before>import _ from "underscore";
import $ from "jquery";
import context from "context-utils";
import { Collection } from "backbone";
import BaseView from "../BaseView";
import ListItemView from "./ListItemView";
import ListGroupView from "./ListGroupView";
import getContextOptions from "../utils/getContextOptions";
import { translate3d, overflowScrolling, getVendorStyle, transition } from "../utils/style";

const TOP_DOWN               = 1;
const DOWN_TOP               = 2;
const LEFT_RIGHT             = 4;
const RIGHT_LEFT             = 8;
const ORIENTATION_VERTICAL   = 'vertical';
const ORIENTATION_HORIZONTAL = 'horizontal';

const INITIAL_POSITION_TOP  = -9000;
const INITIAL_POSITION_LEFT = -9000;

var ticking = false;
let requestTick = function requestTick(callback) {
	if (!ticking) {
		window.requestAnimationFrame(() => {
			ticking = false;
			callback();
		});
	}
	ticking = true;
}

export default class ListView extends BaseView {

	className() {
		return 'ui-list-view ' + ( _.result(this, "addClass") || '' );
	}

	constructor(options) {
		super(options);

		this.addEvents({
			'click': 'onClick',
			'scroll': 'onScroll',
			'touchstart': 'onTouchStart',
			'touchmove': 'onTouchMove',
			'touchend': 'onTouchEnd',
			'mousedown': 'onTouchStart',
			'mousemove': 'onTouchMove',
			'mouseup': 'onTouchEnd'
		});

		this.setDefaultsOptions(getContextOptions('ListView'), {
			itemClass: 'ui-list-item',
			itemHeight: 120,
			itemWidth: 120,
			itemsPerRow: 1,
			placeholders: 20, // Number of items
			placeholderCache: 2,
			orientation: ORIENTATION_VERTICAL,
			infinite: false,
			infiniteLoadingSize: 44,
			distance: 100,
			pullToRefresh: false,
			pullToRefreshWithOverscroll: false,
			pullToRefreshSize: 60,
			pullToRefreshContent: 'Pull to refresh...',
			pullToRefreshClass: 'ui-pull-to-refresh',
			touchActiveClassName: 'touch-active',
			emptyText: '',
			emptyView: null,
			delayedRender: 0,
			headerView: null, // view to print on the header
			headerSize: 0, // height of the header in a vertical listview or width in an horizontal listview
			footerView: null,
			changeDirectionPan: 50,
			groupBy: null, // enable group
			groupWidth: 50, // used on horizontal list view
			groupHeight: 30 // used on vertical list view
		});

		this.items = []; // store the placeholders
		this.positionAtIndex = {}; // store item position per index
		this.rowItems = []; // used to retrieve row information
		this.rows = []; // used to retrieve the start and end item per row
		this.groups = []; // contains group position, margin and order
		this.groupViews = {}; // cache all the group views
		this.x = 0; // current horizontal scroll
		this.y = 0; // current vertical scroll
		this.previousX = 0;
		this.previousY = 0;
		this.overscrollX = 0;
		this.overscrollY = 0;
		this.isOverscrolling = false;
		this.offsetCalculated = false;
		this.offsetX = 0;
		this.offsetY = 0;
		this.listWidth = 0;
		this.listHeight = 0;
		this.containerSize = 0;
		this.rendered = false;
		this.renderTries = 5;
		this.isLoadingMore = false;
		this.isRefreshing = false;
		this.isClicking = false;
		this.activeItem = null;
		this.touchX = 0;
		this.touchY = 0;
		this.touchStartedAtScollX = 0;
		this.touchStartedAtScollY = 0;
		this.direction = null;
		this.previousDirection = null;
		this.startChangeDirectionAt = null;
		this.headerSize = 0;

		this.setCollection(this.collection);

		this.onMove = _.bind(this.onMove, this);
		// this.resizeContainer = _.debounce(_.bind(this.resizeContainer, this), 10);
		// this.updateAllContents = _.debounce(_.bind(this.updateAllContents, this), 10);

		this.triggerScrollStart = _.bind(this.triggerScrollStart, this);
		this.triggerScrollStart = _.debounce(this.triggerScrollStart, 500, true);
	}

	//
	// Methods
	//

	render() {
		if (this.onRender) {
			if ( this.timeoutId )
				clearTimeout(this.timeoutId);

			this.timeoutId = setTimeout(() => {
				this.timeoutId = null;
				this.requestRender();
			}, this.options.delayedRender);
		}
		else {
			this.rendered = true;
		}
		return this;
	}

	requestRender() {
		if ( this.rafId )
			this.cancelAnimationFrame(this.rafId);

		this.rafId = this.requestAnimationFrame(() => {
			this.rafId = null;
			// Pass to the onRender callback if the view is already rendered
			let result = this.onRender(this.rendered);
			this.delegateEvents();
			this.rendered = (result !== false);
		});
	}

	onRender(rendered) {
		if (rendered) return this;

		// Check list dimensions in order to render correctly
		this.listWidth  = this.$el.width();
		this.listHeight = this.$el.height();
		// if (this.listWidth === 0 && this.listHeight === 0 && this.renderTries > 0) {
		// 	this.renderTries--;
		// 	this.requestRender();
		// 	return false;
		// }

		// Prepare the content
		this.$el
			.addClass('overflow-scroll')
			.addClass(this.options.orientation);

		if (this.options.orientation === ORIENTATION_HORIZONTAL) {
			this.$el.css('white-space', 'nowrap');
		}

		// For Android and WKWebView
		overflowScrolling(this.el, true, this.options.orientation);

		// Empty text
		this.cache.$emptyText = $('<div class="listview-empty empty"></div>').text(this.options.emptyText).hide();

		// Empty view
		if (this.options.emptyView) {
			this.cache.$emptyText.append(this.options.emptyView.el);
			this.options.emptyView.render();
		}

		if (!this.collection.fetching) {
			this.sync();
		}
		this.$el.append(this.cache.$emptyText);

		// Container
		let $scrollContainer = this.cache.$scrollContainer = $('<div class="listview-container">').css({
			position: 'relative',
			overflow: 'visible',
			display: 'inline-block'
		});
		let $groupsContainer = this.cache.$groupsContainer = $('<div class="listview-groups-container">').css({
			position: 'relative',
			display: 'inline-block',
			overflow: 'visible'
		});
		let $placeholderContainer = this.cache.$placeholderContainer = $('<div class="listview-placeholder-container">').css({
			position: 'absolute',
			display: 'inline-block',
			// overflow: 'hidden',
			top: 0,
			left: 0
		});
		$scrollContainer.append($groupsContainer);
		$scrollContainer.append($placeholderContainer);
		this.resizeContainer();

		this.requestAnimationFrame(() => {
			this.$el.removeClass('overflow-scroll');
			this.requestAnimationFrame(() => {
				this.$el.addClass('overflow-scroll');
			});
		});

		// Prepare placeholders
		var anElement, anItem, aPosition, aXY;
		var itemStyle = {
			position: 'absolute',
			top: INITIAL_POSITION_TOP,
			left: INITIAL_POSITION_LEFT
		};
		for (var i = 0; i < this.options.placeholders; i++) {
			anElement = $('<div>').addClass(this.options.itemClass);
			anItem = {
				index: i,
				$el: anElement
			};

			aXY = this.getXYFromIndex(i);
			if (aXY) {
				anItem.x = aXY.x;
				anItem.y = aXY.y;
			}

			if (this.options.orientation === ORIENTATION_VERTICAL) {
				itemStyle.width = (100 / this.options.itemsPerRow) + '%';
			} else {
				itemStyle.height = (100 / this.options.itemsPerRow) + '%';
			}

			anElement.css(itemStyle);

			this.updateContent(anItem, true);
			this.items.push(anItem);
			$placeholderContainer.append(anElement);
		}

		// Groups
		this.renderGroups();

		// Container
		this.$el.append($scrollContainer);

		// Header
		if (this.options.headerView && this.options.headerView instanceof BaseView) {
			this.options.headerView.$el.css('vertical-align', 'top');
			this.$el.prepend(this.options.headerView.el);
			this.options.headerView.render();
		}

		// Footer
		if (this.options.footerView && this.options.footerView instanceof BaseView) {
			this.$el.append(this.options.footerView.el);
			this.options.footerView.render();
		}

		// Pull to refresh
		if (this.options.pullToRefresh) {
			let $pullToRefresh = this.cache.$pullToRefresh = $('<div>').addClass(this.options.pullToRefreshClass);
			$pullToRefresh.css({
				position: 'absolute',
				top: 0,
				left: 0,
				'margin-top': `-${this.options.pullToRefreshSize}px`,
			});

			// Check if this.options.pullToRefreshContent is a BaseView or not
			if (this.options.pullToRefreshContent instanceof BaseView) {
				$pullToRefresh.append(this.options.pullToRefreshContent.el);
				this.options.pullToRefreshContent.render();
			} else {
				$pullToRefresh.append(this.options.pullToRefreshContent);
			}

			this.$el.prepend($pullToRefresh);
		}
	}

	renderGroups() {
		if (!this.options.groupBy) return;
		let state = this.getState();
		let newGroupViews = {};

		let aListGroupView;
		_.forEach(this.groups, (aGroup, anIndex) => {
			if (!this.groupViews[aGroup.group]) {
				aListGroupView = newGroupViews[aGroup.group] = this.getListGroupViewWithOptions({
					group: aGroup.group,
					index: anIndex,
					state: state
				});
				aListGroupView.el.style.zIndex = 1;

				if (this.options.orientation === ORIENTATION_VERTICAL) {
					aListGroupView.el.style.width   = '100%';
					aListGroupView.el.style.display = 'block';
				} else {
					aListGroupView.el.style.height  = '100%';
					aListGroupView.el.style.display = 'inline-block';
				}
			} else {
				aListGroupView = newGroupViews[aGroup.group] = this.groupViews[aGroup.group];
			}

			// Ensure the order will be correct
			aListGroupView.$el.appendTo(this.cache.$groupsContainer);

			// Update parameters
			if (this.options.orientation === ORIENTATION_VERTICAL) {
				aListGroupView.el.style.height    = aGroup.size + 'px';
				aListGroupView.el.style.marginTop = aGroup.margin + 'px';
			} else {
				aListGroupView.el.style.width      = aGroup.size + 'px';
				aListGroupView.el.style.marginLeft = aGroup.margin + 'px';
			}

			// Render
			aListGroupView.render();
		});

		// Cleanup old ListGroupView
		_.forEach(this.groupViews, (aGroupView, aGroupName) => {
			if (!newGroupViews[aGroupName]) aGroupView.destroy();
		});
		this.groupViews = newGroupViews;
	}

	updateContent(item, immediate) {
		// Aggiorna il contenuto
		var model = this.collection.at(item.index);
		if (!item.view && model) {

			// if (!('getListItemViewAtIndexWithOptions' in this))
			// 	throw new Error('Method getListItemViewAtIndexWithOptions not implemented');

			item.view = this.getListItemViewAtIndexWithOptions(item.index, {
				el: item.$el.get(0),
				model: model,
				state: this.getState(),
				parentList: this,
				removeOnDestroy: true
			});

			// max 3 arguments
			this.listenTo(item.view, 'all', (eventName, arg0, arg1, arg2) => {
				this.trigger(eventName, arg0, arg1, arg2);
			});
		}

		// Render
		if (item.view) {
			item.view.setModel(model);
			item.view.render();
		}

		// Riposiziona l'elemento
		let position = this.getPositionAtIndex(item.index);
		let xy = this.getXYFromIndex(item.index);
		if (xy) {
			item.x = xy.x;
			item.y = xy.y;
		}else{
			item.x = 0;
			item.y = 0;
		}

		if ( position ){
			this.requestAnimationFrame(() => {
				// TODO: store old class in a variable and optimize remove/add class
				for (var c = 0; c < this.options.itemsPerRow; c++) {
					if (position.column !== c)
						item.$el.removeClass('col-' + c);
				}
				item.$el.addClass('col-' + position.column);
			});
		}

		translate3d(item.$el, item.x, item.y, 0, immediate);
	}

	updateRangeContents(startIndex, endIndex, forceUpdate) {
		var anItem;

		startIndex = startIndex - this.options.placeholderCache;
		if (startIndex < 0) startIndex = 0;

		endIndex = endIndex + this.options.placeholderCache;
		if (endIndex > this.getItemsCount()) endIndex = this.getItemsCount();

		for (var i = startIndex; i < endIndex; i++) {
			anItem = this.getItemAtIndex(i);
			if (anItem.index !== i || forceUpdate) {
				anItem.index = i;
				this.updateContent(anItem);
			}
		}
	}

	updateAllContents() {
		if (!this.rendered) return this;

		var anItem;

		for (var i = 0; i < this.options.placeholders; i++) {
			anItem = this.items[i];
			this.updateContent(anItem);
		}

		this.renderGroups();

		return this;
	}

	resizeContainer() {
		if (this.$el.width() > 0)  this.listWidth  = this.$el.width();
		if (this.$el.height() > 0) this.listHeight = this.$el.height();

		if (this.cache.$placeholderContainer) {
			let containerStyle = {};

			// Calculate the size of every item and get his position
			this.calculateRowPosition();

			if (this.options.orientation === ORIENTATION_VERTICAL) {
				containerStyle.height = this.containerSize + 'px';
				containerStyle.width = '100%';
			} else {
				containerStyle.height = '100%';
				containerStyle.width = this.containerSize + 'px';
			}

			this.cache.$scrollContainer.css(containerStyle);
			this.cache.$placeholderContainer.css(containerStyle);
			this.cache.$groupsContainer.css(containerStyle);
		}
		return this;
	}

	refresh() {
		this.requestAnimationFrame(() => {
			this.resizeContainer().updateAllContents();
		});
		return this;
	}

	pause() {
		overflowScrolling(this.el, false, this.options.orientation);
		return this;
	}

	resume() {
		overflowScrolling(this.el, true, this.options.orientation);
		return this;
	}

	reset() {
		this.onReset();
	}

	sync() {
		this.onSync();
	}

	getListItemViewAtIndexWithOptions(index, options) {
		return new ListItemView(options);
	}

	getListItemSizeAtIndexWithOptions(index) {
		if (this.options.orientation === ORIENTATION_VERTICAL) {
			return this.options.itemHeight;
		} else {
			return this.options.itemWidth;
		}
	}

	getListGroupViewWithOptions(options) {
		return new ListGroupView(options);
	}

	getListGroupSizeWithOptions(index) {
		if (this.options.orientation === ORIENTATION_VERTICAL) {
			return this.options.groupHeight;
		} else {
			return this.options.groupWidth;
		}
	}

	setCollection(collection) {
		let CollectionClass = Collection;
		if (this.collection) {
			this.stopListening(this.collection);
			CollectionClass = this.collection.constructor;
		}

		if (!collection) {
			this.collection = null;
			return;
		}

		if ( !(collection instanceof CollectionClass) ){
			collection = new CollectionClass(collection);
		}


		this.collection = collection;

		this.listenTo(this.collection, 'sync',   this.onSync);
		this.listenTo(this.collection, 'reset',  this.onReset);
		this.listenTo(this.collection, 'sort',   this.onSort);
		this.listenTo(this.collection, 'add',    _.debounce(_.bind(this.onAdd, this)));
		this.listenTo(this.collection, 'remove', _.debounce(_.bind(this.onRemove, this)));

		// Reset
		this.reset();
	}

	pullToRefresh() {
		if (this.isRefreshing) return;
		if (typeof this.onRefresh == 'function') {
			this.isRefreshing = true;

			this.showPullToRefresh();

			// Stops the scroll
			this.pause();<commit_msg>Bugfix for change collection type in the ListView<commit_after>import _ from "underscore";
import $ from "jquery";
import context from "context-utils";
import { Collection } from "backbone";
import BaseView from "../BaseView";
import ListItemView from "./ListItemView";
import ListGroupView from "./ListGroupView";
import getContextOptions from "../utils/getContextOptions";
import { translate3d, overflowScrolling, getVendorStyle, transition } from "../utils/style";

const TOP_DOWN               = 1;
const DOWN_TOP               = 2;
const LEFT_RIGHT             = 4;
const RIGHT_LEFT             = 8;
const ORIENTATION_VERTICAL   = 'vertical';
const ORIENTATION_HORIZONTAL = 'horizontal';

const INITIAL_POSITION_TOP  = -9000;
const INITIAL_POSITION_LEFT = -9000;

var ticking = false;
let requestTick = function requestTick(callback) {
	if (!ticking) {
		window.requestAnimationFrame(() => {
			ticking = false;
			callback();
		});
	}
	ticking = true;
}

export default class ListView extends BaseView {

	className() {
		return 'ui-list-view ' + ( _.result(this, "addClass") || '' );
	}

	constructor(options) {
		super(options);

		this.addEvents({
			'click': 'onClick',
			'scroll': 'onScroll',
			'touchstart': 'onTouchStart',
			'touchmove': 'onTouchMove',
			'touchend': 'onTouchEnd',
			'mousedown': 'onTouchStart',
			'mousemove': 'onTouchMove',
			'mouseup': 'onTouchEnd'
		});

		this.setDefaultsOptions(getContextOptions('ListView'), {
			itemClass: 'ui-list-item',
			itemHeight: 120,
			itemWidth: 120,
			itemsPerRow: 1,
			placeholders: 20, // Number of items
			placeholderCache: 2,
			orientation: ORIENTATION_VERTICAL,
			infinite: false,
			infiniteLoadingSize: 44,
			distance: 100,
			pullToRefresh: false,
			pullToRefreshWithOverscroll: false,
			pullToRefreshSize: 60,
			pullToRefreshContent: 'Pull to refresh...',
			pullToRefreshClass: 'ui-pull-to-refresh',
			touchActiveClassName: 'touch-active',
			emptyText: '',
			emptyView: null,
			delayedRender: 0,
			headerView: null, // view to print on the header
			headerSize: 0, // height of the header in a vertical listview or width in an horizontal listview
			footerView: null,
			changeDirectionPan: 50,
			groupBy: null, // enable group
			groupWidth: 50, // used on horizontal list view
			groupHeight: 30 // used on vertical list view
		});

		this.items = []; // store the placeholders
		this.positionAtIndex = {}; // store item position per index
		this.rowItems = []; // used to retrieve row information
		this.rows = []; // used to retrieve the start and end item per row
		this.groups = []; // contains group position, margin and order
		this.groupViews = {}; // cache all the group views
		this.x = 0; // current horizontal scroll
		this.y = 0; // current vertical scroll
		this.previousX = 0;
		this.previousY = 0;
		this.overscrollX = 0;
		this.overscrollY = 0;
		this.isOverscrolling = false;
		this.offsetCalculated = false;
		this.offsetX = 0;
		this.offsetY = 0;
		this.listWidth = 0;
		this.listHeight = 0;
		this.containerSize = 0;
		this.rendered = false;
		this.renderTries = 5;
		this.isLoadingMore = false;
		this.isRefreshing = false;
		this.isClicking = false;
		this.activeItem = null;
		this.touchX = 0;
		this.touchY = 0;
		this.touchStartedAtScollX = 0;
		this.touchStartedAtScollY = 0;
		this.direction = null;
		this.previousDirection = null;
		this.startChangeDirectionAt = null;
		this.headerSize = 0;

		this.setCollection(this.collection);

		this.onMove = _.bind(this.onMove, this);
		// this.resizeContainer = _.debounce(_.bind(this.resizeContainer, this), 10);
		// this.updateAllContents = _.debounce(_.bind(this.updateAllContents, this), 10);

		this.triggerScrollStart = _.bind(this.triggerScrollStart, this);
		this.triggerScrollStart = _.debounce(this.triggerScrollStart, 500, true);
	}

	//
	// Methods
	//

	render() {
		if (this.onRender) {
			if ( this.timeoutId )
				clearTimeout(this.timeoutId);

			this.timeoutId = setTimeout(() => {
				this.timeoutId = null;
				this.requestRender();
			}, this.options.delayedRender);
		}
		else {
			this.rendered = true;
		}
		return this;
	}

	requestRender() {
		if ( this.rafId )
			this.cancelAnimationFrame(this.rafId);

		this.rafId = this.requestAnimationFrame(() => {
			this.rafId = null;
			// Pass to the onRender callback if the view is already rendered
			let result = this.onRender(this.rendered);
			this.delegateEvents();
			this.rendered = (result !== false);
		});
	}

	onRender(rendered) {
		if (rendered) return this;

		// Check list dimensions in order to render correctly
		this.listWidth  = this.$el.width();
		this.listHeight = this.$el.height();
		// if (this.listWidth === 0 && this.listHeight === 0 && this.renderTries > 0) {
		// 	this.renderTries--;
		// 	this.requestRender();
		// 	return false;
		// }

		// Prepare the content
		this.$el
			.addClass('overflow-scroll')
			.addClass(this.options.orientation);

		if (this.options.orientation === ORIENTATION_HORIZONTAL) {
			this.$el.css('white-space', 'nowrap');
		}

		// For Android and WKWebView
		overflowScrolling(this.el, true, this.options.orientation);

		// Empty text
		this.cache.$emptyText = $('<div class="listview-empty empty"></div>').text(this.options.emptyText).hide();

		// Empty view
		if (this.options.emptyView) {
			this.cache.$emptyText.append(this.options.emptyView.el);
			this.options.emptyView.render();
		}

		if (!this.collection.fetching) {
			this.sync();
		}
		this.$el.append(this.cache.$emptyText);

		// Container
		let $scrollContainer = this.cache.$scrollContainer = $('<div class="listview-container">').css({
			position: 'relative',
			overflow: 'visible',
			display: 'inline-block'
		});
		let $groupsContainer = this.cache.$groupsContainer = $('<div class="listview-groups-container">').css({
			position: 'relative',
			display: 'inline-block',
			overflow: 'visible'
		});
		let $placeholderContainer = this.cache.$placeholderContainer = $('<div class="listview-placeholder-container">').css({
			position: 'absolute',
			display: 'inline-block',
			// overflow: 'hidden',
			top: 0,
			left: 0
		});
		$scrollContainer.append($groupsContainer);
		$scrollContainer.append($placeholderContainer);
		this.resizeContainer();

		this.requestAnimationFrame(() => {
			this.$el.removeClass('overflow-scroll');
			this.requestAnimationFrame(() => {
				this.$el.addClass('overflow-scroll');
			});
		});

		// Prepare placeholders
		var anElement, anItem, aPosition, aXY;
		var itemStyle = {
			position: 'absolute',
			top: INITIAL_POSITION_TOP,
			left: INITIAL_POSITION_LEFT
		};
		for (var i = 0; i < this.options.placeholders; i++) {
			anElement = $('<div>').addClass(this.options.itemClass);
			anItem = {
				index: i,
				$el: anElement
			};

			aXY = this.getXYFromIndex(i);
			if (aXY) {
				anItem.x = aXY.x;
				anItem.y = aXY.y;
			}

			if (this.options.orientation === ORIENTATION_VERTICAL) {
				itemStyle.width = (100 / this.options.itemsPerRow) + '%';
			} else {
				itemStyle.height = (100 / this.options.itemsPerRow) + '%';
			}

			anElement.css(itemStyle);

			this.updateContent(anItem, true);
			this.items.push(anItem);
			$placeholderContainer.append(anElement);
		}

		// Groups
		this.renderGroups();

		// Container
		this.$el.append($scrollContainer);

		// Header
		if (this.options.headerView && this.options.headerView instanceof BaseView) {
			this.options.headerView.$el.css('vertical-align', 'top');
			this.$el.prepend(this.options.headerView.el);
			this.options.headerView.render();
		}

		// Footer
		if (this.options.footerView && this.options.footerView instanceof BaseView) {
			this.$el.append(this.options.footerView.el);
			this.options.footerView.render();
		}

		// Pull to refresh
		if (this.options.pullToRefresh) {
			let $pullToRefresh = this.cache.$pullToRefresh = $('<div>').addClass(this.options.pullToRefreshClass);
			$pullToRefresh.css({
				position: 'absolute',
				top: 0,
				left: 0,
				'margin-top': `-${this.options.pullToRefreshSize}px`,
			});

			// Check if this.options.pullToRefreshContent is a BaseView or not
			if (this.options.pullToRefreshContent instanceof BaseView) {
				$pullToRefresh.append(this.options.pullToRefreshContent.el);
				this.options.pullToRefreshContent.render();
			} else {
				$pullToRefresh.append(this.options.pullToRefreshContent);
			}

			this.$el.prepend($pullToRefresh);
		}
	}

	renderGroups() {
		if (!this.options.groupBy) return;
		let state = this.getState();
		let newGroupViews = {};

		let aListGroupView;
		_.forEach(this.groups, (aGroup, anIndex) => {
			if (!this.groupViews[aGroup.group]) {
				aListGroupView = newGroupViews[aGroup.group] = this.getListGroupViewWithOptions({
					group: aGroup.group,
					index: anIndex,
					state: state
				});
				aListGroupView.el.style.zIndex = 1;

				if (this.options.orientation === ORIENTATION_VERTICAL) {
					aListGroupView.el.style.width   = '100%';
					aListGroupView.el.style.display = 'block';
				} else {
					aListGroupView.el.style.height  = '100%';
					aListGroupView.el.style.display = 'inline-block';
				}
			} else {
				aListGroupView = newGroupViews[aGroup.group] = this.groupViews[aGroup.group];
			}

			// Ensure the order will be correct
			aListGroupView.$el.appendTo(this.cache.$groupsContainer);

			// Update parameters
			if (this.options.orientation === ORIENTATION_VERTICAL) {
				aListGroupView.el.style.height    = aGroup.size + 'px';
				aListGroupView.el.style.marginTop = aGroup.margin + 'px';
			} else {
				aListGroupView.el.style.width      = aGroup.size + 'px';
				aListGroupView.el.style.marginLeft = aGroup.margin + 'px';
			}

			// Render
			aListGroupView.render();
		});

		// Cleanup old ListGroupView
		_.forEach(this.groupViews, (aGroupView, aGroupName) => {
			if (!newGroupViews[aGroupName]) aGroupView.destroy();
		});
		this.groupViews = newGroupViews;
	}

	updateContent(item, immediate) {
		// Aggiorna il contenuto
		var model = this.collection.at(item.index);
		if (!item.view && model) {

			// if (!('getListItemViewAtIndexWithOptions' in this))
			// 	throw new Error('Method getListItemViewAtIndexWithOptions not implemented');

			item.view = this.getListItemViewAtIndexWithOptions(item.index, {
				el: item.$el.get(0),
				model: model,
				state: this.getState(),
				parentList: this,
				removeOnDestroy: true
			});

			// max 3 arguments
			this.listenTo(item.view, 'all', (eventName, arg0, arg1, arg2) => {
				this.trigger(eventName, arg0, arg1, arg2);
			});
		}

		// Render
		if (item.view) {
			item.view.setModel(model);
			item.view.render();
		}

		// Riposiziona l'elemento
		let position = this.getPositionAtIndex(item.index);
		let xy = this.getXYFromIndex(item.index);
		if (xy) {
			item.x = xy.x;
			item.y = xy.y;
		}else{
			item.x = 0;
			item.y = 0;
		}

		if ( position ){
			this.requestAnimationFrame(() => {
				// TODO: store old class in a variable and optimize remove/add class
				for (var c = 0; c < this.options.itemsPerRow; c++) {
					if (position.column !== c)
						item.$el.removeClass('col-' + c);
				}
				item.$el.addClass('col-' + position.column);
			});
		}

		translate3d(item.$el, item.x, item.y, 0, immediate);
	}

	updateRangeContents(startIndex, endIndex, forceUpdate) {
		var anItem;

		startIndex = startIndex - this.options.placeholderCache;
		if (startIndex < 0) startIndex = 0;

		endIndex = endIndex + this.options.placeholderCache;
		if (endIndex > this.getItemsCount()) endIndex = this.getItemsCount();

		for (var i = startIndex; i < endIndex; i++) {
			anItem = this.getItemAtIndex(i);
			if (anItem.index !== i || forceUpdate) {
				anItem.index = i;
				this.updateContent(anItem);
			}
		}
	}

	updateAllContents() {
		if (!this.rendered) return this;

		var anItem;

		for (var i = 0; i < this.options.placeholders; i++) {
			anItem = this.items[i];
			this.updateContent(anItem);
		}

		this.renderGroups();

		return this;
	}

	resizeContainer() {
		if (this.$el.width() > 0)  this.listWidth  = this.$el.width();
		if (this.$el.height() > 0) this.listHeight = this.$el.height();

		if (this.cache.$placeholderContainer) {
			let containerStyle = {};

			// Calculate the size of every item and get his position
			this.calculateRowPosition();

			if (this.options.orientation === ORIENTATION_VERTICAL) {
				containerStyle.height = this.containerSize + 'px';
				containerStyle.width = '100%';
			} else {
				containerStyle.height = '100%';
				containerStyle.width = this.containerSize + 'px';
			}

			this.cache.$scrollContainer.css(containerStyle);
			this.cache.$placeholderContainer.css(containerStyle);
			this.cache.$groupsContainer.css(containerStyle);
		}
		return this;
	}

	refresh() {
		this.requestAnimationFrame(() => {
			this.resizeContainer().updateAllContents();
		});
		return this;
	}

	pause() {
		overflowScrolling(this.el, false, this.options.orientation);
		return this;
	}

	resume() {
		overflowScrolling(this.el, true, this.options.orientation);
		return this;
	}

	reset() {
		this.onReset();
	}

	sync() {
		this.onSync();
	}

	getListItemViewAtIndexWithOptions(index, options) {
		return new ListItemView(options);
	}

	getListItemSizeAtIndexWithOptions(index) {
		if (this.options.orientation === ORIENTATION_VERTICAL) {
			return this.options.itemHeight;
		} else {
			return this.options.itemWidth;
		}
	}

	getListGroupViewWithOptions(options) {
		return new ListGroupView(options);
	}

	getListGroupSizeWithOptions(index) {
		if (this.options.orientation === ORIENTATION_VERTICAL) {
			return this.options.groupHeight;
		} else {
			return this.options.groupWidth;
		}
	}

	setCollection(collection) {
		let CollectionClass = Collection;
		if (this.collection) {
			this.stopListening(this.collection);
			CollectionClass = this.collection.constructor;
		}

		if (!collection) {
			this.collection = null;
			return;
		}

		if ( !(collection instanceof Collection) ){
			collection = new CollectionClass(collection);
		}


		this.collection = collection;

		this.listenTo(this.collection, 'sync',   this.onSync);
		this.listenTo(this.collection, 'reset',  this.onReset);
		this.listenTo(this.collection, 'sort',   this.onSort);
		this.listenTo(this.collection, 'add',    _.debounce(_.bind(this.onAdd, this)));
		this.listenTo(this.collection, 'remove', _.debounce(_.bind(this.onRemove, this)));

		// Reset
		this.reset();
	}

	pullToRefresh() {
		if (this.isRefreshing) return;
		if (typeof this.onRefresh == 'function') {
			this.isRefreshing = true;

			this.showPullToRefresh();

			// Stops the scroll
			this.pause();
{'fraction_non_alphanumeric': 0.14520952937536527, 'fraction_numerical': 0.007356732785589054, 'mean_word_length': 3.510077519379845, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 45, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2941202', 'n_tokens_mistral': 11192, 'n_tokens_neox': 9456, 'n_words': 1936}