UNPKG

forerunnerdb

Version:

A NoSQL document store database for browsers and Node.js.

311 lines (237 loc) 9.4 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>JSDoc: Source: Infinilist.js</title> <script src="scripts/prettify/prettify.js"> </script> <script src="scripts/prettify/lang-css.js"> </script> <!--[if lt IE 9]> <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> <![endif]--> <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css"> <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css"> </head> <body> <div id="main"> <h1 class="page-title">Source: Infinilist.js</h1> <section> <article> <pre class="prettyprint source linenums"><code>"use strict"; /** * Provides scrolling lists with large data sets that behave in a very * performance-optimised fashion by controlling the DOM elements currently * on screen to ensure that only the visible elements are rendered and * all other elements are simulated by variable height divs at the top * and bottom of the scrolling list. * * This module requires that the AutoBind module is loaded before it * will work. * @class Infinilist * @requires AutoBind */ var Shared = window.ForerunnerDB.shared, View = Shared.modules.View; /** * Creates an infinilist instance. * @param {Selector} selector A jQuery selector targeting the element that * will contain the list items. * @param {Selector} template jQuery selector of the template to use when * rendering an individual list item. * @param {Object} options The options object. * @param {View} view The view to read data from. * @constructor */ var Infinilist = function (selector, template, options, view) { var self = this; selector = $(selector); options = options || {}; self.options = options.infinilist || {}; delete options.infinilist; self.skip = 0; self.limit = 0; self.ignoreScroll = false; self.previousScrollTop = 0; self.selector = selector; self.template = template; self.view = view; self.itemTopMargin = $("&lt;div class='il_topMargin'>&lt;/div>"); self.itemContainer = $("&lt;div class='il_items'>&lt;/div>"); self.itemBottomMargin = $("&lt;div class='il_bottomMargin'>&lt;/div>"); self.total = self.view.from().count(self.options.countQuery); self.itemHeight(self.options.itemHeight); selector.append(self.itemTopMargin); selector.append(self.itemContainer); selector.append(self.itemBottomMargin); self.resize(); view.link(self.itemContainer, template, options); selector.on('scroll', function () { // Debounce scroll event if (!self.scrollDebouceTimeout) { self.scrollDebouceTimeout = setTimeout(function () { self.scroll(); self.scrollDebouceTimeout = 0; }, 16); } }); $(window).on('resize', function () { // Debounce resize event if (self.resizeDebouceTimeout) { clearTimeout(self.resizeDebouceTimeout); } self.resizeDebouceTimeout = setTimeout(function () { self.resize(); }, 16); }); }; Shared.addModule('Infinilist', Infinilist); Shared.mixin(Infinilist.prototype, 'Mixin.Events'); Shared.synthesize(Infinilist.prototype, 'itemHeight', function (val) { var self = this; if (val !== undefined) { self.virtualHeight = self.total * val; self._itemHeight = val; self.resize(); } return this.$super.apply(this, arguments); }); /** * Handle screen resizing. */ Infinilist.prototype.resize = function () { var self = this, newHeight = self.selector.height(), skipCount, scrollTop = self.selector.scrollTop(); if (self.oldHeight !== newHeight) { self.oldHeight = newHeight; // Calculate number of visible items self.maxItemCount = Math.ceil(newHeight / self._itemHeight); skipCount = Math.floor(scrollTop / self._itemHeight); self.skip = skipCount; self.limit = self.maxItemCount + 1; self.view.queryOptions(self.currentRange()); self.itemBottomMargin.height(self.virtualHeight - (skipCount * self._itemHeight)- (self.maxItemCount * self._itemHeight)); } }; Infinilist.prototype.currentRange = function () { return { $skip: this.skip, $limit: this.limit }; }; Infinilist.prototype.scroll = function () { var self = this, delta, skipCount, scrollTop = self.selector.scrollTop(); // Get the current scroll position delta = scrollTop - self.previousScrollTop; self.previousScrollTop = scrollTop; // Check if a scroll change occurred if (delta !== 0) { // Determine the new item range skipCount = Math.floor(scrollTop / self._itemHeight); self.skip = skipCount; self.view.queryOptions(self.currentRange()); self.itemTopMargin.height(skipCount * self._itemHeight); self.itemBottomMargin.height(self.virtualHeight - (skipCount * self._itemHeight)- (self.maxItemCount * self._itemHeight)); } self.emit('scroll'); }; Infinilist.prototype.scrollToQuery = function (query, options, callback) { var self = this, result, index, orderOp = { $orderBy: self.view.queryOptions().$orderBy }, tmpColl, scrollPos; if (typeof options === 'function') { callback = options; options = undefined; } // Ensure options has properties we expect options = options || {}; options.$inc = options.$inc !== undefined ? options.$inc : 0; options.$incItem = options.$incItem !== undefined ? options.$incItem : 0; // Run query and get first matching record (with current sort) result = self.view.from().findOne(query, orderOp); // Find the position of the element inside the current view // based on the sort order tmpColl = self.view.db().collection('tmpSortCollection'); tmpColl.setData(self.view.from().find(self.view.query())); index = tmpColl.indexOf(result, orderOp); tmpColl.drop(); if (index > -1) { scrollPos = ((index + options.$incItem) * self._itemHeight) + options.$inc; scrollPos = scrollPos > 0 ? scrollPos : 0; if (self.selector.scrollTop() !== scrollPos) { if (callback) { self.once('scroll', function () { callback(); }); } // Scroll the main element to the position of the item self.selector.scrollTop(scrollPos); } else { callback(); } return true; } return false; }; Infinilist.prototype.drop = function () { var self = this; // Unlink the view from the dom self.view.unlink(self.itemContainer, self.template); // Set state to dropped self._state = 'dropped'; // Kill listeners self.selector.off('scroll'); $(window).off('resize'); // Remove references delete self.ignoreScroll; delete self.previousScrollTop; delete self._itemHeight; delete self.selector; delete self.template; delete self.view; delete self.itemTopMargin; delete self.itemContainer; delete self.itemBottomMargin; }; View.prototype.infinilist = function (targetSelector, templateSelector, options) { var target = window.jQuery(targetSelector); if (templateSelector === undefined) { return target.data('infinilist'); } target.data('infinilist', new Infinilist(targetSelector, templateSelector, options, this)); }; View.prototype.unInfinilist = function (targetSelector) { var target = window.jQuery(targetSelector); if (target.data('infinilist')) { target.data('infinilist').drop(); target.removeData('infinilist'); return true; } return false; }; Shared.moduleFinished('AutoBind', function () { Shared.finishModule('Infinilist'); }); module.exports = Infinilist;</code></pre> </article> </section> </div> <nav> <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ActiveBucket.html">ActiveBucket</a></li><li><a href="Angular.html">Angular</a></li><li><a href="AutoBind.html">AutoBind</a></li><li><a href="BinaryTree.html">BinaryTree</a></li><li><a href="Collection.html">Collection</a></li><li><a href="CollectionGroup.html">CollectionGroup</a></li><li><a href="Core.html">Core</a></li><li><a href="Db.html">Db</a></li><li><a href="Document.html">Document</a></li><li><a href="Grid.html">Grid</a></li><li><a href="Highchart.html">Highchart</a></li><li><a href="IndexBinaryTree.html">IndexBinaryTree</a></li><li><a href="IndexHashMap.html">IndexHashMap</a></li><li><a href="Infinilist.html">Infinilist</a></li><li><a href="KeyValueStore.html">KeyValueStore</a></li><li><a href="Metrics.html">Metrics</a></li><li><a href="OldView.html">OldView</a></li><li><a href="Operation.html">Operation</a></li><li><a href="Overload.html">Overload</a></li><li><a href="Path.html">Path</a></li><li><a href="Persist.html">Persist</a></li><li><a href="ReactorIO.html">ReactorIO</a></li><li><a href="Serialiser.html">Serialiser</a></li><li><a href="Shared.overload.html">overload</a></li><li><a href="View.html">View</a></li></ul><h3>Mixins</h3><ul><li><a href="ChainReactor.html">ChainReactor</a></li><li><a href="crcTable.html">crcTable</a></li><li><a href="Shared.html">Shared</a></li></ul><h3>Global</h3><ul><li><a href="global.html#%2522boolean,function%2522">"boolean, function"</a></li><li><a href="global.html#%2522string,*,function%2522">"string, *, function"</a></li><li><a href="global.html#%2522string,function%2522">"string, function"</a></li><li><a href="global.html#boolean">boolean</a></li><li><a href="global.html#function">function</a></li></ul> </nav> <br class="clear"> <footer> Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.3.3</a> on Thu Nov 19 2015 13:31:32 GMT+0000 (GMT) </footer> <script> prettyPrint(); </script> <script src="scripts/linenumber.js"> </script> </body> </html>