mirror of
https://github.com/moodle/moodle.git
synced 2025-08-06 01:16:44 +02:00
Merge branch 'dashboard-split-overview-block' of https://github.com/ryanwyllie/moodle
This commit is contained in:
commit
20f9b981f9
137 changed files with 5871 additions and 2450 deletions
1
lib/amd/build/page_global.min.js
vendored
Normal file
1
lib/amd/build/page_global.min.js
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
define(["jquery","core/custom_interaction_events","core/str"],function(a,b,c){var d=function(){var d=a("body");b.define(d,[b.events.activate]),d.on(b.events.activate,"[data-show-active-item]",function(b){var d=a(b.target).closest(".dropdown-item"),e=d.closest("[data-show-active-item]");if(d.hasClass("dropdown-item")&&!d.hasClass("active")){var f=e.find(".dropdown-item");f.removeClass("active"),f.removeAttr("aria-current"),e.attr("data-skip-active-class")||d.addClass("active"),d.attr("aria-current",!0);var g=d.text(),h=e.parent().find('[data-toggle="dropdown"]'),i=h.find("[data-active-item-text]");i.length?i.html(g):h.html(g);var j=e.attr("data-active-item-button-aria-label-components");if(j){var k=j.split(",");k.push(g),c.get_string(k[0].trim(),k[1].trim(),k[2].trim()).then(function(a){return h.attr("aria-label",a),a})["catch"](function(){return!1})}}})},e=function(){d()};return{init:e}});
|
1
lib/amd/build/paged_content.min.js
vendored
Normal file
1
lib/amd/build/paged_content.min.js
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
define(["jquery","core/paged_content_pages","core/paged_content_paging_bar","core/paged_content_paging_bar_limit_selector","core/paged_content_paging_dropdown"],function(a,b,c,d,e){var f=function(f,g){f=a(f);var h=f.find(b.rootSelector),i=f.find(c.rootSelector),j=f.find(e.rootSelector),k=f.find(d.rootSelector),l=f.attr("id");b.init(h,l,g),i.length&&c.init(i,l),k.length&&d.init(k,l),j.length&&e.init(j,l)};return{init:f,rootSelector:'[data-region="paged-content-container"]'}});
|
2
lib/amd/build/paged_content_events.min.js
vendored
2
lib/amd/build/paged_content_events.min.js
vendored
|
@ -1 +1 @@
|
|||
define([],function(){return{SHOW_PAGES:"core-paged-content-show-pages"}});
|
||||
define([],function(){return{SHOW_PAGES:"core-paged-content-show-pages",PAGES_SHOWN:"core-paged-content-pages-shown",ALL_ITEMS_LOADED:"core-paged-content-all-items-loaded",SET_ITEMS_PER_PAGE_LIMIT:"core-paged-content-set-items-per-page-limit"}});
|
2
lib/amd/build/paged_content_factory.min.js
vendored
2
lib/amd/build/paged_content_factory.min.js
vendored
|
@ -1 +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}});
|
||||
define(["jquery","core/templates","core/notification","core/paged_content"],function(a,b,c,d){var e={PAGED_CONTENT:"core/paged_content"},f={ITEMS_PER_PAGE_SINGLE:25,ITEMS_PER_PAGE_ARRAY:[25,50,100,0],MAX_PAGES:3},g=function(){return{pagingbar:!1,pagingdropdown:!1,skipjs:!0,ignorecontrolwhileloading:!0,controlplacementbottom:!1}},h=function(){return{showitemsperpageselector:!1,itemsperpage:35,previous:!0,next:!0,activepagenumber:1,hidecontrolonsinglepage:!0,pages:[]}},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(b,c){null===c&&(c=f.ITEMS_PER_PAGE_SINGLE),a.isArray(c)&&(c=c[0]);var d=h();d.itemsperpage=c;for(var e=i(b,c),g=1;g<=e;g++){var j={number:g,page:""+g};1===g&&(j.active=!0),d.pages.push(j)}return d},k=function(b){if(a.isArray(b)){var c=b.map(function(a){return"number"==typeof a?{value:a,active:!1}:a}),d=c.filter(function(a){return a.active});return d.length||(c[0].active=!0),c}return b},l=function(b){null===b&&(b=f.ITEMS_PER_PAGE_ARRAY);var c=h();return c.itemsperpage=k(b),c.showitemsperpageselector=a.isArray(b),c},m=function(a,b){return a?j(a,b):l(b)},n=function(b,c){if(null===b&&(b=f.ITEMS_PER_PAGE_SINGLE),a.isArray(b))return{options:b};var d={options:[]},e=0,g=0,h=f.MAX_PAGES;c.hasOwnProperty("maxPages")&&(h=c.maxPages);for(var i=1;i<=h;i++){var j=0;i<=2?(j=b,g=b):(g=2*g,j=g),e+=j;var k={itemcount:j,content:e};1===i&&(k.active=!0),d.options.push(k)}return d},o=function(a,b,c){var d=g();return c.hasOwnProperty("ignoreControlWhileLoading")&&(d.ignorecontrolwhileloading=c.ignoreControlWhileLoading),c.hasOwnProperty("controlPlacementBottom")&&(d.controlplacementbottom=c.controlPlacementBottom),c.hasOwnProperty("hideControlOnSinglePage")&&(d.hidecontrolonsinglepage=c.hideControlOnSinglePage),c.hasOwnProperty("ariaLabels")&&(d.arialabels=c.ariaLabels),c.hasOwnProperty("dropdown")&&c.dropdown?d.pagingdropdown=n(b,c):d.pagingbar=m(a,b),d},p=function(a,b){return r(null,null,a,b)},q=function(a,b,c){return r(null,a,b,c)},r=function(f,g,h,i){i=i||{};var j=a.Deferred(),k=o(f,g,i);return b.render(e.PAGED_CONTENT,k).then(function(b,c){b=a(b);var e=b;d.init(e,h),j.resolve(b,c)}).fail(function(a){j.reject(a)}).fail(c.exception),j.promise()},s=function(a,b,c,d){"undefined"==typeof d&&(d={});var e=a.length;return r(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{create:p,createWithLimit:q,createWithTotalAndLimit:r,createFromStaticList:s,createFromAjax:r}});
|
2
lib/amd/build/paged_content_pages.min.js
vendored
2
lib/amd/build/paged_content_pages.min.js
vendored
|
@ -1 +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}});
|
||||
define(["jquery","core/templates","core/notification","core/pubsub","core/paged_content_events"],function(a,b,c,d,e){var f={ROOT:'[data-region="page-container"]',PAGE_REGION:'[data-region="paged-content-page"]',ACTIVE_PAGE_REGION:'[data-region="paged-content-page"].active'},g={PAGING_CONTENT_ITEM:"core/paged_content_page",LOADING:"core/overlay_loading"},h=300,i=function(a,b){return a.find('[data-page="'+b+'"]')},j=function(d){var e=a.Deferred();return d.attr("aria-busy",!0),b.render(g.LOADING,{visible:!0}).then(function(b){var c=a(b),f=setTimeout(function(){d.css("position","relative"),c.appendTo(d)},h);e.always(function(){clearTimeout(f),c.remove(),d.css("position",""),d.removeAttr("aria-busy")})}).fail(c.exception),e},k=function(d,e,f){var h=a.Deferred();return e.then(function(a,e){e=e||"",b.render(g.PAGING_CONTENT_ITEM,{page:f,content:a}).then(function(a){b.appendNodeContents(d,a,e);var c=i(d,f);h.resolve(c)}).fail(function(a){h.reject(a)}).fail(c.exception)}).fail(function(a){h.reject(a)}).fail(c.exception),h.promise()},l=function(b,g,h,l){var m=[],n=[],o=a.Deferred();if(g.forEach(function(a){var c=a.pageNumber,d=i(b,c);d.length?m.push(d):n.push(a)}),n.length&&"function"==typeof l){var p=l(n,{allItemsLoaded:function(a){d.publish(h+e.ALL_ITEMS_LOADED,a)}}),q=p.map(function(a,c){return k(b,a,n[c].pageNumber)});a.when.apply(a,q).then(function(){var a=Array.prototype.slice.call(arguments);o.resolve(a)}).fail(function(a){o.reject(a)}).fail(c.exception)}else o.resolve([]);var r=j(b);o.then(function(a){var c=m.concat(a);b.find(f.PAGE_REGION).addClass("hidden"),c.forEach(function(a){a.removeClass("hidden")})}).then(function(){d.publish(h+e.PAGES_SHOWN,g)}).fail(c.exception).always(function(){r.resolve()})},m=function(b,c,f){b=a(b),d.subscribe(c+e.SHOW_PAGES,function(a){l(b,a,c,f)}),d.subscribe(c+e.SET_ITEMS_PER_PAGE_LIMIT,function(){b.empty()})};return{init:m,rootSelector:f.ROOT}});
|
|
@ -1 +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}});
|
||||
define(["jquery","core/custom_interaction_events","core/paged_content_events","core/str","core/pubsub"],function(a,b,c,d,e){var f={ROOT:'[data-region="paging-bar"]',PAGE:"[data-page]",PAGE_ITEM:'[data-region="page-item"]',PAGE_LINK:'[data-region="page-link"]',FIRST_BUTTON:'[data-control="first"]',LAST_BUTTON:'[data-control="last"]',NEXT_BUTTON:'[data-control="next"]',PREVIOUS_BUTTON:'[data-control="previous"]'},g=function(a,b){return a.find(f.PAGE_ITEM+'[data-page-number="'+b+'"]')},h=function(a){return a.find(f.NEXT_BUTTON)},i=function(a,b){a.attr("data-last-page-number",b)},j=function(a){return parseInt(a.attr("data-last-page-number"),10)},k=function(a){return parseInt(a.attr("data-active-page-number"),10)},l=function(a,b){a.attr("data-active-page-number",b)},m=function(a){var b=k(a);return!isNaN(b)&&0!=b},n=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=j(a);break;case"next":d=k(a);var e=j(a);c=e?d&&d<e?d+1:e:d+1;break;case"previous":d=k(a),c=d&&d>1?d-1:1;break;default:c=1}return parseInt(c,10)},o=function(a){return parseInt(a.attr("data-items-per-page"),10)},p=function(a,b){a.attr("data-items-per-page",b)},q=function(a){a.removeClass("hidden")},r=function(a){a.addClass("hidden")},s=function(a){var b=a.find(f.NEXT_BUTTON),c=a.find(f.LAST_BUTTON);b.addClass("disabled"),b.attr("aria-disabled",!0),c.addClass("disabled"),c.attr("aria-disabled",!0)},t=function(a){var b=a.find(f.NEXT_BUTTON),c=a.find(f.LAST_BUTTON);b.removeClass("disabled"),b.removeAttr("aria-disabled"),c.removeClass("disabled"),c.removeAttr("aria-disabled")},u=function(a){var b=a.find(f.PREVIOUS_BUTTON),c=a.find(f.FIRST_BUTTON);b.addClass("disabled"),b.attr("aria-disabled",!0),c.addClass("disabled"),c.attr("aria-disabled",!0)},v=function(a){var b=a.find(f.PREVIOUS_BUTTON),c=a.find(f.FIRST_BUTTON);b.removeClass("disabled"),b.removeAttr("aria-disabled"),c.removeClass("disabled"),c.removeAttr("aria-disabled")},w=function(a){var b=a.attr("data-aria-label-components-pagination-item"),c=b.split(",").map(function(a){return a.trim()});return c},x=function(a){var b=a.attr("data-aria-label-components-pagination-active-item"),c=b.split(",").map(function(a){return a.trim()});return c},y=function(b,c){var d=0;l(b,0),c.each(function(c,e){var f=c+1;e=a(e),e.attr("data-page-number",f),d++,e.hasClass("active")&&l(b,f)}),i(b,d)},z=function(b){var c=w(b),e=x(b),g=k(b),h=b.find(f.PAGE_ITEM),i=h.map(function(d,f){f=a(f);var h=n(b,f);return h===g?{key:e[0],component:e[1],param:h}:{key:c[0],component:c[1],param:h}});d.get_strings(i).then(function(b){return h.each(function(c,d){d=a(d);var e=b[c];d.attr("aria-label",e),d.find(f.PAGE_LINK).attr("aria-label",e)}),b})["catch"](function(){})},A=function(a,b,d){var h=j(a),i=b==k(a),m=o(a),n=(b-1)*m;if(!i){a.find(f.PAGE_ITEM).removeClass("active").removeAttr("aria-current");var p=g(a,b);p.addClass("active"),p.attr("aria-current",!0),l(a,b)}h&&b>=h?s(a):t(a),b>1?v(a):u(a),z(a),e.publish(d+c.SHOW_PAGES,[{pageNumber:b,limit:m,offset:n}])},B=function(d,g){var h=d.attr("data-ignore-control-while-loading"),k=!1;""==h&&(h=!0),b.define(d,[b.events.activate]),d.on(b.events.activate,f.PAGE_ITEM,function(b,c){if(c.originalEvent.preventDefault(),c.originalEvent.stopPropagation(),!h||!k){var e=a(b.target).closest(f.PAGE_ITEM);if(!e.hasClass("disabled")){var i=n(d,e);A(d,i,g),k=!0}}}),e.subscribe(g+c.ALL_ITEMS_LOADED,function(a){k=!1;var b=j(d);(!b||a<b)&&i(d,a),1===a&&d.attr("data-hide-control-on-single-page")?(r(d),s(d),u(d)):(q(d),s(d))}),e.subscribe(g+c.PAGES_SHOWN,function(){k=!1}),e.subscribe(g+c.SET_ITEMS_PER_PAGE_LIMIT,function(a){p(d,a),i(d,0),l(d,0),q(d),A(d,1,g)})},C=function(b,c){b=a(b);var d=b.find(f.PAGE);if(y(b,d),B(b,c),m(b)){var e=k(b);g(b,e).click(),1==e&&u(b)}else h(b).click()};return{init:C,showPage:A,rootSelector:f.ROOT}});
|
1
lib/amd/build/paged_content_paging_bar_limit_selector.min.js
vendored
Normal file
1
lib/amd/build/paged_content_paging_bar_limit_selector.min.js
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
define(["jquery","core/custom_interaction_events","core/paged_content_events","core/pubsub"],function(a,b,c,d){var e={ROOT:'[data-region="paging-control-limit-container"]',LIMIT_OPTION:"[data-limit]",LIMIT_TOGGLE:'[data-action="limit-toggle"]'},f=function(f,g){f=a(f),b.define(f,[b.events.activate]),f.on(b.events.activate,e.LIMIT_OPTION,function(b,f){var h=a(b.target).closest(e.LIMIT_OPTION);if(!h.hasClass("active")){var i=parseInt(h.attr("data-limit"),10);d.publish(g+c.SET_ITEMS_PER_PAGE_LIMIT,i),f.originalEvent.preventDefault()}})};return{init:f,rootSelector:e.ROOT}});
|
|
@ -1 +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}});
|
||||
define(["jquery","core/custom_interaction_events","core/paged_content_events","core/pubsub"],function(a,b,c,d){var e={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"]'},f=function(a){return parseInt(a.attr("data-page-number"),10)},g=function(a){return a.find(e.DROPDOWN_ITEM)},h=function(b,c){var d=f(c);return g(b).filter(function(b,c){return f(a(c))<d})},i=function(a){return parseInt(a.attr("data-item-count"),10)},j=function(b,c){if(void 0!=c.attr("data-offset"))return parseInt(c.attr("data-offset"),10);var d=0;return h(b,c).each(function(b,c){c=a(c),d+=i(c)}),c.attr("data-offset",d),d},k=function(a){return a.find(e.ACTIVE_DROPDOWN_ITEM)},l=function(b,c){return c.map(function(c,d){return d=a(d),{pageNumber:f(d),limit:i(d),offset:j(b,d)}}).get()},m=function(b){b.each(function(b,c){c=a(c),c.attr("data-page-number",b+1)})},n=function(a,b,f){var g=h(a,b),i=g.add(b),j=l(a,i),m=a.find(e.DROPDOWN_TOGGLE),n=m.find(e.CARET);k(a).removeClass("active"),b.addClass("active"),m.html(b.text()),m.append(n),d.publish(f+c.SHOW_PAGES,j)},o=function(c,d){c=a(c);var f=g(c);m(f);var h=k(c);h.length&&n(c,h,d),b.define(c,[b.events.activate]),c.on(b.events.activate,e.DROPDOWN_ITEM,function(b,f){var g=a(b.target).closest(e.DROPDOWN_ITEM);n(c,g,d),f.originalEvent.preventDefault()})};return{init:o,rootSelector:e.ROOT}});
|
1
lib/amd/build/pubsub.min.js
vendored
Normal file
1
lib/amd/build/pubsub.min.js
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
define([],function(){var a={},b=function(b,c){a[b]=a[b]||[],a[b].push(c)},c=function(b,c){if(a[b])for(var d=0;d<a[b].length;d++)if(a[b][d]===c){a[b].splice(d,1);break}},d=function(b,c){a[b]&&a[b].forEach(function(a){a(c)})};return{subscribe:b,unsubscribe:c,publish:d}});
|
2
lib/amd/build/user_date.min.js
vendored
2
lib/amd/build/user_date.min.js
vendored
|
@ -1 +1 @@
|
|||
define(["jquery","core/ajax","core/sessionstorage","core/config"],function(a,b,c,d){var e={},f=function(b){var c=a("html").attr("lang").replace(/-/g,"_");return"core_user_date/"+c+"/"+d.usertimezone+"/"+b.timestamp+"/"+b.format},g=function(a){return c.get(a)},h=function(a,b){c.set(a,b)},i=function(a){return"undefined"!=typeof e[a]},j=function(a){return e[a]},k=function(a,b){e[a]=b},l=function(a){var c=a.map(function(a){return{timestamp:a.timestamp,format:a.format}}),e={methodname:"core_get_user_dates",args:{contextid:d.contextid,timestamps:c}};return b.call([e],!0,!0)[0].then(function(b){b.dates.forEach(function(b,c){var d=a[c],e=f(d);h(e,b),d.deferred.resolve(b)})})["catch"](function(b){a.forEach(function(a){a.deferred.reject(b)})})},m=function(b){var c=[],d=[];return b.forEach(function(b){var e=f(b);if(i(e))d.push(j(e));else{var h=a.Deferred(),l=g(e);l?h.resolve(l):(b.deferred=h,c.push(b)),k(e,h.promise()),d.push(h.promise())}}),c.length&&l(c),a.when.apply(a,d).then(function(){return 1===arguments.length?[arguments[0]]:Array.apply(null,arguments)})};return{get:m}});
|
||||
define(["jquery","core/ajax","core/sessionstorage","core/config"],function(a,b,c,d){var e=86400,f={},g=function(b){var c=a("html").attr("lang").replace(/-/g,"_");return"core_user_date/"+c+"/"+d.usertimezone+"/"+b.timestamp+"/"+b.format},h=function(a){return c.get(a)},i=function(a,b){c.set(a,b)},j=function(a){return"undefined"!=typeof f[a]},k=function(a){return f[a]},l=function(a,b){f[a]=b},m=function(a){var c=a.map(function(a){return{timestamp:a.timestamp,format:a.format}}),e={methodname:"core_get_user_dates",args:{contextid:d.contextid,timestamps:c}};return b.call([e],!0,!0)[0].then(function(b){b.dates.forEach(function(b,c){var d=a[c],e=g(d);i(e,b),d.deferred.resolve(b)})})["catch"](function(b){a.forEach(function(a){a.deferred.reject(b)})})},n=function(b){var c=[],d=[];return b.forEach(function(b){var e=g(b);if(j(e))d.push(k(e));else{var f=a.Deferred(),i=h(e);i?f.resolve(i):(b.deferred=f,c.push(b)),l(e,f.promise()),d.push(f.promise())}}),c.length&&m(c),a.when.apply(a,d).then(function(){return 1===arguments.length?[arguments[0]]:Array.apply(null,arguments)})},o=function(a,b){var c=a>b,d=Math.abs(a-b),f=c?Math.floor(d/e):Math.ceil(d/e),g=f*e,h=c?b+g:b-g;return h};return{get:n,getUserMidnightForTimestamp:o}});
|
135
lib/amd/src/page_global.js
Normal file
135
lib/amd/src/page_global.js
Normal file
|
@ -0,0 +1,135 @@
|
|||
// 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/>.
|
||||
|
||||
/**
|
||||
* Provide global helper code to enhance page elements.
|
||||
*
|
||||
* @module core/page_global
|
||||
* @package core
|
||||
* @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/str',
|
||||
],
|
||||
function(
|
||||
$,
|
||||
CustomEvents,
|
||||
Str
|
||||
) {
|
||||
|
||||
/**
|
||||
* Add an event handler for dropdown menus that wish to show their active item
|
||||
* in the dropdown toggle element.
|
||||
*
|
||||
* By default the handler will add the "active" class to the selected dropdown
|
||||
* item and set it's text as the HTML for the dropdown toggle.
|
||||
*
|
||||
* The behaviour of this handler is controlled by adding data attributes to
|
||||
* the HTML and requires the typically Bootstrap dropdown markup.
|
||||
*
|
||||
* data-show-active-item - Add to the .dropdown-menu element to enable default
|
||||
* functionality.
|
||||
* data-skip-active-class - Add to the .dropdown-menu to prevent this code from
|
||||
* adding the active class to the dropdown items
|
||||
* data-active-item-text - Add to an element within the data-toggle="dropdown" element
|
||||
* to use it as the active option text placeholder otherwise the
|
||||
* data-toggle="dropdown" element itself will be used.
|
||||
* data-active-item-button-aria-label-components - String components to set the aria
|
||||
* lable on the dropdown button. The string will be given the
|
||||
* active item text.
|
||||
*/
|
||||
var initActionOptionDropdownHandler = function() {
|
||||
var body = $('body');
|
||||
|
||||
CustomEvents.define(body, [CustomEvents.events.activate]);
|
||||
body.on(CustomEvents.events.activate, '[data-show-active-item]', function(e) {
|
||||
// The dropdown item that the user clicked on.
|
||||
var option = $(e.target).closest('.dropdown-item');
|
||||
// The dropdown menu element.
|
||||
var menuContainer = option.closest('[data-show-active-item]');
|
||||
|
||||
if (!option.hasClass('dropdown-item')) {
|
||||
// Ignore non Bootstrap dropdowns.
|
||||
return;
|
||||
}
|
||||
|
||||
if (option.hasClass('active')) {
|
||||
// If it's already active then we don't need to do anything.
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear the active class from all other options.
|
||||
var dropdownItems = menuContainer.find('.dropdown-item');
|
||||
dropdownItems.removeClass('active');
|
||||
dropdownItems.removeAttr('aria-current');
|
||||
|
||||
if (!menuContainer.attr('data-skip-active-class')) {
|
||||
// Make this option active unless configured to ignore it.
|
||||
// Some code, for example the Bootstrap tabs, may want to handle
|
||||
// adding the active class itself.
|
||||
option.addClass('active');
|
||||
}
|
||||
|
||||
// Update aria attribute for active item.
|
||||
option.attr('aria-current', true);
|
||||
|
||||
var activeOptionText = option.text();
|
||||
var dropdownToggle = menuContainer.parent().find('[data-toggle="dropdown"]');
|
||||
var dropdownToggleText = dropdownToggle.find('[data-active-item-text]');
|
||||
|
||||
if (dropdownToggleText.length) {
|
||||
// We have a specific placeholder for the active item text so
|
||||
// use that.
|
||||
dropdownToggleText.html(activeOptionText);
|
||||
} else {
|
||||
// Otherwise just replace all of the toggle text with the active item.
|
||||
dropdownToggle.html(activeOptionText);
|
||||
}
|
||||
|
||||
var activeItemAriaLabelComponent = menuContainer.attr('data-active-item-button-aria-label-components');
|
||||
if (activeItemAriaLabelComponent) {
|
||||
// If we have string components for the aria label then load the string
|
||||
// and set the label on the dropdown toggle.
|
||||
var strParams = activeItemAriaLabelComponent.split(',');
|
||||
strParams.push(activeOptionText);
|
||||
|
||||
Str.get_string(strParams[0].trim(), strParams[1].trim(), strParams[2].trim())
|
||||
.then(function(string) {
|
||||
dropdownToggle.attr('aria-label', string);
|
||||
return string;
|
||||
})
|
||||
.catch(function() {
|
||||
// Silently ignore that we couldn't load the string.
|
||||
return false;
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialise the global helper functions.
|
||||
*/
|
||||
var init = function() {
|
||||
initActionOptionDropdownHandler();
|
||||
};
|
||||
|
||||
return {
|
||||
init: init
|
||||
};
|
||||
});
|
75
lib/amd/src/paged_content.js
Normal file
75
lib/amd/src/paged_content.js
Normal file
|
@ -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/>.
|
||||
|
||||
/**
|
||||
* Javascript to load and render a paged content section.
|
||||
*
|
||||
* @module core/paged_content
|
||||
* @copyright 2018 Ryan Wyllie <ryan@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
define(
|
||||
[
|
||||
'jquery',
|
||||
'core/paged_content_pages',
|
||||
'core/paged_content_paging_bar',
|
||||
'core/paged_content_paging_bar_limit_selector',
|
||||
'core/paged_content_paging_dropdown'
|
||||
],
|
||||
function(
|
||||
$,
|
||||
Pages,
|
||||
PagingBar,
|
||||
PagingBarLimitSelector,
|
||||
Dropdown
|
||||
) {
|
||||
|
||||
/**
|
||||
* Initialise the paged content region by running the pages
|
||||
* module and initialising any paging controls in the DOM.
|
||||
*
|
||||
* @param {object} root The paged content container element
|
||||
* @param {function} renderPagesContentCallback (optional) A callback function to render a
|
||||
* content page. See core/paged_content_pages for
|
||||
* more defails.
|
||||
*/
|
||||
var init = function(root, renderPagesContentCallback) {
|
||||
root = $(root);
|
||||
var pagesContainer = root.find(Pages.rootSelector);
|
||||
var pagingBarContainer = root.find(PagingBar.rootSelector);
|
||||
var dropdownContainer = root.find(Dropdown.rootSelector);
|
||||
var pagingBarLimitSelectorContainer = root.find(PagingBarLimitSelector.rootSelector);
|
||||
var id = root.attr('id');
|
||||
|
||||
Pages.init(pagesContainer, id, renderPagesContentCallback);
|
||||
|
||||
if (pagingBarContainer.length) {
|
||||
PagingBar.init(pagingBarContainer, id);
|
||||
}
|
||||
|
||||
if (pagingBarLimitSelectorContainer.length) {
|
||||
PagingBarLimitSelector.init(pagingBarLimitSelectorContainer, id);
|
||||
}
|
||||
|
||||
if (dropdownContainer.length) {
|
||||
Dropdown.init(dropdownContainer, id);
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
init: init,
|
||||
rootSelector: '[data-region="paged-content-container"]'
|
||||
};
|
||||
});
|
|
@ -14,14 +14,17 @@
|
|||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Javascript to load and render the paging bar.
|
||||
* Events for the paged content element.
|
||||
*
|
||||
* @module core/paging_bar
|
||||
* @module core/paged_content_events
|
||||
* @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',
|
||||
PAGES_SHOWN: 'core-paged-content-pages-shown',
|
||||
ALL_ITEMS_LOADED: 'core-paged-content-all-items-loaded',
|
||||
SET_ITEMS_PER_PAGE_LIMIT: 'core-paged-content-set-items-per-page-limit'
|
||||
};
|
||||
});
|
||||
|
|
|
@ -25,7 +25,7 @@ define(
|
|||
'jquery',
|
||||
'core/templates',
|
||||
'core/notification',
|
||||
'core/paged_content_pages'
|
||||
'core/paged_content'
|
||||
],
|
||||
function(
|
||||
$,
|
||||
|
@ -37,21 +37,92 @@ function(
|
|||
PAGED_CONTENT: 'core/paged_content'
|
||||
};
|
||||
|
||||
var DEFAULT = {
|
||||
ITEMS_PER_PAGE_SINGLE: 25,
|
||||
ITEMS_PER_PAGE_ARRAY: [25, 50, 100, 0],
|
||||
MAX_PAGES: 3
|
||||
};
|
||||
|
||||
/**
|
||||
* Build the context to render the paging bar template with based on the number
|
||||
* of pages to show.
|
||||
* Get the default context to render the paged content mustache
|
||||
* template.
|
||||
*
|
||||
* @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.
|
||||
* @return {object}
|
||||
*/
|
||||
var buildPagingBarTemplateContext = function(numberOfPages, itemsPerPage) {
|
||||
var context = {
|
||||
"itemsperpage": itemsPerPage,
|
||||
"previous": {},
|
||||
"next": {},
|
||||
"pages": []
|
||||
var getDefaultTemplateContext = function() {
|
||||
return {
|
||||
pagingbar: false,
|
||||
pagingdropdown: false,
|
||||
skipjs: true,
|
||||
ignorecontrolwhileloading: true,
|
||||
controlplacementbottom: false
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the default context to render the paging bar mustache template.
|
||||
*
|
||||
* @return {object}
|
||||
*/
|
||||
var getDefaultPagingBarTemplateContext = function() {
|
||||
return {
|
||||
showitemsperpageselector: false,
|
||||
itemsperpage: 35,
|
||||
previous: true,
|
||||
next: true,
|
||||
activepagenumber: 1,
|
||||
hidecontrolonsinglepage: true,
|
||||
pages: []
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculate the number of pages required for the given number of items and
|
||||
* how many of each item should appear on a page.
|
||||
*
|
||||
* @param {Number} numberOfItems How many items in total.
|
||||
* @param {Number} itemsPerPage How many items will be shown per page.
|
||||
* @return {Number} 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;
|
||||
};
|
||||
|
||||
/**
|
||||
* Build the context for the paging bar template when we have a known number
|
||||
* of items.
|
||||
*
|
||||
* @param {Number} numberOfItems How many items in total.
|
||||
* @param {Number} itemsPerPage How many items will be shown per page.
|
||||
* @return {object} Mustache template
|
||||
*/
|
||||
var buildPagingBarTemplateContextKnownLength = function(numberOfItems, itemsPerPage) {
|
||||
if (itemsPerPage === null) {
|
||||
itemsPerPage = DEFAULT.ITEMS_PER_PAGE_SINGLE;
|
||||
}
|
||||
|
||||
if ($.isArray(itemsPerPage)) {
|
||||
// If we're given a total number of pages then we don't support a variable
|
||||
// set of items per page so just use the first one.
|
||||
itemsPerPage = itemsPerPage[0];
|
||||
}
|
||||
|
||||
var context = getDefaultPagingBarTemplateContext();
|
||||
context.itemsperpage = itemsPerPage;
|
||||
var numberOfPages = calculateNumberOfPages(numberOfItems, itemsPerPage);
|
||||
|
||||
for (var i = 1; i <= numberOfPages; i++) {
|
||||
var page = {
|
||||
|
@ -71,15 +142,101 @@ function(
|
|||
};
|
||||
|
||||
/**
|
||||
* Build the context to render the paging dropdown template with based on the number
|
||||
* Convert the itemsPerPage value into a format applicable for the mustache template.
|
||||
* The given value can be either a single integer or an array of integers / objects.
|
||||
*
|
||||
* E.g.
|
||||
* In: [5, 10]
|
||||
* out: [{value: 5, active: true}, {value: 10, active: false}]
|
||||
*
|
||||
* In: [5, {value: 10, active: true}]
|
||||
* Out: [{value: 5, active: false}, {value: 10, active: true}]
|
||||
*
|
||||
* In: [{value: 5, active: false}, {value: 10, active: true}]
|
||||
* Out: [{value: 5, active: false}, {value: 10, active: true}]
|
||||
*
|
||||
* @param {int|int[]} itemsPerPage Options for number of items per page.
|
||||
* @return {int|array}
|
||||
*/
|
||||
var buildItemsPerPagePagingBarContext = function(itemsPerPage) {
|
||||
if ($.isArray(itemsPerPage)) {
|
||||
// Convert the array into a format accepted by the template.
|
||||
var context = itemsPerPage.map(function(num) {
|
||||
if (typeof num === 'number') {
|
||||
// If the item is just a plain number then convert it into
|
||||
// an object with value and active keys.
|
||||
return {
|
||||
value: num,
|
||||
active: false
|
||||
};
|
||||
} else {
|
||||
// Otherwise we assume the caller has specified things correctly.
|
||||
return num;
|
||||
}
|
||||
});
|
||||
|
||||
var activeItems = context.filter(function(item) {
|
||||
return item.active;
|
||||
});
|
||||
|
||||
// Default the first item to active if one hasn't been specified.
|
||||
if (!activeItems.length) {
|
||||
context[0].active = true;
|
||||
}
|
||||
|
||||
return context;
|
||||
} else {
|
||||
return itemsPerPage;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Build the context for the paging bar template when we have an unknown
|
||||
* number of items.
|
||||
*
|
||||
* @param {Number} itemsPerPage How many items will be shown per page.
|
||||
* @return {object} Mustache template
|
||||
*/
|
||||
var buildPagingBarTemplateContextUnknownLength = function(itemsPerPage) {
|
||||
if (itemsPerPage === null) {
|
||||
itemsPerPage = DEFAULT.ITEMS_PER_PAGE_ARRAY;
|
||||
}
|
||||
|
||||
var context = getDefaultPagingBarTemplateContext();
|
||||
context.itemsperpage = buildItemsPerPagePagingBarContext(itemsPerPage);
|
||||
context.showitemsperpageselector = $.isArray(itemsPerPage);
|
||||
|
||||
return context;
|
||||
};
|
||||
|
||||
/**
|
||||
* Build the context to render the paging bar template with based on the number
|
||||
* of pages to show.
|
||||
*
|
||||
* @param {int|null} numberOfItems How many items are there total.
|
||||
* @param {int|null} itemsPerPage How many items will be shown per page.
|
||||
* @return {object} The template context.
|
||||
*/
|
||||
var buildPagingBarTemplateContext = function(numberOfItems, itemsPerPage) {
|
||||
if (numberOfItems) {
|
||||
return buildPagingBarTemplateContextKnownLength(numberOfItems, itemsPerPage);
|
||||
} else {
|
||||
return buildPagingBarTemplateContextUnknownLength(itemsPerPage);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Build the context to render the paging dropdown template 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).
|
||||
*
|
||||
* By default there will only be 4 pages shown (including the "All" option) unless
|
||||
* a different number of pages is defined using the maxPages config value.
|
||||
*
|
||||
* For example:
|
||||
* Number of pages = 3
|
||||
* Items per page = 25
|
||||
* Would render a dropdown will 4 options:
|
||||
* 25
|
||||
|
@ -87,19 +244,30 @@ function(
|
|||
* 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 {Number} 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 buildPagingDropdownTemplateContext = function(itemsPerPage, config) {
|
||||
if (itemsPerPage === null) {
|
||||
itemsPerPage = DEFAULT.ITEMS_PER_PAGE_SINGLE;
|
||||
}
|
||||
|
||||
if ($.isArray(itemsPerPage)) {
|
||||
// If we're given an array for the items per page, rather than a number,
|
||||
// then just use that as the options for the dropdown.
|
||||
return {
|
||||
options: itemsPerPage
|
||||
};
|
||||
}
|
||||
|
||||
var context = {
|
||||
options: []
|
||||
};
|
||||
|
||||
var totalItems = 0;
|
||||
var lastIncrease = 0;
|
||||
var maxPages = numberOfPages;
|
||||
var maxPages = DEFAULT.MAX_PAGES;
|
||||
|
||||
if (config.hasOwnProperty('maxPages')) {
|
||||
maxPages = config.maxPages;
|
||||
|
@ -140,52 +308,39 @@ function(
|
|||
* 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 {int|null} numberOfItems Total number of items.
|
||||
* @param {int|null|array} 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
|
||||
};
|
||||
var buildTemplateContext = function(numberOfItems, itemsPerPage, config) {
|
||||
var context = getDefaultTemplateContext();
|
||||
|
||||
if (config.hasOwnProperty('ignoreControlWhileLoading')) {
|
||||
context.ignorecontrolwhileloading = config.ignoreControlWhileLoading;
|
||||
}
|
||||
|
||||
if (config.hasOwnProperty('controlPlacementBottom')) {
|
||||
context.controlplacementbottom = config.controlPlacementBottom;
|
||||
}
|
||||
|
||||
if (config.hasOwnProperty('hideControlOnSinglePage')) {
|
||||
context.hidecontrolonsinglepage = config.hideControlOnSinglePage;
|
||||
}
|
||||
|
||||
if (config.hasOwnProperty('ariaLabels')) {
|
||||
context.arialabels = config.ariaLabels;
|
||||
}
|
||||
|
||||
if (config.hasOwnProperty('dropdown') && config.dropdown) {
|
||||
context.pagingdropdown = buildPagingDropdownTemplateContext(numberOfPages, itemsPerPage, config);
|
||||
context.pagingdropdown = buildPagingDropdownTemplateContext(itemsPerPage, config);
|
||||
} else {
|
||||
context.pagingbar = buildPagingBarTemplateContext(numberOfPages, itemsPerPage);
|
||||
context.pagingbar = buildPagingBarTemplateContext(numberOfItems, 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).
|
||||
|
@ -198,30 +353,78 @@ function(
|
|||
*
|
||||
* The current list of configuration options available are:
|
||||
* dropdown {bool} True to render the page control as a dropdown (paging bar is default).
|
||||
* maxPages {Number} The maximum number of pages to show in the dropdown (only works with dropdown option)
|
||||
* ignoreControlWhileLoading {bool} Disable the pagination controls while loading a page (default to true)
|
||||
* controlPlacementBottom {bool} Render controls under paged content (default to false)
|
||||
*
|
||||
* @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 create = function(renderPagesContentCallback, config) {
|
||||
return createWithTotalAndLimit(null, null, renderPagesContentCallback, config);
|
||||
};
|
||||
|
||||
/**
|
||||
* 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).
|
||||
* maxPages {Number} The maximum number of pages to show in the dropdown (only works with dropdown option)
|
||||
* ignoreControlWhileLoading {bool} Disable the pagination controls while loading a page (default to true)
|
||||
* controlPlacementBottom {bool} Render controls under paged content (default to false)
|
||||
*
|
||||
* @param {int|array|null} 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 createWithLimit = function(itemsPerPage, renderPagesContentCallback, config) {
|
||||
return createWithTotalAndLimit(null, itemsPerPage, renderPagesContentCallback, config);
|
||||
};
|
||||
|
||||
/**
|
||||
* 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).
|
||||
* maxPages {Number} The maximum number of pages to show in the dropdown (only works with dropdown option)
|
||||
* ignoreControlWhileLoading {bool} Disable the pagination controls while loading a page (default to true)
|
||||
* controlPlacementBottom {bool} Render controls under paged content (default to false)
|
||||
*
|
||||
* @param {int|null} numberOfItems How many items are there in total.
|
||||
* @param {int|array|null} 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 createWithTotalAndLimit = function(numberOfItems, itemsPerPage, renderPagesContentCallback, config) {
|
||||
config = config || {};
|
||||
|
||||
var deferred = $.Deferred();
|
||||
var numberOfPages = calculateNumberOfPages(numberOfItems, itemsPerPage);
|
||||
var templateContext = buildTemplateContext(numberOfPages, itemsPerPage, config);
|
||||
var templateContext = buildTemplateContext(numberOfItems, 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);
|
||||
PagedContent.init(container, renderPagesContentCallback);
|
||||
|
||||
deferred.resolve(html, js);
|
||||
return;
|
||||
|
@ -231,7 +434,7 @@ function(
|
|||
})
|
||||
.fail(Notification.exception);
|
||||
|
||||
return deferred;
|
||||
return deferred.promise();
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -247,9 +450,12 @@ function(
|
|||
*
|
||||
* The current list of configuration options available are:
|
||||
* dropdown {bool} True to render the page control as a dropdown (paging bar is default).
|
||||
* maxPages {Number} The maximum number of pages to show in the dropdown (only works with dropdown option)
|
||||
* ignoreControlWhileLoading {bool} Disable the pagination controls while loading a page (default to true)
|
||||
* controlPlacementBottom {bool} Render controls under paged content (default to false)
|
||||
*
|
||||
* @param {array} contentItems The list of items to paginate.
|
||||
* @param {int} itemsPerPage How many items will be shown per page.
|
||||
* @param {Number} 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.
|
||||
|
@ -260,7 +466,7 @@ function(
|
|||
}
|
||||
|
||||
var numberOfItems = contentItems.length;
|
||||
return createFromAjax(numberOfItems, itemsPerPage, function(pagesData) {
|
||||
return createWithTotalAndLimit(numberOfItems, itemsPerPage, function(pagesData) {
|
||||
var contentToRender = [];
|
||||
pagesData.forEach(function(pageData) {
|
||||
var begin = pageData.offset;
|
||||
|
@ -274,7 +480,11 @@ function(
|
|||
};
|
||||
|
||||
return {
|
||||
createFromAjax: createFromAjax,
|
||||
createFromStaticList: createFromStaticList
|
||||
create: create,
|
||||
createWithLimit: createWithLimit,
|
||||
createWithTotalAndLimit: createWithTotalAndLimit,
|
||||
createFromStaticList: createFromStaticList,
|
||||
// Backwards compatibility just in case anyone was using this.
|
||||
createFromAjax: createWithTotalAndLimit
|
||||
};
|
||||
});
|
||||
|
|
|
@ -25,12 +25,14 @@ define(
|
|||
'jquery',
|
||||
'core/templates',
|
||||
'core/notification',
|
||||
'core/pubsub',
|
||||
'core/paged_content_events'
|
||||
],
|
||||
function(
|
||||
$,
|
||||
Templates,
|
||||
Notification,
|
||||
PubSub,
|
||||
PagedContentEvents
|
||||
) {
|
||||
|
||||
|
@ -45,6 +47,8 @@ define(
|
|||
LOADING: 'core/overlay_loading'
|
||||
};
|
||||
|
||||
var PRELOADING_GRACE_PERIOD = 300;
|
||||
|
||||
/**
|
||||
* Find a page by the number.
|
||||
*
|
||||
|
@ -60,23 +64,27 @@ define(
|
|||
* Show the loading spinner until the returned deferred is resolved by the
|
||||
* calling code.
|
||||
*
|
||||
* The loading spinner is only rendered after a short grace period to avoid
|
||||
* having it flash up briefly in the interface.
|
||||
*
|
||||
* @param {object} root The root element.
|
||||
* @returns {promise} The page.
|
||||
*/
|
||||
var startLoading = function(root) {
|
||||
var deferred = $.Deferred();
|
||||
root.attr('aria-busy', true);
|
||||
|
||||
Templates.render(TEMPLATES.LOADING, {visible: true})
|
||||
.then(function(html) {
|
||||
var loadingSpinner = $(html);
|
||||
// Put this in a timer to give the calling code 100 milliseconds
|
||||
// Put this in a timer to give the calling code 300 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);
|
||||
}, PRELOADING_GRACE_PERIOD);
|
||||
|
||||
deferred.always(function() {
|
||||
clearTimeout(timerId);
|
||||
|
@ -84,6 +92,7 @@ define(
|
|||
// by the calling code.
|
||||
loadingSpinner.remove();
|
||||
root.css('position', '');
|
||||
root.removeAttr('aria-busy');
|
||||
return;
|
||||
});
|
||||
|
||||
|
@ -102,12 +111,13 @@ define(
|
|||
*
|
||||
* @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.
|
||||
* @param {Number} pageNumber The page number.
|
||||
* @returns {promise} The page.
|
||||
*/
|
||||
var renderPagePromise = function(root, pagePromise, pageNumber) {
|
||||
var deferred = $.Deferred();
|
||||
pagePromise.then(function(html, pageJS) {
|
||||
pageJS = 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, {
|
||||
|
@ -135,7 +145,7 @@ define(
|
|||
})
|
||||
.fail(Notification.exception);
|
||||
|
||||
return deferred;
|
||||
return deferred.promise();
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -164,11 +174,14 @@ define(
|
|||
* If the renderPagesContentCallback is not provided then it is assumed that
|
||||
* all pages have been rendered prior to initialising this module.
|
||||
*
|
||||
* This function triggers the PAGES_SHOWN event after the pages have been rendered.
|
||||
*
|
||||
* @param {object} root The root element.
|
||||
* @param {Number} pagesData The data for which pages need to be visible.
|
||||
* @param {string} id A unique id for this instance.
|
||||
* @param {function} renderPagesContentCallback Render pages content.
|
||||
*/
|
||||
var showPages = function(root, pagesData, renderPagesContentCallback) {
|
||||
var showPages = function(root, pagesData, id, renderPagesContentCallback) {
|
||||
var existingPages = [];
|
||||
var newPageData = [];
|
||||
var newPagesPromise = $.Deferred();
|
||||
|
@ -188,7 +201,11 @@ define(
|
|||
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);
|
||||
var promises = renderPagesContentCallback(newPageData, {
|
||||
allItemsLoaded: function(lastPageNumber) {
|
||||
PubSub.publish(id + PagedContentEvents.ALL_ITEMS_LOADED, lastPageNumber);
|
||||
}
|
||||
});
|
||||
// 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.
|
||||
|
@ -229,6 +246,11 @@ define(
|
|||
|
||||
return;
|
||||
})
|
||||
.then(function() {
|
||||
// Let everything else know we've displayed the pages.
|
||||
PubSub.publish(id + PagedContentEvents.PAGES_SHOWN, pagesData);
|
||||
return;
|
||||
})
|
||||
.fail(Notification.exception)
|
||||
.always(function() {
|
||||
loadingPromise.resolve();
|
||||
|
@ -264,15 +286,20 @@ define(
|
|||
* 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 {string} id A unique id for this instance.
|
||||
* @param {function} renderPagesContentCallback Render pages content.
|
||||
*/
|
||||
var init = function(root, eventElement, renderPagesContentCallback) {
|
||||
var init = function(root, id, renderPagesContentCallback) {
|
||||
root = $(root);
|
||||
eventElement = $(eventElement);
|
||||
|
||||
eventElement.on(PagedContentEvents.SHOW_PAGES, function(e, pagesData) {
|
||||
showPages(root, pagesData, renderPagesContentCallback);
|
||||
PubSub.subscribe(id + PagedContentEvents.SHOW_PAGES, function(pagesData) {
|
||||
showPages(root, pagesData, id, renderPagesContentCallback);
|
||||
});
|
||||
|
||||
PubSub.subscribe(id + PagedContentEvents.SET_ITEMS_PER_PAGE_LIMIT, function() {
|
||||
// If the items per page limit was changed then we need to clear our content
|
||||
// the load new values based on the new limit.
|
||||
root.empty();
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -24,19 +24,27 @@ define(
|
|||
[
|
||||
'jquery',
|
||||
'core/custom_interaction_events',
|
||||
'core/paged_content_events'
|
||||
'core/paged_content_events',
|
||||
'core/str',
|
||||
'core/pubsub'
|
||||
],
|
||||
function(
|
||||
$,
|
||||
CustomEvents,
|
||||
PagedContentEvents
|
||||
PagedContentEvents,
|
||||
Str,
|
||||
PubSub
|
||||
) {
|
||||
|
||||
var SELECTORS = {
|
||||
ROOT: '[data-region="paging-bar"]',
|
||||
PAGE: '[data-page]',
|
||||
PAGE_ITEM: '[data-region="page-item"]',
|
||||
ACTIVE_PAGE_ITEM: '[data-region="page-item"].active'
|
||||
PAGE_LINK: '[data-region="page-link"]',
|
||||
FIRST_BUTTON: '[data-control="first"]',
|
||||
LAST_BUTTON: '[data-control="last"]',
|
||||
NEXT_BUTTON: '[data-control="next"]',
|
||||
PREVIOUS_BUTTON: '[data-control="previous"]'
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -50,43 +58,74 @@ define(
|
|||
return root.find(SELECTORS.PAGE_ITEM + '[data-page-number="' + pageNumber + '"]');
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the next button element.
|
||||
*
|
||||
* @param {object} root The root element.
|
||||
* @return {jQuery}
|
||||
*/
|
||||
var getNextButton = function(root) {
|
||||
return root.find(SELECTORS.NEXT_BUTTON);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the last page number after which no more pages
|
||||
* should be loaded.
|
||||
*
|
||||
* @param {object} root The root element.
|
||||
* @param {Number} number Page number.
|
||||
*/
|
||||
var setLastPageNumber = function(root, number) {
|
||||
root.attr('data-last-page-number', number);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the last page number.
|
||||
*
|
||||
* @param {object} root The root element.
|
||||
* @return {int}
|
||||
* @return {Number}
|
||||
*/
|
||||
var getLastPageNumber = function(root) {
|
||||
var lastPage = root.find(SELECTORS.PAGE).last();
|
||||
if (lastPage) {
|
||||
return parseInt(lastPage.attr('data-page-number'), 10);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
return parseInt(root.attr('data-last-page-number'), 10);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the active page number.
|
||||
*
|
||||
* @param {object} root The root element.
|
||||
* @returns {int} The page number
|
||||
* @returns {Number} 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;
|
||||
}
|
||||
return parseInt(root.attr('data-active-page-number'), 10);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the page number.
|
||||
* Set the active page number.
|
||||
*
|
||||
* @param {object} root The root element.
|
||||
* @param {object} page The page.
|
||||
* @returns {int} The page number
|
||||
* @param {Number} number Page number.
|
||||
*/
|
||||
var setActivePageNumber = function(root, number) {
|
||||
root.attr('data-active-page-number', number);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if there is an active page number.
|
||||
*
|
||||
* @param {object} root The root element.
|
||||
* @returns {bool}
|
||||
*/
|
||||
var hasActivePageNumber = function(root) {
|
||||
var number = getActivePageNumber(root);
|
||||
return !isNaN(number) && number != 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the page number for a given page.
|
||||
*
|
||||
* @param {object} root The root element.
|
||||
* @param {object} page The page element.
|
||||
* @returns {Number} The page number
|
||||
*/
|
||||
var getPageNumber = function(root, page) {
|
||||
if (page.attr('data-page') != undefined) {
|
||||
|
@ -110,7 +149,9 @@ define(
|
|||
case 'next':
|
||||
activePageNumber = getActivePageNumber(root);
|
||||
var lastPage = getLastPageNumber(root);
|
||||
if (activePageNumber && activePageNumber < lastPage) {
|
||||
if (!lastPage) {
|
||||
pageNumber = activePageNumber + 1;
|
||||
} else if (activePageNumber && activePageNumber < lastPage) {
|
||||
pageNumber = activePageNumber + 1;
|
||||
} else {
|
||||
pageNumber = lastPage;
|
||||
|
@ -139,22 +180,207 @@ define(
|
|||
* Get the limit of items for each page.
|
||||
*
|
||||
* @param {object} root The root element.
|
||||
* @returns {int}
|
||||
* @returns {Number}
|
||||
*/
|
||||
var getLimit = function(root) {
|
||||
return parseInt(root.attr('data-items-per-page'), 10);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the limit of items for each page.
|
||||
*
|
||||
* @param {object} root The root element.
|
||||
* @param {Number} limit Items per page limit.
|
||||
*/
|
||||
var setLimit = function(root, limit) {
|
||||
root.attr('data-items-per-page', limit);
|
||||
};
|
||||
|
||||
/**
|
||||
* Show the paging bar.
|
||||
*
|
||||
* @param {object} root The root element.
|
||||
*/
|
||||
var show = function(root) {
|
||||
root.removeClass('hidden');
|
||||
};
|
||||
|
||||
/**
|
||||
* Hide the paging bar.
|
||||
*
|
||||
* @param {object} root The root element.
|
||||
*/
|
||||
var hide = function(root) {
|
||||
root.addClass('hidden');
|
||||
};
|
||||
|
||||
/**
|
||||
* Disable the next and last buttons in the paging bar.
|
||||
*
|
||||
* @param {object} root The root element.
|
||||
*/
|
||||
var disableNextControlButtons = function(root) {
|
||||
var nextButton = root.find(SELECTORS.NEXT_BUTTON);
|
||||
var lastButton = root.find(SELECTORS.LAST_BUTTON);
|
||||
|
||||
nextButton.addClass('disabled');
|
||||
nextButton.attr('aria-disabled', true);
|
||||
lastButton.addClass('disabled');
|
||||
lastButton.attr('aria-disabled', true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Enable the next and last buttons in the paging bar.
|
||||
*
|
||||
* @param {object} root The root element.
|
||||
*/
|
||||
var enableNextControlButtons = function(root) {
|
||||
var nextButton = root.find(SELECTORS.NEXT_BUTTON);
|
||||
var lastButton = root.find(SELECTORS.LAST_BUTTON);
|
||||
|
||||
nextButton.removeClass('disabled');
|
||||
nextButton.removeAttr('aria-disabled');
|
||||
lastButton.removeClass('disabled');
|
||||
lastButton.removeAttr('aria-disabled');
|
||||
};
|
||||
|
||||
/**
|
||||
* Disable the previous and first buttons in the paging bar.
|
||||
*
|
||||
* @param {object} root The root element.
|
||||
*/
|
||||
var disablePreviousControlButtons = function(root) {
|
||||
var previousButton = root.find(SELECTORS.PREVIOUS_BUTTON);
|
||||
var firstButton = root.find(SELECTORS.FIRST_BUTTON);
|
||||
|
||||
previousButton.addClass('disabled');
|
||||
previousButton.attr('aria-disabled', true);
|
||||
firstButton.addClass('disabled');
|
||||
firstButton.attr('aria-disabled', true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Enable the previous and first buttons in the paging bar.
|
||||
*
|
||||
* @param {object} root The root element.
|
||||
*/
|
||||
var enablePreviousControlButtons = function(root) {
|
||||
var previousButton = root.find(SELECTORS.PREVIOUS_BUTTON);
|
||||
var firstButton = root.find(SELECTORS.FIRST_BUTTON);
|
||||
|
||||
previousButton.removeClass('disabled');
|
||||
previousButton.removeAttr('aria-disabled');
|
||||
firstButton.removeClass('disabled');
|
||||
firstButton.removeAttr('aria-disabled');
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the components for a get_string request for the aria-label
|
||||
* on a page. The value is a comma separated string of key and
|
||||
* component.
|
||||
*
|
||||
* @param {object} root The root element.
|
||||
* @return {array} First element is the key, second is the component.
|
||||
*/
|
||||
var getPageAriaLabelComponents = function(root) {
|
||||
var componentString = root.attr('data-aria-label-components-pagination-item');
|
||||
var components = componentString.split(',').map(function(component) {
|
||||
return component.trim();
|
||||
});
|
||||
return components;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the components for a get_string request for the aria-label
|
||||
* on an active page. The value is a comma separated string of key and
|
||||
* component.
|
||||
*
|
||||
* @param {object} root The root element.
|
||||
* @return {array} First element is the key, second is the component.
|
||||
*/
|
||||
var getActivePageAriaLabelComponents = function(root) {
|
||||
var componentString = root.attr('data-aria-label-components-pagination-active-item');
|
||||
var components = componentString.split(',').map(function(component) {
|
||||
return component.trim();
|
||||
});
|
||||
return components;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set page numbers on each of the given items. Page numbers are set
|
||||
* from 1..n (where n is the number of items).
|
||||
*
|
||||
* Sets the active page number to be the last page found with
|
||||
* an "active" class (if any).
|
||||
*
|
||||
* Sets the last page number.
|
||||
*
|
||||
* @param {object} root The root element.
|
||||
* @param {jQuery} items A jQuery list of items.
|
||||
*/
|
||||
var generatePageNumbers = function(items) {
|
||||
var generatePageNumbers = function(root, items) {
|
||||
var lastPageNumber = 0;
|
||||
setActivePageNumber(root, 0);
|
||||
|
||||
items.each(function(index, item) {
|
||||
var pageNumber = index + 1;
|
||||
item = $(item);
|
||||
item.attr('data-page-number', index + 1);
|
||||
item.attr('data-page-number', pageNumber);
|
||||
lastPageNumber++;
|
||||
|
||||
if (item.hasClass('active')) {
|
||||
setActivePageNumber(root, pageNumber);
|
||||
}
|
||||
});
|
||||
|
||||
setLastPageNumber(root, lastPageNumber);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the aria-labels on each of the page items in the paging bar.
|
||||
* This includes the next, previous, first, and last items.
|
||||
*
|
||||
* @param {object} root The root element.
|
||||
*/
|
||||
var generateAriaLabels = function(root) {
|
||||
var pageAriaLabelComponents = getPageAriaLabelComponents(root);
|
||||
var activePageAriaLabelComponents = getActivePageAriaLabelComponents(root);
|
||||
var activePageNumber = getActivePageNumber(root);
|
||||
var pageItems = root.find(SELECTORS.PAGE_ITEM);
|
||||
// We want to request all of the strings at once rather than
|
||||
// one at a time.
|
||||
var stringRequests = pageItems.map(function(index, page) {
|
||||
page = $(page);
|
||||
var pageNumber = getPageNumber(root, page);
|
||||
|
||||
if (pageNumber === activePageNumber) {
|
||||
return {
|
||||
key: activePageAriaLabelComponents[0],
|
||||
component: activePageAriaLabelComponents[1],
|
||||
param: pageNumber
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
key: pageAriaLabelComponents[0],
|
||||
component: pageAriaLabelComponents[1],
|
||||
param: pageNumber
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
Str.get_strings(stringRequests).then(function(strings) {
|
||||
pageItems.each(function(index, page) {
|
||||
page = $(page);
|
||||
var string = strings[index];
|
||||
page.attr('aria-label', string);
|
||||
page.find(SELECTORS.PAGE_LINK).attr('aria-label', string);
|
||||
});
|
||||
|
||||
return strings;
|
||||
})
|
||||
.catch(function() {
|
||||
// No need to interrupt the page if we can't load the aria lang strings.
|
||||
return;
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -164,10 +390,11 @@ define(
|
|||
* update.
|
||||
*
|
||||
* @param {object} root The root element.
|
||||
* @param {int} pageNumber The number for the page to show.
|
||||
* @param {object} page The page.
|
||||
* @param {Number} pageNumber The number for the page to show.
|
||||
* @param {string} id A uniqie id for this instance.
|
||||
*/
|
||||
var showPage = function(root, pageNumber) {
|
||||
var showPage = function(root, pageNumber, id) {
|
||||
var lastPageNumber = getLastPageNumber(root);
|
||||
var isSamePage = pageNumber == getActivePageNumber(root);
|
||||
var limit = getLimit(root);
|
||||
var offset = (pageNumber - 1) * limit;
|
||||
|
@ -175,36 +402,56 @@ define(
|
|||
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');
|
||||
root.find(SELECTORS.PAGE_ITEM).removeClass('active').removeAttr('aria-current');
|
||||
var page = getPageByNumber(root, pageNumber);
|
||||
page.addClass('active');
|
||||
page.attr('aria-current', true);
|
||||
setActivePageNumber(root, pageNumber);
|
||||
}
|
||||
|
||||
// Make sure the control buttons are disabled as the user navigates
|
||||
// to either end of the limits.
|
||||
if (lastPageNumber && pageNumber >= lastPageNumber) {
|
||||
disableNextControlButtons(root);
|
||||
} else {
|
||||
enableNextControlButtons(root);
|
||||
}
|
||||
|
||||
if (pageNumber > 1) {
|
||||
enablePreviousControlButtons(root);
|
||||
} else {
|
||||
disablePreviousControlButtons(root);
|
||||
}
|
||||
|
||||
generateAriaLabels(root);
|
||||
|
||||
// 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, [[{
|
||||
PubSub.publish(id + PagedContentEvents.SHOW_PAGES, [{
|
||||
pageNumber: pageNumber,
|
||||
limit: limit,
|
||||
offset: offset
|
||||
}]]);
|
||||
}]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialise the paging bar.
|
||||
* Add event listeners for interactions with the paging bar as well as listening
|
||||
* for custom paged content events.
|
||||
*
|
||||
* Each event will trigger different logic to update parts of the paging bar's
|
||||
* display.
|
||||
*
|
||||
* @param {object} root The root element.
|
||||
* @param {string} id A uniqie id for this instance.
|
||||
*/
|
||||
var init = function(root) {
|
||||
root = $(root);
|
||||
var pages = root.find(SELECTORS.PAGE);
|
||||
generatePageNumbers(pages);
|
||||
var registerEventListeners = function(root, id) {
|
||||
var ignoreControlWhileLoading = root.attr('data-ignore-control-while-loading');
|
||||
var loading = false;
|
||||
|
||||
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);
|
||||
if (ignoreControlWhileLoading == "") {
|
||||
// Default to ignoring control while loading if not specified.
|
||||
ignoreControlWhileLoading = true;
|
||||
}
|
||||
|
||||
CustomEvents.define(root, [
|
||||
|
@ -212,17 +459,98 @@ define(
|
|||
]);
|
||||
|
||||
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();
|
||||
|
||||
if (ignoreControlWhileLoading && loading) {
|
||||
// Do nothing if configured to ignore control while loading.
|
||||
return;
|
||||
}
|
||||
|
||||
var page = $(e.target).closest(SELECTORS.PAGE_ITEM);
|
||||
|
||||
if (!page.hasClass('disabled')) {
|
||||
var pageNumber = getPageNumber(root, page);
|
||||
showPage(root, pageNumber, id);
|
||||
loading = true;
|
||||
}
|
||||
});
|
||||
|
||||
// This event is fired when all of the items have been loaded. Typically used
|
||||
// in an "infinite" pages context when we don't know the exact number of pages
|
||||
// ahead of time.
|
||||
PubSub.subscribe(id + PagedContentEvents.ALL_ITEMS_LOADED, function(pageNumber) {
|
||||
loading = false;
|
||||
var currentLastPage = getLastPageNumber(root);
|
||||
|
||||
if (!currentLastPage || pageNumber < currentLastPage) {
|
||||
// Somehow the value we've got saved is higher than the new
|
||||
// value we just received. Perhaps events came out of order.
|
||||
// In any case, save the lowest value.
|
||||
setLastPageNumber(root, pageNumber);
|
||||
}
|
||||
|
||||
if (pageNumber === 1 && root.attr('data-hide-control-on-single-page')) {
|
||||
// If all items were loaded on the first page then we can hide
|
||||
// the paging bar because there are no other pages to load.
|
||||
hide(root);
|
||||
disableNextControlButtons(root);
|
||||
disablePreviousControlButtons(root);
|
||||
} else {
|
||||
show(root);
|
||||
disableNextControlButtons(root);
|
||||
}
|
||||
});
|
||||
|
||||
// This event is fired after all of the requested pages have been rendered.
|
||||
PubSub.subscribe(id + PagedContentEvents.PAGES_SHOWN, function() {
|
||||
// All pages have been shown so turn off the loading flag.
|
||||
loading = false;
|
||||
});
|
||||
|
||||
// This is triggered when the paging limit is modified.
|
||||
PubSub.subscribe(id + PagedContentEvents.SET_ITEMS_PER_PAGE_LIMIT, function(limit) {
|
||||
// Update the limit.
|
||||
setLimit(root, limit);
|
||||
setLastPageNumber(root, 0);
|
||||
setActivePageNumber(root, 0);
|
||||
show(root);
|
||||
// Reload the data from page 1 again.
|
||||
showPage(root, 1, id);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialise the paging bar.
|
||||
* @param {object} root The root element.
|
||||
* @param {string} id A uniqie id for this instance.
|
||||
*/
|
||||
var init = function(root, id) {
|
||||
root = $(root);
|
||||
var pages = root.find(SELECTORS.PAGE);
|
||||
generatePageNumbers(root, pages);
|
||||
registerEventListeners(root, id);
|
||||
|
||||
if (hasActivePageNumber(root)) {
|
||||
var activePageNumber = getActivePageNumber(root);
|
||||
// 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.
|
||||
getPageByNumber(root, activePageNumber).click();
|
||||
if (activePageNumber == 1) {
|
||||
// If the first page is active then disable the previous buttons.
|
||||
disablePreviousControlButtons(root);
|
||||
}
|
||||
} else {
|
||||
// There was no active page number so load the first page using
|
||||
// the next button. This allows the infinite pagination to work.
|
||||
getNextButton(root).click();
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
init: init,
|
||||
showPage: showPage,
|
||||
rootSelector: SELECTORS.ROOT,
|
||||
};
|
||||
});
|
||||
|
|
77
lib/amd/src/paged_content_paging_bar_limit_selector.js
Normal file
77
lib/amd/src/paged_content_paging_bar_limit_selector.js
Normal file
|
@ -0,0 +1,77 @@
|
|||
// 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 dynamically changing the page limits.
|
||||
*
|
||||
* @module core/paged_content_paging_bar_limit_selector
|
||||
* @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',
|
||||
'core/pubsub'
|
||||
],
|
||||
function(
|
||||
$,
|
||||
CustomEvents,
|
||||
PagedContentEvents,
|
||||
PubSub
|
||||
) {
|
||||
|
||||
var SELECTORS = {
|
||||
ROOT: '[data-region="paging-control-limit-container"]',
|
||||
LIMIT_OPTION: '[data-limit]',
|
||||
LIMIT_TOGGLE: '[data-action="limit-toggle"]',
|
||||
};
|
||||
|
||||
/**
|
||||
* Trigger the SET_ITEMS_PER_PAGE_LIMIT event when the page limit option
|
||||
* is modified.
|
||||
*
|
||||
* @param {object} root The root element.
|
||||
* @param {string} id A unique id for this instance.
|
||||
*/
|
||||
var init = function(root, id) {
|
||||
root = $(root);
|
||||
|
||||
CustomEvents.define(root, [
|
||||
CustomEvents.events.activate
|
||||
]);
|
||||
|
||||
root.on(CustomEvents.events.activate, SELECTORS.LIMIT_OPTION, function(e, data) {
|
||||
var optionElement = $(e.target).closest(SELECTORS.LIMIT_OPTION);
|
||||
|
||||
if (optionElement.hasClass('active')) {
|
||||
// Don't do anything if it was the active option selected.
|
||||
return;
|
||||
}
|
||||
|
||||
var limit = parseInt(optionElement.attr('data-limit'), 10);
|
||||
// Tell the rest of the pagination components that the limit has changed.
|
||||
PubSub.publish(id + PagedContentEvents.SET_ITEMS_PER_PAGE_LIMIT, limit);
|
||||
|
||||
data.originalEvent.preventDefault();
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
init: init,
|
||||
rootSelector: SELECTORS.ROOT
|
||||
};
|
||||
});
|
|
@ -24,12 +24,14 @@ define(
|
|||
[
|
||||
'jquery',
|
||||
'core/custom_interaction_events',
|
||||
'core/paged_content_events'
|
||||
'core/paged_content_events',
|
||||
'core/pubsub'
|
||||
],
|
||||
function(
|
||||
$,
|
||||
CustomEvents,
|
||||
PagedContentEvents
|
||||
PagedContentEvents,
|
||||
PubSub
|
||||
) {
|
||||
|
||||
var SELECTORS = {
|
||||
|
@ -44,7 +46,7 @@ define(
|
|||
* Get the page number.
|
||||
*
|
||||
* @param {jquery} item The dropdown item.
|
||||
* @returns {int}
|
||||
* @returns {Number}
|
||||
*/
|
||||
var getPageNumber = function(item) {
|
||||
return parseInt(item.attr('data-page-number'), 10);
|
||||
|
@ -79,7 +81,7 @@ define(
|
|||
* Get the number of items to be loaded for the dropdown item.
|
||||
*
|
||||
* @param {jquery} item The dropdown item.
|
||||
* @returns {int}
|
||||
* @returns {Number}
|
||||
*/
|
||||
var getLimit = function(item) {
|
||||
return parseInt(item.attr('data-item-count'), 10);
|
||||
|
@ -91,7 +93,7 @@ define(
|
|||
*
|
||||
* @param {jquery} root The root element.
|
||||
* @param {jquery} item The dropdown item.
|
||||
* @returns {int}
|
||||
* @returns {Number}
|
||||
*/
|
||||
var getOffset = function(root, item) {
|
||||
if (item.attr('data-offset') != undefined) {
|
||||
|
@ -181,8 +183,9 @@ define(
|
|||
*
|
||||
* @param {jquery} root The root element.
|
||||
* @param {jquery} item The dropdown item.
|
||||
* @param {string} id A unique id for this instance.
|
||||
*/
|
||||
var setActiveItem = function(root, item) {
|
||||
var setActiveItem = function(root, item, id) {
|
||||
var prevItems = getPreviousItems(root, item);
|
||||
var allItems = prevItems.add(item);
|
||||
var eventPayload = generateEventPayload(root, allItems);
|
||||
|
@ -197,7 +200,7 @@ define(
|
|||
// Bootstrap 2 compatibility.
|
||||
toggle.append(caret);
|
||||
// Fire the event to tell the content to update.
|
||||
root.trigger(PagedContentEvents.SHOW_PAGES, [eventPayload]);
|
||||
PubSub.publish(id + PagedContentEvents.SHOW_PAGES, eventPayload);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -206,8 +209,9 @@ define(
|
|||
* new pages.
|
||||
*
|
||||
* @param {object} root The root element.
|
||||
* @param {string} id A unique id for this instance.
|
||||
*/
|
||||
var init = function(root) {
|
||||
var init = function(root, id) {
|
||||
root = $(root);
|
||||
var items = getAllItems(root);
|
||||
generatePageNumbers(items);
|
||||
|
@ -215,7 +219,7 @@ define(
|
|||
var activeItem = getActiveItem(root);
|
||||
if (activeItem.length) {
|
||||
// Fire the first event for the content to make sure it's visible.
|
||||
setActiveItem(root, activeItem);
|
||||
setActiveItem(root, activeItem, id);
|
||||
}
|
||||
|
||||
CustomEvents.define(root, [
|
||||
|
@ -224,7 +228,7 @@ define(
|
|||
|
||||
root.on(CustomEvents.events.activate, SELECTORS.DROPDOWN_ITEM, function(e, data) {
|
||||
var item = $(e.target).closest(SELECTORS.DROPDOWN_ITEM);
|
||||
setActiveItem(root, item);
|
||||
setActiveItem(root, item, id);
|
||||
|
||||
data.originalEvent.preventDefault();
|
||||
});
|
||||
|
|
74
lib/amd/src/pubsub.js
Normal file
74
lib/amd/src/pubsub.js
Normal file
|
@ -0,0 +1,74 @@
|
|||
// 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/>.
|
||||
|
||||
/**
|
||||
* A simple Javascript PubSub implementation.
|
||||
*
|
||||
* @module core/pubsub
|
||||
* @copyright 2018 Ryan Wyllie <ryan@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
define([], function() {
|
||||
|
||||
var events = {};
|
||||
|
||||
/**
|
||||
* Subscribe to an event.
|
||||
*
|
||||
* @param {string} eventName The name of the event to subscribe to.
|
||||
* @param {function} callback The callback function to run when eventName occurs.
|
||||
*/
|
||||
var subscribe = function(eventName, callback) {
|
||||
events[eventName] = events[eventName] || [];
|
||||
events[eventName].push(callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Unsubscribe from an event.
|
||||
*
|
||||
* @param {string} eventName The name of the event to unsubscribe from.
|
||||
* @param {function} callback The callback to unsubscribe.
|
||||
*/
|
||||
var unsubscribe = function(eventName, callback) {
|
||||
if (events[eventName]) {
|
||||
for (var i = 0; i < events[eventName].length; i++) {
|
||||
if (events[eventName][i] === callback) {
|
||||
events[eventName].splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Publish an event to all subscribers.
|
||||
*
|
||||
* @param {string} eventName The name of the event to publish.
|
||||
* @param {any} data The data to provide to the subscribed callbacks.
|
||||
*/
|
||||
var publish = function(eventName, data) {
|
||||
if (events[eventName]) {
|
||||
events[eventName].forEach(function(callback) {
|
||||
callback(data);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
subscribe: subscribe,
|
||||
unsubscribe: unsubscribe,
|
||||
publish: publish
|
||||
};
|
||||
});
|
|
@ -24,6 +24,8 @@
|
|||
define(['jquery', 'core/ajax', 'core/sessionstorage', 'core/config'],
|
||||
function($, Ajax, Storage, Config) {
|
||||
|
||||
var SECONDS_IN_DAY = 86400;
|
||||
|
||||
/** @var {object} promisesCache Store all promises we've seen so far. */
|
||||
var promisesCache = {};
|
||||
|
||||
|
@ -228,7 +230,42 @@ define(['jquery', 'core/ajax', 'core/sessionstorage', 'core/config'],
|
|||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* For a given timestamp get the midnight value in the user's timezone.
|
||||
*
|
||||
* The calculation is performed relative to the user's midnight timestamp
|
||||
* for today to ensure that timezones are preserved.
|
||||
*
|
||||
* E.g.
|
||||
* Input:
|
||||
* timestamp: 1514836800 (01/01/2018 8pm GMT)(02/01/2018 4am GMT+8)
|
||||
* midnight: 1514851200 (02/01/2018 midnight GMT)
|
||||
* Output:
|
||||
* 1514764800 (01/01/2018 midnight GMT)
|
||||
*
|
||||
* Input:
|
||||
* timestamp: 1514836800 (01/01/2018 8pm GMT)(02/01/2018 4am GMT+8)
|
||||
* midnight: 1514822400 (02/01/2018 midnight GMT+8)
|
||||
* Output:
|
||||
* 1514822400 (02/01/2018 midnight GMT+8)
|
||||
*
|
||||
* @param {Number} timestamp The timestamp to calculate from
|
||||
* @param {Number} todayMidnight The user's midnight timestamp
|
||||
* @return {Number} The midnight value of the user's timestamp
|
||||
*/
|
||||
var getUserMidnightForTimestamp = function(timestamp, todayMidnight) {
|
||||
var future = timestamp > todayMidnight;
|
||||
var diffSeconds = Math.abs(timestamp - todayMidnight);
|
||||
var diffDays = future ? Math.floor(diffSeconds / SECONDS_IN_DAY) : Math.ceil(diffSeconds / SECONDS_IN_DAY);
|
||||
var diffDaysInSeconds = diffDays * SECONDS_IN_DAY;
|
||||
// Is the timestamp in the future or past?
|
||||
var dayTimestamp = future ? todayMidnight + diffDaysInSeconds : todayMidnight - diffDaysInSeconds;
|
||||
return dayTimestamp;
|
||||
};
|
||||
|
||||
return {
|
||||
get: get
|
||||
get: get,
|
||||
getUserMidnightForTimestamp: getUserMidnightForTimestamp
|
||||
};
|
||||
});
|
||||
|
|
|
@ -2583,7 +2583,7 @@ function blocks_add_default_system_blocks() {
|
|||
$subpagepattern = null;
|
||||
}
|
||||
|
||||
$newblocks = array('private_files', 'online_users', 'badges', 'calendar_month', 'calendar_upcoming');
|
||||
$newblocks = array('timeline', 'private_files', 'online_users', 'badges', 'calendar_month', 'calendar_upcoming');
|
||||
$newcontent = array('lp', 'myoverview');
|
||||
$page->blocks->add_blocks(array(BLOCK_POS_RIGHT => $newblocks, 'content' => $newcontent), 'my-index', $subpagepattern);
|
||||
}
|
||||
|
|
|
@ -260,6 +260,7 @@ class icon_system_fontawesome extends icon_system_font {
|
|||
'core:i/ne_red_mark' => 'fa-remove',
|
||||
'core:i/new' => 'fa-bolt',
|
||||
'core:i/news' => 'fa-newspaper-o',
|
||||
'core:i/next' => 'fa-chevron-right',
|
||||
'core:i/nosubcat' => 'fa-plus-square-o',
|
||||
'core:i/notifications' => 'fa-bell',
|
||||
'core:i/open' => 'fa-folder-open',
|
||||
|
@ -270,6 +271,7 @@ class icon_system_fontawesome extends icon_system_font {
|
|||
'core:i/persona_sign_in_black' => 'fa-male',
|
||||
'core:i/portfolio' => 'fa-id-badge',
|
||||
'core:i/preview' => 'fa-search-plus',
|
||||
'core:i/previous' => 'fa-chevron-left',
|
||||
'core:i/privatefiles' => 'fa-file-o',
|
||||
'core:i/progressbar' => 'fa-spinner fa-spin',
|
||||
'core:i/publish' => 'fa-share',
|
||||
|
|
|
@ -1721,7 +1721,7 @@ class core_plugin_manager {
|
|||
'private_files', 'quiz_results', 'recent_activity',
|
||||
'rss_client', 'search_forums', 'section_links',
|
||||
'selfcompletion', 'settings', 'site_main_menu',
|
||||
'social_activities', 'tag_flickr', 'tag_youtube', 'tags'
|
||||
'social_activities', 'tag_flickr', 'tag_youtube', 'tags', 'timeline'
|
||||
),
|
||||
|
||||
'booktool' => array(
|
||||
|
|
|
@ -510,6 +510,14 @@ $functions = array(
|
|||
'ajax' => true,
|
||||
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
|
||||
),
|
||||
'core_course_get_enrolled_courses_by_timeline_classification' => array(
|
||||
'classname' => 'core_course_external',
|
||||
'methodname' => 'get_enrolled_courses_by_timeline_classification',
|
||||
'classpath' => 'course/externallib.php',
|
||||
'description' => 'List of enrolled courses for the given timeline classification (past, inprogress, or future).',
|
||||
'type' => 'read',
|
||||
'ajax' => true
|
||||
),
|
||||
'core_enrol_get_course_enrolment_methods' => array(
|
||||
'classname' => 'core_enrol_external',
|
||||
'methodname' => 'get_course_enrolment_methods',
|
||||
|
|
|
@ -557,9 +557,10 @@ function enrol_add_course_navigation(navigation_node $coursenode, $course) {
|
|||
* @param int $limit max number of courses
|
||||
* @param array $courseids the list of course ids to filter by
|
||||
* @param bool $allaccessible Include courses user is not enrolled in, but can access
|
||||
* @param int $offset Offset the result set by this number
|
||||
* @return array
|
||||
*/
|
||||
function enrol_get_my_courses($fields = null, $sort = null, $limit = 0, $courseids = [], $allaccessible = false) {
|
||||
function enrol_get_my_courses($fields = null, $sort = null, $limit = 0, $courseids = [], $allaccessible = false, $offset = 0) {
|
||||
global $DB, $USER, $CFG;
|
||||
|
||||
if ($sort === null) {
|
||||
|
@ -714,7 +715,7 @@ function enrol_get_my_courses($fields = null, $sort = null, $limit = 0, $coursei
|
|||
WHERE $wheres
|
||||
$orderby";
|
||||
|
||||
$courses = $DB->get_records_sql($sql, $params, 0, $limit);
|
||||
$courses = $DB->get_records_sql($sql, $params, $offset, $limit);
|
||||
|
||||
// preload contexts and check visibility
|
||||
foreach ($courses as $id=>$course) {
|
||||
|
|
|
@ -3045,7 +3045,7 @@ class global_navigation extends navigation_node {
|
|||
// Show a link to the course page if there are more courses the user is enrolled in.
|
||||
if ($showmorelinkinnav || $showmorelinkinflatnav) {
|
||||
// Adding hash to URL so the link is not highlighted in the navigation when clicked.
|
||||
$url = new moodle_url('/my/?myoverviewtab=courses');
|
||||
$url = new moodle_url('/my/');
|
||||
$parent = $this->rootnodes['mycourses'];
|
||||
$coursenode = $parent->add(get_string('morenavigationlinks'), $url, self::TYPE_CUSTOM, null, self::COURSE_INDEX_PAGE);
|
||||
|
||||
|
|
|
@ -1590,6 +1590,8 @@ class page_requirements_manager {
|
|||
$logconfig->level = 'trace';
|
||||
}
|
||||
$this->js_call_amd('core/log', 'setConfig', array($logconfig));
|
||||
// Add any global JS that needs to run on all pages.
|
||||
$this->js_call_amd('core/page_global', 'init');
|
||||
|
||||
// Call amd init functions.
|
||||
$output .= $this->get_amd_footercode();
|
||||
|
|
|
@ -53,29 +53,41 @@
|
|||
}
|
||||
}}
|
||||
<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}}
|
||||
{{^controlplacementbottom}}
|
||||
<div class="m-b-1">
|
||||
{{#pagingbar}}
|
||||
{{> core/paged_content_paging_bar }}
|
||||
{{/pagingbar}}
|
||||
{{#pagingdropdown}}
|
||||
{{> core/paged_content_paging_dropdown }}
|
||||
{{/pagingdropdown}}
|
||||
</div>
|
||||
{{/controlplacementbottom}}
|
||||
{{> core/paged_content_pages }}
|
||||
{{#controlplacementbottom}}
|
||||
<div class="m-t-1">
|
||||
{{#pagingbar}}
|
||||
{{> core/paged_content_paging_bar }}
|
||||
{{/pagingbar}}
|
||||
{{#pagingdropdown}}
|
||||
{{> core/paged_content_paging_dropdown }}
|
||||
{{/pagingdropdown}}
|
||||
</div>
|
||||
{{/controlplacementbottom}}
|
||||
</div>
|
||||
{{^skipjs}}
|
||||
{{#js}}
|
||||
require(
|
||||
[
|
||||
'jquery',
|
||||
'core/paged_content_pages'
|
||||
'core/paged_content'
|
||||
],
|
||||
function(
|
||||
$,
|
||||
PagedContent
|
||||
) {
|
||||
var container = $("#paged-content-container-{{uniqid}}");
|
||||
var pagingContent = container.find(PagedContent.rootSelector);
|
||||
|
||||
PagedContent.init(pagingContent, container);
|
||||
PagedContent.init(container);
|
||||
});
|
||||
{{/js}}
|
||||
{{/skipjs}}
|
||||
|
|
|
@ -35,7 +35,12 @@
|
|||
]
|
||||
}
|
||||
}}
|
||||
<div id="{{$pagingcontentid}}page-container-{{uniqid}}{{/pagingcontentid}}" data-region="page-container">
|
||||
<div
|
||||
id="{{$pagingcontentid}}page-container-{{uniqid}}{{/pagingcontentid}}"
|
||||
style="min-height: 50px"
|
||||
data-region="page-container"
|
||||
aria-live="polite"
|
||||
>
|
||||
{{#pages}}
|
||||
{{$paged-content-page}}
|
||||
{{> core/paged_content_page }}
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
"next": true,
|
||||
"first": true,
|
||||
"last": true,
|
||||
"activepagenumber": 1,
|
||||
"pages": [
|
||||
{
|
||||
"url": "#",
|
||||
|
@ -40,58 +41,148 @@
|
|||
]
|
||||
}
|
||||
}}
|
||||
<nav aria-label="{{label}}"
|
||||
id="{{$pagingbarid}}paging-bar-{{uniqid}}{{/pagingbarid}}"
|
||||
data-region="paging-bar"
|
||||
data-items-per-page="{{itemsperpage}}">
|
||||
<div
|
||||
data-region="paging-control-container"
|
||||
class="d-flex"
|
||||
>
|
||||
{{#showitemsperpageselector}}
|
||||
<div
|
||||
id="paging-control-limit-container-{{uniqid}}"
|
||||
data-region="paging-control-limit-container"
|
||||
class="d-inline-flex align-items-center"
|
||||
>
|
||||
<span class="mr-1">{{#str}} show {{/str}}</span>
|
||||
<div class="btn-group">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-outline-secondary dropdown-toggle"
|
||||
data-toggle="dropdown"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="false"
|
||||
data-action="limit-toggle"
|
||||
{{#arialabels.itemsperpage}}
|
||||
aria-label="{{.}}"
|
||||
{{/arialabels.itemsperpage}}
|
||||
{{^arialabels.itemsperpage}}
|
||||
aria-label="{{#str}} pagedcontentpagingbaritemsperpage, core, {{#itemsperpage}}{{#active}}{{value}}{{/active}}{{/itemsperpage}}{{/str}}"
|
||||
{{/arialabels.itemsperpage}}
|
||||
>
|
||||
{{#itemsperpage}}
|
||||
{{#active}}
|
||||
{{value}}
|
||||
{{/active}}
|
||||
{{/itemsperpage}}
|
||||
</button>
|
||||
<div
|
||||
role="menu"
|
||||
class="dropdown-menu"
|
||||
data-show-active-item
|
||||
{{#arialabels.itemsperpagecomponents}}
|
||||
data-active-item-button-aria-label-components="{{.}}"
|
||||
{{/arialabels.itemsperpagecomponents}}
|
||||
{{^arialabels.itemsperpagecomponents}}
|
||||
data-active-item-button-aria-label-components="pagedcontentpagingbaritemsperpage, core"
|
||||
{{/arialabels.itemsperpagecomponents}}
|
||||
>
|
||||
{{#itemsperpage}}
|
||||
<a
|
||||
class="dropdown-item {{#active}}active{{/active}}"
|
||||
href="#"
|
||||
data-limit={{value}}
|
||||
{{#active}}aria-current="true"{{/active}}
|
||||
>
|
||||
{{#value}}{{.}}{{/value}}
|
||||
{{^value}}{{#str}} all, core {{/str}}{{/value}}
|
||||
</a>
|
||||
{{/itemsperpage}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/showitemsperpageselector}}
|
||||
|
||||
<ul class="pagination">
|
||||
{{#previous}}
|
||||
{{< core/paged_content_paging_bar_item }}
|
||||
{{$item-content}}
|
||||
<span aria-hidden="true">«</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">»</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}}
|
||||
<nav
|
||||
role="navigation"
|
||||
id="{{$pagingbarid}}paging-bar-{{uniqid}}{{/pagingbarid}}"
|
||||
class="{{#showitemsperpageselector}}ml-auto{{/showitemsperpageselector}}"
|
||||
data-region="paging-bar"
|
||||
data-ignore-control-while-loading="{{ignorecontrolwhileloading}}"
|
||||
data-hide-control-on-single-page="{{hidecontrolonsinglepage}}"
|
||||
{{#activepagenumber}}
|
||||
data-active-page-number="{{.}}"
|
||||
{{/activepagenumber}}
|
||||
{{^activepagenumber}}
|
||||
data-active-page-number="1"
|
||||
{{/activepagenumber}}
|
||||
{{#showitemsperpageselector}}
|
||||
{{#itemsperpage}}
|
||||
{{#active}}
|
||||
data-items-per-page="{{value}}"
|
||||
{{/active}}
|
||||
{{/itemsperpage}}
|
||||
{{/showitemsperpageselector}}
|
||||
{{^showitemsperpageselector}}
|
||||
data-items-per-page="{{itemsperpage}}"
|
||||
{{/showitemsperpageselector}}
|
||||
{{#arialabels.paginationnav}}
|
||||
aria-label="{{.}}"
|
||||
{{/arialabels.paginationnav}}
|
||||
{{^arialabels.paginationnav}}
|
||||
aria-label="{{#str}} pagedcontentnavigation, core {{/str}}"
|
||||
{{/arialabels.paginationnav}}
|
||||
{{#arialabels.paginationnavitemcomponents}}
|
||||
data-aria-label-components-pagination-item="{{.}}"
|
||||
{{/arialabels.paginationnavitemcomponents}}
|
||||
{{^arialabels.paginationnavitemcomponents}}
|
||||
data-aria-label-components-pagination-item="pagedcontentnavigationitem, core"
|
||||
{{/arialabels.paginationnavitemcomponents}}
|
||||
{{#arialabels.paginationactivenavitemcomponents}}
|
||||
data-aria-label-components-pagination-active-item="{{.}}"
|
||||
{{/arialabels.paginationactivenavitemcomponents}}
|
||||
{{^arialabels.paginationactivenavitemcomponents}}
|
||||
data-aria-label-components-pagination-active-item="pagedcontentnavigationactiveitem, core"
|
||||
{{/arialabels.paginationactivenavitemcomponents}}
|
||||
>
|
||||
|
||||
<ul class="pagination mb-0">
|
||||
{{#previous}}
|
||||
{{< core/paged_content_paging_bar_item }}
|
||||
{{$item-content}}
|
||||
<span class="icon-no-margin dir-rtl-hide" aria-hidden="true">{{#pix}} i/previous, core {{/pix}}</span>
|
||||
<span class="icon-no-margin dir-ltr-hide" aria-hidden="true">{{#pix}} i/next, core {{/pix}}</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>
|
||||
{{/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>
|
||||
{{/item-content}}
|
||||
{{$attributes}}data-control="last"{{/attributes}}
|
||||
{{/ core/paged_content_paging_bar_item }}
|
||||
{{/last}}
|
||||
{{#next}}
|
||||
{{< core/paged_content_paging_bar_item }}
|
||||
{{$item-content}}
|
||||
<span class="icon-no-margin dir-rtl-hide" aria-hidden="true">{{#pix}} i/next, core {{/pix}}</span>
|
||||
<span class="icon-no-margin dir-ltr-hide" aria-hidden="true">{{#pix}} i/previous, core {{/pix}}</span>
|
||||
{{/item-content}}
|
||||
{{$attributes}}data-control="next"{{/attributes}}
|
||||
{{/ core/paged_content_paging_bar_item }}
|
||||
{{/next}}
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
|
|
|
@ -44,8 +44,8 @@
|
|||
data-region="paging-dropdown-container">
|
||||
|
||||
<button class="btn btn-secondary dropdown-toggle"
|
||||
id="paging-dropdown-menu-button-{{uniqid}}"
|
||||
type="button"
|
||||
id="dropdown-menu-button-{{uniqid}}"
|
||||
data-region="dropdown-toggle"
|
||||
data-toggle="dropdown"
|
||||
aria-haspopup="true"
|
||||
|
@ -57,7 +57,7 @@
|
|||
{{/active}}
|
||||
{{/options}}
|
||||
</button>
|
||||
<div class="dropdown-menu" aria-labelledby="dropdown-menu-button-{{uniqid}}">
|
||||
<div class="dropdown-menu" aria-labelledby="paging-dropdown-menu-button-{{uniqid}}">
|
||||
{{#options}}
|
||||
{{> core/paged_content_paging_dropdown_item }}
|
||||
{{/options}}
|
||||
|
@ -67,9 +67,3 @@
|
|||
{{/ 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}}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue