UNPKG

forerunnerdb

Version:

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

349 lines (266 loc) 11.1 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. * * Infinilists work from views and those views cannot have an $orderBy * clause in them because that would slow down rendering. Instead if you * wish to have your data ordered you have to create a temporary collection * from which your view feeds from and pre-order the data before inserting * it into the temporary collection. * @class Infinilist * @requires AutoBind */ var Shared = window.ForerunnerDB.shared, View = Shared.modules.View; /** * Creates an infinilist instance. * @param {String} selector A jQuery selector targeting the element that * will contain the list items. * @param {String} 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); self.___fromChangeFunc = function () { // View data changed, recalculate total items count and check // that the currently displayed view data is correct by forcing // a scroll event after a height recalculation self.recalcHeight(); self.scroll(true); }; self.view.from().on('change', self.___fromChangeFunc); 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._itemHeight = val; self.virtualHeight = self.total * self._itemHeight; self.resize(); } return this.$super.apply(this, arguments); }); Infinilist.prototype.recalcHeight = function () { var self = this; if (self._state !== 'dropped') { self.total = self.view.from().count(self.options.countQuery); self.virtualHeight = self.total * self._itemHeight; self.resize(); } }; /** * 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; // Check if current range is different from existing range 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 (force) { 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 (force || 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 { if (callback) { callback(); } } return true; } return false; }; Infinilist.prototype.drop = function (callback) { var self = this; // Unlink the view from the dom self.view.unlink(self.itemContainer, self.template); // Stop listening for changes self.view.from().off('change', self.___fromChangeFunc); // 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; delete self.___fromChangeFunc; this.emit('drop', this); if (callback) { callback(false, true); } delete self._listeners; }; 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="Collection.html">Collection</a></li><li><a href="CollectionGroup.html">CollectionGroup</a></li><li><a href="Condition.html">Condition</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="Index2d.html">Index2d</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="MyModule.html">MyModule</a></li><li><a href="NodeApiClient.html">NodeApiClient</a></li><li><a href="NodeApiServer.html">NodeApiServer</a></li><li><a href="NodeRAS.html">NodeRAS</a></li><li><a href="Odm.html">Odm</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="Overview.html">Overview</a></li><li><a href="Overview_init.html">init</a></li><li><a href="Path.html">Path</a></li><li><a href="Persist.html">Persist</a></li><li><a href="Procedure.html">Procedure</a></li><li><a href="ReactorIO.html">ReactorIO</a></li><li><a href="Section.html">Section</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="Common.html">Common</a></li><li><a href="Constants.html">Constants</a></li><li><a href="Events.html">Events</a></li><li><a href="Matching.html">Matching</a></li><li><a href="Shared.html">Shared</a></li><li><a href="Sorting.html">Sorting</a></li><li><a href="Tags.html">Tags</a></li><li><a href="Triggers.html">Triggers</a></li><li><a href="Updating.html">Updating</a></li></ul><h3><a href="global.html">Global</a></h3> </nav> <br class="clear"> <footer> Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.4.0</a> on Thu Mar 01 2018 11:34:22 GMT+0000 (GMT) </footer> <script> prettyPrint(); </script> <script src="scripts/linenumber.js"> </script> </body> </html>