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 = ''; 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 = ''; 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 || {});