Table of Contents | ||||
---|---|---|---|---|
|
...
Check out some of the class options here. The style attribute can be used, for example, to display an image inline (e.g., as part of a sentence in a paragraph) with style="display: inline;"
.
Info |
---|
If you don’t want to use an attribute for an HTML element while the predefined predicate requires the corresponding input argument, just replace the argument with the empty atom (string) ‘ |
div tag
The div
tag is a versatile block element that can be used to organize and style HTML content in various ways. In the html.pl
file provided to you we have defined a div/4
predicate div(Content, Class, Style, Html)
that facilitates the generation of div
elements with class
and style
attributes that you can set. The code that will be generated looks simply like a basic div element without any content yet:
Code Block |
---|
<div class="CLASS" style="STYLE">CONTENT</div> |
In this code template, CLASS
refers to the value of the Class
variable, STYLE
to the value of the Style
variable, and CONTENT
to the value of the Content
variable in the div(Content, Class, Style, Html)
query. All of these variables need to be fully instantiated to generate HTML code that is returned in the output argument Html
.
Many Bootstrap components are classes that can be used by means of the div
tag. These include, for example, the container, jumbotron, alert, progress bar, spinner, card, flexbox, media, and grid system row and column classes. Check out all of these components in the W3 School tutorial or Bootstrap’s own overview. We provide one example to illustrate the use of the div predicate. The following code would generate a Jumbotron element:
Code Block |
---|
div(Content, 'jumbotron jumbotron-fluid px-3', 'text-align:center', Html) |
Of course, this only works if you add another query (or queries) to generate the Content
for this Jumbotron. To add couple of lines of formatted text (a large title head and a paragraph), we could use the following code for creating a complete Jumbotron element with some text as a simple example:
Code Block |
---|
atomic_list_concat([
'<h3>Introduction</h3>',
'<p>You are about to interact with our conversational agent <b>YourAgentName</b>.'],
Content),
div(Content, 'jumbotron jumbotron-fluid px-3', 'text-align:center', Html) |
The first query, using the built-in predicate atomic_list_concat/2
generates HTML code that consists of the two HTML elements of a h3
heading following by a paragraph. These elements are just simply quoted each (remember to always use single quotes!) and put in that order in a Prolog list [...]
. The atomic_list_concat/2
predicate then returns a new atom that is the concatentiation of these two elements in the output argument Content
. This (quoted) HTML code then is passed on to the div/4
query in its first input argument (using the same variable name) to embed it in the Jumbotron element.
Info |
---|
We will often need to piece together parts of HTML code or elements. As a convention, we will use the |
The resulting HTML code looks likeThe Source
argument for the predicate is a required input argument. You can use links to pictures that are online available here. For your convenience, for all of the recipes included in the database, the picture/2
predicate picture(RecipeNr, UrlLink)
provides images for each recipe. As an example, to illustrate the use of the both picture/2
and img/4
predicates in a combined query, picture('1', Source), img(Source, 'img-thumbnail', 'height: 3rem', Html)
would generate the following HTML element:
Code Block |
---|
<img class="img-thumbnail" style="height: 3rem" src="https://mobkitchen-objects.imgix.net/recipes/849A3215-2.jpg?auto=format&crop=focalpoint&domain=mobkitchen-objects.imgix.net&fit=crop&fp-x=0.5&fp-y=0.5&h=827&ixlib=php-3.3.1&q=82&w=1300&s=e97db51272669560eff9df6badbe8bc7" alt="Can not show this image"> |
which would show a small thumbnail version of the first recipe picture
...
for sweet and spicy gochujang fried chicken.
Alternatively, instead of providing an url to an online image as the source input argument, you can also add image Base64 codes to the Prolog html.pl
file and use those. Examples for variants of a microphone icon have already been included in this file. E.g., the mic_image_closed_src(Source)
query will instantiate the Source
variable with the Base64 code for the closed microphone icon
...
You can add your own images to the html.pl file by encoding them to Base64 format too. You can do so by following these steps:
Go to the https://www.base64encode.org/ site (don’t change any options on that page)
Go to the Encode files to Base64 format section
Upload an image (note that file size cannot exceed 192MB!)
Press the encode button, and
Download the code as a text file.
Copy the text in the file.
Add a fact with a predicate name for your image of your choosing (to illustrate we used
myImageName
) by adding the following template to thehtml.pl
file:myImageName('data:image/png;base64,...')
Paste and replace the
...
in the Prolog fact with the text you copied from the file (a very long encoded string representing your picture)Now you can use
myImageName(Source)
instead of thepicture(RecipeNr, Source)
query to include your picture on your webpage
div tag
The div
tag is a versatile block element that can be used to organize and style HTML content in various ways. In the html.pl
file provided to you we have defined a div/4
predicate div(Content, Class, Style, Html)
that facilitates the generation of div
elements with class
and style
attributes that you can set. The code that will be generated looks simply like a basic div element without any content yet:
Code Block |
---|
<div class="jumbotron jumbotron-fluid px-3CLASS" style="text-align:center"> <h3>Introduction</h3> <p>You are about to interact with our conversational agent <b>YourAgentName</b>.</p> </div> |
Info |
---|
Padding and margins The HTML code above uses |
span tag
The span
tag is an inline element that is commonly used to style HTML content. We have added a span/3
predicate span(Content, Style, Html)
that just does that and nothing else. An example of a span tag that can be useful is to style the content and change the font family that is used within some HTML content:
Code Block |
---|
<span style="font-family:Roboto;">YOUR HTML CONTENT HERE</span> |
A list of system or web-safe fonts that you can choose from (supported on most devices) can be found here.
Note |
---|
In our project, the head of the webpage (containing metadata) is fixed and cannot be changed. It therefore is not possible, for example, to link to other online fonts. |
The span tag has other uses too. It can be used for adding Bootstrap badges and borders to elements, for example. We have also used it for setting the value of a button in the template we used for defining the button/4
predicate.
Check out the html.pl
file for more predefined Prolog predicates for HTML tags that you can use.
Generating an HTML webpage
By means of these and generate with the rule. The HTML code is represented in Bootstrap format, which is also clearly illustrated by examples below or on Bootstrap's documentation website (Bootstrap Documentation).
Note |
---|
Prolog Advice: To manipulate strings and atoms in Prolog it is useful to look at documentation of the following built-in functions: atomic_list_concat, atom_concat, string_concat, append, and maplist here: https://www.swi-prolog.org/. The predicate applyTemplate is a defined predicate that will be explained below. |
Prolog rules are used to add a condition to a webpage (i.e. webpage X is shown when Clause Y is true).
You add a condition that checks for a particular pattern ID to a Prolog rule in the html.pl
file. Other simple conditions that you can add involve counting the number of recipes that are still available while in the recipe selection phase, etc. The general form of the page lay-out rules would be something like:
Code Block |
---|
myPage(Txt, Button, Html) :-
<HERE YOUR CONDITION FOR SHOWING THIS PAGE>,
% e.g. while recipe is being selected (check for e.g. currently active top level pattern id), we'll display recipe features
% below code that specifies the page layout for this context
applyTemplate('<div class="card mx-auto" style="width:67vw">~a</div>, Text, Html),
…
. |
There are still many options to vary. For example, you can choose the parameters for the myPage
predicate that fit your approach best.
Example Prolog Rule for a Page
Code Block | ||
---|---|---|
| ||
page(c10, _, Html) :-
% Condition for when to show this page
CONDITION X,
% Constructing HTML page
atomic_list_concat(['<div class="alert alert-light"><center></br><h1>Hello!</br></br>','I am ~a</h1></br></br></center></div>'], Template),
% Get the bot's name if it has one; other call it 'your assistant'
(agentName(Name) -> N = Name ; N = 'your recipe selection assistant'), applyTemplate(Template, N, Body),
% Create the HTML page
html(Body, Html). |
Adding a chatbox to your page layout for testing purposes
The speech interface is fitting to the context of a user in a kitchen, but not always feasible for testing. It may take additional time for the system to process speech input, you may be in a noisy environment, and there may be speech recognition failures. Hereby a pointer to easily enable a textbox to be rendered for input.
In the html.pl
file, where you format the pages to render during the conversation, for each of the pages that you may display at any point in the conversation add the following code:
Code Block |
---|
A chatbox can be added for using text instead of speech for input by adding
<div class="text-center"><p class="chatbox mx-auto"></p></div>, for example,
to the footer. |
Using and Understanding the code in html.pl
A Quick guide to using Bootstrap components not defined in html.pl
We briefly discuss an example on how to use the code in html.pl
yourself with Bootstrap components that you might want to use but have not yet been introduced yet. To that end, suppose that you would like to create a lay-out with three images side-by-side. The first step you could take is to check which components Bootstrap offers to create such a lay-out. It turns out that a card deck might just be what you were looking for. It is advertised as something you would use when you “Need a set of equal width and height cards that aren’t attached to one another? Use card decks.” Bootstrap also provides the HTML template code as an example of how to use it. This is what that looks like:
Code Block |
---|
<div class="card-deck">
<div class="card">
<img class="card-img-top" src="...">
<div class="card-body">
<h5 class="card-title">Card title</h5>
<p class="card-text">This is the first card.</p>
</div>
</div>
<div class="card">
<img class="card-img-top" src="...">
<div class="card-body">
<h5 class="card-title">Card title</h5>
<p class="card-text">This is the second card.</p>
</div>
</div>
<div class="card">
<img class="card-img-top" src="...">
<div class="card-body">
<h5 class="card-title">Card title</h5>
<p class="card-text">This is the third card.</p>
</div>
</div>
</div> |
How do we break this code down into pieces that we can (already) manage? We first scan the code for the HTML tags that are used in it. We find that the div
tag is often used (with the classes "card-deck"
, "card"
, and "card-body"
). We also find that the img
tag is used as well as some variants of the <h5>
and <p>
tags with specific card-related classes added. We note that for each of these tags we already have the tools to generate them in Prolog. We can use the div/4
and img/4
predicates for the former tags and can use our strategy to quote basic HTML elements for the h5
and p
elements (you can also introduce new predicates for these tags in combination with a class attribute if you like but we prefer to keep it simple here).
Now we have established that all the basic ingredients are available, the next step in our recipe is to create the separate parts of the HTML code by - in a sense - looking from the inside out. We first will generate the elements that are most embedded and then use those to build the elements in which they are directly embedded.
First, we want to render a given image. In the HTML, we see that each of the three images are defined as separate cards.
We will now use Prolog to generate the HTML for such a card, using a given picture URL. The aim is to connect in a single string the static HTML code and the image URL (which differs according to the context of the conversation).
Code Block |
---|
imgCard('<div class="card"><img class="card-img-top" src="~a" alt="Card image cap"></div>'). |
Note that the HTML is copied, with a replacement of the contents of ‘src’, in the form of ‘~a’ which is a placeholder for the image URL. This is needed in order to dynamically add this information. We will do this with the following rule:
Code Block |
---|
imgCard(Image, Html) :- imgCard(I), format(atom(Html), I, [Image]). |
In this rule, the Image
variable is the input and holds the URL of the image, whereas the Html
variable is the output HTML code for this card. In the body of the rule, the imgCard/1
is queried to retrieve the HTML code that we already specified. Then, the URL is placed at the location with `~a' in this HTML code (in the Image
variable), using the format/3
predicate.
We can now use the imgCard term to render each of the three images. For the complete display, we will combine them in the broader ‘card-deck’ option, like in the example HTML we got from bootstrap. We will do this using a self-defined Prolog term, with as input the text, button text and the three images, and as output the HTML code to render. Within this term, we will start specifying the main template, and then collect the images:
Code Block |
---|
myThreeImagesPage(Txt, Button, Image1, Image2, Image3, Html) :-
Template='<div class="card-deck">~a</div>',
imgCard(Image1,I), imgCard(Image2,I2), imgCard(Image1,I3),
atom_concat(I,I2,II), atom_concat(II,I3,III),
format(atom(Card), Template, [III]), html(Card, Html). |
Note that atom_concat is used to append the different HTML snippets for each of the images. Format is then used to plug in the HTML code where the `~a' has been specified in the Template variable (as was done for the imgCard).
The current term will display three given images on the screen. Of course, you need to make sure that the right image URLs can be displayed during the conversation, which you can do using Prolog facts in recipes.pl
.
Defining new predicates for generating HTML elements
applyTemplate('Template String with ~a where you want to insert atom', Atom to Insert, Return Variable).
...
STYLE">CONTENT</div> |
In this code template, CLASS
refers to the value of the Class
variable, STYLE
to the value of the Style
variable, and CONTENT
to the value of the Content
variable in the div(Content, Class, Style, Html)
query. All of these variables need to be fully instantiated to generate HTML code that is returned in the output argument Html
.
Many Bootstrap components are classes that can be used by means of the div
tag. These include, for example, the container, jumbotron, alert, progress bar, spinner, card, flexbox, media, and grid system row and column classes. Check out all of these components in the W3 School tutorial or Bootstrap’s own overview. We provide one example to illustrate the use of the div predicate. The following code would generate a Jumbotron element:
Code Block |
---|
div(Content, 'jumbotron jumbotron-fluid px-3', 'text-align:center', Html) |
Of course, this only works if you add another query (or queries) to generate the Content
for this Jumbotron. To add couple of lines of formatted text (a large title head and a paragraph), we could use the following code for creating a complete Jumbotron element with some text as a simple example:
Code Block |
---|
atomic_list_concat([
'<h3>Introduction</h3>',
'<p>You are about to interact with our conversational agent <b>YourAgentName</b>.'],
Content),
div(Content, 'jumbotron jumbotron-fluid px-3', 'text-align:center', Html) |
The first query, using the built-in predicate atomic_list_concat/2
generates HTML code that consists of the two HTML elements of a h3
heading following by a paragraph. These elements are just simply quoted each (remember to always use single quotes!) and put in that order in a Prolog list [...]
. The atomic_list_concat/2
predicate then returns a new atom that is the concatentiation of these two elements in the output argument Content
. This (quoted) HTML code then is passed on to the div/4
query in its first input argument (using the same variable name) to embed it in the Jumbotron element.
Info |
---|
We will often need to piece together parts of HTML code or elements. As a convention, we will use the |
The resulting HTML code looks like:
Code Block |
---|
<div class="jumbotron jumbotron-fluid px-3" style="text-align:center">
<h3>Introduction</h3>
<p>You are about to interact with our conversational agent <b>YourAgentName</b>.</p>
</div> |
Info |
---|
Padding and margins The HTML code above uses |
span tag
The span
tag is an inline element that is commonly used to style HTML content. We have added a span/3
predicate span(Content, Style, Html)
that just does that and nothing else. An example of a span tag that can be useful is to style the content and change the font family that is used within some HTML content:
Code Block |
---|
<span style="font-family:Roboto;">YOUR HTML CONTENT HERE</span> |
A list of system or web-safe fonts that you can choose from (supported on most devices) can be found here.
Note |
---|
In our project, the head of the webpage (containing metadata) is fixed and cannot be changed. It therefore is not possible, for example, to link to other online fonts. |
The span tag has other uses too. It can be used for adding Bootstrap badges and borders to elements, for example. We have also used it for setting the value of a button in the template we used for defining the button/4
predicate.
Check out the html.pl
file for more predefined Prolog predicates for HTML tags that you can use.
Generating an HTML webpage
Above we have illustrated how to generate individual HTML elements but not yet shown how to generate a complete webpage using one or more of those elements. We will show in this section how you can generate such a webpage and make the agent render it.
Another key question that we have not addressed so far is when to display a webpage. To answer the latter question first, we will use a generic approach where we show a specific webpage when a specific subdialog is ongoing. For example, when the conversation with the user is still in its opening stages and a greeting is ongoing, we can show a welcoming page to a user. As greeting and other subdialogs correspond with names of conversational pattetrns, the idea is to show a webpage when the top level ongoing dialog part corresponds with a name of one of the patterns defined in our conversational agent. The agent will retrieve the body of the HTML webpage that we want to display for such a pattern using the query page(+PatternID, +Txt, -Html)
where PatternID
is the name of the conversational pattern, Txt
is the text the agent will say if its their turn, and Html
is the output argument with the HTML code for the body of our webpage.
As before, but now for complete webpages, we will use a structural template to create a page. The typical structure of these templates looks like this:
Code Block |
---|
page(PatternID, Txt, Html) :-
%%% 1. Condition for when the webpage that will be generated is shown
currentTopLevel(PatternID),
[OPTIONAL: OTHER CONDITIONS FOR SHOWING THIS PAGE,]
%%% 2. HTML code generation part
% First row: Prolog code (queries) that generates HTML code for the first 'row' of the page
% with FirstRow the output argument that is unified with that HTML code
... , FirstRow),
...
% Nth row: Prolog code (queries) that generates HTML code for the Nth 'row' of the page
% with NthRow the output argument that is unified with that HTML code
... , NthRow),
%%% 3. Putting everything together
atomic_list_concat([FirstRow, ..., NthRow], Body),
%%% 4. Generate the HTML webpage code
% Create the HTML page with:
html(Body, Html)
% or, alternatively, without a header and footer:
[OPTIONAL: html_without_header_footer(Body, Html). % replaces previous html(Body, Html)!
. |
The template consists of four consecutive parts. The first part uses the currentTopLevel(PatternID)
query to check whether the currently ongoing top level conversation pattern is PatternID
. The variable PatternID should be instantiated with a specific name of a conversation pattern, and a page rule for each pattern that can be a top level conversation pattern should be created to be able to show a webpage during each ongoing part of the conversation. If you want to differentiate which page is shown also based on other conditions, (optionally) multiple page rules should be defined and (optionally) additional Prolog queries can be added to define more complex conditions for showing a webpage. For example, you could (and will be asked to) add a condition that checks how many recipes still match the requests (preferences, constraints) of a user and show a different page if that drops below a certain number. The second part consists of Prolog code for generating the HTML code for some of the components and elements introduced above. See below for a simple example, and at the end of this page a more complex example for a card deck. Typically, to define a complete webpage, multiple elements will be used that would usually be organized in one or more rows of HTML code (think of these literally as rows on the page, in line with the basic structure of a Bootstrap grid). The third part simply concatenates the HTML code for each of the individual rows using the atomic_list_concat/2
predicate that we explained in detail above. And, finally, the fourth part generates the HTML code for the complete body of the webpage using the predefined html/2
predicate in html.pl
(or, alternatively, the predicates html_without_header
or html_without_header_footer
).
In this project, we generate the HTML code for the body of an HTML webpage (that is what the template for a page/3
rule above is supposed to do). The head of the webpage (a container for metadata, not to be confused with the header of or headings on a webpage!) is predefined and cannot be modified by our MARBEL agent.
Check out the code for the header/2
and footer/2
predicates in the html.pl
to see what HTML code is generated for the header and footer. You are free to change these too if you like, but you should make sure that the microphone icon always is displayed on the relevant pages so a user can engage in talking to your conversational agent (by making it open their microphone and start listening to the user).
Example Prolog Rule for a Page
As indicated above, the idea is to add a rule defining a page(+PatternId, +Txt, -Html)
for each conversation pattern that might be ongoing at the top level during the conversation. You will be asked to add a range of different patterns in the patterns.pl
file during the project. One of those hinted at above is a greeting pattern with a pattern ID code c10
. We will now provide a simple example of rule that generates a page that will be shown when the pattern c10
is active (or ongoing):
Code Block |
---|
% A very basic and simple welcoming page to show when a greeting is still ongoing
page(c10, _, Html) :-
% Part 1: Show this page only when a greeting pattern c10 is ongoing
currentTopLevel(c10),
% Part 2: Constructing HTML for the body of the page
% If the agent has a name (agentName/1 will succeed),
% we want the agent to introduce itself with its name
(agentName(Name) -> N = Name ; N = 'your recipe recommendation assistant'),
% replace the placeholder ~a with the value of variable N
applyTemplate('<h1>I am ~a</h1><br></center>', N, IntroHeading)
% piece a quoted text and our intro self-introduction text elements together
atomic_list_concat(['<center><h2>Hello!</h2><br>', IntroHeading], AlertContent),
% put the introductory text HTML as content into an alert component
div(AlertContent, 'alert alert-light', '', Body),
% Part 3: page is so simple that there is no second row so nothing to do here
% Part 4: Create the HTML page using the HTML code unified with the Body variable
html(Body, Html). |
Now suppose that the query currentTopLevel(c10)
succeeds. In that case the page(c10, _, Html)
query will also succeed (why?). Suppose, moreover, that our conversational agent is called Cookpanion (you think of a good name for your own agent!) and the query agentName(Name)
succeeds with Name='Cookpanion'
. What HTML code will then be generated (and bound to the Html
output argument variable)? To answer this question, you need to understand how the applyTemplate
query works (more on that you can find at the end of this page: https://socialrobotics.atlassian.net/wiki/spaces/PM2/pages/2264236033/Visual+Support+Guide#Defining-new-predicates-for-generating-HTML-elements). But for now the comment just above the query will be sufficient to piece things together. We get the HTML code <h1>I am Cookpanion</h1><br></center>
that binds to IntroHeading
. The atomic_list_concat
query add this to the end of the <center><h2>Hello!</h2></br>
HTML code, which then is used as the content for the alert class using the div predicate. By inspecting the html/2
predicate (check out the html.pl
file) we can then figure out that the resulting HTML code will look like this:
Code Block |
---|
% Next line makes sure (a user is asked to) enable audio interaction via the browser
<div class="audioEnabled"></div>
% A row that we use as header
<div class="row" style="position: static; height: 75px">
<nav class="navbar mb-1" style="position: static">
<button class="btn" style="position: fixed; top: 0; left: 0; z-index: 1">
<img class="img-fluid" style="width:4rem" data-value="Mic" src="data:image/png;base64,MISSING-VERY-LONG-BASE64-CODE-FOR-MIC-ICON>
<h6></h6>
</button>
</nav>
</div>
% Main part of the page
<main class="container-fluid p-3">
% Next three lines represent the code generated by the page(c10,_,Html) predicate
<div class="alert alert-light" style="">
<center><h2>Hello!</h2></br><h1>I am Cookpanion</h1><br></center>
</div>
</main>
% Footer
<footer class="fixed-bottom">
<p class="lead mb-0 bg-light text-center" style="min-height:0rem">
<button>End interaction</button>
</p>
</footer> |
Perhaps the code that is generated is a bit surprising and it is hard to locate the (just!) three lines that the page(c10,_,Html)
query generates (somewhere in the middle, as main part of the page). The code illustrates that the html/2
predicate also does quite a bit of the work for generating the complete body of our webpage, including the generation of a header and footer, and a line at the top to make sure the browser will allow for audio interaction.
Adding a chatbox to your page layout for testing purposes
You will soon enough learn that using speech as the main interface for interaction can be challenging, adding delays and misunderstandings, depending on factors such as the context where it is used (a noisy environment makes ASR much more challenging and may lead to failures), perhaps the accent of the speaker, and other related factors. For testing purposes, it therefore may be useful to also be able to test without these factors potentially complicating the interaction. To this end, we have made a chatbox element available too which makes text-based interaction available as an alternative to (just) speech-based interaction. To add this element to your webpage, we suggest you add the following HTML code to the footer of pages:
Code Block |
---|
<div class="text-center"><p class="chatbox mx-auto"></p></div> |
We have already added the code for doing this in the html.pl
file, but it is still commented out.
Note |
---|
Although we provide the chatbox option for testing purposes, it is up to you and your team to make sure the agent works also without the chatbox option. The chatbox should not be enabled in the conversational agent code that you submit as final deliverable! |
Using and Understanding the code in html.pl
We illustrate how the predicates introduced above can be used to create more complex HTML code with a card deck example. We also briefly discuss how the Prolog clauses for these predicates have been defined.
A Quick guide to using Bootstrap components not defined in html.pl
We briefly discuss an example to illustrate how to use the code in html.pl
yourself with Bootstrap components that you might want to use that have not yet been introduced. To that end, suppose that you would like to create a lay-out with three images side-by-side. The first step you could take is to check which components Bootstrap offers to create such a lay-out. It turns out that a card deck might just be what you were looking for. It is advertised as something you would use when you “Need a set of equal width and height cards that aren’t attached to one another? Use card decks.” Bootstrap also provides the HTML template code as an example of how to use it. This is what that looks like:
Code Block |
---|
<div class="card-deck">
<div class="card">
<img class="card-img-top" src="...">
<div class="card-body">
<h5 class="card-title">Card title</h5>
<p class="card-text">This is a card.</p>
</div>
</div>
<div class="card">
<img class="card-img-top" src="...">
<div class="card-body">
<h5 class="card-title">Card title</h5>
<p class="card-text">This is a card.</p>
</div>
</div>
<div class="card">
<img class="card-img-top" src="...">
<div class="card-body">
<h5 class="card-title">Card title</h5>
<p class="card-text">This is a card.</p>
</div>
</div>
</div> |
How do we break this code down into pieces that we can (already) manage? We first scan the code for the HTML tags that are used in it. We find that the div
tag is often used (with the classes "card-deck"
, "card"
, and "card-body"
). We also find that the img
tag is used as well as some variants of the <h5>
and <p>
tags with specific card-related classes added. We note that for each of these tags we already have the tools to generate them in Prolog. We can use the div/4
and img/4
predicates for the former tags and can use our strategy to quote basic HTML elements for the h5
and p
elements (you can also introduce new predicates for these tags in combination with a class attribute if you like but we will keep things simple here).
Now we have established that all the basic ingredients are available, the next step in our recipe is to create the separate parts of the HTML code by - in a sense - looking from the inside out. We first will generate the elements that are most embedded and then use those to build the elements in which they are directly embedded. The most deeply embedded elements are the heading and paragraph elements. As suggested above, we simply quote these. In order to create a single atom we also concatenate these elements using the atomic_list_concat/2 predicate as follows:
Code Block |
---|
atomic_list_concat([
'<h5 class="card-title">Card title</h5>',
'<p class="card-text">This is a card.</p>'], CardBodyContent) |
We introduced the CardBodyContent
variable here to indicate that the HTML code generated by the atomic_list_concat
query will be used as content for the card-body
class element.
The template example uses three times exactly the same card body content. For practical use, you most likely will want to change the content for each of these three card bodies and generate separate HTML code for each. We did not go to that length but kept things simple to avoid duplicating code.
For the card-body class we can use the div/4
predicate, where we use the CardBodyContent
variable to replace the default input argument Content
variable for this predicate and fill in the class and style arguments. We name the output argument CardBodyElement
and then we get as our next piece of code:
Code Block |
---|
div(CardBodyContent, 'card-body', '', CardBodyElement) |
For creating the card, we also need an image (again we do not bother to fill in the ...
in the example, which we leave up to you; check out the https://socialrobotics.atlassian.net/wiki/spaces/PM2/pages/2264236033/Visual+Support+Guide#img-tag section above on how to do that). We use the img(+Source, +Class, +Style, -Html)
predefined predicate and use CardImage
as output variable:
Code Block |
---|
img('...', 'card-img-top', '', CardImage) |
Now we have code to generate the two elements that we need to create a card
class element, we can piece these two elements together using the div/4
predicate to generate code for that element. Phrases like piece these together by now should have alerted you to the fact that atomic_list_concat
might be useful! We write the following Prolog code to generate the card
element:
Code Block |
---|
atomic_list_concat([CardImage, CardBodyElement], CardContent),
% We need three cards!
atomic_list_concat([CardContent, CardContent, CardContent], CardContent3Times),
div(CardContent3Times, 'card', '', CardDeckContent) |
Finally, as a last step, we now can create our card-deck using div/4
again as follows:
Code Block |
---|
div(CardDeckContent, 'card-deck', '', Html) |
We now have all the different Prolog queries that we need, in the right order, to create one big query that will generate the complete HTML code for the card deck. We simply need to add commas in between these queries (used as conjuncts) to obtain our complete Prolog query:
Code Block |
---|
% Prolog query that generates HTML code for a three-card card deck
% First, we generate the content for the card body
atomic_list_concat([
'<h5 class="card-title">Card title</h5>',
'<p class="card-text">This is a card.</p>'], CardBodyContent),
% then we generate the card body element
div(CardBodyContent, 'card-body', '', CardBodyElement),
% then we generate the card image element
img('...', 'card-img-top', '', CardImage),
% and then we generate the content for the card deck
atomic_list_concat([CardImage, CardBodyElement], CardContent),
% We need three cards!
atomic_list_concat([CardContent, CardContent, CardContent], CardContent3Times),
div(CardContent3Times, 'card', '', CardDeckContent),
% so we can finally create the card deck html element
div(CardDeckContent, 'card-deck', '', Html).
% The Html output variable will return the complete card deck HTML code we showed above. |
The HTML code and query above, of course, will not work yet as we used three dots ...
for the src
attribute. You will still need to make sure that the right image URLs (or Base64 codes) are used to replace these dots and modify the accompanying text in the card bodies to match with these images.
Defining new predicates for generating HTML elements
When you inspect the code in the html.pl file, you will see that a returning pattern has been used to define Prolog predicates for corresponding HTML tags. The general pattern for a tag named tagname
looks like this:
Code Block |
---|
% tagname/1 is used to introduce an HTML template with ~a to indicate placeholders for a
% tag tagname; in this example, there are just two placeholders and we assume that the tag
% has a start <tagname> and end tag </tagname>
tagname('<tagname... ~a ... >~a</tagname>') |
One of the placeholders always should represent the content of the tagname
element (which typically will appear last in the HTML template code, right between the start and end tag). Other placeholders could be used for class
or other attributes. The second clause that defines the tagname
predicate that generates the HTML code for the element should have N+1 arguments where N is the number of placeholders in the template. To generate the HTML code, we use the predefined applyTemplate(+Template, +ArgumentList, -Html)
query for instantiating the placeholders in the HTML template. The typical pattern looks like this:
Code Block |
---|
% tagname/3 predicate for generating HTML code for the tag element;
% we suppose, as an example, that the other placeholder in the template is a class attribute
tagname(Content, Class, Html) :-
tagname(Template), applyTemplate(Template, [Class, Content], Html). |
applyTemplate(+Template, +ArgumentList, -Html)
The predicate applyTemplate/3
replaces placeholders ~a
in the HTML template Template
input argument with values provided by the arguments listed in the input variable ArgumentList
to generate the instatiated HTML code from the template. The ArgumentList should be a Prolog list [Arg1, ..., ArgN]
where the number of arguments N matches the number of placeholders ~a
in the template, and the order of these arguments matches the order in which these arguments are expected in the template.
Above, we suggested that we could also introduce a predicate for a paragraph with a class attribute, e.g. for HTML code that looks like <p class="card-text">SOME TEXT CONTENT</p>
. Perhaps this is not that often needed in practice, but for illustrative purposes we will now show as an example how such a predicate can be defined.
The first thing we always need to do is to add a template for the HTML element. We will simply use the p/1
predicate in line with our convention to name the predicate the same as the HTML tag, which is convenient We want to be able to fill in the class attribute and the paragraph content, so we insert two placeholders ~a
(one for each of these):
Code Block |
---|
% Paragraph template with class and content placeholders
p('<p class="~a">~a</p>'). |
To define a p/3
predicate p(Content, Class, Html)
that allows for filling in a class attribute as well as content for the paragraph element, we use the template and the applyTemplate/3
predicate:
Code Block |
---|
% Rule for paragraph predicate p/3 for a paragraph element with a class attribute
p(Content, Class, Html) :- p(Template), applyTemplate(Template, [Class, Content], Html). |
Note that by convention we put the Content
argument first. This is just a convention but by sticking with it we keep our code consistent and avoid confusion about what the first argument of our predicates should be instantiated with. The elements in the list [Class, Content]
fed to the applyTemplate
predicate, however, should match the order of the placeholders ~a
in the paragraph template we introduced above to make sure that they are substituted in the right order into this template.
As an example, we can now use our p/3
predicate to replace our quoted paragraph element '<p class="card-text">This is the third card.</p>'
in our card deck example above as follows:
Code Block |
---|
p('This is a card.','card-text', ParagraphElement) |
Note that we need to introduce an output argument variable name to retrieve the HTML code for the paragraph in.