#211 Revamp with bootstrap
Opened 2 years ago by hellcp. Modified 2 years ago
hellcp/fedocal bootstrap  into  master

file modified
+7 -13
@@ -276,6 +276,7 @@ 

          flask.g.fas_user = None

  

  

+ 

  # Local function

  def is_admin():

      """ Return whether the user is admin for this application or not. """
@@ -354,13 +355,6 @@ 

      return tzone

  

  

- def chunks(item_list, chunks_size):

-     """ Yield successive n-sized chunks from item_list.

-     """

-     for i in six.moves.range(0, len(item_list), chunks_size):

-         yield item_list[i: i + chunks_size]

- 

- 

  def is_safe_url(target):

      """ Checks that the target url is safe and sending to the current

      website not some other malicious one.
@@ -416,8 +410,8 @@ 

      return flask.render_template(

          'index.html',

          calendars=calendars_enabled,

-         calendars_table=chunks(calendars_enabled, 3),

-         calendars_table2=chunks(calendars_disabled, 3))

+         calendars_enabled=calendars_enabled,

+         calendars_disabled=calendars_disabled)

  

  

  # pylint: disable=R0914
@@ -1203,7 +1197,7 @@ 

                  SESSION.rollback()

                  LOG.debug('Error in delete_meeting - 2')

                  LOG.exception(err)

-                 flask.flash(gettext('Could not delete this meeting.'), 'error')

+                 flask.flash(gettext('Could not delete this meeting.'), 'errors')

  

          year = month = day = None

          if date_limit:
@@ -1511,7 +1505,7 @@ 

      list_locations = fedocallib.get_locations(SESSION)

      return flask.render_template(

          'locations.html',

-         locations=chunks(list_locations, 3))

+         locations=list_locations)

  

  

  # pylint: disable=R0914
@@ -1743,7 +1737,7 @@ 

                        'file: "%s"',

                        flask.g.fas_user.username, ical_file.filename)

              LOG.exception(err)

-             flask.flash("%s" % err, 'error')

+             flask.flash("%s" % err, 'errors')

              return flask.render_template(

                  'upload_calendar.html', form=form, calendar=calendarobj)

  
@@ -1752,7 +1746,7 @@ 

                  SESSION, calendarobj, ical_file.read(), flask.g.fas_user, is_admin())

              flask.flash(gettext('Calendar uploaded'))

          except FedocalException as err:  # pragma: no cover

-             flask.flash("%s" % err, 'error')

+             flask.flash("%s" % err, 'errors')

              return flask.render_template(

                  'upload_calendar.html', form=form, calendar=calendarobj)

          except SQLAlchemyError as err:  # pragma: no cover

@@ -63,7 +63,7 @@ 

          """

          cur_date = date.today()

          if day == 0:

-             return '<td class="noday">&nbsp;</td>'  # day outside month

+             return '<td>&nbsp;</td>'  # day outside month

          else:

              link_day = day

  
@@ -83,15 +83,15 @@ 

                      day)

  

              if day in self.busy_days:

-                 link_day = '<div class="busy_day">%s</div>' % link_day

- 

-             if day == cur_date.day \

+                 output = '<td class="%s secondary text-right">%s</td>' % (

+                     self.cssclasses[weekday], link_day)

+             elif day == cur_date.day \

                      and self.month == cur_date.month \

                      and self.year == cur_date.year:

-                 output = '<td class="%s today">%s</td>' % (

+                 output = '<td class="%s primary text-right">%s</td>' % (

                      self.cssclasses[weekday], link_day)

              else:

-                 output = '<td class="%s">%s</td>' % (

+                 output = '<td class="%s text-right">%s</td>' % (

                      self.cssclasses[weekday], link_day)

  

              return output
@@ -106,7 +106,7 @@ 

          """

          string = ''.join(self.formatday(d, wd) for (d, wd) in theweek)

          if current:

-             return '<tr class="current_week">%s</tr>' % string

+             return '<tr class="bg-light">%s</tr>' % string

          else:

              return '<tr>%s</tr>' % string

  
@@ -141,7 +141,7 @@ 

  

          prev_month_lnk = ''

          if self.calendar_name:

-             prev_month_lnk = '<a class="button" href="%s">&lt;</a>' % (

+             prev_month_lnk = '<a class="btn btn-link" href="%s">&lt;</a>' % (

                  flask.url_for(

                      'calendar',

                      calendar_name=self.calendar_name,
@@ -149,7 +149,7 @@ 

                      month=int(prev_month),

                      day=1))

          elif self.loc_name:

-             prev_month_lnk = '<a class="button" href="%s">&lt;</a>' % (

+             prev_month_lnk = '<a class="btn btn-link" href="%s">&lt;</a>' % (

                  flask.url_for(

                      'location',

                      loc_name=self.loc_name,
@@ -163,7 +163,7 @@ 

  

          next_month_lnk = ''

          if self.calendar_name:

-             next_month_lnk = '<a class="button" href="%s">&gt;</a>' % (

+             next_month_lnk = '<a class="btn btn-link" href="%s">&gt;</a>' % (

                  flask.url_for(

                      'calendar',

                      calendar_name=self.calendar_name,
@@ -171,7 +171,7 @@ 

                      month=int(next_month),

                      day=1))

          elif self.loc_name:

-             next_month_lnk = '<a class="button" href="%s">&gt;</a>' % (

+             next_month_lnk = '<a class="btn btn-link" href="%s">&gt;</a>' % (

                  flask.url_for(

                      'location',

                      loc_name=self.loc_name,
@@ -179,7 +179,7 @@ 

                      month=int(next_month),

                      day=1))

  

-         return '<tr><th colspan="7" class="month">%s %s %s</th></tr>' % (

+         return '<tr><th colspan="7"><div class="d-flex align-items-center justify-content-between text-center">%s <span class="mx-auto">%s</span> %s</div></th></tr>' % (

              prev_month_lnk, string, next_month_lnk)

  

      # pylint: disable=W0221
@@ -189,7 +189,7 @@ 

          """

          values = []

          item = values.append

-         item('<table class="month">')

+         item('<table class="table table-sm month">')

          item('\n')

          item(self.formatmonthname(self.year, self.month, withyear=withyear))

          item('\n')

@@ -0,0 +1,109 @@ 

+ @media all {

+   :root {

+     --fedocal-navbar-bg: #e9ecef;

+     --fedocal-secondary-bg: #6c757d;

+     --fedocal-border: #dee2e6;

+     --fedocal-dropdown-bg: #ffffff;

+     --fedocal-blue-hl: #3c6eb4;

+     --fedocal-blue-bg: #d7e6fa;

+     --fedocal-yellow-hl: #f6d32d;

+     --fedocal-yellow-bg: #fdf6d5;

+    }

+ }

+ 

+ .month .primary {

+   background-color: var(--fedocal-yellow-hl)

+ }

+ 

+ .month .secondary {

+   background-color: var(--fedocal-blue-hl)

+ }

+ 

+ .month .secondary a {

+   color: white;

+ }

+ 

+ table.weekly {

+     border-collapse: collapse;

+     width: 100%;

+ }

+ 

+ .event_blue {

+     display: block;

+     padding:.1em .2em;

+     background-color:var(--fedocal-blue-bg);

+     border:1px solid var(--fedocal-blue-hl);

+     text-align: center;

+ }

+ 

+ td > .event_blue {

+     min-height: 2em;

+     height: 100%;

+ }

+ 

+ td .first_event {

+     border-bottom-color: var(--fedocal-blue-bg);

+     border-top-left-radius: 4px;

+     border-top-right-radius: 4px;

+ }

+ 

+ td .middle_event {

+     border-top-color: var(--fedocal-blue-bg);

+     border-bottom-color: var(--fedocal-blue-bg);

+ }

+ 

+ td .last_event {

+     border-top-color: var(--fedocal-blue-bg);

+     border-bottom-left-radius: 4px;

+     border-bottom-right-radius: 4px;

+ }

+ 

+ table.weekly td {

+     min-width: 100px;

+     padding: 0;

+ }

+ 

+ table.weekly tr:nth-child(even) td {

+     border-top: 1px solid var(--fedocal-border);

+ }

+ 

+ table.weekly tr:not(#full_day):nth-child(even) td:nth-child(even), table.weekly tr:nth-child(odd) td:nth-child(odd), table.weekly tr#full_day td:nth-child(odd) {

+     background-color: var(--light);

+ }

+ 

+ table.weekly tr:nth-child(even) td.today {

+     border-top-color: var(--fedocal-yellow-hl);

+ }

+ 

+ table.weekly td > .row_height,

+ table.weekly th > .row_height

+ {

+     min-height: 2em;

+ }

+ 

+ table.weekly td.empty {

+     background-color: transparent;

+     border: none;

+ }

+ 

+ table.weekly .time {

+     padding-right: 1em;

+     text-align: right;

+     width: 7em;

+ }

+ 

+ table.weekly tr td.today {

+     background-color: var(--fedocal-yellow-bg)!important;

+     vertical-align: middle;

+ }

+ 

+ #shortmenu {

+     position: relative

+ }

+ 

+ #shortmenu div {

+     position: fixed;

+     bottom: 20px;

+     right: 10px;

+     list-style: none;

+ }

The added file is too large to be shown here, see it at: fedocal/static/default/bootstrap.css
@@ -0,0 +1,7 @@ 

+ /*!

+   * Bootstrap v4.1.3 (https://getbootstrap.com/)

+   * Copyright 2011-2018 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)

+   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)

+   */

