jekyll-theme-chirpy 4.3.4 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -5
  3. data/_config.yml +9 -5
  4. data/_data/locales/en.yml +2 -1
  5. data/_data/locales/id-ID.yml +2 -1
  6. data/_data/locales/ko-KR.yml +91 -0
  7. data/_data/locales/zh-CN.yml +2 -1
  8. data/_includes/{disqus.html → comments/disqus.html} +4 -4
  9. data/_includes/comments/utterances.html +51 -0
  10. data/_includes/comments.html +5 -0
  11. data/_includes/footer.html +1 -1
  12. data/_includes/js-selector.html +1 -1
  13. data/_includes/mermaid.html +28 -0
  14. data/_includes/mode-toggle.html +48 -65
  15. data/_includes/read-time.html +3 -3
  16. data/_includes/refactor-content.html +109 -35
  17. data/_includes/related-posts.html +1 -1
  18. data/_includes/search-results.html +0 -8
  19. data/_includes/sidebar.html +10 -11
  20. data/_includes/timeago.html +11 -12
  21. data/_includes/toc.html +16 -0
  22. data/_includes/topbar.html +1 -1
  23. data/_includes/trending-tags.html +14 -0
  24. data/_includes/update-list.html +16 -0
  25. data/_layouts/default.html +6 -2
  26. data/_layouts/home.html +12 -9
  27. data/_layouts/page.html +45 -22
  28. data/_layouts/post.html +128 -127
  29. data/_sass/addon/commons.scss +154 -175
  30. data/_sass/addon/module.scss +48 -28
  31. data/_sass/addon/syntax.scss +55 -44
  32. data/_sass/addon/variables.scss +1 -1
  33. data/_sass/colors/dark-syntax.scss +3 -4
  34. data/_sass/colors/dark-typography.scss +11 -8
  35. data/_sass/colors/light-syntax.scss +3 -3
  36. data/_sass/colors/light-typography.scss +4 -5
  37. data/_sass/jekyll-theme-chirpy.scss +1 -1
  38. data/_sass/layout/home.scss +6 -2
  39. data/_sass/layout/post.scss +52 -46
  40. data/assets/js/dist/categories.min.js +2 -2
  41. data/assets/js/dist/commons.min.js +2 -2
  42. data/assets/js/dist/home.min.js +2 -2
  43. data/assets/js/dist/page.min.js +2 -2
  44. data/assets/js/dist/post.min.js +2 -2
  45. data/assets/js/dist/pvreport.min.js +2 -2
  46. metadata +7 -4
  47. data/_includes/panel.html +0 -59
@@ -38,63 +38,100 @@
38
38
  %}
39
39
  {% endif %}
40
40
 
41
-
42
41
  <!-- images -->
43
42
 
44
- {% if _content contains '<img src="' %}
45
-
46
- <!-- add CDN prefix if it exists -->
47
-
48
- {% if site.img_cdn != '' %}
49
- {% assign img_path_replacement = '<img src="' | append: site.img_cdn | append: '/' %}
50
- {% else %}
51
- {% assign img_path_replacement = '<img src="' | append: site.baseurl | append: '/' %}
52
- {% endif %}
53
-
54
- {% assign _content = _content | replace: '<img src="/', img_path_replacement %}
55
-
56
- <!-- lazy-load images <https://github.com/ApoorvSaxena/lozad.js#usage> -->
57
-
58
- {% assign _content = _content | replace: '<img src="', '<img data-proofer-ignore data-src="' %}
59
-
60
- <!-- add image placehoder to prevent layout reflow -->
43
+ {% assign IMG_TAG = '<img ' %}
61
44
 
45
+ {% if _content contains IMG_TAG %}
62
46
  {% assign _img_content = nil %}
47
+ {% assign _img_snippets = _content | split: IMG_TAG %}
63
48
 
64
- {% assign _images = _content | split: '<img ' %}
65
-
66
- {% for _img in _images %}
49
+ {% for _img_snippet in _img_snippets %}
67
50
  {% if forloop.first %}
68
- {% assign _img_content = _img %}
51
+ {% assign _img_content = _img_snippet %}
69
52
  {% continue %}
70
53
  {% endif %}
71
54
 
72
55
  {% assign _width = nil %}
