Compare commits

...

3 Commits

  1. 2
      amd/build/aria.min.js
  2. 1
      amd/build/aria.min.js.map
  3. 2
      amd/build/bootstrap/alert.min.js
  4. 1
      amd/build/bootstrap/alert.min.js.map
  5. 2
      amd/build/bootstrap/button.min.js
  6. 1
      amd/build/bootstrap/button.min.js.map
  7. 2
      amd/build/bootstrap/carousel.min.js
  8. 1
      amd/build/bootstrap/carousel.min.js.map
  9. 2
      amd/build/bootstrap/collapse.min.js
  10. 1
      amd/build/bootstrap/collapse.min.js.map
  11. 2
      amd/build/bootstrap/dropdown.min.js
  12. 1
      amd/build/bootstrap/dropdown.min.js.map
  13. 2
      amd/build/bootstrap/modal.min.js
  14. 1
      amd/build/bootstrap/modal.min.js.map
  15. 2
      amd/build/bootstrap/popover.min.js
  16. 1
      amd/build/bootstrap/popover.min.js.map
  17. 2
      amd/build/bootstrap/scrollspy.min.js
  18. 1
      amd/build/bootstrap/scrollspy.min.js.map
  19. 2
      amd/build/bootstrap/tab.min.js
  20. 1
      amd/build/bootstrap/tab.min.js.map
  21. 2
      amd/build/bootstrap/toast.min.js
  22. 1
      amd/build/bootstrap/toast.min.js.map
  23. 2
      amd/build/bootstrap/tools/sanitizer.min.js
  24. 1
      amd/build/bootstrap/tools/sanitizer.min.js.map
  25. 2
      amd/build/bootstrap/tooltip.min.js
  26. 1
      amd/build/bootstrap/tooltip.min.js.map
  27. 2
      amd/build/bootstrap/util.min.js
  28. 1
      amd/build/bootstrap/util.min.js.map
  29. 2
      amd/build/drawer.min.js
  30. 1
      amd/build/drawer.min.js.map
  31. 2
      amd/build/form-display-errors.min.js
  32. 1
      amd/build/form-display-errors.min.js.map
  33. 2
      amd/build/index.min.js
  34. 1
      amd/build/index.min.js.map
  35. 2
      amd/build/loader.min.js
  36. 1
      amd/build/loader.min.js.map
  37. 2
      amd/build/pending.min.js
  38. 1
      amd/build/pending.min.js.map
  39. 2
      amd/build/popover.min.js
  40. 1
      amd/build/popover.min.js.map
  41. 2
      amd/build/scroll.min.js
  42. 1
      amd/build/scroll.min.js.map
  43. 2
      amd/build/toast.min.js
  44. 1
      amd/build/toast.min.js.map
  45. 282
      amd/src/aria.js
  46. 173
      amd/src/bootstrap/alert.js
  47. 209
      amd/src/bootstrap/button.js
  48. 613
      amd/src/bootstrap/carousel.js
  49. 392
      amd/src/bootstrap/collapse.js
  50. 538
      amd/src/bootstrap/dropdown.js
  51. 629
      amd/src/bootstrap/modal.js
  52. 182
      amd/src/bootstrap/popover.js
  53. 324
      amd/src/bootstrap/scrollspy.js
  54. 255
      amd/src/bootstrap/tab.js
  55. 230
      amd/src/bootstrap/toast.js
  56. 127
      amd/src/bootstrap/tools/sanitizer.js
  57. 778
      amd/src/bootstrap/tooltip.js
  58. 198
      amd/src/bootstrap/util.js
  59. 199
      amd/src/drawer.js
  60. 114
      amd/src/form-display-errors.js
  61. 34
      amd/src/index.js
  62. 109
      amd/src/loader.js
  63. 133
      amd/src/pending.js
  64. 22
      amd/src/popover.js
  65. 72
      amd/src/scroll.js
  66. 30
      amd/src/toast.js
  67. 8
      classes/admin_settingspage_tabs.php
  68. 10
      classes/autoprefixer.php
  69. 50
      classes/output/block_settings_renderer.php
  70. 52
      classes/output/core/admin_renderer.php
  71. 81
      classes/output/core/course_renderer.php
  72. 140
      classes/output/core/files_renderer.php
  73. 58
      classes/output/core_question/bank_renderer.php
  74. 691
      classes/output/core_renderer.php
  75. 197
      classes/output/core_renderer_maintenance.php
  76. 52
      classes/privacy/provider.php
  77. 106
      cli/import-bootswatch.php
  78. 155
      config.php
  79. BIN
      js/apple-touch-icon-114x114.png?v=OmmK4dNQkX
  80. BIN
      js/apple-touch-icon-120x120.png?v=OmmK4dNQkX
  81. BIN
      js/apple-touch-icon-144x144.png?v=OmmK4dNQkX
  82. BIN
      js/apple-touch-icon-152x152.png?v=OmmK4dNQkX
  83. BIN
      js/apple-touch-icon-180x180.png?v=OmmK4dNQkX
  84. BIN
      js/apple-touch-icon-57x57.png?v=OmmK4dNQkX
  85. BIN
      js/apple-touch-icon-60x60.png?v=OmmK4dNQkX
  86. BIN
      js/apple-touch-icon-72x72.png?v=OmmK4dNQkX
  87. BIN
      js/apple-touch-icon-76x76.png?v=OmmK4dNQkX
  88. 48
      js/audio.min.js
  89. 35
      js/audiojs-doc-ready.js
  90. 2381
      js/base-cachekey3894.css
  91. 25
      js/collapse-image.js
  92. 10253
      js/jquery.js
  93. 2
      js/jquery.lazyload.min.js
  94. 11
      js/manifest.json?v=697oma3x8q
  95. 169
      js/resourceconfigbootstrapbootstrap.min-cachekey1567.js
  96. 335
      js/resourceconfigjqueryjquery-1.11.2.min-cachekey3986.js
  97. 2208
      js/resourcesenado.jqueryjquery-cachekey5865.js
  98. 85
      js/resourcestaticjscalendario-cachekey4269.js
  99. 68
      js/resourcestaticjsphoto-gallery-cachekey3318.js
  100. 22
      js/root_index.js

2
amd/build/aria.min.js

@ -0,0 +1,2 @@
define ("theme_ilb/aria",["exports","jquery","core/pending"],function(a,b,c){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.init=void 0;b=d(b);c=d(c);function d(a){return a&&a.__esModule?a:{default:a}}var e=function(){var a=!1,d=function(){a=!0},e=function(){var b=a;a=!1;return b};document.addEventListener("keydown",function(a){if(a.target.matches("[data-toggle=\"dropdown\"]")){var b=a.key;if("ArrowUp"==b){d()}if(" "==b||"Enter"==b){a.preventDefault();a.target.click()}}});var f=function(a){setTimeout(function delayedFocus(b){a.focus();b.resolve()},50,new c.default("core/aria:delayed-focus"))};(0,b.default)(".dropdown").on("shown.bs.dropdown",function(a){var b=a.target.querySelector("[role=\"menu\"]"),c=!1,d=!1;if(b){c=b.querySelectorAll("[role=\"menuitem\"]")}if(c&&0<c.length){if(e()){d=c[c.length-1]}else{d=c[0]}}if(d){f(d)}});document.addEventListener("keypress",function(a){if(a.target.matches(".dropdown [role=\"menu\"] [role=\"menuitem\"]")){var g=a.target.closest("[role=\"menu\"]");if(!g){return}var h=g.querySelectorAll("[role=\"menuitem\"]");if(!h){return}for(var b=a.key.toLowerCase(),c=0;c<h.length;c++){var d=h[c],e=d.text.trim().toLowerCase();if(0==e.indexOf(b)){f(d);break}}}});document.addEventListener("keydown",function(a){if(a.target.matches(".dropdown [role=\"menu\"] [role=\"menuitem\"]")){var b=a.key,c=!1,d=a.target.closest("[role=\"menu\"]");if(!d){return}var e=d.querySelectorAll("[role=\"menuitem\"]");if(!e){return}if("ArrowDown"==b){for(var g=0;g<e.length-1;g++){if(e[g]==a.target){c=e[g+1];break}}if(!c){c=e[0]}}else if("ArrowUp"==b){for(var h=1;h<e.length;h++){if(e[h]==a.target){c=e[h-1];break}}if(!c){c=e[e.length-1]}}else if("Home"==b){c=e[0]}else if("End"==b){c=e[e.length-1]}if(c){a.preventDefault();f(c)}}});(0,b.default)(".dropdown").on("hidden.bs.dropdown",function(a){var b=a.target.querySelector("[data-toggle=\"dropdown\"]");if(b){f(b)}})},f=function(){window.addEventListener("load",function(){var a=document.querySelectorAll("[data-aria-autofocus=\"true\"][role=\"alert\"]");Array.prototype.forEach.call(a,function(a){a.innerHTML+=" ";a.removeAttribute("data-aria-autofocus")})})},g=function(a){for(var c=a.target.closest("[role=\"tablist\"]"),d="vertical"==c.getAttribute("aria-orientation"),e=window.right_to_left(),f=d?"ArrowDown":e?"ArrowLeft":"ArrowRight",g=d?"ArrowUp":e?"ArrowRight":"ArrowLeft",h=Array.prototype.filter.call(c.querySelectorAll("[role=\"tab\"]"),function(a){return"none"!==getComputedStyle(a).display}),j=0;j<h.length;j++){h[j].index=j}switch(a.key){case f:a.preventDefault();if(a.target.index!==void 0&&h[a.target.index+1]){h[a.target.index+1].focus()}else{h[0].focus()}break;case g:a.preventDefault();if(a.target.index!==void 0&&h[a.target.index-1]){h[a.target.index-1].focus()}else{h[h.length-1].focus()}break;case"Home":a.preventDefault();h[0].focus();break;case"End":a.preventDefault();h[h.length-1].focus();break;case"Enter":case" ":a.preventDefault();(0,b.default)(a.target).tab("show");h.forEach(function(a){a.tabIndex=-1});a.target.tabIndex=0;}},h=function(){document.addEventListener("keydown",function(a){if(["ArrowUp","ArrowDown","ArrowLeft","ArrowRight","Home","End","Enter"," "].includes(a.key)){if(a.target.matches("[role=\"tablist\"] [role=\"tab\"]")){g(a)}}});document.addEventListener("click",function(a){if(a.target.matches("[role=\"tablist\"] [role=\"tab\"]")){var c=a.target.closest("[role=\"tablist\"]").querySelectorAll("[role=\"tab\"]");a.preventDefault();(0,b.default)(a.target).tab("show");c.forEach(function(a){a.tabIndex=-1});a.target.tabIndex=0}})};a.init=function init(){e();f();h()}});
//# sourceMappingURL=aria.min.js.map

1
amd/build/aria.min.js.map

File diff suppressed because one or more lines are too long

2
amd/build/bootstrap/alert.min.js

@ -0,0 +1,2 @@
define ("theme_ilb/bootstrap/alert",["exports","jquery","./util"],function(a,b,c){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;b=d(b);c=d(c);function d(a){return a&&a.__esModule?a:{default:a}}function e(a,b){if(!(a instanceof b)){throw new TypeError("Cannot call a class as a function")}}function f(a,b){for(var c=0,d;c<b.length;c++){d=b[c];d.enumerable=d.enumerable||!1;d.configurable=!0;if("value"in d)d.writable=!0;Object.defineProperty(a,d.key,d)}}function g(a,b,c){if(b)f(a.prototype,b);if(c)f(a,c);return a}var h="bs.alert",i=".".concat(h),j=b.default.fn.alert,k="close".concat(i),l="closed".concat(i),m="click".concat(i).concat(".data-api"),n=function(){function a(b){e(this,a);this._element=b}g(a,[{key:"close",value:function close(a){var b=this._element;if(a){b=this._getRootElement(a)}var c=this._triggerCloseEvent(b);if(c.isDefaultPrevented()){return}this._removeElement(b)}},{key:"dispose",value:function dispose(){b.default.removeData(this._element,h);this._element=null}},{key:"_getRootElement",value:function _getRootElement(a){var d=c.default.getSelectorFromElement(a),e=!1;if(d){e=document.querySelector(d)}if(!e){e=(0,b.default)(a).closest(".".concat("alert"))[0]}return e}},{key:"_triggerCloseEvent",value:function _triggerCloseEvent(a){var c=b.default.Event(k);(0,b.default)(a).trigger(c);return c}},{key:"_removeElement",value:function _removeElement(a){var d=this;(0,b.default)(a).removeClass("show");if(!(0,b.default)(a).hasClass("fade")){this._destroyElement(a);return}var e=c.default.getTransitionDurationFromElement(a);(0,b.default)(a).one(c.default.TRANSITION_END,function(b){return d._destroyElement(a,b)}).emulateTransitionEnd(e)}},{key:"_destroyElement",value:function _destroyElement(a){(0,b.default)(a).detach().trigger(l).remove()}}],[{key:"_jQueryInterface",value:function _jQueryInterface(c){return this.each(function(){var d=(0,b.default)(this),e=d.data(h);if(!e){e=new a(this);d.data(h,e)}if("close"===c){e[c](this)}})}},{key:"_handleDismiss",value:function _handleDismiss(a){return function(b){if(b){b.preventDefault()}a.close(this)}}},{key:"VERSION",get:function get(){return"4.6.0"}}]);return a}();(0,b.default)(document).on(m,"[data-dismiss=\"alert\"]",n._handleDismiss(new n));b.default.fn.alert=n._jQueryInterface;b.default.fn.alert.Constructor=n;b.default.fn.alert.noConflict=function(){b.default.fn.alert=j;return n._jQueryInterface};a.default=n;return a.default});
//# sourceMappingURL=alert.min.js.map

1
amd/build/bootstrap/alert.min.js.map

File diff suppressed because one or more lines are too long

2
amd/build/bootstrap/button.min.js

@ -0,0 +1,2 @@
define ("theme_ilb/bootstrap/button",["exports","jquery"],function(a,b){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;b=function(a){return a&&a.__esModule?a:{default:a}}(b);function c(a,b){if(!(a instanceof b)){throw new TypeError("Cannot call a class as a function")}}function d(a,b){for(var c=0,d;c<b.length;c++){d=b[c];d.enumerable=d.enumerable||!1;d.configurable=!0;if("value"in d)d.writable=!0;Object.defineProperty(a,d.key,d)}}function e(a,b,c){if(b)d(a.prototype,b);if(c)d(a,c);return a}var f="bs.button",g=".".concat(f),h=".data-api",i=b.default.fn.button,j="active",k="[data-toggle^=\"button\"]",l="input:not([type=\"hidden\"])",m=".btn",n="click".concat(g).concat(h),o="focus".concat(g).concat(h," ")+"blur".concat(g).concat(h),p="load".concat(g).concat(h),q=function(){function a(b){c(this,a);this._element=b;this.shouldAvoidTriggerChange=!1}e(a,[{key:"toggle",value:function toggle(){var a=!0,c=!0,d=(0,b.default)(this._element).closest("[data-toggle=\"buttons\"]")[0];if(d){var e=this._element.querySelector(l);if(e){if("radio"===e.type){if(e.checked&&this._element.classList.contains(j)){a=!1}else{var f=d.querySelector(".active");if(f){(0,b.default)(f).removeClass(j)}}}if(a){if("checkbox"===e.type||"radio"===e.type){e.checked=!this._element.classList.contains(j)}if(!this.shouldAvoidTriggerChange){(0,b.default)(e).trigger("change")}}e.focus();c=!1}}if(!(this._element.hasAttribute("disabled")||this._element.classList.contains("disabled"))){if(c){this._element.setAttribute("aria-pressed",!this._element.classList.contains(j))}if(a){(0,b.default)(this._element).toggleClass(j)}}}},{key:"dispose",value:function dispose(){b.default.removeData(this._element,f);this._element=null}}],[{key:"_jQueryInterface",value:function _jQueryInterface(c,d){return this.each(function(){var e=(0,b.default)(this),g=e.data(f);if(!g){g=new a(this);e.data(f,g)}g.shouldAvoidTriggerChange=d;if("toggle"===c){g[c]()}})}},{key:"VERSION",get:function get(){return"4.6.0"}}]);return a}();(0,b.default)(document).on(n,k,function(a){var c=a.target,d=c;if(!(0,b.default)(c).hasClass("btn")){c=(0,b.default)(c).closest(m)[0]}if(!c||c.hasAttribute("disabled")||c.classList.contains("disabled")){a.preventDefault()}else{var e=c.querySelector(l);if(e&&(e.hasAttribute("disabled")||e.classList.contains("disabled"))){a.preventDefault();return}if("INPUT"===d.tagName||"LABEL"!==c.tagName){q._jQueryInterface.call((0,b.default)(c),"toggle","INPUT"===d.tagName)}}}).on(o,k,function(a){var c=(0,b.default)(a.target).closest(m)[0];(0,b.default)(c).toggleClass("focus",/^focus(in)?$/.test(a.type))});(0,b.default)(window).on(p,function(){for(var a=[].slice.call(document.querySelectorAll("[data-toggle=\"buttons\"] .btn")),b=0,c=a.length;b<c;b++){var d=a[b],e=d.querySelector(l);if(e.checked||e.hasAttribute("checked")){d.classList.add(j)}else{d.classList.remove(j)}}a=[].slice.call(document.querySelectorAll("[data-toggle=\"button\"]"));for(var f=0,g=a.length,h;f<g;f++){h=a[f];if("true"===h.getAttribute("aria-pressed")){h.classList.add(j)}else{h.classList.remove(j)}}});b.default.fn.button=q._jQueryInterface;b.default.fn.button.Constructor=q;b.default.fn.button.noConflict=function(){b.default.fn.button=i;return q._jQueryInterface};a.default=q;return a.default});
//# sourceMappingURL=button.min.js.map

1
amd/build/bootstrap/button.min.js.map

File diff suppressed because one or more lines are too long

2
amd/build/bootstrap/carousel.min.js

File diff suppressed because one or more lines are too long

1
amd/build/bootstrap/carousel.min.js.map

File diff suppressed because one or more lines are too long

2
amd/build/bootstrap/collapse.min.js

File diff suppressed because one or more lines are too long

1
amd/build/bootstrap/collapse.min.js.map

File diff suppressed because one or more lines are too long

2
amd/build/bootstrap/dropdown.min.js

File diff suppressed because one or more lines are too long

1
amd/build/bootstrap/dropdown.min.js.map

File diff suppressed because one or more lines are too long

2
amd/build/bootstrap/modal.min.js

File diff suppressed because one or more lines are too long

1
amd/build/bootstrap/modal.min.js.map

File diff suppressed because one or more lines are too long

2
amd/build/bootstrap/popover.min.js

File diff suppressed because one or more lines are too long

1
amd/build/bootstrap/popover.min.js.map

File diff suppressed because one or more lines are too long

2
amd/build/bootstrap/scrollspy.min.js

File diff suppressed because one or more lines are too long

1
amd/build/bootstrap/scrollspy.min.js.map

File diff suppressed because one or more lines are too long

2
amd/build/bootstrap/tab.min.js

@ -0,0 +1,2 @@
define ("theme_ilb/bootstrap/tab",["exports","jquery","./util"],function(a,b,c){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;b=d(b);c=d(c);function d(a){return a&&a.__esModule?a:{default:a}}function e(a,b){if(!(a instanceof b)){throw new TypeError("Cannot call a class as a function")}}function f(a,b){for(var c=0,d;c<b.length;c++){d=b[c];d.enumerable=d.enumerable||!1;d.configurable=!0;if("value"in d)d.writable=!0;Object.defineProperty(a,d.key,d)}}function g(a,b,c){if(b)f(a.prototype,b);if(c)f(a,c);return a}var h="bs.tab",i=".".concat(h),j=b.default.fn.tab,k="hide".concat(i),l="hidden".concat(i),m="show".concat(i),n="shown".concat(i),o="click".concat(i).concat(".data-api"),p="active",q="fade",r="show",s=".active",t="> li > .active",u=function(){function a(b){e(this,a);this._element=b}g(a,[{key:"show",value:function show(){var a=this;if(this._element.parentNode&&this._element.parentNode.nodeType===Node.ELEMENT_NODE&&(0,b.default)(this._element).hasClass(p)||(0,b.default)(this._element).hasClass("disabled")){return}var d,e,f=(0,b.default)(this._element).closest(".nav, .list-group")[0],g=c.default.getSelectorFromElement(this._element);if(f){var o="UL"===f.nodeName||"OL"===f.nodeName?t:s;e=b.default.makeArray((0,b.default)(f).find(o));e=e[e.length-1]}var h=b.default.Event(k,{relatedTarget:this._element}),i=b.default.Event(m,{relatedTarget:e});if(e){(0,b.default)(e).trigger(h)}(0,b.default)(this._element).trigger(i);if(i.isDefaultPrevented()||h.isDefaultPrevented()){return}if(g){d=document.querySelector(g)}this._activate(this._element,f);var j=function(){var c=b.default.Event(l,{relatedTarget:a._element}),d=b.default.Event(n,{relatedTarget:e});(0,b.default)(e).trigger(c);(0,b.default)(a._element).trigger(d)};if(d){this._activate(d,d.parentNode,j)}else{j()}}},{key:"dispose",value:function dispose(){b.default.removeData(this._element,h);this._element=null}},{key:"_activate",value:function _activate(a,d,e){var f=this,g=d&&("UL"===d.nodeName||"OL"===d.nodeName)?(0,b.default)(d).find(t):(0,b.default)(d).children(s),h=g[0],i=e&&h&&(0,b.default)(h).hasClass(q),j=function(){return f._transitionComplete(a,h,e)};if(h&&i){var k=c.default.getTransitionDurationFromElement(h);(0,b.default)(h).removeClass(r).one(c.default.TRANSITION_END,j).emulateTransitionEnd(k)}else{j()}}},{key:"_transitionComplete",value:function _transitionComplete(a,d,e){if(d){(0,b.default)(d).removeClass(p);var f=(0,b.default)(d.parentNode).find("> .dropdown-menu .active")[0];if(f){(0,b.default)(f).removeClass(p)}if("tab"===d.getAttribute("role")){d.setAttribute("aria-selected",!1)}}(0,b.default)(a).addClass(p);if("tab"===a.getAttribute("role")){a.setAttribute("aria-selected",!0)}c.default.reflow(a);if(a.classList.contains(q)){a.classList.add(r)}if(a.parentNode&&(0,b.default)(a.parentNode).hasClass("dropdown-menu")){var g=(0,b.default)(a).closest(".dropdown")[0];if(g){var h=[].slice.call(g.querySelectorAll(".dropdown-toggle"));(0,b.default)(h).addClass(p)}a.setAttribute("aria-expanded",!0)}if(e){e()}}}],[{key:"_jQueryInterface",value:function _jQueryInterface(c){return this.each(function(){var d=(0,b.default)(this),e=d.data(h);if(!e){e=new a(this);d.data(h,e)}if("string"==typeof c){if("undefined"==typeof e[c]){throw new TypeError("No method named \"".concat(c,"\""))}e[c]()}})}},{key:"VERSION",get:function get(){return"4.6.0"}}]);return a}();(0,b.default)(document).on(o,"[data-toggle=\"tab\"], [data-toggle=\"pill\"], [data-toggle=\"list\"]",function(a){a.preventDefault();u._jQueryInterface.call((0,b.default)(this),"show")});b.default.fn.tab=u._jQueryInterface;b.default.fn.tab.Constructor=u;b.default.fn.tab.noConflict=function(){b.default.fn.tab=j;return u._jQueryInterface};a.default=u;return a.default});
//# sourceMappingURL=tab.min.js.map

1
amd/build/bootstrap/tab.min.js.map

File diff suppressed because one or more lines are too long

2
amd/build/bootstrap/toast.min.js

@ -0,0 +1,2 @@
define ("theme_ilb/bootstrap/toast",["exports","jquery","./util"],function(a,b,c){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;b=d(b);c=d(c);function d(a){return a&&a.__esModule?a:{default:a}}function e(a){"@babel/helpers - typeof";if("function"==typeof Symbol&&"symbol"==typeof Symbol.iterator){e=function(a){return typeof a}}else{e=function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a}}return e(a)}function f(a,b){var c=Object.keys(a);if(Object.getOwnPropertySymbols){var d=Object.getOwnPropertySymbols(a);if(b)d=d.filter(function(b){return Object.getOwnPropertyDescriptor(a,b).enumerable});c.push.apply(c,d)}return c}function g(a){for(var b=1,c;b<arguments.length;b++){c=null!=arguments[b]?arguments[b]:{};if(b%2){f(Object(c),!0).forEach(function(b){h(a,b,c[b])})}else if(Object.getOwnPropertyDescriptors){Object.defineProperties(a,Object.getOwnPropertyDescriptors(c))}else{f(Object(c)).forEach(function(b){Object.defineProperty(a,b,Object.getOwnPropertyDescriptor(c,b))})}}return a}function h(a,b,c){if(b in a){Object.defineProperty(a,b,{value:c,enumerable:!0,configurable:!0,writable:!0})}else{a[b]=c}return a}function i(a,b){if(!(a instanceof b)){throw new TypeError("Cannot call a class as a function")}}function j(a,b){for(var c=0,d;c<b.length;c++){d=b[c];d.enumerable=d.enumerable||!1;d.configurable=!0;if("value"in d)d.writable=!0;Object.defineProperty(a,d.key,d)}}function k(a,b,c){if(b)j(a.prototype,b);if(c)j(a,c);return a}var l="bs.toast",m=".".concat(l),n=b.default.fn.toast,o="click.dismiss".concat(m),p="hide".concat(m),q="hidden".concat(m),r="show".concat(m),s="shown".concat(m),t="hide",u="show",v="showing",w={animation:"boolean",autohide:"boolean",delay:"number"},x={animation:!0,autohide:!0,delay:500},y=function(){function a(b,c){i(this,a);this._element=b;this._config=this._getConfig(c);this._timeout=null;this._setListeners()}k(a,[{key:"show",value:function show(){var a=this,d=b.default.Event(r);(0,b.default)(this._element).trigger(d);if(d.isDefaultPrevented()){return}this._clearTimeout();if(this._config.animation){this._element.classList.add("fade")}var e=function(){a._element.classList.remove(v);a._element.classList.add(u);(0,b.default)(a._element).trigger(s);if(a._config.autohide){a._timeout=setTimeout(function(){a.hide()},a._config.delay)}};this._element.classList.remove(t);c.default.reflow(this._element);this._element.classList.add(v);if(this._config.animation){var f=c.default.getTransitionDurationFromElement(this._element);(0,b.default)(this._element).one(c.default.TRANSITION_END,e).emulateTransitionEnd(f)}else{e()}}},{key:"hide",value:function hide(){if(!this._element.classList.contains(u)){return}var a=b.default.Event(p);(0,b.default)(this._element).trigger(a);if(a.isDefaultPrevented()){return}this._close()}},{key:"dispose",value:function dispose(){this._clearTimeout();if(this._element.classList.contains(u)){this._element.classList.remove(u)}(0,b.default)(this._element).off(o);b.default.removeData(this._element,l);this._element=null;this._config=null}},{key:"_getConfig",value:function _getConfig(a){a=g({},x,{},(0,b.default)(this._element).data(),{},"object"===e(a)&&a?a:{});c.default.typeCheckConfig("toast",a,this.constructor.DefaultType);return a}},{key:"_setListeners",value:function _setListeners(){var a=this;(0,b.default)(this._element).on(o,"[data-dismiss=\"toast\"]",function(){return a.hide()})}},{key:"_close",value:function _close(){var a=this,d=function(){a._element.classList.add(t);(0,b.default)(a._element).trigger(q)};this._element.classList.remove(u);if(this._config.animation){var e=c.default.getTransitionDurationFromElement(this._element);(0,b.default)(this._element).one(c.default.TRANSITION_END,d).emulateTransitionEnd(e)}else{d()}}},{key:"_clearTimeout",value:function _clearTimeout(){clearTimeout(this._timeout);this._timeout=null}}],[{key:"_jQueryInterface",value:function _jQueryInterface(c){return this.each(function(){var d=(0,b.default)(this),f=d.data(l),g="object"===e(c)&&c;if(!f){f=new a(this,g);d.data(l,f)}if("string"==typeof c){if("undefined"==typeof f[c]){throw new TypeError("No method named \"".concat(c,"\""))}f[c](this)}})}},{key:"VERSION",get:function get(){return"4.6.0"}},{key:"DefaultType",get:function get(){return w}},{key:"Default",get:function get(){return x}}]);return a}();b.default.fn.toast=y._jQueryInterface;b.default.fn.toast.Constructor=y;b.default.fn.toast.noConflict=function(){b.default.fn.toast=n;return y._jQueryInterface};a.default=y;return a.default});
//# sourceMappingURL=toast.min.js.map

1
amd/build/bootstrap/toast.min.js.map

File diff suppressed because one or more lines are too long

2
amd/build/bootstrap/tools/sanitizer.min.js