+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("jquery")):"function"==typeof define&&define.amd?define(["exports","jquery"],t):t(e.bootstrap={},e.jQuery)}(this,function(e,t){"use strict";function i(e,t){for(var n=0;n<t.length;n++){var i=t[n];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(e,i.key,i)}}function s(e,t,n){return t&&i(e.prototype,t),n&&i(e,n),e}function l(r){for(var e=1;e<arguments.length;e++){var o=null!=arguments[e]?arguments[e]:{},t=Object.keys(o);"function"==typeof Object.getOwnPropertySymbols&&(t=t.concat(Object.getOwnPropertySymbols(o).filter(function(e){return Object.getOwnPropertyDescriptor(o,e).enumerable}))),t.forEach(function(e){var t,n,i;t=r,i=o[n=e],n in t?Object.defineProperty(t,n,{value:i,enumerable:!0,configurable:!0,writable:!0}):t[n]=i})}return r}for(var r,n,o,a,c,u,f,h,d,p,m,g,_,v,y,E,b,w,C,T,S,D,A,I,O,N,k,x,P,L,j,H,M,F,W,R,U,B,q,K,Q,Y,V,z,G,J,Z,X,$,ee,te,ne,ie,re,oe,se,ae,le,ce,ue,fe,he,de,pe,me,ge,_e,ve,ye,Ee,be,we=function(i){var t="transitionend";function e(e){var t=this,n=!1;return i(this).one(l.TRANSITION_END,function(){n=!0}),setTimeout(function(){n||l.triggerTransitionEnd(t)},e),this}var l={TRANSITION_END:"bsTransitionEnd",getUID:function(e){for(;e+=~~(1e6*Math.random()),document.getElementById(e););return e},getSelectorFromElement:function(e){var t=e.getAttribute("data-target");t&&"#"!==t||(t=e.getAttribute("href")||"");try{return document.querySelector(t)?t:null}catch(e){return null}},getTransitionDurationFromElement:function(e){if(!e)return 0;var t=i(e).css("transition-duration");return parseFloat(t)?(t=t.split(",")[0],1e3*parseFloat(t)):0},reflow:function(e){return e.offsetHeight},triggerTransitionEnd:function(e){i(e).trigger(t)},supportsTransitionEnd:function(){return Boolean(t)},isElement:function(e){return(e[0]||e).nodeType},typeCheckConfig:function(e,t,n){for(var i in n)if(Object.prototype.hasOwnProperty.call(n,i)){var r=n[i],o=t[i],s=o&&l.isElement(o)?"element":(a=o,{}.toString.call(a).match(/\s([a-z]+)/i)[1].toLowerCase());if(!new RegExp(r).test(s))throw new Error(e.toUpperCase()+': Option "'+i+'" provided type "'+s+'" but expected type "'+r+'".')}var a}};return i.fn.emulateTransitionEnd=e,i.event.special[l.TRANSITION_END]={bindType:t,delegateType:t,handle:function(e){if(i(e.target).is(this))return e.handleObj.handler.apply(this,arguments)}},l}(t=t&&t.hasOwnProperty("default")?t.default:t),Ce=(n="alert",a="."+(o="bs.alert"),c=(r=t).fn[n],u={CLOSE:"close"+a,CLOSED:"closed"+a,CLICK_DATA_API:"click"+a+".data-api"},f="alert",h="fade",d="show",p=function(){function i(e){this._element=e}var e=i.prototype;return e.close=function(e){var t=this._element;e&&(t=this._getRootElement(e)),this._triggerCloseEvent(t).isDefaultPrevented()||this._removeElement(t)},e.dispose=function(){r.removeData(this._element,o),this._element=null},e._getRootElement=function(e){var t=we.getSelectorFromElement(e),n=!1;return t&&(n=document.querySelector(t)),n||(n=r(e).closest("."+f)[0]),n},e._triggerCloseEvent=function(e){var t=r.Event(u.CLOSE);return r(e).trigger(t),t},e._removeElement=function(t){var n=this;if(r(t).removeClass(d),r(t).hasClass(h)){var e=we.getTransitionDurationFromElement(t);r(t).one(we.TRANSITION_END,function(e){return n._destroyElement(t,e)}).emulateTransitionEnd(e)}else this._destroyElement(t)},e._destroyElement=function(e){r(e).detach().trigger(u.CLOSED).remove()},i._jQueryInterface=function(n){return this.each(function(){var e=r(this),t=e.data(o);t||(t=new i(this),e.data(o,t)),"close"===n&&t[n](this)})},i._handleDismiss=function(t){return function(e){e&&e.preventDefault(),t.close(this)}},s(i,null,[{key:"VERSION",get:function(){return"4.1.3"}}]),i}(),r(document).on(u.CLICK_DATA_API,'[data-dismiss="alert"]',p._handleDismiss(new p)),r.fn[n]=p._jQueryInterface,r.fn[n].Constructor=p,r.fn[n].noConflict=function(){return r.fn[n]=c,p._jQueryInterface},p),Te=(g="button",v="."+(_="bs.button"),y=".data-api",E=(m=t).fn[g],b="active",w="btn",T='[data-toggle^="button"]',S='[data-toggle="buttons"]',D="input",A=".active",I=".btn",O={CLICK_DATA_API:"click"+v+y,FOCUS_BLUR_DATA_API:(C="focus")+v+y+" blur"+v+y},N=function(){function n(e){this._element=e}var e=n.prototype;return e.toggle=function(){var e=!0,t=!0,n=m(this._element).closest(S)[0];if(n){var i=this._element.querySelector(D);if(i){if("radio"===i.type)if(i.checked&&this._element.classList.contains(b))e=!1;else{var r=n.querySelector(A);r&&m(r).removeClass(b)}if(e){if(i.hasAttribute("disabled")||n.hasAttribute("disabled")||i.classList.contains("disabled")||n.classList.contains("disabled"))return;i.checked=!this._element.classList.contains(b),m(i).trigger("change")}i.focus(),t=!1}}t&&this._element.setAttribute("aria-pressed",!this._element.classList.contains(b)),e&&m(this._element).toggleClass(b)},e.dispose=function(){m.removeData(this._element,_),this._element=null},n._jQueryInterface=function(t){return this.each(function(){var e=m(this).data(_);e||(e=new n(this),m(this).data(_,e)),"toggle"===t&&e[t]()})},s(n,null,[{key:"VERSION",get:function(){return"4.1.3"}}]),n}(),m(document).on(O.CLICK_DATA_API,T,function(e){e.preventDefault();var t=e.target;m(t).hasClass(w)||(t=m(t).closest(I)),N._jQueryInterface.call(m(t),"toggle")}).on(O.FOCUS_BLUR_DATA_API,T,function(e){var t=m(e.target).closest(I)[0];m(t).toggleClass(C,/^focus(in)?$/.test(e.type))}),m.fn[g]=N._jQueryInterface,m.fn[g].Constructor=N,m.fn[g].noConflict=function(){return m.fn[g]=E,N._jQueryInterface},N),Se=(x="carousel",L="."+(P="bs.carousel"),j=".data-api",H=(k=t).fn[x],M={interval:5e3,keyboard:!0,slide:!1,pause:"hover",wrap:!0},F={interval:"(number|boolean)",keyboard:"boolean",slide:"(boolean|string)",pause:"(string|boolean)",wrap:"boolean"},W="next",R="prev",U="left",B="right",q={SLIDE:"slide"+L,SLID:"slid"+L,KEYDOWN:"keydown"+L,MOUSEENTER:"mouseenter"+L,MOUSELEAVE:"mouseleave"+L,TOUCHEND:"touchend"+L,LOAD_DATA_API:"load"+L+j,CLICK_DATA_API:"click"+L+j},K="carousel",Q="active",Y="slide",V="carousel-item-right",z="carousel-item-left",G="carousel-item-next",J="carousel-item-prev",Z=".active",X=".active.carousel-item",$=".carousel-item",ee=".carousel-item-next, .carousel-item-prev",te=".carousel-indicators",ne="[data-slide], [data-slide-to]",ie='[data-ride="carousel"]',re=function(){function o(e,t){this._items=null,this._interval=null,this._activeElement=null,this._isPaused=!1,this._isSliding=!1,this.touchTimeout=null,this._config=this._getConfig(t),this._element=k(e)[0],this._indicatorsElement=this._element.querySelector(te),this._addEventListeners()}var e=o.prototype;return e.next=function(){this._isSliding||this._slide(W)},e.nextWhenVisible=function(){!document.hidden&&k(this._element).is(":visible")&&"hidden"!==k(this._element).css("visibility")&&this.next()},e.prev=function(){this._isSliding||this._slide(R)},e.pause=function(e){e||(this._isPaused=!0),this._element.querySelector(ee)&&(we.triggerTransitionEnd(this._element),this.cycle(!0)),clearInterval(this._interval),this._interval=null},e.cycle=function(e){e||(this._isPaused=!1),this._interval&&(clearInterval(this._interval),this._interval=null),this._config.interval&&!this._isPaused&&(this._interval=setInterval((document.visibilityState?this.nextWhenVisible:this.next).bind(this),this._config.interval))},e.to=function(e){var t=this;this._activeElement=this._element.querySelector(X);var n=this._getItemIndex(this._activeElement);if(!(e>this._items.length-1||e<0))if(this._isSliding)k(this._element).one(q.SLID,function(){return t.to(e)});else{if(n===e)return this.pause(),void this.cycle();var i=n<e?W:R;this._slide(i,this._items[e])}},e.dispose=function(){k(this._element).off(L),k.removeData(this._element,P),this._items=null,this._config=null,this._element=null,this._interval=null,this._isPaused=null,this._isSliding=null,this._activeElement=null,this._indicatorsElement=null},e._getConfig=function(e){return e=l({},M,e),we.typeCheckConfig(x,e,F),e},e._addEventListeners=function(){var t=this;this._config.keyboard&&k(this._element).on(q.KEYDOWN,function(e){return t._keydown(e)}),"hover"===this._config.pause&&(k(this._element).on(q.MOUSEENTER,function(e){return t.pause(e)}).on(q.MOUSELEAVE,function(e){return t.cycle(e)}),"ontouchstart"in document.documentElement&&k(this._element).on(q.TOUCHEND,function(){t.pause(),t.touchTimeout&&clearTimeout(t.touchTimeout),t.touchTimeout=setTimeout(function(e){return t.cycle(e)},500+t._config.interval)}))},e._keydown=function(e){if(!/input|textarea/i.test(e.target.tagName))switch(e.which){case 37:e.preventDefault(),this.prev();break;case 39:e.preventDefault(),this.next()}},e._getItemIndex=function(e){return this._items=e&&e.parentNode?[].slice.call(e.parentNode.querySelectorAll($)):[],this._items.indexOf(e)},e._getItemByDirection=function(e,t){var n=e===W,i=e===R,r=this._getItemIndex(t),o=this._items.length-1;if((i&&0===r||n&&r===o)&&!this._config.wrap)return t;var s=(r+(e===R?-1:1))%this._items.length;return-1===s?this._items[this._items.length-1]:this._items[s]},e._triggerSlideEvent=function(e,t){var n=this._getItemIndex(e),i=this._getItemIndex(this._element.querySelector(X)),r=k.Event(q.SLIDE,{relatedTarget:e,direction:t,from:i,to:n});return k(this._element).trigger(r),r},e._setActiveIndicatorElement=function(e){if(this._indicatorsElement){var t=[].slice.call(this._indicatorsElement.querySelectorAll(Z));k(t).removeClass(Q);var n=this._indicatorsElement.children[this._getItemIndex(e)];n&&k(n).addClass(Q)}},e._slide=function(e,t){var n,i,r,o=this,s=this._element.querySelector(X),a=this._getItemIndex(s),l=t||s&&this._getItemByDirection(e,s),c=this._getItemIndex(l),u=Boolean(this._interval);if(e===W?(n=z,i=G,r=U):(n=V,i=J,r=B),l&&k(l).hasClass(Q))this._isSliding=!1;else if(!this._triggerSlideEvent(l,r).isDefaultPrevented()&&s&&l){this._isSliding=!0,u&&this.pause(),this._setActiveIndicatorElement(l);var f=k.Event(q.SLID,{relatedTarget:l,direction:r,from:a,to:c});if(k(this._element).hasClass(Y)){k(l).addClass(i),we.reflow(l),k(s).addClass(n),k(l).addClass(n);var h=we.getTransitionDurationFromElement(s);k(s).one(we.TRANSITION_END,function(){k(l).removeClass(n+" "+i).addClass(Q),k(s).removeClass(Q+" "+i+" "+n),o._isSliding=!1,setTimeout(function(){return k(o._element).trigger(f)},0)}).emulateTransitionEnd(h)}else k(s).removeClass(Q),k(l).addClass(Q),this._isSliding=!1,k(this._element).trigger(f);u&&this.cycle()}},o._jQueryInterface=function(i){return this.each(function(){var e=k(this).data(P),t=l({},M,k(this).data());"object"==typeof i&&(t=l({},t,i));var n="string"==typeof i?i:t.slide;if(e||(e=new o(this,t),k(this).data(P,e)),"number"==typeof i)e.to(i);else if("string"==typeof n){if("undefined"==typeof e[n])throw new TypeError('No method named "'+n+'"');e[n]()}else t.interval&&(e.pause(),e.cycle())})},o._dataApiClickHandler=function(e){var t=we.getSelectorFromElement(this);if(t){var n=k(t)[0];if(n&&k(n).hasClass(K)){var i=l({},k(n).data(),k(this).data()),r=this.getAttribute("data-slide-to");r&&(i.interval=!1),o._jQueryInterface.call(k(n),i),r&&k(n).data(P).to(r),e.preventDefault()}}},s(o,null,[{key:"VERSION",get:function(){return"4.1.3"}},{key:"Default",get:function(){return M}}]),o}(),k(document).on(q.CLICK_DATA_API,ne,re._dataApiClickHandler),k(window).on(q.LOAD_DATA_API,function(){for(var e=[].slice.call(document.querySelectorAll(ie)),t=0,n=e.length;t<n;t++){var i=k(e[t]);re._jQueryInterface.call(i,i.data())}}),k.fn[x]=re._jQueryInterface,k.fn[x].Constructor=re,k.fn[x].noConflict=function(){return k.fn[x]=H,re._jQueryInterface},re),De=(se="collapse",le="."+(ae="bs.collapse"),ce=(oe=t).fn[se],ue={toggle:!0,parent:""},fe={toggle:"boolean",parent:"(string|element)"},he={SHOW:"show"+le,SHOWN:"shown"+le,HIDE:"hide"+le,HIDDEN:"hidden"+le,CLICK_DATA_API:"click"+le+".data-api"},de="show",pe="collapse",me="collapsing",ge="collapsed",_e="width",ve="height",ye=".show, .collapsing",Ee='[data-toggle="collapse"]',be=function(){function a(t,e){this._isTransitioning=!1,this._element=t,this._config=this._getConfig(e),this._triggerArray=oe.makeArray(document.querySelectorAll('[data-toggle="collapse"][href="#'+t.id+'"],[data-toggle="collapse"][data-target="#'+t.id+'"]'));for(var n=[].slice.call(document.querySelectorAll(Ee)),i=0,r=n.length;i<r;i++){var o=n[i],s=we.getSelectorFromElement(o),a=[].slice.call(document.querySelectorAll(s)).filter(function(e){return e===t});null!==s&&0<a.length&&(this._selector=s,this._triggerArray.push(o))}this._parent=this._config.parent?this._getParent():null,this._config.parent||this._addAriaAndCollapsedClass(this._element,this._triggerArray),this._config.toggle&&this.toggle()}var e=a.prototype;return e.toggle=function(){oe(this._element).hasClass(de)?this.hide():this.show()},e.show=function(){var e,t,n=this;if(!this._isTransitioning&&!oe(this._element).hasClass(de)&&(this._parent&&0===(e=[].slice.call(this._parent.querySelectorAll(ye)).filter(function(e){return e.getAttribute("data-parent")===n._config.parent})).length&&(e=null),!(e&&(t=oe(e).not(this._selector).data(ae))&&t._isTransitioning))){var i=oe.Event(he.SHOW);if(oe(this._element).trigger(i),!i.isDefaultPrevented()){e&&(a._jQueryInterface.call(oe(e).not(this._selector),"hide"),t||oe(e).data(ae,null));var r=this._getDimension();oe(this._element).removeClass(pe).addClass(me),this._element.style[r]=0,this._triggerArray.length&&oe(this._triggerArray).removeClass(ge).attr("aria-expanded",!0),this.setTransitioning(!0);var o="scroll"+(r[0].toUpperCase()+r.slice(1)),s=we.getTransitionDurationFromElement(this._element);oe(this._element).one(we.TRANSITION_END,function(){oe(n._element).removeClass(me).addClass(pe).addClass(de),n._element.style[r]="",n.setTransitioning(!1),oe(n._element).trigger(he.SHOWN)}).emulateTransitionEnd(s),this._element.style[r]=this._element[o]+"px"}}},e.hide=function(){var e=this;if(!this._isTransitioning&&oe(this._element).hasClass(de)){var t=oe.Event(he.HIDE);if(oe(this._element).trigger(t),!t.isDefaultPrevented()){var n=this._getDimension();this._element.style[n]=this._element.getBoundingClientRect()[n]+"px",we.reflow(this._element),oe(this._element).addClass(me).removeClass(pe).removeClass(de);var i=this._triggerArray.length;if(0<i)for(var r=0;r<i;r++){var o=this._triggerArray[r],s=we.getSelectorFromElement(o);if(null!==s)oe([].slice.call(document.querySelectorAll(s))).hasClass(de)||oe(o).addClass(ge).attr("aria-expanded",!1)}this.setTransitioning(!0);this._element.style[n]="";var a=we.getTransitionDurationFromElement(this._element);oe(this._element).one(we.TRANSITION_END,function(){e.setTransitioning(!1),oe(e._element).removeClass(me).addClass(pe).trigger(he.HIDDEN)}).emulateTransitionEnd(a)}}},e.setTransitioning=function(e){this._isTransitioning=e},e.dispose=function(){oe.removeData(this._element,ae),this._config=null,this._parent=null,this._element=null,this._triggerArray=null,this._isTransitioning=null},e._getConfig=function(e){return(e=l({},ue,e)).toggle=Boolean(e.toggle),we.typeCheckConfig(se,e,fe),e},e._getDimension=function(){return oe(this._element).hasClass(_e)?_e:ve},e._getParent=function(){var n=this,e=null;we.isElement(this._config.parent)?(e=this._config.parent,"undefined"!=typeof this._config.parent.jquery&&(e=this._config.parent[0])):e=document.querySelector(this._config.parent);var t='[data-toggle="collapse"][data-parent="'+this._config.parent+'"]',i=[].slice.call(e.querySelectorAll(t));return oe(i).each(function(e,t){n._addAriaAndCollapsedClass(a._getTargetFromElement(t),[t])}),e},e._addAriaAndCollapsedClass=function(e,t){if(e){var n=oe(e).hasClass(de);t.length&&oe(t).toggleClass(ge,!n).attr("aria-expanded",n)}},a._getTargetFromElement=function(e){var t=we.getSelectorFromElement(e);return t?document.querySelector(t):null},a._jQueryInterface=function(i){return this.each(function(){var e=oe(this),t=e.data(ae),n=l({},ue,e.data(),"object"==typeof i&&i?i:{});if(!t&&n.toggle&&/show|hide/.test(i)&&(n.toggle=!1),t||(t=new a(this,n),e.data(ae,t)),"string"==typeof i){if("undefined"==typeof t[i])throw new TypeError('No method named "'+i+'"');t[i]()}})},s(a,null,[{key:"VERSION",get:function(){return"4.1.3"}},{key:"Default",get:function(){return ue}}]),a}(),oe(document).on(he.CLICK_DATA_API,Ee,function(e){"A"===e.currentTarget.tagName&&e.preventDefault();var n=oe(this),t=we.getSelectorFromElement(this),i=[].slice.call(document.querySelectorAll(t));oe(i).each(function(){var e=oe(this),t=e.data(ae)?"toggle":n.data();be._jQueryInterface.call(e,t)})}),oe.fn[se]=be._jQueryInterface,oe.fn[se].Constructor=be,oe.fn[se].noConflict=function(){return oe.fn[se]=ce,be._jQueryInterface},be),Ae="undefined"!=typeof window&&"undefined"!=typeof document,Ie=["Edge","Trident","Firefox"],Oe=0,Ne=0;Ne<Ie.length;Ne+=1)if(Ae&&0<=navigator.userAgent.indexOf(Ie[Ne])){Oe=1;break}var ke=Ae&&window.Promise?function(e){var t=!1;return function(){t||(t=!0,window.Promise.resolve().then(function(){t=!1,e()}))}}:function(e){var t=!1;return function(){t||(t=!0,setTimeout(function(){t=!1,e()},Oe))}};function xe(e){return e&&"[object Function]"==={}.toString.call(e)}function Pe(e,t){if(1!==e.nodeType)return[];var n=getComputedStyle(e,null);return t?n[t]:n}function Le(e){return"HTML"===e.nodeName?e:e.parentNode||e.host}function je(e){if(!e)return document.body;switch(e.nodeName){case"HTML":case"BODY":return e.ownerDocument.body;case"#document":return e.body}var t=Pe(e),n=t.overflow,i=t.overflowX,r=t.overflowY;return/(auto|scroll|overlay)/.test(n+r+i)?e:je(Le(e))}var He=Ae&&!(!window.MSInputMethodContext||!document.documentMode),Me=Ae&&/MSIE 10/.test(navigator.userAgent);function Fe(e){return 11===e?He:10===e?Me:He||Me}function We(e){if(!e)return document.documentElement;for(var t=Fe(10)?document.body:null,n=e.offsetParent;n===t&&e.nextElementSibling;)n=(e=e.nextElementSibling).offsetParent;var i=n&&n.nodeName;return i&&"BODY"!==i&&"HTML"!==i?-1!==["TD","TABLE"].indexOf(n.nodeName)&&"static"===Pe(n,"position")?We(n):n:e?e.ownerDocument.documentElement:document.documentElement}function Re(e){return null!==e.parentNode?Re(e.parentNode):e}function Ue(e,t){if(!(e&&e.nodeType&&t&&t.nodeType))return document.documentElement;var n=e.compareDocumentPosition(t)&Node.DOCUMENT_POSITION_FOLLOWING,i=n?e:t,r=n?t:e,o=document.createRange();o.setStart(i,0),o.setEnd(r,0);var s,a,l=o.commonAncestorContainer;if(e!==l&&t!==l||i.contains(r))return"BODY"===(a=(s=l).nodeName)||"HTML"!==a&&We(s.firstElementChild)!==s?We(l):l;var c=Re(e);return c.host?Ue(c.host,t):Ue(e,Re(t).host)}function Be(e){var t="top"===(1<arguments.length&&void 0!==arguments[1]?arguments[1]:"top")?"scrollTop":"scrollLeft",n=e.nodeName;if("BODY"===n||"HTML"===n){var i=e.ownerDocument.documentElement;return(e.ownerDocument.scrollingElement||i)[t]}return e[t]}function qe(e,t){var n="x"===t?"Left":"Top",i="Left"===n?"Right":"Bottom";return parseFloat(e["border"+n+"Width"],10)+parseFloat(e["border"+i+"Width"],10)}function Ke(e,t,n,i){return Math.max(t["offset"+e],t["scroll"+e],n["client"+e],n["offset"+e],n["scroll"+e],Fe(10)?n["offset"+e]+i["margin"+("Height"===e?"Top":"Left")]+i["margin"+("Height"===e?"Bottom":"Right")]:0)}function Qe(){var e=document.body,t=document.documentElement,n=Fe(10)&&getComputedStyle(t);return{height:Ke("Height",e,t,n),width:Ke("Width",e,t,n)}}var Ye=function(){function i(e,t){for(var n=0;n<t.length;n++){var i=t[n];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(e,i.key,i)}}return function(e,t,n){return t&&i(e.prototype,t),n&&i(e,n),e}}(),Ve=function(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e},ze=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var i in n)Object.prototype.hasOwnProperty.call(n,i)&&(e[i]=n[i])}return e};function Ge(e){return ze({},e,{right:e.left+e.width,bottom:e.top+e.height})}function Je(e){var t={};try{if(Fe(10)){t=e.getBoundingClientRect();var n=Be(e,"top"),i=Be(e,"left");t.top+=n,t.left+=i,t.bottom+=n,t.right+=i}else t=e.getBoundingClientRect()}catch(e){}var r={left:t.left,top:t.top,width:t.right-t.left,height:t.bottom-t.top},o="HTML"===e.nodeName?Qe():{},s=o.width||e.clientWidth||r.right-r.left,a=o.height||e.clientHeight||r.bottom-r.top,l=e.offsetWidth-s,c=e.offsetHeight-a;if(l||c){var u=Pe(e);l-=qe(u,"x"),c-=qe(u,"y"),r.width-=l,r.height-=c}return Ge(r)}function Ze(e,t){var n=2<arguments.length&&void 0!==arguments[2]&&arguments[2],i=Fe(10),r="HTML"===t.nodeName,o=Je(e),s=Je(t),a=je(e),l=Pe(t),c=parseFloat(l.borderTopWidth,10),u=parseFloat(l.borderLeftWidth,10);n&&"HTML"===t.nodeName&&(s.top=Math.max(s.top,0),s.left=Math.max(s.left,0));var f=Ge({top:o.top-s.top-c,left:o.left-s.left-u,width:o.width,height:o.height});if(f.marginTop=0,f.marginLeft=0,!i&&r){var h=parseFloat(l.marginTop,10),d=parseFloat(l.marginLeft,10);f.top-=c-h,f.bottom-=c-h,f.left-=u-d,f.right-=u-d,f.marginTop=h,f.marginLeft=d}return(i&&!n?t.contains(a):t===a&&"BODY"!==a.nodeName)&&(f=function(e,t){var n=2<arguments.length&&void 0!==arguments[2]&&arguments[2],i=Be(t,"top"),r=Be(t,"left"),o=n?-1:1;return e.top+=i*o,e.bottom+=i*o,e.left+=r*o,e.right+=r*o,e}(f,t)),f}function Xe(e){if(!e||!e.parentElement||Fe())return document.documentElement;for(var t=e.parentElement;t&&"none"===Pe(t,"transform");)t=t.parentElement;return t||document.documentElement}function $e(e,t,n,i){var r=4<arguments.length&&void 0!==arguments[4]&&arguments[4],o={top:0,left:0},s=r?Xe(e):Ue(e,t);if("viewport"===i)o=function(e){var t=1<arguments.length&&void 0!==arguments[1]&&arguments[1],n=e.ownerDocument.documentElement,i=Ze(e,n),r=Math.max(n.clientWidth,window.innerWidth||0),o=Math.max(n.clientHeight,window.innerHeight||0),s=t?0:Be(n),a=t?0:Be(n,"left");return Ge({top:s-i.top+i.marginTop,left:a-i.left+i.marginLeft,width:r,height:o})}(s,r);else{var a=void 0;"scrollParent"===i?"BODY"===(a=je(Le(t))).nodeName&&(a=e.ownerDocument.documentElement):a="window"===i?e.ownerDocument.documentElement:i;var l=Ze(a,s,r);if("HTML"!==a.nodeName||function e(t){var n=t.nodeName;return"BODY"!==n&&"HTML"!==n&&("fixed"===Pe(t,"position")||e(Le(t)))}(s))o=l;else{var c=Qe(),u=c.height,f=c.width;o.top+=l.top-l.marginTop,o.bottom=u+l.top,o.left+=l.left-l.marginLeft,o.right=f+l.left}}return o.left+=n,o.top+=n,o.right-=n,o.bottom-=n,o}function et(e,t,i,n,r){var o=5<arguments.length&&void 0!==arguments[5]?arguments[5]:0;if(-1===e.indexOf("auto"))return e;var s=$e(i,n,o,r),a={top:{width:s.width,height:t.top-s.top},right:{width:s.right-t.right,height:s.height},bottom:{width:s.width,height:s.bottom-t.bottom},left:{width:t.left-s.left,height:s.height}},l=Object.keys(a).map(function(e){return ze({key:e},a[e],{area:(t=a[e],t.width*t.height)});var t}).sort(function(e,t){return t.area-e.area}),c=l.filter(function(e){var t=e.width,n=e.height;return t>=i.clientWidth&&n>=i.clientHeight}),u=0<c.length?c[0].key:l[0].key,f=e.split("-")[1];return u+(f?"-"+f:"")}function tt(e,t,n){var i=3<arguments.length&&void 0!==arguments[3]?arguments[3]:null;return Ze(n,i?Xe(t):Ue(t,n),i)}function nt(e){var t=getComputedStyle(e),n=parseFloat(t.marginTop)+parseFloat(t.marginBottom),i=parseFloat(t.marginLeft)+parseFloat(t.marginRight);return{width:e.offsetWidth+i,height:e.offsetHeight+n}}function it(e){var t={left:"right",right:"left",bottom:"top",top:"bottom"};return e.replace(/left|right|bottom|top/g,function(e){return t[e]})}function rt(e,t,n){n=n.split("-")[0];var i=nt(e),r={width:i.width,height:i.height},o=-1!==["right","left"].indexOf(n),s=o?"top":"left",a=o?"left":"top",l=o?"height":"width",c=o?"width":"height";return r[s]=t[s]+t[l]/2-i[l]/2,r[a]=n===a?t[a]-i[c]:t[it(a)],r}function ot(e,t){return Array.prototype.find?e.find(t):e.filter(t)[0]}function st(e,n,t){return(void 0===t?e:e.slice(0,function(e,t,n){if(Array.prototype.findIndex)return e.findIndex(function(e){return e[t]===n});var i=ot(e,function(e){return e[t]===n});return e.indexOf(i)}(e,"name",t))).forEach(function(e){e.function&&console.warn("`modifier.function` is deprecated, use `modifier.fn`!");var t=e.function||e.fn;e.enabled&&xe(t)&&(n.offsets.popper=Ge(n.offsets.popper),n.offsets.reference=Ge(n.offsets.reference),n=t(n,e))}),n}function at(e,n){return e.some(function(e){var t=e.name;return e.enabled&&t===n})}function lt(e){for(var t=[!1,"ms","Webkit","Moz","O"],n=e.charAt(0).toUpperCase()+e.slice(1),i=0;i<t.length;i++){var r=t[i],o=r?""+r+n:e;if("undefined"!=typeof document.body.style[o])return o}return null}function ct(e){var t=e.ownerDocument;return t?t.defaultView:window}function ut(e,t,n,i){n.updateBound=i,ct(e).addEventListener("resize",n.updateBound,{passive:!0});var r=je(e);return function e(t,n,i,r){var o="BODY"===t.nodeName,s=o?t.ownerDocument.defaultView:t;s.addEventListener(n,i,{passive:!0}),o||e(je(s.parentNode),n,i,r),r.push(s)}(r,"scroll",n.updateBound,n.scrollParents),n.scrollElement=r,n.eventsEnabled=!0,n}function ft(){var e,t;this.state.eventsEnabled&&(cancelAnimationFrame(this.scheduleUpdate),this.state=(e=this.reference,t=this.state,ct(e).removeEventListener("resize",t.updateBound),t.scrollParents.forEach(function(e){e.removeEventListener("scroll",t.updateBound)}),t.updateBound=null,t.scrollParents=[],t.scrollElement=null,t.eventsEnabled=!1,t))}function ht(e){return""!==e&&!isNaN(parseFloat(e))&&isFinite(e)}function dt(n,i){Object.keys(i).forEach(function(e){var t="";-1!==["width","height","top","right","bottom","left"].indexOf(e)&&ht(i[e])&&(t="px"),n.style[e]=i[e]+t})}function pt(e,t,n){var i=ot(e,function(e){return e.name===t}),r=!!i&&e.some(function(e){return e.name===n&&e.enabled&&e.order<i.order});if(!r){var o="`"+t+"`",s="`"+n+"`";console.warn(s+" modifier is required by "+o+" modifier in order to work, be sure to include it before "+o+"!")}return r}var mt=["auto-start","auto","auto-end","top-start","top","top-end","right-start","right","right-end","bottom-end","bottom","bottom-start","left-end","left","left-start"],gt=mt.slice(3);function _t(e){var t=1<arguments.length&&void 0!==arguments[1]&&arguments[1],n=gt.indexOf(e),i=gt.slice(n+1).concat(gt.slice(0,n));return t?i.reverse():i}var vt="flip",yt="clockwise",Et="counterclockwise";function bt(e,r,o,t){var s=[0,0],a=-1!==["right","left"].indexOf(t),n=e.split(/(\+|\-)/).map(function(e){return e.trim()}),i=n.indexOf(ot(n,function(e){return-1!==e.search(/,|\s/)}));n[i]&&-1===n[i].indexOf(",")&&console.warn("Offsets separated by white space(s) are deprecated, use a comma (,) instead.");var l=/\s*,\s*|\s+/,c=-1!==i?[n.slice(0,i).concat([n[i].split(l)[0]]),[n[i].split(l)[1]].concat(n.slice(i+1))]:[n];return(c=c.map(function(e,t){var n=(1===t?!a:a)?"height":"width",i=!1;return e.reduce(function(e,t){return""===e[e.length-1]&&-1!==["+","-"].indexOf(t)?(e[e.length-1]=t,i=!0,e):i?(e[e.length-1]+=t,i=!1,e):e.concat(t)},[]).map(function(e){return function(e,t,n,i){var r=e.match(/((?:\-|\+)?\d*\.?\d*)(.*)/),o=+r[1],s=r[2];if(!o)return e;if(0===s.indexOf("%")){var a=void 0;switch(s){case"%p":a=n;break;case"%":case"%r":default:a=i}return Ge(a)[t]/100*o}if("vh"===s||"vw"===s)return("vh"===s?Math.max(document.documentElement.clientHeight,window.innerHeight||0):Math.max(document.documentElement.clientWidth,window.innerWidth||0))/100*o;return o}(e,n,r,o)})})).forEach(function(n,i){n.forEach(function(e,t){ht(e)&&(s[i]+=e*("-"===n[t-1]?-1:1))})}),s}var wt={placement:"bottom",positionFixed:!1,eventsEnabled:!0,removeOnDestroy:!1,onCreate:function(){},onUpdate:function(){},modifiers:{shift:{order:100,enabled:!0,fn:function(e){var t=e.placement,n=t.split("-")[0],i=t.split("-")[1];if(i){var r=e.offsets,o=r.reference,s=r.popper,a=-1!==["bottom","top"].indexOf(n),l=a?"left":"top",c=a?"width":"height",u={start:Ve({},l,o[l]),end:Ve({},l,o[l]+o[c]-s[c])};e.offsets.popper=ze({},s,u[i])}return e}},offset:{order:200,enabled:!0,fn:function(e,t){var n=t.offset,i=e.placement,r=e.offsets,o=r.popper,s=r.reference,a=i.split("-")[0],l=void 0;return l=ht(+n)?[+n,0]:bt(n,o,s,a),"left"===a?(o.top+=l[0],o.left-=l[1]):"right"===a?(o.top+=l[0],o.left+=l[1]):"top"===a?(o.left+=l[0],o.top-=l[1]):"bottom"===a&&(o.left+=l[0],o.top+=l[1]),e.popper=o,e},offset:0},preventOverflow:{order:300,enabled:!0,fn:function(e,i){var t=i.boundariesElement||We(e.instance.popper);e.instance.reference===t&&(t=We(t));var n=lt("transform"),r=e.instance.popper.style,o=r.top,s=r.left,a=r[n];r.top="",r.left="",r[n]="";var l=$e(e.instance.popper,e.instance.reference,i.padding,t,e.positionFixed);r.top=o,r.left=s,r[n]=a,i.boundaries=l;var c=i.priority,u=e.offsets.popper,f={primary:function(e){var t=u[e];return u[e]<l[e]&&!i.escapeWithReference&&(t=Math.max(u[e],l[e])),Ve({},e,t)},secondary:function(e){var t="right"===e?"left":"top",n=u[t];return u[e]>l[e]&&!i.escapeWithReference&&(n=Math.min(u[t],l[e]-("right"===e?u.width:u.height))),Ve({},t,n)}};return c.forEach(function(e){var t=-1!==["left","top"].indexOf(e)?"primary":"secondary";u=ze({},u,f[t](e))}),e.offsets.popper=u,e},priority:["left","right","top","bottom"],padding:5,boundariesElement:"scrollParent"},keepTogether:{order:400,enabled:!0,fn:function(e){var t=e.offsets,n=t.popper,i=t.reference,r=e.placement.split("-")[0],o=Math.floor,s=-1!==["top","bottom"].indexOf(r),a=s?"right":"bottom",l=s?"left":"top",c=s?"width":"height";return n[a]<o(i[l])&&(e.offsets.popper[l]=o(i[l])-n[c]),n[l]>o(i[a])&&(e.offsets.popper[l]=o(i[a])),e}},arrow:{order:500,enabled:!0,fn:function(e,t){var n;if(!pt(e.instance.modifiers,"arrow","keepTogether"))return e;var i=t.element;if("string"==typeof i){if(!(i=e.instance.popper.querySelector(i)))return e}else if(!e.instance.popper.contains(i))return console.warn("WARNING: `arrow.element` must be child of its popper element!"),e;var r=e.placement.split("-")[0],o=e.offsets,s=o.popper,a=o.reference,l=-1!==["left","right"].indexOf(r),c=l?"height":"width",u=l?"Top":"Left",f=u.toLowerCase(),h=l?"left":"top",d=l?"bottom":"right",p=nt(i)[c];a[d]-p<s[f]&&(e.offsets.popper[f]-=s[f]-(a[d]-p)),a[f]+p>s[d]&&(e.offsets.popper[f]+=a[f]+p-s[d]),e.offsets.popper=Ge(e.offsets.popper);var m=a[f]+a[c]/2-p/2,g=Pe(e.instance.popper),_=parseFloat(g["margin"+u],10),v=parseFloat(g["border"+u+"Width"],10),y=m-e.offsets.popper[f]-_-v;return y=Math.max(Math.min(s[c]-p,y),0),e.arrowElement=i,e.offsets.arrow=(Ve(n={},f,Math.round(y)),Ve(n,h,""),n),e},element:"[x-arrow]"},flip:{order:600,enabled:!0,fn:function(p,m){if(at(p.instance.modifiers,"inner"))return p;if(p.flipped&&p.placement===p.originalPlacement)return p;var g=$e(p.instance.popper,p.instance.reference,m.padding,m.boundariesElement,p.positionFixed),_=p.placement.split("-")[0],v=it(_),y=p.placement.split("-")[1]||"",E=[];switch(m.behavior){case vt:E=[_,v];break;case yt:E=_t(_);break;case Et:E=_t(_,!0);break;default:E=m.behavior}return E.forEach(function(e,t){if(_!==e||E.length===t+1)return p;_=p.placement.split("-")[0],v=it(_);var n,i=p.offsets.popper,r=p.offsets.reference,o=Math.floor,s="left"===_&&o(i.right)>o(r.left)||"right"===_&&o(i.left)<o(r.right)||"top"===_&&o(i.bottom)>o(r.top)||"bottom"===_&&o(i.top)<o(r.bottom),a=o(i.left)<o(g.left),l=o(i.right)>o(g.right),c=o(i.top)<o(g.top),u=o(i.bottom)>o(g.bottom),f="left"===_&&a||"right"===_&&l||"top"===_&&c||"bottom"===_&&u,h=-1!==["top","bottom"].indexOf(_),d=!!m.flipVariations&&(h&&"start"===y&&a||h&&"end"===y&&l||!h&&"start"===y&&c||!h&&"end"===y&&u);(s||f||d)&&(p.flipped=!0,(s||f)&&(_=E[t+1]),d&&(y="end"===(n=y)?"start":"start"===n?"end":n),p.placement=_+(y?"-"+y:""),p.offsets.popper=ze({},p.offsets.popper,rt(p.instance.popper,p.offsets.reference,p.placement)),p=st(p.instance.modifiers,p,"flip"))}),p},behavior:"flip",padding:5,boundariesElement:"viewport"},inner:{order:700,enabled:!1,fn:function(e){var t=e.placement,n=t.split("-")[0],i=e.offsets,r=i.popper,o=i.reference,s=-1!==["left","right"].indexOf(n),a=-1===["top","left"].indexOf(n);return r[s?"left":"top"]=o[n]-(a?r[s?"width":"height"]:0),e.placement=it(t),e.offsets.popper=Ge(r),e}},hide:{order:800,enabled:!0,fn:function(e){if(!pt(e.instance.modifiers,"hide","preventOverflow"))return e;var t=e.offsets.reference,n=ot(e.instance.modifiers,function(e){return"preventOverflow"===e.name}).boundaries;if(t.bottom<n.top||t.left>n.right||t.top>n.bottom||t.right<n.left){if(!0===e.hide)return e;e.hide=!0,e.attributes["x-out-of-boundaries"]=""}else{if(!1===e.hide)return e;e.hide=!1,e.attributes["x-out-of-boundaries"]=!1}return e}},computeStyle:{order:850,enabled:!0,fn:function(e,t){var n=t.x,i=t.y,r=e.offsets.popper,o=ot(e.instance.modifiers,function(e){return"applyStyle"===e.name}).gpuAcceleration;void 0!==o&&console.warn("WARNING: `gpuAcceleration` option moved to `computeStyle` modifier and will not be supported in future versions of Popper.js!");var s=void 0!==o?o:t.gpuAcceleration,a=Je(We(e.instance.popper)),l={position:r.position},c={left:Math.floor(r.left),top:Math.round(r.top),bottom:Math.round(r.bottom),right:Math.floor(r.right)},u="bottom"===n?"top":"bottom",f="right"===i?"left":"right",h=lt("transform"),d=void 0,p=void 0;if(p="bottom"===u?-a.height+c.bottom:c.top,d="right"===f?-a.width+c.right:c.left,s&&h)l[h]="translate3d("+d+"px, "+p+"px, 0)",l[u]=0,l[f]=0,l.willChange="transform";else{var m="bottom"===u?-1:1,g="right"===f?-1:1;l[u]=p*m,l[f]=d*g,l.willChange=u+", "+f}var _={"x-placement":e.placement};return e.attributes=ze({},_,e.attributes),e.styles=ze({},l,e.styles),e.arrowStyles=ze({},e.offsets.arrow,e.arrowStyles),e},gpuAcceleration:!0,x:"bottom",y:"right"},applyStyle:{order:900,enabled:!0,fn:function(e){var t,n;return dt(e.instance.popper,e.styles),t=e.instance.popper,n=e.attributes,Object.keys(n).forEach(function(e){!1!==n[e]?t.setAttribute(e,n[e]):t.removeAttribute(e)}),e.arrowElement&&Object.keys(e.arrowStyles).length&&dt(e.arrowElement,e.arrowStyles),e},onLoad:function(e,t,n,i,r){var o=tt(r,t,e,n.positionFixed),s=et(n.placement,o,t,e,n.modifiers.flip.boundariesElement,n.modifiers.flip.padding);return t.setAttribute("x-placement",s),dt(t,{position:n.positionFixed?"fixed":"absolute"}),n},gpuAcceleration:void 0}}},Ct=function(){function o(e,t){var n=this,i=2<arguments.length&&void 0!==arguments[2]?arguments[2]:{};!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,o),this.scheduleUpdate=function(){return requestAnimationFrame(n.update)},this.update=ke(this.update.bind(this)),this.options=ze({},o.Defaults,i),this.state={isDestroyed:!1,isCreated:!1,scrollParents:[]},this.reference=e&&e.jquery?e[0]:e,this.popper=t&&t.jquery?t[0]:t,this.options.modifiers={},Object.keys(ze({},o.Defaults.modifiers,i.modifiers)).forEach(function(e){n.options.modifiers[e]=ze({},o.Defaults.modifiers[e]||{},i.modifiers?i.modifiers[e]:{})}),this.modifiers=Object.keys(this.options.modifiers).map(function(e){return ze({name:e},n.options.modifiers[e])}).sort(function(e,t){return e.order-t.order}),this.modifiers.forEach(function(e){e.enabled&&xe(e.onLoad)&&e.onLoad(n.reference,n.popper,n.options,e,n.state)}),this.update();var r=this.options.eventsEnabled;r&&this.enableEventListeners(),this.state.eventsEnabled=r}return Ye(o,[{key:"update",value:function(){return function(){if(!this.state.isDestroyed){var e={instance:this,styles:{},arrowStyles:{},attributes:{},flipped:!1,offsets:{}};e.offsets.reference=tt(this.state,this.popper,this.reference,this.options.positionFixed),e.placement=et(this.options.placement,e.offsets.reference,this.popper,this.reference,this.options.modifiers.flip.boundariesElement,this.options.modifiers.flip.padding),e.originalPlacement=e.placement,e.positionFixed=this.options.positionFixed,e.offsets.popper=rt(this.popper,e.offsets.reference,e.placement),e.offsets.popper.position=this.options.positionFixed?"fixed":"absolute",e=st(this.modifiers,e),this.state.isCreated?this.options.onUpdate(e):(this.state.isCreated=!0,this.options.onCreate(e))}}.call(this)}},{key:"destroy",value:function(){return function(){return this.state.isDestroyed=!0,at(this.modifiers,"applyStyle")&&(this.popper.removeAttribute("x-placement"),this.popper.style.position="",this.popper.style.top="",this.popper.style.left="",this.popper.style.right="",this.popper.style.bottom="",this.popper.style.willChange="",this.popper.style[lt("transform")]=""),this.disableEventListeners(),this.options.removeOnDestroy&&this.popper.parentNode.removeChild(this.popper),this}.call(this)}},{key:"enableEventListeners",value:function(){return function(){this.state.eventsEnabled||(this.state=ut(this.reference,this.options,this.state,this.scheduleUpdate))}.call(this)}},{key:"disableEventListeners",value:function(){return ft.call(this)}}]),o}();Ct.Utils=("undefined"!=typeof window?window:global).PopperUtils,Ct.placements=mt,Ct.Defaults=wt;var Tt,St,Dt,At,It,Ot,Nt,kt,xt,Pt,Lt,jt,Ht,Mt,Ft,Wt,Rt,Ut,Bt,qt,Kt,Qt,Yt,Vt,zt,Gt,Jt,Zt,Xt,$t,en,tn,nn,rn,on,sn,an,ln,cn,un,fn,hn,dn,pn,mn,gn,_n,vn,yn,En,bn,wn,Cn,Tn,Sn,Dn,An,In,On,Nn,kn,xn,Pn,Ln,jn,Hn,Mn,Fn,Wn,Rn,Un,Bn,qn,Kn,Qn,Yn,Vn,zn,Gn,Jn,Zn,Xn,$n,ei,ti,ni,ii,ri,oi,si,ai,li,ci,ui,fi,hi,di,pi,mi,gi,_i,vi,yi,Ei,bi,wi,Ci,Ti,Si,Di,Ai,Ii,Oi,Ni,ki,xi,Pi,Li,ji,Hi,Mi,Fi,Wi,Ri,Ui,Bi=(St="dropdown",At="."+(Dt="bs.dropdown"),It=".data-api",Ot=(Tt=t).fn[St],Nt=new RegExp("38|40|27"),kt={HIDE:"hide"+At,HIDDEN:"hidden"+At,SHOW:"show"+At,SHOWN:"shown"+At,CLICK:"click"+At,CLICK_DATA_API:"click"+At+It,KEYDOWN_DATA_API:"keydown"+At+It,KEYUP_DATA_API:"keyup"+At+It},xt="disabled",Pt="show",Lt="dropup",jt="dropright",Ht="dropleft",Mt="dropdown-menu-right",Ft="position-static",Wt='[data-toggle="dropdown"]',Rt=".dropdown form",Ut=".dropdown-menu",Bt=".navbar-nav",qt=".dropdown-menu .dropdown-item:not(.disabled):not(:disabled)",Kt="top-start",Qt="top-end",Yt="bottom-start",Vt="bottom-end",zt="right-start",Gt="left-start",Jt={offset:0,flip:!0,boundary:"scrollParent",reference:"toggle",display:"dynamic"},Zt={offset:"(number|string|function)",flip:"boolean",boundary:"(string|element)",reference:"(string|element)",display:"string"},Xt=function(){function c(e,t){this._element=e,this._popper=null,this._config=this._getConfig(t),this._menu=this._getMenuElement(),this._inNavbar=this._detectNavbar(),this._addEventListeners()}var e=c.prototype;return e.toggle=function(){if(!this._element.disabled&&!Tt(this._element).hasClass(xt)){var e=c._getParentFromElement(this._element),t=Tt(this._menu).hasClass(Pt);if(c._clearMenus(),!t){var n={relatedTarget:this._element},i=Tt.Event(kt.SHOW,n);if(Tt(e).trigger(i),!i.isDefaultPrevented()){if(!this._inNavbar){if("undefined"==typeof Ct)throw new TypeError("Bootstrap dropdown require Popper.js (https://popper.js.org)");var r=this._element;"parent"===this._config.reference?r=e:we.isElement(this._config.reference)&&(r=this._config.reference,"undefined"!=typeof this._config.reference.jquery&&(r=this._config.reference[0])),"scrollParent"!==this._config.boundary&&Tt(e).addClass(Ft),this._popper=new Ct(r,this._menu,this._getPopperConfig())}"ontouchstart"in document.documentElement&&0===Tt(e).closest(Bt).length&&Tt(document.body).children().on("mouseover",null,Tt.noop),this._element.focus(),this._element.setAttribute("aria-expanded",!0),Tt(this._menu).toggleClass(Pt),Tt(e).toggleClass(Pt).trigger(Tt.Event(kt.SHOWN,n))}}}},e.dispose=function(){Tt.removeData(this._element,Dt),Tt(this._element).off(At),this._element=null,(this._menu=null)!==this._popper&&(this._popper.destroy(),this._popper=null)},e.update=function(){this._inNavbar=this._detectNavbar(),null!==this._popper&&this._popper.scheduleUpdate()},e._addEventListeners=function(){var t=this;Tt(this._element).on(kt.CLICK,function(e){e.preventDefault(),e.stopPropagation(),t.toggle()})},e._getConfig=function(e){return e=l({},this.constructor.Default,Tt(this._element).data(),e),we.typeCheckConfig(St,e,this.constructor.DefaultType),e},e._getMenuElement=function(){if(!this._menu){var e=c._getParentFromElement(this._element);e&&(this._menu=e.querySelector(Ut))}return this._menu},e._getPlacement=function(){var e=Tt(this._element.parentNode),t=Yt;return e.hasClass(Lt)?(t=Kt,Tt(this._menu).hasClass(Mt)&&(t=Qt)):e.hasClass(jt)?t=zt:e.hasClass(Ht)?t=Gt:Tt(this._menu).hasClass(Mt)&&(t=Vt),t},e._detectNavbar=function(){return 0<Tt(this._element).closest(".navbar").length},e._getPopperConfig=function(){var t=this,e={};"function"==typeof this._config.offset?e.fn=function(e){return e.offsets=l({},e.offsets,t._config.offset(e.offsets)||{}),e}:e.offset=this._config.offset;var n={placement:this._getPlacement(),modifiers:{offset:e,flip:{enabled:this._config.flip},preventOverflow:{boundariesElement:this._config.boundary}}};return"static"===this._config.display&&(n.modifiers.applyStyle={enabled:!1}),n},c._jQueryInterface=function(t){return this.each(function(){var e=Tt(this).data(Dt);if(e||(e=new c(this,"object"==typeof t?t:null),Tt(this).data(Dt,e)),"string"==typeof t){if("undefined"==typeof e[t])throw new TypeError('No method named "'+t+'"');e[t]()}})},c._clearMenus=function(e){if(!e||3!==e.which&&("keyup"!==e.type||9===e.which))for(var t=[].slice.call(document.querySelectorAll(Wt)),n=0,i=t.length;n<i;n++){var r=c._getParentFromElement(t[n]),o=Tt(t[n]).data(Dt),s={relatedTarget:t[n]};if(e&&"click"===e.type&&(s.clickEvent=e),o){var a=o._menu;if(Tt(r).hasClass(Pt)&&!(e&&("click"===e.type&&/input|textarea/i.test(e.target.tagName)||"keyup"===e.type&&9===e.which)&&Tt.contains(r,e.target))){var l=Tt.Event(kt.HIDE,s);Tt(r).trigger(l),l.isDefaultPrevented()||("ontouchstart"in document.documentElement&&Tt(document.body).children().off("mouseover",null,Tt.noop),t[n].setAttribute("aria-expanded","false"),Tt(a).removeClass(Pt),Tt(r).removeClass(Pt).trigger(Tt.Event(kt.HIDDEN,s)))}}}},c._getParentFromElement=function(e){var t,n=we.getSelectorFromElement(e);return n&&(t=document.querySelector(n)),t||e.parentNode},c._dataApiKeydownHandler=function(e){if((/input|textarea/i.test(e.target.tagName)?!(32===e.which||27!==e.which&&(40!==e.which&&38!==e.which||Tt(e.target).closest(Ut).length)):Nt.test(e.which))&&(e.preventDefault(),e.stopPropagation(),!this.disabled&&!Tt(this).hasClass(xt))){var t=c._getParentFromElement(this),n=Tt(t).hasClass(Pt);if((n||27===e.which&&32===e.which)&&(!n||27!==e.which&&32!==e.which)){var i=[].slice.call(t.querySelectorAll(qt));if(0!==i.length){var r=i.indexOf(e.target);38===e.which&&0<r&&r--,40===e.which&&r<i.length-1&&r++,r<0&&(r=0),i[r].focus()}}else{if(27===e.which){var o=t.querySelector(Wt);Tt(o).trigger("focus")}Tt(this).trigger("click")}}},s(c,null,[{key:"VERSION",get:function(){return"4.1.3"}},{key:"Default",get:function(){return Jt}},{key:"DefaultType",get:function(){return Zt}}]),c}(),Tt(document).on(kt.KEYDOWN_DATA_API,Wt,Xt._dataApiKeydownHandler).on(kt.KEYDOWN_DATA_API,Ut,Xt._dataApiKeydownHandler).on(kt.CLICK_DATA_API+" "+kt.KEYUP_DATA_API,Xt._clearMenus).on(kt.CLICK_DATA_API,Wt,function(e){e.preventDefault(),e.stopPropagation(),Xt._jQueryInterface.call(Tt(this),"toggle")}).on(kt.CLICK_DATA_API,Rt,function(e){e.stopPropagation()}),Tt.fn[St]=Xt._jQueryInterface,Tt.fn[St].Constructor=Xt,Tt.fn[St].noConflict=function(){return Tt.fn[St]=Ot,Xt._jQueryInterface},Xt),qi=(en="modal",nn="."+(tn="bs.modal"),rn=($t=t).fn[en],on={backdrop:!0,keyboard:!0,focus:!0,show:!0},sn={backdrop:"(boolean|string)",keyboard:"boolean",focus:"boolean",show:"boolean"},an={HIDE:"hide"+nn,HIDDEN:"hidden"+nn,SHOW:"show"+nn,SHOWN:"shown"+nn,FOCUSIN:"focusin"+nn,RESIZE:"resize"+nn,CLICK_DISMISS:"click.dismiss"+nn,KEYDOWN_DISMISS:"keydown.dismiss"+nn,MOUSEUP_DISMISS:"mouseup.dismiss"+nn,MOUSEDOWN_DISMISS:"mousedown.dismiss"+nn,CLICK_DATA_API:"click"+nn+".data-api"},ln="modal-scrollbar-measure",cn="modal-backdrop",un="modal-open",fn="fade",hn="show",dn=".modal-dialog",pn='[data-toggle="modal"]',mn='[data-dismiss="modal"]',gn=".fixed-top, .fixed-bottom, .is-fixed, .sticky-top",_n=".sticky-top",vn=function(){function r(e,t){this._config=this._getConfig(t),this._element=e,this._dialog=e.querySelector(dn),this._backdrop=null,this._isShown=!1,this._isBodyOverflowing=!1,this._ignoreBackdropClick=!1,this._scrollbarWidth=0}var e=r.prototype;return e.toggle=function(e){return this._isShown?this.hide():this.show(e)},e.show=function(e){var t=this;if(!this._isTransitioning&&!this._isShown){$t(this._element).hasClass(fn)&&(this._isTransitioning=!0);var n=$t.Event(an.SHOW,{relatedTarget:e});$t(this._element).trigger(n),this._isShown||n.isDefaultPrevented()||(this._isShown=!0,this._checkScrollbar(),this._setScrollbar(),this._adjustDialog(),$t(document.body).addClass(un),this._setEscapeEvent(),this._setResizeEvent(),$t(this._element).on(an.CLICK_DISMISS,mn,function(e){return t.hide(e)}),$t(this._dialog).on(an.MOUSEDOWN_DISMISS,function(){$t(t._element).one(an.MOUSEUP_DISMISS,function(e){$t(e.target).is(t._element)&&(t._ignoreBackdropClick=!0)})}),this._showBackdrop(function(){return t._showElement(e)}))}},e.hide=function(e){var t=this;if(e&&e.preventDefault(),!this._isTransitioning&&this._isShown){var n=$t.Event(an.HIDE);if($t(this._element).trigger(n),this._isShown&&!n.isDefaultPrevented()){this._isShown=!1;var i=$t(this._element).hasClass(fn);if(i&&(this._isTransitioning=!0),this._setEscapeEvent(),this._setResizeEvent(),$t(document).off(an.FOCUSIN),$t(this._element).removeClass(hn),$t(this._element).off(an.CLICK_DISMISS),$t(this._dialog).off(an.MOUSEDOWN_DISMISS),i){var r=we.getTransitionDurationFromElement(this._element);$t(this._element).one(we.TRANSITION_END,function(e){return t._hideModal(e)}).emulateTransitionEnd(r)}else this._hideModal()}}},e.dispose=function(){$t.removeData(this._element,tn),$t(window,document,this._element,this._backdrop).off(nn),this._config=null,this._element=null,this._dialog=null,this._backdrop=null,this._isShown=null,this._isBodyOverflowing=null,this._ignoreBackdropClick=null,this._scrollbarWidth=null},e.handleUpdate=function(){this._adjustDialog()},e._getConfig=function(e){return e=l({},on,e),we.typeCheckConfig(en,e,sn),e},e._showElement=function(e){var t=this,n=$t(this._element).hasClass(fn);this._element.parentNode&&this._element.parentNode.nodeType===Node.ELEMENT_NODE||document.body.appendChild(this._element),this._element.style.display="block",this._element.removeAttribute("aria-hidden"),this._element.scrollTop=0,n&&we.reflow(this._element),$t(this._element).addClass(hn),this._config.focus&&this._enforceFocus();var i=$t.Event(an.SHOWN,{relatedTarget:e}),r=function(){t._config.focus&&t._element.focus(),t._isTransitioning=!1,$t(t._element).trigger(i)};if(n){var o=we.getTransitionDurationFromElement(this._element);$t(this._dialog).one(we.TRANSITION_END,r).emulateTransitionEnd(o)}else r()},e._enforceFocus=function(){var t=this;$t(document).off(an.FOCUSIN).on(an.FOCUSIN,function(e){document!==e.target&&t._element!==e.target&&0===$t(t._element).has(e.target).length&&t._element.focus()})},e._setEscapeEvent=function(){var t=this;this._isShown&&this._config.keyboard?$t(this._element).on(an.KEYDOWN_DISMISS,function(e){27===e.which&&(e.preventDefault(),t.hide())}):this._isShown||$t(this._element).off(an.KEYDOWN_DISMISS)},e._setResizeEvent=function(){var t=this;this._isShown?$t(window).on(an.RESIZE,function(e){return t.handleUpdate(e)}):$t(window).off(an.RESIZE)},e._hideModal=function(){var e=this;this._element.style.display="none",this._element.setAttribute("aria-hidden",!0),this._isTransitioning=!1,this._showBackdrop(function(){$t(document.body).removeClass(un),e._resetAdjustments(),e._resetScrollbar(),$t(e._element).trigger(an.HIDDEN)})},e._removeBackdrop=function(){this._backdrop&&($t(this._backdrop).remove(),this._backdrop=null)},e._showBackdrop=function(e){var t=this,n=$t(this._element).hasClass(fn)?fn:"";if(this._isShown&&this._config.backdrop){if(this._backdrop=document.createElement("div"),this._backdrop.className=cn,n&&this._backdrop.classList.add(n),$t(this._backdrop).appendTo(document.body),$t(this._element).on(an.CLICK_DISMISS,function(e){t._ignoreBackdropClick?t._ignoreBackdropClick=!1:e.target===e.currentTarget&&("static"===t._config.backdrop?t._element.focus():t.hide())}),n&&we.reflow(this._backdrop),$t(this._backdrop).addClass(hn),!e)return;if(!n)return void e();var i=we.getTransitionDurationFromElement(this._backdrop);$t(this._backdrop).one(we.TRANSITION_END,e).emulateTransitionEnd(i)}else if(!this._isShown&&this._backdrop){$t(this._backdrop).removeClass(hn);var r=function(){t._removeBackdrop(),e&&e()};if($t(this._element).hasClass(fn)){var o=we.getTransitionDurationFromElement(this._backdrop);$t(this._backdrop).one(we.TRANSITION_END,r).emulateTransitionEnd(o)}else r()}else e&&e()},e._adjustDialog=function(){var e=this._element.scrollHeight>document.documentElement.clientHeight;!this._isBodyOverflowing&&e&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!e&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},e._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},e._checkScrollbar=function(){var e=document.body.getBoundingClientRect();this._isBodyOverflowing=e.left+e.right<window.innerWidth,this._scrollbarWidth=this._getScrollbarWidth()},e._setScrollbar=function(){var r=this;if(this._isBodyOverflowing){var e=[].slice.call(document.querySelectorAll(gn)),t=[].slice.call(document.querySelectorAll(_n));$t(e).each(function(e,t){var n=t.style.paddingRight,i=$t(t).css("padding-right");$t(t).data("padding-right",n).css("padding-right",parseFloat(i)+r._scrollbarWidth+"px")}),$t(t).each(function(e,t){var n=t.style.marginRight,i=$t(t).css("margin-right");$t(t).data("margin-right",n).css("margin-right",parseFloat(i)-r._scrollbarWidth+"px")});var n=document.body.style.paddingRight,i=$t(document.body).css("padding-right");$t(document.body).data("padding-right",n).css("padding-right",parseFloat(i)+this._scrollbarWidth+"px")}},e._resetScrollbar=function(){var e=[].slice.call(document.querySelectorAll(gn));$t(e).each(function(e,t){var n=$t(t).data("padding-right");$t(t).removeData("padding-right"),t.style.paddingRight=n||""});var t=[].slice.call(document.querySelectorAll(""+_n));$t(t).each(function(e,t){var n=$t(t).data("margin-right");"undefined"!=typeof n&&$t(t).css("margin-right",n).removeData("margin-right")});var n=$t(document.body).data("padding-right");$t(document.body).removeData("padding-right"),document.body.style.paddingRight=n||""},e._getScrollbarWidth=function(){var e=document.createElement("div");e.className=ln,document.body.appendChild(e);var t=e.getBoundingClientRect().width-e.clientWidth;return document.body.removeChild(e),t},r._jQueryInterface=function(n,i){return this.each(function(){var e=$t(this).data(tn),t=l({},on,$t(this).data(),"object"==typeof n&&n?n:{});if(e||(e=new r(this,t),$t(this).data(tn,e)),"string"==typeof n){if("undefined"==typeof e[n])throw new TypeError('No method named "'+n+'"');e[n](i)}else t.show&&e.show(i)})},s(r,null,[{key:"VERSION",get:function(){return"4.1.3"}},{key:"Default",get:function(){return on}}]),r}(),$t(document).on(an.CLICK_DATA_API,pn,function(e){var t,n=this,i=we.getSelectorFromElement(this);i&&(t=document.querySelector(i));var r=$t(t).data(tn)?"toggle":l({},$t(t).data(),$t(this).data());"A"!==this.tagName&&"AREA"!==this.tagName||e.preventDefault();var o=$t(t).one(an.SHOW,function(e){e.isDefaultPrevented()||o.one(an.HIDDEN,function(){$t(n).is(":visible")&&n.focus()})});vn._jQueryInterface.call($t(t),r,this)}),$t.fn[en]=vn._jQueryInterface,$t.fn[en].Constructor=vn,$t.fn[en].noConflict=function(){return $t.fn[en]=rn,vn._jQueryInterface},vn),Ki=(En="tooltip",wn="."+(bn="bs.tooltip"),Cn=(yn=t).fn[En],Tn="bs-tooltip",Sn=new RegExp("(^|\\s)"+Tn+"\\S+","g"),In={animation:!0,template:'<div class="tooltip" role="tooltip"><div class="arrow"></div><div class="tooltip-inner"></div></div>',trigger:"hover focus",title:"",delay:0,html:!(An={AUTO:"auto",TOP:"top",RIGHT:"right",BOTTOM:"bottom",LEFT:"left"}),selector:!(Dn={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)"}),placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent"},Nn="out",kn={HIDE:"hide"+wn,HIDDEN:"hidden"+wn,SHOW:(On="show")+wn,SHOWN:"shown"+wn,INSERTED:"inserted"+wn,CLICK:"click"+wn,FOCUSIN:"focusin"+wn,FOCUSOUT:"focusout"+wn,MOUSEENTER:"mouseenter"+wn,MOUSELEAVE:"mouseleave"+wn},xn="fade",Pn="show",Ln=".tooltip-inner",jn=".arrow",Hn="hover",Mn="focus",Fn="click",Wn="manual",Rn=function(){function i(e,t){if("undefined"==typeof Ct)throw new TypeError("Bootstrap tooltips require Popper.js (https://popper.js.org)");this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this.element=e,this.config=this._getConfig(t),this.tip=null,this._setListeners()}var e=i.prototype;return e.enable=function(){this._isEnabled=!0},e.disable=function(){this._isEnabled=!1},e.toggleEnabled=function(){this._isEnabled=!this._isEnabled},e.toggle=function(e){if(this._isEnabled)if(e){var t=this.constructor.DATA_KEY,n=yn(e.currentTarget).data(t);n||(n=new this.constructor(e.currentTarget,this._getDelegateConfig()),yn(e.currentTarget).data(t,n)),n._activeTrigger.click=!n._activeTrigger.click,n._isWithActiveTrigger()?n._enter(null,n):n._leave(null,n)}else{if(yn(this.getTipElement()).hasClass(Pn))return void this._leave(null,this);this._enter(null,this)}},e.dispose=function(){clearTimeout(this._timeout),yn.removeData(this.element,this.constructor.DATA_KEY),yn(this.element).off(this.constructor.EVENT_KEY),yn(this.element).closest(".modal").off("hide.bs.modal"),this.tip&&yn(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,(this._activeTrigger=null)!==this._popper&&this._popper.destroy(),this._popper=null,this.element=null,this.config=null,this.tip=null},e.show=function(){var t=this;if("none"===yn(this.element).css("display"))throw new Error("Please use show on visible elements");var e=yn.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){yn(this.element).trigger(e);var n=yn.contains(this.element.ownerDocument.documentElement,this.element);if(e.isDefaultPrevented()||!n)return;var i=this.getTipElement(),r=we.getUID(this.constructor.NAME);i.setAttribute("id",r),this.element.setAttribute("aria-describedby",r),this.setContent(),this.config.animation&&yn(i).addClass(xn);var o="function"==typeof this.config.placement?this.config.placement.call(this,i,this.element):this.config.placement,s=this._getAttachment(o);this.addAttachmentClass(s);var a=!1===this.config.container?document.body:yn(document).find(this.config.container);yn(i).data(this.constructor.DATA_KEY,this),yn.contains(this.element.ownerDocument.documentElement,this.tip)||yn(i).appendTo(a),yn(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new Ct(this.element,i,{placement:s,modifiers:{offset:{offset:this.config.offset},flip:{behavior:this.config.fallbackPlacement},arrow:{element:jn},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(e){e.originalPlacement!==e.placement&&t._handlePopperPlacementChange(e)},onUpdate:function(e){t._handlePopperPlacementChange(e)}}),yn(i).addClass(Pn),"ontouchstart"in document.documentElement&&yn(document.body).children().on("mouseover",null,yn.noop);var l=function(){t.config.animation&&t._fixTransition();var e=t._hoverState;t._hoverState=null,yn(t.element).trigger(t.constructor.Event.SHOWN),e===Nn&&t._leave(null,t)};if(yn(this.tip).hasClass(xn)){var c=we.getTransitionDurationFromElement(this.tip);yn(this.tip).one(we.TRANSITION_END,l).emulateTransitionEnd(c)}else l()}},e.hide=function(e){var t=this,n=this.getTipElement(),i=yn.Event(this.constructor.Event.HIDE),r=function(){t._hoverState!==On&&n.parentNode&&n.parentNode.removeChild(n),t._cleanTipClass(),t.element.removeAttribute("aria-describedby"),yn(t.element).trigger(t.constructor.Event.HIDDEN),null!==t._popper&&t._popper.destroy(),e&&e()};if(yn(this.element).trigger(i),!i.isDefaultPrevented()){if(yn(n).removeClass(Pn),"ontouchstart"in document.documentElement&&yn(document.body).children().off("mouseover",null,yn.noop),this._activeTrigger[Fn]=!1,this._activeTrigger[Mn]=!1,this._activeTrigger[Hn]=!1,yn(this.tip).hasClass(xn)){var o=we.getTransitionDurationFromElement(n);yn(n).one(we.TRANSITION_END,r).emulateTransitionEnd(o)}else r();this._hoverState=""}},e.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},e.isWithContent=function(){return Boolean(this.getTitle())},e.addAttachmentClass=function(e){yn(this.getTipElement()).addClass(Tn+"-"+e)},e.getTipElement=function(){return this.tip=this.tip||yn(this.config.template)[0],this.tip},e.setContent=function(){var e=this.getTipElement();this.setElementContent(yn(e.querySelectorAll(Ln)),this.getTitle()),yn(e).removeClass(xn+" "+Pn)},e.setElementContent=function(e,t){var n=this.config.html;"object"==typeof t&&(t.nodeType||t.jquery)?n?yn(t).parent().is(e)||e.empty().append(t):e.text(yn(t).text()):e[n?"html":"text"](t)},e.getTitle=function(){var e=this.element.getAttribute("data-original-title");return e||(e="function"==typeof this.config.title?this.config.title.call(this.element):this.config.title),e},e._getAttachment=function(e){return An[e.toUpperCase()]},e._setListeners=function(){var i=this;this.config.trigger.split(" ").forEach(function(e){if("click"===e)yn(i.element).on(i.constructor.Event.CLICK,i.config.selector,function(e){return i.toggle(e)});else if(e!==Wn){var t=e===Hn?i.constructor.Event.MOUSEENTER:i.constructor.Event.FOCUSIN,n=e===Hn?i.constructor.Event.MOUSELEAVE:i.constructor.Event.FOCUSOUT;yn(i.element).on(t,i.config.selector,function(e){return i._enter(e)}).on(n,i.config.selector,function(e){return i._leave(e)})}yn(i.element).closest(".modal").on("hide.bs.modal",function(){return i.hide()})}),this.config.selector?this.config=l({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},e._fixTitle=function(){var e=typeof this.element.getAttribute("data-original-title");(this.element.getAttribute("title")||"string"!==e)&&(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},e._enter=function(e,t){var n=this.constructor.DATA_KEY;(t=t||yn(e.currentTarget).data(n))||(t=new this.constructor(e.currentTarget,this._getDelegateConfig()),yn(e.currentTarget).data(n,t)),e&&(t._activeTrigger["focusin"===e.type?Mn:Hn]=!0),yn(t.getTipElement()).hasClass(Pn)||t._hoverState===On?t._hoverState=On:(clearTimeout(t._timeout),t._hoverState=On,t.config.delay&&t.config.delay.show?t._timeout=setTimeout(function(){t._hoverState===On&&t.show()},t.config.delay.show):t.show())},e._leave=function(e,t){var n=this.constructor.DATA_KEY;(t=t||yn(e.currentTarget).data(n))||(t=new this.constructor(e.currentTarget,this._getDelegateConfig()),yn(e.currentTarget).data(n,t)),e&&(t._activeTrigger["focusout"===e.type?Mn:Hn]=!1),t._isWithActiveTrigger()||(clearTimeout(t._timeout),t._hoverState=Nn,t.config.delay&&t.config.delay.hide?t._timeout=setTimeout(function(){t._hoverState===Nn&&t.hide()},t.config.delay.hide):t.hide())},e._isWithActiveTrigger=function(){for(var e in this._activeTrigger)if(this._activeTrigger[e])return!0;return!1},e._getConfig=function(e){return"number"==typeof(e=l({},this.constructor.Default,yn(this.element).data(),"object"==typeof e&&e?e:{})).delay&&(e.delay={show:e.delay,hide:e.delay}),"number"==typeof e.title&&(e.title=e.title.toString()),"number"==typeof e.content&&(e.content=e.content.toString()),we.typeCheckConfig(En,e,this.constructor.DefaultType),e},e._getDelegateConfig=function(){var e={};if(this.config)for(var t in this.config)this.constructor.Default[t]!==this.config[t]&&(e[t]=this.config[t]);return e},e._cleanTipClass=function(){var e=yn(this.getTipElement()),t=e.attr("class").match(Sn);null!==t&&t.length&&e.removeClass(t.join(""))},e._handlePopperPlacementChange=function(e){var t=e.instance;this.tip=t.popper,this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(e.placement))},e._fixTransition=function(){var e=this.getTipElement(),t=this.config.animation;null===e.getAttribute("x-placement")&&(yn(e).removeClass(xn),this.config.animation=!1,this.hide(),this.show(),this.config.animation=t)},i._jQueryInterface=function(n){return this.each(function(){var e=yn(this).data(bn),t="object"==typeof n&&n;if((e||!/dispose|hide/.test(n))&&(e||(e=new i(this,t),yn(this).data(bn,e)),"string"==typeof n)){if("undefined"==typeof e[n])throw new TypeError('No method named "'+n+'"');e[n]()}})},s(i,null,[{key:"VERSION",get:function(){return"4.1.3"}},{key:"Default",get:function(){return In}},{key:"NAME",get:function(){return En}},{key:"DATA_KEY",get:function(){return bn}},{key:"Event",get:function(){return kn}},{key:"EVENT_KEY",get:function(){return wn}},{key:"DefaultType",get:function(){return Dn}}]),i}(),yn.fn[En]=Rn._jQueryInterface,yn.fn[En].Constructor=Rn,yn.fn[En].noConflict=function(){return yn.fn[En]=Cn,Rn._jQueryInterface},Rn),Qi=(Bn="popover",Kn="."+(qn="bs.popover"),Qn=(Un=t).fn[Bn],Yn="bs-popover",Vn=new RegExp("(^|\\s)"+Yn+"\\S+","g"),zn=l({},Ki.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>'}),Gn=l({},Ki.DefaultType,{content:"(string|element|function)"}),Jn="fade",Xn=".popover-header",$n=".popover-body",ei={HIDE:"hide"+Kn,HIDDEN:"hidden"+Kn,SHOW:(Zn="show")+Kn,SHOWN:"shown"+Kn,INSERTED:"inserted"+Kn,CLICK:"click"+Kn,FOCUSIN:"focusin"+Kn,FOCUSOUT:"focusout"+Kn,MOUSEENTER:"mouseenter"+Kn,MOUSELEAVE:"mouseleave"+Kn},ti=function(e){var t,n;function i(){return e.apply(this,arguments)||this}n=e,(t=i).prototype=Object.create(n.prototype),(t.prototype.constructor=t).__proto__=n;var r=i.prototype;return r.isWithContent=function(){return this.getTitle()||this._getContent()},r.addAttachmentClass=function(e){Un(this.getTipElement()).addClass(Yn+"-"+e)},r.getTipElement=function(){return this.tip=this.tip||Un(this.config.template)[0],this.tip},r.setContent=function(){var e=Un(this.getTipElement());this.setElementContent(e.find(Xn),this.getTitle());var t=this._getContent();"function"==typeof t&&(t=t.call(this.element)),this.setElementContent(e.find($n),t),e.removeClass(Jn+" "+Zn)},r._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},r._cleanTipClass=function(){var e=Un(this.getTipElement()),t=e.attr("class").match(Vn);null!==t&&0<t.length&&e.removeClass(t.join(""))},i._jQueryInterface=function(n){return this.each(function(){var e=Un(this).data(qn),t="object"==typeof n?n:null;if((e||!/destroy|hide/.test(n))&&(e||(e=new i(this,t),Un(this).data(qn,e)),"string"==typeof n)){if("undefined"==typeof e[n])throw new TypeError('No method named "'+n+'"');e[n]()}})},s(i,null,[{key:"VERSION",get:function(){return"4.1.3"}},{key:"Default",get:function(){return zn}},{key:"NAME",get:function(){return Bn}},{key:"DATA_KEY",get:function(){return qn}},{key:"Event",get:function(){return ei}},{key:"EVENT_KEY",get:function(){return Kn}},{key:"DefaultType",get:function(){return Gn}}]),i}(Ki),Un.fn[Bn]=ti._jQueryInterface,Un.fn[Bn].Constructor=ti,Un.fn[Bn].noConflict=function(){return Un.fn[Bn]=Qn,ti._jQueryInterface},ti),Yi=(ii="scrollspy",oi="."+(ri="bs.scrollspy"),si=(ni=t).fn[ii],ai={offset:10,method:"auto",target:""},li={offset:"number",method:"string",target:"(string|element)"},ci={ACTIVATE:"activate"+oi,SCROLL:"scroll"+oi,LOAD_DATA_API:"load"+oi+".data-api"},ui="dropdown-item",fi="active",hi='[data-spy="scroll"]',di=".active",pi=".nav, .list-group",mi=".nav-link",gi=".nav-item",_i=".list-group-item",vi=".dropdown",yi=".dropdown-item",Ei=".dropdown-toggle",bi="offset",wi="position",Ci=function(){function n(e,t){var n=this;this._element=e,this._scrollElement="BODY"===e.tagName?window:e,this._config=this._getConfig(t),this._selector=this._config.target+" "+mi+","+this._config.target+" "+_i+","+this._config.target+" "+yi,this._offsets=[],this._targets=[],this._activeTarget=null,this._scrollHeight=0,ni(this._scrollElement).on(ci.SCROLL,function(e){return n._process(e)}),this.refresh(),this._process()}var e=n.prototype;return e.refresh=function(){var t=this,e=this._scrollElement===this._scrollElement.window?bi:wi,r="auto"===this._config.method?e:this._config.method,o=r===wi?this._getScrollTop():0;this._offsets=[],this._targets=[],this._scrollHeight=this._getScrollHeight(),[].slice.call(document.querySelectorAll(this._selector)).map(function(e){var t,n=we.getSelectorFromElement(e);if(n&&(t=document.querySelector(n)),t){var i=t.getBoundingClientRect();if(i.width||i.height)return[ni(t)[r]().top+o,n]}return null}).filter(function(e){return e}).sort(function(e,t){return e[0]-t[0]}).forEach(function(e){t._offsets.push(e[0]),t._targets.push(e[1])})},e.dispose=function(){ni.removeData(this._element,ri),ni(this._scrollElement).off(oi),this._element=null,this._scrollElement=null,this._config=null,this._selector=null,this._offsets=null,this._targets=null,this._activeTarget=null,this._scrollHeight=null},e._getConfig=function(e){if("string"!=typeof(e=l({},ai,"object"==typeof e&&e?e:{})).target){var t=ni(e.target).attr("id");t||(t=we.getUID(ii),ni(e.target).attr("id",t)),e.target="#"+t}return we.typeCheckConfig(ii,e,li),e},e._getScrollTop=function(){return this._scrollElement===window?this._scrollElement.pageYOffset:this._scrollElement.scrollTop},e._getScrollHeight=function(){return this._scrollElement.scrollHeight||Math.max(document.body.scrollHeight,document.documentElement.scrollHeight)},e._getOffsetHeight=function(){return this._scrollElement===window?window.innerHeight:this._scrollElement.getBoundingClientRect().height},e._process=function(){var e=this._getScrollTop()+this._config.offset,t=this._getScrollHeight(),n=this._config.offset+t-this._getOffsetHeight();if(this._scrollHeight!==t&&this.refresh(),n<=e){var i=this._targets[this._targets.length-1];this._activeTarget!==i&&this._activate(i)}else{if(this._activeTarget&&e<this._offsets[0]&&0<this._offsets[0])return this._activeTarget=null,void this._clear();for(var r=this._offsets.length;r--;){this._activeTarget!==this._targets[r]&&e>=this._offsets[r]&&("undefined"==typeof this._offsets[r+1]||e<this._offsets[r+1])&&this._activate(this._targets[r])}}},e._activate=function(t){this._activeTarget=t,this._clear();var e=this._selector.split(",");e=e.map(function(e){return e+'[data-target="'+t+'"],'+e+'[href="'+t+'"]'});var n=ni([].slice.call(document.querySelectorAll(e.join(","))));n.hasClass(ui)?(n.closest(vi).find(Ei).addClass(fi),n.addClass(fi)):(n.addClass(fi),n.parents(pi).prev(mi+", "+_i).addClass(fi),n.parents(pi).prev(gi).children(mi).addClass(fi)),ni(this._scrollElement).trigger(ci.ACTIVATE,{relatedTarget:t})},e._clear=function(){var e=[].slice.call(document.querySelectorAll(this._selector));ni(e).filter(di).removeClass(fi)},n._jQueryInterface=function(t){return this.each(function(){var e=ni(this).data(ri);if(e||(e=new n(this,"object"==typeof t&&t),ni(this).data(ri,e)),"string"==typeof t){if("undefined"==typeof e[t])throw new TypeError('No method named "'+t+'"');e[t]()}})},s(n,null,[{key:"VERSION",get:function(){return"4.1.3"}},{key:"Default",get:function(){return ai}}]),n}(),ni(window).on(ci.LOAD_DATA_API,function(){for(var e=[].slice.call(document.querySelectorAll(hi)),t=e.length;t--;){var n=ni(e[t]);Ci._jQueryInterface.call(n,n.data())}}),ni.fn[ii]=Ci._jQueryInterface,ni.fn[ii].Constructor=Ci,ni.fn[ii].noConflict=function(){return ni.fn[ii]=si,Ci._jQueryInterface},Ci),Vi=(Di="."+(Si="bs.tab"),Ai=(Ti=t).fn.tab,Ii={HIDE:"hide"+Di,HIDDEN:"hidden"+Di,SHOW:"show"+Di,SHOWN:"shown"+Di,CLICK_DATA_API:"click"+Di+".data-api"},Oi="dropdown-menu",Ni="active",ki="disabled",xi="fade",Pi="show",Li=".dropdown",ji=".nav, .list-group",Hi=".active",Mi="> li > .active",Fi='[data-toggle="tab"], [data-toggle="pill"], [data-toggle="list"]',Wi=".dropdown-toggle",Ri="> .dropdown-menu .active",Ui=function(){function i(e){this._element=e}var e=i.prototype;return e.show=function(){var n=this;if(!(this._element.parentNode&&this._element.parentNode.nodeType===Node.ELEMENT_NODE&&Ti(this._element).hasClass(Ni)||Ti(this._element).hasClass(ki))){var e,i,t=Ti(this._element).closest(ji)[0],r=we.getSelectorFromElement(this._element);if(t){var o="UL"===t.nodeName?Mi:Hi;i=(i=Ti.makeArray(Ti(t).find(o)))[i.length-1]}var s=Ti.Event(Ii.HIDE,{relatedTarget:this._element}),a=Ti.Event(Ii.SHOW,{relatedTarget:i});if(i&&Ti(i).trigger(s),Ti(this._element).trigger(a),!a.isDefaultPrevented()&&!s.isDefaultPrevented()){r&&(e=document.querySelector(r)),this._activate(this._element,t);var l=function(){var e=Ti.Event(Ii.HIDDEN,{relatedTarget:n._element}),t=Ti.Event(Ii.SHOWN,{relatedTarget:i});Ti(i).trigger(e),Ti(n._element).trigger(t)};e?this._activate(e,e.parentNode,l):l()}}},e.dispose=function(){Ti.removeData(this._element,Si),this._element=null},e._activate=function(e,t,n){var i=this,r=("UL"===t.nodeName?Ti(t).find(Mi):Ti(t).children(Hi))[0],o=n&&r&&Ti(r).hasClass(xi),s=function(){return i._transitionComplete(e,r,n)};if(r&&o){var a=we.getTransitionDurationFromElement(r);Ti(r).one(we.TRANSITION_END,s).emulateTransitionEnd(a)}else s()},e._transitionComplete=function(e,t,n){if(t){Ti(t).removeClass(Pi+" "+Ni);var i=Ti(t.parentNode).find(Ri)[0];i&&Ti(i).removeClass(Ni),"tab"===t.getAttribute("role")&&t.setAttribute("aria-selected",!1)}if(Ti(e).addClass(Ni),"tab"===e.getAttribute("role")&&e.setAttribute("aria-selected",!0),we.reflow(e),Ti(e).addClass(Pi),e.parentNode&&Ti(e.parentNode).hasClass(Oi)){var r=Ti(e).closest(Li)[0];if(r){var o=[].slice.call(r.querySelectorAll(Wi));Ti(o).addClass(Ni)}e.setAttribute("aria-expanded",!0)}n&&n()},i._jQueryInterface=function(n){return this.each(function(){var e=Ti(this),t=e.data(Si);if(t||(t=new i(this),e.data(Si,t)),"string"==typeof n){if("undefined"==typeof t[n])throw new TypeError('No method named "'+n+'"');t[n]()}})},s(i,null,[{key:"VERSION",get:function(){return"4.1.3"}}]),i}(),Ti(document).on(Ii.CLICK_DATA_API,Fi,function(e){e.preventDefault(),Ui._jQueryInterface.call(Ti(this),"show")}),Ti.fn.tab=Ui._jQueryInterface,Ti.fn.tab.Constructor=Ui,Ti.fn.tab.noConflict=function(){return Ti.fn.tab=Ai,Ui._jQueryInterface},Ui);!function(e){if("undefined"==typeof e)throw new TypeError("Bootstrap's JavaScript requires jQuery. jQuery must be included before Bootstrap's JavaScript.");var t=e.fn.jquery.split(" ")[0].split(".");if(t[0]<2&&t[1]<9||1===t[0]&&9===t[1]&&t[2]<1||4<=t[0])throw new Error("Bootstrap's JavaScript requires at least jQuery v1.9.1 but less than v4.0.0")}(t),e.Util=we,e.Alert=Ce,e.Button=Te,e.Carousel=Se,e.Collapse=De,e.Dropdown=Bi,e.Modal=qi,e.Popover=Qi,e.Scrollspy=Yi,e.Tab=Vi,e.Tooltip=Ki,Object.defineProperty(e,"__esModule",{value:!0})});

+ //# sourceMappingURL=bootstrap.bundle.min.js.map

empty or binary file added
empty or binary file added
empty or binary file added
empty or binary file added
empty or binary file added
@@ -0,0 +1,1 @@ 

+ <svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m4.5 3e-5c-0.277 0-0.5 0.223-0.5 0.5v0.5h-2c-0.554 0-1 0.446-1 1v11c0 0.554 0.446 1 1 1h3v-1h-3v-11h2v0.5c0 0.277 0.223 0.5 0.5 0.5s0.5-0.223 0.5-0.5v-0.5h4v0.5c0 0.277 0.223 0.5 0.5 0.5s0.5-0.223 0.5-0.5v-0.5h2v3.0859c0.3437 0.05827 0.67848 0.1453 1 0.25977v-3.3457c0-0.554-0.446-1-1-1h-2v-0.5c0-0.277-0.223-0.5-0.5-0.5s-0.5 0.223-0.5 0.5v0.5h-4v-0.5c0-0.277-0.223-0.5-0.5-0.5zm5 3h-6.5v9h2v-1.0039a0.99994 0.99994 0 0 0 0.00195-2e-3c4e-3 -3.2987 2.6983-5.9941 5.998-5.9941v-2h-1.5zm1.5 3c-2.7605 0-4.9982 2.2362-5 4.9961v3.8711c2e-3 0.62657 0.50983 1.1328 1.1367 1.1328h3.8653c2.7608-0.0019 4.998-2.2394 4.998-5 0-2.7618-2.2382-5-5-5zm0.6875 2.1211c0.88509 0 1.7207 0.67043 1.7207 1.5957 0 0.08581-0.0013 0.17221-0.01562 0.26953-0.0243 0.24618-0.25058 0.42338-0.49805 0.38867-0.24746-0.03511-0.41491-0.26645-0.36914-0.50977 4e-3 -0.02778 0.0039-0.07349 0.0039-0.14844 0-0.52461-0.43361-0.72656-0.84375-0.72656-0.40988 0-0.77833 0.34059-0.7793 0.72656 8e-3 0.44646 0 0.89037 0 1.3359l0.75977-0.0059c0.5939-0.01218 0.60137 0.87364 0.0078 0.86914l-0.76758 0.0059c-2e-3 0.3591 2e-3 0.29375 0 0.47461 0 0 0.0081 0.4385-0.0059 0.77148-0.0918 0.97794-0.9329 1.7598-1.9434 1.7598-1.071 0-1.9551-0.86692-1.9551-1.9297 0.0323-1.0933 0.91433-1.9516 2.0234-1.9414l0.61719-0.0059v0.86914l-0.61719 0.0059h-0.00385c-0.60958 0.01781-1.1306 0.42608-1.1406 1.0723 0 0.58879 0.4797 1.0605 1.0762 1.0605 0.59546 0 1.0723-0.42833 1.0723-1.0586 7e-3 -1.0452-5e-3 -2.1995-2e-3 -3.2852 3.8e-4 -0.061 0.0042-0.11046 0.01172-0.17774 0.10057-0.80378 0.82646-1.416 1.6484-1.416z"/></svg>

@@ -0,0 +1,19 @@ 

+ {

+     "name": "Fedora Calendar",

+     "short_name": "Fedocal",

+     "icons": [

+         {

+             "src": "/static/favicon/android-chrome-192x192.png",

+             "sizes": "192x192",

+             "type": "image/png"

+         },

+         {

+             "src": "/static/favicon/android-chrome-512x512.png",

+             "sizes": "512x512",

+             "type": "image/png"

+         }

+     ],

+     "theme_color": "#ffffff",

+     "background_color": "#ffffff",

+     "display": "standalone"

+ }

@@ -0,0 +1,6 @@ 

+ <?xml version="1.0" encoding="UTF-8"?>

+ <svg width="340" height="80" version="1.1" viewBox="0 0 340 80" xmlns="http://www.w3.org/2000/svg">

+  <circle cx="38" cy="38" r="24" fill="#fff"/>

+  <path d="m38.664 12.001c-14.72 0-26.649 11.925-26.659 26.642h-5e-3v20.636h5e-3c8e-3 3.341 2.7141 6.0454 6.0568 6.0454h20.613c14.721-0.01 26.648-11.939 26.648-26.659 0-14.726-11.932-26.665-26.659-26.665zm5.4034 10.972c4.4772 0 8.7045 3.4306 8.7045 8.1591 0 0.43853-2e-3 0.87761-0.0739 1.375-0.12394 1.2581-1.2709 2.1603-2.5227 1.983-1.2518-0.17945-2.1009-1.3645-1.8693-2.6079 0.0212-0.14194 0.0285-0.36695 0.0285-0.74998 0-2.6809-2.1924-3.7159-4.267-3.7159-2.0734 0-3.9439 1.7435-3.9488 3.7159 0.0358 2.2816 0 4.5468 0 6.8238l3.8466-0.029c3.0043-0.0622 3.038 4.4661 0.0341 4.4432l-3.8807 0.029c-0.01 1.8351 0.015 1.5019 5e-3 2.4261 0 0 0.033 2.2415-0.0341 3.9432-0.46441 4.9976-4.718 8.9942-9.8295 8.9942-5.4178 0-9.8863-4.4325-9.8863-9.8636 0.16276-5.5872 4.6224-9.9783 10.233-9.9261l3.125-0.0233v4.4375l-3.125 0.029h-0.0227c-3.0836 0.0911-5.7222 2.1807-5.7727 5.4829 0 3.0089 2.4316 5.4205 5.4488 5.4205 3.0122 0 5.4204-2.1939 5.4204-5.4148 0.0343-5.3414-0.0238-11.242-5e-3 -16.79 2e-3 -0.31171 0.0169-0.55957 0.0512-0.90339 0.50864-4.1076 4.1828-7.2386 8.3409-7.2386z" fill="#51a2da"/>

+  <path d="m98.098 25.273c-2.3922 0-4.5933 0.39791-6.6016 1.1953-2.0083 0.76787-3.7653 1.8751-5.2715 3.3223-1.5062 1.4176-2.6731 3.1016-3.5 5.0508-0.82693 1.9492-1.2402 4.0753-1.2402 6.3789s0.4133 4.4297 1.2402 6.3789c0.82693 1.9492 1.9784 3.6466 3.4551 5.0938 1.5062 1.4176 3.2632 2.5268 5.2715 3.3242 2.0378 0.76787 4.2389 1.1504 6.6016 1.1504 2.5694 0 4.9031-0.44213 7-1.3281 2.0969-0.886 3.8673-2.1701 5.3145-3.8535l-3.7207-3.5449c-1.1223 1.2404-2.3776 2.1708-3.7656 2.791-1.3881 0.59067-2.8932 0.88672-4.5176 0.88672-1.6243 0-3.1161-0.26723-4.4746-0.79883-1.329-0.5316-2.4959-1.2832-3.5-2.2578-0.9746-0.9746-1.7416-2.128-2.3027-3.457-0.5316-1.329-0.79883-2.79-0.79883-4.3848s0.26723-3.0577 0.79883-4.3867c0.56113-1.329 1.3281-2.4805 2.3027-3.4551 1.0041-0.9746 2.171-1.7262 3.5-2.2578 1.3585-0.5316 2.8503-0.79883 4.4746-0.79883s3.1295 0.31144 4.5176 0.93164c1.3881 0.59067 2.6434 1.4903 3.7656 2.7012l3.7207-3.5c-1.4471-1.7129-3.2176-2.997-5.3145-3.8535-2.0969-0.886-4.4152-1.3281-6.9551-1.3281zm26.713 0.44141-13.955 31.01h5.9355l3.0703-7.1758h15.512l3.082 7.1758h6.0234l-13.998-31.01h-5.6699zm23.611 0v31.01h21.973v-4.8711h-16.215v-26.139h-5.7578zm26.535 0v31.01h23.258v-4.8281h-17.498v-8.5488h14.973v-4.7402h-14.973v-8.0625h16.877v-4.8301h-22.637zm29.682 0v31.01h5.7148v-20.994l17.1 20.994h4.7402v-31.01h-5.7148v20.996l-17.1-20.996zm35.883 0v31.01h13.555c3.3668 0 6.3215-0.6478 8.8613-1.9473 2.5399-1.2995 4.518-3.1027 5.9356-5.4062 1.4176-2.3331 2.127-5.0494 2.127-8.1504 0-3.1305-0.70935-5.8468-2.127-8.1504-1.4176-2.3036-3.3957-4.1068-5.9356-5.4062-2.5399-1.2995-5.4945-1.9492-8.8613-1.9492h-13.555zm45.717 0-13.953 31.01h5.9355l3.0703-7.1758h15.51l3.082 7.1758h6.0254l-14-31.01h-5.6699zm23.611 0v31.01h5.7598v-9.0371h7c0.41496 0 0.80245-0.02928 1.1992-0.04883l6.332 9.0859h6.2012l-7.1133-10.15c0.14166-0.05622 0.28614-0.10943 0.42382-0.16992 1.9787-0.886 3.4993-2.1413 4.5625-3.7656 1.0632-1.6539 1.5957-3.6185 1.5957-5.8926s-0.53251-4.2387-1.5957-5.8926-2.5838-2.9226-4.5625-3.8086c-1.9492-0.886-4.2964-1.3301-7.043-1.3301h-12.76zm-63.57 4.873h7.5313c2.3331 0 4.342 0.44408 6.0254 1.3301 1.7129 0.886 3.0278 2.1259 3.9434 3.7207 0.94506 1.5653 1.416 3.4261 1.416 5.582 0 2.1264-0.47095 3.9872-1.416 5.582-0.91554 1.5948-2.2304 2.8347-3.9434 3.7207-1.6834 0.886-3.6923 1.3301-6.0254 1.3301h-7.5313v-21.266zm69.33 0h6.7344c2.5399 0 4.4449 0.5325 5.7148 1.5957 1.2995 1.0632 1.9492 2.5838 1.9492 4.5625 0 1.9492-0.64975 3.4698-1.9492 4.5625-1.2699 1.0927-3.175 1.6406-5.7148 1.6406h-6.7344v-12.361zm-188.01 0.86914 5.8301 13.574h-11.639l5.8086-13.574zm161.43 2e-3 5.8301 13.572h-11.637l5.8066-13.572z" fill="#294172"/>

+ </svg>

@@ -1,486 +0,0 @@ 

- body {

-     padding: 0;

-     height: 100%;

-     margin: 0;

- }

- 

- h4 {

-     color: #CE5C00;

-     font-size: 16px;

-     margin: 0.8em 0 0.4em;

- }

- 

- input {

-     font-size: 100%;

- }

- 

- textarea {

-     font-size: 100%;

- }

- 

- button {

-     font-size: 100%;

- }

- 

- select {

-     font-size: 100%;

- }

- 

- .message {

-     color: green;

- }

- 

- .errors, .required {

-     color: red;

- }

- 

- .required {

-     color: red;

- }

- 

- .warnings {

-     color: orange;

- }

- 

- .orange {

-     color: #CE5C00;

- }

- 

- .blue {

-     color: #0066CC;

- }

- 

- .nobutton {

-     background:none!important;

-     border:none;

-     padding:0!important;

-     color: #0066CC;

-     text-decoration: none;

- }

- 

- #header input {

-     border-radius: 10px;

- }

- 

- header#mainheader{

-     color: white;

-     background: #426ead; /* Old browsers */

-     background: -moz-linear-gradient(top, #426ead 0%, #1e4a88 100%); /* FF3.6+ */

-     background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#426ead), color-stop(100%,#1e4a88)); /* Chrome,Safari4+ */

-     background: -webkit-linear-gradient(top, #426ead 0%,#1e4a88 100%); /* Chrome10+,Safari5.1+ */

-     background: -o-linear-gradient(top, #426ead 0%,#1e4a88 100%); /* Opera 11.10+ */

-     background: -ms-linear-gradient(top, #426ead 0%,#1e4a88 100%); /* IE10+ */

-     background: linear-gradient(to bottom, #426ead 0%,#1e4a88 100%); /* W3C */

-     filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#426ead', endColorstr='#1e4a88',GradientType=0 ); /* IE6-9 */

-     padding: .5em;

-     margin-bottom: .5em;

- }

- 

- header h1,

- header h2,

- header p {

-     margin:0;

- }

- 

- header h1 span {

-     float: right;

-     color: rgba(255, 255, 255, .5);

-     font-size: 0.7em;

-     font-weight: normal;

- }

- 

- #goto_date {

-     display: block;

- }

- 

- #goto_date input[type=text] {

-     width: 42px;

- }

- 

- #tzones {

-     width: 152px;

- }

- 

- section header,

- section aside,

- section nav {

-     display: inline-block;

-     vertical-align: middle;

- }

- 

- #shortmenu {

-     position: relative

- }

- 

- #shortmenu ul {

-     position: fixed;

-     bottom: 20px;

-     right: 10px;

-     list-style: none;

- }

- 

- #content section > header {

-     margin-bottom: .5em;

-     background: url(calendar.png) 0 50% no-repeat;

-     padding-left: 40px;

-     color: #666;

- }

- 

- #content section > header p {

-     font-style: italic;

- }

- 

- #content {

-     position: relative;

-     margin: .5em;

-     min-height: 400px;

- }

- 

- #content #agenda, #content #meeting_list{

-     padding-left: 210px;

- }