73
56
  {% assign _height = nil %}
74
- {% assign _attrs = _img | split: '>' | first | split: ' ' %}
57
+ {% assign _src = nil %}
58
+
59
+ {% assign _left = _img_snippet | split: '/>' | first %}
60
+ {% assign _right = _img_snippet | replace: _left, '' %}
61
+
62
+ {% assign _left = _left | replace: ' w=', ' width=' | replace: ' h=', ' height=' %}
63
+ {% assign _attrs = _left | split: ' ' %}
75
64
 
76
65
  {% for _attr in _attrs %}
77
- {% capture _key %}{{ _attr | split: '=' | first }}{% endcapture %}
78
- {% capture _value %}{{ _attr | split: '=' | last | replace: '"', '' }}{% endcapture %}
66
+ {% assign _pair = _attr | split: '=' %}
67
+ {% if _pair.size < 2 %}
68
+ {% continue %}
69
+ {% endif %}
70
+
71
+ {% capture _key %}{{ _pair | first }}{% endcapture %}
72
+ {% capture _value %}{{ _pair | last | replace: '"', '' }}{% endcapture %}
79
73
 
80
74
  {% case _key %}
81
75
  {% when 'width' %}
82
76
  {% assign _width = _value %}
83
77
  {% when 'height' %}
84
78
  {% assign _height = _value %}
79
+ {% when 'src' %}
80
+ {% assign _src = _value %}
85
81
  {% endcase %}
86
82
 
87
- {% if _width and _height %}
88
- {% capture _svg %}data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 {{_width}} {{_height}}'%3E%3C/svg%3E{% endcapture %}
89
- {% assign _img_content = _img_content | append: '<img src="' | append: _svg | append: '" ' | append: _img %}
83
+ {% if _width and _height and _src %}
90
84
  {% break %}
91
85
  {% endif %}
92
-
93
86
  {% endfor %}
94
87
 
95
- {% unless _width and _height %}
96
- {% assign _img_content = _img_content | append: '<img ' | append: _img %}
97
- {% endunless %}
88
+ {% if _src %}
89
+ {% unless _src contains '://' %}
90
+
91
+ <!-- Add CDN URL -->
92
+ {% if site.img_cdn %}
93
+ {% assign _src_prefix = site.img_cdn %}
94
+ {% else %}
95
+ {% assign _src_prefix = site.baseurl %}
96
+ {% endif %}
97
+
98
+ <!-- Add image path -->
99
+ {% if page.img_path %}
100
+ {% assign _path = page.img_path %}
101
+ {% assign last_char = _path | slice: -1 %}
102
+
103
+ {% unless last_char == '/' %}
104
+ {% assign _path = _path | append: '/' %}
105
+ {% endunless %}
106
+
107
+ {% assign _src_prefix = _src_prefix | append: _path %}
108
+ {% endif %}
109
+
110
+ {% assign _final_src = _src_prefix | append: _src %}
111
+ {% assign _left = _left | replace: _src, _final_src %}
112
+
113
+ {% endunless %}
114
+
115
+ <!-- lazy-load images <https://github.com/ApoorvSaxena/lozad.js#usage> -->
116
+
117
+ {% assign _left = _left | replace: 'src=', 'data-src=' %}
118
+
119
+ {% endif %}
120
+
121
+ <!-- Add SVG placehoder to prevent layout reflow -->
122
+
123
+ {% if _width and _height %}
124
+ {%- capture _svg -%}
125
+ src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 {{ _width }} {{ _height }}'%3E%3C/svg%3E"
126
+ {%- endcapture -%}
127
+
128
+ {% assign _left = _svg | append: ' ' | append: _left %}
129
+ {% endif %}
130
+
131
+ <!-- Bypass the HTML-proofer test -->
132
+ {% assign _left = _left | append: ' data-proofer-ignore' %}
133
+
134
+ {% assign _img_content = _img_content | append: IMG_TAG | append: _left | append: _right %}
98
135
 
99
136
  {% endfor %}
100
137
 
@@ -118,16 +155,16 @@
118
155
  {% assign _left = _snippet | split: '><' | last%}
119
156
 
120
157
  {% if _left contains 'file="' %}
