1
0
mirror of https://github.com/moodle/moodle.git synced 2025-04-24 18:04:43 +02:00

MDL-61138 javascript: add paged content widget

This commit is contained in:
Ryan Wyllie 2018-03-21 15:15:11 +08:00
parent 2803c8683b
commit 4ab09853f4
20 changed files with 1677 additions and 0 deletions

@ -0,0 +1 @@
define([],function(){return{SHOW_PAGES:"core-paged-content-show-pages"}});

@ -0,0 +1 @@
define(["jquery","core/templates","core/notification","core/paged_content_pages"],function(a,b,c,d){var e={PAGED_CONTENT:"core/paged_content"},f=function(a,b){for(var c={itemsperpage:b,previous:{},next:{},pages:[]},d=1;d<=a;d++){var e={number:d,page:""+d};1===d&&(e.active=!0),c.pages.push(e)}return c},g=function(a,b,c){var d={options:[]},e=0,f=0,g=a;c.hasOwnProperty("maxPages")&&(g=c.maxPages);for(var h=1;h<=g;h++){var i=0;h<=2?(i=b,f=b):(f=2*f,i=f),e+=i;var j={itemcount:i,content:e};1===h&&(j.active=!0),d.options.push(j)}return d},h=function(a,b,c){var d={pagingbar:!1,pagingdropdown:!1,skipjs:!0};return c.hasOwnProperty("dropdown")&&c.dropdown?d.pagingdropdown=g(a,b,c):d.pagingbar=f(a,b),d},i=function(a,b){var c=1;if(a>0){var d=a%b;d?(a-=d,c=a/b+1):c=a/b}return c},j=function(f,g,j,k){"undefined"==typeof k&&(k={});var l=a.Deferred(),m=i(f,g),n=h(m,g,k);return b.render(e.PAGED_CONTENT,n).then(function(b,c){b=a(b);var e=b,f=b.find(d.rootSelector);d.init(f,e,j),l.resolve(b,c)}).fail(function(a){l.reject(a)}).fail(c.exception),l},k=function(a,b,c,d){"undefined"==typeof d&&(d={});var e=a.length;return j(e,b,function(b){var d=[];return b.forEach(function(b){var c=b.offset,f=b.limit?c+b.limit:e,g=a.slice(c,f);d.push(g)}),c(d)},d)};return{createFromAjax:j,createFromStaticList:k}});

@ -0,0 +1 @@
define(["jquery","core/templates","core/notification","core/paged_content_events"],function(a,b,c,d){var e={ROOT:'[data-region="page-container"]',PAGE_REGION:'[data-region="paged-content-page"]',ACTIVE_PAGE_REGION:'[data-region="paged-content-page"].active'},f={PAGING_CONTENT_ITEM:"core/paged_content_page",LOADING:"core/overlay_loading"},g=function(a,b){return a.find('[data-page="'+b+'"]')},h=function(d){var e=a.Deferred();return b.render(f.LOADING,{visible:!0}).then(function(b){var c=a(b),f=setTimeout(function(){d.css("position","relative"),c.appendTo(d)},100);e.always(function(){clearTimeout(f),c.remove(),d.css("position","")})}).fail(c.exception),e},i=function(d,e,h){var i=a.Deferred();return e.then(function(a,e){b.render(f.PAGING_CONTENT_ITEM,{page:h,content:a}).then(function(a){b.appendNodeContents(d,a,e);var c=g(d,h);i.resolve(c)}).fail(function(a){i.reject(a)}).fail(c.exception)}).fail(function(a){i.reject(a)}).fail(c.exception),i},j=function(b,d,f){var j=[],k=[],l=a.Deferred();if(d.forEach(function(a){var c=a.pageNumber,d=g(b,c);d.length?j.push(d):k.push(a)}),k.length&&"function"==typeof f){var m=f(k),n=m.map(function(a,c){return i(b,a,k[c].pageNumber)});a.when.apply(a,n).then(function(){var a=Array.prototype.slice.call(arguments);l.resolve(a)}).fail(function(a){l.reject(a)}).fail(c.exception)}else l.resolve([]);var o=h(b);l.then(function(a){var c=j.concat(a);b.find(e.PAGE_REGION).addClass("hidden"),c.forEach(function(a){a.removeClass("hidden")})}).fail(c.exception).always(function(){o.resolve()})},k=function(b,c,e){b=a(b),c=a(c),c.on(d.SHOW_PAGES,function(a,c){j(b,c,e)})};return{init:k,rootSelector:e.ROOT}});

@ -0,0 +1 @@
define(["jquery","core/custom_interaction_events","core/paged_content_events"],function(a,b,c){var d={ROOT:'[data-region="paging-bar"]',PAGE:"[data-page]",PAGE_ITEM:'[data-region="page-item"]',ACTIVE_PAGE_ITEM:'[data-region="page-item"].active'},e=function(a,b){return a.find(d.PAGE_ITEM+'[data-page-number="'+b+'"]')},f=function(a){var b=a.find(d.PAGE).last();return b?parseInt(b.attr("data-page-number"),10):null},g=function(a){var b=a.find(d.ACTIVE_PAGE_ITEM);return b.length?h(a,b):null},h=function(a,b){if(void 0!=b.attr("data-page"))return parseInt(b.attr("data-page-number"),10);var c=1,d=null;switch(b.attr("data-control")){case"first":c=1;break;case"last":c=f(a);break;case"next":d=g(a);var e=f(a);c=d&&d<e?d+1:e;break;case"previous":d=g(a),c=d&&d>1?d-1:1;break;default:c=1}return parseInt(c,10)},i=function(a){return parseInt(a.attr("data-items-per-page"),10)},j=function(b){b.each(function(b,c){c=a(c),c.attr("data-page-number",b+1)})},k=function(a,b){var f=b==g(a),h=i(a),j=(b-1)*h;if(!f){a.find(d.PAGE_ITEM).removeClass("active");var k=e(a,b);k.addClass("active")}a.trigger(c.SHOW_PAGES,[[{pageNumber:b,limit:h,offset:j}]])},l=function(c){c=a(c);var e=c.find(d.PAGE);j(e);var f=g(c);f&&k(c,f),b.define(c,[b.events.activate]),c.on(b.events.activate,d.PAGE_ITEM,function(b,e){var f=a(b.target).closest(d.PAGE_ITEM),g=h(c,f);k(c,g),e.originalEvent.preventDefault(),e.originalEvent.stopPropagation()})};return{init:l,rootSelector:d.ROOT}});

