29
30:- module(plweb_page, []). 31:- use_module(footer). 32:- use_module(library(http/html_write)). 33:- use_module(library(http/html_head)). 34:- use_module(library(http/http_path)). 35:- use_module(library(pldoc/doc_search)). 36:- use_module(library(http/js_write)). 37:- use_module(library(http/html_head)). 38:- use_module(library(http/http_wrapper)). 39:- use_module(library(http/http_dispatch)). 40:- use_module(library(http/http_parameters)). 41:- use_module(library(pldoc/doc_html), [object_name//2]). 42:- use_module(library(uri)). 43:- use_module(library(option)). 44
45:- use_module(wiki). 46:- use_module(post). 47:- use_module(openid). 48:- use_module(did_you_know). 49:- use_module(holidays). 50
51:- html_meta
52 outer_container(html, +, ?, ?). 53
54:- http_handler(root(search), plweb_search, []).
60:- multifile
61 user:body//2,
62 plweb:page_title//1,
63 html_write:html_header_hook/1. 64
65html_write:(_) :-
66 format('Content-Security-Policy: frame-ancestors \'none\'~n').
67
68user:body(homepage, Body) --> !,
69 outer_container([ \tag_line_area,
70 \menubar(homepage),
71 \blurb,
72 \cta_area,
73 \enhanced_search_area,
74 Body
75 ], []).
76user:body(Style, Body) -->
77 { page_style(Style, Options), !,
78 functor(Style, ContentClass, _)
79 },
80 outer_container(
81 [ \title_area(Style),
82 \menubar(Style),
83 div(class(breadcrumb), []),
84 div(class(['inner-contents', ContentClass]),
85 div([id(contents), class([contents, ContentClass])],
86 Body))
87 ],
88 Options).
89user:body(Style, Body) -->
90 { Style = forum(_) }, !,
91 outer_container(
92 [ \title_area(Style),
93 \menubar(Style),
94 div(class(breadcrumb), []),
95 Body
96 ],
97 []).
98user:body(plain, Body) --> !,
99 html(body(class(plain), Body)).
100user:body(default, Body) --> !,
101 html(body(class(plain), Body)).
102user:body(Style, _Body) -->
103 html(div('Unknown page style ~q'-[Style])).
109page_style(user(_Action), [show_user(false)]).
110page_style(download(_Dir, _Title), []).
111page_style(dir_index(_Dir, _Title),[]).
112page_style(news(_Which), []).
113page_style(wiki(_Special), []).
114page_style(wiki(Path, _Title), [object(wiki(Path))]).
115page_style(blog(_Special), []).
116page_style(blog(Path, _Title), [object(blog(Path))]).
117page_style(pack(_Action), []).
118page_style(tags(_Action), []).
119page_style(pldoc(object(Obj)), [object(Obj)]) :- !.
120page_style(pldoc(search(For)), [for(For)]) :- !.
121page_style(pldoc(_), []).
122page_style(pack(_Type, _Title), []).
123page_style(git(_), []).
129outer_container(Content, Options) -->
130 html(body(div(class('outer-container'),
131 [ \html_requires(plweb),
132 \html_requires(swipl_css),
133 \shortcut_icons,
134 \upper_header(Options),
135 Content,
136 div([id(dialog),style('display:none;')], []),
137 div(class([footer, newstyle]), \footer(Options)),
138 div(id('tail-end'), &(nbsp))
139 ]))),
140 html_receive(script).
150:- multifile
151 prolog:doc_page_header//2,
152 prolog:doc_links//2. 153
154prolog:(_File, _Options) --> [].
155prolog:doc_links(_Directory, _Options) --> [].
156prolog:doc_file_title(_Title, _File, _Options) --> [].
157
158shortcut_icons -->
159 { http_absolute_location(icons('favicon.ico'), FavIcon, []),
160 http_absolute_location(root('apple-touch-icon.png'), TouchIcon, [])
161 },
162 html_post(head,
163 [ link([ rel('shortcut icon'), href(FavIcon) ]),
164 link([ rel('apple-touch-icon'), href(TouchIcon) ])
165 ]).
171upper_header(Options) -->
172 { http_link_to_id(plweb_search, [], Action),
173 option(for(Search), Options, '')
174 },
175 html(div(id('upper-header'),
176 table(id('upper-header-contents'),
177 tr([ td(id('dyknow-container'),
178 \did_you_know_script('dyknow-container')),
179 td(id('search-container'),
180 [ span(class(lbl), 'Search Documentation:'),
181 form([action(Action),id('search-form')],
182 [ input([ name(for),
183 id(for),
184 value(Search)
185 ], []),
186 input([ id('submit-for'),
187 type(submit),
188 value('Search')
189 ], []),
190 \searchbox_script(for)
191 ])
192 ])
193 ])))).
199plweb_search(Request) :-
200 http_parameters(
201 Request,
202 [ for(For,
203 [ default(''),
204 description('String to search for')
205 ]),
206 in(In,
207 [ oneof([all,app,noapp,man,lib,pack,wiki]),
208 default(noapp),
209 description('Search everying, application only \c
210 or manual only')
211 ]),
212 match(Match,
213 [ oneof([name,summary]),
214 default(summary),
215 description('Match only the name or also the summary')
216 ]),
217 resultFormat(Format,
218 [ oneof(long,summary),
219 default(summary),
220 description('Return full documentation \c
221 or summary-lines')
222 ]),
223 page(Page,
224 [ integer,
225 default(1),
226 description('Page of search results to view')
227 ])
228
229 ]),
230 format(string(Title), 'Prolog search -- ~w', [For]),
231 reply_html_page(pldoc(search(For)),
232 title(Title),
233 \search_reply(For,
234 [ resultFormat(Format),
235 search_in(In),
236 search_match(Match),
237 header(false),
238 private(false),
239 edit(false),
240 page(Page)
241 ])).
248searchbox_script(Tag) -->
249 html([
250 \html_requires(jquery_ui),
251 script(type('text/javascript'), {|javascript(Tag)||
252 $(function() {
253 function htmlEncode(text) {
254 if ( !text ) return "";
255 return document.createElement('a')
256 .appendChild(document.createTextNode(text))
257 .parentNode
258 .innerHTML;
259 }
260 $("#"+Tag).autocomplete({
261 minLength: 1,
262 delay: 0.3,
263 source: "/autocomplete/ac_predicate",
264 focus: function(event,ui) {
265 $("#"+Tag).val(ui.item.label);
266 return false;
267 },
268 select: function(event,ui) {
269 $("#"+Tag).val(ui.item.label);
270 window.location.href = ui.item.href;
271 return false;
272 }
273 })
274 .data("ui-autocomplete")._renderItem = function(ul,item) {
275 var label = String(htmlEncode(item.label)).replace(
276 htmlEncode(this.term),
277 "<span class=\"acmatch\">"+this.term+"</span>");
278 var tag = item.tag ? " <i>["+item.tag+"]</i>" : "";
279 return $("<li>")
280 .append("<a class=\""+item.class+"\">"+label+tag+"</a>")
281 .appendTo(ul)
282 };
283 });
284|})]).
291tag_line_area -->
292 html(div(id('tag-line-area'),
293 [ \swi_logo,
294 span(class(tagline),
295 [ 'Robust, mature, free. ',
296 b('Prolog for the real world.')
297 ])
298 ])).
303title_area(pldoc(file(File, Title))) --> !,
304 { file_base_name(File, Base) },
305 html([ table(id('header-line-area'),
306 tr([ td(id('logo'), \swi_logo),
307 td(class('primary-header'),
308 \page_title(title(Title)))
309 ])),
310 div([ class('file-buttons')
311 ],
312 [ \zoom_button(Base, []),
313 \source_button(Base, [])
314 ])
315 ]).
316title_area(Style) -->
317 html(table(id('header-line-area'),
318 tr([ td(id('logo'), \swi_logo),
319 td(class('primary-header'),
320 \page_title(Style))
321 ]))).
322
323page_title(For) -->
324 plweb:page_title(For), !.
325page_title(pldoc(search(''))) --> !,
326 html('How to use the search box').
327page_title(pldoc(search(For))) --> !,
328 html(['Search results for ', span(class(for), ['"', For, '"'])]).
329page_title(pldoc(object(Obj))) -->
330 object_name(Obj,
331 [ style(title)
332 ]), !.
333page_title(title(Title)) --> !,
334 html(Title).
335page_title(user(login)) --> !,
336 html('Login to www.swi-prolog.org').
337page_title(user(logout)) --> !,
338 html('Logged out from www.swi-prolog.org').
339page_title(user(create_profile)) --> !,
340 html('Create user profile').
341page_title(user(view_profile(UUID))) --> !,
342 { site_user_property(UUID, name(Name)) },
343 html('Profile for user ~w'-[Name]).
344page_title(user(list)) --> !,
345 html('Registered site users').
346page_title(news(fresh)) --> !,
347 html('News').
348page_title(news(all)) --> !,
349 html('News archive').
350page_title(news(Id)) -->
351 { post(Id, title, Title) },
352 html(Title).
353page_title(pack(list)) -->
354 html('Packs (add-ons) for SWI-Prolog').
355page_title(wiki(sandbox)) -->
356 html('PlDoc wiki sandbox').
357page_title(wiki(edit(Action, Location))) -->
358 html([Action, ' wiki page ', Location]).
359page_title(wiki(_Path, Title)) -->
360 html(Title).
361page_title(blog(index)) -->
362 html('SWI-Prolog blog -- index').
363page_title(blog(_Path, Title)) -->
364 html(Title).
365page_title(tags(list)) -->
366 html('Tags').
367page_title(download(_Dir, Title)) -->
368 html(Title).
369page_title(dir_index(_Dir, Title)) -->
370 html(Title).
371page_title(Term) -->
372 html('Title for ~q'-[Term]).
381todays_logo('christmas.png', 'Merry Christmas.') :-
382 todays_holiday(christmas).
383todays_logo('koningsdag.png', 'Kings day in the Netherlands') :-
384 todays_holiday(koningsdag).
385todays_logo('santiklaas.png', 'St. Nicholas\' eve in the Netherlands') :-
386 todays_holiday(santiklaas).
387todays_logo('carnivalswipl.png', 'Carnival in the Netherlands') :-
388 todays_holiday(carnival).
389todays_logo('halloween.png', 'Hoooo.... on Halloween') :-
390 todays_holiday(halloween).
391todays_logo('chinesenewyear.png', 'Chinese New Year') :-
392 todays_holiday(chinese_new_year).
393todays_logo('liberationday.png', 'Liberation Day in the Netherlands') :-
394 todays_holiday(liberation_day).
395todays_logo('swipl.png', 'SWI-Prolog owl logo') :-
396 todays_holiday(_).
402swi_logo -->
403 { todays_logo(File, Alt),
404 http_absolute_location(icons(File), Logo, [])
405 },
406 html(a(href('http://www.swi-prolog.org'),
407 img([ class(owl),
408 src(Logo),
409 alt(Alt),
410 title(Alt)
411 ], []))).
418menubar(Style) -->
419 { menu(Style, Menu) },
420 html_requires(jquery),
421 html_requires(jq('menu.js')),
422 html(div(id(menubar),
423 div(class([menubar, 'fixed-width']),
424 ul(class('menubar-container'),
425 \menu(Menu, 1))))).
426
([], _) --> !.
428menu([H|T], Level) --> !, menu(H, Level), menu(T, Level).
429menu(Label = Link + SubMenu, Level) --> !,
430 submenu(Label, Level, SubMenu, Link).
431menu(Label = SubMenu, Level) -->
432 { is_list(SubMenu)
433 }, !,
434 submenu(Label, Level, SubMenu, -).
435menu(Label = Link, _) -->
436 { atom(Link),
437 uri_is_global(Link), !,
438 http_absolute_location(icons('ext-link.png'), IMG, [])
439 }, !,
440 html(li([ a(href(Link),
441 [ Label,
442 img([ class('ext-link'),
443 src(IMG),
444 alt('External')
445 ])
446 ])
447 ])).
448menu(_Label = (-), _) --> !,
449 [].
450menu(Label = Link, 1) -->
451 { upcase_atom(Label, LABEL) },
452 html(li(a(href(Link), LABEL))).
453menu(Label = Link, _) -->
454 html(li(a(href(Link), Label))).
455
(Label, Level, SubMenu, -) --> !,
457 { SubLevel is Level+1 },
458 html(li([ \submenu_label(Label, Level),
459 ul(\menu(SubMenu, SubLevel))
460 ])).
461submenu(Label, Level, SubMenu, HREF) -->
462 { SubLevel is Level+1 },
463 html(li([ a(href(HREF), \submenu_label(Label, Level)),
464 ul(\menu(SubMenu, SubLevel))
465 ])).
466
(Label, Level) -->
468 { Level =< 1,
469 upcase_atom(Label, LABEL)
470 }, !,
471 html(LABEL).
472submenu_label(Label, _) -->
473 html([Label, span(class(arrow), &('#x25B6'))]).
474
475
(Style,
477 [ 'Home' = '/',
478 'Download' =
479 [ 'SWI-Prolog' = '/Download.html',
480 'Sources/building' = '/build/',
481 'Docker images' = '/Docker.html',
482 'Add-ons' = '/pack/list',
483 'Browse GIT' = 'https://github.com/SWI-Prolog'
484 ],
485 'Documentation' =
486 [ 'Manual' = '/pldoc/refman/',
487 'Packages' = '/pldoc/package/',
488 'FAQ' = '/FAQ/',
489 'Command line' = '/pldoc/man?section=cmdline',
490 'PlDoc' = '/pldoc/package/pldoc.html',
491 'Bluffers' =
492 [ 'Prolog syntax' = '/pldoc/man?section=syntax',
493 'PceEmacs' = '/pldoc/man?section=emacsbluff',
494 'HTML generation' = '/pldoc/man?section=htmlwrite'
495 ],
496 'License' = '/license.html',
497 'Publications' = '/Publications.html',
498 'Rev 7 Extensions' = '/pldoc/man?section=extensions'
499 ],
500 'Tutorials' =
501 [ 'Beginner' =
502 [ 'Getting started' = '/pldoc/man?section=quickstart',
503 'Learn Prolog Now!' = 'http://lpn.swi-prolog.org/',
504 'Simply Logical' = 'http://book.simply-logical.space/',
505 'Debugger' = '/pldoc/man?section=debugoverview',
506 'Development tools' = '/IDE.html'
507 ],
508 'Advanced' =
509 [ 'Modules' = 'http://chiselapp.com/user/ttmrichter/repository/gng/doc/trunk/output/tutorials/swiplmodtut.html',
510 'Grammars (DCGs)' = 'https://www.github.com/Anniepoo/swipldcgtut/blob/master/dcgcourse.adoc',
511 'clp(fd)' = 'https://www.github.com/Anniepoo/swiplclpfd/blob/master/clpfd.adoc',
512 'Printing messages' = 'https://www.github.com/Anniepoo/swiplmessage/blob/master/message.adoc',
513 'PlDoc' = 'http://chiselapp.com/user/ttmrichter/repository/swipldoctut/doc/tip/doc/tutorial.html'
514 ],
515 'Web applications' =
516 [ 'Web applications' = 'https://www.github.com/Anniepoo/swiplwebtut/blob/master/web.adoc',
517 'Let\'s Encrypt!' = 'https://github.com/triska/letswicrypt',
518 'Pengines' = '/pengines/'
519 ],
520 'Semantic web' =
521 [ 'ClioPatria' = 'https://cliopatria.swi-prolog.org/tutorial/',
522 'RDF namespaces' = '/howto/UseRdfMeta.html'
523 ],
524 'Graphics' =
525 [ 'XPCE' = '/download/xpce/doc/coursenotes/coursenotes.pdf',
526 'GUI options' = '/Graphics.html'
527 ],
528 'Machine learning' =
529 [ 'Probabilistic Logic Programming' =
530 'http://cplint.ml.unife.it/'
531 ],
532 'External collections' =
533 [ 'Meta level tutorials' = 'https://www.metalevel.at/prolog'
534 ],
535 'For packagers' =
536 [ 'Linux packages' = '/build/guidelines.html'
537 ]
538 ],
539 'Community' = '/community.html' +
540 [ 'IRC' = 'https://web.libera.chat/?channels=##prolog',
541 'Forum & mailing list'= 'https://swi-prolog.discourse.group',
542 'Blog' = '/blog',
543 'News' = '/news/archive',
544 'Report a bug' = '/bug.html',
545 'Submit a patch' = '/howto/SubmitPatch.html',
546 'Submit an add-on' = '/howto/Pack.html',
547 'Roadmap (on GitHub)' = 'https://github.com/SWI-Prolog/roadmap',
548 'External links' = '/Links.html',
549 'Contributing' = '/contributing.html',
550 'Code of Conduct' = '/Code-of-Conduct.html',
551 'Contributors' = '/Contributors.html',
552 'SWI-Prolog items' = '/loot.html'
553 ],
554 'Users' =
555 [ 'Semantic web' = '/web/index.html',
556 'Students' = '/students/index.html',
557 'Researchers' = '/research/index.html',
558 'Commercial users' = '/commercial/index.html',
559 'Dog food' = '/dogfood.html',
560 'Is SWIPL right for me?' = '/pldoc/man?section=swiorother'
561 ],
562 'Wiki' =
563 [ LoginLabel = LoginURL,
564 'Edit this page' = EditHREF,
565 'View changes' = '/wiki/changes',
566 'Sandbox' = '/wiki/sandbox',
567 'Wiki help' = '/wiki/',
568 'All tags' = '/list-tags'
569 ]
570 ]) :-
571 http_current_request(Request),
572 memberchk(request_uri(ReqURL), Request),
573 ( functor(Style, wiki, _)
574 -> http_link_to_id(wiki_edit,
575 [location(ReqURL)], EditHREF)
576 ; EditHREF = (-)
577 ),
578 ( site_user_logged_in(_)
579 -> LoginLabel = 'Logout',
580 http_link_to_id(logout, ['openid.return_to'(ReqURL)], LoginURL)
581 ; LoginLabel = 'Login',
582 ( http_link_to_id(logout, [], ReqURL)
583 -> RetURL = '/' 584 ; RetURL = ReqURL
585 ),
586 http_link_to_id(plweb_login_page,
587 ['openid.return_to'(RetURL)], LoginURL)
588 ).
594blurb -->
595 html({|html||
596 <div id="blurb">
597 <div>
598 SWI-Prolog offers a comprehensive free Prolog environment.
599 Since its start in 1987, SWI-Prolog development has been driven
600 by the needs of real world applications. SWI-Prolog is widely
601 used in research and education as well as commercial applications.
602 Join over a million users who have downloaded SWI-Prolog.
603 <a href="/features.html">more ...</a>
604 </div>
605 </div>
606 |}).
612cta_area -->
613 html_post(head,
614 script([ defer(defer), async(async),
615 src('https://buttons.github.io/buttons.js')
616 ], [])),
617 html({|html(_)||
618 <table id='cta-container'>
619 <tr>
620 <td style="text-align:left; vertical-align: top">
621 <a href="Download.html">Download SWI-Prolog</a>
622 <td style="text-align:center; vertical-align: top">
623 <a href="GetStarted.html">Get Started</a>
624 <td style="text-align:right; white-space: nowrap; vertical-align: top">
625 <a href="http://swish.swi-prolog.org">
626 Try SWI-Prolog online (SWISH) </a><br>
627 <a href="http://dev.swi-prolog.org/wasm/shell"
628 style="font-size: 60%">
629 🔥 Try SWI-Prolog in your browser (WASM)</a><br>
630 </tr>
631 </table>
632|}),
633 html(div(id('cta-github'),
634 [ a([ class('github-button'), id('github-star'),
635 href('https://github.com/SWI-Prolog/swipl-devel'),
636 'data-color-scheme'('no-preference: light; \c
637 light: light; dark: dark;'),
638 'data-size'(large),
639 'data-show-count'(true),
640 'aria-label'('Star SWI-Prolog/swipl-devel on GitHub')
641 ], 'Star'),
642 a([ class('github-button'), id('github-sponsor'),
643 href('https://github.com/sponsors/SWI-Prolog'),
644 'data-color-scheme'('no-preference: light; \c
645 light: light; dark: dark;'),
646 'data-size'(large),
647 'data-icon'('octicon-heart'),
648 'data-show-count'(true),
649 'aria-label'('Sponsor @SWI-Prolog on GitHub')
650 ], 'Sponsor')
651 ])).
658enhanced_search_area -->
659 { http_link_to_id(plweb_search, [], Action) },
660 html({|html(Action)||
661 <div id='enhanced-search-container'>
662 <div>
663 <span class='lbl'>SEARCH DOCUMENTATION:</span>
664 <form id="search-form-enhanced" action="Action">
665 <input name="for" type='text' id="forenhanced">
666 <input type="image" src="/icons/go.png" alt='Search'>
667 </form>
668 </div>
669 </div>|}),
670 searchbox_script(forenhanced)