guy 8 years ago
parent
commit
afd1cdbbcb
  1. 32
      bi/base.css
  2. 502
      bi/base.js
  3. 2
      bi/core.js
  4. 2
      demo/js/core/abstract/demo.collection_view.js
  5. 4
      demo/js/core/abstract/demo.grid_view.js
  6. 9
      demo/js/core/abstract/demo.virtual_list.js
  7. 32
      docs/base.css
  8. 502
      docs/base.js
  9. 2
      docs/core.js
  10. 15
      docs/demo.js
  11. 4
      src/base/collection/collection.js
  12. 12
      src/base/grid/grid.js
  13. 329
      src/base/list/clusterize.js
  14. 151
      src/base/list/virtuallist.js
  15. 2
      src/core/utils/prefixIntervalTree.js
  16. 32
      src/css/base/list/clusterize.css
  17. 36
      src/less/base/list/clusterize.less

32
bi/base.css

@ -633,38 +633,6 @@ li.CodeMirror-hint-active {
cursor: text;
font-size: 14px;
}
/* max-height - the only parameter in this file that needs to be edited.
* Change it to suit your needs. The rest is recommended to leave as is.
*/
.clusterize-scroll {
overflow: auto;
}
/**
* Avoid vertical margins for extra tags
* Necessary for correct calculations when rows have nonzero vertical margins
*/
.clusterize-extra-row {
margin-top: 0 !important;
margin-bottom: 0 !important;
}
/* By default extra tag .clusterize-keep-parity added to keep parity of rows.
* Useful when used :nth-child(even/odd)
*/
.clusterize-extra-row.clusterize-keep-parity {
display: none;
}
/* During initialization clusterize adds tabindex to force the browser to keep focus
* on the scrolling list, see issue #11
* Outline removes default browser's borders for focused elements.
*/
.clusterize-content {
outline: 0;
}
/* Centering message that appears when no data provided
*/
.clusterize-no-data td {
text-align: center;
}
/****添加计算宽度的--运算符直接需要space****/
/****** common color(常用颜色,可用于普遍场景) *****/
/**** custom color(自定义颜色,用于特定场景) ****/

502
bi/base.js