@ -0,0 +1 @@
define(["jquery","core/custom_interaction_events","core/paged_content_events"],function(a,b,c){var d={ROOT:'[data-region="paging-dropdown-container"]',DROPDOWN_ITEM:'[data-region="dropdown-item"]',DROPDOWN_TOGGLE:'[data-region="dropdown-toggle"]',ACTIVE_DROPDOWN_ITEM:'[data-region="dropdown-item"].active',CARET:'[data-region="caret"]'},e=function(a){return parseInt(a.attr("data-page-number"),10)},f=function(a){return a.find(d.DROPDOWN_ITEM)},g=function(b,c){var d=e(c);return f(b).filter(function(b,c){return e(a(c))<d})},h=function(a){return parseInt(a.attr("data-item-count"),10)},i=function(b,c){if(void 0!=c.attr("data-offset"))return parseInt(c.attr("data-offset"),10);var d=0;return g(b,c).each(function(b,c){c=a(c),d+=h(c)}),c.attr("data-offset",d),d},j=function(a){return a.find(d.ACTIVE_DROPDOWN_ITEM)},k=function(b,c){return c.map(function(c,d){return d=a(d),{pageNumber:e(d),limit:h(d),offset:i(b,d)}}).get()},l=function(b){b.each(function(b,c){c=a(c),c.attr("data-page-number",b+1)})},m=function(a,b){var e=g(a,b),f=e.add(b),h=k(a,f),i=a.find(d.DROPDOWN_TOGGLE),l=i.find(d.CARET);j(a).removeClass("active"),b.addClass("active"),i.html(b.text()),i.append(l),a.trigger(c.SHOW_PAGES,[h])},n=function(c){c=a(c);var e=f(c);l(e);var g=j(c);g.length&&m(c,g),b.define(c,[b.events.activate]),c.on(b.events.activate,d.DROPDOWN_ITEM,function(b,e){var f=a(b.target).closest(d.DROPDOWN_ITEM);m(c,f),e.originalEvent.preventDefault()})};return{init:n,rootSelector:d.ROOT}});

@ -0,0 +1,27 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Javascript to load and render the paging bar.
*
* @module core/paging_bar
* @copyright 2018 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define([], function() {
return {
SHOW_PAGES: 'core-paged-content-show-pages',
};
});