- 

- #agenda, .my_meetings {

-     margin-bottom: 2em;

- }

- 

- #agenda table, .my_meetings table {

-     border-collapse:collapse;

-     min-width: 790px;

-     width: 100%;

- }

- 

- .event_blue {

-     display: block;

-     padding:.1em .2em;

-     background-color:rgba(175, 221, 233, .8);

-     color: #333;

-     text-decoration: none;

-     border:1px solid #37abc8;

- }

- 

- td > .event_blue {

-     min-height: 2em;

-     height: 100%;

- }

- 

- 

- .first_event {

-     border-bottom:0px;

- }

- 

- td .first_event {

-     border-bottom: solid 1px rgba(175, 221, 233, .8);

- }

- 

- td .middle_event {

-     border-top: solid 1px rgba(175, 221, 233, .8);

-     border-bottom: solid 1px rgba(175, 221, 233, .8);

- }

- 

- .last_event {

-     border-top:0px;

- }

- 

- td .last_event {

-     border-top: solid 1px rgba(175, 221, 233, .8);

- }

- 

- .my_meetings td {

-     border-bottom: solid 1px grey;

-     border-top: solid 1px grey;

-     border-left: solid 1px grey;

- }

- 

- #agenda td {

-     min-width: 100px;

-     border: 1px solid #e6e6e6;

