repoindex/web/js/pagination.js

226 lines
9.3 KiB
JavaScript

var repoindex = (function(repoindex) {
// Adds a bootstrap pagination to the HTML element with the specified id
repoindex.Pagination = function(containerId) {
// basic initialization
this.containerId = containerId;
this.containerElement = document.getElementById(containerId);
this.currentElement = undefined;
this.elements = [];
this.entriesPerPage = 20;
this.entryCount = 0;
this.visibleElementsBeforeAfter = 7;
// define a default function which will be called when a pageElement is selected
this.pageSelected = function() {};
this.internalPageSelected = function(pageElement) {
repoindex.pageManager.rehash(pageElement && pageElement.pageIndex > 0 ? function(hash) {
hash[1].p = pageElement.pageIndex;
} : function(hash) {
delete hash[1].p;
});
this.pageSelected(pageElement);
};
// define a function to create page elements
this.createPageElement = function() {
var pageElement = document.createElement("li");
pageElement.pagination = this;
var aElement = document.createElement("a");
aElement.href = "#";
aElement.pageElement = pageElement;
pageElement.appendChild(aElement);
pageElement.setVisible = function(visible) {
this.style.display = visible ? "inline" : "none";
};
pageElement.setEnabled = function(enabled) {
this.className = enabled ? "" : "disabled";
};
pageElement.setActive = function(active) {
this.className = active ? "active" : "";
};
pageElement.setVisible(false);
return pageElement;
};
// initialize the "previous element"
this.previousElement = this.createPageElement();
this.previousElement.firstChild.setAttribute("aria-label", "Previous");
this.previousElement.firstChild.innerHTML = '<span aria-hidden="true">&laquo;</span>';
this.previousElement.select = function() {
var currentElement = this.pagination.currentElement;
if(currentElement && currentElement.previousElement) {
currentElement.previousElement.select();
}
};
this.previousElement.firstChild.onclick = function() {
this.pageElement.select();
return false;
};
this.previousElement.setVisible(true);
this.containerElement.appendChild(this.previousElement);
// initialize the "next element"
this.nextElement = this.createPageElement();
this.nextElement.firstChild.setAttribute("aria-label", "Next");
this.nextElement.firstChild.innerHTML = '<span aria-hidden="true">&raquo;</span>';
this.nextElement.select = function() {
var currentElement = this.pagination.currentElement;
if(currentElement && currentElement.nextElement) {
currentElement.nextElement.select();
}
};
// initialize the "settings element"
this.nextElement.firstChild.onclick = function() {
this.pageElement.select();
return false;
};
this.nextElement.setVisible(true);
this.containerElement.appendChild(this.nextElement);
// provide a function to append a page element
this.appendPage = function(name) {
// create and initiate the new page element
var pageElement = this.createPageElement();
// provide a select function for the page element (will be called when the page element
// is clicked)
pageElement.select = function() {
// update active/inactive status and visibility of all page elements
var pagination = this.pagination;
var updateVisibility = function(index, visible) {
var lowest = 0;
var highest = pagination.elements.length - 1;
var low = index - pagination.visibleElementsBeforeAfter;
var high = index + pagination.visibleElementsBeforeAfter;
if(low < lowest) {
high = Math.min(highest, high + lowest - low);
low = lowest;
} else if(high > highest) {
low = Math.max(lowest, low + highest - high);
high = highest;
}
for(var i = low; i <= high; ++i) {
pagination.elements[i].setVisible(visible);
}
}
if(pagination.currentElement) {
updateVisibility(pagination.currentElement.pageIndex, false);
pagination.currentElement.setActive(false);
}
updateVisibility(this.pageIndex, true);
this.setActive(true);
// update status of the containing pagination object
pagination.currentElement = this;
pagination.previousElement.setEnabled(this !== pagination.elements.first());
pagination.nextElement.setEnabled(this !== pagination.elements.last());
// call the pageSelected function to allow the managing objects to handle the page selection
pagination.internalPageSelected(this);
};
// invokes the specified function for each index of the currently visible range
pageElement.forRange = function(func, maxIndex) {
for(var i = this.pageIndex * this.pagination.entriesPerPage, pagesLeft = this.pagination.entriesPerPage;
pagesLeft > 0 && i < maxIndex; ++i, --pagesLeft) {
func(i);
}
};
// further initialization of the page element
pageElement.pageName = name;
var aElement = pageElement.firstChild;
aElement.appendChild(document.createTextNode(name));
// invoke the select method of the page element when clicked
aElement.onclick = function() {
this.pageElement.select();
return false;
};
if((pageElement.previousElement = this.elements.last())) {
pageElement.pageIndex = pageElement.previousElement.pageIndex + 1;
pageElement.previousElement.nextElement = pageElement;
} else {
pageElement.pageIndex = 0;
}
this.containerElement.insertBefore(pageElement, this.nextElement);
this.elements.push(pageElement);
};
// provide a function to append a range of page elements
this.appendPageRange = function(from, to) {
for(var i = from; i <= to; ++i) {
this.appendPage(i);
}
};
// provide a function to void selection
this.voidSelection = function() {
this.currentElement = undefined;
this.previousElement.setEnabled(false);
this.nextElement.setEnabled(false);
this.internalPageSelected();
}
// provide a function to remove all page elements
this.removePages = function(startIndex) {
if(!startIndex) {
startIndex = 0;
}
for(var i = startIndex, end = this.elements.length; i < end; ++i) {
this.containerElement.removeChild(this.elements[i]);
}
if(startIndex !== 0) {
this.elements.splice(startIndex, this.elements.length - startIndex);
this.elements.last().nextElement = null; // ensure the last element has no next element anymore
} else {
this.elements = [];
}
this.currentElement = undefined;
};
// provide a function to update the pagination (after properties such as the entryCount have been changed)
this.update = function() {
var requiredPages = Math.ceil(this.entryCount / this.entriesPerPage);
var currentPages = this.elements.length;
if(requiredPages < currentPages) {
this.removePages(requiredPages);
} else if(currentPages < requiredPages) {
this.appendPageRange(currentPages + 1, requiredPages);
}
// select the noted page or first element or void selection if there are no pages
if(this.notedPageIndex && (this.notedPageIndex < this.elements.length)) {
this.elements[this.notedPageIndex].select();
} else if(this.elements.first()) {
this.elements.first().select();
} else {
this.voidSelection();
}
};
this.currentPageIndex = function() {
if(this.currentElement && this.currentElement.pageIndex > 0) {
return this.currentElement.pageIndex;
}
};
this.selectPage = function(pageIndex) {
if(pageIndex && pageIndex >= 0) {
this.notedPageIndex = pageIndex;
if(pageIndex < this.elements.length) {
this.elements[pageIndex].select();
}
}
};
};
return repoindex;
})(repoindex || {});