@ -0,0 +1,2 @@
define ("theme_ilb/bootstrap/tools/sanitizer",["exports"],function(a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.sanitizeHtml=function(a,c,d){if(0===a.length){return a}if(d&&"function"==typeof d){return d(a)}for(var e=new window.DOMParser,f=e.parseFromString(a,"text/html"),g=Object.keys(c),h=[].slice.call(f.body.querySelectorAll("*")),j=function(a){var d=h[a],e=d.nodeName.toLowerCase();if(-1===g.indexOf(d.nodeName.toLowerCase())){d.parentNode.removeChild(d);return"continue"}var f=[].slice.call(d.attributes),i=[].concat(c["*"]||[],c[e]||[]);f.forEach(function(a){if(!b(a,i)){d.removeAttribute(a.nodeName)}})},k=0,l=h.length,m;k<l;k++){m=j(k,l);if("continue"===m)continue}return f.body.innerHTML};a.DefaultWhitelist=void 0;var c=["background","cite","href","itemtype","longdesc","poster","src","xlink:href"];a.DefaultWhitelist={"*":["class","dir","id","lang","role",/^aria-[\w-]*$/i],a:["target","href","title","rel"],area:[],b:[],br:[],col:[],code:[],div:[],em:[],hr:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],i:[],img:["src","srcset","alt","title","width","height"],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]};function b(a,b){var d=a.nodeName.toLowerCase();if(-1!==b.indexOf(d)){if(-1!==c.indexOf(d)){return!!(a.nodeValue.match(/^(?:(?:https?|mailto|ftp|tel|file):|[^#&/:?]*(?:[#/?]|$))/gi)||a.nodeValue.match(/^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[\d+/a-z]+=*$/i))}return!0}for(var e=b.filter(function(a){return a instanceof RegExp}),f=0,g=e.length;f<g;f++){if(d.match(e[f])){return!0}}return!1}});
//# sourceMappingURL=sanitizer.min.js.map

1
amd/build/bootstrap/tools/sanitizer.min.js.map

File diff suppressed because one or more lines are too long

2
amd/build/bootstrap/tooltip.min.js

File diff suppressed because one or more lines are too long

1
amd/build/bootstrap/tooltip.min.js.map

File diff suppressed because one or more lines are too long

2
amd/build/bootstrap/util.min.js

@ -0,0 +1,2 @@
define ("theme_ilb/bootstrap/util",["exports","jquery"],function(a,b){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;b=function(a){return a&&a.__esModule?a:{default:a}}(b);function c(a){if(null===a||"undefined"==typeof a){return"".concat(a)}return{}.toString.call(a).match(/\s([a-z]+)/i)[1].toLowerCase()}function d(){return{bindType:"transitionend",delegateType:"transitionend",handle:function handle(a){if((0,b.default)(a.target).is(this)){return a.handleObj.handler.apply(this,arguments)}}}}function e(a){var c=this,d=!1;(0,b.default)(this).one(g.TRANSITION_END,function(){d=!0});setTimeout(function(){if(!d){g.triggerTransitionEnd(c)}},a);return this}function f(){b.default.fn.emulateTransitionEnd=e;b.default.event.special[g.TRANSITION_END]=d()}var g={TRANSITION_END:"bsTransitionEnd",getUID:function getUID(a){do{a+=~~(Math.random()*1e6)}while(document.getElementById(a));return a},getSelectorFromElement:function getSelectorFromElement(a){var b=a.getAttribute("data-target");if(!b||"#"===b){var c=a.getAttribute("href");b=c&&"#"!==c?c.trim():""}try{return document.querySelector(b)?b:null}catch(a){return null}},getTransitionDurationFromElement:function getTransitionDurationFromElement(a){if(!a){return 0}var c=(0,b.default)(a).css("transition-duration"),d=(0,b.default)(a).css("transition-delay"),e=parseFloat(c),f=parseFloat(d);if(!e&&!f){return 0}c=c.split(",")[0];d=d.split(",")[0];return(parseFloat(c)+parseFloat(d))*1e3},reflow:function reflow(a){return a.offsetHeight},triggerTransitionEnd:function triggerTransitionEnd(a){(0,b.default)(a).trigger("transitionend")},supportsTransitionEnd:function supportsTransitionEnd(){return!0},isElement:function isElement(a){return(a[0]||a).nodeType},typeCheckConfig:function typeCheckConfig(a,b,d){for(var i in d){if(Object.prototype.hasOwnProperty.call(d,i)){var e=d[i],f=b[i],h=f&&g.isElement(f)?"element":c(f);if(!new RegExp(e).test(h)){throw new Error("".concat(a.toUpperCase(),": ")+"Option \"".concat(i,"\" provided type \"").concat(h,"\" ")+"but expected type \"".concat(e,"\"."))}}}},findShadowRoot:function findShadowRoot(a){if(!document.documentElement.attachShadow){return null}if("function"==typeof a.getRootNode){var b=a.getRootNode();return b instanceof ShadowRoot?b:null}if(a instanceof ShadowRoot){return a}if(!a.parentNode){return null}return g.findShadowRoot(a.parentNode)},jQueryDetection:function jQueryDetection(){if("undefined"==typeof b.default){throw new TypeError("Bootstrap's JavaScript requires jQuery. jQuery must be included before Bootstrap's JavaScript.")}var a=b.default.fn.jquery.split(" ")[0].split("."),c=9;if(a[0]<2&&a[1]<c||a[0]===1&&a[1]===c&&a[2]<1||a[0]>=4){throw new Error("Bootstrap's JavaScript requires at least jQuery v1.9.1 but less than v4.0.0")}}};g.jQueryDetection();f();a.default=g;return a.default});
//# sourceMappingURL=util.min.js.map

1
amd/build/bootstrap/util.min.js.map

File diff suppressed because one or more lines are too long

2
amd/build/drawer.min.js

@ -0,0 +1,2 @@
define ("theme_ilb/drawer",["jquery","core/custom_interaction_events","core/log","core/pubsub","core/aria"],function(a,b,c,d,e){var f={TOGGLE_REGION:"[data-region=\"drawer-toggle\"]",TOGGLE_ACTION:"[data-action=\"toggle-drawer\"]",TOGGLE_TARGET:"aria-controls",TOGGLE_SIDE:"left",BODY:"body",SECTION:".list-group-item[href*=\"#section-\"]",DRAWER:"#nav-drawer"},g=768>a(document).width(),h=function(){if(!a(f.TOGGLE_REGION).length){c.debug("Page is missing a drawer region")}if(!a(f.TOGGLE_ACTION).length){c.debug("Page is missing a drawer toggle link")}a(f.TOGGLE_REGION).each(function(b,c){var d=a(c).find(f.TOGGLE_ACTION),e=d.attr("aria-controls"),h=a(document.getElementById(e)),i="false"==d.attr("aria-expanded"),j=d.attr("data-side"),k=a(f.BODY),l=d.attr("data-preference");if(g){M.util.set_user_preference(l,"false")}h.on("mousewheel DOMMouseScroll",this.preventPageScroll);if(!i){k.addClass("drawer-open-"+j);d.attr("aria-expanded","true")}else{d.attr("aria-expanded","false")}}.bind(this));this.registerEventListeners();if(g){this.closeAll()}};h.prototype.closeAll=function(){a(f.TOGGLE_REGION).each(function(b,c){var d=a(c).find(f.TOGGLE_ACTION),h=d.attr("data-side"),i=a(f.BODY),j=d.attr("aria-controls"),k=a(document.getElementById(j)),l=d.attr("data-preference");d.attr("aria-expanded","false");i.removeClass("drawer-open-"+h);e.hide(k.get());k.addClass("closed");if(!g){M.util.set_user_preference(l,"false")}})};h.prototype.toggleDrawer=function(b){var c=a(b.target).closest("[data-action=toggle-drawer]"),h=c.attr("aria-controls"),i=a(document.getElementById(h)),j=a(f.BODY),k=c.attr("data-side"),l=c.attr("data-preference");if(g){M.util.set_user_preference(l,"false")}j.addClass("drawer-ease");var m="true"==c.attr("aria-expanded");if(!m){c.attr("aria-expanded","true");e.unhide(i.get());i.focus();j.addClass("drawer-open-"+k);i.removeClass("closed");if(!g){M.util.set_user_preference(l,"true")}}else{j.removeClass("drawer-open-"+k);c.attr("aria-expanded","false");i.addClass("closed").delay(500).queue(function(){if(a(this).hasClass("closed")){e.hide(this)}a(this).dequeue()});if(!g){M.util.set_user_preference(l,"false")}}d.publish("nav-drawer-toggle-start",m)};h.prototype.preventPageScroll=function(b){var c=b.wheelDelta||b.originalEvent&&b.originalEvent.wheelDelta||-b.originalEvent.detail,d=0<=this.scrollTop+a(this).outerHeight()-this.scrollHeight,e=0>=this.scrollTop;if(0>c&&d||0<c&&e){b.preventDefault()}};h.prototype.registerEventListeners=function(){a(f.TOGGLE_ACTION).each(function(c,d){b.define(a(d),[b.events.activate]);a(d).on(b.events.activate,function(a,b){this.toggleDrawer(b.originalEvent);b.originalEvent.preventDefault()}.bind(this))}.bind(this));a(f.SECTION).click(function(){if(g){this.closeAll()}}.bind(this));a(f.DRAWER).on("webkitTransitionEnd msTransitionEnd transitionend",function(b){var c=a(b.target).closest(f.DRAWER),e=!!c.attr("aria-hidden");d.publish("nav-drawer-toggle-end",e)})};return{init:function init(){return new h}}});
//# sourceMappingURL=drawer.min.js.map

1
amd/build/drawer.min.js.map

File diff suppressed because one or more lines are too long

2
amd/build/form-display-errors.min.js

@ -0,0 +1,2 @@
define ("theme_ilb/form-display-errors",["jquery","core/event"],function(a,b){return{enhance:function enhance(c){var d=document.getElementById(c);if(!d){return}a(d).on(b.Events.FORM_FIELD_VALIDATION,function(b,c){b.preventDefault();var e=a(d).closest(".form-group"),f=e.find(".form-control-feedback"),g=f.attr("id"),h=a(d).attr("aria-describedby");if("undefined"==typeof h){h=""}var i=[];if(h.length){i=h.split(" ")}var j=i.indexOf(g);if("TEXTAREA"==a(d).prop("tagName")&&e.find("[contenteditable]")){d=e.find("[contenteditable]")}if(""!==c){e.addClass("has-danger");e.data("client-validation-error",!0);a(d).addClass("is-invalid");if(-1===j){i.push(g);a(d).attr("aria-describedby",i.join(" "))}a(d).attr("aria-invalid",!0);f.attr("tabindex",0);f.html(c);if(!f.is(":visible")){f.show();f.focus()}}else{if(!0===e.data("client-validation-error")){e.removeClass("has-danger");e.data("client-validation-error",!1);a(d).removeClass("is-invalid");if(-1<j){i.splice(j,1)}if(i.length){h=i.join(" ");a(d).attr("aria-describedby",h)}else{a(d).removeAttr("aria-describedby")}a(d).attr("aria-invalid",!1);f.hide()}}});var e=d.closest("form");if(e&&!("ilbFormErrorsEnhanced"in e.dataset)){e.addEventListener("submit",function(){var b=a(".form-control-feedback:visible");if(b.length){b[0].focus()}});e.dataset.ilbFormErrorsEnhanced=1}}}});
//# sourceMappingURL=form-display-errors.min.js.map

1
amd/build/form-display-errors.min.js.map

File diff suppressed because one or more lines are too long

2
amd/build/index.min.js

@ -0,0 +1,2 @@
define ("theme_ilb/index",["exports","./bootstrap/alert","./bootstrap/button","./bootstrap/carousel","./bootstrap/collapse","./bootstrap/dropdown","./bootstrap/modal","./bootstrap/popover","./bootstrap/scrollspy","./bootstrap/tab","./bootstrap/toast","./bootstrap/tooltip","./bootstrap/util"],function(a,b,c,d,e,f,g,h,i,j,k,l,m){"use strict";Object.defineProperty(a,"__esModule",{value:!0});Object.defineProperty(a,"Alert",{enumerable:!0,get:function get(){return b.default}});Object.defineProperty(a,"Button",{enumerable:!0,get:function get(){return c.default}});Object.defineProperty(a,"Carousel",{enumerable:!0,get:function get(){return d.default}});Object.defineProperty(a,"Collapse",{enumerable:!0,get:function get(){return e.default}});Object.defineProperty(a,"Dropdown",{enumerable:!0,get:function get(){return f.default}});Object.defineProperty(a,"Modal",{enumerable:!0,get:function get(){return g.default}});Object.defineProperty(a,"Popover",{enumerable:!0,get:function get(){return h.default}});Object.defineProperty(a,"Scrollspy",{enumerable:!0,get:function get(){return i.default}});Object.defineProperty(a,"Tab",{enumerable:!0,get:function get(){return j.default}});Object.defineProperty(a,"Toast",{enumerable:!0,get:function get(){return k.default}});Object.defineProperty(a,"Tooltip",{enumerable:!0,get:function get(){return l.default}});Object.defineProperty(a,"Util",{enumerable:!0,get:function get(){return m.default}});b=n(b);c=n(c);d=n(d);e=n(e);f=n(f);g=n(g);h=n(h);i=n(i);j=n(j);k=n(k);l=n(l);m=n(m);function n(a){return a&&a.__esModule?a:{default:a}}});
//# sourceMappingURL=index.min.js.map

1
amd/build/index.min.js.map

@ -0,0 +1 @@
{"version":3,"sources":["../src/index.js"],"names":[],"mappings":"g6CAOA,OACA,OACA,OACA,OACA,OACA,OACA,OACA,OACA,OACA,OACA,OACA,O","sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap (v4.6.0): index.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport Alert from './bootstrap/alert';\nimport Button from './bootstrap/button';\nimport Carousel from './bootstrap/carousel';\nimport Collapse from './bootstrap/collapse';\nimport Dropdown from './bootstrap/dropdown';\nimport Modal from './bootstrap/modal';\nimport Popover from './bootstrap/popover';\nimport Scrollspy from './bootstrap/scrollspy';\nimport Tab from './bootstrap/tab';\nimport Toast from './bootstrap/toast';\nimport Tooltip from './bootstrap/tooltip';\nimport Util from './bootstrap/util';\n\nexport {\n Util,\n Alert,\n Button,\n Carousel,\n Collapse,\n Dropdown,\n Modal,\n Popover,\n Scrollspy,\n Tab,\n Toast,\n Tooltip\n};\n"],"file":"index.min.js"}

2
amd/build/loader.min.js

@ -0,0 +1,2 @@
function _typeof(a){"@babel/helpers - typeof";if("function"==typeof Symbol&&"symbol"==typeof Symbol.iterator){_typeof=function(a){return typeof a}}else{_typeof=function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a}}return _typeof(a)}define ("theme_ilb/loader",["exports","jquery","./aria","./index","core/pending","./scroll","./pending"],function(a,b,c,d,e,f,g){"use strict";Object.defineProperty(a,"__esModule",{value:!0});Object.defineProperty(a,"Bootstrap",{enumerable:!0,get:function get(){return d.default}});b=j(b);c=i(c);d=j(d);e=j(e);f=j(f);g=j(g);function h(){if("function"!=typeof WeakMap)return null;var a=new WeakMap;h=function(){return a};return a}function i(a){if(a&&a.__esModule){return a}if(null===a||"object"!==_typeof(a)&&"function"!=typeof a){return{default:a}}var b=h();if(b&&b.has(a)){return b.get(a)}var c={},d=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var e in a){if(Object.prototype.hasOwnProperty.call(a,e)){var f=d?Object.getOwnPropertyDescriptor(a,e):null;if(f&&(f.get||f.set)){Object.defineProperty(c,e,f)}else{c[e]=a[e]}}}c.default=a;if(b){b.set(a,c)}return c}function j(a){return a&&a.__esModule?a:{default:a}}var k=function(){(0,b.default)("a[data-toggle=\"tab\"]").on("shown.bs.tab",function(a){var c=(0,b.default)(a.target).attr("href");if(history.replaceState){history.replaceState(null,null,c)}else{location.hash=c}});var a=window.location.hash;if(a){var c=document.querySelector(".nav-link[href=\""+a+"\"]");if(c){c.click()}}},l=function(){(0,b.default)("body").popover({container:"body",selector:"[data-toggle=\"popover\"]",trigger:"focus"});document.addEventListener("keydown",function(a){if("Escape"===a.key&&a.target.closest("[data-toggle=\"popover\"]")){(0,b.default)(a.target).popover("hide")}})},m=function(){(0,b.default)("body").tooltip({container:"body",selector:"[data-toggle=\"tooltip\"]"})},n=new e.default("theme_ilb/loader:init");(0,g.default)();c.init();k();l();m();new f.default().init();b.default.fn.dropdown.Constructor.Default.flip=!1;n.resolve()});
//# sourceMappingURL=loader.min.js.map

1
amd/build/loader.min.js.map

@ -0,0 +1 @@
{"version":3,"sources":["../src/loader.js"],"names":["rememberTabs","on","e","hash","target","attr","history","replaceState","location","window","tab","document","querySelector","click","enablePopovers","popover","container","selector","trigger","addEventListener","key","closest","enableTooltips","tooltip","pendingPromise","Pending","Aria","init","Scroll","$","fn","dropdown","Constructor","Default","flip","resolve"],"mappings":"8jBAwBA,OACA,OACA,OACA,OACA,OACA,O,4lBAKMA,CAAAA,CAAY,CAAG,UAAM,CACvB,cAAE,wBAAF,EAA0BC,EAA1B,CAA6B,cAA7B,CAA6C,SAASC,CAAT,CAAY,CACrD,GAAIC,CAAAA,CAAI,CAAG,cAAED,CAAC,CAACE,MAAJ,EAAYC,IAAZ,CAAiB,MAAjB,CAAX,CACA,GAAIC,OAAO,CAACC,YAAZ,CAA0B,CACtBD,OAAO,CAACC,YAAR,CAAqB,IAArB,CAA2B,IAA3B,CAAiCJ,CAAjC,CACH,CAFD,IAEO,CACHK,QAAQ,CAACL,IAAT,CAAgBA,CACnB,CACJ,CAPD,EAQA,GAAMA,CAAAA,CAAI,CAAGM,MAAM,CAACD,QAAP,CAAgBL,IAA7B,CACA,GAAIA,CAAJ,CAAU,CACN,GAAMO,CAAAA,CAAG,CAAGC,QAAQ,CAACC,aAAT,CAAuB,oBAAqBT,CAArB,CAA4B,KAAnD,CAAZ,CACA,GAAIO,CAAJ,CAAS,CACLA,CAAG,CAACG,KAAJ,EACH,CACJ,CACJ,C,CAMKC,CAAc,CAAG,UAAM,CACzB,cAAE,MAAF,EAAUC,OAAV,CAAkB,CACdC,SAAS,CAAE,MADG,CAEdC,QAAQ,CAAE,2BAFI,CAGdC,OAAO,CAAE,OAHK,CAAlB,EAMAP,QAAQ,CAACQ,gBAAT,CAA0B,SAA1B,CAAqC,SAAAjB,CAAC,CAAI,CACtC,GAAc,QAAV,GAAAA,CAAC,CAACkB,GAAF,EAAsBlB,CAAC,CAACE,MAAF,CAASiB,OAAT,CAAiB,2BAAjB,CAA1B,CAAuE,CACnE,cAAEnB,CAAC,CAACE,MAAJ,EAAYW,OAAZ,CAAoB,MAApB,CACH,CACJ,CAJD,CAKH,C,CAMKO,CAAc,CAAG,UAAM,CACzB,cAAE,MAAF,EAAUC,OAAV,CAAkB,CACdP,SAAS,CAAE,MADG,CAEdC,QAAQ,CAAE,2BAFI,CAAlB,CAIH,C,CAEKO,CAAc,CAAG,GAAIC,UAAJ,CAAY,yBAAZ,C,CAGvB,gBAGAC,CAAI,CAACC,IAAL,GAGA3B,CAAY,GAGZc,CAAc,GAGdQ,CAAc,GAGb,GAAIM,UAAJ,EAAD,CAAeD,IAAf,GAGAE,UAAEC,EAAF,CAAKC,QAAL,CAAcC,WAAd,CAA0BC,OAA1B,CAAkCC,IAAlC,IAEAV,CAAc,CAACW,OAAf,E","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Template renderer for Moodle. Load and render Moodle templates with Mustache.\n *\n * @module theme_ilb/loader\n * @copyright 2015 Damyon Wiese <damyon@moodle.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 2.9\n */\n\nimport $ from 'jquery';\nimport * as Aria from './aria';\nimport Bootstrap from './index';\nimport Pending from 'core/pending';\nimport Scroll from './scroll';\nimport setupBootstrapPendingChecks from './pending';\n\n/**\n * Rember the last visited tabs.\n */\nconst rememberTabs = () => {\n $('a[data-toggle=\"tab\"]').on('shown.bs.tab', function(e) {\n var hash = $(e.target).attr('href');\n if (history.replaceState) {\n history.replaceState(null, null, hash);\n } else {\n location.hash = hash;\n }\n });\n const hash = window.location.hash;\n if (hash) {\n const tab = document.querySelector('.nav-link[href=\"' + hash + '\"]');\n if (tab) {\n tab.click();\n }\n }\n};\n\n/**\n * Enable all popovers\n *\n */\nconst enablePopovers = () => {\n $('body').popover({\n container: 'body',\n selector: '[data-toggle=\"popover\"]',\n trigger: 'focus',\n });\n\n document.addEventListener('keydown', e => {\n if (e.key === 'Escape' && e.target.closest('[data-toggle=\"popover\"]')) {\n $(e.target).popover('hide');\n }\n });\n};\n\n/**\n * Enable tooltips\n *\n */\nconst enableTooltips = () => {\n $('body').tooltip({\n container: 'body',\n selector: '[data-toggle=\"tooltip\"]',\n });\n};\n\nconst pendingPromise = new Pending('theme_ilb/loader:init');\n\n// Add pending promise event listeners to relevant Bootstrap custom events.\nsetupBootstrapPendingChecks();\n\n// Setup Aria helpers for Bootstrap features.\nAria.init();\n\n// Remember the last visited tabs.\nrememberTabs();\n\n// Enable all popovers.\nenablePopovers();\n\n// Enable all tooltips.\nenableTooltips();\n\n// Add scroll handling.\n(new Scroll()).init();\n\n// Disables flipping the dropdowns up and getting hidden behind the navbar.\n$.fn.dropdown.Constructor.Default.flip = false;\n\npendingPromise.resolve();\n\nexport {\n Bootstrap,\n};\n"],"file":"loader.min.js"}

2
amd/build/pending.min.js

@ -0,0 +1,2 @@
define ("theme_ilb/pending",["exports","jquery"],function(a,b){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;b=function(a){return a&&a.__esModule?a:{default:a}}(b);function c(a,b){return h(a)||g(a,b)||e(a,b)||d()}function d(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function e(a,b){if(!a)return;if("string"==typeof a)return f(a,b);var c=Object.prototype.toString.call(a).slice(8,-1);if("Object"===c&&a.constructor)c=a.constructor.name;if("Map"===c||"Set"===c)return Array.from(c);if("Arguments"===c||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(c))return f(a,b)}function f(a,b){if(null==b||b>a.length)b=a.length;for(var c=0,d=Array(b);c<b;c++){d[c]=a[c]}return d}function g(a,b){if("undefined"==typeof Symbol||!(Symbol.iterator in Object(a)))return;var c=[],d=!0,e=!1,f=void 0;try{for(var g=a[Symbol.iterator](),h;!(d=(h=g.next()).done);d=!0){c.push(h.value);if(b&&c.length===b)break}}catch(a){e=!0;f=a}finally{try{if(!d&&null!=g["return"])g["return"]()}finally{if(e)throw f}}return c}function h(a){if(Array.isArray(a))return a}var i={alert:[{start:"close",end:"closed"}],carousel:[{start:"slide",end:"slid"}],collapse:[{start:"hide",end:"hidden"},{start:"show",end:"shown"}],dropdown:[{start:"hide",end:"hidden"},{start:"show",end:"shown"}],modal:[{start:"hide",end:"hidden"},{start:"show",end:"shown"}],popover:[{start:"hide",end:"hidden"},{start:"show",end:"shown"}],tab:[{start:"hide",end:"hidden"},{start:"show",end:"shown"}],toast:[{start:"hide",end:"hidden"},{start:"show",end:"shown"}],tooltip:[{start:"hide",end:"hidden"},{start:"show",end:"shown"}]};a.default=function(){Object.entries(i).forEach(function(a){var d=c(a,2),e=d[0],f=d[1];f.forEach(function(a){var c="".concat(a.start,".bs.").concat(e),d="".concat(a.end,".bs.").concat(e);(0,b.default)(document.body).on(c,function(a){M.util.js_pending(d);(0,b.default)(a.target).one(d,function(){M.util.js_complete(d)})})})})};return a.default});
//# sourceMappingURL=pending.min.js.map

1
amd/build/pending.min.js.map

File diff suppressed because one or more lines are too long

2
amd/build/popover.min.js

@ -0,0 +1,2 @@
define ("theme_ilb/popover",["exports","./bootstrap/popover"],function(a,b){"use strict";Object.defineProperty(a,"__esModule",{value:!0});Object.defineProperty(a,"Popover",{enumerable:!0,get:function get(){return b.default}});b=function(a){return a&&a.__esModule?a:{default:a}}(b)});
//# sourceMappingURL=popover.min.js.map

1
amd/build/popover.min.js.map

@ -0,0 +1 @@
{"version":3,"sources":["../src/popover.js"],"names":[],"mappings":"oOAuBA,sD","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Backward compatibility file for the old popover.js\n *\n * @module theme_ilb/popover\n * @copyright 2020 Bas Brands <bas@moodle.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Popover from './bootstrap/popover';\n\nexport {\n Popover\n};\n"],"file":"popover.min.js"}

2
amd/build/scroll.min.js

@ -0,0 +1,2 @@
define ("theme_ilb/scroll",["exports"],function(a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;function b(a,b){if(!(a instanceof b)){throw new TypeError("Cannot call a class as a function")}}function c(a,b){for(var c=0,d;c<b.length;c++){d=b[c];d.enumerable=d.enumerable||!1;d.configurable=!0;if("value"in d)d.writable=!0;Object.defineProperty(a,d.key,d)}}function d(a,b,d){if(b)c(a.prototype,b);if(d)c(a,d);return a}var e=function(){function a(){b(this,a)}d(a,[{key:"init",value:function init(){this.scrollY=0;window.addEventListener("scroll",this.scrollHandler.bind(this));return this}},{key:"getScrollPosition",value:function getScrollPosition(){return window.pageYOffset||document.documentElement.scrollTop}},{key:"scrollHandler",value:function scrollHandler(){var a=document.querySelector("body"),b=this.getScrollPosition();if(b>=window.innerHeight){a.classList.add("scrolled")}else{a.classList.remove("scrolled")}}}]);return a}();a.default=e;return a.default});
//# sourceMappingURL=scroll.min.js.map

1
amd/build/scroll.min.js.map

@ -0,0 +1 @@
{"version":3,"sources":["../src/scroll.js"],"names":["MoodleScroll","scrollY","window","addEventListener","scrollHandler","bind","pageYOffset","document","documentElement","scrollTop","body","querySelector","getScrollPosition","innerHeight","classList","add","remove"],"mappings":"wcA8BqBA,CAAAA,C,yEASV,CACH,KAAKC,OAAL,CAAe,CAAf,CACAC,MAAM,CAACC,gBAAP,CAAwB,QAAxB,CAAkC,KAAKC,aAAL,CAAmBC,IAAnB,CAAwB,IAAxB,CAAlC,EACA,MAAO,KACV,C,6DASmB,CAChB,MAAOH,CAAAA,MAAM,CAACI,WAAP,EAAsBC,QAAQ,CAACC,eAAT,CAAyBC,SACzD,C,qDAQe,IACNC,CAAAA,CAAI,CAAGH,QAAQ,CAACI,aAAT,CAAuB,MAAvB,CADD,CAENV,CAAO,CAAG,KAAKW,iBAAL,EAFJ,CAGZ,GAAIX,CAAO,EAAIC,MAAM,CAACW,WAAtB,CAAmC,CAC/BH,CAAI,CAACI,SAAL,CAAeC,GAAf,CAAmB,UAAnB,CACH,CAFD,IAEO,CACHL,CAAI,CAACI,SAAL,CAAeE,MAAf,CAAsB,UAAtB,CACH,CACJ,C","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Manage user scroll in Moodle for future floating elements.\n *\n * @module theme_ilb/scroll\n * @copyright 2020 Ferran Recio <ferran@moodle.org>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/**\n * Moodle scroll handling. For now it just handle a \"scrolled\" class\n * on the body tag but in the near future could handle more floating\n * elements like option bars, docked elements or other active elements.\n *\n * @class theme_ilb/scroll\n */\nexport default class MoodleScroll {\n\n /**\n * Initialise the scroll monitoring.\n *\n * @method init\n * @chainable\n * @return {Object} this.\n */\n init() {\n this.scrollY = 0;\n window.addEventListener(\"scroll\", this.scrollHandler.bind(this));\n return this;\n }\n\n /**\n * Add special classes to body depending on scroll position.\n *\n * @method update\n * @chainable\n * @return {Integer} current scroll position.\n */\n getScrollPosition() {\n return window.pageYOffset || document.documentElement.scrollTop;\n }\n\n /**\n * Add special classes to body depending on scroll position.\n *\n * @method update\n * @chainable\n */\n scrollHandler() {\n const body = document.querySelector('body');\n const scrollY = this.getScrollPosition();\n if (scrollY >= window.innerHeight) {\n body.classList.add('scrolled');\n } else {\n body.classList.remove('scrolled');\n }\n }\n}\n"],"file":"scroll.min.js"}

2
amd/build/toast.min.js

@ -0,0 +1,2 @@
define ("theme_ilb/toast",["exports","./bootstrap/toast"],function(a,b){"use strict";Object.defineProperty(a,"__esModule",{value:!0});Object.defineProperty(a,"Toast",{enumerable:!0,get:function get(){return b.default}});b=function(a){return a&&a.__esModule?a:{default:a}}(b)});
//# sourceMappingURL=toast.min.js.map

1
amd/build/toast.min.js.map

@ -0,0 +1 @@
{"version":3,"sources":["../src/toast.js"],"names":[],"mappings":"8NAuBA,sD","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Backward compatibility file for the old toast.js\n *\n * @module theme_ilb/toast\n * @copyright 2020 Bas Brands <bas@moodle.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Toast from './bootstrap/toast';\n\nexport {\n Toast\n};\n"],"file":"toast.min.js"}

282
amd/src/aria.js

@ -0,0 +1,282 @@
// 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/>.
/**
* Enhancements to Bootstrap components for accessibility.
*
* @module theme_ilb/aria
* @copyright 2018 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import $ from 'jquery';
import Pending from 'core/pending';
/**
* Drop downs from bootstrap don't support keyboard accessibility by default.
*/
const dropdownFix = () => {
let focusEnd = false;
const setFocusEnd = () => {
focusEnd = true;
};
const getFocusEnd = () => {
const result = focusEnd;
focusEnd = false;
return result;
};
// Special handling for "up" keyboard control.
document.addEventListener('keydown', e => {
if (e.target.matches('[data-toggle="dropdown"]')) {
const trigger = e.key;
// Up key opens the menu at the end.
if (trigger == 'ArrowUp') {
// Focus the end of the menu, not the beginning.
setFocusEnd();
}
// Space key or Enter key opens the menu.
if (trigger == ' ' || trigger == 'Enter') {
// Cancel random scroll.
e.preventDefault();
// Open the menu instead.
e.target.click();
}
}
});
// Special handling for navigation keys when menu is open.
const shiftFocus = element => {
const delayedFocus = pendingPromise => {
element.focus();
pendingPromise.resolve();
};
setTimeout(delayedFocus, 50, new Pending('core/aria:delayed-focus'));
};
$('.dropdown').on('shown.bs.dropdown', e => {
// We need to focus on the first menuitem.
const menu = e.target.querySelector('[role="menu"]');
let menuItems = false;
let foundMenuItem = false;
if (menu) {
menuItems = menu.querySelectorAll('[role="menuitem"]');
}
if (menuItems && menuItems.length > 0) {
if (getFocusEnd()) {
foundMenuItem = menuItems[menuItems.length - 1];
} else {
// The first menu entry, pretty reasonable.
foundMenuItem = menuItems[0];
}
}
if (foundMenuItem) {
shiftFocus(foundMenuItem);
}
});
// Search for menu items by finding the first item that has
// text starting with the typed character (case insensitive).
document.addEventListener('keypress', e => {
if (e.target.matches('.dropdown [role="menu"] [role="menuitem"]')) {
const menu = e.target.closest('[role="menu"]');
if (!menu) {
return;
}
const menuItems = menu.querySelectorAll('[role="menuitem"]');
if (!menuItems) {
return;
}
const trigger = e.key.toLowerCase();
for (let i = 0; i < menuItems.length; i++) {
const item = menuItems[i];
const itemText = item.text.trim().toLowerCase();
if (itemText.indexOf(trigger) == 0) {
shiftFocus(item);
break;
}
}
}
});
// Keyboard navigation for arrow keys, home and end keys.
document.addEventListener('keydown', e => {
if (e.target.matches('.dropdown [role="menu"] [role="menuitem"]')) {
const trigger = e.key;
let next = false;
const menu = e.target.closest('[role="menu"]');
if (!menu) {
return;
}
const menuItems = menu.querySelectorAll('[role="menuitem"]');
if (!menuItems) {
return;
}
// Down key.
if (trigger == 'ArrowDown') {
for (let i = 0; i < menuItems.length - 1; i++) {
if (menuItems[i] == e.target) {
next = menuItems[i + 1];
break;
}
}
if (!next) {
// Wrap to first item.
next = menuItems[0];
}
} else if (trigger == 'ArrowUp') {
// Up key.
for (let i = 1; i < menuItems.length; i++) {
if (menuItems[i] == e.target) {
next = menuItems[i - 1];
break;
}
}
if (!next) {
// Wrap to last item.
next = menuItems[menuItems.length - 1];
}
} else if (trigger == 'Home') {
// Home key.
next = menuItems[0];
} else if (trigger == 'End') {
// End key.
next = menuItems[menuItems.length - 1];
}
// Variable next is set if we do want to act on the keypress.
if (next) {
e.preventDefault();
shiftFocus(next);
}
return;
}
});
$('.dropdown').on('hidden.bs.dropdown', e => {
// We need to focus on the menu trigger.
const trigger = e.target.querySelector('[data-toggle="dropdown"]');
if (trigger) {
shiftFocus(trigger);
}
});
};
/**
* After page load, focus on any element with special autofocus attribute.
*/
const autoFocus = () => {
window.addEventListener("load", () => {
const alerts = document.querySelectorAll('[data-aria-autofocus="true"][role="alert"]');
Array.prototype.forEach.call(alerts, autofocusElement => {
// According to the specification an role="alert" region is only read out on change to the content
// of that region.
autofocusElement.innerHTML += ' ';
autofocusElement.removeAttribute('data-aria-autofocus');
});
});
};
/**
* Changes the focus to the correct tab based on the key that is pressed.
* @param {KeyboardEvent} e
*/
const updateTabFocus = e => {
const tabList = e.target.closest('[role="tablist"]');
const vertical = tabList.getAttribute('aria-orientation') == 'vertical';
const rtl = window.right_to_left();
const arrowNext = vertical ? 'ArrowDown' : (rtl ? 'ArrowLeft' : 'ArrowRight');
const arrowPrevious = vertical ? 'ArrowUp' : (rtl ? 'ArrowRight' : 'ArrowLeft');
const tabs = Array.prototype.filter.call(
tabList.querySelectorAll('[role="tab"]'),
tab => getComputedStyle(tab).display !== 'none'); // We only work with the visible tabs.
for (let i = 0; i < tabs.length; i++) {
tabs[i].index = i;
}
switch (e.key) {
case arrowNext:
e.preventDefault();
if (e.target.index !== undefined && tabs[e.target.index + 1]) {
tabs[e.target.index + 1].focus();
} else {
tabs[0].focus();
}
break;
case arrowPrevious:
e.preventDefault();
if (e.target.index !== undefined && tabs[e.target.index - 1]) {
tabs[e.target.index - 1].focus();
} else {
tabs[tabs.length - 1].focus();
}
break;
case 'Home':
e.preventDefault();
tabs[0].focus();
break;
case 'End':
e.preventDefault();
tabs[tabs.length - 1].focus();
break;
case 'Enter':
case ' ':
e.preventDefault();
$(e.target).tab('show');
tabs.forEach(tab => {
tab.tabIndex = -1;
});
e.target.tabIndex = 0;
}
};
/**
* Fix accessibility issues regarding tab elements focus and their tab order in Bootstrap navs.
*/
const tabElementFix = () => {
document.addEventListener('keydown', e => {
if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'Home', 'End', 'Enter', ' '].includes(e.key)) {
if (e.target.matches('[role="tablist"] [role="tab"]')) {
updateTabFocus(e);
}
}
});
document.addEventListener('click', e => {
if (e.target.matches('[role="tablist"] [role="tab"]')) {
const tabs = e.target.closest('[role="tablist"]').querySelectorAll('[role="tab"]');
e.preventDefault();
$(e.target).tab('show');
tabs.forEach(tab => {
tab.tabIndex = -1;
});
e.target.tabIndex = 0;
}
});
};
export const init = () => {
dropdownFix();
autoFocus();
tabElementFix();
};

173
amd/src/bootstrap/alert.js

