Makefile, AppCache and numerous other changes
14
src/assets/app.webmanifest
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "42 recipes",
|
||||
"display": "standalone",
|
||||
"background_color" : "#EAE9E7",
|
||||
"theme_color": "#DC3A59",
|
||||
"scope": ".",
|
||||
"icons": [{
|
||||
"src": "img/icon-180.png",
|
||||
"sizes": "180x180"
|
||||
},{
|
||||
"src": "img/icon-196.png",
|
||||
"sizes": "196x196"
|
||||
}]
|
||||
}
|
||||
BIN
src/assets/favicon.ico
Normal file
|
After Width: | Height: | Size: 5.3 KiB |
20
src/assets/img/favicon.svg
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="favicon" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 1200 1200" enable-background="new 0 0 1200 1200" xml:space="preserve">
|
||||
<polygon fill="#FFFFFF" points="952.7,1090.5 233.7,1090.5 66.4,907.3 64.1,672.2 233.7,532 233.7,111.5 952.7,111.5 952.7,523
|
||||
1135.8,658.6 1131.3,925.4 "/>
|
||||
<path d="M609.4,1165.1h-19.1c-63.2-2.7-125.5-23.9-176.2-61.5c-91.5,23.5-193,9.9-271.2-44.5C60.1,1004.4,6.8,910,0,811.3v-43.7
|
||||
c7.7-121,88.1-232.7,200.1-278.8c-0.3-118.2,0-315.6-0.1-433.9h800c0,118.2,0,315.7,0,434c112.3,45.7,192.3,157.9,200,278.8v42.6
|
||||
c-5.1,61.5-25.7,122.2-62.9,171.8c-47.7,65.1-120.2,111.4-199.5,126.5c-50.3,10.9-102.5,6-152.2-5.4
|
||||
C735,1141.5,672.4,1162,609.4,1165.1z M656.9,1068.1c93.2-23.3,164-113,165-209c-2.1-24.3,16.4-48.5,41.1-50
|
||||
c23.2-2.6,45.6,15.8,48.2,38.7c0.3,62.4-17.4,124.9-51.5,177.1c97.8,7.8,195.5-52.8,232.5-143.5c29.1-67.5,23.9-148.8-14-211.9
|
||||
c-24.6-43-64.2-76.8-109.5-96.4c-19.2-8.1-38.5-16.2-57.8-24.2c-0.2-106,0-291.1-0.1-397c-207.7,0-415.3,0-623,0
|
||||
c-0.1,105.9,0.2,291-0.2,396.9c-28.3,12.1-57.7,22.2-84.2,37.9C147,620,106,678.2,93.2,742.5c-17,79.5,11,166.4,71.4,220.8
|
||||
c46.7,43.4,111.5,66.4,175.1,62c-28.9-43.7-46.5-95.1-50.6-147.4c-0.1-14.1-2.5-29.3,3.7-42.6c8.4-18.7,30.5-30.1,50.6-25.1
|
||||
c18.9,3.7,33.8,21.3,34.9,40.5c-2.3,68.4,31.1,136.3,85.1,177.9C517,1071.2,590.9,1085.8,656.9,1068.1z"/>
|
||||
<path d="M688.3,316.1L688.3,316.1c-24.6,0-44.7,20-44.7,44.7v222.6c0,24.6,20,44.7,44.7,44.7l0,0c24.6,0,44.7-20,44.7-44.7V360.6
|
||||
C732.9,336.1,713,316.1,688.3,316.1z"/>
|
||||
<path d="M512.3,316.1L512.3,316.1c-24.6,0-44.7,20-44.7,44.7v222.6c0,24.6,20,44.7,44.7,44.7l0,0c24.6,0,44.7-20,44.7-44.7V360.6
|
||||
C557,336.1,537,316.1,512.3,316.1z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
BIN
src/assets/img/icon-180.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
src/assets/img/icon-196.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
src/assets/img/icon-32.png
Normal file
|
After Width: | Height: | Size: 485 B |
36
src/assets/img/icon-glutenfree.svg
Normal file
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="glutenfree" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 1000 1000" style="enable-background:new 0 0 1000 1000;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#66CCCC;}
|
||||
.st1{fill:#FFB903;stroke:#000000;stroke-width:16;stroke-miterlimit:10;}
|
||||
</style>
|
||||
<circle id="back" cx="500" cy="500" r="500"/>
|
||||
<circle id="color" class="st0" cx="500" cy="500" r="400"/>
|
||||
<g id="grain">
|
||||
<path class="st1" d="M628.1,358.7c4.3-42.9,19.3-65.4,29.3-75c45.7-44.3,96.9-65.9,114.1-47.7c17.2,18.2-6.1,68.9-51.8,113.3
|
||||
c-8.3,8.1-29.8,25.2-74.3,26.5"/>
|
||||
<path class="st1" d="M558.5,422.5c-10.5-21.3-19-56.9-18.9-93.8c0.2-63.9,20.6-115.2,45.5-114.8c25,0.4,45,52.3,44.8,116.2
|
||||
c-0.1,15.5-1.3,30.4-3.5,43.9"/>
|
||||
|
||||
<ellipse transform="matrix(9.121996e-03 -1 1 9.121996e-03 248.7508 1087.4275)" class="st1" cx="673.1" cy="418.2" rx="45.1" ry="115.6"/>
|
||||
<path class="st1" d="M541.2,461.9c14.9-2.6,31.5-3.8,49-3.7c63.9,0.8,115.1,21.5,114.6,46.4c-0.6,24.9-52.8,44.5-116.6,43.8
|
||||
c-46.7-0.5-88.2-12.9-106.1-28.6"/>
|
||||
|
||||
<ellipse transform="matrix(1.272804e-03 -1 1 1.272804e-03 85.9093 914.6296)" class="st1" cx="500.9" cy="414.3" rx="115.6" ry="45.1"/>
|
||||
<path class="st1" d="M390.1,590.6c-10.5-21.3-19-56.9-18.9-93.8c0.2-63.9,20.6-115.2,45.5-114.8c25,0.4,45,52.3,44.8,116.2
|
||||
c-0.1,15.5-1.3,30.4-3.5,43.9"/>
|
||||
|
||||
<ellipse transform="matrix(9.121996e-03 -1 1 9.121996e-03 -86.1381 1085.9659)" class="st1" cx="504.9" cy="586.4" rx="45.1" ry="115.6"/>
|
||||
<path class="st1" d="M372.7,630c14.9-2.6,31.5-3.8,49-3.7c63.9,0.8,115.1,21.5,114.6,46.4c-0.6,24.9-52.8,44.5-116.6,43.8
|
||||
c-46.7-0.5-88.2-12.9-106.1-28.6"/>
|
||||
|
||||
<ellipse transform="matrix(1.272804e-03 -1 1 1.272804e-03 -250.5945 913.9741)" class="st1" cx="332.3" cy="582.4" rx="115.6" ry="45.1"/>
|
||||
</g>
|
||||
<path id="stick" d="M236.3,777.6l-10.7-10.7c-11.8-11.8-15.1-27.9-7.4-35.6l77.8-77.8l53.7,53.7L271.9,785
|
||||
C264.1,792.8,248.1,789.4,236.3,777.6z"/>
|
||||
<g id="cross">
|
||||
<polygon points="123.2,175.1 65.8,257 876.8,824.9 934.2,743 "/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
42
src/assets/img/icon-glutenfree_old.svg
Normal file
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="glutenfree" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 1000 1000" style="enable-background:new 0 0 1000 1000;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#66CCCC;}
|
||||
.st1{fill:#E8BB00;stroke:#000000;stroke-width:16;stroke-miterlimit:10;}
|
||||
</style>
|
||||
<circle id="back" cx="500" cy="500" r="500"/>
|
||||
<circle id="color" class="st0" cx="500" cy="500" r="400"/>
|
||||
<g id="grain">
|
||||
<path class="st1" d="M491.7,256.5c-19.8-24.2-23.6-43.4-23.4-53.4c0.7-46.2,15.9-83.4,34-82.9s32.2,38.4,31.5,84.6
|
||||
c-0.1,8.4-2.4,28.2-24.5,51.7"/>
|
||||
<path class="st1" d="M488.7,324.8c-16.3-5.5-38.9-19.4-57.8-38.4c-32.6-32.8-48.5-69.6-35.5-82.2c13-12.6,49.9,3.8,82.5,36.6
|
||||
c7.9,8,14.9,16.2,20.7,24.3"/>
|
||||
|
||||
<ellipse transform="matrix(0.7135 -0.7006 0.7006 0.7135 -28.7219 457.6503)" class="st1" cx="545.3" cy="263.9" rx="83.8" ry="32.7"/>
|
||||
<path class="st1" d="M500.1,353.9c6.3-9,14.2-18.1,23.2-27c33.1-32.3,70-48,82.5-34.9s-4.2,49.9-37.3,82.2
|
||||
c-24.2,23.7-51.8,38.6-69.1,39.7"/>
|
||||
<ellipse transform="matrix(0.708 -0.7062 0.7062 0.708 -114.4428 423.593)" class="st1" cx="455" cy="350.2" rx="32.7" ry="83.8"/>
|
||||
<path class="st1" d="M488.6,497.3c-16.3-5.5-38.9-19.4-57.8-38.4c-32.6-32.8-48.5-69.6-35.5-82.2c13-12.6,49.9,3.8,82.5,36.6
|
||||
c7.9,8,14.9,16.2,20.7,24.3"/>
|
||||
|
||||
<ellipse transform="matrix(0.7135 -0.7006 0.7006 0.7135 -149.5211 507.0457)" class="st1" cx="545.3" cy="436.4" rx="83.8" ry="32.7"/>
|
||||
<path class="st1" d="M499.9,526.4c6.3-9,14.2-18.1,23.2-27c33.1-32.3,70-48,82.5-34.9s-4.2,49.9-37.3,82.2
|
||||
c-24.2,23.7-51.8,38.6-69.1,39.7"/>
|
||||
|
||||
<ellipse transform="matrix(0.708 -0.7062 0.7062 0.708 -236.4149 473.8377)" class="st1" cx="454.8" cy="522.8" rx="32.7" ry="83.8"/>
|
||||
<path class="st1" d="M487.9,670.7c-16.3-5.5-38.9-19.4-57.8-38.4c-32.6-32.8-48.5-69.6-35.5-82.2s49.9,3.8,82.5,36.6
|
||||
c7.9,8,14.9,16.2,20.7,24.3"/>
|
||||
|
||||
<ellipse transform="matrix(0.7135 -0.7006 0.7006 0.7135 -271.2175 556.2379)" class="st1" cx="544.6" cy="609.8" rx="83.8" ry="32.7"/>
|
||||
<path class="st1" d="M499.2,699.8c6.3-9,14.2-18.1,23.2-27c33.1-32.3,70-48,82.5-34.9s-4.2,49.9-37.3,82.2
|
||||
c-24.2,23.7-51.8,38.6-69.1,39.7"/>
|
||||
|
||||
<ellipse transform="matrix(0.708 -0.7062 0.7062 0.708 -358.9856 523.9573)" class="st1" cx="454.1" cy="696.1" rx="32.7" ry="83.8"/>
|
||||
</g>
|
||||
<path id="stick" d="M505,884h-10c-11,0-20-9-20-20V754h50v110C525,875,516,884,505,884z"/>
|
||||
<g id="cross">
|
||||
<polygon points="123.2,175.1 65.8,257 876.8,824.9 934.2,743 "/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
19
src/assets/img/icon-raw.svg
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="raw" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 1000 1000" style="enable-background:new 0 0 1000 1000;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#993300;}
|
||||
.st1{fill:#FFD3C0;}
|
||||
</style>
|
||||
<circle id="back" cx="500" cy="500" r="500"/>
|
||||
<circle id="color" class="st0" cx="500" cy="500" r="400"/>
|
||||
<g id="RAW">
|
||||
<polygon id="w" class="st1" points="787,350 771.2,500 742.7,500 747.4,455 677.9,455 682.7,500 654.1,500 638.4,350 578,350
|
||||
609.6,650 669.9,650 660.4,560 689,560 693.7,605 731.8,605 736.5,560 764.9,560 755.4,650 815.8,650 847.3,350 "/>
|
||||
<path id="a" class="st1" d="M566,560L566,560l-6.3-60l0,0l-9.5-90l0,0l-6.3-60l0,0l0,0h-60.3l0,0h-16.7l0,0h-60.3l0,0l0,0l-6.3,60
|
||||
l0,0l-9.5,90l0,0l-6.3,60l0,0l-9.5,90h60.3l9.5-90h60.8l9.5,90h60.3L566,560z M451.2,500l9.5-90H490l9.5,90H451.2z"/>
|
||||
<path id="r" class="st1" d="M169.9,350l-0.1,301.1L230,650v-90l0,0c62.5,0,60,0,60,45v45h60v-45c0-45,0-75-45-75c45,0,45-30,45-75
|
||||
c0-105,0-105-120-105C212.5,350,169.9,350,169.9,350z M230,500c0-16,0-75.3,0-90c60,0,60,0,60,45S290,500,230,500z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
15
src/assets/img/icon-vegan.svg
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="vegan" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 1000 1000" style="enable-background:new 0 0 1000 1000;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#41A748;}
|
||||
</style>
|
||||
<circle id="back" cx="500" cy="500" r="500"/>
|
||||
<circle id="color" class="st0" cx="500" cy="500" r="400"/>
|
||||
<path id="v" d="M433.3,676.4c-32.9-86.9-65.2-170.8-133.3-236.9c-38.8-37.6-104-94.5-161-101.6c81.9,69.1,150.2,154.8,198.3,250.8
|
||||
c45.8,91.5,81.9,210,81.9,313.2h97.5c-0.8-73.5,10.6-149.3,36.4-218.2c18.7-50,44.9-111.8,86.1-147.6c46-1.7,99.3-5.3,124.2-49.5
|
||||
c18.9-33.7,13.2-72.6,23.4-108.5c10-35.2,34.6-60.8,50.2-93c-21.6,21.9-53.1,44.4-82.1,55.5c-27.2,10.4-57.3,6.6-85.7,10.2
|
||||
c-97.9,12.4-119.4,96.8-91.4,181.2c16.9-79.3,45.6-123,130.3-130.3c-8.4,30.5-47.8,55-67.7,77.7c-31.8,36.3-60.1,66-86.9,115.9
|
||||
c-26.8,50-32.7,75.3-54.4,108.9C482.5,728.3,449.2,726.1,433.3,676.4z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
8
src/assets/img/icon-yield.svg
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="fork_knife" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 1000 1000" style="enable-background:new 0 0 1000 1000;" xml:space="preserve">
|
||||
<path id="path" d="M210.3,0l-26.8,366.6c-9.8,53.6,118.8,100.8,116.6,155.4l-6.6,411.4c-2.6,66.6,56.6,66.6,56.6,66.6
|
||||
s59.2,0,56.6-66.6L400.4,522c-2-54.6,115.6-100.8,116.6-155.4L490.4,0H457l-3.4,306.6l-70,13.4L367,0h-33.4l-16.6,320l-70-13.4
|
||||
L243.5,0H210.3z M817,0C655,0,627,166,627,500c0,58,56.8,66.6,84,66.6l-5.4,366.6c-6,66.4,60.6,66.6,60.6,66.6s50.6,0,50.6-66.6"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 730 B |
@@ -1,10 +0,0 @@
|
||||
(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
@@ -1,9 +0,0 @@
|
||||
/*! 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}}});
|
||||
39
src/assets/static/script.js
Normal file
@@ -0,0 +1,39 @@
|
||||
(function(){// main entry
|
||||
handleAppCache();
|
||||
updateViewport();
|
||||
})();
|
||||
function updateViewport() {// 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);
|
||||
}
|
||||
}
|
||||
function handleAppCache() {// update cache status icon
|
||||
var cache = window.applicationCache;
|
||||
if (cache) {
|
||||
cache.addEventListener('updateready', update);
|
||||
cache.addEventListener('cached', ready); // initial
|
||||
cache.addEventListener('noupdate', ready); // consecutive
|
||||
cache.addEventListener('downloading', busy);
|
||||
cache.addEventListener('obsolete', failed);
|
||||
if(cache.status===cache.UPDATEREADY){update()}
|
||||
if(cache.status===cache.IDLE){window.onload=(event)=>{ready()};}
|
||||
function update(){ready(); cache.swapCache(); window.location.reload()}
|
||||
function busy(){document.getElementById('cache-status').style='background:darkorange'}
|
||||
function ready(){document.getElementById('cache-status').style='background:forestgreen'}
|
||||
function failed(){document.getElementById('cache-status').style='background:red'}
|
||||
}
|
||||
}
|
||||
/*! 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}}});
|
||||
@@ -12,11 +12,22 @@
|
||||
.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 }
|
||||
.v-scroll { overflow-y: hidden; 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 }
|
||||
|
||||
i.icon:before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
vertical-align: bottom;
|
||||
width: 1em; height: 1em
|
||||
}
|
||||
i.icon.gf:before {background-image: url('../img/icon-glutenfree.svg')}
|
||||
i.icon.raw:before {background-image: url('../img/icon-raw.svg')}
|
||||
i.icon.veg:before {background-image: url('../img/icon-vegan.svg')}
|
||||
i.icon.yield:before {background-image: url('../img/icon-yield.svg.svg')}
|
||||
|
||||
/*
|
||||
* General
|
||||
*/
|
||||
@@ -24,11 +35,15 @@ 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;
|
||||
background: var(--cBg3); color: var(--cTxt);
|
||||
}
|
||||
header #logo { font-size: 42px; display: block; margin-bottom: 15px }
|
||||
#logo { font-size: 42px; display: block; margin-bottom: 15px }
|
||||
#cache-status {
|
||||
position: absolute; right: 10px; top: 10px;
|
||||
border-radius: 50%; width: 10px; height: 10px;
|
||||
}
|
||||
header { position: relative }
|
||||
header a { color: var(--cTxt) }
|
||||
header, h1 { text-align: center }
|
||||
header, footer, .page {
|
||||
@@ -43,33 +58,32 @@ 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 screen and (max-width: 485px) { body { font-size: 1.4em } }
|
||||
@media print {
|
||||
header, footer { display: none }
|
||||
body, .page { background-color: #FFF }
|
||||
body, .page { background: #FFF }
|
||||
}
|
||||
|
||||
/*
|
||||
* Components
|
||||
* Parts & Components
|
||||
*/
|
||||
.tags { display: flex; flex-wrap: wrap; justify-content: center }
|
||||
.tags > * {
|
||||
background-color: #FFF;
|
||||
background: #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;
|
||||
.tags a:hover, .tags .active, a:hover .recipe-tile, .recipe-tile .hover .time {
|
||||
background: 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) {
|
||||
@media(max-width: 32em) {
|
||||
.cluster dd { margin-left: 0 }
|
||||
.cluster dd a { white-space: unset }
|
||||
}
|
||||
@@ -77,25 +91,31 @@ header .tags { max-width: 600px; margin: 0 auto }
|
||||
/*
|
||||
* Grid overview
|
||||
*/
|
||||
.pagination { text-align: center; margin-top: 1em; }
|
||||
.pagination { text-align: center; margin-top: 1em }
|
||||
.recipe-tile {
|
||||
background-color: var(--cBg2);
|
||||
color: var(--cTxt);
|
||||
background: 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;
|
||||
.recipe-tile .placeholder {
|
||||
font: bold 25px/150px 'Courier New', monospace;
|
||||
background: #777; color: var(--cBg2);
|
||||
}
|
||||
a:hover .recipe-tile img { mix-blend-mode: overlay }
|
||||
.recipe-tile p { height: 2.6em; margin: 0.3em 10px; overflow-y: auto }
|
||||
.recipe-tile p { height: 2.5em; margin: 0.3em 10px; overflow-y: auto }
|
||||
.recipe-tile img, .recipe-tile .overlay { display: block; width: 200px; height: 150px }
|
||||
.recipe-tile .overlay { position: absolute }
|
||||
.recipe-tile .icon-bar { position: absolute; bottom: 3px; right: 3px }
|
||||
.recipe-tile .icon-bar i.icon { margin-left: 2px; font-size: 28px }
|
||||
a:hover .recipe-tile .hover { display: block; background: #0006 }
|
||||
.recipe-tile .hover { display: none; height: 100% }
|
||||
.recipe-tile .hover .time {
|
||||
position: relative; top: -1.25em;
|
||||
font: bold 1.1em/1.3em monospace;
|
||||
}
|
||||
/* snap to column grid */
|
||||
.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); */
|
||||
@@ -107,9 +127,9 @@ a:hover .recipe-tile img { mix-blend-mode: overlay }
|
||||
@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 }
|
||||
.recipe-tile .overlay { display: none }
|
||||
.recipe-tile, a:hover .recipe-tile, .recipe-tile .placeholder {
|
||||
background: #FFF; color: #000 }
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -120,16 +140,19 @@ a:hover .recipe-tile img { mix-blend-mode: overlay }
|
||||
.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 #ingredients { float: left; margin: 0 30px 15px 0; max-width: 300px }
|
||||
.recipe #ingredients a { line-height: 1em }
|
||||
.recipe #directions ul { list-style-type: circle }
|
||||
.recipe #directions dl dt { color: var(--cRed2); font-weight: bold }
|
||||
.recipe #directions dl dd { margin-bottom: 1em }
|
||||
/* Colored, 3-part, difficulty bar */
|
||||
.difficulty.easy > div:nth-child(1) { background-color: #3C3 }
|
||||
.difficulty.easy > div:nth-child(1) { background: #3C3 }
|
||||
.difficulty.medium > div:nth-child(1),
|
||||
.difficulty.medium > div:nth-child(2) { background-color: #FC3 }
|
||||
.difficulty.medium > div:nth-child(2) { background: #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.hard > div:nth-child(3) { background: #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 {
|
||||
@@ -138,16 +161,26 @@ a:hover .recipe-tile img { mix-blend-mode: overlay }
|
||||
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 screen and (max-width: 50em) {
|
||||
.recipe h1 { margin-bottom: 0 }
|
||||
.recipe #img-carousel { height: calc(75vw - 2*50px) }
|
||||
.recipe #img-carousel img { height: 100%; max-width: 100%; padding: 0 }
|
||||
.recipe #metrics { float: unset; max-width: max-content; margin: .5em auto 2em }
|
||||
.recipe #metrics > *:not(:first-child) { margin-right: -100vw; max-width: 50vw }
|
||||
}
|
||||
@media screen and (max-width: 40em), print and (orientation: portrait) {
|
||||
.recipe #ingredients { float: unset; max-width: 100% }
|
||||
}
|
||||
@media screen and (max-width: 32em) {
|
||||
.recipe #img-carousel { padding: 0 10px; height: calc(75vw - 2*10px) }
|
||||
}
|
||||
@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 {
|
||||
h1 { margin-top:0 }
|
||||
#source, #rating, .difficulty, #img-carousel { 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 }
|
||||
}
|
||||
}*/
|
||||
|
||||
2
src/configs/force-update.ini
Normal file
@@ -0,0 +1,2 @@
|
||||
enabled = yes
|
||||
endswith = .appcache
|
||||
3
src/content/app.appcache/contents.lr
Normal file
@@ -0,0 +1,3 @@
|
||||
_template: cache.manifest
|
||||
---
|
||||
_model: none
|
||||
@@ -10,4 +10,4 @@ xdata:
|
||||
120
|
||||
180
|
||||
360
|
||||
9999
|
||||
1440
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
_model: recipes
|
||||
@@ -1,16 +0,0 @@
|
||||
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.
|
||||
@@ -1,26 +0,0 @@
|
||||
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
|
||||
@@ -1,11 +0,0 @@
|
||||
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
|
||||
|
Before Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 70 KiB |
|
Before Width: | Height: | Size: 41 KiB |
@@ -1 +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
|
||||
measures: EL TL kg g L dl cl ml cm Msp Prise Tasse Tassen Dose Dosen kleine große Bund Packung Packungen Scheibe Scheiben Schuss Stängel Tropfen Tube
|
||||
@@ -1 +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
|
||||
measures: kg g L dl cl ml oz lb pt qt cm inch tsp tbsp c cup cups pkg pkgs drop drops tube dash dashes ounce ounces small medium large box can tin pinch pinches clove cloves stick sticks bunch handful splash splashes stem stems slice slices
|
||||
@@ -5,5 +5,3 @@ _hidden: yes
|
||||
replace_frac: yes
|
||||
---
|
||||
replace_temp: yes
|
||||
---
|
||||
show_empty_tags: no
|
||||
|
||||
@@ -1 +1 @@
|
||||
name: Brot
|
||||
name: Brot
|
||||
@@ -1 +1 @@
|
||||
name: Bread
|
||||
name: Bread
|
||||
@@ -1 +1 @@
|
||||
name: Kuchen
|
||||
name: Kuchen
|
||||
@@ -1 +1 @@
|
||||
name: Cake
|
||||
name: Cake
|
||||
1
src/content/tags/cheese/contents+de.lr
Normal file
@@ -0,0 +1 @@
|
||||
name: Käse
|
||||
1
src/content/tags/cheese/contents.lr
Normal file
@@ -0,0 +1 @@
|
||||
name: Cheese
|
||||
@@ -1 +0,0 @@
|
||||
name: Schokolade
|
||||
@@ -1 +0,0 @@
|
||||
name: Chocolate
|
||||
@@ -1 +1 @@
|
||||
name: Kekse
|
||||
name: Keks
|
||||
@@ -1 +1 @@
|
||||
name: Cookies
|
||||
name: Cookie
|
||||
1
src/content/tags/crust/contents+de.lr
Normal file
@@ -0,0 +1 @@
|
||||
name: Kuchenboden
|
||||
1
src/content/tags/crust/contents.lr
Normal file
@@ -0,0 +1 @@
|
||||
name: Crust
|
||||
@@ -1 +1 @@
|
||||
name: Dip
|
||||
name: Dip
|
||||
@@ -1 +0,0 @@
|
||||
name: Dressing
|
||||
@@ -1 +1 @@
|
||||
name: Drinks
|
||||
name: Drink
|
||||
@@ -1 +1 @@
|
||||
name: Glutenfrei
|
||||
name: Glutenfrei
|
||||
@@ -1 +1 @@
|
||||
name: Glutenfree
|
||||
name: Gluten-free
|
||||
@@ -1 +1 @@
|
||||
name: Zutat
|
||||
name: Zutat
|
||||
@@ -1 +1 @@
|
||||
name: Ingredient
|
||||
name: Ingredient
|
||||
@@ -1 +1 @@
|
||||
name: Hauptspeise
|
||||
name: Hauptspeise
|
||||
@@ -1 +1 @@
|
||||
name: Main dish
|
||||
name: Main dish
|
||||
1
src/content/tags/muffins/contents.lr
Normal file
@@ -0,0 +1 @@
|
||||
name: Muffin
|
||||
@@ -1 +1 @@
|
||||
name: Raw
|
||||
name: Raw
|
||||
@@ -1 +1 @@
|
||||
name: Salat
|
||||
name: Salat
|
||||
@@ -1 +1 @@
|
||||
name: Salad
|
||||
name: Salad
|
||||
@@ -1 +0,0 @@
|
||||
name: Soße
|
||||
@@ -1 +0,0 @@
|
||||
name: Sauce
|
||||
1
src/content/tags/savory/contents+de.lr
Normal file
@@ -0,0 +1 @@
|
||||
name: Herzhaft
|
||||
1
src/content/tags/savory/contents.lr
Normal file
@@ -0,0 +1 @@
|
||||
name: Savory
|
||||
@@ -1 +1 @@
|
||||
name: Aufstrich
|
||||
name: Aufstrich
|
||||
@@ -1 +1 @@
|
||||
name: Spread
|
||||
name: Spread
|
||||
@@ -1 +1 @@
|
||||
name: Süßes
|
||||
name: Süß
|
||||
@@ -1 +1 @@
|
||||
name: Sweet
|
||||
name: Sweet
|
||||
@@ -1 +1 @@
|
||||
name: Weihnachten
|
||||
name: Weihnachten
|
||||
@@ -1 +1 @@
|
||||
name: Xmas
|
||||
name: Xmas
|
||||
@@ -1,11 +1,5 @@
|
||||
[duration]
|
||||
label = Zeit
|
||||
day = Tag
|
||||
days = Tage
|
||||
hour = Std
|
||||
hours = Std
|
||||
min = Min
|
||||
mins = Min
|
||||
|
||||
[yield]
|
||||
label = Menge
|
||||
@@ -17,6 +11,9 @@ easy = Einfach
|
||||
medium = Mittel
|
||||
hard = Schwer
|
||||
|
||||
[ingredients]
|
||||
recipeLink = ⤳Rezept
|
||||
|
||||
[title]
|
||||
latest = Zuletzt hinzugefügt
|
||||
all_recipes = Alle Rezepte
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
[duration]
|
||||
label = Time
|
||||
day = day
|
||||
days = days
|
||||
hour = hour
|
||||
hours = hours
|
||||
min = minutes
|
||||
mins = min
|
||||
|
||||
[yield]
|
||||
label = Yield
|
||||
@@ -17,6 +11,9 @@ easy = Easy
|
||||
medium = Medium
|
||||
hard = Hard
|
||||
|
||||
[ingredients]
|
||||
recipeLink = ⤳recipe
|
||||
|
||||
[title]
|
||||
latest = Latest recipes
|
||||
all_recipes = All recipes
|
||||
|
||||
@@ -21,9 +21,8 @@ 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
|
||||
type = integer
|
||||
addon_label = min
|
||||
|
||||
[fields.difficulty]
|
||||
label = Difficulty
|
||||
|
||||
@@ -12,10 +12,10 @@ enabled = no
|
||||
|
||||
[fields.measures]
|
||||
label = Measures
|
||||
description = Comma separated list
|
||||
description = Space 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
|
||||
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
|
||||
@@ -26,9 +26,3 @@ type = boolean
|
||||
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
|
||||
|
||||
1
src/packages/force-update
Submodule
@@ -1,9 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from lektor.pluginsystem import Plugin
|
||||
from lektor.pluginsystem import Plugin, get_plugin
|
||||
from lektor.databags import Databags
|
||||
from markupsafe import Markup
|
||||
from datetime import datetime
|
||||
import unicodedata
|
||||
import os
|
||||
import shutil
|
||||
|
||||
# -------
|
||||
# Sorting
|
||||
@@ -39,20 +39,17 @@ def noUmlaut(text):
|
||||
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]
|
||||
i = ['1/2', '1/3', '2/3', '1/4', '3/4', '1/8'].index(x)
|
||||
res += [u'½', u'⅓', u'⅔', u'¼', u'¾', u'⅛'][i]
|
||||
except ValueError:
|
||||
res += ' ' + x
|
||||
if x in u'-–—':
|
||||
res += u' - '
|
||||
else:
|
||||
res += ' ' + x
|
||||
return res.lstrip()
|
||||
|
||||
|
||||
@@ -77,24 +74,6 @@ def updateSet_if(dic, parent, parentkey, value):
|
||||
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
|
||||
|
||||
@@ -105,13 +84,14 @@ def splitIngredientLine(line):
|
||||
indices = [0, len(line)]
|
||||
for i, char in enumerate(line):
|
||||
if char.isspace():
|
||||
capture = False
|
||||
indices[state] = i
|
||||
state += 1
|
||||
if capture:
|
||||
capture = False
|
||||
indices[state] = i
|
||||
state += 1
|
||||
continue
|
||||
elif capture:
|
||||
continue
|
||||
elif state == 1 and char in '0123456789-.,':
|
||||
elif state == 1 and char in u'0123456789-–—.,':
|
||||
state -= 1
|
||||
elif state > 1:
|
||||
break
|
||||
@@ -127,6 +107,9 @@ def parseIngredientLine(line, measureList=[], rep_frac=False):
|
||||
measure = line[idx[0]:idx[1]].lstrip()
|
||||
if measure.lower() in measureList:
|
||||
name = line[idx[1]:].lstrip()
|
||||
# if name.startswith('of '):
|
||||
# measure += ' of'
|
||||
# name = name[3:]
|
||||
else:
|
||||
measure = ''
|
||||
name = line[idx[0]:].lstrip()
|
||||
@@ -136,19 +119,18 @@ def parseIngredientLine(line, measureList=[], rep_frac=False):
|
||||
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))
|
||||
def replace_atref_urls(text, label=None):
|
||||
if '@' not in text:
|
||||
return text
|
||||
result = list()
|
||||
for x in text.split():
|
||||
if x[0] == '@':
|
||||
x = x[1:]
|
||||
result.append(u'<a href="{}">{}</a>'.format(x, label or x))
|
||||
else:
|
||||
result.append(x)
|
||||
return Markup(' '.join(result))
|
||||
|
||||
# ----------------
|
||||
# Main entry point
|
||||
@@ -157,68 +139,56 @@ def groupByMergeCluster(dic, arr=[30, 60, 120], reverse=False):
|
||||
class HelperPlugin(Plugin):
|
||||
name = u'Helper'
|
||||
description = u'Some helper methods, filters, and templates.'
|
||||
alt = None
|
||||
availableTags = set()
|
||||
buildTime = None
|
||||
settings = dict()
|
||||
translations = dict()
|
||||
|
||||
# -----------
|
||||
# Event hooks
|
||||
# -----------
|
||||
|
||||
def on_before_build_all(self, builder, **extra):
|
||||
# display only tags that contain at least one recipe
|
||||
def processCLI(self, extra_flags):
|
||||
useCache = bool(extra_flags.get('ENABLE_APPCACHE'))
|
||||
plugin = get_plugin('force-update', self.env)
|
||||
if plugin.enabled and not useCache:
|
||||
plugin.enabled = False
|
||||
print('AppCache: ' + ('ENABLED' if useCache else 'DISABLED'))
|
||||
self.env.jinja_env.globals['ENABLE_APPCACHE'] = useCache
|
||||
|
||||
def processSettings(self):
|
||||
bag = Databags(self.env)
|
||||
pad = self.env.new_pad()
|
||||
for r in pad.query('recipes'):
|
||||
self.availableTags.update(r['tags'])
|
||||
for alt in self.env.load_config().iter_alternatives():
|
||||
set = pad.get('settings', alt=alt)
|
||||
self.translations[alt] = bag.lookup('i18n+' + alt)
|
||||
self.settings[alt] = {
|
||||
'measures': set['measures'].lower().split(),
|
||||
'replFrac': set['replace_frac']
|
||||
}
|
||||
|
||||
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_before_build_all(self, builder, **extra):
|
||||
build_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
print('Build time: ' + build_time)
|
||||
self.env.jinja_env.globals['DATE_NOW'] = build_time
|
||||
# update project settings once per build
|
||||
self.processCLI(getattr(builder, 'extra_flags'))
|
||||
self.processSettings()
|
||||
|
||||
def on_process_template_context(self, context, **extra):
|
||||
self.alt = context['alt']
|
||||
# def on_process_template_context(self, context, **extra):
|
||||
# pass
|
||||
|
||||
def on_setup_env(self, **extra):
|
||||
# self.env.load_config().iter_alternatives()
|
||||
# pad = self.env.new_pad()
|
||||
# pad.query('groupby', alt=alt)
|
||||
def localizeDic(alt, partA, partB=None):
|
||||
if alt not in self.translations:
|
||||
raise RuntimeError(
|
||||
'localize() expects first parameter to be an alternate')
|
||||
if partB is None:
|
||||
partA, partB = partA.split('.', 1)
|
||||
return self.translations[alt][partA][partB]
|
||||
|
||||
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']
|
||||
def ingredientsForRecipe(recipe, alt='en'):
|
||||
meaList = self.settings[alt]['measures']
|
||||
repFrac = self.settings[alt]['replFrac']
|
||||
|
||||
for line in recipe['ingredients']:
|
||||
line = line.strip()
|
||||
@@ -229,23 +199,21 @@ class HelperPlugin(Plugin):
|
||||
else:
|
||||
yield parseIngredientLine(line, meaList, repFrac)
|
||||
|
||||
def groupByAttribute(recipeList, attribute):
|
||||
def groupByAttribute(recipeList, attribute, alt='en'):
|
||||
groups = dict()
|
||||
for recipe in recipeList:
|
||||
if attribute == 'ingredients':
|
||||
for ing in ingredientsForRecipe(recipe):
|
||||
for ing in ingredientsForRecipe(recipe, alt):
|
||||
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['replaceAtRefURLs'] = replace_atref_urls
|
||||
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
|
||||
|
||||
5
src/packages/time-duration/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
dist
|
||||
build
|
||||
*.pyc
|
||||
*.pyo
|
||||
*.egg-info
|
||||
4
src/packages/time-duration/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# Time Duration
|
||||
|
||||
This plugin converts integer numbers to a human readable duration.
|
||||
E.g. 90 -> 1 hour 30 minutes
|
||||
86
src/packages/time-duration/lektor_time_duration.py
Normal file
@@ -0,0 +1,86 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from lektor.pluginsystem import Plugin
|
||||
|
||||
durationLocale = {
|
||||
'de': {'day': 'Tag', 'hour': 'Std', 'min': 'Min',
|
||||
'days': 'Tage', 'hours': 'Std', 'mins': 'Min'},
|
||||
'en': {'day': 'day', 'hour': 'hour', 'min': 'min',
|
||||
'days': 'days', 'hours': 'hours', 'mins': 'min'}
|
||||
}
|
||||
|
||||
# -----------
|
||||
# Single Time
|
||||
|
||||
|
||||
def pluralize(n, single, multi):
|
||||
if n == 0:
|
||||
return ''
|
||||
return u'{} {}'.format(n, single if n == 1 else multi)
|
||||
|
||||
|
||||
def to_duration(time, alt='en'):
|
||||
time = int(time) if time else 0
|
||||
if (time <= 0):
|
||||
return ''
|
||||
days = time // (60 * 24)
|
||||
time -= days * (60 * 24)
|
||||
L = durationLocale[alt]
|
||||
return ' '.join([
|
||||
pluralize(days, L['day'], L['days']),
|
||||
pluralize(time // 60, L['hour'], L['hours']),
|
||||
pluralize(time % 60, L['min'], L['mins'])]).strip()
|
||||
|
||||
# ------------
|
||||
# Time Cluster
|
||||
|
||||
|
||||
def to_time_in_cluster(time, cluster, alt='en'):
|
||||
for idx, x in enumerate(cluster):
|
||||
x = int(x)
|
||||
if x == time:
|
||||
if idx == 0:
|
||||
timeB = to_duration(time, alt)
|
||||
return '<' + timeB
|
||||
else:
|
||||
timeA = to_duration(cluster[idx - 1], alt)
|
||||
timeB = to_duration(time - 1, alt)
|
||||
return u'{} – {}'.format(timeA, timeB)
|
||||
else:
|
||||
return '>' + to_duration(cluster[-1], alt)
|
||||
|
||||
|
||||
def find_in_cluster(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
|
||||
else:
|
||||
key = clusterList[-1] + 1
|
||||
return key
|
||||
|
||||
|
||||
def group_by_time_cluster(dic, arr=[30, 60, 120], reverse=False):
|
||||
arr = sorted([int(x) for x in arr])
|
||||
groups = dict()
|
||||
for key, recipes in dic:
|
||||
key = find_in_cluster(key, arr)
|
||||
if key == 0 and not reverse:
|
||||
key = ''
|
||||
try:
|
||||
groups[key]
|
||||
except KeyError:
|
||||
groups[key] = set()
|
||||
groups[key].update(recipes)
|
||||
return sorted(groups.items(), reverse=bool(reverse))
|
||||
|
||||
|
||||
class TimeDurationPlugin(Plugin):
|
||||
name = u'Time Duration'
|
||||
description = u'Convert int to duration. E.g., 90 -> "1hr 30min".'
|
||||
|
||||
def on_setup_env(self, **extra):
|
||||
self.env.jinja_env.filters['duration'] = to_duration
|
||||
self.env.jinja_env.filters['durationCluster'] = to_time_in_cluster
|
||||
self.env.jinja_env.filters['groupTimeCluster'] = group_by_time_cluster
|
||||
38
src/packages/time-duration/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_time_duration.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-time-duration',
|
||||
packages=find_packages(),
|
||||
py_modules=['lektor_time_duration'],
|
||||
# url='[link to your repository]',
|
||||
version='0.1',
|
||||
classifiers=[
|
||||
'Framework :: Lektor',
|
||||
'Environment :: Plugins',
|
||||
],
|
||||
entry_points={
|
||||
'lektor.plugins': [
|
||||
'time-duration = lektor_time_duration:TimeDurationPlugin',
|
||||
]
|
||||
}
|
||||
)
|
||||
54
src/templates/cache.manifest
Normal file
@@ -0,0 +1,54 @@
|
||||
CACHE MANIFEST
|
||||
# Date build: {{ DATE_NOW }}
|
||||
|
||||
{%- macro _print_(items) -%}
|
||||
{%- for item in items -%}
|
||||
{{ item|replace('../', '', 1) }}
|
||||
{% endfor -%}
|
||||
{%- endmacro -%}
|
||||
|
||||
{%- macro _add_(list, item) -%}
|
||||
{{- list.append(item|url) or pass -}}
|
||||
{%- endmacro -%}
|
||||
|
||||
{%- set root = site.get('/', this.alt) -%}
|
||||
|
||||
{%- set assetList = [] -%}
|
||||
{%- for asset in root.pad.asset_root.children recursive -%}
|
||||
{%- if asset.__class__.__name__ != 'Directory' -%}
|
||||
{{- _add_(assetList, asset) -}}
|
||||
{%- endif -%}
|
||||
{{- loop(asset.children) -}}
|
||||
{%- endfor -%}
|
||||
|
||||
{%- set cacheList = [] -%}
|
||||
{{- _add_(cacheList, root) -}}
|
||||
{%- for x in root.children if x != this recursive -%}
|
||||
{{- _add_(cacheList, x) -}}
|
||||
|
||||
{%- set pg = x.datamodel.pagination_config -%}
|
||||
{%- if pg.enabled -%}
|
||||
{%- for page in range(2, pg.count_pages(x) + 1) -%}
|
||||
{{- _add_(cacheList, pg.get_record_for_page(x, page)) -}}
|
||||
{%- endfor -%}
|
||||
{%- endif -%}
|
||||
|
||||
{% set img = x.attachments.images|sort(attribute='record_label')|first -%}
|
||||
{%- if img -%}
|
||||
{{- _add_(cacheList, img.thumbnail(200, 150, 'crop')) -}}
|
||||
{%- endif -%}
|
||||
|
||||
{%- if x.datamodel.has_own_children -%}
|
||||
{{- loop(x.children) -}}
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
|
||||
{#- Generate cache file index #}
|
||||
# static
|
||||
{{ _print_(assetList) -}}
|
||||
# index
|
||||
{{ _print_(cacheList) -}}
|
||||
|
||||
{#- All other requests are forwarded #}
|
||||
NETWORK:
|
||||
*
|
||||
@@ -8,10 +8,10 @@
|
||||
{%- set sortType = this.xdata + [''] -%}
|
||||
{%- endif -%}
|
||||
|
||||
{%- set all = site.query('/recipes', this.alt) | groupByAttribute(this.group_key) | groupSort(sortType, this.reverse_order) -%}
|
||||
{%- set all = site.query('/recipes', this.alt) | groupByAttribute(this.group_key, this.alt) | groupSort(sortType, this.reverse_order) -%}
|
||||
|
||||
{%- if this.group_key == 'time' -%}
|
||||
{%- set all = all | groupMergeCluster(this.xdata, this.reverse_order) -%}
|
||||
{%- set all = all | groupTimeCluster(this.xdata, this.reverse_order) -%}
|
||||
{%- endif -%}
|
||||
|
||||
<h1>{{ this.name }}</h1>
|
||||
@@ -22,9 +22,9 @@
|
||||
{%- elif not attrib -%}
|
||||
{{ this.null_fallback }}
|
||||
{%- elif this.group_key == 'time' -%}
|
||||
{{ attrib | duration(this.xdata) }}
|
||||
{{ attrib | durationCluster(this.xdata, this.alt) }}
|
||||
{%- elif this.group_key == 'difficulty' -%}
|
||||
{{ localize('difficulty', attrib) }}
|
||||
{{ localize(this.alt, 'difficulty', attrib) }}
|
||||
{%- else -%}
|
||||
{{ attrib }}
|
||||
{%- endif -%}</dt>
|
||||
|
||||
@@ -1,31 +1,39 @@
|
||||
<!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>
|
||||
{%- if ENABLE_APPCACHE %}
|
||||
<html manifest="{{ site.get('app.appcache', alt=this.alt)|url }}">
|
||||
{% endif -%}
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=0.75">
|
||||
<script type="text/javascript" src="{{ '/static/script.js'|url }}"></script>
|
||||
<link rel="stylesheet" href="{{ '/static/style.css'|url }}">
|
||||
<link rel="icon" sizes="32x32" href="{{ '/img/icon-32.png'|url }}">
|
||||
<link rel="icon" sizes="196x196" href="{{ '/img/icon-196.png'|url }}">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="{{ '/img/icon-180.png'|url }}">
|
||||
<link rel="manifest" href="{{ '/app.webmanifest'|asseturl }}">
|
||||
<title>{% block title %}Welcome{% endblock %} · recipe lekture</title>
|
||||
</head>
|
||||
<body> {#- ontouchstart="" #}
|
||||
<header>
|
||||
<a id="logo" href="{{ site.get('/', alt=this.alt)|url }}">recipe lekture</a>
|
||||
{%- if ENABLE_APPCACHE %}
|
||||
<i id="cache-status" title="cache status"></i>
|
||||
{%- endif %}
|
||||
<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>
|
||||
<li><a {% if this == allRecipes %}class="active"{% endif %} href="{{ allRecipes|url }}">{{ localize(this.alt, '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>
|
||||
|
||||
@@ -5,13 +5,24 @@
|
||||
{%- 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 -%}
|
||||
|
||||
{#- overlay on hover and always-visible icons #}
|
||||
<div class="overlay">
|
||||
<div class="hover"><div class="time">{{ recipe.time|duration(recipe.alt) }}</div></div>
|
||||
<div class="icon-bar">
|
||||
{%- if 'raw' in recipe.tags -%}<i class="icon raw"></i>{%- endif -%}
|
||||
{%- if 'glutenfree' in recipe.tags -%}<i class="icon gf"></i>{%- endif -%}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#- show image or placeholder text #}
|
||||
{% if img -%}
|
||||
<img class="lozad" data-src="{{ img.thumbnail(200, 150, 'crop')|url }}">
|
||||
{%- else -%}
|
||||
<div class="placeholder">No Image</div>
|
||||
{%- endif -%}
|
||||
|
||||
{#- recipe title #}
|
||||
<p>{{ recipe.name }}</p>
|
||||
</div>{#--#}
|
||||
</a>
|
||||
|
||||
@@ -2,37 +2,42 @@
|
||||
{% block title %}{{ this.name }}{% endblock %}
|
||||
{% block body %}
|
||||
<article class="recipe">
|
||||
<!-- date added: {{ this.date }} -->
|
||||
<section id="img-carousel" class="v-scroll center">
|
||||
{%- for img in this.attachments.images|sort(attribute='record_label') %}
|
||||
<img src="{{ img|url }}" height="400px">
|
||||
<img class="lozad" data-src="{{ img|url }}" height="400">
|
||||
{%- endfor %}
|
||||
</section>
|
||||
|
||||
{% if this.source -%}
|
||||
<div id="source" class="small center">
|
||||
<a href="{{ this.source }}">⤳ {{ this.source.host }}</a>
|
||||
{%- if this.source.host -%}
|
||||
<a href="{{ this.source }}">⤳ {{ this.source.host }}</a>
|
||||
{%- else -%}
|
||||
⤳ {{ this.source }}
|
||||
{%- endif -%}
|
||||
</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>
|
||||
<span>{{ localize(this.alt, 'difficulty', this.difficulty) }}</span>
|
||||
{%- else %}
|
||||
<span class="small">{{ localize('difficulty._unset') }}</span>
|
||||
<span class="small">{{ localize(this.alt, '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>
|
||||
<div>{{ localize(this.alt, 'duration.label') }}: {{ this.time|duration(this.alt) if this.time else '—' }}</div>
|
||||
<div>{{ localize(this.alt, 'yield.label') }}: {{ this.yield or '—' }}</div>
|
||||
</section>
|
||||
|
||||
<section id="ingredients">
|
||||
<h2>{{ localize('title.ingredients') }}:</h2>
|
||||
<h2>{{ localize(this.alt, 'title.ingredients') }}:</h2>
|
||||
<ul class="no-bullets li-lg-space">
|
||||
{%- for ing in this|enumIngredients %}
|
||||
{%- for ing in this|enumIngredients(this.alt) %}
|
||||
{%- if ing['group'] %}
|
||||
<li class="dark-red bold mrgTopMd">{{ ing['group'] }}</li>
|
||||
{%- else %}
|
||||
@@ -41,7 +46,7 @@
|
||||
{%- if ing['measure'] %}{{ ing['measure'] }} {% endif -%}
|
||||
<span class="light-red">{{ ing['name'] }}</span>
|
||||
{%- if ing['note'] -%}
|
||||
<span class="small italic">{{ ', ' ~ ing['note'] }}</span>
|
||||
<span class="small italic">{{ ', ' ~ ing['note'] | replaceAtRefURLs(label=localize(this.alt, 'ingredients.recipeLink')) }}</span>
|
||||
{%- endif -%}
|
||||
</li>
|
||||
{%- endif %}
|
||||
@@ -50,9 +55,9 @@
|
||||
</section>
|
||||
|
||||
<section id="directions">
|
||||
<h2>{{ localize('title.directions') }}:</h2>
|
||||
<h2>{{ localize(this.alt, 'title.directions') }}:</h2>
|
||||
{% if site.get('settings', alt=this.alt)['replace_temp'] -%}
|
||||
{{ this.directions|string|replace('°C', '℃')|replace('°F', '℉')|markdown }}
|
||||
{{ this.directions.html|replace('°C', '℃')|replace('°F', '℉') }}
|
||||
{% else -%}
|
||||
{{ this.directions }}
|
||||
{% endif -%}
|
||||
|
||||
@@ -1,9 +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 title %}{{ localize(this.alt, 'title.recipes') }}{% endblock %}
|
||||
{% block body %}
|
||||
<h1>{{ localize('title.recipes') }}</h1>
|
||||
<h1>{{ localize(this.alt, 'title.recipes') }}</h1>
|
||||
{{ render_recipe_list(this.pagination.items) }}
|
||||
{{ render_pagination_all(this.pagination) }}
|
||||
{% endblock %}
|
||||
@@ -1,7 +1,7 @@
|
||||
{% extends "layout.html" %}
|
||||
{% from "macros/recipes.html" import render_recipe_list %}
|
||||
{% block body %}
|
||||
<h1>{{ localize('title.latest') }}</h1>
|
||||
<h1>{{ localize(this.alt, 'title.latest') }}</h1>
|
||||
<div class="latest">
|
||||
{{ render_recipe_list(site.query('recipes', this.alt) | sort(attribute='date', reverse=True), limit=6) }}
|
||||
</div>
|
||||
|
||||