-     background-color: #f9f9f9;

-     padding: 0;

- }

- 

- #agenda td > .row_height,

- #agenda th > .row_height

- {

-     min-height: 2em;

- }

- 

- #agenda td.empty {

-     background-color: transparent;

-     border: none;

- }

- 

- #agenda .time {

-     border: none;

-     padding-right: .2em;

-     text-align: right;

-     width: 7em;

- }

- 

- #agenda .today {

-     background-color: #FFF6D5;

-     vertical-align: middle;

- }

- 

- .my_meetings .date {

-     border-left: solid 0px;

-     width: 100px;

- }

- 

- .my_meetings .time {

-     width: 150px;

- }

- 

- .my_meetings .calendar {

-     width: 150px;

- }

- 

- .my_meetings .buttons {

-     width: 1%;

-     white-space: nowrap;

-     text-align: center;

-     padding: .1em .2em;

- }

- 

- .my_meetings img {

-     width: 24px;

- }

- 

- #agenda th {

-     border: none;

-     padding: .1em;

-     color: #999;

- }

- 

- #agenda th.today {

-     color: black;

- }

- 

- #mainmenu {

-     width: 200px;

-     margin-right: 0;

-     position: absolute;

-     top: 70px;

-     left: 0;

- }

- 

- #weeks {

-     float:right;

- }

- 

- .ui-button:hover {

-     color: white;

- }

- 

- #home_btn > span {

-     text-indent: 50px;

-     width: 20px;

-     overflow: hidden;

-     background: url(home.png) 50% 6px no-repeat;

- }

- 

- #home_btn:hover span {

-     background-position: 50% -25px;

- }

- 

- /** Menu */

- #mainmenu #accordion > .ui-accordion-content {

-     padding: 0;

- }

- 

- #calendars_menu {

-     overflow: visible;

- }

- 

- .ui-menu-item ul {

-     margin-left: 2px;

- }

- 

- .ui-menu-item {

-     white-space: nowrap;

- }

- 

- #accordion .ui-widget-content a

- {

-     display: block;

-     font-weight: normal;

- }

- 

- .ui-state-focus .ui-menu-item > a,

- .ui-state-focus .ui-menu-item > a:link,

- .ui-state-focus .ui-menu-item > a:visited,

- .ui-state-active .ui-menu-item > a,

- .ui-state-active .ui-menu-item > a:link,

- .ui-state-active .ui-menu-item > a:visited

- {

-     color: #0066cc;

-     display: block;

- }

- 

- .ui-state-focus.ui-menu-item > a,

- .ui-state-focus.ui-menu-item > a:link,

- .ui-state-focus.ui-menu-item > a:visited

- {

-     color: white;

- }

- 

- table.month {

-     margin: 0 auto;

-     border-collapse: collapse;

-     margin-bottom: .5em;

- }

- 

- table.month td {

-     text-align: center;

-     border: 1px solid #e6e6e6;

-     color: #808080;

-     font-weight: bold;

-     padding: .1em;

-     background-color: #fff;

- }

- 

- table.month a{

-     color: #808080;

-     display: block;

-     padding: .1em;

- }

- 

- .busy_day {

-     background-color: #3C6EB4;

-     border-radius: 1em 1em 1em 0.3em;

-     color: #FFFFFF !important;

-     padding: 0px;

-     margin: 0px;

- }

- 

- .busy_day a{

-     color: #FFFFFF !important;

-     padding: .1em;

- }

- 

- table.month .today a{

-     color: #000;

- }

- 

- table.month .current_week td {

-     background-color: #ffcc69;

- }

- 

- table.month td.noday {

-     background-color: #f9f9f9;

- }

- 

- table.month td.today{

-     background-color: #FFF6D5;

-     border-color: #000;

-     border-width: 1px;

-     /* ugly hack to work with table collapsed borders */

-     border-style: double solid solid double;

-     color: #000;

- }

- 

- th.month {

-     color: #666;

-     padding-bottom: .4em;

- }

- 

- th.month a {

-     color: #4d4d4d;

-     display: inline-block;

- }

- 

- th.month a:hover {

-     color: #fff;

- }

- 

- th.month .ui-button-text {

-     padding: 0 .5em;

- }

- 

- .ui-menu a.ui-state-focus {

-     border: 1px solid #DDDDDD;

- }

- 

- li.menu-focus

- {

-     border: 1px solid #3C6EB4;

- }

- 

- .ui-menu .ui-state-focus.menu-focus {

-     margin: 0;

- }

- 

- .calendar_list {

-     display: table;

-     border-spacing: 1em;

- }

- 

- .calendar_link {

-     border: 1px solid #CCCCCC;

-     border-radius: 5px 5px 5px 5px;

-     /*color: black;*/

-     display: table-cell;

-     /*margin: 1em auto;*/

-     overflow: hidden;

-     padding: 2em;

-     width: 15em;

- }

- 

- .calendar_link img, .calendar_link span {

-     display: block;

- }

- 

- .calendar_name {

-     font-weight: bold;

-     margin: .5em 0;

- }

- 

- .calendar_row {

-     display: table-row;

- }

- 

- .calendar_row[a] {

-     display: block;

- }

- 

- #information {

-     width: 500px;

-     height: 200px;

- }

- 

- .ui-autocomplete {

-     width: 100px;

- }

- 

- .limit_future td {

-     border-top: 2px solid;

-     border-color: #31a90c;

-     padding-top: 1em;

-     padding-bottom: 1em;

- }

- 

- .limit_past td {

-     border-top: 2px solid;

-     border-color: red;

- }

- 

- .meeting_normal td {

-     border-top: 1px solid #CCCCCC;

-     padding-top: 1em;

-     padding-bottom: 1em;

- }

- 

- .today {

-     background-color: #FFF6D5;

- }

- 

- .u_dashed {

-     background-image: linear-gradient(to right, #333 40%, rgba(255, 255, 255, 0) 20%);

-     background-position: bottom;

-     background-size: 3px 1px;

-     background-repeat: repeat-x;

- }

- 

- #meeting_description {

-     width: 60%;

- }

@@ -1,452 +0,0 @@ 

- /*

-   Koji styling

-   Copyright (c) 2007,2008, 2009, 2012 Red Hat, Inc.

- 

-   Authors:

-     Mike Bonnet <mikeb@redhat.com>

-     Mike McLean <mikem@redhat.com>

-     Dennis Gilmore <dgilmore@redhat.com>

- */

- 

- html {

-     min-width: 800px;

- }

- 

- body {

-     margin: 0px;

-     padding: 0px;

-     font-size: small;

-     font-family: Cantarell, sans-serif;

-     color: #666;

-     background: #fff url(header-bg.png) repeat-x;

- }

- 

- a, a:visited, a:hover {

-     color: #0066CC;

-     text-decoration: none;

- }

- 

- h4

- {

-     font-size:19px;

-     margin-bottom:14px;

- }

- 

- #wrap {

-     min-width: 750px;

-     max-width:1100px;

-     margin-left:auto;

-     margin-right:auto;

-     padding: 0;

-     text-align: left;

- }

- 

- #innerwrap {

- position:relative;

- }

- 

- #header {

- height:87px

- }

- 

- #headerSearch {

-     display:inline-block;

-     position:relative;

-     float:right;

-     top: 40px;

- }

- 

- #headerSearch input,

- #headerSearch select {

-     font-size: 16px;

- }

- 

- #headerSearch input[type="submit"], #pending input[type="submit"],

- .acls input[type="submit"], #options input[type="submit"]

- {

-     background-color: white;

-     box-shadow: 0 6px 10px rgba(0, 0, 0, 0.15) inset;

-     border-radius: 10px 10px 10px 10px;

-     color: #3D69A8;

-     cursor:pointer;

- }

- 

- #headerSearch input[type="text"]

- {

-     border: 1px solid white;

-     box-shadow: 0 6px 10px rgba(0, 0, 0, 0.15) inset;

-     -webkit-box-shadow:  0 6px 10px rgba(0, 0, 0, 0.15) inset;

-     -moz-box-shadow: 0 6px 10px rgba(0, 0, 0, 0.15) inset;

-     padding: 3px;

- }

- 

- #kojiLogo {

-     /* Used only for the koji logo icon */

-     display:inline-block;

-     padding-top:8px;

- }

- 

- #PoweredByKojiLogo {

-     /* Used only for the koji logo icon */

-     float: right;

-     border: 0px;

-     height: 20px;

- }

- 

- 

- div#content {

-     margin: 0 20px;

-     margin-top:30px;

-     clear: both;

- }

- 

- p#footer {

-     padding-top: 40px;

-     margin-left: 15px;

-     line-height: 1.5em;

-     color: #999;

-     font-size: xx-small;

-     clear: both;

- }

- 

- p#footer a {

-     text-decoration: none;

- }

- 

- .hide {

-     display: none;

- }

- 

- .disabled {

-     color: #808080;

- }

- 

- #mainNav {

-     font-size:16px;

-     font-weight:bold;

-     height: 30px;

- }

- 

- #mainNav ul {

-     padding: 0px;

-     margin: 0px;

-     list-style-type: none;

- }

- 

- #mainNav ul li {

- 

-     float: left;

- 

- }

- 

- #mainNav ul li a {

-     display: block;

-     color: rgba(255,255,255,0.8);

-     text-decoration: none;

-     padding: 0.5em 1.5em;

-     font-size: 0.77em;

-     height: 1.5em;

- }

- 

- #mainNav ul li:hover {

-     -webkit-border-top-left-radius: 5px;

-     -webkit-border-top-right-radius: 5px;

-     -moz-border-radius-topleft: 5px;

-     -moz-border-radius-topright: 5px;

-     border-top-left-radius: 5px;

-     border-top-right-radius: 5px;

-     background-color:rgba(255,255,255,0.1);

- }

- 

- body#home #mainNav li#homeTab a,

- body#locations #mainNav li#locationsTab a,

- body#mymeeting #mainNav li#mymeetingTab a,

- body#admin #mainNav li#adminTab a {

-     background-color: #fff;

-     color: #444;

-     -webkit-border-top-left-radius: 5px;

-     -webkit-border-top-right-radius: 5px;

-     -moz-border-radius-topleft: 5px;

-     -moz-border-radius-topright: 5px;

-     border-top-left-radius: 5px;

