Compare commits

...

47 Commits

Author SHA1 Message Date
Matheus Garcia 2d30d1988b Retirada de link para certificados antigos (ambiente Trilhas) 3 years ago
Matheus Garcia ff718ebefa Alteração em upgrade.txt 4 years ago
Matheus e4b3034217 Atualização em vídeo e mensagens do FAQ 5 years ago
MateusO97 530f6eed04 Alterando mensagem de inscrever para inscreva-se 5 years ago
thiagourias 8ae94adcd6 Alteracao min-height bg course 5 years ago
thiagourias 4142f176e7 Correcoes mensagem mais informacoes cursos 5 years ago
Matheus Garcia 42ec6f8e5a correção em padding 5 years ago
thiagourias de6eba99ee Alteracoes card 5 years ago
Matheus Garcia ba6d101f3c Adaptação para exibir sumário ao lado da caixa do curso 5 years ago
Matheus Garcia 1890ae202f Troca de imagem de destaque do engitec 5 years ago
thiagourias a97d46b0f5 Incorporando alterações feitas por @thiagourias 5 years ago
Matheus Garcia d99a125372 Parametrização para exibir curso em destaque na página inicial 5 years ago
Matheus Garcia ebe4923151 Merge branch 'MOODLE_37_STABLE' of https://github.com/interlegis/moodle_theme_ilb into MOODLE_37_STABLE 6 years ago
Matheus Garcia e2aaeb114a Incorporando alterações do Boost 2019052000 6 years ago
thiagourias 102f670bd0 Cache msg 6 years ago
Matheus Garcia c85467541b Inclusão de link em botão de inscrição 6 years ago
Matheus Garcia 401d1ff93a Omitindo nome dos tutores dos cards 6 years ago
Matheus Garcia c38ceac26a Merge branch 'MOODLE_37_STABLE' of https://github.com/interlegis/moodle_theme_ilb into MOODLE_37_STABLE 6 years ago
thiagourias 59846a6529 Alteracoes classse box 6 years ago
thiagourias c3c9a1ab99 Remoção icone inscrever 6 years ago
thiagourias af72b75a97 Alteracoes card 6 years ago
thiagourias 79b3549db4 Remoção de sombra e alteração da letra - cursos 6 years ago
thiagourias 5ba286d185 Correcao popover responsivo 6 years ago
thiagourias 40fd28d73e Alteracoes href 6 years ago
thiagourias 122c74aad9 Alteracoes layout 6 years ago
thiagourias fb9f847692 Alteracoes Button mais informações 6 years ago
Matheus Garcia d8fc551ecf Merge branch 'MOODLE_37_STABLE' of https://github.com/interlegis/moodle_theme_ilb into MOODLE_37_STABLE 6 years ago
Matheus Garcia c8eafa8e59 Retirada de imagem da página columns2 6 years ago
thiagourias d8338335c8 giMerge branch 'MOODLE_37_STABLE' of https://github.com/interlegis/moodle_theme_ilb into MOODLE_37_STABLE 6 years ago
thiagourias b49dd3ac03 Inclusão de hover nas boxes cursos 6 years ago
Matheus Garcia d85caa732e Ajuste em largura de popover 6 years ago
thiagourias cde357f2eb Alteracoes imagens cursos 6 years ago
thiagourias fc6fe248e9 Alteracoes responsividade 6 years ago
thiagourias fcb80bcd2e Alteracoes cards 6 years ago
Matheus Garcia 9a0a838f12 Adaptação na lista de cursos 6 years ago
Matheus Garcia cac2cf0c0b Mudança na visualização de cursos por categoria 6 years ago
Matheus Garcia c0ce11e4a2 Ajuste em tamanho de imagem na página de cursos 6 years ago
Matheus Garcia 01715a5e59 Início de ajustes para inclusão de imagens na página do curso 6 years ago
thiagourias 1121f04b2a Alteracoes tamanho 6 years ago
thiagourias af8ed07a7b Alterações width 6 years ago
thiagourias 9a4ca44bd6 Alteraçoes tamanho 6 years ago
thiagourias 70ca983f23 Correcao 6 years ago
thiagourias e3a02cd893 correcoes botoes 6 years ago
thiagourias c984eed920 Alteracoes login footer e navbar 6 years ago
thiagourias e3310bb470 Correcoes navbar 6 years ago
Matheus Garcia 4bced6d898 Passagem de logintoken na página principal (exigência de segurança do Moodle 3.6) 6 years ago
thiagourias bcfe236430 Alteracoes moodle 3.6 6 years ago
  1. 6
      .idea/vcs.xml
  2. 1
      amd/build/alert.min.js
  3. 1
      amd/build/aria.min.js
  4. 1
      amd/build/button.min.js
  5. 1
      amd/build/carousel.min.js
  6. 1
      amd/build/collapse.min.js
  7. 1
      amd/build/drawer.min.js
  8. 1
      amd/build/dropdown.min.js
  9. 1
      amd/build/form-display-errors.min.js
  10. 1
      amd/build/loader.min.js
  11. 1
      amd/build/modal.min.js
  12. 1
      amd/build/pending.min.js
  13. 1
      amd/build/popover.min.js
  14. 1
      amd/build/scrollspy.min.js
  15. 1
      amd/build/tab.min.js
  16. 1
      amd/build/tether.min.js
  17. 1
      amd/build/tooltip.min.js
  18. 1
      amd/build/util.min.js
  19. 216
      amd/src/alert.js
  20. 201
      amd/src/aria.js
  21. 200
      amd/src/button.js
  22. 589
      amd/src/carousel.js
  23. 455
      amd/src/collapse.js
  24. 181
      amd/src/drawer.js
  25. 549
      amd/src/dropdown.js
  26. 65
      amd/src/form-display-errors.js
  27. 76
      amd/src/loader.js
  28. 662
      amd/src/modal.js
  29. 120
      amd/src/pending.js
  30. 297
      amd/src/popover.js
  31. 390
      amd/src/scrollspy.js
  32. 289
      amd/src/tab.js
  33. 1792
      amd/src/tether.js
  34. 762
      amd/src/tooltip.js
  35. 156
      amd/src/util.js
  36. 8
      classes/admin_settingspage_tabs.php
  37. 8
      classes/autoprefixer.php
  38. 50
      classes/output/block_settings_renderer.php
  39. 52
      classes/output/core/admin_renderer.php
  40. 453
      classes/output/core/course_renderer.php
  41. 140
      classes/output/core/files_renderer.php
  42. 58
      classes/output/core_question/bank_renderer.php
  43. 680
      classes/output/core_renderer.php
  44. 197
      classes/output/core_renderer_maintenance.php
  45. 44
      classes/output/gradereport_history_renderer.php
  46. 52
      classes/privacy/provider.php
  47. 106
      cli/import-bootswatch.php
  48. 165
      config.php
  49. 64
      lang/en/theme_ilb.php
  50. 5
      layout/columns1.php
  51. 8
      layout/columns2.php
  52. 15
      layout/frontpage_ilb.php
  53. 5
      layout/login.php
  54. 36
      layout/login_nova_conta.php
  55. 188
      lib.php
  56. BIN
      pix/engitec.jpg
  57. 2
      pix/fp/th.svg
  58. BIN
      pix/screenshot.jpg
  59. 35
      readme_moodle.txt
  60. 2
      scss/bootstrap.scss
  61. 4
      scss/bootstrap/LICENSE
  62. 29
      scss/bootstrap/_alert.scss
  63. 47
      scss/bootstrap/_badge.scss
  64. 8
      scss/bootstrap/_breadcrumb.scss
  65. 158
      scss/bootstrap/_button-group.scss
  66. 147
      scss/bootstrap/_buttons.scss
  67. 294
      scss/bootstrap/_card.scss
  68. 288
      scss/bootstrap/_carousel.scss
  69. 17
      scss/bootstrap/_close.scss
  70. 19
      scss/bootstrap/_code.scss
  71. 270
      scss/bootstrap/_custom-forms.scss
  72. 176
      scss/bootstrap/_dropdown.scss
  73. 271
      scss/bootstrap/_forms.scss
  74. 86
      scss/bootstrap/_functions.scss
  75. 17
      scss/bootstrap/_grid.scss
  76. 18
      scss/bootstrap/_images.scss
  77. 241
      scss/bootstrap/_input-group.scss
  78. 4
      scss/bootstrap/_jumbotron.scss
  79. 126
      scss/bootstrap/_list-group.scss
  80. 83
      scss/bootstrap/_media.scss
  81. 24
      scss/bootstrap/_mixins.scss
  82. 86
      scss/bootstrap/_modal.scss
  83. 105
      scss/bootstrap/_nav.scss
  84. 351
      scss/bootstrap/_navbar.scss
  85. 78
      scss/bootstrap/_pagination.scss
  86. 250
      scss/bootstrap/_popover.scss
  87. 42
      scss/bootstrap/_print.scss
  88. 146
      scss/bootstrap/_progress.scss
  89. 367
      scss/bootstrap/_reboot.scss
  90. 19
      scss/bootstrap/_root.scss
  91. 134
      scss/bootstrap/_tables.scss
  92. 124
      scss/bootstrap/_tooltip.scss
  93. 36
      scss/bootstrap/_transitions.scss
  94. 55
      scss/bootstrap/_type.scss
  95. 7
      scss/bootstrap/_utilities.scss
  96. 1215
      scss/bootstrap/_variables.scss
  97. 35
      scss/bootstrap/bootstrap-grid.scss
  98. 17
      scss/bootstrap/bootstrap-reboot.scss
  99. 28
      scss/bootstrap/bootstrap.scss
  100. 11
      scss/bootstrap/mixins/_alert.scss

6
.idea/vcs.xml

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

1
amd/build/alert.min.js

@ -0,0 +1 @@
define(["exports","jquery","./util"],function(a,b,c){"use strict";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;c<b.length;c++){var d=b[c];d.enumerable=d.enumerable||!1,d.configurable=!0,"value"in d&&(d.writable=!0),Object.defineProperty(a,d.key,d)}}function g(a,b,c){return b&&f(a.prototype,b),c&&f(a,c),a}Object.defineProperty(a,"__esModule",{value:!0});var h=d(b),i=d(c),j=function(a){var b="alert",c="4.0.0",d="bs.alert",f=".".concat(d),h=".data-api",j=a.fn[b],k=150,l={DISMISS:'[data-dismiss="alert"]'},m={CLOSE:"close".concat(f),CLOSED:"closed".concat(f),CLICK_DATA_API:"click".concat(f).concat(h)},n={ALERT:"alert",FADE:"fade",SHOW:"show"},o=function(){function b(a){e(this,b),this._element=a}return g(b,[{key:"close",value:function(a){a=a||this._element;var b=this._getRootElement(a),c=this._triggerCloseEvent(b);c.isDefaultPrevented()||this._removeElement(b)}},{key:"dispose",value:function(){a.removeData(this._element,d),this._element=null}},{key:"_getRootElement",value:function(b){var c=i["default"].getSelectorFromElement(b),d=!1;return c&&(d=a(c)[0]),d||(d=a(b).closest(".".concat(n.ALERT))[0]),d}},{key:"_triggerCloseEvent",value:function(b){var c=a.Event(m.CLOSE);return a(b).trigger(c),c}},{key:"_removeElement",value:function(b){var c=this;return a(b).removeClass(n.SHOW),i["default"].supportsTransitionEnd()&&a(b).hasClass(n.FADE)?void a(b).one(i["default"].TRANSITION_END,function(a){return c._destroyElement(b,a)}).emulateTransitionEnd(k):void this._destroyElement(b)}},{key:"_destroyElement",value:function(b){a(b).detach().trigger(m.CLOSED).remove()}}],[{key:"_jQueryInterface",value:function(c){return this.each(function(){var e=a(this),f=e.data(d);f||(f=new b(this),e.data(d,f)),"close"===c&&f[c](this)})}},{key:"_handleDismiss",value:function(a){return function(b){b&&b.preventDefault(),a.close(this)}}},{key:"VERSION",get:function(){return c}}]),b}();return a(document).on(m.CLICK_DATA_API,l.DISMISS,o._handleDismiss(new o)),a.fn[b]=o._jQueryInterface,a.fn[b].Constructor=o,a.fn[b].noConflict=function(){return a.fn[b]=j,o._jQueryInterface},o}(h["default"]);a["default"]=j});

1
amd/build/aria.min.js

@ -0,0 +1 @@
define(["jquery","core/pending"],function(a,b){return{init:function(){var c=!1,d=function(){c=!0},e=function(){var a=c;return c=!1,a};a('[data-toggle="dropdown"]').keydown(function(b){var c,e=b.which||b.keyCode;38==e&&d(),27==e&&(c=a(b.target).attr("aria-expanded"),b.preventDefault(),"false"==c&&a(b.target).click()),32!=e&&13!=e||(b.preventDefault(),a(b.target).click())});var f=function(c){var d=function(b){a(this).focus(),b.resolve()}.bind(c);setTimeout(d,50,new b("core/aria:delayed-focus"))};a(".dropdown").on("shown.bs.dropdown",function(b){var c=a(b.target).find('[role="menu"]'),d=!1,g=!1;c&&(d=a(c).find('[role="menuitem"]')),d&&d.length>0&&(g=e()?d[d.length-1]:d[0]),g&&f(g)}),a('.dropdown [role="menu"] [role="menuitem"]').keypress(function(b){var c,d,e=String.fromCharCode(b.which||b.keyCode),g=a(b.target).closest('[role="menu"]'),h=0,i=!1;if(g&&(i=a(g).find('[role="menuitem"]')))for(e=e.toLowerCase(),h=0;h<i.length;h++)if(c=a(i[h]),d=c.text().trim().toLowerCase(),0==d.indexOf(e)){f(c);break}}),a('.dropdown [role="menu"] [role="menuitem"]').keydown(function(b){var c=b.which||b.keyCode,d=!1,e=a(b.target).closest('[role="menu"]'),g=0,h=!1;if(e&&(h=a(e).find('[role="menuitem"]'))){if(40==c){for(g=0;g<h.length-1;g++)if(h[g]==b.target){d=h[g+1];break}d||(d=h[0])}else if(38==c){for(g=1;g<h.length;g++)if(h[g]==b.target){d=h[g-1];break}d||(d=h[h.length-1])}else 36==c?d=h[0]:35==c&&(d=h[h.length-1]);d&&(b.preventDefault(),f(d))}}),a(".dropdown").on("hidden.bs.dropdown",function(b){var c=a(b.target).find('[data-toggle="dropdown"]');c&&f(c)}),a(function(){window.setTimeout(function(b){var c=a('[role="alert"][data-aria-autofocus="true"]');c.length>0&&(a(c[0]).attr("tabindex","0"),a(c[0]).focus()),b.resolve()},300,new b("core/aria:delayed-focus"))})}}});

1
amd/build/button.min.js

@ -0,0 +1 @@
define(["exports","jquery"],function(a,b){"use strict";function c(a){return a&&a.__esModule?a:{"default":a}}function d(a,b){if(!(a instanceof b))throw new TypeError("Cannot call a class as a function")}function e(a,b){for(var c=0;c<b.length;c++){var d=b[c];d.enumerable=d.enumerable||!1,d.configurable=!0,"value"in d&&(d.writable=!0),Object.defineProperty(a,d.key,d)}}function f(a,b,c){return b&&e(a.prototype,b),c&&e(a,c),a}Object.defineProperty(a,"__esModule",{value:!0});var g=c(b),h=function(a){var b="button",c="4.0.0",e="bs.button",g=".".concat(e),h=".data-api",i=a.fn[b],j={ACTIVE:"active",BUTTON:"btn",FOCUS:"focus"},k={DATA_TOGGLE_CARROT:'[data-toggle^="button"]',DATA_TOGGLE:'[data-toggle="buttons"]',INPUT:"input",ACTIVE:".active",BUTTON:".btn"},l={CLICK_DATA_API:"click".concat(g).concat(h),FOCUS_BLUR_DATA_API:"focus".concat(g).concat(h," ")+"blur".concat(g).concat(h)},m=function(){function b(a){d(this,b),this._element=a}return f(b,[{key:"toggle",value:function(){var b=!0,c=!0,d=a(this._element).closest(k.DATA_TOGGLE)[0];if(d){var e=a(this._element).find(k.INPUT)[0];if(e){if("radio"===e.type)if(e.checked&&a(this._element).hasClass(j.ACTIVE))b=!1;else{var f=a(d).find(k.ACTIVE)[0];f&&a(f).removeClass(j.ACTIVE)}if(b){if(e.hasAttribute("disabled")||d.hasAttribute("disabled")||e.classList.contains("disabled")||d.classList.contains("disabled"))return;e.checked=!a(this._element).hasClass(j.ACTIVE),a(e).trigger("change")}e.focus(),c=!1}}c&&this._element.setAttribute("aria-pressed",!a(this._element).hasClass(j.ACTIVE)),b&&a(this._element).toggleClass(j.ACTIVE)}},{key:"dispose",value:function(){a.removeData(this._element,e),this._element=null}}],[{key:"_jQueryInterface",value:function(c){return this.each(function(){var d=a(this).data(e);d||(d=new b(this),a(this).data(e,d)),"toggle"===c&&d[c]()})}},{key:"VERSION",get:function(){return c}}]),b}();return a(document).on(l.CLICK_DATA_API,k.DATA_TOGGLE_CARROT,function(b){b.preventDefault();var c=b.target;a(c).hasClass(j.BUTTON)||(c=a(c).closest(k.BUTTON)),m._jQueryInterface.call(a(c),"toggle")}).on(l.FOCUS_BLUR_DATA_API,k.DATA_TOGGLE_CARROT,function(b){var c=a(b.target).closest(k.BUTTON)[0];a(c).toggleClass(j.FOCUS,/^focus(in)?$/.test(b.type))}),a.fn[b]=m._jQueryInterface,a.fn[b].Constructor=m,a.fn[b].noConflict=function(){return a.fn[b]=i,m._jQueryInterface},m}(g["default"]);a["default"]=h});

1
amd/build/carousel.min.js

File diff suppressed because one or more lines are too long

1
amd/build/collapse.min.js

File diff suppressed because one or more lines are too long

1
amd/build/drawer.min.js

@ -0,0 +1 @@
define(["jquery","core/custom_interaction_events","core/log","core/pubsub"],function(a,b,c,d){var e={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"},f=a(document).width()<768,g=function(){a(e.TOGGLE_REGION).length||c.debug("Page is missing a drawer region"),a(e.TOGGLE_ACTION).length||c.debug("Page is missing a drawer toggle link"),a(e.TOGGLE_REGION).each(function(b,c){var d=a(c).find(e.TOGGLE_ACTION),g=d.attr("aria-controls"),h=a(document.getElementById(g)),i="false"==d.attr("aria-expanded"),j=d.attr("data-side"),k=a(e.BODY),l=d.attr("data-preference");f&&M.util.set_user_preference(l,"false"),h.on("mousewheel DOMMouseScroll",this.preventPageScroll),i?d.attr("aria-expanded","false"):(k.addClass("drawer-open-"+j),d.attr("aria-expanded","true"))}.bind(this)),this.registerEventListeners(),f&&this.closeAll()};return g.prototype.closeAll=function(){a(e.TOGGLE_REGION).each(function(b,c){var d=a(c).find(e.TOGGLE_ACTION),g=d.attr("data-side"),h=a(e.BODY),i=d.attr("aria-controls"),j=a(document.getElementById(i)),k=d.attr("data-preference");d.attr("aria-expanded","false"),h.removeClass("drawer-open-"+g),j.attr("aria-hidden","true"),j.addClass("closed"),f||M.util.set_user_preference(k,"false")})},g.prototype.toggleDrawer=function(b){var c=a(b.target).closest("[data-action=toggle-drawer]"),g=c.attr("aria-controls"),h=a(document.getElementById(g)),i=a(e.BODY),j=c.attr("data-side"),k=c.attr("data-preference");f&&M.util.set_user_preference(k,"false"),i.addClass("drawer-ease");var l="true"==c.attr("aria-expanded");l?(i.removeClass("drawer-open-"+j),c.attr("aria-expanded","false"),h.attr("aria-hidden","true"),h.addClass("closed"),f||M.util.set_user_preference(k,"false")):(c.attr("aria-expanded","true"),h.attr("aria-hidden","false"),h.focus(),i.addClass("drawer-open-"+j),h.removeClass("closed"),f||M.util.set_user_preference(k,"true")),d.publish("nav-drawer-toggle-start",l)},g.prototype.preventPageScroll=function(b){var c=b.wheelDelta||b.originalEvent&&b.originalEvent.wheelDelta||-b.originalEvent.detail,d=this.scrollTop+a(this).outerHeight()-this.scrollHeight>=0,e=this.scrollTop<=0;(c<0&&d||c>0&&e)&&b.preventDefault()},g.prototype.registerEventListeners=function(){a(e.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(e.SECTION).click(function(){f&&this.closeAll()}.bind(this)),a(e.DRAWER).on("webkitTransitionEnd msTransitionEnd transitionend",function(b){var c=a(b.target).closest(e.DRAWER),f="false"==c.attr("aria-hidden");d.publish("nav-drawer-toggle-end",f)})},{init:function(){return new g}}});

1
amd/build/dropdown.min.js

File diff suppressed because one or more lines are too long

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

@ -0,0 +1 @@
define(["jquery","core/event"],function(a,b){return{enhance:function(c){var d=document.getElementById(c);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");"TEXTAREA"==a(d).prop("tagName")&&e.find("[contenteditable]")&&(d=e.find("[contenteditable]")),""!==c?(e.addClass("has-danger"),e.data("client-validation-error",!0),a(d).addClass("is-invalid"),a(d).attr("aria-describedby",f.attr("id")),a(d).attr("aria-invalid",!0),f.attr("tabindex",0),f.html(c),f.is(":visible")||(f.show(),f.focus())):e.data("client-validation-error")===!0&&(e.removeClass("has-danger"),e.data("client-validation-error",!1),a(d).removeClass("is-invalid"),a(d).removeAttr("aria-describedby"),a(d).attr("aria-invalid",!1),f.hide())})}}});

1
amd/build/loader.min.js

@ -0,0 +1 @@
define(["jquery","./tether","core/event"],function(a,b,c){return window.jQuery=a,window.Tether=b,require(["theme_ilb/aria","theme_ilb/pending","theme_ilb/util","theme_ilb/alert","theme_ilb/button","theme_ilb/carousel","theme_ilb/collapse","theme_ilb/dropdown","theme_ilb/modal","theme_ilb/scrollspy","theme_ilb/tab","theme_ilb/tooltip","theme_ilb/popover"],function(b){a("body").popover({trigger:"focus",selector:"[data-toggle=popover][data-trigger!=hover]"}),a("html").popover({container:"body",selector:"[data-toggle=popover][data-trigger=hover]",trigger:"hover",delay:{hide:500}}),c.getLegacyEvents().done(function(b){a(document).on(b.FILTER_CONTENT_UPDATED,function(){a("body").popover({selector:'[data-toggle="popover"]',trigger:"focus"})})}),b.init()}),{}});

1
amd/build/modal.min.js

File diff suppressed because one or more lines are too long

1
amd/build/pending.min.js

@ -0,0 +1 @@
define(["jquery"],function(a){var b={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"}],tooltip:[{start:"hide",end:"hidden"},{start:"show",end:"shown"}]};Object.keys(b).forEach(function(c){b[c].forEach(function(b){var d=b.start+".bs."+c,e=b.end+".bs."+c;a(document.body).on(d,function(){M.util.js_pending(e)}),a(document.body).on(e,function(){M.util.js_complete(e)})})})});

1
amd/build/popover.min.js

@ -0,0 +1 @@
define(["exports","jquery","./tooltip"],function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{"default":a}}function e(a){return(e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(a){return typeof a}:function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a})(a)}function f(a,b){if(!(a instanceof b))throw new TypeError("Cannot call a class as a function")}function g(a,b){for(var c=0;c<b.length;c++){var d=b[c];d.enumerable=d.enumerable||!1,d.configurable=!0,"value"in d&&(d.writable=!0),Object.defineProperty(a,d.key,d)}}function h(a,b,c){return b&&g(a.prototype,b),c&&g(a,c),a}function i(a,b){if(b&&("object"===e(b)||"function"==typeof b))return b;if(void 0===a)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return a}function j(a,b){if("function"!=typeof b&&null!==b)throw new TypeError("Super expression must either be null or a function");a.prototype=Object.create(b&&b.prototype,{constructor:{value:a,enumerable:!1,writable:!0,configurable:!0}}),b&&(Object.setPrototypeOf?Object.setPrototypeOf(a,b):a.__proto__=b)}function k(){return k=Object.assign||function(a){for(var b=1;b<arguments.length;b++){var c=arguments[b];for(var d in c)Object.prototype.hasOwnProperty.call(c,d)&&(a[d]=c[d])}return a},k.apply(this,arguments)}Object.defineProperty(a,"__esModule",{value:!0});var l=d(b),m=d(c),n=function(a){var b="popover",c="4.0.0",d="bs.popover",g=".".concat(d),l=a.fn[b],n="bs-popover",o=new RegExp("(^|\\s)".concat(n,"\\S+"),"g"),p=k({},m["default"].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>'}),q=k({},m["default"].DefaultType,{content:"(string|element|function)"}),r={FADE:"fade",SHOW:"show"},s={TITLE:".popover-header",CONTENT:".popover-body"},t={HIDE:"hide".concat(g),HIDDEN:"hidden".concat(g),SHOW:"show".concat(g),SHOWN:"shown".concat(g),INSERTED:"inserted".concat(g),CLICK:"click".concat(g),FOCUSIN:"focusin".concat(g),FOCUSOUT:"focusout".concat(g),MOUSEENTER:"mouseenter".concat(g),MOUSELEAVE:"mouseleave".concat(g)},u=function(k){function l(){return f(this,l),i(this,(l.__proto__||Object.getPrototypeOf(l)).apply(this,arguments))}return j(l,k),h(l,[{key:"isWithContent",value:function(){return this.getTitle()||this._getContent()}},{key:"addAttachmentClass",value:function(b){a(this.getTipElement()).addClass("".concat(n,"-").concat(b))}},{key:"getTipElement",value:function(){return this.tip=this.tip||a(this.config.template)[0],this.tip}},{key:"setContent",value:function(){var b=a(this.getTipElement());this.setElementContent(b.find(s.TITLE),this.getTitle());var c=this._getContent();"function"==typeof c&&(c=c.call(this.element)),this.setElementContent(b.find(s.CONTENT),c),b.removeClass("".concat(r.FADE," ").concat(r.SHOW))}},{key:"_getContent",value:function(){return this.element.getAttribute("data-content")||this.config.content}},{key:"_cleanTipClass",value:function(){var b=a(this.getTipElement()),c=b.attr("class").match(o);null!==c&&c.length>0&&b.removeClass(c.join(""))}}],[{key:"_jQueryInterface",value:function(b){return this.each(function(){var c=a(this).data(d),f="object"===e(b)?b:null;if((c||!/destroy|hide/.test(b))&&(c||(c=new l(this,f),a(this).data(d,c)),"string"==typeof b)){if("undefined"==typeof c[b])throw new TypeError('No method named "'.concat(b,'"'));c[b]()}})}},{key:"VERSION",get:function(){return c}},{key:"Default",get:function(){return p}},{key:"NAME",get:function(){return b}},{key:"DATA_KEY",get:function(){return d}},{key:"Event",get:function(){return t}},{key:"EVENT_KEY",get:function(){return g}},{key:"DefaultType",get:function(){return q}}]),l}(m["default"]);return a.fn[b]=u._jQueryInterface,a.fn[b].Constructor=u,a.fn[b].noConflict=function(){return a.fn[b]=l,u._jQueryInterface},u}(l["default"]);a["default"]=n});

1
amd/build/scrollspy.min.js

File diff suppressed because one or more lines are too long

1
amd/build/tab.min.js

@ -0,0 +1 @@
define(["exports","jquery","./util"],function(a,b,c){"use strict";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;c<b.length;c++){var d=b[c];d.enumerable=d.enumerable||!1,d.configurable=!0,"value"in d&&(d.writable=!0),Object.defineProperty(a,d.key,d)}}function g(a,b,c){return b&&f(a.prototype,b),c&&f(a,c),a}Object.defineProperty(a,"__esModule",{value:!0});var h=d(b),i=d(c),j=function(a){var b="tab",c="4.0.0",d="bs.tab",f=".".concat(d),h=".data-api",j=a.fn[b],k=150,l={HIDE:"hide".concat(f),HIDDEN:"hidden".concat(f),SHOW:"show".concat(f),SHOWN:"shown".concat(f),CLICK_DATA_API:"click".concat(f).concat(h)},m={DROPDOWN_MENU:"dropdown-menu",ACTIVE:"active",DISABLED:"disabled",FADE:"fade",SHOW:"show"},n={DROPDOWN:".dropdown",NAV_LIST_GROUP:".nav, .list-group",ACTIVE:".active",ACTIVE_UL:"> li > .active",DATA_TOGGLE:'[data-toggle="tab"], [data-toggle="pill"], [data-toggle="list"]',DROPDOWN_TOGGLE:".dropdown-toggle",DROPDOWN_ACTIVE_CHILD:"> .dropdown-menu .active"},o=function(){function b(a){e(this,b),this._element=a}return g(b,[{key:"show",value:function(){var b=this;if(!(this._element.parentNode&&this._element.parentNode.nodeType===Node.ELEMENT_NODE&&a(this._element).hasClass(m.ACTIVE)||a(this._element).hasClass(m.DISABLED))){var c,d,e=a(this._element).closest(n.NAV_LIST_GROUP)[0],f=i["default"].getSelectorFromElement(this._element);if(e){var g="UL"===e.nodeName?n.ACTIVE_UL:n.ACTIVE;d=a.makeArray(a(e).find(g)),d=d[d.length-1]}var h=a.Event(l.HIDE,{relatedTarget:this._element}),j=a.Event(l.SHOW,{relatedTarget:d});if(d&&a(d).trigger(h),a(this._element).trigger(j),!j.isDefaultPrevented()&&!h.isDefaultPrevented()){f&&(c=a(f)[0]),this._activate(this._element,e);var k=function(){var c=a.Event(l.HIDDEN,{relatedTarget:b._element}),e=a.Event(l.SHOWN,{relatedTarget:d});a(d).trigger(c),a(b._element).trigger(e)};c?this._activate(c,c.parentNode,k):k()}}}},{key:"dispose",value:function(){a.removeData(this._element,d),this._element=null}},{key:"_activate",value:function(b,c,d){var e,f=this;e="UL"===c.nodeName?a(c).find(n.ACTIVE_UL):a(c).children(n.ACTIVE);var g=e[0],h=d&&i["default"].supportsTransitionEnd()&&g&&a(g).hasClass(m.FADE),j=function(){return f._transitionComplete(b,g,d)};g&&h?a(g).one(i["default"].TRANSITION_END,j).emulateTransitionEnd(k):j()}},{key:"_transitionComplete",value:function(b,c,d){if(c){a(c).removeClass("".concat(m.SHOW," ").concat(m.ACTIVE));var e=a(c.parentNode).find(n.DROPDOWN_ACTIVE_CHILD)[0];e&&a(e).removeClass(m.ACTIVE),"tab"===c.getAttribute("role")&&c.setAttribute("aria-selected",!1)}if(a(b).addClass(m.ACTIVE),"tab"===b.getAttribute("role")&&b.setAttribute("aria-selected",!0),i["default"].reflow(b),a(b).addClass(m.SHOW),b.parentNode&&a(b.parentNode).hasClass(m.DROPDOWN_MENU)){var f=a(b).closest(n.DROPDOWN)[0];f&&a(f).find(n.DROPDOWN_TOGGLE).addClass(m.ACTIVE),b.setAttribute("aria-expanded",!0)}d&&d()}}],[{key:"_jQueryInterface",value:function(c){return this.each(function(){var e=a(this),f=e.data(d);if(f||(f=new b(this),e.data(d,f)),"string"==typeof c){if("undefined"==typeof f[c])throw new TypeError('No method named "'.concat(c,'"'));f[c]()}})}},{key:"VERSION",get:function(){return c}}]),b}();return a(document).on(l.CLICK_DATA_API,n.DATA_TOGGLE,function(b){b.preventDefault(),o._jQueryInterface.call(a(this),"show")}),a.fn[b]=o._jQueryInterface,a.fn[b].Constructor=o,a.fn[b].noConflict=function(){return a.fn[b]=j,o._jQueryInterface},o}(h["default"]);a["default"]=j});

1
amd/build/tether.min.js

File diff suppressed because one or more lines are too long

1
amd/build/tooltip.min.js

File diff suppressed because one or more lines are too long

1
amd/build/util.min.js

@ -0,0 +1 @@
define(["exports","jquery"],function(a,b){"use strict";function c(a){return a&&a.__esModule?a:{"default":a}}Object.defineProperty(a,"__esModule",{value:!0});var d=c(b),e=function(a){function b(a){return{}.toString.call(a).match(/\s([a-zA-Z]+)/)[1].toLowerCase()}function c(){return{bindType:h.end,delegateType:h.end,handle:function(b){if(a(b.target).is(this))return b.handleObj.handler.apply(this,arguments)}}}function d(){return("undefined"==typeof window||!window.QUnit)&&{end:"transitionend"}}function e(b){var c=this,d=!1;return a(this).one(j.TRANSITION_END,function(){d=!0}),setTimeout(function(){d||j.triggerTransitionEnd(c)},b),this}function f(){h=d(),a.fn.emulateTransitionEnd=e,j.supportsTransitionEnd()&&(a.event.special[j.TRANSITION_END]=c())}function g(b){return b="function"==typeof a.escapeSelector?a.escapeSelector(b).substr(1):b.replace(/(:|\.|\[|\]|,|=|@)/g,"\\$1")}var h=!1,i=1e6,j={TRANSITION_END:"bsTransitionEnd",getUID:function(a){do a+=~~(Math.random()*i);while(document.getElementById(a));return a},getSelectorFromElement:function(b){var c=b.getAttribute("data-target");c&&"#"!==c||(c=b.getAttribute("href")||""),"#"===c.charAt(0)&&(c=g(c));try{var d=a(document).find(c);return d.length>0?c:null}catch(e){return null}},reflow:function(a){return a.offsetHeight},triggerTransitionEnd:function(b){a(b).trigger(h.end)},supportsTransitionEnd:function(){return Boolean(h)},isElement:function(a){return(a[0]||a).nodeType},typeCheckConfig:function(a,c,d){for(var e in d)if(Object.prototype.hasOwnProperty.call(d,e)){var f=d[e],g=c[e],h=g&&j.isElement(g)?"element":b(g);if(!new RegExp(f).test(h))throw new Error("".concat(a.toUpperCase(),": ")+'Option "'.concat(e,'" provided type "').concat(h,'" ')+'but expected type "'.concat(f,'".'))}}};return f(),j}(d["default"]);a["default"]=e});

216
amd/src/alert.js

@ -0,0 +1,216 @@
define(["exports", "jquery", "./util"], function (exports, _jquery, _util) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _jquery2 = _interopRequireDefault(_jquery);
var _util2 = _interopRequireDefault(_util);
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
/**
* --------------------------------------------------------------------------
* Bootstrap (v4.0.0): alert.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* --------------------------------------------------------------------------
*/
var Alert = function ($) {
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
var NAME = 'alert';
var VERSION = '4.0.0';
var DATA_KEY = 'bs.alert';
var EVENT_KEY = ".".concat(DATA_KEY);
var DATA_API_KEY = '.data-api';
var JQUERY_NO_CONFLICT = $.fn[NAME];
var TRANSITION_DURATION = 150;
var Selector = {
DISMISS: '[data-dismiss="alert"]'
};
var Event = {
CLOSE: "close".concat(EVENT_KEY),
CLOSED: "closed".concat(EVENT_KEY),
CLICK_DATA_API: "click".concat(EVENT_KEY).concat(DATA_API_KEY)
};
var ClassName = {
ALERT: 'alert',
FADE: 'fade',
SHOW: 'show'
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
};
var Alert = function () {
function Alert(element) {
_classCallCheck(this, Alert);
this._element = element;
} // Getters
_createClass(Alert, [{
key: "close",
value: function close(element) {
element = element || this._element;
var rootElement = this._getRootElement(element);
var customEvent = this._triggerCloseEvent(rootElement);
if (customEvent.isDefaultPrevented()) {
return;
}
this._removeElement(rootElement);
}
}, {
key: "dispose",
value: function dispose() {
$.removeData(this._element, DATA_KEY);
this._element = null;
}
}, {
key: "_getRootElement",
value: function _getRootElement(element) {
var selector = _util2.default.getSelectorFromElement(element);
var parent = false;
if (selector) {
parent = $(selector)[0];
}
if (!parent) {
parent = $(element).closest(".".concat(ClassName.ALERT))[0];
}
return parent;
}
}, {
key: "_triggerCloseEvent",
value: function _triggerCloseEvent(element) {
var closeEvent = $.Event(Event.CLOSE);
$(element).trigger(closeEvent);
return closeEvent;
}
}, {
key: "_removeElement",
value: function _removeElement(element) {
var _this = this;
$(element).removeClass(ClassName.SHOW);
if (!_util2.default.supportsTransitionEnd() || !$(element).hasClass(ClassName.FADE)) {
this._destroyElement(element);
return;
}
$(element).one(_util2.default.TRANSITION_END, function (event) {
return _this._destroyElement(element, event);
}).emulateTransitionEnd(TRANSITION_DURATION);
}
}, {
key: "_destroyElement",
value: function _destroyElement(element) {
$(element).detach().trigger(Event.CLOSED).remove();
}
}], [{
key: "_jQueryInterface",
value: function _jQueryInterface(config) {
return this.each(function () {
var $element = $(this);
var data = $element.data(DATA_KEY);
if (!data) {
data = new Alert(this);
$element.data(DATA_KEY, data);
}
if (config === 'close') {
data[config](this);
}
});
}
}, {
key: "_handleDismiss",
value: function _handleDismiss(alertInstance) {
return function (event) {
if (event) {
event.preventDefault();
}
alertInstance.close(this);
};
}
}, {
key: "VERSION",
get: function get() {
return VERSION;
}
}]);
return Alert;
}();
/**
* ------------------------------------------------------------------------
* 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 = function () {
$.fn[NAME] = JQUERY_NO_CONFLICT;
return Alert._jQueryInterface;
};
return Alert;
}(_jquery2.default);
exports.default = Alert;
});

201
amd/src/aria.js

@ -0,0 +1,201 @@
// 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
*/
define(['jquery', 'core/pending'], function($, Pending) {
return {
init: function() {
// Drop downs from bootstrap don't support keyboard accessibility by default.
var focusEnd = false,
setFocusEnd = function() {
focusEnd = true;
},
getFocusEnd = function() {
var result = focusEnd;
focusEnd = false;
return result;
};
// Special handling for "up" keyboard control.
$('[data-toggle="dropdown"]').keydown(function(e) {
var trigger = e.which || e.keyCode,
expanded;
// Up key opens the menu at the end.
if (trigger == 38) {
// Focus the end of the menu, not the beginning.
setFocusEnd();
}
// Escape key only closes the menu, it doesn't open it.
if (trigger == 27) {
expanded = $(e.target).attr('aria-expanded');
e.preventDefault();
if (expanded == "false") {
$(e.target).click();
}
}
// Space key or Enter key opens the menu.
if (trigger == 32 || trigger == 13) {
// Cancel random scroll.
e.preventDefault();
// Open the menu instead.
$(e.target).click();
}
});
// Special handling for navigation keys when menu is open.
var shiftFocus = function(element) {
var delayedFocus = function(pendingPromise) {
$(this).focus();
pendingPromise.resolve();
}.bind(element);
setTimeout(delayedFocus, 50, new Pending('core/aria:delayed-focus'));
};
$('.dropdown').on('shown.bs.dropdown', function(e) {
// We need to focus on the first menuitem.
var menu = $(e.target).find('[role="menu"]'),
menuItems = false,
foundMenuItem = false;
if (menu) {
menuItems = $(menu).find('[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).
$('.dropdown [role="menu"] [role="menuitem"]').keypress(function(e) {
var trigger = String.fromCharCode(e.which || e.keyCode),
menu = $(e.target).closest('[role="menu"]'),
i = 0,
menuItems = false,
item,
itemText;
if (!menu) {
return;
}
menuItems = $(menu).find('[role="menuitem"]');
if (!menuItems) {
return;
}
trigger = trigger.toLowerCase();
for (i = 0; i < menuItems.length; i++) {
item = $(menuItems[i]);
itemText = item.text().trim().toLowerCase();
if (itemText.indexOf(trigger) == 0) {
shiftFocus(item);
break;
}
}
});
// Keyboard navigation for arrow keys, home and end keys.
$('.dropdown [role="menu"] [role="menuitem"]').keydown(function(e) {
var trigger = e.which || e.keyCode,
next = false,
menu = $(e.target).closest('[role="menu"]'),
i = 0,
menuItems = false;
if (!menu) {
return;
}
menuItems = $(menu).find('[role="menuitem"]');
if (!menuItems) {
return;
}
// Down key.
if (trigger == 40) {
for (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 == 38) {
// Up key.
for (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 == 36) {
// Home key.
next = menuItems[0];
} else if (trigger == 35) {
// 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', function(e) {
// We need to focus on the menu trigger.
var trigger = $(e.target).find('[data-toggle="dropdown"]');
if (trigger) {
shiftFocus(trigger);
}
});
// After page load, focus on any element with special autofocus attribute.
$(function() {
window.setTimeout(function(pendingPromise) {
var alerts = $('[role="alert"][data-aria-autofocus="true"]');
if (alerts.length > 0) {
$(alerts[0]).attr('tabindex', '0');
$(alerts[0]).focus();
}
pendingPromise.resolve();
}, 300, new Pending('core/aria:delayed-focus'));
});
}
};
});

200
amd/src/button.js

@ -0,0 +1,200 @@
define(["exports", "jquery"], function (exports, _jquery) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _jquery2 = _interopRequireDefault(_jquery);
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
/**
* --------------------------------------------------------------------------
* Bootstrap (v4.0.0): button.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* --------------------------------------------------------------------------
*/
var Button = function ($) {
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
var NAME = 'button';
var VERSION = '4.0.0';
var DATA_KEY = 'bs.button';
var EVENT_KEY = ".".concat(DATA_KEY);
var DATA_API_KEY = '.data-api';
var JQUERY_NO_CONFLICT = $.fn[NAME];
var ClassName = {
ACTIVE: 'active',
BUTTON: 'btn',
FOCUS: 'focus'
};
var Selector = {
DATA_TOGGLE_CARROT: '[data-toggle^="button"]',
DATA_TOGGLE: '[data-toggle="buttons"]',
INPUT: 'input',
ACTIVE: '.active',
BUTTON: '.btn'
};
var Event = {
CLICK_DATA_API: "click".concat(EVENT_KEY).concat(DATA_API_KEY),
FOCUS_BLUR_DATA_API: "focus".concat(EVENT_KEY).concat(DATA_API_KEY, " ") + "blur".concat(EVENT_KEY).concat(DATA_API_KEY)
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
};
var Button = function () {
function Button(element) {
_classCallCheck(this, Button);
this._element = element;
} // Getters
_createClass(Button, [{
key: "toggle",
value: function toggle() {
var triggerChangeEvent = true;
var addAriaPressed = true;
var rootElement = $(this._element).closest(Selector.DATA_TOGGLE)[0];
if (rootElement) {
var input = $(this._element).find(Selector.INPUT)[0];
if (input) {
if (input.type === 'radio') {
if (input.checked && $(this._element).hasClass(ClassName.ACTIVE)) {
triggerChangeEvent = false;
} else {
var activeElement = $(rootElement).find(Selector.ACTIVE)[0];
if (activeElement) {
$(activeElement).removeClass(ClassName.ACTIVE);
}
}
}
if (triggerChangeEvent) {
if (input.hasAttribute('disabled') || rootElement.hasAttribute('disabled') || input.classList.contains('disabled') || rootElement.classList.contains('disabled')) {
return;
}
input.checked = !$(this._element).hasClass(ClassName.ACTIVE);
$(input).trigger('change');
}
input.focus();
addAriaPressed = false;
}
}
if (addAriaPressed) {
this._element.setAttribute('aria-pressed', !$(this._element).hasClass(ClassName.ACTIVE));
}
if (triggerChangeEvent) {
$(this._element).toggleClass(ClassName.ACTIVE);
}
}
}, {
key: "dispose",
value: function dispose() {
$.removeData(this._element, DATA_KEY);
this._element = null;
}
}], [{
key: "_jQueryInterface",
value: function _jQueryInterface(config) {
return this.each(function () {
var data = $(this).data(DATA_KEY);
if (!data) {
data = new Button(this);
$(this).data(DATA_KEY, data);
}
if (config === 'toggle') {
data[config]();
}
});
}
}, {
key: "VERSION",
get: function get() {
return VERSION;
}
}]);
return Button;
}();
/**
* ------------------------------------------------------------------------
* Data Api implementation
* ------------------------------------------------------------------------
*/
$(document).on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE_CARROT, function (event) {
event.preventDefault();
var button = event.target;
if (!$(button).hasClass(ClassName.BUTTON)) {
button = $(button).closest(Selector.BUTTON);
}
Button._jQueryInterface.call($(button), 'toggle');
}).on(Event.FOCUS_BLUR_DATA_API, Selector.DATA_TOGGLE_CARROT, function (event) {
var button = $(event.target).closest(Selector.BUTTON)[0];
$(button).toggleClass(ClassName.FOCUS, /^focus(in)?$/.test(event.type));
});
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = Button._jQueryInterface;
$.fn[NAME].Constructor = Button;
$.fn[NAME].noConflict = function () {
$.fn[NAME] = JQUERY_NO_CONFLICT;
return Button._jQueryInterface;
};
return Button;
}(_jquery2.default);
exports.default = Button;
});

589
amd/src/carousel.js

@ -0,0 +1,589 @@
define(["exports", "jquery", "./util"], function (exports, _jquery, _util) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _jquery2 = _interopRequireDefault(_jquery);
var _util2 = _interopRequireDefault(_util);
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
function _typeof(obj) {
if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
_typeof = function _typeof(obj) {
return typeof obj;
};
} else {
_typeof = function _typeof(obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
};
}
return _typeof(obj);
}
function _extends() {
_extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
return _extends.apply(this, arguments);
}
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
/**
* --------------------------------------------------------------------------
* Bootstrap (v4.0.0): carousel.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* --------------------------------------------------------------------------
*/
var Carousel = function ($) {
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
var NAME = 'carousel';
var VERSION = '4.0.0';
var DATA_KEY = 'bs.carousel';
var EVENT_KEY = ".".concat(DATA_KEY);
var DATA_API_KEY = '.data-api';
var JQUERY_NO_CONFLICT = $.fn[NAME];
var TRANSITION_DURATION = 600;
var ARROW_LEFT_KEYCODE = 37; // KeyboardEvent.which value for left arrow key
var ARROW_RIGHT_KEYCODE = 39; // KeyboardEvent.which value for right arrow key
var TOUCHEVENT_COMPAT_WAIT = 500; // Time for mouse compat events to fire after touch
var Default = {
interval: 5000,
keyboard: true,
slide: false,
pause: 'hover',
wrap: true
};
var DefaultType = {
interval: '(number|boolean)',
keyboard: 'boolean',
slide: '(boolean|string)',
pause: '(string|boolean)',
wrap: 'boolean'
};
var Direction = {
NEXT: 'next',
PREV: 'prev',
LEFT: 'left',
RIGHT: 'right'
};
var Event = {
SLIDE: "slide".concat(EVENT_KEY),
SLID: "slid".concat(EVENT_KEY),
KEYDOWN: "keydown".concat(EVENT_KEY),
MOUSEENTER: "mouseenter".concat(EVENT_KEY),
MOUSELEAVE: "mouseleave".concat(EVENT_KEY),
TOUCHEND: "touchend".concat(EVENT_KEY),
LOAD_DATA_API: "load".concat(EVENT_KEY).concat(DATA_API_KEY),
CLICK_DATA_API: "click".concat(EVENT_KEY).concat(DATA_API_KEY)
};
var ClassName = {
CAROUSEL: 'carousel',
ACTIVE: 'active',
SLIDE: 'slide',
RIGHT: 'carousel-item-right',
LEFT: 'carousel-item-left',
NEXT: 'carousel-item-next',
PREV: 'carousel-item-prev',
ITEM: 'carousel-item'
};
var Selector = {
ACTIVE: '.active',
ACTIVE_ITEM: '.active.carousel-item',
ITEM: '.carousel-item',
NEXT_PREV: '.carousel-item-next, .carousel-item-prev',
INDICATORS: '.carousel-indicators',
DATA_SLIDE: '[data-slide], [data-slide-to]',
DATA_RIDE: '[data-ride="carousel"]'
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
};
var Carousel = function () {
function Carousel(element, config) {
_classCallCheck(this, Carousel);
this._items = null;
this._interval = null;
this._activeElement = null;
this._isPaused = false;
this._isSliding = false;
this.touchTimeout = null;
this._config = this._getConfig(config);
this._element = $(element)[0];
this._indicatorsElement = $(this._element).find(Selector.INDICATORS)[0];
this._addEventListeners();
} // Getters
_createClass(Carousel, [{
key: "next",
value: function next() {
if (!this._isSliding) {
this._slide(Direction.NEXT);
}
}
}, {
key: "nextWhenVisible",
value: function nextWhenVisible() {
// Don't call next when the page isn't visible
// or the carousel or its parent isn't visible
if (!document.hidden && $(this._element).is(':visible') && $(this._element).css('visibility') !== 'hidden') {
this.next();
}
}
}, {
key: "prev",
value: function prev() {
if (!this._isSliding) {
this._slide(Direction.PREV);
}
}
}, {
key: "pause",
value: function pause(event) {
if (!event) {
this._isPaused = true;
}
if ($(this._element).find(Selector.NEXT_PREV)[0] && _util2.default.supportsTransitionEnd()) {
_util2.default.triggerTransitionEnd(this._element);
this.cycle(true);
}
clearInterval(this._interval);
this._interval = null;
}
}, {
key: "cycle",
value: function cycle(event) {
if (!event) {
this._isPaused = false;
}
if (this._interval) {
clearInterval(this._interval);
this._interval = null;
}
if (this._config.interval && !this._isPaused) {
this._interval = setInterval((document.visibilityState ? this.nextWhenVisible : this.next).bind(this), this._config.interval);
}
}
}, {
key: "to",
value: function to(index) {
var _this = this;
this._activeElement = $(this._element).find(Selector.ACTIVE_ITEM)[0];
var activeIndex = this._getItemIndex(this._activeElement);
if (index > this._items.length - 1 || index < 0) {
return;
}
if (this._isSliding) {
$(this._element).one(Event.SLID, function () {
return _this.to(index);
});
return;
}
if (activeIndex === index) {
this.pause();
this.cycle();
return;
}
var direction = index > activeIndex ? Direction.NEXT : Direction.PREV;
this._slide(direction, this._items[index]);
}
}, {
key: "dispose",
value: function 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;
}
}, {
key: "_getConfig",
value: function _getConfig(config) {
config = _extends({}, Default, config);
_util2.default.typeCheckConfig(NAME, config, DefaultType);
return config;
}
}, {
key: "_addEventListeners",
value: function _addEventListeners() {
var _this2 = this;
if (this._config.keyboard) {
$(this._element).on(Event.KEYDOWN, function (event) {
return _this2._keydown(event);
});
}
if (this._config.pause === 'hover') {
$(this._element).on(Event.MOUSEENTER, function (event) {
return _this2.pause(event);
}).on(Event.MOUSELEAVE, function (event) {
return _this2.cycle(event);
});
if ('ontouchstart' in document.documentElement) {
// 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._element).on(Event.TOUCHEND, function () {
_this2.pause();
if (_this2.touchTimeout) {
clearTimeout(_this2.touchTimeout);
}
_this2.touchTimeout = setTimeout(function (event) {
return _this2.cycle(event);
}, TOUCHEVENT_COMPAT_WAIT + _this2._config.interval);
});
}
}
}
}, {
key: "_keydown",
value: function _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:
}
}
}, {
key: "_getItemIndex",
value: function _getItemIndex(element) {
this._items = $.makeArray($(element).parent().find(Selector.ITEM));
return this._items.indexOf(element);
}
}, {
key: "_getItemByDirection",
value: function _getItemByDirection(direction, activeElement) {
var isNextDirection = direction === Direction.NEXT;
var isPrevDirection = direction === Direction.PREV;
var activeIndex = this._getItemIndex(activeElement);
var lastItemIndex = this._items.length - 1;
var isGoingToWrap = isPrevDirection && activeIndex === 0 || isNextDirection && activeIndex === lastItemIndex;
if (isGoingToWrap && !this._config.wrap) {
return activeElement;
}
var delta = direction === Direction.PREV ? -1 : 1;
var itemIndex = (activeIndex + delta) % this._items.length;
return itemIndex === -1 ? this._items[this._items.length - 1] : this._items[itemIndex];
}
}, {
key: "_triggerSlideEvent",
value: function _triggerSlideEvent(relatedTarget, eventDirectionName) {
var targetIndex = this._getItemIndex(relatedTarget);
var fromIndex = this._getItemIndex($(this._element).find(Selector.ACTIVE_ITEM)[0]);
var slideEvent = $.Event(Event.SLIDE, {
relatedTarget: relatedTarget,
direction: eventDirectionName,
from: fromIndex,
to: targetIndex
});
$(this._element).trigger(slideEvent);
return slideEvent;
}
}, {
key: "_setActiveIndicatorElement",
value: function _setActiveIndicatorElement(element) {
if (this._indicatorsElement) {
$(this._indicatorsElement).find(Selector.ACTIVE).removeClass(ClassName.ACTIVE);
var nextIndicator = this._indicatorsElement.children[this._getItemIndex(element)];
if (nextIndicator) {
$(nextIndicator).addClass(ClassName.ACTIVE);
}
}
}
}, {
key: "_slide",
value: function _slide(direction, element) {
var _this3 = this;
var activeElement = $(this._element).find(Selector.ACTIVE_ITEM)[0];
var activeElementIndex = this._getItemIndex(activeElement);
var nextElement = element || activeElement && this._getItemByDirection(direction, activeElement);
var nextElementIndex = this._getItemIndex(nextElement);
var isCycling = Boolean(this._interval);
var directionalClassName;
var orderClassName;
var eventDirectionName;
if (direction === Direction.NEXT) {
directionalClassName = ClassName.LEFT;
orderClassName = ClassName.NEXT;
eventDirectionName = Direction.LEFT;
} else {
directionalClassName = ClassName.RIGHT;
orderClassName = ClassName.PREV;
eventDirectionName = Direction.RIGHT;
}
if (nextElement && $(nextElement).hasClass(ClassName.ACTIVE)) {
this._isSliding = false;
return;
}
var 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);
var slidEvent = $.Event(Event.SLID, {
relatedTarget: nextElement,
direction: eventDirectionName,
from: activeElementIndex,
to: nextElementIndex
});
if (_util2.default.supportsTransitionEnd() && $(this._element).hasClass(ClassName.SLIDE)) {
$(nextElement).addClass(orderClassName);
_util2.default.reflow(nextElement);
$(activeElement).addClass(directionalClassName);
$(nextElement).addClass(directionalClassName);
$(activeElement).one(_util2.default.TRANSITION_END, function () {
$(nextElement).removeClass("".concat(directionalClassName, " ").concat(orderClassName)).addClass(ClassName.ACTIVE);
$(activeElement).removeClass("".concat(ClassName.ACTIVE, " ").concat(orderClassName, " ").concat(directionalClassName));
_this3._isSliding = false;
setTimeout(function () {
return $(_this3._element).trigger(slidEvent);
}, 0);
}).emulateTransitionEnd(TRANSITION_DURATION);
} else {
$(activeElement).removeClass(ClassName.ACTIVE);
$(nextElement).addClass(ClassName.ACTIVE);
this._isSliding = false;
$(this._element).trigger(slidEvent);
}
if (isCycling) {
this.cycle();
}
}
}], [{
key: "_jQueryInterface",
value: function _jQueryInterface(config) {
return this.each(function () {
var data = $(this).data(DATA_KEY);
var _config = _extends({}, Default, $(this).data());
if (_typeof(config) === 'object') {
_config = _extends({}, _config, config);
}
var 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 \"".concat(action, "\""));
}
data[action]();
} else if (_config.interval) {
data.pause();
data.cycle();
}
});
}
}, {
key: "_dataApiClickHandler",
value: function _dataApiClickHandler(event) {
var selector = _util2.default.getSelectorFromElement(this);
if (!selector) {
return;
}
var target = $(selector)[0];
if (!target || !$(target).hasClass(ClassName.CAROUSEL)) {
return;
}
var config = _extends({}, $(target).data(), $(this).data());
var 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();
}
}, {
key: "VERSION",
get: function get() {
return VERSION;
}
}, {
key: "Default",
get: function get() {
return Default;
}
}]);
return Carousel;
}();
/**
* ------------------------------------------------------------------------
* Data Api implementation
* ------------------------------------------------------------------------
*/
$(document).on(Event.CLICK_DATA_API, Selector.DATA_SLIDE, Carousel._dataApiClickHandler);
$(window).on(Event.LOAD_DATA_API, function () {
$(Selector.DATA_RIDE).each(function () {
var $carousel = $(this);
Carousel._jQueryInterface.call($carousel, $carousel.data());
});
});
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = Carousel._jQueryInterface;
$.fn[NAME].Constructor = Carousel;
$.fn[NAME].noConflict = function () {
$.fn[NAME] = JQUERY_NO_CONFLICT;
return Carousel._jQueryInterface;
};
return Carousel;
}(_jquery2.default);
exports.default = Carousel;
});

455
amd/src/collapse.js

@ -0,0 +1,455 @@
define(["exports", "jquery", "./util"], function (exports, _jquery, _util) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _jquery2 = _interopRequireDefault(_jquery);
var _util2 = _interopRequireDefault(_util);
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
function _typeof(obj) {
if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
_typeof = function _typeof(obj) {
return typeof obj;
};
} else {
_typeof = function _typeof(obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
};
}
return _typeof(obj);
}
function _extends() {
_extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
return _extends.apply(this, arguments);
}
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
/**
* --------------------------------------------------------------------------
* Bootstrap (v4.0.0): collapse.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* --------------------------------------------------------------------------
*/
var Collapse = function ($) {
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
var NAME = 'collapse';
var VERSION = '4.0.0';
var DATA_KEY = 'bs.collapse';
var EVENT_KEY = ".".concat(DATA_KEY);
var DATA_API_KEY = '.data-api';
var JQUERY_NO_CONFLICT = $.fn[NAME];
var TRANSITION_DURATION = 600;
var Default = {
toggle: true,
parent: ''
};
var DefaultType = {
toggle: 'boolean',
parent: '(string|element)'
};
var Event = {
SHOW: "show".concat(EVENT_KEY),
SHOWN: "shown".concat(EVENT_KEY),
HIDE: "hide".concat(EVENT_KEY),
HIDDEN: "hidden".concat(EVENT_KEY),
CLICK_DATA_API: "click".concat(EVENT_KEY).concat(DATA_API_KEY)
};
var ClassName = {
SHOW: 'show',
COLLAPSE: 'collapse',
COLLAPSING: 'collapsing',
COLLAPSED: 'collapsed'
};
var Dimension = {
WIDTH: 'width',
HEIGHT: 'height'
};
var Selector = {
ACTIVES: '.show, .collapsing',
DATA_TOGGLE: '[data-toggle="collapse"]'
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
};
var Collapse = function () {
function Collapse(element, config) {
_classCallCheck(this, Collapse);
this._isTransitioning = false;
this._element = element;
this._config = this._getConfig(config);
this._triggerArray = $.makeArray($("[data-toggle=\"collapse\"][href=\"#".concat(element.id, "\"],") + "[data-toggle=\"collapse\"][data-target=\"#".concat(element.id, "\"]")));
var tabToggles = $(Selector.DATA_TOGGLE);
for (var i = 0; i < tabToggles.length; i++) {
var elem = tabToggles[i];
var selector = _util2.default.getSelectorFromElement(elem);
if (selector !== null && $(selector).filter(element).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
_createClass(Collapse, [{
key: "toggle",
value: function toggle() {
if ($(this._element).hasClass(ClassName.SHOW)) {
this.hide();
} else {
this.show();
}
}
}, {
key: "show",
value: function show() {
var _this = this;
if (this._isTransitioning || $(this._element).hasClass(ClassName.SHOW)) {
return;
}
var actives;
var activesData;
if (this._parent) {
actives = $.makeArray($(this._parent).find(Selector.ACTIVES).filter("[data-parent=\"".concat(this._config.parent, "\"]")));
if (actives.length === 0) {
actives = null;
}
}
if (actives) {
activesData = $(actives).not(this._selector).data(DATA_KEY);
if (activesData && activesData._isTransitioning) {
return;
}
}
var 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);
}
}
var dimension = this._getDimension();
$(this._element).removeClass(ClassName.COLLAPSE).addClass(ClassName.COLLAPSING);
this._element.style[dimension] = 0;
if (this._triggerArray.length > 0) {
$(this._triggerArray).removeClass(ClassName.COLLAPSED).attr('aria-expanded', true);
}
this.setTransitioning(true);
var complete = function complete() {
$(_this._element).removeClass(ClassName.COLLAPSING).addClass(ClassName.COLLAPSE).addClass(ClassName.SHOW);
_this._element.style[dimension] = '';
_this.setTransitioning(false);
$(_this._element).trigger(Event.SHOWN);
};
if (!_util2.default.supportsTransitionEnd()) {
complete();
return;
}
var capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1);
var scrollSize = "scroll".concat(capitalizedDimension);
$(this._element).one(_util2.default.TRANSITION_END, complete).emulateTransitionEnd(TRANSITION_DURATION);
this._element.style[dimension] = "".concat(this._element[scrollSize], "px");
}
}, {
key: "hide",
value: function hide() {
var _this2 = this;
if (this._isTransitioning || !$(this._element).hasClass(ClassName.SHOW)) {
return;
}
var startEvent = $.Event(Event.HIDE);
$(this._element).trigger(startEvent);
if (startEvent.isDefaultPrevented()) {
return;
}
var dimension = this._getDimension();
this._element.style[dimension] = "".concat(this._element.getBoundingClientRect()[dimension], "px");
_util2.default.reflow(this._element);
$(this._element).addClass(ClassName.COLLAPSING).removeClass(ClassName.COLLAPSE).removeClass(ClassName.SHOW);
if (this._triggerArray.length > 0) {
for (var i = 0; i < this._triggerArray.length; i++) {
var trigger = this._triggerArray[i];
var selector = _util2.default.getSelectorFromElement(trigger);
if (selector !== null) {
var $elem = $(selector);
if (!$elem.hasClass(ClassName.SHOW)) {
$(trigger).addClass(ClassName.COLLAPSED).attr('aria-expanded', false);
}
}
}
}
this.setTransitioning(true);
var complete = function complete() {
_this2.setTransitioning(false);
$(_this2._element).removeClass(ClassName.COLLAPSING).addClass(ClassName.COLLAPSE).trigger(Event.HIDDEN);
};
this._element.style[dimension] = '';
if (!_util2.default.supportsTransitionEnd()) {
complete();
return;
}
$(this._element).one(_util2.default.TRANSITION_END, complete).emulateTransitionEnd(TRANSITION_DURATION);
}
}, {
key: "setTransitioning",
value: function setTransitioning(isTransitioning) {
this._isTransitioning = isTransitioning;
}
}, {
key: "dispose",
value: function dispose() {
$.removeData(this._element, DATA_KEY);
this._config = null;
this._parent = null;
this._element = null;
this._triggerArray = null;
this._isTransitioning = null;
}
}, {
key: "_getConfig",
value: function _getConfig(config) {
config = _extends({}, Default, config);
config.toggle = Boolean(config.toggle); // Coerce string values
_util2.default.typeCheckConfig(NAME, config, DefaultType);
return config;
}
}, {
key: "_getDimension",
value: function _getDimension() {
var hasWidth = $(this._element).hasClass(Dimension.WIDTH);
return hasWidth ? Dimension.WIDTH : Dimension.HEIGHT;
}
}, {
key: "_getParent",
value: function _getParent() {
var _this3 = this;
var parent = null;
if (_util2.default.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 = $(this._config.parent)[0];
}
var selector = "[data-toggle=\"collapse\"][data-parent=\"".concat(this._config.parent, "\"]");
$(parent).find(selector).each(function (i, element) {
_this3._addAriaAndCollapsedClass(Collapse._getTargetFromElement(element), [element]);
});
return parent;
}
}, {
key: "_addAriaAndCollapsedClass",
value: function _addAriaAndCollapsedClass(element, triggerArray) {
if (element) {
var isOpen = $(element).hasClass(ClassName.SHOW);
if (triggerArray.length > 0) {
$(triggerArray).toggleClass(ClassName.COLLAPSED, !isOpen).attr('aria-expanded', isOpen);
}
}
}
}], [{
key: "_getTargetFromElement",
value: function _getTargetFromElement(element) {
var selector = _util2.default.getSelectorFromElement(element);
return selector ? $(selector)[0] : null;
}
}, {
key: "_jQueryInterface",
value: function _jQueryInterface(config) {
return this.each(function () {
var $this = $(this);
var data = $this.data(DATA_KEY);
var _config = _extends({}, Default, $this.data(), _typeof(config) === 'object' && config);
if (!data && _config.toggle && /show|hide/.test(config)) {
_config.toggle = false;
}
if (!data) {
data = new Collapse(this, _config);
$this.data(DATA_KEY, data);
}
if (typeof config === 'string') {
if (typeof data[config] === 'undefined') {
throw new TypeError("No method named \"".concat(config, "\""));
}
data[config]();
}
});
}
}, {
key: "VERSION",
get: function get() {
return VERSION;
}
}, {
key: "Default",
get: function get() {
return Default;
}
}]);
return Collapse;
}();
/**
* ------------------------------------------------------------------------
* 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();
}
var $trigger = $(this);
var selector = _util2.default.getSelectorFromElement(this);
$(selector).each(function () {
var $target = $(this);
var data = $target.data(DATA_KEY);
var config = data ? 'toggle' : $trigger.data();
Collapse._jQueryInterface.call($target, config);
});
});
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = Collapse._jQueryInterface;
$.fn[NAME].Constructor = Collapse;
$.fn[NAME].noConflict = function () {
$.fn[NAME] = JQUERY_NO_CONFLICT;
return Collapse._jQueryInterface;
};
return Collapse;
}(_jquery2.default);
exports.default = Collapse;
});

181
amd/src/drawer.js

@ -0,0 +1,181 @@
// 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.
*
* @package theme_ilb
* @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'],
function($, CustomEvents, Log) {
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-"]'
};
var small = $(document).width() < 768;
/**
* Constructor for the Drawer.
*
* @param {object} root The root jQuery element for the modal
*/
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);
drawer.attr('aria-hidden', 'true');
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');
drawer.attr('aria-hidden', 'false');
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.attr('aria-hidden', 'true');
drawer.addClass('closed');
if (!small) {
M.util.set_user_preference(preference, 'false');
}
}
};
/**
* 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));
};
return {
'init': function() {
return new Drawer();
}
};
});

549
amd/src/dropdown.js

@ -0,0 +1,549 @@
define(["exports", "jquery", "core/popper", "./util"], function (exports, _jquery, _popper, _util) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _jquery2 = _interopRequireDefault(_jquery);
var _popper2 = _interopRequireDefault(_popper);
var _util2 = _interopRequireDefault(_util);
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
function _typeof(obj) {
if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
_typeof = function _typeof(obj) {
return typeof obj;
};
} else {
_typeof = function _typeof(obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
};
}
return _typeof(obj);
}
function _extends() {
_extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
return _extends.apply(this, arguments);
}
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
/**
* --------------------------------------------------------------------------
* Bootstrap (v4.0.0): dropdown.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* --------------------------------------------------------------------------
*/
var Dropdown = function ($) {
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
var NAME = 'dropdown';
var VERSION = '4.0.0';
var DATA_KEY = 'bs.dropdown';
var EVENT_KEY = ".".concat(DATA_KEY);
var DATA_API_KEY = '.data-api';
var JQUERY_NO_CONFLICT = $.fn[NAME];
var ESCAPE_KEYCODE = 27; // KeyboardEvent.which value for Escape (Esc) key
var SPACE_KEYCODE = 32; // KeyboardEvent.which value for space key
var TAB_KEYCODE = 9; // KeyboardEvent.which value for tab key
var ARROW_UP_KEYCODE = 38; // KeyboardEvent.which value for up arrow key
var ARROW_DOWN_KEYCODE = 40; // KeyboardEvent.which value for down arrow key
var RIGHT_MOUSE_BUTTON_WHICH = 3; // MouseEvent.which value for the right button (assuming a right-handed mouse)
var REGEXP_KEYDOWN = new RegExp("".concat(ARROW_UP_KEYCODE, "|").concat(ARROW_DOWN_KEYCODE, "|").concat(ESCAPE_KEYCODE));
var Event = {
HIDE: "hide".concat(EVENT_KEY),
HIDDEN: "hidden".concat(EVENT_KEY),
SHOW: "show".concat(EVENT_KEY),
SHOWN: "shown".concat(EVENT_KEY),
CLICK: "click".concat(EVENT_KEY),
CLICK_DATA_API: "click".concat(EVENT_KEY).concat(DATA_API_KEY),
KEYDOWN_DATA_API: "keydown".concat(EVENT_KEY).concat(DATA_API_KEY),
KEYUP_DATA_API: "keyup".concat(EVENT_KEY).concat(DATA_API_KEY)
};
var ClassName = {
DISABLED: 'disabled',
SHOW: 'show',
DROPUP: 'dropup',
DROPRIGHT: 'dropright',
DROPLEFT: 'dropleft',
MENURIGHT: 'dropdown-menu-right',
MENULEFT: 'dropdown-menu-left',
POSITION_STATIC: 'position-static'
};
var Selector = {
DATA_TOGGLE: '[data-toggle="dropdown"]',
FORM_CHILD: '.dropdown form',
MENU: '.dropdown-menu',
NAVBAR_NAV: '.navbar-nav',
VISIBLE_ITEMS: '.dropdown-menu .dropdown-item:not(.disabled)'
};
var AttachmentMap = {
TOP: 'top-start',
TOPEND: 'top-end',
BOTTOM: 'bottom-start',
BOTTOMEND: 'bottom-end',
RIGHT: 'right-start',
RIGHTEND: 'right-end',
LEFT: 'left-start',
LEFTEND: 'left-end'
};
var Default = {
offset: 0,
flip: false,
boundary: 'scrollParent'
};
var DefaultType = {
offset: '(number|string|function)',
flip: 'boolean',
boundary: '(string|element)'
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
};
var Dropdown = function () {
function Dropdown(element, config) {
_classCallCheck(this, Dropdown);
this._element = element;
this._popper = null;
this._config = this._getConfig(config);
this._menu = this._getMenuElement();
this._inNavbar = this._detectNavbar();
this._addEventListeners();
} // Getters
_createClass(Dropdown, [{
key: "toggle",
value: function toggle() {
if (this._element.disabled || $(this._element).hasClass(ClassName.DISABLED)) {
return;
}
var parent = Dropdown._getParentFromElement(this._element);
var isActive = $(this._menu).hasClass(ClassName.SHOW);
Dropdown._clearMenus();
if (isActive) {
return;
}
var relatedTarget = {
relatedTarget: this._element
};
var showEvent = $.Event(Event.SHOW, relatedTarget);
$(parent).trigger(showEvent);
if (showEvent.isDefaultPrevented()) {
return;
} // Disable totally Popper.js for Dropdown in Navbar
if (!this._inNavbar) {
/**
* Check for Popper dependency
* Popper - https://popper.js.org
*/
if (typeof _popper2.default === 'undefined') {
throw new TypeError('Bootstrap dropdown require Popper.js (https://popper.js.org)');
}
var element = this._element; // For dropup with alignment we use the parent as popper container
if ($(parent).hasClass(ClassName.DROPUP)) {
if ($(this._menu).hasClass(ClassName.MENULEFT) || $(this._menu).hasClass(ClassName.MENURIGHT)) {
element = parent;
}
} // 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(ClassName.POSITION_STATIC);
}
this._popper = new _popper2.default(element, 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) {
$('body').children().on('mouseover', null, $.noop);
}
this._element.focus();
this._element.setAttribute('aria-expanded', true);
$(this._menu).toggleClass(ClassName.SHOW);
$(parent).toggleClass(ClassName.SHOW).trigger($.Event(Event.SHOWN, relatedTarget));
}
}, {
key: "dispose",
value: function 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;
}
}
}, {
key: "update",
value: function update() {
this._inNavbar = this._detectNavbar();
if (this._popper !== null) {
this._popper.scheduleUpdate();
}
}
}, {
key: "_addEventListeners",
value: function _addEventListeners() {
var _this = this;
$(this._element).on(Event.CLICK, function (event) {
event.preventDefault();
event.stopPropagation();
_this.toggle();
});
}
}, {
key: "_getConfig",
value: function _getConfig(config) {
config = _extends({}, this.constructor.Default, $(this._element).data(), config);
_util2.default.typeCheckConfig(NAME, config, this.constructor.DefaultType);
return config;
}
}, {
key: "_getMenuElement",
value: function _getMenuElement() {
if (!this._menu) {
var parent = Dropdown._getParentFromElement(this._element);
this._menu = $(parent).find(Selector.MENU)[0];
}
return this._menu;
}
}, {
key: "_getPlacement",
value: function _getPlacement() {
var $parentDropdown = $(this._element).parent();
var placement = AttachmentMap.BOTTOM; // Handle dropup
if ($parentDropdown.hasClass(ClassName.DROPUP)) {
placement = AttachmentMap.TOP;
if ($(this._menu).hasClass(ClassName.MENURIGHT)) {
placement = AttachmentMap.TOPEND;
}
} else if ($parentDropdown.hasClass(ClassName.DROPRIGHT)) {
placement = AttachmentMap.RIGHT;
} else if ($parentDropdown.hasClass(ClassName.DROPLEFT)) {
placement = AttachmentMap.LEFT;
} else if ($(this._menu).hasClass(ClassName.MENURIGHT)) {
placement = AttachmentMap.BOTTOMEND;
}
return placement;
}
}, {
key: "_detectNavbar",
value: function _detectNavbar() {
return $(this._element).closest('.navbar').length > 0;
}
}, {
key: "_getPopperConfig",
value: function _getPopperConfig() {
var _this2 = this;
var offsetConf = {};
if (typeof this._config.offset === 'function') {
offsetConf.fn = function (data) {
data.offsets = _extends({}, data.offsets, _this2._config.offset(data.offsets) || {});
return data;
};
} else {
offsetConf.offset = this._config.offset;
}
var popperConfig = {
placement: this._getPlacement(),
modifiers: {
offset: offsetConf,
flip: {
enabled: this._config.flip
},
preventOverflow: {
boundariesElement: this._config.boundary
}
}
};
return popperConfig;
}
}], [{
key: "_jQueryInterface",
value: function _jQueryInterface(config) {
return this.each(function () {
var data = $(this).data(DATA_KEY);
var _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 \"".concat(config, "\""));
}
data[config]();
}
});
}
}, {
key: "_clearMenus",
value: function _clearMenus(event) {
if (event && (event.which === RIGHT_MOUSE_BUTTON_WHICH || event.type === 'keyup' && event.which !== TAB_KEYCODE)) {
return;
}
var toggles = $.makeArray($(Selector.DATA_TOGGLE));
for (var i = 0; i < toggles.length; i++) {
var parent = Dropdown._getParentFromElement(toggles[i]);
var context = $(toggles[i]).data(DATA_KEY);
var relatedTarget = {
relatedTarget: toggles[i]
};
if (!context) {
continue;
}
var dropdownMenu = context._menu;
if (!$(parent).hasClass(ClassName.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;
}
var 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) {
$('body').children().off('mouseover', null, $.noop);
}
toggles[i].setAttribute('aria-expanded', 'false');
$(dropdownMenu).removeClass(ClassName.SHOW);
$(parent).removeClass(ClassName.SHOW).trigger($.Event(Event.HIDDEN, relatedTarget));
}
}
}, {
key: "_getParentFromElement",
value: function _getParentFromElement(element) {
var parent;
var selector = _util2.default.getSelectorFromElement(element);
if (selector) {
parent = $(selector)[0];
}
return parent || element.parentNode;
}
}, {
key: "_dataApiKeydownHandler",
value: function _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;
}
event.preventDefault();
event.stopPropagation();
if (this.disabled || $(this).hasClass(ClassName.DISABLED)) {
return;
}
var parent = Dropdown._getParentFromElement(this);
var isActive = $(parent).hasClass(ClassName.SHOW);
if (!isActive && (event.which !== ESCAPE_KEYCODE || event.which !== SPACE_KEYCODE) || isActive && (event.which === ESCAPE_KEYCODE || event.which === SPACE_KEYCODE)) {
if (event.which === ESCAPE_KEYCODE) {
var toggle = $(parent).find(Selector.DATA_TOGGLE)[0];
$(toggle).trigger('focus');
}
$(this).trigger('click');
return;
}
var items = $(parent).find(Selector.VISIBLE_ITEMS).get();
if (items.length === 0) {
return;
}
var 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();
}
}, {
key: "VERSION",
get: function get() {
return VERSION;
}
}, {
key: "Default",
get: function get() {
return Default;
}
}, {
key: "DefaultType",
get: function get() {
return DefaultType;
}
}]);
return Dropdown;
}();
/**
* ------------------------------------------------------------------------
* Data Api implementation
* ------------------------------------------------------------------------
*/
$(document).on(Event.KEYDOWN_DATA_API, Selector.DATA_TOGGLE, Dropdown._dataApiKeydownHandler).on(Event.KEYDOWN_DATA_API, Selector.MENU, Dropdown._dataApiKeydownHandler).on("".concat(Event.CLICK_DATA_API, " ").concat(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, function (e) {
e.stopPropagation();
});
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = Dropdown._jQueryInterface;
$.fn[NAME].Constructor = Dropdown;
$.fn[NAME].noConflict = function () {
$.fn[NAME] = JQUERY_NO_CONFLICT;
return Dropdown._jQueryInterface;
};
return Dropdown;
}(_jquery2.default, _popper2.default);
exports.default = Dropdown;
});

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

@ -0,0 +1,65 @@
// 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);
$(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');
// 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');
$(element).attr('aria-describedby', feedback.attr('id'));
$(element).attr('aria-invalid', true);
feedback.attr('tabindex', 0);
feedback.html(msg);
// Only display and focus when the error was not already visible.
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');
$(element).removeAttr('aria-describedby');
$(element).attr('aria-invalid', false);
feedback.hide();
}
}
});
}
};
});

76
amd/src/loader.js

@ -0,0 +1,76 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Template renderer for Moodle. Load and render Moodle templates with Mustache.
*
* @module core/templates
* @package core
* @class templates
* @copyright 2015 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since 2.9
*/
define(['jquery', './tether', 'core/event'], function(jQuery, Tether, Event) {
window.jQuery = jQuery;
window.Tether = Tether;
require(['theme_ilb/aria',
'theme_ilb/util',
'theme_ilb/alert',
'theme_ilb/button',
'theme_ilb/carousel',
'theme_ilb/collapse',
'theme_ilb/dropdown',
'theme_ilb/modal',
'theme_ilb/scrollspy',
'theme_ilb/tab',
'theme_ilb/tooltip',
'theme_ilb/popover'],
function(Aria) {
// We do twice because: https://github.com/twbs/bootstrap/issues/10547
jQuery('body').popover({
trigger: 'focus',
selector: "[data-toggle=popover][data-trigger!=hover]"
});
jQuery("html").popover({
container: "body",
selector: "[data-toggle=popover][data-trigger=hover]",
trigger: "hover",
delay: {
hide: 500
}
});
// We need to call popover automatically if nodes are added to the page later.
Event.getLegacyEvents().done(function(events) {
jQuery(document).on(events.FILTER_CONTENT_UPDATED, function() {
jQuery('body').popover({
selector: '[data-toggle="popover"]',
trigger: 'focus'
});
});
});
Aria.init();
});
return {};
});

662
amd/src/modal.js

@ -0,0 +1,662 @@
define(["exports", "jquery", "./util"], function (exports, _jquery, _util) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _jquery2 = _interopRequireDefault(_jquery);
var _util2 = _interopRequireDefault(_util);
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
function _typeof(obj) {
if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
_typeof = function _typeof(obj) {
return typeof obj;
};
} else {
_typeof = function _typeof(obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
};
}
return _typeof(obj);
}
function _extends() {
_extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
return _extends.apply(this, arguments);
}
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
/**
* --------------------------------------------------------------------------
* Bootstrap (v4.0.0): modal.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* --------------------------------------------------------------------------
*/
var Modal = function ($) {
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
var NAME = 'modal';
var VERSION = '4.0.0';
var DATA_KEY = 'bs.modal';
var EVENT_KEY = ".".concat(DATA_KEY);
var DATA_API_KEY = '.data-api';
var JQUERY_NO_CONFLICT = $.fn[NAME];
var TRANSITION_DURATION = 300;
var BACKDROP_TRANSITION_DURATION = 150;
var ESCAPE_KEYCODE = 27; // KeyboardEvent.which value for Escape (Esc) key
var Default = {
backdrop: true,
keyboard: true,
focus: true,
show: true
};
var DefaultType = {
backdrop: '(boolean|string)',
keyboard: 'boolean',
focus: 'boolean',
show: 'boolean'
};
var Event = {
HIDE: "hide".concat(EVENT_KEY),
HIDDEN: "hidden".concat(EVENT_KEY),
SHOW: "show".concat(EVENT_KEY),
SHOWN: "shown".concat(EVENT_KEY),
FOCUSIN: "focusin".concat(EVENT_KEY),
RESIZE: "resize".concat(EVENT_KEY),
CLICK_DISMISS: "click.dismiss".concat(EVENT_KEY),
KEYDOWN_DISMISS: "keydown.dismiss".concat(EVENT_KEY),
MOUSEUP_DISMISS: "mouseup.dismiss".concat(EVENT_KEY),
MOUSEDOWN_DISMISS: "mousedown.dismiss".concat(EVENT_KEY),
CLICK_DATA_API: "click".concat(EVENT_KEY).concat(DATA_API_KEY)
};
var ClassName = {
SCROLLBAR_MEASURER: 'modal-scrollbar-measure',
BACKDROP: 'modal-backdrop',
OPEN: 'modal-open',
FADE: 'fade',
SHOW: 'show'
};
var Selector = {
DIALOG: '.modal-dialog',
DATA_TOGGLE: '[data-toggle="modal"]',
DATA_DISMISS: '[data-dismiss="modal"]',
FIXED_CONTENT: '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top',
STICKY_CONTENT: '.sticky-top',
NAVBAR_TOGGLER: '.navbar-toggler'
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
};
var Modal = function () {
function Modal(element, config) {
_classCallCheck(this, Modal);
this._config = this._getConfig(config);
this._element = element;
this._dialog = $(element).find(Selector.DIALOG)[0];
this._backdrop = null;
this._isShown = false;
this._isBodyOverflowing = false;
this._ignoreBackdropClick = false;
this._originalBodyPadding = 0;
this._scrollbarWidth = 0;
} // Getters
_createClass(Modal, [{
key: "toggle",
value: function toggle(relatedTarget) {
return this._isShown ? this.hide() : this.show(relatedTarget);
}
}, {
key: "show",
value: function show(relatedTarget) {
var _this = this;
if (this._isTransitioning || this._isShown) {
return;
}
if (_util2.default.supportsTransitionEnd() && $(this._element).hasClass(ClassName.FADE)) {
this._isTransitioning = true;
}
var showEvent = $.Event(Event.SHOW, {
relatedTarget: relatedTarget
});
$(this._element).trigger(showEvent);
if (this._isShown || showEvent.isDefaultPrevented()) {
return;
}
this._isShown = true;
this._checkScrollbar();
this._setScrollbar();
this._adjustDialog();
$(document.body).addClass(ClassName.OPEN);
this._setEscapeEvent();
this._setResizeEvent();
$(this._element).on(Event.CLICK_DISMISS, Selector.DATA_DISMISS, function (event) {
return _this.hide(event);
});
$(this._dialog).on(Event.MOUSEDOWN_DISMISS, function () {
$(_this._element).one(Event.MOUSEUP_DISMISS, function (event) {
if ($(event.target).is(_this._element)) {
_this._ignoreBackdropClick = true;
}
});
});
this._showBackdrop(function () {
return _this._showElement(relatedTarget);
});
}
}, {
key: "hide",
value: function hide(event) {
var _this2 = this;
if (event) {
event.preventDefault();
}
if (this._isTransitioning || !this._isShown) {
return;
}
var hideEvent = $.Event(Event.HIDE);
$(this._element).trigger(hideEvent);
if (!this._isShown || hideEvent.isDefaultPrevented()) {
return;
}
this._isShown = false;
var transition = _util2.default.supportsTransitionEnd() && $(this._element).hasClass(ClassName.FADE);
if (transition) {
this._isTransitioning = true;
}
this._setEscapeEvent();
this._setResizeEvent();
$(document).off(Event.FOCUSIN);
$(this._element).removeClass(ClassName.SHOW);
$(this._element).off(Event.CLICK_DISMISS);
$(this._dialog).off(Event.MOUSEDOWN_DISMISS);
if (transition) {
$(this._element).one(_util2.default.TRANSITION_END, function (event) {
return _this2._hideModal(event);
}).emulateTransitionEnd(TRANSITION_DURATION);
} else {
this._hideModal();
}
}
}, {
key: "dispose",
value: function dispose() {
$.removeData(this._element, DATA_KEY);
$(window, document, this._element, this._backdrop).off(EVENT_KEY);
this._config = null;
this._element = null;
this._dialog = null;
this._backdrop = null;
this._isShown = null;
this._isBodyOverflowing = null;
this._ignoreBackdropClick = null;
this._scrollbarWidth = null;
}
}, {
key: "handleUpdate",
value: function handleUpdate() {
this._adjustDialog();
}
}, {
key: "_getConfig",
value: function _getConfig(config) {
config = _extends({}, Default, config);
_util2.default.typeCheckConfig(NAME, config, DefaultType);
return config;
}
}, {
key: "_showElement",
value: function _showElement(relatedTarget) {
var _this3 = this;
var transition = _util2.default.supportsTransitionEnd() && $(this._element).hasClass(ClassName.FADE);
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.scrollTop = 0;
if (transition) {
_util2.default.reflow(this._element);
}
$(this._element).addClass(ClassName.SHOW);
if (this._config.focus) {
this._enforceFocus();
}
var shownEvent = $.Event(Event.SHOWN, {
relatedTarget: relatedTarget
});
var transitionComplete = function transitionComplete() {
if (_this3._config.focus) {
_this3._element.focus();
}
_this3._isTransitioning = false;
$(_this3._element).trigger(shownEvent);
};
if (transition) {
$(this._dialog).one(_util2.default.TRANSITION_END, transitionComplete).emulateTransitionEnd(TRANSITION_DURATION);
} else {
transitionComplete();
}
}
}, {
key: "_enforceFocus",
value: function _enforceFocus() {
var _this4 = this;
$(document).off(Event.FOCUSIN) // Guard against infinite focus loop
.on(Event.FOCUSIN, function (event) {
if (document !== event.target && _this4._element !== event.target && $(_this4._element).has(event.target).length === 0) {
_this4._element.focus();
}
});
}
}, {
key: "_setEscapeEvent",
value: function _setEscapeEvent() {
var _this5 = this;
if (this._isShown && this._config.keyboard) {
$(this._element).on(Event.KEYDOWN_DISMISS, function (event) {
if (event.which === ESCAPE_KEYCODE) {
event.preventDefault();
_this5.hide();
}
});
} else if (!this._isShown) {
$(this._element).off(Event.KEYDOWN_DISMISS);
}
}
}, {
key: "_setResizeEvent",
value: function _setResizeEvent() {
var _this6 = this;
if (this._isShown) {
$(window).on(Event.RESIZE, function (event) {
return _this6.handleUpdate(event);
});
} else {
$(window).off(Event.RESIZE);
}
}
}, {
key: "_hideModal",
value: function _hideModal() {
var _this7 = this;
this._element.style.display = 'none';
this._element.setAttribute('aria-hidden', true);
this._isTransitioning = false;
this._showBackdrop(function () {
$(document.body).removeClass(ClassName.OPEN);
_this7._resetAdjustments();
_this7._resetScrollbar();
$(_this7._element).trigger(Event.HIDDEN);
});
}
}, {
key: "_removeBackdrop",
value: function _removeBackdrop() {
if (this._backdrop) {
$(this._backdrop).remove();
this._backdrop = null;
}
}
}, {
key: "_showBackdrop",
value: function _showBackdrop(callback) {
var _this8 = this;
var animate = $(this._element).hasClass(ClassName.FADE) ? ClassName.FADE : '';
if (this._isShown && this._config.backdrop) {
var doAnimate = _util2.default.supportsTransitionEnd() && animate;
this._backdrop = document.createElement('div');
this._backdrop.className = ClassName.BACKDROP;
if (animate) {
$(this._backdrop).addClass(animate);
}
$(this._backdrop).appendTo(document.body);
$(this._element).on(Event.CLICK_DISMISS, function (event) {
if (_this8._ignoreBackdropClick) {
_this8._ignoreBackdropClick = false;
return;
}
if (event.target !== event.currentTarget) {
return;
}
if (_this8._config.backdrop === 'static') {
_this8._element.focus();
} else {
_this8.hide();
}
});
if (doAnimate) {
_util2.default.reflow(this._backdrop);
}
$(this._backdrop).addClass(ClassName.SHOW);
if (!callback) {
return;
}
if (!doAnimate) {
callback();
return;
}
$(this._backdrop).one(_util2.default.TRANSITION_END, callback).emulateTransitionEnd(BACKDROP_TRANSITION_DURATION);
} else if (!this._isShown && this._backdrop) {
$(this._backdrop).removeClass(ClassName.SHOW);
var callbackRemove = function callbackRemove() {
_this8._removeBackdrop();
if (callback) {
callback();
}
};
if (_util2.default.supportsTransitionEnd() && $(this._element).hasClass(ClassName.FADE)) {
$(this._backdrop).one(_util2.default.TRANSITION_END, callbackRemove).emulateTransitionEnd(BACKDROP_TRANSITION_DURATION);
} else {
callbackRemove();
}
} else if (callback) {
callback();
}
}
}, {
key: "_adjustDialog",
value: function _adjustDialog() {
var isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight;
if (!this._isBodyOverflowing && isModalOverflowing) {
this._element.style.paddingLeft = "".concat(this._scrollbarWidth, "px");
}
if (this._isBodyOverflowing && !isModalOverflowing) {
this._element.style.paddingRight = "".concat(this._scrollbarWidth, "px");
}
}
}, {
key: "_resetAdjustments",
value: function _resetAdjustments() {
this._element.style.paddingLeft = '';
this._element.style.paddingRight = '';
}
}, {
key: "_checkScrollbar",
value: function _checkScrollbar() {
var rect = document.body.getBoundingClientRect();
this._isBodyOverflowing = rect.left + rect.right < window.innerWidth;
this._scrollbarWidth = this._getScrollbarWidth();
}
}, {
key: "_setScrollbar",
value: function _setScrollbar() {
var _this9 = this;
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
// Adjust fixed content padding
$(Selector.FIXED_CONTENT).each(function (index, element) {
var actualPadding = $(element)[0].style.paddingRight;
var calculatedPadding = $(element).css('padding-right');
$(element).data('padding-right', actualPadding).css('padding-right', "".concat(parseFloat(calculatedPadding) + _this9._scrollbarWidth, "px"));
}); // Adjust sticky content margin
$(Selector.STICKY_CONTENT).each(function (index, element) {
var actualMargin = $(element)[0].style.marginRight;
var calculatedMargin = $(element).css('margin-right');
$(element).data('margin-right', actualMargin).css('margin-right', "".concat(parseFloat(calculatedMargin) - _this9._scrollbarWidth, "px"));
}); // Adjust navbar-toggler margin
$(Selector.NAVBAR_TOGGLER).each(function (index, element) {
var actualMargin = $(element)[0].style.marginRight;
var calculatedMargin = $(element).css('margin-right');
$(element).data('margin-right', actualMargin).css('margin-right', "".concat(parseFloat(calculatedMargin) + _this9._scrollbarWidth, "px"));
}); // Adjust body padding
var actualPadding = document.body.style.paddingRight;
var calculatedPadding = $('body').css('padding-right');
$('body').data('padding-right', actualPadding).css('padding-right', "".concat(parseFloat(calculatedPadding) + this._scrollbarWidth, "px"));
}
}
}, {
key: "_resetScrollbar",
value: function _resetScrollbar() {
// Restore fixed content padding
$(Selector.FIXED_CONTENT).each(function (index, element) {
var padding = $(element).data('padding-right');
if (typeof padding !== 'undefined') {
$(element).css('padding-right', padding).removeData('padding-right');
}
}); // Restore sticky content and navbar-toggler margin
$("".concat(Selector.STICKY_CONTENT, ", ").concat(Selector.NAVBAR_TOGGLER)).each(function (index, element) {
var margin = $(element).data('margin-right');
if (typeof margin !== 'undefined') {
$(element).css('margin-right', margin).removeData('margin-right');
}
}); // Restore body padding
var padding = $('body').data('padding-right');
if (typeof padding !== 'undefined') {
$('body').css('padding-right', padding).removeData('padding-right');
}
}
}, {
key: "_getScrollbarWidth",
value: function _getScrollbarWidth() {
// thx d.walsh
var scrollDiv = document.createElement('div');
scrollDiv.className = ClassName.SCROLLBAR_MEASURER;
document.body.appendChild(scrollDiv);
var scrollbarWidth = scrollDiv.getBoundingClientRect().width - scrollDiv.clientWidth;
document.body.removeChild(scrollDiv);
return scrollbarWidth;
}
}], [{
key: "_jQueryInterface",
value: function _jQueryInterface(config, relatedTarget) {
return this.each(function () {
var data = $(this).data(DATA_KEY);
var _config = _extends({}, Modal.Default, $(this).data(), _typeof(config) === 'object' && 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 \"".concat(config, "\""));
}
data[config](relatedTarget);
} else if (_config.show) {
data.show(relatedTarget);
}
});
}
}, {
key: "VERSION",
get: function get() {
return VERSION;
}
}, {
key: "Default",
get: function get() {
return Default;
}
}]);
return Modal;
}();
/**
* ------------------------------------------------------------------------
* Data Api implementation
* ------------------------------------------------------------------------
*/
$(document).on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) {
var _this10 = this;
var target;
var selector = _util2.default.getSelectorFromElement(this);
if (selector) {
target = $(selector)[0];
}
var config = $(target).data(DATA_KEY) ? 'toggle' : _extends({}, $(target).data(), $(this).data());
if (this.tagName === 'A' || this.tagName === 'AREA') {
event.preventDefault();
}
var $target = $(target).one(Event.SHOW, function (showEvent) {
if (showEvent.isDefaultPrevented()) {
// Only register focus restorer if modal will actually get shown
return;
}
$target.one(Event.HIDDEN, function () {
if ($(_this10).is(':visible')) {
_this10.focus();
}
});
});
Modal._jQueryInterface.call($(target), config, this);
});
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = Modal._jQueryInterface;
$.fn[NAME].Constructor = Modal;
$.fn[NAME].noConflict = function () {
$.fn[NAME] = JQUERY_NO_CONFLICT;
return Modal._jQueryInterface;
};
return Modal;
}(_jquery2.default);
exports.default = Modal;
});

120
amd/src/pending.js

@ -0,0 +1,120 @@
// 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_boost/pending
* @copyright 2019 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define(['jquery'], function($) {
var 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',
},
],
tooltip: [
{
start: 'hide',
end: 'hidden',
},
{
start: 'show',
end: 'shown',
},
],
};
Object.keys(moduleTransitions).forEach(function(key) {
moduleTransitions[key].forEach(function(pair) {
var eventStart = pair.start + '.bs.' + key;
var eventEnd = pair.end + '.bs.' + key;
$(document.body).on(eventStart, function() {
M.util.js_pending(eventEnd);
});
$(document.body).on(eventEnd, function() {
M.util.js_complete(eventEnd);
});
});
});
});

297
amd/src/popover.js

@ -0,0 +1,297 @@
define(["exports", "jquery", "./tooltip"], function (exports, _jquery, _tooltip) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _jquery2 = _interopRequireDefault(_jquery);
var _tooltip2 = _interopRequireDefault(_tooltip);
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
function _typeof(obj) {
if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
_typeof = function _typeof(obj) {
return typeof obj;
};
} else {
_typeof = function _typeof(obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
};
}
return _typeof(obj);
}
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
function _possibleConstructorReturn(self, call) {
if (call && (_typeof(call) === "object" || typeof call === "function")) {
return call;
}
if (self === void 0) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
}
return self;
}
function _inherits(subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function");
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
enumerable: false,
writable: true,
configurable: true
}
});
if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
}
function _extends() {
_extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
return _extends.apply(this, arguments);
}
/**
* --------------------------------------------------------------------------
* Bootstrap (v4.0.0): popover.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* --------------------------------------------------------------------------
*/
var Popover = function ($) {
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
var NAME = 'popover';
var VERSION = '4.0.0';
var DATA_KEY = 'bs.popover';
var EVENT_KEY = ".".concat(DATA_KEY);
var JQUERY_NO_CONFLICT = $.fn[NAME];
var CLASS_PREFIX = 'bs-popover';
var BSCLS_PREFIX_REGEX = new RegExp("(^|\\s)".concat(CLASS_PREFIX, "\\S+"), 'g');
var Default = _extends({}, _tooltip2.default.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>'
});
var DefaultType = _extends({}, _tooltip2.default.DefaultType, {
content: '(string|element|function)'
});
var ClassName = {
FADE: 'fade',
SHOW: 'show'
};
var Selector = {
TITLE: '.popover-header',
CONTENT: '.popover-body'
};
var Event = {
HIDE: "hide".concat(EVENT_KEY),
HIDDEN: "hidden".concat(EVENT_KEY),
SHOW: "show".concat(EVENT_KEY),
SHOWN: "shown".concat(EVENT_KEY),
INSERTED: "inserted".concat(EVENT_KEY),
CLICK: "click".concat(EVENT_KEY),
FOCUSIN: "focusin".concat(EVENT_KEY),
FOCUSOUT: "focusout".concat(EVENT_KEY),
MOUSEENTER: "mouseenter".concat(EVENT_KEY),
MOUSELEAVE: "mouseleave".concat(EVENT_KEY)
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
};
var Popover = function (_Tooltip) {
_inherits(Popover, _Tooltip);
function Popover() {
_classCallCheck(this, Popover);
return _possibleConstructorReturn(this, (Popover.__proto__ || Object.getPrototypeOf(Popover)).apply(this, arguments));
}
_createClass(Popover, [{
key: "isWithContent",
value: function isWithContent() {
return this.getTitle() || this._getContent();
}
}, {
key: "addAttachmentClass",
value: function addAttachmentClass(attachment) {
$(this.getTipElement()).addClass("".concat(CLASS_PREFIX, "-").concat(attachment));
}
}, {
key: "getTipElement",
value: function getTipElement() {
this.tip = this.tip || $(this.config.template)[0];
return this.tip;
}
}, {
key: "setContent",
value: function setContent() {
var $tip = $(this.getTipElement()); // We use append for html objects to maintain js events
this.setElementContent($tip.find(Selector.TITLE), this.getTitle());
var content = this._getContent();
if (typeof content === 'function') {
content = content.call(this.element);
}
this.setElementContent($tip.find(Selector.CONTENT), content);
$tip.removeClass("".concat(ClassName.FADE, " ").concat(ClassName.SHOW));
}
}, {
key: "_getContent",
value: function _getContent() {
return this.element.getAttribute('data-content') || this.config.content;
}
}, {
key: "_cleanTipClass",
value: function _cleanTipClass() {
var $tip = $(this.getTipElement());
var tabClass = $tip.attr('class').match(BSCLS_PREFIX_REGEX);
if (tabClass !== null && tabClass.length > 0) {
$tip.removeClass(tabClass.join(''));
}
}
}], [{
key: "_jQueryInterface",
value: function _jQueryInterface(config) {
return this.each(function () {
var data = $(this).data(DATA_KEY);
var _config = _typeof(config) === 'object' ? config : null;
if (!data && /destroy|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 \"".concat(config, "\""));
}
data[config]();
}
});
}
}, {
key: "VERSION",
get: function get() {
return VERSION;
}
}, {
key: "Default",
get: function get() {
return Default;
}
}, {
key: "NAME",
get: function get() {
return NAME;
}
}, {
key: "DATA_KEY",
get: function get() {
return DATA_KEY;
}
}, {
key: "Event",
get: function get() {
return Event;
}
}, {
key: "EVENT_KEY",
get: function get() {
return EVENT_KEY;
}
}, {
key: "DefaultType",
get: function get() {
return DefaultType;
}
}]);
return Popover;
}(_tooltip2.default);
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = Popover._jQueryInterface;
$.fn[NAME].Constructor = Popover;
$.fn[NAME].noConflict = function () {
$.fn[NAME] = JQUERY_NO_CONFLICT;
return Popover._jQueryInterface;
};
return Popover;
}(_jquery2.default);
exports.default = Popover;
});

390
amd/src/scrollspy.js

@ -0,0 +1,390 @@
define(["exports", "jquery", "./util"], function (exports, _jquery, _util) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _jquery2 = _interopRequireDefault(_jquery);
var _util2 = _interopRequireDefault(_util);
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
function _typeof(obj) {
if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
_typeof = function _typeof(obj) {
return typeof obj;
};
} else {
_typeof = function _typeof(obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
};
}
return _typeof(obj);
}
function _extends() {
_extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
return _extends.apply(this, arguments);
}
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
/**
* --------------------------------------------------------------------------
* Bootstrap (v4.0.0): scrollspy.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* --------------------------------------------------------------------------
*/
var ScrollSpy = function ($) {
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
var NAME = 'scrollspy';
var VERSION = '4.0.0';
var DATA_KEY = 'bs.scrollspy';
var EVENT_KEY = ".".concat(DATA_KEY);
var DATA_API_KEY = '.data-api';
var JQUERY_NO_CONFLICT = $.fn[NAME];
var Default = {
offset: 10,
method: 'auto',
target: ''
};
var DefaultType = {
offset: 'number',
method: 'string',
target: '(string|element)'
};
var Event = {
ACTIVATE: "activate".concat(EVENT_KEY),
SCROLL: "scroll".concat(EVENT_KEY),
LOAD_DATA_API: "load".concat(EVENT_KEY).concat(DATA_API_KEY)
};
var ClassName = {
DROPDOWN_ITEM: 'dropdown-item',
DROPDOWN_MENU: 'dropdown-menu',
ACTIVE: 'active'
};
var Selector = {
DATA_SPY: '[data-spy="scroll"]',
ACTIVE: '.active',
NAV_LIST_GROUP: '.nav, .list-group',
NAV_LINKS: '.nav-link',
NAV_ITEMS: '.nav-item',
LIST_ITEMS: '.list-group-item',
DROPDOWN: '.dropdown',
DROPDOWN_ITEMS: '.dropdown-item',
DROPDOWN_TOGGLE: '.dropdown-toggle'
};
var OffsetMethod = {
OFFSET: 'offset',
POSITION: 'position'
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
};
var ScrollSpy = function () {
function ScrollSpy(element, config) {
var _this = this;
_classCallCheck(this, ScrollSpy);
this._element = element;
this._scrollElement = element.tagName === 'BODY' ? window : element;
this._config = this._getConfig(config);
this._selector = "".concat(this._config.target, " ").concat(Selector.NAV_LINKS, ",") + "".concat(this._config.target, " ").concat(Selector.LIST_ITEMS, ",") + "".concat(this._config.target, " ").concat(Selector.DROPDOWN_ITEMS);
this._offsets = [];
this._targets = [];
this._activeTarget = null;
this._scrollHeight = 0;
$(this._scrollElement).on(Event.SCROLL, function (event) {
return _this._process(event);
});
this.refresh();
this._process();
} // Getters
_createClass(ScrollSpy, [{
key: "refresh",
value: function refresh() {
var _this2 = this;
var autoMethod = this._scrollElement === this._scrollElement.window ? OffsetMethod.OFFSET : OffsetMethod.POSITION;
var offsetMethod = this._config.method === 'auto' ? autoMethod : this._config.method;
var offsetBase = offsetMethod === OffsetMethod.POSITION ? this._getScrollTop() : 0;
this._offsets = [];
this._targets = [];
this._scrollHeight = this._getScrollHeight();
var targets = $.makeArray($(this._selector));
targets.map(function (element) {
var target;
var targetSelector = _util2.default.getSelectorFromElement(element);
if (targetSelector) {
target = $(targetSelector)[0];
}
if (target) {
var 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(function (item) {
return item;
}).sort(function (a, b) {
return a[0] - b[0];
}).forEach(function (item) {
_this2._offsets.push(item[0]);
_this2._targets.push(item[1]);
});
}
}, {
key: "dispose",
value: function 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;
}
}, {
key: "_getConfig",
value: function _getConfig(config) {
config = _extends({}, Default, config);
if (typeof config.target !== 'string') {
var id = $(config.target).attr('id');
if (!id) {
id = _util2.default.getUID(NAME);
$(config.target).attr('id', id);
}
config.target = "#".concat(id);
}
_util2.default.typeCheckConfig(NAME, config, DefaultType);
return config;
}
}, {
key: "_getScrollTop",
value: function _getScrollTop() {
return this._scrollElement === window ? this._scrollElement.pageYOffset : this._scrollElement.scrollTop;
}
}, {
key: "_getScrollHeight",
value: function _getScrollHeight() {
return this._scrollElement.scrollHeight || Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
}
}, {
key: "_getOffsetHeight",
value: function _getOffsetHeight() {
return this._scrollElement === window ? window.innerHeight : this._scrollElement.getBoundingClientRect().height;
}
}, {
key: "_process",
value: function _process() {
var scrollTop = this._getScrollTop() + this._config.offset;
var scrollHeight = this._getScrollHeight();
var maxScroll = this._config.offset + scrollHeight - this._getOffsetHeight();
if (this._scrollHeight !== scrollHeight) {
this.refresh();
}
if (scrollTop >= maxScroll) {
var 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 (var i = this._offsets.length; i--;) {
var 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]);
}
}
}
}, {
key: "_activate",
value: function _activate(target) {
this._activeTarget = target;
this._clear();
var queries = this._selector.split(','); // eslint-disable-next-line arrow-body-style
queries = queries.map(function (selector) {
return "".concat(selector, "[data-target=\"").concat(target, "\"],") + "".concat(selector, "[href=\"").concat(target, "\"]");
});
var $link = $(queries.join(','));
if ($link.hasClass(ClassName.DROPDOWN_ITEM)) {
$link.closest(Selector.DROPDOWN).find(Selector.DROPDOWN_TOGGLE).addClass(ClassName.ACTIVE);
$link.addClass(ClassName.ACTIVE);
} else {
// Set triggered link as active
$link.addClass(ClassName.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("".concat(Selector.NAV_LINKS, ", ").concat(Selector.LIST_ITEMS)).addClass(ClassName.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(ClassName.ACTIVE);
}
$(this._scrollElement).trigger(Event.ACTIVATE, {
relatedTarget: target
});
}
}, {
key: "_clear",
value: function _clear() {
$(this._selector).filter(Selector.ACTIVE).removeClass(ClassName.ACTIVE);
}
}], [{
key: "_jQueryInterface",
value: function _jQueryInterface(config) {
return this.each(function () {
var data = $(this).data(DATA_KEY);
var _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 \"".concat(config, "\""));
}
data[config]();
}
});
}
}, {
key: "VERSION",
get: function get() {
return VERSION;
}
}, {
key: "Default",
get: function get() {
return Default;
}
}]);
return ScrollSpy;
}();
/**
* ------------------------------------------------------------------------
* Data Api implementation
* ------------------------------------------------------------------------
*/
$(window).on(Event.LOAD_DATA_API, function () {
var scrollSpys = $.makeArray($(Selector.DATA_SPY));
for (var i = scrollSpys.length; i--;) {
var $spy = $(scrollSpys[i]);
ScrollSpy._jQueryInterface.call($spy, $spy.data());
}
});
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = ScrollSpy._jQueryInterface;
$.fn[NAME].Constructor = ScrollSpy;
$.fn[NAME].noConflict = function () {
$.fn[NAME] = JQUERY_NO_CONFLICT;
return ScrollSpy._jQueryInterface;
};
return ScrollSpy;
}(_jquery2.default);
exports.default = ScrollSpy;
});

289
amd/src/tab.js

@ -0,0 +1,289 @@
define(["exports", "jquery", "./util"], function (exports, _jquery, _util) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _jquery2 = _interopRequireDefault(_jquery);
var _util2 = _interopRequireDefault(_util);
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
/**
* --------------------------------------------------------------------------
* Bootstrap (v4.0.0): tab.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* --------------------------------------------------------------------------
*/
var Tab = function ($) {
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
var NAME = 'tab';
var VERSION = '4.0.0';
var DATA_KEY = 'bs.tab';
var EVENT_KEY = ".".concat(DATA_KEY);
var DATA_API_KEY = '.data-api';
var JQUERY_NO_CONFLICT = $.fn[NAME];
var TRANSITION_DURATION = 150;
var Event = {
HIDE: "hide".concat(EVENT_KEY),
HIDDEN: "hidden".concat(EVENT_KEY),
SHOW: "show".concat(EVENT_KEY),
SHOWN: "shown".concat(EVENT_KEY),
CLICK_DATA_API: "click".concat(EVENT_KEY).concat(DATA_API_KEY)
};
var ClassName = {
DROPDOWN_MENU: 'dropdown-menu',
ACTIVE: 'active',
DISABLED: 'disabled',
FADE: 'fade',
SHOW: 'show'
};
var Selector = {
DROPDOWN: '.dropdown',
NAV_LIST_GROUP: '.nav, .list-group',
ACTIVE: '.active',
ACTIVE_UL: '> li > .active',
DATA_TOGGLE: '[data-toggle="tab"], [data-toggle="pill"], [data-toggle="list"]',
DROPDOWN_TOGGLE: '.dropdown-toggle',
DROPDOWN_ACTIVE_CHILD: '> .dropdown-menu .active'
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
};
var Tab = function () {
function Tab(element) {
_classCallCheck(this, Tab);
this._element = element;
} // Getters
_createClass(Tab, [{
key: "show",
value: function show() {
var _this = this;
if (this._element.parentNode && this._element.parentNode.nodeType === Node.ELEMENT_NODE && $(this._element).hasClass(ClassName.ACTIVE) || $(this._element).hasClass(ClassName.DISABLED)) {
return;
}
var target;
var previous;
var listElement = $(this._element).closest(Selector.NAV_LIST_GROUP)[0];
var selector = _util2.default.getSelectorFromElement(this._element);
if (listElement) {
var itemSelector = listElement.nodeName === 'UL' ? Selector.ACTIVE_UL : Selector.ACTIVE;
previous = $.makeArray($(listElement).find(itemSelector));
previous = previous[previous.length - 1];
}
var hideEvent = $.Event(Event.HIDE, {
relatedTarget: this._element
});
var showEvent = $.Event(Event.SHOW, {
relatedTarget: previous
});
if (previous) {
$(previous).trigger(hideEvent);
}
$(this._element).trigger(showEvent);
if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) {
return;
}
if (selector) {
target = $(selector)[0];
}
this._activate(this._element, listElement);
var complete = function complete() {
var hiddenEvent = $.Event(Event.HIDDEN, {
relatedTarget: _this._element
});
var shownEvent = $.Event(Event.SHOWN, {
relatedTarget: previous
});
$(previous).trigger(hiddenEvent);
$(_this._element).trigger(shownEvent);
};
if (target) {
this._activate(target, target.parentNode, complete);
} else {
complete();
}
}
}, {
key: "dispose",
value: function dispose() {
$.removeData(this._element, DATA_KEY);
this._element = null;
}
}, {
key: "_activate",
value: function _activate(element, container, callback) {
var _this2 = this;
var activeElements;
if (container.nodeName === 'UL') {
activeElements = $(container).find(Selector.ACTIVE_UL);
} else {
activeElements = $(container).children(Selector.ACTIVE);
}
var active = activeElements[0];
var isTransitioning = callback && _util2.default.supportsTransitionEnd() && active && $(active).hasClass(ClassName.FADE);
var complete = function complete() {
return _this2._transitionComplete(element, active, callback);
};
if (active && isTransitioning) {
$(active).one(_util2.default.TRANSITION_END, complete).emulateTransitionEnd(TRANSITION_DURATION);
} else {
complete();
}
}
}, {
key: "_transitionComplete",
value: function _transitionComplete(element, active, callback) {
if (active) {
$(active).removeClass("".concat(ClassName.SHOW, " ").concat(ClassName.ACTIVE));
var dropdownChild = $(active.parentNode).find(Selector.DROPDOWN_ACTIVE_CHILD)[0];
if (dropdownChild) {
$(dropdownChild).removeClass(ClassName.ACTIVE);
}
if (active.getAttribute('role') === 'tab') {
active.setAttribute('aria-selected', false);
}
}
$(element).addClass(ClassName.ACTIVE);
if (element.getAttribute('role') === 'tab') {
element.setAttribute('aria-selected', true);
}
_util2.default.reflow(element);
$(element).addClass(ClassName.SHOW);
if (element.parentNode && $(element.parentNode).hasClass(ClassName.DROPDOWN_MENU)) {
var dropdownElement = $(element).closest(Selector.DROPDOWN)[0];
if (dropdownElement) {
$(dropdownElement).find(Selector.DROPDOWN_TOGGLE).addClass(ClassName.ACTIVE);
}
element.setAttribute('aria-expanded', true);
}
if (callback) {
callback();
}
}
}], [{
key: "_jQueryInterface",
value: function _jQueryInterface(config) {
return this.each(function () {
var $this = $(this);
var 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 \"".concat(config, "\""));
}
data[config]();
}
});
}
}, {
key: "VERSION",
get: function get() {
return VERSION;
}
}]);
return Tab;
}();
/**
* ------------------------------------------------------------------------
* 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 = function () {
$.fn[NAME] = JQUERY_NO_CONFLICT;
return Tab._jQueryInterface;
};
return Tab;
}(_jquery2.default);
exports.default = Tab;
});

1792
amd/src/tether.js

File diff suppressed because it is too large

762
amd/src/tooltip.js

@ -0,0 +1,762 @@
define(["exports", "jquery", "core/popper", "./util"], function (exports, _jquery, _popper, _util) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _jquery2 = _interopRequireDefault(_jquery);
var _popper2 = _interopRequireDefault(_popper);
var _util2 = _interopRequireDefault(_util);
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
function _extends() {
_extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
return _extends.apply(this, arguments);
}
function _typeof(obj) {
if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
_typeof = function _typeof(obj) {
return typeof obj;
};
} else {
_typeof = function _typeof(obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
};
}
return _typeof(obj);
}
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
/**
* --------------------------------------------------------------------------
* Bootstrap (v4.0.0): tooltip.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* --------------------------------------------------------------------------
*/
var Tooltip = function ($) {
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
var NAME = 'tooltip';
var VERSION = '4.0.0';
var DATA_KEY = 'bs.tooltip';
var EVENT_KEY = ".".concat(DATA_KEY);
var JQUERY_NO_CONFLICT = $.fn[NAME];
var TRANSITION_DURATION = 150;
var CLASS_PREFIX = 'bs-tooltip';
var BSCLS_PREFIX_REGEX = new RegExp("(^|\\s)".concat(CLASS_PREFIX, "\\S+"), 'g');
var 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)',
container: '(string|element|boolean)',
fallbackPlacement: '(string|array)',
boundary: '(string|element)'
};
var AttachmentMap = {
AUTO: 'auto',
TOP: 'top',
RIGHT: 'right',
BOTTOM: 'bottom',
LEFT: 'left'
};
var 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'
};
var HoverState = {
SHOW: 'show',
OUT: 'out'
};
var Event = {
HIDE: "hide".concat(EVENT_KEY),
HIDDEN: "hidden".concat(EVENT_KEY),
SHOW: "show".concat(EVENT_KEY),
SHOWN: "shown".concat(EVENT_KEY),
INSERTED: "inserted".concat(EVENT_KEY),
CLICK: "click".concat(EVENT_KEY),
FOCUSIN: "focusin".concat(EVENT_KEY),
FOCUSOUT: "focusout".concat(EVENT_KEY),
MOUSEENTER: "mouseenter".concat(EVENT_KEY),
MOUSELEAVE: "mouseleave".concat(EVENT_KEY)
};
var ClassName = {
FADE: 'fade',
SHOW: 'show'
};
var Selector = {
TOOLTIP: '.tooltip',
TOOLTIP_INNER: '.tooltip-inner',
ARROW: '.arrow'
};
var Trigger = {
HOVER: 'hover',
FOCUS: 'focus',
CLICK: 'click',
MANUAL: 'manual'
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
};
var Tooltip = function () {
function Tooltip(element, config) {
_classCallCheck(this, Tooltip);
/**
* Check for Popper dependency
* Popper - https://popper.js.org
*/
if (typeof _popper2.default === 'undefined') {
throw new TypeError('Bootstrap tooltips require Popper.js (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
_createClass(Tooltip, [{
key: "enable",
value: function enable() {
this._isEnabled = true;
}
}, {
key: "disable",
value: function disable() {
this._isEnabled = false;
}
}, {
key: "toggleEnabled",
value: function toggleEnabled() {
this._isEnabled = !this._isEnabled;
}
}, {
key: "toggle",
value: function toggle(event) {
if (!this._isEnabled) {
return;
}
if (event) {
var dataKey = this.constructor.DATA_KEY;
var 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(ClassName.SHOW)) {
this._leave(null, this);
return;
}
this._enter(null, this);
}
}
}, {
key: "dispose",
value: function 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');
if (this.tip) {
$(this.tip).remove();
}
this._isEnabled = null;
this._timeout = null;
this._hoverState = null;
this._activeTrigger = null;
if (this._popper !== null) {
this._popper.destroy();
}
this._popper = null;
this.element = null;
this.config = null;
this.tip = null;
}
}, {
key: "show",
value: function show() {
var _this = this;
if ($(this.element).css('display') === 'none') {
throw new Error('Please use show on visible elements');
}
var showEvent = $.Event(this.constructor.Event.SHOW);
if (this.isWithContent() && this._isEnabled) {
$(this.element).trigger(showEvent);
var isInTheDom = $.contains(this.element.ownerDocument.documentElement, this.element);
if (showEvent.isDefaultPrevented() || !isInTheDom) {
return;
}
var tip = this.getTipElement();
var tipId = _util2.default.getUID(this.constructor.NAME);
tip.setAttribute('id', tipId);
this.element.setAttribute('aria-describedby', tipId);
this.setContent();
if (this.config.animation) {
$(tip).addClass(ClassName.FADE);
}
var placement = typeof this.config.placement === 'function' ? this.config.placement.call(this, tip, this.element) : this.config.placement;
var attachment = this._getAttachment(placement);
this.addAttachmentClass(attachment);
var container = this.config.container === false ? document.body : $(this.config.container);
$(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 _popper2.default(this.element, tip, {
placement: attachment,
modifiers: {
offset: {
offset: this.config.offset
},
flip: {
behavior: this.config.fallbackPlacement
},
arrow: {
element: Selector.ARROW
},
preventOverflow: {
boundariesElement: this.config.boundary
}
},
onCreate: function onCreate(data) {
if (data.originalPlacement !== data.placement) {
_this._handlePopperPlacementChange(data);
}
},
onUpdate: function onUpdate(data) {
_this._handlePopperPlacementChange(data);
}
});
$(tip).addClass(ClassName.SHOW); // 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) {
$('body').children().on('mouseover', null, $.noop);
}
var complete = function complete() {
if (_this.config.animation) {
_this._fixTransition();
}
var prevHoverState = _this._hoverState;
_this._hoverState = null;
$(_this.element).trigger(_this.constructor.Event.SHOWN);
if (prevHoverState === HoverState.OUT) {
_this._leave(null, _this);
}
};
if (_util2.default.supportsTransitionEnd() && $(this.tip).hasClass(ClassName.FADE)) {
$(this.tip).one(_util2.default.TRANSITION_END, complete).emulateTransitionEnd(Tooltip._TRANSITION_DURATION);
} else {
complete();
}
}
}
}, {
key: "hide",
value: function hide(callback) {
var _this2 = this;
var tip = this.getTipElement();
var hideEvent = $.Event(this.constructor.Event.HIDE);
var complete = function complete() {
if (_this2._hoverState !== HoverState.SHOW && tip.parentNode) {
tip.parentNode.removeChild(tip);
}
_this2._cleanTipClass();
_this2.element.removeAttribute('aria-describedby');
$(_this2.element).trigger(_this2.constructor.Event.HIDDEN);
if (_this2._popper !== null) {
_this2._popper.destroy();
}
if (callback) {
callback();
}
};
$(this.element).trigger(hideEvent);
if (hideEvent.isDefaultPrevented()) {
return;
}
$(tip).removeClass(ClassName.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) {
$('body').children().off('mouseover', null, $.noop);
}
this._activeTrigger[Trigger.CLICK] = false;
this._activeTrigger[Trigger.FOCUS] = false;
this._activeTrigger[Trigger.HOVER] = false;
if (_util2.default.supportsTransitionEnd() && $(this.tip).hasClass(ClassName.FADE)) {
$(tip).one(_util2.default.TRANSITION_END, complete).emulateTransitionEnd(TRANSITION_DURATION);
} else {
complete();
}
this._hoverState = '';
}
}, {
key: "update",
value: function update() {
if (this._popper !== null) {
this._popper.scheduleUpdate();
}
}
}, {
key: "isWithContent",
value: function isWithContent() {
return Boolean(this.getTitle());
}
}, {
key: "addAttachmentClass",
value: function addAttachmentClass(attachment) {
$(this.getTipElement()).addClass("".concat(CLASS_PREFIX, "-").concat(attachment));
}
}, {
key: "getTipElement",
value: function getTipElement() {
this.tip = this.tip || $(this.config.template)[0];
return this.tip;
}
}, {
key: "setContent",
value: function setContent() {
var $tip = $(this.getTipElement());
this.setElementContent($tip.find(Selector.TOOLTIP_INNER), this.getTitle());
$tip.removeClass("".concat(ClassName.FADE, " ").concat(ClassName.SHOW));
}
}, {
key: "setElementContent",
value: function setElementContent($element, content) {
var html = this.config.html;
if (_typeof(content) === 'object' && (content.nodeType || content.jquery)) {
// Content is a DOM node or a jQuery
if (html) {
if (!$(content).parent().is($element)) {
$element.empty().append(content);
}
} else {
$element.text($(content).text());
}
} else {
$element[html ? 'html' : 'text'](content);
}
}
}, {
key: "getTitle",
value: function getTitle() {
var 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;
}
}, {
key: "_getAttachment",
value: function _getAttachment(placement) {
return AttachmentMap[placement.toUpperCase()];
}
}, {
key: "_setListeners",
value: function _setListeners() {
var _this3 = this;
var triggers = this.config.trigger.split(' ');
triggers.forEach(function (trigger) {
if (trigger === 'click') {
$(_this3.element).on(_this3.constructor.Event.CLICK, _this3.config.selector, function (event) {
return _this3.toggle(event);
});
} else if (trigger !== Trigger.MANUAL) {
var eventIn = trigger === Trigger.HOVER ? _this3.constructor.Event.MOUSEENTER : _this3.constructor.Event.FOCUSIN;
var eventOut = trigger === Trigger.HOVER ? _this3.constructor.Event.MOUSELEAVE : _this3.constructor.Event.FOCUSOUT;
$(_this3.element).on(eventIn, _this3.config.selector, function (event) {
return _this3._enter(event);
}).on(eventOut, _this3.config.selector, function (event) {
return _this3._leave(event);
});
}
$(_this3.element).closest('.modal').on('hide.bs.modal', function () {
return _this3.hide();
});
});
if (this.config.selector) {
this.config = _extends({}, this.config, {
trigger: 'manual',
selector: ''
});
} else {
this._fixTitle();
}
}
}, {
key: "_fixTitle",
value: function _fixTitle() {
var 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', '');
}
}
}, {
key: "_enter",
value: function _enter(event, context) {
var 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(ClassName.SHOW) || context._hoverState === HoverState.SHOW) {
context._hoverState = HoverState.SHOW;
return;
}
clearTimeout(context._timeout);
context._hoverState = HoverState.SHOW;
if (!context.config.delay || !context.config.delay.show) {
context.show();
return;
}
context._timeout = setTimeout(function () {
if (context._hoverState === HoverState.SHOW) {
context.show();
}
}, context.config.delay.show);
}
}, {
key: "_leave",
value: function _leave(event, context) {
var 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 = HoverState.OUT;
if (!context.config.delay || !context.config.delay.hide) {
context.hide();
return;
}
context._timeout = setTimeout(function () {
if (context._hoverState === HoverState.OUT) {
context.hide();
}
}, context.config.delay.hide);
}
}, {
key: "_isWithActiveTrigger",
value: function _isWithActiveTrigger() {
for (var trigger in this._activeTrigger) {
if (this._activeTrigger[trigger]) {
return true;
}
}
return false;
}
}, {
key: "_getConfig",
value: function _getConfig(config) {
config = _extends({}, this.constructor.Default, $(this.element).data(), 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();
}
_util2.default.typeCheckConfig(NAME, config, this.constructor.DefaultType);
return config;
}
}, {
key: "_getDelegateConfig",
value: function _getDelegateConfig() {
var config = {};
if (this.config) {
for (var key in this.config) {
if (this.constructor.Default[key] !== this.config[key]) {
config[key] = this.config[key];
}
}
}
return config;
}
}, {
key: "_cleanTipClass",
value: function _cleanTipClass() {
var $tip = $(this.getTipElement());
var tabClass = $tip.attr('class').match(BSCLS_PREFIX_REGEX);
if (tabClass !== null && tabClass.length > 0) {
$tip.removeClass(tabClass.join(''));
}
}
}, {
key: "_handlePopperPlacementChange",
value: function _handlePopperPlacementChange(data) {
this._cleanTipClass();
this.addAttachmentClass(this._getAttachment(data.placement));
}
}, {
key: "_fixTransition",
value: function _fixTransition() {
var tip = this.getTipElement();
var initConfigAnimation = this.config.animation;
if (tip.getAttribute('x-placement') !== null) {
return;
}
$(tip).removeClass(ClassName.FADE);
this.config.animation = false;
this.hide();
this.show();
this.config.animation = initConfigAnimation;
}
}], [{
key: "_jQueryInterface",
value: function _jQueryInterface(config) {
return this.each(function () {
var data = $(this).data(DATA_KEY);
var _config = _typeof(config) === 'object' && config;
if (!data && /dispose|hide/.test(config)) {
return;
}
if (!data) {
data = new Tooltip(this, _config);
$(this).data(DATA_KEY, data);
}
if (typeof config === 'string') {
if (typeof data[config] === 'undefined') {
throw new TypeError("No method named \"".concat(config, "\""));
}
data[config]();
}
});
}
}, {
key: "VERSION",
get: function get() {
return VERSION;
}
}, {
key: "Default",
get: function get() {
return Default;
}
}, {
key: "NAME",
get: function get() {
return NAME;
}
}, {
key: "DATA_KEY",
get: function get() {
return DATA_KEY;
}
}, {
key: "Event",
get: function get() {
return Event;
}
}, {
key: "EVENT_KEY",
get: function get() {
return EVENT_KEY;
}
}, {
key: "DefaultType",
get: function get() {
return DefaultType;
}
}]);
return Tooltip;
}();
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = Tooltip._jQueryInterface;
$.fn[NAME].Constructor = Tooltip;
$.fn[NAME].noConflict = function () {
$.fn[NAME] = JQUERY_NO_CONFLICT;
return Tooltip._jQueryInterface;
};
return Tooltip;
}(_jquery2.default, _popper2.default);
exports.default = Tooltip;
});

156
amd/src/util.js

@ -0,0 +1,156 @@
define(["exports", "jquery"], function (exports, _jquery) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _jquery2 = _interopRequireDefault(_jquery);
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
/**
* --------------------------------------------------------------------------
* Bootstrap (v4.0.0): util.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* --------------------------------------------------------------------------
*/
var Util = function ($) {
/**
* ------------------------------------------------------------------------
* Private TransitionEnd Helpers
* ------------------------------------------------------------------------
*/
var transition = false;
var MAX_UID = 1000000; // Shoutout AngusCroll (https://goo.gl/pxwQGp)
function toType(obj) {
return {}.toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase();
}
function getSpecialTransitionEndEvent() {
return {
bindType: transition.end,
delegateType: transition.end,
handle: function handle(event) {
if ($(event.target).is(this)) {
return event.handleObj.handler.apply(this, arguments); // eslint-disable-line prefer-rest-params
}
return undefined; // eslint-disable-line no-undefined
}
};
}
function transitionEndTest() {
if (typeof window !== 'undefined' && window.QUnit) {
return false;
}
return {
end: 'transitionend'
};
}
function transitionEndEmulator(duration) {
var _this = this;
var called = false;
$(this).one(Util.TRANSITION_END, function () {
called = true;
});
setTimeout(function () {
if (!called) {
Util.triggerTransitionEnd(_this);
}
}, duration);
return this;
}
function setTransitionEndSupport() {
transition = transitionEndTest();
$.fn.emulateTransitionEnd = transitionEndEmulator;
if (Util.supportsTransitionEnd()) {
$.event.special[Util.TRANSITION_END] = getSpecialTransitionEndEvent();
}
}
function escapeId(selector) {
// We escape IDs in case of special selectors (selector = '#myId:something')
// $.escapeSelector does not exist in jQuery < 3
selector = typeof $.escapeSelector === 'function' ? $.escapeSelector(selector).substr(1) : selector.replace(/(:|\.|\[|\]|,|=|@)/g, '\\$1');
return selector;
}
/**
* --------------------------------------------------------------------------
* Public Util Api
* --------------------------------------------------------------------------
*/
var Util = {
TRANSITION_END: 'bsTransitionEnd',
getUID: function getUID(prefix) {
do {
// eslint-disable-next-line no-bitwise
prefix += ~~(Math.random() * MAX_UID); // "~~" acts like a faster Math.floor() here
} while (document.getElementById(prefix));
return prefix;
},
getSelectorFromElement: function getSelectorFromElement(element) {
var selector = element.getAttribute('data-target');
if (!selector || selector === '#') {
selector = element.getAttribute('href') || '';
} // If it's an ID
if (selector.charAt(0) === '#') {
selector = escapeId(selector);
}
try {
var $selector = $(document).find(selector);
return $selector.length > 0 ? selector : null;
} catch (err) {
return null;
}
},
reflow: function reflow(element) {
return element.offsetHeight;
},
triggerTransitionEnd: function triggerTransitionEnd(element) {
$(element).trigger(transition.end);
},
supportsTransitionEnd: function supportsTransitionEnd() {
return Boolean(transition);
},
isElement: function isElement(obj) {
return (obj[0] || obj).nodeType;
},
typeCheckConfig: function typeCheckConfig(componentName, config, configTypes) {
for (var property in configTypes) {
if (Object.prototype.hasOwnProperty.call(configTypes, property)) {
var expectedTypes = configTypes[property];
var value = config[property];
var valueType = value && Util.isElement(value) ? 'element' : toType(value);
if (!new RegExp(expectedTypes).test(valueType)) {
throw new Error("".concat(componentName.toUpperCase(), ": ") + "Option \"".concat(property, "\" provided type \"").concat(valueType, "\" ") + "but expected type \"".concat(expectedTypes, "\"."));
}
}
}
}
};
setTransitionEndSupport();
return Util;
}(_jquery2.default);
exports.default = Util;
});

8
classes/admin_settingspage_tabs.php

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

8
classes/autoprefixer.php

@ -18,15 +18,15 @@
* Autoprefixer. * Autoprefixer.
* *
* This autoprefixer has been developed to satisfy the basic needs of the * 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. * 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 * @copyright 2016 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/ */
namespace theme_boost; namespace theme_ilb;
defined('MOODLE_INTERNAL') || die(); defined('MOODLE_INTERNAL') || die();
use Sabberworm\CSS\CSSList\CSSList; use Sabberworm\CSS\CSSList\CSSList;
@ -54,7 +54,7 @@ use Sabberworm\CSS\Value\ValueList;
* *
* Very basic implementation covering simple needs for Bootstrap 4. * Very basic implementation covering simple needs for Bootstrap 4.
* *
* @package theme_boost * @package theme_ilb
* @copyright 2016 Frédéric Massart - FMCorz.net * @copyright 2016 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/ */

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);
}
}

453
classes/output/core/course_renderer.php

@ -22,10 +22,15 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/ */
namespace theme_boost\output\core; namespace theme_ilb\output\core;
defined('MOODLE_INTERNAL') || die(); defined('MOODLE_INTERNAL') || die();
use moodle_url; use moodle_url;
use core_course_category;
use core_course_list_element;
use html_writer;
use coursecat_helper;
use stdClass;
require_once($CFG->dirroot . '/course/renderer.php'); require_once($CFG->dirroot . '/course/renderer.php');
@ -36,46 +41,430 @@ require_once($CFG->dirroot . '/course/renderer.php');
* @copyright 2016 Frédéric Massart - FMCorz.net * @copyright 2016 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/ */
/**
* Class course_renderer
* @package theme_trema
* @copyright 2019 Trema - {@link https://trema.tech/}
* @author Rodrigo Mady
* @author Trevor Furtado
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class course_renderer extends \core_course_renderer { class course_renderer extends \core_course_renderer {
// Renderiza caixa de informações de curso
// @param $displayCourseInfo indica se deve exibir informações do curso à direita (true)
// ou em botão "Mais Informações" (false)
public function course_info_box(stdClass $course, $displayCourseInfo = true) {
if(!$displayCourseInfo){
$content = '';
$content .= $this->output->box_start('generalbox info');
$chelper = new coursecat_helper();
$chelper->set_show_courses(self::COURSECAT_SHOW_COURSES_EXPANDED);
$content .= $this->coursecat_coursebox($chelper, $course);
$content .= $this->output->box_end();
return $content;
}else{
$content = '<div class="row">';
$content .= '<div class="box generalbox info col-sm-3">';
$chelper = new coursecat_helper();
$chelper->set_show_courses(self::COURSECAT_SHOW_COURSES_EXPANDED);
$content .= $this->coursecat_coursebox($chelper, $course, '', $displayCourseInfo);
$content .= '</div>';
if ($course instanceof stdClass) {
$course = new core_course_list_element($course);
}
$content .= '<div class="col-sm-9">';
$content .= $chelper->get_course_formatted_summary($course, array('noclean' => true, 'para' => false));
$content .= '</div>';
$content .= '</div>';
return $content;
}
}
/** /**
* Renders html to display a course search form. * Renders the list of courses for frontpage and /course
*
* If list of courses is specified in $courses; the argument $chelper is only used
* to retrieve display options and attributes, only methods get_show_courses(),
* get_courses_display_option() and get_and_erase_attributes() are called.
* *
* @param string $value default value to populate the search field * @param coursecat_helper $chelper various display options
* @param string $format display format - 'plain' (default), 'short' or 'navbar' * @param array $courses the list of courses to display
* @param int|null $totalcount total number of courses (affects display mode if it is AUTO or pagination if applicable),
* defaulted to count($courses)
* @return string * @return string
*/ */
public function course_search_form($value = '', $format = 'plain') { protected function coursecat_courses(coursecat_helper $chelper, $courses, $totalcount = null) {
static $count = 0; global $CFG;
$formid = 'coursesearch'; if ($totalcount === null) {
if ((++$count) > 1) { $totalcount = count($courses);
$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;
} }
if (!$totalcount) {
// Courses count is cached during courses retrieval.
return '';
}
if ($chelper->get_show_courses() == self::COURSECAT_SHOW_COURSES_AUTO) {
// In 'auto' course display mode we analyse if number of courses is more or less than $CFG->courseswithsummarieslimit.
if ($totalcount <= $CFG->courseswithsummarieslimit) {
$chelper->set_show_courses(self::COURSECAT_SHOW_COURSES_EXPANDED);
} else {
$chelper->set_show_courses(self::COURSECAT_SHOW_COURSES_COLLAPSED);
}
}
// Prepare content of paging bar if it is needed.
$paginationurl = $chelper->get_courses_display_option('paginationurl');
$paginationallowall = $chelper->get_courses_display_option('paginationallowall');
if ($totalcount > count($courses)) {
// There are more results that can fit on one page.
if ($paginationurl) {
// The option paginationurl was specified, display pagingbar.
$perpage = $chelper->get_courses_display_option('limit', $CFG->coursesperpage);
$page = $chelper->get_courses_display_option('offset') / $perpage;
$pagingbar = $this->paging_bar($totalcount, $page, $perpage,
$paginationurl->out(false, array('perpage' => $perpage)));
if ($paginationallowall) {
$pagingbar .= html_writer::tag('div', html_writer::link($paginationurl->out(false, array('perpage' => 'all')),
get_string('showall', '', $totalcount)), array('class' => 'paging paging-showall'));
}
} else if ($viewmoreurl = $chelper->get_courses_display_option('viewmoreurl')) {
// The option for 'View more' link was specified, display more link.
$viewmoretext = $chelper->get_courses_display_option('viewmoretext', new \lang_string('viewmore'));
$morelink = html_writer::tag('div', html_writer::link($viewmoreurl, $viewmoretext),
array('class' => 'paging paging-morelink'));
}
} else if (($totalcount > $CFG->coursesperpage) && $paginationurl && $paginationallowall) {
// There are more than one page of results and we are in 'view all' mode, suggest to go back to paginated view mode.
$pagingbar = html_writer::tag(
'div',
html_writer::link(
$paginationurl->out(
false,
array('perpage' => $CFG->coursesperpage)
),
get_string('showperpage', '', $CFG->coursesperpage)
),
array('class' => 'paging paging-showperpage')
);
}
// Display list of courses.
$attributes = $chelper->get_and_erase_attributes('courses');
$content = html_writer::start_tag('div', $attributes);
if (!empty($pagingbar)) {
$content .= $pagingbar;
}
$coursecount = 1;
$content .= html_writer::start_tag('div', array('class' => ' row card-deck my-4'));
foreach ($courses as $course) {
$content .= $this->coursecat_coursebox($chelper, $course, 'card mb-3 course-card-view boxCursos tamanhoBoxCursos customPaddingBottom');
$coursecount ++;
}
$content .= html_writer::end_tag('div');
if (!empty($pagingbar)) {
$content .= $pagingbar;
}
if (!empty($morelink)) {
$content .= $morelink;
}
$content .= html_writer::end_tag('div'); // End courses.
return $content;
}
/**
* Displays one course in the list of courses.
*
* This is an internal function, to display an information about just one course
* please use {@link core_course_renderer::course_info_box()}
*
* @param coursecat_helper $chelper various display options
* @param core_course_list_element|stdClass $course
* @param string $additionalclasses additional classes to add to the main <div> tag (usually
* depend on the course position in list - first/last/even/odd)
* @return string
*/
$data = (object) [ protected function coursecat_coursebox(coursecat_helper $chelper, $course, $additionalclasses = '', $displayCourseInfo = false) {
'searchurl' => (new moodle_url('/course/search.php'))->out(false), global $CFG;
'id' => $formid, if (!isset($this->strings->summary)) {
'inputid' => $inputid, $this->strings->summary = get_string('summary');
'inputsize' => $inputsize, }
'value' => $value if ($chelper->get_show_courses() <= self::COURSECAT_SHOW_COURSES_COUNT) {
]; return '';
}
if ($course instanceof stdClass) {
$course = new core_course_list_element($course);
}
if($displayCourseInfo){
$content = html_writer::start_tag('div', array('style' => 'min-width: 100% !important; max-width: 100% !important;'));
}else {
$content = html_writer::start_tag('div', array('class' => $additionalclasses));
}
return $this->render_from_template('theme_boost/course_search_form', $data); $classes = '';
if ($chelper->get_show_courses() >= self::COURSECAT_SHOW_COURSES_EXPANDED) {
$nametag = 'h5';
} else {
$classes .= ' collapsed';
$nametag = 'div';
}
// End coursebox.
$content .= html_writer::start_tag('div', array(
'class' => $classes,
'data-courseid' => $course->id,
'data-type' => self::COURSECAT_TYPE_COURSE,
));
$content .= $this->coursecat_coursebox_content($chelper, $course, $displayCourseInfo);
$content .= html_writer::end_tag('div');
// End coursebox.
$content .= html_writer::end_tag('div');
// End col-md-4.
return $content;
} }
/**
* Returns HTML to display course content (summary, course contacts and optionally category name)
*
* This method is called from coursecat_coursebox() and may be re-used in AJAX
*
* @param coursecat_helper $chelper various display options
* @param stdClass|core_course_list_element $course
* @return string
*/
protected function coursecat_coursebox_content(coursecat_helper $chelper, $course, $displayCourseInfo = false) {
if ($course instanceof stdClass) {
$course = new core_course_list_element($course);
}
// Course name.
$coursename = $chelper->get_course_formatted_name($course);
$courseurl = new moodle_url('/course/view.php', array('id' => $course->id));
$coursenamelink = html_writer::link($courseurl,
$coursename, array('class' => $course->visible ? 'propertiesTextColor' : 'dimmed propertiesTextColor'));
$content = html_writer::start_tag('a', array ('href' => $courseurl, 'class' => 'course-card-img'));
$content .= $this->get_course_summary_image($course);
$content .= html_writer::end_tag('a');
$content .= html_writer::start_tag('div', array('class' => 'card-body'));
$content .= "<div class='elegantshd textCardEdited'>". $coursenamelink ."</div>";
$content .= html_writer::end_tag('div');
// if ($course->has_course_contacts()) {
// $content .= html_writer::start_tag('div', array('class' => 'card-footer teachers'));
// $content .= html_writer::start_tag('ul');
// foreach ($course->get_course_contacts() as $userid => $coursecontact) {
// $name = $coursecontact['rolename'].': '.
// html_writer::link(new moodle_url('/user/view.php',
// array('id' => $userid, 'course' => SITEID)),
// $coursecontact['username']);
// $content .= html_writer::tag('li', $name);
// }
// $content .= html_writer::end_tag('ul'); // End teachers.
// $content .= html_writer::end_tag('div'); // End teachers.
// }
// Display course category if necessary (for example in search results).
if ($chelper->get_show_courses() == self::COURSECAT_SHOW_COURSES_EXPANDED_WITH_CAT) {
if ($cat = core_course_category::get($course->category, IGNORE_MISSING)) {
$content .= html_writer::start_tag('div', array('class' => 'coursecat text-center'));
$content .= get_string('category').': '.
html_writer::link(new moodle_url('/course/index.php', array('categoryid' => $cat->id)),
$cat->get_formatted_name(), array('class' => $cat->visible ? '' : 'dimmed'));
$content .= html_writer::end_tag('div'); // End coursecat.
}
}
// Display course summary.
if($displayCourseInfo){
$content .= html_writer::start_tag('div', array('class' => 'card-see-more text-center', 'style' => 'padding-bottom: 3px !important;'));
//$content .= html_writer::start_tag('div', array('class' => 'card-see-more text-center'));
}else{
}
if ($icons = enrol_get_course_info_icons($course)) {
$content .= html_writer::start_tag('div',
array('class' => 'btn btn-inscrever sizeBlock bottomAlign',
'onclick'=>"window.location.href='" . $courseurl . "';")
);
$content .= 'Inscreva-se';
$content .= html_writer::end_tag('div');
}else{
$content .= html_writer::start_tag('div',
array('class' => 'btn btn-inscrever sizeBlock disabledBotao bottomAlign')
);
$content .= 'Inscreva-se';
$content .= html_writer::end_tag('div');
}
if (!$displayCourseInfo) {
$content .= html_writer::start_tag('button', array('class' => 'btn btn-mais-info sizeBlock bottomAlign', 'data-toggle' => 'modal', 'data-target' => "#course-popover-{$course->id}", 'data-backdrop' => 'static'));
$content .= 'Mais informações';
$content .= html_writer::end_tag('button');
$content .= html_writer::start_tag('div', array('class' => 'modal fade', 'tab-index' => '-1', 'role' => 'dialog', 'id' => "course-popover-{$course->id}", 'aria-hidden' => 'true'));
$content .= html_writer::start_tag('div', array('class' => 'modal-dialog modal-dialog-centered maxWidthModal', 'role' => 'document'));
$content .= html_writer::start_tag('div', array('class' => 'modal-content'));
$content .= html_writer::start_tag('div', array('class' => 'modal-header'));
$content .= html_writer::start_tag('h5', array('class' => 'modal-title'));
$content .= "<div>". $coursenamelink ."</div>";
$content .= html_writer::end_tag('h5');
$content .= html_writer::end_tag('div');
$content .= html_writer::start_tag('div', array('class' => 'modal-body'));
$content .= html_writer::start_tag('div');
$content .= $chelper->get_course_formatted_summary($course,
array());
$content .= html_writer::end_tag('div');
$content .= html_writer::end_tag('div');
$content .= html_writer::start_tag('div', array('class' => 'modal-footer'));
$content .= html_writer::start_tag('button', array('class' => 'btn btnFechar', 'data-dismiss' => 'modal'));
$content .= 'Fechar';
$content .= html_writer::end_tag('button');
$content .= html_writer::end_tag('div');
$content .= html_writer::end_tag('div');
$content .= html_writer::end_tag('div');
}
$content .= html_writer::end_tag('div'); // End summary.
return $content;
}
/** <img src=""
* Returns the first course's summary issue
*
* @param stdClass $course the course object
* @return string
*/
protected function get_course_summary_image($course) {
global $CFG;
$contentimage = '';
foreach ($course->get_course_overviewfiles() as $file) {
$isimage = $file->is_valid_image();
$url = file_encode_url("$CFG->wwwroot/pluginfile.php",
'/'. $file->get_contextid(). '/'. $file->get_component(). '/'.
$file->get_filearea(). $file->get_filepath(). $file->get_filename(), !$isimage);
if ($isimage) {
$contentimage = html_writer::start_tag('img', array('src' => "$url",
'class' => 'card-img-top minheight'));
$contentimage .= html_writer::end_tag('img');
break;
}
}
if (empty($contentimage)) {
$pattern = new \core_geopattern();
$pattern->setColor($this->coursecolor($course->id));
$pattern->patternbyid($course->id);
$contentimage = html_writer::start_tag('div', array('style' => "background-image:url('{$pattern->datauri()}')",
'class' => 'card-img-top minheight'));
$contentimage .= html_writer::end_tag('div');
}
return $contentimage;
}
/**
* Generate a semi-random color based on the courseid number (so it will always return
* the same color for a course)
*
* @param int $courseid
* @return string $color, hexvalue color code.
*/
protected function coursecolor($courseid) {
// The colour palette is hardcoded for now. It would make sense to combine it with theme settings.
$basecolors = ['#81ecec', '#74b9ff', '#a29bfe', '#dfe6e9', '#00b894', '#0984e3', '#b2bec3',
'#fdcb6e', '#fd79a8', '#6c5ce7'];
$color = $basecolors[$courseid % 10];
return $color;
}
} }
// 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
// ];
// if ($format != 'navbar') {
// $helpicon = new \help_icon('coursesearch', 'core');
// $data->helpicon = $helpicon->export_for_template($this);
// }
// return $this->render_from_template('theme_ilb/course_search_form', $data);
// }
// }
//
//$content .= html_writer::start_tag('div', array('class' => 'btn btn-mais-info m-2',
// 'id' => "course-popover-{$course->id}", 'role' => 'button', 'data-trigger' => 'click',
// 'data-toggle' => 'popover', 'data-placement' => 'top',
// 'data-content' => $chelper->get_course_formatted_summary($course,
// array('noclean' => true, 'para' => false)), 'data-html' => 'true', 'tabindex' => '0'));
//$content .= 'Mais informações';
//$content .= html_writer::end_tag('div');
//
//$content .= html_writer::start_tag('button', array('class' => 'btn btn-primary', 'data-toggle' => 'modal', 'data-target' => '#{$course->id'));
//$content .= 'Mais informações';
//$content .= html_writer::end_tag('button');
//
//$content .= html_writer::start_tag('div', array('class' => 'modal fade', 'tab-index' => '-1', 'role' => 'dialog', 'data-target' => '#{$course->id', 'aria-hidden' => 'true'));
//$content .= html_writer::start_tag('div', array('class' => 'modal-dialog modal-dialog-centered', 'role' => 'document'));
//$content .= html_writer::start_tag('div', array('class' => 'modal-content'));
//$content .= html_writer::start_tag('div', array('class' => 'modal-body'));
//$content .= html_writer::end_tag('div');
//$content .= html_writer::start_tag('div', array('class' => 'modal-footer'));
//$content .= html_writer::start_tag('button', array('class' => 'btn btn-secondary', 'data-dismiss' => 'modal'));
//$content .= 'fechar';
//$content .= html_writer::end_tag('button');
//$content .= html_writer::end_tag('div');
//$content .= html_writer::end_tag('div');
//$content .= html_writer::end_tag('div');

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 '';
}
}

680
classes/output/core_renderer.php

@ -14,118 +14,22 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>. // 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 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; defined('MOODLE_INTERNAL') || die;
/** /**
* Renderers to align Moodle's HTML with that expected by Bootstrap * Renderers to align Moodle's HTML with that expected by Bootstrap
* *
* @package theme_boost * @package theme_ilb
* @copyright 2012 Bas Brands, www.basbrands.nl * @copyright 2012 Bas Brands, www.basbrands.nl
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/ */
class core_renderer extends \core_renderer { 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... * We don't like these...
* *
@ -133,584 +37,4 @@ class core_renderer extends \core_renderer {
public function edit_button(moodle_url $url) { public function edit_button(moodle_url $url) {
return ''; 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);
}
} else {
if ($onlytopleafnodes) {
$skipped = true;
continue;
}
$link = new action_link(new moodle_url('#'), $menuitem->text, null, ['disabled' => true], $menuitem->icon);
}
if ($indent) {
$link->add_class('m-l-1');
}
if (!empty($menuitem->classes)) {
$link->add_class(implode(" ", $menuitem->classes));
}
$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 '';
}
}

44
classes/output/gradereport_history_renderer.php

@ -1,44 +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 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
*/
namespace theme_boost\output;
defined('MOODLE_INTERNAL') || die();
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);
}
}

52
classes/privacy/provider.php

@ -15,32 +15,64 @@
// along with Moodle. If not, see <http://www.gnu.org/licenses/>. // 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> * @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @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(); 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> * @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @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 * Store all user preferences for the plugin.
* file to explain why this plugin stores no data.
* *
* @return string * @param int $userid The userid of the user whose data is to be exported.
*/ */
public static function get_reason() : string { public static function export_user_preferences(int $userid) {
return 'privacy:metadata'; $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);

165
config.php

@ -1,54 +1,39 @@
<?php <?php
// This file is part of Moodle - http://moodle.org/
// Every file should have GPL and copyright in the header - we skip it in tutorials but you should not skip it for real. //
// Moodle is free software: you can redistribute it and/or modify
// This line protects the file from being accessed by a URL directly. // 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
*/
defined('MOODLE_INTERNAL') || die(); defined('MOODLE_INTERNAL') || die();
// $THEME is defined before this page is included and we can define settings by adding properties to this global object.
// 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;
require_once(__DIR__ . '/lib.php');
$THEME->name = 'ilb';
$THEME->sheets = [];
$THEME->editor_sheets = [];
$THEME->editor_scss = ['editor'];
$THEME->usefallback = true; $THEME->usefallback = true;
$THEME->iconsystem = \core\output\icon_system::FONTAWESOME; $THEME->scss = function($theme) {
return theme_ilb_get_main_scss_content($theme);
};
$THEME->layouts = [ $THEME->layouts = [
// Most backwards compatible layout without the blocks - this is the layout used by default. // Most backwards compatible layout without the blocks - this is the layout used by default.
@ -67,7 +52,7 @@ $THEME->layouts = [
'file' => 'columns2.php', 'file' => 'columns2.php',
'regions' => array('side-pre'), 'regions' => array('side-pre'),
'defaultregion' => 'side-pre', 'defaultregion' => 'side-pre',
'options' => array('langmenu' => true), 'options' => array('langmenu' => true, 'courseimg' => true),
), ),
'coursecategory' => array( 'coursecategory' => array(
'file' => 'columns2.php', 'file' => 'columns2.php',
@ -87,18 +72,88 @@ $THEME->layouts = [
'defaultregion' => 'side-pre', 'defaultregion' => 'side-pre',
'options' => array('nonavbar' => true), 'options' => array('nonavbar' => true),
), ),
// Server administration scripts.
'admin' => array(
'file' => 'columns2.php',
'regions' => array('side-pre'),
'defaultregion' => 'side-pre',
),
// My dashboard page. // My dashboard page.
'mydashboard' => array( 'mydashboard' => array(
'file' => 'columns2.php', 'file' => 'columns2.php',
'regions' => array('side-pre'), 'regions' => array('side-pre'),
'defaultregion' => '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()
),
// 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'
) )
]; ];
$THEME->parents = [];
// This is the function that returns the SCSS source for the main file in our theme. We override the boost version because $THEME->enable_dock = false;
// we want to allow presets uploaded to our own theme file area to be selected in the preset list. $THEME->csstreepostprocessor = 'theme_ilb_css_tree_post_processor';
$THEME->scss = function($theme) { $THEME->extrascsscallback = 'theme_ilb_get_extra_scss';
return theme_ilb_get_main_scss_content($theme); $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;

64
lang/en/theme_ilb.php

@ -1,12 +1,54 @@
<?php <?php
// Every file should have GPL and copyright in the header - we skip it in tutorials but you should not skip it for real. // This file is part of Moodle - http://moodle.org/
//
// This line protects the file from being accessed by a URL directly. // Moodle is free software: you can redistribute it and/or modify
defined('MOODLE_INTERNAL') || die(); // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// A description shown in the admin theme selector. // (at your option) any later version.
$string['choosereadme'] = 'Tema do ILB para o Moodle'; //
// The name of our plugin. // Moodle is distributed in the hope that it will be useful,
$string['pluginname'] = 'ILB'; // but WITHOUT ANY WARRANTY; without even the implied warranty of
// We need to include a lang string for each block region. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
$string['region-side-pre'] = 'Right'; // 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/>.
/**
* Language file.
*
* @package theme_ilb
* @copyright 2016 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$string['advancedsettings'] = 'Advanced settings';
$string['backgroundimage'] = 'Background image';
$string['backgroundimage_desc'] = 'The image to display as a background of the site. The background image you upload here will override the background image in your theme preset files.';
$string['brandcolor'] = 'Brand colour';
$string['brandcolor_desc'] = 'The accent colour.';
$string['bootswatch'] = 'Bootswatch';
$string['bootswatch_desc'] = 'A bootswatch is a set of Bootstrap variables and css to style Bootstrap';
$string['choosereadme'] = 'Ilb is a modern highly-customisable theme. This theme is intended to be used directly, or as a parent theme when creating new themes utilising Bootstrap 4.';
$string['currentinparentheses'] = '(current)';
$string['configtitle'] = 'Ilb';
$string['fontsize'] = 'Theme base fontsize';
$string['fontsize_desc'] = 'Enter a fontsize in %';
$string['generalsettings'] = 'General settings';
$string['nobootswatch'] = 'None';
$string['pluginname'] = 'Ilb';
$string['presetfiles'] = 'Additional theme preset files';
$string['presetfiles_desc'] = 'Preset files can be used to dramatically alter the appearance of the theme. See <a href="https://docs.moodle.org/dev/Ilb_Presets">Ilb presets</a> for information on creating and sharing your own preset files, and see the <a href="http://moodle.net/ilb">Presets repository</a> for presets that others have shared.';
$string['preset'] = 'Theme preset';
$string['preset_desc'] = 'Pick a preset to broadly change the look of the theme.';
$string['privacy:metadata'] = 'The Ilb theme does not store any personal data about any user.';
$string['rawscss'] = 'Raw SCSS';
$string['rawscss_desc'] = 'Use this field to provide SCSS or CSS code which will be injected at the end of the style sheet.';
$string['rawscsspre'] = 'Raw initial SCSS';
$string['rawscsspre_desc'] = 'In this field you can provide initialising SCSS code, it will be injected before everything else. Most of the time you will use this setting to define variables.';
$string['region-side-pre'] = 'Right';
$string['privacy:metadata:preference:draweropennav'] = 'The user\'s preference for hiding or showing the drawer menu navigation.';
$string['privacy:drawernavclosed'] = 'The current preference for the navigation drawer is closed.';
$string['privacy:drawernavopen'] = 'The current preference for the navigation drawer is open.';

5
layout/columns1.php

@ -24,15 +24,12 @@
defined('MOODLE_INTERNAL') || die(); defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir . '/../config.php');
$bodyattributes = $OUTPUT->body_attributes([]); $bodyattributes = $OUTPUT->body_attributes([]);
$templatecontext = [ $templatecontext = [
'sitename' => format_string($SITE->shortname, true, ['context' => context_course::instance(SITEID), "escape" => false]), 'sitename' => format_string($SITE->shortname, true, ['context' => context_course::instance(SITEID), "escape" => false]),
'output' => $OUTPUT, 'output' => $OUTPUT,
'bodyattributes' => $bodyattributes, 'bodyattributes' => $bodyattributes
'moodle_url' => $CFG->wwwroot
]; ];
echo $OUTPUT->render_from_template('theme_ilb/columns1', $templatecontext); echo $OUTPUT->render_from_template('theme_ilb/columns1', $templatecontext);

8
layout/columns2.php

@ -26,7 +26,6 @@ defined('MOODLE_INTERNAL') || die();
user_preference_allow_ajax_update('drawer-open-nav', PARAM_ALPHA); user_preference_allow_ajax_update('drawer-open-nav', PARAM_ALPHA);
require_once($CFG->libdir . '/behat/lib.php'); require_once($CFG->libdir . '/behat/lib.php');
require_once($CFG->libdir . '/../config.php');
if (isloggedin()) { if (isloggedin()) {
$navdraweropen = (get_user_preferences('drawer-open-nav', 'true') == 'true'); $navdraweropen = (get_user_preferences('drawer-open-nav', 'true') == 'true');
@ -38,9 +37,13 @@ if ($navdraweropen) {
$extraclasses[] = 'drawer-open-left'; $extraclasses[] = 'drawer-open-left';
} }
$bodyattributes = $OUTPUT->body_attributes($extraclasses); $bodyattributes = $OUTPUT->body_attributes($extraclasses);
$blockshtml = $OUTPUT->blocks('side-pre'); $blockshtml = $OUTPUT->blocks('side-pre');
$hasblocks = strpos($blockshtml, 'data-block=') !== false; $hasblocks = strpos($blockshtml, 'data-block=') !== false;
$regionmainsettingsmenu = $OUTPUT->region_main_settings_menu(); $regionmainsettingsmenu = $OUTPUT->region_main_settings_menu();
$courseimg = 'http://localhost/saberes/pluginfile.php/1562711/course/overviewfiles/Cerimonial%20no%20Ambiente%20Legislativo.png';
$templatecontext = [ $templatecontext = [
'sitename' => format_string($SITE->shortname, true, ['context' => context_course::instance(SITEID), "escape" => false]), 'sitename' => format_string($SITE->shortname, true, ['context' => context_course::instance(SITEID), "escape" => false]),
'output' => $OUTPUT, 'output' => $OUTPUT,
@ -50,8 +53,9 @@ $templatecontext = [
'navdraweropen' => $navdraweropen, 'navdraweropen' => $navdraweropen,
'regionmainsettingsmenu' => $regionmainsettingsmenu, 'regionmainsettingsmenu' => $regionmainsettingsmenu,
'hasregionmainsettingsmenu' => !empty($regionmainsettingsmenu), 'hasregionmainsettingsmenu' => !empty($regionmainsettingsmenu),
'moodle_url' => $CFG->wwwroot 'courseimg' => $courseimg
]; ];
$templatecontext['flatnavigation'] = $PAGE->flatnav; $templatecontext['flatnavigation'] = $PAGE->flatnav;
echo $OUTPUT->render_from_template('theme_ilb/columns2', $templatecontext); echo $OUTPUT->render_from_template('theme_ilb/columns2', $templatecontext);

15
layout/frontpage_ilb.php

@ -28,6 +28,12 @@ require_once($CFG->libdir . '/../config.php');
$bodyattributes = $OUTPUT->body_attributes([]); $bodyattributes = $OUTPUT->body_attributes([]);
$habilitar_destaque = $this->page->theme->settings->habilitar_destaque;
$curso_destaque = $this->page->theme->settings->curso_destaque;
$imagem_destaque = $this->page->theme->settings->imagem_destaque;
if (isloggedin()) { if (isloggedin()) {
user_preference_allow_ajax_update('drawer-open-nav', PARAM_ALPHA); user_preference_allow_ajax_update('drawer-open-nav', PARAM_ALPHA);
require_once($CFG->libdir . '/behat/lib.php'); require_once($CFG->libdir . '/behat/lib.php');
@ -78,6 +84,9 @@ if (isloggedin()) {
'moodle_url' => $CFG->wwwroot, 'moodle_url' => $CFG->wwwroot,
'userpictureurl' => $user_picture_url, 'userpictureurl' => $user_picture_url,
'userprofileurl' => $user_profile_url, 'userprofileurl' => $user_profile_url,
'habilitar_destaque' => $habilitar_destaque,
'imagem_destaque' => $imagem_destaque,
'curso_destaque' => $curso_destaque
]; ];
@ -99,7 +108,11 @@ if (isloggedin()) {
'logo_saberes_xl' => $OUTPUT->image_url('logo_saberes_xl', 'theme'), 'logo_saberes_xl' => $OUTPUT->image_url('logo_saberes_xl', 'theme'),
'output' => $OUTPUT, 'output' => $OUTPUT,
'bodyattributes' => $bodyattributes, 'bodyattributes' => $bodyattributes,
'moodle_url' => $CFG->wwwroot 'moodle_url' => $CFG->wwwroot,
'logintoken' => s(\core\session\manager::get_login_token()),
'habilitar_destaque' => $habilitar_destaque,
'imagem_destaque' => $imagem_destaque,
'curso_destaque' => $curso_destaque
]; ];
} }

5
layout/login.php

@ -16,8 +16,6 @@
defined('MOODLE_INTERNAL') || die(); defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir . '/../config.php');
/** /**
* A login page layout for the ilb theme. * A login page layout for the ilb theme.
* *
@ -31,8 +29,7 @@ $bodyattributes = $OUTPUT->body_attributes();
$templatecontext = [ $templatecontext = [
'sitename' => format_string($SITE->shortname, true, ['context' => context_course::instance(SITEID), "escape" => false]), 'sitename' => format_string($SITE->shortname, true, ['context' => context_course::instance(SITEID), "escape" => false]),
'output' => $OUTPUT, 'output' => $OUTPUT,
'bodyattributes' => $bodyattributes, 'bodyattributes' => $bodyattributes
'moodle_url' => $CFG->wwwroot
]; ];
echo $OUTPUT->render_from_template('theme_ilb/login', $templatecontext); echo $OUTPUT->render_from_template('theme_ilb/login', $templatecontext);

36
layout/login_nova_conta.php

@ -1,36 +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/>.
defined('MOODLE_INTERNAL') || die();
/**
* A login page layout for the ilb theme.
*
* @package theme_ilb
* @copyright 2016 Damyon Wiese
* @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);

188
lib.php

@ -1,34 +1,158 @@
<?php <?php
// This file is part of Moodle - http://moodle.org/
// Every file should have GPL and copyright in the header - we skip it in tutorials but you should not skip it for real. //
// Moodle is free software: you can redistribute it and/or modify
// This line protects the file from being accessed by a URL directly. // 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/>.
/**
* Theme functions.
*
* @package theme_ilb
* @copyright 2016 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die(); defined('MOODLE_INTERNAL') || die();
// We will add callbacks here as we add features to our theme. /**
* Post process the CSS tree.
function theme_ilb_get_main_scss_content($theme) { *
global $CFG; * @param string $tree The CSS tree.
* @param theme_config $theme The theme config object.
$scss = ''; */
$filename = !empty($theme->settings->preset) ? $theme->settings->preset : null; function theme_ilb_css_tree_post_processor($tree, $theme) {
$fs = get_file_storage(); $prefixer = new theme_ilb\autoprefixer($tree);
$prefixer->prefix();
$context = context_system::instance(); }
if ($filename == 'default.scss') {
// We still load the default preset files directly from the boost theme. No sense in duplicating them. /**
$scss .= file_get_contents($CFG->dirroot . '/theme/boost/scss/preset/default.scss'); * Inject additional SCSS.
} else if ($filename == 'plain.scss') { *
// We still load the default preset files directly from the boost theme. No sense in duplicating them. * @param theme_config $theme The theme config object.
$scss .= file_get_contents($CFG->dirroot . '/theme/boost/scss/preset/plain.scss'); * @return string
*/
} else if ($filename && ($presetfile = $fs->get_file($context->id, 'theme_ilb', 'preset', 0, '/', $filename))) { function theme_ilb_get_extra_scss($theme) {
// This preset file was fetched from the file area for theme_ilb and not theme_boost (see the line above). $content = '';
$scss .= $presetfile->get_content(); $imageurl = $theme->setting_file_url('backgroundimage', 'backgroundimage');
} else {
// Safety fallback - maybe new installs etc. // Sets the background image, and its settings.
$scss .= file_get_contents($CFG->dirroot . '/theme/boost/scss/preset/default.scss'); if (!empty($imageurl)) {
} $content .= 'body { ';
$content .= "background-image: url('$imageurl'); background-size: cover;";
return $scss; $content .= ' }';
} }
// Always return the background image with the scss when we have it.
return !empty($theme->settings->scss) ? $theme->settings->scss . ' ' . $content : $content;
}
/**
* Serves any files associated with the theme settings.
*
* @param stdClass $course
* @param stdClass $cm
* @param context $context
* @param string $filearea
* @param array $args
* @param bool $forcedownload
* @param array $options
* @return bool
*/
function theme_ilb_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload, array $options = array()) {
if ($context->contextlevel == CONTEXT_SYSTEM && ($filearea === 'logo' || $filearea === 'backgroundimage')) {
$theme = theme_config::load('ilb');
// By default, theme files must be cache-able by both browsers and proxies.
if (!array_key_exists('cacheability', $options)) {
$options['cacheability'] = 'public';
}
return $theme->setting_file_serve($filearea, $args, $forcedownload, $options);
} else {
send_file_not_found();
}
}
/**
* Returns the main SCSS content.
*
* @param theme_config $theme The theme config object.
* @return string
*/
function theme_ilb_get_main_scss_content($theme) {
global $CFG;
$scss = '';
$filename = !empty($theme->settings->preset) ? $theme->settings->preset : null;
$fs = get_file_storage();
$context = context_system::instance();
if ($filename == 'default.scss') {
$scss .= file_get_contents($CFG->dirroot . '/theme/ilb/scss/preset/default.scss');
} else if ($filename == 'plain.scss') {
$scss .= file_get_contents($CFG->dirroot . '/theme/ilb/scss/preset/plain.scss');
} else if ($filename && ($presetfile = $fs->get_file($context->id, 'theme_ilb', 'preset', 0, '/', $filename))) {
$scss .= $presetfile->get_content();
} else {
// Safety fallback - maybe new installs etc.
$scss .= file_get_contents($CFG->dirroot . '/theme/ilb/scss/preset/default.scss');
}
return $scss;
}
/**
* Get compiled css.
*
* @return string compiled css
*/
function theme_ilb_get_precompiled_css() {
global $CFG;
return file_get_contents($CFG->dirroot . '/theme/ilb/style/moodle.css');
}
/**
* Get SCSS to prepend.
*
* @param theme_config $theme The theme config object.
* @return array
*/
function theme_ilb_get_pre_scss($theme) {
global $CFG;
$scss = '';
$configurable = [
// Config key => [variableName, ...].
'brandcolor' => ['primary'],
];
// Prepend variables first.
foreach ($configurable as $configkey => $targets) {
$value = isset($theme->settings->{$configkey}) ? $theme->settings->{$configkey} : null;
if (empty($value)) {
continue;
}
array_map(function($target) use (&$scss, $value) {
$scss .= '$' . $target . ': ' . $value . ";\n";
}, (array) $targets);
}
// Prepend pre-scss.
if (!empty($theme->settings->scsspre)) {
$scss .= $theme->settings->scsspre;
}
if (!empty($theme->settings->fontsize)) {
$scss .= '$font-size-base: ' . (1 / 100 * $theme->settings->fontsize) . "rem !default;\n";
}
return $scss;
}

BIN
pix/engitec.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

2
pix/fp/th.svg

@ -1 +1 @@
<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMid meet"><path d="M512 1248v192q0 40-28 68t-68 28h-320q-40 0-68-28t-28-68v-192q0-40 28-68t68-28h320q40 0 68 28t28 68zm0-512v192q0 40-28 68t-68 28h-320q-40 0-68-28t-28-68v-192q0-40 28-68t68-28h320q40 0 68 28t28 68zm640 512v192q0 40-28 68t-68 28h-320q-40 0-68-28t-28-68v-192q0-40 28-68t68-28h320q40 0 68 28t28 68zm-640-1024v192q0 40-28 68t-68 28h-320q-40 0-68-28t-28-68v-192q0-40 28-68t68-28h320q40 0 68 28t28 68zm640 512v192q0 40-28 68t-68 28h-320q-40 0-68-28t-28-68v-192q0-40 28-68t68-28h320q40 0 68 28t28 68zm640 512v192q0 40-28 68t-68 28h-320q-40 0-68-28t-28-68v-192q0-40 28-68t68-28h320q40 0 68 28t28 68zm-640-1024v192q0 40-28 68t-68 28h-320q-40 0-68-28t-28-68v-192q0-40 28-68t68-28h320q40 0 68 28t28 68zm640 512v192q0 40-28 68t-68 28h-320q-40 0-68-28t-28-68v-192q0-40 28-68t68-28h320q40 0 68 28t28 68zm0-512v192q0 40-28 68t-68 28h-320q-40 0-68-28t-28-68v-192q0-40 28-68t68-28h320q40 0 68 28t28 68z"/></svg> /var/www/html/moodle/theme/ilb/pix<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMid meet"><path d="M512 1248v192q0 40-28 68t-68 28h-320q-40 0-68-28t-28-68v-192q0-40 28-68t68-28h320q40 0 68 28t28 68zm0-512v192q0 40-28 68t-68 28h-320q-40 0-68-28t-28-68v-192q0-40 28-68t68-28h320q40 0 68 28t28 68zm640 512v192q0 40-28 68t-68 28h-320q-40 0-68-28t-28-68v-192q0-40 28-68t68-28h320q40 0 68 28t28 68zm-640-1024v192q0 40-28 68t-68 28h-320q-40 0-68-28t-28-68v-192q0-40 28-68t68-28h320q40 0 68 28t28 68zm640 512v192q0 40-28 68t-68 28h-320q-40 0-68-28t-28-68v-192q0-40 28-68t68-28h320q40 0 68 28t28 68zm640 512v192q0 40-28 68t-68 28h-320q-40 0-68-28t-28-68v-192q0-40 28-68t68-28h320q40 0 68 28t28 68zm-640-1024v192q0 40-28 68t-68 28h-320q-40 0-68-28t-28-68v-192q0-40 28-68t68-28h320q40 0 68 28t28 68zm640 512v192q0 40-28 68t-68 28h-320q-40 0-68-28t-28-68v-192q0-40 28-68t68-28h320q40 0 68 28t28 68zm0-512v192q0 40-28 68t-68 28h-320q-40 0-68-28t-28-68v-192q0-40 28-68t68-28h320q40 0 68 28t28 68z"/></svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

BIN
pix/screenshot.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

35
readme_moodle.txt

@ -0,0 +1,35 @@
Description of Twitter bootstrap import into Moodle
Twitter bootstrap
-----------------
Sass:
This theme uses the version 4.0.0 Twitter bootstrap sass files.
The bootstrap repository is available on:
https://github.com/twitter/bootstrap.git
To update to the latest release of twitter bootstrap:
* remove all files from scss/bootstrap,
* download the new scss files and store them in scss/bootstrap
* re-apply /* rtl:begin:ignore */ on the top of _popover.scss before .popover rule and /* rtl:end:ignore */ before
.popover-arrow::after rule. See MDL-56763 commit (1a4faf9b).
* comment out all uses of the @supports syntax in SCSS (see https://github.com/sabberworm/PHP-CSS-Parser/issues/127). In Bootstrap 4.0 The @supports rules are used for carousal transitions (nice sliding) and the .sticky-top helper class. The carousel bootstrap component will still be functional.
* update ./thirdpartylibs.xml
Javascript:
This theme uses the transpiled javascript from bootstrap4 as amd modules.
To update the javascript files:
Checkout the latest branch of bootstrap to a folder, Run the follwing inside the cloned Bootstrap repository:
```
$ npm install @babel/cli@7.0.0-beta.37 @babel/preset-env@7.0.0-beta.37 babel-plugin-transform-es2015-modules-amd @babel/plugin-proposal-object-rest-spread
$ mkdir out
$ ./node_modules/@babel/cli/bin/babel.js --presets @babel/preset-env --plugins transform-es2015-modules-amd,@babel/plugin-proposal-object-rest-spread -d ./out/ js/src/
```
Copy the transpiled files from out/ into the amd/src/ folder for the theme.
Run grunt to re-compile the JS files. (thanks to Joby Harding)

2
scss/bootstrap.scss

@ -0,0 +1,2 @@
// Import Bootstrap.
@import "bootstrap/bootstrap";

4
scss/bootstrap/LICENSE

@ -1,7 +1,7 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2011-2016 Twitter, Inc. Copyright (c) 2011-2018 Twitter, Inc.
Copyright (c) 2011-2016 The Bootstrap Authors Copyright (c) 2011-2018 The Bootstrap Authors
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

29
scss/bootstrap/_alert.scss

@ -3,8 +3,9 @@
// //
.alert { .alert {
padding: $alert-padding; position: relative;
margin-bottom: $spacer-y; padding: $alert-padding-y $alert-padding-x;
margin-bottom: $alert-margin-bottom;
border: $alert-border-width solid transparent; border: $alert-border-width solid transparent;
@include border-radius($alert-border-radius); @include border-radius($alert-border-radius);
} }
@ -26,13 +27,14 @@
// Expand the right padding and account for the close button's positioning. // Expand the right padding and account for the close button's positioning.
.alert-dismissible { .alert-dismissible {
padding-right: ($alert-padding * 2); padding-right: ($close-font-size + $alert-padding-x * 2);
// Adjust close link position // Adjust close link position
.close { .close {
position: relative; position: absolute;
top: -.125rem; top: 0;
right: -$alert-padding; right: 0;
padding: $alert-padding-y $alert-padding-x;
color: inherit; color: inherit;
} }
} }
@ -42,15 +44,8 @@
// //
// Generate contextual modifier classes for colorizing the alert. // Generate contextual modifier classes for colorizing the alert.
.alert-success { @each $color, $value in $theme-colors {
@include alert-variant($alert-success-bg, $alert-success-border, $alert-success-text); .alert-#{$color} {
} @include alert-variant(theme-color-level($color, $alert-bg-level), theme-color-level($color, $alert-border-level), theme-color-level($color, $alert-color-level));
.alert-info { }
@include alert-variant($alert-info-bg, $alert-info-border, $alert-info-text);
}
.alert-warning {
@include alert-variant($alert-warning-bg, $alert-warning-border, $alert-warning-text);
}
.alert-danger {
@include alert-variant($alert-danger-bg, $alert-danger-border, $alert-danger-text);
} }

47
scss/bootstrap/_badge.scss

@ -0,0 +1,47 @@
// Base class
//
// Requires one of the contextual, color modifier classes for `color` and
// `background-color`.
.badge {
display: inline-block;
padding: $badge-padding-y $badge-padding-x;
font-size: $badge-font-size;
font-weight: $badge-font-weight;
line-height: 1;
text-align: center;
white-space: nowrap;
vertical-align: baseline;
@include border-radius($badge-border-radius);
// Empty badges collapse automatically
&:empty {
display: none;
}
}
// Quick fix for badges in buttons
.btn .badge {
position: relative;
top: -1px;
}
// Pill badges
//
// Make them extra rounded with a modifier to replace v3's badges.
.badge-pill {
padding-right: $badge-pill-padding-x;
padding-left: $badge-pill-padding-x;
@include border-radius($badge-pill-border-radius);
}
// Colors
//
// Contextual variations (linked badges get darker on :hover).
@each $color, $value in $theme-colors {
.badge-#{$color} {
@include badge-variant($value);
}
}

8
scss/bootstrap/_breadcrumb.scss

@ -1,15 +1,14 @@
.breadcrumb { .breadcrumb {
display: flex;
flex-wrap: wrap;
padding: $breadcrumb-padding-y $breadcrumb-padding-x; padding: $breadcrumb-padding-y $breadcrumb-padding-x;
margin-bottom: $spacer-y; margin-bottom: $breadcrumb-margin-bottom;
list-style: none; list-style: none;
background-color: $breadcrumb-bg; background-color: $breadcrumb-bg;
@include border-radius($border-radius); @include border-radius($border-radius);
@include clearfix;
} }
.breadcrumb-item { .breadcrumb-item {
float: left;
// The separator between breadcrumbs (by default, a forward-slash: "/") // The separator between breadcrumbs (by default, a forward-slash: "/")
+ .breadcrumb-item::before { + .breadcrumb-item::before {
display: inline-block; // Suppress underlining of the separator in modern browsers display: inline-block; // Suppress underlining of the separator in modern browsers
@ -28,6 +27,7 @@
+ .breadcrumb-item:hover::before { + .breadcrumb-item:hover::before {
text-decoration: underline; text-decoration: underline;
} }
// stylelint-disable-next-line no-duplicate-selectors
+ .breadcrumb-item:hover::before { + .breadcrumb-item:hover::before {
text-decoration: none; text-decoration: none;
} }

158
scss/bootstrap/_button-group.scss

@ -1,97 +1,65 @@
// scss-lint:disable QualifyingElement // stylelint-disable selector-no-qualifying-type
// Make the div behave like a button // Make the div behave like a button
.btn-group, .btn-group,
.btn-group-vertical { .btn-group-vertical {
position: relative; position: relative;
display: inline-block; display: inline-flex;
vertical-align: middle; // match .btn alignment given font-size hack above vertical-align: middle; // match .btn alignment given font-size hack above
> .btn { > .btn {
position: relative; position: relative;
float: left; flex: 0 1 auto;
// Bring the "active" button to the front // Bring the hover, focused, and "active" buttons to the front to overlay
// the borders properly
@include hover {
z-index: 1;
}
&:focus, &:focus,
&:active, &:active,
&.active { &.active {
z-index: 2; z-index: 1;
}
@include hover {
z-index: 2;
} }
} }
}
// Prevent double borders when buttons are next to each other // Prevent double borders when buttons are next to each other
.btn-group {
.btn + .btn, .btn + .btn,
.btn + .btn-group, .btn + .btn-group,
.btn-group + .btn, .btn-group + .btn,
.btn-group + .btn-group { .btn-group + .btn-group {
margin-left: -$input-btn-border-width; margin-left: -$btn-border-width;
} }
} }
// Optional: Group multiple button groups together for a toolbar // Optional: Group multiple button groups together for a toolbar
.btn-toolbar { .btn-toolbar {
margin-left: -$btn-toolbar-margin; // Offset the first child's margin display: flex;
@include clearfix(); flex-wrap: wrap;
justify-content: flex-start;
.btn-group,
.input-group { .input-group {
float: left; width: auto;
}
> .btn,
> .btn-group,
> .input-group {
margin-left: $btn-toolbar-margin;
} }
} }
.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { .btn-group {
border-radius: 0; > .btn:first-child {
} margin-left: 0;
// Set corners individual because sometimes a single button can be in a .btn-group and we need :first-child and :last-child to both match
.btn-group > .btn:first-child {
margin-left: 0;
&:not(:last-child):not(.dropdown-toggle) {
@include border-right-radius(0);
} }
}
// Need .dropdown-toggle since :last-child doesn't apply given a .dropdown-menu immediately after it
.btn-group > .btn:last-child:not(:first-child),
.btn-group > .dropdown-toggle:not(:first-child) {
@include border-left-radius(0);
}
// Custom edits for including btn-groups within btn-groups (useful for including dropdown buttons within a btn-group) // Reset rounded corners
.btn-group > .btn-group { > .btn:not(:last-child):not(.dropdown-toggle),
float: left; > .btn-group:not(:last-child) > .btn {
}
.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn {
border-radius: 0;
}
.btn-group > .btn-group:first-child:not(:last-child) {
> .btn:last-child,
> .dropdown-toggle {
@include border-right-radius(0); @include border-right-radius(0);
} }
}
.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child {
@include border-left-radius(0);
}
// On active and open, don't show outline > .btn:not(:first-child),
.btn-group .dropdown-toggle:active, > .btn-group:not(:first-child) > .btn {
.btn-group.open .dropdown-toggle { @include border-left-radius(0);
outline: 0; }
} }
// Sizing // Sizing
// //
// Remix the default button sizing classes into new ones for easier manipulation. // Remix the default button sizing classes into new ones for easier manipulation.
@ -104,7 +72,7 @@
// Split button dropdowns // Split button dropdowns
// //
.btn + .dropdown-toggle-split { .dropdown-toggle-split {
padding-right: $btn-padding-x * .75; padding-right: $btn-padding-x * .75;
padding-left: $btn-padding-x * .75; padding-left: $btn-padding-x * .75;
@ -125,8 +93,8 @@
// The clickable button for toggling the menu // The clickable button for toggling the menu
// Remove the gradient and set the same inset shadow as the :active state // Set the same inset shadow as the :active state
.btn-group.open .dropdown-toggle { .btn-group.show .dropdown-toggle {
@include box-shadow($btn-active-box-shadow); @include box-shadow($btn-active-box-shadow);
// Show no shadow for `.btn-link` since it has no other button styles. // Show no shadow for `.btn-link` since it has no other button styles.
@ -136,77 +104,39 @@
} }
// Reposition the caret
.btn .caret {
margin-left: 0;
}
// Carets in other button sizes
.btn-lg .caret {
border-width: $caret-width-lg $caret-width-lg 0;
border-bottom-width: 0;
}
// Upside down carets for .dropup
.dropup .btn-lg .caret {
border-width: 0 $caret-width-lg $caret-width-lg;
}
// //
// Vertical button groups // Vertical button groups
// //
.btn-group-vertical { .btn-group-vertical {
> .btn, flex-direction: column;
> .btn-group, align-items: flex-start;
> .btn-group > .btn { justify-content: center;
display: block;
float: none;
width: 100%;
max-width: 100%;
}
// Clear floats so dropdown menus can be properly placed
> .btn-group {
@include clearfix();
> .btn { .btn,
float: none; .btn-group {
} width: 100%;
} }
> .btn + .btn, > .btn + .btn,
> .btn + .btn-group, > .btn + .btn-group,
> .btn-group + .btn, > .btn-group + .btn,
> .btn-group + .btn-group { > .btn-group + .btn-group {
margin-top: -$input-btn-border-width; margin-top: -$btn-border-width;
margin-left: 0; margin-left: 0;
} }
}
.btn-group-vertical > .btn { // Reset rounded corners
&:not(:first-child):not(:last-child) { > .btn:not(:last-child):not(.dropdown-toggle),
border-radius: 0; > .btn-group:not(:last-child) > .btn {
}
&:first-child:not(:last-child) {
@include border-bottom-radius(0); @include border-bottom-radius(0);
} }
&:last-child:not(:first-child) {
> .btn:not(:first-child),
> .btn-group:not(:first-child) > .btn {
@include border-top-radius(0); @include border-top-radius(0);
} }
} }
.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn {
border-radius: 0;
}
.btn-group-vertical > .btn-group:first-child:not(:last-child) {
> .btn:last-child,
> .dropdown-toggle {
@include border-bottom-radius(0);
}
}
.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child {
@include border-top-radius(0);
}
// Checkbox and radio options // Checkbox and radio options
@ -221,13 +151,15 @@
// See https://github.com/twbs/bootstrap/pull/12794 and // See https://github.com/twbs/bootstrap/pull/12794 and
// https://github.com/twbs/bootstrap/pull/14559 for more information. // https://github.com/twbs/bootstrap/pull/14559 for more information.
[data-toggle="buttons"] { .btn-group-toggle {
> .btn, > .btn,
> .btn-group > .btn { > .btn-group > .btn {
margin-bottom: 0; // Override default `<label>` value
input[type="radio"], input[type="radio"],
input[type="checkbox"] { input[type="checkbox"] {
position: absolute; position: absolute;
clip: rect(0,0,0,0); clip: rect(0, 0, 0, 0);
pointer-events: none; pointer-events: none;
} }
} }

147
scss/bootstrap/_buttons.scss

@ -1,4 +1,4 @@
// scss-lint:disable QualifyingElement // stylelint-disable selector-no-qualifying-type
// //
// Base styles // Base styles
@ -7,50 +7,51 @@
.btn { .btn {
display: inline-block; display: inline-block;
font-weight: $btn-font-weight; font-weight: $btn-font-weight;
line-height: $btn-line-height;
text-align: center; text-align: center;
white-space: nowrap; white-space: nowrap;
vertical-align: middle; vertical-align: middle;
cursor: pointer;
user-select: none; user-select: none;
border: $input-btn-border-width solid transparent; border: $btn-border-width solid transparent;
@include button-size($btn-padding-y, $btn-padding-x, $font-size-base, $btn-border-radius); @include button-size($btn-padding-y, $btn-padding-x, $font-size-base, $btn-line-height, $btn-border-radius);
@include transition(all .2s ease-in-out); @include transition($btn-transition);
&,
&:active,
&.active {
&:focus,
&.focus {
@include tab-focus();
}
}
// Share hover and focus styles
@include hover-focus { @include hover-focus {
text-decoration: none; text-decoration: none;
} }
&.focus {
text-decoration: none;
}
&:active, &:focus,
&.active { &.focus {
background-image: none;
outline: 0; outline: 0;
@include box-shadow($btn-active-box-shadow); box-shadow: $btn-focus-box-shadow;
} }
// Disabled comes first so active can properly restyle
&.disabled, &.disabled,
&:disabled { &:disabled {
cursor: $cursor-disabled; opacity: $btn-disabled-opacity;
opacity: .65;
@include box-shadow(none); @include box-shadow(none);
} }
// Opinionated: add "hand" cursor to non-disabled .btn elements
&:not(:disabled):not(.disabled) {
cursor: pointer;
}
&:not(:disabled):not(.disabled):active,
&:not(:disabled):not(.disabled).active {
background-image: none;
@include box-shadow($btn-active-box-shadow);
&:focus {
@include box-shadow($btn-focus-box-shadow, $btn-active-box-shadow);
}
}
} }
// Future-proof disabling of clicks on `<a>` elements // Future-proof disabling of clicks on `<a>` elements
a.btn.disabled, a.btn.disabled,
fieldset[disabled] a.btn { fieldset:disabled a.btn {
pointer-events: none; pointer-events: none;
} }
@ -59,43 +60,16 @@ fieldset[disabled] a.btn {
// Alternate buttons // Alternate buttons
// //
.btn-primary { @each $color, $value in $theme-colors {
@include button-variant($btn-primary-color, $btn-primary-bg, $btn-primary-border); .btn-#{$color} {
} @include button-variant($value, $value);
.btn-secondary { }
@include button-variant($btn-secondary-color, $btn-secondary-bg, $btn-secondary-border);
}
.btn-info {
@include button-variant($btn-info-color, $btn-info-bg, $btn-info-border);
}
.btn-success {
@include button-variant($btn-success-color, $btn-success-bg, $btn-success-border);
}
.btn-warning {
@include button-variant($btn-warning-color, $btn-warning-bg, $btn-warning-border);
}
.btn-danger {
@include button-variant($btn-danger-color, $btn-danger-bg, $btn-danger-border);
} }
// Remove all backgrounds @each $color, $value in $theme-colors {
.btn-outline-primary { .btn-outline-#{$color} {
@include button-outline-variant($btn-primary-bg); @include button-outline-variant($value);
} }
.btn-outline-secondary {
@include button-outline-variant($btn-secondary-border);
}
.btn-outline-info {
@include button-outline-variant($btn-info-bg);
}
.btn-outline-success {
@include button-outline-variant($btn-success-bg);
}
.btn-outline-warning {
@include button-outline-variant($btn-warning-bg);
}
.btn-outline-danger {
@include button-outline-variant($btn-danger-bg);
} }
@ -105,36 +79,30 @@ fieldset[disabled] a.btn {
// Make a button look and behave like a link // Make a button look and behave like a link
.btn-link { .btn-link {
font-weight: normal; font-weight: $font-weight-normal;
color: $link-color; color: $link-color;
border-radius: 0; background-color: transparent;
&,
&:active,
&.active,
&:disabled {
background-color: transparent;
@include box-shadow(none);
}
&,
&:focus,
&:active {
border-color: transparent;
}
@include hover { @include hover {
border-color: transparent;
}
@include hover-focus {
color: $link-hover-color; color: $link-hover-color;
text-decoration: $link-hover-decoration; text-decoration: $link-hover-decoration;
background-color: transparent; background-color: transparent;
border-color: transparent;
} }
&:disabled {
@include hover-focus { &:focus,
color: $btn-link-disabled-color; &.focus {
text-decoration: none; text-decoration: $link-hover-decoration;
} border-color: transparent;
box-shadow: none;
} }
&:disabled,
&.disabled {
color: $btn-link-disabled-color;
}
// No need for an active state here
} }
@ -143,12 +111,11 @@ fieldset[disabled] a.btn {
// //
.btn-lg { .btn-lg {
// line-height: ensure even-numbered height of button next to large input @include button-size($btn-padding-y-lg, $btn-padding-x-lg, $font-size-lg, $btn-line-height-lg, $btn-border-radius-lg);
@include button-size($btn-padding-y-lg, $btn-padding-x-lg, $font-size-lg, $btn-border-radius-lg);
} }
.btn-sm { .btn-sm {
// line-height: ensure proper height of button next to small input @include button-size($btn-padding-y-sm, $btn-padding-x-sm, $font-size-sm, $btn-line-height-sm, $btn-border-radius-sm);
@include button-size($btn-padding-y-sm, $btn-padding-x-sm, $font-size-sm, $btn-border-radius-sm);
} }
@ -159,11 +126,11 @@ fieldset[disabled] a.btn {
.btn-block { .btn-block {
display: block; display: block;
width: 100%; width: 100%;
}
// Vertically space out multiple block buttons // Vertically space out multiple block buttons
.btn-block + .btn-block { + .btn-block {
margin-top: $btn-block-spacing-y; margin-top: $btn-block-spacing-y;
}
} }
// Specificity overrides // Specificity overrides

294
scss/bootstrap/_card.scss

@ -4,16 +4,37 @@
.card { .card {
position: relative; position: relative;
display: block; display: flex;
margin-bottom: $card-spacer-y; flex-direction: column;
min-width: 0;
word-wrap: break-word;
background-color: $card-bg; background-color: $card-bg;
// border: $card-border-width solid $card-border-color; background-clip: border-box;
@include border-radius($card-border-radius);
border: $card-border-width solid $card-border-color; border: $card-border-width solid $card-border-color;
@include border-radius($card-border-radius);
> hr {
margin-right: 0;
margin-left: 0;
}
> .list-group:first-child {
.list-group-item:first-child {
@include border-top-radius($card-border-radius);
}
}
> .list-group:last-child {
.list-group-item:last-child {
@include border-bottom-radius($card-border-radius);
}
}
} }
.card-block { .card-body {
@include clearfix; // Enable `flex-grow: 1` for decks and groups so that card blocks take up
// as much space as possible, ensuring footers are aligned to the bottom.
flex: 1 1 auto;
padding: $card-spacer-x; padding: $card-spacer-x;
} }
@ -30,14 +51,6 @@
margin-bottom: 0; margin-bottom: 0;
} }
// .card-actions {
// padding: $card-spacer-y $card-spacer-x;
// .card-link + .card-link {
// margin-left: $card-spacer-x;
// }
// }
.card-link { .card-link {
@include hover { @include hover {
text-decoration: none; text-decoration: none;
@ -48,44 +61,34 @@
} }
} }
.card {
> .list-group:first-child {
.list-group-item:first-child {
@include border-top-radius($card-border-radius);
}
}
> .list-group:last-child {
.list-group-item:last-child {
@include border-bottom-radius($card-border-radius);
}
}
}
// //
// Optional textual caps // Optional textual caps
// //
.card-header { .card-header {
@include clearfix;
padding: $card-spacer-y $card-spacer-x; padding: $card-spacer-y $card-spacer-x;
margin-bottom: 0; // Removes the default margin-bottom of <hN>
background-color: $card-cap-bg; background-color: $card-cap-bg;
border-bottom: $card-border-width solid $card-border-color; border-bottom: $card-border-width solid $card-border-color;
&:first-child { &:first-child {
@include border-radius($card-border-radius-inner $card-border-radius-inner 0 0); @include border-radius($card-inner-border-radius $card-inner-border-radius 0 0);
}
+ .list-group {
.list-group-item:first-child {
border-top: 0;
}
} }
} }
.card-footer { .card-footer {
@include clearfix;
padding: $card-spacer-y $card-spacer-x; padding: $card-spacer-y $card-spacer-x;
background-color: $card-cap-bg; background-color: $card-cap-bg;
border-top: $card-border-width solid $card-border-color; border-top: $card-border-width solid $card-border-color;
&:last-child { &:last-child {
@include border-radius(0 0 $card-border-radius-inner $card-border-radius-inner); @include border-radius(0 0 $card-inner-border-radius $card-inner-border-radius);
} }
} }
@ -106,70 +109,7 @@
margin-left: -($card-spacer-x / 2); margin-left: -($card-spacer-x / 2);
} }
//
// Background variations
//
.card-primary {
@include card-variant($brand-primary, $brand-primary);
}
.card-success {
@include card-variant($brand-success, $brand-success);
}
.card-info {
@include card-variant($brand-info, $brand-info);
}
.card-warning {
@include card-variant($brand-warning, $brand-warning);
}
.card-danger {
@include card-variant($brand-danger, $brand-danger);
}
// Remove all backgrounds
.card-outline-primary {
@include card-outline-variant($btn-primary-bg);
}
.card-outline-secondary {
@include card-outline-variant($btn-secondary-border);
}
.card-outline-info {
@include card-outline-variant($btn-info-bg);
}
.card-outline-success {
@include card-outline-variant($btn-success-bg);
}
.card-outline-warning {
@include card-outline-variant($btn-warning-bg);
}
.card-outline-danger {
@include card-outline-variant($btn-danger-bg);
}
//
// Inverse text within a card for use with dark backgrounds
//
.card-inverse {
@include card-inverse;
}
//
// Blockquote
//
.card-blockquote {
padding: 0;
margin-bottom: 0;
border-left: 0;
}
// Card image // Card image
.card-img {
// margin: -1.325rem;
@include border-radius(.25rem);
}
.card-img-overlay { .card-img-overlay {
position: absolute; position: absolute;
top: 0; top: 0;
@ -179,88 +119,73 @@
padding: $card-img-overlay-padding; padding: $card-img-overlay-padding;
} }
.card-img {
width: 100%; // Required because we use flexbox and this inherently applies align-self: stretch
@include border-radius($card-inner-border-radius);
}
// Card image caps // Card image caps
.card-img-top { .card-img-top {
@include border-top-radius($card-border-radius-inner); width: 100%; // Required because we use flexbox and this inherently applies align-self: stretch
@include border-top-radius($card-inner-border-radius);
} }
.card-img-bottom { .card-img-bottom {
@include border-bottom-radius($card-border-radius-inner); width: 100%; // Required because we use flexbox and this inherently applies align-self: stretch
@include border-bottom-radius($card-inner-border-radius);
} }
// Card set // Card deck
//
// Heads up! We do some funky style resetting here for margins across our two
// variations (one flex, one table). Individual cards have margin-bottom by
// default, but they're ignored due to table styles. For a consistent design,
// we've done the same to the flex variation.
//
// Those changes are noted by `// Margin balancing`.
@if $enable-flex { .card-deck {
@include media-breakpoint-up(sm) { display: flex;
.card-deck { flex-direction: column;
display: flex;
flex-flow: row wrap; .card {
margin-right: -$card-deck-margin; margin-bottom: $card-deck-margin;
margin-bottom: $card-spacer-y; // Margin balancing
margin-left: -$card-deck-margin;
.card {
flex: 1 0 0;
margin-right: $card-deck-margin;
margin-bottom: 0; // Margin balancing
margin-left: $card-deck-margin;
}
}
} }
} @else {
@include media-breakpoint-up(sm) { @include media-breakpoint-up(sm) {
$space-between-cards: (2 * $card-deck-margin); flex-flow: row wrap;
.card-deck { margin-right: -$card-deck-margin;
display: table; margin-left: -$card-deck-margin;
width: 100%;
margin-bottom: $card-spacer-y; // Margin balancing .card {
table-layout: fixed; display: flex;
border-spacing: $space-between-cards 0; // Flexbugs #4: https://github.com/philipwalton/flexbugs#4-flex-shorthand-declarations-with-unitless-flex-basis-values-are-ignored
flex: 1 0 0%;
.card { flex-direction: column;
display: table-cell; margin-right: $card-deck-margin;
margin-bottom: 0; // Margin balancing margin-bottom: 0; // Override the default
vertical-align: top; margin-left: $card-deck-margin;
}
}
.card-deck-wrapper {
margin-right: (-$space-between-cards);
margin-left: (-$space-between-cards);
} }
} }
} }
// //
// Card groups // Card groups
// //
@include media-breakpoint-up(sm) { .card-group {
.card-group { display: flex;
@if $enable-flex { flex-direction: column;
display: flex;
flex-flow: row wrap;
} @else {
display: table;
width: 100%;
table-layout: fixed;
}
.card { // The child selector allows nested `.card` within `.card-group`
@if $enable-flex { // to display properly.
flex: 1 0 0; > .card {
} @else { margin-bottom: $card-group-margin;
display: table-cell; }
vertical-align: top;
} @include media-breakpoint-up(sm) {
flex-flow: row wrap;
// The child selector allows nested `.card` within `.card-group`
// to display properly.
> .card {
// Flexbugs #4: https://github.com/philipwalton/flexbugs#4-flex-shorthand-declarations-with-unitless-flex-basis-values-are-ignored
flex: 1 0 0%;
margin-bottom: 0;
+ .card { + .card {
margin-left: 0; margin-left: 0;
@ -272,30 +197,50 @@
&:first-child { &:first-child {
@include border-right-radius(0); @include border-right-radius(0);
.card-img-top { .card-img-top,
.card-header {
border-top-right-radius: 0; border-top-right-radius: 0;
} }
.card-img-bottom { .card-img-bottom,
.card-footer {
border-bottom-right-radius: 0; border-bottom-right-radius: 0;
} }
} }
&:last-child { &:last-child {
@include border-left-radius(0); @include border-left-radius(0);
.card-img-top { .card-img-top,
.card-header {
border-top-left-radius: 0; border-top-left-radius: 0;
} }
.card-img-bottom { .card-img-bottom,
.card-footer {
border-bottom-left-radius: 0; border-bottom-left-radius: 0;
} }
} }
&:not(:first-child):not(:last-child) { &:only-child {
border-radius: 0; @include border-radius($card-border-radius);
.card-img-top, .card-img-top,
.card-img-bottom { .card-header {
border-radius: 0; @include border-top-radius($card-border-radius);
}
.card-img-bottom,
.card-footer {
@include border-bottom-radius($card-border-radius);
}
}
&:not(:first-child):not(:last-child):not(:only-child) {
@include border-radius(0);
.card-img-top,
.card-img-bottom,
.card-header,
.card-footer {
@include border-radius(0);
} }
} }
} }
@ -305,16 +250,21 @@
// //
// Card // Columns
// //
@include media-breakpoint-up(sm) { .card-columns {
.card-columns { .card {
column-count: 3; margin-bottom: $card-columns-margin;
column-gap: $card-columns-sm-up-column-gap; }
@include media-breakpoint-up(sm) {
column-count: $card-columns-count;
column-gap: $card-columns-gap;
.card { .card {
width: 100%; // Don't let them exceed the column width display: inline-block; // Don't let them vertically span multiple columns
width: 100%; // Don't let their width change
} }
} }
} }

288
scss/bootstrap/_carousel.scss

@ -7,109 +7,79 @@
position: relative; position: relative;
width: 100%; width: 100%;
overflow: hidden; overflow: hidden;
}
> .carousel-item { .carousel-item {
position: relative; position: relative;
display: none; display: none;
transition: .6s ease-in-out left; align-items: center;
width: 100%;
// Account for jankitude on images @include transition($carousel-transition);
> img, backface-visibility: hidden;
> a > img { perspective: 1000px;
@extend .img-fluid; }
line-height: 1;
}
// WebKit CSS3 transforms for supported devices .carousel-item.active,
@media all and (transform-3d), (-webkit-transform-3d) { .carousel-item-next,
transition: transform .6s ease-in-out; .carousel-item-prev {
backface-visibility: hidden; display: block;
perspective: 1000px; }
&.next,
&.active.right {
left: 0;
transform: translate3d(100%, 0, 0);
}
&.prev,
&.active.left {
left: 0;
transform: translate3d(-100%, 0, 0);
}
&.next.left,
&.prev.right,
&.active {
left: 0;
transform: translate3d(0, 0, 0);
}
}
}
> .active, .carousel-item-next,
> .next, .carousel-item-prev {
> .prev { position: absolute;
display: block; top: 0;
} }
> .active { // CSS3 transforms when supported by the browser
left: 0; // .carousel-item-next.carousel-item-left,
} // .carousel-item-prev.carousel-item-right {
// transform: translateX(0);
> .next, // @supports (transform-style: preserve-3d) {
> .prev { // transform: translate3d(0, 0, 0);
position: absolute; // }
top: 0; // }
width: 100%;
}
> .next { // .carousel-item-next,
left: 100%; // .active.carousel-item-right {
} // transform: translateX(100%);
> .prev {
left: -100%;
}
> .next.left,
> .prev.right {
left: 0;
}
> .active.left { // @supports (transform-style: preserve-3d) {
left: -100%; // transform: translate3d(100%, 0, 0);
} // }
> .active.right { // }
left: 100%;
} // .carousel-item-prev,
} // .active.carousel-item-left {
// transform: translateX(-100%);
// @supports (transform-style: preserve-3d) {
// transform: translate3d(-100%, 0, 0);
// }
// }
// //
// Left/right controls for nav // Left/right controls for nav
// //
.carousel-control { .carousel-control-prev,
.carousel-control-next {
position: absolute; position: absolute;
top: 0; top: 0;
bottom: 0; bottom: 0;
left: 0; // Use flex for alignment (1-3)
display: flex; // 1. allow flex styles
align-items: center; // 2. vertically center contents
justify-content: center; // 3. horizontally center contents
width: $carousel-control-width; width: $carousel-control-width;
font-size: $carousel-control-font-size;
color: $carousel-control-color; color: $carousel-control-color;
text-align: center; text-align: center;
text-shadow: $carousel-text-shadow;
opacity: $carousel-control-opacity; opacity: $carousel-control-opacity;
// We can't have this transition here because WebKit cancels the carousel // We can't have a transition here because WebKit cancels the carousel
// animation if you trip this while in the middle of another animation. // animation if you trip this while in the middle of another animation.
// Set gradients for backgrounds
&.left {
@include gradient-x($start-color: rgba(0,0,0,.5), $end-color: rgba(0,0,0,.0001));
}
&.right {
right: 0;
left: auto;
@include gradient-x($start-color: rgba(0,0,0,.0001), $end-color: rgba(0,0,0,.5));
}
// Hover/focus state // Hover/focus state
@include hover-focus { @include hover-focus {
color: $carousel-control-color; color: $carousel-control-color;
@ -117,79 +87,88 @@
outline: 0; outline: 0;
opacity: .9; opacity: .9;
} }
}
// Toggles .carousel-control-prev {
.icon-prev, left: 0;
.icon-next { @if $enable-gradients {
position: absolute; background: linear-gradient(90deg, rgba(0, 0, 0, .25), rgba(0, 0, 0, .001));
top: 50%;
z-index: 5;
display: inline-block;
width: $carousel-icon-width;
height: $carousel-icon-width;
margin-top: -($carousel-icon-width / 2);
font-family: serif;
line-height: 1;
}
.icon-prev {
left: 50%;
margin-left: -($carousel-icon-width / 2);
} }
.icon-next { }
right: 50%; .carousel-control-next {
margin-right: -($carousel-icon-width / 2); right: 0;
@if $enable-gradients {
background: linear-gradient(270deg, rgba(0, 0, 0, .25), rgba(0, 0, 0, .001));
} }
}
.icon-prev { // Icons for within
&::before { .carousel-control-prev-icon,
content: "\2039";// SINGLE LEFT-POINTING ANGLE QUOTATION MARK (U+2039) .carousel-control-next-icon {
} display: inline-block;
} width: $carousel-control-icon-width;
.icon-next { height: $carousel-control-icon-width;
&::before { background: transparent no-repeat center center;
content: "\203a";// SINGLE RIGHT-POINTING ANGLE QUOTATION MARK (U+203A) background-size: 100% 100%;
} }
} .carousel-control-prev-icon {
background-image: $carousel-control-prev-icon-bg;
}
.carousel-control-next-icon {
background-image: $carousel-control-next-icon-bg;
} }
// Optional indicator pips // Optional indicator pips
// //
// Add an unordered list with the following class and add a list item for each // Add an ordered list with the following class and add a list item for each
// slide your carousel holds. // slide your carousel holds.
.carousel-indicators { .carousel-indicators {
position: absolute; position: absolute;
right: 0;
bottom: 10px; bottom: 10px;
left: 50%; left: 0;
z-index: 15; z-index: 15;
width: $carousel-indicators-width; display: flex;
padding-left: 0; justify-content: center;
margin-left: -($carousel-indicators-width / 2); padding-left: 0; // override <ol> default
text-align: center; // Use the .carousel-control's width as margin so we don't overlay those
margin-right: $carousel-control-width;
margin-left: $carousel-control-width;
list-style: none; list-style: none;
li { li {
display: inline-block; position: relative;
width: $carousel-indicator-size; flex: 0 1 auto;
height: $carousel-indicator-size; width: $carousel-indicator-width;
margin: 1px; height: $carousel-indicator-height;
margin-right: $carousel-indicator-spacer;
margin-left: $carousel-indicator-spacer;
text-indent: -999px; text-indent: -999px;
cursor: pointer; background-color: rgba($carousel-indicator-active-bg, .5);
// IE9 hack for event handling
// // Use pseudo classes to increase the hit area by 10px on top and bottom.
// Internet Explorer 9 does not properly handle clicks on elements with a `background-color` of `transparent`, &::before {
// so we use `rgba(0,0,0,0)` instead since it's a non-buggy equivalent. position: absolute;
// See https://developer.mozilla.org/en-US/docs/Web/Events/click#Internet_Explorer top: -10px;
background-color: rgba(0,0,0,0); // IE9 left: 0;
border: 1px solid $carousel-indicator-border-color; display: inline-block;
border-radius: $carousel-indicator-size; width: 100%;
height: 10px;
content: "";
}
&::after {
position: absolute;
bottom: -10px;
left: 0;
display: inline-block;
width: 100%;
height: 10px;
content: "";
}
} }
.active { .active {
width: $carousel-indicator-active-size;
height: $carousel-indicator-active-size;
margin: 0;
background-color: $carousel-indicator-active-bg; background-color: $carousel-indicator-active-bg;
} }
} }
@ -197,7 +176,7 @@
// Optional captions // Optional captions
// //
// Hidden by default for smaller viewports. //
.carousel-caption { .carousel-caption {
position: absolute; position: absolute;
@ -209,45 +188,4 @@
padding-bottom: 20px; padding-bottom: 20px;
color: $carousel-caption-color; color: $carousel-caption-color;
text-align: center; text-align: center;
text-shadow: $carousel-text-shadow;
.btn {
text-shadow: none; // No shadow for button elements in carousel-caption
}
}
//
// Responsive variations
//
@include media-breakpoint-up(sm) {
// Scale up the controls a smidge
.carousel-control {
.icon-prev,
.icon-next {
width: $carousel-control-sm-up-size;
height: $carousel-control-sm-up-size;
margin-top: -($carousel-control-sm-up-size / 2);
font-size: $carousel-control-sm-up-size;
}
.icon-prev {
margin-left: -($carousel-control-sm-up-size / 2);
}
.icon-next {
margin-right: -($carousel-control-sm-up-size / 2);
}
}
// Show and left align the captions
.carousel-caption {
right: ((100% - $carousel-caption-sm-up-width) / 2);
left: ((100% - $carousel-caption-sm-up-width) / 2);
padding-bottom: 30px;
}
// Move up the indicators
.carousel-indicators {
bottom: 20px;
}
} }

17
scss/bootstrap/_close.scss

@ -1,17 +1,21 @@
.close { .close {
float: right; float: right;
font-size: ($font-size-base * 1.5); font-size: $close-font-size;
font-weight: $close-font-weight; font-weight: $close-font-weight;
line-height: 1; line-height: 1;
color: $close-color; color: $close-color;
text-shadow: $close-text-shadow; text-shadow: $close-text-shadow;
opacity: .2; opacity: .5;
@include hover-focus { @include hover-focus {
color: $close-color; color: $close-color;
text-decoration: none; text-decoration: none;
opacity: .75;
}
// Opinionated: add "hand" cursor to non-disabled .close elements
&:not(:disabled):not(.disabled) {
cursor: pointer; cursor: pointer;
opacity: .5;
} }
} }
@ -20,12 +24,11 @@
// If you want the anchor version, it requires `href="#"`. // If you want the anchor version, it requires `href="#"`.
// See https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile // See https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile
// scss-lint:disable QualifyingElement // stylelint-disable property-no-vendor-prefix, selector-no-qualifying-type
button.close { button.close {
padding: 0; padding: 0;
cursor: pointer; background-color: transparent;
background: transparent;
border: 0; border: 0;
-webkit-appearance: none; -webkit-appearance: none;
} }
// scss-lint:enable QualifyingElement // stylelint-enable

19
scss/bootstrap/_code.scss

@ -8,17 +8,20 @@ samp {
// Inline code // Inline code
code { code {
padding: $code-padding-y $code-padding-x;
font-size: $code-font-size; font-size: $code-font-size;
color: $code-color; color: $code-color;
background-color: $code-bg; word-break: break-word;
@include border-radius($border-radius);
// Streamline the style when inside anchors to avoid broken underline and more
a > & {
color: inherit;
}
} }
// User input typically entered via keyboard // User input typically entered via keyboard
kbd { kbd {
padding: $code-padding-y $code-padding-x; padding: $kbd-padding-y $kbd-padding-x;
font-size: $code-font-size; font-size: $kbd-font-size;
color: $kbd-color; color: $kbd-color;
background-color: $kbd-bg; background-color: $kbd-bg;
@include border-radius($border-radius-sm); @include border-radius($border-radius-sm);
@ -35,18 +38,14 @@ kbd {
// Blocks of code // Blocks of code
pre { pre {
display: block; display: block;
margin-top: 0;
margin-bottom: 1rem;
font-size: $code-font-size; font-size: $code-font-size;
color: $pre-color; color: $pre-color;
// Account for some code outputs that place code tags in pre tags // Account for some code outputs that place code tags in pre tags
code { code {
padding: 0;
font-size: inherit; font-size: inherit;
color: inherit; color: inherit;
background-color: transparent; word-break: normal;
border-radius: 0;
} }
} }

270
scss/bootstrap/_custom-forms.scss

@ -1,8 +1,6 @@
// scss-lint:disable PropertyCount
// Embedded icons from Open Iconic. // Embedded icons from Open Iconic.
// Released under MIT and copyright 2014 Waybury. // Released under MIT and copyright 2014 Waybury.
// http://useiconic.com/open // https://useiconic.com/open
// Checkboxes and radios // Checkboxes and radios
@ -11,13 +9,14 @@
.custom-control { .custom-control {
position: relative; position: relative;
display: inline; display: block;
min-height: (1rem * $line-height-base);
padding-left: $custom-control-gutter; padding-left: $custom-control-gutter;
cursor: pointer; }
+ .custom-control { .custom-control-inline {
margin-left: $custom-control-spacer-x; display: inline-flex;
} margin-right: $custom-control-spacer-x;
} }
.custom-control-input { .custom-control-input {
@ -25,73 +24,107 @@
z-index: -1; // Put the input behind the label so it doesn't overlay text z-index: -1; // Put the input behind the label so it doesn't overlay text
opacity: 0; opacity: 0;
&:checked ~ .custom-control-indicator { &:checked ~ .custom-control-label::before {
color: $custom-control-checked-indicator-color; color: $custom-control-indicator-checked-color;
background-color: $custom-control-checked-indicator-bg; @include gradient-bg($custom-control-indicator-checked-bg);
@include box-shadow($custom-control-checked-indicator-box-shadow); @include box-shadow($custom-control-indicator-checked-box-shadow);
} }
&:focus ~ .custom-control-indicator { &:focus ~ .custom-control-label::before {
// the mixin is not used here to make sure there is feedback // the mixin is not used here to make sure there is feedback
box-shadow: $custom-control-focus-indicator-box-shadow; box-shadow: $custom-control-indicator-focus-box-shadow;
} }
&:active ~ .custom-control-indicator { &:active ~ .custom-control-label::before {
color: $custom-control-active-indicator-color; color: $custom-control-indicator-active-color;
background-color: $custom-control-active-indicator-bg; background-color: $custom-control-indicator-active-bg;
@include box-shadow($custom-control-active-indicator-box-shadow); @include box-shadow($custom-control-indicator-active-box-shadow);
} }
&:disabled { &:disabled {
~ .custom-control-indicator { ~ .custom-control-label {
cursor: $custom-control-disabled-cursor; color: $custom-control-label-disabled-color;
background-color: $custom-control-disabled-indicator-bg;
}
~ .custom-control-description { &::before {
color: $custom-control-disabled-description-color; background-color: $custom-control-indicator-disabled-bg;
cursor: $custom-control-disabled-cursor; }
} }
} }
} }
// Custom indicator // Custom control indicators
// //
// Generates a shadow element to create our makeshift checkbox/radio background. // Build the custom controls out of psuedo-elements.
.custom-control-indicator { .custom-control-label {
position: absolute; margin-bottom: 0;
top: .0625rem;
left: 0; // Background-color and (when enabled) gradient
display: block; &::before {
width: $custom-control-indicator-size; position: absolute;
height: $custom-control-indicator-size; top: (($line-height-base - $custom-control-indicator-size) / 2);
pointer-events: none; left: 0;
user-select: none; display: block;
background-color: $custom-control-indicator-bg; width: $custom-control-indicator-size;
background-repeat: no-repeat; height: $custom-control-indicator-size;
background-position: center center; pointer-events: none;
background-size: $custom-control-indicator-bg-size; content: "";
@include box-shadow($custom-control-indicator-box-shadow); user-select: none;
background-color: $custom-control-indicator-bg;
@include box-shadow($custom-control-indicator-box-shadow);
}
// Foreground (icon)
&::after {
position: absolute;
top: (($line-height-base - $custom-control-indicator-size) / 2);
left: 0;
display: block;
width: $custom-control-indicator-size;
height: $custom-control-indicator-size;
content: "";
background-repeat: no-repeat;
background-position: center center;
background-size: $custom-control-indicator-bg-size;
}
} }
// Checkboxes // Checkboxes
// //
// Tweak just a few things for checkboxes. // Tweak just a few things for checkboxes.
.custom-checkbox { .custom-checkbox {
.custom-control-indicator { .custom-control-label::before {
@include border-radius($custom-checkbox-radius); @include border-radius($custom-checkbox-indicator-border-radius);
}
.custom-control-input:checked ~ .custom-control-label {
&::before {
@include gradient-bg($custom-control-indicator-checked-bg);
}
&::after {
background-image: $custom-checkbox-indicator-icon-checked;
}
} }
.custom-control-input:checked ~ .custom-control-indicator { .custom-control-input:indeterminate ~ .custom-control-label {
background-image: $custom-checkbox-checked-icon; &::before {
@include gradient-bg($custom-checkbox-indicator-indeterminate-bg);
@include box-shadow($custom-checkbox-indicator-indeterminate-box-shadow);
}
&::after {
background-image: $custom-checkbox-indicator-icon-indeterminate;
}
} }
.custom-control-input:indeterminate ~ .custom-control-indicator { .custom-control-input:disabled {
background-color: $custom-checkbox-indeterminate-bg; &:checked ~ .custom-control-label::before {
background-image: $custom-checkbox-indeterminate-icon; background-color: $custom-control-indicator-checked-disabled-bg;
@include box-shadow($custom-checkbox-indeterminate-box-shadow); }
&:indeterminate ~ .custom-control-label::before {
background-color: $custom-control-indicator-checked-disabled-bg;
}
} }
} }
@ -100,33 +133,22 @@
// Tweak just a few things for radios. // Tweak just a few things for radios.
.custom-radio { .custom-radio {
.custom-control-indicator { .custom-control-label::before {
border-radius: $custom-radio-radius; border-radius: $custom-radio-indicator-border-radius;
} }
.custom-control-input:checked ~ .custom-control-indicator { .custom-control-input:checked ~ .custom-control-label {
background-image: $custom-radio-checked-icon; &::before {
} @include gradient-bg($custom-control-indicator-checked-bg);
} }
// Layout options
//
// By default radios and checkboxes are `inline-block` with no additional spacing
// set. Use these optional classes to tweak the layout.
.custom-controls-stacked {
.custom-control {
display: inline;
&::after { &::after {
display: block; background-image: $custom-radio-indicator-icon-checked;
margin-bottom: $custom-control-spacer-y;
content: "";
} }
}
+ .custom-control { .custom-control-input:disabled {
margin-left: 0; &:checked ~ .custom-control-label::before {
background-color: $custom-control-indicator-checked-disabled-bg;
} }
} }
} }
@ -137,32 +159,33 @@
// Replaces the browser default select with a custom one, mostly pulled from // Replaces the browser default select with a custom one, mostly pulled from
// http://primercss.io. // http://primercss.io.
// //
// Includes IE9-specific hacks (noted by ` \9`).
.custom-select { .custom-select {
display: inline-block; display: inline-block;
max-width: 100%; width: 100%;
height: $custom-select-height;
padding: $custom-select-padding-y ($custom-select-padding-x + $custom-select-indicator-padding) $custom-select-padding-y $custom-select-padding-x; padding: $custom-select-padding-y ($custom-select-padding-x + $custom-select-indicator-padding) $custom-select-padding-y $custom-select-padding-x;
padding-right: $custom-select-padding-x \9; line-height: $custom-select-line-height;
color: $custom-select-color; color: $custom-select-color;
vertical-align: middle; vertical-align: middle;
background: $custom-select-bg $custom-select-indicator no-repeat right $custom-select-padding-x center; background: $custom-select-bg $custom-select-indicator no-repeat right $custom-select-padding-x center;
background-image: none \9;
background-size: $custom-select-bg-size; background-size: $custom-select-bg-size;
border: $custom-select-border-width solid $custom-select-border-color; border: $custom-select-border-width solid $custom-select-border-color;
@include border-radius($custom-select-border-radius); @if $enable-rounded {
// Use vendor prefixes as `appearance` isn't part of the CSS spec. border-radius: $custom-select-border-radius;
-moz-appearance: none; } @else {
-webkit-appearance: none; border-radius: 0;
}
appearance: none;
&:focus { &:focus {
border-color: $custom-select-focus-border-color; border-color: $custom-select-focus-border-color;
outline: none; outline: 0;
@include box-shadow($custom-select-focus-box-shadow); box-shadow: $custom-select-focus-box-shadow;
&::-ms-value { &::-ms-value {
// For visual consistency with other platforms/browsers, // For visual consistency with other platforms/browsers,
// supress the default white text on blue background highlight given to // suppress the default white text on blue background highlight given to
// the selected option text when the (still closed) <select> receives focus // the selected option text when the (still closed) <select> receives focus
// in IE and (under certain conditions) Edge. // in IE and (under certain conditions) Edge.
// See https://github.com/twbs/bootstrap/issues/19398. // See https://github.com/twbs/bootstrap/issues/19398.
@ -171,9 +194,15 @@
} }
} }
&[multiple],
&[size]:not([size="1"]) {
height: auto;
padding-right: $custom-select-padding-x;
background-image: none;
}
&:disabled { &:disabled {
color: $custom-select-disabled-color; color: $custom-select-disabled-color;
cursor: $cursor-disabled;
background-color: $custom-select-disabled-bg; background-color: $custom-select-disabled-bg;
} }
@ -184,14 +213,17 @@
} }
.custom-select-sm { .custom-select-sm {
height: $custom-select-height-sm;
padding-top: $custom-select-padding-y; padding-top: $custom-select-padding-y;
padding-bottom: $custom-select-padding-y; padding-bottom: $custom-select-padding-y;
font-size: $custom-select-sm-font-size; font-size: $custom-select-font-size-sm;
}
// &:not([multiple]) { .custom-select-lg {
// height: 26px; height: $custom-select-height-lg;
// min-height: 26px; padding-top: $custom-select-padding-y;
// } padding-bottom: $custom-select-padding-y;
font-size: $custom-select-font-size-lg;
} }
@ -202,64 +234,64 @@
.custom-file { .custom-file {
position: relative; position: relative;
display: inline-block; display: inline-block;
max-width: 100%; width: 100%;
height: $custom-file-height; height: $custom-file-height;
cursor: pointer; margin-bottom: 0;
} }
.custom-file-input { .custom-file-input {
min-width: $custom-file-width; position: relative;
max-width: 100%; z-index: 2;
width: 100%;
height: $custom-file-height;
margin: 0; margin: 0;
filter: alpha(opacity = 0);
opacity: 0; opacity: 0;
&:focus ~ .custom-file-control { &:focus ~ .custom-file-control {
@include box-shadow($custom-file-focus-box-shadow); border-color: $custom-file-focus-border-color;
box-shadow: $custom-file-focus-box-shadow;
&::before {
border-color: $custom-file-focus-border-color;
}
}
@each $lang, $value in $custom-file-text {
&:lang(#{$lang}) ~ .custom-file-label::after {
content: $value;
}
} }
} }
.custom-file-control { .custom-file-label {
position: absolute; position: absolute;
top: 0; top: 0;
right: 0; right: 0;
left: 0; left: 0;
z-index: 5; z-index: 1;
height: $custom-file-height; height: $custom-file-height;
padding: $custom-file-padding-x $custom-file-padding-y; padding: $custom-file-padding-y $custom-file-padding-x;
line-height: $custom-file-line-height; line-height: $custom-file-line-height;
color: $custom-file-color; color: $custom-file-color;
user-select: none;
background-color: $custom-file-bg; background-color: $custom-file-bg;
border: $custom-file-border-width solid $custom-file-border-color; border: $custom-file-border-width solid $custom-file-border-color;
@include border-radius($custom-file-border-radius); @include border-radius($custom-file-border-radius);
@include box-shadow($custom-file-box-shadow); @include box-shadow($custom-file-box-shadow);
@each $lang, $text in map-get($custom-file-text, placeholder) { &::after {
&:lang(#{$lang})::after {
content: $text;
}
}
&::before {
position: absolute; position: absolute;
top: -$custom-file-border-width; top: 0;
right: -$custom-file-border-width; right: 0;
bottom: -$custom-file-border-width; bottom: 0;
z-index: 6; z-index: 3;
display: block; display: block;
height: $custom-file-height; height: calc(#{$custom-file-height} - #{$custom-file-border-width} * 2);
padding: $custom-file-padding-x $custom-file-padding-y; padding: $custom-file-padding-y $custom-file-padding-x;
line-height: $custom-file-line-height; line-height: $custom-file-line-height;
color: $custom-file-button-color; color: $custom-file-button-color;
background-color: $custom-file-button-bg; content: "Browse";
border: $custom-file-border-width solid $custom-file-border-color; @include gradient-bg($custom-file-button-bg);
border-left: $custom-file-border-width solid $custom-file-border-color;
@include border-radius(0 $custom-file-border-radius $custom-file-border-radius 0); @include border-radius(0 $custom-file-border-radius $custom-file-border-radius 0);
} }
@each $lang, $text in map-get($custom-file-text, button-label) {
&:lang(#{$lang})::before {
content: $text;
}
}
} }

176
scss/bootstrap/_dropdown.scss

@ -6,31 +6,7 @@
.dropdown-toggle { .dropdown-toggle {
// Generate the caret automatically // Generate the caret automatically
&::after { @include caret;
display: inline-block;
width: 0;
height: 0;
margin-left: $caret-width;
vertical-align: middle;
content: "";
border-top: $caret-width solid;
border-right: $caret-width solid transparent;
border-left: $caret-width solid transparent;
}
// Prevent the focus on the dropdown toggle when closing dropdowns
&:focus {
outline: 0;
}
}
.dropup {
.dropdown-toggle {
&::after {
border-top: 0;
border-bottom: $caret-width solid;
}
}
} }
// The dropdown menu // The dropdown menu
@ -43,18 +19,59 @@
float: left; float: left;
min-width: $dropdown-min-width; min-width: $dropdown-min-width;
padding: $dropdown-padding-y 0; padding: $dropdown-padding-y 0;
margin: $dropdown-margin-top 0 0; // override default ul margin: $dropdown-spacer 0 0; // override default ul
font-size: $font-size-base; font-size: $font-size-base; // Redeclare because nesting can cause inheritance issues
color: $body-color; color: $body-color;
text-align: left; // Ensures proper alignment if parent has it changed (e.g., modal footer) text-align: left; // Ensures proper alignment if parent has it changed (e.g., modal footer)
list-style: none; list-style: none;
background-color: $dropdown-bg; background-color: $dropdown-bg;
background-clip: padding-box; background-clip: padding-box;
border: $dropdown-border-width solid $dropdown-border-color; border: $dropdown-border-width solid $dropdown-border-color;
@include border-radius($border-radius); @include border-radius($dropdown-border-radius);
@include box-shadow($dropdown-box-shadow); @include box-shadow($dropdown-box-shadow);
} }
// Allow for dropdowns to go bottom up (aka, dropup-menu)
// Just add .dropup after the standard .dropdown class and you're set.
.dropup {
.dropdown-menu {
margin-top: 0;
margin-bottom: $dropdown-spacer;
}
.dropdown-toggle {
@include caret(up);
}
}
.dropright {
.dropdown-menu {
margin-top: 0;
margin-left: $dropdown-spacer;
}
.dropdown-toggle {
@include caret(right);
&::after {
vertical-align: 0;
}
}
}
.dropleft {
.dropdown-menu {
margin-top: 0;
margin-right: $dropdown-spacer;
}
.dropdown-toggle {
@include caret(left);
&::before {
vertical-align: 0;
}
}
}
// Dividers (basically an `<hr>`) within the dropdown // Dividers (basically an `<hr>`) within the dropdown
.dropdown-divider { .dropdown-divider {
@include nav-divider($dropdown-divider-bg); @include nav-divider($dropdown-divider-bg);
@ -66,114 +83,49 @@
.dropdown-item { .dropdown-item {
display: block; display: block;
width: 100%; // For `<button>`s width: 100%; // For `<button>`s
padding: 3px $dropdown-item-padding-x; padding: $dropdown-item-padding-y $dropdown-item-padding-x;
clear: both; clear: both;
font-weight: normal; font-weight: $font-weight-normal;
color: $dropdown-link-color; color: $dropdown-link-color;
text-align: inherit; // For `<button>`s text-align: inherit; // For `<button>`s
white-space: nowrap; // prevent links from randomly breaking onto new lines white-space: nowrap; // prevent links from randomly breaking onto new lines
background: none; // For `<button>`s background-color: transparent; // For `<button>`s
border: 0; // For `<button>`s border: 0; // For `<button>`s
@include hover-focus { @include hover-focus {
color: $dropdown-link-hover-color; color: $dropdown-link-hover-color;
text-decoration: none; text-decoration: none;
background-color: $dropdown-link-hover-bg; @include gradient-bg($dropdown-link-hover-bg);
} }
// Active state &.active,
&.active { &:active {
@include plain-hover-focus { color: $dropdown-link-active-color;
color: $dropdown-link-active-color; text-decoration: none;
text-decoration: none; @include gradient-bg($dropdown-link-active-bg);
background-color: $dropdown-link-active-bg;
outline: 0;
}
} }
// Disabled state &.disabled,
// &:disabled {
// Gray out text and ensure the hover/focus state remains gray color: $dropdown-link-disabled-color;
&.disabled { background-color: transparent;
@include plain-hover-focus { // Remove CSS gradients if they're enabled
color: $dropdown-link-disabled-color; @if $enable-gradients {
} background-image: none;
// Nuke hover/focus effects
@include hover-focus {
text-decoration: none;
cursor: $cursor-disabled;
background-color: transparent;
background-image: none; // Remove CSS gradient
@include reset-filter();
} }
} }
} }
// Open state for the dropdown .dropdown-menu.show {
.open { display: block;
// Show the menu
> .dropdown-menu {
display: block;
}
// Remove the outline when :focus is triggered
> a {
outline: 0;
}
}
// Menu positioning
//
// Add extra class to `.dropdown-menu` to flip the alignment of the dropdown
// menu with the parent.
.dropdown-menu-right {
right: 0;
left: auto; // Reset the default from `.dropdown-menu`
}
.dropdown-menu-left {
right: auto;
left: 0;
} }
// Dropdown section headers // Dropdown section headers
.dropdown-header { .dropdown-header {
display: block; display: block;
padding: $dropdown-padding-y $dropdown-item-padding-x; padding: $dropdown-padding-y $dropdown-item-padding-x;
margin-bottom: 0; // for use with heading elements
font-size: $font-size-sm; font-size: $font-size-sm;
color: $dropdown-header-color; color: $dropdown-header-color;
white-space: nowrap; // as with > li > a white-space: nowrap; // as with > li > a
} }
// Backdrop to catch body clicks on mobile, etc.
.dropdown-backdrop {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: $zindex-dropdown-backdrop;
}
// Allow for dropdowns to go bottom up (aka, dropup-menu)
//
// Just add .dropup after the standard .dropdown class and you're set.
// TODO: abstract this so that the navbar fixed styles are not placed here?
.dropup,
.navbar-fixed-bottom .dropdown {
// Reverse the caret
.caret {
content: "";
border-top: 0;
border-bottom: $caret-width solid;
}
// Different positioning for bottom up menu
.dropdown-menu {
top: auto;
bottom: 100%;
margin-bottom: $dropdown-margin-top;
}
}

271
scss/bootstrap/_forms.scss

@ -1,4 +1,4 @@
// scss-lint:disable QualifyingElement // stylelint-disable selector-no-qualifying-type
// //
// Textual form controls // Textual form controls
@ -7,21 +7,25 @@
.form-control { .form-control {
display: block; display: block;
width: 100%; width: 100%;
// // Make inputs at least the height of their button counterpart (base line-height + padding + border)
// height: $input-height;
padding: $input-padding-y $input-padding-x; padding: $input-padding-y $input-padding-x;
font-size: $font-size-base; font-size: $font-size-base;
line-height: $input-line-height; line-height: $input-line-height;
color: $input-color; color: $input-color;
background-color: $input-bg; background-color: $input-bg;
// Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214.
background-image: none;
background-clip: padding-box; background-clip: padding-box;
border: $input-btn-border-width solid $input-border-color; border: $input-border-width solid $input-border-color;
// Note: This has no effect on <select>s in some browsers, due to the limited stylability of `<select>`s in CSS. // Note: This has no effect on <select>s in some browsers, due to the limited stylability of `<select>`s in CSS.
@include border-radius($input-border-radius); @if $enable-rounded {
// Manually use the if/else instead of the mixin to account for iOS override
border-radius: $input-border-radius;
} @else {
// Otherwise undo the iOS default
border-radius: 0;
}
@include box-shadow($input-box-shadow); @include box-shadow($input-box-shadow);
@include transition(border-color ease-in-out .15s, box-shadow ease-in-out .15s); @include transition($input-transition);
// Unstyle the caret on `<select>`s in IE10+. // Unstyle the caret on `<select>`s in IE10+.
&::-ms-expand { &::-ms-expand {
@ -34,7 +38,7 @@
// Placeholder // Placeholder
&::placeholder { &::placeholder {
color: $input-color-placeholder; color: $input-placeholder-color;
// Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526. // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526.
opacity: 1; opacity: 1;
} }
@ -46,14 +50,10 @@
// don't honor that edge case; we style them as disabled anyway. // don't honor that edge case; we style them as disabled anyway.
&:disabled, &:disabled,
&[readonly] { &[readonly] {
background-color: $input-bg-disabled; background-color: $input-disabled-bg;
// iOS fix for unreadable disabled content; see https://github.com/twbs/bootstrap/issues/11655. // iOS fix for unreadable disabled content; see https://github.com/twbs/bootstrap/issues/11655.
opacity: 1; opacity: 1;
} }
&:disabled {
cursor: $cursor-disabled;
}
} }
select.form-control { select.form-control {
@ -76,6 +76,7 @@ select.form-control {
.form-control-file, .form-control-file,
.form-control-range { .form-control-range {
display: block; display: block;
width: 100%;
} }
@ -83,53 +84,46 @@ select.form-control {
// Labels // Labels
// //
// For use with horizontal and inline forms, when you need the label text to // For use with horizontal and inline forms, when you need the label (or legend)
// align with the form controls. // text to align with the form controls.
.col-form-label { .col-form-label {
padding-top: $input-padding-y; padding-top: calc(#{$input-padding-y} + #{$input-border-width});
padding-bottom: $input-padding-y; padding-bottom: calc(#{$input-padding-y} + #{$input-border-width});
margin-bottom: 0; // Override the `<label>` default margin-bottom: 0; // Override the `<label>/<legend>` default
font-size: inherit; // Override the `<legend>` default
line-height: $input-line-height;
} }
.col-form-label-lg { .col-form-label-lg {
padding-top: $input-padding-y-lg; padding-top: calc(#{$input-padding-y-lg} + #{$input-border-width});
padding-bottom: $input-padding-y-lg; padding-bottom: calc(#{$input-padding-y-lg} + #{$input-border-width});
font-size: $font-size-lg; font-size: $font-size-lg;
line-height: $input-line-height-lg;
} }
.col-form-label-sm { .col-form-label-sm {
padding-top: $input-padding-y-sm; padding-top: calc(#{$input-padding-y-sm} + #{$input-border-width});
padding-bottom: $input-padding-y-sm; padding-bottom: calc(#{$input-padding-y-sm} + #{$input-border-width});
font-size: $font-size-sm; font-size: $font-size-sm;
line-height: $input-line-height-sm;
} }
// Readonly controls as plain text
// //
// Legends // Apply class to a readonly input to make it appear like regular plain
// // text (without any border, background color, focus indicator)
// For use with horizontal and inline forms, when you need the legend text to .form-control-plaintext {
// be the same size as regular labels, and to align with the form controls. display: block;
.col-form-legend { width: 100%;
padding-top: $input-padding-y;
padding-bottom: $input-padding-y;
margin-bottom: 0;
font-size: $font-size-base;
}
// Static form control text
//
// Apply class to an element to make any string of text align with labels in a
// horizontal form layout.
.form-control-static {
min-height: $input-height;
// Size it appropriately next to real form controls
padding-top: $input-padding-y; padding-top: $input-padding-y;
padding-bottom: $input-padding-y; padding-bottom: $input-padding-y;
// Remove default margin from `p` margin-bottom: 0; // match inputs if this class comes on inputs with default margins
margin-bottom: 0; line-height: $input-line-height;
background-color: transparent;
border: solid transparent;
border-width: $input-border-width 0;
&.form-control-sm, &.form-control-sm,
&.form-control-lg { &.form-control-lg {
@ -150,6 +144,7 @@ select.form-control {
.form-control-sm { .form-control-sm {
padding: $input-padding-y-sm $input-padding-x-sm; padding: $input-padding-y-sm $input-padding-x-sm;
font-size: $font-size-sm; font-size: $font-size-sm;
line-height: $input-line-height-sm;
@include border-radius($input-border-radius-sm); @include border-radius($input-border-radius-sm);
} }
@ -162,6 +157,7 @@ select.form-control-sm {
.form-control-lg { .form-control-lg {
padding: $input-padding-y-lg $input-padding-x-lg; padding: $input-padding-y-lg $input-padding-x-lg;
font-size: $font-size-lg; font-size: $font-size-lg;
line-height: $input-line-height-lg;
@include border-radius($input-border-radius-lg); @include border-radius($input-border-radius-lg);
} }
@ -183,7 +179,25 @@ select.form-control-lg {
.form-text { .form-text {
display: block; display: block;
margin-top: ($spacer * .25); margin-top: $form-text-margin-top;
}
// Form grid
//
// Special replacement for our grid system's `.row` for tighter form layouts.
.form-row {
display: flex;
flex-wrap: wrap;
margin-right: -5px;
margin-left: -5px;
> .col,
> [class*="col-"] {
padding-right: 5px;
padding-left: 5px;
}
} }
@ -194,98 +208,48 @@ select.form-control-lg {
.form-check { .form-check {
position: relative; position: relative;
display: block; display: block;
margin-bottom: ($spacer * .75); padding-left: $form-check-input-gutter;
// Move up sibling radios or checkboxes for tighter spacing
+ .form-check {
margin-top: -.25rem;
}
&.disabled {
.form-check-label {
color: $text-muted;
cursor: $cursor-disabled;
}
}
}
.form-check-label {
padding-left: 1.25rem;
margin-bottom: 0; // Override default `<label>` bottom margin
cursor: pointer;
} }
.form-check-input { .form-check-input {
position: absolute; position: absolute;
margin-top: .25rem; margin-top: $form-check-input-margin-y;
margin-left: -1.25rem; margin-left: -$form-check-input-gutter;
&:only-child { &:disabled ~ .form-check-label {
position: static; color: $text-muted;
} }
} }
// Radios and checkboxes on same line .form-check-label {
.form-check-inline {
position: relative;
display: inline-block;
padding-left: 1.25rem;
margin-bottom: 0; // Override default `<label>` bottom margin margin-bottom: 0; // Override default `<label>` bottom margin
vertical-align: middle;
cursor: pointer;
+ .form-check-inline {
margin-left: .75rem;
}
&.disabled {
cursor: $cursor-disabled;
}
}
// Form control feedback states
//
// Apply contextual and semantic states to individual form controls.
.form-control-feedback {
margin-top: ($spacer * .25);
}
.form-control-success,
.form-control-warning,
.form-control-danger {
padding-right: ($input-padding-x * 3);
background-repeat: no-repeat;
background-position: center right ($input-height / 4);
background-size: ($input-height / 2) ($input-height / 2);
}
// Form validation states
.has-success {
@include form-control-validation($brand-success);
.form-control-success {
background-image: $form-icon-success;
}
} }
.has-warning { .form-check-inline {
@include form-control-validation($brand-warning); display: inline-flex;
align-items: center;
padding-left: 0; // Override base .form-check
margin-right: $form-check-inline-margin-x;
.form-control-warning { // Undo .form-check-input defaults and add some `margin-right`.
background-image: $form-icon-warning; .form-check-input {
position: static;
margin-top: 0;
margin-right: $form-check-inline-input-margin-x;
margin-left: 0;
} }
} }
.has-danger {
@include form-control-validation($brand-danger);
.form-control-danger { // Form validation
background-image: $form-icon-danger; //
} // Provide feedback to users when form field values are valid or invalid. Works
} // primarily for client-side validation via scoped `:invalid` and `:valid`
// pseudo-classes but also includes `.is-invalid` and `.is-valid` classes for
// server side validation.
@include form-validation-state("valid", $form-feedback-valid-color);
@include form-validation-state("invalid", $form-feedback-invalid-color);
// Inline forms // Inline forms
// //
@ -297,14 +261,33 @@ select.form-control-lg {
// default HTML form controls and our custom form controls (e.g., input groups). // default HTML form controls and our custom form controls (e.g., input groups).
.form-inline { .form-inline {
display: flex;
flex-flow: row wrap;
align-items: center; // Prevent shorter elements from growing to same height as others (e.g., small buttons growing to normal sized button height)
// Because we use flex, the initial sizing of checkboxes is collapsed and
// doesn't occupy the full-width (which is what we want for xs grid tier),
// so we force that here.
.form-check {
width: 100%;
}
// Kick in the inline // Kick in the inline
@include media-breakpoint-up(sm) { @include media-breakpoint-up(sm) {
label {
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 0;
}
// Inline-block all the things for "inline" // Inline-block all the things for "inline"
.form-group { .form-group {
display: inline-block; display: flex;
flex: 0 0 auto;
flex-flow: row wrap;
align-items: center;
margin-bottom: 0; margin-bottom: 0;
vertical-align: middle;
} }
// Allow folks to *not* use `.form-group` // Allow folks to *not* use `.form-group`
@ -315,50 +298,36 @@ select.form-control-lg {
} }
// Make static controls behave like regular ones // Make static controls behave like regular ones
.form-control-static { .form-control-plaintext {
display: inline-block; display: inline-block;
} }
.input-group { .input-group {
display: inline-table; width: auto;
vertical-align: middle;
.input-group-addon,
.input-group-btn,
.form-control {
width: auto;
}
}
// Input groups need that 100% width though
.input-group > .form-control {
width: 100%;
}
.form-control-label {
margin-bottom: 0;
vertical-align: middle;
} }
// Remove default margin on radios/checkboxes that were used for stacking, and // Remove default margin on radios/checkboxes that were used for stacking, and
// then undo the floating of radios and checkboxes to match. // then undo the floating of radios and checkboxes to match.
.form-check { .form-check {
display: inline-block; display: flex;
margin-top: 0; align-items: center;
margin-bottom: 0; justify-content: center;
vertical-align: middle; width: auto;
}
.form-check-label {
padding-left: 0; padding-left: 0;
} }
.form-check-input { .form-check-input {
position: relative; position: relative;
margin-top: 0;
margin-right: $form-check-input-margin-x;
margin-left: 0; margin-left: 0;
} }
// Re-override the feedback icon. .custom-control {
.has-feedback .form-control-feedback { align-items: center;
top: 0; justify-content: center;
}
.custom-control-label {
margin-bottom: 0;
} }
} }
} }

86
scss/bootstrap/_functions.scss

@ -0,0 +1,86 @@
// Bootstrap functions
//
// Utility mixins and functions for evalutating source code across our variables, maps, and mixins.
// Ascending
// Used to evaluate Sass maps like our grid breakpoints.
@mixin _assert-ascending($map, $map-name) {
$prev-key: null;
$prev-num: null;
@each $key, $num in $map {
@if $prev-num == null {
// Do nothing
} @else if not comparable($prev-num, $num) {
@warn "Potentially invalid value for #{$map-name}: This map must be in ascending order, but key '#{$key}' has value #{$num} whose unit makes it incomparable to #{$prev-num}, the value of the previous key '#{$prev-key}' !";
} @else if $prev-num >= $num {
@warn "Invalid value for #{$map-name}: This map must be in ascending order, but key '#{$key}' has value #{$num} which isn't greater than #{$prev-num}, the value of the previous key '#{$prev-key}' !";
}
$prev-key: $key;
$prev-num: $num;
}
}
// Starts at zero
// Another grid mixin that ensures the min-width of the lowest breakpoint starts at 0.
@mixin _assert-starts-at-zero($map) {
$values: map-values($map);
$first-value: nth($values, 1);
@if $first-value != 0 {
@warn "First breakpoint in `$grid-breakpoints` must start at 0, but starts at #{$first-value}.";
}
}
// Replace `$search` with `$replace` in `$string`
// Used on our SVG icon backgrounds for custom forms.
//
// @author Hugo Giraudel
// @param {String} $string - Initial string
// @param {String} $search - Substring to replace
// @param {String} $replace ('') - New value
// @return {String} - Updated string
@function str-replace($string, $search, $replace: "") {
$index: str-index($string, $search);
@if $index {
@return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace);
}
@return $string;
}
// Color contrast
@function color-yiq($color) {
$r: red($color);
$g: green($color);
$b: blue($color);
$yiq: (($r * 299) + ($g * 587) + ($b * 114)) / 1000;
@if ($yiq >= $yiq-contrasted-threshold) {
@return $yiq-text-dark;
} @else {
@return $yiq-text-light;
}
}
// Retrieve color Sass maps
@function color($key: "blue") {
@return map-get($colors, $key);
}
@function theme-color($key: "primary") {
@return map-get($theme-colors, $key);
}
@function gray($key: "100") {
@return map-get($grays, $key);
}
// Request a theme color level
@function theme-color-level($color-name: "primary", $level: 0) {
$color: theme-color($color-name);
$color-base: if($level > 0, #000, #fff);
$level: abs($level);
@return mix($color-base, $color, $level * $theme-color-interval);
}

17
scss/bootstrap/_grid.scss

@ -11,8 +11,8 @@
// Fluid container // Fluid container
// //
// Utilizes the mixin meant for fixed width containers, but without any defined // Utilizes the mixin meant for fixed width containers, but with 100% width for
// width for fluid, full width layouts. // fluid, full width layouts.
@if $enable-grid-classes { @if $enable-grid-classes {
.container-fluid { .container-fluid {
@ -28,6 +28,19 @@
.row { .row {
@include make-row(); @include make-row();
} }
// Remove the negative margin from default .row, then the horizontal padding
// from all immediate children columns (to prevent runaway style inheritance).
.no-gutters {
margin-right: 0;
margin-left: 0;
> .col,
> [class*="col-"] {
padding-right: 0;
padding-left: 0;
}
}
} }
// Columns // Columns

18
scss/bootstrap/_images.scss

@ -6,13 +6,9 @@
// which weren't expecting the images within themselves to be involuntarily resized. // which weren't expecting the images within themselves to be involuntarily resized.
// See also https://github.com/twbs/bootstrap/issues/18178 // See also https://github.com/twbs/bootstrap/issues/18178
.img-fluid { .img-fluid {
@include img-fluid(); @include img-fluid;
} }
// Rounded corners
.img-rounded {
@include border-radius($border-radius-lg);
}
// Image thumbnails // Image thumbnails
.img-thumbnail { .img-thumbnail {
@ -20,16 +16,10 @@
background-color: $thumbnail-bg; background-color: $thumbnail-bg;
border: $thumbnail-border-width solid $thumbnail-border-color; border: $thumbnail-border-width solid $thumbnail-border-color;
@include border-radius($thumbnail-border-radius); @include border-radius($thumbnail-border-radius);
transition: all .2s ease-in-out;
@include box-shadow($thumbnail-box-shadow); @include box-shadow($thumbnail-box-shadow);
// Keep them at most 100% wide // Keep them at most 100% wide
@include img-fluid(inline-block); @include img-fluid;
}
// Perfect circle
.img-circle {
border-radius: 50%;
} }
// //
@ -42,11 +32,11 @@
} }
.figure-img { .figure-img {
margin-bottom: ($spacer-y / 2); margin-bottom: ($spacer / 2);
line-height: 1; line-height: 1;
} }
.figure-caption { .figure-caption {
font-size: $figure-caption-font-size; font-size: $figure-caption-font-size;
color: $gray-light; color: $figure-caption-color;
} }

241
scss/bootstrap/_input-group.scss

@ -1,188 +1,159 @@
// stylelint-disable selector-no-qualifying-type
// //
// Base styles // Base styles
// //
.input-group { .input-group {
position: relative; position: relative;
display: flex;
flex-wrap: wrap; // For form validation feedback
align-items: stretch;
width: 100%; width: 100%;
@if $enable-flex { > .form-control,
display: flex; > .custom-select,
} @else { > .custom-file {
display: table; position: relative; // For focus state's z-index
// Prevent input groups from inheriting border styles from table cells when flex: 1 1 auto;
// placed within a table. // Add width 1% and flex-basis auto to ensure that button will not wrap out
border-collapse: separate; // the column. Applies to IE Edge+ and Firefox. Chrome does not require this.
} width: 1%;
margin-bottom: 0;
.form-control { // Bring the "active" form control to the top of surrounding elements
// Ensure that the input is always above the *appended* addon button for &:focus {
// proper border colors.
position: relative;
z-index: 2;
// Bring the "active" form control to the front
@include hover-focus-active {
z-index: 3; z-index: 3;
} }
@if $enable-flex {
flex: 1; + .form-control,
} @else { + .custom-select,
// IE9 fubars the placeholder attribute in text inputs and the arrows on + .custom-file {
// select elements in input groups. To fix it, we float the input. Details: margin-left: -$input-border-width;
// https://github.com/twbs/bootstrap/issues/11561#issuecomment-28936855
float: left;
width: 100%;
} }
margin-bottom: 0;
} }
}
.input-group-addon, > .form-control,
.input-group-btn, > .custom-select {
.input-group .form-control { &:not(:last-child) { @include border-right-radius(0); }
@if not $enable-flex { &:not(:first-child) { @include border-left-radius(0); }
display: table-cell;
} }
&:not(:first-child):not(:last-child) { // Custom file inputs have more complex markup, thus requiring different
@include border-radius(0); // border-radius overrides.
} > .custom-file {
} display: flex;
align-items: center;
.input-group-addon, &:not(:last-child) .custom-file-label,
.input-group-btn { &:not(:last-child) .custom-file-label::before { @include border-right-radius(0); }
@if not $enable-flex { &:not(:first-child) .custom-file-label,
width: 1%; &:not(:first-child) .custom-file-label::before { @include border-left-radius(0); }
} }
white-space: nowrap;
vertical-align: middle; // Match the inputs
} }
// Sizing options // Prepend and append
// //
// Remix the default form control sizing classes into new ones for easier // While it requires one extra layer of HTML for each, dedicated prepend and
// manipulation. // append elements allow us to 1) be less clever, 2) simplify our selectors, and
// 3) support HTML5 form validation.
.input-group-prepend,
.input-group-append {
display: flex;
// Ensure buttons are always above inputs for more visually pleasing borders.
// This isn't needed for `.input-group-text` since it shares the same border-color
// as our inputs.
.btn {
position: relative;
z-index: 2;
}
.input-group-lg > .form-control, .btn + .btn,
.input-group-lg > .input-group-addon, .btn + .input-group-text,
.input-group-lg > .input-group-btn > .btn { .input-group-text + .input-group-text,
@extend .form-control-lg; .input-group-text + .btn {
} margin-left: -$input-border-width;
.input-group-sm > .form-control, }
.input-group-sm > .input-group-addon,
.input-group-sm > .input-group-btn > .btn {
@extend .form-control-sm;
} }
.input-group-prepend { margin-right: -$input-border-width; }
.input-group-append { margin-left: -$input-border-width; }
// Textual addons
// //
// Text input groups // Serves as a catch-all element for any text or radio/checkbox input you wish
// // to prepend or append to an input.
.input-group-addon { .input-group-text {
display: flex;
align-items: center;
padding: $input-padding-y $input-padding-x; padding: $input-padding-y $input-padding-x;
margin-bottom: 0; // Allow use of <label> elements by overriding our default margin-bottom margin-bottom: 0; // Allow use of <label> elements by overriding our default margin-bottom
font-size: $font-size-base; font-size: $font-size-base; // Match inputs
font-weight: normal; font-weight: $font-weight-normal;
line-height: $input-line-height; line-height: $input-line-height;
color: $input-color; color: $input-group-addon-color;
text-align: center; text-align: center;
white-space: nowrap;
background-color: $input-group-addon-bg; background-color: $input-group-addon-bg;
border: $input-btn-border-width solid $input-group-addon-border-color; border: $input-border-width solid $input-group-addon-border-color;
@include border-radius($input-border-radius); @include border-radius($input-border-radius);
// Sizing
&.form-control-sm {
padding: $input-padding-y-sm $input-padding-x-sm;
font-size: $font-size-sm;
@include border-radius($input-border-radius-sm);
}
&.form-control-lg {
padding: $input-padding-y-lg $input-padding-x-lg;
font-size: $font-size-lg;
@include border-radius($input-border-radius-lg);
}
// scss-lint:disable QualifyingElement
// Nuke default margins from checkboxes and radios to vertically center within. // Nuke default margins from checkboxes and radios to vertically center within.
input[type="radio"], input[type="radio"],
input[type="checkbox"] { input[type="checkbox"] {
margin-top: 0; margin-top: 0;
} }
// scss-lint:enable QualifyingElement
} }
// Sizing
// //
// Reset rounded corners // Remix the default form control sizing classes into new ones for easier
// // manipulation.
.input-group .form-control:not(:last-child), .input-group-lg > .form-control,
.input-group-addon:not(:last-child), .input-group-lg > .input-group-prepend > .input-group-text,
.input-group-btn:not(:last-child) > .btn, .input-group-lg > .input-group-append > .input-group-text,
.input-group-btn:not(:last-child) > .btn-group > .btn, .input-group-lg > .input-group-prepend > .btn,
.input-group-btn:not(:last-child) > .dropdown-toggle, .input-group-lg > .input-group-append > .btn {
.input-group-btn:not(:first-child) > .btn:not(:last-child):not(.dropdown-toggle), @extend .form-control-lg;
.input-group-btn:not(:first-child) > .btn-group:not(:last-child) > .btn {
@include border-right-radius(0);
}
.input-group-addon:not(:last-child) {
border-right: 0;
}
.input-group .form-control:not(:first-child),
.input-group-addon:not(:first-child),
.input-group-btn:not(:first-child) > .btn,
.input-group-btn:not(:first-child) > .btn-group > .btn,
.input-group-btn:not(:first-child) > .dropdown-toggle,
.input-group-btn:not(:last-child) > .btn:not(:first-child),
.input-group-btn:not(:last-child) > .btn-group:not(:first-child) > .btn {
@include border-left-radius(0);
} }
.form-control + .input-group-addon:not(:first-child) {
border-left: 0; .input-group-sm > .form-control,
.input-group-sm > .input-group-prepend > .input-group-text,
.input-group-sm > .input-group-append > .input-group-text,
.input-group-sm > .input-group-prepend > .btn,
.input-group-sm > .input-group-append > .btn {
@extend .form-control-sm;
} }
// Prepend and append rounded corners
// //
// Button input groups // These rulesets must come after the sizing ones to properly override sm and lg
// // border-radius values when extending. They're more specific than we'd like
// with the `.input-group >` part, but without it, we cannot override the sizing.
.input-group-btn {
position: relative;
// Jankily prevent input button groups from wrapping with `white-space` and
// `font-size` in combination with `inline-block` on buttons.
font-size: 0;
white-space: nowrap;
// Negative margin for spacing, position for bringing hovered/focused/actived .input-group > .input-group-prepend > .btn,
// element above the siblings. .input-group > .input-group-prepend > .input-group-text,
> .btn { .input-group > .input-group-append:not(:last-child) > .btn,
position: relative; .input-group > .input-group-append:not(:last-child) > .input-group-text,
+ .btn { .input-group > .input-group-append:last-child > .btn:not(:last-child):not(.dropdown-toggle),
margin-left: (-$input-btn-border-width); .input-group > .input-group-append:last-child > .input-group-text:not(:last-child) {
} @include border-right-radius(0);
// Bring the "active" button to the front }
@include hover-focus-active {
z-index: 3;
}
}
// Negative margin to only have a single, shared border between the two .input-group > .input-group-append > .btn,
&:not(:last-child) { .input-group > .input-group-append > .input-group-text,
> .btn, .input-group > .input-group-prepend:not(:first-child) > .btn,
> .btn-group { .input-group > .input-group-prepend:not(:first-child) > .input-group-text,
margin-right: (-$input-btn-border-width); .input-group > .input-group-prepend:first-child > .btn:not(:first-child),
} .input-group > .input-group-prepend:first-child > .input-group-text:not(:first-child) {
} @include border-left-radius(0);
&:not(:first-child) {
> .btn,
> .btn-group {
z-index: 2;
margin-left: (-$input-btn-border-width);
// Because specificity
@include hover-focus-active {
z-index: 3;
}
}
}
} }

4
scss/bootstrap/_jumbotron.scss

@ -9,10 +9,6 @@
} }
} }
.jumbotron-hr {
border-top-color: darken($jumbotron-bg, 10%);
}
.jumbotron-fluid { .jumbotron-fluid {
padding-right: 0; padding-right: 0;
padding-left: 0; padding-left: 0;

126
scss/bootstrap/_list-group.scss

@ -3,12 +3,39 @@
// Easily usable on <ul>, <ol>, or <div>. // Easily usable on <ul>, <ol>, or <div>.
.list-group { .list-group {
display: flex;
flex-direction: column;
// No need to set list-style: none; since .list-group-item is block level // No need to set list-style: none; since .list-group-item is block level
padding-left: 0; // reset padding because ul and ol padding-left: 0; // reset padding because ul and ol
margin-bottom: 0; margin-bottom: 0;
} }
// Interactive list items
//
// Use anchor or button elements instead of `li`s or `div`s to create interactive
// list items. Includes an extra `.active` modifier class for selected items.
.list-group-item-action {
width: 100%; // For `<button>`s (anchors become 100% by default though)
color: $list-group-action-color;
text-align: inherit; // For `<button>`s (anchors inherit)
// Hover state
@include hover-focus {
color: $list-group-action-hover-color;
text-decoration: none;
background-color: $list-group-hover-bg;
}
&:active {
color: $list-group-action-active-color;
background-color: $list-group-action-active-bg;
}
}
// Individual list items // Individual list items
// //
// Use on `li`s or `div`s within the `.list-group` parent. // Use on `li`s or `div`s within the `.list-group` parent.
@ -31,69 +58,49 @@
@include border-bottom-radius($list-group-border-radius); @include border-bottom-radius($list-group-border-radius);
} }
&.disabled { @include hover-focus {
@include plain-hover-focus { z-index: 1; // Place hover/active items above their siblings for proper border styling
color: $list-group-disabled-color; text-decoration: none;
cursor: $cursor-disabled;
background-color: $list-group-disabled-bg;
// Force color to inherit for custom content
.list-group-item-heading {
color: inherit;
}
.list-group-item-text {
color: $list-group-disabled-text-color;
}
}
} }
&.active { &.disabled,
@include plain-hover-focus { &:disabled {
z-index: 2; // Place active items above their siblings for proper border styling color: $list-group-disabled-color;
color: $list-group-active-color; background-color: $list-group-disabled-bg;
text-decoration: none; // Repeat here because it inherits global a:hover otherwise
background-color: $list-group-active-bg;
border-color: $list-group-active-border;
// Force color to inherit for custom content
.list-group-item-heading,
.list-group-item-heading > small,
.list-group-item-heading > .small {
color: inherit;
}
.list-group-item-text {
color: $list-group-active-text-color;
}
}
} }
}
.list-group-flush { // Include both here for `<a>`s and `<button>`s
.list-group-item { &.active {
border-radius: 0; z-index: 2; // Place active items above their siblings for proper border styling
color: $list-group-active-color;
background-color: $list-group-active-bg;
border-color: $list-group-active-border-color;
} }
} }
// Interactive list items // Flush list items
// //
// Use anchor or button elements instead of `li`s or `div`s to create interactive // Remove borders and border-radius to keep list group items edge-to-edge. Most
// list items. Includes an extra `.active` modifier class for selected items. // useful within other components (e.g., cards).
.list-group-item-action { .list-group-flush {
width: 100%; // For `<button>`s (anchors become 100% by default though) .list-group-item {
color: $list-group-link-color; border-right: 0;
text-align: inherit; // For `<button>`s (anchors inherit) border-left: 0;
@include border-radius(0);
}
.list-group-item-heading { &:first-child {
color: $list-group-link-heading-color; .list-group-item:first-child {
border-top: 0;
}
} }
// Hover state &:last-child {
@include hover-focus { .list-group-item:last-child {
color: $list-group-link-hover-color; border-bottom: 0;
text-decoration: none; }
background-color: $list-group-hover-bg;
} }
} }
@ -103,21 +110,6 @@
// Add modifier classes to change text and background color on individual items. // Add modifier classes to change text and background color on individual items.
// Organizationally, this must come after the `:hover` states. // Organizationally, this must come after the `:hover` states.
@include list-group-item-variant(success, $state-success-bg, $state-success-text); @each $color, $value in $theme-colors {
@include list-group-item-variant(info, $state-info-bg, $state-info-text); @include list-group-item-variant($color, theme-color-level($color, -9), theme-color-level($color, 6));
@include list-group-item-variant(warning, $state-warning-bg, $state-warning-text);
@include list-group-item-variant(danger, $state-danger-bg, $state-danger-text);
// Custom content options
//
// Extra classes for creating well-formatted content within `.list-group-item`s.
.list-group-item-heading {
margin-top: 0;
margin-bottom: $list-group-item-heading-margin-bottom;
}
.list-group-item-text {
margin-bottom: 0;
line-height: 1.3;
} }

83
scss/bootstrap/_media.scss

@ -1,81 +1,8 @@
@if $enable-flex { .media {
.media { display: flex;
display: flex; align-items: flex-start;
}
.media-body {
flex: 1;
}
.media-middle {
align-self: center;
}
.media-bottom {
align-self: flex-end;
}
} @else {
.media,
.media-body {
overflow: hidden;
}
.media-body {
width: 10000px;
}
.media-left,
.media-right,
.media-body {
display: table-cell;
vertical-align: top;
}
.media-middle {
vertical-align: middle;
}
.media-bottom {
vertical-align: bottom;
}
} }
.media-body {
// flex: 1;
// Images/elements as the media anchor
//
.media-object {
display: block;
// Fix collapse in webkit from max-width: 100% and display: table-cell.
&.img-thumbnail {
max-width: none;
}
}
//
// Alignment
//
.media-right {
padding-left: $media-alignment-padding-x;
}
.media-left {
padding-right: $media-alignment-padding-x;
}
//
// Headings
//
.media-heading {
margin-top: 0;
margin-bottom: $media-heading-margin-bottom;
}
//
// Media list variation
//
.media-list {
padding-left: 0;
list-style: none;
} }

24
scss/bootstrap/_mixins.scss

@ -2,53 +2,41 @@
// //
// Used in conjunction with global variables to enable certain theme features. // Used in conjunction with global variables to enable certain theme features.
@mixin box-shadow($shadow...) {
@if $enable-shadows {
box-shadow: $shadow;
}
}
@mixin transition($transition...) {
@if $enable-transitions {
transition: $transition;
}
}
// Utilities // Utilities
@import "mixins/breakpoints"; @import "mixins/breakpoints";
@import "mixins/hover"; @import "mixins/hover";
@import "mixins/image"; @import "mixins/image";
@import "mixins/tag"; @import "mixins/badge";
@import "mixins/reset-filter";
@import "mixins/resize"; @import "mixins/resize";
@import "mixins/screen-reader"; @import "mixins/screen-reader";
@import "mixins/size"; @import "mixins/size";
@import "mixins/tab-focus";
@import "mixins/reset-text"; @import "mixins/reset-text";
@import "mixins/text-emphasis"; @import "mixins/text-emphasis";
@import "mixins/text-hide"; @import "mixins/text-hide";
@import "mixins/text-truncate"; @import "mixins/text-truncate";
@import "mixins/visibility";
// // Components // // Components
@import "mixins/alert"; @import "mixins/alert";
@import "mixins/buttons"; @import "mixins/buttons";
@import "mixins/cards"; @import "mixins/caret";
@import "mixins/pagination"; @import "mixins/pagination";
@import "mixins/lists"; @import "mixins/lists";
@import "mixins/list-group"; @import "mixins/list-group";
@import "mixins/nav-divider"; @import "mixins/nav-divider";
@import "mixins/forms"; @import "mixins/forms";
@import "mixins/progress";
@import "mixins/table-row"; @import "mixins/table-row";
// // Skins // // Skins
@import "mixins/background-variant"; @import "mixins/background-variant";
@import "mixins/border-radius"; @import "mixins/border-radius";
@import "mixins/box-shadow";
@import "mixins/gradients"; @import "mixins/gradients";
@import "mixins/transition";
// // Layout // // Layout
@import "mixins/clearfix"; @import "mixins/clearfix";
// @import "mixins/navbar-align"; // @import "mixins/navbar-align";
@import "mixins/grid-framework"; @import "mixins/grid-framework";
@import "mixins/grid"; @import "mixins/grid";
@import "mixins/pulls"; @import "mixins/float";

86
scss/bootstrap/_modal.scss

@ -1,7 +1,7 @@
// .modal-open - body class for killing the scroll // .modal-open - body class for killing the scroll
// .modal - container to scroll within // .modal - container to scroll within
// .modal-dialog - positioning shell for the actual modal // .modal-dialog - positioning shell for the actual modal
// .modal-content - actual modal w/ bg and corners and shit // .modal-content - actual modal w/ bg and corners and stuff
// Kill the scroll on the body // Kill the scroll on the body
@ -22,18 +22,14 @@
// Prevent Chrome on Windows from adding a focus outline. For details, see // Prevent Chrome on Windows from adding a focus outline. For details, see
// https://github.com/twbs/bootstrap/pull/10951. // https://github.com/twbs/bootstrap/pull/10951.
outline: 0; outline: 0;
-webkit-overflow-scrolling: touch; // We deliberately don't use `-webkit-overflow-scrolling: touch;` due to a
// gnarly iOS Safari bug: https://bugs.webkit.org/show_bug.cgi?id=158342
// See also https://github.com/twbs/bootstrap/issues/17695
// When fading in the modal, animate it to slide down .modal-open & {
&.fade .modal-dialog { overflow-x: hidden;
transition: transform .3s ease-out; overflow-y: auto;
transform: translate(0, -25%);
} }
&.in .modal-dialog { transform: translate(0, 0); }
}
.modal-open .modal {
overflow-x: hidden;
overflow-y: auto;
} }
// Shell div to position the modal with bottom padding // Shell div to position the modal with bottom padding
@ -41,16 +37,38 @@
position: relative; position: relative;
width: auto; width: auto;
margin: $modal-dialog-margin; margin: $modal-dialog-margin;
// allow clicks to pass through for custom click handling to close modal
pointer-events: none;
// When fading in the modal, animate it to slide down
.modal.fade & {
@include transition($modal-transition);
transform: translate(0, -25%);
}
.modal.show & {
transform: translate(0, 0);
}
}
.modal-dialog-centered {
display: flex;
align-items: center;
min-height: calc(100% - (#{$modal-dialog-margin} * 2));
} }
// Actual modal // Actual modal
.modal-content { .modal-content {
position: relative; position: relative;
display: flex;
flex-direction: column;
width: 100%; // Ensure `.modal-content` extends the full width of the parent `.modal-dialog`
// counteract the pointer-events: none; in the .modal-dialog
pointer-events: auto;
background-color: $modal-content-bg; background-color: $modal-content-bg;
background-clip: padding-box; background-clip: padding-box;
border: $modal-content-border-width solid $modal-content-border-color; border: $modal-content-border-width solid $modal-content-border-color;
@include border-radius($border-radius-lg); @include border-radius($border-radius-lg);
@include box-shadow($modal-content-xs-box-shadow); @include box-shadow($modal-content-box-shadow-xs);
// Remove focus outline from opened modal // Remove focus outline from opened modal
outline: 0; outline: 0;
} }
@ -62,29 +80,34 @@
right: 0; right: 0;
bottom: 0; bottom: 0;
left: 0; left: 0;
z-index: $zindex-modal-bg; z-index: $zindex-modal-backdrop;
background-color: $modal-backdrop-bg; background-color: $modal-backdrop-bg;
// Fade for backdrop // Fade for backdrop
&.fade { opacity: 0; } &.fade { opacity: 0; }
&.in { opacity: $modal-backdrop-opacity; } &.show { opacity: $modal-backdrop-opacity; }
} }
// Modal header // Modal header
// Top section of the modal w/ title and dismiss // Top section of the modal w/ title and dismiss
.modal-header { .modal-header {
padding: $modal-title-padding; display: flex;
align-items: flex-start; // so the close btn always stays on the upper right corner
justify-content: space-between; // Put modal header elements (title and dismiss) on opposite ends
padding: $modal-header-padding;
border-bottom: $modal-header-border-width solid $modal-header-border-color; border-bottom: $modal-header-border-width solid $modal-header-border-color;
@include clearfix; @include border-top-radius($border-radius-lg);
}
// Close icon .close {
.modal-header .close { padding: $modal-header-padding;
margin-top: -2px; // auto on the left force icon to the right even when there is no .modal-title
margin: (-$modal-header-padding) (-$modal-header-padding) (-$modal-header-padding) auto;
}
} }
// Title text within header // Title text within header
.modal-title { .modal-title {
margin: 0; margin-bottom: 0;
line-height: $modal-title-line-height; line-height: $modal-title-line-height;
} }
@ -92,15 +115,23 @@
// Where all modal content resides (sibling of .modal-header and .modal-footer) // Where all modal content resides (sibling of .modal-header and .modal-footer)
.modal-body { .modal-body {
position: relative; position: relative;
// Enable `flex-grow: 1` so that the body take up as much space as possible
// when should there be a fixed height on `.modal-dialog`.
flex: 1 1 auto;
padding: $modal-inner-padding; padding: $modal-inner-padding;
} }
// Footer (for actions) // Footer (for actions)
.modal-footer { .modal-footer {
display: flex;
align-items: center; // vertically center
justify-content: flex-end; // Right align buttons with flex property because text-align doesn't work on flex items
padding: $modal-inner-padding; padding: $modal-inner-padding;
text-align: right; // right align buttons
border-top: $modal-footer-border-width solid $modal-footer-border-color; border-top: $modal-footer-border-width solid $modal-footer-border-color;
@include clearfix(); // clear it in case folks use .pull-* classes on buttons
// Easily place margin between footer elements
> :not(:first-child) { margin-left: .25rem; }
> :not(:last-child) { margin-right: .25rem; }
} }
// Measure scrollbar width for padding body during modal show/hide // Measure scrollbar width for padding body during modal show/hide
@ -117,14 +148,19 @@
// Automatically set modal's width for larger viewports // Automatically set modal's width for larger viewports
.modal-dialog { .modal-dialog {
max-width: $modal-md; max-width: $modal-md;
margin: $modal-dialog-sm-up-margin-y auto; margin: $modal-dialog-margin-y-sm-up auto;
}
.modal-dialog-centered {
min-height: calc(100% - (#{$modal-dialog-margin-y-sm-up} * 2));
} }
.modal-content { .modal-content {
@include box-shadow($modal-content-sm-up-box-shadow); @include box-shadow($modal-content-box-shadow-sm-up);
} }
.modal-sm { max-width: $modal-sm; } .modal-sm { max-width: $modal-sm; }
} }
@include media-breakpoint-up(lg) { @include media-breakpoint-up(lg) {

105
scss/bootstrap/_nav.scss

@ -4,89 +4,58 @@
// `<nav>`s or `<ul>`s. // `<nav>`s or `<ul>`s.
.nav { .nav {
display: flex;
flex-wrap: wrap;
padding-left: 0; padding-left: 0;
margin-bottom: 0; margin-bottom: 0;
list-style: none; list-style: none;
} }
.nav-link { .nav-link {
display: inline-block; display: block;
padding: $nav-link-padding-y $nav-link-padding-x;
@include hover-focus { @include hover-focus {
text-decoration: none; text-decoration: none;
} }
// Disabled state lightens text and removes hover/tab effects // Disabled state lightens text
&.disabled { &.disabled {
color: $nav-disabled-link-color; color: $nav-link-disabled-color;
@include plain-hover-focus {
color: $nav-disabled-link-hover-color;
cursor: $cursor-disabled;
background-color: $nav-disabled-link-hover-bg;
}
}
}
// Nav inline
.nav-inline {
.nav-item {
display: inline-block;
}
.nav-item + .nav-item,
.nav-link + .nav-link {
margin-left: $nav-item-inline-spacer;
} }
} }
// //
// Tabs // Tabs
// //
.nav-tabs { .nav-tabs {
border-bottom: $nav-tabs-border-width solid $nav-tabs-border-color; border-bottom: $nav-tabs-border-width solid $nav-tabs-border-color;
@include clearfix();
.nav-item { .nav-item {
float: left;
// Make the list-items overlay the bottom border
margin-bottom: -$nav-tabs-border-width; margin-bottom: -$nav-tabs-border-width;
+ .nav-item {
margin-left: $nav-item-margin;
}
} }
.nav-link { .nav-link {
display: block;
padding: $nav-link-padding;
border: $nav-tabs-border-width solid transparent; border: $nav-tabs-border-width solid transparent;
@include border-top-radius($nav-tabs-border-radius); @include border-top-radius($nav-tabs-border-radius);
@include hover-focus { @include hover-focus {
border-color: $nav-tabs-link-hover-border-color $nav-tabs-link-hover-border-color $nav-tabs-border-color; border-color: $nav-tabs-link-hover-border-color;
} }
&.disabled { &.disabled {
@include plain-hover-focus { color: $nav-link-disabled-color;
color: $nav-disabled-link-color; background-color: transparent;
background-color: transparent; border-color: transparent;
border-color: transparent;
}
} }
} }
.nav-link.active, .nav-link.active,
.nav-item.open .nav-link { .nav-item.show .nav-link {
@include plain-hover-focus { color: $nav-tabs-link-active-color;
color: $nav-tabs-active-link-hover-color; background-color: $nav-tabs-link-active-bg;
background-color: $nav-tabs-active-link-hover-bg; border-color: $nav-tabs-link-active-border-color;
border-color: $nav-tabs-active-link-hover-border-color $nav-tabs-active-link-hover-border-color transparent;
}
} }
.dropdown-menu { .dropdown-menu {
@ -103,50 +72,42 @@
// //
.nav-pills { .nav-pills {
@include clearfix();
.nav-item {
float: left;
+ .nav-item {
margin-left: $nav-item-margin;
}
}
.nav-link { .nav-link {
display: block;
padding: $nav-link-padding;
@include border-radius($nav-pills-border-radius); @include border-radius($nav-pills-border-radius);
} }
.nav-link.active, .nav-link.active,
.nav-item.open .nav-link { .show > .nav-link {
@include plain-hover-focus { color: $nav-pills-link-active-color;
color: $nav-pills-active-link-color; background-color: $nav-pills-link-active-bg;
cursor: default;
background-color: $nav-pills-active-link-bg;
}
} }
} }
.nav-stacked {
//
// Justified variants
//
.nav-fill {
.nav-item { .nav-item {
display: block; flex: 1 1 auto;
float: none; text-align: center;
}
}
+ .nav-item { .nav-justified {
margin-top: $nav-item-margin; .nav-item {
margin-left: 0; flex-basis: 0;
} flex-grow: 1;
text-align: center;
} }
} }
//
// Tabbable tabs // Tabbable tabs
// //
// Hide tabbable panes to start, show them when `.active` // Hide tabbable panes to start, show them when `.active`
.tab-content { .tab-content {
> .tab-pane { > .tab-pane {
display: none; display: none;

351
scss/bootstrap/_navbar.scss

@ -1,146 +1,211 @@
// Wrapper and base class // Contents
//
// Navbar
// Navbar brand
// Navbar nav
// Navbar text
// Navbar divider
// Responsive navbar
// Navbar position
// Navbar themes
// Navbar
// //
// Provide a static navbar from which we expand to create full-width, fixed, and // Provide a static navbar from which we expand to create full-width, fixed, and
// other navbar variations. // other navbar variations.
.navbar { .navbar {
position: relative; position: relative;
display: flex;
flex-wrap: wrap; // allow us to do the line break for collapsing content
align-items: center;
justify-content: space-between; // space out brand from logo
padding: $navbar-padding-y $navbar-padding-x; padding: $navbar-padding-y $navbar-padding-x;
@include clearfix;
@include media-breakpoint-up(sm) { // Because flex properties aren't inherited, we need to redeclare these first
@include border-radius($navbar-border-radius); // few properities so that content nested within behave properly.
> .container,
> .container-fluid {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
} }
} }
// Navbar alignment options // Navbar brand
// //
// Display the navbar across the entirety of the page or fixed it to the top or // Used for brand, project, or site names.
// bottom of the page.
// A static, full width modifier with no rounded corners.
.navbar-full {
z-index: $zindex-navbar;
@include media-breakpoint-up(sm) {
@include border-radius(0);
}
}
// Fix the top/bottom navbars when screen real estate supports it
.navbar-fixed-top,
.navbar-fixed-bottom {
position: fixed;
right: 0;
left: 0;
z-index: $zindex-navbar-fixed;
// Undo the rounded corners
@include media-breakpoint-up(sm) {
@include border-radius(0);
}
}
.navbar-fixed-top { .navbar-brand {
top: 0; display: inline-block;
} padding-top: $navbar-brand-padding-y;
padding-bottom: $navbar-brand-padding-y;
.navbar-fixed-bottom { margin-right: $navbar-padding-x;
bottom: 0; font-size: $navbar-brand-font-size;
} line-height: inherit;
white-space: nowrap;
.navbar-sticky-top {
position: sticky;
top: 0;
z-index: $zindex-navbar-sticky;
width: 100%;
// Undo the rounded corners @include hover-focus {
@include media-breakpoint-up(sm) { text-decoration: none;
@include border-radius(0);
} }
} }
// Navbar nav
// //
// Brand/project name // Custom navbar navigation (doesn't require `.nav`, but does make use of `.nav-link`).
//
.navbar-brand { .navbar-nav {
float: left; display: flex;
padding-top: $navbar-brand-padding-y; flex-direction: column; // cannot use `inherit` to get the `.navbar`s value
padding-bottom: $navbar-brand-padding-y; padding-left: 0;
margin-right: 1rem; margin-bottom: 0;
font-size: $font-size-lg; list-style: none;
@include hover-focus { .nav-link {
text-decoration: none; padding-right: 0;
padding-left: 0;
} }
> img { .dropdown-menu {
display: block; position: static;
float: none;
} }
} }
.navbar-divider { // Navbar text
float: left; //
width: $border-width; //
padding-top: .425rem;
padding-bottom: .425rem;
margin-right: $navbar-padding-x;
margin-left: $navbar-padding-x;
overflow: hidden;
&::before { .navbar-text {
content: "\00a0"; display: inline-block;
} padding-top: $nav-link-padding-y;
padding-bottom: $nav-link-padding-y;
} }
// Navbar toggle // Responsive navbar
// //
// Custom button for toggling the `.navbar-collapse`, powered by the collapse // Custom styles for responsive collapsing and toggling of navbar contents.
// Bootstrap JavaScript plugin. // Powered by the collapse Bootstrap JavaScript plugin.
// When collapsed, prevent the toggleable navbar contents from appearing in
// the default flexbox row orienation. Requires the use of `flex-wrap: wrap`
// on the `.navbar` parent.
.navbar-collapse {
flex-basis: 100%;
flex-grow: 1;
// For always expanded or extra full navbars, ensure content aligns itself
// properly vertically. Can be easily overridden with flex utilities.
align-items: center;
}
// Button for toggling the navbar when in its collapsed state
.navbar-toggler { .navbar-toggler {
padding: .5rem .75rem; padding: $navbar-toggler-padding-y $navbar-toggler-padding-x;
font-size: $font-size-lg; font-size: $navbar-toggler-font-size;
line-height: 1; line-height: 1;
background: none; background-color: transparent; // remove default button style
border: $border-width solid transparent; border: $border-width solid transparent; // remove default button style
@include border-radius($btn-border-radius); @include border-radius($navbar-toggler-border-radius);
@include hover-focus { @include hover-focus {
text-decoration: none; text-decoration: none;
} }
}
// Navigation // Opinionated: add "hand" cursor to non-disabled .navbar-toggler elements
// &:not(:disabled):not(.disabled) {
// Custom navbar navigation built on the base `.nav` styles. cursor: pointer;
.navbar-nav {
.nav-item {
float: left;
} }
}
.nav-link { // Keep as a separate element so folks can easily override it with another icon
display: block; // or image file as needed.
padding-top: .425rem; .navbar-toggler-icon {
padding-bottom: .425rem; display: inline-block;
width: 1.5em;
height: 1.5em;
vertical-align: middle;
content: "";
background: no-repeat center center;
background-size: 100% 100%;
}
+ .nav-link { // Generate series of `.navbar-expand-*` responsive classes for configuring
margin-left: 1rem; // where your navbar collapses.
} .navbar-expand {
} @each $breakpoint in map-keys($grid-breakpoints) {
$next: breakpoint-next($breakpoint, $grid-breakpoints);
$infix: breakpoint-infix($next, $grid-breakpoints);
&#{$infix} {
@include media-breakpoint-down($breakpoint) {
> .container,
> .container-fluid {
padding-right: 0;
padding-left: 0;
}
}
.nav-item + .nav-item { @include media-breakpoint-up($next) {
margin-left: 1rem; flex-flow: row nowrap;
justify-content: flex-start;
.navbar-nav {
flex-direction: row;
.dropdown-menu {
position: absolute;
}
.dropdown-menu-right {
right: 0;
left: auto; // Reset the default from `.dropdown-menu`
}
.nav-link {
padding-right: $navbar-nav-link-padding-x;
padding-left: $navbar-nav-link-padding-x;
}
}
// For nesting containers, have to redeclare for alignment purposes
> .container,
> .container-fluid {
flex-wrap: nowrap;
}
.navbar-collapse {
display: flex !important; // stylelint-disable-line declaration-no-important
// Changes flex-bases to auto because of an IE10 bug
flex-basis: auto;
}
.navbar-toggler {
display: none;
}
.dropup {
.dropdown-menu {
top: auto;
bottom: 100%;
}
}
}
}
} }
} }
// Navbar themes
//
// Styles for switching between navbars with light or dark background.
// Dark links against a light background // Dark links against a light background
.navbar-light { .navbar-light {
.navbar-brand { .navbar-brand {
@ -158,20 +223,38 @@
@include hover-focus { @include hover-focus {
color: $navbar-light-hover-color; color: $navbar-light-hover-color;
} }
&.disabled {
color: $navbar-light-disabled-color;
}
} }
.open > .nav-link, .show > .nav-link,
.active > .nav-link, .active > .nav-link,
.nav-link.open, .nav-link.show,
.nav-link.active { .nav-link.active {
@include plain-hover-focus { color: $navbar-light-active-color;
color: $navbar-light-active-color;
}
} }
} }
.navbar-divider { .navbar-toggler {
background-color: rgba(0,0,0,.075); color: $navbar-light-color;
border-color: $navbar-light-toggler-border-color;
}
.navbar-toggler-icon {
background-image: $navbar-light-toggler-icon-bg;
}
.navbar-text {
color: $navbar-light-color;
a {
color: $navbar-light-active-color;
@include hover-focus {
color: $navbar-light-active-color;
}
}
} }
} }
@ -192,65 +275,37 @@
@include hover-focus { @include hover-focus {
color: $navbar-dark-hover-color; color: $navbar-dark-hover-color;
} }
&.disabled {
color: $navbar-dark-disabled-color;
}
} }
.open > .nav-link, .show > .nav-link,
.active > .nav-link, .active > .nav-link,
.nav-link.open, .nav-link.show,
.nav-link.active { .nav-link.active {
@include plain-hover-focus { color: $navbar-dark-active-color;
color: $navbar-dark-active-color;
}
} }
} }
.navbar-divider { .navbar-toggler {
background-color: rgba(255,255,255,.075); color: $navbar-dark-color;
border-color: $navbar-dark-toggler-border-color;
} }
}
// Navbar toggleable .navbar-toggler-icon {
// background-image: $navbar-dark-toggler-icon-bg;
// Custom override for collapse plugin in navbar.
.navbar-toggleable {
&-xs {
@include clearfix;
@include media-breakpoint-down(xs) {
.navbar-nav .nav-item {
float: none;
margin-left: 0;
}
}
@include media-breakpoint-up(sm) {
display: block !important;
}
} }
&-sm { .navbar-text {
@include clearfix; color: $navbar-dark-color;
@include media-breakpoint-down(sm) { a {
.navbar-nav .nav-item { color: $navbar-dark-active-color;
float: none;
margin-left: 0;
}
}
@include media-breakpoint-up(md) {
display: block !important;
}
}
&-md { @include hover-focus {
@include clearfix; color: $navbar-dark-active-color;
@include media-breakpoint-down(md) {
.navbar-nav .nav-item {
float: none;
margin-left: 0;
} }
} }
@include media-breakpoint-up(lg) {
display: block !important;
}
} }
} }

78
scss/bootstrap/_pagination.scss

@ -1,14 +1,39 @@
.pagination { .pagination {
display: inline-block; display: flex;
padding-left: 0; @include list-unstyled();
margin-top: $spacer-y;
margin-bottom: $spacer-y;
@include border-radius(); @include border-radius();
} }
.page-item { .page-link {
display: inline; // Remove list-style and block-level defaults position: relative;
display: block;
padding: $pagination-padding-y $pagination-padding-x;
margin-left: -$pagination-border-width;
line-height: $pagination-line-height;
color: $pagination-color;
background-color: $pagination-bg;
border: $pagination-border-width solid $pagination-border-color;
&:hover {
color: $pagination-hover-color;
text-decoration: none;
background-color: $pagination-hover-bg;
border-color: $pagination-hover-border-color;
}
&:focus {
z-index: 2;
outline: 0;
box-shadow: $pagination-focus-box-shadow;
}
// Opinionated: add "hand" cursor to non-disabled .page-link elements
&:not(:disabled):not(.disabled) {
cursor: pointer;
}
}
.page-item {
&:first-child { &:first-child {
.page-link { .page-link {
margin-left: 0; margin-left: 0;
@ -22,40 +47,19 @@
} }
&.active .page-link { &.active .page-link {
@include plain-hover-focus { z-index: 1;
z-index: 2; color: $pagination-active-color;
color: $pagination-active-color; background-color: $pagination-active-bg;
cursor: default; border-color: $pagination-active-border-color;
background-color: $pagination-active-bg;
border-color: $pagination-active-border;
}
} }
&.disabled .page-link { &.disabled .page-link {
@include plain-hover-focus { color: $pagination-disabled-color;
color: $pagination-disabled-color; pointer-events: none;
pointer-events: none; // Opinionated: remove the "hand" cursor set previously for .page-link
cursor: $cursor-disabled; cursor: auto;
background-color: $pagination-disabled-bg; background-color: $pagination-disabled-bg;
border-color: $pagination-disabled-border; border-color: $pagination-disabled-border-color;
}
}
}
.page-link {
position: relative;
float: left; // Collapse white-space
padding: $pagination-padding-y $pagination-padding-x;
margin-left: -1px;
color: $pagination-color;
text-decoration: none;
background-color: $pagination-bg;
border: $pagination-border-width solid $pagination-border-color;
@include hover-focus {
color: $pagination-hover-color;
background-color: $pagination-hover-bg;
border-color: $pagination-hover-border;
} }
} }

250
scss/bootstrap/_popover.scss

@ -6,144 +6,180 @@
z-index: $zindex-popover; z-index: $zindex-popover;
display: block; display: block;
max-width: $popover-max-width; max-width: $popover-max-width;
padding: $popover-inner-padding;
// Our parent element can be arbitrary since tooltips are by default inserted as a sibling of their target element. // Our parent element can be arbitrary since tooltips are by default inserted as a sibling of their target element.
// So reset our font and text properties to avoid inheriting weird values. // So reset our font and text properties to avoid inheriting weird values.
@include reset-text(); @include reset-text();
font-size: $font-size-sm; font-size: $popover-font-size;
// Allow breaking very long words so they don't overflow the popover's bounds // Allow breaking very long words so they don't overflow the popover's bounds
word-wrap: break-word; word-wrap: break-word;
background-color: $popover-bg; background-color: $popover-bg;
background-clip: padding-box; background-clip: padding-box;
border: $popover-border-width solid $popover-border-color; border: $popover-border-width solid $popover-border-color;
@include border-radius($border-radius-lg); @include border-radius($popover-border-radius);
@include box-shadow($popover-box-shadow); @include box-shadow($popover-box-shadow);
.arrow {
// Popover directions position: absolute;
display: block;
&.popover-top, width: $popover-arrow-width;
&.bs-tether-element-attached-bottom { height: $popover-arrow-height;
margin-top: -$popover-arrow-width; margin: 0 $border-radius-lg;
.popover-arrow { &::before,
bottom: -$popover-arrow-outer-width; &::after {
left: 50%; position: absolute;
margin-left: -$popover-arrow-outer-width; display: block;
border-top-color: $popover-arrow-outer-color; content: "";
border-bottom-width: 0; border-color: transparent;
&::after { border-style: solid;
bottom: 1px;
margin-left: -$popover-arrow-width;
content: "";
border-top-color: $popover-arrow-color;
border-bottom-width: 0;
}
} }
} }
}
&.popover-right, .bs-popover-top {
&.bs-tether-element-attached-left { margin-bottom: $popover-arrow-height;
margin-left: $popover-arrow-width;
.arrow {
.popover-arrow { bottom: calc((#{$popover-arrow-height} + #{$popover-border-width}) * -1);
top: 50%;
left: -$popover-arrow-outer-width;
margin-top: -$popover-arrow-outer-width;
border-right-color: $popover-arrow-outer-color;
border-left-width: 0;
&::after {
bottom: -$popover-arrow-width;
left: 1px;
content: "";
border-right-color: $popover-arrow-color;
border-left-width: 0;
}
}
} }
&.popover-bottom, .arrow::before,
&.bs-tether-element-attached-top { .arrow::after {
margin-top: $popover-arrow-width; border-width: $popover-arrow-height ($popover-arrow-width / 2) 0;
.popover-arrow {
top: -$popover-arrow-outer-width;
left: 50%;
margin-left: -$popover-arrow-outer-width;
border-top-width: 0;
border-bottom-color: $popover-arrow-outer-color;
&::after {
top: 1px;
margin-left: -$popover-arrow-width;
content: "";
border-top-width: 0;
border-bottom-color: $popover-arrow-color;
}
}
} }
&.popover-left, .arrow::before {
&.bs-tether-element-attached-right { bottom: 0;
margin-left: -$popover-arrow-width; border-top-color: $popover-arrow-outer-color;
}
.popover-arrow {
top: 50%; .arrow::after {
right: -$popover-arrow-outer-width; bottom: $popover-border-width;
margin-top: -$popover-arrow-outer-width; border-top-color: $popover-arrow-color;
border-right-width: 0;
border-left-color: $popover-arrow-outer-color;
&::after {
right: 1px;
bottom: -$popover-arrow-width;
content: "";
border-right-width: 0;
border-left-color: $popover-arrow-color;
}
}
} }
} }
.bs-popover-right {
margin-left: $popover-arrow-height;
// Offset the popover to account for the popover arrow .arrow {
.popover-title { left: calc((#{$popover-arrow-height} + #{$popover-border-width}) * -1);
padding: $popover-title-padding-y $popover-title-padding-x; width: $popover-arrow-height;
margin: 0; // reset heading margin height: $popover-arrow-width;
font-size: $font-size-base; margin: $border-radius-lg 0; // make sure the arrow does not touch the popover's rounded corners
background-color: $popover-title-bg; }
border-bottom: $popover-border-width solid darken($popover-title-bg, 5%);
$offset-border-width: ($border-width / $font-size-root);
@include border-radius(($border-radius-lg - $offset-border-width) ($border-radius-lg - $offset-border-width) 0 0);
&:empty { .arrow::before,
display: none; .arrow::after {
border-width: ($popover-arrow-width / 2) $popover-arrow-height ($popover-arrow-width / 2) 0;
}
.arrow::before {
left: 0;
border-right-color: $popover-arrow-outer-color;
} }
}
.popover-content { .arrow::after {
padding: $popover-content-padding-y $popover-content-padding-x; left: $popover-border-width;
border-right-color: $popover-arrow-color;
}
} }
.bs-popover-bottom {
margin-top: $popover-arrow-height;
// Arrows .arrow {
// top: calc((#{$popover-arrow-height} + #{$popover-border-width}) * -1);
// .popover-arrow is outer, .popover-arrow::after is inner }
.popover-arrow { .arrow::before,
&, .arrow::after {
&::after { border-width: 0 ($popover-arrow-width / 2) $popover-arrow-height ($popover-arrow-width / 2);
}
.arrow::before {
top: 0;
border-bottom-color: $popover-arrow-outer-color;
}
.arrow::after {
top: $popover-border-width;
border-bottom-color: $popover-arrow-color;
}
// This will remove the popover-header's border just below the arrow
.popover-header::before {
position: absolute; position: absolute;
top: 0;
left: 50%;
display: block; display: block;
width: 0; width: $popover-arrow-width;
height: 0; margin-left: ($popover-arrow-width / -2);
border-color: transparent; content: "";
border-style: solid; border-bottom: $popover-border-width solid $popover-header-bg;
} }
} }
.popover-arrow {
border-width: $popover-arrow-outer-width; .bs-popover-left {
margin-right: $popover-arrow-height;
.arrow {
right: calc((#{$popover-arrow-height} + #{$popover-border-width}) * -1);
width: $popover-arrow-height;
height: $popover-arrow-width;
margin: $border-radius-lg 0; // make sure the arrow does not touch the popover's rounded corners
}
.arrow::before,
.arrow::after {
border-width: ($popover-arrow-width / 2) 0 ($popover-arrow-width / 2) $popover-arrow-height;
}
.arrow::before {
right: 0;
border-left-color: $popover-arrow-outer-color;
}
.arrow::after {
right: $popover-border-width;
border-left-color: $popover-arrow-color;
}
} }
/* rtl:end:ignore */
.popover-arrow::after { .bs-popover-auto {
content: ""; &[x-placement^="top"] {
border-width: $popover-arrow-width; @extend .bs-popover-top;
}
&[x-placement^="right"] {
@extend .bs-popover-right;
}
&[x-placement^="bottom"] {
@extend .bs-popover-bottom;
}
&[x-placement^="left"] {
@extend .bs-popover-left;
}
} }
// Offset the popover to account for the popover arrow
.popover-header {
padding: $popover-header-padding-y $popover-header-padding-x;
margin-bottom: 0; // Reset the default from Reboot
font-size: $font-size-base;
color: $popover-header-color;
background-color: $popover-header-bg;
border-bottom: $popover-border-width solid darken($popover-header-bg, 5%);
$offset-border-width: calc(#{$border-radius-lg} - #{$popover-border-width});
@include border-top-radius($offset-border-width);
&:empty {
display: none;
}
}
.popover-body {
padding: $popover-body-padding-y $popover-body-padding-x;
color: $popover-body-color;
}
/* rtl:end:ignore */

42
scss/bootstrap/_print.scss

@ -1,4 +1,4 @@
// scss-lint:disable QualifyingElement // stylelint-disable declaration-no-important, selector-no-qualifying-type
// Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css // Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css
@ -12,20 +12,18 @@
@media print { @media print {
*, *,
*::before, *::before,
*::after, *::after {
*::first-letter,
*::first-line {
// Bootstrap specific; comment out `color` and `background` // Bootstrap specific; comment out `color` and `background`
//color: #000 !important; // Black prints faster: //color: #000 !important; // Black prints faster: http://www.sanbeiji.com/archives/953
// http://www.sanbeiji.com/archives/953
text-shadow: none !important; text-shadow: none !important;
//background: transparent !important; //background: transparent !important;
box-shadow: none !important; box-shadow: none !important;
} }
a, a {
a:visited { &:not(.btn) {
text-decoration: underline; text-decoration: underline;
}
} }
// Bootstrap specific; comment the following selector out // Bootstrap specific; comment the following selector out
@ -45,9 +43,12 @@
//a[href^="#"]::after, //a[href^="#"]::after,
//a[href^="javascript:"]::after { //a[href^="javascript:"]::after {
// content: ""; // content: "";
//} //}
pre {
white-space: pre-wrap !important;
}
pre, pre,
blockquote { blockquote {
border: $border-width solid #999; // Bootstrap custom code; using `$border-width` instead of 1px border: $border-width solid #999; // Bootstrap custom code; using `$border-width` instead of 1px
@ -82,17 +83,24 @@
// Bootstrap specific changes start // Bootstrap specific changes start
// Specify a size and min-width to make printing closer across browsers.
// We don't set margin here because it breaks `size` in Chrome. We also
// don't use `!important` on `size` as it breaks in Chrome.
@page {
size: $print-page-size;
}
body {
min-width: $print-body-min-width !important;
}
.container {
min-width: $print-body-min-width !important;
}
// Bootstrap components // Bootstrap components
.navbar { .navbar {
display: none; display: none;
} }
.btn, .badge {
.dropup > .btn {
> .caret {
border-top-color: #000 !important;
}
}
.tag {
border: $border-width solid #000; border: $border-width solid #000;
} }

146
scss/bootstrap/_progress.scss

@ -1,145 +1,33 @@
//
// Progress animations
//
@keyframes progress-bar-stripes { @keyframes progress-bar-stripes {
from { background-position: $spacer-y 0; } from { background-position: $progress-height 0; }
to { background-position: 0 0; } to { background-position: 0 0; }
} }
//
// Basic progress bar
//
.progress { .progress {
display: block; display: flex;
width: 100%; height: $progress-height;
height: $spacer-y; // todo: make a new var for this overflow: hidden; // force rounded corners by cropping it
margin-bottom: $spacer-y; font-size: $progress-font-size;
}
.progress[value] {
// Set overall background
background-color: $progress-bg;
// Remove Firefox and Opera border
border: 0;
// Reset the default appearance
appearance: none;
// Set overall border radius
@include border-radius($progress-border-radius);
}
// Filled-in portion of the bar
.progress[value]::-ms-fill {
background-color: $progress-bar-color;
// Remove right-hand border of value bar from IE10+/Edge
border: 0;
}
.progress[value]::-moz-progress-bar {
background-color: $progress-bar-color;
@include border-left-radius($progress-border-radius);
}
.progress[value]::-webkit-progress-value {
background-color: $progress-bar-color;
@include border-left-radius($progress-border-radius);
}
// Tweaks for full progress bar
.progress[value="100"]::-moz-progress-bar {
@include border-right-radius($progress-border-radius);
}
.progress[value="100"]::-webkit-progress-value {
@include border-right-radius($progress-border-radius);
}
// Unfilled portion of the bar
.progress[value]::-webkit-progress-bar {
background-color: $progress-bg;
@include border-radius($progress-border-radius);
@include box-shadow($progress-box-shadow);
}
base::-moz-progress-bar, // Absurd-but-syntactically-valid selector to make these styles Firefox-only
.progress[value] {
background-color: $progress-bg; background-color: $progress-bg;
@include border-radius($progress-border-radius); @include border-radius($progress-border-radius);
@include box-shadow($progress-box-shadow); @include box-shadow($progress-box-shadow);
} }
// IE9 hacks to accompany custom markup. We don't need to scope this via media queries, but I feel better doing it anyway. .progress-bar {
@media screen and (min-width:0\0) { display: flex;
.progress { flex-direction: column;
background-color: $progress-bg; justify-content: center;
@include border-radius($progress-border-radius); color: $progress-bar-color;
@include box-shadow($progress-box-shadow); text-align: center;
} background-color: $progress-bar-bg;
.progress-bar { @include transition($progress-bar-transition);
display: inline-block;
height: $spacer-y;
text-indent: -999rem; // Simulate hiding of value as in native `<progress>`
background-color: $progress-bar-color;
@include border-left-radius($progress-border-radius);
}
.progress[width="100%"] {
@include border-right-radius($progress-border-radius);
}
} }
.progress-bar-striped {
//
// Striped
//
.progress-striped[value]::-webkit-progress-value {
@include gradient-striped(); @include gradient-striped();
background-size: $spacer-y $spacer-y; background-size: $progress-height $progress-height;
}
.progress-striped[value]::-moz-progress-bar {
@include gradient-striped();
background-size: $spacer-y $spacer-y;
}
.progress-striped[value]::-ms-fill {
@include gradient-striped();
background-size: $spacer-y $spacer-y;
}
// IE9
@media screen and (min-width:0\0) {
.progress-bar-striped {
@include gradient-striped();
background-size: $spacer-y $spacer-y;
}
}
//
// Animated
//
.progress-animated[value]::-webkit-progress-value {
animation: progress-bar-stripes 2s linear infinite;
}
.progress-animated[value]::-moz-progress-bar {
animation: progress-bar-stripes 2s linear infinite;
} }
// IE9
@media screen and (min-width:0\0) {
.progress-animated .progress-bar-striped {
animation: progress-bar-stripes 2s linear infinite;
}
}
// .progress-bar-animated {
// Variations animation: progress-bar-stripes $progress-bar-animation-timing;
//
.progress-success {
@include progress-variant($progress-bar-success-bg);
}
.progress-info {
@include progress-variant($progress-bar-info-bg);
}
.progress-warning {
@include progress-variant($progress-bar-warning-bg);
}
.progress-danger {
@include progress-variant($progress-bar-danger-bg);
} }

367
scss/bootstrap/_reboot.scss

@ -1,82 +1,68 @@
// scss-lint:disable QualifyingElement, DuplicateProperty // stylelint-disable at-rule-no-vendor-prefix, declaration-no-important, selector-no-qualifying-type, property-no-vendor-prefix
// Reboot // Reboot
// //
// Global resets to common HTML elements and more for easier usage by Bootstrap. // Normalization of HTML elements, manually forked from Normalize.css to remove
// Adds additional rules on top of Normalize.css, including several overrides. // styles targeting irrelevant browsers while applying new styles.
//
// Normalize is licensed MIT. https://github.com/necolas/normalize.css
// Reset the box-sizing // Document
//
// Change from `box-sizing: content-box` to `border-box` so that when you add
// `padding` or `border`s to an element, the overall declared `width` does not
// change. For example, `width: 100px;` will always be `100px` despite the
// `border: 10px solid red;` and `padding: 20px;`.
// //
// Heads up! This reset may cause conflicts with some third-party widgets. For // 1. Change from `box-sizing: content-box` so that `width` is not affected by `padding` or `border`.
// recommendations on resolving such conflicts, see // 2. Change the default font family in all browsers.
// http://getbootstrap.com/getting-started/#third-box-sizing. // 3. Correct the line height in all browsers.
// // 4. Prevent adjustments of font size after orientation changes in IE on Windows Phone and in iOS.
// Credit: https://css-tricks.com/inheriting-box-sizing-probably-slightly-better-best-practice/ // 5. Setting @viewport causes scrollbars to overlap content in IE11 and Edge, so
// we force a non-overlapping, non-auto-hiding scrollbar to counteract.
html { // 6. Change the default tap highlight to be completely transparent in iOS.
box-sizing: border-box;
}
*, *,
*::before, *::before,
*::after { *::after {
box-sizing: inherit; box-sizing: border-box; // 1
} }
html {
font-family: sans-serif; // 2
line-height: 1.15; // 3
-webkit-text-size-adjust: 100%; // 4
-ms-text-size-adjust: 100%; // 4
-ms-overflow-style: scrollbar; // 5
-webkit-tap-highlight-color: rgba(0, 0, 0, 0); // 6
}
// Make viewport responsive // IE10+ doesn't honor `<meta name="viewport">` in some cases.
//
// @viewport is needed because IE 10+ doesn't honor <meta name="viewport"> in
// some cases. See http://timkadlec.com/2012/10/ie10-snap-mode-and-responsive-design/.
// Eventually @viewport will replace <meta name="viewport">.
//
// However, `device-width` is broken on IE 10 on Windows (Phone) 8,
// (see http://timkadlec.com/2013/01/windows-phone-8-and-device-width/ and https://github.com/twbs/bootstrap/issues/10497)
// and the fix for that involves a snippet of JavaScript to sniff the user agent
// and apply some conditional CSS.
//
// See http://getbootstrap.com/getting-started/#support-ie10-width for the relevant hack.
//
// Wrap `@viewport` with `@at-root` for when folks do a nested import (e.g.,
// `.class-name { @import "bootstrap"; }`).
@at-root { @at-root {
@-ms-viewport { width: device-width; } @-ms-viewport {
width: device-width;
}
} }
// stylelint-disable selector-list-comma-newline-after
// Shim for "new" HTML5 structural elements to display correctly (IE10, older browsers)
article, aside, dialog, figcaption, figure, footer, header, hgroup, main, nav, section {
display: block;
}
// stylelint-enable selector-list-comma-newline-after
// Body
// //
// Reset HTML, body, and more // 1. Remove the margin in all browsers.
// // 2. As a best practice, apply a default `background-color`.
// 3. Set an explicit initial text-align value so that we can later use the
html { // the `inherit` value on things like `<th>` elements.
// Sets a specific default `font-size` for user with `rem` type scales.
font-size: $font-size-root;
// As a side-effect of setting the @viewport above,
// IE11 & Edge make the scrollbar overlap the content and automatically hide itself when not in use.
// Unfortunately, the auto-showing of the scrollbar is sometimes too sensitive,
// thus making it hard to click on stuff near the right edge of the page.
// So we add this style to force IE11 & Edge to use a "normal", non-overlapping, non-auto-hiding scrollbar.
// See https://github.com/twbs/bootstrap/issues/18543
-ms-overflow-style: scrollbar;
// Changes the default tap highlight to be completely transparent in iOS.
-webkit-tap-highlight-color: rgba(0,0,0,0);
}
body { body {
// Make the `body` use the `font-size-root` margin: 0; // 1
font-family: $font-family-base; font-family: $font-family-base;
font-size: $font-size-base; font-size: $font-size-base;
font-weight: $font-weight-base;
line-height: $line-height-base; line-height: $line-height-base;
// Go easy on the eyes and use something other than `#000` for text
color: $body-color; color: $body-color;
// By default, `<body>` has no `background-color` so we set one as a best practice. text-align: left; // 3
background-color: $body-bg; background-color: $body-bg; // 2
} }
// Suppress the focus outline on elements that cannot be accessed via keyboard. // Suppress the focus outline on elements that cannot be accessed via keyboard.
@ -85,7 +71,19 @@ body {
// //
// Credit: https://github.com/suitcss/base // Credit: https://github.com/suitcss/base
[tabindex="-1"]:focus { [tabindex="-1"]:focus {
outline: none !important; outline: 0 !important;
}
// Content grouping
//
// 1. Add the correct box sizing in Firefox.
// 2. Show the overflow in Edge and IE.
hr {
box-sizing: content-box; // 1
height: 0; // 1
overflow: visible; // 2
} }
@ -97,10 +95,12 @@ body {
// //
// By default, `<h1>`-`<h6>` all receive top and bottom margins. We nuke the top // By default, `<h1>`-`<h6>` all receive top and bottom margins. We nuke the top
// margin for easier control within type scales as it avoids margin collapsing. // margin for easier control within type scales as it avoids margin collapsing.
// stylelint-disable selector-list-comma-newline-after
h1, h2, h3, h4, h5, h6 { h1, h2, h3, h4, h5, h6 {
margin-top: 0; margin-top: 0;
margin-bottom: .5rem; margin-bottom: $headings-margin-bottom;
} }
// stylelint-enable selector-list-comma-newline-after
// Reset margins on paragraphs // Reset margins on paragraphs
// //
@ -108,15 +108,22 @@ h1, h2, h3, h4, h5, h6 {
// bottom margin to use `rem` units instead of `em`. // bottom margin to use `rem` units instead of `em`.
p { p {
margin-top: 0; margin-top: 0;
margin-bottom: 1rem; margin-bottom: $paragraph-margin-bottom;
} }
// Abbreviations and acronyms // Abbreviations
//
// 1. Remove the bottom border in Firefox 39-.
// 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
// 3. Add explicit cursor to indicate changed behavior.
// 4. Duplicate behavior to the data-* attribute for our tooltip plugin
abbr[title], abbr[title],
// Add data-* attribute to help out our tooltip plugin, per https://github.com/twbs/bootstrap/issues/5257 abbr[data-original-title] { // 4
abbr[data-original-title] { text-decoration: underline; // 2
cursor: help; text-decoration: underline dotted; // 2
border-bottom: 1px dotted $abbr-border-color; cursor: help; // 3
border-bottom: 0; // 1
} }
address { address {
@ -152,6 +159,37 @@ blockquote {
margin: 0 0 1rem; margin: 0 0 1rem;
} }
dfn {
font-style: italic; // Add the correct font style in Android 4.3-
}
// stylelint-disable font-weight-notation
b,
strong {
font-weight: bolder; // Add the correct font weight in Chrome, Edge, and Safari
}
// stylelint-enable font-weight-notation
small {
font-size: 80%; // Add the correct font size in all browsers
}
//
// Prevent `sub` and `sup` elements from affecting the line height in
// all browsers.
//
sub,
sup {
position: relative;
font-size: 75%;
line-height: 0;
vertical-align: baseline;
}
sub { bottom: -.25em; }
sup { top: -.5em; }
// //
// Links // Links
@ -160,15 +198,13 @@ blockquote {
a { a {
color: $link-color; color: $link-color;
text-decoration: $link-decoration; text-decoration: $link-decoration;
background-color: transparent; // Remove the gray background on active links in IE 10.
-webkit-text-decoration-skip: objects; // Remove gaps in links underline in iOS 8+ and Safari 8+.
@include hover-focus { @include hover {
color: $link-hover-color; color: $link-hover-color;
text-decoration: $link-hover-decoration; text-decoration: $link-hover-decoration;
} }
&:focus {
@include tab-focus();
}
} }
// And undo these styles for placeholder links/named anchors (without href) // And undo these styles for placeholder links/named anchors (without href)
@ -187,7 +223,7 @@ a:not([href]):not([tabindex]) {
} }
&:focus { &:focus {
outline: none; outline: 0;
} }
} }
@ -196,13 +232,26 @@ a:not([href]):not([tabindex]) {
// Code // Code
// //
// stylelint-disable font-family-no-duplicate-names
pre,
code,
kbd,
samp {
font-family: monospace, monospace; // Correct the inheritance and scaling of font size in all browsers.
font-size: 1em; // Correct the odd `em` font sizing in all browsers.
}
// stylelint-enable font-family-no-duplicate-names
pre { pre {
// Remove browser default top margin // Remove browser default top margin
margin-top: 0; margin-top: 0;
// Reset browser default of `1em` to use `rem`s // Reset browser default of `1em` to use `rem`s
margin-bottom: 1rem; margin-bottom: 1rem;
// Normalize v4 removed this property, causing `<pre>` content to break out of wrapping code snippets // Don't allow content to break outside
overflow: auto; overflow: auto;
// We have @viewport set which causes scrollbars to overlap content in IE11 and Edge, so
// we force a non-overlapping, non-auto-hiding scrollbar to counteract.
-ms-overflow-style: scrollbar;
} }
@ -211,56 +260,22 @@ pre {
// //
figure { figure {
// Normalize adds `margin` to `figure`s as browsers apply it inconsistently. // Apply a consistent margin strategy (matches our type styles).
// We reset that to create a better flow in-page.
margin: 0 0 1rem; margin: 0 0 1rem;
} }
// //
// Images // Images and content
// //
img { img {
// By default, `<img>`s are `inline-block`. This assumes that, and vertically
// centers them. This won't apply should you reset them to `block` level.
vertical-align: middle; vertical-align: middle;
// Note: `<img>`s are deliberately not made responsive by default. border-style: none; // Remove the border on images inside links in IE 10-.
// For the rationale behind this, see the comments on the `.img-fluid` class.
}
// iOS "clickable elements" fix for role="button"
//
// Fixes "clickability" issue (and more generally, the firing of events such as focus as well)
// for traditionally non-focusable elements with role="button"
// see https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile
[role="button"] {
cursor: pointer;
} }
svg:not(:root) {
// Avoid 300ms click delay on touch devices that support the `touch-action` CSS property. overflow: hidden; // Hide the overflow in IE
//
// In particular, unlike most other browsers, IE11+Edge on Windows 10 on touch devices and IE Mobile 10-11
// DON'T remove the click delay when `<meta name="viewport" content="width=device-width">` is present.
// However, they DO support removing the click delay via `touch-action: manipulation`.
// See:
// * http://v4-alpha.getbootstrap.com/content/reboot/#click-delay-optimization-for-touch
// * http://caniuse.com/#feat=css-touch-action
// * http://patrickhlauke.github.io/touch/tests/results/#suppressing-300ms-delay
a,
area,
button,
[role="button"],
input,
label,
select,
summary,
textarea {
touch-action: manipulation;
} }
@ -269,10 +284,7 @@ textarea {
// //
table { table {
// No longer part of Normalize since v4 border-collapse: collapse; // Prevent double borders
border-collapse: collapse;
// Reset for nesting within parents with `background-color`.
background-color: $table-bg;
} }
caption { caption {
@ -284,8 +296,9 @@ caption {
} }
th { th {
// Centered by default, but left-align-ed to match the `td`s below. // Matches default `<td>` alignment by inheriting from the `<body>`, or the
text-align: left; // closest parent with a set `text-align`.
text-align: inherit;
} }
@ -299,6 +312,13 @@ label {
margin-bottom: .5rem; margin-bottom: .5rem;
} }
// Remove the default `border-radius` that macOS Chrome adds.
//
// Details at https://github.com/twbs/bootstrap/issues/24093
button {
border-radius: 0;
}
// Work around a Firefox/IE bug where the transparent `button` background // Work around a Firefox/IE bug where the transparent `button` background
// results in a loss of the default `button` focus styles. // results in a loss of the default `button` focus styles.
// //
@ -311,25 +331,47 @@ button:focus {
input, input,
button, button,
select, select,
optgroup,
textarea { textarea {
// Remove all `margin`s so our classes don't have to do it themselves. margin: 0; // Remove the margin in Firefox and Safari
margin: 0; font-family: inherit;
// Normalize includes `font: inherit;`, so `font-family`. `font-size`, etc are font-size: inherit;
// properly inherited. However, `line-height` isn't addressed there. Using this
// ensures we don't need to unnecessarily redeclare the global font stack.
line-height: inherit; line-height: inherit;
// iOS adds rounded borders by default }
border-radius: 0;
button,
input {
overflow: visible; // Show the overflow in Edge
}
button,
select {
text-transform: none; // Remove the inheritance of text transform in Firefox
}
// 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`
// controls in Android 4.
// 2. Correct the inability to style clickable types in iOS and Safari.
button,
html [type="button"], // 1
[type="reset"],
[type="submit"] {
-webkit-appearance: button; // 2
}
// Remove inner border and padding from Firefox, but don't restore the outline like Normalize.
button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
padding: 0;
border-style: none;
} }
input[type="radio"], input[type="radio"],
input[type="checkbox"] { input[type="checkbox"] {
// Apply a disabled cursor for radios and checkboxes. box-sizing: border-box; // 1. Add the correct box sizing in IE 10-
// padding: 0; // 2. Remove the padding in IE 10-
// Note: Neither radios nor checkboxes can be readonly.
&:disabled {
cursor: $cursor-disabled;
}
} }
@ -340,20 +382,23 @@ input[type="month"] {
// Remove the default appearance of temporal inputs to avoid a Mobile Safari // Remove the default appearance of temporal inputs to avoid a Mobile Safari
// bug where setting a custom line-height prevents text from being vertically // bug where setting a custom line-height prevents text from being vertically
// centered within the input. // centered within the input.
// // See https://bugs.webkit.org/show_bug.cgi?id=139848
// Bug report: https://github.com/twbs/bootstrap/issues/11266 // and https://github.com/twbs/bootstrap/issues/11266
-webkit-appearance: listbox; -webkit-appearance: listbox;
} }
textarea { textarea {
overflow: auto; // Remove the default vertical scrollbar in IE.
// Textareas should really only resize vertically so they don't break their (horizontal) containers. // Textareas should really only resize vertically so they don't break their (horizontal) containers.
resize: vertical; resize: vertical;
} }
fieldset { fieldset {
// Chrome and Firefox set a `min-width: min-content;` on fieldsets, // Browsers set a default `min-width: min-content;` on fieldsets,
// so we reset that to ensure it behaves more like a standard block element. // unlike e.g. `<div>`s, which have `min-width: 0;` by default.
// See https://github.com/twbs/bootstrap/issues/12359. // So we reset that to ensure fieldsets behave more like a standard block element.
// See https://github.com/twbs/bootstrap/issues/12359
// and https://html.spec.whatwg.org/multipage/#the-fieldset-and-legend-elements
min-width: 0; min-width: 0;
// Reset the default outline behavior of fieldsets so they don't affect page layout. // Reset the default outline behavior of fieldsets so they don't affect page layout.
padding: 0; padding: 0;
@ -361,33 +406,77 @@ fieldset {
border: 0; border: 0;
} }
// 1. Correct the text wrapping in Edge and IE.
// 2. Correct the color inheritance from `fieldset` elements in IE.
legend { legend {
// Reset the entire legend element to match the `fieldset`
display: block; display: block;
width: 100%; width: 100%;
max-width: 100%; // 1
padding: 0; padding: 0;
margin-bottom: .5rem; margin-bottom: .5rem;
font-size: 1.5rem; font-size: 1.5rem;
line-height: inherit; line-height: inherit;
color: inherit; // 2
white-space: normal; // 1
}
progress {
vertical-align: baseline; // Add the correct vertical alignment in Chrome, Firefox, and Opera.
}
// Correct the cursor style of increment and decrement buttons in Chrome.
[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
height: auto;
} }
input[type="search"] { [type="search"] {
// This overrides the extra rounded corners on search inputs in iOS so that our // This overrides the extra rounded corners on search inputs in iOS so that our
// `.form-control` class can properly style them. Note that this cannot simply // `.form-control` class can properly style them. Note that this cannot simply
// be added to `.form-control` as it's not specific enough. For details, see // be added to `.form-control` as it's not specific enough. For details, see
// https://github.com/twbs/bootstrap/issues/11586. // https://github.com/twbs/bootstrap/issues/11586.
outline-offset: -2px; // 2. Correct the outline style in Safari.
-webkit-appearance: none;
}
//
// Remove the inner padding and cancel buttons in Chrome and Safari on macOS.
//
[type="search"]::-webkit-search-cancel-button,
[type="search"]::-webkit-search-decoration {
-webkit-appearance: none; -webkit-appearance: none;
} }
// todo: needed? //
// 1. Correct the inability to style clickable types in iOS and Safari.
// 2. Change font properties to `inherit` in Safari.
//
::-webkit-file-upload-button {
font: inherit; // 2
-webkit-appearance: button; // 1
}
//
// Correct element displays
//
output { output {
display: inline-block; display: inline-block;
// font-size: $font-size-base; }
// line-height: $line-height;
// color: $input-color; summary {
display: list-item; // Add the correct display in all browsers
cursor: pointer;
}
template {
display: none; // Add the correct display in IE
} }
// Always hide an element with the `hidden` HTML attribute (from PureCSS). // Always hide an element with the `hidden` HTML attribute (from PureCSS).
// Needed for proper display in IE 10-.
[hidden] { [hidden] {
display: none !important; display: none !important;
} }

19
scss/bootstrap/_root.scss

@ -0,0 +1,19 @@
:root {
// Custom variable values only support SassScript inside `#{}`.
@each $color, $value in $colors {
--#{$color}: #{$value};
}
@each $color, $value in $theme-colors {
--#{$color}: #{$value};
}
@each $bp, $value in $grid-breakpoints {
--breakpoint-#{$bp}: #{$value};
}
// Use `inspect` for lists so that quoted items keep the quotes.
// See https://github.com/sass/sass/issues/2383#issuecomment-336349172
--font-family-sans-serif: #{inspect($font-family-sans-serif)};
--font-family-monospace: #{inspect($font-family-monospace)};
}

134
scss/bootstrap/_tables.scss

@ -6,6 +6,7 @@
width: 100%; width: 100%;
max-width: 100%; max-width: 100%;
margin-bottom: $spacer; margin-bottom: $spacer;
background-color: $table-bg; // Reset for nesting within parents with `background-color`.
th, th,
td { td {
@ -36,7 +37,7 @@
.table-sm { .table-sm {
th, th,
td { td {
padding: $table-sm-cell-padding; padding: $table-cell-padding-sm;
} }
} }
@ -68,7 +69,7 @@
.table-striped { .table-striped {
tbody tr:nth-of-type(odd) { tbody tr:nth-of-type(odd) {
background-color: $table-bg-accent; background-color: $table-accent-bg;
} }
} }
@ -80,7 +81,7 @@
.table-hover { .table-hover {
tbody tr { tbody tr {
@include hover { @include hover {
background-color: $table-bg-hover; background-color: $table-hover-bg;
} }
} }
} }
@ -91,106 +92,89 @@
// Exact selectors below required to override `.table-striped` and prevent // Exact selectors below required to override `.table-striped` and prevent
// inheritance to nested tables. // inheritance to nested tables.
// Generate the contextual variants @each $color, $value in $theme-colors {
@include table-row-variant(active, $table-bg-active); @include table-row-variant($color, theme-color-level($color, -9));
@include table-row-variant(success, $state-success-bg); }
@include table-row-variant(info, $state-info-bg);
@include table-row-variant(warning, $state-warning-bg); @include table-row-variant(active, $table-active-bg);
@include table-row-variant(danger, $state-danger-bg);
// Inverse styles // Dark styles
// //
// Same table markup, but inverted color scheme: dark background and light text. // Same table markup, but inverted color scheme: dark background and light text.
.thead-inverse { // stylelint-disable-next-line no-duplicate-selectors
th { .table {
color: #fff; .thead-dark {
background-color: $gray-dark; th {
color: $table-dark-color;
background-color: $table-dark-bg;
border-color: $table-dark-border-color;
}
} }
}
.thead-default { .thead-light {
th { th {
color: $gray; color: $table-head-color;
background-color: $gray-lighter; background-color: $table-head-bg;
border-color: $table-border-color;
}
} }
} }
.table-inverse { .table-dark {
color: $gray-lighter; color: $table-dark-color;
background-color: $gray-dark; background-color: $table-dark-bg;
th, th,
td, td,
thead th { thead th {
border-color: $gray; border-color: $table-dark-border-color;
} }
&.table-bordered { &.table-bordered {
border: 0; border: 0;
} }
}
// Responsive tables
//
// Wrap your tables in `.table-responsive` and we'll make them mobile friendly
// by enabling horizontal scrolling. Only applies <768px. Everything above that
// will display normally.
.table-responsive {
display: block;
width: 100%;
min-height: .01%; // Workaround for IE9 bug (see https://github.com/twbs/bootstrap/issues/14837)
overflow-x: auto;
// TODO: find out if we need this still.
//
// border: $table-border-width solid $table-border-color;
// -ms-overflow-style: -ms-autohiding-scrollbar; // See https://github.com/twbs/bootstrap/pull/10057
}
.table-reflow { &.table-striped {
thead { tbody tr:nth-of-type(odd) {
float: left; background-color: $table-dark-accent-bg;
}
tbody {
display: block;
white-space: nowrap;
}
th,
td {
border-top: $table-border-width solid $table-border-color;
border-left: $table-border-width solid $table-border-color;
&:last-child {
border-right: $table-border-width solid $table-border-color;
} }
} }
thead, &.table-hover {
tbody, tbody tr {
tfoot { @include hover {
&:last-child { background-color: $table-dark-hover-bg;
tr:last-child th,
tr:last-child td {
border-bottom: $table-border-width solid $table-border-color;
} }
} }
} }
}
tr {
float: left;
th, // Responsive tables
td { //
display: block !important; // Generate series of `.table-responsive-*` classes for configuring the screen
border: $table-border-width solid $table-border-color; // size of where your table will overflow.
.table-responsive {
@each $breakpoint in map-keys($grid-breakpoints) {
$next: breakpoint-next($breakpoint, $grid-breakpoints);
$infix: breakpoint-infix($next, $grid-breakpoints);
&#{$infix} {
@include media-breakpoint-down($breakpoint) {
display: block;
width: 100%;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
-ms-overflow-style: -ms-autohiding-scrollbar; // See https://github.com/twbs/bootstrap/pull/10057
// Prevent double border on horizontal scroll due to use of `display: block;`
> .table-bordered {
border: 0;
}
}
} }
} }
} }

124
scss/bootstrap/_tooltip.scss

@ -3,70 +3,107 @@
position: absolute; position: absolute;
z-index: $zindex-tooltip; z-index: $zindex-tooltip;
display: block; display: block;
margin: $tooltip-margin;
// Our parent element can be arbitrary since tooltips are by default inserted as a sibling of their target element. // Our parent element can be arbitrary since tooltips are by default inserted as a sibling of their target element.
// So reset our font and text properties to avoid inheriting weird values. // So reset our font and text properties to avoid inheriting weird values.
@include reset-text(); @include reset-text();
font-size: $font-size-sm; font-size: $tooltip-font-size;
// Allow breaking very long words so they don't overflow the tooltip's bounds // Allow breaking very long words so they don't overflow the tooltip's bounds
word-wrap: break-word; word-wrap: break-word;
opacity: 0; opacity: 0;
&.in { opacity: $tooltip-opacity; } &.show { opacity: $tooltip-opacity; }
&.tooltip-top, .arrow {
&.bs-tether-element-attached-bottom { position: absolute;
padding: $tooltip-arrow-width 0; display: block;
margin-top: -$tooltip-margin; width: $tooltip-arrow-width;
height: $tooltip-arrow-height;
.tooltip-arrow { &::before {
bottom: 0; position: absolute;
left: 50%; content: "";
margin-left: -$tooltip-arrow-width; border-color: transparent;
border-width: $tooltip-arrow-width $tooltip-arrow-width 0; border-style: solid;
}
}
}
.bs-tooltip-top {
padding: $tooltip-arrow-height 0;
.arrow {
bottom: 0;
&::before {
top: 0;
border-width: $tooltip-arrow-height ($tooltip-arrow-width / 2) 0;
border-top-color: $tooltip-arrow-color; border-top-color: $tooltip-arrow-color;
} }
} }
&.tooltip-right, }
&.bs-tether-element-attached-left {
padding: 0 $tooltip-arrow-width;
margin-left: $tooltip-margin;
.tooltip-arrow { .bs-tooltip-right {
top: 50%; padding: 0 $tooltip-arrow-height;
left: 0;
margin-top: -$tooltip-arrow-width; .arrow {
border-width: $tooltip-arrow-width $tooltip-arrow-width $tooltip-arrow-width 0; left: 0;
width: $tooltip-arrow-height;
height: $tooltip-arrow-width;
&::before {
right: 0;
border-width: ($tooltip-arrow-width / 2) $tooltip-arrow-height ($tooltip-arrow-width / 2) 0;
border-right-color: $tooltip-arrow-color; border-right-color: $tooltip-arrow-color;
} }
} }
&.tooltip-bottom, }
&.bs-tether-element-attached-top {
padding: $tooltip-arrow-width 0;
margin-top: $tooltip-margin;
.tooltip-arrow { .bs-tooltip-bottom {
top: 0; padding: $tooltip-arrow-height 0;
left: 50%;
margin-left: -$tooltip-arrow-width; .arrow {
border-width: 0 $tooltip-arrow-width $tooltip-arrow-width; top: 0;
&::before {
bottom: 0;
border-width: 0 ($tooltip-arrow-width / 2) $tooltip-arrow-height;
border-bottom-color: $tooltip-arrow-color; border-bottom-color: $tooltip-arrow-color;
} }
} }
&.tooltip-left, }
&.bs-tether-element-attached-right {
padding: 0 $tooltip-arrow-width;
margin-left: -$tooltip-margin;
.tooltip-arrow { .bs-tooltip-left {
top: 50%; padding: 0 $tooltip-arrow-height;
right: 0;
margin-top: -$tooltip-arrow-width; .arrow {
border-width: $tooltip-arrow-width 0 $tooltip-arrow-width $tooltip-arrow-width; right: 0;
width: $tooltip-arrow-height;
height: $tooltip-arrow-width;
&::before {
left: 0;
border-width: ($tooltip-arrow-width / 2) 0 ($tooltip-arrow-width / 2) $tooltip-arrow-height;
border-left-color: $tooltip-arrow-color; border-left-color: $tooltip-arrow-color;
} }
} }
} }
.bs-tooltip-auto {
&[x-placement^="top"] {
@extend .bs-tooltip-top;
}
&[x-placement^="right"] {
@extend .bs-tooltip-right;
}
&[x-placement^="bottom"] {
@extend .bs-tooltip-bottom;
}
&[x-placement^="left"] {
@extend .bs-tooltip-left;
}
}
// Wrapper for the tooltip content // Wrapper for the tooltip content
.tooltip-inner { .tooltip-inner {
max-width: $tooltip-max-width; max-width: $tooltip-max-width;
@ -74,14 +111,5 @@
color: $tooltip-color; color: $tooltip-color;
text-align: center; text-align: center;
background-color: $tooltip-bg; background-color: $tooltip-bg;
@include border-radius($border-radius); @include border-radius($tooltip-border-radius);
}
// Arrows
.tooltip-arrow {
position: absolute;
width: 0;
height: 0;
border-color: transparent;
border-style: solid;
} }

36
scss/bootstrap/_transitions.scss

@ -0,0 +1,36 @@
// stylelint-disable selector-no-qualifying-type
.fade {
opacity: 0;
@include transition($transition-fade);
&.show {
opacity: 1;
}
}
.collapse {
display: none;
&.show {
display: block;
}
}
tr {
&.collapse.show {
display: table-row;
}
}
tbody {
&.collapse.show {
display: table-row-group;
}
}
.collapsing {
position: relative;
height: 0;
overflow: hidden;
@include transition($transition-collapse);
}

55
scss/bootstrap/_type.scss

@ -1,3 +1,5 @@
// stylelint-disable declaration-no-important, selector-list-comma-newline-after
// //
// Headings // Headings
// //
@ -11,12 +13,12 @@ h1, h2, h3, h4, h5, h6,
color: $headings-color; color: $headings-color;
} }
h1, .h1 { font-size: $font-size-h1; } h1, .h1 { font-size: $h1-font-size; }
h2, .h2 { font-size: $font-size-h2; } h2, .h2 { font-size: $h2-font-size; }
h3, .h3 { font-size: $font-size-h3; } h3, .h3 { font-size: $h3-font-size; }
h4, .h4 { font-size: $font-size-h4; } h4, .h4 { font-size: $h4-font-size; }
h5, .h5 { font-size: $font-size-h5; } h5, .h5 { font-size: $h5-font-size; }
h6, .h6 { font-size: $font-size-h6; } h6, .h6 { font-size: $h6-font-size; }
.lead { .lead {
font-size: $lead-font-size; font-size: $lead-font-size;
@ -27,18 +29,22 @@ h6, .h6 { font-size: $font-size-h6; }
.display-1 { .display-1 {
font-size: $display1-size; font-size: $display1-size;
font-weight: $display1-weight; font-weight: $display1-weight;
line-height: $display-line-height;
} }
.display-2 { .display-2 {
font-size: $display2-size; font-size: $display2-size;
font-weight: $display2-weight; font-weight: $display2-weight;
line-height: $display-line-height;
} }
.display-3 { .display-3 {
font-size: $display3-size; font-size: $display3-size;
font-weight: $display3-weight; font-weight: $display3-weight;
line-height: $display-line-height;
} }
.display-4 { .display-4 {
font-size: $display4-size; font-size: $display4-size;
font-weight: $display4-weight; font-weight: $display4-weight;
line-height: $display-line-height;
} }
@ -47,8 +53,8 @@ h6, .h6 { font-size: $font-size-h6; }
// //
hr { hr {
margin-top: $spacer-y; margin-top: $hr-margin-y;
margin-bottom: $spacer-y; margin-bottom: $hr-margin-y;
border: 0; border: 0;
border-top: $hr-border-width solid $hr-border-color; border-top: $hr-border-width solid $hr-border-color;
} }
@ -61,7 +67,7 @@ hr {
small, small,
.small { .small {
font-size: $small-font-size; font-size: $small-font-size;
font-weight: normal; font-weight: $font-weight-normal;
} }
mark, mark,
@ -104,10 +110,8 @@ mark,
// Blockquotes // Blockquotes
.blockquote { .blockquote {
padding: ($spacer / 2) $spacer;
margin-bottom: $spacer; margin-bottom: $spacer;
font-size: $blockquote-font-size; font-size: $blockquote-font-size;
border-left: $blockquote-border-width solid $blockquote-border-color;
} }
.blockquote-footer { .blockquote-footer {
@ -119,32 +123,3 @@ mark,
content: "\2014 \00A0"; // em dash, nbsp content: "\2014 \00A0"; // em dash, nbsp
} }
} }
// Opposite alignment of blockquote
.blockquote-reverse {
padding-right: $spacer;
padding-left: 0;
text-align: right;
border-right: $blockquote-border-width solid $blockquote-border-color;
border-left: 0;
}
.blockquote-reverse .blockquote-footer {
&::before {
content: "";
}
&::after {
content: "\00A0 \2014"; // nbsp, em dash
}
}
@if not $enable-flex {
// Clean up some horizontal `<dl>`s built with grids
// scss-lint:disable QualifyingElement
dl.row {
> dd + dt {
clear: left;
}
}
// scss-lint:enable QualifyingElement
}

7
scss/bootstrap/_utilities.scss

@ -1,9 +1,14 @@
@import "utilities/align";
@import "utilities/background"; @import "utilities/background";
@import "utilities/borders";
@import "utilities/clearfix"; @import "utilities/clearfix";
@import "utilities/display"; @import "utilities/display";
@import "utilities/embed";
@import "utilities/flex"; @import "utilities/flex";
@import "utilities/pulls"; @import "utilities/float";
@import "utilities/position";
@import "utilities/screenreaders"; @import "utilities/screenreaders";
@import "utilities/sizing";
@import "utilities/spacing"; @import "utilities/spacing";
@import "utilities/text"; @import "utilities/text";
@import "utilities/visibility"; @import "utilities/visibility";

1215
scss/bootstrap/_variables.scss

File diff suppressed because it is too large

35
scss/bootstrap/bootstrap-grid.scss

@ -1,23 +1,32 @@
// Bootstrap Grid only /*!
// * Bootstrap Grid v4.0.0 (https://getbootstrap.com)
// Includes relevant variables and mixins for the regular (non-flexbox) grid * Copyright 2011-2018 The Bootstrap Authors
// system, as well as the generated predefined classes (e.g., `.col-4-sm`). * Copyright 2011-2018 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*/
@at-root {
@-ms-viewport { width: device-width; } // stylelint-disable-line at-rule-no-vendor-prefix
}
// html {
// Variables box-sizing: border-box;
// -ms-overflow-style: scrollbar;
}
@import "custom"; *,
@import "variables"; *::before,
*::after {
box-sizing: inherit;
}
// @import "functions";
// Grid mixins @import "variables";
//
@import "mixins/clearfix";
@import "mixins/breakpoints"; @import "mixins/breakpoints";
@import "mixins/grid-framework"; @import "mixins/grid-framework";
@import "mixins/grid"; @import "mixins/grid";
@import "grid"; @import "grid";
@import "utilities/display";
@import "utilities/flex";

17
scss/bootstrap/bootstrap-reboot.scss

@ -1,11 +1,12 @@
// Bootstrap Reboot only /*!
// * Bootstrap Reboot v4.0.0 (https://getbootstrap.com)
// Includes only Normalize and our custom Reboot reset. * Copyright 2011-2018 The Bootstrap Authors
* Copyright 2011-2018 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
*/
@import "custom"; @import "functions";
@import "variables"; @import "variables";
@import "mixins/hover"; @import "mixins";
@import "mixins/tab-focus";
@import "normalize";
@import "reboot"; @import "reboot";

28
scss/bootstrap/bootstrap.scss

@ -1,20 +1,14 @@
/*! /*!
* Bootstrap v4.0.0-alpha.4 (http://getbootstrap.com) * Bootstrap v4.0.0 (https://getbootstrap.com)
* Copyright 2011-2016 The Bootstrap Authors * Copyright 2011-2018 The Bootstrap Authors
* Copyright 2011-2016 Twitter, Inc. * Copyright 2011-2018 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*/ */
// Core variables and mixins @import "functions";
@import "custom";
@import "variables"; @import "variables";
@import "mixins"; @import "mixins";
@import "root";
// Reset and dependencies
@import "normalize";
@import "print";
// Core CSS
@import "reboot"; @import "reboot";
@import "type"; @import "type";
@import "images"; @import "images";
@ -23,9 +17,7 @@
@import "tables"; @import "tables";
@import "forms"; @import "forms";
@import "buttons"; @import "buttons";
@import "transitions";
// Components
@import "animation";
@import "dropdown"; @import "dropdown";
@import "button-group"; @import "button-group";
@import "input-group"; @import "input-group";
@ -35,20 +27,16 @@
@import "card"; @import "card";
@import "breadcrumb"; @import "breadcrumb";
@import "pagination"; @import "pagination";
@import "tags"; @import "badge";
@import "jumbotron"; @import "jumbotron";
@import "alert"; @import "alert";
@import "progress"; @import "progress";
@import "media"; @import "media";
@import "list-group"; @import "list-group";
@import "responsive-embed";
@import "close"; @import "close";
// Components w/ JavaScript
@import "modal"; @import "modal";
@import "tooltip"; @import "tooltip";
@import "popover"; @import "popover";
@import "carousel"; @import "carousel";
// Utility classes
@import "utilities"; @import "utilities";
@import "print";

11
scss/bootstrap/mixins/_alert.scss

@ -1,14 +1,13 @@
// Alerts @mixin alert-variant($background, $border, $color) {
color: $color;
@mixin alert-variant($background, $border, $body-color) { @include gradient-bg($background);
background-color: $background;
border-color: $border; border-color: $border;
color: $body-color;
hr { hr {
border-top-color: darken($border, 5%); border-top-color: darken($border, 5%);
} }
.alert-link { .alert-link {
color: darken($body-color, 10%); color: darken($color, 10%);
} }
} }

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

Loading…
Cancel
Save