@ -0,0 +1,173 @@
/**
* --------------------------------------------------------------------------
* Bootstrap (v4.6.0): alert.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
import $ from 'jquery'
import Util from './util'
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
const NAME = 'alert'
const VERSION = '4.6.0'
const DATA_KEY = 'bs.alert'
const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api'
const JQUERY_NO_CONFLICT = $.fn[NAME]
const SELECTOR_DISMISS = '[data-dismiss="alert"]'
const EVENT_CLOSE = `close${EVENT_KEY}`
const EVENT_CLOSED = `closed${EVENT_KEY}`
const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`
const CLASS_NAME_ALERT = 'alert'
const CLASS_NAME_FADE = 'fade'
const CLASS_NAME_SHOW = 'show'
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
class Alert {
constructor(element) {
this._element = element
}
// Getters
static get VERSION() {
return VERSION
}
// Public
close(element) {
let rootElement = this._element
if (element) {
rootElement = this._getRootElement(element)
}
const customEvent = this._triggerCloseEvent(rootElement)
if (customEvent.isDefaultPrevented()) {
return
}
this._removeElement(rootElement)
}
dispose() {
$.removeData(this._element, DATA_KEY)
this._element = null
}
// Private
_getRootElement(element) {
const selector = Util.getSelectorFromElement(element)
let parent = false
if (selector) {
parent = document.querySelector(selector)
}
if (!parent) {
parent = $(element).closest(`.${CLASS_NAME_ALERT}`)[0]
}
return parent
}
_triggerCloseEvent(element) {
const closeEvent = $.Event(EVENT_CLOSE)
$(element).trigger(closeEvent)
return closeEvent
}
_removeElement(element) {
$(element).removeClass(CLASS_NAME_SHOW)
if (!$(element).hasClass(CLASS_NAME_FADE)) {
this._destroyElement(element)
return
}
const transitionDuration = Util.getTransitionDurationFromElement(element)
$(element)
.one(Util.TRANSITION_END, event => this._destroyElement(element, event))
.emulateTransitionEnd(transitionDuration)
}
_destroyElement(element) {
$(element)
.detach()
.trigger(EVENT_CLOSED)
.remove()
}
// Static
static _jQueryInterface(config) {
return this.each(function () {
const $element = $(this)
let data = $element.data(DATA_KEY)
if (!data) {
data = new Alert(this)
$element.data(DATA_KEY, data)
}
if (config === 'close') {
data[config](this)
}
})
}
static _handleDismiss(alertInstance) {
return function (event) {
if (event) {
event.preventDefault()
}
alertInstance.close(this)
}
}
}
/**
* ------------------------------------------------------------------------
* Data Api implementation
* ------------------------------------------------------------------------
*/
$(document).on(
EVENT_CLICK_DATA_API,
SELECTOR_DISMISS,
Alert._handleDismiss(new Alert())
)
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = Alert._jQueryInterface
$.fn[NAME].Constructor = Alert
$.fn[NAME].noConflict = () => {
$.fn[NAME] = JQUERY_NO_CONFLICT
return Alert._jQueryInterface
}
export default Alert

209
amd/src/bootstrap/button.js

@ -0,0 +1,209 @@
/**
* --------------------------------------------------------------------------
* Bootstrap (v4.6.0): button.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
import $ from 'jquery'
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
const NAME = 'button'
const VERSION = '4.6.0'
const DATA_KEY = 'bs.button'
const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api'
const JQUERY_NO_CONFLICT = $.fn[NAME]
const CLASS_NAME_ACTIVE = 'active'
const CLASS_NAME_BUTTON = 'btn'
const CLASS_NAME_FOCUS = 'focus'
const SELECTOR_DATA_TOGGLE_CARROT = '[data-toggle^="button"]'
const SELECTOR_DATA_TOGGLES = '[data-toggle="buttons"]'
const SELECTOR_DATA_TOGGLE = '[data-toggle="button"]'
const SELECTOR_DATA_TOGGLES_BUTTONS = '[data-toggle="buttons"] .btn'
const SELECTOR_INPUT = 'input:not([type="hidden"])'
const SELECTOR_ACTIVE = '.active'
const SELECTOR_BUTTON = '.btn'
const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`
const EVENT_FOCUS_BLUR_DATA_API = `focus${EVENT_KEY}${DATA_API_KEY} ` +
`blur${EVENT_KEY}${DATA_API_KEY}`
const EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
class Button {
constructor(element) {
this._element = element
this.shouldAvoidTriggerChange = false
}
// Getters
static get VERSION() {
return VERSION
}
// Public
toggle() {
let triggerChangeEvent = true
let addAriaPressed = true
const rootElement = $(this._element).closest(SELECTOR_DATA_TOGGLES)[0]
if (rootElement) {
const input = this._element.querySelector(SELECTOR_INPUT)
if (input) {
if (input.type === 'radio') {
if (input.checked && this._element.classList.contains(CLASS_NAME_ACTIVE)) {
triggerChangeEvent = false
} else {
const activeElement = rootElement.querySelector(SELECTOR_ACTIVE)
if (activeElement) {
$(activeElement).removeClass(CLASS_NAME_ACTIVE)
}
}
}
if (triggerChangeEvent) {
// if it's not a radio button or checkbox don't add a pointless/invalid checked property to the input
if (input.type === 'checkbox' || input.type === 'radio') {
input.checked = !this._element.classList.contains(CLASS_NAME_ACTIVE)
}
if (!this.shouldAvoidTriggerChange) {
$(input).trigger('change')
}
}
input.focus()
addAriaPressed = false
}
}
if (!(this._element.hasAttribute('disabled') || this._element.classList.contains('disabled'))) {
if (addAriaPressed) {
this._element.setAttribute('aria-pressed', !this._element.classList.contains(CLASS_NAME_ACTIVE))
}
if (triggerChangeEvent) {
$(this._element).toggleClass(CLASS_NAME_ACTIVE)
}
}
}
dispose() {
$.removeData(this._element, DATA_KEY)
this._element = null
}
// Static
static _jQueryInterface(config, avoidTriggerChange) {
return this.each(function () {
const $element = $(this)
let data = $element.data(DATA_KEY)
if (!data) {
data = new Button(this)
$element.data(DATA_KEY, data)
}
data.shouldAvoidTriggerChange = avoidTriggerChange
if (config === 'toggle') {
data[config]()
}
})
}
}
/**
* ------------------------------------------------------------------------
* Data Api implementation
* ------------------------------------------------------------------------
*/
$(document)
.on(EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE_CARROT, event => {
let button = event.target
const initialButton = button
if (!$(button).hasClass(CLASS_NAME_BUTTON)) {
button = $(button).closest(SELECTOR_BUTTON)[0]
}
if (!button || button.hasAttribute('disabled') || button.classList.contains('disabled')) {
event.preventDefault() // work around Firefox bug #1540995
} else {
const inputBtn = button.querySelector(SELECTOR_INPUT)
if (inputBtn && (inputBtn.hasAttribute('disabled') || inputBtn.classList.contains('disabled'))) {
event.preventDefault() // work around Firefox bug #1540995
return
}
if (initialButton.tagName === 'INPUT' || button.tagName !== 'LABEL') {
Button._jQueryInterface.call($(button), 'toggle', initialButton.tagName === 'INPUT')
}
}
})
.on(EVENT_FOCUS_BLUR_DATA_API, SELECTOR_DATA_TOGGLE_CARROT, event => {
const button = $(event.target).closest(SELECTOR_BUTTON)[0]
$(button).toggleClass(CLASS_NAME_FOCUS, /^focus(in)?$/.test(event.type))
})
$(window).on(EVENT_LOAD_DATA_API, () => {
// ensure correct active class is set to match the controls' actual values/states
// find all checkboxes/readio buttons inside data-toggle groups
let buttons = [].slice.call(document.querySelectorAll(SELECTOR_DATA_TOGGLES_BUTTONS))
for (let i = 0, len = buttons.length; i < len; i++) {
const button = buttons[i]
const input = button.querySelector(SELECTOR_INPUT)
if (input.checked || input.hasAttribute('checked')) {
button.classList.add(CLASS_NAME_ACTIVE)
} else {
button.classList.remove(CLASS_NAME_ACTIVE)
}
}
// find all button toggles
buttons = [].slice.call(document.querySelectorAll(SELECTOR_DATA_TOGGLE))
for (let i = 0, len = buttons.length; i < len; i++) {
const button = buttons[i]
if (button.getAttribute('aria-pressed') === 'true') {
button.classList.add(CLASS_NAME_ACTIVE)
} else {
button.classList.remove(CLASS_NAME_ACTIVE)
}
}
})
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = Button._jQueryInterface
$.fn[NAME].Constructor = Button
$.fn[NAME].noConflict = () => {
$.fn[NAME] = JQUERY_NO_CONFLICT
return Button._jQueryInterface
}
export default Button

613
amd/src/bootstrap/carousel.js

@ -0,0 +1,613 @@
/**
* --------------------------------------------------------------------------
* Bootstrap (v4.6.0): carousel.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
import $ from 'jquery'
import Util from './util'
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
const NAME = 'carousel'
const VERSION = '4.6.0'
const DATA_KEY = 'bs.carousel'
const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api'
const JQUERY_NO_CONFLICT = $.fn[NAME]
const ARROW_LEFT_KEYCODE = 37 // KeyboardEvent.which value for left arrow key
const ARROW_RIGHT_KEYCODE = 39 // KeyboardEvent.which value for right arrow key
const TOUCHEVENT_COMPAT_WAIT = 500 // Time for mouse compat events to fire after touch
const SWIPE_THRESHOLD = 40
const Default = {
interval: 5000,
keyboard: true,
slide: false,
pause: 'hover',
wrap: true,
touch: true
}
const DefaultType = {
interval: '(number|boolean)',
keyboard: 'boolean',
slide: '(boolean|string)',
pause: '(string|boolean)',
wrap: 'boolean',
touch: 'boolean'
}
const DIRECTION_NEXT = 'next'
const DIRECTION_PREV = 'prev'
const DIRECTION_LEFT = 'left'
const DIRECTION_RIGHT = 'right'
const EVENT_SLIDE = `slide${EVENT_KEY}`
const EVENT_SLID = `slid${EVENT_KEY}`
const EVENT_KEYDOWN = `keydown${EVENT_KEY}`
const EVENT_MOUSEENTER = `mouseenter${EVENT_KEY}`
const EVENT_MOUSELEAVE = `mouseleave${EVENT_KEY}`
const EVENT_TOUCHSTART = `touchstart${EVENT_KEY}`
const EVENT_TOUCHMOVE = `touchmove${EVENT_KEY}`
const EVENT_TOUCHEND = `touchend${EVENT_KEY}`
const EVENT_POINTERDOWN = `pointerdown${EVENT_KEY}`
const EVENT_POINTERUP = `pointerup${EVENT_KEY}`
const EVENT_DRAG_START = `dragstart${EVENT_KEY}`
const EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`
const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`
const CLASS_NAME_CAROUSEL = 'carousel'
const CLASS_NAME_ACTIVE = 'active'
const CLASS_NAME_SLIDE = 'slide'
const CLASS_NAME_RIGHT = 'carousel-item-right'
const CLASS_NAME_LEFT = 'carousel-item-left'
const CLASS_NAME_NEXT = 'carousel-item-next'
const CLASS_NAME_PREV = 'carousel-item-prev'
const CLASS_NAME_POINTER_EVENT = 'pointer-event'
const SELECTOR_ACTIVE = '.active'
const SELECTOR_ACTIVE_ITEM = '.active.carousel-item'
const SELECTOR_ITEM = '.carousel-item'
const SELECTOR_ITEM_IMG = '.carousel-item img'
const SELECTOR_NEXT_PREV = '.carousel-item-next, .carousel-item-prev'
const SELECTOR_INDICATORS = '.carousel-indicators'
const SELECTOR_DATA_SLIDE = '[data-slide], [data-slide-to]'
const SELECTOR_DATA_RIDE = '[data-ride="carousel"]'
const PointerType = {
TOUCH: 'touch',
PEN: 'pen'
}
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
class Carousel {
constructor(element, config) {
this._items = null
this._interval = null
this._activeElement = null
this._isPaused = false
this._isSliding = false
this.touchTimeout = null
this.touchStartX = 0
this.touchDeltaX = 0
this._config = this._getConfig(config)
this._element = element
this._indicatorsElement = this._element.querySelector(SELECTOR_INDICATORS)
this._touchSupported = 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0
this._pointerEvent = Boolean(window.PointerEvent || window.MSPointerEvent)
this._addEventListeners()
}
// Getters
static get VERSION() {
return VERSION
}
static get Default() {
return Default
}
// Public
next() {
if (!this._isSliding) {
this._slide(DIRECTION_NEXT)
}
}
nextWhenVisible() {
const $element = $(this._element)
// Don't call next when the page isn't visible
// or the carousel or its parent isn't visible
if (!document.hidden &&
($element.is(':visible') && $element.css('visibility') !== 'hidden')) {
this.next()
}
}
prev() {
if (!this._isSliding) {
this._slide(DIRECTION_PREV)
}
}
pause(event) {
if (!event) {
this._isPaused = true
}
if (this._element.querySelector(SELECTOR_NEXT_PREV)) {
Util.triggerTransitionEnd(this._element)
this.cycle(true)
}
clearInterval(this._interval)
this._interval = null
}
cycle(event) {
if (!event) {
this._isPaused = false
}
if (this._interval) {
clearInterval(this._interval)
this._interval = null
}
if (this._config.interval && !this._isPaused) {
this._updateInterval()
this._interval = setInterval(
(document.visibilityState ? this.nextWhenVisible : this.next).bind(this),
this._config.interval
)
}
}
to(index) {
this._activeElement = this._element.querySelector(SELECTOR_ACTIVE_ITEM)
const activeIndex = this._getItemIndex(this._activeElement)
if (index > this._items.length - 1 || index < 0) {
return
}
if (this._isSliding) {
$(this._element).one(EVENT_SLID, () => this.to(index))
return
}
if (activeIndex === index) {
this.pause()
this.cycle()
return
}
const direction = index > activeIndex ?
DIRECTION_NEXT :
DIRECTION_PREV
this._slide(direction, this._items[index])
}
dispose() {
$(this._element).off(EVENT_KEY)
$.removeData(this._element, DATA_KEY)
this._items = null
this._config = null
this._element = null
this._interval = null
this._isPaused = null
this._isSliding = null
this._activeElement = null
this._indicatorsElement = null
}
// Private
_getConfig(config) {
config = {
...Default,
...config
}
Util.typeCheckConfig(NAME, config, DefaultType)
return config
}
_handleSwipe() {
const absDeltax = Math.abs(this.touchDeltaX)
if (absDeltax <= SWIPE_THRESHOLD) {
return
}
const direction = absDeltax / this.touchDeltaX
this.touchDeltaX = 0
// swipe left
if (direction > 0) {
this.prev()
}
// swipe right
if (direction < 0) {
this.next()
}
}
_addEventListeners() {
if (this._config.keyboard) {
$(this._element).on(EVENT_KEYDOWN, event => this._keydown(event))
}
if (this._config.pause === 'hover') {
$(this._element)
.on(EVENT_MOUSEENTER, event => this.pause(event))
.on(EVENT_MOUSELEAVE, event => this.cycle(event))
}
if (this._config.touch) {
this._addTouchEventListeners()
}
}
_addTouchEventListeners() {
if (!this._touchSupported) {
return
}
const start = event => {
if (this._pointerEvent && PointerType[event.originalEvent.pointerType.toUpperCase()]) {
this.touchStartX = event.originalEvent.clientX
} else if (!this._pointerEvent) {
this.touchStartX = event.originalEvent.touches[0].clientX
}
}
const move = event => {
// ensure swiping with one touch and not pinching
if (event.originalEvent.touches && event.originalEvent.touches.length > 1) {
this.touchDeltaX = 0
} else {
this.touchDeltaX = event.originalEvent.touches[0].clientX - this.touchStartX
}
}
const end = event => {
if (this._pointerEvent && PointerType[event.originalEvent.pointerType.toUpperCase()]) {
this.touchDeltaX = event.originalEvent.clientX - this.touchStartX
}
this._handleSwipe()
if (this._config.pause === 'hover') {
// If it's a touch-enabled device, mouseenter/leave are fired as
// part of the mouse compatibility events on first tap - the carousel
// would stop cycling until user tapped out of it;
// here, we listen for touchend, explicitly pause the carousel
// (as if it's the second time we tap on it, mouseenter compat event
// is NOT fired) and after a timeout (to allow for mouse compatibility
// events to fire) we explicitly restart cycling
this.pause()
if (this.touchTimeout) {
clearTimeout(this.touchTimeout)
}
this.touchTimeout = setTimeout(event => this.cycle(event), TOUCHEVENT_COMPAT_WAIT + this._config.interval)
}
}
$(this._element.querySelectorAll(SELECTOR_ITEM_IMG))
.on(EVENT_DRAG_START, e => e.preventDefault())
if (this._pointerEvent) {
$(this._element).on(EVENT_POINTERDOWN, event => start(event))
$(this._element).on(EVENT_POINTERUP, event => end(event))
this._element.classList.add(CLASS_NAME_POINTER_EVENT)
} else {
$(this._element).on(EVENT_TOUCHSTART, event => start(event))
$(this._element).on(EVENT_TOUCHMOVE, event => move(event))
$(this._element).on(EVENT_TOUCHEND, event => end(event))
}
}
_keydown(event) {
if (/input|textarea/i.test(event.target.tagName)) {
return
}
switch (event.which) {
case ARROW_LEFT_KEYCODE:
event.preventDefault()
this.prev()
break
case ARROW_RIGHT_KEYCODE:
event.preventDefault()
this.next()
break
default:
}
}
_getItemIndex(element) {
this._items = element && element.parentNode ?
[].slice.call(element.parentNode.querySelectorAll(SELECTOR_ITEM)) :
[]
return this._items.indexOf(element)
}
_getItemByDirection(direction, activeElement) {
const isNextDirection = direction === DIRECTION_NEXT
const isPrevDirection = direction === DIRECTION_PREV
const activeIndex = this._getItemIndex(activeElement)
const lastItemIndex = this._items.length - 1
const isGoingToWrap = isPrevDirection && activeIndex === 0 ||
isNextDirection && activeIndex === lastItemIndex
if (isGoingToWrap && !this._config.wrap) {
return activeElement
}
const delta = direction === DIRECTION_PREV ? -1 : 1
const itemIndex = (activeIndex + delta) % this._items.length
return itemIndex === -1 ?
this._items[this._items.length - 1] : this._items[itemIndex]
}
_triggerSlideEvent(relatedTarget, eventDirectionName) {
const targetIndex = this._getItemIndex(relatedTarget)
const fromIndex = this._getItemIndex(this._element.querySelector(SELECTOR_ACTIVE_ITEM))
const slideEvent = $.Event(EVENT_SLIDE, {
relatedTarget,
direction: eventDirectionName,
from: fromIndex,
to: targetIndex
})
$(this._element).trigger(slideEvent)
return slideEvent
}
_setActiveIndicatorElement(element) {
if (this._indicatorsElement) {
const indicators = [].slice.call(this._indicatorsElement.querySelectorAll(SELECTOR_ACTIVE))
$(indicators).removeClass(CLASS_NAME_ACTIVE)
const nextIndicator = this._indicatorsElement.children[
this._getItemIndex(element)
]
if (nextIndicator) {
$(nextIndicator).addClass(CLASS_NAME_ACTIVE)
}
}
}
_updateInterval() {
const element = this._activeElement || this._element.querySelector(SELECTOR_ACTIVE_ITEM)
if (!element) {
return
}
const elementInterval = parseInt(element.getAttribute('data-interval'), 10)
if (elementInterval) {
this._config.defaultInterval = this._config.defaultInterval || this._config.interval
this._config.interval = elementInterval
} else {
this._config.interval = this._config.defaultInterval || this._config.interval
}
}
_slide(direction, element) {
const activeElement = this._element.querySelector(SELECTOR_ACTIVE_ITEM)
const activeElementIndex = this._getItemIndex(activeElement)
const nextElement = element || activeElement &&
this._getItemByDirection(direction, activeElement)
const nextElementIndex = this._getItemIndex(nextElement)
const isCycling = Boolean(this._interval)
let directionalClassName
let orderClassName
let eventDirectionName
if (direction === DIRECTION_NEXT) {
directionalClassName = CLASS_NAME_LEFT
orderClassName = CLASS_NAME_NEXT
eventDirectionName = DIRECTION_LEFT
} else {
directionalClassName = CLASS_NAME_RIGHT
orderClassName = CLASS_NAME_PREV
eventDirectionName = DIRECTION_RIGHT
}
if (nextElement && $(nextElement).hasClass(CLASS_NAME_ACTIVE)) {
this._isSliding = false
return
}
const slideEvent = this._triggerSlideEvent(nextElement, eventDirectionName)
if (slideEvent.isDefaultPrevented()) {
return
}
if (!activeElement || !nextElement) {
// Some weirdness is happening, so we bail
return
}
this._isSliding = true
if (isCycling) {
this.pause()
}
this._setActiveIndicatorElement(nextElement)
this._activeElement = nextElement
const slidEvent = $.Event(EVENT_SLID, {
relatedTarget: nextElement,
direction: eventDirectionName,
from: activeElementIndex,
to: nextElementIndex
})
if ($(this._element).hasClass(CLASS_NAME_SLIDE)) {
$(nextElement).addClass(orderClassName)
Util.reflow(nextElement)
$(activeElement).addClass(directionalClassName)
$(nextElement).addClass(directionalClassName)
const transitionDuration = Util.getTransitionDurationFromElement(activeElement)
$(activeElement)
.one(Util.TRANSITION_END, () => {
$(nextElement)
.removeClass(`${directionalClassName} ${orderClassName}`)
.addClass(CLASS_NAME_ACTIVE)
$(activeElement).removeClass(`${CLASS_NAME_ACTIVE} ${orderClassName} ${directionalClassName}`)
this._isSliding = false
setTimeout(() => $(this._element).trigger(slidEvent), 0)
})
.emulateTransitionEnd(transitionDuration)
} else {
$(activeElement).removeClass(CLASS_NAME_ACTIVE)
$(nextElement).addClass(CLASS_NAME_ACTIVE)
this._isSliding = false
$(this._element).trigger(slidEvent)
}
if (isCycling) {
this.cycle()
}
}
// Static
static _jQueryInterface(config) {
return this.each(function () {
let data = $(this).data(DATA_KEY)
let _config = {
...Default,
...$(this).data()
}
if (typeof config === 'object') {
_config = {
..._config,
...config
}
}
const action = typeof config === 'string' ? config : _config.slide
if (!data) {
data = new Carousel(this, _config)
$(this).data(DATA_KEY, data)
}
if (typeof config === 'number') {
data.to(config)
} else if (typeof action === 'string') {
if (typeof data[action] === 'undefined') {
throw new TypeError(`No method named "${action}"`)
}
data[action]()
} else if (_config.interval && _config.ride) {
data.pause()
data.cycle()
}
})
}
static _dataApiClickHandler(event) {
const selector = Util.getSelectorFromElement(this)
if (!selector) {
return
}
const target = $(selector)[0]
if (!target || !$(target).hasClass(CLASS_NAME_CAROUSEL)) {
return
}
const config = {
...$(target).data(),
...$(this).data()
}
const slideIndex = this.getAttribute('data-slide-to')
if (slideIndex) {
config.interval = false
}
Carousel._jQueryInterface.call($(target), config)
if (slideIndex) {
$(target).data(DATA_KEY).to(slideIndex)
}
event.preventDefault()
}
}
/**
* ------------------------------------------------------------------------
* Data Api implementation
* ------------------------------------------------------------------------
*/
$(document).on(EVENT_CLICK_DATA_API, SELECTOR_DATA_SLIDE, Carousel._dataApiClickHandler)
$(window).on(EVENT_LOAD_DATA_API, () => {
const carousels = [].slice.call(document.querySelectorAll(SELECTOR_DATA_RIDE))
for (let i = 0, len = carousels.length; i < len; i++) {
const $carousel = $(carousels[i])
Carousel._jQueryInterface.call($carousel, $carousel.data())
}
})
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = Carousel._jQueryInterface
$.fn[NAME].Constructor = Carousel
$.fn[NAME].noConflict = () => {
$.fn[NAME] = JQUERY_NO_CONFLICT
return Carousel._jQueryInterface
}
export default Carousel

392
amd/src/bootstrap/collapse.js

@ -0,0 +1,392 @@
/**
* --------------------------------------------------------------------------
* Bootstrap (v4.6.0): collapse.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
import $ from 'jquery'
import Util from './util'
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
const NAME = 'collapse'
const VERSION = '4.6.0'
const DATA_KEY = 'bs.collapse'
const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api'
const JQUERY_NO_CONFLICT = $.fn[NAME]
const Default = {
toggle: true,
parent: ''
}
const DefaultType = {
toggle: 'boolean',
parent: '(string|element)'
}
const EVENT_SHOW = `show${EVENT_KEY}`
const EVENT_SHOWN = `shown${EVENT_KEY}`
const EVENT_HIDE = `hide${EVENT_KEY}`
const EVENT_HIDDEN = `hidden${EVENT_KEY}`
const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`
const CLASS_NAME_SHOW = 'show'
const CLASS_NAME_COLLAPSE = 'collapse'
const CLASS_NAME_COLLAPSING = 'collapsing'
const CLASS_NAME_COLLAPSED = 'collapsed'
const DIMENSION_WIDTH = 'width'
const DIMENSION_HEIGHT = 'height'
const SELECTOR_ACTIVES = '.show, .collapsing'
const SELECTOR_DATA_TOGGLE = '[data-toggle="collapse"]'
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
class Collapse {
constructor(element, config) {
this._isTransitioning = false
this._element = element
this._config = this._getConfig(config)
this._triggerArray = [].slice.call(document.querySelectorAll(
`[data-toggle="collapse"][href="#${element.id}"],` +
`[data-toggle="collapse"][data-target="#${element.id}"]`
))
const toggleList = [].slice.call(document.querySelectorAll(SELECTOR_DATA_TOGGLE))
for (let i = 0, len = toggleList.length; i < len; i++) {
const elem = toggleList[i]
const selector = Util.getSelectorFromElement(elem)
const filterElement = [].slice.call(document.querySelectorAll(selector))
.filter(foundElem => foundElem === element)
if (selector !== null && filterElement.length > 0) {
this._selector = selector
this._triggerArray.push(elem)
}
}
this._parent = this._config.parent ? this._getParent() : null
if (!this._config.parent) {
this._addAriaAndCollapsedClass(this._element, this._triggerArray)
}
if (this._config.toggle) {
this.toggle()
}
}
// Getters
static get VERSION() {
return VERSION
}
static get Default() {
return Default
}
// Public
toggle() {
if ($(this._element).hasClass(CLASS_NAME_SHOW)) {
this.hide()
} else {
this.show()
}
}
show() {
if (this._isTransitioning ||
$(this._element).hasClass(CLASS_NAME_SHOW)) {
return
}
let actives
let activesData
if (this._parent) {
actives = [].slice.call(this._parent.querySelectorAll(SELECTOR_ACTIVES))
.filter(elem => {
if (typeof this._config.parent === 'string') {
return elem.getAttribute('data-parent') === this._config.parent
}
return elem.classList.contains(CLASS_NAME_COLLAPSE)
})
if (actives.length === 0) {
actives = null
}
}
if (actives) {
activesData = $(actives).not(this._selector).data(DATA_KEY)
if (activesData && activesData._isTransitioning) {
return
}
}
const startEvent = $.Event(EVENT_SHOW)
$(this._element).trigger(startEvent)
if (startEvent.isDefaultPrevented()) {
return
}
if (actives) {
Collapse._jQueryInterface.call($(actives).not(this._selector), 'hide')
if (!activesData) {
$(actives).data(DATA_KEY, null)
}
}
const dimension = this._getDimension()
$(this._element)
.removeClass(CLASS_NAME_COLLAPSE)
.addClass(CLASS_NAME_COLLAPSING)
this._element.style[dimension] = 0
if (this._triggerArray.length) {
$(this._triggerArray)
.removeClass(CLASS_NAME_COLLAPSED)
.attr('aria-expanded', true)
}
this.setTransitioning(true)
const complete = () => {
$(this._element)
.removeClass(CLASS_NAME_COLLAPSING)
.addClass(`${CLASS_NAME_COLLAPSE} ${CLASS_NAME_SHOW}`)
this._element.style[dimension] = ''
this.setTransitioning(false)
$(this._element).trigger(EVENT_SHOWN)
}
const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1)
const scrollSize = `scroll${capitalizedDimension}`
const transitionDuration = Util.getTransitionDurationFromElement(this._element)
$(this._element)
.one(Util.TRANSITION_END, complete)
.emulateTransitionEnd(transitionDuration)
this._element.style[dimension] = `${this._element[scrollSize]}px`
}
hide() {
if (this._isTransitioning ||
!$(this._element).hasClass(CLASS_NAME_SHOW)) {
return
}
const startEvent = $.Event(EVENT_HIDE)
$(this._element).trigger(startEvent)
if (startEvent.isDefaultPrevented()) {
return
}
const dimension = this._getDimension()
this._element.style[dimension] = `${this._element.getBoundingClientRect()[dimension]}px`
Util.reflow(this._element)
$(this._element)
.addClass(CLASS_NAME_COLLAPSING)
.removeClass(`${CLASS_NAME_COLLAPSE} ${CLASS_NAME_SHOW}`)
const triggerArrayLength = this._triggerArray.length
if (triggerArrayLength > 0) {
for (let i = 0; i < triggerArrayLength; i++) {
const trigger = this._triggerArray[i]
const selector = Util.getSelectorFromElement(trigger)
if (selector !== null) {
const $elem = $([].slice.call(document.querySelectorAll(selector)))
if (!$elem.hasClass(CLASS_NAME_SHOW)) {
$(trigger).addClass(CLASS_NAME_COLLAPSED)
.attr('aria-expanded', false)
}
}
}
}
this.setTransitioning(true)
const complete = () => {
this.setTransitioning(false)
$(this._element)
.removeClass(CLASS_NAME_COLLAPSING)
.addClass(CLASS_NAME_COLLAPSE)
.trigger(EVENT_HIDDEN)
}
this._element.style[dimension] = ''
const transitionDuration = Util.getTransitionDurationFromElement(this._element)
$(this._element)
.one(Util.TRANSITION_END, complete)
.emulateTransitionEnd(transitionDuration)
}
setTransitioning(isTransitioning) {
this._isTransitioning = isTransitioning
}
dispose() {
$.removeData(this._element, DATA_KEY)
this._config = null
this._parent = null
this._element = null
this._triggerArray = null
this._isTransitioning = null
}
// Private
_getConfig(config) {
config = {
...Default,
...config
}
config.toggle = Boolean(config.toggle) // Coerce string values
Util.typeCheckConfig(NAME, config, DefaultType)
return config
}
_getDimension() {
const hasWidth = $(this._element).hasClass(DIMENSION_WIDTH)
return hasWidth ? DIMENSION_WIDTH : DIMENSION_HEIGHT
}
_getParent() {
let parent
if (Util.isElement(this._config.parent)) {
parent = this._config.parent
// It's a jQuery object
if (typeof this._config.parent.jquery !== 'undefined') {
parent = this._config.parent[0]
}
} else {
parent = document.querySelector(this._config.parent)
}
const selector = `[data-toggle="collapse"][data-parent="${this._config.parent}"]`
const children = [].slice.call(parent.querySelectorAll(selector))
$(children).each((i, element) => {
this._addAriaAndCollapsedClass(
Collapse._getTargetFromElement(element),
[element]
)
})
return parent
}
_addAriaAndCollapsedClass(element, triggerArray) {
const isOpen = $(element).hasClass(CLASS_NAME_SHOW)
if (triggerArray.length) {
$(triggerArray)
.toggleClass(CLASS_NAME_COLLAPSED, !isOpen)
.attr('aria-expanded', isOpen)
}
}
// Static
static _getTargetFromElement(element) {
const selector = Util.getSelectorFromElement(element)
return selector ? document.querySelector(selector) : null
}
static _jQueryInterface(config) {
return this.each(function () {
const $element = $(this)
let data = $element.data(DATA_KEY)
const _config = {
...Default,
...$element.data(),
...(typeof config === 'object' && config ? config : {})
}
if (!data && _config.toggle && typeof config === 'string' && /show|hide/.test(config)) {
_config.toggle = false
}
if (!data) {
data = new Collapse(this, _config)
$element.data(DATA_KEY, data)
}
if (typeof config === 'string') {
if (typeof data[config] === 'undefined') {
throw new TypeError(`No method named "${config}"`)
}
data[config]()
}
})
}
}
/**
* ------------------------------------------------------------------------
* Data Api implementation
* ------------------------------------------------------------------------
*/
$(document).on(EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
// preventDefault only for <a> elements (which change the URL) not inside the collapsible element
if (event.currentTarget.tagName === 'A') {
event.preventDefault()
}
const $trigger = $(this)
const selector = Util.getSelectorFromElement(this)
const selectors = [].slice.call(document.querySelectorAll(selector))
$(selectors).each(function () {
const $target = $(this)
const data = $target.data(DATA_KEY)
const config = data ? 'toggle' : $trigger.data()
Collapse._jQueryInterface.call($target, config)
})
})
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = Collapse._jQueryInterface
$.fn[NAME].Constructor = Collapse
$.fn[NAME].noConflict = () => {
$.fn[NAME] = JQUERY_NO_CONFLICT
return Collapse._jQueryInterface
}
export default Collapse

538
amd/src/bootstrap/dropdown.js

