Did you know ... | Search Documentation: |
Adding style (CSS and simple JavaScript) to your pages |
Adding style using CSS and JavaScript seems easy: just link the required style-sheet and javascript source from the page-head and add the appropriate attributes (class, id, onXXX) to your HTML elements. Even better, this works! So, what is wrong with it?
Well, it breaks the reusability. Why? Think of our
\predicates_ul(Preds)
that created an 1980-ugly unnumbered list of
predicates. If we add style to the elements themselves, we end-up with
ugly HTML that cannot be reused with different skins while CSS
programming now needs do be done in Prolog. This harms hiring a
CSS-wizard to do the nice styling in a CSS file that we, programmers,
cannot do. If we put the style in a file however, we cannot just use
\predicates_ul(Preds)
anywhere in our code, but we also have to
adjust the page header. I.e., whenever we create a page-header, we must
be aware of all components we include and what style we need for them.
Same story for JavaScript that can be needed by the component. This
page discusses a web-application with CSS-styling.
NOTE: Sindice currently (Jan 17, 2013) serves RDF/XML using the
invalid Content-Type: text/xml
. This
patch
provides a work around for this issue. It will appear in SWI-Prolog
6.2.7/6.3.9.
Now that we covered the basics, it is time for a bit more sexy demo: a browser for Linked Open Data. Our program consists of four files. The file lod_crawler.pl is the main topic of this page.
First, our familiar declarations. The first block gets the HTML and HTTP
infrastructure that we need. The library(http/html_head) is new and
deals with dependencies. The second block gets the RDF and Linked Open
Data (LOD) infrastructure and the third block defines the HTTP locations
that we serve. We define a welcome page (/), a page to handle search
requests (/search?q=Query), a page to display a result (/resource?r=URI)
and because we are going to deal with a style-sheet, we define a new
alias css
(last block) and use it to define the HTTP location of our
style-sheets. The handlers for the style-sheets use the
library-predicate http_reply_file/3 to serve a static file.
:- use_module(library(http/thread_httpd)). :- use_module(library(http/http_dispatch)). :- use_module(library(http/html_write)). :- use_module(library(http/http_parameters)). :- use_module(library(http/html_head)). % new :- use_module(library(semweb/rdf_db)). :- use_module(lod). :- http_handler(root(.), home, []). :- http_handler(root(search), search, []). :- http_handler(root(resource), resource, []). :- http_handler(css('ptable.css'), http_reply_file('ptable.css', []), []). :- http_handler(css('sindice.css'), http_reply_file('sindice.css', []), []). http:location(css, root(css), []).
Next step, we provide the main page of the server. This should all be
familiar by now. Using class(Class)
, we just add an HTML class-attribute
for the CSS-file. The DCG-rule search_form//0 contains a new element:
\html_requires(css('sindice.css'))
, tells the HTML infrastructure
that the page needs the HTML resource css('sindice.css')
.
server(Port) :- http_server(http_dispatch, [port(Port)]). %% home(+Request) % % Provides the initial page of the LOD-crawler with a form % to search on http://sindice.com home(_Request) :- reply_html_page(title('LOD Crawler'), [ h1(class(title), 'LOD Crawler'), p(class(banner), [ 'Welcome to the SWI-Prolog Linked Open Data ', 'crawler. To start your experience, enter a ', 'search term such as "Amsterdam".' ]), \search_form ]). search_form --> { http_link_to_id(search, [], Ref) }, html([ \html_requires(css('sindice.css')), % new form([id(search), action(Ref)], [ input(name(q)), input([type(submit), value('Search')]) ]) ]).
We skip most of the rest of the code, except for two fragments. The rule property_table//1 below shows another use of html_requires//1. Both search_form//1 and property_table//1 depend on CSS styling and both ensure they get the needed style-sheet without any modifications to the head in reply_html_page/2.
property_table(Grouped) --> html([ \html_requires(css('ptable.css')), % new table(class(properties), [ \ptable_header | \ptable_rows(Grouped) ]) ]).
The final fragment illustrates that reply_html_page/2 can be hooked to
define the overall structure of all pages generated by this predicate.
in this example, the hook adds a div
holding the search-form at the
top of the page, unless the page itself already contains such a form.
%% body(+Content)// % % Define overall style. This hook into reply_html_page/2 is called % to translate the 2nd argument. It is searched for in the current % module as well as the user-module. % % Redefining head//1 or body//1 is a way to redefine the overall % page-style of all pages served. body(Content) --> % contents already provides a form { sub_term(\search_form, Content) }, !, html(Content). body(Content) --> % add header with search-form html([ div(class(top), \search_form) | Content ]).