-     border-top-right-radius: 5px;

- }

- 

- 

- img.sort {

-     /* used for up/down sort arrows*/

-     vertical-align: baseline;

-     width: 10px;

-     height: 9px;

- }

- 

- td.paginate {

-     text-align: center;

- }

- 

- form.pageJump {

-     float: right;

-     margin-left: 20px;

- }

- 

- form.pageJump select {

-     font-size: smaller;

- }

- 

- div.dataHeader {

-     font-weight: bold;

-     font-size:17px;

-     line-height:28px;

-     padding-bottom:4px;

- }

- 

- div.pageHeader {

-     margin-bottom: 10px;

-     font-weight: bold;

-     font-size: 19px;

- }

- 

- table.nested {

-     float: left;

- }

- 

- td.container {

-     /*padding: 4px 0px;*/

-     width: 100%;

- }

- 

- table.nested th,

- table.nested td {

-     padding: 2px 4px;

- }

- 

- div.toggle {

-     padding: 6px;

- }

- 

- td.tree {

- 

- }

- 

- .tree span.root {

-     font-weight: bold;

- 

- }

- 

- .tree ul {

-     padding-left: 2em;

-     list-style: none;

-     margin-top: 0em;

-     margin-bottom: 0em;

- }

- 

- .tree span.treeBranch {

-     border-bottom: 1px solid #000;

-     border-left: 1px solid #000;

-     font-size: 1.2em;

- }

- 

- .tree li.sibling > span.treeBranch {

-     border-left-width: 0em;

- }

- 

- .tree li.sibling {

-     border-left: 1px solid #000;

- }

- 

- .tree a {

-     text-decoration: none;

- }

- 

- .tree span.treeLabel {

-     position: relative;

-     top: 0.6em;

-     margin-left: 1.2em;

-     padding-left: 0.2em;

- 

-     font-size: 0.83em;

- }

- 

- .tree > ul {

-     padding-bottom: 0.6em;

- }

- 

- .hidden {

-     display: none;

- }

- 

- .tree span.treeToggle {

-     font-weight: bold;

- }

- 

- .tree span.treeLink {

-     font-size: smaller;

- }

- 

- .adminLink {

-     color: #000;

- }

- 

- img.stateimg {

-     margin-top: -6px;

-     margin-bottom: -6px;

- }

- 

- .charlist {

-     text-align: center;

- }

- 

- img.graphrow {

-     background-color: #00f;

-     vertical-align: bottom;

- }

- 

- table.data-list {

-     width: 100%;

- }

- 

- table.data-list td {

-     vertical-align: text-top;

-     padding-left:3px;

- }

- 

- tr.list-header {

-     background-color: #fff;

- }

- 

- tr.list-header th {

-     background-color: #eee;

-     box-shadow: 0 -1px 0 #ddd,  0 1px 0 #ddd;

-     text-align:left;

-     padding-left:4px;

- }

- 

- tr.list-header th {

- min-width:1px;

- }

- 

- table.nested th

- {

-     min-width:1px;

- }

- 

- tr.row-even,

- tr.row-odd

- {

-     box-shadow: 0 1px 0 #ddd;

- }

- 

- tr.row-even td,

- tr.row-odd td

- {

- padding-left:4px;

- }

- 

- tr.row-odd td:first-child,

- tr.row-even td:first-child

-  {

-     box-shadow:  -1px 0 0 #ddd;

- }

- 

- tr.row-odd td:last-child,

- tr.row-even td:last-child {

-     box-shadow:  1px 0 0 #ddd;

- }

- 

- tr.list-header th:first-child

- {

-     box-shadow:  -1px 0 0 #ddd,0 -1px 0 #ddd,  0 1px 0 #ddd;

- }

- 

- tr.list-header th:last-child

- {

-     box-shadow:  1px 0 0 #ddd,0 -1px 0 #ddd,  0 1px 0 #ddd;

- }

- 

- tr.row-even td.tree {

- 

- }

- 

- tr.row-even td.tree span.treeLabel {

- 

- }

- 

- .taskfree, .taskfree:visited, .taskfree:hover {

-     color: #3300CC;

-     background-color:#fff;

- }

- .taskopen, .taskopen:visited, .taskopen:hover {

-     color: #FF6600;

-     background-color:#fff;

- }

- .taskclosed, .taskclosed:visited, .taskclosed:hover {

-     color: #00CC00;

-     background-color:#fff;

- }

- .taskcanceled, .taskcanceled:visited, .taskcanceled:hover {

-     color: #CC9900;

-     background-color:#fff;

- }

- .taskassigned, .taskassigned:visited, .taskassigned:hover {

-     color: #CC00FF;

-     background-color:#fff;

- }

- .taskfailed, .taskfailed:visited, .taskfailed:hover {

-     color: #CC0000;

-     background-color:#fff;

- }

- 

- a.help {

-     text-decoration: underline;

- }

- 

- abbr {

-     cursor: help;

- }

- 

- .changelog {

-     font-family: monospace;

-     font-size: medium;

-     white-space: pre;

- }

- 

- #headerHelp {

-     float: right;

-     margin: 15px 10px 0 0;

- }

- 

- .filterlist {

-     font-size: smaller;

- }

- 

- span#loginInfo {

-     background-color: #ccc;

-     font-weight: bold;

-     padding: 3px 15px;

-     position: absolute;

-     right: 0;

-     top: 0;

-     -webkit-border-bottom-left-radius: 3px;

-     -webkit-border-bottom-right-radius: 3px;

-     -moz-border-radius-bottomleft: 3px;

-     -moz-border-radius-bottomright: 3px;

-     border-bottom-left-radius: 3px;

-     border-bottom-right-radius: 3px;

-     box-shadow: 0 10px 10px rgba(255, 255, 255, 0.5) inset, 0 1px 3px rgba(0, 0, 0, 0.5);

-     -mox-box-shadow: 0 10px 10px rgba(255, 255, 255, 0.5) inset, 0 1px 3px rgba(0, 0, 0, 0.5);

-     -webkit-box-shadow: 0 10px 10px rgba(255, 255, 255, 0.5) inset, 0 1px 3px rgba(0, 0, 0, 0.5);

- }

- 

- .smaller {

-     font-size: smaller;

- }

- 

- .rpmheader {

-     /*font-family: monospace;

-     font-size: medium;

-     white-space: pre;*/

- }

- 

- .error {

-     color: red;

- }

@@ -1,51 +1,78 @@ 

  {% macro render_field(field, after="") %}

- <div class="row">

-   {{ field.label }}{{ field(**kwargs)|safe }}

-   {% if field.flags.required %}<span class="required">*</span>{% endif %}

-   {% if after %} {{ after }}{% endif %}

+ <div class="form-group">

+   {{ field.label }}

+   {% if field.flags.required %}<span class="text-danger">*</span>{% endif %}

    {% if field.errors %}

-   <ul class="errors">

+   {{ field(class_="form-control is-invalid", **kwargs)|safe }}

+   {% else %}

+   {{ field(class_="form-control", **kwargs)|safe }}

+   {% endif %}

+   {% if after %}<small>{{ after }}</small>{% endif %}

+   {% if field.errors %}

+   <ul class="text-danger list-unstyled">

      {% for error in field.errors %}

-     <li>{{ error }}</li>

+     <li><small>{{ error }}</small></li>

      {% endfor %}

    </ul>

    {% endif %}

  </div>

  {% endmacro %}

  

- {% macro render_field_invert(field, after="") %}

- <div class="row">

-   {{ field(**kwargs)|safe }}{{ field.label }}

-   {% if field.flags.required %}<span class="required">*</span>{% endif %}

-   {% if after %} {{ after }}{% endif %}

-   {% if field.errors %}

-   <ul class="errors">

-     {% for error in field.errors %}

-     <li>{{ error }}</li>

-     {% endfor %}

-   </ul>

-   {% endif %}

+ {% macro render_field_checkbox(field, after="") %}

+ <div class="form-group">

+   <div class="custom-control custom-checkbox">

+     {{ field(class_="custom-control-input", **kwargs)|safe }}

+     {{ field.label(class_="custom-control-label") }}

+     {% if field.flags.required %}<span class="text-danger">*</span>{% endif %}

+     {% if after %}<small>{{ after }}</small>{% endif %}

+     {% if field.errors %}

+     <ul class="text-danger list-unstyled">

+       {% for error in field.errors %}

+       <li><small>{{ error }}</small></li>

+       {% endfor %}

+     </ul>

+     {% endif %}

+   </div>

+ </div>

+ {% endmacro %}

+ 

+ {% macro render_field_file(field, after="") %}

+ <div class="form-group">

+   <div class="custom-file">

+     {% if field.errors %}

+     {{ field(class_="custom-file-input is-invalid", **kwargs)|safe }}

+     {% else %}

+     {{ field(class_="custom-file-input", **kwargs)|safe }}

+     {% endif %}

+     {{ field.label(class_="custom-file-label") }}

+     {% if after %}<small>{{ after }}</small>{% endif %}

+     {% if field.errors %}

+     <ul class="text-danger list-unstyled">

+       {% for error in field.errors %}

+       <li><small>{{ error }}</small></li>

+       {% endfor %}

+     </ul>

+     {% endif %}

+   </div>

  </div>

  {% endmacro %}

  

  {% macro render_field_in_row(field, after="", escape_after=False) %}

- <tr>

-     <td>

-       {{ field.label }}

-       {% if field.flags.required %}<span class="required">*</span>{% endif %}

-     </td>

-     <td>{{ field(**kwargs)|safe }}</td>

+ <div class="col">

+     {{ field.label }}

+     {% if field.flags.required %}<span class="text-danger">*</span>{% endif %}

+     {{ field(class_="form-control", **kwargs)|safe }}

      {% if escape_after %}

-     {% autoescape off%} {% if after %} <td>{{ after }}</td>{% endif %} {% endautoescape %}

+     {% autoescape off%} {% if after %} <small>{{ after }}</small>{% endif %} {% endautoescape %}

      {% else %}

-     {% if after %} <td>{{ after }}</td>{% endif %}

+     {% if after %} <small>{{ after }}</small>{% endif %}

      {% endif %}

- </tr>

- {% if field.errors %}

- <tr class="errors">

- {% for error in field.errors %}

- <td>{{ error }}</td>

- {% endfor %}

- </tr>

- {% endif %}

+     {% if field.errors %}

+     <tr class="text-danger list-unstyled">

+     {% for error in field.errors %}

+     <td>{{ error }}</td>

+     {% endfor %}

+     </tr>

+     {% endif %}

+ </div>

  {% endmacro %}

@@ -1,5 +1,4 @@ 

  {% extends "master.html" %}

- {% from "_formhelpers.html" import render_field_in_row %}

  

  {% block title %}{{ _('Add calendar') }}{% endblock %}

  
@@ -13,17 +12,17 @@ 

      </header>

  

      <form action="" method="post">

-         <table>

-         {{ render_field_in_row(form.calendar_name) }}

-         {{ render_field_in_row(form.calendar_contact) }}

-         {{ render_field_in_row(form.calendar_description) }}

-         {{ render_field_in_row(form.calendar_editor_groups) }}

-         {{ render_field_in_row(form.calendar_admin_groups) }}

-         {{ render_field_in_row(form.calendar_status) }}

-         </table>

-         <p class="buttons indent">

-             <input type="submit" class="submit positive button" value="{{ _('Create') }}">

-             <input type="button" value="{{ _('Cancel') }}" class="button" onclick="history.back();">

+         <div>

+         {{ render_field(form.calendar_name) }}

+         {{ render_field(form.calendar_contact) }}

+         {{ render_field(form.calendar_description) }}

+         {{ render_field(form.calendar_editor_groups) }}

+         {{ render_field(form.calendar_admin_groups) }}

+         {{ render_field(form.calendar_status) }}

+         </div>

+         <p class="d-flex justify-content-end">

+             <input type="button" value="{{ _('Cancel') }}" class="btn btn-secondary mr-1" onclick="history.back();">

+             <input type="submit" class="btn btn-primary" value="{{ _('Create') }}">

              {{ form.csrf_token }}

          </p>

      </form>

@@ -10,7 +10,7 @@ 

  

  

  {% block content %}

- {% from "_formhelpers.html" import render_field_in_row %}

+ {% from "_formhelpers.html" import render_field, render_field_checkbox, render_field_in_row %}

  

  <section>

      <header>
@@ -20,46 +20,48 @@ 

  

      <form action="{{ url_for('add_meeting',

                      calendar_name=calendar.calendar_name) }}" method="post">

-         <table>

-             {{ render_field_in_row(form.meeting_name) }}

-             {{ render_field_in_row(form.meeting_date) }}

-             {{ render_field_in_row(form.meeting_date_end) }}

+         <div>

+             {{ render_field(form.meeting_name) }}

+             {{ render_field(form.meeting_date) }}

+             {{ render_field(form.meeting_date_end) }}

+             <div class="form-row">

              {{ render_field_in_row(form.meeting_time_start)}}

              {{ render_field_in_row(form.meeting_time_stop)}}

-             {{ render_field_in_row(form.full_day) }}

-             {{ render_field_in_row(form.meeting_timezone) }}

+             </div>

+             {{ render_field_checkbox(form.full_day) }}

+             {{ render_field(form.meeting_timezone) }}

              {% macro get_after_markdown() %}

                  {{_('Supports the') }} <a href="http://daringfireball.net/projects/markdown/syntax"

                      rel="noopener noreferrer" target="_blank">{{ _('Markdown syntax') }}</a>

-                 <input type="button" class="event preview" value="{{ _('Preview')  }}">

+                 <input type="button" class="btn btn-sm btn-secondary preview" value="{{ _('Preview')  }}">

              {% endmacro %}

-             {{ render_field_in_row(

+             {{ render_field(

                      form.information,

                      after=get_after_markdown(),

                      escape_after=True) }}

-             {{ render_field_in_row(form.wiki_link) }}

-             {{ render_field_in_row(form.meeting_location) }}

-         </table>

+             {{ render_field(form.wiki_link) }}

+             {{ render_field(form.meeting_location) }}

+         </div>

  

          <h4>{{ _('Recurring event') }}</h4>

          <p>{{ _('If this is a regular meeting this is where you want to set it as so.') }}</p>

-         <table>

-             {{ render_field_in_row(form.frequency) }}

-             {{ render_field_in_row(form.end_repeats, after=_('Leave empty if there is no end date for the repeat.')) }}

-         </table>

+         <div>

+             {{ render_field(form.frequency) }}

+             {{ render_field(form.end_repeats, after=_('Leave empty if there is no end date for the repeat.')) }}

+         </div>

  

          <h4>{{ _('Reminder') }}</h4>

          <p>{{ _('You may want fedocal to send an email to a mailing list of your choices.') }}</p>

          <p>{{ _('Note that fedocal will send the reminder in your name.') }}</p>

-         <table>

-             {{ render_field_in_row(form.remind_when) }}

-             {{ render_field_in_row(form.reminder_from) }}

-             {{ render_field_in_row(form.remind_who) }}

-         </table>

- 

-         <p class="buttons indent">

-             <input type="submit" class="submit positive button" value="{{ _('Add') }}">

-             <input type="button" value="{{ _('Cancel') }}" class="button" onclick="history.back();">

+         <div>

+             {{ render_field(form.remind_when) }}

+             {{ render_field(form.reminder_from) }}

+             {{ render_field(form.remind_who) }}

+         </div>

+ 

+         <p class="d-flex justify-content-end">

+             <input type="button" value="{{ _('Cancel') }}" class="btn btn-secondary mr-1" onclick="history.back();">

+             <input type="submit" class="btn btn-primary" value="{{ _('Add') }}">

              {{ form.csrf_token }}

          </p>

      </form>
@@ -217,14 +219,14 @@ 

        });

  

        var cb = $('#full_day')

-       var tz = $('#meeting_timezone').parent().parent();

+       var tz = $('#meeting_timezone').parent();

        if(cb.prop('checked')) {

          tz.val('UTC');

          tz.hide();

        };

  

        $('#full_day').click(function() {

-         var tz = $('#meeting_timezone').parent().parent();

+         var tz = $('#meeting_timezone').parent();

          if(this.checked == false) {

            tz.show();

          } else {

@@ -1,12 +1,11 @@ 

  {% extends "master.html" %}

  

  {% block title %}{{ _('Admin') }}{% endblock %}

- {%block tag %}admin{% endblock %}

  

  {% block content %}

  <h2>{{ _('Admin interface') }}</h2>

  

- <ul>

+ <ul class="list-unstyled">

    <li>

      <a href="{{ url_for('add_calendar') }}">

          {{ _('Add calendar') }}
@@ -14,18 +13,22 @@ 

    </li>

  

    <li>

-     <form action="{{url_for('admin')}}">

-       <select name="calendar">

-         {% for calendar in calendars %}

-         <option value="{{ calendar.calendar_name }}">

-           {{ calendar.calendar_name }}</option>

-         {% endfor %}

-       </select>

-       <select name="action">

-         <option value="edit">{{ _('Edit') }}</option>

-         <option value="delete">{{ _('Delete') }}</option>

-       </select>

-       <input type="submit" value="{{ _('Go') }}" class="nobutton"/>

+     <form class="form-inline" action="{{url_for('admin')}}">

+       <div class="input-group">

+         <select class="custom-select" name="calendar">

+           {% for calendar in calendars %}

+           <option value="{{ calendar.calendar_name }}">

+             {{ calendar.calendar_name }}</option>

+           {% endfor %}

+         </select>

+         <select class="custom-select" name="action">

+           <option value="edit">{{ _('Edit') }}</option>

+           <option value="delete">{{ _('Delete') }}</option>

+         </select>

+         <div class="input-group-append">

+           <input type="submit" value="{{ _('Go') }}" class="btn btn-primary"/>

+         </div>

+       </div>

      </form>

    </li>

  

@@ -7,148 +7,104 @@ 

  

  {% block content %}

  <section>

- 

-       <header>

-           <h2>

-               {% if calendar %}

-               <a href="{{url_for('calendar',

-                   calendar_name=calendar.calendar_name)}}">

-               {{ calendar.calendar_name }}

-               {% else %}

-               <a href="{{url_for('location',

-                   loc_name=location) }}">

-               {{ location }}

-               {% endif %}

-               </a>

-           </h2>

-           {% if calendar %}

-           <p>{{ calendar.calendar_description or '-' }}</p>

-           {% else %}

-           <p> - </p>

-           {% endif %}

-       </header>

-       <nav id="weeks">

-           {% if calendar %}

-           <a href="{{url_for('calendar',

-               calendar_name=calendar.calendar_name, year=prev_week.year,

-               month=prev_week.month, day=prev_week.day)}}" class="button"

-               title="{{ _('Previous week') }}">

-               &lt;

-           </a>

-           {{ weekdays | WeekHeading }}

-           <a href="{{url_for('calendar',

-               calendar_name=calendar.calendar_name, year=next_week.year,

-               month=next_week.month, day=next_week.day)}}" class="button"

-               title="{{ _('Next week') }}">

-               &gt;

-           </a>

-           <a href="{{url_for('calendar',

-               calendar_name=calendar.calendar_name)}}" class="button" id="home_btn" title="{{ _('Today') }}">

-               {{ _('Today') }}

-           </a>

-           {% else %}

-           <a href="{{url_for('location',

-               loc_name=location, year=prev_week.year,

-               month=prev_week.month, day=prev_week.day)}}" class="button"

-               title="{{ _('Previous week') }}">

-               &lt;

-           </a>

-           {{ weekdays | WeekHeading }}

-           <a href="{{url_for('location',

-               loc_name=location, year=next_week.year,

-               month=next_week.month, day=next_week.day)}}" class="button"

-               title="{{ _('Next week') }}">

-               &gt;

-           </a>

-           <a href="{{url_for('location', loc_name=location,)}}"

-               class="button" title="{{ _('Today') }}" id="home_btn">

-             {{ _('Today') }}

-           </a>

-           {% endif %}

-       </nav>

+     <div class="row">

+     <div class="col-md-3">

+     <header>

+         <h2>

+             {% if calendar %}

+             <a href="{{url_for('calendar',

+                 calendar_name=calendar.calendar_name)}}">

+             {{ calendar.calendar_name }}

+             {% else %}

+             <a href="{{url_for('location',

+                 loc_name=location) }}">

+             {{ location }}

+             {% endif %}

+             </a>

+         </h2>

+         {% if calendar %}

+         <p>{{ calendar.calendar_description or '-' }}</p>

+         {% else %}

+         <p> - </p>

+         {% endif %}

+     </header>

      <aside id="mainmenu">

          {% autoescape off%}{{ curmonth_cal }}{% endautoescape %}

-         <form action="{{url_for('goto')}}" id="goto_date">

+         <form class="form-inline mb-2" action="{{url_for('goto')}}" id="goto_date">

+           <div class="input-group">

            {% if calendar %}

            <input type="hidden" name="calendar" value="{{calendar.calendar_name}}"/>

            {% else %}

            <input type="hidden" name="location" value="{{location}}"/>

            {% endif %}

- 	  <input type="text" name="day" placeholder="{{ now.day }}" title="{{ _('Day in decimal') }}"/>

- 	  <input type="text" name="month" placeholder="{{ now.month }}" title="{{ _('Month in decimal') }}"/>

- 	  <input type="text" name="year" placeholder="{{  now.year }}" title="{{ _('Year in decimal') }}"/>

-           <input type="submit" value="{{ _('Go') }}"/>

+ 	  <input type="text" class="form-control" name="day" placeholder="{{ now.day }}" title="{{ _('Day in decimal') }}"/>

+ 	  <input type="text" class="form-control" name="month" placeholder="{{ now.month }}" title="{{ _('Month in decimal') }}"/>

+ 	  <input type="text" class="form-control" name="year" placeholder="{{  now.year }}" title="{{ _('Year in decimal') }}"/>

+           <div class="input-group-append">

+             <input type="submit" class="btn btn-secondary" value="{{ _('Go') }}"/>

+           </div>

+           </div>

          </form>

-         <form action="{{ url_for('update_tz') }}" id="update_tz">

-             <select name="tzone" id="tzones">

+         <form class="form-inline mb-2" action="{{ url_for('update_tz') }}" id="update_tz">

+           <div class="input-group">

+             <select class="custom-select" name="tzone" id="tzones">

                  {% for tz in tzones %}

                  {% if tz == tzone %}<option value="{{tz}}" selected>{{ tz }}</option>

                  {% else %}<option value="{{tz}}">{{ tz }}</option>{% endif %}

                  {% endfor %}

              </select>

-             <input type="submit" value="Go"/>

+             <div class="input-group-append">

+               <input class="btn btn-secondary" type="submit" value="Go"/>

+             </div>

+           </div>

          </form>

  

-         {% if not calendar %}

-         <p>

-           <a href="{{ url_for('location_list',loc_name=location) }}">

-               {{ _('List view') }}

-           </a>

-         </p>

-         {% endif %}

+         <div>

  

-         <div id="accordion">

-         <h4>{{ _('Calendars') }}</h4>

-         <ul id="calendars_menu" class="checkmark-list">

-             {% for _calendar in calendars %}

-             {% if _calendar.calendar_status == 'Enabled' %}

-             <li {% if calendar and _calendar.calendar_name == calendar.calendar_name%}

-             class="menu-focus"

-             {% endif %}>

-                 <a href="{{url_for('calendar',

-                     calendar_name=_calendar.calendar_name)}}">

-                     {{ _calendar.calendar_name }}

-                 </a>

-                 <ul>

-                     {% if g.fas_user %}

-                     <li>

-                         <a href="{{ url_for('add_meeting',

-                             calendar_name=_calendar.calendar_name) }}">{{ _('Add a meeting') }}</a>

-                     </li>

-                     {% endif %}

-                     <li>

-                         <a href="{{ url_for('calendar_list',

-                             calendar_name=_calendar.calendar_name) }}">

-                             {{ _('List view') }}

-                         </a>

-                     </li>

-                     <li>

-                         <a href="{{url_for('ical_out',

-                                            calendar_name=_calendar.calendar_name)}}"

-                             title="{{ _('Add this calendar to your agenda') }}">{{ _('iCal export') }}</a>

-                     </li>

-                     <li>

-                         <a onclick="CopyToClipboard('{{url_for('ical_out',

-                                            calendar_name=_calendar.calendar_name)}}')"

-                             title="{{ _('Copy the iCal feed URL') }}">{{ _('Copy iCal link') }}</a>

-                     </li>

-                     {% if _calendar.calendar_contact %}

-                     <li>

-                         <a href="mailto:{{_calendar.calendar_contact}}">

-                             {{ _('Contact admin') }}

-                         </a>

-                     </li>

-                     {% endif %}

-                 </ul>

-             </li>

-             {% endif %}

-             {% endfor %}

+         <ul class="list-unstyled">

+         {% if calendar %}

+         {% if g.fas_user %}

+         <li>

+             <a href="{{ url_for('add_meeting',

+                                 calendar_name=calendar.calendar_name) }}">{{ _('Add a meeting') }}</a>

+         </li>

+         {% endif %}

+         <li>

+             <a href="{{ url_for('calendar_list',

+                                 calendar_name=calendar.calendar_name) }}">

+                 {{ _('List view') }}

+             </a>

+         </li>

+         <li>

+             <a href="{{url_for('ical_out',

+                                calendar_name=calendar.calendar_name)}}"

+                title="{{ _('Add this calendar to your agenda') }}">{{ _('iCal export') }}</a>

+         </li>

+         <li>

+             <a onclick="CopyToClipboard('{{url_for('ical_out',

+                                                     calendar_name=calendar.calendar_name)}}')"

+                title="{{ _('Copy the iCal feed URL') }}" href="#">{{ _('Copy iCal link') }}</a>

+         </li>

+         {% if calendar.calendar_contact %}

+         <li>

+             <a href="mailto:{{calendar.calendar_contact}}">

+                 {{ _('Contact admin') }}

+             </a>

+         </li>

+         {% endif %}

+         {% else %}

+         <li>

+             <a href="{{ url_for('location_list',loc_name=location) }}">

+                 {{ _('List view') }}

+             </a>

+         </li>

+         {% endif %}

          </ul>

  

          {% if calendar and (admin or calendar_admin)  %}

          <h4>{{ _('Admin') }}</h4>

          <div>

-         <ul class="checkmark-list">

+         <ul class="list-unstyled">

              {% if admin %}

              <li>

                  <a href="{{ url_for('edit_calendar',
@@ -174,7 +130,7 @@ 

          {% endif %}

        </div>

        {% if calendar %}

-       <ul class="checkmark-list">

+       <ul class="list-unstyled">

          {% if calendar.calendar_admin_group %}

          <li>{{ _('Administrators:') }} {{ calendar.calendar_admin_group }}</li>

          {% endif %}
@@ -187,7 +143,47 @@ 

        {% endif %}

  

      </aside>

-     <div id='agenda'>

+     </div>

+     <div class="col-md-9" id='agenda'>

+     <nav class="text-center">

+         {% if calendar %}

+         <a href="{{url_for('calendar',

+             calendar_name=calendar.calendar_name, year=prev_week.year,

+             month=prev_week.month, day=prev_week.day)}}" class="btn btn-link"

+             title="{{ _('Previous week') }}">

+             &lt;

+         </a>

+         {{ weekdays | WeekHeading }}

+         <a href="{{url_for('calendar',

+             calendar_name=calendar.calendar_name, year=next_week.year,

+             month=next_week.month, day=next_week.day)}}" class="btn btn-link"

+             title="{{ _('Next week') }}">

+             &gt;

+         </a>

+         <a href="{{url_for('calendar',

+             calendar_name=calendar.calendar_name)}}" class="btn btn-primary" id="home_btn" title="{{ _('Today') }}">

+             {{ _('Today') }}

+         </a>

+         {% else %}

+         <a href="{{url_for('location',

+             loc_name=location, year=prev_week.year,

+             month=prev_week.month, day=prev_week.day)}}" class="btn btn-link"

+             title="{{ _('Previous week') }}">

+             &lt;

+         </a>

+         {{ weekdays | WeekHeading }}

+         <a href="{{url_for('location',

+             loc_name=location, year=next_week.year,

+             month=next_week.month, day=next_week.day)}}" class="btn btn-link"

+             title="{{ _('Next week') }}">

+             &gt;

+         </a>

+         <a href="{{url_for('location', loc_name=location,)}}"

+             class="btn btn-primary" title="{{ _('Today') }}" id="home_btn">

+           {{ _('Today') }}

+         </a>

+         {% endif %}

+     </nav>

      {% macro render_meetings(meeting_list, loopidx, prev_meetings, next_meetings, time) -%}

        {% if loopidx == day_index %}

            <td class="today" data-date="{{ weekdays[loopidx - 1] }}", data-time="{{ time }}">
@@ -246,7 +242,7 @@ 

        {% endif %}

        </td>

      {%- endmacro %}

-       <table>

+       <table class="weekly table-responsive">

            <tr>

                <th>{{ _('%(timezone)s time', timezone=tzone) }}</th>

                {% for day in weekdays %}
@@ -319,34 +315,29 @@ 

            </tr>

        </table>

      </div>

+     </div>

    </section>

  {% endblock %}

  

  {% block sidebar %}

  {% if calendar %}

    <aside id="shortmenu">

-     <ul>

+     <div class="btn-group">

        {% if calendar_editor %}

-       <li>

-         <a href="{{ url_for('add_meeting',

+         <a class="btn btn-light" href="{{ url_for('add_meeting',

                              calendar_name=calendar.calendar_name) }}">

              <img src="{{ url_for('static', filename='plus.png') }}" title="{{ _('Add meeting') }}" alt="{{ _('Add meeting') }}" />

          </a>

-       </li>

        {% endif %}

-       <li>

-         <a href="{{ url_for('calendar_list',

+         <a class="btn btn-light" href="{{ url_for('calendar_list',

                              calendar_name=calendar.calendar_name) }}">

              <img src="{{ url_for('static', filename='list.png') }}" title="{{ _('List view') }}" alt="{{ _('List view') }}" />

          </a>

-       </li>

-       <li>

-         <a href="{{url_for('ical_out',

+         <a class="btn btn-light" href="{{url_for('ical_out',

                             calendar_name=calendar.calendar_name)}}">

              <img src="{{ url_for('static', filename='ical.png') }}" title="{{ _('iCal Export') }}" alt="{{ _('iCal export') }}" />

          </a>

