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