121
- {% assign _text = _left | split: 'file="' | last | split: '"' | first %}
158
+ {% assign _label_text = _left | split: 'file="' | last | split: '"' | first %}
122
159
  {% assign _label_icon = 'far fa-file-code' %}
123
160
  {% else %}
124
161
  {% assign _lang = _left | split: 'language-' | last | split: ' ' | first %}
125
- {% capture _text %}{% include language-alias.html language=_lang %}{% endcapture %}
162
+ {% capture _label_text %}{% include language-alias.html language=_lang %}{% endcapture %}
126
163
  {% assign _label_icon = 'fas fa-code small' %}
127
164
  {% endif %}
128
165
 
129
166
  {% capture _label %}
130
- <span text-data="{{ _text }}"><i class="fa-fw {{ _label_icon }}"></i></span>
167
+ <span label-text="{{ _label_text | strip }}"><i class="{{ _label_icon }}"></i></span>
131
168
  {% endcapture %}
132
169
 
133
170
  {% assign _new_content = _new_content | append: _snippet
@@ -147,6 +184,43 @@
147
184
 
148
185
  {% endif %}
149
186
 
187
+ <!-- Create heading anchors -->
188
+
189
+ {% assign heading_levels = '2,3,4,5' | split: ',' %}
190
+ {% assign _heading_content = _content %}
191
+
192
+ {% for level in heading_levels %}
193
+ {% capture mark_start %}<h{{ level }} id="{% endcapture %}
194
+ {% capture mark_end %}</h{{ level }}>{% endcapture %}
195
+
196
+ {% if _heading_content contains mark_start %}
197
+ {% assign _new_content = nil %}
198
+ {% assign heading_snippets = _heading_content | split: mark_start %}
199
+
200
+ {% for snippet in heading_snippets %}
201
+ {% if forloop.first %}
202
+ {% assign _new_content = snippet %}
203
+ {% continue %}
204
+ {% endif %}
205
+
206
+ {% assign id = snippet | split: '"' | first %}
207
+ {% capture anchor %}<a href="#{{ id }}" class="anchor"><i class="fas fa-hashtag"></i></a>{% endcapture %}
208
+
209
+ {% assign left = snippet | split: mark_end | first %}
210
+ {% assign right = snippet | replace: left, '' %}
211
+
212
+ {% assign _new_content = _new_content | append: mark_start
213
+ | append: left | append: anchor | append: mark_end | append: right
214
+ %}
215
+
216
+ {% endfor %}
217
+
218
+ {% assign _heading_content = _new_content %}
219
+
220
+ {% endif %}
221
+ {% endfor %}
222
+
223
+ {% assign _content = _heading_content %}
150
224
 
151
225
  <!-- return -->
152
226
 
@@ -94,7 +94,7 @@
94
94
  <div class="text-muted small">
95
95
  <p>
96
96
  {% include no-linenos.html content=post.content %}
97
- {{ content | markdownify | strip_html | truncate: 200 }}
97
+ {{ content | markdownify | strip_html | truncate: 200 | escape }}
98
98
  </p>
99
99
  </div>
100
100
  </div>
@@ -4,15 +4,7 @@
4
4
  <div id="search-result-wrapper" class="d-flex justify-content-center unloaded">
5
5
  <div class="col-12 col-sm-11 post-content">
6
6
  <div id="search-hints">
7
- <h4 class="text-muted mb-4">{{ site.data.locales[lang].panel.trending_tags }}</h4>
8
-
9
7
  {% include trending-tags.html %}
10
-
11
- {% for tag in trending_tags %}
12
- {% capture url %}/tags/{{ tag | slugify | url_encode }}/{% endcapture %}
13
- <a class="post-tag" href="{{ url | relative_url }}">{{ tag | replace: '-', ' ' }}</a>
14
- {% endfor %}
15
-
16
8
  </div>
17
9
  <div id="search-results" class="d-flex flex-wrap justify-content-center text-muted mt-3"></div>
18
10
  </div>
@@ -53,6 +53,16 @@
53
53
 
54
54
  <div class="sidebar-bottom mt-auto d-flex flex-wrap justify-content-center align-items-center">
55
55
 
56
+ {% unless site.theme_mode %}
57
+ <button class="mode-toggle btn" aria-label="Switch Mode">
58
+ <i class="fas fa-adjust"></i>
59
+ </button>
60
+
61
+ {% if site.data.contact.size > 0 %}
62
+ <span class="icon-border"></span>
63
+ {% endif %}
64
+ {% endunless %}
65
+
56
66
  {% for entry in site.data.contact %}
57
67
  {% capture url %}
58
68
  {%- if entry.type == 'github' -%}
@@ -71,7 +81,6 @@
71
81
 
72
82
  {% if url %}
73
83
  <a href="{{ url }}" aria-label="{{ entry.type }}"
74
- {% unless site.theme_mode %}class="order-{{ forloop.index | plus: 2 }}"{% endunless %}
75
84
  {% unless entry.noblank %}target="_blank" rel="noopener"{% endunless %}>
76
85
  <i class="{{ entry.icon }}"></i>
77
86
  </a>
@@ -79,16 +88,6 @@
79
88
 
80
89
  {% endfor %}
81
90
 
82
- {% unless site.theme_mode %}
83
- {% if site.data.contact.size > 0 %}
84
- <span class="icon-border order-2"></span>
85
- {% endif %}
86
-
87
- <span id="mode-toggle-wrapper" class="order-1">
88
- {% include mode-toggle.html %}
89
- </span>
90
- {% endunless %}
91
-
92
91
  </div> <!-- .sidebar-bottom -->
93
92
 
94
93
  </div><!-- #sidebar -->
@@ -1,27 +1,26 @@
1
1
  <!--
2
2
  Date format snippet
3
- See: /assets/js/_utils/timeage.js
3
+ See: ${JS_ROOT}/utils/timeago.js
4
4
  -->
5
5
 
6
6
  {% assign tooltip_df = site.data.locales[lang].date_format.tooltip %}
7
7
  {% assign post_long_df = site.data.locales[lang].date_format.post.long %}
8
8
  {% assign post_short_df = site.data.locales[lang].date_format.post.short %}
9
9
 
10
- {% if include.preposition %}
11
- {{ include.preposition }}
12
- {% endif %}
13
- <span class="timeago {% if include.class %}{{ include.class }}{% endif %}"
14
- {% if include.tooltip %}
15
- data-toggle="tooltip"
16
- data-placement="bottom"
17
- title="{{ include.date | date: tooltip_df }}"
18
- {% endif %}>
10
+ <em class="timeago{% if include.class %} {{ include.class }}{% endif %}"
11
+ date="{{ include.date }}"
12
+ {% if include.tooltip %}
13
+ data-toggle="tooltip"
14
+ data-placement="bottom"
15
+ title="{{ include.date | date: tooltip_df }}"
16
+ {% endif %}>
17
+
19
18
  {%- assign this_year = site.time | date: "%Y" -%}
20
19
  {%- assign post_year = include.date | date: "%Y" -%}
20
+
21
21
  {%- if post_year == this_year -%}
22
22
  {{ include.date | date: post_short_df }}
23
23
  {%- else -%}
24
24
  {{ include.date | date: post_long_df }}
25
25
  {%- endif -%}
26
- <i class="unloaded">{{ include.date | date_to_xmlschema }}</i>
27
- </span>
26
+ </em>
@@ -0,0 +1,16 @@
1
+ {% assign enable_toc = false %}
2
+ {% if site.toc and page.toc %}
3
+ {% if page.content contains '<h2' or page.content contains '<h3' %}
4
+ {% assign enable_toc = true %}
5
+ {% endif %}
6
+ {% endif %}
7
+
8
+ {% if enable_toc %}
9
+ <!-- BS-toc.js will be loaded at medium priority -->
10
+ <script src="https://cdn.jsdelivr.net/gh/afeld/bootstrap-toc@1.0.1/dist/bootstrap-toc.min.js"></script>
11
+
12
+ <div id="toc-wrapper" class="pl-0 pr-4 mb-5">
13
+ <div class="panel-heading pl-3 pt-2 mb-2">{{- site.data.locales[lang].panel.toc -}}</div>
14
+ <nav id="toc" data-toggle="toc"></nav>
15
+ </div>
16
+ {% endif %}
@@ -50,7 +50,7 @@
50
50
  <div id="topbar-title">
51
51
  {% if page.layout == 'home' %}
52
52
  {{- site.data.locales[lang].title | default: site.title -}}
53
- {% elsif page.collection == 'tabs' %}
53
+ {% elsif page.collection == 'tabs' or page.dynamic_title %}
54
54
  {%- capture tab_key -%}{{ page.url | split: '/' }}{%- endcapture -%}
55
55
  {{- site.data.locales[lang].tabs[tab_key] | default: page.title -}}
56
56
  {% else %}
@@ -34,3 +34,17 @@
34
34
  {% endif %}
35
35
  {% endfor %}
36
36
  {% endfor %}
37
+
38
+ {% if trending_tags.size > 0 %}
39
+ <div id="access-tags">
40
+ <div class="panel-heading">{{- site.data.locales[lang].panel.trending_tags -}}</div>
41
+ <div class="d-flex flex-wrap mt-3 mb-1 mr-3">
42
+
43
+ {% for tag_name in trending_tags %}
44
+ {% assign url = tag_name | slugify | url_encode | prepend: "/tags/" | append: "/" %}
45
+ <a class="post-tag" href="{{ url | relative_url }}">{{ tag_name }}</a>
46
+ {% endfor %}
47
+
48
+ </div>
49
+ </div>
50
+ {% endif %}
@@ -22,3 +22,19 @@
22
22
  {% for entry in all_list limit:MAX_SIZE %}
23
23
  {% assign update_list = update_list | push: entry %}
24
24
  {% endfor %}
25
+
26
+ {% if update_list.size > 0 %}
27
+
28
+ <div id="access-lastmod" class="post">
29
+ <div class="panel-heading">{{- site.data.locales[lang].panel.lastmod -}}</div>
30
+ <ul class="post-content pl-0 pb-1 ml-1 mt-2">
31
+ {% for item in update_list %}
32
+ {% assign index = item | split: "::" | last | plus: 0 %}
33
+ {% assign post = site.posts[index] %}
34
+ {% assign url = post.url | relative_url %}
35
+ <li><a href="{{ url }}">{{ post.title }}</a></li>
36
+ {% endfor %}
37
+ </ul>
38
+ </div> <!-- #access-lastmod -->
39
+
40
+ {% endif %}
@@ -13,10 +13,14 @@ layout: compress
13
13
  {% endif %}
14
14
  {% endcapture %}
15
15
 
16
- <html lang="{{ site.lang }}" {{ prefer_mode }}>
16
+ <html lang="{{ site.lang }}"{{ prefer_mode }}>
17
17
 
18
18
  {% include head.html %}
19
19
 
20
+ {% unless site.theme_mode %}
21
+ {% include mode-toggle.html %}
22
+ {% endunless %}
23
+
20
24
  <body data-spy="scroll" data-target="#toc">
21
25
 
22
26
  {% include sidebar.html %}
@@ -26,7 +30,7 @@ layout: compress
26
30
  <div id="main-wrapper">
27
31
  <div id="main">
28
32
 
29
- {% include refactor-content.html content=content %}
33
+ {{ content }}
30
34
 
31
35
  {% include footer.html %}
32
36
 
data/_layouts/home.html CHANGED
@@ -53,13 +53,13 @@ layout: page
53
53
  <div class="post-content">
54
54
  <p>
55
55
  {% include no-linenos.html content=post.content %}
56
- {{ content | markdownify | strip_html | truncate: 200 }}
56
+ {{ content | markdownify | strip_html | truncate: 200 | escape }}
57
57
  </p>
58
58
  </div>
59
59
 
60
- <div class="post-meta text-muted d-flex justify-content-between">
60
+ <div class="post-meta text-muted d-flex">
61
61
 
62
- <div>
62
+ <div class="mr-auto">
63
63
  <!-- posted date -->
64
64
  <i class="far fa-calendar fa-fw"></i>
65
65
  {% include timeago.html date=post.date tooltip=true %}
@@ -68,12 +68,15 @@ layout: page
68
68
  <i class="far fa-clock fa-fw"></i>
69
69
  {% include read-time.html content=post.content %}
70
70
 
71
- <!-- page views -->
72
- {% if site.google_analytics.pv.proxy_endpoint or site.google_analytics.pv.cache_path %}
73
- <i class="far fa-eye fa-fw"></i>
74
- <span id="pv_{{-post.title-}}" class="pageviews">
75
- <i class="fas fa-spinner fa-spin fa-fw"></i>
76
- </span>
71
+ <!-- categories -->
72
+ {% if post.categories.size > 0 %}
73
+ <i class="far fa-folder-open fa-fw"></i>
74
+ <span>
75
+ {% for category in post.categories %}
76
+ {{ category }}
77
+ {%- unless forloop.last -%},{%- endunless -%}
78
+ {% endfor %}
79
+ </span>
77
80
  {% endif %}
78
81
  </div>
79
82
 
data/_layouts/page.html CHANGED
@@ -1,41 +1,64 @@
1
1
  ---
2
2
  layout: default
3
- # The page layout
4
3
  ---
5
4
 
5
+ {% include lang.html %}
6
+
6
7
  <div class="row">
7
- <div class="col-12 col-lg-11 col-xl-8">
8
- <div id="page" class="post pb-5 pl-1 pr-1 pl-sm-2 pr-sm-2 pl-md-4 pr-md-4 mb-md-4">
9
- {% if page.dynamic_title %}
8
+
9
+ <!-- core -->
10
+ <div id="core-wrapper" class="col-12 col-lg-11 col-xl-8">
11
+ <div class="post pl-1 pr-1 pl-sm-2 pr-sm-2 pl-md-4 pr-md-4">
12
+
13
+ {% capture _content %}
14
+ {% if layout.refactor or page.layout == 'page' %}
15
+ {% include refactor-content.html content=content %}
16
+ {% else %}
17
+ {{ content }}
18
+ {% endif %}
19
+ {% endcapture %}
20
+
21
+ {% if page.layout == 'page' or page.collection == 'tabs'%}
22
+ {% assign title = site.data.locales[lang].tabs[tab_key] | default: page.title %}
10
23
  <h1 class="dynamic-title">
11
- {% if page.collection == 'tabs' %}
12
- {%- capture tab_key -%}{{ page.url | split: '/' }}{%- endcapture -%}
13
- {{- site.data.locales[lang].tabs[tab_key] | default: page.title -}}
14
- {% else %}
15
- {{- page.title -}}
16
- {% endif %}
24
+ {{ title }}
17
25
  </h1>
18
26
  <div class="post-content">
19
- {{ content }}
27
+ {{ _content }}
20
28
  </div>
21
29
  {% else %}
22
- {{ content }}
30
+ {{ _content }}
23
31
  {% endif %}
24
- </div> <!-- #page -->
25
- </div><!-- .col-12 -->
26
32
 
27
- {% include panel.html %}
33
+ </div>
34
+ </div> <!-- #core-wrapper -->
35
+
36
+ <!-- pannel -->
37
+ <div id="panel-wrapper" class="col-xl-3 pl-2 text-muted topbar-down">
38
+
39
+ <div class="access">
40
+ {% include update-list.html %}
41
+ {% include trending-tags.html %}
42
+ </div>
43
+
44
+ {% for _include in layout.pannel_includes %}
45
+ {% assign _include_path = _include | append: '.html' %}
46
+ {% include {{ _include_path }} %}
47
+ {% endfor %}
48
+ </div>
28
49
 
29
50
  </div>
30
51
 
31
- {% if site.disqus.comments and page.comments %}
52
+ <!-- tail -->
53
+ {% if layout.tail_includes %}
32
54
  <div class="row">
33
55
  <div class="col-12 col-lg-11 col-xl-8">
34
- <div class="pl-1 pr-1 pl-sm-2 pr-sm-2 pl-md-4 pr-md-4">
35
-
36
- {% include disqus.html %}
37
-
38
- </div> <!-- .pl-1 pr-1 -->
39
- </div> <!-- .col-12 -->
56
+ <div id="tail-wrapper" class="pl-1 pr-1 pl-sm-2 pr-sm-2 pl-md-4 pr-md-4">
57
+ {% for _include in layout.tail_includes %}
58
+ {% assign _include_path = _include | append: '.html' %}
59
+ {% include {{ _include_path }} %}
60
+ {% endfor %}
61
+ </div>
62
+ </div>
40
63
  </div> <!-- .row -->
41
64
  {% endif %}