123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187 |
- /**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- var App = require('app');
- /**
- * @typedef {Object} InfiniteScrollMixinOptions
- * @property {String} appendHtml html to append when scroll ends and callback executed. It's common
- * that root node has unique <code>id</code> or <code>class</code> attributes.
- * @property {Function} callback function to execute when scroll ends. This function should return
- * <code>$.Deferred().promise()</code> instance
- * @property {Function} onReject function to execute when <code>callback</code> rejected.
- */
- /**
- * @mixin App.InfiniteScrollMixin
- * This mixin provides methods to attach infinite scroll to specific scrollable element.
- *
- * Usage:
- * <code>
- * // mix it
- * var myView = Em.View.extend(App.InfiniteScrollMixin, {
- * didInsertElement: function() {
- * // call method infiniteScrollInit
- * this.infiniteScrollInit($('.some-scrollable'), {
- * callback: this.someCallbacOnEndReached
- * });
- * }
- * });
- * </code>
- *
- */
- App.InfiniteScrollMixin = Ember.Mixin.create({
- /**
- * Stores callback execution progress.
- *
- * @type {Boolean}
- */
- _infiniteScrollCallbackInProgress: false,
- /**
- * Stores HTMLElement infinite scroll initiated on.
- *
- * @type {HTMLElement}
- */
- _infiniteScrollEl: null,
- /**
- * Default options for infinite scroll.
- *
- * @type {InfiniteScrollMixinOptions}
- */
- _infiniteScrollDefaults: {
- appendHtml: '<div id="infinite-scroll-append"><i class="icon-spinner icon-spin"></i></div>',
- callback: function() { return $.Deferred().resolve().promise(); },
- onReject: function() {},
- onResolve: function() {}
- },
- /**
- * Determines that there is no data to load on next callback call.
- *
- */
- _infiniteScrollMoreData: true,
- /**
- * Initialize infinite scroll on specified HTMLElement.
- *
- * @param {HTMLElement} el DOM element to attach infinite scroll.
- * @param {InfiniteScrollMixinOptions} opts
- */
- infiniteScrollInit: function(el, opts) {
- var options = $.extend({}, this.get('_infiniteScrollDefaults'), opts || {});
- this.set('_infiniteScrollEl', el);
- this.get('_infiniteScrollEl').on('scroll', this._infiniteScrollHandler.bind(this));
- this.get('_infiniteScrollEl').on('infinite-scroll-end', this._infiniteScrollEndHandler(options).bind(this));
- },
- /**
- * Handler executed on scrolling.
- * @param {jQuery.Event} e
- */
- _infiniteScrollHandler: function(e) {
- var el = $(e.target);
- var height = el.get(0).clientHeight;
- var scrollHeight = el.prop('scrollHeight');
- var endPoint = scrollHeight - height;
- if (endPoint === el.scrollTop() && !this.get('_infiniteScrollCallbackInProgress')) {
- el.trigger('infinite-scroll-end');
- }
- },
- /**
- * Handler called when scroll ends.
- *
- * @param {InfiniteScrollMixinOptions} options
- * @return {Function}
- */
- _infiniteScrollEndHandler: function(options) {
- return function(e) {
- var self = this;
- if (this.get('_infiniteScrollCallbackInProgress') || !this.get('_infiniteScrollMoreData')) return;
- this._infiniteScrollAppendHtml(options.appendHtml);
- // always scroll to bottom
- this.get('_infiniteScrollEl').scrollTop(this.get('_infiniteScrollEl').get(0).scrollHeight);
- this.set('_infiniteScrollCallbackInProgress', true);
- options.callback().then(function() {
- options.onResolve();
- }, function() {
- options.onReject();
- }).always(function() {
- self.set('_infiniteScrollCallbackInProgress', false);
- self._infiniteScrollRemoveHtml(options.appendHtml);
- });
- }.bind(this);
- },
- /**
- * Helper function to append String as html node to.
- * @param {String} htmlString string to append
- */
- _infiniteScrollAppendHtml: function(htmlString) {
- this.get('_infiniteScrollEl').append(htmlString);
- },
- /**
- * Remove HTMLElement by specified string that can be converted to html. HTMLElement root node
- * should have unique <code>id</code> or <code>class</code> attribute to avoid removing additional
- * elements.
- *
- * @param {String} htmlString string to remove
- */
- _infiniteScrollRemoveHtml: function(htmlString) {
- this.get('_infiniteScrollEl').find(this._infiniteScrollGetSelector(htmlString)).remove();
- },
- /**
- * Get root node selector.
- * <code>id</code> attribute has higher priority and will return if found.
- * <code>class</code> if no <code>id</code> attribute found <code>class</code> attribute
- * will be used.
- *
- * @param {String} htmlString string processed as HTML
- * @return {[type]} [description]
- */
- _infiniteScrollGetSelector: function(htmlString) {
- var html = $(htmlString);
- var elId = html.attr('id');
- var elClass = (html.attr('class') || '').split(' ').join('.');
- html = null;
- return !!elId ? '#' + elId : '.' + elClass;
- },
- /**
- * Remove infinite scroll.
- * Unbind all listeners.
- */
- infiniteScrollDestroy: function() {
- this.get('_infiniteScrollEl').off('scroll', this._infiniteScrollHandler);
- this.get('_infiniteScrollEl').off('infinite-scroll-end', this._infiniteScrollHandler);
- this.set('_infiniteScrollEl', null);
- },
- /**
- * Set if there is more data to load on next scroll end event.
- * @param {boolean} isAvailable <code>true</code> when there are more data to fetch
- */
- infiniteScrollSetDataAvailable: function(isAvailable) {
- this.set('_infiniteScrollMoreData', isAvailable);
- }
- });
|