@ -0,0 +1,280 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Factory to create a paged content widget.
*
* @module core/paged_content_factory
* @copyright 2018 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define(
[
'jquery',
'core/templates',
'core/notification',
'core/paged_content_pages'
],
function(
$,
Templates,
Notification,
PagedContent
) {
var TEMPLATES = {
PAGED_CONTENT: 'core/paged_content'
};
/**
* Build the context to render the paging bar template with based on the number
* of pages to show.
*
* @param {int} numberOfPages How many pages to have in the paging bar.
* @param {int} itemsPerPage How many items will be shown per page.
* @return {object} The template context.
*/
var buildPagingBarTemplateContext = function(numberOfPages, itemsPerPage) {
var context = {
"itemsperpage": itemsPerPage,
"previous": {},
"next": {},
"pages": []
};
for (var i = 1; i <= numberOfPages; i++) {
var page = {
number: i,
page: "" + i,
};
// Make the first page active by default.
if (i === 1) {
page.active = true;
}
context.pages.push(page);
}
return context;
};
/**
* Build the context to render the paging dropdown template with based on the number
* of pages to show and items per page.
*
* This control is rendered with a gradual increase of the items per page to
* limit the number of pages in the dropdown. Each page will show twice as much
* as the previous page (except for the first two pages).
*
* For example:
* Number of pages = 3
* Items per page = 25
* Would render a dropdown will 4 options:
* 25
* 50
* 100
* All
*
* @param {int} numberOfPages How many options to have in the dropdown.
* @param {int} itemsPerPage How many items will be shown per page.
* @param {object} config Configuration options provided by the client.
* @return {object} The template context.
*/
var buildPagingDropdownTemplateContext = function(numberOfPages, itemsPerPage, config) {
var context = {
options: []
};
var totalItems = 0;
var lastIncrease = 0;
var maxPages = numberOfPages;
if (config.hasOwnProperty('maxPages')) {
maxPages = config.maxPages;
}
for (var i = 1; i <= maxPages; i++) {
var itemCount = 0;
if (i <= 2) {
itemCount = itemsPerPage;
lastIncrease = itemsPerPage;
} else {
lastIncrease = lastIncrease * 2;
itemCount = lastIncrease;
}
totalItems += itemCount;
var option = {
itemcount: itemCount,
content: totalItems
};
// Make the first option active by default.
if (i === 1) {
option.active = true;
}
context.options.push(option);
}
return context;
};
/**
* Build the context to render the paged content template with based on the number
* of pages to show, items per page, and configuration option.
*
* By default the code will render a paging bar for the paging controls unless
* otherwise specified in the provided config.
*
* @param {int} numberOfPages How many pages to have.
* @param {int} itemsPerPage How many items will be shown per page.
* @param {object} config Configuration options provided by the client.
* @return {object} The template context.
*/
var buildTemplateContext = function(numberOfPages, itemsPerPage, config) {
var context = {
pagingbar: false,
pagingdropdown: false,
skipjs: true
};
if (config.hasOwnProperty('dropdown') && config.dropdown) {
context.pagingdropdown = buildPagingDropdownTemplateContext(numberOfPages, itemsPerPage, config);
} else {
context.pagingbar = buildPagingBarTemplateContext(numberOfPages, itemsPerPage);
}
return context;
};
/**
* Calculate the number of pages required for the given number of items and
* how many of each item should appear on a page.
*
* @param {int} numberOfItems How many items in total.
* @param {int} itemsPerPage How many items will be shown per page.
* @return {int} The number of pages required.
*/
var calculateNumberOfPages = function(numberOfItems, itemsPerPage) {
var numberOfPages = 1;
if (numberOfItems > 0) {
var partial = numberOfItems % itemsPerPage;
if (partial) {
numberOfItems -= partial;
numberOfPages = (numberOfItems / itemsPerPage) + 1;
} else {
numberOfPages = numberOfItems / itemsPerPage;
}
}
return numberOfPages;
};
/**
* Create a paged content widget where the complete list of items is not loaded
* up front but will instead be loaded by an ajax request (or similar).
*
* The client code must provide a callback function which loads and renders the
* items for each page. See PagedContent.init for more details.
*
* The function will return a deferred that is resolved with a jQuery object
* for the HTML content and a string for the JavaScript.
*
* The current list of configuration options available are:
* dropdown {bool} True to render the page control as a dropdown (paging bar is default).
*
* @param {int} numberOfItems How many items are there in total.
* @param {int} itemsPerPage How many items will be shown per page.
* @param {function} renderPagesContentCallback Callback for loading and rendering the items.
* @param {object} config Configuration options provided by the client.
* @return {promise} Resolved with jQuery HTML and string JS.
*/
var createFromAjax = function(numberOfItems, itemsPerPage, renderPagesContentCallback, config) {
if (typeof config == 'undefined') {
config = {};
}
var deferred = $.Deferred();
var numberOfPages = calculateNumberOfPages(numberOfItems, itemsPerPage);
var templateContext = buildTemplateContext(numberOfPages, itemsPerPage, config);
Templates.render(TEMPLATES.PAGED_CONTENT, templateContext)
.then(function(html, js) {
html = $(html);
var container = html;
var pagedContent = html.find(PagedContent.rootSelector);
PagedContent.init(pagedContent, container, renderPagesContentCallback);
deferred.resolve(html, js);
return;
})
.fail(function(exception) {
deferred.reject(exception);
})
.fail(Notification.exception);
return deferred;
};
/**
* Create a paged content widget where the complete list of items is loaded
* up front.
*
* The client code must provide a callback function which renders the
* items for each page. The callback will be provided with an array where each
* value in the array is a the list of items to render for the page.
*
* The function will return a deferred that is resolved with a jQuery object
* for the HTML content and a string for the JavaScript.
*
* The current list of configuration options available are:
* dropdown {bool} True to render the page control as a dropdown (paging bar is default).
*
* @param {array} contentItems The list of items to paginate.
* @param {int} itemsPerPage How many items will be shown per page.
* @param {function} renderContentCallback Callback for rendering the items for the page.
* @param {object} config Configuration options provided by the client.
* @return {promise} Resolved with jQuery HTML and string JS.
*/
var createFromStaticList = function(contentItems, itemsPerPage, renderContentCallback, config) {
if (typeof config == 'undefined') {
config = {};
}
var numberOfItems = contentItems.length;
return createFromAjax(numberOfItems, itemsPerPage, function(pagesData) {
var contentToRender = [];
pagesData.forEach(function(pageData) {
var begin = pageData.offset;
var end = pageData.limit ? begin + pageData.limit : numberOfItems;
var items = contentItems.slice(begin, end);
contentToRender.push(items);
});
return renderContentCallback(contentToRender);
}, config);
};
return {
createFromAjax: createFromAjax,
createFromStaticList: createFromStaticList
};
});

