Initial
This commit is contained in:
10
src/assets/static/col2.js
Normal file
10
src/assets/static/col2.js
Normal file
@@ -0,0 +1,10 @@
|
||||
(function(){// show at least 2 columns on mobile devices
|
||||
var viewport = document.head.querySelector("meta[name=viewport]");
|
||||
if (viewport && screen.width < 485) {
|
||||
document.head.removeChild(viewport);
|
||||
var x = document.createElement("meta");
|
||||
x.setAttribute("name", "viewport");
|
||||
x.setAttribute("content", "width=485");
|
||||
document.head.appendChild(x);
|
||||
}
|
||||
})();
|
||||
9
src/assets/static/lozad.min.js
vendored
Normal file
9
src/assets/static/lozad.min.js
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
/*! lozad.js - v1.9.0 - 2019-02-09
|
||||
* https://github.com/ApoorvSaxena/lozad.js
|
||||
* Copyright (c) 2019 Apoorv Saxena; Licensed MIT */
|
||||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):t.lozad=e()}(this,function(){"use strict";var g=Object.assign||function(t){for(var e=1;e<arguments.length;e++){var r=arguments[e];for(var o in r)Object.prototype.hasOwnProperty.call(r,o)&&(t[o]=r[o])}return t},n="undefined"!=typeof document&&document.documentMode,l={rootMargin:"0px",threshold:0,load:function(t){if("picture"===t.nodeName.toLowerCase()){var e=document.createElement("img");n&&t.getAttribute("data-iesrc")&&(e.src=t.getAttribute("data-iesrc")),t.getAttribute("data-alt")&&(e.alt=t.getAttribute("data-alt")),t.appendChild(e)}if("video"===t.nodeName.toLowerCase()&&!t.getAttribute("data-src")&&t.children){for(var r=t.children,o=void 0,a=0;a<=r.length-1;a++)(o=r[a].getAttribute("data-src"))&&(r[a].src=o);t.load()}t.getAttribute("data-src")&&(t.src=t.getAttribute("data-src")),t.getAttribute("data-srcset")&&t.setAttribute("srcset",t.getAttribute("data-srcset")),t.getAttribute("data-background-image")&&(t.style.backgroundImage="url('"+t.getAttribute("data-background-image")+"')"),t.getAttribute("data-toggle-class")&&t.classList.toggle(t.getAttribute("data-toggle-class"))},loaded:function(){}};
|
||||
/**
|
||||
* Detect IE browser
|
||||
* @const {boolean}
|
||||
* @private
|
||||
*/function f(t){t.setAttribute("data-loaded",!0)}var b=function(t){return"true"===t.getAttribute("data-loaded")};return function(){var r,o,a=0<arguments.length&&void 0!==arguments[0]?arguments[0]:".lozad",t=1<arguments.length&&void 0!==arguments[1]?arguments[1]:{},e=g({},l,t),n=e.root,i=e.rootMargin,d=e.threshold,c=e.load,u=e.loaded,s=void 0;return window.IntersectionObserver&&(s=new IntersectionObserver((r=c,o=u,function(t,e){t.forEach(function(t){(0<t.intersectionRatio||t.isIntersecting)&&(e.unobserve(t.target),b(t.target)||(r(t.target),f(t.target),o(t.target)))})}),{root:n,rootMargin:i,threshold:d})),{observe:function(){for(var t=function(t){var e=1<arguments.length&&void 0!==arguments[1]?arguments[1]:document;return t instanceof Element?[t]:t instanceof NodeList?t:e.querySelectorAll(t)}(a,n),e=0;e<t.length;e++)b(t[e])||(s?s.observe(t[e]):(c(t[e]),f(t[e]),u(t[e])))},triggerLoad:function(t){b(t)||(c(t),f(t),u(t))},observer:s}}});
|
||||
153
src/assets/static/style.css
Normal file
153
src/assets/static/style.css
Normal file
@@ -0,0 +1,153 @@
|
||||
:root { --cTxt: #222;
|
||||
--cBg1: #FAF9F7; --cBg2: #EAE9E7; --cBg3: #9A9997;
|
||||
--cRed1: #DC3A59; --cRed2: #AA203A; --cRed3: #EE6A84;
|
||||
}
|
||||
|
||||
.center { text-align: center }
|
||||
.small { font-size: 0.8em }
|
||||
.large { font-size: 1.3em }
|
||||
.xlarge { font-size: 1.6em }
|
||||
.italic { font-style: italic }
|
||||
.bold { font-weight: bold }
|
||||
.dark-red { color: var(--cRed2) }
|
||||
.light-red { color: var(--cRed3) }
|
||||
.mrgTopMd { margin-top: 0.7em }
|
||||
.v-scroll { overflow-x: scroll; white-space: nowrap; -webkit-overflow-scrolling: touch }
|
||||
|
||||
ul.no-bullets { padding: unset; margin: unset; list-style: none }
|
||||
ul.li-lg-space li { padding-bottom: 0.3em }
|
||||
|
||||
/*
|
||||
* General
|
||||
*/
|
||||
a { color: var(--cRed2); text-decoration: none }
|
||||
a:hover { color: var(--cRed1) }
|
||||
body {
|
||||
font-family: 'Verdana', sans-serif;
|
||||
background-color: var(--cBg3);
|
||||
color: var(--cTxt);
|
||||
margin: unset;
|
||||
}
|
||||
header #logo { font-size: 42px; display: block; margin-bottom: 15px }
|
||||
header a { color: var(--cTxt) }
|
||||
header, h1 { text-align: center }
|
||||
header, footer, .page {
|
||||
background: var(--cBg2);
|
||||
max-width: 1060px;
|
||||
margin: 0 auto;
|
||||
padding: 20px 30px;
|
||||
}
|
||||
.page { background: var(--cBg1) }
|
||||
nav ul { padding: unset }
|
||||
nav ul li { display: inline-block; margin: 0.1em 0.5em }
|
||||
nav ul li a.active { text-decoration: overline }
|
||||
footer table { margin: -10px 0 }
|
||||
|
||||
@media(max-width: 485px) { body { font-size: 1.4em } }
|
||||
@media print {
|
||||
header, footer { display: none }
|
||||
body, .page { background-color: #FFF }
|
||||
}
|
||||
|
||||
/*
|
||||
* Components
|
||||
*/
|
||||
.tags { display: flex; flex-wrap: wrap; justify-content: center }
|
||||
.tags > * {
|
||||
background-color: #FFF;
|
||||
border: 1px solid var(--cRed1);
|
||||
border-radius: 0.3em;
|
||||
padding: 0.3em 0.5em;
|
||||
margin: 0.2em;
|
||||
}
|
||||
.tags a:hover, .tags .active, a:hover .recipe-tile {
|
||||
background-color: var(--cRed1);
|
||||
color: #FFF;
|
||||
}
|
||||
header .tags { max-width: 600px; margin: 0 auto }
|
||||
.cluster dt { margin-top: 0.7em; font-size: 1.6em }
|
||||
.cluster dd { margin-top: 0.4em }
|
||||
.cluster dd a { white-space: nowrap }
|
||||
|
||||
@media(max-width: 500px) {
|
||||
.cluster dd { margin-left: 0 }
|
||||
.cluster dd a { white-space: unset }
|
||||
}
|
||||
|
||||
/*
|
||||
* Grid overview
|
||||
*/
|
||||
.pagination { text-align: center; margin-top: 1em; }
|
||||
.recipe-tile {
|
||||
background-color: var(--cBg2);
|
||||
color: var(--cTxt);
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
margin: 6px;
|
||||
width: 200px;
|
||||
text-align: center;
|
||||
}
|
||||
.recipe-tile .img-placeholder {
|
||||
background-color: #777;
|
||||
color: var(--cBg2);
|
||||
width: 200px;
|
||||
height: 150px;
|
||||
font: bold 25px/150px 'Courier New', monospace;
|
||||
}
|
||||
a:hover .recipe-tile img { mix-blend-mode: overlay }
|
||||
.recipe-tile p { height: 2.6em; margin: 0.3em 10px; overflow-y: auto }
|
||||
.tile-grid { width: fit-content; max-width: 1060px; margin: 0 auto }
|
||||
.latest .tile-grid { max-width: 636px }
|
||||
/* max-width = prev + 2*30; width = x * (200 + 2*6); */
|
||||
@media screen and (max-width: 1120px) { .tile-grid { max-width: 848px } }
|
||||
@media screen and (max-width: 908px) { .tile-grid { max-width: 636px } }
|
||||
@media screen and (max-width: 696px) { .tile-grid { max-width: 424px !important } }
|
||||
@media screen and (max-width: 484px) { .tile-grid { max-width: 212px !important } }
|
||||
|
||||
@media print and (orientation: portrait) { .tile-grid { width: 636px } }
|
||||
@media print and (orientation: landscape) { .tile-grid { width: 1060px } }
|
||||
@media print {
|
||||
a:hover .recipe-tile img { mix-blend-mode: unset !important }
|
||||
.recipe-tile, .recipe-tile .img-placeholder, a:hover .recipe-tile {
|
||||
background-color: #FFF; color: #000 }
|
||||
}
|
||||
|
||||
/*
|
||||
* Individual recipe
|
||||
*/
|
||||
.recipe h2 { font-size: 0.8em; margin: 0 0 1em 0 }
|
||||
.recipe #img-carousel { padding: 0 50px; margin: 0 -30px }
|
||||
.recipe #source { margin-left: -1em; margin-bottom: -1.5em }
|
||||
.recipe #metrics { float: right; margin: 0 0 15px 25px; max-width: 180px }
|
||||
.recipe #metrics > * { text-indent: -20px; margin-left: 20px; padding-top: 0.3em }
|
||||
.recipe #ingredients { float: left; margin: 0 25px 15px 0; max-width: 300px }
|
||||
.recipe #directions ul { list-style-type: circle }
|
||||
/* Colored, 3-part, difficulty bar */
|
||||
.difficulty.easy > div:nth-child(1) { background-color: #3C3 }
|
||||
.difficulty.medium > div:nth-child(1),
|
||||
.difficulty.medium > div:nth-child(2) { background-color: #FC3 }
|
||||
.difficulty.hard > div:nth-child(1),
|
||||
.difficulty.hard > div:nth-child(2),
|
||||
.difficulty.hard > div:nth-child(3) { background-color: #F30 }
|
||||
.difficulty > * { vertical-align: middle; }
|
||||
.difficulty > div:nth-child(1) { border-radius: 50% 0 0 50% }
|
||||
.difficulty > div:nth-child(3) { border-radius: 0 50% 50% 0 }
|
||||
.difficulty > div {
|
||||
display: inline-block;
|
||||
width: 1em; height: 1em;
|
||||
border: 1px solid #555;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 800px) {
|
||||
.recipe #img-carousel img { height: auto; width: 100%; padding: 0 }
|
||||
.recipe #metrics { float: unset; max-width: fit-content; margin: 20px auto }
|
||||
}
|
||||
@media(max-width: 600px) { .recipe #ingredients { float: unset; max-width: 100% } }
|
||||
@media(max-width: 500px) { .recipe #img-carousel { padding: 0 10px } }
|
||||
|
||||
@media print { #source, #rating, .difficulty { display: none } }
|
||||
@media print and (orientation: landscape) { #img-carousel img { display: none } }
|
||||
@media print and (orientation: portrait) {
|
||||
#img-carousel img:not(:first-child) { display: none }
|
||||
.recipe #metrics { float: unset; padding-bottom: 1em }
|
||||
}
|
||||
1
src/content/contents.lr
Normal file
1
src/content/contents.lr
Normal file
@@ -0,0 +1 @@
|
||||
_model: root
|
||||
5
src/content/groupby/contents.lr
Normal file
5
src/content/groupby/contents.lr
Normal file
@@ -0,0 +1,5 @@
|
||||
_model: clusters
|
||||
---
|
||||
_template: querylist.html
|
||||
---
|
||||
_slug: recipes/by
|
||||
1
src/content/groupby/difficulty/contents+de.lr
Normal file
1
src/content/groupby/difficulty/contents+de.lr
Normal file
@@ -0,0 +1 @@
|
||||
name: Aufwand
|
||||
1
src/content/groupby/difficulty/contents+en.lr
Normal file
1
src/content/groupby/difficulty/contents+en.lr
Normal file
@@ -0,0 +1 @@
|
||||
name: Difficulty
|
||||
9
src/content/groupby/difficulty/contents.lr
Normal file
9
src/content/groupby/difficulty/contents.lr
Normal file
@@ -0,0 +1,9 @@
|
||||
sort_key: 10
|
||||
---
|
||||
group_key: difficulty
|
||||
---
|
||||
xdata:
|
||||
|
||||
easy
|
||||
medium
|
||||
hard
|
||||
1
src/content/groupby/ingredient/contents+de.lr
Normal file
1
src/content/groupby/ingredient/contents+de.lr
Normal file
@@ -0,0 +1 @@
|
||||
name: Zutaten
|
||||
1
src/content/groupby/ingredient/contents+en.lr
Normal file
1
src/content/groupby/ingredient/contents+en.lr
Normal file
@@ -0,0 +1 @@
|
||||
name: Ingredients
|
||||
3
src/content/groupby/ingredient/contents.lr
Normal file
3
src/content/groupby/ingredient/contents.lr
Normal file
@@ -0,0 +1,3 @@
|
||||
sort_key: 5
|
||||
---
|
||||
group_key: ingredients
|
||||
1
src/content/groupby/rating/contents+de.lr
Normal file
1
src/content/groupby/rating/contents+de.lr
Normal file
@@ -0,0 +1 @@
|
||||
name: Bewertung
|
||||
1
src/content/groupby/rating/contents+en.lr
Normal file
1
src/content/groupby/rating/contents+en.lr
Normal file
@@ -0,0 +1 @@
|
||||
name: Rating
|
||||
5
src/content/groupby/rating/contents.lr
Normal file
5
src/content/groupby/rating/contents.lr
Normal file
@@ -0,0 +1,5 @@
|
||||
sort_key: 15
|
||||
---
|
||||
group_key: rating
|
||||
---
|
||||
reverse_order: yes
|
||||
1
src/content/groupby/time/contents+de.lr
Normal file
1
src/content/groupby/time/contents+de.lr
Normal file
@@ -0,0 +1 @@
|
||||
name: Zeit
|
||||
1
src/content/groupby/time/contents+en.lr
Normal file
1
src/content/groupby/time/contents+en.lr
Normal file
@@ -0,0 +1 @@
|
||||
name: Time
|
||||
13
src/content/groupby/time/contents.lr
Normal file
13
src/content/groupby/time/contents.lr
Normal file
@@ -0,0 +1,13 @@
|
||||
sort_key: 20
|
||||
---
|
||||
group_key: time
|
||||
---
|
||||
xdata:
|
||||
|
||||
15
|
||||
30
|
||||
60
|
||||
120
|
||||
180
|
||||
360
|
||||
9999
|
||||
1
src/content/recipes/contents.lr
Normal file
1
src/content/recipes/contents.lr
Normal file
@@ -0,0 +1 @@
|
||||
_model: recipes
|
||||
16
src/content/recipes/vanilla-cut-out-cookies/contents+de.lr
Normal file
16
src/content/recipes/vanilla-cut-out-cookies/contents+de.lr
Normal file
@@ -0,0 +1,16 @@
|
||||
name: Vanille Ausstech-Kekse
|
||||
---
|
||||
yield: 26-28 Kekse
|
||||
---
|
||||
ingredients:
|
||||
|
||||
1/2 Tasse Margarine, oder Kokos Öl
|
||||
1/2 Tasse Ahornsirup
|
||||
1/2 TL Vanille Extrakt
|
||||
1/4 TL Vanilleshote
|
||||
1 Prise Salz
|
||||
2 1/4 Tassen Mehl, glutenfrei
|
||||
---
|
||||
directions:
|
||||
|
||||
No translation yet. Click the flag (🇱🇷) at the bottom.
|
||||
26
src/content/recipes/vanilla-cut-out-cookies/contents+en.lr
Normal file
26
src/content/recipes/vanilla-cut-out-cookies/contents+en.lr
Normal file
@@ -0,0 +1,26 @@
|
||||
name: Vanilla cut-out cookies
|
||||
---
|
||||
yield: 26-28 cookies
|
||||
---
|
||||
ingredients:
|
||||
|
||||
1/2 cup non-dairy butter, or coconut oil
|
||||
1/2 cup maple syrup
|
||||
1/2 tsp vanilla extract
|
||||
1/4 tsp vanilla bean
|
||||
dash salt
|
||||
2 1/4 cups gluten-free flour blend
|
||||
---
|
||||
directions:
|
||||
|
||||
1) Preheat oven to 350°F. Line 2 cookie sheets with parchment paper. Prepare a rolling area with two additional sheets of parchment paper for that, and have your cookie cutter(s) handy.
|
||||
|
||||
2) Place butter in a large mixing bowl and whip it with a mixer until it’s creamy. Add sweetener, vanilla extract and bean, and salt and mix once again to combine. Add in flour and use a wooden spoon to mix. Then get in there with your hands and mix everything together by working the dough until you can shape it into a ball {note: as depending on the flour mix you use there may be a slight variance, know that the consistency of the dough should not be sticky but should press together when pinched — be sure to knead it really well first for some time — if it’s a little sticky, add a little more flour (try 1-2 tbsp); if it’s a little dry add a little more sweetener (try 1 tbsp)}. Shape the dough into 2 balls and then flatten each into a disk.
|
||||
|
||||
3) Roll out one of the dough balls between two sheets of parchment paper to ¼” thickness {or thinner or thicker depending on how you want your cookies to turn out}. Use a cookie cutter to cut out the cookies. Carefully transfer to a prepared cookie sheets, spacing them ½” apart {they won’t spread as they bake}. Gather up any dough scraps and repeat until all dough is used up. Repeat the process with the second dough ball.
|
||||
|
||||
4) Bake in a pre-heated oven for approximately 11-13 minutes, until the edges just begin to become golden. Remove from oven and place on a cooling rack. {Note: cookies will harden a little within minutes of cooling, so don’t overbake}. Allow the cookies to cool for 10 minutes and enjoy!
|
||||
|
||||
__Note:__
|
||||
You can make your own glutenfree flour blend by combining:
|
||||
1 cup brown rice flour, ¾ cup tapioca starch, ½ cup sweet rice flour, ½ tsp guar gum
|
||||
11
src/content/recipes/vanilla-cut-out-cookies/contents.lr
Normal file
11
src/content/recipes/vanilla-cut-out-cookies/contents.lr
Normal file
@@ -0,0 +1,11 @@
|
||||
tags: cookies, sweet, xmas, glutenfree
|
||||
---
|
||||
time: 30
|
||||
---
|
||||
rating: 4
|
||||
---
|
||||
difficulty: easy
|
||||
---
|
||||
source: https://www.unconventionalbaker.com/recipes/gluten-free-vegan-vanilla-cut-out-cookies/
|
||||
---
|
||||
date: 2019-05-15
|
||||
BIN
src/content/recipes/vanilla-cut-out-cookies/image.jpg
Normal file
BIN
src/content/recipes/vanilla-cut-out-cookies/image.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 58 KiB |
BIN
src/content/recipes/vanilla-cut-out-cookies/image2.jpg
Normal file
BIN
src/content/recipes/vanilla-cut-out-cookies/image2.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 70 KiB |
BIN
src/content/recipes/vanilla-cut-out-cookies/image3.jpg
Normal file
BIN
src/content/recipes/vanilla-cut-out-cookies/image3.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 41 KiB |
1
src/content/settings/contents+de.lr
Normal file
1
src/content/settings/contents+de.lr
Normal file
@@ -0,0 +1 @@
|
||||
measures: EL, TL, kg, g, L, dl, cl, ml, cm, Msp, Prise, Tasse, Tassen, Dose, Dosen, kleine, Bund, Packung, Packungen, Scheibe, Scheiben, Schuss, Stängel, Tropfen, Tube
|
||||
1
src/content/settings/contents+en.lr
Normal file
1
src/content/settings/contents+en.lr
Normal file
@@ -0,0 +1 @@
|
||||
measures: kg, g, L, dl, cl, ml, oz, lb, pt, qt, cm, tsp, tbsp, c, cup, cups, pkg, pck, drop, drops, tube, dash, dashes, ounce, ounces, small, medium, large, box, can, pinch, tin, clove, cloves
|
||||
9
src/content/settings/contents.lr
Normal file
9
src/content/settings/contents.lr
Normal file
@@ -0,0 +1,9 @@
|
||||
_model: settings
|
||||
---
|
||||
_hidden: yes
|
||||
---
|
||||
replace_frac: yes
|
||||
---
|
||||
replace_temp: yes
|
||||
---
|
||||
show_empty_tags: no
|
||||
1
src/content/tags/bread/contents+de.lr
Normal file
1
src/content/tags/bread/contents+de.lr
Normal file
@@ -0,0 +1 @@
|
||||
name: Brot
|
||||
1
src/content/tags/bread/contents.lr
Normal file
1
src/content/tags/bread/contents.lr
Normal file
@@ -0,0 +1 @@
|
||||
name: Bread
|
||||
1
src/content/tags/cake/contents+de.lr
Normal file
1
src/content/tags/cake/contents+de.lr
Normal file
@@ -0,0 +1 @@
|
||||
name: Kuchen
|
||||
1
src/content/tags/cake/contents.lr
Normal file
1
src/content/tags/cake/contents.lr
Normal file
@@ -0,0 +1 @@
|
||||
name: Cake
|
||||
1
src/content/tags/chocolate/contents+de.lr
Normal file
1
src/content/tags/chocolate/contents+de.lr
Normal file
@@ -0,0 +1 @@
|
||||
name: Schokolade
|
||||
1
src/content/tags/chocolate/contents.lr
Normal file
1
src/content/tags/chocolate/contents.lr
Normal file
@@ -0,0 +1 @@
|
||||
name: Chocolate
|
||||
5
src/content/tags/contents.lr
Normal file
5
src/content/tags/contents.lr
Normal file
@@ -0,0 +1,5 @@
|
||||
_model: tags
|
||||
---
|
||||
_slug: recipes/tags
|
||||
---
|
||||
_template: querylist.html
|
||||
1
src/content/tags/cookies/contents+de.lr
Normal file
1
src/content/tags/cookies/contents+de.lr
Normal file
@@ -0,0 +1 @@
|
||||
name: Kekse
|
||||
1
src/content/tags/cookies/contents.lr
Normal file
1
src/content/tags/cookies/contents.lr
Normal file
@@ -0,0 +1 @@
|
||||
name: Cookies
|
||||
1
src/content/tags/dip/contents.lr
Normal file
1
src/content/tags/dip/contents.lr
Normal file
@@ -0,0 +1 @@
|
||||
name: Dip
|
||||
1
src/content/tags/dressing/contents.lr
Normal file
1
src/content/tags/dressing/contents.lr
Normal file
@@ -0,0 +1 @@
|
||||
name: Dressing
|
||||
1
src/content/tags/drinks/contents.lr
Normal file
1
src/content/tags/drinks/contents.lr
Normal file
@@ -0,0 +1 @@
|
||||
name: Drinks
|
||||
1
src/content/tags/glutenfree/contents+de.lr
Normal file
1
src/content/tags/glutenfree/contents+de.lr
Normal file
@@ -0,0 +1 @@
|
||||
name: Glutenfrei
|
||||
1
src/content/tags/glutenfree/contents.lr
Normal file
1
src/content/tags/glutenfree/contents.lr
Normal file
@@ -0,0 +1 @@
|
||||
name: Glutenfree
|
||||
1
src/content/tags/ingredient/contents+de.lr
Normal file
1
src/content/tags/ingredient/contents+de.lr
Normal file
@@ -0,0 +1 @@
|
||||
name: Zutat
|
||||
1
src/content/tags/ingredient/contents.lr
Normal file
1
src/content/tags/ingredient/contents.lr
Normal file
@@ -0,0 +1 @@
|
||||
name: Ingredient
|
||||
1
src/content/tags/main-dish/contents+de.lr
Normal file
1
src/content/tags/main-dish/contents+de.lr
Normal file
@@ -0,0 +1 @@
|
||||
name: Hauptspeise
|
||||
1
src/content/tags/main-dish/contents.lr
Normal file
1
src/content/tags/main-dish/contents.lr
Normal file
@@ -0,0 +1 @@
|
||||
name: Main dish
|
||||
1
src/content/tags/raw/contents.lr
Normal file
1
src/content/tags/raw/contents.lr
Normal file
@@ -0,0 +1 @@
|
||||
name: Raw
|
||||
1
src/content/tags/salad/contents+de.lr
Normal file
1
src/content/tags/salad/contents+de.lr
Normal file
@@ -0,0 +1 @@
|
||||
name: Salat
|
||||
1
src/content/tags/salad/contents.lr
Normal file
1
src/content/tags/salad/contents.lr
Normal file
@@ -0,0 +1 @@
|
||||
name: Salad
|
||||
1
src/content/tags/sauce/contents+de.lr
Normal file
1
src/content/tags/sauce/contents+de.lr
Normal file
@@ -0,0 +1 @@
|
||||
name: Soße
|
||||
1
src/content/tags/sauce/contents.lr
Normal file
1
src/content/tags/sauce/contents.lr
Normal file
@@ -0,0 +1 @@
|
||||
name: Sauce
|
||||
1
src/content/tags/spread/contents+de.lr
Normal file
1
src/content/tags/spread/contents+de.lr
Normal file
@@ -0,0 +1 @@
|
||||
name: Aufstrich
|
||||
1
src/content/tags/spread/contents.lr
Normal file
1
src/content/tags/spread/contents.lr
Normal file
@@ -0,0 +1 @@
|
||||
name: Spread
|
||||
1
src/content/tags/sweet/contents+de.lr
Normal file
1
src/content/tags/sweet/contents+de.lr
Normal file
@@ -0,0 +1 @@
|
||||
name: Süßes
|
||||
1
src/content/tags/sweet/contents.lr
Normal file
1
src/content/tags/sweet/contents.lr
Normal file
@@ -0,0 +1 @@
|
||||
name: Sweet
|
||||
1
src/content/tags/xmas/contents+de.lr
Normal file
1
src/content/tags/xmas/contents+de.lr
Normal file
@@ -0,0 +1 @@
|
||||
name: Weihnachten
|
||||
1
src/content/tags/xmas/contents.lr
Normal file
1
src/content/tags/xmas/contents.lr
Normal file
@@ -0,0 +1 @@
|
||||
name: Xmas
|
||||
25
src/databags/i18n+de.ini
Normal file
25
src/databags/i18n+de.ini
Normal file
@@ -0,0 +1,25 @@
|
||||
[duration]
|
||||
label = Zeit
|
||||
day = Tag
|
||||
days = Tage
|
||||
hour = Std
|
||||
hours = Std
|
||||
min = Min
|
||||
mins = Min
|
||||
|
||||
[yield]
|
||||
label = Menge
|
||||
|
||||
[difficulty]
|
||||
label = Aufwand
|
||||
_unset = Aufwand unklar
|
||||
easy = Einfach
|
||||
medium = Mittel
|
||||
hard = Schwer
|
||||
|
||||
[title]
|
||||
latest = Zuletzt hinzugefügt
|
||||
all_recipes = Alle Rezepte
|
||||
recipes = Rezepte
|
||||
ingredients = Zutaten
|
||||
directions = Zubereitung
|
||||
25
src/databags/i18n+en.ini
Normal file
25
src/databags/i18n+en.ini
Normal file
@@ -0,0 +1,25 @@
|
||||
[duration]
|
||||
label = Time
|
||||
day = day
|
||||
days = days
|
||||
hour = hour
|
||||
hours = hours
|
||||
min = minutes
|
||||
mins = min
|
||||
|
||||
[yield]
|
||||
label = Yield
|
||||
|
||||
[difficulty]
|
||||
label = Difficulty
|
||||
_unset = Difficulty not set
|
||||
easy = Easy
|
||||
medium = Medium
|
||||
hard = Hard
|
||||
|
||||
[title]
|
||||
latest = Latest recipes
|
||||
all_recipes = All recipes
|
||||
recipes = Recipes
|
||||
ingredients = Ingredients
|
||||
directions = Directions
|
||||
44
src/models/cluster.ini
Normal file
44
src/models/cluster.ini
Normal file
@@ -0,0 +1,44 @@
|
||||
[model]
|
||||
name = Cluster
|
||||
label = {{ this.name }}
|
||||
hidden = yes
|
||||
protected = yes
|
||||
|
||||
[children]
|
||||
enabled = no
|
||||
|
||||
[attachments]
|
||||
enabled = no
|
||||
|
||||
[fields.name]
|
||||
label = Name
|
||||
width = 1/2
|
||||
type = string
|
||||
|
||||
[fields.group_key]
|
||||
label = Grouping Attribute
|
||||
width = 1/3
|
||||
type = string
|
||||
|
||||
[fields.sort_key]
|
||||
label = Sort order
|
||||
width = 1/5
|
||||
default = 0
|
||||
type = sort_key
|
||||
|
||||
[fields.null_fallback]
|
||||
label = Null Fallback
|
||||
width = 1/3
|
||||
type = string
|
||||
default = ???
|
||||
|
||||
[fields.reverse_order]
|
||||
label = Reverse Sort
|
||||
width = 1/5
|
||||
type = boolean
|
||||
|
||||
[fields.xdata]
|
||||
label = Extended Data
|
||||
description = Used for ordinal sort order or merging integer cluster
|
||||
width = 1/2
|
||||
type = strings
|
||||
12
src/models/clusters.ini
Normal file
12
src/models/clusters.ini
Normal file
@@ -0,0 +1,12 @@
|
||||
[model]
|
||||
name = Cluster
|
||||
label = Cluster
|
||||
hidden = yes
|
||||
protected = yes
|
||||
|
||||
[children]
|
||||
model = cluster
|
||||
order_by = sort_key
|
||||
|
||||
[attachments]
|
||||
enabled = no
|
||||
67
src/models/recipe.ini
Normal file
67
src/models/recipe.ini
Normal file
@@ -0,0 +1,67 @@
|
||||
[model]
|
||||
name = Recipe
|
||||
label = {{ this._id }}
|
||||
hidden = yes
|
||||
|
||||
[children]
|
||||
enabled = no
|
||||
|
||||
[fields.name]
|
||||
label = Name
|
||||
width = 2/3
|
||||
type = string
|
||||
size = large
|
||||
|
||||
[fields.date]
|
||||
label = Date / Datum
|
||||
width = 1/3
|
||||
type = date
|
||||
size = large
|
||||
|
||||
[fields.time]
|
||||
label = Time / Zeit
|
||||
width = 1/8
|
||||
type = select
|
||||
choices = 5, 10, 15, 20, 30, 45, 60, 75, 90, 105, 120, 150, 180, 240, 360, 480, 720, 1440
|
||||
choice_labels = 5m, 10m, 15m, 20m, 30m, 45m, 1h, 1h 15m, 1h 30m, 1h 45m, 2h, 2h 30m, 3h, 4h, 6h, 8h, 12h, 24h
|
||||
|
||||
[fields.difficulty]
|
||||
label = Difficulty
|
||||
width = 1/8
|
||||
type = select
|
||||
choices = easy, medium, hard
|
||||
choice_labels = Easy, Medium, Hard
|
||||
|
||||
[fields.rating]
|
||||
label = Rating
|
||||
width = 1/8
|
||||
type = select
|
||||
choices = 1, 2, 3, 4, 5
|
||||
choice_labels = ★☆☆☆☆, ★★☆☆☆, ★★★☆☆, ★★★★☆, ★★★★★
|
||||
|
||||
[fields.yield]
|
||||
label = Yield / Menge
|
||||
width = 1/2
|
||||
type = string
|
||||
|
||||
[fields.ingredients]
|
||||
label = Ingredients / Zutaten
|
||||
description = 42 g Ingredient, Notes (add additional measures in settings)
|
||||
width = 2/3
|
||||
type = strings
|
||||
|
||||
[fields.tags]
|
||||
label = Tags / Kategorie
|
||||
width = 1/3
|
||||
type = checkboxes
|
||||
source = site.query('/tags', alt)
|
||||
|
||||
[fields.directions]
|
||||
label = Directions / Zubereitung
|
||||
description = Markdown formatting applies: ### Header, __bold__, _italic_
|
||||
type = markdown
|
||||
|
||||
[fields.source]
|
||||
label = Source / Quelle
|
||||
type = url
|
||||
size = small
|
||||
16
src/models/recipes.ini
Normal file
16
src/models/recipes.ini
Normal file
@@ -0,0 +1,16 @@
|
||||
[model]
|
||||
name = Recipes
|
||||
label = Recipes
|
||||
hidden = yes
|
||||
protected = yes
|
||||
|
||||
[attachments]
|
||||
enabled = no
|
||||
|
||||
[children]
|
||||
model = recipe
|
||||
order_by = name
|
||||
|
||||
[pagination]
|
||||
enabled = yes
|
||||
per_page = 60
|
||||
6
src/models/root.ini
Normal file
6
src/models/root.ini
Normal file
@@ -0,0 +1,6 @@
|
||||
[model]
|
||||
name = Root Page
|
||||
label = root
|
||||
|
||||
[attachments]
|
||||
enabled = no
|
||||
34
src/models/settings.ini
Normal file
34
src/models/settings.ini
Normal file
@@ -0,0 +1,34 @@
|
||||
[model]
|
||||
name = Settings
|
||||
label = Settings
|
||||
hidden = yes
|
||||
protected = yes
|
||||
|
||||
[children]
|
||||
enabled = no
|
||||
|
||||
[attachments]
|
||||
enabled = no
|
||||
|
||||
[fields.measures]
|
||||
label = Measures
|
||||
description = Comma separated list
|
||||
width = 3/5
|
||||
type = text
|
||||
default = kg, g, L, dl, cl, ml, oz, lb, pt, qt, cm, tsp, tbsp, c, cup, cups, pkg, pck
|
||||
|
||||
[fields.replace_frac]
|
||||
label = Replace 1/2 with ½, ⅔, et.c
|
||||
width = 1/5
|
||||
type = boolean
|
||||
|
||||
[fields.replace_temp]
|
||||
label = Replace °C/°F with ℃/℉
|
||||
width = 1/5
|
||||
type = boolean
|
||||
|
||||
[fields.show_empty_tags]
|
||||
label = Show empty tags
|
||||
description = Even if no recipes exist in that category
|
||||
width = 1/4
|
||||
type = boolean
|
||||
18
src/models/tag.ini
Normal file
18
src/models/tag.ini
Normal file
@@ -0,0 +1,18 @@
|
||||
[model]
|
||||
name = Tag
|
||||
label = {{ this.name }}
|
||||
hidden = yes
|
||||
|
||||
[attachments]
|
||||
enabled = no
|
||||
|
||||
[children]
|
||||
replaced_with = site.query('/recipes', alt).filter(F.tags.contains(this))
|
||||
|
||||
[pagination]
|
||||
enabled = yes
|
||||
per_page = 60
|
||||
|
||||
[fields.name]
|
||||
label = Name
|
||||
type = string
|
||||
12
src/models/tags.ini
Normal file
12
src/models/tags.ini
Normal file
@@ -0,0 +1,12 @@
|
||||
[model]
|
||||
name = Tags
|
||||
label = Tags
|
||||
hidden = yes
|
||||
protected = yes
|
||||
|
||||
[children]
|
||||
model = tag
|
||||
order_by = name
|
||||
|
||||
[attachments]
|
||||
enabled = no
|
||||
5
src/packages/helper/.gitignore
vendored
Normal file
5
src/packages/helper/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
dist
|
||||
build
|
||||
*.pyc
|
||||
*.pyo
|
||||
*.egg-info
|
||||
3
src/packages/helper/README.md
Normal file
3
src/packages/helper/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Helper
|
||||
|
||||
Just some python functions that are necessary for the project.
|
||||
251
src/packages/helper/lektor_helper.py
Normal file
251
src/packages/helper/lektor_helper.py
Normal file
@@ -0,0 +1,251 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from lektor.pluginsystem import Plugin
|
||||
from lektor.databags import Databags
|
||||
import unicodedata
|
||||
import os
|
||||
import shutil
|
||||
|
||||
# -------
|
||||
# Sorting
|
||||
|
||||
|
||||
def sortKeyInt(x):
|
||||
return int(x[0]) if x[0] else 0
|
||||
|
||||
|
||||
def sortKeyStr(x):
|
||||
return noUmlaut(x[0]).lower()
|
||||
|
||||
|
||||
def groupByDictSort(dic, sorter=None, reverse=False):
|
||||
if type(sorter) == list: # sort by pre-defined, ordered list
|
||||
return sorted(dic, reverse=bool(reverse), key=lambda x:
|
||||
sorter.index(x[0]) if x[0] in sorter else 0)
|
||||
fn = sortKeyInt if sorter == 'int' else sortKeyStr
|
||||
return sorted(dic, reverse=bool(reverse), key=fn)
|
||||
|
||||
# -----------------------
|
||||
# Pure text manupulations
|
||||
|
||||
|
||||
def noUmlaut(text):
|
||||
try:
|
||||
text = unicode(text, 'utf-8')
|
||||
except (TypeError, NameError):
|
||||
pass
|
||||
text = unicodedata.normalize('NFD', text)
|
||||
text = text.encode('ascii', 'ignore')
|
||||
text = text.decode("utf-8")
|
||||
return str(text)
|
||||
|
||||
|
||||
def pluralize(n, single, multi):
|
||||
if n == 0:
|
||||
return ''
|
||||
return u'{} {}'.format(n, single if n == 1 else multi)
|
||||
|
||||
|
||||
def replaceFractions(txt):
|
||||
res = ''
|
||||
for x in txt.split():
|
||||
try:
|
||||
i = ['1/2', '1/3', '2/3', '1/4', '3/4', '1/8', '-'].index(x)
|
||||
res += [u'½', u'⅓', u'⅔', u'¼', u'¾', u'⅛', u' - '][i]
|
||||
except ValueError:
|
||||
res += ' ' + x
|
||||
return res.lstrip()
|
||||
|
||||
|
||||
def numFillWithText(num, fill=u'★', empty=u'☆', total=5):
|
||||
num = int(num) if num else 0
|
||||
return fill * num + empty * (total - num)
|
||||
|
||||
# ------------------
|
||||
# Array manipulation
|
||||
|
||||
|
||||
def updateSet_if(dic, parent, parentkey, value):
|
||||
try:
|
||||
key = parent[parentkey]
|
||||
except KeyError:
|
||||
return
|
||||
if not key:
|
||||
key = ''
|
||||
try:
|
||||
dic[key]
|
||||
except KeyError:
|
||||
dic[key] = set()
|
||||
dic[key].add(value)
|
||||
|
||||
|
||||
def updateSet_addMultiple(dic, key, others):
|
||||
try:
|
||||
dic[key]
|
||||
except KeyError:
|
||||
dic[key] = set()
|
||||
dic[key].update(others)
|
||||
|
||||
|
||||
def findCluster(key, clusterList=[30, 60, 120]):
|
||||
key = int(key) if key else 0
|
||||
if key > 0:
|
||||
for cluster in clusterList:
|
||||
if key < cluster:
|
||||
key = cluster
|
||||
break
|
||||
return key
|
||||
|
||||
# --------------------
|
||||
# Ingredient splitting
|
||||
|
||||
|
||||
def splitIngredientLine(line):
|
||||
state = 1
|
||||
capture = False
|
||||
indices = [0, len(line)]
|
||||
for i, char in enumerate(line):
|
||||
if char.isspace():
|
||||
capture = False
|
||||
indices[state] = i
|
||||
state += 1
|
||||
continue
|
||||
elif capture:
|
||||
continue
|
||||
elif state == 1 and char in '0123456789-.,':
|
||||
state -= 1
|
||||
elif state > 1:
|
||||
break
|
||||
capture = True
|
||||
return indices
|
||||
|
||||
|
||||
def parseIngredientLine(line, measureList=[], rep_frac=False):
|
||||
idx = splitIngredientLine(line)
|
||||
val = line[:idx[0]]
|
||||
if rep_frac:
|
||||
val = replaceFractions(val)
|
||||
measure = line[idx[0]:idx[1]].lstrip()
|
||||
if measure.lower() in measureList:
|
||||
name = line[idx[1]:].lstrip()
|
||||
else:
|
||||
measure = ''
|
||||
name = line[idx[0]:].lstrip()
|
||||
note = ''
|
||||
name_note = name.split(',', 1)
|
||||
if len(name_note) > 1:
|
||||
name, note = [x.strip() for x in name_note]
|
||||
return {'value': val, 'measure': measure, 'name': name, 'note': note}
|
||||
|
||||
# --------------------
|
||||
# Other Helper methods
|
||||
|
||||
|
||||
def groupByMergeCluster(dic, arr=[30, 60, 120], reverse=False):
|
||||
arr = sorted([int(x) for x in arr])
|
||||
groups = dict()
|
||||
for key, recipes in dic:
|
||||
key = findCluster(key, arr)
|
||||
if key == 0 and not reverse:
|
||||
key = ''
|
||||
updateSet_addMultiple(groups, key, recipes)
|
||||
return sorted(groups.items(), reverse=bool(reverse))
|
||||
|
||||
# ----------------
|
||||
# Main entry point
|
||||
|
||||
|
||||
class HelperPlugin(Plugin):
|
||||
name = u'Helper'
|
||||
description = u'Some helper methods, filters, and templates.'
|
||||
alt = None
|
||||
availableTags = set()
|
||||
|
||||
# -----------
|
||||
# Event hooks
|
||||
# -----------
|
||||
|
||||
def on_before_build_all(self, builder, **extra):
|
||||
# display only tags that contain at least one recipe
|
||||
pad = self.env.new_pad()
|
||||
for r in pad.query('recipes'):
|
||||
self.availableTags.update(r['tags'])
|
||||
|
||||
def on_after_prune(self, builder, **extra):
|
||||
# redirect to /en/
|
||||
for file in ['index.html']:
|
||||
src_f = os.path.join(self.env.root_path, 'root', file)
|
||||
if os.path.exists(src_f):
|
||||
dst_f = os.path.join(builder.destination_path, file)
|
||||
with open(dst_f, 'wb') as df:
|
||||
with open(src_f, 'rb') as sf:
|
||||
shutil.copyfileobj(sf, df)
|
||||
|
||||
def on_process_template_context(self, context, **extra):
|
||||
self.alt = context['alt']
|
||||
|
||||
def on_setup_env(self, **extra):
|
||||
# self.env.load_config().iter_alternatives()
|
||||
# pad = self.env.new_pad()
|
||||
# pad.query('groupby', alt=alt)
|
||||
|
||||
def localizeDic(key, subkey=None):
|
||||
bag = Databags(self.env).lookup('i18n+{}.{}'.format(self.alt, key))
|
||||
return bag[subkey] if subkey else bag
|
||||
|
||||
def to_duration(time, cluster=None):
|
||||
time = int(time) if time else 0
|
||||
if (time <= 0):
|
||||
return ''
|
||||
# Calls itself without cluster argument
|
||||
if cluster:
|
||||
cluster = [int(x) for x in cluster]
|
||||
idx = cluster.index(time)
|
||||
if idx == 0:
|
||||
return '<' + to_duration(time)
|
||||
timeA = to_duration(cluster[idx - 1])
|
||||
if idx + 1 >= len(cluster):
|
||||
return '>' + timeA
|
||||
else:
|
||||
return u'{} – {}'.format(timeA, to_duration(time))
|
||||
days = time // (60 * 24)
|
||||
time -= days * (60 * 24)
|
||||
L = localizeDic('duration')
|
||||
return ' '.join([
|
||||
pluralize(days, L['day'], L['days']),
|
||||
pluralize(time // 60, L['hour'], L['hours']),
|
||||
pluralize(time % 60, L['min'], L['mins'])]).strip()
|
||||
|
||||
def ingredientsForRecipe(recipe):
|
||||
set = self.env.new_pad().get('settings', alt=self.alt)
|
||||
meaList = [x.strip() for x in set['measures'].lower().split(',')]
|
||||
repFrac = set['replace_frac']
|
||||
|
||||
for line in recipe['ingredients']:
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
elif line.endswith(':'):
|
||||
yield {'group': line}
|
||||
else:
|
||||
yield parseIngredientLine(line, meaList, repFrac)
|
||||
|
||||
def groupByAttribute(recipeList, attribute):
|
||||
groups = dict()
|
||||
for recipe in recipeList:
|
||||
if attribute == 'ingredients':
|
||||
for ing in ingredientsForRecipe(recipe):
|
||||
updateSet_if(groups, ing, 'name', recipe)
|
||||
else:
|
||||
updateSet_if(groups, recipe, attribute, recipe)
|
||||
# groups[undefinedKey].update(groups.pop('_undefined'))
|
||||
return groups.items()
|
||||
|
||||
self.env.jinja_env.filters['duration'] = to_duration
|
||||
self.env.jinja_env.filters['rating'] = numFillWithText
|
||||
self.env.jinja_env.filters['replaceFractions'] = replaceFractions
|
||||
self.env.jinja_env.filters['enumIngredients'] = ingredientsForRecipe
|
||||
self.env.jinja_env.filters['groupByAttribute'] = groupByAttribute
|
||||
self.env.jinja_env.filters['groupSort'] = groupByDictSort
|
||||
self.env.jinja_env.filters['groupMergeCluster'] = groupByMergeCluster
|
||||
self.env.jinja_env.globals['localize'] = localizeDic
|
||||
self.env.jinja_env.globals['availableTags'] = self.availableTags
|
||||
38
src/packages/helper/setup.py
Normal file
38
src/packages/helper/setup.py
Normal file
@@ -0,0 +1,38 @@
|
||||
import ast
|
||||
import io
|
||||
import re
|
||||
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
with io.open('README.md', 'rt', encoding="utf8") as f:
|
||||
readme = f.read()
|
||||
|
||||
_description_re = re.compile(r'description\s+=\s+(?P<description>.*)')
|
||||
|
||||
with open('lektor_helper.py', 'rb') as f:
|
||||
description = str(ast.literal_eval(_description_re.search(
|
||||
f.read().decode('utf-8')).group(1)))
|
||||
|
||||
setup(
|
||||
author=u'relikd',
|
||||
author_email='oleg@relikd.de',
|
||||
description=description,
|
||||
keywords='Lektor plugin',
|
||||
license='MIT',
|
||||
long_description=readme,
|
||||
long_description_content_type='text/markdown',
|
||||
name='lektor-helper',
|
||||
packages=find_packages(),
|
||||
py_modules=['lektor_helper'],
|
||||
# url='[link to your repository]',
|
||||
version='0.1',
|
||||
classifiers=[
|
||||
'Framework :: Lektor',
|
||||
'Environment :: Plugins',
|
||||
],
|
||||
entry_points={
|
||||
'lektor.plugins': [
|
||||
'helper = lektor_helper:HelperPlugin',
|
||||
]
|
||||
}
|
||||
)
|
||||
14
src/recipes.lektorproject
Normal file
14
src/recipes.lektorproject
Normal file
@@ -0,0 +1,14 @@
|
||||
[project]
|
||||
name = recipe lekture
|
||||
url_style = relative
|
||||
|
||||
[alternatives.en]
|
||||
name = English
|
||||
primary = yes
|
||||
url_prefix = /en/
|
||||
locale = en_US
|
||||
|
||||
[alternatives.de]
|
||||
name = German
|
||||
url_prefix = /de/
|
||||
locale = de_DE
|
||||
1
src/root/index.html
Normal file
1
src/root/index.html
Normal file
@@ -0,0 +1 @@
|
||||
<meta http-equiv="refresh" content="0; URL='en/'" />
|
||||
39
src/templates/cluster.html
Normal file
39
src/templates/cluster.html
Normal file
@@ -0,0 +1,39 @@
|
||||
{% extends "layout.html" %}
|
||||
{% block title %}{{ this.name }}{% endblock %}
|
||||
{% block body %}
|
||||
|
||||
{%- if this.group_key in ['rating', 'time'] -%}
|
||||
{%- set sortType = 'int' -%}
|
||||
{%- elif this.xdata -%}
|
||||
{%- set sortType = this.xdata + [''] -%}
|
||||
{%- endif -%}
|
||||
|
||||
{%- set all = site.query('/recipes', this.alt) | groupByAttribute(this.group_key) | groupSort(sortType, this.reverse_order) -%}
|
||||
|
||||
{%- if this.group_key == 'time' -%}
|
||||
{%- set all = all | groupMergeCluster(this.xdata, this.reverse_order) -%}
|
||||
{%- endif -%}
|
||||
|
||||
<h1>{{ this.name }}</h1>
|
||||
<dl class="cluster">
|
||||
{%- for attrib, recipes in all -%}
|
||||
<dt>{%- if this.group_key == 'rating' -%}
|
||||
{{ attrib | rating }}
|
||||
{%- elif not attrib -%}
|
||||
{{ this.null_fallback }}
|
||||
{%- elif this.group_key == 'time' -%}
|
||||
{{ attrib | duration(this.xdata) }}
|
||||
{%- elif this.group_key == 'difficulty' -%}
|
||||
{{ localize('difficulty', attrib) }}
|
||||
{%- else -%}
|
||||
{{ attrib }}
|
||||
{%- endif -%}</dt>
|
||||
<dd>
|
||||
{%- set pipe = joiner(' | ') -%}
|
||||
{%- for recipe in recipes | sort(attribute='name') -%}
|
||||
{{ pipe() }}<a href="{{ recipe|url }}">{{ recipe.name }}</a>
|
||||
{%- endfor -%}
|
||||
</dd>
|
||||
{%- endfor %}
|
||||
</dl>
|
||||
{% endblock %}
|
||||
48
src/templates/layout.html
Normal file
48
src/templates/layout.html
Normal file
@@ -0,0 +1,48 @@
|
||||
<!doctype html>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=0.75">
|
||||
<script type="text/javascript" src="{{ '/static/col2.js'|url }}"></script>
|
||||
<script type="text/javascript" src="{{ '/static/lozad.min.js'|url }}"></script>
|
||||
<link rel="stylesheet" href="{{ '/static/style.css'|url }}">
|
||||
<title>{% block title %}Welcome{% endblock %} · recipe lekture</title>
|
||||
<body>
|
||||
<header>
|
||||
<a id="logo" href="{{ site.get('/', alt=this.alt)|url }}">recipe lekture</a>
|
||||
<nav>
|
||||
<ul>
|
||||
{%- set allRecipes = site.get('recipes', this.alt) %}
|
||||
<li><a {% if this == allRecipes %}class="active"{% endif %} href="{{ allRecipes|url }}">{{ localize('title.all_recipes') }}</a></li>
|
||||
{%- for navpage in site.query('/groupby', this.alt) %}
|
||||
<li><a {% if this.is_child_of(navpage) %}class="active"{% endif %} href="{{ navpage|url }}">{{ navpage.name }}</a></li>
|
||||
{%- endfor %}
|
||||
</ul>
|
||||
</nav>
|
||||
<div class="tags small">
|
||||
{%- set allowEmptyTags = site.get('settings', alt=this.alt)['show_empty_tags'] -%}
|
||||
{%- for tag in site.query('/tags', this.alt) %}
|
||||
{%- if allowEmptyTags or tag._id in availableTags -%}
|
||||
<a {%
|
||||
if this.is_child_of(tag) or (this.tags and tag._id in this.tags) -%}
|
||||
class="active"
|
||||
{%- endif %} href="{{ tag|url }}">{{ tag.name }}</a>
|
||||
{%- endif -%}
|
||||
{%- endfor %}
|
||||
</div>
|
||||
</header>
|
||||
<div class="page">
|
||||
{% block body %}{% endblock %}
|
||||
</div>
|
||||
<footer>{#--#}
|
||||
<table width="100%">{#--#}
|
||||
<td>Build with <a href="https://www.getlektor.com/">Lektor</a>, template by <a href="https://github.com/relikd/lektor-recipes">relikd</a>.</td>{#--#}
|
||||
<td class="xlarge" width="1em">
|
||||
{%- if this.alt == 'de' -%}
|
||||
<a href="{{ '.'|url(alt='en') }}" title="zur englischen Seite wechseln">🇱🇷</a>
|
||||
{%- else -%}
|
||||
<a href="{{ '.'|url(alt='de') }}" title="switch to german page">🇩🇪</a>
|
||||
{%- endif -%}
|
||||
</td>{#--#}
|
||||
</table>{#--#}
|
||||
</footer>
|
||||
<script type="text/javascript">const observer = lozad(); observer.observe();</script>
|
||||
</body>
|
||||
33
src/templates/macros/pagination.html
Normal file
33
src/templates/macros/pagination.html
Normal file
@@ -0,0 +1,33 @@
|
||||
{%- macro render_pagination_all(pagination) -%}
|
||||
{%- if pagination.pages > 1 -%}
|
||||
<div class=pagination>
|
||||
{%- for page in pagination.iter_pages() %}
|
||||
{% if page -%}
|
||||
{%- if page != pagination.page -%}
|
||||
<a href="{{ pagination.for_page(page)|url }}">{{ page }}</a>
|
||||
{%- else -%}
|
||||
<strong>{{ page }}</strong>
|
||||
{%- endif -%}
|
||||
{%- else -%}
|
||||
<span class=ellipsis>...</span>
|
||||
{%- endif -%}
|
||||
{%- endfor %}
|
||||
</div>
|
||||
{%- endif -%}
|
||||
{%- endmacro -%}
|
||||
|
||||
{% macro render_pagination_prev_next(pagination) %}
|
||||
<div class="pagination">
|
||||
{% if pagination.has_prev %}
|
||||
<a href="{{ pagination.prev|url }}">« Previous</a>
|
||||
{% else %}
|
||||
<span class="disabled">« Previous</span>
|
||||
{% endif %}
|
||||
| {{ pagination.page }} |
|
||||
{% if pagination.has_next %}
|
||||
<a href="{{ pagination.next|url }}">Next »</a>
|
||||
{% else %}
|
||||
<span class="disabled">Next »</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endmacro %}
|
||||
21
src/templates/macros/recipes.html
Normal file
21
src/templates/macros/recipes.html
Normal file
@@ -0,0 +1,21 @@
|
||||
{%- macro render_recipe_list(recipes, limit=0) -%}
|
||||
<div class="tile-grid">
|
||||
{%- for recipe in recipes -%}
|
||||
{%- if limit == 0 or loop.index <= limit -%}
|
||||
{%- set img = recipe.attachments.images|sort(attribute='record_label')|first -%}
|
||||
<a href="{{ recipe|url }}">{#--#}
|
||||
<div class="recipe-tile">
|
||||
<div class="img-placeholder">
|
||||
{%- if img -%}
|
||||
<img class="lozad" width="200" height="150" data-src="{{ img.thumbnail(200, 150)|url }}" />
|
||||
{%- else -%}
|
||||
No Image
|
||||
{%- endif -%}
|
||||
</div>
|
||||
<p>{{ recipe.name }}</p>
|
||||
</div>{#--#}
|
||||
</a>
|
||||
{%- endif -%}
|
||||
{%- endfor %}
|
||||
</div>
|
||||
{%- endmacro -%}
|
||||
10
src/templates/querylist.html
Normal file
10
src/templates/querylist.html
Normal file
@@ -0,0 +1,10 @@
|
||||
{% extends "layout.html" %}
|
||||
{% block title %}{{ this.datamodel.name }}{% endblock %}
|
||||
{% block body %}
|
||||
<h1>{{ this.datamodel.name }}</h1>
|
||||
<ul class="li-lg-space">
|
||||
{%- for cluster in this.children %}
|
||||
<li><a href="{{ cluster|url }}">{{ cluster.name }}</a></li>
|
||||
{%- endfor %}
|
||||
</ul>
|
||||
{% endblock %}
|
||||
63
src/templates/recipe.html
Normal file
63
src/templates/recipe.html
Normal file
@@ -0,0 +1,63 @@
|
||||
{% extends "layout.html" %}
|
||||
{% block title %}{{ this.name }}{% endblock %}
|
||||
{% block body %}
|
||||
<article class="recipe">
|
||||
<section id="img-carousel" class="v-scroll center">
|
||||
{%- for img in this.attachments.images|sort(attribute='record_label') %}
|
||||
<img src="{{ img|url }}" height="400px">
|
||||
{%- endfor %}
|
||||
</section>
|
||||
|
||||
{% if this.source -%}
|
||||
<div id="source" class="small center">
|
||||
<a href="{{ this.source }}">⤳ {{ this.source.host }}</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
<h1>{{ this.name }}</h1>
|
||||
|
||||
<section id="metrics" class="small">
|
||||
<div id="rating" class="xlarge">{{ this.rating|rating }}</div>
|
||||
<div class="difficulty {{this.difficulty}}">
|
||||
<div></div><div></div><div></div>
|
||||
{%- if this.difficulty %}
|
||||
<span>{{ localize('difficulty', this.difficulty) }}</span>
|
||||
{%- else %}
|
||||
<span class="small">{{ localize('difficulty._unset') }}</span>
|
||||
{%- endif %}
|
||||
</div>
|
||||
<div>{{ localize('duration.label') }}: {{ this.time|duration if this.time else '—' }}</div>
|
||||
<div>{{ localize('yield.label') }}: {{ this.yield if this.yield else '—' }}</div>
|
||||
</section>
|
||||
|
||||
<section id="ingredients">
|
||||
<h2>{{ localize('title.ingredients') }}:</h2>
|
||||
<ul class="no-bullets li-lg-space">
|
||||
{%- for ing in this|enumIngredients %}
|
||||
{%- if ing['group'] %}
|
||||
<li class="dark-red bold mrgTopMd">{{ ing['group'] }}</li>
|
||||
{%- else %}
|
||||
<li>
|
||||
{%- if ing['value'] %}{{ ing['value'] }} {% endif -%}
|
||||
{%- if ing['measure'] %}{{ ing['measure'] }} {% endif -%}
|
||||
<span class="light-red">{{ ing['name'] }}</span>
|
||||
{%- if ing['note'] -%}
|
||||
<span class="small italic">{{ ', ' ~ ing['note'] }}</span>
|
||||
{%- endif -%}
|
||||
</li>
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section id="directions">
|
||||
<h2>{{ localize('title.directions') }}:</h2>
|
||||
{% if site.get('settings', alt=this.alt)['replace_temp'] -%}
|
||||
{{ this.directions|string|replace('°C', '℃')|replace('°F', '℉')|markdown }}
|
||||
{% else -%}
|
||||
{{ this.directions }}
|
||||
{% endif -%}
|
||||
</section>
|
||||
|
||||
<div style="clear: both;"></div>
|
||||
</article>
|
||||
{% endblock %}
|
||||
9
src/templates/recipes.html
Normal file
9
src/templates/recipes.html
Normal file
@@ -0,0 +1,9 @@
|
||||
{% extends "layout.html" %}
|
||||
{% from "macros/recipes.html" import render_recipe_list %}
|
||||
{% from "macros/pagination.html" import render_pagination_all %}
|
||||
{% block title %}{{ localize('title.recipes') }}{% endblock %}
|
||||
{% block body %}
|
||||
<h1>{{ localize('title.recipes') }}</h1>
|
||||
{{ render_recipe_list(this.pagination.items) }}
|
||||
{{ render_pagination_all(this.pagination) }}
|
||||
{% endblock %}
|
||||
8
src/templates/root.html
Normal file
8
src/templates/root.html
Normal file
@@ -0,0 +1,8 @@
|
||||
{% extends "layout.html" %}
|
||||
{% from "macros/recipes.html" import render_recipe_list %}
|
||||
{% block body %}
|
||||
<h1>{{ localize('title.latest') }}</h1>
|
||||
<div class="latest">
|
||||
{{ render_recipe_list(site.query('recipes', this.alt) | sort(attribute='date', reverse=True), limit=6) }}
|
||||
</div>
|
||||
{% endblock %}
|
||||
9
src/templates/tag.html
Normal file
9
src/templates/tag.html
Normal file
@@ -0,0 +1,9 @@
|
||||
{% extends "layout.html" %}
|
||||
{% from "macros/recipes.html" import render_recipe_list %}
|
||||
{% from "macros/pagination.html" import render_pagination_all %}
|
||||
{% block title %}{{ this.name }}{% endblock %}
|
||||
{% block body %}
|
||||
<h1>Tag: {{ this.name }}</h1>
|
||||
{{ render_recipe_list(this.pagination.items) }}
|
||||
{{ render_pagination_all(this.pagination) }}
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user