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:- use_module(library(debug),[debug/3]). 78:- use_module(html_decl, [(html_meta)/1, html_no_content/1, op(_,_,_)]). 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
90:- set_prolog_flag(generate_debug_info, false). 91
92:- meta_predicate
93 reply_html_page(+, :, :),
94 reply_html_page(:, :),
95 html(:, -, +),
96 page(:, -, +),
97 page(:, :, -, +),
98 pagehead(+, :, -, +),
99 pagebody(+, :, -, +),
100 html_receive(+, 3, -, +),
101 html_post(+, :, -, +). 102
103:- multifile
104 expand//1, 105 expand_attribute_value//1, 106 html_header_hook/1. 107
108
141
142
143 146
170
171html_set_options(Options) :-
172 must_be(list, Options),
173 set_options(Options).
174
175set_options([]).
176set_options([H|T]) :-
177 html_set_option(H),
178 set_options(T).
179
180html_set_option(dialect(Dialect0)) :-
181 !,
182 must_be(oneof([html,html4,xhtml,html5]), Dialect0),
183 ( html_version_alias(Dialect0, Dialect)
184 -> true
185 ; Dialect = Dialect0
186 ),
187 set_prolog_flag(html_dialect, Dialect).
188html_set_option(doctype(Atom)) :-
189 !,
190 must_be(atom, Atom),
191 current_prolog_flag(html_dialect, Dialect),
192 dialect_doctype_flag(Dialect, Flag),
193 set_prolog_flag(Flag, Atom).
194html_set_option(content_type(Atom)) :-
195 !,
196 must_be(atom, Atom),
197 current_prolog_flag(html_dialect, Dialect),
198 dialect_content_type_flag(Dialect, Flag),
199 set_prolog_flag(Flag, Atom).
200html_set_option(O) :-
201 domain_error(html_option, O).
202
203html_version_alias(html, html4).
204
208
209html_current_option(dialect(Dialect)) :-
210 current_prolog_flag(html_dialect, Dialect).
211html_current_option(doctype(DocType)) :-
212 current_prolog_flag(html_dialect, Dialect),
213 dialect_doctype_flag(Dialect, Flag),
214 current_prolog_flag(Flag, DocType).
215html_current_option(content_type(ContentType)) :-
216 current_prolog_flag(html_dialect, Dialect),
217 dialect_content_type_flag(Dialect, Flag),
218 current_prolog_flag(Flag, ContentType).
219
220dialect_doctype_flag(html4, html4_doctype).
221dialect_doctype_flag(html5, html5_doctype).
222dialect_doctype_flag(xhtml, xhtml_doctype).
223
224dialect_content_type_flag(html4, html4_content_type).
225dialect_content_type_flag(html5, html5_content_type).
226dialect_content_type_flag(xhtml, xhtml_content_type).
227
228option_default(html_dialect, html5).
229option_default(html4_doctype,
230 'HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" \c
231 "http://www.w3.org/TR/html4/loose.dtd"').
232option_default(html5_doctype,
233 'html').
234option_default(xhtml_doctype,
235 'html PUBLIC "-//W3C//DTD XHTML 1.0 \c
236 Transitional//EN" \c
237 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"').
238option_default(html4_content_type, 'text/html; charset=UTF-8').
239option_default(html5_content_type, 'text/html; charset=UTF-8').
240option_default(xhtml_content_type, 'application/xhtml+xml; charset=UTF-8').
241
245
246init_options :-
247 ( option_default(Name, Value),
248 ( current_prolog_flag(Name, _)
249 -> true
250 ; create_prolog_flag(Name, Value, [])
251 ),
252 fail
253 ; true
254 ).
255
256:- init_options. 257
261
('<?xml version=\'1.0\' encoding=\'UTF-8\'?>').
263
267
268ns(xhtml, 'http://www.w3.org/1999/xhtml').
269
270
271 274
281
282page(Content) -->
283 doctype,
284 html(html(Content)).
285
286page(Head, Body) -->
287 page(default, Head, Body).
288
289page(Style, Head, Body) -->
290 doctype,
291 content_type,
292 html_begin(html),
293 pagehead(Style, Head),
294 pagebody(Style, Body),
295 html_end(html).
296
303
304doctype -->
305 { html_current_option(doctype(DocType)),
306 DocType \== ''
307 },
308 !,
309 [ '<!DOCTYPE ', DocType, '>' ].
310doctype -->
311 [].
312
313content_type -->
314 { html_current_option(content_type(Type))
315 },
316 !,
317 html_post(head, meta([ 'http-equiv'('content-type'),
318 content(Type)
319 ], [])).
320content_type -->
321 { html_current_option(dialect(html5)) },
322 !,
323 html_post(head, meta('charset=UTF-8')).
324content_type -->
325 [].
326
327pagehead(_, Head) -->
328 { functor(Head, head, _)
329 },
330 !,
331 html(Head).
332pagehead(Style, Head) -->
333 { strip_module(Head, M, _),
334 hook_module(M, HM, head//2)
335 },
336 HM:head(Style, Head),
337 !.
338pagehead(_, Head) -->
339 { strip_module(Head, M, _),
340 hook_module(M, HM, head//1)
341 },
342 HM:head(Head),
343 !.
344pagehead(_, Head) -->
345 html(head(Head)).
346
347
348pagebody(_, Body) -->
349 { functor(Body, body, _)
350 },
351 !,
352 html(Body).
353pagebody(Style, Body) -->
354 { strip_module(Body, M, _),
355 hook_module(M, HM, body//2)
356 },
357 HM:body(Style, Body),
358 !.
359pagebody(_, Body) -->
360 { strip_module(Body, M, _),
361 hook_module(M, HM, body//1)
362 },
363 HM:body(Body),
364 !.
365pagebody(_, Body) -->
366 html(body(Body)).
367
368
369hook_module(M, M, PI) :-
370 current_predicate(M:PI),
371 !.
372hook_module(_, user, PI) :-
373 current_predicate(user:PI).
374
379
380html(Spec) -->
381 { strip_module(Spec, M, T) },
382 qhtml(T, M).
383
384qhtml(Var, _) -->
385 { var(Var),
386 !,
387 instantiation_error(Var)
388 }.
389qhtml([], _) -->
390 !,
391 [].
392qhtml([H|T], M) -->
393 !,
394 html_expand(H, M),
395 qhtml(T, M).
396qhtml(X, M) -->
397 html_expand(X, M).
398
399html_expand(Var, _) -->
400 { var(Var),
401 !,
402 instantiation_error(Var)
403 }.
404html_expand(Term, Module) -->
405 do_expand(Term, Module),
406 !.
407html_expand(Term, _Module) -->
408 { print_message(error, html(expand_failed(Term))) }.
409
410
411do_expand(Token, _) --> 412 expand(Token),
413 !.
414do_expand(Fmt-Args, _) -->
415 !,
416 { format(string(String), Fmt, Args)
417 },
418 html_quoted(String).
419do_expand(\List, Module) -->
420 { is_list(List)
421 },
422 !,
423 raw(List, Module).
424do_expand(\Term, Module, In, Rest) :-
425 !,
426 call(Module:Term, In, Rest).
427do_expand(Module:Term, _) -->
428 !,
429 qhtml(Term, Module).
430do_expand(&(Entity), _) -->
431 !,
432 { integer(Entity)
433 -> format(string(String), '&#~d;', [Entity])
434 ; format(string(String), '&~w;', [Entity])
435 },
436 [ String ].
437do_expand(Token, _) -->
438 { atomic(Token)
439 },
440 !,
441 html_quoted(Token).
442do_expand(element(Env, Attributes, Contents), M) -->
443 !,
444 ( { Contents == [],
445 html_current_option(dialect(xhtml))
446 }
447 -> xhtml_empty(Env, Attributes)
448 ; html_begin(Env, Attributes),
449 qhtml(Env, Contents, M),
450 html_end(Env)
451 ).
452do_expand(Term, M) -->
453 { Term =.. [Env, Contents]
454 },
455 !,
456 ( { html_no_content(Env) }
457 -> html_begin(Env, Contents)
458 ; ( { Contents == [],
459 html_current_option(dialect(xhtml))
460 }
461 -> xhtml_empty(Env, [])
462 ; html_begin(Env),
463 qhtml(Env, Contents, M),
464 html_end(Env)
465 )
466 ).
467do_expand(Term, M) -->
468 { Term =.. [Env, Attributes, Contents],
469 check_non_empty(Contents, Env, Term)
470 },
471 !,
472 ( { Contents == [],
473 html_current_option(dialect(xhtml))
474 }
475 -> xhtml_empty(Env, Attributes)
476 ; html_begin(Env, Attributes),
477 qhtml(Env, Contents, M),
478 html_end(Env)
479 ).
480
481qhtml(Env, Contents, M) -->
482 { cdata_element(Env),
483 phrase(cdata(Contents, M), Tokens)
484 },
485 !,
486 [ cdata(Env, Tokens) ].
487qhtml(_, Contents, M) -->
488 qhtml(Contents, M).
489
490
491check_non_empty([], _, _) :- !.
492check_non_empty(_, Tag, Term) :-
493 html_no_content(Tag),
494 !,
495 print_message(warning,
496 format('Using empty element with content: ~p', [Term])).
497check_non_empty(_, _, _).
498
499cdata(List, M) -->
500 { is_list(List) },
501 !,
502 raw(List, M).
503cdata(One, M) -->
504 raw_element(One, M).
505
509
510raw([], _) -->
511 [].
512raw([H|T], Module) -->
513 raw_element(H, Module),
514 raw(T, Module).
515
516raw_element(Var, _) -->
517 { var(Var),
518 !,
519 instantiation_error(Var)
520 }.
521raw_element(\List, Module) -->
522 { is_list(List)
523 },
524 !,
525 raw(List, Module).
526raw_element(\Term, Module, In, Rest) :-
527 !,
528 call(Module:Term, In, Rest).
529raw_element(Module:Term, _) -->
530 !,
531 raw_element(Term, Module).
532raw_element(Fmt-Args, _) -->
533 !,
534 { format(string(S), Fmt, Args) },
535 [S].
536raw_element(Value, _) -->
537 { must_be(atomic, Value) },
538 [Value].
539
540
558
559html_begin(Env) -->
560 { Env =.. [Name|Attributes]
561 },
562 html_begin(Name, Attributes).
563
564html_begin(Env, Attributes) -->
565 pre_open(Env),
566 [<],
567 [Env],
568 attributes(Env, Attributes),
569 ( { html_no_content(Env),
570 html_current_option(dialect(xhtml))
571 }
572 -> ['/>']
573 ; [>]
574 ),
575 post_open(Env).
576
577html_end(Env) --> 578 { html_no_content(Env)
579 ; layout(Env, _, -),
580 html_current_option(dialect(html))
581 },
582 !,
583 [].
584html_end(Env) -->
585 pre_close(Env),
586 ['</'],
587 [Env],
588 ['>'],
589 post_close(Env).
590
594
595xhtml_empty(Env, Attributes) -->
596 pre_open(Env),
597 [<],
598 [Env],
599 attributes(Attributes),
600 ['/>'].
601
624
625xhtml_ns(Id, Value) -->
626 { html_current_option(dialect(xhtml)) },
627 !,
628 html_post(xmlns, \attribute(xmlns:Id=Value)).
629xhtml_ns(_, _) -->
630 [].
631
642
643html_root_attribute(Name, Value) -->
644 html_post(html_begin, \attribute(Name=Value)).
645
650
651attributes(html, L) -->
652 !,
653 ( { html_current_option(dialect(xhtml)) }
654 -> ( { option(xmlns(_), L) }
655 -> attributes(L)
656 ; { ns(xhtml, NS) },
657 attributes([xmlns(NS)|L])
658 ),
659 html_receive(xmlns)
660 ; attributes(L),
661 html_noreceive(xmlns)
662 ),
663 html_receive(html_begin).
664attributes(_, L) -->
665 attributes(L).
666
667attributes([]) -->
668 !,
669 [].
670attributes([H|T]) -->
671 !,
672 attribute(H),
673 attributes(T).
674attributes(One) -->
675 attribute(One).
676
677attribute(Name=Value) -->
678 !,
679 [' '], name(Name), [ '="' ],
680 attribute_value(Value),
681 ['"'].
682attribute(NS:Term) -->
683 !,
684 { Term =.. [Name, Value]
685 },
686 !,
687 attribute((NS:Name)=Value).
688attribute(Term) -->
689 { Term =.. [Name, Value]
690 },
691 !,
692 attribute(Name=Value).
693attribute(Atom) --> 694 { atom(Atom)
695 },
696 [ ' ', Atom ].
697
698name(NS:Name) -->
699 !,
700 [NS, :, Name].
701name(Name) -->
702 [ Name ].
703
723
724attribute_value(List) -->
725 { is_list(List) },
726 !,
727 attribute_value_m(List).
728attribute_value(Value) -->
729 attribute_value_s(Value).
730
732
733attribute_value_s(Var) -->
734 { var(Var),
735 !,
736 instantiation_error(Var)
737 }.
738attribute_value_s(A+B) -->
739 !,
740 attribute_value(A),
741 ( { is_list(B) }
742 -> ( { B == [] }
743 -> []
744 ; [?], search_parameters(B)
745 )
746 ; attribute_value(B)
747 ).
748attribute_value_s(encode(Value)) -->
749 !,
750 { uri_encoded(query_value, Value, Encoded) },
751 [ Encoded ].
752attribute_value_s(Value) -->
753 expand_attribute_value(Value),
754 !.
755attribute_value_s(Fmt-Args) -->
756 !,
757 { format(string(Value), Fmt, Args) },
758 html_quoted_attribute(Value).
759attribute_value_s(Value) -->
760 html_quoted_attribute(Value).
761
762search_parameters([H|T]) -->
763 search_parameter(H),
764 ( {T == []}
765 -> []
766 ; ['&'],
767 search_parameters(T)
768 ).
769
770search_parameter(Var) -->
771 { var(Var),
772 !,
773 instantiation_error(Var)
774 }.
775search_parameter(Name=Value) -->
776 { www_form_encode(Value, Encoded) },
777 [Name, =, Encoded].
778search_parameter(Term) -->
779 { Term =.. [Name, Value],
780 !,
781 www_form_encode(Value, Encoded)
782 },
783 [Name, =, Encoded].
784search_parameter(Term) -->
785 { domain_error(search_parameter, Term)
786 }.
787
797
798attribute_value_m([]) -->
799 [].
800attribute_value_m([H|T]) -->
801 attribute_value_s(H),
802 ( { T == [] }
803 -> []
804 ; [' '],
805 attribute_value_m(T)
806 ).
807
808
809 812
825
826html_quoted(Text) -->
827 { xml_quote_cdata(Text, Quoted, utf8) },
828 [ Quoted ].
829
838
839html_quoted_attribute(Text) -->
840 { xml_quote_attribute(Text, Quoted, utf8) },
841 [ Quoted ].
842
847
848cdata_element(script).
849cdata_element(style).
850
851
852 855
885
886html_post(Id, Content) -->
887 { strip_module(Content, M, C) },
888 [ mailbox(Id, post(M, C)) ].
889
900
901html_receive(Id) -->
902 html_receive(Id, sorted_html).
903
920
921html_receive(Id, Handler) -->
922 { strip_module(Handler, M, P) },
923 [ mailbox(Id, accept(M:P, _)) ].
924
928
929html_noreceive(Id) -->
930 [ mailbox(Id, ignore(_,_)) ].
931
940
941mailman(Tokens) :-
942 ( html_token(mailbox(_, accept(_, Accepted)), Tokens)
943 -> true
944 ),
945 var(Accepted), 946 !,
947 mailboxes(Tokens, Boxes),
948 keysort(Boxes, Keyed),
949 group_pairs_by_key(Keyed, PerKey),
950 move_last(PerKey, script, PerKey1),
951 move_last(PerKey1, head, PerKey2),
952 ( permutation(PerKey2, PerKeyPerm),
953 ( mail_ids(PerKeyPerm)
954 -> !
955 ; debug(html(mailman),
956 'Failed mail delivery order; retrying', []),
957 fail
958 )
959 -> true
960 ; print_message(error, html(cyclic_mailboxes))
961 ).
962mailman(_).
963
964move_last(Box0, Id, Box) :-
965 selectchk(Id-List, Box0, Box1),
966 !,
967 append(Box1, [Id-List], Box).
968move_last(Box, _, Box).
969
974
975html_token(Token, [H|T]) :-
976 html_token_(T, H, Token).
977
978html_token_(_, Token, Token) :- !.
979html_token_(_, cdata(_,Tokens), Token) :-
980 html_token(Token, Tokens).
981html_token_([H|T], _, Token) :-
982 html_token_(T, H, Token).
983
987
988mailboxes(Tokens, MailBoxes) :-
989 mailboxes(Tokens, MailBoxes, []).
990
991mailboxes([], List, List).
992mailboxes([mailbox(Id, Value)|T0], [Id-Value|T], Tail) :-
993 !,
994 mailboxes(T0, T, Tail).
995mailboxes([cdata(_Type, Tokens)|T0], Boxes, Tail) :-
996 !,
997 mailboxes(Tokens, Boxes, Tail0),
998 mailboxes(T0, Tail0, Tail).
999mailboxes([_|T0], T, Tail) :-
1000 mailboxes(T0, T, Tail).
1001
1002mail_ids([]).
1003mail_ids([H|T0]) :-
1004 mail_id(H, NewPosts),
1005 add_new_posts(NewPosts, T0, T),
1006 mail_ids(T).
1007
1008mail_id(Id-List, NewPosts) :-
1009 mail_handlers(List, Boxes, Content),
1010 ( Boxes = [accept(MH:Handler, In)]
1011 -> extend_args(Handler, Content, Goal),
1012 phrase(MH:Goal, In),
1013 mailboxes(In, NewBoxes),
1014 keysort(NewBoxes, Keyed),
1015 group_pairs_by_key(Keyed, NewPosts)
1016 ; Boxes = [ignore(_, _)|_]
1017 -> NewPosts = []
1018 ; Boxes = [accept(_,_),accept(_,_)|_]
1019 -> print_message(error, html(multiple_receivers(Id))),
1020 NewPosts = []
1021 ; print_message(error, html(no_receiver(Id))),
1022 NewPosts = []
1023 ).
1024
1025add_new_posts([], T, T).
1026add_new_posts([Id-Posts|NewT], T0, T) :-
1027 ( select(Id-List0, T0, Id-List, T1)
1028 -> append(List0, Posts, List)
1029 ; debug(html(mailman), 'Stuck with new posts on ~q', [Id]),
1030 fail
1031 ),
1032 add_new_posts(NewT, T1, T).
1033
1034
1040
1041mail_handlers([], [], []).
1042mail_handlers([post(Module,HTML)|T0], H, [Module:HTML|T]) :-
1043 !,
1044 mail_handlers(T0, H, T).
1045mail_handlers([H|T0], [H|T], C) :-
1046 mail_handlers(T0, T, C).
1047
1048extend_args(Term, Extra, NewTerm) :-
1049 Term =.. [Name|Args],
1050 append(Args, [Extra], NewArgs),
1051 NewTerm =.. [Name|NewArgs].
1052
1061
1062sorted_html(List) -->
1063 { sort(List, Unique) },
1064 html(Unique).
1065
1076
1077head_html(List) -->
1078 { list_to_set(List, Unique),
1079 html_expand_head(Unique, NewList)
1080 },
1081 html(NewList).
1082
1083:- multifile
1084 html_head_expansion/2. 1085
1086html_expand_head(List0, List) :-
1087 html_head_expansion(List0, List1),
1088 List0 \== List1,
1089 !,
1090 html_expand_head(List1, List).
1091html_expand_head(List, List).
1092
1093
1094 1097
1098pre_open(Env) -->
1099 { layout(Env, N-_, _)
1100 },
1101 !,
1102 [ nl(N) ].
1103pre_open(_) --> [].
1104
1105post_open(Env) -->
1106 { layout(Env, _-N, _)
1107 },
1108 !,
1109 [ nl(N) ].
1110post_open(_) -->
1111 [].
1112
1113pre_close(head) -->
1114 !,
1115 html_receive(head, head_html),
1116 { layout(head, _, N-_) },
1117 [ nl(N) ].
1118pre_close(Env) -->
1119 { layout(Env, _, N-_)
1120 },
1121 !,
1122 [ nl(N) ].
1123pre_close(_) -->
1124 [].
1125
1126post_close(Env) -->
1127 { layout(Env, _, _-N)
1128 },
1129 !,
1130 [ nl(N) ].
1131post_close(_) -->
1132 [].
1133
1147
1148:- multifile
1149 layout/3. 1150
1151layout(table, 2-1, 1-2).
1152layout(blockquote, 2-1, 1-2).
1153layout(pre, 2-1, 0-2).
1154layout(textarea, 1-1, 0-1).
1155layout(center, 2-1, 1-2).
1156layout(dl, 2-1, 1-2).
1157layout(ul, 1-1, 1-1).
1158layout(ol, 2-1, 1-2).
1159layout(form, 2-1, 1-2).
1160layout(frameset, 2-1, 1-2).
1161layout(address, 2-1, 1-2).
1162
1163layout(head, 1-1, 1-1).
1164layout(body, 1-1, 1-1).
1165layout(script, 1-1, 1-1).
1166layout(style, 1-1, 1-1).
1167layout(select, 1-1, 1-1).
1168layout(map, 1-1, 1-1).
1169layout(html, 1-1, 1-1).
1170layout(caption, 1-1, 1-1).
1171layout(applet, 1-1, 1-1).
1172
1173layout(tr, 1-0, 0-1).
1174layout(option, 1-0, 0-1).
1175layout(li, 1-0, 0-1).
1176layout(dt, 1-0, -).
1177layout(dd, 0-0, -).
1178layout(title, 1-0, 0-1).
1179
1180layout(h1, 2-0, 0-2).
1181layout(h2, 2-0, 0-2).
1182layout(h3, 2-0, 0-2).
1183layout(h4, 2-0, 0-2).
1184
1185layout(iframe, 1-1, 1-1).
1186
1187layout(area, 1-0, -).
1188layout(base, 1-1, -).
1189layout(br, 0-1, -).
1190layout(col, 0-0, -).
1191layout(embed, 1-1, -).
1192layout(hr, 1-1, -).
1193layout(img, 0-0, -).
1194layout(input, 1-0, -).
1195layout(link, 1-1, -).
1196layout(meta, 1-1, -).
1197layout(param, 1-0, -).
1198layout(source, 1-0, -).
1199layout(track, 1-0, -).
1200layout(wbr, 0-0, -).
1201
1202layout(p, 2-1, -). 1203layout(td, 0-0, 0-0).
1204
1205layout(div, 1-0, 0-1).
1206
1207 1210
1223
1224print_html(List) :-
1225 current_output(Out),
1226 mailman(List),
1227 write_html(List, Out).
1228print_html(Out, List) :-
1229 ( html_current_option(dialect(xhtml))
1230 -> stream_property(Out, encoding(Enc)),
1231 ( Enc == utf8
1232 -> true
1233 ; print_message(warning, html(wrong_encoding(Out, Enc)))
1234 ),
1235 xml_header(Hdr),
1236 write(Out, Hdr), nl(Out)
1237 ; true
1238 ),
1239 mailman(List),
1240 write_html(List, Out),
1241 flush_output(Out).
1242
1243write_html([], _).
1244write_html([nl(N)|T], Out) :-
1245 !,
1246 join_nl(T, N, Lines, T2),
1247 write_nl(Lines, Out),
1248 write_html(T2, Out).
1249write_html([mailbox(_, Box)|T], Out) :-
1250 !,
1251 ( Box = accept(_, Accepted),
1252 nonvar(Accepted)
1253 -> write_html(Accepted, Out)
1254 ; true
1255 ),
1256 write_html(T, Out).
1257write_html([cdata(Env, Tokens)|T], Out) :-
1258 !,
1259 with_output_to(string(CDATA), write_html(Tokens, current_output)),
1260 valid_cdata(Env, CDATA),
1261 write(Out, CDATA),
1262 write_html(T, Out).
1263write_html([H|T], Out) :-
1264 write(Out, H),
1265 write_html(T, Out).
1266
1267join_nl([nl(N0)|T0], N1, N, T) :-
1268 !,
1269 N2 is max(N0, N1),
1270 join_nl(T0, N2, N, T).
1271join_nl(L, N, N, L).
1272
1273write_nl(0, _) :- !.
1274write_nl(N, Out) :-
1275 nl(Out),
1276 N1 is N - 1,
1277 write_nl(N1, Out).
1278
1290
1291valid_cdata(Env, String) :-
1292 atomics_to_string(['</', Env, '>'], End),
1293 sub_atom_icasechk(String, _, End),
1294 !,
1295 domain_error(cdata, String).
1296valid_cdata(_, _).
1297
1311
1312html_print_length(List, Len) :-
1313 mailman(List),
1314 ( html_current_option(dialect(xhtml))
1315 -> xml_header(Hdr),
1316 atom_length(Hdr, L0),
1317 L1 is L0+1 1318 ; L1 = 0
1319 ),
1320 html_print_length(List, L1, Len).
1321
1322html_print_length([], L, L).
1323html_print_length([nl(N)|T], L0, L) :-
1324 !,
1325 join_nl(T, N, Lines, T1),
1326 L1 is L0 + Lines, 1327 html_print_length(T1, L1, L).
1328html_print_length([mailbox(_, Box)|T], L0, L) :-
1329 !,
1330 ( Box = accept(_, Accepted)
1331 -> html_print_length(Accepted, L0, L1)
1332 ; L1 = L0
1333 ),
1334 html_print_length(T, L1, L).
1335html_print_length([cdata(_, CDATA)|T], L0, L) :-
1336 !,
1337 html_print_length(CDATA, L0, L1),
1338 html_print_length(T, L1, L).
1339html_print_length([H|T], L0, L) :-
1340 atom_length(H, Hlen),
1341 L1 is L0+Hlen,
1342 html_print_length(T, L1, L).
1343
1344
1354
1355reply_html_page(Head, Body) :-
1356 reply_html_page(default, Head, Body).
1357reply_html_page(Style, Head, Body) :-
1358 html_current_option(content_type(Type)),
1359 phrase(page(Style, Head, Body), HTML),
1360 forall(html_header_hook(Style), true),
1361 format('Content-type: ~w~n~n', [Type]),
1362 print_html(HTML).
1363
1364
1375
1376reply_html_partial(HTML) :-
1377 html_current_option(content_type(Type)),
1378 phrase(html(HTML), Tokens),
1379 format('Content-type: ~w~n~n', [Type]),
1380 print_html(Tokens).
1381
1382
1388
1389
1390:- html_meta
1391 html(html,?,?),
1392 page(html,?,?),
1393 page(html,html,?,?),
1394 page(+,html,html,?,?),
1395 pagehead(+,html,?,?),
1396 pagebody(+,html,?,?),
1397 reply_html_page(html,html),
1398 reply_html_page(+,html,html),
1399 html_post(+,html,?,?). 1400
1401
1402:- multifile
1403 prolog:hook/1. 1404
1405prolog:hook(body(_,_,_)).
1406prolog:hook(body(_,_,_,_)).
1407prolog:hook(head(_,_,_)).
1408prolog:hook(head(_,_,_,_)).
1409
1410
1411 1414
1415:- multifile
1416 prolog:message/3. 1417
1418prolog:message(html(expand_failed(What))) -->
1419 [ 'Failed to translate to HTML: ~p'-[What] ].
1420prolog:message(html(wrong_encoding(Stream, Enc))) -->
1421 [ 'XHTML demands UTF-8 encoding; encoding of ~p is ~w'-[Stream, Enc] ].
1422prolog:message(html(multiple_receivers(Id))) -->
1423 [ 'html_post//2: multiple receivers for: ~p'-[Id] ].
1424prolog:message(html(no_receiver(Id))) -->
1425 [ 'html_post//2: no receivers for: ~p'-[Id] ]