@ -0,0 +1,283 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Javascript for showing/hiding pages of content.
*
* @module core/paged_content_pages
* @copyright 2018 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define(
[
'jquery',
'core/templates',
'core/notification',
'core/paged_content_events'
],
function(
$,
Templates,
Notification,
PagedContentEvents
) {
var SELECTORS = {
ROOT: '[data-region="page-container"]',
PAGE_REGION: '[data-region="paged-content-page"]',
ACTIVE_PAGE_REGION: '[data-region="paged-content-page"].active'
};
var TEMPLATES = {
PAGING_CONTENT_ITEM: 'core/paged_content_page',
LOADING: 'core/overlay_loading'
};
/**
* Find a page by the number.
*
* @param {object} root The root element.
* @param {Number} pageNumber The number of the page to be found.
* @returns {jQuery} The page.
*/
var findPage = function(root, pageNumber) {
return root.find('[data-page="' + pageNumber + '"]');
};
/**
* Show the loading spinner until the returned deferred is resolved by the
* calling code.
*
* @param {object} root The root element.
* @returns {promise} The page.
*/
var startLoading = function(root) {
var deferred = $.Deferred();
Templates.render(TEMPLATES.LOADING, {visible: true})
.then(function(html) {
var loadingSpinner = $(html);
// Put this in a timer to give the calling code 100 milliseconds
// to render the content before we show the loading spinner. This
// helps prevent a loading icon flicker on close to instant
// rendering.
var timerId = setTimeout(function() {
root.css('position', 'relative');
loadingSpinner.appendTo(root);
}, 100);
deferred.always(function() {
clearTimeout(timerId);
// Remove the loading spinner when our deferred is resolved
// by the calling code.
loadingSpinner.remove();
root.css('position', '');
return;
});
return;
})
.fail(Notification.exception);
return deferred;
};
/**
* Render the result of the page promise in a paged content page.
*
* This function returns a promise that is resolved with the new paged content
* page.
*
* @param {object} root The root element.
* @param {promise} pagePromise The promise resolved with HTML and JS to render in the page.
* @param {int} pageNumber The page number.
* @returns {promise} The page.
*/
var renderPagePromise = function(root, pagePromise, pageNumber) {
var deferred = $.Deferred();
pagePromise.then(function(html, pageJS) {
// When we get the contents to be rendered we can pass it in as the
// content for a new page.
Templates.render(TEMPLATES.PAGING_CONTENT_ITEM, {
page: pageNumber,
content: html
})
.then(function(html) {
// Make sure the JS we got from the page promise is being added
// to the page when we render the page.
Templates.appendNodeContents(root, html, pageJS);
var page = findPage(root, pageNumber);
deferred.resolve(page);
return;
})
.fail(function(exception) {
deferred.reject(exception);
})
.fail(Notification.exception);
return;
})
.fail(function(exception) {
deferred.reject(exception);
return;
})
.fail(Notification.exception);
return deferred;
};
/**
* Make one or more pages visible based on the SHOW_PAGES event. The show
* pages event provides data containing which pages should be shown as well
* as the limit and offset values for loading the items for each of those pages.
*
* The renderPagesContentCallback is provided this list of data to know which
* pages to load. E.g. the data to load 2 pages might look like:
* [
* {
* pageNumber: 1,
* limit: 5,
* offset: 0
* },
* {
* pageNumber: 2,
* limit: 5,
* offset: 5
* }
* ]
*
* The renderPagesContentCallback should return an array of promises, one for
* each page in the pages data, that is resolved with the HTML and JS for that page.
*
* If the renderPagesContentCallback is not provided then it is assumed that
* all pages have been rendered prior to initialising this module.
*
* @param {object} root The root element.
* @param {Number} pagesData The data for which pages need to be visible.
* @param {function} renderPagesContentCallback Render pages content.
*/
var showPages = function(root, pagesData, renderPagesContentCallback) {
var existingPages = [];
var newPageData = [];
var newPagesPromise = $.Deferred();
// Check which of the pages being requests have previously been rendered
// so that we only ask for new pages to be rendered by the callback.
pagesData.forEach(function(pageData) {
var pageNumber = pageData.pageNumber;
var existingPage = findPage(root, pageNumber);
if (existingPage.length) {
existingPages.push(existingPage);
} else {
newPageData.push(pageData);
}
});
if (newPageData.length && typeof renderPagesContentCallback === 'function') {
// If we have pages we haven't previously seen then ask the client code
// to render them for us by calling the callback.
var promises = renderPagesContentCallback(newPageData);
// After the client has finished rendering each of the pages being asked
// for then begin our rendering process to put that content into paged
// content pages.
var renderPagePromises = promises.map(function(promise, index) {
// Create our promise for when our rendering will be completed.
return renderPagePromise(root, promise, newPageData[index].pageNumber);
});
// After each of our rendering promises have been completed then we can
// give all of the new pages to the next bit of code for handling.
$.when.apply($, renderPagePromises)
.then(function() {
var newPages = Array.prototype.slice.call(arguments);
// Resolve the promise with the list of newly rendered pages.
newPagesPromise.resolve(newPages);
return;
})
.fail(function(exception) {
newPagesPromise.reject(exception);
return;
})
.fail(Notification.exception);
} else {
// If there aren't any pages to load then immediately resolve the promise.
newPagesPromise.resolve([]);
}
var loadingPromise = startLoading(root);
newPagesPromise.then(function(newPages) {
// Once all of the new pages have been created then add them to any
// existing pages we have.
var pagesToShow = existingPages.concat(newPages);
// Hide all existing pages.
root.find(SELECTORS.PAGE_REGION).addClass('hidden');
// Show each of the pages that were requested.
pagesToShow.forEach(function(page) {
page.removeClass('hidden');
});
return;
})
.fail(Notification.exception)
.always(function() {
loadingPromise.resolve();
});
};
/**
* Initialise the module to listen for SHOW_PAGES events and render the
* appropriate pages using the provided renderPagesContentCallback function.
*
* The renderPagesContentCallback is provided a list of data to know which
* pages to load.
* E.g. the data to load 2 pages might look like:
* [
* {
* pageNumber: 1,
* limit: 5,
* offset: 0
* },
* {
* pageNumber: 2,
* limit: 5,
* offset: 5
* }
* ]
*
* The renderPagesContentCallback should return an array of promises, one for
* each page in the pages data, that is resolved with the HTML and JS for that page.
*
* If the renderPagesContentCallback is not provided then it is assumed that
* all pages have been rendered prior to initialising this module.
*
* The event element is the element to listen for the paged content events on.
*
* @param {object} root The root element.
* @param {object} eventElement The element to listen for events on.
* @param {function} renderPagesContentCallback Render pages content.
*/
var init = function(root, eventElement, renderPagesContentCallback) {
root = $(root);
eventElement = $(eventElement);
eventElement.on(PagedContentEvents.SHOW_PAGES, function(e, pagesData) {
showPages(root, pagesData, renderPagesContentCallback);
});
};
return {
init: init,
rootSelector: SELECTORS.ROOT,
};
});