@ -0,0 +1,538 @@
/**
* --------------------------------------------------------------------------
* Bootstrap (v4.6.0): dropdown.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
import $ from 'jquery'
import Popper from 'core/popper'
import Util from './util'
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
const NAME = 'dropdown'
const VERSION = '4.6.0'
const DATA_KEY = 'bs.dropdown'
const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api'
const JQUERY_NO_CONFLICT = $.fn[NAME]
const ESCAPE_KEYCODE = 27 // KeyboardEvent.which value for Escape (Esc) key
const SPACE_KEYCODE = 32 // KeyboardEvent.which value for space key
const TAB_KEYCODE = 9 // KeyboardEvent.which value for tab key
const ARROW_UP_KEYCODE = 38 // KeyboardEvent.which value for up arrow key
const ARROW_DOWN_KEYCODE = 40 // KeyboardEvent.which value for down arrow key
const RIGHT_MOUSE_BUTTON_WHICH = 3 // MouseEvent.which value for the right button (assuming a right-handed mouse)
const REGEXP_KEYDOWN = new RegExp(`${ARROW_UP_KEYCODE}|${ARROW_DOWN_KEYCODE}|${ESCAPE_KEYCODE}`)
const EVENT_HIDE = `hide${EVENT_KEY}`
const EVENT_HIDDEN = `hidden${EVENT_KEY}`
const EVENT_SHOW = `show${EVENT_KEY}`
const EVENT_SHOWN = `shown${EVENT_KEY}`
const EVENT_CLICK = `click${EVENT_KEY}`
const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`
const EVENT_KEYDOWN_DATA_API = `keydown${EVENT_KEY}${DATA_API_KEY}`
const EVENT_KEYUP_DATA_API = `keyup${EVENT_KEY}${DATA_API_KEY}`
const CLASS_NAME_DISABLED = 'disabled'
const CLASS_NAME_SHOW = 'show'
const CLASS_NAME_DROPUP = 'dropup'
const CLASS_NAME_DROPRIGHT = 'dropright'
const CLASS_NAME_DROPLEFT = 'dropleft'
const CLASS_NAME_MENURIGHT = 'dropdown-menu-right'
const CLASS_NAME_POSITION_STATIC = 'position-static'
const SELECTOR_DATA_TOGGLE = '[data-toggle="dropdown"]'
const SELECTOR_FORM_CHILD = '.dropdown form'
const SELECTOR_MENU = '.dropdown-menu'
const SELECTOR_NAVBAR_NAV = '.navbar-nav'
const SELECTOR_VISIBLE_ITEMS = '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)'
const PLACEMENT_TOP = 'top-start'
const PLACEMENT_TOPEND = 'top-end'
const PLACEMENT_BOTTOM = 'bottom-start'
const PLACEMENT_BOTTOMEND = 'bottom-end'
const PLACEMENT_RIGHT = 'right-start'
const PLACEMENT_LEFT = 'left-start'
const Default = {
offset: 0,
flip: true,
boundary: 'scrollParent',
reference: 'toggle',
display: 'dynamic',
popperConfig: null
}
const DefaultType = {
offset: '(number|string|function)',
flip: 'boolean',
boundary: '(string|element)',
reference: '(string|element)',
display: 'string',
popperConfig: '(null|object)'
}
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
class Dropdown {
constructor(element, config) {
this._element = element
this._popper = null
this._config = this._getConfig(config)
this._menu = this._getMenuElement()
this._inNavbar = this._detectNavbar()
this._addEventListeners()
}
// Getters
static get VERSION() {
return VERSION
}
static get Default() {
return Default
}
static get DefaultType() {
return DefaultType
}
// Public
toggle() {
if (this._element.disabled || $(this._element).hasClass(CLASS_NAME_DISABLED)) {
return
}
const isActive = $(this._menu).hasClass(CLASS_NAME_SHOW)
Dropdown._clearMenus()
if (isActive) {
return
}
this.show(true)
}
show(usePopper = false) {
if (this._element.disabled || $(this._element).hasClass(CLASS_NAME_DISABLED) || $(this._menu).hasClass(CLASS_NAME_SHOW)) {
return
}
const relatedTarget = {
relatedTarget: this._element
}
const showEvent = $.Event(EVENT_SHOW, relatedTarget)
const parent = Dropdown._getParentFromElement(this._element)
$(parent).trigger(showEvent)
if (showEvent.isDefaultPrevented()) {
return
}
// Totally disable Popper for Dropdowns in Navbar
if (!this._inNavbar && usePopper) {
/**
* Check for Popper dependency
* Popper - https://popper.js.org
*/
if (typeof Popper === 'undefined') {
throw new TypeError('Bootstrap\'s dropdowns require Popper (https://popper.js.org)')
}
let referenceElement = this._element
if (this._config.reference === 'parent') {
referenceElement = parent
} else if (Util.isElement(this._config.reference)) {
referenceElement = this._config.reference
// Check if it's jQuery element
if (typeof this._config.reference.jquery !== 'undefined') {
referenceElement = this._config.reference[0]
}
}
// If boundary is not `scrollParent`, then set position to `static`
// to allow the menu to "escape" the scroll parent's boundaries
// https://github.com/twbs/bootstrap/issues/24251
if (this._config.boundary !== 'scrollParent') {
$(parent).addClass(CLASS_NAME_POSITION_STATIC)
}
this._popper = new Popper(referenceElement, this._menu, this._getPopperConfig())
}
// If this is a touch-enabled device we add extra
// empty mouseover listeners to the body's immediate children;
// only needed because of broken event delegation on iOS
// https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
if ('ontouchstart' in document.documentElement &&
$(parent).closest(SELECTOR_NAVBAR_NAV).length === 0) {
$(document.body).children().on('mouseover', null, $.noop)
}
this._element.focus()
this._element.setAttribute('aria-expanded', true)
$(this._menu).toggleClass(CLASS_NAME_SHOW)
$(parent)
.toggleClass(CLASS_NAME_SHOW)
.trigger($.Event(EVENT_SHOWN, relatedTarget))
}
hide() {
if (this._element.disabled || $(this._element).hasClass(CLASS_NAME_DISABLED) || !$(this._menu).hasClass(CLASS_NAME_SHOW)) {
return
}
const relatedTarget = {
relatedTarget: this._element
}
const hideEvent = $.Event(EVENT_HIDE, relatedTarget)
const parent = Dropdown._getParentFromElement(this._element)
$(parent).trigger(hideEvent)
if (hideEvent.isDefaultPrevented()) {
return
}
if (this._popper) {
this._popper.destroy()
}
$(this._menu).toggleClass(CLASS_NAME_SHOW)
$(parent)
.toggleClass(CLASS_NAME_SHOW)
.trigger($.Event(EVENT_HIDDEN, relatedTarget))
}
dispose() {
$.removeData(this._element, DATA_KEY)
$(this._element).off(EVENT_KEY)
this._element = null
this._menu = null
if (this._popper !== null) {
this._popper.destroy()
this._popper = null
}
}
update() {
this._inNavbar = this._detectNavbar()
if (this._popper !== null) {
this._popper.scheduleUpdate()
}
}
// Private
_addEventListeners() {
$(this._element).on(EVENT_CLICK, event => {
event.preventDefault()
event.stopPropagation()
this.toggle()
})
}
_getConfig(config) {
config = {
...this.constructor.Default,
...$(this._element).data(),
...config
}
Util.typeCheckConfig(
NAME,
config,
this.constructor.DefaultType
)
return config
}
_getMenuElement() {
if (!this._menu) {
const parent = Dropdown._getParentFromElement(this._element)
if (parent) {
this._menu = parent.querySelector(SELECTOR_MENU)
}
}
return this._menu
}
_getPlacement() {
const $parentDropdown = $(this._element.parentNode)
let placement = PLACEMENT_BOTTOM
// Handle dropup
if ($parentDropdown.hasClass(CLASS_NAME_DROPUP)) {
placement = $(this._menu).hasClass(CLASS_NAME_MENURIGHT) ?
PLACEMENT_TOPEND :
PLACEMENT_TOP
} else if ($parentDropdown.hasClass(CLASS_NAME_DROPRIGHT)) {
placement = PLACEMENT_RIGHT
} else if ($parentDropdown.hasClass(CLASS_NAME_DROPLEFT)) {
placement = PLACEMENT_LEFT
} else if ($(this._menu).hasClass(CLASS_NAME_MENURIGHT)) {
placement = PLACEMENT_BOTTOMEND
}
return placement
}
_detectNavbar() {
return $(this._element).closest('.navbar').length > 0
}
_getOffset() {
const offset = {}
if (typeof this._config.offset === 'function') {
offset.fn = data => {
data.offsets = {
...data.offsets,
...(this._config.offset(data.offsets, this._element) || {})
}
return data
}
} else {
offset.offset = this._config.offset
}
return offset
}
_getPopperConfig() {
const popperConfig = {
placement: this._getPlacement(),
modifiers: {
offset: this._getOffset(),
flip: {
enabled: this._config.flip
},
preventOverflow: {
boundariesElement: this._config.boundary
}
}
}
// Disable Popper if we have a static display
if (this._config.display === 'static') {
popperConfig.modifiers.applyStyle = {
enabled: false
}
}
return {
...popperConfig,
...this._config.popperConfig
}
}
// Static
static _jQueryInterface(config) {
return this.each(function () {
let data = $(this).data(DATA_KEY)
const _config = typeof config === 'object' ? config : null
if (!data) {
data = new Dropdown(this, _config)
$(this).data(DATA_KEY, data)
}
if (typeof config === 'string') {
if (typeof data[config] === 'undefined') {
throw new TypeError(`No method named "${config}"`)
}
data[config]()
}
})
}
static _clearMenus(event) {
if (event && (event.which === RIGHT_MOUSE_BUTTON_WHICH ||
event.type === 'keyup' && event.which !== TAB_KEYCODE)) {
return
}
const toggles = [].slice.call(document.querySelectorAll(SELECTOR_DATA_TOGGLE))
for (let i = 0, len = toggles.length; i < len; i++) {
const parent = Dropdown._getParentFromElement(toggles[i])
const context = $(toggles[i]).data(DATA_KEY)
const relatedTarget = {
relatedTarget: toggles[i]
}
if (event && event.type === 'click') {
relatedTarget.clickEvent = event
}
if (!context) {
continue
}
const dropdownMenu = context._menu
if (!$(parent).hasClass(CLASS_NAME_SHOW)) {
continue
}
if (event && (event.type === 'click' &&
/input|textarea/i.test(event.target.tagName) || event.type === 'keyup' && event.which === TAB_KEYCODE) &&
$.contains(parent, event.target)) {
continue
}
const hideEvent = $.Event(EVENT_HIDE, relatedTarget)
$(parent).trigger(hideEvent)
if (hideEvent.isDefaultPrevented()) {
continue
}
// If this is a touch-enabled device we remove the extra
// empty mouseover listeners we added for iOS support
if ('ontouchstart' in document.documentElement) {
$(document.body).children().off('mouseover', null, $.noop)
}
toggles[i].setAttribute('aria-expanded', 'false')
if (context._popper) {
context._popper.destroy()
}
$(dropdownMenu).removeClass(CLASS_NAME_SHOW)
$(parent)
.removeClass(CLASS_NAME_SHOW)
.trigger($.Event(EVENT_HIDDEN, relatedTarget))
}
}
static _getParentFromElement(element) {
let parent
const selector = Util.getSelectorFromElement(element)
if (selector) {
parent = document.querySelector(selector)
}
return parent || element.parentNode
}
// eslint-disable-next-line complexity
static _dataApiKeydownHandler(event) {
// If not input/textarea:
// - And not a key in REGEXP_KEYDOWN => not a dropdown command
// If input/textarea:
// - If space key => not a dropdown command
// - If key is other than escape
// - If key is not up or down => not a dropdown command
// - If trigger inside the menu => not a dropdown command
if (/input|textarea/i.test(event.target.tagName) ?
event.which === SPACE_KEYCODE || event.which !== ESCAPE_KEYCODE &&
(event.which !== ARROW_DOWN_KEYCODE && event.which !== ARROW_UP_KEYCODE ||
$(event.target).closest(SELECTOR_MENU).length) : !REGEXP_KEYDOWN.test(event.which)) {
return
}
if (this.disabled || $(this).hasClass(CLASS_NAME_DISABLED)) {
return
}
const parent = Dropdown._getParentFromElement(this)
const isActive = $(parent).hasClass(CLASS_NAME_SHOW)
if (!isActive && event.which === ESCAPE_KEYCODE) {
return
}
event.preventDefault()
event.stopPropagation()
if (!isActive || (event.which === ESCAPE_KEYCODE || event.which === SPACE_KEYCODE)) {
if (event.which === ESCAPE_KEYCODE) {
$(parent.querySelector(SELECTOR_DATA_TOGGLE)).trigger('focus')
}
$(this).trigger('click')
return
}
const items = [].slice.call(parent.querySelectorAll(SELECTOR_VISIBLE_ITEMS))
.filter(item => $(item).is(':visible'))
if (items.length === 0) {
return
}
let index = items.indexOf(event.target)
if (event.which === ARROW_UP_KEYCODE && index > 0) { // Up
index--
}
if (event.which === ARROW_DOWN_KEYCODE && index < items.length - 1) { // Down
index++
}
if (index < 0) {
index = 0
}
items[index].focus()
}
}
/**
* ------------------------------------------------------------------------
* Data Api implementation
* ------------------------------------------------------------------------
*/
$(document)
.on(EVENT_KEYDOWN_DATA_API, SELECTOR_DATA_TOGGLE, Dropdown._dataApiKeydownHandler)
.on(EVENT_KEYDOWN_DATA_API, SELECTOR_MENU, Dropdown._dataApiKeydownHandler)
.on(`${EVENT_CLICK_DATA_API} ${EVENT_KEYUP_DATA_API}`, Dropdown._clearMenus)
.on(EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
event.preventDefault()
event.stopPropagation()
Dropdown._jQueryInterface.call($(this), 'toggle')
})
.on(EVENT_CLICK_DATA_API, SELECTOR_FORM_CHILD, e => {
e.stopPropagation()
})
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = Dropdown._jQueryInterface
$.fn[NAME].Constructor = Dropdown
$.fn[NAME].noConflict = () => {
$.fn[NAME] = JQUERY_NO_CONFLICT
return Dropdown._jQueryInterface
}
export default Dropdown

629
amd/src/bootstrap/modal.js

@ -0,0 +1,629 @@
/**
* --------------------------------------------------------------------------
* Bootstrap (v4.6.0): modal.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
import $ from 'jquery'
import Util from './util'
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
const NAME = 'modal'
const VERSION = '4.6.0'
const DATA_KEY = 'bs.modal'
const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api'
const JQUERY_NO_CONFLICT = $.fn[NAME]
const ESCAPE_KEYCODE = 27 // KeyboardEvent.which value for Escape (Esc) key
const Default = {
backdrop: true,
keyboard: true,
focus: true,
show: true
}
const DefaultType = {
backdrop: '(boolean|string)',
keyboard: 'boolean',
focus: 'boolean',
show: 'boolean'
}
const EVENT_HIDE = `hide${EVENT_KEY}`
const EVENT_HIDE_PREVENTED = `hidePrevented${EVENT_KEY}`
const EVENT_HIDDEN = `hidden${EVENT_KEY}`
const EVENT_SHOW = `show${EVENT_KEY}`
const EVENT_SHOWN = `shown${EVENT_KEY}`
const EVENT_FOCUSIN = `focusin${EVENT_KEY}`
const EVENT_RESIZE = `resize${EVENT_KEY}`
const EVENT_CLICK_DISMISS = `click.dismiss${EVENT_KEY}`
const EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY}`
const EVENT_MOUSEUP_DISMISS = `mouseup.dismiss${EVENT_KEY}`
const EVENT_MOUSEDOWN_DISMISS = `mousedown.dismiss${EVENT_KEY}`
const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`
const CLASS_NAME_SCROLLABLE = 'modal-dialog-scrollable'
const CLASS_NAME_SCROLLBAR_MEASURER = 'modal-scrollbar-measure'
const CLASS_NAME_BACKDROP = 'modal-backdrop'
const CLASS_NAME_OPEN = 'modal-open'
const CLASS_NAME_FADE = 'fade'
const CLASS_NAME_SHOW = 'show'
const CLASS_NAME_STATIC = 'modal-static'
const SELECTOR_DIALOG = '.modal-dialog'
const SELECTOR_MODAL_BODY = '.modal-body'
const SELECTOR_DATA_TOGGLE = '[data-toggle="modal"]'
const SELECTOR_DATA_DISMISS = '[data-dismiss="modal"]'
const SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top'
const SELECTOR_STICKY_CONTENT = '.sticky-top'
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
class Modal {
constructor(element, config) {
this._config = this._getConfig(config)
this._element = element
this._dialog = element.querySelector(SELECTOR_DIALOG)
this._backdrop = null
this._isShown = false
this._isBodyOverflowing = false
this._ignoreBackdropClick = false
this._isTransitioning = false
this._scrollbarWidth = 0
}
// Getters
static get VERSION() {
return VERSION
}
static get Default() {
return Default
}
// Public
toggle(relatedTarget) {
return this._isShown ? this.hide() : this.show(relatedTarget)
}
show(relatedTarget) {
if (this._isShown || this._isTransitioning) {
return
}
if ($(this._element).hasClass(CLASS_NAME_FADE)) {
this._isTransitioning = true
}
const showEvent = $.Event(EVENT_SHOW, {
relatedTarget
})
$(this._element).trigger(showEvent)
if (this._isShown || showEvent.isDefaultPrevented()) {
return
}
this._isShown = true
this._checkScrollbar()
this._setScrollbar()
this._adjustDialog()
this._setEscapeEvent()
this._setResizeEvent()
$(this._element).on(
EVENT_CLICK_DISMISS,
SELECTOR_DATA_DISMISS,
event => this.hide(event)
)
$(this._dialog).on(EVENT_MOUSEDOWN_DISMISS, () => {
$(this._element).one(EVENT_MOUSEUP_DISMISS, event => {
if ($(event.target).is(this._element)) {
this._ignoreBackdropClick = true
}
})
})
this._showBackdrop(() => this._showElement(relatedTarget))
}
hide(event) {
if (event) {
event.preventDefault()
}
if (!this._isShown || this._isTransitioning) {
return
}
const hideEvent = $.Event(EVENT_HIDE)
$(this._element).trigger(hideEvent)
if (!this._isShown || hideEvent.isDefaultPrevented()) {
return
}
this._isShown = false
const transition = $(this._element).hasClass(CLASS_NAME_FADE)
if (transition) {
this._isTransitioning = true
}
this._setEscapeEvent()
this._setResizeEvent()
$(document).off(EVENT_FOCUSIN)
$(this._element).removeClass(CLASS_NAME_SHOW)
$(this._element).off(EVENT_CLICK_DISMISS)
$(this._dialog).off(EVENT_MOUSEDOWN_DISMISS)
if (transition) {
const transitionDuration = Util.getTransitionDurationFromElement(this._element)
$(this._element)
.one(Util.TRANSITION_END, event => this._hideModal(event))
.emulateTransitionEnd(transitionDuration)
} else {
this._hideModal()
}
}
dispose() {
[window, this._element, this._dialog]
.forEach(htmlElement => $(htmlElement).off(EVENT_KEY))
/**
* `document` has 2 events `EVENT_FOCUSIN` and `EVENT_CLICK_DATA_API`
* Do not move `document` in `htmlElements` array
* It will remove `EVENT_CLICK_DATA_API` event that should remain
*/
$(document).off(EVENT_FOCUSIN)
$.removeData(this._element, DATA_KEY)
this._config = null
this._element = null
this._dialog = null
this._backdrop = null
this._isShown = null
this._isBodyOverflowing = null
this._ignoreBackdropClick = null
this._isTransitioning = null
this._scrollbarWidth = null
}
handleUpdate() {
this._adjustDialog()
}
// Private
_getConfig(config) {
config = {
...Default,
...config
}
Util.typeCheckConfig(NAME, config, DefaultType)
return config
}
_triggerBackdropTransition() {
const hideEventPrevented = $.Event(EVENT_HIDE_PREVENTED)
$(this._element).trigger(hideEventPrevented)
if (hideEventPrevented.isDefaultPrevented()) {
return
}
const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight
if (!isModalOverflowing) {
this._element.style.overflowY = 'hidden'
}
this._element.classList.add(CLASS_NAME_STATIC)
const modalTransitionDuration = Util.getTransitionDurationFromElement(this._dialog)
$(this._element).off(Util.TRANSITION_END)
$(this._element).one(Util.TRANSITION_END, () => {
this._element.classList.remove(CLASS_NAME_STATIC)
if (!isModalOverflowing) {
$(this._element).one(Util.TRANSITION_END, () => {
this._element.style.overflowY = ''
})
.emulateTransitionEnd(this._element, modalTransitionDuration)
}
})
.emulateTransitionEnd(modalTransitionDuration)
this._element.focus()
}
_showElement(relatedTarget) {
const transition = $(this._element).hasClass(CLASS_NAME_FADE)
const modalBody = this._dialog ? this._dialog.querySelector(SELECTOR_MODAL_BODY) : null
if (!this._element.parentNode ||
this._element.parentNode.nodeType !== Node.ELEMENT_NODE) {
// Don't move modal's DOM position
document.body.appendChild(this._element)
}
this._element.style.display = 'block'
this._element.removeAttribute('aria-hidden')
this._element.setAttribute('aria-modal', true)
this._element.setAttribute('role', 'dialog')
if ($(this._dialog).hasClass(CLASS_NAME_SCROLLABLE) && modalBody) {
modalBody.scrollTop = 0
} else {
this._element.scrollTop = 0
}
if (transition) {
Util.reflow(this._element)
}
$(this._element).addClass(CLASS_NAME_SHOW)
if (this._config.focus) {
this._enforceFocus()
}
const shownEvent = $.Event(EVENT_SHOWN, {
relatedTarget
})
const transitionComplete = () => {
if (this._config.focus) {
this._element.focus()
}
this._isTransitioning = false
$(this._element).trigger(shownEvent)
}
if (transition) {
const transitionDuration = Util.getTransitionDurationFromElement(this._dialog)
$(this._dialog)
.one(Util.TRANSITION_END, transitionComplete)
.emulateTransitionEnd(transitionDuration)
} else {
transitionComplete()
}
}
_enforceFocus() {
$(document)
.off(EVENT_FOCUSIN) // Guard against infinite focus loop
.on(EVENT_FOCUSIN, event => {
if (document !== event.target &&
this._element !== event.target &&
$(this._element).has(event.target).length === 0) {
this._element.focus()
}
})
}
_setEscapeEvent() {
if (this._isShown) {
$(this._element).on(EVENT_KEYDOWN_DISMISS, event => {
if (this._config.keyboard && event.which === ESCAPE_KEYCODE) {
event.preventDefault()
this.hide()
} else if (!this._config.keyboard && event.which === ESCAPE_KEYCODE) {
this._triggerBackdropTransition()
}
})
} else if (!this._isShown) {
$(this._element).off(EVENT_KEYDOWN_DISMISS)
}
}
_setResizeEvent() {
if (this._isShown) {
$(window).on(EVENT_RESIZE, event => this.handleUpdate(event))
} else {
$(window).off(EVENT_RESIZE)
}
}
_hideModal() {
this._element.style.display = 'none'
this._element.setAttribute('aria-hidden', true)
this._element.removeAttribute('aria-modal')
this._element.removeAttribute('role')
this._isTransitioning = false
this._showBackdrop(() => {
$(document.body).removeClass(CLASS_NAME_OPEN)
this._resetAdjustments()
this._resetScrollbar()
$(this._element).trigger(EVENT_HIDDEN)
})
}
_removeBackdrop() {
if (this._backdrop) {
$(this._backdrop).remove()
this._backdrop = null
}
}
_showBackdrop(callback) {
const animate = $(this._element).hasClass(CLASS_NAME_FADE) ?
CLASS_NAME_FADE : ''
if (this._isShown && this._config.backdrop) {
this._backdrop = document.createElement('div')
this._backdrop.className = CLASS_NAME_BACKDROP
if (animate) {
this._backdrop.classList.add(animate)
}
$(this._backdrop).appendTo(document.body)
$(this._element).on(EVENT_CLICK_DISMISS, event => {
if (this._ignoreBackdropClick) {
this._ignoreBackdropClick = false
return
}
if (event.target !== event.currentTarget) {
return
}
if (this._config.backdrop === 'static') {
this._triggerBackdropTransition()
} else {
this.hide()
}
})
if (animate) {
Util.reflow(this._backdrop)
}
$(this._backdrop).addClass(CLASS_NAME_SHOW)
if (!callback) {
return
}
if (!animate) {
callback()
return
}
const backdropTransitionDuration = Util.getTransitionDurationFromElement(this._backdrop)
$(this._backdrop)
.one(Util.TRANSITION_END, callback)
.emulateTransitionEnd(backdropTransitionDuration)
} else if (!this._isShown && this._backdrop) {
$(this._backdrop).removeClass(CLASS_NAME_SHOW)
const callbackRemove = () => {
this._removeBackdrop()
if (callback) {
callback()
}
}
if ($(this._element).hasClass(CLASS_NAME_FADE)) {
const backdropTransitionDuration = Util.getTransitionDurationFromElement(this._backdrop)
$(this._backdrop)
.one(Util.TRANSITION_END, callbackRemove)
.emulateTransitionEnd(backdropTransitionDuration)
} else {
callbackRemove()
}
} else if (callback) {
callback()
}
}
// ----------------------------------------------------------------------
// the following methods are used to handle overflowing modals
// todo (fat): these should probably be refactored out of modal.js
// ----------------------------------------------------------------------
_adjustDialog() {
const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight
if (!this._isBodyOverflowing && isModalOverflowing) {
this._element.style.paddingLeft = `${this._scrollbarWidth}px`
}
if (this._isBodyOverflowing && !isModalOverflowing) {
this._element.style.paddingRight = `${this._scrollbarWidth}px`
}
}
_resetAdjustments() {
this._element.style.paddingLeft = ''
this._element.style.paddingRight = ''
}
_checkScrollbar() {
const rect = document.body.getBoundingClientRect()
this._isBodyOverflowing = Math.round(rect.left + rect.right) < window.innerWidth
this._scrollbarWidth = this._getScrollbarWidth()
}
_setScrollbar() {
if (this._isBodyOverflowing) {
// Note: DOMNode.style.paddingRight returns the actual value or '' if not set
// while $(DOMNode).css('padding-right') returns the calculated value or 0 if not set
const fixedContent = [].slice.call(document.querySelectorAll(SELECTOR_FIXED_CONTENT))
const stickyContent = [].slice.call(document.querySelectorAll(SELECTOR_STICKY_CONTENT))
// Adjust fixed content padding
$(fixedContent).each((index, element) => {
const actualPadding = element.style.paddingRight
const calculatedPadding = $(element).css('padding-right')
$(element)
.data('padding-right', actualPadding)
.css('padding-right', `${parseFloat(calculatedPadding) + this._scrollbarWidth}px`)
})
// Adjust sticky content margin
$(stickyContent).each((index, element) => {
const actualMargin = element.style.marginRight
const calculatedMargin = $(element).css('margin-right')
$(element)
.data('margin-right', actualMargin)
.css('margin-right', `${parseFloat(calculatedMargin) - this._scrollbarWidth}px`)
})
// Adjust body padding
const actualPadding = document.body.style.paddingRight
const calculatedPadding = $(document.body).css('padding-right')
$(document.body)
.data('padding-right', actualPadding)
.css('padding-right', `${parseFloat(calculatedPadding) + this._scrollbarWidth}px`)
}
$(document.body).addClass(CLASS_NAME_OPEN)
}
_resetScrollbar() {
// Restore fixed content padding
const fixedContent = [].slice.call(document.querySelectorAll(SELECTOR_FIXED_CONTENT))
$(fixedContent).each((index, element) => {
const padding = $(element).data('padding-right')
$(element).removeData('padding-right')
element.style.paddingRight = padding ? padding : ''
})
// Restore sticky content
const elements = [].slice.call(document.querySelectorAll(`${SELECTOR_STICKY_CONTENT}`))
$(elements).each((index, element) => {
const margin = $(element).data('margin-right')
if (typeof margin !== 'undefined') {
$(element).css('margin-right', margin).removeData('margin-right')
}
})
// Restore body padding
const padding = $(document.body).data('padding-right')
$(document.body).removeData('padding-right')
document.body.style.paddingRight = padding ? padding : ''
}
_getScrollbarWidth() { // thx d.walsh
const scrollDiv = document.createElement('div')
scrollDiv.className = CLASS_NAME_SCROLLBAR_MEASURER
document.body.appendChild(scrollDiv)
const scrollbarWidth = scrollDiv.getBoundingClientRect().width - scrollDiv.clientWidth
document.body.removeChild(scrollDiv)
return scrollbarWidth
}
// Static
static _jQueryInterface(config, relatedTarget) {
return this.each(function () {
let data = $(this).data(DATA_KEY)
const _config = {
...Default,
...$(this).data(),
...(typeof config === 'object' && config ? config : {})
}
if (!data) {
data = new Modal(this, _config)
$(this).data(DATA_KEY, data)
}
if (typeof config === 'string') {
if (typeof data[config] === 'undefined') {
throw new TypeError(`No method named "${config}"`)
}
data[config](relatedTarget)
} else if (_config.show) {
data.show(relatedTarget)
}
})
}
}
/**
* ------------------------------------------------------------------------
* Data Api implementation
* ------------------------------------------------------------------------
*/
$(document).on(EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
let target
const selector = Util.getSelectorFromElement(this)
if (selector) {
target = document.querySelector(selector)
}
const config = $(target).data(DATA_KEY) ?
'toggle' : {
...$(target).data(),
...$(this).data()
}
if (this.tagName === 'A' || this.tagName === 'AREA') {
event.preventDefault()
}
const $target = $(target).one(EVENT_SHOW, showEvent => {
if (showEvent.isDefaultPrevented()) {
// Only register focus restorer if modal will actually get shown
return
}
$target.one(EVENT_HIDDEN, () => {
if ($(this).is(':visible')) {
this.focus()
}
})
})
Modal._jQueryInterface.call($(target), config, this)
})
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = Modal._jQueryInterface
$.fn[NAME].Constructor = Modal
$.fn[NAME].noConflict = () => {
$.fn[NAME] = JQUERY_NO_CONFLICT
return Modal._jQueryInterface
}
export default Modal

182
amd/src/bootstrap/popover.js

