36
37:- module(html_write,
38 [ reply_html_page/2, 39 reply_html_page/3, 40 reply_html_partial/1, 41
42 43 page//1, 44 page//2, 45 page//3, 46 html//1, 47
48 49 html_set_options/1, 50 html_current_option/1, 51
52 53 html_post//2, 54 html_receive//1, 55 html_receive//2, 56 xhtml_ns//2, 57 html_root_attribute//2, 58
59 html/4, 60
61 62 html_begin//1, 63 html_end//1, 64 html_quoted//1, 65 html_quoted_attribute//1, 66
67 68 print_html/1, 69 print_html/2, 70 html_print_length/2, 71
72 73 (html_meta)/1, 74 op(1150, fx, html_meta)
75 ]). 76:- use_module(html_quasiquotations, [html/4]). 77:- autoload(library(apply),[maplist/3,maplist/4]). 78:- use_module(library(debug),[debug/3]). 79:- autoload(library(error),
80 [must_be/2,domain_error/2,instantiation_error/1]). 81:- autoload(library(lists),
82 [permutation/2,selectchk/3,append/3,select/4,list_to_set/2]). 83:- autoload(library(option),[option/2]). 84:- autoload(library(pairs),[group_pairs_by_key/2]). 85:- autoload(library(sgml),[xml_quote_cdata/3,xml_quote_attribute/3]). 86:- autoload(library(uri),[uri_encoded/3]). 87:- autoload(library(url),[www_form_encode/2]). 88:- if(exists_source(library(http/http_dispatch))). 89:- autoload(library(http/http_dispatch), [http_location_by_id/2]). 90:- endif. 91
93:- set_prolog_flag(generate_debug_info, false). 94
95:- meta_predicate
96 reply_html_page(+, :, :),
97 reply_html_page(:, :),
98 html(:, -, +),
99 page(:, -, +),
100 page(:, :, -, +),
101 pagehead(+, :, -, +),
102 pagebody(+, :, -, +),
103 html_receive(+, 3, -, +),
104 html_post(+, :, -, +). 105
106:- multifile
107 expand//1, 108 expand_attribute_value//1, 109 html_header_hook/1. 110
111
144
145
146 149
173
174html_set_options(Options) :-
175 must_be(list, Options),
176 set_options(Options).
177
178set_options([]).
179set_options([H|T]) :-
180 html_set_option(H),
181 set_options(T).
182
183html_set_option(dialect(Dialect0)) :-
184 !,
185 must_be(oneof([html,html4,xhtml,html5]), Dialect0),
186 ( html_version_alias(Dialect0, Dialect)
187 -> true
188 ; Dialect = Dialect0
189 ),
190 set_prolog_flag(html_dialect, Dialect).
191html_set_option(doctype(Atom)) :-
192 !,
193 must_be(atom, Atom),
194 current_prolog_flag(html_dialect, Dialect),
195 dialect_doctype_flag(Dialect, Flag),
196 set_prolog_flag(Flag, Atom).
197html_set_option(content_type(Atom)) :-
198 !,
199 must_be(atom, Atom),
200 current_prolog_flag(html_dialect, Dialect),
201 dialect_content_type_flag(Dialect, Flag),
202 set_prolog_flag(Flag, Atom).
203html_set_option(O) :-
204 domain_error(html_option, O).
205
206html_version_alias(html, html4).
207
211
212html_current_option(dialect(Dialect)) :-
213 current_prolog_flag(html_dialect, Dialect).
214html_current_option(doctype(DocType)) :-
215 current_prolog_flag(html_dialect, Dialect),
216 dialect_doctype_flag(Dialect, Flag),
217 current_prolog_flag(Flag, DocType).
218html_current_option(content_type(ContentType)) :-
219 current_prolog_flag(html_dialect, Dialect),
220 dialect_content_type_flag(Dialect, Flag),
221 current_prolog_flag(Flag, ContentType).
222
223dialect_doctype_flag(html4, html4_doctype).
224dialect_doctype_flag(html5, html5_doctype).
225dialect_doctype_flag(xhtml, xhtml_doctype).
226
227dialect_content_type_flag(html4, html4_content_type).
228dialect_content_type_flag(html5, html5_content_type).
229dialect_content_type_flag(xhtml, xhtml_content_type).
230
231option_default(html_dialect, html5).
232option_default(html4_doctype,
233 'HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" \c
234 "http://www.w3.org/TR/html4/loose.dtd"').
235option_default(html5_doctype,
236 'html').
237option_default(xhtml_doctype,
238 'html PUBLIC "-//W3C//DTD XHTML 1.0 \c
239 Transitional//EN" \c
240 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"').
241option_default(html4_content_type, 'text/html; charset=UTF-8').
242option_default(html5_content_type, 'text/html; charset=UTF-8').
243option_default(xhtml_content_type, 'application/xhtml+xml; charset=UTF-8').
244
248
249init_options :-
250 ( option_default(Name, Value),
251 ( current_prolog_flag(Name, _)
252 -> true
253 ; create_prolog_flag(Name, Value, [])
254 ),
255 fail
256 ; true
257 ).
258
259:- init_options. 260
264
('<?xml version=\'1.0\' encoding=\'UTF-8\'?>').
266
270
271ns(xhtml, 'http://www.w3.org/1999/xhtml').
272
273
274 277
284
285page(Content) -->
286 doctype,
287 html(html(Content)).
288
289page(Head, Body) -->
290 page(default, Head, Body).
291
292page(Style, Head, Body) -->
293 doctype,
294 content_type,
295 html_begin(html),
296 pagehead(Style, Head),
297 pagebody(Style, Body),
298 html_end(html).
299
306
307doctype -->
308 { html_current_option(doctype(DocType)),
309 DocType \== ''
310 },
311 !,
312 [ '<!DOCTYPE ', DocType, '>' ].
313doctype -->
314 [].
315
316content_type -->
317 { html_current_option(content_type(Type))
318 },
319 !,
320 html_post(head, meta([ 'http-equiv'('content-type'),
321 content(Type)
322 ], [])).
323content_type -->
324 { html_current_option(dialect(html5)) },
325 !,
326 html_post(head, meta('charset=UTF-8')).
327content_type -->
328 [].
329
330pagehead(_, Head) -->
331 { functor(Head, head, _)
332 },
333 !,
334 html(Head).
335pagehead(Style, Head) -->
336 { strip_module(Head, M, _),
337 hook_module(M, HM, head//2)
338 },
339 HM:head(Style, Head),
340 !.
341pagehead(_, Head) -->
342 { strip_module(Head, M, _),
343 hook_module(M, HM, head//1)
344 },
345 HM:head(Head),
346 !.
347pagehead(_, Head) -->
348 html(head(Head)).
349
350
351pagebody(_, Body) -->
352 { functor(Body, body, _)
353 },
354 !,
355 html(Body).
356pagebody(Style, Body) -->
357 { strip_module(Body, M, _),
358 hook_module(M, HM, body//2)
359 },
360 HM:body(Style, Body),
361 !.
362pagebody(_, Body) -->
363 { strip_module(Body, M, _),
364 hook_module(M, HM, body//1)
365 },
366 HM:body(Body),
367 !.
368pagebody(_, Body) -->
369 html(body(Body)).
370
371
372hook_module(M, M, PI) :-
373 current_predicate(M:PI),
374 !.
375hook_module(_, user, PI) :-
376 current_predicate(user:PI).
377
382
383html(Spec) -->
384 { strip_module(Spec, M, T) },
385 qhtml(T, M).
386
387qhtml(Var, _) -->
388 { var(Var),
389 !,
390 instantiation_error(Var)
391 }.
392qhtml([], _) -->
393 !,
394 [].
395qhtml([H|T], M) -->
396 !,
397 html_expand(H, M),
398 qhtml(T, M).
399qhtml(X, M) -->
400 html_expand(X, M).
401
402html_expand(Var, _) -->
403 { var(Var),
404 !,
405 instantiation_error(Var)
406 }.
407html_expand(Term, Module) -->
408 do_expand(Term, Module),
409 !.
410html_expand(Term, _Module) -->
411 { print_message(error, html(expand_failed(Term))) }.
412
413
414do_expand(Token, _) --> 415 expand(Token),
416 !.
417do_expand(Fmt-Args, _) -->
418 !,
419 { format(string(String), Fmt, Args)
420 },
421 html_quoted(String).
422do_expand(\List, Module) -->
423 { is_list(List)
424 },
425 !,
426 raw(List, Module).
427do_expand(\Term, Module, In, Rest) :-
428 !,
429 call(Module:Term, In, Rest).
430do_expand(Module:Term, _) -->
431 !,
432 qhtml(Term, Module).
433do_expand(&(Entity), _) -->
434 !,
435 { integer(Entity)
436 -> format(string(String), '&#~d;', [Entity])
437 ; format(string(String), '&~w;', [Entity])
438 },
439 [ String ].
440do_expand(Token, _) -->
441 { atomic(Token)
442 },
443 !,
444 html_quoted(Token).
445do_expand(element(Env, Attributes, Contents), M) -->
446 !,
447 ( { Contents == [],
448 html_current_option(dialect(xhtml))
449 }
450 -> xhtml_empty(Env, Attributes)
451 ; html_begin(Env, Attributes),
452 qhtml(Env, Contents, M),
453 html_end(Env)
454 ).
455do_expand(Term, M) -->
456 { Term =.. [Env, Contents]
457 },
458 !,
459 ( { layout(Env, _, empty)
460 }
461 -> html_begin(Env, Contents)
462 ; ( { Contents == [],
463 html_current_option(dialect(xhtml))
464 }
465 -> xhtml_empty(Env, [])
466 ; html_begin(Env),
467 qhtml(Env, Contents, M),
468 html_end(Env)
469 )
470 ).
471do_expand(Term, M) -->
472 { Term =.. [Env, Attributes, Contents],
473 check_non_empty(Contents, Env, Term)
474 },
475 !,
476 ( { Contents == [],
477 html_current_option(dialect(xhtml))
478 }
479 -> xhtml_empty(Env, Attributes)
480 ; html_begin(Env, Attributes),
481 qhtml(Env, Contents, M),
482 html_end(Env)
483 ).
484
485qhtml(Env, Contents, M) -->
486 { cdata_element(Env),
487 phrase(cdata(Contents, M), Tokens)
488 },
489 !,
490 [ cdata(Env, Tokens) ].
491qhtml(_, Contents, M) -->
492 qhtml(Contents, M).
493
494
495check_non_empty([], _, _) :- !.
496check_non_empty(_, Tag, Term) :-
497 layout(Tag, _, empty),
498 !,
499 print_message(warning,
500 format('Using empty element with content: ~p', [Term])).
501check_non_empty(_, _, _).
502
503cdata(List, M) -->
504 { is_list(List) },
505 !,
506 raw(List, M).
507cdata(One, M) -->
508 raw_element(One, M).
509
513
514raw([], _) -->
515 [].
516raw([H|T], Module) -->
517 raw_element(H, Module),
518 raw(T, Module).
519
520raw_element(Var, _) -->
521 { var(Var),
522 !,
523 instantiation_error(Var)
524 }.
525raw_element(\List, Module) -->
526 { is_list(List)
527 },
528 !,
529 raw(List, Module).
530raw_element(\Term, Module, In, Rest) :-
531 !,
532 call(Module:Term, In, Rest).
533raw_element(Module:Term, _) -->
534 !,
535 raw_element(Term, Module).
536raw_element(Fmt-Args, _) -->
537 !,
538 { format(string(S), Fmt, Args) },
539 [S].
540raw_element(Value, _) -->
541 { must_be(atomic, Value) },
542 [Value].
543
544
562
563html_begin(Env) -->
564 { Env =.. [Name|Attributes]
565 },
566 html_begin(Name, Attributes).
567
568html_begin(Env, Attributes) -->
569 pre_open(Env),
570 [<],
571 [Env],
572 attributes(Env, Attributes),
573 ( { layout(Env, _, empty),
574 html_current_option(dialect(xhtml))
575 }
576 -> ['/>']
577 ; [>]
578 ),
579 post_open(Env).
580
581html_end(Env) --> 582 { layout(Env, _, -),
583 html_current_option(dialect(html))
584 ; layout(Env, _, empty)
585 },
586 !,
587 [].
588html_end(Env) -->
589 pre_close(Env),
590 ['</'],
591 [Env],
592 ['>'],
593 post_close(Env).
594
598
599xhtml_empty(Env, Attributes) -->
600 pre_open(Env),
601 [<],
602 [Env],
603 attributes(Attributes),
604 ['/>'].
605
628
629xhtml_ns(Id, Value) -->
630 { html_current_option(dialect(xhtml)) },
631 !,
632 html_post(xmlns, \attribute(xmlns:Id=Value)).
633xhtml_ns(_, _) -->
634 [].
635
646
647html_root_attribute(Name, Value) -->
648 html_post(html_begin, \attribute(Name=Value)).
649
654
655attributes(html, L) -->
656 !,
657 ( { html_current_option(dialect(xhtml)) }
658 -> ( { option(xmlns(_), L) }
659 -> attributes(L)
660 ; { ns(xhtml, NS) },
661 attributes([xmlns(NS)|L])
662 ),
663 html_receive(xmlns)
664 ; attributes(L),
665 html_noreceive(xmlns)
666 ),
667 html_receive(html_begin).
668attributes(_, L) -->
669 attributes(L).
670
671attributes([]) -->
672 !,
673 [].
674attributes([H|T]) -->
675 !,
676 attribute(H),
677 attributes(T).
678attributes(One) -->
679 attribute(One).
680
681attribute(Name=Value) -->
682 !,
683 [' '], name(Name), [ '="' ],
684 attribute_value(Value),
685 ['"'].
686attribute(NS:Term) -->
687 !,
688 { Term =.. [Name, Value]
689 },
690 !,
691 attribute((NS:Name)=Value).
692attribute(Term) -->
693 { Term =.. [Name, Value]
694 },
695 !,
696 attribute(Name=Value).
697attribute(Atom) --> 698 { atom(Atom)
699 },
700 [ ' ', Atom ].
701
702name(NS:Name) -->
703 !,
704 [NS, :, Name].
705name(Name) -->
706 [ Name ].
707
727
728attribute_value(List) -->
729 { is_list(List) },
730 !,
731 attribute_value_m(List).
732attribute_value(Value) -->
733 attribute_value_s(Value).
734
736
737attribute_value_s(Var) -->
738 { var(Var),
739 !,
740 instantiation_error(Var)
741 }.
742attribute_value_s(A+B) -->
743 !,
744 attribute_value(A),
745 ( { is_list(B) }
746 -> ( { B == [] }
747 -> []
748 ; [?], search_parameters(B)
749 )
750 ; attribute_value(B)
751 ).
752attribute_value_s(encode(Value)) -->
753 !,
754 { uri_encoded(query_value, Value, Encoded) },
755 [ Encoded ].
756attribute_value_s(Value) -->
757 expand_attribute_value(Value),
758 !.
759attribute_value_s(Fmt-Args) -->
760 !,
761 { format(string(Value), Fmt, Args) },
762 html_quoted_attribute(Value).
763attribute_value_s(Value) -->
764 html_quoted_attribute(Value).
765
766search_parameters([H|T]) -->
767 search_parameter(H),
768 ( {T == []}
769 -> []
770 ; ['&'],
771 search_parameters(T)
772 ).
773
774search_parameter(Var) -->
775 { var(Var),
776 !,
777 instantiation_error(Var)
778 }.
779search_parameter(Name=Value) -->
780 { www_form_encode(Value, Encoded) },
781 [Name, =, Encoded].
782search_parameter(Term) -->
783 { Term =.. [Name, Value],
784 !,
785 www_form_encode(Value, Encoded)
786 },
787 [Name, =, Encoded].
788search_parameter(Term) -->
789 { domain_error(search_parameter, Term)
790 }.
791
801
802attribute_value_m([]) -->
803 [].
804attribute_value_m([H|T]) -->
805 attribute_value_s(H),
806 ( { T == [] }
807 -> []
808 ; [' '],
809 attribute_value_m(T)
810 ).
811
812
813 816
829
830html_quoted(Text) -->
831 { xml_quote_cdata(Text, Quoted, utf8) },
832 [ Quoted ].
833
842
843html_quoted_attribute(Text) -->
844 { xml_quote_attribute(Text, Quoted, utf8) },
845 [ Quoted ].
846
851
852cdata_element(script).
853cdata_element(style).
854
855
856 859
889
890html_post(Id, Content) -->
891 { strip_module(Content, M, C) },
892 [ mailbox(Id, post(M, C)) ].
893
904
905html_receive(Id) -->
906 html_receive(Id, sorted_html).
907
924
925html_receive(Id, Handler) -->
926 { strip_module(Handler, M, P) },
927 [ mailbox(Id, accept(M:P, _)) ].
928
932
933html_noreceive(Id) -->
934 [ mailbox(Id, ignore(_,_)) ].
935
944
945mailman(Tokens) :-
946 ( html_token(mailbox(_, accept(_, Accepted)), Tokens)
947 -> true
948 ),
949 var(Accepted), 950 !,
951 mailboxes(Tokens, Boxes),
952 keysort(Boxes, Keyed),
953 group_pairs_by_key(Keyed, PerKey),
954 move_last(PerKey, script, PerKey1),
955 move_last(PerKey1, head, PerKey2),
956 ( permutation(PerKey2, PerKeyPerm),
957 ( mail_ids(PerKeyPerm)
958 -> !
959 ; debug(html(mailman),
960 'Failed mail delivery order; retrying', []),
961 fail
962 )
963 -> true
964 ; print_message(error, html(cyclic_mailboxes))
965 ).
966mailman(_).
967
968move_last(Box0, Id, Box) :-
969 selectchk(Id-List, Box0, Box1),
970 !,
971 append(Box1, [Id-List], Box).
972move_last(Box, _, Box).
973
978
979html_token(Token, [H|T]) :-
980 html_token_(T, H, Token).
981
982html_token_(_, Token, Token) :- !.
983html_token_(_, cdata(_,Tokens), Token) :-
984 html_token(Token, Tokens).
985html_token_([H|T], _, Token) :-
986 html_token_(T, H, Token).
987
991
992mailboxes(Tokens, MailBoxes) :-
993 mailboxes(Tokens, MailBoxes, []).
994
995mailboxes([], List, List).
996mailboxes([mailbox(Id, Value)|T0], [Id-Value|T], Tail) :-
997 !,
998 mailboxes(T0, T, Tail).
999mailboxes([cdata(_Type, Tokens)|T0], Boxes, Tail) :-
1000 !,
1001 mailboxes(Tokens, Boxes, Tail0),
1002 mailboxes(T0, Tail0, Tail).
1003mailboxes([_|T0], T, Tail) :-
1004 mailboxes(T0, T, Tail).
1005
1006mail_ids([]).
1007mail_ids([H|T0]) :-
1008 mail_id(H, NewPosts),
1009 add_new_posts(NewPosts, T0, T),
1010 mail_ids(T).
1011
1012mail_id(Id-List, NewPosts) :-
1013 mail_handlers(List, Boxes, Content),
1014 ( Boxes = [accept(MH:Handler, In)]
1015 -> extend_args(Handler, Content, Goal),
1016 phrase(MH:Goal, In),
1017 mailboxes(In, NewBoxes),
1018 keysort(NewBoxes, Keyed),
1019 group_pairs_by_key(Keyed, NewPosts)
1020 ; Boxes = [ignore(_, _)|_]
1021 -> NewPosts = []
1022 ; Boxes = [accept(_,_),accept(_,_)|_]
1023 -> print_message(error, html(multiple_receivers(Id))),
1024 NewPosts = []
1025 ; print_message(error, html(no_receiver(Id))),
1026 NewPosts = []
1027 ).
1028
1029add_new_posts([], T, T).
1030add_new_posts([Id-Posts|NewT], T0, T) :-
1031 ( select(Id-List0, T0, Id-List, T1)
1032 -> append(List0, Posts, List)
1033 ; debug(html(mailman), 'Stuck with new posts on ~q', [Id]),
1034 fail
1035 ),
1036 add_new_posts(NewT, T1, T).
1037
1038
1044
1045mail_handlers([], [], []).
1046mail_handlers([post(Module,HTML)|T0], H, [Module:HTML|T]) :-
1047 !,
1048 mail_handlers(T0, H, T).
1049mail_handlers([H|T0], [H|T], C) :-
1050 mail_handlers(T0, T, C).
1051
1052extend_args(Term, Extra, NewTerm) :-
1053 Term =.. [Name|Args],
1054 append(Args, [Extra], NewArgs),
1055 NewTerm =.. [Name|NewArgs].
1056
1065
1066sorted_html(List) -->
1067 { sort(List, Unique) },
1068 html(Unique).
1069
1080
1081head_html(List) -->
1082 { list_to_set(List, Unique),
1083 html_expand_head(Unique, NewList)
1084 },
1085 html(NewList).
1086
1087:- multifile
1088 html_head_expansion/2. 1089
1090html_expand_head(List0, List) :-
1091 html_head_expansion(List0, List1),
1092 List0 \== List1,
1093 !,
1094 html_expand_head(List1, List).
1095html_expand_head(List, List).
1096
1097
1098 1101
1102pre_open(Env) -->
1103 { layout(Env, N-_, _)
1104 },
1105 !,
1106 [ nl(N) ].
1107pre_open(_) --> [].
1108
1109post_open(Env) -->
1110 { layout(Env, _-N, _)
1111 },
1112 !,
1113 [ nl(N) ].
1114post_open(_) -->
1115 [].
1116
1117pre_close(head) -->
1118 !,
1119 html_receive(head, head_html),
1120 { layout(head, _, N-_) },
1121 [ nl(N) ].
1122pre_close(Env) -->
1123 { layout(Env, _, N-_)
1124 },
1125 !,
1126 [ nl(N) ].
1127pre_close(_) -->
1128 [].
1129
1130post_close(Env) -->
1131 { layout(Env, _, _-N)
1132 },
1133 !,
1134 [ nl(N) ].
1135post_close(_) -->
1136 [].
1137
1152
1153:- multifile
1154 layout/3. 1155
1156layout(table, 2-1, 1-2).
1157layout(blockquote, 2-1, 1-2).
1158layout(pre, 2-1, 0-2).
1159layout(textarea, 1-1, 0-1).
1160layout(center, 2-1, 1-2).
1161layout(dl, 2-1, 1-2).
1162layout(ul, 1-1, 1-1).
1163layout(ol, 2-1, 1-2).
1164layout(form, 2-1, 1-2).
1165layout(frameset, 2-1, 1-2).
1166layout(address, 2-1, 1-2).
1167
1168layout(head, 1-1, 1-1).
1169layout(body, 1-1, 1-1).
1170layout(script, 1-1, 1-1).
1171layout(style, 1-1, 1-1).
1172layout(select, 1-1, 1-1).
1173layout(map, 1-1, 1-1).
1174layout(html, 1-1, 1-1).
1175layout(caption, 1-1, 1-1).
1176layout(applet, 1-1, 1-1).
1177
1178layout(tr, 1-0, 0-1).
1179layout(option, 1-0, 0-1).
1180layout(li, 1-0, 0-1).
1181layout(dt, 1-0, -).
1182layout(dd, 0-0, -).
1183layout(title, 1-0, 0-1).
1184
1185layout(h1, 2-0, 0-2).
1186layout(h2, 2-0, 0-2).
1187layout(h3, 2-0, 0-2).
1188layout(h4, 2-0, 0-2).
1189
1190layout(iframe, 1-1, 1-1).
1191
1192layout(area, 1-0, empty).
1193layout(base, 1-1, empty).
1194layout(br, 0-1, empty).
1195layout(col, 0-0, empty).
1196layout(embed, 1-1, empty).
1197layout(hr, 1-1, empty). 1198layout(img, 0-0, empty).
1199layout(input, 1-0, empty).
1200layout(link, 1-1, empty).
1201layout(meta, 1-1, empty).
1202layout(param, 1-0, empty).
1203layout(source, 1-0, empty).
1204layout(track, 1-0, empty).
1205layout(wbr, 0-0, empty).
1206
1207layout(p, 2-1, -). 1208layout(td, 0-0, 0-0).
1209
1210layout(div, 1-0, 0-1).
1211
1212 1215
1228
1229print_html(List) :-
1230 current_output(Out),
1231 mailman(List),
1232 write_html(List, Out).
1233print_html(Out, List) :-
1234 ( html_current_option(dialect(xhtml))
1235 -> stream_property(Out, encoding(Enc)),
1236 ( Enc == utf8
1237 -> true
1238 ; print_message(warning, html(wrong_encoding(Out, Enc)))
1239 ),
1240 xml_header(Hdr),
1241 write(Out, Hdr), nl(Out)
1242 ; true
1243 ),
1244 mailman(List),
1245 write_html(List, Out),
1246 flush_output(Out).
1247
1248write_html([], _).
1249write_html([nl(N)|T], Out) :-
1250 !,
1251 join_nl(T, N, Lines, T2),
1252 write_nl(Lines, Out),
1253 write_html(T2, Out).
1254write_html([mailbox(_, Box)|T], Out) :-
1255 !,
1256 ( Box = accept(_, Accepted),
1257 nonvar(Accepted)
1258 -> write_html(Accepted, Out)
1259 ; true
1260 ),
1261 write_html(T, Out).
1262write_html([cdata(Env, Tokens)|T], Out) :-
1263 !,
1264 with_output_to(string(CDATA), write_html(Tokens, current_output)),
1265 valid_cdata(Env, CDATA),
1266 write(Out, CDATA),
1267 write_html(T, Out).
1268write_html([H|T], Out) :-
1269 write(Out, H),
1270 write_html(T, Out).
1271
1272join_nl([nl(N0)|T0], N1, N, T) :-
1273 !,
1274 N2 is max(N0, N1),
1275 join_nl(T0, N2, N, T).
1276join_nl(L, N, N, L).
1277
1278write_nl(0, _) :- !.
1279write_nl(N, Out) :-
1280 nl(Out),
1281 N1 is N - 1,
1282 write_nl(N1, Out).
1283
1295
1296valid_cdata(Env, String) :-
1297 atomics_to_string(['</', Env, '>'], End),
1298 sub_atom_icasechk(String, _, End),
1299 !,
1300 domain_error(cdata, String).
1301valid_cdata(_, _).
1302
1316
1317html_print_length(List, Len) :-
1318 mailman(List),
1319 ( html_current_option(dialect(xhtml))
1320 -> xml_header(Hdr),
1321 atom_length(Hdr, L0),
1322 L1 is L0+1 1323 ; L1 = 0
1324 ),
1325 html_print_length(List, L1, Len).
1326
1327html_print_length([], L, L).
1328html_print_length([nl(N)|T], L0, L) :-
1329 !,
1330 join_nl(T, N, Lines, T1),
1331 L1 is L0 + Lines, 1332 html_print_length(T1, L1, L).
1333html_print_length([mailbox(_, Box)|T], L0, L) :-
1334 !,
1335 ( Box = accept(_, Accepted)
1336 -> html_print_length(Accepted, L0, L1)
1337 ; L1 = L0
1338 ),
1339 html_print_length(T, L1, L).
1340html_print_length([cdata(_, CDATA)|T], L0, L) :-
1341 !,
1342 html_print_length(CDATA, L0, L1),
1343 html_print_length(T, L1, L).
1344html_print_length([H|T], L0, L) :-
1345 atom_length(H, Hlen),
1346 L1 is L0+Hlen,
1347 html_print_length(T, L1, L).
1348
1349
1359
1360reply_html_page(Head, Body) :-
1361 reply_html_page(default, Head, Body).
1362reply_html_page(Style, Head, Body) :-
1363 html_current_option(content_type(Type)),
1364 phrase(page(Style, Head, Body), HTML),
1365 forall(html_header_hook(Style), true),
1366 format('Content-type: ~w~n~n', [Type]),
1367 print_html(HTML).
1368
1369
1380
1381reply_html_partial(HTML) :-
1382 html_current_option(content_type(Type)),
1383 phrase(html(HTML), Tokens),
1384 format('Content-type: ~w~n~n', [Type]),
1385 print_html(Tokens).
1386
1387
1393
1394
1395
1396 1399
1413
1414html_meta(Spec) :-
1415 throw(error(context_error(nodirective, html_meta(Spec)), _)).
1416
1417html_meta_decls(Var, _, _) :-
1418 var(Var),
1419 !,
1420 instantiation_error(Var).
1421html_meta_decls((A,B), (MA,MB), [MH|T]) :-
1422 !,
1423 html_meta_decl(A, MA, MH),
1424 html_meta_decls(B, MB, T).
1425html_meta_decls(A, MA, [MH]) :-
1426 html_meta_decl(A, MA, MH).
1427
1428html_meta_decl(Head, MetaHead,
1429 html_write:html_meta_head(GenHead, Module, Head)) :-
1430 functor(Head, Name, Arity),
1431 functor(GenHead, Name, Arity),
1432 prolog_load_context(module, Module),
1433 Head =.. [Name|HArgs],
1434 maplist(html_meta_decl, HArgs, MArgs),
1435 MetaHead =.. [Name|MArgs].
1436
1437html_meta_decl(html, :) :- !.
1438html_meta_decl(Meta, Meta).
1439
1440system:term_expansion((:- html_meta(Heads)),
1441 [ (:- meta_predicate(Meta))
1442 | MetaHeads
1443 ]) :-
1444 html_meta_decls(Heads, Meta, MetaHeads).
1445
1446:- multifile
1447 html_meta_head/3. 1448
1449html_meta_colours(Head, Goal, built_in-Colours) :-
1450 Head =.. [_|MArgs],
1451 Goal =.. [_|Args],
1452 maplist(meta_colours, MArgs, Args, Colours).
1453
1454meta_colours(html, HTML, Colours) :-
1455 !,
1456 html_colours(HTML, Colours).
1457meta_colours(I, _, Colours) :-
1458 integer(I), I>=0,
1459 !,
1460 Colours = meta(I).
1461meta_colours(_, _, classify).
1462
1463html_meta_called(Head, Goal, Called) :-
1464 Head =.. [_|MArgs],
1465 Goal =.. [_|Args],
1466 meta_called(MArgs, Args, Called, []).
1467
1468meta_called([], [], Called, Called).
1469meta_called([html|MT], [A|AT], Called, Tail) :-
1470 !,
1471 phrase(called_by(A), Called, Tail1),
1472 meta_called(MT, AT, Tail1, Tail).
1473meta_called([0|MT], [A|AT], [A|CT0], CT) :-
1474 !,
1475 meta_called(MT, AT, CT0, CT).
1476meta_called([I|MT], [A|AT], [A+I|CT0], CT) :-
1477 integer(I), I>0,
1478 !,
1479 meta_called(MT, AT, CT0, CT).
1480meta_called([_|MT], [_|AT], Called, Tail) :-
1481 !,
1482 meta_called(MT, AT, Called, Tail).
1483
1484
1485:- html_meta
1486 html(html,?,?),
1487 page(html,?,?),
1488 page(html,html,?,?),
1489 page(+,html,html,?,?),
1490 pagehead(+,html,?,?),
1491 pagebody(+,html,?,?),
1492 reply_html_page(html,html),
1493 reply_html_page(+,html,html),
1494 html_post(+,html,?,?). 1495
1496
1497 1500
1501:- multifile
1502 prolog_colour:goal_colours/2,
1503 prolog_colour:style/2,
1504 prolog_colour:message//1,
1505 prolog:called_by/2. 1506
1507prolog_colour:goal_colours(Goal, Colours) :-
1508 html_meta_head(Goal, _Module, Head),
1509 html_meta_colours(Head, Goal, Colours).
1510prolog_colour:goal_colours(html_meta(_),
1511 built_in-[meta_declarations([html])]).
1512
1513 1514html_colours(Var, classify) :-
1515 var(Var),
1516 !.
1517html_colours(\List, html_raw-[list-Colours]) :-
1518 is_list(List),
1519 !,
1520 list_colours(List, Colours).
1521html_colours(\_, html_call-[dcg]) :- !.
1522html_colours(_:Term, built_in-[classify,Colours]) :-
1523 !,
1524 html_colours(Term, Colours).
1525html_colours(&(Entity), functor-[entity(Entity)]) :- !.
1526html_colours(List, list-ListColours) :-
1527 List = [_|_],
1528 !,
1529 list_colours(List, ListColours).
1530html_colours(Format-Args, functor-[FormatColor,ArgsColors]) :-
1531 !,
1532 format_colours(Format, FormatColor),
1533 format_arg_colours(Args, Format, ArgsColors).
1534html_colours(Term, TermColours) :-
1535 compound(Term),
1536 compound_name_arguments(Term, Name, Args),
1537 Name \== '.',
1538 !,
1539 ( Args = [One]
1540 -> TermColours = html(Name)-ArgColours,
1541 ( layout(Name, _, empty)
1542 -> attr_colours(One, ArgColours)
1543 ; html_colours(One, Colours),
1544 ArgColours = [Colours]
1545 )
1546 ; Args = [AList,Content]
1547 -> TermColours = html(Name)-[AColours, Colours],
1548 attr_colours(AList, AColours),
1549 html_colours(Content, Colours)
1550 ; TermColours = error
1551 ).
1552html_colours(_, classify).
1553
1554list_colours(Var, classify) :-
1555 var(Var),
1556 !.
1557list_colours([], []).
1558list_colours([H0|T0], [H|T]) :-
1559 !,
1560 html_colours(H0, H),
1561 list_colours(T0, T).
1562list_colours(Last, Colours) :- 1563 html_colours(Last, Colours).
1564
1565attr_colours(Var, classify) :-
1566 var(Var),
1567 !.
1568attr_colours([], classify) :- !.
1569attr_colours(Term, list-Elements) :-
1570 Term = [_|_],
1571 !,
1572 attr_list_colours(Term, Elements).
1573attr_colours(Name=Value, built_in-[html_attribute(Name), VColour]) :-
1574 !,
1575 attr_value_colour(Value, VColour).
1576attr_colours(NS:Term, built_in-[ html_xmlns(NS),
1577 html_attribute(Name)-[classify]
1578 ]) :-
1579 compound(Term),
1580 compound_name_arity(Term, Name, 1).
1581attr_colours(Term, html_attribute(Name)-[VColour]) :-
1582 compound(Term),
1583 compound_name_arity(Term, Name, 1),
1584 !,
1585 Term =.. [Name,Value],
1586 attr_value_colour(Value, VColour).
1587attr_colours(Name, html_attribute(Name)) :-
1588 atom(Name),
1589 !.
1590attr_colours(Term, classify) :-
1591 compound(Term),
1592 compound_name_arity(Term, '.', 2),
1593 !.
1594attr_colours(_, error).
1595
1596attr_list_colours(Var, classify) :-
1597 var(Var),
1598 !.
1599attr_list_colours([], []).
1600attr_list_colours([H0|T0], [H|T]) :-
1601 attr_colours(H0, H),
1602 attr_list_colours(T0, T).
1603
1604attr_value_colour(Var, classify) :-
1605 var(Var).
1606attr_value_colour(location_by_id(ID), sgml_attr_function-[Colour]) :-
1607 !,
1608 location_id(ID, Colour).
1609attr_value_colour(#(ID), sgml_attr_function-[Colour]) :-
1610 !,
1611 location_id(ID, Colour).
1612attr_value_colour(A+B, sgml_attr_function-[CA,CB]) :-
1613 !,
1614 attr_value_colour(A, CA),
1615 attr_value_colour(B, CB).
1616attr_value_colour(encode(_), sgml_attr_function-[classify]) :- !.
1617attr_value_colour(Atom, classify) :-
1618 atomic(Atom),
1619 !.
1620attr_value_colour([_|_], classify) :- !.
1621attr_value_colour(_Fmt-_Args, classify) :- !.
1622attr_value_colour(Term, classify) :-
1623 compound(Term),
1624 compound_name_arity(Term, '.', 2),
1625 !.
1626attr_value_colour(_, error).
1627
1628location_id(ID, classify) :-
1629 var(ID),
1630 !.
1631:- if(current_predicate(http_location_for_id/1)). 1632location_id(ID, Class) :-
1633 ( catch(http_location_by_id(ID, Location), _, fail)
1634 -> Class = http_location_for_id(Location)
1635 ; Class = http_no_location_for_id(ID)
1636 ).
1637:- endif. 1638location_id(_, classify).
1639
1640format_colours(Format, format_string) :- atom(Format), !.
1641format_colours(Format, format_string) :- string(Format), !.
1642format_colours(_Format, type_error(text)).
1643
1644format_arg_colours(Args, _Format, classify) :- is_list(Args), !.
1645format_arg_colours(_, _, type_error(list)).
1646
1647:- op(990, xfx, :=). 1648:- op(200, fy, @). 1649
1650prolog_colour:style(html(_), [colour(magenta4), bold(true)]).
1651prolog_colour:style(entity(_), [colour(magenta4)]).
1652prolog_colour:style(html_attribute(_), [colour(magenta4)]).
1653prolog_colour:style(html_xmlns(_), [colour(magenta4)]).
1654prolog_colour:style(format_string(_), [colour(magenta4)]).
1655prolog_colour:style(sgml_attr_function, [colour(blue)]).
1656prolog_colour:style(http_location_for_id(_), [bold(true)]).
1657prolog_colour:style(http_no_location_for_id(_), [colour(red), bold(true)]).
1658
1659
1660prolog_colour:message(html(Element)) -->
1661 [ '~w: SGML element'-[Element] ].
1662prolog_colour:message(entity(Entity)) -->
1663 [ '~w: SGML entity'-[Entity] ].
1664prolog_colour:message(html_attribute(Attr)) -->
1665 [ '~w: SGML attribute'-[Attr] ].
1666prolog_colour:message(sgml_attr_function) -->
1667 [ 'SGML Attribute function'-[] ].
1668prolog_colour:message(http_location_for_id(Location)) -->
1669 [ 'ID resolves to ~w'-[Location] ].
1670prolog_colour:message(http_no_location_for_id(ID)) -->
1671 [ '~w: no such ID'-[ID] ].
1672
1673
1678
1679
1680prolog:called_by(Goal, Called) :-
1681 html_meta_head(Goal, _Module, Head),
1682 html_meta_called(Head, Goal, Called).
1683
1684called_by(Term) -->
1685 called_by(Term, _).
1686
1687called_by(Var, _) -->
1688 { var(Var) },
1689 !,
1690 [].
1691called_by(\G, M) -->
1692 !,
1693 ( { is_list(G) }
1694 -> called_by(G, M)
1695 ; {atom(M)}
1696 -> [(M:G)+2]
1697 ; [G+2]
1698 ).
1699called_by([], _) -->
1700 !,
1701 [].
1702called_by([H|T], M) -->
1703 !,
1704 called_by(H, M),
1705 called_by(T, M).
1706called_by(M:Term, _) -->
1707 !,
1708 ( {atom(M)}
1709 -> called_by(Term, M)
1710 ; []
1711 ).
1712called_by(Term, M) -->
1713 { compound(Term),
1714 !,
1715 Term =.. [_|Args]
1716 },
1717 called_by(Args, M).
1718called_by(_, _) -->
1719 [].
1720
1721:- multifile
1722 prolog:hook/1. 1723
1724prolog:hook(body(_,_,_)).
1725prolog:hook(body(_,_,_,_)).
1726prolog:hook(head(_,_,_)).
1727prolog:hook(head(_,_,_,_)).
1728
1729
1730 1733
1734:- multifile
1735 prolog:message/3. 1736
1737prolog:message(html(expand_failed(What))) -->
1738 [ 'Failed to translate to HTML: ~p'-[What] ].
1739prolog:message(html(wrong_encoding(Stream, Enc))) -->
1740 [ 'XHTML demands UTF-8 encoding; encoding of ~p is ~w'-[Stream, Enc] ].
1741prolog:message(html(multiple_receivers(Id))) -->
1742 [ 'html_post//2: multiple receivers for: ~p'-[Id] ].
1743prolog:message(html(no_receiver(Id))) -->
1744 [ 'html_post//2: no receivers for: ~p'-[Id] ]