app/assets/javascripts/ckeditor/filebrowser/javascripts/jquery.endless-scroll.js
/**
* Author: Sergey Kukunin
* See: https://github.com/Kukunin/jquery-endless-scroll
*/
(function($) {
'use strict';
// Is pushState supported by this browser?
$.support.pushState =
window.history && window.history.pushState && window.history.replaceState &&
// pushState isn't reliable on iOS until 5.
!navigator.userAgent.match(/((iPod|iPhone|iPad).+\bOS\s+[1-4]|WebApps\/.+CFNetwork)/)
//Declaration of modules
var scrollModule = {
init: function(options, obj) {
obj.options = $.extend({
scrollContainer: window,
scrollPadding: 100,
scrollEventDelay: 300
}, options);
this.options = obj.options;
this.container = obj.container;
obj.scrollModule = this;
this._toplock = true;
this._bottomlock = true;
this.scrollContainer = $(this.options.scrollContainer);
this.updateEntities();
this.sortMarkers();
this.currentPage = null;
this.container.on("jes:afterPageLoad", $.proxy(function(event, url, placement) {
this.updateEntities();
this.sortMarkers();
this.checkMarker();
if ( placement == "top" ) {
//Get offset between first and second pages
var offset = this.markers[1].top,
scrollTop = this.scrollContainer.scrollTop();
this.scrollContainer.scrollTop(scrollTop + offset);
}
}, this));
this.bind();
},
updateEntities: function() {
this.entities = $(this.options.entity, this.container);
},
sortMarkers: function() {
var markers = [];
$(".jes-marker", this.container).each(function() {
markers.push({ top: $(this).position().top, url: $(this).data("jesUrl") });
});
this.markers = markers;
},
//Check, whether user scrolled to another page
checkMarker: function() {
var newPage = this.markers[0],
scrollTop = this.scrollContainer.scrollTop();
//Determine, what is current page
for ( var i = 1; i < this.markers.length; i++ ) {
if ( scrollTop > this.markers[i].top ) {
newPage = this.markers[i];
} else {
break;
}
}
if ( newPage.url != this.currentPage ) {
this.currentPage = newPage.url;
$(this.container).trigger("jes:scrollToPage", newPage.url);
}
},
bind: function() {
this.scrollContainer.on("scroll.jes", $.proxy(function(event) {
if ( this._tId ) { return; }
this.scrollHandler(event);
//Clean up mark
this._tId= setTimeout($.proxy(function(){
this._tId = null;
},this), this.options.scrollEventDelay);
}, this));
},
unbind: function() {
$(this.options.scrollContainer).off("scroll.jes");
},
scrollHandler: function(ev) {
var $scrollable = this.scrollContainer,
$entities = this.entities,
$firstEntity = $entities.first(),
$lastEntity = $entities.last();
var scrollTop = $scrollable.scrollTop(),
height = $scrollable.height(),
scrollBottom = scrollTop + height;
var topEntityPosition = $firstEntity.position().top,
topTarget = topEntityPosition + this.options.scrollPadding,
bottomEntityPosition = $lastEntity.outerHeight() + $lastEntity.position().top,
bottomTarget = bottomEntityPosition - this.options.scrollPadding;
//Don't trigger event again, if already fired
//Visitor have to leave the area and get back to fire event again
//Process top threshold
if ( scrollTop < topTarget ) {
if ( !this._toplock ) {
$(this.container).trigger("jes:topThreshold");
this._toplock = true;
}
} else {
this._toplock = false;
}
//Process bottom threshold
if ( scrollBottom > bottomTarget ) {
if ( !this._bottomlock ) {
$(this.container).trigger("jes:bottomThreshold");
this._bottomlock = true;
}
} else {
this._bottomlock = false;
}
this.checkMarker();
}
}
var ajaxModule = {
init: function(options, obj) {
obj.options = $.extend({
}, options);
this.options = obj.options;
this.container = obj.container;
//markers
this.setMarker($(this.options.entity, this.container).first(), location.href);
obj.ajaxModule = this;
},
loadPage: function(url, placement, callback) {
//The hash with methods list
//depends from placement
var actions = {
top: {
find: 'first',
inject: 'insertBefore'
},
bottom: {
find: 'last',
inject: 'insertAfter'
}
},
action = actions[placement];
this.container.trigger("jes:beforePageLoad", [url, placement]);
//Make AJAX query
$.get(url, null, $.proxy(function (_data) {
var data = $("<div>").html(_data),
containerSelector = this.options.container,
container = $(containerSelector, data).first();
if ( container.length ) {
//Find entities
var entities = container.find(this.options.entity);
if ( placement == "bottom" ) {
//Remove duplicated (staled) entities from page
entities.each(function(i) {
var id = $(this).attr("id");
if ( id ) {
$('#' + id, this.container).remove();
}
});
}
//Find the cursor
var cursor = $(this.options.entity, containerSelector)[action.find]();
//Find and insert entities
entities[action.inject](cursor);
this.setMarker(entities.first(), url);
}
if ( $.isFunction(callback) ) {
callback(data);
}
this.container.trigger("jes:afterPageLoad", [url, placement, data, entities]);
}, this), 'html');
},
setMarker: function(entity, url) {
entity.addClass("jes-marker").data("jesUrl", url);
}
}
var navigationModule = {
init: function(options, obj) {
obj.options = $.extend({
nextPage: ".pagination a[rel=next]",
previousPage: ".pagination a[rel=previous]"
}, options);
this.options = obj.options;
this.container = obj.container;
$.each([{
selector: this.options.nextPage,
event: "jes:bottomThreshold.navigation",
placement: 'bottom'
}, {
selector: this.options.previousPage,
event: "jes:topThreshold.navigation",
placement: 'top'
}], $.proxy(function(i, v) {
v.element = $(v.selector);
if ( v.element.length ) {
var url = v.element.prop("href"),
lock = false;
var handler = function() {
//this object is container
if ( lock || !url ) return;
lock = true;
obj.ajaxModule.loadPage(url, v.placement, $.proxy(function( data ) {
//Search new next link
var newElement = $(v.selector, $(data));
if ( newElement.length ) {
//Update URL and remove lock
lock = false;
url = newElement.prop("href");
v.element.attr("href", url);
} else {
//Remove event at all
$(this).off(v.event);
v.element.remove();
}
}, this));
};
$(this.container).on(v.event, handler);
$(v.element).on("click", $.proxy(function(ev) {
ev.preventDefault();
handler.apply(this.container)
}, this));
}
},this));
}
}
var pushStateHistory = {
init: function(options, obj) {
if ( !$.support.pushState ) {
return;
}
obj.container.on("jes:scrollToPage", function(event, url) {
history.replaceState({}, null, url);
});
}
}
$.endlessScroll = function(options) {
//Initialize modules
this.options = $.extend(true, {
container: "#container",
entity: ".entity",
_modules: [ ajaxModule, scrollModule, navigationModule, pushStateHistory ],
modules: []
}, options);
this.container = $(this.options.container);
if ( !this.container.length ) {
throw "Container for endless scroll isn't available on the page";
return;
}
//Merge custom options with default
$.merge(this.options.modules, this.options._modules);
//Init modules
this.options.modules.forEach($.proxy(function(module) {
module.init(this.options, this);
},this));
return this;
}
})(jQuery);