@ -0,0 +1,182 @@
/**
* --------------------------------------------------------------------------
* Bootstrap (v4.6.0): popover.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
import $ from 'jquery'
import Tooltip from './tooltip'
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
const NAME = 'popover'
const VERSION = '4.6.0'
const DATA_KEY = 'bs.popover'
const EVENT_KEY = `.${DATA_KEY}`
const JQUERY_NO_CONFLICT = $.fn[NAME]
const CLASS_PREFIX = 'bs-popover'
const BSCLS_PREFIX_REGEX = new RegExp(`(^|\\s)${CLASS_PREFIX}\\S+`, 'g')
const Default = {
...Tooltip.Default,
placement: 'right',
trigger: 'click',
content: '',
template: '<div class="popover" role="tooltip">' +
'<div class="arrow"></div>' +
'<h3 class="popover-header"></h3>' +
'<div class="popover-body"></div></div>'
}
const DefaultType = {
...Tooltip.DefaultType,
content: '(string|element|function)'
}
const CLASS_NAME_FADE = 'fade'
const CLASS_NAME_SHOW = 'show'
const SELECTOR_TITLE = '.popover-header'
const SELECTOR_CONTENT = '.popover-body'
const Event = {
HIDE: `hide${EVENT_KEY}`,
HIDDEN: `hidden${EVENT_KEY}`,
SHOW: `show${EVENT_KEY}`,
SHOWN: `shown${EVENT_KEY}`,
INSERTED: `inserted${EVENT_KEY}`,
CLICK: `click${EVENT_KEY}`,
FOCUSIN: `focusin${EVENT_KEY}`,
FOCUSOUT: `focusout${EVENT_KEY}`,
MOUSEENTER: `mouseenter${EVENT_KEY}`,
MOUSELEAVE: `mouseleave${EVENT_KEY}`
}
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
class Popover extends Tooltip {
// Getters
static get VERSION() {
return VERSION
}
static get Default() {
return Default
}
static get NAME() {
return NAME
}
static get DATA_KEY() {
return DATA_KEY
}
static get Event() {
return Event
}
static get EVENT_KEY() {
return EVENT_KEY
}
static get DefaultType() {
return DefaultType
}
// Overrides
isWithContent() {
return this.getTitle() || this._getContent()
}
addAttachmentClass(attachment) {
$(this.getTipElement()).addClass(`${CLASS_PREFIX}-${attachment}`)
}
getTipElement() {
this.tip = this.tip || $(this.config.template)[0]
return this.tip
}
setContent() {
const $tip = $(this.getTipElement())
// We use append for html objects to maintain js events
this.setElementContent($tip.find(SELECTOR_TITLE), this.getTitle())
let content = this._getContent()
if (typeof content === 'function') {
content = content.call(this.element)
}
this.setElementContent($tip.find(SELECTOR_CONTENT), content)
$tip.removeClass(`${CLASS_NAME_FADE} ${CLASS_NAME_SHOW}`)
}
// Private
_getContent() {
return this.element.getAttribute('data-content') ||
this.config.content
}
_cleanTipClass() {
const $tip = $(this.getTipElement())
const tabClass = $tip.attr('class').match(BSCLS_PREFIX_REGEX)
if (tabClass !== null && tabClass.length > 0) {
$tip.removeClass(tabClass.join(''))
}
}
// Static
static _jQueryInterface(config) {
return this.each(function () {
let data = $(this).data(DATA_KEY)
const _config = typeof config === 'object' ? config : null
if (!data && /dispose|hide/.test(config)) {
return
}
if (!data) {
data = new Popover(this, _config)
$(this).data(DATA_KEY, data)
}
if (typeof config === 'string') {
if (typeof data[config] === 'undefined') {
throw new TypeError(`No method named "${config}"`)
}
data[config]()
}
})
}
}
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = Popover._jQueryInterface
$.fn[NAME].Constructor = Popover
$.fn[NAME].noConflict = () => {
$.fn[NAME] = JQUERY_NO_CONFLICT
return Popover._jQueryInterface
}
export default Popover

324
amd/src/bootstrap/scrollspy.js

@ -0,0 +1,324 @@
/**
* --------------------------------------------------------------------------
* Bootstrap (v4.6.0): scrollspy.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
import $ from 'jquery'
import Util from './util'
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
const NAME = 'scrollspy'
const VERSION = '4.6.0'
const DATA_KEY = 'bs.scrollspy'
const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api'
const JQUERY_NO_CONFLICT = $.fn[NAME]
const Default = {
offset: 10,
method: 'auto',
target: ''
}
const DefaultType = {
offset: 'number',
method: 'string',
target: '(string|element)'
}
const EVENT_ACTIVATE = `activate${EVENT_KEY}`
const EVENT_SCROLL = `scroll${EVENT_KEY}`
const EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`
const CLASS_NAME_DROPDOWN_ITEM = 'dropdown-item'
const CLASS_NAME_ACTIVE = 'active'
const SELECTOR_DATA_SPY = '[data-spy="scroll"]'
const SELECTOR_NAV_LIST_GROUP = '.nav, .list-group'
const SELECTOR_NAV_LINKS = '.nav-link'
const SELECTOR_NAV_ITEMS = '.nav-item'
const SELECTOR_LIST_ITEMS = '.list-group-item'
const SELECTOR_DROPDOWN = '.dropdown'
const SELECTOR_DROPDOWN_ITEMS = '.dropdown-item'
const SELECTOR_DROPDOWN_TOGGLE = '.dropdown-toggle'
const METHOD_OFFSET = 'offset'
const METHOD_POSITION = 'position'
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
class ScrollSpy {
constructor(element, config) {
this._element = element
this._scrollElement = element.tagName === 'BODY' ? window : element
this._config = this._getConfig(config)
this._selector = `${this._config.target} ${SELECTOR_NAV_LINKS},` +
`${this._config.target} ${SELECTOR_LIST_ITEMS},` +
`${this._config.target} ${SELECTOR_DROPDOWN_ITEMS}`
this._offsets = []
this._targets = []
this._activeTarget = null
this._scrollHeight = 0
$(this._scrollElement).on(EVENT_SCROLL, event => this._process(event))
this.refresh()
this._process()
}
// Getters
static get VERSION() {
return VERSION
}
static get Default() {
return Default
}
// Public
refresh() {
const autoMethod = this._scrollElement === this._scrollElement.window ?
METHOD_OFFSET : METHOD_POSITION
const offsetMethod = this._config.method === 'auto' ?
autoMethod : this._config.method
const offsetBase = offsetMethod === METHOD_POSITION ?
this._getScrollTop() : 0
this._offsets = []
this._targets = []
this._scrollHeight = this._getScrollHeight()
const targets = [].slice.call(document.querySelectorAll(this._selector))
targets
.map(element => {
let target
const targetSelector = Util.getSelectorFromElement(element)
if (targetSelector) {
target = document.querySelector(targetSelector)
}
if (target) {
const targetBCR = target.getBoundingClientRect()
if (targetBCR.width || targetBCR.height) {
// TODO (fat): remove sketch reliance on jQuery position/offset
return [
$(target)[offsetMethod]().top + offsetBase,
targetSelector
]
}
}
return null
})
.filter(item => item)
.sort((a, b) => a[0] - b[0])
.forEach(item => {
this._offsets.push(item[0])
this._targets.push(item[1])
})
}
dispose() {
$.removeData(this._element, DATA_KEY)
$(this._scrollElement).off(EVENT_KEY)
this._element = null
this._scrollElement = null
this._config = null
this._selector = null
this._offsets = null
this._targets = null
this._activeTarget = null
this._scrollHeight = null
}
// Private
_getConfig(config) {
config = {
...Default,
...(typeof config === 'object' && config ? config : {})
}
if (typeof config.target !== 'string' && Util.isElement(config.target)) {
let id = $(config.target).attr('id')
if (!id) {
id = Util.getUID(NAME)
$(config.target).attr('id', id)
}
config.target = `#${id}`
}
Util.typeCheckConfig(NAME, config, DefaultType)
return config
}
_getScrollTop() {
return this._scrollElement === window ?
this._scrollElement.pageYOffset : this._scrollElement.scrollTop
}
_getScrollHeight() {
return this._scrollElement.scrollHeight || Math.max(
document.body.scrollHeight,
document.documentElement.scrollHeight
)
}
_getOffsetHeight() {
return this._scrollElement === window ?
window.innerHeight : this._scrollElement.getBoundingClientRect().height
}
_process() {
const scrollTop = this._getScrollTop() + this._config.offset
const scrollHeight = this._getScrollHeight()
const maxScroll = this._config.offset + scrollHeight - this._getOffsetHeight()
if (this._scrollHeight !== scrollHeight) {
this.refresh()
}
if (scrollTop >= maxScroll) {
const target = this._targets[this._targets.length - 1]
if (this._activeTarget !== target) {
this._activate(target)
}
return
}
if (this._activeTarget && scrollTop < this._offsets[0] && this._offsets[0] > 0) {
this._activeTarget = null
this._clear()
return
}
for (let i = this._offsets.length; i--;) {
const isActiveTarget = this._activeTarget !== this._targets[i] &&
scrollTop >= this._offsets[i] &&
(typeof this._offsets[i + 1] === 'undefined' ||
scrollTop < this._offsets[i + 1])
if (isActiveTarget) {
this._activate(this._targets[i])
}
}
}
_activate(target) {
this._activeTarget = target
this._clear()
const queries = this._selector
.split(',')
.map(selector => `${selector}[data-target="${target}"],${selector}[href="${target}"]`)
const $link = $([].slice.call(document.querySelectorAll(queries.join(','))))
if ($link.hasClass(CLASS_NAME_DROPDOWN_ITEM)) {
$link.closest(SELECTOR_DROPDOWN)
.find(SELECTOR_DROPDOWN_TOGGLE)
.addClass(CLASS_NAME_ACTIVE)
$link.addClass(CLASS_NAME_ACTIVE)
} else {
// Set triggered link as active
$link.addClass(CLASS_NAME_ACTIVE)
// Set triggered links parents as active
// With both <ul> and <nav> markup a parent is the previous sibling of any nav ancestor
$link.parents(SELECTOR_NAV_LIST_GROUP)
.prev(`${SELECTOR_NAV_LINKS}, ${SELECTOR_LIST_ITEMS}`)
.addClass(CLASS_NAME_ACTIVE)
// Handle special case when .nav-link is inside .nav-item
$link.parents(SELECTOR_NAV_LIST_GROUP)
.prev(SELECTOR_NAV_ITEMS)
.children(SELECTOR_NAV_LINKS)
.addClass(CLASS_NAME_ACTIVE)
}
$(this._scrollElement).trigger(EVENT_ACTIVATE, {
relatedTarget: target
})
}
_clear() {
[].slice.call(document.querySelectorAll(this._selector))
.filter(node => node.classList.contains(CLASS_NAME_ACTIVE))
.forEach(node => node.classList.remove(CLASS_NAME_ACTIVE))
}
// Static
static _jQueryInterface(config) {
return this.each(function () {
let data = $(this).data(DATA_KEY)
const _config = typeof config === 'object' && config
if (!data) {
data = new ScrollSpy(this, _config)
$(this).data(DATA_KEY, data)
}
if (typeof config === 'string') {
if (typeof data[config] === 'undefined') {
throw new TypeError(`No method named "${config}"`)
}
data[config]()
}
})
}
}
/**
* ------------------------------------------------------------------------
* Data Api implementation
* ------------------------------------------------------------------------
*/
$(window).on(EVENT_LOAD_DATA_API, () => {
const scrollSpys = [].slice.call(document.querySelectorAll(SELECTOR_DATA_SPY))
const scrollSpysLength = scrollSpys.length
for (let i = scrollSpysLength; i--;) {
const $spy = $(scrollSpys[i])
ScrollSpy._jQueryInterface.call($spy, $spy.data())
}
})
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = ScrollSpy._jQueryInterface
$.fn[NAME].Constructor = ScrollSpy
$.fn[NAME].noConflict = () => {
$.fn[NAME] = JQUERY_NO_CONFLICT
return ScrollSpy._jQueryInterface
}
export default ScrollSpy

255
amd/src/bootstrap/tab.js

@ -0,0 +1,255 @@
/**
* --------------------------------------------------------------------------
* Bootstrap (v4.6.0): tab.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
import $ from 'jquery'
import Util from './util'
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
const NAME = 'tab'
const VERSION = '4.6.0'
const DATA_KEY = 'bs.tab'
const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api'
const JQUERY_NO_CONFLICT = $.fn[NAME]
const EVENT_HIDE = `hide${EVENT_KEY}`
const EVENT_HIDDEN = `hidden${EVENT_KEY}`
const EVENT_SHOW = `show${EVENT_KEY}`
const EVENT_SHOWN = `shown${EVENT_KEY}`
const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`
const CLASS_NAME_DROPDOWN_MENU = 'dropdown-menu'
const CLASS_NAME_ACTIVE = 'active'
const CLASS_NAME_DISABLED = 'disabled'
const CLASS_NAME_FADE = 'fade'
const CLASS_NAME_SHOW = 'show'
const SELECTOR_DROPDOWN = '.dropdown'
const SELECTOR_NAV_LIST_GROUP = '.nav, .list-group'
const SELECTOR_ACTIVE = '.active'
const SELECTOR_ACTIVE_UL = '> li > .active'
const SELECTOR_DATA_TOGGLE = '[data-toggle="tab"], [data-toggle="pill"], [data-toggle="list"]'
const SELECTOR_DROPDOWN_TOGGLE = '.dropdown-toggle'
const SELECTOR_DROPDOWN_ACTIVE_CHILD = '> .dropdown-menu .active'
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
class Tab {
constructor(element) {
this._element = element
}
// Getters
static get VERSION() {
return VERSION
}
// Public
show() {
if (this._element.parentNode &&
this._element.parentNode.nodeType === Node.ELEMENT_NODE &&
$(this._element).hasClass(CLASS_NAME_ACTIVE) ||
$(this._element).hasClass(CLASS_NAME_DISABLED)) {
return
}
let target
let previous
const listElement = $(this._element).closest(SELECTOR_NAV_LIST_GROUP)[0]
const selector = Util.getSelectorFromElement(this._element)
if (listElement) {
const itemSelector = listElement.nodeName === 'UL' || listElement.nodeName === 'OL' ? SELECTOR_ACTIVE_UL : SELECTOR_ACTIVE
previous = $.makeArray($(listElement).find(itemSelector))
previous = previous[previous.length - 1]
}
const hideEvent = $.Event(EVENT_HIDE, {
relatedTarget: this._element
})
const showEvent = $.Event(EVENT_SHOW, {
relatedTarget: previous
})
if (previous) {
$(previous).trigger(hideEvent)
}
$(this._element).trigger(showEvent)
if (showEvent.isDefaultPrevented() ||
hideEvent.isDefaultPrevented()) {
return
}
if (selector) {
target = document.querySelector(selector)
}
this._activate(
this._element,
listElement
)
const complete = () => {
const hiddenEvent = $.Event(EVENT_HIDDEN, {
relatedTarget: this._element
})
const shownEvent = $.Event(EVENT_SHOWN, {
relatedTarget: previous
})
$(previous).trigger(hiddenEvent)
$(this._element).trigger(shownEvent)
}
if (target) {
this._activate(target, target.parentNode, complete)
} else {
complete()
}
}
dispose() {
$.removeData(this._element, DATA_KEY)
this._element = null
}
// Private
_activate(element, container, callback) {
const activeElements = container && (container.nodeName === 'UL' || container.nodeName === 'OL') ?
$(container).find(SELECTOR_ACTIVE_UL) :
$(container).children(SELECTOR_ACTIVE)
const active = activeElements[0]
const isTransitioning = callback && (active && $(active).hasClass(CLASS_NAME_FADE))
const complete = () => this._transitionComplete(
element,
active,
callback
)
if (active && isTransitioning) {
const transitionDuration = Util.getTransitionDurationFromElement(active)
$(active)
.removeClass(CLASS_NAME_SHOW)
.one(Util.TRANSITION_END, complete)
.emulateTransitionEnd(transitionDuration)
} else {
complete()
}
}
_transitionComplete(element, active, callback) {
if (active) {
$(active).removeClass(CLASS_NAME_ACTIVE)
const dropdownChild = $(active.parentNode).find(
SELECTOR_DROPDOWN_ACTIVE_CHILD
)[0]
if (dropdownChild) {
$(dropdownChild).removeClass(CLASS_NAME_ACTIVE)
}
if (active.getAttribute('role') === 'tab') {
active.setAttribute('aria-selected', false)
}
}
$(element).addClass(CLASS_NAME_ACTIVE)
if (element.getAttribute('role') === 'tab') {
element.setAttribute('aria-selected', true)
}
Util.reflow(element)
if (element.classList.contains(CLASS_NAME_FADE)) {
element.classList.add(CLASS_NAME_SHOW)
}
if (element.parentNode && $(element.parentNode).hasClass(CLASS_NAME_DROPDOWN_MENU)) {
const dropdownElement = $(element).closest(SELECTOR_DROPDOWN)[0]
if (dropdownElement) {
const dropdownToggleList = [].slice.call(dropdownElement.querySelectorAll(SELECTOR_DROPDOWN_TOGGLE))
$(dropdownToggleList).addClass(CLASS_NAME_ACTIVE)
}
element.setAttribute('aria-expanded', true)
}
if (callback) {
callback()
}
}
// Static
static _jQueryInterface(config) {
return this.each(function () {
const $this = $(this)
let data = $this.data(DATA_KEY)
if (!data) {
data = new Tab(this)
$this.data(DATA_KEY, data)
}
if (typeof config === 'string') {
if (typeof data[config] === 'undefined') {
throw new TypeError(`No method named "${config}"`)
}
data[config]()
}
})
}
}
/**
* ------------------------------------------------------------------------
* Data Api implementation
* ------------------------------------------------------------------------
*/
$(document)
.on(EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
event.preventDefault()
Tab._jQueryInterface.call($(this), 'show')
})
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = Tab._jQueryInterface
$.fn[NAME].Constructor = Tab
$.fn[NAME].noConflict = () => {
$.fn[NAME] = JQUERY_NO_CONFLICT
return Tab._jQueryInterface
}
export default Tab

230
amd/src/bootstrap/toast.js