-       </li>

-     </ul>

+     </div>

    </aside>

  {% endif %}

  {% endblock %}

@@ -1,5 +1,4 @@ 

  {% extends "master.html" %}

- {% from "_formhelpers.html" import render_field %}

  

  {%block tag %}api{% endblock %}

  

@@ -19,10 +19,10 @@ 

  <p>{{ _('Are you positively sure that\'s what you want to do?') }}</p>

  

  <form action="" method="post">

- {{ render_field_invert(form.confirm_delete) }}

- <p class="buttons indent">

-     <input type="submit" class="submit positive button" value="{{ _('Delete') }}">

-     <input type="button" value="{{ _('Cancel') }}" class="button" onclick="history.back();">

+ {{ render_field_checkbox(form.confirm_delete) }}

+ <p class="d-flex justify-content-end">

+     <input type="button" value="{{ _('Cancel') }}" class="btn btn-secondary mr-1" onclick="history.back();">

+     <input type="submit" class="btn btn-danger" value="{{ _('Delete') }}">

  </p>

  {{ form.csrf_token }}

  </form>

@@ -18,11 +18,11 @@ 

  <p>{{ _("Are you positively sure that's what you want to do?") }}</p>

  

  <form action="" method="post">

- {{ render_field_invert(form.confirm_delete) }}

- <p class="buttons indent">

-     <input id="confirm_button" type="submit" class="submit positive button"

+ {{ render_field_checkbox(form.confirm_delete) }}

+ <p class="d-flex justify-content-end">

+     <input type="button" value="{{ _('Cancel') }}" class="btn btn-secondary mr-1" onclick="history.back();">

+     <input id="confirm_button" type="submit" class="btn btn-danger"

          value="{{ _('Delete') }}">

-     <input type="button" value="{{ _('Cancel') }}" class="button" onclick="history.back();">

  </p>

  {{ form.csrf_token }}

  </form>

@@ -35,7 +35,7 @@ 

  <p>{{ _("Are you positively sure that's what you want to do?") }}</p>

  

  <form action="" method="post">

- {{ render_field_invert(form.confirm_delete) }}

+ {{ render_field_checkbox(form.confirm_delete) }}

  {% if meeting.recursion_frequency %}

  <p>{{ _('This meeting is a regular meeting, are you sure you want to delete all

  of them or just this one ? (Check only if you want to delete all future
@@ -44,13 +44,13 @@ 

  <p>

      {{ _('This meeting is recurrent, it occurs every %(frequency)s days until %(ends)s', frequency=meeting.recursion_frequency, ends=meeting.recursion_ends) }}

  </p>

- {{ render_field_invert(form.confirm_futher_delete) }}

+ {{ render_field_checkbox(form.confirm_futher_delete) }}

  {% endif %}

- <p class="buttons indent">

-     <input id="confirm_button" type="submit" class="submit positive button"

-         value="{{ _('Delete') }}">

-     <input type="button" value="{{ _('Cancel') }}" class="button"

+ <p class="d-flex justify-content-end">

+     <input type="button" value="{{ _('Cancel') }}" class="btn btn-secondary mr-1"

             onclick="history.back();">

+     <input id="confirm_button" type="submit" class="btn btn-danger"

+         value="{{ _('Delete') }}">

  </p>

  {{ form.csrf_token }}

  </form>

@@ -1,5 +1,4 @@ 

  {% extends "master.html" %}

- {% from "_formhelpers.html" import render_field_in_row %}

  

  {% block title %}{{ _('Edit calendar') }}{% endblock %}

  {%block tag %}admin{% endblock %}
@@ -13,16 +12,16 @@ 

  

      <form action="" method="post">

          <table>

-         {{ render_field_in_row(form.calendar_name) }}

-         {{ render_field_in_row(form.calendar_contact) }}

-         {{ render_field_in_row(form.calendar_description) }}

-         {{ render_field_in_row(form.calendar_editor_groups) }}

-         {{ render_field_in_row(form.calendar_admin_groups) }}

-         {{ render_field_in_row(form.calendar_status) }}

+         {{ render_field(form.calendar_name) }}

+         {{ render_field(form.calendar_contact) }}

+         {{ render_field(form.calendar_description) }}

+         {{ render_field(form.calendar_editor_groups) }}

+         {{ render_field(form.calendar_admin_groups) }}

+         {{ render_field(form.calendar_status) }}

          </table>

-         <p class="buttons indent">

-             <input type="submit" class="submit positive button" value="{{ _('Edit') }}">

-             <input type="button" value="{{ _('Cancel') }}" class="button" onclick="history.back();">

+         <p class="d-flex justify-content-end">

+             <input type="button" value="{{ _('Cancel') }}" class="btn btn-secondary mr-1" onclick="history.back();">

+             <input type="submit" class="btn btn-primary" value="{{ _('Edit') }}">

              {{ form.csrf_token }}

          </p>

      </form>

@@ -14,32 +14,34 @@ 

  

      <form action="" method="post">

          <table>

-             {{ render_field_in_row(form.calendar_name) }}

-             {{ render_field_in_row(form.meeting_name) }}

-             {{ render_field_in_row(form.meeting_date) }}

-             {{ render_field_in_row(form.meeting_date_end) }}

+             {{ render_field(form.calendar_name) }}

+             {{ render_field(form.meeting_name) }}

+             {{ render_field(form.meeting_date) }}

+             {{ render_field(form.meeting_date_end) }}

+             <div class="form-row">

              {{ render_field_in_row(form.meeting_time_start) }}

              {{ render_field_in_row(form.meeting_time_stop) }}

-             {{ render_field_in_row(form.full_day) }}

-             {{ render_field_in_row(form.meeting_timezone) }}

-             {{ render_field_in_row(form.comanager) }}

+             </div>

+             {{ render_field_checkbox(form.full_day) }}

+             {{ render_field(form.meeting_timezone) }}

+             {{ render_field(form.comanager) }}

              {% macro get_after_markdown() %}

                  {{_('Supports the') }} <a href="http://daringfireball.net/projects/markdown/syntax"

                      rel="noopener noreferrer" target="_blank">{{ _('Markdown syntax') }}</a>

-                 <input type="button" class="event preview" value="{{ _('Preview')  }}">

+                 <input type="button" class="btn btn-sm btn-secondary preview" value="{{ _('Preview')  }}">

              {% endmacro %}

-             {{ render_field_in_row(

+             {{ render_field(

                  form.information,

                      after=get_after_markdown(),

                      escape_after=True) }}

-             {{ render_field_in_row(form.meeting_location) }}

+             {{ render_field(form.meeting_location) }}

          </table>

  

          <h4>{{ _('Recurring event') }}</h4>

          <p>{{ _('If this is a regular meeting this is where you want to set it as so.') }}</p>

          <table>

-             {{ render_field_in_row(form.frequency) }}

-             {{ render_field_in_row(form.end_repeats,

+             {{ render_field(form.frequency) }}

+             {{ render_field(form.end_repeats,

                  after=_('Leave empty if there is no end date for the repeat.')) }}

          </table>

  
@@ -47,19 +49,19 @@ 

          <p>{{ _('You may want fedocal to send an email to a mailing list of your choices.') }}</p>

          <p>{{ _('Note that fedocal will send the reminder in your name.') }}</p>

          <table>

-             {{ render_field_in_row(form.remind_when) }}

-             {{ render_field_in_row(form.reminder_from) }}

-             {{ render_field_in_row(form.remind_who) }}

+             {{ render_field(form.remind_when) }}

+             {{ render_field(form.reminder_from) }}

+             {{ render_field(form.remind_who) }}

          </table>

  

          {{ form.csrf_token }}

  

-         <p class="buttons indent">

-             <input type="submit" name="action" class="submit positive button" value="{{ _('Save') }}"/>

+         <p class="d-flex justify-content-end">

+             <input type="button" name="action" value="{{ _('Cancel') }}" class="btn btn-secondary mr-1" onclick="history.back();"/>

          {% if meeting.recursion_frequency %}

-             <input type="submit" name="action" class="submit positive button" value="{{ _('Save all') }}"/>

+             <input type="submit" name="action" class="btn btn-primary mr-1" value="{{ _('Save all') }}"/>

          {% endif %}

-             <input type="button" name="action" value="{{ _('Cancel') }}" class="button" onclick="history.back();"/>

+             <input type="submit" name="action" class="btn btn-primary" value="{{ _('Save') }}"/>

          </p>

  

      </form>
@@ -213,14 +215,14 @@ 

      });

  

      var cb = $('#full_day')

-     var tz = $('#meeting_timezone').parent().parent();

+     var tz = $('#meeting_timezone').parent();

      if(cb.prop('checked')) {

        tz.val('UTC');

        tz.hide();

      };

  

      $('#full_day').click(function() {

-       var tz = $('#meeting_timezone').parent().parent();

+       var tz = $('#meeting_timezone').parent();

        if(this.checked == false) {

          tz.show();

        } else {

@@ -1,33 +1,34 @@ 

  {% extends "master.html" %}

  

  {% block title %}{{ _('Home') }}{% endblock %}

- {%block tag %}home{% endblock %}

  

  {% macro render_calendars(calendars) -%}

-     {% for calendar_row in calendars %}

-     <div class="calendar_row">

-         {% for calendar in calendar_row %}

-             <a class="calendar_link"

-                 href="{{ url_for('calendar', calendar_name=calendar.calendar_name) }}">

-                 <img src='{{ url_for("static", filename="fedocal.png") }}' alt="" />

-                 <span class="calendar_name">{{ calendar.calendar_name }}</span>

-                 <span class="calendar_desc">{{ calendar.calendar_description }}</span>

-             </a>

-         {% endfor %}

-     </div>

+     <div class="row">

+     {% for calendar in calendars %}

+         <a class="col-sm-6 col-md-4 col-lg-3 mb-3"

+             href="{{ url_for('calendar', calendar_name=calendar.calendar_name) }}">

+             <div class="d-flex">

+               <img src='{{ url_for("static", filename="fedocal.png") }}' alt="" class="my-auto mr-2" />

+               <div>

+                 <strong>{{ calendar.calendar_name }}</strong>

+                 <div>{{ calendar.calendar_description }}</div>

+               </div>

+             </div>

+         </a>

      {% else %}

-     <p>{{ _('No calendar found') }}</p>

+     <p class="col">{{ _('No calendar found') }}</p>

      {% endfor %}

+     </div>

  {%- endmacro %}

  

  {% block content %}

  <h2>{{ _('Active Calendars') }}</h2>

- <section class="calendar_list">

-     {{ render_calendars(calendars_table) }}

+ <section>

+     {{ render_calendars(calendars_enabled) }}

  </section>

  

  <h3>{{ _('Calendars disabled') }}</h3>

- <section class="calendar_list">

-     {{ render_calendars(calendars_table2) }}

+ <section>

+     {{ render_calendars(calendars_disabled) }}

  </section>

  {% endblock %}

@@ -4,23 +4,21 @@ 

  {%block tag %}locations{% endblock %}

  

  {% macro render_locations(calendars) -%}

-     {% for location_row in locations %}

-     <div class="calendar_row">

-         {% for location in location_row %}

-             <a class="calendar_link"

-                 href="{{ url_for('location', loc_name=location) }}">

-                 <span class="calendar_name">{{ location }}</span>

-             </a>

-         {% endfor %}

-     </div>

+     <div class="row">

+     {% for location in locations %}

+         <a class="col-md-4"

+             href="{{ url_for('location', loc_name=location) }}">

+             <span>{{ location }}</span>

+         </a>

      {% else %}

-     <p>{{ _('No locations found') }}</p>

+     <p class="col">{{ _('No locations found') }}</p>

      {% endfor %}

+     </div>

  {%- endmacro %}

  

  {% block content %}

  <h2>{{ _('Locations') }}</h2>

- <section class="calendar_list">

+ <section>

      {{ render_locations(locations) }}

  </section>

  {% endblock %}

@@ -1,90 +1,132 @@ 

- {% from "_formhelpers.html" import render_field, render_field_invert,

+ {% from "_formhelpers.html" import render_field, render_field_checkbox, render_field_file,

      render_field_in_row %}

  <!DOCTYPE html>

  <html lang='en'>

  <head>

      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>

+     <meta name="viewport" content="width=device-width, initial-scale=1.0">

      <title>{% block title %}{% endblock %} - Fedocal</title>

      <link rel="shortcut icon" type="image/vnd.microsoft.icon"

          href="{{ url_for('static', filename='favicon.ico')}}"/>

      <link rel="stylesheet" type="text/css" media="screen"

-         href="{{ url_for('static', filename='koji.css') }}"/>

+         href="{{ url_for('static', filename='bootstrap.css') }}"/>

      <link rel="stylesheet" type="text/css" media="screen"

-         href="{{ url_for('static', filename='fedocal.css') }}"/>

+         href="{{ url_for('static', filename='addon.css') }}"/>

      <link type="text/css" rel="stylesheet"

          href="{{ url_for('static',

              filename='fedocal/jquery-ui-1.11.1.min.css')}}" />

+     <link rel="apple-touch-icon" sizes="180x180" href="{{ url_for('static', filename='favicon/apple-touch-icon.png') }}">

+     <link rel="icon" type="image/png" sizes="32x32" href="{{ url_for('static', filename='favicon/favicon-32x32.png') }}">

+     <link rel="icon" type="image/png" sizes="16x16" href="{{ url_for('static', filename='favicon/favicon-16x16.png') }}">

+     <link rel="manifest" href="{{ url_for('static', filename='favicon/site.webmanifest') }}">

+     <link rel="mask-icon" href="{{ url_for('static', filename='favicon/safari-pinned-tab.svg') }}" color="#51a2da">

+     <link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}">

+     <meta name="theme-color" content="#ffffff">

      {% block header %}{% endblock %}

    </head>

-   <body id="{% block tag %}{% endblock %}">

+   <body class="d-flex flex-column">

  

-     <div id="wrap">

-       <div id="innerwrap">

- 

-         <!-- HEADER -->

-         <div id="header">

+     <!-- HEADER -->

+     <nav class="navbar navbar-expand-lg {% block navbar_class %}masthead{% endblock %}">

+       <div class="{% block navbar_container %}container{% endblock %}">

+         <a class="navbar-brand" href="/">

+           {% block navbar_brand %}

            <img src="{{ url_for('static', filename='fedocal-logo.png') }}"

-             alt="Fedocal" id="kojiLogo"/>

-           <form action="{{url_for('search')}}" id="headerSearch">

-               <input type="text" name="keyword" title="{{ _("Search for meetings, you may use '*'") }}"/>

-               <input type="submit" value="{{ _('Search') }}"/>

+                alt="Fedora Calendar" id="kojiLogo" height="40" width="170"/></a>

+           {% endblock %}

+         <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#fedocal-navbar" aria-controls="fedocal-navbar" aria-expanded="false" aria-label="{{ _("Toggle navigation") }}">

+           <span class="navbar-toggler-icon"></span>

+         </button>

+         

+         <div class="collapse navbar-collapse" id="fedocal-navbar">

+           <ul class="navbar-nav mr-auto">

+             <li class="nav-item">

+               <a class="nav-link" href="{{url_for('locations')}}">{{ _('Locations') }}</a>

+             </li>

+             <li class="nav-item">

+               <a class="nav-link" href="{{url_for('api')}}">{{ _('API') }}</a>

+             </li>

+           </ul>

+           <form class="form-inline my-2 my-lg-0" action="{{url_for('search')}}" id="headerSearch">

+             <div class="input-group">

+               <input class="form-control" name="keyword" type="search" placeholder="{{ _("Search for meetings") }}" aria-label="{{ _("Search for meetings") }}" title="{{ _("To match, you may use '*'") }}">

+               <div class="input-group-append">

+                 <button class="btn btn-secondary" type="submit" title="{{ _("Search") }}">

+                   <svg class="bi bi-search" width="1em" height="1em" viewBox="0 0 16 16" fill="currentColor"

+                        xmlns="http://www.w3.org/2000/svg">

+                     <path fill-rule="evenodd"

+                           d="M10.442 10.442a1 1 0 0 1 1.415 0l3.85 3.85a1 1 0 0 1-1.414 1.415l-3.85-3.85a1 1 0 0 1 0-1.415z">

+                     </path>

+                     <path fill-rule="evenodd"

+                           d="M6.5 12a5.5 5.5 0 1 0 0-11 5.5 5.5 0 0 0 0 11zM13 6.5a6.5 6.5 0 1 1-13 0 6.5 6.5 0 0 1 13 0z"></path>

+                     </svg>

+                 </button>

+               </div>

+             </div>

            </form>

-         </div><!-- end header -->

- 

-         <!-- MAIN NAVIGATION -->

-         <div id="mainNav">

-           <h4 class="hide">{{ _('Main Site Links') }}</h4>

-           <ul>

-               <li id="homeTab"><a href="{{url_for('index')}}">{{ _('Home') }}</a></li>

-               <li id="locationsTab"><a href="{{url_for('locations')}}">{{ _('Locations') }}</a></li>

-               <li id="apiTab"><a href="{{url_for('api')}}">{{ _('API') }}</a></li>

+           <div class="navbar-nav">

              {% if g.fas_user %}

-             <li id="mymeetingTab"><a href="{{url_for('my_meetings')}}">{{ _('My meetings') }}</a></li>

-             {% endif %}

-             {% if admin %}

-             <li id="adminTab"><a href="{{url_for('admin')}}">{{ _('Admin') }}</a></li>

+             <div class="nav-item dropdown">

+               <a class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-expanded="false">

+                 {{ g.fas_user.username }}

+               </a>

+               <ul class="dropdown-menu dropdown-menu-right">

+                 <li><a class="dropdown-item" href="{{url_for('my_meetings',)}}">{{ _('My meetings') }}</a></li>

+                 {% if admin %}

+                 <li><a class="dropdown-item" href="{{url_for('admin')}}">{{ _('Admin') }}</a></li>

+                 {% endif %}

+                 <li><hr class="dropdown-divider"></li>

+                 <li><a class="dropdown-item" href="{{ url_for('auth_logout') }}?next={{request.url}}">{{ _('Log Out') }}</a></li>

+               </ul>

+             </div>

+             {% else %}

+             <a class="btn btn-primary ml-2" href="{{ url_for('auth_login') }}?next={{request.url}}">{{ _('Log In') }}</a>

              {% endif %}

-           </ul>

-         </div><!-- end mainNav -->

+           </div>

+         </div>

+       </div>

+     </nav><!-- end header -->

  

-         <span id="loginInfo">

-           {% if g.fas_user %}

-           <span class="text">{{ _('logged in as') }} </span>

-             <a href="{{url_for('my_meetings',)}}">{{ g.fas_user.username }}</a> |

-             <a href="{{ url_for('auth_logout') }}?next={{request.url}}">{{ _('log out') }}</a>

-           {% else %}

-           <a href="{{ url_for('auth_login') }}?next={{request.url}}">{{ _('login') }}</a>

-           {% endif %}

-         </span>

+     <main class="bodycontent py-2 flex-fill">

+       <div class="container">

  

-         <div id="content">

+         {% with messages = get_flashed_messages(with_categories=true) %}

+             {% if category, messages %}

+             <ul id="flashes" class="list-unstyled">

+                 {% for category, message in messages %}

+                 <li class="alert {% if category == 'errors' -%}alert-danger

+                                  {%- elif category == 'warnings' -%}alert-warning

+                                  {%- else -%}alert-info

+                                  {%- endif -%}">{{ message }}</li>

+                 {% endfor %}

+             </ul>

+             {% endif %}

+         {% endwith %}

  

-             {% with messages = get_flashed_messages(with_categories=true) %}

-                 {% if category, messages %}

-                 <ul id="flashes">

-                     {% for category, message in messages %}

-                     <li class="{{ category }}">{{ message }}</li>

-                     {% endfor %}

-                 </ul>

-                 {% endif %}

-             {% endwith %}

+         {% block content %}{% endblock %}

  

-             {% block content %}{% endblock %}

+       </div>

+     </main>

  

+     <footer id="footer" class="footer my-4 {% block footer_class %}text-white{% endblock %}">

+       <div class="container">

+         <div class="d-flex align-items-center justify-content-between">

+           <span>Copyright &copy; 2012-2022 Red Hat</span>

+           <ul class="list-inline m-0">

+             <li class="list-inline-item">

+               <a href="https://pagure.io/fedocal/">fedocal {{version}}</a>

+             </li>

+             <li class="list-inline-item">

+               <a href="http://fedocal.rtfd.org" rel="noopener noreferrer"

+                  target="_blank">{{ _('Documentation') }}</a>

+             </li>

+             <li class="list-inline-item">

+               <a href="http://fedocal.readthedocs.org/en/latest/contributors.html">{{ _('Authors') }}</a>

+             </li>

+           </ul>

          </div>

- 

-         <p id="footer">

-           Copyright &copy; 2012-2015 Red Hat

-           <a href="https://pagure.io/fedocal/">fedocal</a>

-           -- {{version}}

-           -- <a href="http://fedocal.rtfd.org" rel="noopener noreferrer"

-               target="_blank">{{ _('Documentation') }}</a>

-           -- <a href="http://fedocal.readthedocs.org/en/latest/contributors.html">{{ _('Authors') }}</a>

-           -- <a href="{{ url_for('api') }}">{{ _('API') }}</a>

-         </p>

- 

        </div>

-     </div>

+     </footer>

  

      {% block sidebar %}{% endblock %}

  
@@ -97,6 +139,10 @@ 

          src="{{ url_for('static',

              filename='jquery-ui.min-1.11.1.js') }}">

      </script>

+     <script type="text/javascript"

+         src="{{ url_for('static',

+             filename='bootstrap.js') }}">

+     </script>

      <script type="text/javascript">

          FEDOCAL = {};

          function CopyToClipboard (text) {

@@ -7,7 +7,9 @@ 

  

  {% block content %}

  <section>

- {% if calendar or location %}

+     <div class="row">

+     <div class="col-md-3">

+     {% if calendar or location %}

      <header>

          <h2>

              {% if calendar %}
@@ -23,101 +25,81 @@ 

          </h2>

          <p>{{ calendar.calendar_description or '-' }}</p>

      </header>

-     <nav id="weeks">

-       <p>

-         {% if meetings %}

-         {{ meetings[0].meeting_date.strftime('%d %b %Y') }} to

-         {{ meetings[-1].meeting_date_end.strftime('%d %b %Y')  }}

-         {% endif %}

-       </p>

-     </nav>

- {% else %}

- <header>

-     <h2>{{ _('Search') }}</h2>

-     <p>{{ _('Result of your search for "%(keyword)s"', keyword=keyword) }}</p>

- </header>

- {% endif %}

+     {% else %}

+     <header>

+         <h2>{{ _('Search') }}</h2>

+         <p>{{ _('Result of your search for "%(keyword)s"', keyword=keyword) }}</p>

+     </header>

+     {% endif %}

      <aside id="mainmenu">

          {% autoescape off%}{{ curmonth_cal }}{% endautoescape %}

          {% if calendar or location %}

-         <form action="{{url_for('goto')}}" id="goto_date">

+         <form class="form-inline mb-2" action="{{url_for('goto')}}" id="goto_date">

+           <div class="input-group">

            {% if calendar %}

            <input type="hidden" name="calendar" value="{{calendar.calendar_name}}"/>

            {% else %}

            <input type="hidden" name="location" value="{{location}}"/>

            {% endif %}

            <input type="hidden" name="type" value="list"/>

-           <input type="text" name="day" placeholder="28" title="{{ _('Day in decimal') }}"/>

-           <input type="text" name="month" placeholder="02" title="{{ _('Month in decimal') }}"/>

-           <input type="text" name="year" placeholder="2014" title="{{ _('Year in decimal') }}"/>

-           <input type="submit" value="{{ _('Go') }}"/>

+           <input type="text" class="form-control" name="day" placeholder="28" title="{{ _('Day in decimal') }}"/>

+           <input type="text" class="form-control" name="month" placeholder="02" title="{{ _('Month in decimal') }}"/>

+           <input type="text" class="form-control" name="year" placeholder="2014" title="{{ _('Year in decimal') }}"/>

+           <div class="input-group-append">

+             <input type="submit" class="btn btn-secondary" value="{{ _('Go') }}"/>

+           </div>

+           </div>

          </form>

          <p class="smaller">

              {{ _('Specify a year to see the yearly report, a month of a year to see

              the monthly report and a complete date to see that specific date.') }}

          </p>

-         <p>

-           {% if calendar %}

-             <a href="{{ url_for('calendar', calendar_name=calendar.calendar_name) }}">

-                 {{ _('Calendar view') }}

-             </a>

-           {% else %}

-             <a href="{{ url_for('location',loc_name=location) }}">

-                 {{ _('Calendar view') }}

-             </a>

-           {% endif %}

-         </p>

-         {% endif %}

-         <div id="accordion">

-         <h4>{{ _('Calendars') }}</h4>

-         <ul id="calendars_menu" class="checkmark-list">

-             {% for calendar in calendars %}

-             {% if calendar.calendar_status == 'Enabled' %}

+         <ul class="list-unstyled">

+         {% if calendar %}

+             {% if g.fas_user %}

              <li>

-                 <a href="{{url_for('calendar',

-                     calendar_name=calendar.calendar_name)}}">

-                     {{ calendar.calendar_name }}

+                 <a href="{{ url_for('add_meeting',

+                     calendar_name=calendar.calendar_name) }}">{{ _('Add a meeting') }}</a>

+             </li>

+             {% endif %}

+             <li>

+                <a href="{{ url_for('calendar_list',

+                     calendar_name=calendar.calendar_name) }}">

+                     {{ _('List view') }}

+                 </a>

+             </li>

+             <li>

+                 <a href="{{url_for('ical_out',

+                                    calendar_name=calendar.calendar_name)}}"

+                     title="{{ _('Add this calendar to your agenda') }}">{{ _('iCal export') }}</a>

+             </li>

+             <li>

+                 <a onclick="CopyToClipboard('{{url_for('ical_out',

+                                    calendar_name=calendar.calendar_name)}}');return false;"

+                     title="{{ _('Copy the iCal feed URL') }}" href="#">{{ _('Copy iCal link') }}</a>

+             </li>

+             {% if calendar.calendar_contact %}

+             <li>

+                 <a href="mailto:{{calendar.calendar_contact}}">

+                     {{ _('Contact admin') }}

                  </a>

-                 <ul>

-                     {% if g.fas_user %}

-                     <li>

-                         <a href="{{ url_for('add_meeting',

-                             calendar_name=calendar.calendar_name) }}">{{ _('Add a meeting') }}</a>

-                     </li>

-                     {% endif %}

-                     <li>

-                         <a href="{{ url_for('calendar_list',

-                             calendar_name=calendar.calendar_name) }}">

-                             {{ _('List view') }}

-                         </a>

-                     </li>

-                     <li>

-                         <a href="{{url_for('ical_out',

-                                            calendar_name=calendar.calendar_name)}}"

-                             title="{{ _('Add this calendar to your agenda') }}">{{ _('iCal export') }}</a>

-                     </li>

-                     <li>

-                         <a onclick="CopyToClipboard('{{url_for('ical_out',

-                                            calendar_name=calendar.calendar_name)}}');return false;"

-                             title="{{ _('Copy the iCal feed URL') }}" href="#">{{ _('Copy iCal link') }}</a>

-                     </li>

-                     {% if calendar.calendar_contact %}

-                     <li>

-                         <a href="mailto:{{calendar.calendar_contact}}">

-                             {{ _('Contact admin') }}

-                         </a>

-                     </li>

-                     {% endif %}

-                 </ul>

              </li>

              {% endif %}

-             {% endfor %}

+         {% else %}

+             <li>

+                 <a href="{{ url_for('location',loc_name=location) }}">

+                     {{ _('Calendar view') }}

+                 </a>

+             </li>

+         {% endif %}

          </ul>

+         {% endif %}

+         <div>

  

          {% if calendar and (admin or calendar_admin)  %}

          <h4>{{ _('Admin') }}</h4>

          <div>

-         <ul class="checkmark-list">

+         <ul class="list-unstyled">

              {% if admin %}

              <li>

                  <a href="{{ url_for('edit_calendar',
@@ -161,12 +143,24 @@ 

      </aside>

  

      </aside>

+     </div>

+ 

+     <div class="col-md-9" id='meeting_list'>

+     {% if calendar or location %}

+     <nav id="weeks" class="text-center">

+       <p>

+         {% if meetings %}

+         {{ meetings[0].meeting_date.strftime('%d %b %Y') }} to

+         {{ meetings[-1].meeting_date_end.strftime('%d %b %Y')  }}

+         {% endif %}

+       </p>

+     </nav>

+     {% endif %}

  

- <div id='meeting_list'>

      {% if calendar %}

      <p>{{ _('Time are expressed in the %(timezone)s time zone.', timezone=tzone) }}</p>

      {% endif %}

-     <table>

+     <table class="table table-sm">

          <tr>

              <td>{{ _('Date') }}</td>

              <td>{{ _('Time') }}</td>
@@ -226,33 +220,28 @@ 

          </tr>

          {% endfor %}

      </table>

- </div>

+     </div>

+     </div>

  </section>

  {% endblock %}

  

  {% block sidebar %}

  {% if calendar %}

    <aside id="shortmenu">

-     <ul>

-       <li>

-         <a href="{{ url_for('add_meeting',

+     <div class="btn-group">

+         <a class="btn btn-light" href="{{ url_for('add_meeting',

                              calendar_name=calendar.calendar_name) }}">

              <img src="{{ url_for('static', filename='plus.png') }}" title="{{ _('Add meeting') }}" alt="{{ _('Add meeting') }}" />

          </a>

-       </li>

-       <li>

-         <a href="{{ url_for('calendar',

+         <a class="btn btn-light" href="{{ url_for('calendar',

                              calendar_name=calendar.calendar_name) }}">

              <img src="{{ url_for('static', filename='table.png') }}" title="{{ _('Calendar view') }}" alt="{{ _('Calendar view') }}" />

          </a>

-       </li>

-       <li>

-         <a href="{{url_for('ical_out',

+         <a class="btn btn-light" href="{{url_for('ical_out',

                             calendar_name=calendar.calendar_name)}}">

              <img src="{{ url_for('static', filename='ical.png') }}" title="{{ _('iCal Export') }}" alt="{{ _('iCal Export') }}" />

          </a>

