commit: d18c3c5a0e0f87701469ca55b4f26a29050f5889
parent ba6f16a696c0b241751ed8ffba76cabf440e1b6e
Author: Лu Лinveгa <aliceffekt@gmail.com>
Date: Fri, 28 Jun 2019 13:54:53 +0900
Merge pull request #14 from microlith57/parent-ingredients
Add parent-child relationships
Diffstat:
33 files changed, 317 insertions(+), 36 deletions(-)
diff --git a/index.html b/index.html
@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=800, initial-scale=1.0">
- <meta name="description" content="Grim Grains is an illustrated food blog, it features plant-based (vegan) recipes with a strong attention to colour and form.">
+ <meta name="description" content="Grimgrains is an illustrated food blog, it features plant-based (vegan) recipes with a strong attention to colour and form.">
<!-- Twitter -->
<meta name="twitter:card" content="summary">
<meta name="twitter:site" content="@RekkaBell">
@@ -40,6 +40,7 @@
<script src="scripts/templates/page.js"></script>
<script src="scripts/templates/home.js"></script>
<script src="scripts/templates/search.js"></script>
+ <script src="scripts/templates/service.js"></script>
<link rel="stylesheet" type="text/css" href="links/reset.css"/>
<link rel="stylesheet" type="text/css" href="links/main.css"/>
diff --git a/media/ingredients/beans.png b/media/ingredients/beans.png
Binary files differ.
diff --git a/media/ingredients/beets.png b/media/ingredients/beets.png
Binary files differ.
diff --git a/media/ingredients/black.beans.png b/media/ingredients/black.beans.png
Binary files differ.
diff --git a/media/ingredients/black.mushrooms.png b/media/ingredients/black.mushrooms.png
Binary files differ.
diff --git a/media/ingredients/cauliflower.png b/media/ingredients/cauliflower.png
Binary files differ.
diff --git a/media/ingredients/cayenne.powder.png b/media/ingredients/cayenne.powder.png
Binary files differ.
diff --git a/media/ingredients/cucumber.png b/media/ingredients/cucumber.png
Binary files differ.
diff --git a/media/ingredients/flour.png b/media/ingredients/flour.png
Binary files differ.
diff --git a/media/ingredients/ginger.png b/media/ingredients/ginger.png
Binary files differ.
diff --git a/media/ingredients/green.olives.png b/media/ingredients/green.olives.png
Binary files differ.
diff --git a/media/ingredients/green.peppers.png b/media/ingredients/green.peppers.png
Binary files differ.
diff --git a/media/ingredients/kidney.beans.png b/media/ingredients/kidney.beans.png
Binary files differ.
diff --git a/media/ingredients/lentils.png b/media/ingredients/lentils.png
Binary files differ.
diff --git a/media/ingredients/mungbeans.png b/media/ingredients/mungbeans.png
Binary files differ.
diff --git a/media/ingredients/mushrooms.png b/media/ingredients/mushrooms.png
Binary files differ.
diff --git a/media/ingredients/nori.png b/media/ingredients/nori.png
Binary files differ.
diff --git a/media/ingredients/olives.png b/media/ingredients/olives.png
Binary files differ.
diff --git a/media/ingredients/onion.png b/media/ingredients/onion.png
Binary files differ.
diff --git a/media/ingredients/peas.png b/media/ingredients/peas.png
Binary files differ.
diff --git a/media/ingredients/peppers.png b/media/ingredients/peppers.png
Binary files differ.
diff --git a/media/ingredients/potatoe.png b/media/ingredients/potato.png
Binary files differ.
diff --git a/media/ingredients/seaweed.png b/media/ingredients/seaweed.png
Binary files differ.
diff --git a/media/ingredients/sesame.seeds.png b/media/ingredients/sesame.seeds.png
Binary files differ.
diff --git a/media/ingredients/soy.beans.png b/media/ingredients/soy.beans.png
Binary files differ.
diff --git a/media/ingredients/straw.mushrooms.png b/media/ingredients/straw.mushrooms.png
Binary files differ.
diff --git a/riven.html b/riven.html
@@ -25,6 +25,7 @@
<script src="scripts/templates/recipe.js"></script>
<script src="scripts/templates/ingredient.js"></script>
<script src="scripts/templates/page.js"></script>
+ <script src="scripts/templates/service.js"></script>
<link rel="stylesheet" type="text/css" href="links/reset.css"/>
<link rel="stylesheet" type="text/css" href="links/riven.fonts.css"/>
diff --git a/scripts/database/ingredients.ndtl b/scripts/database/ingredients.ndtl
@@ -1,6 +1,7 @@
DATABASE.ingredients = `
Coffee
+ PARENT : Beans
BREF : {{Coffee}} is a brewed drink prepared from roasted coffee beans, which are the seeds of berries from the {{Coffea plant|https://en.wikipedia.org/wiki/Coffea}}.
LONG
% blog/coffee.2.jpg
@@ -28,11 +29,13 @@ Lentils
Lentil
Beluga Lentils
+ PARENT : Lentils
COLOR : #000000
TAGS
Legume
Lentil
Brown Lentils
+ PARENT : Lentils
BREF : The most common variety of lentils, found in most grocery stores. They have a mild, earthy-flavor.
TAGS
Legume
@@ -45,6 +48,7 @@ Peanut Butter
Peanuts
BREF : A crop grown mainly for its edible seeds! Peanuts are similar in taste and nutritional profile to tree nuts, and can be made into {{peanut butter}}.
Soy beans
+ PARENT : Beans
COLOR : #EFEFEF
LONG
& A staple in Eastern Asia, soy beans are used as a base for many vegan faux-meat dishes. If you produce your own {{soy milk}}, know that there won't be any waste. The pulp can be used to make okara, which you can use to make baked goods.
@@ -53,13 +57,21 @@ Beans
LONG
& Fun fact: there are over 130 varieties of {{green beans}}.
& See also: {{black beans}}, {{green beans}}, and {{kidney beans}}
+Black beans
+ PARENT : Beans
+Green beans
+ PARENT : Beans
+Kidney beans
+ PARENT : Beans
Edamame
Chickpeas
Chickpea Flour
- BREF : Chickpea flour is made from {{chickpeas}}.
+ PARENT : chickpeas, flour
Peas
Green Peas
+ PARENT : Peas
Mungbeans
+ PARENT : Beans
~ CRUCIFEROUS
@@ -96,28 +108,41 @@ Radish
~ ALGEA FUNGI
+Seaweed
+Mushroom
Wakame
+ PARENT : Seaweed
COLOR : #006633
BREF : Wakame is a very invasive plant - it's even banned in Australia.
Dried Hijiki
+ PARENT : Seaweed
Bull kelp powder
+ PARENT : Seaweed
Nori
+ PARENT : Seaweed
COLOR : #000000
- BREF : Nori can be made into {{sheets|nori sheets}}. Fun fact: seaweed takes about 45 days to grow.
+ BREF : Fun fact: seaweed takes about 45 days to grow.
Nori Sheets
+ PARENT : Nori, Seaweed
Shiitake
+ PARENT : Mushroom
COLOR : #875A2C
Crimini
+ PARENT : Mushroom
COLOR : #875A2C
BREF : Mature crimini mushrooms are actually {{portobello}} mushrooms.
Portobello
+ PARENT : Mushroom
COLOR : #875A2C
- BREF : Portobello are mature {{crimini}} (or common) mushrooms. They can be used in plantbased recipes as hamburger steaks, you can even use them as buns.
+ BREF : Portobello are mature {{crimini}} (or common) mushrooms. They can be used in plant-based recipes as hamburger steaks, you can even use them as buns.
Shimeji
+ PARENT : Mushroom
Button Mushrooms
-Dehydrated mushro
+ PARENT : Mushroom
Black mushrooms
+ PARENT : Mushroom
Straw mushrooms
+ PARENT : Mushroom
~ SEEDS
@@ -136,35 +161,43 @@ Chia seeds
~ ROOT VEGETABLES
Nagaimo
-Carrot
+Carrots
BREF : Overconsumming carrots can cause what is reffered to as "Carotenosis", a condition in which the skin turns orange.
Heirloom Carrots
Ginger
Ginger Root
+ PARENT : Ginger
Potatoes
BREF : Since vitamin A deficiency is a common problem in Africa, people are encouraged to eat {{sweet potatoes}}.
Russet Potatoes
- BREF : A type of {{potato|potatoes}}.
+ PARENT : Potatoes
Sweet Potatoes
- BREF : A type of {{potato|potatoes}}.
+ PARENT : Potatoes
Beets
LONG
& {{Golden beets}} are rich in b-xanthin pigment, so they cannot replace {{red beets}} (which contain betalain pigment). Both pigments offer different health benefits!
& The root of the beet plant is called a beetroot. Don't be alarmed, beetroot juice will make your urine red for a day.
Golden Beets
+ PARENT : Beets
BREF : A type of {{beet|beets}} containing b-xanthin pigment.
Red Beets
+ PARENT : Beets
BREF : A type of {{beet|beets}} containing betalain pigment.
Yuka
BREF : Tapioca is actually a starch extracted from Yuca roots.
Garlic
BREF : Garlic has been used in many cultures around the world for thousands of years, dating all the way back to the time the pyramids were built!
Garlic Powder
+ PARENT : Garlic
+Onion
Red Onion
+ PARENT : Onion
COLOR : #C820B3
- BREF : There is a variety of red onions in italy that has a stronger and sweeter taste, it is sometimes made into marmalades.
+ BREF : There is a variety of red onion in Italy that has a stronger and sweeter taste, and is sometimes made into marmalade.
Onion Powder
+ PARENT : Onion
Yellow Onion
+ PARENT : Onion
~ OTHER VEGETABLES
@@ -177,11 +210,14 @@ Chives
Peppers
BREF : The misleading name 'pepper' was given by Christopher Columbus when he brought back a plant to Europe. The word pepper was given to all spices in Europe that had a hot and pungent taste.
Green Peppers
+ PARENT : Peppers
Red Peppers
+ PARENT : Peppers
Yellow Peppers
+ PARENT : Peppers
Tomato
- BREF : The tomatoes can also be dried with an oven.
Tomato Paste
+ PARENT : Tomato
BREF : The tomatoes can also be dried with an oven.
Avocado
BREF : Avocados are botanically 'berries', they may be pear-shaped, round or egg-shaped. They are a good source of fat. Fun fact: Avocado trees don't self-pollinate, they need another avocado tree nearby to bear fruit.
@@ -189,7 +225,11 @@ Pumpkin
BREF : The darker the skin of the pumpkin, the higher the beta-carotene content.
Olives
Green Olives
+ PARENT : Olives
Black Olives
+ PARENT : Olives
+Pimento Olives
+ PARENT : Olives
Palm
BREF : Heart of palm is a vegetable that is harvested from the inner core of certain palm trees. They can be eaten as is, but they're especially delicious when tossed into a salad.
Squash
@@ -208,6 +248,7 @@ Alfaalfa sprouts
Raisins
Dried Raisins
+ PARENT : Raisins
Blackberries
Cherries
Mulberries
@@ -249,19 +290,22 @@ Persimmon
Dates
BREF : Date palms have been around for at least 50 million years!
Deglet Noor Dates
+ PARENT : Dates
+Date caramel
+ PARENT : Dates
+ BREF : Date caramel is used to make {{salted caramel carob chip cookies}}, and has a recipe there.
Pamplemousse
Rhubarb
Apricot Jam
Starfruit
Mixed fruits
Coconut
-Date
Plums
Pineapple
~ SPICES
-Aonori
+Ao nori
Kanten powder
BREF : A seaweed-based gelling agent derived from tengusa, a specific type of red seaweed. Kanten confections don't dissolve at room temperature, unlike gelatin.
Turmeric
@@ -284,10 +328,13 @@ Mint
COLOR : #006633
BREF : Oil derived from {{fresh mint}} can be used as a friendly insecticide.
Fresh Mint
+ PARENT : Mint
Cocoa
Cocoa Powder
-Cayenne
+ PARENT : Cocoa
Cayenne Pepper
+Cayenne Powder
+ PARENT : Cayenne Pepper
Anise
Anise Seeds
Nutritional yeast
@@ -366,18 +413,26 @@ Fenugreek
~ WHOLEGRAINS
Whole wheat flour
+ PARENT : Flour
Buckwheat
Buckwheat noodles
Buckwheat flour
+ PARENT : Flour
Quinoa
Whole wheat
Einkorn
Einkorn Flour
+ PARENT : Flour
Spelt
Spelt Flour
+ PARENT : Flour
+Gluten Flour
+ PARENT : Flour
Corn
Cornmeal
+ PARENT : Corn
Corn Semolina
+ PARENT : Corn
~ GRAINS
@@ -387,14 +442,24 @@ Oats
Rice
BREF : Preparing puffed rice this way makes it less perishable. Brown rice is a wholegrain rice that has a nutty flavour, and it's more nutritious and chewy than white rice. It is produced by only removing the outermost husk, while white rice has several other layers removed. It is best to soak the rice for a day before cooking it to obtain a more nutritionally complete food, soaking it beforehand activates various enzymes in the rice.
Basmati Rice
+ PARENT : Rice
White Rice
+ PARENT : Rice
+Short Grain White Rice
+ PARENT : White Rice, Rice
Black Rice
+ PARENT : Rice
Puffed Rice
+ PARENT : Rice
Brown Rice
+ PARENT : Rice
Black Glutinous Rice
+ PARENT : Black Rice, Rice
Wholegrain Brown Rice
+ PARENT : Brown Rice, Rice
Rice Noodles
Rice Flour
+ PARENT : Rice, Flour
Flour
COLOR : #EFEFEF
@@ -405,7 +470,9 @@ Flour
& {{Einkorn|einkorn flour}} wheat was one of the first plants to be domesticated and cultivated. It has a high percentage of protein, more than regular wheat. It also has high levels of fat, phosphorus, potassium, pyridoxine (a form of vitamin b6) and beta-carotene, making it more nutritious than other kinds of grains. Another great thing about einkorn is that it isn't as toxic to people on gluten-free diets, it as yet to be proven but it should definitely be looked into!
& {{Breadfruit flour}} is a type of flour made from dried and ground breadfruit.
All Purpose Flour
+ PARENT : Flour
Breadfruit Flour
+ PARENT : Flour
BREF : The product of dried and ground breadfruit. Can be used to make cakes, pasta and a number of other meals.
Wheat Semolina
Oatmeal
@@ -424,14 +491,18 @@ Red Miso
~ Tell me (microlith57) if this color needs changing.
White Miso
COLOR : #955C19
-~ #FFD800 mayo?
Tofu
COLOR : #EFEFEF
BREF : The word {_bean curds_} for tofu has been used in the US since 1840. See also: {{silken tofu}}, {{burmese tofu}}.
Silken Tofu
+ PARENT : Tofu
COLOR : #EFEFEF
Burmese Tofu
+ PARENT : Tofu
COLOR : #EFEFEF
+Tofu Mayo
+ PARENT : Tofu
+ COLOR : #FFD800
Nutolene
Soy protein
@@ -458,7 +529,7 @@ Cornstarch
Active dry yeast
Baking soda
BREF : Since sodium bicarbonate can cause alkalosis, it's sometimes used to treat aspirin overdoses.
-Agar agar
+Agar agar powder
BREF : Agar is used to make impression material in dentistry.
Arrowroot starch
diff --git a/scripts/database/recipes.ndtl b/scripts/database/recipes.ndtl
@@ -98,10 +98,10 @@ SWEET AND SOUR LENTILS
Main
Brown lentils : 1/2 cup
Vegetable bouillon : 1 1/2 cups
- carrot : 1, cubed
- daikon : 2", cubed
- chives : 3 stalks
- salt : 1/4 tsp
+ Carrots : 1, cubed
+ Daikon : 2", cubed
+ Chives : 3 stalks
+ Salt : 1/4 tsp
Sauce
Soy sauce : 2 tbsp
Rice vinegar : 2 tbsp
@@ -1153,7 +1153,7 @@ CHILI POMEGRANATE BROWNIES
- Stir in the flax 'egg', as well as the {_2 tbsp_} of {{red pepper flakes}} and {_1 tsp_} of {{cayenne powder}}. Add {_1/2 cup_} of {{all purpose flour}} and mix well. Mixture should be very thick.
- Pour into a 8X8 baking dish lined with parchment papper. Flatten with the back of a spoon to even it out and bake for {#25 minutes#}, or until knife comes out clean. Let cool. {_Cut in 24 small squares_}.
Syrup
- - Pour {_2 cups_} of {{unsweetened pomegranate juice}} into a pot with {_1 tsp_} {{red pepper flakes}} and {_1 tsp_} {{cayenne}}. Bring to a boil, lower to medium-high heat and leave for up to {#1h#} or until liquid has been reduced to {_1 cup_}.
+ - Pour {_2 cups_} of {{unsweetened pomegranate juice}} into a pot with {_1 tsp_} {{red pepper flakes}} and {_1 tsp_} {{cayenne powder}}. Bring to a boil, lower to medium-high heat and leave for up to {#1h#} or until liquid has been reduced to {_1 cup_}.
- Let cool, the syrup will thicken when cooled.
- Top brownies with fresh {{pomegranate seeds}}, and drizzle with the chili-infused syrup!
INGR
@@ -1167,12 +1167,12 @@ CHILI POMEGRANATE BROWNIES
Sea salt : 1/4 tsp
All purpose flour : 1/2 cup
Chili pepper flakes : 2 tbsp
- Cayenne pepper : 1 tsp
+ Cayenne powder : 1 tsp
Syrup
Pomegranate juice : 2 cups
Sugar : 3/4 cup
Chili pepper flakes : 1 tsp
- Cayenne pepper : 1 tsp
+ Cayenne powder : 1 tsp
Topping
Pomegranate seeds : 1 cup
@@ -1686,7 +1686,7 @@ SALTED CARAMEL CAROB CHIP COOKIES
Cooking batter
Vegan butter : 1/4 cup, earth balance
Coconut sugar : 3/4 cup
- Dates caramel : 1/4 cup, see above
+ Date caramel : 1/4 cup, see above
Flax seeds : 1 tbsp
Einkorn flour : 1 cup + 3 tbsp
Baking soda : 3/4 tsp
@@ -1760,7 +1760,7 @@ CHICKPEA KELP SANDWICH
Dijon mustard : 1 tbsp
Bull kelp powder : 1/2 tsp
Scallions : 2 branches
- Cayenne pepper : 1/4 tsp
+ Cayenne powder : 1/4 tsp
Sea salt : 1/4 tsp
Black pepper : 1/4 tsp
Shichimi togarashi : To taste
diff --git a/scripts/graph.js b/scripts/graph.js
@@ -16,7 +16,8 @@ function graph () {
Ø('search').create({ x: 5, y: 14 }, SearchTemplate),
Ø('home').create({ x: 2, y: 14 }, HomeTemplate),
Ø('recipe').create({ x: 5, y: 8 }, RecipeTemplate),
- Ø('ingredient').create({ x: 8, y: 8 }, IngredientTemplate)
+ Ø('ingredient').create({ x: 8, y: 8 }, IngredientTemplate),
+ Ø('service').create({ x: 8, y: 14 }, ServiceTemplate)
])
Ø('client').mesh({ x: 32, y: 0 }, [
@@ -44,7 +45,7 @@ function graph () {
// Assoc
Ø('template').syphon(['recipe', 'ingredient', 'page'])
- Ø('page').syphon(['home', 'search'])
+ Ø('page').syphon(['home', 'search', 'service'])
Ø('template').connect(['view', 'document'])
Ø('view').bind(['header', 'core', 'footer'])
diff --git a/scripts/templates/home.js b/scripts/templates/home.js
@@ -57,14 +57,14 @@ function HomeTemplate (id, rect) {
for (id in ingredients) {
let name = ingredients[id][0]
html += `
- <li class='ingredient ${!table[name] ? 'missing' : ''}'>
+ <li class='ingredient${!table[name] ? ' missing' : ''}'>
<a href='#${name.to_url()}' onclick="Ø('query').bang('${name}')">
<img src='media/ingredients/${name.to_path()}.png'/>
</a>
<t class='name'>${name.capitalize()}</t>
</li>`
}
- return `<ul class='ingredients'>${html}<hr/></ul>`
+ return `<ul class='ingredients'>${html}</ul>`
}
function count_ingredients (recipe) {
diff --git a/scripts/templates/ingredient.js b/scripts/templates/ingredient.js
@@ -12,27 +12,73 @@ function IngredientTemplate (id, rect) {
title: `GrimGrains — ${t.name.capitalize()}`,
view: {
core: {
- content: make_ingredient(t.name, ingredient, t.tables.recipes),
+ content: make_ingredient(t.name, ingredient, t.tables.recipes, t.tables.ingredients),
related: make_related(related_recipes(t.name, t.tables.recipes))
}
}
}
}
- function make_ingredient (name, ingredient, recipes) {
+ function make_ingredient (name, ingredient, recipes, all_ingredients) {
let html = ''
- html += `<h1>${ingredient.TYPE ? ingredient.TYPE.capitalize() + '/' : ''}${name.capitalize()}</h1>`
+ html += `<h1>${name.capitalize()}</h1>`
html += ingredient.BREF ? `<p class='bref'>${ingredient.BREF.to_markup()}</p>` : ''
html += ingredient.LONG ? `${new Runic(ingredient.LONG)}` : ''
- html += `${make_similar(name, recipes)}`
+ html += `${make_parents(ingredient)}`
+ html += `${make_children(name, all_ingredients)}`
+ html += `${make_similar(name, recipes, all_ingredients)}`
+ return html
+ }
+
+ function make_parents (ingredient) {
+ let html = ''
+ if (!ingredient.PARENT) {return html}
+
+ html += "<h2>Parent Ingredients</h2><ul class='ingredients'>"
+ let parents = ingredient.PARENT.split(",")
+
+ for (id in parents) {
+ let name = parents[id].trim()
+ html += `
+ <li class='ingredient'>
+ <a onclick="Ø('query').bang('${name}')" href='#${name.to_url()}'>
+ <img src='media/ingredients/${name.to_path()}.png'/>
+ </a>
+ <t class='name'>${name.capitalize()}</t>
+ </li>`
+ }
+
+ html += "</ul>"
+ return html
+ }
+
+ function make_children (ingredient, all_ingredients) {
+ let html = ''
+ let child_ingredients = find_child_ingredients(ingredient, all_ingredients)
+ if (child_ingredients.length == 0) {return html}
+
+ html += "<h2>Child Ingredients</h2><ul class='ingredients'>"
+
+ for (id in child_ingredients) {
+ let name = child_ingredients[id]
+ html += `
+ <li class='ingredient'>
+ <a onclick="Ø('query').bang('${name}')" href='#${name.to_url()}'>
+ <img src='media/ingredients/${name.to_path()}.png'/>
+ </a>
+ <t class='name'>${name.capitalize()}</t>
+ </li>`
+ }
+
+ html += "</ul>"
return html
}
- function make_similar (search_name, recipes) {
+ function make_similar (search_name, recipes, all_ingredients) {
let html = ''
let ingredients = find_ingredients(recipes)
- let similar_ingredients = find_similar_ingredients(search_name, ingredients)
+ let similar_ingredients = find_similar_ingredients(search_name, ingredients, all_ingredients)
for (id in similar_ingredients) {
if (similar_ingredients[id][1] < 1) { break }
@@ -46,7 +92,7 @@ function IngredientTemplate (id, rect) {
<t class='name'>${name.capitalize()}</t>
</li>`
}
- return similar_ingredients.length > 1 ? `<h2>Related Ingredients</h2><ul class='ingredients'>${html}<hr /></ul>` : ''
+ return similar_ingredients.length >= 1 ? `<h2>Related Ingredients</h2><ul class='ingredients'>${html}<hr /></ul>` : ''
}
function find_ingredients (recipes) {
@@ -63,10 +109,13 @@ function IngredientTemplate (id, rect) {
return h
}
- function find_similar_ingredients (name, ingredients) {
+ function find_similar_ingredients (name, ingredients, all_ingredients) {
let a = []
+
+ let children = find_child_ingredients(name, all_ingredients)
for (id in ingredients) {
+ if (children.includes(id.toLowerCase())) {continue}
let words = id.toLowerCase().split(' ')
let index = similarity(name.toLowerCase().split(' '), words)
if (index > 0) {
@@ -80,6 +129,21 @@ function IngredientTemplate (id, rect) {
return a.reverse()
}
+
+ function find_child_ingredients (search_name, all_ingredients) {
+ let a = []
+
+ for (name in all_ingredients) {
+ let ingr = all_ingredients[name]
+ if (!ingr.PARENT) { continue }
+ let parents = ingr.PARENT.split(",").map(function (name) {return name.trim().toLowerCase()})
+ if (parents.includes(search_name.toLowerCase())) {
+ a.push(name.toLowerCase())
+ }
+ }
+
+ return a
+ }
function similarity (a, b) {
let score = 0
diff --git a/scripts/templates/service.js b/scripts/templates/service.js
@@ -0,0 +1,143 @@
+function ServiceTemplate (id, rect) {
+ Node.call(this, id, rect)
+
+ this.glyph = NODE_GLYPHS.render
+
+ this.answer = function (q) {
+ let recipe_ingredients = find_ingredients(q.tables.recipes)
+
+ let html = `
+ ${make_pageless(recipe_ingredients, q.tables.ingredients)}
+ ${make_unused(recipe_ingredients, q.tables.ingredients)}
+ `
+ return {
+ title: `GrimGrains — Service Panel`,
+ view: {
+ core: {
+ content: html,
+ related: ''
+ }
+ }
+ }
+ }
+
+ function find_ingredients (recipes) {
+ let h = []
+ for (id in recipes) {
+ let recipe = recipes[id]
+ for (id in recipe.INGR) {
+ let category = recipe.INGR[id]
+ for (name in category) {
+ if (!h.includes(name.toLowerCase())) {h.push(name.toLowerCase())}
+ }
+ }
+ }
+ return h
+ }
+
+ function make_pageless (used, pages) {
+ let pageless = find_pageless(used, pages)
+ let html = ""
+ for (id in pageless) {
+ html += `
+ <li class='ingredient missing'>
+ <a href='#${pageless[id].to_url()}' onclick="Ø('query').bang('${pageless[id]}')">
+ <img src='media/ingredients/${pageless[id].to_path()}.png'/>
+ </a>
+ <t class='name'>${pageless[id].capitalize()}</t>
+ </li>`
+ }
+ if (html == "") {return `<h2>No Ingredients Without Pages!</h2>`}
+ return `<h2>Ingredients Without Pages</h2><ul class='ingredients'>${html}</ul>`
+ }
+
+ function find_pageless (used, pages) {
+ let pageless = []
+
+ for (id in used) {
+ let name = used[id].toUpperCase()
+ if (!pages[name]) {pageless.push(name)}
+ }
+
+ return pageless
+ }
+
+ function make_unused (used, pages) {
+ let unused = find_unused(used, pages)
+ let html = ""
+
+ for (id in unused) {
+ html += `
+ <li class='ingredient'>
+ <a href='#${unused[id].to_url()}' onclick="Ø('query').bang('${unused[id]}')">
+ <img src='media/ingredients/${unused[id].to_path()}.png'/>
+ </a>
+ <t class='name'>${unused[id].capitalize()}</t>
+ </li>`
+ }
+ if (html == "") {return `<h2>No Unused Ingredients!</h2>`}
+ return `<h2>Unused Ingredients</h2><ul class='ingredients'>${html}</ul>`
+ }
+
+ function find_unused (used, pages) {
+ let unused = []
+
+ for (name in pages) {
+ if (!used.includes(name.toLowerCase())) {unused.push(name)}
+ }
+
+ return unused
+ }
+
+ function make_ingredients (ingredients, table) {
+ let html = ''
+ for (id in ingredients) {
+ let name = ingredients[id][0]
+ html += `
+ <li class='ingredient${!table[name] ? ' missing' : ''}'>
+ <a href='#${name.to_url()}' onclick="Ø('query').bang('${name}')">
+ <img src='media/ingredients/${name.to_path()}.png'/>
+ </a>
+ <t class='name'>${name.capitalize()}</t>
+ </li>`
+ }
+ return `<ul class='ingredients'>${html}</ul>`
+ }
+
+ function count_ingredients (recipe) {
+ let ingredients = {}
+ for (cat in recipe.INGR) {
+ for (id in recipe.INGR[cat]) {
+ ingredients[id] = 1
+ }
+ }
+ return Object.keys(ingredients).length
+ }
+
+ function make_recipes (recipes) {
+ let html = ''
+
+ // Sort by tag
+
+ let categorized = {}
+
+ for (name in recipes) {
+ let recipe = recipes[name]
+ if (!categorized[recipe.TAGS[0]]) { categorized[recipe.TAGS[0]] = [] }
+ recipe.name = name
+ categorized[recipe.TAGS[0]].push(recipe)
+ }
+
+ for (cat in categorized) {
+ let recipes = categorized[cat]
+ html += `<h3>${cat.capitalize()}</h3>`
+ html += "<ul style='margin-bottom:15px'>"
+ for (id in recipes) {
+ let recipe = recipes[id]
+ html += `<li><a href="#${recipe.name.to_url()}" onclick="Ø('query').bang('${recipe.name.capitalize()}')">${recipe.name.capitalize()}</a></li>`
+ }
+ html += '</ul>'
+ }
+ return `<columns id='recipes'>${html}</columns>`
+ }
+}