@ -0,0 +1,230 @@
/**
* --------------------------------------------------------------------------
* Bootstrap (v4.6.0): toast.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
import $ from 'jquery'
import Util from './util'
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
const NAME = 'toast'
const VERSION = '4.6.0'
const DATA_KEY = 'bs.toast'
const EVENT_KEY = `.${DATA_KEY}`
const JQUERY_NO_CONFLICT = $.fn[NAME]
const EVENT_CLICK_DISMISS = `click.dismiss${EVENT_KEY}`
const EVENT_HIDE = `hide${EVENT_KEY}`
const EVENT_HIDDEN = `hidden${EVENT_KEY}`
const EVENT_SHOW = `show${EVENT_KEY}`
const EVENT_SHOWN = `shown${EVENT_KEY}`
const CLASS_NAME_FADE = 'fade'
const CLASS_NAME_HIDE = 'hide'
const CLASS_NAME_SHOW = 'show'
const CLASS_NAME_SHOWING = 'showing'
const DefaultType = {
animation: 'boolean',
autohide: 'boolean',
delay: 'number'
}
const Default = {
animation: true,
autohide: true,
delay: 500
}
const SELECTOR_DATA_DISMISS = '[data-dismiss="toast"]'
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
class Toast {
constructor(element, config) {
this._element = element
this._config = this._getConfig(config)
this._timeout = null
this._setListeners()
}
// Getters
static get VERSION() {
return VERSION
}
static get DefaultType() {
return DefaultType
}
static get Default() {
return Default
}
// Public
show() {
const showEvent = $.Event(EVENT_SHOW)
$(this._element).trigger(showEvent)
if (showEvent.isDefaultPrevented()) {
return
}
this._clearTimeout()
if (this._config.animation) {
this._element.classList.add(CLASS_NAME_FADE)
}
const complete = () => {
this._element.classList.remove(CLASS_NAME_SHOWING)
this._element.classList.add(CLASS_NAME_SHOW)
$(this._element).trigger(EVENT_SHOWN)
if (this._config.autohide) {
this._timeout = setTimeout(() => {
this.hide()
}, this._config.delay)
}
}
this._element.classList.remove(CLASS_NAME_HIDE)
Util.reflow(this._element)
this._element.classList.add(CLASS_NAME_SHOWING)
if (this._config.animation) {
const transitionDuration = Util.getTransitionDurationFromElement(this._element)
$(this._element)
.one(Util.TRANSITION_END, complete)
.emulateTransitionEnd(transitionDuration)
} else {
complete()
}
}
hide() {
if (!this._element.classList.contains(CLASS_NAME_SHOW)) {
return
}
const hideEvent = $.Event(EVENT_HIDE)
$(this._element).trigger(hideEvent)
if (hideEvent.isDefaultPrevented()) {
return
}
this._close()
}
dispose() {
this._clearTimeout()
if (this._element.classList.contains(CLASS_NAME_SHOW)) {
this._element.classList.remove(CLASS_NAME_SHOW)
}
$(this._element).off(EVENT_CLICK_DISMISS)
$.removeData(this._element, DATA_KEY)
this._element = null
this._config = null
}
// Private
_getConfig(config) {
config = {
...Default,
...$(this._element).data(),
...(typeof config === 'object' && config ? config : {})
}
Util.typeCheckConfig(
NAME,
config,
this.constructor.DefaultType
)
return config
}
_setListeners() {
$(this._element).on(EVENT_CLICK_DISMISS, SELECTOR_DATA_DISMISS, () => this.hide())
}
_close() {
const complete = () => {
this._element.classList.add(CLASS_NAME_HIDE)
$(this._element).trigger(EVENT_HIDDEN)
}
this._element.classList.remove(CLASS_NAME_SHOW)
if (this._config.animation) {
const transitionDuration = Util.getTransitionDurationFromElement(this._element)
$(this._element)
.one(Util.TRANSITION_END, complete)
.emulateTransitionEnd(transitionDuration)
} else {
complete()
}
}
_clearTimeout() {
clearTimeout(this._timeout)
this._timeout = null
}
// Static
static _jQueryInterface(config) {
return this.each(function () {
const $element = $(this)
let data = $element.data(DATA_KEY)
const _config = typeof config === 'object' && config
if (!data) {
data = new Toast(this, _config)
$element.data(DATA_KEY, data)
}
if (typeof config === 'string') {
if (typeof data[config] === 'undefined') {
throw new TypeError(`No method named "${config}"`)
}
data[config](this)
}
})
}
}
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = Toast._jQueryInterface
$.fn[NAME].Constructor = Toast
$.fn[NAME].noConflict = () => {
$.fn[NAME] = JQUERY_NO_CONFLICT
return Toast._jQueryInterface
}
export default Toast

127
amd/src/bootstrap/tools/sanitizer.js

@ -0,0 +1,127 @@
/**
* --------------------------------------------------------------------------
* Bootstrap (v4.6.0): tools/sanitizer.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
const uriAttrs = [
'background',
'cite',
'href',
'itemtype',
'longdesc',
'poster',
'src',
'xlink:href'
]
const ARIA_ATTRIBUTE_PATTERN = /^aria-[\w-]*$/i
export const DefaultWhitelist = {
// Global attributes allowed on any supplied element below.
'*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN],
a: ['target', 'href', 'title', 'rel'],
area: [],
b: [],
br: [],
col: [],
code: [],
div: [],
em: [],
hr: [],
h1: [],
h2: [],
h3: [],
h4: [],
h5: [],
h6: [],
i: [],
img: ['src', 'srcset', 'alt', 'title', 'width', 'height'],
li: [],
ol: [],
p: [],
pre: [],
s: [],
small: [],
span: [],
sub: [],
sup: [],
strong: [],
u: [],
ul: []
}
/**
* A pattern that recognizes a commonly useful subset of URLs that are safe.
*
* Shoutout to Angular 7 https://github.com/angular/angular/blob/7.2.4/packages/core/src/sanitization/url_sanitizer.ts
*/
const SAFE_URL_PATTERN = /^(?:(?:https?|mailto|ftp|tel|file):|[^#&/:?]*(?:[#/?]|$))/gi
/**
* A pattern that matches safe data URLs. Only matches image, video and audio types.
*
* Shoutout to Angular 7 https://github.com/angular/angular/blob/7.2.4/packages/core/src/sanitization/url_sanitizer.ts
*/
const DATA_URL_PATTERN = /^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[\d+/a-z]+=*$/i
function allowedAttribute(attr, allowedAttributeList) {
const attrName = attr.nodeName.toLowerCase()
if (allowedAttributeList.indexOf(attrName) !== -1) {
if (uriAttrs.indexOf(attrName) !== -1) {
return Boolean(attr.nodeValue.match(SAFE_URL_PATTERN) || attr.nodeValue.match(DATA_URL_PATTERN))
}
return true
}
const regExp = allowedAttributeList.filter(attrRegex => attrRegex instanceof RegExp)
// Check if a regular expression validates the attribute.
for (let i = 0, len = regExp.length; i < len; i++) {
if (attrName.match(regExp[i])) {
return true
}
}
return false
}
export function sanitizeHtml(unsafeHtml, whiteList, sanitizeFn) {
if (unsafeHtml.length === 0) {
return unsafeHtml
}
if (sanitizeFn && typeof sanitizeFn === 'function') {
return sanitizeFn(unsafeHtml)
}
const domParser = new window.DOMParser()
const createdDocument = domParser.parseFromString(unsafeHtml, 'text/html')
const whitelistKeys = Object.keys(whiteList)
const elements = [].slice.call(createdDocument.body.querySelectorAll('*'))
for (let i = 0, len = elements.length; i < len; i++) {
const el = elements[i]
const elName = el.nodeName.toLowerCase()
if (whitelistKeys.indexOf(el.nodeName.toLowerCase()) === -1) {
el.parentNode.removeChild(el)
continue
}
const attributeList = [].slice.call(el.attributes)
const whitelistedAttributes = [].concat(whiteList['*'] || [], whiteList[elName] || [])
attributeList.forEach(attr => {
if (!allowedAttribute(attr, whitelistedAttributes)) {
el.removeAttribute(attr.nodeName)
}
})
}
return createdDocument.body.innerHTML
}

778
amd/src/bootstrap/tooltip.js

@ -0,0 +1,778 @@
/**
* --------------------------------------------------------------------------
* Bootstrap (v4.6.0): tooltip.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
import {
DefaultWhitelist,
sanitizeHtml
} from './tools/sanitizer'
import $ from 'jquery'
import Popper from 'core/popper'
import Util from './util'
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
const NAME = 'tooltip'
const VERSION = '4.6.0'
const DATA_KEY = 'bs.tooltip'
const EVENT_KEY = `.${DATA_KEY}`
const JQUERY_NO_CONFLICT = $.fn[NAME]
const CLASS_PREFIX = 'bs-tooltip'
const BSCLS_PREFIX_REGEX = new RegExp(`(^|\\s)${CLASS_PREFIX}\\S+`, 'g')
const DISALLOWED_ATTRIBUTES = ['sanitize', 'whiteList', 'sanitizeFn']
const DefaultType = {
animation: 'boolean',
template: 'string',
title: '(string|element|function)',
trigger: 'string',
delay: '(number|object)',
html: 'boolean',
selector: '(string|boolean)',
placement: '(string|function)',
offset: '(number|string|function)',
container: '(string|element|boolean)',
fallbackPlacement: '(string|array)',
boundary: '(string|element)',
customClass: '(string|function)',
sanitize: 'boolean',
sanitizeFn: '(null|function)',
whiteList: 'object',
popperConfig: '(null|object)'
}
const AttachmentMap = {
AUTO: 'auto',
TOP: 'top',
RIGHT: 'right',
BOTTOM: 'bottom',
LEFT: 'left'
}
const Default = {
animation: true,
template: '<div class="tooltip" role="tooltip">' +
'<div class="arrow"></div>' +
'<div class="tooltip-inner"></div></div>',
trigger: 'hover focus',
title: '',
delay: 0,
html: false,
selector: false,
placement: 'top',
offset: 0,
container: false,
fallbackPlacement: 'flip',
boundary: 'scrollParent',
customClass: '',
sanitize: true,
sanitizeFn: null,
whiteList: DefaultWhitelist,
popperConfig: null
}
const HOVER_STATE_SHOW = 'show'
const HOVER_STATE_OUT = 'out'
const Event = {
HIDE: `hide${EVENT_KEY}`,
HIDDEN: `hidden${EVENT_KEY}`,
SHOW: `show${EVENT_KEY}`,
SHOWN: `shown${EVENT_KEY}`,
INSERTED: `inserted${EVENT_KEY}`,
CLICK: `click${EVENT_KEY}`,
FOCUSIN: `focusin${EVENT_KEY}`,
FOCUSOUT: `focusout${EVENT_KEY}`,
MOUSEENTER: `mouseenter${EVENT_KEY}`,
MOUSELEAVE: `mouseleave${EVENT_KEY}`
}
const CLASS_NAME_FADE = 'fade'
const CLASS_NAME_SHOW = 'show'
const SELECTOR_TOOLTIP_INNER = '.tooltip-inner'
const SELECTOR_ARROW = '.arrow'
const TRIGGER_HOVER = 'hover'
const TRIGGER_FOCUS = 'focus'
const TRIGGER_CLICK = 'click'
const TRIGGER_MANUAL = 'manual'
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
class Tooltip {
constructor(element, config) {
if (typeof Popper === 'undefined') {
throw new TypeError('Bootstrap\'s tooltips require Popper (https://popper.js.org)')
}
// private
this._isEnabled = true
this._timeout = 0
this._hoverState = ''
this._activeTrigger = {}
this._popper = null
// Protected
this.element = element
this.config = this._getConfig(config)
this.tip = null
this._setListeners()
}
// Getters
static get VERSION() {
return VERSION
}
static get Default() {
return Default
}
static get NAME() {
return NAME
}
static get DATA_KEY() {
return DATA_KEY
}
static get Event() {
return Event
}
static get EVENT_KEY() {
return EVENT_KEY
}
static get DefaultType() {
return DefaultType
}
// Public
enable() {
this._isEnabled = true
}
disable() {
this._isEnabled = false
}
toggleEnabled() {
this._isEnabled = !this._isEnabled
}
toggle(event) {
if (!this._isEnabled) {
return
}
if (event) {
const dataKey = this.constructor.DATA_KEY
let context = $(event.currentTarget).data(dataKey)
if (!context) {
context = new this.constructor(
event.currentTarget,
this._getDelegateConfig()
)
$(event.currentTarget).data(dataKey, context)
}
context._activeTrigger.click = !context._activeTrigger.click
if (context._isWithActiveTrigger()) {
context._enter(null, context)
} else {
context._leave(null, context)
}
} else {
if ($(this.getTipElement()).hasClass(CLASS_NAME_SHOW)) {
this._leave(null, this)
return
}
this._enter(null, this)
}
}
dispose() {
clearTimeout(this._timeout)
$.removeData(this.element, this.constructor.DATA_KEY)
$(this.element).off(this.constructor.EVENT_KEY)
$(this.element).closest('.modal').off('hide.bs.modal', this._hideModalHandler)
if (this.tip) {
$(this.tip).remove()
}
this._isEnabled = null
this._timeout = null
this._hoverState = null
this._activeTrigger = null
if (this._popper) {
this._popper.destroy()
}
this._popper = null
this.element = null
this.config = null
this.tip = null
}
show() {
if ($(this.element).css('display') === 'none') {
throw new Error('Please use show on visible elements')
}
const showEvent = $.Event(this.constructor.Event.SHOW)
if (this.isWithContent() && this._isEnabled) {
$(this.element).trigger(showEvent)
const shadowRoot = Util.findShadowRoot(this.element)
const isInTheDom = $.contains(
shadowRoot !== null ? shadowRoot : this.element.ownerDocument.documentElement,
this.element
)
if (showEvent.isDefaultPrevented() || !isInTheDom) {
return
}
const tip = this.getTipElement()
const tipId = Util.getUID(this.constructor.NAME)
tip.setAttribute('id', tipId)
this.element.setAttribute('aria-describedby', tipId)
this.setContent()
if (this.config.animation) {
$(tip).addClass(CLASS_NAME_FADE)
}
const placement = typeof this.config.placement === 'function' ?
this.config.placement.call(this, tip, this.element) :
this.config.placement
const attachment = this._getAttachment(placement)
this.addAttachmentClass(attachment)
const container = this._getContainer()
$(tip).data(this.constructor.DATA_KEY, this)
if (!$.contains(this.element.ownerDocument.documentElement, this.tip)) {
$(tip).appendTo(container)
}
$(this.element).trigger(this.constructor.Event.INSERTED)
this._popper = new Popper(this.element, tip, this._getPopperConfig(attachment))
$(tip).addClass(CLASS_NAME_SHOW)
$(tip).addClass(this.config.customClass)
// If this is a touch-enabled device we add extra
// empty mouseover listeners to the body's immediate children;
// only needed because of broken event delegation on iOS
// https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
if ('ontouchstart' in document.documentElement) {
$(document.body).children().on('mouseover', null, $.noop)
}
const complete = () => {
if (this.config.animation) {
this._fixTransition()
}
const prevHoverState = this._hoverState
this._hoverState = null
$(this.element).trigger(this.constructor.Event.SHOWN)
if (prevHoverState === HOVER_STATE_OUT) {
this._leave(null, this)
}
}
if ($(this.tip).hasClass(CLASS_NAME_FADE)) {
const transitionDuration = Util.getTransitionDurationFromElement(this.tip)
$(this.tip)
.one(Util.TRANSITION_END, complete)
.emulateTransitionEnd(transitionDuration)
} else {
complete()
}
}
}
hide(callback) {
const tip = this.getTipElement()
const hideEvent = $.Event(this.constructor.Event.HIDE)
const complete = () => {
if (this._hoverState !== HOVER_STATE_SHOW && tip.parentNode) {
tip.parentNode.removeChild(tip)
}
this._cleanTipClass()
this.element.removeAttribute('aria-describedby')
$(this.element).trigger(this.constructor.Event.HIDDEN)
if (this._popper !== null) {
this._popper.destroy()
}
if (callback) {
callback()
}
}
$(this.element).trigger(hideEvent)
if (hideEvent.isDefaultPrevented()) {
return
}
$(tip).removeClass(CLASS_NAME_SHOW)
// If this is a touch-enabled device we remove the extra
// empty mouseover listeners we added for iOS support
if ('ontouchstart' in document.documentElement) {
$(document.body).children().off('mouseover', null, $.noop)
}
this._activeTrigger[TRIGGER_CLICK] = false
this._activeTrigger[TRIGGER_FOCUS] = false
this._activeTrigger[TRIGGER_HOVER] = false
if ($(this.tip).hasClass(CLASS_NAME_FADE)) {
const transitionDuration = Util.getTransitionDurationFromElement(tip)
$(tip)
.one(Util.TRANSITION_END, complete)
.emulateTransitionEnd(transitionDuration)
} else {
complete()
}
this._hoverState = ''
}
update() {
if (this._popper !== null) {
this._popper.scheduleUpdate()
}
}
// Protected
isWithContent() {
return Boolean(this.getTitle())
}
addAttachmentClass(attachment) {
$(this.getTipElement()).addClass(`${CLASS_PREFIX}-${attachment}`)
}
getTipElement() {
this.tip = this.tip || $(this.config.template)[0]
return this.tip
}
setContent() {
const tip = this.getTipElement()
this.setElementContent($(tip.querySelectorAll(SELECTOR_TOOLTIP_INNER)), this.getTitle())
$(tip).removeClass(`${CLASS_NAME_FADE} ${CLASS_NAME_SHOW}`)
}
setElementContent($element, content) {
if (typeof content === 'object' && (content.nodeType || content.jquery)) {
// Content is a DOM node or a jQuery
if (this.config.html) {
if (!$(content).parent().is($element)) {
$element.empty().append(content)
}
} else {
$element.text($(content).text())
}
return
}
if (this.config.html) {
if (this.config.sanitize) {
content = sanitizeHtml(content, this.config.whiteList, this.config.sanitizeFn)
}
$element.html(content)
} else {
$element.text(content)
}
}
getTitle() {
let title = this.element.getAttribute('data-original-title')
if (!title) {
title = typeof this.config.title === 'function' ?
this.config.title.call(this.element) :
this.config.title
}
return title
}
// Private
_getPopperConfig(attachment) {
const defaultBsConfig = {
placement: attachment,
modifiers: {
offset: this._getOffset(),
flip: {
behavior: this.config.fallbackPlacement
},
arrow: {
element: SELECTOR_ARROW
},
preventOverflow: {
boundariesElement: this.config.boundary
}
},
onCreate: data => {
if (data.originalPlacement !== data.placement) {
this._handlePopperPlacementChange(data)
}
},
onUpdate: data => this._handlePopperPlacementChange(data)
}
return {
...defaultBsConfig,
...this.config.popperConfig
}
}
_getOffset() {
const offset = {}
if (typeof this.config.offset === 'function') {
offset.fn = data => {
data.offsets = {
...data.offsets,
...(this.config.offset(data.offsets, this.element) || {})
}
return data
}
} else {
offset.offset = this.config.offset
}
return offset
}
_getContainer() {
if (this.config.container === false) {
return document.body
}
if (Util.isElement(this.config.container)) {
return $(this.config.container)
}
return $(document).find(this.config.container)
}
_getAttachment(placement) {
return AttachmentMap[placement.toUpperCase()]
}
_setListeners() {
const triggers = this.config.trigger.split(' ')
triggers.forEach(trigger => {
if (trigger === 'click') {
$(this.element).on(
this.constructor.Event.CLICK,
this.config.selector,
event => this.toggle(event)
)
} else if (trigger !== TRIGGER_MANUAL) {
const eventIn = trigger === TRIGGER_HOVER ?
this.constructor.Event.MOUSEENTER :
this.constructor.Event.FOCUSIN
const eventOut = trigger === TRIGGER_HOVER ?
this.constructor.Event.MOUSELEAVE :
this.constructor.Event.FOCUSOUT
$(this.element)
.on(eventIn, this.config.selector, event => this._enter(event))
.on(eventOut, this.config.selector, event => this._leave(event))
}
})
this._hideModalHandler = () => {
if (this.element) {
this.hide()
}
}
$(this.element).closest('.modal').on('hide.bs.modal', this._hideModalHandler)
if (this.config.selector) {
this.config = {
...this.config,
trigger: 'manual',
selector: ''
}
} else {
this._fixTitle()
}
}
_fixTitle() {
const titleType = typeof this.element.getAttribute('data-original-title')
if (this.element.getAttribute('title') || titleType !== 'string') {
this.element.setAttribute(
'data-original-title',
this.element.getAttribute('title') || ''
)
this.element.setAttribute('title', '')
}
}
_enter(event, context) {
const dataKey = this.constructor.DATA_KEY
context = context || $(event.currentTarget).data(dataKey)
if (!context) {
context = new this.constructor(
event.currentTarget,
this._getDelegateConfig()
)
$(event.currentTarget).data(dataKey, context)
}
if (event) {
context._activeTrigger[
event.type === 'focusin' ? TRIGGER_FOCUS : TRIGGER_HOVER
] = true
}
if ($(context.getTipElement()).hasClass(CLASS_NAME_SHOW) || context._hoverState === HOVER_STATE_SHOW) {
context._hoverState = HOVER_STATE_SHOW
return
}
clearTimeout(context._timeout)
context._hoverState = HOVER_STATE_SHOW
if (!context.config.delay || !context.config.delay.show) {
context.show()
return
}
context._timeout = setTimeout(() => {
if (context._hoverState === HOVER_STATE_SHOW) {
context.show()
}
}, context.config.delay.show)
}
_leave(event, context) {
const dataKey = this.constructor.DATA_KEY
context = context || $(event.currentTarget).data(dataKey)
if (!context) {
context = new this.constructor(
event.currentTarget,
this._getDelegateConfig()
)
$(event.currentTarget).data(dataKey, context)
}
if (event) {
context._activeTrigger[
event.type === 'focusout' ? TRIGGER_FOCUS : TRIGGER_HOVER
] = false
}
if (context._isWithActiveTrigger()) {
return
}
clearTimeout(context._timeout)
context._hoverState = HOVER_STATE_OUT
if (!context.config.delay || !context.config.delay.hide) {
context.hide()
return
}
context._timeout = setTimeout(() => {
if (context._hoverState === HOVER_STATE_OUT) {
context.hide()
}
}, context.config.delay.hide)
}
_isWithActiveTrigger() {
for (const trigger in this._activeTrigger) {
if (this._activeTrigger[trigger]) {
return true
}
}
return false
}
_getConfig(config) {
const dataAttributes = $(this.element).data()
Object.keys(dataAttributes)
.forEach(dataAttr => {
if (DISALLOWED_ATTRIBUTES.indexOf(dataAttr) !== -1) {
delete dataAttributes[dataAttr]
}
})
config = {
...this.constructor.Default,
...dataAttributes,
...(typeof config === 'object' && config ? config : {})
}
if (typeof config.delay === 'number') {
config.delay = {
show: config.delay,
hide: config.delay
}
}
if (typeof config.title === 'number') {
config.title = config.title.toString()
}
if (typeof config.content === 'number') {
config.content = config.content.toString()
}
Util.typeCheckConfig(
NAME,
config,
this.constructor.DefaultType
)
if (config.sanitize) {
config.template = sanitizeHtml(config.template, config.whiteList, config.sanitizeFn)
}
return config
}
_getDelegateConfig() {
const config = {}
if (this.config) {
for (const key in this.config) {
if (this.constructor.Default[key] !== this.config[key]) {
config[key] = this.config[key]
}
}
}
return config
}
_cleanTipClass() {
const $tip = $(this.getTipElement())
const tabClass = $tip.attr('class').match(BSCLS_PREFIX_REGEX)
if (tabClass !== null && tabClass.length) {
$tip.removeClass(tabClass.join(''))
}
}
_handlePopperPlacementChange(popperData) {
this.tip = popperData.instance.popper
this._cleanTipClass()
this.addAttachmentClass(this._getAttachment(popperData.placement))
}
_fixTransition() {
const tip = this.getTipElement()
const initConfigAnimation = this.config.animation
if (tip.getAttribute('x-placement') !== null) {
return
}
$(tip).removeClass(CLASS_NAME_FADE)
this.config.animation = false
this.hide()
this.show()
this.config.animation = initConfigAnimation
}
// Static
static _jQueryInterface(config) {
return this.each(function () {
const $element = $(this)
let data = $element.data(DATA_KEY)
const _config = typeof config === 'object' && config
if (!data && /dispose|hide/.test(config)) {
return
}
if (!data) {
data = new Tooltip(this, _config)
$element.data(DATA_KEY, data)
}
if (typeof config === 'string') {
if (typeof data[config] === 'undefined') {
throw new TypeError(`No method named "${config}"`)
}
data[config]()
}
})
}
}
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = Tooltip._jQueryInterface
$.fn[NAME].Constructor = Tooltip
$.fn[NAME].noConflict = () => {
$.fn[NAME] = JQUERY_NO_CONFLICT
return Tooltip._jQueryInterface
}
export default Tooltip

198
amd/src/bootstrap/util.js

@ -0,0 +1,198 @@
/**
* --------------------------------------------------------------------------
* Bootstrap (v4.6.0): util.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
import $ from 'jquery'
/**
* ------------------------------------------------------------------------
* Private TransitionEnd Helpers
* ------------------------------------------------------------------------
*/
const TRANSITION_END = 'transitionend'
const MAX_UID = 1000000
const MILLISECONDS_MULTIPLIER = 1000
// Shoutout AngusCroll (https://goo.gl/pxwQGp)
function toType(obj) {
if (obj === null || typeof obj === 'undefined') {
return `${obj}`
}
return {}.toString.call(obj).match(/\s([a-z]+)/i)[1].toLowerCase()
}
function getSpecialTransitionEndEvent() {
return {
bindType: TRANSITION_END,
delegateType: TRANSITION_END,
handle(event) {
if ($(event.target).is(this)) {
return event.handleObj.handler.apply(this, arguments) // eslint-disable-line prefer-rest-params
}
return undefined
}
}
}
function transitionEndEmulator(duration) {
let called = false
$(this).one(Util.TRANSITION_END, () => {
called = true
})
setTimeout(() => {
if (!called) {
Util.triggerTransitionEnd(this)
}
}, duration)
return this
}
function setTransitionEndSupport() {
$.fn.emulateTransitionEnd = transitionEndEmulator
$.event.special[Util.TRANSITION_END] = getSpecialTransitionEndEvent()
}
/**
* --------------------------------------------------------------------------
* Public Util Api
* --------------------------------------------------------------------------
*/
const Util = {
TRANSITION_END: 'bsTransitionEnd',
getUID(prefix) {
do {
prefix += ~~(Math.random() * MAX_UID) // "~~" acts like a faster Math.floor() here
} while (document.getElementById(prefix))
return prefix
},
getSelectorFromElement(element) {
let selector = element.getAttribute('data-target')
if (!selector || selector === '#') {
const hrefAttr = element.getAttribute('href')
selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : ''
}
try {
return document.querySelector(selector) ? selector : null
} catch (_) {
return null
}
},
getTransitionDurationFromElement(element) {
if (!element) {
return 0
}
// Get transition-duration of the element
let transitionDuration = $(element).css('transition-duration')
let transitionDelay = $(element).css('transition-delay')
const floatTransitionDuration = parseFloat(transitionDuration)
const floatTransitionDelay = parseFloat(transitionDelay)
// Return 0 if element or transition duration is not found
if (!floatTransitionDuration && !floatTransitionDelay) {
return 0
}
// If multiple durations are defined, take the first
transitionDuration = transitionDuration.split(',')[0]
transitionDelay = transitionDelay.split(',')[0]
return (parseFloat(transitionDuration) + parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER
},
reflow(element) {
return element.offsetHeight
},
triggerTransitionEnd(element) {
$(element).trigger(TRANSITION_END)
},
supportsTransitionEnd() {
return Boolean(TRANSITION_END)
},
isElement(obj) {
return (obj[0] || obj).nodeType
},
typeCheckConfig(componentName, config, configTypes) {
for (const property in configTypes) {
if (Object.prototype.hasOwnProperty.call(configTypes, property)) {
const expectedTypes = configTypes[property]
const value = config[property]
const valueType = value && Util.isElement(value) ?
'element' : toType(value)
if (!new RegExp(expectedTypes).test(valueType)) {
throw new Error(
`${componentName.toUpperCase()}: ` +
`Option "${property}" provided type "${valueType}" ` +
`but expected type "${expectedTypes}".`)
}
}
}
},
findShadowRoot(element) {
if (!document.documentElement.attachShadow) {
return null
}
// Can find the shadow root otherwise it'll return the document
if (typeof element.getRootNode === 'function') {
const root = element.getRootNode()
return root instanceof ShadowRoot ? root : null
}
if (element instanceof ShadowRoot) {
return element
}
// when we don't find a shadow root
if (!element.parentNode) {
return null
}
return Util.findShadowRoot(element.parentNode)
},
jQueryDetection() {
if (typeof $ === 'undefined') {
throw new TypeError('Bootstrap\'s JavaScript requires jQuery. jQuery must be included before Bootstrap\'s JavaScript.')
}
const version = $.fn.jquery.split(' ')[0].split('.')
const minMajor = 1
const ltMajor = 2
const minMinor = 9
const minPatch = 1
const maxMajor = 4
if (version[0] < ltMajor && version[1] < minMinor || version[0] === minMajor && version[1] === minMinor && version[2] < minPatch || version[0] >= maxMajor) {
throw new Error('Bootstrap\'s JavaScript requires at least jQuery v1.9.1 but less than v4.0.0')
}
}
}
Util.jQueryDetection()
setTransitionEndSupport()
export default Util

199
amd/src/drawer.js

@ -0,0 +1,199 @@
// 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/>.
/**
* Contain the logic for a drawer.
*
* @module theme_ilb/drawer
* @copyright 2016 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define(['jquery', 'core/custom_interaction_events', 'core/log', 'core/pubsub', 'core/aria'],
function($, CustomEvents, Log, PubSub, Aria) {
var SELECTORS = {
TOGGLE_REGION: '[data-region="drawer-toggle"]',
TOGGLE_ACTION: '[data-action="toggle-drawer"]',
TOGGLE_TARGET: 'aria-controls',
TOGGLE_SIDE: 'left',
BODY: 'body',
SECTION: '.list-group-item[href*="#section-"]',
DRAWER: '#nav-drawer'
};
var small = $(document).width() < 768;
/**
* Constructor for the Drawer.
*/
var Drawer = function() {
if (!$(SELECTORS.TOGGLE_REGION).length) {
Log.debug('Page is missing a drawer region');
}
if (!$(SELECTORS.TOGGLE_ACTION).length) {
Log.debug('Page is missing a drawer toggle link');
}
$(SELECTORS.TOGGLE_REGION).each(function(index, ele) {
var trigger = $(ele).find(SELECTORS.TOGGLE_ACTION);
var drawerid = trigger.attr('aria-controls');
var drawer = $(document.getElementById(drawerid));
var hidden = trigger.attr('aria-expanded') == 'false';
var side = trigger.attr('data-side');
var body = $(SELECTORS.BODY);
var preference = trigger.attr('data-preference');
if (small) {
M.util.set_user_preference(preference, 'false');
}
drawer.on('mousewheel DOMMouseScroll', this.preventPageScroll);
if (!hidden) {
body.addClass('drawer-open-' + side);
trigger.attr('aria-expanded', 'true');
} else {
trigger.attr('aria-expanded', 'false');
}
}.bind(this));
this.registerEventListeners();
if (small) {
this.closeAll();
}
};
Drawer.prototype.closeAll = function() {
$(SELECTORS.TOGGLE_REGION).each(function(index, ele) {
var trigger = $(ele).find(SELECTORS.TOGGLE_ACTION);
var side = trigger.attr('data-side');
var body = $(SELECTORS.BODY);
var drawerid = trigger.attr('aria-controls');
var drawer = $(document.getElementById(drawerid));
var preference = trigger.attr('data-preference');
trigger.attr('aria-expanded', 'false');
body.removeClass('drawer-open-' + side);
Aria.hide(drawer.get());
drawer.addClass('closed');
if (!small) {
M.util.set_user_preference(preference, 'false');
}
});
};
/**
* Open / close the blocks drawer.
*
* @method toggleDrawer
* @param {Event} e
*/
Drawer.prototype.toggleDrawer = function(e) {
var trigger = $(e.target).closest('[data-action=toggle-drawer]');
var drawerid = trigger.attr('aria-controls');
var drawer = $(document.getElementById(drawerid));
var body = $(SELECTORS.BODY);
var side = trigger.attr('data-side');
var preference = trigger.attr('data-preference');
if (small) {
M.util.set_user_preference(preference, 'false');
}
body.addClass('drawer-ease');
var open = trigger.attr('aria-expanded') == 'true';
if (!open) {
// Open.
trigger.attr('aria-expanded', 'true');
Aria.unhide(drawer.get());
drawer.focus();
body.addClass('drawer-open-' + side);
drawer.removeClass('closed');
if (!small) {
M.util.set_user_preference(preference, 'true');
}
} else {
// Close.
body.removeClass('drawer-open-' + side);
trigger.attr('aria-expanded', 'false');
drawer.addClass('closed').delay(500).queue(function() {
// Ensure that during the delay, the drawer wasn't re-opened.
if ($(this).hasClass('closed')) {
Aria.hide(this);
}
$(this).dequeue();
});
if (!small) {
M.util.set_user_preference(preference, 'false');
}
}
// Publish an event to tell everything that the drawer has been toggled.
// The drawer transitions closed so another event will fire once teh transition
// has completed.
PubSub.publish('nav-drawer-toggle-start', open);
};
/**
* Prevent the page from scrolling when the drawer is at max scroll.
*
* @method preventPageScroll
* @param {Event} e
*/
Drawer.prototype.preventPageScroll = function(e) {
var delta = e.wheelDelta || (e.originalEvent && e.originalEvent.wheelDelta) || -e.originalEvent.detail,
bottomOverflow = (this.scrollTop + $(this).outerHeight() - this.scrollHeight) >= 0,
topOverflow = this.scrollTop <= 0;
if ((delta < 0 && bottomOverflow) || (delta > 0 && topOverflow)) {
e.preventDefault();
}
};
/**
* Set up all of the event handling for the modal.
*
* @method registerEventListeners
*/
Drawer.prototype.registerEventListeners = function() {
$(SELECTORS.TOGGLE_ACTION).each(function(index, element) {
CustomEvents.define($(element), [CustomEvents.events.activate]);
$(element).on(CustomEvents.events.activate, function(e, data) {
this.toggleDrawer(data.originalEvent);
data.originalEvent.preventDefault();
}.bind(this));
}.bind(this));
$(SELECTORS.SECTION).click(function() {
if (small) {
this.closeAll();
}
}.bind(this));
// Publish an event to tell everything that the drawer completed the transition
// to either an open or closed state.
$(SELECTORS.DRAWER).on('webkitTransitionEnd msTransitionEnd transitionend', function(e) {
var drawer = $(e.target).closest(SELECTORS.DRAWER);
// Note: aria-hidden is either present, or absent. It should not be set to false.
var open = !!drawer.attr('aria-hidden');
PubSub.publish('nav-drawer-toggle-end', open);
});
};
return {
'init': function() {
return new Drawer();
}
};
});

114
amd/src/form-display-errors.js

@ -0,0 +1,114 @@
// 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/>.
/**
* Custom form error event handler to manipulate the bootstrap markup and show
* nicely styled errors in an mform.
*
* @module theme_ilb/form-display-errors
* @copyright 2016 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define(['jquery', 'core/event'], function($, Event) {
return {
enhance: function(elementid) {
var element = document.getElementById(elementid);
if (!element) {
// Some elements (e.g. static) don't have a form field.
// Hence there is no validation. So, no setup required here.
return;
}
$(element).on(Event.Events.FORM_FIELD_VALIDATION, function(event, msg) {
event.preventDefault();
var parent = $(element).closest('.form-group');
var feedback = parent.find('.form-control-feedback');
const feedbackId = feedback.attr('id');
// Get current aria-describedby value.
let describedBy = $(element).attr('aria-describedby');
if (typeof describedBy === "undefined") {
describedBy = '';
}
// Split aria-describedby attribute into an array of IDs if necessary.
let describedByIds = [];
if (describedBy.length) {
describedByIds = describedBy.split(" ");
}
// Find the the feedback container in the aria-describedby attribute.
const feedbackIndex = describedByIds.indexOf(feedbackId);
// Sometimes (atto) we have a hidden textarea backed by a real contenteditable div.
if (($(element).prop("tagName") == 'TEXTAREA') && parent.find('[contenteditable]')) {
element = parent.find('[contenteditable]');
}
if (msg !== '') {
parent.addClass('has-danger');
parent.data('client-validation-error', true);
$(element).addClass('is-invalid');
// Append the feedback ID to the aria-describedby attribute if it doesn't exist yet.
if (feedbackIndex === -1) {
describedByIds.push(feedbackId);
$(element).attr('aria-describedby', describedByIds.join(" "));
}
$(element).attr('aria-invalid', true);
feedback.attr('tabindex', 0);
feedback.html(msg);
// Only display and focus when the error was not already visible.
// This is so that, when tabbing around the form, you don't get stuck.
if (!feedback.is(':visible')) {
feedback.show();
feedback.focus();
}
} else {
if (parent.data('client-validation-error') === true) {
parent.removeClass('has-danger');
parent.data('client-validation-error', false);
$(element).removeClass('is-invalid');
// If the aria-describedby attribute contains the error container's ID, remove it.
if (feedbackIndex > -1) {
describedByIds.splice(feedbackIndex, 1);
}
// Check the remaining element IDs in the aria-describedby attribute.
if (describedByIds.length) {
// If there's at least one, combine them with a blank space and update the aria-describedby attribute.
describedBy = describedByIds.join(" ");
// Put back the new describedby attribute.
$(element).attr('aria-describedby', describedBy);
} else {
// If there's none, remove the aria-describedby attribute.
$(element).removeAttr('aria-describedby');
}
$(element).attr('aria-invalid', false);
feedback.hide();
}
}
});
var form = element.closest('form');
if (form && !('ilbFormErrorsEnhanced' in form.dataset)) {
form.addEventListener('submit', function() {
var visibleError = $('.form-control-feedback:visible');
if (visibleError.length) {
visibleError[0].focus();
}
});
form.dataset.ilbFormErrorsEnhanced = 1;
}
}
};
});

34
amd/src/index.js

@ -0,0 +1,34 @@
/**
* --------------------------------------------------------------------------
* Bootstrap (v4.6.0): index.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
import Alert from './bootstrap/alert';
import Button from './bootstrap/button';
import Carousel from './bootstrap/carousel';
import Collapse from './bootstrap/collapse';
import Dropdown from './bootstrap/dropdown';
import Modal from './bootstrap/modal';
import Popover from './bootstrap/popover';
import Scrollspy from './bootstrap/scrollspy';
import Tab from './bootstrap/tab';
import Toast from './bootstrap/toast';
import Tooltip from './bootstrap/tooltip';
import Util from './bootstrap/util';
export {
Util,
Alert,
Button,
Carousel,
Collapse,
Dropdown,
Modal,
Popover,
Scrollspy,
Tab,
Toast,
Tooltip
};

109
amd/src/loader.js

@ -0,0 +1,109 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Template renderer for Moodle. Load and render Moodle templates with Mustache.
*
* @module theme_ilb/loader
* @copyright 2015 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since 2.9
*/
import $ from 'jquery';
import * as Aria from './aria';
import Bootstrap from './index';
import Pending from 'core/pending';
import Scroll from './scroll';
import setupBootstrapPendingChecks from './pending';
/**
* Rember the last visited tabs.
*/
const rememberTabs = () => {
$('a[data-toggle="tab"]').on('shown.bs.tab', function(e) {
var hash = $(e.target).attr('href');
if (history.replaceState) {
history.replaceState(null, null, hash);
} else {
location.hash = hash;
}
});
const hash = window.location.hash;
if (hash) {
const tab = document.querySelector('.nav-link[href="' + hash + '"]');
if (tab) {
tab.click();
}
}
};
/**
* Enable all popovers
*
*/
const enablePopovers = () => {
$('body').popover({
container: 'body',
selector: '[data-toggle="popover"]',
trigger: 'focus',
});
document.addEventListener('keydown', e => {
if (e.key === 'Escape' && e.target.closest('[data-toggle="popover"]')) {
$(e.target).popover('hide');
}
});
};
/**
* Enable tooltips
*
*/
const enableTooltips = () => {
$('body').tooltip({
container: 'body',
selector: '[data-toggle="tooltip"]',
});
};
const pendingPromise = new Pending('theme_ilb/loader:init');
// Add pending promise event listeners to relevant Bootstrap custom events.
setupBootstrapPendingChecks();
// Setup Aria helpers for Bootstrap features.
Aria.init();
// Remember the last visited tabs.
rememberTabs();
// Enable all popovers.
enablePopovers();
// Enable all tooltips.
enableTooltips();
// Add scroll handling.
(new Scroll()).init();
// Disables flipping the dropdowns up and getting hidden behind the navbar.
$.fn.dropdown.Constructor.Default.flip = false;
pendingPromise.resolve();
export {
Bootstrap,
};

133
amd/src/pending.js

@ -0,0 +1,133 @@
// 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/>.
/**
* Add Pending JS checks to stock Bootstrap transitions.
*
* @module theme_ilb/pending
* @copyright 2019 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import jQuery from 'jquery';
const moduleTransitions = {
alert: [
// Alert.
{
start: 'close',
end: 'closed',
},
],
carousel: [
{
start: 'slide',
end: 'slid',
},
],
collapse: [
{
start: 'hide',
end: 'hidden',
},
{
start: 'show',
end: 'shown',
},
],
dropdown: [
{
start: 'hide',
end: 'hidden',
},
{
start: 'show',
end: 'shown',
},
],
modal: [
{
start: 'hide',
end: 'hidden',
},
{
start: 'show',
end: 'shown',
},
],
popover: [
{
start: 'hide',
end: 'hidden',
},
{
start: 'show',
end: 'shown',
},
],
tab: [
{
start: 'hide',
end: 'hidden',
},
{
start: 'show',
end: 'shown',
},
],
toast: [
{
start: 'hide',
end: 'hidden',
},
{
start: 'show',
end: 'shown',
},
],
tooltip: [
{
start: 'hide',
end: 'hidden',
},
{
start: 'show',
end: 'shown',
},
],
};
export default () => {
Object.entries(moduleTransitions).forEach(([key, pairs]) => {
pairs.forEach(pair => {
const eventStart = `${pair.start}.bs.${key}`;
const eventEnd = `${pair.end}.bs.${key}`;
jQuery(document.body).on(eventStart, e => {
M.util.js_pending(eventEnd);
jQuery(e.target).one(eventEnd, () => {
M.util.js_complete(eventEnd);
});
});
});
});
};

22
layout/login_nova_conta.php → amd/src/popover.js

@ -1,4 +1,3 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
@ -14,23 +13,16 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
defined('MOODLE_INTERNAL') || die();
/**
* A login page layout for the ilb theme.
* Backward compatibility file for the old popover.js
*
* @package theme_ilb
* @copyright 2016 Damyon Wiese
* @module theme_ilb/popover
* @copyright 2020 Bas Brands <bas@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$bodyattributes = $OUTPUT->body_attributes();
$templatecontext = [
'sitename' => format_string($SITE->shortname, true, ['context' => context_course::instance(SITEID), "escape" => false]),
'output' => $OUTPUT,
'bodyattributes' => $bodyattributes
];
echo $OUTPUT->render_from_template('theme_ilb/login_nova_conta', $templatecontext);
import Popover from './bootstrap/popover';
export {
Popover
};

72
amd/src/scroll.js

@ -0,0 +1,72 @@
// 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/>.
/**
* Manage user scroll in Moodle for future floating elements.
*
* @module theme_ilb/scroll
* @copyright 2020 Ferran Recio <ferran@moodle.org>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Moodle scroll handling. For now it just handle a "scrolled" class
* on the body tag but in the near future could handle more floating
* elements like option bars, docked elements or other active elements.
*
* @class theme_ilb/scroll
*/
export default class MoodleScroll {
/**
* Initialise the scroll monitoring.
*
* @method init
* @chainable
* @return {Object} this.
*/
init() {
this.scrollY = 0;
window.addEventListener("scroll", this.scrollHandler.bind(this));
return this;
}
/**
* Add special classes to body depending on scroll position.
*
* @method update
* @chainable
* @return {Integer} current scroll position.
*/
getScrollPosition() {
return window.pageYOffset || document.documentElement.scrollTop;
}
/**
* Add special classes to body depending on scroll position.
*
* @method update
* @chainable
*/
scrollHandler() {
const body = document.querySelector('body');
const scrollY = this.getScrollPosition();
if (scrollY >= window.innerHeight) {
body.classList.add('scrolled');
} else {
body.classList.remove('scrolled');
}
}
}

30
classes/output/gradereport_history_renderer.php → amd/src/toast.js

@ -1,4 +1,3 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
@ -15,30 +14,15 @@
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Overriden gradereport_history renderer.
* Backward compatibility file for the old toast.js
*
* @package theme_boost
* @copyright 2016 Frédéric Massart - FMCorz.net
* @module theme_ilb/toast
* @copyright 2020 Bas Brands <bas@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace theme_boost\output;
defined('MOODLE_INTERNAL') || die();
import Toast from './bootstrap/toast';
use gradereport_history\output\user_button;
/**
* Overriden gradereport_history renderer.
*
* @package theme_boost
* @copyright 2016 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class gradereport_history_renderer extends \gradereport_history\output\renderer {
public function render_user_button(user_button $button) {
$data = $button->export_for_template($this);
return $this->render_from_template('gradereport_history/user_button', $data);
}
}
export {
Toast
};

8
classes/admin_settingspage_tabs.php

@ -15,7 +15,7 @@
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* @package theme_boost
* @package theme_ilb
* @copyright 2016 Ryan Wyllie
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
@ -23,11 +23,11 @@
defined('MOODLE_INTERNAL') || die();
/**
* @package theme_boost
* @package theme_ilb
* @copyright 2016 Ryan Wyllie
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class theme_boost_admin_settingspage_tabs extends admin_settingpage {
class theme_ilb_admin_settingspage_tabs extends admin_settingpage {
/** @var The tabs */
protected $tabs = array();
@ -93,7 +93,7 @@ class theme_boost_admin_settingspage_tabs extends admin_settingpage {
return '';
}
return $OUTPUT->render_from_template('theme_boost/admin_setting_tabs', $context);
return $OUTPUT->render_from_template('theme_ilb/admin_setting_tabs', $context);
}
}

10
classes/autoprefixer.php

@ -18,15 +18,15 @@
* Autoprefixer.
*
* This autoprefixer has been developed to satisfy the basic needs of the
* theme Boost when working with Bootstrap 4 alpha. We do not recommend
* theme Ilb when working with Bootstrap 4 alpha. We do not recommend
* that this tool is shared, nor used outside of this theme.
*
* @package theme_boost
* @package theme_ilb
* @copyright 2016 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace theme_boost;
namespace theme_ilb;
defined('MOODLE_INTERNAL') || die();
use Sabberworm\CSS\CSSList\CSSList;
@ -54,7 +54,7 @@ use Sabberworm\CSS\Value\ValueList;
*
* Very basic implementation covering simple needs for Bootstrap 4.
*
* @package theme_boost
* @package theme_ilb
* @copyright 2016 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
@ -104,6 +104,8 @@ class autoprefixer {
* @param Document $tree The CSS tree.
*/
public function __construct(Document $tree) {
debugging('theme_ilb\autoprefixer() is deprecated. Required prefixes for Bootstrap ' .
'are now in theme/ilb/scss/moodle/prefixes.scss', DEBUG_DEVELOPER);
$this->tree = $tree;
$pseudos = array_map(function($pseudo) {

50
classes/output/block_settings_renderer.php

@ -1,50 +0,0 @@
<?php
// 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/>.
/**
* Overriden block settings renderer.
*
* @package theme_boost
* @copyright 2016 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace theme_boost\output;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot . '/blocks/settings/renderer.php');
use moodle_url;
/**
* Overriden block settings renderer.
*
* @package theme_boost
* @copyright 2016 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class block_settings_renderer extends \block_settings_renderer {
public function search_form(moodle_url $formtarget, $searchvalue) {
$data = [
'action' => $formtarget->out(false),
'label' => get_string('searchinsettings', 'admin'),
'searchvalue' => $searchvalue
];
return $this->render_from_template('block_settings/search_form', $data);
}
}

52
classes/output/core/admin_renderer.php

@ -1,52 +0,0 @@
<?php
// 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/>.
/**
* Admin renderer.
*
* @package theme_noanme
* @copyright 2016 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace theme_boost\output\core;
defined('MOODLE_INTERNAL') || die();
use moodle_url;
require_once($CFG->dirroot . '/' . $CFG->admin . '/renderer.php');
/**
* Admin renderer class.
*
* @package theme_noanme
* @copyright 2016 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class admin_renderer extends \core_admin_renderer {
/**
* Output a warning message.
*
* @param string $message the message to display.
* @param string $type type class
* @return string HTML to output.
*/
protected function warning($message, $type = 'warning') {
return $this->box($message, 'generalbox m-b-1 admin' . $type);
}
}

81
classes/output/core/course_renderer.php

@ -1,81 +0,0 @@
<?php
// 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/>.
/**
* Course renderer.
*
* @package theme_noanme
* @copyright 2016 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace theme_boost\output\core;
defined('MOODLE_INTERNAL') || die();
use moodle_url;
require_once($CFG->dirroot . '/course/renderer.php');
/**
* Course renderer class.
*
* @package theme_noanme
* @copyright 2016 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class course_renderer extends \core_course_renderer {
/**
* Renders html to display a course search form.
*
* @param string $value default value to populate the search field
* @param string $format display format - 'plain' (default), 'short' or 'navbar'
* @return string
*/
public function course_search_form($value = '', $format = 'plain') {
static $count = 0;
$formid = 'coursesearch';
if ((++$count) > 1) {
$formid .= $count;
}
switch ($format) {
case 'navbar' :
$formid = 'coursesearchnavbar';
$inputid = 'navsearchbox';
$inputsize = 20;
break;
case 'short' :
$inputid = 'shortsearchbox';
$inputsize = 12;
break;
default :
$inputid = 'coursesearchbox';
$inputsize = 30;
}
$data = (object) [
'searchurl' => (new moodle_url('/course/search.php'))->out(false),
'id' => $formid,
'inputid' => $inputid,
'inputsize' => $inputsize,
'value' => $value
];
return $this->render_from_template('theme_boost/course_search_form', $data);
}
}

140
classes/output/core/files_renderer.php

@ -1,140 +0,0 @@
<?php
// 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/>.
namespace theme_boost\output\core;
use plugin_renderer_base;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot . '/files/renderer.php');
/**
* Rendering of files viewer related widgets.
* @package theme_boost
* @copyright 2016 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Rendering of files viewer related widgets.
* @package theme_boost
* @copyright 2016 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class files_renderer extends \core_files_renderer {
/**
* FileManager JS template for window with file information/actions.
*
*/
protected function fm_js_template_fileselectlayout() {
$context = [
'helpicon' => $this->help_icon('setmainfile', 'repository')
];
return $this->render_from_template('core/filemanager_fileselect', $context);
}
/**
* FileManager JS template for popup confirm dialogue window.
*
* @return string
*/
protected function fm_js_template_confirmdialog() {
return $this->render_from_template('core/filemanager_confirmdialog', []);
}
/**
* Template for FilePicker with general layout (not QuickUpload).
*
*
* @return string
*/
protected function fp_js_template_generallayout() {
return $this->render_from_template('core/filemanager_modal_generallayout', []);
}
/**
* Returns html for displaying one file manager
*
* @param form_filemanager $fm
* @return string
*/
protected function fm_print_generallayout($fm) {
$context = [
'client_id' => $fm->options->client_id,
'helpicon' => $this->help_icon('setmainfile', 'repository'),
'restrictions' => $this->fm_print_restrictions($fm)
];
return $this->render_from_template('core/filemanager_page_generallayout', $context);
}
/**
* Returns HTML for default repository searchform to be passed to Filepicker
*
* This will be used as contents for search form defined in generallayout template
* (form with id {TOOLSEARCHID}).
* Default contents is one text input field with name="s"
*/
public function repository_default_searchform() {
return $this->render_from_template('core/filemanager_default_searchform', []);
}
/**
* FilePicker JS template for 'Upload file' repository
*
* @return string
*/
protected function fp_js_template_uploadform() {
return $this->render_from_template('core/filemanager_uploadform', []);
}
/**
* FilePicker JS template for repository login form including templates for each element type
*
* @return string
*/
protected function fp_js_template_loginform() {
return $this->render_from_template('core/filemanager_loginform', []);
}
/**
* FilePicker JS template for window appearing to select a file.
*
* @return string
*/
protected function fp_js_template_selectlayout() {
return $this->render_from_template('core/filemanager_selectlayout', []);
}
/**
* FilePicker JS template for popup dialogue window asking for action when file with the same name already exists
* (multiple-file version).
*
* @return string
*/
protected function fp_js_template_processexistingfilemultiple() {
return $this->render_from_template('core/filemanager_processexistingfilemultiple', []);
}
/**
* FilePicker JS template for popup dialogue window asking for action when file with the same name already exists.
*
* @return string
*/
protected function fp_js_template_processexistingfile() {
return $this->render_from_template('core/filemanager_processexistingfile', []);
}
}