@ -0,0 +1,228 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Javascript to enhance the paged content paging bar.
*
* @module core/paging_bar
* @copyright 2018 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define(
[
'jquery',
'core/custom_interaction_events',
'core/paged_content_events'
],
function(
$,
CustomEvents,
PagedContentEvents
) {
var SELECTORS = {
ROOT: '[data-region="paging-bar"]',
PAGE: '[data-page]',
PAGE_ITEM: '[data-region="page-item"]',
ACTIVE_PAGE_ITEM: '[data-region="page-item"].active'
};
/**
* Get the page element by number.
*
* @param {object} root The root element.
* @param {Number} pageNumber The page number.
* @return {jQuery}
*/
var getPageByNumber = function(root, pageNumber) {
return root.find(SELECTORS.PAGE_ITEM + '[data-page-number="' + pageNumber + '"]');
};
/**
* Get the last page number.
*
* @param {object} root The root element.
* @return {int}
*/
var getLastPageNumber = function(root) {
var lastPage = root.find(SELECTORS.PAGE).last();
if (lastPage) {
return parseInt(lastPage.attr('data-page-number'), 10);
} else {
return null;
}
};
/**
* Get the active page number.
*
* @param {object} root The root element.
* @returns {int} The page number
*/
var getActivePageNumber = function(root) {
var activePage = root.find(SELECTORS.ACTIVE_PAGE_ITEM);
if (activePage.length) {
return getPageNumber(root, activePage);
} else {
return null;
}
};
/**
* Get the page number.
*
* @param {object} root The root element.
* @param {object} page The page.
* @returns {int} The page number
*/
var getPageNumber = function(root, page) {
if (page.attr('data-page') != undefined) {
// If it's an actual page then we can just use the page number
// attribute.
return parseInt(page.attr('data-page-number'), 10);
}
var pageNumber = 1;
var activePageNumber = null;
switch (page.attr('data-control')) {
case 'first':
pageNumber = 1;
break;
case 'last':
pageNumber = getLastPageNumber(root);
break;
case 'next':
activePageNumber = getActivePageNumber(root);
var lastPage = getLastPageNumber(root);
if (activePageNumber && activePageNumber < lastPage) {
pageNumber = activePageNumber + 1;
} else {
pageNumber = lastPage;
}
break;
case 'previous':
activePageNumber = getActivePageNumber(root);
if (activePageNumber && activePageNumber > 1) {
pageNumber = activePageNumber - 1;
} else {
pageNumber = 1;
}
break;
default:
pageNumber = 1;
break;
}
// Make sure we return an int not a string.
return parseInt(pageNumber, 10);
};
/**
* Get the limit of items for each page.
*
* @param {object} root The root element.
* @returns {int}
*/
var getLimit = function(root) {
return parseInt(root.attr('data-items-per-page'), 10);
};
/**
* Set page numbers on each of the given items. Page numbers are set
* from 1..n (where n is the number of items).
*
* @param {jQuery} items A jQuery list of items.
*/
var generatePageNumbers = function(items) {
items.each(function(index, item) {
item = $(item);
item.attr('data-page-number', index + 1);
});
};
/**
* Make the paging bar item for the given page number visible and fire
* the SHOW_PAGES paged content event to tell any listening content to
* update.
*
* @param {object} root The root element.
* @param {int} pageNumber The number for the page to show.
* @param {object} page The page.
*/
var showPage = function(root, pageNumber) {
var isSamePage = pageNumber == getActivePageNumber(root);
var limit = getLimit(root);
var offset = (pageNumber - 1) * limit;
if (!isSamePage) {
// We only need to toggle the active class if the user didn't click
// on the already active page.
root.find(SELECTORS.PAGE_ITEM).removeClass('active');
var page = getPageByNumber(root, pageNumber);
page.addClass('active');
}
// This event requires a payload that contains a list of all pages that
// were activated. In the case of the paging bar we only show one page at
// a time.
root.trigger(PagedContentEvents.SHOW_PAGES, [[{
pageNumber: pageNumber,
limit: limit,
offset: offset
}]]);
};
/**
* Initialise the paging bar.
* @param {object} root The root element.
*/
var init = function(root) {
root = $(root);
var pages = root.find(SELECTORS.PAGE);
generatePageNumbers(pages);
var activePageNumber = getActivePageNumber(root);
if (activePageNumber) {
// If the the paging bar was rendered with an active page selected
// then make sure we fired off the event to tell the content page to
// show.
showPage(root, activePageNumber);
}
CustomEvents.define(root, [
CustomEvents.events.activate
]);
root.on(CustomEvents.events.activate, SELECTORS.PAGE_ITEM, function(e, data) {
var page = $(e.target).closest(SELECTORS.PAGE_ITEM);
var pageNumber = getPageNumber(root, page);
showPage(root, pageNumber);
data.originalEvent.preventDefault();
data.originalEvent.stopPropagation();
});
};
return {
init: init,
rootSelector: SELECTORS.ROOT,
};
});

