From 64d15d90491dc2eebd62e31e5f34a54b0629058e Mon Sep 17 00:00:00 2001 From: Nicholas Jitkoff Date: Tue, 16 Aug 2022 21:47:10 -0700 Subject: [PATCH] Updated recipe rendering --- docs/bitty.js | 3 +- docs/js/qrious.min.js | 6 +++ docs/render.js | 18 +++---- docs/render/recipe.css | 117 ++++++++++++++++++++++++++--------------- docs/render/recipe.js | 86 +++++++++++++++++++++--------- 5 files changed, 153 insertions(+), 77 deletions(-) create mode 100644 docs/js/qrious.min.js diff --git a/docs/bitty.js b/docs/bitty.js index 59c5e6c..3923e54 100644 --- a/docs/bitty.js +++ b/docs/bitty.js @@ -113,6 +113,7 @@ class DataURL { let rawData = this.encoding ? await base64ToByteArray(this.data) : stringToByteArray(this.data); let compressedData = await compressData(rawData, format); var base64String = dataToBase64(compressedData); + base64String = base64String.replace(/=+$/, ""); this.data = base64String; this.params.format = format; return this; @@ -420,7 +421,7 @@ function encodePrettyComponent(s) { let replacements = {' - ': '---', '-': '--', ' ' : '-'} let re = new RegExp('(' + Object.keys(replacements).join('|') + ')', 'g'); return encodeURIComponent(s.replace(re, e => replacements[e] ?? '-')) - .replace(/\%2C/g, ",") + // .replace(/\%2C/g, ",") .replace(/[!'()*]/g, (c) => '%' + c.charCodeAt(0).toString(16)); } diff --git a/docs/js/qrious.min.js b/docs/js/qrious.min.js new file mode 100644 index 0000000..5735ea6 --- /dev/null +++ b/docs/js/qrious.min.js @@ -0,0 +1,6 @@ +/*! QRious v4.0.2 | (C) 2017 Alasdair Mercer | GPL v3 License +Based on jsqrencode | (C) 2010 tz@execpc.com | GPL v3 License +*/ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):t.QRious=e()}(this,function(){"use strict";function t(t,e){var n;return"function"==typeof Object.create?n=Object.create(t):(s.prototype=t,n=new s,s.prototype=null),e&&i(!0,n,e),n}function e(e,n,s,r){var o=this;return"string"!=typeof e&&(r=s,s=n,n=e,e=null),"function"!=typeof n&&(r=s,s=n,n=function(){return o.apply(this,arguments)}),i(!1,n,o,r),n.prototype=t(o.prototype,s),n.prototype.constructor=n,n.class_=e||o.class_,n.super_=o,n}function i(t,e,i){for(var n,s,a=0,h=(i=o.call(arguments,2)).length;a>1&1,n=0;n0;e--)n[e]=n[e]?n[e-1]^_.EXPONENT[v._modN(_.LOG[n[e]]+t)]:n[e-1];n[0]=_.EXPONENT[v._modN(_.LOG[n[0]]+t)]}for(t=0;t<=i;t++)n[t]=_.LOG[n[t]]},_checkBadness:function(){var t,e,i,n,s,r=0,o=this._badness,a=this.buffer,h=this.width;for(s=0;sh*h;)u-=h*h,c++;for(r+=c*v.N4,n=0;n=o-2&&(t=o-2,s>9&&t--);var a=t;if(s>9){for(r[a+2]=0,r[a+3]=0;a--;)e=r[a],r[a+3]|=255&e<<4,r[a+2]=e>>4;r[2]|=255&t<<4,r[1]=t>>4,r[0]=64|t>>12}else{for(r[a+1]=0,r[a+2]=0;a--;)e=r[a],r[a+2]|=255&e<<4,r[a+1]=e>>4;r[1]|=255&t<<4,r[0]=64|t>>4}for(a=t+3-(s<10);a=5&&(i+=v.N1+n[e]-5);for(e=3;et||3*n[e-3]>=4*n[e]||3*n[e+3]>=4*n[e])&&(i+=v.N3);return i},_finish:function(){this._stringBuffer=this.buffer.slice();var t,e,i=0,n=3e4;for(e=0;e<8&&(this._applyMask(e),(t=this._checkBadness())>=1)1&n&&(s[r-1-e+8*r]=1,e<6?s[8+r*e]=1:s[8+r*(e+1)]=1);for(e=0;e<7;e++,n>>=1)1&n&&(s[8+r*(r-7+e)]=1,e?s[6-e+8*r]=1:s[7+8*r]=1)},_interleaveBlocks:function(){var t,e,i=this._dataBlock,n=this._ecc,s=this._eccBlock,r=0,o=this._calculateMaxLength(),a=this._neccBlock1,h=this._neccBlock2,f=this._stringBuffer;for(t=0;t1)for(t=u.BLOCK[n],i=s-7;;){for(e=s-7;e>t-3&&(this._addAlignment(e,i),!(e6)for(t=d.BLOCK[r-7],e=17,i=0;i<6;i++)for(n=0;n<3;n++,e--)1&(e>11?r>>e-12:t>>e)?(s[5-i+o*(2-n+o-11)]=1,s[2-n+o-11+o*(5-i)]=1):(this._setMask(5-i,2-n+o-11),this._setMask(2-n+o-11,5-i))},_isMasked:function(t,e){var i=v._getMaskBit(t,e);return 1===this._mask[i]},_pack:function(){var t,e,i,n=1,s=1,r=this.width,o=r-1,a=r-1,h=(this._dataBlock+this._eccBlock)*(this._neccBlock1+this._neccBlock2)+this._neccBlock2;for(e=0;ee&&(i=t,t=e,e=i),i=e,i+=e*e,i>>=1,i+=t},_modN:function(t){for(;t>=255;)t=((t-=255)>>8)+(255&t);return t},N1:3,N2:3,N3:40,N4:10}),p=v,m=f.extend({draw:function(){this.element.src=this.qrious.toDataURL()},reset:function(){this.element.src=""},resize:function(){var t=this.element;t.width=t.height=this.qrious.size}}),g=h.extend(function(t,e,i,n){this.name=t,this.modifiable=Boolean(e),this.defaultValue=i,this._valueTransformer=n},{transform:function(t){var e=this._valueTransformer;return"function"==typeof e?e(t,this):t}}),k=h.extend(null,{abs:function(t){return null!=t?Math.abs(t):null},hasOwn:function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},noop:function(){},toUpperCase:function(t){return null!=t?t.toUpperCase():null}}),w=h.extend(function(t){this.options={},t.forEach(function(t){this.options[t.name]=t},this)},{exists:function(t){return null!=this.options[t]},get:function(t,e){return w._get(this.options[t],e)},getAll:function(t){var e,i=this.options,n={};for(e in i)k.hasOwn(i,e)&&(n[e]=w._get(i[e],t));return n},init:function(t,e,i){"function"!=typeof i&&(i=k.noop);var n,s;for(n in this.options)k.hasOwn(this.options,n)&&(s=this.options[n],w._set(s,s.defaultValue,e),w._createAccessor(s,e,i));this._setAll(t,e,!0)},set:function(t,e,i){return this._set(t,e,i)},setAll:function(t,e){return this._setAll(t,e)},_set:function(t,e,i,n){var s=this.options[t];if(!s)throw new Error("Invalid option: "+t);if(!s.modifiable&&!n)throw new Error("Option cannot be modified: "+t);return w._set(s,e,i)},_setAll:function(t,e,i){if(!t)return!1;var n,s=!1;for(n in t)k.hasOwn(t,n)&&this._set(n,t[n],e,i)&&(s=!0);return s}},{_createAccessor:function(t,e,i){var n={get:function(){return w._get(t,e)}};t.modifiable&&(n.set=function(n){w._set(t,n,e)&&i(n,t)}),Object.defineProperty(e,t.name,n)},_get:function(t,e){return e["_"+t.name]},_set:function(t,e,i){var n="_"+t.name,s=i[n],r=t.transform(null!=e?e:t.defaultValue);return i[n]=r,r!==s}}),M=w,b=h.extend(function(){this._services={}},{getService:function(t){var e=this._services[t];if(!e)throw new Error("Service is not being managed with name: "+t);return e},setService:function(t,e){if(this._services[t])throw new Error("Service is already managed with name: "+t);e&&(this._services[t]=e)}}),B=new M([new g("background",!0,"white"),new g("backgroundAlpha",!0,1,k.abs),new g("element"),new g("foreground",!0,"black"),new g("foregroundAlpha",!0,1,k.abs),new g("level",!0,"L",k.toUpperCase),new g("mime",!0,"image/png"),new g("padding",!0,null,k.abs),new g("size",!0,100,k.abs),new g("value",!0,"")]),y=new b,O=h.extend(function(t){B.init(t,this,this.update.bind(this));var e=B.get("element",this),i=y.getService("element"),n=e&&i.isCanvas(e)?e:i.createCanvas(),s=e&&i.isImage(e)?e:i.createImage();this._canvasRenderer=new c(this,n,!0),this._imageRenderer=new m(this,s,s===e),this.update()},{get:function(){return B.getAll(this)},set:function(t){B.setAll(t,this)&&this.update()},toDataURL:function(t){return this.canvas.toDataURL(t||this.mime)},update:function(){var t=new p({level:this.level,value:this.value});this._canvasRenderer.render(t),this._imageRenderer.render(t)}},{use:function(t){y.setService(t.getName(),t)}});Object.defineProperties(O.prototype,{canvas:{get:function(){return this._canvasRenderer.getElement()}},image:{get:function(){return this._imageRenderer.getElement()}}});var A=O,L=h.extend({getName:function(){}}).extend({createCanvas:function(){},createImage:function(){},getName:function(){return"element"},isCanvas:function(t){},isImage:function(t){}}).extend({createCanvas:function(){return document.createElement("canvas")},createImage:function(){return document.createElement("img")},isCanvas:function(t){return t instanceof HTMLCanvasElement},isImage:function(t){return t instanceof HTMLImageElement}});return A.use(new L),A}); + +//# sourceMappingURL=qrious.min.js.map \ No newline at end of file diff --git a/docs/render.js b/docs/render.js index dcac286..6c89613 100644 --- a/docs/render.js +++ b/docs/render.js @@ -38,15 +38,6 @@ const el = (selector, ...args) => { }; window.el = el; -function loadScript(src, callback, type = "module") { - let script = el("script", { src, type }); - script.addEventListener('load', function(e) { - console.debug("Loaded script", src); - if (callback) callback(e); - }); - document.head.appendChild(script); -} - function async(u, c) { var d = document, t = 'script', o = d.createElement(t), @@ -56,11 +47,18 @@ function async(u, c) { s.parentNode.insertBefore(o, s); } +function loadScript(src, callback, type = "module") { + let promise = new Promise((resolve, reject) => { + document.head.appendChild(el("script", { src, type, onload:resolve})); + }) + return callback ? promise.then(callback) : promise; +} + function loadSyle(href, callback) { let promise = new Promise((resolve, reject) => { document.head.appendChild(el("link", { type: "text/css", rel: "stylesheet", href, onload:resolve})); }) - return callback ? promise.then(callback) : promise; + return callback ? promise.then(callback) : promise; } diff --git a/docs/render/recipe.css b/docs/render/recipe.css index dd93a43..64ab707 100644 --- a/docs/render/recipe.css +++ b/docs/render/recipe.css @@ -53,10 +53,10 @@ body { text-decoration-style: dotted; box-sizing: border-box; /* color: var(--background-color); */ - - border-bottom:1px solid transparent; + /* border-bottom:1px solid transparent; */ border-bottom-color: currentColor; /* background-color: var(--text-color); */ + font-weight: bold; } .timer:hover, @@ -85,6 +85,11 @@ body { .timer.active .countdown { display:inline-block; } + .timer svg { + width: 1em; + vertical-align: sub; + margin-bottom: 1px; + } } @@ -92,6 +97,9 @@ body { .timer { font-weight:800; } + .timer svg { + display:none; + } } img.publisher { @@ -173,8 +181,12 @@ ul.step:after { height: 0.5px; background-color: currentColor; opacity: 1; - border:none; - margin: 1em 0 0.2em 0 ; + border: + none; + margin: 1em 0 0.2em 0; + print-color-adjust: exact; + -webkit-print-color-adjust: exact; + /* margin-left: -19em; */ } ul.step.header:after, @@ -200,8 +212,10 @@ span.number { /* line-height: 16px; */ opacity: 1; user-select: none; - color:var(--accent-color) - /* background: black; */ + color: + var(--accent-color) + /* background: black; */; + display: none; } @media screen { @@ -282,7 +296,7 @@ li:hover { } */ .time:before { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='22' fill='none' viewBox='0 0 18 22'%3E%3Cpath fill='%23000' d='M4.031 17.953C5.406 19.328 7.063 20.016 9 20.016c1.938 0 3.578-.688 4.922-2.063 1.375-1.375 2.062-3.031 2.062-4.969 0-1.937-.687-3.578-2.062-4.921C12.578 6.687 10.937 6 9 6c-1.938 0-3.594.688-4.969 2.063-1.343 1.343-2.015 2.984-2.015 4.921 0 1.938.671 3.594 2.015 4.97zm12-10.547c.532.688.985 1.578 1.36 2.672.406 1.063.609 2.031.609 2.906 0 2.47-.875 4.594-2.625 6.375-1.75 1.75-3.875 2.625-6.375 2.625s-4.625-.875-6.375-2.625C.875 17.58 0 15.453 0 12.984c0-2.468.875-4.578 2.625-6.328C4.375 4.875 6.5 3.984 9 3.984c.844 0 1.813.204 2.906.61C13.031 5 13.937 5.469 14.625 6l1.406-1.453c.5.406.969.875 1.407 1.406L16.03 7.406zm-8.015 6.61v-6h1.968v6H8.016zM12 .984V3H6V.984h6z'/%3E%3C/svg%3E%0A"); -} */ +} .time { background-size:18px; @@ -301,6 +315,7 @@ li:hover { /* color: rgba(0,0,0,0.8); */ /* color: rgb(0, 118, 173); */ + max-width: 16em; } .noun { @@ -317,7 +332,7 @@ li:hover .noun { white-space: pre-wrap; /* line-height: 125%; */ /* font-size: 100%; */ - flex: 0 1 65%; + flex: 1 1 65%; /* margin-top: -0.7em; */ } @@ -328,8 +343,8 @@ li:hover .noun { } .instructions.numbered li { - padding-left: 3rem; - margin-left: 0; + /* padding-left: 3rem; */ + /* margin-left: 0; */ } .substep { @@ -362,6 +377,8 @@ li:hover .noun { min-width: 3rem; text-align: right; + color: var(--accent-color); + /* font-weight: bold; */ } .ingredient:hover { @@ -512,44 +529,19 @@ hr { } -@media print { - body { - font-size: 14px; - min-height:100vh; - } - .print-hide { - display: none !important; - } - - @page { - size: auto; /* auto is the initial value */ - /* margin: 1cm; */ - } - - a[href]:after { content: none !important; } - img[src]:after { content: none !important; } -} - -@media not print { - body { - color:var(--text-color); - } - .print-show { - display:none; - } -} - .instructions.numbered hr {/* margin: 0.7em -1px; *//* height: 1.5px; *//* opacity: 0.2; *//* width: auto; */} .instructions.numbered {/* margin-top: -1.6em; */} img.qr { - margin-top:2em; - position:absolute; + /* margin-top:2em; */ + position: + absolute; bottom:0; - right:0; - width:2in; + left: 0; + max-width: 1in; + height: auto; } @media screen and (max-width: 1199px) { @@ -561,6 +553,7 @@ img.qr { .recipe-content { max-width:50em; padding: 1em 0; + /* padding-left: 2em; */ } @media screen and (min-width: 1200px) { body { @@ -745,4 +738,44 @@ a.publisherlink { span.timer.active.expired { background-color: rgb(221, 40, 0); -} \ No newline at end of file +} + +@media print { + body { + font-size: 14px; + min-height:100vh; + margin: 0; + padding: 0; + } + .print-hide { + display: none !important; + } + + .recipe { + margin:0; + max-width:unset; + } + .recipe-content { + max-width: + unset; + padding-left: 2em; + } + + @page { + size: auto; /* auto is the initial value */ + /* margin: 1cm; */ + } + + a[href]:after { content: none !important; } + img[src]:after { content: none !important; } +} + +@media not print { + body { + color:var(--text-color); + } + .print-show { + display:none; + } +} + diff --git a/docs/render/recipe.js b/docs/render/recipe.js index f73aa24..0cf8435 100644 --- a/docs/render/recipe.js +++ b/docs/render/recipe.js @@ -24,7 +24,7 @@ let FRACTION_MAP = { } let ignoredTerms = [ - "fresh", "out", "not", "sprig", "sprigs", "room", "temperature", "still", "see", "notes", "with", "beat", "together", "crust", "very", "cold", "hot", "top", "warm", "one", "note", "teaspoon", "teaspoons", "tablespoon", "tablespoons", "cup", "cups", "taste", "more", "melted", "into", "wide", "pound", "pounds", "gram", "grams", "you", "ounce", "ounces", "thinly", "sliced", + "broken", "pieces", "tbsp", "tsp", "peeled", "then", "can", "oz", "fresh", "out", "not", "sprig", "sprigs", "room", "temperature", "still", "see", "notes", "with", "beat", "together", "crust", "very", "cold", "hot", "top", "warm", "one", "note", "teaspoon", "teaspoons", "tablespoon", "tablespoons", "cup", "cups", "taste", "more", "melted", "into", "wide", "pound", "pounds", "gram", "grams", "you", "ounce", "ounces", "thinly", "sliced", "pan", "cube", "cubes", "finely", "ground", "garnish", "about", "cut", "and", "smashed", "each", "the", "medium", "large", "small", "for", "chopped", "minced", "grated", "box", "softened", "directed", "shredded", "cooked", "from", "frozen", "thawed" ] let emojiMap = { @@ -315,7 +315,7 @@ function highlightTimes(string) { return string.replace(pattern, (match, t1, t2, tt, offset, groups) => { // console.log("match", t1, t2, tt) - return `${match}` + return `${icons.timer}${match}` }); } @@ -411,7 +411,8 @@ function render() { image = image?.url || image; let instructions = json.recipeInstructions; let title = clean(json.name); - let description = clean(json.description?.replace(/\\n/g, "
")) + let description = json.description || json.articleBody; + description = clean(description?.replace(/\\n/g, "
")) let favicon = faviconForTitle(title) || "🍴"; parent.postMessage({title:title, favicon:favicon, image:image, description:description, wakeLock:true, updateURL:true}, "*"); description = description?.split("\n").join("

") @@ -461,26 +462,49 @@ function render() { m("span.substep",{innerHTML:highlightTimes(highlightTerms(FRACTION_MAP.replace(text.trim()), terms))})) } - function stepsFromText(text) { - try { - let steps = [] - let components = text.split(new RegExp("(?<=[\.\!\?]|[\.\!\?]\)|\))\s+(?!\()")); - let append = false; - while (components.length) { - let step = components.shift() - if (append) { - steps.push(steps.pop() + " " + step) - } else { - steps.push(step); - } - if (step.indexOf("(") >= 0) { append = true; } - if (step.includes(")")) { append = false; } + function stepsFromText(text) { + let abbreviations = ["tsp", "tbsp", "oz", "lb", "lbs"]; + let steps = []; + let i = 0; + let start = 0; + let depth = 0; + let lastSpace = 0; + for (let i = 0; i < text.length; i++) { + switch (text[i]) { + case "(": depth++; break; + case ")": depth--; break; + case " ": lastSpace = i; break; + case ".": + case "!": + case "?": + let lastWord = text.substring(lastSpace, i).trim(); + lastWord = lastWord.match(/\w+$/)?.pop().toLowerCase(); + if (abbreviations.includes(lastWord)) { continue; } + let end; + if (text[i+1] == ")" && text[i+2] == " ") { + end = i + 2; + } else if (text[i+1] == " " && text[i+2] != "(" && depth == 0) { + end = i + 1; + } + if (end) { + let string = text.substring(start, end); + steps.push(string); + start = end; + } + break; + case "\n": + steps.push(text.substring(start, i)); + start = i; + default: } - return steps; - } catch (e) { - return text.replace(/(\.\)? )+/g,"$1\n").split("\n") } + + steps.push (text.substring(start)); + console.log("step", steps) + + return steps; + } function flattenInstructions(instruction) { if (instruction.itemListElement) { @@ -532,8 +556,11 @@ function render() { let thumbnail = document.querySelector("#thumbnail"); let thumbnailContainer = document.querySelector("#thumbnail-container"); thumbnail.style.backgroundImage = 'url("' + bgImg.src + '")'; - thumbnail.style.filter = `blur(${thumbnailContainer.offsetHeight / bgImg.naturalHeight * 1}px)`; - // thumbnail.style.transform = `scale(1.1)`; + + let blur = 1; + blur = Math.max(blur, Math.max(thumbnailContainer.offsetHeight * window.devicePixelRatio / bgImg.naturalHeight, thumbnailContainer.offsetWidth * window.devicePixelRatio / bgImg.naturalWidth)) + if (blur > 1) thumbnail.style.filter = `blur(${blur/2}px)`; + setTimeout(() => thumbnail.style.opacity = 1.0, 0); if (window.scrollY == 0) setTimeout(() => { const yOffset = -20; @@ -597,10 +624,11 @@ function render() { ), m(".columns", - m("section.ingredients", + m("section.ingredients.hanging", m("caption.ingredients-title", {onclick:(e) => {e.target.closest("section").classList.toggle("hanging")}},"Ingredients"), ingredients, - qrImage ? m("img.qr.print-show", {src:qrImage}) : null + qrImage ? m("img#qr.qr.print-show", {src:qrImage}) : null, + // m("canvas#qr") ), m(reformat ? "section.instructions.numbered" : "section.instructions", m("caption.ingredients-title", {onclick:() => {reformat = !reformat; render(); return false;}}, @@ -642,4 +670,14 @@ function keepAwake() { var path = window.script.substring(0, window.script.lastIndexOf(".")); var cssURL = path + ".css"; +loadScript(path + '/../../js/qrious.min.js', null, "").then(() => { + console.log("qrious loaded"); + var qr = new QRious({ + element: document.getElementById("qr"), + background: 'transparent', + foreground: 'currentColor', + size: 512, + value: params.originalUrl, + }); +}) loadSyle(cssURL).then(render); \ No newline at end of file