35
36:- module(html_head,
37 [ html_resource/2, 38 html_requires//1, 39
40 html_current_resource/1 41 ]). 42:- use_module(library(http/html_write)). 43:- use_module(library(http/mimetype)). 44:- use_module(library(http/http_path)). 45:- use_module(library(error)). 46:- use_module(library(lists)). 47:- use_module(library(occurs)). 48:- use_module(library(option)). 49:- use_module(library(ordsets)). 50:- use_module(library(assoc)). 51:- use_module(library(ugraphs)). 52:- use_module(library(apply)). 53:- use_module(library(debug)). 54
55
103
104:- dynamic
105 html_resource/3. 106:- multifile
107 html_resource/3,
108 mime_include//2. 109
152
153html_resource(About, Properties) :-
154 assert_resource(About, -, Properties).
155
156assert_resource(About, Location, Properties) :-
157 retractall(html_resource(About, _, _)),
158 assert(html_resource(About, Location, Properties)),
159 clean_cache(About, Properties).
160
161system:term_expansion((:-html_resource(About, Properties)),
162 html_head:html_resource(About, File:Line, Properties)) :-
163 source_location(File, Line),
164 clean_cache(About, Properties).
165
166clean_cache(_About, Properties) :-
167 clean_same_about_cache,
168 ( memberchk(aggregate(_), Properties)
169 -> clean_aggregate_cache
170 ; true
171 ).
172
173
177
178html_current_resource(About) :-
179 ( ground(About)
180 -> html_resource(About, _, _), !
181 ; html_resource(About, _, _)
182 ).
183
184
191
192html_requires(Required) -->
193 html_post(head, 'html required'(Required)).
194
195:- multifile
196 html_write:html_head_expansion/2. 197
198html_write:html_head_expansion(In, Out) :-
199 require_commands(In, Required, Rest),
200 Required \== [],
201 !,
202 flatten(Required, Plain),
203 Out = [ html_head:(\html_insert_resource(Plain))
204 | Rest
205 ].
206
207require_commands([], [], []).
208require_commands([_:('html required'(Required))|T0], [Required|TR], R) :-
209 !,
210 require_commands(T0, TR, R).
211require_commands([R|T0], TR, [R|T]) :-
212 !,
213 require_commands(T0, TR, T).
214
215
227
228 229:- public html_insert_resource//1. 230
231html_insert_resource(Required) -->
232 { requirements(Required, Paths),
233 debug(html(script), 'Requirements: ~q~nFinal: ~q', [Required, Paths])
234 },
235 html_include(Paths).
236
237requirements(Required, Paths) :-
238 phrase(requires(Required), List),
239 sort(List, Paths0), 240 use_agregates(Paths0, Paths1, AggregatedBy),
241 order_html_resources(Paths1, AggregatedBy, Paths2),
242 exclude(virtual, Paths2, Paths).
243
244virtual('V'(_)).
245
253
254use_agregates(Paths, Aggregated, AggregatedBy) :-
255 empty_assoc(AggregatedBy0),
256 use_agregates(Paths, Aggregated, AggregatedBy0, AggregatedBy).
257
258use_agregates(Paths, Aggregated, AggregatedBy0, AggregatedBy) :-
259 current_aggregate(Aggregate, Parts, Size),
260 ord_subtract(Paths, Parts, NotCovered),
261 length(Paths, Len0),
262 length(NotCovered, Len1),
263 Covered is Len0-Len1,
264 Covered >= Size/2,
265 !,
266 ord_add_element(NotCovered, Aggregate, NewPaths),
267 add_aggregated_by(Parts, AggregatedBy0, Aggregate, AggregatedBy1),
268 use_agregates(NewPaths, Aggregated, AggregatedBy1, AggregatedBy).
269use_agregates(Paths, Paths, AggregatedBy, AggregatedBy).
270
271add_aggregated_by([], Assoc, _, Assoc).
272add_aggregated_by([H|T], Assoc0, V, Assoc) :-
273 put_assoc(H, Assoc0, V, Assoc1),
274 add_aggregated_by(T, Assoc1, V, Assoc).
275
276
277:- dynamic
278 aggregate_cache_filled/0,
279 aggregate_cache/3. 280:- volatile
281 aggregate_cache_filled/0,
282 aggregate_cache/3. 283
284clean_aggregate_cache :-
285 retractall(aggregate_cache_filled).
286
292
293current_aggregate(Path, Parts, Size) :-
294 aggregate_cache_filled,
295 !,
296 aggregate_cache(Path, Parts, Size).
297current_aggregate(Path, Parts, Size) :-
298 retractall(aggregate_cache(_,_, _)),
299 forall(uncached_aggregate(Path, Parts, Size),
300 assert(aggregate_cache(Path, Parts, Size))),
301 assert(aggregate_cache_filled),
302 aggregate_cache(Path, Parts, Size).
303
304uncached_aggregate(Path, APartsS, Size) :-
305 html_resource(Aggregate, _, Properties),
306 memberchk(aggregate(Parts), Properties),
307 http_absolute_location(Aggregate, Path, []),
308 absolute_paths(Parts, Path, AParts),
309 sort(AParts, APartsS),
310 length(APartsS, Size).
311
312absolute_paths([], _, []).
313absolute_paths([H0|T0], Base, [H|T]) :-
314 http_absolute_location(H0, H, [relative_to(Base)]),
315 absolute_paths(T0, Base, T).
316
317
325
326requires(Spec) -->
327 requires(Spec, /).
328
329requires([], _) -->
330 !,
331 [].
332requires([H|T], Base) -->
333 !,
334 requires(H, Base),
335 requires(T, Base).
336requires(Spec, Base) -->
337 requires(Spec, Base, _, true).
338
339requires('V'(Spec), Base, Properties, Virtual) -->
340 { nonvar(Spec) },
341 !,
342 requires(Spec, Base, Properties, Virtual).
343requires(Spec, Base, Properties, Virtual) -->
344 { res_properties(Spec, Properties),
345 http_absolute_location(Spec, File, [relative_to(Base)])
346 },
347 ( { option(virtual(true), Properties)
348 ; Virtual == false
349 }
350 -> ['V'(Spec)]
351 ; [File]
352 ),
353 requires_from_properties(Properties, File).
354
355
356requires_from_properties([], _) -->
357 [].
358requires_from_properties([H|T], Base) -->
359 requires_from_property(H, Base),
360 requires_from_properties(T, Base).
361
362requires_from_property(requires(What), Base) -->
363 !,
364 requires(What, Base).
365requires_from_property(_, _) -->
366 [].
367
368
373
374order_html_resources(Requirements, AggregatedBy, Ordered) :-
375 requirements_graph(Requirements, AggregatedBy, Graph),
376 ( top_sort(Graph, Ordered)
377 -> true
378 ; connect_graph(Graph, Start, Connected),
379 top_sort(Connected, Ordered0),
380 Ordered0 = [Start|Ordered]
381 ).
382
388
389requirements_graph(Requirements, AggregatedBy, Graph) :-
390 phrase(prerequisites(Requirements, AggregatedBy, Vertices, []), Edges),
391 vertices_edges_to_ugraph(Vertices, Edges, Graph).
392
393prerequisites([], _, Vs, Vs) -->
394 [].
395prerequisites([R|T], AggregatedBy, Vs, Vt) -->
396 prerequisites_for(R, AggregatedBy, Vs, Vt0),
397 prerequisites(T, AggregatedBy, Vt0, Vt).
398
399prerequisites_for(R, AggregatedBy, Vs, Vt) -->
400 { phrase(requires(R, /, Properties, true), Req0),
401 delete(Req0, R, Req)
402 },
403 prop_edges(Properties),
404 ( {Req == []}
405 -> {Vs = [R|Vt]}
406 ; req_edges(Req, AggregatedBy, R),
407 {Vs = Vt}
408 ).
409
410req_edges([], _, _) -->
411 [].
412req_edges([H|T], AggregatedBy, R) -->
413 ( { get_assoc(H, AggregatedBy, Aggregate) }
414 -> [Aggregate-R]
415 ; [H-R]
416 ),
417 req_edges(T, AggregatedBy, R).
418
423
424prop_edges(Properties) -->
425 { option(ordered(true), Properties) },
426 !,
427 ordered_reqs(Properties).
428prop_edges(_) --> [].
429
430ordered_reqs([]) --> [].
431ordered_reqs([H|T]) --> ordered_req(H), ordered_reqs(T).
432
433ordered_req(requires([H|T])) -->
434 { T \== [],
435 !,
436 absolute_req(H, File)
437 },
438 order_pairs(T, File).
439ordered_req(_) --> [].
440
441order_pairs([H|T], P) -->
442 !,
443 { absolute_req(H, File)
444 },
445 [ P-File ],
446 order_pairs(T, File).
447order_pairs(_, _) --> [].
448
449absolute_req(Virtual, Abs) :-
450 html_resource(Virtual, _, Properties),
451 option(virtual(true), Properties),
452 !,
453 Abs = 'V'(Virtual).
454absolute_req(Spec, Abs) :-
455 http_absolute_location(Spec, Abs, [relative_to(/)]).
456
457
462
463connect_graph([], 0, []) :- !.
464connect_graph(Graph, Start, [Start-Vertices|Graph]) :-
465 vertices(Graph, Vertices),
466 Vertices = [First|_],
467 before(First, Start).
468
475
476before(X, _) :-
477 var(X),
478 !,
479 instantiation_error(X).
480before(Number, Start) :-
481 number(Number),
482 !,
483 Start is Number - 1.
484before(_, 0).
485
486
490
491res_properties(Spec, Properties) :-
492 findall(P, res_property(Spec, P), Properties0),
493 list_to_set(Properties0, Properties).
494
495res_property(Spec, Property) :-
496 same_about(Spec, About),
497 html_resource(About, _, Properties),
498 member(Property, Properties).
499
500:- dynamic
501 same_about_cache/2. 502:- volatile
503 same_about_cache/2. 504
505clean_same_about_cache :-
506 retractall(same_about_cache(_,_)).
507
508same_about(Spec, About) :-
509 same_about_cache(Spec, Same),
510 !,
511 member(About, Same).
512same_about(Spec, About) :-
513 findall(A, uncached_same_about(Spec, A), List),
514 assert(same_about_cache(Spec, List)),
515 member(About, List).
516
517uncached_same_about(Spec, About) :-
518 html_resource(About, _, _),
519 same_resource(Spec, About).
520
521
526
527same_resource(R, R) :- !.
528same_resource(R1, R2) :-
529 resource_base_name(R1, B),
530 resource_base_name(R2, B),
531 http_absolute_location(R1, Path, []),
532 http_absolute_location(R2, Path, []).
533
534:- dynamic
535 base_cache/2. 536:- volatile
537 base_cache/2. 538
539resource_base_name(Spec, Base) :-
540 ( base_cache(Spec, Base0)
541 -> Base = Base0
542 ; uncached_resource_base_name(Spec, Base0),
543 assert(base_cache(Spec, Base0)),
544 Base = Base0
545 ).
546
547uncached_resource_base_name(Atom, Base) :-
548 atomic(Atom),
549 !,
550 file_base_name(Atom, Base).
551uncached_resource_base_name(Compound, Base) :-
552 arg(1, Compound, Base0),
553 file_base_name(Base0, Base).
554
564
565html_include([]) --> !.
566html_include([H|T]) -->
567 !,
568 html_include(H),
569 html_include(T).
570html_include(Path) -->
571 { res_property(Path, mime_type(Mime))
572 },
573 !,
574 html_include(Mime, Path).
575html_include(Path) -->
576 { file_mime_type(Path, Mime) },
577 !,
578 html_include(Mime, Path).
579
580html_include(Mime, Path) -->
581 mime_include(Mime, Path),
582 !. 583html_include(text/css, Path) -->
584 !,
585 html(link([ rel(stylesheet),
586 type('text/css'),
587 href(Path)
588 ], [])).
589html_include(text/javascript, Path) -->
590 !,
591 html(script([ type('text/javascript'),
592 src(Path)
593 ], [])).
594html_include(Mime, Path) -->
595 { print_message(warning, html_include(dont_know, Mime, Path))
596 }.
597
616
617 620
621:- multifile
622 user:message_hook/3,
623 prolog:message//1. 624:- dynamic
625 user:message_hook/3. 626
627user:message_hook(load_file(done(_Nesting, _File, _Action,
628 _Module, _Time, _Clauses)),
629 _Level, _Lines) :-
630 clean_same_about_cache,
631 clean_aggregate_cache,
632 fail.
633
634prolog:message(html_include(dont_know, Mime, Path)) -->
635 [ 'Don\'t know how to include resource ~q (mime-type ~q)'-
636 [Path, Mime]
637 ].
638
639
640 643
645:- multifile
646 prolog_edit:locate/3. 647
648prolog_edit:locate(Path, html_resource(Spec), [file(File), line(Line)]) :-
649 atom(Path),
650 html_resource(Spec, File:Line, _Properties),
651 sub_term(Path, Spec)