@ -0,0 +1,237 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Javascript to manage the paging dropdown control.
*
* @module core/paged_content_paging_dropdown
* @copyright 2018 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define(
[
'jquery',
'core/custom_interaction_events',
'core/paged_content_events'
],
function(
$,
CustomEvents,
PagedContentEvents
) {
var SELECTORS = {
ROOT: '[data-region="paging-dropdown-container"]',
DROPDOWN_ITEM: '[data-region="dropdown-item"]',
DROPDOWN_TOGGLE: '[data-region="dropdown-toggle"]',
ACTIVE_DROPDOWN_ITEM: '[data-region="dropdown-item"].active',
CARET: '[data-region="caret"]'
};
/**
* Get the page number.
*
* @param {jquery} item The dropdown item.
* @returns {int}
*/
var getPageNumber = function(item) {
return parseInt(item.attr('data-page-number'), 10);
};
/**
* Get all paging dropdown items.
*
* @param {jquery} root The root element.
* @returns {jquery} A jquery object with all items.
*/
var getAllItems = function(root) {
return root.find(SELECTORS.DROPDOWN_ITEM);
};
/**
* Get all paging dropdown items with lower page numbers than the given
* dropdown item.
*
* @param {jquery} root The root element.
* @param {jquery} item The dropdown item.
* @returns {jquery} A jquery object with all items.
*/
var getPreviousItems = function(root, item) {
var pageNumber = getPageNumber(item);
return getAllItems(root).filter(function(index, element) {
return getPageNumber($(element)) < pageNumber;
});
};
/**
* Get the number of items to be loaded for the dropdown item.
*
* @param {jquery} item The dropdown item.
* @returns {int}
*/
var getLimit = function(item) {
return parseInt(item.attr('data-item-count'), 10);
};
/**
* Get the offset of items from the start of the itemset for the given
* dropdown item.
*
* @param {jquery} root The root element.
* @param {jquery} item The dropdown item.
* @returns {int}
*/
var getOffset = function(root, item) {
if (item.attr('data-offset') != undefined) {
return parseInt(item.attr('data-offset'), 10);
}
var offset = 0;
getPreviousItems(root, item).each(function(index, prevItem) {
prevItem = $(prevItem);
offset += getLimit(prevItem);
});
item.attr('data-offset', offset);
return offset;
};
/**
* Get the active dropdown item.
*
* @param {jquery} root The root element.
* @returns {jquery} The active dropdown item.
*/
var getActiveItem = function(root) {
return root.find(SELECTORS.ACTIVE_DROPDOWN_ITEM);
};
/**
* Create the event payload for the list of dropdown items. The event payload
* is an array of objects with one object per dropdown item.
*
* Each payload object contains the page number, limit, and offset for the
* corresponding dropdown item.
*
* For example: If we had 3 dropdown items with incrementing page numbers loading
* 25 items per page then the generated payload would look like:
* [
* {
* pageNumber: 1,
* limit: 25,
* offset: 0
* },
* {
* pageNumber: 2,
* limit: 25,
* offset: 25
* },
* {
* pageNumber: 3,
* limit: 25,
* offset: 50
* }
* ]
*
* @param {jquery} root The root element.
* @param {jquery} items The dropdown items.
* @returns {object[]} The payload for the event.
*/
var generateEventPayload = function(root, items) {
return items.map(function(index, item) {
item = $(item);
return {
pageNumber: getPageNumber(item),
limit: getLimit(item),
offset: getOffset(root, item),
};
}).get();
};
/**
* Add page number attributes to each of the given items. The page numbers
* start at 1 and increment by 1 for each item, e.g. 1, 2, 3 etc.
*
* @param {jquery} items The dropdown items.
*/
var generatePageNumbers = function(items) {
items.each(function(index, item) {
item = $(item);
item.attr('data-page-number', index + 1);
});
};
/**
* Make the given item active by setting the active class on it and firing
* the SHOW_PAGES event for the paged content to show the appropriate
* pages.
*
* @param {jquery} root The root element.
* @param {jquery} item The dropdown item.
*/
var setActiveItem = function(root, item) {
var prevItems = getPreviousItems(root, item);
var allItems = prevItems.add(item);
var eventPayload = generateEventPayload(root, allItems);
var toggle = root.find(SELECTORS.DROPDOWN_TOGGLE);
var caret = toggle.find(SELECTORS.CARET);
getActiveItem(root).removeClass('active');
item.addClass('active');
// Update the dropdown toggle to show which item is selected.
toggle.html(item.text());
// Bootstrap 2 compatibility.
toggle.append(caret);
// Fire the event to tell the content to update.
root.trigger(PagedContentEvents.SHOW_PAGES, [eventPayload]);
};
/**
* Initialise the module by firing the SHOW_PAGES event for an existing
* active page found and setting up the event listener for the user to select
* new pages.
*
* @param {object} root The root element.
*/
var init = function(root) {
root = $(root);
var items = getAllItems(root);
generatePageNumbers(items);
var activeItem = getActiveItem(root);
if (activeItem.length) {
// Fire the first event for the content to make sure it's visible.
setActiveItem(root, activeItem);
}
CustomEvents.define(root, [
CustomEvents.events.activate
]);
root.on(CustomEvents.events.activate, SELECTORS.DROPDOWN_ITEM, function(e, data) {
var item = $(e.target).closest(SELECTORS.DROPDOWN_ITEM);
setActiveItem(root, item);
data.originalEvent.preventDefault();
});
};
return {
init: init,
rootSelector: SELECTORS.ROOT,
};
});

