@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
JavaScript
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) {