58
classes/output/core_question/bank_renderer.php

@ -1,58 +0,0 @@
<?php
// 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/>.
/**
* Question renderer.
*
* @package theme_boost
* @copyright 2017 onwards Ankit Agarwal <ankit.agrr@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace theme_boost\output\core_question;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot . '/' . $CFG->admin . '/renderer.php');
/**
* Question renderer class.
*
* @package theme_boost
* @copyright 2017 onwards Ankit Agarwal <ankit.agrr@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class bank_renderer extends \core_question_bank_renderer {
/**
* Display additional navigation if needed.
*
* @return string
*/
public function extra_horizontal_navigation() {
// Horizontal navigation for question bank.
if ($questionnode = $this->page->settingsnav->find("questionbank", \navigation_node::TYPE_CONTAINER)) {
if ($children = $questionnode->children) {
$tabs = [];
foreach ($children as $key => $node) {
$tabs[] = new \tabobject($node->key, $node->action, $node->text);
}
$active = $questionnode->find_active_node()->key;
return \html_writer::div(print_tabs([$tabs], $active, null, null, true), 'questionbank-navigation');
}
}
return '';
}
}

691
classes/output/core_renderer.php

@ -14,703 +14,32 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace theme_boost\output;
namespace theme_ilb\output;
use coding_exception;
use html_writer;
use tabobject;
use tabtree;
use custom_menu_item;
use custom_menu;
use block_contents;
use navigation_node;
use action_link;
use stdClass;
use moodle_url;
use preferences_groups;
use action_menu;
use help_icon;
use single_button;
use paging_bar;
use context_course;
use pix_icon;
defined('MOODLE_INTERNAL') || die;
/**
* Renderers to align Moodle's HTML with that expected by Bootstrap
*
* @package theme_boost
* @package theme_ilb
* @copyright 2012 Bas Brands, www.basbrands.nl
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class core_renderer extends \core_renderer {
/** @var custom_menu_item language The language menu if created */
protected $language = null;
/**
* Outputs the opening section of a box.
*
* @param string $classes A space-separated list of CSS classes
* @param string $id An optional ID
* @param array $attributes An array of other attributes to give the box.
* @return string the HTML to output.
*/
public function box_start($classes = 'generalbox', $id = null, $attributes = array()) {
if (is_array($classes)) {
$classes = implode(' ', $classes);
}
return parent::box_start($classes . ' p-y-1', $id, $attributes);
}
/**
* Wrapper for header elements.
*
* @return string HTML to display the main header.
*/
public function full_header() {
global $PAGE;
$html = html_writer::start_tag('header', array('id' => 'page-header', 'class' => 'row'));
$html .= html_writer::start_div('col-xs-12 p-a-1');
$html .= html_writer::start_div('card');
$html .= html_writer::start_div('card-block');
$html .= html_writer::div($this->context_header_settings_menu(), 'pull-xs-right context-header-settings-menu');
$html .= html_writer::start_div('pull-xs-left');
$html .= $this->context_header();
$html .= html_writer::end_div();
$pageheadingbutton = $this->page_heading_button();
if (empty($PAGE->layout_options['nonavbar'])) {
$html .= html_writer::start_div('clearfix w-100 pull-xs-left', array('id' => 'page-navbar'));
$html .= html_writer::tag('div', $this->navbar(), array('class' => 'breadcrumb-nav'));
$html .= html_writer::div($pageheadingbutton, 'breadcrumb-button pull-xs-right');
$html .= html_writer::end_div();
} else if ($pageheadingbutton) {
$html .= html_writer::div($pageheadingbutton, 'breadcrumb-button nonavbar pull-xs-right');
}
$html .= html_writer::tag('div', $this->course_header(), array('id' => 'course-header'));
$html .= html_writer::end_div();
$html .= html_writer::end_div();
$html .= html_writer::end_div();
$html .= html_writer::end_tag('header');
return $html;
}
/**
* The standard tags that should be included in the <head> tag
* including a meta description for the front page
*
* @return string HTML fragment.
*/
public function standard_head_html() {
global $SITE, $PAGE;
$output = parent::standard_head_html();
if ($PAGE->pagelayout == 'frontpage') {
$summary = s(strip_tags(format_text($SITE->summary, FORMAT_HTML)));
if (!empty($summary)) {
$output .= "<meta name=\"description\" content=\"$summary\" />\n";
}
}
return $output;
}
/*
* This renders the navbar.
* Uses bootstrap compatible html.
*/
public function navbar() {
return $this->render_from_template('core/navbar', $this->page->navbar);
}
/**
* We don't like these...
*
*/
public function edit_button(moodle_url $url) {
return '';
}
/**
* Override to inject the logo.
*
* @param array $headerinfo The header info.
* @param int $headinglevel What level the 'h' tag will be.
* @return string HTML for the header bar.
*/
public function context_header($headerinfo = null, $headinglevel = 1) {
global $SITE;
if ($this->should_display_main_logo($headinglevel)) {
$sitename = format_string($SITE->fullname, true, array('context' => context_course::instance(SITEID)));
return html_writer::div(html_writer::empty_tag('img', [
'src' => $this->get_logo_url(null, 150), 'alt' => $sitename]), 'logo');
}
return parent::context_header($headerinfo, $headinglevel);
}
/**
* Get the compact logo URL.
*
* @return string
*/
public function get_compact_logo_url($maxwidth = 100, $maxheight = 100) {
return parent::get_compact_logo_url(null, 70);
}
/**
* Whether we should display the main logo.
*
* @return bool
*/
public function should_display_main_logo($headinglevel = 1) {
global $PAGE;
// Only render the logo if we're on the front page or login page and the we have a logo.
$logo = $this->get_logo_url();
if ($headinglevel == 1 && !empty($logo)) {
if ($PAGE->pagelayout == 'frontpage' || $PAGE->pagelayout == 'login') {
return true;
}
}
return false;
}
/**
* Whether we should display the logo in the navbar.
*
* We will when there are no main logos, and we have compact logo.
*
* @return bool
*/
public function should_display_navbar_logo() {
$logo = $this->get_compact_logo_url();
return !empty($logo) && !$this->should_display_main_logo();
}
/*
* Overriding the custom_menu function ensures the custom menu is
* always shown, even if no menu items are configured in the global
* theme settings page.
*/
public function custom_menu($custommenuitems = '') {
global $CFG;
if (empty($custommenuitems) && !empty($CFG->custommenuitems)) {
$custommenuitems = $CFG->custommenuitems;
}
$custommenu = new custom_menu($custommenuitems, current_language());
return $this->render_custom_menu($custommenu);
}
/**
* We want to show the custom menus as a list of links in the footer on small screens.
* Just return the menu object exported so we can render it differently.
*/
public function custom_menu_flat() {
global $CFG;
$custommenuitems = '';
if (empty($custommenuitems) && !empty($CFG->custommenuitems)) {
$custommenuitems = $CFG->custommenuitems;
}
$custommenu = new custom_menu($custommenuitems, current_language());
$langs = get_string_manager()->get_list_of_translations();
$haslangmenu = $this->lang_menu() != '';
if ($haslangmenu) {
$strlang = get_string('language');
$currentlang = current_language();
if (isset($langs[$currentlang])) {
$currentlang = $langs[$currentlang];
} else {
$currentlang = $strlang;
}
$this->language = $custommenu->add($currentlang, new moodle_url('#'), $strlang, 10000);
foreach ($langs as $langtype => $langname) {
$this->language->add($langname, new moodle_url($this->page->url, array('lang' => $langtype)), $langname);
}
}
return $custommenu->export_for_template($this);
}
/*
* This renders the bootstrap top menu.
*
* This renderer is needed to enable the Bootstrap style navigation.
*/
protected function render_custom_menu(custom_menu $menu) {
global $CFG;
$langs = get_string_manager()->get_list_of_translations();
$haslangmenu = $this->lang_menu() != '';
if (!$menu->has_children() && !$haslangmenu) {
return '';
}
if ($haslangmenu) {
$strlang = get_string('language');
$currentlang = current_language();
if (isset($langs[$currentlang])) {
$currentlang = $langs[$currentlang];
} else {
$currentlang = $strlang;
}
$this->language = $menu->add($currentlang, new moodle_url('#'), $strlang, 10000);
foreach ($langs as $langtype => $langname) {
$this->language->add($langname, new moodle_url($this->page->url, array('lang' => $langtype)), $langname);
}
}
$content = '';
foreach ($menu->get_children() as $item) {
$context = $item->export_for_template($this);
$content .= $this->render_from_template('core/custom_menu_item', $context);
}
return $content;
}
/**
* This code renders the navbar button to control the display of the custom menu
* on smaller screens.
*
* Do not display the button if the menu is empty.
*
* @return string HTML fragment
*/
public function navbar_button() {
global $CFG;
if (empty($CFG->custommenuitems) && $this->lang_menu() == '') {
return '';
}
$iconbar = html_writer::tag('span', '', array('class' => 'icon-bar'));
$button = html_writer::tag('a', $iconbar . "\n" . $iconbar. "\n" . $iconbar, array(
'class' => 'btn btn-navbar',
'data-toggle' => 'collapse',
'data-target' => '.nav-collapse'
));
return $button;
}
/**
* Renders tabtree
*
* @param tabtree $tabtree
* @return string
*/
protected function render_tabtree(tabtree $tabtree) {
if (empty($tabtree->subtree)) {
return '';
}
$data = $tabtree->export_for_template($this);
return $this->render_from_template('core/tabtree', $data);
}
/**
* Renders tabobject (part of tabtree)
*
* This function is called from {@link core_renderer::render_tabtree()}
* and also it calls itself when printing the $tabobject subtree recursively.
*
* @param tabobject $tabobject
* @return string HTML fragment
*/
protected function render_tabobject(tabobject $tab) {
throw new coding_exception('Tab objects should not be directly rendered.');
}
/**
* Prints a nice side block with an optional header.
*
* @param block_contents $bc HTML for the content
* @param string $region the region the block is appearing in.
* @return string the HTML to be output.
*/
public function block(block_contents $bc, $region) {
$bc = clone($bc); // Avoid messing up the object passed in.
if (empty($bc->blockinstanceid) || !strip_tags($bc->title)) {
$bc->collapsible = block_contents::NOT_HIDEABLE;
}
$id = !empty($bc->attributes['id']) ? $bc->attributes['id'] : uniqid('block-');
$context = new stdClass();
$context->skipid = $bc->skipid;
$context->blockinstanceid = $bc->blockinstanceid;
$context->dockable = $bc->dockable;
$context->id = $id;
$context->hidden = $bc->collapsible == block_contents::HIDDEN;
$context->skiptitle = strip_tags($bc->title);
$context->showskiplink = !empty($context->skiptitle);
$context->arialabel = $bc->arialabel;
$context->ariarole = !empty($bc->attributes['role']) ? $bc->attributes['role'] : 'complementary';
$context->type = $bc->attributes['data-block'];
$context->title = $bc->title;
$context->content = $bc->content;
$context->annotation = $bc->annotation;
$context->footer = $bc->footer;
$context->hascontrols = !empty($bc->controls);
if ($context->hascontrols) {
$context->controls = $this->block_controls($bc->controls, $id);
}
return $this->render_from_template('core/block', $context);
}
/**
* Returns the CSS classes to apply to the body tag.
*
* @since Moodle 2.5.1 2.6
* @param array $additionalclasses Any additional classes to apply.
* @return string
*/
public function body_css_classes(array $additionalclasses = array()) {
return $this->page->bodyclasses . ' ' . implode(' ', $additionalclasses);
}
/**
* Renders preferences groups.
*
* @param preferences_groups $renderable The renderable
* @return string The output.
*/
public function render_preferences_groups(preferences_groups $renderable) {
return $this->render_from_template('core/preferences_groups', $renderable);
}
/**
* Renders an action menu component.
*
* @param action_menu $menu
* @return string HTML
*/
public function render_action_menu(action_menu $menu) {
// We don't want the class icon there!
foreach ($menu->get_secondary_actions() as $action) {
if ($action instanceof \action_menu_link && $action->has_class('icon')) {
$action->attributes['class'] = preg_replace('/(^|\s+)icon(\s+|$)/i', '', $action->attributes['class']);
}
}
if ($menu->is_empty()) {
return '';
}
$context = $menu->export_for_template($this);
return $this->render_from_template('core/action_menu', $context);
}
/**
* Implementation of user image rendering.
*
* @param help_icon $helpicon A help icon instance
* @return string HTML fragment
*/
protected function render_help_icon(help_icon $helpicon) {
$context = $helpicon->export_for_template($this);
return $this->render_from_template('core/help_icon', $context);
}
/**
* Renders a single button widget.
*
* This will return HTML to display a form containing a single button.
*
* @param single_button $button
* @return string HTML fragment
*/
protected function render_single_button(single_button $button) {
return $this->render_from_template('core/single_button', $button->export_for_template($this));
}
/**
* Renders a paging bar.
*
* @param paging_bar $pagingbar The object.
* @return string HTML
*/
protected function render_paging_bar(paging_bar $pagingbar) {
// Any more than 10 is not usable and causes wierd wrapping of the pagination in this theme.
$pagingbar->maxdisplay = 10;
return $this->render_from_template('core/paging_bar', $pagingbar->export_for_template($this));
}
/**
* Renders the login form.
*
* @param \core_auth\output\login $form The renderable.
* @return string
*/
public function render_login(\core_auth\output\login $form) {
global $SITE;
$context = $form->export_for_template($this);
// Override because rendering is not supported in template yet.
$context->cookieshelpiconformatted = $this->help_icon('cookiesenabled');
$context->errorformatted = $this->error_text($context->error);
$url = $this->get_logo_url();
if ($url) {
$url = $url->out(false);
}
$context->logourl = $url;
$context->sitename = format_string($SITE->fullname, true, ['context' => context_course::instance(SITEID), "escape" => false]);
return $this->render_from_template('core/loginform', $context);
}
/**
* Render the login signup form into a nice template for the theme.
*
* @param mform $form
* @return string
*/
public function render_login_signup_form($form) {
global $SITE;
$context = $form->export_for_template($this);
$url = $this->get_logo_url();
if ($url) {
$url = $url->out(false);
}
$context['logourl'] = $url;
$context['sitename'] = format_string($SITE->fullname, true, ['context' => context_course::instance(SITEID), "escape" => false]);
return $this->render_from_template('core/signup_form_layout', $context);
}
/**
* This is an optional menu that can be added to a layout by a theme. It contains the
* menu for the course administration, only on the course main page.
*
* @return string
*/
public function context_header_settings_menu() {
$context = $this->page->context;
$menu = new action_menu();
$items = $this->page->navbar->get_items();
$currentnode = end($items);
$showcoursemenu = false;
$showfrontpagemenu = false;
$showusermenu = false;
// We are on the course home page.
if (($context->contextlevel == CONTEXT_COURSE) &&
!empty($currentnode) &&
($currentnode->type == navigation_node::TYPE_COURSE || $currentnode->type == navigation_node::TYPE_SECTION)) {
$showcoursemenu = true;
}
$courseformat = course_get_format($this->page->course);
// This is a single activity course format, always show the course menu on the activity main page.
if ($context->contextlevel == CONTEXT_MODULE &&
!$courseformat->has_view_page()) {
$this->page->navigation->initialise();
$activenode = $this->page->navigation->find_active_node();
// If the settings menu has been forced then show the menu.
if ($this->page->is_settings_menu_forced()) {
$showcoursemenu = true;
} else if (!empty($activenode) && ($activenode->type == navigation_node::TYPE_ACTIVITY ||
$activenode->type == navigation_node::TYPE_RESOURCE)) {
// We only want to show the menu on the first page of the activity. This means
// the breadcrumb has no additional nodes.
if ($currentnode && ($currentnode->key == $activenode->key && $currentnode->type == $activenode->type)) {
$showcoursemenu = true;
}
}
}
// This is the site front page.
if ($context->contextlevel == CONTEXT_COURSE &&
!empty($currentnode) &&
$currentnode->key === 'home') {
$showfrontpagemenu = true;
}
// This is the user profile page.
if ($context->contextlevel == CONTEXT_USER &&
!empty($currentnode) &&
($currentnode->key === 'myprofile')) {
$showusermenu = true;
}
if ($showfrontpagemenu) {
$settingsnode = $this->page->settingsnav->find('frontpage', navigation_node::TYPE_SETTING);
if ($settingsnode) {
// Build an action menu based on the visible nodes from this navigation tree.
$skipped = $this->build_action_menu_from_navigation($menu, $settingsnode, false, true);
// We only add a list to the full settings menu if we didn't include every node in the short menu.
if ($skipped) {
$text = get_string('morenavigationlinks');
$url = new moodle_url('/course/admin.php', array('courseid' => $this->page->course->id));
$link = new action_link($url, $text, null, null, new pix_icon('t/edit', $text));
$menu->add_secondary_action($link);
}
}
} else if ($showcoursemenu) {
$settingsnode = $this->page->settingsnav->find('courseadmin', navigation_node::TYPE_COURSE);
if ($settingsnode) {
// Build an action menu based on the visible nodes from this navigation tree.
$skipped = $this->build_action_menu_from_navigation($menu, $settingsnode, false, true);
// We only add a list to the full settings menu if we didn't include every node in the short menu.
if ($skipped) {
$text = get_string('morenavigationlinks');
$url = new moodle_url('/course/admin.php', array('courseid' => $this->page->course->id));
$link = new action_link($url, $text, null, null, new pix_icon('t/edit', $text));
$menu->add_secondary_action($link);
}
}
} else if ($showusermenu) {
// Get the course admin node from the settings navigation.
$settingsnode = $this->page->settingsnav->find('useraccount', navigation_node::TYPE_CONTAINER);
if ($settingsnode) {
// Build an action menu based on the visible nodes from this navigation tree.
$this->build_action_menu_from_navigation($menu, $settingsnode);
}
}
return $this->render($menu);
}
/**
* This is an optional menu that can be added to a layout by a theme. It contains the
* menu for the most specific thing from the settings block. E.g. Module administration.
*
* @return string
*/
public function region_main_settings_menu() {
$context = $this->page->context;
$menu = new action_menu();
if ($context->contextlevel == CONTEXT_MODULE) {
$this->page->navigation->initialise();
$node = $this->page->navigation->find_active_node();
$buildmenu = false;
// If the settings menu has been forced then show the menu.
if ($this->page->is_settings_menu_forced()) {
$buildmenu = true;
} else if (!empty($node) && ($node->type == navigation_node::TYPE_ACTIVITY ||
$node->type == navigation_node::TYPE_RESOURCE)) {
$items = $this->page->navbar->get_items();
$navbarnode = end($items);
// We only want to show the menu on the first page of the activity. This means
// the breadcrumb has no additional nodes.
if ($navbarnode && ($navbarnode->key === $node->key && $navbarnode->type == $node->type)) {
$buildmenu = true;
}
}
if ($buildmenu) {
// Get the course admin node from the settings navigation.
$node = $this->page->settingsnav->find('modulesettings', navigation_node::TYPE_SETTING);
if ($node) {
// Build an action menu based on the visible nodes from this navigation tree.
$this->build_action_menu_from_navigation($menu, $node);
}
}
} else if ($context->contextlevel == CONTEXT_COURSECAT) {
// For course category context, show category settings menu, if we're on the course category page.
if ($this->page->pagetype === 'course-index-category') {
$node = $this->page->settingsnav->find('categorysettings', navigation_node::TYPE_CONTAINER);
if ($node) {
// Build an action menu based on the visible nodes from this navigation tree.
$this->build_action_menu_from_navigation($menu, $node);
}
}
} else {
$items = $this->page->navbar->get_items();
$navbarnode = end($items);
if ($navbarnode && ($navbarnode->key === 'participants')) {
$node = $this->page->settingsnav->find('users', navigation_node::TYPE_CONTAINER);
if ($node) {
// Build an action menu based on the visible nodes from this navigation tree.
$this->build_action_menu_from_navigation($menu, $node);
}
}
}
return $this->render($menu);
}
/**
* Take a node in the nav tree and make an action menu out of it.
* The links are injected in the action menu.
*
* @param action_menu $menu
* @param navigation_node $node
* @param boolean $indent
* @param boolean $onlytopleafnodes
* @return boolean nodesskipped - True if nodes were skipped in building the menu
*/
protected function build_action_menu_from_navigation(action_menu $menu,
navigation_node $node,
$indent = false,
$onlytopleafnodes = false) {
$skipped = false;
// Build an action menu based on the visible nodes from this navigation tree.
foreach ($node->children as $menuitem) {
if ($menuitem->display) {
if ($onlytopleafnodes && $menuitem->children->count()) {
$skipped = true;
continue;
}
if ($menuitem->action) {
if ($menuitem->action instanceof action_link) {
$link = $menuitem->action;
// Give preference to setting icon over action icon.
if (!empty($menuitem->icon)) {
$link->icon = $menuitem->icon;
}
} else {
$link = new action_link($menuitem->action, $menuitem->text, null, null, $menuitem->icon);
}
$url->param('sesskey', sesskey());
if ($this->page->user_is_editing()) {
$url->param('edit', 'off');
$editstring = get_string('turneditingoff');
} else {
if ($onlytopleafnodes) {
$skipped = true;
continue;
}
$link = new action_link(new moodle_url('#'), $menuitem->text, null, ['disabled' => true], $menuitem->icon);
$url->param('edit', 'on');
$editstring = get_string('turneditingon');
}
if ($indent) {
$link->add_class('m-l-1');
}
if (!empty($menuitem->classes)) {
$link->add_class(implode(" ", $menuitem->classes));
$button = new \single_button($url, $editstring, 'post', ['class' => 'btn btn-primary']);
return $this->render_single_button($button);
}
$menu->add_secondary_action($link);
$skipped = $skipped || $this->build_action_menu_from_navigation($menu, $menuitem, true);
}
}
return $skipped;
}
/**
* Secure login info.
*
* @return string
*/
public function secure_login_info() {
return $this->login_info(false);
}
}

197
classes/output/core_renderer_maintenance.php

@ -1,197 +0,0 @@
<?php
// 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/>.
/**
* Overriden core maintenance renderer.
*
* @package theme_boost
* @copyright 2016 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace theme_boost\output;
defined('MOODLE_INTERNAL') || die();
use coding_exception;
use moodle_page;
use block_contents;
use stdClass;
/**
* The maintenance renderer.
*
* @package theme_boost
* @copyright 2016 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class core_renderer_maintenance extends core_renderer {
/**
* Initialises the renderer instance.
*
* @param moodle_page $page
* @param string $target
* @throws coding_exception
*/
public function __construct(moodle_page $page, $target) {
if ($target !== RENDERER_TARGET_MAINTENANCE || $page->pagelayout !== 'maintenance') {
throw new coding_exception('Invalid request for the maintenance renderer.');
}
parent::__construct($page, $target);
}
/**
* Does nothing. The maintenance renderer cannot produce blocks.
*
* @param block_contents $bc
* @param string $region
* @return string
*/
public function block(block_contents $bc, $region) {
return '';
}
/**
* Does nothing. The maintenance renderer cannot produce blocks.
*
* @param string $region
* @param array $classes
* @param string $tag
* @return string
*/
public function blocks($region, $classes = array(), $tag = 'aside') {
return '';
}
/**
* Does nothing. The maintenance renderer cannot produce blocks.
*
* @param string $region
* @return string
*/
public function blocks_for_region($region) {
return '';
}
/**
* Does nothing. The maintenance renderer cannot produce a course content header.
*
* @param bool $onlyifnotcalledbefore
* @return string
*/
public function course_content_header($onlyifnotcalledbefore = false) {
return '';
}
/**
* Does nothing. The maintenance renderer cannot produce a course content footer.
*
* @param bool $onlyifnotcalledbefore
* @return string
*/
public function course_content_footer($onlyifnotcalledbefore = false) {
return '';
}
/**
* Does nothing. The maintenance renderer cannot produce a course header.
*
* @return string
*/
public function course_header() {
return '';
}
/**
* Does nothing. The maintenance renderer cannot produce a course footer.
*
* @return string
*/
public function course_footer() {
return '';
}
/**
* Does nothing. The maintenance renderer cannot produce a custom menu.
*
* @param string $custommenuitems
* @return string
*/
public function custom_menu($custommenuitems = '') {
return '';
}
/**
* Does nothing. The maintenance renderer cannot produce a file picker.
*
* @param array $options
* @return string
*/
public function file_picker($options) {
return '';
}
/**
* Does nothing. The maintenance renderer cannot produce and HTML file tree.
*
* @param array $dir
* @return string
*/
public function htmllize_file_tree($dir) {
return '';
}
/**
* Does nothing. The maintenance renderer does not support JS.
*
* @param block_contents $bc
*/
public function init_block_hider_js(block_contents $bc) {
// Does nothing.
}
/**
* Does nothing. The maintenance renderer cannot produce language menus.
*
* @return string
*/
public function lang_menu() {
return '';
}
/**
* Does nothing. The maintenance renderer has no need for login information.
*
* @param null $withlinks
* @return string
*/
public function login_info($withlinks = null) {
return '';
}
/**
* Does nothing. The maintenance renderer cannot produce user pictures.
*
* @param stdClass $user
* @param array $options
* @return string
*/
public function user_picture(stdClass $user, array $options = null) {
return '';
}
}

52
classes/privacy/provider.php

@ -15,32 +15,64 @@
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Privacy Subsystem implementation for theme_boost.
* Privacy Subsystem implementation for theme_ilb.
*
* @package theme_boost
* @package theme_ilb
* @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace theme_boost\privacy;
namespace theme_ilb\privacy;
use \core_privacy\local\metadata\collection;
defined('MOODLE_INTERNAL') || die();
/**
* The boost theme does not store any data.
* The ilb theme stores a user preference data.
*
* @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements \core_privacy\local\metadata\null_provider {
class provider implements
// This plugin has data.
\core_privacy\local\metadata\provider,
// This plugin has some sitewide user preferences to export.
\core_privacy\local\request\user_preference_provider {
/** The user preference for the navigation drawer. */
const DRAWER_OPEN_NAV = 'drawer-open-nav';
/**
* Returns meta data about this system.
*
* @param collection $items The initialised item collection to add items to.
* @return collection A listing of user data stored through this system.
*/
public static function get_metadata(collection $items) : collection {
$items->add_user_preference(self::DRAWER_OPEN_NAV, 'privacy:metadata:preference:draweropennav');
return $items;
}
/**
* Get the language string identifier with the component's language
* file to explain why this plugin stores no data.
* Store all user preferences for the plugin.
*
* @return string
* @param int $userid The userid of the user whose data is to be exported.
*/
public static function get_reason() : string {
return 'privacy:metadata';
public static function export_user_preferences(int $userid) {
$draweropennavpref = get_user_preferences(self::DRAWER_OPEN_NAV, null, $userid);
if (isset($draweropennavpref)) {
$preferencestring = get_string('privacy:drawernavclosed', 'theme_ilb');
if ($draweropennavpref == 'true') {
$preferencestring = get_string('privacy:drawernavopen', 'theme_ilb');
}
\core_privacy\local\request\writer::export_user_preference(
'theme_ilb',
self::DRAWER_OPEN_NAV,
$draweropennavpref,
$preferencestring
);
}
}
}

106
cli/import-bootswatch.php