-       </li>

-     </ul>

+     </div>

    </aside>

  {% endif %}

  {% endblock %}

@@ -1,5 +1,4 @@ 

  {% extends "master.html" %}

- {% from "_formhelpers.html" import render_field %}

  

  {% block title %}{{ _('My meetings') }}{% endblock %}

  
@@ -49,16 +48,18 @@ 

      {% if regular_meetings %}

      <p>{{ _('This is the list of all regular meeting you created') }}</p>

      <div class='my_meetings'>

-         <table>

+         <div class="list-group mb-2">

              {% for meeting in regular_meetings %}

-             <tr>

-                 <td class="date">{{ meeting.meeting_date }}</td>

-                 <td class="time">

+             <div class="list-group-item d-flex justify-content-between align-content-center">

+                 <div>

+                 <span class="text-muted">{{ meeting.meeting_date }}</span>

+                 <span class="text-muted">

                      {{ _('%(start)s to %(end)s', start=meeting.meeting_time_start, end=meeting.meeting_time_stop) }}

-                 </td>

-                 <td class="calendar"> {{ meeting.calendar_name }} </td>

-                 <td> {{ meeting.meeting_name }} </td>

-                 <td class="buttons">

+                 </span>

+                 <span class="calendar"> {{ meeting.calendar_name }} </span>

+                 <span> {{ meeting.meeting_name }} </span>

+                 </div>

+                 <div>

                      <a class="button" href="{{ url_for('edit_meeting',

                          meeting_id=meeting.meeting_id) }}">

                          <img src="{{ url_for('static', filename='edit.png') }}"
@@ -69,27 +70,27 @@ 

                          <img src="{{ url_for('static', filename='delete.png') }}"

                          title="{{ _('Delete') }}" alt="{{ _('Delete') }}"/>

                      </a>

-                 </td>

-             </tr>

+                 </div>

+             </div>

              {% endfor %}

-         </table>

+         </div>

      </div>

      {% endif %}

  

      <h4>{{ _('Past meetings') }}</h4>

      <div class='my_meetings'>

-         <table>

+         <div class="list-group mb-2">

              {% for meeting in pas_meetings %}

-             <tr>

-                 <td class="date">{{ meeting.meeting_date }}</td>

-                 <td class="time">

+             <div class="list-group-item">

+                 <span class="text-muted">{{ meeting.meeting_date }}</span>

+                 <span class="text-muted">

                      {{ _('%(start)s to %(end)s', start=meeting.meeting_time_start, end=meeting.meeting_time_stop) }}

-                 </td>

-                 <td class="calendar"> {{ meeting.calendar_name }} </td>

-                 <td> {{ meeting.meeting_name }} </td>

-             </tr>

+                 </span>

+                 <span class="calendar"> {{ meeting.calendar_name }} </span>

+                 <span> {{ meeting.meeting_name }} </span>

+             </div>

              {% endfor %}

-         </table>

+         </div>

      </div>

  </section>

  {% endblock %}

@@ -1,5 +1,4 @@ 

  {% extends "master.html" %}

- {% from "_formhelpers.html" import render_field_in_row %}

  

  {% block title %}{{ _('Upload calendar') }}{% endblock %}

  
@@ -19,12 +18,10 @@ 

      <p>{{ _('Note2: Todo are considered as full day meeting and displayed as such.') }}</p>

  

      <form action="" method="post" enctype="multipart/form-data">

-         <table>

-         {{ render_field_in_row(form.ics_file) }}

-         </table>

-         <p class="buttons indent">

-             <input type="submit" class="submit positive button" value="{{ _('Upload') }}">

-             <input type="button" value="{{ _('Cancel') }}" class="button" onclick="history.back();">

+         {{ render_field_file(form.ics_file) }}

+         <p class="d-flex justify-content-end">

+             <input type="button" value="{{ _('Cancel') }}" class="btn btn-secondary mr-1" onclick="history.back();">

+             <input type="submit" class="btn btn-primary" value="{{ _('Upload') }}">

              {{ form.csrf_token }}

          </p>

      </form>

@@ -11,8 +11,8 @@ 

  

  {% block content %}

  <section>

-         <h2 class="orange">{{ _('Meeting "%(name)s"', name=org_meeting.meeting_name) }}</h2>

-         <p class="blue">(<a href="{{ url_for('calendar', calendar_name=org_meeting.calendar_name) }}">

+         <h2>{{ _('Meeting "%(name)s"', name=org_meeting.meeting_name) }}</h2>

+         <p>(<a href="{{ url_for('calendar', calendar_name=org_meeting.calendar_name) }}">

              {{ org_meeting.calendar_name }}</a>)

          </p>

  

file modified
+1
@@ -24,3 +24,4 @@ 

  fedora-messaging

  email_validator

  fedocal-messages>=1.5.0

+ itsdangerous<2.1.0

file modified
+3 -3
@@ -782,16 +782,16 @@ 

              today.day, today.month, today.year)

          # Check today css class

          self.assertTrue(

-             'class="%s today">%s' % (

+             'class="%s primary text-right">%s' % (

                  today.strftime('%a').lower(),

                  today.day) in output)

          # Check the month

          self.assertTrue(

-             'class="month"> %s </th>' % (

+             '><div class="d-flex align-items-center justify-content-between text-center"> <span class="mx-auto">%s</span> </div></th>' % (

                  today.strftime('%B %Y')) in output)

          # Check the current_week css class

          self.assertNotEqual(

-             re.match('<tr class="current_week">.*'

+             re.match('<tr class="bg-light">.*'

                       '<td>%s</td>' % (today.day), output), [])

  

      def test_get_week_day_index(self):

file modified
+64 -60
@@ -50,10 +50,10 @@ 

          cal = FedocalCalendar(2013, 1, 6)

          self.assertEqual(

              cal.formatday(6, 1),

-             '<td class="tue">6</td>')

+             '<td class="tue text-right">6</td>')

          self.assertEqual(

              cal.formatday(7, 3),

-             '<td class="thu">7</td>')

+             '<td class="thu text-right">7</td>')

  

      def test_formatweek(self):

          """ Test the formatweek function. """
@@ -62,16 +62,16 @@ 

          output = cal.formatweek([(1, 1), (2, 2)]).replace(' today', '')

          self.assertEqual(

              output,

-             '<tr><td class="tue">1</td><td class="wed">2</td></tr>')

+             '<tr><td class="tue text-right">1</td><td class="wed text-right">2</td></tr>')

          self.assertEqual(

              cal.formatweek([(0, 1), (1, 2)]).replace(' today', ''),

-             '<tr><td class="noday">&nbsp;</td><td class="wed">1</td></tr>')

+             '<tr><td>&nbsp;</td><td class="wed text-right">1</td></tr>')

          output = cal.formatweek(

              [(1, 1), (2, 2)], True).replace(' today', '')

          self.assertEqual(

              output,

-             '<tr class="current_week"><td class="tue">1</td><td '

-             'class="wed">2</td></tr>')

+             '<tr class="bg-light"><td class="tue text-right">1</td><td '

+             'class="wed text-right">2</td></tr>')

  

      def test_formatmonthname(self):

          """ Test the formatmonthname function. """
@@ -79,42 +79,44 @@ 

          cal = FedocalCalendar(today.year, today.month, today.day)

          self.assertEqual(

              cal.formatmonthname(2012, 4),

-             '<tr><th colspan="7" class="month"> April 2012 </th></tr>')

+             '<tr><th colspan="7"><div class="d-flex align-items-center justify-content-between text-center"> <span class="mx-auto">April 2012</span> </div></th></tr>')

          self.assertEqual(

              cal.formatmonthname(2012, 4, False),

-             '<tr><th colspan="7" class="month"> April </th></tr>')

+             '<tr><th colspan="7"><div class="d-flex align-items-center justify-content-between text-center"> <span class="mx-auto">April</span> </div></th></tr>')

  

      def test_formatmonth(self):

          """ Test the formatmonth function. """

          cal = FedocalCalendar(2012, 1, 10)

  

          expcal = [

-             '<table class="month">',

-             '<tr><th colspan="7" class="month"> January 2012 </th></tr>',

-             '<tr><td class="noday">&nbsp;</td><td class="noday">&nbsp;</td>'

-             '<td class="noday">&nbsp;</td><td class="noday">&nbsp;</td>'

-             '<td class="noday">&nbsp;</td><td class="noday">&nbsp;</td>'

-             '<td class="sun">1</td></tr>',

-             '<tr><td class="mon">2</td><td class="tue">3</td>'

-             '<td class="wed">4</td><td class="thu">5</td>'

-             '<td class="fri">6</td><td class="sat">7</td>'

-             '<td class="sun">8</td></tr>',

-             '<tr class="current_week"><td class="mon">9</td>'

-             '<td class="tue">10</td><td class="wed">11</td>'

-             '<td class="thu">12</td><td class="fri">13</td>'

-             '<td class="sat">14</td><td class="sun">15</td></tr>',

-             '<tr><td class="mon">16</td><td class="tue">17</td>'

-             '<td class="wed">18</td><td class="thu">19</td>'

-             '<td class="fri">20</td><td class="sat">21</td>'

-             '<td class="sun">22</td></tr>',

-             '<tr><td class="mon">23</td><td class="tue">24</td>'

-             '<td class="wed">25</td><td class="thu">26</td>'

-             '<td class="fri">27</td><td class="sat">28</td>'

-             '<td class="sun">29</td></tr>',

-             '<tr><td class="mon">30</td><td class="tue">31</td>'

-             '<td class="noday">&nbsp;</td><td class="noday">&nbsp;</td>'

-             '<td class="noday">&nbsp;</td><td class="noday">&nbsp;</td>'

-             '<td class="noday">&nbsp;</td></tr>',

+             '<table class="table table-sm month">',

+             '<tr><th colspan="7"><div class="d-flex align-items-center '

+             'justify-content-between text-center"> <span class="mx-auto">'

+             'January 2012</span> </div></th></tr>',

+             '<tr><td>&nbsp;</td><td>&nbsp;</td>'

+             '<td>&nbsp;</td><td>&nbsp;</td>'

+             '<td>&nbsp;</td><td>&nbsp;</td>'

+             '<td class="sun text-right">1</td></tr>',

+             '<tr><td class="mon text-right">2</td><td class="tue text-right">3</td>'

+             '<td class="wed text-right">4</td><td class="thu text-right">5</td>'

+             '<td class="fri text-right">6</td><td class="sat text-right">7</td>'

+             '<td class="sun text-right">8</td></tr>',

+             '<tr class="bg-light"><td class="mon text-right">9</td>'

+             '<td class="tue text-right">10</td><td class="wed text-right">11</td>'

+             '<td class="thu text-right">12</td><td class="fri text-right">13</td>'

+             '<td class="sat text-right">14</td><td class="sun text-right">15</td></tr>',

+             '<tr><td class="mon text-right">16</td><td class="tue text-right">17</td>'

+             '<td class="wed text-right">18</td><td class="thu text-right">19</td>'

+             '<td class="fri text-right">20</td><td class="sat text-right">21</td>'

+             '<td class="sun text-right">22</td></tr>',

+             '<tr><td class="mon text-right">23</td><td class="tue text-right">24</td>'

+             '<td class="wed text-right">25</td><td class="thu text-right">26</td>'

+             '<td class="fri text-right">27</td><td class="sat text-right">28</td>'

+             '<td class="sun text-right">29</td></tr>',

+             '<tr><td class="mon text-right">30</td><td class="tue text-right">31</td>'

+             '<td>&nbsp;</td><td>&nbsp;</td>'

+             '<td>&nbsp;</td><td>&nbsp;</td>'

+             '<td>&nbsp;</td></tr>',

              '</table>', '']

          callines = cal.formatmonth().split('\n')

  
@@ -123,32 +125,34 @@ 

              self.assertEqual(expcal[cnt], callines[cnt])

  

          expcal = [

-             '<table class="month">',

-             '<tr><th colspan="7" class="month"> January </th></tr>',

-             '<tr><td class="noday">&nbsp;</td><td class="noday">&nbsp;</td>'

-             '<td class="noday">&nbsp;</td><td class="noday">&nbsp;</td>'

-             '<td class="noday">&nbsp;</td><td class="noday">&nbsp;</td>'

-             '<td class="sun">1</td></tr>',

-             '<tr><td class="mon">2</td><td class="tue">3</td>'

-             '<td class="wed">4</td><td class="thu">5</td>'

-             '<td class="fri">6</td><td class="sat">7</td>'

-             '<td class="sun">8</td></tr>',

-             '<tr class="current_week"><td class="mon">9</td>'

-             '<td class="tue">10</td><td class="wed">11</td>'

-             '<td class="thu">12</td><td class="fri">13</td>'

-             '<td class="sat">14</td><td class="sun">15</td></tr>',

-             '<tr><td class="mon">16</td><td class="tue">17</td>'

-             '<td class="wed">18</td><td class="thu">19</td>'

-             '<td class="fri">20</td><td class="sat">21</td>'

-             '<td class="sun">22</td></tr>',

-             '<tr><td class="mon">23</td><td class="tue">24</td>'

-             '<td class="wed">25</td><td class="thu">26</td>'

-             '<td class="fri">27</td><td class="sat">28</td>'

-             '<td class="sun">29</td></tr>',

-             '<tr><td class="mon">30</td><td class="tue">31</td>'

-             '<td class="noday">&nbsp;</td><td class="noday">&nbsp;</td>'

-             '<td class="noday">&nbsp;</td><td class="noday">&nbsp;</td>'

-             '<td class="noday">&nbsp;</td></tr>',

+             '<table class="table table-sm month">',

+             '<tr><th colspan="7"><div class="d-flex align-items-center '

+             'justify-content-between text-center"> <span class="mx-auto">'

+             'January</span> </div></th></tr>',

+             '<tr><td>&nbsp;</td><td>&nbsp;</td>'

+             '<td>&nbsp;</td><td>&nbsp;</td>'

+             '<td>&nbsp;</td><td>&nbsp;</td>'

+             '<td class="sun text-right">1</td></tr>',

+             '<tr><td class="mon text-right">2</td><td class="tue text-right">3</td>'

+             '<td class="wed text-right">4</td><td class="thu text-right">5</td>'

+             '<td class="fri text-right">6</td><td class="sat text-right">7</td>'

+             '<td class="sun text-right">8</td></tr>',

+             '<tr class="bg-light"><td class="mon text-right">9</td>'

+             '<td class="tue text-right">10</td><td class="wed text-right">11</td>'

+             '<td class="thu text-right">12</td><td class="fri text-right">13</td>'

+             '<td class="sat text-right">14</td><td class="sun text-right">15</td></tr>',

+             '<tr><td class="mon text-right">16</td><td class="tue text-right">17</td>'

+             '<td class="wed text-right">18</td><td class="thu text-right">19</td>'

+             '<td class="fri text-right">20</td><td class="sat text-right">21</td>'

+             '<td class="sun text-right">22</td></tr>',

+             '<tr><td class="mon text-right">23</td><td class="tue text-right">24</td>'

+             '<td class="wed text-right">25</td><td class="thu text-right">26</td>'

+             '<td class="fri text-right">27</td><td class="sat text-right">28</td>'

+             '<td class="sun text-right">29</td></tr>',

+             '<tr><td class="mon text-right">30</td><td class="tue text-right">31</td>'

+             '<td>&nbsp;</td><td>&nbsp;</td>'

+             '<td>&nbsp;</td><td>&nbsp;</td>'

+             '<td>&nbsp;</td></tr>',

              '</table>', '']

          callines = cal.formatmonth(withyear=False).split('\n')

  

file modified
+93 -135
@@ -144,24 +144,18 @@ 

          output_text = output.get_data(as_text=True)

          self.assertIn(

              '<title>test_calendar - Fedocal</title>', output_text)

-         self.assertIn(' <a href="/test_calendar/">', output_text)

-         self.assertIn(' <a href="/test_calendar2/">', output_text)

-         self.assertIn(' <a href="/test_calendar4/">', output_text)

  

          output = self.app.get('/test_calendar2/')

          self.assertEqual(output.status_code, 200)

          output_text = output.get_data(as_text=True)

          self.assertIn(

              '<title>test_calendar2 - Fedocal</title>', output_text)

-         self.assertIn(' <a href="/test_calendar/">', output_text)

-         self.assertIn(' <a href="/test_calendar2/">', output_text)

-         self.assertIn(' <a href="/test_calendar4/">', output_text)

  

          output = self.app.get('/foorbar/', follow_redirects=True)

          self.assertEqual(output.status_code, 200)

          output_text = output.get_data(as_text=True)

          self.assertIn(

-             'class="errors">No calendar named foorbar could be found</',

+             'class="alert alert-danger">No calendar named foorbar could be found</',

              output_text)

  

          output = self.app.get('/test_calendar2/?tzone=Europe/Paris',
@@ -183,9 +177,6 @@ 

          output_text = output.get_data(as_text=True)

          self.assertIn(

              '<title>test - Fedocal</title>', output_text)

-         self.assertIn(' <a href="/test_calendar/">', output_text)

-         self.assertIn(' <a href="/test_calendar2/">', output_text)

-         self.assertIn(' <a href="/test_calendar4/">', output_text)

  

      def test_calendar_fullday(self):

          """ Test the calendar_fullday function. """
@@ -199,9 +190,6 @@ 

          output_text = output.get_data(as_text=True)

          self.assertIn(

              '<title>test_calendar - Fedocal</title>', output_text)

-         self.assertIn(' <a href="/test_calendar/">', output_text)

-         self.assertIn(' <a href="/test_calendar2/">', output_text)

-         self.assertIn(' <a href="/test_calendar4/">', output_text)

  

          output = self.app.get(

              '/test_calendar/%s/%s/%s' % (
@@ -214,9 +202,6 @@ 

          output_text = output.get_data(as_text=True)

          self.assertIn(

              '<title>test_calendar - Fedocal</title>', output_text)

-         self.assertIn(' <a href="/test_calendar/">', output_text)

-         self.assertIn(' <a href="/test_calendar2/">', output_text)

-         self.assertIn(' <a href="/test_calendar4/">', output_text)

  

      def test_calendar_list(self):

          """ Test the calendar_list function. """
@@ -227,15 +212,12 @@ 

          output_text = output.get_data(as_text=True)

          self.assertIn(

              '<title>test_calendar - Fedocal</title>', output_text)

-         self.assertIn(' <a href="/test_calendar/">', output_text)

-         self.assertIn(' <a href="/test_calendar2/">', output_text)

-         self.assertIn(' <a href="/test_calendar4/">', output_text)

  

          output = self.app.get('/list/foorbar/', follow_redirects=True)

          self.assertEqual(output.status_code, 200)

          output_text = output.get_data(as_text=True)

          self.assertIn(

-             'class="errors">No calendar named foorbar could be found</',

+             'class="alert alert-danger">No calendar named foorbar could be found</',

              output_text)

  

          today = date.today()
@@ -245,9 +227,6 @@ 

          output_text = output.get_data(as_text=True)

          self.assertIn(

              '<title>test_calendar - Fedocal</title>', output_text)

-         self.assertIn(' <a href="/test_calendar/">', output_text)

-         self.assertIn(' <a href="/test_calendar2/">', output_text)

-         self.assertIn(' <a href="/test_calendar4/">', output_text)

  

          output = self.app.get('/list/test_calendar/%s/%s/%s' % (

              today.year, today.month, today.day))
@@ -259,9 +238,6 @@ 

          output_text = output.get_data(as_text=True)

          self.assertIn(

              '<title>test_calendar - Fedocal</title>', output_text)

-         self.assertIn(' <a href="/test_calendar/">', output_text)

-         self.assertIn(' <a href="/test_calendar2/">', output_text)

-         self.assertIn(' <a href="/test_calendar4/">', output_text)

  

          output = self.app.get('/list/test_calendar/%s/%s/' % (

              today.year, today.month), follow_redirects=True)
@@ -272,9 +248,6 @@ 

          self.assertTrue(output_text.count('<tr') > 10)

          self.assertIn(

              '<title>test_calendar - Fedocal</title>', output_text)

-         self.assertIn(' <a href="/test_calendar/">', output_text)

-         self.assertIn(' <a href="/test_calendar2/">', output_text)

-         self.assertIn(' <a href="/test_calendar4/">', output_text)

  

          output = self.app.get('/list/test_calendar/%s/%s/?subject=Another'

              % (today.year, today.month), follow_redirects=True)
@@ -285,9 +258,6 @@ 

          self.assertTrue(output_text.count('<tr') > 10)

          self.assertIn(

              '<title>test_calendar - Fedocal</title>', output_text)

-         self.assertIn(' <a href="/test_calendar/">', output_text)

-         self.assertIn(' <a href="/test_calendar2/">', output_text)

-         self.assertIn(' <a href="/test_calendar4/">', output_text)

  

          output = self.app.get('/list/test_calendar/%s/%s/?subject=Another past'

              % (today.year, today.month), follow_redirects=True)
@@ -298,9 +268,6 @@ 

          self.assertTrue(output_text.count('<tr') >= 10)

          self.assertIn(

              '<title>test_calendar - Fedocal</title>', output_text)

-         self.assertIn(' <a href="/test_calendar/">', output_text)

-         self.assertIn(' <a href="/test_calendar2/">', output_text)

-         self.assertIn(' <a href="/test_calendar4/">', output_text)

  

          output = self.app.get('/list/test_calendar/%s/%s/?delta=10'

              % (today.year, today.month), follow_redirects=True)
@@ -380,9 +347,6 @@ 

          output_text = output.get_data(as_text=True)

          self.assertIn(

              '<title> EMEA  - Fedocal</title>', output_text)

-         self.assertIn(' <a href="/test_calendar/">', output_text)

-         self.assertIn(' <a href="/test_calendar2/">', output_text)

-         self.assertIn(' <a href="/test_calendar4/">', output_text)

          self.assertEqual(output_text.count('<a class="event event_blue'), 2)

          self.assertEqual(output_text.count('<a class="event'), 4)

  
@@ -390,7 +354,7 @@ 

          self.assertEqual(output.status_code, 200)

          output_text = output.get_data(as_text=True)

          self.assertIn(

-             'class="errors">No location named foorbar could be found</',

+             'class="alert alert-danger">No location named foorbar could be found</',

              output_text)

  

          today = date.today()
@@ -400,9 +364,6 @@ 

          output_text = output.get_data(as_text=True)

          self.assertIn(

              '<title> EMEA  - Fedocal</title>', output_text)

-         self.assertIn(' <a href="/test_calendar/">', output_text)

-         self.assertIn(' <a href="/test_calendar2/">', output_text)

-         self.assertIn(' <a href="/test_calendar4/">', output_text)

          self.assertIn(

              output_text.count('<a class="event event_blue'), [0, 1])

          self.assertIn(output_text.count('<a class="event'), range(3))
@@ -413,9 +374,6 @@ 

          output_text = output.get_data(as_text=True)

          self.assertIn(

              '<title> EMEA  - Fedocal</title>', output_text)

-         self.assertIn(' <a href="/test_calendar/">', output_text)

-         self.assertIn(' <a href="/test_calendar2/">', output_text)

-         self.assertIn(' <a href="/test_calendar4/">', output_text)

          self.assertIn(

              output_text.count('<a class="event event_blue'), [1, 2])

          self.assertIn(output_text.count('<a class="event'), [2, 4])
@@ -530,7 +488,7 @@ 

              '<title>Meeting "test-meeting-st-1" - Fedocal</title>',

              output_text)

          self.assertIn(

-             '<h2 class="orange">Meeting "test-meeting-st-1"</h2>',

+             '<h2>Meeting "test-meeting-st-1"</h2>',

              output_text)

          self.assertIn(

              'This is a test meeting at the same time',
@@ -551,7 +509,7 @@ 

              '<title>Meeting "test-meeting-st-1" - Fedocal</title>',

              output_text)

          self.assertIn(

-             '<h2 class="orange">Meeting "test-meeting-st-1"</h2>',

+             '<h2>Meeting "test-meeting-st-1"</h2>',

              output_text)

          self.assertIn(

              'This is a test meeting at the same time',
@@ -567,7 +525,7 @@ 

              '<title>Meeting "test-meeting-st-1" - Fedocal</title>',

              output_text)

          self.assertIn(

-             '<h2 class="orange">Meeting "test-meeting-st-1"</h2>',

+             '<h2>Meeting "test-meeting-st-1"</h2>',

              output_text)

          self.assertIn(

              'This is a test meeting at the same time',
@@ -584,7 +542,7 @@ 

              '<title>Meeting "test-meeting-st-1" - Fedocal</title>',

              output_text)

          self.assertIn(

-             '<h2 class="orange">Meeting "test-meeting-st-1"</h2>',

+             '<h2>Meeting "test-meeting-st-1"</h2>',

              output_text)

          self.assertIn(

              'This is a test meeting at the same time',
@@ -602,7 +560,7 @@ 

              '<title>Meeting "test-meeting-st-1" - Fedocal</title>',

              output_text)

          self.assertIn(

-             '<h2 class="orange">Meeting "test-meeting-st-1"</h2>',

+             '<h2>Meeting "test-meeting-st-1"</h2>',

              output_text)

          self.assertIn(

              'This is a test meeting at the same time',
@@ -615,7 +573,7 @@ 

          self.assertEqual(output.status_code, 200)

          output_text = output.get_data(as_text=True)

          self.assertIn(

-             'class="errors">No meeting could be found for this identifier</',

+             'class="alert alert-danger">No meeting could be found for this identifier</',

              output_text)

  

      def test_is_admin(self):
@@ -863,10 +821,10 @@ 

          self.assertIn('<h2>Locations</h2>', output_text)

          self.assertIn('href="/location/EMEA/">', output_text)

          self.assertIn(

-             '<span class="calendar_name">EMEA</span>', output_text)

+             '<span>EMEA</span>', output_text)

          self.assertIn('href="/location/NA/">', output_text)

          self.assertIn(

-             '<span class="calendar_name">NA</span>', output_text)

+             '<span>NA</span>', output_text)

  

      def test_location(self):

          """ Test the location function. """
@@ -903,7 +861,7 @@ 

          self.assertEqual(output.status_code, 200)

          output_text = output.get_data(as_text=True)

          self.assertIn(

-             'class="errors">No location named foobar could be found</',

+             'class="alert alert-danger">No location named foobar could be found</',

              output_text)

  

      def test_admin(self):
@@ -921,7 +879,7 @@ 

              self.assertEqual(output.status_code, 200)

              output_text = output.get_data(as_text=True)

              self.assertIn(

-                 '"errors">You are not a fedocal admin, you are not allowed '

+                 '"alert alert-danger">You are not a fedocal admin, you are not allowed '

                  'to access the admin part.</', output_text)

  

          user = FakeUser(fedocal.APP.config['ADMIN_GROUP'])
@@ -944,7 +902,7 @@ 

              self.assertIn(

                  '<title>Home - Fedocal</title>', output_text)

              self.assertIn(

-                 '<li class="errors">No calendar named test_calendar could '

+                 '<li class="alert alert-danger">No calendar named test_calendar could '

                  'be found</li>', output_text)

  

              self.__setup_db()
@@ -959,7 +917,7 @@ 

              self.assertIn(

                  '<h2>Edit calendar "test_calendar"</h2>', output_text)

              self.assertIn(

-                 'type="text" value="test_calendar"></td>', output_text)

+                 'type="text" value="test_calendar">', output_text)

  

              output = self.app.get(

                  '/admin/?calendar=test_calendar&action=delete',
@@ -986,7 +944,7 @@ 

              self.assertEqual(output.status_code, 200)

              output_text = output.get_data(as_text=True)

              self.assertIn(

-                 '"errors">You are not a fedocal admin, you are not allowed '

+                 '"alert alert-danger">You are not a fedocal admin, you are not allowed '

                  'to add calendars.</', output_text)

  

          user = FakeUser(fedocal.APP.config['ADMIN_GROUP'])
@@ -1001,7 +959,7 @@ 

              self.assertIn(

                  'contact">Contact email', output_text)

              self.assertEqual(

-                 output_text.count('<span class="required">*</span>'), 3)

+                 output_text.count('<span class="text-danger">*</span>'), 3)

  

              csrf_token = output_text.split(

                  'name="csrf_token" type="hidden" value="')[1].split('">')[0]
@@ -1018,7 +976,7 @@ 

              self.assertEqual(output.status_code, 200)

              output_text = output.get_data(as_text=True)

              self.assertIn(

-                 '<td>This field is required.</td>', output_text)

+                 '<small>This field is required.</small>', output_text)

  

              # Works

              data = {
@@ -1047,7 +1005,7 @@ 

                  self.assertEqual(output.status_code, 200)

                  output_text = output.get_data(as_text=True)

                  self.assertIn(

-                     '<li class="message">Calendar added</li>', output_text)

+                     '<li class="alert alert-info">Calendar added</li>', output_text)

  

              # This calendar already exists

              data = {
@@ -1062,7 +1020,7 @@ 

              self.assertEqual(output.status_code, 200)

              output_text = output.get_data(as_text=True)

              self.assertIn(

-                 '="errors">Could not add this calendar to the database</',

+                 '="alert alert-danger">Could not add this calendar to the database</',

                  output_text)

  

      def test_delete_calendar(self):
@@ -1075,7 +1033,7 @@ 

              self.assertEqual(output.status_code, 200)

              output_text = output.get_data(as_text=True)

              self.assertTrue(

-                 '<li class="errors">You are not a fedocal admin, you are not'

+                 '<li class="alert alert-danger">You are not a fedocal admin, you are not'

                  ' allowed to delete the calendar.</l',

                  output_text)

              self.assertIn('<title>Home - Fedocal</title>', output_text)
@@ -1087,7 +1045,7 @@ 

              self.assertEqual(output.status_code, 200)

              output_text = output.get_data(as_text=True)

              self.assertIn(

-                 '"errors">No calendar named 50 could be found</',

+                 '"alert alert-danger">No calendar named 50 could be found</',

                  output_text)

              self.assertIn('<title>Home - Fedocal</title>', output_text)

  
@@ -1100,7 +1058,7 @@ 

                  "Are you positively sure that's what you want to do?",

                  output_text)

              self.assertIn(

-                 'name="confirm_delete" type="checkbox" value="y"><label',

+                 "name=\"confirm_delete\" type=\"checkbox\" value=\"y\">\n    <label",

                  output_text)

  

              # No data
