1 |
|
2 |
|
3 |
|
4 |
|
5 | ;(function(name, definition) {
|
6 | if (typeof module != 'undefined') module.exports = definition();
|
7 | else if (typeof define == 'function' && typeof define.amd == 'object') define(definition);
|
8 | else this[name] = definition();
|
9 | }('Clusterize', function() {
|
10 | "use strict"
|
11 |
|
12 |
|
13 |
|
14 | var ie = (function(){
|
15 | for( var v = 3,
|
16 | el = document.createElement('b'),
|
17 | all = el.all || [];
|
18 | el.innerHTML = '<!--[if gt IE ' + (++v) + ']><i><![endif]-->',
|
19 | all[0];
|
20 | ){}
|
21 | return v > 4 ? v : document.documentMode;
|
22 | }()),
|
23 | is_mac = navigator.platform.toLowerCase().indexOf('mac') + 1;
|
24 | var Clusterize = function(data) {
|
25 | if( ! (this instanceof Clusterize))
|
26 | return new Clusterize(data);
|
27 | var self = this;
|
28 |
|
29 | var defaults = {
|
30 | rows_in_block: 50,
|
31 | blocks_in_cluster: 4,
|
32 | tag: null,
|
33 | show_no_data_row: true,
|
34 | no_data_class: 'clusterize-no-data',
|
35 | no_data_text: 'No data',
|
36 | keep_parity: true,
|
37 | callbacks: {}
|
38 | }
|
39 |
|
40 |
|
41 | self.options = {};
|
42 | var options = ['rows_in_block', 'blocks_in_cluster', 'show_no_data_row', 'no_data_class', 'no_data_text', 'keep_parity', 'tag', 'callbacks'];
|
43 | for(var i = 0, option; option = options[i]; i++) {
|
44 | self.options[option] = typeof data[option] != 'undefined' && data[option] != null
|
45 | ? data[option]
|
46 | : defaults[option];
|
47 | }
|
48 |
|
49 | var elems = ['scroll', 'content'];
|
50 | for(var i = 0, elem; elem = elems[i]; i++) {
|
51 | self[elem + '_elem'] = data[elem + 'Id']
|
52 | ? document.getElementById(data[elem + 'Id'])
|
53 | : data[elem + 'Elem'];
|
54 | if( ! self[elem + '_elem'])
|
55 | throw new Error("Error! Could not find " + elem + " element");
|
56 | }
|
57 |
|
58 |
|
59 | if( ! self.content_elem.hasAttribute('tabindex'))
|
60 | self.content_elem.setAttribute('tabindex', 0);
|
61 |
|
62 |
|
63 | var rows = isArray(data.rows)
|
64 | ? data.rows
|
65 | : self.fetchMarkup(),
|
66 | cache = {},
|
67 | scroll_top = self.scroll_elem.scrollTop;
|
68 |
|
69 |
|
70 | self.insertToDOM(rows, cache);
|
71 |
|
72 |
|
73 | self.scroll_elem.scrollTop = scroll_top;
|
74 |
|
75 |
|
76 | var last_cluster = false,
|
77 | scroll_debounce = 0,
|
78 | pointer_events_set = false,
|
79 | scrollEv = function() {
|
80 |
|
81 | if (is_mac) {
|
82 | if( ! pointer_events_set) self.content_elem.style.pointerEvents = 'none';
|
83 | pointer_events_set = true;
|
84 | clearTimeout(scroll_debounce);
|
85 | scroll_debounce = setTimeout(function () {
|
86 | self.content_elem.style.pointerEvents = 'auto';
|
87 | pointer_events_set = false;
|
88 | }, 50);
|
89 | }
|
90 | if (last_cluster != (last_cluster = self.getClusterNum()))
|
91 | self.insertToDOM(rows, cache);
|
92 | if (self.options.callbacks.scrollingProgress)
|
93 | self.options.callbacks.scrollingProgress(self.getScrollProgress());
|
94 | },
|
95 | resize_debounce = 0,
|
96 | resizeEv = function() {
|
97 | clearTimeout(resize_debounce);
|
98 | resize_debounce = setTimeout(self.refresh, 100);
|
99 | }
|
100 | on('scroll', self.scroll_elem, scrollEv);
|
101 | on('resize', window, resizeEv);
|
102 |
|
103 |
|
104 | self.destroy = function(clean) {
|
105 | off('scroll', self.scroll_elem, scrollEv);
|
106 | off('resize', window, resizeEv);
|
107 | self.html((clean ? self.generateEmptyRow() : rows).join(''));
|
108 | }
|
109 | self.refresh = function(force) {
|
110 | if(self.getRowsHeight(rows) || force) self.update(rows);
|
111 | }
|
112 | self.update = function(new_rows) {
|
113 | rows = isArray(new_rows)
|
114 | ? new_rows
|
115 | : [];
|
116 | var scroll_top = self.scroll_elem.scrollTop;
|
117 |
|
118 | if(rows.length * self.options.item_height < scroll_top) {
|
119 | self.scroll_elem.scrollTop = 0;
|
120 | last_cluster = 0;
|
121 | }
|
122 | self.insertToDOM(rows, cache);
|
123 | self.scroll_elem.scrollTop = scroll_top;
|
124 | }
|
125 | self.clear = function() {
|
126 | self.update([]);
|
127 | }
|
128 | self.getRowsAmount = function() {
|
129 | return rows.length;
|
130 | }
|
131 | self.getScrollProgress = function() {
|
132 | return this.options.scroll_top / (rows.length * this.options.item_height) * 100 || 0;
|
133 | }
|
134 |
|
135 | var add = function(where, _new_rows) {
|
136 | var new_rows = isArray(_new_rows)
|
137 | ? _new_rows
|
138 | : [];
|
139 | if( ! new_rows.length) return;
|
140 | rows = where == 'append'
|
141 | ? rows.concat(new_rows)
|
142 | : new_rows.concat(rows);
|
143 | self.insertToDOM(rows, cache);
|
144 | }
|
145 | self.append = function(rows) {
|
146 | add('append', rows);
|
147 | }
|
148 | self.prepend = function(rows) {
|
149 | add('prepend', rows);
|
150 | }
|
151 | }
|
152 |
|
153 | Clusterize.prototype = {
|
154 | constructor: Clusterize,
|
155 | // fetch existing markup
|
156 | fetchMarkup: function() {
|
157 | var rows = [], rows_nodes = this.getChildNodes(this.content_elem);
|
158 | while (rows_nodes.length) {
|
159 | rows.push(rows_nodes.shift().outerHTML);
|
160 | }
|
161 | return rows;
|
162 | },
|
163 |
|
164 | exploreEnvironment: function(rows, cache) {
|
165 | var opts = this.options;
|
166 | opts.content_tag = this.content_elem.tagName.toLowerCase();
|
167 | if( ! rows.length) return;
|
168 | if(ie && ie <= 9 && ! opts.tag) opts.tag = rows[0].match(/<([^>\s/]*)/)[1].toLowerCase();
|
169 | if(this.content_elem.children.length <= 1) cache.data = this.html(rows[0] + rows[0] + rows[0]);
|
170 | if( ! opts.tag) opts.tag = this.content_elem.children[0].tagName.toLowerCase();
|
171 | this.getRowsHeight(rows);
|
172 | },
|
173 | getRowsHeight: function(rows) {
|
174 | var opts = this.options,
|
175 | prev_item_height = opts.item_height;
|
176 | opts.cluster_height = 0;
|
177 | if( ! rows.length) return;
|
178 | var nodes = this.content_elem.children;
|
179 | if( ! nodes.length) return;
|
180 | var node = nodes[Math.floor(nodes.length / 2)];
|
181 | opts.item_height = node.offsetHeight;
|
182 |
|
183 | if(opts.tag == 'tr' && getStyle('borderCollapse', this.content_elem) != 'collapse')
|
184 | opts.item_height += parseInt(getStyle('borderSpacing', this.content_elem), 10) || 0;
|
185 |
|
186 | if(opts.tag != 'tr') {
|
187 | var marginTop = parseInt(getStyle('marginTop', node), 10) || 0;
|
188 | var marginBottom = parseInt(getStyle('marginBottom', node), 10) || 0;
|
189 | opts.item_height += Math.max(marginTop, marginBottom);
|
190 | }
|
191 | opts.block_height = opts.item_height * opts.rows_in_block;
|
192 | opts.rows_in_cluster = opts.blocks_in_cluster * opts.rows_in_block;
|
193 | opts.cluster_height = opts.blocks_in_cluster * opts.block_height;
|
194 | return prev_item_height != opts.item_height;
|
195 | },
|
196 |
|
197 | getClusterNum: function () {
|
198 | this.options.scroll_top = this.scroll_elem.scrollTop;
|
199 | return Math.floor(this.options.scroll_top / (this.options.cluster_height - this.options.block_height)) || 0;
|
200 | },
|
201 |
|
202 | generateEmptyRow: function() {
|
203 | var opts = this.options;
|
204 | if( ! opts.tag || ! opts.show_no_data_row) return [];
|
205 | var empty_row = document.createElement(opts.tag),
|
206 | no_data_content = document.createTextNode(opts.no_data_text), td;
|
207 | empty_row.className = opts.no_data_class;
|
208 | if(opts.tag == 'tr') {
|
209 | td = document.createElement('td');
|
210 |
|
211 | td.colSpan = 100;
|
212 | td.appendChild(no_data_content);
|
213 | }
|
214 | empty_row.appendChild(td || no_data_content);
|
215 | return [empty_row.outerHTML];
|
216 | },
|
217 |
|
218 | generate: function (rows, cluster_num) {
|
219 | var opts = this.options,
|
220 | rows_len = rows.length;
|
221 | if (rows_len < opts.rows_in_block) {
|
222 | return {
|
223 | top_offset: 0,
|
224 | bottom_offset: 0,
|
225 | rows_above: 0,
|
226 | rows: rows_len ? rows : this.generateEmptyRow()
|
227 | }
|
228 | }
|
229 | var items_start = Math.max((opts.rows_in_cluster - opts.rows_in_block) * cluster_num, 0),
|
230 | items_end = items_start + opts.rows_in_cluster,
|
231 | top_offset = Math.max(items_start * opts.item_height, 0),
|
232 | bottom_offset = Math.max((rows_len - items_end) * opts.item_height, 0),
|
233 | this_cluster_rows = [],
|
234 | rows_above = items_start;
|
235 | if(top_offset < 1) {
|
236 | rows_above++;
|
237 | }
|
238 | for (var i = items_start; i < items_end; i++) {
|
239 | rows[i] && this_cluster_rows.push(rows[i]);
|
240 | }
|
241 | return {
|
242 | top_offset: top_offset,
|
243 | bottom_offset: bottom_offset,
|
244 | rows_above: rows_above,
|
245 | rows: this_cluster_rows
|
246 | }
|
247 | },
|
248 | renderExtraTag: function(class_name, height) {
|
249 | var tag = document.createElement(this.options.tag),
|
250 | clusterize_prefix = 'clusterize-';
|
251 | tag.className = [clusterize_prefix + 'extra-row', clusterize_prefix + class_name].join(' ');
|
252 | height && (tag.style.height = height + 'px');
|
253 | return tag.outerHTML;
|
254 | },
|
255 |
|
256 | insertToDOM: function(rows, cache) {
|
257 |
|
258 | if( ! this.options.cluster_height) {
|
259 | this.exploreEnvironment(rows, cache);
|
260 | }
|
261 | var data = this.generate(rows, this.getClusterNum()),
|
262 | this_cluster_rows = data.rows.join(''),
|
263 | this_cluster_content_changed = this.checkChanges('data', this_cluster_rows, cache),
|
264 | top_offset_changed = this.checkChanges('top', data.top_offset, cache),
|
265 | only_bottom_offset_changed = this.checkChanges('bottom', data.bottom_offset, cache),
|
266 | callbacks = this.options.callbacks,
|
267 | layout = [];
|
268 |
|
269 | if(this_cluster_content_changed || top_offset_changed) {
|
270 | if(data.top_offset) {
|
271 | this.options.keep_parity && layout.push(this.renderExtraTag('keep-parity'));
|
272 | layout.push(this.renderExtraTag('top-space', data.top_offset));
|
273 | }
|
274 | layout.push(this_cluster_rows);
|
275 | data.bottom_offset && layout.push(this.renderExtraTag('bottom-space', data.bottom_offset));
|
276 | callbacks.clusterWillChange && callbacks.clusterWillChange();
|
277 | this.html(layout.join(''));
|
278 | this.options.content_tag == 'ol' && this.content_elem.setAttribute('start', data.rows_above);
|
279 | this.content_elem.style['counter-increment'] = 'clusterize-counter ' + (data.rows_above-1);
|
280 | callbacks.clusterChanged && callbacks.clusterChanged();
|
281 | } else if(only_bottom_offset_changed) {
|
282 | this.content_elem.lastChild.style.height = data.bottom_offset + 'px';
|
283 | }
|
284 | },
|
285 |
|
286 | html: function(data) {
|
287 | var content_elem = this.content_elem;
|
288 | if(ie && ie <= 9 && this.options.tag == 'tr') {
|
289 | var div = document.createElement('div'), last;
|
290 | div.innerHTML = '<table><tbody>' + data + '</tbody></table>';
|
291 | while((last = content_elem.lastChild)) {
|
292 | content_elem.removeChild(last);
|
293 | }
|
294 | var rows_nodes = this.getChildNodes(div.firstChild.firstChild);
|
295 | while (rows_nodes.length) {
|
296 | content_elem.appendChild(rows_nodes.shift());
|
297 | }
|
298 | } else {
|
299 | content_elem.innerHTML = data;
|
300 | }
|
301 | },
|
302 | getChildNodes: function(tag) {
|
303 | var child_nodes = tag.children, nodes = [];
|
304 | for (var i = 0, ii = child_nodes.length; i < ii; i++) {
|
305 | nodes.push(child_nodes[i]);
|
306 | }
|
307 | return nodes;
|
308 | },
|
309 | checkChanges: function(type, value, cache) {
|
310 | var changed = value != cache[type];
|
311 | cache[type] = value;
|
312 | return changed;
|
313 | }
|
314 | }
|
315 |
|
316 |
|
317 | function on(evt, element, fnc) {
|
318 | return element.addEventListener ? element.addEventListener(evt, fnc, false) : element.attachEvent("on" + evt, fnc);
|
319 | }
|
320 | function off(evt, element, fnc) {
|
321 | return element.removeEventListener ? element.removeEventListener(evt, fnc, false) : element.detachEvent("on" + evt, fnc);
|
322 | }
|
323 | function isArray(arr) {
|
324 | return Object.prototype.toString.call(arr) === '[object Array]';
|
325 | }
|
326 | function getStyle(prop, elem) {
|
327 | return window.getComputedStyle ? window.getComputedStyle(elem)[prop] : elem.currentStyle[prop];
|
328 | }
|
329 |
|
330 | return Clusterize;
|
331 | })); |
\ | No newline at end of file |