@ -0,0 +1,81 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template core/paged_content
This template renders each of the content regions for a paginated
content section.
Example context (json):
{
"pagingbar": {
"itemsperpage": 1,
"previous": true,
"next": true,
"first": true,
"last": true,
"pages": [
{
"page": "1",
"active": true
},
{
"url": "#",
"page": "2"
}
]
},
"pages": [
{
"active": true,
"page": 1,
"content": "<p>Some page 1 content</p>"
},
{
"page": 2,
"content": "<p>Some page 2 content</p>"
}
]
}
}}
<div id="paged-content-container-{{uniqid}}" data-region="paged-content-container">
{{#pagingbar}}
{{> core/paged_content_paging_bar }}
{{/pagingbar}}
{{#pagingdropdown}}
{{> core/paged_content_paging_dropdown }}
{{/pagingdropdown}}
{{> core/paged_content_pages }}
</div>
{{^skipjs}}
{{#js}}
require(
[
'jquery',
'core/paged_content_pages'
],
function(
$,
PagedContent
) {
var container = $("#paged-content-container-{{uniqid}}");
var pagingContent = container.find(PagedContent.rootSelector);
PagedContent.init(pagingContent, container);
});
{{/js}}
{{/skipjs}}

@ -0,0 +1,36 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template core/paged_content_page
This template renders the content of a page. It is to be used with
the paging bar or paging dropdown to toggle visibility of the content items.
Example context (json):
{
"active": true,
"page": 1,
"content": "<p>Some page content</p>"
}
}}
<div data-region="paged-content-page"
data-page="{{page}}"
class="{{^active}}hidden{{/active}} {{$classes}}{{/classes}}">
{{$content}}
{{{content}}}
{{/content}}
</div>

@ -0,0 +1,44 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template core/paged_content_pages
This template renders each of the content regions for a paginated
content section.
Example context (json):
{
"pages": [
{
"active": true,
"page": 1,
"content": "<p>Some page content</p>"
},
{
"page": 2,
"content": "<p>Some page content</p>"
}
]
}
}}
<div id="{{$pagingcontentid}}page-container-{{uniqid}}{{/pagingcontentid}}" data-region="page-container">
{{#pages}}
{{$paged-content-page}}
{{> core/paged_content_page }}
{{/paged-content-page}}
{{/pages}}
</div>

@ -0,0 +1,97 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template core/paged_content_paging_bar
This template renders the bootstrap style paging bar to control a paged
content section.
Example context (json):
{
"itemsperpage": 2,
"previous": true,
"next": true,
"first": true,
"last": true,
"pages": [
{
"url": "#",
"page": "1",
"active": true
},
{
"url": "#",
"page": "2"
}
]
}
}}
<nav aria-label="{{label}}"
id="{{$pagingbarid}}paging-bar-{{uniqid}}{{/pagingbarid}}"
data-region="paging-bar"
data-items-per-page="{{itemsperpage}}">
<ul class="pagination">
{{#previous}}
{{< core/paged_content_paging_bar_item }}
{{$item-content}}
<span aria-hidden="true">&laquo;</span>
<span class="sr-only">{{#str}}previous{{/str}}</span>
{{/item-content}}
{{$attributes}}data-control="previous"{{/attributes}}
{{/ core/paged_content_paging_bar_item }}
{{/previous}}
{{#first}}
{{< core/paged_content_paging_bar_item }}
{{$item-content}}
<span aria-hidden="true">{{#str}}first{{/str}}</span>
<span class="sr-only">{{#str}}first{{/str}}</span>
{{/item-content}}
{{$attributes}}data-control="first"{{/attributes}}
{{/ core/paged_content_paging_bar_item }}
{{/first}}
{{#pages}}
{{< core/paged_content_paging_bar_item }}
{{$attributes}}data-page="true"{{/attributes}}
{{/ core/paged_content_paging_bar_item }}
{{/pages}}
{{#last}}
{{< core/paged_content_paging_bar_item }}
{{$item-content}}
<span aria-hidden="true">{{#str}}last{{/str}}</span>
<span class="sr-only">{{#str}}last{{/str}}</span>
{{/item-content}}
{{$attributes}}data-control="last"{{/attributes}}
{{/ core/paged_content_paging_bar_item }}
{{/last}}
{{#next}}
{{< core/paged_content_paging_bar_item }}
{{$item-content}}
<span aria-hidden="true">&raquo;</span>
<span class="sr-only">{{#str}}next{{/str}}</span>
{{/item-content}}
{{$attributes}}data-control="next"{{/attributes}}
{{/ core/paged_content_paging_bar_item }}
{{/next}}
</ul>
</nav>
{{#js}}
require(['jquery', 'core/paged_content_paging_bar'], function($, PagingControl) {
var root = $('#{{$pagingbarid}}paging-bar-{{uniqid}}{{/pagingbarid}}');
PagingControl.init(root);
});
{{/js}}

@ -0,0 +1,41 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template core/paged_content_paging_bar_item
This template renders a single item in the paging bar.
Example context (json):
{
"url": "#",
"number": 1,
"page": "1",
"active": true
}
}}
<li class="page-item {{#active}}active{{/active}} {{#disabled}}disabled{{/disabled}}"
data-region="page-item"
{{$attributes}}{{/attributes}}>
<a href="{{#url}}{{.}}{{/url}}{{^url}}#{{/url}}"
class="page-link"
data-region="page-link">
{{$item-content}}
{{{page}}}
{{/item-content}}
</a>
</li>

@ -0,0 +1,75 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template core/paged_content_paging_dropdown
This template renders the bootstrap style dropdown menu to control
paged content.
Example context (json):
{
"options": [
{
"itemcount": 25,
"content": "25",
"active": true
},
{
"itemcount": 25,
"content": "50"
},
{
"itemcount": 50,
"content": "100"
}
]
}
}}
<div class="dropdown m-b-1"
id="paging-dropdown-{{uniqid}}"
data-region="paging-dropdown-container">
<button class="btn btn-secondary dropdown-toggle"
type="button"
id="dropdown-menu-button-{{uniqid}}"
data-region="dropdown-toggle"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false">
{{#options}}
{{#active}}
{{{content}}}
{{/active}}
{{/options}}
</button>
<div class="dropdown-menu" aria-labelledby="dropdown-menu-button-{{uniqid}}">
{{#options}}
{{> core/paged_content_paging_dropdown_item }}
{{/options}}
{{< core/paged_content_paging_dropdown_item }}
{{$itemcount}}0{{/itemcount}}
{{$content}}{{#str}} all {{/str}}{{/content}}
{{/ core/paged_content_paging_dropdown_item }}
</div>
</div>
{{#js}}
require(['jquery', 'core/paged_content_paging_dropdown'], function($, PagingControl) {
var root = $('#paging-dropdown-{{uniqid}}');
PagingControl.init(root);
});
{{/js}}

@ -0,0 +1,35 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template core/paged_content_paging_dropdown_item
This template renders a single item in the paging dropdown menu.
Example context (json):
{
"itemcount": 25,
"content": "25",
"active": true
}
}}
<a class="dropdown-item {{#active}}active{{/active}} {{#disabled}}disabled{{/disabled}} {{$classes}}{{/classes}}"
data-region="dropdown-item"
data-item-count="{{$itemcount}}{{itemcount}}{{/itemcount}}"
href="#">
{{$content}}{{{content}}}{{/content}}
</a>

@ -0,0 +1,98 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template core/paged_content_paging_bar
This template renders the bootstrap style paging bar to control a paged
content section.
Example context (json):
{
"itemsperpage": 2,
"previous": true,
"next": true,
"first": true,
"last": true,
"pages": [
{
"url": "#",
"page": "1",
"active": true
},
{
"url": "#",
"page": "2"
}
]
}
}}
<div aria-label="{{label}}"
class="pagination"
id="{{$pagingbarid}}paging-bar-{{uniqid}}{{/pagingbarid}}"
data-region="paging-bar"
data-items-per-page="{{itemsperpage}}">
<ul class="pagination">
{{#previous}}
{{< core/paged_content_paging_bar_item }}
{{$item-content}}
<span aria-hidden="true">&laquo;</span>
<span class="sr-only">{{#str}}previous{{/str}}</span>
{{/item-content}}
{{$attributes}}data-control="previous"{{/attributes}}
{{/ core/paged_content_paging_bar_item }}
{{/previous}}
{{#first}}
{{< core/paged_content_paging_bar_item }}
{{$item-content}}
<span aria-hidden="true">{{#str}}first{{/str}}</span>
<span class="sr-only">{{#str}}first{{/str}}</span>
{{/item-content}}
{{$attributes}}data-control="first"{{/attributes}}
{{/ core/paged_content_paging_bar_item }}
{{/first}}
{{#pages}}
{{< core/paged_content_paging_bar_item }}
{{$attributes}}data-page="true"{{/attributes}}
{{/ core/paged_content_paging_bar_item }}
{{/pages}}
{{#last}}
{{< core/paged_content_paging_bar_item }}
{{$item-content}}
<span aria-hidden="true">{{#str}}last{{/str}}</span>
<span class="sr-only">{{#str}}last{{/str}}</span>
{{/item-content}}
{{$attributes}}data-control="last"{{/attributes}}
{{/ core/paged_content_paging_bar_item }}
{{/last}}
{{#next}}
{{< core/paged_content_paging_bar_item }}
{{$item-content}}
<span aria-hidden="true">&raquo;</span>
<span class="sr-only">{{#str}}next{{/str}}</span>
{{/item-content}}
{{$attributes}}data-control="next"{{/attributes}}
{{/ core/paged_content_paging_bar_item }}
{{/next}}
</ul>
</div>
{{#js}}
require(['jquery', 'core/paged_content_paging_bar'], function($, PagingControl) {
var root = $('#{{$pagingbarid}}paging-bar-{{uniqid}}{{/pagingbarid}}');
PagingControl.init(root);
});
{{/js}}

@ -0,0 +1,76 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template core/paged_content_paging_dropdown
This template renders the bootstrap style dropdown menu to control
paged content.
Example context (json):
{
"options": [
{
"itemcount": 25,
"content": "25",
"active": true
},
{
"itemcount": 25,
"content": "50"
},
{
"itemcount": 50,
"content": "100"
}
]
}
}}
<div class="btn-group m-b-1"
id="paging-dropdown-{{uniqid}}"
data-region="paging-dropdown-container">
<button class="btn btn-secondary dropdown-toggle"
type="button"
id="dropdown-menu-button-{{uniqid}}"
data-region="dropdown-toggle"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false">
{{#options}}
{{#active}}
{{{content}}}
{{/active}}
{{/options}}
<span data-region="caret" class="caret"></span>
</button>
<ul class="dropdown-menu" aria-labelledby="dropdown-menu-button-{{uniqid}}">
{{#options}}
{{> core/paged_content_paging_dropdown_item }}
{{/options}}
{{< core/paged_content_paging_dropdown_item }}
{{$itemcount}}0{{/itemcount}}
{{$content}}{{#str}} all {{/str}}{{/content}}
{{/ core/paged_content_paging_dropdown_item }}
</ul>
</div>
{{#js}}
require(['jquery', 'core/paged_content_paging_dropdown'], function($, PagingControl) {
var root = $('#paging-dropdown-{{uniqid}}');
PagingControl.init(root);
});
{{/js}}

@ -0,0 +1,34 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template core/paged_content_paging_dropdown_item
This template renders a single item in the paging dropdown menu.
Example context (json):
{
"itemcount": 25,
"content": "25",
"active": true
}
}}
<li class="dropdown-item {{#active}}active{{/active}} {{#disabled}}disabled{{/disabled}} {{$classes}}{{/classes}}"
data-region="dropdown-item"
data-item-count="{{$itemcount}}{{itemcount}}{{/itemcount}}">
<a href="#">{{$content}}{{{content}}}{{/content}}</a>
</li>