@@ -1116,7 +1074,7 @@ 

                  "Are you positively sure that's what you want to do?",

                  output_text)

              self.assertIn(

-                 'name="confirm_delete" type="checkbox" value="y"><label',

+                 "name=\"confirm_delete\" type=\"checkbox\" value=\"y\">\n    <label",

                  output_text)

  

              csrf_token = output_text.split(
@@ -1133,7 +1091,7 @@ 

              output_text = output.get_data(as_text=True)

              self.assertIn('<title>Home - Fedocal</title>', output_text)

              self.assertIn(

-                 '<span class="calendar_name">test_calendar</span>',

+                 '<strong>test_calendar</strong',

                  output_text)

  

              # Delete
@@ -1163,10 +1121,10 @@ 

                  self.assertIn(

                      '<title>Home - Fedocal</title>', output_text)

                  self.assertIn(

-                     '<li class="message">Calendar deleted</li>',

+                     '<li class="alert alert-info">Calendar deleted</li>',

                      output_text)

                  self.assertNotIn(

-                     '<span class="calendar_name">test_calendar</span>',

+                     '<strong>test_calendar</strong>',

                      output_text)

  

      def test_clear_calendar(self):
@@ -1180,7 +1138,7 @@ 

              self.assertEqual(output.status_code, 200)

              output_text = output.get_data(as_text=True)

              self.assertIn(

-                 '<li class="errors">No calendar named 1 could be found</li',

+                 '<li class="alert alert-danger">No calendar named 1 could be found</li',

                  output_text)

  

              output = self.app.get('/calendar/clear/test_calendar/',
@@ -1188,7 +1146,7 @@ 

              self.assertEqual(output.status_code, 200)

              output_text = output.get_data(as_text=True)

              self.assertIn(

-                 '<li class="errors">You are not an admin of this calendar, '

+                 '<li class="alert alert-danger">You are not an admin of this calendar, '

                  'you are not allowed to clear the calendar.</l',

                  output_text)

              self.assertIn(
@@ -1205,7 +1163,7 @@ 

                  "Are you positively sure that's what you want to do?",

                  output_text)

              self.assertIn(

-                 'name="confirm_delete" type="checkbox" value="y"><label',

+                 "name=\"confirm_delete\" type=\"checkbox\" value=\"y\">\n    <label",

                  output_text)

              self.assertIn(

                  '>Yes I want to clear this calendar</label>', output_text)
@@ -1223,7 +1181,7 @@ 

                  "Are you positively sure that's what you want to do?",

                  output_text)

              self.assertIn(

-                 'name="confirm_delete" type="checkbox" value="y"><label',

+                 "name=\"confirm_delete\" type=\"checkbox\" value=\"y\">\n    <label",

                  output_text)

  

              csrf_token = output_text.split(
@@ -1241,7 +1199,7 @@ 

              self.assertIn(

                  '<title>test_calendar - Fedocal</title>', output_text)

              self.assertNotIn(

-                 '<li class="message">Calendar cleared</li>', output_text)

+                 '<li class="alert alert-info">Calendar cleared</li>', output_text)

  

              # Delete

              data = {
@@ -1272,7 +1230,7 @@ 

                  self.assertIn(

                      '<title>test_calendar - Fedocal</title>', output_text)

                  self.assertIn(

-                     '<li class="message">Calendar cleared</li>',

+                     '<li class="alert alert-info">Calendar cleared</li>',

                      output_text)

  

      def test_edit_calendar(self):
@@ -1286,7 +1244,7 @@ 

              self.assertEqual(output.status_code, 200)

              output_text = output.get_data(as_text=True)

              self.assertIn(

-                 '<li class="errors">You are not a fedocal admin, you are '

+                 '<li class="alert alert-danger">You are not a fedocal admin, you are '

                  'not allowed to edit the calendar.</li>', output_text)

              self.assertIn(

                  '<title>Home - Fedocal</title>', output_text)
@@ -1298,7 +1256,7 @@ 

              self.assertEqual(output.status_code, 200)

              output_text = output.get_data(as_text=True)

              self.assertIn(

-                 '<li class="errors">No calendar named 1 could be found</li',

+                 '<li class="alert alert-danger">No calendar named 1 could be found</li',

                  output_text)

  

              output = self.app.get('/calendar/edit/test_calendar/')
@@ -1309,7 +1267,7 @@ 

              self.assertIn(

                  '<h2>Edit calendar "test_calendar"</h2>', output_text)

              self.assertIn(

-                 'class="submit positive button" value="Edit">',

+                 'class="btn btn-primary" value="Edit">',

                  output_text)

  

              # No data
@@ -1324,7 +1282,7 @@ 

              self.assertIn(

                  '<h2>Edit calendar "test_calendar"</h2>', output_text)

              self.assertIn(

-                 'class="submit positive button" value="Edit">',

+                 'class="btn btn-primary" value="Edit">',

                  output_text)

  

              csrf_token = output_text.split(
@@ -1340,12 +1298,12 @@ 

              self.assertEqual(output.status_code, 200)

              output_text = output.get_data(as_text=True)

              self.assertEqual(

-                 output_text.count('<td>This field is required.</td>'), 3)

+                 output_text.count('<small>This field is required.</small>'), 3)

              self.assertIn(

                  '<h2>Edit calendar "test_calendar"</h2>',

                  output_text)

              self.assertIn(

-                 'class="submit positive button" value="Edit">',

+                 'class="btn btn-primary" value="Edit">',

                  output_text)

  

              # Edit
@@ -1377,13 +1335,13 @@ 

                  self.assertIn(

                      '<title>Election1 - Fedocal</title>', output_text)

                  self.assertIn(

-                     '<li class="message">Calendar updated</li>',

+                     '<li class="alert alert-info">Calendar updated</li>',

                      output_text)

                  self.assertNotIn(

-                     '<span class="calendar_name">test_calendar</span>',

+                     '<span>test_calendar</span>',

                      output_text)

                  self.assertNotIn(

-                     '<span class="calendar_name">election1</span>',

+                     '<span>election1</span>',

                      output_text)

  

      def test_auth_logout(self):
@@ -1395,7 +1353,7 @@ 

              output_text = output.get_data(as_text=True)

              self.assertIn('<title>Home - Fedocal</title>', output_text)

              self.assertIn(

-                 '<li class="message">You have been logged out</li>',

+                 '<li class="alert alert-info">You have been logged out</li>',

                  output_text)

  

          user = None
@@ -1405,7 +1363,7 @@ 

              output_text = output.get_data(as_text=True)

              self.assertIn('<title>Home - Fedocal</title>', output_text)

              self.assertNotIn(

-                 '<li class="message">You have been logged out</li>',

+                 '<li class="alert alert-info">You have been logged out</li>',

                  output_text)

  

      def test_my_meetings(self):
@@ -1417,7 +1375,7 @@ 

              self.assertEqual(output.status_code, 200)

              output_text = output.get_data(as_text=True)

              self.assertIn(

-                 '"errors">You must be in one more group than the CLA</li>',

+                 '"alert alert-danger">You must be in one more group than the CLA</li>',

                  output_text)

  

          user = FakeUser()
@@ -1451,14 +1409,14 @@ 

              self.assertEqual(output.status_code, 200)

              output_text = output.get_data(as_text=True)

              self.assertIn(

-                 '"errors">No calendar named calendar_test could be found</',

+                 '"alert alert-danger">No calendar named calendar_test could be found</',

                  output_text)

  

              output = self.app.get('/test_calendar/add/', follow_redirects=True)

              self.assertEqual(output.status_code, 200)

              output_text = output.get_data(as_text=True)

              self.assertIn(

-                 '<li class="errors">You are not one of the editors of this '

+                 '<li class="alert alert-danger">You are not one of the editors of this '

                  'calendar, or one of its admins, you are not allowed to add'

                  ' new meetings.</li>', output_text)

              self.assertIn(
@@ -1476,7 +1434,7 @@ 

              self.assertIn(

                  'for="meeting_date">Date</label>', output_text)

              self.assertEqual(

-                 output_text.count('<span class="required">*</span>'), 5)

+                 output_text.count('<span class="text-danger">*</span>'), 5)

  

              csrf_token = output_text.split(

                  'name="csrf_token" type="hidden" value="')[1].split('">')[0]
@@ -1496,7 +1454,7 @@ 

              self.assertEqual(output.status_code, 200)

              output_text = output.get_data(as_text=True)

              self.assertIn(

-                 '<td>This field is required.</td>', output_text)

+                 '<small>This field is required.</small>', output_text)

  

              # Format of the start time wrong

              data = {
@@ -1557,7 +1515,7 @@ 

              self.assertEqual(output.status_code, 200)

              output_text = output.get_data(as_text=True)

              self.assertIn(

-                 '<li class="warnings">The start date of your meeting is '

+                 '<li class="alert alert-warning">The start date of your meeting is '

                  'later than the stop date.</li>', output_text)

              self.assertIn(

                  '<title>Add meeting - Fedocal</title>', output_text)
@@ -1579,7 +1537,7 @@ 

              self.assertEqual(output.status_code, 200)

              output_text = output.get_data(as_text=True)

              self.assertIn(

-                 '<td>Please use channel@server format!</td>', output_text)

+                 '<small>Please use channel@server format!</small>', output_text)

              self.assertIn(

                  '<title>Add meeting - Fedocal</title>', output_text)

  
@@ -1626,7 +1584,7 @@ 

                  self.assertEqual(output.status_code, 200)

                  output_text = output.get_data(as_text=True)

                  self.assertIn(

-                     '<li class="message">Meeting added</li>', output_text)

+                     '<li class="alert alert-info">Meeting added</li>', output_text)

                  self.assertIn(

                      'href="/meeting/16/?from_date=', output_text)

                  self.assertNotIn(
@@ -1650,7 +1608,7 @@ 

                  self.assertEqual(output.status_code, 200)

                  output_text = output.get_data(as_text=True)

                  self.assertIn(

-                     '<li class="message">Meeting added</li>', output_text)

+                     '<li class="alert alert-info">Meeting added</li>', output_text)

                  self.assertIn(

                      'href="/meeting/17/?from_date=', output_text)

                  self.assertNotIn(
@@ -1675,7 +1633,7 @@ 

                  '<title>test_calendar_disabled - Fedocal</title>'

                 , output_text)

              self.assertIn(

-                 '<li class="errors">This calendar is &#34;Disabled&#34;, '

+                 '<li class="alert alert-danger">This calendar is &#34;Disabled&#34;, '

                  'you are not allowed to add meetings anymore.</li>'

                 , output_text)

  
@@ -1700,7 +1658,7 @@ 

              self.assertIn(

                  '<h2>New meeting</h2>', output_text)

              self.assertIn(

-                 '<td>Invalid email address.</td>', output_text)

+                 '<small>Invalid email address.</small>', output_text)

  

              # Fails - one of the two email specified as recipient of the

              # reminder is invalid
@@ -1724,7 +1682,7 @@ 

              self.assertIn(

                  '<h2>New meeting</h2>', output_text)

              self.assertIn(

-                 '<td>The domain name fp is not valid. It should have a period.</td>',

+                 '<small>The domain name fp is not valid. It should have a period.</small>',

                  output_text)

  

              # Works - with one email as recipient of the reminder
@@ -1747,7 +1705,7 @@ 

                  self.assertEqual(output.status_code, 200)

                  output_text = output.get_data(as_text=True)

                  self.assertIn(

-                     '<li class="message">Meeting added</li>', output_text)

+                     '<li class="alert alert-info">Meeting added</li>', output_text)

                  self.assertIn(

                      'href="/meeting/17/?from_date=', output_text)

                  self.assertIn(
@@ -1777,7 +1735,7 @@ 

                  self.assertEqual(output.status_code, 200)

                  output_text = output.get_data(as_text=True)

                  self.assertIn(

-                     '<li class="message">Meeting added</li>', output_text)

+                     '<li class="alert alert-info">Meeting added</li>', output_text)

                  self.assertIn(

                      'href="/meeting/18/?from_date=', output_text)

                  self.assertIn(
@@ -1796,7 +1754,7 @@ 

              self.assertEqual(output.status_code, 200)

              output_text = output.get_data(as_text=True)

              self.assertIn(

-                 'class="errors">The meeting #50 could not be found</li>',

+                 'class="alert alert-danger">The meeting #50 could not be found</li>',

                  output_text)

              self.assertIn('<title>Home - Fedocal</title>', output_text)

  
@@ -1804,7 +1762,7 @@ 

              self.assertEqual(output.status_code, 200)

              output_text = output.get_data(as_text=True)

              self.assertIn(

-                 '<li class="errors">You are not one of the manager of this '

+                 '<li class="alert alert-danger">You are not one of the manager of this '

                  'meeting, or an admin, you are not allowed to edit it.</li>',

                  output_text)

              self.assertIn(
@@ -1817,7 +1775,7 @@ 

              self.assertEqual(output.status_code, 200)

              output_text = output.get_data(as_text=True)

              self.assertIn(

-                 '<li class="errors">You are not one of the manager of this '

+                 '<li class="alert alert-danger">You are not one of the manager of this '

                  'meeting, or an admin, you are not allowed to edit it.</li>',

                  output_text)

              self.assertIn(
@@ -1836,7 +1794,7 @@ 

              self.assertIn(

                  'for="meeting_date">Date</label>', output_text)

              self.assertEqual(

-                 output_text.count('<span class="required">*</span>'), 6)

+                 output_text.count('<span class="text-danger">*</span>'), 6)

  

              csrf_token = output_text.split(

                  'name="csrf_token" type="hidden" value="')[1].split('">')[0]
@@ -1856,7 +1814,7 @@ 

              self.assertEqual(output.status_code, 200)

              output_text = output.get_data(as_text=True)

              self.assertIn(

-                 '<td>This field is required.</td>', output_text)

+                 '<small>This field is required.</small>', output_text)

  

              # No calendar provided

              data = {
@@ -1875,7 +1833,7 @@ 

              self.assertEqual(output.status_code, 200)

              output_text = output.get_data(as_text=True)

              self.assertIn(

-                 '<td>This field is required.</td>', output_text)

+                 '<small>This field is required.</small>', output_text)

              self.assertIn(

                  '<title>Edit meeting - Fedocal</title>', output_text)

  
@@ -1897,7 +1855,7 @@ 

              self.assertEqual(output.status_code, 200)

              output_text = output.get_data(as_text=True)

              self.assertIn(

-                 '<li class="warnings">The start date of your meeting is '

+                 '<li class="alert alert-warning">The start date of your meeting is '

                  'later than the stop date.</li>', output_text)

              self.assertIn(

                  '<title>Edit meeting - Fedocal</title>', output_text)
@@ -1946,7 +1904,7 @@ 

                  self.assertEqual(output.status_code, 200)

                  output_text = output.get_data(as_text=True)

                  self.assertIn(

-                     '<li class="message">Meeting updated</li>', output_text)

+                     '<li class="alert alert-info">Meeting updated</li>', output_text)

                  self.assertIn(

                      '<title>Meeting "guess what?" - Fedocal</title>', output_text)

  
@@ -1970,7 +1928,7 @@ 

                  '<title>test_calendar_disabled - Fedocal</title>',

                  output_text)

              self.assertIn(

-                 '<li class="errors">This calendar is &#34;Disabled&#34;, '

+                 '<li class="alert alert-danger">This calendar is &#34;Disabled&#34;, '

                  'you are not allowed to add meetings to it anymore.</li>',

                  output_text)

  
@@ -2013,7 +1971,7 @@ 

                  '<title>test_calendar_disabled - Fedocal</title>',

                  output_text)

              self.assertIn(

-                 '<li class="errors">This calendar is &#34;Disabled&#34;, '

+                 '<li class="alert alert-danger">This calendar is &#34;Disabled&#34;, '

                  'you are not allowed to edit its meetings anymore.</li>',

                  output_text)

  
@@ -2034,7 +1992,7 @@ 

              self.assertIn(

                  'for="meeting_date">Date</label>', output_text)

              self.assertEqual(

-                 output_text.count('<span class="required">*</span>'), 6)

+                 output_text.count('<span class="text-danger">*</span>'), 6)

  

              output = self.app.get(

                  '/meeting/edit/12/?from_date=%s' % TODAY.strftime('%Y-%m-%d'),
@@ -2051,7 +2009,7 @@ 

              self.assertIn(

                  'for="meeting_date">Date</label>', output_text)

              self.assertEqual(

-                 output_text.count('<span class="required">*</span>'), 6)

+                 output_text.count('<span class="text-danger">*</span>'), 6)

  

      def test_delete_meeting(self):

          """ Test the delete_meeting function. """
@@ -2062,7 +2020,7 @@ 

              self.assertEqual(output.status_code, 200)

              output_text = output.get_data(as_text=True)

              self.assertIn(

-                 '"errors">No meeting with this identifier could be found.</',

+                 '"alert alert-danger">No meeting with this identifier could be found.</',

                  output_text)

              self.assertIn('<title>Home - Fedocal</title>', output_text)

  
@@ -2070,7 +2028,7 @@ 

              self.assertEqual(output.status_code, 200)

              output_text = output.get_data(as_text=True)

              self.assertIn(

-                 '<li class="errors">You are not one of the manager of this '

+                 '<li class="alert alert-danger">You are not one of the manager of this '

                  'meeting, or an admin, you are not allowed to delete it.</l',

                  output_text)

              self.assertIn(
@@ -2083,7 +2041,7 @@ 

              self.assertEqual(output.status_code, 200)

              output_text = output.get_data(as_text=True)

              self.assertIn(

-                 '<li class="errors">You are not one of the manager of this '

+                 '<li class="alert alert-danger">You are not one of the manager of this '

                  'meeting, or an admin, you are not allowed to delete it.</l',

                  output_text)

              self.assertIn(
@@ -2104,10 +2062,10 @@ 

                  "positively sure that's what you want to do?",

                  output_text)

              self.assertIn(

-                 'name="confirm_delete" type="checkbox" value="y"><label',

+                 "name=\"confirm_delete\" type=\"checkbox\" value=\"y\">\n    <label",

                  output_text)

              self.assertIn(

-                 '<input id="confirm_button" type="submit" class="submit positi',

+                 '<input id="confirm_button" type="submit" class="btn btn-danger',

                  output_text)

  

              csrf_token = output_text.split(
@@ -2127,7 +2085,7 @@ 

                  "positively sure that's what you want to do?",

                  output_text)

              self.assertIn(

-                 'name="confirm_delete" type="checkbox" value="y"><label',

+                 "name=\"confirm_delete\" type=\"checkbox\" value=\"y\">\n    <label",

                  output_text)

  

              # Do not delete
@@ -2183,7 +2141,7 @@ 

                  self.assertIn(

                      '<title>test_calendar - Fedocal</title>', output_text)

                  self.assertIn(

-                     '<li class="message">Meeting deleted</li>', output_text)

+                     '<li class="alert alert-info">Meeting deleted</li>', output_text)

  

              # Delete all

              data = {
@@ -2200,7 +2158,7 @@ 

                  self.assertIn(

                      '<title>test_calendar - Fedocal</title>', output_text)

                  self.assertIn(

-                     '<li class="message">Meeting deleted</li>', output_text)

+                     '<li class="alert alert-info">Meeting deleted</li>', output_text)

  

          # Add a meeting to the test_calendar_disabled calendar

          obj = model.Meeting(  # id:16
@@ -2236,7 +2194,7 @@ 

                  '<title>test_calendar_disabled - Fedocal</title>',

                  output_text)

              self.assertTrue(

-                 '<li class="errors">This calendar is &#34;Disabled&#34;, '

+                 '<li class="alert alert-danger">This calendar is &#34;Disabled&#34;, '

                  'you are not allowed to delete its meetings anymore.</li>',

                  output_text)

  
@@ -2250,13 +2208,13 @@ 

          output_text = output.get_data(as_text=True)

          self.assertIn('<title>Home - Fedocal</title>', output_text)

          self.assertIn(

-             '<li class="warnings">Invalid referred url</li>', output_text)

+             '<li class="alert alert-warning">Invalid referred url</li>', output_text)

  

          output = self.app.get('/updatetz/',

                                follow_redirects=True)

          self.assertIn('<title>Home - Fedocal</title>', output_text)

          self.assertIn(

-             '<li class="warnings">Invalid referred url</li>',

+             '<li class="alert alert-warning">Invalid referred url</li>',

              output_text)

  

      def test_search(self):
@@ -2297,7 +2255,7 @@ 

          output_text = output.get_data(as_text=True)

          self.assertIn('<title>Home - Fedocal</title>', output_text)

          self.assertIn(

-             'class="errors">No keyword provided for the search</',

+             'class="alert alert-danger">No keyword provided for the search</',

              output_text)

  

      def test_goto(self):
@@ -2317,7 +2275,7 @@ 

          output_text = output.get_data(as_text=True)

          self.assertIn('<title>Home - Fedocal</title>', output_text)

          self.assertIn(

-             '<li class="errors">No calendar specified</li>', output_text)

+             '<li class="alert alert-danger">No calendar specified</li>', output_text)

  

          output = self.app.get('/goto/?calendar=test_calendar',

                                follow_redirects=True)
@@ -2364,7 +2322,7 @@ 

          self.assertIn(

              '<title>test_calendar - Fedocal</title>', output_text)

          self.assertIn(

-             '<li class="errors">Invalid date specified</li>', output_text)

+             '<li class="alert alert-danger">Invalid date specified</li>', output_text)

          self.assertIn(

              '<p>This is a test calendar</p>', output_text)

  
@@ -2376,7 +2334,7 @@ 

          self.assertIn(

              '<title>test_calendar - Fedocal</title>', output_text)

          self.assertIn(

-             '<li class="errors">Invalid date specified</li>', output_text)

+             '<li class="alert alert-danger">Invalid date specified</li>', output_text)

          self.assertIn(

              '<p>This is a test calendar</p>', output_text)

  
@@ -2387,7 +2345,7 @@ 

          self.assertIn(

              '<title>test_calendar - Fedocal</title>', output_text)

          self.assertIn(

-             '<li class="errors">Invalid date specified</li>', output_text)

+             '<li class="alert alert-danger">Invalid date specified</li>', output_text)

          self.assertIn(

              '<p>This is a test calendar</p>', output_text)

  
@@ -2398,7 +2356,7 @@ 

          self.assertIn(

              '<title>test_calendar - Fedocal</title>', output_text)

          self.assertIn(

-             '="warnings">Dates before 1900 are not allowed</li',

+             '="alert alert-warning">Dates before 1900 are not allowed</li',

              output_text)

          self.assertIn(

              '<p>This is a test calendar</p>', output_text)
@@ -2432,7 +2390,7 @@ 

              self.assertEqual(output.status_code, 200)

              output_text = output.get_data(as_text=True)

              self.assertIn(

-                 '<li class="errors">No calendar named 1 could be found</li',

+                 '<li class="alert alert-danger">No calendar named 1 could be found</li',

                  output_text)

  

              output = self.app.get('/calendar/upload/test_calendar/',
@@ -2440,7 +2398,7 @@ 

              self.assertEqual(output.status_code, 200)

              output_text = output.get_data(as_text=True)

              self.assertIn(

-                 '<li class="errors">You are not an admin for this calendar, '

+                 '<li class="alert alert-danger">You are not an admin for this calendar, '

                  'you are not allowed to upload a iCalendar file to it.</l',

                  output_text)

              self.assertIn(
@@ -2483,18 +2441,18 @@ 

                                             follow_redirects=True, data=data)

                      self.assertEqual(output.status_code, 200)

                      output_text = output.get_data(as_text=True)

-                     if '<li class="error">' not in output_text:

+                     if '<li class="alert alert-danger">' not in output_text:

                          self.assertIn(

                              '<title>test_calendar - Fedocal</title>',

                              output_text)

                          self.assertIn(

                              '<p>This is a test calendar</p>', output_text)

                          self.assertIn(

-                             'li class="message">Calendar uploaded</li>',

+                             'li class="alert alert-info">Calendar uploaded</li>',

                              output_text)

                      else:

                          self.assertIn(

-                             '<li class="error">The submitted candidate has the '

+                             '<li class="alert alert-danger">The submitted candidate has the '

                              'MIME type &#34;application/octet-stream&#34; which '

                              'is not an allowed MIME type</li>', output_text)

  
@@ -2512,7 +2470,7 @@ 

                      '<title>Upload calendar - Fedocal</title>',

                      output_text)

                  self.assertIn(

-                     '<li class="error">The submitted candidate has the file '

+                     '<li class="alert alert-danger">The submitted candidate has the file '

                      'extension &#34;txt&#34; which is not an allowed '

                      'format</li>', output_text)

  

file modified
+8 -8
@@ -111,8 +111,8 @@ 

  

              # If no date is specified, it returns the next occurence

              self.assertIn(

-                 '<input id="meeting_date" name="meeting_date" required type="date" '

-                 'value="%s">' % (next_date), output_text

+                 '<input class="form-control" id="meeting_date" name="meeting_date" '

+                 'required type="date" value="%s">' % (next_date), output_text

              )

  

              # If a date in the future is specified, return the next occurence
@@ -124,8 +124,8 @@ 

              output2_text = output2.get_data(as_text=True)

  

              self.assertIn(

-                 '<input id="meeting_date" name="meeting_date" required type="date" '

-                 'value="%s">' % (TODAY + timedelta(days=28)), output2_text

+                 '<input class="form-control" id="meeting_date" name="meeting_date" '

+                 'required type="date" value="%s">' % (TODAY + timedelta(days=28)), output2_text

              )

  

              # If an exact date in the future is specified, return that date
@@ -136,8 +136,8 @@ 

              output2_text = output2.get_data(as_text=True)

  

              self.assertIn(

-                 '<input id="meeting_date" name="meeting_date" required type="date" '

-                 'value="%s">' % (TODAY + timedelta(days=14)), output2_text

+                 '<input class="form-control" id="meeting_date" name="meeting_date" '

+                 'required type="date" value="%s">' % (TODAY + timedelta(days=14)), output2_text

              )

  

              # If an old date in the future is specified, return the first date
@@ -146,8 +146,8 @@ 

              output2_text = output2.get_data(as_text=True)

  

              self.assertIn(

-                 '<input id="meeting_date" name="meeting_date" required type="date" '

-                 'value="%s">' % (TODAY), output2_text

+                 '<input class="form-control" id="meeting_date" name="meeting_date" '

+                 'required type="date" value="%s">' % (TODAY), output2_text

              )

  

      def test_start_date_delete_meeting_form(self):

This is a relatively simple bootstrap revamp, as far as I can tell everything works fine, though I am not fully familiar with the admin side of things ;)

1 new commit added

  • Remove unused icons and add new ones
2 years ago

1 new commit added

  • Tone down the colours
2 years ago

3 new commits added

  • Tone down the colours
  • Remove unused icons and add new ones
  • Revamp with bootstrap
2 years ago

1 new commit added

  • Use bootstrap forms
2 years ago

1 new commit added

  • Use the correct styling for the flashes
2 years ago

pretty please pagure-ci rebuild

2 years ago

1 new commit added

  • Fix up tests
2 years ago

1 new commit added

  • Remove chunks
2 years ago
Metadata
Changes Summary 51
+7 -13
file changed
fedocal/__init__.py
+13 -13
file changed
fedocal/fedocallib/fedora_calendar.py
-0
file removed
fedocal/static/default/Approved.png
-0
file removed
fedocal/static/default/Denied.png
+109
file added
fedocal/static/default/addon.css
+7637
file added
fedocal/static/default/bootstrap.css
+7
file added
fedocal/static/default/bootstrap.js
-0
file removed
fedocal/static/default/calendar-small.png
-0
file removed
fedocal/static/default/calendar.png
+0 -0
file changed
fedocal/static/default/favicon.ico
+0
file added
fedocal/static/default/favicon/android-chrome-192x192.png
+0
file added
fedocal/static/default/favicon/android-chrome-512x512.png
+0
file added
fedocal/static/default/favicon/apple-touch-icon.png
+0
file added
fedocal/static/default/favicon/favicon-16x16.png
+0
file added
fedocal/static/default/favicon/favicon-24x24.png
+0
file added
fedocal/static/default/favicon/favicon-32x32.png
+0
file added
fedocal/static/default/favicon/favicon-48x48.png
+1
file added
fedocal/static/default/favicon/safari-pinned-tab.svg
+19
file added
fedocal/static/default/favicon/site.webmanifest
+0 -0
file changed
fedocal/static/default/fedocal-logo.png
+6
file added
fedocal/static/default/fedocal-logo.svg
-486
file removed
fedocal/static/default/fedocal.css
+0 -0
file changed
fedocal/static/default/fedocal.png
-0
file removed
fedocal/static/default/fedora-logo.png
-0
file removed
fedocal/static/default/header-bg.png
-0
file removed
fedocal/static/default/home.png
-0
file removed
fedocal/static/default/html-bg.png
-452
file removed
fedocal/static/default/koji.css
+61 -34
file changed
fedocal/templates/default/_formhelpers.html
+11 -12
file changed
fedocal/templates/default/add_calendar.html
+29 -27
file changed
fedocal/templates/default/add_meeting.html
+17 -14
file changed
fedocal/templates/default/admin.html
+126 -135
file changed
fedocal/templates/default/agenda.html
+0 -1
file changed
fedocal/templates/default/api.html
+4 -4
file changed
fedocal/templates/default/clear_calendar.html
+4 -4
file changed
fedocal/templates/default/delete_calendar.html
+6 -6
file changed
fedocal/templates/default/delete_meeting.html
+9 -10
file changed
fedocal/templates/default/edit_calendar.html
+23 -21
file changed
fedocal/templates/default/edit_meeting.html
+18 -17
file changed
fedocal/templates/default/index.html
+9 -11
file changed
fedocal/templates/default/locations.html
+106 -60
file changed
fedocal/templates/default/master.html
+76 -87
file changed
fedocal/templates/default/meeting_list.html
+22 -21
file changed
fedocal/templates/default/my_meeting.html
+4 -7
file changed
fedocal/templates/default/upload_calendar.html
+2 -2
file changed
fedocal/templates/default/view_meeting.html
+1 -0
file changed
requirements.txt
+3 -3
file changed
tests/test_fedocallib.py
+64 -60
file changed
tests/test_fedora_calendar.py
+93 -135
file changed
tests/test_flask.py
+8 -8
file changed
tests/test_flask_extras.py