UNPKG

@progress/kendo-ui

Version:

This package is part of the [Kendo UI for jQuery](http://www.telerik.com/kendo-ui) suite.

1,498 lines (1,204 loc) 62.6 kB
module.exports = /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) /******/ return installedModules[moduleId].exports; /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ exports: {}, /******/ id: moduleId, /******/ loaded: false /******/ }; /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ // Flag the module as loaded /******/ module.loaded = true; /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ // Load entry module and return exports /******/ return __webpack_require__(0); /******/ }) /************************************************************************/ /******/ ({ /***/ 0: /***/ (function(module, exports, __webpack_require__) { module.exports = __webpack_require__(1373); /***/ }), /***/ 3: /***/ (function(module, exports) { module.exports = function() { throw new Error("define cannot be used indirect"); }; /***/ }), /***/ 1005: /***/ (function(module, exports) { module.exports = require("jquery"); /***/ }), /***/ 1015: /***/ (function(module, exports) { module.exports = require("./kendo.data"); /***/ }), /***/ 1373: /***/ (function(module, exports, __webpack_require__) { var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(jQuery) {(function(f, define){ !(__WEBPACK_AMD_DEFINE_ARRAY__ = [ __webpack_require__(1015) ], __WEBPACK_AMD_DEFINE_FACTORY__ = (f), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); })(function(){ var __meta__ = { // jshint ignore:line id: "virtuallist", name: "VirtualList", category: "framework", depends: [ "data" ], hidden: true }; (function($, undefined) { var kendo = window.kendo, ui = kendo.ui, Widget = ui.Widget, DataBoundWidget = ui.DataBoundWidget, proxy = $.proxy, percentageUnitsRegex = /^\d+(\.\d+)?%$/i, WRAPPER = "k-virtual-wrap", VIRTUALLIST = "k-virtual-list", CONTENT = "k-virtual-content", LIST = "k-list", HEADER = "k-group-header", VIRTUALITEM = "k-virtual-item", ITEM = "k-item", HEIGHTCONTAINER = "k-height-container", GROUPITEM = "k-group", SELECTED = "k-state-selected", FOCUSED = "k-state-focused", HOVER = "k-state-hover", CHANGE = "change", CLICK = "click", LISTBOUND = "listBound", ITEMCHANGE = "itemChange", ACTIVATE = "activate", DEACTIVATE = "deactivate", VIRTUAL_LIST_NS = ".VirtualList"; function lastFrom(array) { return array[array.length - 1]; } function toArray(value) { return value instanceof Array ? value : [value]; } function isPrimitive(dataItem) { return typeof dataItem === "string" || typeof dataItem === "number" || typeof dataItem === "boolean"; } function getItemCount(screenHeight, listScreens, itemHeight) { return Math.ceil(screenHeight * listScreens / itemHeight); } function appendChild(parent, className, tagName) { var element = document.createElement(tagName || "div"); if (className) { element.className = className; } parent.appendChild(element); return element; } function getDefaultItemHeight() { var mockList = $('<div class="k-popup"><ul class="k-list"><li class="k-item"><li></ul></div>'), lineHeight; mockList.css({ position: "absolute", left: "-200000px", visibility: "hidden" }); mockList.appendTo(document.body); lineHeight = parseFloat(kendo.getComputedStyles(mockList.find(".k-item")[0], ["line-height"])["line-height"]); mockList.remove(); return lineHeight; } function bufferSizes(screenHeight, listScreens, opposite) { //in pixels return { down: screenHeight * opposite, up: screenHeight * (listScreens - 1 - opposite) }; } function listValidator(options, screenHeight) { var downThreshold = (options.listScreens - 1 - options.threshold) * screenHeight; var upThreshold = options.threshold * screenHeight; return function(list, scrollTop, lastScrollTop) { if (scrollTop > lastScrollTop) { return scrollTop - list.top < downThreshold; } else { return list.top === 0 || scrollTop - list.top > upThreshold; } }; } function scrollCallback(element, callback) { return function(force) { return callback(element.scrollTop, force); }; } function syncList(reorder) { return function(list, force) { reorder(list.items, list.index, force); return list; }; } function position(element, y) { if (kendo.support.browser.msie && kendo.support.browser.version < 10) { element.style.top = y + "px"; } else { element.style.webkitTransform = 'translateY(' + y + "px)"; element.style.transform = 'translateY(' + y + "px)"; } } function map2(callback, templates) { return function(arr1, arr2) { for (var i = 0, len = arr1.length; i < len; i++) { callback(arr1[i], arr2[i], templates); if (arr2[i].item) { this.trigger(ITEMCHANGE, { item: $(arr1[i]), data: arr2[i].item, ns: kendo.ui }); } } }; } function reshift(items, diff) { var range; if (diff > 0) { // down range = items.splice(0, diff); items.push.apply(items, range); } else { // up range = items.splice(diff, -diff); items.unshift.apply(items, range); } return range; } function render(element, data, templates) { var itemTemplate = templates.template; element = $(element); if (!data.item) { itemTemplate = templates.placeholderTemplate; } if (data.index === 0 && this.header && data.group) { this.header.html(templates.fixedGroupTemplate(data.group)); } this.angular("cleanup", function() { return { elements: [ element ]}; }); element .attr("data-uid", data.item ? data.item.uid : "") .attr("data-offset-index", data.index); if (this.options.columns && this.options.columns.length && data.item) { element.html(renderColumns(this.options, data.item, templates)); } else { element.html(itemTemplate(data.item || {})); } element.toggleClass(FOCUSED, data.current); element.toggleClass(SELECTED, data.selected); element.toggleClass("k-first", data.newGroup); element.toggleClass("k-last", data.isLastGroupedItem); element.toggleClass("k-loading-item", !data.item); if (data.index !== 0 && data.newGroup) { $("<div class=" + GROUPITEM + "></div>") .appendTo(element) .html(templates.groupTemplate(data.group)); } if (data.top !== undefined) { position(element[0], data.top); } this.angular("compile", function() { return { elements: [ element ], data: [ { dataItem: data.item, group: data.group, newGroup: data.newGroup } ]}; }); } function renderColumns(options, dataItem, templates) { var item = ""; for (var i = 0; i < options.columns.length; i++) { var currentWidth = options.columns[i].width; var currentWidthInt = parseInt(currentWidth, 10); var widthStyle = ''; if(currentWidth){ widthStyle += "style='width:"; widthStyle += currentWidthInt; widthStyle += percentageUnitsRegex.test(currentWidth) ? "%" : "px"; widthStyle += ";'"; } item += "<span class='k-cell' " + widthStyle + ">"; item += templates["column"+ i](dataItem); item += "</span>"; } return item; } function mapChangedItems(selected, itemsToMatch) { var itemsLength = itemsToMatch.length; var selectedLength = selected.length; var dataItem; var found; var i, j; var changed = []; var unchanged = []; if (selectedLength) { for (i = 0; i < selectedLength; i++) { dataItem = selected[i]; found = false; for (j = 0; j < itemsLength; j++) { if (dataItem === itemsToMatch[j]) { found = true; changed.push({ index: i, item: dataItem }); break; } } if (!found) { unchanged.push(dataItem); } } } return { changed: changed, unchanged: unchanged }; } function isActivePromise(promise) { return promise && promise.state() !== "resolved"; } var VirtualList = DataBoundWidget.extend({ init: function(element, options) { var that = this; that.bound(false); that._fetching = false; Widget.fn.init.call(that, element, options); if (!that.options.itemHeight) { that.options.itemHeight = getDefaultItemHeight(); } options = that.options; that.element.addClass(LIST + " " + VIRTUALLIST).attr("role", "listbox"); that.content = that.element.wrap("<div unselectable='on' class='" + CONTENT + "'></div>").parent(); that.wrapper = that.content.wrap("<div class='" + WRAPPER + "'></div>").parent(); that.header = that.content.before("<div class='" + HEADER + "'></div>").prev(); if (options.columns && options.columns.length) { that.element.removeClass(LIST); } that.element.on("mouseenter" + VIRTUAL_LIST_NS, "li:not(.k-loading-item)", function() { $(this).addClass(HOVER); }) .on("mouseleave" + VIRTUAL_LIST_NS, "li", function() { $(this).removeClass(HOVER); }); that._values = toArray(that.options.value); that._selectedDataItems = []; that._selectedIndexes = []; that._rangesList = {}; that._promisesList = []; that._optionID = kendo.guid(); that._templates(); that.setDataSource(options.dataSource); that.content.on("scroll" + VIRTUAL_LIST_NS, kendo.throttle(function() { that._renderItems(); that._triggerListBound(); }, options.delay)); that._selectable(); }, options: { name: "VirtualList", autoBind: true, delay: 100, height: null, listScreens: 4, threshold: 0.5, itemHeight: null, oppositeBuffer: 1, type: "flat", selectable: false, value: [], dataValueField: null, template: "#:data#", placeholderTemplate: "loading...", groupTemplate: "#:data#", fixedGroupTemplate: "#:data#", mapValueTo: "index", valueMapper: null }, events: [ CHANGE, CLICK, LISTBOUND, ITEMCHANGE, ACTIVATE, DEACTIVATE ], setOptions: function(options) { Widget.fn.setOptions.call(this, options); if (this._selectProxy && this.options.selectable === false) { this.element.off(CLICK, "." + VIRTUALITEM, this._selectProxy); } else if (!this._selectProxy && this.options.selectable) { this._selectable(); } this._templates(); this.refresh(); }, items: function() { return $(this._items); }, destroy: function() { this.wrapper.off(VIRTUAL_LIST_NS); this.dataSource.unbind(CHANGE, this._refreshHandler); Widget.fn.destroy.call(this); }, setDataSource: function(source) { var that = this; var dataSource = source || {}; var value; dataSource = $.isArray(dataSource) ? {data: dataSource} : dataSource; dataSource = kendo.data.DataSource.create(dataSource); if (that.dataSource) { that.dataSource.unbind(CHANGE, that._refreshHandler); that._clean(); that.bound(false); that._deferValueSet = true; value = that.value(); that.value([]); that.mute(function() { that.value(value); }); } else { that._refreshHandler = $.proxy(that.refresh, that); } that.dataSource = dataSource.bind(CHANGE, that._refreshHandler); that.setDSFilter(dataSource.filter()); if (dataSource.view().length !== 0) { that.refresh(); } else if (that.options.autoBind) { dataSource.fetch(); } }, skip: function() { return this.dataSource.currentRangeStart(); }, _triggerListBound: function () { var that = this; var skip = that.skip(); if (that.bound() && !that._selectingValue && that._skip !== skip) { that._skip = skip; that.trigger(LISTBOUND); } }, _getValues: function(dataItems) { var getter = this._valueGetter; return $.map(dataItems, function(dataItem) { return getter(dataItem); }); }, _highlightSelectedItems: function () { for (var i = 0; i < this._selectedDataItems.length; i++) { var item = this._getElementByDataItem(this._selectedDataItems[i]); if(item.length){ item.addClass(SELECTED); } } }, refresh: function(e) { var that = this; var action = e && e.action; var isItemChange = action === "itemchange"; var filtered = this.isFiltered(); var result; if (that._mute) { return; } that._deferValueSet = false; if (!that._fetching) { if (filtered) { that.focus(0); } that._createList(); if (!action && that._values.length && !filtered && !that.options.skipUpdateOnBind && !that._emptySearch) { that._selectingValue = true; that.bound(true); that.value(that._values, true).done(function () { that._selectingValue = false; that._triggerListBound(); }); } else { that.bound(true); that._highlightSelectedItems(); that._triggerListBound(); } } else { if (that._renderItems) { that._renderItems(true); } that._triggerListBound(); } if (isItemChange || action === "remove") { result = mapChangedItems(that._selectedDataItems, e.items); if (result.changed.length) { if (isItemChange) { that.trigger("selectedItemChange", { items: result.changed }); } else { that.value(that._getValues(result.unchanged)); } } } that._fetching = false; }, removeAt: function(position) { this._selectedIndexes.splice(position, 1); this._values.splice(position, 1); return { position: position, dataItem: this._selectedDataItems.splice(position, 1)[0] }; }, setValue: function(value) { this._values = toArray(value); }, value: function(value, _forcePrefetch) { var that = this; if (value === undefined) { return that._values.slice(); } if (value === null) { value = []; } value = toArray(value); if (!that._valueDeferred || that._valueDeferred.state() === "resolved") { that._valueDeferred = $.Deferred(); } var shouldClear = that.options.selectable === "multiple" && that.select().length && value.length; if (shouldClear || !value.length) { that.select(-1); } that._values = value; if ((that.bound() && !that._mute && !that._deferValueSet) || _forcePrefetch) { that._prefetchByValue(value); } return that._valueDeferred; }, _checkValuesOrder: function (value) { if (this._removedAddedIndexes && this._removedAddedIndexes.length === value.length) { var newValue = this._removedAddedIndexes.slice(); this._removedAddedIndexes = null; return newValue; } return value; }, _prefetchByValue: function(value) { var that = this, dataView = that._dataView, valueGetter = that._valueGetter, mapValueTo = that.options.mapValueTo, item, match = false, forSelection = []; //try to find the items in the loaded data for (var i = 0; i < value.length; i++) { for (var idx = 0; idx < dataView.length; idx++) { item = dataView[idx].item; if (item) { match = isPrimitive(item) ? value[i] === item : value[i] === valueGetter(item); if (match) { forSelection.push(dataView[idx].index); } } } } if (forSelection.length === value.length) { that._values = []; that.select(forSelection); return; } //prefetch the items if (typeof that.options.valueMapper === "function") { that.options.valueMapper({ value: (this.options.selectable === "multiple") ? value : value[0], success: function(response) { if (mapValueTo === "index") { that.mapValueToIndex(response); } else if (mapValueTo === "dataItem") { that.mapValueToDataItem(response); } } }); } else { if (!that.value()[0]) { that.select([-1]); } else { that._selectingValue = false; that._triggerListBound(); } } }, mapValueToIndex: function(indexes) { if (indexes === undefined || indexes === -1 || indexes === null) { indexes = []; } else { indexes = toArray(indexes); } if (!indexes.length) { indexes = [-1]; } else { var removed = this._deselect([]).removed; if (removed.length) { this._triggerChange(removed, []); } } this.select(indexes); }, mapValueToDataItem: function(dataItems) { var removed, added; if (dataItems === undefined || dataItems === null) { dataItems = []; } else { dataItems = toArray(dataItems); } if (!dataItems.length) { this.select([-1]); } else { removed = $.map(this._selectedDataItems, function(item, index) { return { index: index, dataItem: item }; }); added = $.map(dataItems, function(item, index) { return { index: index, dataItem: item }; }); this._selectedDataItems = dataItems; this._selectedIndexes = []; for (var i = 0; i < this._selectedDataItems.length; i++) { var item = this._getElementByDataItem(this._selectedDataItems[i]); this._selectedIndexes.push(this._getIndecies(item)[0]); item.addClass(SELECTED); } this._triggerChange(removed, added); if (this._valueDeferred) { this._valueDeferred.resolve(); } } }, deferredRange: function(index) { var dataSource = this.dataSource; var take = this.itemCount; var ranges = this._rangesList; var result = $.Deferred(); var defs = []; var low = Math.floor(index / take) * take; var high = Math.ceil(index / take) * take; var pages = high === low ? [ high ] : [ low, high ]; $.each(pages, function(_, skip) { var end = skip + take; var existingRange = ranges[skip]; var deferred; if (!existingRange || (existingRange.end !== end)) { deferred = $.Deferred(); ranges[skip] = { end: end, deferred: deferred }; dataSource._multiplePrefetch(skip, take, function() { deferred.resolve(); }); } else { deferred = existingRange.deferred; } defs.push(deferred); }); $.when.apply($, defs).then(function() { result.resolve(); }); return result; }, prefetch: function(indexes) { var that = this, take = this.itemCount, isEmptyList = !that._promisesList.length; if (!isActivePromise(that._activeDeferred)) { that._activeDeferred = $.Deferred(); that._promisesList = []; } $.each(indexes, function(_, index) { that._promisesList.push(that.deferredRange(that._getSkip(index, take))); }); if (isEmptyList) { $.when.apply($, that._promisesList).done(function() { that._promisesList = []; that._activeDeferred.resolve(); }); } return that._activeDeferred; }, _findDataItem: function(view, index) { var group; //find in grouped view if (this.options.type === "group") { for (var i = 0; i < view.length; i++) { group = view[i].items; if (group.length <= index) { index = index - group.length; } else { return group[index]; } } } //find in flat view return view[index]; }, _getRange: function(skip, take) { return this.dataSource._findRange(skip, Math.min(skip + take, this.dataSource.total())); }, dataItemByIndex: function(index) { var that = this; var take = that.itemCount; var skip = that._getSkip(index, take); var view = this._getRange(skip, take); //should not return item if data is not loaded if (!that._getRange(skip, take).length) { return null; } if (that.options.type === "group") { kendo.ui.progress($(that.wrapper), true); that.mute(function() { that.dataSource.range(skip, take, function () { kendo.ui.progress($(that.wrapper), false); }); view = that.dataSource.view(); }); } return that._findDataItem(view, [index - skip]); }, selectedDataItems: function() { return this._selectedDataItems.slice(); }, scrollWith: function(value) { this.content.scrollTop(this.content.scrollTop() + value); }, scrollTo: function(y) { this.content.scrollTop(y); //works only if the element is visible }, scrollToIndex: function(index) { this.scrollTo(index * this.options.itemHeight); }, focus: function(candidate) { var element, index, data, current, itemHeight = this.options.itemHeight, id = this._optionID, triggerEvent = true; if (candidate === undefined) { current = this.element.find("." + FOCUSED); return current.length ? current : null; } if (typeof candidate === "function") { data = this.dataSource.flatView(); for (var idx = 0; idx < data.length; idx++) { if (candidate(data[idx])) { candidate = idx; break; } } } if (candidate instanceof Array) { candidate = lastFrom(candidate); } if (isNaN(candidate)) { element = $(candidate); index = parseInt($(element).attr("data-offset-index"), 10); } else { index = candidate; element = this._getElementByIndex(index); } if (index === -1) { this.element.find("." + FOCUSED).removeClass(FOCUSED); this._focusedIndex = undefined; return; } if (element.length) { /*focus rendered item*/ if (element.hasClass(FOCUSED)) { triggerEvent = false; } if (this._focusedIndex !== undefined) { current = this._getElementByIndex(this._focusedIndex); current .removeClass(FOCUSED) .removeAttr("id"); if (triggerEvent) { this.trigger(DEACTIVATE); } } this._focusedIndex = index; element .addClass(FOCUSED) .attr("id", id); var position = this._getElementLocation(index); if (position === "top") { this.scrollTo(index * itemHeight); } else if (position === "bottom") { this.scrollTo((index * itemHeight + itemHeight) - this._screenHeight); } else if (position === "outScreen") { this.scrollTo(index * itemHeight); } if (triggerEvent) { this.trigger(ACTIVATE); } } else { /*focus non rendered item*/ this._focusedIndex = index; this.items().removeClass(FOCUSED); this.scrollToIndex(index); } }, focusIndex: function() { return this._focusedIndex; }, focusFirst: function() { this.scrollTo(0); this.focus(0); }, focusLast: function() { var lastIndex = this.dataSource.total(); this.scrollTo(this.heightContainer.offsetHeight); this.focus(lastIndex - 1); }, focusPrev: function() { var index = this._focusedIndex; var current; if (!isNaN(index) && index > 0) { index -= 1; this.focus(index); current = this.focus(); if (current && current.hasClass("k-loading-item")) { index += 1; this.focus(index); } return index; } else { index = this.dataSource.total() - 1; this.focus(index); return index; } }, focusNext: function() { var index = this._focusedIndex; var lastIndex = this.dataSource.total() - 1; var current; if (!isNaN(index) && index < lastIndex) { index += 1; this.focus(index); current = this.focus(); if (current && current.hasClass("k-loading-item")) { index -= 1; this.focus(index); } return index; } else { index = 0; this.focus(index); return index; } }, _triggerChange: function(removed, added) { removed = removed || []; added = added || []; if (removed.length || added.length) { this.trigger(CHANGE, { removed: removed, added: added }); } }, select: function(candidate) { var that = this, indices, initialIndices, singleSelection = that.options.selectable !== "multiple", prefetchStarted = isActivePromise(that._activeDeferred), filtered = this.isFiltered(), isAlreadySelected, deferred, result, removed = []; if (candidate === undefined) { return that._selectedIndexes.slice(); } if (!that._selectDeferred || that._selectDeferred.state() === "resolved") { that._selectDeferred = $.Deferred(); } indices = that._getIndecies(candidate); isAlreadySelected = singleSelection && !filtered && lastFrom(indices) === lastFrom(this._selectedIndexes); removed = that._deselectCurrentValues(indices); if (removed.length || !indices.length || isAlreadySelected) { that._triggerChange(removed); if (that._valueDeferred) { that._valueDeferred.resolve().promise(); } return that._selectDeferred.resolve().promise(); } if (indices.length === 1 && indices[0] === -1) { indices = []; } initialIndices = indices; result = that._deselect(indices); removed = result.removed; indices = result.indices; if (singleSelection) { prefetchStarted = false; if (indices.length) { indices = [lastFrom(indices)]; } } var done = function() { var added = that._select(indices); if (initialIndices.length === indices.length || singleSelection) { that.focus(indices); } that._triggerChange(removed, added); if (that._valueDeferred) { that._valueDeferred.resolve(); } that._selectDeferred.resolve(); }; deferred = that.prefetch(indices); if (!prefetchStarted) { if (deferred) { deferred.done(done); } else { done(); } } return that._selectDeferred.promise(); }, bound: function(bound) { if (bound === undefined) { return this._listCreated; } this._listCreated = bound; }, mute: function(callback) { this._mute = true; proxy(callback(), this); this._mute = false; }, setDSFilter: function(filter) { this._lastDSFilter = $.extend({}, filter); }, isFiltered: function() { if (!this._lastDSFilter) { this.setDSFilter(this.dataSource.filter()); } return !kendo.data.Query.compareFilters(this.dataSource.filter(), this._lastDSFilter); }, skipUpdate: $.noop, _getElementByIndex: function(index) { return this.items().filter(function(idx, element) { return index === parseInt($(element).attr("data-offset-index"), 10); }); }, _getElementByDataItem: function(dataItem) { var dataView = this._dataView, valueGetter = this._valueGetter, element, match; for (var i = 0; i < dataView.length; i++) { match = dataView[i].item && isPrimitive(dataView[i].item) ? dataView[i].item === dataItem : dataView[i].item && dataItem && valueGetter(dataView[i].item) == valueGetter(dataItem); if (match) { element = dataView[i]; break; } } return element ? this._getElementByIndex(element.index) : $(); }, _clean: function() { this.result = undefined; this._lastScrollTop = undefined; this._skip = undefined; $(this.heightContainer).remove(); this.heightContainer = undefined; this.element.empty(); }, _height: function() { var hasData = !!this.dataSource.view().length, height = this.options.height, itemHeight = this.options.itemHeight, total = this.dataSource.total(); if (!hasData) { height = 0; } else if (height/itemHeight > total) { height = total * itemHeight; } return height; }, setScreenHeight: function() { var height = this._height(); this.content.height(height); this._screenHeight = height; }, screenHeight: function() { return this._screenHeight; }, _getElementLocation: function(index) { var scrollTop = this.content.scrollTop(), screenHeight = this._screenHeight, itemHeight = this.options.itemHeight, yPosition = index * itemHeight, yDownPostion = yPosition + itemHeight, screenEnd = scrollTop + screenHeight, position; if (yPosition === (scrollTop - itemHeight) || (yDownPostion > scrollTop && yPosition < scrollTop)) { position = "top"; } else if (yPosition === screenEnd || (yPosition < screenEnd && screenEnd < yDownPostion)) { position = "bottom"; } else if ((yPosition >= scrollTop) && (yPosition <= scrollTop + (screenHeight - itemHeight))) { position = "inScreen"; } else { position = "outScreen"; } return position; }, _templates: function() { var options = this.options; var templates = { template: options.template, placeholderTemplate: options.placeholderTemplate, groupTemplate: options.groupTemplate, fixedGroupTemplate: options.fixedGroupTemplate }; if (options.columns) { for (var i = 0; i < options.columns.length; i++) { var currentColumn = options.columns[i]; var templateText = currentColumn.field ? currentColumn.field.toString(): "text"; templates["column"+ i] = currentColumn.template || "#: " + templateText + "#"; } } for (var key in templates) { if (typeof templates[key] !== "function") { templates[key] = kendo.template(templates[key] || ""); } } this.templates = templates; }, _generateItems: function(element, count) { var items = [], item, itemHeight = this.options.itemHeight + "px"; while(count-- > 0) { item = document.createElement("li"); item.tabIndex = -1; item.className = VIRTUALITEM + " " + ITEM; item.setAttribute("role", "option"); item.style.height = itemHeight; item.style.minHeight = itemHeight; element.appendChild(item); items.push(item); } return items; }, _saveInitialRanges: function() { var ranges = this.dataSource._ranges; var deferred = $.Deferred(); deferred.resolve(); this._rangesList = {}; for (var i = 0; i < ranges.length; i++) { this._rangesList[ranges[i].start] = { end: ranges[i].end, deferred: deferred }; } }, _createList: function() { var that = this, content = that.content.get(0), options = that.options, dataSource = that.dataSource; if (that.bound()) { that._clean(); } that._saveInitialRanges(); that._buildValueGetter(); that.setScreenHeight(); that.itemCount = getItemCount(that._screenHeight, options.listScreens, options.itemHeight); if (that.itemCount > dataSource.total()) { that.itemCount = dataSource.total(); } that._items = that._generateItems(that.element[0], that.itemCount); that._setHeight(options.itemHeight * dataSource.total()); that.options.type = (dataSource.group() || []).length ? "group" : "flat"; if (that.options.type === "flat") { that.header.hide(); } else { that.header.show(); } that.getter = that._getter(function() { that._renderItems(true); }); that._onScroll = function(scrollTop, force) { var getList = that._listItems(that.getter); return that._fixedHeader(scrollTop, getList(scrollTop, force)); }; that._renderItems = that._whenChanged( scrollCallback(content, that._onScroll), syncList(that._reorderList(that._items, $.proxy(render, that))) ); that._renderItems(); that._calculateGroupPadding(that._screenHeight); that._calculateColumnsHeaderPadding(); }, _setHeight: function(height) { var currentHeight, heightContainer = this.heightContainer; if (!heightContainer) { heightContainer = this.heightContainer = appendChild(this.content[0], HEIGHTCONTAINER); } else { currentHeight = heightContainer.offsetHeight; } if (height !== currentHeight) { heightContainer.innerHTML = ""; while (height > 0) { var padHeight = Math.min(height, 250000); //IE workaround, should not create elements with height larger than 250000px appendChild(heightContainer).style.height = padHeight + "px"; height -= padHeight; } } }, _getter: function() { var lastRequestedRange = null, dataSource = this.dataSource, lastRangeStart = dataSource.skip(), type = this.options.type, pageSize = this.itemCount, flatGroups = {}; if (dataSource.pageSize() < pageSize) { this.mute(function() { dataSource.pageSize(pageSize); }); } return function(index, rangeStart) { var that = this; if (!dataSource.inRange(rangeStart, pageSize)) { if (lastRequestedRange !== rangeStart) { lastRequestedRange = rangeStart; lastRangeStart = rangeStart; if (that._getterDeferred) { that._getterDeferred.reject(); } that._getterDeferred = that.deferredRange(rangeStart); that._getterDeferred.then(function() { var firstItemIndex = that._indexConstraint(that.content[0].scrollTop); that._getterDeferred = null; if (rangeStart <= firstItemIndex && firstItemIndex <= (rangeStart + pageSize)) { that._fetching = true; dataSource.range(rangeStart, pageSize); } }); } return null; } else { if (lastRangeStart !== rangeStart) { this.mute(function() { dataSource.range(rangeStart, pageSize); lastRangeStart = rangeStart; }); } var result; if (type === "group") { //grouped list if (!flatGroups[rangeStart]) { var flatGroup = flatGroups[rangeStart] = []; var groups = dataSource.view(); for (var i = 0, len = groups.length; i < len; i++) { var group = groups[i]; for (var j = 0, groupLength = group.items.length; j < groupLength; j++) { flatGroup.push({ item: group.items[j], group: group.value }); } } } result = flatGroups[rangeStart][index - rangeStart]; } else { //flat list result = dataSource.view()[index - rangeStart]; } return result; } }; }, _fixedHeader: function(scrollTop, list) { var group = this.currentVisibleGroup, itemHeight = this.options.itemHeight, firstVisibleDataItemIndex = Math.floor((scrollTop - list.top) / itemHeight), firstVisibleDataItem = list.items[firstVisibleDataItemIndex]; if (firstVisibleDataItem && firstVisibleDataItem.item) { var firstVisibleGroup = firstVisibleDataItem.group; if (firstVisibleGroup !== group) { var fixedGroupText = firstVisibleGroup || ""; this.header.html(this.templates.fixedGroupTemplate(fixedGroupText)); this.currentVisibleGroup = firstVisibleGroup; } } return list; }, _itemMapper: function(item, index, value) { var listType = this.options.type, itemHeight = this.options.itemHeight, currentIndex = this._focusedIndex, selected = false, current = false, newGroup = false, group = null, match = false, valueGetter = this._valueGetter; if (listType === "group") { if (item) { newGroup = index === 0 || (this._currentGroup !== false && this._currentGroup !== item.group); this._currentGroup = item.group; } group = item ? item.group : null; item = item ? item.item : null; } if (this.options.mapValueTo === "dataItem" && this._selectedDataItems.length && item) { for (var i = 0; i < this._selectedDataItems.length; i++) { match = valueGetter(this._selectedDataItems[i]) === valueGetter(item); if (match) { selected = true; break; } } } else if (!this.isFiltered() && value.length && item) { for (var j = 0; j < value.length; j++) { match = isPrimitive(item) ? value[j] === item : value[j] === valueGetter(item); if (match) { value.splice(j , 1); selected = true; break; } } } if (currentIndex === index) { current = true; } return { item: item ? item : null, group: group, newGroup: newGroup, selected: selected, current: current, index: index, top: index * itemHeight }; }, _range: function(index) { var itemCount = this.itemCount, value = this._values.slice(), items = [], item; this._view = {}; this._currentGroup = false; for (var i = index, length = index + itemCount; i < length; i++) { item = this._itemMapper(this.getter(i, index), i, value); if(items[items.length - 1]){ items[items.length - 1].isLastGroupedItem = item.newGroup; } items.push(item); this._view[item.index] = item; } this._dataView = items; return items; }, _getDataItemsCollection: function(scrollTop, lastScrollTop) { var items = this._range(this._listIndex(scrollTop, lastScrollTop)); return { index: items.length ? items[0].index : 0, top: items.length ? items[0].top : 0, items: items }; }, _listItems: function() { var screenHeight = this._screenHeight, options = this.options; var theValidator = listValidator(options, screenHeight); return $.proxy(function(value, force) {