From afd1cdbbcb72616161b02a60450906b3cffeb230 Mon Sep 17 00:00:00 2001 From: guy Date: Mon, 22 May 2017 16:26:44 +0800 Subject: [PATCH] add --- bi/base.css | 32 -- bi/base.js | 502 ++++++------------ bi/core.js | 2 +- demo/js/core/abstract/demo.collection_view.js | 2 + demo/js/core/abstract/demo.grid_view.js | 4 + demo/js/core/abstract/demo.virtual_list.js | 9 +- docs/base.css | 32 -- docs/base.js | 502 ++++++------------ docs/core.js | 2 +- docs/demo.js | 15 +- src/base/collection/collection.js | 4 +- src/base/grid/grid.js | 12 +- src/base/list/clusterize.js | 329 ------------ src/base/list/virtuallist.js | 153 +++++- src/core/utils/prefixIntervalTree.js | 2 +- src/css/base/list/clusterize.css | 32 -- src/less/base/list/clusterize.less | 36 -- 17 files changed, 472 insertions(+), 1198 deletions(-) delete mode 100644 src/base/list/clusterize.js delete mode 100644 src/css/base/list/clusterize.css delete mode 100644 src/less/base/list/clusterize.less diff --git a/bi/base.css b/bi/base.css index a470b9f27..9a756c550 100644 --- a/bi/base.css +++ b/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(自定义颜色,用于特定场景) ****/ diff --git a/bi/base.js b/bi/base.js index cace48d53..40ab66d6f 100644 --- a/bi/base.js +++ b/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 = '', - 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 = '' + data + '
'; - 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("
" + i + "
"); - } - 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. diff --git a/bi/core.js b/bi/core.js index de5f6d5d1..746f642b8 100644 --- a/bi/core.js +++ b/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); }; diff --git a/demo/js/core/abstract/demo.collection_view.js b/demo/js/core/abstract/demo.collection_view.js index 2867e1ed9..ab175423f 100644 --- a/demo/js/core/abstract/demo.collection_view.js +++ b/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 { diff --git a/demo/js/core/abstract/demo.grid_view.js b/demo/js/core/abstract/demo.grid_view.js index 141b77bb5..bea89520b 100644 --- a/demo/js/core/abstract/demo.grid_view.js +++ b/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 () { diff --git a/demo/js/core/abstract/demo.virtual_list.js b/demo/js/core/abstract/demo.virtual_list.js index 792a5d828..1a8a754d8 100644 --- a/demo/js/core/abstract/demo.virtual_list.js +++ b/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 + }; + }) } } }); diff --git a/docs/base.css b/docs/base.css index a470b9f27..9a756c550 100644 --- a/docs/base.css +++ b/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(自定义颜色,用于特定场景) ****/ diff --git a/docs/base.js b/docs/base.js index cace48d53..40ab66d6f 100644 --- a/docs/base.js +++ b/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 = '', - 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 = '' + data + '
'; - 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("
" + i + "
"); - } - 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. diff --git a/docs/core.js b/docs/core.js index 4b8006604..8d2d7eb6c 100644 --- a/docs/core.js +++ b/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); }; diff --git a/docs/demo.js b/docs/demo.js index 7542faa5b..0c590a563 100644 --- a/docs/demo.js +++ b/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 + }; + }) } } }); diff --git a/src/base/collection/collection.js b/src/base/collection/collection.js index ab942f7bc..039b02ca4 100644 --- a/src/base/collection/collection.js +++ b/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, diff --git a/src/base/grid/grid.js b/src/base/grid/grid.js index 3ff3ff30f..556040c04 100644 --- a/src/base/grid/grid.js +++ b/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: [] diff --git a/src/base/list/clusterize.js b/src/base/list/clusterize.js deleted file mode 100644 index 63c59e2ba..000000000 --- a/src/base/list/clusterize.js +++ /dev/null @@ -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 = '', - 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 = '' + data + '
'; - 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; -})); \ No newline at end of file diff --git a/src/base/list/virtuallist.js b/src/base/list/virtuallist.js index aca28e197..2b164bd6b 100644 --- a/src/base/list/virtuallist.js +++ b/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("
" + i + "
"); + 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); \ No newline at end of file +BI.shortcut('bi.virtual_list', BI.VirtualList); diff --git a/src/core/utils/prefixIntervalTree.js b/src/core/utils/prefixIntervalTree.js index 82e1a50a7..043aa668b 100644 --- a/src/core/utils/prefixIntervalTree.js +++ b/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); }; diff --git a/src/css/base/list/clusterize.css b/src/css/base/list/clusterize.css deleted file mode 100644 index 2b8d17ee5..000000000 --- a/src/css/base/list/clusterize.css +++ /dev/null @@ -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; -} diff --git a/src/less/base/list/clusterize.less b/src/less/base/list/clusterize.less deleted file mode 100644 index 6aafca865..000000000 --- a/src/less/base/list/clusterize.less +++ /dev/null @@ -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; -} \ No newline at end of file