@ -2530,8 +2530,8 @@ BI.CollectionView = BI.inherit(BI.Widget, {
_defaultConfig: function () {
return BI.extend(BI.CollectionView.superclass._defaultConfig.apply(this, arguments), {
baseCls: "bi-collection",
// width: 400,
// height: 300,
// width: 400, //必设
// height: 300, //必设
overflowX: true,
overflowY: true,
cellSizeAndPositionGetter: BI.emptyFn,
@ -14671,16 +14671,16 @@ BI.GridView = BI.inherit(BI.Widget, {
_defaultConfig: function () {
return BI.extend(BI.GridView.superclass._defaultConfig.apply(this, arguments), {
baseCls: "bi-grid-view",
// width: 400,
// height: 300,
// width: 400, //必设
// height: 300, //必设
overflowX: true,
overflowY: true,
overscanColumnCount: 0,
overscanRowCount: 0,
rowHeightGetter: BI.emptyFn,
columnWidthGetter: BI.emptyFn,
// estimatedColumnSize: 100,
// estimatedRowSize: 30,
rowHeightGetter: BI.emptyFn, //number类型或function类型
columnWidthGetter: BI.emptyFn, //number类型或function类型
// estimatedColumnSize: 100, //columnWidthGetter为function时必设
// estimatedRowSize: 30, //rowHeightGetter为function时必设
scrollLeft: 0,
scrollTop: 0,
items: []
@ -15455,335 +15455,7 @@ BI.SearcherView = BI.inherit(BI.Pane, {
});
BI.SearcherView.EVENT_CHANGE = "EVENT_CHANGE";
BI.shortcut("bi.searcher_view", BI.SearcherView);/*! Clusterize.js - v0.17.6 - 2017-03-05
* http://NeXTs.github.com/Clusterize.js/
* Copyright (c) 2015 Denis Lukov; Licensed GPLv3 */
;(function(name, definition) {
if (typeof module != 'undefined') module.exports = definition();
else if (typeof define == 'function' && typeof define.amd == 'object') define(definition);
else this[name] = definition();
}('Clusterize', function() {
"use strict"
// detect ie9 and lower
// https://gist.github.com/padolsey/527683#comment-786682
var ie = (function(){
for( var v = 3,
el = document.createElement('b'),
all = el.all || [];
el.innerHTML = '<!--[if gt IE ' + (++v) + ']><i><![endif]-->',
all[0];
){}
return v > 4 ? v : document.documentMode;
}()),
is_mac = navigator.platform.toLowerCase().indexOf('mac') + 1;
var Clusterize = function(data) {
if( ! (this instanceof Clusterize))
return new Clusterize(data);
var self = this;
var defaults = {
rows_in_block: 50,
blocks_in_cluster: 4,
tag: null,
show_no_data_row: true,
no_data_class: 'clusterize-no-data',
no_data_text: 'No data',
keep_parity: true,
callbacks: {}
}
// public parameters
self.options = {};
var options = ['rows_in_block', 'blocks_in_cluster', 'show_no_data_row', 'no_data_class', 'no_data_text', 'keep_parity', 'tag', 'callbacks'];
for(var i = 0, option; option = options[i]; i++) {
self.options[option] = typeof data[option] != 'undefined' && data[option] != null
? data[option]
: defaults[option];
}
var elems = ['scroll', 'content'];
for(var i = 0, elem; elem = elems[i]; i++) {
self[elem + '_elem'] = data[elem + 'Id']
? document.getElementById(data[elem + 'Id'])
: data[elem + 'Elem'];
if( ! self[elem + '_elem'])
throw new Error("Error! Could not find " + elem + " element");
}
// tabindex forces the browser to keep focus on the scrolling list, fixes #11
if( ! self.content_elem.hasAttribute('tabindex'))
self.content_elem.setAttribute('tabindex', 0);
// private parameters
var rows = isArray(data.rows)
? data.rows
: self.fetchMarkup(),
cache = {},
scroll_top = self.scroll_elem.scrollTop;
// append initial data
self.insertToDOM(rows, cache);
// restore the scroll position
self.scroll_elem.scrollTop = scroll_top;
// adding scroll handler
var last_cluster = false,
scroll_debounce = 0,
pointer_events_set = false,
scrollEv = function() {
// fixes scrolling issue on Mac #3
if (is_mac) {
if( ! pointer_events_set) self.content_elem.style.pointerEvents = 'none';
pointer_events_set = true;
clearTimeout(scroll_debounce);
scroll_debounce = setTimeout(function () {
self.content_elem.style.pointerEvents = 'auto';
pointer_events_set = false;
}, 50);
}
if (last_cluster != (last_cluster = self.getClusterNum()))
self.insertToDOM(rows, cache);
if (self.options.callbacks.scrollingProgress)
self.options.callbacks.scrollingProgress(self.getScrollProgress());
},
resize_debounce = 0,
resizeEv = function() {
clearTimeout(resize_debounce);
resize_debounce = setTimeout(self.refresh, 100);
}
on('scroll', self.scroll_elem, scrollEv);
on('resize', window, resizeEv);
// public methods
self.destroy = function(clean) {
off('scroll', self.scroll_elem, scrollEv);
off('resize', window, resizeEv);
self.html((clean ? self.generateEmptyRow() : rows).join(''));
}
self.refresh = function(force) {
if(self.getRowsHeight(rows) || force) self.update(rows);
}
self.update = function(new_rows) {
rows = isArray(new_rows)
? new_rows
: [];
var scroll_top = self.scroll_elem.scrollTop;
// fixes #39
if(rows.length * self.options.item_height < scroll_top) {
self.scroll_elem.scrollTop = 0;
last_cluster = 0;
}
self.insertToDOM(rows, cache);
self.scroll_elem.scrollTop = scroll_top;
}
self.clear = function() {
self.update([]);
}
self.getRowsAmount = function() {
return rows.length;
}
self.getScrollProgress = function() {
return this.options.scroll_top / (rows.length * this.options.item_height) * 100 || 0;
}
var add = function(where, _new_rows) {
var new_rows = isArray(_new_rows)
? _new_rows
: [];
if( ! new_rows.length) return;
rows = where == 'append'
? rows.concat(new_rows)
: new_rows.concat(rows);
self.insertToDOM(rows, cache);
}
self.append = function(rows) {
add('append', rows);
}
self.prepend = function(rows) {
add('prepend', rows);
}
}
Clusterize.prototype = {
constructor: Clusterize,
// fetch existing markup
fetchMarkup: function() {
var rows = [], rows_nodes = this.getChildNodes(this.content_elem);
while (rows_nodes.length) {
rows.push(rows_nodes.shift().outerHTML);
}
return rows;
},
// get tag name, content tag name, tag height, calc cluster height
exploreEnvironment: function(rows, cache) {
var opts = this.options;
opts.content_tag = this.content_elem.tagName.toLowerCase();
if( ! rows.length) return;
if(ie && ie <= 9 && ! opts.tag) opts.tag = rows[0].match(/<([^>\s/]*)/)[1].toLowerCase();
if(this.content_elem.children.length <= 1) cache.data = this.html(rows[0] + rows[0] + rows[0]);
if( ! opts.tag) opts.tag = this.content_elem.children[0].tagName.toLowerCase();
this.getRowsHeight(rows);
},
getRowsHeight: function(rows) {
var opts = this.options,
prev_item_height = opts.item_height;
opts.cluster_height = 0;
if( ! rows.length) return;
var nodes = this.content_elem.children;
var node = nodes[Math.floor(nodes.length / 2)];
opts.item_height = node.offsetHeight;
// consider table's border-spacing
if(opts.tag == 'tr' && getStyle('borderCollapse', this.content_elem) != 'collapse')
opts.item_height += parseInt(getStyle('borderSpacing', this.content_elem), 10) || 0;
// consider margins (and margins collapsing)
if(opts.tag != 'tr') {
var marginTop = parseInt(getStyle('marginTop', node), 10) || 0;
var marginBottom = parseInt(getStyle('marginBottom', node), 10) || 0;
opts.item_height += Math.max(marginTop, marginBottom);
}
opts.block_height = opts.item_height * opts.rows_in_block;
opts.rows_in_cluster = opts.blocks_in_cluster * opts.rows_in_block;
opts.cluster_height = opts.blocks_in_cluster * opts.block_height;
return prev_item_height != opts.item_height;
},
// get current cluster number
getClusterNum: function () {
this.options.scroll_top = this.scroll_elem.scrollTop;
return Math.floor(this.options.scroll_top / (this.options.cluster_height - this.options.block_height)) || 0;
},
// generate empty row if no data provided
generateEmptyRow: function() {
var opts = this.options;
if( ! opts.tag || ! opts.show_no_data_row) return [];
var empty_row = document.createElement(opts.tag),
no_data_content = document.createTextNode(opts.no_data_text), td;
empty_row.className = opts.no_data_class;
if(opts.tag == 'tr') {
td = document.createElement('td');
// fixes #53
td.colSpan = 100;
td.appendChild(no_data_content);
}
empty_row.appendChild(td || no_data_content);
return [empty_row.outerHTML];
},
// generate cluster for current scroll position
generate: function (rows, cluster_num) {
var opts = this.options,
rows_len = rows.length;
if (rows_len < opts.rows_in_block) {
return {
top_offset: 0,
bottom_offset: 0,
rows_above: 0,
rows: rows_len ? rows : this.generateEmptyRow()
}
}
var items_start = Math.max((opts.rows_in_cluster - opts.rows_in_block) * cluster_num, 0),
items_end = items_start + opts.rows_in_cluster,
top_offset = Math.max(items_start * opts.item_height, 0),
bottom_offset = Math.max((rows_len - items_end) * opts.item_height, 0),
this_cluster_rows = [],
rows_above = items_start;
if(top_offset < 1) {
rows_above++;
}
for (var i = items_start; i < items_end; i++) {
rows[i] && this_cluster_rows.push(rows[i]);
}
return {
top_offset: top_offset,
bottom_offset: bottom_offset,
rows_above: rows_above,
rows: this_cluster_rows
}
},
renderExtraTag: function(class_name, height) {
var tag = document.createElement(this.options.tag),
clusterize_prefix = 'clusterize-';
tag.className = [clusterize_prefix + 'extra-row', clusterize_prefix + class_name].join(' ');
height && (tag.style.height = height + 'px');
return tag.outerHTML;
},
// if necessary verify data changed and insert to DOM
insertToDOM: function(rows, cache) {
// explore row's height
if( ! this.options.cluster_height) {
this.exploreEnvironment(rows, cache);
}
var data = this.generate(rows, this.getClusterNum()),
this_cluster_rows = data.rows.join(''),
this_cluster_content_changed = this.checkChanges('data', this_cluster_rows, cache),
top_offset_changed = this.checkChanges('top', data.top_offset, cache),
only_bottom_offset_changed = this.checkChanges('bottom', data.bottom_offset, cache),
callbacks = this.options.callbacks,
layout = [];
if(this_cluster_content_changed || top_offset_changed) {
if(data.top_offset) {
this.options.keep_parity && layout.push(this.renderExtraTag('keep-parity'));
layout.push(this.renderExtraTag('top-space', data.top_offset));
}
layout.push(this_cluster_rows);
data.bottom_offset && layout.push(this.renderExtraTag('bottom-space', data.bottom_offset));
callbacks.clusterWillChange && callbacks.clusterWillChange();
this.html(layout.join(''));
this.options.content_tag == 'ol' && this.content_elem.setAttribute('start', data.rows_above);
callbacks.clusterChanged && callbacks.clusterChanged();
} else if(only_bottom_offset_changed) {
this.content_elem.lastChild.style.height = data.bottom_offset + 'px';
}
},
// unfortunately ie <= 9 does not allow to use innerHTML for table elements, so make a workaround
html: function(data) {
var content_elem = this.content_elem;
if(ie && ie <= 9 && this.options.tag == 'tr') {
var div = document.createElement('div'), last;
div.innerHTML = '<table><tbody>' + data + '</tbody></table>';
while((last = content_elem.lastChild)) {
content_elem.removeChild(last);
}
var rows_nodes = this.getChildNodes(div.firstChild.firstChild);
while (rows_nodes.length) {
content_elem.appendChild(rows_nodes.shift());
}
} else {
content_elem.innerHTML = data;
}
},
getChildNodes: function(tag) {
var child_nodes = tag.children, nodes = [];
for (var i = 0, ii = child_nodes.length; i < ii; i++) {
nodes.push(child_nodes[i]);
}
return nodes;
},
checkChanges: function(type, value, cache) {
var changed = value != cache[type];
cache[type] = value;
return changed;
}
}
// support functions
function on(evt, element, fnc) {
return element.addEventListener ? element.addEventListener(evt, fnc, false) : element.attachEvent("on" + evt, fnc);
}
function off(evt, element, fnc) {
return element.removeEventListener ? element.removeEventListener(evt, fnc, false) : element.detachEvent("on" + evt, fnc);
}
function isArray(arr) {
return Object.prototype.toString.call(arr) === '[object Array]';
}
function getStyle(prop, elem) {
return window.getComputedStyle ? window.getComputedStyle(elem)[prop] : elem.currentStyle[prop];
}
return Clusterize;
}));/**
BI.shortcut("bi.searcher_view", BI.SearcherView);/**
* 表示当前对象
*
* Created by GUY on 2015/9/7.
@ -15793,39 +15465,161 @@ BI.shortcut("bi.searcher_view", BI.SearcherView);/*! Clusterize.js - v0.17.6 - 2
BI.VirtualList = BI.inherit(BI.Widget, {
props: function () {
return {
baseCls: "bi-virtual-list clusterize-scroll",
baseCls: "bi-virtual-list",
overscanHeight: 100,
blockSize: 10,
scrollTop: 0,
items: []
};
},
init: function () {
this.renderedIndex = -1;
this.cache = {};
},
render: function () {
var self = this, o = this.options;
return {
type: "bi.default",
type: "bi.vertical",
items: [{
type: "bi.layout",
ref: function () {
self.contentEl = this;
},
cls: "clusterize-content"
}]
self.topBlank = this;
}
}, {
type: "bi.vertical",
scrolly: false,
ref: function () {
self.container = this;
}
}, {
type: "bi.layout",
ref: function () {
self.bottomBlank = this;
}
}],
element: this
}
},
mounted: function () {
var data = [];
for (var i = 0; i < 10000; i++) {
data.push("<div style='height: 30px;'>" + i + "</div>");
}
new Clusterize({
rows: data,
scrollElem: this.element[0],
contentElem: this.contentEl.element[0]
})
var self = this, o = this.options;
this._populate();
this.element.scroll(function (e) {
o.scrollTop = self.element.scrollTop();
self._calculateBlocksToRender();
});
BI.ResizeDetector.addResizeListener(this, function () {
self._renderMoreIf();
});
},
populate: function () {
_renderMoreIf: function () {
var o = this.options;
var height = this.element.height();
var minContentHeight = o.scrollTop + height + o.overscanHeight;
var index = (this.cache[this.renderedIndex] && (this.cache[this.renderedIndex].index + o.blockSize)) || 0,
cnt = this.renderedIndex + 1;
var lastHeight;
while ((lastHeight = this.container.element.height()) < minContentHeight && index < o.items.length) {
var items = o.items.slice(index, index + o.blockSize);
this.container.addItems(items);
var addedHeight = this.container.element.height() - lastHeight;
this.cache[cnt] = {
index: index,
height: addedHeight
};
this.tree.set(cnt, addedHeight);
this.renderedIndex = cnt;
cnt++;
index += o.blockSize;
}
},
_calculateBlocksToRender: function () {
var o = this.options;
this._renderMoreIf();
// var height = this.element.height();
// var minContentHeightFrom = o.scrollTop - o.overscanHeight;
// var minContentHeightTo = o.scrollTop + height + o.overscanHeight;
// var start = this.tree.greatestLowerBound(minContentHeightFrom);
// var end = this.tree.leastUpperBound(minContentHeightTo);
// var summaryTopHeight = 0;
// this.topBlank.setHeight(0);
// this.bottomBlank.setHeight(0);
// var needDestroyed = [];
// for (var i = 0; i < start; i++) {
// var index = this.cache[i].index;
// summaryTopHeight += this.cache[i].height;
// if (!this.cache[i].destroyed) {
// for (var j = index; j < index + o.blockSize && j < o.items.length; j++) {
// needDestroyed.push(this.container._children[j]);
// this.container._children[j] = null;
// }
// this.cache[i].destroyed = true;
// // this.topBlank.setHeight(summaryTopHeight);
// }
// }
// summaryTopHeight = 0;
// for (var i = end + 1; i <= this.renderedIndex; i++) {
// var index = this.cache[i].index;
// summaryTopHeight += this.cache[i].height;
// if (!this.cache[i].destroyed) {
// for (var j = index; j < index + o.blockSize && j < o.items.length; j++) {
// needDestroyed.push(this.container._children[j]);
// this.container._children[j] = null;
// }
// this.cache[i].destroyed = true;
// // this.bottomBlank.setHeight(summaryTopHeight);
// }
// }
// var firstFragment = document.createDocumentFragment(), lastFragment = document.createDocumentFragment();
// var currentFragment = firstFragment;
// for (var i = (start < 0 ? 0 : start); i <= end && i <= this.renderedIndex; i++) {
// var index = this.cache[i].index;
// if (!this.cache[i].destroyed) {
// currentFragment = lastFragment;
// }
// if (this.cache[i].destroyed === true) {
// for (var j = index; j < index + o.blockSize && j < o.items.length; j++) {
// var w = this.container._children[j] = BI.createWidget(BI.extend({
// root: true
// }, BI.stripEL(o.items[j])));
// currentFragment.appendChild(w.element[0]);
// }
// this.cache[i].destroyed = false;
// }
// }
// this.container.element.prepend(firstFragment);
// this.container.element.append(lastFragment);
// BI.each(needDestroyed, function (i, child) {
// child && child._destroy();
// });
},
_populate: function () {
var o = this.options;
this.tree = BI.PrefixIntervalTree.empty(Math.ceil(o.items.length / o.blockSize));
this._calculateBlocksToRender();
this.element.scrollTop(o.scrollTop);
},
restore: function () {
this.renderedIndex = -1;
this.cache = {};
},
populate: function (items) {
},
destroyed: function () {
this.restore();
}
});
BI.shortcut('bi.virtual_list', BI.VirtualList);/**
BI.shortcut('bi.virtual_list', BI.VirtualList);
/**
* 分页控件
*
* Created by GUY on 2015/8/31.

2
bi/core.js

@ -9657,7 +9657,7 @@ $.extend(BI, {
return new BI.PrefixIntervalTree(xs);
};
BI.PrefixIntervalTree.empty = function () {
BI.PrefixIntervalTree.empty = function (size) {
return BI.PrefixIntervalTree.uniform(size, 0);
};

2
demo/js/core/abstract/demo.collection_view.js

@ -13,6 +13,8 @@ Demo.Func = BI.inherit(BI.Widget, {
}
var grid = BI.createWidget({
type: "bi.collection_view",
width: 400,
height: 300,
items: items,
cellSizeAndPositionGetter: function (index) {
return {

4
demo/js/core/abstract/demo.grid_view.js

@ -16,6 +16,10 @@ Demo.Func = BI.inherit(BI.Widget, {
}
var grid = BI.createWidget({
type: "bi.grid_view",
width: 400,
height: 300,
estimatedRowSize: 30,
estimatedColumnSize: 100,
items: items,
scrollTop: 100,
rowHeightGetter: function () {

9
demo/js/core/abstract/demo.virtual_list.js

@ -4,7 +4,14 @@ Demo.Func = BI.inherit(BI.Widget, {
},
render: function () {
return {
type: "bi.virtual_list"
type: "bi.virtual_list",
items: BI.map(BI.makeArray(200, 0), function (i, item) {
return {
type: "bi.label",
height: 30,
text: i
};
})
}
}
});

32
docs/base.css

@ -633,38 +633,6 @@ li.CodeMirror-hint-active {
cursor: text;
font-size: 14px;
}
/* max-height - the only parameter in this file that needs to be edited.
* Change it to suit your needs. The rest is recommended to leave as is.
*/
.clusterize-scroll {
overflow: auto;
}
/**
* Avoid vertical margins for extra tags
* Necessary for correct calculations when rows have nonzero vertical margins
*/
.clusterize-extra-row {
margin-top: 0 !important;
margin-bottom: 0 !important;
}
/* By default extra tag .clusterize-keep-parity added to keep parity of rows.
* Useful when used :nth-child(even/odd)
*/
.clusterize-extra-row.clusterize-keep-parity {
display: none;
}
/* During initialization clusterize adds tabindex to force the browser to keep focus
* on the scrolling list, see issue #11
* Outline removes default browser's borders for focused elements.
*/
.clusterize-content {
outline: 0;
}
/* Centering message that appears when no data provided
*/
.clusterize-no-data td {
text-align: center;
}
/****添加计算宽度的--运算符直接需要space****/
/****** common color(常用颜色,可用于普遍场景) *****/
/**** custom color(自定义颜色,用于特定场景) ****/

502
docs/base.js

@ -2530,8 +2530,8 @@ BI.CollectionView = BI.inherit(BI.Widget, {
_defaultConfig: function () {
return BI.extend(BI.CollectionView.superclass._defaultConfig.apply(this, arguments), {
baseCls: "bi-collection",
// width: 400,
// height: 300,
// width: 400, //必设
// height: 300, //必设
overflowX: true,
overflowY: true,
cellSizeAndPositionGetter: BI.emptyFn,
@ -14671,16 +14671,16 @@ BI.GridView = BI.inherit(BI.Widget, {
_defaultConfig: function () {
return BI.extend(BI.GridView.superclass._defaultConfig.apply(this, arguments), {
baseCls: "bi-grid-view",
// width: 400,
// height: 300,
// width: 400, //必设
// height: 300, //必设
overflowX: true,
overflowY: true,
overscanColumnCount: 0,
overscanRowCount: 0,
rowHeightGetter: BI.emptyFn,
columnWidthGetter: BI.emptyFn,
// estimatedColumnSize: 100,
// estimatedRowSize: 30,
rowHeightGetter: BI.emptyFn, //number类型或function类型
columnWidthGetter: BI.emptyFn, //number类型或function类型
// estimatedColumnSize: 100, //columnWidthGetter为function时必设
// estimatedRowSize: 30, //rowHeightGetter为function时必设
scrollLeft: 0,
scrollTop: 0,
items: []
@ -15455,335 +15455,7 @@ BI.SearcherView = BI.inherit(BI.Pane, {
});
BI.SearcherView.EVENT_CHANGE = "EVENT_CHANGE";
BI.shortcut("bi.searcher_view", BI.SearcherView);/*! Clusterize.js - v0.17.6 - 2017-03-05
* http://NeXTs.github.com/Clusterize.js/
* Copyright (c) 2015 Denis Lukov; Licensed GPLv3 */
;(function(name, definition) {
if (typeof module != 'undefined') module.exports = definition();
else if (typeof define == 'function' && typeof define.amd == 'object') define(definition);
else this[name] = definition();
}('Clusterize', function() {
"use strict"
// detect ie9 and lower
// https://gist.github.com/padolsey/527683#comment-786682
var ie = (function(){
for( var v = 3,
el = document.createElement('b'),
all = el.all || [];
el.innerHTML = '<!--[if gt IE ' + (++v) + ']><i><![endif]-->',
all[0];
){}
return v > 4 ? v : document.documentMode;
}()),
is_mac = navigator.platform.toLowerCase().indexOf('mac') + 1;
var Clusterize = function(data) {
if( ! (this instanceof Clusterize))
return new Clusterize(data);
var self = this;
var defaults = {
rows_in_block: 50,
blocks_in_cluster: 4,
tag: null,
show_no_data_row: true,
no_data_class: 'clusterize-no-data',
no_data_text: 'No data',
keep_parity: true,
callbacks: {}
}
// public parameters
self.options = {};
var options = ['rows_in_block', 'blocks_in_cluster', 'show_no_data_row', 'no_data_class', 'no_data_text', 'keep_parity', 'tag', 'callbacks'];
for(var i = 0, option; option = options[i]; i++) {
self.options[option] = typeof data[option] != 'undefined' && data[option] != null
? data[option]
: defaults[option];
}
var elems = ['scroll', 'content'];
for(var i = 0, elem; elem = elems[i]; i++) {
self[elem + '_elem'] = data[elem + 'Id']
? document.getElementById(data[elem + 'Id'])
: data[elem + 'Elem'];
if( ! self[elem + '_elem'])
throw new Error("Error! Could not find " + elem + " element");
}
// tabindex forces the browser to keep focus on the scrolling list, fixes #11
if( ! self.content_elem.hasAttribute('tabindex'))
self.content_elem.setAttribute('tabindex', 0);
// private parameters
var rows = isArray(data.rows)
? data.rows
: self.fetchMarkup(),
cache = {},
scroll_top = self.scroll_elem.scrollTop;
// append initial data
self.insertToDOM(rows, cache);
// restore the scroll position
self.scroll_elem.scrollTop = scroll_top;
// adding scroll handler
var last_cluster = false,
scroll_debounce = 0,
pointer_events_set = false,
scrollEv = function() {
// fixes scrolling issue on Mac #3
if (is_mac) {
if( ! pointer_events_set) self.content_elem.style.pointerEvents = 'none';
pointer_events_set = true;
clearTimeout(scroll_debounce);
scroll_debounce = setTimeout(function () {
self.content_elem.style.pointerEvents = 'auto';
pointer_events_set = false;
}, 50);
}
if (last_cluster != (last_cluster = self.getClusterNum()))
self.insertToDOM(rows, cache);
if (self.options.callbacks.scrollingProgress)
self.options.callbacks.scrollingProgress(self.getScrollProgress());
},
resize_debounce = 0,
resizeEv = function() {
clearTimeout(resize_debounce);
resize_debounce = setTimeout(self.refresh, 100);
}
on('scroll', self.scroll_elem, scrollEv);
on('resize', window, resizeEv);
// public methods
self.destroy = function(clean) {
off('scroll', self.scroll_elem, scrollEv);
off('resize', window, resizeEv);
self.html((clean ? self.generateEmptyRow() : rows).join(''));
}
self.refresh = function(force) {
if(self.getRowsHeight(rows) || force) self.update(rows);
}
self.update = function(new_rows) {
rows = isArray(new_rows)
? new_rows
: [];
var scroll_top = self.scroll_elem.scrollTop;
// fixes #39
if(rows.length * self.options.item_height < scroll_top) {
self.scroll_elem.scrollTop = 0;
last_cluster = 0;
}
self.insertToDOM(rows, cache);
self.scroll_elem.scrollTop = scroll_top;
}
self.clear = function() {
self.update([]);
}
self.getRowsAmount = function() {
return rows.length;
}
self.getScrollProgress = function() {
return this.options.scroll_top / (rows.length * this.options.item_height) * 100 || 0;
}
var add = function(where, _new_rows) {
var new_rows = isArray(_new_rows)
? _new_rows
: [];
if( ! new_rows.length) return;
rows = where == 'append'
? rows.concat(new_rows)
: new_rows.concat(rows);
self.insertToDOM(rows, cache);
}
self.append = function(rows) {
add('append', rows);
}
self.prepend = function(rows) {
add('prepend', rows);
}
}
Clusterize.prototype = {
constructor: Clusterize,
// fetch existing markup
fetchMarkup: function() {
var rows = [], rows_nodes = this.getChildNodes(this.content_elem);
while (rows_nodes.length) {
rows.push(rows_nodes.shift().outerHTML);
}
return rows;
},
// get tag name, content tag name, tag height, calc cluster height
exploreEnvironment: function(rows, cache) {
var opts = this.options;
opts.content_tag = this.content_elem.tagName.toLowerCase();
if( ! rows.length) return;
if(ie && ie <= 9 && ! opts.tag) opts.tag = rows[0].match(/<([^>\s/]*)/)[1].toLowerCase();
if(this.content_elem.children.length <= 1) cache.data = this.html(rows[0] + rows[0] + rows[0]);
if( ! opts.tag) opts.tag = this.content_elem.children[0].tagName.toLowerCase();
this.getRowsHeight(rows);
},
getRowsHeight: function(rows) {
var opts = this.options,
prev_item_height = opts.item_height;
opts.cluster_height = 0;
if( ! rows.length) return;
var nodes = this.content_elem.children;
var node = nodes[Math.floor(nodes.length / 2)];
opts.item_height = node.offsetHeight;
// consider table's border-spacing
if(opts.tag == 'tr' && getStyle('borderCollapse', this.content_elem) != 'collapse')
opts.item_height += parseInt(getStyle('borderSpacing', this.content_elem), 10) || 0;
// consider margins (and margins collapsing)
if(opts.tag != 'tr') {
var marginTop = parseInt(getStyle('marginTop', node), 10) || 0;
var marginBottom = parseInt(getStyle('marginBottom', node), 10) || 0;
opts.item_height += Math.max(marginTop, marginBottom);
}
opts.block_height = opts.item_height * opts.rows_in_block;
opts.rows_in_cluster = opts.blocks_in_cluster * opts.rows_in_block;
opts.cluster_height = opts.blocks_in_cluster * opts.block_height;
return prev_item_height != opts.item_height;
},
// get current cluster number
getClusterNum: function () {
this.options.scroll_top = this.scroll_elem.scrollTop;
return Math.floor(this.options.scroll_top / (this.options.cluster_height - this.options.block_height)) || 0;
},
// generate empty row if no data provided
generateEmptyRow: function() {
var opts = this.options;
if( ! opts.tag || ! opts.show_no_data_row) return [];
var empty_row = document.createElement(opts.tag),
no_data_content = document.createTextNode(opts.no_data_text), td;
empty_row.className = opts.no_data_class;
if(opts.tag == 'tr') {
td = document.createElement('td');
// fixes #53
td.colSpan = 100;
td.appendChild(no_data_content);
}
empty_row.appendChild(td || no_data_content);
return [empty_row.outerHTML];
},
// generate cluster for current scroll position
generate: function (rows, cluster_num) {
var opts = this.options,
rows_len = rows.length;
if (rows_len < opts.rows_in_block) {
return {
top_offset: 0,
bottom_offset: 0,
rows_above: 0,
rows: rows_len ? rows : this.generateEmptyRow()
}
}
var items_start = Math.max((opts.rows_in_cluster - opts.rows_in_block) * cluster_num, 0),
items_end = items_start + opts.rows_in_cluster,
top_offset = Math.max(items_start * opts.item_height, 0),
bottom_offset = Math.max((rows_len - items_end) * opts.item_height, 0),
this_cluster_rows = [],
rows_above = items_start;
if(top_offset < 1) {
rows_above++;
}
for (var i = items_start; i < items_end; i++) {
rows[i] && this_cluster_rows.push(rows[i]);
}
return {
top_offset: top_offset,
bottom_offset: bottom_offset,
rows_above: rows_above,
rows: this_cluster_rows
}
},
renderExtraTag: function(class_name, height) {
var tag = document.createElement(this.options.tag),
clusterize_prefix = 'clusterize-';
tag.className = [clusterize_prefix + 'extra-row', clusterize_prefix + class_name].join(' ');
height && (tag.style.height = height + 'px');
return tag.outerHTML;
},
// if necessary verify data changed and insert to DOM
insertToDOM: function(rows, cache) {
// explore row's height
if( ! this.options.cluster_height) {
this.exploreEnvironment(rows, cache);
}
var data = this.generate(rows, this.getClusterNum()),
this_cluster_rows = data.rows.join(''),
this_cluster_content_changed = this.checkChanges('data', this_cluster_rows, cache),
top_offset_changed = this.checkChanges('top', data.top_offset, cache),
only_bottom_offset_changed = this.checkChanges('bottom', data.bottom_offset, cache),
callbacks = this.options.callbacks,
layout = [];
if(this_cluster_content_changed || top_offset_changed) {
if(data.top_offset) {
this.options.keep_parity && layout.push(this.renderExtraTag('keep-parity'));
layout.push(this.renderExtraTag('top-space', data.top_offset));
}
layout.push(this_cluster_rows);
data.bottom_offset && layout.push(this.renderExtraTag('bottom-space', data.bottom_offset));
callbacks.clusterWillChange && callbacks.clusterWillChange();
this.html(layout.join(''));
this.options.content_tag == 'ol' && this.content_elem.setAttribute('start', data.rows_above);
callbacks.clusterChanged && callbacks.clusterChanged();
} else if(only_bottom_offset_changed) {
this.content_elem.lastChild.style.height = data.bottom_offset + 'px';
}
},
// unfortunately ie <= 9 does not allow to use innerHTML for table elements, so make a workaround
html: function(data) {
var content_elem = this.content_elem;
if(ie && ie <= 9 && this.options.tag == 'tr') {
var div = document.createElement('div'), last;
div.innerHTML = '<table><tbody>' + data + '</tbody></table>';
while((last = content_elem.lastChild)) {
content_elem.removeChild(last);
}
var rows_nodes = this.getChildNodes(div.firstChild.firstChild);
while (rows_nodes.length) {
content_elem.appendChild(rows_nodes.shift());
}
} else {
content_elem.innerHTML = data;
}
},
getChildNodes: function(tag) {
var child_nodes = tag.children, nodes = [];
for (var i = 0, ii = child_nodes.length; i < ii; i++) {
nodes.push(child_nodes[i]);
}
return nodes;
},
checkChanges: function(type, value, cache) {
var changed = value != cache[type];
cache[type] = value;
return changed;
}
}
// support functions
function on(evt, element, fnc) {
return element.addEventListener ? element.addEventListener(evt, fnc, false) : element.attachEvent("on" + evt, fnc);
}
function off(evt, element, fnc) {
return element.removeEventListener ? element.removeEventListener(evt, fnc, false) : element.detachEvent("on" + evt, fnc);
}
function isArray(arr) {
return Object.prototype.toString.call(arr) === '[object Array]';
}
function getStyle(prop, elem) {
return window.getComputedStyle ? window.getComputedStyle(elem)[prop] : elem.currentStyle[prop];
}
return Clusterize;
}));/**
BI.shortcut("bi.searcher_view", BI.SearcherView);/**
* 表示当前对象
*
* Created by GUY on 2015/9/7.
@ -15793,39 +15465,161 @@ BI.shortcut("bi.searcher_view", BI.SearcherView);/*! Clusterize.js - v0.17.6 - 2
BI.VirtualList = BI.inherit(BI.Widget, {
props: function () {
return {
baseCls: "bi-virtual-list clusterize-scroll",
baseCls: "bi-virtual-list",
overscanHeight: 100,
blockSize: 10,
scrollTop: 0,
items: []
};
},
init: function () {
this.renderedIndex = -1;
this.cache = {};
},
render: function () {
var self = this, o = this.options;
return {
type: "bi.default",
type: "bi.vertical",
items: [{
type: "bi.layout",
ref: function () {
self.contentEl = this;
},
cls: "clusterize-content"
}]
self.topBlank = this;
}
}, {
type: "bi.vertical",
scrolly: false,
ref: function () {
self.container = this;
}
}, {
type: "bi.layout",
ref: function () {
self.bottomBlank = this;
}
}],
element: this
}
},
mounted: function () {
var data = [];
for (var i = 0; i < 10000; i++) {
data.push("<div style='height: 30px;'>" + i + "</div>");
}
new Clusterize({
rows: data,
scrollElem: this.element[0],
contentElem: this.contentEl.element[0]
})
var self = this, o = this.options;
this._populate();
this.element.scroll(function (e) {
o.scrollTop = self.element.scrollTop();
self._calculateBlocksToRender();
});
BI.ResizeDetector.addResizeListener(this, function () {
self._renderMoreIf();
});
},
populate: function () {
_renderMoreIf: function () {
var o = this.options;
var height = this.element.height();
var minContentHeight = o.scrollTop + height + o.overscanHeight;
var index = (this.cache[this.renderedIndex] && (this.cache[this.renderedIndex].index + o.blockSize)) || 0,
cnt = this.renderedIndex + 1;
var lastHeight;
while ((lastHeight = this.container.element.height()) < minContentHeight && index < o.items.length) {
var items = o.items.slice(index, index + o.blockSize);
this.container.addItems(items);
var addedHeight = this.container.element.height() - lastHeight;
this.cache[cnt] = {
index: index,
height: addedHeight
};
this.tree.set(cnt, addedHeight);
this.renderedIndex = cnt;
cnt++;
index += o.blockSize;
}
},
_calculateBlocksToRender: function () {
var o = this.options;
this._renderMoreIf();
// var height = this.element.height();
// var minContentHeightFrom = o.scrollTop - o.overscanHeight;
// var minContentHeightTo = o.scrollTop + height + o.overscanHeight;
// var start = this.tree.greatestLowerBound(minContentHeightFrom);
// var end = this.tree.leastUpperBound(minContentHeightTo);
// var summaryTopHeight = 0;
// this.topBlank.setHeight(0);
// this.bottomBlank.setHeight(0);
// var needDestroyed = [];
// for (var i = 0; i < start; i++) {
// var index = this.cache[i].index;
// summaryTopHeight += this.cache[i].height;
// if (!this.cache[i].destroyed) {
// for (var j = index; j < index + o.blockSize && j < o.items.length; j++) {
// needDestroyed.push(this.container._children[j]);
// this.container._children[j] = null;
// }
// this.cache[i].destroyed = true;
// // this.topBlank.setHeight(summaryTopHeight);
// }
// }
// summaryTopHeight = 0;
// for (var i = end + 1; i <= this.renderedIndex; i++) {
// var index = this.cache[i].index;
// summaryTopHeight += this.cache[i].height;
// if (!this.cache[i].destroyed) {
// for (var j = index; j < index + o.blockSize && j < o.items.length; j++) {
// needDestroyed.push(this.container._children[j]);
// this.container._children[j] = null;
// }
// this.cache[i].destroyed = true;
// // this.bottomBlank.setHeight(summaryTopHeight);
// }
// }
// var firstFragment = document.createDocumentFragment(), lastFragment = document.createDocumentFragment();
// var currentFragment = firstFragment;
// for (var i = (start < 0 ? 0 : start); i <= end && i <= this.renderedIndex; i++) {
// var index = this.cache[i].index;
// if (!this.cache[i].destroyed) {
// currentFragment = lastFragment;
// }
// if (this.cache[i].destroyed === true) {
// for (var j = index; j < index + o.blockSize && j < o.items.length; j++) {
// var w = this.container._children[j] = BI.createWidget(BI.extend({
// root: true
// }, BI.stripEL(o.items[j])));
// currentFragment.appendChild(w.element[0]);
// }
// this.cache[i].destroyed = false;
// }
// }
// this.container.element.prepend(firstFragment);
// this.container.element.append(lastFragment);
// BI.each(needDestroyed, function (i, child) {
// child && child._destroy();
// });
},
_populate: function () {
var o = this.options;
this.tree = BI.PrefixIntervalTree.empty(Math.ceil(o.items.length / o.blockSize));
this._calculateBlocksToRender();
this.element.scrollTop(o.scrollTop);
},
restore: function () {
this.renderedIndex = -1;
this.cache = {};
},
populate: function (items) {
},
destroyed: function () {
this.restore();
}
});
BI.shortcut('bi.virtual_list', BI.VirtualList);/**
BI.shortcut('bi.virtual_list', BI.VirtualList);
/**
* 分页控件
*
* Created by GUY on 2015/8/31.

2
docs/core.js

@ -18115,7 +18115,7 @@ $.extend(BI, {
return new BI.PrefixIntervalTree(xs);
};
BI.PrefixIntervalTree.empty = function () {
BI.PrefixIntervalTree.empty = function (size) {
return BI.PrefixIntervalTree.uniform(size, 0);
};

15
docs/demo.js

@ -3325,6 +3325,8 @@ BI.shortcut("demo.button_tree", Demo.Func);Demo.Func = BI.inherit(BI.Widget, {
}
var grid = BI.createWidget({
type: "bi.collection_view",
width: 400,
height: 300,
items: items,
cellSizeAndPositionGetter: function (index) {
return {
@ -3366,6 +3368,10 @@ BI.shortcut("demo.collection_view", Demo.Func);Demo.Func = BI.inherit(BI.Widget,
}
var grid = BI.createWidget({
type: "bi.grid_view",
width: 400,
height: 300,
estimatedRowSize: 30,
estimatedColumnSize: 100,
items: items,
scrollTop: 100,
rowHeightGetter: function () {
@ -3482,7 +3488,14 @@ BI.shortcut("demo.virtual_group_item", Demo.Item);Demo.Func = BI.inherit(BI.Widg
},
render: function () {
return {
type: "bi.virtual_list"
type: "bi.virtual_list",
items: BI.map(BI.makeArray(200, 0), function (i, item) {
return {
type: "bi.label",
height: 30,
text: i
};
})
}
}
});

4
src/base/collection/collection.js

@ -9,8 +9,8 @@ BI.CollectionView = BI.inherit(BI.Widget, {
_defaultConfig: function () {
return BI.extend(BI.CollectionView.superclass._defaultConfig.apply(this, arguments), {
baseCls: "bi-collection",
// width: 400,
// height: 300,
// width: 400, //必设
// height: 300, //必设
overflowX: true,
overflowY: true,
cellSizeAndPositionGetter: BI.emptyFn,

12
src/base/grid/grid.js

@ -9,16 +9,16 @@ BI.GridView = BI.inherit(BI.Widget, {
_defaultConfig: function () {
return BI.extend(BI.GridView.superclass._defaultConfig.apply(this, arguments), {
baseCls: "bi-grid-view",
// width: 400,
// height: 300,
// width: 400, //必设
// height: 300, //必设
overflowX: true,
overflowY: true,
overscanColumnCount: 0,
overscanRowCount: 0,
rowHeightGetter: BI.emptyFn,
columnWidthGetter: BI.emptyFn,
// estimatedColumnSize: 100,
// estimatedRowSize: 30,
rowHeightGetter: BI.emptyFn, //number类型或function类型
columnWidthGetter: BI.emptyFn, //number类型或function类型
// estimatedColumnSize: 100, //columnWidthGetter为function时必设
// estimatedRowSize: 30, //rowHeightGetter为function时必设
scrollLeft: 0,
scrollTop: 0,
items: []

329
src/base/list/clusterize.js

@ -1,329 +0,0 @@
/*! Clusterize.js - v0.17.6 - 2017-03-05
* http://NeXTs.github.com/Clusterize.js/
* Copyright (c) 2015 Denis Lukov; Licensed GPLv3 */
;(function(name, definition) {
if (typeof module != 'undefined') module.exports = definition();
else if (typeof define == 'function' && typeof define.amd == 'object') define(definition);
else this[name] = definition();
}('Clusterize', function() {
"use strict"
// detect ie9 and lower
// https://gist.github.com/padolsey/527683#comment-786682
var ie = (function(){
for( var v = 3,
el = document.createElement('b'),
all = el.all || [];
el.innerHTML = '<!--[if gt IE ' + (++v) + ']><i><![endif]-->',
all[0];
){}
return v > 4 ? v : document.documentMode;
}()),
is_mac = navigator.platform.toLowerCase().indexOf('mac') + 1;
var Clusterize = function(data) {
if( ! (this instanceof Clusterize))
return new Clusterize(data);
var self = this;
var defaults = {
rows_in_block: 50,
blocks_in_cluster: 4,
tag: null,
show_no_data_row: true,
no_data_class: 'clusterize-no-data',
no_data_text: 'No data',
keep_parity: true,
callbacks: {}
}
// public parameters
self.options = {};
var options = ['rows_in_block', 'blocks_in_cluster', 'show_no_data_row', 'no_data_class', 'no_data_text', 'keep_parity', 'tag', 'callbacks'];
for(var i = 0, option; option = options[i]; i++) {
self.options[option] = typeof data[option] != 'undefined' && data[option] != null
? data[option]
: defaults[option];
}
var elems = ['scroll', 'content'];
for(var i = 0, elem; elem = elems[i]; i++) {
self[elem + '_elem'] = data[elem + 'Id']
? document.getElementById(data[elem + 'Id'])
: data[elem + 'Elem'];
if( ! self[elem + '_elem'])
throw new Error("Error! Could not find " + elem + " element");
}
// tabindex forces the browser to keep focus on the scrolling list, fixes #11
if( ! self.content_elem.hasAttribute('tabindex'))
self.content_elem.setAttribute('tabindex', 0);
// private parameters
var rows = isArray(data.rows)
? data.rows
: self.fetchMarkup(),
cache = {},
scroll_top = self.scroll_elem.scrollTop;
// append initial data
self.insertToDOM(rows, cache);
// restore the scroll position
self.scroll_elem.scrollTop = scroll_top;
// adding scroll handler
var last_cluster = false,
scroll_debounce = 0,
pointer_events_set = false,
scrollEv = function() {
// fixes scrolling issue on Mac #3
if (is_mac) {
if( ! pointer_events_set) self.content_elem.style.pointerEvents = 'none';
pointer_events_set = true;
clearTimeout(scroll_debounce);
scroll_debounce = setTimeout(function () {
self.content_elem.style.pointerEvents = 'auto';
pointer_events_set = false;
}, 50);
}
if (last_cluster != (last_cluster = self.getClusterNum()))
self.insertToDOM(rows, cache);
if (self.options.callbacks.scrollingProgress)
self.options.callbacks.scrollingProgress(self.getScrollProgress());
},
resize_debounce = 0,
resizeEv = function() {
clearTimeout(resize_debounce);
resize_debounce = setTimeout(self.refresh, 100);
}
on('scroll', self.scroll_elem, scrollEv);
on('resize', window, resizeEv);
// public methods
self.destroy = function(clean) {
off('scroll', self.scroll_elem, scrollEv);
off('resize', window, resizeEv);
self.html((clean ? self.generateEmptyRow() : rows).join(''));
}
self.refresh = function(force) {
if(self.getRowsHeight(rows) || force) self.update(rows);
}
self.update = function(new_rows) {
rows = isArray(new_rows)
? new_rows
: [];
var scroll_top = self.scroll_elem.scrollTop;
// fixes #39
if(rows.length * self.options.item_height < scroll_top) {
self.scroll_elem.scrollTop = 0;
last_cluster = 0;
}
self.insertToDOM(rows, cache);
self.scroll_elem.scrollTop = scroll_top;
}
self.clear = function() {
self.update([]);
}
self.getRowsAmount = function() {
return rows.length;
}
self.getScrollProgress = function() {
return this.options.scroll_top / (rows.length * this.options.item_height) * 100 || 0;
}
var add = function(where, _new_rows) {
var new_rows = isArray(_new_rows)
? _new_rows
: [];
if( ! new_rows.length) return;
rows = where == 'append'
? rows.concat(new_rows)
: new_rows.concat(rows);
self.insertToDOM(rows, cache);
}
self.append = function(rows) {
add('append', rows);
}
self.prepend = function(rows) {
add('prepend', rows);
}
}
Clusterize.prototype = {
constructor: Clusterize,
// fetch existing markup
fetchMarkup: function() {
var rows = [], rows_nodes = this.getChildNodes(this.content_elem);
while (rows_nodes.length) {
rows.push(rows_nodes.shift().outerHTML);
}
return rows;
},
// get tag name, content tag name, tag height, calc cluster height
exploreEnvironment: function(rows, cache) {
var opts = this.options;
opts.content_tag = this.content_elem.tagName.toLowerCase();
if( ! rows.length) return;
if(ie && ie <= 9 && ! opts.tag) opts.tag = rows[0].match(/<([^>\s/]*)/)[1].toLowerCase();
if(this.content_elem.children.length <= 1) cache.data = this.html(rows[0] + rows[0] + rows[0]);
if( ! opts.tag) opts.tag = this.content_elem.children[0].tagName.toLowerCase();
this.getRowsHeight(rows);
},
getRowsHeight: function(rows) {
var opts = this.options,
prev_item_height = opts.item_height;
opts.cluster_height = 0;
if( ! rows.length) return;
var nodes = this.content_elem.children;
var node = nodes[Math.floor(nodes.length / 2)];
opts.item_height = node.offsetHeight;
// consider table's border-spacing
if(opts.tag == 'tr' && getStyle('borderCollapse', this.content_elem) != 'collapse')
opts.item_height += parseInt(getStyle('borderSpacing', this.content_elem), 10) || 0;
// consider margins (and margins collapsing)
if(opts.tag != 'tr') {
var marginTop = parseInt(getStyle('marginTop', node), 10) || 0;
var marginBottom = parseInt(getStyle('marginBottom', node), 10) || 0;
opts.item_height += Math.max(marginTop, marginBottom);
}
opts.block_height = opts.item_height * opts.rows_in_block;
opts.rows_in_cluster = opts.blocks_in_cluster * opts.rows_in_block;
opts.cluster_height = opts.blocks_in_cluster * opts.block_height;
return prev_item_height != opts.item_height;
},
// get current cluster number
getClusterNum: function () {
this.options.scroll_top = this.scroll_elem.scrollTop;
return Math.floor(this.options.scroll_top / (this.options.cluster_height - this.options.block_height)) || 0;
},
// generate empty row if no data provided
generateEmptyRow: function() {
var opts = this.options;
if( ! opts.tag || ! opts.show_no_data_row) return [];
var empty_row = document.createElement(opts.tag),
no_data_content = document.createTextNode(opts.no_data_text), td;
empty_row.className = opts.no_data_class;
if(opts.tag == 'tr') {
td = document.createElement('td');
// fixes #53
td.colSpan = 100;
td.appendChild(no_data_content);
}
empty_row.appendChild(td || no_data_content);
return [empty_row.outerHTML];
},
// generate cluster for current scroll position
generate: function (rows, cluster_num) {
var opts = this.options,
rows_len = rows.length;
if (rows_len < opts.rows_in_block) {
return {
top_offset: 0,
bottom_offset: 0,
rows_above: 0,
rows: rows_len ? rows : this.generateEmptyRow()
}
}
var items_start = Math.max((opts.rows_in_cluster - opts.rows_in_block) * cluster_num, 0),
items_end = items_start + opts.rows_in_cluster,
top_offset = Math.max(items_start * opts.item_height, 0),
bottom_offset = Math.max((rows_len - items_end) * opts.item_height, 0),
this_cluster_rows = [],
rows_above = items_start;
if(top_offset < 1) {
rows_above++;
}
for (var i = items_start; i < items_end; i++) {
rows[i] && this_cluster_rows.push(rows[i]);
}
return {
top_offset: top_offset,
bottom_offset: bottom_offset,
rows_above: rows_above,
rows: this_cluster_rows
}
},
renderExtraTag: function(class_name, height) {
var tag = document.createElement(this.options.tag),
clusterize_prefix = 'clusterize-';
tag.className = [clusterize_prefix + 'extra-row', clusterize_prefix + class_name].join(' ');
height && (tag.style.height = height + 'px');
return tag.outerHTML;
},
// if necessary verify data changed and insert to DOM
insertToDOM: function(rows, cache) {
// explore row's height
if( ! this.options.cluster_height) {
this.exploreEnvironment(rows, cache);
}
var data = this.generate(rows, this.getClusterNum()),
this_cluster_rows = data.rows.join(''),
this_cluster_content_changed = this.checkChanges('data', this_cluster_rows, cache),
top_offset_changed = this.checkChanges('top', data.top_offset, cache),
only_bottom_offset_changed = this.checkChanges('bottom', data.bottom_offset, cache),
callbacks = this.options.callbacks,
layout = [];
if(this_cluster_content_changed || top_offset_changed) {
if(data.top_offset) {
this.options.keep_parity && layout.push(this.renderExtraTag('keep-parity'));
layout.push(this.renderExtraTag('top-space', data.top_offset));
}
layout.push(this_cluster_rows);
data.bottom_offset && layout.push(this.renderExtraTag('bottom-space', data.bottom_offset));
callbacks.clusterWillChange && callbacks.clusterWillChange();
this.html(layout.join(''));
this.options.content_tag == 'ol' && this.content_elem.setAttribute('start', data.rows_above);
callbacks.clusterChanged && callbacks.clusterChanged();
} else if(only_bottom_offset_changed) {
this.content_elem.lastChild.style.height = data.bottom_offset + 'px';
}
},
// unfortunately ie <= 9 does not allow to use innerHTML for table elements, so make a workaround
html: function(data) {
var content_elem = this.content_elem;
if(ie && ie <= 9 && this.options.tag == 'tr') {
var div = document.createElement('div'), last;
div.innerHTML = '<table><tbody>' + data + '</tbody></table>';
while((last = content_elem.lastChild)) {
content_elem.removeChild(last);
}
var rows_nodes = this.getChildNodes(div.firstChild.firstChild);
while (rows_nodes.length) {
content_elem.appendChild(rows_nodes.shift());
}
} else {
content_elem.innerHTML = data;
}
},
getChildNodes: function(tag) {
var child_nodes = tag.children, nodes = [];
for (var i = 0, ii = child_nodes.length; i < ii; i++) {
nodes.push(child_nodes[i]);
}
return nodes;
},
checkChanges: function(type, value, cache) {
var changed = value != cache[type];
cache[type] = value;
return changed;
}
}
// support functions
function on(evt, element, fnc) {
return element.addEventListener ? element.addEventListener(evt, fnc, false) : element.attachEvent("on" + evt, fnc);
}
function off(evt, element, fnc) {
return element.removeEventListener ? element.removeEventListener(evt, fnc, false) : element.detachEvent("on" + evt, fnc);
}
function isArray(arr) {
return Object.prototype.toString.call(arr) === '[object Array]';
}
function getStyle(prop, elem) {
return window.getComputedStyle ? window.getComputedStyle(elem)[prop] : elem.currentStyle[prop];
}
return Clusterize;
}));

151
src/base/list/virtuallist.js

@ -8,36 +8,157 @@
BI.VirtualList = BI.inherit(BI.Widget, {
props: function () {
return {
baseCls: "bi-virtual-list clusterize-scroll",
baseCls: "bi-virtual-list",
overscanHeight: 100,
blockSize: 10,
scrollTop: 0,
items: []
};
},
init: function () {
this.renderedIndex = -1;
this.cache = {};
},
render: function () {
var self = this, o = this.options;
return {
type: "bi.default",
type: "bi.vertical",
items: [{
type: "bi.layout",
ref: function () {
self.contentEl = this;
},
cls: "clusterize-content"
}]
self.topBlank = this;
}
}, {
type: "bi.vertical",
scrolly: false,
ref: function () {
self.container = this;
}
}, {
type: "bi.layout",
ref: function () {
self.bottomBlank = this;
}
}],
element: this
}
},
mounted: function () {
var data = [];
for (var i = 0; i < 10000; i++) {
data.push("<div style='height: 30px;'>" + i + "</div>");
var self = this, o = this.options;
this._populate();
this.element.scroll(function (e) {
o.scrollTop = self.element.scrollTop();
self._calculateBlocksToRender();
});
BI.ResizeDetector.addResizeListener(this, function () {
self._renderMoreIf();
});
},
_renderMoreIf: function () {
var o = this.options;
var height = this.element.height();
var minContentHeight = o.scrollTop + height + o.overscanHeight;
var index = (this.cache[this.renderedIndex] && (this.cache[this.renderedIndex].index + o.blockSize)) || 0,
cnt = this.renderedIndex + 1;
var lastHeight;
while ((lastHeight = this.container.element.height()) < minContentHeight && index < o.items.length) {
var items = o.items.slice(index, index + o.blockSize);
this.container.addItems(items);
var addedHeight = this.container.element.height() - lastHeight;
this.cache[cnt] = {
index: index,
height: addedHeight
};
this.tree.set(cnt, addedHeight);
this.renderedIndex = cnt;
cnt++;
index += o.blockSize;
}
new Clusterize({
rows: data,
scrollElem: this.element[0],
contentElem: this.contentEl.element[0]
})
},
populate: function () {
_calculateBlocksToRender: function () {
var o = this.options;
this._renderMoreIf();
// var height = this.element.height();
// var minContentHeightFrom = o.scrollTop - o.overscanHeight;
// var minContentHeightTo = o.scrollTop + height + o.overscanHeight;
// var start = this.tree.greatestLowerBound(minContentHeightFrom);
// var end = this.tree.leastUpperBound(minContentHeightTo);
// var summaryTopHeight = 0;
// this.topBlank.setHeight(0);
// this.bottomBlank.setHeight(0);
// var needDestroyed = [];
// for (var i = 0; i < start; i++) {
// var index = this.cache[i].index;
// summaryTopHeight += this.cache[i].height;
// if (!this.cache[i].destroyed) {
// for (var j = index; j < index + o.blockSize && j < o.items.length; j++) {
// needDestroyed.push(this.container._children[j]);
// this.container._children[j] = null;
// }
// this.cache[i].destroyed = true;
// // this.topBlank.setHeight(summaryTopHeight);
// }
// }
// summaryTopHeight = 0;
// for (var i = end + 1; i <= this.renderedIndex; i++) {
// var index = this.cache[i].index;
// summaryTopHeight += this.cache[i].height;
// if (!this.cache[i].destroyed) {
// for (var j = index; j < index + o.blockSize && j < o.items.length; j++) {
// needDestroyed.push(this.container._children[j]);
// this.container._children[j] = null;
// }
// this.cache[i].destroyed = true;
// // this.bottomBlank.setHeight(summaryTopHeight);
// }
// }
// var firstFragment = document.createDocumentFragment(), lastFragment = document.createDocumentFragment();
// var currentFragment = firstFragment;
// for (var i = (start < 0 ? 0 : start); i <= end && i <= this.renderedIndex; i++) {
// var index = this.cache[i].index;
// if (!this.cache[i].destroyed) {
// currentFragment = lastFragment;
// }
// if (this.cache[i].destroyed === true) {
// for (var j = index; j < index + o.blockSize && j < o.items.length; j++) {
// var w = this.container._children[j] = BI.createWidget(BI.extend({
// root: true
// }, BI.stripEL(o.items[j])));
// currentFragment.appendChild(w.element[0]);
// }
// this.cache[i].destroyed = false;
// }
// }
// this.container.element.prepend(firstFragment);
// this.container.element.append(lastFragment);
// BI.each(needDestroyed, function (i, child) {
// child && child._destroy();
// });
},
_populate: function () {
var o = this.options;
this.tree = BI.PrefixIntervalTree.empty(Math.ceil(o.items.length / o.blockSize));
this._calculateBlocksToRender();
this.element.scrollTop(o.scrollTop);
},
restore: function () {
this.renderedIndex = -1;
this.cache = {};
},
populate: function (items) {
},
destroyed: function () {
this.restore();
}
});
BI.shortcut('bi.virtual_list', BI.VirtualList);

2
src/core/utils/prefixIntervalTree.js

@ -170,7 +170,7 @@
return new BI.PrefixIntervalTree(xs);
};
BI.PrefixIntervalTree.empty = function () {
BI.PrefixIntervalTree.empty = function (size) {
return BI.PrefixIntervalTree.uniform(size, 0);
};

32
src/css/base/list/clusterize.css

@ -1,32 +0,0 @@
/* max-height - the only parameter in this file that needs to be edited.
* Change it to suit your needs. The rest is recommended to leave as is.
*/
.clusterize-scroll {
overflow: auto;
}
/**
* Avoid vertical margins for extra tags
* Necessary for correct calculations when rows have nonzero vertical margins
*/
.clusterize-extra-row {
margin-top: 0 !important;
margin-bottom: 0 !important;
}
/* By default extra tag .clusterize-keep-parity added to keep parity of rows.
* Useful when used :nth-child(even/odd)
*/
.clusterize-extra-row.clusterize-keep-parity {
display: none;
}
/* During initialization clusterize adds tabindex to force the browser to keep focus
* on the scrolling list, see issue #11
* Outline removes default browser's borders for focused elements.
*/
.clusterize-content {
outline: 0;
}
/* Centering message that appears when no data provided
*/
.clusterize-no-data td {
text-align: center;
}

36
src/less/base/list/clusterize.less

@ -1,36 +0,0 @@
/* max-height - the only parameter in this file that needs to be edited.
* Change it to suit your needs. The rest is recommended to leave as is.
*/
.clusterize-scroll{
overflow: auto;
}
/**
* Avoid vertical margins for extra tags
* Necessary for correct calculations when rows have nonzero vertical margins
*/
.clusterize-extra-row{
margin-top: 0 !important;
margin-bottom: 0 !important;
}
/* By default extra tag .clusterize-keep-parity added to keep parity of rows.
* Useful when used :nth-child(even/odd)
*/
.clusterize-extra-row.clusterize-keep-parity{
display: none;
}
/* During initialization clusterize adds tabindex to force the browser to keep focus
* on the scrolling list, see issue #11
* Outline removes default browser's borders for focused elements.
*/
.clusterize-content{
outline: 0;
}
/* Centering message that appears when no data provided
*/
.clusterize-no-data td{
text-align: center;
}
Loading…
Cancel
Save