@ -0,0 +1,106 @@
<?php
// 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/>.
/**
* Used to convert a bootswatch file from https://bootswatch.com/ to a Moodle preset.
*
* @package theme_ilb
* @subpackage cli
* @copyright 2016 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define('CLI_SCRIPT', true);
require(__DIR__.'/../../../config.php');
require_once($CFG->libdir.'/clilib.php');
$usage = "
Utility to convert a Bootswatch theme to a Moodle preset compatible with Bootstrap 4.
Download _variables.scss and _bootswatch.scss files from https://bootswatch.com/
Run this script. It will generate a new file 'preset.scss' which can be used as
a Moodle preset.
Usage:
# php import-bootswatch.php [--help|-h]
# php import-bootswatch.php --variables=<path> --bootswatch=<path> --preset=<path>
Options:
-h --help Print this help.
--variables=<path> Path to the input variables file, defaults to _variables.scss
--bootswatch=<path> Path to the input bootswatch file, defauls to _bootswatch.scss
--preset=<path> Path to the output preset file, defaults to preset.scss
";
list($options, $unrecognised) = cli_get_params([
'help' => false,
'variables' => '_variables.scss',
'bootswatch' => '_bootswatch.scss',
'preset' => 'preset.scss',
], [
'h' => 'help',
]);
if ($unrecognised) {
$unrecognised = implode(PHP_EOL.' ', $unrecognised);
cli_error(get_string('cliunknowoption', 'core_admin', $unrecognised));
}
if ($options['help']) {
cli_writeln($usage);
exit(2);
}
if (is_readable($options['variables'])) {
$sourcevariables = file_get_contents($options['variables']);
} else {
cli_writeln($usage);
cli_error('Error reading the variables file: '.$options['variables']);
}
if (is_readable($options['bootswatch'])) {
$sourcebootswatch = file_get_contents($options['bootswatch']);
} else {
cli_writeln($usage);
cli_error('Error reading the bootswatch file: '.$options['bootswatch']);
}
// Write the preset file.
$out = fopen($options['preset'], 'w');
if (!$out) {
cli_error('Error writing to the preset file');
}
fwrite($out, $sourcevariables);
fwrite($out, '
// Import FontAwesome.
@import "fontawesome";
// Import All of Bootstrap
@import "bootstrap";
// Import Core moodle CSS
@import "moodle";
');
// Add the bootswatch file.
fwrite($out, $sourcebootswatch);
fclose($out);

155
config.php

@ -1,54 +1,39 @@
<?php
// 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/>.
/**
* Ilb config.
*
* @package theme_ilb
* @copyright 2016 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
// Every file should have GPL and copyright in the header - we skip it in tutorials but you should not skip it for real.
// This line protects the file from being accessed by a URL directly.
defined('MOODLE_INTERNAL') || die();
// $THEME is defined before this page is included and we can define settings by adding properties to this global object.
require_once(__DIR__ . '/lib.php');
// The first setting we need is the name of the theme. This should be the last part of the component name, and the same
// as the directory name for our theme.
$THEME->name = 'ilb';
// This setting list the style sheets we want to include in our theme. Because we want to use SCSS instead of CSS - we won't
// list any style sheets. If we did we would list the name of a file in the /style/ folder for our theme without any css file
// extensions.
$THEME->sheets = [];
// This is a setting that can be used to provide some styling to the content in the TinyMCE text editor. This is no longer the
// default text editor and "Atto" does not need this setting so we won't provide anything. If we did it would work the same
// as the previous setting - listing a file in the /styles/ folder.
$THEME->editor_sheets = [];
// This is a critical setting. We want to inherit from theme_boost because it provides a great starting point for SCSS bootstrap4
// themes. We could add more than one parent here to inherit from multiple parents, and if we did they would be processed in
// order of importance (later themes overriding earlier ones). Things we will inherit from the parent theme include
// styles and mustache templates and some (not all) settings.
$THEME->parents = ['boost'];
// A dock is a way to take blocks out of the page and put them in a persistent floating area on the side of the page. Boost
// does not support a dock so we won't either - but look at bootstrapbase for an example of a theme with a dock.
$THEME->enable_dock = false;
// This is an old setting used to load specific CSS for some YUI JS. We don't need it in Boost based themes because Boost
// provides default styling for the YUI modules that we use. It is not recommended to use this setting anymore.
$THEME->yuicssmodules = array();
// Most themes will use this rendererfactory as this is the one that allows the theme to override any other renderer.
$THEME->rendererfactory = 'theme_overridden_renderer_factory';
// This is a list of blocks that are required to exist on all pages for this theme to function correctly. For example
// bootstrap base requires the settings and navigation blocks because otherwise there would be no way to navigate to all the
// pages in Moodle. Boost does not require these blocks because it provides other ways to navigate built into the theme.
$THEME->requiredblocks = '';
// This is a feature that tells the blocks library not to use the "Add a block" block. We don't want this in boost based themes because
// it forces a block region into the page when editing is enabled and it takes up too much room.
$THEME->addblockposition = BLOCK_ADDBLOCK_POSITION_FLATNAV;
$THEME->editor_scss = ['editor'];
$THEME->usefallback = true;
$THEME->iconsystem = \core\output\icon_system::FONTAWESOME;
$THEME->scss = function($theme) {
return theme_ilb_get_main_scss_content($theme);
};
$THEME->layouts = [
// Most backwards compatible layout without the blocks - this is the layout used by default.
@ -82,23 +67,93 @@ $THEME->layouts = [
),
// The site home page.
'frontpage' => array(
'file' => 'frontpage_ilb.php',
'file' => 'frontpage.php',
'regions' => array('side-pre'),
'defaultregion' => 'side-pre',
'options' => array('nonavbar' => true),
),
// Server administration scripts.
'admin' => array(
'file' => 'columns2.php',
'regions' => array('side-pre'),
'defaultregion' => 'side-pre',
),
// My dashboard page.
'mydashboard' => array(
'file' => 'columns2.php',
'regions' => array('side-pre'),
'defaultregion' => 'side-pre',
'options' => array('nonavbar' => true, 'langmenu' => true),
'options' => array('nonavbar' => true, 'langmenu' => true, 'nocontextheader' => true),
),
// My public page.
'mypublic' => array(
'file' => 'columns2.php',
'regions' => array('side-pre'),
'defaultregion' => 'side-pre',
),
'login' => array(
'file' => 'login.php',
'regions' => array(),
'options' => array('langmenu' => true),
),
// Pages that appear in pop-up windows - no navigation, no blocks, no header.
'popup' => array(
'file' => 'columns1.php',
'regions' => array(),
'options' => array('nofooter' => true, 'nonavbar' => true),
),
// No blocks and minimal footer - used for legacy frame layouts only!
'frametop' => array(
'file' => 'columns1.php',
'regions' => array(),
'options' => array('nofooter' => true, 'nocoursefooter' => true),
),
// Embeded pages, like iframe/object embeded in moodleform - it needs as much space as possible.
'embedded' => array(
'file' => 'embedded.php',
'regions' => array('side-pre'),
'defaultregion' => 'side-pre',
),
// Used during upgrade and install, and for the 'This site is undergoing maintenance' message.
// This must not have any blocks, links, or API calls that would lead to database or cache interaction.
// Please be extremely careful if you are modifying this layout.
'maintenance' => array(
'file' => 'maintenance.php',
'regions' => array(),
),
// Should display the content and basic headers only.
'print' => array(
'file' => 'columns1.php',
'regions' => array(),
'options' => array('nofooter' => true, 'nonavbar' => false),
),
// The pagelayout used when a redirection is occuring.
'redirect' => array(
'file' => 'embedded.php',
'regions' => array(),
),
// The pagelayout used for reports.
'report' => array(
'file' => 'columns2.php',
'regions' => array('side-pre'),
'defaultregion' => 'side-pre',
),
// The pagelayout used for safebrowser and securewindow.
'secure' => array(
'file' => 'secure.php',
'regions' => array('side-pre'),
'defaultregion' => 'side-pre'
)
];
// This is the function that returns the SCSS source for the main file in our theme. We override the boost version because
// we want to allow presets uploaded to our own theme file area to be selected in the preset list.
$THEME->scss = function($theme) {
return theme_ilb_get_main_scss_content($theme);
};
$THEME->parents = [];
$THEME->enable_dock = false;
$THEME->extrascsscallback = 'theme_ilb_get_extra_scss';
$THEME->prescsscallback = 'theme_ilb_get_pre_scss';
$THEME->precompiledcsscallback = 'theme_ilb_get_precompiled_css';
$THEME->yuicssmodules = array();
$THEME->rendererfactory = 'theme_overridden_renderer_factory';
$THEME->requiredblocks = '';
$THEME->addblockposition = BLOCK_ADDBLOCK_POSITION_FLATNAV;
$THEME->iconsystem = \core\output\icon_system::FONTAWESOME;

BIN
js/apple-touch-icon-114x114.png?v=OmmK4dNQkX

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

BIN
js/apple-touch-icon-120x120.png?v=OmmK4dNQkX

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

BIN
js/apple-touch-icon-144x144.png?v=OmmK4dNQkX

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

BIN
js/apple-touch-icon-152x152.png?v=OmmK4dNQkX

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

BIN
js/apple-touch-icon-180x180.png?v=OmmK4dNQkX

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

BIN
js/apple-touch-icon-57x57.png?v=OmmK4dNQkX

Binary file not shown.

Before

Width:  |  Height:  |  Size: 733 B

BIN
js/apple-touch-icon-60x60.png?v=OmmK4dNQkX

Binary file not shown.

Before

Width:  |  Height:  |  Size: 792 B

BIN
js/apple-touch-icon-72x72.png?v=OmmK4dNQkX

Binary file not shown.

Before

Width:  |  Height:  |  Size: 859 B

BIN
js/apple-touch-icon-76x76.png?v=OmmK4dNQkX

Binary file not shown.

Before

Width:  |  Height:  |  Size: 926 B

48
js/audio.min.js

@ -1,48 +0,0 @@
(function(audiojs,audiojsInstance,container){var path=(function(){var re=new RegExp('audio(\.min)?\.js.*'),scripts=document.getElementsByTagName('script');for(var i=0,ii=scripts.length;i<ii;i++){var path=scripts[i].getAttribute('src');if(re.test(path))return path.replace(re,'');}})();container[audiojs]={instanceCount:0,instances:{},flashSource:'\
<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" id="$1" width="1" height="1" name="$1" style="position: absolute; left: -1px;"> \
<param name="movie" value="$2?playerInstance='+audiojs+'.instances[\'$1\']&datetime=$3"> \
<param name="allowscriptaccess" value="always"> \
<embed name="$1" src="$2?playerInstance='+audiojs+'.instances[\'$1\']&datetime=$3" width="1" height="1" allowscriptaccess="always"> \
</object>',settings:{autoplay:false,loop:false,preload:true,imageLocation:path+'player-graphics.gif',swfLocation:path+'audiojs.swf',useFlash:(function(){var a=document.createElement('audio');return!(a.canPlayType&&a.canPlayType('audio/mpeg;').replace(/no/,''));})(),hasFlash:(function(){if(navigator.plugins&&navigator.plugins.length&&navigator.plugins['Shockwave Flash']){return true;}else if(navigator.mimeTypes&&navigator.mimeTypes.length){var mimeType=navigator.mimeTypes['application/x-shockwave-flash'];return mimeType&&mimeType.enabledPlugin;}else{try{var ax=new ActiveXObject('ShockwaveFlash.ShockwaveFlash');return true;}catch(e){}}
return false;})(),createPlayer:{markup:'\
<div class="play-pause"> \
<p class="play glyphicon glyphicon-play"></p> \
<p class="pause glyphicon glyphicon-pause"></p> \
<p class="loading glyphicon glyphicon-repeat"></p> \
<p class="error glyphicon glyphicon-remove"></p> \
</div> \
<div class="scrubber"> \
<div class="progress"></div> \
<div class="loaded"></div> \
</div> \
<div class="time"> \
<em class="played">00:00</em><strong class="duration">00:00</strong> \
</div> \
<div class="error-message"></div>',playPauseClass:'play-pause',scrubberClass:'scrubber',progressClass:'progress',loaderClass:'loaded',timeClass:'time',durationClass:'duration',playedClass:'played',errorMessageClass:'error-message',playingClass:'playing',loadingClass:'loading',errorClass:'error'},trackEnded:function(e){},flashError:function(){var player=this.settings.createPlayer,errorMessage=getByClass(player.errorMessageClass,this.wrapper),html='Missing <a href="http://get.adobe.com/flashplayer/">flash player</a> plugin.';if(this.mp3)html+=' <a href="'+this.mp3+'">Download audio file</a>.';container[audiojs].helpers.removeClass(this.wrapper,player.loadingClass);container[audiojs].helpers.addClass(this.wrapper,player.errorClass);errorMessage.innerHTML=html;},loadError:function(e){var player=this.settings.createPlayer,errorMessage=getByClass(player.errorMessageClass,this.wrapper);container[audiojs].helpers.removeClass(this.wrapper,player.loadingClass);container[audiojs].helpers.addClass(this.wrapper,player.errorClass);errorMessage.innerHTML='Error loading: "'+this.mp3+'"';},init:function(){var player=this.settings.createPlayer;container[audiojs].helpers.addClass(this.wrapper,player.loadingClass);},loadStarted:function(){var player=this.settings.createPlayer,duration=getByClass(player.durationClass,this.wrapper),m=Math.floor(this.duration/60),s=Math.floor(this.duration%60);container[audiojs].helpers.removeClass(this.wrapper,player.loadingClass);duration.innerHTML=((m<10?'0':'')+m+':'+(s<10?'0':'')+s);},loadProgress:function(percent){var player=this.settings.createPlayer,scrubber=getByClass(player.scrubberClass,this.wrapper),loaded=getByClass(player.loaderClass,this.wrapper);loaded.style.width=(scrubber.offsetWidth*percent)+'px';},playPause:function(){if(this.playing)this.settings.play();else this.settings.pause();},play:function(){var player=this.settings.createPlayer;container[audiojs].helpers.addClass(this.wrapper,player.playingClass);},pause:function(){var player=this.settings.createPlayer;container[audiojs].helpers.removeClass(this.wrapper,player.playingClass);},updatePlayhead:function(percent){var player=this.settings.createPlayer,scrubber=getByClass(player.scrubberClass,this.wrapper),progress=getByClass(player.progressClass,this.wrapper);progress.style.width=(scrubber.offsetWidth*percent)+'px';var played=getByClass(player.playedClass,this.wrapper),p=this.duration*percent,m=Math.floor(p/60),s=Math.floor(p%60);played.innerHTML=((m<10?'0':'')+m+':'+(s<10?'0':'')+s);}},create:function(element,options){var options=options||{}
if(element.length){return this.createAll(options,element);}else{return this.newInstance(element,options);}},createAll:function(options,elements){var audioElements=elements||document.getElementsByTagName('audio'),instances=[]
options=options||{};for(var i=0,ii=audioElements.length;i<ii;i++){instances.push(this.newInstance(audioElements[i],options));}
return instances;},newInstance:function(element,options){var element=element,s=this.helpers.clone(this.settings),id='audiojs'+this.instanceCount,wrapperId='audiojs_wrapper'+this.instanceCount,instanceCount=this.instanceCount++;if(element.getAttribute('autoplay')!=null)s.autoplay=true;if(element.getAttribute('loop')!=null)s.loop=true;if(element.getAttribute('preload')=='none')s.preload=false;if(options)this.helpers.merge(s,options);if(s.createPlayer.markup)element=this.createPlayer(element,s.createPlayer,wrapperId);else element.parentNode.setAttribute('id',wrapperId);var audio=new container[audiojsInstance](element,s);if(s.css)this.helpers.injectCss(audio,s.css);if(s.useFlash&&s.hasFlash){this.injectFlash(audio,id);this.attachFlashEvents(audio.wrapper,audio);}else if(s.useFlash&&!s.hasFlash){this.settings.flashError.apply(audio);}
if(!s.useFlash||(s.useFlash&&s.hasFlash))this.attachEvents(audio.wrapper,audio);this.instances[id]=audio;return audio;},createPlayer:function(element,player,id){var wrapper=document.createElement('div'),newElement=element.cloneNode(true);wrapper.setAttribute('class','audiojs');wrapper.setAttribute('className','audiojs');wrapper.setAttribute('id',id);if(newElement.outerHTML&&!document.createElement('audio').canPlayType){newElement=this.helpers.cloneHtml5Node(element);wrapper.innerHTML=player.markup;wrapper.appendChild(newElement);element.outerHTML=wrapper.outerHTML;wrapper=document.getElementById(id);}else{wrapper.appendChild(newElement);wrapper.innerHTML=wrapper.innerHTML+player.markup;element.parentNode.replaceChild(wrapper,element);}
return wrapper.getElementsByTagName('audio')[0];},attachEvents:function(wrapper,audio){if(!audio.settings.createPlayer)return;var player=audio.settings.createPlayer,playPause=getByClass(player.playPauseClass,wrapper),scrubber=getByClass(player.scrubberClass,wrapper),leftPos=function(elem){var curleft=0;if(elem.offsetParent){do{curleft+=elem.offsetLeft;}while(elem=elem.offsetParent);}
return curleft;};container[audiojs].events.addListener(playPause,'click',function(e){audio.playPause.apply(audio);});container[audiojs].events.addListener(scrubber,'click',function(e){var relativeLeft=e.clientX-leftPos(this);audio.skipTo(relativeLeft/scrubber.offsetWidth);});if(audio.settings.useFlash)return;container[audiojs].events.trackLoadProgress(audio);container[audiojs].events.addListener(audio.element,'timeupdate',function(e){audio.updatePlayhead.apply(audio);});container[audiojs].events.addListener(audio.element,'ended',function(e){audio.trackEnded.apply(audio);});container[audiojs].events.addListener(audio.source,'error',function(e){clearInterval(audio.readyTimer);clearInterval(audio.loadTimer);audio.settings.loadError.apply(audio);});},attachFlashEvents:function(element,audio){audio['swfReady']=false;audio['load']=function(mp3){audio.mp3=mp3;if(audio.swfReady)audio.element.load(mp3);}
audio['loadProgress']=function(percent,duration){audio.loadedPercent=percent;audio.duration=duration;audio.settings.loadStarted.apply(audio);audio.settings.loadProgress.apply(audio,[percent]);}
audio['skipTo']=function(percent){if(percent>audio.loadedPercent)return;audio.updatePlayhead.call(audio,[percent])
audio.element.skipTo(percent);}
audio['updatePlayhead']=function(percent){audio.settings.updatePlayhead.apply(audio,[percent]);}
audio['play']=function(){if(!audio.settings.preload){audio.settings.preload=true;audio.element.init(audio.mp3);}
audio.playing=true;audio.element.pplay();audio.settings.play.apply(audio);}
audio['pause']=function(){audio.playing=false;audio.element.ppause();audio.settings.pause.apply(audio);}
audio['setVolume']=function(v){audio.element.setVolume(v);}
audio['loadStarted']=function(){audio.swfReady=true;if(audio.settings.preload)audio.element.init(audio.mp3);if(audio.settings.autoplay)audio.play.apply(audio);}},injectFlash:function(audio,id){var flashSource=this.flashSource.replace(/\$1/g,id);flashSource=flashSource.replace(/\$2/g,audio.settings.swfLocation);flashSource=flashSource.replace(/\$3/g,(+new Date+Math.random()));var html=audio.wrapper.innerHTML,div=document.createElement('div');div.innerHTML=flashSource+html;audio.wrapper.innerHTML=div.innerHTML;audio.element=this.helpers.getSwf(id);},helpers:{merge:function(obj1,obj2){for(attr in obj2){if(obj1.hasOwnProperty(attr)||obj2.hasOwnProperty(attr)){obj1[attr]=obj2[attr];}}},clone:function(obj){if(obj==null||typeof(obj)!=='object')return obj;var temp=new obj.constructor();for(var key in obj)temp[key]=arguments.callee(obj[key]);return temp;},addClass:function(element,className){var re=new RegExp('(\\s|^)'+className+'(\\s|$)');if(re.test(element.className))return;element.className+=' '+className;},removeClass:function(element,className){var re=new RegExp('(\\s|^)'+className+'(\\s|$)');element.className=element.className.replace(re,' ');},injectCss:function(audio,string){var prepend='',styles=document.getElementsByTagName('style'),css=string.replace(/\$1/g,audio.settings.imageLocation);for(var i=0,ii=styles.length;i<ii;i++){var title=styles[i].getAttribute('title');if(title&&~title.indexOf('audiojs')){style=styles[i];if(style.innerHTML===css)return;prepend=style.innerHTML;break;}};var head=document.getElementsByTagName('head')[0],firstchild=head.firstChild,style=document.createElement('style');if(!head)return;style.setAttribute('type','text/css');style.setAttribute('title','audiojs');if(style.styleSheet)style.styleSheet.cssText=prepend+css;else style.appendChild(document.createTextNode(prepend+css));if(firstchild)head.insertBefore(style,firstchild);else head.appendChild(styleElement);},cloneHtml5Node:function(audioTag){var fragment=document.createDocumentFragment(),doc=fragment.createElement?fragment:document;doc.createElement('audio');var div=doc.createElement('div');fragment.appendChild(div);div.innerHTML=audioTag.outerHTML;return div.firstChild;},getSwf:function(name){var swf=document[name]||window[name];return swf.length>1?swf[swf.length-1]:swf;}},events:{memoryLeaking:false,listeners:[],addListener:function(element,eventName,func){if(element.addEventListener){element.addEventListener(eventName,func,false);}else if(element.attachEvent){this.listeners.push(element);if(!this.memoryLeaking){window.attachEvent('onunload',function(){if(this.listeners){for(var i=0,ii=this.listeners.length;i<ii;i++){container[audiojs].events.purge(this.listeners[i]);}}});this.memoryLeaking=true;}
element.attachEvent('on'+eventName,function(){func.call(element,window.event);});}},trackLoadProgress:function(audio){if(!audio.settings.preload)return;var readyTimer,loadTimer,audio=audio,ios=(/(ipod|iphone|ipad)/i).test(navigator.userAgent);readyTimer=setInterval(function(){if(audio.element.readyState>-1){if(!ios)audio.init.apply(audio);}
if(audio.element.readyState>1){if(audio.settings.autoplay)audio.play.apply(audio);clearInterval(readyTimer);loadTimer=setInterval(function(){audio.loadProgress.apply(audio);if(audio.loadedPercent>=1)clearInterval(loadTimer);});}},10);audio.readyTimer=readyTimer;audio.loadTimer=loadTimer;},purge:function(d){var a=d.attributes,i;if(a){for(i=0;i<a.length;i+=1){if(typeof d[a[i].name]==='function')d[a[i].name]=null;}}
a=d.childNodes;if(a){for(i=0;i<a.length;i+=1)purge(d.childNodes[i]);}},ready:(function(){return function(fn){var win=window,done=false,top=true,doc=win.document,root=doc.documentElement,add=doc.addEventListener?'addEventListener':'attachEvent',rem=doc.addEventListener?'removeEventListener':'detachEvent',pre=doc.addEventListener?'':'on',init=function(e){if(e.type=='readystatechange'&&doc.readyState!='complete')return;(e.type=='load'?win:doc)[rem](pre+e.type,init,false);if(!done&&(done=true))fn.call(win,e.type||e);},poll=function(){try{root.doScroll('left');}catch(e){setTimeout(poll,50);return;}
init('poll');};if(doc.readyState=='complete')fn.call(win,'lazy');else{if(doc.createEventObject&&root.doScroll){try{top=!win.frameElement;}catch(e){}
if(top)poll();}
doc[add](pre+'DOMContentLoaded',init,false);doc[add](pre+'readystatechange',init,false);win[add](pre+'load',init,false);}}})()}}
container[audiojsInstance]=function(element,settings){this.element=element;this.wrapper=element.parentNode;this.source=element.getElementsByTagName('source')[0]||element;this.mp3=(function(element){var source=element.getElementsByTagName('source')[0];return element.getAttribute('src')||(source?source.getAttribute('src'):null);})(element);this.settings=settings;this.loadStartedCalled=false;this.loadedPercent=0;this.duration=1;this.playing=false;}
container[audiojsInstance].prototype={updatePlayhead:function(){var percent=this.element.currentTime/this.duration;this.settings.updatePlayhead.apply(this,[percent]);},skipTo:function(percent){if(percent>this.loadedPercent)return;this.element.currentTime=this.duration*percent;this.updatePlayhead();},load:function(mp3){this.loadStartedCalled=false;this.source.setAttribute('src',mp3);this.element.load();this.mp3=mp3;container[audiojs].events.trackLoadProgress(this);},loadError:function(){this.settings.loadError.apply(this);},init:function(){this.settings.init.apply(this);},loadStarted:function(){if(!this.element.duration)return false;this.duration=this.element.duration;this.updatePlayhead();this.settings.loadStarted.apply(this);},loadProgress:function(){if(this.element.buffered!=null&&this.element.buffered.length){if(!this.loadStartedCalled){this.loadStartedCalled=this.loadStarted();}
var durationLoaded=this.element.buffered.end(this.element.buffered.length-1);this.loadedPercent=durationLoaded/this.duration;this.settings.loadProgress.apply(this,[this.loadedPercent]);}},playPause:function(){if(this.playing)this.pause();else this.play();},play:function(){var ios=(/(ipod|iphone|ipad)/i).test(navigator.userAgent);if(ios&&this.element.readyState==0)this.init.apply(this);if(!this.settings.preload){this.settings.preload=true;this.element.setAttribute('preload','auto');container[audiojs].events.trackLoadProgress(this);}
this.playing=true;this.element.play();this.settings.play.apply(this);},pause:function(){this.playing=false;this.element.pause();this.settings.pause.apply(this);},setVolume:function(v){this.element.volume=v;},trackEnded:function(e){this.skipTo.apply(this,[0]);if(!this.settings.loop)this.pause.apply(this);this.settings.trackEnded.apply(this);}}
var getByClass=function(searchClass,node){var matches=[];node=node||document;if(node.getElementsByClassName){matches=node.getElementsByClassName(searchClass);}else{var i,l,els=node.getElementsByTagName("*"),pattern=new RegExp("(^|\\s)"+searchClass+"(\\s|$)");for(i=0,l=els.length;i<l;i++){if(pattern.test(els[i].className)){matches.push(els[i]);}}}
return matches.length>1?matches:matches[0];};})('audiojs','audiojsInstance',this);

35
js/audiojs-doc-ready.js

@ -1,35 +0,0 @@
// initialise plugins
jQuery(document).ready(function($){
//alert('is running');
// Audio js
audiojs.events.ready(function() {
var as = audiojs.createAll();
});
});
jQuery(window).load(function($){
jQuery(".audiojs").each(function() {
var wi = jQuery(this).width();
if (wi <= 310){
jQuery(this).addClass('audioResize');
}else {
jQuery(this).removeClass('audioResize');
}
});
});
/*
$(window).on("resize load", function () {
var wi = $('#audiojs_wrapper0').width();
if (wi <= 267){
$('#audiojs_wrapper0').addClass('audioResize');
}
else {
$('#audiojs_wrapper0').removeClass('audioResize');
}
});
*/

2381
js/base-cachekey3894.css

File diff suppressed because it is too large

25
js/collapse-image.js

@ -1,25 +0,0 @@
$(document).ready(function(){
$(".div-collapse").click(function () {
//open up the content needed - toggle the slide- if visible, slide up, if not slidedown.
$(".caixa-collapse").slideToggle(2, function () {
//execute this after slideToggle is done
//change text of header based on visibility of content div
});
if ($("#img_click").attr("src") == "../../theme/ilb/pix/smarts/bg_saiba_mais_header2_curso.png") {
$('#img_click').attr('src','../../theme/ilb/pix/smarts/bg_saiba_mais_fechado2.png');
}
else{
$('#img_click').attr('src','../../theme/ilb/pix/smarts/bg_saiba_mais_header2_curso.png');
$(".caixa-collapse").css("margin-top", "0");
}
$header = $(this);
//getting the next element
});
});

10253
js/jquery.js

File diff suppressed because it is too large

2
js/jquery.lazyload.min.js

@ -1,2 +0,0 @@
/*! Lazy Load 1.9.3 - MIT license - Copyright 2010-2013 Mika Tuupola */
!function(a,b,c,d){var e=a(b);a.fn.lazyload=function(f){function g(){var b=0;i.each(function(){var c=a(this);if(!j.skip_invisible||c.is(":visible"))if(a.abovethetop(this,j)||a.leftofbegin(this,j));else if(a.belowthefold(this,j)||a.rightoffold(this,j)){if(++b>j.failure_limit)return!1}else c.trigger("appear"),b=0})}var h,i=this,j={threshold:0,failure_limit:0,event:"scroll",effect:"show",container:b,data_attribute:"original",skip_invisible:!0,appear:null,load:null,placeholder:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC"};return f&&(d!==f.failurelimit&&(f.failure_limit=f.failurelimit,delete f.failurelimit),d!==f.effectspeed&&(f.effect_speed=f.effectspeed,delete f.effectspeed),a.extend(j,f)),h=j.container===d||j.container===b?e:a(j.container),0===j.event.indexOf("scroll")&&h.bind(j.event,function(){return g()}),this.each(function(){var b=this,c=a(b);b.loaded=!1,(c.attr("src")===d||c.attr("src")===!1)&&c.is("img")&&c.attr("src",j.placeholder),c.one("appear",function(){if(!this.loaded){if(j.appear){var d=i.length;j.appear.call(b,d,j)}a("<img />").bind("load",function(){var d=c.attr("data-"+j.data_attribute);c.hide(),c.is("img")?c.attr("src",d):c.css("background-image","url('"+d+"')"),c[j.effect](j.effect_speed),b.loaded=!0;var e=a.grep(i,function(a){return!a.loaded});if(i=a(e),j.load){var f=i.length;j.load.call(b,f,j)}}).attr("src",c.attr("data-"+j.data_attribute))}}),0!==j.event.indexOf("scroll")&&c.bind(j.event,function(){b.loaded||c.trigger("appear")})}),e.bind("resize",function(){g()}),/(?:iphone|ipod|ipad).*os 5/gi.test(navigator.appVersion)&&e.bind("pageshow",function(b){b.originalEvent&&b.originalEvent.persisted&&i.each(function(){a(this).trigger("appear")})}),a(c).ready(function(){g()}),this},a.belowthefold=function(c,f){var g;return g=f.container===d||f.container===b?(b.innerHeight?b.innerHeight:e.height())+e.scrollTop():a(f.container).offset().top+a(f.container).height(),g<=a(c).offset().top-f.threshold},a.rightoffold=function(c,f){var g;return g=f.container===d||f.container===b?e.width()+e.scrollLeft():a(f.container).offset().left+a(f.container).width(),g<=a(c).offset().left-f.threshold},a.abovethetop=function(c,f){var g;return g=f.container===d||f.container===b?e.scrollTop():a(f.container).offset().top,g>=a(c).offset().top+f.threshold+a(c).height()},a.leftofbegin=function(c,f){var g;return g=f.container===d||f.container===b?e.scrollLeft():a(f.container).offset().left,g>=a(c).offset().left+f.threshold+a(c).width()},a.inviewport=function(b,c){return!(a.rightoffold(b,c)||a.leftofbegin(b,c)||a.belowthefold(b,c)||a.abovethetop(b,c))},a.extend(a.expr[":"],{"below-the-fold":function(b){return a.belowthefold(b,{threshold:0})},"above-the-top":function(b){return!a.belowthefold(b,{threshold:0})},"right-of-screen":function(b){return a.rightoffold(b,{threshold:0})},"left-of-screen":function(b){return!a.rightoffold(b,{threshold:0})},"in-viewport":function(b){return a.inviewport(b,{threshold:0})},"above-the-fold":function(b){return!a.belowthefold(b,{threshold:0})},"right-of-fold":function(b){return a.rightoffold(b,{threshold:0})},"left-of-fold":function(b){return!a.rightoffold(b,{threshold:0})}})}(jQuery,window,document);

11
js/manifest.json?v=697oma3x8q

@ -1,11 +0,0 @@
{
"icons": [
{
"src": "\/noticias\/++resource++senado.noticias\/favicon\/android-chrome-192x192.png?v=697oma3x8q",
"sizes": "192x192",
"type": "image\/png"
}
],
"theme_color": "#00305c",
"display": "browser"
}

169
js/resourceconfigbootstrapbootstrap.min-cachekey1567.js

File diff suppressed because one or more lines are too long

335
js/resourceconfigjqueryjquery-1.11.2.min-cachekey3986.js

File diff suppressed because one or more lines are too long

2208
js/resourcesenado.jqueryjquery-cachekey5865.js

File diff suppressed because one or more lines are too long

85
js/resourcestaticjscalendario-cachekey4269.js

@ -1,85 +0,0 @@
/* - ++resource++static/js/calendario.js - */
$(document).ready(function(){
// var $ = $18; // version 1.8
// alert("[window.$, $]: " + window.$(document).jquery + ", " + $(document).jquery)
// Agenda
configCalendar();
// Notícia
configNoticia();
});
////////////////////////////////////////////////////////////////////////////
// Configuração do calendário para a utilização da agenda.
var calendarMesAtual = "";
// Configuração do calendário.
function configCalendar() {
mes = $(".portletCalendar .portletHeader").text();
if (mes == calendarMesAtual) {
return true;
}
// O mês foi alterado.
configCalendarLinks();
return false;
}
// Configuração de links para a agenda.
function configCalendarLinks() {
// Modifica o endereço dos links para apontarem para a view.
$("table.ploneCalendar a").each(function(index) {
var linkAgenda = $(this);
var endereco = linkAgenda.attr('href');
var texto = $(this).attr('title');
var data = texto.substring(0, 10);
var parts = endereco.split("/search");
var urlPresidencia = parts[0] + "/eventos";
var novoEndereco = urlPresidencia + "/pesquisaagenda?data=" + data;
linkAgenda.attr("href", novoEndereco);
});
// Tratamento dos eventos de mudança de mês (previous e next).
$(".portletCalendar a#calendar-previous, .portletCalendar a#calendar-next").each(function(index) {
$(this).click(function() {
calendarMesAtual = $(".portletCalendar .portletHeader").text();
serviceTimer(configCalendar, 1000);
});
});
}
// Configuração da view de notícia.
// Remove a imagem de upload na view de detalhamento da notícia.
function configNoticia() {
imgNoticia = $("div.newsImageContainer");
imgNoticia.remove();
sumario = $("div#parent-fieldname-description");
sumario.remove();
$(".newsDataPublicacao").after($('<div />').load('socialshare?NoticiaID='+ window.location));
}
function Imprimir(objeto){
var printContent = document.getElementById(objeto);
var parametrosExibicaoJanela = 'left=250,top=150,width=800,height=800px,toolbar=0,scrollbars=0,status=0,directories=no, menubar=no, resizable=no';
var winPrint = window.open('', '', parametrosExibicaoJanela);
winPrint.document.write(printContent.innerHTML);
var printIconesAcessibilidade = winPrint.document.getElementById('socialshare');
printIconesAcessibilidade.innerHTML = "";
winPrint.document.close();
winPrint.focus();
winPrint.print();
winPrint.close();
}
function EnviarEmail(objeto){
titulo = objeto.title;
endereco = objeto.location;
window.open("mailto:?Subject=" + titulo + "&body=" + titulo + escape('\n') + endereco);
}

68
js/resourcestaticjsphoto-gallery-cachekey3318.js

@ -1,68 +0,0 @@
/* - ++resource++static/js/photo-gallery.js - */
$(document).ready(function(){
$('li img').on('click',function(){
var src = $(this).attr('src');
var img = '<img src="' + src + '" class="img-responsive"/>';
//start of new code new code
var index = $(this).parent('li').index();
var html = '';
html += img;
html += '<div style="height:25px;clear:both;display:block;">';
html += '<a class="controls next" href="'+ (index+2) + '">próximo </a>';
html += '<a class="controls previous" href="' + (index) + '"> anterior</a>';
html += '</div>';
$('#myModal').modal();
$('#myModal').on('shown.bs.modal', function(){
$('#myModal .modal-body').html(html);
//new code
$('a.controls').trigger('click');
})
$('#myModal').on('hidden.bs.modal', function(){
$('#myModal .modal-body').html('');
});
});
})
$(document).on('click', 'a.controls', function(){
var index = $(this).attr('href');
var src = $('ul.row li:nth-child('+ index +') img').attr('src');
$('.modal-body img').attr('src', src);
var newPrevIndex = parseInt(index) - 1;
var newNextIndex = parseInt(newPrevIndex) + 2;
if($(this).hasClass('previous')){
$(this).attr('href', newPrevIndex);
$('a.next').attr('href', newNextIndex);
}else{
$(this).attr('href', newNextIndex);
$('a.previous').attr('href', newPrevIndex);
}
var total = $('ul.row li').length + 1;
//hide next button
if(total === newNextIndex){
$('a.next').hide();
}else{
$('a.next').show()
}
//hide previous button
if(newPrevIndex === 0){
$('a.previous').hide();
}else{
$('a.previous').show()
}
return false;
});

22
js/root_index.js

@ -1,22 +0,0 @@
/* global jQuery */
(function($) {
$(function() {
var carousel = $('#carrosselImagensSenado')
$("img.lazyload").lazyload({
event : "cliqueseta"
});
var carregaImagem = function() {
carousel.find('.item.active img.lazyload').trigger("cliqueseta")
}
carousel.on('slid.bs.carousel', function () {
carregaImagem();
})
carregaImagem();
});
})(jQuery);

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save